[
  {
    "path": ".docker/Dockerfile-alpine",
    "content": "FROM alpine:3.20.0\n\n# Because this image supports SQLite, we create /home/ory and /home/ory/sqlite which is owned by the ory user\n# and declare /home/ory/sqlite a volume.\n#\n# To get SQLite and Docker Volumes working with this image, mount the volume where SQLite should be written to at:\n#\n#   /home/ory/sqlite/some-file.\n\nRUN addgroup -S ory; \\\n    adduser -S ory -G ory -D -u 10000 -h /home/ory -s /bin/nologin; \\\n    chown -R ory:ory /home/ory\nRUN apk --update upgrade && apk --no-cache --update-cache --upgrade --latest add ca-certificates\n\nWORKDIR /home/ory\n\nCOPY kratos /usr/bin/kratos\n\n# By creating the sqlite folder as the ory user, the mounted volume will be owned by ory:ory, which\n# is required for read/write of SQLite.\nRUN mkdir -p /var/lib/sqlite\nRUN chown ory:ory /var/lib/sqlite\nVOLUME /var/lib/sqlite\n\n# Exposing the ory home directory to simplify passing in Kratos configuration (e.g. if the file $HOME/.kratos.yaml\n# exists, it will be automatically used as the configuration file).\nVOLUME /home/ory\n\n# Declare the standard ports used by Kratos (4433 for public service endpoint, 4434 for admin service endpoint)\nEXPOSE 4433 4434\n\nUSER 10000\n\nENTRYPOINT [\"kratos\"]\nCMD [\"serve\"]\n"
  },
  {
    "path": ".docker/Dockerfile-build",
    "content": "FROM golang:1.26-trixie AS builder\n\nRUN apt-get update && apt-get upgrade -y &&\\\n  mkdir -p /var/lib/sqlite\n\nWORKDIR /go/src/github.com/ory/kratos\n\nCOPY oryx/go.mod oryx/go.mod\nCOPY oryx/go.sum oryx/go.sum\n                \n\nCOPY go.mod go.mod\nCOPY go.sum go.sum\nCOPY pkg/client-go/go.* pkg/client-go/\n\nENV CGO_ENABLED 1\nENV CGO_CPPFLAGS -DSQLITE_DEFAULT_FILE_PERMISSIONS=0600\n\nRUN go mod download\n\nCOPY . .\n\nARG VERSION\nARG COMMIT\nARG BUILD_DATE\n\nRUN --mount=type=cache,target=/root/.cache/go-build go build -tags sqlite \\\n  -ldflags=\"-X 'github.com/ory/kratos/driver/config.Version=${VERSION}' -X 'github.com/ory/kratos/driver/config.Date=${BUILD_DATE}' -X 'github.com/ory/kratos/driver/config.Commit=${COMMIT}'\" \\\n  -o /usr/bin/kratos\n\n#########################\nFROM gcr.io/distroless/base-nossl-debian12:nonroot AS runner\n\nCOPY --from=builder --chown=nonroot:nonroot /var/lib/sqlite /var/lib/sqlite\nCOPY --from=builder --chown=nonroot:nonroot /usr/bin/kratos /usr/bin/kratos\n\nVOLUME /var/lib/sqlite\n\n# Declare the standard ports used by Kratos (4433 for public service endpoint, 4434 for admin service endpoint)\nEXPOSE 4433 4434\n\nENTRYPOINT [\"kratos\"]\nCMD [\"serve\"]\n"
  },
  {
    "path": ".docker/Dockerfile-debug",
    "content": "FROM golang:1.26-trixie\nENV CGO_ENABLED 1\n\nRUN apt-get update && apt-get install -y --no-install-recommends inotify-tools psmisc\nRUN go install github.com/go-delve/delve/cmd/dlv@latest\n\nCOPY script/debug-entrypoint.sh /entrypoint.sh\n\nVOLUME /dockerdev\n\nWORKDIR /dockerdev\n\nENV DELVE_PORT 40000\nENV SERVICE_NAME service\n\nEXPOSE 8000 $DELVE_PORT\n\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": ".docker/Dockerfile-distroless-static",
    "content": "FROM gcr.io/distroless/static-debian12:nonroot\n\nCOPY kratos /usr/bin/kratos\nEXPOSE 4433 4434\n\nENTRYPOINT [\"kratos\"]\nCMD [\"serve\"]\n"
  },
  {
    "path": ".docker/docker-compose.template.dbg",
    "content": "version: '3.7'\n\nservices:\n  ${SERVICE_NAME}:\n    build:\n      dockerfile: ./.docker/Dockerfile-debug\n    ports:\n      - ${REMOTE_DEBUGGING_PORT}:40000\n    security_opt:\n      - apparmor=unconfined\n    cap_add:\n      - SYS_PTRACE\n    volumes:\n      - type: bind\n        source: ${SERVICE_ROOT}\n        target: /dockerdev\n        read_only: false\n"
  },
  {
    "path": ".dockerignore",
    "content": "docs\n.releaser\n.github\n.circleci\ntmp\nscripts\n.idea\n.git/\ndatabase.yaml\ncontrib/quickstart\nnode_modules/\n./quickstart.yml\n./quickstart-*.yml\n.bin/\ntest/\npgked.go\n"
  },
  {
    "path": ".editorconfig",
    "content": "[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 2\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.go]\nindent_size = 4\nindent_style = tab\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "*            @aeneasr @ory/product-development\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/FUNDING.yml\n\n# These are supported funding model platforms\n\n# github:\npatreon: _ory\nopen_collective: ory\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/BUG-REPORT.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/BUG-REPORT.yml\n\ndescription: \"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: \"Preflight checklist\"\n      options:\n        - label:\n            \"I could not find a solution in the existing issues, docs, nor\n            discussions.\"\n          required: true\n        - label:\n            \"I agree to follow this project's [Code of\n            Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md).\"\n          required: true\n        - label:\n            \"I have read and am following this repository's [Contribution\n            Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md).\"\n          required: true\n        - label:\n            \"I have joined the [Ory Community Slack](https://slack.ory.com).\"\n        - label:\n            \"I am signed up to the [Ory Security Patch\n            Newsletter](https://www.ory.com/l/sign-up-newsletter).\"\n    id: checklist\n    type: checkboxes\n  - attributes:\n      description:\n        \"Enter the slug or API URL of the affected Ory Network project. Leave\n        empty when you are self-hosting.\"\n      label: \"Ory Network Project\"\n      placeholder: \"https://<your-project-slug>.projects.oryapis.com\"\n    id: ory-network-project\n    type: input\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      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      label: \"Reproducing the bug\"\n    id: reproduce-bug\n    type: textarea\n    validations:\n      required: true\n  - attributes:\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      label: \"Relevant log output\"\n      render: shell\n      placeholder: |\n        log=error ....\n    id: logs\n    type: textarea\n  - attributes:\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      label: \"Relevant configuration\"\n      render: yml\n      placeholder: |\n        server:\n          admin:\n            port: 1234\n    id: config\n    type: textarea\n  - attributes:\n      description: \"What version of our software are you running?\"\n      label: Version\n    id: version\n    type: input\n    validations:\n      required: true\n  - attributes:\n      label: \"On which operating system are you observing this issue?\"\n      options:\n        - Ory Network\n        - macOS\n        - Linux\n        - Windows\n        - FreeBSD\n        - Other\n    id: operating-system\n    type: dropdown\n  - attributes:\n      label: \"In which environment are you deploying?\"\n      options:\n        - Ory Network\n        - Docker\n        - \"Docker Compose\"\n        - \"Kubernetes with Helm\"\n        - Kubernetes\n        - Binary\n        - Other\n    id: deployment\n    type: dropdown\n  - attributes:\n      description: \"Add any other context about the problem here.\"\n      label: Additional Context\n    id: additional\n    type: textarea\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/DESIGN-DOC.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/DESIGN-DOC.yml\n\ndescription:\n  \"A design document is needed for non-trivial changes to the code base.\"\nlabels:\n  - rfc\nname: \"Design Document\"\nbody:\n  - attributes:\n      value: |\n        Thank you for writing this design document. \n\n        One of the key elements of Ory's software engineering culture is the use of defining software designs through design docs. These are relatively informal documents that the primary author or authors of a software system or application create before they embark on the coding project. The design doc documents the high level implementation strategy and key design decisions with emphasis on the trade-offs that were considered during those decisions.\n\n        Ory is leaning heavily on [Google's design docs process](https://www.industrialempathy.com/posts/design-docs-at-google/)\n        and [Golang Proposals](https://github.com/golang/proposal).\n\n        Writing a design doc before contributing your change ensures that your ideas are checked with\n        the community and maintainers. It will save you a lot of time developing things that might need to be changed\n        after code reviews, and your pull requests will be merged faster.\n    type: markdown\n  - attributes:\n      label: \"Preflight checklist\"\n      options:\n        - label:\n            \"I could not find a solution in the existing issues, docs, nor\n            discussions.\"\n          required: true\n        - label:\n            \"I agree to follow this project's [Code of\n            Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md).\"\n          required: true\n        - label:\n            \"I have read and am following this repository's [Contribution\n            Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md).\"\n          required: true\n        - label:\n            \"I have joined the [Ory Community Slack](https://slack.ory.com).\"\n        - label:\n            \"I am signed up to the [Ory Security Patch\n            Newsletter](https://www.ory.com/l/sign-up-newsletter).\"\n    id: checklist\n    type: checkboxes\n  - attributes:\n      description:\n        \"Enter the slug or API URL of the affected Ory Network project. Leave\n        empty when you are self-hosting.\"\n      label: \"Ory Network Project\"\n      placeholder: \"https://<your-project-slug>.projects.oryapis.com\"\n    id: ory-network-project\n    type: input\n  - attributes:\n      description: |\n        This section gives the reader a very rough overview of the landscape in which the new system is being built and what is actually being built. This isn’t a requirements doc. Keep it succinct! The goal is that readers are brought up to speed but some previous knowledge can be assumed and detailed info can be linked to. This section should be entirely focused on objective background facts.\n      label: \"Context and scope\"\n    id: scope\n    type: textarea\n    validations:\n      required: true\n\n  - attributes:\n      description: |\n        A short list of bullet points of what the goals of the system are, and, sometimes more importantly, what non-goals are. Note, that non-goals aren’t negated goals like “The system shouldn’t crash”, but rather things that could reasonably be goals, but are explicitly chosen not to be goals. A good example would be “ACID compliance”; when designing a database, you’d certainly want to know whether that is a goal or non-goal. And if it is a non-goal you might still select a solution that provides it, if it doesn’t introduce trade-offs that prevent achieving the goals.\n      label: \"Goals and non-goals\"\n    id: goals\n    type: textarea\n    validations:\n      required: true\n\n  - attributes:\n      description: |\n        This section should start with an overview and then go into details.\n        The design doc is the place to write down the trade-offs you made in designing your software. Focus on those trade-offs to produce a useful document with long-term value. That is, given the context (facts), goals and non-goals (requirements), the design doc is the place to suggest solutions and show why a particular solution best satisfies those goals.\n\n        The point of writing a document over a more formal medium is to provide the flexibility to express the problem at hand in an appropriate manner. Because of this, there is no explicit guidance on how to actually describe the design.\n      label: \"The design\"\n    id: design\n    type: textarea\n    validations:\n      required: true\n\n  - attributes:\n      description: |\n        If the system under design exposes an API, then sketching out that API is usually a good idea. In most cases, however, one should withstand the temptation to copy-paste formal interface or data definitions into the doc as these are often verbose, contain unnecessary detail and quickly get out of date. Instead, focus on the parts that are relevant to the design and its trade-offs.\n      label: \"APIs\"\n    id: apis\n    type: textarea\n\n  - attributes:\n      description: |\n        Systems that store data should likely discuss how and in what rough form this happens. Similar to the advice on APIs, and for the same reasons, copy-pasting complete schema definitions should be avoided. Instead, focus on the parts that are relevant to the design and its trade-offs.\n      label: \"Data storage\"\n    id: persistence\n    type: textarea\n\n  - attributes:\n      description: |\n        Design docs should rarely contain code, or pseudo-code except in situations where novel algorithms are described. As appropriate, link to prototypes that show the feasibility of the design.\n      label: \"Code and pseudo-code\"\n    id: pseudocode\n    type: textarea\n\n  - attributes:\n      description: |\n        One of the primary factors that would influence the shape of a software design and hence the design doc, is the degree of constraint of the solution space.\n\n        On one end of the extreme is the “greenfield software project”, where all we know are the goals, and the solution can be whatever makes the most sense. Such a document may be wide-ranging, but it also needs to quickly define a set of rules that allow zooming in on a manageable set of solutions.\n\n        On the other end are systems where the possible solutions are very well defined, but it isn't at all obvious how they could even be combined to achieve the goals. This may be a legacy system that is difficult to change and wasn't designed to do what you want it to do or a library design that needs to operate within the constraints of the host programming language.\n\n        In this situation, you may be able to enumerate all the things you can do relatively easily, but you need to creatively put those things together to achieve the goals. There may be multiple solutions, and none of them are great, and hence such a document should focus on selecting the best way given all identified trade-offs.\n      label: \"Degree of constraint\"\n    id: constrait\n    type: textarea\n\n  - attributes:\n      description: |\n        This section lists alternative designs that would have reasonably achieved similar outcomes. The focus should be on the trade-offs that each respective design makes and how those trade-offs led to the decision to select the design that is the primary topic of the document.\n\n        While it is fine to be succinct about a solution that ended up not being selected, this section is one of the most important ones as it shows very explicitly why the selected solution is the best given the project goals and how other solutions, that the reader may be wondering about, introduce trade-offs that are less desirable given the goals.\n\n      label: Alternatives considered\n    id: alternatives\n    type: textarea\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml\n\ndescription:\n  \"Suggest an idea for this project without a plan for implementation\"\nlabels:\n  - feat\nname: \"Feature Request\"\nbody:\n  - attributes:\n      value: |\n        Thank you for suggesting an idea for this project!\n\n        If you already have a plan to implement a feature or a change, please create a [design document](https://github.com/aeneasr/gh-template-test/issues/new?assignees=&labels=rfc&template=DESIGN-DOC.yml) instead if the change is non-trivial!\n    type: markdown\n  - attributes:\n      label: \"Preflight checklist\"\n      options:\n        - label:\n            \"I could not find a solution in the existing issues, docs, nor\n            discussions.\"\n          required: true\n        - label:\n            \"I agree to follow this project's [Code of\n            Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md).\"\n          required: true\n        - label:\n            \"I have read and am following this repository's [Contribution\n            Guidelines](https://github.com/ory/kratos/blob/master/CONTRIBUTING.md).\"\n          required: true\n        - label:\n            \"I have joined the [Ory Community Slack](https://slack.ory.com).\"\n        - label:\n            \"I am signed up to the [Ory Security Patch\n            Newsletter](https://www.ory.com/l/sign-up-newsletter).\"\n    id: checklist\n    type: checkboxes\n  - attributes:\n      description:\n        \"Enter the slug or API URL of the affected Ory Network project. Leave\n        empty when you are self-hosting.\"\n      label: \"Ory Network Project\"\n      placeholder: \"https://<your-project-slug>.projects.oryapis.com\"\n    id: ory-network-project\n    type: input\n  - attributes:\n      description:\n        \"Is your feature request related to a problem? Please describe.\"\n      label: \"Describe your problem\"\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\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: true\n  - attributes:\n      description: \"Describe alternatives you've considered\"\n      label: \"Workarounds or alternatives\"\n    id: alternatives\n    type: textarea\n    validations:\n      required: true\n  - attributes:\n      description: \"What version of our software are you running?\"\n      label: Version\n    id: version\n    type: input\n    validations:\n      required: true\n  - attributes:\n      description:\n        \"Add any other context or screenshots about the feature request here.\"\n      label: Additional Context\n    id: additional\n    type: textarea\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/ISSUE_TEMPLATE/config.yml\n\nblank_issues_enabled: false\ncontact_links:\n  - name: Ory Kratos Forum\n    url: https://github.com/ory/kratos/discussions\n    about:\n      Please ask and answer questions here, show your implementations and\n      discuss ideas.\n  - name: Ory Chat\n    url: https://www.ory.com/chat\n    about:\n      Hang out with other Ory community members to ask and answer questions.\n"
  },
  {
    "path": ".github/auto_assign.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/auto_assign.yml\n\n# Set to true to add reviewers to pull requests\naddReviewers: true\n\n# Set to true to add assignees to pull requests\naddAssignees: true\n\n# A list of reviewers to be added to pull requests (GitHub user name)\nassignees:\n  - ory/maintainers\n\n# A number of reviewers added to the pull request\n# Set 0 to add all the reviewers (default: 0)\nnumberOfReviewers: 0\n"
  },
  {
    "path": ".github/codeql/codeql-config.yml",
    "content": "name: \"CodeQL config\"\n\nqueries:\n  - uses: security-and-quality\n\npaths-ignore:\n  - \"/test/\"\n  - \"/pkg/testhelpers\"\n"
  },
  {
    "path": ".github/config.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/config.yml\n\ntodo:\n  keyword: \"@todo\"\n  label: todo\n"
  },
  {
    "path": ".github/conventional_commits.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/ory/ci/master/conventional_commit_config/dist/config.schema.json\",\n  \"addTypes\": [\"improvement\", \"perf\"],\n  \"addScopes\": [\"cli\", \"sql\"]\n}\n"
  },
  {
    "path": ".github/labels.json",
    "content": "[\n  {\n    \"name\": \"package/2fa\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:2fa\"]\n  },\n  {\n    \"name\": \"package/cli\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:cli\"]\n  },\n  {\n    \"name\": \"package/courier\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:courier\"]\n  },\n  {\n    \"name\": \"package/courier\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:docs\"]\n  },\n  {\n    \"name\": \"package/selfservice/errorx\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:errorx\"]\n  },\n  {\n    \"name\": \"package/identity\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:identity\"]\n  },\n  {\n    \"name\": \"package/persistence/sql\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:migrations\"]\n  },\n  {\n    \"name\": \"package/selfservice\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:selfservice\"]\n  },\n  {\n    \"name\": \"package/selfservice/oidc\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:ss/oidc\"]\n  },\n  {\n    \"name\": \"package/selfservice/password\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:ss/password\"]\n  },\n  {\n    \"name\": \"package/selfservice/verification\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:verification\"]\n  },\n  {\n    \"name\": \"package/selfservice/recovery\",\n    \"color\": \"0A28FD\",\n    \"aliases\": []\n  },\n  {\n    \"name\": \"package/session\",\n    \"color\": \"0A28FD\",\n    \"aliases\": [\"module:session\"]\n  }\n]\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "<!--\nDescribe the big picture of your changes here to communicate to the maintainers why we should accept this pull request.\n\nThis text will be included in the changelog. If applicable, include links to documentation or pieces of code.\nIf your change includes breaking changes please add a code block documenting the breaking change:\n\n```\nBREAKING CHANGES: This patch changes the behavior of configuration item `foo` to do bar. To keep the existing\nbehavior please do baz.\n```\n-->\n\n## Related issue(s)\n\n<!--\nIf this pull request\n\n1. is a fix for a known bug, link the issue where the bug was reported in the format of `#1234`;\n2. is a fix for a previously unknown bug, explain the bug and how to reproduce it in this pull request;\n3. implements a new feature, link the issue containing the design document in the format of `#1234`;\n4. improves the documentation, no issue reference is required.\n\nPull requests introducing new features, which do not have a design document linked are more likely to be rejected and take on average 2-8 weeks longer to\nget merged.\n\nYou can discuss changes with maintainers either in the Github Discussions in this repository or\njoin the [Ory Chat](https://www.ory.com/chat).\n-->\n\n## Checklist\n\n<!--\nPut an `x` in the boxes that apply. You can also fill these out after creating the PR.\n\nPlease be aware that pull requests must have all boxes ticked in order to be merged.\n\nIf you're unsure about any of them, don't hesitate to ask. We're here to help!\n-->\n\n- [ ] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md).\n- [ ] I have referenced an issue containing the design document if my change\n      introduces a new feature.\n- [ ] I am following the\n      [contributing code guidelines](../blob/master/CONTRIBUTING.md#contributing-code).\n- [ ] I have read the [security policy](../security/policy).\n- [ ] I confirm that this pull request does not address a security\n      vulnerability. If this pull request addresses a security vulnerability, I\n      confirm that I got the approval (please contact\n      [security@ory.com](mailto:security@ory.com)) from the maintainers to push\n      the changes.\n- [ ] I have added tests that prove my fix is effective or that my feature\n      works.\n- [ ] I have added or changed [the documentation](https://github.com/ory/docs).\n\n## Further Comments\n\n<!--\nIf this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution\nyou did and what alternatives you considered, etc...\n-->\n"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "content": "name: CI\non:\n  push:\n    branches:\n      - master\n    tags:\n      - \"*\"\n  pull_request:\n\n# Cancel in-progress runs in current workflow.\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  test:\n    name: Run tests and lints\n    runs-on: ubuntu-latest\n    services:\n      postgres:\n        image: postgres:18\n        env:\n          POSTGRES_DB: postgres\n          POSTGRES_PASSWORD: test\n          POSTGRES_USER: test\n        ports:\n          - 5432:5432\n      mysql:\n        image: mysql:9.6\n        env:\n          MYSQL_ROOT_PASSWORD: test\n        ports:\n          - 3306:3306\n    env:\n      TEST_DATABASE_POSTGRESQL: \"postgres://test:test@localhost:5432/postgres?sslmode=disable\"\n      TEST_DATABASE_MYSQL: \"mysql://root:test@(localhost:3306)/mysql?parseTime=true&multiStatements=true\"\n      TEST_DATABASE_COCKROACHDB: \"cockroach://root@localhost:26257/defaultdb?sslmode=disable\"\n    steps:\n      - run: |\n          docker create --name cockroach -p 26257:26257 \\\n           cockroachdb/cockroach:latest-v25.4 start-single-node --insecure \\\n            || true\n          docker start cockroach\n        name: Start CockroachDB\n      - run: docker pull oryd/hydra:v2.2.0\n        name: Pull Hydra\n      - uses: ory/ci/checkout@master\n        with:\n          fetch-depth: 2\n      - uses: actions/setup-go@v6\n        with:\n          check-latest: true\n          go-version-file: go.mod\n      - run: go list -json > go.list\n      - name: Run nancy\n        uses: sonatype-nexus-community/nancy-github-action@v1.0.3\n        with:\n          nancyVersion: v1.0.42\n      - run: |\n          sudo apt-get update\n        name: apt-get update\n      - run: npm install\n        name: Install node deps\n      - name: Run golangci-lint\n        if: ${{ github.ref_type != 'tag' }}\n        uses: golangci/golangci-lint-action@v9\n        env:\n          GOGC: 100\n        with:\n          args: --timeout 10m0s\n          version: latest\n          only-new-issues: \"true\"\n      - name: Build Kratos\n        run: make install\n      - name: Run go tests\n        run: make test-coverage\n      - name: Submit to Codecov\n        run: |\n          bash <(curl -s https://codecov.io/bash)\n        env:\n          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n\n  test-e2e:\n    name: Run end-to-end tests\n    runs-on: ubuntu-latest\n    services:\n      postgres:\n        image: postgres:18\n        env:\n          POSTGRES_DB: postgres\n          POSTGRES_PASSWORD: test\n          POSTGRES_USER: test\n        ports:\n          - 5432:5432\n      mysql:\n        image: mysql:9.6\n        env:\n          MYSQL_ROOT_PASSWORD: test\n        ports:\n          - 3306:3306\n      mailslurper:\n        image: oryd/mailslurper:latest-smtps\n        ports:\n          - 4436:4436\n          - 4437:4437\n          - 1025:1025\n    env:\n      TEST_DATABASE_POSTGRESQL: \"postgres://test:test@localhost:5432/postgres?sslmode=disable\"\n      TEST_DATABASE_MYSQL: \"mysql://root:test@(localhost:3306)/mysql?parseTime=true&multiStatements=true\"\n      TEST_DATABASE_COCKROACHDB: \"cockroach://root@localhost:26257/defaultdb?sslmode=disable\"\n    strategy:\n      fail-fast: false\n      matrix:\n        database: [\"postgres\", \"sqlite\"]\n        # \"cockroach\", \"mysql\" TODO: fix tests and uncomment\n    steps:\n      - uses: actions/setup-node@v6\n        with:\n          node-version: \"24\"\n      - run: |\n          docker create --name cockroach -p 26257:26257 \\\n           cockroachdb/cockroach:latest-v25.4 start-single-node --insecure\n          docker start cockroach\n        name: Start CockroachDB\n      - uses: browser-actions/setup-chrome@latest\n        name: Install Chrome\n      # - uses: browser-actions/setup-firefox@latest\n      #   name: Install Firefox\n      # - uses: browser-actions/setup-geckodriver@latest\n      #   name: Install Geckodriver\n      #   with:\n      #     geckodriver-version: 0.32.0\n      - uses: ory/ci/checkout@master\n        with:\n          fetch-depth: 2\n      - run: |\n          sudo apt-get update\n        name: apt-get update\n      - run: |\n          npm ci\n          cd test/e2e; npm ci\n          npm i -g expo-cli\n        name: Install node deps\n      - run: |\n          sudo apt-get install -y moreutils gettext\n        name: Install tools\n      - name: Setup Go\n        uses: actions/setup-go@v6\n        with:\n          check-latest: true\n          go-version-file: go.mod\n      - name: Install selfservice-ui-react-native\n        uses: actions/checkout@v6\n        with:\n          repository: ory/kratos-selfservice-ui-react-native\n          path: react-native-ui\n      - run: |\n          cd react-native-ui\n          npm install\n\n      - name: Install selfservice-ui-node\n        uses: actions/checkout@v6\n        with:\n          repository: ory/kratos-selfservice-ui-node\n          path: node-ui\n      - run: |\n          cd node-ui\n          npm install --legacy-peer-deps\n\n      - name: Install selfservice-ui-react-nextjs\n        uses: actions/checkout@v6\n        with:\n          repository: ory/kratos-selfservice-ui-react-nextjs\n          path: react-ui\n      - run: |\n          cd react-ui\n          npm ci\n\n      - run: |\n          echo 'RN_UI_PATH='\"$(realpath react-native-ui)\" >> \"$GITHUB_ENV\"\n          echo 'NODE_UI_PATH='\"$(realpath node-ui)\" >> \"$GITHUB_ENV\"\n          echo 'REACT_UI_PATH='\"$(realpath react-ui)\" >> \"$GITHUB_ENV\"\n      - name: \"Run Cypress tests\"\n        run: ./test/e2e/run.sh ${{ matrix.database }}\n        env:\n          RN_UI_PATH: react-native-ui\n          NODE_UI_PATH: node-ui\n          REACT_UI_PATH: react-ui\n          CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}\n      - if: failure()\n        uses: actions/upload-artifact@v7\n        with:\n          name: cypress-${{ matrix.database }}-logs\n          path: test/e2e/*.e2e.log\n\n  test-e2e-playwright:\n    name: Run Playwright end-to-end tests\n    runs-on: ubuntu-latest\n    services:\n      postgres:\n        image: postgres:18\n        env:\n          POSTGRES_DB: postgres\n          POSTGRES_PASSWORD: test\n          POSTGRES_USER: test\n        ports:\n          - 5432:5432\n      mysql:\n        image: mysql:9.6\n        env:\n          MYSQL_ROOT_PASSWORD: test\n        ports:\n          - 3306:3306\n      mailslurper:\n        image: oryd/mailslurper:latest-smtps\n        ports:\n          - 4436:4436\n          - 4437:4437\n          - 1025:1025\n    env:\n      TEST_DATABASE_POSTGRESQL: \"postgres://test:test@localhost:5432/postgres?sslmode=disable\"\n      TEST_DATABASE_MYSQL: \"mysql://root:test@(localhost:3306)/mysql?parseTime=true&multiStatements=true\"\n      TEST_DATABASE_COCKROACHDB: \"cockroach://root@localhost:26257/defaultdb?sslmode=disable\"\n    strategy:\n      fail-fast: false\n      matrix:\n        database: [\"postgres\", \"cockroach\", \"sqlite\", \"mysql\"]\n    steps:\n      - uses: actions/setup-node@v6\n        with:\n          node-version: \"24\"\n      - run: |\n          docker create --name cockroach -p 26257:26257 \\\n           cockroachdb/cockroach:latest-v25.4 start-single-node --insecure\n          docker start cockroach\n        name: Start CockroachDB\n      - uses: ory/ci/checkout@master\n        with:\n          fetch-depth: 2\n      - run: |\n          sudo apt-get update\n        name: apt-get update\n      - run: |\n          npm ci\n          cd test/e2e; npm ci\n          npx playwright install --with-deps\n          npm i -g expo-cli\n        name: Install node deps\n      - run: |\n          sudo apt-get install -y moreutils gettext\n        name: Install tools\n      - name: Setup Go\n        uses: actions/setup-go@v6\n        with:\n          check-latest: true\n          go-version-file: go.mod\n      - run: go build -tags sqlite,json1 .\n\n      - name: Install selfservice-ui-react-native\n        uses: actions/checkout@v6\n        with:\n          repository: ory/kratos-selfservice-ui-react-native\n          path: react-native-ui\n      - run: |\n          cd react-native-ui\n          npm install\n\n      - name: Install selfservice-ui-node\n        uses: actions/checkout@v6\n        with:\n          repository: ory/kratos-selfservice-ui-node\n          path: node-ui\n      - run: |\n          cd node-ui\n          npm install --legacy-peer-deps\n\n      - name: Install selfservice-ui-react-nextjs\n        uses: actions/checkout@v6\n        with:\n          repository: ory/kratos-selfservice-ui-react-nextjs\n          path: react-ui\n      - run: |\n          cd react-ui\n          npm ci\n\n      - run: |\n          echo 'RN_UI_PATH='\"$(realpath react-native-ui)\" >> \"$GITHUB_ENV\"\n          echo 'NODE_UI_PATH='\"$(realpath node-ui)\" >> \"$GITHUB_ENV\"\n          echo 'REACT_UI_PATH='\"$(realpath react-ui)\" >> \"$GITHUB_ENV\"\n\n      - name: \"Set up environment\"\n        run: test/e2e/run.sh --only-setup\n      - name: \"Run Playwright tests\"\n        run: |\n          cd test/e2e\n          npm run playwright\n        env:\n          DB: ${{ matrix.database }}\n          RN_UI_PATH: react-native-ui\n          NODE_UI_PATH: node-ui\n          REACT_UI_PATH: react-ui\n      - if: failure()\n        uses: actions/upload-artifact@v7\n        with:\n          name: playwright-${{ matrix.database }}-logs\n          path: test/e2e/*.e2e.log\n      - if: failure()\n        uses: actions/upload-artifact@v7\n        with:\n          name: playwright-test-results-${{ matrix.database }}-${{ github.sha }}\n          path: |\n            test/e2e/test-results/\n            test/e2e/playwright-report/\n\n  docs-cli:\n    runs-on: ubuntu-latest\n    name: Build CLI docs\n    needs:\n      - test\n    steps:\n      - uses: ory/ci/docs/cli-next@master\n        with:\n          token: ${{ secrets.ORY_BOT_PAT }}\n          arg: \".\"\n          output-dir: docs/kratos\n\n  release:\n    name: Generate release\n    runs-on: ubuntu-latest\n    if: ${{ github.ref_type == 'tag' }}\n    needs:\n      - test\n      - test-e2e\n    steps:\n      - uses: ory/ci/releaser@master\n        with:\n          token: ${{ secrets.ORY_BOT_PAT }}\n          goreleaser_key: ${{ secrets.GORELEASER_KEY }}\n          cosign_pwd: ${{ secrets.COSIGN_PWD }}\n          docker_username: ${{ secrets.DOCKERHUB_USERNAME }}\n          docker_password: ${{ secrets.DOCKERHUB_PASSWORD }}\n\n  newsletter-draft:\n    name: Draft newsletter\n    runs-on: ubuntu-latest\n    if: ${{ github.ref_type == 'tag' }}\n    needs:\n      - release\n    steps:\n      - uses: ory/ci/newsletter@master\n        with:\n          mailchimp_list_id: f605a41b53\n          mailchmip_segment_id: 6479477\n          mailchimp_api_key: ${{ secrets.MAILCHIMP_API_KEY }}\n          draft: \"true\"\n          ssh_key: ${{ secrets.ORY_BOT_SSH_KEY }}\n\n  slack-approval-notification:\n    name: Pending approval Slack notification\n    runs-on: ubuntu-latest\n    if: ${{ github.ref_type == 'tag' }}\n    needs:\n      - newsletter-draft\n    steps:\n      - uses: ory/ci/newsletter/slack-notify@master\n        with:\n          slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}\n\n  newsletter-send:\n    name: Send newsletter\n    runs-on: ubuntu-latest\n    needs:\n      - newsletter-draft\n    if: ${{ github.ref_type == 'tag' }}\n    environment: production\n    steps:\n      - uses: ory/ci/newsletter@master\n        with:\n          mailchimp_list_id: f605a41b53\n          mailchmip_segment_id: 6479477\n          mailchimp_api_key: ${{ secrets.MAILCHIMP_API_KEY }}\n          draft: \"false\"\n          ssh_key: ${{ secrets.ORY_BOT_SSH_KEY }}\n"
  },
  {
    "path": ".github/workflows/closed_references.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/closed_references.yml\n\nname: Closed Reference Notifier\n\non:\n  schedule:\n    - cron: \"0 0 * * *\"\n  workflow_dispatch:\n    inputs:\n      issueLimit:\n        description: Max. number of issues to create\n        required: true\n        default: \"5\"\n\njobs:\n  find_closed_references:\n    if: github.repository_owner == 'ory'\n    runs-on: ubuntu-latest\n    name: Find closed references\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: \"24\"\n      - uses: ory/closed-reference-notifier@v1\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          issueLabels: upstream,good first issue,help wanted\n          issueLimit: ${{ github.event.inputs.issueLimit || '5' }}\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: [master]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [master]\n  schedule:\n    - cron: \"26 21 * * 3\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [\"go\", \"javascript\"]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed\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          config-file: ./.github/codeql/codeql-config.yml\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          # queries: ./path/to/local/query, your-org/your-repo/queries@main\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      # 📚 https://git.io/JvXDl\n\n      # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n      #    and modify them (or add more) to build your code if your project\n      #    uses a compiled language\n\n      #- run: |\n      #   make bootstrap\n      #   make release\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v4\n"
  },
  {
    "path": ".github/workflows/conventional_commits.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/conventional_commits.yml\n\nname: Conventional commits\n\n# This GitHub CI Action enforces that pull request titles follow conventional commits.\n# More info at https://www.conventionalcommits.org.\n#\n# The Ory-wide defaults for commit titles and scopes are below.\n# Your repository can add/replace elements via a configuration file at the path below.\n# More info at https://github.com/ory/ci/blob/master/conventional_commit_config/README.md\n\non:\n  pull_request_target:\n    types:\n      - edited\n      - opened\n      - ready_for_review\n      - reopened\n  # pull_request: # for debugging, uses config in local branch but supports only Pull Requests from this repo\n\njobs:\n  main:\n    name: Validate PR title\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - id: config\n        uses: ory/ci/conventional_commit_config@master\n        with:\n          config_path: .github/conventional_commits.json\n          default_types: |\n            feat\n            fix\n            revert\n            docs\n            style\n            refactor\n            test\n            build\n            autogen\n            security\n            ci\n            chore\n          default_scopes: |\n            deps\n            docs\n          default_require_scope: false\n      - uses: amannn/action-semantic-pull-request@v6\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          types: ${{ steps.config.outputs.types }}\n          scopes: ${{ steps.config.outputs.scopes }}\n          requireScope: ${{ steps.config.outputs.requireScope }}\n          subjectPattern: ^(?![A-Z]).+$\n          subjectPatternError: |\n            The subject should start with a lowercase letter, yours is uppercase:\n            \"{subject}\"\n"
  },
  {
    "path": ".github/workflows/cve-scan.yaml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/server/.github/workflows/cve-scan.yaml\n\nname: Docker Image Scanners\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - \"master\"\n    tags:\n      - \"v*.*.*\"\n  pull_request:\n    branches:\n      - \"master\"\n\npermissions:\n  contents: read\n  security-events: write\n\njobs:\n  scanners:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Setup Env\n        id: vars\n        shell: bash\n        run: |\n          # Store values in local variables\n          SHA_SHORT=$(git rev-parse --short HEAD)\n          REPO_NAME=${{ github.event.repository.name }}\n\n          IMAGE_NAME=\"oryd/${REPO_NAME}:${SHA_SHORT}\"\n\n          # Output values for debugging\n          echo \"Values to be set:\"\n          echo \"SHA_SHORT:  ${SHA_SHORT}\"\n          echo \"REPO_NAME:  ${REPO_NAME}\"\n          echo \"IMAGE_NAME: ${IMAGE_NAME}\"\n\n          # Set GitHub Environment variables\n          echo \"SHA_SHORT=${SHA_SHORT}\" >> \"${GITHUB_ENV}\"\n          echo \"IMAGE_NAME=${IMAGE_NAME}\" >> \"${GITHUB_ENV}\"\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v4\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v4\n        with:\n          driver: docker\n      - name: Build images\n        shell: bash\n        run: |\n          IMAGE_TAG=\"${{ env.SHA_SHORT }}\" make docker\n\n      - name: Login to GitHub Container Registry\n        uses: docker/login-action@v4\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Configure Trivy\n        run: |\n          mkdir -p \"$HOME/.cache/trivy\"\n          echo \"TRIVY_USERNAME=${{ github.actor }}\" >> \"$GITHUB_ENV\"\n          echo \"TRIVY_PASSWORD=${{ secrets.GITHUB_TOKEN }}\" >> \"$GITHUB_ENV\"\n\n      - name: Anchore Scanner\n        uses: anchore/scan-action@v7\n        id: grype-scan\n        with:\n          image: ${{ env.IMAGE_NAME }}\n          fail-build: true\n          severity-cutoff: high\n          add-cpes-if-none: true\n      - name: Inspect action SARIF report\n        shell: bash\n        if: ${{ always() }}\n        run: |\n          echo \"::group::Anchore Scan Details\"\n          jq '.runs[0].results' ${{ steps.grype-scan.outputs.sarif }}\n          echo \"::endgroup::\"\n      - name: Anchore upload scan SARIF report\n        if: always()\n        uses: github/codeql-action/upload-sarif@v4\n        with:\n          sarif_file: ${{ steps.grype-scan.outputs.sarif }}\n      - name: Kubescape scanner\n        uses: kubescape/github-action@main\n        id: kubescape\n        with:\n          image: ${{ env.IMAGE_NAME }}\n          verbose: true\n          format: pretty-printer\n          # can't whitelist CVE yet: https://github.com/kubescape/kubescape/pull/1568\n          severityThreshold: critical\n      - name: Trivy Scanner\n        uses: aquasecurity/trivy-action@master\n        if: ${{ always() }}\n        with:\n          image-ref: ${{ env.IMAGE_NAME }}\n          format: \"table\"\n          exit-code: \"42\"\n          ignore-unfixed: true\n          vuln-type: \"os,library\"\n          severity: \"CRITICAL,HIGH\"\n          scanners: \"vuln,secret,misconfig\"\n        env:\n          TRIVY_SKIP_JAVA_DB_UPDATE: \"true\"\n          TRIVY_DISABLE_VEX_NOTICE: \"true\"\n          TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db\n\n      - name: Dockle Linter\n        uses: erzz/dockle-action@v1\n        if: ${{ always() }}\n        with:\n          image: ${{ env.IMAGE_NAME }}\n          exit-code: 42\n          failure-threshold: high\n      - name: Hadolint\n        uses: hadolint/hadolint-action@v3.3.0\n        id: hadolint\n        if: ${{ always() }}\n        with:\n          dockerfile: .docker/Dockerfile-build\n          verbose: true\n          format: \"json\"\n          failure-threshold: \"error\"\n      - name: View Hadolint results\n        if: ${{ always() }}\n        shell: bash\n        run: |\n          echo \"::group::Hadolint Scan Details\"\n          echo \"${HADOLINT_RESULTS}\" | jq '.'\n          echo \"::endgroup::\"\n"
  },
  {
    "path": ".github/workflows/format.yml",
    "content": "name: Format\n\non:\n  pull_request:\n  merge_group:\n\njobs:\n  format:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-go@v6\n        with:\n          check-latest: true\n          go-version-file: go.mod\n      - run: make format\n      - name: Indicate formatting issues\n        run: git diff HEAD --exit-code --color\n"
  },
  {
    "path": ".github/workflows/labels.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/labels.yml\n\nname: Synchronize Issue Labels\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n\njobs:\n  milestone:\n    if: github.repository_owner == 'ory'\n    name: Synchronize Issue Labels\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Synchronize Issue Labels\n        uses: ory/label-sync-action@v0\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          dry: false\n          forced: true\n"
  },
  {
    "path": ".github/workflows/milestone.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/server/.github/workflows/milestone.yml\n\nname: Generate and Publish Milestone Document\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 0 * * *\"\n\njobs:\n  milestone:\n    if: github.repository_owner == 'ory'\n    name: Generate and Publish Milestone Document\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          token: ${{ secrets.TOKEN_PRIVILEGED }}\n      - name: Milestone Documentation Generator\n        uses: ory/milestone-action@v0\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          outputFile: docs/docs/milestones.md\n      - name: Commit Milestone Documentation\n        uses: EndBug/add-and-commit@v9.1.4\n        with:\n          message: \"autogen(docs): update milestone document\"\n          author_name: aeneasr\n          author_email: \"3372410+aeneasr@users.noreply.github.com\"\n        env:\n          GITHUB_TOKEN: ${{ secrets.TOKEN_PRIVILEGED }}\n"
  },
  {
    "path": ".github/workflows/pm.yml",
    "content": "name: Synchronize with product board\n\non:\n  issues:\n    types:\n      - opened\n  pull_request:\n    types:\n      - opened\n      - ready_for_review\n\njobs:\n  automate:\n    if: github.event.pull_request.head.repo.fork == false\n    name: Add issue to project\n    runs-on: ubuntu-latest\n    timeout-minutes: 5\n    steps:\n      - uses: ory-corp/planning-automation-action@v0.2\n        with:\n          project: 5\n          organization: ory-corp\n          token: ${{ secrets.ORY_BOT_PAT }}\n          todoLabel: \"Needs Triage\"\n          statusName: Status\n          prStatusValue: \"Needs Triage\"\n          issueStatusValue: \"Needs Triage\"\n          includeEffort: \"false\"\n          monthlyMilestoneName: Roadmap Monthly\n          quarterlyMilestoneName: Roadmap\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "# AUTO-GENERATED, DO NOT EDIT!\n# Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/.github/workflows/stale.yml\n\nname: \"Close Stale Issues\"\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 0 * * *\"\n\njobs:\n  stale:\n    if: github.repository_owner == 'ory'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/stale@v10\n        with:\n          repo-token: ${{ secrets.GITHUB_TOKEN }}\n          stale-issue-message: |\n            Hello contributors!\n\n            I am marking this issue as stale as it has not received any engagement from the community or maintainers for a year. That does not imply that the issue has no merit! If you feel strongly about this issue\n\n            - open a PR referencing and resolving the issue;\n            - leave a comment on it and discuss ideas on how you could contribute towards resolving it;\n            - leave a comment and describe in detail why this issue is critical for your use case;\n            - open a new issue with updated details and a plan for resolving the issue.\n\n            Throughout its lifetime, Ory has received over 10.000 issues and PRs. To sustain that growth, we need to prioritize and focus on issues that are important to the community. A good indication of importance, and thus priority, is activity on a topic.\n\n            Unfortunately, [burnout](https://www.jeffgeerling.com/blog/2016/why-i-close-prs-oss-project-maintainer-notes) has become a [topic](https://opensource.guide/best-practices/#its-okay-to-hit-pause) of [concern](https://docs.brew.sh/Maintainers-Avoiding-Burnout) amongst open-source projects.\n\n            It can lead to severe personal and health issues as well as [opening](https://haacked.com/archive/2019/05/28/maintainer-burnout/) catastrophic [attack vectors](https://www.gradiant.org/en/blog/open-source-maintainer-burnout-as-an-attack-surface/).\n\n            The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone.\n\n            If this issue was marked as stale erroneously you can exempt it by adding the `backlog` label, assigning someone, or setting a milestone for it.\n\n            Thank you for your understanding and to anyone who participated in the conversation! And as written above, please do participate in the conversation if this topic is important to you!\n\n            Thank you 🙏✌️\n          stale-issue-label: \"stale\"\n          exempt-issue-labels: \"bug,blocking,docs,backlog\"\n          days-before-stale: 365\n          days-before-close: 30\n          exempt-milestones: true\n          exempt-assignees: true\n          only-pr-labels: \"stale\"\n"
  },
  {
    "path": ".gitignore",
    "content": "cover.out\n.idea/\ntmp/\nddl/\n.DS_Store\n/kratos\npackrd/\n*-packr.go\ndist/\nnode_modules\n.bin/\ntest/e2e/cypress/videos\ntest/e2e/cypress/screenshots\ntest/e2e/.bin\npkged.go\ncoverage.*\nschema.sql\n*.sqlite\nheap_profiler/\ngoroutine_dump/\ninflight_trace_dump/\n\ncontrib/quickstart/kratos/oidc\n\ne2e/*.log\ne2e/kratos.*.yml\ne2e/proxy.json\ne2e/cypress/downloads\n\n# Compiled Object files, Static and Dynamic libs (Shared Objects)\ninternal/httpclient/*.o\ninternal/httpclient/*.a\ninternal/httpclient/*.so\n\n# Folders\ninternal/httpclient/_obj\ninternal/httpclient/_test\n\n# Architecture specific extensions/prefixes\ninternal/httpclient/*.[568vq]\ninternal/httpclient/[568vq].out\n\ninternal/httpclient/*.cgo1.go\ninternal/httpclient/*.cgo2.c\ninternal/httpclient/_cgo_defun.c\ninternal/httpclient/_cgo_gotypes.go\ninternal/httpclient/_cgo_export.*\n\ninternal/httpclient/_testmain.go\n\ninternal/httpclient/*.exe\ninternal/httpclient/*.test\ninternal/httpclient/*.prof\n\ntest/e2e/hydra-login-consent/hydra-login-consent\n\npersistence/sql/migrations/sql/schema.sql\n\ntest/e2e/hydra-kratos-login-consent/hydra-kratos-login-consent\n\n*.log\ntest/e2e/proxy.json\ntest/e2e/kratos.*.yml\n\n# VSCode debug artifact\n__debug_bin\n.debug.sqlite.db\n.last-run.json"
  },
  {
    "path": ".golangci.yml",
    "content": "version: \"2\"\n\nlinters:\n  enable:\n    - gosec\n    - govet\n    - errcheck\n    - ineffassign\n    - staticcheck\n    - unused\n  disable:\n    - bodyclose # too many false negatives\n\n  settings:\n    gosec:\n      excludes:\n        - G101\n        - G117\n        - G306\n        - G704\n        - G705\n  exclusions:\n    rules:\n      - linters:\n          - staticcheck\n        text: \"SA1019\" # we do use deprecated APIs on purpose sometimes\n"
  },
  {
    "path": ".goreleaser.yml",
    "content": "version: 2\n\nincludes:\n  - from_url:\n      url: https://raw.githubusercontent.com/ory/xgoreleaser/master/build.tmpl.yml\n\nvariables:\n  brew_name: kratos\n  brew_description: \"The Ory Identity Platform (Ory Kratos)\"\n  buildinfo_hash: \"github.com/ory/kratos/driver/config.Commit\"\n  buildinfo_tag: \"github.com/ory/kratos/driver/config.Version\"\n  buildinfo_date: \"github.com/ory/kratos/driver/config.Date\"\n  dockerfile_alpine: \".docker/Dockerfile-alpine\"\n  dockerfile_static: \".docker/Dockerfile-distroless-static\"\n\nproject_name: kratos\n\nafter:\n  hooks:\n  - cmd: \"bash <(curl -s https://raw.githubusercontent.com/ory/xgoreleaser/master/docs.sh)\"\n    env:\n      - \"TAG_VERSION={{ .Tag }}\"\n      - \"DOCS_VERSION={{ .Major }}.{{ .Minor }}\"\n"
  },
  {
    "path": ".grype.yaml",
    "content": "#only-fixed: true\nignore:\n  - vulnerability: GHSA-c5pj-mqfh-rvc3 # https://github.com/advisories/GHSA-c5pj-mqfh-rvc3\n  - vulnerability: CVE-2015-5237\n  - vulnerability: CVE-2022-30065\n  - vulnerability: CVE-2023-2650\n  - vulnerability: CVE-2023-4813\n  - vulnerability: CVE-2023-4806\n  - vulnerability: CVE-2025-0395 # no fix available\n"
  },
  {
    "path": ".mailmap",
    "content": "Aeneas Rekkas <aeneas@ory.sh> <arekkas@users.noreply.github.com>\nAeneas Rekkas <aeneas@ory.sh> <3372410+aeneasr@users.noreply.github.com>\nAeneas Rekkas <aeneas@ory.sh> <aeneasr@users.noreply.github.com>\nAeneas Rekkas <aeneas@ory.sh> <aeneas@ory.sh>\nAjay Kelkar <ajay.kelkar@ory.sh> <ajaykelkar@outlook.com>\nAlano Terblanche <alano@ory.sh> <18033717+Benehiko@users.noreply.github.com>\nAlano Terblanche <alano@ory.sh> <alanoterblanche@gmail.com>\nJonas Hungershausen <jonas.hungershausen@ory.sh> <jonas.hungershausen@gmail.com>\nMatt Bonnell <matt.bonnell@icloud.com> <64976795+mbonnell-wish@users.noreply.github.com>\nNick Ufer <nick@ufer.dev> <me@nick-ufer.de>\nNick Ufer <nick@ufer.dev> <nick@ufer.dev>\nPatrik Neu <patrik@ory.sh> <zepatrik@users.noreply.github.com>\nPatrik Neu <patrik@ory.sh> <patrik@ory.sh>\n"
  },
  {
    "path": ".nancy-ignore",
    "content": "# HashiCorp Consul related CVEs\n# Consul is not used but instead an indirect dependency to the pf13/viper config backend which is not actively used.\nCVE-2020-7219\nCVE-2019-12291\nCVE-2020-7955\nCVE-2020-12797\nCVE-2020-13250\nCVE-2020-13170\n# End HashiCorp Consul\n\n# etcd issues - can be ignored because etcd is not used.\nCVE-2020-15114\nCVE-2020-15136\nCVE-2020-15115\n# end\n"
  },
  {
    "path": ".nvmrc",
    "content": "v16.19.0\n"
  },
  {
    "path": ".orycli.yml",
    "content": "project: kratos\n\npre_release_hooks:\n  - make sdk\n  - ./script/render-schemas.sh\n  - git config --unset user.email\n  - git config --unset user.name\n"
  },
  {
    "path": ".prettierignore",
    "content": ".schema/\n.github/ISSUE_TEMPLATE\noryx"
  },
  {
    "path": ".reference-ignore",
    "content": "**/node_modules\ndocs\nCHANGELOG.md\n"
  },
  {
    "path": ".reports/dep-licenses.csv",
    "content": "\"module name\",\"licenses\"\n\n\"github.com/arbovm/levenshtein\",\"BSD-3-Clause\"\n\"github.com/ory/x\",\"Apache-2.0\"\n\"github.com/stretchr/testify\",\"MIT\"\n\"go.opentelemetry.io/otel/sdk\",\"Apache-2.0\"\n\"golang.org/x/text\",\"BSD-3-Clause\"\n\n"
  },
  {
    "path": ".schema/api.openapi.json",
    "content": "{\n  \"components\": {\n    \"responses\": {\n      \"emptyResponse\": {\n        \"description\": \"Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\\ntypically 201.\"\n      },\n      \"errorContainer\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"$ref\": \"#/components/schemas/errorContainer\"\n            }\n          }\n        },\n        \"description\": \"User-facing error response\"\n      },\n      \"identityList\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"items\": {\n                \"$ref\": \"#/components/schemas/Identity\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        },\n        \"description\": \"A list of identities.\"\n      },\n      \"identityResponse\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"$ref\": \"#/components/schemas/Identity\"\n            }\n          }\n        },\n        \"description\": \"A single identity.\"\n      }\n    },\n    \"schemas\": {\n      \"CompleteSelfServiceBrowserSettingsProfileStrategyFlow\": {\n        \"description\": \"nolint:deadcode,unused\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The Anti-CSRF Token\\n\\nThis token is only required when performing browser flows.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"Traits contains all of the identity's traits.\",\n            \"type\": \"object\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"CompleteSelfServiceLoginFlowWithPasswordMethod\": {\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"identifier\": {\n            \"description\": \"Identifier is the email or username of the user trying to log in.\",\n            \"type\": \"string\"\n          },\n          \"password\": {\n            \"description\": \"The user's password.\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"CompleteSelfServiceSettingsFlowWithPasswordMethod\": {\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\\n\\ntype: string\",\n            \"type\": \"string\"\n          },\n          \"password\": {\n            \"description\": \"Password is the updated password\\n\\ntype: string\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"password\"\n        ],\n        \"type\": \"object\"\n      },\n      \"CreateIdentity\": {\n        \"properties\": {\n          \"schema_id\": {\n            \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_url`.\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"schema_id\",\n          \"traits\"\n        ],\n        \"type\": \"object\"\n      },\n      \"CreateRecoveryLink\": {\n        \"properties\": {\n          \"expires_in\": {\n            \"description\": \"Link Expires In\\n\\nThe recovery link will expire at that point in time. Defaults to the configuration value of\\n`selfservice.flows.recovery.request_lifespan`.\",\n            \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n            \"type\": \"string\"\n          },\n          \"identity_id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          }\n        },\n        \"required\": [\n          \"identity_id\"\n        ],\n        \"type\": \"object\"\n      },\n      \"CredentialsType\": {\n        \"description\": \"and so on.\",\n        \"title\": \"CredentialsType  represents several different credential types, like password credentials, passwordless credentials,\",\n        \"type\": \"string\"\n      },\n      \"ID\": {\n        \"format\": \"int64\",\n        \"type\": \"integer\"\n      },\n      \"Identity\": {\n        \"properties\": {\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"recovery_addresses\": {\n            \"description\": \"RecoveryAddresses contains all the addresses that can be used to recover an identity.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/RecoveryAddress\"\n            },\n            \"type\": \"array\",\n            \"x-omitempty\": true\n          },\n          \"schema_id\": {\n            \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\",\n            \"type\": \"string\"\n          },\n          \"schema_url\": {\n            \"description\": \"SchemaURL is the URL of the endpoint where the identity's traits schema can be fetched from.\\n\\nformat: url\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"$ref\": \"#/components/schemas/Traits\"\n          },\n          \"verifiable_addresses\": {\n            \"description\": \"VerifiableAddresses contains all the addresses that can be verified by the user.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/VerifiableAddress\"\n            },\n            \"type\": \"array\",\n            \"x-omitempty\": true\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"schema_id\",\n          \"schema_url\",\n          \"traits\"\n        ],\n        \"type\": \"object\"\n      },\n      \"NullTime\": {\n        \"format\": \"date-time\",\n        \"title\": \"NullTime implements sql.NullTime functionality.\",\n        \"type\": \"string\"\n      },\n      \"RecoveryAddress\": {\n        \"properties\": {\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"value\": {\n            \"type\": \"string\"\n          },\n          \"via\": {\n            \"$ref\": \"#/components/schemas/RecoveryAddressType\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"value\",\n          \"via\"\n        ],\n        \"type\": \"object\"\n      },\n      \"RecoveryAddressType\": {\n        \"type\": \"string\"\n      },\n      \"State\": {\n        \"type\": \"string\"\n      },\n      \"Traits\": {\n        \"type\": \"object\"\n      },\n      \"Type\": {\n        \"description\": \"The flow type can either be `api` or `browser`.\",\n        \"title\": \"Type is the flow type.\",\n        \"type\": \"string\"\n      },\n      \"UUID\": {\n        \"format\": \"uuid4\",\n        \"type\": \"string\"\n      },\n      \"UpdateIdentity\": {\n        \"properties\": {\n          \"schema_id\": {\n            \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. If set\\nwill update the Identity's SchemaID.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_id`.\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"traits\"\n        ],\n        \"type\": \"object\"\n      },\n      \"VerifiableAddress\": {\n        \"properties\": {\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"status\": {\n            \"$ref\": \"#/components/schemas/VerifiableAddressStatus\"\n          },\n          \"value\": {\n            \"type\": \"string\"\n          },\n          \"verified\": {\n            \"type\": \"boolean\"\n          },\n          \"verified_at\": {\n            \"$ref\": \"#/components/schemas/NullTime\"\n          },\n          \"via\": {\n            \"$ref\": \"#/components/schemas/VerifiableAddressType\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"value\",\n          \"verified\",\n          \"via\",\n          \"status\"\n        ],\n        \"type\": \"object\"\n      },\n      \"VerifiableAddressStatus\": {\n        \"type\": \"string\"\n      },\n      \"VerifiableAddressType\": {\n        \"type\": \"string\"\n      },\n      \"completeSelfServiceBrowserSettingsOIDCFlowPayload\": {\n        \"properties\": {\n          \"flow\": {\n            \"description\": \"Flow ID is the flow's ID.\\n\\nin: query\",\n            \"type\": \"string\"\n          },\n          \"link\": {\n            \"description\": \"Link this provider\\n\\nEither this or `unlink` must be set.\\n\\ntype: string\\nin: body\",\n            \"type\": \"string\"\n          },\n          \"unlink\": {\n            \"description\": \"Unlink this provider\\n\\nEither this or `link` must be set.\\n\\ntype: string\\nin: body\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"completeSelfServiceRecoveryFlowWithLinkMethod\": {\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"email\": {\n            \"description\": \"Email to Recover\\n\\nNeeds to be set when initiating the flow. If the email is a registered\\nrecovery email, a recovery link will be sent. If the email is not known,\\na email with details on what happened will be sent instead.\\n\\nformat: email\\nin: body\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"completeSelfServiceVerificationFlowWithLinkMethod\": {\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"email\": {\n            \"description\": \"Email to Verify\\n\\nNeeds to be set when initiating the flow. If the email is a registered\\nverification email, a verification link will be sent. If the email is not known,\\na email with details on what happened will be sent instead.\\n\\nformat: email\\nin: body\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"errorContainer\": {\n        \"properties\": {\n          \"errors\": {\n            \"description\": \"Errors in the container\",\n            \"items\": {\n              \"type\": \"object\"\n            },\n            \"type\": \"array\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"errors\"\n        ],\n        \"type\": \"object\"\n      },\n      \"genericError\": {\n        \"description\": \"Error responses are sent when an error (e.g. unauthorized, bad request, ...) occurred.\",\n        \"properties\": {\n          \"error\": {\n            \"$ref\": \"#/components/schemas/genericErrorPayload\"\n          }\n        },\n        \"title\": \"Error response\",\n        \"type\": \"object\"\n      },\n      \"genericErrorPayload\": {\n        \"properties\": {\n          \"code\": {\n            \"description\": \"Code represents the error status code (404, 403, 401, ...).\",\n            \"example\": 404,\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          },\n          \"debug\": {\n            \"description\": \"Debug contains debug information. This is usually not available and has to be enabled.\",\n            \"example\": \"The database adapter was unable to find the element\",\n            \"type\": \"string\"\n          },\n          \"details\": {\n            \"additionalProperties\": true,\n            \"type\": \"object\"\n          },\n          \"message\": {\n            \"type\": \"string\"\n          },\n          \"reason\": {\n            \"type\": \"string\"\n          },\n          \"request\": {\n            \"type\": \"string\"\n          },\n          \"status\": {\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"healthNotReadyStatus\": {\n        \"properties\": {\n          \"errors\": {\n            \"additionalProperties\": {\n              \"type\": \"string\"\n            },\n            \"description\": \"Errors contains a list of errors that caused the not ready status.\",\n            \"type\": \"object\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"healthStatus\": {\n        \"properties\": {\n          \"status\": {\n            \"description\": \"Status always contains \\\"ok\\\".\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"jsonSchema\": {\n        \"description\": \"Raw JSON Schema\",\n        \"type\": \"object\"\n      },\n      \"loginFlow\": {\n        \"description\": \"This object represents a login flow. A login flow is initiated at the \\\"Initiate Login API / Browser Flow\\\"\\nendpoint by a client.\\n\\nOnce a login flow is completed successfully, a session cookie or session token will be issued.\",\n        \"properties\": {\n          \"active\": {\n            \"$ref\": \"#/components/schemas/CredentialsType\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\\na new flow has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"forced\": {\n            \"description\": \"Forced stores whether this login flow should enforce re-authentication.\",\n            \"type\": \"boolean\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the flow started.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          },\n          \"ui\": {\n            \"$ref\": \"#/components/schemas/uiContainer\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"type\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"ui\"\n        ],\n        \"title\": \"Login Flow\",\n        \"type\": \"object\"\n      },\n      \"loginViaApiResponse\": {\n        \"description\": \"The Response for Login Flows via API\",\n        \"properties\": {\n          \"session\": {\n            \"$ref\": \"#/components/schemas/session\"\n          },\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"session_token\",\n          \"session\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryFlow\": {\n        \"description\": \"This request is used when an identity wants to recover their account.\\n\\nWe recommend reading the [Account Recovery Documentation](../self-service/flows/password-reset-account-recovery)\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the request expires. If the user still wishes to update the setting,\\na new request has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the request occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"methods\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/recoveryFlowMethod\"\n            },\n            \"description\": \"Methods contains context for all account recovery methods. If a registration request has been\\nprocessed, but for example the password is incorrect, this will contain error messages.\",\n            \"type\": \"object\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"$ref\": \"#/components/schemas/State\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"methods\",\n          \"state\"\n        ],\n        \"title\": \"A Recovery Flow\",\n        \"type\": \"object\"\n      },\n      \"recoveryFlowMethod\": {\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/recoveryFlowMethodConfig\"\n          },\n          \"method\": {\n            \"description\": \"Method contains the request credentials type.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"config\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryFlowMethodConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryFlowMethodConfigPayload\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryLink\": {\n        \"properties\": {\n          \"expires_at\": {\n            \"description\": \"Recovery Link Expires At\\n\\nThe timestamp when the recovery link expires.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"recovery_link\": {\n            \"description\": \"Recovery Link\\n\\nThis link can be used to recover the account.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"recovery_link\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationFlow\": {\n        \"properties\": {\n          \"active\": {\n            \"$ref\": \"#/components/schemas/CredentialsType\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\\na new flow has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the flow occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"methods\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/registrationFlowMethod\"\n            },\n            \"description\": \"Methods contains context for all enabled registration methods. If a registration flow has been\\nprocessed, but for example the password is incorrect, this will contain error messages.\",\n            \"type\": \"object\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"methods\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationFlowMethod\": {\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/registrationFlowMethodConfig\"\n          },\n          \"method\": {\n            \"$ref\": \"#/components/schemas/CredentialsType\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"config\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationFlowMethodConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          },\n          \"providers\": {\n            \"description\": \"Providers is set for the \\\"oidc\\\" registration method.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/uiNodes\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationFlowMethodConfigPayload\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          },\n          \"providers\": {\n            \"description\": \"Providers is set for the \\\"oidc\\\" registration method.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/uiNodes\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationViaApiResponse\": {\n        \"description\": \"The Response for Registration Flows via API\",\n        \"properties\": {\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/Identity\"\n          },\n          \"session\": {\n            \"$ref\": \"#/components/schemas/session\"\n          },\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nThis field is only set when the session hook is configured as a post-registration hook.\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"session_token\",\n          \"identity\"\n        ],\n        \"type\": \"object\"\n      },\n      \"revokeSession\": {\n        \"properties\": {\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nInvalidate this session token.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"session_token\"\n        ],\n        \"type\": \"object\"\n      },\n      \"session\": {\n        \"properties\": {\n          \"active\": {\n            \"type\": \"boolean\"\n          },\n          \"authenticated_at\": {\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/Identity\"\n          },\n          \"issued_at\": {\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"expires_at\",\n          \"authenticated_at\",\n          \"issued_at\",\n          \"identity\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsFlow\": {\n        \"description\": \"This flow is used when an identity wants to update settings\\n(e.g. profile data, passwords, ...) in a selfservice manner.\\n\\nWe recommend reading the [User Settings Documentation](../self-service/flows/user-settings)\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to update the setting,\\na new flow has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/Identity\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the flow occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"methods\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/settingsFlowMethod\"\n            },\n            \"description\": \"Methods contains context for all enabled registration methods. If a settings flow has been\\nprocessed, but for example the first name is empty, this will contain error messages.\",\n            \"type\": \"object\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"$ref\": \"#/components/schemas/State\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"methods\",\n          \"identity\",\n          \"state\"\n        ],\n        \"title\": \"Flow represents a Settings Flow\",\n        \"type\": \"object\"\n      },\n      \"settingsFlowMethod\": {\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/settingsFlowMethodConfig\"\n          },\n          \"method\": {\n            \"description\": \"Method is the name of this flow method.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"config\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsFlowMethodConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsFlowMethodConfigPayload\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsProfileFormConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsViaApiResponse\": {\n        \"description\": \"The Response for Settings Flows via API\",\n        \"properties\": {\n          \"flow\": {\n            \"$ref\": \"#/components/schemas/settingsFlow\"\n          },\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/Identity\"\n          }\n        },\n        \"required\": [\n          \"flow\",\n          \"identity\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiContainer\": {\n        \"description\": \"Container represents a HTML Form. The container can work with both HTTP Form and JSON requests\",\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiNode\": {\n        \"description\": \"Nodes are represented as HTML elements or their native UI equivalents. For example,\\na node can be an `\\u003cimg\\u003e` tag, or an `\\u003cinput element\\u003e` but also `some plain text`.\",\n        \"properties\": {\n          \"attributes\": {\n            \"$ref\": \"#/components/schemas/uiNodeAttributes\"\n          },\n          \"group\": {\n            \"$ref\": \"#/components/schemas/uiNodeGroup\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/uiNodeType\"\n          }\n        },\n        \"required\": [\n          \"type\",\n          \"group\",\n          \"attributes\",\n          \"messages\"\n        ],\n        \"title\": \"Node represents a flow's nodes\",\n        \"type\": \"object\"\n      },\n      \"uiNodeAnchorAttributes\": {\n        \"properties\": {\n          \"href\": {\n            \"description\": \"The link's href (destination) URL.\\n\\nformat: uri\",\n            \"type\": \"string\"\n          },\n          \"title\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          }\n        },\n        \"required\": [\n          \"href\",\n          \"title\"\n        ],\n        \"title\": \"AnchorAttributes represents the attributes of an anchor node.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeAttributes\": {\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/components/schemas/uiNodeInputAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeTextAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeImageAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeAnchorAttributes\"\n          }\n        ],\n        \"title\": \"Attributes represents a list of attributes (e.g. `href=\\\"foo\\\"` for links).\"\n      },\n      \"uiNodeGroup\": {\n        \"type\": \"string\"\n      },\n      \"uiNodeImageAttributes\": {\n        \"properties\": {\n          \"src\": {\n            \"description\": \"The image's source URL.\\n\\nformat: uri\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"src\"\n        ],\n        \"title\": \"ImageAttributes represents the attributes of an image node.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeInputAttributeType\": {\n        \"type\": \"string\"\n      },\n      \"uiNodeInputAttributes\": {\n        \"description\": \"InputAttributes represents the attributes of an input node\",\n        \"properties\": {\n          \"disabled\": {\n            \"description\": \"Sets the input's disabled field to true or false.\",\n            \"type\": \"boolean\"\n          },\n          \"label\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          },\n          \"name\": {\n            \"description\": \"The input's element name.\",\n            \"type\": \"string\"\n          },\n          \"pattern\": {\n            \"description\": \"The input's pattern.\",\n            \"type\": \"string\"\n          },\n          \"required\": {\n            \"description\": \"Mark this input field as required.\",\n            \"type\": \"boolean\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/uiNodeInputAttributeType\"\n          },\n          \"value\": {\n            \"$ref\": \"#/components/schemas/uiNodeInputAttributesValue\",\n            \"description\": \"The input's value.\",\n            \"nullable\": true\n          }\n        },\n        \"required\": [\n          \"name\",\n          \"type\",\n          \"disabled\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiNodeInputAttributesValue\": {\n        \"oneOf\": [\n          {\n            \"type\": \"string\"\n          },\n          {\n            \"type\": \"number\"\n          },\n          {\n            \"type\": \"boolean\"\n          }\n        ]\n      },\n      \"uiNodeTextAttributes\": {\n        \"properties\": {\n          \"text\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          }\n        },\n        \"required\": [\n          \"text\"\n        ],\n        \"title\": \"TextAttributes represents the attributes of a text node.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeType\": {\n        \"type\": \"string\"\n      },\n      \"uiNodes\": {\n        \"items\": {\n          \"$ref\": \"#/components/schemas/uiNode\"\n        },\n        \"type\": \"array\"\n      },\n      \"uiText\": {\n        \"properties\": {\n          \"context\": {\n            \"description\": \"The message's context. Useful when customizing messages.\",\n            \"type\": \"object\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/ID\"\n          },\n          \"text\": {\n            \"description\": \"The message text. Written in american english.\",\n            \"type\": \"string\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/uiTextType\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"text\",\n          \"type\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiTextType\": {\n        \"type\": \"string\"\n      },\n      \"uiTexts\": {\n        \"items\": {\n          \"$ref\": \"#/components/schemas/uiText\"\n        },\n        \"type\": \"array\"\n      },\n      \"verificationFlow\": {\n        \"description\": \"Used to verify an out-of-band communication\\nchannel such as an email address or a phone number.\\n\\nFor more information head over to: https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the request expires. If the user still wishes to verify the address,\\na new request has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the request occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"methods\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/verificationFlowMethod\"\n            },\n            \"description\": \"Methods contains context for all account verification methods. If a registration request has been\\nprocessed, but for example the password is incorrect, this will contain error messages.\",\n            \"type\": \"object\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"$ref\": \"#/components/schemas/State\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"type\",\n          \"methods\",\n          \"state\"\n        ],\n        \"title\": \"A Verification Flow\",\n        \"type\": \"object\"\n      },\n      \"verificationFlowMethod\": {\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/verificationFlowMethodConfig\"\n          },\n          \"method\": {\n            \"description\": \"Method contains the request credentials type.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"config\"\n        ],\n        \"type\": \"object\"\n      },\n      \"verificationFlowMethodConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"verificationFlowMethodConfigPayload\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"version\": {\n        \"properties\": {\n          \"version\": {\n            \"description\": \"Version is the service's version.\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      }\n    },\n    \"securitySchemes\": {\n      \"sessionCookie\": {\n        \"in\": \"cookie\",\n        \"name\": \"ory_kratos_session\",\n        \"type\": \"apiKey\"\n      },\n      \"sessionToken\": {\n        \"in\": \"header\",\n        \"name\": \"X-Session-Token\",\n        \"type\": \"apiKey\"\n      }\n    }\n  },\n  \"info\": {\n    \"contact\": {\n      \"email\": \"hi@ory.sh\"\n    },\n    \"description\": \"Documentation for all public and administrative Ory Kratos APIs. Public and administrative APIs\\nare exposed on different ports. Public APIs can face the public internet without any protection\\nwhile administrative APIs should never be exposed without prior authorization. To protect\\nthe administative API port you should use something like Nginx, Ory Oathkeeper, or any other\\ntechnology capable of authorizing incoming requests.\\n\",\n    \"license\": {\n      \"name\": \"Apache 2.0\"\n    },\n    \"title\": \"Ory Kratos API\",\n    \"version\": \"\"\n  },\n  \"openapi\": \"3.0.3\",\n  \"paths\": {\n    \"/health/alive\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a HTTP 200 status code when Ory Kratos is accepting incoming\\nHTTP requests. This status does currently not include checks whether the database connection is working.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of this service, the health status will never\\nrefer to the cluster state, only to a single instance.\",\n        \"operationId\": \"isAlive\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"status\": {\n                      \"description\": \"Always \\\"ok\\\".\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"required\": [\n                    \"status\"\n                  ],\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Ory Kratos is ready to accept connections.\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Check HTTP Server Status\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/health/ready\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a HTTP 200 status code when Ory Kratos is up running and the environment dependencies (e.g.\\nthe database) are responsive as well.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of Ory Kratos, the health status will never\\nrefer to the cluster state, only to a single instance.\",\n        \"operationId\": \"isReady\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"status\": {\n                      \"description\": \"Always \\\"ok\\\".\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"required\": [\n                    \"status\"\n                  ],\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Ory Kratos is ready to accept requests.\"\n          },\n          \"503\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"errors\": {\n                      \"additionalProperties\": {\n                        \"type\": \"string\"\n                      },\n                      \"description\": \"Errors contains a list of errors that caused the not ready status.\",\n                      \"type\": \"object\"\n                    }\n                  },\n                  \"required\": [\n                    \"errors\"\n                  ],\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Ory Kratos is not yet ready to accept requests.\"\n          }\n        },\n        \"summary\": \"Check HTTP Server and Database Status\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/identities\": {\n      \"get\": {\n        \"description\": \"Lists all identities. Does not support search at the moment.\\n\\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"listIdentities\",\n        \"parameters\": [\n          {\n            \"description\": \"Items per Page\\n\\nThis is the number of items per page.\",\n            \"in\": \"query\",\n            \"name\": \"per_page\",\n            \"schema\": {\n              \"default\": 100,\n              \"format\": \"int64\",\n              \"maximum\": 500,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Pagination Page\",\n            \"in\": \"query\",\n            \"name\": \"page\",\n            \"schema\": {\n              \"default\": 0,\n              \"format\": \"int64\",\n              \"minimum\": 0,\n              \"type\": \"integer\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/identityList\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"List Identities\",\n        \"tags\": [\n          \"admin\"\n        ]\n      },\n      \"post\": {\n        \"description\": \"This endpoint creates an identity. It is NOT possible to set an identity's credentials (password, ...)\\nusing this method! A way to achieve that will be introduced in the future.\\n\\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"createIdentity\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CreateIdentity\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"201\": {\n            \"$ref\": \"#/components/responses/identityResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"409\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Create an Identity\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/identities/{id}\": {\n      \"delete\": {\n        \"description\": \"Calling this endpoint irrecoverably and permanently deletes the identity given its ID. This action can not be undone.\\nThis endpoint returns 204 when the identity was deleted or when the identity was not found, in which case it is\\nassumed that is has been deleted already.\\n\\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"deleteIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the identity's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Delete an Identity\",\n        \"tags\": [\n          \"admin\"\n        ]\n      },\n      \"get\": {\n        \"description\": \"Learn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"getIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of identity you want to get\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/identityResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get an Identity\",\n        \"tags\": [\n          \"admin\"\n        ]\n      },\n      \"put\": {\n        \"description\": \"This endpoint updates an identity. It is NOT possible to set an identity's credentials (password, ...)\\nusing this method! A way to achieve that will be introduced in the future.\\n\\nThe full identity payload (except credentials) is expected. This endpoint does not support patching.\\n\\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"updateIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of identity you want to update\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/UpdateIdentity\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/identityResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Update an Identity\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/metrics/prometheus\": {\n      \"get\": {\n        \"description\": \"```\\nmetadata:\\nannotations:\\nprometheus.io/port: \\\"4434\\\"\\nprometheus.io/path: \\\"/metrics/prometheus\\\"\\n```\",\n        \"operationId\": \"prometheus\",\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          }\n        },\n        \"summary\": \"Get snapshot metrics from the Kratos service. If you're using k8s, you can then add annotations to\\nyour deployment like so:\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/recovery/link\": {\n      \"post\": {\n        \"description\": \"This endpoint creates a recovery link which should be given to the user in order for them to recover\\n(or activate) their account.\",\n        \"operationId\": \"createRecoveryLink\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CreateRecoveryLink\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryLink\"\n                }\n              }\n            },\n            \"description\": \"recoveryLink\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Create a Recovery Link\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/schemas/{id}\": {\n      \"get\": {\n        \"description\": \"Get a Traits Schema Definition\",\n        \"operationId\": \"getSchema\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of schema you want to get\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/jsonSchema\"\n                }\n              }\n            },\n            \"description\": \"jsonSchema\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/browser/flows/logout\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a logout flow.\\n\\n\\u003e This endpoint is NOT INTENDED for API clients and only works\\nwith browsers (Chrome, Firefox, ...).\\n\\nOn successful logout, the browser will be redirected (HTTP 302 Found) to the `return_to` parameter of the initial request\\nor fall back to `urls.default_return_to`.\\n\\nMore information can be found at [Ory Kratos User Logout Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-logout).\",\n        \"operationId\": \"initializeSelfServiceBrowserLogoutFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Browser-Based Logout User Flow\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/browser/flows/registration/strategies/oidc/settings/connections\": {\n      \"post\": {\n        \"description\": \"This endpoint completes a browser-based settings flow. This is usually achieved by POSTing data to this\\nendpoint.\\n\\n\\u003e This endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...) and HTML Forms.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"completeSelfServiceBrowserSettingsOIDCSettingsFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete the Browser-Based Settings Flow for the OpenID Connect Strategy\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/errors\": {\n      \"get\": {\n        \"description\": \"This endpoint returns the error associated with a user-facing self service errors.\\n\\nThis endpoint supports stub values to help you implement the error UI:\\n\\n`?error=stub:500` - returns a stub 500 (Internal Server Error) error.\\n\\nMore information can be found at [Ory Kratos User User Facing Error Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors).\",\n        \"operationId\": \"getSelfServiceError\",\n        \"parameters\": [\n          {\n            \"description\": \"Error is the container's ID\",\n            \"in\": \"query\",\n            \"name\": \"error\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/errorContainer\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get User-Facing Self-Service Errors\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/login/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a login flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\\nwill be returned unless the URL query parameter `?refresh=true` is set.\\n\\nTo fetch an existing login flow call `/self-service/login/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks, including CSRF login attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"initializeSelfServiceLoginViaAPIFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"Refresh a login session\\n\\nIf set to true, this will refresh an existing login session by\\nasking the user to sign in again. This will reset the\\nauthenticated_at time of the session.\",\n            \"in\": \"query\",\n            \"name\": \"refresh\",\n            \"schema\": {\n              \"type\": \"boolean\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Login Flow for API clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/login/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user login flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\\n`?refresh=true` was set.\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"initializeSelfServiceLoginViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Login Flow for browsers\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/login/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a login flow's context with, for example, error details and other information.\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"getSelfServiceLoginFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Login Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/login?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get Login Flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/login/methods/password\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a login flow by sending an identity's identifier and password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI flows expect `application/json` to be sent in the body and responds with\\nHTTP 200 and a application/json body with the session token on success;\\nHTTP 302 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\n\\nBrowser flows expect `application/x-www-form-urlencoded` to be sent in the body and responds with\\na HTTP 302 redirect to the post/after login URL or the `return_to` value if it was set and if the login succeeded;\\na HTTP 302 redirect to the login UI URL with the flow ID containing the validation errors otherwise.\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"completeSelfServiceLoginFlowWithPasswordMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"The Flow ID\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CompleteSelfServiceLoginFlowWithPasswordMethod\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CompleteSelfServiceLoginFlowWithPasswordMethod\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginViaApiResponse\"\n                }\n              }\n            },\n            \"description\": \"loginViaApiResponse\"\n          },\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete Login Flow with Username/Email Password Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/recovery/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a recovery flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error.\\n\\nTo fetch an existing recovery flow call `/self-service/recovery/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery.mdx).\",\n        \"operationId\": \"initializeSelfServiceRecoveryViaAPIFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Recovery Flow for API Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/recovery/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based account recovery flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.recovery.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists, the browser is returned to the configured return URL.\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery.mdx).\",\n        \"operationId\": \"initializeSelfServiceRecoveryViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Recovery Flow for Browser Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/recovery/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a recovery flow's context with, for example, error details and other information.\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery.mdx).\",\n        \"operationId\": \"getSelfServiceRecoveryFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Flow ID\\n\\nThe value for this parameter comes from `request` URL Query parameter sent to your\\napplication (e.g. `/recovery?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get information about a recovery flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/recovery/methods/link\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a recovery flow using the link method. This endpoint\\nbehaves differently for API and browser flows and has several states:\\n\\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\\nand works with API- and Browser-initiated flows.\\nFor API clients it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\\nand a HTTP 302 Found redirect with a fresh recovery flow if the flow was otherwise invalid (e.g. expired).\\nFor Browser clients it returns a HTTP 302 Found redirect to the Recovery UI URL with the Recovery Flow ID appended.\\n`sent_email` is the success state after `choose_method` and allows the user to request another recovery email. It\\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\\\"sending a recovery link\\\")\\ndoes not have any API capabilities. The server responds with a HTTP 302 Found redirect either to the Settings UI URL\\n(if the link was valid) and instructs the user to update their password, or a redirect to the Recover UI URL with\\na new Recovery Flow ID which contains an error message that the recovery link was invalid.\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery.mdx).\",\n        \"operationId\": \"completeSelfServiceRecoveryFlowWithLinkMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Recovery Token\\n\\nThe recovery token which completes the recovery request. If the token\\nis invalid (e.g. expired) an error will be shown to the end-user.\",\n            \"in\": \"query\",\n            \"name\": \"token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The Flow ID\\n\\nformat: uuid\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/completeSelfServiceRecoveryFlowWithLinkMethod\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/completeSelfServiceRecoveryFlowWithLinkMethod\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete Recovery Flow with Link Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/registration/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\\nwill be returned unless the URL query parameter `?refresh=true` is set.\\n\\nTo fetch an existing registration flow call `/self-service/registration/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"initializeSelfServiceRegistrationViaAPIFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Registration Flow for API clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/registration/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user registration flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.registration.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\\n`?refresh=true` was set.\\n\\n:::note\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"initializeSelfServiceRegistrationViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Registration Flow for browsers\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/registration/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a registration flow's context with, for example, error details and other information.\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"getSelfServiceRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Registration Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/registration?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get Registration Flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/registration/methods/password\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a registration flow by sending an identity's traits and password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and a application/json body with the created identity success - if the session hook is configured the\\n`session` and `session_token` will also be included;\\nHTTP 302 redirect to a fresh registration flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\n\\nBrowser flows expect `application/x-www-form-urlencoded` to be sent in the body and responds with\\na HTTP 302 redirect to the post/after registration URL or the `return_to` value if it was set and if the registration succeeded;\\na HTTP 302 redirect to the registration UI URL with the flow ID containing the validation errors otherwise.\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"completeSelfServiceRegistrationFlowWithPasswordMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Flow is flow ID.\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"type\": \"object\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"type\": \"object\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Payload\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationViaApiResponse\"\n                }\n              }\n            },\n            \"description\": \"registrationViaApiResponse\"\n          },\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete Registration Flow with Username/Email Password Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/settings/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\\nYou must provide a valid Ory Kratos Session Token for this endpoint to respond with HTTP 200 OK.\\n\\nTo fetch an existing settings flow call `/self-service/settings/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"initializeSelfServiceSettingsViaAPIFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Initialize Settings Flow for API Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/settings/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\\nOry Kratos Session Cookie is included in the request, a login flow will be initialized.\\n\\n:::note\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"initializeSelfServiceSettingsViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Initialize Settings Flow for Browsers\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/settings/flows\": {\n      \"get\": {\n        \"description\": \"When accessing this endpoint through Ory Kratos' Public API you must ensure that either the Ory Kratos Session Cookie\\nor the Ory Kratos Session Token are set. The public endpoint does not return 404 status codes\\nbut instead 403 or 500 to improve data privacy.\\n\\nYou can access this endpoint without credentials when using Ory Kratos' Admin API.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"getSelfServiceSettingsFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the Settings Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/settings?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Get Settings Flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/settings/methods/password\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a settings flow by sending an identity's updated password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI-initiated flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and an application/json body with the session token on success;\\nHTTP 302 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\nHTTP 401 when the endpoint is called without a valid session token.\\nHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached.\\nImplies that the user needs to re-authenticate.\\n\\nBrowser flows expect `application/x-www-form-urlencoded` to be sent in the body and responds with\\na HTTP 302 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\\na HTTP 302 redirect to the Settings UI URL with the flow ID containing the validation errors otherwise.\\na HTTP 302 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"completeSelfServiceSettingsFlowWithPasswordMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Flow is flow ID.\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CompleteSelfServiceSettingsFlowWithPasswordMethod\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CompleteSelfServiceSettingsFlowWithPasswordMethod\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsViaApiResponse\"\n                }\n              }\n            },\n            \"description\": \"settingsViaApiResponse\"\n          },\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Complete Settings Flow with Username/Email Password Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/settings/methods/profile\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a settings flow by sending an identity's updated traits. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI-initiated flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and an application/json body with the session token on success;\\nHTTP 302 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\nHTTP 401 when the endpoint is called without a valid session token.\\nHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached and a sensitive field was\\nupdated (e.g. recovery email). Implies that the user needs to re-authenticate.\\n\\nBrowser flows expect `application/x-www-form-urlencoded` to be sent in the body and responds with\\na HTTP 302 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\\na HTTP 302 redirect to the settings UI URL with the flow ID containing the validation errors otherwise.\\na HTTP 302 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"completeSelfServiceSettingsFlowWithProfileMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Flow is flow ID.\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"type\": \"object\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"type\": \"object\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Payload\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Complete Settings Flow with Profile Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/verification/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nTo fetch an existing verification flow call `/self-service/verification/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"initializeSelfServiceVerificationViaAPIFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Verification Flow for API Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/verification/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based account verification flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.verification.ui_url` with the flow ID set as the query parameter `?flow=`.\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"initializeSelfServiceVerificationViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Verification Flow for Browser Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/verification/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a verification flow's context with, for example, error details and other information.\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"getSelfServiceVerificationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Flow ID\\n\\nThe value for this parameter comes from `request` URL Query parameter sent to your\\napplication (e.g. `/verification?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get Verification Flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/verification/methods/link\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a verification flow using the link method. This endpoint\\nbehaves differently for API and browser flows and has several states:\\n\\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\\nand works with API- and Browser-initiated flows.\\nFor API clients it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\\nand a HTTP 302 Found redirect with a fresh verification flow if the flow was otherwise invalid (e.g. expired).\\nFor Browser clients it returns a HTTP 302 Found redirect to the Verification UI URL with the Verification Flow ID appended.\\n`sent_email` is the success state after `choose_method` and allows the user to request another verification email. It\\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\\\"sending a verification link\\\")\\ndoes not have any API capabilities. The server responds with a HTTP 302 Found redirect either to the Settings UI URL\\n(if the link was valid) and instructs the user to update their password, or a redirect to the Verification UI URL with\\na new Verification Flow ID which contains an error message that the verification link was invalid.\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"completeSelfServiceVerificationFlowWithLinkMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Verification Token\\n\\nThe verification token which completes the verification request. If the token\\nis invalid (e.g. expired) an error will be shown to the end-user.\",\n            \"in\": \"query\",\n            \"name\": \"token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The Flow ID\\n\\nformat: uuid\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/completeSelfServiceVerificationFlowWithLinkMethod\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/completeSelfServiceVerificationFlowWithLinkMethod\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete Verification Flow with Link Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/sessions\": {\n      \"delete\": {\n        \"description\": \"Use this endpoint to revoke a session using its token. This endpoint is particularly useful for API clients\\nsuch as mobile apps to log the user out of the system and invalidate the session.\\n\\nThis endpoint does not remove any HTTP Cookies - use the Self-Service Logout Flow instead.\",\n        \"operationId\": \"revokeSession\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/revokeSession\"\n              }\n            }\n          },\n          \"required\": true,\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Revoke and Invalidate a Session\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/sessions/whoami\": {\n      \"get\": {\n        \"description\": \"Uses the HTTP Headers in the GET request to determine (e.g. by using checking the cookies) who is authenticated.\\nReturns a session object in the body or 401 if the credentials are invalid or no credentials were sent.\\nAdditionally when the request it successful it adds the user ID to the 'X-Kratos-Authenticated-Identity-Id' header in the response.\\n\\nThis endpoint is useful for reverse proxies and API Gateways.\",\n        \"operationId\": \"whoami\",\n        \"parameters\": [\n          {\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"in: authorization\",\n            \"in\": \"query\",\n            \"name\": \"Authorization\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/session\"\n                }\n              }\n            },\n            \"description\": \"session\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Check Who the Current HTTP Session Belongs To\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/version\": {\n      \"get\": {\n        \"description\": \"This endpoint returns the version of Ory Kratos.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of this service, the version will never\\nrefer to the cluster state, only to a single instance.\",\n        \"operationId\": \"getVersion\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"version\": {\n                      \"description\": \"The version of Ory Kratos.\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"required\": [\n                    \"version\"\n                  ],\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Returns the Ory Kratos version.\"\n          }\n        },\n        \"summary\": \"Return Running Software Version.\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    }\n  },\n  \"servers\": [\n    {\n      \"url\": \"https://{tenant}.tenants.oryapis.com/api/kratos/{api}\",\n      \"variables\": {\n        \"api\": {\n          \"default\": \"public\",\n          \"description\": \"Target the public or administrative API.\",\n          \"enum\": [\n            \"public\",\n            \"admin\"\n          ]\n        },\n        \"tenant\": {\n          \"default\": \"demo\",\n          \"description\": \"Tenant ID as provided by Ory Cloud.\"\n        }\n      }\n    }\n  ],\n  \"tags\": [\n    {\n      \"description\": \"All administrative API endpoints exposed at the admin API port.\",\n      \"externalDocs\": {\n        \"url\": \"https://www.ory.sh/kratos/docs/reference/api\"\n      },\n      \"name\": \"admin\"\n    },\n    {\n      \"description\": \"All public API endpoints exposed at the public API port.\",\n      \"externalDocs\": {\n        \"url\": \"https://www.ory.sh/kratos/docs/reference/api\"\n      },\n      \"name\": \"public\"\n    }\n  ],\n  \"x-forwarded-proto\": \"string\",\n  \"x-request-id\": \"string\"\n}\n"
  },
  {
    "path": ".schema/openapi/gen.go.yml",
    "content": "disallowAdditionalPropertiesIfNotPresent: false\npackageName: client\ngenerateInterfaces: true\nstructPrefix: true\nenumClassPrefix: true\nuseOneOfDiscriminatorLookup: true\nisGoSubmodule: false\n"
  },
  {
    "path": ".schema/openapi/gen.typescript.yml",
    "content": "npmName: \"@ory/kratos-client\"\nnpmVersion: 0.0.0\n# typescriptThreePlus: true\n#npmRepository: https://github.com/ory/sdk.git\nsupportsES6: true\nensureUniqueParams: true\nmodelPropertyNaming: original\ndisallowAdditionalPropertiesIfNotPresent: false\nwithInterfaces: false\nuseSingleRequestParameter: true\nenumUnknownDefaultCase: true\n"
  },
  {
    "path": ".schema/openapi/patches/common.yaml",
    "content": "[]\n"
  },
  {
    "path": ".schema/openapi/patches/courier.yaml",
    "content": "# Makes courierMessageStatus a string enum\n- op: remove\n  path: /components/schemas/courierMessageStatus/format\n- op: replace\n  path: /components/schemas/courierMessageStatus/type\n  value: string\n- op: add\n  path: /components/schemas/courierMessageStatus/enum\n  value:\n    - queued\n    - sent\n    - processing\n    - abandoned\n# Makes courierMessageType a string enum\n- op: remove\n  path: /components/schemas/courierMessageType/format\n- op: replace\n  path: /components/schemas/courierMessageType/type\n  value: string\n- op: add\n  path: /components/schemas/courierMessageType/enum\n  value:\n    - email\n    - phone\n# Fix courierMessageStatus query parameter in listMessages endpoint\n- op: replace\n  path: /paths/~1admin~1courier~1messages/get/parameters/2/schema\n  value:\n    $ref: \"#/components/schemas/courierMessageStatus\"\n"
  },
  {
    "path": ".schema/openapi/patches/generic_error.yaml",
    "content": "- op: add\n  path: /paths/~1sessions~1whoami/get/parameters/0/example\n  value: MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj\n- op: add\n  path: /paths/~1sessions~1whoami/get/parameters/1/example\n  value: ory_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==\n"
  },
  {
    "path": ".schema/openapi/patches/identity.yaml",
    "content": "- op: remove\n  path: /components/schemas/updateIdentityBody/properties/metadata_admin/type\n- op: remove\n  path: /components/schemas/updateIdentityBody/properties/metadata_public/type\n- op: remove\n  path: /components/schemas/createIdentityBody/properties/metadata_admin/type\n- op: remove\n  path: /components/schemas/createIdentityBody/properties/metadata_public/type\n- op: remove\n  path: /components/schemas/nullJsonRawMessage/type\n- op: add\n  path: /components/schemas/nullJsonRawMessage/nullable\n  value: true\n"
  },
  {
    "path": ".schema/openapi/patches/meta.yaml",
    "content": "- op: replace\n  path: /info\n  value:\n    title: Ory Identities API\n    description: |\n      This is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n    version: >-\n      {{ getenv \"CIRCLE_TAG\" }}\n    license:\n      name: Apache 2.0\n    contact:\n      email: \"office@ory.sh\"\n- op: replace\n  path: /tags\n  value:\n    - name: identity\n      description: APIs for managing identities.\n    - name: frontend\n      description: Endpoints used by frontend applications (e.g. Single-Page-App, Native Apps, Server Apps, ...) to manage a user's own profile.\n    - name: courier\n      description: APIs for managing email and SMS message delivery.\n    - name: metadata\n      description: Server Metadata provides relevant information about the running server. Only available when self-hosting this service.\n"
  },
  {
    "path": ".schema/openapi/patches/nulls.yaml",
    "content": "- op: replace\n  path: \"#/components/schemas/NullUUID\"\n  value:\n    type: string\n    format: uuid4\n    nullable: true\n- op: replace\n  path: \"#/components/schemas/NullTime\"\n  value:\n    format: date-time\n    type: string\n    nullable: true\n- op: replace\n  path: \"#/components/schemas/Time\"\n  value:\n    format: date-time\n    type: string\n- op: replace\n  path: \"#/components/schemas/NullString\"\n  value:\n    type: string\n    nullable: true\n- op: replace\n  path: \"#/components/schemas/NullBool\"\n  value:\n    type: boolean\n    nullable: true\n- op: replace\n  path: \"#/components/schemas/NullInt\"\n  value:\n    type: integer\n    nullable: true\n- op: replace\n  path: \"#/components/schemas/nullInt64\"\n  value:\n    type: integer\n    nullable: true\n- op: replace\n  path: \"#/components/schemas/nullDuration\"\n  value:\n    type: string\n    nullable: true\n    pattern: ^[0-9]+(ns|us|ms|s|m|h)$\n"
  },
  {
    "path": ".schema/openapi/patches/schema.yaml",
    "content": "# Makes uiNodeAttributes polymorph\n- op: remove\n  path: /components/schemas/uiNodeAttributes/type\n- op: add\n  path: /components/schemas/uiNodeAttributes/discriminator\n  value:\n    propertyName: node_type\n    mapping:\n      input: \"#/components/schemas/uiNodeInputAttributes\"\n      text: \"#/components/schemas/uiNodeTextAttributes\"\n      img: \"#/components/schemas/uiNodeImageAttributes\"\n      a: \"#/components/schemas/uiNodeAnchorAttributes\"\n      script: \"#/components/schemas/uiNodeScriptAttributes\"\n      div: \"#/components/schemas/uiNodeDivisionAttributes\"\n- op: add\n  path: /components/schemas/uiNodeAttributes/oneOf\n  value:\n    - \"$ref\": \"#/components/schemas/uiNodeInputAttributes\"\n    - \"$ref\": \"#/components/schemas/uiNodeTextAttributes\"\n    - \"$ref\": \"#/components/schemas/uiNodeImageAttributes\"\n    - \"$ref\": \"#/components/schemas/uiNodeAnchorAttributes\"\n    - \"$ref\": \"#/components/schemas/uiNodeScriptAttributes\"\n    - \"$ref\": \"#/components/schemas/uiNodeDivisionAttributes\"\n\n- op: replace\n  path: /components/schemas/uiNodeDivisionAttributes/properties/node_type/enum\n  value:\n    - div\n\n- op: replace\n  path: /components/schemas/uiNodeInputAttributes/properties/node_type/enum\n  value:\n    - input\n\n- op: replace\n  path: /components/schemas/uiNodeTextAttributes/properties/node_type/enum\n  value:\n    - text\n\n- op: replace\n  path: /components/schemas/uiNodeImageAttributes/properties/node_type/enum\n  value:\n    - img\n\n- op: replace\n  path: /components/schemas/uiNodeAnchorAttributes/properties/node_type/enum\n  value:\n    - a\n\n- op: replace\n  path: /components/schemas/uiNodeScriptAttributes/properties/node_type/enum\n  value:\n    - script\n\n# Makes the uiNodeInputAttributes value attribute polymorph\n- op: add\n  path: /components/schemas/uiNodeInputAttributes/properties/value/nullable\n  value: true\n\n- op: replace\n  path: /components/schemas/flowError/properties/error\n  value:\n    type: object\n\n- op: remove\n  path: \"#/components/schemas/identityTraits/type\"\n\n- op: add\n  path: /components/schemas/continueWith/discriminator\n  value:\n    propertyName: action\n    mapping:\n      show_verification_ui: \"#/components/schemas/continueWithVerificationUi\"\n      set_ory_session_token: \"#/components/schemas/continueWithSetOrySessionToken\"\n      show_settings_ui: \"#/components/schemas/continueWithSettingsUi\"\n      show_recovery_ui: \"#/components/schemas/continueWithRecoveryUi\"\n      redirect_browser_to: \"#/components/schemas/continueWithRedirectBrowserTo\"\n\n- op: add\n  path: /components/schemas/continueWith/oneOf\n  value:\n    - \"$ref\": \"#/components/schemas/continueWithVerificationUi\"\n    - \"$ref\": \"#/components/schemas/continueWithSetOrySessionToken\"\n    - \"$ref\": \"#/components/schemas/continueWithSettingsUi\"\n    - \"$ref\": \"#/components/schemas/continueWithRecoveryUi\"\n    - \"$ref\": \"#/components/schemas/continueWithRedirectBrowserTo\"\n"
  },
  {
    "path": ".schema/openapi/patches/security.yaml",
    "content": "- op: replace\n  path: /components/schemas/genericError/properties/details/additionalProperties\n  value: false\n"
  },
  {
    "path": ".schema/openapi/patches/selfservice.yaml",
    "content": "# Makes updateLoginFlowPayload polymorph\n#- op: remove\n#  path: /components/schemas/updateLoginFlowBody/type\n#- op: add\n#  path: /components/schemas/updateLoginFlowBody/oneOf\n#  value:\n#    - \"$ref\": \"#/components/schemas/updateLoginFlowWithPasswordMethod\"\n\n# Makes updateRegistrationFlowPayload polymorph\n\n# All modifications for the registration flow\n- op: remove\n  path: /components/schemas/updateRegistrationFlowBody/type\n- op: add\n  path: /components/schemas/updateRegistrationFlowBody/oneOf\n  value:\n    - \"$ref\": \"#/components/schemas/updateRegistrationFlowWithPasswordMethod\"\n    - \"$ref\": \"#/components/schemas/updateRegistrationFlowWithOidcMethod\"\n    - \"$ref\": \"#/components/schemas/updateRegistrationFlowWithSamlMethod\"\n    - \"$ref\": \"#/components/schemas/updateRegistrationFlowWithWebAuthnMethod\"\n    - \"$ref\": \"#/components/schemas/updateRegistrationFlowWithCodeMethod\"\n    - \"$ref\": \"#/components/schemas/updateRegistrationFlowWithPasskeyMethod\"\n    - \"$ref\": \"#/components/schemas/updateRegistrationFlowWithProfileMethod\"\n- op: add\n  path: /components/schemas/updateRegistrationFlowBody/discriminator\n  value:\n    propertyName: method\n    mapping:\n      password: \"#/components/schemas/updateRegistrationFlowWithPasswordMethod\"\n      oidc: \"#/components/schemas/updateRegistrationFlowWithOidcMethod\"\n      saml: \"#/components/schemas/updateRegistrationFlowWithSamlMethod\"\n      webauthn: \"#/components/schemas/updateRegistrationFlowWithWebAuthnMethod\"\n      code: \"#/components/schemas/updateRegistrationFlowWithCodeMethod\"\n      passkey: \"#/components/schemas/updateRegistrationFlowWithPasskeyMethod\"\n      profile: \"#/components/schemas/updateRegistrationFlowWithProfileMethod\"\n- op: add\n  path: /components/schemas/registrationFlowState\n  value:\n    title: Registration flow state (experimental)\n    description: The experimental state represents the state of a registration flow. This field is EXPERIMENTAL and subject to change!\n    type: string\n    enum:\n      - choose_method\n      - sent_email\n      - passed_challenge\n# end\n\n# All modifications for the login flow\n- op: remove\n  path: /components/schemas/updateLoginFlowBody/type\n- op: add\n  path: /components/schemas/updateLoginFlowBody/oneOf\n  value:\n    - \"$ref\": \"#/components/schemas/updateLoginFlowWithPasswordMethod\"\n    - \"$ref\": \"#/components/schemas/updateLoginFlowWithOidcMethod\"\n    - \"$ref\": \"#/components/schemas/updateLoginFlowWithSamlMethod\"\n    - \"$ref\": \"#/components/schemas/updateLoginFlowWithTotpMethod\"\n    - \"$ref\": \"#/components/schemas/updateLoginFlowWithWebAuthnMethod\"\n    - \"$ref\": \"#/components/schemas/updateLoginFlowWithLookupSecretMethod\"\n    - \"$ref\": \"#/components/schemas/updateLoginFlowWithCodeMethod\"\n    - \"$ref\": \"#/components/schemas/updateLoginFlowWithPasskeyMethod\"\n    - \"$ref\": \"#/components/schemas/updateLoginFlowWithIdentifierFirstMethod\"\n- op: add\n  path: /components/schemas/updateLoginFlowBody/discriminator\n  value:\n    propertyName: method\n    mapping:\n      password: \"#/components/schemas/updateLoginFlowWithPasswordMethod\"\n      oidc: \"#/components/schemas/updateLoginFlowWithOidcMethod\"\n      saml: \"#/components/schemas/updateLoginFlowWithSamlMethod\"\n      totp: \"#/components/schemas/updateLoginFlowWithTotpMethod\"\n      webauthn: \"#/components/schemas/updateLoginFlowWithWebAuthnMethod\"\n      lookup_secret: \"#/components/schemas/updateLoginFlowWithLookupSecretMethod\"\n      code: \"#/components/schemas/updateLoginFlowWithCodeMethod\"\n      passkey: \"#/components/schemas/updateLoginFlowWithPasskeyMethod\"\n      identifier_first: \"#/components/schemas/updateLoginFlowWithIdentifierFirstMethod\"\n- op: add\n  path: /components/schemas/loginFlowState\n  value:\n    title: Login flow state (experimental)\n    description: The experimental state represents the state of a login flow. This field is EXPERIMENTAL and subject to change!\n    type: string\n    enum:\n      - choose_method\n      - sent_email\n      - passed_challenge\n# end\n\n# All modifications for the recovery flow\n- op: remove\n  path: /components/schemas/updateRecoveryFlowBody/type\n- op: add\n  path: /components/schemas/updateRecoveryFlowBody/oneOf\n  value:\n    - \"$ref\": \"#/components/schemas/updateRecoveryFlowWithLinkMethod\"\n    - \"$ref\": \"#/components/schemas/updateRecoveryFlowWithCodeMethod\"\n- op: add\n  path: /components/schemas/updateRecoveryFlowBody/discriminator\n  value:\n    propertyName: method\n    mapping:\n      link: \"#/components/schemas/updateRecoveryFlowWithLinkMethod\"\n      code: \"#/components/schemas/updateRecoveryFlowWithCodeMethod\"\n- op: add\n  path: /components/schemas/recoveryFlowState\n  type: string\n  value:\n    title: Recovery flow state (experimental)\n    description: The experimental state represents the state of a recovery flow. This field is EXPERIMENTAL and subject to change!\n    enum:\n      - choose_method\n      - sent_email\n      - passed_challenge\n# End\n\n# All modifications for the verification flow\n- op: remove\n  path: /components/schemas/updateVerificationFlowBody/type\n- op: add\n  path: /components/schemas/updateVerificationFlowBody/oneOf\n  value:\n    - \"$ref\": \"#/components/schemas/updateVerificationFlowWithLinkMethod\"\n    - \"$ref\": \"#/components/schemas/updateVerificationFlowWithCodeMethod\"\n- op: add\n  path: /components/schemas/updateVerificationFlowBody/discriminator\n  value:\n    propertyName: method\n    mapping:\n      link: \"#/components/schemas/updateVerificationFlowWithLinkMethod\"\n      code: \"#/components/schemas/updateVerificationFlowWithCodeMethod\"\n- op: add\n  path: /components/schemas/verificationFlowState\n  type: string\n  value:\n    title: Verification flow state (experimental)\n    description: The experimental state represents the state of a verification flow. This field is EXPERIMENTAL and subject to change!\n    enum:\n      - choose_method\n      - sent_email\n      - passed_challenge\n# End\n\n# All modifications for the settings flow\n- op: remove\n  path: /components/schemas/updateSettingsFlowBody/type\n- op: add\n  path: /components/schemas/updateSettingsFlowBody/oneOf\n  value:\n    - \"$ref\": \"#/components/schemas/updateSettingsFlowWithPasswordMethod\"\n    - \"$ref\": \"#/components/schemas/updateSettingsFlowWithProfileMethod\"\n    - \"$ref\": \"#/components/schemas/updateSettingsFlowWithOidcMethod\"\n    - \"$ref\": \"#/components/schemas/updateSettingsFlowWithSamlMethod\"\n    - \"$ref\": \"#/components/schemas/updateSettingsFlowWithTotpMethod\"\n    - \"$ref\": \"#/components/schemas/updateSettingsFlowWithWebAuthnMethod\"\n    - \"$ref\": \"#/components/schemas/updateSettingsFlowWithLookupMethod\"\n    - \"$ref\": \"#/components/schemas/updateSettingsFlowWithPasskeyMethod\"\n- op: add\n  path: /components/schemas/updateSettingsFlowBody/discriminator\n  value:\n    propertyName: method\n    mapping:\n      password: \"#/components/schemas/updateSettingsFlowWithPasswordMethod\"\n      profile: \"#/components/schemas/updateSettingsFlowWithProfileMethod\"\n      oidc: \"#/components/schemas/updateSettingsFlowWithOidcMethod\"\n      saml: \"#/components/schemas/updateSettingsFlowWithSamlMethod\"\n      totp: \"#/components/schemas/updateSettingsFlowWithTotpMethod\"\n      webauthn: \"#/components/schemas/updateSettingsFlowWithWebAuthnMethod\"\n      passkey: \"#/components/schemas/updateSettingsFlowWithPasskeyMethod\"\n      lookup_secret: \"#/components/schemas/updateSettingsFlowWithLookupMethod\"\n- op: add\n  path: /components/schemas/settingsFlowState\n  value:\n    title: Settings flow state (experimental)\n    description: The experimental state represents the state of a settings flow. This field is EXPERIMENTAL and subject to change!\n    type: string\n    enum:\n      - show_form\n      - success\n# end\n\n# Some issues with AdditionalProperties\n- op: remove\n  path: \"#/components/schemas/OAuth2LoginRequest/properties/AdditionalProperties\"\n- op: remove\n  path: \"#/components/schemas/OAuth2ConsentRequestOpenIDConnectContext/properties/AdditionalProperties\"\n- op: remove\n  path: \"#/components/schemas/OAuth2Client/properties/AdditionalProperties\"\n"
  },
  {
    "path": ".schema/openapi/patches/session.yaml",
    "content": "- op: add\n  path: /paths/~1sessions~1whoami/get/parameters/0/example\n  value: MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj\n- op: add\n  path: /paths/~1sessions~1whoami/get/parameters/1/example\n  value: ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==\n- op: add\n  path: /components/schemas/authenticatorAssuranceLevel/enum\n  value:\n    - aal0\n    - aal1\n    - aal2\n    - aal3\n"
  },
  {
    "path": ".schema/openapi.json",
    "content": "{\n  \"components\": {\n    \"responses\": {\n      \"emptyResponse\": {\n        \"description\": \"Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\\ntypically 201.\"\n      },\n      \"errorContainer\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"$ref\": \"#/components/schemas/errorContainer\"\n            }\n          }\n        },\n        \"description\": \"User-facing error response\"\n      },\n      \"identityList\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"items\": {\n                \"$ref\": \"#/components/schemas/Identity\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        },\n        \"description\": \"A list of identities.\"\n      },\n      \"identityResponse\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"$ref\": \"#/components/schemas/Identity\"\n            }\n          }\n        },\n        \"description\": \"A single identity.\"\n      },\n      \"schemaResponse\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"type\": \"object\"\n            }\n          }\n        },\n        \"description\": \"The raw identity traits schema\"\n      }\n    },\n    \"schemas\": {\n      \"CompleteSelfServiceBrowserSettingsProfileStrategyFlow\": {\n        \"description\": \"nolint:deadcode,unused\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The Anti-CSRF Token\\n\\nThis token is only required when performing browser flows.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"Traits contains all of the identity's traits.\",\n            \"type\": \"object\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"CompleteSelfServiceLoginFlowWithPasswordMethod\": {\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"identifier\": {\n            \"description\": \"Identifier is the email or username of the user trying to log in.\",\n            \"type\": \"string\"\n          },\n          \"password\": {\n            \"description\": \"The user's password.\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"CompleteSelfServiceSettingsFlowWithPasswordMethod\": {\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\\n\\ntype: string\",\n            \"type\": \"string\"\n          },\n          \"password\": {\n            \"description\": \"Password is the updated password\\n\\ntype: string\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"password\"\n        ],\n        \"type\": \"object\"\n      },\n      \"CreateIdentity\": {\n        \"properties\": {\n          \"schema_id\": {\n            \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_url`.\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"schema_id\",\n          \"traits\"\n        ],\n        \"type\": \"object\"\n      },\n      \"CreateRecoveryLink\": {\n        \"properties\": {\n          \"expires_in\": {\n            \"description\": \"Link Expires In\\n\\nThe recovery link will expire at that point in time. Defaults to the configuration value of\\n`selfservice.flows.recovery.request_lifespan`.\",\n            \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n            \"type\": \"string\"\n          },\n          \"identity_id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          }\n        },\n        \"required\": [\n          \"identity_id\"\n        ],\n        \"type\": \"object\"\n      },\n      \"CredentialsType\": {\n        \"description\": \"and so on.\",\n        \"title\": \"CredentialsType  represents several different credential types, like password credentials, passwordless credentials,\",\n        \"type\": \"string\"\n      },\n      \"ID\": {\n        \"format\": \"int64\",\n        \"type\": \"integer\"\n      },\n      \"Identity\": {\n        \"properties\": {\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"recovery_addresses\": {\n            \"description\": \"RecoveryAddresses contains all the addresses that can be used to recover an identity.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/RecoveryAddress\"\n            },\n            \"type\": \"array\",\n            \"x-omitempty\": true\n          },\n          \"schema_id\": {\n            \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\",\n            \"type\": \"string\"\n          },\n          \"schema_url\": {\n            \"description\": \"SchemaURL is the URL of the endpoint where the identity's traits schema can be fetched from.\\n\\nformat: url\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"$ref\": \"#/components/schemas/Traits\"\n          },\n          \"verifiable_addresses\": {\n            \"description\": \"VerifiableAddresses contains all the addresses that can be verified by the user.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/VerifiableAddress\"\n            },\n            \"type\": \"array\",\n            \"x-omitempty\": true\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"schema_id\",\n          \"schema_url\",\n          \"traits\"\n        ],\n        \"type\": \"object\"\n      },\n      \"NullTime\": {\n        \"format\": \"date-time\",\n        \"title\": \"NullTime implements sql.NullTime functionality.\",\n        \"type\": \"string\"\n      },\n      \"RecoveryAddress\": {\n        \"properties\": {\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"value\": {\n            \"type\": \"string\"\n          },\n          \"via\": {\n            \"$ref\": \"#/components/schemas/RecoveryAddressType\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"value\",\n          \"via\"\n        ],\n        \"type\": \"object\"\n      },\n      \"RecoveryAddressType\": {\n        \"type\": \"string\"\n      },\n      \"State\": {\n        \"type\": \"string\"\n      },\n      \"Traits\": {\n        \"type\": \"object\"\n      },\n      \"Type\": {\n        \"description\": \"The flow type can either be `api` or `browser`.\",\n        \"title\": \"Type is the flow type.\",\n        \"type\": \"string\"\n      },\n      \"UUID\": {\n        \"format\": \"uuid4\",\n        \"type\": \"string\"\n      },\n      \"UpdateIdentity\": {\n        \"properties\": {\n          \"schema_id\": {\n            \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. If set\\nwill update the Identity's SchemaID.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_id`.\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"traits\"\n        ],\n        \"type\": \"object\"\n      },\n      \"VerifiableAddress\": {\n        \"properties\": {\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"status\": {\n            \"$ref\": \"#/components/schemas/VerifiableAddressStatus\"\n          },\n          \"value\": {\n            \"type\": \"string\"\n          },\n          \"verified\": {\n            \"type\": \"boolean\"\n          },\n          \"verified_at\": {\n            \"$ref\": \"#/components/schemas/NullTime\"\n          },\n          \"via\": {\n            \"$ref\": \"#/components/schemas/VerifiableAddressType\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"value\",\n          \"verified\",\n          \"via\",\n          \"status\"\n        ],\n        \"type\": \"object\"\n      },\n      \"VerifiableAddressStatus\": {\n        \"type\": \"string\"\n      },\n      \"VerifiableAddressType\": {\n        \"type\": \"string\"\n      },\n      \"completeSelfServiceBrowserSettingsOIDCFlowPayload\": {\n        \"properties\": {\n          \"flow\": {\n            \"description\": \"Flow ID is the flow's ID.\\n\\nin: query\",\n            \"type\": \"string\"\n          },\n          \"link\": {\n            \"description\": \"Link this provider\\n\\nEither this or `unlink` must be set.\\n\\ntype: string\\nin: body\",\n            \"type\": \"string\"\n          },\n          \"unlink\": {\n            \"description\": \"Unlink this provider\\n\\nEither this or `link` must be set.\\n\\ntype: string\\nin: body\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"completeSelfServiceRecoveryFlowWithLinkMethod\": {\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"email\": {\n            \"description\": \"Email to Recover\\n\\nNeeds to be set when initiating the flow. If the email is a registered\\nrecovery email, a recovery link will be sent. If the email is not known,\\na email with details on what happened will be sent instead.\\n\\nformat: email\\nin: body\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"completeSelfServiceVerificationFlowWithLinkMethod\": {\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"email\": {\n            \"description\": \"Email to Verify\\n\\nNeeds to be set when initiating the flow. If the email is a registered\\nverification email, a verification link will be sent. If the email is not known,\\na email with details on what happened will be sent instead.\\n\\nformat: email\\nin: body\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"errorContainer\": {\n        \"properties\": {\n          \"errors\": {\n            \"description\": \"Errors in the container\",\n            \"type\": \"object\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"errors\"\n        ],\n        \"type\": \"object\"\n      },\n      \"form\": {\n        \"description\": \"HTMLForm represents a HTML Form. The container can work with both HTTP Form and JSON requests\",\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"genericError\": {\n        \"description\": \"Error responses are sent when an error (e.g. unauthorized, bad request, ...) occurred.\",\n        \"properties\": {\n          \"error\": {\n            \"$ref\": \"#/components/schemas/genericErrorPayload\"\n          }\n        },\n        \"title\": \"Error response\",\n        \"type\": \"object\"\n      },\n      \"genericErrorPayload\": {\n        \"properties\": {\n          \"code\": {\n            \"description\": \"Code represents the error status code (404, 403, 401, ...).\",\n            \"example\": 404,\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          },\n          \"debug\": {\n            \"description\": \"Debug contains debug information. This is usually not available and has to be enabled.\",\n            \"example\": \"The database adapter was unable to find the element\",\n            \"type\": \"string\"\n          },\n          \"details\": {\n            \"additionalProperties\": true,\n            \"type\": \"object\"\n          },\n          \"message\": {\n            \"type\": \"string\"\n          },\n          \"reason\": {\n            \"type\": \"string\"\n          },\n          \"request\": {\n            \"type\": \"string\"\n          },\n          \"status\": {\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"healthNotReadyStatus\": {\n        \"properties\": {\n          \"errors\": {\n            \"additionalProperties\": {\n              \"type\": \"string\"\n            },\n            \"description\": \"Errors contains a list of errors that caused the not ready status.\",\n            \"type\": \"object\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"healthStatus\": {\n        \"properties\": {\n          \"status\": {\n            \"description\": \"Status always contains \\\"ok\\\".\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"loginFlow\": {\n        \"description\": \"This object represents a login flow. A login flow is initiated at the \\\"Initiate Login API / Browser Flow\\\"\\nendpoint by a client.\\n\\nOnce a login flow is completed successfully, a session cookie or session token will be issued.\",\n        \"properties\": {\n          \"active\": {\n            \"$ref\": \"#/components/schemas/CredentialsType\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\\na new flow has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"forced\": {\n            \"description\": \"Forced stores whether this login flow should enforce re-authentication.\",\n            \"type\": \"boolean\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the flow started.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"methods\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/loginFlowMethod\"\n            },\n            \"description\": \"List of login methods\\n\\nThis is the list of available login methods with their required form fields, such as `identifier` and `password`\\nfor the password login method. This will also contain error messages such as \\\"password can not be empty\\\".\",\n            \"type\": \"object\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"methods\"\n        ],\n        \"title\": \"Login Flow\",\n        \"type\": \"object\"\n      },\n      \"loginFlowMethod\": {\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/loginFlowMethodConfig\"\n          },\n          \"method\": {\n            \"$ref\": \"#/components/schemas/CredentialsType\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"config\"\n        ],\n        \"type\": \"object\"\n      },\n      \"loginFlowMethodConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          },\n          \"providers\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"loginFlowMethodConfigPayload\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          },\n          \"providers\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"loginViaApiResponse\": {\n        \"description\": \"The Response for Login Flows via API\",\n        \"properties\": {\n          \"session\": {\n            \"$ref\": \"#/components/schemas/session\"\n          },\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"session_token\",\n          \"session\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryFlow\": {\n        \"description\": \"This request is used when an identity wants to recover their account.\\n\\nWe recommend reading the [Account Recovery Documentation](../self-service/flows/password-reset-account-recovery)\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the request expires. If the user still wishes to update the setting,\\na new request has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the request occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"methods\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/recoveryFlowMethod\"\n            },\n            \"description\": \"Methods contains context for all account recovery methods. If a registration request has been\\nprocessed, but for example the password is incorrect, this will contain error messages.\",\n            \"type\": \"object\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"$ref\": \"#/components/schemas/State\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"methods\",\n          \"state\"\n        ],\n        \"title\": \"A Recovery Flow\",\n        \"type\": \"object\"\n      },\n      \"recoveryFlowMethod\": {\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/recoveryFlowMethodConfig\"\n          },\n          \"method\": {\n            \"description\": \"Method contains the request credentials type.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"config\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryFlowMethodConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryFlowMethodConfigPayload\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryLink\": {\n        \"properties\": {\n          \"expires_at\": {\n            \"description\": \"Recovery Link Expires At\\n\\nThe timestamp when the recovery link expires.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"recovery_link\": {\n            \"description\": \"Recovery Link\\n\\nThis link can be used to recover the account.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"recovery_link\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationFlow\": {\n        \"properties\": {\n          \"active\": {\n            \"$ref\": \"#/components/schemas/CredentialsType\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\\na new flow has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the flow occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"methods\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/registrationFlowMethod\"\n            },\n            \"description\": \"Methods contains context for all enabled registration methods. If a registration flow has been\\nprocessed, but for example the password is incorrect, this will contain error messages.\",\n            \"type\": \"object\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"methods\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationFlowMethod\": {\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/registrationFlowMethodConfig\"\n          },\n          \"method\": {\n            \"$ref\": \"#/components/schemas/CredentialsType\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"config\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationFlowMethodConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          },\n          \"providers\": {\n            \"description\": \"Providers is set for the \\\"oidc\\\" registration method.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/uiNodes\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationFlowMethodConfigPayload\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          },\n          \"providers\": {\n            \"description\": \"Providers is set for the \\\"oidc\\\" registration method.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/uiNodes\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationViaApiResponse\": {\n        \"description\": \"The Response for Registration Flows via API\",\n        \"properties\": {\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/Identity\"\n          },\n          \"session\": {\n            \"$ref\": \"#/components/schemas/session\"\n          },\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nThis field is only set when the session hook is configured as a post-registration hook.\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"session_token\",\n          \"identity\"\n        ],\n        \"type\": \"object\"\n      },\n      \"revokeSession\": {\n        \"properties\": {\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nInvalidate this session token.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"session_token\"\n        ],\n        \"type\": \"object\"\n      },\n      \"session\": {\n        \"properties\": {\n          \"active\": {\n            \"type\": \"boolean\"\n          },\n          \"authenticated_at\": {\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/Identity\"\n          },\n          \"issued_at\": {\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"expires_at\",\n          \"authenticated_at\",\n          \"issued_at\",\n          \"identity\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsFlow\": {\n        \"description\": \"This flow is used when an identity wants to update settings\\n(e.g. profile data, passwords, ...) in a selfservice manner.\\n\\nWe recommend reading the [User Settings Documentation](../self-service/flows/user-settings)\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to update the setting,\\na new flow has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/Identity\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the flow occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"methods\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/settingsFlowMethod\"\n            },\n            \"description\": \"Methods contains context for all enabled registration methods. If a settings flow has been\\nprocessed, but for example the first name is empty, this will contain error messages.\",\n            \"type\": \"object\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"$ref\": \"#/components/schemas/State\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"methods\",\n          \"identity\",\n          \"state\"\n        ],\n        \"title\": \"Flow represents a Settings Flow\",\n        \"type\": \"object\"\n      },\n      \"settingsFlowMethod\": {\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/settingsFlowMethodConfig\"\n          },\n          \"method\": {\n            \"description\": \"Method is the name of this flow method.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"config\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsFlowMethodConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsFlowMethodConfigPayload\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsProfileFormConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsViaApiResponse\": {\n        \"description\": \"The Response for Settings Flows via API\",\n        \"properties\": {\n          \"flow\": {\n            \"$ref\": \"#/components/schemas/settingsFlow\"\n          },\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/Identity\"\n          }\n        },\n        \"required\": [\n          \"flow\",\n          \"identity\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiNode\": {\n        \"description\": \"Nodes are represented as HTML elements or their native UI equivalents. For example,\\na node can be an `\\u003cimg\\u003e` tag, or an `\\u003cinput element\\u003e` but also `some plain text`.\",\n        \"properties\": {\n          \"attributes\": {\n            \"$ref\": \"#/components/schemas/uiNodeAttributes\"\n          },\n          \"group\": {\n            \"$ref\": \"#/components/schemas/uiNodeGroup\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/uiNodeType\"\n          }\n        },\n        \"required\": [\n          \"type\",\n          \"attributes\"\n        ],\n        \"title\": \"Node represents a flow's nodes\",\n        \"type\": \"object\"\n      },\n      \"uiNodeAnchorAttributes\": {\n        \"properties\": {\n          \"href\": {\n            \"description\": \"The link's href (destination) URL.\\n\\nformat: uri\",\n            \"type\": \"string\"\n          },\n          \"title\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          }\n        },\n        \"required\": [\n          \"href\",\n          \"title\"\n        ],\n        \"title\": \"AnchorAttributes represents the attributes of an anchor node.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeAttributes\": {\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/components/schemas/uiNodeInputAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeTextAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeImageAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeAnchorAttributes\"\n          }\n        ],\n        \"title\": \"Attributes represents a list of attributes (e.g. `href=\\\"foo\\\"` for links).\"\n      },\n      \"uiNodeGroup\": {\n        \"type\": \"string\"\n      },\n      \"uiNodeImageAttributes\": {\n        \"properties\": {\n          \"src\": {\n            \"description\": \"The image's source URL.\\n\\nformat: uri\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"src\"\n        ],\n        \"title\": \"ImageAttributes represents the attributes of an image node.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeInputAttributeType\": {\n        \"type\": \"string\"\n      },\n      \"uiNodeInputAttributes\": {\n        \"description\": \"InputAttributes represents the attributes of an input node\",\n        \"properties\": {\n          \"disabled\": {\n            \"description\": \"Sets the input's disabled field to true or false.\",\n            \"type\": \"boolean\"\n          },\n          \"label\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          },\n          \"name\": {\n            \"description\": \"The input's element name.\",\n            \"type\": \"string\"\n          },\n          \"pattern\": {\n            \"description\": \"The input's pattern.\",\n            \"type\": \"string\"\n          },\n          \"required\": {\n            \"description\": \"Mark this input field as required.\",\n            \"type\": \"boolean\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/uiNodeInputAttributeType\"\n          },\n          \"value\": {\n            \"description\": \"The input's value.\",\n            \"nullable\": true,\n            \"oneOf\": [\n              {\n                \"type\": \"string\"\n              },\n              {\n                \"type\": \"number\"\n              },\n              {\n                \"type\": \"boolean\"\n              }\n            ]\n          }\n        },\n        \"required\": [\n          \"name\",\n          \"type\",\n          \"disabled\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiNodeTextAttributes\": {\n        \"properties\": {\n          \"text\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          }\n        },\n        \"required\": [\n          \"text\"\n        ],\n        \"title\": \"TextAttributes represents the attributes of a text node.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeType\": {\n        \"type\": \"string\"\n      },\n      \"uiNodes\": {\n        \"items\": {\n          \"$ref\": \"#/components/schemas/uiNode\"\n        },\n        \"type\": \"array\"\n      },\n      \"uiText\": {\n        \"properties\": {\n          \"context\": {\n            \"description\": \"The message's context. Useful when customizing messages.\",\n            \"type\": \"object\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/ID\"\n          },\n          \"text\": {\n            \"description\": \"The message text. Written in american english.\",\n            \"type\": \"string\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/uiTextType\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"text\",\n          \"type\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiTextType\": {\n        \"type\": \"string\"\n      },\n      \"uiTexts\": {\n        \"items\": {\n          \"$ref\": \"#/components/schemas/uiText\"\n        },\n        \"type\": \"array\"\n      },\n      \"verificationFlow\": {\n        \"description\": \"Used to verify an out-of-band communication\\nchannel such as an email address or a phone number.\\n\\nFor more information head over to: https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the request expires. If the user still wishes to verify the address,\\na new request has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/UUID\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the request occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"methods\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/verificationFlowMethod\"\n            },\n            \"description\": \"Methods contains context for all account verification methods. If a registration request has been\\nprocessed, but for example the password is incorrect, this will contain error messages.\",\n            \"type\": \"object\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"$ref\": \"#/components/schemas/State\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/Type\"\n          }\n        },\n        \"required\": [\n          \"methods\",\n          \"state\"\n        ],\n        \"title\": \"A Verification Flow\",\n        \"type\": \"object\"\n      },\n      \"verificationFlowMethod\": {\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/verificationFlowMethodConfig\"\n          },\n          \"method\": {\n            \"description\": \"Method contains the request credentials type.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"config\"\n        ],\n        \"type\": \"object\"\n      },\n      \"verificationFlowMethodConfig\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"verificationFlowMethodConfigPayload\": {\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"version\": {\n        \"properties\": {\n          \"version\": {\n            \"description\": \"Version is the service's version.\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      }\n    },\n    \"securitySchemes\": {\n      \"sessionCookie\": {\n        \"in\": \"cookie\",\n        \"name\": \"ory_kratos_session\",\n        \"type\": \"apiKey\"\n      },\n      \"sessionToken\": {\n        \"in\": \"header\",\n        \"name\": \"X-Session-Token\",\n        \"type\": \"apiKey\"\n      }\n    }\n  },\n  \"info\": {\n    \"contact\": {\n      \"email\": \"hi@ory.sh\"\n    },\n    \"description\": \"Documentation for all public and administrative Ory Kratos APIs. Public and administrative APIs\\nare exposed on different ports. Public APIs can face the public internet without any protection\\nwhile administrative APIs should never be exposed without prior authorization. To protect\\nthe administative API port you should use something like Nginx, Ory Oathkeeper, or any other\\ntechnology capable of authorizing incoming requests.\\n\",\n    \"license\": {\n      \"name\": \"Apache 2.0\"\n    },\n    \"title\": \"Ory Kratos API\",\n    \"version\": \"\"\n  },\n  \"openapi\": \"3.0.3\",\n  \"paths\": {\n    \"/health/alive\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a HTTP 200 status code when Ory Kratos is accepting incoming\\nHTTP requests. This status does currently not include checks whether the database connection is working.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of this service, the health status will never\\nrefer to the cluster state, only to a single instance.\",\n        \"operationId\": \"isAlive\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/healthStatus\"\n                }\n              }\n            },\n            \"description\": \"Ory Kratos is ready to accept connections.\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Check HTTP Server Status\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/health/ready\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a HTTP 200 status code when Ory Kratos is up running and the environment dependencies (e.g.\\nthe database) are responsive as well.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of Ory Kratos, the health status will never\\nrefer to the cluster state, only to a single instance.\",\n        \"operationId\": \"isReady\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"status\": {\n                      \"description\": \"Always \\\"ok\\\".\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Ory Kratos is ready to accept requests.\"\n          },\n          \"503\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"errors\": {\n                      \"additionalProperties\": {\n                        \"type\": \"string\"\n                      },\n                      \"description\": \"Errors contains a list of errors that caused the not ready status.\",\n                      \"type\": \"object\"\n                    }\n                  },\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Ory Kratos is not yet ready to accept requests.\"\n          }\n        },\n        \"summary\": \"Check HTTP Server and Database Status\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/identities\": {\n      \"get\": {\n        \"description\": \"Lists all identities. Does not support search at the moment.\\n\\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"listIdentities\",\n        \"parameters\": [\n          {\n            \"description\": \"Items per Page\\n\\nThis is the number of items per page.\",\n            \"in\": \"query\",\n            \"name\": \"per_page\",\n            \"schema\": {\n              \"default\": 100,\n              \"format\": \"int64\",\n              \"maximum\": 500,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Pagination Page\",\n            \"in\": \"query\",\n            \"name\": \"page\",\n            \"schema\": {\n              \"default\": 0,\n              \"format\": \"int64\",\n              \"minimum\": 0,\n              \"type\": \"integer\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/identityList\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"List Identities\",\n        \"tags\": [\n          \"admin\"\n        ]\n      },\n      \"post\": {\n        \"description\": \"This endpoint creates an identity. It is NOT possible to set an identity's credentials (password, ...)\\nusing this method! A way to achieve that will be introduced in the future.\\n\\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"createIdentity\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CreateIdentity\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"201\": {\n            \"$ref\": \"#/components/responses/identityResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"409\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Create an Identity\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/identities/{id}\": {\n      \"delete\": {\n        \"description\": \"Calling this endpoint irrecoverably and permanently deletes the identity given its ID. This action can not be undone.\\nThis endpoint returns 204 when the identity was deleted or when the identity was not found, in which case it is\\nassumed that is has been deleted already.\\n\\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"deleteIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the identity's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Delete an Identity\",\n        \"tags\": [\n          \"admin\"\n        ]\n      },\n      \"get\": {\n        \"description\": \"Learn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"getIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of identity you want to get\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/identityResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get an Identity\",\n        \"tags\": [\n          \"admin\"\n        ]\n      },\n      \"put\": {\n        \"description\": \"This endpoint updates an identity. It is NOT possible to set an identity's credentials (password, ...)\\nusing this method! A way to achieve that will be introduced in the future.\\n\\nThe full identity payload (except credentials) is expected. This endpoint does not support patching.\\n\\nLearn how identities work in [Ory Kratos' User And Identity Model Documentation](https://www.ory.sh/docs/next/kratos/concepts/identity-user-model).\",\n        \"operationId\": \"updateIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of identity you want to update\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/UpdateIdentity\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/identityResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Update an Identity\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/metrics/prometheus\": {\n      \"get\": {\n        \"description\": \"```\\nmetadata:\\nannotations:\\nprometheus.io/port: \\\"4434\\\"\\nprometheus.io/path: \\\"/metrics/prometheus\\\"\\n```\",\n        \"operationId\": \"prometheus\",\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          }\n        },\n        \"summary\": \"Get snapshot metrics from the Kratos service. If you're using k8s, you can then add annotations to\\nyour deployment like so:\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/recovery/link\": {\n      \"post\": {\n        \"description\": \"This endpoint creates a recovery link which should be given to the user in order for them to recover\\n(or activate) their account.\",\n        \"operationId\": \"createRecoveryLink\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CreateRecoveryLink\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryLink\"\n                }\n              }\n            },\n            \"description\": \"recoveryLink\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Create a Recovery Link\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    },\n    \"/schemas/{id}\": {\n      \"get\": {\n        \"description\": \"Get a Traits Schema Definition\",\n        \"operationId\": \"getSchema\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of schema you want to get\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/schemaResponse\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/browser/flows/logout\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a logout flow.\\n\\n\\u003e This endpoint is NOT INTENDED for API clients and only works\\nwith browsers (Chrome, Firefox, ...).\\n\\nOn successful logout, the browser will be redirected (HTTP 302 Found) to the `return_to` parameter of the initial request\\nor fall back to `urls.default_return_to`.\\n\\nMore information can be found at [Ory Kratos User Logout Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-logout).\",\n        \"operationId\": \"initializeSelfServiceBrowserLogoutFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Browser-Based Logout User Flow\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/browser/flows/registration/strategies/oidc/settings/connections\": {\n      \"post\": {\n        \"description\": \"This endpoint completes a browser-based settings flow. This is usually achieved by POSTing data to this\\nendpoint.\\n\\n\\u003e This endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...) and HTML Forms.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"completeSelfServiceBrowserSettingsOIDCSettingsFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete the Browser-Based Settings Flow for the OpenID Connect Strategy\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/errors\": {\n      \"get\": {\n        \"description\": \"This endpoint returns the error associated with a user-facing self service errors.\\n\\nThis endpoint supports stub values to help you implement the error UI:\\n\\n`?error=stub:500` - returns a stub 500 (Internal Server Error) error.\\n\\nMore information can be found at [Ory Kratos User User Facing Error Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors).\",\n        \"operationId\": \"getSelfServiceError\",\n        \"parameters\": [\n          {\n            \"description\": \"Error is the container's ID\",\n            \"in\": \"query\",\n            \"name\": \"error\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/errorContainer\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get User-Facing Self-Service Errors\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/login/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a login flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\\nwill be returned unless the URL query parameter `?refresh=true` is set.\\n\\nTo fetch an existing login flow call `/self-service/login/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks, including CSRF login attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"initializeSelfServiceLoginViaAPIFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"Refresh a login session\\n\\nIf set to true, this will refresh an existing login session by\\nasking the user to sign in again. This will reset the\\nauthenticated_at time of the session.\",\n            \"in\": \"query\",\n            \"name\": \"refresh\",\n            \"schema\": {\n              \"type\": \"boolean\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Login Flow for API clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/login/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user login flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\\n`?refresh=true` was set.\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"initializeSelfServiceLoginViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Login Flow for browsers\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/login/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a login flow's context with, for example, error details and other information.\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"getSelfServiceLoginFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Login Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/login?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get Login Flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/login/methods/password\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a login flow by sending an identity's identifier and password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI flows expect `application/json` to be sent in the body and responds with\\nHTTP 200 and a application/json body with the session token on success;\\nHTTP 302 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\n\\nBrowser flows expect `application/x-www-form-urlencoded` to be sent in the body and responds with\\na HTTP 302 redirect to the post/after login URL or the `return_to` value if it was set and if the login succeeded;\\na HTTP 302 redirect to the login UI URL with the flow ID containing the validation errors otherwise.\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"completeSelfServiceLoginFlowWithPasswordMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"The Flow ID\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CompleteSelfServiceLoginFlowWithPasswordMethod\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CompleteSelfServiceLoginFlowWithPasswordMethod\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginViaApiResponse\"\n                }\n              }\n            },\n            \"description\": \"loginViaApiResponse\"\n          },\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete Login Flow with Username/Email Password Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/recovery/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a recovery flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error.\\n\\nTo fetch an existing recovery flow call `/self-service/recovery/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery.mdx).\",\n        \"operationId\": \"initializeSelfServiceRecoveryViaAPIFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Recovery Flow for API Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/recovery/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based account recovery flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.recovery.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists, the browser is returned to the configured return URL.\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery.mdx).\",\n        \"operationId\": \"initializeSelfServiceRecoveryViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Recovery Flow for Browser Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/recovery/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a recovery flow's context with, for example, error details and other information.\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery.mdx).\",\n        \"operationId\": \"getSelfServiceRecoveryFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Flow ID\\n\\nThe value for this parameter comes from `request` URL Query parameter sent to your\\napplication (e.g. `/recovery?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get information about a recovery flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/recovery/methods/link\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a recovery flow using the link method. This endpoint\\nbehaves differently for API and browser flows and has several states:\\n\\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\\nand works with API- and Browser-initiated flows.\\nFor API clients it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\\nand a HTTP 302 Found redirect with a fresh recovery flow if the flow was otherwise invalid (e.g. expired).\\nFor Browser clients it returns a HTTP 302 Found redirect to the Recovery UI URL with the Recovery Flow ID appended.\\n`sent_email` is the success state after `choose_method` and allows the user to request another recovery email. It\\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\\\"sending a recovery link\\\")\\ndoes not have any API capabilities. The server responds with a HTTP 302 Found redirect either to the Settings UI URL\\n(if the link was valid) and instructs the user to update their password, or a redirect to the Recover UI URL with\\na new Recovery Flow ID which contains an error message that the recovery link was invalid.\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery.mdx).\",\n        \"operationId\": \"completeSelfServiceRecoveryFlowWithLinkMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Recovery Token\\n\\nThe recovery token which completes the recovery request. If the token\\nis invalid (e.g. expired) an error will be shown to the end-user.\",\n            \"in\": \"query\",\n            \"name\": \"token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The Flow ID\\n\\nformat: uuid\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/completeSelfServiceRecoveryFlowWithLinkMethod\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/completeSelfServiceRecoveryFlowWithLinkMethod\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete Recovery Flow with Link Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/registration/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\\nwill be returned unless the URL query parameter `?refresh=true` is set.\\n\\nTo fetch an existing registration flow call `/self-service/registration/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"initializeSelfServiceRegistrationViaAPIFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Registration Flow for API clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/registration/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user registration flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.registration.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\\n`?refresh=true` was set.\\n\\n:::note\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"initializeSelfServiceRegistrationViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Registration Flow for browsers\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/registration/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a registration flow's context with, for example, error details and other information.\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"getSelfServiceRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Registration Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/registration?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get Registration Flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/registration/methods/password\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a registration flow by sending an identity's traits and password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and a application/json body with the created identity success - if the session hook is configured the\\n`session` and `session_token` will also be included;\\nHTTP 302 redirect to a fresh registration flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\n\\nBrowser flows expect `application/x-www-form-urlencoded` to be sent in the body and responds with\\na HTTP 302 redirect to the post/after registration URL or the `return_to` value if it was set and if the registration succeeded;\\na HTTP 302 redirect to the registration UI URL with the flow ID containing the validation errors otherwise.\\n\\nMore information can be found at [Ory Kratos User Login and User Registration Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-login-user-registration).\",\n        \"operationId\": \"completeSelfServiceRegistrationFlowWithPasswordMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Flow is flow ID.\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"type\": \"object\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"type\": \"object\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Payload\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationViaApiResponse\"\n                }\n              }\n            },\n            \"description\": \"registrationViaApiResponse\"\n          },\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete Registration Flow with Username/Email Password Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/settings/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\\nYou must provide a valid Ory Kratos Session Token for this endpoint to respond with HTTP 200 OK.\\n\\nTo fetch an existing settings flow call `/self-service/settings/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"initializeSelfServiceSettingsViaAPIFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Initialize Settings Flow for API Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/settings/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\\nOry Kratos Session Cookie is included in the request, a login flow will be initialized.\\n\\n:::note\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"initializeSelfServiceSettingsViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Initialize Settings Flow for Browsers\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/settings/flows\": {\n      \"get\": {\n        \"description\": \"When accessing this endpoint through Ory Kratos' Public API you must ensure that either the Ory Kratos Session Cookie\\nor the Ory Kratos Session Token are set. The public endpoint does not return 404 status codes\\nbut instead 403 or 500 to improve data privacy.\\n\\nYou can access this endpoint without credentials when using Ory Kratos' Admin API.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"getSelfServiceSettingsFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the Settings Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/settings?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Get Settings Flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/settings/methods/password\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a settings flow by sending an identity's updated password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI-initiated flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and an application/json body with the session token on success;\\nHTTP 302 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\nHTTP 401 when the endpoint is called without a valid session token.\\nHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached.\\nImplies that the user needs to re-authenticate.\\n\\nBrowser flows expect `application/x-www-form-urlencoded` to be sent in the body and responds with\\na HTTP 302 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\\na HTTP 302 redirect to the Settings UI URL with the flow ID containing the validation errors otherwise.\\na HTTP 302 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"completeSelfServiceSettingsFlowWithPasswordMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Flow is flow ID.\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CompleteSelfServiceSettingsFlowWithPasswordMethod\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/CompleteSelfServiceSettingsFlowWithPasswordMethod\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsViaApiResponse\"\n                }\n              }\n            },\n            \"description\": \"settingsViaApiResponse\"\n          },\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Complete Settings Flow with Username/Email Password Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/settings/methods/profile\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a settings flow by sending an identity's updated traits. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI-initiated flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and an application/json body with the session token on success;\\nHTTP 302 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\nHTTP 401 when the endpoint is called without a valid session token.\\nHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached and a sensitive field was\\nupdated (e.g. recovery email). Implies that the user needs to re-authenticate.\\n\\nBrowser flows expect `application/x-www-form-urlencoded` to be sent in the body and responds with\\na HTTP 302 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\\na HTTP 302 redirect to the settings UI URL with the flow ID containing the validation errors otherwise.\\na HTTP 302 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"completeSelfServiceSettingsFlowWithProfileMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Flow is flow ID.\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"type\": \"object\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"type\": \"object\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Payload\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Complete Settings Flow with Profile Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/verification/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nTo fetch an existing verification flow call `/self-service/verification/flows?flow=\\u003cflow_id\\u003e`.\\n\\n:::warning\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\n:::\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"initializeSelfServiceVerificationViaAPIFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Verification Flow for API Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/verification/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based account verification flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.verification.ui_url` with the flow ID set as the query parameter `?flow=`.\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"initializeSelfServiceVerificationViaBrowserFlow\",\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Initialize Verification Flow for Browser Clients\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/self-service/verification/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a verification flow's context with, for example, error details and other information.\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"getSelfServiceVerificationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Flow ID\\n\\nThe value for this parameter comes from `request` URL Query parameter sent to your\\napplication (e.g. `/verification?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Get Verification Flow\",\n        \"tags\": [\n          \"public\",\n          \"admin\"\n        ]\n      }\n    },\n    \"/self-service/verification/methods/link\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a verification flow using the link method. This endpoint\\nbehaves differently for API and browser flows and has several states:\\n\\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\\nand works with API- and Browser-initiated flows.\\nFor API clients it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\\nand a HTTP 302 Found redirect with a fresh verification flow if the flow was otherwise invalid (e.g. expired).\\nFor Browser clients it returns a HTTP 302 Found redirect to the Verification UI URL with the Verification Flow ID appended.\\n`sent_email` is the success state after `choose_method` and allows the user to request another verification email. It\\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\\\"sending a verification link\\\")\\ndoes not have any API capabilities. The server responds with a HTTP 302 Found redirect either to the Settings UI URL\\n(if the link was valid) and instructs the user to update their password, or a redirect to the Verification UI URL with\\na new Verification Flow ID which contains an error message that the verification link was invalid.\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"completeSelfServiceVerificationFlowWithLinkMethod\",\n        \"parameters\": [\n          {\n            \"description\": \"Verification Token\\n\\nThe verification token which completes the verification request. If the token\\nis invalid (e.g. expired) an error will be shown to the end-user.\",\n            \"in\": \"query\",\n            \"name\": \"token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The Flow ID\\n\\nformat: uuid\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/completeSelfServiceVerificationFlowWithLinkMethod\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/completeSelfServiceVerificationFlowWithLinkMethod\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"302\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Complete Verification Flow with Link Method\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/sessions\": {\n      \"delete\": {\n        \"description\": \"Use this endpoint to revoke a session using its token. This endpoint is particularly useful for API clients\\nsuch as mobile apps to log the user out of the system and invalidate the session.\\n\\nThis endpoint does not remove any HTTP Cookies - use the Self-Service Logout Flow instead.\",\n        \"operationId\": \"revokeSession\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/revokeSession\"\n              }\n            }\n          },\n          \"required\": true,\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"summary\": \"Revoke and Invalidate a Session\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/sessions/whoami\": {\n      \"get\": {\n        \"description\": \"Uses the HTTP Headers in the GET request to determine (e.g. by using checking the cookies) who is authenticated.\\nReturns a session object in the body or 401 if the credentials are invalid or no credentials were sent.\\nAdditionally when the request it successful it adds the user ID to the 'X-Kratos-Authenticated-Identity-Id' header in the response.\\n\\nThis endpoint is useful for reverse proxies and API Gateways.\",\n        \"operationId\": \"whoami\",\n        \"parameters\": [\n          {\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"in: authorization\",\n            \"in\": \"query\",\n            \"name\": \"Authorization\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/session\"\n                }\n              }\n            },\n            \"description\": \"session\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/genericError\"\n                }\n              }\n            },\n            \"description\": \"genericError\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Check Who the Current HTTP Session Belongs To\",\n        \"tags\": [\n          \"public\"\n        ]\n      }\n    },\n    \"/version\": {\n      \"get\": {\n        \"description\": \"This endpoint returns the version of Ory Kratos.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of this service, the version will never\\nrefer to the cluster state, only to a single instance.\",\n        \"operationId\": \"getVersion\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"version\": {\n                      \"description\": \"The version of Ory Kratos.\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Returns the Ory Kratos version.\"\n          }\n        },\n        \"summary\": \"Return Running Software Version.\",\n        \"tags\": [\n          \"admin\"\n        ]\n      }\n    }\n  },\n  \"servers\": [\n    {\n      \"url\": \"https://{tenant}.tenants.oryapis.com/api/kratos/{api}\",\n      \"variables\": {\n        \"api\": {\n          \"default\": \"public\",\n          \"description\": \"Target the public or administrative API.\",\n          \"enum\": [\n            \"public\",\n            \"admin\"\n          ]\n        },\n        \"tenant\": {\n          \"default\": \"demo\",\n          \"description\": \"Tenant ID as provided by Ory Cloud.\"\n        }\n      }\n    }\n  ],\n  \"tags\": [\n    {\n      \"description\": \"All administrative API endpoints exposed at the admin API port.\",\n      \"externalDocs\": {\n        \"url\": \"https://www.ory.sh/kratos/docs/reference/api\"\n      },\n      \"name\": \"admin\"\n    },\n    {\n      \"description\": \"All public API endpoints exposed at the public API port.\",\n      \"externalDocs\": {\n        \"url\": \"https://www.ory.sh/kratos/docs/reference/api\"\n      },\n      \"name\": \"public\"\n    }\n  ],\n  \"x-forwarded-proto\": \"string\",\n  \"x-request-id\": \"string\"\n}\n"
  },
  {
    "path": ".schema/version.schema.json",
    "content": "{\n    \"$id\": \"https://github.com/ory/kratos/.schema/versions.config.schema.json\",\n    \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n    \"oneOf\": [\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v1.3.0\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v1.3.0/.schemastore/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v1.2.0\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v1.2.0/.schemastore/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v1.1.0\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v1.1.0/.schemastore/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v1.0.0\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v1.0.0/.schemastore/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.13.0\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.13.0/.schemastore/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.11.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.11.1/.schemastore/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.11.0\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.11.0/.schemastore/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.10.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.10.1/.schemastore/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.10.0\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.10.0/.schemastore/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.9.0-alpha.3\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.9.0-alpha.3/embedx/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.9.0-alpha.2\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.9.0-alpha.2/embedx/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.4.6-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.4.6-alpha.1/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.5.0-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.5.0-alpha.1/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.5.1-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.5.1-alpha.1/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.5.2-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.5.2-alpha.1/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.5.3-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.5.3-alpha.1/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.5.4-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.5.4-alpha.1/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.5.5-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.5.5-alpha.1/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.6.0-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.6.0-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.6.0-alpha.2\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.6.0-alpha.2/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.6.1-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.6.1-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.6.2-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.6.2-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.6.3-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.6.3-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.7.0-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.7.0-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.7.1-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.7.1-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.7.3-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.7.3-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.7.4-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.7.4-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.7.5-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.7.5-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.7.6-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.7.6-alpha.1/driver/config/.schema/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.8.0-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.8.0-alpha.1/embedx/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.8.0-alpha.2\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.8.0-alpha.2/embedx/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.8.0-alpha.3\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.8.0-alpha.3/embedx/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"properties\": {\n                        \"version\": {\n                            \"const\": \"v0.8.2-alpha.1\"\n                        }\n                    },\n                    \"required\": [\n                        \"version\"\n                    ]\n                },\n                {\n                    \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/v0.8.2-alpha.1/embedx/config.schema.json\"\n                }\n            ]\n        },\n        {\n            \"allOf\": [\n                {\n                    \"oneOf\": [\n                        {\n                            \"properties\": {\n                                \"version\": {\n                                    \"type\": \"string\",\n                                    \"maxLength\": 0\n                                }\n                            },\n                            \"required\": [\n                                \"version\"\n                            ]\n                        },\n                        {\n                            \"not\": {\n                                \"properties\": {\n                                    \"version\": {}\n                                },\n                                \"required\": [\n                                    \"version\"\n                                ]\n                            }\n                        }\n                    ]\n                },\n                {\n                    \"$ref\": \"#/oneOf/0/allOf/1\"\n                }\n            ]\n        }\n    ],\n    \"title\": \"All Versions for Ory Kratos Configuration\",\n    \"type\": \"object\"\n}"
  },
  {
    "path": ".schemastore/README.md",
    "content": "The config schema is generated from the internal one at\n`embedx/config.schema.json`, so in case of changes to the config schema,\nplease edit that internal schema instead.\n"
  },
  {
    "path": ".schemastore/config.schema.json",
    "content": "{\n  \"$id\": \"https://github.com/ory/kratos/embedx/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Ory Kratos Configuration\",\n  \"type\": \"object\",\n  \"definitions\": {\n    \"baseUrl\": {\n      \"title\": \"Base URL\",\n      \"description\": \"The URL where the endpoint is exposed at. This domain is used to generate redirects, form URLs, and more.\",\n      \"type\": \"string\",\n      \"format\": \"uri-reference\",\n      \"examples\": [\n        \"https://my-app.com/\",\n        \"https://my-app.com/.ory/kratos/public\"\n      ]\n    },\n    \"socket\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Sets the permissions of the unix socket\",\n      \"properties\": {\n        \"owner\": {\n          \"type\": \"string\",\n          \"description\": \"Owner of unix socket. If empty, the owner will be the user running Kratos.\",\n          \"default\": \"\"\n        },\n        \"group\": {\n          \"type\": \"string\",\n          \"description\": \"Group of unix socket. If empty, the group will be the primary group of the user running Kratos.\",\n          \"default\": \"\"\n        },\n        \"mode\": {\n          \"type\": \"integer\",\n          \"description\": \"Mode of unix socket in numeric form\",\n          \"default\": 493,\n          \"minimum\": 0,\n          \"maximum\": 511\n        }\n      }\n    },\n    \"defaultReturnTo\": {\n      \"title\": \"Redirect browsers to set URL per default\",\n      \"description\": \"Ory Kratos redirects to this URL per default on completion of self-service flows and other browser interaction. Read this [article for more information on browser redirects](https://www.ory.sh/kratos/docs/concepts/browser-redirect-flow-completion).\",\n      \"type\": \"string\",\n      \"format\": \"uri-reference\",\n      \"examples\": [\"https://my-app.com/dashboard\", \"/dashboard\"]\n    },\n    \"selfServiceSessionRevokerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"revoke_active_sessions\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceSessionIssuerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"session\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceRequireVerifiedAddressHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"require_verified_address\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceVerificationHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"verification\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceShowVerificationUIHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"show_verification_ui\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"b2bSSOHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"enum\": [\"b2b_sso\", \"organization\"]\n        },\n        \"config\": {\n          \"type\": \"object\",\n          \"additionalProperties\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\", \"config\"]\n    },\n    \"webHookAuthBasicAuthProperties\": {\n      \"properties\": {\n        \"type\": {\n          \"const\": \"basic_auth\"\n        },\n        \"config\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"user\": {\n              \"type\": \"string\",\n              \"description\": \"user name for basic auth\"\n            },\n            \"password\": {\n              \"type\": \"string\",\n              \"description\": \"password for basic auth\"\n            }\n          },\n          \"additionalProperties\": false,\n          \"required\": [\"user\", \"password\"]\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"type\", \"config\"]\n    },\n    \"httpRequestConfig\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"url\": {\n          \"title\": \"HTTP address of API endpoint\",\n          \"description\": \"This URL will be used to send the emails to.\",\n          \"examples\": [\"https://example.com/api/v1/email\"],\n          \"type\": \"string\",\n          \"pattern\": \"^https?://\"\n        },\n        \"method\": {\n          \"type\": \"string\",\n          \"description\": \"The HTTP method to use (GET, POST, etc). Defaults to POST.\",\n          \"default\": \"POST\"\n        },\n        \"headers\": {\n          \"type\": \"object\",\n          \"description\": \"The HTTP headers that must be applied to request\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          }\n        },\n        \"body\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"pattern\": \"^(http|https|file|base64)://\",\n          \"description\": \"URI pointing to the jsonnet template used for payload generation. Only used for those HTTP methods which support HTTP body payloads\",\n          \"default\": \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgcmVjaXBpZW50OiBjdHgucmVjaXBpZW50LAogIHRlbXBsYXRlX3R5cGU6IGN0eC50ZW1wbGF0ZV90eXBlLAogIHRvOiBpZiAidGVtcGxhdGVfZGF0YSIgaW4gY3R4ICYmICJ0byIgaW4gY3R4LnRlbXBsYXRlX2RhdGEgdGhlbiBjdHgudGVtcGxhdGVfZGF0YS50byBlbHNlIG51bGwsCiAgcmVjb3ZlcnlfY29kZTogaWYgInRlbXBsYXRlX2RhdGEiIGluIGN0eCAmJiAicmVjb3ZlcnlfY29kZSIgaW4gY3R4LnRlbXBsYXRlX2RhdGEgdGhlbiBjdHgudGVtcGxhdGVfZGF0YS5yZWNvdmVyeV9jb2RlIGVsc2UgbnVsbCwKICByZWNvdmVyeV91cmw6IGlmICJ0ZW1wbGF0ZV9kYXRhIiBpbiBjdHggJiYgInJlY292ZXJ5X3VybCIgaW4gY3R4LnRlbXBsYXRlX2RhdGEgdGhlbiBjdHgudGVtcGxhdGVfZGF0YS5yZWNvdmVyeV91cmwgZWxzZSBudWxsLAogIHZlcmlmaWNhdGlvbl91cmw6IGlmICJ0ZW1wbGF0ZV9kYXRhIiBpbiBjdHggJiYgInZlcmlmaWNhdGlvbl91cmwiIGluIGN0eC50ZW1wbGF0ZV9kYXRhIHRoZW4gY3R4LnRlbXBsYXRlX2RhdGEudmVyaWZpY2F0aW9uX3VybCBlbHNlIG51bGwsCiAgdmVyaWZpY2F0aW9uX2NvZGU6IGlmICJ0ZW1wbGF0ZV9kYXRhIiBpbiBjdHggJiYgInZlcmlmaWNhdGlvbl9jb2RlIiBpbiBjdHgudGVtcGxhdGVfZGF0YSB0aGVuIGN0eC50ZW1wbGF0ZV9kYXRhLnZlcmlmaWNhdGlvbl9jb2RlIGVsc2UgbnVsbCwKICBzdWJqZWN0OiBjdHguc3ViamVjdCwKICBib2R5OiBjdHguYm9keQp9Cg==\",\n          \"examples\": [\n            \"file:///path/to/body.jsonnet\",\n            \"file://./body.jsonnet\",\n            \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgaWRlbnRpdHlfaWQ6IGlmIGN0eFsiaWRlbnRpdHkiXSAhPSBudWxsIHRoZW4gY3R4LmlkZW50aXR5LmlkLAp9=\",\n            \"https://oryapis.com/default_body.jsonnet\"\n          ]\n        },\n        \"auth\": {\n          \"type\": \"object\",\n          \"title\": \"Auth mechanisms\",\n          \"description\": \"Define which auth mechanism to use for auth with the HTTP email provider\",\n          \"oneOf\": [\n            {\n              \"$ref\": \"#/definitions/webHookAuthApiKeyProperties\"\n            },\n            {\n              \"$ref\": \"#/definitions/webHookAuthBasicAuthProperties\"\n            }\n          ]\n        },\n        \"additionalProperties\": false\n      },\n      \"additionalProperties\": false\n    },\n    \"webHookAuthApiKeyProperties\": {\n      \"properties\": {\n        \"type\": {\n          \"const\": \"api_key\"\n        },\n        \"config\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the api key\"\n            },\n            \"value\": {\n              \"type\": \"string\",\n              \"description\": \"The value of the api key\"\n            },\n            \"in\": {\n              \"type\": \"string\",\n              \"description\": \"How the api key should be transferred\",\n              \"enum\": [\"header\", \"cookie\"]\n            }\n          },\n          \"additionalProperties\": false,\n          \"required\": [\"name\", \"value\", \"in\"]\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"type\", \"config\"]\n    },\n    \"selfServiceWebHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"web_hook\"\n        },\n        \"config\": {\n          \"type\": \"object\",\n          \"title\": \"Web-Hook Configuration\",\n          \"description\": \"Define what the hook should do\",\n          \"properties\": {\n            \"id\": {\n              \"type\": \"string\",\n              \"description\": \"The ID of the hook. Used to identify the hook in logs and errors. For debugging purposes only.\"\n            },\n            \"response\": {\n              \"title\": \"Response Handling\",\n              \"description\": \"How the web hook should handle the response\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ignore\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Ignore the response from the web hook. If enabled the request will be made asynchronously which can be useful if you only wish to notify another system but do not parse the response.\",\n                  \"default\": false\n                },\n                \"parse\": {\n                  \"type\": \"boolean\",\n                  \"default\": false,\n                  \"description\": \"If enabled parses the response before saving the flow result. Set this value to true if you would like to modify the identity, for example identity metadata, before saving it during registration. When enabled, you may also abort the registration, verification, login or settings flow due to, for example, a validation flow. Head over to the [web hook documentation](https://www.ory.sh/docs/kratos/hooks/configure-hooks) for more information.\"\n                }\n              },\n              \"not\": {\n                \"properties\": {\n                  \"ignore\": {\n                    \"const\": true\n                  },\n                  \"parse\": {\n                    \"const\": true\n                  }\n                },\n                \"required\": [\"ignore\", \"parse\"]\n              }\n            },\n            \"url\": {\n              \"type\": \"string\",\n              \"description\": \"The URL the Web-Hook should call\",\n              \"format\": \"uri\"\n            },\n            \"method\": {\n              \"type\": \"string\",\n              \"description\": \"The HTTP method to use (GET, POST, etc).\"\n            },\n            \"headers\": {\n              \"type\": \"object\",\n              \"description\": \"The HTTP headers that must be applied to the Web-Hook\",\n              \"additionalProperties\": {\n                \"type\": \"string\"\n              }\n            },\n            \"body\": {\n              \"type\": \"string\",\n              \"oneOf\": [\n                {\n                  \"format\": \"uri\",\n                  \"pattern\": \"^(http|https|file|base64)://\",\n                  \"description\": \"URI pointing to the jsonnet template used for payload generation. Only used for those HTTP methods, which support HTTP body payloads\",\n                  \"examples\": [\n                    \"file:///path/to/body.jsonnet\",\n                    \"file://./body.jsonnet\",\n                    \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgaWRlbnRpdHlfaWQ6IGlmIGN0eFsiaWRlbnRpdHkiXSAhPSBudWxsIHRoZW4gY3R4LmlkZW50aXR5LmlkLAp9=\",\n                    \"https://oryapis.com/default_body.jsonnet\"\n                  ]\n                },\n                {\n                  \"description\": \"DEPRECATED: please use a URI instead (i.e. prefix your filepath with 'file://')\",\n                  \"not\": {\n                    \"pattern\": \"^(http|https|file|base64)://\"\n                  }\n                }\n              ]\n            },\n            \"can_interrupt\": {\n              \"type\": \"boolean\",\n              \"default\": false,\n              \"description\": \"Deprecated, please use `response.parse` instead. If enabled allows the web hook to interrupt / abort the self-service flow. It only applies to certain flows (registration/verification/login/settings) and requires a valid response format.\"\n            },\n            \"emit_analytics_event\": {\n              \"type\": \"boolean\",\n              \"default\": true,\n              \"description\": \"Emit tracing events for this webhook on delivery or error\"\n            },\n            \"auth\": {\n              \"type\": \"object\",\n              \"title\": \"Auth mechanisms\",\n              \"description\": \"Define which auth mechanism the Web-Hook should use\",\n              \"oneOf\": [\n                {\n                  \"$ref\": \"#/definitions/webHookAuthApiKeyProperties\"\n                },\n                {\n                  \"$ref\": \"#/definitions/webHookAuthBasicAuthProperties\"\n                }\n              ]\n            },\n            \"additionalProperties\": false\n          },\n          \"anyOf\": [\n            {\n              \"not\": {\n                \"properties\": {\n                  \"response\": {\n                    \"properties\": {\n                      \"ignore\": {\n                        \"const\": true\n                      }\n                    },\n                    \"required\": [\"ignore\"]\n                  }\n                },\n                \"required\": [\"response\"]\n              }\n            },\n            {\n              \"properties\": {\n                \"can_interrupt\": {\n                  \"const\": false\n                }\n              },\n              \"require\": [\"can_interrupt\"]\n            }\n          ],\n          \"additionalProperties\": false,\n          \"required\": [\"url\", \"method\"]\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\", \"config\"]\n    },\n    \"OIDCClaims\": {\n      \"title\": \"OpenID Connect claims\",\n      \"description\": \"The OpenID Connect claims and optionally their properties which should be included in the id_token or returned from the UserInfo Endpoint.\",\n      \"type\": \"object\",\n      \"examples\": [\n        {\n          \"id_token\": {\n            \"email\": null,\n            \"email_verified\": null\n          }\n        },\n        {\n          \"userinfo\": {\n            \"given_name\": {\n              \"essential\": true\n            },\n            \"nickname\": null,\n            \"email\": {\n              \"essential\": true\n            },\n            \"email_verified\": {\n              \"essential\": true\n            },\n            \"picture\": null,\n            \"http://example.info/claims/groups\": null\n          },\n          \"id_token\": {\n            \"auth_time\": {\n              \"essential\": true\n            },\n            \"acr\": {\n              \"values\": [\"urn:mace:incommon:iap:silver\"]\n            }\n          }\n        }\n      ],\n      \"patternProperties\": {\n        \"^userinfo$|^id_token$\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"patternProperties\": {\n            \".*\": {\n              \"oneOf\": [\n                {\n                  \"const\": null,\n                  \"description\": \"Indicates that this Claim is being requested in the default manner.\"\n                },\n                {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"essential\": {\n                      \"description\": \"Indicates whether the Claim being requested is an Essential Claim.\",\n                      \"type\": \"boolean\"\n                    },\n                    \"value\": {\n                      \"description\": \"Requests that the Claim be returned with a particular value.\",\n                      \"$comment\": \"There seem to be no constrains on value\"\n                    },\n                    \"values\": {\n                      \"description\": \"Requests that the Claim be returned with one of a set of values, with the values appearing in order of preference.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$comment\": \"There seem to be no constrains on individual items\"\n                      }\n                    }\n                  }\n                }\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"selfServiceOIDCProvider\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"examples\": [\"google\"]\n        },\n        \"provider\": {\n          \"title\": \"Provider\",\n          \"description\": \"Can be one of github, github-app, gitlab, generic, google, microsoft, discord, salesforce, slack, facebook, auth0, vk, yandex, apple, spotify, netid, dingtalk, patreon, amazon.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"github\",\n            \"github-app\",\n            \"gitlab\",\n            \"generic\",\n            \"google\",\n            \"microsoft\",\n            \"discord\",\n            \"salesforce\",\n            \"slack\",\n            \"facebook\",\n            \"auth0\",\n            \"vk\",\n            \"yandex\",\n            \"apple\",\n            \"spotify\",\n            \"netid\",\n            \"dingtalk\",\n            \"patreon\",\n            \"line\",\n            \"linkedin\",\n            \"linkedin_v2\",\n            \"lark\",\n            \"x\",\n            \"fedcm-test\",\n            \"amazon\",\n            \"uaepass\"\n          ],\n          \"examples\": [\"google\"]\n        },\n        \"label\": {\n          \"title\": \"Optional string which will be used when generating labels for UI buttons.\",\n          \"type\": \"string\"\n        },\n        \"client_id\": {\n          \"type\": \"string\"\n        },\n        \"client_secret\": {\n          \"type\": \"string\"\n        },\n        \"issuer_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com\"]\n        },\n        \"auth_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com/o/oauth2/v2/auth\"]\n        },\n        \"token_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://www.googleapis.com/oauth2/v4/token\"]\n        },\n        \"mapper_url\": {\n          \"title\": \"Jsonnet Mapper URL\",\n          \"description\": \"The URL where the jsonnet source is located for mapping the provider's data to Ory Kratos data.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/oidc.jsonnet\",\n            \"https://foo.bar.com/path/to/oidc.jsonnet\",\n            \"base64://bG9jYWwgc3ViamVjdCA9I...\"\n          ]\n        },\n        \"scope\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"examples\": [\"offline_access\", \"profile\"]\n          }\n        },\n        \"microsoft_tenant\": {\n          \"title\": \"Azure AD Tenant\",\n          \"description\": \"The Azure AD Tenant to use for authentication.\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"common\",\n            \"organizations\",\n            \"consumers\",\n            \"8eaef023-2b34-4da1-9baa-8bc8c9d6a490\",\n            \"contoso.onmicrosoft.com\"\n          ]\n        },\n        \"subject_source\": {\n          \"title\": \"Microsoft subject source\",\n          \"description\": \"Controls which source the subject identifier is taken from by microsoft provider. If set to `userinfo` (the default) then the identifier is taken from the `sub` field of OIDC ID token or data received from `/userinfo` standard OIDC endpoint. If set to `me` then the `id` field of data structure received from `https://graph.microsoft.com/v1.0/me` is taken as an identifier. If the value is `oid` then the the oid (Object ID) is taken to identify users across different services.\",\n          \"type\": \"string\",\n          \"enum\": [\"userinfo\", \"me\", \"oid\"],\n          \"default\": \"userinfo\",\n          \"examples\": [\"userinfo\"]\n        },\n        \"apple_team_id\": {\n          \"title\": \"Apple Developer Team ID\",\n          \"description\": \"Apple Developer Team ID needed for generating a JWT token for client secret\",\n          \"type\": \"string\",\n          \"examples\": [\"KP76DQS54M\"]\n        },\n        \"apple_private_key_id\": {\n          \"title\": \"Apple Private Key Identifier\",\n          \"description\": \"Sign In with Apple Private Key Identifier needed for generating a JWT token for client secret\",\n          \"type\": \"string\",\n          \"examples\": [\"UX56C66723\"]\n        },\n        \"apple_private_key\": {\n          \"title\": \"Apple Private Key\",\n          \"description\": \"Sign In with Apple Private Key needed for generating a JWT token for client secret\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"-----BEGIN PRIVATE KEY-----\\n........\\n-----END PRIVATE KEY-----\"\n          ]\n        },\n        \"requested_claims\": {\n          \"$ref\": \"#/definitions/OIDCClaims\"\n        },\n        \"organization_id\": {\n          \"title\": \"Organization ID\",\n          \"description\": \"The ID of the organization that this provider belongs to. Only effective in the Ory Network.\",\n          \"type\": \"string\",\n          \"examples\": [\"12345678-1234-1234-1234-123456789012\"]\n        },\n        \"additional_id_token_audiences\": {\n          \"title\": \"Additional client ids allowed when using ID token submission\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"examples\": [\"12345678-1234-1234-1234-123456789012\"]\n          }\n        },\n        \"claims_source\": {\n          \"title\": \"Claims source\",\n          \"description\": \"Can be either `userinfo` (calls the userinfo endpoint to get the claims) or `id_token` (takes the claims from the id token). It defaults to `id_token`\",\n          \"type\": \"string\",\n          \"enum\": [\"id_token\", \"userinfo\"],\n          \"default\": \"id_token\",\n          \"examples\": [\"id_token\", \"userinfo\"]\n        },\n        \"pkce\": {\n          \"title\": \"Proof Key for Code Exchange\",\n          \"description\": \"PKCE controls if the OpenID Connect OAuth2 flow should use PKCE (Proof Key for Code Exchange). IMPORTANT: If you set this to `force`, you must whitelist a different return URL for your OAuth2 client in the provider's configuration. Instead of <base-url>/self-service/methods/oidc/callback/<provider>, you must use <base-url>/self-service/methods/oidc/callback\",\n          \"type\": \"string\",\n          \"enum\": [\"auto\", \"never\", \"force\"],\n          \"default\": \"auto\"\n        },\n        \"fedcm_config_url\": {\n          \"title\": \"Federation Configuration URL\",\n          \"description\": \"The URL where the FedCM IdP configuration is located for the provider. This is only effective in the Ory Network.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://example.com/config.json\"]\n        },\n        \"net_id_token_origin_header\": {\n          \"title\": \"NetID Token Origin Header\",\n          \"description\": \"Contains the orgin header to be used when exchanging a NetID FedCM token for an ID token\",\n          \"type\": \"string\",\n          \"examples\": [\"https://example.com\"]\n        },\n        \"account_linking_mode\": {\n          \"title\": \"Account linking mode\",\n          \"description\": \"Controls how account conflicts are resolved for this provider. `confirm_with_existing_credential` (default) requires the user to verify their identity with an existing credential. `automatic` silently links accounts if the provider verifies email ownership. This is only effective in the Ory Network.\",\n          \"type\": \"string\",\n          \"enum\": [\"confirm_with_existing_credential\", \"automatic\"],\n          \"default\": \"confirm_with_existing_credential\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"id\", \"provider\", \"client_id\", \"mapper_url\"],\n      \"allOf\": [\n        {\n          \"if\": {\n            \"properties\": {\n              \"provider\": {\n                \"const\": \"microsoft\"\n              }\n            },\n            \"required\": [\"provider\"]\n          },\n          \"then\": {\n            \"required\": [\"microsoft_tenant\"]\n          },\n          \"else\": {\n            \"not\": {\n              \"properties\": {\n                \"microsoft_tenant\": {}\n              },\n              \"required\": [\"microsoft_tenant\"]\n            }\n          }\n        },\n        {\n          \"if\": {\n            \"properties\": {\n              \"provider\": {\n                \"const\": \"apple\"\n              }\n            },\n            \"required\": [\"provider\"]\n          },\n          \"then\": {\n            \"not\": {\n              \"properties\": {\n                \"client_secret\": {\n                  \"type\": \"string\",\n                  \"minLength\": 1\n                }\n              },\n              \"required\": [\"client_secret\"]\n            },\n            \"required\": [\n              \"apple_private_key_id\",\n              \"apple_private_key\",\n              \"apple_team_id\"\n            ]\n          },\n          \"else\": {\n            \"required\": [\"client_secret\"],\n            \"allOf\": [\n              {\n                \"not\": {\n                  \"properties\": {\n                    \"apple_team_id\": {\n                      \"type\": \"string\",\n                      \"minLength\": 1\n                    }\n                  },\n                  \"required\": [\"apple_team_id\"]\n                }\n              },\n              {\n                \"not\": {\n                  \"properties\": {\n                    \"apple_private_key_id\": {\n                      \"type\": \"string\",\n                      \"minLength\": 1\n                    }\n                  },\n                  \"required\": [\"apple_private_key_id\"]\n                }\n              },\n              {\n                \"not\": {\n                  \"properties\": {\n                    \"apple_private_key\": {\n                      \"type\": \"string\",\n                      \"minLength\": 1\n                    }\n                  },\n                  \"required\": [\"apple_private_key\"]\n                }\n              }\n            ]\n          }\n        }\n      ]\n    },\n    \"selfServiceHooks\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"anyOf\": [\n          {\n            \"$ref\": \"#/definitions/selfServiceWebHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/b2bSSOHook\"\n          }\n        ]\n      },\n      \"uniqueItems\": true,\n      \"additionalItems\": false\n    },\n    \"selfServiceAfterRecoveryHooks\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"anyOf\": [\n          {\n            \"$ref\": \"#/definitions/selfServiceWebHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n          }\n        ]\n      },\n      \"uniqueItems\": true,\n      \"additionalItems\": false\n    },\n    \"selfServiceAfterSettingsProfileMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceWebHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceShowVerificationUIHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/b2bSSOHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterSettingsAuthMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceWebHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterDefaultLoginMethodHooks\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"anyOf\": [\n          {\n            \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceRequireVerifiedAddressHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceWebHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceVerificationHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceShowVerificationUIHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/b2bSSOHook\"\n          }\n        ]\n      },\n      \"uniqueItems\": true,\n      \"additionalItems\": false\n    },\n    \"selfServiceAfterDefaultLoginMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethodHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterOIDCLoginMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceWebHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceRequireVerifiedAddressHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/b2bSSOHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterRegistrationMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionIssuerHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceWebHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceShowVerificationUIHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/b2bSSOHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"featureRequiredAal\": {\n      \"title\": \"Required Authenticator Assurance Level\",\n      \"description\": \"Sets what Authenticator Assurance Level (used for 2FA) is required to access this feature. If set to `highest_available` then this endpoint requires the highest AAL the identity has set up. If set to `aal1` then the identity can access this feature without 2FA.\",\n      \"type\": \"string\",\n      \"enum\": [\"aal1\", \"highest_available\"],\n      \"default\": \"highest_available\"\n    },\n    \"selfServiceAfterSettings\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"totp\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"webauthn\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"passkey\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"lookup_secret\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"profile\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsProfileMethod\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeLogin\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterLogin\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"webauthn\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"passkey\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterOIDCLoginMethod\"\n        },\n        \"code\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"totp\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"lookup_secret\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethodHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeRegistration\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeSettings\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeRecovery\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeVerification\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterRegistration\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"webauthn\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"passkey\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"code\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterVerification\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterRecovery\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRecoveryHooks\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"tlsxSource\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"path\": {\n          \"title\": \"Path to PEM-encoded Fle\",\n          \"type\": \"string\",\n          \"examples\": [\"path/to/file.pem\"]\n        },\n        \"base64\": {\n          \"title\": \"Base64 Encoded Inline\",\n          \"description\": \"The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n          ]\n        }\n      }\n    },\n    \"tlsx\": {\n      \"title\": \"HTTPS\",\n      \"description\": \"Configure HTTP over TLS (HTTPS). All options can also be set using environment variables by replacing dots (`.`) with underscores (`_`) and uppercasing the key. For example, `some.prefix.tls.key.path` becomes `export SOME_PREFIX_TLS_KEY_PATH`. If all keys are left undefined, TLS will be disabled.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"key\": {\n          \"title\": \"Private Key (PEM)\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/tlsxSource\"\n            }\n          ]\n        },\n        \"cert\": {\n          \"title\": \"TLS Certificate (PEM)\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/tlsxSource\"\n            }\n          ]\n        }\n      }\n    },\n    \"courierTemplates\": {\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"invalid\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"email\": {\n              \"$ref\": \"#/definitions/emailCourierTemplate\"\n            }\n          },\n          \"required\": [\"email\"]\n        },\n        \"valid\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"email\": {\n              \"$ref\": \"#/definitions/emailCourierTemplate\"\n            },\n            \"sms\": {\n              \"$ref\": \"#/definitions/smsCourierTemplate\"\n            }\n          },\n          \"required\": [\"email\"]\n        }\n      }\n    },\n    \"smsCourierTemplate\": {\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"body\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"plaintext\": {\n              \"type\": \"string\",\n              \"description\": \"A template send to the SMS provider.\",\n              \"format\": \"uri\",\n              \"examples\": [\n                \"file://path/to/body.plaintext.gotmpl\",\n                \"https://foo.bar.com/path/to/body.plaintext.gotmpl\"\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"emailCourierTemplate\": {\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"body\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"plaintext\": {\n              \"type\": \"string\",\n              \"description\": \"The fallback template for email clients that do not support html.\",\n              \"format\": \"uri\",\n              \"examples\": [\n                \"file://path/to/body.plaintext.gotmpl\",\n                \"https://foo.bar.com/path/to/body.plaintext.gotmpl\",\n                \"base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQp7eyAuUmVjb3ZlcnlVUkwgfX0K\"\n              ]\n            },\n            \"html\": {\n              \"type\": \"string\",\n              \"description\": \"The default template used for sending out emails. The template can contain HTML \",\n              \"format\": \"uri\",\n              \"examples\": [\n                \"file://path/to/body.html.gotmpl\",\n                \"https://foo.bar.com/path/to/body.html.gotmpl\",\n                \"base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4\"\n              ]\n            }\n          }\n        },\n        \"subject\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/subject.gotmpl\",\n            \"https://foo.bar.com/path/to/subject.gotmpl\",\n            \"base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4\"\n          ]\n        }\n      }\n    }\n  },\n  \"properties\": {\n    \"selfservice\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default_browser_return_url\"],\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"allowed_return_urls\": {\n          \"title\": \"Allowed Return To URLs\",\n          \"description\": \"List of URLs that are allowed to be redirected to. A redirection request is made by appending `?return_to=...` to Login, Registration, and other self-service flows.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"format\": \"uri-reference\"\n          },\n          \"examples\": [\n            [\n              \"https://app.my-app.com/dashboard\",\n              \"/dashboard\",\n              \"https://www.my-app.com/\",\n              \"https://*.my-app.com/\"\n            ]\n          ]\n        },\n        \"flows\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"settings\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"URL of the Settings page.\",\n                  \"description\": \"URL where the Settings UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/user/settings\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/settings\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"privileged_session_max_age\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"required_aal\": {\n                  \"$ref\": \"#/definitions/featureRequiredAal\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterSettings\"\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeSettings\"\n                }\n              }\n            },\n            \"logout\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"after\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  }\n                }\n              }\n            },\n            \"registration\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable User Registration\",\n                  \"description\": \"If set to true will enable [User Registration](https://www.ory.sh/kratos/docs/self-service/flows/user-registration/).\",\n                  \"default\": true\n                },\n                \"login_hints\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Provide Login Hints on Failed Registration\",\n                  \"description\": \"When registration fails because an account with the given credentials or addresses previously signed up, provide login hints about available methods to sign in to the user.\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Registration UI URL\",\n                  \"description\": \"URL where the Registration UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/signup\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/registration\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeRegistration\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterRegistration\"\n                },\n                \"enable_legacy_one_step\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Disable two-step registration\",\n                  \"description\": \"Deprecated, please use `style` instead.\",\n                  \"deprecationMessage\": \"Deprecated, please use `style` instead.\",\n                  \"default\": false\n                },\n                \"style\": {\n                  \"title\": \"Registration Flow Style\",\n                  \"description\": \"The style of the registration flow. If set to `unified` the login flow will be a one-step process. If set to `profile_first` the registration flow will first ask for the profile information first, and then the credentials.\",\n                  \"type\": \"string\",\n                  \"enum\": [\"unified\", \"profile_first\"],\n                  \"default\": \"profile_first\"\n                }\n              }\n            },\n            \"login\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Login UI URL\",\n                  \"description\": \"URL where the Login UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/login\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/login\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"style\": {\n                  \"title\": \"Login Flow Style\",\n                  \"description\": \"The style of the login flow. If set to `unified` the login flow will be a one-step process. If set to `identifier_first` (experimental!) the login flow will first ask for the identifier and then the credentials.\",\n                  \"type\": \"string\",\n                  \"enum\": [\"unified\", \"identifier_first\"],\n                  \"default\": \"unified\"\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeLogin\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterLogin\"\n                }\n              }\n            },\n            \"verification\": {\n              \"title\": \"Email and Phone Verification and Account Activation Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Email/Phone Verification\",\n                  \"description\": \"If set to true will enable [Email and Phone Verification and Account Activation](https://www.ory.sh/kratos/docs/self-service/flows/verify-email-account-activation/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Verify UI URL\",\n                  \"description\": \"URL where the Ory Verify UI is hosted. This is the page where users activate and / or verify their email or telephone number. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/verification\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterVerification\"\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Verification Request Lifespan\",\n                  \"description\": \"Sets how long the verification request (for the UI interaction) is valid.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeVerification\"\n                },\n                \"use\": {\n                  \"title\": \"Verification Strategy\",\n                  \"description\": \"The strategy to use for verification requests\",\n                  \"type\": \"string\",\n                  \"enum\": [\"link\", \"code\"],\n                  \"default\": \"code\"\n                },\n                \"notify_unknown_recipients\": {\n                  \"title\": \"Notify unknown recipients\",\n                  \"description\": \"Whether to notify recipients, if verification was requested for their address.\",\n                  \"type\": \"boolean\",\n                  \"default\": false\n                }\n              }\n            },\n            \"recovery\": {\n              \"title\": \"Account Recovery Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Account Recovery\",\n                  \"description\": \"If set to true will enable [Account Recovery](https://www.ory.sh/kratos/docs/self-service/flows/password-reset-account-recovery/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Recovery UI URL\",\n                  \"description\": \"URL where the Ory Recovery UI is hosted. This is the page where users request and complete account recovery. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/recovery\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterRecovery\"\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Recovery Request Lifespan\",\n                  \"description\": \"Sets how long the recovery request is valid. If expired, the user has to redo the flow.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeRecovery\"\n                },\n                \"use\": {\n                  \"title\": \"Recovery Strategy\",\n                  \"description\": \"The strategy to use for recovery requests\",\n                  \"type\": \"string\",\n                  \"enum\": [\"link\", \"code\"],\n                  \"default\": \"code\"\n                },\n                \"notify_unknown_recipients\": {\n                  \"title\": \"Notify unknown recipients\",\n                  \"description\": \"Whether to notify recipients, if recovery was requested for their account.\",\n                  \"type\": \"boolean\",\n                  \"default\": false\n                }\n              }\n            },\n            \"error\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Ory Kratos Error UI URL\",\n                  \"description\": \"URL where the Ory Kratos Error UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/kratos-error\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/error\"\n                }\n              }\n            }\n          }\n        },\n        \"methods\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"b2b\": {\n              \"title\": \"Single Sign-On for B2B\",\n              \"description\": \"Single Sign-On for B2B allows your customers to bring their own (workforce) identity server (e.g. OneLogin). This feature is not available in the open source licensed code.\",\n              \"type\": \"object\",\n              \"properties\": {\n                \"config\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"organizations\": {\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"type\": \"object\",\n                        \"properties\": {\n                          \"id\": {\n                            \"type\": \"string\",\n                            \"description\": \"The ID of the organization.\",\n                            \"format\": \"uuid\",\n                            \"examples\": [\"00000000-0000-0000-0000-000000000000\"]\n                          },\n                          \"label\": {\n                            \"type\": \"string\",\n                            \"description\": \"The label of the organization.\",\n                            \"examples\": [\"ACME SSO\"]\n                          },\n                          \"domains\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                              \"type\": \"string\",\n                              \"format\": \"hostname\",\n                              \"examples\": [\"my-app.com\"],\n                              \"description\": \"If this domain matches the email's domain, this provider is shown.\"\n                            }\n                          }\n                        }\n                      }\n                    }\n                  }\n                }\n              },\n              \"additionalProperties\": false\n            },\n            \"profile\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Profile Management Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"link\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Link Method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"Link Configuration\",\n                  \"description\": \"Additional configuration for the link strategy.\",\n                  \"properties\": {\n                    \"base_url\": {\n                      \"title\": \"Override the base URL which should be used as the base for recovery and verification links.\",\n                      \"type\": \"string\",\n                      \"deprecationMessage\": \"This option has no effect, because the request URL is now used as the base URL.\",\n                      \"examples\": [\"https://my-app.com\"]\n                    },\n                    \"lifespan\": {\n                      \"title\": \"How long a link is valid for\",\n                      \"type\": \"string\",\n                      \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                      \"default\": \"1h\",\n                      \"examples\": [\"1h\", \"1m\", \"1s\"]\n                    }\n                  }\n                }\n              }\n            },\n            \"code\": {\n              \"type\": \"object\",\n              \"additionalProperties\": true,\n              \"anyOf\": [\n                {\n                  \"properties\": {\n                    \"passwordless_enabled\": {\n                      \"const\": true\n                    },\n                    \"mfa_enabled\": {\n                      \"const\": false\n                    }\n                  }\n                },\n                {\n                  \"properties\": {\n                    \"mfa_enabled\": {\n                      \"const\": true\n                    },\n                    \"passwordless_enabled\": {\n                      \"const\": false\n                    }\n                  }\n                },\n                {\n                  \"properties\": {\n                    \"mfa_enabled\": {\n                      \"const\": false\n                    },\n                    \"passwordless_enabled\": {\n                      \"const\": false\n                    }\n                  }\n                }\n              ],\n              \"properties\": {\n                \"passwordless_enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables login and registration with the code method.\",\n                  \"description\": \"If set to true, code.enabled will be set to true as well.\",\n                  \"default\": false\n                },\n                \"mfa_enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables login flows code method to fulfil MFA requests\",\n                  \"default\": false\n                },\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Code Method\",\n                  \"default\": true\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"Code Configuration\",\n                  \"description\": \"Additional configuration for the code strategy.\",\n                  \"properties\": {\n                    \"lifespan\": {\n                      \"title\": \"How long a code is valid for\",\n                      \"type\": \"string\",\n                      \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                      \"default\": \"1h\",\n                      \"examples\": [\"1h\", \"1m\", \"1s\"]\n                    },\n                    \"max_submissions\": {\n                      \"type\": \"integer\",\n                      \"title\": \"Maximum number of times the code can be submitted before a flow is invalidated\",\n                      \"minimum\": 1,\n                      \"maximum\": 255,\n                      \"default\": 5\n                    },\n                    \"missing_credential_fallback_enabled\": {\n                      \"type\": \"boolean\",\n                      \"title\": \"Enable Code OTP as a Fallback\",\n                      \"description\": \"Enabling this allows users to sign in with the code method, even if their identity schema or their credentials are not set up to use the code method. If enabled, a verified address (such as an email) will be used to send the code to the user. Use with caution and only if actually needed.\",\n                      \"default\": false\n                    }\n                  }\n                }\n              }\n            },\n            \"password\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Username/Email and Password Method\",\n                  \"default\": true\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"Password Configuration\",\n                  \"description\": \"Define how passwords are validated.\",\n                  \"properties\": {\n                    \"haveibeenpwned_host\": {\n                      \"title\": \"Custom haveibeenpwned host\",\n                      \"description\": \"Allows changing the default HIBP host to a self hosted version.\",\n                      \"type\": \"string\",\n                      \"default\": \"api.pwnedpasswords.com\"\n                    },\n                    \"haveibeenpwned_enabled\": {\n                      \"title\": \"Enable the HaveIBeenPwned API\",\n                      \"description\": \"If set to false the password validation does not utilize the Have I Been Pwnd API.\",\n                      \"type\": \"boolean\",\n                      \"default\": true\n                    },\n                    \"max_breaches\": {\n                      \"title\": \"Allow Password Breaches\",\n                      \"description\": \"Defines how often a password may have been breached before it is rejected.\",\n                      \"type\": \"integer\",\n                      \"minimum\": 0,\n                      \"maximum\": 100,\n                      \"default\": 0\n                    },\n                    \"ignore_network_errors\": {\n                      \"title\": \"Ignore Lookup Network Errors\",\n                      \"description\": \"If set to false the password validation fails when the network or the Have I Been Pwnd API is down.\",\n                      \"type\": \"boolean\",\n                      \"default\": true\n                    },\n                    \"min_password_length\": {\n                      \"title\": \"Minimum Password Length\",\n                      \"description\": \"Defines the minimum length of the password.\",\n                      \"type\": \"integer\",\n                      \"default\": 8,\n                      \"minimum\": 6\n                    },\n                    \"identifier_similarity_check_enabled\": {\n                      \"title\": \"Enable password-identifier similarity check\",\n                      \"description\": \"If set to false the password validation does not check for similarity between the password and the user identifier.\",\n                      \"type\": \"boolean\",\n                      \"default\": true\n                    },\n                    \"migrate_hook\": {\n                      \"type\": \"object\",\n                      \"additionalProperties\": false,\n                      \"properties\": {\n                        \"enabled\": {\n                          \"type\": \"boolean\",\n                          \"title\": \"Enable Password Migration\",\n                          \"description\": \"If set to true will enable password migration.\",\n                          \"default\": false\n                        },\n                        \"config\": {\n                          \"type\": \"object\",\n                          \"additionalProperties\": false,\n                          \"properties\": {\n                            \"url\": {\n                              \"type\": \"string\",\n                              \"description\": \"The URL the password migration hook should call\",\n                              \"format\": \"uri\"\n                            },\n                            \"method\": {\n                              \"type\": \"string\",\n                              \"description\": \"The HTTP method to use (GET, POST, etc).\",\n                              \"const\": \"POST\",\n                              \"default\": \"POST\"\n                            },\n                            \"headers\": {\n                              \"type\": \"object\",\n                              \"description\": \"The HTTP headers that must be applied to the password migration hook.\",\n                              \"additionalProperties\": {\n                                \"type\": \"string\"\n                              }\n                            },\n                            \"emit_analytics_event\": {\n                              \"type\": \"boolean\",\n                              \"default\": true,\n                              \"description\": \"Emit tracing events for this hook on delivery or error\"\n                            },\n                            \"auth\": {\n                              \"type\": \"object\",\n                              \"title\": \"Auth mechanisms\",\n                              \"description\": \"Define which auth mechanism the Web-Hook should use\",\n                              \"oneOf\": [\n                                {\n                                  \"$ref\": \"#/definitions/webHookAuthApiKeyProperties\"\n                                },\n                                {\n                                  \"$ref\": \"#/definitions/webHookAuthBasicAuthProperties\"\n                                }\n                              ]\n                            },\n                            \"body\": {\n                              \"type\": \"string\",\n                              \"format\": \"uri\",\n                              \"pattern\": \"^(http|https|file|base64)://\",\n                              \"description\": \"URI pointing to the jsonnet template used for payload generation. Only used for those HTTP methods, which support HTTP body payloads\",\n                              \"examples\": [\n                                \"file:///path/to/body.jsonnet\",\n                                \"file://./body.jsonnet\",\n                                \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgaWRlbnRpdHlfaWQ6IGlmIGN0eFsiaWRlbnRpdHkiXSAhPSBudWxsIHRoZW4gY3R4LmlkZW50aXR5LmlkLAp9=\",\n                                \"https://oryapis.com/default_body.jsonnet\"\n                              ]\n                            },\n                            \"additionalProperties\": false\n                          }\n                        }\n                      }\n                    }\n                  },\n                  \"additionalProperties\": false\n                }\n              }\n            },\n            \"totp\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables the TOTP method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"TOTP Configuration\",\n                  \"properties\": {\n                    \"issuer\": {\n                      \"title\": \"TOTP Issuer\",\n                      \"description\": \"The issuer (e.g. a domain name) will be shown in the TOTP app (e.g. Google Authenticator). It helps the user differentiate between different codes.\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"additionalProperties\": false\n                }\n              }\n            },\n            \"lookup_secret\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables the lookup secret method\",\n                  \"default\": false\n                }\n              }\n            },\n            \"webauthn\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables the WebAuthn method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"WebAuthn Configuration\",\n                  \"properties\": {\n                    \"passwordless\": {\n                      \"type\": \"boolean\",\n                      \"title\": \"Use For Passwordless Flows\",\n                      \"description\": \"If enabled will have the effect that WebAuthn is used for passwordless flows (as a first factor) and not for multi-factor set ups. With this set to true, users will see an option to sign up with WebAuthn on the registration screen.\"\n                    },\n                    \"rp\": {\n                      \"title\": \"Relying Party (RP) Config\",\n                      \"properties\": {\n                        \"display_name\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Display Name\",\n                          \"description\": \"An name to help the user identify this RP.\",\n                          \"examples\": [\"Ory Foundation\"]\n                        },\n                        \"id\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Identifier\",\n                          \"description\": \"The id must be a subset of the domain currently in the browser.\",\n                          \"examples\": [\"ory.sh\"]\n                        },\n                        \"origin\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Origin\",\n                          \"description\": \"An explicit RP origin. If left empty, this defaults to `id`, prepended with the current protocol schema (HTTP or HTTPS).\",\n                          \"format\": \"uri\",\n                          \"deprecationMessage\": \"This field is deprecated. Use `origins` instead.\",\n                          \"examples\": [\"https://www.ory.sh\"]\n                        },\n                        \"origins\": {\n                          \"type\": \"array\",\n                          \"title\": \"Relying Party Origins\",\n                          \"description\": \"A list of explicit RP origins. If left empty, this defaults to either `origin` or `id`, prepended with the current protocol schema (HTTP or HTTPS).\",\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"format\": \"uri\",\n                            \"examples\": [\n                              \"https://www.ory.sh\",\n                              \"https://auth.ory.sh\"\n                            ]\n                          }\n                        },\n                        \"icon\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Icon\",\n                          \"description\": \"An icon to help the user identify this RP.\",\n                          \"format\": \"uri\",\n                          \"deprecationMessage\": \"This field is deprecated and ignored due to security considerations.\",\n                          \"examples\": [\"https://www.ory.sh/an-icon.png\"]\n                        }\n                      },\n                      \"type\": \"object\",\n                      \"oneOf\": [\n                        {\n                          \"required\": [\"id\", \"display_name\"],\n                          \"properties\": {\n                            \"origin\": {\n                              \"not\": {}\n                            },\n                            \"origins\": {\n                              \"not\": {}\n                            }\n                          }\n                        },\n                        {\n                          \"required\": [\"id\", \"display_name\", \"origin\"],\n                          \"properties\": {\n                            \"origin\": {\n                              \"type\": \"string\"\n                            },\n                            \"origins\": {\n                              \"not\": {}\n                            }\n                          }\n                        },\n                        {\n                          \"required\": [\"id\", \"display_name\", \"origins\"],\n                          \"properties\": {\n                            \"origin\": {\n                              \"not\": {}\n                            },\n                            \"origins\": {\n                              \"type\": \"array\",\n                              \"items\": {\n                                \"type\": \"string\"\n                              }\n                            }\n                          }\n                        }\n                      ]\n                    }\n                  },\n                  \"additionalProperties\": false\n                }\n              },\n              \"if\": {\n                \"properties\": {\n                  \"enabled\": {\n                    \"const\": true\n                  }\n                },\n                \"required\": [\"enabled\"]\n              },\n              \"then\": {\n                \"required\": [\"config\"]\n              }\n            },\n            \"passkey\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables the Passkey method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"Passkey Configuration\",\n                  \"properties\": {\n                    \"rp\": {\n                      \"title\": \"Relying Party (RP) Config\",\n                      \"properties\": {\n                        \"display_name\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Display Name\",\n                          \"description\": \"A name to help the user identify this RP.\",\n                          \"examples\": [\"Ory Foundation\"]\n                        },\n                        \"id\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Identifier\",\n                          \"description\": \"The id must be a subset of the domain currently in the browser.\",\n                          \"examples\": [\"ory.sh\"]\n                        },\n                        \"origins\": {\n                          \"type\": \"array\",\n                          \"title\": \"Relying Party Origins\",\n                          \"description\": \"A list of explicit RP origins. If left empty, this defaults to either `origin` or `id`, prepended with the current protocol schema (HTTP or HTTPS).\",\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"examples\": [\n                              \"https://www.ory.sh\",\n                              \"https://auth.ory.sh\"\n                            ]\n                          }\n                        }\n                      },\n                      \"type\": \"object\",\n                      \"required\": [\"display_name\", \"id\"]\n                    }\n                  },\n                  \"additionalProperties\": false\n                }\n              },\n              \"if\": {\n                \"properties\": {\n                  \"enabled\": {\n                    \"const\": true\n                  }\n                },\n                \"required\": [\"enabled\"]\n              },\n              \"then\": {\n                \"required\": [\"config\"]\n              }\n            },\n            \"oidc\": {\n              \"type\": \"object\",\n              \"title\": \"Specify OpenID Connect and OAuth2 Configuration\",\n              \"showEnvVarBlockForObject\": true,\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables OpenID Connect Method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"base_redirect_uri\": {\n                      \"type\": \"string\",\n                      \"title\": \"Base URL for OAuth2 Redirect URIs\",\n                      \"description\": \"Can be used to modify the base URL for OAuth2 Redirect URLs. If unset, the Public Base URL will be used.\",\n                      \"format\": \"uri\",\n                      \"examples\": [\"https://auth.myexample.org/\"]\n                    },\n                    \"providers\": {\n                      \"title\": \"OpenID Connect and OAuth2 Providers\",\n                      \"description\": \"A list and configuration of OAuth2 and OpenID Connect providers Ory Kratos should integrate with.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$ref\": \"#/definitions/selfServiceOIDCProvider\"\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    },\n    \"database\": {\n      \"type\": \"object\",\n      \"title\": \"Database related configuration\",\n      \"description\": \"Miscellaneous settings used in database related tasks (cleanup, etc.)\",\n      \"properties\": {\n        \"cleanup\": {\n          \"type\": \"object\",\n          \"title\": \"Database cleanup settings\",\n          \"description\": \"Settings that controls how the database cleanup process is configured (delays, batch size, etc.)\",\n          \"properties\": {\n            \"batch_size\": {\n              \"type\": \"integer\",\n              \"title\": \"Number of records to clean in one iteration\",\n              \"description\": \"Controls how many records should be purged from one table during database cleanup task\",\n              \"minimum\": 1,\n              \"default\": 100\n            },\n            \"sleep\": {\n              \"type\": \"object\",\n              \"title\": \"Delays between various database cleanup phases\",\n              \"description\": \"Configures delays between each step of the cleanup process. It is useful to tune the process so it will be efficient and performant.\",\n              \"properties\": {\n                \"tables\": {\n                  \"type\": \"string\",\n                  \"title\": \"Delay between each table cleanups\",\n                  \"description\": \"Controls the delay time between cleaning each table in one cleanup iteration\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1m\"\n                }\n              }\n            },\n            \"older_than\": {\n              \"type\": \"string\",\n              \"title\": \"Remove records older than\",\n              \"description\": \"Controls how old records do we want to leave\",\n              \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n              \"default\": \"0s\"\n            }\n          }\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"dsn\": {\n      \"type\": \"string\",\n      \"title\": \"Data Source Name\",\n      \"description\": \"DSN is used to specify the database credentials as a connection URI.\",\n      \"examples\": [\n        \"postgres://user: password@postgresd:5432/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"mysql://user:secret@tcp(mysqld:3306)/database?max_conns=20&max_idle_conns=4\",\n        \"cockroach://user@cockroachdb:26257/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc\"\n      ]\n    },\n    \"courier\": {\n      \"type\": \"object\",\n      \"title\": \"Courier configuration\",\n      \"description\": \"The courier is responsible for sending and delivering messages over email, sms, and other means.\",\n      \"properties\": {\n        \"templates\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"recovery\": {\n              \"$ref\": \"#/definitions/courierTemplates\"\n            },\n            \"recovery_code\": {\n              \"$ref\": \"#/definitions/courierTemplates\"\n            },\n            \"verification\": {\n              \"$ref\": \"#/definitions/courierTemplates\"\n            },\n            \"verification_code\": {\n              \"$ref\": \"#/definitions/courierTemplates\"\n            },\n            \"registration_code\": {\n              \"additionalProperties\": false,\n              \"type\": \"object\",\n              \"properties\": {\n                \"valid\": {\n                  \"additionalProperties\": false,\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"email\": {\n                      \"$ref\": \"#/definitions/emailCourierTemplate\"\n                    },\n                    \"sms\": {\n                      \"$ref\": \"#/definitions/smsCourierTemplate\"\n                    }\n                  },\n                  \"required\": [\"email\"]\n                }\n              }\n            },\n            \"login_code\": {\n              \"additionalProperties\": false,\n              \"type\": \"object\",\n              \"properties\": {\n                \"valid\": {\n                  \"additionalProperties\": false,\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"email\": {\n                      \"$ref\": \"#/definitions/emailCourierTemplate\"\n                    },\n                    \"sms\": {\n                      \"$ref\": \"#/definitions/smsCourierTemplate\"\n                    }\n                  },\n                  \"required\": [\"email\"]\n                }\n              }\n            }\n          }\n        },\n        \"template_override_path\": {\n          \"type\": \"string\",\n          \"title\": \"Override message templates\",\n          \"description\": \"You can override certain or all message templates by pointing this key to the path where the templates are located.\",\n          \"examples\": [\"/conf/courier-templates\"]\n        },\n        \"message_retries\": {\n          \"description\": \"Defines the maximum number of times the sending of a message is retried after it failed before it is marked as abandoned\",\n          \"type\": \"integer\",\n          \"default\": 5,\n          \"examples\": [10, 60]\n        },\n        \"worker\": {\n          \"description\": \"Configures the dispatch worker.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"pull_count\": {\n              \"description\": \"Defines how many messages are pulled from the queue at once.\",\n              \"type\": \"integer\",\n              \"default\": 10\n            },\n            \"pull_wait\": {\n              \"description\": \"Defines how long the worker waits before pulling messages from the queue again.\",\n              \"type\": \"string\",\n              \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n              \"default\": \"1s\"\n            }\n          }\n        },\n        \"delivery_strategy\": {\n          \"title\": \"Delivery Strategy\",\n          \"description\": \"Defines how emails will be sent, either through SMTP (default) or HTTP.\",\n          \"type\": \"string\",\n          \"enum\": [\"smtp\", \"http\"],\n          \"default\": \"smtp\"\n        },\n        \"http\": {\n          \"title\": \"HTTP Configuration\",\n          \"description\": \"Configures outgoing emails using HTTP.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"request_config\": {\n              \"$ref\": \"#/definitions/httpRequestConfig\"\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"smtp\": {\n          \"title\": \"SMTP Configuration\",\n          \"description\": \"Configures outgoing emails using the SMTP protocol.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"connection_uri\": {\n              \"title\": \"SMTP connection string\",\n              \"description\": \"This URI will be used to connect to the SMTP server. Use the scheme smtps for implicit TLS sessions or smtp for explicit StartTLS/cleartext sessions. Please note that TLS is always enforced with certificate trust verification by default for security reasons on both schemes. With the smtp scheme you can use the query parameter (`?disable_starttls=true`) to allow cleartext sessions or (`?disable_starttls=false`) to enforce StartTLS (default behaviour). Additionally, use the query parameter to allow (`?skip_ssl_verify=true`) or disallow (`?skip_ssl_verify=false`) self-signed TLS certificates (default behaviour) on both implicit and explicit TLS sessions.\",\n              \"examples\": [\n                \"smtps://foo:bar@my-mailserver:1234/?skip_ssl_verify=false\",\n                \"smtp://foo:bar@my-mailserver:1234/?disable_starttls=true (NOT RECOMMENDED: Cleartext smtp for devel and legacy infrastructure only)\",\n                \"smtp://foo:bar@my-mailserver:1234/ (Explicit StartTLS with certificate trust verification)\",\n                \"smtp://foo:bar@my-mailserver:1234/?skip_ssl_verify=true (NOT RECOMMENDED: Explicit StartTLS without certificate trust verification)\",\n                \"smtps://foo:bar@my-mailserver:1234/ (Implicit TLS with certificate trust verification)\",\n                \"smtps://foo:bar@my-mailserver:1234/?skip_ssl_verify=true (NOT RECOMMENDED: Implicit TLS without certificate trust verification)\",\n                \"smtps://subdomain.my-mailserver:1234/?server_name=my-mailserver (allows TLS to work if the server is hosted on a sudomain that uses a non-wildcard domain certificate)\"\n              ],\n              \"type\": \"string\",\n              \"pattern\": \"^smtps?://.*\"\n            },\n            \"client_cert_path\": {\n              \"title\": \"SMTP Client certificate path\",\n              \"description\": \"Path of the client X.509 certificate, in case of certificate based client authentication to the SMTP server.\",\n              \"type\": \"string\",\n              \"default\": \"\"\n            },\n            \"client_key_path\": {\n              \"title\": \"SMTP Client private key path\",\n              \"description\": \"Path of the client certificate private key, in case of certificate based client authentication to the SMTP server\",\n              \"type\": \"string\",\n              \"default\": \"\"\n            },\n            \"from_address\": {\n              \"title\": \"SMTP Sender Address\",\n              \"description\": \"The recipient of an email will see this as the sender address.\",\n              \"type\": \"string\",\n              \"format\": \"email\",\n              \"default\": \"no-reply@ory.kratos.sh\"\n            },\n            \"from_name\": {\n              \"title\": \"SMTP Sender Name\",\n              \"description\": \"The recipient of an email will see this as the sender name.\",\n              \"type\": \"string\",\n              \"examples\": [\"Bob\"]\n            },\n            \"headers\": {\n              \"title\": \"SMTP Headers\",\n              \"description\": \"These headers will be passed in the SMTP conversation -- e.g. when using the AWS SES SMTP interface for cross-account sending.\",\n              \"type\": \"object\",\n              \"additionalProperties\": {\n                \"type\": \"string\"\n              },\n              \"examples\": [\n                {\n                  \"X-SES-SOURCE-ARN\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\",\n                  \"X-SES-FROM-ARN\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\",\n                  \"X-SES-RETURN-PATH-ARN\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n                }\n              ]\n            },\n            \"local_name\": {\n              \"title\": \"SMTP HELO/EHLO name\",\n              \"description\": \"Identifier used in the SMTP HELO/EHLO command. Some SMTP relays require a unique identifier.\",\n              \"type\": \"string\",\n              \"default\": \"localhost\"\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"channels\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"title\": \"Courier channel configuration\",\n            \"type\": \"object\",\n            \"properties\": {\n              \"id\": {\n                \"type\": \"string\",\n                \"title\": \"Channel id\",\n                \"description\": \"The channel id. Corresponds to the .via property of the identity schema for recovery, verification, etc. Currently only sms is supported.\",\n                \"maxLength\": 32,\n                \"enum\": [\"sms\"]\n              },\n              \"type\": {\n                \"type\": \"string\",\n                \"title\": \"Channel type\",\n                \"description\": \"The channel type. Currently only http is supported.\",\n                \"enum\": [\"http\"]\n              },\n              \"request_config\": {\n                \"$ref\": \"#/definitions/httpRequestConfig\"\n              }\n            },\n            \"required\": [\"id\", \"request_config\"],\n            \"additionalProperties\": false\n          }\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"oauth2_provider\": {\n      \"title\": \"OAuth2 Provider Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"url\": {\n          \"title\": \"OAuth 2.0 Provider URL.\",\n          \"description\": \"If set, the login and registration flows will handle the Ory OAuth 2.0 & OpenID `login_challenge` query parameter to serve as an OpenID Connect Provider. This URL should point to Ory Hydra when you are not running on the Ory Network and be left untouched otherwise.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"https://some-slug.projects.oryapis.com\",\n            \"https://domain-of-ory-hydra:4445\"\n          ]\n        },\n        \"headers\": {\n          \"title\": \"HTTP Request Headers\",\n          \"description\": \"These headers will be passed in HTTP request to the OAuth2 Provider.\",\n          \"type\": \"object\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          },\n          \"examples\": [\n            {\n              \"Authorization\": \"Bearer some-token\"\n            }\n          ]\n        },\n        \"override_return_to\": {\n          \"title\": \"Persist OAuth2 request between flows\",\n          \"type\": \"boolean\",\n          \"default\": false,\n          \"description\": \"Override the return_to query parameter with the OAuth2 provider request URL when perfoming an OAuth2 login flow.\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"preview\": {\n      \"title\": \"Configure Preview Features\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"default_read_consistency_level\": {\n          \"type\": \"string\",\n          \"title\": \"Default Read Consistency Level\",\n          \"description\": \"The default consistency level to use when reading from the database. Defaults to `strong` to not break existing API contracts. Only set this to `eventual` if you can accept that other read APIs will suddenly return eventually consistent results. It is only effective in Ory Network.\",\n          \"enum\": [\"strong\", \"eventual\"],\n          \"default\": \"strong\"\n        }\n      }\n    },\n    \"serve\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"admin\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"request_log\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"disable_for_health\": {\n                  \"title\": \"Disable health endpoints request logging\",\n                  \"description\": \"Disable request logging for /health/alive and /health/ready endpoints\",\n                  \"type\": \"boolean\",\n                  \"default\": false\n                }\n              },\n              \"additionalProperties\": false\n            },\n            \"base_url\": {\n              \"title\": \"Admin Base URL\",\n              \"description\": \"The URL where the admin endpoint is exposed at.\",\n              \"type\": \"string\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://kratos.private-network:4434/\"]\n            },\n            \"host\": {\n              \"title\": \"Admin Host\",\n              \"description\": \"The host (interface) kratos' admin endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Admin Port\",\n              \"description\": \"The port kratos' admin endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4434],\n              \"default\": 4434\n            },\n            \"socket\": {\n              \"$ref\": \"#/definitions/socket\"\n            },\n            \"tls\": {\n              \"$ref\": \"#/definitions/tlsx\"\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"public\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"request_log\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"disable_for_health\": {\n                  \"title\": \"Disable health endpoints request logging\",\n                  \"description\": \"Disable request logging for /health/alive and /health/ready endpoints\",\n                  \"type\": \"boolean\",\n                  \"default\": false\n                }\n              },\n              \"additionalProperties\": false\n            },\n            \"cors\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"description\": \"Configures Cross Origin Resource Sharing for public endpoints.\",\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether CORS is enabled.\",\n                  \"default\": false\n                },\n                \"allowed_origins\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin.\",\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"minLength\": 1,\n                    \"not\": {\n                      \"type\": \"string\",\n                      \"description\": \"does match all strings that contain two or more (*)\",\n                      \"pattern\": \".*\\\\*.*\\\\*.*\"\n                    },\n                    \"anyOf\": [\n                      {\n                        \"type\": \"string\",\n                        \"format\": \"uri\"\n                      },\n                      {\n                        \"const\": \"*\"\n                      }\n                    ]\n                  },\n                  \"uniqueItems\": true,\n                  \"default\": [\"*\"],\n                  \"examples\": [\n                    [\n                      \"https://example.com\",\n                      \"https://*.example.com\",\n                      \"https://*.foo.example.com\"\n                    ]\n                  ]\n                },\n                \"allowed_methods\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of HTTP methods the user agent is allowed to use with cross-domain requests.\",\n                  \"default\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"enum\": [\n                      \"POST\",\n                      \"GET\",\n                      \"PUT\",\n                      \"PATCH\",\n                      \"DELETE\",\n                      \"CONNECT\",\n                      \"HEAD\",\n                      \"OPTIONS\",\n                      \"TRACE\"\n                    ]\n                  }\n                },\n                \"allowed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n                  \"default\": [\n                    \"Authorization\",\n                    \"Content-Type\",\n                    \"Max-Age\",\n                    \"X-Session-Token\",\n                    \"X-XSRF-TOKEN\",\n                    \"X-CSRF-TOKEN\"\n                  ],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"exposed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"Sets which headers are safe to expose to the API of a CORS API specification.\",\n                  \"default\": [\"Content-Type\"],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"allow_credentials\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\",\n                  \"default\": true\n                },\n                \"options_passthrough\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"TODO\",\n                  \"default\": false\n                },\n                \"max_age\": {\n                  \"type\": \"integer\",\n                  \"description\": \"Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request is preceded by a preflight request.\",\n                  \"default\": 0,\n                  \"minimum\": 0\n                },\n                \"debug\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Adds additional log output to debug server side CORS issues.\",\n                  \"default\": false\n                }\n              }\n            },\n            \"base_url\": {\n              \"$ref\": \"#/definitions/baseUrl\"\n            },\n            \"host\": {\n              \"title\": \"Public Host\",\n              \"description\": \"The host (interface) kratos' public endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Public Port\",\n              \"description\": \"The port kratos' public endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4433],\n              \"default\": 4433\n            },\n            \"socket\": {\n              \"$ref\": \"#/definitions/socket\"\n            },\n            \"tls\": {\n              \"$ref\": \"#/definitions/tlsx\"\n            }\n          },\n          \"additionalProperties\": false\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"tracing\": {\n      \"$ref\": \"https://raw.githubusercontent.com/ory/kratos/c651ecf2e/oryx/otelx/config.schema.json\"\n    },\n    \"log\": {\n      \"title\": \"Log\",\n      \"description\": \"Configure logging using the following options. Logging will always be sent to stdout and stderr.\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"level\": {\n          \"description\": \"Debug enables stack traces on errors. Can also be set using environment variable LOG_LEVEL.\",\n          \"type\": \"string\",\n          \"default\": \"info\",\n          \"enum\": [\n            \"trace\",\n            \"debug\",\n            \"info\",\n            \"warning\",\n            \"error\",\n            \"fatal\",\n            \"panic\"\n          ]\n        },\n        \"leak_sensitive_values\": {\n          \"type\": \"boolean\",\n          \"title\": \"Leak Sensitive Log Values\",\n          \"description\": \"If set will leak sensitive values (e.g. emails) in the logs.\"\n        },\n        \"redaction_text\": {\n          \"type\": \"string\",\n          \"title\": \"Sensitive log value redaction text\",\n          \"description\": \"Text to use, when redacting sensitive log value.\"\n        },\n        \"format\": {\n          \"description\": \"The log format can either be text or JSON.\",\n          \"type\": \"string\",\n          \"enum\": [\"json\", \"text\"]\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"identity\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default_schema_id\": {\n          \"title\": \"The default Identity Schema\",\n          \"description\": \"This Identity Schema will be used as the default for self-service flows. Its ID needs to exist in the \\\"schemas\\\" list.\",\n          \"type\": \"string\",\n          \"default\": \"default\"\n        },\n        \"schemas\": {\n          \"type\": \"array\",\n          \"title\": \"All JSON Schemas for Identity Traits\",\n          \"description\": \"Note that identities that used the \\\"default_schema_url\\\" field in older kratos versions will be corrupted unless you specify their schema url with the id \\\"default\\\" in this list.\",\n          \"examples\": [\n            [\n              {\n                \"id\": \"customer\",\n                \"url\": \"base64://ewogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInR5cGUiOiAib2JqZWN0IiwKICAicHJvcGVydGllcyI6IHsKICAgICJiYXIiOiB7CiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0KICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJiYXIiCiAgXQp9\"\n              },\n              {\n                \"id\": \"employee\",\n                \"url\": \"https://foo.bar.com/path/to/employee.traits.schema.json\"\n              },\n              {\n                \"id\": \"employee-v2\",\n                \"url\": \"file://path/to/employee.v2.traits.schema.json\"\n              }\n            ]\n          ],\n          \"minItems\": 1,\n          \"items\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"id\": {\n                \"title\": \"The schema's ID.\",\n                \"type\": \"string\",\n                \"examples\": [\"employee\"]\n              },\n              \"url\": {\n                \"type\": \"string\",\n                \"title\": \"JSON Schema URL for identity traits schema\",\n                \"description\": \"URL for JSON Schema which describes a identity's traits. Can be a file path, a https URL, or a base64 encoded string.\",\n                \"format\": \"uri\",\n                \"examples\": [\n                  \"file://path/to/identity.traits.schema.json\",\n                  \"https://foo.bar.com/path/to/identity.traits.schema.json\",\n                  \"base64://ewogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInR5cGUiOiAib2JqZWN0IiwKICAicHJvcGVydGllcyI6IHsKICAgICJiYXIiOiB7CiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0KICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJiYXIiCiAgXQp9\"\n                ]\n              },\n              \"selfservice_selectable\": {\n                \"type\": \"boolean\",\n                \"title\": \"Is the schema enabled in self-service flows\",\n                \"description\": \"If set to true, this schema can be used explicity in self-service flows by setting `identity_schema` query parameter to the schema's ID.\",\n                \"default\": false\n              }\n            },\n            \"required\": [\"id\", \"url\"]\n          }\n        }\n      },\n      \"required\": [\"schemas\"],\n      \"additionalProperties\": false\n    },\n    \"secrets\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default\": {\n          \"type\": \"array\",\n          \"title\": \"Default Encryption Signing Secrets\",\n          \"description\": \"The first secret in the array is used for signing and encrypting things while all other keys are used to verify and decrypt older things that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        },\n        \"cookie\": {\n          \"type\": \"array\",\n          \"title\": \"Signing Keys for Cookies\",\n          \"description\": \"The first secret in the array is used for encrypting cookies while all other keys are used to decrypt older cookies that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        },\n        \"pagination\": {\n          \"type\": \"array\",\n          \"title\": \"Secrets to encrypt the pagination token\",\n          \"description\": \"To avoid clients reverse-engineering and relying on the implementation details of the pagination token, it is encrypted with these keys\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"minItems\": 1,\n          \"examples\": [\n            [\n              \"secret used for encryption\",\n              \"old secret kept for decryption\",\n              \"another old secret kept for decryption\"\n            ]\n          ]\n        },\n        \"cipher\": {\n          \"type\": \"array\",\n          \"title\": \"Secrets to use for encryption by cipher\",\n          \"description\": \"The first secret in the array is used for encryption data while all other keys are used to decrypt older data that were signed with.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 32,\n            \"maxLength\": 32\n          },\n          \"minItems\": 1\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"hashers\": {\n      \"title\": \"Hashing Algorithm Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"algorithm\": {\n          \"title\": \"Password hashing algorithm\",\n          \"description\": \"One of the values: argon2, bcrypt.\\nAny other hashes will be migrated to the set algorithm once an identity authenticates using their password.\",\n          \"type\": \"string\",\n          \"default\": \"bcrypt\",\n          \"enum\": [\"argon2\", \"bcrypt\"]\n        },\n        \"argon2\": {\n          \"title\": \"Configuration for the Argon2id hasher.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"memory\": {\n              \"type\": \"string\",\n              \"pattern\": \"^[0-9]+(B|KB|MB|GB|TB|PB|EB)\",\n              \"default\": \"128MB\"\n            },\n            \"iterations\": {\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"default\": 1\n            },\n            \"parallelism\": {\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"description\": \"Number of parallel workers, defaults to 2*runtime.NumCPU().\"\n            },\n            \"salt_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16,\n              \"default\": 16\n            },\n            \"key_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16,\n              \"default\": 32\n            },\n            \"expected_duration\": {\n              \"description\": \"The time a hashing operation (~login latency) should take.\",\n              \"type\": \"string\",\n              \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n              \"default\": \"500ms\"\n            },\n            \"expected_deviation\": {\n              \"description\": \"The standard deviation expected for hashing operations. If this value is exceeded you will be warned in the logs to adjust the parameters.\",\n              \"type\": \"string\",\n              \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n              \"default\": \"500ms\"\n            },\n            \"dedicated_memory\": {\n              \"description\": \"The memory dedicated for Kratos. As password hashing is very resource intense, Kratos will monitor the memory consumption and warn about high values.\",\n              \"type\": \"string\",\n              \"pattern\": \"^[0-9]+(B|KB|MB|GB|TB|PB|EB)\",\n              \"default\": \"1GB\"\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"bcrypt\": {\n          \"title\": \"Configuration for the Bcrypt hasher. Minimum is 4 when --dev flag is used and 12 otherwise.\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"required\": [\"cost\"],\n          \"properties\": {\n            \"cost\": {\n              \"type\": \"integer\",\n              \"minimum\": 4,\n              \"maximum\": 31,\n              \"default\": 12\n            }\n          }\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"ciphers\": {\n      \"title\": \"Cipher Algorithm Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"algorithm\": {\n          \"title\": \"ciphering algorithm\",\n          \"description\": \"One of the values: noop, aes, xchacha20-poly1305\",\n          \"type\": \"string\",\n          \"default\": \"noop\",\n          \"enum\": [\"noop\", \"aes\", \"xchacha20-poly1305\"]\n        }\n      }\n    },\n    \"cookies\": {\n      \"type\": \"object\",\n      \"title\": \"HTTP Cookie Configuration\",\n      \"description\": \"Configure the HTTP Cookies. Applies to both CSRF and session cookies.\",\n      \"properties\": {\n        \"domain\": {\n          \"title\": \"HTTP Cookie Domain\",\n          \"description\": \"Sets the cookie domain for session and CSRF cookies. Useful when dealing with subdomains. Use with care!\",\n          \"type\": \"string\"\n        },\n        \"path\": {\n          \"title\": \"HTTP  Cookie Path\",\n          \"description\": \"Sets the session and CSRF cookie path. Use with care!\",\n          \"type\": \"string\",\n          \"default\": \"/\"\n        },\n        \"secure\": {\n          \"title\": \"Session Cookie Secure Flag\",\n          \"description\": \"Sets the session secure flag. If unset, defaults to !dev mode.\",\n          \"type\": \"string\"\n        },\n        \"same_site\": {\n          \"title\": \"HTTP Cookie Same Site Configuration\",\n          \"description\": \"Sets the session and CSRF cookie SameSite.\",\n          \"type\": \"string\",\n          \"enum\": [\"Strict\", \"Lax\", \"None\"],\n          \"default\": \"Lax\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"session\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"whoami\": {\n          \"title\": \"WhoAmI / ToSession Settings\",\n          \"description\": \"Control how the `/sessions/whoami` endpoint is behaving.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"required_aal\": {\n              \"$ref\": \"#/definitions/featureRequiredAal\"\n            },\n            \"tokenizer\": {\n              \"title\": \"Tokenizer configuration\",\n              \"description\": \"Configure the tokenizer, responsible for converting a session into a token format such as JWT.\",\n              \"type\": \"object\",\n              \"properties\": {\n                \"templates\": {\n                  \"title\": \"Tokenizer templates\",\n                  \"description\": \"A list of different templates that govern how a session is converted to a token format.\",\n                  \"type\": \"object\",\n                  \"patternProperties\": {\n                    \"[a-zA-Z0-9-_.]+\": {\n                      \"type\": \"object\",\n                      \"required\": [\"jwks_url\"],\n                      \"properties\": {\n                        \"ttl\": {\n                          \"type\": \"string\",\n                          \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                          \"default\": \"1m\",\n                          \"title\": \"Token time to live\"\n                        },\n                        \"claims_mapper_url\": {\n                          \"type\": \"string\",\n                          \"format\": \"uri\",\n                          \"title\": \"Jsonnet mapper URL\"\n                        },\n                        \"jwks_url\": {\n                          \"type\": \"string\",\n                          \"format\": \"uri\",\n                          \"title\": \"JSON Web Key Set URL\"\n                        },\n                        \"subject_source\": {\n                          \"type\": \"string\",\n                          \"title\": \"Subject source\",\n                          \"description\": \"The source of the subject claim in the token. Can be one of: `id`, or `external_id`.\",\n                          \"enum\": [\"id\", \"external_id\"],\n                          \"default\": \"id\"\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"lifespan\": {\n          \"title\": \"Session Lifespan\",\n          \"description\": \"Defines how long a session is active. Once that lifespan has been reached, the user needs to sign in again.\",\n          \"type\": \"string\",\n          \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n          \"default\": \"24h\",\n          \"examples\": [\"1h\", \"1m\", \"1s\"]\n        },\n        \"cookie\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"domain\": {\n              \"title\": \"Session Cookie Domain\",\n              \"description\": \"Sets the session cookie domain. Useful when dealing with subdomains. Use with care! Overrides `cookies.domain`.\",\n              \"type\": \"string\"\n            },\n            \"name\": {\n              \"title\": \"Session Cookie Name\",\n              \"description\": \"Sets the session cookie name. Use with care!\",\n              \"type\": \"string\",\n              \"default\": \"ory_kratos_session\"\n            },\n            \"persistent\": {\n              \"title\": \"Make Session Cookie Persistent\",\n              \"description\": \"If set to true will persist the cookie in the end-user's browser using the `max-age` parameter which is set to the `session.lifespan` value. Persistent cookies are not deleted when the browser is closed (e.g. on reboot or alt+f4). This option affects the Ory OAuth2 and OpenID Provider's remember feature as well.\",\n              \"type\": \"boolean\",\n              \"default\": true\n            },\n            \"path\": {\n              \"title\": \"Session Cookie Path\",\n              \"description\": \"Sets the session cookie path. Use with care! Overrides `cookies.path`.\",\n              \"type\": \"string\"\n            },\n            \"secure\": {\n              \"title\": \"Session Cookie Secure Flag\",\n              \"description\": \"Sets the session secure flag. If unset, defaults to !dev mode.\",\n              \"type\": \"string\"\n            },\n            \"same_site\": {\n              \"title\": \"Session Cookie SameSite Configuration\",\n              \"description\": \"Sets the session cookie SameSite. Overrides `cookies.same_site`.\",\n              \"type\": \"string\",\n              \"enum\": [\"Strict\", \"Lax\", \"None\"]\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"earliest_possible_extend\": {\n          \"title\": \"Earliest Possible Session Extension\",\n          \"description\": \"Sets when a session can be extended. Settings this value to `24h` will prevent the session from being extended before until 24 hours before it expires. This setting prevents excessive writes to the database. We highly recommend setting this value.\",\n          \"type\": \"string\",\n          \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n          \"examples\": [\"1h\", \"1m\", \"1s\"]\n        }\n      }\n    },\n    \"security\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"account_enumeration\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"mitigate\": {\n              \"type\": \"boolean\",\n              \"default\": false,\n              \"description\": \"Mitigate account enumeration by making it harder to figure out if an identifier (email, phone number) exists or not. Enabling this setting degrades user experience. This setting does not mitigate all possible attack vectors yet.\"\n            }\n          }\n        }\n      }\n    },\n    \"version\": {\n      \"title\": \"The kratos version this config is written for.\",\n      \"description\": \"SemVer according to https://semver.org/ prefixed with `v` as in our releases.\",\n      \"type\": \"string\",\n      \"pattern\": \"^(v(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?)|$\",\n      \"examples\": [\"v0.5.0-alpha.1\"]\n    },\n    \"dev\": {\n      \"type\": \"boolean\"\n    },\n    \"help\": {\n      \"type\": \"boolean\"\n    },\n    \"sqa-opt-out\": {\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"description\": \"This is a CLI flag and environment variable and can not be set using the config file.\"\n    },\n    \"watch-courier\": {\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"description\": \"This is a CLI flag and environment variable and can not be set using the config file.\"\n    },\n    \"expose-metrics-port\": {\n      \"title\": \"Metrics port\",\n      \"description\": \"The port the courier's metrics endpoint listens on (0/disabled by default). This is a CLI flag and environment variable and can not be set using the config file.\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 65535,\n      \"examples\": [4434],\n      \"default\": 0\n    },\n    \"config\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"description\": \"This is a CLI flag and environment variable and can not be set using the config file.\"\n    },\n    \"clients\": {\n      \"title\": \"Global outgoing network settings\",\n      \"description\": \"Configure how outgoing network calls behave.\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"http\": {\n          \"title\": \"Global HTTP client configuration\",\n          \"description\": \"Configure how outgoing HTTP calls behave.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"disallow_private_ip_ranges\": {\n              \"title\": \"Disallow private IP ranges\",\n              \"description\": \"Disallow all outgoing HTTP calls to private IP ranges. This feature can help protect against SSRF attacks.\",\n              \"type\": \"boolean\",\n              \"default\": false\n            },\n            \"private_ip_exception_urls\": {\n              \"title\": \"Add exempt URLs to private IP ranges\",\n              \"description\": \"Allows the given URLs to be called despite them being in the private IP range. URLs need to have an exact and case-sensitive match to be excempt.\",\n              \"type\": \"array\",\n              \"items\": {\n                \"type\": \"string\",\n                \"format\": \"uri-reference\"\n              },\n              \"default\": []\n            }\n          }\n        },\n        \"web_hook\": {\n          \"title\": \"Global web_hook HTTP client configuration\",\n          \"description\": \"Configure the global HTTP client of the web_hook action.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"header_allowlist\": {\n              \"title\": \"Allowed request headers\",\n              \"description\": \"List of request headers that are forwarded to the web hook target in canonical form.\",\n              \"type\": \"array\",\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"default\": [\n                \"Accept\",\n                \"Accept-Encoding\",\n                \"Accept-Language\",\n                \"Content-Length\",\n                \"Content-Type\",\n                \"Origin\",\n                \"Priority\",\n                \"Referer\",\n                \"Sec-Ch-Ua\",\n                \"Sec-Ch-Ua-Mobile\",\n                \"Sec-Ch-Ua-Platform\",\n                \"Sec-Fetch-Dest\",\n                \"Sec-Fetch-Mode\",\n                \"Sec-Fetch-Site\",\n                \"Sec-Fetch-User\",\n                \"True-Client-Ip\",\n                \"User-Agent\"\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"feature_flags\": {\n      \"title\": \"Feature flags\",\n      \"properties\": {\n        \"cacheable_sessions\": {\n          \"type\": \"boolean\",\n          \"title\": \"Enable Ory Sessions caching\",\n          \"description\": \"If enabled allows Ory Sessions to be cached. Only effective in the Ory Network.\",\n          \"default\": false\n        },\n        \"cacheable_sessions_max_age\": {\n          \"title\": \"Set Ory Session Edge Caching maximum age\",\n          \"description\": \"Set how long Ory Sessions are cached on the edge. If unset, the session expiry will be used. Only effective in the Ory Network.\",\n          \"type\": \"string\",\n          \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\"\n        },\n        \"use_continue_with_transitions\": {\n          \"type\": \"boolean\",\n          \"title\": \"Enable new flow transitions using `continue_with` items\",\n          \"description\": \"If enabled allows new flow transitions using `continue_with` items.\",\n          \"default\": false\n        },\n        \"choose_recovery_address\": {\n          \"type\": \"boolean\",\n          \"title\": \"Enable new recovery screens to pick which address to send a recovery code/link to\",\n          \"description\": \"If enabled, enable new recovery screens to pick which address to send a recovery code to, and can send a code via SMS. It is safe to toggle it back and forth, existing recovery flows will be handled with their respective logic. That is because it is decided at creation time whether a recovery flow is V1 or V2 and this cannot be changed afterwards. Thus, if a recovery flow is created with this flag enabled, it will be created as a recovery v2 flow. If this flag is disabled while this flow is still active, this flow will still be handled with the correct logic (v2).\",\n          \"default\": false\n        },\n        \"legacy_continue_with_verification_ui\": {\n          \"type\": \"boolean\",\n          \"title\": \"Always include show_verification_ui in continue_with\",\n          \"description\": \"If true, restores the legacy behavior of always including `show_verification_ui` in the registration flow's `continue_with` when verification is enabled. If set to false, `show_verification_ui` is only set in `continue_with` if the `show_verification_ui` hook is used. This flag will be removed in the future.\",\n          \"deprecationMessage\": \"This behavior is deprecated and will be removed in the future. Use the `show_verification_hook` in the post-registration hook instead.\",\n          \"default\": false\n        },\n        \"legacy_require_verified_login_error\": {\n          \"type\": \"boolean\",\n          \"title\": \"Return a form error if the login identifier is not verified\",\n          \"description\": \"If true, the login flow will return a form error if the login identifier is not verified, which restores legacy behavior. If this value is false, the `continue_with` array will contain a `show_verification_ui` hook instead.\",\n          \"deprecationMessage\": \"This behavior is deprecated and will be removed in the future. Please upgrade your SDKs.\",\n          \"default\": false\n        },\n        \"faster_session_extend\": {\n          \"type\": \"boolean\",\n          \"title\": \"Enable faster session extension\",\n          \"description\": \"If enabled allows faster session extension by skipping the session lookup. Disabling this feature will be deprecated in the future.\",\n          \"default\": false\n        },\n        \"password_profile_registration_node_group\": {\n          \"title\": \"Registration node group\",\n          \"description\": \"The node group to use for registration flows. Previously, the node group for the password method's profile fields was `password`. Going forward, it will be `default`. This switch can toggle between those two for backwards compatibility.\",\n          \"enum\": [\"password\", \"default\"],\n          \"default\": \"default\"\n        },\n        \"legacy_oidc_registration_node_group\": {\n          \"title\": \"Registration node group for OIDC\",\n          \"description\": \"The node group to use for registration flows. Previously, the node group for the oidc method's profile fields was `oidc`. Going forward, it will be `default`. This switch can toggle between those two for backwards compatibility and will be removed in the future.\",\n          \"default\": false,\n          \"type\": \"boolean\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"organizations\": {\n      \"title\": \"Organizations\",\n      \"description\": \"Please use selfservice.methods.b2b instead. This key will be removed. Only effective in the Ory Network.\",\n      \"type\": \"array\",\n      \"default\": []\n    },\n    \"enterprise\": {\n      \"title\": \"Enterprise features\",\n      \"description\": \"Specifies enterprise features. Only effective in the Ory Network or with a valid license.\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"identity_schema_fallback_url_template\": {\n          \"type\": \"string\",\n          \"title\": \"Fallback URL template for identity schemas\",\n          \"description\": \"A fallback URL template used when looking up identity schemas.\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"revision\": {\n      \"title\": \"Config revision\",\n      \"description\": \"Set a recognizable revision. This could be the commit time or a random value. This value is exposed at the `/health/config` endpoint and allows you to ensure that the correct config is loaded.\",\n      \"type\": \"string\"\n    }\n  },\n  \"allOf\": [\n    {\n      \"if\": {\n        \"properties\": {\n          \"selfservice\": {\n            \"properties\": {\n              \"flows\": {\n                \"oneOf\": [\n                  {\n                    \"properties\": {\n                      \"verification\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"verification\"]\n                  },\n                  {\n                    \"properties\": {\n                      \"recovery\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"recovery\"]\n                  }\n                ]\n              }\n            },\n            \"required\": [\"flows\"]\n          }\n        },\n        \"required\": [\"selfservice\"]\n      },\n      \"then\": {\n        \"required\": [\"courier\"]\n      }\n    },\n    {\n      \"if\": {\n        \"properties\": {\n          \"ciphers\": {\n            \"properties\": {\n              \"algorithm\": {\n                \"oneOf\": [\n                  {\n                    \"const\": \"aes\"\n                  },\n                  {\n                    \"const\": \"xchacha20-poly1305\"\n                  }\n                ]\n              }\n            },\n            \"required\": [\"algorithm\"]\n          }\n        },\n        \"required\": [\"ciphers\"]\n      },\n      \"then\": {\n        \"required\": [\"secrets\"],\n        \"properties\": {\n          \"secrets\": {\n            \"required\": [\"cipher\"]\n          }\n        }\n      }\n    }\n  ],\n  \"required\": [\"identity\", \"dsn\", \"selfservice\"],\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": ".trivyignore",
    "content": "CVE-2022-30065\nCVE-2024-2961\nCVE-2023-2650\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  // Launch configuration to build and launch Kratos\n  // It uses a barebones\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Debug Kratos\",\n      \"type\": \"go\",\n      \"request\": \"launch\",\n      \"mode\": \"debug\",\n      \"program\": \"${workspaceFolder}/main.go\",\n      \"buildFlags\": \"-tags sqlite\",\n      \"preLaunchTask\": \"Kratos: setup\",\n      \"postDebugTask\": \"close tasks\", // stops mailhog. Needed, because VSCode does not re-use existing isBackground tasks\n      \"args\": [\n        \"serve\",\n        \"--dev\",\n        \"--watch-courier\",\n        \"-c=${workspaceFolder}/contrib/quickstart/kratos/email-password/kratos.yml\"\n      ],\n      \"internalConsoleOptions\": \"openOnSessionStart\",\n      \"env\": {\n        \"IDENTITY_SCHEMAS_0_URL\": \"file://${workspaceFolder}/contrib/quickstart/kratos/email-password/identity.schema.json\",\n        \"DSN\": \"sqlite://${workspaceFolder}/.debug.sqlite.db?_fk=true\",\n        \"COURIER_SMTP_CONNECTION_URI\": \"smtp://localhost:8026/?disable_starttls=true\",\n        \"DEV_DISABLE_API_FLOW_ENFORCEMENT\": \"true\",\n        \"SELFSERVICE_METHODS_PASSWORD_CONFIG_HAVEIBEENPWNED_ENABLED\": \"false\", // disable locally, as the integration hangs requests, if internet is slow\n        \"TRACING_PROVIDER\": \"jaeger\",\n        \"TRACING_PROVIDERS_JAEGER_SAMPLING_SERVER_URL\": \"http://127.0.0.1:5778/sampling\",\n        \"TRACING_PROVIDERS_JAEGER_LOCAL_AGENT_ADDRESS\": \"127.0.0.1:6831\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"gopls\": {\n        \"formatting.gofumpt\": true,\n        \"formatting.local\": \"github.com/ory\"\n    }\n}\n"
  },
  {
    "path": ".vscode/tasks.json",
    "content": "{\n  // See https://go.microsoft.com/fwlink/?LinkId=733558\n  // for the documentation about the tasks.json format\n  \"version\": \"2.0.0\",\n  \"tasks\": [\n    {\n      \"label\": \"Kratos: build\",\n      \"type\": \"shell\",\n      \"command\": \"go\",\n      \"args\": [\n        \"build\",\n        \"-tags\",\n        \"sqlite\",\n        \"-o\",\n        \"${workspaceFolder}/kratos\",\n        \"${workspaceFolder}/main.go\"\n      ]\n    },\n    {\n      \"label\": \"Kratos debug: setup sqlite\",\n      \"type\": \"shell\",\n      \"presentation\": {\n        \"echo\": false,\n        \"reveal\": \"always\"\n      },\n      \"dependsOn\": [\"Kratos: build\"],\n      \"command\": \"rm -f ${workspaceFolder}/.debug.sqlite.db && ${workspaceFolder}/kratos migrate sql up -e --yes\",\n      \"options\": {\n        \"env\": {\n          \"DSN\": \"sqlite://${workspaceFolder}/.debug.sqlite.db?_fk=true\"\n        }\n      }\n    },\n    {\n      \"label\": \"Kratos: setup\",\n      \"type\": \"shell\",\n      \"presentation\": {\n        \"reveal\": \"silent\"\n      },\n      \"dependsOn\": [\"Kratos debug: setup sqlite\"],\n      \"problemMatcher\": []\n    },\n    {\n      \"label\": \"close tasks\",\n      \"command\": \"echo ${input:terminate}\",\n      \"type\": \"shell\",\n      \"problemMatcher\": []\n    }\n  ],\n  \"inputs\": [\n    {\n      \"id\": \"terminate\",\n      \"type\": \"command\",\n      \"command\": \"workbench.action.tasks.terminate\",\n      \"args\": \"terminateAll\"\n    }\n  ]\n}\n"
  },
  {
    "path": "AUTHORS",
    "content": "# This is the official list of Kratos authors.\n# If you don't want to be on this list, please contact Ory.\n\nAdam Schepis <adam.schepis@gmail.com>\nAddison Snelling <addison@asnell.io>\nadrian5 <adrian5@users.noreply.github.com>\nAeneas Rekkas <aeneas@ory.sh>\naenoralanar <38026322+aenoralanar@users.noreply.github.com>\nAidan Holland <thehappydinoa@gmail.com>\nAjay Kelkar <ajay.kelkar@ory.sh>\nAlano Terblanche <alano@ory.sh>\nAlexander Probst <probstal.hamm@gmail.com>\nAlexandre Burgoni <burgoni.alexandre@protonmail.com>\nAlexandre Gagneux <46828169+alexGNX@users.noreply.github.com>\nAlexey Reshetnik <getupandgo11@gmail.com>\nAndreas <AndreasGB@users.noreply.github.com>\nAndreas Bucksteeg <tricky42@users.noreply.github.com>\nAndrew Banchich <13824577+andrewbanchich@users.noreply.github.com>\nAndrew Minkin <minkin.andrew@gmail.com>\nAndrew Tan <origandrew@gmail.com>\nAndrey <weivall@gmail.com>\nAndy Steele <andy@spacemeld.com>\nangryPopcorn <me@renyijiu.com>\nAnirudh Oppiliappan <x@icyphox.sh>\nAntoine Beyet <Stoakes@users.noreply.github.com>\nAnuar Ustayev <anuar.ustayev@gmail.com>\nArne <a.luenser@gmail.com>\nArne <arne.luenser@ory.sh>\naspeteRakete <fdubrownik@gmail.com>\nAtreya <44151328+atreya2011@users.noreply.github.com>\nAVA Monitoring <56961522+avamonitoring@users.noreply.github.com>\nAvinash Dwarapu <d.nash.avi@gmail.com>\nb3j0f <b3j0f@users.noreply.github.com>\nBengt Hagemeister <30391176+BengtHagemeister@users.noreply.github.com>\nBengt Hagemeister <bengt@localticketing.de>\nBenjamin Freeman <b.t.freeman16@gmail.com>\nBIKI DAS <bikid475@gmail.com>\nBill Monkman <bmonkman@gmail.com>\nBipin Paul Bedi <bipinpaulbedi@gmail.com>\nBrahm Lower <bplower@gmail.com>\nBrahm Lower <brahm@censys.io>\nBrentChesny <bchesny@fb.com>\nCaptainStandby <18215579+CaptainStandby@users.noreply.github.com>\nCarlos Chida <carlos.chida@gmail.com>\nchlasch <72044019+chlasch@users.noreply.github.com>\ncwei-wish <cwei@wish.com>\nCλctysman <git@cactys.dev>\nDaniel Hobbs <daniel.b.hobbs@gmail.com>\nDavid ALEXANDRE <9482408+david972@users.noreply.github.com>\nDavid Cheung <davidcheung@live.ca>\nDavid Laban <alsuren@gmail.com>\nDavid Laban <david.laban@red-badger.com>\nDavid San <davidsanfr@gmail.com>\nDavid van der Sluijs <david@pexlab.net>\ndebrutal <duennes@gmail.com>\nDejan Filipovic <dejan.filipovic@gmail.com>\nDejan Filipovic <dejan@gensdeconfiance.com>\nDenis Palnitsky <palnitsky@gmail.com>\nDibyajyoti Behera <dibyajyotibehera@gmail.com>\nDimitrij Drus <dadrus@gmx.de>\nDimitrij Drus <dimitrij.drus@innoq.com>\nEifoen <35534229+Eifoen@users.noreply.github.com>\nemrah <emrah.com@gmail.com>\nemtammaru <emtammaru@gmail.com>\nEric B <ericbellotti@gmail.com>\nernax78 <65963237+ernax78@users.noreply.github.com>\nErol Keskin <erolkeskin.dev@gmail.com>\nErol Keskin <erolmon.kskn@gmail.com>\nFabian Meyer <3982806+meyfa@users.noreply.github.com>\nFelix Beuke <felix.j.beuke@gmail.com>\nFerdi <ferdina.kusumah@gmail.com>\nFlorian Kramer <flo.kramer@outlook.com>\nFlorian Nagel <nagelflorian@users.noreply.github.com>\nFlorian Wiech <florian.wiech@gmail.com>\nfrederikhors <41120635+frederikhors@users.noreply.github.com>\nGajewski Dmitriy <dmit8815@gmail.com>\nGeoff Lawler <geoff.lawler@gmail.com>\nGiacomo Mazzamuto <g.mazzamuto@gmail.com>\nGibheer <gibheer+github@zero-knowledge.org>\ngoughjo02 <goughjo02@gmail.com>\nGrant Zvolský <GrantZvolsky@gmail.com>\nHarsimran Singh Maan <maan.harry@gmail.com>\nHenning Perl <henning.perl@gmail.com>\nJacob Lehr <jacoblehr@gmail.com>\nJakob Høgenes <1014990+jakhog@users.noreply.github.com>\nJakob Sinclair <sinclair.jakob@mailbox.org>\nJakub Błaszczyk <daemonsthere@gmail.com>\nJames D. Nurmi <github@qwe.cc>\nJames D. Nurmi <jdnurmi@qwe.cc>\nJason Ertel <jertel@users.noreply.github.com>\nJeffreyThijs <jeffrey.thijs@hotmail.com>\nJelle Besseling <jelle@pingiun.com>\nJhonatan Hulse <jhonatan.hulse@gmail.com>\nJiggyDown <84430646+JiggyDown@users.noreply.github.com>\njingkai <contact@jingk.ai>\njld3103 <jld3103@users.noreply.github.com>\nJoe Krill <joekrill@users.noreply.github.com>\nJohan Forssell <johanforssell@users.noreply.github.com>\nJohn Binstead <67282336+Bin-fluence@users.noreply.github.com>\nJohn <jfcurran@users.noreply.github.com>\nJonas Hungershausen <jonas.hungershausen@ory.sh>\nJonathon Sheffield <samsmug@gmail.com>\nJordan <jdisaacs@hotmail.com>\nJordan May <jwmay2012@gmail.com>\nJuri <1773075+2mol@users.noreply.github.com>\nKevin Goslar <kevin.goslar@gmail.com>\nKim Neunert <kneunert@gmail.com>\nKlaus Herrmann <106238709+kmherrmann@users.noreply.github.com>\nkoenmtb1 <koenmtb1@users.noreply.github.com>\nKoen van Marrewijk <koenmtb1@users.noreply.github.com>\nkszafran <k.p.szafranski@gmail.com>\nKun Chong <kun@refs.cn>\nLandon Pattison <67596936+LandonPattison@users.noreply.github.com>\nLan Phan <quoclan@gmail.com>\nLi Ming <lmcl90@hotmail.com>\nLorenzo Baldassarri <lorenzobaldassarri16@gmail.com>\nŁukasz Harasimowicz <dev@harnash.eu>\nŁukasz Szcześniak <viters@student.agh.edu.pl>\nMárk Bartos <Jackneill@users.noreply.github.com>\nMart Aarma <mart.aarma@nortal.com>\nMartin Eigenbrodt <martin.eigenbrodt@googlemail.com>\nMartin Eigenbrodt <martin.eigenbrodt@innoq.com>\nMartin <martin.boehm@1mbsoftware.net>\nMathias Polligkeit <13847569+woylie@users.noreply.github.com>\nMatt Bonnell <matt.bonnell@icloud.com>\nMatthieu Jacquot <matthieu.jacquot.tech@protonmail.ch>\nMatúš Múčka <zippersk.mm@gmail.com>\nMaurice Freitag <maurice.freitag@hotmail.de>\nMike Milano <coder1@gmail.com>\nMike <perryao@users.noreply.github.com>\nMikołaj Meller <52668809+mmeller-wikia@users.noreply.github.com>\nMiłosz <12242002+mszekiel@users.noreply.github.com>\nMiniDigger | Martin <admin@benndorf.dev>\nMitar <mitar.github@tnode.com>\nMitsuo Heijo <25817501+johejo@users.noreply.github.com>\nNanik <nanikjava@gmail.com>\nNatanael Oliva <natanael.og@gmail.com>\nNeil Rutherford <374275+nrutherford@users.noreply.github.com>\nNichlas Lendal <62498670+pr1ze@users.noreply.github.com>\nNicholas Bush <nickdbush@hotmail.co.uk>\nNick Campbell <nickcampbell18@gmail.com>\nNick Malcev <41836660+nmlc@users.noreply.github.com>\nNick Ufer <nick@ufer.dev>\nNikita Puzankov <humb1t@yandex.ru>\nnipsufn <17983323+nipsufn@users.noreply.github.com>\nOle Petersen <56505957+peteole@users.noreply.github.com>\noliverpool <3864879+oliverpool@users.noreply.github.com>\nOmar A. Hachach <contact@omarhachach.com>\nPatrik Neu <patrik@ory.sh>\nPaweł Hemperek <pawel+github@hemperek.pl>\nPiotr Mścichowski <piotr.mscichowski@gmail.com>\nPrzemysław Czaus <przemyslaw@czaus.pl>\nQuentin Gliech <quentingliech@gmail.com>\nqvamatel <qvamatel@freemail.hu>\nRadek Gruchalski <radekg@users.noreply.github.com>\nRamiBerm <54766858+RamiBerm@users.noreply.github.com>\nRauno Viskus <Rauno56@users.noreply.github.com>\nreshetnik-alexey <oleksii.reshetnik@gmail.com>\nRodrigo Queiro <overdrigzed@gmail.com>\nrvo <ryanvanoss@users.noreply.github.com>\nsawadashota <shota@sslife.tech>\nsawadashota <xiootas@gmail.com>\nSeb <git@erhardt.net>\nseremenko-wish <60801091+seremenko-wish@users.noreply.github.com>\nSergey Plaunov <splaunov@users.noreply.github.com>\nsherbang <brian@sherbang.com>\nshivam <86538330+shivam-909@users.noreply.github.com>\nSimon Lipp <sloonz@gmail.com>\nSimon-Pierre Gingras <892367+spg@users.noreply.github.com>\nSteffen Heidel <kontakt@sthh.eu>\nTannerGabriel <40315960+TannerGabriel@users.noreply.github.com>\nTheodor Brandt <teddy@brandts.se>\ntheotherian <ian.g.simpson@gmail.com>\nThomas Aidan Curran <2030403+tacurran@users.noreply.github.com>\nThomas Guillet <guillet.thomas@gmail.com>\nThomas Ruiz <contact@thomasruiz.eu>\nTomasz Tomalak <12939493+t-tomalak@users.noreply.github.com>\nTom Fenech <tomjwfenech@gmail.com>\nToon van Strijp <toonvanstrijp@gmail.com>\nTsirkin Evgeny <tsirkin@gmail.com>\nttimonen <toni.timonen@iki.fi>\nTyler Battle <tyler@battle.ca>\nVeenaInd <104088519+VeenaInd@users.noreply.github.com>\nVictor Duarte <zvictor@users.noreply.github.com>\nVincent <vincent@ory.sh>\nvinckr <vincent@ory.sh>\nViz <viz@linux.com>\nWeiQi <17816875364@163.com>\nwezzle <wesley@black-sky.nl>\nYagiz Nizipli <yagiz@nizipli.com>\nyon <38630464+yonbh@users.noreply.github.com>\nYorick Holkamp <github@nextpulse.nl>\nyou1996 <45292366+you1996@users.noreply.github.com>\nYuvraj <10830562+evalsocket@users.noreply.github.com>\nZageron <hello@adambryant.ca>\nZhiming Guo <guo.zhming@gmail.com>\nznerol <lo+github@znerol.ch>\n好风 <gwindfree@gmail.com>\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n**Table of Contents**\n\n- [ (2026-03-20)](#2026-03-20)\n- [26.2.0 (2026-03-20)](#2620-2026-03-20)\n  - [Bug Fixes](#bug-fixes)\n  - [Code Refactoring](#code-refactoring)\n  - [Documentation](#documentation)\n  - [Features](#features)\n  - [Tests](#tests)\n  - [Unclassified](#unclassified)\n- [25.4.0 (2025-11-07)](#2540-2025-11-07)\n  - [Breaking Changes](#breaking-changes)\n  - [Related issue(s)](#related-issues)\n  - [Checklist](#checklist)\n  - [Related issue(s)](#related-issues-1)\n  - [Checklist](#checklist-1)\n  - [Related issue(s)](#related-issues-2)\n  - [Checklist](#checklist-2)\n    - [Bug Fixes](#bug-fixes-1)\n    - [Chores](#chores)\n    - [Code Generation](#code-generation)\n    - [Code Refactoring](#code-refactoring-1)\n    - [Documentation](#documentation-1)\n    - [Features](#features-1)\n    - [Reverts](#reverts)\n    - [Tests](#tests-1)\n    - [Unclassified](#unclassified-1)\n- [1.3.0 (2024-09-26)](#130-2024-09-26)\n  - [Breaking Changes](#breaking-changes-1)\n    - [Bug Fixes](#bug-fixes-2)\n    - [Code Generation](#code-generation-1)\n    - [Documentation](#documentation-2)\n    - [Features](#features-2)\n    - [Tests](#tests-2)\n    - [Unclassified](#unclassified-2)\n- [1.2.0 (2024-06-05)](#120-2024-06-05)\n  - [Breaking Changes](#breaking-changes-2)\n    - [Bug Fixes](#bug-fixes-3)\n    - [Code Generation](#code-generation-2)\n    - [Documentation](#documentation-3)\n    - [Features](#features-3)\n    - [Tests](#tests-3)\n    - [Unclassified](#unclassified-3)\n- [1.1.0 (2024-02-20)](#110-2024-02-20)\n  - [Breaking Changes](#breaking-changes-3)\n    - [Bug Fixes](#bug-fixes-4)\n    - [Code Generation](#code-generation-3)\n    - [Documentation](#documentation-4)\n    - [Features](#features-4)\n    - [Reverts](#reverts-1)\n    - [Tests](#tests-4)\n    - [Unclassified](#unclassified-4)\n- [1.0.0 (2023-07-12)](#100-2023-07-12)\n  - [Bug Fixes](#bug-fixes-5)\n  - [Code Generation](#code-generation-4)\n  - [Documentation](#documentation-5)\n  - [Features](#features-5)\n  - [Tests](#tests-5)\n  - [Unclassified](#unclassified-5)\n- [0.13.0 (2023-04-18)](#0130-2023-04-18)\n  - [Breaking Changes](#breaking-changes-4)\n    - [Bug Fixes](#bug-fixes-6)\n    - [Code Generation](#code-generation-5)\n    - [Code Refactoring](#code-refactoring-2)\n    - [Documentation](#documentation-6)\n    - [Features](#features-6)\n    - [Tests](#tests-6)\n    - [Unclassified](#unclassified-6)\n- [0.11.1 (2023-01-14)](#0111-2023-01-14)\n  - [Breaking Changes](#breaking-changes-5)\n    - [Bug Fixes](#bug-fixes-7)\n    - [Code Generation](#code-generation-6)\n    - [Documentation](#documentation-7)\n    - [Features](#features-7)\n    - [Tests](#tests-7)\n- [0.11.0 (2022-12-02)](#0110-2022-12-02)\n  - [Code Generation](#code-generation-7)\n  - [Features](#features-8)\n- [0.11.0-alpha.0.pre.2 (2022-11-28)](#0110-alpha0pre2-2022-11-28)\n  - [Breaking Changes](#breaking-changes-6)\n    - [Bug Fixes](#bug-fixes-8)\n    - [Code Generation](#code-generation-8)\n    - [Code Refactoring](#code-refactoring-3)\n    - [Documentation](#documentation-8)\n    - [Features](#features-9)\n    - [Reverts](#reverts-2)\n    - [Tests](#tests-8)\n    - [Unclassified](#unclassified-7)\n- [0.10.1 (2022-06-01)](#0101-2022-06-01)\n  - [Bug Fixes](#bug-fixes-9)\n  - [Code Generation](#code-generation-9)\n- [0.10.0 (2022-05-30)](#0100-2022-05-30)\n  - [Breaking Changes](#breaking-changes-7)\n    - [Bug Fixes](#bug-fixes-10)\n    - [Code Generation](#code-generation-10)\n    - [Code Refactoring](#code-refactoring-4)\n    - [Documentation](#documentation-9)\n    - [Features](#features-10)\n    - [Tests](#tests-9)\n    - [Unclassified](#unclassified-8)\n- [0.9.0-alpha.3 (2022-03-25)](#090-alpha3-2022-03-25)\n  - [Breaking Changes](#breaking-changes-8)\n    - [Bug Fixes](#bug-fixes-11)\n    - [Code Generation](#code-generation-11)\n    - [Documentation](#documentation-10)\n- [0.9.0-alpha.2 (2022-03-22)](#090-alpha2-2022-03-22)\n  - [Bug Fixes](#bug-fixes-12)\n  - [Code Generation](#code-generation-12)\n- [0.9.0-alpha.1 (2022-03-21)](#090-alpha1-2022-03-21)\n  - [Breaking Changes](#breaking-changes-9)\n    - [Bug Fixes](#bug-fixes-13)\n    - [Code Generation](#code-generation-13)\n    - [Code Refactoring](#code-refactoring-5)\n    - [Documentation](#documentation-11)\n    - [Features](#features-11)\n    - [Tests](#tests-10)\n    - [Unclassified](#unclassified-9)\n- [0.8.3-alpha.1.pre.0 (2022-01-21)](#083-alpha1pre0-2022-01-21)\n  - [Breaking Changes](#breaking-changes-10)\n    - [Bug Fixes](#bug-fixes-14)\n    - [Code Generation](#code-generation-14)\n    - [Code Refactoring](#code-refactoring-6)\n    - [Documentation](#documentation-12)\n    - [Features](#features-12)\n    - [Tests](#tests-11)\n- [0.8.2-alpha.1 (2021-12-17)](#082-alpha1-2021-12-17)\n  - [Bug Fixes](#bug-fixes-15)\n  - [Code Generation](#code-generation-15)\n  - [Documentation](#documentation-13)\n- [0.8.1-alpha.1 (2021-12-13)](#081-alpha1-2021-12-13)\n  - [Bug Fixes](#bug-fixes-16)\n  - [Code Generation](#code-generation-16)\n  - [Documentation](#documentation-14)\n  - [Features](#features-13)\n  - [Tests](#tests-12)\n- [0.8.0-alpha.4.pre.0 (2021-11-09)](#080-alpha4pre0-2021-11-09)\n  - [Breaking Changes](#breaking-changes-11)\n    - [Bug Fixes](#bug-fixes-17)\n    - [Code Generation](#code-generation-17)\n    - [Documentation](#documentation-15)\n    - [Features](#features-14)\n    - [Tests](#tests-13)\n- [0.8.0-alpha.3 (2021-10-28)](#080-alpha3-2021-10-28)\n  - [Bug Fixes](#bug-fixes-18)\n  - [Code Generation](#code-generation-18)\n- [0.8.0-alpha.2 (2021-10-28)](#080-alpha2-2021-10-28)\n  - [Code Generation](#code-generation-19)\n- [0.8.0-alpha.1 (2021-10-27)](#080-alpha1-2021-10-27)\n  - [Breaking Changes](#breaking-changes-12)\n    - [Bug Fixes](#bug-fixes-19)\n    - [Code Generation](#code-generation-20)\n    - [Code Refactoring](#code-refactoring-7)\n    - [Documentation](#documentation-16)\n    - [Features](#features-15)\n    - [Reverts](#reverts-3)\n    - [Tests](#tests-14)\n    - [Unclassified](#unclassified-10)\n- [0.7.6-alpha.1 (2021-09-12)](#076-alpha1-2021-09-12)\n  - [Code Generation](#code-generation-21)\n- [0.7.5-alpha.1 (2021-09-11)](#075-alpha1-2021-09-11)\n  - [Code Generation](#code-generation-22)\n- [0.7.4-alpha.1 (2021-09-09)](#074-alpha1-2021-09-09)\n  - [Bug Fixes](#bug-fixes-20)\n  - [Code Generation](#code-generation-23)\n  - [Documentation](#documentation-17)\n  - [Features](#features-16)\n  - [Tests](#tests-15)\n- [0.7.3-alpha.1 (2021-08-28)](#073-alpha1-2021-08-28)\n  - [Bug Fixes](#bug-fixes-21)\n  - [Code Generation](#code-generation-24)\n  - [Documentation](#documentation-18)\n  - [Features](#features-17)\n- [0.7.1-alpha.1 (2021-07-22)](#071-alpha1-2021-07-22)\n  - [Bug Fixes](#bug-fixes-22)\n  - [Code Generation](#code-generation-25)\n  - [Documentation](#documentation-19)\n  - [Tests](#tests-16)\n- [0.7.0-alpha.1 (2021-07-13)](#070-alpha1-2021-07-13)\n  - [Breaking Changes](#breaking-changes-13)\n    - [Bug Fixes](#bug-fixes-23)\n    - [Code Generation](#code-generation-26)\n    - [Code Refactoring](#code-refactoring-8)\n    - [Documentation](#documentation-20)\n    - [Features](#features-18)\n    - [Tests](#tests-17)\n    - [Unclassified](#unclassified-11)\n- [0.6.3-alpha.1 (2021-05-17)](#063-alpha1-2021-05-17)\n  - [Breaking Changes](#breaking-changes-14)\n    - [Bug Fixes](#bug-fixes-24)\n    - [Code Generation](#code-generation-27)\n    - [Code Refactoring](#code-refactoring-9)\n- [0.6.2-alpha.1 (2021-05-14)](#062-alpha1-2021-05-14)\n  - [Code Generation](#code-generation-28)\n  - [Documentation](#documentation-21)\n- [0.6.1-alpha.1 (2021-05-11)](#061-alpha1-2021-05-11)\n  - [Code Generation](#code-generation-29)\n  - [Features](#features-19)\n- [0.6.0-alpha.2 (2021-05-07)](#060-alpha2-2021-05-07)\n  - [Bug Fixes](#bug-fixes-25)\n  - [Code Generation](#code-generation-30)\n  - [Features](#features-20)\n- [0.6.0-alpha.1 (2021-05-05)](#060-alpha1-2021-05-05)\n  - [Breaking Changes](#breaking-changes-15)\n    - [Bug Fixes](#bug-fixes-26)\n    - [Code Generation](#code-generation-31)\n    - [Code Refactoring](#code-refactoring-10)\n    - [Documentation](#documentation-22)\n    - [Features](#features-21)\n    - [Tests](#tests-18)\n    - [Unclassified](#unclassified-12)\n- [0.5.5-alpha.1 (2020-12-09)](#055-alpha1-2020-12-09)\n  - [Bug Fixes](#bug-fixes-27)\n  - [Code Generation](#code-generation-32)\n  - [Documentation](#documentation-23)\n  - [Features](#features-22)\n  - [Tests](#tests-19)\n  - [Unclassified](#unclassified-13)\n- [0.5.4-alpha.1 (2020-11-11)](#054-alpha1-2020-11-11)\n  - [Bug Fixes](#bug-fixes-28)\n  - [Code Generation](#code-generation-33)\n  - [Code Refactoring](#code-refactoring-11)\n  - [Documentation](#documentation-24)\n  - [Features](#features-23)\n- [0.5.3-alpha.1 (2020-10-27)](#053-alpha1-2020-10-27)\n  - [Bug Fixes](#bug-fixes-29)\n  - [Code Generation](#code-generation-34)\n  - [Documentation](#documentation-25)\n  - [Features](#features-24)\n  - [Tests](#tests-20)\n- [0.5.2-alpha.1 (2020-10-22)](#052-alpha1-2020-10-22)\n  - [Bug Fixes](#bug-fixes-30)\n  - [Code Generation](#code-generation-35)\n  - [Documentation](#documentation-26)\n  - [Tests](#tests-21)\n- [0.5.1-alpha.1 (2020-10-20)](#051-alpha1-2020-10-20)\n  - [Bug Fixes](#bug-fixes-31)\n  - [Code Generation](#code-generation-36)\n  - [Documentation](#documentation-27)\n  - [Features](#features-25)\n  - [Tests](#tests-22)\n  - [Unclassified](#unclassified-14)\n- [0.5.0-alpha.1 (2020-10-15)](#050-alpha1-2020-10-15)\n  - [Breaking Changes](#breaking-changes-16)\n    - [Bug Fixes](#bug-fixes-32)\n    - [Code Generation](#code-generation-37)\n    - [Code Refactoring](#code-refactoring-12)\n    - [Documentation](#documentation-28)\n    - [Features](#features-26)\n    - [Tests](#tests-23)\n    - [Unclassified](#unclassified-15)\n- [0.4.6-alpha.1 (2020-07-13)](#046-alpha1-2020-07-13)\n  - [Bug Fixes](#bug-fixes-33)\n  - [Code Generation](#code-generation-38)\n- [0.4.5-alpha.1 (2020-07-13)](#045-alpha1-2020-07-13)\n  - [Bug Fixes](#bug-fixes-34)\n  - [Code Generation](#code-generation-39)\n- [0.4.4-alpha.1 (2020-07-10)](#044-alpha1-2020-07-10)\n  - [Bug Fixes](#bug-fixes-35)\n  - [Code Generation](#code-generation-40)\n  - [Documentation](#documentation-29)\n- [0.4.3-alpha.1 (2020-07-08)](#043-alpha1-2020-07-08)\n  - [Bug Fixes](#bug-fixes-36)\n  - [Code Generation](#code-generation-41)\n- [0.4.2-alpha.1 (2020-07-08)](#042-alpha1-2020-07-08)\n  - [Bug Fixes](#bug-fixes-37)\n  - [Code Generation](#code-generation-42)\n- [0.4.0-alpha.1 (2020-07-08)](#040-alpha1-2020-07-08)\n  - [Breaking Changes](#breaking-changes-17)\n    - [Bug Fixes](#bug-fixes-38)\n    - [Code Generation](#code-generation-43)\n    - [Code Refactoring](#code-refactoring-13)\n    - [Documentation](#documentation-30)\n    - [Features](#features-27)\n    - [Unclassified](#unclassified-16)\n- [0.3.0-alpha.1 (2020-05-15)](#030-alpha1-2020-05-15)\n  - [Breaking Changes](#breaking-changes-18)\n    - [Bug Fixes](#bug-fixes-39)\n    - [Chores](#chores-1)\n    - [Code Refactoring](#code-refactoring-14)\n    - [Documentation](#documentation-31)\n    - [Features](#features-28)\n    - [Unclassified](#unclassified-17)\n- [0.2.1-alpha.1 (2020-05-05)](#021-alpha1-2020-05-05)\n  - [Chores](#chores-2)\n  - [Documentation](#documentation-32)\n- [0.2.0-alpha.2 (2020-05-04)](#020-alpha2-2020-05-04)\n  - [Breaking Changes](#breaking-changes-19)\n    - [Bug Fixes](#bug-fixes-40)\n    - [Chores](#chores-3)\n    - [Code Refactoring](#code-refactoring-15)\n    - [Documentation](#documentation-33)\n    - [Features](#features-29)\n    - [Unclassified](#unclassified-18)\n- [0.1.1-alpha.1 (2020-02-18)](#011-alpha1-2020-02-18)\n  - [Bug Fixes](#bug-fixes-41)\n  - [Code Refactoring](#code-refactoring-16)\n  - [Documentation](#documentation-34)\n- [0.1.0-alpha.6 (2020-02-16)](#010-alpha6-2020-02-16)\n  - [Bug Fixes](#bug-fixes-42)\n  - [Code Refactoring](#code-refactoring-17)\n  - [Documentation](#documentation-35)\n  - [Features](#features-30)\n- [0.1.0-alpha.5 (2020-02-06)](#010-alpha5-2020-02-06)\n  - [Documentation](#documentation-36)\n  - [Features](#features-31)\n- [0.1.0-alpha.4 (2020-02-06)](#010-alpha4-2020-02-06)\n  - [Continuous Integration](#continuous-integration)\n  - [Documentation](#documentation-37)\n- [0.1.0-alpha.3 (2020-02-06)](#010-alpha3-2020-02-06)\n  - [Continuous Integration](#continuous-integration-1)\n- [0.1.0-alpha.2 (2020-02-03)](#010-alpha2-2020-02-03)\n  - [Bug Fixes](#bug-fixes-43)\n  - [Documentation](#documentation-38)\n  - [Features](#features-32)\n  - [Unclassified](#unclassified-19)\n- [0.1.0-alpha.1 (2020-01-31)](#010-alpha1-2020-01-31)\n  - [Documentation](#documentation-39)\n- [0.0.3-alpha.15 (2020-01-31)](#003-alpha15-2020-01-31)\n  - [Unclassified](#unclassified-20)\n- [0.0.3-alpha.14 (2020-01-31)](#003-alpha14-2020-01-31)\n  - [Unclassified](#unclassified-21)\n- [0.0.3-alpha.13 (2020-01-31)](#003-alpha13-2020-01-31)\n  - [Unclassified](#unclassified-22)\n- [0.0.3-alpha.11 (2020-01-31)](#003-alpha11-2020-01-31)\n  - [Unclassified](#unclassified-23)\n- [0.0.3-alpha.10 (2020-01-31)](#003-alpha10-2020-01-31)\n  - [Unclassified](#unclassified-24)\n- [0.0.3-alpha.7 (2020-01-30)](#003-alpha7-2020-01-30)\n  - [Unclassified](#unclassified-25)\n- [0.0.3-alpha.5 (2020-01-30)](#003-alpha5-2020-01-30)\n  - [Continuous Integration](#continuous-integration-2)\n  - [Unclassified](#unclassified-26)\n- [0.0.3-alpha.4 (2020-01-30)](#003-alpha4-2020-01-30)\n  - [Unclassified](#unclassified-27)\n- [0.0.3-alpha.2 (2020-01-30)](#003-alpha2-2020-01-30)\n  - [Unclassified](#unclassified-28)\n- [0.0.3-alpha.1 (2020-01-30)](#003-alpha1-2020-01-30)\n  - [Unclassified](#unclassified-29)\n- [0.0.1-alpha.9 (2020-01-29)](#001-alpha9-2020-01-29)\n  - [Continuous Integration](#continuous-integration-3)\n- [0.0.2-alpha.1 (2020-01-29)](#002-alpha1-2020-01-29)\n  - [Unclassified](#unclassified-30)\n- [0.0.1-alpha.6 (2020-01-29)](#001-alpha6-2020-01-29)\n  - [Continuous Integration](#continuous-integration-4)\n- [0.0.1-alpha.5 (2020-01-29)](#001-alpha5-2020-01-29)\n  - [Continuous Integration](#continuous-integration-5)\n  - [Unclassified](#unclassified-31)\n- [0.0.1-alpha.3 (2020-01-28)](#001-alpha3-2020-01-28)\n  - [Continuous Integration](#continuous-integration-6)\n  - [Documentation](#documentation-40)\n  - [Unclassified](#unclassified-32)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# [](https://github.com/ory/kratos/compare/v26.2.0...v) (2026-03-20)\n\n# [26.2.0](https://github.com/ory/kratos/compare/v25.4.0...v26.2.0) (2026-03-20)\n\nv26.2.0\n\n### Bug Fixes\n\n- remove more instances of injecting unrecoverable email faults\n  ([81e9151](https://github.com/ory/kratos/commit/81e9151758d89d31a673b2513102ccb9fe46fd1f)):\n\n  GitOrigin-RevId: 7432f6c605be10cc59b58cf656736d627d7d6f74\n\n- Add missing indices on identity_id\n  ([a085d87](https://github.com/ory/kratos/commit/a085d872fa180f28f22809fc6624cfc1e8318580)):\n\n  GitOrigin-RevId: 0e9820c93111db7f89f85325a40081737e89978f\n\n- Add missing StrategyUsed attribute to Login and registration events\n  ([e72c297](https://github.com/ory/kratos/commit/e72c2972340fa58415d53cc939d3e42e8ea6d6f9)):\n\n  GitOrigin-RevId: 34d5db665ed61f78a94249d59ea686abce048983\n\n- Add missing transient nodes clear\n  ([ed56dac](https://github.com/ory/kratos/commit/ed56daccaafb6b98f889b61834a8d8d31a055930)):\n\n  GitOrigin-RevId: 8d262434213c9fbcb483b8fdc8a498e1167ed26f\n\n- Add oidc linking/unlinking to api settings flow\n  ([6a6928c](https://github.com/ory/kratos/commit/6a6928c401084443f2475a9361096e9ddfcd1bb0)):\n\n  GitOrigin-RevId: c3fc0688e531326c0e00b797402d01e324b9abfb\n\n- Always retry curl invocations to surmount transient third-party failures\n  ([2473954](https://github.com/ory/kratos/commit/2473954f4e3e8b0d2f8d18a36ddb08c02953cfc2)):\n\n  GitOrigin-RevId: b23ca94a4e1ba11367012a70a9e528b614f3ceb9\n\n- Base64encoded schemaURL cannot be resolved\n  ([a86c212](https://github.com/ory/kratos/commit/a86c212198369083aaf47ab38e798781d42ceb34)):\n\n  GitOrigin-RevId: 7b224a32afd2ff5ebf62ef96d797258e8de56afd\n\n- Batch identity error propagation\n  ([2f9c3e3](https://github.com/ory/kratos/commit/2f9c3e3490e077b46bcff8e1167ee4204c00e328)):\n\n  GitOrigin-RevId: 66a8cca953ee45f50adcdee2aabcb1fa6c429e50\n\n- Clarify password import\n  ([849b0de](https://github.com/ory/kratos/commit/849b0def539bbe96ccca950a99998fcd3b0b6618)):\n\n  GitOrigin-RevId: 8d1bffaae93e9aa484dd67803c2ad86398500b37\n\n- Context passing in jsonnetsecure\n  ([7e33125](https://github.com/ory/kratos/commit/7e33125db8fcbc2163649a03eab20e5e6394c7bb)):\n\n  GitOrigin-RevId: 5545bf7446208fafbc8f45f88ab2903a7000a318\n\n- Correctly scan SQL `NULL` into go JSON types\n  ([6183672](https://github.com/ory/kratos/commit/6183672902a86988b82c6b4854089df8d6b96915)):\n\n  GitOrigin-RevId: 7493c9efdb70ceea61e216660b83408bc7a5d367\n\n- Courier should not retry message dispatches in one go\n  ([70f7b38](https://github.com/ory/kratos/commit/70f7b38ab3cf1ac198537379c09e9332f24d4d9f)):\n\n  GitOrigin-RevId: f7631f29dff77e700e7495376527eca1f56b0e51\n\n- Data race making test flaky\n  ([c651ecf](https://github.com/ory/kratos/commit/c651ecf2ebef1fa6bd43ae57f06c4133b05ce068)):\n\n  GitOrigin-RevId: 263cada3af66101a06e617077ddd508d8ef86705\n\n- Deadlock when using -parallel 1\n  ([8adaa02](https://github.com/ory/kratos/commit/8adaa024e8701c974f7644ec08d1be5caeb15822)):\n\n  GitOrigin-RevId: 467c9c3c2ef22da59b31b7fadc443ca66bf208b9\n\n- Don't attempt to redirect to ory.com in kratos tests\n  ([a06b3c2](https://github.com/ory/kratos/commit/a06b3c2b7d827aa64328825c38132c167044d852)):\n\n  GitOrigin-RevId: 5ff2118ee1ded9ddfd6f3e8c2d11ebdac46b70cc\n\n- Down migrations in newer MySQL versions\n  ([e948a0b](https://github.com/ory/kratos/commit/e948a0bfd4baf2bbb1382cd8a778cafa9697affa)):\n\n  GitOrigin-RevId: 85c133fa17445d2579918d673db27d9e68e3fc8b\n\n- Duplicate credential error placeholder case mismatch\n  ([84ee596](https://github.com/ory/kratos/commit/84ee59662ce43e250b00716c95aa47b37eba82f1)):\n\n  GitOrigin-RevId: 04b013ad8069ef993d337a0cb97ea7fea84c188e\n\n- Failing down migration\n  ([7bb24c5](https://github.com/ory/kratos/commit/7bb24c591704b83ea2fee62029029dd71786dc30)):\n\n  GitOrigin-RevId: 30d15680f8916d2ec12c2acdad6768cb3c0ed439\n\n- Fetch login challenge after code submissions\n  ([048d315](https://github.com/ory/kratos/commit/048d3150e518c8bf7a9cd4c7e57ea0617ca34ff5)):\n\n  GitOrigin-RevId: 4ff0402e96d4b8242bbfcbae518f96c77e1fdf09\n\n- Fix benchmark test\n  ([2886abe](https://github.com/ory/kratos/commit/2886abe1a0e39a63dc95dc6e1b009655af9aa4bb)):\n\n  GitOrigin-RevId: 8cd5d90c7d7124671389cafbb89b87e6c9ced34c\n\n- Fix data race in courier test by protecting slice with mutex\n  ([6673982](https://github.com/ory/kratos/commit/66739820c9d45ad4bc465b2ce3e10311967e29e4)):\n\n  GitOrigin-RevId: ae11f746a59d8c0c35a04c1242e8c23ad4aa2497\n\n- Fix flaky email test\n  ([01e1dd0](https://github.com/ory/kratos/commit/01e1dd04dce4ca08ba8a855866f47d635ed68076)):\n\n  GitOrigin-RevId: feab815802aa1088bceca0ea7a968d99f674e657\n\n- Handle batch identities errors more gracefully\n  ([952d7ea](https://github.com/ory/kratos/commit/952d7ea7829ba84699d51c52c5ea2eaf86a09c13)):\n\n  GitOrigin-RevId: 973ed113f3cb0f6b3b43897b67d02fb5caf2533b\n\n- Incorrect default value for page_tokens\n  ([9a5f8b9](https://github.com/ory/kratos/commit/9a5f8b9dee2eebfacd7be7440aadfa764c2f1a62)):\n\n  GitOrigin-RevId: 74d3aa40f77a7b08f7616a5d00dd6097b6441e08\n\n- Incorrect error handling\n  ([1757cdd](https://github.com/ory/kratos/commit/1757cdd2896235acb6d1d11901adfe8ec17a270a)):\n\n  GitOrigin-RevId: 0fd490b859fdd3df0f628904dc4b9531b28a8f68\n\n- Incorrect usage of database/sql\n  ([590d898](https://github.com/ory/kratos/commit/590d898dc7fcbd90ff6abc94f97cd1795315ba1e)):\n\n  GitOrigin-RevId: a6bc423928fb9910a76e0b18afa2fc2a56fc052d\n\n- **kratos:** Otp fast-path 2fa body error\n  ([95341a8](https://github.com/ory/kratos/commit/95341a83a6b639aeec7db9cbec44a93b55bfca98)):\n\n  GitOrigin-RevId: 86ef2d84eed29078257d4fc30ca7e278d00e9947\n\n- Lint\n  ([e7045c5](https://github.com/ory/kratos/commit/e7045c55a35c10ba5c9ef42627ee394de0cd2056)):\n\n  GitOrigin-RevId: 76a6f406c39711497a9029cf8937e3b6d1b58e0e\n\n- Pass transient payload to webhooks in API/native OIDC flows\n  ([d023775](https://github.com/ory/kratos/commit/d023775750c0e86f5aef64b91dc976f554f90699)):\n\n  GitOrigin-RevId: f31711edace2a1515579991437250bbd08fa74a1\n\n- Properly accept login challenge in verification after login flows\n  ([f6d59bb](https://github.com/ory/kratos/commit/f6d59bb1a7d5d9850dac9254bf67166c4e44f023)):\n\n  GitOrigin-RevId: d71d9a347e6ec39854e291aa00d2a635f480a55c\n\n- Recovery code expires_in regression\n  ([8f54814](https://github.com/ory/kratos/commit/8f5481428695071bdb4f09c3e22ebe848cb89a0c)):\n\n  GitOrigin-RevId: 00c5dfe3b3c2ded1f6622410bb4b7c7f6cfbf129\n\n- Recovery code expiry error\n  ([3447e0a](https://github.com/ory/kratos/commit/3447e0a6f5e5b8b22ef360e989e4944edc8491a2)):\n\n  GitOrigin-RevId: 9505d3eb2da5ea82b200700483168bb1987966fa\n\n- Redact subject codes\n  ([071ad54](https://github.com/ory/kratos/commit/071ad54b0a1fc601b162a63d8f96a7a95c8ad118)):\n\n  GitOrigin-RevId: 56be74ec84ca7b8d9885c92f89cd1332e7af4238\n\n- Remove flaky test for unused function\n  ([b4d8591](https://github.com/ory/kratos/commit/b4d85919854ee5b888d63519af6d0fe176973559)):\n\n  GitOrigin-RevId: 736a6ab0e4bbdc424705c3d4589caf61b72d00d1\n\n- Remove redundant ORDER BY in QueryForCredentials\n  ([65b27fd](https://github.com/ory/kratos/commit/65b27fd812b2fe6fe3fd1a2a706b8e38e265f523)):\n\n  GitOrigin-RevId: 78e60dcdf2ed5082ffa3b7e5489ab9018fc2f5b2\n\n- Remove WithDumpMigrations option to MigrationBox\n  ([7ee85fb](https://github.com/ory/kratos/commit/7ee85fb985b01efcfd7c9d486e1db85c0104648e)):\n\n  GitOrigin-RevId: ec8bf6fb2d78337c71a158ba2f65dfe2e2d612f6\n\n- Request log config key\n  ([1799e3a](https://github.com/ory/kratos/commit/1799e3a49f56875a3328add32ef90d3bab485caa)):\n\n  GitOrigin-RevId: d81d89baac28e6ac486b3a0b1ca3683f537eb15e\n\n- Resolve incorrect error handling\n  ([9144b55](https://github.com/ory/kratos/commit/9144b554c0feb5fc737f245ac71e99da904f2d98)):\n\n  GitOrigin-RevId: e8bb70305cf8218b60e89db4d7bf128f7b590aa7\n\n- Resolve null response in OAuth2 flow with existing session\n  ([e7d8bd1](https://github.com/ory/kratos/commit/e7d8bd1f7d13fff2e9aa065444556f27f9a9d688)):\n\n  GitOrigin-RevId: 54adcc002fa545e2eb4abf1ef819d9e08089a279\n\n- Return a specific error message for email & phone validation errors\n  ([d6b0f49](https://github.com/ory/kratos/commit/d6b0f492c4aa6608b821252ede2ae0175ff89b66)):\n\n  GitOrigin-RevId: 48ed3f3642f831b1bc58f3238105911ad8a55de0\n\n- Return correct CSRF errors\n  ([b7b7fd4](https://github.com/ory/kratos/commit/b7b7fd4f0bdbca38eb7e3f26c641cb99ad1337de)):\n\n  GitOrigin-RevId: cab70529e9041391cd406fb96b8ce0b53b1a657f\n\n- Return oauth2 login challenge on Bad Request in self-service flows\n  ([bc33d5c](https://github.com/ory/kratos/commit/bc33d5cd99c89a304e6e55b5bc8a3118133e9d9d)):\n\n  GitOrigin-RevId: ab4e0fd801d619550234afba63614c098bc79fea\n\n- Seamlessly migrate existing users to SCIM\n  ([76d35cf](https://github.com/ory/kratos/commit/76d35cfe1c79346628bcbc4adfe540b6099d2a62)):\n\n  GitOrigin-RevId: b2088ed6bf263a408a124d411c118b1f3fa040b9\n\n- Show captcha on otp submission\n  ([039d5bc](https://github.com/ory/kratos/commit/039d5bccd7955ca3d6ae4bfd656c790778c583c4)):\n\n  GitOrigin-RevId: e1cee4e841345aec0aae698e25ddefa36a93210b\n\n- Stray debug print\n  ([3da622f](https://github.com/ory/kratos/commit/3da622f0194469289f7e1067e42c16a8721b795c)):\n\n  GitOrigin-RevId: eeb3d3c38faa92f79d2c0b5eb758bdf912a43bc7\n\n- Transfer OAuth2 login challenge in account linking flow\n  ([1ab143c](https://github.com/ory/kratos/commit/1ab143c5f5427032d16fd5c03dfd5ee3dd86184f)):\n\n  GitOrigin-RevId: b666119e260f3b0b2071161578b5179e49a89616\n\n- Update CONTRIBUTING.md\n  ([95bf33b](https://github.com/ory/kratos/commit/95bf33bb29cc81d880933efa3ab38b8089006a3a)):\n\n  GitOrigin-RevId: c40330cae452ab009c3a86892c0c2772d82ccfb8\n\n- Update dependencies and replace @ory/client for\n  kratos-selfserivce-ui-react-native\n  ([3d88a43](https://github.com/ory/kratos/commit/3d88a439c5084da333e2f514c6c7a114c9650867)):\n\n  GitOrigin-RevId: 4d380b9988c7f01acf6b71d30eeb5021cdaef973\n\n- Update packages to fix GHSA-7h2j-956f-4vf2\n  ([79fb49d](https://github.com/ory/kratos/commit/79fb49d39a400f6e8c67cf2cb7097ce43bb037bf)):\n\n  GitOrigin-RevId: 55aad6f8b36d1d22a6e149b842d035eae6ccae35\n\n- Upgrade vulnerable dependencies across Go and npm\n  ([c2adee4](https://github.com/ory/kratos/commit/c2adee419e9c6c6c35817193a2a9b45dc0022a26)):\n\n  Co-authored-by: Deepak Prabhakara <deepak.prabhakara@ory.sh> GitOrigin-RevId:\n  c3fc33561a0d0eedbc9be03ff551f62452f1c905\n\n- Use correct client authentication method for Apple OIDC\n  ([6c2f8fb](https://github.com/ory/kratos/commit/6c2f8fb90967d69637b5587ff5c31512b8232c92)):\n\n  GitOrigin-RevId: 499278d7a86a26153f98d1c820f54e69879738f4\n\n- X data race and parallize some tests\n  ([116a66e](https://github.com/ory/kratos/commit/116a66e18adfc10fe2cfc60c46c902d5d680543d)):\n\n  GitOrigin-RevId: 737267e41d80a6278bcb8cc657208a3a0cf13dcd\n\n### Code Refactoring\n\n- Squash merge old backoffice migration and fix up command\n  ([7790322](https://github.com/ory/kratos/commit/77903222ee85a015ffa1caa8504655895817bdb2)):\n\n  GitOrigin-RevId: 047d524cb71a5f0e7a082c4f3ebec6426935739d\n\n### Documentation\n\n- Improve readme and dev instructions\n  ([56be7ba](https://github.com/ory/kratos/commit/56be7baaabe11702b9406900412964028b17762c)):\n\n  GitOrigin-RevId: 5269520d6b9313732e2a9f13747e8696bb96a8e4\n\n- Update readmes\n  ([bc8dca6](https://github.com/ory/kratos/commit/bc8dca6807e0deb77aa3a9c589a367bc6772ba9d)):\n\n  GitOrigin-RevId: 936016692123f481c9addb607355ac326edee206\n\n### Features\n\n- Add captcha strategy for recovery flow\n  ([3dee8f5](https://github.com/ory/kratos/commit/3dee8f5f949973373f5e3d4ea9878981c30da07f)):\n\n  GitOrigin-RevId: 8bf87d0de35855b50cd0c995329b4e40520a4c2b\n\n- Add captcha strategy for verification flow\n  ([420f69d](https://github.com/ory/kratos/commit/420f69d5bcecb8358bd24a432d61608cccfd4479)):\n\n  GitOrigin-RevId: c500a8b1fad779bd3a4da04ebc816ee488d21d25\n\n- Add column identity_id to identity_credential_identifiers and session_devices\n  ([57b099f](https://github.com/ory/kratos/commit/57b099f24ce94ed3c443d2ee3acbc5c5670d1aa8)):\n\n  GitOrigin-RevId: a8fc43b6bba9119a2d9472343eede30978ee72d7\n\n- Add native api flow support for passkeys\n  ([39c341b](https://github.com/ory/kratos/commit/39c341b9260694bf5ac19874c66ef58e18094b9e)):\n\n  GitOrigin-RevId: 407616212851156151f786e176be6e6349917c8d\n\n- Add ratelimit buckets to swagger definitions\n  ([a14c3f2](https://github.com/ory/kratos/commit/a14c3f222617eef5573534745e957d5c15e4f589)):\n\n  GitOrigin-RevId: 854dea8de34fc0402fbe1641af7f076f977cbcbc\n\n- Add session to all settings hooks payloads\n  ([aebbc2b](https://github.com/ory/kratos/commit/aebbc2be0096e5727c1da0fa936d1a9a0b3273e1)):\n\n  GitOrigin-RevId: baeb33ceb72409aaa231fee2988734d729115896\n\n- Add support for NULL and more column types to keysetpagination\n  ([3f24dbf](https://github.com/ory/kratos/commit/3f24dbf3b98f51726a94240a5d6de947586c8112)):\n\n  GitOrigin-RevId: 4e314fd71e85ebeb0fc5ef3bd8d5e892c610f01e\n\n- Auto account linking for google and apple\n  ([623742e](https://github.com/ory/kratos/commit/623742e15227c36f85730185fe4230781febdd09)):\n\n  GitOrigin-RevId: c66e7aeda756b80aee97ee82fed988123da72541\n\n- Automatic transaction retries for postgres\n  ([80dcbac](https://github.com/ory/kratos/commit/80dcbac48d1c9a1882319fa009269692aee1709a)):\n\n  GitOrigin-RevId: e7d567988621937fb25e9f41af7892b6f0aad65d\n\n- Better multi-region queries\n  ([af48288](https://github.com/ory/kratos/commit/af48288839e0dee36f7a4cb86434bf3237cb6fd2)):\n\n  GitOrigin-RevId: 007a93a33d5fe26ef336c35f3f6100ee8bcb4f61\n\n- Collect external latency data and write to logs\n  ([97ce640](https://github.com/ory/kratos/commit/97ce6402184842f1cfcd79da274ab367a0e8124a)):\n\n  GitOrigin-RevId: 6ffdbd26c4346ed65646a8f508a4ed44dd4b7637\n\n- Consider Go migrations DirHash when restoring full schema from backups\n  ([99c8cdc](https://github.com/ory/kratos/commit/99c8cdc97bd8d70cef981f2c78fdae186806c389)):\n\n  GitOrigin-RevId: 3b1e15bede6a985c746e16aee512d778c7bf9743\n\n- Forward (some) user request headers to SMS HTTP channel\n  ([f2ce286](https://github.com/ory/kratos/commit/f2ce286eb11eb3fcd4ef63d2ec5b596c3cfc76df)):\n\n  GitOrigin-RevId: 2f63cc936d612b530a3b1058656e54716f71559f\n\n- Generate events for SSO and SCIM provider revisions\n  ([da8ec11](https://github.com/ory/kratos/commit/da8ec11af3506af8220280727c074cedcdf34f43)):\n\n  GitOrigin-RevId: 61d08f591404d69e55057d745e0342c17446a713\n\n- Hydra benchmarking tool\n  ([aa3071f](https://github.com/ory/kratos/commit/aa3071fa7a35ea4d6a6bd4f60c147bf571deedcb)):\n\n  GitOrigin-RevId: a2db2f17a2eaf000ff7ffbfdd71c422721259d39\n\n- Improved tracing\n  ([46c1028](https://github.com/ory/kratos/commit/46c1028e2b57ba17f4de5f79c882f34b19d84822)):\n\n  GitOrigin-RevId: 98ae16a6f1de4414538c7692758af850aa0caaac\n\n- Infer regional-by-row region using foreign key constraints\n  ([46c18eb](https://github.com/ory/kratos/commit/46c18eb372e93b3bcd1d92b5edb0606dbcc10b50)):\n\n  GitOrigin-RevId: 0b33189034afb6a1090f49b06ec28cb68b335a89\n\n- Keto-cli improvements\n  ([86968f5](https://github.com/ory/kratos/commit/86968f5773c0d7f6ecff71db08bd505b85222b7d)):\n\n  GitOrigin-RevId: 7e569c384d891743c8b9a5c2cfca635e550cfc89\n\n- **kratos:** Auto-send code when it is the only available method\n  ([86103bc](https://github.com/ory/kratos/commit/86103bcff46b4e4cb0d18c3934bbcef30fb4af68)):\n\n  GitOrigin-RevId: cedeed8f92b51fdc746807b10937cb633d3c62d0\n\n- Login with uae pass\n  ([1544efe](https://github.com/ory/kratos/commit/1544efe5ea3975f2eb9d61aea4d893eb51b7abaa)):\n\n  GitOrigin-RevId: 9ea7e86014ca7202b0b54bc1e5707253121db6cb\n\n- Make new identity_id column on identifiers and session_devices NOT NULL and\n  establish foreign key\n  ([6bf18bf](https://github.com/ory/kratos/commit/6bf18bf87e02a25bd1f87bb40af71f8439a6c0c5)):\n\n  GitOrigin-RevId: a837b24e7f0642fcd77b2f5c1c0532de07c49078\n\n- Make SCIM work with MySQL\n  ([a34e951](https://github.com/ory/kratos/commit/a34e9515ad9742cb6dd5f981c248bbed8050a404)):\n\n  GitOrigin-RevId: a833fe21172d9c218b55d7229e4ed3a131cfcb6c\n\n- Rename project revision columns\n  ([e25723e](https://github.com/ory/kratos/commit/e25723e39492fc2c9f7e43970dfb4f08bba0a1b3)):\n\n  GitOrigin-RevId: b20a153d86d72c43f83f7107b4c3ed602bcb5009\n\n- Speed up OIDC login+registration handling\n  ([6bfbaf5](https://github.com/ory/kratos/commit/6bfbaf5791eddce0a2777b580a62983762314930)):\n\n  GitOrigin-RevId: bd516d8f488b385e2968d56509123cde78d0f642\n\n- Update GetActiveRecoveryStrategies method\n  ([b94f4c9](https://github.com/ory/kratos/commit/b94f4c9ba5b1178ac8ca16af2ce544c0479956a6)):\n\n  GitOrigin-RevId: ae8121e4488d8fc332bea1bcea704b15c2cd7987\n\n- Use keysetpagination planner for keto read queries\n  ([85590e8](https://github.com/ory/kratos/commit/85590e8a4ca192ebf8aad6a2ce79f5dc50c5f53f)):\n\n  GitOrigin-RevId: e2ed9c3eecc60fd26eaedb3f8cca4f222fd1cb1e\n\n### Tests\n\n- Add assertions for json response body\n  ([0f5085c](https://github.com/ory/kratos/commit/0f5085c67a3f6f8481246c1d86b080d932a4cfa4)):\n\n  GitOrigin-RevId: b9ff0ad8e58bf96953980bb8aeb55aeee8138c04\n\n- Deflake and improve performance\n  ([0451169](https://github.com/ory/kratos/commit/04511690ca6702f05ce7c64227b9db028fa5204f)):\n\n  GitOrigin-RevId: 0f7be9e16ea12f9cb277f8cb3f03058e9db1aaa9\n\n- Deflake directory watcherx\n  ([00c4f9e](https://github.com/ory/kratos/commit/00c4f9e4a67b7c78ff8359d4240a2ddaef05b4bb)):\n\n  GitOrigin-RevId: 6b1c66f96f46833273490b22179aadcb5c7ce01c\n\n- Deflake SAML config assertion\n  ([71da1e3](https://github.com/ory/kratos/commit/71da1e3c590f5001933b28102ffeb454497e1827)):\n\n  GitOrigin-RevId: 3cad41ed2ded379328e6c0f50d1b27a9da9b0f38\n\n- Faster and more reliable courier tests\n  ([2a552ea](https://github.com/ory/kratos/commit/2a552eacefb0dd385f01c678eae8544d276dfa90)):\n\n  GitOrigin-RevId: 30c7ebf095fd5c233e173dc0a645d7bcbe69115f\n\n- Fix data races\n  ([4014eeb](https://github.com/ory/kratos/commit/4014eeb08c65dee3956881127ce1e5c50d018449)):\n\n  GitOrigin-RevId: 7dc520ddc926be30393d474734d37a5f58cc4851\n\n- Fix data races\n  ([8482dd5](https://github.com/ory/kratos/commit/8482dd5de02900fd59746313504ee9b81c9ff874)):\n\n  GitOrigin-RevId: 624aeee77f199dd8c67b67cefe2a61c2f9e7d759\n\n- **hydra:** Add plaintext backups for all DB types\n  ([3369ebd](https://github.com/ory/kratos/commit/3369ebd6f3695c94c41e0bdd49fdff29d79971c7)):\n\n  GitOrigin-RevId: ee2c8ff9ffacff66d09241827780350979957dba\n\n- Minor setup improvements\n  ([b9e094d](https://github.com/ory/kratos/commit/b9e094d674629c8b4f8f4e3cf7679658c5551f55)):\n\n  GitOrigin-RevId: cc7740e9ef1c5469df10001c6dd1bca50368ee01\n\n### Unclassified\n\n- apply review changes\n  ([e7d5dd2](https://github.com/ory/kratos/commit/e7d5dd272b3618767223732e61b0b9a2f3c752f5)):\n\n  GitOrigin-RevId: 9d4a25d813cb86f4a42fe879d7d1c346674e5dc6\n\n- storybook snapshots\n  ([3f06c5d](https://github.com/ory/kratos/commit/3f06c5da026861664677472ffec986f90b5e5590)):\n\n  GitOrigin-RevId: 066f7ecfdb4bc5614a7c238c4e0e21e959b7e6b7\n\n- fixes\n  ([7982b73](https://github.com/ory/kratos/commit/7982b73ae145e5e709ab742c29d784d1a7f85db2)):\n\n  GitOrigin-RevId: 11ef68918584d7711fca8b20f371ea0c8b86f127\n\n# [25.4.0](https://github.com/ory/kratos/compare/v1.3.0...v25.4.0) (2025-11-07)\n\nv25.4.0\n\n## Breaking Changes\n\nThe `require_verified_address` hook no longer returns a plain error. Previously,\nusers had to manually start the verification flow, which caused a poor\nexperience. Now, Ory Kratos automatically creates a verification flow and\nredirects the user using `continue_with` or an HTTP redirect. The verification\nflow starts with the first verified address found for the user. This aligns the\nbehavior of `require_verified_address` with using the `verification` and\n`show_verification_ui` hook combination for login.\n\nGoing forward, the node group of fields that are failing validation during oidc\nsign up are `default` and no longer `oidc`. For now, you can get the legacy\nbehavior back by turning on\n`feature_flags.legacy_oidc_registration_node_group=true`.\n\nCo-authored-by: Jonas Hungershausen <jonas.hungershausen@ory.sh>\n\nBefore this change, `show_verification_ui` would always be included in\n`continue_with` for the registration flow when verification was enabled. After\nthis change, `show_verification_ui` is only included when the\n`show_verification_ui` post-registration hook is defined.\n\nAccount linking incorrectly returned a 200 OK status code even though the login\nflow was not completed successfully. Going forward, the correct 400 OK status\ncode will be sent when using the API flow or `Accept: application/json`.\n\nThis patch changes the behavior of configuration item `foo` to do bar. To keep\nthe existing behavior please do baz.\n\n```\n-->\n\n## Related issue(s)\n\n<!--\nIf this pull request\n\n1. is a fix for a known bug, link the issue where the bug was reported\nin the format of `#1234`;\n2. is a fix for a previously unknown bug, explain the bug and how to\nreproduce it in this pull request;\n3. implements a new feature, link the issue containing the design\ndocument in the format of `#1234`;\n4. improves the documentation, no issue reference is required.\n\nPull requests introducing new features, which do not have a design\ndocument linked are more likely to be rejected and take on average 2-8\nweeks longer to\nget merged.\n\nYou can discuss changes with maintainers either in the Github\nDiscussions in this repository or\njoin the [Ory Chat](https://www.ory.sh/chat).\n-->\n\n## Checklist\n\n<!--\nPut an `x` in the boxes that apply. You can also fill these out after\ncreating the PR.\n\nPlease be aware that pull requests must have all boxes ticked in order\nto be merged.\n\nIf you're unsure about any of them, don't hesitate to ask. We're here to\nhelp!\n-->\n\n- [ ] I have read the [contributing\nguidelines](../blob/master/CONTRIBUTING.md).\n- [ ] I have referenced an issue containing the design document if my\nchange\n      introduces a new feature.\n- [ ] I am following the\n[contributing code\n\nThis patch changes the behavior of configuration item `foo` to do bar. To keep the existing\nbehavior please do baz.\n```\n\n-->\n\n## Related issue(s)\n\n<!--\nIf this pull request\n\n1. is a fix for a known bug, link the issue where the bug was reported\nin the format of `#1234`;\n2. is a fix for a previously unknown bug, explain the bug and how to\nreproduce it in this pull request;\n3. implements a new feature, link the issue containing the design\ndocument in the format of `#1234`;\n4. improves the documentation, no issue reference is required.\n\nPull requests introducing new features, which do not have a design\ndocument linked are more likely to be rejected and take on average 2-8\nweeks longer to\nget merged.\n\nYou can discuss changes with maintainers either in the Github\nDiscussions in this repository or\njoin the [Ory Chat](https://www.ory.sh/chat).\n-->\n\n## Checklist\n\n<!--\nPut an `x` in the boxes that apply. You can also fill these out after\ncreating the PR.\n\nPlease be aware that pull requests must have all boxes ticked in order\nto be merged.\n\nIf you're unsure about any of them, don't hesitate to ask. We're here to\nhelp!\n-->\n\n- [ ] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md).\n- [ ] I have referenced an issue containing the design document if my change\n      introduces a new feature.\n- [ ] I am following the [contributing code\n\nThis patch changes the behavior of configuration item `foo` to do bar. To keep\nthe existing behavior please do baz.\n\n```\n-->\n\n## Related issue(s)\n\n<!--\nIf this pull request\n\n1. is a fix for a known bug, link the issue where the bug was reported\nin the format of `#1234`;\n2. is a fix for a previously unknown bug, explain the bug and how to\nreproduce it in this pull request;\n3. implements a new feature, link the issue containing the design\ndocument in the format of `#1234`;\n4. improves the documentation, no issue reference is required.\n\nPull requests introducing new features, which do not have a design\ndocument linked are more likely to be rejected and take on average 2-8\nweeks longer to\nget merged.\n\nYou can discuss changes with maintainers either in the Github\nDiscussions in this repository or\njoin the [Ory Chat](https://www.ory.sh/chat).\n-->\n\n## Checklist\n\n<!--\nPut an `x` in the boxes that apply. You can also fill these out after\ncreating the PR.\n\nPlease be aware that pull requests must have all boxes ticked in order\nto be merged.\n\nIf you're unsure about any of them, don't hesitate to ask. We're here to\nhelp!\n-->\n\n- [ ] I have read the [contributing\nguidelines](../blob/master/CONTRIBUTING.md).\n- [ ] I have referenced an issue containing the design document if my\nchange\n      introduces a new feature.\n- [ ] I am following the\n[contributing code\n\nThis patch changes the behavior of configuration item `foo` to do bar. To keep the existing\nbehavior please do baz.\n```\n\n-->\n\n## Related issue(s)\n\n<!--\nIf this pull request\n\n1. is a fix for a known bug, link the issue where the bug was reported\nin the format of `#1234`;\n2. is a fix for a previously unknown bug, explain the bug and how to\nreproduce it in this pull request;\n3. implements a new feature, link the issue containing the design\ndocument in the format of `#1234`;\n4. improves the documentation, no issue reference is required.\n\nPull requests introducing new features, which do not have a design\ndocument linked are more likely to be rejected and take on average 2-8\nweeks longer to\nget merged.\n\nYou can discuss changes with maintainers either in the Github\nDiscussions in this repository or\njoin the [Ory Chat](https://www.ory.sh/chat).\n-->\n\n## Checklist\n\n<!--\nPut an `x` in the boxes that apply. You can also fill these out after\ncreating the PR.\n\nPlease be aware that pull requests must have all boxes ticked in order\nto be merged.\n\nIf you're unsure about any of them, don't hesitate to ask. We're here to\nhelp!\n-->\n\n- [ ] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md).\n- [ ] I have referenced an issue containing the design document if my change\n      introduces a new feature.\n- [ ] I am following the [contributing code\n\nThis patch changes the behavior of configuration item `foo` to do bar. To keep\nthe existing behavior please do baz.\n\n```\n-->\n\n## Related issue(s)\n\n<!--\nIf this pull request\n\n1. is a fix for a known bug, link the issue where the bug was reported\nin the format of `#1234`;\n2. is a fix for a previously unknown bug, explain the bug and how to\nreproduce it in this pull request;\n3. implements a new feature, link the issue containing the design\ndocument in the format of `#1234`;\n4. improves the documentation, no issue reference is required.\n\nPull requests introducing new features, which do not have a design\ndocument linked are more likely to be rejected and take on average 2-8\nweeks longer to\nget merged.\n\nYou can discuss changes with maintainers either in the Github\nDiscussions in this repository or\njoin the [Ory Chat](https://www.ory.sh/chat).\n-->\n\n## Checklist\n\n<!--\nPut an `x` in the boxes that apply. You can also fill these out after\ncreating the PR.\n\nPlease be aware that pull requests must have all boxes ticked in order\nto be merged.\n\nIf you're unsure about any of them, don't hesitate to ask. We're here to\nhelp!\n-->\n\n- [ ] I have read the [contributing\nguidelines](../blob/master/CONTRIBUTING.md).\n- [ ] I have referenced an issue containing the design document if my\nchange\n      introduces a new feature.\n- [ ] I am following the\n[contributing code\n\nThis patch changes the behavior of configuration item `foo` to do bar. To keep the existing\nbehavior please do baz.\n```\n\n-->\n\n## Related issue(s)\n\n<!--\nIf this pull request\n\n1. is a fix for a known bug, link the issue where the bug was reported\nin the format of `#1234`;\n2. is a fix for a previously unknown bug, explain the bug and how to\nreproduce it in this pull request;\n3. implements a new feature, link the issue containing the design\ndocument in the format of `#1234`;\n4. improves the documentation, no issue reference is required.\n\nPull requests introducing new features, which do not have a design\ndocument linked are more likely to be rejected and take on average 2-8\nweeks longer to\nget merged.\n\nYou can discuss changes with maintainers either in the Github\nDiscussions in this repository or\njoin the [Ory Chat](https://www.ory.sh/chat).\n-->\n\n## Checklist\n\n<!--\nPut an `x` in the boxes that apply. You can also fill these out after\ncreating the PR.\n\nPlease be aware that pull requests must have all boxes ticked in order\nto be merged.\n\nIf you're unsure about any of them, don't hesitate to ask. We're here to\nhelp!\n-->\n\n- [ ] I have read the [contributing guidelines](../blob/master/CONTRIBUTING.md).\n- [ ] I have referenced an issue containing the design document if my change\n      introduces a new feature.\n- [ ] I am following the [contributing code\n\nThe total count header `x-total-count` will no longer be sent in response to\n`GET /admin/sessions` requests.\n\nCloses https://github.com/ory-corp/cloud/issues/7177 Closes\nhttps://github.com/ory-corp/cloud/issues/7175 Closes\nhttps://github.com/ory-corp/cloud/issues/7176\n\n### Bug Fixes\n\n- Accept login challenge in session_issuer on SPA flows\n  ([#4288](https://github.com/ory/kratos/issues/4288))\n  ([e13687a](https://github.com/ory/kratos/commit/e13687ad51cdb889f0e680a005145a0134086fc7))\n- Accept login_challenge in SPA verification flows\n  ([#4284](https://github.com/ory/kratos/issues/4284))\n  ([7ca3b6b](https://github.com/ory/kratos/commit/7ca3b6be14c53e16c3a8f4e7eb83efe0b0e7c88e))\n- Account linking should only happen after 2fa when required\n  ([#4174](https://github.com/ory/kratos/issues/4174))\n  ([8e29b68](https://github.com/ory/kratos/commit/8e29b68a595d2ef18e48c2a01072335cefa36d86))\n- Account linking with 2FA ([#4188](https://github.com/ory/kratos/issues/4188))\n  ([4a870a6](https://github.com/ory/kratos/commit/4a870a678dd3676abda7afc9803399dec4411b05)):\n\n  This fixes some edge cases with OIDC account linking for accounts with 2FA\n  enabled.\n\n- Add default issuer URL for LINE\n  ([#4415](https://github.com/ory/kratos/issues/4415))\n  ([292f65d](https://github.com/ory/kratos/commit/292f65d6bd1bc70b2f13b92bdbcb8e30256e0a17)):\n\n  Fixed+expanded relevant comment.\n\n  Fixed some tracing issues.\n\n  Added error info and missing res.Body.Close() in courier.\n\n- Add exists clause ([#4191](https://github.com/ory/kratos/issues/4191))\n  ([a313dd6](https://github.com/ory/kratos/commit/a313dd6ba6d823deb40f14c738e3b609dbaad56c))\n- Add missing autocomplete attributes to identifier_first strategy\n  ([#4215](https://github.com/ory/kratos/issues/4215))\n  ([e1f29c2](https://github.com/ory/kratos/commit/e1f29c2d3524f9444ec067c52d2c9f1d44fa6539))\n- Add missing csrf_token ([#4363](https://github.com/ory/kratos/issues/4363))\n  ([f441f41](https://github.com/ory/kratos/commit/f441f41312b81a570e99348f69b88008f4516660))\n- Add missing discriminator ([#4365](https://github.com/ory/kratos/issues/4365))\n  ([c10bb06](https://github.com/ory/kratos/commit/c10bb06bb9125fbc71863c5aa82194da2f2e2888))\n- Add missing saml group ([#4268](https://github.com/ory/kratos/issues/4268))\n  ([44eb305](https://github.com/ory/kratos/commit/44eb305cf91672798f7d57550a026c6b970f7566))\n- Add missing submit group ([#4354](https://github.com/ory/kratos/issues/4354))\n  ([106163d](https://github.com/ory/kratos/commit/106163d15e2eb84c3403d0ce8f829a9d9b3ce94f))\n- Add missing values to the session method enum\n  ([a043b43](https://github.com/ory/kratos/commit/a043b43ceb5e7e1ce4fd1ef25f4ba8db72d7b478)):\n\n  GitOrigin-RevId: 60b31e9f7d7b50dc652efc5f3a385be4adb25ba1\n\n- Add resend node to after registration verification flow\n  ([#4260](https://github.com/ory/kratos/issues/4260))\n  ([9bc83a4](https://github.com/ory/kratos/commit/9bc83a410b8de9d649b6393f136889dd14098b0d))\n- Add transient payload to fedcm\n  ([#4369](https://github.com/ory/kratos/issues/4369))\n  ([245f5dc](https://github.com/ory/kratos/commit/245f5dc1c83d35d7e228a45ee267d6bcdb705e98)),\n  closes\n  [../blob/master/CONTRIBUTING.md#contributing-code](https://github.com/../blob/master/CONTRIBUTING.md/issues/contributing-code):\n\n    <!--\n    Describe the big picture of your changes here to communicate to the\n    maintainers why we should accept this pull request.\n    \n    This text will be included in the changelog. If applicable, include\n    links to documentation or pieces of code.\n    If your change includes breaking changes please add a code block\n    documenting the breaking change:\n    \n    ```\n\n\n- Allow patching some /credentials sub-paths\n  ([#4277](https://github.com/ory/kratos/issues/4277))\n  ([aefa806](https://github.com/ory/kratos/commit/aefa80623ee942254653b03ef4b273ae2779af0e)),\n  closes\n  [../blob/master/CONTRIBUTING.md#contributing-code](https://github.com/../blob/master/CONTRIBUTING.md/issues/contributing-code):\n\n    <!--\n    Describe the big picture of your changes here to communicate to the\n    maintainers why we should accept this pull request.\n    \n    This text will be included in the changelog. If applicable, include\n    links to documentation or pieces of code.\n    If your change includes breaking changes please add a code block\n    documenting the breaking change:\n    \n    ```\n\n\n- Also update identifiers ([#4321](https://github.com/ory/kratos/issues/4321))\n  ([7c63727](https://github.com/ory/kratos/commit/7c6372794a94868555f647f6160be8205072c506)):\n\n  This fixes a bug where when an identity is merged into another, the identifier\n  of the original identity was not updated.\n\n- Another apple fix\n  ([8a220c0](https://github.com/ory/kratos/commit/8a220c04365a30f9a4b939de7db6f21fd9ead230)):\n\n  GitOrigin-RevId: 51654dc0a642032ee70d1c7f4172d61ba76d85a3\n\n- Apply strategy filters in identifier first as well\n  ([#4352](https://github.com/ory/kratos/issues/4352))\n  ([ec3ecc5](https://github.com/ory/kratos/commit/ec3ecc562a4d6ab511e53210d14c143903176b8c))\n- Better tracing in proxy HTTP\n  ([ff5fa9b](https://github.com/ory/kratos/commit/ff5fa9be2b7c864324c187639997476559d017ea)):\n\n  GitOrigin-RevId: e66493762481986aefa8c73c676b1f7515cd29cb\n\n- Cancel conditional passkey before trying again\n  ([#4247](https://github.com/ory/kratos/issues/4247))\n  ([d9f6f75](https://github.com/ory/kratos/commit/d9f6f75b6a43aad996f6390f73616a2cf596c6e4))\n- Check aal on sessions list endpoint\n  ([#4305](https://github.com/ory/kratos/issues/4305))\n  ([44f97b8](https://github.com/ory/kratos/commit/44f97b85e36160b8cce272fd61fbe3ac7d810fbf)),\n  closes [#3671](https://github.com/ory/kratos/issues/3671):\n\n  The session check to list a user's own sessions now requires the same AAL\n  level as the whoami check.\n\n- Clarify import responses\n  ([2a44fd5](https://github.com/ory/kratos/commit/2a44fd55d741de50f93ff186e014904472a41e73)):\n\n  GitOrigin-RevId: 9d924622bb84059ace26a7cafcb67ef619bff0f9\n\n- Context passing and missing body close\n  ([79f4e2a](https://github.com/ory/kratos/commit/79f4e2ab44deecdb8a400d5d43e65eab7e9400ed)):\n\n  GitOrigin-RevId: f044fca6429d86f9dd7f0b0ab8b30a69c10f3487\n\n- Copybara script\n  ([b3af828](https://github.com/ory/kratos/commit/b3af828d3b460a7d22d30b8d95849911b7c7b80e)):\n\n  GitOrigin-RevId: 14665e01451ac5fcdda148b473b8fc35d4fe21ef\n\n- Correctly handle HTTP route patterns in metrics\n  ([dc992d3](https://github.com/ory/kratos/commit/dc992d33d270d6ad284d9e604c00ab4c218d7dde)):\n\n  GitOrigin-RevId: 534f4347eb820a3c51357b9d8defcefecd9845dd\n\n- Count MFA addresses in CountActiveMultiFactorCredentials for code method\n  ([9860c9a](https://github.com/ory/kratos/commit/9860c9a4faa5bd5d725c742c4d4ce9473baa0963)),\n  closes [ory/network#409](https://github.com/ory/network/issues/409)\n- Deduplicate down migrations\n  ([a000460](https://github.com/ory/kratos/commit/a0004605f6c23d7dcd246ed43f0beb89a772732e)):\n\n  GitOrigin-RevId: 94c68daeded4f3b6f42d079d71415d8935a74e69\n\n- **deps:** Update go-x\n  ([ae80380](https://github.com/ory/kratos/commit/ae80380d39a9f534f1e14160390807b5a7eb437a)):\n\n  GitOrigin-RevId: 2d32f7710b9c6111a30f4e0d3cc0abc967d7dfb6\n\n- Detect whether external_id is set in webhook response\n  ([7d0d7f6](https://github.com/ory/kratos/commit/7d0d7f6069d9a415dfb642b129ff6a38630ad781)):\n\n  GitOrigin-RevId: 9caac2424ee78c08b7c81be7f15c2f3405665b90\n\n- Div decoding ([#4362](https://github.com/ory/kratos/issues/4362))\n  ([ef9ee23](https://github.com/ory/kratos/commit/ef9ee235866c6f3958574d2e98f09de3ca1e83af))\n- Do not roll back transaction on partial identity insert error\n  ([#4211](https://github.com/ory/kratos/issues/4211))\n  ([82660f0](https://github.com/ory/kratos/commit/82660f04e2f33d0aa86fccee42c90773a901d400))\n- Don't remove OIDC buttons if invalid identifier is submitted\n  ([97848c7](https://github.com/ory/kratos/commit/97848c7cd7f37117503754c2d08f7ac9db2240e5)):\n\n  GitOrigin-RevId: da3be498c136654e3b6672c288708dd6514ba9b1\n\n- Don't show oidc subject in login hints\n  ([#4264](https://github.com/ory/kratos/issues/4264))\n  ([b95fd3f](https://github.com/ory/kratos/commit/b95fd3fa723521807824cad84e4a9ce812172311))\n- Duplicate autocomplete trigger\n  ([6bbf915](https://github.com/ory/kratos/commit/6bbf91593a37e4973a86f610290ebab44df8dc81))\n- Enable b2b_sso hook in more places\n  ([#4168](https://github.com/ory/kratos/issues/4168))\n  ([0c48ad1](https://github.com/ory/kratos/commit/0c48ad12b978bf58b6bc68b0684a7879f93ebf06)):\n\n  fix: allow b2b_sso hook in more places\n\n- Ensure `make quickstart-dev` works without options\n  ([#4401](https://github.com/ory/kratos/issues/4401))\n  ([327c5a4](https://github.com/ory/kratos/commit/327c5a44f6d646fc6e318a50154c0e3bb4574557)):\n\n  `make quickstart-dev` uses the make variable `QUICKSTART_OPTIONS` which is set\n  to `\"\"` by default. This will result in two double quotes (`\"\"`) in the final\n  shell command e.g. `docker-compose \"\" up` when the variable is not set on the\n  make command line, which fails at the shell level. The fix is to leave the\n  variable empty by default. No semantic changes.\n\n- Ensure authentication method is added to session after linking OIDC provider\n  ([5cae1f7](https://github.com/ory/kratos/commit/5cae1f7af0d190ae65967c92e7b6cdab43ea81ea)):\n\n  GitOrigin-RevId: 175f8815c1e4acae1fd43ec3c87123ad0bc411c8\n\n- Ensure context is not canceled during password hashing\n  ([#4364](https://github.com/ory/kratos/issues/4364))\n  ([e9c6a18](https://github.com/ory/kratos/commit/e9c6a1803daa622e559d0b8904cde4dc8834f1e2)):\n\n  Especially during large imports of plaintext passwords there can be a lot of\n  useless hashing, even after the request timed out or got canceled.\n\n- Ensure that auto_link_credentials markers are being properly overwritten\n  ([#4320](https://github.com/ory/kratos/issues/4320))\n  ([a4fd8ac](https://github.com/ory/kratos/commit/a4fd8acbbbd0cd0ff054e0f8737b076745aa71c8)),\n  closes\n  [../blob/master/CONTRIBUTING.md#contributing-code](https://github.com/../blob/master/CONTRIBUTING.md/issues/contributing-code):\n\n    <!--\n    Describe the big picture of your changes here to communicate to the\n    maintainers why we should accept this pull request.\n    \n    This text will be included in the changelog. If applicable, include\n    links to documentation or pieces of code.\n    If your change includes breaking changes please add a code block\n    documenting the breaking change:\n    \n    ```\n\n\n- Escape IPv6 regex string\n  ([b629ca7](https://github.com/ory/kratos/commit/b629ca79df83f67fa47194e5850bda3bdafa69f4)):\n\n  GitOrigin-RevId: cf04d7cae93aea32950a149527e2b1319af97b39\n\n- Exclude nothing in copybara\n  ([98b7acd](https://github.com/ory/kratos/commit/98b7acd7f3888acba45439a78f5428575c08900d)):\n\n  GitOrigin-RevId: 2dc0975ac38fbcaa191c1d3c108cc15ed71bad4c\n\n- Exclude orgs ([#4351](https://github.com/ory/kratos/issues/4351))\n  ([68500d1](https://github.com/ory/kratos/commit/68500d14509a2697d2832eafafa5608fd8cfbf47))\n- Explicity set updated_at field when updating identity\n  ([#4131](https://github.com/ory/kratos/issues/4131))\n  ([66afac1](https://github.com/ory/kratos/commit/66afac173dc08b1d6666b107cf7050a2b0b27774))\n- Failing CI in OSS repos\n  ([26518b6](https://github.com/ory/kratos/commit/26518b69894c0dd1fe077b78936f3d54733616b1)):\n\n  GitOrigin-RevId: 3d1f84b0f0d006971aea9489322b3e0f32a6a7e3\n\n- Fix back button for recovery flow not showing in AX v1/v2\n  ([f1cfc36](https://github.com/ory/kratos/commit/f1cfc3684278416b869248c0eb4b56a7b82204a9)):\n\n  GitOrigin-RevId: 579ad35ee577cf141f0ac121e79aa42bd20eb693\n\n- Fix nil dereference & lint warnings\n  ([98f9897](https://github.com/ory/kratos/commit/98f9897aeb636f7573334b45d99cab685defa856)):\n\n  GitOrigin-RevId: ae75b30eb109eacc3ce0369ab133d45f860a276c\n\n- Fixed typo in description of api\n  ([f9ffaae](https://github.com/ory/kratos/commit/f9ffaaeac6d24c2ca177359d0c7ce9fc5bbbf282)):\n\n  GitOrigin-RevId: 020354a01d85ec411d879d7ebf260b7fce71c539\n\n- Force profile to be first hydrator in profile_first strategy\n  ([#4380](https://github.com/ory/kratos/issues/4380))\n  ([f475aea](https://github.com/ory/kratos/commit/f475aea476fee5c7cbde74b695a3f080a585b868))\n- Force SQL operator precedence in pagination v2 to ensure nid isolation\n  ([93d364c](https://github.com/ory/kratos/commit/93d364c8e0b892ff2d61dd0c0a6cec7d2e2fc310)):\n\n  GitOrigin-RevId: 451cbe6c4322222e36c182b4f7c1ff6cb9396dde\n\n- Gracefully handle unused index\n  ([#4196](https://github.com/ory/kratos/issues/4196))\n  ([3dbeb64](https://github.com/ory/kratos/commit/3dbeb64b3f99a3aeba5f7126c301b72fda4c3e3c))\n- **hydra:** Instrument metrics also on public endpoints\n  ([a6ac143](https://github.com/ory/kratos/commit/a6ac1432b136d6a715c9c93af5a169a212c87251)):\n\n  GitOrigin-RevId: 84ae1df26bd3d9a025655e50792ea7312f250cca\n\n- **hydra:** Use prometheus metrics instead of SQA metrics\n  ([da4ea07](https://github.com/ory/kratos/commit/da4ea079f77bb2d1d4458c643437d0ec79e46a5a)):\n\n  GitOrigin-RevId: 2ca878d66e4ab101af51bc32f8606ce6c3af0587\n\n- Identity queries\n  ([cf53971](https://github.com/ory/kratos/commit/cf53971654e37cfe86572b78f919b4e26cf75f42)):\n\n  GitOrigin-RevId: b103d25a807c643b521a12d593486d5125f390be\n\n- IdentityCreated is over-reporting on error inserts\n  ([#4323](https://github.com/ory/kratos/issues/4323))\n  ([c3f4ecf](https://github.com/ory/kratos/commit/c3f4ecf2562ffe400e500da97a93327b6115ddb6)):\n\n  `defer` was the incorrect code path here, as we should only record identity\n  created if the transaction did not error (aka was rolled back).\n\n- Ignore CSRF on all apple provider callback URLs\n  ([#4291](https://github.com/ory/kratos/issues/4291))\n  ([b60edba](https://github.com/ory/kratos/commit/b60edba1f4642f07b411271b6c7a442665dc2a74))\n- Ignore non SQL files when applying migrations\n  ([41b342c](https://github.com/ory/kratos/commit/41b342c9b1c5cea65844ec462a0af3e486de6add)):\n\n  GitOrigin-RevId: d71381b874c6e0dea3cba143a6a643334059ce1e\n\n- Implicit transactions for cockroach v23.5 and simplified migration logic\n  ([e8170fc](https://github.com/ory/kratos/commit/e8170fcacaa9d8c3decdd768a40e1f9f4388a142)):\n\n  GitOrigin-RevId: 003ed88700d3eeb853132633d447dd223489e3be\n\n- Improve linking on OIDC signup\n  ([#4314](https://github.com/ory/kratos/issues/4314))\n  ([687d578](https://github.com/ory/kratos/commit/687d5787b12450895ba613ceee47da408917a0a7)),\n  closes\n  [../blob/master/CONTRIBUTING.md#contributing-code](https://github.com/../blob/master/CONTRIBUTING.md/issues/contributing-code):\n\n    <!--\n    Describe the big picture of your changes here to communicate to the\n    maintainers why we should accept this pull request.\n    \n    This text will be included in the changelog. If applicable, include\n    links to documentation or pieces of code.\n    If your change includes breaking changes please add a code block\n    documenting the breaking change:\n    \n    ```\n\n\n- Include go.mod in vendored oryx\n  ([7c0d9c6](https://github.com/ory/kratos/commit/7c0d9c6ddc4691b24de541f09fd503765296c846)):\n\n  GitOrigin-RevId: 20365bbe6b2cf95ac7973bcca9056455d2cb3803\n\n- Incorrect if switch in previous sceen case in two step registration\n  ([f8ee403](https://github.com/ory/kratos/commit/f8ee40396a36a2e7a348c9cf983dec7db13814c5)),\n  closes [#374](https://github.com/ory/kratos/issues/374)\n- Incorrect query plan ([#4218](https://github.com/ory/kratos/issues/4218))\n  ([7d0e78a](https://github.com/ory/kratos/commit/7d0e78a4f6631b0662beee3b8e9dd0d774b875ea))\n- Incorrect response code on account linking\n  ([#4336](https://github.com/ory/kratos/issues/4336))\n  ([ed4fba3](https://github.com/ory/kratos/commit/ed4fba3efd1e1c88a4920216a515e9820b74eb93))\n- Jsonx.ApplyJSONPatch\n  ([f3a3292](https://github.com/ory/kratos/commit/f3a3292b8e1451d396b8b92ce598995266a1aa99)):\n\n  GitOrigin-RevId: 43c10801f5051e3d5fbea5f4f5e90394f6da0fbb\n\n- **kratos:** Do not explicitly pass identity schema on step-up login\n  ([639f765](https://github.com/ory/kratos/commit/639f7654a2a443533b0881e7faede4f768502e4a)):\n\n  GitOrigin-RevId: d7c3d7d3d391a4f77ceac458b8047bccba6e3b98\n\n- Login otp sent message\n  ([335acd4](https://github.com/ory/kratos/commit/335acd469e9276133be12cca07239a64596ddf8f)):\n\n  GitOrigin-RevId: 7831a0f87850ac84e9aa8d514711f0e032ad9066\n\n- Make external_id settable through webhook\n  ([cfd213a](https://github.com/ory/kratos/commit/cfd213a6301c904453f1b56a3d20c59c2cebdd67)):\n\n  GitOrigin-RevId: 96d986d3da361831594e7a9dcd594e63449fa339\n\n- Make external_id settable through webhook\n  ([bf2b34d](https://github.com/ory/kratos/commit/bf2b34d23ef820fa12f49f2dd37d7965fef48ed6)):\n\n  GitOrigin-RevId: 790eeeca1b8aed5713d12f2b14410942a64ba634\n\n- Make node_type stricter per uiNodeAttributes type\n  ([d7d3ba4](https://github.com/ory/kratos/commit/d7d3ba4fa7f451aaef40924688fe5fda38310f5d)):\n\n  GitOrigin-RevId: 26e444de4a2457b2f3d32394629851b8cd6cbd08\n\n- Make RecoveryAddress.ID optional to prevent errors\n  ([6f5e79a](https://github.com/ory/kratos/commit/6f5e79a57181dfb6478b1406f784096e76dfaf2e)):\n\n  GitOrigin-RevId: 6a4579f2961a1ed62eb2ff806d5be74fe63d9146\n\n- Migration problems\n  ([57d86d2](https://github.com/ory/kratos/commit/57d86d2376ab102f97559c1d007a79c8a17723ee)):\n\n  GitOrigin-RevId: 38f30ae2fbc66a36cdeef5bc8b66e4b10ba23fa5\n\n- Order-by clause and span names\n  ([#4200](https://github.com/ory/kratos/issues/4200))\n  ([b6278af](https://github.com/ory/kratos/commit/b6278af5c7ed7fb845a71ad0e64f8b87402a8f4b))\n- Otlp sampling rate default\n  ([4ac6122](https://github.com/ory/kratos/commit/4ac612223d06e84560775e1b1cffd8cf299c9cd0)):\n\n  GitOrigin-RevId: 8a01bded7d8eca0ac3a81de793286144aab16426\n\n- Pass on correct context during verification\n  ([#4151](https://github.com/ory/kratos/issues/4151))\n  ([7e0b500](https://github.com/ory/kratos/commit/7e0b500aada9c1931c759a43db7360e85afb57e3))\n- Preview_credentials_identifier_similar\n  ([#4246](https://github.com/ory/kratos/issues/4246))\n  ([5ee54ed](https://github.com/ory/kratos/commit/5ee54eda909638fa10c543f156042a217b34cba6))\n- Print correct content of down migrations\n  ([3c84b7a](https://github.com/ory/kratos/commit/3c84b7aac70d9bc889e16855dcc28b59b6d8480b)):\n\n  GitOrigin-RevId: 48b1efa8d3d4f6c6648d3941f67f622fdfb0075c\n\n- Quick typo fix for kratos-oss test script run\n  ([5bd3b52](https://github.com/ory/kratos/commit/5bd3b52e2848133a380673853722e4eb55745ac0)):\n\n  GitOrigin-RevId: a376942e6560c8455b9b005a1c50c89218545120\n\n- Registration post persist hooks should not be cancelable\n  ([#4148](https://github.com/ory/kratos/issues/4148))\n  ([18056a0](https://github.com/ory/kratos/commit/18056a0f1cfdf42769e5a974b2526ccf5c608cc2))\n- Reject invalid migration names\n  ([7dc28eb](https://github.com/ory/kratos/commit/7dc28eb76528f34239852cdce7b8dfd1e6e482c0)):\n\n  GitOrigin-RevId: 43aeadc2c058c3a35092527f54999083674a2ee8\n\n- Remove duplicate address verification\n  ([2cc2b69](https://github.com/ory/kratos/commit/2cc2b69e59d42bdb0136bed109c99f167685062c)):\n\n  GitOrigin-RevId: 7019e183317f941a734886bdd19ada10a6efdbec\n\n- Remove selfservice.methods.link.config.base_url\n  ([db8a94e](https://github.com/ory/kratos/commit/db8a94ef538cfe0f1fc08bf5366643222a7bedfc)):\n\n  GitOrigin-RevId: 8c3c3cc3a502f7e7d016f2a9fc4409d5efa54797\n\n- Rename b2b_sso hook ([#4349](https://github.com/ory/kratos/issues/4349))\n  ([d9e3295](https://github.com/ory/kratos/commit/d9e3295d98b0446a90a960d0f0e957e7a6513dfc))\n- Return `return_to` code if already authenticated\n  ([#4286](https://github.com/ory/kratos/issues/4286))\n  ([119841a](https://github.com/ory/kratos/commit/119841a304917e222d8c0fd4606419a520f481c1)):\n\n  This fixes a bug in native OIDC login and registration flows, where the user\n  already has a session in the browser the flow is continued with (usually a web\n  view, but depending on the platform it already has a session cookie set). In\n  the callback, we now correctly handle the case in `alreadyAuthenticated` to\n  return the session token exchange code.\n\n- Return 404 on schema file not exists\n  ([dd589fa](https://github.com/ory/kratos/commit/dd589fa4432b4b570885e35c9e96bfd06a494c7e)):\n\n  GitOrigin-RevId: 398176da632eecdbd95062c7872a90b6c8662006\n\n- Revert \"fix: otlp sampling rate default\n  ([#9055](https://github.com/ory/kratos/issues/9055))\"\n  ([8379db8](https://github.com/ory/kratos/commit/8379db8fc8085b0d2ecc6bd4d803ada9fcf49e80)):\n\n  GitOrigin-RevId: 9de37a48b68c7ee29caefb01c83f1a78999dc15b\n\n- Routes in AX with identity_schema\n  ([105018d](https://github.com/ory/kratos/commit/105018d2fa51ac99748b11924735bac0ed55c391)):\n\n  GitOrigin-RevId: ab72dc64c194c06bf0301e87aa829c72022ac41e\n\n- Schema key ([#4332](https://github.com/ory/kratos/issues/4332))\n  ([306316f](https://github.com/ory/kratos/commit/306316fedf20467059776c003f8285880d272c95))\n- **sdk:** Add missing captcha group\n  ([#4254](https://github.com/ory/kratos/issues/4254))\n  ([241111b](https://github.com/ory/kratos/commit/241111b21f5d96b26ff8bc8106dc8a527c68063b))\n- **sdk:** Add missing enum type to autocomplete\n  ([#4396](https://github.com/ory/kratos/issues/4396))\n  ([4127cbb](https://github.com/ory/kratos/commit/4127cbb35ca3b7b1ea9d0f8c61d2aa56af57ce9a)):\n  -\n\n- **sdk:** Remove incorrect attributes\n  ([#4163](https://github.com/ory/kratos/issues/4163))\n  ([88c68aa](https://github.com/ory/kratos/commit/88c68aa07281a638c9897e76d300d1095b17601d))\n- Send correct verification status in post-recovery hook\n  ([#4224](https://github.com/ory/kratos/issues/4224))\n  ([7f50400](https://github.com/ory/kratos/commit/7f5040080578e194dde3605dbb1a344fe9ff27ae)):\n\n  The verification status is now correctly being transported when executing a\n  recovery hook.\n\n- Set correct request url in acc linking and oidc flows\n  ([#4282](https://github.com/ory/kratos/issues/4282))\n  ([07cb83c](https://github.com/ory/kratos/commit/07cb83c672326848162998a9cfbc8ca34af42bf0))\n- Set default for CYPRESS_OPTS\n  ([6fb39e2](https://github.com/ory/kratos/commit/6fb39e2e0f62aeec249378e7bf4cf3c8981c02df)):\n\n  GitOrigin-RevId: 8838e7aada8d7caa74d08f5892b728c99490e66e\n\n- Settings linking error override\n  ([#4368](https://github.com/ory/kratos/issues/4368))\n  ([6e30865](https://github.com/ory/kratos/commit/6e30865e1314bc4c4fdc3b472b34c92019eadfa4))\n- Show code email in most error states\n  ([#4338](https://github.com/ory/kratos/issues/4338))\n  ([905d1e5](https://github.com/ory/kratos/commit/905d1e5dc8fcdc7f96afa14a5ee036060ea43056))\n- Show_verification_ui in continue_with only if configured\n  ([#4402](https://github.com/ory/kratos/issues/4402))\n  ([5b00fe1](https://github.com/ory/kratos/commit/5b00fe15d94c5169fd62809ef44ffd8102078297)):\n\n  This patch modifies the self-service registration flow so that the\n  show_verification_ui continue_with element is only returned when the relevant\n  post‑registration hook is defined (or when legacy behavior is enabled via\n  configuration). It also adds a new attribute key and internal context handling\n  for the registration flow and updates related tests and API signatures.\n\n- Span names ([#4232](https://github.com/ory/kratos/issues/4232))\n  ([dbae98a](https://github.com/ory/kratos/commit/dbae98a26b8e2a3328d8510745ddb58c18b7ad3d))\n- Stricter JSON patch checking for PATCH identities\n  ([#4263](https://github.com/ory/kratos/issues/4263))\n  ([906f6c8](https://github.com/ory/kratos/commit/906f6c8fdf9ec0834993a44f8a19697b38dd63d2))\n- Support `show_verification_hook` in settings hooks\n  ([#4410](https://github.com/ory/kratos/issues/4410))\n  ([c433c44](https://github.com/ory/kratos/commit/c433c44aa121a6011309eaec115370f266a4a2a6))\n- Tests for Kratos OSS Cypress\n  ([0332143](https://github.com/ory/kratos/commit/03321433376e4e3f240a93f9792453b7cbb894e2)):\n\n  GitOrigin-RevId: 74b3345bfd7d0456b98c2e0474612bcdb458cb17\n\n- Throw upstream error on OIDC issues\n  ([332873d](https://github.com/ory/kratos/commit/332873ddbd34a375dcff736b9823149f5e0d5ed0)):\n\n  GitOrigin-RevId: 2a32dbf9f848ddd90c01cb7d1064863893c954f4\n\n- Truncate updated at ([#4149](https://github.com/ory/kratos/issues/4149))\n  ([2f8aaee](https://github.com/ory/kratos/commit/2f8aaee0716835caaba0dff9b6cc457c2cdff5d4))\n- Upgrade to go 1.24.4 to fix CVE-2025-4673\n  ([13ebb69](https://github.com/ory/kratos/commit/13ebb69d41a77c964da3a96c2d56f48092fd5edc)):\n\n  GitOrigin-RevId: 64950988a466bbdb4f25b8d9f5c416ff591c00bf\n\n- Use appleid audience for secret exchange\n  ([38f8b36](https://github.com/ory/kratos/commit/38f8b36ff474604f5011217b44fbe3ee5fccf1ba)):\n\n  GitOrigin-RevId: 9dbfa60ab71b3773363d7bad08fdd70de86a9e03\n\n- Use batch insert to speed up project changes\n  ([928c9f8](https://github.com/ory/kratos/commit/928c9f885ef0fc055420e711ef69509519802276)):\n\n  GitOrigin-RevId: 88cb2bc82c71b9576ba5d21010c4c585a7ad3af9\n\n- Use context for readiness probes\n  ([#4219](https://github.com/ory/kratos/issues/4219))\n  ([e6d2d4d](https://github.com/ory/kratos/commit/e6d2d4d0c04e60ab5b0658b9e5c4c52104446368))\n- Use default group for signup nodes in oidc\n  ([#4414](https://github.com/ory/kratos/issues/4414))\n  ([dc8b32e](https://github.com/ory/kratos/commit/dc8b32e0049e842d7aca27a38bf73c8cefb9cae3))\n- Use git hash to render ory x schema references\n  ([6776835](https://github.com/ory/kratos/commit/6776835a7f3c6dcc9a0149a3bd8a86f664cb85d4)):\n\n  GitOrigin-RevId: e07209f10a002acbb0df2f66f6f8746279032733\n\n- Use hard-coded fallback key instead of panic\n  ([29eeb56](https://github.com/ory/kratos/commit/29eeb5605f03074de5bba1d7e18e987d30830fb2)):\n\n  GitOrigin-RevId: d7a2270bbf5360288199e9632b2eac6cbc29737c\n\n- Use non-alerting errors for errors not needing alerts\n  ([49e472c](https://github.com/ory/kratos/commit/49e472c12699d6ae2deb8643c62d005a0cc793e0)):\n\n  GitOrigin-RevId: e0f23053f6410f2d6f1610c165cfee4e387c9a3e\n\n- Use updated appleid issuer\n  ([76afd6d](https://github.com/ory/kratos/commit/76afd6dc6fcd33969071fa96d04e523d3ed8c6af)):\n\n  GitOrigin-RevId: 290abca8469dc46c1ba07708849fed28fdbc1b69\n\n### Chores\n\n- Document test migration ([#4265](https://github.com/ory/kratos/issues/4265))\n  ([9959545](https://github.com/ory/kratos/commit/9959545cb9d90364e1928fcc4f01b3171e052360)),\n  closes\n  [../blob/master/CONTRIBUTING.md#contributing-code](https://github.com/../blob/master/CONTRIBUTING.md/issues/contributing-code):\n\n    <!--\n    Describe the big picture of your changes here to communicate to the\n    maintainers why we should accept this pull request.\n    \n    This text will be included in the changelog. If applicable, include\n    links to documentation or pieces of code.\n    If your change includes breaking changes please add a code block\n    documenting the breaking change:\n    \n    ```\n\n\n- Upgrade to go 1.24 ([#4313](https://github.com/ory/kratos/issues/4313))\n  ([bdb046d](https://github.com/ory/kratos/commit/bdb046da36c290f775ad4cabdbe6191295252cc7)),\n  closes\n  [../blob/master/CONTRIBUTING.md#contributing-code](https://github.com/../blob/master/CONTRIBUTING.md/issues/contributing-code):\n\n    <!--\n    Describe the big picture of your changes here to communicate to the\n    maintainers why we should accept this pull request.\n    \n    This text will be included in the changelog. If applicable, include\n    links to documentation or pieces of code.\n    If your change includes breaking changes please add a code block\n    documenting the breaking change:\n    \n    ```\n\n### Code Generation\n\n- Prepare for OSS release - v25.4.0\n  ([64e04ac](https://github.com/ory/kratos/commit/64e04ace9aaf0d577b66ab9b5ae5189a4f66cc9e)):\n\n  GitOrigin-RevId: 8a80ff652290d5faf26d05286b077325672b6fc5\n\n### Code Refactoring\n\n- Hash comparator instantiation\n  ([#4195](https://github.com/ory/kratos/issues/4195))\n  ([53a5a8b](https://github.com/ory/kratos/commit/53a5a8b93cec274456df3d988eb3bd12bc11fa87))\n- Move database meta functions to root x folder for reusability\n  ([85bf18d](https://github.com/ory/kratos/commit/85bf18df5d3ca4710f9376f78591a2069dbf1239)):\n\n  GitOrigin-RevId: 30ee938ea5f1d19bac8967e0ebfe2d595ec27d2b\n\n- Remove total count from listSessions and improve secondary indices\n  ([#4173](https://github.com/ory/kratos/issues/4173))\n  ([e24f993](https://github.com/ory/kratos/commit/e24f993ea4236bac4e23bd4250c11b5932040fd9)):\n\n  This patch changes sorting to improve performance on list session endpoints.\n  It also removes the `x-total-count` header from list responses.\n\n- Two-step registration ([#4348](https://github.com/ory/kratos/issues/4348))\n  ([f46aed1](https://github.com/ory/kratos/commit/f46aed12a244094e9e3e4014792543d6fb1a2a4b)):\n\n  Refactors internals of the two-step registration to better fit into the\n  architecture.\n\n### Documentation\n\n- Add return_to query parameter to OAS Verification Flow for Native Apps\n  ([#4086](https://github.com/ory/kratos/issues/4086))\n  ([b22135f](https://github.com/ory/kratos/commit/b22135fa05d7fb47dfeaccd7cdc183d16921a7ac))\n- Clarify facebook graph API versioning\n  ([#4208](https://github.com/ory/kratos/issues/4208))\n  ([a90df58](https://github.com/ory/kratos/commit/a90df5852ba96704863cc576edcb8286eaa9b3f9))\n- Defining oid as oidc subject_source\n  ([#4270](https://github.com/ory/kratos/issues/4270))\n  ([b388a4a](https://github.com/ory/kratos/commit/b388a4ab9fe101def70ca604fbfbef2bcccd01a9))\n- Improve SecurityError error message for ory elements local\n  ([#4205](https://github.com/ory/kratos/issues/4205))\n  ([0062d45](https://github.com/ory/kratos/commit/0062d45b6c9a6323f9dccb10f63dce752836c29e))\n- **kratos:** Better identity handler description\n  ([405f2f0](https://github.com/ory/kratos/commit/405f2f0de6f9b175fd60595a97ef1ee584df445c)):\n\n  GitOrigin-RevId: 468e3b93206b6c0930121b15575b2e7527069016\n\n- Remove unused SMS config from schema\n  ([#4212](https://github.com/ory/kratos/issues/4212))\n  ([f076fe4](https://github.com/ory/kratos/commit/f076fe4e1487f67f355eaa7f238090abf3796578))\n- Usage of `organization` parameter in native self-service flows\n  ([#4176](https://github.com/ory/kratos/issues/4176))\n  ([cb71e38](https://github.com/ory/kratos/commit/cb71e38147d21f73e9bd1e081dc3443abb63353e))\n\n### Features\n\n- Add a policy callback to customize OIDC credential linking\n  ([#4302](https://github.com/ory/kratos/issues/4302))\n  ([e2f878a](https://github.com/ory/kratos/commit/e2f878a3ed25b4617bf6bd43b6e147d87d3b8ca2))\n- Add a project revision field to set the maximum number of code submits\n  ([0e68c7e](https://github.com/ory/kratos/commit/0e68c7ef0b31d1140483748059cbae707d508767)):\n\n  GitOrigin-RevId: 57da91bc9fe80d24f33028a40d4700520ea4b817\n\n- Add ability to send recovery code via sms\n  ([eb9d934](https://github.com/ory/kratos/commit/eb9d9342effb5ed2cbb670c829eedf8f414e06c9)):\n\n  GitOrigin-RevId: 0accc6d75ac42c379fae79015214d9db3312c68d\n\n- Add allowed domains configuration for captcha\n  ([edb9e0c](https://github.com/ory/kratos/commit/edb9e0c2fd37f7d571f19088aab324f6cfb01c08)):\n\n  GitOrigin-RevId: 03395362054593f07ff6405c2a747256b5ff528e\n\n- Add attributes to webhook events for better debugging\n  ([#4206](https://github.com/ory/kratos/issues/4206))\n  ([00da05d](https://github.com/ory/kratos/commit/00da05da9f77bbfb68b364b3ba2a5d0a2d9e4f15))\n- Add captcha group to first-step registration\n  ([eca4ae9](https://github.com/ory/kratos/commit/eca4ae9dcce37d03bbd1bf5f0cd492466c02acde))\n- Add context param to policy\n  ([#4315](https://github.com/ory/kratos/issues/4315))\n  ([261596b](https://github.com/ory/kratos/commit/261596b7261c315b7d8291e886023c34fc9135c5))\n- Add email domain matcher ([#4373](https://github.com/ory/kratos/issues/4373))\n  ([1c33c39](https://github.com/ory/kratos/commit/1c33c39875c5c766f3fc18578e036156d6214ade))\n- Add explicit config flag for secure cookies\n  ([#4180](https://github.com/ory/kratos/issues/4180))\n  ([2aabe12](https://github.com/ory/kratos/commit/2aabe12e5329acc807c495445999e5591bdf982b)):\n\n  Adds a new config flag for session and all other cookies. Falls back to the\n  previous behavior of using the dev mode to decide if the cookie should be\n  secure or not.\n\n- Add external ID to identities\n  ([335a1e8](https://github.com/ory/kratos/commit/335a1e844a7e239d32f6f8738f8b34816f892956)):\n\n  GitOrigin-RevId: 7e85994a82bda055ccbdf680768e56bb97a0ac51\n\n- Add failure reason to events\n  ([#4203](https://github.com/ory/kratos/issues/4203))\n  ([afa7618](https://github.com/ory/kratos/commit/afa76180e77df0ee0f96eef3b3f2b2d3fe08a33d))\n- Add HTML email support to HTTP channel\n  ([#4387](https://github.com/ory/kratos/issues/4387))\n  ([fb8856e](https://github.com/ory/kratos/commit/fb8856eb11a3762ffb69dc2639b36d91121b4476)),\n  closes [#4350](https://github.com/ory/kratos/issues/4350)\n- Add Login with Amazon\n  ([6cb3e99](https://github.com/ory/kratos/commit/6cb3e9941b5e7e2c5127779adf2d63be7ae58609)):\n\n  GitOrigin-RevId: d50a99f9152f7e42ccb780037e711f500bfdbeba\n\n- Add LoginStarted and RegistrationStarted events\n  ([#4404](https://github.com/ory/kratos/issues/4404))\n  ([7032fec](https://github.com/ory/kratos/commit/7032fec71a01bfd0838310dd2b2d8ea4b9fcee63)):\n\n    <!--\n    \n    This text will be used for the merge commit.\n    \n    Please read\n    https://www.notion.so/Merging-PRs-998760750c5740debdb6d7ea1661ac01\n    \n    -->\n\n  **Changes:**\n  - Add `LoginStarted` and `RegistrationStarted` events along their required\n    attributes\n  - Sort all event attributes alphabetically\n  - Emit these events when a new login/registration flow is created, _after_\n    basic validation passed\n  - It is unclear yet how many of these events will be emitted, as such it is\n    suggested that in a first phase, they remain internal and are not yet sent\n    externally to avoid surprises (note: sometimes, these events can be emitted\n    without user action such as simply visiting/being redirected to the sign-in\n    page, etc)\n\n  **Documentation PR:** [ory/docs#2144](https://github.com/ory/docs/pull/2144)\n\n  **Issue:** https://github.com/ory-corp/cloud/issues/7895\n\n    <!-- Dependency update:\n    https://github.com/ory/{repo}/compare/{old-commit-hash}...{new-commit-hash}\n    -->\n\n  Examples in Grafana:\n  - LoginStarted: <img width=\"955\" alt=\"Screenshot 2025-05-06 at 14 54 32\"\n    src=\"https://github.com/user-attachments/assets/f066f0a7-4b03-4f71-b6e9-385c3f772425\"\n    />\n  - RegistrationStarted: <img width=\"953\" alt=\"Screenshot 2025-05-06 at 14\n    46 17\"\n    src=\"https://github.com/user-attachments/assets/e4e07f59-ecbb-4bfb-a01e-72412adca2f5\"\n    />\n\n- Add migrate sql up|down|status\n  ([#4228](https://github.com/ory/kratos/issues/4228))\n  ([e6fa520](https://github.com/ory/kratos/commit/e6fa520058ca778e01d4e93a8ab4b31a74dd2e11)):\n\n  This patch adds the ability to execute down migrations using:\n\n  ```\n  kratos migrate sql down -e --steps {num_of_steps}\n  ```\n\n  Please read `kratos migrate sql down --help` carefully.\n\n  Going forward, please use the following commands\n\n  ```\n  kratos migrate sql up ...\n  kratos migrate sql status ...\n  ```\n\n  instead of the previous, now deprecated\n\n  ```\n  kratos migrate sql ...\n  kratos migrate status ...\n  ```\n\n  commands.\n\n  See https://github.com/ory-corp/cloud/issues/7350\n\n- Add new Division ui node attributes\n  ([235af52](https://github.com/ory/kratos/commit/235af527dea47b87ad0f18ff04f9b807e4639ae3)):\n\n  Division nodes may be used to hook dynamic scripts and are not actively used\n  in the Ory Kratos open source.\n\n- Add new endpoint to tokenize JWT with a webhook\n  ([f7fa792](https://github.com/ory/kratos/commit/f7fa792a52af5bddbd2869fc4bc0383c73641dfb)):\n\n  GitOrigin-RevId: ff93a3daadc993348ff40ee21c28ec0a30c6cfbe\n\n- Add oid as subject source for microsoft\n  ([#4171](https://github.com/ory/kratos/issues/4171))\n  ([77beb4d](https://github.com/ory/kratos/commit/77beb4de5209cee0bea4b63dfec21d656cf64473)),\n  closes [#4170](https://github.com/ory/kratos/issues/4170):\n\n  In the case of Microsoft, using `sub` as an identifier can lead to problems.\n  Because the use of OIDC at Microsoft is based on an app registration, the\n  content of `sub` changes with every new app registration. `Sub` is therefore\n  not uniquely related to the user. It is therefore not possible to transfer\n  users from one app registration to another without further problems.\n  https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#payload-claims\n\n  With the use of `oid` it is possible to identify a user by a unique id.\n\n- Add session in settings after hook\n  ([1b57fdf](https://github.com/ory/kratos/commit/1b57fdf375e218ff1cb13fa4888ee01bbb2a986c)):\n\n  GitOrigin-RevId: 754c057a2d2d5b92a417b429caea524a5d54b184\n\n- Add support for Line v2.1 OIDC provider\n  ([#4240](https://github.com/ory/kratos/issues/4240))\n  ([729effd](https://github.com/ory/kratos/commit/729effd61e4b08f28099bba09acef87aeb0c7ffd)):\n\n  For OIDC Line Login, you only need to add id_token_key_type=JWK in the\n  exchange step to issue tokens in ES256 format.\n\n  https://github.com/ory/kratos/discussions/1116\n\n- Allow deleting password credentials\n  ([#4304](https://github.com/ory/kratos/issues/4304))\n  ([f2212d4](https://github.com/ory/kratos/commit/f2212d48af47f24ca6e504ca98bc31afe6774241)):\n\n  The admin API did not allow to delete passwords at all. The restriction is now\n  lifted to only block deletion of the first-factor credential if it is the last\n  one.\n\n- Allow extra go migrations in persister\n  ([#4183](https://github.com/ory/kratos/issues/4183))\n  ([7bec935](https://github.com/ory/kratos/commit/7bec935c33b9adb6033aaecfa9a6dbe6c9c3daa1))\n- Allow listing identities by organization ID\n  ([#4115](https://github.com/ory/kratos/issues/4115))\n  ([b4c453b](https://github.com/ory/kratos/commit/b4c453b0472f67d0a52b345691f66aa48777a897))\n- Allow setting the org ID on creation\n  ([#4306](https://github.com/ory/kratos/issues/4306))\n  ([bccd2fb](https://github.com/ory/kratos/commit/bccd2fb8c8efac96938e564f1f34cd711b41d0a1))\n- Autoconfigure kratos-changefeed\n  ([b8bf4c7](https://github.com/ory/kratos/commit/b8bf4c7ca4323adfa936cf2e24fc98e34123150a)):\n\n  GitOrigin-RevId: 8e684d3c1ed528798c0c81cc4330858c54a39acf\n\n- Bump CRDB, establish foreign key,\n  ([d76e70f](https://github.com/ory/kratos/commit/d76e70f275846e598c601b63fc337e5ceefa8f81)):\n\n  GitOrigin-RevId: ca6d967ddb2e2eeb2d2eaf25e851652dddbc1d47\n\n- Cache OIDC providers ([#4222](https://github.com/ory/kratos/issues/4222))\n  ([30485c4](https://github.com/ory/kratos/commit/30485c44e61c17231e0c46b321be842b19ea5a5f)):\n\n  This change significantly reduces the number of requests to\n  `/.well-known/openid-configuration` endpoints.\n\n- **changelog-oel:** Choose identity schema in self-service registration and\n  login flows\n  ([53f4b9f](https://github.com/ory/kratos/commit/53f4b9f943495ad265e4f0d87cbb018032f068a3)):\n\n  GitOrigin-RevId: 8d6ee03cc8181d3277100a4b7412a3a113799964\n\n- **changelog-oel:** Improved tracing and metrics for the high-performance SQL\n  connection pool\n  ([ce1bf9f](https://github.com/ory/kratos/commit/ce1bf9f46810553ffd8e7ff8aff4abc44e4ce1f0)),\n  closes [hi#performance](https://github.com/hi/issues/performance):\n\n  GitOrigin-RevId: 9480f8997f7641b0f1276ca2ae0f25781428fdbc\n\n- **changelog:** Add a new feature flag for the Recovery V2 to ensure\n  backwards-compatibility\n  ([d68736b](https://github.com/ory/kratos/commit/d68736bed28e956e52a54fef5591bd4c88de6594)):\n\n  GitOrigin-RevId: e630152345321a187bc75ee59a190cc3485556a3\n\n- **changelog:** Add CourierMessageAbandoned & CourierMessageDispatched events\n  ([dfed493](https://github.com/ory/kratos/commit/dfed493184c64d6eee061577549caf7501d017ad)):\n\n  GitOrigin-RevId: b4a2680d2fc9438b565a1283641b49871d1cbb11\n\n- **changelog:** Find-by and delete SAML credentials\n  ([0c80f61](https://github.com/ory/kratos/commit/0c80f61ccafc24e5bb1a497e652edfc8e951431a)):\n\n  GitOrigin-RevId: 4a34b9acfc999454a8678c3e520a1bba3fe84b16\n\n- **changelog:** Migrate http router to stdlib router\n  ([48f5adb](https://github.com/ory/kratos/commit/48f5adb9ce720f6906283372515b85f365a7f0b5)):\n\n  GitOrigin-RevId: ebd7ec330a4f7b9826cb70ba36ba2f727ea64c96\n\n- **changelog:** Reject new password same as old password when changing the\n  password\n  ([a7f50ab](https://github.com/ory/kratos/commit/a7f50abc99ddd7b6dac7dea09004feeb8e84c323)):\n\n  GitOrigin-RevId: 96efafceac92934eb2ab81f1a1b329b0e777cd74\n\n- Console UI for multiple identity schemas\n  ([1145cda](https://github.com/ory/kratos/commit/1145cda7ce2a7b7b30d78f936d005edaf5060fc3)):\n\n  GitOrigin-RevId: c235c2874236762c54e619a1c09def1fd713ce78\n\n- Custom page token column extraction\n  ([c5cb85e](https://github.com/ory/kratos/commit/c5cb85eabee29fe32b7b2ed4acae7308c41cb245)):\n\n  GitOrigin-RevId: 706b836df390da53f8ef3e3800391b206b715949\n\n- Domain telemetry improvements\n  ([93345d7](https://github.com/ory/kratos/commit/93345d7b9f2b302e3b35041191c44f85a6ea0973)):\n\n  GitOrigin-RevId: 9a0825160976ff16b7a39024e650ecfaf9ce82a5\n\n- Drop unused indices post index migration\n  ([#4201](https://github.com/ory/kratos/issues/4201))\n  ([1008639](https://github.com/ory/kratos/commit/1008639428a6b72e0aa47bd13fe9c1d120aafb6e))\n- Emit admin recovery code event\n  ([#4230](https://github.com/ory/kratos/issues/4230))\n  ([a7cdc3a](https://github.com/ory/kratos/commit/a7cdc3a6911e265f4e78c780d8e4b8922066875c))\n- Emit event on Jsonnet claims mapping error\n  ([#4394](https://github.com/ory/kratos/issues/4394))\n  ([8caebdb](https://github.com/ory/kratos/commit/8caebdb6eb67c2039251b53804aac6a9f166f578)):\n\n  We now emit an event containing the Jsonnet input and output in anonymized\n  form when mapping the claims in the OIDC flow fails.\n\n- Emit events on jsonnet failure when templating a jwt\n  ([#4409](https://github.com/ory/kratos/issues/4409))\n  ([959ded5](https://github.com/ory/kratos/commit/959ded5c8bb17b12b2bf242e959802a56f7c43e0)),\n  closes\n  [../blob/master/CONTRIBUTING.md#contributing-code](https://github.com/../blob/master/CONTRIBUTING.md/issues/contributing-code):\n  - Fix typo: parital -> partial\n  - Document with comments why an event is not emitted or not documented\n  - Emit `JsonnetMappingFailed` events on jsonnet failure when templating a jwt\n    (see https://www.ory.sh/docs/identities/session-to-jwt-cors). After review\n    it seems we otherwise always emit events in all the right places, except in\n    this very case. Tested end-to-end manually with the UI.\n\n  ## Related issue(s)\n\n  https://github.com/ory-corp/cloud/issues/7291\n\n  ## Checklist\n  - [x] I have read the\n        [contributing guidelines](../blob/master/CONTRIBUTING.md).\n  - [x] I have referenced an issue containing the design document if my change\n        introduces a new feature.\n  - [x] I am following the [contributing code\n\n- Emit oryWebAuthnInitialized event once webauthn is initialized\n  ([b4485f4](https://github.com/ory/kratos/commit/b4485f411651713a673b11e6984a3e467bd75e51)):\n\n  GitOrigin-RevId: 65bf66553ee2027ce592b1f48741d320e1840de0\n\n- Enable JSONNet templating for password migration hook\n  ([#4390](https://github.com/ory/kratos/issues/4390))\n  ([b162897](https://github.com/ory/kratos/commit/b1628976a0251a0ad84fd2128d1df23f4dff5e99)):\n\n  This enables JSONNet body templating for the password migration hook. There is\n  also a significant refactoring of some internals around webhook config\n  handling.\n\n- Expose Ory-Error-Id HTTP header\n  ([f2b0cd5](https://github.com/ory/kratos/commit/f2b0cd5a5669e9116d248d1a43a875e5a38d723c)):\n\n  GitOrigin-RevId: 3fe0ebc17fec11dd8135bfdd8e6facfd99ac2d5a\n\n- Fast add credential type lookups\n  ([#4177](https://github.com/ory/kratos/issues/4177))\n  ([eeb1355](https://github.com/ory/kratos/commit/eeb13552118504f17b48f2c7e002e777f5ee73f4))\n- Faster UpdateIdentity\n  ([4c2cfae](https://github.com/ory/kratos/commit/4c2cfaefc777c38fee35d529156629f148b6da85)):\n\n  GitOrigin-RevId: d3d0ea990908443967a2c576d6b04e5c4f8ba03a\n\n- Fewer DB loads when linking credentials, add tracing\n  ([2c5bb21](https://github.com/ory/kratos/commit/2c5bb21224e28d5218354349f77514f4fbe71762))\n- Goreleaser\n  ([db10a68](https://github.com/ory/kratos/commit/db10a68529088b7e45489e8ad463661cbdcffbff)):\n\n  GitOrigin-RevId: c4975f609610e3f05eaff13eb1d07eec90c49a2d\n\n- Gracefully handle failing password rehashing during login\n  ([#4235](https://github.com/ory/kratos/issues/4235))\n  ([3905787](https://github.com/ory/kratos/commit/39057879821b387b49f5d4f7cb19b9e02ec924a7)):\n\n  This fixes an issue where we would successfully import long passwords (>72\n  chars), but fail when the user attempts to login with the correct password\n  because we can't rehash it. In this case, we simply issue a warning to the\n  logs, keep the old hash intact, and continue logging in the user.\n\n- **hydra:** Split up persister\n  ([910cf9c](https://github.com/ory/kratos/commit/910cf9c0a220f310e08c35701fb721faeb8fb685)):\n\n  GitOrigin-RevId: 203cf926c1613fcbb20393c5b7d0af25c7aecb15\n\n- Improve domain telemetry for OSS (Hydra & Kratos)\n  ([86ab72a](https://github.com/ory/kratos/commit/86ab72ac7850b84e4608774a4fb98e4bd1d46ca0)):\n\n  GitOrigin-RevId: b8aebb0ad8bae28ee8295b9052b2f60603244b7e\n\n- Improve identity import limits\n  ([#4378](https://github.com/ory/kratos/issues/4378))\n  ([e38e812](https://github.com/ory/kratos/commit/e38e812314850c80cb18f8e1921dffc7d324aeda))\n- Improve kratos courier metrics and debug log message\n  ([c50ffcc](https://github.com/ory/kratos/commit/c50ffcc5cb2210a79953f38af2f99d46f013c780)):\n\n  GitOrigin-RevId: 077bc86e499ca7be1076c3ae38412384c2777553\n\n- Improve QueryForCredentials\n  ([#4181](https://github.com/ory/kratos/issues/4181))\n  ([ca0d6a7](https://github.com/ory/kratos/commit/ca0d6a7ea717495429b8bac7fd843ac69c1ebf16))\n- Improve secondary indices for self service tables\n  ([#4179](https://github.com/ory/kratos/issues/4179))\n  ([825aec2](https://github.com/ory/kratos/commit/825aec208d966b54df9eeac6643e6d8129cf2253))\n- Improve verification required flows\n  ([#4407](https://github.com/ory/kratos/issues/4407))\n  ([2014a40](https://github.com/ory/kratos/commit/2014a403e0bc05a10d1f805b9ef81bdd4d8e2223))\n- Improved events and identity recent activity\n  ([e47b858](https://github.com/ory/kratos/commit/e47b85851539345abc70468eb8d05db882e19df6)):\n\n  GitOrigin-RevId: 3ef8d9391a402381025baaf25ba3c8c199805b7e\n\n- Improved tracing for courier\n  ([85a7071](https://github.com/ory/kratos/commit/85a7071d20d0f072316c74bee82c76ee690276f8))\n- Index hint for CRDB when deleting identity credentials\n  ([#4276](https://github.com/ory/kratos/issues/4276))\n  ([c703a33](https://github.com/ory/kratos/commit/c703a338894f865c7dc1dcebc6e6980ad98eaa1d)):\n\n  Ref https://support.cockroachlabs.com/hc/en-us/requests/25430\n\n- Jackson provider ([#4242](https://github.com/ory/kratos/issues/4242))\n  ([f18d1b2](https://github.com/ory/kratos/commit/f18d1b24539f7d8dcf9c27986af861d0f8cb9683)):\n\n  This adds a jackson provider to Kratos.\n\n- Load session only once when middleware is used\n  ([#4187](https://github.com/ory/kratos/issues/4187))\n  ([234b6f2](https://github.com/ory/kratos/commit/234b6f2f6435c62b7e161c032b888c4e2b3328d4))\n- Monorepo\n  ([31f1894](https://github.com/ory/kratos/commit/31f18944116f6fde546fdd5139faf7e9b68a4c10)):\n\n  GitOrigin-RevId: dbb48d171fad1f9b4fd31385f0ef4fb01e39e823\n\n- More extension points ([#4272](https://github.com/ory/kratos/issues/4272))\n  ([373a2e6](https://github.com/ory/kratos/commit/373a2e6552f0da0488638306a58d8bd63a6ca10a)):\n\n  This adds more extension points to the Kratos registry.\n\n- Move config testhelpers to ory/x\n  ([8d43aae](https://github.com/ory/kratos/commit/8d43aae7bc6c003287b03e6f8900a536fd13dcef)):\n\n  GitOrigin-RevId: fd484445e9715760231f7f86ec212d094e826377\n\n- Optimize identity-related secondary indices\n  ([#4182](https://github.com/ory/kratos/issues/4182))\n  ([53874c1](https://github.com/ory/kratos/commit/53874c1753940e08e0bf50753a1d3126add77af1))\n- Passwordless SMS and expiry notice in code / link templates\n  ([#4104](https://github.com/ory/kratos/issues/4104))\n  ([462cea9](https://github.com/ory/kratos/commit/462cea91448a00a0db21e20c2c347bf74957dc8f)):\n\n  This feature allows Ory Kratos to use the SMS gateway for login and\n  registration with code via SMS.\n\n  Additionally, the default email and sms templates have been updated. We now\n  also expose `ExpiresInMinutes` / `expires_in_minutes` in the templates, making\n  it easier to remind the user how long the code or link is valid for.\n\n  Closes https://github.com/ory/kratos/issues/1570 Closes\n  https://github.com/ory/kratos/issues/3779\n\n- Recovery with any address including with a code via SMS\n  ([71844dd](https://github.com/ory/kratos/commit/71844dd75fed5c60d29399dc3595ccafcc5f0809)):\n\n  GitOrigin-RevId: 4fa4ea56feacf71fa7fc84fa2fc33ce94db5a21e\n\n- Refactor cmd/daemon ([#4371](https://github.com/ory/kratos/issues/4371))\n  ([7fe55d9](https://github.com/ory/kratos/commit/7fe55d9fec5e5f4048b211eaa56ac61e29635157))\n- Remove duplicate queries during settings flow and use better index hint for\n  credentials lookup ([#4193](https://github.com/ory/kratos/issues/4193))\n  ([c33965e](https://github.com/ory/kratos/commit/c33965e5735ead3acddac87ef84c3a730874f9ab)):\n\n  This patch reduces duplicate GetIdentity queries as part of submitting the\n  settings flow, and improves an index to significantly reduce credential\n  lookup.\n\n  For better debugging, more tracing ha been added to the settings module.\n\n- Remove more unused indices\n  ([#4186](https://github.com/ory/kratos/issues/4186))\n  ([b294804](https://github.com/ory/kratos/commit/b2948044de4eee1841110162fe874055182bd2d2))\n- Return field name in generated node text label\n  ([8c7a3dc](https://github.com/ory/kratos/commit/8c7a3dc5eb7e863b7a710cc9256a12e6125e359d)):\n\n  GitOrigin-RevId: c37d048759b18337eafafbf9cdf8449253b64836\n\n- Rework the OTP code submit count mechanism\n  ([#4251](https://github.com/ory/kratos/issues/4251))\n  ([4ca4d79](https://github.com/ory/kratos/commit/4ca4d79cff5185caad27eddee7e6f8d0e58463ba)):\n  - feat: rework the OTP code submit count mechanism\n\n  Unlike what the previous comment suggested, incrementing and checking the\n  submit count inside the database transaction is not actually optimal\n  peformance- or security-wise.\n\n  We now check atomically increment and check the submit count as the first part\n  of the operation, and abort as early as possible if we detect brute-forcing.\n  This prevents a situation where the check works only on certain transaction\n  isolation levels.\n  - chore: bump dependencies\n\n- Support android webauthn origins\n  ([#4155](https://github.com/ory/kratos/issues/4155))\n  ([a82d288](https://github.com/ory/kratos/commit/a82d288014411ae4eb82c718bfe825ca55b4fab0)):\n\n  This patch adds the ability to verify Android APK origins used during\n  WebAuthn/Passkey exchange.\n\n  Upgrades go-webauthn and includes fixes for Go 1.23 and workarounds for\n  Swagger.\n\n- Support CRUD OIDC providers through the onboarding portal API\n  ([664fd1a](https://github.com/ory/kratos/commit/664fd1a48d822f29b1ee2164818c560df63b29e2)):\n\n  GitOrigin-RevId: 76c77654a4dc1150d3edb92c2ba428bd325850bc\n\n- Support importing more credentials\n  ([#4361](https://github.com/ory/kratos/issues/4361))\n  ([9a6dadf](https://github.com/ory/kratos/commit/9a6dadfefaf0d54c227cdbab5a2cbe7da14faa96)):\n\n  Adds support to import SAML credentials. SAML connections are only available\n  in Ory Enterprise License / Ory Network.\n\n- Trace identity id in errors\n  ([772572c](https://github.com/ory/kratos/commit/772572c0d633d5848f12d41947d52df1d391329b)):\n\n  GitOrigin-RevId: dec38a20911ebf8038ed8eb5a5f286e79430266d\n\n- Update only necessary database columns in UpdateVerifiableAddress\n  ([#4292](https://github.com/ory/kratos/issues/4292))\n  ([168a3f6](https://github.com/ory/kratos/commit/168a3f6c68b1fbc0ddcd455f8762f6de19879442)):\n\n  This is an optimization to reduce database load.\n\n  When we specify exactly which columns changed, we should be able to elide\n  updates to the\n  `identity_verifiable_addresses_status_via_uq_idx   (nid,via,value)` index.\n  Updating that index requires contacting remote regions.\n\n  Also fixed a bug where we did not set the `verified_at` timestamp correctly\n  sometimes.\n\n- Use one transaction for `/admin/recovery/code`\n  ([#4225](https://github.com/ory/kratos/issues/4225))\n  ([3e87e0c](https://github.com/ory/kratos/commit/3e87e0c4559736f9476eba943bac8d67cde91aad))\n- Use stdlib HTTP router in Kratos\n  ([acfa6ef](https://github.com/ory/kratos/commit/acfa6ef2ec4aa61f9a1a7da6fdda59609f1360e3)):\n\n  GitOrigin-RevId: 799513e99acbf43a05fe3113ffda45d2fff2a9e0\n\n- Use vendored ory/x\n  ([a9ab800](https://github.com/ory/kratos/commit/a9ab800a4373875a29c58492f174165e0b4592e7)):\n\n  GitOrigin-RevId: 994f3b754946ca5b2bd1bab0fe20532f5d5ab62f\n\n- Webhook header allowlist configuration option\n  ([#4309](https://github.com/ory/kratos/issues/4309))\n  ([871f5aa](https://github.com/ory/kratos/commit/871f5aab6d7b2a655ebcd6f0f90e79635ffc85f6)),\n  closes [#4290](https://github.com/ory/kratos/issues/4290):\n\n  Adds a `clients.web_hook.header_allowlist` configuration option for\n  configuring the webhook header allowlist.\n\n### Reverts\n\n- Tests: improve randomness in e2e tests\n  ([19a41ec](https://github.com/ory/kratos/commit/19a41ecd505e1a78dcbefb8c1f264268adfd4415)):\n\n  GitOrigin-RevId: 377b6b2dca8eec59f244d3b0f94883a6b443b772\n\n- Use account.apple.com for oidc discovery and appleid.apple.com for token\n  verification and signing\n  ([c772d8b](https://github.com/ory/kratos/commit/c772d8b87b1e13e761a2f64f545607c70b330b55)):\n\n  GitOrigin-RevId: 5afe7dc747df323cf9c89d6406d4ea644c36ca68\n\n- Use appleid audience for secret exchange\n  ([620e33e](https://github.com/ory/kratos/commit/620e33e11343b84f80cc1e83ec585fcc375bd65b)):\n\n  GitOrigin-RevId: 2110e8e47501f01ead389eb5b76bd90bcd12ada7\n\n- Use updated appleid issuer\n  ([9697c45](https://github.com/ory/kratos/commit/9697c453933a3ca33e35a8fbd2c59908603db28b)):\n\n  GitOrigin-RevId: 56915792c8cdcf0330523a482ca3a8f1f68d95e3\n\n### Tests\n\n- Add golangci-lint config and GHA\n  ([0720950](https://github.com/ory/kratos/commit/0720950361ffa44deb7078c6c842a2d9b49540c0)):\n\n  GitOrigin-RevId: eb14c9f38e2b98d11a78ee0b90fd8f4f689abd3d\n\n- Don't require DB for hasher tests\n  ([41c69db](https://github.com/ory/kratos/commit/41c69db6b2b44cdc8d3a72d3fb5c459bf96c1358)):\n\n  GitOrigin-RevId: 2fd89b72bf82c88cff030b0df2eaa97fe8d4f095\n\n- **hydra:** Add snapshots for login & consent requests\n  ([ee39bdb](https://github.com/ory/kratos/commit/ee39bdb5bd337dade757ed9f6ac33dd07bd76c35)):\n\n  GitOrigin-RevId: 47d041cf207af6c3e9e21bf3016e5ea0cf044344\n\n- Improve pgxpool tests\n  ([6297a8f](https://github.com/ory/kratos/commit/6297a8fc22cdb9c9a9301cfbba4496160d2b4aa5)):\n\n  GitOrigin-RevId: 2008231a7e0eb05276484b7eda885899c67f0a3a\n\n- Resturcture and improve integration tests\n  ([6f32d5d](https://github.com/ory/kratos/commit/6f32d5d623882e3e742c305f0eddd81156283173)):\n\n  GitOrigin-RevId: 83dfe53cfc33f0a974d7b2f7eeed81d017d2518c\n\n- Update snapshots ([#4167](https://github.com/ory/kratos/issues/4167))\n  ([b51f780](https://github.com/ory/kratos/commit/b51f780b7e4abc79a757ac1efe1cb65b3d35c8a4))\n\n### Unclassified\n\n- Improve randomness in e2e tests\n  ([ecfe435](https://github.com/ory/kratos/commit/ecfe43591d6a9e476e22d4a3872d393f8b7179d0)):\n\n  GitOrigin-RevId: 2dcb862d4375fa22824a5694767329bdea990bde\n\n- Run credential validation in its own goroutine when changing the password\n  ([c7fedfe](https://github.com/ory/kratos/commit/c7fedfe21f2e95f89b54ced34bf9b49bd5f64fb9)):\n\n  GitOrigin-RevId: fbc4ca277a1f3feeb2c6ec9344f498cee6d76dee\n\n# [1.3.0](https://github.com/ory/kratos/compare/v1.2.0...v1.3.0) (2024-09-26)\n\nWe are thrilled to announce the release\nof [Ory Kratos v1.3.0](https://www.ory.sh/kratos)! This release includes\nsignificant updates, enhancements, and fixes to improve your experience with Ory\nKratos.\n\n![Ory Kratos 1.3.0 Release](https://www.ory.sh/images/newsletter/kratos-1.3.0/kratos-1.3-release.png)\n\nEnhance your sign-in experience with Identifier First Authentication. This\nfeature allows users to first identify themselves (e.g., by providing their\nemail or username) and then proceed with the chosen authentication method,\nwhether it be OTP code, passkeys, passwords, or social login. By streamlining\nthe sign-in process, users can select the authentication method that best suits\ntheir needs, reducing friction and enhancing security. Identifier First\nAuthentication improves user flow and reduces the likelihood of errors,\nresulting in a more user-friendly and efficient login experience.\n\n![Identifier First Authentication](https://www.ory.sh/images/newsletter/kratos-1.3.0/identifier-first-demo.png)\n\nThe UI for OpenID Connect (OIDC) account linking has been improved to provide\nbetter user guidance and error messages during the linking process. As a result,\naccount linking error rates have dropped significantly, making it easier for\nusers to link multiple identities (e.g., social login and email-based accounts)\nto the same profile. This improvement enhances user convenience, reduces support\ninquiries, and offers a seamless multi-account experience.\n\nYou can now use Salesforce as an identity provider, expanding the range of\nsupported identity providers. This integration allows organizations already\nusing Salesforce for identity management to leverage their existing\ninfrastructure, simplifying user management and enhancing the authentication\nexperience.\n\nSocial sign-in has been enhanced with better detection and handling of\ndouble-submit issues, especially for platforms like Facebook and Apple mobile\nlogin. These changes make the social login process more reliable, reducing\nerrors and improving the user experience. Additionally, Ory Kratos now supports\nsocial providers in credential discovery, offering more flexibility during\nsign-up and sign-in flows.\n\nOne-Time Password (OTP) MFA has been improved with more robust handling of\ncode-based authentication. The enhancements ensure a smoother flow when using\nOTP for multi-factor authentication (MFA), providing clearer guidance to users\nand improving fallback mechanisms. These updates help to prevent users from\nbeing locked out due to misconfigurations or errors during the MFA process,\nincreasing security without compromising user convenience.\n\n- **Deprecated `via` Parameter for SMS 2FA**: The `via` parameter is now\n  deprecated when performing SMS 2FA. If not included, users will see all their\n  phone/email addresses to perform the flow. This parameter will be removed in a\n  future version. Ensure your identity schema has the appropriate code\n  configuration for passwordless or 2FA login.\n- **Endpoint Change**: The `/admin/session/.../extend` endpoint will now return\n  204 No Content for new Ory Network projects. Returning 200 with the session\n  body will be deprecated in future versions.\n\n- **SDK Enhancements**: Added new methods and support for additional actions in\n  the SDK, improving integration capabilities.\n- **Password Migration Hook**: Added a password migration hook to facilitate\n  migrating passwords where the hash is unavailable, easing the transition to\n  Ory Kratos.\n- **Partially Failing Batch Inserts:** When batch-inserting multiple identities,\n  conflicts or validation errors of a subset of identities in the batch still\n  allow the rest of the identities to be inserted. The returned JSON contains\n  the error details that led to the failure.\n\n- **Security Fixes**: Fixed a security vulnerability where the `code` method did\n  not respect the `highest_available`setting. Refer to\n  the [security advisory](https://github.com/ory/kratos/security/advisories/GHSA-wc43-73w7-x2f5) for\n  more details.\n- **Session Extension Issues**: Fixed issues related to session extension to\n  prevent long response times on `/session/whoami` when extending sessions\n  simultaneously.\n- **OIDC and Social Sign-In**: Fixed UI and error handling for OpenID Connect\n  and social sign-in flows, improving the overall experience.\n- **Credential Identifier Handling**: Corrected handling of code credential\n  identifiers, ensuring proper detection of phone numbers and correct\n  functioning of SMS/email MFA.\n- **Concurrent Updates for Webhooks**: Fixed concurrent map update issues for\n  webhook headers, improving webhook reliability.\n\n- **Passwordless & 2FA Login**: Before upgrading, ensure your identity schema\n  has the appropriate code configuration when using the code method for\n  passwordless or 2FA login.\n- **Code Method for 2FA**: If you use the code method for 2FA or 1FA login but\n  haven't configured the code identifier,\n  set `selfservice.methods.code.config.missing_credential_fallback_enabled` to `true` to\n  avoid user lockouts.\n\nWe hope you enjoy the new features and improvements in Ory Kratos v1.3.0. Please\nremember to leave a [GitHub star](https://github.com/ory/kratos) and check out\nour other [open-source projects](https://github.com/ory). Your feedback is\nvaluable to us, so join the [Ory community](https://slack.ory.sh/) and help us\nshape the future of identity management.\n\n## Breaking Changes\n\nWhen using two-step registration, it was previously possible to send\n`method=profile:back` to get to the previous screen. This feature was not\ndocumented in the SDK API yet. Going forward, please instead use\n`screen=previous`.\n\nPlease note that the `via` parameter is deprecated when performing SMS 2FA. It\nwill be removed in a future version. If the parameter is not included in the\nrequest, the user will see all their phone/email addresses from which to perform\nthe flow.\n\nBefore upgrading, ensure that your identity schema has the appropriate code\nconfiguration when using the code method for passwordless or 2fa login.\n\nIf you are using the code method for 2FA login already, or you are using it for\n1FA login but have not yet configured the code identifier, set\n`selfservice.methods.code.config.missing_credential_fallback_enabled` to `true`\nto prevent users from being locked out.\n\nPlease note that the `via` parameter is deprecated when performing SMS 2FA. It\nwill be removed in a future version. If the parameter is not included in the\nrequest, the user will see all their phone/email addresses from which to perform\nthe flow.\n\nBefore upgrading, ensure that your identity schema has the appropriate code\nconfiguration when using the code method for passwordless or 2fa login.\n\nIf you are using the code method for 2FA login already, or you are using it for\n1FA login but have not yet configured the code identifier, set\n`selfservice.methods.code.config.missing_credential_fallback_enabled` to `true`\nto prevent users from being locked out.\n\nGoing forward, the `/admin/session/.../extend` endpoint will return 204 no\ncontent for new Ory Network projects. We will deprecate returning 200 + session\nbody in the future.\n\n### Bug Fixes\n\n- Add continue with only for json browser requests\n  ([#4002](https://github.com/ory/kratos/issues/4002))\n  ([e0a4010](https://github.com/ory/kratos/commit/e0a4010b84b43f364be14414a380c872b166274d))\n- Add fallback to providerLabel\n  ([#3999](https://github.com/ory/kratos/issues/3999))\n  ([d26f204](https://github.com/ory/kratos/commit/d26f2042eb5325a8d639c08d95a005724e61cb8e)):\n\n  This adds a fallback to the provider label when trying to register a duplicate\n  identifier with an oidc.\n\n  Current error message:\n\n  `Signing in will link your account to \"test@test.com\" at provider \"\". If you do not wish to link that account, please start a new login flow.`\n\n  The label represents an optional label for the UI, but in my case it's always\n  empty. I suggest we fallback to the provider when the label is not present. In\n  case the label is present, the behaviour won't change.\n\n  Fallback to provider:\n\n  `Signing in will link your account to \"test@test.com\" at provider \"google\". If you do not wish to link that account, please start a new login flow.`\n\n- Add missing JS triggers\n  ([7597bc6](https://github.com/ory/kratos/commit/7597bc6345848b66161d5a9b7a42307bbc85c978))\n- Add PKCE config key to config schema\n  ([#4098](https://github.com/ory/kratos/issues/4098))\n  ([2c7ff3c](https://github.com/ory/kratos/commit/2c7ff3c8baab6aaa105e2d733a483fc07537470f))\n- Batch identity created event\n  ([#4111](https://github.com/ory/kratos/issues/4111))\n  ([340f698](https://github.com/ory/kratos/commit/340f698243bd908e217394710b475a7f686a8cf9))\n- Concurrent map update for webhook header\n  ([#4055](https://github.com/ory/kratos/issues/4055))\n  ([6ceb2f1](https://github.com/ory/kratos/commit/6ceb2f1213e1b28d3aa72380661e4aa985bfa437))\n- Do not populate `id_first` first step for account linking flows\n  ([#4074](https://github.com/ory/kratos/issues/4074))\n  ([6ab2637](https://github.com/ory/kratos/commit/6ab2637652013e0ff377f52355e2025d68c7b3d3))\n- Downgrade go-webauthn ([#4035](https://github.com/ory/kratos/issues/4035))\n  ([4d1954a](https://github.com/ory/kratos/commit/4d1954ac74dee358f9a08e619848dfe94e4934ce))\n- Emit SelfServiceMethodUsed in SettingsSucceeded event\n  ([#4056](https://github.com/ory/kratos/issues/4056))\n  ([76af303](https://github.com/ory/kratos/commit/76af303b20ae5dffb932169a73667a55be3f3f80))\n- Filter web hook headers ([#4048](https://github.com/ory/kratos/issues/4048))\n  ([ddb838e](https://github.com/ory/kratos/commit/ddb838e0e8f7d752cd1708c505e80b6c0ccc0b8a))\n- Improve OIDC account linking UI\n  ([#4036](https://github.com/ory/kratos/issues/4036))\n  ([2b4a618](https://github.com/ory/kratos/commit/2b4a618485c9d79762243f59b35f142083f5492c))\n- Include duplicate credentials in account linking message\n  ([#4079](https://github.com/ory/kratos/issues/4079))\n  ([122b63d](https://github.com/ory/kratos/commit/122b63d68a3ff2ad78107300869c5a6d2aa43354))\n- Incorrect append of code credential identifier\n  ([#4102](https://github.com/ory/kratos/issues/4102))\n  ([3215792](https://github.com/ory/kratos/commit/3215792df4cab494c05ef09e969b2fa0ed95a98b)),\n  closes [#4076](https://github.com/ory/kratos/issues/4076)\n- Jsonnet timeouts ([#3979](https://github.com/ory/kratos/issues/3979))\n  ([7c5299f](https://github.com/ory/kratos/commit/7c5299f1f832ebbe0622d0920b7a91253d26b06c))\n- Move password migration hook config\n  ([#3986](https://github.com/ory/kratos/issues/3986))\n  ([b5a66e0](https://github.com/ory/kratos/commit/b5a66e0dde3a8fa6fdeb727482481b6302589631)):\n\n  This moves the password migration hook to\n\n  ```yaml\n  selfservice:\n    methods:\n      password:\n        config:\n          migrate_hook: ...\n  ```\n\n- Normalize code credentials and deprecate via parameter\n  ([c417b4a](https://github.com/ory/kratos/commit/c417b4aa76a76d3aebb4474999d7bb072615bd9f)):\n\n  Before this, code credentials for passwordless and mfa login were incorrectly\n  stored and normalized. This could cause issues where the system would not\n  detect the user's phone number, and where SMS/email MFA would not properly\n  work with the `highest_available` setting.\n\n- Passthrough correct organization ID to CompletedLoginForWithProvider\n  ([#4124](https://github.com/ory/kratos/issues/4124))\n  ([ad1acd5](https://github.com/ory/kratos/commit/ad1acd51d8dd7582b05a3078b92f73970e1e2715))\n- Password migration hook config\n  ([#4001](https://github.com/ory/kratos/issues/4001))\n  ([50deedf](https://github.com/ory/kratos/commit/50deedfeecf7adbc948521371b181306a0c26cf1)):\n\n  This fixes the config loading for the password migration hook.\n\n- Pw migration param ([#3998](https://github.com/ory/kratos/issues/3998))\n  ([6016cc8](https://github.com/ory/kratos/commit/6016cc88a076eeea71a85d75cfb5191808b69844))\n- Refactor internal API to prevent panics\n  ([#4028](https://github.com/ory/kratos/issues/4028))\n  ([81bc152](https://github.com/ory/kratos/commit/81bc1525f09504729c666192d458cf2eaafab99f))\n- Remove flows from log messages\n  ([#3913](https://github.com/ory/kratos/issues/3913))\n  ([310a405](https://github.com/ory/kratos/commit/310a405202c6b44633b15ad30e1fdb8ebd153e4b))\n- Replace submit with continue button for recovery and verification and add\n  maxlength\n  ([04850f4](https://github.com/ory/kratos/commit/04850f45cfbdc89223366ffa3b540d579a3b44be))\n- Return credentials in FindByCredentialsIdentifier\n  ([#4068](https://github.com/ory/kratos/issues/4068))\n  ([f949173](https://github.com/ory/kratos/commit/f949173b3ed3d45167bb4af8b95440d5e4a39636)):\n\n  Instead of re-fetching the credentials later (expensive), we load them only\n  once.\n\n- Return error if invalid UUID is supplied to ids filter\n  ([#4116](https://github.com/ory/kratos/issues/4116))\n  ([98140f2](https://github.com/ory/kratos/commit/98140f2fd43ccd889e2635e4f3e7582b92fe96ab))\n- **security:** Code credential does not respect `highest_available` setting\n  ([b0111d4](https://github.com/ory/kratos/commit/b0111d4bd561d0f0e2f5883f30fac36fcf7135d5)):\n\n  This patch fixes a security vulnerability which prevents the `code` method to\n  properly report it's credentials count to the `highest_available` mechanism.\n\n  For more details on this issue please refer to the\n  [security advisory](https://github.com/ory/kratos/security/advisories/GHSA-wc43-73w7-x2f5).\n\n- Timestamp precision on mysql\n  ([9a1f171](https://github.com/ory/kratos/commit/9a1f171c1a4a8d20dc2103073bdc11ee3fdc70af))\n- Transient_payload is lost when verification flow started as part of\n  registration ([#3983](https://github.com/ory/kratos/issues/3983))\n  ([192f10f](https://github.com/ory/kratos/commit/192f10f4ad9eb44a612baaccfc71765d52c7e1ed))\n- Trigger oidc web hook on sign in after registration\n  ([#4027](https://github.com/ory/kratos/issues/4027))\n  ([ad5fb09](https://github.com/ory/kratos/commit/ad5fb09687f863e7c5d45868d0b8f5ec2d965372))\n- Typo in login link CLI error messages\n  ([#3995](https://github.com/ory/kratos/issues/3995))\n  ([8350625](https://github.com/ory/kratos/commit/835062542077b9dd8d6a30836d0455adb015265d))\n- Validate page tokens for better error codes\n  ([#4021](https://github.com/ory/kratos/issues/4021))\n  ([32737dc](https://github.com/ory/kratos/commit/32737dc708c1ecf0ec0ceaa4bbc0ac09286186fd))\n- Whoami latency ([#4070](https://github.com/ory/kratos/issues/4070))\n  ([ff6ed5b](https://github.com/ory/kratos/commit/ff6ed5b70b7f715fc38a41cedd17b5323aebd79e))\n\n### Code Generation\n\n- Pin v1.3.0 release commit\n  ([0a49fd0](https://github.com/ory/kratos/commit/0a49fd05245f179501b117163cd574786f287fe8))\n\n### Documentation\n\n- Add google to supported providers in ID Token doc strings\n  ([#4026](https://github.com/ory/kratos/issues/4026))\n  ([955bd8f](https://github.com/ory/kratos/commit/955bd8fbc1353d7a9f84d8f591c3af31781cf7b7))\n- Typo in changelog\n  ([c508980](https://github.com/ory/kratos/commit/c5089801af2a656e9c1fc371a11aeb23918ba359))\n\n### Features\n\n- Add additional messages\n  ([735fc5b](https://github.com/ory/kratos/commit/735fc5b2c5a99746d3012cc38ee2e1b7cc3a67f2))\n- Add browser return_to continue_with action\n  ([7b636d8](https://github.com/ory/kratos/commit/7b636d860c6917cb1133d6d1d7401808adb890c7))\n- Add if method to sdk\n  ([612e3bf](https://github.com/ory/kratos/commit/612e3bf09dbffd3feba08d5100bffbc39cbd240a))\n- Add redirect to continue_with for SPA flows\n  ([99c945c](https://github.com/ory/kratos/commit/99c945c92d0c2745dc8df4402d755afd53e1b9aa)):\n\n  This patch adds the new `continue_with` action `redirect_browser_to`, which\n  contains the redirect URL the app should redirect to. It is only supported for\n  SPA (not server-side browser apps, not native apps) flows at this point in\n  time.\n\n- Add social providers to credential discovery as well\n  ([5f4a2bf](https://github.com/ory/kratos/commit/5f4a2bf619d540d45e96586129c8ee1e7850e745))\n- Add support for Salesforce as identity provider\n  ([#4003](https://github.com/ory/kratos/issues/4003))\n  ([3bf1ca9](https://github.com/ory/kratos/commit/3bf1ca9030555df90ef9903c34313ae4bd1fecae))\n- Add tests for two step login\n  ([#3959](https://github.com/ory/kratos/issues/3959))\n  ([8225e40](https://github.com/ory/kratos/commit/8225e40e3d767e945006b33eebdfc47fd242ff06))\n- Allow deletion of an individual OIDC credential\n  ([#3968](https://github.com/ory/kratos/issues/3968))\n  ([a43cef2](https://github.com/ory/kratos/commit/a43cef23c177acddbf8b03afef087feeaca51981)):\n\n  This extends the existing `DELETE /admin/identities/{id}/credentials/{type}`\n  API to accept an `?identifier=foobar` query parameter for `{type}==oidc` like\n  such:\n\n  `DELETE /admin/identities/{id}/credentials/oidc?identifier=github%3A012345`\n\n  This will delete the GitHub OIDC credential with the identifier\n  `github:012345` (`012345` is the subject as returned by GitHub).\n\n  To find out which OIDC credentials exist, call\n  `GET /admin/identities/{id}?include_credential=oidc` beforehand.\n\n  This will allow you to delete individual OIDC credentials for users even if\n  they have several set up.\n\n- Allow partially failing batch inserts\n  ([#4083](https://github.com/ory/kratos/issues/4083))\n  ([4ba7033](https://github.com/ory/kratos/commit/4ba70330cf9e0eda9044b0a5a504c34493ae17ed)):\n\n  When batch-inserting multiple identities, conflicts or validation errors of a\n  subset of identities in the batch still allow the rest of the identities to be\n  inserted. The returned JSON contains the error details that lead to the\n  failure.\n\n- Better detection if credentials exist on identifier first login\n  ([#3963](https://github.com/ory/kratos/issues/3963))\n  ([42ade94](https://github.com/ory/kratos/commit/42ade94e32a9a7ad6c0bda785e86d7209c46d8bb))\n- Change `method=profile:back` to `screen=previous`\n  ([#4119](https://github.com/ory/kratos/issues/4119))\n  ([2cd8483](https://github.com/ory/kratos/commit/2cd8483e809170d0524fe6a5d13837108d29fa54))\n- Clarify session extend behavior\n  ([#3962](https://github.com/ory/kratos/issues/3962))\n  ([af5ea35](https://github.com/ory/kratos/commit/af5ea35759e74d7a1637823abcc21dc8e3e39a9d))\n- Client-side PKCE take 3 ([#4078](https://github.com/ory/kratos/issues/4078))\n  ([f7c1024](https://github.com/ory/kratos/commit/f7c102456a71b226d8353b9d59cc03fb2ba0af40)):\n  - feat: client-side PKCE\n\n  This change introduces a new configuration for OIDC providers: pkce with\n  values auto (default), never, force.\n\n  When auto is specified or the field is omitted, Kratos will perform\n  autodiscovery and perform PKCE when the server advertises support for it. This\n  requires the issuer_url to be set for the provider.\n\n  never completely disables PKCE support. This is only theoretically useful:\n  when a provider advertises PKCE support but doesn't actually implement it.\n\n  force always sends a PKCE challenge in the initial redirect URL, regardless of\n  what the provider advertises. This setting is useful when the provider offers\n  PKCE but doesn't advertise it in his ./well-known/openid-configuration.\n\n  Important: When setting pkce: force, you must whitelist a different return URL\n  for your OAuth2 client in the provider's configuration. Instead of\n  <base-url>/self-service/methods/oidc/callback/<provider>, you must use\n  <base-url>/self-service/methods/oidc/callback (note missing last path\n  segment). This is to enable the use of the same OAuth client ID+secret when\n  configuring several Kratos OIDC providers, without having to whitelist\n  individual redirect_uris for each Kratos provider config.\n  - chore: regenerate SDK, bump DB versions, cleanup tool install\n  - chore: get final organization ID from provider config during registration\n    and login\n  - chore: fixup OIDC function signatures and improve tests\n\n- Emit events in identity persister\n  ([#4107](https://github.com/ory/kratos/issues/4107))\n  ([20156f6](https://github.com/ory/kratos/commit/20156f651f2faa0a79842de8d2fb4a09ee7094c1))\n- Enable new-style OIDC state generation\n  ([#4121](https://github.com/ory/kratos/issues/4121))\n  ([eb97243](https://github.com/ory/kratos/commit/eb97243d6499e2d9f2338a2ce3f5e39579d19086))\n- Identifier first auth\n  ([1bdc19a](https://github.com/ory/kratos/commit/1bdc19ae3e1a3df38234cb892f65de4a2c95f041))\n- Identifier first login for all first factor login methods\n  ([638b274](https://github.com/ory/kratos/commit/638b27431312bcd91844ac4a00733a840976aa4f))\n- Improve session extend performance\n  ([#3948](https://github.com/ory/kratos/issues/3948))\n  ([4e3fad4](https://github.com/ory/kratos/commit/4e3fad4b4739b5cf00d658155350cb599f2cd06a)):\n\n  This patch improves the performance for extending session lifespans. Lifespan\n  extension is tricky as it is often part of the middleware of Ory Kratos\n  consumers. As such, it is prone to transaction contention when we read and\n  write to the same session row at the same time (and potentially multiple\n  times).\n\n  To address this, we:\n  1. Introduce a locking mechanism on the row to reduce transaction contention;\n  2. Add a new feature flag that toggles returning 204 no content instead of\n     200 + session.\n\n  Be aware that all reads on the session table will have to wait for the\n  transaction to commit before they return a value. This may cause long(er)\n  response times on `/session/whoami` for sessions that are being extended at\n  the same time.\n\n- Password migration hook ([#3978](https://github.com/ory/kratos/issues/3978))\n  ([c9d5573](https://github.com/ory/kratos/commit/c9d55730a10b71ac61bb5097f5f9c33f144f2a95)):\n\n  This adds a password migration hook to easily migrate passwords for which we\n  do not have the hash.\n\n  For each user that needs to be migrated to Ory Network, a new identity is\n  created with a credential of type password with a config of\n  {\"use_password_migration_hook\": true} . When a user logs in, the credential\n  identifier and password will be sent to the password_migration web hook if all\n  of these are true: The user’s identity’s password credential is\n  {\"use_password_migration_hook\": true} The password_migration hook is\n  configured After calling the password_migration hook, the HTTP status code\n  will be inspected: On 200, we parse the response as JSON and look for\n  {\"status\": \"password_match\"}. The password credential config will be replaced\n  with the hash of the actual password. On any other status code, we assume that\n  the password is not valid.\n\n- **sdk:** Add missing profile discriminator to update registration\n  ([0150795](https://github.com/ory/kratos/commit/0150795d902dcc7cfb2298c3b5a98da1c2541e46))\n- **sdk:** Avoid eval with javascript triggers\n  ([dd6e53d](https://github.com/ory/kratos/commit/dd6e53d62f343a317edf403218b20599539218c6)):\n\n  Using `OnLoadTrigger` and `OnClickTrigger` one can now map the trigger to the\n  corresponding JavaScript function.\n\n  For example, trigger `{\"on_click_trigger\":\"oryWebAuthnRegistration\"}` should\n  be translated to `window.oryWebAuthnRegistration()`:\n\n  ```\n  if (attrs.onClickTrigger) {\n    window[attrs.onClickTrigger]()\n  }\n  ```\n\n- Separate 2fa refresh from 1st factor refresh\n  ([#3961](https://github.com/ory/kratos/issues/3961))\n  ([89355d8](https://github.com/ory/kratos/commit/89355d86258ace19c03fcb38dd3861f88e28af59))\n- Set maxlength for totp input\n  ([51042d9](https://github.com/ory/kratos/commit/51042d99fab301f0bb44665e56c5a2364e7d8866))\n\n### Tests\n\n- Add form hydration tests for code login\n  ([37781a9](https://github.com/ory/kratos/commit/37781a93dda9b8f0127217a6b0ac2434dda1cc58))\n- Add form hydration tests for idfirst login\n  ([633b0ba](https://github.com/ory/kratos/commit/633b0ba7f724374f4c02128a5b0f748bd2e9413e))\n- Add form hydration tests for oidc login\n  ([df0cdcb](https://github.com/ory/kratos/commit/df0cdcb424cae6c49143ef2ef2d0b2c95f14fffb))\n- Add form hydration tests for passkey login\n  ([a777854](https://github.com/ory/kratos/commit/a777854e8d99336ab8f5755fdbc9d257e5edd1c0))\n- Add form hydration tests for password login\n  ([7186e7e](https://github.com/ory/kratos/commit/7186e7e060e04a4918e22e0b03fefbf4eb9f4a4b))\n- Add form hydration tests for webauthn login\n  ([8b68163](https://github.com/ory/kratos/commit/8b68163a3f293f7dceb58397f0ef555f1d8fd7c3))\n- Add tests for idfirst\n  ([5f76c15](https://github.com/ory/kratos/commit/5f76c1565e89bfb99f23c3f0f3a9beadbdfa270c))\n- Additional code credential test case\n  ([#4122](https://github.com/ory/kratos/issues/4122))\n  ([4f2c854](https://github.com/ory/kratos/commit/4f2c8542ab04b88c7112d7b564d91bcfd8f5791a))\n- Deflake and parallelize persister tests\n  ([#3953](https://github.com/ory/kratos/issues/3953))\n  ([61f87d9](https://github.com/ory/kratos/commit/61f87d90bd67e5bb1f00ee110d986e4f72fc4c91))\n- Deflake session extend config side-effect\n  ([#3950](https://github.com/ory/kratos/issues/3950))\n  ([b192c92](https://github.com/ory/kratos/commit/b192c92d6c969d470d6479bc33dbc351d327c1f9))\n- Enable server-side config from context\n  ([#3954](https://github.com/ory/kratos/issues/3954))\n  ([e0001b0](https://github.com/ory/kratos/commit/e0001b0db784457652581366bd7ead7cdf6b3898))\n- Improve stability of refresh test\n  ([#4037](https://github.com/ory/kratos/issues/4037))\n  ([68693a4](https://github.com/ory/kratos/commit/68693a43e4e1e3028f17789e72d0b79f6298d139))\n- Resolve CI failures ([#4067](https://github.com/ory/kratos/issues/4067))\n  ([dbf7274](https://github.com/ory/kratos/commit/dbf7274f7a4be56c33b06559875c42725bf4a351))\n- Resolve issues and update snapshots for all selfservice strategies\n  ([e2e81ac](https://github.com/ory/kratos/commit/e2e81ac16726b180d33c57913e3cac099daf946b))\n- Update incorrect usage of Auth0 in Salesforce tests\n  ([#4007](https://github.com/ory/kratos/issues/4007))\n  ([6ce3068](https://github.com/ory/kratos/commit/6ce306824cec81890c50dcf23c2b8a5825f20a10))\n- Verify redirect continue_with in hook executor for browser clients\n  ([7b0b94d](https://github.com/ory/kratos/commit/7b0b94d30ec9069de6978427814d55a30e62adb8))\n\n### Unclassified\n\n- Merge commit from fork\n  ([123e807](https://github.com/ory/kratos/commit/123e80782b392095631ee2e0d1bd6ec337c1fb79)):\n  - fix(security): code credential does not respect `highest_available` setting\n\n  This patch fixes a security vulnerability which prevents the `code` method to\n  properly report it's credentials count to the `highest_available` mechanism.\n\n  For more details on this issue please refer to the\n  [security advisory](https://github.com/ory/kratos/security/advisories/GHSA-wc43-73w7-x2f5).\n  - fix: normalize code credentials and deprecate via parameter\n\n  Before this, code credentials for passwordless and mfa login were incorrectly\n  stored and normalized. This could cause issues where the system would not\n  detect the user's phone number, and where SMS/email MFA would not properly\n  work with the `highest_available` setting.\n\n- Update .github/workflows/ci.yaml\n  ([2d60772](https://github.com/ory/kratos/commit/2d60772062a684c3a27f28b8836c3548f5b8cea9))\n- Update Code QL action to v2\n  ([#4008](https://github.com/ory/kratos/issues/4008))\n  ([e3f1da0](https://github.com/ory/kratos/commit/e3f1da0f4bf41a8a8733758fcd9edb9910c55cfa))\n\n# [1.2.0](https://github.com/ory/kratos/compare/v1.1.0...v1.2.0) (2024-06-05)\n\nOry Kratos v1.2 is the most complete, scalable, and secure open-source identity\nserver available. We are thrilled to announce its release!\n\n![Ory Kratos 1.2 released](https://www.ory.sh/images/newsletter/kratos-1.2.0/banner.png)\n\nThis release introduces two major features: two-step registration and full\nPassKey with resident key support.\n\nPasskeys provide a secure and convenient authentication method, eliminating the\nneed for passwords while ensuring strong security. With this release, we have\nadded support for resident keys, enabling offline authentication. Credential\ndiscovery allows users to link existing passkeys to their Ory account\nseamlessly.\n\n[Watch the PassKey demo video](https://github.com/aeneasr/web-next-deprecated/assets/3372410/e676c518-c82a-42a6-821e-28aecadb270c)\n\nTwo-step registration improves the user experience by dividing the registration\nprocess into two steps. Users first enter their identity traits, and then choose\na credential method for authentication, resulting in a streamlined process. This\nfeature is especially useful when enabling multiple authentication strategies,\nas it eliminates the need to repeat identity traits for each strategy.\n\n![Two-Step Registration](https://ik.imagekit.io/launchnotes/production/tr:w-1640,c-at_max,f-auto/ngul9dzfjdt3pe8benegjjeeagi1)\n\nThe 107 commits since v1.1 include several improvements:\n\n- **Webhooks** now carry session information if available.\n- **Transient Payloads** are now available across all self-service flows.\n- **Sign in with Twitter** is now available.\n- **Sign in with LinkedIn** now includes an additional v2 provider compatible\n  with LinkedIn's new SSO API.\n- **Two-Step Registration**: An improved registration experience that separates\n  entering profile information from choosing authentication methods.\n- **User Credentials Meta-Information** can now be included on the list\n  endpoint.\n- **Social Sign-In** is now resilient to double-submit issues common with\n  Facebook and Apple mobile login.\n\n**Two-Step Registration Enabled by Default**: This is now the default setting.\nTo disable, set `selfservice.flows.registration.enable_legacy_flow` to `true`.\n\n- Improved account linking and credential discovery during sign-up.\n- The `return_to` parameter is now respected in OIDC API flows.\n- Adjustments to database indices.\n- Enhanced error messages for security violations.\n- Improved SDK types.\n- The `verification` and `verification_ui` hooks are now available in the login\n  flow.\n- Webhooks now contain the correct identity state in the after-verification hook\n  chain.\n\nWe are doing this survey to find out how we can support self-hosted Ory users\nbetter. We strive to provide you with the best product and service possible and\nyour feedback will help us understand what we're doing well and where we can\nimprove to better meet your needs. We truly value your opinion and thank you in\nadvance for taking the time to share your thoughts with us!\n\nFill out the\n[survey now](https://share-eu1.hsforms.com/15DiCnJpcRuijnpAdnDhxxwextgn)!\n\n## Breaking Changes\n\nThis feature enables two-step registration per default. Two-step registration is\na significantly improved sign up flow and recommended when using more than one\nsign up methods. To disable two-step registration, set\n`selfservice.flows.registration.enable_legacy_flow` to `true`. This value\ndefaults to `false`.\n\n### Bug Fixes\n\n- Add login succeeded event to post registration hook\n  ([#3739](https://github.com/ory/kratos/issues/3739))\n  ([b685fa5](https://github.com/ory/kratos/commit/b685fa5477be2ba099fd2420b27b2411fafc7e51))\n- Add missing env vars to set up guide\n  ([#3855](https://github.com/ory/kratos/issues/3855))\n  ([da90502](https://github.com/ory/kratos/commit/da90502dc3bf8e3d34fb4ecc531834b1919989ad)):\n\n  Closes https://github.com/ory/kratos/issues/3828\n\n- Add missing indexes and remove unused index\n  ([6d7372e](https://github.com/ory/kratos/commit/6d7372ee3d88ee4fc552b969dd0ff338dcc0544c))\n- Add missing indexes and remove unused index\n  ([#3756](https://github.com/ory/kratos/issues/3756))\n  ([c905f02](https://github.com/ory/kratos/commit/c905f02473c5d77ab309a45f10251b1ba7e88584))\n- Add sms mfa via parameter to spec\n  ([#3766](https://github.com/ory/kratos/issues/3766))\n  ([b291c95](https://github.com/ory/kratos/commit/b291c959c18c72f5edc55607ab23b4592faf8d53))\n- Allow updating just the verified_at timestamp of addresses\n  ([#3880](https://github.com/ory/kratos/issues/3880))\n  ([696cc1b](https://github.com/ory/kratos/commit/696cc1b59b18627fec63915070f4d8c5b3e3250d))\n- Always issue session last ([#3876](https://github.com/ory/kratos/issues/3876))\n  ([e942507](https://github.com/ory/kratos/commit/e94250705e999567e2ed58cebdb3f6a9d589e3ef)):\n\n  In post persist hooks, the session issuance hook always needs to come last.\n  This fixes the getHooks function to ensure this.\n\n- Audit issues ([#3797](https://github.com/ory/kratos/issues/3797))\n  ([7017490](https://github.com/ory/kratos/commit/7017490caa9c70e22d5c626773c0266521813ff5))\n- Change return urls in quickstarts\n  ([#3928](https://github.com/ory/kratos/issues/3928))\n  ([9730e09](https://github.com/ory/kratos/commit/9730e099a656d211389d8e993c64d8082784c929))\n- Close res body ([#3870](https://github.com/ory/kratos/issues/3870))\n  ([cc39f8d](https://github.com/ory/kratos/commit/cc39f8df7c235af0df616432bc4f88681896ad85))\n- CVEs in dependencies ([#3902](https://github.com/ory/kratos/issues/3902))\n  ([e5d3b0a](https://github.com/ory/kratos/commit/e5d3b0afde3c80c6c9cf8815c56d82e291ede663))\n- Db index and duplicate credentials error\n  ([#3896](https://github.com/ory/kratos/issues/3896))\n  ([9f34a21](https://github.com/ory/kratos/commit/9f34a21ea2035a5d33edd96753023a3c8c6c054c)):\n  - fix: don't return password cred type if empty\n  - fix: better index for config.user_handle on identity_credentials\n\n- Do not require method to be passkey in settings schema\n  ([#3862](https://github.com/ory/kratos/issues/3862))\n  ([660f330](https://github.com/ory/kratos/commit/660f330ab69ef0e6fd21501fbc9dfed693d4a715))\n- Don't require connection_uri in SMTP\n  ([#3861](https://github.com/ory/kratos/issues/3861))\n  ([800f8f1](https://github.com/ory/kratos/commit/800f8f1036ef46a561d24dcdec45dd48803978d7))\n- Don't treat passkeys as AAL2\n  ([#3853](https://github.com/ory/kratos/issues/3853))\n  ([8eee972](https://github.com/ory/kratos/commit/8eee972d89accb02b3caa053fca2f16ed2c876f1))\n- Drop index if exists ([#3846](https://github.com/ory/kratos/issues/3846))\n  ([ad0619d](https://github.com/ory/kratos/commit/ad0619d803cd2842a67c56a545ec5ab252501b0f))\n- Drop trigram index on identifiers\n  ([#3827](https://github.com/ory/kratos/issues/3827))\n  ([8f8fd90](https://github.com/ory/kratos/commit/8f8fd90304886ecd689a85fc60c4712e47526cdd))\n- Enum type of session expandables\n  ([#3891](https://github.com/ory/kratos/issues/3891))\n  ([63d785e](https://github.com/ory/kratos/commit/63d785e5e73ff067ec804ecc2107fac1525d3688))\n- Enum type of session expandables\n  ([#3895](https://github.com/ory/kratos/issues/3895))\n  ([c435727](https://github.com/ory/kratos/commit/c435727c1e3c70c040b7fc7648ce621b136e5fc2))\n- Execute verification & verification_ui properly in login flows\n  ([#3847](https://github.com/ory/kratos/issues/3847))\n  ([5aad1c1](https://github.com/ory/kratos/commit/5aad1c1e6cc92f72af56511dacb9812edb600813))\n- Ignore decrypt errors in WithDeclassifiedCredentials\n  ([#3731](https://github.com/ory/kratos/issues/3731))\n  ([8f5192f](https://github.com/ory/kratos/commit/8f5192fbb74c4b952029a6856284de8d59027770))\n- Improve SDK discriminators\n  ([#3844](https://github.com/ory/kratos/issues/3844))\n  ([c08b3ad](https://github.com/ory/kratos/commit/c08b3ad76c5adb712c945cdbd92a9a51832e94b9))\n- Include all creds in duplicate credential err\n  ([#3881](https://github.com/ory/kratos/issues/3881))\n  ([e06c241](https://github.com/ory/kratos/commit/e06c241ffe3f0e696bb1cbc1d1080f9d4e09fbd2))\n- Linkedin issuer override ([#3875](https://github.com/ory/kratos/issues/3875))\n  ([11d221a](https://github.com/ory/kratos/commit/11d221a4d33878930ca7025ae1b5c18b25dd1add))\n- Make sure emails can still be sent with SMS enabled\n  ([#3795](https://github.com/ory/kratos/issues/3795))\n  ([7c68c5a](https://github.com/ory/kratos/commit/7c68c5aa69ed76a84a37a37a3555277ddc772cf8))\n- Missing indices and foreign keys\n  ([#3800](https://github.com/ory/kratos/issues/3800))\n  ([0b32ce1](https://github.com/ory/kratos/commit/0b32ce113be47aa724d3468062ced09f8f60c52a))\n- **oidc:** Grace period for continuity container on oidc callbacks\n  ([#3915](https://github.com/ory/kratos/issues/3915))\n  ([1a9a096](https://github.com/ory/kratos/commit/1a9a096d619925dd3718ad9dd9daf77387572ece))\n- Passing transient payloads\n  ([#3838](https://github.com/ory/kratos/issues/3838))\n  ([d01b670](https://github.com/ory/kratos/commit/d01b6705bf36efb6e0f3d71ed22d0574ab8a98a4))\n- Prevent SMTP URL leak on unparsable URL\n  ([#3770](https://github.com/ory/kratos/issues/3770))\n  ([c5f39f4](https://github.com/ory/kratos/commit/c5f39f4bc481e400f736ede7f8f0be546a55eebf))\n- Respect return_to in OIDC API flow error case\n  ([#3893](https://github.com/ory/kratos/issues/3893))\n  ([e8f1bcb](https://github.com/ory/kratos/commit/e8f1bcb1342af994b8e08282aa4066ee00ffe7d4)):\n  - fix: respect return_to in OIDC API flow error case\n\n  This fix ensures that we redirect the user to the return_to URL when an error\n  occurs during the OIDC login for native flows.\n\n  Native flows are initialized through the API, and the browser URL is retrieved\n  from a 422 response after a POST to submit the login flow. Successful OIDC\n  flows already returned the `code` to the `return_to` URL. Now, unsuccessful\n  flows return the `flow` with the current flow ID (which might have changed),\n  so that the caller can retrieve the full flow and act accordingly.\n  - fix: ignore trivvy CVE report\n\n  Bump in distroless is still open\n\n- **sdk:** Expand identity in session extension\n  ([#3843](https://github.com/ory/kratos/issues/3843))\n  ([04f0231](https://github.com/ory/kratos/commit/04f02318d4de5290cbf100e9b301284d5ee40fe7)),\n  closes [#3842](https://github.com/ory/kratos/issues/3842)\n- **sdk:** Improve discriminators for node and Go\n  ([#3821](https://github.com/ory/kratos/issues/3821))\n  ([9ddf7cc](https://github.com/ory/kratos/commit/9ddf7cc7c52313c4ee13ccdc2886ad94b5d1317f))\n- Show error page on identity mismatch\n  ([#3790](https://github.com/ory/kratos/issues/3790))\n  ([e6db689](https://github.com/ory/kratos/commit/e6db689e0de41067e6e78889c3dab9637a96236e))\n- Test assertions on declassifying OIDC tokens\n  ([#3773](https://github.com/ory/kratos/issues/3773))\n  ([7f8a7f1](https://github.com/ory/kratos/commit/7f8a7f142a91c8c74f32eadb41224fc4f69c2109))\n- Tolerate more \"truthy\" values when creating new flows\n  ([#3841](https://github.com/ory/kratos/issues/3841))\n  ([49d93c0](https://github.com/ory/kratos/commit/49d93c0e3383f602fe6be3c7bf749b54f344aa72)),\n  closes [#3839](https://github.com/ory/kratos/issues/3839):\n\n  Use strconv.ParseBool to accept multiple \"truthy\" values for the `refresh` and\n  `return_session_token_exchange_code` query parameters when creating a new\n  login flow.\n\n  For some SDKs (e.g.: Python), these stringification of booleans is not\n  user-controlled and these endpoints could not be used fully due to the backend\n  ignoring any value other than `true` (all lowercase).\n\n- Tweaks to UpsertSessions ([#3878](https://github.com/ory/kratos/issues/3878))\n  ([da51dcd](https://github.com/ory/kratos/commit/da51dcdb8c82a5dbd290ab2f48ad74a1c6dd18f0))\n- Use correct post-verification identity state in post-hooks\n  ([#3863](https://github.com/ory/kratos/issues/3863))\n  ([6e63d06](https://github.com/ory/kratos/commit/6e63d06db1cd1ab62f8a2d0b202ec74572420204))\n- Webhook transient payload in OIDC login flows\n  ([#3857](https://github.com/ory/kratos/issues/3857))\n  ([2cdfc70](https://github.com/ory/kratos/commit/2cdfc70c726a166790b98d419895f0396d13176f)):\n  - fix: transient payload with OIDC login\n\n### Code Generation\n\n- Pin v1.2.0 release commit\n  ([1a70648](https://github.com/ory/kratos/commit/1a70648c4d5b9b8d135dd7bea3842057e67b574e))\n\n### Documentation\n\n- Remove delete reference from batch patch identity\n  ([#3906](https://github.com/ory/kratos/issues/3906))\n  ([cd01cb9](https://github.com/ory/kratos/commit/cd01cb9fb23a24e52d46538a9ea63c2144c3b145))\n\n### Features\n\n- Add `include_credential` query param to `/admin/identities` list call\n  ([#3343](https://github.com/ory/kratos/issues/3343))\n  ([d94530a](https://github.com/ory/kratos/commit/d94530a716358895b01b65babd77226fab69f494))\n- Add headers to web hooks ([#3849](https://github.com/ory/kratos/issues/3849))\n  ([4642de0](https://github.com/ory/kratos/commit/4642de0cfd1fb15bc48c7093be9449abd488755c))\n- Add session to post login webhook\n  ([#3877](https://github.com/ory/kratos/issues/3877))\n  ([386078e](https://github.com/ory/kratos/commit/386078e0b5c74c54ce2c7dc6fd12fd865817b87a))\n- Add transient payloads to all flows\n  ([#3738](https://github.com/ory/kratos/issues/3738))\n  ([b8b747b](https://github.com/ory/kratos/commit/b8b747b2adc59c8cf938a0ee30accdb4135634b8))\n- Add twitter SSO ([#3778](https://github.com/ory/kratos/issues/3778))\n  ([930fb19](https://github.com/ory/kratos/commit/930fb19842e527e5e9c415efa983b36e02829516))\n- Add verification hook to login flow\n  ([#3829](https://github.com/ory/kratos/issues/3829))\n  ([43e4ead](https://github.com/ory/kratos/commit/43e4eadce7fa6e66bf1f9c03136d141bffd3094f))\n- Allow admin to create API code recovery flows\n  ([#3939](https://github.com/ory/kratos/issues/3939))\n  ([25d1ecd](https://github.com/ory/kratos/commit/25d1ecd90317193095e01b97ff21d92920035b02))\n- Control edge cache ttl ([#3808](https://github.com/ory/kratos/issues/3808))\n  ([c9dcce5](https://github.com/ory/kratos/commit/c9dcce5a41137937df1aad7ac81170b443740f88))\n- Linkedin v2 provider ([#3804](https://github.com/ory/kratos/issues/3804))\n  ([a6ad983](https://github.com/ory/kratos/commit/a6ad983ac83aa3ea65c4dc0c46b582096574c25a)):\n  - feat: add linkedin-v2 provider\n  - docs: document linkedin special-case\n\n- PassKeys with Resident Keys and two-step registration\n  ([#3748](https://github.com/ory/kratos/issues/3748))\n  ([3621411](https://github.com/ory/kratos/commit/3621411dc4386d841bc6766a5ab8d03e65812073))\n- Send OIDC claim keys to tracing\n  ([#3798](https://github.com/ory/kratos/issues/3798))\n  ([04390be](https://github.com/ory/kratos/commit/04390bee426befe51af2ee8177afabaa9ce4fa80))\n- Use authenticate endpoint for x\n  ([#3833](https://github.com/ory/kratos/issues/3833))\n  ([3d9ba5d](https://github.com/ory/kratos/commit/3d9ba5df85e0d0c4d8002365987e536b37678104)):\n\n  Improves the \"Log in with X\" experience by not asking the user to\n  re-authenticate every time.\n\n### Tests\n\n- Deflake session test ([#3864](https://github.com/ory/kratos/issues/3864))\n  ([6b275f3](https://github.com/ory/kratos/commit/6b275f35a0732ffb723d47df5b6afbdc06eaf71f))\n- Resolve failing test for empty tokens\n  ([#3775](https://github.com/ory/kratos/issues/3775))\n  ([7277368](https://github.com/ory/kratos/commit/7277368bc28df8f0badffc7e739cef20f05e9a02))\n- Resolve flaky e2e tests ([#3935](https://github.com/ory/kratos/issues/3935))\n  ([a14927d](https://github.com/ory/kratos/commit/a14927dfa5f8d0fbda7e5a831f0a09a42369e06c)):\n  - test: resolve flaky code registration tests\n  - chore: don't fail logout if cookie is not found\n  - chore: remove .only\n  - chore: reduce wait\n  - chore: u\n  - chore: u\n  - chore: u\n\n### Unclassified\n\n- Remove unnecessary COPY command from Dockerfile (#3771)\n  ([087748c](https://github.com/ory/kratos/commit/087748c0651ff0fc93259f7ab6b10668c09f5eba)),\n  closes [#3771](https://github.com/ory/kratos/issues/3771)\n\n# [1.1.0](https://github.com/ory/kratos/compare/v1.0.0...v1.1.0) (2024-02-20)\n\n![Ory Kratos v1.1.0](https://www.ory.sh/images/newsletter/kratos-1.1.0/banner.png)\n\nOry Kratos v1.1 is the most complete, most scalable, and most secure open-source\nidentity server on the planet, and we are thrilled to announce its release! This\nrelease comes with over 270 commits and an incredible amount of new features and\ncapabilities!\n\n- **Phone Verification & 2FA with SMS**: Enhance convenient security with phone\n  verification and two-factor authentication (2FA) via SMS, integrating easily\n  with SMS gateways like Twilio. This feature not only adds a convenient layer\n  of security but also offers a straightforward method for user verification,\n  increasing your trust in user accounts.\n- **Translations & Internationalization**: Ory Kratos now supports multiple\n  languages, making it accessible to a global audience. This improvement\n  enhances the user experience by providing a localized interface, ensuring\n  users interact with the system in their preferred language.\n- **Native Support for Sign in with Google and Apple on Android/iOS**: Get more\n  sign-ups with native support for \"Sign in with Google\" and \"Sign in with\n  Apple\" on mobile platforms. Great user experience matters!\n- **Account Linking**: Simplify user management with new features that\n  facilitate account linking. If a user registers with a password and later\n  signs in with a social account sharing the same email, new screens make\n  account linking straightforward, enhancing user convenience and reducing\n  support inquiries.\n- **Passwordless \"Magic Code\"**: Introduce a passwordless login method with\n  \"Magic Code,\" which sends a one-time code to the user's email for sign-up and\n  login. This method can also serve as a fallback when users forget their\n  password or their social login is unavailable, streamlining the login process\n  and improving user accessibility.\n- **Session to JWT Conversion**: Convert an Ory Session Cookie or Ory Session\n  Token into a JSON Web Token (JWT), providing more flexibility in handling\n  sessions and integrating with other systems. This feature allows for seamless\n  authentication and authorization processes across different platforms and\n  services.\n\n**Note:** To ensure a seamless upgrade experience with minimal impact, some of\nthese features are gated behind the `feature_flags` config parameter, allowing\ncontrolled deployment and testing.\n\nThe following features have been shipped exclusively to Ory Network for this\nversion:\n\n- **[B2B SSO](https://www.ory.sh/docs/kratos/organizations)** allows your\n  customers to connect their LDAP / Okta / AD / … to your login. Ory selects the\n  correct login provider based on the user’s email domain.\n- [\\*\\*Significantly better API performance](https://www.ory.sh/docs/api/eventual-consistency)\\*\\*\n  for expensive API operations by specifying the desired consistency\n  (`strong`, `eventual`).\n- **Finding users effortlessly** with our new fuzzy search for credential\n  identifiers available for\n  the [Identity List API](https://www.ory.sh/docs/kratos/reference/api#tag/identity/operation/listIdentities).\n\n- Better reliability when sending out emails across different providers.\n- Streamlining the HTTP API and improving related SDK methods.\n- Better performance when calling the whoami API endpoint, updating identities,\n  and listing identities.\n- The performance of listing identities has significantly improved with the\n  introduction of keyset pagination. Page pagination is still available but will\n  be fully deprecated soon.\n- Ability to list multiple identities in a batch call.\n- Passkeys and WebAuthn now support multiple origins, useful when working with\n  subdomains.\n- The logout flow now redirects the user back to the `return_to` parameter set\n  in the API call.\n- When updating their settings, the user was sometimes incorrectly asked to\n  confirm the changes by providing their password. This issue has now been\n  fixed.\n- When signing up with an account that already exists, the user will be shown a\n  hint helping them sign in to their existing account.\n- CORS configuration can now be hot-reloaded.\n- The integration with Ory OAuth2 / Ory Hydra has improved for logout, login\n  session management, verification, and recovery flows.\n- A new passwordless method has been added: \"Magic code\". It sends a one-time\n  code to the user's email during sign-up and log-in. This method can\n  additionally be used as a fallback login method when the user forgets their\n  password.\n- Integration with social sign-in has improved, and it is now possible to use\n  the email verified status from the social sign-in provider.\n- Ory Elements and the default Ory Account Experience are now internationalized\n  with translations.\n- It is now possible to convert an Ory Session Cookie or Ory Session Token into\n  a JSON Web Token.\n- Recovery on native apps has improved significantly and no longer requires the\n  user to switch to a browser for the recovery step.\n- Administrators can now find users by their identifiers with fuzzy search -\n  this feature is still in preview.\n- Importing HMAC-hashed passwords is now possible.\n- Webhooks can now update identity admin metadata.\n- New screens have been added to make account linking possible when a user has\n  registered with a password and later tries signing in with a social account\n  sharing the same email.\n- Ability to revoke all sessions of a user when they change their password.\n- Webhooks are now available for all login, registration, and login methods,\n  including Passkeys, TOTP, and others.\n- The login screen now longer shows “ID” for the primary identifier, but instead\n  extracts the correct label - for example, “Email” or “Username” from the\n  Identity Schema.\n- Login hints help users with guidance when they are unable to sign in (wrong\n  social sign-in provider) but have an active account.\n- Phone numbers can now be verified via an SMS gateway like Twilio.\n- SMS OTP is now a two-factor option. Ory Kratos 1.1 is a major release that\n  marks a significant milestone in our journey.\n\nWe sincerely hope that you find these new features and improvements in Ory\nKratos 1.1 valuable for your projects. To experience the power of the latest\nrelease, we encourage you to get the latest version of Ory\nKratos [here](https://github.com/ory/kratos) or leverage Ory Kratos\nin [Ory Network](https://www.ory.sh/network/) — the easiest, simplest, and most\ncost-effective way to run Ory.\n\nFor organizations seeking to upgrade their self-hosted solution, **Ory offers\nenterprise support services to ensure a smooth transition**. Our team is ready\nto assist you throughout the migration process, ensuring uninterrupted access to\nthe latest features and improvements. Additionally, we provide\nvarious [support plans](https://www.ory.sh/support/) specifically tailored for\nself-hosting organizations. These plans offer comprehensive assistance and\nguidance to optimize your Ory deployments and meet your unique requirements. We\nextend our heartfelt gratitude to the vibrant and supportive Ory Community.\nWithout your constant support, feedback, and contributions, reaching this\nsignificant milestone would not have been possible. As we continue on this\njourney, your feedback and suggestions are invaluable to us. Together, we are\nshaping the future of identity management and authentication in the digital\nlandscape.\n\nContributors to this release in no particular\norder: [moose115](https://github.com/ory/kratos/commits?author=moose115), [K3das](https://github.com/ory/kratos/commits?author=K3das), [sidartha](https://github.com/ory/kratos/commits?author=sidartha), [efesler](https://github.com/ory/kratos/commits?author=efesler), [BrandonNoad](https://github.com/ory/kratos/commits?author=BrandonNoad) ,[Saancreed](https://github.com/ory/kratos/commits?author=Saancreed), [jpogorzelski](https://github.com/ory/kratos/commits?author=jpogorzelski), [dreksx](https://github.com/ory/kratos/commits?author=dreksx), [martinloesethjensen](https://github.com/ory/kratos/commits?author=martinloesethjensen), [cpoyatos1](https://github.com/ory/kratos/commits?author=cpoyatos1), [misamu](https://github.com/ory/kratos/commits?author=misamu), [tristankenney](https://github.com/ory/kratos/commits?author=tristankenney), [nxy7](https://github.com/ory/kratos/commits?author=nxy7), [anhnmt](https://github.com/ory/kratos/commits?author=anhnmt)\n\nAre you passionate about security and want to make a meaningful impact in one of\nthe biggest open-source communities? Join\nthe [Ory community](https://slack.ory.sh/) and become a part of the new ID\nstack. Together, we are building the next generation of IAM solutions that\nempower organizations and individuals to secure their identities effectively.\nWant to check out Ory Kratos yourself? Use these commands to get your Ory Kratos\nproject running on the Ory Network:\n\n```\nbrew install ory/tap/cli\n\nscoop bucket add ory <https://github.com/ory/scoop.git>\nscoop install ory\n\nbash <(curl <https://raw.githubusercontent.com/ory/meta/master/install.sh>) -b . ory\nsudo mv ./ory /usr/local/bin/\n\nory auth login\n\nory create project --name \"My first Kratos project\"\n\nory open account-experience registration\n\nory patch identity-config \\\n  --replace '/identity/default_schema_id=\"preset://username\"' \\\n  --replace '/identity/schemas=[{\"id\":\"preset://username\",\"url\":\"preset://username\"}]' \\\n  --format yaml\n\nory open account-experience registration\n```\n\n## Breaking Changes\n\nPagination parameters for the `list identities` CLI command have changed from\narguments to flags `--page-token` and `page-size`:\n\n```\n- kratos list identities 1 100\n+ kratos list identities --page-size 100 --page-token ...\n```\n\nFurthermore, the JSON / JSON pretty output of `list identities` has changed:\n\n```patch\n-[\n-  { \"id\": \"...\" },\n-  { /* ... */ },\n-  // ...\n-]\n+{\n+  \"identities\": [\n+    {\"id\": \"...\"},\n+    { /* ... */ },\n+    // ...\n+  ],\n+  \"next_page_token\": \"...\"\n+}\n```\n\nCloses https://github.com/ory/sdk/issues/284 Closes\nhttps://github.com/ory/kratos/pull/3480\n\n### Bug Fixes\n\n- `oidc` does not require a method in the payload\n  ([#3564](https://github.com/ory/kratos/issues/3564))\n  ([b299abc](https://github.com/ory/kratos/commit/b299abcfa1ebdb8bbb6bb9339f61873d5c77c44f)):\n  - fix: `oidc` does not require a method in the payload\n  - refactor: only update strategies order in test\n  - chore: update audit messages and comments\n\n- Accept all 200 responses as OK in courier\n  ([#3401](https://github.com/ory/kratos/issues/3401))\n  ([88237e2](https://github.com/ory/kratos/commit/88237e25b080a9643f6cbf7eedbf23988ba9ba7c)),\n  closes [#3399](https://github.com/ory/kratos/issues/3399):\n  - fix: accept all 200 responses as OK in courier\n\n- Accept login_challenge after verification\n  ([#3427](https://github.com/ory/kratos/issues/3427))\n  ([6b02350](https://github.com/ory/kratos/commit/6b02350c21aa65decd1bb16e559e1cc7dae42d55)):\n\n  Part of https://github.com/ory/network/issues/320\n\n- Add caching to Jsonnet snippet during session JWT tokenization\n  ([#3699](https://github.com/ory/kratos/issues/3699))\n  ([1da8180](https://github.com/ory/kratos/commit/1da818072154baa5c0921134919afde595031e94))\n- Add consistency flag ([#3733](https://github.com/ory/kratos/issues/3733))\n  ([fd79950](https://github.com/ory/kratos/commit/fd7995077307cc101550eda5d7724ea1f68fa98a))\n- Add max-age to default cors headers\n  ([#3584](https://github.com/ory/kratos/issues/3584))\n  ([c5b4aaa](https://github.com/ory/kratos/commit/c5b4aaa2df5d010b62a99ccf45850583daad3a66))\n- Add missing tracing & attributes in oidc strategy\n  ([#3429](https://github.com/ory/kratos/issues/3429))\n  ([09bcb71](https://github.com/ory/kratos/commit/09bcb71f1f0b3238e2d0f4376a1a2290d062c6c1))\n- Add return_to parameter to API spec of createRecoveryLinkForIdentity\n  ([#3711](https://github.com/ory/kratos/issues/3711))\n  ([757a5e4](https://github.com/ory/kratos/commit/757a5e43257e9ff28a16bfe76f8e737b656d3696))\n- Add value code to authentication method enum\n  ([#3546](https://github.com/ory/kratos/issues/3546))\n  ([95dc7a2](https://github.com/ory/kratos/commit/95dc7a20f49aa682f324b70e507ec56c20159ebb)):\n  - fix: add value code to authentication method enum\n  - chore: generate sdk\n\n- Additional_id_token_audiences key in config schema\n  ([#3622](https://github.com/ory/kratos/issues/3622))\n  ([9396bb0](https://github.com/ory/kratos/commit/9396bb0b586d1d1e74a85c0ae3bcf9de81214f1b))\n- Adjust tracing verbosity\n  ([976cd0d](https://github.com/ory/kratos/commit/976cd0dc3dd95c2c1992bfa82394e9fad39f34f2))\n- Allow post recovery hooks to interrupt the flow\n  ([#3393](https://github.com/ory/kratos/issues/3393))\n  ([6c1d2f1](https://github.com/ory/kratos/commit/6c1d2f1e4173cfb9a7abe2bfe4f20e47b7568d3b))\n- Allow updating admin metadata from webhook responses\n  ([#3569](https://github.com/ory/kratos/issues/3569))\n  ([22f61f0](https://github.com/ory/kratos/commit/22f61f015495c55e58db4f31ee6882444b9a3caf))\n- Always return relative URLs in the Link header for pagination\n  ([fb229c9](https://github.com/ory/kratos/commit/fb229c982c6f7d7a4f5f0f84ffc971a576906160))\n- Auto migrate old accounts to use code credential\n  ([#3581](https://github.com/ory/kratos/issues/3581))\n  ([569b14a](https://github.com/ory/kratos/commit/569b14aba864761236bd3d5a48e4e69f10ea6c86))\n- Carry `oauth2_login_challenge` over to registration flow\n  ([#3419](https://github.com/ory/kratos/issues/3419))\n  ([76241be](https://github.com/ory/kratos/commit/76241bee3dc7fec4690346ee85bc4b9f897fdd34)):\n\n  Fixes https://github.com/ory/kratos/issues/3321\n\n- Change ListIdentities to keyset pagination\n  ([e16fed1](https://github.com/ory/kratos/commit/e16fed1f8563509aac30886386668bb85e6dc797))\n- Change shebangs and makefile from /bin/bash to /usr/bin/env bash\n  ([#3597](https://github.com/ory/kratos/issues/3597))\n  ([1343bbb](https://github.com/ory/kratos/commit/1343bbbfa11ff3e7fcbc0f233b858d13fd40c66d)):\n  - makefile fix\n  - shebangs changed to /usr/bin/env bash\n\n  Signed-off-by: nxy7 <lolnoxy@gmail.com>\n\n- Check whoami aal before accepting hydra login request\n  ([#3669](https://github.com/ory/kratos/issues/3669))\n  ([a2f79c3](https://github.com/ory/kratos/commit/a2f79c31f3208b88024897fc8bf1307ccac6f895))\n- Code method on registration and 2fa\n  ([#3481](https://github.com/ory/kratos/issues/3481))\n  ([7aa2e29](https://github.com/ory/kratos/commit/7aa2e293175d0f4b6c13552cc3781f54f8caf3a0))\n- Consider OIDC registration flows errored with duplicate credential to be\n  completed by strategy ([#3525](https://github.com/ory/kratos/issues/3525))\n  ([3e3c789](https://github.com/ory/kratos/commit/3e3c78967523676cbce9a227d574c2f7f4ea314d)):\n\n  Returning anything else here may cause Kratos to respond with two concatenated\n  JSON objects: new login flow with actual error message as the first one and a\n  very confusing '500, aborted registration hook execution' as the second one.\n\n- Csrf token regenerate on browser flows\n  ([#3706](https://github.com/ory/kratos/issues/3706))\n  ([e4908db](https://github.com/ory/kratos/commit/e4908dbe4a42fad5a80c4d46004e1e3710cabeb7)),\n  closes [#3705](https://github.com/ory/kratos/issues/3705)\n- Data race in test\n  ([ab6dc31](https://github.com/ory/kratos/commit/ab6dc3121535d27668fed58804a218b17b17ae43))\n- Do not encode full config in multiple places\n  ([#3500](https://github.com/ory/kratos/issues/3500))\n  ([57a3273](https://github.com/ory/kratos/commit/57a3273055c6e8627dd0b736e881dba3fb0fe75d))\n- Do not generate CSRF token for api flows\n  ([#3704](https://github.com/ory/kratos/issues/3704))\n  ([d93570d](https://github.com/ory/kratos/commit/d93570d330155c27a9315d1f530a0002a459910a))\n- Do not initialize parts of the registry in parallel\n  ([#3534](https://github.com/ory/kratos/issues/3534))\n  ([ff177db](https://github.com/ory/kratos/commit/ff177db8a97f27abc3e883e79832685348602334))\n- Don't list org SSOs in settings\n  ([#3637](https://github.com/ory/kratos/issues/3637))\n  ([6c7068c](https://github.com/ory/kratos/commit/6c7068cf41df51cde5fe9fc79cca84ec6124d38a))\n- Don't require code credential for MFA flows\n  ([#3753](https://github.com/ory/kratos/issues/3753))\n  ([40ed809](https://github.com/ory/kratos/commit/40ed809db631149874864f216a106c43ea5df670))\n- Don't require session for OIDC verification\n  ([#3443](https://github.com/ory/kratos/issues/3443))\n  ([e08f831](https://github.com/ory/kratos/commit/e08f831c2715e515bf58dc2dbb47fc3576421a5c))\n- Don't return 500 on conflict for POST /admin/identities\n  ([#3437](https://github.com/ory/kratos/issues/3437))\n  ([1429949](https://github.com/ory/kratos/commit/142994932e449d9948148804502c98ef73daafff))\n- Don't return nil if code is invalid\n  ([#3662](https://github.com/ory/kratos/issues/3662))\n  ([df8ec2b](https://github.com/ory/kratos/commit/df8ec2b9b77a53beb32e3f94a8fccb711896d8e7)):\n  - fix: don't return nil if code is invalid\n  - chore: add test\n\n- Error handling on identity import\n  ([#3520](https://github.com/ory/kratos/issues/3520))\n  ([83bfb2d](https://github.com/ory/kratos/commit/83bfb2d2a9c69bf3a3442500b9484c1a69f8c794)):\n\n  When importing identities without any traits, or with malformed traits, 500s\n  are returned. This improves the error handling and messaging.\n\n- False-positives for requiring re-authentication on update\n  ([#3421](https://github.com/ory/kratos/issues/3421))\n  ([ce8139f](https://github.com/ory/kratos/commit/ce8139f2325a8317388cbcaaa98f3f83d626657b))\n- Http courier using should use lower case json\n  ([#3740](https://github.com/ory/kratos/issues/3740))\n  ([84149c4](https://github.com/ory/kratos/commit/84149c4b420ea89f0a16a579c017a8e7e1670204))\n- Identity list pagination in CLI command and SDK\n  ([#3482](https://github.com/ory/kratos/issues/3482))\n  ([1e8b1ae](https://github.com/ory/kratos/commit/1e8b1aeb4bf866892788986f62a31255372de999)):\n\n  Adds correct pagination parameters to the SDK methods for listing identities\n  and sessions.\n\n- Ignore CSRF middleware on Apple OIDC callback\n  ([309c506](https://github.com/ory/kratos/commit/309c50694c11162cad070337f9b1d4e0fcdf444b))\n- Ignore more cloudflare cookies\n  ([#3499](https://github.com/ory/kratos/issues/3499))\n  ([f124ab5](https://github.com/ory/kratos/commit/f124ab5586781cdbfc0a0cfd11b4355bfc8a115c))\n- Improved SSRF protection ([#3629](https://github.com/ory/kratos/issues/3629))\n  ([6d08576](https://github.com/ory/kratos/commit/6d08576bbc2c06014192f05e0129b95eb6c9fd80)):\n\n  This also improves tracing in the OIDC strategy.\n\n- Incorrect login accept challenge\n  ([#3658](https://github.com/ory/kratos/issues/3658))\n  ([b5dede3](https://github.com/ory/kratos/commit/b5dede329247d0962688b15872a6caf027cf910f))\n- Incorrect sdk generator path\n  ([#3488](https://github.com/ory/kratos/issues/3488))\n  ([ed996c0](https://github.com/ory/kratos/commit/ed996c0d25e68e8a2c7de861c546f0b0e42e9e6e))\n- Incorrect SMTP error handling\n  ([#3636](https://github.com/ory/kratos/issues/3636))\n  ([ee138ec](https://github.com/ory/kratos/commit/ee138ec4e1ba55ef077858653220db9e6b0c7254))\n- Incorrect swagger spec for filter parameter\n  ([#3684](https://github.com/ory/kratos/issues/3684))\n  ([2c1470a](https://github.com/ory/kratos/commit/2c1470ab3556e639f06a01ac1646a6b90c7ecac7)),\n  closes [#3676](https://github.com/ory/kratos/issues/3676)\n  [#3675](https://github.com/ory/kratos/issues/3675)\n- Increase connection-level timeouts and shutdown timeouts\n  ([#3570](https://github.com/ory/kratos/issues/3570))\n  ([200b413](https://github.com/ory/kratos/commit/200b4138a429d113ee045d16031bb0a6312c1c01)):\n\n  The admin API is generally expected to require longer timeouts, for example\n  during bulk identity import.\n\n- Issue session after verification after registration with OIDC SSO\n  ([#3467](https://github.com/ory/kratos/issues/3467))\n  ([a28b523](https://github.com/ory/kratos/commit/a28b523238743f3873b51479eea3b86d684092f9))\n- Lint\n  ([e8740c3](https://github.com/ory/kratos/commit/e8740c3498446dcaeab2990604a317e61dc170df))\n- Lower-case recovery & verification emails on import\n  ([#3571](https://github.com/ory/kratos/issues/3571))\n  ([e2ac9ff](https://github.com/ory/kratos/commit/e2ac9ff4e2101788f1fca1b8c83f8791cce446e2)):\n\n  Emails that contained upper-case characters would be overwritten by the\n  identity schema extension runner, because there all emails are lower-cased.\n\n- Mark identity as optional in session struct\n  ([#3463](https://github.com/ory/kratos/issues/3463))\n  ([7ae02ba](https://github.com/ory/kratos/commit/7ae02ba697f68c9cfae5fe8f696b2c55a3ba9ddc)),\n  closes [#3461](https://github.com/ory/kratos/issues/3461):\n\n  The identity is not always available in the session struct, for example when\n  AAL2 is required.\n\n- Omit irrelevant OIDC providers in forced refresh login flows\n  ([#3608](https://github.com/ory/kratos/issues/3608))\n  ([912dccd](https://github.com/ory/kratos/commit/912dccdf04a550604c5bfeb53ccf79c5f1133ef2)):\n\n  Whenever an user is asked to reauthenticate (e.g. because they wish to execute\n  settings flow touching their credentials and their session is no longer\n  privileged) they are asked to provide their credentials again. The\n  forced-refresh login flow generated for such cases already excludes some\n  strategies that are enabled in Kratos but cannot be used to authenticate as\n  current identity, and for example the form presented to the user will not have\n  a password field if the identity does not have a password credential.\n\n  This, however, does not currently apply to OIDC providers; the user will\n  always see the full set even if some of them can't be used to sign in as\n  current identity. This change causes forced refresh login flows to also omit\n  irrelevant OIDC providers in generated form in order to avoid confunding the\n  user about which strategies/providers are valid and can actually be used to\n  reauthenticate.\n\n- On verification required after registration, preserve return_to\n  ([#3589](https://github.com/ory/kratos/issues/3589))\n  ([6a0a914](https://github.com/ory/kratos/commit/6a0a9149b9828ba994bec9b48a43f9d70245f43f)):\n  - fix: on verification required after registration, preserve return_to\n  - test: return_to on verification flow\n  - chore: refactor\n\n- Panic in recovery ([#3639](https://github.com/ory/kratos/issues/3639))\n  ([c25ddff](https://github.com/ory/kratos/commit/c25ddffd2270a8d0861e2fc78cd0ba26e63af4eb))\n- Pass context ([#3452](https://github.com/ory/kratos/issues/3452))\n  ([c492bdc](https://github.com/ory/kratos/commit/c492bdcd0c5dbdf527ae523d879a6c1eeb9c4cdf))\n- Properly normalize OIDC verified emails\n  ([#3450](https://github.com/ory/kratos/issues/3450))\n  ([703b910](https://github.com/ory/kratos/commit/703b910927d879558bfeb0fd2c3339b1d301fac8))\n- Redirect to verification URL even if login_challenge is set\n  ([#3412](https://github.com/ory/kratos/issues/3412))\n  ([cd9e6a0](https://github.com/ory/kratos/commit/cd9e6a0e1e4cb4957d2a50ae3d288ebb0591e42d)):\n\n  Fixes https://github.com/ory/network/issues/320\n\n- Reduce db lookups in whoami for aal check\n  ([#3372](https://github.com/ory/kratos/issues/3372))\n  ([d814a48](https://github.com/ory/kratos/commit/d814a4864d5c25c4f320daca733873577d517331)):\n\n  Significantly improves performance by reducing the amount of queries we need\n  to do when checking for the different AAL levels.\n\n- Registration code ui nodes group\n  ([#3505](https://github.com/ory/kratos/issues/3505))\n  ([6220184](https://github.com/ory/kratos/commit/622018459ddb16c182da49dfd91fd1c6ef8c6b73)):\n  - fix: registration code ui nodes group\n  - style: format\n\n- Registration should accept hydra login\n  ([#3592](https://github.com/ory/kratos/issues/3592))\n  ([7a47827](https://github.com/ory/kratos/commit/7a47827cfd58ef68ebfbbeaf5ed86c394ba2bd5e)):\n  - fix: registration should accept hydra login\n  - fix: oauth2 registration flow with session\n  - wip: registration oauth flow tests\n  - wip: refactor oauth flows test\n  - wip: refactor op_registration_test\n  - wip: oauth provider registration test\n  - wip: refactor oauth flows test\n  - fix(test): oauth provider login\n  - style: format\n\n- Registration with verification\n  ([#3451](https://github.com/ory/kratos/issues/3451))\n  ([77c3196](https://github.com/ory/kratos/commit/77c3196fd60c5927b84e9a7f6546f80ac2d78ee5))\n- Reject obviously invalid email addresses from courier\n  ([8cb9e4c](https://github.com/ory/kratos/commit/8cb9e4cae9dffd4c25d52920186f9c5fbe2bd0fe))\n- Remove `earliest_possible_extend` default in schema\n  ([#3464](https://github.com/ory/kratos/issues/3464))\n  ([7e05b7d](https://github.com/ory/kratos/commit/7e05b7db3c01efc96185ac18042e971e33da37c8))\n- Remove duplicate message ID usage\n  ([#3468](https://github.com/ory/kratos/issues/3468))\n  ([dfcbe22](https://github.com/ory/kratos/commit/dfcbe226bc53b91f3a6c9837496a159b85c2e68a))\n- Remove requirement for smtp section\n  ([#3405](https://github.com/ory/kratos/issues/3405))\n  ([59a3f14](https://github.com/ory/kratos/commit/59a3f1469b8412e49846a500493cb02fc6eb34b1))\n- Remove slow queries from update identities\n  ([#3553](https://github.com/ory/kratos/issues/3553))\n  ([d138abb](https://github.com/ory/kratos/commit/d138abb6278ebb232e120bee0fb956a0f2816b8d))\n- Rename \"phone\" courier channel to \"sms\"\n  ([#3680](https://github.com/ory/kratos/issues/3680))\n  ([eb8d1b9](https://github.com/ory/kratos/commit/eb8d1b9abd6d2b3eb86ab11d48d9ebd059586b67))\n- Respect gomail.SendError in mail queue\n  ([#3600](https://github.com/ory/kratos/issues/3600))\n  ([9c608b9](https://github.com/ory/kratos/commit/9c608b991874d839782d9219f2fc27d0d4a398af))\n- Respond with 422 when SPA identity requires AAL2\n  ([#3572](https://github.com/ory/kratos/issues/3572))\n  ([df18c09](https://github.com/ory/kratos/commit/df18c09e0089743e8aee17540d277b9572252e06)):\n\n  If you submit a browser login flow with an `Accept` header of\n  `application/json`, but the login flow requires AAL2, then there is no way for\n  the code to know it needs to redirect the user to the 2FA page. Instead of\n  responding with the `Session` in this scenario, this PR changes the behaviour\n  to respond with a `browser_location_change_required` error (status `422`) to\n  indicate that the browser needs to open a specific URL,\n  /self-service/login/browser?aal=aal2.\n\n- Return 400 bad request for invalid login challenge\n  ([#3404](https://github.com/ory/kratos/issues/3404))\n  ([ca34e9b](https://github.com/ory/kratos/commit/ca34e9b744482b41d65082f3bed52e9c4ebd7ba4))\n- Return HTTP 400 if key unmarshal fails\n  ([#3594](https://github.com/ory/kratos/issues/3594))\n  ([fdf4956](https://github.com/ory/kratos/commit/fdf4956d9218cfa1d2227c4880e48f9bbdaeb95d)):\n  - fix: return HTTP 400 if key unmarshal fails\n  - fix: apply reviewer's suggestion, prepare for bump\n  - fix: follow up reviewer suggestion from ory/x\n  - chore: bump ory/x\n\n- Schema test errors ([#3528](https://github.com/ory/kratos/issues/3528))\n  ([bee0341](https://github.com/ory/kratos/commit/bee0341c5bf5708a2210146fc59f050a1b9df663))\n- Set iss from userinfo claims if missing\n  ([#3744](https://github.com/ory/kratos/issues/3744))\n  ([241a911](https://github.com/ory/kratos/commit/241a911af74e8ad7353d6e3cab86db20758b86fc))\n- Specify correct minimum versions in migratest\n  ([18b89ea](https://github.com/ory/kratos/commit/18b89ea588d129fa88379f7b0d7f4fd00ec6023d))\n- Tracing context passing in /sessions/whoami\n  ([1254bf5](https://github.com/ory/kratos/commit/1254bf5a38dbe2c0e2798e07dd0ee5e4b2f63d6e))\n- Tracing improvements\n  ([c804cb2](https://github.com/ory/kratos/commit/c804cb2bebbefc97073cf3b8fa250c3eefc58894))\n- Type-assert all interfaces that WebHook implements\n  ([ffda1a0](https://github.com/ory/kratos/commit/ffda1a0dab661c5f11ad849b9287094313561b79))\n- Ui node input attributes key added\n  ([#3561](https://github.com/ory/kratos/issues/3561))\n  ([9eff0f3](https://github.com/ory/kratos/commit/9eff0f3a611f32af7aa7f27587b3d3f4448ce915)):\n  - fix: ui node InputAttributes.Key added\n  - fix: selfservice recovery flow add React unique key and numeric pattern\n  - fix: remove React related key addition\n  - test: update snapshot\n\n- Use ID label on login with multiple identifiers\n  ([#3657](https://github.com/ory/kratos/issues/3657))\n  ([be907db](https://github.com/ory/kratos/commit/be907dbbd841025fd854344b77d3368b2ff8089f))\n- Use org ID from session if available in login flow\n  ([#3545](https://github.com/ory/kratos/issues/3545))\n  ([1b3647c](https://github.com/ory/kratos/commit/1b3647c2acdad966f920c2b9e6e657c52aa50c6e))\n- Use provider label in link message\n  ([#3661](https://github.com/ory/kratos/issues/3661))\n  ([fa5ec93](https://github.com/ory/kratos/commit/fa5ec93e8ae7d971d07f0e9b3acaa0840b9ac7de))\n- Use registry client for schema loading\n  ([#3471](https://github.com/ory/kratos/issues/3471))\n  ([3a57726](https://github.com/ory/kratos/commit/3a577269980213e4415fd5fa713882990e2e7640))\n- Using first name as last name\n  ([#3556](https://github.com/ory/kratos/issues/3556))\n  ([df80377](https://github.com/ory/kratos/commit/df80377f5fe6180fba5904baa5be1ba1d68eb2aa))\n- Wrong continue_with enum declaration\n  ([#3522](https://github.com/ory/kratos/issues/3522))\n  ([4c34c24](https://github.com/ory/kratos/commit/4c34c2417db0cb1f79b42db5f33544c90b38ad87))\n\n### Code Generation\n\n- Pin v1.1.0 release commit\n  ([f47675b](https://github.com/ory/kratos/commit/f47675b82012e0ff74b05b9b7e713b3aa2fdda54))\n\n### Documentation\n\n- Add example for `allowed_return_urls` to include wildcard url\n  ([#3533](https://github.com/ory/kratos/issues/3533))\n  ([39b0c3c](https://github.com/ory/kratos/commit/39b0c3c03df0aec254b32c840730452d4856872b)),\n  closes [#1528](https://github.com/ory/kratos/issues/1528)\n- Improve enum handling and completeness\n  ([#3714](https://github.com/ory/kratos/issues/3714))\n  ([4b881ca](https://github.com/ory/kratos/commit/4b881cae4359bfa068261d2d0765ce3daadcbcf2))\n- Remove experimental warnings\n  ([#3406](https://github.com/ory/kratos/issues/3406))\n  ([d4d26e6](https://github.com/ory/kratos/commit/d4d26e6e1510c8e09346e95251f420f95ec54998)):\n\n  See https://github.com/ory/kratos/discussions/3388\n\n- Update link to hashed password formats\n  ([#3484](https://github.com/ory/kratos/issues/3484))\n  ([8ca3adc](https://github.com/ory/kratos/commit/8ca3adcb8a5db2906fbeb92f4b74aa4242fabdef))\n\n### Features\n\n- Add ability to convert session to JWT when calling whoami\n  ([#3472](https://github.com/ory/kratos/issues/3472))\n  ([57b7bb8](https://github.com/ory/kratos/commit/57b7bb846c8072f786ea6b80cd688fdee75805da)),\n  closes [#2487](https://github.com/ory/kratos/issues/2487):\n\n  This patch adds a query parameter `tokenize_as` to `/session/whoami` which\n  encodes the session to a JWT. It is possible to customize the JWT claims by\n  using a JsonNet template, and furthermore change the expiry of the token.\n\n  The tokenize feature supports multiple templates, which makes it easy to use\n  the resulting JWT in a variety of use cases.\n\n- Add event ([#3524](https://github.com/ory/kratos/issues/3524))\n  ([75031e6](https://github.com/ory/kratos/commit/75031e67bc82a820a6aba134115e8d5f93303638))\n- Add GetID member functions to RecoveryAddress and Credentials\n  ([#3474](https://github.com/ory/kratos/issues/3474))\n  ([085d500](https://github.com/ory/kratos/commit/085d5002df27d455057d33bd2d93dfbca0de4872))\n- Add ID Token sign in with Google Android/iOS SDK\n  ([#3515](https://github.com/ory/kratos/issues/3515))\n  ([055ed92](https://github.com/ory/kratos/commit/055ed9226d9d12f5142542be2e18438ff708c2e2))\n- Add OpenTelemetry span for password hash comparison\n  ([#3383](https://github.com/ory/kratos/issues/3383))\n  ([e3fcf0c](https://github.com/ory/kratos/commit/e3fcf0c31db9742ed61bcf783e37ee119ed19d42))\n- Add request URL to email and SMS templates\n  ([bf5f8c3](https://github.com/ory/kratos/commit/bf5f8c3cfb2eb523a77239addb8249adf9f8b31d))\n- Add sms verification for phone numbers\n  ([#3649](https://github.com/ory/kratos/issues/3649))\n  ([e3a3c4f](https://github.com/ory/kratos/commit/e3a3c4fe0d6697f6864283daf4be8a8f8971c7b4))\n- Add support for recovery on native flows\n  ([#3273](https://github.com/ory/kratos/issues/3273))\n  ([e363889](https://github.com/ory/kratos/commit/e363889732c0a1cb801fd12b2e0e8546006e9714))\n- Add WebhookSucceeded event\n  ([aa8c936](https://github.com/ory/kratos/commit/aa8c93677a8f682f7693afe69f1baf1887355e0a))\n- Added various new text messages\n  ([ea91483](https://github.com/ory/kratos/commit/ea914834e6bb626de2977e228af2b40935ccc980)):\n\n  To improve i18n and message customization, we added a bunch of new messages.\n  Integrations that do message customization should probably handle those new\n  message codes:\n  - 1010014\n  - 1010015\n  - 1040005\n  - 1040006\n  - 1070012\n  - 1070013\n  - 4000028\n  - 4000029\n  - 4000030\n  - 4000031\n  - 4000032\n  - 4000033\n  - 4000034\n  - 4000035\n  - 4000036\n  - 4010007\n  - 4010008\n  - 4040002\n  - 4040003\n\n  Additionally, these messages got more context:\n  - 1050014\n  - 1050018\n  - 1070002\n  - 4000001\n  - 4000003\n  - 4000004\n  - 4000017\n  - 4000018\n  - 4000019\n  - 4000020\n  - 4000021\n  - 4000022\n  - 4000023\n  - 4000024\n  - 4000025\n  - 4000026\n  - 4010001\n  - 4040001\n  - 4050001\n  - 4060005\n  - 4070005\n  - 5000001\n\n- Allow additional id token audiences\n  ([#3616](https://github.com/ory/kratos/issues/3616))\n  ([0fa648d](https://github.com/ory/kratos/commit/0fa648d9f7b837a35de9b230a05b5951e95d5874))\n- Allow extra migrations in NewPersister\n  ([96c1ff7](https://github.com/ory/kratos/commit/96c1ff7747ea38e23a3892f74b75ee555ed49c88))\n- Allow fuzzy-search on credential identifiers\n  ([#3526](https://github.com/ory/kratos/issues/3526))\n  ([2cb3ea2](https://github.com/ory/kratos/commit/2cb3ea2eaff909ac936611d5653f69e713f41b64)):\n\n  This PR adds the ability to search for sub-strings and similar strings in\n  credential identifiers.\n\n  Note that the **postgres** and **CRDB** migrations create special indexes\n  useful for this feature. To use\n  [online schema changes](https://www.cockroachlabs.com/docs/v23.1/online-schema-changes)\n  with cockroach, we recommend to manually copy the index definition and run it\n  before applying migrations. The migration will then be a no-op.\n\n  If you run on **mysql** (or **sqlite**), no special index is created. If\n  desired, you can create such an index manually, and it would be highly\n  appreciated if you could contribute its definition.\n\n  This feature is a preview and will change in behavior! Similarity search is\n  not expected to return deterministic results but are useful for humans.\n\n- Allow importing hmac hashed passwords\n  ([#3544](https://github.com/ory/kratos/issues/3544))\n  ([0a0e1f7](https://github.com/ory/kratos/commit/0a0e1f7200e226ef24de062811a05bcdd02b6acd)),\n  closes [#2422](https://github.com/ory/kratos/issues/2422):\n\n  The basic format is\n  `$hmac-<hashfunction>$<base64 encoded hash>$<base64 encoded key>`:\n\n  ```\n  # password = test; key=key; hash function=sha\n  $hmac-sha1$NjcxZjU0Y2UwYzU0MGY3OGZmZTFlMjZkY2Y5YzJhMDQ3YWVhNGZkYQ==$a2V5\n  ```\n\n- Allow marking OIDC provider-verified addresses as verified during registration\n  ([#3448](https://github.com/ory/kratos/issues/3448))\n  ([e7b33a1](https://github.com/ory/kratos/commit/e7b33a168bf0c0fe0492901abd3df8b6d6a08a68)),\n  closes [#3445](https://github.com/ory/kratos/issues/3445)\n  [#3424](https://github.com/ory/kratos/issues/3424)\n  [#1057](https://github.com/ory/kratos/issues/1057):\n\n  This feature allows marking emails provided by social sign in providers as\n  verified.\n\n- Batch list identities ([#3598](https://github.com/ory/kratos/issues/3598))\n  ([8ad54f1](https://github.com/ory/kratos/commit/8ad54f1be53b30fdb24b616be0c52fd66829f201)),\n  closes [#2448](https://github.com/ory/kratos/issues/2448):\n\n  This change allows to filter `GET /admin/identities` by ID with the following\n  syntax:\n\n  ```\n  /admin/identities?ids=id1&ids=id2&ids=id3\n  ```\n\n- **changelog:** Add support for native recovery\n  ([#3624](https://github.com/ory/kratos/issues/3624))\n  ([492808c](https://github.com/ory/kratos/commit/492808cae0e804793aef9a02a902fce988f9fc6d)):\n\n  Adds the ability to complete the recovery flow properly on API flows. This PR\n  also streamlines the behavior for SPA flows to not return 422 errors anymore.\n  To enable this new behavior, set the features.use_continue_with_transitions\n  flag in the config to `true`.\n\n  See also https://github.com/ory/kratos/pull/3273\n\n- Claims from userinfo endpoint\n  ([#3718](https://github.com/ory/kratos/issues/3718))\n  ([90bdc61](https://github.com/ory/kratos/commit/90bdc61d28466f10e4e609df014b220afbee0478)):\n  - feat: claims from userinfo endpoint\n  - chore: update libraries\n  - test: improve coverage\n\n- Emit error details when we find stray cookies in an API flow\n  ([#3496](https://github.com/ory/kratos/issues/3496))\n  ([df74339](https://github.com/ory/kratos/commit/df74339802d98a292abb32806eca35fb2554960b))\n- Eventually consistency API controls\n  ([#3558](https://github.com/ory/kratos/issues/3558))\n  ([00cf11c](https://github.com/ory/kratos/commit/00cf11c071344103c603c078f07196401d091780)):\n\n  Adds a feature used in Ory Network which enables trading faster reads for\n  slightly stale data.\n\n  This feature depends on Cockroach functionality and configuration, and is not\n  possible for MySQL or PostgreSQL.\n\n- Extend Microsoft Graph API capabilities\n  ([#3609](https://github.com/ory/kratos/issues/3609))\n  ([4a7bcc9](https://github.com/ory/kratos/commit/4a7bcc9322be37e6fd141e411bd65e3977eeb692)):\n\n  This change queries for all user information available with the `User.Read`\n  scope during OIDC, and populates the `RawClaims` field.\n\n- Extract identifier label for login from default identity schema\n  ([#3645](https://github.com/ory/kratos/issues/3645))\n  ([180828e](https://github.com/ory/kratos/commit/180828eb507ab239a9c6589f747a6816b6e50074))\n- Fine-grained hooks for all available flow methods\n  ([#3519](https://github.com/ory/kratos/issues/3519))\n  ([a37f6bd](https://github.com/ory/kratos/commit/a37f6bddc48443b2fc464699fa5c2922f64d81f6)):\n\n  Adds fine-grained hook configurations to the post-settings flow for methods\n  totp, webauthn, lookup_secret and the post-login flow for totp, lookup_secret,\n  and code.\n\n- Hook to revoke sessions after password changed\n  ([#3514](https://github.com/ory/kratos/issues/3514))\n  ([e6af6db](https://github.com/ory/kratos/commit/e6af6db37ff5de33a656ce7804c813451395459d)),\n  closes [#3513](https://github.com/ory/kratos/issues/3513):\n\n  Currently, the Kratos system does not automatically log out or invalidate\n  other active sessions when a user changes their password. This poses a\n  significant security risk as it allows potentially unauthorized individuals to\n  maintain access to the account even after the password has been updated.\n\n  This PR provides the option to add the `revoke_active_sessions` hook to the\n  actions sections of the selfservice settings.\n\n- Hot-reload CORS origins ([#3423](https://github.com/ory/kratos/issues/3423))\n  ([157d934](https://github.com/ory/kratos/commit/157d9345aeb04f371f9d85b70c89e8646e781333))\n- Improve messages for easier i18n\n  ([#3457](https://github.com/ory/kratos/issues/3457))\n  ([37f1657](https://github.com/ory/kratos/commit/37f16577d92ba88869bf15fb1ea54e819b062724))\n- Improve performance by computing password hashes while validating\n  ([#3508](https://github.com/ory/kratos/issues/3508))\n  ([a9786c5](https://github.com/ory/kratos/commit/a9786c599d09f61e2e07df5066ce94feb2d99bac))\n- Improved webhook tracing ([#3746](https://github.com/ory/kratos/issues/3746))\n  ([9d7021d](https://github.com/ory/kratos/commit/9d7021d87f47690c2c1f8000e87b425e49bc9496))\n- Jsonnet caching for OIDC claims mapper, webhooks, JWT session tokenizer\n  ([#3701](https://github.com/ory/kratos/issues/3701))\n  ([1d26e09](https://github.com/ory/kratos/commit/1d26e097b273aeda36f73637765da5bdb2aa4a66))\n- Link oidc credentials when login\n  ([#3563](https://github.com/ory/kratos/issues/3563))\n  ([b784949](https://github.com/ory/kratos/commit/b784949d03b849d9d1d594977f75f5843b7b5da8)),\n  closes [#2727](https://github.com/ory/kratos/issues/2727)\n  [#3222](https://github.com/ory/kratos/issues/3222):\n\n  When user tries to login with OIDC for the first time but has already\n  registered before with email/password a credentials identifier conflict may be\n  detected by Kratos. In this case user needs to login with email/password first\n  and then link OIDC credentials on a settings screen. This PR simplifies UX and\n  allows user to link OIDC credentials to existing account right in the login\n  flow, without switching to settings flow.\n\n- List by OIDC cred ([#3721](https://github.com/ory/kratos/issues/3721))\n  ([bff9c61](https://github.com/ory/kratos/commit/bff9c61b147648ab139e7e86cda4336b5d1cfd39))\n- Login with code on any credential type\n  ([#3549](https://github.com/ory/kratos/issues/3549))\n  ([ceed7d5](https://github.com/ory/kratos/commit/ceed7d5478c5cca894587698c57f676dda100b27)):\n\n  Should be able to login with the `code` credential even if the user did not\n  register on the `code` credential. Only `identifier` matching is done and\n  validation based on the identity schema.\n\n- One-time code native flows\n  ([#3516](https://github.com/ory/kratos/issues/3516))\n  ([9b0fee3](https://github.com/ory/kratos/commit/9b0fee30f980d860fd548e7589fa6a06e593537a))\n- Order sessions by created_at\n  ([#3696](https://github.com/ory/kratos/issues/3696))\n  ([688111c](https://github.com/ory/kratos/commit/688111c9a6bf9872657cf6aada77f55fa2520e00))\n- Parametrize courier worker\n  ([#3601](https://github.com/ory/kratos/issues/3601))\n  ([0e4be57](https://github.com/ory/kratos/commit/0e4be57e41e1152f4be22f490541c2c099cfe3fe)):\n\n  Allows one to parametrize how many messages the courier will fetch and how\n  often it will fetch messages.\n\n- Passwordless browser login and registration via code to email\n  ([#3378](https://github.com/ory/kratos/issues/3378))\n  ([eaaf375](https://github.com/ory/kratos/commit/eaaf37519917612671238412a633847386d7c613)),\n  closes [#2029](https://github.com/ory/kratos/issues/2029)\n  [ory-corp/cloud#3573](https://github.com/ory-corp/cloud/issues/3573):\n\n  This feature adds passwordless email code login. When a user signs up, or\n  signs in, a code is sent to their email address which they can use to complete\n  the authentication process.\n\n  This feature is currently only working for browser facing APIs.\n\n- Pooled process-isolated Jsonnet VM\n  ([9a52ddf](https://github.com/ory/kratos/commit/9a52ddfbe7c24c41b6aa3ddc3c79c6fcbfb8db02))\n- Provide login hints when registration fails due to duplicate\n  credentials/addresses ([#3430](https://github.com/ory/kratos/issues/3430))\n  ([8b28469](https://github.com/ory/kratos/commit/8b284697e4a26fb01ad57d2e9ebd8f714be49f33)):\n  - feat: provide login hints when registration fails due to duplicate\n    credentials or identifiers\n  - feat: identify edge cases and write tests\n  - chore: synchronize workspaces\n  - feat: make login hints configurable\n  - chore: synchronize workspaces\n  - chore: synchronize workspaces\n  - chore: synchronize workspaces\n  - chore: synchronize workspaces\n\n- Support auth_type parameter\n  ([#3487](https://github.com/ory/kratos/issues/3487))\n  ([fc30304](https://github.com/ory/kratos/commit/fc303040b71139f512fd1491ce30f80837b940b9)):\n\n  The Facebook OIDC provider supports an auth_type parameter that when set to\n  \"reauthenticate\" will force the user to reauthenticate (similar to\n  `prompt=login` for other Providers).\n\n- Support for B2B SSO ([#3489](https://github.com/ory/kratos/issues/3489))\n  ([0ec037a](https://github.com/ory/kratos/commit/0ec037ab298ed28fb0ac84db6a4d2b14b81e57df))\n- Support MFA via SMS ([#3682](https://github.com/ory/kratos/issues/3682))\n  ([1516cf6](https://github.com/ory/kratos/commit/1516cf64e346819dccace1cc25aaccac38b9e47c))\n- Support multiple origins for WebAuthN\n  ([#3380](https://github.com/ory/kratos/issues/3380))\n  ([013f335](https://github.com/ory/kratos/commit/013f335881831bbf90ac31b219b57118fc089fe6)):\n\n  Users can now supply a list of origins for webauthn in the configuration.\n\n- Support native social sign using apple sdk\n  ([#3476](https://github.com/ory/kratos/issues/3476))\n  ([f561013](https://github.com/ory/kratos/commit/f561013dd737dadcc82c4ec049fde12861e91e43))\n- Transmit current session ID to Hydra when accepting the login\n  ([#3426](https://github.com/ory/kratos/issues/3426))\n  ([610c76d](https://github.com/ory/kratos/commit/610c76d9140f2f43217ac55094051a994ea83ecc)):\n  - chore: change react-native port to 19006\n  - feat: transmit current session ID when accepting login\n  - fix: upgrade hydra in tests\n\n- Webhook analytic events\n  ([9c8a25e](https://github.com/ory/kratos/commit/9c8a25eb0d3e06df182565d3d959d57e5dccfed8))\n\n### Reverts\n\n- Revert \"chore: simplify courier code (#3603)\"\n  ([7c54c9f](https://github.com/ory/kratos/commit/7c54c9f36c86142c8e071a5359c71cf6213a1a69)),\n  closes [#3603](https://github.com/ory/kratos/issues/3603):\n\n  This reverts commit 316cd4aacfe31efafa7d737a7c476e2c794e9c9b.\n\n### Tests\n\n- Add test for link + oidc challenge\n  ([#3720](https://github.com/ory/kratos/issues/3720))\n  ([67360cf](https://github.com/ory/kratos/commit/67360cf39482b935604f088a4b7a83cc4deab375))\n- **e2e:** Logout return_to ([#3418](https://github.com/ory/kratos/issues/3418))\n  ([c348c12](https://github.com/ory/kratos/commit/c348c12ab3c9cdb4ce8159fe774ed179ff6a4d8a))\n- Fix cypress setup ([#3527](https://github.com/ory/kratos/issues/3527))\n  ([70c8ddd](https://github.com/ory/kratos/commit/70c8ddd49c8abb9c10f2ca349e01061b791c5e7b))\n- Fix e2e failures and speed up e2e tests\n  ([#3483](https://github.com/ory/kratos/issues/3483))\n  ([70a6171](https://github.com/ory/kratos/commit/70a617194d61763f4b75691b22cfa76ba71ab019))\n- Fix hydra tests on master ([#3737](https://github.com/ory/kratos/issues/3737))\n  ([12166b4](https://github.com/ory/kratos/commit/12166b4370d607a069f268227752bb7b18a50b57))\n- Reduce logging in go tests\n  ([#3562](https://github.com/ory/kratos/issues/3562))\n  ([05de3a2](https://github.com/ory/kratos/commit/05de3a29fed020593c44ea7a7b29e45197fef4f7))\n- Resolve cypress issues ([#3531](https://github.com/ory/kratos/issues/3531))\n  ([4206d26](https://github.com/ory/kratos/commit/4206d2605dfa30b19e132be31b85b1a35f8dca78))\n\n### Unclassified\n\n- Revert \"feat: extend Microsoft Graph API capabilities (#3609)\" (#3717)\n  ([549308d](https://github.com/ory/kratos/commit/549308db1f7dca42004631ed6156cae5f827b8fe)),\n  closes [#3609](https://github.com/ory/kratos/issues/3609)\n  [#3717](https://github.com/ory/kratos/issues/3717):\n\n  This reverts commit 4a7bcc9322be37e6fd141e411bd65e3977eeb692.\n\n# [1.0.0](https://github.com/ory/kratos/compare/v0.13.0...v1.0.0) (2023-07-12)\n\nWe are thrilled to announce Ory Kratos v1.0, the powerful Identity, User\nManagement, and Authentication system! With this major update, Ory Kratos brings\na host of enhancements and fixes that greatly improve the user experience and\noverall performance.\n\nSeveral compelling reasons led to label Ory Kratos as a major release, like\nsuccessfully processing over 100 million API requests daily and having about 100\nmillion Docker Pulls. We have maintained stability within the Ory Kratos APIs\nfor nearly two years, demonstrating their robustness and reliability. No\nbreaking changes mean that developers can trust the stability of Ory Kratos in\nproduction.\n\nOry Kratos 1.0 introduces a variety of new features while focusing on stability,\nrobustness, and improved performance. Major enhancements include support for\nsocial login and single-sign-on via OpenID connect in native apps, emails sent\nthrough HTTP rather than SMTP, and full compatibility with Ory Hydra v2.2.0.\nUsers will also find multi-region support in the Ory Network for broader\ngeographic reach, improved export functionality for all credential types, and\nenhanced session management with the introduction of the \"provider ID\"\nparameter. Other additions comprise distroless images for leaner resource\nutilization and faster deployment and support for the Lark OIDC provider.\n\nSignificant improvements and fixes accompany these new features. Enhanced OIDC\nflows now include the ability to forward prompt upstream parameters, offering\ndevelopers increased flexibility and customization options. The logout flow also\nsupports the `return_to` parameter, facilitating more flexible redirection\npost-user logout. Performance has been a key focus, with Ory Kratos 1.0 now\ncapable of handling hundreds of millions of active users monthly. Critical bug\nfixes have been applied to prevent users from being redirected to incorrect\ndestinations, ensuring smoother authentication and authorization. Additionally,\nthere's more support for legacy systems via implemented crypt(3) hashers and a\nfix for metadata patching has been deployed to ensure consistent user metadata\nmanagement. For a detailed view of all changes, refer to the\n[changelog on GitHub](https://github.com/ory/kratos/blob/master/CHANGELOG.md).\nFeedback and support are, as always, greatly appreciated.\n\nOry Kratos 1.0 is a major release that marks a significant milestone in our\njourney.\n\nWe sincerely hope that you find these new features and improvements in Ory\nKratos 1.0 valuable for your projects. To experience the power of the latest\nrelease, we encourage you to get the latest version of Ory\nKratos [here](https://github.com/ory/kratos) or leverage Kratos\nin [Ory Network](https://www.ory.sh/network/) — the easiest, simplest, and most\ncost-effective way to run Ory.\n\nFor organizations seeking to upgrade their self-hosted solution, **Ory offers\ndedicated support services to ensure a smooth transition**. Our team is ready to\nassist you throughout the migration process, ensuring uninterrupted access to\nthe latest features and improvements. Additionally, we provide\nvarious [support plans](https://www.ory.sh/support/) specifically tailored for\nself-hosting organizations. These plans offer comprehensive assistance and\nguidance to optimize your Ory deployments and meet your unique requirements.\n\nWe extend our heartfelt gratitude to the vibrant and supportive Ory Community.\nWithout your constant support, feedback, and contributions, reaching this\nsignificant milestone would not have been possible. As we continue on this\njourney, your feedback and suggestions are invaluable to us. Together, we are\nshaping the future of identity management and authentication in the digital\nlandscape.\n\nContributors to this release in alphabetical order:\n[borisroman](https://github.com/ory/kratos/commits?author=borisroman),\n[ci42](https://github.com/ory/kratos/commits?author=ci42),\n[CNLHC](https://github.com/ory/kratos/commits?author=CNLHC),\n[David-Wobrock](https://github.com/ory/kratos/commits?author=David-Wobrock),\n[giautm](https://github.com/ory/kratos/commits?author=giautm),\n[IchordeDionysos](https://github.com/ory/kratos/commits?author=IchordeDionysos),\n[indietyp](https://github.com/ory/kratos/commits?author=indietyp),\n[jossbnd](https://github.com/ory/kratos/commits?author=jossbnd),\n[kralicky](https://github.com/ory/kratos/commits?author=kralicky),\n[PhakornKiong](https://github.com/ory/kratos/commits?author=PhakornKiong),\n[sunakan](https://github.com/ory/kratos/commits?author=sunakan),\n[steverusso](https://github.com/ory/kratos/commits?author=steverusso)\n\nAre you passionate about security and want to make a meaningful impact in one of\nthe biggest open-source communities? Join the\n[Ory community](https://slack.ory.sh) and become a part of the new ID stack.\nTogether, we are building the next generation of IAM solutions that empower\norganizations and individuals to secure their identities effectively.\n\nWant to check out Ory Kratos yourself? Use these commands to get your Ory Kratos\nproject running on the Ory Network:\n\n```shell\nbrew install ory/tap/cli\n\nscoop bucket add ory https://github.com/ory/scoop.git\nscoop install ory\n\nbash <(curl <https://raw.githubusercontent.com/ory/meta/master/install.sh>) -b . ory\nsudo mv ./ory /usr/local/bin/\n\nory auth\n\nory create project --name \"My first Kratos project\"\n\nory open account-experience registration\n\nory patch identity-config \\\\\n  --replace '/identity/default_schema_id=\"preset://username\"' \\\\\n  --replace '/identity/schemas=[{\"id\":\"preset://username\",\"url\":\"preset://username\"}]' \\\\\n  --format yaml\n\nory open account-experience registration\n```\n\n### Bug Fixes\n\n- Ability to patch metadata even if it is `null`\n  ([#3304](https://github.com/ory/kratos/issues/3304))\n  ([3c04d8f](https://github.com/ory/kratos/commit/3c04d8fb63cacf91774864450b02d6d1eb90d856))\n- Accept OIDC login request in browser+JSON login flow\n  ([#3271](https://github.com/ory/kratos/issues/3271))\n  ([ad54093](https://github.com/ory/kratos/commit/ad540930df96e84fb65a36616d5081ec0bb46df5)):\n  - fix: OIDC login in browser JSON flow\n  - test: add test for OIDC+JSON continuity cookie\n\n- Add error checking when creating verification code\n  ([#3328](https://github.com/ory/kratos/issues/3328))\n  ([7182eca](https://github.com/ory/kratos/commit/7182eca074c8e84be325d62c75b62d22698878be))\n- Add missing SessionIssued event for api flows\n  ([#3348](https://github.com/ory/kratos/issues/3348))\n  ([adf78e0](https://github.com/ory/kratos/commit/adf78e09f336b2ac83f8ff1ba5ca382c7cfbec23)):\n  - fix: missing SessionIssued event for api flows\n  - chore: add SessionIssued event to post registration hook\n  - chore: format\n  - chore: move sessionissued event to persister\n\n- Bump quickstart version ([#3257](https://github.com/ory/kratos/issues/3257))\n  ([6db70a8](https://github.com/ory/kratos/commit/6db70a81afac5860a86c31881a6fc988096ff0e4))\n- Cypress TOTP test\n  ([eac908c](https://github.com/ory/kratos/commit/eac908c4fc14831288e6fd5b3c65ac197d2f58e1))\n- Do not require items to be unique\n  ([#3349](https://github.com/ory/kratos/issues/3349))\n  ([17be30d](https://github.com/ory/kratos/commit/17be30dd84c667e5d1ae13bd79827b7ca9cdd2de))\n- Don't assume the login challenge to be a UUID\n  ([#3317](https://github.com/ory/kratos/issues/3317))\n  ([3172862](https://github.com/ory/kratos/commit/3172862929ad68011fc940a6e0876fa07187a275)):\n\n  For compatibility with https://github.com/ory/hydra/pull/3515, which now\n  encodes the whole flow in the login challenge, we cannot further assume that\n  the challenge is a UUID.\n\n- **e2e:** Install kratos-selfservice-ui-node peer deps\n  ([#3354](https://github.com/ory/kratos/issues/3354))\n  ([ce20063](https://github.com/ory/kratos/commit/ce20063a858acecb5d9124792fe6d3899bf95c1c))\n- Identity list pagination ([#3325](https://github.com/ory/kratos/issues/3325))\n  ([9d3ef0d](https://github.com/ory/kratos/commit/9d3ef0df9333aff2c587005df0cdd263028029f3)):\n\n  Resolves a pesky issue that would skip the last page.\n\n- IdentityCreated event ([#3314](https://github.com/ory/kratos/issues/3314))\n  ([78e31cb](https://github.com/ory/kratos/commit/78e31cb82a28e240a6176c8d3d9ef3bc64559e75))\n- Incorrect override in identity hydrate\n  ([#3368](https://github.com/ory/kratos/issues/3368))\n  ([eaa3f3c](https://github.com/ory/kratos/commit/eaa3f3c19feaf9048e800cc5a5f1e28d3708c624))\n- Increase size for request url\n  ([#3366](https://github.com/ory/kratos/issues/3366))\n  ([10713cc](https://github.com/ory/kratos/commit/10713cc703457cb6f4a1b38482c836e54a0cb224))\n- Minor refactorings in package hash\n  ([#3186](https://github.com/ory/kratos/issues/3186))\n  ([831fb19](https://github.com/ory/kratos/commit/831fb19e1c98b9fade3ff61d26ad249c548292d6))\n- Missing id for login event\n  ([#3315](https://github.com/ory/kratos/issues/3315))\n  ([b6b80a3](https://github.com/ory/kratos/commit/b6b80a3af1162e4009fa8c7c5e9ae7225e941849))\n- Properly normalize uppercase mail addresses\n  ([4984e0f](https://github.com/ory/kratos/commit/4984e0fb329291484a54344255f797008142b7cc)):\n\n  Fixes https://github.com/ory/kratos/issues/3187 Fixes\n  https://github.com/ory/kratos/issues/3289\n\n- Provide index hint in QueryForCredentials\n  ([#3329](https://github.com/ory/kratos/issues/3329))\n  ([4ba530e](https://github.com/ory/kratos/commit/4ba530ef593272d3cc0a9e1d354e81db495e8686)):\n  - fix: provide index hint in QueryForCredentials\n  - feat: remove customizable join predicate in QueryForCredentials\n  - chore: remove obsolete config tracer\n\n- Reduce lookups in whoami call\n  ([#3364](https://github.com/ory/kratos/issues/3364))\n  ([5bb7b0c](https://github.com/ory/kratos/commit/5bb7b0c83b330ee893bdeb4e636655179bd29e39))\n- Reintroduce ExpandAll ([#3369](https://github.com/ory/kratos/issues/3369))\n  ([8f9bff5](https://github.com/ory/kratos/commit/8f9bff527528780b623bf8e4801f7f3c37a5a6f3))\n- Remove codeball\n  ([aa29606](https://github.com/ory/kratos/commit/aa296067e2736cad329814f7acffd816ce0d74a3))\n- Remove duplicate SessionIssued event\n  ([#3351](https://github.com/ory/kratos/issues/3351))\n  ([b1e78ad](https://github.com/ory/kratos/commit/b1e78ad3e39418695639e521ddceb64589455d87))\n- Return HTTP 400 instead of 500 for bad query parameters\n  ([58258eb](https://github.com/ory/kratos/commit/58258eba99aa15f2ac852123c0200f56518ecb2a))\n- **sdk:** Add cookie for updateLogoutFlow\n  ([#3284](https://github.com/ory/kratos/issues/3284))\n  ([95ed2b9](https://github.com/ory/kratos/commit/95ed2b94cc99d40af6bbe57e5356ec0f28cb9b78)):\n\n  Closes https://github.com/ory/sdk/issues/255\n\n- **sdk:** Update the API spec to reflect the 204 NoContent in\n  DeleteIdentityCredentials ([#3347](https://github.com/ory/kratos/issues/3347))\n  ([f3dee86](https://github.com/ory/kratos/commit/f3dee869bef0e0dd2d36541823ae57d54ba5788e))\n- Settings should persist `return_to` after required mfa login flow\n  ([#3263](https://github.com/ory/kratos/issues/3263))\n  ([0ed1abd](https://github.com/ory/kratos/commit/0ed1abd391b6b5369862ee5db8faa4f4aaf68b09)):\n  - fix: get settings should persist `return_to` when redirecting to aal2\n  - feat(e2e): verify `return_to` persists in recovery flows\n  - test: recovery strategy with mfa account\n  - test: code recovery return to persists to settings with aal2\n  - u\n  - fix: return to settings flow after mfa login\n  - fix(test): login handler\n  - fix: flow between settings and mfa\n  - fix: get settings endpoint should redirect to settings ui instead of to\n    itself\n  - feat(test): preserve URL from various settings flows through login mfa flow\n  - chore: cleanup\n  - fix(e2e): recovery return to spa tests\n  - fix: e2e proxy\n  - fix: do not always redirect back to settings on mfa\n  - fix: new settings flow with required mfa shouldn't be added to login flow\n    return_to unless it contains a return_to parameter\n  - fix(e2e): let test dynamically handle required_aal\n  - chore: cleanup unused code\n  - test: `DoesSessionSatisfy` with method options\n  - test: recovery strategy with aal2\n\n- String to enum for updateVerificationFlowWithLinkMethod Method\n  ([#3279](https://github.com/ory/kratos/issues/3279))\n  ([34ff1d2](https://github.com/ory/kratos/commit/34ff1d2912e7f7aefb35dae759dce2eb37ecb790)),\n  closes [#2943](https://github.com/ory/kratos/issues/2943)\n- Update correct typo ([#3281](https://github.com/ory/kratos/issues/3281))\n  ([0fea75c](https://github.com/ory/kratos/commit/0fea75c4093d2c7edc84c14f0ab5bebf33a58970)):\n\n  The text for verification code input should be `Verification code` not\n  `Verify code`.\n\n- Update README ([#3363](https://github.com/ory/kratos/issues/3363))\n  ([c426014](https://github.com/ory/kratos/commit/c4260140966489a05169a0197e209ff98181bc2e))\n- Use RETURNING clause for batch create\n  ([#3293](https://github.com/ory/kratos/issues/3293))\n  ([8ae8783](https://github.com/ory/kratos/commit/8ae8783935292fb011b1018ac7417ed77eb6abb7))\n- Use the correct redirect_uri for linkedin social login\n  ([#3269](https://github.com/ory/kratos/issues/3269))\n  ([27ccecc](https://github.com/ory/kratos/commit/27ccecc1cd490eaa71da7f8235b4b0057b8f14fe))\n- Webhook config parse for settings flow\n  ([#3305](https://github.com/ory/kratos/issues/3305))\n  ([95ad94d](https://github.com/ory/kratos/commit/95ad94d08efdbb369caecaa64cd0a30058c34ed3))\n\n### Code Generation\n\n- Pin v1.0.0 release commit\n  ([41b7c51](https://github.com/ory/kratos/commit/41b7c51c1c6b3bdff9e9ea8bb5e455e3c15c5256))\n\n### Documentation\n\n- Fix typo in readme ([#3299](https://github.com/ory/kratos/issues/3299))\n  ([b40544e](https://github.com/ory/kratos/commit/b40544e427891f20cea6838e79f4dee5b52ea5d1))\n\n### Features\n\n- Add “provider id” parameter to kratos session\n  ([#3292](https://github.com/ory/kratos/issues/3292))\n  ([387f5a2](https://github.com/ory/kratos/commit/387f5a2711ca8eee97ad0f6bb2575ec9ba4797d9)),\n  closes [#3283](https://github.com/ory/kratos/issues/3283)\n- Add distroless and static images\n  ([#3350](https://github.com/ory/kratos/issues/3350))\n  ([1e65662](https://github.com/ory/kratos/commit/1e65662c92b107290466c20de38bbdc0571b596a))\n- Add return_to parameters to the `createLogout` handler\n  ([#3336](https://github.com/ory/kratos/issues/3336))\n  ([08fed36](https://github.com/ory/kratos/commit/08fed36973274ef294491d00811bc867f1537d62)):\n  - feat: add return_to parameters to the `createLogout` handler\n  - test: logout take over return_to from create to update\n  - test(e2e): logout return to\n  - test(e2e): logout return to\n  - test: logout return_to isnt applicable to react\n\n- Allow customization of JOIN predicate in QueryForCredentials\n  ([#3253](https://github.com/ory/kratos/issues/3253))\n  ([8785166](https://github.com/ory/kratos/commit/87851668e776404aabbfbc67af73a43ea3ee28fc))\n- Emit events for login/logout and registration\n  ([#3235](https://github.com/ory/kratos/issues/3235))\n  ([c784b7e](https://github.com/ory/kratos/commit/c784b7e7ed2834ca83c6db2326b735e78e5a75f2))\n- Forward `prompt` upstream parameter during OIDC flow\n  ([#3276](https://github.com/ory/kratos/issues/3276))\n  ([d290cb0](https://github.com/ory/kratos/commit/d290cb05bb4f63d04ec3763db127060e13c350dc)),\n  closes [#2709](https://github.com/ory/kratos/issues/2709)\n- Implement `crypt(3)` hashers\n  ([#3303](https://github.com/ory/kratos/issues/3303))\n  ([afe06db](https://github.com/ory/kratos/commit/afe06db95663cc0cb9704ba4f7014ed9bfb4de09)),\n  closes [#3291](https://github.com/ory/kratos/issues/3291):\n\n  This PR implements md5crypt, sha256crypt, sha512crypt, which are considered\n  legacy (like md5), but are used in legacy systems looking to convert to ory.\n  They use the existing format of crypt(5) (which is compliant to PHC).\n\n- Improve event types and capture more events\n  ([#3297](https://github.com/ory/kratos/issues/3297))\n  ([835fe13](https://github.com/ory/kratos/commit/835fe13d9ce81f7c0ed91dd2863a740fbb0c6209))\n- Lark OIDC provider ([#2925](https://github.com/ory/kratos/issues/2925))\n  ([f884dfb](https://github.com/ory/kratos/commit/f884dfbaa8aeba58b3b1595bd45e41f9b3e5a0e0))\n- Return to oauth flow after switching from login to other flows\n  ([#3212](https://github.com/ory/kratos/issues/3212))\n  ([a1fea6c](https://github.com/ory/kratos/commit/a1fea6c353768bbf154900766fbbe51f2a148554)):\n  - feat: return to oauth flow after switching from login to other flows\n  - feat(e2e): flows should have return_to set to hydra request_url\n  - u\n  - fix: override return_to URL on OAuth flows\n  - style: format\n  - fix: TestOAuth2Provider\n  - feat: config to opt into using OAuth request url as return_to\n  - chore: cleanup\n  - fix(e2e): oauth2 login flow switching to recovery\n  - feat(test): oauth2 login flow to recovery through oidc provider\n  - fix(e2e): oidc-provider registration\n  - chore: rename `oauth2_provider.return_to_enabled` to\n    `oauth2_provider.override_return_to`\n  - style: format\n  - chore: nit config description\n\n- Sort sessions by authenticated_at\n  ([#3324](https://github.com/ory/kratos/issues/3324))\n  ([46f92ff](https://github.com/ory/kratos/commit/46f92ffebf14d1cf4133ca37a2151e8c3aef9d2d)):\n\n  Closes https://github.com/ory/network/issues/295\n\n- Sqa metrics v2 ([#3300](https://github.com/ory/kratos/issues/3300))\n  ([98fe73f](https://github.com/ory/kratos/commit/98fe73faa75c56be47c19c61a780578ef24e7267))\n- Support exporting of all credential types\n  ([#3290](https://github.com/ory/kratos/issues/3290))\n  ([de6c857](https://github.com/ory/kratos/commit/de6c8574c9c6070458303f9b5caf7e8533f06b69)):\n\n  It's now possible to export all credential types (including passwords) when\n  calling the `getIdentity` SDK method.\n\n- Support OIDC flows for native apps\n  ([#3216](https://github.com/ory/kratos/issues/3216))\n  ([cb10609](https://github.com/ory/kratos/commit/cb106097210ac9a146738d06c20a4306c2345923)),\n  closes [#707](https://github.com/ory/kratos/issues/707):\n\n  Implements Social Sign In and OpenID Connect for native apps.\n\n### Tests\n\n- Run Playwright in CI ([#3259](https://github.com/ory/kratos/issues/3259))\n  ([342edec](https://github.com/ory/kratos/commit/342edeced4080a1b914000dfb8427196abebc596)):\n  - run Playwright in CI\n  - add cleanup for session token exchangers\n  - fixup: ci\n  - fix: compatibility between OIDC+code and other flows\n\n  This improves the compatibility between OIDC+code and other flows such as\n  TOTP, settings, password auth.\n  - Update persistence/sql/persister_cleanup_test.go\n  - fix: error handling with OIDC+Code\n  - fix: increase playwright timeout\n\n### Unclassified\n\n- @barnarddt @hperl feat: send emails via http api endpoint instead of smtp\n  (#1030) (#3341)\n  ([28b7b04](https://github.com/ory/kratos/commit/28b7b04a34eeba2d84de5c543f5ba8b41b38a129)),\n  closes [#1030](https://github.com/ory/kratos/issues/1030)\n  [#3341](https://github.com/ory/kratos/issues/3341)\n  [#1030](https://github.com/ory/kratos/issues/1030)\n  [#3008](https://github.com/ory/kratos/issues/3008):\n\n  This change adds a new delivery method to the courier called `mailer`. Similar\n  to SMS functionality it posts a templated Data model to a API endpoint. This\n  API can then send emails via a CRM or any other mechanism that it wants.\n\n  `Mailer` still uses the existing email data models so any new email added will\n  automatically be sent to the API/CRM as well.\n\n  ## Related issue(s)\n\n  Resolves https://github.com/ory/kratos/issues/2825\n\n# [0.13.0](https://github.com/ory/kratos/compare/v0.11.1...v0.13.0) (2023-04-18)\n\nWe’re excited to announce the release of Ory Kratos v0.13.0! This update brings\nmany enhancements and fixes, improving the user experience and overall\nperformance. Here are the highlights:\n\n- We’ve added new social sign-in options with Patreon OIDC and LinkedIn\n  providers, making it even easier for your users to register and log in.\n  Furthermore, we’ve introduced a new admin API that allows you to remove\n  specific 2nd factor credentials, giving you more control over your user\n  accounts.\n- Performance has been a key focus in this release. We’ve optimized the whoami\n  calls, parallelized the getIdentity and getSession calls, and made\n  asynchronous webhooks fully async. These improvements will result in faster\n  response times and a smoother experience for your users. Additionally, we’ve\n  implemented better tracing to help you diagnose and resolve issues more\n  effectively.\n- We’ve also made several updates to the webhook system. A new response.parse\n  configuration has been introduced, allowing you to update identity data during\n  registration. This includes admin/public metadata, identity traits,\n  enabling/disabling identity, and modifying verified/recovery addresses. Please\n  note that can_interrupt is now deprecated in favor of response.parse.\n- Lastly, we’ve made several important fixes, such as resolving the wrong\n  message ID on resend code buttons, implementing the offline scope as Google\n  expects, and improving the OIDC flow on duplicate account registration. We’ve\n  also added the ability to configure whether the system should notify unknown\n  recipients when attempting to recover an account or verify an address,\n  enhancing security with “anti-account-enumeration measures.”\n\nWe hope you enjoy these new features and improvements in Ory Kratos v0.13.0! All\nfeatures are already live on the Ory Network - the simplest, fastest and most\nscalable way to run Ory.\n\nPlease note that the v0.12.0 release was skipped due to CI issues.\n\nHead over to the changelog at\n[https://github.com/ory/kratos/blob/master/CHANGELOG.md](https://github.com/ory/kratos/blob/master/CHANGELOG.md)\nto read all the details. As always, we appreciate your feedback and support!\n\n## Breaking Changes\n\nBy default, Kratos no longer sends out these Emails. If you want to keep\nnotifying unknown addresses (keep the current behavior), set\n`selfservice.flows.recovery.notify_unknown_recipients` to `true` for recovery,\nor `selfservice.flows.verification.notify_unknown_recipients` for verification\nflows.\n\n### Bug Fixes\n\n- Access rules example ([#3178](https://github.com/ory/kratos/issues/3178))\n  ([a206772](https://github.com/ory/kratos/commit/a206772d78efed6febe783ee88dae92de80063d0))\n- Account experience redirects to verification page\n  ([#3195](https://github.com/ory/kratos/issues/3195))\n  ([2e96d75](https://github.com/ory/kratos/commit/2e96d75c2e0a1c9a884e2d3342725fb1983b495d))\n- Account settings broken on OIDC removal\n  ([#3185](https://github.com/ory/kratos/issues/3185))\n  ([61ae531](https://github.com/ory/kratos/commit/61ae531ba86636e1ad4d63e37df47ef76dfa5f29)),\n  closes [ory-corp/cloud#3514](https://github.com/ory-corp/cloud/issues/3514)\n- Add `after_verification_return_to` to sdk and api docs\n  ([#3097](https://github.com/ory/kratos/issues/3097))\n  ([c70704c](https://github.com/ory/kratos/commit/c70704cebafff7a92f32928273e4570abb3b1c3d)),\n  closes [#3096](https://github.com/ory/kratos/issues/3096)\n- Add `HydraLoginRequest` on flow creation\n  ([#3152](https://github.com/ory/kratos/issues/3152))\n  ([09312dd](https://github.com/ory/kratos/commit/09312dd2d7f89eadbae603e4c8891f39630a2570)),\n  closes [#3108](https://github.com/ory/kratos/issues/3108):\n\n  The oauth2_login_request field was missing when initially creating the login\n  flow.\n\n- Add missing `code` discriminator in updateVerificationFlow\n  ([#3213](https://github.com/ory/kratos/issues/3213))\n  ([21576be](https://github.com/ory/kratos/commit/21576bebc0d8c3796a4a16b1972ff42889814d61))\n- Add missing index ([#3181](https://github.com/ory/kratos/issues/3181))\n  ([756bed4](https://github.com/ory/kratos/commit/756bed4db3789428117ec105ac0713a52d610938))\n- Add mutex to test SMTP server setup/teardown\n  ([20c2359](https://github.com/ory/kratos/commit/20c2359407044c81850759e27b03c371cb0e4886))\n- Avoid unchecked casts from IdentityPool to PrivilegedIdentityPool\n  ([71d35dd](https://github.com/ory/kratos/commit/71d35ddd582b3c7081f66e0cdc0c43457816ab25))\n- Correctly apply patches to identity metadata\n  ([#3103](https://github.com/ory/kratos/issues/3103))\n  ([1193a56](https://github.com/ory/kratos/commit/1193a5681fbc25d03c1e26a4296fa0b9abd2452b)),\n  closes [#2950](https://github.com/ory/kratos/issues/2950)\n- Do not omit last page on identity list\n  ([#3169](https://github.com/ory/kratos/issues/3169))\n  ([f95f48a](https://github.com/ory/kratos/commit/f95f48a79395b7b99c7482c0974bc5188e007cc0))\n- Don't return 500 if active strategy is disabled\n  ([#3197](https://github.com/ory/kratos/issues/3197))\n  ([3a734c2](https://github.com/ory/kratos/commit/3a734c2dc2bd848033dbdc7d6116b8b6db6fa760))\n- Don't reuse ports in courier/SMTP tests\n  ([#3156](https://github.com/ory/kratos/issues/3156))\n  ([e260fcf](https://github.com/ory/kratos/commit/e260fcf06181ce9339edc729ab74826aa4be78cf))\n- Don't treat missing session as error in tracing\n  ([290d28a](https://github.com/ory/kratos/commit/290d28ada1a55b599af7e41e638de699a474f1d8))\n- Error messages in OpenAPI/Swagger / improve error messages from failed\n  webhooks and client timeouts\n  ([#3218](https://github.com/ory/kratos/issues/3218))\n  ([b1bdcd3](https://github.com/ory/kratos/commit/b1bdcd32828fcdbf65bc43b85b64df210ba4c646))\n- Handle upstream errors in patreon provider\n  ([#3032](https://github.com/ory/kratos/issues/3032))\n  ([39fa31f](https://github.com/ory/kratos/commit/39fa31f85deb3f015aa0f1b30b4a17e4b51d461b))\n- Identity.CopyWithoutCredentials\n  ([989c99d](https://github.com/ory/kratos/commit/989c99d6a32e02759a8a7a07606a90832afec460))\n- Implement offline scope in the way google expects\n  ([#3088](https://github.com/ory/kratos/issues/3088))\n  ([39043d4](https://github.com/ory/kratos/commit/39043d451e154af44123ba031381f0e3c10fbb00))\n- Improve webhook resilience\n  ([#3200](https://github.com/ory/kratos/issues/3200))\n  ([0a05d99](https://github.com/ory/kratos/commit/0a05d9941c6be549acfe65a78f4a8b21d6efbcdc)):\n  - fix: improve webhook logging\n  - chore: bump x\n  - feat: decouple context in PostRegistrationPostPersist hook\n\n- Invalid SQL syntax in ListIdentities\n  ([#3202](https://github.com/ory/kratos/issues/3202))\n  ([162ab9b](https://github.com/ory/kratos/commit/162ab9b5634329135b1b729ad401701019aca222)):\n\n  PostgresQL does not support `... WHERE x IN ( )` with an empty argument list.\n\n- Issuer missing from netid claims\n  ([#3080](https://github.com/ory/kratos/issues/3080))\n  ([dec7cbc](https://github.com/ory/kratos/commit/dec7cbc4286cbbe2d787b1f8998ee57054d7c95b)):\n\n  The NetID provider omits the issuer claim in the userinfo response. To resolve\n  this issue, the ID token returned by NetID is now validated and its `sub` and\n  `iss` values are used.\n\n- Lint errors and unused code\n  ([ae49ef0](https://github.com/ory/kratos/commit/ae49ef04ed24c23406a5639d34c2e81ab0130c75))\n- Make async webhooks fully async\n  ([#3111](https://github.com/ory/kratos/issues/3111))\n  ([342bfb0](https://github.com/ory/kratos/commit/342bfb0332d235a2d535493d586192815b7d4974))\n- Make session AAL satisfaction check resilient against a nil identity in the\n  session\n  ([5ab1a56](https://github.com/ory/kratos/commit/5ab1a56cfd41e95fbb30b8f93426a27e510c62c7)):\n\n  Also fix tracing.\n\n- Missing issuer regression in OIDC\n  ([#3220](https://github.com/ory/kratos/issues/3220))\n  ([52f0740](https://github.com/ory/kratos/commit/52f07402edac2624cb37c72c768737a785658d29)):\n\n  Closes https://github.com/ory/kratos/issues/3182 Closes\n  https://github.com/ory/kratos/issues/3040\n\n- Nolint comment\n  ([93e6501](https://github.com/ory/kratos/commit/93e6501c63a253336c081f156ada58458b83ef92))\n- Only return one result set for credentials_identifier\n  ([#3107](https://github.com/ory/kratos/issues/3107))\n  ([59f35d1](https://github.com/ory/kratos/commit/59f35d11e61a246d1079ac02cb8958ba81b37f75)),\n  closes [#3105](https://github.com/ory/kratos/issues/3105)\n- Orphaned webhook spans\n  ([a7f9414](https://github.com/ory/kratos/commit/a7f9414460eb214a8f2b2ff96a2b6b303721f806))\n- Re-use existing CSRF token in verification flows\n  ([#3188](https://github.com/ory/kratos/issues/3188))\n  ([08a3447](https://github.com/ory/kratos/commit/08a344761e049c64cffafca2f94c942468201d24)):\n  - fix: re-use existing CSRF token in verification flows\n  - chore: fix if/else\n\n- Reduce SQL tracing noise\n  ([1650426](https://github.com/ory/kratos/commit/1650426a2b59cd46035e5556ff8f69994602e88e))\n- Remove `http.Redirect` from `show_verification_ui` hook\n  ([#3238](https://github.com/ory/kratos/issues/3238))\n  ([054705b](https://github.com/ory/kratos/commit/054705b8c6c933d20b8fb45fcb2593a451cee685))\n- Remove network omit flag ([#3066](https://github.com/ory/kratos/issues/3066))\n  ([c629b72](https://github.com/ory/kratos/commit/c629b72be42001e3e1671d61cc8348373b686844))\n- Report correct errors for json schema validation\n  ([#3085](https://github.com/ory/kratos/issues/3085))\n  ([9477ea4](https://github.com/ory/kratos/commit/9477ea4a7bde6efa73ed94f61c2d4ed66fd43a08)):\n  - Implemented the translation of `jsonschema.ValidationError` to errors codes\n    documented\n    [here](https://www.ory.sh/docs/kratos/concepts/ui-user-interface#machine-readable-format)\n  - Added missing error codes for relevant schema errors | Validation | Name |\n    ID | | ------------------ | ------------------------------- | ------- | |\n    `maxLength` | ErrorValidationMaxLength | 4000017 | | `minimum` |\n    ErrorValidationMinimum. | 4000018 | | `exclusiveMinimum` |\n    ErrorValidationExclusiveMinimum | 4000019 | | `maximum` |\n    ErrorValidationMaximum | 4000020 | | `exclusiveMaximum` |\n    ErrorValidationExclusiveMaximum | 4000021 | | `multipleOf` |\n    ErrorValidationMultipleOf | 4000022 | | `maxItems` | ErrorValidationMaxItems\n    | 4000023 | | `minItems` | ErrorValidationMinItems | 4000024 | |\n    `uniqueItems` | ErrorValidationUniqueItems | 4000025 | | `type` |\n    ErrorValidationWrongType | 4000026 |\n  - Updated e2e tests to check these IDs explicitly\n\n- Respect the after recovery return to URL from config\n  ([#3141](https://github.com/ory/kratos/issues/3141))\n  ([3467fd3](https://github.com/ory/kratos/commit/3467fd3b860dd2ad915449e3fff7e4da2d2c61ca)):\n\n  Fixes https://github.com/ory-corp/cloud/issues/1405\n\n- Set DB connection max idle time\n  ([8d4762c](https://github.com/ory/kratos/commit/8d4762c1bffad14c94ac69575e488fc67d3f5dde))\n- Set proper maxAge for session cookies\n  ([#3209](https://github.com/ory/kratos/issues/3209))\n  ([1180c05](https://github.com/ory/kratos/commit/1180c051b34eb5de786d6b4e4bd94e863f60d06a)),\n  closes [#3208](https://github.com/ory/kratos/issues/3208)\n- Sqa config values unified across projects\n  ([#3237](https://github.com/ory/kratos/issues/3237))\n  ([523b93f](https://github.com/ory/kratos/commit/523b93fd1fe8715d06aeedc2db0ac072dfcafb71))\n- Test contract names\n  ([e9ac00b](https://github.com/ory/kratos/commit/e9ac00b3941641a955f5d8f32f25a4031c87a726))\n- Use correct names in WebAuthN dialogs\n  ([#3215](https://github.com/ory/kratos/issues/3215))\n  ([3bc1ff0](https://github.com/ory/kratos/commit/3bc1ff0e63c885c1db08e3d1332d959799edb0a8))\n- Use type alias instead of type definition\n  ([#3148](https://github.com/ory/kratos/issues/3148))\n  ([dba3803](https://github.com/ory/kratos/commit/dba38032d5939ff7286560ec19d83a89fe0410ce))\n- Webhook tracing and missing defers\n  ([#3145](https://github.com/ory/kratos/issues/3145))\n  ([46eb063](https://github.com/ory/kratos/commit/46eb063f414a0ad9b901407cf781002ccb97ad93))\n- Wrong context in logout trace span\n  ([#3168](https://github.com/ory/kratos/issues/3168))\n  ([b9ccccf](https://github.com/ory/kratos/commit/b9ccccf0f1b6a5ba903293133b2be15b528c8308))\n\n### Code Generation\n\n- Pin v0.13.0 release commit\n  ([349d0ee](https://github.com/ory/kratos/commit/349d0ee1899e2ff0f81587b528c04fa0287e5546))\n\n### Code Refactoring\n\n- Identity persistence ([#3101](https://github.com/ory/kratos/issues/3101))\n  ([ceb5cc2](https://github.com/ory/kratos/commit/ceb5cc2b8a78be2f5b65d9a026c01ff0afe106af))\n\n### Documentation\n\n- Fix broken docs links and code example to get verification flow\n  ([#3170](https://github.com/ory/kratos/issues/3170))\n  ([bdbddcc](https://github.com/ory/kratos/commit/bdbddcce2909b290e2e04dee493519b842715ab4))\n- Update security email ([#3164](https://github.com/ory/kratos/issues/3164))\n  ([9252f5a](https://github.com/ory/kratos/commit/9252f5a3c746927a2f537efc39cb1eb0aba167a5))\n\n### Features\n\n- Add a new admin API to remove a specific 2nd factor credential\n  ([#2962](https://github.com/ory/kratos/issues/2962))\n  ([44556a4](https://github.com/ory/kratos/commit/44556a468ef233b18fd0f16a83a4e1b2e5f05dcf)),\n  closes [#2505](https://github.com/ory/kratos/issues/2505)\n- Add API to batch insert identities\n  ([#3157](https://github.com/ory/kratos/issues/3157))\n  ([829bda7](https://github.com/ory/kratos/commit/829bda701acfd6706ffd72845414d177895ff8fe)),\n  closes [ory/network#266](https://github.com/ory/network/issues/266)\n- Add Inspect option to driver\n  ([8aa75e9](https://github.com/ory/kratos/commit/8aa75e97e4bfee37e7cf551173b516c6244786ff))\n- Add patreon oidc provider ([#3021](https://github.com/ory/kratos/issues/3021))\n  ([20ea29e](https://github.com/ory/kratos/commit/20ea29e018b33231cf6b2743de74d2233f756c2a))\n- Add test to verify GetIdentityConfidential expands everything\n  ([#3217](https://github.com/ory/kratos/issues/3217))\n  ([f088ccd](https://github.com/ory/kratos/commit/f088ccdf462f5e6373aceb142caa181d98975a09))\n- Add token prefixes to session and logout tokens\n  ([#3132](https://github.com/ory/kratos/issues/3132))\n  ([8210cd0](https://github.com/ory/kratos/commit/8210cd09200d370b101072649fddd1ad9a7f32a9)):\n\n  This feature adds token prefixes to Ory session and logout tokens:\n  - `ory_st_`: Ory session token prefix\n  - `ory_lt_`: Logout token prefix\n\n- Add upstream parameters to oidc provider\n  ([#3138](https://github.com/ory/kratos/issues/3138))\n  ([b6b1679](https://github.com/ory/kratos/commit/b6b1679c3bd053cd08ff8f26c762735e380fed67)),\n  closes [#3127](https://github.com/ory/kratos/issues/3127)\n  [#2069](https://github.com/ory/kratos/issues/2069):\n\n  This PR introduces the upstream OIDC query parameters `login_hint` and `hd`.\n\n  To send additional upstream parameters the form can post this on a login,\n  registration or settings link submit. For example the form below does an OIDC\n  flow to Google. We can now add additional parameters such as `login_hint` and\n  `hd` to the upstream request to Google login with a pre-filled email\n  `email@example.com`:\n\n  ```html\n  <form action=\"https://kratos/self-service/login?flow=\">\n    <input type=\"submit\" name=\"provider\" value=\"google\" />\n    <input\n      type=\"hidden\"\n      name=\"upstream_parameters.login_hint\"\n      value=\"email@example.com\"\n    />\n    <input type=\"hidden\" name=\"upstream_parameters.hd\" value=\"example.com\" />\n  </form>\n  ```\n\n- Allow importing (salted) SHA hashing algorithms\n  ([#2741](https://github.com/ory/kratos/issues/2741))\n  ([132255e](https://github.com/ory/kratos/commit/132255eff24a3f5a7fc2249a0ecf9b8716a8f1e7)),\n  closes [#2422](https://github.com/ory/kratos/issues/2422)\n- Allow passing transient data from registration to webhook\n  ([#3104](https://github.com/ory/kratos/issues/3104))\n  ([4a3a076](https://github.com/ory/kratos/commit/4a3a07657d2eb2a39d777565b58882cb48e928fa))\n- Don't pre-generate UUIDs for transient objects\n  ([e17f307](https://github.com/ory/kratos/commit/e17f307732f8ced34727d5f3a70929866a0595e0))\n- Drop unused index ([#3165](https://github.com/ory/kratos/issues/3165))\n  ([852dea9](https://github.com/ory/kratos/commit/852dea90881a7c9abdbfc127a2e8d1cc0aacb166))\n- Even more tracing of hidden HTTP requests\n  ([9d8b1e2](https://github.com/ory/kratos/commit/9d8b1e223072e66d284c9e7890060678b77c1d4f))\n- Identity by identifier ([#3077](https://github.com/ory/kratos/issues/3077))\n  ([c288d4d](https://github.com/ory/kratos/commit/c288d4d136bca1a9ed3931b4827967eb44e80ede))\n- Improve tracing span naming in hooks\n  ([bf828d3](https://github.com/ory/kratos/commit/bf828d3f5d56a963529e98958f4039f0dc569979))\n- Improve webhook diagnostics\n  ([d4eb2f6](https://github.com/ory/kratos/commit/d4eb2f6b728a211f1e1454559c2eff73f2f77936))\n- Improved oidc flow on duplicate account registration\n  ([#3151](https://github.com/ory/kratos/issues/3151))\n  ([4d2fda4](https://github.com/ory/kratos/commit/4d2fda453b16349589e941af06fcce312c2e5c37)):\n\n  This PR improves the OIDC registration flow when a duplicate account error\n  happens.\n\n  Currently the flow looks as follows:\n  1. User registers with password (or other credentials)\n  2. User forgot they registered with password and tries to login through an\n     OIDC provider (e.g. Google)\n  3. Kratos attempts a registration since the OIDC credentials do not exist\n  4. (optional) User needs to add missing traits (e.g. full name) which could\n     not be retrieved from the OIDC provider\n  5. User gets a duplicate account error with a \"Continue\" button.\n  6. After submitting the \"Continue\" button the flow continues again to the OIDC\n     provider, back to Kratos and redirects to UI with duplicate error (Steps 3\n     to 5)\n\n  Instead of causing a confusing redirect loop we should show the user the error\n  with a fresh login flow (since the account exists). This also gives the user\n  the option to do a recovery flow.\n  1. User registers with password (or other credentials)\n  2. User forgot they registered with password and tries to login through an\n     OIDC provider (e.g. Google)\n  3. Kratos attempts a registration since the OIDC credentials do not exist\n  4. (optional) User needs to add missing traits\n  5. User is returned to a Login flow with the duplication error\n\n- Let DB generate ID for session devices\n  ([62402c7](https://github.com/ory/kratos/commit/62402c7bed3c57ef5b957572e4b84f56d9c530ae))\n- Make notification to unknown recipients configurable\n  ([#3075](https://github.com/ory/kratos/issues/3075))\n  ([1a5ead4](https://github.com/ory/kratos/commit/1a5ead43a60e7a0388617877a9f16d1dec61459b)),\n  closes [#2345](https://github.com/ory/kratos/issues/2345)\n  [#2585](https://github.com/ory/kratos/issues/2585):\n\n  Added the ability to configure whether the system should notify unknown\n  recipients, if some tries to recover their account or verify their address\n  (\"anti-account-enumeration measures\").\n\n- Make password validator (HIBP check) cancelable and add tracing\n  ([28f8914](https://github.com/ory/kratos/commit/28f8914bfb8276d38e08b9be9a3ad1c59d1410bb))\n- Parallelize get identity and session calls\n  ([#3023](https://github.com/ory/kratos/issues/3023))\n  ([6393519](https://github.com/ory/kratos/commit/6393519977bc3d804673b5669166e07c561f1c79))\n- Refactor credentials fetching\n  ([#3183](https://github.com/ory/kratos/issues/3183))\n  ([590269f](https://github.com/ory/kratos/commit/590269f91e24203f987124cfbf11d31c04c1d35c)):\n\n  This change revamps the way we fetch identity credentials. We no longer need\n  most of the helper fields for gobuffalo/pop inside the `Identity` and\n  `Credentials` structures, and we collect all the credentials in one joined\n  query rather than using pop's `EagerPreload` functionality.\n\n- Return hydra error messages\n  ([b3d037b](https://github.com/ory/kratos/commit/b3d037b33b248f1873f09d641e5d61376bcfde80))\n- Return verification flow ID after registration flow\n  ([#3144](https://github.com/ory/kratos/issues/3144))\n  ([eb854be](https://github.com/ory/kratos/commit/eb854becd9fe75213fba6ebe4283cc4ed2c9d128)),\n  closes [#2975](https://github.com/ory/kratos/issues/2975)\n- Show \"continue\" screen after successful verification\n  ([#3090](https://github.com/ory/kratos/issues/3090))\n  ([fb6b160](https://github.com/ory/kratos/commit/fb6b1600d3d75e5d11fb98445c499a6218e6b869)):\n\n  The `link` strategy for verification now shows a confirmation screen with a\n  \"continue\" link after successful verification, aligning its behavior to the\n  `code` strategy.\n\n  Also fixes a bug, where the `default_browser_return_url` of the verification\n  flow was not respected when using the code strategy.\n\n  Closes https://github.com/ory-corp/cloud#3925 Fixes\n  https://github.com/ory/network#228 Fixes\n  https://github.com/ory/network/issues/224\n\n- Social sign in via linkedin\n  ([#3079](https://github.com/ory/kratos/issues/3079))\n  ([5de6bf4](https://github.com/ory/kratos/commit/5de6bf46aba6c13f927ef1c4c425322a34063ca9)),\n  closes [#2856](https://github.com/ory/kratos/issues/2856):\n\n  Adds LinkedIn as a social sign in provider.\n\n- Webhooks that update identities\n  ([2cbee3e](https://github.com/ory/kratos/commit/2cbee3e8eea6bac376faf9382bf5b15acb732f03)),\n  closes [#2161](https://github.com/ory/kratos/issues/2161):\n\n  Introduces a new configuration `response.parse` in webhooks. This enables\n  updating of identity data during registration, including admin/public\n  metadata, identity traits, enabling/disabling identity, and modifying\n  verified/recovery addresses.\n\n  Please note that `can_interrupt` is being deprecated in favor of\n  `response.parse`.\n\n### Tests\n\n- **e2e:** Fix compile errors in commands\n  ([#3179](https://github.com/ory/kratos/issues/3179))\n  ([0002668](https://github.com/ory/kratos/commit/00026682b548b1f33e255a8ee865d90ea127a254))\n- Parallelize several unit tests\n  ([#3081](https://github.com/ory/kratos/issues/3081))\n  ([5403f86](https://github.com/ory/kratos/commit/5403f863d21a6fb5ba4b8572fb054d52e5a8205d))\n\n### Unclassified\n\n- Revert \"fix: do not omit last page on identity list (#3169)\" (#3184)\n  ([73b5f13](https://github.com/ory/kratos/commit/73b5f13935ef051aae5538cf3d189bb430ea49ae)),\n  closes [#3169](https://github.com/ory/kratos/issues/3169)\n  [#3184](https://github.com/ory/kratos/issues/3184):\n\n  This reverts commit f95f48a79395b7b99c7482c0974bc5188e007cc0.\n\n# [0.11.1](https://github.com/ory/kratos/compare/v0.11.0...v0.11.1) (2023-01-14)\n\n- Fixed several bugs to improve overall stability.\n- Optimized performance for faster load times and smoother operation.\n- Improved tracing capabilities for better debugging and issue resolution.\n\nWe are constantly working to improve Ory Kratos and this release is no\nexception. Thank you for using Ory and please let us know if you have any\nfeedback or encounter any issues.\n\n## Breaking Changes\n\nThe `/admin/courier/messages` endpoint now uses `keysetpagination` instead.\n\n### Bug Fixes\n\n- Add missing indexes ([#2973](https://github.com/ory/kratos/issues/2973))\n  ([bbb3995](https://github.com/ory/kratos/commit/bbb399572926bd433928b22764f7b3558bb0c21d))\n- Add missing indexes for identity delete\n  ([#2952](https://github.com/ory/kratos/issues/2952))\n  ([dc311f9](https://github.com/ory/kratos/commit/dc311f9a9dc0dbb26e2375b3cd4232a4e8cccb61)):\n\n  This significantly improves the performance of identity deletes.\n\n- Cors headers not added to the response\n  [#2922](https://github.com/ory/kratos/issues/2922)\n  ([#2934](https://github.com/ory/kratos/issues/2934))\n  ([1ed6839](https://github.com/ory/kratos/commit/1ed6839369baeecc99610d9f04d78dfee53ad72a))\n- Dont reset to false ([#2965](https://github.com/ory/kratos/issues/2965))\n  ([ae8ad7b](https://github.com/ory/kratos/commit/ae8ad7be5b6f3dbb9142bee55448a71c7df44e52))\n- Flaky test now stable\n  ([4e5dcd0](https://github.com/ory/kratos/commit/4e5dcd0df6baffda8b15eda37fd7a247793f3297))\n- Listing sessions query ([#2958](https://github.com/ory/kratos/issues/2958))\n  ([3e06c99](https://github.com/ory/kratos/commit/3e06c991ad557f4629ef7412c256ede2386a7bed)),\n  closes [#2930](https://github.com/ory/kratos/issues/2930)\n- Missing index on courier list count\n  ([#3002](https://github.com/ory/kratos/issues/3002))\n  ([3b50711](https://github.com/ory/kratos/commit/3b507110d6e0296e90d3c495515bf2a066b7c09b))\n- Pin geckodriver version to bypass GitHub API quota\n  ([#2972](https://github.com/ory/kratos/issues/2972))\n  ([585cb9e](https://github.com/ory/kratos/commit/585cb9e79be5de8b3d684313edb72bb703ffaa78))\n- Quickstart demos ([#2940](https://github.com/ory/kratos/issues/2940))\n  ([a7720b2](https://github.com/ory/kratos/commit/a7720b2ba389c08c83c4f3118b83e1fc044773cc))\n- Remove duplicate query in GetIdentity\n  ([#2987](https://github.com/ory/kratos/issues/2987))\n  ([33b01bb](https://github.com/ory/kratos/commit/33b01bbb0e53fc8ac0127531de72ee1b680be656))\n- Remove unused x-session-cookie parameter\n  ([#2983](https://github.com/ory/kratos/issues/2983))\n  ([56b5c26](https://github.com/ory/kratos/commit/56b5c26e666af2442b3e99449b62b2f76a3a4677)):\n\n  This patch removes the undocumented and experimental `X-Session-Cookie` header\n  from the `/sessions/whoami` endpoint.\n\n- Resilient social sign in ([#3011](https://github.com/ory/kratos/issues/3011))\n  ([ca35b45](https://github.com/ory/kratos/commit/ca35b45a26c6781be81086a7677344fc165dac9f))\n- Respect `return_to` URL parameter in registration flow when the user is\n  already registered ([#2957](https://github.com/ory/kratos/issues/2957))\n  ([3462ce1](https://github.com/ory/kratos/commit/3462ce1512d03529b613421a69bcf4c1d5e98e08))\n- Set accept header for GitLab\n  ([#2998](https://github.com/ory/kratos/issues/2998))\n  ([e892113](https://github.com/ory/kratos/commit/e892113cc00a010490492def7f128bfb5c15b8de))\n- Set config at the start\n  ([e58bc6e](https://github.com/ory/kratos/commit/e58bc6e9bacd5c9c6ee9369beb843a4c54059ae2))\n- Spurious cancelation of async webhooks, better tracing\n  ([#2969](https://github.com/ory/kratos/issues/2969))\n  ([72de640](https://github.com/ory/kratos/commit/72de640bad75da29424222bd613a21d10e1811ec)):\n\n  Previously, async webhooks (response.ignore=true) would be canceled early once\n  the incoming Kratos request was served and it's associated context released.\n  We now dissociate the cancellation of async hooks from the normal request\n  processing flow.\n\n- TOTP internal context after saving settings\n  ([#2960](https://github.com/ory/kratos/issues/2960))\n  ([8b647b1](https://github.com/ory/kratos/commit/8b647b1f54bb674982b982ce483fbd877e42c43a)),\n  closes [#2680](https://github.com/ory/kratos/issues/2680)\n- Update pquerna/otp to fix TOTP URL encoding\n  ([#2951](https://github.com/ory/kratos/issues/2951))\n  ([7248636](https://github.com/ory/kratos/commit/72486368f5403c02772e4a99ed9edc34e84c217c)):\n\n  v1.4.0 fixes generating TOTP URLs. Query params now use %20 instead of + to\n  encode spaces. + was not correctly interpreted by some Android authenticator\n  apps, and would show up in the issuer name, e.g. \"My+Issuer\" instead of \"My\n  Issuer\".\n\n- Update year\n  ([d77e2cf](https://github.com/ory/kratos/commit/d77e2cf56ceab4c73e1c2fd579d43ae25a19d345))\n- Webhook tracing instrumentation+memory leak\n  ([f0044a3](https://github.com/ory/kratos/commit/f0044a365b39a5f940d6d268977744f8fcb2e49b))\n\n### Code Generation\n\n- Pin v0.11.1 release commit\n  ([41595c5](https://github.com/ory/kratos/commit/41595c52cf48e2bae81b1a901577062cc6e3dc06))\n\n### Documentation\n\n- Improve api headline ([#2989](https://github.com/ory/kratos/issues/2989))\n  ([fc2787b](https://github.com/ory/kratos/commit/fc2787ba9a5cb9088a76b7ec25752d75ef399281))\n\n### Features\n\n- Add client IP to span events\n  ([7ce3a74](https://github.com/ory/kratos/commit/7ce3a7471243898e111ca3e2b5d1346131c55dae))\n- Add NID to logs in courier\n  ([#2956](https://github.com/ory/kratos/issues/2956))\n  ([b407aa9](https://github.com/ory/kratos/commit/b407aa9427382f38dd8a992a6998202a7b6ba83a))\n- Improve error message when no session is found\n  ([#2988](https://github.com/ory/kratos/issues/2988))\n  ([7ad2b97](https://github.com/ory/kratos/commit/7ad2b970089cee2209b3afeaaffd7e04f803918d))\n- Improve tracing ([#2992](https://github.com/ory/kratos/issues/2992))\n  ([04d0280](https://github.com/ory/kratos/commit/04d0280ca1338b93ac6e3026a8a2d852fbb46ef2))\n- Remove duplicate queries from whoami calls\n  ([#2995](https://github.com/ory/kratos/issues/2995))\n  ([b50a222](https://github.com/ory/kratos/commit/b50a22298eedef30a45979866163921604bc698a)),\n  closes [#2402](https://github.com/ory/kratos/issues/2402):\n\n  Introduces an expand API to the identity persister which greatly improves\n  whoami performance.\n\n- Require verification on login\n  ([#2927](https://github.com/ory/kratos/issues/2927))\n  ([efb8ae8](https://github.com/ory/kratos/commit/efb8ae89cbc31477c2696a0df4c89d6dbf856d27))\n- Store errors of courier message\n  ([#2914](https://github.com/ory/kratos/issues/2914))\n  ([fc7aa86](https://github.com/ory/kratos/commit/fc7aa86545f9e74c22738891af92abafe0030d7f))\n\n### Tests\n\n- Improve parallelization\n  ([e8e8ce5](https://github.com/ory/kratos/commit/e8e8ce5eb3713f28ce1c9a05564ec7f74b48ab4d))\n- Regenerate csrf if verification flow expired\n  ([#2455](https://github.com/ory/kratos/issues/2455))\n  ([7025081](https://github.com/ory/kratos/commit/7025081b76171ce0a8f312a7b671aead1bb21215))\n- Update integrity snapshots\n  ([#3000](https://github.com/ory/kratos/issues/3000))\n  ([6d26e5c](https://github.com/ory/kratos/commit/6d26e5c735a28ecb8b2d8cd142751ef679e19e86))\n\n# [0.11.0](https://github.com/ory/kratos/compare/v0.11.0-alpha.0.pre.2...v0.11.0) (2022-12-02)\n\nThe 2022 winter release of Ory Kratos is here, and we are extremely excited to\nshare with you some of the highlights included:\n\n- Ory Kratos now supports verification and recovery codes, which replace are now\n  the default strategy and should be used instead of magic links.\n- Import of MD5-hashed passwords is now supported.\n- Ory Kratos can now act as the login app for the Ory Hydra Consent & Login Flow\n  using the `oauth2_provider.url` configuration value.\n- Ory Kratos' SDK is now released as version 1. Learn more in the\n  [upgrade guide](https://www.ory.sh/docs/guides/upgrade/sdk-v1).\n- New APIs are available to manage Ory Sessions.\n- Ory Sessions now contain device information.\n- Added all claims to the Social Sign-In data mapper as well as the option to\n  customize admin and public metadata.\n- Add webhooks that can block the request, useful to do some additional\n  validation.\n- Add asynchronous webhooks which do not block the request.\n- A CLI helper to clean up stale data.\n\nPlease read the changelog carefully to identify changes which might affect you.\nAlways test upgrading with a copy of your production system before applying the\nupgrade in production.\n\n### Code Generation\n\n- Pin v0.11.0 release commit\n  ([59c30b6](https://github.com/ory/kratos/commit/59c30b6860b56990e132416366e0ae6abe7a275f))\n\n### Features\n\n- Forward parsed request cookies to webhook Jsonnet snippet\n  ([#2917](https://github.com/ory/kratos/issues/2917))\n  ([70ed068](https://github.com/ory/kratos/commit/70ed068debe7a711ba36e2eb4fcf60be8cae4681)):\n\n  Request cookies were already available in raw form in the ctx.request_headers\n  top-level argument to the Jsonnet snippet. Parsing cookies in Jsonnet is\n  tedious and error-prone, though, so we parse them internally for convenience.\n\n# [0.11.0-alpha.0.pre.2](https://github.com/ory/kratos/compare/v0.10.1...v0.11.0-alpha.0.pre.2) (2022-11-28)\n\nautogen: pin v0.11.0-alpha.0.pre.2 release commit\n\n## Breaking Changes\n\nThis patch changes the behavior of the recovery flow. It introduces a new\nstrategy for account recovery that sends out short \"one-time passwords\" (`code`)\nthat a user can use to prove ownership of their account and recovery access to\nit. This PR also updates the default recovery strategy to `code`.\n\nThis patch invalidates recovery flows initiated using the Admin API. Please\nre-generate any admin-generated recovery flows and tokens.\n\nThis is a breaking change, as it removes the `courier.message_ttl` config key\nand replaces it with a counter `courier.message_retries`.\n\nCloses https://github.com/ory/kratos/issues/402 Closes\nhttps://github.com/ory/kratos/issues/1598\n\nSDK Method `getJsonSchema` was renamed to `getIdentitySchema`.\n\n### Bug Fixes\n\n- Active attribute based off IsActive checks\n  ([#2901](https://github.com/ory/kratos/issues/2901))\n  ([bcbf68e](https://github.com/ory/kratos/commit/bcbf68e716aa62f684acbe91e8c35f6c006a4706))\n- Add issuerURL for apple id\n  ([#2565](https://github.com/ory/kratos/issues/2565))\n  ([2aeb0a2](https://github.com/ory/kratos/commit/2aeb0a210e6e6433f1a9d9e6a75b21b8e3083239)):\n\n  No issuer url was specified when using the Apple ID provider, this forced\n  usersers to manually enter it in the provider config.\n\n  This PR adds the Apple ID issuer url to the provider simplifying the setup.\n\n- Add missing go.mod to docker build\n  ([7c4964e](https://github.com/ory/kratos/commit/7c4964ef65769b40f1ec572a87c2c4106a800bf9))\n- Add support for verified Graph API calls for facebook oidc provider\n  ([#2547](https://github.com/ory/kratos/issues/2547))\n  ([1ba7c66](https://github.com/ory/kratos/commit/1ba7c66fc4897b676690f0ac701a0b68aee4f151))\n- Admin recovery CSRF & duplicate form elements\n  ([#2846](https://github.com/ory/kratos/issues/2846))\n  ([de80b7f](https://github.com/ory/kratos/commit/de80b7f508afdd56f5d8396f03919bd9a98e49d3))\n- Bump docker image ([#2594](https://github.com/ory/kratos/issues/2594))\n  ([071c885](https://github.com/ory/kratos/commit/071c885d8231a1a66051002ecfcff5c8e5237085))\n- Bump graceful to deal with http header timeouts\n  ([9ce2d26](https://github.com/ory/kratos/commit/9ce2d260338f020e2da077e81464e520883f582b))\n- Cache migration status ([#2631](https://github.com/ory/kratos/issues/2631))\n  ([9020738](https://github.com/ory/kratos/commit/902073836e4dcf6dc87776921e7988d795943718)):\n\n  See https://github.com/ory-corp/cloud/issues/2691\n\n- Check return code of ms graphapi /me request.\n  ([#2647](https://github.com/ory/kratos/issues/2647))\n  ([3f490a3](https://github.com/ory/kratos/commit/3f490a31cddc53ce5d9958454f41c352580904c9))\n- **cli:** Dry up code ([#2572](https://github.com/ory/kratos/issues/2572))\n  ([d1b6b40](https://github.com/ory/kratos/commit/d1b6b40aa9dcc7a3ec9237eec28c4fa55f0b8627))\n- Codecov ([#2879](https://github.com/ory/kratos/issues/2879))\n  ([e446c5a](https://github.com/ory/kratos/commit/e446c5a53dbe9963e8047a3e9ca443fa6a7e64eb))\n- Correct name of span on recovery code deletion\n  ([#2823](https://github.com/ory/kratos/issues/2823))\n  ([44f775f](https://github.com/ory/kratos/commit/44f775f45d47eff63379d77a2339b824a6ede235))\n- Correctly calculate `expired_at` timestamp for FlowExpired errors\n  ([#2836](https://github.com/ory/kratos/issues/2836))\n  ([ddde43e](https://github.com/ory/kratos/commit/ddde43ec0d77a1214cd03e1f3e48ab4c34193779))\n- Debugging Docker setup ([#2616](https://github.com/ory/kratos/issues/2616))\n  ([aaabe75](https://github.com/ory/kratos/commit/aaabe754659b96d2a5b727c4cada3ec300624434))\n- Disappearing title label on verification and recovery flow\n  ([#2613](https://github.com/ory/kratos/issues/2613))\n  ([29aa3b6](https://github.com/ory/kratos/commit/29aa3b6c37b3a173dcfeb02fdad4abc83774bc0b)),\n  closes [#2591](https://github.com/ory/kratos/issues/2591)\n- Distinguish credential types properly when collecting identifiers\n  ([#2873](https://github.com/ory/kratos/issues/2873))\n  ([705f7b1](https://github.com/ory/kratos/commit/705f7b105c98b1d68b3e35d6e6893e9cfb661548))\n- Do not crash process on invalid smtp url\n  ([#2890](https://github.com/ory/kratos/issues/2890))\n  ([c5d3ebc](https://github.com/ory/kratos/commit/c5d3ebc6927f7293ee05b65aee745a19ec96ce77)):\n\n  Closes https://github.com/ory-corp/cloud/issues/3321\n\n- Do not double-commit webhooks on registration\n  ([#2888](https://github.com/ory/kratos/issues/2888))\n  ([88e75d9](https://github.com/ory/kratos/commit/88e75d997348450b1a2a3e4619bcbd614a5582e8))\n- Do not invalidate recovery addr on update\n  ([#2699](https://github.com/ory/kratos/issues/2699))\n  ([1689bb9](https://github.com/ory/kratos/commit/1689bb9f0a52387f699568da6bc773929b1201ae))\n- **docker:** Add missing dependencies\n  ([#2643](https://github.com/ory/kratos/issues/2643))\n  ([c589520](https://github.com/ory/kratos/commit/c589520ff865cefdb287e597b9e858851a778755))\n- **docker:** Update images\n  ([b5f80c1](https://github.com/ory/kratos/commit/b5f80c1198e4bb9ed392521daca934548eb21ee6))\n- Duplicate messages in recovery flow\n  ([#2592](https://github.com/ory/kratos/issues/2592))\n  ([43fcc51](https://github.com/ory/kratos/commit/43fcc51b9bf6996fc4f7b0ef797189eb8f3978dc))\n- Express e2e tests for new account experience\n  ([#2708](https://github.com/ory/kratos/issues/2708))\n  ([84ea0cf](https://github.com/ory/kratos/commit/84ea0cf4c72b14f246835d435d22a31f96d9e644))\n- Format\n  ([0934def](https://github.com/ory/kratos/commit/0934defff7a0d56e712af98c1cec87c60b3c934b))\n- Format check stage in the CI\n  ([#2737](https://github.com/ory/kratos/issues/2737))\n  ([bbe4463](https://github.com/ory/kratos/commit/bbe44632de77cfb3d4983b68647107d914cd4c46))\n- Gosec false positives\n  ([e3e7ed0](https://github.com/ory/kratos/commit/e3e7ed08f5ce47fc794bd5c093018cee51baf689))\n- Identity sessions list response includes pagination headers\n  ([#2763](https://github.com/ory/kratos/issues/2763))\n  ([0c2efa2](https://github.com/ory/kratos/commit/0c2efa2d4345c035649208a71332a64c225313c3)),\n  closes [#2762](https://github.com/ory/kratos/issues/2762)\n- **identity:** Migrate identity_addresses to lower case\n  ([#2517](https://github.com/ory/kratos/issues/2517))\n  ([c058e23](https://github.com/ory/kratos/commit/c058e23599d994e12b676e87f7282c1f2b2e089c)),\n  closes [#2426](https://github.com/ory/kratos/issues/2426)\n- Ignore commata in HIBP response\n  ([0856bd7](https://github.com/ory/kratos/commit/0856bd719b7e06a6d2163bf428ff6513d86376db))\n- Ignore CSRF for session extension on public route\n  ([866b472](https://github.com/ory/kratos/commit/866b472750fba7bf498d359796f24867af7270ad))\n- Ignore error explicitly\n  ([772d596](https://github.com/ory/kratos/commit/772d5968d5a0cb7ac9415cfb2b1e9e86ae3a3131))\n- Improve migration status speed\n  ([#2637](https://github.com/ory/kratos/issues/2637))\n  ([a2e3c41](https://github.com/ory/kratos/commit/a2e3c41f9e513e1de47f6320f6a10acd1fed5eea))\n- Include flow id in use recovery token query\n  ([#2679](https://github.com/ory/kratos/issues/2679))\n  ([d56586b](https://github.com/ory/kratos/commit/d56586b028d79387886f880c1455edb5e4df2209)):\n\n  This PR adds the `selfservice_recovery_flow_id` to the query used when \"using\"\n  a token in the recovery flow.\n\n  This PR also adds a new enum field for `identity_recovery_tokens` to\n  distinguish the two flows: admin versus self-service recovery.\n\n- Include metadata_admin in admin identity list response\n  ([#2791](https://github.com/ory/kratos/issues/2791))\n  ([aa698e0](https://github.com/ory/kratos/commit/aa698e03a3a96abf1563aea24273735bd9cc412d)),\n  closes [#2711](https://github.com/ory/kratos/issues/2711)\n- Incorrect swagger annotation for `getSession`\n  ([#2891](https://github.com/ory/kratos/issues/2891))\n  ([797ea68](https://github.com/ory/kratos/commit/797ea6857e29e5477e0769af5dd51dd7e43080b2))\n- **lint:** Fixed lint error causing ci failures\n  ([4aab5e0](https://github.com/ory/kratos/commit/4aab5e0114dd02b8b0ce45376a0fe4bf11e38221))\n- Make `courier.TemplateType` an enum\n  ([#2875](https://github.com/ory/kratos/issues/2875))\n  ([65aeb0a](https://github.com/ory/kratos/commit/65aeb0a7fd90bfbc81f68b77141f8271aef011fe))\n- Make hydra consistently localhost\n  ([70211a1](https://github.com/ory/kratos/commit/70211a17a452d5ced8317822afda3f8e6185cc71))\n- Make ID field in VerifiableAddress struct optional\n  ([#2507](https://github.com/ory/kratos/issues/2507))\n  ([0844b47](https://github.com/ory/kratos/commit/0844b47c30851c548d46273927afee103cdc0e97)),\n  closes [#2506](https://github.com/ory/kratos/issues/2506)\n- Make servicelocator explicit\n  ([4f841da](https://github.com/ory/kratos/commit/4f841dae5423acf3514d50add9e99d28bc339fbb))\n- Make swagger/openapi go 1.19 compatible\n  ([fec6772](https://github.com/ory/kratos/commit/fec6772739129e0d5bb4103c717b1ac60df45aa8))\n- Mark gosec false positives\n  ([13eaddb](https://github.com/ory/kratos/commit/13eaddb7babe630750361c6d8f3ffc736898ddec))\n- Metadata should not be required\n  ([05afd68](https://github.com/ory/kratos/commit/05afd68381abe58c5e7cdd51cbf0ae409f5f0eb0))\n- Migration error detection\n  ([a115486](https://github.com/ory/kratos/commit/a11548603a4c9b46ba238d2a7ee58fffb7f6d857))\n- Missing usage to recovery_code_invalid template\n  ([#2798](https://github.com/ory/kratos/issues/2798))\n  ([5ac7553](https://github.com/ory/kratos/commit/5ac7553d191885957215b5a63f3bbdc2d020f3fe))\n- Not cleared field validation message\n  ([#2800](https://github.com/ory/kratos/issues/2800))\n  ([cdaf68d](https://github.com/ory/kratos/commit/cdaf68db8e6dd7bacfdb5fc6ff28e5d960f75c2c))\n- Panic\n  ([1182278](https://github.com/ory/kratos/commit/11822789c1561b27c2d769c9ea53a81835702f4a))\n- Patch invalidates credentials\n  ([#2721](https://github.com/ory/kratos/issues/2721))\n  ([c4d95af](https://github.com/ory/kratos/commit/c4d95afac590136acd14efa093f48c301fd07164)),\n  closes [ory/cloud#148](https://github.com/ory/cloud/issues/148)\n- Potentially resolve tx issue in crdb\n  ([#2595](https://github.com/ory/kratos/issues/2595))\n  ([9d22035](https://github.com/ory/kratos/commit/9d22035695b6a793ac4bc5e2bd0a68b3aeea039c))\n- Preserve return_to param between flows\n  ([#2644](https://github.com/ory/kratos/issues/2644))\n  ([f002649](https://github.com/ory/kratos/commit/f002649d45658a1486fac551d8ca6b37b3d03026))\n- Proper annotation for patch\n  ([#2784](https://github.com/ory/kratos/issues/2784))\n  ([0cbfe41](https://github.com/ory/kratos/commit/0cbfe410c50cfe551693683881b4145d115c1aa3))\n- Re-add service to quickstart\n  ([8c52c33](https://github.com/ory/kratos/commit/8c52c33cf277eda82c9b00b77cd9e03f1e5b4602))\n- Re-issue outdated cookie in /whoami\n  ([#2598](https://github.com/ory/kratos/issues/2598))\n  ([bf6f27e](https://github.com/ory/kratos/commit/bf6f27e37b8aa342ae002e0a9f227a31e0f7c279)),\n  closes [#2562](https://github.com/ory/kratos/issues/2562)\n- Remove jackc rewrites ([#2634](https://github.com/ory/kratos/issues/2634))\n  ([fe00c5b](https://github.com/ory/kratos/commit/fe00c5be72b0cdcc8d462a97aa04c413f758e8e3))\n- Remove jsonnet import support\n  ([d708c81](https://github.com/ory/kratos/commit/d708c81abbec424e4376a68140e5008bdba4eaaf))\n- Remove newline sign from email subject\n  ([#2576](https://github.com/ory/kratos/issues/2576))\n  ([ca3d9c2](https://github.com/ory/kratos/commit/ca3d9c24e25ce501e9eae23547f87e1c35b2ea97))\n- Remove rust workaround\n  ([355ec43](https://github.com/ory/kratos/commit/355ec431a304eef236a088571e2414f96c49d862))\n- Replace io/util usage by io and os package\n  ([e2d805b](https://github.com/ory/kratos/commit/e2d805b7e336d202f7cf3c2e0ce586d78ac03cc0))\n- Resolve bug where 500s in web hooks are not properly retried\n  ([e572e81](https://github.com/ory/kratos/commit/e572e8185e17839addabf2a72f4e9921bda8b47a))\n- Respect more http sources for computing request URL\n  ([66a9448](https://github.com/ory/kratos/commit/66a94488eb2fc778a00a5c69916e7958b3535440))\n- Return browser to 'return_to' when logging in without registered account using\n  oidc. ([#2496](https://github.com/ory/kratos/issues/2496))\n  ([a4194f5](https://github.com/ory/kratos/commit/a4194f58dd4ccecca6698d5b43284d857a70a221)),\n  closes [#2444](https://github.com/ory/kratos/issues/2444)\n- Return empty array not null when there are no sessions\n  ([#2548](https://github.com/ory/kratos/issues/2548))\n  ([fffba47](https://github.com/ory/kratos/commit/fffba473440fec3118a3951b697d5a0d2d4e30d6))\n- Revert Go 1.19 formatting changes\n  ([7fb085b](https://github.com/ory/kratos/commit/7fb085b6ca4fbfe2978998bea868959966ae193d))\n- Revert removal of required field in uiNodeInputAttributes\n  ([#2623](https://github.com/ory/kratos/issues/2623))\n  ([fee154b](https://github.com/ory/kratos/commit/fee154b28dfb3007f8d20a807cfd6d362c3bd9e7))\n- **sdk:** Identity metadata is nullable\n  ([#2841](https://github.com/ory/kratos/issues/2841))\n  ([4c70578](https://github.com/ory/kratos/commit/4c7057823b5292cb38f43bd5a96041aed178ad0a)):\n\n  Closes https://github.com/ory/sdk/issues/218\n\n- **sdk:** Make InputAttributes.Type an enum\n  ([ff6190f](https://github.com/ory/kratos/commit/ff6190f31f538cf8ed735dfd1bb3b7afcd944c36))\n- **sdk:** Rust compile issue with required enum\n  ([#2619](https://github.com/ory/kratos/issues/2619))\n  ([8800085](https://github.com/ory/kratos/commit/8800085d5bde32367217170d00f7141b7ea46733))\n- Send out correct verification invalid email in code strategy\n  ([#2908](https://github.com/ory/kratos/issues/2908))\n  ([d2bb67a](https://github.com/ory/kratos/commit/d2bb67af64d031613f2516b4848208d4f709e7b4))\n- Set cache default to false\n  ([#2906](https://github.com/ory/kratos/issues/2906))\n  ([e407f92](https://github.com/ory/kratos/commit/e407f92572b7823f70df17d463400807f14c8ae8))\n- Take over return_to param from unauthorized settings to login flow\n  ([#2787](https://github.com/ory/kratos/issues/2787))\n  ([504fb36](https://github.com/ory/kratos/commit/504fb36b6e72900808666dde778906a069f3c48b))\n- Unable to find JSON Schema ID: default\n  ([#2393](https://github.com/ory/kratos/issues/2393))\n  ([f43396b](https://github.com/ory/kratos/commit/f43396bdc03f89812f026c2a94b0b50100134c23))\n- Use correct download location for golangci-lint\n  ([c36ca53](https://github.com/ory/kratos/commit/c36ca53d4552596e62ec323795c3bf21438d4f26))\n- Use errors instead of fatal for serve cmd\n  ([02f7e9c](https://github.com/ory/kratos/commit/02f7e9cfd17ab60c3f38aab3ae977c427b26990d))\n- Use full URL for webhook payload\n  ([72595ad](https://github.com/ory/kratos/commit/72595adcb68a1a2d350c4687328653e28d888847))\n- Use process-isolated Jsonnet VM\n  ([#2869](https://github.com/ory/kratos/issues/2869))\n  ([9eeedc0](https://github.com/ory/kratos/commit/9eeedc06408c447077b630fff65e9ca4ed1ec59a))\n- Verification redirect & continue label\n  ([#2905](https://github.com/ory/kratos/issues/2905))\n  ([e1119e8](https://github.com/ory/kratos/commit/e1119e8f2e0372152d7d8367e7843fd5a49bf728)):\n\n  This PR resolves an issue with the redirect after a successful verification,\n  if not specified.\n\n- Wrap migration error in WithStack\n  ([#2636](https://github.com/ory/kratos/issues/2636))\n  ([4ce9f1e](https://github.com/ory/kratos/commit/4ce9f1ebb39cccfd36c4f0fb4a2ae2a17fbc18cc))\n- Wrong config key in admin recovery documentation\n  ([#2815](https://github.com/ory/kratos/issues/2815))\n  ([154b61b](https://github.com/ory/kratos/commit/154b61b9ff50306c540eb0904ae012195e735da4))\n- X-forwarded-for header parsing\n  ([#2807](https://github.com/ory/kratos/issues/2807))\n  ([4682afa](https://github.com/ory/kratos/commit/4682afaca3655dc809582b775a5a1c56205a4b4a))\n\n### Code Generation\n\n- Pin v0.11.0-alpha.0.pre.2 release commit\n  ([624e1f0](https://github.com/ory/kratos/commit/624e1f0d23b1c58bc28b2eaf845d4ef63e64bdba))\n\n### Code Refactoring\n\n- Hot reloading\n  ([b0d8f38](https://github.com/ory/kratos/commit/b0d8f3853886228a64e82437643a82b3970d6ff7))\n- Make embedding easier with internal sdk\n  ([e9aa21f](https://github.com/ory/kratos/commit/e9aa21f02b4bb7b09e268197334beb9c5772d13d))\n- SDK v1 naming\n  ([11f9d30](https://github.com/ory/kratos/commit/11f9d30a5d245b4dfc922a766853eaac2a20a8f5)):\n\n  Find the full\n  [upgrade guide in our documentation](https://www.ory.sh/docs/guides/upgrade/sdk).\n\n- **sdk:** Rename `getJsonSchema` to `getIdentitySchema`\n  ([#2606](https://github.com/ory/kratos/issues/2606))\n  ([8dc2ecf](https://github.com/ory/kratos/commit/8dc2ecf4919c9a14ef0bd089677de66ab3cfed92))\n- Use gotemplates for command usage\n  ([baa84c6](https://github.com/ory/kratos/commit/baa84c681b0c7fa29d653bd7226e792a5f44cb4c))\n- Use gotemplates for command usage\n  ([#2770](https://github.com/ory/kratos/issues/2770))\n  ([1d22b23](https://github.com/ory/kratos/commit/1d22b235291ce7102dd186a53a431b55780973d3))\n\n### Documentation\n\n- Cleanup v0alpha2 endpoint summaries\n  ([db9a95b](https://github.com/ory/kratos/commit/db9a95b6d28f7db3416c9d1530be4fd63a17ac6b))\n- Cypress on arm based mac ([#2795](https://github.com/ory/kratos/issues/2795))\n  ([d8514b5](https://github.com/ory/kratos/commit/d8514b50b5df9c098c77c5cb817602657b2a02ea))\n- Enable 2FA methods in docker-compose quickstart setup\n  ([#2828](https://github.com/ory/kratos/issues/2828))\n  ([8f52e8b](https://github.com/ory/kratos/commit/8f52e8b728bf8e2a99807f4d4899c2eaaca9e7e5))\n- Fix badge\n  ([dbb7506](https://github.com/ory/kratos/commit/dbb7506ec1a5a2b5bef21cb7838b6c86e755f0f9))\n- Importing credentials supported\n  ([4e8b5cf](https://github.com/ory/kratos/commit/4e8b5cf775c1bfe4c2eb5588bfebe900d1c390eb))\n- **sdk:** Identifier is actually required\n  ([#2593](https://github.com/ory/kratos/issues/2593))\n  ([f89d279](https://github.com/ory/kratos/commit/f89d2794d8a2122e3f86eeb8aa5d554da32e753e))\n- **sdk:** Incorrect URL ([#2521](https://github.com/ory/kratos/issues/2521))\n  ([ac6c4cc](https://github.com/ory/kratos/commit/ac6c4ccfc1901d38855ecd9991ef8de80e9d7c40))\n- Update README\n  ([5da4c6b](https://github.com/ory/kratos/commit/5da4c6b934b1b820d4a6ca67621855e87ecef773))\n- Update readme badges\n  ([7136e94](https://github.com/ory/kratos/commit/7136e94028dc64877e887776a1ccafb8826ce23c))\n- Write messages as single json document\n  ([#2519](https://github.com/ory/kratos/issues/2519))\n  ([3d8cf38](https://github.com/ory/kratos/commit/3d8cf38ef05c6ca5edf1161846c63bd3a23d9adc)),\n  closes [#2498](https://github.com/ory/kratos/issues/2498)\n\n### Features\n\n- Add \"success\" UITextType ([#2900](https://github.com/ory/kratos/issues/2900))\n  ([2ff34b6](https://github.com/ory/kratos/commit/2ff34b604757c46aae5cf3cbb23f39f982341486))\n- Add admin get api for session\n  ([#2855](https://github.com/ory/kratos/issues/2855))\n  ([1aa1321](https://github.com/ory/kratos/commit/1aa13211d1459e7453c2ba8fec69fee1c79aecbc))\n- Add api endpoint to fetch messages\n  ([#2651](https://github.com/ory/kratos/issues/2651))\n  ([5fddcbf](https://github.com/ory/kratos/commit/5fddcbf6554264766301e63ed3889ba746f0cd1a)):\n\n  Closes https://github.com/ory/kratos/issues/2639\n\n- Add autocomplete attributes\n  ([#2523](https://github.com/ory/kratos/issues/2523))\n  ([6284a9a](https://github.com/ory/kratos/commit/6284a9a5152924018d85f306e5758e9d8d759283)),\n  closes [#2396](https://github.com/ory/kratos/issues/2396)\n- Add cache headers ([#2817](https://github.com/ory/kratos/issues/2817))\n  ([71e2449](https://github.com/ory/kratos/commit/71e2449d7038594e107f39934e4716f845be7bb7))\n- Add codecov yaml\n  ([90da0bb](https://github.com/ory/kratos/commit/90da0bb4aeb50ed697c998342300cc56de5d5e1c))\n- Add DingTalk social login ([#2494](https://github.com/ory/kratos/issues/2494))\n  ([7b966bd](https://github.com/ory/kratos/commit/7b966bd16333f419b2a57f2a0b8684d6d86b34e6))\n- Add flow id check to use verification token\n  ([#2695](https://github.com/ory/kratos/issues/2695))\n  ([54c64fc](https://github.com/ory/kratos/commit/54c64fcea40ede17a87253042259fd97eeb780fe))\n- Add handler with openapi def for admin revoke session\n  ([#2867](https://github.com/ory/kratos/issues/2867))\n  ([2438ca0](https://github.com/ory/kratos/commit/2438ca0c9aed997870dcf60d41dad783838dd840))\n- Add identity id to \"account disabled\" error\n  ([#2557](https://github.com/ory/kratos/issues/2557))\n  ([f09b1b3](https://github.com/ory/kratos/commit/f09b1b3701c6deda4d25cebb7ccf2e97089be32a))\n- Add missing config entry\n  ([8fe9de6](https://github.com/ory/kratos/commit/8fe9de6d60a381611e07226614241a83b0010126))\n- Add missing cookie headers to SDK methods\n  ([#2720](https://github.com/ory/kratos/issues/2720))\n  ([32e32d1](https://github.com/ory/kratos/commit/32e32d1b98404ac14a44b2f0ccefa8c02d38c5f7)):\n\n  See https://github.com/ory/kratos/discussions/2583\n\n- Add OpenTelemetry span events\n  ([#2858](https://github.com/ory/kratos/issues/2858))\n  ([37b1a3b](https://github.com/ory/kratos/commit/37b1a3bb0cf2ea859d672674ca0e95893e63301b))\n- Add PATCH to adminUpdateIdentity\n  ([#2380](https://github.com/ory/kratos/issues/2380))\n  ([#2471](https://github.com/ory/kratos/issues/2471))\n  ([94a3741](https://github.com/ory/kratos/commit/94a37416011086582e309f62dc2c45ca84083a33))\n- Add pre-hooks to settings, verification, recovery\n  ([c0ceaf3](https://github.com/ory/kratos/commit/c0ceaf31f9327cca903c19b77597cae4587737e6))\n- Add session cache header feature flag\n  ([#2899](https://github.com/ory/kratos/issues/2899))\n  ([02a92b4](https://github.com/ory/kratos/commit/02a92b4d8ab5ced5d0d9387b38491990fa7cb724)),\n  closes [ory-corp/cloud#3283](https://github.com/ory-corp/cloud/issues/3283)\n- Add support for firebase scrypt hashes on identity import and login hash\n  upgrade ([#2734](https://github.com/ory/kratos/issues/2734))\n  ([3852eb4](https://github.com/ory/kratos/commit/3852eb460251a079bad68d08bee2aef23516d168)),\n  closes [#2422](https://github.com/ory/kratos/issues/2422)\n- Add verification via `code`\n  ([#2838](https://github.com/ory/kratos/issues/2838))\n  ([a82ee92](https://github.com/ory/kratos/commit/a82ee9295681b8dde96c3c6fb156e791df68613c)),\n  closes [#2824](https://github.com/ory/kratos/issues/2824):\n\n  The new `code` strategy is now supported as a verification strategy. If\n  enabled, the strategy sends a code, instead of a magic link to the user's\n  address, which they can use to verify their address.\n\n- Adding admin session listing api\n  ([#2818](https://github.com/ory/kratos/issues/2818))\n  ([59588d2](https://github.com/ory/kratos/commit/59588d2e290a8b72125021fa899661622e4cd946))\n- Adding device information to the session\n  ([#2715](https://github.com/ory/kratos/issues/2715))\n  ([82bc9ce](https://github.com/ory/kratos/commit/82bc9ce00d44085287e6d8d9e3fb67e107be2503)):\n\n  Closes https://github.com/ory/kratos/issues/2091 See\n  https://github.com/ory-corp/cloud/issues/3011\n\n  Co-authored-by: Patrik <zepatrik@users.noreply.github.com>\n\n- Allow importing scrypt hashing algorithm\n  ([#2689](https://github.com/ory/kratos/issues/2689))\n  ([3e3b59e](https://github.com/ory/kratos/commit/3e3b59e53de8cb89e9fd01cfec75a0f8a601035b)),\n  closes [#2422](https://github.com/ory/kratos/issues/2422):\n\n  It is now possible to import scrypt-hashed passwords.\n\n- Allow setting public and admin metadata with the jsonnet data mapper\n  ([#2569](https://github.com/ory/kratos/issues/2569))\n  ([aa6eb13](https://github.com/ory/kratos/commit/aa6eb13c1c42c11354074553fac9c90ee0a8999e)),\n  closes [#2552](https://github.com/ory/kratos/issues/2552)\n- Automatic TLS certificate reloading\n  ([#2744](https://github.com/ory/kratos/issues/2744))\n  ([09751e6](https://github.com/ory/kratos/commit/09751e6a03783701af60ce606633694ef67deacc))\n- Change code length to 6 numbers\n  ([#2894](https://github.com/ory/kratos/issues/2894))\n  ([56feb07](https://github.com/ory/kratos/commit/56feb079c3b99856c03cd8beb950673c10310520))\n- **cli:** Helper for cleaning up stale records\n  ([#2406](https://github.com/ory/kratos/issues/2406))\n  ([29d6376](https://github.com/ory/kratos/commit/29d6376e22e4de617ec63ca0a5dcb4dbf34c7c37)),\n  closes [#952](https://github.com/ory/kratos/issues/952)\n- Handler for update API with credentials\n  ([#2423](https://github.com/ory/kratos/issues/2423))\n  ([561187d](https://github.com/ory/kratos/commit/561187dafe2fea324d55c4efe3ffa6b65f9bed72)),\n  closes [#2334](https://github.com/ory/kratos/issues/2334)\n- Immutable cookie session values\n  ([#2761](https://github.com/ory/kratos/issues/2761))\n  ([a6f2793](https://github.com/ory/kratos/commit/a6f27935ce17a7ff5b3deaa4973d72a7d83454fb)),\n  closes [#2701](https://github.com/ory/kratos/issues/2701)\n- Implement blocking webhooks\n  ([#1585](https://github.com/ory/kratos/issues/1585))\n  ([e48e9fa](https://github.com/ory/kratos/commit/e48e9fac7ab6a982e0e941bfea1d15569eb53582)),\n  closes [#1724](https://github.com/ory/kratos/issues/1724)\n  [#1483](https://github.com/ory/kratos/issues/1483)\n- Improve cache handling\n  ([6e8579b](https://github.com/ory/kratos/commit/6e8579b835d54d5ebb5371297ea60f24e915882d))\n- Improve state generation logic\n  ([546ee3d](https://github.com/ory/kratos/commit/546ee3dc900874bc0614923b10697388c4e7676b))\n- Ingest hydra bugfix\n  ([3c11216](https://github.com/ory/kratos/commit/3c112165e553161696cf746befb9e03c2e6e07fb))\n- OAuth2 integration ([#2804](https://github.com/ory/kratos/issues/2804))\n  ([7c6eb2a](https://github.com/ory/kratos/commit/7c6eb2a5128c6bc76ac7306edafaa54c4893ea82)):\n\n  This feature allows Ory Kratos to act as a login provider for Ory Hydra using\n  the `oauth2_provider.url` configuration value.\n\n  Closes https://github.com/ory/kratos/issues/273 Closes\n  https://github.com/ory/kratos/discussions/2293 See\n  https://github.com/ory/kratos-selfservice-ui-node/pull/50 See\n  https://github.com/ory/kratos-selfservice-ui-node/pull/68 See\n  https://github.com/ory/kratos-selfservice-ui-node/pull/108 See\n  https://github.com/ory/kratos-selfservice-ui-node/pull/111 See\n  https://github.com/ory/kratos-selfservice-ui-node/pull/149 See\n  https://github.com/ory/kratos-selfservice-ui-node/pull/170 See\n  https://github.com/ory/kratos-selfservice-ui-node/pull/198 See\n  https://github.com/ory/kratos-selfservice-ui-node/pull/207\n\n- Parse all id token claims into raw_claims\n  ([#2765](https://github.com/ory/kratos/issues/2765))\n  ([1da0cf6](https://github.com/ory/kratos/commit/1da0cf62b3f0ed8a81bca22123474baa7cf6de65)),\n  closes [#2528](https://github.com/ory/kratos/issues/2528):\n\n  All ID Token claims resulting from the Social Sign In flow are now available\n  in `raw_claims` and can be used in the Social Sign In JsonNet Mapper.\n\n- Replace magic links with one time codes in recovery flow\n  ([#2645](https://github.com/ory/kratos/issues/2645))\n  ([a1532ba](https://github.com/ory/kratos/commit/a1532ba79722ccfc9c8608ef6f51a6d9ecb24a8e)),\n  closes [#1451](https://github.com/ory/kratos/issues/1451):\n\n  This feature introduces a new `code` strategy to recover an account.\n\n  Currently, if a user needs to initiate a recovery flow to recover a lost\n  password/MFA/etc., they’ll receive an email containing a “magic link”. This\n  link contains a flow_id and a recovery_token. This is problematic because some\n  antivirus software opens links in emails to check for malicious content, etc.\n\n  Instead of the magic link, we send an 8-digit code that is clearly displayed\n  in the email or SMS. A user can now copy/paste or type it manually into the\n  text-field that is shown after the user clicks “submit” on the initiate flow\n  page.\n\n- Replace message_ttl with static max retry count\n  ([#2638](https://github.com/ory/kratos/issues/2638))\n  ([b341756](https://github.com/ory/kratos/commit/b341756130ee808ddcc003163884f09e3f006d0a)):\n\n  This PR replaces the `courier.message_ttl` configuration option with a\n  `courier.message_retries` option to limit how often the sending of a message\n  is retried before it is marked as `abandoned`.\n\n- Standardize license headers\n  ([#2790](https://github.com/ory/kratos/issues/2790))\n  ([8406eaf](https://github.com/ory/kratos/commit/8406eaf92006d9812108bd3ae57245f01e627bfc))\n- Support ip exceptions\n  ([de46c08](https://github.com/ory/kratos/commit/de46c08534dfae6165f6a570cc59829f367c0b57))\n- Support md5 hash import ([#2725](https://github.com/ory/kratos/issues/2725))\n  ([d1b4e17](https://github.com/ory/kratos/commit/d1b4e1748f66c0dc8033235f1a9c155aac0d5caa))\n- Trace WebHooks ([#2911](https://github.com/ory/kratos/issues/2911))\n  ([665605b](https://github.com/ory/kratos/commit/665605bbc4f6ca838f0180680cdd68905f07d482)):\n\n  Previously the context was not propagated to the http client. As a result the\n  (instrumented) client did not find the existing span and the sapns for\n  outgoing http request have been orphains.\n\n  With this simple Fix they are now children of the corresponding webhook spans.\n\n- Update for the Ory Network\n  ([#2814](https://github.com/ory/kratos/issues/2814))\n  ([3e09e58](https://github.com/ory/kratos/commit/3e09e58a695cf5d9d57b9f773e0f50b1fd794915))\n- Upgrade hydra to v2\n  ([fdb108f](https://github.com/ory/kratos/commit/fdb108fe2542569202bfb39ef55e1a7e8c5b5ebf))\n\n### Reverts\n\n- Revert \"autogen(openapi): regenerate swagger spec and internal client\"\n  ([24eddfb](https://github.com/ory/kratos/commit/24eddfb2adc67e22d34efdc6b6a6723c7be64237)):\n\n  This reverts commit 4159b93ae3f8175cf7ccf77d34e4a7a2d0181d4f.\n\n### Tests\n\n- **e2e:** Add typescript\n  ([37018c0](https://github.com/ory/kratos/commit/37018c0161d0affe88c9f2574d043f337579e4a9))\n- **e2e:** Fix flaky assertions\n  ([21a8487](https://github.com/ory/kratos/commit/21a8487f984168abbc7279c590c66822414c718e))\n- **e2e:** Fix issuer config\n  ([32454d2](https://github.com/ory/kratos/commit/32454d2fbd169a7839fc3d02786376ef4c7c986d))\n- **e2e:** Fix webauthn regression\n  ([26001e7](https://github.com/ory/kratos/commit/26001e7544b60ad0004153773a21c1d04abf9987))\n- **e2e:** Improve webauthn test reliability\n  ([4d323d0](https://github.com/ory/kratos/commit/4d323d01b53b9f7b0dc346211ac4fda0626d357a))\n- **e2e:** Migrate to cypress 10.x\n  ([317fab0](https://github.com/ory/kratos/commit/317fab0fe76a2762a77b3d2f8a75735598cb1c0e))\n- **e2e:** Resolve flaky hydra configuration\n  ([d8c82da](https://github.com/ory/kratos/commit/d8c82dabad4f04874647c48ecbf0eda91c7c90fa))\n- **e2e:** Resolve max-age and issuer regression\n  ([0ee4cf0](https://github.com/ory/kratos/commit/0ee4cf058cbda2bef52b3fa830f3db411f442197))\n- **e2e:** Resolve max-age regression\n  ([904f75d](https://github.com/ory/kratos/commit/904f75d254e9513aa3edad4fa3f9ead4d80e46df))\n- **e2e:** Use correct dir\n  ([907dbe3](https://github.com/ory/kratos/commit/907dbe3f605d5be5038ddc06029082b2df0914e2))\n- Fix broken assertions\n  ([e5f1311](https://github.com/ory/kratos/commit/e5f131138243ad5806c7927dd5a642d029cfad6c))\n- Fix oidc test regression\n  ([6c14b68](https://github.com/ory/kratos/commit/6c14b682d0984175495051308985281d72c0988e))\n- Improve e2e tooling\n  ([390ccaa](https://github.com/ory/kratos/commit/390ccaac18023979ff36bc7ee2df6c0d4a90d8c8))\n- Parallelize and speed up config tests\n  ([#2611](https://github.com/ory/kratos/issues/2611))\n  ([d8dea01](https://github.com/ory/kratos/commit/d8dea0138b09d4dff3c30aa14e0e99e423b355fe))\n- Resolve builder regression\n  ([934c30d](https://github.com/ory/kratos/commit/934c30d6064d1e7dfc59f4eef43d096e977c113e))\n- Try and recover from allocated port error\n  ([3b5ac5f](https://github.com/ory/kratos/commit/3b5ac5ff03b653191c1979fe1e4e9a4ea3ed7d36))\n- Update snapshots ([#2877](https://github.com/ory/kratos/issues/2877))\n  ([cbaaceb](https://github.com/ory/kratos/commit/cbaaceb9ef73a91e1b4ce5e4f7b9d7bac04d4c03))\n\n### Unclassified\n\n- Revert \"refactor: use gotemplates for command usage (#2770)\" (#2778)\n  ([d612612](https://github.com/ory/kratos/commit/d612612313dc26f1ddaaa84dbca65139b967d52c)),\n  closes [#2770](https://github.com/ory/kratos/issues/2770)\n  [#2778](https://github.com/ory/kratos/issues/2778):\n\n  This reverts commit 1d22b235291ce7102dd186a53a431b55780973d3.\n\n- Remove empty script (#2739)\n  ([1515b83](https://github.com/ory/kratos/commit/1515b839f52044d6c9674d4a2df43dfeda3bb15b)),\n  closes [#2739](https://github.com/ory/kratos/issues/2739)\n\n# [0.10.1](https://github.com/ory/kratos/compare/v0.10.0...v0.10.1) (2022-06-01)\n\nRe-release the SDK.\n\n### Bug Fixes\n\n- Bump ory cli\n  ([12ceae0](https://github.com/ory/kratos/commit/12ceae005749c5dd01959720925418d643f13070))\n\n### Code Generation\n\n- Pin v0.10.1 release commit\n  ([ab16580](https://github.com/ory/kratos/commit/ab16580b4326250885b920198b280456eb873a6b))\n\n# [0.10.0](https://github.com/ory/kratos/compare/v0.9.0-alpha.3...v0.10.0) (2022-05-30)\n\nWe achieved a major milestone - Ory Kratos is out of alpha! Ory Kratos had no\nmajor changes in the APIs for the last months and feel confident that no large\nbreaking changes will need to be introduced in the near future.\n\nThis release focuses on quality-of-live improvements, resolves several bugs,\nirons out developer experience issues, and introduces session renew\ncapabilities!\n\n## Breaking Changes\n\nPlease be aware that the SDK method signatures for\n`submitSelfServiceRecoveryFlow`, `submitSelfServiceRegistrationFlow`,\n`submitSelfServiceLoginFlow`, `submitSelfServiceSettingsFlow`,\n`submitSelfServiceVerificationFlow` might have changed in your SDK.\n\nThis patch moves several CLI command to comply with the Ory CLI command\nstructure:\n\n```patch\n- ory identities get ...\n+ ory get identity ...\n\n- ory identities delete ...\n+ ory delete identity ...\n\n- ory identities import ...\n+ ory import identity ...\n\n- ory identities list ...\n+ ory list identities ...\n\n- ory identities validate ...\n+ ory validate identity ...\n\n- ory jsonnet format ...\n+ ory format jsonnet ...\n\n- ory jsonnet lint ...\n+ ory lint jsonnet ...\n```\n\nThis patch moves several CLI command to comply with the Ory CLI command\nstructure:\n\n```patch\n- ory identities get ...\n+ ory get identity ...\n\n- ory identities delete ...\n+ ory delete identity ...\n\n- ory identities import ...\n+ ory import identity ...\n\n- ory identities list ...\n+ ory list identities ...\n\n- ory identities validate ...\n+ ory validate identity ...\n\n- ory jsonnet format ...\n+ ory format jsonnet ...\n\n- ory jsonnet lint ...\n+ ory lint jsonnet ...\n```\n\n### Bug Fixes\n\n- Add flow id when return_to is passed to the verification\n  ([#2482](https://github.com/ory/kratos/issues/2482))\n  ([c2b1c23](https://github.com/ory/kratos/commit/c2b1c2303cd0587b9419d500f2e3d5f9c9c80ad4))\n- Add indices for slow queries\n  ([e0cdbc9](https://github.com/ory/kratos/commit/e0cdbc9ab3389de0f65b37758d86bea56d294d64))\n- Add legacy session value\n  ([ecfd052](https://github.com/ory/kratos/commit/ecfd05216f5ebb70f1617595d2d398cf1fa3c660)),\n  closes [#2398](https://github.com/ory/kratos/issues/2398)\n- **auth0:** Created_at workaround\n  ([#2492](https://github.com/ory/kratos/issues/2492))\n  ([52a965d](https://github.com/ory/kratos/commit/52a965dc7e4ac868d21261cb44576846426bffa5)),\n  closes [#2485](https://github.com/ory/kratos/issues/2485)\n- Avoid excessive memory allocations in HIBP cache\n  ([#2389](https://github.com/ory/kratos/issues/2389))\n  ([ee2d410](https://github.com/ory/kratos/commit/ee2d41057a7e6cb2c57c6304c2e7bbf5ad7c56da)),\n  closes [#2354](https://github.com/ory/kratos/issues/2354)\n- Change SQLite database mode to 0600\n  ([#2344](https://github.com/ory/kratos/issues/2344))\n  ([0e5d3b7](https://github.com/ory/kratos/commit/0e5d3b7726a8923fbc2a4c10ec18f0ba97ffbcff)):\n\n  The default mode is 0644, which is allows broader access than necessary.\n\n- Compile issues from merge conflict\n  ([#2419](https://github.com/ory/kratos/issues/2419))\n  ([85a90c8](https://github.com/ory/kratos/commit/85a90c892d785b834cbdf8d029315550210444e2))\n- Correct location\n  ([b249aaa](https://github.com/ory/kratos/commit/b249aaad97eabc88c269265359a33cea920ef7f2))\n- **courier:** Add ability to specify backoff\n  ([#2349](https://github.com/ory/kratos/issues/2349))\n  ([bf970f3](https://github.com/ory/kratos/commit/bf970f32f571164b8081f09f602a3473e079194e))\n- Do not expose debug in a response when a schema is not found\n  ([#2348](https://github.com/ory/kratos/issues/2348))\n  ([aee2b1e](https://github.com/ory/kratos/commit/aee2b1ed1189b57fcbb1aaa456444d5121be94b1))\n- Do not fail release if no changes needed\n  ([114c93e](https://github.com/ory/kratos/commit/114c93eb48c242702b72d7785da70bd31d858214))\n- **Dockerfile:** Use existing builder base image\n  ([#2390](https://github.com/ory/kratos/issues/2390))\n  ([37de25a](https://github.com/ory/kratos/commit/37de25a541a24e03407ecf344fb750775e48c782))\n- Embed schema\n  ([b797bba](https://github.com/ory/kratos/commit/b797bba5910dfd925a11fb86e2dbd14b5dd839d9))\n- Get user first name and last name from Apple\n  ([#2331](https://github.com/ory/kratos/issues/2331))\n  ([4779909](https://github.com/ory/kratos/commit/47799098b35ea1cf5a1163f57d872a5bb2242d97))\n- Improve error reporting from OpenAPI\n  ([8a1009b](https://github.com/ory/kratos/commit/8a1009b16653df13485bab8e33926967c449bf4e))\n- Improve performance of identity schema call\n  ([af28de2](https://github.com/ory/kratos/commit/af28de267f21cd72953f3f353d8fd587937b2249))\n- Internal Server Error on Empty PUT /identities/id body\n  ([#2417](https://github.com/ory/kratos/issues/2417))\n  ([5a50231](https://github.com/ory/kratos/commit/5a50231b553aaa64bd90a3d2cd1be9d2e3aba9ac))\n- Load return_to and append to errors\n  ([#2333](https://github.com/ory/kratos/issues/2333))\n  ([5efe4a3](https://github.com/ory/kratos/commit/5efe4a33e35e74d248d4eec43dc901b7b6334037)),\n  closes [#2275](https://github.com/ory/kratos/issues/2275)\n  [#2279](https://github.com/ory/kratos/issues/2279)\n  [#2285](https://github.com/ory/kratos/issues/2285)\n- Make delete formattable\n  ([0005f35](https://github.com/ory/kratos/commit/0005f357a049ecbf94d76a1e73434837753a04ea))\n- Mark body as required ([#2479](https://github.com/ory/kratos/issues/2479))\n  ([c9ae117](https://github.com/ory/kratos/commit/c9ae1175340993cfc93db436c06462c80935ea2a))\n- New issue templates\n  ([b9ad684](https://github.com/ory/kratos/commit/b9ad684311ee8c654b2fa382010315e892581f5c))\n- Openapi regression ([#2465](https://github.com/ory/kratos/issues/2465))\n  ([37a3369](https://github.com/ory/kratos/commit/37a3369cea8ed5af34e8324a291a7d7dba0eb43a))\n- Quickstart docker-compose ([#2490](https://github.com/ory/kratos/issues/2490))\n  ([9717762](https://github.com/ory/kratos/commit/97177629c715028affbc294bdd432fd6c954d5ad)),\n  closes [#2488](https://github.com/ory/kratos/issues/2488)\n- Refresh is always false when session exists\n  ([d3436d7](https://github.com/ory/kratos/commit/d3436d7fa17589d91e25c9f0bd66bc3bb5b150fa)),\n  closes [#2341](https://github.com/ory/kratos/issues/2341)\n- Remove required legacy field\n  ([#2410](https://github.com/ory/kratos/issues/2410))\n  ([638d45c](https://github.com/ory/kratos/commit/638d45caf480b7287c9762cbf3c593217f40e3e8))\n- Remove wrong templates\n  ([4fe2d25](https://github.com/ory/kratos/commit/4fe2d25dd68033a8d7b3dd5f62d87b23a7ba361d))\n- Reorder transactions\n  ([78ca4c6](https://github.com/ory/kratos/commit/78ca4c6ca5a49b0800d9c34954638a926d80078b))\n- Resolve index naming issues\n  ([d5550b5](https://github.com/ory/kratos/commit/d5550b5ddc4e1677e4c4f808578f573760c6581e))\n- Resolve MySQL index issues\n  ([50bdba9](https://github.com/ory/kratos/commit/50bdba9f1117c60e80e153416bc997187b4a60b7))\n- Resolve otelx panics\n  ([6613a02](https://github.com/ory/kratos/commit/6613a02b8fd5f6f06e9b6301bdc39037771b3d9b))\n- **sdk:** Improved OpenAPI specifications for UI nodes\n  ([#2375](https://github.com/ory/kratos/issues/2375))\n  ([a42a0f7](https://github.com/ory/kratos/commit/a42a0f772af3625c457032d6dcc34289a62acc61)),\n  closes [#2357](https://github.com/ory/kratos/issues/2357)\n- Serve.admin.request_log.disable_for_health behaviour\n  ([#2399](https://github.com/ory/kratos/issues/2399))\n  ([0a381fa](https://github.com/ory/kratos/commit/0a381fa3d702f77e614d0492dafa3ac2cd102c7e))\n- **sql:** Add additional join argument to resolve MySQL query issue\n  ([854e5cb](https://github.com/ory/kratos/commit/854e5cba80cad52b58571587980c00c038ff6596)),\n  closes [#2262](https://github.com/ory/kratos/issues/2262)\n- Unreliable HIBP caching strategy\n  ([#2468](https://github.com/ory/kratos/issues/2468))\n  ([93bf1e2](https://github.com/ory/kratos/commit/93bf1e2cd53f3a4de3ff414017c17813d36b56da))\n- Use `path` instead of `filepath` to join http route paths\n  ([16b1244](https://github.com/ory/kratos/commit/16b12449c841bf7a237fe436b884b4b5012cd022)),\n  closes [#2292](https://github.com/ory/kratos/issues/2292)\n- Use JOIN instead of iterative queries\n  ([0998cfb](https://github.com/ory/kratos/commit/0998cfb2fdda27ba8baeebcc603aae5fbe5c901f)),\n  closes [#2402](https://github.com/ory/kratos/issues/2402)\n- Use pointer of string for PasswordIdentifier in example code\n  ([#2421](https://github.com/ory/kratos/issues/2421))\n  ([61f12e7](https://github.com/ory/kratos/commit/61f12e7579c7c337d0f415ac2b4029790c659c3d))\n- Use predictable SQLite in memory DSNs\n  ([#2415](https://github.com/ory/kratos/issues/2415))\n  ([51a13f7](https://github.com/ory/kratos/commit/51a13f712d38a942772b3f4c014971ecb4658d7a)),\n  closes [#2059](https://github.com/ory/kratos/issues/2059)\n\n### Code Generation\n\n- Pin v0.10.0 release commit\n  ([87e0de7](https://github.com/ory/kratos/commit/87e0de7a10b2a7478d8113ca028bfdb6525bc8e5))\n\n### Code Refactoring\n\n- Deprecate fizz renderer\n  ([5277668](https://github.com/ory/kratos/commit/5277668b1324173df95db5e9e4b96ed841ff088b))\n- Move CLI commands to match Ory CLI structure\n  ([d11a9a9](https://github.com/ory/kratos/commit/d11a9a9dafdebb53ed9a8359496eb70b8adb99dd))\n- Move CLI commands to match Ory CLI structure\n  ([73910a3](https://github.com/ory/kratos/commit/73910a329b1ee46de2607c7ab1958ef2fb6de5f4))\n\n### Documentation\n\n- Add docs about change in default schema\n  ([#2447](https://github.com/ory/kratos/issues/2447))\n  ([5093cd4](https://github.com/ory/kratos/commit/5093cd47f22311c2e1fdbffd82f0494806076f08))\n- Remove notice importing credentials not possible\n  ([#2418](https://github.com/ory/kratos/issues/2418))\n  ([b80ed69](https://github.com/ory/kratos/commit/b80ed6955518003ae6b7f647dffd2d49cc999fbc))\n\n### Features\n\n- Add certificate based authentication for smtp client\n  ([#2351](https://github.com/ory/kratos/issues/2351))\n  ([7200037](https://github.com/ory/kratos/commit/72000375c028f5f7f9cb0d0b1b02f8aa09503e4f))\n- Add ID to the recovery error when already logged in\n  ([#2483](https://github.com/ory/kratos/issues/2483))\n  ([29e4a51](https://github.com/ory/kratos/commit/29e4a51cc5344dcb44839f8aa57197c41aeeb78d))\n- Add localName to smtp config\n  ([#2445](https://github.com/ory/kratos/issues/2445))\n  ([27336b6](https://github.com/ory/kratos/commit/27336b63b0c11c1667d5a07230bed82283475aa4)),\n  closes [#2425](https://github.com/ory/kratos/issues/2425)\n- Add render-schema script\n  ([a0c006e](https://github.com/ory/kratos/commit/a0c006e40fb00608d682b74f44725883b9c7bf4f))\n- Add session renew capabilities\n  ([#2146](https://github.com/ory/kratos/issues/2146))\n  ([4348b86](https://github.com/ory/kratos/commit/4348b8640a282cd61fe30961faba5753e2af8bb0)),\n  closes [#615](https://github.com/ory/kratos/issues/615)\n- Add support for netID provider\n  ([#2394](https://github.com/ory/kratos/issues/2394))\n  ([ee7fc79](https://github.com/ory/kratos/commit/ee7fc79d49cd6d8f2985809585d1675c8e2ed376))\n- Add tracing to persister\n  ([391c54e](https://github.com/ory/kratos/commit/391c54eb3ba721e4912a7a4676acc2f630be2a72))\n- **identity:** Add admin and public metadata fields\n  ([562e340](https://github.com/ory/kratos/commit/562e340fe980e7c65ab3fc41f82a2a8899a33bfa)),\n  closes [#2388](https://github.com/ory/kratos/issues/2388)\n  [#47](https://github.com/ory/kratos/issues/47):\n\n  This patch adds two new keys to identities, `metadata_public` and\n  `metadata_admin` that can be used to store additional metadata about\n  identities in Ory.\n\n- Read subject id from https://graph.microsoft.com/v1.0/me for microsoft\n  ([#2347](https://github.com/ory/kratos/issues/2347))\n  ([852f24f](https://github.com/ory/kratos/commit/852f24fb5cd8576f3f6d35017ce85e4fa1c51c95)):\n\n  Adds the ability to read the OIDC subject ID from the\n  `https://graph.microsoft.com/v1.0/me` endpoint. This introduces a new field\n  `subject_source` to the OIDC configuration.\n\n  Closes https://github.com/ory/kratos/pull/2153\n\n- **sdk:** Add cookie headers to all form submissions\n  ([#2467](https://github.com/ory/kratos/issues/2467))\n  ([9a969fd](https://github.com/ory/kratos/commit/9a969fd927ae8436a863e91ecb6574cb3bb1c3a6)),\n  closes [#2003](https://github.com/ory/kratos/issues/2003)\n  [#2454](https://github.com/ory/kratos/issues/2454)\n- **sdk:** Add csrf cookie for login flow submission\n  ([#2454](https://github.com/ory/kratos/issues/2454))\n  ([2bffee8](https://github.com/ory/kratos/commit/2bffee81f0e8a98851a3e11b4fc4969d95e9b445))\n- Support argon2i password ([#2395](https://github.com/ory/kratos/issues/2395))\n  ([8fdadf9](https://github.com/ory/kratos/commit/8fdadf9d1724d28ae11996304703e06671549660))\n- Switch to opentelemetry tracing\n  ([#2318](https://github.com/ory/kratos/issues/2318))\n  ([121a4d3](https://github.com/ory/kratos/commit/121a4d3fc0f396e8da50ad1985cacf68a5c85a12))\n- **tracing:** Improved tracing for requests\n  ([#2475](https://github.com/ory/kratos/issues/2475))\n  ([b90a558](https://github.com/ory/kratos/commit/b90a5582284f1ceb0e97575e3b3562603b65ec5f))\n- Upgrade to Go 1.18\n  ([725d202](https://github.com/ory/kratos/commit/725d202e6ae15b3b5c3282e03c03a40480a2e310))\n\n### Tests\n\n- Fix incorrect assertion\n  ([b5b1361](https://github.com/ory/kratos/commit/b5b1361defa8faa6ea36d50a8d940c76f70c4ddd))\n- Resolve regressions\n  ([dd44593](https://github.com/ory/kratos/commit/dd44593a51a9277c717170360f9794837e4f910c))\n\n### Unclassified\n\n- BREAKING CHANGES: This patch group updates the tracing provider from\n  OpenTracing to OpenTelemetry. Due to these changes, tracing providers Zipkin,\n  DataDog, Elastic APM have been deactivated temporarily. The best way to re-add\n  support for them is to make a pull request at\n  https://github.com/ory/x/tree/master/otelx and check the status of\n  https://github.com/ory/x/issues/499\n  ([7165fa0](https://github.com/ory/kratos/commit/7165fa04fa1c9442cad8da5c5814453e1ca0ba7b)):\n\n  The configuration has not changed, and thus no changes to your system are\n  required if you use Jaeger.\n\n# [0.9.0-alpha.3](https://github.com/ory/kratos/compare/v0.9.0-alpha.2...v0.9.0-alpha.3) (2022-03-25)\n\nResolves an issue in the quickstart.\n\n## Breaking Changes\n\nCalling /self-service/recovery without flow ID or with an invalid flow ID while\nauthenticated will now respond with an error instead of redirecting to the\ndefault page.\n\nCloses https://github.com/ory-corp/cloud/issues/2173\n\nCo-authored-by: aeneasr <3372410+aeneasr@users.noreply.github.com>\n\n### Bug Fixes\n\n- Accept recovery link from authenticated users\n  ([#2195](https://github.com/ory/kratos/issues/2195))\n  ([0fa64dd](https://github.com/ory/kratos/commit/0fa64dd7fdaaadf92bddb600bbf201fb6e9d1fed)):\n\n  When a recovery link is opened while the user already has a session cookie\n  (possibly for another account), the endpoint will now correctly complete the\n  recovery process and issue new cookies.\n\n- Quickstart\n  ([73b461c](https://github.com/ory/kratos/commit/73b461c6ea45e0feaab734d0eb0ce380993e95d4)):\n\n  Closes https://github.com/ory/kratos/issues/2339\n\n- Resolve issue where CF cookies would mingle with CSRF detection in API flows\n  ([011219a](https://github.com/ory/kratos/commit/011219a40027d2c1b06c2797951a55e2f07c0845))\n- Typo in error message ([#2332](https://github.com/ory/kratos/issues/2332))\n  ([b075a5b](https://github.com/ory/kratos/commit/b075a5b30b47e79af1330238a3b5ea97a3c2ac4b))\n- Update v0.9.0-alpha.2 config schema path\n  ([#2328](https://github.com/ory/kratos/issues/2328))\n  ([55705c7](https://github.com/ory/kratos/commit/55705c7ce0ff76dc7ddda24524db919dcb51225a))\n- **version schema:** Require version or fall back to latest\n  ([52c9824](https://github.com/ory/kratos/commit/52c98247d4c170f79fa25a019d7f4a73b3e5fdc4))\n\n### Code Generation\n\n- Pin v0.9.0-alpha.3 release commit\n  ([32e36d4](https://github.com/ory/kratos/commit/32e36d4e75f888e69653625a52171200b4968a6c))\n\n### Documentation\n\n- Add missing error codes\n  ([b854bb8](https://github.com/ory/kratos/commit/b854bb8a33794bba684abbfe5abc6b8da1c54f44))\n- Clarify 410 error for api payloads\n  ([2c7ac3b](https://github.com/ory/kratos/commit/2c7ac3b15a65e629ba25c0170fce68aa9eb3a80a))\n\n# [0.9.0-alpha.2](https://github.com/ory/kratos/compare/v0.9.0-alpha.1...v0.9.0-alpha.2) (2022-03-22)\n\nResolves an issue in the SDK release pipeline.\n\n### Bug Fixes\n\n- Swag location\n  ([5b51bfb](https://github.com/ory/kratos/commit/5b51bfbb10592c9e7dce14689f48530427c34edc))\n\n### Code Generation\n\n- Pin v0.9.0-alpha.2 release commit\n  ([f5501cf](https://github.com/ory/kratos/commit/f5501cf575a74884555e0e1e4cba39c552f4868f))\n\n# [0.9.0-alpha.1](https://github.com/ory/kratos/compare/v0.8.3-alpha.1.pre.0...v0.9.0-alpha.1) (2022-03-21)\n\nOry Kratos v0.9 is here! We're extremely happy to announce that the new release\nis out and once again it's been made even better thanks to the incredible\ncontributions from our awesome community. <3\n\nEnjoy!\n\nHere's an overview of things you can expect from the v0.9 release:\n\n1. We introduced 1:1 compatibility between self-hosting Ory Kratos and using Ory\n   Cloud. The configuration works the same across all modes of operation and\n   deployment!\n2. Passwordless login with WebAuthn is now available! Authentication with\n   YubiKeys, TouchID, FaceID, Microsoft Hello, and other WebAuthn-supported\n   methods is now available. The refactored infrastructure lays a foundation for\n   more passwordless flows to come.\n3. All the docs are now available in a single repo. Go to the\n   [ory/docs](https://github.com/ory/docs) repository to find docs for all Ory\n   projects.\n4. You can now load custom email templates that'll make your essential messaging\n   like project invitations or password recovery emails look slick.\n5. We've laid the foundation for adding SMS-dependant flows.\n6. Security is always a top priority. We've made changes and updates such as CSP\n   nonces, SSRF defenses, session invalidation hooks, and more.\n7. Kratos now gracefully handles cookie errors.\n8. Password policies are now configurable.\n9. Added configuration to control the flow of webhooks. Now you can cancel flows\n   & run them in the background.\n10. You can import identities along with their credentials (password, social\n    sign-in connections, WebAuthn, ...).\n11. Infra: we migrated all of our CIs from CircleCI to GitHub Actions.\n12. We moved the admin API from `/` to `admin`. **This is a breaking change**.\n    Please read the explanation and proceed with caution!\n13. Bugfix: fixed a bug in the handling of secrets. **This is a breaking\n    change**. Please read the explanation and proceed with caution!\n14. Bugfix: several bugs in different self-service flows are no more.\n\nAs you can see, this release introduces breaking changes. We tried to keep the\nHTTP API as backward-compatible as possible by introducing HTTP redirects and\nother measures, but this update requires you to take extra care. Make sure\nyou've read the release notes and understand the risk before updating.\n\nYou must apply SQL migrations for this release. **Make sure to create backup\nbefore you start!**\n\n## Breaking Changes\n\nConfiguration key `selfservice.whitelisted_return_urls` has been renamed to\n`allowed_return_urls`.\n\nAll endpoints at the Admin API are now exposed at `/admin/`. For example,\nendpoint `https://kratos:4434/identities` is now exposed at\n`https://kratos:4434/admin/identities`. This change makes it easier to configure\nreverse proxies and API Gateways. Additionally, it introduces 1:1 compatibility\nbetween Ory Cloud's APIs and self-hosted Ory Kratos. Please note that nothing\nhas changed in terms of the port. To make the migration less painful, we have\nset up redirects from the old endpoints to the new `/admin` endpoints, so your\nAPIs, SDKs, and clients should continue working as they were working before.\nThis change is marked as a breaking change as it touches many endpoints and\nmight be confusing when encountering the redirect for the first time.\n\nIf you are using two or more secrets for the `secrets.session`, this patch might\nbreak existing Ory Session Cookies. This has the effect that users will need to\nre-authenticate when visiting your app.\n\nThe `password_identifier` form field of the password login strategy has been\nrenamed to `identifier` to make compatibility with passwordless flows possible.\nField name `password_identifier` will still be accepted. Please note that the UI\nnode for displaying the \"username\" / \"email\" field has this `name=\"identifier\"`\ngoing forward. Additionally, the `traits` of the password strategy are no longer\nwithin group `password` but instead in group `profile` going forward!\n\nThe following OpenID Connect configuration keys have been renamed to better\nexplain their purpose:\n\n```patch\n- private_key_id\n+ apple_private_key_id\n\n- private_key\n+ apple_private_key\n\n- team_id\n+ apple_team_id\n\n- tenant\n+ microsoft_tenant\n```\n\nA major issue has been lingering in the configuration for a while. What happens\nto your identities when you update a schema? The answer was, it depends on the\nchange. If the change is incompatible, some things might break!\n\nTo resolve this problem we changed the way you define schemas. Instead of having\na global `default_schema_url` which developers used to update their schema, you\nnow need to define the `default_schema_id` which must reference schema ID in\nyour config. To update your existing configuration, check out the patch example\nbelow:\n\n```patch\nidentity:\n-  default_schema_url: file://stub/identity.schema.json\n+  default_schema_id: default\n+  schemas:\n+  - id: default\n+    url: file://stub/identity.schema.json\n```\n\nIdeally, you would version your schema and update the `default_schema_id` with\nevery change to the new version:\n\n```yaml\nidentity:\n  default_schema_id: user_v1\n  schemas:\n    - id: user_v0\n      url: file://path/to/user_v0.json\n    - id: user_v1\n      url: file://path/to/user_v1.json\n```\n\n### Bug Fixes\n\n- Add CourierConfig to default registry\n  ([#2243](https://github.com/ory/kratos/issues/2243))\n  ([2e1fba3](https://github.com/ory/kratos/commit/2e1fba3ca88e273362978fe29197fe44a879813e))\n- Add DispatchMessage to interface\n  ([df2ca7a](https://github.com/ory/kratos/commit/df2ca7a7c97a28d40c6a8af082f99ff7706ee9db))\n- Add missing enum ([#2223](https://github.com/ory/kratos/issues/2223))\n  ([4b7d7d0](https://github.com/ory/kratos/commit/4b7d7d0011207614ab12f52bb3a911b62581ebe9)):\n\n  Closes https://github.com/ory/sdk/issues/147\n\n- Add output-dir input to cli-next\n  ([#2230](https://github.com/ory/kratos/issues/2230))\n  ([1eb3f18](https://github.com/ory/kratos/commit/1eb3f189f29cc032c44cbd9803acbf99362e5a62))\n- Added malformed config test\n  ([5a3c9c1](https://github.com/ory/kratos/commit/5a3c9c162bd1da5c7bb938192a5e82789bac52cc))\n- Appropriately pass context around\n  ([#2241](https://github.com/ory/kratos/issues/2241))\n  ([668f6b2](https://github.com/ory/kratos/commit/668f6b246db1f61b9800f7581bedba4fa25318c4)):\n\n  Closes https://github.com/ory/cloud/issues/56\n\n- Base redirect URL decoding\n  ([acdefa7](https://github.com/ory/kratos/commit/acdefa7464825e5307132eab5cd2752e1841c3de))\n- Base64 encode identity schema URLs\n  ([ad44e4d](https://github.com/ory/kratos/commit/ad44e4d5f2cea86a95cc376c94fb5f5ac5bc1b82)):\n\n  Previously, identity schema IDs with special characters could lead to broken\n  URLs. This patch introduces a change where identity schema IDs are base64\n  encoded to address this issue. Schema IDs that are not base64 encoded will\n  continue working.\n\n- Broken links API spec\n  ([e1e7516](https://github.com/ory/kratos/commit/e1e75165785f48f5a154c899e1c4168bcbb7d8c3))\n- Cloud config issue\n  ([135b29c](https://github.com/ory/kratos/commit/135b29c647c87569cc85e8a72babb8d6777ebd24))\n- Correct recovery hook\n  ([c7682a8](https://github.com/ory/kratos/commit/c7682a8fd97fdac87d59d3e7fb798384b018c40f))\n- **courier:** Improve composability\n  ([d47150e](https://github.com/ory/kratos/commit/d47150e8440a03ce34d6085fb693bddf2c02620b))\n- Do not error when HIBP behaves unexpectedly\n  ([#2251](https://github.com/ory/kratos/issues/2251))\n  ([a431c1e](https://github.com/ory/kratos/commit/a431c1e1976f740bedb2fec4ce88b7d1b832e42c)),\n  closes [#2145](https://github.com/ory/kratos/issues/2145)\n- Do not remove all credentials when remove all security keys\n  ([#2233](https://github.com/ory/kratos/issues/2233))\n  ([ecd715a](https://github.com/ory/kratos/commit/ecd715a0437c0b068aa0c6a17cd2ba53fe034354))\n- Don't inherit flow type in recovery and verification flows\n  ([#2250](https://github.com/ory/kratos/issues/2250))\n  ([c5b444a](https://github.com/ory/kratos/commit/c5b444aa2bf46b3a86d08f693ab200a30bd4a609)),\n  closes [#2049](https://github.com/ory/kratos/issues/2049)\n- **embed:** Disallow additional props\n  ([b2018ce](https://github.com/ory/kratos/commit/b2018ce3b1667fffc9d0a2c4c82cfafed7f3cac5))\n- **embed:** Do not require plaintext/html in email config\n  ([dfe4140](https://github.com/ory/kratos/commit/dfe4140dda44d4b64988b94272b4776e362abde5))\n- Ensure no internal networks can be called in SMS sender\n  ([65e42e5](https://github.com/ory/kratos/commit/65e42e5cb3a9a3a81e3c623fa066a7651dfb0699))\n- **identity:** Slow query performance on MySQL\n  ([731b3c7](https://github.com/ory/kratos/commit/731b3c7ba48271e2fb6bbd53b0281d5269012332)),\n  closes [#2278](https://github.com/ory/kratos/issues/2278)\n- Improve password error resilience on settings flow\n  ([e614f6e](https://github.com/ory/kratos/commit/e614f6e94e1d0f66f48bd058b015ab467d6b1b07))\n- Improve soundness of credential identifier normalization\n  ([e475163](https://github.com/ory/kratos/commit/e475163330d06ca02cd0419e4b7216f03218e8c5))\n- Incorrect makefile rule ([#2222](https://github.com/ory/kratos/issues/2222))\n  ([83a0ce7](https://github.com/ory/kratos/commit/83a0ce7d20e59c2fb1a35fa071a3d11a9280bcad))\n- **login:** Put passwordless login before password\n  ([df9245f](https://github.com/ory/kratos/commit/df9245fbc403e1b8f2dd1378678963cc0d71ef1a))\n- **lookup:** Resolve credentials counting regression\n  ([50782c6](https://github.com/ory/kratos/commit/50782c68c77ce1c0d8c092678a6710e0be6fa18d))\n- Lower-case jsonnet context for sms\n  ([8c58e94](https://github.com/ory/kratos/commit/8c58e94707122a9b50873ca1acaa32659b5b8416))\n- Mark struct as used\n  ([33f3dfe](https://github.com/ory/kratos/commit/33f3dfeba5af3808f34b16241d74993ceed788be))\n- Mark width and height as required\n  ([#2322](https://github.com/ory/kratos/issues/2322))\n  ([37f2f22](https://github.com/ory/kratos/commit/37f2f220ce699e031018777c9976cafa22faa984)):\n\n  Closes https://github.com/ory/sdk/issues/157\n\n- Move to new post-release steps\n  ([#2206](https://github.com/ory/kratos/issues/2206))\n  ([10778fd](https://github.com/ory/kratos/commit/10778fdd16a116b5dc8f4c2bdc96a895728d9aec))\n- Mr comment fix\n  ([96c917e](https://github.com/ory/kratos/commit/96c917e3c1b02b13be55056bfd94b517007fc206))\n- **oidc:** Improve empty credential handling\n  ([124d4ce](https://github.com/ory/kratos/commit/124d4ce9fe949dcea4fd5ff8e45530835d38cb3c))\n- **oidc:** Incorrect error handling\n  ([c8d789c](https://github.com/ory/kratos/commit/c8d789c10e2be11dfc8c3eea01a339637f89ea63))\n- Order regression\n  ([2cb5d2b](https://github.com/ory/kratos/commit/2cb5d2bf2d645a0e63cf289c966ee8557edbf333))\n- Pass context to registration flow\n  ([c8d55b3](https://github.com/ory/kratos/commit/c8d55b339647cdca3c9beace760dc3a9beac31c1))\n- Pass docs output dir as a separate argument\n  ([78c69a2](https://github.com/ory/kratos/commit/78c69a2790c957bf8102260150d69b1844899ed9))\n- Pass token to render-version-schema\n  ([#2246](https://github.com/ory/kratos/issues/2246))\n  ([4d117e5](https://github.com/ory/kratos/commit/4d117e51abef739d686e48dede63a030a753be41))\n- **password:** Schema regressions\n  ([271d5fa](https://github.com/ory/kratos/commit/271d5fa93f96721d7bf8aa841c700dfec1de4104))\n- Properly check for not found\n  ([77ac199](https://github.com/ory/kratos/commit/77ac199f00f04eb7fd40db6fb546921271026e20))\n- Properly pass context ([#2300](https://github.com/ory/kratos/issues/2300))\n  ([fab8a93](https://github.com/ory/kratos/commit/fab8a939c97e61c028143e37e2a78d3edd569da0))\n- Provide access to root path and error page\n  ([#2317](https://github.com/ory/kratos/issues/2317))\n  ([f360ee8](https://github.com/ory/kratos/commit/f360ee8e65dc64983181746d1059eac53588e029))\n- Rebase regressions\n  ([d1c5085](https://github.com/ory/kratos/commit/d1c508570032c620a654b896111215a76a811517))\n- **registration:** Order for passwordless webauthn\n  ([8427322](https://github.com/ory/kratos/commit/8427322b31fb5206a55e9f62823745fcc6983a22))\n- Remove non-hermetic sprig functions\n  ([#2201](https://github.com/ory/kratos/issues/2201))\n  ([17e0acc](https://github.com/ory/kratos/commit/17e0acc527cfbb703d9d44b776138da23b217ca4)):\n\n  Closes https://github.com/ory/kratos/issues/2087\n\n- Resolve issues with the CI pipeline\n  ([d15bd90](https://github.com/ory/kratos/commit/d15bd90433ed191c2eb41f119ed288906827334e))\n- Resolve merge regression\n  ([d8ca4f3](https://github.com/ory/kratos/commit/d8ca4f327499f94c811c55237f210288fb6a9dd5))\n- Resolve prettier issues\n  ([32bf052](https://github.com/ory/kratos/commit/32bf052f0084860623ea815ed913e94261c89070))\n- Resolve remaining passwordless regressions\n  ([151c8cf](https://github.com/ory/kratos/commit/151c8cfb53402aaf2518a471579c25c3785b13d2))\n- Resovle lint errors\n  ([afb7aaf](https://github.com/ory/kratos/commit/afb7aaf7b019756a624e7f1b2e35fd575882570a))\n- Return 400 instead of 404 on admin recovery\n  ([ae2509c](https://github.com/ory/kratos/commit/ae2509cf7a95f940d33945271ac1fe8fc255506b)),\n  closes [#1664](https://github.com/ory/kratos/issues/1664)\n- **sdk:** Add all available discriminators\n  ([5d70f9c](https://github.com/ory/kratos/commit/5d70f9c70a39067c2d6c0b1f127ff28ca39e77a9)),\n  closes [#2287](https://github.com/ory/kratos/issues/2287)\n  [#2288](https://github.com/ory/kratos/issues/2288)\n- **sdk:** Add webauth and lookup_secret to identityCredentialsType\n  ([#2276](https://github.com/ory/kratos/issues/2276))\n  ([61ce3c0](https://github.com/ory/kratos/commit/61ce3c0c35366f587bfee5c89496fa15432bb241))\n- **sdk:** Correct minimum page to 1\n  ([a28362e](https://github.com/ory/kratos/commit/a28362e054cf12441ed25d8927cd63e3264bfed6)),\n  closes [#2286](https://github.com/ory/kratos/issues/2286)\n- **selfservice:** Cannot login after remove security keys and all other 2FA\n  settings ([#2181](https://github.com/ory/kratos/issues/2181))\n  ([5ff6773](https://github.com/ory/kratos/commit/5ff6773ab8512bdfb8d2c7b650970711cbb012ba)),\n  closes [#2180](https://github.com/ory/kratos/issues/2180)\n- **selfservice:** Login self service flow with TOTP does not pass on return_to\n  URL ([#2175](https://github.com/ory/kratos/issues/2175))\n  ([3eaa88e](https://github.com/ory/kratos/commit/3eaa88e74e1540b14b6e41df2881346c60b92046)),\n  closes [#2172](https://github.com/ory/kratos/issues/2172)\n- **session:** Correctly calculate aal for passwordless webauthn\n  ([c7eb970](https://github.com/ory/kratos/commit/c7eb970ed252577e06d3d769d2545d5e8e98175a))\n- **session:** Properly declare session secrets\n  ([6312afd](https://github.com/ory/kratos/commit/6312afd2eb0d1dc808d600a902eb1e16b07fd9cb)),\n  closes [#2272](https://github.com/ory/kratos/issues/2272):\n\n  Previously, a misconfiguration of Gorilla's session store caused incorrect\n  handling of the configured secrets. From now on, cookies will also be properly\n  encrypted at all times.\n\n- Snapshot regression\n  ([6481441](https://github.com/ory/kratos/commit/6481441fe7df1a2fc43ff153697e9bd2160c49b3))\n- Static analysis\n  ([a1d3254](https://github.com/ory/kratos/commit/a1d3254346ec0bcc0a8c42bf66a8171e027f0d97))\n- **test:** Parallelization issues\n  ([dbcf3fb](https://github.com/ory/kratos/commit/dbcf3fb616db64e1b1f4cb5066113f703ca0b2ee))\n- **text:** Incorrect IDs for different messages\n  ([0833321](https://github.com/ory/kratos/commit/0833321e04e9865046294b051376bed415a41441)),\n  closes [#2277](https://github.com/ory/kratos/issues/2277)\n- **totp:** Resolve credentials counting regression\n  ([737bb3f](https://github.com/ory/kratos/commit/737bb3f71e91f7c735231d0131072aca4f5622ea))\n- Typo\n  ([fbc8b4f](https://github.com/ory/kratos/commit/fbc8b4f9901e7761bef9a7f74a483cb077007cf8))\n- Typo\n  ([3bb0d41](https://github.com/ory/kratos/commit/3bb0d41e3696be90cfc12f1bf00a546536e283b6))\n- Unstable ordering\n  ([bee26c6](https://github.com/ory/kratos/commit/bee26c65c9511af82b9ed2051ab4f45b9570602d))\n- Unstable webauthn order\n  ([6262160](https://github.com/ory/kratos/commit/626216098fcd9411c1b4b7cb3b42784146b29924))\n- Updated oathkeeper+kratos example\n  ([#2273](https://github.com/ory/kratos/issues/2273))\n  ([567a3d7](https://github.com/ory/kratos/commit/567a3d765aa2115951f6af5b4ed4d2c791231de0))\n- URL with hash sign in after_verification_return_to stays encoded\n  ([#2173](https://github.com/ory/kratos/issues/2173))\n  ([fb1cb8a](https://github.com/ory/kratos/commit/fb1cb8a993cbf6cb050d7dce91672b05efd53224)),\n  closes [#2068](https://github.com/ory/kratos/issues/2068)\n- Use actions/checkout for ui repos\n  ([f0136ca](https://github.com/ory/kratos/commit/f0136cac639862bf50933063b7dc38973739139b))\n- Use correct dir for clidoc\n  ([8c8a1ab](https://github.com/ory/kratos/commit/8c8a1ab7b41fa026189cec8d1f77e2e89c696d11))\n- Use HTTP 303 instead of 302 for selfservice redirects\n  ([#2215](https://github.com/ory/kratos/issues/2215))\n  ([50b6bd8](https://github.com/ory/kratos/commit/50b6bd892ae6efba34773811ef488f15fc95154f)),\n  closes [#1969](https://github.com/ory/kratos/issues/1969)\n- Use latest hydra version\n  ([ffb3f20](https://github.com/ory/kratos/commit/ffb3f20e67d357160c024f5e58ebf63a9aec41ff))\n- **webauthn:** Resolve missing identifier bug\n  ([93a1ae4](https://github.com/ory/kratos/commit/93a1ae4fe98487a0bca00d2afdc5e7b07c0e1c46))\n- **webauthn:** Schema regressions\n  ([970e861](https://github.com/ory/kratos/commit/970e861714ec01c5cfe19545871798d9ad0ae70c))\n- **webauth:** SPA regressions for login\n  ([be378ff](https://github.com/ory/kratos/commit/be378ffa5ddbd56a00b471dce861ec074eed5192))\n- Yq version\n  ([41b6f18](https://github.com/ory/kratos/commit/41b6f1879f23866c070100dd1767f841bff3a815))\n\n### Code Generation\n\n- Pin v0.9.0-alpha.1 release commit\n  ([72bd2ed](https://github.com/ory/kratos/commit/72bd2ed67559a64415b2686e8f67c42df888e49e))\n\n### Code Refactoring\n\n- All admin endpoints are now exposed under `/admin/` on the admin port\n  ([8acb4cf](https://github.com/ory/kratos/commit/8acb4cfaa61ef52619e889b8c862191c6b92e5eb))\n- Distinguish between first and multi factor credentials\n  ([8de9d01](https://github.com/ory/kratos/commit/8de9d01d9edae485f5a6ea7c68584ba4019a24d6))\n- Identity.default_schema_url is now `identity.default_schema_id`\n  ([#1964](https://github.com/ory/kratos/issues/1964))\n  ([e4f205d](https://github.com/ory/kratos/commit/e4f205d69bec07a71bf1d34d97ab3a6b99a4cc46))\n- **identity:** Move credentials counter\n  ([c9875a7](https://github.com/ory/kratos/commit/c9875a7582accc740061e6a19d7b4b0998899f3f))\n- Mimic credentials config on import\n  ([c3eb7ce](https://github.com/ory/kratos/commit/c3eb7ce60597954a60b8903ac011a643d0facf12))\n- Move credential configs for oidc and password\n  ([50ac851](https://github.com/ory/kratos/commit/50ac851cc4534aa474a76c208f15483548ec8631))\n- Move docs to ory/docs\n  ([57151da](https://github.com/ory/kratos/commit/57151da6adc85753d54c108637298642ccbc8347))\n- **oidc:** Credentials counting\n  ([b75a639](https://github.com/ory/kratos/commit/b75a6390de85e10db8e9e17a74e95dd6dd716442))\n- **password:** DRY up registration helpers\n  ([8a51839](https://github.com/ory/kratos/commit/8a51839ba85ddb5a345fef65f30b4325103ce38a))\n- **password:** Internals and deprecated fields\n  ([a7784bd](https://github.com/ory/kratos/commit/a7784bdb52aff0ac171e59b2301755b65c842813))\n- Rename `password_identifier` field to `identifier`\n  ([4dbe0ea](https://github.com/ory/kratos/commit/4dbe0ea41f49e198840292fc101258a4bdca826e))\n- Rename `whitelisted_return_urls` to `allowed_return_urls`\n  ([#2299](https://github.com/ory/kratos/issues/2299))\n  ([686c9ba](https://github.com/ory/kratos/commit/686c9ba08ff1db8a310eaed5c4b3aec69e0f84da))\n- **session:** Aal computation\n  ([a136de9](https://github.com/ory/kratos/commit/a136de99a0f8fe78ee344f2243359c781b166378))\n- Update apple and microsoft config key names\n  ([#2261](https://github.com/ory/kratos/issues/2261))\n  ([6da2370](https://github.com/ory/kratos/commit/6da2370b4e6833ef61ca03214261e45c4786cb44)),\n  closes [#1979](https://github.com/ory/kratos/issues/1979)\n\n### Documentation\n\n- Add debug tip ([#2186](https://github.com/ory/kratos/issues/2186))\n  ([a1ada22](https://github.com/ory/kratos/commit/a1ada2255d132b1f3ea8cb494620b9c17b42f161))\n- Add react example code ([#2185](https://github.com/ory/kratos/issues/2185))\n  ([0689cc7](https://github.com/ory/kratos/commit/0689cc73ccc9a472c5610f1e011c6ccbc5e0c20d))\n- Cloud\n  ([8d1d65d](https://github.com/ory/kratos/commit/8d1d65d9d12a894bd25c82394e0392e228fe383d))\n- Fix broken links\n  ([d88c56f](https://github.com/ory/kratos/commit/d88c56fc0ebf042d1270d04a2382784e5200654d))\n- Fix broken links API doc ([#2296](https://github.com/ory/kratos/issues/2296))\n  ([47eaae5](https://github.com/ory/kratos/commit/47eaae575023469834c0c3a4aac64dc6d880e164))\n- Fix versions\n  ([7186ff3](https://github.com/ory/kratos/commit/7186ff354b9c3d0fbd3fb809546075fcfcd0c57f))\n- Replace all mentions of Ory Kratos SDK with Ory SDK\n  ([#2187](https://github.com/ory/kratos/issues/2187))\n  ([4e6897f](https://github.com/ory/kratos/commit/4e6897ff2220b5668d784a16dd1f48db30f271f0))\n- Update readme\n  ([e7d9da1](https://github.com/ory/kratos/commit/e7d9da199825fb15ae720c0496a257590b353a26))\n\n### Features\n\n- Abandon courier messages after configurable timeout\n  ([#2257](https://github.com/ory/kratos/issues/2257))\n  ([bff92f7](https://github.com/ory/kratos/commit/bff92f73b3f12d2dffa2061eb0e51e746eba2185))\n- Add `webauthn` to list of identifiers\n  ([1a8b256](https://github.com/ory/kratos/commit/1a8b256cca33aa9cbb143e7e8fc1efc8217e9b8a)):\n\n  This patch adds the key `webauthn` to the list of possible identifiers in the\n  Identity JSON Schema. Use this key to specify what field is used to find the\n  WebAuthn credentials on passwordless login flows.\n\n- Add credential migrator pattern\n  ([77afc6f](https://github.com/ory/kratos/commit/77afc6f8ea868eaba7853adfcb9ed159b44ecbc8))\n- Add message for missing webauthn credentials\n  ([303dc6b](https://github.com/ory/kratos/commit/303dc6bc33c20cd619d2542180247bd7b7f02092))\n- Add new messages\n  ([09e6fd1](https://github.com/ory/kratos/commit/09e6fd16bb6be0ff3ee209bbfe69e967546f70da))\n- Add npm install step\n  ([3d253e5](https://github.com/ory/kratos/commit/3d253e58ec7d4464d9749efe6ecc4a5c1d9be789))\n- Add versioning and improve compatibility for credential migrations\n  ([78ce668](https://github.com/ory/kratos/commit/78ce668a38c914939028be42cd30eefa566ed09a))\n- Added sms sending support to courier\n  ([687eca2](https://github.com/ory/kratos/commit/687eca24aac7a7b89cc949693271343573107898))\n- Allow empty version string\n  ([419f94b](https://github.com/ory/kratos/commit/419f94bc1065771e49982faf56f8ef90a30bc306))\n- Cancelable web hooks\n  ([44a5323](https://github.com/ory/kratos/commit/44a5323f835860dccd11460d666f620026e8b58d)):\n\n  Introduces the ability to cancel web hooks by calling `error \"cancel\"` in\n  JsonNet.\n\n- **config:** Add option to mark webauthn as passwordless-able\n  ([0455e3f](https://github.com/ory/kratos/commit/0455e3fe901cff6ff314fd59a35864886672327c)):\n\n  Adds option `passwordless` to `selfservice.methods.webauthn.config`, making it\n  possible to use WebAuthn for first-factor authentication, or so-called\n  \"passwordless\" authentication.\n\n- Courier template configs ([#2156](https://github.com/ory/kratos/issues/2156))\n  ([799b6a8](https://github.com/ory/kratos/commit/799b6a81add747d3001a1758e08ee7b4c6463d64)),\n  closes [#2054](https://github.com/ory/kratos/issues/2054):\n\n  It is now possible to override individual courier email templates using the\n  configuration system!\n\n- **courier:** Expose setters again\n  ([598dc3a](https://github.com/ory/kratos/commit/598dc3a4d7c27838e9058382378972a1c0330bde))\n- **e2e:** Add passwordless flows and fix bugs\n  ([ef3871b](https://github.com/ory/kratos/commit/ef3871bd9b3e7e5f4360da8d1b7749cc005b4e19))\n- **identity:** Add identity credentials helpers\n  ([b7be327](https://github.com/ory/kratos/commit/b7be327a370368932ff390968acffaa1ce6d55a0))\n- **identity:** Add versioning to credentials\n  ([aaf779a](https://github.com/ory/kratos/commit/aaf779ac1c29b24ece6d5f3d7892a3bf08277653))\n- Ignore web hook response\n  ([ae87914](https://github.com/ory/kratos/commit/ae87914512025c05d814a1200eda66d8f931ce44)):\n\n  Introduces the ability to ignore responses from web hooks in favor of faster\n  and non-blocking execution.\n\n- Make sensitive log value redaction text configurable\n  ([#2321](https://github.com/ory/kratos/issues/2321))\n  ([9b66e43](https://github.com/ory/kratos/commit/9b66e437d0aeed61643b76aea7d49cad001dc8cf))\n- **oidc:** Customizable base redirect uri\n  ([fa1f234](https://github.com/ory/kratos/commit/fa1f23469f2fecfa82fa38147f601d969bd9aaa4)):\n\n  Closes https://github.com/ory-corp/cloud/issues/2003\n\n- Password, social sign, verified email in import\n  ([41a27b1](https://github.com/ory/kratos/commit/41a27b1e15e090d3e99cdcfc3c1ba8eac76097a4)),\n  closes [#605](https://github.com/ory/kratos/issues/605):\n\n  This patch introduces the ability to import passwords (cleartext, PKBDF2,\n  Argon2, BCrypt) and Social Sign In connections when creating identities!\n\n- **recovery:** Allow invalidation of existing sessions\n  ([5029884](https://github.com/ory/kratos/commit/502988474e2bce46752f7fc7885bc1b91423bbdd)),\n  closes [#1077](https://github.com/ory/kratos/issues/1077):\n\n  You can now use the `revoke_active_sessions` hook in the recovery flow. It\n  invalidates all of an identity's sessions on successful account recovery.\n\n- **schema:** Add functionality to disallow internal HTTP requests\n  ([6e08416](https://github.com/ory/kratos/commit/6e08416235bd821493df4d9cda2e8bd76d507871)):\n\n  See https://github.com/ory-corp/cloud/issues/1261\n\n- **security:** Add e2e tests for various private network SSRF defenses\n  ([b049bc3](https://github.com/ory/kratos/commit/b049bc304cd79568ee82f1423e583949f63d3377))\n- **security:** Add SSRF defenses in OIDC\n  ([d37dc5d](https://github.com/ory/kratos/commit/d37dc5d7946252783463bc9e99f7f792e2735614))\n- **session:** Add webauthn to extension validation\n  ([049fd8e](https://github.com/ory/kratos/commit/049fd8edc382f344018398027a4e0b3915116ff2))\n- **session:** Webauthn can now be a first factor as well\n  ([861bee0](https://github.com/ory/kratos/commit/861bee0f029e3bb3f6b7218be19eaf6c26562b76))\n- Trace web hook calls ([#2154](https://github.com/ory/kratos/issues/2154))\n  ([98ee300](https://github.com/ory/kratos/commit/98ee300e065c6e81e6128a509af3f48612cda88a))\n- **webauthn:** Add error preventing deleting last webauthn credential\n  ([1209eda](https://github.com/ory/kratos/commit/1209edacaf1b7dea32bd1bd124c86910bc2553c6))\n- **webauthn:** Add new decoder schemas\n  ([c3e1501](https://github.com/ory/kratos/commit/c3e1501bf5170416a034130eb68d1db456a47239))\n- **webauthn:** Add passwordless credentials indicator\n  ([6e3057a](https://github.com/ory/kratos/commit/6e3057a96a34d22cac193e5c17b4a3c01d2ca045))\n- **webauthn:** Add swagger type\n  ([14c2b74](https://github.com/ory/kratos/commit/14c2b745e951a185dee600f6f2e8f93788c67285))\n- **webauthn:** Count passwordless credentials\n  ([145af23](https://github.com/ory/kratos/commit/145af23aef8f5c9ffdcec47bac5758da709d4646))\n- **webauthn:** Implement refresh using webauth\n  ([bf10868](https://github.com/ory/kratos/commit/bf108688ed146211da3cc2ec4bf0df015e535220)),\n  closes [#2284](https://github.com/ory/kratos/issues/2284):\n\n  This change introduces the ability to refresh a session (for example when\n  entering \"sudo\" mode\") using WebAuthn credentials. In this case, it does not\n  matter whether the WebAuthN credentials are for MFA or passwordless flows.\n\n- **webauthn:** Improve schema\n  ([790dcf3](https://github.com/ory/kratos/commit/790dcf3a7079d57a088d399c03d040af1019a3aa))\n- **webauthn:** Manage webauthn passwordless keys\n  ([5a62ced](https://github.com/ory/kratos/commit/5a62ced175248a85b1e843b4017757aa86d62d23))\n- **webauthn:** Passwordless login\n  ([b4c4fd2](https://github.com/ory/kratos/commit/b4c4fd2c25ae5d55350ce573df8295fe6d8c42a1))\n- **webauthn:** Update messages and nodes\n  ([22534d8](https://github.com/ory/kratos/commit/22534d8253384f2002033a5b2bbdcf573779a49c))\n- **webauthn:** Use plain bytes for wrapped user\n  ([97c8c9e](https://github.com/ory/kratos/commit/97c8c9e25234847622f1ab508cd5d50758d323c0))\n\n### Tests\n\n- Add data for new migration\n  ([b0488ef](https://github.com/ory/kratos/commit/b0488efa600024f40b2c019fa0f492dd39c8bfa9))\n- Add tests for new sms options\n  ([799fa10](https://github.com/ory/kratos/commit/799fa106cd0fed33afbe76903911df9292d49bf6))\n- **cmd:** Fix regressions\n  ([4b92be9](https://github.com/ory/kratos/commit/4b92be9325d02e605e12d96c7990774234ed1d1d))\n- **driver:** Fix regressions\n  ([c6f5137](https://github.com/ory/kratos/commit/c6f51377f253275bf7321c67a5e949699ac12adb))\n- **e2e:** Add import tests\n  ([ed90f39](https://github.com/ory/kratos/commit/ed90f394d32ee0a3e42c3a9c1c066f94a05d02c1))\n- **e2e:** Reenable hydra\n  ([055a491](https://github.com/ory/kratos/commit/055a4912d3e7712d4bc3a3f5cf9c68d1834998dc))\n- **e2e:** Resolve privileged regression\n  ([f7dd5ab](https://github.com/ory/kratos/commit/f7dd5aba26b43aa9f60d8429a7d256f48f228578))\n- **e2e:** Resolve regression\n  ([b5053c9](https://github.com/ory/kratos/commit/b5053c902331ae166824eb92b89295e693bf0dc7))\n- **e2e:** Resolve regressions\n  ([da154c5](https://github.com/ory/kratos/commit/da154c5e549f79ca5703209852981ded07281f43))\n- **e2e:** Resolve regressions\n  ([d46d435](https://github.com/ory/kratos/commit/d46d435c40c383bbd844af8fead283ee46a137fb))\n- **e2e:** Resolve regressions and flakes\n  ([a607385](https://github.com/ory/kratos/commit/a60738510875f770f9dbb0b3449dbcf2d473ada3))\n- **e2e:** Wait for initial network requests\n  ([#2242](https://github.com/ory/kratos/issues/2242))\n  ([c5a04b5](https://github.com/ory/kratos/commit/c5a04b5f174e06faca99ebc7461c8ebe8e1f694d))\n- Extract common registration helpers to library\n  ([5c1f11b](https://github.com/ory/kratos/commit/5c1f11b2ae65dd73d572e456b522a7d83ac1f473))\n- Fix concurrent database access\n  ([46f6fb7](https://github.com/ory/kratos/commit/46f6fb7d246b384e561bdf8952185855f25cce56))\n- Fix regression\n  ([f96e48f](https://github.com/ory/kratos/commit/f96e48fa6d4d8b341bcd3f52228b7abff8b934fb))\n- **identity:** Ensure migrations run when fetching identities\n  ([322d467](https://github.com/ory/kratos/commit/322d467ac11dcdf4e3210f947b80029c77662065))\n- **identity:** Fix regressions\n  ([f492f0e](https://github.com/ory/kratos/commit/f492f0e1d112813d926eac48b5ad5d2e1857a382))\n- Re-enable MySQL\n  ([cbe8f6e](https://github.com/ory/kratos/commit/cbe8f6ea4fe48fe84a5cbc8915754f83e7eff428))\n- Remove obsolete test\n  ([cd644ae](https://github.com/ory/kratos/commit/cd644aef9175fe21024c37a381722503fcd88555))\n- Remove obsolete test failure\n  ([f8fd480](https://github.com/ory/kratos/commit/f8fd48041404344636c51b63d55a668209bed0e0))\n- Remove only\n  ([87b3bce](https://github.com/ory/kratos/commit/87b3bce3433601dd918f76c0bc2d25ea4af6e482))\n- Remove unnecessary test\n  ([2fa33e4](https://github.com/ory/kratos/commit/2fa33e4f28759b5dc5de78e00e42ed8cc4ccce89))\n- Resolve potential panic\n  ([d44af28](https://github.com/ory/kratos/commit/d44af289e9c09a981e80b6f69d22a5cce6b1dbfa))\n- **schema:** Resolve regressions\n  ([c6d0810](https://github.com/ory/kratos/commit/c6d08105a270fafd21a14a19e412d7081dedc754))\n- Significantly reduce persister run time\n  ([647d6ef](https://github.com/ory/kratos/commit/647d6ef73797462020c2f59ece15e645561182b0))\n- Update fixtures\n  ([21462b7](https://github.com/ory/kratos/commit/21462b7eb8cbac719d8ae531969b0fd9d42b5e0c))\n- Update fixtures\n  ([299c6e3](https://github.com/ory/kratos/commit/299c6e3be7c120bb769a4b2572ebe42c5ab3ddb1))\n- **webauthn:** Add passwordless profile\n  ([88199ea](https://github.com/ory/kratos/commit/88199ea28e8b3460ccc585e5fd1713d398cae15c))\n- **webauthn:** Passwordless registration\n  ([c9b6280](https://github.com/ory/kratos/commit/c9b6280720c2fd08191994c86e85ceb1f52a27d2))\n\n### Unclassified\n\n- Move login hinting to own package\n  ([1eb2604](https://github.com/ory/kratos/commit/1eb260423491af917edb1256d260ca3d3fb198dc))\n\n# [0.8.3-alpha.1.pre.0](https://github.com/ory/kratos/compare/v0.8.2-alpha.1...v0.8.3-alpha.1.pre.0) (2022-01-21)\n\nautogen: pin v0.8.3-alpha.1.pre.0 release commit\n\n## Breaking Changes\n\nThis patch removes the ability to use domain aliases, an obscure feature rarely\nused that had several issues and inconsistencies.\n\n### Bug Fixes\n\n- Add `identity_id` index to `identity_verifiable_addresses` table\n  ([#2147](https://github.com/ory/kratos/issues/2147))\n  ([86fd942](https://github.com/ory/kratos/commit/86fd942e9a80e36dd65ef4ac57c5a5546f94995a)):\n\n  The verifiable addresses are loaded eagerly into the identity. When that\n  happens, the `identity_verifiable_addresses` table is queried by `nid` and\n  `identity_id`. This index should greatly improve performance, especially of\n  the `/sessions/whoami` endpoint.\n\n- Add ability to resume continuity sessions from several cookies\n  ([#2131](https://github.com/ory/kratos/issues/2131))\n  ([8b87bdb](https://github.com/ory/kratos/commit/8b87bdb1967654b5fbfbf9799948485b2a9a6af0)),\n  closes [#2016](https://github.com/ory/kratos/issues/2016)\n  [#1786](https://github.com/ory/kratos/issues/1786)\n- Add hiring notice to README\n  ([#2074](https://github.com/ory/kratos/issues/2074))\n  ([0c1e816](https://github.com/ory/kratos/commit/0c1e816693ad4a6c3fdb7206bbc95c81cdfdf3c0))\n- Add missing version tag in quickstart.yml\n  ([#2110](https://github.com/ory/kratos/issues/2110))\n  ([1d281ea](https://github.com/ory/kratos/commit/1d281ea69e551cc3d40415f5405690f445891bb6))\n- Adjust scan configuration ([#2140](https://github.com/ory/kratos/issues/2140))\n  ([8506fcf](https://github.com/ory/kratos/commit/8506fcf59d572851b24041b48af6a04b31520a32)),\n  closes [#2083](https://github.com/ory/kratos/issues/2083)\n- Admin endpoint `/schemas` not redirecting to public endpoint\n  ([#2133](https://github.com/ory/kratos/issues/2133))\n  ([413833f](https://github.com/ory/kratos/commit/413833f128c0674f4e8dbb9e73698a9df04cfc1a)),\n  closes [#2084](https://github.com/ory/kratos/issues/2084)\n- Choose correct CSRF cookie when multiple are set\n  ([633076b](https://github.com/ory/kratos/commit/633076be008104afd50186ebe60722ef21999d5d)),\n  closes [ory/kratos#2121](https://github.com/ory/kratos/issues/2121)\n  [ory-corp/cloud#1786](https://github.com/ory-corp/cloud/issues/1786):\n\n  Resolves an issue where, when multiple CSRF cookies are set, a random one\n  would be used to verify the CSRF token. Now, regardless of how many\n  conflicting CSRF cookies exist, if one of them is valid, the request will pass\n  and clean up the cookie store.\n\n- **continuity:** Properly reset cookies that became invalid\n  ([8e4b4fb](https://github.com/ory/kratos/commit/8e4b4fb3d6dbe668cf0166f4cff49eae753d481c)),\n  closes [#2121](https://github.com/ory/kratos/issues/2121)\n  [ory-corp/cloud#1786](https://github.com/ory-corp/cloud/issues/1786):\n\n  Resolves several reports related to incorrect handling of invalid continuity\n  issues.\n\n- **continuity:** Remove cookie on any error\n  ([428ac03](https://github.com/ory/kratos/commit/428ac03b582184dbbbc0c9c3ffd399273fd8e1a5))\n- Do not send session after registration without hook\n  ([#2094](https://github.com/ory/kratos/issues/2094))\n  ([3044229](https://github.com/ory/kratos/commit/3044229227229e81a4ba770eec241a748dd0945c)),\n  closes [#2093](https://github.com/ory/kratos/issues/2093)\n- Docker-compose standalone definition\n  ([3c7065a](https://github.com/ory/kratos/commit/3c7065ad32ff314c8cbdad8ed89fd9a9f5928f72))\n- Explain mitigations in cookie error messages\n  ([ef4b01a](https://github.com/ory/kratos/commit/ef4b01a80ea91114b182ff26759d98cd5ba2cd02))\n- Expose network wrapper\n  ([a570607](https://github.com/ory/kratos/commit/a570607d460e7c5f9d49ce38ba7a4e06ae172359))\n- Faq ([#2101](https://github.com/ory/kratos/issues/2101))\n  ([311f906](https://github.com/ory/kratos/commit/311f9066a524308b970afc81d98d1a14b78bf63d)):\n\n  This patch\n  - moves the FAQ to the Debug & Help section\n  - renames it to Tips & Troubleshooting\n  - moves many of the questions to documents where they fit better, reformatted\n    and with added information where needed.\n  - also some other spelling/format fixes\n\n  See also https://github.com/ory/docusaurus-template/pull/87\n\n- Ignore whitespace around identifier with password strategy\n  ([#2160](https://github.com/ory/kratos/issues/2160))\n  ([45335c5](https://github.com/ory/kratos/commit/45335c50f719af504974fe54e504d7653db03c78)),\n  closes [#2158](https://github.com/ory/kratos/issues/2158)\n- Improve courier test signature\n  ([b8888e3](https://github.com/ory/kratos/commit/b8888e3c93a602635b396503b7301396ce740ff8))\n- Include missing type string in config schema\n  ([#2142](https://github.com/ory/kratos/issues/2142))\n  ([ec2c88a](https://github.com/ory/kratos/commit/ec2c88ac2d65ea1db1146101519cdbb709ebdbbb)):\n\n  Inside the config.schema.json under the CORS setting, add the missing type\n  (string) for the items of the allowed_origins array\n\n- **login:** Error handling when failed to prepare for an expired flow\n  ([#2120](https://github.com/ory/kratos/issues/2120))\n  ([fdad834](https://github.com/ory/kratos/commit/fdad834e7577e298887b83b693ddf20632cd7c43))\n- Minor fixes in FAQ update ([#2130](https://github.com/ory/kratos/issues/2130))\n  ([b53eec7](https://github.com/ory/kratos/commit/b53eec721489514a80719b73bc5c758dc2adedfd))\n- Quickstart standalone service definition\n  ([#2149](https://github.com/ory/kratos/issues/2149))\n  ([872b06e](https://github.com/ory/kratos/commit/872b06e1f798deacfef101edc3ab33fd75af9b29))\n- Resolve configx regression\n  ([672c0ff](https://github.com/ory/kratos/commit/672c0ffc7f5edd1fd238dcdd0c5d0430b30966c6))\n- **selfservice:** Recovery self service flow passes on return_to URL\n  ([#1920](https://github.com/ory/kratos/issues/1920))\n  ([b925d35](https://github.com/ory/kratos/commit/b925d351dd0ce48cb6aed046dcf2698796453751)),\n  closes [#914](https://github.com/ory/kratos/issues/914)\n- Send 404 instead of null response for unknown verification flows\n  ([#2102](https://github.com/ory/kratos/issues/2102))\n  ([c9490c8](https://github.com/ory/kratos/commit/c9490c8927209b686aafe54b8a16207a8ef47ebe)),\n  closes [#2099](https://github.com/ory/kratos/issues/2099):\n\n  Fixes the verification handler to write the error, instead of nil object, when\n  the flow does not exist. Adds tests for every handler to check proper behavior\n  in that regard.\n\n- Support setting complex configs from the environment\n  ([c45bf83](https://github.com/ory/kratos/commit/c45bf83a9e6744a0b3f2f24e3b07a6f0131d9a40)):\n\n  Closes https://github.com/ory/kratos/issues/1535 Closes\n  https://github.com/ory/kratos/issues/1792 Closes\n  https://github.com/ory/kratos/issues/1801\n\n- Update download urls according to the new names\n  ([#2078](https://github.com/ory/kratos/issues/2078))\n  ([86ae016](https://github.com/ory/kratos/commit/86ae0166c8893b809929c7c45a2ba84416ddf228))\n\n### Code Generation\n\n- Pin v0.8.3-alpha.1.pre.0 release commit\n  ([b1f1da2](https://github.com/ory/kratos/commit/b1f1da2c0b4fbf6e6b4259c58b39a3e88e990142))\n\n### Code Refactoring\n\n- Deprecate domain aliases\n  ([894a2cc](https://github.com/ory/kratos/commit/894a2cc39671fbc9d2c13b1fc1b45b217da5145d))\n\n### Documentation\n\n- Fix incorrect port\n  ([c9a3587](https://github.com/ory/kratos/commit/c9a358717a99af436c6802f45c9c1f6edc77585f)),\n  closes [#2095](https://github.com/ory/kratos/issues/2095)\n- Fix link\n  ([c245ed4](https://github.com/ory/kratos/commit/c245ed40d443e3068bc5eee902e6b14f6ae777c6)):\n\n  Closes https://github.com/ory/kratos-selfservice-ui-node/issues/164\n\n- Ory cloud mentions + spelling\n  ([#2100](https://github.com/ory/kratos/issues/2100))\n  ([0c2fa5b](https://github.com/ory/kratos/commit/0c2fa5bdb98b95877ef740297b6d96a931a3430f))\n- Pagination ([#2143](https://github.com/ory/kratos/issues/2143))\n  ([0807a03](https://github.com/ory/kratos/commit/0807a03fba8ff9a3123cd038a472e90895502e82)),\n  closes [#2039](https://github.com/ory/kratos/issues/2039)\n- Typo ([#2073](https://github.com/ory/kratos/issues/2073))\n  ([e1a54f9](https://github.com/ory/kratos/commit/e1a54f9129d41b34cc8864c8ac38d1448e1f9372))\n- Typo ([#2114](https://github.com/ory/kratos/issues/2114))\n  ([a7a16d7](https://github.com/ory/kratos/commit/a7a16d7c91d89e274ea5fd79787cd4671d825532))\n- Update docker guide\n  ([072ca4d](https://github.com/ory/kratos/commit/072ca4d990cf4060555c8b2626f39ff18172d064)),\n  closes [#2086](https://github.com/ory/kratos/issues/2086)\n- Upgrade guide ([#2132](https://github.com/ory/kratos/issues/2132))\n  ([4a4ab05](https://github.com/ory/kratos/commit/4a4ab05573ebb20f82f62bfd38767de68d7708e9)):\n\n  Closes https://github.com/ory/kratos/discussions/2104\n\n### Features\n\n- Add preset CSP nonce ([#2096](https://github.com/ory/kratos/issues/2096))\n  ([8913292](https://github.com/ory/kratos/commit/8913292c1193c416e5a54997e3635bef87affc01)):\n\n  Closes https://github.com/ory/kratos-selfservice-ui-node/issues/162\n\n- Added phone number identifier\n  ([#1938](https://github.com/ory/kratos/issues/1938))\n  ([294dfa8](https://github.com/ory/kratos/commit/294dfa85b4552b9266c44bb3376b8610c1ff5521)),\n  closes [#137](https://github.com/ory/kratos/issues/137)\n- Allow registration to be disabled\n  ([#2081](https://github.com/ory/kratos/issues/2081))\n  ([864b00d](https://github.com/ory/kratos/commit/864b00d6ecddefdb06ac22fda04670bfa43f2fd5)),\n  closes [#882](https://github.com/ory/kratos/issues/882)\n- Courier templates fs support\n  ([#2164](https://github.com/ory/kratos/issues/2164))\n  ([13689a7](https://github.com/ory/kratos/commit/13689a7135311a05b17383486f5fdab2e7a412d0))\n- **courier:** Override default link base URL\n  ([cc99096](https://github.com/ory/kratos/commit/cc99096d07408c8b713ef9a7b17b8345597a9129)):\n\n  Added a new configuration value `selfservice.methods.link.config.base_url`\n  which allows to change the default base URL of recovery and verification\n  links. This is useful when the email should send a link which does not match\n  the globally configured base URL.\n\n  See https://github.com/ory-corp/cloud/issues/1766\n\n- **docker:** Add jaeger\n  ([27ec2b7](https://github.com/ory/kratos/commit/27ec2b74ee42697102c6a9a79bc5ca3c09756d94))\n- Enable Buildkit ([#2079](https://github.com/ory/kratos/issues/2079))\n  ([f40df5c](https://github.com/ory/kratos/commit/f40df5cd932aa3185b2155368db51a49b7f05991)):\n\n  Looks like this was attempted before but the magic comment was not on the\n  first line.\n\n- Expose courier template load\n  ([#2082](https://github.com/ory/kratos/issues/2082))\n  ([790716e](https://github.com/ory/kratos/commit/790716e58a4be06f04f3cbc5b974f16d873ae0d8))\n- Generalise courier tests ([#2125](https://github.com/ory/kratos/issues/2125))\n  ([75c6053](https://github.com/ory/kratos/commit/75c60537e366760fe87b7b8978e9854873b7f702))\n- Make the password policy more configurable\n  ([#2118](https://github.com/ory/kratos/issues/2118))\n  ([70c627b](https://github.com/ory/kratos/commit/70c627b9feb3ec55765070b7c6c3fd64f2640e59)),\n  closes [#970](https://github.com/ory/kratos/issues/970)\n- **security:** Add option to disallow private IP ranges in webhooks\n  ([05f1e5a](https://github.com/ory/kratos/commit/05f1e5a99426ed54cb70514554e64d851f0ba8d6)),\n  closes [#2152](https://github.com/ory/kratos/issues/2152)\n- Selfservice and administrative session management\n  ([#2011](https://github.com/ory/kratos/issues/2011))\n  ([0fe4155](https://github.com/ory/kratos/commit/0fe4155b878102b77f7f13de5f0754ff75961498)),\n  closes [#655](https://github.com/ory/kratos/issues/655)\n  [#2007](https://github.com/ory/kratos/issues/2007)\n\n### Tests\n\n- Update cypress ([#2090](https://github.com/ory/kratos/issues/2090))\n  ([883a1b1](https://github.com/ory/kratos/commit/883a1b1ea33a1d3ef8b33342328382b59e4f18c3))\n\n# [0.8.2-alpha.1](https://github.com/ory/kratos/compare/v0.8.1-alpha.1...v0.8.2-alpha.1) (2021-12-17)\n\nThis release addresses further important security updates in the base Docker\nImages. We also resolved all issues related to ARM support on both Linux and\nmacOS and fixed a bug that prevent the binary from compiling on FreeBSD.\n\nThis release also makes use of our new build architecture which means that the\nDocker Images names have changed. We removed the \"scratch\" images as we received\nfrequent complaints about them. Additionally, all Docker Images have now, per\ndefault, SQLite support built-in. If you are relying on the SQLite images,\nupdate your Docker Pull commands as follows:\n\n```patch\n- docker pull oryd/kratos:{version}-sqlite\n+ docker pull oryd/kratos:{version}\n```\n\nAdditionally, all passwords now have to be at least 8 characters long, following\nrecommendations from Microsoft and others.\n\nIn v0.8.1-alpha.1 we failed to include all the exciting things that landed, so\nwe'll cover them now!\n\n1. Advanced E-Mail templating support with sprig - makes it possible to\n   translate emails as well!\n2. Support wildcards for allowing redirection targets.\n3. Account Recovery initiated by the Admin API now works even if identities have\n   no email address.\n\nEnjoy this release!\n\n### Bug Fixes\n\n- Add missing sample app paths to oathkeeper config\n  ([#2058](https://github.com/ory/kratos/issues/2058))\n  ([a527db4](https://github.com/ory/kratos/commit/a527db4487c4efd2e96f8bf84d48a3cca30a14a1)):\n\n  Add \"welcome,registration,login,verification\" and \"\\*\\*.png\" to the paths\n  oathkeeper forwards to self service ui.\n\n- Add section on webauthn constraints\n  ([#2072](https://github.com/ory/kratos/issues/2072))\n  ([23663b5](https://github.com/ory/kratos/commit/23663b50afce59cec2cfcaa4d3f50ae0abcf6310))\n- After release hooks\n  ([56c2e61](https://github.com/ory/kratos/commit/56c2e61195b6e6808ed76b9fd5dee0da1f489ce9))\n- Dockerfile clean up\n  ([52420cc](https://github.com/ory/kratos/commit/52420ccc17a8d395f0b13c0ad03ac334434c4b0e)),\n  closes [#2070](https://github.com/ory/kratos/issues/2070)\n- Goreleaser after hook\n  ([c763f2b](https://github.com/ory/kratos/commit/c763f2b394543a142f35b022d9c9d154c8e8489c))\n- Goreleaser config\n  ([7099af2](https://github.com/ory/kratos/commit/7099af20929ad003968e7fc9e47a4fe745984fbb)):\n\n  See https://github.com/goreleaser/goreleaser/issues/2762\n\n- Release hook\n  ([90bd769](https://github.com/ory/kratos/commit/90bd7698380168b88ee301d9f343054052b208fd))\n\n### Code Generation\n\n- Pin v0.8.2-alpha.1 release commit\n  ([627f4a1](https://github.com/ory/kratos/commit/627f4a1ddb378db84510a85013c4580a9d8024ad))\n\n### Documentation\n\n- Fix bodged release\n  ([032b23a](https://github.com/ory/kratos/commit/032b23aba3fa04e5e2a638b78b806ca49a6a8e1c))\n- Quickstart update ([#2060](https://github.com/ory/kratos/issues/2060))\n  ([3387cf6](https://github.com/ory/kratos/commit/3387cf6f111db5944fbff536fd0a9a67bc388f9a)),\n  closes [#2032](https://github.com/ory/kratos/issues/2032)\n  [#1916](https://github.com/ory/kratos/issues/1916)\n\n# [0.8.1-alpha.1](https://github.com/ory/kratos/compare/v0.8.0-alpha.4.pre.0...v0.8.1-alpha.1) (2021-12-13)\n\nThis maintenance release important security updates for the base Docker Images\n(e.g. Alpine). Additionally, several hiccups with the new ARM support have been\nresolved and the binaries are now downloadable for all major platforms. Please\nnote that passwords now have to be at least 8 characters long, following\nrecommendations from Microsoft and others.\n\nEnjoy this release!\n\n### Bug Fixes\n\n- Bodget docs commit\n  ([f9d2f82](https://github.com/ory/kratos/commit/f9d2f8245bc94aaf21ddc9e5516b64e7887dae4b))\n- Build docs on release\n  ([2cf137a](https://github.com/ory/kratos/commit/2cf137a0540b81f4e405920cafd251db71d2f9fa))\n- De-duplicate message IDs ([#1973](https://github.com/ory/kratos/issues/1973))\n  ([9d8e197](https://github.com/ory/kratos/commit/9d8e19720fcc2e5b5371c2ddea4e2501304a93fd))\n- Docs links ([#2008](https://github.com/ory/kratos/issues/2008))\n  ([8515e17](https://github.com/ory/kratos/commit/8515e17938570770ca4cbf93028782925e28f431))\n- Require minimum length of 8 characters password\n  ([#2009](https://github.com/ory/kratos/issues/2009))\n  ([bb5846e](https://github.com/ory/kratos/commit/bb5846ecb446b9e58b2a4949c678fddac4bbac4f)):\n\n  Kratos follows\n  [NIST Digital Identity Guidelines - 5.1.1.2 Memorized Secret Verifiers](https://pages.nist.gov/800-63-3/sp800-63b.html)\n  and\n  [password policy](https://www.ory.sh/kratos/docs/concepts/security#password-policy)\n  says\n\n  > Passwords must have a minimum length of 8 characters and all characters\n  > (unicode, ASCII) must be allowed.\n\n- Resolve freebsd build issue\n  ([#2004](https://github.com/ory/kratos/issues/2004))\n  ([9c75fe9](https://github.com/ory/kratos/commit/9c75fe9e7ab4ff27f8d1f2399a58baaadefaaa0d)),\n  closes [#1645](https://github.com/ory/kratos/issues/1645)\n- Revert tag\n  ([f1d7b9e](https://github.com/ory/kratos/commit/f1d7b9e2db2cab4acdcaacbae06a85c42417b334)),\n  closes [#1945](https://github.com/ory/kratos/issues/1945)\n- Set dockerfile\n  ([c860b99](https://github.com/ory/kratos/commit/c860b992aee6a63d9696377ed9047e8cdeef0098))\n- Skip docs publishing for pre releases\n  ([eb6d8cd](https://github.com/ory/kratos/commit/eb6d8cdb2d3d400eb3b9398a15825ecdb10d3cf8))\n- Support complex lifespans ([#2050](https://github.com/ory/kratos/issues/2050))\n  ([0edbebe](https://github.com/ory/kratos/commit/0edbebed896e79fd2979a54756932ea27c2ddb99))\n- Update docs after release\n  ([850be90](https://github.com/ory/kratos/commit/850be9065b64bcf268b42e4018f60b25a7a73da5))\n- Verification error code ([#1967](https://github.com/ory/kratos/issues/1967))\n  ([44411ab](https://github.com/ory/kratos/commit/44411ab4ac5f184c7f42e6ece0ccb2ae7cbdc42c)),\n  closes [#1956](https://github.com/ory/kratos/issues/1956)\n\n### Code Generation\n\n- Pin v0.8.1-alpha.1 release commit\n  ([8247416](https://github.com/ory/kratos/commit/82474161f61a3a22afad478838ffe8fe837d41ac))\n\n### Documentation\n\n- Add `Content-Type` to recommended CORS allowed headers\n  ([#2015](https://github.com/ory/kratos/issues/2015))\n  ([dd890ab](https://github.com/ory/kratos/commit/dd890ab96727d7a2c8c2f52279dc3516096213f0))\n- **debug:** Fix typo ([#1976](https://github.com/ory/kratos/issues/1976))\n  ([0647554](https://github.com/ory/kratos/commit/0647554179d7b0119ed01d353cd0ea9eb8317752))\n- Fix incorrect tag\n  ([bbd2355](https://github.com/ory/kratos/commit/bbd2355bbb220389021b596eec339a25652d932a)),\n  closes [#2032](https://github.com/ory/kratos/issues/2032)\n  [#2028](https://github.com/ory/kratos/issues/2028)\n- Fixed date format example ([#2038](https://github.com/ory/kratos/issues/2038))\n  ([fc4703a](https://github.com/ory/kratos/commit/fc4703aa34066a56fa3cf3b664a0d032157e477a))\n- Improve text around bcrypt\n  ([#2037](https://github.com/ory/kratos/issues/2037))\n  ([ba6981e](https://github.com/ory/kratos/commit/ba6981e344e880936b5e995c433dae85659ba780))\n- Levenshtein-Distance has been released\n  ([#2040](https://github.com/ory/kratos/issues/2040))\n  ([393b6b3](https://github.com/ory/kratos/commit/393b6b38cdc4758e838eec20e81d486662f7b4a7))\n- Minor fixes ([#2010](https://github.com/ory/kratos/issues/2010))\n  ([12918db](https://github.com/ory/kratos/commit/12918dbf4b0edb2857e06736aee9cccf1a5f76ff))\n- Password-strength meter has been dropped\n  ([#2041](https://github.com/ory/kratos/issues/2041))\n  ([9848fb3](https://github.com/ory/kratos/commit/9848fb3b40c12799eafc73d2ec0f410bf5b22aa8))\n- This has been done ([#2045](https://github.com/ory/kratos/issues/2045))\n  ([7e8c91a](https://github.com/ory/kratos/commit/7e8c91ace5229fdc394461b3453acb3f01da0a6c))\n- Totp unlink image in 2fa docs\n  ([#1957](https://github.com/ory/kratos/issues/1957))\n  ([7afb731](https://github.com/ory/kratos/commit/7afb731c15ebbd6bab54a133f2e80e938dd937d4))\n- Update email template docs\n  ([#1960](https://github.com/ory/kratos/issues/1960))\n  ([#1968](https://github.com/ory/kratos/issues/1968))\n  ([b0f25a9](https://github.com/ory/kratos/commit/b0f25a9a6013f1e450163f5c08b221d328c210be))\n- Webhooks have landed ([#2035](https://github.com/ory/kratos/issues/2035))\n  ([80e53eb](https://github.com/ory/kratos/commit/80e53eb83d0dc84d2082ee343bfcecd2bfd99e13))\n\n### Features\n\n- Add alpine dockerfile\n  ([587eaee](https://github.com/ory/kratos/commit/587eaeee60cab2f539af8f309800f5a6e9cdfe6f))\n- Add x-total-count to paginated pages\n  ([b633ec3](https://github.com/ory/kratos/commit/b633ec3da6ccca196cd9d78c3c43d9797bd8d982))\n- Buildkit with multi stage build\n  ([#2025](https://github.com/ory/kratos/issues/2025))\n  ([57ab7f7](https://github.com/ory/kratos/commit/57ab7f784674c2cef2b1cef4b6922e9834213e3d))\n- **cmd:** Add OIDC credential include\n  ([#2017](https://github.com/ory/kratos/issues/2017))\n  ([1482844](https://github.com/ory/kratos/commit/148284485db8a86aa10c5aefb34373f9a8c7d95a)):\n\n  With this change, the `kratos identities get` CLI can additionally fetch OIDC\n  credentials.\n\n- Generalise courier ([#2019](https://github.com/ory/kratos/issues/2019))\n  ([1762a73](https://github.com/ory/kratos/commit/1762a730886707be3549bc6789f65c66d755e1d0))\n- **oidc:** Add spotify provider\n  ([#2024](https://github.com/ory/kratos/issues/2024))\n  ([0064e35](https://github.com/ory/kratos/commit/0064e350ccb417fefee6f48ca5895f3d75247bb3))\n\n### Tests\n\n- Add web hook test cases ([#2051](https://github.com/ory/kratos/issues/2051))\n  ([316e940](https://github.com/ory/kratos/commit/316e940a70684084c857e80a2ffaf334a64aee94))\n- **e2e:** Split e2e script into setup and test phase\n  ([#2027](https://github.com/ory/kratos/issues/2027))\n  ([1761418](https://github.com/ory/kratos/commit/176141860f3aa946519073d0e35bf3acacd6c685))\n- Fix changed message ID ([#2013](https://github.com/ory/kratos/issues/2013))\n  ([0bb66de](https://github.com/ory/kratos/commit/0bb66de582ebcb501c161655ae00e276a1d7d5d2))\n\n# [0.8.0-alpha.4.pre.0](https://github.com/ory/kratos/compare/v0.8.0-alpha.3...v0.8.0-alpha.4.pre.0) (2021-11-09)\n\nautogen: pin v0.8.0-alpha.4.pre.0 release commit\n\n## Breaking Changes\n\nTo celebrate this change, we cleaned up the ways you install Ory software, and\nwill roll this out to all other projects soon:\n\nThere is now one central brew / bash curl repository:\n\n```patch\n-brew install ory/kratos/kratos\n+brew install ory/tap/kratos\n\n-bash <(curl https://raw.githubusercontent.com/ory/kratos/master/install.sh)\n+bash <(curl https://raw.githubusercontent.com/ory/meta/master/install.sh) kratos\n```\n\n### Bug Fixes\n\n- Add base64 to ReadSchema ([#1918](https://github.com/ory/kratos/issues/1918))\n  ([8c8815b](https://github.com/ory/kratos/commit/8c8815b7ced0051eb0120198ae75b8fcf0fce2ba)),\n  closes [#1529](https://github.com/ory/kratos/issues/1529)\n- Add error.id to invalid cookie/token settings flow\n  ([#1919](https://github.com/ory/kratos/issues/1919))\n  ([73610d4](https://github.com/ory/kratos/commit/73610d4cfb16789385d2660e278419664b1ea3f3)),\n  closes [#1888](https://github.com/ory/kratos/issues/1888)\n- Adds missing webauthn authentication method\n  ([#1914](https://github.com/ory/kratos/issues/1914))\n  ([44892f3](https://github.com/ory/kratos/commit/44892f379c1aa9ffd7f5c92c9c1b32cc34a0dada))\n- Allow use of relative URLs in config\n  ([#1754](https://github.com/ory/kratos/issues/1754))\n  ([5f73bb0](https://github.com/ory/kratos/commit/5f73bb0784aeb7c4f3b1ed949926f9d9aed968d1)),\n  closes [#1446](https://github.com/ory/kratos/issues/1446)\n- Do not use csrf for meta endpoints\n  ([#1927](https://github.com/ory/kratos/issues/1927))\n  ([fd14798](https://github.com/ory/kratos/commit/fd147989a55357248a37a30548c5d4c104bcf0f7))\n- E2e test regression ([#1937](https://github.com/ory/kratos/issues/1937))\n  ([c9be009](https://github.com/ory/kratos/commit/c9be009112b03291ea76dd4de0911f495cf1e1ac))\n- Include text label for link email field\n  ([07a1dbb](https://github.com/ory/kratos/commit/07a1dbb95156ca50116219dc837ca61e3d597df1)),\n  closes [#1909](https://github.com/ory/kratos/issues/1909)\n- Panic on webhook with nil body\n  ([#1890](https://github.com/ory/kratos/issues/1890))\n  ([4bf1825](https://github.com/ory/kratos/commit/4bf18250373b7255e26e95d51a257e5280ad3148)),\n  closes [#1885](https://github.com/ory/kratos/issues/1885)\n- Paths\n  ([8c852c7](https://github.com/ory/kratos/commit/8c852c73136e130d163e2c9c5e0ca8a3449f4e26))\n- Speed up git clone\n  ([d3e4bde](https://github.com/ory/kratos/commit/d3e4bdefd252131b6a1b84917962ff07284e3f9f))\n- Update sdk orb\n  ([94e12e6](https://github.com/ory/kratos/commit/94e12e6d767ffa46d9060fdfb463adb83806990b))\n- Use bcrypt for password hashing in example\n  ([a9196f2](https://github.com/ory/kratos/commit/a9196f27791c30d32743e6b69a86595d76362f29))\n- Use new ory installation method\n  ([09cfc7e](https://github.com/ory/kratos/commit/09cfc7e2c23885270ef02193b4fdddc5550f3c23))\n\n### Code Generation\n\n- Pin v0.8.0-alpha.4.pre.0 release commit\n  ([3e443b7](https://github.com/ory/kratos/commit/3e443b77ef63d72e5bf0b806790c86841a140afc))\n\n### Documentation\n\n- Add subdomain configuration in csrf page\n  ([#1896](https://github.com/ory/kratos/issues/1896))\n  ([681750f](https://github.com/ory/kratos/commit/681750f92d7fe517e7cc184cb4b65e6a21903ee9)):\n\n  Add some instructions as to how kratos can be configured to work across\n  subdomains.\n\n- Remove unintended characters in subdomain section in csrf page\n  ([#1897](https://github.com/ory/kratos/issues/1897))\n  ([dfb9007](https://github.com/ory/kratos/commit/dfb900797fc98ca7900631ccf8018858c4e43e85))\n\n### Features\n\n- Add new goreleaser build chain\n  ([#1932](https://github.com/ory/kratos/issues/1932))\n  ([cf1714d](https://github.com/ory/kratos/commit/cf1714dafaa0cda98640c772106620586dae7763)):\n\n  This patch adds full compatibility with ARM architectures, including Apple\n  Silicon (M1). We additionally added cryptographically signed signatures\n  verifiable using [cosign](https://github.com/sigstore/cosign) for both\n  binaries as well as docker images.\n\n- Add quickstart mimicking hosted ui\n  ([813fb4c](https://github.com/ory/kratos/commit/813fb4cf48df1154ea334cca751cb55f7b3c77eb))\n- Advanced e-mail templating support\n  ([#1859](https://github.com/ory/kratos/issues/1859))\n  ([54b97b4](https://github.com/ory/kratos/commit/54b97b45506eff9cfafe338842ddf818b0c81f62)),\n  closes [#834](https://github.com/ory/kratos/issues/834)\n  [#925](https://github.com/ory/kratos/issues/925)\n- Allow wildcard domains for redirect_to checks\n  ([#1528](https://github.com/ory/kratos/issues/1528))\n  ([349cdcf](https://github.com/ory/kratos/commit/349cdcf4b1298d9e544344705ecd8e7b5eada48c)),\n  closes [#943](https://github.com/ory/kratos/issues/943):\n\n  Support wildcard domains in redirect_to checks.\n\n- Configurable health endpoints access logging\n  ([#1934](https://github.com/ory/kratos/issues/1934))\n  ([1301f68](https://github.com/ory/kratos/commit/1301f689bb0f1f44b66a057c8915f77ac71f30cc)):\n\n  This PR introduces a new boolean configuration parameter that allows turning\n  off logging of health endpoints requests in the access log. The implementation\n  is basically a rip-off from Ory Hydra and the configuration parameter is the\n  same:\n\n  ```\n  serve.public.request_log.disable_for_health\n  serve.admin.request_log.disable_for_health\n  ```\n\n  The default value is _false_.\n\n- Integrate sbom generation to goreleaser\n  ([#1850](https://github.com/ory/kratos/issues/1850))\n  ([305bb28](https://github.com/ory/kratos/commit/305bb28d689dabc4d211baac5e6babd34862af5f))\n- Make admin recovery to work without emails\n  [#1419](https://github.com/ory/kratos/issues/1419)\n  ([#1750](https://github.com/ory/kratos/issues/1750))\n  ([db00e85](https://github.com/ory/kratos/commit/db00e85e65c31b2bc497f0f4b4a28684b9f8bb9a))\n\n### Tests\n\n- **e2e:** Improved SDK set up and arm fix\n  ([#1933](https://github.com/ory/kratos/issues/1933))\n  ([c914ba1](https://github.com/ory/kratos/commit/c914ba10a85e89c031e7acfb73bf22c53201e287))\n- Update snapshots\n  ([a820653](https://github.com/ory/kratos/commit/a820653718475656b7ae44a1bc7235a8fb97b8b5))\n\n# [0.8.0-alpha.3](https://github.com/ory/kratos/compare/v0.8.0-alpha.2...v0.8.0-alpha.3) (2021-10-28)\n\nResolves issues in the quickstart.\n\n### Bug Fixes\n\n- Resolve quickstart issues ([#1900](https://github.com/ory/kratos/issues/1900))\n  ([d047009](https://github.com/ory/kratos/commit/d0470095f3263e287f76e8be0abb8df332492dd9)):\n\n  Closes https://github.com/ory/kratos/discussions/1899\n\n### Code Generation\n\n- Pin v0.8.0-alpha.3 release commit\n  ([a307deb](https://github.com/ory/kratos/commit/a307deb6779dacd2ce54e161a00d347600d2c583))\n\n# [0.8.0-alpha.2](https://github.com/ory/kratos/compare/v0.8.0-alpha.1...v0.8.0-alpha.2) (2021-10-28)\n\nResolves an issue in the SDK release pipeline.\n\n### Code Generation\n\n- Pin v0.8.0-alpha.2 release commit\n  ([2178929](https://github.com/ory/kratos/commit/217892978c4fa9897a88b140276c2d27622c5de4))\n\n# [0.8.0-alpha.1](https://github.com/ory/kratos/compare/v0.7.6-alpha.1...v0.8.0-alpha.1) (2021-10-27)\n\nWe are extremely excited to share this next generation of Ory Kratos! The\nproject is truly maturing and the community is getting larger by the hour.\n\nOn this special occasion, we would like to bring to your attention that the\n[**Ory Summit is happening tomorrow and on Friday!**](https://events.hubilo.com/ory-summit/register?mtm_campaign=ory-summit-2021&mtm_kwd=banner-landingpage)\nYou will hear gripping talks from the Ory Community and Ory maintainers! And the\nbest part, tickets are free and we are covering multiple time zones!\n\nThis release is truly the best version of Ory Kratos to date and we want to give\nyou a tl;dr of the 345 commits and 1152 files changed, and what you can expect\nfrom this release:\n\n- Full multi-factor authentication with different enforcement policies\n  (soft/hard MFA).\n- Support for WebAuthn (FIDO2 / U2F) two-factor authentication - from\n  fingerprints to hardware tokens every FIDO2 device is supported!\n- Ability to fetch the initial OAuth2 Access and Refresh and OpenID Connect ID\n  Tokens an identity receives when performing social sign up. Optionally, these\n  tokens are stored encrypted in the database (XChaCha20Poly1305 or AES-GCM)!\n- Support for TOTP (Google Authenticator) two-factor\n  verification/authentication.\n- Advanced two-factor recovery with lookup secrets.\n- [A complete reference implementation of the Ory Kratos end-user (self-service) facing UI in ReactJS & VercelJS](https://github.com/ory/kratos-react-nextjs-ui).\n- \"Native\" support for Single-Page App Single Sign-On.\n- Much improved single-page app and native app APIs for all self-service flows.\n- Support for PKBDF2 password hashing, which will help import user passwords\n  from other systems in the future.\n- Bugfixes and improvements to the OpenAPI spec and auto-generated SDKs.\n- ARM Docker Images.\n- Greatly improved internal e2e test pipeline using Cypress 8.x.\n- Improved functional tests with cupaloy snapshot testing.\n- Documentation on different error codes and message identifiers to easier\n  translate messages in your own UI.\n- Better form decoding and ability to mark required JSON Schema fields as\n  required in the UI.\n- Bug fixes that could result in users ending up in irrecoverable UI states.\n- Better support for `return_to` across flows (e.g. OIDC) and in custom UIs.\n- SBOM Software Supply Chain scanning & reporting.\n- Docker Image vulnerability checking as part of the release pipeline.\n- Support sending emails via AWS SES SMTP.\n- A REST endpoint to invalidate all an identity's sessions.\n\nAs you can see, much has happened and we are grateful for all the great\ninteractions we have with you, every day!\n\nLet's take a look at some of the breaking changes. Even though much was added,\nlittle has changed in breaking ways! This is a testament that Ory Kratos'\ninternals and APIs are becoming more stable!\n\nThis release requires you to run SQL migrations. Please, as always, create a\nbackup of your database first!\n\nThe SDKs are now generated with tag v0alpha2 to reflect that some signatures\nhave changed in a breaking fashion. Please update your imports from `v0alpha1`\nto `v0alpha2`.\n\nThe SMTPS scheme used in courier config URL with cleartext/StartTLS/TLS SMTP\nconnection types is now only supporting implicit TLS. For StartTLS and cleartext\nSMTP, please use the SMTP scheme instead.\n\nExample:\n\n- SMTP Cleartext: `smtp://foo:bar@my-mailserver:1234/?disable_starttls=true`\n- SMTP with StartTLS: `smtps://foo:bar@my-mailserver:1234/` ->\n  `smtp://foo:bar@my-mailserver:1234/`\n- SMTP with implicit TLS: `smtps://foo:bar@my-mailserver:1234/?legacy_ssl=true`\n  -> `smtps://foo:bar@my-mailserver:1234/We are extremely excited to share this\n  next generation of Ory Kratos! The project is truly maturing and the community\n  is getting larger by the hour.\n\nOn this special occasion, we would like to bring to your attention that the\n[**Ory Summit is happening tomorrow and on Friday!**](https://events.hubilo.com/ory-summit/register?mtm_campaign=ory-summit-2021&mtm_kwd=banner-landingpage)\nYou will hear gripping talks from the Ory Community and Ory maintainers! And the\nbest part, tickets are free and we are covering multiple time zones!\n\nThis release is truly the best version of Ory Kratos to date and we want to give\nyou a tl;dr of the 345 commits and 1152 files changed, and what you can expect\nfrom this release:\n\n- Full multi-factor authentication with different enforcement policies\n  (soft/hard MFA).\n- Support for WebAuthn (FIDO2 / U2F) two-factor authentication - from\n  fingerprints to hardware tokens every FIDO2 device is supported!\n- Ability to fetch the initial OAuth2 Access and Refresh and OpenID Connect ID\n  Tokens an identity receives when performing social sign up. Optionally, these\n  tokens are stored encrypted in the database (XChaCha20Poly1305 or AES-GCM)!\n- Support for TOTP (Google Authenticator) two-factor\n  verification/authentication.\n- Advanced two-factor recovery with lookup secrets.\n- [A complete reference implementation of the Ory Kratos end-user (self-service) facing UI in ReactJS & VercelJS](https://github.com/ory/kratos-react-nextjs-ui).\n- \"Native\" support for Single-Page App Single Sign-On.\n- Much improved single-page app and native app APIs for all self-service flows.\n- Support for PKBDF2 password hashing, which will help import user passwords\n  from other systems in the future.\n- Bugfixes and improvements to the OpenAPI spec and auto-generated SDKs.\n- ARM Docker Images.\n- Greatly improved internal e2e test pipeline using Cypress 8.x.\n- Improved functional tests with cupaloy snapshot testing.\n- Documentation on different error codes and message identifiers to easier\n  translate messages in your own UI.\n- Better form decoding and ability to mark required JSON Schema fields as\n  required in the UI.\n- Bug fixes that could result in users ending up in irrecoverable UI states.\n- Better support for `return_to` across flows (e.g. OIDC) and in custom UIs.\n- SBOM Software Supply Chain scanning & reporting.\n- Docker Image vulnerability checking as part of the release pipeline.\n- Support sending emails via AWS SES SMTP.\n- A REST endpoint to invalidate all an identity's sessions.\n\nAs you can see, much has happened and we are grateful for all the great\ninteractions we have with you, every day!\n\nLet's take a look at some of the breaking changes. Even though much was added,\nlittle has changed in breaking ways! This is a testament that Ory Kratos'\ninternals and APIs are becoming more stable!\n\nThis release requires you to run SQL migrations. Please, as always, create a\nbackup of your database first!\n\nThe SDKs are now generated with tag v0alpha2 to reflect that some signatures\nhave changed in a breaking fashion. Please update your imports from `v0alpha1`\nto `v0alpha2`.\n\nThe SMTPS scheme used in courier config URL with cleartext/StartTLS/TLS SMTP\nconnection types is now only supporting implicit TLS. For StartTLS and cleartext\nSMTP, please use the SMTP scheme instead.\n\nExample:\n\n- SMTP Cleartext: `smtp://foo:bar@my-mailserver:1234/?disable_starttls=true`\n- SMTP with StartTLS: `smtps://foo:bar@my-mailserver:1234/` ->\n  `smtp://foo:bar@my-mailserver:1234/`\n- SMTP with implicit TLS: `smtps://foo:bar@my-mailserver:1234/?legacy_ssl=true`\n  -> `smtps://foo:bar@my-mailserver:1234/We are extremely excited to share this\n  next generation of Ory Kratos! The project is truly maturing and the community\n  is getting larger by the hour.\n\nOn this special occasion, we would like to bring to your attention that the\n[**Ory Summit is happening tomorrow and on Friday!**](https://events.hubilo.com/ory-summit/register?mtm_campaign=ory-summit-2021&mtm_kwd=banner-landingpage)\nYou will hear gripping talks from the Ory Community and Ory maintainers! And the\nbest part, tickets are free and we are covering multiple time zones!\n\nThis release is truly the best version of Ory Kratos to date and we want to give\nyou a tl;dr of the 345 commits and 1152 files changed, and what you can expect\nfrom this release:\n\n- Full multi-factor authentication with different enforcement policies\n  (soft/hard MFA).\n- Support for WebAuthn (FIDO2 / U2F) two-factor authentication - from\n  fingerprints to hardware tokens every FIDO2 device is supported!\n- Ability to fetch the initial OAuth2 Access and Refresh and OpenID Connect ID\n  Tokens an identity receives when performing social sign up. Optionally, these\n  tokens are stored encrypted in the database (XChaCha20Poly1305 or AES-GCM)!\n- Support for TOTP (Google Authenticator) two-factor\n  verification/authentication.\n- Advanced two-factor recovery with lookup secrets.\n- [A complete reference implementation of the Ory Kratos end-user (self-service) facing UI in ReactJS & VercelJS](https://github.com/ory/kratos-react-nextjs-ui).\n- \"Native\" support for Single-Page App Single Sign-On.\n- Much improved single-page app and native app APIs for all self-service flows.\n- Support for PKBDF2 password hashing, which will help import user passwords\n  from other systems in the future.\n- Bugfixes and improvements to the OpenAPI spec and auto-generated SDKs.\n- ARM Docker Images.\n- Greatly improved internal e2e test pipeline using Cypress 8.x.\n- Improved functional tests with cupaloy snapshot testing.\n- Documentation on different error codes and message identifiers to easier\n  translate messages in your own UI.\n- Better form decoding and ability to mark required JSON Schema fields as\n  required in the UI.\n- Bug fixes that could result in users ending up in irrecoverable UI states.\n- Better support for `return_to` across flows (e.g. OIDC) and in custom UIs.\n- SBOM Software Supply Chain scanning & reporting.\n- Docker Image vulnerability checking as part of the release pipeline.\n- Support sending emails via AWS SES SMTP.\n- A REST endpoint to invalidate all an identity's sessions.\n\nAs you can see, much has happened and we are grateful for all the great\ninteractions we have with you, every day!\n\nLet's take a look at some of the breaking changes. Even though much was added,\nlittle has changed in breaking ways! This is a testament that Ory Kratos'\ninternals and APIs are becoming more stable!\n\nThis release requires you to run SQL migrations. Please, as always, create a\nbackup of your database first!\n\nThe SDKs are now generated with tag v0alpha2 to reflect that some signatures\nhave changed in a breaking fashion. Please update your imports from `v0alpha1`\nto `v0alpha2`.\n\nThe SMTPS scheme used in courier config URL with cleartext/StartTLS/TLS SMTP\nconnection types is now only supporting implicit TLS. For StartTLS and cleartext\nSMTP, please use the SMTP scheme instead.\n\nExample:\n\n- SMTP Cleartext: `smtp://foo:bar@my-mailserver:1234/?disable_starttls=true`\n- SMTP with StartTLS: `smtps://foo:bar@my-mailserver:1234/` ->\n  `smtp://foo:bar@my-mailserver:1234/`\n- SMTP with implicit TLS: `smtps://foo:bar@my-mailserver:1234/?legacy_ssl=true`\n  -> `smtps://foo:bar@my-mailserver:1234/`\n\n## Breaking Changes\n\nThe location of the homebrew tap has changed from `ory/ory/kratos` to\n`ory/tap/kratos`.\n\nTo stay consistent with other query parameter's, the self-service login flow's\n`forced` key has been renamed to `refresh`.\n\nThe SDKs are now generated with tag v0alpha2 to reflect that some signatures\nhave changed in a breaking fashion. Please update your imports from `v0alpha1`\nto `v0alpha2`.\n\nTo support 2FA on non-browser (e.g. native mobile) apps we have added the Ory\nSession Token as a possible parameter to both\n`initializeSelfServiceLoginFlowWithoutBrowser` and `submitSelfServiceLoginFlow`.\nDepending on the SDK generator, the order of the arguments may have changed. In\nJavaScript:\n\n```patch\n- .submitSelfServiceLoginFlow(flow.id, payload)\n+ .submitSelfServiceLoginFlow(flow.id, sessionToken, payload)\n+ // or if the user has no session yet:\n+ .submitSelfServiceLoginFlow(flow.id, undefined, payload)\n```\n\nTo improve the overall API design we have changed the result of\n`POST /self-service/settings`. Instead of having flow be a key, the flow is now\nthe response. The updated identity payload stays the same!\n\n```patch\n {\n-  \"flow\": {\n-    \"id\": \"flow-id-...\"\n-    ...\n-  },\n+  \"id\": \"flow-id-...\"\n+  ...\n   \"identity\": {\n     \"id\": \"identity-id-...\"\n   }\n }\n```\n\nThe SMTPS scheme used in courier config url with cleartext/StartTLS/TLS SMTP\nconnection types is now only supporting implicit TLS. For StartTLS and cleartext\nSMTP, please use the smtp scheme instead.\n\nExample:\n\n- SMTP Cleartext: `smtp://foo:bar@my-mailserver:1234/?disable_starttls=true`\n- SMTP with StartTLS: `smtps://foo:bar@my-mailserver:1234/` ->\n  `smtp://foo:bar@my-mailserver:1234/`\n- SMTP with implicit TLS: `smtps://foo:bar@my-mailserver:1234/?legacy_ssl=true`\n  -> `smtps://foo:bar@my-mailserver:1234/`\n\nThis patch changes the naming and number of prometheus metrics (see:\nhttps://github.com/ory/x/pull/379). In short: all metrics will have now `http_`\nprefix to conform to Prometheus best practices.\n\n### Bug Fixes\n\n- Add error id\n  ([1442784](https://github.com/ory/kratos/commit/1442784264d1f5032830a0646b853b925bb19c62))\n- Add mfa e2e test scenarios and resolve found issues\n  ([436992d](https://github.com/ory/kratos/commit/436992ddf2ace68b247c708fc955fccb95cf6fd2))\n- Add middleware earlier [#1775](https://github.com/ory/kratos/issues/1775)\n  ([#1776](https://github.com/ory/kratos/issues/1776))\n  ([b9d253e](https://github.com/ory/kratos/commit/b9d253ef05ff7cd616111a817d03a17e39f8f4a8))\n- Allow refresh and aal upgrade at the same time\n  ([2ec801f](https://github.com/ory/kratos/commit/2ec801f262cd8f6dcdf8121a20897257e3b74ad3))\n- API client leaks stack trace with an error\n  ([#1772](https://github.com/ory/kratos/issues/1772))\n  ([d3aff6d](https://github.com/ory/kratos/commit/d3aff6d3eb11942fbfd6f2de71f4399053075b62)),\n  closes [#1771](https://github.com/ory/kratos/issues/1771)\n- Better const handling for internal context\n  ([1e457e3](https://github.com/ory/kratos/commit/1e457e3b3dea9ea9a05c12740578af2d45902aba))\n- Correct swagger path for /identities/:id/session endpoint\n  ([#1756](https://github.com/ory/kratos/issues/1756))\n  ([d614f2a](https://github.com/ory/kratos/commit/d614f2a737eef90ad60a4bdedae248b74131ff35))\n- Decoder regression in registration\n  ([febf75a](https://github.com/ory/kratos/commit/febf75ae959a2b67c19fcd1705b591f22ff5314b))\n- Deterministic clidoc dates\n  ([e48d90a](https://github.com/ory/kratos/commit/e48d90ad5a178ab3317d89800526c516aad6e274))\n- Disable totp per default\n  ([7278589](https://github.com/ory/kratos/commit/7278589ff2460a13302650b5e3fae01d774f9684))\n- Docs autogen should not use `time.Now`\n  ([a830f5b](https://github.com/ory/kratos/commit/a830f5b3b535bc375e879c797626b6084b76776e))\n- Ensure correct error propagation\n  ([77ce709](https://github.com/ory/kratos/commit/77ce709d53d88f70c892ab0892c13e16f5b761a5))\n- Ensure refresh issues a new session when the identity changes\n  ([a10b385](https://github.com/ory/kratos/commit/a10b385510a0102ede5850f9be30b7deba810acf))\n- Ensure return_to works for OIDC flows\n  ([d615734](https://github.com/ory/kratos/commit/d615734c312db6f7fa48fb8c7b4090a80c9e5ce7)),\n  closes [#1773](https://github.com/ory/kratos/issues/1773)\n- Explicit validation for return to in new flows\n  ([284cf29](https://github.com/ory/kratos/commit/284cf29a6be82530b55c24a15c465ec9f1b6a210))\n- Follow chrome webauthn best practice recommendation\n  ([0a7c812](https://github.com/ory/kratos/commit/0a7c8128bb0b78f8dc236af06ca9be038b201829))\n- Githup-app name in config ([#1822](https://github.com/ory/kratos/issues/1822))\n  ([1b50963](https://github.com/ory/kratos/commit/1b50963525ceaceea9afb8d1236d728de3107a8e))\n- Handle return errors on the frontend and break early\n  ([0e8d481](https://github.com/ory/kratos/commit/0e8d481cc220777aa56faf2e716da15537fa27fc)):\n\n  Closes https://github.com/ory-corp/cloud/issues/1426\n\n- Identity credential identifiers are now unique per method\n  ([57fd99a](https://github.com/ory/kratos/commit/57fd99ac05d29fc0362f14e5910641944232d61e))\n- Improve schema validation error tracing\n  ([f793fe5](https://github.com/ory/kratos/commit/f793fe56182f3f195a57fe5f4b54f7fcf8402c81))\n- Incorrect JSON response for browser flows\n  ([1501f56](https://github.com/ory/kratos/commit/1501f5627ed12d2d149f1fcf49fcf326120e6b0b))\n- Kill modd as well\n  ([e5a98e5](https://github.com/ory/kratos/commit/e5a98e54ec68f122615dd902df9ebac788fdb579))\n- **link:** Resolve incorrect response types when opening API recovery link in\n  browser\n  ([35ea8db](https://github.com/ory/kratos/commit/35ea8db300c2d3eeaf7d8f0e29c604ecc455cd2b))\n- **login:** Properly handle refresh\n  ([8dc7059](https://github.com/ory/kratos/commit/8dc7059222fa12dd0bca0183f42306b5169addb6))\n- **lookup:** Ensure correct fields are set\n  ([5ed4c55](https://github.com/ory/kratos/commit/5ed4c5572f9cbb35461e45dfc6b7c5eb4bce7434))\n- **lookup:** Resolve reuse scenarios\n  ([dbfe475](https://github.com/ory/kratos/commit/dbfe475ba5f0d2b9d4b0b67d0d8e7cb99e89ad5d))\n- **lookup:** Set up codes correctly\n  ([2f373f3](https://github.com/ory/kratos/commit/2f373f344326fbd5dbebf6233dbf5b56252b7e95))\n- OIDC provider field in spec\n  ([#1809](https://github.com/ory/kratos/issues/1809))\n  ([11b25de](https://github.com/ory/kratos/commit/11b25deb46b73c7d0ab95a77ff2ab60c032c1942))\n- **oidc:** Ensure nested keys work on login\n  ([71583c5](https://github.com/ory/kratos/commit/71583c57f1334bee1e5c9be1fae6a1b241ea3d6d))\n- Omitempty for VerifiedAt and StateChangedAt\n  ([#1736](https://github.com/ory/kratos/issues/1736))\n  ([bf2ec6e](https://github.com/ory/kratos/commit/bf2ec6e6ae8d656ea6dcac037dedd3603ad12915)):\n\n  Closes https://github.com/ory/sdk/issues/95\n\n- Only respect required modules for SDK\n  ([4c5677f](https://github.com/ory/kratos/commit/4c5677f3ea48bd87e5d7a1f95e3807b7884a0b64))\n- Panic when recovering deactivated user\n  ([0a49f27](https://github.com/ory/kratos/commit/0a49f2714991a3f397dc5c721fe22d11846d3db5)),\n  closes [#1794](https://github.com/ory/kratos/issues/1794)\n  [#1826](https://github.com/ory/kratos/issues/1826)\n- Potentially resolve hanging postgres connection closing\n  ([693a928](https://github.com/ory/kratos/commit/693a9286b02c2329dcfd358a038857901193b459))\n- Properly encode aal error\n  ([49b6288](https://github.com/ory/kratos/commit/49b6288c2345840a7517272e9616c2c20a254edb))\n- Properly open recovery endpoints in browser if flow was initiated via API\n  ([23c12e5](https://github.com/ory/kratos/commit/23c12e55d24591ca69c9178017355a9262fa35eb))\n- Remove duplicate schema error\n  ([4e69123](https://github.com/ory/kratos/commit/4e691238da3bf3ee8d9a92d4d9507b27fce20199))\n- Remove initial_value again as it was not useful outside of booleans\n  ([0cc984b](https://github.com/ory/kratos/commit/0cc984b85baff3db500fb656bd541cfa0396df98))\n- Remove obsolete openapi patch\n  ([11618ec](https://github.com/ory/kratos/commit/11618ecc6681a9108ee70a3e0d1ab3d21e33f9db))\n- Remove unnecessary cmd reference\n  ([351760e](https://github.com/ory/kratos/commit/351760ece01d421687179b8e3f6f48a720247a1d))\n- Replace 302 with 303\n  ([2e2b0f8](https://github.com/ory/kratos/commit/2e2b0f840450c6d23f3e51e5885d0908685ef3f6))\n- Resolve clidoc generation issue\n  ([1aaaa03](https://github.com/ory/kratos/commit/1aaaa035f863852799575e1f65e9d9ed276a3160))\n- Resolve merge issues\n  ([1dc7497](https://github.com/ory/kratos/commit/1dc74976c785afca8079379cd5060116b5f3d831))\n- Resolve openapi issues and regenerate clients\n  ([f7d60c0](https://github.com/ory/kratos/commit/f7d60c02392d2ad664c73ee4ff6bb108a4cb04e2))\n- Resolve swagger regression\n  ([02b9d47](https://github.com/ory/kratos/commit/02b9d470df012ae9818a8516a5549aee83c0963d))\n- Run format on ts files\n  ([f55f6f6](https://github.com/ory/kratos/commit/f55f6f69bf0df88d001fda791b330bdcbf5d92b2))\n- Slow CLI start-up time\n  ([ae20c17](https://github.com/ory/kratos/commit/ae20c17777eb57363f811b57d782db88b2de91ae)):\n\n  Found a deeply nested dependency which was importing\n  `https://github.com/markbates/pkger`, causing unreasonable CPU consumption and\n  significant delay at start up time. With this patch, start up time was reduced\n  from almost 3s to ~0.01s.\n\n  ```\n  $ time kratos\n  kratos  2.55s user 2.46s system 508% cpu 0.986 total\n\n  $ time ./kratos-patch\n  ./kratos-patch  0.00s user 0.00s system 64% cpu 0.001 total\n  ```\n\n- **test:** OIDC storategy test\n  ([#1836](https://github.com/ory/kratos/issues/1836))\n  ([b877dbe](https://github.com/ory/kratos/commit/b877dbecaf84e2d102bcceff4ad85c5b4efe18c5))\n- **totp:** Reorder QR\n  ([d096df7](https://github.com/ory/kratos/commit/d096df734ba8cf7dcfb872af03a19550d320c8b7))\n- Try and reduce cookie flakyness\n  ([e7ae8d6](https://github.com/ory/kratos/commit/e7ae8d63a16df69fd43afdf41691b9c1d3efe439))\n- Typo\n  ([8c4d8a2](https://github.com/ory/kratos/commit/8c4d8a2284f7a52a2dca7e7fd5e686756d410647))\n- **ui:** Use correct type for anchor\n  ([a6595e4](https://github.com/ory/kratos/commit/a6595e49c38a302f4a603dd46f5a0764680a24b1))\n- Update schema config location\n  ([539ae73](https://github.com/ory/kratos/commit/539ae7303158f14ca42165c12f9d3e8ef9dcdbdf))\n- Use parallelism of 1 in go test\n  ([8736334](https://github.com/ory/kratos/commit/8736334bf11fc9a742e2972aa97ee56c407c7c0c))\n- **webauthn:** Support react-based webauth\n  ([b6123b4](https://github.com/ory/kratos/commit/b6123b4840547b295be44272e76454462a0f60c4))\n- X-session-token must not be mandatory\n  ([05d73be](https://github.com/ory/kratos/commit/05d73beed26f1be31c6f2a62499c7c71d7d54bec))\n\n### Code Generation\n\n- Pin v0.8.0-alpha.1 release commit\n  ([c2c902c](https://github.com/ory/kratos/commit/c2c902c1bd8d910843d747c25b99ee1bcc6f962d))\n\n### Code Refactoring\n\n- **courier:** Support SMTP schemes for implicit TLS, explicit StartTLS, and\n  cleartext SMTP ([#1831](https://github.com/ory/kratos/issues/1831))\n  ([4cb082c](https://github.com/ory/kratos/commit/4cb082ce1e15ddd1d992a2def9e7d6410142cc02)),\n  closes [#1770](https://github.com/ory/kratos/issues/1770)\n  [#1769](https://github.com/ory/kratos/issues/1769)\n- Homogenize error messages\n  ([421a319](https://github.com/ory/kratos/commit/421a3190d1d4f6f5d96ef8ad87c3a2a667b57a28))\n- Improved prometheus metrics\n  ([#1830](https://github.com/ory/kratos/issues/1830))\n  ([0be993b](https://github.com/ory/kratos/commit/0be993bebeb9e50d90806ad13f60bb8d72c3b2d3)),\n  closes [#1735](https://github.com/ory/kratos/issues/1735):\n\n  This will add new prometheus metrics for Kratos that are more useful for\n  alerting and increase overall observability.\n\n- Login flow `forced` renamed to `refresh`\n  ([92087e5](https://github.com/ory/kratos/commit/92087e5f00b4fcce1706442c9edf1b466f9a23c9))\n- **login:** Rename forced -> refresh\n  ([8d1e54b](https://github.com/ory/kratos/commit/8d1e54bd79cf617985602997f1121e168f58c389))\n- **login:** Support 2FA for non-browser SDKs\n  ([df4846d](https://github.com/ory/kratos/commit/df4846d3867599f49e58b6b4d59b338916f37cbf))\n- Move expired error into top-level flow module\n  ([01a2602](https://github.com/ory/kratos/commit/01a26025375f1d958a7e345c61fb6ba5e3403efe))\n- Move homebrew tap to ory/tap\n  ([0ee67c3](https://github.com/ory/kratos/commit/0ee67c388a1fea8aa9633cbf684e1f62e16d61cc))\n- Move node identifiers to node package\n  ([b0a86dc](https://github.com/ory/kratos/commit/b0a86dc6e5005017a9a0fa2120560f668ab2432f))\n- Revert decision to return 422 errors and streamline 401/403\n  ([8aa5318](https://github.com/ory/kratos/commit/8aa53187f1e78d693463a47fcd9aedab30d1b55f))\n- Sdk API is no v0alpha2\n  ([3f06738](https://github.com/ory/kratos/commit/3f067386e32ad3baeec48fd21dd51659a5725970))\n- **session:** CreateAndIssueCookie is now UpsertAndIssueCookie\n  ([a6d134d](https://github.com/ory/kratos/commit/a6d134de7710c7e92e51f735f13b7757eb7011e5))\n- **session:** CreateSession is now UpsertSession\n  ([3ec81a2](https://github.com/ory/kratos/commit/3ec81a2cc401ff18052abd2a9ba060e665f0baa2))\n- **settings:** Change settings success response\n  ([12f98f2](https://github.com/ory/kratos/commit/12f98f2884294669bbb7eab7e8ed73a5372386f6))\n\n### Documentation\n\n- Add 2fa credentials\n  ([f7899a7](https://github.com/ory/kratos/commit/f7899a761aaf59d2cfddc2c330a805456cfca947))\n- Add 2fa guide\n  ([b4eed76](https://github.com/ory/kratos/commit/b4eed76305ecf1de3461525fd2ea748ec94da53c))\n- Add a commandline example for the logout\n  ([#1753](https://github.com/ory/kratos/issues/1753))\n  ([81ba264](https://github.com/ory/kratos/commit/81ba2647a66fca99b7ed2e56a67deec75ac06b89))\n- Add admin ui guide\n  ([ac88060](https://github.com/ory/kratos/commit/ac88060ed7390f0a34db637880c1660b8c45b352))\n- Add advanced custom UI documentation\n  ([5e3a2cd](https://github.com/ory/kratos/commit/5e3a2cdbedf0005c89db717d1136c56ab3304ede))\n- Add image assets\n  ([6bc93ca](https://github.com/ory/kratos/commit/6bc93ca79283bd993b0176dda11ad9d5860a5e4f))\n- Add missing angle bracket ([#1799](https://github.com/ory/kratos/issues/1799))\n  ([4270140](https://github.com/ory/kratos/commit/427014052ef905c2003e2cd0133d57bf83819776))\n- Add ory sessions as a concept\n  ([626c0c9](https://github.com/ory/kratos/commit/626c0c90bd2d683048618452ba421e40be92f587))\n- Add powershell to deps ([#1853](https://github.com/ory/kratos/issues/1853))\n  ([e945336](https://github.com/ory/kratos/commit/e94533690b658c4afba81e694a052579f0ffff42)),\n  closes [#1848](https://github.com/ory/kratos/issues/1848)\n- **credentials:** Add AAL explanation\n  ([c1f501e](https://github.com/ory/kratos/commit/c1f501e9ec3ba203fb252fbd56cb87843d667b17))\n- Enhance error return values\n  ([3799c24](https://github.com/ory/kratos/commit/3799c24fbc0397876df4f1c530e325bd1212d750))\n- Fix invalid syntax ([#1819](https://github.com/ory/kratos/issues/1819))\n  ([8cd6428](https://github.com/ory/kratos/commit/8cd6428e40610fa40b9c59414beb3d5c614dddaa))\n- Fix the flow links used for rendering\n  ([#1752](https://github.com/ory/kratos/issues/1752))\n  ([131d2c2](https://github.com/ory/kratos/commit/131d2c284d4191ee979077937ea3b48fce772f3c))\n- Fix the invalid links ([#1868](https://github.com/ory/kratos/issues/1868))\n  ([6d621ec](https://github.com/ory/kratos/commit/6d621ec89d1a7c37daf4622b06a0ad94f2d77b31))\n- Remove obsolete file\n  ([b7f9052](https://github.com/ory/kratos/commit/b7f905278edf4aed1e2984aa3d2d94a41368d6d8))\n- Update generated docs\n  ([72afb81](https://github.com/ory/kratos/commit/72afb81be8bfaa36236087ec7715bca1804aa62c))\n- Update quickstart curl examples\n  ([#1778](https://github.com/ory/kratos/issues/1778))\n  ([6c677c4](https://github.com/ory/kratos/commit/6c677c49df8fa8d48e7c0bbf91bbd18874f4c514))\n- Use correct link\n  ([f007919](https://github.com/ory/kratos/commit/f007919b7bd86c1d1b20b3625709e01b5f123302)),\n  closes [#1793](https://github.com/ory/kratos/issues/1793)\n\n### Features\n\n- Add `intended_for_someone_else` error code\n  ([572a131](https://github.com/ory/kratos/commit/572a1315aec7d1103c8d4fb9c128644ea2af6d3b))\n- Add aal fallback for existing sessions\n  ([a5c7b11](https://github.com/ory/kratos/commit/a5c7b1143bca7029bf94fc42fe638534961a06bc))\n- Add authenticators after set up\n  ([035c276](https://github.com/ory/kratos/commit/035c276152a22a2c9c7159b1cf89dbe7724728dd))\n- Add DeleteCredentialsType to identity struct including tests\n  ([b12bf52](https://github.com/ory/kratos/commit/b12bf523e4213e49f545206c457a1739f493d385))\n- Add e2e tests for react native 2fa\n  ([a3ac253](https://github.com/ory/kratos/commit/a3ac253bdb9c42df6dce9288a2e7c2dada24d255))\n- Add error ids for csrf-related errors\n  ([dc2adbf](https://github.com/ory/kratos/commit/dc2adbf52f7ee845778ee5c3c943b9e10c41e181))\n- Add error ids for redirect-related errors\n  ([246a045](https://github.com/ory/kratos/commit/246a0453e65e70635331c95ff02ec9133ae81e46))\n- Add error ids for session-related errors\n  ([087d907](https://github.com/ory/kratos/commit/087d90731185b71cd88cc6451ce360d6c1dada34))\n- Add explicit return_to to flow objects and API parameters\n  ([50d04ea](https://github.com/ory/kratos/commit/50d04eaa455932a9a5cc31f812f66518e1d4ad3b)),\n  closes [#1605](https://github.com/ory/kratos/issues/1605)\n  [#1121](https://github.com/ory/kratos/issues/1121):\n\n  This patch adds a `return_to` field to the flow objects which contains the\n  original `?return_to=...` value. It uses the Flow's `request_url` for that\n  purpose.\n\n- Add ids for user-facing errors for login, registration, settings\n  ([787558b](https://github.com/ory/kratos/commit/787558b48fd7405ac61a48d3c18c7252ac1aaf19)):\n\n  This patch adds a new field `id` to JSON error payloads. This helps\n  tremendously in implementing better client-side (native / SPA) apps as the API\n  now returns error IDs like `no_active_session`, `orbidden_return_to`,\n  `no_verified_address` and more. UIs can use these IDs to decide what to do\n  next in the application - for example redirecting to a particular endpoint or\n  showing an error message.\n\n- Add initial value to bool checkboxes\n  ([63dba73](https://github.com/ory/kratos/commit/63dba737376dbe2f15c5afb5df22c593328c6483))\n- Add internal context to login and registration\n  ([723e6ee](https://github.com/ory/kratos/commit/723e6eee731d34f85bc4346a1040f2f121662ae9))\n- Add internal context to settings flow\n  ([afb6895](https://github.com/ory/kratos/commit/afb6895daa8743edbf4fca957b2a156e676ef63a))\n- Add lookup node to disable lookup\n  ([d0836be](https://github.com/ory/kratos/commit/d0836beb53709c88eb9ed78df39e95a3204c7cec)):\n\n  See https://github.com/ory/cloud/issues/12\n\n- Add lookup to config\n  ([14119b6](https://github.com/ory/kratos/commit/14119b623941b6f5e795ef0d369ee9e3adb73207))\n- Add lookup to identity\n  ([ead3833](https://github.com/ory/kratos/commit/ead3833e254b4939f2b86b34e954580960cc7ea1))\n- Add lookup to migrations\n  ([dac4f75](https://github.com/ory/kratos/commit/dac4f759a0b92c1eebca177c3c931fb3146e7dee))\n- Add MFA enforcment option to whoami and settings\n  ([554d725](https://github.com/ory/kratos/commit/554d72552702818c8f1fc45fd1daf9d93c0d2cad))\n- Add mfa for non-browser\n  ([4096fd3](https://github.com/ory/kratos/commit/4096fd3fbdb430fd325b38fe9102defa31dd1b6d))\n- Add missing migrations\n  ([ccc64d8](https://github.com/ory/kratos/commit/ccc64d87935c6b5ad506dce6a5f903d56541f864))\n- Add option to disable recovery codes\n  ([9d3daa6](https://github.com/ory/kratos/commit/9d3daa656a5361ef8a90fe3511f9c1a6e9015969)):\n\n  Closes https://github.com/ory/cloud/issues/12\n\n- Add ory cli config\n  ([5b959be](https://github.com/ory/kratos/commit/5b959beaba4d03e143f7701c30bc30e25f2c51cc))\n- Add schema patch for new initial_value field\n  ([131e380](https://github.com/ory/kratos/commit/131e3803ff6d04af9ec668286c8e6fcf88467214)):\n\n  The field sets a node input's initial value. This is primarily used for fields\n  which are e.g. checkboxes or buttons (active/inactive). If this field is set\n  on a button, it implies that clicking the button should trigger the \"value\" to\n  be set.\n\n- Add script type and discriminator for attributes\n  ([de0af95](https://github.com/ory/kratos/commit/de0af955904894d97997cf598686b6d33cd88bd4)):\n\n  See https://github.com/ory/sdk/issues/72\n\n- Add smtp headers config option\n  ([#1747](https://github.com/ory/kratos/issues/1747))\n  ([7ffe0e9](https://github.com/ory/kratos/commit/7ffe0e9766e930615dbb6833e650b73a8975a544)),\n  closes [#1725](https://github.com/ory/kratos/issues/1725)\n- Add support for onclick javascript in ui nodes\n  ([7cc7efa](https://github.com/ory/kratos/commit/7cc7efa00ff0e8107f1369573bfdf766fcfc0e93))\n- Add totp strategy for settings flow\n  ([d1d6617](https://github.com/ory/kratos/commit/d1d6617013fbcc37eaf48cf19061b86955fc5d5e)):\n\n  This patch allows adding a TOTP device in the settings, and also removing it\n  when no longer needed.\n\n- Add webauthn identity credential\n  ([f8b9582](https://github.com/ory/kratos/commit/f8b95828ea41d29c7f3577cc7772168135bc5514))\n- Adding Dockle Container Linter\n  ([#1852](https://github.com/ory/kratos/issues/1852))\n  ([3c0d519](https://github.com/ory/kratos/commit/3c0d519dd47657c6adca3d64bca8b3ed02cb7a8f))\n- Adjust to new aal error handling\n  ([b8956bc](https://github.com/ory/kratos/commit/b8956bc0fc8a45e88dd51f79608f9d6c34e2b6f3))\n- API to return access, refresh, id tokens from social sign in\n  ([#1818](https://github.com/ory/kratos/issues/1818))\n  ([198991a](https://github.com/ory/kratos/commit/198991a9ce25fbaccc927be3bd3f6b1593771bec)),\n  closes [#1518](https://github.com/ory/kratos/issues/1518)\n  [#397](https://github.com/ory/kratos/issues/397):\n\n  This patch introduces the new `include_credential` query parameter to the\n  `GET /identities` endpoint which allows administrators to receive the initial\n  access, refresh, and ID tokens from Social Sign In (OpenID Connect / OAuth\n  2.0) flows.\n\n  These tokens can be stored in an encrypted format (XChaCha20Poly1305 or\n  AES-GCM) in the database if an appropriate encryption secret is set. To get\n  started easily these values are not encrypted per default.\n\n  For more information head\n  [over to the docs](https://kratos/docs/guides/retrieve-social-sign-in-access-refresh-id-token).\n\n- Auto-generate list of messages\n  ([cf46339](https://github.com/ory/kratos/commit/cf46339b9a07cd72b4d01e40c2df72e6c8104e9b)),\n  closes [#1784](https://github.com/ory/kratos/issues/1784)\n- Endpoint to list all identity schemas\n  ([#1703](https://github.com/ory/kratos/issues/1703))\n  ([aa23d5d](https://github.com/ory/kratos/commit/aa23d5d5af28d8a7789b4a0c7e97197c7758ad98)),\n  closes [#1699](https://github.com/ory/kratos/issues/1699)\n- Generate sdks and update versions\n  ([c9d22d9](https://github.com/ory/kratos/commit/c9d22d91f5fe49b5f2818160ade58bfd265f03e5))\n- **hash:** PBKDF2 password hash verification\n  ([#1774](https://github.com/ory/kratos/issues/1774))\n  ([33cc7e0](https://github.com/ory/kratos/commit/33cc7e02d9bcc24ae1de438102660cc89fd008d6)),\n  closes [#1659](https://github.com/ory/kratos/issues/1659)\n- Identity schema validation on startup\n  ([#1779](https://github.com/ory/kratos/issues/1779))\n  ([99db3f0](https://github.com/ory/kratos/commit/99db3f03afd4b2525cbce54133a1abd1d49d2886)),\n  closes [#701](https://github.com/ory/kratos/issues/701)\n- **identity:** Add AAL constants\n  ([882573d](https://github.com/ory/kratos/commit/882573df5621446e799b17ca0ab09d3934e44437))\n- Implement AAL for login and sessions\n  ([45467e0](https://github.com/ory/kratos/commit/45467e0caba7ed31e2ebde71a8b32ecd5f8db7c2))\n- Implement endpoint for invalidating all sessions for a given identity\n  ([#1740](https://github.com/ory/kratos/issues/1740))\n  ([dbd1689](https://github.com/ory/kratos/commit/dbd1689c11fd0a3d999ea09b553dd4a14a7a6972)),\n  closes [#655](https://github.com/ory/kratos/issues/655):\n\n  This PR introduces endpoint to destroy all sessions for a given identity which\n  effectively logouts user from all devices/sessions. This is useful when for\n  some security concern we want to make sure there are no \"old\" sessions active\n  or other \"staff\" related actions (such as force logout after password change\n  etc.).\n\n- Implement lookup code settings and login\n  ([8f3ce7b](https://github.com/ory/kratos/commit/8f3ce7b33390fcae85e605193806364ca9d099c9))\n- Improve detection of AAL errors and return 422 instead of 403\n  ([e2bfbea](https://github.com/ory/kratos/commit/e2bfbea1541aca983eb835d3da2b5fe70ac4b7a5))\n- Improve labels for totp and lookup\n  ([b92e00e](https://github.com/ory/kratos/commit/b92e00e345da1f8ab76750e3f0ae1301977bbae0))\n- Improve session device annotations\n  ([87907b8](https://github.com/ory/kratos/commit/87907b8d29dc9cd7140535e81ea62c2d7f8e41c3))\n- In docker debug support with delve\n  ([#1789](https://github.com/ory/kratos/issues/1789))\n  ([37325a1](https://github.com/ory/kratos/commit/37325a18d9430130d0062674433fa0d3f9a59eb3))\n- Introduce cve scanning ([#1798](https://github.com/ory/kratos/issues/1798))\n  ([ade13ea](https://github.com/ory/kratos/commit/ade13ea082ee11e9c1005de3ccb3ae6b5f02bb49))\n- **logout:** Add logout token to browser response\n  ([#1758](https://github.com/ory/kratos/issues/1758))\n  ([d3f1177](https://github.com/ory/kratos/commit/d3f1177a9a82dc2c4f930f15c6ec87c3ec5a1d53))\n- Mark recovery email address verified\n  ([#1665](https://github.com/ory/kratos/issues/1665))\n  ([e3efc5d](https://github.com/ory/kratos/commit/e3efc5d0673106115a236e38b5d76d6672d64d20)),\n  closes [#1662](https://github.com/ory/kratos/issues/1662)\n- Mark required fiels as required\n  ([34cd5e8](https://github.com/ory/kratos/commit/34cd5e8e638be3d48ed8174112417bc36400e8cb)):\n\n  Closes https://github.com/ory-corp/cloud/issues/1328 Closes\n  https://github.com/ory/kratos/issues/400 Closes\n  https://github.com/ory/kratos/issues/1058 See\n  https://ory-community.slack.com/archives/C012RJ2MQ1H/p1631825476159000\n\n- Natively support social sign in for single-page apps\n  ([1a1a350](https://github.com/ory/kratos/commit/1a1a350a9f0df85195505690fc52086eddf78371))\n- **persistence:** Add new columns for mfa\n  ([6184fe3](https://github.com/ory/kratos/commit/6184fe385cf87b260117290089b06445e5b6b205))\n- Potentially add arm64 docker support\n  ([68112de](https://github.com/ory/kratos/commit/68112defb97db1c6f4b8bf65e2e522b22e27d280))\n- Proper enum and type assertions for openapi\n  ([c4d8516](https://github.com/ory/kratos/commit/c4d8516fb93c2127c6d0c28a914ed7b8f8646832))\n- Publish webauthn as loadable script instead of eval\n  ([2717c59](https://github.com/ory/kratos/commit/2717c5958ab3f088821fdf96fdf6d44d48fea310))\n- Redirect on login if session aal is not matched\n  ([8feff8d](https://github.com/ory/kratos/commit/8feff8daaf4ac744fab22627d9bdab45740570d5))\n- Respect webauthn in session aal\n  ([869b4a5](https://github.com/ory/kratos/commit/869b4a5a812b840196eaf1e591aeb685d7f0e904))\n- **session:** Respect 2fa enforcement in whoami\n  ([3a82c88](https://github.com/ory/kratos/commit/3a82c8806931a2b4cd05142a6dae8040a76658bc))\n- Sign in with apple ([#1833](https://github.com/ory/kratos/issues/1833))\n  ([16ed123](https://github.com/ory/kratos/commit/16ed123adba06167f70eb952ae3877d4476f8c71)),\n  closes [#1782](https://github.com/ory/kratos/issues/1782):\n\n  Adds an adapter and configuration options for enabling Social Sign In with\n  Apple.\n\n- Sort totp nodes\n  ([5c9a494](https://github.com/ory/kratos/commit/5c9a49487f45af5b7edf069edf9c3d37ef293cd5))\n- Stubable time in text package\n  ([22e4ed1](https://github.com/ory/kratos/commit/22e4ed15e2eecb51b393762077872b19f6f2acd2))\n- Support apple m1\n  ([54b4fb6](https://github.com/ory/kratos/commit/54b4fb698c6a087afef8821fa8300798e484ae18))\n- Support setting the identity state via the admin API\n  ([#1805](https://github.com/ory/kratos/issues/1805))\n  ([29c060b](https://github.com/ory/kratos/commit/29c060bd348733eeafee98d5f255c737a8cbcad0)),\n  closes [#1767](https://github.com/ory/kratos/issues/1767)\n- Support strategy return to ui for settings\n  ([74670bb](https://github.com/ory/kratos/commit/74670bb4b0cc45626537e5ac63283fd14f05dee1))\n- Support webauthn for mfa\n  ([e8f4d3c](https://github.com/ory/kratos/commit/e8f4d3cb899d44c777b094f2ae4d84ff68532bf9))\n- **totp:** Add width and height to QR code\n  ([a648ba3](https://github.com/ory/kratos/commit/a648ba3de9a0ba707ce39c37fa5d5e38c4da74d3))\n- **totp:** Support account name setting from schema\n  ([19a6bcc](https://github.com/ory/kratos/commit/19a6bcc9d8940acb2a5f0eb4a6cc7f28801a2f92))\n- Treat lookup as aal2 in session\n  ([3269028](https://github.com/ory/kratos/commit/3269028d46d0ef23de3f905c325d514f24db43b8))\n- Use discriminators for ui node types in spec\n  ([59e808e](https://github.com/ory/kratos/commit/59e808e8dc6339da59bbe08ebbcf7b840e3fdd50))\n- Use initial_value in lookup strategy\n  ([efe272f](https://github.com/ory/kratos/commit/efe272f06966edc4858602d94740b6ed36c12e57))\n\n### Reverts\n\n- 3745014\n  ([d493d10](https://github.com/ory/kratos/commit/d493d1049f90ca6ee7b85931e3652aa9fdeb0254))\n\n### Tests\n\n- Aal in login.NewFlow\n  ([5986e38](https://github.com/ory/kratos/commit/5986e38e6ab9eec1761e4c723c807dc0ef2a3dfa))\n- AcceptToRedirectOrJSON\n  ([2ca153f](https://github.com/ory/kratos/commit/2ca153f027599c18583ce0ebacb5ed577b56ddf3))\n- Add credentials test\n  ([58b388c](https://github.com/ory/kratos/commit/58b388c70d5ff32822e8ac5f3a394e683273ac6a))\n- Add expired test to login handler\n  ([3bdb8ab](https://github.com/ory/kratos/commit/3bdb8abb558c0f8c4b33f712678f5da02d0ef4ee))\n- Add identity change test to settings submit\n  ([5eb090b](https://github.com/ory/kratos/commit/5eb090b2564192deb77e64dd74a07b96c381391d))\n- Add initial spa e2e test\n  ([20617f6](https://github.com/ory/kratos/commit/20617f628ac84981c3b47ce9e9ab193b8ff426d0))\n- Add initial totp integration tests\n  ([c9d456b](https://github.com/ory/kratos/commit/c9d456bf03cb33baf0745fe9a511f84b4c9427e3))\n- Add login tests\n  ([a71cadd](https://github.com/ory/kratos/commit/a71cadde91bdaf960caf30dcfa957a2646da86a2))\n- Add migrations tests for new tables\n  ([3c96ab0](https://github.com/ory/kratos/commit/3c96ab059af9bf6002b341c5db51d1b3ca5da655))\n- Add react app to e2e tests\n  ([1214eee](https://github.com/ory/kratos/commit/1214eeee24b06e6e72c55cfed2176860ecbf3c13))\n- Add schema test for totp config\n  ([c4f05ba](https://github.com/ory/kratos/commit/c4f05ba60af1d7ca31b4cf54097cbefa88085704))\n- Add session amr test\n  ([eedb60b](https://github.com/ory/kratos/commit/eedb60bec9bebfb0a4ffb67dd484d2e6b466e776))\n- Add settings tests\n  ([6959565](https://github.com/ory/kratos/commit/6959565212dc5e7296aad7f1365a944379dd5d6d))\n- Add test for TOTPIssuer\n  ([14731c4](https://github.com/ory/kratos/commit/14731c4e7809c2202c9298422c005358b7b26fc3))\n- Add test for ui error page\n  ([3977a9c](https://github.com/ory/kratos/commit/3977a9c4d6f98ef6d8f7f4c88d55b46579401ba8))\n- Add TestEnsureInternalContext\n  ([152bfc7](https://github.com/ory/kratos/commit/152bfc7294078081ca9f8fc6dd194db6d2e699ad))\n- Add totp registry tests\n  ([817e3ec](https://github.com/ory/kratos/commit/817e3ecb213454e4ce3f987ce8a8714301ee8165))\n- Add totp settings tests\n  ([c5a0d0f](https://github.com/ory/kratos/commit/c5a0d0f8435690786eaf719bb1376f7da15a6203))\n- Add TOTP to profile\n  ([7431e9f](https://github.com/ory/kratos/commit/7431e9fcf4e9c9853ec4d378221c7a3744b3b239))\n- Add update session test\n  ([47bd057](https://github.com/ory/kratos/commit/47bd057da0fbf849d643c27c6eb75ef09c5075fb))\n- Additional checks for flow hydration\n  ([a40d7fe](https://github.com/ory/kratos/commit/a40d7fe4340ff61c3fa9ac0a70dc5f7e4641a15e))\n- Amr persistence\n  ([b0b2d81](https://github.com/ory/kratos/commit/b0b2d8174ca46e066e8eb912a24d9e6efeea0ce8))\n- Check if internal context is validated in store\n  ([a23d851](https://github.com/ory/kratos/commit/a23d8518fc65f645cae9c196ff70df4efca67266))\n- CheckAAL\n  ([03b37e7](https://github.com/ory/kratos/commit/03b37e7675e369817d2bb226047ec9f26b18a456))\n- Complete TOTP login integration tests\n  ([6e503cf](https://github.com/ory/kratos/commit/6e503cff28428e707b3812cd2bf8e44ccc487b89))\n- **e2e:** Add baseurl\n  ([159b25f](https://github.com/ory/kratos/commit/159b25f7ab0ac659033d861868f472183b852167))\n- **e2e:** Add checkboxes to schemas\n  ([0c91f0c](https://github.com/ory/kratos/commit/0c91f0c89081726e7451d5411a6adeb631ae2edb))\n- **e2e:** Add config for proxy to simplify cy.visit logic\n  ([7d87985](https://github.com/ory/kratos/commit/7d8798560947227d64a35d2dd69623bc1a1ddc8f))\n- **e2e:** Add mfa profile\n  ([a60d157](https://github.com/ory/kratos/commit/a60d157bfeb79cb527bf73b3fc38e1ba5388cbed))\n- **e2e:** Add modd to build\n  ([48cd8ae](https://github.com/ory/kratos/commit/48cd8aeb851d02e2fd31e73e044befb45242e953))\n- **e2e:** Add more helpers and ts defs\n  ([21b35b0](https://github.com/ory/kratos/commit/21b35b025a21b1f6ab3ac8be79339f1734b3033a))\n- **e2e:** Add more helpers for various flows and proxy settings\n  ([755ac60](https://github.com/ory/kratos/commit/755ac60cb1a54cd188ab07d9448598d738c5e866))\n- **e2e:** Add more routes to registry\n  ([30423c9](https://github.com/ory/kratos/commit/30423c92ba27709e003e88e58072b78ef3e2aa04))\n- **e2e:** Add more typings for cypress helpers\n  ([60bd63f](https://github.com/ory/kratos/commit/60bd63f31d6b639af19048cc3d1e392b885213e0))\n- **e2e:** Add plugin for using got\n  ([8fafc40](https://github.com/ory/kratos/commit/8fafc40dff8a0d9d5d678b59ecf4c13755906a4f))\n- **e2e:** Add proxy capabilities for react native app\n  ([b5668df](https://github.com/ory/kratos/commit/b5668df755e186f12c0e543715bc2e16011583a6))\n- **e2e:** Add recovery tests for SPA\n  ([b6014ee](https://github.com/ory/kratos/commit/b6014eee8b507abf6e3b4324097b3015f722cbe3))\n- **e2e:** Add spa as allowed redirect url\n  ([2625d16](https://github.com/ory/kratos/commit/2625d1689d47fb1cdbe34708be27f2317cdc7bea))\n- **e2e:** Add SPA tests for login and refactor tests to typescript\n  ([d9a25df](https://github.com/ory/kratos/commit/d9a25df1ba34cbefd416dccfdb2f5fc93e0290b9))\n- **e2e:** Add SPA tests for logout and refactor tests to typescript\n  ([b0c6776](https://github.com/ory/kratos/commit/b0c67769e4afcdbc05d2c1966e38faa18404a5db))\n- **e2e:** Add SPA tests for registration and refactor tests to typescript\n  ([a61ed1e](https://github.com/ory/kratos/commit/a61ed1edb41df64f58e23f8c88894fb742fd275d))\n- **e2e:** Add support functions and type definitions\n  ([c82d68d](https://github.com/ory/kratos/commit/c82d68db36563b16623a63be9efaf6b25322f855))\n- **e2e:** Clean up helper\n  ([4806add](https://github.com/ory/kratos/commit/4806add17a5dd0ea8c8fded644a6c240b17861b3))\n- **e2e:** Complete SPA tests for all mfa flows\n  ([2196129](https://github.com/ory/kratos/commit/219612903bd4dce208e2074e4595980c1cb60711))\n- **e2e:** Default and empty values and required fields\n  ([72f2c5f](https://github.com/ory/kratos/commit/72f2c5fbd8227e19d62f26aeddfb1bd14d7c768b))\n- **e2e:** Ensure advanced types work in forms also\n  ([287269c](https://github.com/ory/kratos/commit/287269c9992390b52ff380b31eda3bb7ad205f09))\n- **e2e:** Ensure correct app\n  ([a9ff545](https://github.com/ory/kratos/commit/a9ff5457cb48a90668b62e54d0b08cb1e9108994))\n- **e2e:** Finalize mobile tests\n  ([acf5c3d](https://github.com/ory/kratos/commit/acf5c3d649e51edfd9e1e3755222d9c7161a92e7))\n- **e2e:** Force port\n  ([a49eda8](https://github.com/ory/kratos/commit/a49eda8e0405954d62058d8c1410a62f72bfb7ae))\n- **e2e:** Homogenize profiles\n  ([7798e19](https://github.com/ory/kratos/commit/7798e193aa3cce0347e5ca018e09685b6fda0ba2))\n- **e2e:** Hot reload ory kratos on changes\n  ([841da09](https://github.com/ory/kratos/commit/841da091689f9a3fceb5509490d7a2f4828b926f))\n- **e2e:** Implement recovery tests for SPA\n  ([3dea57f](https://github.com/ory/kratos/commit/3dea57ff986702b9a31621198794e1cc94e4881e))\n- **e2e:** Implement required verification tests for SPA\n  ([fb55f34](https://github.com/ory/kratos/commit/fb55f3475f25ab3aa6f7b1765ec5b9f13ef72b15))\n- **e2e:** Improve stability for login tests\n  ([43df22b](https://github.com/ory/kratos/commit/43df22bdd52305b2b5d98a0db1c09751bd3ebb4f))\n- **e2e:** Improve stability for registration tests\n  ([a1c59a3](https://github.com/ory/kratos/commit/a1c59a349cab3819e5f869dc89eba3c05100f1b8))\n- **e2e:** Improve test reliability\n  ([061a7e3](https://github.com/ory/kratos/commit/061a7e340c86b580abde02de3cb521dda7c23efb))\n- **e2e:** Migrate email tests to new proxy set up\n  ([54d8cd6](https://github.com/ory/kratos/commit/54d8cd65b8b19f7a643bf9d4060906b818fc91d6))\n- **e2e:** Migrate settings tests to typescript and add SPA tests\n  ([566336d](https://github.com/ory/kratos/commit/566336d910f0b3deb4675e1413bfd0182bde6a79))\n- **e2e:** Move config to lower level and publish as package\n  ([c21fa26](https://github.com/ory/kratos/commit/c21fa2688e560bb9c714d2078dbc9a72a1da125f))\n- **e2e:** Move registration tests to new proxy set up\n  ([eddeb85](https://github.com/ory/kratos/commit/eddeb8510ca4cb13d0644d7083d436778828d0bd))\n- **e2e:** Port mobile test to typescript\n  ([db42346](https://github.com/ory/kratos/commit/db4234694723b7dc965c9e2cf4ba792bad0374e9))\n- **e2e:** Port remaining e2e tests to typescript\n  ([5853d1a](https://github.com/ory/kratos/commit/5853d1a64b3f7b20af79cc6ebbc381de0d213139))\n- **e2e:** Potentially resolve flaky login test\n  ([e237d66](https://github.com/ory/kratos/commit/e237d66adbc3cce972d8e4689a88d02b9a925354))\n- **e2e:** Potentially resolve webauthn startup issues\n  ([eae6f5d](https://github.com/ory/kratos/commit/eae6f5d1e9dc08dc8f7152a9c441e029dd4351f3))\n- **e2e:** Prototype typescript implementation\n  ([2e869cf](https://github.com/ory/kratos/commit/2e869cff7b1cb87e15013a86b54fda16a01e0267))\n- **e2e:** Recreate identities per flow\n  ([1a560a3](https://github.com/ory/kratos/commit/1a560a37c13240d9ae16d34188a6221f589ebbbc))\n- **e2e:** Reduce flaky tests\n  ([cae86e7](https://github.com/ory/kratos/commit/cae86e7f6a4fcc9e1433b9c063efe3745273f2dc))\n- **e2e:** Reduce test flakes in lookup codes\n  ([bfea354](https://github.com/ory/kratos/commit/bfea354f45858e5be0a588840f6e8125819a244c))\n- **e2e:** Refactor and add support for SPA app\n  ([7609219](https://github.com/ory/kratos/commit/7609219448effde35844675533e71583babe1d14))\n- **e2e:** Remove wait condition\n  ([af10b03](https://github.com/ory/kratos/commit/af10b03ebca03cdb5654c116efbd3c23b47c7594))\n- **e2e:** Resolve broken test\n  ([c7cf134](https://github.com/ory/kratos/commit/c7cf134fbfbbb59b276aa00d02bbad3886f78dee))\n- **e2e:** Resolve flaky test\n  ([de7cc59](https://github.com/ory/kratos/commit/de7cc59f07a6b77e3bbf3d98a7b2104b60ce708c))\n- **e2e:** Resolve flaky test issues\n  ([1627745](https://github.com/ory/kratos/commit/162774567d44336c8999ee0c1362adb191855d0c))\n- **e2e:** Resolve next not starting\n  ([2a2a3cb](https://github.com/ory/kratos/commit/2a2a3cb016e820f651f3cf6cd33123672e5977cb))\n- **e2e:** Resolve regression\n  ([d62f0c0](https://github.com/ory/kratos/commit/d62f0c02315702f55b998d4c48d4ca8c6a41827f))\n- **e2e:** Resolve regressions\n  ([aaff34e](https://github.com/ory/kratos/commit/aaff34ed66165f787103292ac0a034a0cdaf1308))\n- **e2e:** Resolve regressions\n  ([af9aedc](https://github.com/ory/kratos/commit/af9aedc8d29678f480b1b6bad128aefbacd6a373))\n- **e2e:** Revert proxy changes\n  ([293d920](https://github.com/ory/kratos/commit/293d92084a7614ae0cd7d5326dc82a209a0841be))\n- **e2e:** Stabilize e2e tests\n  ([a5dca28](https://github.com/ory/kratos/commit/a5dca2839ef66217b0046262a7e1fc886276509f))\n- **e2e:** Temporarily add totp to default profile\n  ([8ffac9d](https://github.com/ory/kratos/commit/8ffac9d138656eb2322913992b350cea31ed7e87))\n- **e2e:** Update e2e profiles to new proxy set up\n  ([a3204cf](https://github.com/ory/kratos/commit/a3204cf9b85e274441c02592288a4f322481e894))\n- **e2e:** Use 127.0.0.1 to prevent ipv6 issues\n  ([6f4b534](https://github.com/ory/kratos/commit/6f4b5340d33b31a5e4582858b544beb9c82181c7))\n- **e2e:** Wait for oidc to trigger\n  ([9c67c49](https://github.com/ory/kratos/commit/9c67c49235a562430da7ae60426d60cfd6120fca))\n- Enable cookie debug\n  ([81c3064](https://github.com/ory/kratos/commit/81c3064d69f8a233b8e0b78e103f2a23ae63cb63))\n- Ensure aal and amr is set on recovery\n  ([5cbab54](https://github.com/ory/kratos/commit/5cbab54fe5780689f0b64700567ac4632eb04c0b)),\n  closes [#1322](https://github.com/ory/kratos/issues/1322)\n- Ensure aal2 can not be used for oidc\n  ([cbbcdd2](https://github.com/ory/kratos/commit/cbbcdd2e86c2d4da14c478637105eb8a36ae06c0))\n- Ensure aal2 can not be used for password\n  ([d9d39f0](https://github.com/ory/kratos/commit/d9d39f0bdda0725989a0a8261a449cf1a71afb6b))\n- Ensure authenticated_at after all upgrade\n  ([80408b4](https://github.com/ory/kratos/commit/80408b4c90229c61138411be8534fc577b8f0f33))\n- Ensure redirect_url in password strategy\n  ([9eafc10](https://github.com/ory/kratos/commit/9eafc10189ca88724fa6d75748299c2dd2c470b1))\n- ErrStrategyAsksToReturnToUI behavior\n  ([f739018](https://github.com/ory/kratos/commit/f7390184b02d526bb6e3ff496abc4522afc39d5a))\n- Finalize webauthn tests\n  ([97e59e6](https://github.com/ory/kratos/commit/97e59e61ee8be263199c3749e27dd81344777166))\n- Fix regressions in the tests\n  ([246c580](https://github.com/ory/kratos/commit/246c580222acd193eea784a6cbfd1e75181a484f))\n- Fix tests in cmd/serve ([#1755](https://github.com/ory/kratos/issues/1755))\n  ([b704d08](https://github.com/ory/kratos/commit/b704d08382a9059157c2a649872e88943d66a99f))\n- ID methods of node attributes\n  ([ff9ff04](https://github.com/ory/kratos/commit/ff9ff048ddfa13ae73571064a36b33a867727392))\n- Login form submission with AAL\n  ([4d54fbb](https://github.com/ory/kratos/commit/4d54fbb37349126418274de8e21473c2ff81f785))\n- **lookup:** Add secret_disable to snapshots\n  ([68d6a87](https://github.com/ory/kratos/commit/68d6a876a4f1a0fd74789798397bd325a68d71d6))\n- **lookup:** Ensure context is cleaned up after use\n  ([8a210c4](https://github.com/ory/kratos/commit/8a210c41696d1865cce4c589a7cb3e52283fe24d))\n- **lookup:** Refresh and reuse scenarios\n  ([89736ed](https://github.com/ory/kratos/commit/89736ed9ba8667314313ca549a6377faddcc3d80))\n- **migration:** Resolve mysql migration issue with empty array\n  ([71a5649](https://github.com/ory/kratos/commit/71a5649a52036e29b351b6b4ee220ec7ce3aed05))\n- Move to cupaloy for snapshots\n  ([0cce70f](https://github.com/ory/kratos/commit/0cce70f47712da44d891c6d2890e818da6d9971b))\n- Properly refresh mobile session\n  ([c31915d](https://github.com/ory/kratos/commit/c31915de32e4b3db4af8ca8f3b5ecb0adf01a510))\n- Registry regression\n  ([25c88b5](https://github.com/ory/kratos/commit/25c88b55577b016aa77d2df3c595410633d0eefe))\n- Remove todo items\n  ([f60050e](https://github.com/ory/kratos/commit/f60050e0e30b1bf5441c95ada5777743719d65f1))\n- Resolve flaky config test\n  ([147c670](https://github.com/ory/kratos/commit/147c6704a9d38b5687eb8aba5661f24f99e577e3))\n- Resolve flaky config test ([#1832](https://github.com/ory/kratos/issues/1832))\n  ([db98d01](https://github.com/ory/kratos/commit/db98d010639bfc387ef927c4f80ff6cd0ebc9588))\n- Resolve flaky example tests\n  ([#1817](https://github.com/ory/kratos/issues/1817))\n  ([0e700d8](https://github.com/ory/kratos/commit/0e700d89c0aaa99b9eec7ce070b7974373377f03))\n- Resolve flaky tests\n  ([2bd9100](https://github.com/ory/kratos/commit/2bd910037efd20ab1829784ee087c533e5e8b177))\n- Resolve migratest regressions\n  ([e9a1ed1](https://github.com/ory/kratos/commit/e9a1ed188a8f2556e1f60d1c171506dc0dd931d4))\n- Resolve regressions\n  ([1502ca1](https://github.com/ory/kratos/commit/1502ca1eb6c2e7ab698dc94675a50db63c326a41))\n- Resolve regressions\n  ([1a93b2f](https://github.com/ory/kratos/commit/1a93b2fba1fc41a6ba314253387af9770fd36f5a))\n- Resolve regressions\n  ([64850ed](https://github.com/ory/kratos/commit/64850ed3277185ebf68b50449721c903c01eab89))\n- Resolve remaining regressions\n  ([f02804c](https://github.com/ory/kratos/commit/f02804c567a532a30eaa228b0ba784b7f7fb0d9a))\n- Resolve remaining regressions\n  ([0224c22](https://github.com/ory/kratos/commit/0224c22ebda566c69363ae09dea9d42368c86f48))\n- Resolve remaining regressions\n  ([1fa2aa5](https://github.com/ory/kratos/commit/1fa2aa5b60d0b81e2035ae18c60d199b060a4c1f))\n- Resolve time locality issues\n  ([53b8b2a](https://github.com/ory/kratos/commit/53b8b2a22e5bad12dabf90c7bcbaf05b13a73a55))\n- Restructure session struct tests\n  ([50d3f66](https://github.com/ory/kratos/commit/50d3f66f82cb4e85a213fd86dc20bfadafefae23))\n- Session AAL handling\n  ([6fea3e5](https://github.com/ory/kratos/commit/6fea3e5aec6556697092c9a9d12295ed7e4d408b))\n- Session activate\n  ([c86fa03](https://github.com/ory/kratos/commit/c86fa03d3b2390403dcb14ef93307adc61ac7c79))\n- **sql:** Fix incorrect UUID\n  ([ea2894e](https://github.com/ory/kratos/commit/ea2894ed0f12de011fd5ce304dd614579ea5e96c))\n- Temporarily enable lookup globally\n  ([458f559](https://github.com/ory/kratos/commit/458f559ec816e64c6c9f53ecacdb4ae30fc9f8f7))\n- **totp:** Ensure context is cleaned up after use\n  ([1905883](https://github.com/ory/kratos/commit/19058830c0541f717360d3f599760b2a5cf47c4e))\n- Upgrade cypress to 8.x\n  ([c8a1dfc](https://github.com/ory/kratos/commit/c8a1dfcae3d42555b1215ad7eaa03a521bdcb1da))\n- Use different return handler\n  ([e489a43](https://github.com/ory/kratos/commit/e489a439e56dcd4218cf81284beaca0ef2ecd35e))\n- Various aal combinations for newflow\n  ([b095b99](https://github.com/ory/kratos/commit/b095b990224cbbd5ffa272b8f443b3345634d353))\n- Webauth settings flow\n  ([4c82772](https://github.com/ory/kratos/commit/4c82772ae28643ce69a5778c37f3c67644ef6f4c))\n- Webauthn aal2 login\n  ([60ace8b](https://github.com/ory/kratos/commit/60ace8b36c033ac4f9cd7e8cd929921e2e882946))\n- Webauthn credentials\n  ([c3e1184](https://github.com/ory/kratos/commit/c3e1184e719cd2041df8894edd4bd921bf2c3b00))\n- Webauthn credentials counter\n  ([f7701f6](https://github.com/ory/kratos/commit/f7701f629d5553e229546b00d3c345a8d74dd627))\n- **webauthn:** Ensure context is cleaned up after use\n  ([7a8055b](https://github.com/ory/kratos/commit/7a8055be357a64a1f4074fe28b249fbaf05cf519))\n\n### Unclassified\n\n- test(e2e) improve reliability\n  ([763dd00](https://github.com/ory/kratos/commit/763dd0063f3166fad323b25a1b0e7bdf9850e519))\n- Correct session godoc\n  ([7108e65](https://github.com/ory/kratos/commit/7108e65447c37cc6f2937083a2a61442e0a43cb8))\n\n# [0.7.6-alpha.1](https://github.com/ory/kratos/compare/v0.7.5-alpha.1...v0.7.6-alpha.1) (2021-09-12)\n\nResolves further issues in the SDK and release pipeline.\n\n### Code Generation\n\n- Pin v0.7.6-alpha.1 release commit\n  ([8b0d1ee](https://github.com/ory/kratos/commit/8b0d1ee66f1ee2b9f37cd178ac2bcbd8980d6f1d))\n\n# [0.7.5-alpha.1](https://github.com/ory/kratos/compare/v0.7.4-alpha.1...v0.7.5-alpha.1) (2021-09-11)\n\nPrimarily resolves issues in the SDK pipeline.\n\n### Code Generation\n\n- Pin v0.7.5-alpha.1 release commit\n  ([3a741a5](https://github.com/ory/kratos/commit/3a741a5ed5cff78e0e060bc98f8526537e8719d7))\n\n# [0.7.4-alpha.1](https://github.com/ory/kratos/compare/v0.7.3-alpha.1...v0.7.4-alpha.1) (2021-09-09)\n\nThis release adds the GitHub-app provider, improves SQL instrumentation,\nresolves an expired flow bug, and resolves documentation issues.\n\n### Bug Fixes\n\n- Corret sdk annotations for enums\n  ([6152363](https://github.com/ory/kratos/commit/6152363cda20992a9b894e618c3a438f30808a97))\n- Do not panic if cookiemanager returns a nil cookie\n  ([6ea5678](https://github.com/ory/kratos/commit/6ea56785fa0354d8d9479a699304a4b933d6c294)),\n  closes [#1695](https://github.com/ory/kratos/issues/1695)\n- Respect return_to in expired flows\n  ([#1697](https://github.com/ory/kratos/issues/1697))\n  ([394a8de](https://github.com/ory/kratos/commit/394a8de9c0cdd33df91d56008eac12510ff14e07)),\n  closes [#1251](https://github.com/ory/kratos/issues/1251)\n\n### Code Generation\n\n- Pin v0.7.4-alpha.1 release commit\n  ([67ff8a9](https://github.com/ory/kratos/commit/67ff8a947b5b339648aeb4c22aba89205c61382b))\n\n### Documentation\n\n- Add e2e quickstart\n  ([2b749d3](https://github.com/ory/kratos/commit/2b749d39fcb0d320d193290966a558ee2c5734d1))\n- Browser redirects ([#1700](https://github.com/ory/kratos/issues/1700))\n  ([a44089a](https://github.com/ory/kratos/commit/a44089a506f5ea9daa406fcb862ad707f569c2bb))\n- Mark logout_url always available\n  ([9021805](https://github.com/ory/kratos/commit/9021805c4399beb73f234726f8f5f3bfd312482c))\n- Minor improvements ([#1707](https://github.com/ory/kratos/issues/1707))\n  ([79c132c](https://github.com/ory/kratos/commit/79c132c5a0737ea1632655d8aea0af63c4200d37))\n\n### Features\n\n- Making use of the updated instrumentedsql version\n  ([#1723](https://github.com/ory/kratos/issues/1723))\n  ([9e6fbdd](https://github.com/ory/kratos/commit/9e6fbdd06a75d7207b4801d1148267b3a1a0a0c7))\n- **oidc:** Github-app provider\n  ([#1711](https://github.com/ory/kratos/issues/1711))\n  ([fb1fe8c](https://github.com/ory/kratos/commit/fb1fe8c468bb6f8275618b84c5fa157a314c345f))\n\n### Tests\n\n- **session:** Resolve incorrect assertion\n  ([0531220](https://github.com/ory/kratos/commit/05312203ab12eec44e59dcd9210160f2781a69b4))\n\n# [0.7.3-alpha.1](https://github.com/ory/kratos/compare/v0.7.1-alpha.1...v0.7.3-alpha.1) (2021-08-28)\n\nThis patch resolves a regression issue with Facebook login, a memory leak issue\nintroduced by an external dependency, adds a \"requires verification\" login hook,\nand improves performance for some endpoints.\n\nAlso, Ory Kratos SDKs are now published in individual\n[GitHub repositories for every language](https://github.com/ory?q=kratos-client).\n\n### Bug Fixes\n\n- Add new message when refresh parameter is true\n  ([#1560](https://github.com/ory/kratos/issues/1560))\n  ([0525623](https://github.com/ory/kratos/commit/05256232bf85d68e068eece6c883f46a447ba5bd)),\n  closes [#1117](https://github.com/ory/kratos/issues/1117)\n- Add session in spa registration if session cook is configured\n  ([#1657](https://github.com/ory/kratos/issues/1657))\n  ([639a7dd](https://github.com/ory/kratos/commit/639a7dd52d43c57e9708ed3e7360c17d6efde6a5)),\n  closes [#1604](https://github.com/ory/kratos/issues/1604)\n- **docs:** Ensure config reference is updated\n  ([f6b3aa4](https://github.com/ory/kratos/commit/f6b3aa45b1f39ca5e9ee7ef4cd96de1970b2ed71)),\n  closes [#1597](https://github.com/ory/kratos/issues/1597)\n- Facebook sign in regression\n  ([#1689](https://github.com/ory/kratos/issues/1689))\n  ([85337bf](https://github.com/ory/kratos/commit/85337bf65af767d7296b14e8fd21bab5c64d23e2)),\n  closes [#1687](https://github.com/ory/kratos/issues/1687)\n  [#1686](https://github.com/ory/kratos/issues/1686)\n- Http context memory leak\n  ([b21bd22](https://github.com/ory/kratos/commit/b21bd224059e8a42da9814237572a118297c5210)):\n\n  Ory Kratos was using `gorilla/sessions` prior to version v1.2 which had a\n  dependency on `gorilla/context`, a deprecated library with known memory\n  management issues. Even though we used `gorilla/context`'s clean up\n  middleware, it appears that `r.Context()` was not properly cleaned up, causing\n  memory leaks.\n\n  On average, the memory leak is pretty small, but depending on what gets added\n  to `r.Context()` it could significantly increase the memory leak.\n\n  By replacing `gorilla/sessions` with v1.2.1 we:\n  1. Increased the HTTP API throughput by an estimate of 4 times;\n  2. Brought average memory use back down to about 12MB;\n\n  Closes https://github.com/ory-corp/cloud/issues/1292\n\n- Outdated label ([#1681](https://github.com/ory/kratos/issues/1681))\n  ([149101e](https://github.com/ory/kratos/commit/149101ed145dae2b75e5150013efc478f5fd0cc3))\n- Register argon2 CLI commands properly\n  ([#1592](https://github.com/ory/kratos/issues/1592))\n  ([45c28d9](https://github.com/ory/kratos/commit/45c28d99064baf8051521a1078ac2b59bb3206ec))\n- Remove session cookie on logout\n  ([#1587](https://github.com/ory/kratos/issues/1587))\n  ([cdb30bb](https://github.com/ory/kratos/commit/cdb30bb65ac932a17e4924b4efc8952113452513)),\n  closes [#1584](https://github.com/ory/kratos/issues/1584):\n\n  Before, the logout endpoint would invalidate the session cookie, but not\n  remove it. This was a regression introduced in 0.7.0. This patch resolves that\n  issue.\n\n- **sdk:** Use proper annotation for genericError\n  ([#1611](https://github.com/ory/kratos/issues/1611))\n  ([da214b2](https://github.com/ory/kratos/commit/da214b2933ae2a91d8c5bf6aa8eea613a2078b9d)),\n  closes [#1609](https://github.com/ory/kratos/issues/1609)\n- Skip prompt on discord authorization by default\n  ([#1594](https://github.com/ory/kratos/issues/1594))\n  ([a667255](https://github.com/ory/kratos/commit/a6672554b02378eb2dac7b1af99ea2915395867b)):\n\n  When a value for prompt is not provided, Discord defaults to\n  `prompt=\"consent\"`. This change makes it so that if the request is not forced,\n  prompt is explicitly set to \"none\".\n\n- Static parameter for warning message in config.baseURL(...)\n  ([#1673](https://github.com/ory/kratos/issues/1673))\n  ([db54a1b](https://github.com/ory/kratos/commit/db54a1bd0c93d7a5845ee09d0a16cbc3b8f26a4a)),\n  closes [#1672](https://github.com/ory/kratos/issues/1672)\n- Update csrf token cookie name\n  ([#1601](https://github.com/ory/kratos/issues/1601))\n  ([64c90bf](https://github.com/ory/kratos/commit/64c90bf5e5cec6545a81f88ad5fabb29e9e80850)):\n\n  See https://github.com/ory-corp/cloud/issues/1252\n\n- Use eager preloading for list identites endpoint\n  ([#1588](https://github.com/ory/kratos/issues/1588))\n  ([de5fb3e](https://github.com/ory/kratos/commit/de5fb3e52af9f2d0f1209eed217403a5d7d1ae2d))\n\n### Code Generation\n\n- Pin v0.7.3-alpha.1 release commit\n  ([b5ad53e](https://github.com/ory/kratos/commit/b5ad53eca933438126eda3c6c647d99e05e37695))\n\n### Documentation\n\n- Change model to schema ([#1639](https://github.com/ory/kratos/issues/1639))\n  ([09c403e](https://github.com/ory/kratos/commit/09c403e55482e91a5bfe9a253e514b7a90826709))\n- Fix func naming for Logout flow\n  ([#1676](https://github.com/ory/kratos/issues/1676))\n  ([bbeb613](https://github.com/ory/kratos/commit/bbeb6132ba82e28057bc14bf35ea99b70f0c4118)):\n\n  rename createSelfServiceLogoutUrlForBrowsers to\n  createSelfServiceLogoutFlowUrlForBrowsers\n\n- Fix stub error example ([#1642](https://github.com/ory/kratos/issues/1642))\n  ([9bc2fd0](https://github.com/ory/kratos/commit/9bc2fd088ed9b3e7334713e63bae3c7bbcb922db)),\n  closes [#1568](https://github.com/ory/kratos/issues/1568)\n- Fixes incorrect yaml identation\n  ([#1641](https://github.com/ory/kratos/issues/1641))\n  ([6b58278](https://github.com/ory/kratos/commit/6b582784b49c1d103bbf7a6843cdf197fbd93931))\n- Identity traits are visible to user\n  ([#1621](https://github.com/ory/kratos/issues/1621))\n  ([641eba6](https://github.com/ory/kratos/commit/641eba675bdc583661565a6378776bfad26067c6))\n- Make qickstart URLs consistent (playground vs. localhost)\n  ([#1626](https://github.com/ory/kratos/issues/1626))\n  ([bae1847](https://github.com/ory/kratos/commit/bae1847eba0d925f28a010876e35e3c2093bc8c6)):\n\n  Since the quick-start describes how to run Kratos locally the actual location\n  of the redirect is `http://127.0.0.1:4433/self-service/login/browser`.\n\n- Update docker.md - Outdated information\n  ([#1627](https://github.com/ory/kratos/issues/1627))\n  ([dc32720](https://github.com/ory/kratos/commit/dc32720de25f52b7deb3e32f7530c7827a6ce5df)),\n  closes [#1619](https://github.com/ory/kratos/issues/1619):\n\n  Kratos does not automatically use a config file that exists at\n  `$HOME/.kratos.yaml`, or any other similar pattern. The documentation in the\n  Docker Images section of the guides could lead developers to believe that the\n  --config flag is unnecessary if they are binding the directory the\n  configuration file is in to $HOME or using a custom docker image to provide\n  the file.\n\n### Features\n\n- Allow multiple webhook body sources\n  ([#1606](https://github.com/ory/kratos/issues/1606))\n  ([51b1311](https://github.com/ory/kratos/commit/51b131177c9e0db018eced939fef43742c9e86cf)):\n\n  This patch adds support for loading webhooks from the local filesystem, base64\n  encoded inline string, and remote (http/https) sources. Please note that\n  support for relative/absolute paths without an URI scheme are deprecated and\n  will eventually be removed.\n\n- Require verified address ([#1355](https://github.com/ory/kratos/issues/1355))\n  ([1cf61cd](https://github.com/ory/kratos/commit/1cf61cdeedbd8bf5b66310793249681ff976baab)),\n  closes [#1328](https://github.com/ory/kratos/issues/1328)\n\n# [0.7.1-alpha.1](https://github.com/ory/kratos/compare/v0.7.0-alpha.1...v0.7.1-alpha.1) (2021-07-22)\n\nThis release addresses regressions introduced in Ory Kratos v0.7.0 and resolves\nsome bugs and documentation inconsistencies.\n\n### Bug Fixes\n\n- Automatic tagging for node ui\n  ([fe5056e](https://github.com/ory/kratos/commit/fe5056e11d1f8e4355cafa72ed1ff953077181cc)),\n  closes [#1537](https://github.com/ory/kratos/issues/1537)\n- Bump kratos ui image for quickstart\n  ([aedbb5a](https://github.com/ory/kratos/commit/aedbb5a259ea8ee63fb06c36fb1c7af78bb63ffc)),\n  closes [#1537](https://github.com/ory/kratos/issues/1537)\n- Cleanup lint errors and add doc to x\n  ([#1545](https://github.com/ory/kratos/issues/1545))\n  ([3cfd784](https://github.com/ory/kratos/commit/3cfd7845730685a4493c2b5d1974b79d873eea86))\n- Correct meta schema\n  ([8d4f3ff](https://github.com/ory/kratos/commit/8d4f3ff22d4ade6ae3f923c33303002e5f534cff))\n- Do not reset link method ([#1573](https://github.com/ory/kratos/issues/1573))\n  ([835fb31](https://github.com/ory/kratos/commit/835fb3127bc10b1642b4a7573722e5dce63fedc7))\n- Do not set csrf cookies on /sessions/whoami\n  ([#1580](https://github.com/ory/kratos/issues/1580))\n  ([36bbd43](https://github.com/ory/kratos/commit/36bbd434114d120006d49785787a3c94c7f103f9))\n- Export extensionschemas ([#1553](https://github.com/ory/kratos/issues/1553))\n  ([6af7638](https://github.com/ory/kratos/commit/6af76387caf37160ded75d83dc09ba0bc177a895))\n- Generate CSRF token on validation creation\n  ([#1549](https://github.com/ory/kratos/issues/1549))\n  ([6612c5f](https://github.com/ory/kratos/commit/6612c5f62e5cc242a808032def5714715ce49d11)),\n  closes [#1547](https://github.com/ory/kratos/issues/1547)\n- Identity extension meta schema\n  ([#1554](https://github.com/ory/kratos/issues/1554))\n  ([ba5ca64](https://github.com/ory/kratos/commit/ba5ca642d01917b43d49e009bf140ae13b4f1313)):\n\n  Up until now the extension meta schema was only applied to top level keys.\n  This fix now recursively checks the extension schema on any depth.\n\n- Remove domain alias config constraint\n  ([#1542](https://github.com/ory/kratos/issues/1542))\n  ([c6145db](https://github.com/ory/kratos/commit/c6145dbfb278369c8e3ad6eae7e8574ed49ba193))\n- Resolve wrong openapi types\n  ([b07927c](https://github.com/ory/kratos/commit/b07927cd23cbfce23f3b0676303a2d0ca564143b))\n- Update identity state openapi spec\n  ([0217737](https://github.com/ory/kratos/commit/0217737f5a2860e299ccec4387a2cc83aaac1557))\n- Use legacy ssl in quickstart config\n  ([6c13c2b](https://github.com/ory/kratos/commit/6c13c2bedd45c10713907e24976658d4a4b88de6)),\n  closes [#1569](https://github.com/ory/kratos/issues/1569)\n\n### Code Generation\n\n- Pin v0.7.1-alpha.1 release commit\n  ([4fe76af](https://github.com/ory/kratos/commit/4fe76af1302d45ddf4cf3c2c5949311c9cf1f8b8))\n\n### Documentation\n\n- Add instruction for creating user\n  ([#1541](https://github.com/ory/kratos/issues/1541))\n  ([c2a1b6d](https://github.com/ory/kratos/commit/c2a1b6df95bcb5dfe2b238be5903f483b9e701b5)),\n  closes [#1530](https://github.com/ory/kratos/issues/1530)\n- Clarify flags in schema which are not available in config file\n  ([e5ea5fe](https://github.com/ory/kratos/commit/e5ea5fee31eb2f70dc7c33565f791da9e2e87cc2)),\n  closes [#1514](https://github.com/ory/kratos/issues/1514)\n- Fix formatting of Email and Phone Verification Flow tab content\n  ([#1536](https://github.com/ory/kratos/issues/1536))\n  ([0bfac67](https://github.com/ory/kratos/commit/0bfac67a06ef0d96ffd6a487c90edb44d3a40710))\n- Fix typo ([#1543](https://github.com/ory/kratos/issues/1543))\n  ([b25bae7](https://github.com/ory/kratos/commit/b25bae7f2cdcbb60384808041744edd718a2a814))\n- Fix typo ([#1544](https://github.com/ory/kratos/issues/1544))\n  ([547788d](https://github.com/ory/kratos/commit/547788de74794a1dcf43e5190cdfc9d2e1a2dc92))\n- Update csrf pitfall flow section\n  ([#1558](https://github.com/ory/kratos/issues/1558))\n  ([cc7ed4b](https://github.com/ory/kratos/commit/cc7ed4b5f65d2971a45d5d0ec6188908d070d915)),\n  closes [#1557](https://github.com/ory/kratos/issues/1557)\n\n### Tests\n\n- Longer wait time for e2e boot\n  ([3a85a33](https://github.com/ory/kratos/commit/3a85a33ad8a8eec2ebf57d5a47937499141b6bc0))\n\n# [0.7.0-alpha.1](https://github.com/ory/kratos/compare/v0.6.3-alpha.1...v0.7.0-alpha.1) (2021-07-13)\n\nAbout two months ago we released Ory Kratos v0.6. Today, we are excited to\nannounce the next iteration of Ory Kratos v0.7! This release includes 215\ncommits from 24 contributors with over 770 files and more than 100.000 lines of\ncode changed!\n\nOry Kratos v0.7 brings massive developer experience improvements:\n\n- A reworked, tested, and standardized SDK based on OpenAPI 3.0.3\n  ([#1477](https://github.com/ory/kratos/pull/1477),\n  [#1424](https://github.com/ory/kratos/issues/1424));\n- Native support of Single-Page-Apps (ReactJS, AngularJS, ...) for all\n  self-service flows ([#1367](https://github.com/ory/kratos/pull/1367));\n- Sign in with Yandex, VK, Auth0, Slack;\n- An all-new, secure logout flow\n  ([#1433](https://github.com/ory/kratos/pull/1433));\n- Important security updates to the self-service GET APIs\n  ([#1458](https://github.com/ory/kratos/pull/1458),\n  [#1282](https://github.com/ory/kratos/issues/1282));\n- Built-in support for TLS ([#1466](https://github.com/ory/kratos/pull/1466));\n- Improved documentation and Go Module structure;\n- Resolving a case-sensitivity bug in self-service recovery and verification\n  flows;\n- Improved performance for listing identities;\n- Support for Instant tracing\n  ([#1429](https://github.com/ory/kratos/pull/1429));\n- Improved control for SMTPS, supporting SSL and STARTTLS\n  ([#1430](https://github.com/ory/kratos/pull/1430));\n- Ability to run Ory Kratos in networks without outbound requests\n  ([#1445](https://github.com/ory/kratos/pull/1445));\n- Improved control over HTTP Cookie behavior\n  ([#1531](https://github.com/ory/kratos/pull/1531));\n- Several smaller user experience improvements and bug fixes;\n- Improved e2e test pipeline.\n\nIn the next iteration of Ory Kratos, we will focus on providing a NextJS example\napplication for the SPA integration as well as the long-awaited MFA flows!\n\nPlease be aware that upgrading to Ory Kratos 0.7 requires you to apply SQL\nmigrations. Make sure to back up your database before migration!\n\nFor more details on breaking changes and patch notes, see below.\n\n## Breaking Changes\n\nPrior to this change it was not possible to specify the verification/recovery\nlink lifetime. Instead, it was bound to the flow expiry. This patch changes that\nand adds the ability to configure the lifespan of the link individually:\n\n```patch\n selfservice:\n   methods:\n     link:\n       enabled: true\n       config:\n+        # Defines how long a recovery link is valid for (default 1h)\n+        lifespan: 15m\n```\n\nThis is a breaking change because the link strategy no longer respects the\nrecovery / verification flow expiry time and, unless set, will default to one\nhour.\n\nThis change introduces a better SDK. As part of this change, several breaking\nchanges with regards to the SDK have been introduced. We recommend reading this\nsection carefully to understand the changes and how they might affect you.\n\nBefore, the SDK was structured into tags `public` and `admin`. This stems from\nthe fact that we have two ports in Ory Kratos - one administrative and one\npublic port.\n\nWhile serves as a good overview when working with Ory Kratos, it does not\nexpress:\n\n- What module the API belongs to (e.g. self-service, identity, ...)\n- What maturity the API has (e.g. experimental, alpha, beta, ...)\n- What version the API has (e.g. v0alpha0, v1beta0, ...)\n\nThis patch replaces the current `admin` and `public` tags with a versioned\napproach indicating the maturity of the API used. For example,\n`initializeSelfServiceSettingsForBrowsers` would no longer be under the `public`\ntag but instead under the `v0alpha1` tag:\n\n```patch\nimport {\n  Configuration,\n- PublicApi\n+ V0Alpha1\n} from '@ory/kratos-client';\n\n- const kratos = new PublicApi(new Configuration({ basePath: config.kratos.public }));\n+ const kratos = new V0Alpha1(new Configuration({ basePath: config.kratos.public }));\n```\n\nTo avoid confusion when setting up the SDK, and potentially using the wrong\nendpoints in your codebase and ending up with strange 404 errors, Ory Kratos now\nredirects you to the correct port, given that `serve.(public|admin).base_url`\nare configured correctly. This is a significant improvement towards a more\nrobust API experience!\n\nFurther, all administrative functions require, in the Ory SaaS, authorization\nusing e.g. an Ory Personal Access Token. In the open source, we do not know what\ndevelopers use to protect their APIs. As such, we believe that it is ok to have\nadmin and public functions under one common API and differentiate with an\n`admin` prefix. Therefore, the following patches should be made in your\ncodebase:\n\n```patch\nimport {\n- AdminApi,\n+ V0Alpha1,\n  Configuration\n} from '@ory/kratos-client';\n\n-const kratos = new AdminApi(new Configuration({ basePath: config.kratos.admin }));\n+const kratos = new V0Alpha1(new Configuration({ basePath: config.kratos.admin }));\n\n-kratos.createIdentity({\n+kratos.adminCreateIdentity({\n  schema_id: 'default',\n  traits: { /* ... */ }\n})\n```\n\nFurther, we have introduced a\n[style guide for writing SDKs annotations](https://www.ory.sh/docs/ecosystem/contributing#openapi-spec-and-go-swagger)\ngoverning how naming conventions should be chosen.\n\nWe also streamlined how credentials are used. We now differentiate between:\n\n- Per-request credentials such as the Ory Session Token / Cookie\n  ```\n  - public getSelfServiceRegistrationFlow(id: string, cookie?: string, options?: any) {}\n  + public getSelfServiceSettingsFlow(id: string, xSessionToken?: string, cookie?: string, options?: any) {}\n  ```\n- Global credentials such as the Ory (SaaS) Personal Access Token.\n\n  ```typescript\n  const kratos = new V0Alpha0(\n    new Configuration({\n      basePath: config.kratos.admin,\n      accessToken: \"some-token\",\n    }),\n  )\n\n  kratosAdmin.adminCreateIdentity({\n    schema_id: \"default\",\n    traits: {\n      /* ... */\n    },\n  })\n  ```\n\nWe hope you enjoy the vastly improved experience! There are still many things\nthat we want to iterate on. For full context, we recommend reading the proposal\nand discussion around these changes at\n[kratos#1424](https://github.com/ory/kratos/issues/1424).\n\nAdditionally, the Self-Service Error endpoint was updated. First, the endpoint\n`/self-service/errors` is now located at the public port only with the admin\nport redirecting to it. Second, the parameter `?error` was renamed to `?id` for\nbetter SDK compatibility. Parameter `?error` is still working but will be\ndeprecated at some point. Third, the response no longer contains an error array\nin `errors` but instead just a single error under `error`:\n\n```patch\n{\n  \"id\": \"60208346-3a61-4880-96ae-0419cde8fca8\",\n- \"errors\": [{\n+ \"error\": {\n    \"code\": 404,\n    \"status\": \"Not Found\",\n    \"reason\": \"foobar\",\n    \"message\": \"The requested resource could not be found\"\n- }],\n+ },\n  \"created_at\": \"2021-07-07T11:20:15.310506+02:00\",\n  \"updated_at\": \"2021-07-07T11:20:15.310506+02:00\"\n}\n```\n\nThis patch introduces CSRF countermeasures for fetching all self-service flows.\nThis ensures that users can not accidentally leak sensitive information when\ncopy/pasting e.g. login URLs (see #1282). If a self-service flow for browsers is\nrequested, the CSRF cookie must be included in the call, regardless if it is a\nclient-side browser app or a server-side browser app calling. This **does not\napply** for API-based flows.\n\nAs part of this change, the following endpoints have been removed:\n\n- `GET <ory-kratos-admin>/self-service/login/flows`;\n- `GET <ory-kratos-admin>/self-service/registration/flows`;\n- `GET <ory-kratos-admin>/self-service/verification/flows`;\n- `GET <ory-kratos-admin>/self-service/recovery/flows`;\n- `GET <ory-kratos-admin>/self-service/settings/flows`.\n\nPlease ensure that your server-side applications use the public port (e.g.\n`GET <ory-kratos-public>/self-service/login/flows`) for fetching self-service\nflows going forward.\n\nIf you use the SDKs, upgrading is easy by adding the `cookie` header when\nfetching the flows. This is only required when **using browser flows on the\nserver side**.\n\nThe following example illustrates a ExpressJS (NodeJS) server-side application\nfetching the self-service flows.\n\n```patch\napp.get('some-route', (req: Request, res: Response) => {\n-   kratos.getSelfServiceLoginFlow(flow).then((flow) => /* ... */ )\n+   kratos.getSelfServiceLoginFlow(flow, req.header('cookie')).then((flow) => /* ... */ )\n\n-   kratos.getSelfServiceRecoveryFlow(flow).then((flow) => /* ... */ )\n+   kratos.getSelfServiceRecoveryFlow(flow, req.header('cookie')).then((flow) => /* ... */ )\n\n-   kratos.getSelfServiceRegistrationFlow(flow).then((flow) => /* ... */ )\n+   kratos.getSelfServiceRegistrationFlow(flow, req.header('cookie')).then((flow) => /* ... */ )\n\n-   kratos.getSelfServiceVerificationFlow(flow).then((flow) => /* ... */ )\n+   kratos.getSelfServiceVerificationFlow(flow, req.header('cookie')).then((flow) => /* ... */ )\n\n-   kratos.getSelfServiceSettingsFlow(flow).then((flow) => /* ... */ )\n+   kratos.getSelfServiceSettingsFlow(flow, undefined, req.header('cookie')).then((flow) => /* ... */ )\n})\n```\n\nFor concrete details, check out\n[the changes in the NodeJS app](https://github.com/ory/kratos-selfservice-ui-node/commit/e7fa292968111e06401fcfc9b1dd0e8e285a4d87).\n\nThis patch refactors the logout functionality for browsers and APIs. It adds\nincreased security and DoS-defenses to the logout flow.\n\nPreviously, calling `GET /self-service/browser/flows/logout` would remove the\nsession cookie and redirect the user to the logout endpoint. Now you have to\nmake a call to `GET /self-service/logout/browser` which returns a JSON response\nincluding a `logout_url` URL to be used for logout. The call to\n`/self-service/logout/browser` must be made using AJAX with cookies enabled or\nby including the Ory Session Cookie in the `X-Session-Cookie` HTTP Header. You\nmay also use the SDK method `createSelfServiceLogoutUrlForBrowsers` to do that.\n\nAdditionally, the endpoint `DELETE /sessions` has been moved to\n`DELETE /self-service/logout/api`. Payloads and responses stay equal. The SDK\nmethod `revokeSession` has been renamed to\n`submitSelfServiceLogoutFlowWithoutBrowser`.\n\nWe listened to your feedback and have improved the naming of the SDK method\n`initializeSelfServiceRecoveryForNativeApps` to better match what it does:\n`initializeSelfServiceRecoveryWithoutBrowser`. As in the previous release you\nmay still use the old SDK if you do not want to deal with the SDK breaking\nchanges for now.\n\nWe listened to your feedback and have improved the naming of the SDK method\n`initializeSelfServiceVerificationForNativeApps` to better match what it does:\n`initializeSelfServiceVerificationWithoutBrowser`. As in the previous release\nyou may still use the old SDK if you do not want to deal with the SDK breaking\nchanges for now.\n\nWe listened to your feedback and have improved the naming of the SDK method\n`initializeSelfServiceSettingsForNativeApps` to better match what it does:\n`initializeSelfServiceSettingsWithoutBrowser`. As in the previous release you\nmay still use the old SDK if you do not want to deal with the SDK breaking\nchanges for now.\n\nWe listened to your feedback and have improved the naming of the SDK method\n`initializeSelfServiceregistrationForNativeApps` to better match what it does:\n`initializeSelfServiceregistrationWithoutBrowser`. As in the previous release\nyou may still use the old SDK if you do not want to deal with the SDK breaking\nchanges for now.\n\nWe listened to your feedback and have improved the naming of the SDK method\n`initializeSelfServiceLoginForNativeApps` to better match what it does:\n`initializeSelfServiceLoginWithoutBrowser`. As in the previous release you may\nstill use the old SDK if you do not want to deal with the SDK breaking changes\nfor now.\n\n### Bug Fixes\n\n- Add json detection to setting error subbranches\n  ([fb83dcb](https://github.com/ory/kratos/commit/fb83dcb8ae7463079ddb33c04673cf4556f6058c))\n- Add verification success message\n  ([#1526](https://github.com/ory/kratos/issues/1526))\n  ([126698c](https://github.com/ory/kratos/commit/126698c0b531ca304bb323c825cbeb86b5814f31)),\n  closes [#1450](https://github.com/ory/kratos/issues/1450)\n- Cache migration status\n  ([5be2f14](https://github.com/ory/kratos/commit/5be2f149cd79ddfbe8496eccf5d5aacb6a9a0b8e)),\n  closes [#1337](https://github.com/ory/kratos/issues/1337)\n- Change SMTP config validation from URI to a Regex pattern\n  ([#1436](https://github.com/ory/kratos/issues/1436))\n  ([5ab1e8f](https://github.com/ory/kratos/commit/5ab1e8f17bcbc229fada2c584b2c1f576b819761)),\n  closes [#1435](https://github.com/ory/kratos/issues/1435)\n- Check filesystem before fallback to bundled templates\n  ([#1401](https://github.com/ory/kratos/issues/1401))\n  ([22d999e](https://github.com/ory/kratos/commit/22d999e78eb4f67d2f3ba07e62fd28ffb3331d6d))\n- Continue button for oidc registration step\n  ([2aad5ac](https://github.com/ory/kratos/commit/2aad5ac8f7055f39f4f434d26fbca74cdbe75337)),\n  closes [#1422](https://github.com/ory/kratos/issues/1422)\n  [#1320](https://github.com/ory/kratos/issues/1320):\n\n  When signing up with an OIDC provider and the traits model is missing some\n  fields, the submit button shows all OIDC options. Instead, it should show just\n  one option called \"Continue\".\n\n- Deprecate sessionCookie ([#1428](https://github.com/ory/kratos/issues/1428))\n  ([eccad74](https://github.com/ory/kratos/commit/eccad741a1702181d4b207aad954a950906a808b)),\n  closes [#1426](https://github.com/ory/kratos/issues/1426)\n- Do not cache incomplete migrations\n  ([#1434](https://github.com/ory/kratos/issues/1434))\n  ([154c26f](https://github.com/ory/kratos/commit/154c26f6da4bb7040deabdc352c90cdae42c69fe))\n- Do not run network migrations when booting\n  ([12bbab9](https://github.com/ory/kratos/commit/12bbab9d3cf788998cd4a9be50ac8c7a9d2232bd)),\n  closes [#1399](https://github.com/ory/kratos/issues/1399)\n- Format test files\n  ([0468aa1](https://github.com/ory/kratos/commit/0468aa19ebfb0f68de5d9d1e59180d953f197cc0))\n- Improve identity list performance\n  ([f76886f](https://github.com/ory/kratos/commit/f76886fe7436f71fbef00081888a2f8d0106ba98)),\n  closes [#1412](https://github.com/ory/kratos/issues/1412)\n- Incorrect openapi specification for verification submission\n  ([#1431](https://github.com/ory/kratos/issues/1431))\n  ([ecb0a01](https://github.com/ory/kratos/commit/ecb0a01f61441aa97751943b5e9ddcc28f783d91)),\n  closes [#1368](https://github.com/ory/kratos/issues/1368)\n- Link t docker guide\n  ([953c6d6](https://github.com/ory/kratos/commit/953c6d60f6b6d82ac1406e84c2d87119e63dac48))\n- Mark ui node message as optional\n  ([#1365](https://github.com/ory/kratos/issues/1365))\n  ([7b8d59f](https://github.com/ory/kratos/commit/7b8d59f48ed14a6d0672238645d8675d4bf7fd77)),\n  closes [#1361](https://github.com/ory/kratos/issues/1361)\n  [#1362](https://github.com/ory/kratos/issues/1362)\n- Mark verified_at as omitempty\n  ([77b258e](https://github.com/ory/kratos/commit/77b258e57a3d53fe437838a5e9c57805e9c970aa)):\n\n  Closes https://github.com/ory/sdk/issues/46\n\n- Panic if contextualizer is not set\n  ([760035a](https://github.com/ory/kratos/commit/760035a6c5efa08561b93daff57ebb4655032b2a))\n- Panic on error in issue session\n  ([5fbd855](https://github.com/ory/kratos/commit/5fbd8557e1f907dd400bfcd26c187db16dc344ba)),\n  closes [#1384](https://github.com/ory/kratos/issues/1384)\n- Prometheus metrics fix ([#1299](https://github.com/ory/kratos/issues/1299))\n  ([ac5d00d](https://github.com/ory/kratos/commit/ac5d00d472a87ab51e7c6834e2cb59f107fc3b3b))\n- Recovery email case sensitive\n  ([#1357](https://github.com/ory/kratos/issues/1357))\n  ([bce14c4](https://github.com/ory/kratos/commit/bce14c487450bd668859f362b98704644fa4c72a)),\n  closes [#1329](https://github.com/ory/kratos/issues/1329)\n- Remove changelog\n  ([7affb7a](https://github.com/ory/kratos/commit/7affb7a25bc84082e0ad8096e6c0e4b3933ac5f6))\n- Remove obsolete ADD for corp module\n  ([#1455](https://github.com/ory/kratos/issues/1455))\n  ([0fa3a53](https://github.com/ory/kratos/commit/0fa3a539fbe1ae498434b200c3b636de10d73a7c))\n- Remove typing from node.attribute.value\n  ([63a5e08](https://github.com/ory/kratos/commit/63a5e08afab76dafbfe13e6126e165af28492aad)):\n\n  Closes https://github.com/ory/sdk/issues/75 Closes\n  https://github.com/ory/sdk/issues/74 Closes\n  https://github.com/ory/sdk/issues/72\n\n- Rename client package for external consumption\n  ([cba8b00](https://github.com/ory/kratos/commit/cba8b00c8b755cc0bdc7818bc9d7390ff3532ce1))\n- Resolve build issues on release\n  ([7c265a8](https://github.com/ory/kratos/commit/7c265a8b909dcc07ceeeda546a748ad28ab0c746))\n- Resolve driver issues\n  ([47b1c8d](https://github.com/ory/kratos/commit/47b1c8dce57a023e89a2b178bc8a033496ef4ff2))\n- Resolve network regression\n  ([8f96b1f](https://github.com/ory/kratos/commit/8f96b1fe4d0846a3ad97a45bc972ece04109289d))\n- Resolve network regressions\n  ([8fc52c0](https://github.com/ory/kratos/commit/8fc52c034ed9978c2a04cc66bccc9b795c9bbefa))\n- Testhelper regressions\n  ([bf3b04f](https://github.com/ory/kratos/commit/bf3b04fd2c7f9162073cb584d6fb0d59e868ecbf))\n- Use correct url in submitSelfServiceVerificationFlow\n  ([ab8a600](https://github.com/ory/kratos/commit/ab8a600080ac0d6a6235806b74c5b9e3dc1c2d60))\n- Use local schema URL for sorting UI nodes\n  ([#1449](https://github.com/ory/kratos/issues/1449))\n  ([a003885](https://github.com/ory/kratos/commit/a0038853f30cd7d139d42d1d4601c8cf49d03934))\n- Use session cookie path settings for csrf cookie\n  ([#1493](https://github.com/ory/kratos/issues/1493))\n  ([c6d08ed](https://github.com/ory/kratos/commit/c6d08edae32fd94877fb58355d3c711460c7d1a2)),\n  closes [#1292](https://github.com/ory/kratos/issues/1292):\n\n  This PR adds configuration option for CSRF cookies and improves the domain\n  alias logic as well as adding tests for it.\n\n- Use STARTTLS for smtps connections\n  ([#1430](https://github.com/ory/kratos/issues/1430))\n  ([c21bb80](https://github.com/ory/kratos/commit/c21bb80a749df7b224a8ac3f15fa62523a78d805)),\n  closes [#781](https://github.com/ory/kratos/issues/781)\n- Version schema ([#1359](https://github.com/ory/kratos/issues/1359))\n  ([8c4bac7](https://github.com/ory/kratos/commit/8c4bac71674e45e440d916c6c947ed018a8ea29a)),\n  closes [#1331](https://github.com/ory/kratos/issues/1331)\n  [#1101](https://github.com/ory/kratos/issues/1101)\n  [ory/hydra#2427](https://github.com/ory/hydra/issues/2427)\n\n### Code Generation\n\n- Pin v0.7.0-alpha.1 release commit\n  ([53a0e38](https://github.com/ory/kratos/commit/53a0e38c2b5d7003786a8386a9c4cf129acc06aa))\n\n### Code Refactoring\n\n- Corp package ([#1402](https://github.com/ory/kratos/issues/1402))\n  ([0202dc5](https://github.com/ory/kratos/commit/0202dc57aacc0d48e4c1ee4e68c91654451f63fa))\n- Finalize SDK refactoring\n  ([e772641](https://github.com/ory/kratos/commit/e772641f9bcfa462aa5111cf1329a479e3cdff99)),\n  closes [#1424](https://github.com/ory/kratos/issues/1424)\n- Identity SDKs\n  ([d8658dc](https://github.com/ory/kratos/commit/d8658dc887a76d82e3cf23386c03b5ebf7053189)),\n  closes [#1477](https://github.com/ory/kratos/issues/1477)\n- Improve session sdk\n  ([7207af4](https://github.com/ory/kratos/commit/7207af4cdf6c78dd3f0fd42b6727d7e320d252e6))\n- Introduce DefaultContextualizer in corp package\n  ([#1390](https://github.com/ory/kratos/issues/1390))\n  ([944d045](https://github.com/ory/kratos/commit/944d045aa7fc59eadfdd18951f0d4937b1ea79df)),\n  closes [#1363](https://github.com/ory/kratos/issues/1363)\n- Move cleansql to separate package\n  ([7c203dc](https://github.com/ory/kratos/commit/7c203dc8219afe07f180143f832158615b51f60a))\n- Openapi.json -> api.json\n  ([6df0de5](https://github.com/ory/kratos/commit/6df0de5d0b4c952576bf9e14c18d521934edd9bb))\n- Self-service error APIs\n  ([65c482f](https://github.com/ory/kratos/commit/65c482fba62c2782b03a3b840124eac062499266))\n\n### Documentation\n\n- Add docs for registration SPA flow\n  ([84458f1](https://github.com/ory/kratos/commit/84458f1a9dfe8be6a97bddd832fcc508b60b8498))\n- Add go sdk examples\n  ([e948fad](https://github.com/ory/kratos/commit/e948faddce3a1f52df964c701f6ba2a28f5dfe03))\n- Add kratos quickstart config notes\n  ([#1490](https://github.com/ory/kratos/issues/1490))\n  ([2f8094c](https://github.com/ory/kratos/commit/2f8094c50eaf7e1cd964067172adcad407713764))\n- Add replit instructions\n  ([8ab8607](https://github.com/ory/kratos/commit/8ab8607dee433f6e708ade296a6c26d0a87d0aae))\n- Add tested and running go sdk examples\n  ([3b56bb5](https://github.com/ory/kratos/commit/3b56bb5fd37d0e7d4479967aa0b5721a68a267f2))\n- Correct CII badge ([#1447](https://github.com/ory/kratos/issues/1447))\n  ([048aec3](https://github.com/ory/kratos/commit/048aec39295f0a3534df5e43e3cd7684d4fbd758))\n- Fix broken link\n  ([9eaf764](https://github.com/ory/kratos/commit/9eaf764b28f3ca1dae2816d4c0a985c4866c409b))\n- Fix building from source ([#1473](https://github.com/ory/kratos/issues/1473))\n  ([af54d5b](https://github.com/ory/kratos/commit/af54d5bb9e36f90d272d293817f0d6d7eb2e79a8))\n- Fix typo in \"Sign in/up with ID & assword\"\n  ([#1383](https://github.com/ory/kratos/issues/1383))\n  ([f39739d](https://github.com/ory/kratos/commit/f39739d94e97f20b94630b957371d11294dc8300))\n- Mark login endpoints as experimental\n  ([6faf0f6](https://github.com/ory/kratos/commit/6faf0f65bb05bbafdee6b1274a719695fd5b4173))\n- Refactor documentation and adopt changes for\n  [#1477](https://github.com/ory/kratos/issues/1477)\n  ([f5e96cd](https://github.com/ory/kratos/commit/f5e96cd5054e734c319ed32992357fcd73ac44a1)),\n  closes [#1472](https://github.com/ory/kratos/issues/1472)\n- Remove changelog from docs folder\n  ([5a7e3d8](https://github.com/ory/kratos/commit/5a7e3d83a5fb7f3e6945f37d42abca14d2982e72))\n- Resolve build issues\n  ([b51bb55](https://github.com/ory/kratos/commit/b51bb555d829ab020e593a764cbce4c5ba4885a2))\n- Resolve typos and docs react issues\n  ([2d640e4](https://github.com/ory/kratos/commit/2d640e4b9b556fd866c29c83564cb1c7702ab9ff))\n- Update docs for all flows\n  ([d29ea69](https://github.com/ory/kratos/commit/d29ea69f6bb908b529502030942b1ced52227372))\n- Update documentation for plaintext templates\n  ([#1369](https://github.com/ory/kratos/issues/1369))\n  ([419784d](https://github.com/ory/kratos/commit/419784dd0d4ddc338830ed0d77a7d99f8f440777)),\n  closes [#1351](https://github.com/ory/kratos/issues/1351)\n- Update error documentation\n  ([7d83609](https://github.com/ory/kratos/commit/7d8360973a3359bec321a60f4f3a4202ac7d2430))\n- Update login flow documentation\n  ([a27de91](https://github.com/ory/kratos/commit/a27de91e9e06f8501ae9cb70446ed0aae5a39f71))\n- Update path\n  ([f0384d9](https://github.com/ory/kratos/commit/f0384d9c11085230fd16290c524d22fac6002870))\n- Update README.md Go instructions\n  ([#1464](https://github.com/ory/kratos/issues/1464))\n  ([8db4b4a](https://github.com/ory/kratos/commit/8db4b4a966c5c418cf9d9169b66d7dacff256113))\n- Update remaining self service documentation\n  ([bcc6284](https://github.com/ory/kratos/commit/bcc62846297a67216e01e8c31d375d376c1b7cef))\n- Update sdk use\n  ([bcb8c06](https://github.com/ory/kratos/commit/bcb8c06ee324c639e548fc06315d9e952f470582))\n- Update settings documentation\n  ([258ceaf](https://github.com/ory/kratos/commit/258ceaf84e6ee15b8eee2f203f456f73e7d406d5))\n- Use correct path ([#1333](https://github.com/ory/kratos/issues/1333))\n  ([e401135](https://github.com/ory/kratos/commit/e401135cf415d7e3e6a8ca463dd47e46fe399b33))\n\n### Features\n\n- Add examples for usage of go sdk\n  ([870c2bd](https://github.com/ory/kratos/commit/870c2bd316a3e5b7ce9d526ebf369e41dbea2630))\n- Add GetContextualizer\n  ([ac32717](https://github.com/ory/kratos/commit/ac3271742c9c2b968b08dd2b35a5d120c5befcd9))\n- Add helper for starting kratos e2e\n  ([#1469](https://github.com/ory/kratos/issues/1469))\n  ([b9c7674](https://github.com/ory/kratos/commit/b9c7674c30df8200bcd7223c2fa6b058e833bb8a))\n- Add instana as possible tracing provider\n  ([#1429](https://github.com/ory/kratos/issues/1429))\n  ([abe48a9](https://github.com/ory/kratos/commit/abe48a97ee75567979a70f00dd73ff698efcc75d)),\n  closes [#1385](https://github.com/ory/kratos/issues/1385)\n- Add redoc ([#1502](https://github.com/ory/kratos/issues/1502))\n  ([492266d](https://github.com/ory/kratos/commit/492266de9c9b7b775a7b21b5890361380d911da4))\n- Add vk and yandex providers to oidc providers and documentation\n  ([#1339](https://github.com/ory/kratos/issues/1339))\n  ([22a3ef9](https://github.com/ory/kratos/commit/22a3ef98181eb5922cc0f1c016d42ce46732d0a2)),\n  closes [#1234](https://github.com/ory/kratos/issues/1234)\n- Anti-CSRF measures when fetching flows\n  ([#1458](https://github.com/ory/kratos/issues/1458))\n  ([5171557](https://github.com/ory/kratos/commit/51715572ea08f654d1e97d760b9c3d3a9113aa3d)),\n  closes [#1282](https://github.com/ory/kratos/issues/1282)\n- Configurable recovery/verification link lifetime\n  ([f80d4e3](https://github.com/ory/kratos/commit/f80d4e3bf7df603b73589dbc6805c69d049921e0))\n- Disable HaveIBeenPwned validation when HaveIBeenPwnedEnabled is set to false\n  ([#1445](https://github.com/ory/kratos/issues/1445))\n  ([44002f4](https://github.com/ory/kratos/commit/44002f4fa93b40a6bb18f1e759bb416d082cec08)),\n  closes [#316](https://github.com/ory/kratos/issues/316):\n\n  This patch introduces an option to disable HaveIBeenPwned checks in\n  environments where outbound network calls are disabled.\n\n- **identities:** Add a state to identities\n  ([#1312](https://github.com/ory/kratos/issues/1312))\n  ([d22954e](https://github.com/ory/kratos/commit/d22954e2fdb7b2dd5206651b6dd5cf96185a33ba)),\n  closes [#598](https://github.com/ory/kratos/issues/598)\n- Improve contextualization in serve/daemon\n  ([f83cd35](https://github.com/ory/kratos/commit/f83cd355422fb4b422f703406473bda914d8419c))\n- Include Credentials Metadata in admin api\n  ([#1274](https://github.com/ory/kratos/issues/1274))\n  ([c8b6219](https://github.com/ory/kratos/commit/c8b62190fca53db4e1b3a4ddb5253fbd2fd46002)),\n  closes [#820](https://github.com/ory/kratos/issues/820)\n- Include Credentials Metadata in admin api Missing changes in handler\n  ([#1366](https://github.com/ory/kratos/issues/1366))\n  ([a71c220](https://github.com/ory/kratos/commit/a71c2208dedac45d32dab578e62a5e3105c8dee0))\n- Natively support SPA for login flows\n  ([6ff67af](https://github.com/ory/kratos/commit/6ff67afa8b0fc0a95cec44d3dda2cbc1987b51dd)),\n  closes [#1138](https://github.com/ory/kratos/issues/1138)\n  [#668](https://github.com/ory/kratos/issues/668):\n\n  This patch adds the long-awaited capabilities for natively working with SPAs\n  and AJAX requests. Previously, requests to the `/self-service/login/browser`\n  endpoint would always end up in a redirect. Now, if the `Accept` header is set\n  to `application/json`, the login flow will be returned as JSON instead.\n  Accordingly, changes to the error and submission flow have been made to\n  support `application/json` content types and SPA / AJAX requests.\n\n- Natively support SPA for recovery flows\n  ([5461244](https://github.com/ory/kratos/commit/5461244943286081e13c304a3b38413b8ee6fdf2)):\n\n  This patch adds the long-awaited capabilities for natively working with SPAs\n  and AJAX requests. Previously, requests to the\n  `/self-service/recovery/browser` endpoint would always end up in a redirect.\n  Now, if the `Accept` header is set to `application/json`, the registration\n  flow will be returned as JSON instead. Accordingly, changes to the error and\n  submission flow have been made to support `application/json` content types and\n  SPA / AJAX requests.\n\n- Natively support SPA for registration flows\n  ([57d3c57](https://github.com/ory/kratos/commit/57d3c5786a88f0648e7fa57f181f060a057ec19f)),\n  closes [#1138](https://github.com/ory/kratos/issues/1138)\n  [#668](https://github.com/ory/kratos/issues/668):\n\n  This patch adds the long-awaited capabilities for natively working with SPAs\n  and AJAX requests. Previously, requests to the\n  `/self-service/registration/browser` endpoint would always end up in a\n  redirect. Now, if the `Accept` header is set to `application/json`, the\n  registration flow will be returned as JSON instead. Accordingly, changes to\n  the error and submission flow have been made to support `application/json`\n  content types and SPA / AJAX requests.\n\n- Natively support SPA for settings flows\n  ([ea4395e](https://github.com/ory/kratos/commit/ea4395ed25d5668e4ce365336cd7a5e13e0ba1cc)):\n\n  This patch adds the long-awaited capabilities for natively working with SPAs\n  and AJAX requests. Previously, requests to the\n  `/self-service/settings/browser` endpoint would always end up in a redirect.\n  Now, if the `Accept` header is set to `application/json`, the registration\n  flow will be returned as JSON instead. Accordingly, changes to the error and\n  submission flow have been made to support `application/json` content types and\n  SPA / AJAX requests.\n\n- Natively support SPA for verification flows\n  ([c151500](https://github.com/ory/kratos/commit/c1515009dcd1b5946a93733feedb01753de91c3d)):\n\n  This patch adds the long-awaited capabilities for natively working with SPAs\n  and AJAX requests. Previously, requests to the\n  `/self-service/verification/browser` endpoint would always end up in a\n  redirect. Now, if the `Accept` header is set to `application/json`, the\n  registration flow will be returned as JSON instead. Accordingly, changes to\n  the error and submission flow have been made to support `application/json`\n  content types and SPA / AJAX requests.\n\n- Protect logout against CSRF\n  ([#1433](https://github.com/ory/kratos/issues/1433))\n  ([1a7a74c](https://github.com/ory/kratos/commit/1a7a74c3fe425f139a87bb68fbc07f8862c00e58)),\n  closes [#142](https://github.com/ory/kratos/issues/142)\n- Sign in with Auth0 ([#1352](https://github.com/ory/kratos/issues/1352))\n  ([f618a53](https://github.com/ory/kratos/commit/f618a53fb971ad16121aa8728cfec54253bb3f44)),\n  closes [#609](https://github.com/ory/kratos/issues/609)\n- Support api in settings error\n  ([23105db](https://github.com/ory/kratos/commit/23105dbb836d920b8766536b65de58932f53d6f6))\n- Support reading session token from X-Session-Token HTTP header\n  ([dcaefd9](https://github.com/ory/kratos/commit/dcaefd94a0b2cf819424f2e10b3bdae63b256726))\n- Team id in slack oidc ([#1409](https://github.com/ory/kratos/issues/1409))\n  ([e4d021a](https://github.com/ory/kratos/commit/e4d021a037a6b44f8bd66372e9c260c640e87b9d)),\n  closes [#1408](https://github.com/ory/kratos/issues/1408)\n- TLS support for public and admin endpoints\n  ([#1466](https://github.com/ory/kratos/issues/1466))\n  ([7f44f81](https://github.com/ory/kratos/commit/7f44f819a5989a699e403e02c69541369573078f)),\n  closes [#791](https://github.com/ory/kratos/issues/791)\n- Update openapi specs and regenerate\n  ([cac507e](https://github.com/ory/kratos/commit/cac507eb5b1f39d003d72e57912dbbfe6f92deb1))\n\n### Tests\n\n- Add tests for cookie behavior of API and browser endpoints\n  ([d1b1521](https://github.com/ory/kratos/commit/d1b15217867cfb92a615c793b26fad288f5e5742))\n- **e2e:** Greatly improve test performance\n  ([#1421](https://github.com/ory/kratos/issues/1421))\n  ([2ffad9e](https://github.com/ory/kratos/commit/2ffad9ee751471451e2151719a2e70d5f89437b0)):\n\n  Instead of running the individual profiles as separate Cypress instances, we\n  now use one singular instance which updates the Ory Kratos configuration\n  depending on the test context. This ensures that hot-reloading is properly\n  working while also signficantly reducing the amount of time spent on booting\n  up the service dependencies.\n\n- **e2e:** Resolve flaky test issues related to timeouts and speed\n  ([b083791](https://github.com/ory/kratos/commit/b083791858bc26a02250d7f5a4e8883cd7392a58))\n- **e2e:** Resolve recovery regression\n  ([72c47d6](https://github.com/ory/kratos/commit/72c47d65415efbb53d5d680bd9d78156d577b67f))\n- **e2e:** Resolve test config regressions\n  ([eb9c4f9](https://github.com/ory/kratos/commit/eb9c4f98f2e30ac420ed1e3f18a3f0d9ff23846e))\n- Remove obsolete console.log\n  ([3ecc869](https://github.com/ory/kratos/commit/3ecc869ebfef5c97334ae4334fb4af98ca9baf97))\n- Resolve e2e regressions\n  ([b0d3b82](https://github.com/ory/kratos/commit/b0d3b82f301942bebe3c0027c8b3160749f907af))\n- Resolve migratest panic\n  ([89d05ae](https://github.com/ory/kratos/commit/89d05ae0c376c4ea1f23708cccf95c9754a29c94))\n- Resolve mobile regressions\n  ([868e82e](https://github.com/ory/kratos/commit/868e82e3d7aec4cde80d7c1d0ce4601e40695f27))\n- Resolve oidc regressions\n  ([2403082](https://github.com/ory/kratos/commit/2403082701ac5d667706afd893a6d406496f67fa))\n\n### Unclassified\n\n- add CoC shield (#1439)\n  ([826ed1a](https://github.com/ory/kratos/commit/826ed1a6deafdc2631a5c72f0bfacc91b06a3435)),\n  closes [#1439](https://github.com/ory/kratos/issues/1439)\n- u\n  ([b03549b](https://github.com/ory/kratos/commit/b03549b6340ec0bf4f9d741ce145ca90bbc09968))\n- u\n  ([318a31d](https://github.com/ory/kratos/commit/318a31d400b97653b4f377c67df4ae0afea189d9))\n- Format\n  ([eca7aff](https://github.com/ory/kratos/commit/eca7aff2be96c673dd6be5dc36ab1f4850cc44f0))\n- Format\n  ([5cc9fc3](https://github.com/ory/kratos/commit/5cc9fc3a6e91a96225d016d60c8da5cef647ac18))\n- Format\n  ([e525805](https://github.com/ory/kratos/commit/e525805246431075d26c3f47596ae93f6580d8ee))\n- Format\n  ([4a692ac](https://github.com/ory/kratos/commit/4a692acc7db160068ed7d81461b173bc957e4736))\n- Format\n  ([169c0cd](https://github.com/ory/kratos/commit/169c0cd8d424babef69a52ddf65e2b75ded09a46))\n\n# [0.6.3-alpha.1](https://github.com/ory/kratos/compare/v0.6.2-alpha.1...v0.6.3-alpha.1) (2021-05-17)\n\nThis release addresses some minor bugs and improves the SDK experience. Please\nbe aware that the Ory Kratos SDK v0.6.3+ have breaking changes compared to Ory\nKratos SDK v0.6.2. If you do not wish to update your code, you can keep using\nthe Ory Kratos v0.6.2 SDK and upgrade to v0.6.3+ SDKs at a later stage, as only\nnaming conventions have changed!\n\n## Breaking Changes\n\nUnfortunately, some method signatures have changed in the SDKs. Below is a list\nof changed entries:\n\n- Error `genericError` was renamed to `jsonError` and now includes more\n  information and better typing for errors;\n- The following functions have been renamed:\n  - `initializeSelfServiceLoginViaAPIFlow` ->\n    `initializeSelfServiceLoginForNativeApps`\n  - `initializeSelfServiceLoginViaBrowserFlow` ->\n    `initializeSelfServiceLoginForBrowsers`\n  - `initializeSelfServiceRegistrationViaAPIFlow` ->\n    `initializeSelfServiceRegistrationForNativeApps`\n  - `initializeSelfServiceRegistrationViaBrowserFlow` ->\n    `initializeSelfServiceRegistrationForBrowsers`\n  - `initializeSelfServiceSettingsViaAPIFlow` ->\n    `initializeSelfServiceSettingsForNativeApps`\n  - `initializeSelfServiceSettingsViaBrowserFlow` ->\n    `initializeSelfServiceSettingsForBrowsers`\n  - `initializeSelfServiceRecoveryViaAPIFlow` ->\n    `initializeSelfServiceRecoveryForNativeApps`\n  - `initializeSelfServiceRecoveryViaBrowserFlow` ->\n    `initializeSelfServiceRecoveryForBrowsers`\n  - `initializeSelfServiceVerificationViaAPIFlow` ->\n    `initializeSelfServiceVerificationForNativeApps`\n  - `initializeSelfServiceVerificationViaBrowserFlow` ->\n    `initializeSelfServiceVerificationForBrowsers`\n- Some type names have changed, for example `traits` -> `identityTraits`.\n\n### Bug Fixes\n\n- Improve settings oas definition\n  ([867abfc](https://github.com/ory/kratos/commit/867abfc813b08142786f71bfe28e373d4754c959))\n- Properly handle CSRF for API flows in recovery and verification strategies\n  ([461c829](https://github.com/ory/kratos/commit/461c829dc4d7f7b70620abee2263efba78ce463a)),\n  closes [#1141](https://github.com/ory/kratos/issues/1141)\n- **session:** Use specific headers before bearer use\n  ([82c0b54](https://github.com/ory/kratos/commit/82c0b545b29b30fcf3521d9621ec5c5f1a23dc96))\n- Use correct api spec path\n  ([5f41f87](https://github.com/ory/kratos/commit/5f41f87bea2919cdf4e9f55c6ad938c5bc08b619))\n- Use correct openapi path for validation\n  ([#1340](https://github.com/ory/kratos/issues/1340))\n  ([a0f5673](https://github.com/ory/kratos/commit/a0f5673d6aa4e60bab06ef699dce231f0bf4aeff))\n\n### Code Generation\n\n- Pin v0.6.3-alpha.1 release commit\n  ([5edf952](https://github.com/ory/kratos/commit/5edf9524d812795ac5712e4a9541b34359234724))\n\n### Code Refactoring\n\n- Improve SDK experience\n  ([71b8511](https://github.com/ory/kratos/commit/71b8511ae1f6f77b2996a01a55accc99d171cfaf)):\n\n  This patch resolves UX issues in the auto-generated SDKs by using consistent\n  naming and introducing a test suite for the Ory SaaS.\n\n# [0.6.2-alpha.1](https://github.com/ory/kratos/compare/v0.6.1-alpha.1...v0.6.2-alpha.1) (2021-05-14)\n\nResolves an issue in the Go SDK.\n\n### Code Generation\n\n- Pin v0.6.2-alpha.1 release commit\n  ([99c1b1d](https://github.com/ory/kratos/commit/99c1b1d674df3bd8263f7cbf1ed2bdfae6281f69))\n\n### Documentation\n\n- Update link to example email template.\n  ([#1326](https://github.com/ory/kratos/issues/1326))\n  ([28a1723](https://github.com/ory/kratos/commit/28a17234b557cabf17b592ee68041aec695f6d20))\n\n# [0.6.1-alpha.1](https://github.com/ory/kratos/compare/v0.6.0-alpha.2...v0.6.1-alpha.1) (2021-05-11)\n\nThis release primarily addresses issues in the SDK CI pipeline.\n\n### Code Generation\n\n- Pin v0.6.1-alpha.1 release commit\n  ([1df82da](https://github.com/ory/kratos/commit/1df82daaf3f9cfd3a470d7c9bf8d96abbd52b872))\n\n### Features\n\n- Allow changing password validation API DNS name\n  ([#1009](https://github.com/ory/kratos/issues/1009))\n  ([ced85e8](https://github.com/ory/kratos/commit/ced85e8091b06d864cc55c9975f8b006f6be1ce4))\n\n# [0.6.0-alpha.2](https://github.com/ory/kratos/compare/v0.6.0-alpha.1...v0.6.0-alpha.2) (2021-05-07)\n\nThis release addresses issues with the SDK pipeline and also closes a bug\nrelated to email sending.\n\n### Bug Fixes\n\n- Update node image\n  ([eef307e](https://github.com/ory/kratos/commit/eef307e6bc33c9ec36ed9138f99c19f72c7be575))\n\n### Code Generation\n\n- Pin v0.6.0-alpha.2 release commit\n  ([a3658ba](https://github.com/ory/kratos/commit/a3658badb848656b61d54b3ee35114972afc1f35))\n\n### Features\n\n- Fix unexpected emails when update profile\n  ([#1300](https://github.com/ory/kratos/issues/1300))\n  ([7b24485](https://github.com/ory/kratos/commit/7b2448566f82e69d555997654ee410f9b4ff3939)),\n  closes [#1221](https://github.com/ory/kratos/issues/1221)\n\n# [0.6.0-alpha.1](https://github.com/ory/kratos/compare/v0.5.5-alpha.1...v0.6.0-alpha.1) (2021-05-05)\n\nToday Ory Kratos v0.6 has been released! We are extremely happy with this\nrelease where we made many changes that pave the path for exciting future\nadditions such as integrating 2FA more easily! We would like to thank the\nawesome community for the many contributions.\n\nKratos v0.6 includes an insane amount of work spread over the last five months -\n480 commits and over 4200 files changed. The team at Ory would like to thank all\nthe amazing contributors that made this release possible!\n\nHere is a summary of the most important changes:\n\n- Ory Kratos now support highly customizable web hooks - contributed by\n  [@dadrus](https://github.com/dadrus) and\n  [@martinei](https://github.com/martinei);\n- Ory Kratos Courier can now be run as a standalone task using\n  `kratos courier watch -c your/config.yaml`. To use the mail courier as a\n  background task of the server run `kratos serve --watch-courier` - contributed\n  by [@mattbonnell](https://github.com/mattbonnell);\n- Reworked migrations to ensure stable migrations in production systems -\n  backward compatibility is ensured and tested;\n- Upgraded to Go 1.16 and removed all static file packers, greatly improving\n  build time;\n- Refactored our SDK pipeline from Swagger 2.0 to OpenAPI Spec 3.0. Ory's SDKs\n  are now properly typed and bugs can easily be addressed using a patch process.\n  Due to this, we had to move away from go-swagger client generation for the Go\n  SDK and replace it with openapi-generator. This, unfortunately, introduced\n  breaking changes in the Go SDK APIs. If you have problems migrating, or have a\n  tutorial on how to migrate, please share it with the community on GitHub!\n- Created reliable health and status checks by ensuring that e.g. migrations\n  have completed;\n- Made resilient CLI client commands e.g. kratos identities list;\n- Better support for cookies in multi-domain setups called\n  [domain aliasing](https://www.ory.sh/kratos/docs/guides/configuring-cookies);\n- A new, [dynamically generated FAQ](https://www.ory.sh/kratos/docs/next/faq);\n- Enhanced GitHub and Google claims parsing;\n- Faster and more resilient CI/CD pipeline;\n- Improvements for running Ory Kratos in secure Kubernetes environments;\n- Better Helm Charts for Ory Kratos;\n- Support for BCrypt hashing, which is now the default hashing implementation.\n  Existing Argon2id hashes will be automatically translated to BCrypt hashes\n  when the user signs in the next time. We recommend using Argon2id in use cases\n  where password hashing is required to take at least 2 seconds. For regular web\n  workloads (200ms) BCrypt is recommended - contributed by\n  [@seremenko-wish](https://github.com/seremenko-wish);\n- The Argon2 memory configuration is now human readable:\n  `hashers.argon2.memory: 131072` -> `hashers.argon2.memory: 131072B` (supports\n  kb, mb, kib, mib, ...).\n- Add possibility to keep track of the return_to URLs for verification_flows\n  after sign up using the new `after_verification_return_to` query parameter\n  (e.g.\n  `http://foo.com/registration?after_verification_return_to=verification_callback`) -\n  contributed by [@mattbonnell](https://github.com/mattbonnell);\n- Emails are now populated at delivery time, offering more flexibility in terms\n  of templating;\n- Emails contain a plaintext variant for email clients that do not display HTML\n  emails - contributed by [@mattbonnell](https://github.com/mattbonnell);\n- Mitigation for password hash timing attacks by adding a random delay to login\n  attempts where the user does not exist;\n- Resolving SDKs issues for whoami requests;\n- Simplified database schema for faster processing, significantly reducing the\n  amount of data stored and latency as several JOINS have been removed;\n- Support for binding the HTTP server on UNIX sockets - contributed by\n  [@sloonz](https://github.com/sloonz);\n\nThere are even more contributions by [@NickUfer](https://github.com/NickUfer)\nand [harnash](https://github.com/harnash). In total,\n[33 people contributed to this release](https://github.com/ory/kratos/graphs/contributors?from=2020-12-09&to=2021-05-04&type=c)!\nThank you all!\n\n_IMPORTANT:_ Please be aware that the database schema has changed significantly.\nApplying migrations might, depending on the size of your tables, take a long\ntime. If your database does not support online schema migrations, you will\nexperience downtimes. Please test the migration process before applying it to\nproduction!\n\nThe probably biggest and most significant change is the refactoring of how\nself-service flows work and what their payloads look like. This took the most\namount of time and introduces the biggest breaking changes in our APIs. We did\nthis refactoring to support several flows planned for Ory Kratos 0.7:\n\n1. Displaying QR codes (images) in login, registration, settings flows -\n   necessary for TOTP 2FA;\n2. Asking the login/registration/... UI to render JavaScript - necessary for\n   CAPTCHA, WebAuthN, and more;\n3. Refactoring the form submission API to use one endpoint per flow instead of\n   one endpoint per flow per method. This allows us to process several\n   registration/settings/login/... methods such as password + 2FA in one Go.\n\n[Check out how we migrated the NodeJS app](https://github.com/ory/kratos-selfservice-ui-node/commit/53ad90b6c82cde48994feebcc75d754ba74929ec)\nfrom the Ory Kratos 0.5 to Ory Kratos 0.6 SDK.\n\nLet's take a look into how these payloads have changed (the flows have identical\nconfiguration):\n\n**Ory Kratos v0.5**\n\n_Login_\n\n```json\n{\n  \"id\": \"ee6e1565-d3c3-4f3a-a6ff-0ba6b3a6481b\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2020-09-13T10:49:54.8295242Z\",\n  \"issued_at\": \"2020-09-13T10:39:54.8295242Z\",\n  \"request_url\": \"http://127.0.0.1:4433/self-service/login/browser\",\n  \"methods\": {\n    \"password\": {\n      \"method\": \"password\",\n      \"config\": {\n        \"action\": \"http://127.0.0.1:4433/self-service/login/methods/password?flow=ee6e1565-d3c3-4f3a-a6ff-0ba6b3a6481b\",\n        \"method\": \"POST\",\n        \"fields\": [\n          {\n            \"name\": \"identifier\",\n            \"type\": \"text\",\n            \"required\": true,\n            \"value\": \"\"\n          },\n          {\n            \"name\": \"password\",\n            \"type\": \"password\",\n            \"required\": true\n          },\n          {\n            \"name\": \"csrf_token\",\n            \"type\": \"hidden\",\n            \"required\": true,\n            \"value\": \"lNrB8sW2fZY6xnnA91V7ISYrUVcJbmRCOoGHjsnsfI7MsIL5RTbuWFm5TRv1azQW+7IRCfnt2Ch6pC42/45sJQ==\"\n          }\n        ]\n      }\n    }\n  },\n  \"forced\": false\n}\n```\n\n_Registration_\n\n```json\n{\n  \"id\": \"2b1f8c5d-e830-4068-97b8-35f776df9217\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2020-09-13T10:53:15.1774019Z\",\n  \"issued_at\": \"2020-09-13T10:43:15.1774019Z\",\n  \"request_url\": \"http://127.0.0.1:4433/self-service/registration/browser\",\n  \"active\": \"password\",\n  \"messages\": null,\n  \"methods\": {\n    \"password\": {\n      \"method\": \"password\",\n      \"config\": {\n        \"action\": \"http://127.0.0.1:4433/self-service/registration/methods/password?flow=2b1f8c5d-e830-4068-97b8-35f776df9217\",\n        \"method\": \"POST\",\n        \"fields\": [\n          {\n            \"name\": \"csrf_token\",\n            \"type\": \"hidden\",\n            \"required\": true,\n            \"value\": \"1IlHWNjkAZxuYhO82WPgNTgujKsUSaW87j6og/20i2uM4wRTWGSSUg0dJ2fbXa8C5bfM9eTKGdauGwE7y9abwA==\"\n          },\n          {\n            \"name\": \"password\",\n            \"type\": \"password\",\n            \"required\": true,\n            \"messages\": [\n              {\n                \"id\": 4000005,\n                \"text\": \"The password can not be used because the password has been found in at least 23597311 data breaches and must no longer be used..\",\n                \"type\": \"error\",\n                \"context\": {\n                  \"reason\": \"the password has been found in at least 23597311 data breaches and must no longer be used.\"\n                }\n              }\n            ]\n          },\n          {\n            \"name\": \"traits.email\",\n            \"type\": \"text\",\n            \"value\": \"foo@ory.sh\"\n          },\n          {\n            \"name\": \"traits.name.first\",\n            \"type\": \"text\",\n            \"value\": \"Ory\"\n          },\n          {\n            \"name\": \"traits.name.last\",\n            \"type\": \"text\",\n            \"value\": \"Corp\"\n          }\n        ]\n      }\n    }\n  }\n}\n```\n\n**Ory Kratos v0.6**\n\n_Login_\n\nAs you can see below, the input name `identifier` has changed to\n`password_identifier`.\n\n```json\n{\n  \"id\": \"07016811-917d-4788-bb9c-fc297897af6c\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2021-04-28T08:37:53.924337873Z\",\n  \"issued_at\": \"2021-04-28T08:27:53.924337873Z\",\n  \"request_url\": \"http://127.0.0.1:4433/self-service/login/browser\",\n  \"ui\": {\n    \"action\": \"http://127.0.0.1:4433/self-service/login?flow=07016811-917d-4788-bb9c-fc297897af6c\",\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"value\": \"IuiHo8fajl6Nwi2CfR33bmC7ZI+geYY44oinK/npkS9gaeV6DlkzS0voYZuyGawsCruvlawFl/pY6/Ph6d9JVg==\",\n          \"required\": true,\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password_identifier\",\n          \"type\": \"text\",\n          \"value\": \"\",\n          \"required\": true,\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010001,\n            \"text\": \"Sign in\",\n            \"type\": \"info\",\n            \"context\": {}\n          }\n        }\n      }\n    ]\n  },\n  \"forced\": false\n}\n```\n\n_Registration_\n\n```json\n{\n  \"id\": \"f0c0830a-f5b2-4c2d-a37f-2e70152a4f7c\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2021-04-28T08:54:12.951178972Z\",\n  \"issued_at\": \"2021-04-28T08:44:12.951178972Z\",\n  \"request_url\": \"http://127.0.0.1:4433/self-service/registration/browser\",\n  \"ui\": {\n    \"action\": \"http://127.0.0.1:4433/self-service/registration?flow=f0c0830a-f5b2-4c2d-a37f-2e70152a4f7c\",\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"value\": \"408SIAOvpKxW/WbcYfKue26MlLTMbON7T7JT1yhiSemhznD5yiwZuZDXKsWu9vU5BIxfrsAQ8rn10QcdOFSRkA==\",\n          \"required\": true,\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"traits.email\",\n          \"type\": \"email\",\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070002,\n            \"text\": \"E-Mail\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"traits.name.first\",\n          \"type\": \"text\",\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070002,\n            \"text\": \"First Name\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"traits.name.last\",\n          \"type\": \"text\",\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070002,\n            \"text\": \"Last Name\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false\n        },\n        \"messages\": null,\n        \"meta\": {\n          \"label\": {\n            \"id\": 1040001,\n            \"text\": \"Sign up\",\n            \"type\": \"info\",\n            \"context\": {}\n          }\n        }\n      }\n    ]\n  }\n}\n```\n\nThese changes are analogous to settings, recovery, verification as well!\n\nWe hope you enjoy these new features as much as we do, even if we were not able\nto deliver 2FA in time for 0.6!\n\nOn the last note, Ory Platform, a SaaS is launching in May as early access. It\nincludes Ory Kratos as a managed service and we plan on adding all the other Ory\nopen source technology soon. In our view, Ory is a 10x improvement to the\nexisting \"IAM\" ecosystem:\n\n1. The major components of Ory Platform are and will remain Apache 2.0 licensed\n   open source. We are _not changing our approach or commitment to open source_.\n   The SaaS model allows us to keep commercialization and open source in\n   harmony;\n2. Affordable pricing - Ory does not charge on a per identity basis;\n3. Supporting migrations from the Ory Platform (SaaS) to the open-source and\n   vice versa;\n4. Offering a planet-scale service with ultra-low latencies no matter where your\n   users are;\n5. The largest set of features and APIs of any Identity Product, including\n   Identity and Credentials Management (Ory Kratos), Permissions and Access\n   Control (Ory Keto), Zero-Trust Networking (Ory Oathkeeper), OAuth2, and\n   OpenID Connect (Ory Hydra) plus integrations with Stripe, Mailchimp,\n   Salesforce, and much more.\n6. Data aggregation for threat mitigation, auditing, and other use cases (e.g.\n   integration with Snowflake, AWS RedShift, GCP BigQuery, ...)\n7. All the advantages of the open source projects - headless, fully\n   customizable, strong security, built with a community; If you wish to become\n   a part of the preview, please write a short email to\n   [sales@ory.sh](mailto:sales@ory.sh). Early access adopters are also eligible\n   for Ory Hypercare - helping you integrate with Ory fast and designing your\n   security architecture following industry best practices.\n\nThank you for being a part of our community!\n\n## Breaking Changes\n\nBCrypt is now the default hashing alogrithm. If you wish to continue using\nArgon2id please set `hashers.algorithm` to `argon2`.\n\nThis implies a significant breaking change in the verification flow payload.\nPlease consult the new ui documentation. In essence, the login flow's `methods`\nkey was replaced with a generic `ui` key which provides information for the UI\nthat needs to be rendered.\n\nTo apply this patch you must apply SQL migrations. These migrations will drop\nthe flow method table implying that all verification flows that are ongoing will\nbecome invalid. We recommend purging the flow table manually as well after this\nmigration has been applied, if you have users doing at least one self-service\nflow per minute.\n\nThis implies a significant breaking change in the recovery flow payload. Please\nconsult the new ui documentation. In essence, the login flow's `methods` key was\nreplaced with a generic `ui` key which provides information for the UI that\nneeds to be rendered.\n\nTo apply this patch you must apply SQL migrations. These migrations will drop\nthe flow method table implying that all recovery flows that are ongoing will\nbecome invalid. We recommend purging the flow table manually as well after this\nmigration has been applied, if you have users doing at least one self-service\nflow per minute.\n\nThis implies a significant breaking change in the settings flow payload. Please\nconsult the new ui documentation. In essence, the login flow's `methods` key was\nreplaced with a generic `ui` key which provides information for the UI that\nneeds to be rendered.\n\nTo apply this patch you must apply SQL migrations. These migrations will drop\nthe flow method table implying that all settings flows that are ongoing will\nbecome invalid. We recommend purging the flow table manually as well after this\nmigration has been applied, if you have users doing at least one self-service\nflow per minute.\n\nThis implies a significant breaking change in the registration flow payload.\nPlease consult the new ui documentation. In essence, the login flow's `methods`\nkey was replaced with a generic `ui` key which provides information for the UI\nthat needs to be rendered.\n\nTo apply this patch you must apply SQL migrations. These migrations will drop\nthe flow method table implying that all registration flows that are ongoing will\nbecome invalid. We recommend purging the flow table manually as well after this\nmigration has been applied, if you have users doing at least one self-service\nflow per minute.\n\nThis implies a significant breaking change in the login flow payload. Please\nconsult the new ui documentation. In essence, the login flow's `methods` key was\nreplaced with a generic `ui` key which provides information for the UI that\nneeds to be rendered.\n\nTo apply this patch you must apply SQL migrations. These migrations will drop\nthe flow method table implying that all login flows that are ongoing will become\ninvalid. We recommend purging the flow table manually as well after this\nmigration has been applied, if you have users doing at least one self-service\nflow per minute.\n\nThis change introduces a new feature: UI Nodes. Previously, all self-service\nflows (login, registration, ...) included form fields (e.g.\n`methods.password.config.fields`). However, these form fields lacked support for\nother types of UI elements such as links (for e.g. \"Sign in with Google\"),\nimages (e.g. QR codes), javascript (e.g. WebAuthn), or text (e.g. recovery\ncodes). With this patch, these new features have been introduced. Please be\naware that this introduces significant breaking changes which you will need to\nadopt to in your UI. Please refer to the most recent documentation to see what\nhas changed. Conceptionally, most things stayed the same - you do however need\nto update how you access and render the form fields.\n\nPlease be also aware that this patch includes SQL migrations which **purge\nexisting self-service forms** from the database. This means that users will need\nto re-start the login/registration/... flow after the SQL migrations have been\napplied! If you wish to keep these records, make a back up of your database\nprior!\n\nThis change introduces a new feature: UI Nodes. Previously, all self-service\nflows (login, registration, ...) included form fields (e.g.\n`methods.password.config.fields`). However, these form fields lacked support for\nother types of UI elements such as links (for e.g. \"Sign in with Google\"),\nimages (e.g. QR codes), javascript (e.g. WebAuthn), or text (e.g. recovery\ncodes). With this patch, these new features have been introduced. Please be\naware that this introduces significant breaking changes which you will need to\nadopt to in your UI. Please refer to the most recent documentation to see what\nhas changed. Conceptionally, most things stayed the same - you do however need\nto update how you access and render the form fields.\n\nPlease be also aware that this patch includes SQL migrations which **purge\nexisting self-service forms** from the database. This means that users will need\nto re-start the login/registration/... flow after the SQL migrations have been\napplied! If you wish to keep these records, make a back up of your database\nprior!\n\nThe configuration value for `hashers.argon2.memory` is now a string\nrepresentation of the memory amount including the unit of measurement. To\nconvert the value divide your current setting (KB) by 1024 to get a result in MB\nor 1048576 to get a result in GB. Example: `131072` would now become `128MB`.\n\nCo-authored-by: aeneasr <3372410+aeneasr@users.noreply.github.com>\nCo-authored-by: aeneasr <aeneas@ory.sh>\n\nPlease run SQL migrations when applying this patch.\n\nThe following configuration keys were updated:\n\n```patch\nselfservice.methods.password.config.max_breaches\n```\n\n- `password.max_breaches` -> `selfservice.methods.password.config.max_breaches`\n- `password.ignore_network_errors` ->\n  `selfservice.methods.password.config.ignore_network_errors`\n\nAfter battling with [spf13/viper](https://github.com/spf13/viper) for several\nyears we finally found a viable alternative with\n[knadh/koanf](https://github.com/knadh/koanf). The complete internal\nconfiguration infrastructure has changed, with several highlights:\n\n1. Configuration sourcing works from all sources (file, env, cli flags) with\n   validation against the configuration schema, greatly improving developer\n   experience when changing or updating configuration.\n2. Configuration reloading has improved significantly and works flawlessly on\n   Kubernetes.\n3. Performance increased dramatically, completely removing the need for a cache\n   layer between the configuration system and ORY Hydra.\n4. It is now possible to load several config files using the `--config` flag.\n5. Configuration values are now sent to the tracer (e.g. Jaeger) if tracing is\n   enabled.\n\nPlease be aware that ORY Kratos might complain about an invalid configuration,\nbecause the validation process has improved significantly.\n\n### Bug Fixes\n\n- Add include stub go files\n  ([6d725b1](https://github.com/ory/kratos/commit/6d725b1461a26d99c8b179be8ca219ba83ba0f17))\n- Add index to migration status\n  ([8c6ec27](https://github.com/ory/kratos/commit/8c6ec2741535c090aae16f02a744f56c15923e2b))\n- Add node_modules to format tasks\n  ([e5f6b36](https://github.com/ory/kratos/commit/e5f6b36caeff080905d15566cf55f8fe4905dbc0))\n- Add titles to identity schema\n  ([73c15d2](https://github.com/ory/kratos/commit/73c15d23840aa83d2c99c013cad52ad7df285f18))\n- Adopt to new go-swagger changes\n  ([5c45bd9](https://github.com/ory/kratos/commit/5c45bd9f354bfe19b8cbcd7eb4eaebf22c441f42))\n- Allow absolute file URLs as config values\n  ([#1069](https://github.com/ory/kratos/issues/1069))\n  ([4bb4f67](https://github.com/ory/kratos/commit/4bb4f679d1fe0a49edb0c0189bb7a2188d4f850d))\n- Allow hashtag in ui urls ([#1040](https://github.com/ory/kratos/issues/1040))\n  ([7591f07](https://github.com/ory/kratos/commit/7591f07f7d48376a03e9eacfdb6f4a93fd26c0d5))\n- Avoid unicode-escaping ampersand in recovery URL query string\n  ([#1212](https://github.com/ory/kratos/issues/1212))\n  ([d172368](https://github.com/ory/kratos/commit/d17236870af490f043d87e220179b35c9eb2dd4e))\n- Bcrypt regression in credentials counting\n  ([23fc13b](https://github.com/ory/kratos/commit/23fc13ba778e0045ca30c00d673ebd6c2f2b7fb7))\n- Broken make quickstart-dev task\n  ([#980](https://github.com/ory/kratos/issues/980))\n  ([999828a](https://github.com/ory/kratos/commit/999828ae036f20bde6d12fe89851e1fde9bdaca6)),\n  closes [#965](https://github.com/ory/kratos/issues/965)\n- Broken make sdk task ([#977](https://github.com/ory/kratos/issues/977))\n  ([5b01c7a](https://github.com/ory/kratos/commit/5b01c7a368c5bcfaa3af218d42f15288f51ab3e4)),\n  closes [#950](https://github.com/ory/kratos/issues/950)\n- Call contextualized test helpers\n  ([e1f3f78](https://github.com/ory/kratos/commit/e1f3f7835696b039409c9d05f63665aba7a179ae))\n- **cmd:** Make HTTP calls resilient\n  ([e8ed61f](https://github.com/ory/kratos/commit/e8ed61fc3e806453f78b8fa629e96ff7b320bf95))\n- Code integer parsing bit size\n  ([#1178](https://github.com/ory/kratos/issues/1178))\n  ([31e9632](https://github.com/ory/kratos/commit/31e9632bcd6ec3bdeabe862a4cce89021c6dd361)):\n\n  In some cases we had a wrong bitsize of `64`, while the var was later cast to\n  `int`. Replaced with a bitsize of `0`, which is the value to cast to `int`.\n\n- Contextualize identity persister\n  ([f8640c0](https://github.com/ory/kratos/commit/f8640c04f0c5873c39c8af4652d16bfbd347b79e))\n- Convert all identifiers to lower case on login\n  ([#815](https://github.com/ory/kratos/issues/815))\n  ([d64b575](https://github.com/ory/kratos/commit/d64b5757c710c436d6789dbdb33ed04dc11cbdf9)),\n  closes [#814](https://github.com/ory/kratos/issues/814)\n- Courier adress ([#1198](https://github.com/ory/kratos/issues/1198))\n  ([ebe4e64](https://github.com/ory/kratos/commit/ebe4e643150f7603a1e3a3cf6f909135097b3f49)),\n  closes [#1194](https://github.com/ory/kratos/issues/1194)\n- Courier message dequeue race condition\n  ([#1024](https://github.com/ory/kratos/issues/1024))\n  ([5396a82](https://github.com/ory/kratos/commit/5396a82c34eef5d42444b5c4371bd4f820fe3eb0)),\n  closes [#652](https://github.com/ory/kratos/issues/652)\n  [#732](https://github.com/ory/kratos/issues/732):\n\n  Fixes the courier message dequeuing race condition by modifying\n  `*sql.Persister.NextMessages(ctx context.Context, limit uint8)` to retrieve\n  only messages with status `MessageStatusQueued` and update the status of the\n  retrieved messages to `MessageStatusProcessing` within a transaction. On\n  message send failure, the message's status is reset to `MessageStatusQueued`,\n  so that the message can be dequeued in a subsequent `NextMessages` call. On\n  message send success, the status is updated to `MessageStatusSent` (no change\n  there).\n\n- Define credentials types as sql template and resolve crdb issue\n  ([a2d6eeb](https://github.com/ory/kratos/commit/a2d6eeb2928c9750741237f559197fd80494310d))\n- Dereference pointer types from new flow structures\n  ([#1019](https://github.com/ory/kratos/issues/1019))\n  ([efedc92](https://github.com/ory/kratos/commit/efedc920e592bd6e963726e6b123ddc40df93a59))\n- Do not include smtp in tracing\n  ([#1268](https://github.com/ory/kratos/issues/1268))\n  ([bbfcbf9](https://github.com/ory/kratos/commit/bbfcbf9ce595d842a53a3ea21c286d5899eeb28f))\n- Do not publish version at public endpoint\n  ([3726ed4](https://github.com/ory/kratos/commit/3726ed4d145a949b25f5b5da5f58d4f448a2a90f))\n- Do not reset registration method\n  ([554bb0b](https://github.com/ory/kratos/commit/554bb0b4e62e4ac2a321fa4dbf89ffdf37b188df))\n- Do not return system errors for missing identifiers\n  ([1fcc855](https://github.com/ory/kratos/commit/1fcc8557bfee0f7ba562a635670b61dc9acb3530)),\n  closes [#1286](https://github.com/ory/kratos/issues/1286)\n- Export mailhog dockertest runner\n  ([1384148](https://github.com/ory/kratos/commit/138414873ad319c6c32c6cc64a73547540dffc74))\n- Fix random delay norm distribution math\n  ([#1131](https://github.com/ory/kratos/issues/1131))\n  ([bd9d28f](https://github.com/ory/kratos/commit/bd9d28fe354710957f4ebaf71d1fffeae3968364))\n- Fork audit logger from root logger\n  ([68a09e7](https://github.com/ory/kratos/commit/68a09e7f3dc3ded9a477bb309c68ac8c4e2c2836))\n- Gitlab oidc flow ([#1159](https://github.com/ory/kratos/issues/1159))\n  ([0bb3eb6](https://github.com/ory/kratos/commit/0bb3eb6db1144a09f4ac356cc45e1644d862bb70)),\n  closes [#1157](https://github.com/ory/kratos/issues/1157)\n- Give specific message instead of only 404 when method is disabled\n  ([#1025](https://github.com/ory/kratos/issues/1025))\n  ([2f62041](https://github.com/ory/kratos/commit/2f62041a62588f5b3b062092c57053facb858e62)):\n\n  Enabled strategies are not only used for handlers but also in other areas\n  (e.g. populating the flow methods). So we should keep the logic to get enabled\n  strategies and add new functions for getting all strategies.\n\n- **hashing:** Make bcrypt default hashing algorithm\n  ([04abe77](https://github.com/ory/kratos/commit/04abe774ada1ef4bf318658fcf84c1d39a2a922d))\n- Ignore unset domain aliases\n  ([ada6997](https://github.com/ory/kratos/commit/ada6997ff3dc7e48fd098e40267db5f231a5201f))\n- Improve cli error output\n  ([43e9678](https://github.com/ory/kratos/commit/43e967887280b57639565dabd92a07f02fbddeb5))\n- Improve error stack trace\n  ([4351773](https://github.com/ory/kratos/commit/43517737109088eda3b1d7f5b42f78bd5eb701d2))\n- Improve error tracing ([#1005](https://github.com/ory/kratos/issues/1005))\n  ([456fd25](https://github.com/ory/kratos/commit/456fd254485fc80b9ae02dfca672a9fea8ae0134))\n- Improve test contextualization\n  ([2f92a70](https://github.com/ory/kratos/commit/2f92a7066d72535d32146a98207996fda45e0b96))\n- Initialize randomdelay with seeded source\n  ([9896289](https://github.com/ory/kratos/commit/9896289216f10b808a8c78b86d9c27b8d74379de))\n- Insert credentials type constants as part of migrations\n  ([#865](https://github.com/ory/kratos/issues/865))\n  ([92b79b8](https://github.com/ory/kratos/commit/92b79b86762edddf2ad6529b98b3383b641148d5)),\n  closes [#861](https://github.com/ory/kratos/issues/861)\n- Linking a connection may result in system error\n  ([#990](https://github.com/ory/kratos/issues/990))\n  ([be02a70](https://github.com/ory/kratos/commit/be02a70c3cd60adbcc13559e1cb5dc01a8572da4)),\n  closes [#694](https://github.com/ory/kratos/issues/694)\n- Marking whoami auhorization parameter as 'in header'\n  ([#1244](https://github.com/ory/kratos/issues/1244))\n  ([62d8b85](https://github.com/ory/kratos/commit/62d8b85223a0535b07620b08d35c6c3f6b127642)),\n  closes [#1215](https://github.com/ory/kratos/issues/1215)\n- Move schema loaders to correct file\n  ([029781f](https://github.com/ory/kratos/commit/029781f69448e8abc85607a03b4bd2055158cf2c))\n- Move to new transaction-safe migrations\n  ([#1063](https://github.com/ory/kratos/issues/1063))\n  ([2588fb4](https://github.com/ory/kratos/commit/2588fb489d76939aeec2986d30fde9075b373831)):\n\n  This patch introduces a new SQL transaction model for running SQL migrations.\n  This fix is particularly targeted at CockroachDB which has limited support for\n  mixing DDL and DML statements.\n\n  Previously it could happen that migrations failure needed manual intervention.\n  This has now been resolved. The new migration model is compatible with the old\n  one and should work without a problem.\n\n- Pass down context to registry\n  ([0879446](https://github.com/ory/kratos/commit/08794461ed95965a9e5460ded2b4c04ab0f5e2e8))\n- Re-enable SDK generation\n  ([1d5854d](https://github.com/ory/kratos/commit/1d5854d6298e3d21f85a8fa01d3004166c4b3f50))\n- Record cypress runs\n  ([db35d8f](https://github.com/ory/kratos/commit/db35d8ff6bb44dc9e9acf131cb0a14a7f4a7d160))\n- Rehydrate settings form on successful submission\n  ([3457e1a](https://github.com/ory/kratos/commit/3457e1a46f48ed79eabff76f8af08b82f12ecc89)),\n  closes [#1305](https://github.com/ory/kratos/issues/1305)\n- Remove absolete 'make pack' from Dockerfile\n  ([#1172](https://github.com/ory/kratos/issues/1172))\n  ([b8eb908](https://github.com/ory/kratos/commit/b8eb908529cc72a3147ad28e4eeee71850a8e431))\n- Remove continuity cookies on errors\n  ([85eea67](https://github.com/ory/kratos/commit/85eea6748be6ae8cdfc10cabaa6b677e4efd63eb))\n- Remove include stubs\n  ([1764e3a](https://github.com/ory/kratos/commit/1764e3a08a24db82dc391a77fdea09a91faffb5f))\n- Remove obsolete clihelpers\n  ([230fd13](https://github.com/ory/kratos/commit/230fd138d1bc7ec57647ea8eeca8e17baaacce0a))\n- Remove record from bash script\n  ([84a9315](https://github.com/ory/kratos/commit/84a9315a824cacd29d30b98b65725343af22732d))\n- Remove stray non-ctx configs\n  ([#1053](https://github.com/ory/kratos/issues/1053))\n  ([1fe137e](https://github.com/ory/kratos/commit/1fe137e0d6314bd0af47a29c00e2f72564e71cef))\n- Remove trailing double-dot from error\n  ([59581e3](https://github.com/ory/kratos/commit/59581e3fede0fd43028a5f064c350c3cc833b5b0))\n- Remove unused sql migration\n  ([1445d1d](https://github.com/ory/kratos/commit/1445d1d1b4b0b5e8ef3426a98ced9573063d8646))\n- Remove unused var\n  ([30a8cee](https://github.com/ory/kratos/commit/30a8cee22238d9f400e6d315a9bc99f710945f81))\n- Remove verify hook\n  ([98cfec6](https://github.com/ory/kratos/commit/98cfec6d72c2e7bf2db2e8dd6f8875e885923ba8)),\n  closes [#1302](https://github.com/ory/kratos/issues/1302):\n\n  The verify hook is automatically used when verification is enabled and has\n  been removed as a configuration option.\n\n- Replace jwt module ([#1254](https://github.com/ory/kratos/issues/1254))\n  ([3803c8c](https://github.com/ory/kratos/commit/3803c8ce43e35c51a9c1d7ab55bc662c398cf0d8)),\n  closes [#1250](https://github.com/ory/kratos/issues/1250)\n- Resolve build and release issues\n  ([fb582aa](https://github.com/ory/kratos/commit/fb582aa06ad55ca3fd4e2b083e1e9bbb4ba7c715))\n- Resolve clidoc issues\n  ([599e9f7](https://github.com/ory/kratos/commit/599e9f773a743f811329cc57cea2748831105e58))\n- Resolve compile issues\n  ([63063c1](https://github.com/ory/kratos/commit/63063c15c17f4d3aca96b106275a3478a8ed717e))\n- Resolve contextualized table issues\n  ([5a4f0d9](https://github.com/ory/kratos/commit/5a4f0d92800df7fb5ca0df18203a6d73416814e1))\n- Resolve crdb migration issue\n  ([9f6edfd](https://github.com/ory/kratos/commit/9f6edfd1f544d5f85e5f5558a08672f40e928136))\n- Resolve double hook invokation for registration\n  ([032322c](https://github.com/ory/kratos/commit/032322c66fb6925d8f1473746cb4bfd800d60590))\n- Resolve incorrect field types on oidc sign up completion\n  ([f88b6ab](https://github.com/ory/kratos/commit/f88b6abe202605739092a8230fbdebaebcd4407a))\n- Resolve lint issues\n  ([0348825](https://github.com/ory/kratos/commit/03488250bcdbfda6ef6a536b4de6117fa8924dc8))\n- Resolve lint issues\n  ([75a995b](https://github.com/ory/kratos/commit/75a995b3f69778655611929b65ae22bd77c5370b))\n- Resolve linting issues and disable nancy\n  ([c8396f6](https://github.com/ory/kratos/commit/c8396f6007831240d83f77433876c5971a2191ef))\n- Resolve mail queue issues\n  ([b968bc4](https://github.com/ory/kratos/commit/b968bc4ed8962d421175adbcaa2dba6eaeea2245))\n- Resolve merge regressions\n  ([9862ac7](https://github.com/ory/kratos/commit/9862ac72e0877df4cf17c93e140c354e1ddbd0e7))\n- Resolve oidc e2e regressions\n  ([f28087a](https://github.com/ory/kratos/commit/f28087aaf133c116a81213f787dc6f2e982564c0))\n- Resolve oidc regressions and e2e tests\n  ([f5091fa](https://github.com/ory/kratos/commit/f5091fac161db0b1401b340a002278bc26891251))\n- Resolve potential fsnotify leaks\n  ([3159c0a](https://github.com/ory/kratos/commit/3159c0abe109ea4e3832770278c4e9bc4ca3b3e1))\n- Resolve regressions and test failures\n  ([8bae356](https://github.com/ory/kratos/commit/8bae3565ea5410b60c3e638a49f5454fac8e63d3))\n- Resolve regressions in cookies and payloads\n  ([9e34bf2](https://github.com/ory/kratos/commit/9e34bf2f6a2f3b007069a5415643c448798207a6))\n- Resolve settings sudo regressions\n  ([4b611f3](https://github.com/ory/kratos/commit/4b611f34755369eafcbafa2fc16da13ea3b82370))\n- Resolve test regressions\n  ([e3fb028](https://github.com/ory/kratos/commit/e3fb0281dd9be123271d11f2934cfb08fdc470b7))\n- Resolve ui issues with nested form objects\n  ([8e744b9](https://github.com/ory/kratos/commit/8e744b931954283cf5f5cbf3ebaca3fa94e035ed))\n- Resolve update regression\n  ([d0d661a](https://github.com/ory/kratos/commit/d0d661aaffcba8b039738b773c891ee6e8f6449e))\n- Return delay instead of sleeping to improve tests\n  ([27b977e](https://github.com/ory/kratos/commit/27b977ebbaa25b95caa7e3e4536a09ea0bfa61c3))\n- Revert generator changes\n  ([c18b97f](https://github.com/ory/kratos/commit/c18b97f333a638d4b4495678013c55faca4b04d0))\n- Run correct error handler for registration hooks\n  ([0d80447](https://github.com/ory/kratos/commit/0d80447102d5092e310ca728012f083147c0c5c9))\n- Simplify data breaches password error reason\n  ([#1136](https://github.com/ory/kratos/issues/1136))\n  ([33d29bf](https://github.com/ory/kratos/commit/33d29bf72af03aea77f1d318c19f5087a506719f)):\n\n  This PR simplifies the error reason given when a password has appeared in data\n  breaches to not include the actual number and rather just show \"this password\n  has appeared in data breaches and must not be used\".\n\n- Support form and json formats in decoder\n  ([d420fe6](https://github.com/ory/kratos/commit/d420fe6e8a491b20063d4bfeaa0a841058087d32))\n- Update openapi definitions for signup\n  ([eb0b69d](https://github.com/ory/kratos/commit/eb0b69d50ce834b170186a39bbc9cda4d3366c36))\n- Update quickstart node image\n  ([c19b2f4](https://github.com/ory/kratos/commit/c19b2f4c57307e27ce289d44eff34f5aec1341da)):\n\n  See https://github.com/ory/kratos/discussions/1301\n\n- Update to new goreleaser config\n  ([4c2a1b7](https://github.com/ory/kratos/commit/4c2a1b7f5a0059a6e0c28779808ffb27e8910553))\n- Update to new healthx\n  ([6ec987a](https://github.com/ory/kratos/commit/6ec987ae81ef0c05f2c4d1eb836c40f9d15950b2))\n- Use equalfold\n  ([1c0e52e](https://github.com/ory/kratos/commit/1c0e52ec36ff95b53e3537c5ef457f1c818d7f6b))\n- Use new TB interface\n  ([d75a378](https://github.com/ory/kratos/commit/d75a378e700a206753f2cb17032315f2981960e7))\n- Use numerical User ID instead of name to avoid k8s security warnings\n  ([#1151](https://github.com/ory/kratos/issues/1151))\n  ([468a12e](https://github.com/ory/kratos/commit/468a12e56f22cfdf7bd05d68159cc735e75211b2)):\n\n  Our docker image scanner does not allow running processes inside container\n  using non-numeric User spec (to determine if we are trying to run docker image\n  as root).\n\n- Use remote dependencies\n  ([1e56457](https://github.com/ory/kratos/commit/1e56457d49e1cde69baa41e3111ca113aa49ee3c))\n\n### Code Generation\n\n- Pin v0.6.0-alpha.1 release commit\n  ([507d13a](https://github.com/ory/kratos/commit/507d13a8ec9cd89c9933fc8814a8a99921da69fb))\n\n### Code Refactoring\n\n- Adapt new sdk in testhelpers\n  ([6e15f6f](https://github.com/ory/kratos/commit/6e15f6f86c0f146e846a384ffd6eac78406178bc))\n- Add nid everywhere\n  ([407fd95](https://github.com/ory/kratos/commit/407fd95889f416f0d76d6f3f43644a6fafa13b44))\n- Contextualize everything\n  ([7ebc3a9](https://github.com/ory/kratos/commit/7ebc3a9a1a2cd85d28c5a9adf2c0c8c10cbd072e)):\n\n  This patch contextualizes all configuration and DBAL models.\n\n- Do not use prefixed node names\n  ([fc42ece](https://github.com/ory/kratos/commit/fc42ece24107dcb6e6a416cc54a2fb5de524fd94))\n- Improve Argon2 tooling ([#961](https://github.com/ory/kratos/issues/961))\n  ([3151187](https://github.com/ory/kratos/commit/315118720419194be8baf5e5e64d7bf190179568)),\n  closes [#955](https://github.com/ory/kratos/issues/955):\n\n  This adds a load testing CLI that allows to adjust the hasher parameters under\n  simulated load.\n\n- Move faker to exportable module\n  ([09f8ae5](https://github.com/ory/kratos/commit/09f8ae5755c9978574e91676bf5df6a23a2feb78))\n- Move migratest helpers to ory/x\n  ([7eca67e](https://github.com/ory/kratos/commit/7eca67eb9ec3e4ab065af7221911a74ed16c7c48))\n- Move password config to selfservice\n  ([cd0e0eb](https://github.com/ory/kratos/commit/cd0e0ebb0de372ff31c982ef023fe1979addb05a))\n- Move to go 1.16 embed\n  ([43c4a13](https://github.com/ory/kratos/commit/43c4a13c25be4a3a23a1ffdbecfaa0f9eda1a11d)):\n\n  This patch replaces packr and pkged with the Go 1.16 embed feature.\n\n- Remove password node attribute prefix\n  ([e27fae4](https://github.com/ory/kratos/commit/e27fae4b0d7a91ff3964804963d4885178b80803))\n- Remove profile node attribute prefix\n  ([a3ff6f7](https://github.com/ory/kratos/commit/a3ff6f7eec45b1a9a1e7eb8569793fbc6a047d4f))\n- Rename config structs and interfaces\n  ([4a2f419](https://github.com/ory/kratos/commit/4a2f41977439354415118df3e37dd0cde8dac1aa))\n- Rename form to container\n  ([5da155a](https://github.com/ory/kratos/commit/5da155a07d3737cefabaf98c4ff650115f662480))\n- Replace flow's forms with new ui node module\n  ([647eb1e](https://github.com/ory/kratos/commit/647eb1e66850c67e539d0338cca6cb8ae476ee55))\n- Replace flow's forms with new ui node module\n  ([f74a5c2](https://github.com/ory/kratos/commit/f74a5c25af60936b59caee0866a21637a5c0ae6f))\n- Replace login flow methods with ui container\n  ([d4ca364](https://github.com/ory/kratos/commit/d4ca364fd8905cfb205ee047a9cb831064a6b9d0))\n- Replace recovery flow methods with ui container\n  ([cac0456](https://github.com/ory/kratos/commit/cac04562f2e4e77875275fcfd82c039d787607fb))\n- Replace registration flow methods with ui container\n  ([3f6388d](https://github.com/ory/kratos/commit/3f6388d03f91cfad17bd74ebca4d924b4b546668))\n- Replace settings flow methods with ui container\n  ([0efd17e](https://github.com/ory/kratos/commit/0efd17e76ba0a0cbd46916a7644b7bdf19bd4ab4))\n- Replace verification flow methods with ui container\n  ([dbf2668](https://github.com/ory/kratos/commit/dbf2668747922c93dd967961cd843354afbecfde))\n- Replace viper with koanf config management\n  ([5eb1bc0](https://github.com/ory/kratos/commit/5eb1bc0bff7c5d0f83c604484b8e845701112cad))\n- Update RegisterFakes calls\n  ([6268310](https://github.com/ory/kratos/commit/626831069ab4f971094ba0bc0b43ac9ff618d91d))\n- Use underscore in webhook auth types\n  ([26829d2](https://github.com/ory/kratos/commit/26829d21911cccd4a87c8693b6089af661c1bfe3))\n\n### Documentation\n\n- Add docker to docs main\n  ([8ce8b78](https://github.com/ory/kratos/commit/8ce8b785e2246557253420ea97cf6b7d5ee75d58))\n- Add docker to sidebar\n  ([ed38c88](https://github.com/ory/kratos/commit/ed38c88bdbadcdcd2527a2b5270390251742bbe4))\n- Add dotnet sdk ([#1183](https://github.com/ory/kratos/issues/1183))\n  ([32d874a](https://github.com/ory/kratos/commit/32d874a04bb384259aeb544a3fcd6b3a8b23acdd))\n- Add faq sidebar ([#1105](https://github.com/ory/kratos/issues/1105))\n  ([10697aa](https://github.com/ory/kratos/commit/10697aa4ab5dc3e2ab90d1c037dfbe3492bf2bdf))\n- Add log docs to schema config\n  ([4967f11](https://github.com/ory/kratos/commit/4967f11d8df177ebdae855eb745e90d21ce38e9f))\n- Add more HA docs\n  ([cbb2e27](https://github.com/ory/kratos/commit/cbb2e27f8919a8991c4797a3f1c192ec364f0dd3))\n- Add Rust and Dart SDKs\n  ([6d96952](https://github.com/ory/kratos/commit/6d969528e13350ef099669510d3d37df1c007c82)):\n\n  We now support for Rust and Dart SDKs!\n\n- Add SameSite help\n  ([2df6729](https://github.com/ory/kratos/commit/2df6729b4acc70532024658e8874682de64b06b3))\n- Add shell-session language\n  ([d16db87](https://github.com/ory/kratos/commit/d16db87802ae2f230a02e4deed189f473588552c))\n- Add ui node docs\n  ([e48a07d](https://github.com/ory/kratos/commit/e48a07d03c19a0677d3a56f9e57294b358f24501))\n- Adding double colons ([#1187](https://github.com/ory/kratos/issues/1187))\n  ([fc712f4](https://github.com/ory/kratos/commit/fc712f4530066c429242491c19d1534ffb267b0c))\n- Bcrypt is default and add 72 char warning\n  ([29ae53a](https://github.com/ory/kratos/commit/29ae53a96b4472ff549b34241894d72d439c8ea1))\n- Better import identities examples\n  ([#997](https://github.com/ory/kratos/issues/997))\n  ([2e2880a](https://github.com/ory/kratos/commit/2e2880ac057b5c98cd69481c4f6f36b564b5871d))\n- Change forum to discussions readme\n  ([#1220](https://github.com/ory/kratos/issues/1220))\n  ([ae39956](https://github.com/ory/kratos/commit/ae399561ea6ed89aaadd4128bc564254984520e8))\n- Describe more about Kratos login/browser flow on quickstart doc\n  ([#1047](https://github.com/ory/kratos/issues/1047))\n  ([fe725ad](https://github.com/ory/kratos/commit/fe725ad12b5aed5faa8f95bec24ed3aa82512de8))\n- Docker file links ([#1182](https://github.com/ory/kratos/issues/1182))\n  ([4d9b6a3](https://github.com/ory/kratos/commit/4d9b6a3fd5de81310016a811126e40a263ecd27c))\n- Document hash timing attack mitigation\n  ([ec86993](https://github.com/ory/kratos/commit/ec869930a9c0e6f6f56c2614835894e0a6a3eaab))\n- Explain how to use `after_verification_return_to`\n  ([7e1546b](https://github.com/ory/kratos/commit/7e1546be1fd20baca10507d642d4f209eb88dcbc))\n- FAQ improvements ([#1135](https://github.com/ory/kratos/issues/1135))\n  ([44d0bc9](https://github.com/ory/kratos/commit/44d0bc968a7c0ba5c0793b2349820fa8133bada3))\n- FAQ item & minor changes ([#1174](https://github.com/ory/kratos/issues/1174))\n  ([11cf630](https://github.com/ory/kratos/commit/11cf630082b56c80d12f5915f8e34aa03a7e8c54))\n- Fix broken link ([#1037](https://github.com/ory/kratos/issues/1037))\n  ([6b9aae8](https://github.com/ory/kratos/commit/6b9aae8af5aa3bd614c99b32e341fbd533caf116))\n- Fix failing build\n  ([0de328f](https://github.com/ory/kratos/commit/0de328ff0053605e6bded589a79d3ab938d55b31))\n- Fix formatting ([#966](https://github.com/ory/kratos/issues/966))\n  ([687251a](https://github.com/ory/kratos/commit/687251a24e796322b43f8aed6b1fb3d7900e3271))\n- Fix identity state bullets\n  ([#1095](https://github.com/ory/kratos/issues/1095))\n  ([f476334](https://github.com/ory/kratos/commit/f476334c4693277656ad88e768f66b59cbcba126))\n- Fix known/unknown email account recovery\n  ([#1211](https://github.com/ory/kratos/issues/1211))\n  ([e208ca5](https://github.com/ory/kratos/commit/e208ca50ba4f03d5410c9644aaa3b04bdf1b8dbd))\n- Fix link\n  ([7f6d7f5](https://github.com/ory/kratos/commit/7f6d7f501d7118dfe6868c9d923fb5ecc5eded48))\n- Fix link ([#1128](https://github.com/ory/kratos/issues/1128))\n  ([e7043e9](https://github.com/ory/kratos/commit/e7043e9b99260eaff2b48ca6f457af46a1521654))\n- Fix link to blogpost ([#949](https://github.com/ory/kratos/issues/949))\n  ([4622e32](https://github.com/ory/kratos/commit/4622e3228fb12231222c7e6b602458111f35f727)),\n  closes [#945](https://github.com/ory/kratos/issues/945)\n- Fix link to self-service flows overview\n  ([#995](https://github.com/ory/kratos/issues/995))\n  ([2be8778](https://github.com/ory/kratos/commit/2be877847644a3df2645ac3be4bbd7704db30b17))\n- Fix note block in third party login guide\n  ([#920](https://github.com/ory/kratos/issues/920))\n  ([745cea0](https://github.com/ory/kratos/commit/745cea02d0e9940f689e668bbd814b29fd53bf37)):\n\n  Allows the document to render properly\n\n- Fix npm links ([#991](https://github.com/ory/kratos/issues/991))\n  ([4ce4468](https://github.com/ory/kratos/commit/4ce4468132dde21c1692e3a834ad7780bee12b90))\n- Fix self-service code flows labels\n  ([#1253](https://github.com/ory/kratos/issues/1253))\n  ([f2ed424](https://github.com/ory/kratos/commit/f2ed424289cdd2a0edc1736888dd15be6df65f11))\n- Fix typo in README ([#1122](https://github.com/ory/kratos/issues/1122))\n  ([e500707](https://github.com/ory/kratos/commit/e5007078c3cd597cea669827b96c7e6f205f2f32))\n- Link to argon2 blogpost and add cross-references\n  ([#1038](https://github.com/ory/kratos/issues/1038))\n  ([9ab7c3d](https://github.com/ory/kratos/commit/9ab7c3df59ecd94a74a7bf18af9c0ded5305e042))\n- Make explicit the ID of the default schema\n  ([#1173](https://github.com/ory/kratos/issues/1173))\n  ([cc6e9ff](https://github.com/ory/kratos/commit/cc6e9ffbac7118436d85078720cde2de98a68044))\n- Minor cosmetics ([#1050](https://github.com/ory/kratos/issues/1050))\n  ([34db06f](https://github.com/ory/kratos/commit/34db06fd4f83d415c09109b06dfd3b82ce03705e))\n- Minor improvements ([#1052](https://github.com/ory/kratos/issues/1052))\n  ([f0672b5](https://github.com/ory/kratos/commit/f0672b5cb8cca41fa914db21798d20f00a5699f9))\n- ORY -> Ory\n  ([ea30979](https://github.com/ory/kratos/commit/ea309797bf59f3da5c5cd184e45f2e585144be56))\n- **prometheus:** Update codedoc\n  ([47146ea](https://github.com/ory/kratos/commit/47146ea8ce169ee908aa4d33b59a01e9df4bae10))\n- Reformat settings code samples\n  ([cdbbf4d](https://github.com/ory/kratos/commit/cdbbf4df5fa3fa667a78d5cf682bc7fa36693e9d))\n- Remove unnecessary and wrong docker pull commands\n  ([#1203](https://github.com/ory/kratos/issues/1203))\n  ([2b0342a](https://github.com/ory/kratos/commit/2b0342ad7607d705bcebfafd5a78e4e09e57a940))\n- Resolve duplication error\n  ([a3d8284](https://github.com/ory/kratos/commit/a3d8284ab20ae76bccba361601b7290af20bdde6))\n- Update build from source\n  ([9b5754f](https://github.com/ory/kratos/commit/9b5754f36661f6de9c95f30c06f28164fe5be48b)),\n  closes [#979](https://github.com/ory/kratos/issues/979)\n- Update email template docs\n  ([1778cb9](https://github.com/ory/kratos/commit/1778cb9a293feb2c91c0b1921ab78a0395cdca98)),\n  closes [#897](https://github.com/ory/kratos/issues/897)\n- Update identity-data-model links\n  ([b5fd9a3](https://github.com/ory/kratos/commit/b5fd9a3a0821215f94da168c9c6f87dceba8c8f4))\n- Update identity.ID field documentation\n  ([4624f03](https://github.com/ory/kratos/commit/4624f03a5e9249a5449992a1f0b7ec80dc3499fd)):\n\n  See https://github.com/ory/kratos/discussions/956\n\n- Update kratos video link ([#1073](https://github.com/ory/kratos/issues/1073))\n  ([e86178f](https://github.com/ory/kratos/commit/e86178f4ee66e5053e0da2fab2c21ecb2e730ada))\n- Update login code samples\n  ([695a30f](https://github.com/ory/kratos/commit/695a30f6c80f277676bf04b4665efeb7ea4db618))\n- Update login code samples\n  ([ce6c755](https://github.com/ory/kratos/commit/ce6c75587bea80ef83855d764fed79a9d6c948d3))\n- Update quickstart samples\n  ([c3fcaba](https://github.com/ory/kratos/commit/c3fcaba65899d9d46a08ca8b60ec0c010f70b16c))\n- Update recovery code samples\n  ([d9fbb62](https://github.com/ory/kratos/commit/d9fbb62faff5144f587136935f15d24b6399f29c))\n- Update registration code samples\n  ([317810f](https://github.com/ory/kratos/commit/317810ffd8ba6faf87f2248263b6c82cf4e9ffd8))\n- Update self-service code samples\n  ([6415011](https://github.com/ory/kratos/commit/6415011ab83a19972c6f52467055fbdcef23a0cc))\n- Update settings code samples\n  ([bbd6266](https://github.com/ory/kratos/commit/bbd6266c22097fae195654957cbab589d04892c7))\n- Update verification code samples\n  ([4285dec](https://github.com/ory/kratos/commit/4285dec59a8fc31fa3416b594c765f5da9a9de1c))\n- Use correct extension for identity-data-model\n  ([acab3e8](https://github.com/ory/kratos/commit/acab3e8b489d9865e4bf0805895f0b7ae9e6f1b8)):\n\n  See https://github.com/ory/kratos/pull/1197#issuecomment-819455322\n\n### Features\n\n- Add email template specification in doc\n  ([#898](https://github.com/ory/kratos/issues/898))\n  ([4230d9e](https://github.com/ory/kratos/commit/4230d9e0fc35c651b0d2cbdbbf9e1f1c514743f8))\n- Add error for when no login strategy was found\n  ([6bae66c](https://github.com/ory/kratos/commit/6bae66cde362c4e2995c9d06a0d3ffee403feb74))\n- Add facebook provider to oidc providers and documentation\n  ([#1035](https://github.com/ory/kratos/issues/1035))\n  ([905bb03](https://github.com/ory/kratos/commit/905bb032520189212bd88f29641903945ae03608)),\n  closes [#1034](https://github.com/ory/kratos/issues/1034)\n- Add FAQ to docs ([#1096](https://github.com/ory/kratos/issues/1096))\n  ([9c6b68c](https://github.com/ory/kratos/commit/9c6b68c454f472b26c34e1975b6a67b24b218f47))\n- Add gh login to claims\n  ([49deb2e](https://github.com/ory/kratos/commit/49deb2e166362a5d051bc08523ef44425f144bdd))\n- Add login strategy text message\n  ([7468c83](https://github.com/ory/kratos/commit/7468c835d4800c207035897fc9962860d8ab7803))\n- Add more tests for multi domain args\n  ([e99803b](https://github.com/ory/kratos/commit/e99803b62a847bcee52bcd87fa8088124b4deae2))\n- Add Prometheus monitoring to Public APIs\n  ([#1022](https://github.com/ory/kratos/issues/1022))\n  ([75a4f1a](https://github.com/ory/kratos/commit/75a4f1a5472ffd780fed43a7395a191ed495c6e9))\n- Add random delay to login flow\n  ([#1088](https://github.com/ory/kratos/issues/1088))\n  ([cb9894f](https://github.com/ory/kratos/commit/cb9894fefc694a4092215d3981e80f287021542f)),\n  closes [#832](https://github.com/ory/kratos/issues/832)\n- Add return_url to verification flow\n  ([#1149](https://github.com/ory/kratos/issues/1149))\n  ([bb99912](https://github.com/ory/kratos/commit/bb99912d823e9bcffa41edf50a01dcae40117fe6)),\n  closes [#1123](https://github.com/ory/kratos/issues/1123)\n  [#1133](https://github.com/ory/kratos/issues/1133)\n- Add sql migrations for new login flow\n  ([e947edf](https://github.com/ory/kratos/commit/e947edf497b36bc576061c9ae38049e84ee48575))\n- Add sql tracing\n  ([3c4cc1c](https://github.com/ory/kratos/commit/3c4cc1cec170df14331288170a94ada770d3289f))\n- Add tracing to config schema\n  ([007dde4](https://github.com/ory/kratos/commit/007dde4482d11f22b8527c94b002da675152a872))\n- Add transporter with host modification\n  ([2c41b81](https://github.com/ory/kratos/commit/2c41b81be947f9972638d082105f0f5c83078b91))\n- Add workaround template for go openapi\n  ([5d72d10](https://github.com/ory/kratos/commit/5d72d10f6c6948c48c5701fe348084a668c8311a))\n- Adds slack sogial login ([#974](https://github.com/ory/kratos/issues/974))\n  ([7c66053](https://github.com/ory/kratos/commit/7c66053390b3086fe7233625038a78431a61e507)),\n  closes [#953](https://github.com/ory/kratos/issues/953)\n- Allow session cookie name configuration\n  ([77ce316](https://github.com/ory/kratos/commit/77ce3162ba97cf5c516c26ef499d9fa892162f0a)),\n  closes [#268](https://github.com/ory/kratos/issues/268)\n- Allow specifying sender name in smtp.from_address\n  ([#1100](https://github.com/ory/kratos/issues/1100))\n  ([5904fe3](https://github.com/ory/kratos/commit/5904fe319f75f8138783434d568db6fc7c55b301))\n- Bcrypt algorithm support ([#1169](https://github.com/ory/kratos/issues/1169))\n  ([b2612ee](https://github.com/ory/kratos/commit/b2612eefbad98d29482d364f670549f470d0a6f5)):\n\n  This patch adds the ability to use BCrypt instead of Argon2id for password\n  hashing. We recommend using BCrypt for web workloads where password hashing\n  should take around 200ms. For workloads where login takes >= 2 seconds, we\n  recommend to continue using Argon2id.\n\n  To use bcrypt for password hashing, set your config as follows:\n\n  ```\n  hashers:\n  bcrypt:\n     cost: 12\n   algorithm: bcrypt\n  ```\n\n  Switching the hashing algorithm will not break existing passwords!\n\n  Co-authored-by: Patrik <zepatrik@users.noreply.github.com>\n\n- Check migrations in health check\n  ([c6ef7ad](https://github.com/ory/kratos/commit/c6ef7ad16b70310c645550f7e41b3c8aff847de3))\n- Configure domain alias as query param\n  ([9d8563e](https://github.com/ory/kratos/commit/9d8563eeb3293c42cce440ad74f025b304cccbbe))\n- Contextualize configuration\n  ([d3d5327](https://github.com/ory/kratos/commit/d3d5327a3622318265a063be4782caa25e645a05))\n- Contextualize health checks\n  ([8145a1c](https://github.com/ory/kratos/commit/8145a1c9acaeab441e787118d40ccd448ea82fe4))\n- Contextualize http client in cli calls\n  ([3b3ef8f](https://github.com/ory/kratos/commit/3b3ef8f025d75b244d9285036e66f79af7d5ee35))\n- Contextualize persitence testers\n  ([6440373](https://github.com/ory/kratos/commit/64403736ad9f8b264567e1f8eed1af710cab6046))\n- Courier foreground worker with \"kratos courier watch\"\n  ([#1062](https://github.com/ory/kratos/issues/1062))\n  ([500b8ba](https://github.com/ory/kratos/commit/500b8bacd9fd541afd053f42fec66443cfebabda)),\n  closes [#1033](https://github.com/ory/kratos/issues/1033)\n  [#1024](https://github.com/ory/kratos/issues/1024):\n\n  BREACKING CHANGES: This patch moves the courier watcher (responsible for\n  sending mail) to its own foreground worker, which can be executed as a, for\n  example, Kubernetes job.\n\n  It is still possible to have the previous behaviour which would run the worker\n  as a background task when running `kratos serve` by using the\n  `--watch-courier` flag.\n\n  To run the foreground worker, use `kratos courier watch -c your/config.yaml`.\n\n- **courier:** Allow sending individual messages\n  ([cbb2c0b](https://github.com/ory/kratos/commit/cbb2c0bef63323a177589e9d2a809c84b4f1acdd))\n- Do not enforce bcrypt 12 for dev envs\n  ([bbf44d8](https://github.com/ory/kratos/commit/bbf44d887ae5cdb5975516149c74b3ba10896209))\n- Email input validation ([#1287](https://github.com/ory/kratos/issues/1287))\n  ([cd56b73](https://github.com/ory/kratos/commit/cd56b73df363dd37485f07d31fef11fd4d9f40a6)),\n  closes [#1285](https://github.com/ory/kratos/issues/1285)\n- Export and add config options\n  ([4391fe5](https://github.com/ory/kratos/commit/4391fe572eb6a766afe9808396847ca5fdca07f5))\n- Expose courier worker\n  ([f50969e](https://github.com/ory/kratos/commit/f50969ecba757dea558e9e8b9dd142f5f564d53a))\n- Expose crdb ui\n  ([504d518](https://github.com/ory/kratos/commit/504d5181f5e391bb8d67768b314a0348ed252c8b))\n- Global docs sidebar ([#1258](https://github.com/ory/kratos/issues/1258))\n  ([7108262](https://github.com/ory/kratos/commit/71082624e093b8c100e71ae59050f89b35ac20a2))\n- Implement and test domain aliasing\n  ([1516a54](https://github.com/ory/kratos/commit/1516a54657df485627251de4e7019bc16353c956)):\n\n  This patch adds a feature called domain aliasing. For more information, head\n  over to http://ory.sh/docs/kratos/next/guides/multi-domain-cookies\n\n- Improve oas spec and fix mobile tests\n  ([4ead2c8](https://github.com/ory/kratos/commit/4ead2c826a2f1a307e327b9736dd8ac99ef52743))\n- Improve sorting of ui fields\n  ([797b49d](https://github.com/ory/kratos/commit/797b49d0175280f85f568014cf3083e9bc42d354)):\n\n  See https://github.com/ory/kratos/discussions/1196\n\n- Include schema\n  ([348a493](https://github.com/ory/kratos/commit/348a493c9e5381830b76e57cad803a308e6ce53a))\n- Make cli commands consumable in Ory Cloud\n  ([#926](https://github.com/ory/kratos/issues/926))\n  ([fed790b](https://github.com/ory/kratos/commit/fed790b0f71f028f6d92e8ebceee188dbdb20770))\n- Migrate to openapi v3\n  ([595224b](https://github.com/ory/kratos/commit/595224b1efd5a225702ef236a87f08180a7118b8))\n- **oidc:** Support google hd claim\n  ([#1097](https://github.com/ory/kratos/issues/1097))\n  ([1f20a5c](https://github.com/ory/kratos/commit/1f20a5ceba7682719112d24a3b18bf046fb2ac22))\n- Populate email templates at delivery time, add plaintext defaults\n  ([#1155](https://github.com/ory/kratos/issues/1155))\n  ([7749c7a](https://github.com/ory/kratos/commit/7749c7a75a4386c1fd53db57626355467b698c2f)),\n  closes [#1065](https://github.com/ory/kratos/issues/1065)\n- **schema:** Add totp errors\n  ([a61f881](https://github.com/ory/kratos/commit/a61f8814101401dbb422967e37b6c6c1ae85d113))\n- Sort and label nodes with easy to use defaults\n  ([cbec27c](https://github.com/ory/kratos/commit/cbec27c957a733411e4c1d511ed5854855b7236e)):\n\n  Ory Kratos takes a guess based on best practices for\n  - ordering UI nodes (e.g. email, password, submit button)\n  - grouping UI nodes (e.g. keep password and oidc nodes together)\n  - labeling UI nodes (e.g. \"Sign in with GitHub\")\n  - using the \"title\" attribute from the identity schema to label trait fields\n\n  This greatly simplifies front-end code on your end and makes it even easier to\n  integrate with Ory Kratos! If you want a custom experience with e.g.\n  translations or other things you can always adjust this in your UI\n  integration!\n\n- Support base64 inline schemas\n  ([815a248](https://github.com/ory/kratos/commit/815a24890a118f4128ac083241a93d8df27042f7))\n- Support contextual csrf cookies\n  ([957ef38](https://github.com/ory/kratos/commit/957ef38b69fc6ab071b91262736e6c191be3a4b8))\n- Support domain aliasing in session cookie\n  ([0681c12](https://github.com/ory/kratos/commit/0681c123f2d856ca27caee645dadc9e6e3731d2c))\n- Support label in oidc config\n  ([a99cdcd](https://github.com/ory/kratos/commit/a99cdcddaa0c4bd7b679884b232c2ef8f2dcd978))\n- Support retryable CRDB transactions\n  ([f0c21d7](https://github.com/ory/kratos/commit/f0c21d7e0a6ed85818d0e9025a451cb8cbdee086))\n- Unix sockets support ([#1255](https://github.com/ory/kratos/issues/1255))\n  ([ad010de](https://github.com/ory/kratos/commit/ad010de240ddd9219f0cfb2ca3fbb180d2d3a697))\n- Web hooks support (recovery)\n  ([#1289](https://github.com/ory/kratos/issues/1289))\n  ([3e181fe](https://github.com/ory/kratos/commit/3e181fe3d7750a715ab31eb8347fbb4bdb89d6e6)),\n  closes [#271](https://github.com/ory/kratos/issues/271):\n\n  feat: web hooks for self-service flows\n\n  This feature adds the ability to define web-hooks using a mixture of\n  configuration and JsonNet. This allows integration with services like\n  Mailchimp, Stripe, CRMs, and all other APIs that support REST requests.\n  Additional to these new changes it is now possible to define hooks for\n  verification and recovery as well!\n\n  For more information, head over to the\n  [hooks documentation](https://www.ory.sh/kratos/docs/self-service/hooks).\n\n### Tests\n\n- Add case to ensure correct behavior when verifying a different email address\n  ([#999](https://github.com/ory/kratos/issues/999))\n  ([f95a117](https://github.com/ory/kratos/commit/f95a117677c9c59436ad10aa8951fe875c39a64f)),\n  closes [#998](https://github.com/ory/kratos/issues/998)\n- Add oasis test case\n  ([f80691b](https://github.com/ory/kratos/commit/f80691b9dd77566857c4284e2639cc94d5b8c333))\n- Bump poll interval\n  ([b3dc925](https://github.com/ory/kratos/commit/b3dc925a5d43557293745ee81c0ffb3db37b6342))\n- Bump video quality\n  ([b7f8d04](https://github.com/ory/kratos/commit/b7f8d042646037e1589ae2d03602bd63a5cec2fe))\n- Bump wait times\n  ([b2e43f8](https://github.com/ory/kratos/commit/b2e43f8b0b64784f60e5f57d9a0f5d2928c2b891))\n- Clean up hydra env before restart\n  ([cf49414](https://github.com/ory/kratos/commit/cf494149e6a46b15e3b174185e1e87cfcd6f9f7a))\n- **e2e:** Significantly reduce wait and idle times\n  ([f525fc5](https://github.com/ory/kratos/commit/f525fc53afec6f5232ce507fe25ddec1b9069196))\n- Longer wait times\n  ([4bec9ef](https://github.com/ory/kratos/commit/4bec9ef50f14f22342a311f09ba1b59cde47befc))\n- Reliable migration tests on crdb\n  ([2e3764b](https://github.com/ory/kratos/commit/2e3764ba66c156d810de66fba2b0e142dced6f4d))\n- Remove old noop test\n  ([16dca3f](https://github.com/ory/kratos/commit/16dca3f78b2021c09ec83e81ab6d2e68c42ca081))\n- Resolve compile issues\n  ([c1b5ba4](https://github.com/ory/kratos/commit/c1b5ba42171ec522579df9dfaff27b5b74a1566a))\n- Resolve flaky tests\n  ([cb670a8](https://github.com/ory/kratos/commit/cb670a854cbb09b8437bfed7e4a6908ff6dcfd27))\n- Resolve json parser test regression\n  ([a1b9b9a](https://github.com/ory/kratos/commit/a1b9b9a95d58583dc7ecf6d2a501da52f84dd6bb))\n- Resolve login integration regressions\n  ([388b5b2](https://github.com/ory/kratos/commit/388b5b27d6dee7770e5f37d6d83c532044a4e984))\n- Resolve migration regression\n  ([2051a71](https://github.com/ory/kratos/commit/2051a716cb4b8cf334dd65f2ccddb31e5fbed545))\n- Resolve more json parser test regressions\n  ([ff791c4](https://github.com/ory/kratos/commit/ff791c41a1d9ce25af4e883469d3f8c0ef9eb302))\n- Resolve more regressions\n  ([c5a23af](https://github.com/ory/kratos/commit/c5a23af81427480088651833d904e3403a969fab))\n- Resolve order regression\n  ([40a849c](https://github.com/ory/kratos/commit/40a849ca35f4700185322e9ac4f6a4b70132851c))\n- Resolve regression\n  ([e2b0ad3](https://github.com/ory/kratos/commit/e2b0ad3c1845da80f078b11b327b9a0376cbb7c5))\n- Resolve regression\n  ([f0c9e5f](https://github.com/ory/kratos/commit/f0c9e5ff105d76d6bc9478c98522b2440c7181df))\n- Resolve regressions\n  ([4b9da3c](https://github.com/ory/kratos/commit/4b9da3c9d98d40f7b71a56c51543fc115974630d))\n- Resolve stub regressions\n  ([82650cf](https://github.com/ory/kratos/commit/82650cf1843f6bfde015f556f4452a7b6fd52b11))\n- Resolve test migrations\n  ([de0b65d](https://github.com/ory/kratos/commit/de0b65d96daef0e31c12b3b6915f283a8e71244b))\n- Resolve test regression issues\n  ([ccf9fed](https://github.com/ory/kratos/commit/ccf9feddade11f9fcaaf1c37dd3efeb2c4df6649))\n- Speed up tests\n  ([a16737c](https://github.com/ory/kratos/commit/a16737cccc36a14444711660f1737913ffd7ba01))\n- Update schema tests for webhooks\n  ([d1ddfa8](https://github.com/ory/kratos/commit/d1ddfa80742728b28dc5710ca5b6e7282a2dec55))\n- Update test description\n  ([55fb37f](https://github.com/ory/kratos/commit/55fb37f62fc3ab7c0d5324ed31ef3e7f66a73aa2))\n- Use bcrypt cost 4 to reduce CI times\n  ([cabe97d](https://github.com/ory/kratos/commit/cabe97d0656858fd1ee0442b40881417e91294f3))\n- Use fast bcrypt for e2e\n  ([d90cf13](https://github.com/ory/kratos/commit/d90cf13230632e76eb74965c0945573b4f2e98ff))\n\n### Unclassified\n\n- fix: resolve clidoc issues (#976)\n  ([346bc73](https://github.com/ory/kratos/commit/346bc73921655d52861b8803eb3351c4205657ee)),\n  closes [#976](https://github.com/ory/kratos/issues/976)\n  [#951](https://github.com/ory/kratos/issues/951)\n- :bug: fix ory home directory path (#897)\n  ([2fca2be](https://github.com/ory/kratos/commit/2fca2bedaa907691bef324c11545e007b51d4881)),\n  closes [#897](https://github.com/ory/kratos/issues/897)\n- Fix typo in config schema\n  ([16337f1](https://github.com/ory/kratos/commit/16337f13e4388a715c8109c29cf198c82a848a16))\n- Format\n  ([e4b7e79](https://github.com/ory/kratos/commit/e4b7e79f4ee91dadfcd008a5b3e318b6bfedad10))\n- Format\n  ([193d266](https://github.com/ory/kratos/commit/193d2668ae0955a1346390057539a8b796d17afd))\n- Format\n  ([1ebfbde](https://github.com/ory/kratos/commit/1ebfbdea75f27c8eeafa7d3aff45de133ea340bb))\n- Format\n  ([ba1eeef](https://github.com/ory/kratos/commit/ba1eeef4f232c4ab59343a2ca3c7cf0eb6dfd110))\n- Format\n  ([ada5dbb](https://github.com/ory/kratos/commit/ada5dbb58c45502b8275850a3bc0876debc66888))\n- Format\n  ([17a0bf5](https://github.com/ory/kratos/commit/17a0bf5872b33eac615afc675c7d92d7c7441b2e))\n- Initial documentation tests via Text-Runner\n  ([#567](https://github.com/ory/kratos/issues/567))\n  ([c30eb26](https://github.com/ory/kratos/commit/c30eb26f76ab70a6098c0b40c9a04726d36d72f2))\n\n# [0.5.5-alpha.1](https://github.com/ory/kratos/compare/v0.5.4-alpha.1...v0.5.5-alpha.1) (2020-12-09)\n\nThe ORY Community is proud to present you the next iteration of ORY Kratos. In\nthis release, we focused on improving production stability!\n\n### Bug Fixes\n\n- CSRF token is required when using the Revoke Session API endpoint\n  ([#839](https://github.com/ory/kratos/issues/839))\n  ([d3218a0](https://github.com/ory/kratos/commit/d3218a0f23de7293b0a4a966ad21369a92b68b1a)),\n  closes [#838](https://github.com/ory/kratos/issues/838)\n- Incorrect home path ([#848](https://github.com/ory/kratos/issues/848))\n  ([5265af0](https://github.com/ory/kratos/commit/5265af00c92fe505819300caddfcc64004d45c65))\n- Make password policy configurable\n  ([#888](https://github.com/ory/kratos/issues/888))\n  ([7a00483](https://github.com/ory/kratos/commit/7a00483908bb623efdf281e76005c4485ea6b1ab)),\n  closes [#450](https://github.com/ory/kratos/issues/450)\n  [#316](https://github.com/ory/kratos/issues/316):\n\n  Allows configuring password breach thresholds and optionally enforces checks\n  against the HIBP API.\n\n- Remove obsolete types ([#887](https://github.com/ory/kratos/issues/887))\n  ([b8bac7a](https://github.com/ory/kratos/commit/b8bac7aa56c16cd98f76a95a5e0d01fb1bbde6b7)),\n  closes [#716](https://github.com/ory/kratos/issues/716)\n- Set samesite attribute to lax if in dev mode\n  ([#824](https://github.com/ory/kratos/issues/824))\n  ([91d6698](https://github.com/ory/kratos/commit/91d6698e4ce05ee59bb72fc84b54af9d1d204b41)),\n  closes [#821](https://github.com/ory/kratos/issues/821)\n- Use working cache-control header for cdn/proxies/cache\n  ([#869](https://github.com/ory/kratos/issues/869))\n  ([d8e3d40](https://github.com/ory/kratos/commit/d8e3d40001ffdc64da2288f3cffd53cf3bfdf781)),\n  closes [#601](https://github.com/ory/kratos/issues/601)\n\n### Code Generation\n\n- Pin v0.5.5-alpha.1 release commit\n  ([83aedcb](https://github.com/ory/kratos/commit/83aedcb885acb96c5deb39fff675d5f0528af32d))\n\n### Documentation\n\n- Add contributing to sidebar ([#866](https://github.com/ory/kratos/issues/866))\n  ([44f33f9](https://github.com/ory/kratos/commit/44f33f97d43f2a3c553a65ebb2986e0731c0e5f2)):\n\n  The same change as in https://github.com/ory/hydra/pull/2209\n\n- Add newsletter to config\n  ([1735ca2](https://github.com/ory/kratos/commit/1735ca2ced104971de4e97524d0a23d57ba045f2))\n- Add recovery flow ([#868](https://github.com/ory/kratos/issues/868))\n  ([d95cfe9](https://github.com/ory/kratos/commit/d95cfe9759d3ffc08c24048a064c0c800abdf4b4)),\n  closes [#864](https://github.com/ory/kratos/issues/864):\n\n  Added a short section for the recovery flow on managing-user-identities.\n\n- Fix account recovery click instruction\n  ([#870](https://github.com/ory/kratos/issues/870))\n  ([383de9e](https://github.com/ory/kratos/commit/383de9ecf6f6504dbb9c20fb4cb984e934f0751e))\n- Fix broken link ([#893](https://github.com/ory/kratos/issues/893))\n  ([dec38a2](https://github.com/ory/kratos/commit/dec38a28964aaa13827d356e5bfa12c2a6d1400e)),\n  closes [#835](https://github.com/ory/kratos/issues/835)\n- Fix oidc config example structure\n  ([#845](https://github.com/ory/kratos/issues/845))\n  ([c102a68](https://github.com/ory/kratos/commit/c102a6844db29f994b67d23bb04e64ee71376264))\n- Fix redirect ([#802](https://github.com/ory/kratos/issues/802))\n  ([b868782](https://github.com/ory/kratos/commit/b86878229f343e6b11521596b04040f892d1e2c3))\n- Fix typo ([#847](https://github.com/ory/kratos/issues/847))\n  ([9b3da9f](https://github.com/ory/kratos/commit/9b3da9f0fe2ce71743115844d8c91a1dc9c4cbae))\n- Fix typo ([#881](https://github.com/ory/kratos/issues/881))\n  ([3078293](https://github.com/ory/kratos/commit/3078293717a2ce21c4b939de4c2c4886c75303b5))\n- Fix typo MKFA to MFA ([#826](https://github.com/ory/kratos/issues/826))\n  ([a5613d0](https://github.com/ory/kratos/commit/a5613d08aa21f90f4d192e5663ba4977b3de16c3))\n- Remove workaround note ([#886](https://github.com/ory/kratos/issues/886))\n  ([05409bc](https://github.com/ory/kratos/commit/05409bc13f527398e3de01f29437e5d4353ef8d4)),\n  closes [#718](https://github.com/ory/kratos/issues/718)\n- Swagger specs for selfservice settings browser flow\n  ([#825](https://github.com/ory/kratos/issues/825))\n  ([28d50f4](https://github.com/ory/kratos/commit/28d50f45ab14d561609be7047cac13902394b547))\n- Update oidc provider with json conf support\n  ([#833](https://github.com/ory/kratos/issues/833))\n  ([670eb37](https://github.com/ory/kratos/commit/670eb37d19674f33a36402cd9a88d61ca7327751))\n\n### Features\n\n- Add return_to parameter to logout flow\n  ([#823](https://github.com/ory/kratos/issues/823))\n  ([1c146dd](https://github.com/ory/kratos/commit/1c146dd21d616a56f510019abadd37402782bb39)),\n  closes [#702](https://github.com/ory/kratos/issues/702)\n- Add selinux compatible quickstart config\n  ([#889](https://github.com/ory/kratos/issues/889))\n  ([0f87948](https://github.com/ory/kratos/commit/0f879481df209ed96b778799adcc2a9424449b37)),\n  closes [#831](https://github.com/ory/kratos/issues/831)\n\n### Tests\n\n- Ensure registration runs only once\n  ([#872](https://github.com/ory/kratos/issues/872))\n  ([5ffc036](https://github.com/ory/kratos/commit/5ffc036ac82f36ad6ef499e217971275a35fc23a))\n\n### Unclassified\n\n- docs: fix link and typo in Configuring Cookies (#883)\n  ([c51ed6b](https://github.com/ory/kratos/commit/c51ed6b789d2e3a8fe4e93565c3bded37d298f98)),\n  closes [#883](https://github.com/ory/kratos/issues/883)\n\n# [0.5.4-alpha.1](https://github.com/ory/kratos/compare/v0.5.3-alpha.1...v0.5.4-alpha.1) (2020-11-11)\n\nThis release introduces the new CLI command\n`kratos hashers argon2 calibrate 500ms`. This command will choose the best\nparameterization for Argon2. Check out the\n[Choose Argon2 Parameters for Secure Password Hashing and Login](https://www.ory.sh/choose-recommended-argon2-parameters-password-hashing/)\nblog article for more insights!\n\n### Bug Fixes\n\n- Case in settings handler method\n  ([#798](https://github.com/ory/kratos/issues/798))\n  ([83eb4e0](https://github.com/ory/kratos/commit/83eb4e0021621014d2b543e57a01401381f07fe4))\n- Force brew install statement\n  ([#796](https://github.com/ory/kratos/issues/796))\n  ([ad542ad](https://github.com/ory/kratos/commit/ad542ad5919205ac26a757145474e5a46f3937ec)):\n\n  Closes https://github.com/ory/homebrew-kratos/issues/1\n\n### Code Generation\n\n- Pin v0.5.4-alpha.1 release commit\n  ([b02926c](https://github.com/ory/kratos/commit/b02926c42aee2748bc37ce2600596bd0c2537a0d))\n\n### Code Refactoring\n\n- Move pkger and ioutil helpers to ory/x\n  ([60a0fc4](https://github.com/ory/kratos/commit/60a0fc449d90ead6065ca00926536a989d8b2a2b))\n\n### Documentation\n\n- Fix another broken link\n  ([15bae9f](https://github.com/ory/kratos/commit/15bae9f893c2e2910167326d987455246c110001))\n- Fix broken links ([#795](https://github.com/ory/kratos/issues/795))\n  ([0ab0e7e](https://github.com/ory/kratos/commit/0ab0e7eca8e95d6c26d028c177cbbd1f06b68871)),\n  closes [#793](https://github.com/ory/kratos/issues/793)\n- Fix broken relative link ([#812](https://github.com/ory/kratos/issues/812))\n  ([b32b173](https://github.com/ory/kratos/commit/b32b173fe30b7c5c43700abfa4ddb3409a33556b))\n- Fix links ([#800](https://github.com/ory/kratos/issues/800))\n  ([5fcc272](https://github.com/ory/kratos/commit/5fcc272e625de9e583b2ec24d5679895a6d24c1b))\n- Fix oidc config examples ([#799](https://github.com/ory/kratos/issues/799))\n  ([8a4f480](https://github.com/ory/kratos/commit/8a4f480121995d9899668f037382086fcdd2da4c))\n- Fix self-service recovery flow typo\n  ([#807](https://github.com/ory/kratos/issues/807))\n  ([800110d](https://github.com/ory/kratos/commit/800110d87c9df70a5ec79b58d9fcb9ae39ff76b9))\n- Remove duplicate words & fix spelling\n  ([#810](https://github.com/ory/kratos/issues/810))\n  ([4e1b966](https://github.com/ory/kratos/commit/4e1b96667d9f08dbafeb2f5ce144ca43309de8e0))\n- Remove leftover category from reference sidebar\n  ([#813](https://github.com/ory/kratos/issues/813))\n  ([94fde51](https://github.com/ory/kratos/commit/94fde5101d00b9e1f7228e9d122ef0a8e4719355))\n- Use correct links ([#797](https://github.com/ory/kratos/issues/797))\n  ([a4de293](https://github.com/ory/kratos/commit/a4de29399e4f1b5d0a33acc85478f2d38579a174))\n\n### Features\n\n- Add helper for choosing argon2 parameters\n  ([#803](https://github.com/ory/kratos/issues/803))\n  ([ca5a69b](https://github.com/ory/kratos/commit/ca5a69b798635d0e5361fd5b0cc369b035dca738)),\n  closes [#723](https://github.com/ory/kratos/issues/723)\n  [#572](https://github.com/ory/kratos/issues/572)\n  [#647](https://github.com/ory/kratos/issues/647):\n\n  This patch adds the new command \"hashers argon2 calibrate\" which allows one to\n  pick the desired hashing time for password hashing and then chooses the\n  optimal parameters for the hardware the command is running on:\n\n  ```\n  $ kratos hashers argon2 calibrate 500ms\n  Increasing memory to get over 500ms:\n      took 2.846592732s in try 0\n      took 6.006488824s in try 1\n    took 4.42657975s with 4.00GB of memory\n  [...]\n  Decreasing iterations to get under 500ms:\n      took 484.257775ms in try 0\n      took 488.784192ms in try 1\n    took 486.534204ms with 3 iterations\n  Settled on 3 iterations.\n\n  {\n    \"memory\": 1048576,\n    \"iterations\": 3,\n    \"parallelism\": 32,\n    \"salt_length\": 16,\n    \"key_length\": 32\n  }\n  ```\n\n# [0.5.3-alpha.1](https://github.com/ory/kratos/compare/v0.5.2-alpha.1...v0.5.3-alpha.1) (2020-10-27)\n\nThis release improves the developer and user experience around CSRF\ncounter-measures. It should now be possible to use the self-service API flows\nwithout having to explicitly disable cookie features in your SDKs and\nintegrations. Additionally, another issue in the CGO pipeline was resolved which\nfinally allows running ORY Kratos without CGO if the target database is not\nSQLite.\n\nFurther improvements to default config values have been made and a full\nend-to-end test suite for the exemplary\n[kratos-selfservice-ui-react-native](kratos-selfservice-ui-react-native) app.\nThe app is now available in the iTunes store as well - just search for \"ORY\nProfile App\"!\n\n### Bug Fixes\n\n- Add \"x-session-token\" to default allowed headers\n  ([3c912e4](https://github.com/ory/kratos/commit/3c912e4c7d46fd45c00cabb68ed7770bd44f7d07))\n- Do not set cookies on api endpoints\n  ([2f67c28](https://github.com/ory/kratos/commit/2f67c28718856ea03ea2effa89b28a8c4b3b8ae0))\n- Do not set csrf cookies on potential api endpoints\n  ([4d97a95](https://github.com/ory/kratos/commit/4d97a95d084ea99f5aca158609e197acd256cdd7))\n- Ignore unsupported migration dialects\n  ([12bb8d1](https://github.com/ory/kratos/commit/12bb8d14ae1edef18591996411be67d5693e5101)),\n  closes [#778](https://github.com/ory/kratos/issues/778):\n\n  Skips sqlite3 migrations when support is lacking.\n\n- Improve semver regex\n  ([584c0b5](https://github.com/ory/kratos/commit/584c0b5043e85e88ac2648cf699d60fed3e775a9))\n- Properly set nosurf context even when ignored\n  ([0dcb774](https://github.com/ory/kratos/commit/0dcb774157bcbfd41a5d9df3914c31162226da75))\n- Update cypress\n  ([ba8b172](https://github.com/ory/kratos/commit/ba8b1729477233f79d099e5d7b397430ac1c6ace))\n- Use correct regex for version replacement\n  ([ce870ab](https://github.com/ory/kratos/commit/ce870ababdf089344a9428d3a405e18504a3c906)),\n  closes [#787](https://github.com/ory/kratos/issues/787)\n\n### Code Generation\n\n- Pin v0.5.3-alpha.1 release commit\n  ([64dc91a](https://github.com/ory/kratos/commit/64dc91af54cdf3eba158a50690240cdc8f7cb43b))\n\n### Documentation\n\n- Fix docosaurus admonitions ([#788](https://github.com/ory/kratos/issues/788))\n  ([281a7c9](https://github.com/ory/kratos/commit/281a7c9289570d4bee33447655281b610cbe7e52))\n- Pin download script version\n  ([e4137a6](https://github.com/ory/kratos/commit/e4137a6a41d68b1480af2075bda8c5f46c42cd22))\n- Remove trailing garbage from quickstart\n  ([#787](https://github.com/ory/kratos/issues/787))\n  ([7e70924](https://github.com/ory/kratos/commit/7e709242ada28b7781c6ace272f60f9d1b9d5b2f))\n\n### Features\n\n- Improve makefile install process and update deps\n  ([d1eb37f](https://github.com/ory/kratos/commit/d1eb37f5d9d0f16e7864b5f8f08a44ba80853fa5))\n\n### Tests\n\n- Add e2e tests for mobile\n  ([d481d51](https://github.com/ory/kratos/commit/d481d51f5f4de96cbbc7c347f5dbff381b44462d))\n- Add option to disable csrf protection in apis\n  ([a0077f1](https://github.com/ory/kratos/commit/a0077f12adf94ff428b502b69bbb0eaafd05be66))\n- Bump wait time\n  ([7a719e1](https://github.com/ory/kratos/commit/7a719e17c5641f4df47314f6f0ac2cf73dddc8bb))\n- Install expo-cli globally\n  ([db21cfa](https://github.com/ory/kratos/commit/db21cfa1c589a2dab829a4c8eaf1db15d14d965e))\n- Install expo-cli in cci config with sudo\n  ([d255f46](https://github.com/ory/kratos/commit/d255f462402f2d2c2278dcba1a139d0064343b22))\n- Log wait-on output\n  ([62b5ba9](https://github.com/ory/kratos/commit/62b5ba92d56e9f6b98adb8fb9c4daff03be08f2e))\n- Output web server address\n  ([cb41ca7](https://github.com/ory/kratos/commit/cb41ca78367b1943d230fa9ac116fcf3cf69b1c1))\n- Resolve csrf test issues in settings\n  ([ef8ba7d](https://github.com/ory/kratos/commit/ef8ba7dc93d6ba84f22b7aa65d00797e33b520a3))\n- Resolve test panic\n  ([6f6461f](https://github.com/ory/kratos/commit/6f6461fe3690576015ded9146c065a1e5d950be1))\n- Revert delay increase and improve install scripts\n  ([1eafcaa](https://github.com/ory/kratos/commit/1eafcaa86be194e412b0470a759bff6afc6c21af))\n\n# [0.5.2-alpha.1](https://github.com/ory/kratos/compare/v0.5.1-alpha.1...v0.5.2-alpha.1) (2020-10-22)\n\nThis release addresses bugs and user experience issues.\n\n### Bug Fixes\n\n- Add debug quickstart yml ([#780](https://github.com/ory/kratos/issues/780))\n  ([16e6b4d](https://github.com/ory/kratos/commit/16e6b4d76d297182ea9a1f5dc6367570f02f7b42))\n- Gracefully handle double slashes in URLs\n  ([aeb9414](https://github.com/ory/kratos/commit/aeb941477910b5ab54429a6aab7a3e1e388c48c5)),\n  closes [#779](https://github.com/ory/kratos/issues/779)\n- Merge gobuffalo CGO fix\n  ([fea2e77](https://github.com/ory/kratos/commit/fea2e77ca0f9b20185c7a7704854fdcf29b7ab33))\n- Remove obsolete recovery_token and add link to schema\n  ([acf6ac4](https://github.com/ory/kratos/commit/acf6ac4e11c755e56c7d40728088257de367f7ff))\n- Return correct error in login csrf\n  ([dd9cab0](https://github.com/ory/kratos/commit/dd9cab0e02400c88e89877f755f03c6179013123)),\n  closes [#785](https://github.com/ory/kratos/issues/785)\n- Use correct assert package\n  ([76be5b0](https://github.com/ory/kratos/commit/76be5b0a5d94c251f5f07eee9f700ec11b341e2e))\n\n### Code Generation\n\n- Pin v0.5.2-alpha.1 release commit\n  ([79fcd8a](https://github.com/ory/kratos/commit/79fcd8a6949886f847f7be0c9ba2aba7554ab204))\n\n### Documentation\n\n- Small improvements to discord oidc provider guide\n  ([#783](https://github.com/ory/kratos/issues/783))\n  ([6a3c453](https://github.com/ory/kratos/commit/6a3c45330885eb95015fa7ee9b58a72c38132499))\n\n### Tests\n\n- Add tests for csrf behavior\n  ([48993e2](https://github.com/ory/kratos/commit/48993e2c496fb8af7e7b9e2752ba7078a134a75a)),\n  closes [#785](https://github.com/ory/kratos/issues/785)\n- Mark link as enabled in e2e test\n  ([c214b81](https://github.com/ory/kratos/commit/c214b81a7026b06aaca062b2aa77951d01b0e237))\n- Resolve schema test regression\n  ([bb7af1b](https://github.com/ory/kratos/commit/bb7af1b759d6c812755956ef872bcbd31b9c50be))\n\n# [0.5.1-alpha.1](https://github.com/ory/kratos/compare/v0.5.0-alpha.1...v0.5.1-alpha.1) (2020-10-20)\n\nThis release resolves an issue where ORY Kratos Docker Images without CGO and\nSQLite support would fail to boot even when SQLite was not used as a data\nsource.\n\n### Bug Fixes\n\n- Do not require sqlite without build tag\n  ([2ee787b](https://github.com/ory/kratos/commit/2ee787bc1e97bdc11d0c92d55664d59e777f7ed1))\n- Use extra dc config file for quickstart-dev\n  ([72c03f9](https://github.com/ory/kratos/commit/72c03f9bcb91d30d5ff6b94030f2cbb6144fbf8d))\n\n### Code Generation\n\n- Pin v0.5.1-alpha.1 release commit\n  ([b85b36b](https://github.com/ory/kratos/commit/b85b36b967d91c13b6d70ed668f17d3474eafae7))\n\n### Documentation\n\n- Fix spelling mistake\n  ([14e7f65](https://github.com/ory/kratos/commit/14e7f6535e69f4bee2e3ca611a8d1a36bfd5f8f8))\n- Fix spelling mistake ([#772](https://github.com/ory/kratos/issues/772))\n  ([bf401a2](https://github.com/ory/kratos/commit/bf401a26ee4422a8ea1b52f642885b0d8bac1272))\n- Improve schemas ([#773](https://github.com/ory/kratos/issues/773))\n  ([e614859](https://github.com/ory/kratos/commit/e6148590577e1688d58534b8559d3bc602f9c2e7))\n\n### Features\n\n- Auto-update docker and git tags on release\n  ([08084a9](https://github.com/ory/kratos/commit/08084a987501939544da1a1c7ee102819e2480ce))\n- Use fixed versions for docker-compose\n  ([e73c4ce](https://github.com/ory/kratos/commit/e73c4ce6f328376ad310b8f6d5c391ea06573003))\n\n### Tests\n\n- Increase waittime\n  ([5e911d6](https://github.com/ory/kratos/commit/5e911d687247e4878bdcf82e5b008617f0bbdf4e))\n- Reduce flakes by increasing wait time for expiry test\n  ([cddf29e](https://github.com/ory/kratos/commit/cddf29e7dc5304c497d5ba7c1e6a2d63c9b6c137))\n\n### Unclassified\n\n- Format\n  ([8be02c8](https://github.com/ory/kratos/commit/8be02c8938769dfcd7c9b7ed5e72e4ded3b1924b))\n\n# [0.5.0-alpha.1](https://github.com/ory/kratos/compare/v0.4.6-alpha.1...v0.5.0-alpha.1) (2020-10-15)\n\nThe ORY team and community is very proud to present the next ORY Kratos\niteration!\n\nORY Kratos is now capable of handling native (iOS, Android, Windows, macOS, ...)\nlogin, registration, settings, recovery, and verification flows. As a goodie on\ntop, we released a reference React Native application which you can find on\n[GitHub](http://github.com/ory/kratos-selfservice-ui-react-native).\n\nWe co-released our reference React Native application which acts as a reference\non implementing these flows:\n\n![Registration](http://ory.sh/images/newsletter/kratos-0.5.0/registration-screen.png)\n\n![Welcome](http://ory.sh/images/newsletter/kratos-0.5.0/welcome-screen.png)\n\n![Settings](http://ory.sh/images/newsletter/kratos-0.5.0/settings-screen.png)\n\nIn total, almost 1200 files were changed in about 480 commits. While you can\nfind a list of all changes in the changelist below, these are the changes we are\nmost proud of:\n\n- We renamed login, registration, ... requests to \"flows\" consistently across\n  the code base, APIs, and data storage. We now:\n  - Initiate a login, registration, ... flow;\n  - Fetch a login, registration, ... flow; and\n  - Complete a login, registration, ... flow using a login flow method such as\n    \"Log in with username and password\".\n- All self-service flows are now capable of handling API-based requests that do\n  not originate from Browser such as Chrome. This is set groundwork for handling\n  native flows (see above)!\n- The self service documentation has been refactored and simplified. We added\n  code samples, screenshots, payloads, and curl commands to make things easier\n  and clearer to understand. Video guides have also been added to help you and\n  the community get things done faster!\n- Documentation for rotating important secrets such as the cookie and session\n  secrets was added.\n- The need for reverse proxies was removed by adding the ability to change the\n  ORY Kratos Session Cookie domain and path! The\n  [kratos-selfservice-ui-node](https://github.com/ory/kratos-selfservice-ui-node)\n  reference implementation no longer requires HTTP Request piping which greatly\n  simplifies the network layout and codebase!\n- The ORY Kratos CLI is now capable of managing identities with an interface\n  that works almost like the Docker CLI we all love!\n- Admins are now able to initiate account recovery for identities.\n- Email verification and account recovery were refactored. It is now possible to\n  add additional strategies (e.g. recovery codes) in the future, greatly\n  increasing the feature set and security capabilities of future ORY Kratos\n  versions!\n- Lookup to Have I Been Pwnd is no longer a hard requirement, allowing\n  registration processes to complete when the service is unavailable or the\n  network is slow.\n- We contributed several issues and features in upstream projects such as\n  justinas/nosurf, gobuffalo/pop, and many more!\n- The build pipeline has been upgraded to support cross-compilation of CGO with\n  Go 1.15+.\n- Fetching flows no longer requires CSRF cookies to be set, improving developer\n  experience while not compromising on security!\n- ORY Kratos now has ORY Kratos Session Cookies (set in the HTTP Cookie header)\n  and ORY Kratos Session Tokens (set as a HTTP Bearer Authorization token or the\n  `X-Session-Token` HTTP Header).\n\nAdditionally tons of bugs were fixed, tests added, documentation improved, and\nmuch more. Please note that several things have changed in a breaking fashion.\nYou can find details for the individual breaking changes in the changelog below.\n\nWe would like to thank all community members who contributed towards this\nrelease (in no particular order):\n\n- https://github.com/kevgo\n- https://github.com/NickUfer\n- https://github.com/drwatsno\n- https://github.com/alsuren\n- https://github.com/wezzle\n- https://github.com/sherbang\n- https://github.com/perryao\n- https://github.com/jikunchong\n- https://github.com/err0r500\n- https://github.com/debrutal\n- https://github.com/c0depwn\n- https://github.com/aschepis\n- https://github.com/jakhog\n\nHave fun exploring the new release, we hope you like it! If you haven't already,\njoin the [ORY Community Slack](http://slack.ory.sh) where we hold weekly\ncommunity hangouts via video chat and answer your questions, exchange ideas, and\npresent new developments!\n\n## Breaking Changes\n\nThe \"common\" keyword has been removed from the Swagger 2.0 spec which deprecates\nthe `common` module / package / class (depending on the generated SDK). Please\nuse `public` or `admin` instead!\n\nAdditionally, the SDK for TypeScript now uses the `fetch` API which allows the\nSDK to be used in both client-side as well as server-side contexts. Please note\nthat several methods and parameters in the generated TypeScript SDK have\nchanged. Please check the TypeScript results to see what needs to be changed!\n\nThis patch changes the OpenID Connect and OAuth2 (\"Sign in with Google,\nFacebook, ...\") Callback URL from\n`http(s)://<kratos-public>/self-service/browser/flows/strategies/oidc/<provider>`\nto `http(s)://<kratos-public>/self-service/methods/oidc/<provider>`. To apply\nthis patch, you need to update these URLs at the OAuth2 Client configuration\npages of the individual OpenID Conenct providers (e.g. GitHub, Google).\n\nConfiguration key `selfservice.strategies` was renamed to `selfservice.methods`.\n\nThis patch significantly changes how email verification works. The Verification\nFlow no longer uses its own system but now re-uses the API and Browser flows and\nflow methods established in other components such as login, recovery,\nregistration.\n\nDue to the many changes these patch notes does not cover how to upgrade this\nparticular flow. We instead want to kindly ask you to check out the updated\ndocumentation for this flow at:\nhttps://www.ory.sh/kratos/docs/self-service/flows/verify-email-account-activation\n\nThis patch changes the SQL schema and thus requires running the SQL Migration\ncommand (e.g. `... migrate sql`). Never apply SQL migrations without backing up\nyour database prior.\n\nConfiguration items `selfservice.flows.<name>.request_lifespan` have been\nrenamed to `selfservice.flows.<name>.lifespan` to match the new flow semantics.\n\nWording has changed from \"Self-Service Recovery Request\" to \"Self-Service\nRecovery Flow\" to follow community feedback and practice already applied in the\ndocumentation. Additionally, fetching a recovery flow over the public API no\nlonger requires Anti-CSRF cookies to be sent.\n\nThis patch renames several important recovery flow endpoints:\n\n- `/self-service/browser/flows/recovery` is now `/self-service/recovery/browser`\n  without functional changes.\n- `/self-service/browser/flows/requests/recovery?request=abcd` is now\n  `/self-service/recovery/flows?id=abcd` and no longer needs anti-CSRF cookies\n  to be available.\n\nAdditionally, the URL for completing the password and oidc recovery method has\nbeen moved. Given that this endpoint is typically not manually called, you can\nprobably ignore this change:\n\n- `/self-service/browser/flows/recovery/link?request=abcd` is now\n  `/self-service/recovery/methods/link?flow=abcd` without functional changes.\n\nThe Recovery UI Endpoint no longer receives a `?request=abcde` query parameter\nbut instead a `?flow=abcde` query parameter. Functionality did not change\nhowever.\n\nAs part of this change SDK methods have been renamed:\n\n```\n  const kratos = new CommonApi(config.kratos.public)\n  // ...\n- kratos.completeSelfServiceBrowserRecoveryLinkStrategyFlow(req.query.request)\n+ kratos.completeSelfServiceRecoveryFlowWithLinkMethod(req.query.flow)\n```\n\nThis patch requires you to run SQL migrations.\n\nWording has changed from \"Self-Service Settings Request\" to \"Self-Service\nSettings Flow\" to follow community feedback and practice already applied in the\ndocumentation.\n\nThis patch renames several important settings flow endpoints:\n\n- `/self-service/browser/flows/settings` is now `/self-service/settings/browser`\n  without functional changes.\n- `/self-service/browser/flows/requests/settings?request=abcd` is now\n  `/self-service/settings/flows?id=abcd` and no longer needs anti-CSRF cookies\n  to be available.\n\nAdditionally, the URL for completing the password, profile, and oidc settings\nmethod has been moved. Given that this endpoint is typically not manually\ncalled, you can probably ignore this change:\n\n- `/self-service/browser/flows/login/strategies/password?request=abcd` is now\n  `/self-service/login/methods/password?flow=abcd` without functional changes.\n- `/self-service/browser/flows/strategies/oidc?request=abcd` is now\n  `/self-service/methods/oidc?flow=abcd` without functional changes.\n- `/self-service/browser/flows/settings/strategies/profile?request=abcd` is now\n  `/self-service/settings/methods/profile?flow=abcd` without functional changes.\n\nThe Settings UI Endpoint no longer receives a `?request=abcde` query parameter\nbut instead a `?flow=abcde` query parameter. Functionality did not change\nhowever.\n\nAs part of this change SDK methods have been renamed:\n\n```\n  const kratos = new CommonApi(config.kratos.public)\n  // ...\n- kratos.getSelfServiceBrowserSettingsRequest(req.query.request)\n+ kratos.getSelfServiceSettingsFlow(req.query.flow)\n\n  // You will most likely not be using this:\n  const kratos = new PublicApi(config.kratos.public)\n- kratos.completeSelfServiceBrowserSettingsPasswordStrategyFlow //...\n- kratos.completeSelfServiceSettingsFlowWithPasswordMethod //..\n- kratos.completeSelfServiceBrowserSettingsProfileStrategyFlow //...\n- kratos.completeSelfServiceSettingsFlowWithProfileMethod //..\n```\n\nThis patch requires you to run SQL migrations.\n\nThis patch makes the reverse proxy functionality required in prior versions of\nthe self-service UI example obsolete. All examples work now with a simple set up\nand documentation has been added to assist in subdomain scenarios.\n\nThe session field `sid` has been renamed to `id` to stay consistent with other\nAPIs which also use `id` terminology to clarify identifiers. The payload of, for\nexample, `/session/whoami` has changed as follows:\n\n```patch\n  {\n-   \"sid\": \"abcde\",\n+   \"id\": \"abcde\",\n    \"expires_at\": \"...\"\n    \"identity\": {\n      // ..\n    }\n  }\n```\n\nWording has changed from \"Self-Service Registration Request\" to \"Self-Service\nRegistration Flow\" to follow community feedback and practice already applied in\nthe documentation. Additionally, fetching a login flow over the public API no\nlonger requires Anti-CSRF cookies to be sent.\n\nThis patch renames several important registration flow endpoints:\n\n- `/self-service/browser/flows/registration` is now\n  `/self-service/registration/browser` without behavioral change.\n- `/self-service/browser/flows/requests/registration?request=abcd` is now\n  `/self-service/registration/flows?id=abcd` and no longer needs anti-CSRF\n  cookies to be available.\n\nAdditionally, the URL for completing the password registration method has been\nmoved. Given that this endpoint is typically not manually called, you can\nprobably ignore this change:\n\n- `/self-service/browser/flows/registration/strategies/password?request=abcd` is\n  now `/self-service/registration/methods/password?flow=abcd` without functional\n  changes.\n- `/self-service/browser/flows/strategies/oidc?request=abcd` is now\n  `/self-service/methods/oidc?flow=abcd` without functional changes.\n\nThe Registration UI Endpoint no longer receives a `?request=abcde` query\nparameter but instead a `?flow=abcde` query parameter. Functionality did not\nchange however.\n\nAs part of this change SDK methods have been renamed:\n\n```\n  const kratos = new CommonApi(config.kratos.public)\n  // ...\n- kratos.getSelfServiceBrowserRegistrationRequest(req.query.request)\n+ kratos.getSelfServiceRegistrationFlow(req.query.flow)\n```\n\nThis patch requires you to run SQL migrations.\n\nExisting login sessions will no longer be valid because the session cookie data\nmodel changed. If you apply this patch, your users will need to sign in again.\n\nWording has changed from \"Self-Service Login Request\" to \"Self-Service Login\nFlow\" to follow community feedback and practice already applied in the\ndocumentation. Additionally, fetching a login flow over the public API no longer\nrequires Anti-CSRF cookies to be sent.\n\nThis patch renames several important login flow endpoints:\n\n- `/self-service/browser/flows/login` is now `/self-service/login/browser`\n  without functional changes.\n- `/self-service/browser/flows/requests/login?request=abcd` is now\n  `/self-service/login/flows?id=abcd` and no longer needs anti-CSRF cookies to\n  be available.\n\nAdditionally, the URL for completing the password and oidc login method has been\nmoved. Given that this endpoint is typically not manually called, you can\nprobably ignore this change:\n\n- `/self-service/browser/flows/login/strategies/password?request=abcd` is now\n  `/self-service/login/methods/password?flow=abcd` without functional changes.\n- `/self-service/browser/flows/strategies/oidc?request=abcd` is now\n  `/self-service/methods/oidc?flow=abcd` without functional changes.\n\nThe Login UI Endpoint no longer receives a `?request=abcde` query parameter but\ninstead a `?flow=abcde` query parameter. Functionality did not change however.\n\nAs part of this change SDK methods have been renamed:\n\n```\n  const kratos = new CommonApi(config.kratos.public)\n  // ...\n- kratos.getSelfServiceBrowserLoginRequest(req.query.request)\n+ kratos.getSelfServiceLoginFlow(req.query.flow)\n```\n\nThis patch requires you to run SQL migrations.\n\nConfiguraiton value `session.cookie_same_site` has moved to\n`session.cookie.same_site`. There was no functional change.\n\n### Bug Fixes\n\n- Add missing 'recovery' path in oathkeeper access-rules.yml\n  ([#763](https://github.com/ory/kratos/issues/763))\n  ([f180dba](https://github.com/ory/kratos/commit/f180dba2207638e83e4a23ebc213cddaecb5677f))\n- Add missing error handling\n  ([43c1446](https://github.com/ory/kratos/commit/43c14464efa7b736695e2144b031daf6fca87703))\n- Add ory-prettier-styles to main repo\n  ([#744](https://github.com/ory/kratos/issues/744))\n  ([aeaddbc](https://github.com/ory/kratos/commit/aeaddbcb27f89d61b076bdd9ad1739fb1da2ffd9))\n- Add remote help description\n  ([f66bbe1](https://github.com/ory/kratos/commit/f66bbe18cfad1e8725ecbcf6e2843b34c3d5119f))\n- Add serve help description\n  ([2eb072b](https://github.com/ory/kratos/commit/2eb072b71e5602895d4232e197bfd76180fcdcd7))\n- Allow using json with form layout in password registration\n  ([bd2225c](https://github.com/ory/kratos/commit/bd2225c0fff3e0363716d2096346d59046838bb7))\n- Annotate whoami endpoint with cookie and token\n  ([a8a781c](https://github.com/ory/kratos/commit/a8a781c00847c74c65558b55e882e12c1e69d8c8))\n- Bump datadog version to fix build failure\n  ([4dfd322](https://github.com/ory/kratos/commit/4dfd322290313ec8467ebe8b385b56004b2417bd))\n- Change KRATOS_ADMIN_ENDPOINT to KRATOS_ADMIN_URL\n  ([763fdc5](https://github.com/ory/kratos/commit/763fdc56d19d12fa2b83eed2757fbf178d9288b1))\n- Clarify fetch use\n  ([8eb2e6f](https://github.com/ory/kratos/commit/8eb2e6f222788a9a579774772696c77987f3cf97))\n- Complete verification by redirecting to UI with success\n  ([f0ecf51](https://github.com/ory/kratos/commit/f0ecf5144970f666643aa7c00a3f4ca73f4ab047))\n- Correct cookie domain on logout\n  ([#646](https://github.com/ory/kratos/issues/646))\n  ([6d77e04](https://github.com/ory/kratos/commit/6d77e043ce3bec0864b8abdee371a101f68e4335)),\n  closes [#645](https://github.com/ory/kratos/issues/645)\n- Correct help message for import\n  ([a5f46d2](https://github.com/ory/kratos/commit/a5f46d260b43d15f8e77b04cb36c589e103468bf))\n- Correct password and profile swagger annotations\n  ([668c184](https://github.com/ory/kratos/commit/668c1847c4c4236ca28f9dcd5147b523a2f60832))\n- Correct password registration method api spec\n  ([08dd582](https://github.com/ory/kratos/commit/08dd582195cdb6a891d2428ba5d02cd956555e48))\n- Correct PHONY spelling ([#739](https://github.com/ory/kratos/issues/739))\n  ([e3d3617](https://github.com/ory/kratos/commit/e3d3617b8d82812b0ad67cc1cb02ff86c2c0c66c))\n- Cover more test cases for persister\n  ([37d2e08](https://github.com/ory/kratos/commit/37d2e0839b88792733387f26abb98c51bd1e1395))\n- Create decoder only once\n  ([34dc43b](https://github.com/ory/kratos/commit/34dc43b0c75303f88d2c304225c027faf5366c1f))\n- Deprecate packr2 dependency in makefile\n  ([be9a84d](https://github.com/ory/kratos/commit/be9a84dcffbccd5f0e073a38264cf11a404d3b66)),\n  closes [#711](https://github.com/ory/kratos/issues/711)\n  [#750](https://github.com/ory/kratos/issues/750)\n- Do not propagate parent validation error\n  ([bf6093d](https://github.com/ory/kratos/commit/bf6093d442d9779b4df051031565d020ef628ded))\n- Don't resend verification emails once verified\n  ([#583](https://github.com/ory/kratos/issues/583))\n  ([a4d9969](https://github.com/ory/kratos/commit/a4d99694525e65b58d49197c96324b27fb8c31c2)),\n  closes [#578](https://github.com/ory/kratos/issues/578)\n- Enforce endpoint to be set\n  ([171ac18](https://github.com/ory/kratos/commit/171ac18d73eaa0822b45f544a9034d6734400f31))\n- Escape jsx characters in api documentation\n  ([0946094](https://github.com/ory/kratos/commit/09460948a24918b2a84804cafa86cf88189af919))\n- Exit with code 1 on unimplemented CLI commands\n  ([66943d7](https://github.com/ory/kratos/commit/66943d7e5b47fc477a378d8a7cf2b2009ccfceb3))\n- Explicitly ignore fprint return values\n  ([f50e582](https://github.com/ory/kratos/commit/f50e5823f4ee047fdc3e276b80b4fb08c9128d99))\n- Explicitly ignore fprintf results\n  ([a83dc50](https://github.com/ory/kratos/commit/a83dc509970b3be46d832743481357f336fecc35))\n- Fallback to default return url if logout after url is not defined\n  ([#594](https://github.com/ory/kratos/issues/594))\n  ([7edd367](https://github.com/ory/kratos/commit/7edd367dc64a01dbe252ca0ab8cf4d3926a35014))\n- Favor packr2 over pkger\n  ([ac18a45](https://github.com/ory/kratos/commit/ac18a45ea55929c34ca20953e3baa197363483bc)):\n\n  See https://github.com/markbates/pkger/issues/117\n\n- Find and replace \"request\" references\n  ([41fb673](https://github.com/ory/kratos/commit/41fb673e38779cb27d4400f70458617eb7e5b93c))\n- Force exe buildmode for windows CGO\n  ([e017bb5](https://github.com/ory/kratos/commit/e017bb579cd29ad1a634cd552e2601295ff9c104))\n- Html form parse regression issue\n  ([6b07cbb](https://github.com/ory/kratos/commit/6b07cbb657702d36423d1fa66fe8a149222c8772))\n- Ignore x/net false positives\n  ([7044b95](https://github.com/ory/kratos/commit/7044b95f6188c4ffbfff42c666dee6ebaba055c8))\n- Improve debugging output for login hook and restructure files\n  ([dabac40](https://github.com/ory/kratos/commit/dabac40f82407f72071780840f468d0b5b389777))\n- Improve debugging output for registration hook and restructure files\n  ([ec11775](https://github.com/ory/kratos/commit/ec117754f5dd41e5a3a43b3807c05796396ced55))\n- Improve expired error responses\n  ([124a92e](https://github.com/ory/kratos/commit/124a92ee98d62abeb695e1e271ee2536a69d6047))\n- Improve hook tests\n  ([55ba485](https://github.com/ory/kratos/commit/55ba48530a890fdd55ed7da380940f2791148f26))\n- Improve makefile dependency building\n  ([8e1d69a](https://github.com/ory/kratos/commit/8e1d69a024414196b39eb3d419f4850cd547e3b5))\n- Improve pagination when listing identities\n  ([c60bf44](https://github.com/ory/kratos/commit/c60bf440b9c85b4f2e871237e3d7725571151efe))\n- Improve post login hook log and audit messages\n  ([ddd5d5a](https://github.com/ory/kratos/commit/ddd5d5a253d01d2b7b74239a1c7c701759084140))\n- Improve post registration hook log and audit messages\n  ([2495629](https://github.com/ory/kratos/commit/24956296dd91cf6f5b110a17f65f9f60d8a7aa78))\n- Improve registration hook tests\n  ([8163152](https://github.com/ory/kratos/commit/8163152a4d9595b1ea73d2887205e7ba80b016f9))\n- Improve session max-age behavior\n  ([65189fe](https://github.com/ory/kratos/commit/65189fe4a2f84f832240cd67366400e44bb7f09a)),\n  closes [#42](https://github.com/ory/kratos/issues/42)\n- Keep HTML form type on registration error\n  ([#698](https://github.com/ory/kratos/issues/698))\n  ([6c9e756](https://github.com/ory/kratos/commit/6c9e7564efffe1452004d4eda42e1b9ec9feac6b)),\n  closes [#670](https://github.com/ory/kratos/issues/670)\n- Lowercase emails on login\n  ([244b4dd](https://github.com/ory/kratos/commit/244b4dd825b9a2448cc61465cef81bd9dcb051db))\n- Mark flow methods' fields as required\n  ([#708](https://github.com/ory/kratos/issues/708))\n  ([834c607](https://github.com/ory/kratos/commit/834c60738ca7bb26e982ff73134b7b0e85a72076))\n- Merge public and admin login flow fetch handlers\n  ([48c4906](https://github.com/ory/kratos/commit/48c4906a606396d889e057a03dc83b619220db54))\n- Missing write in registration error handler\n  ([3b2af53](https://github.com/ory/kratos/commit/3b2af5397048d63099eace092bf2e50e84a4c610))\n- Properly annotate swagger password parameters\n  ([2ef57c4](https://github.com/ory/kratos/commit/2ef57c4323eb2623f4115bee0e44ee27dd1648a9))\n- Properly fetch identity for session\n  ([7be4086](https://github.com/ory/kratos/commit/7be4086045fddfacc38813ca3dd7fbcc7039391f))\n- Recursive loop on network errors in password validator\n  ([#589](https://github.com/ory/kratos/issues/589))\n  ([b4d5a42](https://github.com/ory/kratos/commit/b4d5a42346510e40222b8eb59b455b585f0a05cf)),\n  closes [#316](https://github.com/ory/kratos/issues/316):\n\n  The old code no error when ignoreNetworkErrors was set to true, but did not\n  set a hash result which caused an infinite loop.\n\n- Remove incorrect security specs\n  ([4c3d46d](https://github.com/ory/kratos/commit/4c3d46dac20363202f0ccd043e1c9d6bf97fb1f8))\n- Remove obsolete tests\n  ([f102f95](https://github.com/ory/kratos/commit/f102f95f420c8a03520602880d096616069c9233)):\n\n  The test is no longer valid as CSRF checks now happen after checking for login\n  sessions in settings flows.\n\n- Remove redirector from code base\n  ([6689ecf](https://github.com/ory/kratos/commit/6689ecf110b11ba15ec39af822906c2b4b17369e))\n- Remove stray debug statements\n  ([a8e1ec4](https://github.com/ory/kratos/commit/a8e1ec42cda6ebc664e9434bb5ba7e4dd7c21b4c))\n- Rename import to put\n  ([8003e0f](https://github.com/ory/kratos/commit/8003e0f42a5d1b77e326d1dba0a70fcd44c704c0))\n- Rename quickstart config files and path\n  ([#671](https://github.com/ory/kratos/issues/671))\n  ([be8b9e5](https://github.com/ory/kratos/commit/be8b9e5f1ca70b1aa06b77bb2ca35644d8cd3c00))\n- Rename quickstart schema file name\n  ([e943c90](https://github.com/ory/kratos/commit/e943c9018a495b39b72ae463fd4727b1798d5ba2))\n- Rename recovery models and generate SDKs\n  ([d764435](https://github.com/ory/kratos/commit/d7644359c39732e0b25f43e122d05c1566fb837b))\n- Resolve and test for missing data when updating flows\n  ([045ecab](https://github.com/ory/kratos/commit/045ecab11ec185ca688a10de75e506fe413afa26))\n- Resolve broken csrf tests\n  ([6befe2e](https://github.com/ory/kratos/commit/6befe2ec08c01c6c9fb397ba119ecebdcecf7db3))\n- Resolve broken docs links\n  ([56f4a39](https://github.com/ory/kratos/commit/56f4a397a715b6c0428ae63baa0d2e4bc936f737))\n- Resolve broken migrations and bump fizz\n  ([1ed9c70](https://github.com/ory/kratos/commit/1ed9c700b946a090bce9587a57eeb9ac64f04c59))\n- Resolve broken OIDC tests and disallow API flows\n  ([9986d8f](https://github.com/ory/kratos/commit/9986d8f818934bd5e073f59bf7a73c6b7a74b6e2))\n- Resolve cookie issues\n  ([6e2b6d2](https://github.com/ory/kratos/commit/6e2b6d2f0ce2fb6df7d3e26d6cc8e755e6593a81))\n- Resolve e2e headless test failures\n  ([82d506e](https://github.com/ory/kratos/commit/82d506e9d35bbbe4c1578f72e5bcf380ebc97142))\n- Resolve e2e test failures\n  ([2627db2](https://github.com/ory/kratos/commit/2627db26089e8f8e4c18782ff59b4cb2068b276f))\n- Resolve failing test cases\n  ([f8647b4](https://github.com/ory/kratos/commit/f8647b4c637b4aee29d68df2336fd216306ec78c))\n- Resolve flaky passwort setting tests\n  ([#582](https://github.com/ory/kratos/issues/582))\n  ([c42d936](https://github.com/ory/kratos/commit/c42d936ef51d2ffb48b491b99988d048442e3b8b)),\n  closes [#581](https://github.com/ory/kratos/issues/581)\n  [#577](https://github.com/ory/kratos/issues/577)\n- Resolve handler testing issue\n  ([4f6bafd](https://github.com/ory/kratos/commit/4f6bafdc84ba4d878c68700dc243cd3cfe8fe530))\n- Resolve identity admin api issues\n  ([#586](https://github.com/ory/kratos/issues/586))\n  ([feef8a7](https://github.com/ory/kratos/commit/feef8a7d4454c1b343c34a96fa4dadd56149b0cd)),\n  closes [#435](https://github.com/ory/kratos/issues/435)\n  [#500](https://github.com/ory/kratos/issues/500):\n\n  This patch resolves several issues that occurred when creating or updating\n  identities using the Admin API. Now, all hooks are running properly and\n  updating privileged properties no longer causes errors.\n\n- Resolve interface type issues\n  ([064b305](https://github.com/ory/kratos/commit/064b305ab31dc003ccb5992eb1ed2804f85085b9))\n- Resolve logout csrf issues ([#761](https://github.com/ory/kratos/issues/761))\n  ([74c0aac](https://github.com/ory/kratos/commit/74c0aac3b94446c3824ae52b04b6f69395938b81))\n- Resolve migratest failures\n  ([e2f34d3](https://github.com/ory/kratos/commit/e2f34d3f411bac042079d7f5425063ef117fae77))\n- Resolve migratest ordering failing tests\n  ([dffecc0](https://github.com/ory/kratos/commit/dffecc0e80810ffae57870fd313ee0103ad3f60c))\n- Resolve migration issues\n  ([b545e15](https://github.com/ory/kratos/commit/b545e15eeaa3e6e1f4a8fe0f8e1890012ac62c94))\n- Resolve panic on `serve`\n  ([ae34155](https://github.com/ory/kratos/commit/ae341555e7b2b622cf58d09d3eb6a78d833dfdcc))\n- Resolve panic when DSN=\"memory\"\n  ([#574](https://github.com/ory/kratos/issues/574))\n  ([05e55f3](https://github.com/ory/kratos/commit/05e55f3584e20ae5d39cfda6e542d4da40d718e4)):\n\n  Executing the migration logic in registry.go cause a panic as the registry is\n  not initalized at that point. Therefore we decided to move the handling to\n  driver_default.go, after the registry has been initialized.\n\n- Resolve pkger issues\n  ([294066c](https://github.com/ory/kratos/commit/294066c41be1d508681caa435afda4858a37b7f1))\n- Resolve remaining testing issues\n  ([af40d93](https://github.com/ory/kratos/commit/af40d933b2f663adb6a537b32546b43ba13ae237))\n- Resolve SQL persistence tester issues\n  ([4952df4](https://github.com/ory/kratos/commit/4952df43e0aba067c06cdedb1fc2c2d9a2a81a40))\n- Resolve swagger issues and regenerate SDK\n  ([be4c7e4](https://github.com/ory/kratos/commit/be4c7e4ea72d2ad7cec67b1d6709858d5a1b3d61))\n- Resolve template loading issue\n  ([145fb20](https://github.com/ory/kratos/commit/145fb204d9a8ca189480f9f2221527ccc62980a0))\n- Resolve test issues introduced by new csrf protection\n  ([625ef5e](https://github.com/ory/kratos/commit/625ef5e4781700449af0c4e4f1f6cb8aa1787764))\n- Resolve verification sql errors\n  ([784da53](https://github.com/ory/kratos/commit/784da53ddefe59aea90254be40ae63e919b4b419))\n- Resolves a bug that prevents sessions from expiring\n  ([#612](https://github.com/ory/kratos/issues/612))\n  ([86b281a](https://github.com/ory/kratos/commit/86b281a46b676d80c8f70bfc42c91d988997c21c)),\n  closes [#611](https://github.com/ory/kratos/issues/611)\n- Revert disabling `swagger flatten` during sdk generation\n  ([98c7915](https://github.com/ory/kratos/commit/98c7915cc493ad99c959244eef68b70bc9baa971))\n- Set correct path for kratos in oathkeeper set up\n  ([414259f](https://github.com/ory/kratos/commit/414259f9383f30b762051c712763d484f5358075))\n- Set quickstart logging to trace\n  ([d3e9192](https://github.com/ory/kratos/commit/d3e919249ae59b449367511d3cc8adef839f31c9))\n- Support browser flows only in redirector\n  ([cab5280](https://github.com/ory/kratos/commit/cab5280859b0fc7fc7fec2b2ec9945f457910b20))\n- Swagger models\n  ([1b5f9ab](https://github.com/ory/kratos/commit/1b5f9abd5d82251ab93a05d4ff26b4c48c8151ca)):\n\n  The `swagger:parameters <id>` definitions for `updateIdentity` and\n  `createIdentity` where defined two times with the same ID. They had some old\n  definition swagger used. The `internal/httpclient` should now work again as\n  expected.\n\n- Tell tls what the smtps server name is\n  ([#634](https://github.com/ory/kratos/issues/634))\n  ([b724038](https://github.com/ory/kratos/commit/b724038a67e84ca71b146bf4b9b044be2dc8c0b4))\n- Type\n  ([e264c69](https://github.com/ory/kratos/commit/e264c69a07e569429b5e835b1e15c318eff23339))\n- Update cli documentation examples\n  ([216ea7f](https://github.com/ory/kratos/commit/216ea7f926798ff03d211447200919f9ef3c8b39))\n- Update contrib samples\n  ([79d24b4](https://github.com/ory/kratos/commit/79d24b4472017a75854cce4a45b4c762e5390a67))\n- Update crdb quickstart version\n  ([249a6ba](https://github.com/ory/kratos/commit/249a6bae32ccaa6cf002eaab921388e8cb10e58f))\n- Update import description\n  ([aef1e1a](https://github.com/ory/kratos/commit/aef1e1acf757637590fe19644952a44d1994ba18))\n- Update quickstart kratos config\n  ([e3246e5](https://github.com/ory/kratos/commit/e3246e5d56b95750529239663bab03168789cc09))\n- Update recovery token field and column names\n  ([42abfa1](https://github.com/ory/kratos/commit/42abfa1dea2a6291c5b723baf25f35a66f2af835))\n- Update status help description\n  ([b147831](https://github.com/ory/kratos/commit/b1478316d2f601843133fd33d75c3b047384f283))\n- Update swagger names and fix broken tests\n  ([85b7fb1](https://github.com/ory/kratos/commit/85b7fb1d466bc4dcee97ad75cc92b8bea8e44d9f))\n- Update version help description\n  ([8bf4a79](https://github.com/ory/kratos/commit/8bf4a79064a93cb53ef8aee3433b24602bc9f30a))\n- Use and test for csrf tokens and prevent api misuse\n  ([a4e3bc5](https://github.com/ory/kratos/commit/a4e3bc55e43ba42582a33551c1cc2e83ecd865fa))\n- Use correct HTTP method for password login\n  ([4f4fcee](https://github.com/ory/kratos/commit/4f4fcee8931ab4998e974106b8d88e0c61736e3f))\n- Use correct log message\n  ([53c384a](https://github.com/ory/kratos/commit/53c384a542a583259a75315b2602cf4fb41a0ef0))\n- Use correct redirection for registration\n  ([8d47113](https://github.com/ory/kratos/commit/8d47113a5f7c0c25dc5f92c683b560763cfd47c9))\n- Use correct security annotation\n  ([c9bebe0](https://github.com/ory/kratos/commit/c9bebe00452a73d1c831831e5a95cb4ed8de37b9))\n- Use correct swagger tags and regenerate\n  ([df99d8c](https://github.com/ory/kratos/commit/df99d8cbe6e0f2f6a5da872f66db557b2a5e9f70))\n- Use helpers to create flow\n  ([aba8610](https://github.com/ory/kratos/commit/aba861097d2c67ce9ebff85df59fce8018862516))\n- Use nosurf fork to address VerifyToken bug\n  ([cd84e51](https://github.com/ory/kratos/commit/cd84e51b7b1861ca9bd2312a4dfc5e84afd890cf))\n- Use params per_page and page for pagination\n  ([5dfb6e3](https://github.com/ory/kratos/commit/5dfb6e32c44420ed49d652733b9099a41c9347f2))\n- Use proper pwd in makefile\n  ([52e22c3](https://github.com/ory/kratos/commit/52e22c3b5c0130afd3e235aba9847389369f435e))\n- Use public instead of common sdk\n  ([dcb4a36](https://github.com/ory/kratos/commit/dcb4a36f9fb3c25ace9a252b7e05f7ab71d2e21f))\n- Use relative threshold to judge longest common substring in password policy\n  ([#585](https://github.com/ory/kratos/issues/585))\n  ([3e9f8cc](https://github.com/ory/kratos/commit/3e9f8cce4b058b05d69c73fff514f3b8e46c2be3)),\n  closes [#581](https://github.com/ory/kratos/issues/581)\n- Whoami returns 401 not 403\n  ([3b3b78c](https://github.com/ory/kratos/commit/3b3b78c04bbbbb7b7fb05635d96b4f7c7fa7776f)),\n  closes [#729](https://github.com/ory/kratos/issues/729)\n\n### Code Generation\n\n- Pin v0.5.0-alpha.1 release commit\n  ([557d37d](https://github.com/ory/kratos/commit/557d37d1139adb14a25abe40d0174d47d4e18fee))\n\n### Code Refactoring\n\n- Add flow methods to verification\n  ([00ee828](https://github.com/ory/kratos/commit/00ee828842bd4bc6f917ba2446b1374d28b62000)):\n\n  Completely refactors the verification flow to support other methods. The\n  original email verification flow now moved to the \"link\" method also used for\n  recovery.\n\n  Additionally, several upstream bugs in gobuffalo/pop and gobuffalo/fizz have\n  been addressed, patched, and merged which improves support for SQLite and\n  CockroachDB migrations:\n  - https://github.com/gobuffalo/fizz/pull/97\n  - https://github.com/gobuffalo/fizz/pull/96\n\n- Add method and rename request to flow\n  ([006bf56](https://github.com/ory/kratos/commit/006bf56671d8162cdb5bcce630c027b67935263d))\n- Change oidc callback URL\n  ([36d9380](https://github.com/ory/kratos/commit/36d9380b2123d27219c908b51ad97574ee11bc57))\n- Complete login flow refactoring\n  ([ad2b3db](https://github.com/ory/kratos/commit/ad2b3db4493085b80889cbc0dce9562288ec6896))\n- Dry up login.NewFlow\n  ([f261c44](https://github.com/ory/kratos/commit/f261c442dbe74e3b9887193b74e36fe70306f9d8))\n- Improve CSRF infrastructure\n  ([7e367e7](https://github.com/ory/kratos/commit/7e367e7f45481147d5c231d0ea8cbb30b738226f))\n- Improve login test reuse\n  ([b4184e5](https://github.com/ory/kratos/commit/b4184e5f1525a9918bc795f2353b186141ce5399))\n- Improve NewFlowExpiredError\n  ([1caefac](https://github.com/ory/kratos/commit/1caefac6e0e82aa2b12458ef16d7f5af24014bf9))\n- Improve registration tests with testhelpers\n  ([9bf4530](https://github.com/ory/kratos/commit/9bf45303be908449b78c68c7382eab5cfc5c40fa))\n- Improve selfservice method tests\n  ([df4d06d](https://github.com/ory/kratos/commit/df4d06d553852cdb8b914810c19bdd0fcc845c9c))\n- Improve settings helper functions\n  ([fda17ca](https://github.com/ory/kratos/commit/fda17ca5ea7824c4bf5010218cace7d5fbc7ad5b))\n- Move samesite config to cookie parent-key\n  ([753eb86](https://github.com/ory/kratos/commit/753eb86c904c4af9e7d91e46ff4c836dcce35807))\n- Moved clihelpers to ory/x ([#756](https://github.com/ory/kratos/issues/756))\n  ([6ccffa8](https://github.com/ory/kratos/commit/6ccffa8a1cc5b9fd33435187720257bb66323546)):\n\n  Contributes to https://github.com/ory/hydra/issues/2124.\n\n- Profile settings method is now API-able\n  ([c5f361f](https://github.com/ory/kratos/commit/c5f361ff418336cfcaa452eded4bd61132808b16))\n- Remove common keyword from API spec\n  ([6619562](https://github.com/ory/kratos/commit/6619562667ef0e363d14c57cfbcd15c16f292853))\n- Remove need for reverse proxy in selfservice-ui\n  ([beb4c32](https://github.com/ory/kratos/commit/beb4c3284e552fe51c3a8cebb20a8c2bfc07cdf8)),\n  closes [#661](https://github.com/ory/kratos/issues/661)\n- Rename `session.sid` to `session.id`\n  ([809fe73](https://github.com/ory/kratos/commit/809fe7334e4a308405c1f03ada1dbef6ed33c01a))\n- Rename login request to login flow\n  ([9369d1b](https://github.com/ory/kratos/commit/9369d1bb637fc80b5d5980140693d5bcac0c76bb)),\n  closes [#635](https://github.com/ory/kratos/issues/635):\n\n  As part of this change, fetching a login flow over the public API no longer\n  requires Anti-CSRF cookies to be sent.\n\n- Rename LoginRequestErrorHandler to LoginFlowErrorHandler\n  ([66ae029](https://github.com/ory/kratos/commit/66ae029f49aecdfba5fa6905cfccfcdad992dd5a))\n- Rename package recoverytoken to link\n  ([f87fb54](https://github.com/ory/kratos/commit/f87fb549f6d8a10ba5adffddeb2fe12060d520ab))\n- Rename recovery request to flow internally\n  ([16c5618](https://github.com/ory/kratos/commit/16c5618644e78cf1081f966e01b570a36eea709b))\n- Rename recovery request to recovery flow\n  ([b0f433d](https://github.com/ory/kratos/commit/b0f433d4cb65d79acba789394d828663e873a833)),\n  closes [#635](https://github.com/ory/kratos/issues/635):\n\n  As part of this change, fetching a login flow over the public API no longer\n  requires Anti-CSRF cookies to be sent.\n\n- Rename registration request to flow\n  ([8437ebc](https://github.com/ory/kratos/commit/8437ebcf4deb2844562ec701af3bbbb2a9b5dea4))\n- Rename registration request to registration flow\n  ([0470956](https://github.com/ory/kratos/commit/0470956128d03921d8554c43af2c5a0003abe82f)),\n  closes [#635](https://github.com/ory/kratos/issues/635):\n\n  As part of this change, fetching a registration flow over the public API no\n  longer requires Anti-CSRF cookies to be sent.\n\n- Rename request_lifespan to lifespan\n  ([#677](https://github.com/ory/kratos/issues/677))\n  ([3c8d5e0](https://github.com/ory/kratos/commit/3c8d5e02b04686a1e0bfbd28caa0bc536e3414e4)),\n  closes [#666](https://github.com/ory/kratos/issues/666)\n- Rename strategies to methods\n  ([8985189](https://github.com/ory/kratos/commit/89851896d563518909bc2b47a7ff91683eec4958)):\n\n  This patch renames `strategies` such as \"Username/Email & Password\" to\n  methods.\n\n- Rename verify to verificaiton\n  ([#597](https://github.com/ory/kratos/issues/597))\n  ([0ecd69a](https://github.com/ory/kratos/commit/0ecd69a60f741fc334c9b060b6aeaafc39e048b1))\n- Replace all occurrences of login request to flow\n  ([1b3c491](https://github.com/ory/kratos/commit/1b3c49174a7a2eff51dd531f3a49afc15c31c536))\n- Replace all registration request occurrences with registration flow\n  ([308ef47](https://github.com/ory/kratos/commit/308ef47846c9ab4f18a598ef6ef78514fad77c42))\n- Replace packr2 with pkger fork\n  ([4e2acae](https://github.com/ory/kratos/commit/4e2acae7c4fc17880cf88ef05cf7cca5f20f5be3))\n- Restructure login package\n  ([c99e2a2](https://github.com/ory/kratos/commit/c99e2a2f23c3c2aabaae55de67e40ab7fb2dd307))\n- Use session token as cookie identifier\n  ([60fd9c2](https://github.com/ory/kratos/commit/60fd9c2efa881fcdd769a8967abe73c05a198868))\n\n### Documentation\n\n- Add administrative user management guide\n  ([b97e0c6](https://github.com/ory/kratos/commit/b97e0c69bb1115bdec88b218e8cdda34f137d798))\n- Add code samples to session checking\n  ([eba8eda](https://github.com/ory/kratos/commit/eba8eda70423aa802eace278889a5e8d2e0bc513))\n- Add configuring introduction\n  ([#630](https://github.com/ory/kratos/issues/630))\n  ([b8cfb35](https://github.com/ory/kratos/commit/b8cfb351c2dca783e355f39d25ce17b65fef7dd4))\n- Add descriptions to cobra commands\n  ([607b76d](https://github.com/ory/kratos/commit/607b76d109d1fa519235fe9d6af78c8315b9c4fc))\n- Add documentation for configuring cookies\n  ([e3dbc8a](https://github.com/ory/kratos/commit/e3dbc8acc055f6e2d78bc959be7356f9a66ac90f)),\n  closes [#516](https://github.com/ory/kratos/issues/516)\n- Add domain, subdomain, multi-domain cookie guides\n  ([3eb1e59](https://github.com/ory/kratos/commit/3eb1e5987df56993c792684a6a2bc11f5eb570b8)),\n  closes [#661](https://github.com/ory/kratos/issues/661)\n- Add github video tutorial ([#622](https://github.com/ory/kratos/issues/622))\n  ([0c4222c](https://github.com/ory/kratos/commit/0c4222c0d12df4e971fd7e5099006484e0bcb317))\n- Add guide for cors\n  ([a8ae759](https://github.com/ory/kratos/commit/a8ae759565d94ebd9d0f758b7eb6efbddf486372))\n- Add guide for cors\n  ([91fd278](https://github.com/ory/kratos/commit/91fd278d1a6720576998b115dedb882b90915561))\n- Add guide for dealing with login sessions\n  ([4e2718c](https://github.com/ory/kratos/commit/4e2718c779031c0e3b877e9df1747ccb2371927b))\n- Add identity state\n  ([fb4aedb](https://github.com/ory/kratos/commit/fb4aedb9a95367e25080491b54aab11de491d819))\n- Add login session to navbar\n  ([b212d64](https://github.com/ory/kratos/commit/b212d6484e40c9f2cce10f2ba4aaf4e2a72f03a1))\n- Add milestones to sidebar\n  ([aae13ec](https://github.com/ory/kratos/commit/aae13ec141a2c315aff1a53aa005bb9465efcdc0))\n- Add missing GitLab provider to the list of supported OIDC providers\n  ([#766](https://github.com/ory/kratos/issues/766))\n  ([a43ed33](https://github.com/ory/kratos/commit/a43ed335262fd542f349224aef918af5263c384d))\n- Add missing TOC entries ([#748](https://github.com/ory/kratos/issues/748))\n  ([bd7edfb](https://github.com/ory/kratos/commit/bd7edfbebd19f01af337c34293ebc2865f2b077d))\n- Add pagination docs\n  ([7fe0901](https://github.com/ory/kratos/commit/7fe0901ee5d0e829e110bd0c4fdecb24bfc27768))\n- Add secret key rotation guide\n  ([3d6e21a](https://github.com/ory/kratos/commit/3d6e21af2f726944468299c326600a8ab0e4e885))\n- Add sequence diagrams for browser/api flows\n  ([590d767](https://github.com/ory/kratos/commit/590d767352b9253b7550eaba56fea99400399cd7))\n- Add session hook to ssi guide\n  ([#623](https://github.com/ory/kratos/issues/623))\n  ([1bbed39](https://github.com/ory/kratos/commit/1bbed390ffedd811afdb5fcfe69047554419d8ce))\n- Add terminology section\n  ([29b81a7](https://github.com/ory/kratos/commit/29b81a78fcf880cd6d9d3b2cbb03f955b701ffbd))\n- Add theme helpers and decouple mermaid\n  ([7c3eb32](https://github.com/ory/kratos/commit/7c3eb32df5d9287845258bf25d6719733f6c4227))\n- Add video to OIDC guide ([#619](https://github.com/ory/kratos/issues/619))\n  ([f286980](https://github.com/ory/kratos/commit/f286980c29ce8460ba550e5d74b8dee23602e920))\n- Added sidebar cli label\n  ([5d24a29](https://github.com/ory/kratos/commit/5d24a2998b412159295feca40421b8b11cf02274)):\n\n  `clidoc.Generate` expects to find an entry under `sidebar.json/Reference` that\n  contains the substring \"CLI\" in it's label. Because that was missing, a new\n  entry was appended on every regeneration of the file.\n\n- Added sidebar item ([#639](https://github.com/ory/kratos/issues/639))\n  ([8574761](https://github.com/ory/kratos/commit/857476112d12b8ab79ef49054452a950ff81bc23)):\n\n  Added Kratos Video Tutorial Transcripts document to sidebar.\n\n- Added transcript ([#627](https://github.com/ory/kratos/issues/627))\n  ([cec7f1f](https://github.com/ory/kratos/commit/cec7f1fc4955b02d21d772e748ec791f31bad24e)):\n\n  Added Login with Github Transcript\n\n- Adds twitch oidc provider guide\n  ([#760](https://github.com/ory/kratos/issues/760))\n  ([339e622](https://github.com/ory/kratos/commit/339e62202170bf21d469d1a2bfe6b053a78c374d))\n- Bring oidc docs up to date\n  ([7d0e470](https://github.com/ory/kratos/commit/7d0e47058cd6dca1763f01e45ed46cee49321240))\n- Changed transcript location ([#642](https://github.com/ory/kratos/issues/642))\n  ([c52764d](https://github.com/ory/kratos/commit/c52764d4394181b24dffbf8301418530ba5dbcc2)):\n\n  Changed the location so it is in the right place.\n\n- Clarify 302 redirect on expired login flows\n  ([ca31b53](https://github.com/ory/kratos/commit/ca31b53837e8eb2b811bf384da3724fdf61b423b))\n- Clarify api flow use\n  ([a38b4a1](https://github.com/ory/kratos/commit/a38b4a1684cfbc385ca21005c91a47e57df5a35d))\n- Clarify feature-set\n  ([2266ae7](https://github.com/ory/kratos/commit/2266ae7ea92207cdc4fcb58ef1384e287a5b34dc))\n- Clarify kratos config snippet\n  ([e7732f3](https://github.com/ory/kratos/commit/e7732f3283d82a1678076cd2463ef5ff33dd30ea))\n- Clean up docs and correct samples\n  ([8627ec5](https://github.com/ory/kratos/commit/8627ec58edb15118e0c4ce2cfcef7a5573482c5a))\n- Complete registration documentation\n  ([b3af02b](https://github.com/ory/kratos/commit/b3af02b0ea4cbf16ea282b7ce5f5057d99044ac3))\n- Consistent formatting of badges\n  ([#745](https://github.com/ory/kratos/issues/745))\n  ([b391a03](https://github.com/ory/kratos/commit/b391a036f3b49cd6c1915444c9f26dead4855a7c))\n- Correct settings and verification redir\n  ([30e25e7](https://github.com/ory/kratos/commit/30e25e7287a2579da99a6a6dc2f890e7e06fcc81))\n- Docker image documentation ([#573](https://github.com/ory/kratos/issues/573))\n  ([bfe032e](https://github.com/ory/kratos/commit/bfe032e2b6bfd8b9415d466011bdd7e36efa4146))\n- Document APi flows in self-service overview\n  ([71ed0bd](https://github.com/ory/kratos/commit/71ed0bd2027d61c2e5cebf6b031fe66469bdf97e))\n- Document how to check for login sessions\n  ([9ad73b8](https://github.com/ory/kratos/commit/9ad73b8dab06c6796933448cb93ae4e55d9f2c51))\n- Explain high-level API and browser flows\n  ([fe3ee0a](https://github.com/ory/kratos/commit/fe3ee0a0c8681a99dc6b61b90cff547c6a7fc6d2)),\n  closes [hi#level](https://github.com/hi/issues/level)\n- Fix logout url ([#593](https://github.com/ory/kratos/issues/593))\n  ([f0971d4](https://github.com/ory/kratos/commit/f0971d44a911caed8a6071358fa6b7ebc0fcf145))\n- Fix sidebar missing comment\n  ([d90123a](https://github.com/ory/kratos/commit/d90123ae31edbae6a39a1f039cc9362f9acdfdcb))\n- Fix typo\n  ([c2f94da](https://github.com/ory/kratos/commit/c2f94daa4143a70c13426ccd5366ec891182e4d0))\n- Fix typo on index page ([#656](https://github.com/ory/kratos/issues/656))\n  ([907add5](https://github.com/ory/kratos/commit/907add5edb526adb4de57d35da16929ac08041e1))\n- Fix url of admin-api /recovery/link\n  ([#650](https://github.com/ory/kratos/issues/650))\n  ([e68c7cb](https://github.com/ory/kratos/commit/e68c7cbdc2191565570d0ee6812318ac9ad3421d))\n- Fixed link\n  ([c2aebbd](https://github.com/ory/kratos/commit/c2aebbd898f38388d849954938d56212c88d280f))\n- Fixed link ([#629](https://github.com/ory/kratos/issues/629))\n  ([ad1276f](https://github.com/ory/kratos/commit/ad1276f2b2cf3cbbecba4dee1d6d433999286946))\n- Fixed typos/readability ([#620](https://github.com/ory/kratos/issues/620))\n  ([7fd3ce0](https://github.com/ory/kratos/commit/7fd3ce0d8c52346ba3504ce5777321937baf8d1e)):\n\n  Fixed a few typos, and moved some sentences around to improve readability.\n\n- Fixed typos/readability ([#621](https://github.com/ory/kratos/issues/621))\n  ([c4fc75f](https://github.com/ory/kratos/commit/c4fc75f7dca59fa8f31d068f57179f49bf798b6a))\n- Import mermaid ([#696](https://github.com/ory/kratos/issues/696))\n  ([6f75004](https://github.com/ory/kratos/commit/6f750047d41add6bd2d30adb1c654181c9636d2d))\n- Improve charts and examples in self-service overview\n  ([312c91d](https://github.com/ory/kratos/commit/312c91de3ae3c086f836ec3928735d787ad40dde))\n- Improve documentation and add tests\n  ([3dde956](https://github.com/ory/kratos/commit/3dde956e09d1f3f6411046b12f8684d8760f9b91))\n- Improve long messages and render cli documentation\n  ([e5fc02f](https://github.com/ory/kratos/commit/e5fc02ff22836e074a1dfca043d4b4b8ad64c747))\n- Make assumptions neutral in concepts overview\n  ([e89d980](https://github.com/ory/kratos/commit/e89d98099bd3fc5c8361f9015e44668494211152))\n- Move development section\n  ([2e6f643](https://github.com/ory/kratos/commit/2e6f6430f88105efd5618482043809c6d643216b))\n- Move hooks\n  ([c02b588](https://github.com/ory/kratos/commit/c02b58867ee2c0a386b2b741375ec8cd76122461))\n- Move to json sidebar\n  ([504af3b](https://github.com/ory/kratos/commit/504af3b89d728eb11bf42f4a2037c78b3b7cb788))\n- Password login and registration methods for API clients\n  ([5a44356](https://github.com/ory/kratos/commit/5a4435643ae3463df85458f22f87730c11af10ab))\n- Prettify all files ([#743](https://github.com/ory/kratos/issues/743))\n  ([d9d1bfd](https://github.com/ory/kratos/commit/d9d1bfdff70ad835629a2dba00579925fcb3094d))\n- Quickstart next steps ([#676](https://github.com/ory/kratos/issues/676))\n  ([ee9dd0d](https://github.com/ory/kratos/commit/ee9dd0d58a4146a0e131f6a7b74943bb39d26c0b)):\n\n  Added a section outlining some easy config changes, that users can apply to\n  the quickstart to test out different scenarios and configurations.\n\n- Refactor login and registration documentation\n  ([c660a04](https://github.com/ory/kratos/commit/c660a04ed6a70aefca18896662331fcc5d1919cf))\n- Refactor settings and recovery documentation\n  ([11ca9f7](https://github.com/ory/kratos/commit/11ca9f7d1b858dcda3a96e1e1d2607ba64f7fbbe))\n- Refactor verification docs\n  ([70f2789](https://github.com/ory/kratos/commit/70f2789363773fccc4bd8691597ff588ac6892c6))\n- Regenerate clidocs with up-to-date binary\n  ([e53289c](https://github.com/ory/kratos/commit/e53289c8e9f34a02ec66ec7ee03e2269a4a13c42))\n- Remove `make tools` task\n  ([ec6e664](https://github.com/ory/kratos/commit/ec6e6641234191d4eb39e1ad17bc7fcc03c2a0b5)),\n  closes [#711](https://github.com/ory/kratos/issues/711)\n  [#750](https://github.com/ory/kratos/issues/750):\n\n  This task does not exist any more and the dependency building is much smarter\n  now.\n\n- Remove contraction ([#747](https://github.com/ory/kratos/issues/747))\n  ([cd4f21d](https://github.com/ory/kratos/commit/cd4f21dbfa2b3824468146677f542fbab2417c42))\n- Remove duplicate word\n  ([b84e659](https://github.com/ory/kratos/commit/b84e659af29aa1b129f33ccf5ca9e0d54353c019))\n- Remove duplicate word ([#700](https://github.com/ory/kratos/issues/700))\n  ([a12100e](https://github.com/ory/kratos/commit/a12100e7644b535c4bd3073e03c48229bb81e7b2))\n- Remove react native guide for now\n  ([daa5f2e](https://github.com/ory/kratos/commit/daa5f2e3de3fe8380a91f594e034afcadc6e6ba5))\n- Rename self service and add admin section\n  ([639c424](https://github.com/ory/kratos/commit/639c424d3bde0557f7edd7edc489a476f1aa60b3))\n- Replace ampersand ([#749](https://github.com/ory/kratos/issues/749))\n  ([8337b80](https://github.com/ory/kratos/commit/8337b80a13e8cf0cb2848241c93bb151420ac6a4))\n- Resolve regression issues\n  ([0470fd7](https://github.com/ory/kratos/commit/0470fd734fb30170033e10758d99cf5711c80eb1))\n- Resolve typo in message IDs\n  ([562cfc4](https://github.com/ory/kratos/commit/562cfc4392ba1c9c1fb8854ea0ac85bd44d0fac9))\n- Resolve typo in message IDs ([#607](https://github.com/ory/kratos/issues/607))\n  ([f7688f0](https://github.com/ory/kratos/commit/f7688f0ab07b579a375ce4cc25361b360e82dd88))\n- Update cli docs\n  ([085efca](https://github.com/ory/kratos/commit/085efcae895b3aa3c76c819dca0f080ea79d57cd))\n- Update link to mfa issue\n  ([d03a706](https://github.com/ory/kratos/commit/d03a706307be21b83d18601223fb0d1430459a29))\n- Update links\n  ([a06fd88](https://github.com/ory/kratos/commit/a06fd88b0dcb747808ffea450bf1ac74dd941769))\n- Update MFA link to issue ([#690](https://github.com/ory/kratos/issues/690))\n  ([7a744ad](https://github.com/ory/kratos/commit/7a744ad7b62540dd5789aee8532c1f97ddcab32d)):\n\n  MFA issue was pushed to a later milestone. Update the documentation to point\n  to the issue instead of the milestone.\n\n- Update repository templates\n  ([f422485](https://github.com/ory/kratos/commit/f4224852ceeb054405251b21895efa493e1abc9c))\n- Update repository templates ([#678](https://github.com/ory/kratos/issues/678))\n  ([bdb6875](https://github.com/ory/kratos/commit/bdb6875e55aed454cda061969e1dd4f712e09bb5))\n- Update sidebar\n  ([ea15c20](https://github.com/ory/kratos/commit/ea15c2093fc66e4cfc0a66aabf7dfad6965777dc))\n- Update ts examples\n  ([65cb46e](https://github.com/ory/kratos/commit/65cb46e57595b920bd6544f9a9a4f7b886462be0))\n- Use correct id for multi-domain-cookies\n  ([b49288a](https://github.com/ory/kratos/commit/b49288a351647c91a3c7d4a62537146d4a9f1bd0))\n- Use correct path in 0.4 docs\n  ([9fcaac4](https://github.com/ory/kratos/commit/9fcaac4048e05500d0456eb3cd9cd11cc123e370)),\n  closes [#588](https://github.com/ory/kratos/issues/588)\n- Use NYT Capitalization for all Swagger headlines\n  ([#675](https://github.com/ory/kratos/issues/675))\n  ([6c96429](https://github.com/ory/kratos/commit/6c9642959dab8cf042ad227711609d5726328394)),\n  closes [#664](https://github.com/ory/kratos/issues/664)\n\n### Features\n\n- Add ability to configure session cookie domain/path\n  ([faeb332](https://github.com/ory/kratos/commit/faeb3328dab343c6ef3974065ba0c5c590a8817e)),\n  closes [#516](https://github.com/ory/kratos/issues/516)\n- Add and improve settings testhelpers\n  ([10a43fc](https://github.com/ory/kratos/commit/10a43fc518bd5c764712b549e6d35bf7159d757a))\n- Add bearer helper\n  ([ec6ca20](https://github.com/ory/kratos/commit/ec6ca20279d839dc10e7e3bc80e0442a630e586b))\n- Add config version schema ([#608](https://github.com/ory/kratos/issues/608))\n  ([d218662](https://github.com/ory/kratos/commit/d218662388ef4fb7ea3bfee7b29c5cc8d34f1c8c)),\n  closes [#590](https://github.com/ory/kratos/issues/590)\n- Add discord oidc provider ([#767](https://github.com/ory/kratos/issues/767))\n  ([487296d](https://github.com/ory/kratos/commit/487296dd39d2e59d61b63f00f3d61fea9b8aed8c))\n- Add enum to form field type\n  ([96028d8](https://github.com/ory/kratos/commit/96028d8c80414cdcea177150ba6e986d0ecb29c6))\n- Add flow type to login\n  ([ce9133b](https://github.com/ory/kratos/commit/ce9133b0ff6d03738a5d27cf9c6a213496d75772))\n- Add HTTP request flow validator\n  ([1a6e847](https://github.com/ory/kratos/commit/1a6e84774b65ee7be9294baaaff77192cec8f0f2))\n- Add new prometheus metrics endpoint\n  [#672](https://github.com/ory/kratos/issues/672)\n  ([#673](https://github.com/ory/kratos/issues/673))\n  ([0f5c436](https://github.com/ory/kratos/commit/0f5c436ce6e4aa78ca52ae63e58812e6703a1ab7)):\n\n  Adds endpoint `/metrics` for prometheus metrics collection to the Admin API\n  Endpoint.\n\n- Add nocache helpers\n  ([54dcc4d](https://github.com/ory/kratos/commit/54dcc4da2ff22bdb17e53dd6eac1c0bd54a20390))\n- Add pagination tests\n  ([e3aa81b](https://github.com/ory/kratos/commit/e3aa81b7da55108f43ea6e16c817c97e2f8a1d50))\n- Add session token security definition\n  ([d36c26f](https://github.com/ory/kratos/commit/d36c26f2edd66ddbd8338de4901957a9b9b7342e)):\n\n  Adds the new Session Token as a Swagger security definition to allow setting\n  the session token as a Bearer token when calling `/sessions/whoami`.\n\n- Add stub errors to errorx\n  ([5d452bb](https://github.com/ory/kratos/commit/5d452bb582e6a9e3b893424ec135d0cbdf875659)),\n  closes [#610](https://github.com/ory/kratos/issues/610)\n- Add test helper for fetching settings requests\n  ([3646383](https://github.com/ory/kratos/commit/36463838d81d8b108aa9ded8c1ec6bc8f48f2267))\n- Add tests and helpers to test recovery/verifiable addresses\n  ([#579](https://github.com/ory/kratos/issues/579))\n  ([29979e6](https://github.com/ory/kratos/commit/29979e6c4934b71c7fb158cfa5b85e97be3ea8fc)),\n  closes [#576](https://github.com/ory/kratos/issues/576)\n- Add tests to cover auth\n  ([c9d3a15](https://github.com/ory/kratos/commit/c9d3a1525cc74976d16b483e0ab5c48909b84022))\n- Add texts for settings\n  ([795548c](https://github.com/ory/kratos/commit/795548c25507c34c7fc37ce1c1a8ecc076c34ef4))\n- Add the already declared (and settable) tracer as a middleware\n  ([#614](https://github.com/ory/kratos/issues/614))\n  ([e24fffe](https://github.com/ory/kratos/commit/e24fffe3f13c353e3c07214c1e056a849533a9f6))\n- Add token to session\n  ([08c8c78](https://github.com/ory/kratos/commit/08c8c7837dbf799e6ba01d1820812c9e792d7850))\n- Add type to all flows in SQL\n  ([5515776](https://github.com/ory/kratos/commit/551577659f6a416ff6ef032c35af224b517df413))\n- Allow import/validation of arrays\n  ([d11ac32](https://github.com/ory/kratos/commit/d11ac32db6ddc0dce73067ffe7d4d0a734a3f991))\n- Bump cli and migration render tasks\n  ([6dcb42a](https://github.com/ory/kratos/commit/6dcb42a487476371a545b72f7ee7e820b815bbee))\n- Finalize tests for registration flow refactor\n  ([8e52c3a](https://github.com/ory/kratos/commit/8e52c3a99bd39b3429ff476340b5df49e0a85707))\n- Finish off client cli\n  ([36d60c7](https://github.com/ory/kratos/commit/36d60c7e7bc38d83726b4b4a3061ba6353dd1978))\n- Implement administrative account recovery\n  ([f5f9c43](https://github.com/ory/kratos/commit/f5f9c43e10dd3a9547e87776164d2d4a171f35ce))\n- Implement API flow for recovery link method\n  ([d65bf66](https://github.com/ory/kratos/commit/d65bf66781bdd2fae73e75c0ba39287b1575c45a))\n- Implement API-based tests for password method settings flows\n  ([60664aa](https://github.com/ory/kratos/commit/60664aaf05dbd6b228f420688d0171e5789246be))\n- Implement max-age for session cookie\n  ([2e642ff](https://github.com/ory/kratos/commit/2e642ff13c59a7e23babe9209c1a114ef0163bad)),\n  closes [#326](https://github.com/ory/kratos/issues/326)\n- Implement tests and anti-csrf for API settings flows\n  ([8b8b6e5](https://github.com/ory/kratos/commit/8b8b6e5367e05f49950b851ea6834a9f18e896e7))\n- Implement tests for new migrations\n  ([e08ece9](https://github.com/ory/kratos/commit/e08ece9bb1c8c52580c15cf9152b4203821a0a0e))\n- Improve test readability for password method\n  ([a896d9b](https://github.com/ory/kratos/commit/a896d9b55596d2925941a6b6a91b8a6e4ef2caa1))\n- Log successful hook execution\n  ([f6026cf](https://github.com/ory/kratos/commit/f6026cfb0418767d99d18cd50529c2b71b21d775))\n- Log successful hook execution\n  ([1e7d044](https://github.com/ory/kratos/commit/1e7d044603b204632d2ec73c2e54db896992300b))\n- Make login error handle JSON aware\n  ([88f581f](https://github.com/ory/kratos/commit/88f581ff40a183cb96b5fb6d1ba398c58a9792d1))\n- Make password settings method API-able\n  ([0cf6027](https://github.com/ory/kratos/commit/0cf60274f87f098d5eb57531f5071cd407b65f4d))\n- Make public cors configurable\n  ([863a0d4](https://github.com/ory/kratos/commit/863a0d4f4696b05209b16f2e0c3daa9e8f4c1945)),\n  closes [#712](https://github.com/ory/kratos/issues/712)\n- Oidc provider claims config option\n  ([#753](https://github.com/ory/kratos/issues/753))\n  ([bf94a40](https://github.com/ory/kratos/commit/bf94a40acd52128303c0b878ddb92d56abc4ceaf)),\n  closes [#735](https://github.com/ory/kratos/issues/735)\n- Reply with cache-control: 0 for browser-facing APIs\n  ([1a45b53](https://github.com/ory/kratos/commit/1a45b5341e0ab4580208bfb6a505859d1e5d2faf)),\n  closes [#360](https://github.com/ory/kratos/issues/360)\n- Schemas are now static assets\n  ([1776d58](https://github.com/ory/kratos/commit/1776d58278c42094b2c703e269a5901a96617051))\n- Support and document api flow in session issuer hook\n  ([91f3cc7](https://github.com/ory/kratos/commit/91f3cc7a559b1ea1279216f8dc81abd8e6f73776))\n- Support application/json in registration\n  ([3476b97](https://github.com/ory/kratos/commit/3476b978fdaee90358cc5505e20a0526f812a460)),\n  closes [#44](https://github.com/ory/kratos/issues/44)\n- Support custom session token header\n  ([56bec76](https://github.com/ory/kratos/commit/56bec760fd1b94428ba296395a11358664d9e830)):\n\n  The `/sessions/whoami` endpoint now accepts the ORY Kratos Session Token in\n  the `X-Session-Token` HTTP header.\n\n- Support GitLab OIDC Provider\n  ([#519](https://github.com/ory/kratos/issues/519))\n  ([8580d96](https://github.com/ory/kratos/commit/8580d96b7e345cc85a646f2945c3931f831afebf)),\n  closes [#518](https://github.com/ory/kratos/issues/518)\n- Support json payloads for login and password\n  ([354e8b2](https://github.com/ory/kratos/commit/354e8b2cd63ee8feb1fd8a4ed8b033490155d90c))\n- Support JSON payloads in password login flow\n  ([dd32c23](https://github.com/ory/kratos/commit/dd32c23121da42e7eb3294fc8cb940fb7982723b))\n- Support session token bearer auth and lifecycle\n  ([c12600a](https://github.com/ory/kratos/commit/c12600a7243b541a91631169ec09d618a45c72dc)):\n\n  This patch adds support for issuing, validating, and revoking session tokens.\n  Session tokens carry a reference to a session, and are equal to session\n  cookies but can be used on environments which do not support cookies (e.g.\n  React Native) by sending them in the Bearer Authorization.\n\n- Update migration tests\n  ([fb28173](https://github.com/ory/kratos/commit/fb28173afa46ee828a3090981f394043c075f1ec))\n- Use uri-reference for ui_url etc. to allow relative urls\n  ([#617](https://github.com/ory/kratos/issues/617))\n  ([2dba450](https://github.com/ory/kratos/commit/2dba4503266436a615f4c1c18e07aa36ec713498))\n- Write request -> flow rename migrations\n  ([d7189a9](https://github.com/ory/kratos/commit/d7189a99c9d3e0ce33b4cc9846e6b2530ddfe5ec))\n\n### Tests\n\n- Add handler update tests\n  ([aea1fb8](https://github.com/ory/kratos/commit/aea1fb807a16acd8406b94a72c3b39be8c3e1280)),\n  closes [#325](https://github.com/ory/kratos/issues/325)\n- Add init browser flow tests\n  ([f477ece](https://github.com/ory/kratos/commit/f477ecebc73741b638cd62ef8aa2adb8b7adb8f2))\n- Add test for no-cache on public router\n  ([b8aa63b](https://github.com/ory/kratos/commit/b8aa63b7ebd269a87578e8a5c6b2df27e18f9efa))\n- Add test for registration request\n  ([79ed63c](https://github.com/ory/kratos/commit/79ed63cb4536499712796dab52999bcb73fe8466))\n- Add tests for registration flows\n  ([4772f71](https://github.com/ory/kratos/commit/4772f710f66d1ee36b52eca120d617a354f72413))\n- Complete test suite for API-based auth\n  ([fb9d62f](https://github.com/ory/kratos/commit/fb9d62f658165aa80bd117e1f827bbcc7c635150))\n- Implement API login password tests\n  ([8bfd5f2](https://github.com/ory/kratos/commit/8bfd5f294ff03280bcf01c5066acefe767eabc73))\n- Implement API registration password tests\n  ([db178b7](https://github.com/ory/kratos/commit/db178b73b097820c8dcd8760eec041a6fd0740aa))\n- Replace e2e-memory with unit test\n  ([52bd839](https://github.com/ory/kratos/commit/52bd839ea9fe8de1aac4663b9dc0a88ae18a5765)),\n  closes [#580](https://github.com/ory/kratos/issues/580)\n- Resolve broken decoder tests\n  ([07add1b](https://github.com/ory/kratos/commit/07add1b3e4f46e4aff52174ce43d6970f60cf3ee))\n- Use correct hook in test\n  ([421320c](https://github.com/ory/kratos/commit/421320ca4ad5b346c6dfb6ef0a9d14d7cf23fded))\n\n### Unclassified\n\n- u\n  ([e207a6a](https://github.com/ory/kratos/commit/e207a6adb98f639413accce383633d7e74ca4db9))\n- As part of this change, fetching a settings flow over the public API no longer\n  requires Anti-CSRF cookies to be sent.\n  ([31d560e](https://github.com/ory/kratos/commit/31d560e47d55b087519355081cbca20b2a49da4e)),\n  closes [#635](https://github.com/ory/kratos/issues/635)\n- Create labels.json\n  ([68b1f6f](https://github.com/ory/kratos/commit/68b1f6f5a35c66cc71f74f1473796fa16a852366))\n- Add codedoc to identifier hint block\n  ([6fe840f](https://github.com/ory/kratos/commit/6fe840f9c7a27ed97593e01936913e2239fd9446))\n- Format\n  ([e61a51d](https://github.com/ory/kratos/commit/e61a51dd6e2d5e003165a0b7906a9c86ebbc87d9))\n- Format\n  ([1e5b738](https://github.com/ory/kratos/commit/1e5b738f0765ec110c3ee70d7fc90fad0d1c89ac))\n- Format code\n  ([c3b5ff5](https://github.com/ory/kratos/commit/c3b5ff5d3bc3a1e72f48498fbed60bae9f159617))\n\n# [0.4.6-alpha.1](https://github.com/ory/kratos/compare/v0.4.5-alpha.1...v0.4.6-alpha.1) (2020-07-13)\n\nResolves build and install issues and includes a few bugfixes.\n\n### Bug Fixes\n\n- Use proper binary name in dockerfile\n  ([d36bbb0](https://github.com/ory/kratos/commit/d36bbb0875177ccd68747f4a17e59c981a7a6464))\n\n### Code Generation\n\n- Pin v0.4.6-alpha.1 release commit\n  ([ad90e77](https://github.com/ory/kratos/commit/ad90e772cf59a33b213bc0fb782959a1685d9741)):\n\n  Bumps from v0.4.4-alpha.1\n\n# [0.4.5-alpha.1](https://github.com/ory/kratos/compare/v0.4.4-alpha.1...v0.4.5-alpha.1) (2020-07-13)\n\nResolves build and install issues and includes a few bugfixes.\n\n### Bug Fixes\n\n- Ensure default_browser_return_url for flows is configured in after\n  ([#570](https://github.com/ory/kratos/issues/570))\n  ([cf9753c](https://github.com/ory/kratos/commit/cf9753c690c67e6401be52d2c1ce69f168aae6e8)),\n  closes [#569](https://github.com/ory/kratos/issues/569)\n- Require selfservice.default_browser_return_url to be set in config\n  ([#571](https://github.com/ory/kratos/issues/571))\n  ([af2af7d](https://github.com/ory/kratos/commit/af2af7d35ba8b10dcd6d7636b044b0f7761a719d))\n\n### Code Generation\n\n- Pin v0.4.5-alpha.1 release commit\n  ([3ea7fd3](https://github.com/ory/kratos/commit/3ea7fd3e7fd2c0b4aef638aa30e2b5b05c1bad26)):\n\n  Bumps from v0.4.4-alpha.1\n\n# [0.4.4-alpha.1](https://github.com/ory/kratos/compare/v0.4.3-alpha.1...v0.4.4-alpha.1) (2020-07-10)\n\nThe purpose of this release is to resolve issues with install scripts, homebrew,\nand scoop.\n\n### Bug Fixes\n\n- Detection of SQLite memory mode\n  ([#564](https://github.com/ory/kratos/issues/564))\n  ([605cd57](https://github.com/ory/kratos/commit/605cd579895f3b765d398074cfdb37fa3eae0c4e))\n- Improve goreleaser config\n  ([0f8a0d8](https://github.com/ory/kratos/commit/0f8a0d8afa6489383800d3eff1b7b1da01fbef08))\n\n### Code Generation\n\n- Pin v0.4.4-alpha.1 release commit\n  ([154d543](https://github.com/ory/kratos/commit/154d543eef29ab67be8637a96d8d06620974094f))\n\n### Documentation\n\n- Add description for subkeys of serve\n  ([#562](https://github.com/ory/kratos/issues/562))\n  ([deae005](https://github.com/ory/kratos/commit/deae005a259747872f678d355b49cca21904e565))\n- Add section about password expiry\n  ([19c2414](https://github.com/ory/kratos/commit/19c2414c3defe79fe6e80e50dd0e85026ecd60e6))\n- Specify the use of secrets ([#565](https://github.com/ory/kratos/issues/565))\n  ([7680450](https://github.com/ory/kratos/commit/7680450cfa44049759b27ec09d5bebc236b19a29))\n- Update upgrade guide\n  ([a40b1ec](https://github.com/ory/kratos/commit/a40b1ec18e7801f2862aad4e37becb7ce8f99c37))\n\n# [0.4.3-alpha.1](https://github.com/ory/kratos/compare/v0.4.2-alpha.1...v0.4.3-alpha.1) (2020-07-08)\n\nWe are very happy to announce the 0.4 release of ORY Kratos with 163 commits and\n817 changed files with 52,681 additions and 9,876 deletions.\n\nThere have been many improvements and bugfixes merged. The biggest changes are:\n\n1. Account recovery (\"reset password\") has been implemented.\n2. Documentation has been improved with easier to understand examples -\n   currently only for account recovery so let us know what you think!\n3. The configuration has been simplified a lot. It is now much easier to enable\n   account recovery and email verification. This is a breaking change - please\n   read the breaking changes section with care!\n4. The Identity Traits JSON Schema has been renamed to the Identity JSON Schema.\n   This is a breaking change - please read the breaking changes section with\n   care!\n5. `prompt=login` has been renamed to `refresh=true`. This is a breaking\n   change - please read the breaking changes section with care!\n6. We have reworked how (error) messages are returned. They now include an ID\n   and all the parameters required for translating and customizing UI messages.\n   This is a breaking change - please read the breaking changes section with\n   care!\n7. Instead of keeping track of `update_successful` with booleans, flows (e.g.\n   the settings flow) that have more than one state now include a state machine.\n   This is a breaking change - please read the breaking changes section with\n   care!\n8. Tons of tests have been added.\n9. We have reworked and fully tested the migration pipeline to prevent breaking\n   schema changes in future versions.\n10. ORY Kratos now supports login with Azure AD and the Microsoft Identity\n    Platform.\n\nBefore upgrading, please make a backup of your database and read the section\n\"Breaking Changes\" with care!\n\n### Bug Fixes\n\n- Resolve goreleaser build issues\n  ([223571b](https://github.com/ory/kratos/commit/223571bca15f507067d20bedb104923331f88e59))\n- Update install.sh script\n  ([883d99b](https://github.com/ory/kratos/commit/883d99ba42de084018a32eaa094b5ae1a8ad4fc2))\n\n### Code Generation\n\n- Pin v0.4.3-alpha.1 release commit\n  ([a3a34b1](https://github.com/ory/kratos/commit/a3a34b1e43b2d010ed85e098cd7cea31127df311)):\n\n  Bumps from v0.4.0-alpha.1\n\n# [0.4.2-alpha.1](https://github.com/ory/kratos/compare/v0.4.0-alpha.1...v0.4.2-alpha.1) (2020-07-08)\n\nWe are very happy to announce the 0.4 release of ORY Kratos with 153 commits and\n760 changed files with 36,223 additions and 9,754 deletions.\n\nThere have been many improvements and bugfixes merged. The biggest changes are:\n\n1. Account recovery (\"reset password\") has been implemented.\n2. Documentation has been improved with easier to understand examples -\n   currently only for account recovery so let us know what you think!\n3. The configuration has been simplified a lot. It is now much easier to enable\n   account recovery and email verification. This is a breaking change - please\n   read the breaking changes section with care!\n4. The Identity Traits JSON Schema has been renamed to the Identity JSON Schema.\n   This is a breaking change - please read the breaking changes section with\n   care!\n5. `prompt=login` has been renamed to `refresh=true`. This is a breaking\n   change - please read the breaking changes section with care!\n6. We have reworked how (error) messages are returned. They now include an ID\n   and all the parameters required for translating and customizing UI messages.\n   This is a breaking change - please read the breaking changes section with\n   care!\n7. Instead of keeping track of `update_successful` with booleans, flows (e.g.\n   the settings flow) that have more than one state now include a state machine.\n   This is a breaking change - please read the breaking changes section with\n   care!\n8. Tons of tests have been added.\n9. We have reworked and fully tested the migration pipeline to prevent breaking\n   schema changes in future versions.\n10. ORY Kratos now supports login with Azure AD and the Microsoft Identity\n    Platform.\n\nBefore upgrading, please make a backup of your database and read the section\n\"Breaking Changes\" with care!\n\n### Bug Fixes\n\n- Ignore pkged generated files\n  ([1d385e4](https://github.com/ory/kratos/commit/1d385e4d1a004405099242c3003006d1713a24c6))\n\n### Code Generation\n\n- Pin v0.4.2-alpha.1 release commit\n  ([20024cb](https://github.com/ory/kratos/commit/20024cbbb44b4f556004ef752a7f37e70a070e6a)):\n\n  Bumps from v0.4.0-alpha.1\n\n# [0.4.0-alpha.1](https://github.com/ory/kratos/compare/v0.3.0-alpha.1...v0.4.0-alpha.1) (2020-07-08)\n\nWe are very happy to announce the 0.4 release of ORY Kratos with 153 commits and\n760 changed files with 36,223 additions and 9,754 deletions.\n\nThere have been many improvements and bugfixes merged. The biggest changes are:\n\n1. Account recovery (\"reset password\") has been implemented.\n2. Documentation has been improved with easier to understand examples -\n   currently only for account recovery so let us know what you think!\n3. The configuration has been simplified a lot. It is now much easier to enable\n   account recovery and email verification. This is a breaking change - please\n   read the breaking changes section with care!\n4. The Identity Traits JSON Schema has been renamed to the Identity JSON Schema.\n   This is a breaking change - please read the breaking changes section with\n   care!\n5. `prompt=login` has been renamed to `refresh=true`. This is a breaking\n   change - please read the breaking changes section with care!\n6. We have reworked how (error) messages are returned. They now include an ID\n   and all the parameters required for translating and customizing UI messages.\n   This is a breaking change - please read the breaking changes section with\n   care!\n7. Instead of keeping track of `update_successful` with booleans, flows (e.g.\n   the settings flow) that have more than one state now include a state machine.\n   This is a breaking change - please read the breaking changes section with\n   care!\n8. Tons of tests have been added.\n9. We have reworked and fully tested the migration pipeline to prevent breaking\n   schema changes in future versions.\n10. ORY Kratos now supports login with Azure AD and the Microsoft Identity\n    Platform.\n\nBefore upgrading, please make a backup of your database and read the section\n\"Breaking Changes\" with care! This release requires running SQL migrations when\nupgrading!\n\n## Breaking Changes\n\nThis patch renames the Identity Traits JSON Schema to Identity JSON Schema.\n\nThe identity payload has changed from\n\n```\n {\n-  \"traits_schema_url\": \"...\",\n-  \"traits_schema_id\": \"...\",\n+  \"schema_url\": \"...\",\n+  \"schema_id\": \"...\",\n }\n```\n\nAdditionally, it is now expected that your Identity JSON Schema includes a\n\"traits\" key at the root level.\n\n**Before (example)**\n\n```\n{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"title\": \"E-Mail\",\n      \"minLength\": 3,\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          }\n        },\n        \"verification\": {\n          \"via\": \"email\"\n        },\n        \"recovery\": {\n          \"via\": \"email\"\n        }\n      }\n    }\n  },\n  \"required\": [\n    \"email\"\n  ],\n  \"additionalProperties\": false\n}\n```\n\n**After (example)**\n\n```\n{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n```\n\nYou also need to remove the `traits` key from your ORY Kratos config like this:\n\n```\n identity:\n-   traits:\n-     default_schema_url: http://test.kratos.ory.sh/default-identity.schema.json\n-     schemas:\n-       - id: other\n-         url: http://test.kratos.ory.sh/other-identity.schema.json\n+   default_schema_url: http://test.kratos.ory.sh/default-identity.schema.json\n+   schemas:\n+     - id: other\n+       url: http://test.kratos.ory.sh/other-identity.schema.json\n```\n\nDo not forget to also update environment variables for the Identity JSON Schema\nas well if set.\n\nTo address these refactorings, the configuration had to be changed and with\nbreaking changes as keys have moved or have been removed.\n\nHook configuration has also changed. It is no longer required to include hooks\nsuch as `verification` to get verification working. Instead, verification is\nenabled globally (`selfservice.flows.verification.enabled`). Also, the\n`redirect` hook has been removed as it lead to confusion because there are\nalready default redirect URLs configurable. You will find more information in\nthe details below.\n\n**Session Management**\n\n```diff\n-ttl:\n-  session: 1h\n-security:\n-  session:\n-    cookie:\n-      same_site: Lax\n+session:\n+  lifespan: 1h\n+  cookie_same_site: Lax\n```\n\n**Secrets**\n\n```diff\n-secrets:\n-  session:\n-    - secret-to-encrypt-session-cookies\n-    - old-session-cookie-secret-that-has-been-rotated\n+secrets:\n+  default:\n+    # This secret is used as default and will also be used for encrypting e.g. cookies when a dedicated cookie secret (as shown below) is not defined.\n+    - default-secret-to-encrypt-stuff\n+  cookie:\n+    - secret-to-encrypt-session-cookies\n+    - old-session-cookie-secret-that-has-been-rotated\n```\n\n**URLs**\n\nThe Base URL configuration has moved to `serve.public` and `serve.admin`. They\nare also no longer required and fall back to defaults based on the machine's\nhostname, port configuration, and other settings:\n\n```diff\n-urls:\n-  self:\n-    public: https://kratos.my-website.com/\n-    admin: https://admin.kratos.cluster.localnet/\n+serve:\n+  public:\n+    base_url: https://kratos.my-website.com/\n+  admin:\n+    base_url: https://admin.kratos.cluster.localnet/\n```\n\nThe UI URLs have moved from `urls` to their respective self-service flows:\n\n```diff\n-urls:\n-  login_ui: http://127.0.0.1:4455/auth/login\n-  registration_ui: http://127.0.0.1:4455/auth/registration\n-  settings_ui: http://127.0.0.1:4455/settings\n-  verify_ui: http://127.0.0.1:4455/verify\n-  error_ui: http://127.0.0.1:4455/error\n+selfservice:\n+  flows:\n+    login:\n+      ui_url: http://127.0.0.1:4455/auth/login\n+    registration:\n+      ui_url: http://127.0.0.1:4455/auth/registration\n+    settings:\n+      ui_url: http://127.0.0.1:4455/settings\n+    # please note that `verify` has changed to `verification`!\n+    verification:\n+      ui_url: http://127.0.0.1:4455/verify\n+    error:\n+      ui_url: http://127.0.0.1:4455/error\n```\n\nThe default redirect URL as well as whitelisted redirect URLs have also changed\ntheir location:\n\n```diff\n-urls:\n-  default_return_to: https://self-service/dashboard\n-  whitelisted_return_to_urls:\n-    - https://self-service/some-other-url\n-    - https://example.org/another-url\n+selfservice:\n+  default_browser_return_url: https://self-service/dashboard\n#  Please note that the `to` has been removed (`whitelisted_return_to_urls` -> `whitelisted_return_urls`)\n+  whitelisted_return_urls:\n+    - https://self-service/some-other-url\n+    - https://example.org/another-url\n```\n\n**Self-Service Login**\n\n`selfservice.login` has moved to `selfservice.flow.login`:\n\n```diff\n selfservice:\n-  login:\n+  flows:\n+    login:\n```\n\nOn top of this change, a few keys under `login` have changed as well:\n\n```diff\n selfservice\n   flows:\n     login:\n+      ui_url: http://127.0.0.1:4455/auth/login\n       request_lifespan: 99m\n-      before:\n-        hooks:\n-          - hook: redirect\n-            config:\n-              default_redirect_url: http://test.kratos.ory.sh:4000/\n-              allow_user_defined_redirect: false\n+      # The before hooks have been removed because there were no good use cases for them. If\n+      # this is a problem for you feel free to open an issue!\n\n     after:\n-      default_return_to: https://self-service/login/return_to\n+      default_browser_return_url: https://self-service/login/return_to\n       password:\n-         default_return_to: https://self-service/login/password/return_to\n+         default_browser_return_url: https://self-service/login/password/return_to\n          hooks:\n            - hook: revoke_active_sessions\n       oidc:\n-         default_return_to: https://self-service/login/podc/return_to\n+         default_browser_return_url: https://self-service/login/podc/return_to\n          hooks:\n            - hook: revoke_active_sessions\n```\n\n**Self-Service Registration**\n\n`selfservice.registration` has moved from to `selfservice.flow.registration`:\n\n```diff\n selfservice:\n-  registration:\n+  flows:\n+    registration:\n```\n\nOn top of this change, a few keys under `registration` have changed as well:\n\n```diff\n selfservice\n   flows:\n     registration:\n+      ui_url: http://127.0.0.1:4455/auth/registration\n       request_lifespan: 99m\n-      before:\n-        hooks:\n-          - hook: redirect\n-            config:\n-              default_redirect_url: http://test.kratos.ory.sh:4000/\n-              allow_user_defined_redirect: false\n+      # The before hooks have been removed because there were no good use cases for them. If\n+      # this is a problem for you feel free to open an issue!\n\n     after:\n-      default_return_to: https://self-service/registration/return_to\n+      default_browser_return_url: https://self-service/registration/return_to\n       password:\n-        default_return_to: https://self-service/registration/password/return_to\n+        default_browser_return_url: https://self-service/registration/password/return_to\n         hooks:\n           - hook: revoke_active_sessions\n+          # The verify hook is now executed automatically when verification is turned on.\n-          - hook: verify\n+          # The redirect hook was confusing as it aborts the registration flow and does not solve redirection on\n+          # success. It has thus been removed.\n-          - hook: redirect\n       oidc:\n-        default_return_to: https://self-service/registration/podc/return_to\n+        default_browser_return_url: https://self-service/registration/podc/return_to\n         hooks:\n           - hook: revoke_active_sessions\n+          # The verify hook is now executed automatically when verification is turned on.\n-          - hook: verify\n+          # The redirect hook was confusing as it aborts the registration flow and does not solve redirection on\n+          # success. It has thus been removed.\n-          - hook: redirect\n```\n\n**Self-Service Settings**\n\n`selfservice.settings` has moved from to `selfservice.flow.settings`:\n\n```diff\n selfservice:\n-  settings:\n+  flows:\n+    settings:\n```\n\nOn top of this change, a few keys under `settings` have changed as well:\n\n```diff\n selfservice\n   flows:\n     settings:\n+      ui_url: http://127.0.0.1:4455/settings\n       request_lifespan: 99m\n       privileged_session_max_age: 99m\n-      default_return_to: https://self-service/settings/return_to\n       after:\n+        default_browser_return_url: https://self-service/settings/return_to\n+        # The profile/password after hooks have been removed as verification is now executed automatically\n+        # when turned on.\n-        password:\n-          hooks:\n-            - hook: verify\n-        profile:\n-          hooks:\n-            - hook: verify\n```\n\n**Self-Service Verification**\n\n`selfservice.verify` has moved from to `selfservice.flow.verification`:\n\n```diff\n selfservice:\n-  verify:\n+  flows:\n+    verification:\n```\n\nInstead of configuring verification with hooks and other components, it can now\nbe enabled in a central place. If enabled, a SMTP server must be configured in\nthe `courier` section. You are still required to mark a field as verifiable in\nyour Identity JSON Schema.\n\n```diff\n selfservice:\n   flows:\n     verification:\n+      enabled: true # defaults to true\n+      ui_url: http://127.0.0.1:4455/recovery\n       request_lifespan: 1m\n-      default_return_to: https://self-service/verification/return_to\n       after:\n+        default_browser_return_url: https://self-service/verification/return_to\n```\n\nReplaces the `update_successful` field of the settings request with a field\ncalled `state` which can be either `show_form` or `success`.\n\nFlows, request methods, form fields have had a key errors to show e.g.\nvalidation errors such as (\"not an email address\", \"incorrect\nusername/password\", and so on. The `errors` key is now called `messages`. Each\nmessage now has a `type` which can be `error` or `info`, an `id` which can be\nused to translate messages, a `text` (which was previously errors[*].message).\nThis affects all login, request, settings, and recovery flows and methods.\n\nTo refresh a login session it is now required to append `refresh=true` instead\nof `prompt=login` as the second has implications for revoking an existing issue\nand might be confusing when used in combination with OpenID Connect.\n\n- Applying this patch requires running SQL Migrations.\n- The field `identity.addresses` has moved to `identity.verifiable_addresses`.\n- Configuration key `selfservice.verification.link_lifespan` has been merged\n  with `selfservice.verification.request_lifespan`.\n\n### Bug Fixes\n\n- Account recovery can't use recovery token\n  ([#526](https://github.com/ory/kratos/issues/526))\n  ([379f24e](https://github.com/ory/kratos/commit/379f24e96e50a3e5c71b53a11195bdd84a8dc957)),\n  closes [#525](https://github.com/ory/kratos/issues/525)\n- Add and document recovery to quickstart\n  ([c229c54](https://github.com/ory/kratos/commit/c229c54603bdc3efb863fd76b64096ae599d1aac))\n- Add pkger to docker builds\n  ([d3ef5a0](https://github.com/ory/kratos/commit/d3ef5a0fe90f430999d0d94cb2f55acc8d628212))\n- Allow linking oidc credentials without existing oidc connection\n  ([#548](https://github.com/ory/kratos/issues/548))\n  ([39c1234](https://github.com/ory/kratos/commit/39c1234f8ff3f6c7b0923053c8a317677d6cb667)),\n  closes [#532](https://github.com/ory/kratos/issues/532)\n- Bump pop version ([#558](https://github.com/ory/kratos/issues/558))\n  ([9e46cea](https://github.com/ory/kratos/commit/9e46ceabec8d5c1995321b62cbba9ac3900de446)),\n  closes [#556](https://github.com/ory/kratos/issues/556)\n- Clear error messages after updating settings successfully\n  ([#421](https://github.com/ory/kratos/issues/421))\n  ([7eec388](https://github.com/ory/kratos/commit/7eec38829449237cffe345d8bec67578764559be)),\n  closes [#420](https://github.com/ory/kratos/issues/420)\n- Do not send debug on session/whoami\n  ([16d3670](https://github.com/ory/kratos/commit/16d3670070bf46170c4540203e8380ad81bfb4c3)),\n  closes [#483](https://github.com/ory/kratos/issues/483)\n- Document login refresh parameter in swagger\n  ([#482](https://github.com/ory/kratos/issues/482))\n  ([6b94993](https://github.com/ory/kratos/commit/6b949936725a6100a31851a5d879c877c2c76cbf))\n- Embedded video link properly\n  ([#514](https://github.com/ory/kratos/issues/514))\n  ([962bbc6](https://github.com/ory/kratos/commit/962bbc6e4af0797c190418b812f6298372dabdde))\n- Embedded video link properly\n  ([#515](https://github.com/ory/kratos/issues/515))\n  ([821ca93](https://github.com/ory/kratos/commit/821ca93838a360551378e336e9ce10cfe13369ec))\n- Enable recovery for quickstart\n  ([0ccc651](https://github.com/ory/kratos/commit/0ccc651f809b1e39dd6c41b88f1a10c67451eae2))\n- Improve grammar of similar password error\n  ([#471](https://github.com/ory/kratos/issues/471))\n  ([39873bf](https://github.com/ory/kratos/commit/39873bfad89a654fe12e101b54e9b0c2f95714ec))\n- Improvements to Dockerfiles ([#552](https://github.com/ory/kratos/issues/552))\n  ([6023877](https://github.com/ory/kratos/commit/6023877184efeadd6ec27a050a6969b6d0dd6caa)):\n  - expose ory home as volume to simplify passing in own config file\n  - declare Kratos default ports in Dockerfile\n\n- Initialize verification request with correct state\n  ([3264ecf](https://github.com/ory/kratos/commit/3264ecfbb8f7b34d9dbb22237df8d9f591ac09f3)),\n  closes [#543](https://github.com/ory/kratos/issues/543)\n- Re-add all databases to persister\n  ([#527](https://github.com/ory/kratos/issues/527))\n  ([b04d178](https://github.com/ory/kratos/commit/b04d17815b5a28b5fe73a6a94ce1d907a63115e1))\n- Re-add redirect targets for quickstart\n  ([3c48ad2](https://github.com/ory/kratos/commit/3c48ad26961560d6e10a627a64052e316d9ffdc7))\n- Reduce docker bloat by ignoring docs and others\n  ([ecc555b](https://github.com/ory/kratos/commit/ecc555b5ad0fa888a8d5ba39cc09094fd251e655))\n- Resolve broken redirect in verify flow\n  ([a9ca8fd](https://github.com/ory/kratos/commit/a9ca8fd793347ed8e4404a4bd29e330a3f1ef684)),\n  closes [#436](https://github.com/ory/kratos/issues/436)\n- Respect multiple secrets and fix used flag\n  ([#526](https://github.com/ory/kratos/issues/526))\n  ([b16c2b8](https://github.com/ory/kratos/commit/b16c2b80edfc78afca0c72fa8da7d73b51b3075a)),\n  closes [#525](https://github.com/ory/kratos/issues/525)\n- Respect self-service enabled flag\n  ([#470](https://github.com/ory/kratos/issues/470))\n  ([b198faf](https://github.com/ory/kratos/commit/b198fafce9d96fbb644300243e6a757242fbbd06)),\n  closes [#417](https://github.com/ory/kratos/issues/417):\n\n  Respects the `enabled` flag for self-service strategies.\n\n  Also a new testhelper function was needed, to defer route registration\n  (because whether strategies are enabled or not is determined only once: at\n  route registration)\n\n- Typo accent -> account\n  ([984d978](https://github.com/ory/kratos/commit/984d978cf44763d916a9329742d046e00f21577b))\n- Use correct brew replacements\n  ([fd269b1](https://github.com/ory/kratos/commit/fd269b1afa784becac7ee79cd7a6f9d2bbe39121)),\n  closes [#423](https://github.com/ory/kratos/issues/423)\n- Write migration tests ([#499](https://github.com/ory/kratos/issues/499))\n  ([d32413a](https://github.com/ory/kratos/commit/d32413a1fcd0ce1a82d2529f18b5d4334a490a2a)),\n  closes [#481](https://github.com/ory/kratos/issues/481)\n\n### Code Generation\n\n- Pin v0.4.0-alpha.1 release commit\n  ([e8690c4](https://github.com/ory/kratos/commit/e8690c4037ba5d80aa2459625be553c5bc2d2152))\n\n### Code Refactoring\n\n- Improve and simplify configuration\n  ([#536](https://github.com/ory/kratos/issues/536))\n  ([8e7f9f5](https://github.com/ory/kratos/commit/8e7f9f5ec3ac6f5675584974e8d189247b539634)),\n  closes [#432](https://github.com/ory/kratos/issues/432)\n- Move schema packing to pkger\n  ([173f9d2](https://github.com/ory/kratos/commit/173f9d2b09d597376490b5d4588f7c0a4f525857))\n- Move verify fallback to verification\n  ([1ce6469](https://github.com/ory/kratos/commit/1ce64695ec61c3a31e00875069d2847be502744b))\n- Rename identity traits schema to identity schema\n  ([#557](https://github.com/ory/kratos/issues/557))\n  ([949e743](https://github.com/ory/kratos/commit/949e743ef9ddbc6e711f0174593f59f4fa3a1171)),\n  closes [#531](https://github.com/ory/kratos/issues/531)\n- Rename prompt=login to refresh=true\n  ([#478](https://github.com/ory/kratos/issues/478))\n  ([c04346e](https://github.com/ory/kratos/commit/c04346e0f01aa7ce5627c0b7135032b225e7faf9)),\n  closes [#477](https://github.com/ory/kratos/issues/477)\n- Replace settings update_successful with state\n  ([#488](https://github.com/ory/kratos/issues/488))\n  ([ca3b3f4](https://github.com/ory/kratos/commit/ca3b3f4dbdcd75ceb13c9a1b2c8dc991aba7c7e4)),\n  closes [#449](https://github.com/ory/kratos/issues/449)\n- Text errors to text messages\n  ([#476](https://github.com/ory/kratos/issues/476))\n  ([8106951](https://github.com/ory/kratos/commit/81069514e5ef1d851f76d44bb45d6a896d4985a6)),\n  closes [#428](https://github.com/ory/kratos/issues/428):\n\n  This patch implements a better way to deal with text messages by giving them a\n  unique ID, a context, and a default message.\n\n### Documentation\n\n- Add azure to next docs\n  ([e1dd3fa](https://github.com/ory/kratos/commit/e1dd3fad30a07be6f105201a8478642e9792df46))\n- Add fixme note for viper workaround\n  ([7e3eef6](https://github.com/ory/kratos/commit/7e3eef6d36dcbb1a06ce0a20e2de0874a7dc5d38)):\n\n  See https://github.com/ory/x/issues/169\n\n- Add guide for setting up account recovery\n  ([bbf3762](https://github.com/ory/kratos/commit/bbf37620d5b47fd18cb754c8ed43856652ee33c0))\n- Add guide for setting up email verification\n  ([1435cbc](https://github.com/ory/kratos/commit/1435cbcea5d45c9cde1a0eb7e5ebb66ce65c4b82))\n- Add guide for SSO via Google\n  ([#424](https://github.com/ory/kratos/issues/424))\n  ([5c45b16](https://github.com/ory/kratos/commit/5c45b1653791cc3ab5d4e4694da98da7543e816d))\n- Add new guides to sidebar\n  ([24c5cbc](https://github.com/ory/kratos/commit/24c5cbc129ad185ec02883c3451d7e573409b865))\n- Added video tutorials to guides\n  ([#513](https://github.com/ory/kratos/issues/513))\n  ([956731d](https://github.com/ory/kratos/commit/956731d562f33f2849197b2e692a4f20b18279f9))\n- Added youtube manual ([#490](https://github.com/ory/kratos/issues/490))\n  ([ec232f7](https://github.com/ory/kratos/commit/ec232f72d7204b2cdf946874d51f7473a10a76a4))\n- Connecting Kratos to AzureAD\n  ([#433](https://github.com/ory/kratos/issues/433))\n  ([7660bcd](https://github.com/ory/kratos/commit/7660bcd2ba90d83c4ab0683a2f011e6841b2c810))\n- Correct claims.email in github guide\n  ([#422](https://github.com/ory/kratos/issues/422))\n  ([052a622](https://github.com/ory/kratos/commit/052a622de79d34e32ccab9c7da12a1275c7be51b)):\n\n  There is no email_primary in claims, and the selfservice strategy is currently\n  using claims.email.\n\n- Correct claims.email in github guide\n  ([#422](https://github.com/ory/kratos/issues/422))\n  ([58f7e15](https://github.com/ory/kratos/commit/58f7e15093d2461d4322fe68adb0723ae244bed9)):\n\n  There is no email_primary in claims, and the selfservice strategy is currently\n  using claims.email.\n\n- Correct link in user-settings\n  ([d13317d](https://github.com/ory/kratos/commit/d13317d9bf71db775067a7c17f4c98cdbf1cc7e5))\n- Correct SDK use in quickstart\n  ([#480](https://github.com/ory/kratos/issues/480))\n  ([dfdf975](https://github.com/ory/kratos/commit/dfdf9751d9333994a49537d82a15b780ebd8bc76)),\n  closes [#430](https://github.com/ory/kratos/issues/430)\n- Correct stray dot\n  ([e820f41](https://github.com/ory/kratos/commit/e820f41e63aff1a85094a9e14dfd968353ae6b1b))\n- Correct user settings render form\n  ([197e246](https://github.com/ory/kratos/commit/197e24603fc67707131e54e52e1bfb52011ca839))\n- Delete old redirect homepage\n  ([b6d9244](https://github.com/ory/kratos/commit/b6d9244b5d683f5baf27e9af5970596261a4fd20))\n- Document new account recovery feature\n  ([2252a86](https://github.com/ory/kratos/commit/2252a8676e573b9ade85814acc40b212dcfd48c1)),\n  closes [#436](https://github.com/ory/kratos/issues/436)\n- Document refresh=true for login\n  ([#479](https://github.com/ory/kratos/issues/479))\n  ([2ab5ead](https://github.com/ory/kratos/commit/2ab5ead77517ab5b750835195ab6673e219da71a)),\n  closes [#464](https://github.com/ory/kratos/issues/464)\n- Embedded quickstart video ([#491](https://github.com/ory/kratos/issues/491))\n  ([ee80346](https://github.com/ory/kratos/commit/ee80346a30ebc2c7b06292e58bd3578e002e242a))\n- Fix broken link\n  ([d20816e](https://github.com/ory/kratos/commit/d20816e5335abb8bcde5c6d68b17eaabae5d01b0))\n- Fix broken link\n  ([aa9d3e6](https://github.com/ory/kratos/commit/aa9d3e6347375170a84ba53b2a9050c9544e7e2a))\n- Fix broken link ([#506](https://github.com/ory/kratos/issues/506))\n  ([dac8dfd](https://github.com/ory/kratos/commit/dac8dfd970255f8e79e7fc7811f563e6903f6fc9)):\n\n  The rest api is no longer under sdk but under reference.\n\n- Fix broken link ([#554](https://github.com/ory/kratos/issues/554))\n  ([e80d691](https://github.com/ory/kratos/commit/e80d691e256326aacfa89b391583e0494d8a6872))\n- Fix code sample comment\n  ([781a76b](https://github.com/ory/kratos/commit/781a76bb6de20767d6150b1fcb5236f4f376edd7))\n- Fix copy paste errors in code docs\n  ([e456a4e](https://github.com/ory/kratos/commit/e456a4e435265eade7026fd899c4bc7d2b28a5c9))\n- Fix iframe syntax ([#520](https://github.com/ory/kratos/issues/520))\n  ([0cb36ca](https://github.com/ory/kratos/commit/0cb36ca9d8459dc8027358190e6e8aa8764bffe4))\n- Fix typo ([#535](https://github.com/ory/kratos/issues/535))\n  ([c57d270](https://github.com/ory/kratos/commit/c57d270758a97315c874df3fae867b0031300501))\n- Fix typo in base docs ([#503](https://github.com/ory/kratos/issues/503))\n  ([6668048](https://github.com/ory/kratos/commit/666804812d707b1d50ea160877bdb3878ddfe6b0))\n- Fix typo in oauth sign in documentation\n  ([#504](https://github.com/ory/kratos/issues/504))\n  ([886e24d](https://github.com/ory/kratos/commit/886e24d93a5eb233062b8c7d562c8208f7a4f48f))\n- Fix typos\n  ([81903a5](https://github.com/ory/kratos/commit/81903a5137d87588531391623b92afde70abc3ea))\n- Fix typos ([#489](https://github.com/ory/kratos/issues/489))\n  ([57a7bc8](https://github.com/ory/kratos/commit/57a7bc89961612fea0255202d3dd6a535921ef3c))\n- Fix ui url keys everywhere\n  ([b75debb](https://github.com/ory/kratos/commit/b75debb0ee4f87dd9910b30bd76d8c6ad382fb38))\n- Fix username example by renaming property and removing format\n  ([#508](https://github.com/ory/kratos/issues/508))\n  ([4573426](https://github.com/ory/kratos/commit/45734260bcead3087aadcaaf3033cc1e89bc1844))\n- Fix wording in settings flow graph\n  ([e2a0084](https://github.com/ory/kratos/commit/e2a00842cb5bd3cfbddd0e5117c7f3f968e9f2df))\n- Fixed broken link ([#452](https://github.com/ory/kratos/issues/452))\n  ([d1ddbd1](https://github.com/ory/kratos/commit/d1ddbd1ee465a7d3e29815fcfd9c75b5decbb5f9))\n- Fixed broken link ([#455](https://github.com/ory/kratos/issues/455))\n  ([4f3d179](https://github.com/ory/kratos/commit/4f3d17906f3fa2aea3a0b0505047da6aa54938e4))\n- Fixed broken link ([#456](https://github.com/ory/kratos/issues/456))\n  ([4b43e99](https://github.com/ory/kratos/commit/4b43e993df62d2bf54fa39624651f081eb75bbb0))\n- Fixed broken link ([#460](https://github.com/ory/kratos/issues/460))\n  ([7da304c](https://github.com/ory/kratos/commit/7da304caf0de93442f047872cdd30d7fc316218e))\n- Fixed broken link ([#461](https://github.com/ory/kratos/issues/461))\n  ([c248e4e](https://github.com/ory/kratos/commit/c248e4e2a48a409b53ed02644abfc27e3cebeb11))\n- Fixed broken link ([#462](https://github.com/ory/kratos/issues/462))\n  ([ceacac3](https://github.com/ory/kratos/commit/ceacac30eda7d94cb24403c1fb988d4dd5fcd21f))\n- Fixed broken links ([#451](https://github.com/ory/kratos/issues/451))\n  ([193a781](https://github.com/ory/kratos/commit/193a781576031818006d6e2b72418293cf94dda1)):\n\n  Fixed a few broken links, .md in the url was the problem.\n\n- Fixed broken links ([#453](https://github.com/ory/kratos/issues/453))\n  ([59d00eb](https://github.com/ory/kratos/commit/59d00ebb87564cc9ff9c5ae12bcd7d25fb0b26c9))\n- Fixed broken links ([#457](https://github.com/ory/kratos/issues/457))\n  ([00ec00d](https://github.com/ory/kratos/commit/00ec00d09ca5318c75832caff5e7a97d640ac083))\n- Fixed broken links ([#458](https://github.com/ory/kratos/issues/458))\n  ([f960887](https://github.com/ory/kratos/commit/f9608876e30dbdd7c67ee70dcf5d9a1985b80f0f))\n- Fixed broken links ([#459](https://github.com/ory/kratos/issues/459))\n  ([2749596](https://github.com/ory/kratos/commit/27495964c7cd34e9bf914b19c83157e484c9cde4))\n- Fixed broken markdown ([#474](https://github.com/ory/kratos/issues/474))\n  ([22d5be1](https://github.com/ory/kratos/commit/22d5be16f91ed9df206310c6f04d843cd79328ca))\n- Format guides\n  ([407c70f](https://github.com/ory/kratos/commit/407c70f23d815380d98ee9252f263e07c1f0f4a9))\n- Improve grammar and wording ([#448](https://github.com/ory/kratos/issues/448))\n  ([a19adf3](https://github.com/ory/kratos/commit/a19adf30426ff8df03a3eb725ae0101ebb6c4ab1))\n- Improve grammar, clarify sections, update images\n  ([#419](https://github.com/ory/kratos/issues/419))\n  ([79019d1](https://github.com/ory/kratos/commit/79019d1246b1517b3297996a207a3d2f517fab01))\n- Make whitelisted_return_to_urls examples an array\n  ([#426](https://github.com/ory/kratos/issues/426))\n  ([7ed5605](https://github.com/ory/kratos/commit/7ed56057f533f23ca18cab5a2614429554e877e2)),\n  closes [#425](https://github.com/ory/kratos/issues/425)\n- Minor fixes ([#467](https://github.com/ory/kratos/issues/467))\n  ([8d15307](https://github.com/ory/kratos/commit/8d153079ee44f0765993640500bbe746dc0a34aa))\n- Move security questions to own document\n  ([2b77fba](https://github.com/ory/kratos/commit/2b77fba79b724dcd68ff0cd739cd65517aea4325))\n- Properly annotate forms disabled field\n  ([#486](https://github.com/ory/kratos/issues/486))\n  ([be1acb3](https://github.com/ory/kratos/commit/be1acb3d161412d18599c970364f0c91fa6ebffb)):\n\n  See https://github.com/ory/kratos/pull/467#discussion_r434764266\n\n- Remove rogue slash and fix closing tag\n  ([#521](https://github.com/ory/kratos/issues/521))\n  ([3fd1076](https://github.com/ory/kratos/commit/3fd1076929eeecffb7e8aa8e906970774283daeb))\n- Rename redirect page to browser-redirect-flow-completion\n  ([ae77d48](https://github.com/ory/kratos/commit/ae77d48a3435069556382b9403cb1ad45a9d7c07))\n- Replace mailhog references with mailslurper\n  ([#509](https://github.com/ory/kratos/issues/509))\n  ([d0e5a0f](https://github.com/ory/kratos/commit/d0e5a0fa64e2d46437fb2abd17dc306bdec34a91))\n- Run format\n  ([2b3f299](https://github.com/ory/kratos/commit/2b3f29913be844498a02b9869789c2b2d4aaacf8))\n- Typo correction in credentials.md\n  ([#551](https://github.com/ory/kratos/issues/551))\n  ([3b7e104](https://github.com/ory/kratos/commit/3b7e104c2bcba52326f89761c9e3da14b4f06d08))\n- Typos and stale links\n  ([29fb466](https://github.com/ory/kratos/commit/29fb466d9881b6574ee697d7e25e45785f07114b))\n- Typos and stale links ([#510](https://github.com/ory/kratos/issues/510))\n  ([7557ab8](https://github.com/ory/kratos/commit/7557ab85ddf8501935d70e2558682dff2024897b))\n- Update repository templates\n  ([4c89834](https://github.com/ory/kratos/commit/4c89834ce59195c5b59da5bc5b41db7ed03bf1c4))\n- Use central banner repo for README\n  ([d1e8a82](https://github.com/ory/kratos/commit/d1e8a8272cd536b6e12326778258bfbe0b7e8af7))\n- Use shorthand closing tag for Mermaid\n  ([f9f2dbc](https://github.com/ory/kratos/commit/f9f2dbc063f82a852b540013ddff81501f7c1222))\n\n### Features\n\n- Add support for Multitenant Azure AD as an OIDC provider\n  ([#434](https://github.com/ory/kratos/issues/434))\n  ([a8f1179](https://github.com/ory/kratos/commit/a8f117985217c753cfca52905e43b640e89a6bd1))\n- Add tests for defaults\n  ([a16fc51](https://github.com/ory/kratos/commit/a16fc5121b36353cf2e684190eda976a1ea53a8f))\n- Add User ID to a header when calling whoami\n  ([#530](https://github.com/ory/kratos/issues/530))\n  ([183b4d0](https://github.com/ory/kratos/commit/183b4d075a9ff50c1f9f53d108a48789e49a5138))\n- Implement account recovery ([#428](https://github.com/ory/kratos/issues/428))\n  ([e169a3e](https://github.com/ory/kratos/commit/e169a3e4079b1ef3a18564e0723baf81c44c38ec)),\n  closes [#37](https://github.com/ory/kratos/issues/37):\n\n  This patch implements the account recovery with endpoints such as \"Init\n  Account Recovery\", a new config value `urls.recovery_ui` and so on. A new\n  identity field has been added `identity.recovery_addresses` containing all\n  recovery addresses.\n\n  Additionally, some refactoring was made to DRY code and make naming\n  consistent. As part of dependency upgrades, structured logging has also\n  improved and an audit trail prototype has been added (currently streams to\n  stderr only).\n\n### Unclassified\n\n- docs:fixed broken link (#454)\n  ([22720c6](https://github.com/ory/kratos/commit/22720c6c5e3d31acc175980223183e2336b3751d)),\n  closes [#454](https://github.com/ory/kratos/issues/454)\n- Allow kratos to talk to databases in docker-compose quickstart\n  ([#522](https://github.com/ory/kratos/issues/522))\n  ([8bf9a1a](https://github.com/ory/kratos/commit/8bf9a1ac4162c677a455c2f02de658bd5d146905)):\n\n  All of the databases must exist on the same docker network to allow the main\n  kratos applications to communicate with them.\n\n- Fixed typo ([#472](https://github.com/ory/kratos/issues/472))\n  ([31263b6](https://github.com/ory/kratos/commit/31263b68ab8d81d264e0fa375a915f8f82d70bb3))\n\n# [0.3.0-alpha.1](https://github.com/ory/kratos/compare/v0.2.1-alpha.1...v0.3.0-alpha.1) (2020-05-15)\n\nThis release finalizes the OpenID Connect and OAuth2 login, registration, and\nsettings strategy with JsonNet data transformation! From now on, \"Sign in with\nGoogle, Github, ...\" is officially supported! It's also possible to link and\nunlink these connections using the Self-Service Settings Flow! The documentation\nhas been updated to reflect those changes and includes guides to setting up\n\"Sign in with GitHub\" in under 5 Minutes! Please be aware that existing OpenID\nConnect connections will stop working. Check out the \"Breaking Changes\" section\nfor more info! Want to learn more? Check\n[out the docs](https://www.ory.sh/kratos/docs/concepts/credentials/openid-connect-oidc-oauth2)!\n\nWe also changed the config validation output, making it easier than ever to find\nbugs in your config:\n\n```\n% kratos --config invalid-config.yml serve\nINFO[0001] Config file loaded successfully.              path=invalid-config.yml\nERRO[0001] The provided configuration is invalid and could not be loaded. Check the output below to understand why.  config_file=invalid-config.yml\n\ndsn: <nil>\n     ^-- one or more required properties are missing\n\nurls.whitelisted_return_to_urls: https://selfservice.office.example.com\n                                 ^-- expected array, but got string\n\nFATA[0001] The services failed to start because the configuration is invalid. Check the output above for more details.\n```\n\nThis release concludes over 50 commits and 16.000 lines of code changed.\n\n## Breaking Changes\n\nIf you upgrade and have existing Social Sign In connections, it will no longer\nbe possible to use them to sign in. Because the oidc strategy was undocumented\nand not officially released we do not provide an upgrade guide. If you run into\nthis issue on a production system you may need to use SQL to change the config\nof those identities. If this is a real issue for you that you're unable to\nsolve, please create an issue on GitHub.\n\nThis is a breaking change as previous OIDC configurations will not work. Please\nconsult the newly written documentation on OpenID Connect to learn how to use\nOIDC in your login and registration flows. Since the OIDC feature was not\npublicly broadcasted yet we have chosen not to provide an upgrade path. If you\nhave issues, please reach out on the forums or slack.\n\n### Bug Fixes\n\n- Access rules of oathkeeper for quick start\n  ([#390](https://github.com/ory/kratos/issues/390))\n  ([5ed6d05](https://github.com/ory/kratos/commit/5ed6d05b3e13027e4e7ffef1ff10ab2fb948093d)),\n  closes [#389](https://github.com/ory/kratos/issues/389):\n\n  To access `/` as dashboard\n\n- Active field should not be required\n  ([#401](https://github.com/ory/kratos/issues/401))\n  ([aed2a5c](https://github.com/ory/kratos/commit/aed2a5c3c8e39132df53ae8f0eecfb7924296796)),\n  closes [ory/sdk#14](https://github.com/ory/sdk/issues/14)\n- Adopt jsonnet in e2e oidc tests\n  ([5e518fb](https://github.com/ory/kratos/commit/5e518fb2de678e27fcc0e4fff020a4d575f1c109))\n- Detect postgres unique constraint\n  ([3a777af](https://github.com/ory/kratos/commit/3a777af00244066a42751005d832e4058ddad8d2))\n- Fix oidc strategy jsonnet test\n  ([f6c48bf](https://github.com/ory/kratos/commit/f6c48bf2c64cea1f111e5777de22878e0be5f03c))\n- Improve config validation error message\n  ([#414](https://github.com/ory/kratos/issues/414))\n  ([d1e6896](https://github.com/ory/kratos/commit/d1e6896b3870cad49217ee78f6024a8a5c416f46)),\n  closes [#413](https://github.com/ory/kratos/issues/413)\n- Reset request id after parse\n  ([9550205](https://github.com/ory/kratos/commit/9550205a35364473e0f620ef2b2a7eac223dbfff))\n- Resolve flaky swagger generation\n  ([#416](https://github.com/ory/kratos/issues/416))\n  ([ac4acfc](https://github.com/ory/kratos/commit/ac4acfcd7f4e686b5d5c01136158fdf1687329ac))\n- Resolve regression issues and bugs\n  ([e6d5369](https://github.com/ory/kratos/commit/e6d53693e146ec6e0d9de2ea366323721af3d8fb))\n- Return correct error on id mismatch\n  ([5915f28](https://github.com/ory/kratos/commit/5915f2882d2a481ea357d50b0058093ba3ddb51b))\n- Test and implement mapper_url for jsonnet\n  ([40ac3dc](https://github.com/ory/kratos/commit/40ac3dc7b5828ac775055fed3c0bd9ff393e5d86))\n- Transaction usage in the identity persister\n  ([#404](https://github.com/ory/kratos/issues/404))\n  ([7f5072d](https://github.com/ory/kratos/commit/7f5072dc2d4fbf1f48cdf4d199ce4e89683a87b1))\n\n### Chores\n\n- Pin v0.3.0-alpha.1 release commit\n  ([43b693a](https://github.com/ory/kratos/commit/43b693a449bf7cd219eb6901acf36725ace1c41c))\n\n### Code Refactoring\n\n- Adopt new request parser\n  ([ad16cc9](https://github.com/ory/kratos/commit/ad16cc917c8067eb1c4b89ef8192287be1c912c8))\n- Dry config and oidc tests\n  ([3e98756](https://github.com/ory/kratos/commit/3e9875612ea895f9b565d34f4d5b0f80d136868f))\n- Improve oidc flows and payloads and add e2e tests\n  ([#381](https://github.com/ory/kratos/issues/381))\n  ([f9a5079](https://github.com/ory/kratos/commit/f9a50790637a848897ba275373bc538728e09f3d)),\n  closes [#387](https://github.com/ory/kratos/issues/387):\n\n  This patch improves the OpenID Connect login and registration user experience\n  by simplifying the network flows and introduces e2e tests using ORY Hydra.\n\n- Move cypress files to test/e2e\n  ([df8e627](https://github.com/ory/kratos/commit/df8e627d81d69682e01ec5670c7088ba564df578))\n- Moved scanner json to ory/x ([#412](https://github.com/ory/kratos/issues/412))\n  ([8a0967d](https://github.com/ory/kratos/commit/8a0967daef4329981b01e6c2b8bb55a8105b4829))\n- Partition files and change creds structure\n  ([4f1eb94](https://github.com/ory/kratos/commit/4f1eb946fe1e74e537fc2166fc000180a11c2048)):\n\n  This patch changes the data model of the OpenID Connect strategy. Instead of\n  using an array of providers as the base config item (e.g.\n  `{\"type\":\"oidc\",\"config\":[{\"provider\":\"google\",\"subject\":\"...\"}]}`) the\n  credentials config is now an object with a `providers` key:\n  `{\"type\":\"oidc\",\"config\":{\"providers\":[{\"provider\":\"google\",\"subject\":\"...\"}]}}`.\n  This change allows introduction of future changes to the schema without\n  breaking compatibility.\n\n- Replace oidc jsonschema with jsonnet\n  ([2b45e79](https://github.com/ory/kratos/commit/2b45e7953787ad46a6937fe44cb24b6c786eb223)),\n  closes [#380](https://github.com/ory/kratos/issues/380):\n\n  This patch replaces the previous methodology of merging OIDC data which used\n  JSON Schema with Extensions and JSON Path in favor of a much easier to use\n  approach with JSONNet.\n\n- **settings:** Use common request parser\n  ([ad6c402](https://github.com/ory/kratos/commit/ad6c4026e5fd15924dc906cdc9cb6c9de2fc4daa))\n\n### Documentation\n\n- Document account enumeration defenses for oidc\n  ([266329c](https://github.com/ory/kratos/commit/266329cd2969627c823418c1267360193e6342df)),\n  closes [#32](https://github.com/ory/kratos/issues/32)\n- Document new oidc jsonnet mapper\n  ([#392](https://github.com/ory/kratos/issues/392))\n  ([088b30f](https://github.com/ory/kratos/commit/088b30feb6845863e6651489e0c963cde7e10516))\n- Document oidc strategy ([#415](https://github.com/ory/kratos/issues/415))\n  ([9f079f4](https://github.com/ory/kratos/commit/9f079f4f77e54f7be67ac59e13e8ec2696522637)),\n  closes [#409](https://github.com/ory/kratos/issues/409)\n  [#124](https://github.com/ory/kratos/issues/124)\n  [#32](https://github.com/ory/kratos/issues/32)\n- Explain that form data is merged with oidc data\n  ([#394](https://github.com/ory/kratos/issues/394))\n  ([b0dbec4](https://github.com/ory/kratos/commit/b0dbec403c96af41346b6b14fc74b7010e7f8e8a)),\n  closes [#127](https://github.com/ory/kratos/issues/127)\n- Fix links in README\n  ([efb6102](https://github.com/ory/kratos/commit/efb610239ac2ae828db26ee84c4c5a83c54c0a6a)),\n  closes [#403](https://github.com/ory/kratos/issues/403)\n- Improve social sign in guide\n  ([#393](https://github.com/ory/kratos/issues/393))\n  ([647ced3](https://github.com/ory/kratos/commit/647ced3084d203e9954ca037afea34316f2080d8)),\n  closes [#49](https://github.com/ory/kratos/issues/49):\n\n  This patch changes the social sign in guide to represent more use cases such\n  as Google and Facebook. Additionally, the example has been updated to work\n  with Jsonnet.\n\n  This patch also documents limitations around merging user data from GitHub.\n\n- Improve the identity data model page\n  ([#410](https://github.com/ory/kratos/issues/410))\n  ([2915b8f](https://github.com/ory/kratos/commit/2915b8faf3530fe7b9d252094c3aeb9fdbe9dd08))\n- Include redirect doc in nav\n  ([5aaebff](https://github.com/ory/kratos/commit/5aaebffd8c03e613ec60735536b6ef38d4da39e3)),\n  closes [#406](https://github.com/ory/kratos/issues/406)\n- Prepare v0.3.0-alpha.1\n  ([d6a6f43](https://github.com/ory/kratos/commit/d6a6f432f375018a2dc79d6b60de18455057c25a))\n- Ui should show only active form sections\n  ([#395](https://github.com/ory/kratos/issues/395))\n  ([4db674d](https://github.com/ory/kratos/commit/4db674de14bc50e782321c7bd88ac8077db2bf75))\n- Update github templates ([#408](https://github.com/ory/kratos/issues/408))\n  ([6e646b0](https://github.com/ory/kratos/commit/6e646b033e0d43499bf37579a2f04b726af0e3f7))\n\n### Features\n\n- Add format and lint for JSONNet files\n  ([0a1b244](https://github.com/ory/kratos/commit/0a1b244a6fd2f714a12d101071b3c0f82b4da584)):\n\n  This patch adds two commands `kratos jsonnet format` and `kratos jsonnet lint`\n  that help with formatting and linting JSONNet code.\n\n- Implement oidc settings e2e tests\n  ([919925c](https://github.com/ory/kratos/commit/919925c87be561064300c3981b5a230c6cada4f7))\n- Introduce leaklog for debugging oidc map payloads\n  ([238d7a4](https://github.com/ory/kratos/commit/238d7a493566bcc28f08b1b2bf6463f95b100254))\n- Write tests and fix bugs for oidc settings\n  ([575a61f](https://github.com/ory/kratos/commit/575a61f58a887fefa6b2917761c06304c94c9892))\n\n### Unclassified\n\n- Format code\n  ([bc7557a](https://github.com/ory/kratos/commit/bc7557a4247ede1fdb4141f2670532aec7cbd456))\n\n# [0.2.1-alpha.1](https://github.com/ory/kratos/compare/v0.2.0-alpha.2...v0.2.1-alpha.1) (2020-05-05)\n\nResolves a bug in the kratos-selfservice-ui-node application.\n\n### Chores\n\n- Pin v0.2.1-alpha.1 release commit\n  ([16463ea](https://github.com/ory/kratos/commit/16463ead91a009f33373150d10095aa3857b38f4))\n\n### Documentation\n\n- Fix quickstart hero sections\n  ([7c6c439](https://github.com/ory/kratos/commit/7c6c4397bccd2b505fc04cc8d3b0944ceca18982))\n- Fix typo in upgrade guide\n  ([a1b1d7c](https://github.com/ory/kratos/commit/a1b1d7c9cbe5fad3b1112a16eced4f3064cfdda0))\n\n# [0.2.0-alpha.2](https://github.com/ory/kratos/compare/v0.1.1-alpha.1...v0.2.0-alpha.2) (2020-05-04)\n\nThis is a heavy release with over hundreds of commits and files changed! Let's\ntake a look at some of the highlights!\n\n**ORY Oathkeeper now optional**\n\nUsing ORY Oathkeeper to protect your API is now optional. The basic quickstart\nnow uses a much simpler set up. Go\n[check it out](https://www.ory.sh/kratos/docs/quickstart) now!\n\n**PostgreSQL, MySQL, CockroachDB support now tested and official!**\n\nAll three databases now pass acceptance tests and are thus officially supported!\n\n**Self-Service Profile Flow**\n\nThe self-service profile flow has been refactored into a more generic flow\nallowing users to make modifications to their traits and credentials. Check out\nthe\n[docs to learn more](https://www.ory.sh/kratos/docs/self-service/flows/user-settings-profile-management)\nabout the flow and it's features.\n\nPlease keep in mind that the flow's APIs have changed. We recommend re-reading\nthe docs!\n\n**Managing Privileged Profile Fields**\n\nFlows such as changing ones profile or primary email address should not be\npossible unless the login session is fresh. This prevents your colleague or evil\nfriend to take over your account while you make yourself a coffee.\n\nORY Kratos now supports this by redirecting the user to the login screen if\nchanges to sensitive fields are made. The changes will only be applied after\nsuccessful reauthentication.\n\n**Changes to Hooks**\n\nThis patch focuses on refactoring how self-service flows terminate and changes\nhow hooks behave and when they are executed.\n\nBefore this patch, it was not clear whether hooks run before or after an\nidentity is persisted. This caused problems with multiple writes on the HTTP\nResponseWriter and other bugs.\n\nThis patch removes certain hooks from after login, registration, and profile\nflows. Per default, these flows now respond with an appropriate payload (\nredirect for browsers, JSON for API clients) and deprecate the `redirect` hook.\nThis patch includes documentation which explains how these hooks work now.\n\nAdditionally, the documentation was updated. Especially the sections about hooks\nhave been refactored. The login and user registration docs have been updated to\nreflect the latest changes as well.\n\nBREAKING CHANGE: Please remove the `redirect` hook from both login,\nregistration, and settings after configuration. Please remove the `session` hook\nfrom your login after configuration. Hooks have moved down a level and are now\nconfigured at `selfservice.<login|registration|settings>.<after|before>.hooks`\ninstead of `selfservice.<login|registration|settings>.<after|before>.hooks`.\nHooks are now identified by `hook:` instead of `job:`. Please rename those\nsections accordingly.\n\nWe recommend re-reading the\n[Hooks Documentation](https://www.ory.sh/kratos/docs/self-service/hooks/index).\n\n**Changing Passwords**\n\nIt's now possible to change your password using the Self-Service Settings Flow!\nLean more about this flow\n[here](https://www.ory.sh/kratos/docs/self-service/flows/user-settings-profile-management)\n\n**End-To-End Tests**\n\nWe added tons of end-to-end and integration tests to find and fix pesky bugs.\n\n## Breaking Changes\n\nPlease remove the `redirect` hook from both login, registration, and settings\nafter configuration. Please remove the `session` hook from your login after\nconfiguration. Hooks have moved down a level and are now configured at\n`selfservice.<login|registration|settings>.<after|before>.hooks` instead of\n`selfservice.<login|registration|settings>.<after|before>.hooks`. Hooks are now\nidentified by `hook:` instead of `job:`. Please rename those sections\naccordingly.\n\nSeveral profile-related URLs have and payloads been updated. Please consult the\nmost recent documentation.\n\nThe payloads of the Profile Management Request API that previously were set in\n`{ \"methods\": { \"traits\": { ... } }}` have now moved to\n`{ \"methods\": { \"profile\": { ... } }}`.\n\nThis patch introduces a refactor that is needed for the profile management API\nto be capable of handling (password, oidc, ...) credential changes as well.\n\nTo implement this, the payloads of the Profile Management Request API that\npreviously were set in `{\"form\": {...} }` have now moved to\n`{\"methods\": { \"traits\": { ... } }}`.\n\nIn the future, as more credential updates are handled, there will be additional\nkeys in the forms key `{\"methods\": { \"traits\": { ... }, \"password\": { ... } }}`.\n\n### Bug Fixes\n\n- Allow setting new password in profile flow\n  ([3b5fd5c](https://github.com/ory/kratos/commit/3b5fd5ca8c09b2344c0262547f2b387bda362362))\n- Automatically append multiStatements parameter to mySQL URI\n  ([#374](https://github.com/ory/kratos/issues/374))\n  ([39f77bb](https://github.com/ory/kratos/commit/39f77bb29637db048b15c097d869d8828b0d292b))\n- **config:** Rename config key stmp to smtp\n  ([#278](https://github.com/ory/kratos/issues/278))\n  ([ef95811](https://github.com/ory/kratos/commit/ef95811bb891afe3a0ef3b19514f13a56a32ea3b))\n- Create pop connection without parsed connection options\n  ([#366](https://github.com/ory/kratos/issues/366))\n  ([10b6481](https://github.com/ory/kratos/commit/10b6481774aaff42b70b9c6af3ed776ac8f7734c))\n- Declare proper vars for setting version\n  ([#383](https://github.com/ory/kratos/issues/383))\n  ([2fc7556](https://github.com/ory/kratos/commit/2fc7556b70b11e519162326ded0ba2638b6d32df))\n- Decouple quickstart scenarios\n  ([#336](https://github.com/ory/kratos/issues/336))\n  ([17363b3](https://github.com/ory/kratos/commit/17363b312deff8b92fc1b0d158dc70670d5938e5)),\n  closes [#262](https://github.com/ory/kratos/issues/262):\n\n  Creates several docker compose examples which include various scenarios of the\n  quickstart.\n\n  The regular quickstart guide now works without ORY Oathkeeper and uses the\n  standalone mode of the example app instead.\n\n  Additionally, the Makefile was improved and now automatically pulls required\n  dependencies in the appropriate version.\n\n- **docker:** Throw away build artifacts\n  ([481ec1b](https://github.com/ory/kratos/commit/481ec1ba14480ced39516f6e0c47a40b6a44a631))\n- Document Schema API and serve over admin endpoint\n  ([#299](https://github.com/ory/kratos/issues/299))\n  ([4be417c](https://github.com/ory/kratos/commit/4be417c0ee18622247a15d2803f7f436cfe3c229)),\n  closes [#287](https://github.com/ory/kratos/issues/287)\n- Exempt whomai from csrf protection\n  ([#329](https://github.com/ory/kratos/issues/329))\n  ([31d4065](https://github.com/ory/kratos/commit/31d4065c2b0cbd6c8d2b0031ce8f6f157ff967cf))\n- Fix swagger annotation ([#331](https://github.com/ory/kratos/issues/331))\n  ([5c5c78f](https://github.com/ory/kratos/commit/5c5c78f404a11d5df25cb68584b826b685bf5385)):\n\n  Closes https://github.com/ory/sdk/issues/10\n\n- Move to ory sqa service ([#309](https://github.com/ory/kratos/issues/309))\n  ([7c244e0](https://github.com/ory/kratos/commit/7c244e0a28a010e56e07d061132dad7a0309ea75))\n- Properly annotate error API\n  ([a6f1300](https://github.com/ory/kratos/commit/a6f1300951010e7c862c410e93653f7c02c2e79f))\n- Remove unused returnTo\n  ([e64e5b0](https://github.com/ory/kratos/commit/e64e5b0cecceedda29a525f683cbf6070a9ef1eb))\n- Resolve docker build permission issues\n  ([f3612e8](https://github.com/ory/kratos/commit/f3612e8f82018bae17c9146d273fe7e82ceb033d))\n- Resolve failing test issues\n  ([2e968e5](https://github.com/ory/kratos/commit/2e968e52d3ae3396a3f2e212c0dab22677b4b5fd))\n- Resolve linux install script archive naming\n  ([#302](https://github.com/ory/kratos/issues/302))\n  ([c98b8aa](https://github.com/ory/kratos/commit/c98b8aa4cd3ab881b904e9dc4cdcb6383a8ad09b))\n- Resolve NULL value for seen_at\n  ([#259](https://github.com/ory/kratos/issues/259))\n  ([a7d1e86](https://github.com/ory/kratos/commit/a7d1e86844a9cdd0c58353e1f1e4340dac4260b3)),\n  closes [#244](https://github.com/ory/kratos/issues/244):\n\n  Previously, errorx tests were not executed which caused several bugs.\n\n- Resolve password continuity issues\n  ([56a44fa](https://github.com/ory/kratos/commit/56a44fa33d325eea9fddec4269e34e632310f77b))\n- Revert use host volume mount for sqlite\n  ([#272](https://github.com/ory/kratos/issues/272))\n  ([#285](https://github.com/ory/kratos/issues/285))\n  ([a7477ab](https://github.com/ory/kratos/commit/a7477ab1db0d986f96e754946607d05888de4c97)):\n\n  This reverts commit 230ab2d83f4d187f410e267c6d68554e82514948.\n\n- Self-service error query parameter name\n  ([#308](https://github.com/ory/kratos/issues/308))\n  ([be257f5](https://github.com/ory/kratos/commit/be257f5448abaa48e25735a088757f3fd6dc6d22)):\n\n  The query parameter for the self-service errors endpoint was named `id` in the\n  API docs, whereas it is the `error` param that is used by the handler.\n\n- **session:** Regenerate CSRF Token on principal change\n  ([#290](https://github.com/ory/kratos/issues/290))\n  ([1527ef4](https://github.com/ory/kratos/commit/1527ef4209b937e2175b60d56efd019f17b33b04)),\n  closes [#217](https://github.com/ory/kratos/issues/217)\n- **session:** Whoami endpoint now supports all HTTP methods\n  ([#283](https://github.com/ory/kratos/issues/283))\n  ([4bf645b](https://github.com/ory/kratos/commit/4bf645b66c7a128182ff55e52fdad7f53d752ce7)),\n  closes [#270](https://github.com/ory/kratos/issues/270)\n- Show log in ui only when unauthenticated or forced\n  ([df77310](https://github.com/ory/kratos/commit/df77310ffbe7cfc90fa3bc5dad0450e79c34ebef)),\n  closes [#323](https://github.com/ory/kratos/issues/323)\n- **sql:** Rename migrations with same version\n  ([#280](https://github.com/ory/kratos/issues/280))\n  ([07e46b9](https://github.com/ory/kratos/commit/07e46b9c9e57940bec904d744ffdd272d610a77b)),\n  closes [#279](https://github.com/ory/kratos/issues/279)\n- **swagger:** Move nolint,deadcode instructions to own file\n  ([#293](https://github.com/ory/kratos/issues/293))\n  ([1935510](https://github.com/ory/kratos/commit/1935510ad9b0f387eb3b2e690e31c5313a06883e)):\n\n  Closes https://github.com/ory/docs/pull/279\n\n- Use host volume mount for sqlite\n  ([#272](https://github.com/ory/kratos/issues/272))\n  ([230ab2d](https://github.com/ory/kratos/commit/230ab2d83f4d187f410e267c6d68554e82514948))\n- Use resilient client for HIBP lookup\n  ([#288](https://github.com/ory/kratos/issues/288))\n  ([735b435](https://github.com/ory/kratos/commit/735b43508392c6966a57907c20caa7cf9df4fc4d)),\n  closes [#261](https://github.com/ory/kratos/issues/261)\n- Use semver-regex replacer func\n  ([d5c9a47](https://github.com/ory/kratos/commit/d5c9a47800fc2a55b96c7b9330f68b0a2db328cb))\n- Use sqlite tag on make install\n  ([2c82784](https://github.com/ory/kratos/commit/2c82784cd69e0468a72354f6898945032d826306))\n- Verified_at field should not be required\n  ([#353](https://github.com/ory/kratos/issues/353))\n  ([15d5e26](https://github.com/ory/kratos/commit/15d5e268d2ec397f0647d2407d86404c4ee8bfa3)):\n\n  Closes https://github.com/ory/sdk/issues/11\n\n### Chores\n\n- Pin v0.2.0-alpha.2 release commit\n  ([ab91689](https://github.com/ory/kratos/commit/ab916894b761b18c53e4ed1fd0e42d9f5aa0817c))\n\n### Code Refactoring\n\n- Move docs to this repository\n  ([#317](https://github.com/ory/kratos/issues/317))\n  ([aa0d726](https://github.com/ory/kratos/commit/aa0d72639ecae3b0649761e6ee881a59b2f3e94e))\n- Prepare profile management payloads for credentials\n  ([44493f3](https://github.com/ory/kratos/commit/44493f3ddbb449981576ec317ac45530ca3be14d))\n- Rename traits method to profile\n  ([4f1e033](https://github.com/ory/kratos/commit/4f1e0339ecc1efbdfa3d3680ad64b7683e90e447))\n- Rework hooks and self-service flow completion\n  ([#349](https://github.com/ory/kratos/issues/349))\n  ([a7c7fef](https://github.com/ory/kratos/commit/a7c7fef758e843393b0dc1e60bee11b88b8c9b4a)),\n  closes [#348](https://github.com/ory/kratos/issues/348)\n  [#347](https://github.com/ory/kratos/issues/347)\n  [#179](https://github.com/ory/kratos/issues/179)\n  [#51](https://github.com/ory/kratos/issues/51)\n  [#50](https://github.com/ory/kratos/issues/50)\n  [#31](https://github.com/ory/kratos/issues/31):\n\n  This patch focuses on refactoring how self-service flows terminate and changes\n  how hooks behave and when they are executed.\n\n  Before this patch, it was not clear whether hooks run before or after an\n  identity is persisted. This caused problems with multiple writes on the HTTP\n  ResponseWriter and other bugs.\n\n  This patch removes certain hooks from after login, registration, and profile\n  flows. Per default, these flows now respond with an appropriate payload (\n  redirect for browsers, JSON for API clients) and deprecate the `redirect`\n  hook. This patch includes documentation which explains how these hooks work\n  now.\n\n  Additionally, the documentation was updated. Especially the sections about\n  hooks have been refactored. The login and user registration docs have been\n  updated to reflect the latest changes as well.\n\n  Also, some other minor, cosmetic, changes to the documentation have been made.\n\n### Documentation\n\n- Add banner kratos\n  ([8a9dfbb](https://github.com/ory/kratos/commit/8a9dfbbd54bac14778cc84ec13326eb1ef80f5b3))\n- Add csrf and cookie debug section\n  ([#342](https://github.com/ory/kratos/issues/342))\n  ([cac2948](https://github.com/ory/kratos/commit/cac2948685ed2a3c3edbc8eb4696bbfb8523dfeb)),\n  closes [#341](https://github.com/ory/kratos/issues/341)\n- Add database connection documentation\n  ([#332](https://github.com/ory/kratos/issues/332))\n  ([4f9e8b0](https://github.com/ory/kratos/commit/4f9e8b00bacda3612db3f48b81fabd562075470a))\n- Add HA docs\n  ([2e5c591](https://github.com/ory/kratos/commit/2e5c59158915d1ccbb90363e23f73a09c227b6f7))\n- Add hook changes to upgrade guide\n  ([55b5fe0](https://github.com/ory/kratos/commit/55b5fe00c0472f5f6f7408eee76bf9a39318db7e))\n- Add info to oidc ([#382](https://github.com/ory/kratos/issues/382))\n  ([6eeeb5d](https://github.com/ory/kratos/commit/6eeeb5dbe98d2f31fd922d60a35d9d8f81d0b2a8))\n- Add more examples to config schema\n  ([#372](https://github.com/ory/kratos/issues/372))\n  ([ed2ccb9](https://github.com/ory/kratos/commit/ed2ccb935fdcfcb11999996cd582726bba096435)),\n  closes [#345](https://github.com/ory/kratos/issues/345)\n- Add quickstart notes for docker debugging\n  ([74f082a](https://github.com/ory/kratos/commit/74f082a407ee73741453ff6a394f47790e79b667))\n- Add settings docs and improve flows\n  ([#375](https://github.com/ory/kratos/issues/375))\n  ([478cd9c](https://github.com/ory/kratos/commit/478cd9c5b5755030307d1f11e9bcbd4e171ee0d6)),\n  closes [#345](https://github.com/ory/kratos/issues/345)\n- **concepts:** Fix typo\n  ([a49184c](https://github.com/ory/kratos/commit/a49184c30d9c2ccff5a2d41d3aff61b24e7d2ea9)):\n\n  Closes https://github.com/ory/docs/pull/296\n\n- **concepts:** Properly close code tag\n  ([1c841c2](https://github.com/ory/kratos/commit/1c841c213bdbc79a6aa41e8450444d8d6c1f0284))\n- Declare api frontmatter properly\n  ([df7591f](https://github.com/ory/kratos/commit/df7591f7b70c94cfe62042a598eceb36b6a4f29a))\n- Document 0.2.0 high-level changes\n  ([9be1064](https://github.com/ory/kratos/commit/9be1064500dd86489b79e1abd9cbf1268b97853a)),\n  closes [hi#level](https://github.com/hi/issues/level)\n- Document multi-tenant set up\n  ([891594d](https://github.com/ory/kratos/commit/891594df488e42ce30a81465f10f2936d152cb55)),\n  closes [#370](https://github.com/ory/kratos/issues/370)\n- Fix broken images in quickstart\n  ([52aa4cf](https://github.com/ory/kratos/commit/52aa4cf0b6967108fa58f58b6b151e6f6118bcc9))\n- Fix broken link\n  ([bf7843c](https://github.com/ory/kratos/commit/bf7843cd96795a894488a0910529c847cf7eee19)),\n  closes [#327](https://github.com/ory/kratos/issues/327)\n- Fix broken link\n  ([c2adc73](https://github.com/ory/kratos/commit/c2adc734a73758d858d50d8738dc2a556110f26c)),\n  closes [#327](https://github.com/ory/kratos/issues/327)\n- Fix broken mermaid links\n  ([f24fc1b](https://github.com/ory/kratos/commit/f24fc1bbba234d71098298bcddbba236ac4297f3))\n- Fix spelling in quickstart ([#356](https://github.com/ory/kratos/issues/356))\n  ([3ce6b4a](https://github.com/ory/kratos/commit/3ce6b4a1b0722a96bcbae79b7261616f20741494))\n- Improve changelog ([#384](https://github.com/ory/kratos/issues/384))\n  ([a973ca7](https://github.com/ory/kratos/commit/a973ca7719cd820bb196ec5732c85418528be1d0))\n- Improve profile section and restructure nav\n  ([#373](https://github.com/ory/kratos/issues/373))\n  ([3cc0979](https://github.com/ory/kratos/commit/3cc097934edc81d4c6d853594eed5e68e9e48445)),\n  closes [#345](https://github.com/ory/kratos/issues/345)\n- Regenerate and update changelog\n  ([7d4ed98](https://github.com/ory/kratos/commit/7d4ed9873f25b14b59f727002fb08a8b8a4e91a6))\n- Regenerate and update changelog\n  ([175b626](https://github.com/ory/kratos/commit/175b626f74b4471e068bd79259c6d479fd6c1a7d))\n- Regenerate and update changelog\n  ([e60e2df](https://github.com/ory/kratos/commit/e60e2df5d5cc4c1ef8a6a7f13487d4ebbf54741e))\n- Regenerate and update changelog\n  ([41eeb75](https://github.com/ory/kratos/commit/41eeb7587fad864f64c4179ac20847f902c438b3))\n- Regenerate and update changelog\n  ([468105a](https://github.com/ory/kratos/commit/468105a6080b861f1e02db3a404f2bac7f2f5eb6))\n- Regenerate and update changelog\n  ([8414520](https://github.com/ory/kratos/commit/8414520c995cb2405ed051952357d37ca8111f25))\n- Regenerate and update changelog\n  ([85d5866](https://github.com/ory/kratos/commit/85d5866df403b3cfa5566cef5cb983714b395505))\n- Regenerate and update changelog\n  ([e8d2d10](https://github.com/ory/kratos/commit/e8d2d1019bbc05fbe4eeaaee7a8eb1e8f2d18cf9))\n- Regenerate and update changelog\n  ([4c58b6d](https://github.com/ory/kratos/commit/4c58b6de4a3a39b1e94516abd1ea8ed7b09c1fe4))\n- Regenerate and update changelog\n  ([a726eb2](https://github.com/ory/kratos/commit/a726eb202a070038148612f98f12e5d22170d1ec))\n- Regenerate and update changelog\n  ([87b47ba](https://github.com/ory/kratos/commit/87b47baa9cdc0175c58ccbb20e67b458ce6a445f))\n- Regenerate and update changelog\n  ([537d496](https://github.com/ory/kratos/commit/537d496d2043a17c68f31a8744c39bc76f76314c))\n- Regenerate and update changelog\n  ([00e6af9](https://github.com/ory/kratos/commit/00e6af96060ec38059c449ac5e8b3c1df5bb8c95))\n- Regenerate and update changelog\n  ([48a2eca](https://github.com/ory/kratos/commit/48a2eca2dcd274ca73d55132efca4a6dae63efdf))\n- Regenerate and update changelog\n  ([8a71948](https://github.com/ory/kratos/commit/8a719481b54957681aa21eff5415229f3e5d4bff))\n- Regenerate and update changelog\n  ([ad3d510](https://github.com/ory/kratos/commit/ad3d5101dad3c8a2725083c63f155638905b6e8c))\n- Regenerate and update changelog\n  ([48bcc70](https://github.com/ory/kratos/commit/48bcc704ed22d8c78620aa3a5f8ecb5b41937759))\n- Regenerate and update changelog\n  ([816a55c](https://github.com/ory/kratos/commit/816a55c81a27b53d5bd823392751853b68d3f607))\n- Regenerate and update changelog\n  ([4ed74d2](https://github.com/ory/kratos/commit/4ed74d25c45f6e439377329d42cd7ae0acf9d0f1))\n- Regenerate and update changelog\n  ([367927e](https://github.com/ory/kratos/commit/367927e716e7c1c6898151a5f14876fb30070dd3))\n- Regenerate and update changelog\n  ([38f4019](https://github.com/ory/kratos/commit/38f40190f54264808c7a2716555876d05cdf560f))\n- Typo in README.md ([#265](https://github.com/ory/kratos/issues/265))\n  ([9f865a2](https://github.com/ory/kratos/commit/9f865a2ebace801414b2de17fe2f627d91f23474))\n- Update banner url\n  ([292c986](https://github.com/ory/kratos/commit/292c986729d83187f7e77365e11ef74a6f3cadf6))\n- Update forum and chat links\n  ([3039191](https://github.com/ory/kratos/commit/30391919d7ea58609dd3cd37db2709495e7abc76))\n- Update github templates ([#338](https://github.com/ory/kratos/issues/338))\n  ([57dbc77](https://github.com/ory/kratos/commit/57dbc77b548383522ca428e899dfde461334216c))\n- Update github templates ([#343](https://github.com/ory/kratos/issues/343))\n  ([eb13dc1](https://github.com/ory/kratos/commit/eb13dc1285cb16515d1c63b99cc389147508a31e))\n- Update github templates ([#350](https://github.com/ory/kratos/issues/350))\n  ([faf2f30](https://github.com/ory/kratos/commit/faf2f305aea1826e3d5f0b2614313920ac2b585b))\n- Update github templates ([#351](https://github.com/ory/kratos/issues/351))\n  ([20ff289](https://github.com/ory/kratos/commit/20ff2890004745231073cd4fd6ef1b37521cde72))\n- Update linux install guide\n  ([3b8e549](https://github.com/ory/kratos/commit/3b8e5493a01357f8c442a8a2dc9437712498452c))\n- Update linux install guide ([#354](https://github.com/ory/kratos/issues/354))\n  ([ec49cae](https://github.com/ory/kratos/commit/ec49caec6ddea2c800db0779005bac6da73903e1))\n- Update self service reg docs\n  ([#367](https://github.com/ory/kratos/issues/367))\n  ([4cf0323](https://github.com/ory/kratos/commit/4cf0323095990c5ec25283a01561cb9b8833f9ef)):\n\n  The old links pointed at `/auth/browser/(login|registration)` which seems to\n  be outdated now.\n\n  From the ui node code:\n  https://github.com/ory/kratos-selfservice-ui-node/blob/489c76d1b0474ee55ef56804b28f54d8718747ba/src/routes/auth.ts#L28\n  and the api documentation for kratos\n  https://www.ory.sh/kratos/docs/reference/api#get-the-request-context-of-browser-based-login-user-flows,\n  these seem to be incorrect.\n\n  The actual url hit is\n  `/self-service/browser/flows/requests/(login|registration)`. This commit\n  updates those links\n\n  This blob was previously one large inline string, which personally made the\n  docs a bit hard to read. This formats it into an (arguably) easier to parse\n  code block\n\n- Update user-settings-profile-management.md\n  ([#322](https://github.com/ory/kratos/issues/322))\n  ([45dc3a5](https://github.com/ory/kratos/commit/45dc3a56c15ae442890313a7dbc784b75644248a))\n- Updates issue and pull request templates\n  ([#298](https://github.com/ory/kratos/issues/298))\n  ([1be738d](https://github.com/ory/kratos/commit/1be738d3f8e9bbc6dae31ffad5d990657a66761c))\n- Updates issue and pull request templates\n  ([#313](https://github.com/ory/kratos/issues/313))\n  ([299063c](https://github.com/ory/kratos/commit/299063caf2fdde40713bae4c36abb3b6fac7271d))\n- Updates issue and pull request templates\n  ([#314](https://github.com/ory/kratos/issues/314))\n  ([d5ae452](https://github.com/ory/kratos/commit/d5ae452a8ce5f641a40e510e82441d4eb8137218))\n- Updates issue and pull request templates\n  ([#315](https://github.com/ory/kratos/issues/315))\n  ([8b68db1](https://github.com/ory/kratos/commit/8b68db140a7fc1c0eaa9318c1759ea9d8d0c27df))\n- Use git checkout <tag> in quickstart\n  ([#339](https://github.com/ory/kratos/issues/339))\n  ([2d2562b](https://github.com/ory/kratos/commit/2d2562b587a69a2891ff29d927cb001e15d75b5d)),\n  closes [#335](https://github.com/ory/kratos/issues/335)\n\n### Features\n\n- Add `dsn: memory` shorthand ([#284](https://github.com/ory/kratos/issues/284))\n  ([e66a030](https://github.com/ory/kratos/commit/e66a030f7d67dec639121fb23dfc7f1444474c6b)),\n  closes [#228](https://github.com/ory/kratos/issues/228)\n- Add and test id hint in reauth flow\n  ([2298f01](https://github.com/ory/kratos/commit/2298f0140e77da870c842daa8eaca274e5d64254)),\n  closes [#323](https://github.com/ory/kratos/issues/323)\n- Add cypress e2e tests ([#334](https://github.com/ory/kratos/issues/334))\n  ([abc0e91](https://github.com/ory/kratos/commit/abc0e91e278f7938b264598ac0c60d18c5a9e8a0))\n- Allow configuring same-site for session cookies\n  ([#303](https://github.com/ory/kratos/issues/303))\n  ([2eb2054](https://github.com/ory/kratos/commit/2eb2054a94281aefa9a0818110d168cc9c052094)),\n  closes [#257](https://github.com/ory/kratos/issues/257):\n\n  It is now possible to set SameSite for the session cookie via the key\n  `security.session.cookie.same_site`.\n\n- **continuity:** Implement request continuity\n  ([135e047](https://github.com/ory/kratos/commit/135e04750b1855ab0db812517c61e292a770ba94)),\n  closes [#304](https://github.com/ory/kratos/issues/304)\n  [#311](https://github.com/ory/kratos/issues/311):\n\n  This patch adds a module which is capable of aborting a request, waiting for\n  another option to complete, and then resuming the request again.\n\n  This feature makes use of a temporary cookie which keeps track of the request\n  state.\n\n  This feature is required for several workflows that update privileged fields\n  such as passwords, 2fa recovery codes, email addresses.\n\n  refactor: rename profile to settings flow\n\n  Renames selfservice/profile to settings. The settings flow includes a strategy\n  for managing profile information\n\n- Enable CockroachDB integration\n  ([#260](https://github.com/ory/kratos/issues/260))\n  ([adc5153](https://github.com/ory/kratos/commit/adc5153410fb4d9f99702d7c73a78aeec8c1e9f1)),\n  closes [#132](https://github.com/ory/kratos/issues/132)\n  [#155](https://github.com/ory/kratos/issues/155)\n- Enable continuity management for settings module\n  ([009d755](https://github.com/ory/kratos/commit/009d7558f525168fecf86168de2906088662535e))\n- Enable updating auth related traits\n  ([#266](https://github.com/ory/kratos/issues/266))\n  ([65b88ba](https://github.com/ory/kratos/commit/65b88ba52fb9e6da3c1a65f734352519303327a6)),\n  closes [#243](https://github.com/ory/kratos/issues/243)\n- Implement password profile management flow\n  ([a31839a](https://github.com/ory/kratos/commit/a31839a5c33c80500c900fb50d1dd499ab1161a1)),\n  closes [#243](https://github.com/ory/kratos/issues/243)\n- Introduce fallbacks for required configs\n  ([#376](https://github.com/ory/kratos/issues/376))\n  ([b3bcb25](https://github.com/ory/kratos/commit/b3bcb25be6b417647ece2b3dda26d691f8e8d685)),\n  closes [#369](https://github.com/ory/kratos/issues/369)\n  [#352](https://github.com/ory/kratos/issues/352)\n- **login:** Forced reauthentication\n  ([#248](https://github.com/ory/kratos/issues/248))\n  ([344fc9c](https://github.com/ory/kratos/commit/344fc9cddccff958f13249b999a835d3e46a7771)),\n  closes [#243](https://github.com/ory/kratos/issues/243)\n- Return 410 when selfservice requests expire\n  ([#289](https://github.com/ory/kratos/issues/289))\n  ([b414607](https://github.com/ory/kratos/commit/b4146076148d9ff079e9d433f0a90f5bc938650c)),\n  closes [#235](https://github.com/ory/kratos/issues/235)\n- Send verification emails on profile update\n  ([#333](https://github.com/ory/kratos/issues/333))\n  ([1cacc80](https://github.com/ory/kratos/commit/1cacc80c54f92b380ef3752591970cc4dd97085e)),\n  closes [#267](https://github.com/ory/kratos/issues/267)\n\n### Unclassified\n\n- u\n  ([0b6fa48](https://github.com/ory/kratos/commit/0b6fa48e90fa0c50b9c26bae034eb1662c855d69))\n- u\n  ([03fa4f0](https://github.com/ory/kratos/commit/03fa4f05363aa1f38fe45730317375ce380cfa31))\n- u\n  ([a3dfd9d](https://github.com/ory/kratos/commit/a3dfd9d15e1f7287558b85c3a4f23d02444b0bf4))\n- u\n  ([616aa0f](https://github.com/ory/kratos/commit/616aa0f0cf3d662b48fcaa02715e02e854e05581))\n- fix:add graceful shutdown to courier handler (#296)\n  ([235d784](https://github.com/ory/kratos/commit/235d784b7f8bf38859d15d68c37b089fc9371195)),\n  closes [#296](https://github.com/ory/kratos/issues/296)\n  [#295](https://github.com/ory/kratos/issues/295):\n\n  Courier would not stop with the provided Background handler. This changes the\n  methods of Courier so that the graceful package can be used in the same way as\n  the http endpoints can be used.\n\n- fix(sql) change courier body to text field (#276)\n  ([ed5268d](https://github.com/ory/kratos/commit/ed5268d539b2a28f5367e8ba2e2e6bd3a605ce5b)),\n  closes [#276](https://github.com/ory/kratos/issues/276)\n  [#269](https://github.com/ory/kratos/issues/269)\n- Make format\n  ([b85e5af](https://github.com/ory/kratos/commit/b85e5af2e29f9ca3bc3341ba4f2b1b338b441398))\n\n# [0.1.1-alpha.1](https://github.com/ory/kratos/compare/v0.1.0-alpha.6...v0.1.1-alpha.1) (2020-02-18)\n\ndocs: Regenerate and update changelog\n\n### Bug Fixes\n\n- Add verify return to address\n  ([#252](https://github.com/ory/kratos/issues/252))\n  ([64ab9e5](https://github.com/ory/kratos/commit/64ab9e510e6b65f9dd16fdfaadfd24785dab0c93))\n- Clean up docker quickstart ([#255](https://github.com/ory/kratos/issues/255))\n  ([7f0996b](https://github.com/ory/kratos/commit/7f0996b99646e57136f20c04a77a6f682eecdd9c))\n- Resolve several verification problems\n  ([#253](https://github.com/ory/kratos/issues/253))\n  ([30d4632](https://github.com/ory/kratos/commit/30d46326373cf038b600ee07db3e95ce6d94ab12))\n- Update verify URLs ([#258](https://github.com/ory/kratos/issues/258))\n  ([5d4f909](https://github.com/ory/kratos/commit/5d4f9099b5c61ff9572ad23a3eb9c0e0025d92da))\n\n### Code Refactoring\n\n- Support context-based SQL transactions\n  ([#254](https://github.com/ory/kratos/issues/254))\n  ([6ace1ee](https://github.com/ory/kratos/commit/6ace1ee2070c35b0da3e36dcd5417ff70a4ff9cb))\n\n### Documentation\n\n- Regenerate and update changelog\n  ([a125822](https://github.com/ory/kratos/commit/a1258221a1fef82cc525be7b1042e91e2d20b1eb))\n- Regenerate and update changelog\n  ([b3a8220](https://github.com/ory/kratos/commit/b3a822035509ec2c9fb04037b2088ce6df8191da))\n- Regenerate and update changelog\n  ([a141b30](https://github.com/ory/kratos/commit/a141b309a1fc22bc45d70a090869fdee198a065e))\n- Regenerate and update changelog\n  ([7e12e20](https://github.com/ory/kratos/commit/7e12e20be0fa61a2f41a416a3edcd2b522165196))\n- Regenerate and update changelog\n  ([3c1c67b](https://github.com/ory/kratos/commit/3c1c67b31a54dd8d5fceac9449d305db82ff8844))\n- Regenerate and update changelog\n  ([ee07937](https://github.com/ory/kratos/commit/ee07937d5e797f0217c86946da42d0070ca7c250))\n\n# [0.1.0-alpha.6](https://github.com/ory/kratos/compare/v0.1.0-alpha.5...v0.1.0-alpha.6) (2020-02-16)\n\nfeat: Add verification to quickstart (#251)\n\n### Bug Fixes\n\n- Adapt quickstart to verify changes\n  ([#247](https://github.com/ory/kratos/issues/247))\n  ([24eceb7](https://github.com/ory/kratos/commit/24eceb7147cef1081ac1ad969713ca1bc36229cb))\n- Gracefully handle selfservice request expiry\n  ([#242](https://github.com/ory/kratos/issues/242))\n  ([4421e6b](https://github.com/ory/kratos/commit/4421e6bde494fbe9672251cf813a39e3031bf3fd)),\n  closes [#233](https://github.com/ory/kratos/issues/233)\n- Set AuthenticatedAt in session issuer hook\n  ([#246](https://github.com/ory/kratos/issues/246))\n  ([29c83fa](https://github.com/ory/kratos/commit/29c83fa986c612fb17e13fe9415f7836062159d2)),\n  closes [#224](https://github.com/ory/kratos/issues/224)\n- **swagger:** Sanitize before validate\n  ([c72f140](https://github.com/ory/kratos/commit/c72f140083e94f3a47ee2398c56d188e6d4edcb4))\n- **swagger:** Use correct annotations for request methods\n  ([#237](https://github.com/ory/kratos/issues/237))\n  ([8473c85](https://github.com/ory/kratos/commit/8473c85d8282b27375b53babbbc79046d407b3fb)),\n  closes [#234](https://github.com/ory/kratos/issues/234)\n\n### Code Refactoring\n\n- Move to ory/jsonschema/v3 everywhere\n  ([#229](https://github.com/ory/kratos/issues/229))\n  ([61f5c1d](https://github.com/ory/kratos/commit/61f5c1d3d896841b08deb08c42ba896118e3fc71)),\n  closes [#225](https://github.com/ory/kratos/issues/225)\n\n### Documentation\n\n- Regenerate and update changelog\n  ([922cf0f](https://github.com/ory/kratos/commit/922cf0f3d7ec8860d13aff3b88849a71fb59e2c9))\n- Regenerate and update changelog\n  ([e097c23](https://github.com/ory/kratos/commit/e097c23d8b4902a9013f3a8fa9a397033a92fb88))\n- Regenerate and update changelog\n  ([2d1685f](https://github.com/ory/kratos/commit/2d1685f4f4235e9293b1ab79e67050042787c6e9))\n- Regenerate and update changelog\n  ([f8964e9](https://github.com/ory/kratos/commit/f8964e9e5c442f75ba501ce7cfcb18916b781dc1))\n- Regenerate and update changelog\n  ([92b8001](https://github.com/ory/kratos/commit/92b80013c98e9556138eff04aa24dc696b8d6128))\n- Regenerate and update changelog\n  ([d7083ab](https://github.com/ory/kratos/commit/d7083ab9fb8e8172707cae3ac4a8a183f0c25903))\n- Regenerate and update changelog\n  ([c4547dc](https://github.com/ory/kratos/commit/c4547dc53ecf167b63e5d7d3b6764535bd86fa5a))\n- Regenerate and update changelog\n  ([d8d8bba](https://github.com/ory/kratos/commit/d8d8bbae055e2220023a45b832d2435984191029))\n- Regenerate and update changelog\n  ([b012ed9](https://github.com/ory/kratos/commit/b012ed9ce1f4fd0ece2e3463e952711b4380f4a4))\n\n### Features\n\n- Add disabled flag to identifier form fields\n  ([#238](https://github.com/ory/kratos/issues/238))\n  ([a2178bd](https://github.com/ory/kratos/commit/a2178bdbbe20798a3e1e3fb5ed7b44afc187c640)),\n  closes [#227](https://github.com/ory/kratos/issues/227)\n- Add verification to quickstart\n  ([#251](https://github.com/ory/kratos/issues/251))\n  ([172dc87](https://github.com/ory/kratos/commit/172dc87d22f925668c21da1b3b581156e01d45a4))\n- Implement email verification\n  ([#245](https://github.com/ory/kratos/issues/245))\n  ([eed00f4](https://github.com/ory/kratos/commit/eed00f4b328c173057455980ce0e1aad909c278f)),\n  closes [#27](https://github.com/ory/kratos/issues/27)\n- Improve password validation strategy\n  ([#231](https://github.com/ory/kratos/issues/231))\n  ([256fad3](https://github.com/ory/kratos/commit/256fad37164c81cc44c35e77b99911996722a86a))\n\n# [0.1.0-alpha.5](https://github.com/ory/kratos/compare/v0.1.0-alpha.4...v0.1.0-alpha.5) (2020-02-06)\n\ndocs: Regenerate and update changelog\n\n### Documentation\n\n- Regenerate and update changelog\n  ([e87e9c9](https://github.com/ory/kratos/commit/e87e9c9ec9cf55351439ab16a778f3ea303ec646))\n- Regenerate and update changelog\n  ([d6f0794](https://github.com/ory/kratos/commit/d6f0794d53b6e7d6d9e3bc63a77d402e43a29bed))\n- Regenerate and update changelog\n  ([eb7326c](https://github.com/ory/kratos/commit/eb7326c98c2d5e87a8ac3cd9f2efb43f2552164a))\n\n### Features\n\n- Redirect to new auth session on expired auth sessions\n  ([#230](https://github.com/ory/kratos/issues/230))\n  ([b477ecd](https://github.com/ory/kratos/commit/b477ecd47de33a9a45159a298ac288c4ad5a0b55)),\n  closes [#96](https://github.com/ory/kratos/issues/96)\n\n# [0.1.0-alpha.4](https://github.com/ory/kratos/compare/v0.1.0-alpha.3...v0.1.0-alpha.4) (2020-02-06)\n\nci: Bump ory/sdk to 0.1.22\n\n### Continuous Integration\n\n- Bump ory/sdk to 0.1.22\n  ([c0d0edf](https://github.com/ory/kratos/commit/c0d0edf1f369ecaeb28d1337930b16222b97337f))\n\n### Documentation\n\n- Regenerate and update changelog\n  ([f02afb3](https://github.com/ory/kratos/commit/f02afb3fed310f7fe9c5e6f7df34dfc9738018ad))\n\n# [0.1.0-alpha.3](https://github.com/ory/kratos/compare/v0.1.0-alpha.2...v0.1.0-alpha.3) (2020-02-06)\n\nci: Bump ory/sdk orb\n\n### Continuous Integration\n\n- Bump ory/sdk orb\n  ([65b2ca0](https://github.com/ory/kratos/commit/65b2ca0b8a1da8249aa4b4cb439b1d63aecaf8e0))\n\n# [0.1.0-alpha.2](https://github.com/ory/kratos/compare/v0.1.0-alpha.1...v0.1.0-alpha.2) (2020-02-03)\n\ndocs: Regenerate and update changelog\n\n### Bug Fixes\n\n- Add paths to sqa middleware ([#216](https://github.com/ory/kratos/issues/216))\n  ([130c9c2](https://github.com/ory/kratos/commit/130c9c242e1434074d9fa4970b60ccb9b4f2ff47))\n- **daemon:** Register error routes on admin port\n  ([#226](https://github.com/ory/kratos/issues/226))\n  ([decd8d8](https://github.com/ory/kratos/commit/decd8d8ef8dac3674938b564962238195ffaf017))\n- Set csrf token on public endpoints\n  ([d0b15ae](https://github.com/ory/kratos/commit/d0b15aeca991a94771715a6eabd4a956be41ceda))\n\n### Documentation\n\n- Introduce upgrade guide\n  ([736a3b1](https://github.com/ory/kratos/commit/736a3b19bfe35cc699dea508b4bdb56b3302ba7e))\n- Prepare ecosystem automation\n  ([7013b6c](https://github.com/ory/kratos/commit/7013b6c9a856e05f6ad385eb8ce36c5faf342f5a))\n- Regenerate and update changelog\n  ([f39b942](https://github.com/ory/kratos/commit/f39b9422d79d3e69304f013c85f3850337ca1730))\n- Regenerate and update changelog\n  ([c121601](https://github.com/ory/kratos/commit/c121601b5c741c846d9c478b01aabb9907d81b95))\n- Regenerate and update changelog\n  ([a947d55](https://github.com/ory/kratos/commit/a947d554ba2be94f334568a4e77a501742ca95af))\n- Regenerate and update changelog\n  ([8ba2044](https://github.com/ory/kratos/commit/8ba2044ebb369ea741f99c65163f650c607e6c07))\n- Regenerate and update changelog\n  ([9c023e1](https://github.com/ory/kratos/commit/9c023e1a9288f156c79ea78b3a979d0fefab8825))\n- Regenerate and update changelog\n  ([1e855a9](https://github.com/ory/kratos/commit/1e855a9e0ebd232ba2b07dc4a8bb79b84cd548e6))\n- Regenerate and update changelog\n  ([01ce3a8](https://github.com/ory/kratos/commit/01ce3a891edd84174694111637dd44fe65e48b37))\n- Updates issue and pull request templates\n  ([#222](https://github.com/ory/kratos/issues/222))\n  ([4daae88](https://github.com/ory/kratos/commit/4daae88af527018e9ee4e1e9717a07dffab427fe))\n\n### Features\n\n- Override semantic config ([#220](https://github.com/ory/kratos/issues/220))\n  ([9b4214b](https://github.com/ory/kratos/commit/9b4214bf5eac81a92513e04dc5f862b93df86935))\n\n### Unclassified\n\n- Update CHANGELOG [ci skip]\n  ([ce9390c](https://github.com/ory/kratos/commit/ce9390c27f61966b7ed23244400215c2218bbc0b))\n- refactor!: Improve user-facing error APIs (#219)\n  ([7d4054f](https://github.com/ory/kratos/commit/7d4054f4363da7bc0e943e7abfbd0c804eb7f0c1)),\n  closes [#219](https://github.com/ory/kratos/issues/219)\n  [#204](https://github.com/ory/kratos/issues/204):\n\n  This patch refactors user-facing error APIs:\n  - The `/errors` endpoint moved to `/self-service/errors`\n  - The endpoint is now available at both the Admin and Public API. The Public\n    API requires CSRF Token match or a 403 error will be returned.\n  - The Public API endpoint no longer returns 404 errors but 403 instead.\n  - The response payload changed. What was `[{\"code\": ...}]` is now\n    `{\"id\": \"...\", \"errors\": [{\"code\": ...}]}`\n\n  This patch requires running `kratos migrate sql` as a new column\n  (`csrf_token`) has been added to the user-facing error store.\n\n- Update CHANGELOG [ci skip]\n  ([c368a11](https://github.com/ory/kratos/commit/c368a11523a9bcb30a830d65c11e4f6d27417a78))\n\n# [0.1.0-alpha.1](https://github.com/ory/kratos/compare/v0.0.3-alpha.15...v0.1.0-alpha.1) (2020-01-31)\n\ndocs: Updates issue and pull request templates (#215)\n\nSigned-off-by: aeneasr <aeneas@ory.sh>\n\n### Documentation\n\n- Updates issue and pull request templates\n  ([#215](https://github.com/ory/kratos/issues/215))\n  ([10c45f2](https://github.com/ory/kratos/commit/10c45f23e11abba1ca82095548769cd923a6a6a6))\n\n# [0.0.3-alpha.15](https://github.com/ory/kratos/compare/v0.0.3-alpha.14...v0.0.3-alpha.15) (2020-01-31)\n\nUpdate permissions in SQLite Dockerfile\n\n### Unclassified\n\n- Update permissions in SQLite Dockerfile\n  ([1266e53](https://github.com/ory/kratos/commit/1266e533ac9a1f6ec375980cadce9755998f9fe6))\n\n# [0.0.3-alpha.14](https://github.com/ory/kratos/compare/v0.0.3-alpha.13...v0.0.3-alpha.14) (2020-01-31)\n\nUpdate README.md\n\n### Unclassified\n\n- Update README.md\n  ([db8d65b](https://github.com/ory/kratos/commit/db8d65bf136223df546aa27f1ecff03d01159624))\n\n# [0.0.3-alpha.13](https://github.com/ory/kratos/compare/v0.0.3-alpha.12...v0.0.3-alpha.13) (2020-01-31)\n\nAllow mounting SQLite in /home/ory/sqlite (#212)\n\n### Unclassified\n\n- Allow mounting SQLite in /home/ory/sqlite (#212)\n  ([2fe8c0f](https://github.com/ory/kratos/commit/2fe8c0f752e870028d68e8593a46c0902f673a65)),\n  closes [#212](https://github.com/ory/kratos/issues/212)\n\n# [0.0.3-alpha.11](https://github.com/ory/kratos/compare/v0.0.3-alpha.10...v0.0.3-alpha.11) (2020-01-31)\n\nClean up cmd and resolve packr2 issues (#211)\n\nThis patch addresses issues with the build pipeline caused by an invalid import.\nProfiling was also added.\n\n### Unclassified\n\n- Clean up cmd and resolve packr2 issues (#211)\n  ([2e43ec0](https://github.com/ory/kratos/commit/2e43ec09e9d6aa572c4351bfef4c59dfc43f2343)),\n  closes [#211](https://github.com/ory/kratos/issues/211):\n\n  This patch addresses issues with the build pipeline caused by an invalid\n  import. Profiling was also added.\n\n- Improve field types (#209)\n  ([aeefa93](https://github.com/ory/kratos/commit/aeefa93bf0427685f6ffadad5abfaa1fc26ce074)),\n  closes [#209](https://github.com/ory/kratos/issues/209)\n- Update CHANGELOG [ci skip]\n  ([fc32207](https://github.com/ory/kratos/commit/fc32207482861b8f989cb1d6fe5d96bf34c54e4c))\n\n# [0.0.3-alpha.10](https://github.com/ory/kratos/compare/v0.0.3-alpha.9...v0.0.3-alpha.10) (2020-01-31)\n\nUpdate README\n\n### Unclassified\n\n- Update README\n  ([35a310d](https://github.com/ory/kratos/commit/35a310d6de52fa74ad8728b1df67f88ce900aa61))\n- Update CHANGELOG [ci skip]\n  ([3c98745](https://github.com/ory/kratos/commit/3c987455a44b9e12e31619ba9f447e8a5feafc38))\n- Update CHANGELOG [ci skip]\n  ([c1c01df](https://github.com/ory/kratos/commit/c1c01df3a04fc7988bf847e3f31680112f5a642d))\n\n# [0.0.3-alpha.7](https://github.com/ory/kratos/compare/v0.0.3-alpha.5...v0.0.3-alpha.7) (2020-01-30)\n\nUse correct project root in Dockerfile\n\n### Unclassified\n\n- Use correct project root in Dockerfile\n  ([3528758](https://github.com/ory/kratos/commit/352875878c74d15b522336b518df339c8ad48e49))\n- Update CHANGELOG [ci skip]\n  ([e78bbbe](https://github.com/ory/kratos/commit/e78bbbecbd9515c02e447efc3208599bf27ef85c))\n\n# [0.0.3-alpha.5](https://github.com/ory/kratos/compare/v0.0.3-alpha.4...v0.0.3-alpha.5) (2020-01-30)\n\nci: Resolve final docker build issues (#210)\n\n### Continuous Integration\n\n- Resolve final docker build issues\n  ([#210](https://github.com/ory/kratos/issues/210))\n  ([d703a1e](https://github.com/ory/kratos/commit/d703a1e328808df6761a9da5866a3f4df4c7923e))\n\n### Unclassified\n\n- Update CHANGELOG [ci skip]\n  ([ebb1744](https://github.com/ory/kratos/commit/ebb1744d68b8a416774477182b1e2b2cd8bdfc43))\n- Add libmusl to binary output\n  ([e9b8445](https://github.com/ory/kratos/commit/e9b8445f2fc8e9e571ec0b8480cc70fe3251db9e))\n\n# [0.0.3-alpha.4](https://github.com/ory/kratos/compare/v0.0.3-alpha.3...v0.0.3-alpha.4) (2020-01-30)\n\nUpdate CHANGELOG [ci skip]\n\n### Unclassified\n\n- Update CHANGELOG [ci skip]\n  ([018c229](https://github.com/ory/kratos/commit/018c229c4cff62e47c1154ca29ab9c70766a43e5))\n- Add and use ory docker user\n  ([cccbe09](https://github.com/ory/kratos/commit/cccbe09cc6e2ad72847206d46afe3e0bf7f79ab5))\n- Update CHANGELOG [ci skip]\n  ([0e436e5](https://github.com/ory/kratos/commit/0e436e57f79692c4c6e0a0c25f48a41654afcda1))\n- Update goreleaser changelog filters\n  ([7e5af97](https://github.com/ory/kratos/commit/7e5af97fded9f56a3cc9d1d92a7726e7b613b586))\n- Update CHANGELOG [ci skip]\n  ([4387503](https://github.com/ory/kratos/commit/438750326c5d6ad1569802c82806e831f43e785e))\n\n# [0.0.3-alpha.2](https://github.com/ory/kratos/compare/v0.0.3-alpha.1...v0.0.3-alpha.2) (2020-01-30)\n\nResolve goreleaser build issues (#208)\n\n### Unclassified\n\n- Resolve goreleaser build issues (#208)\n  ([d59a08a](https://github.com/ory/kratos/commit/d59a08a0ef680a984352d7f5068626cc1958185a)),\n  closes [#208](https://github.com/ory/kratos/issues/208)\n\n# [0.0.3-alpha.1](https://github.com/ory/kratos/compare/v0.0.1-alpha.9...v0.0.3-alpha.1) (2020-01-30)\n\nUpdate CHANGELOG [ci skip]\n\n### Unclassified\n\n- Update CHANGELOG [ci skip]\n  ([49e09ea](https://github.com/ory/kratos/commit/49e09eaaab1fc681f9330e12ce6e5483c62ee9e3))\n- Take form field orders from JSON Schema (#205)\n  ([a880f0d](https://github.com/ory/kratos/commit/a880f0ddb52fb4366acf8fbd80aabaa9843445a9)),\n  closes [#205](https://github.com/ory/kratos/issues/205)\n  [#176](https://github.com/ory/kratos/issues/176)\n- Update CHANGELOG [ci skip]\n  ([ff52bbb](https://github.com/ory/kratos/commit/ff52bbb264542b48658679bf5563b0f3b7ad73c7))\n- Adapt quickstart docker compose config (#207)\n  ([e532583](https://github.com/ory/kratos/commit/e532583b35a22cb39bbab0101bf86c0bf01b1088)),\n  closes [#207](https://github.com/ory/kratos/issues/207)\n- Update CHANGELOG [ci skip]\n  ([7f4800b](https://github.com/ory/kratos/commit/7f4800b07556e688ba0cd551438876b3bf23ace5))\n- Update CHANGELOG [ci skip]\n  ([1b2c3f6](https://github.com/ory/kratos/commit/1b2c3f645e64848e7fba6656aa730c7e346ed75d))\n- Rework public and admin fetch strategy (#203)\n  ([99aa169](https://github.com/ory/kratos/commit/99aa1693e758f706f264c2439594e2be37ae9bc6)),\n  closes [#203](https://github.com/ory/kratos/issues/203)\n  [#122](https://github.com/ory/kratos/issues/122)\n- Update CHANGELOG [ci skip]\n  ([1cea427](https://github.com/ory/kratos/commit/1cea42780a95d4ebf5520e1c1803fb13ef596d52))\n- ss/profile: Use request ID as query param everywhere (#202)\n  ([ed32b14](https://github.com/ory/kratos/commit/ed32b14f8ea972cf549480f29cbf1b95d010789c)),\n  closes [#202](https://github.com/ory/kratos/issues/202)\n  [#190](https://github.com/ory/kratos/issues/190)\n- Update CHANGELOG [ci skip]\n  ([a392027](https://github.com/ory/kratos/commit/a3920278129399ce576c5336c2e50dd015b8f2f8))\n- Update HTTP routes for a consistent API naming (#199)\n  ([9ed4bda](https://github.com/ory/kratos/commit/9ed4bda9f0b0d45e8ac0de0c42b78f717f3d92f3)),\n  closes [#199](https://github.com/ory/kratos/issues/199)\n  [#195](https://github.com/ory/kratos/issues/195)\n\n# [0.0.1-alpha.9](https://github.com/ory/kratos/compare/v0.0.1-alpha.11...v0.0.1-alpha.9) (2020-01-29)\n\nci: Bump goreleaser orb\n\n### Continuous Integration\n\n- Bump goreleaser orb\n  ([29cd754](https://github.com/ory/kratos/commit/29cd754d33ec2f800730bd007f17fc0ce53a51eb))\n\n# [0.0.2-alpha.1](https://github.com/ory/kratos/compare/v0.0.1-alpha.8...v0.0.2-alpha.1) (2020-01-29)\n\nUse correct build archive for homebrew\n\n### Unclassified\n\n- Use correct build archive for homebrew\n  ([74ac29f](https://github.com/ory/kratos/commit/74ac29f43f2937cad9065ad3c03cf3cf909cff42))\n\n# [0.0.1-alpha.6](https://github.com/ory/kratos/compare/v0.0.1-alpha.5...v0.0.1-alpha.6) (2020-01-29)\n\nci: Bump goreleaser orb\n\n### Continuous Integration\n\n- Bump goreleaser orb\n  ([018c94c](https://github.com/ory/kratos/commit/018c94ccc9e833f28f827fd10d607a7a1c954ac5))\n\n# [0.0.1-alpha.5](https://github.com/ory/kratos/compare/v0.0.1-alpha.3...v0.0.1-alpha.5) (2020-01-29)\n\nci: Bump goreleaser dependency\n\n### Continuous Integration\n\n- Bump goreleaser dependency\n  ([ec49bfb](https://github.com/ory/kratos/commit/ec49bfb4b636a72e51d3a68521ba047f97d4c5e6))\n\n### Unclassified\n\n- Resolve build issues with CGO (#196)\n  ([298f4ea](https://github.com/ory/kratos/commit/298f4ea85b3e7405929f481b756efe8c5c133479)),\n  closes [#196](https://github.com/ory/kratos/issues/196)\n- ss/password: Make form fields an array (#197)\n  ([6cb0058](https://github.com/ory/kratos/commit/6cb005860755ff897ad847f09af50bc911bbc7f0)),\n  closes [#197](https://github.com/ory/kratos/issues/197)\n  [#186](https://github.com/ory/kratos/issues/186)\n\n# [0.0.1-alpha.3](https://github.com/ory/kratos/compare/ab6f24a85276bdd8687f2fc06390c1279892b005...v0.0.1-alpha.3) (2020-01-28)\n\nci: Only compile goarmv7\n\n### Continuous Integration\n\n- Only compile goarmv7\n  ([d8e7ec7](https://github.com/ory/kratos/commit/d8e7ec788d1b43bcbbe221becde3432fdbf28e9b))\n\n### Documentation\n\n- Present ORY Hive to the world\n  ([#107](https://github.com/ory/kratos/issues/107))\n  ([7883589](https://github.com/ory/kratos/commit/78835897664a5ab5564751fc9f04172f7d20d572))\n- Updates issue and pull request templates\n  ([0441dff](https://github.com/ory/kratos/commit/0441dffe0c439cc54214bf9ee8f4a4bd25206999))\n- Updates issue and pull request templates\n  ([#174](https://github.com/ory/kratos/issues/174))\n  ([ad405e9](https://github.com/ory/kratos/commit/ad405e9037e2db2910a012f414556fea672e732a))\n- Updates issue and pull request templates\n  ([#39](https://github.com/ory/kratos/issues/39))\n  ([daf5aa8](https://github.com/ory/kratos/commit/daf5aa89c717de6176ee25119d2e751ae2ef6558))\n- Updates issue and pull request templates\n  ([#40](https://github.com/ory/kratos/issues/40))\n  ([f5907f3](https://github.com/ory/kratos/commit/f5907f3f248e05511b19ff6dc15bf6f60f8b62da))\n- Updates issue and pull request templates\n  ([#59](https://github.com/ory/kratos/issues/59))\n  ([8c5612c](https://github.com/ory/kratos/commit/8c5612c080e5b7531028b778b86cc4cde2abd516))\n- Updates issue and pull request templates\n  ([#7](https://github.com/ory/kratos/issues/7))\n  ([a1220ba](https://github.com/ory/kratos/commit/a1220ba1e950498a6e9594266dc730c9a8731b49))\n- Updates issue and pull request templates\n  ([#8](https://github.com/ory/kratos/issues/8))\n  ([c56798a](https://github.com/ory/kratos/commit/c56798ab29e72ed308fff840e3b1b98ead19aea6))\n\n### Unclassified\n\n- Remove redundant return statement\n  ([7c2989f](https://github.com/ory/kratos/commit/7c2989f52c090bb9900380b4ec74e04d9c37a441))\n- ss/oidc: Remove obsolete request field from form (#193)\n  ([59671ba](https://github.com/ory/kratos/commit/59671badb63009e2440b14868b622adc75cf882f)),\n  closes [#193](https://github.com/ory/kratos/issues/193)\n  [#180](https://github.com/ory/kratos/issues/180)\n- strategy/oidc: Allow multiple OIDC Connections (#191)\n  ([8984831](https://github.com/ory/kratos/commit/898483137ff9dc47d65750cd94a973f2e5bee770)),\n  closes [#191](https://github.com/ory/kratos/issues/191)\n  [#114](https://github.com/ory/kratos/issues/114)\n- Improve Docker Compose Quickstart (#187)\n  ([9459072](https://github.com/ory/kratos/commit/945907297ded4b18e1bd0e7c9824a975ac7395c6)),\n  closes [#187](https://github.com/ory/kratos/issues/187)\n  [#188](https://github.com/ory/kratos/issues/188)\n- selfservice/password: Remove request field and ensure method is set (#183)\n  ([e035adc](https://github.com/ory/kratos/commit/e035adc233198e9b5c9a6e08d442fb5fb3290816)),\n  closes [#183](https://github.com/ory/kratos/issues/183)\n- Add tests and fixtures for the config JSON Schema (#171)\n  ([ede9c0e](https://github.com/ory/kratos/commit/ede9c0e9c45ee91e60587311dc18a0a04ff62295)),\n  closes [#171](https://github.com/ory/kratos/issues/171)\n- Add example values for config JSON Schema\n  ([12ba728](https://github.com/ory/kratos/commit/12ba7283bf879cd7682d3017c3b3f12e49029d6b))\n- Replace `url` with `uri` format in config JSON Schema\n  ([68eddef](https://github.com/ory/kratos/commit/68eddef0cf179bf61abb999d84d2af19c3703c80))\n- Replace number with integer in config JSON Schema (#177)\n  ([9eff6fd](https://github.com/ory/kratos/commit/9eff6fd09720b11acae089ebfcaf37288bc031b0)),\n  closes [#177](https://github.com/ory/kratos/issues/177)\n- Improve `--dev` flag (#167)\n  ([9b61ee1](https://github.com/ory/kratos/commit/9b61ee10bbb4710d6694addfa60c04313855516f)),\n  closes [#167](https://github.com/ory/kratos/issues/167)\n  [#162](https://github.com/ory/kratos/issues/162)\n- Add goreleaser orb task (#170)\n  ([5df0def](https://github.com/ory/kratos/commit/5df0defefc95ced289a9c59a4f5deb3c67446e75)),\n  closes [#170](https://github.com/ory/kratos/issues/170)\n- Add changelog generation task (#169)\n  ([edd937c](https://github.com/ory/kratos/commit/edd937c21b7e37b2f2e926f0fe62c2e7d4a7d608)),\n  closes [#169](https://github.com/ory/kratos/issues/169)\n- Adopt new SDK pipeline (#168)\n  ([21d9b6d](https://github.com/ory/kratos/commit/21d9b6d27adbfe8504fb46ac95952e7cea239085)),\n  closes [#168](https://github.com/ory/kratos/issues/168)\n- Add docker-compose quickstart (#153)\n  ([e096190](https://github.com/ory/kratos/commit/e096190e778f22573e30f35e85b7cf147caf851b)),\n  closes [#153](https://github.com/ory/kratos/issues/153)\n- Update README (#160)\n  ([533775b](https://github.com/ory/kratos/commit/533775ba78a2c1758c47ed093da6acc18ab951c2)),\n  closes [#160](https://github.com/ory/kratos/issues/160)\n- Separate post register/login hooks (#150)\n  ([f4b7812](https://github.com/ory/kratos/commit/f4b78122d9cbe4dcc05b4fd52d94a2d9f1b16eb2)),\n  closes [#150](https://github.com/ory/kratos/issues/150)\n  [#149](https://github.com/ory/kratos/issues/149)\n- Update README badges\n  ([4f7838e](https://github.com/ory/kratos/commit/4f7838e69181c5a10e27cde1e241779e4e724909))\n- Bump go-acc and resolve test issues (#154)\n  ([15b1b63](https://github.com/ory/kratos/commit/15b1b630c5363e0e1afbed53285b3f39098c0792)),\n  closes [#154](https://github.com/ory/kratos/issues/154)\n  [#152](https://github.com/ory/kratos/issues/152)\n  [#151](https://github.com/ory/kratos/issues/151):\n\n  Due to a bug in `go-acc`, tests would not run if `-tags sqlite` was supplied\n  as a go tool argument to `go-acc`. This patch resolves that issue and also\n  includes several test patches from previous community PRs and some internal\n  test issues.\n\n- Add ORY Kratos banner to README (#145)\n  ([23b824f](https://github.com/ory/kratos/commit/23b824f7f99efbc23787508c03506e73a3240a2a)),\n  closes [#145](https://github.com/ory/kratos/issues/145)\n- Replace DBAL layer with gobuffalo/pop (#130)\n  ([21d08b8](https://github.com/ory/kratos/commit/21d08b84560230d8a063a418a74efcf53c146872)),\n  closes [#130](https://github.com/ory/kratos/issues/130):\n\n  This is a major refactoring of the internal DBAL. After a successful proof of\n  concept and evaluation of gobuffalo/pop, we believe this to be the best DBAL\n  for Go at the moment. It abstracts a lot of boilerplate code away.\n\n  As with all sophisticated DBALs, pop too has its quirks. There are several\n  issues that have been discovered during testing and adoption:\n  https://github.com/gobuffalo/pop/issues/136\n  https://github.com/gobuffalo/pop/issues/476\n  https://github.com/gobuffalo/pop/issues/473\n  https://github.com/gobuffalo/pop/issues/469\n  https://github.com/gobuffalo/pop/issues/466\n\n  However, the upside of moving much of the hard database/sql plumbing into\n  another library cleans up the code base significantly and reduces complexity.\n\n  As part of this change, the \"ephermal\" DBAL (\"in memory\") will be removed and\n  sqlite will be used instead. This further reduces complexity of the code base\n  and code-duplication.\n\n  To support sqlite, CGO is required, which means that we need to run tests with\n  `go test -tags sqlite` on a machine that has g++ installed. This also means\n  that we need a Docker Image with `alpine` as opposed to pure `scratch`. While\n  this is certainly a downside, the upside of less maintenance and \"free\"\n  support for SQLite, PostgreSQL, MySQL, and CockroachDB simply outweighs any\n  downsides that come with CGO.\n\n- Replace local deps with remote ones\n  ([8605e45](https://github.com/ory/kratos/commit/8605e454cf538e047c5a9c3479372892d6b3f483))\n- ss/profile: Improve success and error flows\n  ([9e0015a](https://github.com/ory/kratos/commit/9e0015acec7f8d927498e48366b377e22ec768b7)),\n  closes [#112](https://github.com/ory/kratos/issues/112):\n\n  This patch completes the profile management flow by implementing proper error\n  and success states and adding several data integrity tests.\n\n- Rebrand ORY Hive to ORY Kratos (#111)\n  ([ceda7fb](https://github.com/ory/kratos/commit/ceda7fb3472b081f0c6066aa1f282d4ec1787f7b)),\n  closes [#111](https://github.com/ory/kratos/issues/111)\n- Fix broken tests and ci linter issues (#104)\n  ([69760fe](https://github.com/ory/kratos/commit/69760fe9fecb2f302dd5c1821185ea990f4e411c)),\n  closes [#104](https://github.com/ory/kratos/issues/104)\n- Update to Go modules 1.13\n  ([1da4d75](https://github.com/ory/kratos/commit/1da4d757bc2434f97c588e395305066edce9ef0d))\n- Resolve minor configuration issues and response errors (#85)\n  ([a44913b](https://github.com/ory/kratos/commit/a44913b26b515333576def6b882861ff2c8d4aff)),\n  closes [#85](https://github.com/ory/kratos/issues/85)\n- Clean up dead files (#84)\n  ([e0c96ef](https://github.com/ory/kratos/commit/e0c96effbee2521b12eeedc851b67fa3a1ae41c8)),\n  closes [#84](https://github.com/ory/kratos/issues/84)\n- Add health endpoints (#83)\n  ([0e936f7](https://github.com/ory/kratos/commit/0e936f7047bb9eacae0c5107360ce752a23d8282)),\n  closes [#83](https://github.com/ory/kratos/issues/83)\n  [#82](https://github.com/ory/kratos/issues/82)\n- Update Dockerfile and related build tools (#80)\n  ([d20c701](https://github.com/ory/kratos/commit/d20c701433cea916d3df4863846cf09743150966)),\n  closes [#80](https://github.com/ory/kratos/issues/80)\n- Implement SQL Database adapter (#79)\n  ([86d07c4](https://github.com/ory/kratos/commit/86d07c4a9e3b3e6607e73f4d54b4e7b9f0382e59)),\n  closes [#79](https://github.com/ory/kratos/issues/79)\n  [#69](https://github.com/ory/kratos/issues/69)\n- Prevent duplicate signups (#76)\n  ([4c88968](https://github.com/ory/kratos/commit/4c88968a6853396755f61db2673a0cb2201868f7)),\n  closes [#76](https://github.com/ory/kratos/issues/76)\n  [#46](https://github.com/ory/kratos/issues/46)\n- Contributing 08 10 19 00 52 45 (#74)\n  ([43b511f](https://github.com/ory/kratos/commit/43b511f1a43be114ac04b377434b22ec8afe465b)),\n  closes [#74](https://github.com/ory/kratos/issues/74)\n- Echo form values from oidc signup\n  ([98b1da5](https://github.com/ory/kratos/commit/98b1da5f59d5dcde4416b74ea323af3e29fefa75)),\n  closes [#71](https://github.com/ory/kratos/issues/71)\n- Properly decode values in error handler\n  ([5eb9088](https://github.com/ory/kratos/commit/5eb9088efb291256d65fadbd5a803369cc96bdd2)),\n  closes [#71](https://github.com/ory/kratos/issues/71)\n- Force path and domain on CSRF cookie (#70)\n  ([a80d8b0](https://github.com/ory/kratos/commit/a80d8b0e0bb16fce530559826de29fd6b9836873)),\n  closes [#70](https://github.com/ory/kratos/issues/70)\n  [#68](https://github.com/ory/kratos/issues/68)\n- Require no session when accessing login or sign up (#67)\n  ([c0e0da1](https://github.com/ory/kratos/commit/c0e0da1b38ebadaa33eb5b59dc566731b3320b70)),\n  closes [#67](https://github.com/ory/kratos/issues/67)\n  [#63](https://github.com/ory/kratos/issues/63)\n- Add tests for selfservice ErrorHandler (#62)\n  ([4bb9e70](https://github.com/ory/kratos/commit/4bb9e7086ee57c4eb1a73fea436c7b2dec0257b7)),\n  closes [#62](https://github.com/ory/kratos/issues/62)\n- Enable Circle CI (#57)\n  ([6fb0afd](https://github.com/ory/kratos/commit/6fb0afd30e3755026b6ffca0cc80f2fe00267681)),\n  closes [#57](https://github.com/ory/kratos/issues/57)\n  [#53](https://github.com/ory/kratos/issues/53)\n- OIDC provider selfservice data enrichment (#56)\n  ([936970a](https://github.com/ory/kratos/commit/936970a9abaadeab5c191ff52218bf4f65af2220)),\n  closes [#56](https://github.com/ory/kratos/issues/56)\n  [#23](https://github.com/ory/kratos/issues/23)\n  [#55](https://github.com/ory/kratos/issues/55)\n- Remove local jsonschema module override\n  ([cd2a5d8](https://github.com/ory/kratos/commit/cd2a5d8c74b21b122f5d5437702d8c74fb1cb726))\n- Implement identity management, login, and registration (#22)\n  ([bf3395e](https://github.com/ory/kratos/commit/bf3395ea34ecf85303034f3e941a049c8cbd6229)),\n  closes [#22](https://github.com/ory/kratos/issues/22)\n- Revert incorrect license changes\n  ([fb9740b](https://github.com/ory/kratos/commit/fb9740b37a94dbdde1a8f4433fb7e5a8b4dac295))\n- Create FUNDING.yml\n  ([3c67ac8](https://github.com/ory/kratos/commit/3c67ac83f58c5b03dc3935d279083268b8a85e0d))\n- Initial commit\n  ([ab6f24a](https://github.com/ory/kratos/commit/ab6f24a85276bdd8687f2fc06390c1279892b005))\n- Add ability to define multiple schemas and serve them over HTTP\n  ([#164](https://github.com/ory/kratos/issues/164))\n  ([c65119c](https://github.com/ory/kratos/commit/c65119c24378dabd306e5a49f89c28c0367f7c2e)),\n  closes [#86](https://github.com/ory/kratos/issues/86):\n\n  All identity traits schemas have to be configured using a human readable ID\n  and the corresponding URL. This PR enables multiple schemas to be used next to\n  the default schema. It also adds the kratos.public/schemas/:id endpoint that\n  mirrors all schemas.\n\n- Add helper for requiring authentication\n  ([3888fbd](https://github.com/ory/kratos/commit/3888fbdc239b7a06c7fca34d08de7d55af69a48c))\n- Add helpers for go-swagger\n  ([165a660](https://github.com/ory/kratos/commit/165a660f277588ed572d7843354c207f72f1678d)):\n\n  See https://github.com/go-swagger/go-swagger/issues/2119\n\n- Add profile management and refactor internals\n  ([3ec9263](https://github.com/ory/kratos/commit/3ec9263f597a5949d0de6d10073cc626cfcfcca4)),\n  closes [#112](https://github.com/ory/kratos/issues/112)\n- Add session destroyer hook ([#148](https://github.com/ory/kratos/issues/148))\n  ([d17f002](https://github.com/ory/kratos/commit/d17f002cdfe1f11ebb6bcbb17f6976aa329eab4a)),\n  closes [#139](https://github.com/ory/kratos/issues/139):\n\n  This patch adds a hook that destroys all active session by the identity which\n  is being logged in. This can be useful in scenarios where only one session\n  should be active at any given time.\n\n- Add SQL adapter ([#100](https://github.com/ory/kratos/issues/100))\n  ([9e7f998](https://github.com/ory/kratos/commit/9e7f99871e3f09e7ae9ec1c38c8b8cf94d076f45)),\n  closes [#92](https://github.com/ory/kratos/issues/92)\n- Explicitly whitelist form parser keys\n  ([#105](https://github.com/ory/kratos/issues/105))\n  ([28b056e](https://github.com/ory/kratos/commit/28b056e5bbfec645262914c52f0386d70c787a32)),\n  closes [#98](https://github.com/ory/kratos/issues/98):\n\n  Previously the form parser would try to detect the field type by asserting\n  types for the whole form. That caused passwords containing only numbers to\n  fail to unmarshal into a string value.\n\n  This patch resolves that issue by introducing a prefix option to the\n  BodyParser\n\n- Fix broken import\n  ([308aa13](https://github.com/ory/kratos/commit/308aa1334dd43bc4bebade4e70e9c81c83fe8806))\n- Handle securecookie errors appropriately\n  ([#101](https://github.com/ory/kratos/issues/101))\n  ([75bf6fe](https://github.com/ory/kratos/commit/75bf6fe3f79d025f2aaa79d06db39c26430dc3fc)),\n  closes [#97](https://github.com/ory/kratos/issues/97):\n\n  Previously, IsNotAuthenticated would not handle securecookie errors\n  appropriately. This has been resolved.\n\n- Implement CRUD for identities ([#60](https://github.com/ory/kratos/issues/60))\n  ([58a3c24](https://github.com/ory/kratos/commit/58a3c240fca66e1195bf310024a2f8473826bce6)),\n  closes [#58](https://github.com/ory/kratos/issues/58)\n- Implement message templates and SMTP delivery\n  ([#146](https://github.com/ory/kratos/issues/146))\n  ([dc674bf](https://github.com/ory/kratos/commit/dc674bfa7d1fa9ee94b014d09866bbdc0a97c321)),\n  closes [#99](https://github.com/ory/kratos/issues/99):\n\n  This patch adds a message templates (with override capabilities) and SMTP\n  delivery.\n\n  Integration tests using MailHog test fault resilience and e2e email delivery.\n\n  This system is designed to be extended for SMS and other use cases.\n\n- Improve migration command ([#94](https://github.com/ory/kratos/issues/94))\n  ([2b631de](https://github.com/ory/kratos/commit/2b631de6d621dcebac5318f6dd628646fec7712f))\n- Inject Identity Traits JSON Schema\n  ([3a4c5ad](https://github.com/ory/kratos/commit/3a4c5ad35f885c7d38ffcf1d5836fb485f122fe9)),\n  closes [#189](https://github.com/ory/kratos/issues/189)\n- Mark active field as nullable ([#89](https://github.com/ory/kratos/issues/89))\n  ([292702d](https://github.com/ory/kratos/commit/292702d9e031e43c63e0ecb59354557139499e87))\n- Move package to selfservice\n  ([063b767](https://github.com/ory/kratos/commit/063b7679af76333fc546e94e92b197079e5bdb30)):\n\n  Because this module is primarily used in selfservice scenarios, it has been\n  moved to the selfservice parent.\n\n- Omit request header from login/registration request\n  ([#106](https://github.com/ory/kratos/issues/106))\n  ([9b07587](https://github.com/ory/kratos/commit/9b07587f2de2b270c5c326e37b2b6b3dbbfa8595)),\n  closes [#95](https://github.com/ory/kratos/issues/95):\n\n  When fetching a login and registration request, the HTTP Request Headers must\n  not be included in the response, as they contain irrelevant information for\n  the API caller.\n\n- Properly handle empty credentials config in sql\n  ([#93](https://github.com/ory/kratos/issues/93))\n  ([b79c5d1](https://github.com/ory/kratos/commit/b79c5d1d5216e994f986ce739285cb1a89523df5))\n- Re-introduce migration plans to CLI command\n  ([#192](https://github.com/ory/kratos/issues/192))\n  ([bb32cd3](https://github.com/ory/kratos/commit/bb32cd3cad3cd0bd6f3166de0166701e1f676ac6)),\n  closes [#131](https://github.com/ory/kratos/issues/131)\n- Reset CSRF token on principal change\n  ([#64](https://github.com/ory/kratos/issues/64))\n  ([9c889ab](https://github.com/ory/kratos/commit/9c889ab4f6c846812a4290545fef7d8106da35f0)),\n  closes [#38](https://github.com/ory/kratos/issues/38):\n\n  Add tests for logout.\n\n- Resolve wrong column reference in sql\n  ([#90](https://github.com/ory/kratos/issues/90))\n  ([0c0eb87](https://github.com/ory/kratos/commit/0c0eb87cd341bd3e73eb9adb303054b38c103ba9)):\n\n  Reference ic.method instead of ici.method.\n\n  Added regression tests against this particular issue.\n\n- Update keyword from kratos to ory.sh/kratos\n  ([f45cbe0](https://github.com/ory/kratos/commit/f45cbe0339db8d129522314f3099e6944e4a6ea3)),\n  closes [#115](https://github.com/ory/kratos/issues/115)\n- Update sdk generation method\n  ([24aa3d7](https://github.com/ory/kratos/commit/24aa3d73354d5a28f05999a09e7bbbe51a44d44e))\n- Update to ory/x 0.0.80 ([#110](https://github.com/ory/kratos/issues/110))\n  ([64de2f8](https://github.com/ory/kratos/commit/64de2f86540bf8715a1703d773fa95011603a854)):\n\n  Removes the need for BindEnv()\n\n- Use JSON Schema to type assert form body\n  ([#116](https://github.com/ory/kratos/issues/116))\n  ([1944c7c](https://github.com/ory/kratos/commit/1944c7c6e82b5b6a3b9d47db94c8f8f45248feb7)),\n  closes [#109](https://github.com/ory/kratos/issues/109)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "<!-- AUTO-GENERATED, DO NOT EDIT! -->\n<!-- Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/CODE_OF_CONDUCT.md -->\n\n# 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, caste, color, religion, or sexual\nidentity and 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 overall\n  community\n\nExamples of unacceptable behavior include:\n\n- The use of sexualized language or imagery, and sexual attention or advances of\n  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 address,\n  without their explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Open Source Community Support\n\nOry Open source software is collaborative and based on contributions by\ndevelopers in the Ory community. There is no obligation from Ory to help with\nindividual problems. If Ory open source software is used in production in a\nfor-profit company or enterprise environment, we mandate a paid support contract\nwhere Ory is obligated under their service level agreements (SLAs) to offer a\ndefined level of availability and responsibility. For more information about\npaid support please contact us at sales@ory.com.\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\n[office@ory.com](mailto:office@ory.com). All complaints will be reviewed and\ninvestigated 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 of\nactions.\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 permanent\nban.\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 the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at\n[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder][mozilla coc].\n\nFor answers to common questions about this code of conduct, see the FAQ at\n[https://www.contributor-covenant.org/faq][faq]. Translations are available at\n[https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[mozilla coc]: https://github.com/mozilla/diversity\n[faq]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "<!-- AUTO-GENERATED, DO NOT EDIT! -->\n<!-- Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/CONTRIBUTING.md -->\n\n# Contribute to Ory Kratos<!-- omit in toc -->\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Introduction](#introduction)\n- [FAQ](#faq)\n- [How can I contribute?](#how-can-i-contribute)\n- [Communication](#communication)\n- [Contribute examples or community projects](#contribute-examples-or-community-projects)\n- [Contribute code](#contribute-code)\n- [Contribute documentation](#contribute-documentation)\n- [Disclosing vulnerabilities](#disclosing-vulnerabilities)\n- [Code style](#code-style)\n  - [Working with forks](#working-with-forks)\n- [Conduct](#conduct)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Introduction\n\n_Please note_: We take Ory Kratos's security and our users' trust very\nseriously. If you believe you have found a security issue in Ory Kratos, please\ndisclose it by contacting us at security@ory.com.\n\nThere are many ways in which you can contribute. The goal of this document is to\nprovide a high-level overview of how you can get involved in Ory.\n\nAs a potential contributor, your changes and ideas are welcome at any hour of\nthe day or night, on weekdays, weekends, and holidays. Please do not ever\nhesitate to ask a question or send a pull request.\n\nIf you are unsure, just ask or submit the issue or pull request anyways. You\nwon't be yelled at for giving it your best effort. The worst that can happen is\nthat you'll be politely asked to change something. We appreciate any sort of\ncontributions and don't want a wall of rules to get in the way of that.\n\nThat said, if you want to ensure that a pull request is likely to be merged,\ntalk to us! You can find out our thoughts and ensure that your contribution\nwon't clash with Ory Kratos's direction. A great way to do this is via\n[Ory Kratos Discussions](https://github.com/ory/kratos/discussions) or the\n[Ory Chat](https://www.ory.com/chat).\n\n## FAQ\n\n- I am new to the community. Where can I find the\n  [Ory Community Code of Conduct?](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md)\n\n- I have a question. Where can I get\n  [answers to questions regarding Ory Kratos?](#communication)\n\n- I would like to contribute but I am not sure how. Are there\n  [easy ways to contribute?](#how-can-i-contribute)\n  [Or good first issues?](https://github.com/search?l=&o=desc&q=label%3A%22help+wanted%22+label%3A%22good+first+issue%22+is%3Aopen+user%3Aory+user%3Aory-corp&s=updated&type=Issues)\n\n- I want to talk to other Ory Kratos users.\n  [How can I become a part of the community?](#communication)\n\n- I would like to know what I am agreeing to when I contribute to Ory Kratos.\n  Does Ory have\n  [a Contributors License Agreement?](https://cla-assistant.io/ory/kratos)\n\n- I would like updates about new versions of Ory Kratos.\n  [How are new releases announced?](https://www.ory.com/l/sign-up-newsletter)\n\n## How can I contribute?\n\nIf you want to start to contribute code right away, take a look at the\n[list of good first issues](https://github.com/ory/kratos/labels/good%20first%20issue).\n\nThere are many other ways you can contribute. Here are a few things you can do\nto help out:\n\n- **Give us a star.** It may not seem like much, but it really makes a\n  difference. This is something that everyone can do to help out Ory Kratos.\n  Github stars help the project gain visibility and stand out.\n\n- **Join the community.** Sometimes helping people can be as easy as listening\n  to their problems and offering a different perspective. Join our Slack, have a\n  look at discussions in the forum and take part in community events. More info\n  on this in [Communication](#communication).\n\n- **Answer discussions.** At all times, there are several unanswered discussions\n  on GitHub. You can see an\n  [overview here](https://github.com/discussions?discussions_q=is%3Aunanswered+org%3Aory+sort%3Aupdated-desc).\n  If you think you know an answer or can provide some information that might\n  help, please share it! Bonus: You get GitHub achievements for answered\n  discussions.\n\n- **Help with open issues.** We have a lot of open issues for Ory Kratos and\n  some of them may lack necessary information, some are duplicates of older\n  issues. You can help out by guiding people through the process of filling out\n  the issue template, asking for clarifying information or pointing them to\n  existing issues that match their description of the problem.\n\n- **Review documentation changes.** Most documentation just needs a review for\n  proper spelling and grammar. If you think a document can be improved in any\n  way, feel free to hit the `edit` button at the top of the page. More info on\n  contributing to the documentation [here](#contribute-documentation).\n\n- **Help with tests.** Pull requests may lack proper tests or test plans. These\n  are needed for the change to be implemented safely.\n\n## Communication\n\nWe use [Slack](https://www.ory.com/chat). You are welcome to drop in and ask\nquestions, discuss bugs and feature requests, talk to other users of Ory, etc.\n\nCheck out [Ory Kratos Discussions](https://github.com/ory/kratos/discussions).\nThis is a great place for in-depth discussions and lots of code examples, logs\nand similar data.\n\nYou can also join our community calls if you want to speak to the Ory team\ndirectly or ask some questions. You can find more info and participate in\n[Slack](https://www.ory.com/chat) in the #community-call channel.\n\nIf you want to receive regular notifications about updates to Ory Kratos,\nconsider joining the mailing list. We will _only_ send you vital information on\nthe projects that you are interested in.\n\nAlso, [follow us on Twitter](https://twitter.com/orycorp).\n\n## Contribute examples or community projects\n\nOne of the most impactful ways to contribute is by adding code examples or other\nOry-related code. You can find an overview of community code in the\n[awesome-ory](https://github.com/ory/awesome-ory) repository.\n\n_If you would like to contribute a new example, we would love to hear from you!_\n\nPlease [open a pull request at awesome-ory](https://github.com/ory/awesome-ory/)\nto add your example or Ory-related project to the awesome-ory README.\n\n## Contribute code\n\n**All code contributions require prior discussion and agreement with maintainers\nbefore opening a pull request.**\n\nThis applies to bug fixes, new features, and refactoring. Before writing code:\n\n1. Open a [Discussion](https://github.com/ory/kratos/discussions/new/choose) or\n   [Issue](https://github.com/ory/kratos/issues/new/choose) describing the\n   problem and your proposed solution.\n2. Wait for maintainer feedback and explicit agreement on the implementation\n   approach.\n3. Only then begin writing code.\n\nPull requests without prior discussion may be closed without review. This policy\nexists to protect your time and maintainer time.\n\n\"Drive-by\" PRs, even well-intentioned ones, often conflict with the project\nroadmap, duplicate ongoing work, or introduce architectural inconsistencies. A\nquick conversation can help avoid these problems.\n\nAll contributions are made via pull requests. To make a pull request, you will\nneed a GitHub account; if you are unclear on this process, see GitHub's\ndocumentation on [forking](https://help.github.com/articles/fork-a-repo) and\n[pull requests](https://help.github.com/articles/using-pull-requests). Pull\nrequests should be targeted at the `master` branch. Before creating a pull\nrequest, go through this checklist:\n\n1. Create a feature branch off of `master` so that changes do not get mixed up.\n1. [Rebase](http://git-scm.com/book/en/Git-Branching-Rebasing) your local\n   changes against the `master` branch.\n1. Run the full project test suite with the `go test -tags sqlite ./...` (or\n   equivalent) command and confirm that it passes.\n1. Run `make format`\n1. Add a descriptive prefix to commits. This ensures a uniform commit history\n   and helps structure the changelog. Please refer to this\n   [Convential Commits configuration](https://github.com/ory/kratos/blob/master/.github/workflows/conventional_commits.yml)\n   for the list of accepted prefixes. You can read more about the Conventional\n   Commit specification\n   [at their site](https://www.conventionalcommits.org/en/v1.0.0/).\n\nIf a pull request is not ready to be reviewed yet\n[it should be marked as a \"Draft\"](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request).\n\nBefore your contributions can be reviewed you need to sign our\n[Contributor License Agreement](https://cla-assistant.io/ory/kratos).\n\nThis agreement defines the terms under which your code is contributed to Ory.\nMore specifically it declares that you have the right to, and actually do, grant\nus the rights to use your contribution. You can see the Apache 2.0 license under\nwhich our projects are published\n[here](https://github.com/ory/meta/blob/master/LICENSE).\n\nWhen pull requests fail the automated testing stages (for example unit or E2E\ntests), authors are expected to update their pull requests to address the\nfailures until the tests pass.\n\nPull requests eligible for review\n\n1. follow the repository's code formatting conventions;\n2. include tests that prove that the change works as intended and does not add\n   regressions;\n3. document the changes in the code and/or the project's documentation;\n4. pass the CI pipeline;\n5. have signed our\n   [Contributor License Agreement](https://cla-assistant.io/ory/kratos);\n6. include a proper git commit message following the\n   [Conventional Commit Specification](https://www.conventionalcommits.org/en/v1.0.0/).\n\nIf all of these items are checked, the pull request is ready to be reviewed and\nyou should change the status to \"Ready for review\" and\n[request review from a maintainer](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review).\n\nReviewers will approve the pull request once they are satisfied with the patch.\n\n### AI-assisted contributions\n\nAI can be a valuable aid for writing code, documentation, and tests. However, to\nmaintain the quality and reliability of Ory Kratos, please follow these\nguidelines:\n\n- When submitting a pull request or issue that involved AI assistance, mention\n  the tools you used and the extent of their involvement. This helps reviewers\n  understand the context of your contribution.\n\n- Pull requests created with AI assistance should address an existing, accepted\n  issue. If you have an idea for a new feature or improvement, please open a\n  discussion or issue first to ensure alignment with the project's direction\n  before investing time in implementation.\n\n- You must use AI responsibly when writing code. All contributions must be\n  tested and verified before submission. Unreviewed AI-generated code will not\n  be accepted, and repeated submissions of this nature may result in restricted\n  contribution privileges.\n\n- When using AI to help draft issues, discussions, or documentation, review and\n  edit the output before submitting. AI tends to be verbose. Trim unnecessary\n  content and ensure your submission is clear and focused.\n\nContributors must use AI responsibly. These guidelines exist to ensure that\nevery contribution meets the high standards our community expects, while still\nembracing the productivity benefits that AI tools can provide.\n\n## Contribute documentation\n\nPlease provide documentation when changing, removing, or adding features. All\nOry Documentation resides in the\n[Ory documentation repository](https://github.com/ory/docs/). For further\ninstructions please head over to the Ory Documentation\n[README.md](https://github.com/ory/docs/blob/master/README.md).\n\n## Disclosing vulnerabilities\n\nPlease disclose vulnerabilities exclusively to\n[security@ory.com](mailto:security@ory.com). Do not use GitHub issues.\n\n## Code style\n\nPlease run `make format` to format all source code following the Ory standard.\n\n### Working with forks\n\n```bash\n# First you clone the original repository\ngit clone git@github.com:ory/ory/kratos.git\n\n# Next you add a git remote that is your fork:\ngit remote add fork git@github.com:<YOUR-GITHUB-USERNAME-HERE>/ory/kratos.git\n\n# Next you fetch the latest changes from origin for master:\ngit fetch origin\ngit checkout master\ngit pull --rebase\n\n# Next you create a new feature branch off of master:\ngit checkout my-feature-branch\n\n# Now you do your work and commit your changes:\ngit add -A\ngit commit -a -m \"fix: this is the subject line\" -m \"This is the body line. Closes #123\"\n\n# And the last step is pushing this to your fork\ngit push -u fork my-feature-branch\n```\n\nNow go to the project's GitHub Pull Request page and click \"New pull request\"\n\n## Conduct\n\nWhether you are a regular contributor or a newcomer, we care about making this\ncommunity a safe place for you and we've got your back.\n\n[Ory Community Code of Conduct](https://github.com/ory/kratos/blob/master/CODE_OF_CONDUCT.md)\n\nWe welcome discussion about creating a welcoming, safe, and productive\nenvironment for the community. If you have any questions, feedback, or concerns\n[please let us know](https://www.ory.com/chat).\n"
  },
  {
    "path": "DEVELOP.md",
    "content": "# Development\n\nThis document explains how to develop Ory Kratos, run tests, and work with the tooling around it.\n\n## Upgrading and changelog\n\nCheck [releases tab](https://github.com/ory/kratos/releases) for updates and changelogs when using the open source license.\n\n## Command line documentation\n\nTo see available commands and flags, run:\n\n```bash\nkratos -h\n# or\nkratos help\n```\n\n## Contribution guidelines\n\nWe encourage all contributions. Before opening a pull request, read the\n[contribution guidelines](./CONTRIBUTING.md).\n\n## Prerequisites\n\nYou need Go 1.16+ and, for the test suites:\n\n* Docker and Docker Compose\n* `make`\n* Node.js and npm\n\nYou can develop Ory Kratos on Windows, but most guides assume a Unix shell such as `bash` or `zsh`.\n\n## Install from source\n\nTo install Kratos from source:\n\n```make\nmake install\n```\n\n## Formatting code\n\nFormat all code using:\n\n```make\nmake format\n```\n\nThe continuous integration pipeline checks code formatting.\n\n## Running tests\n\nThere are three types of tests:\n\n* Short tests that do not require a SQL database\n* Regular tests that require PostgreSQL, MySQL, and CockroachDB\n* End to end tests that use real databases and a test browser\n\n### Short tests\n\nShort tests run quickly and use SQLite.\n\nRun all short tests:\n\n```bash\ngo test -short -tags sqlite ./...\n```\n\nRun short tests in a specific module:\n\n```bash\ncd client\ngo test -short -tags sqlite .\n```\n\n### Regular tests\n\nRegular tests require a database setup.\n\nThe test suite can start databases using\n[ory/dockertest](https://github.com/ory/dockertest). In practice, it is usually\neasier and faster to use the Makefile targets.\n\nRun the full test suite:\n\n```make\nmake test\n```\n\n> Note: `make test` recreates the databases every time. This can be slow if you\n> are iterating frequently on a specific test.\n\nIf you want to reuse databases across test runs, initialize them once:\n\n```bash\nmake test-resetdb\nexport TEST_DATABASE_MYSQL='mysql://root:secret@(127.0.0.1:3444)/mysql?parseTime=true'\nexport TEST_DATABASE_POSTGRESQL='postgres://postgres:secret@127.0.0.1:3445/kratos?sslmode=disable'\nexport TEST_DATABASE_COCKROACHDB='cockroach://root@127.0.0.1:3446/defaultdb?sslmode=disable'\n```\n\nThen you can run Go tests directly as often as needed:\n\n```bash\ngo test -tags sqlite ./...\n\n# or in a module:\ncd client\ngo test -tags sqlite .\n```\n\n### Updating test fixtures\n\nSome tests use snapshot fixtures.\n\nUpdate snapshots for short tests:\n\n```bash\nmake test-update-snapshots\n```\n\nUpdate all snapshots:\n\n```bash\nUPDATE_SNAPSHOTS=true go test -p 4 -tags sqlite ./...\n```\n\nYou can run this from the repository root or from subdirectories.\n\n### End-to-end tests\n\nEnd to end tests are implemented with [Cypress](https://www.cypress.io).\n\n> On ARM based Macs you may need to install Rosetta 2 to run Cypress.\n> See the Cypress documentation:\n> [https://www.cypress.io/blog/2021/01/20/running-cypress-on-the-apple-m1-silicon-arm-architecture-using-rosetta-2/](https://www.cypress.io/blog/2021/01/20/running-cypress-on-the-apple-m1-silicon-arm-architecture-using-rosetta-2/)\n\nTo install Rosetta 2:\n\n```bash\nsoftwareupdate --install-rosetta --agree-to-license\n```\n\nRun e2e tests in development mode:\n\n```bash\n./test/e2e/run.sh --dev sqlite\n```\n\nRun all e2e tests with databases:\n\n```make\nmake test-e2e\n```\n\nFor more options:\n\n```bash\n./test/e2e/run.sh\n```\n\n#### Run a single test\n\nAdd `.only` to the test you want to run, for example:\n\n```ts\nit.only('invalid remote recovery email template', () => {\n  // ...\n})\n```\n\n#### Run a subset of tests\n\nTo run a subset of e2e tests:\n\n1. Edit `cypress.json` in `test/e2e/`.\n\n2. Add the `testFiles` option and point it to the specs you want, for example:\n\n   ```json\n   \"testFiles\": [\"profiles/network/*\"]\n   ```\n\n3. Start the tests again using the run script or Makefile.\n\n## Build Docker image\n\nTo build a development Docker image:\n\n```make\nmake docker\n```\n\n## Preview API documentation\n\nTo work on and preview the generated API documentation:\n\n1. Update the SDK including the OpenAPI specification:\n\n   ```make\n   make sdk\n   ```\n\n2. Run the preview server for API documentation:\n\n   ```make\n   make docs/api\n   ```\n\n3. Run the preview server for Swagger documentation:\n\n   ```make\n   make docs/swagger\n   ```\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "Makefile",
    "content": "SHELL=/usr/bin/env bash -o pipefail\n\n#  EXECUTABLES = docker-compose docker node npm go\n#  K := $(foreach exec,$(EXECUTABLES),\\\n#          $(if $(shell which $(exec)),some string,$(error \"No $(exec) in PATH\")))\n\nexport PATH               := .bin:${PATH}\nexport PWD                := $(shell pwd)\nexport BUILD_DATE         := $(shell date -u +\"%Y-%m-%dT%H:%M:%SZ\")\nexport VCS_REF            := $(shell git rev-parse HEAD)\nexport QUICKSTART_OPTIONS ?=\nexport IMAGE_TAG \t\t\t\t\t:= $(if $(IMAGE_TAG),$(IMAGE_TAG),latest)\n\n.bin/clidoc:\n\techo \"deprecated usage, use docs/cli instead\"\n\tgo build -o .bin/clidoc ./cmd/clidoc/.\n\n.PHONY: docs/cli\ndocs/cli:\n\tgo run ./cmd/clidoc/. .\n\n.PHONY: docs/api\ndocs/api:\n\tnpx @redocly/openapi-cli preview-docs spec/api.json\n\n.PHONY: docs/swagger\ndocs/swagger:\n\tnpx @redocly/openapi-cli preview-docs spec/swagger.json\n\n.bin/golangci-lint: Makefile\n\tcurl --retry 7 --retry-connrefused -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -d -b .bin v2.10.1\n\n.bin/hydra: Makefile\n\tbash <(curl --retry 7 --retry-connrefused https://raw.githubusercontent.com/ory/meta/master/install.sh) -d -b .bin hydra v2.2.0\n\n.bin/ory: Makefile\n\tcurl --retry 7 --retry-connrefused https://raw.githubusercontent.com/ory/meta/master/install.sh | bash -s -- -b .bin ory v0.2.2\n\ttouch -a -m .bin/ory\n\n.bin/buf: Makefile\n\tcurl -sSL \\\n\t\"https://github.com/bufbuild/buf/releases/download/v1.39.0/buf-$(shell uname -s)-$(shell uname -m).tar.gz\" | \\\n\ttar -xvzf - -C \".bin/\" --strip-components=2 buf/bin/buf buf/bin/protoc-gen-buf-breaking buf/bin/protoc-gen-buf-lint\n\ttouch -a -m .bin/buf\n\n.PHONY: lint\nlint: .bin/golangci-lint .bin/buf\n\t.bin/golangci-lint run -v --timeout 10m ./...\n\t.bin/buf lint\n\n.PHONY: mocks\nmocks:\n\tgo tool mockgen -mock_names Manager=MockLoginExecutorDependencies -package pkg -destination pkg/hook_login_executor_dependencies.go github.com/ory/kratos/selfservice loginExecutorDependencies\n\n.PHONY: proto\nproto: gen/oidc/v1/state.pb.go\n\ngen/oidc/v1/state.pb.go: proto/oidc/v1/state.proto buf.yaml buf.gen.yaml .bin/buf\n\t.bin/buf generate\n\tgo tool goimports -w gen/\n\n.PHONY: install\ninstall:\n\tgo install -tags sqlite .\n\n.PHONY: test-resetdb\ntest-resetdb:\n\tscript/testenv.sh\n\n.PHONY: test\ntest:\n\tdocker pull oryd/hydra:v2.2.0\n\tgo test -p 1 -tags sqlite -count=1 -failfast ./...\n\ntest-short:\n\tgo test -tags sqlite -count=1 -failfast -short ./...\n\n.PHONY: test-coverage\ntest-coverage:\n\tgo test -coverprofile=coverage.out -failfast -timeout=20m -tags sqlite ./...\n\n.PHONY: test-coverage-next\ntest-coverage-next:\n\tgo test -short -failfast -timeout=20m -tags sqlite -cover ./... --args test.gocoverdir=\"$$PWD/coverage\"\n\tgo tool covdata percent -i=coverage\n\tgo tool covdata textfmt -i=./coverage -o coverage.new.out\n\n# Generates the SDK\n.PHONY: sdk\nsdk: .bin/ory node_modules\n\tgo tool swagger generate spec -m -o spec/swagger.json \\\n\t\t-c github.com/ory/kratos \\\n\t\t-c github.com/ory/x/healthx \\\n\t\t-c github.com/ory/x/crdbx \\\n\t\t-c github.com/ory/x/openapix\n\tory dev swagger sanitize ./spec/swagger.json\n\tgo tool swagger validate ./spec/swagger.json\n\tCIRCLE_PROJECT_USERNAME=ory CIRCLE_PROJECT_REPONAME=kratos \\\n\t\tory dev openapi migrate \\\n\t\t\t--health-path-tags metadata \\\n\t\t\t-p https://raw.githubusercontent.com/ory/x/master/healthx/openapi/patch.yaml \\\n\t\t\t-p file://.schema/openapi/patches/meta.yaml \\\n\t\t\t-p file://.schema/openapi/patches/schema.yaml \\\n\t\t\t-p file://.schema/openapi/patches/selfservice.yaml \\\n\t\t\t-p file://.schema/openapi/patches/security.yaml \\\n\t\t\t-p file://.schema/openapi/patches/session.yaml \\\n\t\t\t-p file://.schema/openapi/patches/identity.yaml \\\n\t\t\t-p file://.schema/openapi/patches/courier.yaml \\\n\t\t\t-p file://.schema/openapi/patches/generic_error.yaml \\\n\t\t\t-p file://.schema/openapi/patches/nulls.yaml \\\n\t\t\t-p file://.schema/openapi/patches/common.yaml \\\n\t\t\tspec/swagger.json spec/api.json\n\n\trm -rf pkg/httpclient\n\tmkdir -p pkg/httpclient/\n\tnpm run openapi-generator-cli -- generate -i \"spec/api.json\" \\\n\t\t-g go \\\n\t\t-o \"pkg/httpclient\" \\\n\t\t--git-user-id ory \\\n\t\t--git-repo-id client-go \\\n\t\t--git-host github.com \\\n\t\t--api-name-suffix \"API\" \\\n\t\t-c .schema/openapi/gen.go.yml\n\n\t(cd pkg/httpclient; rm -rf go.mod go.sum test api docs)\n\n\trm -rf pkg/client-go\n\tmkdir -p pkg/client-go/\n\tnpm run openapi-generator-cli -- generate -i \"spec/api.json\" \\\n\t\t-g go \\\n\t\t-o \"pkg/client-go\" \\\n\t\t--git-user-id ory \\\n\t\t--git-repo-id client-go \\\n\t\t--git-host github.com \\\n\t\t--api-name-suffix \"API\" \\\n\t\t-c .schema/openapi/gen.go.yml\n\n\t(cd pkg/client-go; go mod edit -module github.com/ory/client-go go.mod; rm -rf test api docs; go mod tidy)\n\n\tmake format\n\n.PHONY: quickstart\nquickstart:\n\tdocker pull oryd/kratos:latest\n\tdocker pull oryd/kratos-selfservice-ui-node:latest\n\tdocker-compose -f quickstart.yml -f quickstart-standalone.yml up --build --force-recreate\n\n.PHONY: quickstart-dev\nquickstart-dev:\n\tdocker build -f .docker/Dockerfile-build -t oryd/kratos:latest .\n\tdocker-compose -f quickstart.yml -f quickstart-standalone.yml -f quickstart-latest.yml $(QUICKSTART_OPTIONS) up --build --force-recreate\n\nauthors:  # updates the AUTHORS file\n\tcurl --retry 7 --retry-connrefused https://raw.githubusercontent.com/ory/ci/master/authors/authors.sh | env PRODUCT=\"Ory Kratos\" bash\n\n# Formats the code\n.PHONY: format\nformat: .bin/ory node_modules .bin/buf\n\t.bin/ory dev headers copyright --exclude=gen --exclude=pkg/httpclient --exclude=pkg/client-go --exclude test/e2e/proxy/node_modules --exclude test/e2e/node_modules --exclude node_modules --exclude=oryx\n\tgo tool goimports -w -local github.com/ory .\n\tnpm exec -- prettier --write 'test/e2e/**/*{.ts,.js}'\n\tnpm exec -- prettier --write '.github'\n\t.bin/buf format --write\n\n# Build local docker image\n.PHONY: docker\ndocker:\n\tDOCKER_BUILDKIT=1 DOCKER_CONTENT_TRUST=1 docker build -f .docker/Dockerfile-build --build-arg=COMMIT=$(VCS_REF) --build-arg=BUILD_DATE=$(BUILD_DATE) -t oryd/kratos:${IMAGE_TAG} .\n\n.PHONY: test-e2e\ntest-e2e: node_modules test-resetdb kratos-config-e2e\n\tsource script/test-envs.sh\n\ttest/e2e/run.sh sqlite\n\ttest/e2e/run.sh postgres\n\ttest/e2e/run.sh cockroach\n\ttest/e2e/run.sh mysql\n\n.PHONY: test-e2e-playwright\ntest-e2e-playwright: node_modules test-resetdb kratos-config-e2e\n\tsource script/test-envs.sh\n\ttest/e2e/run.sh --only-setup\n\t(cd test/e2e; DB=memory npm run playwright)\n\n.PHONY: test-refresh\ntest-refresh:\n\tUPDATE_SNAPSHOTS=true go test -tags sqlite,json1,refresh -short ./...\n\n.PHONY: pre-release\npre-release:\n\tgo tool yq '.services.kratos.image = \"oryd/kratos:'$$DOCKER_TAG'\"' -i quickstart.yml\n\tgo tool yq '.services.kratos-migrate.image = \"oryd/kratos:'$$DOCKER_TAG'\"' -i quickstart.yml\n\tgo tool yq '.services.kratos-selfservice-ui-node.image = \"oryd/kratos-selfservice-ui-node:'$$DOCKER_TAG'\"' -i quickstart.yml\n\n.PHONY: post-release\npost-release:\n\techo \"nothing to do\"\n\nlicenses: .bin/licenses node_modules  # checks open-source licenses\n\t.bin/licenses\n\n.bin/licenses: Makefile\n\tcurl --retry 7 --retry-connrefused https://raw.githubusercontent.com/ory/ci/master/licenses/install | sh\n\nnode_modules: package-lock.json\n\tnpm ci\n\ttouch node_modules\n\n.PHONY: kratos-config-e2e\nkratos-config-e2e:\n\tsh ./test/e2e/render-kratos-config.sh\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/ory/meta/master/static/banners/kratos.svg\" alt=\"Ory Kratos - Cloud native identity and user management\">\n</h1>\n\n<h4 align=\"center\">\n  <a href=\"https://www.ory.com/chat\">Chat</a> ·\n  <a href=\"https://github.com/ory/kratos/discussions\">Discussions</a> ·\n  <a href=\"https://www.ory.com/l/sign-up-newsletter\">Newsletter</a> ·\n  <a href=\"https://www.ory.com/docs/\">Docs</a> ·\n  <a href=\"https://console.ory.sh/\">Try Ory Network</a> ·\n  <a href=\"https://www.ory.com/jobs/\">Jobs</a>\n</h4>\n\nOry Kratos is an API first identity and user management system for cloud native\napplications. It centralizes login, registration, recovery, verification, and\nprofile management flows so your services consume them instead of reimplementing\nthem.\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n**Table of contents**\n\n- [What is Ory Kratos?](#what-is-ory-kratos)\n  - [Why Ory Kratos](#why-ory-kratos)\n- [Migrating from Auth0, Okta, and similar providers](#migrating-from-auth0-okta-and-similar-providers)\n- [Deployment options](#deployment-options)\n  - [Use Ory Kratos on the Ory Network](#use-ory-kratos-on-the-ory-network)\n  - [Self-host Ory Kratos](#self-host-ory-kratos)\n- [Quickstart](#quickstart)\n  - [Who is using it?](#who-is-using-it)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## What is Ory Kratos?\n\nOry Kratos is an API first identity and user management system that follows\n[cloud architecture best practices](https://www.ory.com/docs/ecosystem/software-architecture-philosophy).\nIt focuses on core identity workflows that almost every application needs:\n\n- Self service login and registration\n- Account verification and recovery\n- Multi factor authentication\n- Profile and account management\n- Identity schemas and traits\n- Admin APIs for lifecycle management\n\nWe recommend starting with the\n[Ory Kratos introduction docs](https://www.ory.com/kratos/docs/) to learn more\nabout its architecture, feature set, and how it compares to other systems.\n\n### Why Ory Kratos\n\nOry Kratos is designed to:\n\n- Remove identity logic from your application code and expose it over HTTP APIs\n- Work well with any UI framework through browser based and native app flows\n- Scale to large numbers of identities and devices\n- Integrate with the rest of the Ory stack for OAuth2, OpenID Connect, and\n  access control\n- Fit into modern cloud native environments such as Kubernetes and managed\n  platforms\n\n## Migrating from Auth0, Okta, and similar providers\n\nIf you are migrating from Auth0, Okta, or another identity provider that uses\nOAuth2 / OpenID Connect based login, consider using **Ory Hydra + Ory Kratos**\ntogether:\n\n- **Ory Hydra** acts as the OAuth2 and OpenID Connect provider and can replace\n  most authorization server and token issuing capabilities of your existing IdP.\n- **Ory Kratos** provides identity, credentials, and user-facing flows (login,\n  registration, recovery, verification, profile management).\n\nThis combination is often a drop-in replacement for OAuth2 and OpenID Connect\ncapabilities at the protocol level. In practice, you update client configuration\nand endpoints to point to Hydra, migrate identities into Kratos, and keep your\napplications speaking the same OAuth2 / OIDC protocols they already use.\n\n## Deployment options\n\nYou can run Ory Kratos in two main ways:\n\n- As a managed service on the Ory Network\n- As a self hosted service under your own control, with or without the Ory\n  Enterprise License\n\n### Use Ory Kratos on the Ory Network\n\nThe [Ory Network](https://www.ory.com/cloud) is the fastest way to use Ory\nservices in production. **Ory Identities** is powered by the open source Ory\nKratos server and is API compatible.\n\nThe Ory Network provides:\n\n- Identity and credential management that scales to billions of users and\n  devices\n- Registration, login, and account management flows for passkeys, biometrics,\n  social login, SSO, and multi factor authentication\n- Prebuilt login, registration, and account management pages and components\n- OAuth2 and OpenID Connect for single sign on, API access, and machine to\n  machine authorization\n- Low latency permission checks based on the Zanzibar model with the Ory\n  Permission Language\n- GDPR friendly storage with data locality and compliance in mind\n- Web based Ory Console and Ory CLI for administration and operations\n- Cloud native APIs compatible with the open source servers\n- Fair, usage based [pricing](https://www.ory.com/pricing)\n\nSign up for a\n[free developer account](https://console.ory.sh/registration?utm_source=github&utm_medium=banner&utm_campaign=kratos-readme)\nto get started.\n\n### Self-host Ory Kratos\n\nYou can run Ory Kratos yourself for full control over infrastructure,\ndeployment, and customization.\n\nThe [install guide](https://www.ory.com/kratos/docs/install) explains how to:\n\n- Install Kratos on Linux, macOS, Windows, and Docker\n- Configure databases such as PostgreSQL, MySQL, and CockroachDB\n- Deploy to Kubernetes and other orchestration systems\n- Build Kratos from source\n\nThis guide uses the open source distribution to get you started without license\nrequirements. It is a great fit for individuals, researchers, hackers, and\ncompanies that want to experiment, prototype, or run unimportant workloads\nwithout SLAs. You get the full core engine, and you are free to inspect, extend,\nand build it from source.\n\nIf you run Kratos as part of a business-critical system, for example login and\naccount recovery for all your users, you should use a commercial agreement to\nreduce operational and security risk. The **Ory Enterprise License (OEL)**\nlayers on top of self-hosted Kratos and provides:\n\n- Additional enterprise features that are not available in the open source\n  version such as SCIM, SAML, organization login (\"SSO\"), CAPTCHAs and more\n- Regular security releases, including CVE patches, with service level\n  agreements\n- Support for advanced scaling, multi-tenancy, and complex deployments\n- Premium support options with SLAs, direct access to engineers, and onboarding\n  help\n- Access to a private Docker registry with frequent and vetted, up-to-date\n  enterprise builds\n\nFor guaranteed CVE fixes, current enterprise builds, advanced features, and\nsupport in production, you need a valid\n[Ory Enterprise License](https://www.ory.com/ory-enterprise-license) and access\nto the Ory Enterprise Docker registry. To learn more,\n[contact the Ory team](https://www.ory.com/contact/).\n\n## Quickstart\n\nInstall the [Ory CLI](https://www.ory.com/docs/guides/cli/installation) and\ncreate a new project to try Ory Identities.\n\n```bash\n# Install the Ory CLI if you do not have it yet:\nbash <(curl https://raw.githubusercontent.com/ory/meta/master/install.sh) -b . ory\nsudo mv ./ory /usr/local/bin/\n\n# Sign in or sign up\nory auth\n\n# Create a new project\nory create project --create-workspace \"Ory Open Source\" --name \"GitHub Quickstart\"  --use-project\nory open ax login\n```\n\n### Who is using it?\n\n<!--BEGIN ADOPTERS-->\n\nThe Ory community stands on the shoulders of individuals, companies, and\nmaintainers. The Ory team thanks everyone involved - from submitting bug reports\nand feature requests, to contributing patches and documentation. The Ory\ncommunity counts more than 50.000 members and is growing. The Ory stack protects\n7.000.000.000+ API requests every day across thousands of companies. None of\nthis would have been possible without each and everyone of you!\n\nThe following list represents companies that have accompanied us along the way\nand that have made outstanding contributions to our ecosystem. _If you think\nthat your company deserves a spot here, reach out to\n<a href=\"mailto:office@ory.com\">office@ory.com</a> now_!\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Logo</th>\n            <th>Website</th>\n            <th>Case Study</th>\n        </tr>\n    </thead>\n    <tbody>\n        <tr>\n            <td>OpenAI</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/openai.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/openai.svg\" alt=\"OpenAI\">\n                </picture>\n            </td>\n            <td><a href=\"https://openai.com/\">openai.com</a></td>\n            <td><a href=\"https://www.ory.com/case-studies/openai\">OpenAI Case Study</a></td>\n        </tr>\n        <tr>\n            <td>Fandom</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/fandom.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/fandom.svg\" alt=\"Fandom\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.fandom.com/\">fandom.com</a></td>\n            <td><a href=\"https://www.ory.com/case-studies/fandom\">Fandom Case Study</a></td>\n        </tr>\n        <tr>\n            <td>Lumin</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/lumin.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/lumin.svg\" alt=\"Lumin\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.luminpdf.com/\">luminpdf.com</a></td>\n            <td><a href=\"https://www.ory.com/case-studies/lumin\">Lumin Case Study</a></td>\n        </tr>\n        <tr>\n            <td>Sencrop</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/sencrop.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/sencrop.svg\" alt=\"Sencrop\">\n                </picture>\n            </td>\n            <td><a href=\"https://sencrop.com/\">sencrop.com</a></td>\n            <td><a href=\"https://www.ory.com/case-studies/sencrop\">Sencrop Case Study</a></td>\n        </tr>\n        <tr>\n            <td>OSINT Industries</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/osint.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/osint.svg\" alt=\"OSINT Industries\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.osint.industries/\">osint.industries</a></td>\n            <td><a href=\"https://www.ory.com/case-studies/osint\">OSINT Industries Case Study</a></td>\n        </tr>\n        <tr>\n            <td>HGV</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/hgv.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/hgv.svg\" alt=\"HGV\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.hgv.it/\">hgv.it</a></td>\n            <td><a href=\"https://www.ory.com/case-studies/hgv\">HGV Case Study</a></td>\n        </tr>\n        <tr>\n            <td>Maxroll</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/maxroll.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/maxroll.svg\" alt=\"Maxroll\">\n                </picture>\n            </td>\n            <td><a href=\"https://maxroll.gg/\">maxroll.gg</a></td>\n            <td><a href=\"https://www.ory.com/case-studies/maxroll\">Maxroll Case Study</a></td>\n        </tr>\n        <tr>\n            <td>Zezam</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/zezam.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/zezam.svg\" alt=\"Zezam\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.zezam.io/\">zezam.io</a></td>\n            <td><a href=\"https://www.ory.com/case-studies/zezam\">Zezam Case Study</a></td>\n        </tr>\n        <tr>\n            <td>T.RowePrice</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/troweprice.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/troweprice.svg\" alt=\"T.RowePrice\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.troweprice.com/\">troweprice.com</a></td>\n        </tr>\n        <tr>\n            <td>Mistral</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/mistral.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/mistral.svg\" alt=\"Mistral\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.mistral.ai/\">mistral.ai</a></td>\n        </tr>\n        <tr>\n            <td>Axel Springer</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/axelspringer.svg\" />\n                    <img height=\"22px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/axelspringer.svg\" alt=\"Axel Springer\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.axelspringer.com/\">axelspringer.com</a></td>\n        </tr>\n        <tr>\n            <td>Hemnet</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/hemnet.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/hemnet.svg\" alt=\"Hemnet\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.hemnet.se/\">hemnet.se</a></td>\n        </tr>\n        <tr>\n            <td>Cisco</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/cisco.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/cisco.svg\" alt=\"Cisco\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.cisco.com/\">cisco.com</a></td>\n        </tr>\n        <tr>\n            <td>Presidencia de la República Dominicana</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/republica-dominicana.svg\" />\n                    <img height=\"42px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/republica-dominicana.svg\" alt=\"Presidencia de la República Dominicana\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.presidencia.gob.do/\">presidencia.gob.do</a></td>\n        </tr>\n        <tr>\n            <td>Moonpig</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/moonpig.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/moonpig.svg\" alt=\"Moonpig\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.moonpig.com/\">moonpig.com</a></td>\n        </tr>\n        <tr>\n            <td>Booster</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/booster.svg\" />\n                    <img height=\"18px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/booster.svg\" alt=\"Booster\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.choosebooster.com/\">choosebooster.com</a></td>\n        </tr>\n        <tr>\n            <td>Zaptec</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/zaptec.svg\" />\n                    <img height=\"24px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/zaptec.svg\" alt=\"Zaptec\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.zaptec.com/\">zaptec.com</a></td>\n        </tr>\n        <tr>\n            <td>Klarna</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/klarna.svg\" />\n                    <img height=\"24px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/klarna.svg\" alt=\"Klarna\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.klarna.com/\">klarna.com</a></td>\n        </tr>\n        <tr>\n            <td>Raspberry PI Foundation</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/raspi.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/raspi.svg\" alt=\"Raspberry PI Foundation\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.raspberrypi.org/\">raspberrypi.org</a></td>\n        </tr>\n        <tr>\n            <td>Tulip</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/tulip.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/tulip.svg\" alt=\"Tulip Retail\">\n                </picture>\n            </td>\n            <td><a href=\"https://tulip.com/\">tulip.com</a></td>\n        </tr>\n        <tr>\n            <td>Hootsuite</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/hootsuite.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/hootsuite.svg\" alt=\"Hootsuite\">\n                </picture>\n            </td>\n            <td><a href=\"https://hootsuite.com/\">hootsuite.com</a></td>\n        </tr>\n        <tr>\n            <td>Segment</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/segment.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/segment.svg\" alt=\"Segment\">\n                </picture>\n            </td>\n            <td><a href=\"https://segment.com/\">segment.com</a></td>\n        </tr>\n        <tr>\n            <td>Arduino</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/arduino.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/arduino.svg\" alt=\"Arduino\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.arduino.cc/\">arduino.cc</a></td>\n        </tr>\n        <tr>\n            <td>Sainsbury's</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/sainsburys.svg\" />\n                    <img height=\"24px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/sainsburys.svg\" alt=\"Sainsbury's\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.sainsburys.co.uk/\">sainsburys.co.uk</a></td>\n        </tr>\n        <tr>\n            <td>Contraste</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/contraste.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/contraste.svg\" alt=\"Contraste\">\n                </picture>\n            </td>\n            <td><a href=\"https://www.contraste.com/en\">contraste.com</a></td>\n        </tr>\n        <tr>\n            <td>inMusic</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/inmusic.svg\" />\n                    <img height=\"24px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/inmusic.svg\" alt=\"InMusic\">\n                </picture>\n            </td>\n            <td><a href=\"https://inmusicbrands.com/\">inmusicbrands.com</a></td>\n        </tr>\n        <tr>\n            <td>Buhta</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/buhta.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/buhta.svg\" alt=\"Buhta\">\n                </picture>\n            </td>\n            <td><a href=\"https://buhta.com/\">buhta.com</a></td>\n        </tr>\n        </tr>\n            <tr>\n            <td>Amplitude</td>\n            <td align=\"center\">\n                <picture>\n                    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/amplitude.svg\" />\n                    <img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/amplitude.svg\" alt=\"amplitude.com\">\n                </picture>\n            </td>\n            <td><a href=\"https://amplitude.com/\">amplitude.com</a></td>\n        </tr>\n    <tr>\n      <td align=\"center\"><a href=\"https://tier4.jp/en/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/tieriv.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/tieriv.svg\" alt=\"TIER IV\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://kyma-project.io\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/kyma.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/kyma.svg\" alt=\"Kyma Project\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://serlo.org/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/serlo.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/serlo.svg\" alt=\"Serlo\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://padis.io/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/padis.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/padis.svg\" alt=\"Padis\"></picture></a></td>\n    </tr>\n    <tr>\n      <td align=\"center\"><a href=\"https://cloudbear.eu/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/cloudbear.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/cloudbear.svg\" alt=\"Cloudbear\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://securityonionsolutions.com/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/securityonion.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/securityonion.svg\" alt=\"Security Onion Solutions\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://factlylabs.com/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/factly.svg\" /><img height=\"24px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/factly.svg\" alt=\"Factly\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://cashdeck.com.au/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/allmyfunds.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/allmyfunds.svg\" alt=\"All My Funds\"></picture></a></td>\n    </tr>\n    <tr>\n      <td align=\"center\"><a href=\"https://nortal.com/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/nortal.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/nortal.svg\" alt=\"Nortal\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://www.ordermygear.com/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/ordermygear.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/ordermygear.svg\" alt=\"OrderMyGear\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://r2devops.io/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/r2devops.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/r2devops.svg\" alt=\"R2Devops\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://www.paralus.io/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/paralus.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/paralus.svg\" alt=\"Paralus\"></picture></a></td>\n    </tr>\n    <tr>\n      <td align=\"center\"><a href=\"https://dyrector.io/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/dyrector_io.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/dyrector_io.svg\" alt=\"dyrector.io\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://pinniped.dev/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/pinniped.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/pinniped.svg\" alt=\"pinniped.dev\"></picture></a></td>\n      <td align=\"center\"><a href=\"https://pvotal.tech/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/light/pvotal.svg\" /><img height=\"32px\" src=\"https://raw.githubusercontent.com/ory/meta/master/static/adopters/dark/pvotal.svg\" alt=\"pvotal.tech\"></picture></a></td>\n      <td></td>\n    </tr>\n    </tbody>\n</table>\n\nMany thanks to all individual contributors\n\n<a href=\"https://opencollective.com/ory\" target=\"_blank\"><img src=\"https://opencollective.com/ory/contributors.svg?width=890&limit=714&button=false\" /></a>\n\n<!--END ADOPTERS-->\n"
  },
  {
    "path": "SECURITY.md",
    "content": "<!-- AUTO-GENERATED, DO NOT EDIT! -->\n<!-- Please edit the original at https://github.com/ory/meta/blob/master/templates/repository/common/SECURITY.md -->\n\n# Ory Security Policy\n\nThis policy outlines Ory's security commitments and practices for users across\ndifferent licensing and deployment models.\n\nTo learn more about Ory's security service level agreements (SLAs) and\nprocesses, please [contact us](https://www.ory.com/contact/).\n\n## Ory Network Users\n\n- **Security SLA:** Ory addresses vulnerabilities in the Ory Network according\n  to the following guidelines:\n  - Critical: Typically addressed within 14 days.\n  - High: Typically addressed within 30 days.\n  - Medium: Typically addressed within 90 days.\n  - Low: Typically addressed within 180 days.\n  - Informational: Addressed as necessary.  \n    These timelines are targets and may vary based on specific circumstances.\n- **Release Schedule:** Updates are deployed to the Ory Network as\n  vulnerabilities are resolved.\n- **Version Support:** The Ory Network always runs the latest version, ensuring\n  up-to-date security fixes.\n\n## Ory Enterprise License Customers\n\n- **Security SLA:** Ory addresses vulnerabilities based on their severity:\n  - Critical: Typically addressed within 14 days.\n  - High: Typically addressed within 30 days.\n  - Medium: Typically addressed within 90 days.\n  - Low: Typically addressed within 180 days.\n  - Informational: Addressed as necessary.  \n    These timelines are targets and may vary based on specific circumstances.\n- **Release Schedule:** Updates are made available as vulnerabilities are\n  resolved. Ory works closely with enterprise customers to ensure timely updates\n  that align with their operational needs.\n- **Version Support:** Ory may provide security support for multiple versions,\n  depending on the terms of the enterprise agreement.\n\n## Apache 2.0 License Users\n\n- **Security SLA:** Ory does not provide a formal SLA for security issues under\n  the Apache 2.0 License.\n- **Release Schedule:** Releases prioritize new functionality and include fixes\n  for known security vulnerabilities at the time of release. While major\n  releases typically occur one to two times per year, Ory does not guarantee a\n  fixed release schedule.\n- **Version Support:** Security patches are only provided for the latest release\n  version.\n\n## Reporting a Vulnerability\n\nFor details on how to report security vulnerabilities, visit our\n[security policy documentation](https://www.ory.com/docs/ecosystem/security).\n"
  },
  {
    "path": "anonymous_sessions.md",
    "content": "\n# Feasibility Report: Anonymous Sessions in Ory Kratos\n\n## Executive summary\n\nAnonymous sessions (sessions not tied to an authenticated identity) are **not natively supported** in Kratos today. The session model is deeply coupled to the identity model at the database, struct, and API level. Implementing anonymous sessions is feasible but requires changes across multiple layers. This report outlines the current architecture constraints, proposes API designs, and evaluates implementation approaches.\n\n---\n\n## 1. Current architecture analysis\n\n### 1.1 Session–Identity coupling\n\nThe `Session` struct has a **non-nullable** `IdentityID uuid.UUID` field and a hard foreign key constraint at the database level:\n\n```cloud/kratos/kratos-oss/persistence/sql/migrations/sql/20191100000003000000_sessions.postgres.up.sql#L1-10\nCREATE TABLE \"sessions\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"authenticated_at\" timestamp NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);\n```\n\nThe persistence layer explicitly rejects sessions without an identity:\n\n```cloud/kratos/kratos-oss/persistence/sql/persister_session.go#L244-248\n\ts.NID = p.NetworkID(ctx)\n\tif s.Identity != nil {\n\t\ts.IdentityID = s.Identity.ID\n\t} else if s.IdentityID.IsNil() {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"cannot upsert session without an identity or identity ID set\"))\n```\n\n### 1.2 Session activation requires an identity\n\n`ManagerHTTP.ActivateSession` hard-requires a non-nil, active identity:\n\n```cloud/kratos/kratos-oss/session/manager_http.go#L314-324\nfunc (s *ManagerHTTP) ActivateSession(r *http.Request, session *Session, i *identity.Identity, authenticatedAt time.Time) (err error) {\n\t// ...\n\tif i == nil {\n\t\treturn errors.WithStack(x.PseudoPanic.WithReasonf(\"Identity must not be nil when activating a session.\"))\n\t}\n\n\tif !i.IsActive() {\n\t\treturn errors.WithStack(ErrIdentityDisabled.WithDetail(\"identity_id\", i.ID))\n\t}\n```\n\n### 1.3 The `/sessions/whoami` endpoint always returns identity data\n\nThe `whoami` handler unconditionally reads identity data and sets the `X-Kratos-Authenticated-Identity-Id` header:\n\n```cloud/kratos/kratos-oss/session/handler.go#L260-261\n\t// Set userId as the X-Kratos-Authenticated-Identity-Id header.\n\tw.Header().Set(\"X-Kratos-Authenticated-Identity-Id\", s.Identity.ID.String())\n```\n\n### 1.4 JWT tokenization uses identity as subject\n\nThe tokenizer requires the session's identity for the `sub` claim:\n\n```cloud/kratos/kratos-oss/session/tokenizer.go#L71-82\nfunc SetSubjectClaim(claims jwt.MapClaims, session *Session, subjectSource string) error {\n\tswitch subjectSource {\n\tcase \"\", \"id\":\n\t\tclaims[\"sub\"] = session.IdentityID.String()\n\tcase \"external_id\":\n\t\tif session.Identity.ExternalID == \"\" {\n\t\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"The session's identity does not have an external ID set, but it is required for the subject claim.\"))\n\t\t}\n\t\tclaims[\"sub\"] = session.Identity.ExternalID.String()\n```\n\n### 1.5 Hooks assume identity existence\n\nMultiple hooks (e.g., `SessionDestroyer`, `AddressVerifier`, `SessionIssuer`) dereference `s.Identity.ID` without nil checks:\n\n```cloud/kratos/kratos-oss/selfservice/hook/session_destroyer.go#L37-44\nfunc (e *SessionDestroyer) ExecuteLoginPostHook(_ http.ResponseWriter, r *http.Request, _ node.UiNodeGroup, _ *login.Flow, s *session.Session) error {\n\treturn otelx.WithSpan(r.Context(), \"selfservice.hook.SessionDestroyer.ExecuteLoginPostHook\", func(ctx context.Context) error {\n\t\tif _, err := e.r.SessionPersister().RevokeSessionsIdentityExcept(ctx, s.Identity.ID, s.ID); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n```\n\n### 1.6 There is zero existing concept of \"anonymous\" or \"guest\" in the codebase\n\nA codebase-wide search for `anonymous`, `guest`, `ephemeral` in the context of sessions returned **no relevant results**. This is a greenfield feature.\n\n---\n\n## 2. Proposed API design\n\n### 2.1 New endpoint: create anonymous session\n\n```/dev/null/api.yaml#L1-30\n# POST /sessions/anonymous\n# Creates an anonymous session without requiring authentication.\n\n# Request (Browser flow):\n#   No body required. Sets session cookie automatically.\n\n# Request (API flow):\n#   No body required.\n\n# Response 200:\n{\n  \"session\": {\n    \"id\": \"uuid\",\n    \"active\": true,\n    \"expires_at\": \"2024-01-01T00:00:00Z\",\n    \"issued_at\": \"2024-01-01T00:00:00Z\",\n    \"authenticated_at\": \"2024-01-01T00:00:00Z\",\n    \"authenticator_assurance_level\": \"aal0\",\n    \"authentication_methods\": [\n      { \"method\": \"anonymous\", \"aal\": \"aal0\", \"completed_at\": \"...\" }\n    ],\n    \"identity\": null,\n    \"anonymous\": true,\n    \"devices\": [...]\n  },\n  \"session_token\": \"ory_st_...\" // Only for API flows\n}\n```\n\n### 2.2 Modified `/sessions/whoami` behavior\n\nThe whoami endpoint should gracefully handle anonymous sessions:\n\n```/dev/null/api.yaml#L1-22\n# GET /sessions/whoami\n# Returns the current session. For anonymous sessions, identity is null.\n\n# Response 200 (anonymous session):\n{\n  \"id\": \"uuid\",\n  \"active\": true,\n  \"anonymous\": true,\n  \"authenticator_assurance_level\": \"aal0\",\n  \"authentication_methods\": [\n    { \"method\": \"anonymous\", \"aal\": \"aal0\" }\n  ],\n  \"identity\": null,\n  \"devices\": [...]\n}\n\n# Response 200 (authenticated session):\n# Same as today, with \"anonymous\": false\n```\n\n### 2.3 Session promotion: anonymous → authenticated\n\nWhen a user logs in or registers while holding an anonymous session, the session should be promotable:\n\n```/dev/null/api.yaml#L1-15\n# POST /self-service/login?flow=<flow_id>\n# If the user has an active anonymous session cookie/token,\n# the login flow promotes the anonymous session to an authenticated one.\n\n# Behavior:\n# 1. Anonymous session is revoked\n# 2. New authenticated session is created\n# 3. The anonymous session ID is available in the login hook context\n#    so that application logic can migrate anonymous data (e.g., cart)\n\n# New hook context field:\n#   \"previous_anonymous_session_id\": \"uuid\"  (available in post-login webhooks)\n```\n\n### 2.4 Configuration\n\n```/dev/null/config.yaml#L1-14\nsession:\n  anonymous:\n    # Enable anonymous session creation\n    enabled: false\n    # Lifespan of anonymous sessions (shorter than authenticated by default)\n    lifespan: 1h\n    # Maximum number of anonymous sessions per IP (rate limiting)\n    max_per_ip: 100\n    # Cookie name for anonymous sessions (separate from authenticated sessions)\n    cookie:\n      name: ory_kratos_anonymous_session\n```\n\n---\n\n## 3. Implementation approaches\n\n### Approach A: Phantom identity (recommended)\n\nCreate a lightweight \"anonymous\" identity behind the scenes for each anonymous session. This is the **lowest-risk** option.\n\n| Aspect | Detail |\n|---|---|\n| **Core idea** | When an anonymous session is requested, create a special `Identity` with `state: active`, a dedicated `schema_id: \"anonymous\"`, and empty traits. The session's `IdentityID` FK is satisfied. |\n| **Session struct change** | Add `Anonymous bool` field to `Session` (new DB column `is_anonymous`). |\n| **Existing code impact** | Minimal. All existing code that reads `IdentityID` or `Identity` continues to work. `whoami` can check `s.Anonymous` and null out the identity in the response. |\n| **Migration** | One new column: `ALTER TABLE sessions ADD COLUMN is_anonymous BOOL NOT NULL DEFAULT false`. |\n| **Promotion** | On login/registration, update the anonymous session's `IdentityID` to the real identity and set `is_anonymous = false`. Or revoke and create new. |\n| **Cleanup** | Expired anonymous sessions are cleaned up by existing `DeleteExpiredSessions`. The phantom identities can be garbage-collected when their sessions expire. |\n| **Drawbacks** | Creates identity records that aren't \"real\" users. Inflates identity counts. Needs logic to exclude anonymous identities from list/count endpoints. |\n\n### Approach B: Nullable IdentityID\n\nMake `IdentityID` nullable across the entire stack.\n\n| Aspect | Detail |\n|---|---|\n| **Core idea** | Change `IdentityID uuid.UUID` → `IdentityID uuid.NullUUID` in the `Session` struct. Change DB column to nullable. |\n| **Blast radius** | **Very large.** Every code path that references `IdentityID` or `Identity` must handle nil: `UpsertSession`, `ActivateSession`, `GetSessionByToken`, `DoesSessionSatisfy`, `SetSessionDeviceInformation`, `Tokenizer`, all hooks, all self-service flows, OpenAPI spec, generated clients. |\n| **Migration** | `ALTER TABLE sessions ALTER COLUMN identity_id DROP NOT NULL; ALTER TABLE sessions DROP CONSTRAINT sessions_identity_id_fkey; ADD CONSTRAINT ... ON DELETE SET NULL`. |\n| **Drawbacks** | High risk of nil-pointer panics. Breaks the invariant that every session has an owner. Difficult to validate completeness of nil-handling. |\n\n### Approach C: Separate anonymous session table and handler\n\nCreate a distinct `anonymous_sessions` table with its own handler.\n\n| Aspect | Detail |\n|---|---|\n| **Core idea** | `anonymous_sessions` table with `id`, `token`, `expires_at`, `metadata`, `devices`. Completely separate from authenticated sessions. |\n| **Blast radius** | Low on existing code. New code is isolated. |\n| **Migration** | New table only, no changes to existing schema. |\n| **Drawbacks** | Duplicates session management logic (cookie issuance, token handling, expiry, etc.). Two parallel systems to maintain. `whoami` must check both tables. Session promotion requires cross-table coordination. |\n\n---\n\n## 4. Impact matrix\n\n| Component | Approach A (Phantom) | Approach B (Nullable) | Approach C (Separate) |\n|---|---|---|---|\n| `session.Session` struct | +1 field | Change `IdentityID` type | No change |\n| `session.Persister` | Minor guard | Major refactor | New interface |\n| `session.ManagerHTTP` | New method + guards | Refactor `ActivateSession`, `FetchFromRequest`, `DoesSessionSatisfy` | New manager |\n| `session.Handler` | New route + `whoami` guard | Guards in every handler | New handler |\n| `session.Tokenizer` | Guard for anonymous | Guard for nullable identity | Separate tokenizer logic |\n| DB migration | 1 column | ALTER + FK change | New table |\n| Self-service flows | Hook context extension | Nil-handling everywhere | Isolated |\n| Hooks | Nil-guard in ~5 hooks | Nil-guard in ~5 hooks | N/A |\n| OpenAPI spec | New endpoint + field | Modified `session` schema | New endpoints + schema |\n| Identity handler/pool | Exclude anonymous from counts | No change | No change |\n| Risk | **Low-Medium** | **High** | **Low** |\n| Effort | **Medium** (~2-3 weeks) | **High** (~4-6 weeks) | **Medium** (~2-3 weeks) |\n\n---\n\n## 5. Recommendation\n\n**Approach A (Phantom Identity)** is recommended. It satisfies the FK constraint naturally, minimizes blast radius on existing code, and leverages all existing session infrastructure (cookies, tokens, expiry, cleanup, caching). The main trade-off—phantom identities inflating counts—is manageable by filtering on the `is_anonymous` column or a dedicated `schema_id`.\n\n### Key implementation steps for Approach A:\n\n1. **Add `Anonymous` field** to `Session` struct + DB migration.\n2. **Add new `CredentialsType`**: `CredentialsTypeAnonymous = \"anonymous\"` for the AMR.\n3. **Add `POST /sessions/anonymous` endpoint** in `session.Handler` that:\n   - Creates a phantom identity with `schema_id: \"anonymous\"` and empty traits.\n   - Creates and activates a session with `Anonymous: true`, `AAL: aal0`.\n   - Issues cookie or returns token.\n4. **Guard `whoami`**: If `s.Anonymous`, null out identity in response and skip the `X-Kratos-Authenticated-Identity-Id` header.\n5. **Guard hooks**: Add nil/anonymous checks in `SessionDestroyer` and other hooks.\n6. **Session promotion**: In the login/registration post-hook, detect if an anonymous session exists, revoke it, and pass the old session ID to webhooks via `transient_payload` or a new hook context field.\n7. **Configuration**: Add `session.anonymous.enabled` and `session.anonymous.lifespan`.\n8. **Identity list filtering**: Exclude anonymous identities from `/admin/identities` by default (or add a filter parameter).\n9. **Cleanup job**: Extend `DeleteExpiredSessions` to also garbage-collect orphaned phantom identities whose sessions are all expired/revoked.\n\n---\n\n## 6. Open questions\n\n1. **Should anonymous sessions share the same cookie name?** Using a separate cookie avoids interference but complicates promotion. Using the same cookie makes promotion seamless but means authenticated sessions overwrite anonymous ones.\n2. **Should anonymous sessions be tokenizable (JWT)?** The `sub` claim has no meaningful identity. A session-ID-only JWT could work, but consumers expecting an identity subject would break.\n3. **Rate limiting**: Without authentication, anonymous session creation is a DoS vector. Per-IP rate limiting and short lifespans are essential.\n4. **Multi-tenancy (NID)**: Anonymous sessions should respect network isolation like authenticated sessions. No additional work needed since phantom identities inherit the NID.\n5. **Hydra/OAuth2 integration**: Anonymous sessions should likely **not** be usable as OAuth2 login sessions. The `AcceptLoginRequest` flow requires an authenticated identity.\n"
  },
  {
    "path": "buf.gen.yaml",
    "content": "version: v2\nmanaged:\n  enabled: true\n  override:\n    - file_option: go_package_prefix\n      value: github.com/ory/kratos\nplugins:\n  - remote: buf.build/protocolbuffers/go\n    out: gen\n    opt: paths=source_relative\ninputs:\n  - directory: proto\n"
  },
  {
    "path": "buf.yaml",
    "content": "version: v2\nmodules:\n  - path: proto\nlint:\n  use:\n    - DEFAULT\nbreaking:\n  use:\n    - FILE\n"
  },
  {
    "path": "cipher/aes.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cipher\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\n\t\"github.com/gtank/cryptopasta\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/pkg/errors\"\n)\n\ntype AES struct {\n\tc SecretsProvider\n}\n\nfunc NewCryptAES(c SecretsProvider) *AES {\n\treturn &AES{c: c}\n}\n\n// Encrypt return a AES encrypt of plaintext\nfunc (a *AES) Encrypt(ctx context.Context, message []byte) (string, error) {\n\tif len(message) == 0 {\n\t\t// do nothing if empty instead of return an error\n\t\t// return nil, errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Can not encrypt empty string.\"))\n\t\treturn \"\", nil\n\t}\n\n\tif len(a.c.SecretsCipher(ctx)) == 0 {\n\t\treturn \"\", errors.WithStack(herodot.ErrMisconfiguration.WithReason(\"Unable to encrypt message because no cipher secrets were configured.\"))\n\t}\n\n\tciphertext, err := cryptopasta.Encrypt(message, &a.c.SecretsCipher(ctx)[0])\n\tif err != nil {\n\t\treturn \"\", errors.WithStack(herodot.ErrForbidden.WithWrap(err))\n\t}\n\treturn hex.EncodeToString(ciphertext), nil\n}\n\n// Decrypt returns the decrypted aes data\nfunc (a *AES) Decrypt(ctx context.Context, ciphertext string) ([]byte, error) {\n\tif len(ciphertext) == 0 {\n\t\t// do nothing if empty instead of return an error\n\t\t// return \"\", errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Can not decrypt empty message.\"))\n\t\treturn nil, nil\n\t}\n\n\tsecrets := a.c.SecretsCipher(ctx)\n\tif len(secrets) == 0 {\n\t\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.WithReason(\"Unable to decipher the encrypted message because no AES secrets were configured.\"))\n\t}\n\n\tdecode, err := hex.DecodeString(ciphertext)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithWrap(err))\n\t}\n\n\tfor i := range secrets {\n\t\tplaintext, err := cryptopasta.Decrypt(decode, &secrets[i])\n\t\tif err == nil {\n\t\t\treturn plaintext, nil\n\t\t}\n\t}\n\n\treturn nil, errors.WithStack(herodot.ErrForbidden.WithReason(\"Unable to decipher the encrypted message.\"))\n}\n"
  },
  {
    "path": "cipher/chacha20.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cipher\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"io\"\n\t\"math\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/crypto/chacha20poly1305\"\n\n\t\"github.com/ory/herodot\"\n)\n\ntype XChaCha20Poly1305 struct {\n\tc SecretsProvider\n}\n\nfunc NewCryptChaCha20(c SecretsProvider) *XChaCha20Poly1305 {\n\treturn &XChaCha20Poly1305{c: c}\n}\n\n// Encrypt returns a ChaCha encryption of plaintext\nfunc (c *XChaCha20Poly1305) Encrypt(ctx context.Context, message []byte) (string, error) {\n\tif len(message) == 0 {\n\t\treturn \"\", nil\n\t}\n\n\tif len(c.c.SecretsCipher(ctx)) == 0 {\n\t\treturn \"\", errors.WithStack(herodot.ErrMisconfiguration.WithReason(\"Unable to encrypt message because no cipher secrets were configured.\"))\n\t}\n\n\taead, err := chacha20poly1305.NewX(c.c.SecretsCipher(ctx)[0][:])\n\tif err != nil {\n\t\treturn \"\", herodot.ErrInternalServerError.WithWrap(err).WithReason(\"Unable to generate key\")\n\t}\n\n\t// Make sure the size calculation does not overflow.\n\tif len(message) > math.MaxInt-aead.NonceSize()-aead.Overhead() {\n\t\treturn \"\", errors.WithStack(herodot.ErrInternalServerError.WithReason(\"plaintext too large\"))\n\t}\n\n\tnonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(message)+aead.Overhead())\n\t_, err = io.ReadFull(rand.Reader, nonce)\n\tif err != nil {\n\t\treturn \"\", errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReason(\"Unable to generate nonce\"))\n\t}\n\n\tencryptedMsg := aead.Seal(nonce, nonce, message, nil)\n\treturn hex.EncodeToString(encryptedMsg), nil\n}\n\n// Decrypt decrypts data using 256 bit key\nfunc (c *XChaCha20Poly1305) Decrypt(ctx context.Context, ciphertext string) ([]byte, error) {\n\tif len(ciphertext) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tsecrets := c.c.SecretsCipher(ctx)\n\tif len(secrets) == 0 {\n\t\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.WithReason(\"Unable to decipher the encrypted message because no cipher secrets were configured.\"))\n\t}\n\n\trawCiphertext, err := hex.DecodeString(ciphertext)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithWrap(err).WithReason(\"Unable to decode hex encrypted string\"))\n\t}\n\n\tfor i := range secrets {\n\t\taead, err := chacha20poly1305.NewX(secrets[i][:])\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReason(\"Unable to instantiate chacha20\"))\n\t\t}\n\n\t\tif len(ciphertext) < aead.NonceSize() {\n\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReason(\"cipher text too short\"))\n\t\t}\n\n\t\tnonce, ciphertext := rawCiphertext[:aead.NonceSize()], rawCiphertext[aead.NonceSize():]\n\t\tplaintext, err := aead.Open(nil, nonce, ciphertext, nil)\n\t\tif err == nil {\n\t\t\treturn plaintext, nil\n\t\t}\n\t}\n\n\treturn nil, errors.WithStack(herodot.ErrForbidden.WithReason(\"Unable to decrypt string\"))\n}\n"
  },
  {
    "path": "cipher/cipher.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cipher\n\nimport \"context\"\n\n// Cipher provides methods for encrypt and decrypt string\ntype Cipher interface {\n\t// Encrypt encrypts the (binary) message and returns a hex-encoded binary ciphertext\n\t// or an error if the encryption failed.\n\t//\n\t// If the message is empty, the ciphertext is also empty and no error is returned.\n\tEncrypt(ctx context.Context, message []byte) (string, error)\n\n\t// Decrypt takes a hex-encoded binary ciphertext and decrypts it or returns an error if the decryption\n\t// failed.\n\t//\n\t// If the ciphertext is empty a nil byte slice is returned.\n\tDecrypt(ctx context.Context, encrypted string) ([]byte, error)\n}\n\ntype Provider interface {\n\tCipher(ctx context.Context) Cipher\n}\n\ntype SecretsProvider interface {\n\tSecretsCipher(ctx context.Context) [][32]byte\n}\n"
  },
  {
    "path": "cipher/cipher_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cipher_test\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/cipher\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n)\n\nvar goodSecret = []string{\"secret-thirty-two-character-long\"}\n\nfunc TestCipher(t *testing.T) {\n\tctx := context.Background()\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValue(config.ViperKeySecretsDefault, goodSecret))\n\n\tciphers := []cipher.Cipher{\n\t\tcipher.NewCryptAES(reg.Config()),\n\t\tcipher.NewCryptChaCha20(reg.Config()),\n\t}\n\n\tfor _, c := range ciphers {\n\t\tt.Run(fmt.Sprintf(\"cipher=%T\", c), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tt.Run(\"case=all_work\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\ttestAllWork(ctx, t, c)\n\t\t\t})\n\n\t\t\tt.Run(\"case=encryption_failed\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecretsCipher, []string{\"\"})\n\n\t\t\t\t// secret have to be set\n\t\t\t\t_, err := c.Encrypt(ctx, []byte(\"not-empty\"))\n\t\t\t\trequire.Error(t, err)\n\t\t\t\tvar hErr *herodot.DefaultError\n\t\t\t\trequire.ErrorAs(t, err, &hErr)\n\t\t\t\tassert.Equal(t, \"Unable to encrypt message because no cipher secrets were configured.\", hErr.Reason())\n\n\t\t\t\tctx = contextx.WithConfigValue(ctx, config.ViperKeySecretsCipher, []string{\"bad-length\"})\n\n\t\t\t\t// bad secret length\n\t\t\t\t_, err = c.Encrypt(ctx, []byte(\"not-empty\"))\n\t\t\t\trequire.ErrorAs(t, err, &hErr)\n\t\t\t\tassert.Equal(t, \"Unable to encrypt message because no cipher secrets were configured.\", hErr.Reason())\n\t\t\t})\n\n\t\t\tt.Run(\"case=decryption_failed\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\t_, err := c.Decrypt(ctx, hex.EncodeToString([]byte(\"bad-data\")))\n\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t_, err = c.Decrypt(ctx, \"not-empty\")\n\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t_, err = c.Decrypt(contextx.WithConfigValue(ctx, config.ViperKeySecretsCipher, []string{\"\"}), \"not-empty\")\n\t\t\t\trequire.Error(t, err)\n\t\t\t})\n\t\t})\n\t}\n\n\tc := cipher.NewNoop()\n\tt.Run(fmt.Sprintf(\"cipher=%T\", c), func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttestAllWork(ctx, t, c)\n\t})\n}\n\nfunc testAllWork(ctx context.Context, t *testing.T, c cipher.Cipher) {\n\tmessage := \"my secret message!\"\n\n\tencryptedSecret, err := c.Encrypt(ctx, []byte(message))\n\trequire.NoError(t, err)\n\n\tdecryptedSecret, err := c.Decrypt(ctx, encryptedSecret)\n\trequire.NoError(t, err, \"encrypted\", encryptedSecret)\n\tassert.Equal(t, message, string(decryptedSecret))\n\n\t// data to encrypt return blank result\n\t_, err = c.Encrypt(ctx, []byte(\"\"))\n\trequire.NoError(t, err)\n\n\t// empty encrypted data return blank\n\t_, err = c.Decrypt(ctx, \"\")\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "cipher/noop.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cipher\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n)\n\n// Noop is default cipher implementation witch does not do encryption\ntype Noop struct{}\n\nfunc NewNoop() *Noop {\n\treturn &Noop{}\n}\n\n// Encrypt encode message to hex\nfunc (*Noop) Encrypt(_ context.Context, message []byte) (string, error) {\n\treturn hex.EncodeToString(message), nil\n}\n\n// Decrypt decode the hex message\nfunc (*Noop) Decrypt(_ context.Context, ciphertext string) ([]byte, error) {\n\treturn hex.DecodeString(ciphertext)\n}\n"
  },
  {
    "path": "cmd/cleanup/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cleanup\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/configx\"\n)\n\nfunc NewCleanupCmd() *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:   \"cleanup\",\n\t\tShort: \"Various cleanup helpers\",\n\t}\n\tconfigx.RegisterFlags(c.PersistentFlags())\n\treturn c\n}\n\nfunc RegisterCommandRecursive(parent *cobra.Command) {\n\tc := NewCleanupCmd()\n\tparent.AddCommand(c)\n\tc.AddCommand(NewCleanupSQLCmd())\n}\n"
  },
  {
    "path": "cmd/cleanup/sql.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cleanup\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/ory/x/cmdx\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n\t\"github.com/ory/x/configx\"\n)\n\n// cleanupSqlCmd represents the sql command\nfunc NewCleanupSQLCmd() *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:   \"sql <database-url>\",\n\t\tShort: \"Cleanup sql database from expired flows and sessions\",\n\t\tLong: `Run this command as frequently as you need.\nIt is recommended to run this command close to the SQL instance (e.g. same subnet) instead of over the public internet.\nThis decreases risk of failure and decreases time required.\nYou can read in the database URL using the -e flag, for example:\n\texport DSN=...\n\tkratos cleanup sql -e\n### WARNING ###\nBefore running this command on an existing database, create a back up!\n`,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\terr := cliclient.NewCleanupHandler().CleanupSQL(cmd, args)\n\t\t\tif err != nil {\n\t\t\t\t_, _ = fmt.Fprintln(cmd.OutOrStdout(), err)\n\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\n\tconfigx.RegisterFlags(c.PersistentFlags())\n\tc.Flags().BoolP(\"read-from-env\", \"e\", true, \"If set, reads the database connection string from the environment variable DSN or config file key dsn.\")\n\tc.Flags().Duration(config.ViperKeyDatabaseCleanupSleepTables, time.Minute, \"How long to wait between each table cleanup\")\n\tc.Flags().IntP(config.ViperKeyDatabaseCleanupBatchSize, \"b\", 100, \"Set the number of records to be cleaned per run\")\n\tc.Flags().Duration(\"keep-last\", 0, \"Don't remove records younger than\")\n\treturn c\n}\n"
  },
  {
    "path": "cmd/cleanup/sql_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cleanup\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc Test_ExecuteCleanupFailedDSN(t *testing.T) {\n\tcmd := NewCleanupSQLCmd()\n\tb := bytes.NewBufferString(\"\")\n\tcmd.SetOut(b)\n\tcmd.SetArgs([]string{\"--read-from-env=false\"})\n\t_ = cmd.Execute()\n\tout, err := io.ReadAll(b)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !strings.Contains(string(out), \"expected to get the DSN as an argument\") {\n\t\tt.Fatalf(\"expected \\\"%s\\\" got \\\"%s\\\"\", \"expected to get the DSN as an argument\", string(out))\n\t}\n\t_ = cmd.Execute()\n}\n"
  },
  {
    "path": "cmd/cliclient/cleanup.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cliclient\n\nimport (\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/contextx\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/flagx\"\n)\n\ntype CleanupHandler struct{}\n\nfunc NewCleanupHandler() *CleanupHandler {\n\treturn &CleanupHandler{}\n}\n\nfunc (h *CleanupHandler) CleanupSQL(cmd *cobra.Command, args []string) error {\n\topts := []configx.OptionModifier{\n\t\tconfigx.WithFlags(cmd.Flags()),\n\t\tconfigx.SkipValidation(),\n\t}\n\n\tif !flagx.MustGetBool(cmd, \"read-from-env\") {\n\t\tif len(args) != 1 {\n\t\t\treturn errors.New(`expected to get the DSN as an argument, or the \"read-from-env\" flag`)\n\t\t}\n\t\topts = append(opts, configx.WithValue(config.ViperKeyDSN, args[0]))\n\t}\n\n\td, err := driver.NewWithoutInit(\n\t\tcmd.Context(),\n\t\tcmd.ErrOrStderr(),\n\t\tdriver.WithConfigOptions(opts...),\n\t)\n\tif len(d.Config().DSN(cmd.Context())) == 0 {\n\t\treturn errors.New(`required config value \"dsn\" was not set`)\n\t} else if err != nil {\n\t\treturn errors.Wrap(err, \"An error occurred initializing cleanup\")\n\t}\n\n\terr = d.Init(cmd.Context(), &contextx.Default{})\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"An error occurred initializing cleanup\")\n\t}\n\n\tkeepLast := flagx.MustGetDuration(cmd, \"keep-last\")\n\n\terr = d.Persister().CleanupDatabase(\n\t\tcmd.Context(),\n\t\td.Config().DatabaseCleanupSleepTables(cmd.Context()),\n\t\tkeepLast,\n\t\td.Config().DatabaseCleanupBatchSize(cmd.Context()))\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"An error occurred while cleaning up expired data\")\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/cliclient/client.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cliclient\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/spf13/pflag\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n)\n\nconst (\n\tenvKeyEndpoint = \"KRATOS_ADMIN_URL\"\n\tFlagEndpoint   = \"endpoint\"\n)\n\ntype ContextKey int\n\nconst (\n\tClientContextKey ContextKey = iota + 1\n)\n\ntype ClientContext struct {\n\tEndpoint   string\n\tHTTPClient *http.Client\n}\n\nfunc NewClient(cmd *cobra.Command) (*kratos.APIClient, error) {\n\tif f, ok := cmd.Context().Value(ClientContextKey).(func(cmd *cobra.Command) (*ClientContext, error)); ok {\n\t\tcc, err := f(cmd)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tconf := kratos.NewConfiguration()\n\t\tconf.HTTPClient = cc.HTTPClient\n\t\tconf.Servers = kratos.ServerConfigurations{{URL: cc.Endpoint}}\n\t\treturn kratos.NewAPIClient(conf), nil\n\t} else if f != nil {\n\t\treturn nil, errors.Errorf(\"ClientContextKey was expected to be *client.OryKratos but it contained an invalid type %T \", f)\n\t}\n\n\tendpoint, err := cmd.Flags().GetString(FlagEndpoint)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif endpoint == \"\" {\n\t\tendpoint = os.Getenv(envKeyEndpoint)\n\t}\n\n\tif endpoint == \"\" {\n\t\treturn nil, errors.Errorf(\"you have to set the remote endpoint, try --help for details\")\n\t}\n\n\tu, err := url.Parse(endpoint)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, `could not parse the endpoint URL \"%s\"`, endpoint)\n\t}\n\n\tconf := kratos.NewConfiguration()\n\tconf.HTTPClient = retryablehttp.NewClient().StandardClient()\n\tconf.HTTPClient.Timeout = time.Second * 10\n\tconf.Servers = kratos.ServerConfigurations{{URL: u.String()}}\n\treturn kratos.NewAPIClient(conf), nil\n}\n\nfunc RegisterClientFlags(flags *pflag.FlagSet) {\n\tflags.StringP(FlagEndpoint, FlagEndpoint[:1], \"\", fmt.Sprintf(\"The URL of Ory Kratos' Admin API. Alternatively set using the %s environmental variable.\", envKeyEndpoint))\n}\n"
  },
  {
    "path": "cmd/cliclient/migrate.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cliclient\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/popx\"\n\n\t\"github.com/ory/x/contextx\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/flagx\"\n)\n\ntype MigrateHandler struct{}\n\nfunc NewMigrateHandler() *MigrateHandler {\n\treturn &MigrateHandler{}\n}\n\nfunc (h *MigrateHandler) getPersister(cmd *cobra.Command, args []string, opts []driver.RegistryOption) (d driver.Registry, err error) {\n\tif flagx.MustGetBool(cmd, \"read-from-env\") {\n\t\td, err = driver.NewWithoutInit(\n\t\t\tcmd.Context(),\n\t\t\tcmd.ErrOrStderr(),\n\t\t\tdriver.WithConfigOptions(\n\t\t\t\tconfigx.WithFlags(cmd.Flags()),\n\t\t\t\tconfigx.SkipValidation(),\n\t\t\t))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(d.Config().DSN(cmd.Context())) == 0 {\n\t\t\tfmt.Println(cmd.UsageString())\n\t\t\tfmt.Println(\"\")\n\t\t\tfmt.Println(\"When using flag -e, environment variable DSN must be set\")\n\t\t\treturn nil, cmdx.FailSilently(cmd)\n\t\t}\n\t} else {\n\t\tif len(args) != 1 {\n\t\t\tfmt.Println(cmd.UsageString())\n\t\t\treturn nil, cmdx.FailSilently(cmd)\n\t\t}\n\t\td, err = driver.NewWithoutInit(\n\t\t\tcmd.Context(),\n\t\t\tcmd.ErrOrStderr(),\n\t\t\tdriver.WithConfigOptions(\n\t\t\t\tconfigx.WithFlags(cmd.Flags()),\n\t\t\t\tconfigx.SkipValidation(),\n\t\t\t\tconfigx.WithValue(config.ViperKeyDSN, args[0]),\n\t\t\t))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\terr = d.Init(cmd.Context(), &contextx.Default{}, append(opts, driver.SkipNetworkInit)...)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"an error occurred initializing migrations\")\n\t}\n\n\treturn d, nil\n}\n\nfunc (h *MigrateHandler) MigrateSQLDown(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error {\n\tp, err := h.getPersister(cmd, args, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn popx.MigrateSQLDown(cmd, p.Persister())\n}\n\nfunc (h *MigrateHandler) MigrateSQLStatus(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error {\n\tp, err := h.getPersister(cmd, args, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn popx.MigrateStatus(cmd, p.Persister())\n}\n\nfunc (h *MigrateHandler) MigrateSQLUp(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error {\n\tp, err := h.getPersister(cmd, args, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn popx.MigrateSQLUp(cmd, p.Persister())\n}\n"
  },
  {
    "path": "cmd/clidoc/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/importer\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/cmd\"\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/ory/x/clidoc\"\n)\n\nvar (\n\taSecondAgo = time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC).Add(-time.Second)\n\tinAMinute  = time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC).Add(time.Minute)\n)\n\nvar messages map[string]*text.Message\n\nfunc init() {\n\ttext.Until = func(t time.Time) time.Duration {\n\t\treturn time.Minute\n\t}\n\ttext.Since = func(time.Time) time.Duration {\n\t\treturn time.Minute\n\t}\n\n\tmessages = map[string]*text.Message{\n\t\t\"NewInfoNodeLabelVerifyOTP\":                  text.NewInfoNodeLabelVerifyOTP(),\n\t\t\"NewInfoNodeLabelVerificationCode\":           text.NewInfoNodeLabelVerificationCode(),\n\t\t\"NewInfoNodeLabelRecoveryCode\":               text.NewInfoNodeLabelRecoveryCode(),\n\t\t\"NewInfoNodeInputPassword\":                   text.NewInfoNodeInputPassword(),\n\t\t\"NewInfoNodeInputPhoneNumber\":                text.NewInfoNodeInputPhoneNumber(),\n\t\t\"NewInfoNodeLabelGenerated\":                  text.NewInfoNodeLabelGenerated(\"{title}\", \"{name}\"),\n\t\t\"NewInfoNodeLabelSave\":                       text.NewInfoNodeLabelSave(),\n\t\t\"NewInfoNodeLabelSubmit\":                     text.NewInfoNodeLabelSubmit(),\n\t\t\"NewInfoNodeLabelID\":                         text.NewInfoNodeLabelID(),\n\t\t\"NewErrorValidationSettingsFlowExpired\":      text.NewErrorValidationSettingsFlowExpired(aSecondAgo),\n\t\t\"NewInfoSelfServiceSettingsTOTPQRCode\":       text.NewInfoSelfServiceSettingsTOTPQRCode(),\n\t\t\"NewInfoSelfServiceSettingsTOTPSecret\":       text.NewInfoSelfServiceSettingsTOTPSecret(\"{secret}\"),\n\t\t\"NewInfoSelfServiceSettingsTOTPSecretLabel\":  text.NewInfoSelfServiceSettingsTOTPSecretLabel(),\n\t\t\"NewInfoSelfServiceSettingsUpdateSuccess\":    text.NewInfoSelfServiceSettingsUpdateSuccess(),\n\t\t\"NewInfoSelfServiceSettingsUpdateUnlinkTOTP\": text.NewInfoSelfServiceSettingsUpdateUnlinkTOTP(),\n\t\t\"NewInfoSelfServiceSettingsRevealLookup\":     text.NewInfoSelfServiceSettingsRevealLookup(),\n\t\t\"NewInfoSelfServiceSettingsRegenerateLookup\": text.NewInfoSelfServiceSettingsRegenerateLookup(),\n\t\t\"NewInfoSelfServiceSettingsDisableLookup\":    text.NewInfoSelfServiceSettingsDisableLookup(),\n\t\t\"NewInfoSelfServiceSettingsLookupConfirm\":    text.NewInfoSelfServiceSettingsLookupConfirm(),\n\t\t\"NewInfoSelfServiceSettingsLookupSecretList\": text.NewInfoSelfServiceSettingsLookupSecretList([]string{\"{secrets_list}\"}, []interface{}{\n\t\t\ttext.NewInfoSelfServiceSettingsLookupSecret(\"{secret}\"),\n\t\t\ttext.NewInfoSelfServiceSettingsLookupSecretUsed(aSecondAgo),\n\t\t}),\n\t\t\"NewInfoSelfServiceSettingsLookupSecret\":                  text.NewInfoSelfServiceSettingsLookupSecret(\"{secret}\"),\n\t\t\"NewInfoSelfServiceSettingsLookupSecretUsed\":              text.NewInfoSelfServiceSettingsLookupSecretUsed(aSecondAgo),\n\t\t\"NewInfoSelfServiceSettingsLookupSecretsLabel\":            text.NewInfoSelfServiceSettingsLookupSecretsLabel(),\n\t\t\"NewInfoSelfServiceSettingsUpdateLinkOIDC\":                text.NewInfoSelfServiceSettingsUpdateLinkOIDC(\"{provider}\"),\n\t\t\"NewInfoSelfServiceSettingsUpdateUnlinkOIDC\":              text.NewInfoSelfServiceSettingsUpdateUnlinkOIDC(\"{provider}\"),\n\t\t\"NewInfoSelfServiceRegisterWebAuthnDisplayName\":           text.NewInfoSelfServiceRegisterWebAuthnDisplayName(),\n\t\t\"NewInfoSelfServiceRemoveWebAuthn\":                        text.NewInfoSelfServiceRemoveWebAuthn(\"{display_name}\", aSecondAgo),\n\t\t\"NewInfoSelfServiceRemovePasskey\":                         text.NewInfoSelfServiceRemovePasskey(\"{display_name}\", aSecondAgo),\n\t\t\"NewErrorValidationVerificationFlowExpired\":               text.NewErrorValidationVerificationFlowExpired(aSecondAgo),\n\t\t\"NewInfoSelfServiceVerificationSuccessful\":                text.NewInfoSelfServiceVerificationSuccessful(),\n\t\t\"NewVerificationEmailSent\":                                text.NewVerificationEmailSent(),\n\t\t\"NewVerificationEmailWithCodeSent\":                        text.NewVerificationEmailWithCodeSent(),\n\t\t\"NewErrorValidationVerificationTokenInvalidOrAlreadyUsed\": text.NewErrorValidationVerificationTokenInvalidOrAlreadyUsed(),\n\t\t\"NewErrorValidationVerificationRetrySuccess\":              text.NewErrorValidationVerificationRetrySuccess(),\n\t\t\"NewErrorValidationVerificationStateFailure\":              text.NewErrorValidationVerificationStateFailure(),\n\t\t\"NewErrorValidationVerificationCodeInvalidOrAlreadyUsed\":  text.NewErrorValidationVerificationCodeInvalidOrAlreadyUsed(),\n\t\t\"NewErrorSystemGeneric\":                                   text.NewErrorSystemGeneric(\"{reason}\"),\n\t\t\"NewErrorSystemNoAuthenticationMethodsAvailable\":          text.NewErrorSystemNoAuthenticationMethodsAvailable(),\n\t\t\"NewValidationErrorGeneric\":                               text.NewValidationErrorGeneric(\"{reason}\"),\n\t\t\"NewValidationErrorRequired\":                              text.NewValidationErrorRequired(\"{property}\"),\n\t\t\"NewErrorValidationMinLength\":                             text.NewErrorValidationMinLength(5, 3),\n\t\t\"NewErrorValidationMaxLength\":                             text.NewErrorValidationMaxLength(5, 6),\n\t\t\"NewErrorValidationInvalidFormat\":                         text.NewErrorValidationInvalidFormat(\"{pattern}\"),\n\t\t\"NewErrorValidationMinimum\":                               text.NewErrorValidationMinimum(5, 3),\n\t\t\"NewErrorValidationExclusiveMinimum\":                      text.NewErrorValidationExclusiveMinimum(5, 5),\n\t\t\"NewErrorValidationMaximum\":                               text.NewErrorValidationMaximum(5, 6),\n\t\t\"NewErrorValidationExclusiveMaximum\":                      text.NewErrorValidationExclusiveMaximum(5, 5),\n\t\t\"NewErrorValidationMultipleOf\":                            text.NewErrorValidationMultipleOf(7, 3),\n\t\t\"NewErrorValidationMaxItems\":                              text.NewErrorValidationMaxItems(3, 4),\n\t\t\"NewErrorValidationMinItems\":                              text.NewErrorValidationMinItems(3, 2),\n\t\t\"NewErrorValidationUniqueItems\":                           text.NewErrorValidationUniqueItems(0, 2),\n\t\t\"NewErrorValidationWrongType\":                             text.NewErrorValidationWrongType([]string{\"{allowed_types_list}\"}, \"{actual_type}\"),\n\t\t\"NewErrorValidationConst\":                                 text.NewErrorValidationConst(\"{expected}\"),\n\t\t\"NewErrorValidationConstGeneric\":                          text.NewErrorValidationConstGeneric(),\n\t\t\"NewErrorValidationPasswordPolicyViolationGeneric\":        text.NewErrorValidationPasswordPolicyViolationGeneric(\"{reason}\"),\n\t\t\"NewErrorValidationPasswordIdentifierTooSimilar\":          text.NewErrorValidationPasswordIdentifierTooSimilar(),\n\t\t\"NewErrorValidationPasswordMinLength\":                     text.NewErrorValidationPasswordMinLength(6, 5),\n\t\t\"NewErrorValidationPasswordMaxLength\":                     text.NewErrorValidationPasswordMaxLength(72, 80),\n\t\t\"NewErrorValidationPasswordTooManyBreaches\":               text.NewErrorValidationPasswordTooManyBreaches(101),\n\t\t\"NewErrorValidationPasswordNewSameAsOld\":                  text.NewErrorValidationPasswordNewSameAsOld(),\n\t\t\"NewErrorValidationInvalidCredentials\":                    text.NewErrorValidationInvalidCredentials(),\n\t\t\"NewErrorValidationDuplicateCredentials\":                  text.NewErrorValidationDuplicateCredentials(),\n\t\t\"NewErrorValidationDuplicateCredentialsWithHints\":         text.NewErrorValidationDuplicateCredentialsWithHints([]string{\"{available_credential_types_list}\"}, []string{\"{available_oidc_providers_list}\"}, \"{credential_identifier_hint}\"),\n\t\t\"NewErrorValidationDuplicateCredentialsOnOIDCLink\":        text.NewErrorValidationDuplicateCredentialsOnOIDCLink(),\n\t\t\"NewErrorValidationTOTPVerifierWrong\":                     text.NewErrorValidationTOTPVerifierWrong(),\n\t\t\"NewErrorValidationLookupAlreadyUsed\":                     text.NewErrorValidationLookupAlreadyUsed(),\n\t\t\"NewErrorValidationLookupInvalid\":                         text.NewErrorValidationLookupInvalid(),\n\t\t\"NewErrorValidationIdentifierMissing\":                     text.NewErrorValidationIdentifierMissing(),\n\t\t\"NewErrorValidationAddressNotVerified\":                    text.NewErrorValidationAddressNotVerified(),\n\t\t\"NewErrorValidationNoTOTPDevice\":                          text.NewErrorValidationNoTOTPDevice(),\n\t\t\"NewErrorValidationNoLookup\":                              text.NewErrorValidationNoLookup(),\n\t\t\"NewErrorValidationNoWebAuthnDevice\":                      text.NewErrorValidationNoWebAuthnDevice(),\n\t\t\"NewInfoLoginReAuth\":                                      text.NewInfoLoginReAuth(),\n\t\t\"NewInfoLoginMFA\":                                         text.NewInfoLoginMFA(),\n\t\t\"NewInfoLoginTOTPLabel\":                                   text.NewInfoLoginTOTPLabel(),\n\t\t\"NewInfoLoginLookupLabel\":                                 text.NewInfoLoginLookupLabel(),\n\t\t\"NewInfoLogin\":                                            text.NewInfoLogin(),\n\t\t\"NewInfoLoginAndLink\":                                     text.NewInfoLoginAndLink(),\n\t\t\"NewInfoLoginLinkMessage\":                                 text.NewInfoLoginLinkMessage(\"{duplicateIdentifier}\", \"{provider}\", \"{newLoginUrl}\", []string{\"{available_credential_types_list}\"}, []string{\"{available_oidc_providers_list}\"}),\n\t\t\"NewInfoLoginTOTP\":                                        text.NewInfoLoginTOTP(),\n\t\t\"NewInfoLoginLookup\":                                      text.NewInfoLoginLookup(),\n\t\t\"NewInfoLoginVerify\":                                      text.NewInfoLoginVerify(),\n\t\t\"NewInfoLoginWith\":                                        text.NewInfoLoginWith(\"{provider}\", \"{providerID}\"),\n\t\t\"NewInfoLoginWithAndLink\":                                 text.NewInfoLoginWithAndLink(\"{provider}\"),\n\t\t\"NewErrorValidationLoginFlowExpired\":                      text.NewErrorValidationLoginFlowExpired(aSecondAgo),\n\t\t\"NewErrorValidationLoginNoStrategyFound\":                  text.NewErrorValidationLoginNoStrategyFound(),\n\t\t\"NewErrorValidationRegistrationNoStrategyFound\":           text.NewErrorValidationRegistrationNoStrategyFound(),\n\t\t\"NewErrorValidationSettingsNoStrategyFound\":               text.NewErrorValidationSettingsNoStrategyFound(),\n\t\t\"NewErrorValidationRecoveryNoStrategyFound\":               text.NewErrorValidationRecoveryNoStrategyFound(),\n\t\t\"NewErrorValidationVerificationNoStrategyFound\":           text.NewErrorValidationVerificationNoStrategyFound(),\n\t\t\"NewInfoSelfServiceLoginWebAuthn\":                         text.NewInfoSelfServiceLoginWebAuthn(),\n\t\t\"NewInfoRegistration\":                                     text.NewInfoRegistration(),\n\t\t\"NewInfoRegistrationWith\":                                 text.NewInfoRegistrationWith(\"{provider}\", \"{providerID}\"),\n\t\t\"NewInfoRegistrationContinue\":                             text.NewInfoRegistrationContinue(),\n\t\t\"NewInfoRegistrationBack\":                                 text.NewInfoRegistrationBack(),\n\t\t\"NewInfoSelfServiceChooseCredentials\":                     text.NewInfoSelfServiceChooseCredentials(),\n\t\t\"NewErrorValidationRegistrationFlowExpired\":               text.NewErrorValidationRegistrationFlowExpired(aSecondAgo),\n\t\t\"NewErrorValidationRecoveryFlowExpired\":                   text.NewErrorValidationRecoveryFlowExpired(aSecondAgo),\n\t\t\"NewRecoverySuccessful\":                                   text.NewRecoverySuccessful(inAMinute),\n\t\t\"NewRecoveryEmailSent\":                                    text.NewRecoveryEmailSent(),\n\t\t\"NewRecoveryEmailWithCodeSent\":                            text.NewRecoveryEmailWithCodeSent(),\n\t\t\"NewRecoveryCodeRecoverySelectAddressSent\":                text.NewRecoveryCodeRecoverySelectAddressSent(\"{masked_address}\"),\n\t\t\"NewRecoveryAskAnyRecoveryAddress\":                        text.NewRecoveryAskAnyRecoveryAddress(),\n\t\t\"NewRecoveryAskForFullAddress\":                            text.NewRecoveryAskForFullAddress(),\n\t\t\"NewRecoveryAskToChooseAddress\":                           text.NewRecoveryAskToChooseAddress(),\n\t\t\"NewRecoveryBack\":                                         text.NewRecoveryBack(),\n\t\t\"NewErrorValidationRecoveryTokenInvalidOrAlreadyUsed\":     text.NewErrorValidationRecoveryTokenInvalidOrAlreadyUsed(),\n\t\t\"NewErrorValidationRecoveryCodeInvalidOrAlreadyUsed\":      text.NewErrorValidationRecoveryCodeInvalidOrAlreadyUsed(),\n\t\t\"NewErrorValidationRecoveryRetrySuccess\":                  text.NewErrorValidationRecoveryRetrySuccess(),\n\t\t\"NewErrorValidationRecoveryStateFailure\":                  text.NewErrorValidationRecoveryStateFailure(),\n\t\t\"NewInfoNodeInputEmail\":                                   text.NewInfoNodeInputEmail(),\n\t\t\"NewInfoNodeResendOTP\":                                    text.NewInfoNodeResendOTP(),\n\t\t\"NewInfoNodeLoginAndLinkCredential\":                       text.NewInfoNodeLoginAndLinkCredential(),\n\t\t\"NewInfoNodeLabelContinue\":                                text.NewInfoNodeLabelContinue(),\n\t\t\"NewInfoSelfServiceSettingsRegisterWebAuthn\":              text.NewInfoSelfServiceSettingsRegisterWebAuthn(),\n\t\t\"NewInfoSelfServiceSettingsRegisterPasskey\":               text.NewInfoSelfServiceSettingsRegisterPasskey(),\n\t\t\"NewInfoLoginWebAuthnPasswordless\":                        text.NewInfoLoginWebAuthnPasswordless(),\n\t\t\"NewInfoSelfServiceRegistrationRegisterWebAuthn\":          text.NewInfoSelfServiceRegistrationRegisterWebAuthn(),\n\t\t\"NewInfoSelfServiceContinueLoginWebAuthn\":                 text.NewInfoSelfServiceContinueLoginWebAuthn(),\n\t\t\"NewInfoSelfServiceLoginPasskey\":                          text.NewInfoSelfServiceLoginPasskey(),\n\t\t\"NewInfoSelfServiceRegistrationRegisterPasskey\":           text.NewInfoSelfServiceRegistrationRegisterPasskey(),\n\t\t\"NewInfoSelfServiceLoginContinue\":                         text.NewInfoSelfServiceLoginContinue(),\n\t\t\"NewErrorValidationSuchNoWebAuthnUser\":                    text.NewErrorValidationSuchNoWebAuthnUser(),\n\t\t\"NewRegistrationEmailWithCodeSent\":                        text.NewRegistrationEmailWithCodeSent(),\n\t\t\"NewLoginCodeSent\":                                        text.NewLoginCodeSent(),\n\t\t\"NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed\":  text.NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed(),\n\t\t\"NewErrorValidationLoginCodeInvalidOrAlreadyUsed\":         text.NewErrorValidationLoginCodeInvalidOrAlreadyUsed(),\n\t\t\"NewErrorValidationNoCodeUser\":                            text.NewErrorValidationNoCodeUser(),\n\t\t\"NewInfoNodeLabelRegistrationCode\":                        text.NewInfoNodeLabelRegistrationCode(),\n\t\t\"NewInfoNodeLabelLoginCode\":                               text.NewInfoNodeLabelLoginCode(),\n\t\t\"NewErrorValidationLoginRetrySuccessful\":                  text.NewErrorValidationLoginRetrySuccessful(),\n\t\t\"NewErrorValidationTraitsMismatch\":                        text.NewErrorValidationTraitsMismatch(),\n\t\t\"NewInfoSelfServiceLoginCode\":                             text.NewInfoSelfServiceLoginCode(),\n\t\t\"NewErrorValidationRegistrationRetrySuccessful\":           text.NewErrorValidationRegistrationRetrySuccessful(),\n\t\t\"NewInfoSelfServiceRegistrationRegisterCode\":              text.NewInfoSelfServiceRegistrationRegisterCode(),\n\t\t\"NewErrorValidationLoginLinkedCredentialsDoNotMatch\":      text.NewErrorValidationLoginLinkedCredentialsDoNotMatch(),\n\t\t\"NewErrorValidationAddressUnknown\":                        text.NewErrorValidationAddressUnknown(),\n\t\t\"NewInfoSelfServiceLoginCodeMFA\":                          text.NewInfoSelfServiceLoginCodeMFA(),\n\t\t\"NewInfoLoginPassword\":                                    text.NewInfoLoginPassword(),\n\t\t\"NewErrorValidationAccountNotFound\":                       text.NewErrorValidationAccountNotFound(),\n\t\t\"NewInfoSelfServiceLoginAAL2CodeAddress\":                  text.NewInfoSelfServiceLoginAAL2CodeAddress(\"{channel}\", \"{address}\"),\n\t\t\"NewErrorCaptchaFailed\":                                   text.NewErrorCaptchaFailed(),\n\t\t\"NewCaptchaContainerMessage\":                              text.NewCaptchaContainerMessage(),\n\t\t\"NewErrorValidationEmail\":                                 text.NewErrorValidationEmail(\"{value}\"),\n\t\t\"NewErrorValidationPhone\":                                 text.NewErrorValidationPhone(\"{value}\"),\n\t}\n}\n\nfunc main() {\n\tif os.Args[1] == \"elements\" {\n\t\tpath, err := filepath.Abs(os.Args[2])\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(os.Stderr, \"Unable to determine absolute path for elements generation: %+v\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tif err := generateElements(path); err != nil {\n\t\t\t_, _ = fmt.Fprintf(os.Stderr, \"Unable to generate locales for elements: %+v\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\treturn\n\t}\n\n\tif err := clidoc.Generate(cmd.NewRootCmd(nil, nil), []string{filepath.Join(os.Args[2], \"cli\")}); err != nil {\n\t\t_, _ = fmt.Fprintf(os.Stderr, \"Unable to generate CLI docs: %+v\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif err := validateAllMessages(filepath.Join(os.Args[1], \"text\")); err != nil {\n\t\t_, _ = fmt.Fprintf(os.Stderr, \"Unable to validate messages: %+v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tsortedMessages := sortMessages()\n\tfor i := 1; i < len(sortedMessages); i++ {\n\t\tif sortedMessages[i].ID == sortedMessages[i-1].ID {\n\t\t\t_, _ = fmt.Fprintf(os.Stderr, \"Message ID %d is used more than once: %q %q\\n\", sortedMessages[i].ID, sortedMessages[i].Text, sortedMessages[i-1].Text)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tif err := writeMessages(filepath.Join(os.Args[2], \"concepts/ui-messages.md\"), sortedMessages); err != nil {\n\t\t_, _ = fmt.Fprintf(os.Stderr, \"Unable to generate message table: %+v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif err := writeMessagesJson(filepath.Join(os.Args[2], \"concepts/messages.json\"), sortedMessages); err != nil {\n\t\t_, _ = fmt.Fprintf(os.Stderr, \"Unable to generate messages.json: %+v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tfmt.Println(\"All files have been generated and updated.\")\n}\n\nfunc codeEncode(in interface{}) string {\n\tout, err := json.MarshalIndent(in, \"\", \"  \")\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(os.Stderr, \"Unable to encode to JSON: %+v\", err)\n\t\tos.Exit(1)\n\t}\n\n\treturn string(out)\n}\n\nfunc sortMessages() []*text.Message {\n\tvar toSort []*text.Message\n\tfor _, m := range messages {\n\t\ttoSort = append(toSort, m)\n\t}\n\n\tsort.Slice(toSort, func(i, j int) bool {\n\t\tif toSort[i].ID == toSort[j].ID {\n\t\t\treturn toSort[i].Text < toSort[j].Text\n\t\t}\n\t\treturn toSort[i].ID < toSort[j].ID\n\t})\n\n\treturn toSort\n}\n\nfunc writeMessages(path string, sortedMessages []*text.Message) error {\n\t//nolint:gosec\n\tcontent, err := os.ReadFile(path) // #nosec\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar w bytes.Buffer\n\tfor _, m := range sortedMessages {\n\t\tfmt.Fprintf(&w, `###### %s (%d)\n\n%s\n\n`, m.Text, m.ID, \"```json\\n\"+codeEncode(m)+\"\\n```\")\n\t}\n\n\tr := regexp.MustCompile(`(?s)<!-- START MESSAGE TABLE -->(.*?)<!-- END MESSAGE TABLE -->`)\n\tresult := r.ReplaceAllString(string(content), \"<!-- START MESSAGE TABLE -->\\n\"+w.String()+\"\\n<!-- END MESSAGE TABLE -->\")\n\n\tf, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600) // #nosec\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif _, err := f.WriteString(result); err != nil {\n\t\treturn err\n\t}\n\n\tif err := f.Close(); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc writeMessagesJson(path string, sortedMessages []*text.Message) error {\n\tresult := codeEncode(sortedMessages)\n\n\tf, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) // #nosec\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif _, err := f.WriteString(result); err != nil {\n\t\treturn err\n\t}\n\n\tif err := f.Close(); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc validateAllMessages(path string) error {\n\ttype message struct {\n\t\tID, Name string\n\t}\n\n\tusedIDs := make([]message, 0, len(messages))\n\tset := token.NewFileSet()\n\tpacks, err := parser.ParseDir(set, path, nil, 0)\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"unable to parse text directory\")\n\t}\n\tinfo := &types.Info{\n\t\tDefs: make(map[*ast.Ident]types.Object),\n\t}\n\n\t//nolint:staticcheck\n\tvar pack *ast.Package\n\tfor _, p := range packs {\n\t\tif p.Name == \"text\" {\n\t\t\tpack = p\n\t\t\tbreak\n\t\t}\n\t}\n\tallFiles := make([]*ast.File, 0)\n\tfor fn, f := range pack.Files {\n\t\tif strings.HasSuffix(fn, \"/id.go\") {\n\t\t\tallFiles = append(allFiles, f)\n\t\t}\n\t}\n\t_, err = (&types.Config{Importer: importer.Default()}).Check(\"text\", set, allFiles, info)\n\tif err != nil {\n\t\treturn err // type error\n\t}\n\n\tfor _, f := range pack.Files {\n\t\tfor _, d := range f.Decls {\n\t\t\tswitch decl := d.(type) {\n\t\t\tcase *ast.FuncDecl:\n\t\t\t\tif name := decl.Name.String(); decl.Name.IsExported() && strings.HasPrefix(name, \"New\") {\n\t\t\t\t\tif _, ok := messages[name]; !ok {\n\t\t\t\t\t\treturn errors.Errorf(\"expected to find message %s in the list for the documentation generation but could not\", name)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase *ast.GenDecl:\n\t\t\t\tif decl.Tok == token.CONST {\n\t\t\t\t\tfor _, spec := range decl.Specs {\n\t\t\t\t\t\tvalue := spec.(*ast.ValueSpec) // safe because decl.Tok is token.CONST\n\t\t\t\t\t\tif t, ok := value.Type.(*ast.Ident); ok {\n\t\t\t\t\t\t\tif t.Name == \"ID\" {\n\t\t\t\t\t\t\t\tfor _, name := range value.Names {\n\t\t\t\t\t\t\t\t\tc := info.ObjectOf(name)\n\t\t\t\t\t\t\t\t\tif c == nil {\n\t\t\t\t\t\t\t\t\t\treturn errors.Errorf(\"expected to find const %s in text/id.go\", name.Name)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tusedIDs = append(usedIDs, message{\n\t\t\t\t\t\t\t\t\t\tID:   c.(*types.Const).Val().ExactString(),\n\t\t\t\t\t\t\t\t\t\tName: name.Name,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}\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}\n\t\t\t}\n\t\t}\n\t}\n\n\tsort.Slice(usedIDs, func(i, j int) bool {\n\t\treturn usedIDs[i].ID < usedIDs[j].ID\n\t})\n\tfor i := 1; i < len(usedIDs); i++ {\n\t\tif usedIDs[i].ID == usedIDs[i-1].ID {\n\t\t\treturn errors.Errorf(\"message ID %s is used more than once: %s %s\", usedIDs[i].ID, usedIDs[i].Name, usedIDs[i-1].Name)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc generateElements(messageFilePath string) error {\n\t// If the file at messageFilePath does not exist, create it\n\tf, err := os.OpenFile(messageFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) // #nosec\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"unable to open or create message file at %s\", messageFilePath)\n\t}\n\tif err := f.Close(); err != nil {\n\t\treturn errors.Wrapf(err, \"unable to close message file at %s\", messageFilePath)\n\t}\n\n\tsortedMessages := sortMessages()\n\tif err := writeMessagesJson(messageFilePath, sortedMessages); err != nil {\n\t\treturn errors.Wrapf(err, \"unable to write messages json to %s\", messageFilePath)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/clidoc/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMessages(t *testing.T) {\n\trequire.NoError(t, validateAllMessages(\"../../text\"))\n}\n"
  },
  {
    "path": "cmd/courier/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/x/configx\"\n)\n\n// NewCourierCmd creates a new courier command\nfunc NewCourierCmd() *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:   \"courier\",\n\t\tShort: \"Commands related to the Ory Kratos message courier\",\n\t}\n\tconfigx.RegisterFlags(c.PersistentFlags())\n\treturn c\n}\n\nfunc RegisterCommandRecursive(parent *cobra.Command, dOpts []driver.RegistryOption) {\n\tc := NewCourierCmd()\n\tparent.AddCommand(c)\n\tc.AddCommand(NewWatchCmd(dOpts))\n}\n"
  },
  {
    "path": "cmd/courier/watch.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/urfave/negroni\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/ory/graceful\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/prometheusx\"\n\t\"github.com/ory/x/reqlog\"\n)\n\nfunc NewWatchCmd(dOpts []driver.RegistryOption) *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:   \"watch\",\n\t\tShort: \"Starts the Ory Kratos message courier\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tr, err := driver.New(cmd.Context(), cmd.ErrOrStderr(), append(dOpts, driver.WithConfigOptions(configx.WithFlags(cmd.Flags())))...)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn StartCourier(cmd.Context(), r)\n\t\t},\n\t}\n\tc.PersistentFlags().Int(\"expose-metrics-port\", 0, \"The port to expose the metrics endpoint on (not exposed by default)\")\n\treturn c\n}\n\nfunc StartCourier(ctx context.Context, r driver.Registry) error {\n\teg, ctx := errgroup.WithContext(ctx)\n\n\tif port := r.Config().CourierExposeMetricsPort(ctx); port != 0 {\n\t\teg.Go(func() error {\n\t\t\treturn ServeMetrics(ctx, r, port)\n\t\t})\n\t}\n\n\teg.Go(func() error {\n\t\treturn Watch(ctx, r)\n\t})\n\n\treturn eg.Wait()\n}\n\nfunc ServeMetrics(ctx context.Context, r driver.Registry, port int) error {\n\tcfg := r.Config().ServeAdmin(ctx)\n\tl := r.Logger()\n\tn := negroni.New()\n\n\trouter := http.NewServeMux()\n\n\trouter.Handle(prometheusx.MetricsPrometheusPath, promhttp.Handler())\n\tn.Use(reqlog.NewMiddlewareFromLogger(l, \"admin#\"+cfg.BaseURL.String()))\n\n\tn.UseHandler(router)\n\n\tvar handler http.Handler = n\n\n\t//#nosec G112 -- the correct settings are set by graceful.WithDefaults\n\tserver := graceful.WithDefaults(&http.Server{\n\t\tAddr:    configx.GetAddress(cfg.Host, port),\n\t\tHandler: handler,\n\t})\n\n\tl.WithField(\"addr\", server.Addr).Info(\"Starting the metrics httpd\")\n\tif err := graceful.GracefulContext(ctx, server.ListenAndServe, server.Shutdown); err != nil {\n\t\tl.Errorln(\"Failed to gracefully shutdown metrics httpd\")\n\t\treturn err\n\t}\n\tl.Println(\"Metrics httpd was shutdown gracefully\")\n\treturn nil\n}\n\nfunc Watch(ctx context.Context, r driver.Registry) error {\n\tctx, cancel := context.WithCancel(ctx)\n\n\tr.Logger().Println(\"Courier worker started.\")\n\tif err := graceful.Graceful(func() error {\n\t\tc, err := r.Courier(ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn c.Work(ctx)\n\t}, func(_ context.Context) error {\n\t\tcancel()\n\t\treturn nil\n\t}); err != nil {\n\t\tr.Logger().WithError(err).Error(\"Failed to run courier worker.\")\n\t\treturn err\n\t}\n\n\tr.Logger().Println(\"Courier worker was shutdown gracefully.\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/courier/watch_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/phayes/freeport\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/x/configx\"\n)\n\nfunc TestStartCourier(t *testing.T) {\n\tt.Run(\"case=without metrics\", func(t *testing.T) {\n\t\t_, r := pkg.NewFastRegistryWithMocks(t)\n\t\tgo func() { _ = StartCourier(t.Context(), r) }()\n\t\ttime.Sleep(time.Second)\n\t\trequire.Equal(t, r.Config().CourierExposeMetricsPort(t.Context()), 0)\n\t})\n\n\tt.Run(\"case=with metrics\", func(t *testing.T) {\n\t\tport, err := freeport.GetFreePort()\n\t\trequire.NoError(t, err)\n\t\t_, r := pkg.NewFastRegistryWithMocks(t, configx.WithValue(\"expose-metrics-port\", port))\n\t\tgo func() { _ = StartCourier(t.Context(), r) }()\n\t\ttime.Sleep(time.Second)\n\t\tres, err := http.Get(fmt.Sprintf(\"http://127.0.0.1:%d/metrics/prometheus\", port))\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, 200, res.StatusCode)\n\t})\n}\n"
  },
  {
    "path": "cmd/daemon/serve.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage daemon\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/rs/cors\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/urfave/negroni\"\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/ory/analytics-go/v5\"\n\t\"github.com/ory/graceful\"\n\t\"github.com/ory/kratos/cmd/courier\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/logout\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/healthx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/metricsx\"\n\t\"github.com/ory/x/networkx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/prometheusx\"\n\t\"github.com/ory/x/reqlog\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc init() {\n\tgraceful.DefaultShutdownTimeout = 120 * time.Second\n}\n\nvar httpMetrics = prometheusx.NewHTTPMetrics(\"kratos\", prometheusx.HTTPPrefix, config.Version, config.Commit, config.Date)\n\nfunc servePublic(ctx context.Context, r *driver.RegistryDefault, cmd *cobra.Command) (func() error, error) {\n\tcfg := r.Config().ServePublic(ctx)\n\tl := r.Logger()\n\tn := negroni.New()\n\n\tfor _, mw := range r.HTTPMiddlewares() {\n\t\tn.Use(mw)\n\t}\n\n\tpublicLogger := reqlog.NewMiddlewareFromLogger(l, \"public#\"+cfg.BaseURL.String())\n\n\tif cfg.RequestLog.DisableHealth {\n\t\tpublicLogger.ExcludePaths(healthx.AliveCheckPath, healthx.ReadyCheckPath)\n\t}\n\n\tn.UseFunc(semconv.Middleware)\n\tn.Use(publicLogger)\n\tn.Use(x.HTTPLoaderContextMiddleware(r))\n\tn.UseFunc(httprouterx.NoCacheNegroni)\n\tn.Use(sqa(ctx, cmd, r))\n\n\trouter := httprouterx.NewRouterPublic(httpMetrics)\n\tcsrf := nosurfx.NewCSRFHandler(otelx.SpanNameRecorderMiddleware(router), r)\n\n\t// we need to always load the CORS middleware even if it is disabled, to allow hot-enabling CORS\n\tn.UseFunc(func(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {\n\t\tcfg, enabled := r.Config().CORSPublic(req.Context())\n\t\tif !enabled {\n\t\t\tnext(w, req)\n\t\t\treturn\n\t\t}\n\t\tcors.New(cfg).ServeHTTP(w, req, next)\n\t})\n\n\tr.WithCSRFHandler(csrf)\n\tn.UseHandler(r.CSRFHandler())\n\n\t// Disable CSRF for these endpoints\n\tcsrf.DisablePath(healthx.AliveCheckPath)\n\tcsrf.DisablePath(healthx.ReadyCheckPath)\n\tcsrf.DisablePath(healthx.VersionPath)\n\tcsrf.DisablePath(prometheusx.MetricsPrometheusPath)\n\n\tr.RegisterPublicRoutes(ctx, router)\n\n\tvar handler http.Handler = n\n\tif tracer := r.Tracer(ctx); tracer.IsLoaded() {\n\t\thandler = otelx.NewMiddleware(handler, \"servePublic\",\n\t\t\totelhttp.WithTracerProvider(tracer.Provider()),\n\t\t)\n\t}\n\n\thandler = http.MaxBytesHandler(handler, 5*1024*1024 /* 5 MB */) // Important: this must be the outermost handler or our tracing breaks\n\n\tcertFunc, err := cfg.TLS.GetCertFunc(ctx, l, \"public\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t//#nosec G112 -- the correct settings are set by graceful.WithDefaults\n\tserver := graceful.WithDefaults(&http.Server{\n\t\tHandler:           handler,\n\t\tTLSConfig:         &tls.Config{GetCertificate: certFunc, MinVersion: tls.VersionTLS12},\n\t\tReadHeaderTimeout: 5 * time.Second,\n\t\tReadTimeout:       15 * time.Second,\n\t\tWriteTimeout:      60 * time.Second,\n\t\tIdleTimeout:       120 * time.Second,\n\t})\n\taddr := cfg.GetAddress()\n\n\treturn func() error {\n\t\tl.WithField(\"addr\", addr).Info(\"Starting the public httpd\")\n\t\tif err := graceful.GracefulContext(ctx, func() error {\n\t\t\tlistener, err := networkx.MakeListener(addr, &cfg.Socket)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif certFunc == nil {\n\t\t\t\treturn server.Serve(listener)\n\t\t\t}\n\t\t\treturn server.ServeTLS(listener, \"\", \"\")\n\t\t}, server.Shutdown); err != nil {\n\t\t\tif !errors.Is(err, context.Canceled) {\n\t\t\t\tl.Errorf(\"Failed to gracefully shutdown public httpd: %s\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tl.Println(\"Public httpd was shutdown gracefully\")\n\t\treturn nil\n\t}, nil\n}\n\nfunc serveAdmin(ctx context.Context, r *driver.RegistryDefault, cmd *cobra.Command) (func() error, error) {\n\tcfg := r.Config().ServeAdmin(ctx)\n\tl := r.Logger()\n\tn := negroni.New()\n\n\tfor _, mw := range r.HTTPMiddlewares() {\n\t\tn.Use(mw)\n\t}\n\n\tadminLogger := reqlog.NewMiddlewareFromLogger(l, \"admin#\"+cfg.BaseURL.String())\n\n\tif cfg.RequestLog.DisableHealth {\n\t\tadminLogger.ExcludePaths(\n\t\t\thttprouterx.AdminPrefix+healthx.AliveCheckPath,\n\t\t\thttprouterx.AdminPrefix+healthx.ReadyCheckPath,\n\t\t\thttprouterx.AdminPrefix+prometheusx.MetricsPrometheusPath,\n\t\t)\n\t}\n\tn.UseFunc(semconv.Middleware)\n\tn.Use(adminLogger)\n\tn.UseFunc(httprouterx.AddAdminPrefixIfNotPresentNegroni)\n\tn.UseFunc(httprouterx.NoCacheNegroni)\n\tn.Use(x.HTTPLoaderContextMiddleware(r))\n\tn.Use(sqa(ctx, cmd, r))\n\n\trouter := httprouterx.NewRouterAdminWithPrefix(httpMetrics)\n\tr.RegisterAdminRoutes(ctx, router)\n\n\tn.UseHandler(router)\n\n\tn.UseFunc(otelx.SpanNameRecorderNegroniFunc)\n\n\tvar handler http.Handler = n\n\tif tracer := r.Tracer(ctx); tracer.IsLoaded() {\n\t\thandler = otelx.NewMiddleware(handler, \"serveAdmin\",\n\t\t\totelhttp.WithTracerProvider(tracer.Provider()),\n\t\t)\n\t}\n\n\thandler = http.MaxBytesHandler(handler, 5*1024*1024 /* 5 MB */) // Important: this must be the outermost handler or our tracing breaks\n\n\tcertFunc, err := cfg.TLS.GetCertFunc(ctx, l, \"admin\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t//#nosec G112 -- the correct settings are set by graceful.WithDefaults\n\tserver := graceful.WithDefaults(&http.Server{\n\t\tHandler:           handler,\n\t\tTLSConfig:         &tls.Config{GetCertificate: certFunc, MinVersion: tls.VersionTLS12},\n\t\tReadHeaderTimeout: 5 * time.Second,\n\t\tReadTimeout:       30 * time.Second,\n\t\tWriteTimeout:      120 * time.Second,\n\t\tIdleTimeout:       600 * time.Second,\n\t})\n\n\taddr := cfg.GetAddress()\n\n\treturn func() error {\n\t\tl.WithField(\"addr\", addr).Info(\"Starting the admin httpd\")\n\t\tif err := graceful.GracefulContext(ctx, func() error {\n\t\t\tlistener, err := networkx.MakeListener(addr, &cfg.Socket)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif certFunc == nil {\n\t\t\t\treturn server.Serve(listener)\n\t\t\t}\n\t\t\treturn server.ServeTLS(listener, \"\", \"\")\n\t\t}, server.Shutdown); err != nil {\n\t\t\tif !errors.Is(err, context.Canceled) {\n\t\t\t\tl.Errorf(\"Failed to gracefully shutdown admin httpd: %s\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tl.Println(\"Admin httpd was shutdown gracefully\")\n\t\treturn nil\n\t}, nil\n}\n\nfunc sqa(ctx context.Context, cmd *cobra.Command, d driver.Registry) *metricsx.Service {\n\turls := []string{\n\t\td.Config().ServePublic(ctx).BaseURL.Host,\n\t\td.Config().ServeAdmin(ctx).BaseURL.Host,\n\t\td.Config().SelfServiceFlowLoginUI(ctx).Host,\n\t\td.Config().SelfServiceFlowSettingsUI(ctx).Host,\n\t\td.Config().SelfServiceFlowErrorURL(ctx).Host,\n\t\td.Config().SelfServiceFlowRegistrationUI(ctx).Host,\n\t\td.Config().SelfServiceFlowRecoveryUI(ctx).Host,\n\t\td.Config().ServePublic(ctx).Host,\n\t\td.Config().ServeAdmin(ctx).Host,\n\t}\n\tif c, y := d.Config().CORSPublic(ctx); y {\n\t\turls = append(urls, c.AllowedOrigins...)\n\t}\n\thost := urlx.ExtractPublicAddress(urls...)\n\n\t// Creates only ones\n\t// instance\n\treturn metricsx.New(\n\t\tcmd,\n\t\td.Logger(),\n\t\td.Config().GetProvider(ctx),\n\t\t&metricsx.Options{\n\t\t\tService:       \"kratos\",\n\t\t\tDeploymentId:  metricsx.Hash(d.Persister().NetworkID(ctx).String()),\n\t\t\tDBDialect:     d.Persister().GetConnection(ctx).Dialect.Details().Dialect,\n\t\t\tIsDevelopment: d.Config().IsInsecureDevMode(ctx),\n\t\t\tWriteKey:      \"qQlI6q8Q4WvkzTjKQSor4sHYOikHIvvi\",\n\t\t\tWhitelistedPaths: []string{\n\t\t\t\t\"/\",\n\t\t\t\thealthx.AliveCheckPath,\n\t\t\t\thealthx.ReadyCheckPath,\n\t\t\t\thealthx.VersionPath,\n\n\t\t\t\toidc.RouteBase,\n\n\t\t\t\tlogin.RouteInitBrowserFlow,\n\t\t\t\tlogin.RouteInitAPIFlow,\n\t\t\t\tlogin.RouteGetFlow,\n\t\t\t\tlogin.RouteSubmitFlow,\n\n\t\t\t\tlogout.RouteInitBrowserFlow,\n\t\t\t\tlogout.RouteSubmitFlow,\n\t\t\t\tlogout.RouteAPIFlow,\n\n\t\t\t\tregistration.RouteInitBrowserFlow,\n\t\t\t\tregistration.RouteInitAPIFlow,\n\t\t\t\tregistration.RouteGetFlow,\n\t\t\t\tregistration.RouteSubmitFlow,\n\n\t\t\t\tsession.RouteWhoami,\n\n\t\t\t\thttprouterx.AdminPrefix + \"/\" + schema.SchemasPath,\n\t\t\t\thttprouterx.AdminPrefix + identity.RouteCollection,\n\n\t\t\t\tsettings.RouteInitBrowserFlow,\n\t\t\t\tsettings.RouteInitAPIFlow,\n\t\t\t\tsettings.RouteGetFlow,\n\t\t\t\tsettings.RouteSubmitFlow,\n\n\t\t\t\tverification.RouteInitAPIFlow,\n\t\t\t\tverification.RouteInitBrowserFlow,\n\t\t\t\tverification.RouteGetFlow,\n\t\t\t\tverification.RouteSubmitFlow,\n\n\t\t\t\trecovery.RouteInitAPIFlow,\n\t\t\t\trecovery.RouteInitBrowserFlow,\n\t\t\t\trecovery.RouteGetFlow,\n\t\t\t\trecovery.RouteSubmitFlow,\n\n\t\t\t\tlink.RouteAdminCreateRecoveryLink,\n\n\t\t\t\terrorx.RouteGet,\n\t\t\t\tprometheusx.MetricsPrometheusPath,\n\t\t\t},\n\t\t\tBuildVersion: config.Version,\n\t\t\tBuildHash:    config.Commit,\n\t\t\tBuildTime:    config.Date,\n\t\t\tConfig: &analytics.Config{\n\t\t\t\tEndpoint:             \"https://sqa.ory.sh\",\n\t\t\t\tGzipCompressionLevel: 6,\n\t\t\t\tBatchMaxSize:         500 * 1000,\n\t\t\t\tBatchSize:            1000,\n\t\t\t\tInterval:             time.Hour * 6,\n\t\t\t},\n\t\t\tHostname: host,\n\t\t},\n\t)\n}\n\nfunc courierTask(ctx context.Context, d driver.Registry) func() error {\n\treturn func() error {\n\t\tif d.Config().IsBackgroundCourierEnabled(ctx) {\n\t\t\treturn courier.Watch(ctx, d)\n\t\t}\n\t\treturn nil\n\t}\n}\n\nfunc ServeAll(d *driver.RegistryDefault) func(cmd *cobra.Command, args []string) error {\n\treturn func(cmd *cobra.Command, _ []string) error {\n\t\tctx := cmd.Context()\n\t\tg, ctx := errgroup.WithContext(ctx)\n\t\tcmd.SetContext(ctx)\n\n\t\t// construct all tasks upfront to avoid race conditions\n\t\tpublicSrv, err := servePublic(ctx, d, cmd)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tadminSrv, err := serveAdmin(ctx, d, cmd)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\ttasks := []func() error{\n\t\t\tpublicSrv,\n\t\t\tadminSrv,\n\t\t\tcourierTask(ctx, d),\n\t\t}\n\t\tfor _, task := range tasks {\n\t\t\tg.Go(task)\n\t\t}\n\t\treturn g.Wait()\n\t}\n}\n"
  },
  {
    "path": "cmd/daemon/serve_test.go",
    "content": "// Copyright © 2026 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage daemon\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/phayes/freeport\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestMetricsRouterPaths(t *testing.T) {\n\t// TODO refactor this test to be parallelizable once we rewrite the server setup to be properly testable\n\tports, err := freeport.GetFreePorts(2)\n\trequire.NoError(t, err)\n\tadminPort, publicPort := ports[0], ports[1]\n\tcmd := &cobra.Command{}\n\tcmd.Flags().Bool(\"sqa-opt-out\", true, \"\")\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]any{\n\t\t\"serve.admin.port\":  adminPort,\n\t\t\"serve.public.port\": publicPort,\n\t}))\n\n\tstartAdmin, err := serveAdmin(t.Context(), reg, cmd)\n\trequire.NoError(t, err)\n\tstartPublic, err := servePublic(t.Context(), reg, cmd)\n\trequire.NoError(t, err)\n\teg, _ := errgroup.WithContext(t.Context())\n\teg.Go(startAdmin)\n\teg.Go(startPublic)\n\n\trequire.EventuallyWithT(t, func(t *assert.CollectT) {\n\t\tresp, err := http.Get(fmt.Sprintf(\"http://127.0.0.1:%d/health/ready\", publicPort))\n\t\trequire.NoError(t, err)\n\t\tbody, err := io.ReadAll(resp.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Equalf(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t}, 2*time.Second, 10*time.Millisecond)\n\n\t// Make some requests that should be recorded in the metrics\n\treq, _ := http.NewRequest(http.MethodDelete, fmt.Sprintf(\"http://127.0.0.1:%d/sessions/session-id\", publicPort), nil)\n\t_, err = http.DefaultClient.Do(req)\n\trequire.NoError(t, err)\n\t_, err = http.Get(fmt.Sprintf(\"http://127.0.0.1:%d/admin/identities/some-id/sessions\", adminPort))\n\trequire.NoError(t, err)\n\n\tres, err := http.Get(fmt.Sprintf(\"http://127.0.0.1:%d/admin/metrics/prometheus\", adminPort))\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, http.StatusOK, res.StatusCode)\n\trespBody, err := io.ReadAll(res.Body)\n\tbody := string(respBody)\n\n\trequire.NoError(t, err)\n\tassert.Contains(t, body, `endpoint=\"/sessions/{param}\"`)\n\tassert.Contains(t, body, `endpoint=\"/admin/identities/{param}/sessions\"`)\n}\n"
  },
  {
    "path": "cmd/hashers/argon2/calibrate.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage argon2\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/x/cmdx\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/inhies/go-bytesize\"\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n)\n\nconst (\n\tFlagStartMemory     = \"start-memory\"\n\tFlagMaxMemory       = \"max-memory\"\n\tFlagAdjustMemory    = \"adjust-memory-by\"\n\tFlagStartIterations = \"start-iterations\"\n\tFlagMaxConcurrent   = \"max-concurrent\"\n\n\tFlagRuns = \"probe-runs\"\n)\n\ntype (\n\tcolorWriter struct {\n\t\tc *color.Color\n\t\tw io.Writer\n\t}\n\tloadResult struct {\n\t\tr *resultTable\n\t\tv *config.Argon2\n\t}\n\tloadResults []*loadResult\n)\n\nvar _ cmdx.Table = loadResults{}\n\nfunc (l loadResults) Header() []string {\n\treturn append((&resultTable{}).Header(), \"MEMOry PARAM\", \"ITERATIONS PARAM\")\n}\n\nfunc (l loadResults) Table() [][]string {\n\tt := make([][]string, len(l))\n\n\tfor i, r := range l {\n\t\tt[i] = append(r.r.Columns(), fmt.Sprintf(\"%d\", r.v.Memory), fmt.Sprintf(\"%d\", r.v.Iterations))\n\t}\n\n\treturn t\n}\n\nfunc (l loadResults) Interface() interface{} {\n\treturn l\n}\n\nfunc (l loadResults) Len() int {\n\treturn len(l)\n}\n\nfunc (c *colorWriter) Write(o []byte) (int, error) {\n\treturn c.c.Fprintf(c.w, \"%s\", o)\n}\n\nfunc newCalibrateCmd() *cobra.Command {\n\tvar (\n\t\tmaxMemory, adjustMemory bytesize.ByteSize = 0, 512 * bytesize.MB\n\t\truns                    int\n\t)\n\n\tflagConfig := &argon2Config{\n\t\tlocalConfig: config.Argon2{},\n\t}\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"calibrate <requests-per-minute>\",\n\t\tArgs:  cobra.ExactArgs(1),\n\t\tShort: \"Computes Optimal Argon2 Parameters\",\n\t\tLong: `This command helps you calibrate the configuration parameters for Argon2. Password hashing is a trade-off between security, resource consumption, and user experience. Resource consumption should not be too high and the login should not take too long.\n\nWe recommend that the login process takes between half a second and one second for password hashing, giving a good balance between security and user experience.\n\nPlease note that the values depend on the machine you run the hashing on. If you have RAM constraints, please set the memory dedicated to Ory Kratos to avoid out of memory panics.`,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tprogressPrinter := cmdx.NewLoudErrPrinter(cmd)\n\t\t\tresultPrinter := cmdx.NewLoudPrinter(cmd, &colorWriter{c: color.New(color.FgGreen), w: cmd.ErrOrStderr()})\n\n\t\t\tconf, err := configProvider(cmd, flagConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// always take start flags, or their default\n\t\t\tconf.localConfig.Memory = flagConfig.localConfig.Memory\n\t\t\tconf.localConfig.Iterations = flagConfig.localConfig.Iterations\n\n\t\t\tdesiredDuration := conf.localConfig.ExpectedDuration\n\n\t\t\treqPerMin, err := strconv.ParseInt(args[0], 0, 0)\n\t\t\tif err != nil {\n\t\t\t\t// we want the error and usage string printed so just return\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\thasher := hash.NewHasherArgon2(conf)\n\n\t\t\tvar currentDuration time.Duration\n\n\t\t\t_, _ = progressPrinter.Printf(\"Increasing memory to get over %s:\\n\", desiredDuration)\n\n\t\t\tfor {\n\t\t\t\tif maxMemory != 0 && conf.localConfig.Memory > maxMemory {\n\t\t\t\t\t// don't further increase memory\n\t\t\t\t\t_, _ = progressPrinter.Println(\"  ouch, hit the memory limit there\")\n\t\t\t\t\tconf.localConfig.Memory = maxMemory\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tcurrentDuration, err = probe(cmd, hasher, runs, progressPrinter)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\t_, _ = progressPrinter.Printf(\"  took %s with %s of memory\\n\", currentDuration, conf.localConfig.Memory)\n\n\t\t\t\tif currentDuration > desiredDuration {\n\t\t\t\t\tif conf.localConfig.Memory <= adjustMemory {\n\t\t\t\t\t\t// adjusting the memory would now result in <= 0B\n\t\t\t\t\t\tadjustMemory = adjustMemory >> 1\n\t\t\t\t\t}\n\t\t\t\t\tconf.localConfig.Memory -= adjustMemory\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\t// adjust config\n\t\t\t\tconf.localConfig.Memory += adjustMemory\n\t\t\t}\n\n\t\t\t_, _ = progressPrinter.Printf(\"Decreasing memory to get under %s:\\n\", desiredDuration)\n\n\t\t\tfor {\n\t\t\t\tcurrentDuration, err = probe(cmd, hasher, runs, progressPrinter)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\t_, _ = progressPrinter.Printf(\"  took %s with %s of memory\\n\", currentDuration, conf.localConfig.Memory)\n\n\t\t\t\tif currentDuration < desiredDuration {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tfor conf.localConfig.Memory <= adjustMemory {\n\t\t\t\t\t// adjusting the memory would now result in <= 0B\n\t\t\t\t\tadjustMemory = adjustMemory >> 1\n\t\t\t\t}\n\n\t\t\t\t// adjust config\n\t\t\t\tconf.localConfig.Memory -= adjustMemory\n\t\t\t}\n\n\t\t\t_, _ = resultPrinter.Printf(\"Settled on %s of memory.\\n\", conf.localConfig.Memory)\n\t\t\t_, _ = progressPrinter.Printf(\"Increasing iterations to get over %s:\\n\", desiredDuration)\n\n\t\t\tfor {\n\t\t\t\tcurrentDuration, err = probe(cmd, hasher, runs, progressPrinter)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\t_, _ = progressPrinter.Printf(\"  took %s with %d iterations\\n\", currentDuration, conf.localConfig.Iterations)\n\n\t\t\t\tif currentDuration > desiredDuration {\n\t\t\t\t\tif conf.localConfig.Iterations > 1 {\n\t\t\t\t\t\tconf.localConfig.Iterations -= 1\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\t// adjust config\n\t\t\t\tconf.localConfig.Iterations += 1\n\t\t\t}\n\n\t\t\t_, _ = progressPrinter.Printf(\"Decreasing iterations to get under %s:\\n\", desiredDuration)\n\n\t\t\tfor {\n\t\t\t\tcurrentDuration, err = probe(cmd, hasher, runs, progressPrinter)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\t_, _ = progressPrinter.Printf(\"  took %s with %d iterations\\n\", currentDuration, conf.localConfig.Iterations)\n\n\t\t\t\t// break also when iterations is 1; this catches the case where 1 was only slightly under the desired time and took longer a bit longer on another run\n\t\t\t\tif currentDuration < desiredDuration || conf.localConfig.Iterations == 1 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\t// adjust config\n\t\t\t\tconf.localConfig.Iterations -= 1\n\t\t\t}\n\t\t\t_, _ = resultPrinter.Printf(\"Settled on %d iterations.\\n\", conf.localConfig.Iterations)\n\n\t\t\tresults := make(loadResults, 5)\n\t\t\tfor i := 0; i < len(results); i++ {\n\t\t\t\t_, _ = progressPrinter.Printf(\"\\nRunning load test to simulate %d login requests per minute.\\n\", reqPerMin)\n\n\t\t\t\tres, err := runLoadTest(cmd, conf, int(reqPerMin))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tcv, _ := conf.HasherArgon2()\n\t\t\t\tccv := *cv\n\t\t\t\tresults[i] = &loadResult{r: res, v: &ccv}\n\n\t\t\t\t_, _ = progressPrinter.Println(\"The load test result is:\")\n\t\t\t\t_, _ = progressPrinter.Println()\n\t\t\t\tcmdx.PrintRow(cmd, res)\n\n\t\t\t\tswitch {\n\t\t\t\t// too fast\n\t\t\t\tcase res.MedianTime < conf.localConfig.ExpectedDuration:\n\t\t\t\t\t_, _ = progressPrinter.Printf(\"The median was %s under the minimal duration of %s, going to increase the hash cost.\\n\", conf.localConfig.ExpectedDuration-res.MedianTime, conf.localConfig.ExpectedDuration)\n\n\t\t\t\t\t// try to increase memory first\n\t\t\t\t\tif res.MaxMem+64*bytesize.MB < maxMemory {\n\t\t\t\t\t\t// only small amounts of memory are sensible as we already benchmarked a single, non-concurrent request\n\t\t\t\t\t\tconf.localConfig.Memory += 64 * bytesize.MB\n\t\t\t\t\t\t_, _ = progressPrinter.Printf(\"Increasing memory to %s\\n\", conf.localConfig.Memory)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// increasing memory is not allowed by maxMemory, therefore increase CPU load\n\t\t\t\t\t\tconf.localConfig.Iterations++\n\t\t\t\t\t\t_, _ = progressPrinter.Printf(\"Increasing iterations to %d\\n\", conf.localConfig.Iterations)\n\t\t\t\t\t}\n\t\t\t\t// too much memory\n\t\t\t\tcase res.MaxMem > conf.localConfig.DedicatedMemory:\n\t\t\t\t\t_, _ = progressPrinter.Printf(\"The required memory was %s more than the maximum allowed of %s.\\n\", res.MaxMem-maxMemory, conf.localConfig.DedicatedMemory)\n\n\t\t\t\t\t//nolint:gosec // disable G115\n\t\t\t\t\tconf.localConfig.Memory -= (res.MaxMem - conf.localConfig.DedicatedMemory) / bytesize.ByteSize(reqPerMin)\n\t\t\t\t\t_, _ = progressPrinter.Printf(\"Decreasing memory to %s\\n\", conf.localConfig.Memory)\n\t\t\t\t// too slow\n\t\t\t\tcase res.MaxTime > conf.localConfig.ExpectedDeviation+conf.localConfig.ExpectedDuration:\n\t\t\t\t\t_, _ = progressPrinter.Printf(\"The longest request took %s longer than the longest acceptable time of %s, going to decrease the hash cost.\\n\", res.MaxTime-conf.localConfig.ExpectedDeviation+conf.localConfig.ExpectedDuration, conf.localConfig.ExpectedDeviation+conf.localConfig.ExpectedDuration)\n\n\t\t\t\t\t// try to decrease iterations first\n\t\t\t\t\tif conf.localConfig.Iterations > 1 {\n\t\t\t\t\t\tconf.localConfig.Iterations--\n\t\t\t\t\t\t_, _ = progressPrinter.Printf(\"Decreasing iterations to %d\\n\", conf.localConfig.Iterations)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// decreasing iterations is not possible anymore, decreasing memory\n\t\t\t\t\t\t// only small amounts of memory are sensible as we already benchmarked a single, non-concurrent request\n\t\t\t\t\t\tconf.localConfig.Memory -= 64 * bytesize.MB\n\t\t\t\t\t\t_, _ = progressPrinter.Printf(\"Decreasing memory to %s\\n\", conf.localConfig.Memory)\n\t\t\t\t\t}\n\t\t\t\t// too high deviation\n\t\t\t\tcase res.StdDev > conf.localConfig.ExpectedDeviation:\n\t\t\t\t\t_, _ = progressPrinter.Printf(\"The deviation was %s more than the expected deviation of %s.\\n\", res.StdDev-conf.localConfig.ExpectedDeviation, conf.localConfig.ExpectedDeviation)\n\n\t\t\t\t\t// try to decrease iterations first\n\t\t\t\t\tif conf.localConfig.Iterations > 1 {\n\t\t\t\t\t\tconf.localConfig.Iterations--\n\t\t\t\t\t\t_, _ = progressPrinter.Printf(\"Decreasing iterations to %d\\n\", conf.localConfig.Iterations)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// decreasing iterations is not possible anymore, decreasing memory\n\t\t\t\t\t\t// only small amounts of memory are sensible as we already benchmarked a single, non-concurrent request\n\t\t\t\t\t\tconf.localConfig.Memory -= 64 * bytesize.MB\n\t\t\t\t\t\t_, _ = progressPrinter.Printf(\"Decreasing memory to %s\\n\", conf.localConfig.Memory)\n\t\t\t\t\t}\n\t\t\t\t// all values seem reasonable\n\t\t\t\tdefault:\n\t\t\t\t\t_, _ = progressPrinter.Println(\"These values look good to me.\")\n\t\t\t\t\t_, _ = progressPrinter.Println()\n\t\t\t\t\tcmdx.PrintRow(cmd, conf)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"Could not automatically determine good parameters. Have a look at all the measurements taken and select acceptable values yourself. Have a look in the docs for more information: https://www.ory.sh/kratos/docs/debug/performance-out-of-memory-password-hashing-argon2\")\n\t\t\tcmdx.PrintTable(cmd, results)\n\t\t\treturn nil\n\t\t},\n\t}\n\n\tflags := cmd.Flags()\n\n\tflags.IntVarP(&runs, FlagRuns, \"r\", 2, \"Runs per probe, median of all runs is taken as the result.\")\n\n\tflags.VarP(&flagConfig.localConfig.Memory, FlagStartMemory, \"m\", \"Amount of memory to start probing at.\")\n\tflags.Var(&maxMemory, FlagMaxMemory, \"Maximum memory allowed (0 means no limit).\")\n\tflags.Var(&adjustMemory, FlagAdjustMemory, \"Amount by which the memory is adjusted in every step while probing.\")\n\n\tflags.Uint32VarP(&flagConfig.localConfig.Iterations, FlagStartIterations, \"i\", 1, \"Number of iterations to start probing at.\")\n\n\tflags.Uint8(FlagMaxConcurrent, 16, \"Maximum number of concurrent hashing operations.\")\n\n\tregisterArgon2ConstantConfigFlags(flags, flagConfig)\n\tcmdx.RegisterFormatFlags(flags)\n\tconfigx.RegisterFlags(flags)\n\n\treturn cmd\n}\n\nfunc probe(cmd *cobra.Command, hasher hash.Hasher, runs int, progressPrinter *cmdx.ConditionalPrinter) (time.Duration, error) {\n\t// force GC at the start of the experiment\n\truntime.GC()\n\n\tstart := time.Now()\n\n\tvar mid time.Time\n\tfor i := 0; i < runs; i++ {\n\t\tmid = time.Now()\n\t\t_, err := hasher.Generate(cmd.Context(), []byte(\"password\"))\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not generate a hash: %s\\n\", err)\n\t\t\treturn 0, cmdx.FailSilently(cmd)\n\t\t}\n\n\t\t_, _ = progressPrinter.Printf(\"    took %s in try %d\\n\", time.Since(mid), i)\n\t}\n\treturn time.Duration(int64(time.Since(start)) / int64(runs)), nil\n}\n"
  },
  {
    "path": "cmd/hashers/argon2/hash.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage argon2\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/flagx\"\n)\n\nconst (\n\tFlagParallel = \"parallel\"\n)\n\nfunc newHashCmd() *cobra.Command {\n\tflagConfig := &argon2Config{}\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"hash <password1> [<password2> ...]\",\n\t\tShort: \"Hash a list of passwords for benchmarking the hashing parameters\",\n\t\tArgs:  cobra.MinimumNArgs(1),\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tconf, err := configProvider(cmd, flagConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tflagConfig.ctx = cmd.Context()\n\n\t\t\thasher := hash.NewHasherArgon2(conf)\n\t\t\thashes := make([][]byte, len(args))\n\t\t\terrs := make(chan error, len(args))\n\n\t\t\tstart := time.Now()\n\n\t\t\tfor i, pw := range args {\n\t\t\t\tgo func(i int, pw string) {\n\t\t\t\t\tstart := time.Now()\n\t\t\t\t\th, err := hasher.Generate(cmd.Context(), []byte(pw))\n\t\t\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"password %d: %s\\n\", i, time.Since(start))\n\n\t\t\t\t\thashes[i] = h\n\t\t\t\t\terrs <- err\n\t\t\t\t}(i, pw)\n\n\t\t\t\tif !flagx.MustGetBool(cmd, FlagParallel) {\n\t\t\t\t\tif err := <-errs; err != nil {\n\t\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not generate hash: %s\\n\", err.Error())\n\t\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif flagx.MustGetBool(cmd, FlagParallel) {\n\t\t\t\tfor i := 0; i < len(args); i++ {\n\t\t\t\t\tif err := <-errs; err != nil {\n\t\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not generate hash: %s\\n\", err.Error())\n\t\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"total: %s\\n\", time.Since(start))\n\t\t\treturn nil\n\t\t},\n\t}\n\n\tflags := cmd.Flags()\n\n\tflags.Bool(FlagParallel, false, \"Run all hashing operations in parallel.\")\n\n\tregisterArgon2ConfigFlags(flags, flagConfig)\n\tconfigx.RegisterFlags(flags)\n\n\treturn cmd\n}\n"
  },
  {
    "path": "cmd/hashers/argon2/loadtest.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage argon2\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/ory/x/flagx\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/inhies/go-bytesize\"\n\t\"github.com/montanaflynn/stats\"\n\t\"github.com/spf13/cobra\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/configx\"\n)\n\ntype resultTable struct {\n\tTotalTime  time.Duration     `json:\"total_time\"`\n\tMedianTime time.Duration     `json:\"median_request_time\"`\n\tStdDev     time.Duration     `json:\"std_deviation\"`\n\tMinTime    time.Duration     `json:\"min_request_time\"`\n\tMaxTime    time.Duration     `json:\"max_request_time\"`\n\tMaxMem     bytesize.ByteSize `json:\"mem_used\"`\n}\n\nvar (\n\tErrSampleTimeExceeded        = fmt.Errorf(\"the sample time was exceeded\")\n\tErrMemoryConsumptionExceeded = fmt.Errorf(\"the memory consumption was exceeded\")\n\n\t_ cmdx.TableRow = &resultTable{}\n)\n\nfunc (r *resultTable) Header() []string {\n\treturn []string{\"TOTAL SAMPLE TIME\", \"MEDIAN REQUEST TIME\", \"STANDARD DEVIATION\", \"MIN REQUEST TIME\", \"MAX REQUEST TIME\", \"MEMOry USED\"}\n}\n\nfunc (r *resultTable) Columns() []string {\n\treturn []string{\n\t\tr.TotalTime.String(),\n\t\tr.MedianTime.String(),\n\t\tr.StdDev.String(),\n\t\tr.MinTime.String(),\n\t\tr.MaxTime.String(),\n\t\tr.MaxMem.String(),\n\t}\n}\n\nfunc (r *resultTable) Interface() interface{} {\n\treturn r\n}\n\nfunc newLoadTestCmd() *cobra.Command {\n\tflagConf := &argon2Config{}\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"load-test <authentication-requests-per-minute>\",\n\t\tShort: \"Simulate the password hashing with a number of concurrent requests/minute.\",\n\t\tLong:  \"Simulates a number of concurrent authentication requests per minute. Gives statistical data about the measured performance and resource consumption. Can be used to tune and test the hashing parameters for peak demand situations.\",\n\t\tArgs:  cobra.ExactArgs(1),\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tperMinute, err := strconv.ParseInt(args[0], 0, 0)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tflagConf.ctx = cmd.Context()\n\n\t\t\tconf, err := configProvider(cmd, flagConf)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif !flagx.MustGetBool(cmd, cmdx.FlagQuiet) {\n\t\t\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"The hashing configuration used is:\")\n\t\t\t\tcmdx.PrintRow(cmd, conf)\n\t\t\t}\n\n\t\t\tres, err := runLoadTest(cmd, conf, int(perMinute))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tcmdx.PrintRow(cmd, res)\n\t\t\treturn nil\n\t\t},\n\t}\n\n\tregisterArgon2ConfigFlags(cmd.Flags(), flagConf)\n\tconfigx.RegisterFlags(cmd.Flags())\n\tcmdx.RegisterFormatFlags(cmd.Flags())\n\n\treturn cmd\n}\n\nfunc runLoadTest(cmd *cobra.Command, conf *argon2Config, reqPerMin int) (*resultTable, error) {\n\t// force GC at the start of the experiment\n\truntime.GC()\n\n\tsampleTime := time.Minute / 3\n\treqNum := reqPerMin / int(time.Minute/sampleTime)\n\n\tprogressPrinter := cmdx.NewLoudErrPrinter(cmd)\n\t_, _ = progressPrinter.Printf(\"It takes about %s to collect all necessary data, please be patient.\\n\", sampleTime)\n\n\tctx, cancel := context.WithCancel(cmd.Context())\n\thasher := hash.NewHasherArgon2(conf)\n\tallDone := make(chan struct{})\n\tstartAll := time.Now()\n\tvar cancelReason error\n\n\tvar memStats []uint64\n\tgo func() {\n\t\tclock := time.NewTicker(time.Second)\n\t\tdefer func() {\n\t\t\tclock.Stop()\n\t\t}()\n\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-cmd.Context().Done():\n\t\t\t\treturn\n\t\t\tcase <-allDone:\n\t\t\t\treturn\n\t\t\tcase <-clock.C:\n\t\t\t\t// cancel if the allowed time is exceeded by 110%\n\t\t\t\tif time.Since(startAll) > ((sampleTime+conf.localConfig.ExpectedDuration+conf.localConfig.ExpectedDeviation)/100)*110 {\n\t\t\t\t\tcancelReason = ErrSampleTimeExceeded\n\t\t\t\t\tcancel()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tms := runtime.MemStats{}\n\t\t\t\truntime.ReadMemStats(&ms)\n\n\t\t\t\t// cancel if memory is exceeded by 110%\n\t\t\t\tif ms.HeapAlloc > (uint64(conf.localConfig.DedicatedMemory)/100)*110 {\n\t\t\t\t\tcancelReason = ErrMemoryConsumptionExceeded\n\t\t\t\t\tcancel()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tmemStats = append(memStats, ms.HeapAlloc)\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\t// don't read std_in when quiet\n\t\tif flagx.MustGetBool(cmd, cmdx.FlagQuiet) {\n\t\t\treturn\n\t\t}\n\n\t\tinput := make([]byte, 1)\n\t\tfor {\n\t\t\tn, err := cmd.InOrStdin().Read(input)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif n != 0 {\n\t\t\t\t_, _ = color.New(color.FgRed).Fprintln(cmd.ErrOrStderr(), \"I SAID BE PATIENT!!!\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase <-allDone:\n\t\t\t\treturn\n\t\t\tcase <-cmd.Context().Done():\n\t\t\t\treturn\n\t\t\tcase <-time.After(time.Millisecond):\n\t\t\t}\n\t\t}\n\t}()\n\n\tcalcTimes := make([]time.Duration, reqNum)\n\teg, _ := errgroup.WithContext(ctx)\n\n\tfor i := 0; i < reqNum; i++ {\n\t\teg.Go(func(i int) func() error {\n\t\t\treturn func() error {\n\t\t\t\t// wait randomly before starting, max. sample time\n\t\t\t\t//#nosec G404 -- just a timeout to collect statistical data\n\t\t\t\tt := time.Duration(rand.Intn(int(sampleTime)))\n\t\t\t\ttimer := time.NewTimer(t)\n\t\t\t\tdefer timer.Stop()\n\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn nil\n\t\t\t\tcase <-timer.C:\n\t\t\t\t}\n\n\t\t\t\tstart := time.Now()\n\t\t\t\tdone := make(chan struct{})\n\t\t\t\tvar err error\n\n\t\t\t\tgo func() {\n\t\t\t\t\t_, err = hasher.Generate(ctx, []byte(\"password\"))\n\t\t\t\t\tclose(done)\n\t\t\t\t}()\n\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn nil\n\t\t\t\tcase <-done:\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\n\t\t\t\t\tcalcTimes[i] = time.Since(start)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}(i))\n\t}\n\n\tif err := eg.Wait(); err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Error during hashing: %+v\\n\", err)\n\t\treturn nil, cmdx.FailSilently(cmd)\n\t}\n\tswitch cancelReason {\n\tcase ErrSampleTimeExceeded:\n\t\tmemUsed, err2 := stats.LoadRawData(memStats).Max()\n\t\tif err2 != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Unexpected maths error: %+v\\nRaw Data: %+v\\n\", cancelReason, memStats)\n\t\t}\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"The hashing load test took too long. This indicates that you don't have enough resources to handle %d login requests per minute with the desired minimal time of %s. The memory used was %s. Either dedicate more CPU/memory, or decrease the hashing cost (memory and iterations parameters).\\n\", reqPerMin, conf.localConfig.ExpectedDuration, bytesize.ByteSize(memUsed))\n\t\treturn nil, cmdx.FailSilently(cmd)\n\tcase ErrMemoryConsumptionExceeded:\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"The hashing load test exceeded the memory limit of %s. This indicates that you don't have enough resources to handle %d login requests per minute with the desired minimal time of %s. Either dedicate more memory, or decrease the hashing cost (memory and iterations parameters).\\n\", conf.localConfig.DedicatedMemory, reqPerMin, conf.localConfig.ExpectedDuration)\n\t\treturn nil, cmdx.FailSilently(cmd)\n\t}\n\n\ttotalTime := time.Since(startAll)\n\tclose(allDone)\n\n\tcalcData := stats.LoadRawData(calcTimes)\n\n\tduration := func(f func() (float64, error)) time.Duration {\n\t\tv, err := f()\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Unexpected maths error: %+v\\nRaw Data: %+v\\n\", err, calcTimes)\n\t\t}\n\t\treturn time.Duration(int64(v))\n\t}\n\n\tmemUsed, err := stats.LoadRawData(memStats).Max()\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Unexpected maths error: %+v\\nRaw Data: %+v\\n\", err, memStats)\n\t}\n\n\treturn &resultTable{\n\t\tTotalTime:  totalTime,\n\t\tMedianTime: duration(calcData.Mean),\n\t\tStdDev:     duration(calcData.StandardDeviation),\n\t\tMinTime:    duration(calcData.Min),\n\t\tMaxTime:    duration(calcData.Max),\n\t\tMaxMem:     bytesize.ByteSize(memUsed),\n\t}, nil\n}\n"
  },
  {
    "path": "cmd/hashers/argon2/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage argon2\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/ory/x/contextx\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/pflag\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/logrusx\"\n)\n\nconst (\n\tFlagIterations        = \"iterations\"\n\tFlagParallelism       = \"parallelism\"\n\tFlagSaltLength        = \"salt-length\"\n\tFlagKeyLength         = \"key-length\"\n\tFlagMemory            = \"memory\"\n\tFlagDedicatedMemory   = \"dedicated-memory\"\n\tFlagMinimalDuration   = \"min-duration\"\n\tFlagExpectedDeviation = \"expected-deviation\"\n)\n\nvar rootCmd = &cobra.Command{\n\tUse: \"argon2\",\n}\n\nfunc RegisterCommandRecursive(parent *cobra.Command) {\n\tparent.AddCommand(rootCmd)\n\n\trootCmd.AddCommand(newCalibrateCmd(), newHashCmd(), newLoadTestCmd())\n}\n\nfunc registerArgon2ConstantConfigFlags(flags *pflag.FlagSet, c *argon2Config) {\n\t// set default value first\n\tc.localConfig.DedicatedMemory = config.Argon2DefaultDedicatedMemory\n\tflags.Var(&c.localConfig.DedicatedMemory, FlagDedicatedMemory, \"Amount of memory dedicated for password hashing. Kratos will try to not consume more memory.\")\n\tflags.DurationVar(&c.localConfig.ExpectedDuration, FlagMinimalDuration, config.Argon2DefaultDuration, \"Minimal duration a hashing operation (~login request) takes.\")\n\tflags.DurationVar(&c.localConfig.ExpectedDeviation, FlagExpectedDeviation, config.Argon2DefaultDeviation, \"Expected deviation of the time a hashing operation (~login request) takes.\")\n\n\tflags.Uint8Var(&c.localConfig.Parallelism, FlagParallelism, config.Argon2DefaultParallelism, \"Number of threads to use.\")\n\n\tflags.Uint32Var(&c.localConfig.SaltLength, FlagSaltLength, config.Argon2DefaultSaltLength, \"Length of the salt in bytes.\")\n\tflags.Uint32Var(&c.localConfig.KeyLength, FlagKeyLength, config.Argon2DefaultKeyLength, \"Length of the key in bytes.\")\n}\n\nfunc registerArgon2ConfigFlags(flags *pflag.FlagSet, c *argon2Config) {\n\tflags.Uint32Var(&c.localConfig.Iterations, FlagIterations, 1, \"Number of iterations to start probing at.\")\n\n\t// set default value first\n\tc.localConfig.Memory = config.Argon2DefaultMemory\n\tflags.Var(&c.localConfig.Memory, FlagMemory, \"Memory to use.\")\n\n\tregisterArgon2ConstantConfigFlags(flags, c)\n}\n\nfunc configProvider(cmd *cobra.Command, flagConf *argon2Config) (*argon2Config, error) {\n\tl := logrusx.New(\"Ory Kratos\", config.Version)\n\tconf := &argon2Config{}\n\tvar err error\n\tconf.config, err = config.New(\n\t\tcmd.Context(),\n\t\tl,\n\t\tcmd.ErrOrStderr(),\n\t\t&contextx.Default{},\n\t\tconfigx.WithFlags(cmd.Flags()),\n\t\tconfigx.SkipValidation(),\n\t\tconfigx.WithContext(cmd.Context()),\n\t\tconfigx.WithImmutables(\"hashers\"),\n\t)\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Unable to initialize the config provider: %s\\n\", err)\n\t\treturn nil, cmdx.FailSilently(cmd)\n\t}\n\tconf.localConfig = *conf.config.HasherArgon2(cmd.Context())\n\n\tif cmd.Flags().Changed(FlagIterations) {\n\t\tconf.localConfig.Iterations = flagConf.localConfig.Iterations\n\t}\n\tif cmd.Flags().Changed(FlagParallelism) {\n\t\tconf.localConfig.Parallelism = flagConf.localConfig.Parallelism\n\t}\n\tif cmd.Flags().Changed(FlagMemory) {\n\t\tconf.localConfig.Memory = flagConf.localConfig.Memory\n\t}\n\tif cmd.Flags().Changed(FlagDedicatedMemory) {\n\t\tconf.localConfig.DedicatedMemory = flagConf.localConfig.DedicatedMemory\n\t}\n\tif cmd.Flags().Changed(FlagKeyLength) {\n\t\tconf.localConfig.KeyLength = flagConf.localConfig.KeyLength\n\t}\n\tif cmd.Flags().Changed(FlagSaltLength) {\n\t\tconf.localConfig.SaltLength = flagConf.localConfig.SaltLength\n\t}\n\tif cmd.Flags().Changed(FlagExpectedDeviation) {\n\t\tconf.localConfig.ExpectedDeviation = flagConf.localConfig.ExpectedDeviation\n\t}\n\tif cmd.Flags().Changed(FlagMinimalDuration) {\n\t\tconf.localConfig.ExpectedDuration = flagConf.localConfig.ExpectedDuration\n\t}\n\n\treturn conf, nil\n}\n\ntype (\n\targon2Config struct {\n\t\tlocalConfig config.Argon2\n\t\tconfig      *config.Config\n\t\tctx         context.Context\n\t}\n)\n\nvar _ cmdx.TableRow = (*argon2Config)(nil)\n\nfunc (c *argon2Config) Header() []string {\n\tvar header []string\n\n\tt := reflect.TypeOf(c.localConfig)\n\tfor i := 0; i < t.NumField(); i++ {\n\t\theader = append(header, strings.ReplaceAll(strings.ToUpper(t.Field(i).Tag.Get(\"json\")), \"_\", \" \"))\n\t}\n\n\treturn header\n}\n\nfunc (c *argon2Config) Columns() []string {\n\tconf, _ := c.HasherArgon2()\n\treturn []string{\n\t\tconf.Memory.String(),\n\t\tfmt.Sprintf(\"%d\", conf.Iterations),\n\t\tfmt.Sprintf(\"%d\", conf.Parallelism),\n\t\tfmt.Sprintf(\"%d\", conf.SaltLength),\n\t\tfmt.Sprintf(\"%d\", conf.KeyLength),\n\t\tconf.ExpectedDuration.String(),\n\t\tconf.ExpectedDeviation.String(),\n\t\tconf.DedicatedMemory.String(),\n\t}\n}\n\nfunc (c *argon2Config) Interface() interface{} {\n\ti, _ := c.HasherArgon2()\n\treturn i\n}\n\nfunc (c *argon2Config) Config() *config.Config {\n\tac, _ := c.HasherArgon2()\n\tfor k, v := range map[string]interface{}{\n\t\tconfig.ViperKeyHasherArgon2ConfigIterations:        ac.Iterations,\n\t\tconfig.ViperKeyHasherArgon2ConfigMemory:            ac.Memory,\n\t\tconfig.ViperKeyHasherArgon2ConfigParallelism:       ac.Parallelism,\n\t\tconfig.ViperKeyHasherArgon2ConfigDedicatedMemory:   ac.DedicatedMemory,\n\t\tconfig.ViperKeyHasherArgon2ConfigKeyLength:         ac.KeyLength,\n\t\tconfig.ViperKeyHasherArgon2ConfigSaltLength:        ac.SaltLength,\n\t\tconfig.ViperKeyHasherArgon2ConfigExpectedDuration:  ac.ExpectedDuration,\n\t\tconfig.ViperKeyHasherArgon2ConfigExpectedDeviation: ac.ExpectedDeviation,\n\t} {\n\t\t_ = c.config.Set(c.ctx, k, v)\n\t}\n\treturn c.config\n}\n\nfunc (c *argon2Config) HasherArgon2() (*config.Argon2, error) {\n\tif c.localConfig.Memory == 0 {\n\t\tc.localConfig.Memory = config.Argon2DefaultMemory\n\t}\n\treturn &c.localConfig, nil\n}\n"
  },
  {
    "path": "cmd/hashers/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hashers\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/hashers/argon2\"\n)\n\nfunc NewRootCmd() *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:   \"hashers\",\n\t\tShort: \"This command contains helpers around hashing\",\n\t}\n\treturn c\n}\n\nfunc RegisterCommandRecursive(parent *cobra.Command) {\n\trootCmd := NewRootCmd()\n\tparent.AddCommand(rootCmd)\n\n\targon2.RegisterCommandRecursive(rootCmd)\n}\n"
  },
  {
    "path": "cmd/identities/definitions.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities\n\nimport (\n\t\"strings\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\n\t\"github.com/ory/x/cmdx\"\n)\n\ntype (\n\toutputIdentity           kratos.Identity\n\toutputIdentityCollection struct {\n\t\tIdentities       []kratos.Identity `json:\"identities\"`\n\t\tNextPageToken    string            `json:\"next_page_token\"`\n\t\tincludePageToken bool\n\t}\n)\n\nfunc (outputIdentity) Header() []string {\n\treturn []string{\"ID\", \"VERIFIED ADDRESSES\", \"RECOVERY ADDRESSES\", \"SCHEMA ID\", \"SCHEMA URL\"}\n}\n\nfunc (i outputIdentity) Columns() []string {\n\tdata := [5]string{\n\t\ti.Id,\n\t\tcmdx.None,\n\t\tcmdx.None,\n\t\tcmdx.None,\n\t\tcmdx.None,\n\t}\n\n\taddresses := make([]string, 0, len(i.VerifiableAddresses))\n\tfor _, a := range i.VerifiableAddresses {\n\t\tif len(a.Value) > 0 {\n\t\t\taddresses = append(addresses, a.Value)\n\t\t}\n\t}\n\tdata[1] = strings.Join(addresses, \", \")\n\n\taddresses = addresses[:0]\n\tfor _, a := range i.RecoveryAddresses {\n\t\tif len(a.Value) > 0 {\n\t\t\taddresses = append(addresses, a.Value)\n\t\t}\n\t}\n\tdata[2] = strings.Join(addresses, \", \")\n\tdata[3] = i.SchemaId\n\tdata[4] = i.SchemaUrl\n\n\treturn data[:]\n}\n\nfunc (i outputIdentity) Interface() interface{} {\n\treturn i\n}\n\nfunc (outputIdentityCollection) Header() []string {\n\treturn outputIdentity{}.Header()\n}\n\nfunc (c outputIdentityCollection) Table() [][]string {\n\trows := make([][]string, len(c.Identities))\n\tfor i, ident := range c.Identities {\n\t\trows[i] = outputIdentity(ident).Columns()\n\t}\n\treturn append(rows,\n\t\t[]string{\"\"},\n\t\t[]string{\"NEXT PAGE TOKEN\", c.NextPageToken},\n\t)\n}\n\nfunc (c outputIdentityCollection) Interface() interface{} {\n\tif c.includePageToken {\n\t\treturn c\n\t}\n\treturn c.Identities\n}\n\nfunc (c *outputIdentityCollection) Len() int {\n\treturn len(c.Identities)\n}\n"
  },
  {
    "path": "cmd/identities/delete.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n\t\"github.com/ory/kratos/pkg/clihelpers\"\n\t\"github.com/ory/x/cmdx\"\n)\n\nfunc NewDeleteCmd() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"delete\",\n\t\tShort: \"Delete resources\",\n\t}\n\tcmd.AddCommand(NewDeleteIdentityCmd())\n\tcliclient.RegisterClientFlags(cmd.PersistentFlags())\n\tcmdx.RegisterFormatFlags(cmd.PersistentFlags())\n\treturn cmd\n}\n\nfunc NewDeleteIdentityCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"identity id-0 [id-1] [id-2] [id-n]\",\n\t\tShort: \"Delete one or more identities by their ID(s)\",\n\t\tLong: fmt.Sprintf(`This command deletes one or more identities by ID. To delete an identity by some selector, e.g. the recovery email address, use the list command in combination with jq.\n\n%s`, clihelpers.WarningJQIsComplicated),\n\t\tExample: `To delete the identity with the recovery email address \"foo@bar.com\", run:\n\n\t{{ .CommandPath }} $({{ .Root.Name }} list identities --format json | jq -r 'map(select(.recovery_addresses[].value == \"foo@bar.com\")) | .[].id')`,\n\t\tArgs: cobra.MinimumNArgs(1),\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tc, err := cliclient.NewClient(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tvar (\n\t\t\t\tdeleted = make([]cmdx.OutputIder, 0, len(args))\n\t\t\t\tfailed  = make(map[string]error)\n\t\t\t)\n\n\t\t\tfor _, a := range args {\n\t\t\t\t_, err := c.IdentityAPI.DeleteIdentity(cmd.Context(), a).Execute()\n\t\t\t\tif err != nil {\n\t\t\t\t\tfailed[a] = cmdx.PrintOpenAPIError(cmd, err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tdeleted = append(deleted, cmdx.OutputIder(a))\n\t\t\t}\n\n\t\t\tif len(deleted) == 1 {\n\t\t\t\tcmdx.PrintRow(cmd, &deleted[0])\n\t\t\t} else if len(deleted) > 1 {\n\t\t\t\tcmdx.PrintTable(cmd, &cmdx.OutputIderCollection{Items: deleted})\n\t\t\t}\n\n\t\t\tcmdx.PrintErrors(cmd, failed)\n\t\t\tif len(failed) != 0 {\n\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "cmd/identities/delete_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/cmd/identities\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc TestDeleteCmd(t *testing.T) {\n\treg, cmd := setup(t, identities.NewDeleteIdentityCmd)\n\n\tt.Run(\"case=deletes successfully\", func(t *testing.T) {\n\t\t// create identity to delete\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\trequire.NoError(t, reg.Persister().CreateIdentity(context.Background(), i))\n\n\t\tstdOut := cmd.ExecNoErr(t, i.ID.String())\n\n\t\t// expect ID and no error\n\t\tassert.Equal(t, i.ID.String(), gjson.Parse(stdOut).String())\n\n\t\t// expect identity to be deleted\n\t\t_, err := reg.Persister().GetIdentity(context.Background(), i.ID, identity.ExpandNothing)\n\t\tassert.True(t, errors.Is(err, sqlcon.ErrNoRows))\n\t})\n\n\tt.Run(\"case=deletes three identities\", func(t *testing.T) {\n\t\tis, ids := makeIdentities(t, reg, 3)\n\n\t\tstdOut := cmd.ExecNoErr(t, ids...)\n\n\t\tassert.Equal(t, `[\"`+strings.Join(ids, \"\\\",\\\"\")+\"\\\"]\\n\", stdOut)\n\n\t\tfor _, i := range is {\n\t\t\t_, err := reg.Persister().GetIdentity(context.Background(), i.ID, identity.ExpandNothing)\n\t\t\tassert.Error(t, err)\n\t\t}\n\t})\n\n\tt.Run(\"case=fails with unknown ID\", func(t *testing.T) {\n\t\tstdErr := cmd.ExecExpectedErr(t, x.NewUUID().String())\n\n\t\tassert.Contains(t, stdErr, \"Unable to locate the resource\", stdErr)\n\t})\n}\n"
  },
  {
    "path": "cmd/identities/get.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities\n\nimport (\n\t\"fmt\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/stringsx\"\n\n\t\"github.com/ory/kratos/pkg/clihelpers\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n)\n\nconst (\n\tFlagIncludeCreds = \"include-credentials\"\n)\n\nfunc NewGetCmd() *cobra.Command {\n\tvar cmd = &cobra.Command{\n\t\tUse:   \"get\",\n\t\tShort: \"Get resources\",\n\t}\n\tcmd.AddCommand(NewGetIdentityCmd())\n\tcliclient.RegisterClientFlags(cmd.PersistentFlags())\n\tcmdx.RegisterFormatFlags(cmd.PersistentFlags())\n\treturn cmd\n}\n\nfunc NewGetIdentityCmd() *cobra.Command {\n\tvar (\n\t\tincludeCreds []string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"identity [id-1] [id-2] [id-n]\",\n\t\tShort: \"Get one or more identities by their ID(s)\",\n\t\tLong: fmt.Sprintf(`This command gets all the details about an identity. To get an identity by some selector, e.g. the recovery email address, use the list command in combination with jq.\n\n%s`, clihelpers.WarningJQIsComplicated),\n\t\tExample: `To get the identities with the recovery email address at the domain \"ory.sh\", run:\n\n\t{{ .CommandPath }} $({{ .Root.Name }} ls identities --format json | jq -r 'map(select(.recovery_addresses[].value | endswith(\"@ory.sh\"))) | .[].id')`,\n\t\tArgs: cobra.MinimumNArgs(1),\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tc, err := cliclient.NewClient(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// we check includeCreds argument is valid\n\t\t\tfor _, opt := range includeCreds {\n\t\t\t\te := stringsx.SwitchExact(opt)\n\t\t\t\tif !e.AddCase(\"oidc\") {\n\t\t\t\t\tcmd.PrintErrln(`You have to put a valid value of credentials type to be included, try --help for details.`)\n\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tidentities := make([]kratos.Identity, 0, len(args))\n\t\t\tfailed := make(map[string]error)\n\t\t\tfor _, id := range args {\n\t\t\t\tidentity, _, err := c.IdentityAPI.\n\t\t\t\t\tGetIdentity(cmd.Context(), id).\n\t\t\t\t\tIncludeCredential(includeCreds).\n\t\t\t\t\tExecute()\n\n\t\t\t\tif x.SDKError(err) != nil {\n\t\t\t\t\tfailed[id] = cmdx.PrintOpenAPIError(cmd, err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tidentities = append(identities, *identity)\n\t\t\t}\n\n\t\t\tif len(identities) == 1 {\n\t\t\t\tcmdx.PrintRow(cmd, (*outputIdentity)(&identities[0]))\n\t\t\t} else if len(identities) > 1 {\n\t\t\t\tcmdx.PrintTable(cmd, &outputIdentityCollection{Identities: identities, includePageToken: false})\n\t\t\t}\n\t\t\tcmdx.PrintErrors(cmd, failed)\n\n\t\t\tif len(failed) != 0 {\n\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\n\tflags := cmd.Flags()\n\t// include credential flag to add third party tokens in returned data\n\tflags.StringArrayVarP(&includeCreds, FlagIncludeCreds, \"i\", []string{}, `Include third party tokens (only \"oidc\" supported) `)\n\treturn cmd\n}\n"
  },
  {
    "path": "cmd/identities/get_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/cmd/identities\"\n\t\"github.com/ory/x/assertx\"\n\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n)\n\nfunc TestGetCmd(t *testing.T) {\n\treg, cmd := setup(t, identities.NewGetIdentityCmd)\n\n\tt.Run(\"case=gets a single identity\", func(t *testing.T) {\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\ti.MetadataPublic = []byte(`\"public\"`)\n\t\ti.MetadataAdmin = []byte(`\"admin\"`)\n\t\trequire.NoError(t, reg.Persister().CreateIdentity(context.Background(), i))\n\n\t\tstdOut := cmd.ExecNoErr(t, i.ID.String())\n\n\t\tij, err := json.Marshal(identity.WithCredentialsNoConfigAndAdminMetadataInJSON(*i))\n\t\trequire.NoError(t, err)\n\n\t\tassertx.EqualAsJSONExcept(t, json.RawMessage(ij), json.RawMessage(stdOut), []string{\"created_at\", \"updated_at\", \"AdditionalProperties\"})\n\t})\n\n\tt.Run(\"case=gets three identities\", func(t *testing.T) {\n\t\tis, ids := makeIdentities(t, reg, 3)\n\n\t\tstdOut := cmd.ExecNoErr(t, ids...)\n\n\t\tisj, err := json.Marshal(is)\n\t\trequire.NoError(t, err)\n\n\t\tassertx.EqualAsJSONExcept(t, json.RawMessage(isj), json.RawMessage(stdOut), []string{\"created_at\", \"updated_at\", \"AdditionalProperties\"})\n\t})\n\n\tt.Run(\"case=fails with unknown ID\", func(t *testing.T) {\n\t\tstdErr := cmd.ExecExpectedErr(t, x.NewUUID().String())\n\n\t\tassert.Contains(t, stdErr, \"Unable to locate the resource\", stdErr)\n\t})\n\n\tt.Run(\"case=gets a single identity with oidc credentials\", func(t *testing.T) {\n\t\tapplyCredentials := func(identifier, accessToken, refreshToken, idToken string, encrypt bool) identity.Credentials {\n\t\t\ttoJson := func(c identity.CredentialsOIDC) []byte {\n\t\t\t\tout, err := json.Marshal(&c)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\treturn out\n\t\t\t}\n\t\t\ttransform := func(token string) string {\n\t\t\t\tif encrypt {\n\t\t\t\t\ts, err := reg.Cipher(context.Background()).Encrypt(context.Background(), []byte(token))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\treturn s\n\t\t\t\t}\n\t\t\t\treturn token\n\t\t\t}\n\t\t\treturn identity.Credentials{\n\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"bar:\" + identifier},\n\t\t\t\tConfig: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{\n\t\t\t\t\t{\n\t\t\t\t\t\tSubject:             \"foo\",\n\t\t\t\t\t\tProvider:            \"bar\",\n\t\t\t\t\t\tInitialAccessToken:  transform(accessToken + \"0\"),\n\t\t\t\t\t\tInitialRefreshToken: transform(refreshToken + \"0\"),\n\t\t\t\t\t\tInitialIDToken:      transform(idToken + \"0\"),\n\t\t\t\t\t\tOrganization:        \"foo-org-id\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tSubject:             \"baz\",\n\t\t\t\t\t\tProvider:            \"zab\",\n\t\t\t\t\t\tInitialAccessToken:  transform(accessToken + \"1\"),\n\t\t\t\t\t\tInitialRefreshToken: transform(refreshToken + \"1\"),\n\t\t\t\t\t\tInitialIDToken:      transform(idToken + \"1\"),\n\t\t\t\t\t\tOrganization:        \"bar-org-id\",\n\t\t\t\t\t},\n\t\t\t\t}}),\n\t\t\t}\n\t\t}\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\ti.MetadataPublic = []byte(`\"public\"`)\n\t\ti.MetadataAdmin = []byte(`\"admin\"`)\n\t\ti.SetCredentials(identity.CredentialsTypeOIDC, applyCredentials(\"uniqueIdentifier\", \"accessBar\", \"refreshBar\", \"idBar\", true))\n\t\t// duplicate identity with decrypted tokens\n\t\tdi := i.CopyWithoutCredentials()\n\t\tdi.SetCredentials(identity.CredentialsTypeOIDC, applyCredentials(\"uniqueIdentifier\", \"accessBar\", \"refreshBar\", \"idBar\", false))\n\n\t\trequire.NoError(t, reg.Persister().CreateIdentity(context.Background(), i))\n\n\t\tstdOut := cmd.ExecNoErr(t, \"--\"+identities.FlagIncludeCreds, \"oidc\", i.ID.String())\n\t\tij, err := json.Marshal(identity.WithCredentialsAndAdminMetadataInJSON(*di))\n\t\trequire.NoError(t, err)\n\n\t\tii := []string{\"id\", \"schema_url\", \"state_changed_at\", \"created_at\", \"updated_at\", \"credentials.oidc.created_at\", \"credentials.oidc.updated_at\", \"credentials.oidc.version\", \"AdditionalProperties\"}\n\t\tassertx.EqualAsJSONExcept(t, json.RawMessage(ij), json.RawMessage(stdOut), ii)\n\t})\n}\n"
  },
  {
    "path": "cmd/identities/helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/cmdx\"\n)\n\nfunc parseIdentities(raw []byte) (rawIdentities []string) {\n\tres := gjson.ParseBytes(raw)\n\tif !res.IsArray() {\n\t\treturn []string{res.Raw}\n\t}\n\tres.ForEach(func(_, v gjson.Result) bool {\n\t\trawIdentities = append(rawIdentities, v.Raw)\n\t\treturn true\n\t})\n\treturn\n}\n\nfunc readIdentities(cmd *cobra.Command, args []string) (map[string]string, error) {\n\trawIdentities := make(map[string]string)\n\tif len(args) == 0 {\n\t\tfc, err := io.ReadAll(cmd.InOrStdin())\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"STD_IN: Could not read: %s\\n\", err)\n\t\t\treturn nil, cmdx.FailSilently(cmd)\n\t\t}\n\t\tfor i, id := range parseIdentities(fc) {\n\t\t\trawIdentities[fmt.Sprintf(\"STD_IN[%d]\", i)] = id\n\t\t}\n\t\treturn rawIdentities, nil\n\t}\n\tfor _, fn := range args {\n\t\tfc, err := os.ReadFile(fn) // #nosec G304 -- file is supplied by user\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"%s: Could not open identity file: %s\\n\", fn, err)\n\t\t\treturn nil, cmdx.FailSilently(cmd)\n\t\t}\n\t\tfor i, id := range parseIdentities(fc) {\n\t\t\trawIdentities[fmt.Sprintf(\"%s[%d]\", fn, i)] = id\n\t\t}\n\t}\n\treturn rawIdentities, nil\n}\n"
  },
  {
    "path": "cmd/identities/helpers_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/x/cmdx\"\n\n\t\"github.com/ory/kratos/identity\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n)\n\nfunc setup(t *testing.T, newCmd func() *cobra.Command) (*driver.RegistryDefault, *cmdx.CommandExecuter) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t_, admin := testhelpers.NewKratosServerWithCSRF(t, reg)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stubs/identity.schema.json\")\n\t// setup command\n\treturn reg, &cmdx.CommandExecuter{\n\t\tNew: func() *cobra.Command {\n\t\t\tcmd := newCmd()\n\t\t\tcliclient.RegisterClientFlags(cmd.Flags())\n\t\t\tcmdx.RegisterFormatFlags(cmd.Flags())\n\t\t\treturn cmd\n\t\t},\n\t\tPersistentArgs: []string{\"--\" + cliclient.FlagEndpoint, admin.URL, \"--\" + cmdx.FlagFormat, string(cmdx.FormatJSON)},\n\t}\n}\n\nfunc makeIdentities(t *testing.T, reg driver.Registry, n int) (is []*identity.Identity, ids []string) {\n\tfor j := 0; j < n; j++ {\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\ti.MetadataPublic = []byte(`{\"foo\":\"bar\"}`)\n\t\trequire.NoError(t, reg.Persister().CreateIdentity(context.Background(), i))\n\t\tis = append(is, i)\n\t\tids = append(ids, i.ID.String())\n\t}\n\treturn\n}\n"
  },
  {
    "path": "cmd/identities/import.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\n\t\"github.com/ory/x/cmdx\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n)\n\nfunc NewImportCmd() *cobra.Command {\n\tvar cmd = &cobra.Command{\n\t\tUse:   \"import\",\n\t\tShort: \"Import resources\",\n\t}\n\tcmd.AddCommand(NewImportIdentitiesCmd())\n\tcliclient.RegisterClientFlags(cmd.PersistentFlags())\n\tcmdx.RegisterFormatFlags(cmd.PersistentFlags())\n\treturn cmd\n}\n\n// NewImportIdentitiesCmd represents the import command\nfunc NewImportIdentitiesCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"identities file-1.json [file-2.json] [file-3.json] [file-n.json]\",\n\t\tShort: \"Import one or more identities from files or STD_IN\",\n\t\tExample: `Create an example identity:\n\n\tcat > ./file.json <<EOF\n\t{\n\t    \"schema_id\": \"default\",\n\t    \"traits\": {\n\t        \"email\": \"foo@example.com\"\n\t    }\n\t}\n\tEOF\n\n\t{{ .CommandPath }} file.json\n\nAlternatively:\n\n\tcat file.json | {{ .CommandPath }}`,\n\t\tLong: `Import identities from files or STD_IN.\n\nFiles can contain only a single or an array of identities. The validity of files can be tested beforehand using \"... identities validate\".`,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tc, err := cliclient.NewClient(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\timported := make([]kratos.Identity, 0, len(args))\n\t\t\tfailed := make(map[string]error)\n\n\t\t\tis, err := readIdentities(cmd, args)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tfor src, i := range is {\n\t\t\t\tvar params kratos.CreateIdentityBody\n\t\t\t\terr = json.Unmarshal([]byte(i), &params)\n\t\t\t\tif err != nil {\n\t\t\t\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"STD_IN: Could not parse identity\")\n\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t}\n\n\t\t\t\tident, _, err := c.IdentityAPI.CreateIdentity(cmd.Context()).CreateIdentityBody(params).Execute()\n\t\t\t\tif err != nil {\n\t\t\t\t\tfailed[src] = cmdx.PrintOpenAPIError(cmd, err)\n\t\t\t\t} else {\n\t\t\t\t\timported = append(imported, *ident)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(imported) == 1 {\n\t\t\t\tcmdx.PrintRow(cmd, (*outputIdentity)(&imported[0]))\n\t\t\t} else {\n\t\t\t\tcmdx.PrintTable(cmd, &outputIdentityCollection{Identities: imported})\n\t\t\t}\n\t\t\tcmdx.PrintErrors(cmd, failed)\n\n\t\t\tif len(failed) != 0 {\n\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "cmd/identities/import_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/identity\"\n\n\t\"github.com/ory/kratos/cmd/identities\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n)\n\nfunc TestImportCmd(t *testing.T) {\n\treg, cmd := setup(t, identities.NewImportIdentitiesCmd)\n\n\tt.Run(\"case=imports a new identity from file\", func(t *testing.T) {\n\t\ti := kratos.CreateIdentityBody{\n\t\t\tSchemaId: config.DefaultIdentityTraitsSchemaID,\n\t\t\tTraits:   map[string]interface{}{},\n\t\t}\n\t\tij, err := json.Marshal(i)\n\t\trequire.NoError(t, err)\n\t\tf, err := os.CreateTemp(\"\", \"\")\n\t\trequire.NoError(t, err)\n\t\t_, err = f.Write(ij)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, f.Close())\n\n\t\tstdOut := cmd.ExecNoErr(t, f.Name())\n\n\t\tid, err := uuid.FromString(gjson.Get(stdOut, \"id\").String())\n\t\trequire.NoError(t, err)\n\t\t_, err = reg.Persister().GetIdentity(context.Background(), id, identity.ExpandNothing)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"case=imports multiple identities from single file\", func(t *testing.T) {\n\t\ti := []kratos.CreateIdentityBody{\n\t\t\t{\n\t\t\t\tSchemaId: config.DefaultIdentityTraitsSchemaID,\n\t\t\t\tTraits:   map[string]interface{}{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tSchemaId: config.DefaultIdentityTraitsSchemaID,\n\t\t\t\tTraits:   map[string]interface{}{},\n\t\t\t},\n\t\t}\n\t\tij, err := json.Marshal(i)\n\t\trequire.NoError(t, err)\n\t\tf, err := os.CreateTemp(\"\", \"\")\n\t\trequire.NoError(t, err)\n\t\t_, err = f.Write(ij)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, f.Close())\n\n\t\tstdOut := cmd.ExecNoErr(t, f.Name())\n\n\t\tid, err := uuid.FromString(gjson.Get(stdOut, \"0.id\").String())\n\t\trequire.NoError(t, err)\n\t\t_, err = reg.Persister().GetIdentity(context.Background(), id, identity.ExpandNothing)\n\t\tassert.NoError(t, err)\n\n\t\tid, err = uuid.FromString(gjson.Get(stdOut, \"1.id\").String())\n\t\trequire.NoError(t, err)\n\t\t_, err = reg.Persister().GetIdentity(context.Background(), id, identity.ExpandNothing)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"case=imports a new identity from STD_IN\", func(t *testing.T) {\n\t\ti := []kratos.CreateIdentityBody{\n\t\t\t{\n\t\t\t\tSchemaId: config.DefaultIdentityTraitsSchemaID,\n\t\t\t\tTraits:   map[string]interface{}{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tSchemaId: config.DefaultIdentityTraitsSchemaID,\n\t\t\t\tTraits:   map[string]interface{}{},\n\t\t\t},\n\t\t}\n\t\tij, err := json.Marshal(i)\n\t\trequire.NoError(t, err)\n\n\t\tstdOut, stdErr, err := cmd.Exec(bytes.NewBuffer(ij))\n\t\trequire.NoError(t, err, \"%s %s\", stdOut, stdErr)\n\n\t\tid, err := uuid.FromString(gjson.Get(stdOut, \"0.id\").String())\n\t\trequire.NoError(t, err)\n\t\t_, err = reg.Persister().GetIdentity(context.Background(), id, identity.ExpandNothing)\n\t\tassert.NoError(t, err)\n\n\t\tid, err = uuid.FromString(gjson.Get(stdOut, \"1.id\").String())\n\t\trequire.NoError(t, err)\n\t\t_, err = reg.Persister().GetIdentity(context.Background(), id, identity.ExpandNothing)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"case=imports multiple identities from STD_IN\", func(t *testing.T) {\n\t\ti := kratos.CreateIdentityBody{\n\t\t\tSchemaId: config.DefaultIdentityTraitsSchemaID,\n\t\t\tTraits:   map[string]interface{}{},\n\t\t}\n\t\tij, err := json.Marshal(i)\n\t\trequire.NoError(t, err)\n\n\t\tstdOut, stdErr, err := cmd.Exec(bytes.NewBuffer(ij))\n\t\trequire.NoError(t, err, \"%s %s\", stdOut, stdErr)\n\n\t\tid, err := uuid.FromString(gjson.Get(stdOut, \"id\").String())\n\t\trequire.NoError(t, err)\n\t\t_, err = reg.Persister().GetIdentity(context.Background(), id, identity.ExpandNothing)\n\t\tassert.NoError(t, err)\n\t})\n}\n"
  },
  {
    "path": "cmd/identities/list.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/flagx\"\n\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n\t\"github.com/ory/x/cmdx\"\n)\n\nfunc NewListCmd() *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:     \"list\",\n\t\tAliases: []string{\"ls\"},\n\t\tShort:   \"List resources\",\n\t}\n\tc.AddCommand(NewListIdentitiesCmd())\n\tcliclient.RegisterClientFlags(c.PersistentFlags())\n\tcmdx.RegisterFormatFlags(c.PersistentFlags())\n\treturn c\n}\n\nfunc NewListIdentitiesCmd() *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:   \"identities\",\n\t\tShort: \"List identities\",\n\t\tLong: `Return a list of identities.\n\nThe consistency defaults to ` + \"`eventual`\" + ` and can be set to ` + \"`strong`\" + ` or ` + \"`eventual`\" + `.\nEventual consistency means that the list operation will return faster and might not include recently created or updated identities. Replication lag is about 5 seconds.`,\n\t\tExample: \"{{ .CommandPath }} --page-size 100 --consistency eventual\",\n\t\tArgs:    cmdx.ZeroOrTwoArgs,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tc, err := cliclient.NewClient(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tconsistency := flagx.MustGetString(cmd, \"consistency\")\n\t\t\treq := c.IdentityAPI.ListIdentities(cmd.Context()).Consistency(consistency)\n\t\t\tpage, perPage, err := cmdx.ParseTokenPaginationArgs(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treq = req.PageToken(page)\n\t\t\treq = req.PageSize(int64(perPage))\n\n\t\t\tidentities, res, err := req.Execute()\n\t\t\tif err != nil {\n\t\t\t\treturn cmdx.PrintOpenAPIError(cmd, err)\n\t\t\t}\n\n\t\t\tpages := keysetpagination.ParseHeader(res)\n\t\t\tcmdx.PrintTable(cmd, &outputIdentityCollection{\n\t\t\t\tIdentities:       identities,\n\t\t\t\tNextPageToken:    pages.NextToken,\n\t\t\t\tincludePageToken: true,\n\t\t\t})\n\t\t\treturn nil\n\t\t},\n\t}\n\tc.Flags().String(\"consistency\", \"eventual\", \"The read consistency to use. Can be either \\\"strong\\\" or \\\"eventual\\\". Defaults to \\\"eventual\\\".\")\n\tcmdx.RegisterTokenPaginationFlags(c)\n\treturn c\n}\n"
  },
  {
    "path": "cmd/identities/list_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/cmd/identities\"\n\n\t\"github.com/ory/x/cmdx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\nfunc TestListCmd(t *testing.T) {\n\treg, cmd := setup(t, identities.NewListIdentitiesCmd)\n\n\tvar deleteIdentities = func(t *testing.T, is []*identity.Identity) {\n\t\tfor _, i := range is {\n\t\t\trequire.NoError(t, reg.Persister().DeleteIdentity(context.Background(), i.ID))\n\t\t}\n\t}\n\n\tt.Run(\"case=lists all identities with default pagination\", func(t *testing.T) {\n\t\tis, ids := makeIdentities(t, reg, 5)\n\t\tt.Cleanup(func() {\n\t\t\tdeleteIdentities(t, is)\n\t\t})\n\n\t\tstdOut := cmd.ExecNoErr(t, \"--\"+cmdx.FlagQuiet)\n\n\t\tfor _, i := range ids {\n\t\t\tassert.Contains(t, stdOut, i)\n\t\t}\n\t})\n\n\tt.Run(\"case=lists all identities with pagination\", func(t *testing.T) {\n\t\tis, ids := makeIdentities(t, reg, 10)\n\t\tt.Cleanup(func() {\n\t\t\tdeleteIdentities(t, is)\n\t\t})\n\n\t\tfirst := cmd.ExecNoErr(t, \"--format\", \"json-pretty\", \"--page-size\", \"2\")\n\t\tnextPageToken := gjson.Get(first, \"next_page_token\").String()\n\t\tactualIDs := gjson.Get(first, \"identities.#.id\").Array()\n\t\tfor nextPageToken != \"\" {\n\t\t\tnext := cmd.ExecNoErr(t, \"--page-size\", \"2\", \"--page-token\", nextPageToken)\n\t\t\tactualIDs = append(actualIDs, gjson.Get(next, \"identities.#.id\").Array()...)\n\t\t\tnextPageToken = gjson.Get(next, \"next_page_token\").String()\n\t\t}\n\n\t\tactualIDsString := make([]string, len(actualIDs))\n\t\tfor i, id := range actualIDs {\n\t\t\tactualIDsString[i] = id.Str\n\t\t}\n\n\t\tassert.ElementsMatch(t, ids, actualIDsString)\n\t})\n}\n"
  },
  {
    "path": "cmd/identities/stubs/identity.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"testKey\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "cmd/identities/validate.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/spec\"\n\n\t\"github.com/ory/x/jsonschemax\"\n\n\t\"github.com/ory/x/cmdx\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/cmd/cliclient\"\n)\n\nfunc NewValidateCmd() *cobra.Command {\n\tvar cmd = &cobra.Command{\n\t\tUse:   \"validate\",\n\t\tShort: \"Validate resources\",\n\t}\n\tcmd.AddCommand(NewValidateIdentityCmd())\n\tcliclient.RegisterClientFlags(cmd.PersistentFlags())\n\tcmdx.RegisterFormatFlags(cmd.PersistentFlags())\n\treturn cmd\n\n}\n\nfunc NewValidateIdentityCmd() *cobra.Command {\n\tvar c = &cobra.Command{\n\t\tUse:   \"identity file.json [file-2.json] [file-3.json] [file-n.json]\",\n\t\tShort: \"Validate local identity files\",\n\t\tLong: `This command allows validation of identity files.\nIt validates against the payload of the API and the identity schema as configured in Ory Kratos.\nIdentities can be supplied via STD_IN or JSON files containing a single or an array of identities.`,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tc, err := cliclient.NewClient(cmd)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tis, err := readIdentities(cmd, args)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tfor src, i := range is {\n\t\t\t\terr = ValidateIdentity(cmd, src, i, func(ctx context.Context, id string) (map[string]interface{}, *http.Response, error) {\n\t\t\t\t\treturn c.IdentityAPI.GetIdentitySchema(ctx, id).Execute()\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_, _ = fmt.Fprintln(cmd.OutOrStdout(), \"All identity files are valid.\")\n\t\t\treturn nil\n\t\t},\n\t}\n\n\treturn c\n}\n\nvar schemas = make(map[string]*jsonschema.Schema)\n\nconst createIdentityPath = \"api.json#/components/schemas/createIdentityBody\"\n\ntype SchemaGetter = func(ctx context.Context, id string) (map[string]interface{}, *http.Response, error)\n\n// ValidateIdentity validates the json payload fc against\n// 1. the swagger payload definition and\n// 2. the remote custom identity schema.\nfunc ValidateIdentity(cmd *cobra.Command, src, i string, getRemoteSchema SchemaGetter) error {\n\tswaggerSchema, ok := schemas[createIdentityPath]\n\tif !ok {\n\t\t// add swagger schema\n\t\tschemaCompiler := jsonschema.NewCompiler()\n\t\terr := schemaCompiler.AddResource(\"api.json\", bytes.NewReader(spec.API))\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, \"Could not add swagger schema to the schema compiler. This is an error with the binary you use and should be reported. Thanks ;)\")\n\t\t}\n\n\t\t// compile swagger payload definition\n\t\tswaggerSchema, err = schemaCompiler.Compile(cmd.Context(), createIdentityPath)\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, \"Could not compile the identity schema. This is an error with the binary you use and should be reported. Thanks ;)\")\n\t\t}\n\t\t// force additional properties to false because swagger does not render this\n\t\tswaggerSchema.AdditionalProperties = false\n\t\tschemas[createIdentityPath] = swaggerSchema\n\t}\n\n\t// validate against swagger definition\n\tvar foundValidationErrors bool\n\terr := swaggerSchema.Validate(bytes.NewBufferString(i))\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"%s: not valid\\n\", src)\n\t\tjsonschemax.FormatValidationErrorForCLI(cmd.ErrOrStderr(), []byte(i), err)\n\t\tfoundValidationErrors = true\n\t}\n\n\t// get custom identity schema id\n\tsid := gjson.Get(i, \"schema_id\")\n\tif !sid.Exists() {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), `%s: Expected key \"schema_id\" to be defined in identity file`, src)\n\t\treturn cmdx.FailSilently(cmd)\n\t}\n\n\tcustomSchema, ok := schemas[sid.String()]\n\tif !ok {\n\t\tts, _, err := getRemoteSchema(cmd.Context(), sid.String())\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"%s: Could not fetch schema with ID \\\"%s\\\": %s\\n\", src, sid.String(), err)\n\t\t\treturn cmdx.FailSilently(cmd)\n\t\t}\n\t\tsf, err := json.Marshal(ts)\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, fmt.Sprintf(\"%s: Could not marshal the traits schema. This usually means there is a problem with your upstream service as it served an invalid response.\", src))\n\t\t}\n\n\t\t// compile custom identity schema\n\t\tcustomSchema, err = jsonschema.CompileString(cmd.Context(), \"identity_traits.schema.json\", string(sf))\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"%s: Could not compile the traits schema: %s\\n\", src, err)\n\t\t\treturn cmdx.FailSilently(cmd)\n\t\t}\n\t\tschemas[sid.String()] = customSchema\n\t}\n\n\t// validate against custom identity schema\n\terr = customSchema.Validate(bytes.NewBufferString(i))\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"%s: not valid\\n\", src)\n\t\tjsonschemax.FormatValidationErrorForCLI(cmd.ErrOrStderr(), []byte(i), err)\n\t\tfoundValidationErrors = true\n\t}\n\n\tif foundValidationErrors {\n\t\treturn cmdx.FailSilently(cmd)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/identities/validate_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identities_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/cmd/identities\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestValidateIdentity(t *testing.T) {\n\tvar testCmd = func() (*cobra.Command, *bytes.Buffer, *bytes.Buffer) {\n\t\tout, err := &bytes.Buffer{}, &bytes.Buffer{}\n\t\tcmd := &cobra.Command{}\n\t\tcmd.SetOut(out)\n\t\tcmd.SetErr(err)\n\t\treturn cmd, out, err\n\t}\n\n\tvar testSchemaGetter = func(resp map[string]interface{}) identities.SchemaGetter {\n\t\treturn func(_ context.Context, _ string) (map[string]interface{}, *http.Response, error) {\n\t\t\treturn resp, nil, nil\n\t\t}\n\t}\n\n\tfor i, tc := range []struct {\n\t\tdescription           string\n\t\tpayload               string\n\t\texpectedRequiredError string\n\t\tidentitySchema        map[string]interface{}\n\t}{\n\t\t{\n\t\t\tdescription:           \"requires schema_id from swagger schema\",\n\t\t\tpayload:               \"{}\",\n\t\t\texpectedRequiredError: \"schema_id\",\n\t\t},\n\t\t{\n\t\t\tdescription:           \"requires traits from swagger schema\",\n\t\t\tpayload:               `{\"schema_id\": \"case2\"}`,\n\t\t\texpectedRequiredError: \"traits\",\n\t\t},\n\t\t{\n\t\t\tdescription:           \"requires custom trait key\",\n\t\t\tpayload:               `{\"schema_id\": \"case3\", \"traits\": {}}`,\n\t\t\texpectedRequiredError: \"random-property\",\n\t\t\tidentitySchema: map[string]interface{}{\n\t\t\t\t\"type\":     \"object\",\n\t\t\t\t\"required\": []string{\"random-property\"},\n\t\t\t\t\"properties\": map[string]interface{}{\n\t\t\t\t\t\"random-property\": map[string]interface{}{\n\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d/description=%s\", i, tc.description), func(t *testing.T) {\n\t\t\tcmd, stdOut, stdErr := testCmd()\n\n\t\t\tif tc.identitySchema == nil {\n\t\t\t\ttc.identitySchema = map[string]interface{}{}\n\t\t\t}\n\t\t\terr := identities.ValidateIdentity(cmd, \"test identity\", tc.payload, testSchemaGetter(tc.identitySchema))\n\t\t\tassert.Error(t, err, stdOut.String(), stdErr.String())\n\t\t\tassert.Len(t, stdOut.String(), 0, stdErr.String())\n\t\t\tassert.Contains(t, stdErr.String(), \"required\", stdOut.String())\n\t\t\tassert.Contains(t, stdErr.String(), tc.expectedRequiredError, stdOut.String())\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "cmd/jsonnet/format.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnet\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/google/go-jsonnet/formatter\"\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/flagx\"\n)\n\nfunc NewFormatCmd() *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:   \"format\",\n\t\tShort: \"Helpers for formatting code\",\n\t}\n\tc.AddCommand(NewJsonnetFormatCmd())\n\treturn c\n}\n\nfunc NewJsonnetFormatCmd() *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse: \"jsonnet path/to/files/*.jsonnet [more/files.jsonnet] [supports/**/{foo,bar}.jsonnet]\",\n\t\tLong: `Formats JSONNet files using the official JSONNet formatter.\n\nUse -w or --write to write output back to files instead of stdout.\n\n` + GlobHelp,\n\t\tArgs: cobra.MinimumNArgs(1),\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tfor _, pattern := range args {\n\t\t\t\tfiles, err := filepath.Glob(pattern)\n\t\t\t\tcmdx.Must(err, `Glob path \"%s\" is not valid: %s`, pattern, err)\n\n\t\t\t\tshouldWrite := flagx.MustGetBool(cmd, \"write\")\n\t\t\t\tfor _, file := range files {\n\t\t\t\t\tcontent, err := os.ReadFile(file) // #nosec G304 -- the file is provided by the user\n\t\t\t\t\tcmdx.Must(err, `Unable to read file \"%s\" because: %s`, file, err)\n\n\t\t\t\t\toutput, err := formatter.Format(file, string(content), formatter.DefaultOptions())\n\t\t\t\t\tcmdx.Must(err, `JSONNet file \"%s\" could not be formatted: %s`, file, err)\n\n\t\t\t\t\tif shouldWrite {\n\t\t\t\t\t\terr := os.WriteFile(file, []byte(output), 0o644) //#nosec\n\t\t\t\t\t\tcmdx.Must(err, `Could not write to file \"%s\" because: %s`, file, err)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfmt.Println(output)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n\tc.Flags().BoolP(\"write\", \"w\", false, \"Write formatted output back to file.\")\n\treturn c\n}\n"
  },
  {
    "path": "cmd/jsonnet/lint.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnet\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/google/go-jsonnet\"\n\t\"github.com/google/go-jsonnet/linter\"\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n)\n\nfunc NewLintCmd() *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:   \"lint\",\n\t\tShort: \"Helpers for linting code\",\n\t}\n\tc.AddCommand(NewJsonnetLintCmd())\n\treturn c\n}\n\nfunc NewJsonnetLintCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse: \"lint path/to/files/*.jsonnet [more/files.jsonnet] [supports/**/{foo,bar}.jsonnet]\",\n\t\tLong: `Lints JSONNet files using the official JSONNet linter and exits with a status code of 1 when issues are detected.\n\n` + GlobHelp,\n\t\tArgs: cobra.MinimumNArgs(1),\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tvm := jsonnetsecure.MakeSecureVM().(*jsonnet.VM)\n\n\t\t\tfor _, pattern := range args {\n\t\t\t\tfiles, err := filepath.Glob(pattern)\n\t\t\t\tcmdx.Must(err, `Glob path \"%s\" is not valid: %s`, pattern, err)\n\n\t\t\t\tfor _, file := range files {\n\t\t\t\t\tcontent, err := os.ReadFile(file) // #nosec G304 -- file is supplied by user\n\t\t\t\t\tcmdx.Must(err, `Unable to read file \"%s\" because: %s`, file, err)\n\n\t\t\t\t\tvar outBuilder strings.Builder\n\t\t\t\t\terrorsFound := linter.LintSnippet(vm, &outBuilder, []linter.Snippet{{FileName: file, Code: string(content)}})\n\n\t\t\t\t\tif errorsFound {\n\t\t\t\t\t\t_, _ = fmt.Fprintf(os.Stderr, \"Linter found issues.\")\n\t\t\t\t\t\tos.Exit(1)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "cmd/jsonnet/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnet\n\nconst GlobHelp = `Glob Syntax:\n\n    pattern:\n        { term }\n\n    term:\n        '*'         matches any sequence of non-separator characters\n        '**'        matches any sequence of characters\n        '?'         matches any single non-separator character\n        '[' [ '!' ] { character-range } ']'\n                    character class (must be non-empty)\n        '{' pattern-list '}'\n                    pattern alternatives\n        c           matches character c (c != '*', '**', '?', '\\', '[', '{', '}')\n        '\\' c       matches character c\n\n    character-range:\n        c           matches character c (c != '\\\\', '-', ']')\n        '\\' c       matches character c\n        lo '-' hi   matches character c for lo <= c <= hi\n\n    pattern-list:\n        pattern { ',' pattern }\n                    comma-separated (without spaces) patterns`\n"
  },
  {
    "path": "cmd/migrate/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage migrate\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/popx\"\n)\n\nfunc NewMigrateCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"migrate\",\n\t\tShort: \"Various migration helpers\",\n\t}\n}\n\nfunc RegisterCommandRecursive(parent *cobra.Command) {\n\tc := NewMigrateCmd()\n\n\tconfigx.RegisterFlags(c.PersistentFlags())\n\tc.AddCommand(NewMigrateSQLCmd())\n\n\tparent.AddCommand(c)\n}\n\nfunc NewMigrateSQLDownCmd(opts ...driver.RegistryOption) *cobra.Command {\n\treturn popx.NewMigrateSQLDownCmd(func(cmd *cobra.Command, args []string) error {\n\t\treturn cliclient.NewMigrateHandler().MigrateSQLDown(cmd, args, opts...)\n\t})\n}\n\nfunc NewMigrateSQLUpCmd(opts ...driver.RegistryOption) *cobra.Command {\n\treturn popx.NewMigrateSQLUpCmd(func(cmd *cobra.Command, args []string) error {\n\t\treturn cliclient.NewMigrateHandler().MigrateSQLUp(cmd, args, opts...)\n\t})\n}\n\nfunc NewMigrateSQLStatusCmd(opts ...driver.RegistryOption) *cobra.Command {\n\treturn popx.NewMigrateSQLStatusCmd(func(cmd *cobra.Command, args []string) error {\n\t\treturn cliclient.NewMigrateHandler().MigrateSQLStatus(cmd, args, opts...)\n\t})\n}\n"
  },
  {
    "path": "cmd/migrate/sql.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage migrate\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/x/configx\"\n)\n\nfunc NewMigrateSQLCmd(opts ...driver.RegistryOption) *cobra.Command {\n\tc := &cobra.Command{\n\t\tUse:        \"sql <database-url>\",\n\t\tDeprecated: \"Please use `kratos migrate sql` instead.\",\n\t\tShort:      \"Create SQL schemas and apply migration plans\",\n\t\tLong: `Run this command on a fresh SQL installation and when you upgrade Ory Kratos to a new minor version.\n\nIt is recommended to run this command close to the SQL instance (e.g. same subnet) instead of over the public internet.\nThis decreases risk of failure and decreases time required.\n\nYou can read in the database URL using the -e flag, for example:\n\texport DSN=...\n\tkratos migrate sql -e\n\n### WARNING ###\n\nBefore running this command on an existing database, create a back up!\n`,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\treturn cliclient.NewMigrateHandler().MigrateSQLUp(cmd, args, opts...)\n\t\t},\n\t}\n\n\tconfigx.RegisterFlags(c.PersistentFlags())\n\tc.PersistentFlags().BoolP(\"read-from-env\", \"e\", false, \"If set, reads the database connection string from the environment variable DSN or config file key dsn.\")\n\tc.Flags().BoolP(\"yes\", \"y\", false, \"If set all confirmation requests are accepted without user interaction.\")\n\n\tc.AddCommand(NewMigrateSQLStatusCmd(opts...))\n\tc.AddCommand(NewMigrateSQLUpCmd(opts...))\n\tc.AddCommand(NewMigrateSQLDownCmd(opts...))\n\n\treturn c\n}\n"
  },
  {
    "path": "cmd/remote/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage remote\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/cmdx\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n)\n\nvar remoteCmd = &cobra.Command{\n\tUse:   \"remote\",\n\tShort: \"Helpers and management for remote Ory Kratos instances\",\n}\n\nfunc RegisterCommandRecursive(parent *cobra.Command) {\n\tparent.AddCommand(remoteCmd)\n\n\tremoteCmd.AddCommand(versionCmd)\n\tremoteCmd.AddCommand(statusCmd)\n}\n\nfunc init() {\n\tcliclient.RegisterClientFlags(remoteCmd.PersistentFlags())\n\tcmdx.RegisterFormatFlags(remoteCmd.PersistentFlags())\n}\n"
  },
  {
    "path": "cmd/remote/status.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage remote\n\nimport (\n\t\"github.com/ory/x/cmdx\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n)\n\ntype statusState struct {\n\tAlive bool `json:\"alive\"`\n\tReady bool `json:\"ready\"`\n}\n\nfunc (s *statusState) Header() []string {\n\treturn []string{\"ALIVE\", \"READY\"}\n}\n\nfunc (s *statusState) Columns() []string {\n\tf := [2]string{\n\t\t\"false\",\n\t\t\"false\",\n\t}\n\tif s.Alive {\n\t\tf[0] = \"true\"\n\t}\n\tif s.Ready {\n\t\tf[1] = \"true\"\n\t}\n\treturn f[:]\n}\n\nfunc (s *statusState) Interface() interface{} {\n\treturn s\n}\n\nvar statusCmd = &cobra.Command{\n\tUse:   \"status\",\n\tShort: \"Print the alive and readiness status of a Ory Kratos instance\",\n\tArgs:  cobra.NoArgs,\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\tc, err := cliclient.NewClient(cmd)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tstate := &statusState{}\n\t\tdefer cmdx.PrintRow(cmd, state)\n\n\t\talive, _, err := c.MetadataAPI.IsAlive(cmd.Context()).Execute()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tstate.Alive = alive.Status == \"ok\"\n\n\t\tready, _, err := c.MetadataAPI.IsReady(cmd.Context()).Execute()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tstate.Ready = ready.Status == \"ok\"\n\t\treturn nil\n\t},\n}\n"
  },
  {
    "path": "cmd/remote/version.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage remote\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/cliclient\"\n\t\"github.com/ory/x/cmdx\"\n)\n\ntype versionValue struct {\n\tVersion string `json:\"version\"`\n}\n\nfunc (v *versionValue) Header() []string {\n\treturn []string{\"VERSION\"}\n}\n\nfunc (v *versionValue) Columns() []string {\n\treturn []string{v.Version}\n}\n\nfunc (v *versionValue) Interface() interface{} {\n\treturn v\n}\n\nvar versionCmd = &cobra.Command{\n\tUse:   \"version\",\n\tShort: \"Print the version of an Ory Kratos instance\",\n\tArgs:  cobra.NoArgs,\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\tc, err := cliclient.NewClient(cmd)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tresp, _, err := c.MetadataAPI.GetVersion(cmd.Context()).Execute()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcmdx.PrintRow(cmd, &versionValue{Version: resp.Version})\n\t\treturn nil\n\t},\n}\n"
  },
  {
    "path": "cmd/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmd\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/cleanup\"\n\t\"github.com/ory/kratos/cmd/courier\"\n\t\"github.com/ory/kratos/cmd/hashers\"\n\t\"github.com/ory/kratos/cmd/identities\"\n\t\"github.com/ory/kratos/cmd/jsonnet\"\n\t\"github.com/ory/kratos/cmd/migrate\"\n\t\"github.com/ory/kratos/cmd/remote\"\n\t\"github.com/ory/kratos/cmd/serve\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/profilex\"\n)\n\nfunc NewRootCmd(driverOpts ...driver.RegistryOption) (cmd *cobra.Command) {\n\tcmd = &cobra.Command{\n\t\tUse: \"kratos\",\n\t}\n\tcmdx.EnableUsageTemplating(cmd)\n\n\tcourier.RegisterCommandRecursive(cmd, driverOpts)\n\tcmd.AddCommand(identities.NewGetCmd())\n\tcmd.AddCommand(identities.NewDeleteCmd())\n\tcmd.AddCommand(jsonnet.NewFormatCmd())\n\thashers.RegisterCommandRecursive(cmd)\n\tcmd.AddCommand(identities.NewImportCmd())\n\tcmd.AddCommand(jsonnet.NewLintCmd())\n\tcmd.AddCommand(identities.NewListCmd())\n\tmigrate.RegisterCommandRecursive(cmd)\n\tserve.RegisterCommandRecursive(cmd, driverOpts)\n\tcleanup.RegisterCommandRecursive(cmd)\n\tremote.RegisterCommandRecursive(cmd)\n\tcmd.AddCommand(identities.NewValidateCmd())\n\tcmd.AddCommand(cmdx.Version(&config.Version, &config.Commit, &config.Date))\n\n\t// Registers a hidden \"jsonnet\" subcommand for process-isolated Jsonnet VMs.\n\tcmd.AddCommand(jsonnetsecure.NewJsonnetCmd())\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() int {\n\tdefer profilex.Profile().Stop()\n\n\tjsonnetPool := jsonnetsecure.NewProcessPool(runtime.GOMAXPROCS(0))\n\tdefer jsonnetPool.Close()\n\n\tc := NewRootCmd(driver.WithJsonnetPool(jsonnetPool))\n\n\tif err := c.Execute(); err != nil {\n\t\tif !errors.Is(err, cmdx.ErrNoPrintButFail) {\n\t\t\t_, _ = fmt.Fprintln(c.ErrOrStderr(), err)\n\t\t}\n\t\treturn 1\n\t}\n\treturn 0\n}\n"
  },
  {
    "path": "cmd/root_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmd\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ory/x/cmdx\"\n)\n\nfunc TestUsageStrings(t *testing.T) {\n\tcmdx.AssertUsageTemplates(t, NewRootCmd())\n}\n"
  },
  {
    "path": "cmd/serve/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage serve\n\nimport (\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/kratos/cmd/daemon\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/configx\"\n)\n\n// NewServeCmd returns the serve command\nfunc NewServeCmd(dOpts ...driver.RegistryOption) (serveCmd *cobra.Command) {\n\tserveCmd = &cobra.Command{\n\t\tUse:   \"serve\",\n\t\tShort: \"Run the Ory Kratos server\",\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tctx := cmd.Context()\n\t\t\td, err := driver.New(ctx, cmd.ErrOrStderr(), append(dOpts, driver.WithConfigOptions(configx.WithFlags(cmd.Flags())))...)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif d.Config().IsInsecureDevMode(ctx) {\n\t\t\t\td.Logger().Warn(`\n\nYOU ARE RUNNING Ory KRATOS IN DEV MODE.\nSECURITY IS DISABLED.\nDON'T DO THIS IN PRODUCTION!\n\n`)\n\t\t\t}\n\n\t\t\tconfigVersion := d.Config().ConfigVersion(ctx)\n\t\t\tif configVersion == config.UnknownVersion {\n\t\t\t\td.Logger().Warn(\"The config has no version specified. Add the version to improve your development experience.\")\n\t\t\t} else if config.Version != \"\" &&\n\t\t\t\tconfigVersion != config.Version {\n\t\t\t\td.Logger().Warnf(\"Config version is '%s' but kratos runs on version '%s'\", configVersion, config.Version)\n\t\t\t}\n\n\t\t\treturn daemon.ServeAll(d)(cmd, args)\n\t\t},\n\t}\n\tconfigx.RegisterFlags(serveCmd.PersistentFlags())\n\n\tserveCmd.PersistentFlags().Bool(\"sqa-opt-out\", false, \"Disable anonymized telemetry reports - for more information please visit https://www.ory.sh/docs/ecosystem/sqa\")\n\tserveCmd.PersistentFlags().Bool(\"dev\", false, \"Disables critical security features to make development easier\")\n\tserveCmd.PersistentFlags().Bool(\"watch-courier\", false, \"Run the message courier as a background task, to simplify single-instance setup\")\n\treturn serveCmd\n}\n\nfunc RegisterCommandRecursive(parent *cobra.Command, dOpts []driver.RegistryOption) {\n\tparent.AddCommand(NewServeCmd(dOpts...))\n}\n"
  },
  {
    "path": "cmd/serve/root_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage serve_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n)\n\nfunc TestServe(t *testing.T) {\n\t_, _ = testhelpers.StartE2EServer(t, \"./stub/kratos.yml\", nil)\n}\n\nfunc TestServeTLSBase64(t *testing.T) {\n\t_, _, certBase64, keyBase64 := testhelpers.GenerateTLSCertificateFilesForTests(t)\n\tpublicPort, adminPort := testhelpers.StartE2EServerOnly(t,\n\t\t\"./stub/kratos.yml\",\n\t\ttrue,\n\t\ttesthelpers.ConfigOptions{\n\t\t\t\"serve.public.tls.key.base64\":  keyBase64,\n\t\t\t\"serve.public.tls.cert.base64\": certBase64,\n\t\t\t\"serve.admin.tls.key.base64\":   keyBase64,\n\t\t\t\"serve.admin.tls.cert.base64\":  certBase64,\n\t\t},\n\t)\n\ttesthelpers.CheckE2EServerOnHTTPS(t, publicPort, adminPort)\n}\n\nfunc TestServeTLSPaths(t *testing.T) {\n\tcertPath, keyPath, _, _ := testhelpers.GenerateTLSCertificateFilesForTests(t)\n\n\tpublicPort, adminPort := testhelpers.StartE2EServerOnly(t,\n\t\t\"./stub/kratos.yml\",\n\t\ttrue,\n\t\ttesthelpers.ConfigOptions{\n\t\t\t\"serve.public.tls.key.path\":  keyPath,\n\t\t\t\"serve.public.tls.cert.path\": certPath,\n\t\t\t\"serve.admin.tls.key.path\":   keyPath,\n\t\t\t\"serve.admin.tls.cert.path\":  certPath,\n\t\t},\n\t)\n\ttesthelpers.CheckE2EServerOnHTTPS(t, publicPort, adminPort)\n}\n"
  },
  {
    "path": "cmd/serve/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"first\": {\n              \"title\": \"First Name\",\n              \"type\": \"string\"\n            },\n            \"last\": {\n              \"title\": \"Last Name\",\n              \"type\": \"string\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "cmd/serve/stub/kratos.yml",
    "content": "dsn: memory\n\nserve:\n  public:\n    base_url: http://127.0.0.1:4433/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nselfservice:\n  default_browser_return_url: http://127.0.0.1:4455/\n  allowed_return_urls:\n    - http://127.0.0.1:4455\n\n  methods:\n    password:\n      enabled: true\n\n  flows:\n    error:\n      ui_url: http://127.0.0.1:4455/error\n\n    settings:\n      ui_url: http://127.0.0.1:4455/settings\n      privileged_session_max_age: 15m\n\n    recovery:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/recovery\n\n    verification:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/verify\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/\n\n    logout:\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/auth/login\n\n    login:\n      ui_url: http://127.0.0.1:4455/auth/login\n      lifespan: 10m\n\n    registration:\n      lifespan: 10m\n      ui_url: http://127.0.0.1:4455/auth/registration\n      after:\n        password:\n          hooks:\n            -\n              hook: session\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n  pagination:\n    - \"test pagination secret\"\n\nhashers:\n  argon2:\n    parallelism: 1\n    memory: 128MB\n    iterations: 2\n    salt_length: 16\n    key_length: 16\n\nidentity:\n  schemas:\n    - id: default\n      url: file://stub/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true&legacy_ssl=true\n"
  },
  {
    "path": "codecov.yml",
    "content": "coverage:\n  status:\n    project:\n      default:\n        target: 60%\n        threshold: 10%\n        only_pulls: true\nignore:\n  - \"test\"\n  - \"pkg\"\n  - \"docs\"\n  - \"proto\"\n  - \"gen\"\n  - \"examples\"\n  - \"contrib\"\n  - \"selfservice/strategy/oidc/provider_netid.go\" # No way to test this provider automatically\n  - \"**/*_test.go\"\n"
  },
  {
    "path": "continuity/container.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage continuity\n\nimport (\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/pointerx\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\ntype Container struct {\n\tID         uuid.UUID  `json:\"id\" db:\"id\" rw:\"r\"`\n\tNID        uuid.UUID  `json:\"-\" db:\"nid\"`\n\tName       string     `json:\"name\" db:\"name\"`\n\tIdentityID *uuid.UUID `json:\"identity_id\" db:\"identity_id\"`\n\n\t// ExpiresAt defines when this container expires.\n\tExpiresAt time.Time `json:\"expires_at\" db:\"expires_at\"`\n\n\t// Payload is the container's payload.\n\tPayload sqlxx.NullJSONRawMessage `json:\"payload\" db:\"payload\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"created_at\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"updated_at\" db:\"updated_at\"`\n}\n\nfunc (c *Container) UTC() *Container {\n\tc.CreatedAt = c.CreatedAt.UTC()\n\tc.UpdatedAt = c.UpdatedAt.UTC()\n\tc.ExpiresAt = c.ExpiresAt.UTC()\n\treturn c\n}\n\nfunc (Container) TableName() string { return \"continuity_containers\" }\n\nfunc NewContainer(name string, o managerOptions) *Container {\n\treturn &Container{\n\t\tID:         uuid.Nil,\n\t\tName:       name,\n\t\tIdentityID: x.PointToUUID(o.iid),\n\t\tExpiresAt:  time.Now().Add(o.ttl).UTC().Truncate(time.Second),\n\t\tPayload:    sqlxx.NullJSONRawMessage(o.payload),\n\t}\n}\n\nfunc (c *Container) Valid(identity uuid.UUID) error {\n\tif c.ExpiresAt.Before(time.Now()) {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"You must restart the flow because the resumable session has expired.\"))\n\t}\n\n\tif identity != uuid.Nil && pointerx.Deref(c.IdentityID) != identity {\n\t\treturn errors.WithStack(herodot.ErrForbidden.WithReasonf(\"The flow has been blocked for security reasons because it was initiated by another person..\"))\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "continuity/container_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage continuity\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestContainer(t *testing.T) {\n\tid := x.NewUUID()\n\tfor k, tc := range []struct {\n\t\tc    *Container\n\t\ti    uuid.UUID\n\t\tpass bool\n\t}{\n\t\t{\n\t\t\tc: &Container{\n\t\t\t\tExpiresAt: time.Now().Add(-time.Minute),\n\t\t\t},\n\t\t\tpass: false,\n\t\t},\n\t\t{\n\t\t\tc: &Container{\n\t\t\t\tExpiresAt: time.Now().Add(-time.Minute),\n\t\t\t},\n\t\t\ti:    x.NewUUID(),\n\t\t\tpass: false,\n\t\t},\n\t\t{\n\t\t\tc: &Container{\n\t\t\t\tIdentityID: x.PointToUUID(x.NewUUID()),\n\t\t\t\tExpiresAt:  time.Now().Add(-time.Minute),\n\t\t\t},\n\t\t\ti:    x.NewUUID(),\n\t\t\tpass: false,\n\t\t},\n\t\t{\n\t\t\tc: &Container{\n\t\t\t\tExpiresAt: time.Now().Add(time.Minute),\n\t\t\t},\n\t\t\tpass: true,\n\t\t},\n\t\t{\n\t\t\tc: &Container{\n\t\t\t\tIdentityID: x.PointToUUID(id),\n\t\t\t\tExpiresAt:  time.Now().Add(time.Minute),\n\t\t\t},\n\t\t\ti:    id,\n\t\t\tpass: true,\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\terr := tc.c.Valid(tc.i)\n\t\t\tif tc.pass {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t} else {\n\t\t\t\trequire.Error(t, err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "continuity/manager.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage continuity\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\ntype ManagementProvider interface {\n\tContinuityManager() Manager\n}\n\ntype Manager interface {\n\tPause(ctx context.Context, w http.ResponseWriter, r *http.Request, name string, opts ...ManagerOption) error\n\tContinue(ctx context.Context, w http.ResponseWriter, r *http.Request, name string, opts ...ManagerOption) (*Container, error)\n\tAbort(ctx context.Context, w http.ResponseWriter, r *http.Request, name string) error\n}\n\ntype managerOptions struct {\n\tiid          uuid.UUID\n\tttl          time.Duration\n\tsetExpiresIn time.Duration\n\tpayload      json.RawMessage\n\tpayloadRaw   interface{}\n}\n\ntype ManagerOption func(*managerOptions) error\n\nfunc newManagerOptions(opts []ManagerOption) (*managerOptions, error) {\n\tvar o = &managerOptions{\n\t\tttl: time.Minute * 10,\n\t}\n\tfor _, opt := range opts {\n\t\tif err := opt(o); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn o, nil\n}\n\nfunc WithIdentity(i *identity.Identity) ManagerOption {\n\treturn func(o *managerOptions) error {\n\t\tif i != nil {\n\t\t\to.iid = i.ID\n\t\t}\n\t\treturn nil\n\t}\n}\n\nfunc WithLifespan(ttl time.Duration) ManagerOption {\n\treturn func(o *managerOptions) error {\n\t\to.ttl = ttl\n\t\treturn nil\n\t}\n}\n\nfunc WithPayload(payload interface{}) ManagerOption {\n\treturn func(o *managerOptions) error {\n\t\tvar b bytes.Buffer\n\t\tif err := json.NewEncoder(&b).Encode(payload); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\to.payload = b.Bytes()\n\t\to.payloadRaw = payload\n\t\treturn nil\n\t}\n}\n\nfunc WithExpireInsteadOfDelete(duration time.Duration) ManagerOption {\n\treturn func(o *managerOptions) error {\n\t\to.setExpiresIn = duration\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "continuity/manager_cookie.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage continuity\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n)\n\nvar (\n\t_               Manager = new(ManagerCookie)\n\tErrNotResumable         = *herodot.ErrBadRequest.WithError(\"no resumable session found\").WithReasonf(\"The browser does not contain the necessary cookie to resume the session. This is a security violation and was blocked. Please clear your browser's cookies and cache and try again!\")\n)\n\nconst CookieName = \"ory_kratos_continuity\"\n\ntype (\n\tmanagerCookieDependencies interface {\n\t\tPersistenceProvider\n\t\tx.CookieProvider\n\t\tsession.ManagementProvider\n\t\totelx.Provider\n\t}\n\tManagerCookie struct {\n\t\td managerCookieDependencies\n\t}\n)\n\nfunc NewManagerCookie(d managerCookieDependencies) *ManagerCookie {\n\treturn &ManagerCookie{d: d}\n}\n\nfunc (m *ManagerCookie) Pause(ctx context.Context, w http.ResponseWriter, r *http.Request, name string, opts ...ManagerOption) (err error) {\n\tctx, span := m.d.Tracer(ctx).Tracer().Start(ctx, \"continuity.ManagerCookie.Pause\")\n\tdefer otelx.End(span, &err)\n\tif len(name) == 0 {\n\t\treturn errors.Errorf(\"continuity container name must be set\")\n\t}\n\n\to, err := newManagerOptions(opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc := NewContainer(name, *o)\n\n\tif err := m.d.ContinuityPersister().SaveContinuitySession(ctx, c); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif err := x.SessionPersistValues(w, r, m.d.ContinuityCookieManager(ctx), CookieName, map[string]interface{}{\n\t\tname: c.ID.String(),\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (m *ManagerCookie) Continue(ctx context.Context, w http.ResponseWriter, r *http.Request, name string, opts ...ManagerOption) (container *Container, err error) {\n\tctx, span := m.d.Tracer(ctx).Tracer().Start(ctx, \"continuity.ManagerCookie.Continue\")\n\tdefer otelx.End(span, &err)\n\n\tcontainer, err = m.container(ctx, w, r, name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\to, err := newManagerOptions(opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := container.Valid(o.iid); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif o.payloadRaw != nil && container.Payload != nil {\n\t\tif err := json.NewDecoder(bytes.NewBuffer(container.Payload)).Decode(o.payloadRaw); err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t}\n\n\tif o.setExpiresIn > 0 {\n\t\tif err := m.d.ContinuityPersister().SetContinuitySessionExpiry(\n\t\t\tctx,\n\t\t\tcontainer.ID,\n\t\t\ttime.Now().UTC().Add(o.setExpiresIn).Truncate(time.Second),\n\t\t); err != nil && !errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tif err := x.SessionUnsetKey(w, r, m.d.ContinuityCookieManager(ctx), CookieName, name); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err := m.d.ContinuityPersister().DeleteContinuitySession(ctx, container.ID); err != nil && !errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn container, nil\n}\n\nfunc (m *ManagerCookie) sessionID(ctx context.Context, w http.ResponseWriter, r *http.Request, name string) (uuid.UUID, error) {\n\ts, err := x.SessionGetString(r, m.d.ContinuityCookieManager(ctx), CookieName, name)\n\tif err != nil {\n\t\t_ = x.SessionUnsetKey(w, r, m.d.ContinuityCookieManager(ctx), CookieName, name)\n\t\treturn uuid.Nil, errors.WithStack(ErrNotResumable.WithDebugf(\"%+v\", err))\n\t}\n\n\tsid, err := uuid.FromString(s)\n\tif err != nil {\n\t\t_ = x.SessionUnsetKey(w, r, m.d.ContinuityCookieManager(ctx), CookieName, name)\n\t\treturn uuid.Nil, errors.WithStack(ErrNotResumable.WithDebug(\"session id is not a valid uuid\"))\n\t}\n\n\treturn sid, nil\n}\n\nfunc (m *ManagerCookie) container(ctx context.Context, w http.ResponseWriter, r *http.Request, name string) (*Container, error) {\n\tsid, err := m.sessionID(ctx, w, r, name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcontainer, err := m.d.ContinuityPersister().GetContinuitySession(ctx, sid)\n\t// If an error happens, we need to clean up the cookie.\n\tif err != nil {\n\t\t_ = x.SessionUnsetKey(w, r, m.d.ContinuityCookieManager(ctx), CookieName, name)\n\t}\n\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\treturn nil, errors.WithStack(ErrNotResumable.WithDebugf(\"Resumable ID from cookie could not be found in the datastore: %+v\", err))\n\t} else if err != nil {\n\t\treturn nil, err\n\t} else if container.ExpiresAt.Before(time.Now()) {\n\t\t_ = x.SessionUnsetKey(w, r, m.d.ContinuityCookieManager(ctx), CookieName, name)\n\t\treturn nil, errors.WithStack(ErrNotResumable.WithDebugf(\"Resumable session has expired\"))\n\t}\n\n\treturn container, err\n}\n\nfunc (m ManagerCookie) Abort(ctx context.Context, w http.ResponseWriter, r *http.Request, name string) (err error) {\n\tctx, span := m.d.Tracer(ctx).Tracer().Start(ctx, \"continuity.ManagerCookie.Abort\")\n\tdefer otelx.End(span, &err)\n\tsid, err := m.sessionID(ctx, w, r, name)\n\tif errors.Is(err, &ErrNotResumable) {\n\t\t// We do not care about an error here\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn err\n\t}\n\n\tif err := x.SessionUnsetKey(w, r, m.d.ContinuityCookieManager(ctx), CookieName, name); err != nil {\n\t\treturn err\n\t}\n\n\tif err := m.d.ContinuityPersister().DeleteContinuitySession(ctx, sid); err != nil && !errors.Is(err, sqlcon.ErrNoRows) {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "continuity/manager_options_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage continuity\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestManagerOptions(t *testing.T) {\n\tfor k, tc := range []struct {\n\t\terr  bool\n\t\te    func(t *testing.T, actual *managerOptions)\n\t\topts []ManagerOption\n\t}{\n\t\t{\n\t\t\te: func(t *testing.T, actual *managerOptions) {\n\t\t\t\tassert.EqualValues(t, time.Minute*10, actual.ttl)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\topts: []ManagerOption{WithLifespan(time.Minute * 5)},\n\t\t\te: func(t *testing.T, actual *managerOptions) {\n\t\t\t\tassert.EqualValues(t, time.Minute*5, actual.ttl)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\topts: []ManagerOption{WithPayload(map[string]interface{}{\"foo\": \"bar\"})},\n\t\t\te: func(t *testing.T, actual *managerOptions) {\n\t\t\t\tassert.EqualValues(t, map[string]interface{}{\"foo\": \"bar\"}, actual.payloadRaw)\n\t\t\t\tassert.JSONEq(t, `{\"foo\":\"bar\"}`, string(actual.payload))\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\to, err := newManagerOptions(tc.opts)\n\t\t\tif tc.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err)\n\t\t\ttc.e(t, o)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "continuity/manager_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage continuity_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\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/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/logrusx\"\n)\n\ntype persisterTestCase struct {\n\tro          []continuity.ManagerOption\n\two          []continuity.ManagerOption\n\texpected    *persisterTestPayload\n\texpectedErr error\n}\n\ntype persisterTestPayload struct {\n\tFoo string `json:\"foo\"`\n}\n\nfunc TestManager(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://../test/stub/identity/empty.schema.json\")),\n\t)\n\n\ti := identity.NewIdentity(\"\")\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(t.Context(), i))\n\n\tnewServer := func(t *testing.T, p continuity.Manager, tc *persisterTestCase) *httptest.Server {\n\t\twriter := herodot.NewJSONWriter(logrusx.New(\"\", \"\"))\n\t\trouter := http.NewServeMux()\n\t\trouter.HandleFunc(\"PUT /{name}\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif err := p.Pause(r.Context(), w, r, r.PathValue(\"name\"), tc.ro...); err != nil {\n\t\t\t\twriter.WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\n\t\trouter.HandleFunc(\"POST /{name}\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif err := p.Pause(r.Context(), w, r, r.PathValue(\"name\"), tc.ro...); err != nil {\n\t\t\t\twriter.WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tc, err := p.Continue(r.Context(), w, r, r.PathValue(\"name\"), tc.wo...)\n\t\t\tif err != nil {\n\t\t\t\twriter.WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\twriter.Write(w, r, c)\n\t\t})\n\n\t\trouter.HandleFunc(\"GET /{name}\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\tc, err := p.Continue(r.Context(), w, r, r.PathValue(\"name\"), tc.ro...)\n\t\t\tif err != nil {\n\t\t\t\twriter.WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\twriter.Write(w, r, c)\n\t\t})\n\n\t\trouter.HandleFunc(\"DELETE /{name}\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\terr := p.Abort(r.Context(), w, r, r.PathValue(\"name\"))\n\t\t\tif err != nil {\n\t\t\t\twriter.WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\n\t\tts := httptest.NewServer(router)\n\t\tt.Cleanup(ts.Close)\n\t\treturn ts\n\t}\n\n\tnewClient := func() *http.Client {\n\t\treturn &http.Client{Jar: testhelpers.EasyCookieJar(t, nil)}\n\t}\n\n\tp := reg.ContinuityManager()\n\tcl := newClient()\n\n\tt.Run(\"case=continue cookie resets when signature is invalid\", func(t *testing.T) {\n\t\tts := newServer(t, p, new(persisterTestCase))\n\t\thref := ts.URL + \"/\" + x.NewUUID().String()\n\n\t\tres, err := cl.Do(testhelpers.NewTestHTTPRequest(t, \"PUT\", href, nil))\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil)\n\t\trequire.Len(t, res.Cookies(), 1)\n\t\tfor _, c := range res.Cookies() {\n\t\t\t// Change something in the string\n\t\t\tc.Value = strings.Replace(c.Value, \"a\", \"b\", 1)\n\t\t\treq.AddCookie(c)\n\t\t}\n\t\tres, err = http.DefaultClient.Do(req)\n\t\trequire.NoError(t, err)\n\t\tt.Cleanup(func() { require.NoError(t, res.Body.Close()) })\n\n\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"error.reason\").String(), continuity.ErrNotResumable.ReasonField)\n\n\t\trequire.Len(t, res.Cookies(), 1, \"continuing the flow with a broken cookie should instruct the browser to forget it\")\n\t\tassert.EqualValues(t, res.Cookies()[0].Name, continuity.CookieName)\n\t})\n\n\tt.Run(\"case=can deal with duplicate cookies\", func(t *testing.T) {\n\t\ttc := &persisterTestCase{expected: &persisterTestPayload{\"bar\"}}\n\t\tts := newServer(t, p, tc)\n\t\thref := ts.URL + \"/\" + x.NewUUID().String()\n\n\t\tres, err := http.DefaultClient.Do(testhelpers.NewTestHTTPRequest(t, \"PUT\", href, nil))\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\t// We change the key to another one\n\t\thref = ts.URL + \"/\" + x.NewUUID().String()\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil)\n\t\trequire.Len(t, res.Cookies(), 1)\n\t\tfor _, c := range res.Cookies() {\n\t\t\treq.AddCookie(c)\n\t\t}\n\n\t\ttc.ro = []continuity.ManagerOption{continuity.WithPayload(&persisterTestPayload{\"bar\"})}\n\t\tres, err = http.DefaultClient.Do(testhelpers.NewTestHTTPRequest(t, \"PUT\", href, nil))\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\trequire.Len(t, res.Cookies(), 1)\n\t\tfor _, c := range res.Cookies() {\n\t\t\treq.AddCookie(c)\n\t\t}\n\n\t\tres, err = http.DefaultClient.Do(req)\n\t\trequire.NoError(t, err)\n\t\tt.Cleanup(func() { require.NoError(t, res.Body.Close()) })\n\n\t\trequire.Len(t, res.Cookies(), 1, \"continuing the flow with a broken cookie should instruct the browser to forget it\")\n\t\tassert.EqualValues(t, res.Cookies()[0].Name, continuity.CookieName)\n\n\t\tvar b bytes.Buffer\n\t\trequire.NoError(t, json.NewEncoder(&b).Encode(tc.expected))\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\tassert.JSONEq(t, b.String(), gjson.GetBytes(body, \"payload\").Raw, \"%s\", body)\n\t\tassert.Contains(t, href, gjson.GetBytes(body, \"name\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=pause and use session with expiry\", func(t *testing.T) {\n\t\tcl := newClient()\n\n\t\ttc := &persisterTestCase{\n\t\t\tro: []continuity.ManagerOption{continuity.WithPayload(&persisterTestPayload{\"bar\"}), continuity.WithExpireInsteadOfDelete(time.Minute)},\n\t\t\two: []continuity.ManagerOption{continuity.WithPayload(&persisterTestPayload{}), continuity.WithExpireInsteadOfDelete(time.Minute)},\n\t\t}\n\t\tts := newServer(t, p, tc)\n\t\tgenid := func() string {\n\t\t\treturn ts.URL + \"/\" + x.NewUUID().String()\n\t\t}\n\n\t\thref := genid()\n\t\tres, err := cl.Do(testhelpers.NewTestHTTPRequest(t, \"PUT\", href, nil))\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\tres, err = cl.Do(testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil))\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\tres, err = cl.Do(testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil))\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\ttc.ro = []continuity.ManagerOption{continuity.WithPayload(&persisterTestPayload{\"bar\"}), continuity.WithExpireInsteadOfDelete(-time.Minute)}\n\t\ttc.wo = []continuity.ManagerOption{continuity.WithPayload(&persisterTestPayload{\"\"}), continuity.WithExpireInsteadOfDelete(-time.Minute)}\n\n\t\tres, err = cl.Do(testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil))\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\tres, err = cl.Do(testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil))\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\t\tassert.Contains(t, gjson.GetBytes(body, \"error.reason\").String(), continuity.ErrNotResumable.ReasonField)\n\t})\n\n\tfor k, tc := range []persisterTestCase{\n\t\t{},\n\t\t{\n\t\t\tro:       []continuity.ManagerOption{continuity.WithPayload(&persisterTestPayload{\"bar\"})},\n\t\t\two:       []continuity.ManagerOption{continuity.WithPayload(&persisterTestPayload{})},\n\t\t\texpected: &persisterTestPayload{\"bar\"},\n\t\t},\n\t\t{\n\t\t\tro: []continuity.ManagerOption{continuity.WithIdentity(i)},\n\t\t\two: []continuity.ManagerOption{continuity.WithIdentity(i)},\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tcl := newClient()\n\t\t\tts := newServer(t, p, &tc)\n\t\t\tgenid := func() string {\n\t\t\t\treturn ts.URL + \"/\" + x.NewUUID().String()\n\t\t\t}\n\n\t\t\tt.Run(\"case=resume non-existing session\", func(t *testing.T) {\n\t\t\t\thref := genid()\n\t\t\t\tres, err := cl.Do(testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tt.Cleanup(func() { require.NoError(t, res.Body.Close()) })\n\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"error.reason\").String(), continuity.ErrNotResumable.ReasonField)\n\t\t\t})\n\n\t\t\tt.Run(\"case=pause and resume session\", func(t *testing.T) {\n\t\t\t\thref := genid()\n\t\t\t\tres, err := cl.Do(testhelpers.NewTestHTTPRequest(t, \"PUT\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\t\t\tres, err = cl.Do(testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tt.Cleanup(func() { require.NoError(t, res.Body.Close()) })\n\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\tif tc.expectedErr != nil {\n\t\t\t\t\trequire.Equal(t, http.StatusGone, res.StatusCode, \"%s\", body)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\n\t\t\t\tvar b bytes.Buffer\n\t\t\t\trequire.NoError(t, json.NewEncoder(&b).Encode(tc.expected))\n\t\t\t\tassert.JSONEq(t, b.String(), gjson.GetBytes(body, \"payload\").Raw, \"%s\", body)\n\t\t\t\tassert.Contains(t, href, gjson.GetBytes(body, \"name\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=pause and retry session\", func(t *testing.T) {\n\t\t\t\thref := genid()\n\t\t\t\tres, err := cl.Do(testhelpers.NewTestHTTPRequest(t, \"PUT\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\t\t\tres, err = cl.Do(testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tt.Cleanup(func() { require.NoError(t, res.Body.Close()) })\n\n\t\t\t\tres, err = cl.Do(testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\tt.Cleanup(func() { require.NoError(t, res.Body.Close()) })\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"error.reason\").String(), continuity.ErrNotResumable.ReasonField)\n\t\t\t})\n\n\t\t\tt.Run(\"case=pause and resume session in the same request\", func(t *testing.T) {\n\t\t\t\thref := genid()\n\t\t\t\tres, err := cl.Do(testhelpers.NewTestHTTPRequest(t, \"POST\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tt.Cleanup(func() { require.NoError(t, res.Body.Close()) })\n\n\t\t\t\tvar b bytes.Buffer\n\t\t\t\trequire.NoError(t, json.NewEncoder(&b).Encode(tc.expected))\n\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\tassert.JSONEq(t, b.String(), gjson.GetBytes(body, \"payload\").Raw, \"%s\", body)\n\t\t\t\tassert.Contains(t, href, gjson.GetBytes(body, \"name\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=pause, abort, and continue session with failure\", func(t *testing.T) {\n\t\t\t\thref := genid()\n\t\t\t\tres, err := cl.Do(testhelpers.NewTestHTTPRequest(t, \"PUT\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\t\t\tres, err = cl.Do(testhelpers.NewTestHTTPRequest(t, \"DELETE\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tt.Cleanup(func() { require.NoError(t, res.Body.Close()) })\n\t\t\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\t\t\tres, err = cl.Do(testhelpers.NewTestHTTPRequest(t, \"GET\", href, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tt.Cleanup(func() { require.NoError(t, res.Body.Close()) })\n\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"error.reason\").String(), continuity.ErrNotResumable.ReasonField)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "continuity/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage continuity\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype PersistenceProvider interface {\n\tContinuityPersister() Persister\n}\n\ntype Persister interface {\n\tSaveContinuitySession(ctx context.Context, c *Container) error\n\tGetContinuitySession(ctx context.Context, id uuid.UUID) (*Container, error)\n\tDeleteContinuitySession(ctx context.Context, id uuid.UUID) error\n\tSetContinuitySessionExpiry(ctx context.Context, id uuid.UUID, expiresAt time.Time) error\n\tDeleteExpiredContinuitySessions(ctx context.Context, deleteOlder time.Time, pageSize int) error\n}\n"
  },
  {
    "path": "continuity/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc TestPersister(ctx context.Context, p interface {\n\tpersistence.Persister\n\tcontinuity.Persister\n\tidentity.PrivilegedPool\n}) func(t *testing.T) {\n\tvar createIdentity = func(t *testing.T) *identity.Identity {\n\t\tid := identity.Identity{}\n\t\trequire.NoError(t, p.CreateIdentity(ctx, &id))\n\t\treturn &id\n\t}\n\n\tvar createContainer = func(t *testing.T) continuity.Container {\n\t\tm := sqlxx.NullJSONRawMessage(`{\"foo\": \"bar\"}`)\n\t\treturn continuity.Container{Name: \"foo\", IdentityID: x.PointToUUID(createIdentity(t).ID),\n\t\t\tExpiresAt: time.Now().Add(time.Hour).UTC().Truncate(time.Second),\n\t\t\tPayload:   m,\n\t\t}\n\t}\n\n\treturn func(t *testing.T) {\n\t\tnid, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tt.Run(\"case=not found\", func(t *testing.T) {\n\t\t\t_, err := p.GetContinuitySession(ctx, x.NewUUID())\n\t\t\trequire.EqualError(t, err, sqlcon.ErrNoRows.Error())\n\t\t})\n\n\t\tt.Run(\"case=save and find\", func(t *testing.T) {\n\t\t\texpected := createContainer(t)\n\t\t\trequire.NoError(t, p.SaveContinuitySession(ctx, &expected))\n\n\t\t\tactual, err := p.GetContinuitySession(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tactual.UpdatedAt, actual.CreatedAt, expected.UpdatedAt, expected.CreatedAt = time.Time{}, time.Time{}, time.Time{}, time.Time{}\n\t\t\tassert.EqualValues(t, expected.UTC(), actual.UTC())\n\t\t})\n\n\t\tt.Run(\"case=save and delete\", func(t *testing.T) {\n\t\t\texpected := createContainer(t)\n\n\t\t\trequire.NoError(t, p.SaveContinuitySession(ctx, &expected))\n\t\t\trequire.NotEqual(t, uuid.Nil, expected.ID)\n\t\t\trequire.NoError(t, p.DeleteContinuitySession(ctx, expected.ID))\n\n\t\t\t_, err := p.GetContinuitySession(ctx, expected.ID)\n\t\t\trequire.EqualError(t, err, sqlcon.ErrNoRows.Error())\n\t\t})\n\n\t\tt.Run(\"case=network\", func(t *testing.T) {\n\t\t\tid := x.NewUUID()\n\n\t\t\tt.Run(\"sets id on creation\", func(t *testing.T) {\n\t\t\t\texpected := createContainer(t)\n\t\t\t\texpected.ID = id\n\t\t\t\trequire.NoError(t, p.SaveContinuitySession(ctx, &expected))\n\n\t\t\t\tassert.EqualValues(t, id, expected.ID)\n\t\t\t\tassert.EqualValues(t, nid, expected.NID)\n\n\t\t\t\tactual, err := p.GetContinuitySession(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, id, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\t\t\t})\n\n\t\t\tt.Run(\"can not get on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetLoginFlow(ctx, id)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\n\t\t\tt.Run(\"can not delete on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\terr := p.DeleteContinuitySession(ctx, id)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=set expiry\", func(t *testing.T) {\n\t\t\t// Create a new continuity session\n\t\t\texpected := createContainer(t)\n\t\t\trequire.NoError(t, p.SaveContinuitySession(ctx, &expected))\n\n\t\t\t// Set the expiry of the continuity session\n\t\t\tnewExpiry := time.Now().Add(48 * time.Hour).UTC().Truncate(time.Second)\n\t\t\trequire.NoError(t, p.SetContinuitySessionExpiry(ctx, expected.ID, newExpiry))\n\n\t\t\t// Retrieve the continuity session\n\t\t\tactual, err := p.GetContinuitySession(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// Check if the expiry has been updated\n\t\t\tassert.EqualValues(t, newExpiry, actual.ExpiresAt)\n\n\t\t\tt.Run(\"can not update on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\tnewExpiry := time.Now().Add(12 * time.Hour).UTC().Truncate(time.Second)\n\t\t\t\terr := p.SetContinuitySessionExpiry(ctx, expected.ID, newExpiry)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=cleanup\", func(t *testing.T) {\n\t\t\tid := x.NewUUID()\n\t\t\tyesterday := time.Now().Add(-24 * time.Hour).UTC().Truncate(time.Second)\n\t\t\tm := sqlxx.NullJSONRawMessage(`{\"foo\": \"bar\"}`)\n\t\t\texpected := continuity.Container{Name: \"foo\", IdentityID: x.PointToUUID(createIdentity(t).ID),\n\t\t\t\tExpiresAt: yesterday,\n\t\t\t\tPayload:   m,\n\t\t\t}\n\t\t\texpected.ID = id\n\n\t\t\tt.Run(\"can cleanup\", func(t *testing.T) {\n\t\t\t\trequire.NoError(t, p.SaveContinuitySession(ctx, &expected))\n\n\t\t\t\tassert.EqualValues(t, id, expected.ID)\n\t\t\t\tassert.EqualValues(t, nid, expected.NID)\n\n\t\t\t\trequire.NoError(t, p.DeleteExpiredContinuitySessions(ctx, time.Now(), 5))\n\n\t\t\t\t_, err := p.GetContinuitySession(ctx, id)\n\t\t\t\trequire.Error(t, err)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "contrib/quickstart/.dockerignore",
    "content": "*"
  },
  {
    "path": "contrib/quickstart/kratos/all-strategies/identity.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              },\n              \"passkey\": {\n                \"display_name\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"first\": {\n              \"title\": \"First Name\",\n              \"type\": \"string\"\n            },\n            \"last\": {\n              \"title\": \"Last Name\",\n              \"type\": \"string\"\n            }\n          }\n        }\n      },\n      \"required\": [\"email\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "contrib/quickstart/kratos/all-strategies/kratos.yml",
    "content": "version: v0.13.0\n\ndsn: memory\n\nserve:\n  public:\n    base_url: http://localhost:4433/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nsession:\n  whoami:\n    required_aal: aal1\n\nselfservice:\n  default_browser_return_url: http://localhost:4455/\n  allowed_return_urls:\n    - http://localhost:4455\n    - http://localhost:19006/Callback\n    - exp://localhost:8081/--/Callback\n\n  methods:\n    password:\n      enabled: true\n    webauthn:\n      enabled: true\n      config:\n        passwordless: true\n        rp:\n          display_name: Your Application name\n          # Set 'id' to the top-level domain.\n          id: localhost\n          # Set 'origin' to the exact URL of the page that prompts the user to use WebAuthn. You must include the scheme, host, and port.\n          origin: http://localhost:4455\n    passkey:\n      enabled: true\n      config:\n        rp:\n          display_name: Your Application name\n          # Set 'id' to the top-level domain.\n          id: localhost\n          # Set 'origin' to the exact URL of the page that prompts the user to use WebAuthn. You must include the scheme, host, and port.\n          origins:\n            - http://localhost:4455\n\n  flows:\n    error:\n      ui_url: http://localhost:4455/error\n\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 15m\n      required_aal: aal1\n\n    recovery:\n      enabled: true\n      ui_url: http://localhost:4455/recovery\n      use: code\n\n    verification:\n      enabled: false\n      ui_url: http://localhost:4455/verification\n      use: code\n      after:\n        default_browser_return_url: http://localhost:4455/\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    login:\n      ui_url: http://localhost:4455/login\n      lifespan: 10m\n\n    registration:\n      enable_legacy_one_step: false\n      lifespan: 10m\n      ui_url: http://localhost:4455/registration\n      after:\n        passkey:\n          hooks:\n            - hook: session\n        webauthn:\n          hooks:\n            - hook: session\n        password:\n          hooks:\n            - hook: session\n            - hook: show_verification_ui\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n  cipher:\n    - 32-LONG-SECRET-NOT-SECURE-AT-ALL\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nhashers:\n  algorithm: bcrypt\n  bcrypt:\n    cost: 8\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: file://./contrib/quickstart/kratos/all-strategies/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\n"
  },
  {
    "path": "contrib/quickstart/kratos/cloud/Caddyfile",
    "content": "{\n  http_port 4455\n\tauto_https off\n\n}\n\n:4455 {\n  route /ui/* {\n    uri strip_prefix /ui\n    reverse_proxy kratos-selfservice-ui-node:4438 {\n      header_up Host {http.request.hostport}\n    }\n  }\n  reverse_proxy /* kratos:4433 {\n    header_up Host {http.request.hostport}\n  }\n}\n"
  },
  {
    "path": "contrib/quickstart/kratos/cloud/identity.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"first\": {\n              \"title\": \"First Name\",\n              \"type\": \"string\"\n            },\n            \"last\": {\n              \"title\": \"Last Name\",\n              \"type\": \"string\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "contrib/quickstart/kratos/cloud/kratos.yml",
    "content": "version: v0.13.0\n\ndsn: memory\n\nserve:\n  public:\n    base_url: http://localhost:4455/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nselfservice:\n  default_browser_return_url: http://localhost:4455/ui/welcome\n  allowed_return_urls:\n    - http://localhost:4455\n\n  methods:\n    password:\n      enabled: true\n\n  flows:\n    error:\n      ui_url: http://localhost:4455/ui/error\n\n    settings:\n      ui_url: http://localhost:4455/ui/settings\n      privileged_session_max_age: 15m\n\n    recovery:\n      enabled: true\n      ui_url: http://localhost:4455/ui/recovery\n\n    verification:\n      enabled: true\n      ui_url: http://localhost:4455/ui/verification\n      after:\n        default_browser_return_url: http://localhost:4455/ui/welcome\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/ui/login\n\n    login:\n      ui_url: http://localhost:4455/ui/login\n\n    registration:\n      ui_url: http://localhost:4455/ui/registration\n      after:\n        password:\n          hooks:\n            -\n              hook: session\n\nlog:\n  level: info\n  format: text\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n\nhashers:\n  algorithm: bcrypt\n  bcrypt:\n    cost: 8\n\nidentity:\n  default_schema_id: preset://email\n  schemas:\n    - id: preset://email\n      url: file:///etc/config/kratos/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\n"
  },
  {
    "path": "contrib/quickstart/kratos/cloud/quickstart.yml",
    "content": "version: '3.7'\n\nservices:\n  kratos:\n    volumes:\n      - type: volume\n        source: kratos-sqlite\n        target: /var/lib/sqlite\n        read_only: false\n      - type: bind\n        source: ./contrib/quickstart/kratos/cloud\n        target: /etc/config/kratos\n  kratos-migrate:\n    volumes:\n      - type: volume\n        source: kratos-sqlite\n        target: /var/lib/sqlite\n        read_only: false\n      - type: bind\n        source: ./contrib/quickstart/kratos/cloud\n        target: /etc/config/kratos\n\n  kratos-selfservice-ui-node:\n    ports:\n      - \"4438:4438\"\n    environment:\n      - PORT=4438\n      - KRATOS_BROWSER_URL=http://localhost:4455/\n\n  kratos-caddy:\n    image: caddy:2.4.5-alpine\n    ports:\n      - \"4455:4455\"\n    volumes:\n      - type: bind\n        source: ./contrib/quickstart/kratos/cloud/Caddyfile\n        target: /etc/caddy/Caddyfile\n    command: caddy run -watch -config /etc/caddy/Caddyfile\n    restart: on-failure\n    networks:\n      - intranet\n\n"
  },
  {
    "path": "contrib/quickstart/kratos/email-password/identity.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"first\": {\n              \"title\": \"First Name\",\n              \"type\": \"string\"\n            },\n            \"last\": {\n              \"title\": \"Last Name\",\n              \"type\": \"string\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "contrib/quickstart/kratos/email-password/kratos.yml",
    "content": "version: v0.13.0\n\ndsn: memory\n\nserve:\n  public:\n    base_url: http://127.0.0.1:4433/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nselfservice:\n  default_browser_return_url: http://127.0.0.1:4455/welcome\n  allowed_return_urls:\n    - http://127.0.0.1:4455\n    - http://localhost:19006/Callback\n    - exp://localhost:8081/--/Callback\n\n  methods:\n    password:\n      enabled: true\n    totp:\n      config:\n        issuer: Kratos\n      enabled: true\n    lookup_secret:\n      enabled: true\n    link:\n      enabled: true\n    code:\n      enabled: true\n\n  flows:\n    error:\n      ui_url: http://127.0.0.1:4455/error\n\n    settings:\n      ui_url: http://127.0.0.1:4455/settings\n      privileged_session_max_age: 15m\n      required_aal: highest_available\n\n    recovery:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/recovery\n      use: code\n\n    verification:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/verification\n      use: code\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/welcome\n\n    logout:\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/login\n\n    login:\n      ui_url: http://127.0.0.1:4455/login\n      lifespan: 10m\n\n    registration:\n      lifespan: 10m\n      ui_url: http://127.0.0.1:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n            - hook: show_verification_ui\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n  cipher:\n    - 32-LONG-SECRET-NOT-SECURE-AT-ALL\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nhashers:\n  algorithm: bcrypt\n  bcrypt:\n    cost: 8\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: file:///etc/config/kratos/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\n\nfeature_flags:\n  use_continue_with_transitions: true\n"
  },
  {
    "path": "contrib/quickstart/kratos/passkey/identity.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"passkey\": {\n                \"display_name\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\"email\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "contrib/quickstart/kratos/passkey/kratos.yml",
    "content": "serve:\n  public:\n    base_url: http://localhost:4433/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nsession:\n  whoami:\n    required_aal: aal1\n\nselfservice:\n  default_browser_return_url: http://localhost:4455/welcome\n  allowed_return_urls:\n    - http://localhost:4455\n    - http://localhost:19006/Callback\n    - exp://example.com/Callback\n    - https://www.ory.sh/\n    - https://example.org/\n    - https://www.example.org/\n  methods:\n    link:\n      config:\n        lifespan: 1h\n    code:\n      config:\n        lifespan: 1h\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    lookup_secret:\n      enabled: true\n    webauthn:\n      enabled: true\n      config:\n        passwordless: true\n        rp:\n          id: localhost\n          origins:\n            - http://localhost:4455\n          display_name: Ory\n    passkey:\n      enabled: true\n      config:\n        rp:\n          id: localhost\n          origins:\n            - http://localhost:4455\n          display_name: Ory\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n      required_aal: aal1\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n    registration:\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n        webauthn:\n          hooks:\n            - hook: session\n        passkey:\n          hooks:\n            - hook: session\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n  cipher:\n    - 32-LONG-SECRET-NOT-SECURE-AT-ALL\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nhashers:\n  algorithm: bcrypt\n  bcrypt:\n    cost: 8\n\nidentity:\n  schemas:\n    - id: default\n      url: file://contrib/quickstart/kratos/passkey/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\n"
  },
  {
    "path": "contrib/quickstart/kratos/phone-password/identity.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/phone-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"phone\": {\n          \"type\": \"string\",\n          \"format\": \"tel\",\n          \"title\": \"Phone number\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"sms\"\n            }\n          }\n        }\n      },\n      \"required\": [\"email\", \"phone\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "contrib/quickstart/kratos/phone-password/kratos.yml",
    "content": "version: v0.13.0\n\ndsn: memory\n\nserve:\n  public:\n    base_url: http://127.0.0.1:4433/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nselfservice:\n  default_browser_return_url: http://127.0.0.1:4455/welcome\n  allowed_return_urls:\n    - http://127.0.0.1:4455\n    - http://localhost:19006/Callback\n    - exp://localhost:8081/--/Callback\n\n  methods:\n    password:\n      enabled: true\n    totp:\n      config:\n        issuer: Kratos\n      enabled: true\n    lookup_secret:\n      enabled: true\n    link:\n      enabled: true\n    code:\n      enabled: true\n\n  flows:\n    error:\n      ui_url: http://127.0.0.1:4455/error\n\n    settings:\n      ui_url: http://127.0.0.1:4455/settings\n      privileged_session_max_age: 15m\n      required_aal: highest_available\n\n    recovery:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/recovery\n      use: code\n\n    verification:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/verification\n      use: code\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/welcome\n\n    logout:\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/login\n\n    login:\n      ui_url: http://127.0.0.1:4455/login\n      lifespan: 10m\n\n    registration:\n      lifespan: 10m\n      ui_url: http://127.0.0.1:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n            - hook: show_verification_ui\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n  cipher:\n    - 32-LONG-SECRET-NOT-SECURE-AT-ALL\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nhashers:\n  algorithm: bcrypt\n  bcrypt:\n    cost: 8\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: file:///etc/config/kratos/identity.schema.json\n\ncourier:\n  channels:\n    - id: sms\n      type: http\n      request_config:\n        url: https://api.twilio.com/2010-04-01/Accounts/AXXXXXXXXXXXXXX/Messages.json\n        method: POST\n        body: base64://ZnVuY3Rpb24oY3R4KSB7ClRvOiBjdHguUmVjaXBpZW50LApCb2R5OiBjdHguQm9keSwKfQ==\n        headers:\n          Content-Type: application/x-www-form-urlencoded\n        auth:\n          type: basic_auth\n          config:\n            user: AXXXXXXX\n            password: XXXX\n\nfeature_flags:\n  use_continue_with_transitions: true\n"
  },
  {
    "path": "contrib/quickstart/kratos/webauthn/identity.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"first\": {\n              \"title\": \"First Name\",\n              \"type\": \"string\"\n            },\n            \"last\": {\n              \"title\": \"Last Name\",\n              \"type\": \"string\"\n            }\n          }\n        }\n      },\n      \"required\": [\"email\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "contrib/quickstart/kratos/webauthn/kratos.yml",
    "content": "version: v0.13.0\n\ndsn: memory\n\nserve:\n  public:\n    base_url: http://localhost:4433/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nselfservice:\n  default_browser_return_url: http://localhost:4455/welcome\n  allowed_return_urls:\n    - http://localhost:4455\n\n  methods:\n    password:\n      enabled: false\n    totp:\n      config:\n        issuer: Kratos\n      enabled: true\n    lookup_secret:\n      enabled: true\n    link:\n      enabled: true\n    code:\n      enabled: true\n    webauthn:\n      config:\n        passwordless: true\n        rp:\n          display_name: Your Application name\n          # Set 'id' to the top-level domain.\n          id: localhost\n          # Set 'origin' to the exact URL of the page that prompts the user to use WebAuthn. You must include the scheme, host, and port.\n          origin: http://localhost:4455\n      enabled: true\n\n  flows:\n    error:\n      ui_url: http://localhost:4455/error\n\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 15m\n      required_aal: highest_available\n\n    recovery:\n      enabled: true\n      ui_url: http://localhost:4455/recovery\n      use: code\n\n    verification:\n      enabled: true\n      ui_url: http://localhost:4455/verification\n      use: code\n      after:\n        default_browser_return_url: http://localhost:4455/welcome\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    login:\n      ui_url: http://localhost:4455/login\n      lifespan: 10m\n\n    registration:\n      lifespan: 10m\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n            - hook: show_verification_ui\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n  cipher:\n    - 32-LONG-SECRET-NOT-SECURE-AT-ALL\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nhashers:\n  algorithm: bcrypt\n  bcrypt:\n    cost: 8\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: file:///etc/config/kratos/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\n"
  },
  {
    "path": "contrib/quickstart/oathkeeper/access-rules.yml",
    "content": "-\n  id: \"ory:kratos:public\"\n  upstream:\n    preserve_host: true\n    url: \"http://kratos:4433\"\n    strip_path: /.ory/kratos/public\n  match:\n    url: \"http://127.0.0.1:4455/.ory/kratos/public/<**>\"\n    methods:\n      - GET\n      - POST\n      - PUT\n      - DELETE\n      - PATCH\n  authenticators:\n    -\n      handler: noop\n  authorizer:\n    handler: allow\n  mutators:\n    - handler: noop\n\n-\n  id: \"ory:kratos-selfservice-ui-node:anonymous\"\n  upstream:\n    preserve_host: true\n    url: \"http://kratos-selfservice-ui-node:4435\"\n  match:\n    url: \"http://127.0.0.1:4455/<{registration,welcome,recovery,verification,login,error,health/{alive,ready},**.css,**.js,**.png,**.svg,**.woff*}>\"\n    methods:\n      - GET\n  authenticators:\n    -\n      handler: anonymous\n  authorizer:\n    handler: allow\n  mutators:\n    -\n      handler: noop\n\n-\n  id: \"ory:kratos-selfservice-ui-node:protected\"\n  upstream:\n    preserve_host: true\n    url: \"http://kratos-selfservice-ui-node:4435\"\n  match:\n    url: \"http://127.0.0.1:4455/<{sessions,settings}>\"\n    methods:\n      - GET\n  authenticators:\n    -\n      handler: cookie_session\n  authorizer:\n    handler: allow\n  mutators:\n    - handler: id_token\n  errors:\n    - handler: redirect\n      config:\n        to: http://127.0.0.1:4455/login\n"
  },
  {
    "path": "contrib/quickstart/oathkeeper/id_token.jwks.json",
    "content": "{\n  \"keys\": [\n    {\n      \"use\": \"sig\",\n      \"kty\": \"RSA\",\n      \"kid\": \"a2aa9739-d753-4a0d-87ee-61f101050277\",\n      \"alg\": \"RS256\",\n      \"n\": \"zpjSl0ySsdk_YC4ZJYYV-cSznWkzndTo0lyvkYmeBkW60YHuHzXaviHqonY_DjFBdnZC0Vs_QTWmBlZvPzTp4Oni-eOetP-Ce3-B8jkGWpKFOjTLw7uwR3b3jm_mFNiz1dV_utWiweqx62Se0SyYaAXrgStU8-3P2Us7_kz5NnBVL1E7aEP40aB7nytLvPhXau-YhFmUfgykAcov0QrnNY0DH0eTcwL19UysvlKx6Uiu6mnbaFE1qx8X2m2xuLpErfiqj6wLCdCYMWdRTHiVsQMtTzSwuPuXfH7J06GTo3I1cEWN8Mb-RJxlosJA_q7hEd43yYisCO-8szX0lgCasw\",\n      \"e\": \"AQAB\",\n      \"d\": \"x3dfY_rna1UQTmFToBoMn6Edte47irhkra4VSNPwwaeTTvI-oN2TO51td7vo91_xD1nw-0c5FFGi4V2UfRcudBv9LD1rHt_O8EPUh7QtAUeT3_XXgjx1Xxpqu5goMZpkTyGZ-B6JzOY3L8lvWQ_Qeia1EXpvxC-oTOjJnKZeuwIPlcoNKMRU-mIYOnkRFfnUvrDm7N9UZEp3PfI3vhE9AquP1PEvz5KTUYkubsfmupqqR6FmMUm6ulGT7guhBw9A3vxIYbYGKvXLdBvn68mENrEYxXrwmu6ITMh_y208M5rC-hgEHIAIvMu1aVW6jNgyQTunsGST3UyrSbwjI0K9UQ\",\n      \"p\": \"77fDvnfHRFEgyi7mh0c6fAdtMEMJ05W8NwTG_D-cSwfWipfTwJJrroWoRwEgdAg5AWGq-MNUzrubTVXoJdC2T4g1o-VRZkKKYoMvav3CvOIMzCBxBs9I_GAKr5NCSk7maksMqiCTMhmkoZ5RPuMYMY_YzxKNAbjBd9qFLfaVAqs\",\n      \"q\": \"3KEmPA2XQkf7dvtpY1Xkp1IfMV_UBdmYk7J6dB5BYqzviQWdEFvWaSATJ_7qV1dw0JDZynOgipp8gvoL-RepfjtArhPz41wB3J2xmBYrBr1sJ-x5eqAvMkQk2bd5KTor44e79TRIkmkFYAIdUQ5JdVXPA13S8WUZfb_bAbwaCBk\",\n      \"dp\": \"5uyy32AJkNFKchqeLsE6INMSp0RdSftbtfCfM86fZFQno5lA_qjOnO_avJPkTILDT4ZjqoKYxxJJOEXCffNCPPltGvbE5GrDXsUbP8k2-LgWNeoml7XFjIGEqcCFQoohQ1IK4DTDN6cmRh76C0e_Pbdh15D6TydJEIlsdGuu_kM\",\n      \"dq\": \"aegFNYCEojFxeTzX6vIZL2RRSt8oJKK-Be__reu0EUzYMtr5-RdMhev6phFMph54LfXKRc9ZOg9MQ4cJ5klAeDKzKpyzTukkj6U20b2aa8LTvxpZec6YuTVSxxu2Ul71IGRQijTNvVIiXWLGddk409Ub6Q7JqkyQfvdwhpWnnUk\",\n      \"qi\": \"P68-EwgcRy9ce_PZ75c909cU7dzCiaGcTX1psJiXmQAFBcG0msWfsyHGbllOZG27pKde78ORGJDYDNk1FqTwsogZyCP87EiBmOoqXWnMvKYfJ1DOx7x42LMAGwMD3bgQj9jgRACxFJG4n3NI6uFlFruyl_CLQzwW_rQFHshLK7Q\"\n    }\n  ]\n}\n"
  },
  {
    "path": "contrib/quickstart/oathkeeper/oathkeeper.yml",
    "content": "log:\n  level: debug\n  format: json\n\nserve:\n  proxy:\n    cors:\n      enabled: true\n      allowed_origins:\n        - \"*\"\n      allowed_methods:\n        - POST\n        - GET\n        - PUT\n        - PATCH\n        - DELETE\n      allowed_headers:\n        - Authorization\n        - Content-Type\n      exposed_headers:\n        - Content-Type\n      allow_credentials: true\n      debug: true\n\nerrors:\n  fallback:\n    - json\n\n  handlers:\n    redirect:\n      enabled: true\n      config:\n        to: http://127.0.0.1:4455/login\n        when:\n          -\n            error:\n              - unauthorized\n              - forbidden\n            request:\n              header:\n                accept:\n                  - text/html\n    json:\n      enabled: true\n      config:\n        verbose: true\n\naccess_rules:\n  matching_strategy: glob\n  repositories:\n    - file:///etc/config/oathkeeper/access-rules.yml\n\nauthenticators:\n  anonymous:\n    enabled: true\n    config:\n      subject: guest\n\n  cookie_session:\n    enabled: true\n    config:\n      check_session_url: http://kratos:4433/sessions/whoami\n      preserve_path: true\n      extra_from: \"@this\"\n      subject_from: \"identity.id\"\n      only:\n        - ory_kratos_session\n\n  noop:\n    enabled: true\n\nauthorizers:\n  allow:\n    enabled: true\n\nmutators:\n  noop:\n    enabled: true\n\n  id_token:\n    enabled: true\n    config:\n      issuer_url: http://127.0.0.1:4455/\n      jwks_url: file:///etc/config/oathkeeper/id_token.jwks.json\n      claims: |\n        {\n          \"session\": {{ .Extra | toJson }}\n        }\n"
  },
  {
    "path": "corpx/faker.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// #nosec G404 -- used in tests only\npackage corpx\n\nimport (\n\t\"math/rand\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/randx\"\n)\n\nvar setup sync.Once\n\nfunc RegisterFakes() {\n\tsetup.Do(registerFakes)\n}\n\nfunc registerFakes() {\n\t_ = faker.SetRandomMapAndSliceSize(4)\n\n\tif err := faker.AddProvider(\"ptr_geo_location\", func(v reflect.Value) (interface{}, error) {\n\t\treturn new(\"Munich, Germany\"), nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"ptr_ipv4\", func(v reflect.Value) (interface{}, error) {\n\t\treturn new(faker.IPv4()), nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"birthdate\", func(v reflect.Value) (interface{}, error) {\n\t\treturn time.Now().Add(time.Duration(rand.Int())).Round(time.Second).UTC(), nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"time_types\", func(v reflect.Value) (interface{}, error) {\n\t\tes := make([]time.Time, rand.Intn(5))\n\t\tfor k := range es {\n\t\t\tes[k] = time.Now().Add(time.Duration(rand.Int())).Round(time.Second).UTC()\n\t\t}\n\t\treturn es, nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"http_header\", func(v reflect.Value) (interface{}, error) {\n\t\theaders := http.Header{}\n\t\tfor i := 0; i <= rand.Intn(5); i++ {\n\t\t\tvalues := make([]string, rand.Intn(4)+1)\n\t\t\tfor k := range values {\n\t\t\t\tvalues[k] = randx.MustString(8, randx.AlphaNum)\n\t\t\t}\n\t\t\theaders[randx.MustString(8, randx.AlphaNum)] = values\n\t\t}\n\n\t\treturn headers, nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"http_method\", func(v reflect.Value) (interface{}, error) {\n\t\tmethods := []string{\"POST\", \"PUT\", \"GET\", \"PATCH\"}\n\t\treturn methods[rand.Intn(len(methods))], nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"identity_credentials_type\", func(v reflect.Value) (interface{}, error) {\n\t\tmethods := []identity.CredentialsType{identity.CredentialsTypePassword, identity.CredentialsTypePassword}\n\t\treturn string(methods[rand.Intn(len(methods))]), nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"string\", func(v reflect.Value) (interface{}, error) {\n\t\treturn randx.MustString(25, randx.AlphaNum), nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"time_type\", func(v reflect.Value) (interface{}, error) {\n\t\treturn time.Now().Add(time.Duration(rand.Int())).Round(time.Second).UTC(), nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"ui_node_attributes\", func(v reflect.Value) (interface{}, error) {\n\t\tvar a node.Attributes\n\t\tswitch rand.Intn(4) {\n\t\tcase 0:\n\t\t\ta = new(node.InputAttributes)\n\t\tcase 1:\n\t\t\ta = new(node.ImageAttributes)\n\t\tcase 2:\n\t\t\ta = new(node.AnchorAttributes)\n\t\tcase 3:\n\t\t\ta = new(node.TextAttributes)\n\t\t}\n\n\t\tif err := faker.FakeData(a); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn a, nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"uuid\", func(v reflect.Value) (interface{}, error) {\n\t\treturn x.NewUUID(), nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"identity\", func(v reflect.Value) (interface{}, error) {\n\t\tvar i identity.Identity\n\t\treturn &i, faker.FakeData(&i)\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"flow_type\", func(v reflect.Value) (interface{}, error) {\n\t\tif rand.Intn(2) == 0 {\n\t\t\treturn flow.TypeAPI, nil\n\t\t}\n\t\treturn flow.TypeBrowser, nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"session_device\", func(v reflect.Value) (interface{}, error) {\n\t\tvar d session.Device\n\t\treturn &d, faker.FakeData(&d)\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := faker.AddProvider(\"aal_type\", func(v reflect.Value) (interface{}, error) {\n\t\treturn \"aal1\", nil\n\t}); err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "courier/.snapshots/TestHandler-handler=getCourierMessage-case=returns_an_error_if_no_message_is_found-endpoint=admin.json",
    "content": "{\n  \"error\": {\n    \"code\": 404,\n    \"status\": \"Not Found\",\n    \"message\": \"Unable to locate the resource\"\n  }\n}\n"
  },
  {
    "path": "courier/.snapshots/TestHandler-handler=getCourierMessage-case=returns_an_error_if_no_message_is_found-endpoint=public.json",
    "content": "{\n  \"error\": {\n    \"code\": 404,\n    \"status\": \"Not Found\",\n    \"message\": \"Unable to locate the resource\"\n  }\n}\n"
  },
  {
    "path": "courier/.snapshots/TestHandler-handler=getCourierMessage-case=returns_an_error_if_parameter_is_malformed-endpoint=admin.json",
    "content": "{\n  \"error\": {\n    \"code\": 400,\n    \"status\": \"Bad Request\",\n    \"message\": \"uuid: incorrect UUID length 10 in string \\\"not-a-uuid\\\"\"\n  }\n}\n"
  },
  {
    "path": "courier/.snapshots/TestHandler-handler=getCourierMessage-case=returns_an_error_if_parameter_is_malformed-endpoint=public.json",
    "content": "{\n  \"error\": {\n    \"code\": 400,\n    \"status\": \"Bad Request\",\n    \"message\": \"uuid: incorrect UUID length 10 in string \\\"not-a-uuid\\\"\"\n  }\n}\n"
  },
  {
    "path": "courier/channel.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n)\n\ntype Channel interface {\n\tID() string\n\tDispatch(ctx context.Context, msg Message) error\n}\n"
  },
  {
    "path": "courier/courier.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/cenkalti/backoff\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/driver/config\"\n)\n\ntype (\n\tDependencies interface {\n\t\tPersistenceProvider\n\t\totelx.Provider\n\t\tlogrusx.Provider\n\t\tConfigProvider\n\t\thttpx.ClientProvider\n\t\tjsonnetsecure.VMProvider\n\t}\n\n\tCourier interface {\n\t\tWork(ctx context.Context) error\n\t\tQueueEmail(ctx context.Context, t EmailTemplate) (uuid.UUID, error)\n\t\tQueueSMS(ctx context.Context, t SMSTemplate) (uuid.UUID, error)\n\t\tDispatchQueue(ctx context.Context) error\n\t\tDispatchMessage(ctx context.Context, msg Message) error\n\t\tUseBackoff(b backoff.BackOff)\n\t\tFailOnDispatchError()\n\t}\n\n\tProvider interface {\n\t\tCourier(ctx context.Context) (Courier, error)\n\t}\n\n\tConfigProvider interface {\n\t\tCourierConfig() config.CourierConfigs\n\t}\n\n\tcourier struct {\n\t\tdeps                        Dependencies\n\t\tfailOnDispatchError         bool\n\t\tbackoff                     backoff.BackOff\n\t\tnewEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error)\n\t}\n)\n\nfunc NewCourier(ctx context.Context, deps Dependencies) (Courier, error) {\n\treturn NewCourierWithCustomTemplates(ctx, deps, NewEmailTemplateFromMessage)\n}\n\nfunc NewCourierWithCustomTemplates(_ context.Context, deps Dependencies, newEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error)) (Courier, error) {\n\treturn &courier{\n\t\tdeps:                        deps,\n\t\tbackoff:                     backoff.NewExponentialBackOff(),\n\t\tnewEmailTemplateFromMessage: newEmailTemplateFromMessage,\n\t}, nil\n}\n\nfunc (c *courier) FailOnDispatchError() {\n\tc.failOnDispatchError = true\n}\n\nfunc (c *courier) Work(ctx context.Context) error {\n\terrChan := make(chan error)\n\tdefer close(errChan)\n\n\tgo c.watchMessages(ctx, errChan)\n\n\tselect {\n\tcase <-ctx.Done():\n\t\tif errors.Is(ctx.Err(), context.Canceled) {\n\t\t\treturn nil\n\t\t}\n\t\treturn ctx.Err()\n\tcase err := <-errChan:\n\t\treturn errors.WithStack(err)\n\t}\n}\n\nfunc (c *courier) UseBackoff(b backoff.BackOff) {\n\tc.backoff = b\n}\n\nfunc (c *courier) watchMessages(ctx context.Context, errChan chan error) {\n\twait := c.deps.CourierConfig().CourierWorkerPullWait(ctx)\n\tc.backoff.Reset()\n\tfor {\n\t\tif err := backoff.Retry(func() error {\n\t\t\treturn c.DispatchQueue(ctx)\n\t\t}, c.backoff); err != nil {\n\t\t\terrChan <- errors.WithStack(err)\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(wait)\n\t}\n}\n"
  },
  {
    "path": "courier/courier_dispatcher.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/x/otelx\"\n)\n\nfunc (c *courier) channels(ctx context.Context, id string) (Channel, error) {\n\tcs, err := c.deps.CourierConfig().CourierChannels(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, channel := range cs {\n\t\tif channel.ID != id {\n\t\t\tcontinue\n\t\t}\n\t\tswitch channel.Type {\n\t\tcase \"smtp\":\n\t\t\tcourierChannel, err := NewSMTPChannelWithCustomTemplates(c.deps, channel.SMTPConfig, c.newEmailTemplateFromMessage)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn courierChannel, nil\n\t\tcase \"http\":\n\t\t\treturn newHttpChannel(channel.ID, &channel.RequestConfig, c.deps), nil\n\t\tdefault:\n\t\t\treturn nil, errors.Errorf(\"unknown courier channel type: %s\", channel.Type)\n\t\t}\n\t}\n\n\treturn nil, errors.Errorf(\"no courier channels configured for: %s\", id)\n}\n\nfunc (c *courier) DispatchMessage(ctx context.Context, msg Message) (err error) {\n\tctx, span := c.deps.Tracer(ctx).Tracer().Start(ctx, \"courier.DispatchMessage\", trace.WithAttributes(\n\t\tattribute.Stringer(\"message.id\", msg.ID),\n\t\tattribute.Stringer(\"message.nid\", msg.NID),\n\t\tattribute.Stringer(\"message.type\", msg.Type),\n\t\tattribute.String(\"message.template_type\", string(msg.TemplateType)),\n\t\tattribute.Int(\"message.send_count\", msg.SendCount),\n\t))\n\tdefer otelx.End(span, &err)\n\n\tlogger := c.deps.Logger().\n\t\tWithField(\"message_id\", msg.ID).\n\t\tWithField(\"message_nid\", msg.NID).\n\t\tWithField(\"message_type\", msg.Type).\n\t\tWithField(\"message_template_type\", msg.TemplateType).\n\t\tWithField(\"message_subject\", msg.Subject).\n\t\tWithField(\"trace_id\", span.SpanContext().TraceID())\n\n\tif err := c.deps.CourierPersister().IncrementMessageSendCount(ctx, msg.ID); err != nil {\n\t\tlogger.\n\t\t\tWithError(err).\n\t\t\tError(`Unable to increment the message's \"send_count\" field`)\n\t\treturn err\n\t}\n\n\tchannel, err := c.channels(ctx, msg.Channel.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tspan.SetAttributes(attribute.String(\"channel.id\", channel.ID()))\n\tlogger = logger.\n\t\tWithField(\"channel\", channel.ID())\n\n\tif err := channel.Dispatch(ctx, msg); err != nil {\n\t\treturn err\n\t}\n\tspan.AddEvent(events.NewCourierMessageDispatched(ctx, msg.ID, msg.Channel.String(), string(msg.TemplateType)))\n\n\tif err := c.deps.CourierPersister().SetMessageStatus(ctx, msg.ID, MessageStatusSent); err != nil {\n\t\tlogger.\n\t\t\tWithError(err).\n\t\t\tError(`Unable to set the message status to \"sent\".`)\n\t\treturn err\n\t}\n\n\tdispatchDuration := time.Since(msg.CreatedAt).Milliseconds()\n\tlogger.WithField(\"dispatch_duration_ms\", dispatchDuration).Debug(\"Courier sent out message.\")\n\n\treturn nil\n}\n\nfunc (c *courier) DispatchQueue(ctx context.Context) (err error) {\n\tctx, span := c.deps.Tracer(ctx).Tracer().Start(ctx, \"courier.DispatchQueue\")\n\tdefer otelx.End(span, &err)\n\tmaxRetries := c.deps.CourierConfig().CourierMessageRetries(ctx)\n\tpullCount := c.deps.CourierConfig().CourierWorkerPullCount(ctx)\n\n\t//nolint:gosec // disable G115\n\tmessages, err := c.deps.CourierPersister().NextMessages(ctx, uint8(pullCount))\n\tif err != nil {\n\t\tif errors.Is(err, ErrQueueEmpty) {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\tspan.SetAttributes(attribute.Int(\"messages_count\", len(messages)))\n\n\tfor k, msg := range messages {\n\t\tlogger := c.deps.Logger().\n\t\t\tWithField(\"message_id\", msg.ID).\n\t\t\tWithField(\"message_nid\", msg.NID).\n\t\t\tWithField(\"message_type\", msg.Type).\n\t\t\tWithField(\"message_template_type\", msg.TemplateType).\n\t\t\tWithField(\"message_subject\", msg.Subject)\n\n\t\tif msg.SendCount > maxRetries {\n\t\t\tif err := c.deps.CourierPersister().SetMessageStatus(ctx, msg.ID, MessageStatusAbandoned); err != nil {\n\t\t\t\tlogger.\n\t\t\t\t\tWithError(err).\n\t\t\t\t\tError(`Unable to set the retried message's status to \"abandoned\".`)\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tspan.AddEvent(events.NewCourierMessageAbandoned(ctx, msg.ID, msg.Channel.String(), string(msg.TemplateType)))\n\n\t\t\t// Skip the message\n\t\t\tlogger.\n\t\t\t\tWarnf(`Message was abandoned because it did not deliver after %d attempts`, msg.SendCount)\n\t\t} else if err := c.DispatchMessage(ctx, msg); err != nil {\n\t\t\tlogger.\n\t\t\t\tWithError(err).\n\t\t\t\tWarn(`Unable to dispatch message.`)\n\t\t\tif err := c.deps.CourierPersister().RecordDispatch(ctx, msg.ID, CourierMessageDispatchStatusFailed, err); err != nil {\n\t\t\t\tlogger.\n\t\t\t\t\tWithError(err).\n\t\t\t\t\tError(`Unable to record failure log entry.`)\n\t\t\t\tif c.failOnDispatchError {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor _, replace := range messages[k:] {\n\t\t\t\tif err := c.deps.CourierPersister().SetMessageStatus(ctx, replace.ID, MessageStatusQueued); err != nil {\n\t\t\t\t\tlogger.\n\t\t\t\t\t\tWithError(err).\n\t\t\t\t\t\tError(`Unable to reset the failed message's status to \"queued\".`)\n\t\t\t\t\tif c.failOnDispatchError {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif c.failOnDispatchError {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else if err := c.deps.CourierPersister().RecordDispatch(ctx, msg.ID, CourierMessageDispatchStatusSuccess, nil); err != nil {\n\t\t\tlogger.\n\t\t\t\tWithError(err).\n\t\t\t\tError(`Unable to record success log entry.`)\n\t\t\t// continue with execution, as the message was successfully dispatched\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "courier/courier_dispatcher_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/courier\"\n\ttemplates \"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/x/configx\"\n)\n\nfunc queueNewMessage(t *testing.T, c courier.Courier) uuid.UUID {\n\tt.Helper()\n\tid, err := c.QueueEmail(t.Context(), templates.NewTestStub(&templates.TestStubModel{\n\t\tTo:      \"test-recipient-1@example.org\",\n\t\tSubject: \"test-subject-1\",\n\t\tBody:    \"test-body-1\",\n\t}))\n\trequire.NoError(t, err)\n\treturn id\n}\n\nfunc TestDispatchMessageWithInvalidSMTP(t *testing.T) {\n\t_, reg := pkg.NewRegistryDefaultWithDSN(t, \"\", configx.WithValues(map[string]any{\n\t\tconfig.ViperKeyCourierMessageRetries: 5,\n\t\tconfig.ViperKeyCourierSMTPURL:        \"http://foo.url\",\n\t}))\n\n\tc, err := reg.Courier(t.Context())\n\trequire.NoError(t, err)\n\n\tt.Run(\"case=failed sending\", func(t *testing.T) {\n\t\tid := queueNewMessage(t, c)\n\t\tmessage, err := reg.CourierPersister().LatestQueuedMessage(t.Context())\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, id, message.ID)\n\n\t\terr = c.DispatchMessage(t.Context(), *message)\n\t\t// sending the email fails, because there is no SMTP server at foo.url\n\t\trequire.Error(t, err)\n\n\t\tmessages, err := reg.CourierPersister().NextMessages(t.Context(), 10)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, messages, 1)\n\t})\n}\n\nfunc TestDispatchMessage(t *testing.T) {\n\t_, reg := pkg.NewRegistryDefaultWithDSN(t, \"\", configx.WithValues(map[string]any{\n\t\tconfig.ViperKeyCourierMessageRetries: 5,\n\t\tconfig.ViperKeyCourierSMTPURL:        \"http://foo.url\",\n\t}))\n\n\tc, err := reg.Courier(t.Context())\n\trequire.NoError(t, err)\n\tt.Run(\"case=invalid channel\", func(t *testing.T) {\n\t\tmessage := courier.Message{\n\t\t\tChannel:      \"invalid-channel\",\n\t\t\tStatus:       courier.MessageStatusQueued,\n\t\t\tType:         courier.MessageTypeEmail,\n\t\t\tRecipient:    testhelpers.RandomEmail(),\n\t\t\tSubject:      \"test-subject-1\",\n\t\t\tBody:         \"test-body-1\",\n\t\t\tTemplateType: \"stub\",\n\t\t}\n\t\trequire.NoError(t, reg.CourierPersister().AddMessage(t.Context(), &message))\n\t\tassert.ErrorContains(t, c.DispatchMessage(t.Context(), message), \"no courier channels configured for: invalid-channel\")\n\t})\n}\n\nfunc TestDispatchQueue(t *testing.T) {\n\t_, reg := pkg.NewRegistryDefaultWithDSN(t, \"\", configx.WithValue(config.ViperKeyCourierMessageRetries, 1))\n\n\tc, err := reg.Courier(t.Context())\n\trequire.NoError(t, err)\n\tc.FailOnDispatchError()\n\n\tid := queueNewMessage(t, c)\n\trequire.NotEqual(t, uuid.Nil, id)\n\n\t// Fails to deliver the first time\n\terr = c.DispatchQueue(t.Context())\n\trequire.Error(t, err)\n\n\t// Retry once, as we set above - still fails\n\terr = c.DispatchQueue(t.Context())\n\trequire.Error(t, err)\n\n\t// Now it has been retried once, which means 2 > 1 is true and it is no longer tried\n\terr = c.DispatchQueue(t.Context())\n\trequire.NoError(t, err)\n\n\tvar message courier.Message\n\terr = reg.Persister().GetConnection(t.Context()).\n\t\tWhere(\"status = ?\", courier.MessageStatusAbandoned).\n\t\tEager(\"Dispatches\").\n\t\tFirst(&message)\n\n\trequire.NoError(t, err)\n\trequire.Equal(t, id, message.ID)\n\n\trequire.Len(t, message.Dispatches, 2)\n\trequire.Contains(t, gjson.GetBytes(message.Dispatches[0].Error, \"reason\").String(), \"failed to send email via smtp\")\n\trequire.Contains(t, gjson.GetBytes(message.Dispatches[1].Error, \"reason\").String(), \"failed to send email via smtp\")\n}\n"
  },
  {
    "path": "courier/email_templates.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/courier/template/email\"\n)\n\ntype (\n\tTemplate interface {\n\t\tjson.Marshaler\n\t\tTemplateType() template.TemplateType\n\t}\n\n\tEmailTemplate interface {\n\t\tTemplate\n\t\tEmailSubject(context.Context) (string, error)\n\t\tEmailBody(context.Context) (string, error)\n\t\tEmailBodyPlaintext(context.Context) (string, error)\n\t\tEmailRecipient() (string, error)\n\t}\n\n\tRequestHeadersCarrier interface {\n\t\tRequestHeaders() http.Header\n\t}\n)\n\nfunc NewEmailTemplateFromMessage(d template.Dependencies, msg Message) (EmailTemplate, error) {\n\tswitch msg.TemplateType {\n\tcase template.TypeRecoveryInvalid:\n\t\tvar t email.RecoveryInvalidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewRecoveryInvalid(d, &t), nil\n\tcase template.TypeRecoveryValid:\n\t\tvar t email.RecoveryValidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewRecoveryValid(d, &t), nil\n\tcase template.TypeRecoveryCodeInvalid:\n\t\tvar t email.RecoveryCodeInvalidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewRecoveryCodeInvalid(d, &t), nil\n\tcase template.TypeRecoveryCodeValid:\n\t\tvar t email.RecoveryCodeValidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewRecoveryCodeValid(d, &t), nil\n\tcase template.TypeVerificationInvalid:\n\t\tvar t email.VerificationInvalidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewVerificationInvalid(d, &t), nil\n\tcase template.TypeVerificationValid:\n\t\tvar t email.VerificationValidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewVerificationValid(d, &t), nil\n\tcase template.TypeVerificationCodeInvalid:\n\t\tvar t email.VerificationCodeInvalidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewVerificationCodeInvalid(d, &t), nil\n\tcase template.TypeVerificationCodeValid:\n\t\tvar t email.VerificationCodeValidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewVerificationCodeValid(d, &t), nil\n\tcase template.TypeTestStub:\n\t\tvar t email.TestStubModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewTestStub(&t), nil\n\tcase template.TypeLoginCodeValid:\n\t\tvar t email.LoginCodeValidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewLoginCodeValid(d, &t), nil\n\tcase template.TypeRegistrationCodeValid:\n\t\tvar t email.RegistrationCodeValidModel\n\t\tif err := json.Unmarshal(msg.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn email.NewRegistrationCodeValid(d, &t), nil\n\tdefault:\n\t\treturn nil, errors.Errorf(\"received unexpected message template type: %s\", msg.TemplateType)\n\t}\n}\n"
  },
  {
    "path": "courier/email_templates_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestNewEmailTemplateFromMessage(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tctx := context.Background()\n\n\tfor tmplType, expectedTmpl := range map[template.TemplateType]courier.EmailTemplate{\n\t\ttemplate.TypeRecoveryInvalid:         email.NewRecoveryInvalid(reg, &email.RecoveryInvalidModel{To: \"foo\"}),\n\t\ttemplate.TypeRecoveryValid:           email.NewRecoveryValid(reg, &email.RecoveryValidModel{To: \"bar\", RecoveryURL: \"http://foo.bar\"}),\n\t\ttemplate.TypeRecoveryCodeValid:       email.NewRecoveryCodeValid(reg, &email.RecoveryCodeValidModel{To: \"bar\", RecoveryCode: \"12345678\"}),\n\t\ttemplate.TypeRecoveryCodeInvalid:     email.NewRecoveryCodeInvalid(reg, &email.RecoveryCodeInvalidModel{To: \"bar\"}),\n\t\ttemplate.TypeVerificationInvalid:     email.NewVerificationInvalid(reg, &email.VerificationInvalidModel{To: \"baz\"}),\n\t\ttemplate.TypeVerificationValid:       email.NewVerificationValid(reg, &email.VerificationValidModel{To: \"faz\", VerificationURL: \"http://bar.foo\"}),\n\t\ttemplate.TypeVerificationCodeInvalid: email.NewVerificationCodeInvalid(reg, &email.VerificationCodeInvalidModel{To: \"baz\"}),\n\t\ttemplate.TypeVerificationCodeValid:   email.NewVerificationCodeValid(reg, &email.VerificationCodeValidModel{To: \"faz\", VerificationURL: \"http://bar.foo\", VerificationCode: \"123456678\"}),\n\t\ttemplate.TypeTestStub:                email.NewTestStub(&email.TestStubModel{To: \"far\", Subject: \"test subject\", Body: \"test body\"}),\n\t\ttemplate.TypeLoginCodeValid:          email.NewLoginCodeValid(reg, &email.LoginCodeValidModel{To: \"far\", LoginCode: \"123456\"}),\n\t\ttemplate.TypeRegistrationCodeValid:   email.NewRegistrationCodeValid(reg, &email.RegistrationCodeValidModel{To: \"far\", RegistrationCode: \"123456\"}),\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", tmplType), func(t *testing.T) {\n\t\t\ttmplData, err := json.Marshal(expectedTmpl)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tm := courier.Message{TemplateType: tmplType, TemplateData: tmplData}\n\t\t\tactualTmpl, err := courier.NewEmailTemplateFromMessage(reg, m)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.IsType(t, expectedTmpl, actualTmpl)\n\n\t\t\texpectedRecipient, err := expectedTmpl.EmailRecipient()\n\t\t\trequire.NoError(t, err)\n\t\t\tactualRecipient, err := actualTmpl.EmailRecipient()\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, expectedRecipient, actualRecipient)\n\n\t\t\texpectedSubject, err := expectedTmpl.EmailSubject(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\tactualSubject, err := actualTmpl.EmailSubject(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, expectedSubject, actualSubject)\n\n\t\t\texpectedBody, err := expectedTmpl.EmailBody(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\tactualBody, err := actualTmpl.EmailBody(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, expectedBody, actualBody)\n\n\t\t\texpectedBodyPlaintext, err := expectedTmpl.EmailBodyPlaintext(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\tactualBodyPlaintext, err := actualTmpl.EmailBodyPlaintext(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, expectedBodyPlaintext, actualBodyPlaintext)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "courier/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\tkeysetpagination \"github.com/ory/x/pagination/keysetpagination_v2\"\n)\n\nconst (\n\tAdminRouteCourier      = \"/courier\"\n\tAdminRouteListMessages = AdminRouteCourier + \"/messages\"\n\tAdminRouteGetMessage   = AdminRouteCourier + \"/messages/{msgID}\"\n)\n\ntype (\n\thandlerDependencies interface {\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\tnosurfx.CSRFProvider\n\t\tPersistenceProvider\n\t\tconfig.Provider\n\t}\n\tHandler struct {\n\t\tr handlerDependencies\n\t}\n\tHandlerProvider interface {\n\t\tCourierHandler() *Handler\n\t}\n)\n\nfunc NewHandler(r handlerDependencies) *Handler {\n\treturn &Handler{r: r}\n}\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\th.r.CSRFHandler().IgnoreGlobs(httprouterx.AdminPrefix+AdminRouteListMessages, AdminRouteListMessages)\n\tpublic.GET(httprouterx.AdminPrefix+AdminRouteListMessages, redir.RedirectToAdminRoute(h.r))\n\tpublic.GET(httprouterx.AdminPrefix+AdminRouteGetMessage, redir.RedirectToAdminRoute(h.r))\n}\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(AdminRouteListMessages, h.listCourierMessages)\n\tadmin.GET(AdminRouteGetMessage, h.getCourierMessage)\n}\n\n// Paginated Courier Message List Response\n//\n// swagger:response listCourierMessages\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype listCourierMessagesResponse struct {\n\tkeysetpagination.ResponseHeaders\n\n\t// List of identities\n\t//\n\t// in:body\n\tBody []Message\n}\n\n// Paginated List Courier Message Parameters\n//\n// swagger:parameters listCourierMessages\ntype ListCourierMessagesParameters struct {\n\tkeysetpagination.RequestParameters\n\n\t// Status filters out messages based on status.\n\t// If no value is provided, it doesn't take effect on filter.\n\t//\n\t// required: false\n\t// in: query\n\tStatus *MessageStatus `json:\"status\"`\n\n\t// Recipient filters out messages based on recipient.\n\t// If no value is provided, it doesn't take effect on filter.\n\t//\n\t// required: false\n\t// in: query\n\tRecipient string `json:\"recipient\"`\n}\n\n// swagger:route GET /admin/courier/messages courier listCourierMessages\n//\n// # List Messages\n//\n// Lists all messages by given status and recipient.\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: listCourierMessages\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) listCourierMessages(w http.ResponseWriter, r *http.Request) {\n\tkeys := h.r.Config().SecretsPagination(r.Context())\n\tfilter, paginator, err := parseMessagesFilter(r, keys)\n\tif err != nil {\n\t\th.r.Writer().WriteErrorCode(w, r, http.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tmessages, nextPage, err := h.r.CourierPersister().ListMessages(r.Context(), filter, paginator)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif !h.r.Config().IsInsecureDevMode(r.Context()) {\n\t\tfor i := range messages {\n\t\t\tmessages[i].Body = \"<redacted-unless-dev-mode>\"\n\t\t\tmessages[i].Subject = \"<redacted-unless-dev-mode>\"\n\t\t}\n\t}\n\n\tu := *r.URL\n\tkeysetpagination.SetLinkHeader(w, keys, &u, nextPage)\n\th.r.Writer().Write(w, r, messages)\n}\n\nfunc parseMessagesFilter(r *http.Request, keys [][32]byte) (ListCourierMessagesParameters, []keysetpagination.Option, error) {\n\tvar status *MessageStatus\n\n\tif r.URL.Query().Has(\"status\") {\n\t\tms, err := ToMessageStatus(r.URL.Query().Get(\"status\"))\n\t\tif err != nil {\n\t\t\treturn ListCourierMessagesParameters{}, nil, errors.WithStack(err)\n\t\t}\n\n\t\tstatus = &ms\n\t}\n\n\topts, err := keysetpagination.ParseQueryParams(keys, r.URL.Query())\n\tif err != nil {\n\t\treturn ListCourierMessagesParameters{}, nil, errors.WithStack(err)\n\t}\n\n\treturn ListCourierMessagesParameters{\n\t\tStatus:    status,\n\t\tRecipient: r.URL.Query().Get(\"recipient\"),\n\t}, opts, nil\n}\n\n// Get Courier Message Parameters\n//\n// swagger:parameters getCourierMessage\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getCourierMessage struct {\n\t// MessageID is the ID of the message.\n\t//\n\t// required: true\n\t// in: path\n\tMessageID string `json:\"id\"`\n}\n\n// swagger:route GET /admin/courier/messages/{id} courier getCourierMessage\n//\n// # Get a Message\n//\n// Gets a specific messages by the given ID.\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSecurity:\n//\t\toryAccessToken:\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t\t200: message\n//\t\t400: errorGeneric\n//\t\tdefault: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-medium\nfunc (h *Handler) getCourierMessage(w http.ResponseWriter, r *http.Request) {\n\tmsgID, err := uuid.FromString(r.PathValue(\"msgID\"))\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(err.Error()).WithDebugf(\"could not parse parameter {id} as UUID, got %s\", r.PathValue(\"id\")))\n\t\treturn\n\t}\n\n\tmessage, err := h.r.CourierPersister().FetchMessage(r.Context(), msgID)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif !h.r.Config().IsInsecureDevMode(r.Context()) {\n\t\tmessage.Body = \"<redacted-unless-dev-mode>\"\n\t\tmessage.Subject = \"<redacted-unless-dev-mode>\"\n\t}\n\n\th.r.Writer().Write(w, r, message)\n}\n"
  },
  {
    "path": "courier/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/urlx\"\n\t\"github.com/ory/x/uuidx\"\n)\n\nvar defaultPageToken = courier.Message{}.DefaultPageToken().Encrypt(nil)\n\nfunc TestHandler(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t// Start kratos server\n\tpublicTS, adminTS := testhelpers.NewKratosServerWithCSRF(t, reg)\n\n\ttss := []struct {\n\t\tname string\n\t\ts    *httptest.Server\n\t}{\n\t\t{\n\t\t\tname: \"public\",\n\t\t\ts:    publicTS,\n\t\t},\n\t\t{\n\t\t\tname: \"admin\",\n\t\t\ts:    adminTS,\n\t\t},\n\t}\n\n\tmockServerURL := urlx.ParseOrPanic(publicTS.URL)\n\tconf.MustSet(ctx, config.ViperKeyAdminBaseURL, adminTS.URL)\n\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, mockServerURL.String())\n\n\tget := func(t *testing.T, base *httptest.Server, href string, expectCode int) gjson.Result {\n\t\tt.Helper()\n\t\tres, err := base.Client().Get(base.URL + href)\n\t\trequire.NoError(t, err)\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\tassert.EqualValuesf(t, expectCode, res.StatusCode, \"%s\", body)\n\t\treturn gjson.ParseBytes(body)\n\t}\n\n\tgetList := func(t *testing.T, tsName string, qs string) gjson.Result {\n\t\tt.Helper()\n\t\thref := courier.AdminRouteListMessages + qs\n\t\tts := adminTS\n\n\t\tif tsName == \"public\" {\n\t\t\thref = httprouterx.AdminPrefix + href\n\t\t\tts = publicTS\n\t\t}\n\n\t\tparsed := get(t, ts, href, http.StatusOK)\n\t\trequire.Truef(t, parsed.IsArray(), \"%s\", parsed.Raw)\n\t\treturn parsed\n\t}\n\n\tt.Run(\"case=should return an empty list of messages\", func(t *testing.T) {\n\t\tfor _, name := range []string{\"public\", \"admin\"} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tparsed := getList(t, name, \"\")\n\t\t\t\tassert.Len(t, parsed.Array(), 0)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=list messages\", func(t *testing.T) {\n\t\t// Arrange test data\n\t\tconst msgCount = 10    // total message count\n\t\tconst procCount = 5    // how many messages' status should be equal to `processing`\n\t\tconst rcptOryCount = 2 // how many messages' recipient should be equal to `noreply@ory.sh`\n\t\tmessages := make([]courier.Message, msgCount)\n\n\t\tfor i := range messages {\n\t\t\trequire.NoError(t, faker.FakeData(&messages[i]))\n\t\t\tmessages[i].Type = courier.MessageTypeEmail\n\t\t\tmessages[i].Body = \"body content\"\n\t\t\tif i < rcptOryCount {\n\t\t\t\tmessages[i].Recipient = \"noreply@ory.sh\"\n\t\t\t}\n\t\t\trequire.NoError(t, reg.CourierPersister().AddMessage(context.Background(), &messages[i]))\n\t\t}\n\t\tfor i := range procCount {\n\t\t\trequire.NoError(t, reg.CourierPersister().SetMessageStatus(context.Background(), messages[i].ID, courier.MessageStatusProcessing))\n\t\t}\n\n\t\tt.Run(\"paging\", func(t *testing.T) {\n\t\t\tt.Run(\"case=should return first half of the messages\", func(t *testing.T) {\n\t\t\t\tqs := fmt.Sprintf(\"?page_token=%s&page_size=%d\", defaultPageToken, msgCount/2)\n\n\t\t\t\tfor _, tc := range tss {\n\t\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\t\tparsed := getList(t, tc.name, qs)\n\t\t\t\t\t\tassert.Len(t, parsed.Array(), msgCount/2)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"case=should error with random page token\", func(t *testing.T) {\n\t\t\t\tqs := fmt.Sprintf(`?page_token=%s&page_size=%s`, uuidx.NewV4().String(), \"250\")\n\n\t\t\t\tfor _, tc := range tss {\n\t\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\t\tpath := courier.AdminRouteListMessages + qs\n\t\t\t\t\t\tif tc.name == \"public\" {\n\t\t\t\t\t\t\tpath = httprouterx.AdminPrefix + path\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresp, err := tc.s.Client().Get(tc.s.URL + path)\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\tassert.Equal(t, http.StatusBadRequest, resp.StatusCode)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t\tt.Run(\"filtering\", func(t *testing.T) {\n\t\t\tt.Run(\"case=should return all queued messages\", func(t *testing.T) {\n\t\t\t\tqs := fmt.Sprintf(`?page_token=%s&page_size=250&status=queued`, defaultPageToken)\n\n\t\t\t\tfor _, tc := range tss {\n\t\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\t\tparsed := getList(t, tc.name, qs)\n\t\t\t\t\t\tassert.Len(t, parsed.Array(), msgCount-procCount)\n\n\t\t\t\t\t\tfor _, item := range parsed.Array() {\n\t\t\t\t\t\t\tassert.Equal(t, \"queued\", item.Get(\"status\").String())\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\tt.Run(\"case=should return all processing messages\", func(t *testing.T) {\n\t\t\t\tqs := fmt.Sprintf(`?page_token=%s&page_size=250&status=processing`, defaultPageToken)\n\n\t\t\t\tfor _, tc := range tss {\n\t\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\t\tparsed := getList(t, tc.name, qs)\n\t\t\t\t\t\tassert.Len(t, parsed.Array(), procCount)\n\n\t\t\t\t\t\tfor _, item := range parsed.Array() {\n\t\t\t\t\t\t\tassert.Equal(t, \"processing\", item.Get(\"status\").String())\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\tt.Run(\"case=should return all messages with recipient equals to noreply@ory.sh\", func(t *testing.T) {\n\t\t\t\tqs := fmt.Sprintf(`?page_token=%s&page_size=250&recipient=noreply@ory.sh`, defaultPageToken)\n\n\t\t\t\tfor _, tc := range tss {\n\t\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\t\tparsed := getList(t, tc.name, qs)\n\t\t\t\t\t\tassert.Len(t, parsed.Array(), rcptOryCount)\n\n\t\t\t\t\t\tfor _, item := range parsed.Array() {\n\t\t\t\t\t\t\tassert.Equal(t, \"noreply@ory.sh\", item.Get(\"recipient\").String())\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\tt.Run(\"case=body should be redacted if kratos is not in dev mode\", func(t *testing.T) {\n\t\t\tconf.MustSet(ctx, \"dev\", false)\n\t\t\tfor _, tc := range tss {\n\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\tparsed := getList(t, tc.name, \"\")\n\t\t\t\t\trequire.Lenf(t, parsed.Array(), msgCount, \"%s\", parsed.Raw)\n\n\t\t\t\t\tfor _, item := range parsed.Array() {\n\t\t\t\t\t\tassert.Equal(t, \"<redacted-unless-dev-mode>\", item.Get(\"body\").String())\n\t\t\t\t\t\tassert.Equal(t, \"<redacted-unless-dev-mode>\", item.Get(\"subject\").String())\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"case=body should not be redacted if kratos is in dev mode\", func(t *testing.T) {\n\t\t\tconf.MustSet(ctx, \"dev\", true)\n\t\t\tfor _, tc := range tss {\n\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\tparsed := getList(t, tc.name, \"\")\n\t\t\t\t\trequire.Lenf(t, parsed.Array(), msgCount, \"%s\", parsed.Raw)\n\n\t\t\t\t\tfor _, item := range parsed.Array() {\n\t\t\t\t\t\tassert.Equal(t, \"body content\", item.Get(\"body\").String())\n\t\t\t\t\t\tassert.NotEqual(t, \"<redacted-unless-dev-mode>\", item.Get(\"subject\").String())\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"case=should return with http status BadRequest when given status is invalid\", func(t *testing.T) {\n\t\t\tqs := fmt.Sprintf(`?page_token=%s&page_size=250&status=invalid_status`, defaultPageToken)\n\n\t\t\tres, err := adminTS.Client().Get(adminTS.URL + courier.AdminRouteListMessages + qs)\n\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode, \"status code should be equal to StatusBadRequest\")\n\t\t})\n\n\t})\n\tt.Run(\"handler=getCourierMessage\", func(t *testing.T) {\n\n\t\tmessage := courier.Message{}\n\t\trequire.NoError(t, faker.FakeData(&message))\n\t\tmessage.Type = courier.MessageTypeEmail\n\t\tmessage.Body = \"body content\"\n\t\trequire.NoError(t, reg.CourierPersister().AddMessage(context.Background(), &message))\n\t\trequire.NoError(t, reg.CourierPersister().RecordDispatch(ctx, message.ID, courier.CourierMessageDispatchStatusSuccess, errors.New(\"some error\")))\n\n\t\tgetCourierMessag := func(s *httptest.Server, id string) gjson.Result {\n\n\t\t\tr, err := s.Client().Get(s.URL + \"/admin/courier/messages/\" + id)\n\t\t\trequire.NoError(t, err)\n\t\t\treturn gjson.ParseBytes(ioutilx.MustReadAll(r.Body))\n\t\t}\n\n\t\tt.Run(\"case=should return a message by id\", func(t *testing.T) {\n\t\t\tconf.MustSet(ctx, \"dev\", true)\n\n\t\t\tfor _, tc := range tss {\n\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\tbody := getCourierMessag(tc.s, message.ID.String())\n\t\t\t\t\tassert.Equal(t, message.ID.String(), body.Get(\"id\").String())\n\t\t\t\t\tassert.Equal(t, message.Recipient, body.Get(\"recipient\").String())\n\t\t\t\t\tassert.Equal(t, message.Body, body.Get(\"body\").String())\n\t\t\t\t\tassert.Equal(t, message.Subject, body.Get(\"subject\").String())\n\n\t\t\t\t\t// assert Eager works\n\t\t\t\t\tassert.NotEmpty(t, body.Get(\"dispatches\").Array())\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"case=does not contain body if not in production\", func(t *testing.T) {\n\t\t\tconf.MustSet(ctx, \"dev\", false)\n\n\t\t\tfor _, tc := range tss {\n\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\tbody := getCourierMessag(tc.s, message.ID.String())\n\t\t\t\t\tassert.Equal(t, message.ID.String(), body.Get(\"id\").String())\n\t\t\t\t\tassert.Equal(t, \"<redacted-unless-dev-mode>\", body.Get(\"body\").String())\n\t\t\t\t\tassert.Equal(t, \"<redacted-unless-dev-mode>\", body.Get(\"subject\").String())\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"case=returns an error if parameter is malformed\", func(t *testing.T) {\n\t\t\tfor _, tc := range tss {\n\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\tbody := getCourierMessag(tc.s, \"not-a-uuid\")\n\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"case=returns an error if no message is found\", func(t *testing.T) {\n\t\t\tfor _, tc := range tss {\n\t\t\t\tt.Run(\"endpoint=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\tbody := getCourierMessag(tc.s, uuid.Nil.String())\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "courier/http_channel.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/request\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype (\n\thttpChannel struct {\n\t\tid            string\n\t\trequestConfig *request.Config\n\t\td             channelDependencies\n\t}\n\tchannelDependencies interface {\n\t\totelx.Provider\n\t\tlogrusx.Provider\n\t\thttpx.ClientProvider\n\t\tjsonnetsecure.VMProvider\n\t\tConfigProvider\n\t}\n)\n\nvar _ Channel = new(httpChannel)\n\nfunc newHttpChannel(id string, requestConfig *request.Config, d channelDependencies) *httpChannel {\n\treturn &httpChannel{\n\t\tid:            id,\n\t\trequestConfig: requestConfig,\n\t\td:             d,\n\t}\n}\n\nfunc (c *httpChannel) ID() string {\n\treturn c.id\n}\n\ntype httpDataModel struct {\n\tRecipient string `json:\"recipient\"`\n\tSubject   string `json:\"subject\"`\n\tBody      string `json:\"body\"`\n\t// HTMLBody optionally contains the HTML version of an email template when available.\n\tHTMLBody       string                `json:\"html_body,omitempty\"`\n\tTemplateType   template.TemplateType `json:\"template_type\"`\n\tTemplateData   Template              `json:\"template_data\"`\n\tMessageType    string                `json:\"message_type\"`\n\tRequestHeaders json.RawMessage       `json:\"request_headers\"`\n}\n\nfunc (c *httpChannel) Dispatch(ctx context.Context, msg Message) (err error) {\n\tctx, span := c.d.Tracer(ctx).Tracer().Start(ctx, \"courier.httpChannel.Dispatch\")\n\tdefer otelx.End(span, &err)\n\n\tbuilder, err := request.NewBuilder(ctx, c.requestConfig, c.d)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\ttmpl, err := newTemplate(c.d, msg)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\ttd := httpDataModel{\n\t\tRecipient:      msg.Recipient,\n\t\tSubject:        msg.Subject,\n\t\tBody:           msg.Body,\n\t\tTemplateType:   msg.TemplateType,\n\t\tTemplateData:   tmpl,\n\t\tRequestHeaders: msg.RequestHeaders,\n\t\tMessageType:    msg.Type.String(),\n\t}\n\n\tc.tryPopulateHTMLBody(ctx, tmpl, &td)\n\n\treq, err := builder.BuildRequest(ctx, td)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\treq = req.WithContext(ctx)\n\n\tres, err := c.d.HTTPClient(ctx,\n\t\t// fail fast and let the courier retry if needed instead of blocking the queue\n\t\thttpx.ResilientClientWithMaxRetry(0),\n\t\thttpx.ResilientClientWithConnectionTimeout(10*time.Second),\n\t).Do(req)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\tdefer func() { _ = res.Body.Close() }()\n\tres.Body = io.NopCloser(io.LimitReader(res.Body, 1024))\n\n\tlogger := c.d.Logger().\n\t\tWithField(\"http_server\", c.requestConfig.URL).\n\t\tWithField(\"message_id\", msg.ID).\n\t\tWithField(\"message_nid\", msg.NID).\n\t\tWithField(\"message_type\", msg.Type).\n\t\tWithField(\"message_template_type\", msg.TemplateType).\n\t\tWithField(\"message_subject\", msg.Subject)\n\n\tif res.StatusCode >= 200 && res.StatusCode < 300 {\n\t\tlogger.Debug(\"Courier sent out mailer.\")\n\t\treturn nil\n\t}\n\n\terr = errors.Errorf(\n\t\t\"unable to dispatch mail delivery because upstream server replied with status code %d\",\n\t\tres.StatusCode,\n\t)\n\n\tbody, _ := io.ReadAll(res.Body)\n\tlogger.\n\t\tWithError(err).\n\t\tWithField(\"http_response_body\", string(body)).\n\t\tError(\"sending mail via HTTP failed.\")\n\n\treturn errors.WithStack(err)\n}\n\nfunc (c *httpChannel) tryPopulateHTMLBody(ctx context.Context, tmpl Template, td *httpDataModel) {\n\tif emailTmpl, ok := tmpl.(EmailTemplate); ok {\n\t\t// Only get the HTML body from the template; plaintext body comes from msg.Body\n\t\t// to maintain backward compatibility with existing behavior\n\t\tif htmlBody, err := emailTmpl.EmailBody(ctx); err != nil {\n\t\t\tc.d.Logger().WithError(err).Error(\"Unable to get email HTML body from template.\")\n\t\t} else {\n\t\t\ttd.HTMLBody = htmlBody\n\t\t}\n\t}\n}\n\nfunc newTemplate(d template.Dependencies, msg Message) (Template, error) {\n\tswitch msg.Type {\n\tcase MessageTypeEmail:\n\t\treturn NewEmailTemplateFromMessage(d, msg)\n\tcase MessageTypeSMS:\n\t\treturn NewSMSTemplateFromMessage(d, msg)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"received unexpected message type: %s\", msg.Type)\n\t}\n}\n"
  },
  {
    "path": "courier/http_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier_test\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\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/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/x/configx\"\n)\n\nfunc TestQueueHTTPEmail(t *testing.T) {\n\ttype sendEmailRequestBody struct {\n\t\tIdentityID       string `json:\"identity_id\"`\n\t\tIdentityEmail    string `json:\"identity_email\"`\n\t\tRecipient        string `json:\"recipient\"`\n\t\tTemplateType     string `json:\"template_type\"`\n\t\tTo               string `json:\"to\"`\n\t\tRecoveryCode     string `json:\"recovery_code\"`\n\t\tRecoveryURL      string `json:\"recovery_url\"`\n\t\tVerificationURL  string `json:\"verification_url\"`\n\t\tVerificationCode string `json:\"verification_code\"`\n\t\tBody             string `json:\"body\"`\n\t\tHTMLBody         string `json:\"html_body\"`\n\t\tSubject          string `json:\"subject\"`\n\t}\n\n\texpectedEmail := []*email.TestStubModel{\n\t\t{\n\t\t\tTo:       \"test-2@test.com\",\n\t\t\tSubject:  \"test-mailer-subject-1\",\n\t\t\tBody:     \"test-mailer-body-1\",\n\t\t\tHTMLBody: \"<html><body>test-mailer-body-html-1</body></html>\",\n\t\t},\n\t\t{\n\t\t\tTo:      \"test-2@test.com\",\n\t\t\tSubject: \"test-mailer-subject-2\",\n\t\t\tBody:    \"test-mailer-body-2\",\n\t\t},\n\t}\n\n\tactual := make(chan sendEmailRequestBody, len(expectedEmail))\n\tsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\trb, err := io.ReadAll(r.Body)\n\t\trequire.NoError(t, err)\n\n\t\tvar body sendEmailRequestBody\n\n\t\terr = json.Unmarshal(rb, &body)\n\t\trequire.NoError(t, err)\n\n\t\tassert.NotEmpty(t, r.Header[\"Authorization\"])\n\t\tassert.Equal(t, \"Basic bWU6MTIzNDU=\", r.Header[\"Authorization\"][0])\n\n\t\tactual <- body\n\t}))\n\tt.Cleanup(srv.Close)\n\n\trequestConfig := fmt.Sprintf(`{\n\t\t\"url\": \"%s\",\n\t\t\"method\": \"POST\",\n\t\t\"auth\": {\n\t\t\t\"type\": \"basic_auth\",\n\t\t\t\"config\": {\n\t\t\t\t\"user\":     \"me\",\n\t\t\t\t\"password\": \"12345\"\n\t\t\t}\n\t\t},\n\t\t\"body\": \"file://./stub/request.config.mailer.jsonnet\"\n\t}`, srv.URL)\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]any{\n\t\tconfig.ViperKeyCourierDeliveryStrategy:  \"http\",\n\t\tconfig.ViperKeyCourierHTTPRequestConfig: requestConfig,\n\t\tconfig.ViperKeyCourierSMTPURL:           \"http://foo.url\",\n\t}))\n\n\tcourier, err := reg.Courier(t.Context())\n\trequire.NoError(t, err)\n\n\tfor _, message := range expectedEmail {\n\t\tid, err := courier.QueueEmail(t.Context(), email.NewTestStub(message))\n\t\trequire.NoError(t, err)\n\t\trequire.NotEqual(t, uuid.Nil, id)\n\t}\n\n\trequire.NoError(t, courier.DispatchQueue(t.Context()))\n\tclose(actual)\n\n\trequire.Len(t, actual, len(expectedEmail))\n\n\ti := 0\n\tfor message := range actual {\n\t\texpected := expectedEmail[i]\n\n\t\tassert.Equal(t, expected.To, message.To)\n\t\tassert.Equal(t, expected.Body, message.Body)\n\t\tassert.Equal(t, expected.HTMLBody, message.HTMLBody)\n\t\tassert.Equal(t, expected.Subject, message.Subject)\n\n\t\ti++\n\t}\n}\n"
  },
  {
    "path": "courier/message.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/courier/template\"\n\tkeysetpagination \"github.com/ory/x/pagination/keysetpagination_v2\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/stringsx\"\n)\n\n// A Message's Status\n//\n// swagger:model courierMessageStatus\ntype MessageStatus int\n\nconst (\n\tMessageStatusQueued MessageStatus = iota + 1\n\tMessageStatusSent\n\tMessageStatusProcessing\n\tMessageStatusAbandoned\n)\n\nconst (\n\tmessageStatusQueuedText     = \"queued\"\n\tmessageStatusSentText       = \"sent\"\n\tmessageStatusProcessingText = \"processing\"\n\tmessageStatusAbandonedText  = \"abandoned\"\n)\n\nfunc ToMessageStatus(str string) (MessageStatus, error) {\n\tswitch s := stringsx.SwitchExact(str); {\n\tcase s.AddCase(MessageStatusQueued.String()):\n\t\treturn MessageStatusQueued, nil\n\tcase s.AddCase(MessageStatusSent.String()):\n\t\treturn MessageStatusSent, nil\n\tcase s.AddCase(MessageStatusProcessing.String()):\n\t\treturn MessageStatusProcessing, nil\n\tcase s.AddCase(MessageStatusAbandoned.String()):\n\t\treturn MessageStatusAbandoned, nil\n\tdefault:\n\t\treturn 0, errors.WithStack(herodot.ErrBadRequest.WithWrap(s.ToUnknownCaseErr()).WithReason(\"Message status is not valid\"))\n\t}\n}\n\nfunc (ms MessageStatus) String() string {\n\tswitch ms {\n\tcase MessageStatusQueued:\n\t\treturn messageStatusQueuedText\n\tcase MessageStatusSent:\n\t\treturn messageStatusSentText\n\tcase MessageStatusProcessing:\n\t\treturn messageStatusProcessingText\n\tcase MessageStatusAbandoned:\n\t\treturn messageStatusAbandonedText\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc (ms MessageStatus) IsValid() error {\n\tswitch ms {\n\tcase MessageStatusQueued, MessageStatusSent, MessageStatusProcessing, MessageStatusAbandoned:\n\t\treturn nil\n\tdefault:\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReason(\"Message status is not valid\"))\n\t}\n}\n\nfunc (ms MessageStatus) MarshalJSON() ([]byte, error) {\n\tif err := ms.IsValid(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn json.Marshal(ms.String())\n}\n\nfunc (ms *MessageStatus) UnmarshalJSON(data []byte) error {\n\tvar str string\n\tif err := json.Unmarshal(data, &str); err != nil {\n\t\treturn err\n\t}\n\n\ts, err := ToMessageStatus(str)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*ms = s\n\treturn nil\n}\n\n// A Message's Type\n//\n// It can either be `email` or `phone`\n//\n// swagger:model courierMessageType\ntype MessageType int\n\nconst (\n\tMessageTypeEmail MessageType = iota + 1\n\tMessageTypeSMS\n)\n\nconst (\n\tmessageTypeEmailText = \"email\"\n\tmessageTypeSMSText   = \"sms\"\n)\n\nfunc ToMessageType(str string) (MessageType, error) {\n\tswitch s := stringsx.SwitchExact(str); {\n\tcase s.AddCase(messageTypeEmailText):\n\t\treturn MessageTypeEmail, nil\n\tcase s.AddCase(messageTypeSMSText):\n\t\treturn MessageTypeSMS, nil\n\tdefault:\n\t\treturn 0, errors.WithStack(herodot.ErrBadRequest.WithWrap(s.ToUnknownCaseErr()).WithReason(\"Message type is not valid\"))\n\t}\n}\n\nfunc (mt MessageType) String() string {\n\tswitch mt {\n\tcase MessageTypeEmail:\n\t\treturn messageTypeEmailText\n\tcase MessageTypeSMS:\n\t\treturn messageTypeSMSText\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc (mt MessageType) IsValid() error {\n\tswitch mt {\n\tcase MessageTypeEmail, MessageTypeSMS:\n\t\treturn nil\n\tdefault:\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReason(\"Message type is not valid\"))\n\t}\n}\n\nfunc (mt MessageType) MarshalJSON() ([]byte, error) {\n\tif err := mt.IsValid(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn json.Marshal(mt.String())\n}\n\nfunc (mt *MessageType) UnmarshalJSON(data []byte) error {\n\tvar str string\n\tif err := json.Unmarshal(data, &str); err != nil {\n\t\treturn err\n\t}\n\n\tt, err := ToMessageType(str)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*mt = t\n\treturn nil\n}\n\n// swagger:model message\ntype Message struct {\n\t// required: true\n\tID uuid.UUID `json:\"id\" faker:\"-\" db:\"id\"`\n\n\tNID uuid.UUID `json:\"-\" faker:\"-\" db:\"nid\"`\n\t// required: true\n\tStatus MessageStatus `json:\"status\" db:\"status\"`\n\t// required: true\n\tType MessageType `json:\"type\" db:\"type\"`\n\t// required: true\n\tRecipient string `json:\"recipient\" db:\"recipient\"`\n\t// required: true\n\tBody string `json:\"body\" db:\"body\"`\n\t// required: true\n\tSubject string `json:\"subject\" db:\"subject\"`\n\t// required: true\n\tTemplateType template.TemplateType `json:\"template_type\" db:\"template_type\"`\n\n\tChannel sqlxx.NullString `json:\"channel\" db:\"channel\"`\n\n\tTemplateData   []byte `json:\"-\" db:\"template_data\"`\n\tRequestHeaders []byte `json:\"-\" faker:\"-\" db:\"request_headers\"`\n\t// required: true\n\tSendCount int `json:\"send_count\" db:\"send_count\"`\n\n\t// Dispatches store information about the attempts of delivering a message\n\t// May contain an error if any happened, or just the `success` state.\n\tDispatches []MessageDispatch `json:\"dispatches,omitempty\" has_many:\"courier_message_dispatches\" order_by:\"created_at desc\" faker:\"-\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\t// required: true\n\tCreatedAt time.Time `json:\"created_at\" faker:\"-\" db:\"created_at\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\t// required: true\n\tUpdatedAt time.Time `json:\"updated_at\" faker:\"-\" db:\"updated_at\"`\n}\n\nfunc (m Message) PageToken() keysetpagination.PageToken {\n\treturn keysetpagination.NewPageToken(\n\t\tkeysetpagination.Column{\n\t\t\tName:  \"created_at\",\n\t\t\tOrder: keysetpagination.OrderDescending,\n\t\t\tValue: m.CreatedAt,\n\t\t}, keysetpagination.Column{\n\t\t\tName:  \"id\",\n\t\t\tValue: m.ID,\n\t\t},\n\t)\n}\n\nfunc (m Message) DefaultPageToken() keysetpagination.PageToken {\n\treturn Message{ID: uuid.Nil, CreatedAt: time.Date(2200, 12, 31, 23, 59, 59, 0, time.UTC)}.PageToken()\n}\n\nfunc (m Message) TableName() string { return \"courier_messages\" }\nfunc (m *Message) GetID() uuid.UUID { return m.ID }\n"
  },
  {
    "path": "courier/message_dispatch.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/sqlxx\"\n)\n\n// swagger:enum CourierMessageDispatchStatus\ntype CourierMessageDispatchStatus string\n\nconst (\n\tCourierMessageDispatchStatusFailed  CourierMessageDispatchStatus = \"failed\"\n\tCourierMessageDispatchStatusSuccess CourierMessageDispatchStatus = \"success\"\n)\n\n// MessageDispatch represents an attempt of sending a courier message\n// It contains the status of the attempt (failed or successful) and the error if any occured\n//\n// swagger:model messageDispatch\ntype MessageDispatch struct {\n\t// The ID of this message dispatch\n\t// required: true\n\tID uuid.UUID `json:\"id\" db:\"id\"`\n\n\t// The ID of the message being dispatched\n\t// required: true\n\tMessageID uuid.UUID `json:\"message_id\" db:\"message_id\"`\n\n\t// The status of this dispatch\n\t// Either \"failed\" or \"success\"\n\t// required: true\n\tStatus CourierMessageDispatchStatus `json:\"status\" db:\"status\"`\n\n\t// An optional error\n\tError sqlxx.JSONRawMessage `json:\"error,omitempty\" db:\"error\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\t// required: true\n\tCreatedAt time.Time `json:\"created_at\" db:\"created_at\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\t// required: true\n\tUpdatedAt time.Time `json:\"updated_at\" db:\"updated_at\"`\n\n\tNID uuid.UUID `json:\"-\" db:\"nid\"`\n}\n\nfunc (MessageDispatch) TableName() string {\n\treturn \"courier_message_dispatches\"\n}\n"
  },
  {
    "path": "courier/message_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/courier\"\n)\n\nfunc TestMessageStatusValidity(t *testing.T) {\n\tinvalid := courier.MessageStatus(0)\n\trequire.ErrorIs(t, invalid.IsValid(), herodot.ErrBadRequest, \"IsValid() should return an error when message status is invalid\")\n}\n\nfunc TestToMessageStatus(t *testing.T) {\n\tt.Run(\"case=should return corresponding MessageStatus for given str\", func(t *testing.T) {\n\t\tfor str, exp := range map[string]courier.MessageStatus{\n\t\t\t\"queued\":     courier.MessageStatusQueued,\n\t\t\t\"sent\":       courier.MessageStatusSent,\n\t\t\t\"processing\": courier.MessageStatusProcessing,\n\t\t\t\"abandoned\":  courier.MessageStatusAbandoned,\n\t\t} {\n\t\t\tresult, err := courier.ToMessageStatus(str)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, exp, result)\n\t\t}\n\t})\n\tt.Run(\"case=should return error for invalid message status str\", func(t *testing.T) {\n\t\tresult, err := courier.ToMessageStatus(\"invalid\")\n\t\trequire.Error(t, err, herodot.ErrBadRequest)\n\t\trequire.Error(t, result.IsValid(), herodot.ErrBadRequest)\n\t})\n}\n\nfunc TestMessageTypeValidity(t *testing.T) {\n\tinvalid := courier.MessageType(0)\n\trequire.ErrorIs(t, invalid.IsValid(), herodot.ErrBadRequest, \"IsValid() should return an error when message type is invalid\")\n}\n\nfunc TestToMessageType(t *testing.T) {\n\tt.Run(\"case=should return corresponding MessageType for given str\", func(t *testing.T) {\n\t\tfor str, exp := range map[string]courier.MessageType{\n\t\t\t\"email\": courier.MessageTypeEmail,\n\t\t\t\"sms\":   courier.MessageTypeSMS,\n\t\t} {\n\t\t\tresult, err := courier.ToMessageType(str)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, exp, result)\n\t\t}\n\t})\n\tt.Run(\"case=should return error for invalid message type str\", func(t *testing.T) {\n\t\tresult, err := courier.ToMessageType(\"invalid\")\n\t\trequire.ErrorIs(t, err, herodot.ErrBadRequest)\n\t\trequire.ErrorIs(t, result.IsValid(), herodot.ErrBadRequest)\n\t})\n}\n"
  },
  {
    "path": "courier/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\tkeysetpagination \"github.com/ory/x/pagination/keysetpagination_v2\"\n)\n\nvar ErrQueueEmpty = errors.New(\"queue is empty\")\n\ntype (\n\tPersister interface {\n\t\tAddMessage(context.Context, *Message) error\n\n\t\tNextMessages(context.Context, uint8) ([]Message, error)\n\n\t\tSetMessageStatus(context.Context, uuid.UUID, MessageStatus) error\n\n\t\tLatestQueuedMessage(ctx context.Context) (*Message, error)\n\n\t\tIncrementMessageSendCount(context.Context, uuid.UUID) error\n\n\t\t// ListMessages lists all messages in the store given the page, itemsPerPage, status and recipient.\n\t\t// Returns list of messages, total count of messages satisfied by given filter, and error if any\n\t\tListMessages(context.Context, ListCourierMessagesParameters, []keysetpagination.Option) ([]Message, *keysetpagination.Paginator, error)\n\n\t\t// FetchMessage returns a message with the id or nil and an error if not found\n\t\tFetchMessage(context.Context, uuid.UUID) (*Message, error)\n\n\t\t// Records an attempt of sending out a courier message\n\t\t// Returns an error if it fails\n\t\tRecordDispatch(ctx context.Context, msgID uuid.UUID, status CourierMessageDispatchStatus, err error) error\n\t}\n\tPersistenceProvider interface {\n\t\tCourierPersister() Persister\n\t}\n)\n"
  },
  {
    "path": "courier/sms.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\nfunc (c *courier) QueueSMS(ctx context.Context, t SMSTemplate) (uuid.UUID, error) {\n\trecipient, err := t.PhoneNumber()\n\tif err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\ttemplateData, err := json.Marshal(t)\n\tif err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tbody, err := t.SMSBody(ctx)\n\tif err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\trequestHeaders := []byte(`{}`)\n\tif t, ok := t.(RequestHeadersCarrier); ok {\n\t\trequestHeaders, err = json.Marshal(t.RequestHeaders())\n\t\tif err != nil {\n\t\t\treturn uuid.Nil, err\n\t\t}\n\t}\n\n\tmessage := &Message{\n\t\tStatus:         MessageStatusQueued,\n\t\tType:           MessageTypeSMS,\n\t\tChannel:        \"sms\",\n\t\tRecipient:      recipient,\n\t\tTemplateType:   t.TemplateType(),\n\t\tTemplateData:   templateData,\n\t\tRequestHeaders: requestHeaders,\n\t\tBody:           body,\n\t}\n\tif err := c.deps.CourierPersister().AddMessage(ctx, message); err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\treturn message.ID, nil\n}\n"
  },
  {
    "path": "courier/sms_templates.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/sms\"\n)\n\ntype SMSTemplate interface {\n\tTemplate\n\tSMSBody(context.Context) (string, error)\n\tPhoneNumber() (string, error)\n}\n\nfunc NewSMSTemplateFromMessage(d template.Dependencies, m Message) (SMSTemplate, error) {\n\tswitch m.TemplateType {\n\tcase template.TypeVerificationCodeValid:\n\t\tvar t sms.VerificationCodeValidModel\n\t\tif err := json.Unmarshal(m.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn sms.NewVerificationCodeValid(d, &t), nil\n\tcase template.TypeRecoveryCodeValid:\n\t\tvar t sms.RecoveryCodeValidModel\n\t\tif err := json.Unmarshal(m.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn sms.NewRecoveryCodeValid(d, &t), nil\n\tcase template.TypeTestStub:\n\t\tvar t sms.TestStubModel\n\t\tif err := json.Unmarshal(m.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn sms.NewTestStub(&t), nil\n\tcase template.TypeLoginCodeValid:\n\t\tvar t sms.LoginCodeValidModel\n\t\tif err := json.Unmarshal(m.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn sms.NewLoginCodeValid(d, &t), nil\n\tcase template.TypeRegistrationCodeValid:\n\t\tvar t sms.RegistrationCodeValidModel\n\t\tif err := json.Unmarshal(m.TemplateData, &t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn sms.NewRegistrationCodeValid(d, &t), nil\n\n\tdefault:\n\t\treturn nil, errors.Errorf(\"received unexpected message template type: %s\", m.TemplateType)\n\t}\n}\n"
  },
  {
    "path": "courier/sms_templates_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/sms\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestSMSTemplateType(t *testing.T) {\n\tfor expectedType, tmpl := range map[template.TemplateType]courier.SMSTemplate{\n\t\ttemplate.TypeVerificationCodeValid: &sms.VerificationCodeValid{},\n\t\ttemplate.TypeTestStub:              &sms.TestStub{},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", expectedType), func(t *testing.T) {\n\t\t\trequire.Equal(t, expectedType, tmpl.TemplateType())\n\t\t})\n\t}\n}\n\nfunc TestNewSMSTemplateFromMessage(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tctx := context.Background()\n\n\tfor tmplType, expectedTmpl := range map[template.TemplateType]courier.SMSTemplate{\n\t\ttemplate.TypeVerificationCodeValid: sms.NewVerificationCodeValid(reg, &sms.VerificationCodeValidModel{To: \"+12345678901\"}),\n\t\ttemplate.TypeTestStub:              sms.NewTestStub(&sms.TestStubModel{To: \"+12345678901\", Body: \"test body\"}),\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", tmplType), func(t *testing.T) {\n\t\t\ttmplData, err := json.Marshal(expectedTmpl)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tm := courier.Message{TemplateType: tmplType, TemplateData: tmplData}\n\t\t\tactualTmpl, err := courier.NewSMSTemplateFromMessage(reg, m)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.IsType(t, expectedTmpl, actualTmpl)\n\n\t\t\texpectedRecipient, err := expectedTmpl.PhoneNumber()\n\t\t\trequire.NoError(t, err)\n\t\t\tactualRecipient, err := actualTmpl.PhoneNumber()\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, expectedRecipient, actualRecipient)\n\n\t\t\texpectedBody, err := expectedTmpl.SMSBody(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\tactualBody, err := actualTmpl.SMSBody(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, expectedBody, actualBody)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "courier/sms_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier_test\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\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/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier/template/sms\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/x/configx\"\n)\n\nfunc TestQueueSMS(t *testing.T) {\n\texpectedSender := \"Kratos Test\"\n\texpectedSMS := []*sms.TestStubModel{\n\t\t{\n\t\t\tTo:   \"+12065550101\",\n\t\t\tBody: \"test-sms-body-1\",\n\t\t},\n\t\t{\n\t\t\tTo:   \"+12065550102\",\n\t\t\tBody: \"test-sms-body-2\",\n\t\t},\n\t}\n\n\tactual := make(chan *sms.TestStubModel, len(expectedSMS))\n\tsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ttype sendSMSRequestBody struct {\n\t\t\tTo   string\n\t\t\tFrom string\n\t\t\tBody string\n\t\t}\n\n\t\trb, err := io.ReadAll(r.Body)\n\t\trequire.NoError(t, err)\n\n\t\tvar body sendSMSRequestBody\n\n\t\terr = json.Unmarshal(rb, &body)\n\t\trequire.NoError(t, err)\n\n\t\tassert.NotEmpty(t, r.Header[\"Authorization\"])\n\t\tassert.Equal(t, \"Basic bWU6MTIzNDU=\", r.Header[\"Authorization\"][0])\n\n\t\tassert.Equal(t, body.From, expectedSender)\n\t\tactual <- &sms.TestStubModel{\n\t\t\tTo:   body.To,\n\t\t\tBody: body.Body,\n\t\t}\n\t}))\n\tt.Cleanup(srv.Close)\n\n\trequestConfig := fmt.Sprintf(`{\n\t\t\"url\": \"%s\",\n\t\t\"method\": \"POST\",\n\t\t\"body\": \"file://./stub/request.config.twilio.jsonnet\",\n\t\t\"auth\": {\n\t\t\t\"type\": \"basic_auth\",\n\t\t\t\"config\": {\n\t\t\t\t\"user\":     \"me\",\n\t\t\t\t\"password\": \"12345\"\n\t\t\t}\n\t\t}\n\t}`, srv.URL)\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]any{\n\t\tconfig.ViperKeyCourierChannels: fmt.Sprintf(`[{\n\t\t\t\"id\": \"sms\",\n\t\t\t\"type\": \"http\",\n\t\t\t\"request_config\": %s\n\t\t}]`, requestConfig),\n\t\tconfig.ViperKeyCourierSMTPURL: \"http://foo.url\",\n\t}))\n\n\tc, err := reg.Courier(t.Context())\n\trequire.NoError(t, err)\n\n\tfor _, message := range expectedSMS {\n\t\tid, err := c.QueueSMS(t.Context(), sms.NewTestStub(message))\n\t\trequire.NoError(t, err)\n\t\trequire.NotEqual(t, uuid.Nil, id)\n\t}\n\n\trequire.NoError(t, c.DispatchQueue(t.Context()))\n\tclose(actual)\n\n\trequire.Len(t, actual, len(expectedSMS))\n\n\ti := 0\n\tfor message := range actual {\n\t\texpected := expectedSMS[i]\n\n\t\tassert.Equal(t, expected.To, message.To)\n\t\tassert.Equal(t, expected.Body, message.Body)\n\t\ti++\n\t}\n}\n\nfunc TestDisallowedInternalNetwork(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]any{\n\t\tconfig.ViperKeyCourierChannels: `[\n\t\t\t{\n\t\t\t\t\"id\": \"sms\",\n\t\t\t\t\"type\": \"http\",\n\t\t\t\t\"request_config\": {\n\t\t\t\t\t\"url\": \"http://127.0.0.1/\",\n\t\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\t\"body\": \"file://./stub/request.config.twilio.jsonnet\"\n\t\t\t\t}\n\t\t\t}\n\t\t]`,\n\t\tconfig.ViperKeyCourierSMTPURL:              \"http://foo.url\",\n\t\tconfig.ViperKeyClientHTTPNoPrivateIPRanges: true,\n\t}))\n\n\tc, err := reg.Courier(t.Context())\n\trequire.NoError(t, err)\n\tc.(interface {\n\t\tFailOnDispatchError()\n\t}).FailOnDispatchError()\n\t_, err = c.QueueSMS(t.Context(), sms.NewTestStub(&sms.TestStubModel{\n\t\tTo:   \"+12065550101\",\n\t\tBody: \"test-sms-body-1\",\n\t}))\n\trequire.NoError(t, err)\n\n\terr = c.DispatchQueue(t.Context())\n\tassert.ErrorContains(t, err, \"is not a permitted destination\")\n}\n"
  },
  {
    "path": "courier/smtp.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"net/mail\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\n\t\"github.com/gofrs/uuid\"\n\n\tgomail \"github.com/ory/mail/v3\"\n)\n\ntype SMTPClient struct {\n\t*gomail.Dialer\n}\n\nfunc NewSMTPClient(deps Dependencies, cfg *config.SMTPConfig) (*SMTPClient, error) {\n\turi, err := url.Parse(cfg.ConnectionURI)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"The SMTP connection URI is malformed. Please contact a system administrator.\"))\n\t}\n\n\tvar tlsCertificates []tls.Certificate\n\n\tif cfg.ClientCertPath != \"\" && cfg.ClientKeyPath != \"\" {\n\t\tclientCert, err := tls.LoadX509KeyPair(cfg.ClientCertPath, cfg.ClientKeyPath)\n\t\tif err == nil {\n\t\t\ttlsCertificates = append(tlsCertificates, clientCert)\n\t\t} else {\n\t\t\tdeps.Logger().\n\t\t\t\tWithError(err).\n\t\t\t\tError(\"Unable to load tls certificate and private key for smtp client.\")\n\t\t}\n\t}\n\n\tpassword, _ := uri.User.Password()\n\tport, _ := strconv.ParseInt(uri.Port(), 10, 0)\n\n\tdialer := &gomail.Dialer{\n\t\tHost:      uri.Hostname(),\n\t\tPort:      int(port),\n\t\tUsername:  uri.User.Username(),\n\t\tPassword:  password,\n\t\tLocalName: cfg.LocalName,\n\n\t\tTimeout:      time.Second * 10,\n\t\tRetryFailure: true,\n\t}\n\n\tsslSkipVerify, _ := strconv.ParseBool(uri.Query().Get(\"skip_ssl_verify\"))\n\n\tserverName := uri.Query().Get(\"server_name\")\n\tif serverName == \"\" {\n\t\tserverName = uri.Hostname()\n\t}\n\n\ttlsConfig := &tls.Config{\n\t\tInsecureSkipVerify: sslSkipVerify, // #nosec G402 -- This is ok (and required!) because it is configurable and disabled by default.\n\t\tCertificates:       tlsCertificates,\n\t\tServerName:         serverName,\n\t\tMinVersion:         tls.VersionTLS12,\n\t}\n\n\t// SMTP schemes\n\t// smtp: smtp clear text (with uri parameter) or with StartTLS (enforced by default)\n\t// smtps: smtp with implicit TLS (recommended way in 2021 to avoid StartTLS downgrade attacks\n\t//    and defaulting to fully-encrypted protocols https://datatracker.ietf.org/doc/html/rfc8314)\n\tswitch uri.Scheme {\n\tcase \"smtp\":\n\t\t// Enforcing StartTLS by default for security best practices (config review, etc.)\n\t\tskipStartTLS, _ := strconv.ParseBool(uri.Query().Get(\"disable_starttls\"))\n\t\tif !skipStartTLS {\n\t\t\tdialer.TLSConfig = tlsConfig\n\t\t\t// Enforcing StartTLS\n\t\t\tdialer.StartTLSPolicy = gomail.MandatoryStartTLS\n\t\t}\n\tcase \"smtps\":\n\t\tdialer.TLSConfig = tlsConfig\n\t\tdialer.SSL = true\n\t}\n\n\treturn &SMTPClient{\n\t\tDialer: dialer,\n\t}, nil\n}\n\nfunc (c *courier) QueueEmail(ctx context.Context, t EmailTemplate) (uuid.UUID, error) {\n\trecipient, err := t.EmailRecipient()\n\tif err != nil {\n\t\treturn uuid.Nil, errors.WithStack(err)\n\t}\n\tif _, err := mail.ParseAddress(recipient); err != nil {\n\t\treturn uuid.Nil, errors.WithStack(err)\n\t}\n\n\tsubject, err := t.EmailSubject(ctx)\n\tif err != nil {\n\t\treturn uuid.Nil, errors.WithStack(err)\n\t}\n\n\tbodyPlaintext, err := t.EmailBodyPlaintext(ctx)\n\tif err != nil {\n\t\treturn uuid.Nil, errors.WithStack(err)\n\t}\n\n\ttemplateData, err := json.Marshal(t)\n\tif err != nil {\n\t\treturn uuid.Nil, errors.WithStack(err)\n\t}\n\n\trequestHeaders := []byte(`{}`)\n\tif t, ok := t.(RequestHeadersCarrier); ok {\n\t\trequestHeaders, err = json.Marshal(t.RequestHeaders())\n\t\tif err != nil {\n\t\t\treturn uuid.Nil, err\n\t\t}\n\t}\n\n\tmessage := &Message{\n\t\tStatus:         MessageStatusQueued,\n\t\tType:           MessageTypeEmail,\n\t\tChannel:        \"email\",\n\t\tRecipient:      recipient,\n\t\tBody:           bodyPlaintext,\n\t\tSubject:        subject,\n\t\tTemplateType:   t.TemplateType(),\n\t\tTemplateData:   templateData,\n\t\tRequestHeaders: requestHeaders,\n\t}\n\n\tif err := c.deps.CourierPersister().AddMessage(ctx, message); err != nil {\n\t\treturn uuid.Nil, errors.WithStack(err)\n\t}\n\n\treturn message.ID, nil\n}\n"
  },
  {
    "path": "courier/smtp_channel.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/textproto\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.20.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/mail/v3\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype (\n\tSMTPChannel struct {\n\t\tsmtpClient *SMTPClient\n\t\td          Dependencies\n\n\t\tnewEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error)\n\t}\n)\n\nvar _ Channel = new(SMTPChannel)\n\nfunc NewSMTPChannel(deps Dependencies, cfg *config.SMTPConfig) (*SMTPChannel, error) {\n\treturn NewSMTPChannelWithCustomTemplates(deps, cfg, NewEmailTemplateFromMessage)\n}\n\nfunc NewSMTPChannelWithCustomTemplates(deps Dependencies, cfg *config.SMTPConfig, newEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error)) (*SMTPChannel, error) {\n\tsmtpClient, err := NewSMTPClient(deps, cfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &SMTPChannel{\n\t\tsmtpClient:                  smtpClient,\n\t\td:                           deps,\n\t\tnewEmailTemplateFromMessage: newEmailTemplateFromMessage,\n\t}, nil\n}\n\nfunc (c *SMTPChannel) ID() string {\n\treturn \"email\"\n}\n\nfunc (c *SMTPChannel) Dispatch(ctx context.Context, msg Message) (err error) {\n\tctx, span := c.d.Tracer(ctx).Tracer().Start(ctx, \"courier.SMTPChannel.Dispatch\")\n\tdefer otelx.End(span, &err)\n\n\tif c.smtpClient.Host == \"\" {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithErrorf(\"Courier tried to deliver an email but %s is not set!\", config.ViperKeyCourierSMTPURL))\n\t}\n\n\tchannels, err := c.d.CourierConfig().CourierChannels(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar cfg *config.SMTPConfig\n\tfor _, channel := range channels {\n\t\tif channel.ID == \"email\" && channel.SMTPConfig != nil {\n\t\t\tcfg = channel.SMTPConfig\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif cfg == nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithErrorf(\"Courier tried to deliver an email but SMTP channel is misconfigured.\"))\n\t}\n\n\tgm := mail.NewMessage()\n\tif cfg.FromName == \"\" {\n\t\tgm.SetHeader(\"From\", cfg.FromAddress)\n\t} else {\n\t\tgm.SetAddressHeader(\"From\", cfg.FromAddress, cfg.FromName)\n\t}\n\n\tgm.SetHeader(\"To\", msg.Recipient)\n\tgm.SetHeader(\"Subject\", msg.Subject)\n\n\theaders := cfg.Headers\n\tfor k, v := range headers {\n\t\tgm.SetHeader(k, v)\n\t}\n\n\tgm.SetBody(\"text/plain\", msg.Body)\n\n\tlogger := c.d.Logger().\n\t\tWithField(\"smtp_server\", net.JoinHostPort(c.smtpClient.Host, strconv.Itoa(c.smtpClient.Port))).\n\t\tWithField(\"smtp_ssl_enabled\", c.smtpClient.SSL).\n\t\tWithField(\"message_from\", cfg.FromAddress).\n\t\tWithField(\"message_id\", msg.ID).\n\t\tWithField(\"message_nid\", msg.NID).\n\t\tWithField(\"message_type\", msg.Type).\n\t\tWithField(\"message_template_type\", msg.TemplateType).\n\t\tWithField(\"message_subject\", msg.Subject).\n\t\tWithField(\"trace_id\", span.SpanContext().TraceID())\n\n\ttmpl, err := c.newEmailTemplateFromMessage(c.d, msg)\n\tif err != nil {\n\t\tlogger.\n\t\t\tWithError(err).Error(`Unable to get email template from message.`)\n\t} else if htmlBody, err := tmpl.EmailBody(ctx); err != nil {\n\t\tlogger.\n\t\t\tWithError(err).Error(`Unable to get email body from template.`)\n\t} else {\n\t\tgm.AddAlternative(\"text/html\", htmlBody)\n\t}\n\n\tdialCtx, dialSpan := c.d.Tracer(ctx).Tracer().Start(ctx, \"courier.SMTPChannel.Dispatch.Dial\", trace.WithAttributes(\n\t\tsemconv.NetPeerName(c.smtpClient.Host),\n\t\tsemconv.NetPeerPort(c.smtpClient.Port),\n\t\tsemconv.NetProtocolName(\"smtp\"),\n\t))\n\tsnd, err := c.smtpClient.Dial(dialCtx)\n\totelx.End(dialSpan, &err)\n\n\tif err != nil {\n\t\tlogger.\n\t\t\tWithError(err).\n\t\t\tWithField(\"smtp_host\", c.smtpClient.Host).\n\t\t\tWithField(\"smtp_port\", c.smtpClient.Port).\n\t\t\tError(\"Unable to dial SMTP connection.\")\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.\n\t\t\tWithError(err.Error()).WithReason(\"failed to send email via smtp\"))\n\t}\n\tdefer func() { _ = snd.Close() }()\n\n\tsendCtx, sendSpan := c.d.Tracer(ctx).Tracer().Start(ctx, \"courier.SMTPChannel.Dispatch.Send\")\n\terr = mail.Send(sendCtx, snd, gm)\n\totelx.End(sendSpan, &err)\n\n\tif err != nil {\n\t\tlogger.\n\t\t\tWithError(err).\n\t\t\tError(\"Unable to send email using SMTP connection.\")\n\n\t\tvar protoErr *textproto.Error\n\t\tvar mailErr *mail.SendError\n\n\t\tswitch {\n\t\tcase errors.As(err, &mailErr) && errors.As(mailErr.Cause, &protoErr) && protoErr.Code >= 500:\n\t\t\tfallthrough\n\t\tcase errors.As(err, &protoErr) && protoErr.Code >= 500:\n\t\t\t// See https://en.wikipedia.org/wiki/List_of_SMTP_server_return_codes\n\t\t\t// If the SMTP server responds with 5xx, sending the message should not be retried (without changing something about the request)\n\t\t\tif err := c.d.CourierPersister().SetMessageStatus(ctx, msg.ID, MessageStatusAbandoned); err != nil {\n\t\t\t\tlogger.\n\t\t\t\t\tWithError(err).\n\t\t\t\t\tError(`Unable to reset the retried message's status to \"abandoned\".`)\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\t\t}\n\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.\n\t\t\tWithError(err.Error()).WithReason(\"failed to send email via smtp\"))\n\t}\n\n\tdispatchDuration := time.Since(msg.CreatedAt).Milliseconds()\n\tlogger.WithField(\"dispatch_duration_ms\", dispatchDuration).Debug(\"Courier sent out message.\")\n\n\treturn nil\n}\n"
  },
  {
    "path": "courier/smtp_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage courier_test\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/pem\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/big\"\n\t\"net/http\"\n\t\"os\"\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/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/courier\"\n\ttemplates \"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/x\"\n\tgomail \"github.com/ory/mail/v3\"\n\t\"github.com/ory/x/configx\"\n)\n\nfunc TestNewSMTPClientPreventLeak(t *testing.T) {\n\t// Test for https://hackerone.com/reports/2384028\n\n\tinvalidURL := \"sm<>t>p://f%oo::bar:baz@my-server:1234:122/\"\n\tconf, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValue(config.ViperKeyCourierSMTPURL, invalidURL))\n\n\tchannels, err := conf.CourierChannels(t.Context())\n\trequire.NoError(t, err)\n\trequire.Len(t, channels, 1)\n\n\t_, err = courier.NewSMTPClient(reg, channels[0].SMTPConfig)\n\trequire.Error(t, err)\n\tassert.NotContains(t, err.Error(), invalidURL)\n}\n\nfunc TestNewSMTP(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tsetupSMTPClient := func(stringURL string) *courier.SMTPClient {\n\t\tconf.MustSet(t.Context(), config.ViperKeyCourierSMTPURL, stringURL)\n\n\t\tchannels, err := conf.CourierChannels(t.Context())\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, channels, 1)\n\t\tc, err := courier.NewSMTPClient(reg, channels[0].SMTPConfig)\n\t\trequire.NoError(t, err)\n\t\treturn c\n\t}\n\n\t// Should enforce StartTLS => dialer.StartTLSPolicy = gomail.MandatoryStartTLS and dialer.SSL = false\n\tsmtp := setupSMTPClient(\"smtp://foo:bar@my-server:1234/\")\n\tassert.Equal(t, smtp.StartTLSPolicy, gomail.MandatoryStartTLS, \"StartTLS not enforced\")\n\tassert.Equal(t, smtp.SSL, false, \"Implicit TLS should not be enabled\")\n\n\t// Should enforce TLS => dialer.SSL = true\n\tsmtp = setupSMTPClient(\"smtps://foo:bar@my-server:1234/\")\n\tassert.Equal(t, smtp.SSL, true, \"Implicit TLS should be enabled\")\n\n\t// Should allow cleartext => dialer.StartTLSPolicy = gomail.OpportunisticStartTLS and dialer.SSL = false\n\tsmtp = setupSMTPClient(\"smtp://foo:bar@my-server:1234/?disable_starttls=true\")\n\tassert.Equal(t, smtp.StartTLSPolicy, gomail.OpportunisticStartTLS, \"StartTLS is enforced\")\n\tassert.Equal(t, smtp.SSL, false, \"Implicit TLS should not be enabled\")\n\n\t// Test cert based SMTP client auth\n\tclientCert, clientKey, err := generateTestClientCert(t)\n\trequire.NoError(t, err)\n\tt.Cleanup(func() { _ = os.Remove(clientCert.Name()) }) //nolint:gosec\n\tt.Cleanup(func() { _ = os.Remove(clientKey.Name()) })  //nolint:gosec\n\n\tconf.MustSet(t.Context(), config.ViperKeyCourierSMTPClientCertPath, clientCert.Name())\n\tconf.MustSet(t.Context(), config.ViperKeyCourierSMTPClientKeyPath, clientKey.Name())\n\n\tclientPEM, err := tls.LoadX509KeyPair(clientCert.Name(), clientKey.Name())\n\trequire.NoError(t, err)\n\n\tsmtpWithCert := setupSMTPClient(\"smtps://subdomain.my-server:1234/?server_name=my-server\")\n\tassert.Equal(t, smtpWithCert.SSL, true, \"Implicit TLS should be enabled\")\n\tassert.Equal(t, smtpWithCert.Host, \"subdomain.my-server\", \"SMTP Dialer host should match\")\n\tassert.Equal(t, smtpWithCert.TLSConfig.ServerName, \"my-server\", \"TLS config server name should match\")\n\tassert.Equal(t, smtpWithCert.TLSConfig.ServerName, \"my-server\", \"TLS config server name should match\")\n\tassert.Contains(t, smtpWithCert.TLSConfig.Certificates, clientPEM, \"TLS config should contain client pem\")\n\n\t// error case: invalid client key\n\trequire.NoError(t, conf.Set(t.Context(), config.ViperKeyCourierSMTPClientKeyPath, clientCert.Name())) // mixup client key and client cert\n\tsmtpWithCert = setupSMTPClient(\"smtps://subdomain.my-server:1234/?server_name=my-server\")\n\tassert.Equal(t, len(smtpWithCert.TLSConfig.Certificates), 0, \"TLS config certificates should be empty\")\n}\n\nfunc TestQueueEmail(t *testing.T) {\n\tsmtp, api := x.StartMailhog(t, true)\n\n\t_, reg := pkg.NewRegistryDefaultWithDSN(t, \"\", configx.WithValues(map[string]any{\n\t\tconfig.ViperKeyCourierSMTPURL:                            smtp,\n\t\tconfig.ViperKeyCourierSMTPFrom:                           \"test-stub@ory.sh\",\n\t\tconfig.ViperKeyCourierSMTPFromName:                       \"Bob\",\n\t\tconfig.ViperKeyCourierSMTPHeaders + \".test-stub-header1\": \"foo\",\n\t\tconfig.ViperKeyCourierSMTPHeaders + \".test-stub-header2\": \"bar\",\n\t\tconfig.ViperKeyCourierMessageRetries:                     50,\n\t}))\n\n\tc, err := reg.Courier(t.Context())\n\trequire.NoError(t, err)\n\tc.FailOnDispatchError()\n\n\t_, err = c.QueueEmail(t.Context(), templates.NewTestStub(&templates.TestStubModel{\n\t\tTo:      \"invalid-email\",\n\t\tSubject: \"test-subject-1\",\n\t\tBody:    \"test-body-1\",\n\t}))\n\trequire.Error(t, err)\n\n\tid, err := c.QueueEmail(t.Context(), templates.NewTestStub(&templates.TestStubModel{\n\t\tTo:      \"test-recipient-1@example.org\",\n\t\tSubject: \"test-subject-1\",\n\t\tBody:    \"test-body-1\",\n\t}))\n\trequire.NoError(t, err)\n\trequire.NotZero(t, id)\n\n\tid, err = c.QueueEmail(t.Context(), templates.NewTestStub(&templates.TestStubModel{\n\t\tTo:      \"test-recipient-2@example.org\",\n\t\tSubject: \"test-subject-2\",\n\t\tBody:    \"test-body-2\",\n\t}))\n\trequire.NoError(t, err)\n\trequire.NotZero(t, id)\n\n\tid, err = c.QueueEmail(t.Context(), templates.NewTestStub(&templates.TestStubModel{\n\t\tTo:      \"test-recipient-3@example.org\",\n\t\tSubject: \"test-subject-3\",\n\t\tBody:    \"test-body-3\",\n\t}))\n\trequire.NoError(t, err)\n\trequire.NotZero(t, id)\n\n\trequire.EventuallyWithT(t, func(t *assert.CollectT) {\n\t\trequire.NoError(t, c.DispatchQueue(context.Background()))\n\t}, time.Second, 10*time.Millisecond)\n\n\tres, err := http.Get(api + \"/api/v2/messages\")\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\n\tbody, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\trequire.EqualValues(t, 3, gjson.GetBytes(body, \"total\").Int())\n\n\tfor k := 1; k <= 3; k++ {\n\t\tassert.Contains(t, string(body), fmt.Sprintf(\"test-subject-%d\", k))\n\t\tassert.Contains(t, string(body), fmt.Sprintf(\"test-body-%d\", k))\n\t\tassert.Contains(t, string(body), fmt.Sprintf(\"test-recipient-%d@example.org\", k))\n\t\tassert.Contains(t, string(body), \"test-stub@ory.sh\")\n\t}\n\n\tassert.Contains(t, string(body), \"Bob\")\n\tassert.Contains(t, string(body), `\"test-stub-header1\":[\"foo\"]`)\n\tassert.Contains(t, string(body), `\"test-stub-header2\":[\"bar\"]`)\n}\n\nfunc generateTestClientCert(t *testing.T) (clientCert *os.File, clientKey *os.File, err error) {\n\thostName := flag.String(\"host\", \"127.0.0.1\", \"Hostname to certify\")\n\tpriv, err := rsa.GenerateKey(rand.Reader, 1024) // #nosec G403 -- test code\n\trequire.NoError(t, err)\n\tnow := time.Now()\n\tcertTemplate := x509.Certificate{\n\t\tSerialNumber: big.NewInt(1234),\n\t\tSubject: pkix.Name{\n\t\t\tCommonName:   *hostName,\n\t\t\tOrganization: []string{\"myorg\"},\n\t\t},\n\t\tNotBefore:    now.Add(-300 * time.Second),\n\t\tNotAfter:     now.Add(24 * time.Hour),\n\t\tSubjectKeyId: []byte{1, 2, 3, 4},\n\t\tKeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,\n\t}\n\tcert, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &priv.PublicKey, priv)\n\trequire.NoError(t, err)\n\tclientCert, err = os.CreateTemp(\"./test\", \"testCert\")\n\trequire.NoError(t, err)\n\tdefer func() { _ = clientCert.Close() }()\n\n\trequire.NoError(t, pem.Encode(clientCert, &pem.Block{Type: \"CERTIFICATE\", Bytes: cert}))\n\n\tclientKey, err = os.CreateTemp(\"./test\", \"testKey\")\n\trequire.NoError(t, err)\n\tdefer func() { _ = clientKey.Close() }()\n\trequire.NoError(t, pem.Encode(clientKey, &pem.Block{Type: \"RSA PRIVATE KEY\", Bytes: x509.MarshalPKCS1PrivateKey(priv)}))\n\n\treturn clientCert, clientKey, nil\n}\n"
  },
  {
    "path": "courier/stub/request.config.mailer.jsonnet",
    "content": "function(ctx) {\n  recipient: ctx.recipient,\n  template_type: ctx.template_type,\n  to: if \"template_data\" in ctx && \"to\" in ctx.template_data then ctx.template_data.to else null,\n  recovery_code: if \"template_data\" in ctx && \"recovery_code\" in ctx.template_data then ctx.template_data.recovery_code else null,\n  recovery_url: if \"template_data\" in ctx && \"recovery_url\" in ctx.template_data then ctx.template_data.recovery_url else null,\n  verification_url: if \"template_data\" in ctx && \"verification_url\" in ctx.template_data then ctx.template_data.verification_url else null,\n  verification_code: if \"template_data\" in ctx && \"verification_code\" in ctx.template_data then ctx.template_data.verification_code else null,\n  subject: if \"template_data\" in ctx && \"subject\" in ctx.template_data then ctx.template_data.subject else null,\n  body: if \"template_data\" in ctx && \"body\" in ctx.template_data then ctx.template_data.body else null,\n  html_body: if \"template_data\" in ctx && \"html_body\" in ctx.template_data then ctx.template_data.html_body else null\n}\n"
  },
  {
    "path": "courier/stub/request.config.twilio.jsonnet",
    "content": "function(ctx) {\n  from: \"Kratos Test\",\n  to: ctx.recipient,\n  body: ctx.body\n}\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/login_code/valid/email.body.gotmpl",
    "content": "Login to your account with the following code:\n\n{{ .LoginCode }}\n\nIt expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/login_code/valid/email.body.plaintext.gotmpl",
    "content": "Login to your account with the following code:\n\n{{ .LoginCode }}\n\nIt expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/login_code/valid/email.subject.gotmpl",
    "content": "Use code {{ .LoginCode }} to log in\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/login_code/valid/sms.body.gotmpl",
    "content": "Your login code is: {{ .LoginCode }}\n\nIt expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/otp/sms.body.gotmpl",
    "content": "Your verification code is: {{ .Code }}\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery/invalid/email.body.gotmpl",
    "content": "Hi,\n\nyou (or someone else) entered this email address when trying to recover access to an account.\n\nHowever, this email address is not on our database of registered users and therefore the attempt has failed.\n\nIf this was you, check if you signed up using a different address.\n\nIf this was not you, please ignore this email.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery/invalid/email.body.plaintext.gotmpl",
    "content": "Hi,\n\nyou (or someone else) entered this email address when trying to recover access to an account.\n\nHowever, this email address is not on our database of registered users and therefore the attempt has failed.\n\nIf this was you, check if you signed up using a different address.\n\nIf this was not you, please ignore this email.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery/invalid/email.subject.gotmpl",
    "content": "Account access attempted\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery/valid/email.body.gotmpl",
    "content": "Recover access to your account by clicking the following link:\n\n<a href=\"{{ .RecoveryURL }}\">{{ .RecoveryURL }}</a>\n\nIf this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery/valid/email.body.plaintext.gotmpl",
    "content": "Recover access to your account by clicking the following link:\n\n{{ .RecoveryURL }}\n\nIf this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes.\n\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery/valid/email.subject.gotmpl",
    "content": "Recover access to your account\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery_code/invalid/email.body.gotmpl",
    "content": "Hi,\n\nyou (or someone else) entered this email address when trying to recover access to an account.\n\nHowever, this email address is not on our database of registered users and therefore the attempt has failed.\n\nIf this was you, check if you signed up using a different address.\n\nIf this was not you, please ignore this email.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery_code/invalid/email.body.plaintext.gotmpl",
    "content": "Hi,\n\nyou (or someone else) entered this email address when trying to recover access to an account.\n\nHowever, this email address is not on our database of registered users and therefore the attempt has failed.\n\nIf this was you, check if you signed up using a different address.\n\nIf this was not you, please ignore this email.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery_code/invalid/email.subject.gotmpl",
    "content": "Account access attempted\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery_code/valid/email.body.gotmpl",
    "content": "Recover access to your account by entering the following code:\n\n{{ .RecoveryCode }}\n\nIf this was not you, do nothing. This code expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery_code/valid/email.body.plaintext.gotmpl",
    "content": "Recover access to your account by entering the following code:\n\n{{ .RecoveryCode }}\n\nIf this was not you, do nothing. This code expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery_code/valid/email.subject.gotmpl",
    "content": "Use code {{ .RecoveryCode }} to recover access to your account\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/recovery_code/valid/sms.body.gotmpl",
    "content": "Your recovery code is: {{ .RecoveryCode }}\n\n@{{ .RequestURLDomain }} #{{ .RecoveryCode }}\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/registration_code/valid/email.body.gotmpl",
    "content": "Complete your account registration with the following code:\n\n{{ .RegistrationCode }}\n\nThis code expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/registration_code/valid/email.body.plaintext.gotmpl",
    "content": "Complete your account registration with the following code:\n\n{{ .RegistrationCode }}\n\nThis code expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/registration_code/valid/email.subject.gotmpl",
    "content": "Use code {{ .RegistrationCode }} to complete your account registration\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/registration_code/valid/sms.body.gotmpl",
    "content": "Your registration code is: {{ .RegistrationCode }}\n\nIt expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/test_stub/email.body.gotmpl",
    "content": "stub email body {{ if .HTMLBody }}{{ .HTMLBody }}{{ else }}{{ .Body }}{{ end }}"
  },
  {
    "path": "courier/template/courier/builtin/templates/test_stub/email.body.html.en_US.gotmpl",
    "content": "{{ $l := cat \"lang=\" .lang }}\n{{ nospace $l }}\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/test_stub/email.body.html.gotmpl",
    "content": "{{- if eq .lang \"en_US\" -}}\n{{ template \"email.body.html.en_US.gotmpl\" . }}\n{{- end -}}\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/test_stub/email.body.plaintext.gotmpl",
    "content": "stub email body {{ .Body }}"
  },
  {
    "path": "courier/template/courier/builtin/templates/test_stub/email.body.sprig.gotmpl",
    "content": "{{ $t1 := title .input }}\n{{ $t1 := nospace $t1 }}\n{{ $t2 := upper $t1 }}\n{{ $t3 := cat $t1 \",\" $t2 }}\n{{ nospace $t3 }}\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/test_stub/email.subject.gotmpl",
    "content": "stub email subject {{ .Subject }}"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification/invalid/email.body.gotmpl",
    "content": "Someone asked to verify this email address, but we were unable to find an account for this address.\n\nIf this was you, check if you signed up using a different address.\n\nIf this was not you, please ignore this email.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification/invalid/email.body.plaintext.gotmpl",
    "content": "Someone asked to verify this email address, but we were unable to find an account for this address.\n\nIf this was you, check if you signed up using a different address.\n\nIf this was not you, please ignore this email.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification/invalid/email.subject.gotmpl",
    "content": "Someone tried to verify this email address\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification/valid/email.body.gotmpl",
    "content": "Verify your account by opening the following link:\n\n<a href=\"{{ .VerificationURL }}\">{{ .VerificationURL }}</a>\n\nIf this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification/valid/email.body.plaintext.gotmpl",
    "content": "Verify your account by opening the following link:\n\n{{ .VerificationURL }}\n\nIf this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification/valid/email.subject.gotmpl",
    "content": "Please verify your email address\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification_code/invalid/email.body.gotmpl",
    "content": "Hi,\n\nsomeone asked to verify this email address, but we were unable to find an account for this address.\n\nIf this was you, check if you signed up using a different address.\n\nIf this was not you, please ignore this email.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification_code/invalid/email.body.plaintext.gotmpl",
    "content": "Hi,\n\nsomeone asked to verify this email address, but we were unable to find an account for this address.\n\nIf this was you, check if you signed up using a different address.\n\nIf this was not you, please ignore this email.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification_code/invalid/email.subject.gotmpl",
    "content": "Someone tried to verify this email address\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification_code/valid/email.body.gotmpl",
    "content": "Verify your account with the following code:\n\n{{ .VerificationCode }}\n\nor clicking the following link:\n\n<a href=\"{{ .VerificationURL }}\">{{ .VerificationURL }}</a>\n\nIf this was not you, do nothing. This code / link expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification_code/valid/email.body.plaintext.gotmpl",
    "content": "Verify your account with the following code:\n\n{{ .VerificationCode }}\n\nor clicking the following link:\n\n{{ .VerificationURL }}\n\nIf this was not you, do nothing. This code / link expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification_code/valid/email.subject.gotmpl",
    "content": "Use code {{ .VerificationCode }} to verify your account\n"
  },
  {
    "path": "courier/template/courier/builtin/templates/verification_code/valid/sms.body.gotmpl",
    "content": "Your verification code is: {{ .VerificationCode }}\n\nIf this was not you, do nothing. It expires in {{ .ExpiresInMinutes }} minutes.\n"
  },
  {
    "path": "courier/template/email/login_code_valid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tLoginCodeValid struct {\n\t\tdeps  template.Dependencies\n\t\tmodel *LoginCodeValidModel\n\t}\n\tLoginCodeValidModel struct {\n\t\tTo                 string                 `json:\"to\"`\n\t\tLoginCode          string                 `json:\"login_code\"`\n\t\tIdentity           map[string]interface{} `json:\"identity\"`\n\t\tRequestURL         string                 `json:\"request_url\"`\n\t\tTransientPayload   map[string]interface{} `json:\"transient_payload\"`\n\t\tExpiresInMinutes   int                    `json:\"expires_in_minutes\"`\n\t\tUserRequestHeaders http.Header            `json:\"-\"`\n\t}\n)\n\nfunc NewLoginCodeValid(d template.Dependencies, m *LoginCodeValidModel) *LoginCodeValid {\n\treturn &LoginCodeValid{deps: d, model: m}\n}\n\nfunc (t *LoginCodeValid) EmailRecipient() (string, error) {\n\treturn t.model.To, nil\n}\n\nfunc (t *LoginCodeValid) EmailSubject(ctx context.Context) (string, error) {\n\tsubject, err := template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"login_code/valid/email.subject.gotmpl\", \"login_code/valid/email.subject*\", t.model, t.deps.CourierConfig().CourierTemplatesLoginCodeValid(ctx).Subject)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *LoginCodeValid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"login_code/valid/email.body.gotmpl\", \"login_code/valid/email.body*\", t.model, t.deps.CourierConfig().CourierTemplatesLoginCodeValid(ctx).Body.HTML)\n}\n\nfunc (t *LoginCodeValid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"login_code/valid/email.body.plaintext.gotmpl\", \"login_code/valid/email.body.plaintext*\", t.model, t.deps.CourierConfig().CourierTemplatesLoginCodeValid(ctx).Body.PlainText)\n}\n\nfunc (t *LoginCodeValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.model)\n}\n\nfunc (t *LoginCodeValid) TemplateType() template.TemplateType {\n\treturn template.TypeLoginCodeValid\n}\nfunc (t *LoginCodeValid) RequestHeaders() http.Header {\n\treturn t.model.UserRequestHeaders\n}\n"
  },
  {
    "path": "courier/template/email/login_code_valid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestLoginCodeValid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewLoginCodeValid(reg, &email.LoginCodeValidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"test=with remote resources\", func(t *testing.T) {\n\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/login_code/valid\", template.TypeLoginCodeValid)\n\t})\n}\n"
  },
  {
    "path": "courier/template/email/recovery_code_invalid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tRecoveryCodeInvalid struct {\n\t\tdeps  template.Dependencies\n\t\tmodel *RecoveryCodeInvalidModel\n\t}\n\tRecoveryCodeInvalidModel struct {\n\t\tTo               string                 `json:\"to\"`\n\t\tRequestURL       string                 `json:\"request_url\"`\n\t\tTransientPayload map[string]interface{} `json:\"transient_payload\"`\n\t}\n)\n\nfunc NewRecoveryCodeInvalid(d template.Dependencies, m *RecoveryCodeInvalidModel) *RecoveryCodeInvalid {\n\treturn &RecoveryCodeInvalid{deps: d, model: m}\n}\n\nfunc (t *RecoveryCodeInvalid) EmailRecipient() (string, error) {\n\treturn t.model.To, nil\n}\n\nfunc (t *RecoveryCodeInvalid) EmailSubject(ctx context.Context) (string, error) {\n\tfilesystem := os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx))\n\tremoteURL := t.deps.CourierConfig().CourierTemplatesRecoveryCodeInvalid(ctx).Subject\n\n\tsubject, err := template.LoadText(ctx, t.deps, filesystem, \"recovery_code/invalid/email.subject.gotmpl\", \"recovery_code/invalid/email.subject*\", t.model, remoteURL)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *RecoveryCodeInvalid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery_code/invalid/email.body.gotmpl\", \"recovery_code/invalid/email.body*\", t.model, t.deps.CourierConfig().CourierTemplatesRecoveryCodeInvalid(ctx).Body.HTML)\n}\n\nfunc (t *RecoveryCodeInvalid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery_code/invalid/email.body.plaintext.gotmpl\", \"recovery_code/invalid/email.body.plaintext*\", t.model, t.deps.CourierConfig().CourierTemplatesRecoveryCodeInvalid(ctx).Body.PlainText)\n}\n\nfunc (t *RecoveryCodeInvalid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.model)\n}\n\nfunc (t *RecoveryCodeInvalid) TemplateType() template.TemplateType {\n\treturn template.TypeRecoveryCodeInvalid\n}\n"
  },
  {
    "path": "courier/template/email/recovery_code_invalid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestRecoveryCodeInvalid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewRecoveryCodeInvalid(reg, &email.RecoveryCodeInvalidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"case=test remote resources\", func(t *testing.T) {\n\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/recovery_code/invalid\", template.TypeRecoveryCodeInvalid)\n\t})\n}\n"
  },
  {
    "path": "courier/template/email/recovery_code_valid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tRecoveryCodeValid struct {\n\t\tdeps  template.Dependencies\n\t\tmodel *RecoveryCodeValidModel\n\t}\n\tRecoveryCodeValidModel struct {\n\t\tTo                 string                 `json:\"to\"`\n\t\tRecoveryCode       string                 `json:\"recovery_code\"`\n\t\tIdentity           map[string]interface{} `json:\"identity\"`\n\t\tRequestURL         string                 `json:\"request_url\"`\n\t\tTransientPayload   map[string]interface{} `json:\"transient_payload\"`\n\t\tExpiresInMinutes   int                    `json:\"expires_in_minutes\"`\n\t\tUserRequestHeaders http.Header            `json:\"-\"`\n\t}\n)\n\nfunc NewRecoveryCodeValid(d template.Dependencies, m *RecoveryCodeValidModel) *RecoveryCodeValid {\n\treturn &RecoveryCodeValid{deps: d, model: m}\n}\n\nfunc (t *RecoveryCodeValid) EmailRecipient() (string, error) {\n\treturn t.model.To, nil\n}\n\nfunc (t *RecoveryCodeValid) EmailSubject(ctx context.Context) (string, error) {\n\tsubject, err := template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery_code/valid/email.subject.gotmpl\", \"recovery_code/valid/email.subject*\", t.model, t.deps.CourierConfig().CourierTemplatesRecoveryCodeValid(ctx).Subject)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *RecoveryCodeValid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery_code/valid/email.body.gotmpl\", \"recovery_code/valid/email.body*\", t.model, t.deps.CourierConfig().CourierTemplatesRecoveryCodeValid(ctx).Body.HTML)\n}\n\nfunc (t *RecoveryCodeValid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery_code/valid/email.body.plaintext.gotmpl\", \"recovery_code/valid/email.body.plaintext*\", t.model, t.deps.CourierConfig().CourierTemplatesRecoveryCodeValid(ctx).Body.PlainText)\n}\n\nfunc (t *RecoveryCodeValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.model)\n}\n\nfunc (t *RecoveryCodeValid) TemplateType() template.TemplateType {\n\treturn template.TypeRecoveryCodeValid\n}\nfunc (t *RecoveryCodeValid) RequestHeaders() http.Header {\n\treturn t.model.UserRequestHeaders\n}\n"
  },
  {
    "path": "courier/template/email/recovery_code_valid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestRecoveryCodeValid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewRecoveryCodeValid(reg, &email.RecoveryCodeValidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"test=with remote resources\", func(t *testing.T) {\n\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/recovery_code/valid\", template.TypeRecoveryCodeValid)\n\t})\n}\n"
  },
  {
    "path": "courier/template/email/recovery_invalid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tRecoveryInvalid struct {\n\t\td template.Dependencies\n\t\tm *RecoveryInvalidModel\n\t}\n\tRecoveryInvalidModel struct {\n\t\tTo               string                 `json:\"to\"`\n\t\tRequestURL       string                 `json:\"request_url\"`\n\t\tTransientPayload map[string]interface{} `json:\"transient_payload\"`\n\t}\n)\n\nfunc NewRecoveryInvalid(d template.Dependencies, m *RecoveryInvalidModel) *RecoveryInvalid {\n\treturn &RecoveryInvalid{d: d, m: m}\n}\n\nfunc (t *RecoveryInvalid) EmailRecipient() (string, error) {\n\treturn t.m.To, nil\n}\n\nfunc (t *RecoveryInvalid) EmailSubject(ctx context.Context) (string, error) {\n\tsubject, err := template.LoadText(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery/invalid/email.subject.gotmpl\", \"recovery/invalid/email.subject*\", t.m, t.d.CourierConfig().CourierTemplatesRecoveryInvalid(ctx).Subject)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *RecoveryInvalid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery/invalid/email.body.gotmpl\", \"recovery/invalid/email.body*\", t.m, t.d.CourierConfig().CourierTemplatesRecoveryInvalid(ctx).Body.HTML)\n}\n\nfunc (t *RecoveryInvalid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery/invalid/email.body.plaintext.gotmpl\", \"recovery/invalid/email.body.plaintext*\", t.m, t.d.CourierConfig().CourierTemplatesRecoveryInvalid(ctx).Body.PlainText)\n}\n\nfunc (t *RecoveryInvalid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.m)\n}\n\nfunc (t *RecoveryInvalid) TemplateType() template.TemplateType {\n\treturn template.TypeRecoveryInvalid\n}\n"
  },
  {
    "path": "courier/template/email/recovery_invalid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestRecoverInvalid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewRecoveryInvalid(reg, &email.RecoveryInvalidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"case=test remote resources\", func(t *testing.T) {\n\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/recovery/invalid\", template.TypeRecoveryInvalid)\n\t})\n}\n"
  },
  {
    "path": "courier/template/email/recovery_valid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tRecoveryValid struct {\n\t\td template.Dependencies\n\t\tm *RecoveryValidModel\n\t}\n\tRecoveryValidModel struct {\n\t\tTo               string                 `json:\"to\"`\n\t\tRecoveryURL      string                 `json:\"recovery_url\"`\n\t\tIdentity         map[string]interface{} `json:\"identity\"`\n\t\tRequestURL       string                 `json:\"request_url\"`\n\t\tTransientPayload map[string]interface{} `json:\"transient_payload\"`\n\t\tExpiresInMinutes int                    `json:\"expires_in_minutes\"`\n\t}\n)\n\nfunc NewRecoveryValid(d template.Dependencies, m *RecoveryValidModel) *RecoveryValid {\n\treturn &RecoveryValid{d: d, m: m}\n}\n\nfunc (t *RecoveryValid) EmailRecipient() (string, error) {\n\treturn t.m.To, nil\n}\n\nfunc (t *RecoveryValid) EmailSubject(ctx context.Context) (string, error) {\n\tsubject, err := template.LoadText(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery/valid/email.subject.gotmpl\", \"recovery/valid/email.subject*\", t.m, t.d.CourierConfig().CourierTemplatesRecoveryValid(ctx).Subject)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *RecoveryValid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery/valid/email.body.gotmpl\", \"recovery/valid/email.body*\", t.m, t.d.CourierConfig().CourierTemplatesRecoveryValid(ctx).Body.HTML)\n}\n\nfunc (t *RecoveryValid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"recovery/valid/email.body.plaintext.gotmpl\", \"recovery/valid/email.body.plaintext*\", t.m, t.d.CourierConfig().CourierTemplatesRecoveryValid(ctx).Body.PlainText)\n}\n\nfunc (t *RecoveryValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.m)\n}\n\nfunc (t *RecoveryValid) TemplateType() template.TemplateType {\n\treturn template.TypeRecoveryValid\n}\n"
  },
  {
    "path": "courier/template/email/recovery_valid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestRecoverValid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewRecoveryValid(reg, &email.RecoveryValidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"test=with remote resources\", func(t *testing.T) {\n\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/recovery/valid\", template.TypeRecoveryValid)\n\t})\n}\n"
  },
  {
    "path": "courier/template/email/registration_code_valid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tRegistrationCodeValid struct {\n\t\tdeps  template.Dependencies\n\t\tmodel *RegistrationCodeValidModel\n\t}\n\tRegistrationCodeValidModel struct {\n\t\tTo                 string                 `json:\"to\"`\n\t\tTraits             map[string]interface{} `json:\"traits\"`\n\t\tRegistrationCode   string                 `json:\"registration_code\"`\n\t\tRequestURL         string                 `json:\"request_url\"`\n\t\tTransientPayload   map[string]interface{} `json:\"transient_payload\"`\n\t\tExpiresInMinutes   int                    `json:\"expires_in_minutes\"`\n\t\tUserRequestHeaders http.Header            `json:\"-\"`\n\t}\n)\n\nfunc NewRegistrationCodeValid(d template.Dependencies, m *RegistrationCodeValidModel) *RegistrationCodeValid {\n\treturn &RegistrationCodeValid{deps: d, model: m}\n}\n\nfunc (t *RegistrationCodeValid) EmailRecipient() (string, error) {\n\treturn t.model.To, nil\n}\n\nfunc (t *RegistrationCodeValid) EmailSubject(ctx context.Context) (string, error) {\n\tsubject, err := template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"registration_code/valid/email.subject.gotmpl\", \"registration_code/valid/email.subject*\", t.model, t.deps.CourierConfig().CourierTemplatesRegistrationCodeValid(ctx).Subject)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *RegistrationCodeValid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"registration_code/valid/email.body.gotmpl\", \"registration_code/valid/email.body*\", t.model, t.deps.CourierConfig().CourierTemplatesRegistrationCodeValid(ctx).Body.HTML)\n}\n\nfunc (t *RegistrationCodeValid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(ctx, t.deps, os.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)), \"registration_code/valid/email.body.plaintext.gotmpl\", \"registration_code/valid/email.body.plaintext*\", t.model, t.deps.CourierConfig().CourierTemplatesRegistrationCodeValid(ctx).Body.PlainText)\n}\n\nfunc (t *RegistrationCodeValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.model)\n}\n\nfunc (t *RegistrationCodeValid) TemplateType() template.TemplateType {\n\treturn template.TypeRegistrationCodeValid\n}\nfunc (t *RegistrationCodeValid) RequestHeaders() http.Header {\n\treturn t.model.UserRequestHeaders\n\n}\n"
  },
  {
    "path": "courier/template/email/registration_code_valid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestRegistrationCodeValid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewRegistrationCodeValid(reg, &email.RegistrationCodeValidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"test=with remote resources\", func(t *testing.T) {\n\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/registration_code/valid\", template.TypeRegistrationCodeValid)\n\t})\n}\n"
  },
  {
    "path": "courier/template/email/stub.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tTestStub struct {\n\t\tm *TestStubModel\n\t}\n\tTestStubModel struct {\n\t\tTo       string `json:\"to\"`\n\t\tSubject  string `json:\"subject\"`\n\t\tBody     string `json:\"body\"`\n\t\tHTMLBody string `json:\"html_body,omitempty\"`\n\t}\n)\n\nfunc NewTestStub(m *TestStubModel) *TestStub {\n\treturn &TestStub{m: m}\n}\n\nfunc (t *TestStub) EmailRecipient() (string, error)              { return t.m.To, nil }\nfunc (t *TestStub) EmailSubject(context.Context) (string, error) { return t.m.Subject, nil }\n\nfunc (t *TestStub) EmailBody(ctx context.Context) (string, error) {\n\tif t.m.HTMLBody != \"\" {\n\t\treturn t.m.HTMLBody, nil\n\t}\n\treturn t.EmailBodyPlaintext(ctx)\n}\n\nfunc (t *TestStub) EmailBodyPlaintext(context.Context) (string, error) { return t.m.Body, nil }\nfunc (t *TestStub) MarshalJSON() ([]byte, error)                       { return json.Marshal(t.m) }\nfunc (t *TestStub) TemplateType() template.TemplateType                { return template.TypeTestStub }\n"
  },
  {
    "path": "courier/template/email/verification_code_invalid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tVerificationCodeInvalid struct {\n\t\td template.Dependencies\n\t\tm *VerificationCodeInvalidModel\n\t}\n\tVerificationCodeInvalidModel struct {\n\t\tTo               string                 `json:\"to\"`\n\t\tRequestURL       string                 `json:\"request_url\"`\n\t\tTransientPayload map[string]interface{} `json:\"transient_payload\"`\n\t}\n)\n\nfunc NewVerificationCodeInvalid(d template.Dependencies, m *VerificationCodeInvalidModel) *VerificationCodeInvalid {\n\treturn &VerificationCodeInvalid{d: d, m: m}\n}\n\nfunc (t *VerificationCodeInvalid) EmailRecipient() (string, error) {\n\treturn t.m.To, nil\n}\n\nfunc (t *VerificationCodeInvalid) EmailSubject(ctx context.Context) (string, error) {\n\tsubject, err := template.LoadText(\n\t\tctx,\n\t\tt.d,\n\t\tos.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"verification_code/invalid/email.subject.gotmpl\",\n\t\t\"verification_code/invalid/email.subject*\",\n\t\tt.m,\n\t\tt.d.CourierConfig().CourierTemplatesVerificationCodeInvalid(ctx).Subject,\n\t)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *VerificationCodeInvalid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(\n\t\tctx,\n\t\tt.d,\n\t\tos.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"verification_code/invalid/email.body.gotmpl\",\n\t\t\"verification_code/invalid/email.body*\",\n\t\tt.m,\n\t\tt.d.CourierConfig().CourierTemplatesVerificationCodeInvalid(ctx).Body.HTML,\n\t)\n}\n\nfunc (t *VerificationCodeInvalid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(\n\t\tctx,\n\t\tt.d,\n\t\tos.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"verification_code/invalid/email.body.plaintext.gotmpl\",\n\t\t\"verification_code/invalid/email.body.plaintext*\",\n\t\tt.m,\n\t\tt.d.CourierConfig().CourierTemplatesVerificationCodeInvalid(ctx).Body.PlainText,\n\t)\n}\n\nfunc (t *VerificationCodeInvalid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.m)\n}\n\nfunc (t *VerificationCodeInvalid) TemplateType() template.TemplateType {\n\treturn template.TypeVerificationCodeInvalid\n}\n"
  },
  {
    "path": "courier/template/email/verification_code_invalid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestVerifyCodeInvalid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewVerificationCodeInvalid(reg, &email.VerificationCodeInvalidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"test=with remote resources\", func(t *testing.T) {\n\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/verification_code/invalid\", template.TypeVerificationCodeInvalid)\n\t})\n}\n"
  },
  {
    "path": "courier/template/email/verification_code_valid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tVerificationCodeValid struct {\n\t\td template.Dependencies\n\t\tm *VerificationCodeValidModel\n\t}\n\tVerificationCodeValidModel struct {\n\t\tTo               string                 `json:\"to\"`\n\t\tVerificationURL  string                 `json:\"verification_url\"`\n\t\tVerificationCode string                 `json:\"verification_code\"`\n\t\tIdentity         map[string]interface{} `json:\"identity\"`\n\t\tRequestURL       string                 `json:\"request_url\"`\n\t\tTransientPayload map[string]interface{} `json:\"transient_payload\"`\n\t\tExpiresInMinutes int                    `json:\"expires_in_minutes\"`\n\t}\n)\n\nfunc NewVerificationCodeValid(d template.Dependencies, m *VerificationCodeValidModel) *VerificationCodeValid {\n\treturn &VerificationCodeValid{d: d, m: m}\n}\n\nfunc (t *VerificationCodeValid) EmailRecipient() (string, error) {\n\treturn t.m.To, nil\n}\n\nfunc (t *VerificationCodeValid) EmailSubject(ctx context.Context) (string, error) {\n\tsubject, err := template.LoadText(\n\t\tctx,\n\t\tt.d,\n\t\tos.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"verification_code/valid/email.subject.gotmpl\",\n\t\t\"verification_code/valid/email.subject*\",\n\t\tt.m,\n\t\tt.d.CourierConfig().CourierTemplatesVerificationCodeValid(ctx).Subject,\n\t)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *VerificationCodeValid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(ctx,\n\t\tt.d,\n\t\tos.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"verification_code/valid/email.body.gotmpl\",\n\t\t\"verification_code/valid/email.body*\",\n\t\tt.m,\n\t\tt.d.CourierConfig().CourierTemplatesVerificationCodeValid(ctx).Body.HTML,\n\t)\n}\n\nfunc (t *VerificationCodeValid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(ctx,\n\t\tt.d,\n\t\tos.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"verification_code/valid/email.body.plaintext.gotmpl\",\n\t\t\"verification_code/valid/email.body.plaintext*\",\n\t\tt.m,\n\t\tt.d.CourierConfig().CourierTemplatesVerificationCodeValid(ctx).Body.PlainText,\n\t)\n}\n\nfunc (t *VerificationCodeValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.m)\n}\n\nfunc (t *VerificationCodeValid) TemplateType() template.TemplateType {\n\treturn template.TypeVerificationCodeValid\n}\n"
  },
  {
    "path": "courier/template/email/verification_code_valid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestVerifyCodeValid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewVerificationCodeValid(reg, &email.VerificationCodeValidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"test=with remote resources\", func(t *testing.T) {\n\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/verification_code/valid\", template.TypeVerificationCodeValid)\n\t})\n}\n"
  },
  {
    "path": "courier/template/email/verification_invalid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tVerificationInvalid struct {\n\t\td template.Dependencies\n\t\tm *VerificationInvalidModel\n\t}\n\tVerificationInvalidModel struct {\n\t\tTo               string                 `json:\"to\"`\n\t\tRequestURL       string                 `json:\"request_url\"`\n\t\tTransientPayload map[string]interface{} `json:\"transient_payload\"`\n\t}\n)\n\nfunc NewVerificationInvalid(d template.Dependencies, m *VerificationInvalidModel) *VerificationInvalid {\n\treturn &VerificationInvalid{d: d, m: m}\n}\n\nfunc (t *VerificationInvalid) EmailRecipient() (string, error) {\n\treturn t.m.To, nil\n}\n\nfunc (t *VerificationInvalid) EmailSubject(ctx context.Context) (string, error) {\n\tsubject, err := template.LoadText(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"verification/invalid/email.subject.gotmpl\", \"verification/invalid/email.subject*\", t.m, t.d.CourierConfig().CourierTemplatesVerificationInvalid(ctx).Subject)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *VerificationInvalid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"verification/invalid/email.body.gotmpl\", \"verification/invalid/email.body*\", t.m, t.d.CourierConfig().CourierTemplatesVerificationInvalid(ctx).Body.HTML)\n}\n\nfunc (t *VerificationInvalid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"verification/invalid/email.body.plaintext.gotmpl\", \"verification/invalid/email.body.plaintext*\", t.m, t.d.CourierConfig().CourierTemplatesVerificationInvalid(ctx).Body.PlainText)\n}\n\nfunc (t *VerificationInvalid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.m)\n}\n\nfunc (t *VerificationInvalid) TemplateType() template.TemplateType {\n\treturn template.TypeVerificationInvalid\n}\n"
  },
  {
    "path": "courier/template/email/verification_invalid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestVerifyInvalid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewVerificationInvalid(reg, &email.VerificationInvalidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"test=with remote resources\", func(t *testing.T) {\n\t\tt.Run(\"test=with remote resources\", func(t *testing.T) {\n\t\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/verification/invalid\", template.TypeVerificationInvalid)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "courier/template/email/verification_valid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tVerificationValid struct {\n\t\td template.Dependencies\n\t\tm *VerificationValidModel\n\t}\n\tVerificationValidModel struct {\n\t\tTo               string                 `json:\"to\"`\n\t\tVerificationURL  string                 `json:\"verification_url\"`\n\t\tIdentity         map[string]interface{} `json:\"identity\"`\n\t\tRequestURL       string                 `json:\"request_url\"`\n\t\tTransientPayload map[string]interface{} `json:\"transient_payload\"`\n\t\tExpiresInMinutes int                    `json:\"expires_in_minutes\"`\n\t}\n)\n\nfunc NewVerificationValid(d template.Dependencies, m *VerificationValidModel) *VerificationValid {\n\treturn &VerificationValid{d: d, m: m}\n}\n\nfunc (t *VerificationValid) EmailRecipient() (string, error) {\n\treturn t.m.To, nil\n}\n\nfunc (t *VerificationValid) EmailSubject(ctx context.Context) (string, error) {\n\tsubject, err := template.LoadText(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"verification/valid/email.subject.gotmpl\", \"verification/valid/email.subject*\", t.m, t.d.CourierConfig().CourierTemplatesVerificationValid(ctx).Subject)\n\n\treturn strings.TrimSpace(subject), err\n}\n\nfunc (t *VerificationValid) EmailBody(ctx context.Context) (string, error) {\n\treturn template.LoadHTML(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"verification/valid/email.body.gotmpl\", \"verification/valid/email.body*\", t.m, t.d.CourierConfig().CourierTemplatesVerificationValid(ctx).Body.HTML)\n}\n\nfunc (t *VerificationValid) EmailBodyPlaintext(ctx context.Context) (string, error) {\n\treturn template.LoadText(ctx, t.d, os.DirFS(t.d.CourierConfig().CourierTemplatesRoot(ctx)), \"verification/valid/email.body.plaintext.gotmpl\", \"verification/valid/email.body.plaintext*\", t.m, t.d.CourierConfig().CourierTemplatesVerificationValid(ctx).Body.PlainText)\n}\n\nfunc (t *VerificationValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.m)\n}\n\nfunc (t *VerificationValid) TemplateType() template.TemplateType {\n\treturn template.TypeVerificationValid\n}\n"
  },
  {
    "path": "courier/template/email/verification_valid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage email_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestVerifyValid(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"test=with courier templates directory\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttpl := email.NewVerificationValid(reg, &email.VerificationValidModel{})\n\n\t\ttesthelpers.TestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"test=with remote resources\", func(t *testing.T) {\n\t\ttesthelpers.TestRemoteTemplates(t, \"../courier/builtin/templates/verification/valid\", template.TypeVerificationValid)\n\t})\n}\n"
  },
  {
    "path": "courier/template/load_template.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage template\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"embed\"\n\thtemplate \"html/template\"\n\t\"io\"\n\t\"io/fs\"\n\t\"path/filepath\"\n\t\"text/template\"\n\n\t\"github.com/ory/x/fetcher\"\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/Masterminds/sprig/v3\"\n\tlru \"github.com/hashicorp/golang-lru/v2\"\n\t\"github.com/pkg/errors\"\n)\n\n//go:embed courier/builtin/templates/*\nvar templates embed.FS\n\nvar Cache, _ = lru.New[string, Template](16)\n\ntype Template interface {\n\tExecute(wr io.Writer, data interface{}) error\n}\n\ntype templateDependencies interface {\n\thttpx.ClientProvider\n}\n\nfunc loadBuiltInTemplate(filesystem fs.FS, name string, html bool) (Template, error) {\n\tif t, found := Cache.Get(name); found {\n\t\treturn t, nil\n\t}\n\n\tfile, err := filesystem.Open(name)\n\tif err != nil {\n\t\t// try to fallback to bundled templates\n\t\tvar fallbackErr error\n\t\tfile, fallbackErr = templates.Open(filepath.Join(\"courier/builtin/templates\", name))\n\t\tif fallbackErr != nil {\n\t\t\t// return original error from os.DirFS\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t}\n\n\tdefer func() { _ = file.Close() }()\n\n\tvar b bytes.Buffer\n\tif _, err := io.Copy(&b, file); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tvar tpl Template\n\tif html {\n\t\tt, err := htemplate.New(name).Funcs(sprig.HtmlFuncMap()).Parse(b.String())\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\ttpl = t\n\t} else {\n\t\tt, err := template.New(name).Funcs(sprig.TxtFuncMap()).Parse(b.String())\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\ttpl = t\n\t}\n\n\t_ = Cache.Add(name, tpl)\n\treturn tpl, nil\n}\n\nfunc loadRemoteTemplate(ctx context.Context, d templateDependencies, url string, html bool) (t Template, err error) {\n\tif t, found := Cache.Get(url); found {\n\t\treturn t, nil\n\t}\n\n\tf := fetcher.NewFetcher(fetcher.WithClient(d.HTTPClient(ctx)))\n\tbb, err := f.FetchContext(ctx, url)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tb := bb.Bytes()\n\n\tif html {\n\t\tt, err = htemplate.New(url).Funcs(sprig.HermeticHtmlFuncMap()).Parse(string(b))\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t} else {\n\t\tt, err = template.New(url).Funcs(sprig.HermeticTxtFuncMap()).Parse(string(b))\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t}\n\tCache.Add(url, t)\n\n\treturn t, nil\n}\n\nfunc loadTemplate(filesystem fs.FS, name, pattern string, html bool) (Template, error) {\n\tif t, found := Cache.Get(name); found {\n\t\treturn t, nil\n\t}\n\n\tmatches, _ := fs.Glob(filesystem, name)\n\t// make sure the file exists in the fs, otherwise fallback to built in templates\n\tif matches == nil {\n\t\treturn loadBuiltInTemplate(filesystem, name, html)\n\t}\n\n\tglob := name\n\tif pattern != \"\" {\n\t\t// pattern matching is used when we have more than one gotmpl for different use cases, such as i18n support\n\t\t// e.g. some_template/template_name* will match some_template/template_name.body.en_US.gotmpl\n\t\tmatches, _ = fs.Glob(filesystem, pattern)\n\t\t// set the glob string to match patterns\n\t\tif matches != nil {\n\t\t\tglob = pattern\n\t\t}\n\t}\n\n\tvar tpl Template\n\tif html {\n\t\tt, err := htemplate.New(filepath.Base(name)).Funcs(sprig.HermeticHtmlFuncMap()).ParseFS(filesystem, glob)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\ttpl = t\n\t} else {\n\t\tt, err := template.New(filepath.Base(name)).Funcs(sprig.HermeticTxtFuncMap()).ParseFS(filesystem, glob)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\ttpl = t\n\t}\n\n\t_ = Cache.Add(name, tpl)\n\treturn tpl, nil\n}\n\nfunc LoadText(ctx context.Context, d templateDependencies, filesystem fs.FS, name, pattern string, model interface{}, remoteURL string) (string, error) {\n\tvar t Template\n\tvar err error\n\tif remoteURL != \"\" {\n\t\tt, err = loadRemoteTemplate(ctx, d, remoteURL, false)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t} else {\n\t\tt, err = loadTemplate(filesystem, name, pattern, false)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tvar b bytes.Buffer\n\tif err := t.Execute(&b, model); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn b.String(), nil\n}\n\nfunc LoadHTML(ctx context.Context, d templateDependencies, filesystem fs.FS, name, pattern string, model interface{}, remoteURL string) (string, error) {\n\tvar t Template\n\tvar err error\n\tif remoteURL != \"\" {\n\t\tt, err = loadRemoteTemplate(ctx, d, remoteURL, true)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t} else {\n\t\tt, err = loadTemplate(filesystem, name, pattern, true)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tvar b bytes.Buffer\n\tif err := t.Execute(&b, model); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn b.String(), nil\n}\n"
  },
  {
    "path": "courier/template/load_template_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage template_test\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/x/fetcher\"\n\n\tlru \"github.com/hashicorp/golang-lru/v2\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestLoadTextTemplate(t *testing.T) {\n\texecuteTextTemplate := func(t *testing.T, dir, name, pattern string, model map[string]interface{}) string {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttp, err := template.LoadText(t.Context(), reg, os.DirFS(dir), name, pattern, model, \"\")\n\t\trequire.NoError(t, err)\n\t\treturn tp\n\t}\n\n\texecuteHTMLTemplate := func(t *testing.T, dir, name, pattern string, model map[string]interface{}) string {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttp, err := template.LoadHTML(t.Context(), reg, os.DirFS(dir), name, pattern, model, \"\")\n\t\trequire.NoError(t, err)\n\t\treturn tp\n\t}\n\n\tt.Run(\"method=from bundled\", func(t *testing.T) {\n\t\tactual := executeTextTemplate(t, \"courier/builtin/templates/test_stub\", \"email.body.gotmpl\", \"\", nil)\n\t\tassert.Contains(t, actual, \"stub email\")\n\t})\n\n\tt.Run(\"method=fallback to bundled\", func(t *testing.T) {\n\t\ttemplate.Cache, _ = lru.New[string, template.Template](16) // prevent Cache hit\n\t\tactual := executeTextTemplate(t, \"some/inexistent/dir\", \"test_stub/email.body.gotmpl\", \"\", nil)\n\t\tassert.Contains(t, actual, \"stub email\")\n\t})\n\n\tt.Run(\"method=with Sprig functions\", func(t *testing.T) {\n\t\ttemplate.Cache, _ = lru.New[string, template.Template](16) // prevent Cache hit\n\t\tm := map[string]interface{}{\"input\": \"hello world\"}        // create a simple model\n\t\tactual := executeTextTemplate(t, \"courier/builtin/templates/test_stub\", \"email.body.sprig.gotmpl\", \"\", m)\n\t\tassert.Contains(t, actual, \"HelloWorld,HELLOWORLD\")\n\t})\n\n\tt.Run(\"method=sprig should not support non-hermetic\", func(t *testing.T) {\n\t\ttemplate.Cache, _ = lru.New[string, template.Template](16)\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\t\tnonhermetic := []string{\"date\", \"date_in_zone\", \"date_modify\", \"now\", \"htmlDate\", \"htmlDateInZone\", \"dateInZone\", \"dateModify\", \"env\", \"expandenv\", \"getHostByName\", \"uuidv4\", \"randNumeric\", \"randAscii\", \"randAlpha\", \"randAlphaNum\"}\n\n\t\tfor _, tc := range nonhermetic {\n\t\t\tt.Run(\"case=should not support function: \"+tc, func(t *testing.T) {\n\t\t\t\t_, err := template.LoadText(t.Context(), reg, x.NewStubFS(tc, []byte(fmt.Sprintf(\"{{ %s }}\", tc))), tc, \"\", map[string]interface{}{}, \"\")\n\t\t\t\tassert.ErrorContains(t, err, fmt.Sprintf(\"function %q not defined\", tc))\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"method=html with nested templates\", func(t *testing.T) {\n\t\ttemplate.Cache, _ = lru.New[string, template.Template](16) // prevent Cache hit\n\t\tm := map[string]interface{}{\"lang\": \"en_US\"}               // create a simple model\n\t\tactual := executeHTMLTemplate(t, \"courier/builtin/templates/test_stub\", \"email.body.html.gotmpl\", \"email.body.html*\", m)\n\t\tassert.Contains(t, actual, \"lang=en_US\")\n\t})\n\n\tt.Run(\"method=Cache works\", func(t *testing.T) {\n\t\tdir := os.TempDir()\n\t\tname := x.NewUUID().String() + \".body.gotmpl\"\n\t\tfp := filepath.Join(dir, name)\n\n\t\trequire.NoError(t, os.WriteFile(fp, []byte(\"cached stub body\"), 0o600))\n\t\tassert.Contains(t, executeTextTemplate(t, dir, name, \"\", nil), \"cached stub body\")\n\n\t\trequire.NoError(t, os.RemoveAll(fp))\n\t\tassert.Contains(t, executeTextTemplate(t, dir, name, \"\", nil), \"cached stub body\")\n\t})\n\n\tt.Run(\"method=remote resource\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tt.Cleanup(cancel)\n\n\t\tt.Run(\"case=base64 encoded data\", func(t *testing.T) {\n\t\t\tt.Run(\"html template\", func(t *testing.T) {\n\t\t\t\tm := map[string]interface{}{\"lang\": \"en_US\"}\n\t\t\t\tf, err := os.ReadFile(\"courier/builtin/templates/test_stub/email.body.html.en_US.gotmpl\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tb64 := base64.StdEncoding.EncodeToString(f)\n\t\t\t\ttp, err := template.LoadHTML(ctx, reg, nil, \"\", \"\", m, \"base64://\"+b64)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Contains(t, tp, \"lang=en_US\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=plaintext\", func(t *testing.T) {\n\t\t\t\tm := map[string]interface{}{\"Body\": \"something\"}\n\t\t\t\tf, err := os.ReadFile(\"courier/builtin/templates/test_stub/email.body.plaintext.gotmpl\")\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tb64 := base64.StdEncoding.EncodeToString(f)\n\n\t\t\t\ttp, err := template.LoadText(ctx, reg, nil, \"\", \"\", m, \"base64://\"+b64)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Contains(t, tp, \"stub email body something\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=file resource\", func(t *testing.T) {\n\t\t\tt.Run(\"case=html template\", func(t *testing.T) {\n\t\t\t\tm := map[string]interface{}{\"lang\": \"en_US\"}\n\t\t\t\ttp, err := template.LoadHTML(ctx, reg, nil, \"\", \"\", m, \"file://courier/builtin/templates/test_stub/email.body.html.en_US.gotmpl\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Contains(t, tp, \"lang=en_US\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=plaintext\", func(t *testing.T) {\n\t\t\t\tm := map[string]interface{}{\"Body\": \"something\"}\n\t\t\t\ttp, err := template.LoadText(ctx, reg, nil, \"\", \"\", m, \"file://courier/builtin/templates/test_stub/email.body.plaintext.gotmpl\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Contains(t, tp, \"stub email body something\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=http resource\", func(t *testing.T) {\n\t\t\trouter := http.NewServeMux()\n\t\t\trouter.HandleFunc(\"GET /html\", func(writer http.ResponseWriter, request *http.Request) {\n\t\t\t\thttp.ServeFile(writer, request, \"courier/builtin/templates/test_stub/email.body.html.en_US.gotmpl\")\n\t\t\t})\n\t\t\trouter.HandleFunc(\"GET /plaintext\", func(writer http.ResponseWriter, request *http.Request) {\n\t\t\t\thttp.ServeFile(writer, request, \"courier/builtin/templates/test_stub/email.body.plaintext.gotmpl\")\n\t\t\t})\n\t\t\tts := httptest.NewServer(router)\n\t\t\tt.Cleanup(ts.Close)\n\n\t\t\tt.Run(\"case=html template\", func(t *testing.T) {\n\t\t\t\tm := map[string]interface{}{\"lang\": \"en_US\"}\n\t\t\t\ttp, err := template.LoadHTML(ctx, reg, nil, \"\", \"\", m, ts.URL+\"/html\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Contains(t, tp, \"lang=en_US\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=plaintext\", func(t *testing.T) {\n\t\t\t\tm := map[string]interface{}{\"Body\": \"something\"}\n\t\t\t\ttp, err := template.LoadText(ctx, reg, nil, \"\", \"\", m, ts.URL+\"/plaintext\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Contains(t, tp, \"stub email body something\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=unsupported resource\", func(t *testing.T) {\n\t\t\ttp, err := template.LoadHTML(ctx, reg, nil, \"\", \"\", map[string]interface{}{}, \"grpc://unsupported-url\")\n\t\t\trequire.ErrorIs(t, err, fetcher.ErrUnknownScheme)\n\t\t\trequire.Empty(t, tp)\n\n\t\t\ttp, err = template.LoadText(ctx, reg, nil, \"\", \"\", map[string]interface{}{}, \"grpc://unsupported-url\")\n\t\t\trequire.ErrorIs(t, err, fetcher.ErrUnknownScheme)\n\t\t\trequire.Empty(t, tp)\n\t\t})\n\n\t\tt.Run(\"case=disallowed resources\", func(t *testing.T) {\n\t\t\trequire.NoError(t, reg.Config().Set(ctx, config.ViperKeyClientHTTPNoPrivateIPRanges, true))\n\t\t\treg.HTTPClient(ctx).RetryMax = 1\n\t\t\treg.HTTPClient(ctx).RetryWaitMax = time.Millisecond\n\n\t\t\t_, err := template.LoadHTML(ctx, reg, nil, \"\", \"\", map[string]interface{}{}, \"http://localhost:8080/1234\")\n\t\t\tassert.ErrorContains(t, err, \"is not a permitted destination\")\n\n\t\t\t_, err = template.LoadText(ctx, reg, nil, \"\", \"\", map[string]interface{}{}, \"http://localhost:8080/1234\")\n\t\t\tassert.ErrorContains(t, err, \"is not a permitted destination\")\n\t\t})\n\n\t\tt.Run(\"method=cache works\", func(t *testing.T) {\n\t\t\ttp1, err := template.LoadText(ctx, reg, nil, \"\", \"\", map[string]interface{}{}, \"base64://e3sgJGwgOj0gY2F0ICJsYW5nPSIgLmxhbmcgfX0Ke3sgbm9zcGFjZSAkbCB9fQ==\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\ttp2, err := template.LoadText(ctx, reg, nil, \"\", \"\", map[string]interface{}{}, \"base64://c3R1YiBlbWFpbCBib2R5IHt7IC5Cb2R5IH19\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.NotEqualf(t, tp1, tp2, \"Expected remote template 1 and remote template 2 to not be equal\")\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "courier/template/sms/login_code_valid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sms\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tLoginCodeValid struct {\n\t\tdeps  template.Dependencies\n\t\tmodel *LoginCodeValidModel\n\t}\n\tLoginCodeValidModel struct {\n\t\tTo                 string         `json:\"to\"`\n\t\tLoginCode          string         `json:\"login_code\"`\n\t\tIdentity           map[string]any `json:\"identity\"`\n\t\tRequestURL         string         `json:\"request_url\"`\n\t\tTransientPayload   map[string]any `json:\"transient_payload\"`\n\t\tExpiresInMinutes   int            `json:\"expires_in_minutes\"`\n\t\tUserRequestHeaders http.Header    `json:\"-\"`\n\t}\n)\n\nfunc NewLoginCodeValid(d template.Dependencies, m *LoginCodeValidModel) *LoginCodeValid {\n\treturn &LoginCodeValid{deps: d, model: m}\n}\n\nfunc (t *LoginCodeValid) PhoneNumber() (string, error) {\n\treturn t.model.To, nil\n}\n\nfunc (t *LoginCodeValid) SMSBody(ctx context.Context) (string, error) {\n\treturn template.LoadText(\n\t\tctx,\n\t\tt.deps,\n\t\tos.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"login_code/valid/sms.body.gotmpl\",\n\t\t\"login_code/valid/sms.body*\",\n\t\tt.model,\n\t\tt.deps.CourierConfig().CourierSMSTemplatesLoginCodeValid(ctx).Body.PlainText,\n\t)\n}\n\nfunc (t *LoginCodeValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.model)\n}\n\nfunc (t *LoginCodeValid) TemplateType() template.TemplateType {\n\treturn template.TypeLoginCodeValid\n}\nfunc (t *LoginCodeValid) RequestHeaders() http.Header {\n\treturn t.model.UserRequestHeaders\n\n}\n"
  },
  {
    "path": "courier/template/sms/login_code_valid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sms_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier/template/sms\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestNewLoginCodeValid(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tconst (\n\t\texpectedPhone = \"+12345678901\"\n\t\totp           = \"012345\"\n\t)\n\n\ttpl := sms.NewLoginCodeValid(reg, &sms.LoginCodeValidModel{To: expectedPhone, LoginCode: otp})\n\n\texpectedBody := fmt.Sprintf(\"Your login code is: %s\\n\\nIt expires in 0 minutes.\\n\", otp)\n\n\tactualBody, err := tpl.SMSBody(context.Background())\n\trequire.NoError(t, err)\n\tassert.Equal(t, expectedBody, actualBody)\n\n\tactualPhone, err := tpl.PhoneNumber()\n\trequire.NoError(t, err)\n\tassert.Equal(t, expectedPhone, actualPhone)\n}\n"
  },
  {
    "path": "courier/template/sms/recovery_code.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sms\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tRecoveryCodeValid struct {\n\t\tdeps  template.Dependencies\n\t\tmodel *RecoveryCodeValidModel\n\t}\n\n\tRecoveryCodeValidModel struct {\n\t\tTo                 string         `json:\"to\"`\n\t\tRecoveryCode       string         `json:\"verification_code\"`\n\t\tIdentity           map[string]any `json:\"identity\"`\n\t\tRequestURL         string         `json:\"request_url\"`\n\t\tRequestURLDomain   string         `json:\"request_url_domain\"`\n\t\tTransientPayload   map[string]any `json:\"transient_payload\"`\n\t\tExpiresInMinutes   int            `json:\"expires_in_minutes\"`\n\t\tUserRequestHeaders http.Header    `json:\"-\"`\n\t}\n)\n\nfunc NewRecoveryCodeValid(d template.Dependencies, m *RecoveryCodeValidModel) *RecoveryCodeValid {\n\treturn &RecoveryCodeValid{deps: d, model: m}\n}\n\nfunc (t *RecoveryCodeValid) PhoneNumber() (string, error) {\n\treturn t.model.To, nil\n}\n\nfunc (t *RecoveryCodeValid) SMSBody(ctx context.Context) (string, error) {\n\treturn template.LoadText(\n\t\tctx,\n\t\tt.deps,\n\t\tos.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"recovery_code/valid/sms.body.gotmpl\",\n\t\t\"recovery_code/valid/sms.body*\",\n\t\tt.model,\n\t\tt.deps.CourierConfig().CourierSMSTemplatesRecoveryCodeValid(ctx).Body.PlainText,\n\t)\n}\n\nfunc (t *RecoveryCodeValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.model)\n}\nfunc (t *RecoveryCodeValid) TemplateType() template.TemplateType {\n\treturn template.TypeRecoveryCodeValid\n}\nfunc (t *RecoveryCodeValid) RequestHeaders() http.Header {\n\treturn t.model.UserRequestHeaders\n\n}\n"
  },
  {
    "path": "courier/template/sms/registration_code_valid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sms\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tRegistrationCodeValid struct {\n\t\tdeps  template.Dependencies\n\t\tmodel *RegistrationCodeValidModel\n\t}\n\tRegistrationCodeValidModel struct {\n\t\tTo                 string         `json:\"to\"`\n\t\tRegistrationCode   string         `json:\"registration_code\"`\n\t\tIdentity           map[string]any `json:\"identity\"`\n\t\tRequestURL         string         `json:\"request_url\"`\n\t\tTransientPayload   map[string]any `json:\"transient_payload\"`\n\t\tExpiresInMinutes   int            `json:\"expires_in_minutes\"`\n\t\tUserRequestHeaders http.Header    `json:\"-\"`\n\t}\n)\n\nfunc NewRegistrationCodeValid(d template.Dependencies, m *RegistrationCodeValidModel) *RegistrationCodeValid {\n\treturn &RegistrationCodeValid{deps: d, model: m}\n}\n\nfunc (t *RegistrationCodeValid) PhoneNumber() (string, error) {\n\treturn t.model.To, nil\n}\n\nfunc (t *RegistrationCodeValid) SMSBody(ctx context.Context) (string, error) {\n\treturn template.LoadText(\n\t\tctx,\n\t\tt.deps,\n\t\tos.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"registration_code/valid/sms.body.gotmpl\",\n\t\t\"registration_code/valid/sms.body*\",\n\t\tt.model,\n\t\tt.deps.CourierConfig().CourierSMSTemplatesRegistrationCodeValid(ctx).Body.PlainText,\n\t)\n}\n\nfunc (t *RegistrationCodeValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.model)\n}\n\nfunc (t *RegistrationCodeValid) TemplateType() template.TemplateType {\n\treturn template.TypeRegistrationCodeValid\n}\n\nfunc (t *RegistrationCodeValid) RequestHeaders() http.Header {\n\treturn t.model.UserRequestHeaders\n\n}\n"
  },
  {
    "path": "courier/template/sms/registration_code_valid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sms_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier/template/sms\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestNewRegistrationCodeValid(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tconst (\n\t\texpectedPhone = \"+12345678901\"\n\t\totp           = \"012345\"\n\t)\n\n\ttpl := sms.NewRegistrationCodeValid(reg, &sms.RegistrationCodeValidModel{To: expectedPhone, RegistrationCode: otp})\n\n\texpectedBody := fmt.Sprintf(\"Your registration code is: %s\\n\\nIt expires in 0 minutes.\\n\", otp)\n\n\tactualBody, err := tpl.SMSBody(context.Background())\n\trequire.NoError(t, err)\n\tassert.Equal(t, expectedBody, actualBody)\n\n\tactualPhone, err := tpl.PhoneNumber()\n\trequire.NoError(t, err)\n\tassert.Equal(t, expectedPhone, actualPhone)\n}\n"
  },
  {
    "path": "courier/template/sms/stub.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sms\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tTestStub struct{ m *TestStubModel }\n\n\tTestStubModel struct {\n\t\tTo       string                 `json:\"to\"`\n\t\tBody     string                 `json:\"body\"`\n\t\tIdentity map[string]interface{} `json:\"identity\"`\n\t}\n)\n\nfunc NewTestStub(m *TestStubModel) *TestStub                { return &TestStub{m: m} }\nfunc (t *TestStub) PhoneNumber() (string, error)            { return t.m.To, nil }\nfunc (t *TestStub) SMSBody(context.Context) (string, error) { return t.m.Body, nil }\nfunc (t *TestStub) MarshalJSON() ([]byte, error)            { return json.Marshal(t.m) }\nfunc (t *TestStub) TemplateType() template.TemplateType     { return template.TypeTestStub }\n"
  },
  {
    "path": "courier/template/sms/verification_code.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sms\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\n\t\"github.com/ory/kratos/courier/template\"\n)\n\ntype (\n\tVerificationCodeValid struct {\n\t\tdeps  template.Dependencies\n\t\tmodel *VerificationCodeValidModel\n\t}\n\n\tVerificationCodeValidModel struct {\n\t\tTo               string                 `json:\"to\"`\n\t\tVerificationCode string                 `json:\"verification_code\"`\n\t\tIdentity         map[string]interface{} `json:\"identity\"`\n\t\tRequestURL       string                 `json:\"request_url\"`\n\t\tTransientPayload map[string]interface{} `json:\"transient_payload\"`\n\t\tExpiresInMinutes int                    `json:\"expires_in_minutes\"`\n\t}\n)\n\nfunc NewVerificationCodeValid(d template.Dependencies, m *VerificationCodeValidModel) *VerificationCodeValid {\n\treturn &VerificationCodeValid{deps: d, model: m}\n}\n\nfunc (t *VerificationCodeValid) PhoneNumber() (string, error) {\n\treturn t.model.To, nil\n}\n\nfunc (t *VerificationCodeValid) SMSBody(ctx context.Context) (string, error) {\n\treturn template.LoadText(\n\t\tctx,\n\t\tt.deps,\n\t\tos.DirFS(t.deps.CourierConfig().CourierTemplatesRoot(ctx)),\n\t\t\"verification_code/valid/sms.body.gotmpl\",\n\t\t\"verification_code/valid/sms.body*\",\n\t\tt.model,\n\t\tt.deps.CourierConfig().CourierSMSTemplatesVerificationCodeValid(ctx).Body.PlainText,\n\t)\n}\n\nfunc (t *VerificationCodeValid) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.model)\n}\n\nfunc (t *VerificationCodeValid) TemplateType() template.TemplateType {\n\treturn template.TypeVerificationCodeValid\n}\n"
  },
  {
    "path": "courier/template/sms/verification_code_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sms_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier/template/sms\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestNewOTPMessage(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tconst (\n\t\texpectedPhone = \"+12345678901\"\n\t\totp           = \"012345\"\n\t)\n\n\ttpl := sms.NewVerificationCodeValid(reg, &sms.VerificationCodeValidModel{To: expectedPhone, VerificationCode: otp})\n\n\texpectedBody := fmt.Sprintf(\"Your verification code is: %s\\n\\nIf this was not you, do nothing. It expires in 0 minutes.\\n\", otp)\n\n\tactualBody, err := tpl.SMSBody(context.Background())\n\trequire.NoError(t, err)\n\tassert.Equal(t, expectedBody, actualBody)\n\n\tactualPhone, err := tpl.PhoneNumber()\n\trequire.NoError(t, err)\n\tassert.Equal(t, expectedPhone, actualPhone)\n}\n"
  },
  {
    "path": "courier/template/template.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage template\n\nimport (\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/httpx\"\n)\n\ntype Dependencies interface {\n\tCourierConfig() config.CourierConfigs\n\thttpx.ClientProvider\n}\n"
  },
  {
    "path": "courier/template/testhelpers/testhelpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/courier/template/email\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier/template\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc SetupRemoteConfig(t *testing.T, ctx context.Context, plaintext string, html string, subject string) *driver.RegistryDefault {\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\trequire.NoError(t, reg.Config().Set(ctx, config.ViperKeyCourierTemplatesRecoveryInvalidEmail, &config.CourierEmailTemplate{\n\t\tBody: &config.CourierEmailBodyTemplate{\n\t\t\tPlainText: plaintext,\n\t\t\tHTML:      html,\n\t\t},\n\t\tSubject: subject,\n\t}))\n\treturn reg\n}\n\nfunc TestRendered(t *testing.T, ctx context.Context, tpl interface {\n\tEmailBody(context.Context) (string, error)\n\tEmailSubject(context.Context) (string, error)\n},\n) {\n\trendered, err := tpl.EmailBody(ctx)\n\trequire.NoError(t, err)\n\tassert.NotEmpty(t, rendered)\n\n\trendered, err = tpl.EmailSubject(ctx)\n\trequire.NoError(t, err)\n\tassert.NotEmpty(t, rendered)\n}\n\nfunc TestRemoteTemplates(t *testing.T, basePath string, tmplType template.TemplateType) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\ttoBase64 := func(filePath string) string {\n\t\tf, err := os.ReadFile(filePath) // #nosec G304 -- test code\n\t\trequire.NoError(t, err)\n\t\treturn base64.StdEncoding.EncodeToString(f)\n\t}\n\n\tgetTemplate := func(tmpl template.TemplateType, d template.Dependencies) interface {\n\t\tEmailBody(context.Context) (string, error)\n\t\tEmailSubject(context.Context) (string, error)\n\t} {\n\t\tswitch tmpl {\n\t\tcase template.TypeRecoveryInvalid:\n\t\t\treturn email.NewRecoveryInvalid(d, &email.RecoveryInvalidModel{})\n\t\tcase template.TypeRecoveryValid:\n\t\t\treturn email.NewRecoveryValid(d, &email.RecoveryValidModel{})\n\t\tcase template.TypeRecoveryCodeValid:\n\t\t\treturn email.NewRecoveryCodeValid(d, &email.RecoveryCodeValidModel{})\n\t\tcase template.TypeRecoveryCodeInvalid:\n\t\t\treturn email.NewRecoveryCodeInvalid(d, &email.RecoveryCodeInvalidModel{})\n\t\tcase template.TypeTestStub:\n\t\t\treturn email.NewTestStub(&email.TestStubModel{})\n\t\tcase template.TypeVerificationInvalid:\n\t\t\treturn email.NewVerificationInvalid(d, &email.VerificationInvalidModel{})\n\t\tcase template.TypeVerificationValid:\n\t\t\treturn email.NewVerificationValid(d, &email.VerificationValidModel{})\n\t\tcase template.TypeVerificationCodeInvalid:\n\t\t\treturn email.NewVerificationCodeInvalid(d, &email.VerificationCodeInvalidModel{})\n\t\tcase template.TypeVerificationCodeValid:\n\t\t\treturn email.NewVerificationCodeValid(d, &email.VerificationCodeValidModel{})\n\t\tcase template.TypeLoginCodeValid:\n\t\t\treturn email.NewLoginCodeValid(d, &email.LoginCodeValidModel{})\n\t\tcase template.TypeRegistrationCodeValid:\n\t\t\treturn email.NewRegistrationCodeValid(d, &email.RegistrationCodeValidModel{})\n\t\tdefault:\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tt.Run(\"case=http resource\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\trouter := http.NewServeMux()\n\t\trouter.HandleFunc(\"GET /{filename}\", func(writer http.ResponseWriter, request *http.Request) {\n\t\t\thttp.ServeFile(writer, request, path.Join(basePath, request.PathValue(\"filename\")))\n\t\t})\n\t\tts := httptest.NewServer(router)\n\t\tdefer ts.Close()\n\n\t\ttpl := getTemplate(tmplType, SetupRemoteConfig(t, ctx,\n\t\t\tts.URL+\"/email.body.plaintext.gotmpl\",\n\t\t\tts.URL+\"/email.body.gotmpl\",\n\t\t\tts.URL+\"/email.subject.gotmpl\"))\n\n\t\trequire.NotNil(t, tpl, \"Expected to find template for %s in %s\", tmplType, basePath)\n\n\t\tTestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"case=base64 resource\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttpl := getTemplate(tmplType, SetupRemoteConfig(t, ctx,\n\t\t\t\"base64://\"+toBase64(path.Join(basePath, \"email.body.plaintext.gotmpl\")),\n\t\t\t\"base64://\"+toBase64(path.Join(basePath, \"email.body.gotmpl\")),\n\t\t\t\"base64://\"+toBase64(path.Join(basePath, \"email.subject.gotmpl\"))))\n\n\t\trequire.NotNil(t, tpl, \"Expected to find template for %s in %s\", tmplType, basePath)\n\n\t\tTestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"case=file resource\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttpl := getTemplate(tmplType, SetupRemoteConfig(t, ctx,\n\t\t\t\"file://\"+path.Join(basePath, \"email.body.plaintext.gotmpl\"),\n\t\t\t\"file://\"+path.Join(basePath, \"email.body.gotmpl\"),\n\t\t\t\"file://\"+path.Join(basePath, \"email.subject.gotmpl\")))\n\n\t\trequire.NotNil(t, tpl, \"Expected to find template for %s in %s\", tmplType, basePath)\n\t\tTestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"case=partial subject override\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttpl := getTemplate(tmplType, SetupRemoteConfig(t, ctx,\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"base64://\"+toBase64(path.Join(basePath, \"email.subject.gotmpl\"))))\n\n\t\trequire.NotNil(t, tpl, \"Expected to find template for %s in %s\", tmplType, basePath)\n\t\tTestRendered(t, ctx, tpl)\n\t})\n\n\tt.Run(\"case=partial body override\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttpl := getTemplate(tmplType, SetupRemoteConfig(t, ctx,\n\t\t\t\"base64://\"+toBase64(path.Join(basePath, \"email.body.plaintext.gotmpl\")),\n\t\t\t\"base64://\"+toBase64(path.Join(basePath, \"email.body.gotmpl\")),\n\t\t\t\"\"))\n\n\t\trequire.NotNil(t, tpl, \"Expected to find template for %s in %s\", tmplType, basePath)\n\t\tTestRendered(t, ctx, tpl)\n\t})\n}\n"
  },
  {
    "path": "courier/template/type.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage template\n\n// A Template's type\n//\n// swagger:enum TemplateType\ntype TemplateType string\n\nconst (\n\tTypeRecoveryInvalid         TemplateType = \"recovery_invalid\"\n\tTypeRecoveryValid           TemplateType = \"recovery_valid\"\n\tTypeRecoveryCodeInvalid     TemplateType = \"recovery_code_invalid\"\n\tTypeRecoveryCodeValid       TemplateType = \"recovery_code_valid\"\n\tTypeVerificationInvalid     TemplateType = \"verification_invalid\"\n\tTypeVerificationValid       TemplateType = \"verification_valid\"\n\tTypeVerificationCodeInvalid TemplateType = \"verification_code_invalid\"\n\tTypeVerificationCodeValid   TemplateType = \"verification_code_valid\"\n\tTypeTestStub                TemplateType = \"stub\"\n\tTypeLoginCodeValid          TemplateType = \"login_code_valid\"\n\tTypeRegistrationCodeValid   TemplateType = \"registration_code_valid\"\n)\n"
  },
  {
    "path": "courier/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"slices\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/pop/v6\"\n\tkeysetpagination \"github.com/ory/x/pagination/keysetpagination_v2\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\ntype PersisterWrapper interface {\n\tGetConnection(ctx context.Context) *pop.Connection\n\tNetworkID(ctx context.Context) uuid.UUID\n\tcourier.Persister\n}\n\ntype NetworkWrapper func(t *testing.T, ctx context.Context) (uuid.UUID, PersisterWrapper)\n\nfunc TestPersister(ctx context.Context, newNetworkUnlessExisting NetworkWrapper, newNetwork NetworkWrapper) func(t *testing.T) {\n\treturn func(t *testing.T) {\n\t\tnid, p := newNetworkUnlessExisting(t, ctx)\n\n\t\tt.Run(\"case=no messages in queue\", func(t *testing.T) {\n\t\t\tm, err := p.NextMessages(ctx, 10)\n\t\t\trequire.ErrorIs(t, err, courier.ErrQueueEmpty)\n\t\t\tassert.Len(t, m, 0)\n\n\t\t\t_, err = p.LatestQueuedMessage(ctx)\n\t\t\trequire.ErrorIs(t, err, courier.ErrQueueEmpty)\n\t\t})\n\n\t\tmessages := make([]courier.Message, 5)\n\t\tt.Run(\"case=add messages to the queue\", func(t *testing.T) {\n\t\t\tnow := time.Now()\n\t\t\tfor k := range messages {\n\t\t\t\trequire.NoError(t, faker.FakeData(&messages[k]))\n\t\t\t\trequire.NoError(t, p.AddMessage(ctx, &messages[k]))\n\t\t\t\trequire.NoError(t, p.GetConnection(ctx).\n\t\t\t\t\tRawQuery(\n\t\t\t\t\t\t\"UPDATE courier_messages SET created_at = ?, updated_at = ? WHERE id = ? AND nid = ?\",\n\t\t\t\t\t\tnow.Add(time.Duration(k)*time.Hour).Round(time.Second),\n\t\t\t\t\t\tnow.Add(time.Duration(k)*time.Hour).Round(time.Second),\n\t\t\t\t\t\tmessages[k].ID, nid).\n\t\t\t\t\tExec())\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=latest message in queue\", func(t *testing.T) {\n\t\t\texpected, err := p.LatestQueuedMessage(ctx)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactual := messages[len(messages)-1]\n\t\t\tassert.Equal(t, expected.ID, actual.ID)\n\t\t\tassert.Equal(t, expected.Subject, actual.Subject)\n\t\t})\n\n\t\tt.Run(\"case=pull messages from the queue\", func(t *testing.T) {\n\t\t\tfor k, expected := range messages {\n\t\t\t\texpected.Status = courier.MessageStatusProcessing\n\t\t\t\tt.Run(fmt.Sprintf(\"message=%d\", k), func(t *testing.T) {\n\t\t\t\t\tmessages, err := p.NextMessages(ctx, 1)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, messages, 1)\n\n\t\t\t\t\tactual := messages[0]\n\t\t\t\t\tassert.Equal(t, expected.ID, actual.ID)\n\t\t\t\t\tassert.Equal(t, expected.Subject, actual.Subject)\n\t\t\t\t\tassert.Equal(t, expected.Body, actual.Body)\n\t\t\t\t\tassert.Equal(t, expected.Status, actual.Status)\n\t\t\t\t\tassert.Equal(t, expected.Type, actual.Type)\n\t\t\t\t\tassert.Equal(t, expected.Recipient, actual.Recipient)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t_, err := p.NextMessages(ctx, 10)\n\t\t\trequire.ErrorIs(t, err, courier.ErrQueueEmpty)\n\t\t})\n\n\t\tt.Run(\"case=setting message status\", func(t *testing.T) {\n\t\t\trequire.NoError(t, p.SetMessageStatus(ctx, messages[0].ID, courier.MessageStatusQueued))\n\t\t\tms, err := p.NextMessages(ctx, 1)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, ms, 1)\n\t\t\tassert.Equal(t, messages[0].ID, ms[0].ID)\n\n\t\t\trequire.NoError(t, p.SetMessageStatus(ctx, messages[0].ID, courier.MessageStatusSent))\n\t\t\t_, err = p.NextMessages(ctx, 1)\n\t\t\trequire.ErrorIs(t, err, courier.ErrQueueEmpty)\n\n\t\t\trequire.NoError(t, p.SetMessageStatus(ctx, messages[0].ID, courier.MessageStatusAbandoned))\n\t\t\t_, err = p.NextMessages(ctx, 1)\n\t\t\trequire.ErrorIs(t, err, courier.ErrQueueEmpty)\n\t\t})\n\n\t\tt.Run(\"case=incrementing send count\", func(t *testing.T) {\n\t\t\toriginalSendCount := messages[0].SendCount\n\t\t\trequire.NoError(t, p.SetMessageStatus(ctx, messages[0].ID, courier.MessageStatusQueued))\n\n\t\t\trequire.NoError(t, p.IncrementMessageSendCount(ctx, messages[0].ID))\n\t\t\tms, err := p.NextMessages(ctx, 1)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, ms, 1)\n\t\t\tassert.Equal(t, messages[0].ID, ms[0].ID)\n\t\t\tassert.Equal(t, originalSendCount+1, ms[0].SendCount)\n\t\t})\n\n\t\tt.Run(\"case=list messages\", func(t *testing.T) {\n\t\t\t// List by status.\n\t\t\t{\n\t\t\t\tstatus := courier.MessageStatusProcessing\n\t\t\t\tfilter := courier.ListCourierMessagesParameters{\n\t\t\t\t\tStatus: &status,\n\t\t\t\t}\n\t\t\t\tms, _, err := p.ListMessages(ctx, filter, []keysetpagination.Option{})\n\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, ms, len(messages), messages)\n\t\t\t\t// Check that the 'filter by status' works.\n\t\t\t\tfor _, m := range ms {\n\t\t\t\t\trequire.Equal(t, status, m.Status)\n\t\t\t\t}\n\n\t\t\t\t// Check that the 'order by created_at desc' works.\n\t\t\t\trequire.True(t, slices.IsSortedFunc(ms, func(a, b courier.Message) int { return b.CreatedAt.Compare(a.CreatedAt) }))\n\t\t\t}\n\t\t\t// Query fewer items than the total, multiple times.\n\t\t\t{\n\t\t\t\tfilter := courier.ListCourierMessagesParameters{}\n\t\t\t\tmaxSize := 2\n\t\t\t\tms1, pagination, err := p.ListMessages(ctx, filter, []keysetpagination.Option{\n\t\t\t\t\tkeysetpagination.WithSize(2),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, pagination)\n\t\t\t\trequire.False(t, pagination.IsLast())\n\t\t\t\trequire.Len(t, ms1, maxSize)\n\n\t\t\t\t// Check that the 'order by created_at desc' works.\n\t\t\t\trequire.True(t, slices.IsSortedFunc(ms1, func(a, b courier.Message) int { return b.CreatedAt.Compare(a.CreatedAt) }))\n\n\t\t\t\t// Second call.\n\t\t\t\t// Marshal -> unmarshal the pagination token to be more realistic.\n\t\t\t\tencrypted := pagination.PageToken().Encrypt(nil)\n\t\t\t\tunmarshalled, err := keysetpagination.ParsePageToken(nil, encrypted)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tms2, pagination, err := p.ListMessages(ctx, filter,\n\t\t\t\t\t[]keysetpagination.Option{\n\t\t\t\t\t\tkeysetpagination.WithSize(2),\n\t\t\t\t\t\tkeysetpagination.WithToken(unmarshalled),\n\t\t\t\t\t})\n\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, pagination)\n\t\t\t\trequire.False(t, pagination.IsLast())\n\t\t\t\trequire.Len(t, ms2, maxSize)\n\t\t\t\t// Check that the 'order by created_at desc' works.\n\t\t\t\trequire.True(t, slices.IsSortedFunc(ms2, func(a, b courier.Message) int { return b.CreatedAt.Compare(a.CreatedAt) }))\n\n\t\t\t\t// Check that the second call returned different elements.\n\t\t\t\trequire.NotEqual(t, ms1[0].ID, ms2[0].ID)\n\t\t\t\trequire.NotEqual(t, ms1[1].ID, ms2[1].ID)\n\t\t\t\tallElements := append(ms1, ms2...)\n\t\t\t\trequire.True(t, slices.IsSortedFunc(allElements, func(a, b courier.Message) int { return b.CreatedAt.Compare(a.CreatedAt) }))\n\n\t\t\t\t// Last call\n\t\t\t\tms3, pagination, err := p.ListMessages(ctx, filter, pagination.ToOptions())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, pagination)\n\t\t\t\trequire.True(t, pagination.IsLast())\n\t\t\t\trequire.Len(t, ms3, 1)\n\t\t\t\t// Check that the 'order by created_at desc' works.\n\t\t\t\trequire.True(t, slices.IsSortedFunc(ms3, func(a, b courier.Message) int { return b.CreatedAt.Compare(a.CreatedAt) }))\n\n\t\t\t\t// Check that the third call returned different elements.\n\t\t\t\trequire.NotEqual(t, ms2[0].ID, ms3[0].ID)\n\t\t\t\tallElements = append(ms1, ms2...)\n\t\t\t\tallElements = append(allElements, ms3...)\n\t\t\t\trequire.True(t, slices.IsSortedFunc(allElements, func(a, b courier.Message) int { return b.CreatedAt.Compare(a.CreatedAt) }))\n\t\t\t}\n\n\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\tnid1, p1 := newNetwork(t, ctx)\n\t\t\t\tstatus := courier.MessageStatusProcessing\n\t\t\t\tfilter := courier.ListCourierMessagesParameters{\n\t\t\t\t\tStatus: &status,\n\t\t\t\t}\n\t\t\t\tms, _, err := p1.ListMessages(ctx, filter, []keysetpagination.Option{})\n\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, ms, 0)\n\n\t\t\t\t// Due to a bug in the pagination query definition, it was possible to retrieve messages from another `network`\n\t\t\t\t// using the pagination query. That required that 2 message's `created_at` timestamps were equal, to trigger\n\t\t\t\t// the `OR` clause of the paginated query.\n\t\t\t\t// This part of the tests \"simulates\" this behavior, by forcing the same timestamps on multiple messages across\n\t\t\t\t// different networks.\n\t\t\t\tnid2, p2 := newNetwork(t, ctx)\n\t\t\t\tmsg1 := courier.Message{\n\t\t\t\t\tID:     uuid.FromStringOrNil(\"10000000-0000-0000-0000-000000000000\"),\n\t\t\t\t\tNID:    nid1,\n\t\t\t\t\tStatus: courier.MessageStatusProcessing,\n\t\t\t\t}\n\t\t\t\terr = p1.GetConnection(ctx).Create(&msg1)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tmsg2 := courier.Message{\n\t\t\t\t\tID:     uuid.FromStringOrNil(\"20000000-0000-0000-0000-000000000000\"),\n\t\t\t\t\tNID:    nid1,\n\t\t\t\t\tStatus: courier.MessageStatusProcessing,\n\t\t\t\t}\n\t\t\t\terr = p1.GetConnection(ctx).Create(&msg2)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tmsg3 := courier.Message{\n\t\t\t\t\tID:     uuid.FromStringOrNil(\"30000000-0000-0000-0000-000000000000\"),\n\t\t\t\t\tNID:    nid2,\n\t\t\t\t\tStatus: courier.MessageStatusProcessing,\n\t\t\t\t}\n\t\t\t\terr = p2.GetConnection(ctx).Create(&msg3)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tnow := time.Now().UTC().Truncate(time.Second)\n\n\t\t\t\t// Set all `created_at` timestamps to the same value to force the `OR` clause of the paginated query.\n\t\t\t\t// `created_at` is set by \"pop\" and does not allow a manual override, apart from using `pop.SetNowFunc`, but that also influences the other tests in this\n\t\t\t\t// suite, as it just overrides a global function.\n\t\t\t\trequire.NoError(t, p1.GetConnection(ctx).RawQuery(\"UPDATE courier_messages SET created_at = ? WHERE id = ? AND nid = ?\", now, msg1.ID, nid1).Exec())\n\t\t\t\t// get the \"updated\" message from the\n\t\t\t\trequire.NoError(t, p1.GetConnection(ctx).Where(\"id = ? AND nid = ?\", msg1.ID, msg1.NID).First(&msg1))\n\t\t\t\trequire.NoError(t, p1.GetConnection(ctx).RawQuery(\"UPDATE courier_messages SET created_at = ? WHERE id = ? AND nid = ?\", now, msg2.ID, nid1).Exec())\n\t\t\t\trequire.NoError(t, p2.GetConnection(ctx).RawQuery(\"UPDATE courier_messages SET created_at = ? WHERE id = ? AND nid = ?\", now, msg3.ID, nid2).Exec())\n\n\t\t\t\t// Use the updated first message's PageToken as the basis for the paginated request.\n\t\t\t\tms, _, err = p1.ListMessages(ctx, filter, []keysetpagination.Option{keysetpagination.WithToken(msg1.PageToken())})\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// The response should just contain messages from network1, and not from network2.\n\t\t\t\trequire.Len(t, ms, 1)\n\t\t\t\trequire.Equal(t, ms[0].NID, nid1)\n\t\t\t\trequire.Equal(t, ms[0].ID, msg2.ID)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=network\", func(t *testing.T) {\n\t\t\tt.Run(\"generates id on creation\", func(t *testing.T) {\n\t\t\t\texpected := courier.Message{ID: uuid.Nil}\n\t\t\t\trequire.NoError(t, p.AddMessage(ctx, &expected))\n\n\t\t\t\tassert.NotEqual(t, uuid.Nil, expected.ID)\n\t\t\t\tassert.EqualValues(t, nid, expected.NID)\n\t\t\t\tassert.EqualValues(t, nid, p.NetworkID(ctx))\n\n\t\t\t\tactual, err := p.LatestQueuedMessage(ctx)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, expected.ID, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\n\t\t\t\tactuals, err := p.NextMessages(ctx, 255)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tactual = &actuals[0]\n\t\t\t\tassert.EqualValues(t, expected.ID, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\t\t\t})\n\n\t\t\tid := x.NewUUID()\n\n\t\t\tt.Run(\"persists id on creation\", func(t *testing.T) {\n\t\t\t\texpected := courier.Message{ID: id}\n\t\t\t\trequire.NoError(t, p.AddMessage(ctx, &expected))\n\n\t\t\t\tassert.EqualValues(t, id, expected.ID)\n\t\t\t\tassert.EqualValues(t, nid, expected.NID)\n\t\t\t\tassert.EqualValues(t, nid, p.NetworkID(ctx))\n\n\t\t\t\tactual, err := p.LatestQueuedMessage(ctx)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, id, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\n\t\t\t\tactuals, err := p.NextMessages(ctx, 255)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tactual = &actuals[0]\n\t\t\t\tassert.EqualValues(t, id, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\t\t\t})\n\n\t\t\tt.Run(\"can not get on another network\", func(t *testing.T) {\n\t\t\t\t_, p := newNetwork(t, ctx)\n\n\t\t\t\t_, err := p.LatestQueuedMessage(ctx)\n\t\t\t\trequire.ErrorIs(t, err, courier.ErrQueueEmpty)\n\n\t\t\t\t_, err = p.NextMessages(ctx, 255)\n\t\t\t\trequire.ErrorIs(t, err, courier.ErrQueueEmpty)\n\t\t\t})\n\n\t\t\tt.Run(\"can not update on another network\", func(t *testing.T) {\n\t\t\t\t_, p := newNetwork(t, ctx)\n\t\t\t\terr := p.SetMessageStatus(ctx, id, courier.MessageStatusProcessing)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=FetchMessage\", func(t *testing.T) {\n\t\t\tmsgID := messages[0].ID\n\n\t\t\tmessage, err := p.FetchMessage(ctx, msgID)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, msgID, message.ID)\n\n\t\t\tt.Run(\"can not get on another network\", func(t *testing.T) {\n\t\t\t\t_, p := newNetwork(t, ctx)\n\n\t\t\t\t_, err := p.FetchMessage(ctx, msgID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=RecordDispatch\", func(t *testing.T) {\n\t\t\tmsgID := messages[0].ID\n\n\t\t\terr := p.RecordDispatch(ctx, msgID, courier.CourierMessageDispatchStatusFailed, errors.New(\"testerror\"))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tmessage, err := p.FetchMessage(ctx, msgID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.Len(t, message.Dispatches, 1)\n\t\t\tassert.Equal(t, \"testerror\", gjson.GetBytes(message.Dispatches[0].Error, \"message\").String())\n\n\t\t\tt.Run(\"can not get on another network\", func(t *testing.T) {\n\t\t\t\t_, p := newNetwork(t, ctx)\n\n\t\t\t\t_, err := p.FetchMessage(ctx, msgID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "docs/README.md",
    "content": "# Documentation\n\nPlease find the documentation at\n[www.ory.com/docs/kratos](https://www.ory.com/docs/kratos).\n\nTo contribute to the documentation, please head over to:\n[github.com/ory/docs/tree/master/docs/kratos](https://github.com/ory/docs/tree/master/docs/kratos)\n"
  },
  {
    "path": "docs/sidebar.json",
    "content": "[]"
  },
  {
    "path": "driver/config/.snapshots/TestCourierEmailHTTP-case=configs_set.json",
    "content": "{\n  \"auth\": {\n    \"config\": {\n      \"password\": \"YourPass\",\n      \"user\": \"YourUsername\"\n    },\n    \"type\": \"basic_auth\"\n  },\n  \"body\": \"file://some.jsonnet\",\n  \"header\": {\n    \"Content-Type\": \"application/json\"\n  },\n  \"method\": \"POST\",\n  \"url\": \"https://example.com/email\"\n}\n"
  },
  {
    "path": "driver/config/.snapshots/TestCourierSMS-case=configs_set.json",
    "content": "{\n  \"auth\": {\n    \"config\": {\n      \"password\": \"YourPass\",\n      \"user\": \"YourUsername\"\n    },\n    \"type\": \"basic_auth\"\n  },\n  \"body\": \"base64://e30=\",\n  \"header\": {\n    \"Content-Type\": \"application/x-www-form-urlencoded\"\n  },\n  \"method\": \"POST\",\n  \"url\": \"https://api.twilio.com/2010-04-01/Accounts/YourAccountID/Messages.json\"\n}\n"
  },
  {
    "path": "driver/config/.snapshots/TestCourierSMS-case=defaults.json",
    "content": "null\n"
  },
  {
    "path": "driver/config/.snapshots/TestDefaultWebhookHeaderAllowlist.json",
    "content": "[\n  \"Accept\",\n  \"Accept-Encoding\",\n  \"Accept-Language\",\n  \"Content-Length\",\n  \"Content-Type\",\n  \"Origin\",\n  \"Priority\",\n  \"Referer\",\n  \"Sec-Ch-Ua\",\n  \"Sec-Ch-Ua-Mobile\",\n  \"Sec-Ch-Ua-Platform\",\n  \"Sec-Fetch-Dest\",\n  \"Sec-Fetch-Mode\",\n  \"Sec-Fetch-Site\",\n  \"Sec-Fetch-User\",\n  \"True-Client-Ip\",\n  \"User-Agent\"\n]\n"
  },
  {
    "path": "driver/config/buildinfo.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage config\n\nvar (\n\tVersion = \"master\"\n\tDate    = \"undefined\"\n\tCommit  = \"undefined\"\n)\n"
  },
  {
    "path": "driver/config/config.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage config\n\nimport (\n\t\"bytes\"\n\t\"cmp\"\n\t\"context\"\n\t\"crypto/sha512\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\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/inhies/go-bytesize\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/rs/cors\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/net/publicsuffix\"\n\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/jsonschema/v3/httploader\"\n\t\"github.com/ory/kratos/embedx\"\n\t\"github.com/ory/kratos/request\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/crdbx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/jsonschemax\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/watcherx\"\n)\n\nconst (\n\tDefaultIdentityTraitsSchemaID                            = \"default\"\n\tDefaultBrowserReturnURL                                  = \"default_browser_return_url\"\n\tDefaultSQLiteMemoryDSN                                   = \"sqlite://file::memory:?_fk=true&cache=shared\"\n\tDefaultPasswordHashingAlgorithm                          = \"argon2\"\n\tDefaultCipherAlgorithm                                   = \"noop\"\n\tUnknownVersion                                           = \"unknown version\"\n\tViperKeyDSN                                              = \"dsn\"\n\tViperKeyCourierSMTPURL                                   = \"courier.smtp.connection_uri\"\n\tViperKeyCourierSMTPClientCertPath                        = \"courier.smtp.client_cert_path\"\n\tViperKeyCourierSMTPClientKeyPath                         = \"courier.smtp.client_key_path\"\n\tViperKeyCourierTemplatesPath                             = \"courier.template_override_path\"\n\tViperKeyCourierTemplatesRecoveryInvalidEmail             = \"courier.templates.recovery.invalid.email\"\n\tViperKeyCourierTemplatesRecoveryValidEmail               = \"courier.templates.recovery.valid.email\"\n\tViperKeyCourierTemplatesRecoveryCodeInvalidEmail         = \"courier.templates.recovery_code.invalid.email\"\n\tViperKeyCourierTemplatesRecoveryCodeValidEmail           = \"courier.templates.recovery_code.valid.email\"\n\tViperKeyCourierTemplatesVerificationInvalidEmail         = \"courier.templates.verification.invalid.email\"\n\tViperKeyCourierTemplatesVerificationValidEmail           = \"courier.templates.verification.valid.email\"\n\tViperKeyCourierTemplatesVerificationCodeInvalidEmail     = \"courier.templates.verification_code.invalid.email\"\n\tViperKeyCourierTemplatesVerificationCodeValidEmail       = \"courier.templates.verification_code.valid.email\"\n\tViperKeyCourierTemplatesVerificationCodeValidSMS         = \"courier.templates.verification_code.valid.sms\"\n\tViperKeyCourierTemplatesRecoveryCodeValidSMS             = \"courier.templates.recovery_code.valid.sms\"\n\tViperKeyCourierTemplatesLoginCodeValidSMS                = \"courier.templates.login_code.valid.sms\"\n\tViperKeyCourierTemplatesRegistrationCodeValidSMS         = \"courier.templates.registration_code.valid.sms\"\n\tViperKeyCourierDeliveryStrategy                          = \"courier.delivery_strategy\"\n\tViperKeyCourierHTTPRequestConfig                         = \"courier.http.request_config\"\n\tViperKeyCourierTemplatesLoginCodeValidEmail              = \"courier.templates.login_code.valid.email\"\n\tViperKeyCourierTemplatesRegistrationCodeValidEmail       = \"courier.templates.registration_code.valid.email\"\n\tViperKeyCourierSMTP                                      = \"courier.smtp\"\n\tViperKeyCourierSMTPFrom                                  = \"courier.smtp.from_address\"\n\tViperKeyCourierSMTPFromName                              = \"courier.smtp.from_name\"\n\tViperKeyCourierSMTPHeaders                               = \"courier.smtp.headers\"\n\tViperKeyCourierSMTPLocalName                             = \"courier.smtp.local_name\"\n\tViperKeyCourierMessageRetries                            = \"courier.message_retries\"\n\tViperKeyCourierWorkerPullCount                           = \"courier.worker.pull_count\"\n\tViperKeyCourierWorkerPullWait                            = \"courier.worker.pull_wait\"\n\tViperKeyCourierChannels                                  = \"courier.channels\"\n\tViperKeySecretsDefault                                   = \"secrets.default\"\n\tViperKeySecretsCookie                                    = \"secrets.cookie\"\n\tViperKeySecretsCipher                                    = \"secrets.cipher\"\n\tViperKeySecretsPagination                                = \"secrets.pagination\"\n\tViperKeyPublicBaseURL                                    = \"serve.public.base_url\"\n\tViperKeyAdminBaseURL                                     = \"serve.admin.base_url\"\n\tViperKeySessionLifespan                                  = \"session.lifespan\"\n\tViperKeySessionSameSite                                  = \"session.cookie.same_site\"\n\tViperKeySessionSecure                                    = \"session.cookie.secure\"\n\tViperKeySessionDomain                                    = \"session.cookie.domain\"\n\tViperKeySessionName                                      = \"session.cookie.name\"\n\tViperKeySessionPath                                      = \"session.cookie.path\"\n\tViperKeySessionPersistentCookie                          = \"session.cookie.persistent\"\n\tViperKeySessionTokenizerTemplates                        = \"session.whoami.tokenizer.templates\"\n\tViperKeySessionWhoAmIAAL                                 = \"session.whoami.required_aal\"\n\tViperKeySessionWhoAmICaching                             = \"feature_flags.cacheable_sessions\"\n\tViperKeyFeatureFlagFasterSessionExtend                   = \"feature_flags.faster_session_extend\"\n\tViperKeySessionWhoAmICachingMaxAge                       = \"feature_flags.cacheable_sessions_max_age\"\n\tViperKeyUseContinueWithTransitions                       = \"feature_flags.use_continue_with_transitions\"\n\tViperKeyChooseRecoveryAddress                            = \"feature_flags.choose_recovery_address\"\n\tViperKeyUseLegacyShowVerificationUI                      = \"feature_flags.legacy_continue_with_verification_ui\"\n\tViperKeyLegacyOIDCRegistrationGroup                      = \"feature_flags.legacy_oidc_registration_node_group\"\n\tViperKeyUseLegacyRequireVerifiedLoginError               = \"feature_flags.legacy_require_verified_login_error\"\n\tViperKeySessionRefreshMinTimeLeft                        = \"session.earliest_possible_extend\"\n\tViperKeyCookieSameSite                                   = \"cookies.same_site\"\n\tViperKeyCookieDomain                                     = \"cookies.domain\"\n\tViperKeyCookiePath                                       = \"cookies.path\"\n\tViperKeyCookieSecure                                     = \"cookies.secure\"\n\tViperKeySelfServiceStrategyConfig                        = \"selfservice.methods\"\n\tViperKeySelfServiceBrowserDefaultReturnTo                = \"selfservice.\" + DefaultBrowserReturnURL\n\tViperKeyURLsAllowedReturnToDomains                       = \"selfservice.allowed_return_urls\"\n\tViperKeySelfServiceRegistrationEnabled                   = \"selfservice.flows.registration.enabled\"\n\tViperKeySelfServiceRegistrationLoginHints                = \"selfservice.flows.registration.login_hints\"\n\tViperKeySelfServiceRegistrationEnableLegacyOneStep       = \"selfservice.flows.registration.enable_legacy_one_step\"\n\tViperKeySelfServiceRegistrationFlowStyle                 = \"selfservice.flows.registration.style\"\n\tViperKeySelfServiceRegistrationUI                        = \"selfservice.flows.registration.ui_url\"\n\tViperKeySelfServiceRegistrationRequestLifespan           = \"selfservice.flows.registration.lifespan\"\n\tViperKeySelfServiceRegistrationAfter                     = \"selfservice.flows.registration.after\"\n\tViperKeySelfServiceRegistrationBeforeHooks               = \"selfservice.flows.registration.before.hooks\"\n\tViperKeySelfServiceLoginUI                               = \"selfservice.flows.login.ui_url\"\n\tViperKeySelfServiceLoginFlowStyle                        = \"selfservice.flows.login.style\"\n\tViperKeySecurityAccountEnumerationMitigate               = \"security.account_enumeration.mitigate\"\n\tViperKeySelfServiceLoginRequestLifespan                  = \"selfservice.flows.login.lifespan\"\n\tViperKeySelfServiceLoginAfter                            = \"selfservice.flows.login.after\"\n\tViperKeySelfServiceLoginBeforeHooks                      = \"selfservice.flows.login.before.hooks\"\n\tViperKeySelfServiceErrorUI                               = \"selfservice.flows.error.ui_url\"\n\tViperKeySelfServiceLogoutBrowserDefaultReturnTo          = \"selfservice.flows.logout.after.\" + DefaultBrowserReturnURL\n\tViperKeySelfServiceSettingsURL                           = \"selfservice.flows.settings.ui_url\"\n\tViperKeySelfServiceSettingsAfter                         = \"selfservice.flows.settings.after\"\n\tViperKeySelfServiceSettingsBeforeHooks                   = \"selfservice.flows.settings.before.hooks\"\n\tViperKeySelfServiceSettingsRequestLifespan               = \"selfservice.flows.settings.lifespan\"\n\tViperKeySelfServiceSettingsPrivilegedAuthenticationAfter = \"selfservice.flows.settings.privileged_session_max_age\"\n\tViperKeySelfServiceSettingsRequiredAAL                   = \"selfservice.flows.settings.required_aal\"\n\tViperKeySelfServiceRecoveryAfter                         = \"selfservice.flows.recovery.after\"\n\tViperKeySelfServiceRecoveryBeforeHooks                   = \"selfservice.flows.recovery.before.hooks\"\n\tViperKeySelfServiceRecoveryEnabled                       = \"selfservice.flows.recovery.enabled\"\n\tViperKeySelfServiceRecoveryUse                           = \"selfservice.flows.recovery.use\"\n\tViperKeySelfServiceRecoveryUI                            = \"selfservice.flows.recovery.ui_url\"\n\tViperKeySelfServiceRecoveryRequestLifespan               = \"selfservice.flows.recovery.lifespan\"\n\tViperKeySelfServiceRecoveryBrowserDefaultReturnTo        = \"selfservice.flows.recovery.after.\" + DefaultBrowserReturnURL\n\tViperKeySelfServiceRecoveryNotifyUnknownRecipients       = \"selfservice.flows.recovery.notify_unknown_recipients\"\n\tViperKeySelfServiceVerificationEnabled                   = \"selfservice.flows.verification.enabled\"\n\tViperKeySelfServiceVerificationUI                        = \"selfservice.flows.verification.ui_url\"\n\tViperKeySelfServiceVerificationRequestLifespan           = \"selfservice.flows.verification.lifespan\"\n\tViperKeySelfServiceVerificationBrowserDefaultReturnTo    = \"selfservice.flows.verification.after.\" + DefaultBrowserReturnURL\n\tViperKeySelfServiceVerificationAfter                     = \"selfservice.flows.verification.after\"\n\tViperKeySelfServiceVerificationBeforeHooks               = \"selfservice.flows.verification.before.hooks\"\n\tViperKeySelfServiceVerificationUse                       = \"selfservice.flows.verification.use\"\n\tViperKeySelfServiceVerificationNotifyUnknownRecipients   = \"selfservice.flows.verification.notify_unknown_recipients\"\n\tViperKeyDefaultIdentitySchemaID                          = \"identity.default_schema_id\"\n\tViperKeyIdentitySchemas                                  = \"identity.schemas\"\n\tViperKeyHasherAlgorithm                                  = \"hashers.algorithm\"\n\tViperKeyHasherArgon2ConfigMemory                         = \"hashers.argon2.memory\"\n\tViperKeyHasherArgon2ConfigIterations                     = \"hashers.argon2.iterations\"\n\tViperKeyHasherArgon2ConfigParallelism                    = \"hashers.argon2.parallelism\"\n\tViperKeyHasherArgon2ConfigSaltLength                     = \"hashers.argon2.salt_length\"\n\tViperKeyHasherArgon2ConfigKeyLength                      = \"hashers.argon2.key_length\"\n\tViperKeyHasherArgon2ConfigExpectedDuration               = \"hashers.argon2.expected_duration\"\n\tViperKeyHasherArgon2ConfigExpectedDeviation              = \"hashers.argon2.expected_deviation\"\n\tViperKeyHasherArgon2ConfigDedicatedMemory                = \"hashers.argon2.dedicated_memory\"\n\tViperKeyHasherBcryptCost                                 = \"hashers.bcrypt.cost\"\n\tViperKeyCipherAlgorithm                                  = \"ciphers.algorithm\"\n\tViperKeyDatabaseCleanupSleepTables                       = \"database.cleanup.sleep.tables\"\n\tViperKeyDatabaseCleanupBatchSize                         = \"database.cleanup.batch_size\"\n\tViperKeyLinkLifespan                                     = \"selfservice.methods.link.config.lifespan\"\n\tViperKeyCodeLifespan                                     = \"selfservice.methods.code.config.lifespan\"\n\tViperKeyCodeMaxSubmissions                               = \"selfservice.methods.code.config.max_submissions\"\n\tViperKeyCodeConfigMissingCredentialFallbackEnabled       = \"selfservice.methods.code.config.missing_credential_fallback_enabled\"\n\tViperKeyPasswordHaveIBeenPwnedHost                       = \"selfservice.methods.password.config.haveibeenpwned_host\"\n\tViperKeyPasswordHaveIBeenPwnedEnabled                    = \"selfservice.methods.password.config.haveibeenpwned_enabled\"\n\tViperKeyPasswordMaxBreaches                              = \"selfservice.methods.password.config.max_breaches\"\n\tViperKeyPasswordMinLength                                = \"selfservice.methods.password.config.min_password_length\"\n\tViperKeyPasswordIdentifierSimilarityCheckEnabled         = \"selfservice.methods.password.config.identifier_similarity_check_enabled\"\n\tViperKeyIgnoreNetworkErrors                              = \"selfservice.methods.password.config.ignore_network_errors\"\n\tViperKeyPasswordRegistrationProfileGroup                 = \"selfservice.methods.password.config.password_profile_registration_node_group\"\n\tViperKeyTOTPIssuer                                       = \"selfservice.methods.totp.config.issuer\"\n\tViperKeyOIDCBaseRedirectURL                              = \"selfservice.methods.oidc.config.base_redirect_uri\"\n\tViperKeySAMLBaseRedirectURL                              = \"selfservice.methods.saml.config.base_redirect_uri\"\n\tViperKeyWebAuthnRPDisplayName                            = \"selfservice.methods.webauthn.config.rp.display_name\"\n\tViperKeyWebAuthnRPID                                     = \"selfservice.methods.webauthn.config.rp.id\"\n\tViperKeyWebAuthnRPOrigin                                 = \"selfservice.methods.webauthn.config.rp.origin\"\n\tViperKeyWebAuthnRPOrigins                                = \"selfservice.methods.webauthn.config.rp.origins\"\n\tViperKeyWebAuthnPasswordless                             = \"selfservice.methods.webauthn.config.passwordless\"\n\tViperKeyPasskeyEnabled                                   = \"selfservice.methods.passkey.enabled\"\n\tViperKeyPasskeyRPDisplayName                             = \"selfservice.methods.passkey.config.rp.display_name\"\n\tViperKeyPasskeyRPID                                      = \"selfservice.methods.passkey.config.rp.id\"\n\tViperKeyPasskeyRPOrigins                                 = \"selfservice.methods.passkey.config.rp.origins\"\n\tViperKeyOAuth2ProviderURL                                = \"oauth2_provider.url\"\n\tViperKeyOAuth2ProviderHeader                             = \"oauth2_provider.headers\"\n\tViperKeyOAuth2ProviderOverrideReturnTo                   = \"oauth2_provider.override_return_to\"\n\tViperKeyClientHTTPNoPrivateIPRanges                      = \"clients.http.disallow_private_ip_ranges\"\n\tViperKeyClientHTTPPrivateIPExceptionURLs                 = \"clients.http.private_ip_exception_urls\"\n\tViperKeyWebhookHeaderAllowlist                           = \"clients.web_hook.header_allowlist\"\n\tViperKeyPreviewDefaultReadConsistencyLevel               = \"preview.default_read_consistency_level\"\n\tViperKeyVersion                                          = \"version\"\n\tViperKeyPasswordMigrationHook                            = \"selfservice.methods.password.config.migrate_hook\"\n)\n\nconst (\n\tHighestAvailableAAL                 = \"highest_available\"\n\tArgon2DefaultMemory                 = 128 * bytesize.MB\n\tArgon2DefaultIterations      uint32 = 1\n\tArgon2DefaultSaltLength      uint32 = 16\n\tArgon2DefaultKeyLength       uint32 = 32\n\tArgon2DefaultDuration               = 500 * time.Millisecond\n\tArgon2DefaultDeviation              = 500 * time.Millisecond\n\tArgon2DefaultDedicatedMemory        = 1 * bytesize.GB\n\tBcryptDefaultCost            uint32 = 12\n)\n\n// DefaultSessionCookieName returns the default cookie name for the kratos session.\nconst DefaultSessionCookieName = \"ory_kratos_session\"\n\ntype (\n\tArgon2 struct {\n\t\tMemory            bytesize.ByteSize `json:\"memory\"`\n\t\tIterations        uint32            `json:\"iterations\"`\n\t\tParallelism       uint8             `json:\"parallelism\"`\n\t\tSaltLength        uint32            `json:\"salt_length\"`\n\t\tKeyLength         uint32            `json:\"key_length\"`\n\t\tExpectedDuration  time.Duration     `json:\"expected_duration\"`\n\t\tExpectedDeviation time.Duration     `json:\"expected_deviation\"`\n\t\tDedicatedMemory   bytesize.ByteSize `json:\"dedicated_memory\"`\n\t}\n\tBcrypt struct {\n\t\tCost uint32 `json:\"cost\"`\n\t}\n\tSelfServiceHook struct {\n\t\tName   string          `json:\"hook\"`\n\t\tConfig json.RawMessage `json:\"config\"`\n\t}\n\tSelfServiceStrategy struct {\n\t\tEnabled bool            `json:\"enabled\"`\n\t\tConfig  json.RawMessage `json:\"config\"`\n\t}\n\tSelfServiceStrategyCode struct {\n\t\t*SelfServiceStrategy\n\t\tPasswordlessEnabled bool `json:\"passwordless_enabled\"`\n\t\tMFAEnabled          bool `json:\"mfa_enabled\"`\n\t}\n\tSchema struct {\n\t\tID                    string `json:\"id\" koanf:\"id\"`\n\t\tURL                   string `json:\"url\" koanf:\"url\"`\n\t\tSelfserviceSelectable bool   `json:\"selfservice_selectable\" koanf:\"selfservice_selectable\"`\n\t}\n\tPasswordPolicy struct {\n\t\tHaveIBeenPwnedHost               string `json:\"haveibeenpwned_host\"`\n\t\tHaveIBeenPwnedEnabled            bool   `json:\"haveibeenpwned_enabled\"`\n\t\tMaxBreaches                      uint   `json:\"max_breaches\"`\n\t\tIgnoreNetworkErrors              bool   `json:\"ignore_network_errors\"`\n\t\tMinPasswordLength                uint   `json:\"min_password_length\"`\n\t\tIdentifierSimilarityCheckEnabled bool   `json:\"identifier_similarity_check_enabled\"`\n\t}\n\tSchemas                  []Schema\n\tCourierEmailBodyTemplate struct {\n\t\tPlainText string `json:\"plaintext\"`\n\t\tHTML      string `json:\"html\"`\n\t}\n\tCourierEmailTemplate struct {\n\t\tBody    *CourierEmailBodyTemplate `json:\"body\"`\n\t\tSubject string                    `json:\"subject\"`\n\t}\n\tCourierSMSTemplate struct {\n\t\tBody *CourierSMSTemplateBody `json:\"body\"`\n\t}\n\tCourierSMSTemplateBody struct {\n\t\tPlainText string `json:\"plaintext\"`\n\t}\n\tCourierChannel struct {\n\t\tID            string         `json:\"id\" koanf:\"id\"`\n\t\tType          string         `json:\"type\" koanf:\"type\"`\n\t\tSMTPConfig    *SMTPConfig    `json:\"smtp_config\" koanf:\"smtp_config\"`\n\t\tRequestConfig request.Config `json:\"request_config\" koanf:\"request_config\"`\n\t}\n\tSMTPConfig struct {\n\t\tConnectionURI  string            `json:\"connection_uri\" koanf:\"connection_uri\"`\n\t\tClientCertPath string            `json:\"client_cert_path\" koanf:\"client_cert_path\"`\n\t\tClientKeyPath  string            `json:\"client_key_path\" koanf:\"client_key_path\"`\n\t\tFromAddress    string            `json:\"from_address\" koanf:\"from_address\"`\n\t\tFromName       string            `json:\"from_name\" koanf:\"from_name\"`\n\t\tHeaders        map[string]string `json:\"headers\" koanf:\"headers\"`\n\t\tLocalName      string            `json:\"local_name\" koanf:\"local_name\"`\n\t}\n\tPasswordMigrationHook struct {\n\t\tEnabled bool           `json:\"enabled\" koanf:\"enabled\"`\n\t\tConfig  request.Config `json:\"config\" koanf:\"config\"`\n\t}\n\tConfig struct {\n\t\tl                  *logrusx.Logger\n\t\tp                  *configx.Provider\n\t\tc                  contextx.Contextualizer\n\t\tidentityMetaSchema *jsonschema.Schema\n\t\tstdOutOrErr        io.Writer\n\t}\n\tProvider interface {\n\t\tConfig() *Config\n\t}\n\tCourierConfigs interface {\n\t\tCourierTemplatesRoot(ctx context.Context) string\n\t\tCourierTemplatesVerificationInvalid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierTemplatesVerificationValid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierTemplatesRecoveryInvalid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierTemplatesRecoveryValid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierTemplatesRecoveryCodeInvalid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierTemplatesRecoveryCodeValid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierTemplatesVerificationCodeInvalid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierTemplatesVerificationCodeValid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierTemplatesLoginCodeValid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierTemplatesRegistrationCodeValid(ctx context.Context) *CourierEmailTemplate\n\t\tCourierSMSTemplatesVerificationCodeValid(ctx context.Context) *CourierSMSTemplate\n\t\tCourierSMSTemplatesRecoveryCodeValid(ctx context.Context) *CourierSMSTemplate\n\t\tCourierSMSTemplatesLoginCodeValid(ctx context.Context) *CourierSMSTemplate\n\t\tCourierSMSTemplatesRegistrationCodeValid(ctx context.Context) *CourierSMSTemplate\n\t\tCourierMessageRetries(ctx context.Context) int\n\t\tCourierWorkerPullCount(ctx context.Context) int\n\t\tCourierWorkerPullWait(ctx context.Context) time.Duration\n\t\tCourierChannels(context.Context) ([]*CourierChannel, error)\n\t}\n)\n\nfunc (c *Argon2) MarshalJSON() ([]byte, error) {\n\ttype encoded struct {\n\t\tMemory            string `json:\"memory\"`\n\t\tIterations        uint32 `json:\"iterations\"`\n\t\tParallelism       uint8  `json:\"parallelism\"`\n\t\tSaltLength        uint32 `json:\"salt_length\"`\n\t\tKeyLength         uint32 `json:\"key_length\"`\n\t\tExpectedDuration  string `json:\"minimal_duration\"`\n\t\tExpectedDeviation string `json:\"expected_deviation\"`\n\t\tDedicatedMemory   string `json:\"dedicated_memory\"`\n\t}\n\n\treturn json.Marshal(&encoded{\n\t\tMemory:            c.Memory.String(),\n\t\tIterations:        c.Iterations,\n\t\tParallelism:       c.Parallelism,\n\t\tSaltLength:        c.SaltLength,\n\t\tKeyLength:         c.KeyLength,\n\t\tExpectedDuration:  c.ExpectedDuration.String(),\n\t\tExpectedDeviation: c.ExpectedDeviation.String(),\n\t\tDedicatedMemory:   c.DedicatedMemory.String(),\n\t})\n}\n\nvar Argon2DefaultParallelism = uint8(runtime.NumCPU() * 2)\n\nconst HookGlobal = \"global\"\n\nfunc HookStrategyKey(key, strategy string) string {\n\tif strategy == HookGlobal {\n\t\treturn fmt.Sprintf(\"%s.hooks\", key)\n\t} else {\n\t\treturn fmt.Sprintf(\"%s.%s.hooks\", key, strategy)\n\t}\n}\n\nfunc (s Schemas) FindSchemaByID(id string) (*Schema, error) {\n\tfor _, sc := range s {\n\t\tif sc.ID == id {\n\t\t\treturn &sc, nil\n\t\t}\n\t}\n\n\treturn nil, errors.Errorf(\"unable to find identity schema with id: %s\", id)\n}\n\nfunc MustNew(t testing.TB, l *logrusx.Logger, ctxer contextx.Contextualizer, opts ...configx.OptionModifier) *Config {\n\tp, err := New(t.Context(), l, os.Stderr, ctxer, opts...)\n\trequire.NoError(t, err)\n\treturn p\n}\n\nfunc New(ctx context.Context, l *logrusx.Logger, stdOutOrErr io.Writer, ctxer contextx.Contextualizer, opts ...configx.OptionModifier) (*Config, error) {\n\tvar c *Config\n\n\topts = append([]configx.OptionModifier{\n\t\tconfigx.WithStderrValidationReporter(),\n\t\tconfigx.OmitKeysFromTracing(\"dsn\", \"courier.smtp.connection_uri\", \"secrets.default\", \"secrets.cookie\", \"secrets.cipher\", \"client_secret\"),\n\t\tconfigx.WithImmutables(\"serve\", \"profiling\", \"log\"),\n\t\tconfigx.WithExceptImmutables(\"serve.public.cors.allowed_origins\"),\n\t\tconfigx.WithLogrusWatcher(l),\n\t\tconfigx.WithLogger(l),\n\t\tconfigx.WithContext(ctx),\n\t\tconfigx.AttachWatcher(func(event watcherx.Event, err error) {\n\t\t\tif c == nil {\n\t\t\t\tpanic(errors.New(\"the config provider did not initialise correctly in time\"))\n\t\t\t}\n\t\t\tif err := c.validateIdentitySchemas(ctx); err != nil {\n\t\t\t\tl.WithError(err).\n\t\t\t\t\tErrorf(\"The changed identity schema configuration is invalid and could not be loaded. Rolling back to the last working configuration revision. Please address the validation errors before restarting the process.\")\n\t\t\t}\n\t\t}),\n\t}, opts...)\n\n\tp, err := configx.New(ctx, embedx.ConfigSchema, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tl.UseConfig(p)\n\n\tc = NewCustom(l, p, stdOutOrErr, ctxer)\n\n\tif !p.SkipValidation() {\n\t\tif err := c.validateIdentitySchemas(ctx); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn c, nil\n}\n\nfunc NewCustom(l *logrusx.Logger, p *configx.Provider, stdOutOrErr io.Writer, ctxt contextx.Contextualizer) *Config {\n\tl.UseConfig(p)\n\treturn &Config{l: l, p: p, c: ctxt, stdOutOrErr: stdOutOrErr}\n}\n\nfunc (p *Config) getIdentitySchemaValidator(ctx context.Context) (*jsonschema.Schema, error) {\n\tif p.identityMetaSchema == nil {\n\t\tc := jsonschema.NewCompiler()\n\t\terr := embedx.AddSchemaResources(c, embedx.IdentityMeta)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tp.identityMetaSchema, err = c.Compile(ctx, embedx.IdentityMeta.GetSchemaID())\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t}\n\n\treturn p.identityMetaSchema, nil\n}\n\ntype validateIdentitySchemasContextKey int\n\nconst validateIdentitySchemasClientKey validateIdentitySchemasContextKey = 1\n\nfunc SetValidateIdentitySchemaResilientClientOptions(ctx context.Context, options []httpx.ResilientOptions) context.Context {\n\treturn context.WithValue(ctx, validateIdentitySchemasClientKey, options)\n}\n\nfunc (p *Config) validateIdentitySchemas(ctx context.Context) error {\n\topts := []httpx.ResilientOptions{\n\t\thttpx.ResilientClientWithLogger(p.l),\n\t\thttpx.ResilientClientWithMaxRetry(2),\n\t\thttpx.ResilientClientWithConnectionTimeout(30 * time.Second),\n\t}\n\n\tif o, ok := ctx.Value(validateIdentitySchemasClientKey).([]httpx.ResilientOptions); ok {\n\t\topts = o\n\t}\n\n\tif p.ClientHTTPNoPrivateIPRanges(ctx) {\n\t\topts = append(opts, httpx.ResilientClientDisallowInternalIPs())\n\t}\n\n\tctx = context.WithValue(ctx, httploader.ContextKey, httpx.NewResilientClient(opts...))\n\n\tj, err := p.getIdentitySchemaValidator(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tss, err := p.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, s := range ss {\n\t\tresource, err := jsonschema.LoadURL(ctx, s.URL)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tdefer func() { _ = resource.Close() }()\n\n\t\tschema, err := io.ReadAll(io.LimitReader(resource, 1024*1024))\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tif err = j.Validate(bytes.NewBuffer(schema)); err != nil {\n\t\t\tp.formatJsonErrors(schema, err)\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *Config) formatJsonErrors(schema []byte, err error) {\n\t_, _ = fmt.Fprintln(p.stdOutOrErr, \"\")\n\tjsonschemax.FormatValidationErrorForCLI(p.stdOutOrErr, schema, err)\n}\n\nfunc (p *Config) ServePublic(ctx context.Context) *configx.Serve {\n\treturn p.GetProvider(ctx).Serve(\"serve.public\", p.IsInsecureDevMode(ctx), configx.Serve{\n\t\tPort: 4433,\n\t})\n}\n\nfunc (p *Config) ServeAdmin(ctx context.Context) *configx.Serve {\n\treturn p.GetProvider(ctx).Serve(\"serve.admin\", p.IsInsecureDevMode(ctx), configx.Serve{\n\t\tPort: 4434,\n\t})\n}\n\nfunc (p *Config) CORSPublic(ctx context.Context) (cors.Options, bool) {\n\treturn p.GetProvider(ctx).CORS(\"serve.public\", cors.Options{\n\t\tAllowedMethods:   []string{\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"},\n\t\tAllowedHeaders:   []string{\"Authorization\", \"Content-Type\", \"Cookie\"},\n\t\tExposedHeaders:   []string{\"Content-Type\", \"Set-Cookie\"},\n\t\tAllowCredentials: true,\n\t})\n}\n\n// Deprecated: use context-based [contextx.WithConfigValue] instead.\nfunc (p *Config) Set(_ context.Context, key string, value interface{}) error {\n\treturn p.p.Set(key, value)\n}\n\n// Deprecated: use context-based [contextx.WithConfigValue] instead.\nfunc (p *Config) MustSet(_ context.Context, key string, value interface{}) {\n\tif err := p.p.Set(key, value); err != nil {\n\t\tp.l.WithError(err).Fatalf(\"Unable to set %q to %q.\", key, value)\n\t}\n}\n\nfunc (p *Config) SessionName(ctx context.Context) string {\n\treturn cmp.Or(p.GetProvider(ctx).String(ViperKeySessionName), DefaultSessionCookieName)\n}\n\nfunc (p *Config) HasherArgon2(ctx context.Context) *Argon2 {\n\t// warn about usage of default values and point to the docs\n\t// warning will require https://github.com/ory/viper/issues/19\n\treturn &Argon2{\n\t\tMemory: p.GetProvider(ctx).ByteSizeF(ViperKeyHasherArgon2ConfigMemory, Argon2DefaultMemory),\n\t\t//nolint:gosec // disable G115\n\t\tIterations: uint32(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigIterations, int(Argon2DefaultIterations))),\n\t\t//nolint:gosec // disable G115\n\t\tParallelism: uint8(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigParallelism, int(Argon2DefaultParallelism))),\n\t\t//nolint:gosec // disable G115\n\t\tSaltLength: uint32(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigSaltLength, int(Argon2DefaultSaltLength))),\n\t\t//nolint:gosec // disable G115\n\t\tKeyLength:         uint32(p.GetProvider(ctx).IntF(ViperKeyHasherArgon2ConfigKeyLength, int(Argon2DefaultKeyLength))),\n\t\tExpectedDuration:  p.GetProvider(ctx).DurationF(ViperKeyHasherArgon2ConfigExpectedDuration, Argon2DefaultDuration),\n\t\tExpectedDeviation: p.GetProvider(ctx).DurationF(ViperKeyHasherArgon2ConfigExpectedDeviation, Argon2DefaultDeviation),\n\t\tDedicatedMemory:   p.GetProvider(ctx).ByteSizeF(ViperKeyHasherArgon2ConfigDedicatedMemory, Argon2DefaultDedicatedMemory),\n\t}\n}\n\nfunc (p *Config) HasherBcrypt(ctx context.Context) *Bcrypt {\n\tcost := uint32(p.GetProvider(ctx).IntF(ViperKeyHasherBcryptCost, int(BcryptDefaultCost))) // #nosec G115 -- if the user configures a cost > MaxUint32, go falls back to MaxUint32\n\tif !p.IsInsecureDevMode(ctx) && cost < BcryptDefaultCost {\n\t\tcost = BcryptDefaultCost\n\t}\n\n\treturn &Bcrypt{Cost: cost}\n}\n\nfunc (p *Config) DefaultIdentityTraitsSchemaURL(ctx context.Context) (*url.URL, error) {\n\tss, err := p.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsearch := p.GetProvider(ctx).String(ViperKeyDefaultIdentitySchemaID)\n\tfound, err := ss.FindSchemaByID(search)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn p.ParseURI(found.URL)\n}\n\nfunc (p *Config) DefaultIdentityTraitsSchemaID(ctx context.Context) string {\n\treturn p.GetProvider(ctx).String(ViperKeyDefaultIdentitySchemaID)\n}\n\nfunc (p *Config) IdentityTraitsSchemaURL(ctx context.Context, schemaID string) (*url.URL, error) {\n\tss, err := p.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfound, err := ss.FindSchemaByID(schemaID)\n\tif err != nil {\n\t\t// default to default schema\n\t\tsearch := p.GetProvider(ctx).String(ViperKeyDefaultIdentitySchemaID)\n\t\tfound, err = ss.FindSchemaByID(search)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn p.ParseURI(found.URL)\n}\n\nfunc (p *Config) TOTPIssuer(ctx context.Context) string {\n\treturn p.GetProvider(ctx).StringF(ViperKeyTOTPIssuer, p.SelfPublicURL(ctx).Hostname())\n}\n\nfunc (p *Config) OIDCRedirectURIBase(ctx context.Context) *url.URL {\n\treturn p.GetProvider(ctx).URIF(ViperKeyOIDCBaseRedirectURL, p.SelfPublicURL(ctx))\n}\n\nfunc (p *Config) SAMLRedirectURIBase(ctx context.Context) *url.URL {\n\treturn p.GetProvider(ctx).URIF(ViperKeySAMLBaseRedirectURL, p.SelfPublicURL(ctx))\n}\n\nfunc (p *Config) IdentityTraitsSchemas(ctx context.Context) (ss Schemas, err error) {\n\tif err = p.GetProvider(ctx).Unmarshal(ViperKeyIdentitySchemas, &ss); err != nil {\n\t\treturn ss, nil\n\t}\n\n\treturn ss, nil\n}\n\nfunc (p *Config) DSN(ctx context.Context) string {\n\tpp := p.GetProvider(ctx)\n\tdsn := pp.String(ViperKeyDSN)\n\n\tif dsn == \"memory\" {\n\t\treturn DefaultSQLiteMemoryDSN\n\t}\n\n\tif len(dsn) > 0 {\n\t\treturn dsn\n\t}\n\n\t// Print a stack trace to aid debugging.\n\tp.l.Fatalf(\"%+v\", errors.Errorf(\"dsn must be set\"))\n\treturn \"\"\n}\n\nfunc (p *Config) DisableAPIFlowEnforcement(ctx context.Context) bool {\n\tif p.IsInsecureDevMode(ctx) && os.Getenv(\"DEV_DISABLE_API_FLOW_ENFORCEMENT\") == \"true\" {\n\t\tp.l.Warn(\"Because \\\"DEV_DISABLE_API_FLOW_ENFORCEMENT=true\\\" and the \\\"--dev\\\" flag are set, self-service API flows will no longer check if the interaction is actually a browser flow. This is very dangerous as it allows bypassing of anti-CSRF measures, leaving the deployment highly vulnerable. This option should only be used for automated testing and never come close to real user data anywhere.\")\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (p *Config) ClientHTTPNoPrivateIPRanges(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeyClientHTTPNoPrivateIPRanges)\n}\n\nfunc (p *Config) ClientHTTPPrivateIPExceptionURLs(ctx context.Context) []string {\n\treturn p.GetProvider(ctx).Strings(ViperKeyClientHTTPPrivateIPExceptionURLs)\n}\n\nfunc (p *Config) SelfServiceFlowRegistrationEnabled(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeySelfServiceRegistrationEnabled)\n}\n\nfunc (p *Config) SelfServiceFlowRegistrationLoginHints(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeySelfServiceRegistrationLoginHints)\n}\n\nfunc (p *Config) SelfServiceFlowRegistrationPasswordMethodProfileGroup(ctx context.Context) string {\n\tswitch g := p.GetProvider(ctx).String(ViperKeyPasswordRegistrationProfileGroup); g {\n\tcase \"password\":\n\t\treturn \"password\"\n\tdefault:\n\t\treturn \"default\"\n\t}\n}\n\nfunc (p *Config) SelfServiceLegacyOIDCRegistrationGroup(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeyLegacyOIDCRegistrationGroup)\n}\n\nfunc (p *Config) SelfServiceFlowRegistrationTwoSteps(ctx context.Context) bool {\n\t// The default in previous versions that legacy one-step would be disabled. If legacy is enabled, it means the\n\t// user has explicitly set the key to true, in which case we respect it.\n\tif useOneStep := p.GetProvider(ctx).Bool(ViperKeySelfServiceRegistrationEnableLegacyOneStep); useOneStep {\n\t\tp.l.Warnf(\"Found use of deprecated configuration key %q. Please use key %q instead and delete key %[1]q. Will use value from %[1]q to configure registration style.\", ViperKeySelfServiceRegistrationEnableLegacyOneStep, ViperKeySelfServiceRegistrationFlowStyle)\n\t\treturn false\n\t}\n\n\t// In all other cases, we use the new key which (like the old key) defaults to `profile_first` / two-step registration.\n\tswitch style := p.GetProvider(ctx).String(ViperKeySelfServiceRegistrationFlowStyle); style {\n\tcase \"profile_first\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (p *Config) SelfServiceFlowIdentitySchema(ctx context.Context, requestedSchema string) (string, error) {\n\tif requestedSchema == p.GetProvider(ctx).String(ViperKeyDefaultIdentitySchemaID) {\n\t\treturn requestedSchema, nil\n\t}\n\tschemas, err := p.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn \"\", errors.WithStack(err)\n\t}\n\tfor _, schema := range schemas {\n\t\tif schema.ID == requestedSchema {\n\t\t\tif !schema.SelfserviceSelectable {\n\t\t\t\treturn \"\", errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Requested identity schema %q is not enabled for self-service flows.\", requestedSchema))\n\t\t\t}\n\t\t\treturn requestedSchema, nil\n\t\t}\n\t}\n\treturn \"\", errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Requested identity schema %q does not exist.\", requestedSchema))\n}\n\nfunc (p *Config) SelfServiceFlowVerificationEnabled(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeySelfServiceVerificationEnabled)\n}\n\nfunc (p *Config) UseLegacyShowVerificationUI(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeyUseLegacyShowVerificationUI)\n}\n\nfunc (p *Config) UseLegacyRequireVerifiedLoginError(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeyUseLegacyRequireVerifiedLoginError)\n}\n\nfunc (p *Config) SelfServiceFlowRecoveryEnabled(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeySelfServiceRecoveryEnabled)\n}\n\nfunc (p *Config) SelfServiceFlowRecoveryUse(ctx context.Context) string {\n\treturn p.GetProvider(ctx).String(ViperKeySelfServiceRecoveryUse)\n}\n\nfunc (p *Config) SelfServiceFlowLoginBeforeHooks(ctx context.Context) []SelfServiceHook {\n\treturn p.selfServiceHooks(ctx, ViperKeySelfServiceLoginBeforeHooks)\n}\n\nfunc (p *Config) SelfServiceFlowRecoveryBeforeHooks(ctx context.Context) []SelfServiceHook {\n\treturn p.selfServiceHooks(ctx, ViperKeySelfServiceRecoveryBeforeHooks)\n}\n\nfunc (p *Config) SelfServiceFlowVerificationBeforeHooks(ctx context.Context) []SelfServiceHook {\n\treturn p.selfServiceHooks(ctx, ViperKeySelfServiceVerificationBeforeHooks)\n}\n\nfunc (p *Config) SelfServiceFlowVerificationUse(ctx context.Context) string {\n\treturn p.GetProvider(ctx).String(ViperKeySelfServiceVerificationUse)\n}\n\nfunc (p *Config) SelfServiceFlowVerificationNotifyUnknownRecipients(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).BoolF(ViperKeySelfServiceVerificationNotifyUnknownRecipients, false)\n}\n\nfunc (p *Config) SelfServiceFlowSettingsBeforeHooks(ctx context.Context) []SelfServiceHook {\n\treturn p.selfServiceHooks(ctx, ViperKeySelfServiceSettingsBeforeHooks)\n}\n\nfunc (p *Config) SelfServiceFlowRegistrationBeforeHooks(ctx context.Context) []SelfServiceHook {\n\thooks := p.selfServiceHooks(ctx, ViperKeySelfServiceRegistrationBeforeHooks)\n\tif p.SelfServiceFlowRegistrationTwoSteps(ctx) {\n\t\thooks = append(hooks, SelfServiceHook{\"two_step_registration\", json.RawMessage(\"{}\")})\n\t}\n\n\treturn hooks\n}\n\nfunc (p *Config) selfServiceHooks(ctx context.Context, key string) []SelfServiceHook {\n\tpp := p.GetProvider(ctx)\n\tval := pp.Get(key)\n\tif val == nil {\n\t\treturn []SelfServiceHook{}\n\t}\n\n\tconfig, err := json.Marshal(val)\n\tif err != nil {\n\t\tp.l.WithError(err).Fatalf(\"Unable to decode values from configuration key: %s\", key)\n\t}\n\n\tvar hooks []SelfServiceHook\n\tif err := json.Unmarshal(config, &hooks); err != nil {\n\t\tp.l.WithError(err).Fatalf(\"Unable to encode value \\\"%s\\\" from configuration key: %s\", config, key)\n\t}\n\n\tfor k := range hooks {\n\t\tif len(hooks[k].Config) == 0 {\n\t\t\thooks[k].Config = json.RawMessage(\"{}\")\n\t\t}\n\t}\n\n\treturn hooks\n}\n\nfunc (p *Config) SelfServiceFlowLoginAfterHooks(ctx context.Context, strategy string) []SelfServiceHook {\n\treturn p.selfServiceHooks(ctx, HookStrategyKey(ViperKeySelfServiceLoginAfter, strategy))\n}\n\nfunc (p *Config) SelfServiceFlowSettingsAfterHooks(ctx context.Context, strategy string) []SelfServiceHook {\n\treturn p.selfServiceHooks(ctx, HookStrategyKey(ViperKeySelfServiceSettingsAfter, strategy))\n}\n\nfunc (p *Config) SelfServiceFlowRegistrationAfterHooks(ctx context.Context, strategy string) []SelfServiceHook {\n\treturn p.selfServiceHooks(ctx, HookStrategyKey(ViperKeySelfServiceRegistrationAfter, strategy))\n}\n\nfunc (p *Config) SelfServiceStrategy(ctx context.Context, strategy string) *SelfServiceStrategy {\n\tpp := p.GetProvider(ctx)\n\tconfig := json.RawMessage(\"{}\")\n\tbasePath := fmt.Sprintf(\"%s.%s\", ViperKeySelfServiceStrategyConfig, strategy)\n\n\tvar err error\n\tconfig, err = json.Marshal(pp.GetF(basePath+\".config\", config))\n\tif err != nil {\n\t\tp.l.WithError(err).Warn(\"Unable to marshal self-service strategy configuration.\")\n\t\tconfig = json.RawMessage(\"{}\")\n\t}\n\n\t// The default value can easily be overwritten by setting e.g. `{\"selfservice\": \"null\"}` which means that\n\t// we need to forcibly set these values here:\n\tdefaultEnabled := false\n\tswitch strategy {\n\tcase \"identifier_first\":\n\t\tdefaultEnabled = p.SelfServiceLoginFlowIdentifierFirstEnabled(ctx)\n\tcase \"code\", \"password\", \"profile\":\n\t\tdefaultEnabled = true\n\t}\n\n\t// Backwards compatibility for the old \"passwordless_enabled\" key\n\t// This force-enables the code strategy, if passwordless is enabled, because in earlier versions it was possible to\n\t// disable the code strategy, but enable passwordless\n\tenabled := pp.BoolF(basePath+\".enabled\", defaultEnabled)\n\tif strategy == \"code\" {\n\t\tenabled = enabled || pp.Bool(basePath+\".passwordless_enabled\")\n\t}\n\treturn &SelfServiceStrategy{\n\t\tEnabled: enabled,\n\t\tConfig:  config,\n\t}\n}\n\nfunc (p *Config) SelfServiceCodeStrategy(ctx context.Context) *SelfServiceStrategyCode {\n\tpp := p.GetProvider(ctx)\n\tconfig := json.RawMessage(\"{}\")\n\tbasePath := ViperKeySelfServiceStrategyConfig + \".code\"\n\n\tvar err error\n\tconfig, err = json.Marshal(pp.GetF(basePath+\".config\", config))\n\tif err != nil {\n\t\tp.l.WithError(err).Warn(\"Unable to marshal self service strategy configuration.\")\n\t\tconfig = json.RawMessage(\"{}\")\n\t}\n\n\treturn &SelfServiceStrategyCode{\n\t\tSelfServiceStrategy: &SelfServiceStrategy{\n\t\t\tEnabled: pp.BoolF(basePath+\".enabled\", true),\n\t\t\tConfig:  config,\n\t\t},\n\t\tPasswordlessEnabled: pp.BoolF(basePath+\".passwordless_enabled\", false),\n\t\tMFAEnabled:          pp.BoolF(basePath+\".mfa_enabled\", false),\n\t}\n}\n\nfunc (p *Config) SecretsDefault(ctx context.Context) [][]byte {\n\tpp := p.GetProvider(ctx)\n\tsecrets := pp.Strings(ViperKeySecretsDefault)\n\n\tif len(secrets) == 0 {\n\t\tsecrets = []string{uuid.Must(uuid.NewV4()).String()}\n\t\tp.MustSet(ctx, ViperKeySecretsDefault, secrets)\n\t}\n\n\tresult := make([][]byte, len(secrets))\n\tfor k, v := range secrets {\n\t\tresult[k] = []byte(v)\n\t}\n\n\treturn result\n}\n\nfunc (p *Config) SecretsSession(ctx context.Context) [][]byte {\n\tsecrets := p.GetProvider(ctx).Strings(ViperKeySecretsCookie)\n\tif len(secrets) == 0 {\n\t\treturn p.SecretsDefault(ctx)\n\t}\n\n\tresult := make([][]byte, len(secrets))\n\tfor k, v := range secrets {\n\t\tresult[k] = []byte(v)\n\t}\n\n\treturn result\n}\n\nfunc (p *Config) SecretsCipher(ctx context.Context) [][32]byte {\n\tsecrets := p.GetProvider(ctx).Strings(ViperKeySecretsCipher)\n\treturn ToCipherSecrets(secrets)\n}\n\nfunc ToCipherSecrets(secrets []string) [][32]byte {\n\tvar cleanSecrets []string\n\tfor k := range secrets {\n\t\tif len(secrets[k]) == 32 {\n\t\t\tcleanSecrets = append(cleanSecrets, secrets[k])\n\t\t}\n\t}\n\tif len(cleanSecrets) == 0 {\n\t\treturn [][32]byte{}\n\t}\n\tresult := make([][32]byte, len(cleanSecrets))\n\tfor n, s := range secrets {\n\t\tfor k, v := range []byte(s) {\n\t\t\tresult[n][k] = v\n\t\t}\n\t}\n\treturn result\n}\n\nfunc (p *Config) SecretsPagination(ctx context.Context) [][32]byte {\n\tsecrets := p.GetProvider(ctx).Strings(ViperKeySecretsPagination)\n\n\tencryptionKeys := make([][32]byte, len(secrets))\n\tfor i, key := range secrets {\n\t\tencryptionKeys[i] = sha512.Sum512_256([]byte(key))\n\t}\n\n\treturn encryptionKeys\n}\n\nfunc (p *Config) SelfServiceBrowserDefaultReturnTo(ctx context.Context) *url.URL {\n\treturn p.ParseAbsoluteOrRelativeURIOrFail(ctx, ViperKeySelfServiceBrowserDefaultReturnTo)\n}\n\nfunc (p *Config) SelfPublicURL(ctx context.Context) *url.URL {\n\tserve := p.ServePublic(ctx)\n\treturn serve.BaseURL\n}\n\nfunc (p *Config) SelfAdminURL(ctx context.Context) *url.URL {\n\tserve := p.ServeAdmin(ctx)\n\treturn serve.BaseURL\n}\n\nfunc (p *Config) WebhookHeaderAllowlist(ctx context.Context) []string {\n\treturn p.GetProvider(ctx).Strings(ViperKeyWebhookHeaderAllowlist)\n}\n\nfunc (p *Config) OAuth2ProviderHeader(ctx context.Context) http.Header {\n\thh := map[string]string{}\n\tif err := p.GetProvider(ctx).Unmarshal(ViperKeyOAuth2ProviderHeader, &hh); err != nil {\n\t\tp.l.WithError(errors.WithStack(err)).\n\t\t\tErrorf(\"Configuration value from key %s could not be decoded.\", ViperKeyOAuth2ProviderHeader)\n\t\treturn nil\n\t}\n\n\th := make(http.Header)\n\tfor k, v := range hh {\n\t\th.Set(k, v)\n\t}\n\n\treturn h\n}\n\nfunc (p *Config) OAuth2ProviderOverrideReturnTo(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeyOAuth2ProviderOverrideReturnTo)\n}\n\nfunc (p *Config) OAuth2ProviderURL(ctx context.Context) *url.URL {\n\tk := ViperKeyOAuth2ProviderURL\n\tv := p.GetProvider(ctx).String(k)\n\tif v == \"\" {\n\t\treturn nil\n\t}\n\tparsed, err := p.ParseAbsoluteOrRelativeURI(v)\n\tif err != nil {\n\t\tp.l.WithError(errors.WithStack(err)).\n\t\t\tErrorf(\"Configuration value from key %s is not a valid URL: %s\", k, v)\n\t\treturn nil\n\t}\n\treturn parsed\n}\n\nfunc (p *Config) SelfServiceFlowLoginUI(ctx context.Context) *url.URL {\n\treturn p.ParseAbsoluteOrRelativeURIOrFail(ctx, ViperKeySelfServiceLoginUI)\n}\n\nfunc (p *Config) SelfServiceFlowSettingsUI(ctx context.Context) *url.URL {\n\treturn p.ParseAbsoluteOrRelativeURIOrFail(ctx, ViperKeySelfServiceSettingsURL)\n}\n\nfunc (p *Config) SelfServiceFlowErrorURL(ctx context.Context) *url.URL {\n\treturn p.ParseAbsoluteOrRelativeURIOrFail(ctx, ViperKeySelfServiceErrorUI)\n}\n\nfunc (p *Config) SelfServiceFlowRegistrationUI(ctx context.Context) *url.URL {\n\treturn p.ParseAbsoluteOrRelativeURIOrFail(ctx, ViperKeySelfServiceRegistrationUI)\n}\n\nfunc (p *Config) SelfServiceFlowRecoveryUI(ctx context.Context) *url.URL {\n\treturn p.ParseAbsoluteOrRelativeURIOrFail(ctx, ViperKeySelfServiceRecoveryUI)\n}\n\n// SessionLifespan returns time.Hour*24 when the value is not set.\nfunc (p *Config) SessionLifespan(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeySessionLifespan, time.Hour*24)\n}\n\nfunc (p *Config) SessionPersistentCookie(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeySessionPersistentCookie)\n}\n\nfunc (p *Config) SelfServiceBrowserAllowedReturnToDomains(ctx context.Context) (us []url.URL) {\n\tsrc := p.GetProvider(ctx).Strings(ViperKeyURLsAllowedReturnToDomains)\n\tfor k, u := range src {\n\t\tif len(u) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tparsed, err := url.ParseRequestURI(u)\n\t\tif err != nil {\n\t\t\tp.l.WithError(err).Warnf(\"Ignoring URL \\\"%s\\\" from configuration key \\\"%s.%d\\\".\", u, ViperKeyURLsAllowedReturnToDomains, k)\n\t\t\tcontinue\n\t\t}\n\t\tif parsed.Host == \"*\" {\n\t\t\tp.l.Warnf(\"Ignoring wildcard \\\"%s\\\" from configuration key \\\"%s.%d\\\".\", u, ViperKeyURLsAllowedReturnToDomains, k)\n\t\t\tcontinue\n\t\t}\n\t\teTLD, icann := publicsuffix.PublicSuffix(parsed.Host)\n\t\tif len(parsed.Host) > 0 &&\n\t\t\tparsed.Host[:1] == \"*\" &&\n\t\t\ticann &&\n\t\t\tparsed.Host == fmt.Sprintf(\"*.%s\", eTLD) {\n\t\t\tp.l.Warnf(\"Ignoring wildcard \\\"%s\\\" from configuration key \\\"%s.%d\\\".\", u, ViperKeyURLsAllowedReturnToDomains, k)\n\t\t\tcontinue\n\t\t}\n\n\t\tus = append(us, *parsed)\n\t}\n\n\treturn us\n}\n\nfunc (p *Config) SelfServiceFlowLoginRequestLifespan(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeySelfServiceLoginRequestLifespan, time.Hour)\n}\n\nfunc (p *Config) SelfServiceFlowSettingsFlowLifespan(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeySelfServiceSettingsRequestLifespan, time.Hour)\n}\n\nfunc (p *Config) SelfServiceFlowRegistrationRequestLifespan(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeySelfServiceRegistrationRequestLifespan, time.Hour)\n}\n\nfunc (p *Config) SelfServiceFlowLogoutRedirectURL(ctx context.Context) *url.URL {\n\treturn p.GetProvider(ctx).RequestURIF(ViperKeySelfServiceLogoutBrowserDefaultReturnTo, p.SelfServiceBrowserDefaultReturnTo(ctx))\n}\n\nfunc (p *Config) CourierEmailStrategy(ctx context.Context) string {\n\treturn p.GetProvider(ctx).StringF(ViperKeyCourierDeliveryStrategy, \"smtp\")\n}\n\nfunc (p *Config) CourierEmailRequestConfig(ctx context.Context) json.RawMessage {\n\tif p.CourierEmailStrategy(ctx) != \"http\" {\n\t\treturn nil\n\t}\n\n\tconfig, err := json.Marshal(p.GetProvider(ctx).Get(ViperKeyCourierHTTPRequestConfig))\n\tif err != nil {\n\t\tp.l.WithError(err).Warn(\"Unable to marshal mailer request configuration.\")\n\t\treturn nil\n\t}\n\n\treturn config\n}\n\nfunc (p *Config) CourierTemplatesRoot(ctx context.Context) string {\n\treturn p.GetProvider(ctx).StringF(ViperKeyCourierTemplatesPath, \"courier/builtin/templates\")\n}\n\nfunc (p *Config) CourierEmailTemplatesHelper(ctx context.Context, key string) *CourierEmailTemplate {\n\tcourierTemplate := &CourierEmailTemplate{\n\t\tBody: &CourierEmailBodyTemplate{\n\t\t\tPlainText: \"\",\n\t\t\tHTML:      \"\",\n\t\t},\n\t\tSubject: \"\",\n\t}\n\n\tif !p.GetProvider(ctx).Exists(key) {\n\t\treturn courierTemplate\n\t}\n\n\tconfig, err := json.Marshal(p.GetProvider(ctx).Get(key))\n\tif err != nil {\n\t\tp.l.WithError(err).Fatalf(\"Unable to decode values from %s.\", key)\n\t\treturn courierTemplate\n\t}\n\n\tif err := json.Unmarshal(config, courierTemplate); err != nil {\n\t\tp.l.WithError(err).Fatalf(\"Unable to encode values from %s.\", key)\n\t\treturn courierTemplate\n\t}\n\treturn courierTemplate\n}\n\nfunc (p *Config) CourierSMSTemplatesHelper(ctx context.Context, key string) *CourierSMSTemplate {\n\tcourierTemplate := &CourierSMSTemplate{\n\t\tBody: &CourierSMSTemplateBody{\n\t\t\tPlainText: \"\",\n\t\t},\n\t}\n\n\tif !p.GetProvider(ctx).Exists(key) {\n\t\treturn courierTemplate\n\t}\n\n\tconfig, err := json.Marshal(p.GetProvider(ctx).Get(key))\n\tif err != nil {\n\t\tp.l.WithError(err).Fatalf(\"Unable to decode values from %s.\", key)\n\t\treturn courierTemplate\n\t}\n\n\tif err := json.Unmarshal(config, courierTemplate); err != nil {\n\t\tp.l.WithError(err).Fatalf(\"Unable to encode values from %s.\", key)\n\t\treturn courierTemplate\n\t}\n\treturn courierTemplate\n}\n\nfunc (p *Config) CourierTemplatesVerificationInvalid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesVerificationInvalidEmail)\n}\n\nfunc (p *Config) CourierTemplatesVerificationValid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesVerificationValidEmail)\n}\n\nfunc (p *Config) CourierTemplatesRecoveryInvalid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesRecoveryInvalidEmail)\n}\n\nfunc (p *Config) CourierTemplatesRecoveryValid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesRecoveryValidEmail)\n}\n\nfunc (p *Config) CourierTemplatesRecoveryCodeInvalid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesRecoveryCodeInvalidEmail)\n}\n\nfunc (p *Config) CourierTemplatesRecoveryCodeValid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesRecoveryCodeValidEmail)\n}\n\nfunc (p *Config) CourierTemplatesVerificationCodeInvalid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesVerificationCodeInvalidEmail)\n}\n\nfunc (p *Config) CourierTemplatesVerificationCodeValid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesVerificationCodeValidEmail)\n}\n\nfunc (p *Config) CourierSMSTemplatesVerificationCodeValid(ctx context.Context) *CourierSMSTemplate {\n\treturn p.CourierSMSTemplatesHelper(ctx, ViperKeyCourierTemplatesVerificationCodeValidSMS)\n}\n\nfunc (p *Config) CourierSMSTemplatesRecoveryCodeValid(ctx context.Context) *CourierSMSTemplate {\n\treturn p.CourierSMSTemplatesHelper(ctx, ViperKeyCourierTemplatesRecoveryCodeValidSMS)\n}\n\nfunc (p *Config) CourierSMSTemplatesLoginCodeValid(ctx context.Context) *CourierSMSTemplate {\n\treturn p.CourierSMSTemplatesHelper(ctx, ViperKeyCourierTemplatesLoginCodeValidSMS)\n}\n\nfunc (p *Config) CourierSMSTemplatesRegistrationCodeValid(ctx context.Context) *CourierSMSTemplate {\n\treturn p.CourierSMSTemplatesHelper(ctx, ViperKeyCourierTemplatesRegistrationCodeValidSMS)\n}\n\nfunc (p *Config) CourierTemplatesLoginCodeValid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesLoginCodeValidEmail)\n}\n\nfunc (p *Config) CourierTemplatesRegistrationCodeValid(ctx context.Context) *CourierEmailTemplate {\n\treturn p.CourierEmailTemplatesHelper(ctx, ViperKeyCourierTemplatesRegistrationCodeValidEmail)\n}\n\nfunc (p *Config) CourierMessageRetries(ctx context.Context) int {\n\treturn p.GetProvider(ctx).IntF(ViperKeyCourierMessageRetries, 5)\n}\n\nfunc (p *Config) CourierWorkerPullCount(ctx context.Context) int {\n\treturn p.GetProvider(ctx).Int(ViperKeyCourierWorkerPullCount)\n}\n\nfunc (p *Config) CourierWorkerPullWait(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).Duration(ViperKeyCourierWorkerPullWait)\n}\n\nfunc (p *Config) CourierSMTPHeaders(ctx context.Context) map[string]string {\n\treturn p.GetProvider(ctx).StringMap(ViperKeyCourierSMTPHeaders)\n}\n\nfunc (p *Config) CourierChannels(ctx context.Context) (ccs []*CourierChannel, _ error) {\n\tif err := p.GetProvider(ctx).Unmarshal(ViperKeyCourierChannels, &ccs); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\t// load legacy configs\n\tchannel := CourierChannel{\n\t\tID:   \"email\",\n\t\tType: p.CourierEmailStrategy(ctx),\n\t}\n\tif channel.Type == \"smtp\" {\n\t\tif err := p.GetProvider(ctx).Unmarshal(ViperKeyCourierSMTP, &channel.SMTPConfig); err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t} else {\n\t\tif err := p.GetProvider(ctx).Unmarshal(ViperKeyCourierHTTPRequestConfig, &channel.RequestConfig); err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t}\n\tccs = append(ccs, &channel)\n\treturn ccs, nil\n}\n\nfunc splitUrlAndFragment(s string) (string, string) {\n\ti := strings.IndexByte(s, '#')\n\tif i < 0 {\n\t\treturn s, \"\"\n\t}\n\treturn s[:i], s[i+1:]\n}\n\nfunc (p *Config) ParseAbsoluteOrRelativeURIOrFail(ctx context.Context, key string) *url.URL {\n\tparsed, err := p.ParseAbsoluteOrRelativeURI(p.GetProvider(ctx).String(key))\n\tif err != nil {\n\t\tp.l.WithError(errors.WithStack(err)).\n\t\t\tFatalf(\"Configuration value from key %s is not a valid URL: %s\", key, p.GetProvider(ctx).String(key))\n\t}\n\treturn parsed\n}\n\nfunc (p *Config) ParseURIOrFail(ctx context.Context, key string) *url.URL {\n\tparsed, err := p.ParseURI(p.GetProvider(ctx).String(key))\n\tif err != nil {\n\t\tp.l.WithField(\"reason\", \"expected scheme to be set\").\n\t\t\tFatalf(\"Configuration value from key %s is not a valid URL: %s\", key, p.GetProvider(ctx).String(key))\n\t}\n\treturn parsed\n}\n\nfunc (p *Config) ParseAbsoluteOrRelativeURI(rawUrl string) (*url.URL, error) {\n\tu, frag := splitUrlAndFragment(rawUrl)\n\tparsed, err := url.ParseRequestURI(u)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"configuration value not a valid URL: %s\", rawUrl)\n\t}\n\n\tif frag != \"\" {\n\t\tparsed.Fragment = frag\n\t}\n\n\treturn parsed, nil\n}\n\nfunc (p *Config) ParseURI(rawUrl string) (*url.URL, error) {\n\tparsed, err := p.ParseAbsoluteOrRelativeURI(rawUrl)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif parsed.Scheme == \"\" {\n\t\treturn nil, errors.Errorf(\"configuration value is not a valid URL: %s\", rawUrl)\n\t}\n\treturn parsed, nil\n}\n\nfunc (p *Config) Tracing(ctx context.Context) *otelx.Config {\n\treturn p.GetProvider(ctx).TracingConfig(\"Ory Kratos\")\n}\n\nfunc (p *Config) IsInsecureDevMode(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(\"dev\")\n}\n\nfunc (p *Config) IsBackgroundCourierEnabled(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(\"watch-courier\")\n}\n\nfunc (p *Config) CourierExposeMetricsPort(ctx context.Context) int {\n\treturn p.GetProvider(ctx).Int(\"expose-metrics-port\")\n}\n\nfunc (p *Config) SelfServiceFlowVerificationUI(ctx context.Context) *url.URL {\n\treturn p.ParseAbsoluteOrRelativeURIOrFail(ctx, ViperKeySelfServiceVerificationUI)\n}\n\nfunc (p *Config) SelfServiceFlowVerificationRequestLifespan(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeySelfServiceVerificationRequestLifespan, time.Hour)\n}\n\nfunc (p *Config) SelfServiceFlowVerificationReturnTo(ctx context.Context, defaultReturnTo *url.URL) *url.URL {\n\treturn p.GetProvider(ctx).RequestURIF(ViperKeySelfServiceVerificationBrowserDefaultReturnTo, defaultReturnTo)\n}\n\nfunc (p *Config) SelfServiceFlowVerificationAfterHooks(ctx context.Context, strategy string) []SelfServiceHook {\n\treturn p.selfServiceHooks(ctx, HookStrategyKey(ViperKeySelfServiceVerificationAfter, strategy))\n}\n\nfunc (p *Config) SelfServiceFlowRecoveryReturnTo(ctx context.Context, defaultReturnTo *url.URL) *url.URL {\n\treturn p.GetProvider(ctx).RequestURIF(ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, defaultReturnTo)\n}\n\nfunc (p *Config) SelfServiceFlowRecoveryRequestLifespan(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeySelfServiceRecoveryRequestLifespan, time.Hour)\n}\n\nfunc (p *Config) SelfServiceFlowRecoveryNotifyUnknownRecipients(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).BoolF(ViperKeySelfServiceRecoveryNotifyUnknownRecipients, false)\n}\n\nfunc (p *Config) SelfServiceLinkMethodLifespan(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeyLinkLifespan, time.Hour)\n}\n\nfunc (p *Config) SelfServiceLinkMethodBaseURL(ctx context.Context) *url.URL {\n\treturn cmp.Or(x.BaseURLFromContext(ctx), p.SelfPublicURL(ctx))\n}\n\nfunc (p *Config) SelfServiceCodeMethodLifespan(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeyCodeLifespan, time.Hour)\n}\n\nfunc (p *Config) SelfServiceCodeMethodMaxSubmissions(ctx context.Context) int {\n\treturn p.GetProvider(ctx).IntF(ViperKeyCodeMaxSubmissions, 5)\n}\n\nfunc (p *Config) SelfServiceCodeMethodMissingCredentialFallbackEnabled(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeyCodeConfigMissingCredentialFallbackEnabled)\n}\n\nfunc (p *Config) DatabaseCleanupSleepTables(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).Duration(ViperKeyDatabaseCleanupSleepTables)\n}\n\nfunc (p *Config) DatabaseCleanupBatchSize(ctx context.Context) int {\n\treturn p.GetProvider(ctx).Int(ViperKeyDatabaseCleanupBatchSize)\n}\n\nfunc (p *Config) SelfServiceFlowRecoveryAfterHooks(ctx context.Context, strategy string) []SelfServiceHook {\n\treturn p.selfServiceHooks(ctx, HookStrategyKey(ViperKeySelfServiceRecoveryAfter, strategy))\n}\n\nfunc (p *Config) SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, time.Hour)\n}\n\nfunc (p *Config) SessionSameSiteMode(ctx context.Context) http.SameSite {\n\tif !p.GetProvider(ctx).Exists(ViperKeySessionSameSite) {\n\t\treturn p.CookieSameSiteMode(ctx)\n\t}\n\n\tswitch p.GetProvider(ctx).StringF(ViperKeySessionSameSite, \"Lax\") {\n\tcase \"Lax\":\n\t\treturn http.SameSiteLaxMode\n\tcase \"Strict\":\n\t\treturn http.SameSiteStrictMode\n\tcase \"None\":\n\t\treturn http.SameSiteNoneMode\n\t}\n\treturn http.SameSiteDefaultMode\n}\n\nfunc (p *Config) SessionDomain(ctx context.Context) string {\n\tif !p.GetProvider(ctx).Exists(ViperKeySessionDomain) {\n\t\treturn p.CookieDomain(ctx)\n\t}\n\treturn p.GetProvider(ctx).String(ViperKeySessionDomain)\n}\n\nfunc (p *Config) SessionCookieSecure(ctx context.Context) bool {\n\tif !p.GetProvider(ctx).Exists(ViperKeySessionSecure) {\n\t\treturn !p.IsInsecureDevMode(ctx)\n\t}\n\treturn p.GetProvider(ctx).Bool(ViperKeySessionSecure)\n}\n\nfunc (p *Config) CookieDomain(ctx context.Context) string {\n\treturn p.GetProvider(ctx).String(ViperKeyCookieDomain)\n}\n\nfunc (p *Config) SessionWhoAmIAAL(ctx context.Context) string {\n\treturn p.GetProvider(ctx).String(ViperKeySessionWhoAmIAAL)\n}\n\nfunc (p *Config) SessionWhoAmICaching(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeySessionWhoAmICaching)\n}\n\nfunc (p *Config) FeatureFlagFasterSessionExtend(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeyFeatureFlagFasterSessionExtend)\n}\n\nfunc (p *Config) SessionWhoAmICachingMaxAge(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeySessionWhoAmICachingMaxAge, 0)\n}\n\nfunc (p *Config) UseContinueWithTransitions(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeyUseContinueWithTransitions)\n}\n\nfunc (p *Config) ChooseRecoveryAddress(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeyChooseRecoveryAddress)\n}\n\nfunc (p *Config) SessionRefreshMinTimeLeft(ctx context.Context) time.Duration {\n\treturn p.GetProvider(ctx).DurationF(ViperKeySessionRefreshMinTimeLeft, p.SessionLifespan(ctx))\n}\n\nfunc (p *Config) SelfServiceSettingsRequiredAAL(ctx context.Context) string {\n\treturn p.GetProvider(ctx).String(ViperKeySelfServiceSettingsRequiredAAL)\n}\n\nfunc (p *Config) CookieSameSiteMode(ctx context.Context) http.SameSite {\n\tswitch p.GetProvider(ctx).StringF(ViperKeyCookieSameSite, \"Lax\") {\n\tcase \"Lax\":\n\t\treturn http.SameSiteLaxMode\n\tcase \"Strict\":\n\t\treturn http.SameSiteStrictMode\n\tcase \"None\":\n\t\treturn http.SameSiteNoneMode\n\t}\n\treturn http.SameSiteDefaultMode\n}\n\nfunc (p *Config) SessionPath(ctx context.Context) string {\n\tif !p.GetProvider(ctx).Exists(ViperKeySessionPath) {\n\t\treturn p.CookiePath(ctx)\n\t}\n\treturn p.GetProvider(ctx).String(ViperKeySessionPath)\n}\n\nfunc (p *Config) CookiePath(ctx context.Context) string {\n\treturn p.GetProvider(ctx).String(ViperKeyCookiePath)\n}\n\nfunc (p *Config) CookieSecure(ctx context.Context) bool {\n\tif !p.GetProvider(ctx).Exists(ViperKeyCookieSecure) {\n\t\treturn !p.IsInsecureDevMode(ctx)\n\t}\n\treturn p.GetProvider(ctx).Bool(ViperKeyCookieSecure)\n}\n\nfunc (p *Config) SelfServiceFlowLoginReturnTo(ctx context.Context, strategy string) *url.URL {\n\treturn p.selfServiceReturnTo(ctx, ViperKeySelfServiceLoginAfter, strategy)\n}\n\nfunc (p *Config) SelfServiceFlowRegistrationReturnTo(ctx context.Context, strategy string) *url.URL {\n\treturn p.selfServiceReturnTo(ctx, ViperKeySelfServiceRegistrationAfter, strategy)\n}\n\nfunc (p *Config) SelfServiceFlowSettingsReturnTo(ctx context.Context, strategy string, defaultReturnTo *url.URL) *url.URL {\n\treturn p.GetProvider(ctx).RequestURIF(\n\t\tViperKeySelfServiceSettingsAfter+\".\"+strategy+\".\"+DefaultBrowserReturnURL,\n\t\tp.GetProvider(ctx).RequestURIF(ViperKeySelfServiceSettingsAfter+\".\"+DefaultBrowserReturnURL,\n\t\t\tdefaultReturnTo,\n\t\t),\n\t)\n}\n\nfunc (p *Config) selfServiceReturnTo(ctx context.Context, key string, strategy string) *url.URL {\n\treturn p.GetProvider(ctx).RequestURIF(\n\t\tkey+\".\"+strategy+\".\"+DefaultBrowserReturnURL,\n\t\tp.GetProvider(ctx).RequestURIF(key+\".\"+DefaultBrowserReturnURL,\n\t\t\tp.SelfServiceBrowserDefaultReturnTo(ctx),\n\t\t),\n\t)\n}\n\nfunc (p *Config) ConfigVersion(ctx context.Context) string {\n\treturn p.GetProvider(ctx).StringF(ViperKeyVersion, UnknownVersion)\n}\n\nfunc (p *Config) PasswordPolicyConfig(ctx context.Context) *PasswordPolicy {\n\treturn &PasswordPolicy{\n\t\tHaveIBeenPwnedHost:               p.GetProvider(ctx).StringF(ViperKeyPasswordHaveIBeenPwnedHost, \"api.pwnedpasswords.com\"),\n\t\tHaveIBeenPwnedEnabled:            p.GetProvider(ctx).BoolF(ViperKeyPasswordHaveIBeenPwnedEnabled, true),\n\t\tMaxBreaches:                      uint(p.GetProvider(ctx).Int(ViperKeyPasswordMaxBreaches)), // #nosec G115 -- negative values are prevented by the schema validation\n\t\tIgnoreNetworkErrors:              p.GetProvider(ctx).BoolF(ViperKeyIgnoreNetworkErrors, true),\n\t\tMinPasswordLength:                uint(p.GetProvider(ctx).IntF(ViperKeyPasswordMinLength, 8)), // #nosec G115 -- negative values are prevented by the schema validation\n\t\tIdentifierSimilarityCheckEnabled: p.GetProvider(ctx).BoolF(ViperKeyPasswordIdentifierSimilarityCheckEnabled, true),\n\t}\n}\n\nfunc (p *Config) WebAuthnForPasswordless(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).BoolF(ViperKeyWebAuthnPasswordless, false)\n}\n\nfunc (p *Config) WebAuthnConfig(ctx context.Context) *webauthn.Config {\n\tscheme := p.SelfPublicURL(ctx).Scheme\n\tid := p.GetProvider(ctx).String(ViperKeyWebAuthnRPID)\n\torigin := p.GetProvider(ctx).String(ViperKeyWebAuthnRPOrigin)\n\torigins := p.GetProvider(ctx).StringsF(ViperKeyWebAuthnRPOrigins, []string{cmp.Or(origin, scheme+\"://\"+id)})\n\treturn &webauthn.Config{\n\t\tRPDisplayName: p.GetProvider(ctx).String(ViperKeyWebAuthnRPDisplayName),\n\t\tRPID:          id,\n\t\tRPOrigins:     origins,\n\t\tAuthenticatorSelection: protocol.AuthenticatorSelection{\n\t\t\tUserVerification: protocol.VerificationDiscouraged,\n\t\t},\n\t\tEncodeUserIDAsString: false,\n\t}\n}\n\nfunc (p *Config) PasskeyConfig(ctx context.Context) *webauthn.Config {\n\tscheme := p.SelfPublicURL(ctx).Scheme\n\tid := p.GetProvider(ctx).String(ViperKeyPasskeyRPID)\n\torigins := p.GetProvider(ctx).StringsF(ViperKeyPasskeyRPOrigins, []string{scheme + \"://\" + id})\n\treturn &webauthn.Config{\n\t\tRPDisplayName: p.GetProvider(ctx).String(ViperKeyPasskeyRPDisplayName),\n\t\tRPID:          id,\n\t\tRPOrigins:     origins,\n\t\tAuthenticatorSelection: protocol.AuthenticatorSelection{\n\t\t\tAuthenticatorAttachment: \"platform\",\n\t\t\tRequireResidentKey:      new(true),\n\t\t\tResidentKey:             protocol.ResidentKeyRequirementRequired,\n\t\t\tUserVerification:        protocol.VerificationPreferred,\n\t\t},\n\t\tEncodeUserIDAsString: false,\n\t}\n}\n\nfunc (p *Config) HasherPasswordHashingAlgorithm(ctx context.Context) string {\n\tconfigValue := p.GetProvider(ctx).StringF(ViperKeyHasherAlgorithm, DefaultPasswordHashingAlgorithm)\n\tswitch configValue {\n\tcase \"bcrypt\":\n\t\treturn configValue\n\tcase \"argon2\":\n\t\tfallthrough\n\tdefault:\n\t\treturn configValue\n\t}\n}\n\nfunc (p *Config) CipherAlgorithm(ctx context.Context) string {\n\tconfigValue := p.GetProvider(ctx).StringF(ViperKeyCipherAlgorithm, DefaultCipherAlgorithm)\n\tswitch configValue {\n\tcase \"noop\":\n\t\treturn configValue\n\tcase \"xchacha20-poly1305\":\n\t\treturn configValue\n\tcase \"aes\":\n\t\tfallthrough\n\tdefault:\n\t\treturn configValue\n\t}\n}\n\nfunc (p *Config) GetProvider(ctx context.Context) *configx.Provider {\n\treturn p.c.Config(ctx, p.p)\n}\n\ntype SessionTokenizeFormat struct {\n\tTTL             time.Duration `koanf:\"ttl\" json:\"ttl\"`\n\tClaimsMapperURL string        `koanf:\"claims_mapper_url\" json:\"claims_mapper_url\"`\n\tJWKSURL         string        `koanf:\"jwks_url\" json:\"jwks_url\"`\n\tSubjectSource   string        `koanf:\"subject_source\" json:\"subject_source\"`\n}\n\nfunc (p *Config) TokenizeTemplate(ctx context.Context, key string) (_ *SessionTokenizeFormat, err error) {\n\tvar result SessionTokenizeFormat\n\tpath := ViperKeySessionTokenizerTemplates + \".\" + key\n\tif !p.GetProvider(ctx).Exists(path) {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to find tokenizer template \\\"%s\\\".\", key))\n\t}\n\n\tif err := p.GetProvider(ctx).Unmarshal(path, &result); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"Unable to decode tokenizer template \\\"%s\\\": %s\", key, err))\n\t}\n\n\treturn &result, nil\n}\n\nfunc (p *Config) DefaultConsistencyLevel(ctx context.Context) crdbx.ConsistencyLevel {\n\treturn crdbx.ConsistencyLevelFromString(p.GetProvider(ctx).String(ViperKeyPreviewDefaultReadConsistencyLevel))\n}\n\nfunc (p *Config) PasswordMigrationHook(ctx context.Context) *PasswordMigrationHook {\n\thook := &PasswordMigrationHook{\n\t\tEnabled: p.GetProvider(ctx).BoolF(ViperKeyPasswordMigrationHook+\".enabled\", false),\n\t}\n\tif !hook.Enabled {\n\t\treturn hook\n\t}\n\n\t_ = p.GetProvider(ctx).Unmarshal(ViperKeyPasswordMigrationHook+\".config\", &hook.Config)\n\n\treturn hook\n}\n\nfunc (p *Config) SelfServiceLoginFlowIdentifierFirstEnabled(ctx context.Context) bool {\n\tswitch p.GetProvider(ctx).String(ViperKeySelfServiceLoginFlowStyle) {\n\tcase \"identifier_first\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (p *Config) SecurityAccountEnumerationMitigate(ctx context.Context) bool {\n\treturn p.GetProvider(ctx).Bool(ViperKeySecurityAccountEnumerationMitigate)\n}\n"
  },
  {
    "path": "driver/config/config_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage config_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/x/contextx\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/randx\"\n\n\t\"github.com/ory/x/snapshotx\"\n\n\t\"github.com/ghodss/yaml\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/sirupsen/logrus/hooks/test\"\n\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/urlx\"\n\n\t_ \"github.com/ory/jsonschema/v3/fileloader\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestViperProvider(t *testing.T) {\n\tt.Parallel()\n\tctx, cancel := context.WithCancel(context.Background())\n\tt.Cleanup(cancel)\n\n\tt.Run(\"suite=loaders\", func(t *testing.T) {\n\t\tp := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.WithConfigFiles(\"stub/.kratos.yaml\"), configx.WithContext(ctx))\n\n\t\tt.Run(\"group=client config\", func(t *testing.T) {\n\t\t\tassert.False(t, p.ClientHTTPNoPrivateIPRanges(ctx), \"Should not have private IP ranges disabled per default\")\n\t\t\tassert.Equal(t, []string{}, p.ClientHTTPPrivateIPExceptionURLs(ctx), \"Should return the correct exceptions\")\n\n\t\t\tp.MustSet(ctx, config.ViperKeyClientHTTPNoPrivateIPRanges, true)\n\t\t\tassert.True(t, p.ClientHTTPNoPrivateIPRanges(ctx), \"Should disallow private IP ranges if set\")\n\n\t\t\tp.MustSet(ctx, config.ViperKeyClientHTTPPrivateIPExceptionURLs, []string{\"https://foobar.com/baz\"})\n\t\t\tassert.Equal(t, []string{\"https://foobar.com/baz\"}, p.ClientHTTPPrivateIPExceptionURLs(ctx), \"Should return the correct exceptions\")\n\t\t})\n\n\t\tt.Run(\"group=urls\", func(t *testing.T) {\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/login\", p.SelfServiceFlowLoginUI(ctx).String())\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/settings\", p.SelfServiceFlowSettingsUI(ctx).String())\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/register\", p.SelfServiceFlowRegistrationUI(ctx).String())\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/error\", p.SelfServiceFlowErrorURL(ctx).String())\n\n\t\t\tassert.Equal(t, \"http://admin.kratos.ory.sh\", p.SelfAdminURL(ctx).String())\n\t\t\tassert.Equal(t, \"http://public.kratos.ory.sh\", p.SelfPublicURL(ctx).String())\n\n\t\t\tvar ds []string\n\t\t\tfor _, v := range p.SelfServiceBrowserAllowedReturnToDomains(ctx) {\n\t\t\t\tds = append(ds, v.String())\n\t\t\t}\n\n\t\t\tassert.Equal(t, []string{\n\t\t\t\t\"http://return-to-1-test.ory.sh/\",\n\t\t\t\t\"http://return-to-2-test.ory.sh/\",\n\t\t\t\t\"http://*.wildcards.ory.sh\",\n\t\t\t\t\"/return-to-relative-test/\",\n\t\t\t}, ds)\n\n\t\t\tpWithFragments := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.WithValues(map[string]interface{}{\n\t\t\t\tconfig.ViperKeySelfServiceLoginUI:        \"http://test.kratos.ory.sh/#/login\",\n\t\t\t\tconfig.ViperKeySelfServiceSettingsURL:    \"http://test.kratos.ory.sh/#/settings\",\n\t\t\t\tconfig.ViperKeySelfServiceRegistrationUI: \"http://test.kratos.ory.sh/#/register\",\n\t\t\t\tconfig.ViperKeySelfServiceErrorUI:        \"http://test.kratos.ory.sh/#/error\",\n\t\t\t}), configx.SkipValidation())\n\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/#/login\", pWithFragments.SelfServiceFlowLoginUI(ctx).String())\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/#/settings\", pWithFragments.SelfServiceFlowSettingsUI(ctx).String())\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/#/register\", pWithFragments.SelfServiceFlowRegistrationUI(ctx).String())\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/#/error\", pWithFragments.SelfServiceFlowErrorURL(ctx).String())\n\n\t\t\tpWithRelativeFragments := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.WithValues(map[string]interface{}{\n\t\t\t\tconfig.ViperKeySelfServiceLoginUI:        \"/login\",\n\t\t\t\tconfig.ViperKeySelfServiceSettingsURL:    \"/settings\",\n\t\t\t\tconfig.ViperKeySelfServiceRegistrationUI: \"/register\",\n\t\t\t\tconfig.ViperKeySelfServiceErrorUI:        \"/error\",\n\t\t\t}), configx.SkipValidation())\n\n\t\t\tassert.Equal(t, \"/login\", pWithRelativeFragments.SelfServiceFlowLoginUI(ctx).String())\n\t\t\tassert.Equal(t, \"/settings\", pWithRelativeFragments.SelfServiceFlowSettingsUI(ctx).String())\n\t\t\tassert.Equal(t, \"/register\", pWithRelativeFragments.SelfServiceFlowRegistrationUI(ctx).String())\n\t\t\tassert.Equal(t, \"/error\", pWithRelativeFragments.SelfServiceFlowErrorURL(ctx).String())\n\n\t\t\tfor _, v := range []string{\n\t\t\t\t\"#/login\",\n\t\t\t\t\"test.kratos.ory.sh/login\",\n\t\t\t} {\n\t\t\t\tlogger := logrusx.New(\"\", \"\")\n\t\t\t\tlogger.Logger.ExitFunc = func(code int) { panic(\"\") }\n\t\t\t\thook := new(test.Hook)\n\t\t\t\tlogger.Logger.Hooks.Add(hook)\n\n\t\t\t\tpWithIncorrectUrls := config.MustNew(t, logger, &contextx.Default{}, configx.WithValues(map[string]interface{}{\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginUI: v,\n\t\t\t\t}), configx.SkipValidation())\n\n\t\t\t\tassert.Panics(t, func() { pWithIncorrectUrls.SelfServiceFlowLoginUI(ctx) })\n\n\t\t\t\tassert.Equal(t, logrus.FatalLevel, hook.LastEntry().Level)\n\t\t\t\tassert.Equal(t, \"Configuration value from key selfservice.flows.login.ui_url is not a valid URL: \"+v, hook.LastEntry().Message)\n\t\t\t\tassert.Equal(t, 1, len(hook.Entries))\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"group=default_return_to\", func(t *testing.T) {\n\t\t\tassert.Equal(t, \"https://self-service/login/password/return_to\", p.SelfServiceFlowLoginReturnTo(ctx, \"password\").String())\n\t\t\tassert.Equal(t, \"https://self-service/login/return_to\", p.SelfServiceFlowLoginReturnTo(ctx, \"oidc\").String())\n\n\t\t\tassert.Equal(t, \"https://self-service/registration/return_to\", p.SelfServiceFlowRegistrationReturnTo(ctx, \"password\").String())\n\t\t\tassert.Equal(t, \"https://self-service/registration/oidc/return_to\", p.SelfServiceFlowRegistrationReturnTo(ctx, \"oidc\").String())\n\n\t\t\tassert.Equal(t, \"https://self-service/settings/password/return_to\", p.SelfServiceFlowSettingsReturnTo(ctx, \"password\", p.SelfServiceBrowserDefaultReturnTo(ctx)).String())\n\t\t\tassert.Equal(t, \"https://self-service/settings/return_to\", p.SelfServiceFlowSettingsReturnTo(ctx, \"profile\", p.SelfServiceBrowserDefaultReturnTo(ctx)).String())\n\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh:4000/\", p.SelfServiceFlowLogoutRedirectURL(ctx).String())\n\t\t\tp.MustSet(ctx, config.ViperKeySelfServiceLogoutBrowserDefaultReturnTo, \"\")\n\t\t\tassert.Equal(t, \"http://return-to-3-test.ory.sh/\", p.SelfServiceFlowLogoutRedirectURL(ctx).String())\n\t\t})\n\n\t\tt.Run(\"group=identity\", func(t *testing.T) {\n\t\t\tc := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.WithConfigFiles(\"stub/.kratos.mock.identities.yaml\"), configx.SkipValidation())\n\n\t\t\tds, err := c.DefaultIdentityTraitsSchemaURL(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/default-identity.schema.json\", ds.String())\n\n\t\t\tss, err := c.IdentityTraitsSchemas(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, 2, len(ss))\n\t\t\tassert.Contains(t, ss, config.Schema{\n\t\t\t\tID:  \"default\",\n\t\t\t\tURL: \"http://test.kratos.ory.sh/default-identity.schema.json\",\n\t\t\t})\n\t\t\tassert.Contains(t, ss, config.Schema{\n\t\t\t\tID:  \"other\",\n\t\t\t\tURL: \"http://test.kratos.ory.sh/other-identity.schema.json\",\n\t\t\t})\n\n\t\t\tds, err = c.IdentityTraitsSchemaURL(ctx, \"other\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/other-identity.schema.json\", ds.String())\n\n\t\t\tds, err = c.IdentityTraitsSchemaURL(ctx, \"default\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/default-identity.schema.json\", ds.String())\n\n\t\t\tds, err = c.IdentityTraitsSchemaURL(ctx, \"\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/default-identity.schema.json\", ds.String())\n\n\t\t\tds, err = c.IdentityTraitsSchemaURL(ctx, \"does-not-exist\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/default-identity.schema.json\", ds.String())\n\t\t})\n\n\t\tt.Run(\"group=serve\", func(t *testing.T) {\n\t\t\tadmin := p.ServeAdmin(ctx)\n\t\t\tassert.Equal(t, \"admin.kratos.ory.sh\", admin.Host)\n\t\t\tassert.Equal(t, 1234, admin.Port)\n\n\t\t\tpublic := p.ServePublic(ctx)\n\t\t\tassert.Equal(t, \"public.kratos.ory.sh\", public.Host)\n\t\t\tassert.Equal(t, 1235, public.Port)\n\t\t})\n\n\t\tt.Run(\"group=dsn\", func(t *testing.T) {\n\t\t\tassert.Equal(t, \"sqlite://foo.db?mode=memory&_fk=true\", p.DSN(ctx))\n\t\t})\n\n\t\tt.Run(\"group=secrets\", func(t *testing.T) {\n\t\t\tassert.Equal(t, [][]byte{\n\t\t\t\t[]byte(\"session-key-7f8a9b77-1\"),\n\t\t\t\t[]byte(\"session-key-7f8a9b77-2\"),\n\t\t\t}, p.SecretsSession(ctx))\n\t\t\tvar cipherExpected [32]byte\n\t\t\tfor k, v := range []byte(\"secret-thirty-two-character-long\") {\n\t\t\t\tcipherExpected[k] = v\n\t\t\t}\n\t\t\tassert.Equal(t, [][32]byte{\n\t\t\t\tcipherExpected,\n\t\t\t}, p.SecretsCipher(ctx))\n\t\t})\n\n\t\tt.Run(\"group=methods\", func(t *testing.T) {\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tid      string\n\t\t\t\tconfig  string\n\t\t\t\tenabled bool\n\t\t\t}{\n\t\t\t\t{id: \"password\", enabled: true, config: `{\"haveibeenpwned_host\":\"api.pwnedpasswords.com\",\"haveibeenpwned_enabled\":true,\"ignore_network_errors\":true,\"max_breaches\":0,\"migrate_hook\":{\"config\":{\"emit_analytics_event\":true,\"method\":\"POST\"},\"enabled\":false},\"min_password_length\":8,\"identifier_similarity_check_enabled\":true}`},\n\t\t\t\t{id: \"oidc\", enabled: true, config: `{\"providers\":[{\"client_id\":\"a\",\"client_secret\":\"b\",\"id\":\"github\",\"provider\":\"github\",\"mapper_url\":\"http://test.kratos.ory.sh/default-identity.schema.json\"}]}`},\n\t\t\t\t{id: \"totp\", enabled: true, config: `{\"issuer\":\"issuer.ory.sh\"}`},\n\t\t\t} {\n\t\t\t\tstrategy := p.SelfServiceStrategy(ctx, tc.id)\n\t\t\t\tassert.Equal(t, tc.enabled, strategy.Enabled)\n\t\t\t\tassert.JSONEq(t, tc.config, string(strategy.Config))\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"method=registration\", func(t *testing.T) {\n\t\t\tassert.Equal(t, true, p.SelfServiceFlowRegistrationEnabled(ctx))\n\t\t\tassert.Equal(t, time.Minute*98, p.SelfServiceFlowRegistrationRequestLifespan(ctx))\n\n\t\t\tt.Run(\"hook=before\", func(t *testing.T) {\n\t\t\t\texpHooks := []config.SelfServiceHook{\n\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"GET\",\"url\":\"https://test.kratos.ory.sh/before_registration_hook\"}`)},\n\t\t\t\t\t{Name: \"two_step_registration\", Config: json.RawMessage(`{}`)},\n\t\t\t\t}\n\n\t\t\t\thooks := p.SelfServiceFlowRegistrationBeforeHooks(ctx)\n\n\t\t\t\tassert.Equal(t, expHooks, hooks)\n\t\t\t\t// assert.EqualValues(t, \"redirect\", hook.Name)\n\t\t\t\t// assert.JSONEq(t, `{\"allow_user_defined_redirect\":false,\"default_redirect_url\":\"http://test.kratos.ory.sh:4000/\"}`, string(hook.Config))\n\t\t\t})\n\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tstrategy string\n\t\t\t\thooks    []config.SelfServiceHook\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tstrategy: \"password\",\n\t\t\t\t\thooks: []config.SelfServiceHook{\n\t\t\t\t\t\t{Name: \"session\", Config: json.RawMessage(`{}`)},\n\t\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"POST\",\"url\":\"https://test.kratos.ory.sh/after_registration_password_hook\"}`)},\n\t\t\t\t\t\t// {Name: \"verify\", Config: json.RawMessage(`{}`)},\n\t\t\t\t\t\t// {Name: \"redirect\", Config: json.RawMessage(`{\"allow_user_defined_redirect\":false,\"default_redirect_url\":\"http://test.kratos.ory.sh:4000/\"}`)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tstrategy: \"oidc\",\n\t\t\t\t\thooks: []config.SelfServiceHook{\n\t\t\t\t\t\t// {Name: \"verify\", Config: json.RawMessage(`{}`)},\n\t\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"GET\",\"url\":\"https://test.kratos.ory.sh/after_registration_oidc_hook\"}`)},\n\t\t\t\t\t\t{Name: \"session\", Config: json.RawMessage(`{}`)},\n\t\t\t\t\t\t// {Name: \"redirect\", Config: json.RawMessage(`{\"allow_user_defined_redirect\":false,\"default_redirect_url\":\"http://test.kratos.ory.sh:4000/\"}`)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tstrategy: config.HookGlobal,\n\t\t\t\t\thooks: []config.SelfServiceHook{\n\t\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"auth\":{\"config\":{\"in\":\"header\",\"name\":\"My-Key\",\"value\":\"My-Key-Value\"},\"type\":\"api_key\"},\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"POST\",\"url\":\"https://test.kratos.ory.sh/after_registration_global_hook\"}`)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"hook=after/strategy=\"+tc.strategy, func(t *testing.T) {\n\t\t\t\t\thooks := p.SelfServiceFlowRegistrationAfterHooks(ctx, tc.strategy)\n\t\t\t\t\tassert.Equal(t, tc.hooks, hooks)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"method=totp\", func(t *testing.T) {\n\t\t\tassert.Equal(t, \"issuer.ory.sh\", p.TOTPIssuer(ctx))\n\t\t})\n\n\t\tt.Run(\"method=login\", func(t *testing.T) {\n\t\t\tassert.Equal(t, time.Minute*99, p.SelfServiceFlowLoginRequestLifespan(ctx))\n\n\t\t\tt.Run(\"hook=before\", func(t *testing.T) {\n\t\t\t\texpHooks := []config.SelfServiceHook{\n\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"POST\",\"url\":\"https://test.kratos.ory.sh/before_login_hook\"}`)},\n\t\t\t\t}\n\n\t\t\t\thooks := p.SelfServiceFlowLoginBeforeHooks(ctx)\n\n\t\t\t\trequire.Len(t, hooks, 1)\n\t\t\t\tassert.Equal(t, expHooks, hooks)\n\t\t\t\t// assert.EqualValues(t, \"redirect\", hook.Name)\n\t\t\t\t// assert.JSONEq(t, `{\"allow_user_defined_redirect\":false,\"default_redirect_url\":\"http://test.kratos.ory.sh:4000/\"}`, string(hook.Config))\n\t\t\t})\n\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tstrategy string\n\t\t\t\thooks    []config.SelfServiceHook\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tstrategy: \"password\",\n\t\t\t\t\thooks: []config.SelfServiceHook{\n\t\t\t\t\t\t{Name: \"revoke_active_sessions\", Config: json.RawMessage(`{}`)},\n\t\t\t\t\t\t{Name: \"require_verified_address\", Config: json.RawMessage(`{}`)},\n\t\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"auth\":{\"config\":{\"password\":\"super-secret\",\"user\":\"test-user\"},\"type\":\"basic_auth\"},\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"POST\",\"url\":\"https://test.kratos.ory.sh/after_login_password_hook\"}`)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tstrategy: \"oidc\",\n\t\t\t\t\thooks: []config.SelfServiceHook{\n\t\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"GET\",\"url\":\"https://test.kratos.ory.sh/after_login_oidc_hook\"}`)},\n\t\t\t\t\t\t{Name: \"revoke_active_sessions\", Config: json.RawMessage(`{}`)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tstrategy: config.HookGlobal,\n\t\t\t\t\thooks: []config.SelfServiceHook{\n\t\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"POST\",\"url\":\"https://test.kratos.ory.sh/after_login_global_hook\"}`)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"hook=after/strategy=\"+tc.strategy, func(t *testing.T) {\n\t\t\t\t\thooks := p.SelfServiceFlowLoginAfterHooks(ctx, tc.strategy)\n\t\t\t\t\tassert.Equal(t, tc.hooks, hooks)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"method=settings\", func(t *testing.T) {\n\t\t\tassert.Equal(t, time.Minute*99, p.SelfServiceFlowSettingsFlowLifespan(ctx))\n\t\t\tassert.Equal(t, time.Minute*5, p.SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx))\n\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tstrategy string\n\t\t\t\thooks    []config.SelfServiceHook\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tstrategy: \"password\",\n\t\t\t\t\thooks: []config.SelfServiceHook{\n\t\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"POST\",\"url\":\"https://test.kratos.ory.sh/after_settings_password_hook\"}`)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tstrategy: \"profile\",\n\t\t\t\t\thooks: []config.SelfServiceHook{\n\t\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"POST\",\"url\":\"https://test.kratos.ory.sh/after_settings_profile_hook\"}`)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tstrategy: config.HookGlobal,\n\t\t\t\t\thooks: []config.SelfServiceHook{\n\t\t\t\t\t\t{Name: \"web_hook\", Config: json.RawMessage(`{\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"POST\",\"url\":\"https://test.kratos.ory.sh/after_settings_global_hook\"}`)},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"hook=after/strategy=\"+tc.strategy, func(t *testing.T) {\n\t\t\t\t\thooks := p.SelfServiceFlowSettingsAfterHooks(ctx, tc.strategy)\n\t\t\t\t\tassert.Equal(t, tc.hooks, hooks)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"method=recovery\", func(t *testing.T) {\n\t\t\tassert.Equal(t, true, p.SelfServiceFlowRecoveryEnabled(ctx))\n\t\t\tassert.Equal(t, time.Minute*98, p.SelfServiceFlowRecoveryRequestLifespan(ctx))\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/recovery\", p.SelfServiceFlowRecoveryUI(ctx).String())\n\n\t\t\thooks := p.SelfServiceFlowRecoveryAfterHooks(ctx, config.HookGlobal)\n\t\t\tassert.Equal(t, []config.SelfServiceHook{{Name: \"web_hook\", Config: json.RawMessage(`{\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"GET\",\"url\":\"https://test.kratos.ory.sh/after_recovery_hook\"}`)}}, hooks)\n\t\t})\n\n\t\tt.Run(\"method=verification\", func(t *testing.T) {\n\t\t\tassert.Equal(t, time.Minute*97, p.SelfServiceFlowVerificationRequestLifespan(ctx))\n\t\t\tassert.Equal(t, \"http://test.kratos.ory.sh/verification\", p.SelfServiceFlowVerificationUI(ctx).String())\n\n\t\t\thooks := p.SelfServiceFlowVerificationAfterHooks(ctx, config.HookGlobal)\n\t\t\tassert.Equal(t, []config.SelfServiceHook{{Name: \"web_hook\", Config: json.RawMessage(`{\"body\":\"/path/to/template.jsonnet\",\"headers\":{\"X-Custom-Header\":\"test\"},\"method\":\"GET\",\"url\":\"https://test.kratos.ory.sh/after_verification_hook\"}`)}}, hooks)\n\t\t})\n\n\t\tt.Run(\"group=hashers\", func(t *testing.T) {\n\t\t\tc := p.HasherArgon2(ctx)\n\t\t\tassert.Equal(t, &config.Argon2{\n\t\t\t\tMemory: 1048576, Iterations: 2, Parallelism: 4,\n\t\t\t\tSaltLength: 16, KeyLength: 32, DedicatedMemory: config.Argon2DefaultDedicatedMemory, ExpectedDeviation: config.Argon2DefaultDeviation, ExpectedDuration: config.Argon2DefaultDuration,\n\t\t\t}, c)\n\t\t})\n\n\t\tt.Run(\"group=set_provider_by_json\", func(t *testing.T) {\n\t\t\tproviderConfigJSON := `{\"providers\": [{\"id\":\"github-test\",\"provider\":\"github\",\"client_id\":\"set_json_test\",\"client_secret\":\"secret\",\"mapper_url\":\"http://mapper-url\",\"scope\":[\"user:email\"]}]}`\n\t\t\tstrategyConfigJSON := fmt.Sprintf(`{\"enabled\":true, \"config\": %s}`, providerConfigJSON)\n\n\t\t\tp.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".oidc\", strategyConfigJSON)\n\t\t\tstrategy := p.SelfServiceStrategy(ctx, \"oidc\")\n\t\t\tassert.JSONEq(t, providerConfigJSON, string(strategy.Config))\n\t\t})\n\t})\n}\n\nfunc TestBcrypt(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tp := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation())\n\n\trequire.NoError(t, p.Set(ctx, config.ViperKeyHasherBcryptCost, 4))\n\trequire.NoError(t, p.Set(ctx, \"dev\", false))\n\tassert.EqualValues(t, uint32(12), p.HasherBcrypt(ctx).Cost)\n\n\trequire.NoError(t, p.Set(ctx, \"dev\", true))\n\tassert.EqualValues(t, uint32(4), p.HasherBcrypt(ctx).Cost)\n\n\trequire.NoError(t, p.Set(ctx, config.ViperKeyHasherBcryptCost, math.MaxInt64)) // too high\n\tassert.EqualValues(t, math.MaxUint32, p.HasherBcrypt(ctx).Cost)\n}\n\nfunc TestProviderBaseURLs(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tmachineHostname, err := os.Hostname()\n\tif err != nil {\n\t\tmachineHostname = \"127.0.0.1\"\n\t}\n\n\tp := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation())\n\tassert.Equal(t, \"https://\"+machineHostname+\":4433/\", p.SelfPublicURL(ctx).String())\n\tassert.Equal(t, \"https://\"+machineHostname+\":4434/\", p.SelfAdminURL(ctx).String())\n\n\tp = config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation(), configx.WithValues(map[string]interface{}{\n\t\t\"serve.public.port\": 4444,\n\t\t\"serve.admin.port\":  4445,\n\t}))\n\tassert.Equal(t, \"https://\"+machineHostname+\":4444/\", p.SelfPublicURL(ctx).String())\n\tassert.Equal(t, \"https://\"+machineHostname+\":4445/\", p.SelfAdminURL(ctx).String())\n\n\tp = config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation(), configx.WithValues(map[string]interface{}{\n\t\t\"serve.public.host\": \"public.ory.sh\",\n\t\t\"serve.admin.host\":  \"admin.ory.sh\",\n\t\t\"serve.public.port\": 4444,\n\t\t\"serve.admin.port\":  4445,\n\t}))\n\tassert.Equal(t, \"https://public.ory.sh:4444/\", p.SelfPublicURL(ctx).String())\n\tassert.Equal(t, \"https://admin.ory.sh:4445/\", p.SelfAdminURL(ctx).String())\n\n\t// Set to dev mode\n\tp = config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation(), configx.WithValues(map[string]interface{}{\n\t\t\"serve.public.host\": \"public.ory.sh\",\n\t\t\"serve.admin.host\":  \"admin.ory.sh\",\n\t\t\"serve.public.port\": 4444,\n\t\t\"serve.admin.port\":  4445,\n\t\t\"dev\":               true,\n\t}))\n\tassert.Equal(t, \"http://public.ory.sh:4444/\", p.SelfPublicURL(ctx).String())\n\tassert.Equal(t, \"http://admin.ory.sh:4445/\", p.SelfAdminURL(ctx).String())\n}\n\nfunc TestDefaultWebhookHeaderAllowlist(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tp := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation())\n\tsnapshotx.SnapshotT(t, p.WebhookHeaderAllowlist(ctx))\n}\n\nfunc TestViperProvider_Secrets(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tp := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation())\n\n\tdef := p.SecretsDefault(ctx)\n\tassert.NotEmpty(t, def)\n\tassert.Equal(t, def, p.SecretsSession(ctx))\n\tassert.Equal(t, def, p.SecretsDefault(ctx))\n\tassert.Empty(t, p.SecretsCipher(ctx))\n\terr := p.Set(ctx, config.ViperKeySecretsCipher, []string{\"short-secret-key\"})\n\trequire.NoError(t, err)\n\tassert.Equal(t, [][32]byte{}, p.SecretsCipher(ctx))\n}\n\nfunc TestViperProvider_Defaults(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tl := logrusx.New(\"\", \"\")\n\n\tfor k, tc := range []struct {\n\t\tinit   func() *config.Config\n\t\texpect func(t *testing.T, p *config.Config)\n\t}{\n\t\t{\n\t\t\tinit: func() *config.Config {\n\t\t\t\treturn config.MustNew(t, l, &contextx.Default{}, configx.SkipValidation())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tinit: func() *config.Config {\n\t\t\t\treturn config.MustNew(t, l, &contextx.Default{}, configx.WithConfigFiles(\"stub/.defaults.yml\"), configx.SkipValidation())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tinit: func() *config.Config {\n\t\t\t\treturn config.MustNew(t, l, &contextx.Default{}, configx.WithConfigFiles(\"stub/.defaults-password.yml\"), configx.SkipValidation())\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tinit: func() *config.Config {\n\t\t\t\treturn config.MustNew(t, l, &contextx.Default{}, configx.WithConfigFiles(\"../../test/e2e/profiles/recovery/.kratos.yml\"), configx.SkipValidation())\n\t\t\t},\n\t\t\texpect: func(t *testing.T, p *config.Config) {\n\t\t\t\tassert.True(t, p.SelfServiceFlowRecoveryEnabled(ctx))\n\t\t\t\tassert.False(t, p.SelfServiceFlowVerificationEnabled(ctx))\n\t\t\t\tassert.True(t, p.SelfServiceFlowRegistrationEnabled(ctx))\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"password\").Enabled)\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"profile\").Enabled)\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"link\").Enabled)\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"code\").Enabled)\n\t\t\t\tassert.False(t, p.SelfServiceCodeStrategy(ctx).PasswordlessEnabled)\n\t\t\t\tassert.False(t, p.SelfServiceStrategy(ctx, \"oidc\").Enabled)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tinit: func() *config.Config {\n\t\t\t\treturn config.MustNew(t, l, &contextx.Default{}, configx.WithConfigFiles(\"../../test/e2e/profiles/verification/.kratos.yml\"), configx.SkipValidation())\n\t\t\t},\n\t\t\texpect: func(t *testing.T, p *config.Config) {\n\t\t\t\tassert.False(t, p.SelfServiceFlowRecoveryEnabled(ctx))\n\t\t\t\tassert.True(t, p.SelfServiceFlowVerificationEnabled(ctx))\n\t\t\t\tassert.True(t, p.SelfServiceFlowRegistrationEnabled(ctx))\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"password\").Enabled)\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"profile\").Enabled)\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"link\").Enabled)\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"code\").Enabled)\n\t\t\t\tassert.False(t, p.SelfServiceCodeStrategy(ctx).PasswordlessEnabled)\n\t\t\t\tassert.False(t, p.SelfServiceStrategy(ctx, \"oidc\").Enabled)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tinit: func() *config.Config {\n\t\t\t\treturn config.MustNew(t, l, &contextx.Default{}, configx.WithConfigFiles(\"../../test/e2e/profiles/oidc/.kratos.yml\"), configx.SkipValidation())\n\t\t\t},\n\t\t\texpect: func(t *testing.T, p *config.Config) {\n\t\t\t\tassert.False(t, p.SelfServiceFlowRecoveryEnabled(ctx))\n\t\t\t\tassert.False(t, p.SelfServiceFlowVerificationEnabled(ctx))\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"password\").Enabled)\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"profile\").Enabled)\n\t\t\t\tassert.False(t, p.SelfServiceStrategy(ctx, \"link\").Enabled)\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"code\").Enabled)\n\t\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"oidc\").Enabled)\n\t\t\t\tassert.False(t, p.SelfServiceCodeStrategy(ctx).PasswordlessEnabled)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tinit: func() *config.Config {\n\t\t\t\treturn config.MustNew(t, l, &contextx.Default{}, configx.WithConfigFiles(\"stub/.kratos.notify-unknown-recipients.yml\"), configx.SkipValidation())\n\t\t\t},\n\t\t\texpect: func(t *testing.T, p *config.Config) {\n\t\t\t\tassert.True(t, p.SelfServiceFlowRecoveryNotifyUnknownRecipients(ctx))\n\t\t\t\tassert.True(t, p.SelfServiceFlowVerificationNotifyUnknownRecipients(ctx))\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\tp := tc.init()\n\n\t\t\tif tc.expect != nil {\n\t\t\t\ttc.expect(t, p)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tassert.False(t, p.SelfServiceFlowRecoveryEnabled(ctx))\n\t\t\tassert.False(t, p.SelfServiceFlowVerificationEnabled(ctx))\n\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"password\").Enabled)\n\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"profile\").Enabled)\n\t\t\tassert.False(t, p.SelfServiceStrategy(ctx, \"link\").Enabled)\n\t\t\tassert.True(t, p.SelfServiceStrategy(ctx, \"code\").Enabled)\n\t\t\tassert.False(t, p.SelfServiceStrategy(ctx, \"oidc\").Enabled)\n\t\t\tassert.False(t, p.SelfServiceCodeStrategy(ctx).PasswordlessEnabled)\n\t\t\tassert.False(t, p.SelfServiceFlowRecoveryNotifyUnknownRecipients(ctx))\n\t\t\tassert.False(t, p.SelfServiceFlowVerificationNotifyUnknownRecipients(ctx))\n\t\t})\n\t}\n\n\tt.Run(\"suite=ui_url\", func(t *testing.T) {\n\t\tp := config.MustNew(t, l, &contextx.Default{}, configx.SkipValidation())\n\t\tassert.Equal(t, \"https://www.ory.sh/kratos/docs/fallback/login\", p.SelfServiceFlowLoginUI(ctx).String())\n\t\tassert.Equal(t, \"https://www.ory.sh/kratos/docs/fallback/settings\", p.SelfServiceFlowSettingsUI(ctx).String())\n\t\tassert.Equal(t, \"https://www.ory.sh/kratos/docs/fallback/registration\", p.SelfServiceFlowRegistrationUI(ctx).String())\n\t\tassert.Equal(t, \"https://www.ory.sh/kratos/docs/fallback/recovery\", p.SelfServiceFlowRecoveryUI(ctx).String())\n\t\tassert.Equal(t, \"https://www.ory.sh/kratos/docs/fallback/verification\", p.SelfServiceFlowVerificationUI(ctx).String())\n\t})\n}\n\nfunc TestViperProvider_ReturnTo(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tl := logrusx.New(\"\", \"\")\n\tp := config.MustNew(t, l, &contextx.Default{}, configx.SkipValidation())\n\n\tp.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, \"https://www.ory.sh/\")\n\tassert.Equal(t, \"https://www.ory.sh/\", p.SelfServiceFlowVerificationReturnTo(ctx, urlx.ParseOrPanic(\"https://www.ory.sh/\")).String())\n\tassert.Equal(t, \"https://www.ory.sh/\", p.SelfServiceFlowRecoveryReturnTo(ctx, urlx.ParseOrPanic(\"https://www.ory.sh/\")).String())\n\n\tp.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, \"https://www.ory.sh/recovery\")\n\tassert.Equal(t, \"https://www.ory.sh/recovery\", p.SelfServiceFlowRecoveryReturnTo(ctx, urlx.ParseOrPanic(\"https://www.ory.sh/\")).String())\n\n\tp.MustSet(ctx, config.ViperKeySelfServiceVerificationBrowserDefaultReturnTo, \"https://www.ory.sh/verification\")\n\tassert.Equal(t, \"https://www.ory.sh/verification\", p.SelfServiceFlowVerificationReturnTo(ctx, urlx.ParseOrPanic(\"https://www.ory.sh/\")).String())\n}\n\nfunc TestSession(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tl := logrusx.New(\"\", \"\")\n\tp := config.MustNew(t, l, &contextx.Default{}, configx.SkipValidation())\n\n\tassert.Equal(t, \"ory_kratos_session\", p.SessionName(ctx))\n\tp.MustSet(ctx, config.ViperKeySessionName, \"ory_session\")\n\tassert.Equal(t, \"ory_session\", p.SessionName(ctx))\n\n\tassert.Equal(t, time.Hour*24, p.SessionRefreshMinTimeLeft(ctx))\n\tp.MustSet(ctx, config.ViperKeySessionRefreshMinTimeLeft, \"1m\")\n\tassert.Equal(t, time.Minute, p.SessionRefreshMinTimeLeft(ctx))\n\n\tassert.Equal(t, time.Hour*24, p.SessionLifespan(ctx))\n\tp.MustSet(ctx, config.ViperKeySessionLifespan, \"1m\")\n\tassert.Equal(t, time.Minute, p.SessionLifespan(ctx))\n\n\tassert.Equal(t, true, p.SessionPersistentCookie(ctx))\n\tp.MustSet(ctx, config.ViperKeySessionPersistentCookie, false)\n\tassert.Equal(t, false, p.SessionPersistentCookie(ctx))\n\n\tassert.Equal(t, false, p.SessionWhoAmICaching(ctx))\n\tp.MustSet(ctx, config.ViperKeySessionWhoAmICaching, true)\n\tassert.Equal(t, true, p.SessionWhoAmICaching(ctx))\n}\n\nfunc TestCookies(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tl := logrusx.New(\"\", \"\")\n\tp := config.MustNew(t, l, &contextx.Default{}, configx.SkipValidation())\n\n\tt.Run(\"path\", func(t *testing.T) {\n\t\tassert.Equal(t, \"/\", p.CookiePath(ctx))\n\t\tassert.Equal(t, \"/\", p.SessionPath(ctx))\n\n\t\tp.MustSet(ctx, config.ViperKeyCookiePath, \"/cookie\")\n\t\tassert.Equal(t, \"/cookie\", p.CookiePath(ctx))\n\t\tassert.Equal(t, \"/cookie\", p.SessionPath(ctx))\n\n\t\tp.MustSet(ctx, config.ViperKeySessionPath, \"/session\")\n\t\tassert.Equal(t, \"/cookie\", p.CookiePath(ctx))\n\t\tassert.Equal(t, \"/session\", p.SessionPath(ctx))\n\t})\n\n\tt.Run(\"SameSite\", func(t *testing.T) {\n\t\tassert.Equal(t, http.SameSiteLaxMode, p.CookieSameSiteMode(ctx))\n\t\tassert.Equal(t, http.SameSiteLaxMode, p.SessionSameSiteMode(ctx))\n\n\t\tp.MustSet(ctx, config.ViperKeyCookieSameSite, \"Strict\")\n\t\tassert.Equal(t, http.SameSiteStrictMode, p.CookieSameSiteMode(ctx))\n\t\tassert.Equal(t, http.SameSiteStrictMode, p.SessionSameSiteMode(ctx))\n\n\t\tp.MustSet(ctx, config.ViperKeySessionSameSite, \"None\")\n\t\tassert.Equal(t, http.SameSiteStrictMode, p.CookieSameSiteMode(ctx))\n\t\tassert.Equal(t, http.SameSiteNoneMode, p.SessionSameSiteMode(ctx))\n\t})\n\n\tt.Run(\"domain\", func(t *testing.T) {\n\t\tassert.Equal(t, \"\", p.CookieDomain(ctx))\n\t\tassert.Equal(t, \"\", p.SessionDomain(ctx))\n\n\t\tp.MustSet(ctx, config.ViperKeyCookieDomain, \"www.cookie.com\")\n\t\tassert.Equal(t, \"www.cookie.com\", p.CookieDomain(ctx))\n\t\tassert.Equal(t, \"www.cookie.com\", p.SessionDomain(ctx))\n\n\t\tp.MustSet(ctx, config.ViperKeySessionDomain, \"www.session.com\")\n\t\tassert.Equal(t, \"www.cookie.com\", p.CookieDomain(ctx))\n\t\tassert.Equal(t, \"www.session.com\", p.SessionDomain(ctx))\n\t})\n}\n\nfunc TestViperProvider_DSN(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tt.Run(\"case=dsn: memory\", func(t *testing.T) {\n\t\tp := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation())\n\t\tp.MustSet(ctx, config.ViperKeyDSN, \"memory\")\n\n\t\tassert.Equal(t, config.DefaultSQLiteMemoryDSN, p.DSN(ctx))\n\t})\n\n\tt.Run(\"case=dsn: not memory\", func(t *testing.T) {\n\t\tp := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation())\n\n\t\tdsn := \"sqlite://foo.db?_fk=true\"\n\t\tp.MustSet(ctx, config.ViperKeyDSN, dsn)\n\n\t\tassert.Equal(t, dsn, p.DSN(ctx))\n\t})\n\n\tt.Run(\"case=dsn: not set\", func(t *testing.T) {\n\t\tdsn := \"\"\n\n\t\tvar exitCode int\n\t\tl := logrusx.New(\"\", \"\", logrusx.WithExitFunc(func(i int) {\n\t\t\texitCode = i\n\t\t}))\n\t\tp := config.MustNew(t, l, &contextx.Default{}, configx.SkipValidation())\n\n\t\tassert.Equal(t, dsn, p.DSN(ctx))\n\t\tassert.NotEqual(t, 0, exitCode)\n\t})\n}\n\nfunc TestViperProvider_ParseURIOrFail(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tvar exitCode int\n\n\tl := logrusx.New(\"\", \"\", logrusx.WithExitFunc(func(i int) {\n\t\texitCode = i\n\t}))\n\tp := config.MustNew(t, l, &contextx.Default{}, configx.SkipValidation())\n\trequire.Zero(t, exitCode)\n\n\tconst testKey = \"testKeyNotUsedInTheRealSchema\"\n\n\tfor _, tc := range []struct {\n\t\tu        string\n\t\texpected url.URL\n\t}{\n\t\t{\n\t\t\tu: \"file:///etc/config/kratos/identity.schema.json\",\n\t\t\texpected: url.URL{\n\t\t\t\tScheme: \"file\",\n\t\t\t\tPath:   \"/etc/config/kratos/identity.schema.json\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tu: \"file://./identity.schema.json\",\n\t\t\texpected: url.URL{\n\t\t\t\tScheme: \"file\",\n\t\t\t\tHost:   \".\",\n\t\t\t\tPath:   \"/identity.schema.json\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tu: \"base64://bG9jYWwgc3ViamVjdCA9I\",\n\t\t\texpected: url.URL{\n\t\t\t\tScheme: \"base64\",\n\t\t\t\tHost:   \"bG9jYWwgc3ViamVjdCA9I\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tu: \"https://foo.bar/schema.json\",\n\t\t\texpected: url.URL{\n\t\t\t\tScheme: \"https\",\n\t\t\t\tHost:   \"foo.bar\",\n\t\t\t\tPath:   \"/schema.json\",\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(\"case=parse \"+tc.u, func(t *testing.T) {\n\t\t\trequire.NoError(t, p.Set(ctx, testKey, tc.u))\n\n\t\t\tu := p.ParseURIOrFail(ctx, testKey)\n\t\t\trequire.Zero(t, exitCode)\n\t\t\tassert.Equal(t, tc.expected, *u)\n\t\t})\n\t}\n}\n\nfunc TestViperProvider_HaveIBeenPwned(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tp := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation())\n\tt.Run(\"case=hipb: host\", func(t *testing.T) {\n\t\tp.MustSet(ctx, config.ViperKeyPasswordHaveIBeenPwnedHost, \"foo.bar\")\n\t\tassert.Equal(t, \"foo.bar\", p.PasswordPolicyConfig(ctx).HaveIBeenPwnedHost)\n\t})\n\n\tt.Run(\"case=hibp: enabled\", func(t *testing.T) {\n\t\tp.MustSet(ctx, config.ViperKeyPasswordHaveIBeenPwnedEnabled, true)\n\t\tassert.Equal(t, true, p.PasswordPolicyConfig(ctx).HaveIBeenPwnedEnabled)\n\t})\n\n\tt.Run(\"case=hibp: enabled\", func(t *testing.T) {\n\t\tp.MustSet(ctx, config.ViperKeyPasswordHaveIBeenPwnedEnabled, false)\n\t\tassert.Equal(t, false, p.PasswordPolicyConfig(ctx).HaveIBeenPwnedEnabled)\n\t})\n\n\tt.Run(\"case=hibp: max_breaches\", func(t *testing.T) {\n\t\tp.MustSet(ctx, config.ViperKeyPasswordMaxBreaches, 10)\n\t\tassert.Equal(t, uint(10), p.PasswordPolicyConfig(ctx).MaxBreaches)\n\t})\n\n\tt.Run(\"case=hibp: ignore_network_errors\", func(t *testing.T) {\n\t\tp.MustSet(ctx, config.ViperKeyIgnoreNetworkErrors, true)\n\t\tassert.Equal(t, true, p.PasswordPolicyConfig(ctx).IgnoreNetworkErrors)\n\t})\n\n\tt.Run(\"case=hibp: ignore_network_errors\", func(t *testing.T) {\n\t\tp.MustSet(ctx, config.ViperKeyIgnoreNetworkErrors, false)\n\t\tassert.Equal(t, false, p.PasswordPolicyConfig(ctx).IgnoreNetworkErrors)\n\t})\n}\n\nfunc newTestConfig(t *testing.T, opts ...configx.OptionModifier) (c *config.Config, l *logrusx.Logger, h *test.Hook, exited *bool) {\n\tl = logrusx.New(\"\", \"\")\n\th = new(test.Hook)\n\texited = new(bool)\n\tl.Logger.Hooks.Add(h)\n\tl.Logger.ExitFunc = func(code int) { *exited = true }\n\tc = config.MustNew(t, l, &contextx.Default{}, append([]configx.OptionModifier{configx.SkipValidation()}, opts...)...)\n\treturn\n}\n\nfunc TestLoadingTLSConfig(t *testing.T) {\n\tt.Parallel()\n\n\tcertPath, keyPath, certBase64, keyBase64 := testhelpers.GenerateTLSCertificateFilesForTests(t)\n\n\tt.Run(\"case=public: no TLS config\", func(t *testing.T) {\n\t\tp, l, hook, exited := newTestConfig(t)\n\t\tcertFunc, err := p.ServePublic(t.Context()).TLS.GetCertFunc(t.Context(), l, \"public\")\n\t\trequire.NoError(t, err)\n\t\tassert.Nil(t, certFunc)\n\t\tle := hook.LastEntry()\n\t\trequire.NotNil(t, le)\n\t\tassert.Equal(t, \"TLS has not been configured for public, skipping\", le.Message)\n\t\tassert.False(t, *exited)\n\t})\n\n\tt.Run(\"case=admin: no TLS config\", func(t *testing.T) {\n\t\tp, l, hook, exited := newTestConfig(t)\n\t\tcertFunc, err := p.ServeAdmin(t.Context()).TLS.GetCertFunc(t.Context(), l, \"admin\")\n\t\trequire.NoError(t, err)\n\t\tassert.Nil(t, certFunc)\n\t\tle := hook.LastEntry()\n\t\trequire.NotNil(t, le)\n\t\tassert.Equal(t, \"TLS has not been configured for admin, skipping\", le.Message)\n\t\tassert.False(t, *exited)\n\t})\n\n\tt.Run(\"case=public: loading inline base64 certificate\", func(t *testing.T) {\n\t\tp, l, hook, exited := newTestConfig(t, configx.WithValues(map[string]interface{}{\n\t\t\tkeyPublicTLSKeyBase64:  keyBase64,\n\t\t\tkeyPublicTLSCertBase64: certBase64,\n\t\t}))\n\t\tcertFunc, err := p.ServePublic(t.Context()).TLS.GetCertFunc(t.Context(), l, \"public\")\n\t\trequire.NoError(t, err)\n\t\tassert.NotNil(t, certFunc)\n\t\tle := hook.LastEntry()\n\t\trequire.NotNil(t, le)\n\t\tassert.Equal(t, \"Setting up HTTPS for public\", le.Message)\n\t\tassert.False(t, *exited)\n\t})\n\n\tt.Run(\"case=public: loading certificate from a file\", func(t *testing.T) {\n\t\tp, l, hook, exited := newTestConfig(t, configx.WithValues(map[string]interface{}{\n\t\t\tkeyPublicTLSKeyPath:  keyPath,\n\t\t\tkeyPublicTLSCertPath: certPath,\n\t\t}))\n\t\tcertFunc, err := p.ServePublic(t.Context()).TLS.GetCertFunc(t.Context(), l, \"public\")\n\t\trequire.NoError(t, err)\n\t\tassert.NotNil(t, certFunc)\n\t\tle := hook.LastEntry()\n\t\trequire.NotNil(t, le)\n\t\tassert.Equal(t, \"Setting up HTTPS for public (automatic certificate reloading active)\", le.Message)\n\t\tassert.False(t, *exited)\n\t})\n\n\tt.Run(\"case=public: failing to load inline base64 certificate\", func(t *testing.T) {\n\t\tp, l, _, _ := newTestConfig(t, configx.WithValues(map[string]interface{}{\n\t\t\tkeyPublicTLSKeyBase64:  \"invalid\",\n\t\t\tkeyPublicTLSCertBase64: certBase64,\n\t\t}))\n\t\tcertFunc, err := p.ServePublic(t.Context()).TLS.GetCertFunc(t.Context(), l, \"public\")\n\t\trequire.ErrorContains(t, err, \"unable to load TLS certificate for interface public\")\n\t\tassert.Nil(t, certFunc)\n\t})\n\n\tt.Run(\"case=public: failing to load certificate from a file\", func(t *testing.T) {\n\t\tp, l, _, _ := newTestConfig(t, configx.WithValues(map[string]interface{}{\n\t\t\tkeyPublicTLSKeyPath:  \"/dev/null\",\n\t\t\tkeyPublicTLSCertPath: \"/dev/null\",\n\t\t}))\n\t\tcertFunc, err := p.ServePublic(t.Context()).TLS.GetCertFunc(t.Context(), l, \"public\")\n\t\trequire.ErrorContains(t, err, \"unable to load TLS certificate for interface public\")\n\t\tassert.Nil(t, certFunc)\n\t})\n\n\tt.Run(\"case=admin: loading inline base64 certificate\", func(t *testing.T) {\n\t\tp, l, hook, exited := newTestConfig(t, configx.WithValues(map[string]interface{}{\n\t\t\tkeyAdminTLSKeyBase64:  keyBase64,\n\t\t\tkeyAdminTLSCertBase64: certBase64,\n\t\t}))\n\t\tcertFunc, err := p.ServeAdmin(t.Context()).TLS.GetCertFunc(t.Context(), l, \"admin\")\n\t\trequire.NoError(t, err)\n\t\tassert.NotNil(t, certFunc)\n\t\tle := hook.LastEntry()\n\t\trequire.NotNil(t, le)\n\t\tassert.Equal(t, \"Setting up HTTPS for admin\", le.Message)\n\t\tassert.False(t, *exited)\n\t})\n\n\tt.Run(\"case=admin: loading certificate from a file\", func(t *testing.T) {\n\t\tp, l, hook, exited := newTestConfig(t, configx.WithValues(map[string]interface{}{\n\t\t\tkeyAdminTLSKeyPath:  keyPath,\n\t\t\tkeyAdminTLSCertPath: certPath,\n\t\t}))\n\t\tcertFunc, err := p.ServeAdmin(t.Context()).TLS.GetCertFunc(t.Context(), l, \"admin\")\n\t\trequire.NoError(t, err)\n\t\tassert.NotNil(t, certFunc)\n\t\tle := hook.LastEntry()\n\t\trequire.NotNil(t, le)\n\t\tassert.Equal(t, \"Setting up HTTPS for admin (automatic certificate reloading active)\", le.Message)\n\t\tassert.False(t, *exited)\n\t})\n\n\tt.Run(\"case=admin: failing to load inline base64 certificate\", func(t *testing.T) {\n\t\tp, l, _, _ := newTestConfig(t, configx.WithValues(map[string]interface{}{\n\t\t\tkeyAdminTLSKeyBase64:  \"invalid\",\n\t\t\tkeyAdminTLSCertBase64: certBase64,\n\t\t}))\n\t\tcertFunc, err := p.ServeAdmin(t.Context()).TLS.GetCertFunc(t.Context(), l, \"admin\")\n\t\tassert.Nil(t, certFunc)\n\t\trequire.ErrorContains(t, err, \"unable to load TLS certificate for interface admin\")\n\t})\n\n\tt.Run(\"case=admin: failing to load certificate from a file\", func(t *testing.T) {\n\t\tp, l, _, _ := newTestConfig(t, configx.WithValues(map[string]interface{}{\n\t\t\tkeyAdminTLSKeyPath:  \"/dev/null\",\n\t\t\tkeyAdminTLSCertPath: certPath,\n\t\t}))\n\t\tcertFunc, err := p.ServeAdmin(t.Context()).TLS.GetCertFunc(t.Context(), l, \"admin\")\n\t\trequire.ErrorContains(t, err, \"unable to load TLS certificate for interface admin\")\n\t\tassert.Nil(t, certFunc)\n\t})\n}\n\nfunc TestIdentitySchemaValidation(t *testing.T) {\n\tt.Parallel()\n\tfiles := []string{\"stub/.identity.test.json\", \"stub/.identity.other.json\"}\n\n\tctx := context.Background()\n\tctx = config.SetValidateIdentitySchemaResilientClientOptions(ctx, []httpx.ResilientOptions{\n\t\thttpx.ResilientClientWithMaxRetry(0),\n\t\thttpx.ResilientClientWithConnectionTimeout(time.Millisecond * 100),\n\t})\n\n\ttype identity struct {\n\t\tSchemas []map[string]string `json:\"schemas\"`\n\t}\n\n\ttype configFile struct {\n\t\tidentityFileName string\n\t\tSelfService      map[string]string            `json:\"selfservice\"`\n\t\tCourier          map[string]map[string]string `json:\"courier\"`\n\t\tDSN              string                       `json:\"dsn\"`\n\t\tIdentity         *identity                    `json:\"identity\"`\n\t}\n\n\tsetup := func(t *testing.T, file string) *configFile {\n\t\tidentityTest, err := os.ReadFile(file) // #nosec G304 -- test code\n\t\tassert.NoError(t, err)\n\t\treturn &configFile{\n\t\t\tidentityFileName: file,\n\t\t\tSelfService: map[string]string{\n\t\t\t\t\"default_browser_return_url\": \"https://some-return-url\",\n\t\t\t},\n\t\t\tCourier: map[string]map[string]string{\n\t\t\t\t\"smtp\": {\n\t\t\t\t\t\"connection_uri\": \"smtp://foo@bar\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tDSN: \"memory\",\n\t\t\tIdentity: &identity{\n\t\t\t\tSchemas: []map[string]string{{\"id\": \"default\", \"url\": \"base64://\" + base64.StdEncoding.EncodeToString(identityTest)}},\n\t\t\t},\n\t\t}\n\t}\n\n\tmarshalAndWrite := func(t *testing.T, tmpFile *os.File, identity *configFile) {\n\t\tj, err := yaml.Marshal(identity)\n\t\tassert.NoError(t, err)\n\n\t\t_, err = tmpFile.Seek(0, 0)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, tmpFile.Truncate(0))\n\t\t_, err = io.Writer.Write(tmpFile, j)\n\t\tassert.NoError(t, err)\n\t\tassert.NoError(t, tmpFile.Sync())\n\t}\n\n\ttestWatch := func(t *testing.T, ctx context.Context, identity *configFile) (*config.Config, *test.Hook, func([]map[string]string)) {\n\t\ttdir := t.TempDir()\n\t\tassert.NoError(t, os.MkdirAll(tdir, 0o750))\n\t\tconfigFileName := randx.MustString(8, randx.Alpha)\n\t\ttmpConfig, err := os.Create(filepath.Join(tdir, configFileName+\".config.yaml\")) // #nosec G304 -- test code\n\t\tassert.NoError(t, err)\n\t\tt.Cleanup(func() { _ = tmpConfig.Close() })\n\n\t\tmarshalAndWrite(t, tmpConfig, identity)\n\n\t\tl := logrusx.New(\"kratos-\"+tmpConfig.Name(), \"test\")\n\t\thook := test.NewLocal(l.Logger)\n\n\t\tconf, err := config.New(ctx, l, os.Stderr, &contextx.Default{}, configx.WithConfigFiles(tmpConfig.Name()))\n\t\tassert.NoError(t, err)\n\n\t\t// clean the hooks since it will throw an event on first boot\n\t\thook.Reset()\n\n\t\treturn conf, hook, func(schemas []map[string]string) {\n\t\t\tidentity.Identity.Schemas = schemas\n\t\t\tmarshalAndWrite(t, tmpConfig, identity)\n\t\t}\n\t}\n\n\tt.Run(\"case=skip invalid schema validation\", func(t *testing.T) {\n\t\t_, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.invalid.identities.yaml\"),\n\t\t\tconfigx.SkipValidation())\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"case=invalid schema should throw error\", func(t *testing.T) {\n\t\tvar stdErr bytes.Buffer\n\t\t_, err := config.New(ctx, logrusx.New(\"\", \"\"), &stdErr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.invalid.identities.yaml\"))\n\t\tassert.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"minimum 1 properties allowed, but found 0\")\n\t\tassert.Contains(t, stdErr.String(), \"minimum 1 properties allowed, but found 0\")\n\t})\n\n\tt.Run(\"case=must fail on loading unreachable schemas\", func(t *testing.T) {\n\t\t// we make sure that the test runs into DNS issues instead of the context being canceled\n\t\tctx := config.SetValidateIdentitySchemaResilientClientOptions(ctx, []httpx.ResilientOptions{\n\t\t\thttpx.ResilientClientWithMaxRetry(0),\n\t\t\thttpx.ResilientClientWithConnectionTimeout(5 * time.Second),\n\t\t})\n\n\t\terr := make(chan error)\n\t\tgo func(err chan error) {\n\t\t\t_, e := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.mock.identities.yaml\"))\n\t\t\terr <- e\n\t\t}(err)\n\n\t\tselect {\n\t\tcase <-time.After(5 * time.Second):\n\t\t\tt.Fatal(\"the test could not complete as the context timed out before the identity schema loader timed out\")\n\t\tcase e := <-err:\n\t\t\tassert.ErrorContains(t, e, \"no such host\")\n\t\t}\n\t})\n\n\tt.Run(\"case=validate schema is validated on file change\", func(t *testing.T) {\n\t\tvar identities []*configFile\n\n\t\tfor _, f := range files {\n\t\t\tidentities = append(identities, setup(t, f))\n\t\t}\n\n\t\tinvalidIdentity := setup(t, \"stub/.identity.invalid.json\")\n\n\t\tfor _, identity := range identities {\n\t\t\tt.Run(\"test=identity file \"+identity.identityFileName, func(t *testing.T) {\n\t\t\t\tctx, cancel := context.WithTimeout(ctx, time.Second*30)\n\t\t\t\tt.Cleanup(cancel)\n\n\t\t\t\t_, hook, writeSchema := testWatch(t, ctx, identity)\n\t\t\t\twriteSchema(invalidIdentity.Identity.Schemas)\n\n\t\t\t\t// There are a bunch of log messages beeing logged. We are looking for a specific one.\n\t\t\t\tfor {\n\t\t\t\t\tfor _, v := range hook.AllEntries() {\n\t\t\t\t\t\ts, err := v.String()\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\tif strings.Contains(s, \"The changed identity schema configuration is invalid and could not be loaded.\") {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tselect {\n\t\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\t\tt.Fatal(\"the test could not complete as the context timed out before the file watcher updated\")\n\t\t\t\t\tdefault: // nothing\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestPasswordless(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tconf, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\tconfigx.SkipValidation(),\n\t\tconfigx.WithValue(config.ViperKeyWebAuthnPasswordless, true))\n\trequire.NoError(t, err)\n\n\tassert.True(t, conf.WebAuthnForPasswordless(ctx))\n\tconf.MustSet(ctx, config.ViperKeyWebAuthnPasswordless, false)\n\tassert.False(t, conf.WebAuthnForPasswordless(ctx))\n}\n\nfunc TestPasswordlessCode(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\n\tconf, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\tconfigx.SkipValidation(),\n\t\tconfigx.WithValue(config.ViperKeySelfServiceStrategyConfig+\".code\", map[string]interface{}{\n\t\t\t\"passwordless_enabled\":                true,\n\t\t\t\"passwordless_login_fallback_enabled\": true,\n\t\t\t\"config\":                              map[string]interface{}{},\n\t\t}))\n\trequire.NoError(t, err)\n\n\tassert.True(t, conf.SelfServiceCodeStrategy(ctx).PasswordlessEnabled)\n}\n\nfunc TestChangeMinPasswordLength(t *testing.T) {\n\tt.Parallel()\n\tt.Run(\"case=must fail on minimum password length below enforced minimum\", func(t *testing.T) {\n\t\tctx := context.Background()\n\n\t\t_, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.yaml\"),\n\t\t\tconfigx.WithValue(config.ViperKeyPasswordMinLength, 5))\n\n\t\tassert.Error(t, err)\n\t})\n\n\tt.Run(\"case=must not fail on minimum password length above enforced minimum\", func(t *testing.T) {\n\t\tctx := context.Background()\n\n\t\t_, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.yaml\"),\n\t\t\tconfigx.WithValue(config.ViperKeyPasswordMinLength, 9))\n\n\t\tassert.NoError(t, err)\n\t})\n}\n\nfunc TestCourierEmailHTTP(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tt.Run(\"case=configs set\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.courier.email.http.yaml\"), configx.SkipValidation())\n\t\tassert.Equal(t, \"http\", conf.CourierEmailStrategy(ctx))\n\t\tsnapshotx.SnapshotT(t, conf.CourierEmailRequestConfig(ctx))\n\t})\n\n\tt.Run(\"case=defaults\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{}, configx.SkipValidation())\n\n\t\tassert.Equal(t, \"smtp\", conf.CourierEmailStrategy(ctx))\n\t})\n}\n\nfunc TestCourierChannels(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tt.Run(\"case=configs set\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{}, configx.WithConfigFiles(\"stub/.kratos.courier.channels.yaml\"), configx.SkipValidation())\n\n\t\tchannelConfig, err := conf.CourierChannels(ctx)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, channelConfig, 2)\n\t\tassert.Equal(t, channelConfig[0].ID, \"phone\")\n\t\tassert.NotEmpty(t, channelConfig[0].RequestConfig)\n\t\tassert.Equal(t, channelConfig[1].ID, \"email\")\n\t\tassert.NotEmpty(t, channelConfig[1].SMTPConfig)\n\t})\n\n\tt.Run(\"case=defaults\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{}, configx.SkipValidation())\n\n\t\tchannelConfig, err := conf.CourierChannels(ctx)\n\t\trequire.NoError(t, err)\n\t\tassert.Len(t, channelConfig, 1)\n\t\tassert.Equal(t, channelConfig[0].ID, \"email\")\n\t\tassert.Equal(t, channelConfig[0].Type, \"smtp\")\n\t})\n\n\tt.Run(\"smtp urls\", func(t *testing.T) {\n\t\tfor _, tc := range []string{\n\t\t\t\"smtp://a:basdasdasda%2Fc@email-smtp.eu-west-3.amazonaws.com:587/\",\n\t\t\t\"smtp://a:b$c@email-smtp.eu-west-3.amazonaws.com:587/\",\n\t\t\t\"smtp://a/a:bc@email-smtp.eu-west-3.amazonaws.com:587\",\n\t\t\t\"smtp://aa:b+c@email-smtp.eu-west-3.amazonaws.com:587/\",\n\t\t\t\"smtp://user?name:password@email-smtp.eu-west-3.amazonaws.com:587/\",\n\t\t\t\"smtp://username:pass%2Fword@email-smtp.eu-west-3.amazonaws.com:587/\",\n\t\t} {\n\t\t\tt.Run(\"case=\"+tc, func(t *testing.T) {\n\t\t\t\tconf, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{}, configx.WithValue(config.ViperKeyCourierSMTPURL, tc), configx.SkipValidation())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tcs, err := conf.CourierChannels(ctx)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, cs, 1)\n\t\t\t\tassert.Equal(t, tc, cs[0].SMTPConfig.ConnectionURI)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestCourierMessageTTL(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tt.Run(\"case=configs set\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.courier.message_retries.yaml\"), configx.SkipValidation())\n\t\tassert.Equal(t, conf.CourierMessageRetries(ctx), 10)\n\t})\n\n\tt.Run(\"case=defaults\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{}, configx.SkipValidation())\n\t\tassert.Equal(t, conf.CourierMessageRetries(ctx), 5)\n\t})\n}\n\nfunc TestTwoStep(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tt.Run(\"case=nothing is set\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{}, configx.SkipValidation())\n\n\t\tassert.True(t, conf.SelfServiceFlowRegistrationTwoSteps(ctx))\n\t})\n\n\tt.Run(\"case=legacy config explicit off\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithValue(config.ViperKeySelfServiceRegistrationEnableLegacyOneStep, false),\n\t\t\tconfigx.SkipValidation(),\n\t\t)\n\n\t\tassert.True(t, conf.SelfServiceFlowRegistrationTwoSteps(ctx))\n\t})\n\n\tt.Run(\"case=legacy config explicit on\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithValue(config.ViperKeySelfServiceRegistrationEnableLegacyOneStep, true),\n\t\t\tconfigx.SkipValidation(),\n\t\t)\n\n\t\tassert.False(t, conf.SelfServiceFlowRegistrationTwoSteps(ctx))\n\t})\n\n\tt.Run(\"case=new config explicit on\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithValue(config.ViperKeySelfServiceRegistrationFlowStyle, \"profile_first\"),\n\t\t\tconfigx.SkipValidation(),\n\t\t)\n\n\t\tassert.True(t, conf.SelfServiceFlowRegistrationTwoSteps(ctx))\n\t})\n\n\tt.Run(\"case=new config explicit off\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithValue(config.ViperKeySelfServiceRegistrationFlowStyle, \"unified\"),\n\t\t\tconfigx.SkipValidation(),\n\t\t)\n\n\t\tassert.False(t, conf.SelfServiceFlowRegistrationTwoSteps(ctx))\n\t})\n\n\tt.Run(\"case=new config explicit on but legacy off\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithValue(config.ViperKeySelfServiceRegistrationFlowStyle, \"profile_first\"),\n\t\t\tconfigx.WithValue(config.ViperKeySelfServiceRegistrationEnableLegacyOneStep, true),\n\t\t\tconfigx.SkipValidation(),\n\t\t)\n\n\t\tassert.False(t, conf.SelfServiceFlowRegistrationTwoSteps(ctx))\n\t})\n}\n\nfunc TestOAuth2Provider(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tt.Run(\"case=configs set\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.oauth2_provider.yaml\"), configx.SkipValidation())\n\t\tassert.Equal(t, \"https://oauth2_provider/\", conf.OAuth2ProviderURL(ctx).String())\n\t\tassert.Equal(t, http.Header{\"Authorization\": {\"Basic\"}}, conf.OAuth2ProviderHeader(ctx))\n\t\tassert.True(t, conf.OAuth2ProviderOverrideReturnTo(ctx))\n\t})\n\n\tt.Run(\"case=defaults\", func(t *testing.T) {\n\t\tconf, _ := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{}, configx.SkipValidation())\n\t\tassert.Empty(t, conf.OAuth2ProviderURL(ctx))\n\t\tassert.Empty(t, conf.OAuth2ProviderHeader(ctx))\n\t\tassert.False(t, conf.OAuth2ProviderOverrideReturnTo(ctx))\n\t})\n}\n\nfunc TestWebauthn(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tt.Run(\"case=multiple origins\", func(t *testing.T) {\n\t\tconf, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.webauthn.origins.yaml\"))\n\t\trequire.NoError(t, err)\n\t\twebAuthnConfig := conf.WebAuthnConfig(ctx)\n\t\tassert.Equal(t, \"https://example.com/webauthn\", webAuthnConfig.RPID)\n\t\tassert.EqualValues(t, []string{\n\t\t\t\"https://origin-a.example.com\",\n\t\t\t\"https://origin-b.example.com\",\n\t\t\t\"https://origin-c.example.com\",\n\t\t}, webAuthnConfig.RPOrigins)\n\t})\n\n\tt.Run(\"case=one origin\", func(t *testing.T) {\n\t\tconf, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.webauthn.origin.yaml\"))\n\t\trequire.NoError(t, err)\n\t\twebAuthnConfig := conf.WebAuthnConfig(ctx)\n\t\tassert.Equal(t, \"https://example.com/webauthn\", webAuthnConfig.RPID)\n\t\tassert.EqualValues(t, []string{\n\t\t\t\"https://origin-a.example.com\",\n\t\t}, webAuthnConfig.RPOrigins)\n\t})\n\n\tt.Run(\"case=id as origin\", func(t *testing.T) {\n\t\tconf, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.yaml\"))\n\t\trequire.NoError(t, err)\n\t\twebAuthnConfig := conf.WebAuthnConfig(ctx)\n\t\tassert.Equal(t, \"example.com\", webAuthnConfig.RPID)\n\t\tassert.EqualValues(t, []string{\n\t\t\t\"http://example.com\",\n\t\t}, webAuthnConfig.RPOrigins)\n\t})\n\n\tt.Run(\"case=invalid\", func(t *testing.T) {\n\t\t_, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.webauthn.invalid.yaml\"))\n\t\tassert.Error(t, err)\n\t})\n}\n\nfunc TestCourierTemplatesConfig(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tt.Run(\"case=partial template update allowed\", func(t *testing.T) {\n\t\t_, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.courier.remote.partial.templates.yaml\"))\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"case=load remote template with fallback template overrides path\", func(t *testing.T) {\n\t\t_, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.courier.remote.templates.yaml\"))\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"case=courier template helper\", func(t *testing.T) {\n\t\tc, err := config.New(ctx, logrusx.New(\"\", \"\"), os.Stderr, &contextx.Default{},\n\t\t\tconfigx.WithConfigFiles(\"stub/.kratos.courier.remote.templates.yaml\"))\n\n\t\tassert.NoError(t, err)\n\n\t\tcourierTemplateConfig := &config.CourierEmailTemplate{\n\t\t\tBody: &config.CourierEmailBodyTemplate{\n\t\t\t\tPlainText: \"\",\n\t\t\t\tHTML:      \"\",\n\t\t\t},\n\t\t\tSubject: \"\",\n\t\t}\n\n\t\tassert.Equal(t, courierTemplateConfig, c.CourierEmailTemplatesHelper(ctx, config.ViperKeyCourierTemplatesVerificationInvalidEmail))\n\t\tassert.Equal(t, courierTemplateConfig, c.CourierEmailTemplatesHelper(ctx, config.ViperKeyCourierTemplatesVerificationValidEmail))\n\t\t// this should return an empty courierEmailTemplate as the key does not exist\n\t\tassert.Equal(t, courierTemplateConfig, c.CourierEmailTemplatesHelper(ctx, \"a_random_key\"))\n\n\t\tcourierTemplateConfig = &config.CourierEmailTemplate{\n\t\t\tBody: &config.CourierEmailBodyTemplate{\n\t\t\t\tPlainText: \"base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu\",\n\t\t\t\tHTML:      \"base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu\",\n\t\t\t},\n\t\t\tSubject: \"base64://QWNjb3VudCBBY2Nlc3MgQXR0ZW1wdGVk\",\n\t\t}\n\t\tassert.Equal(t, courierTemplateConfig, c.CourierEmailTemplatesHelper(ctx, config.ViperKeyCourierTemplatesRecoveryInvalidEmail))\n\n\t\tcourierTemplateConfig = &config.CourierEmailTemplate{\n\t\t\tBody: &config.CourierEmailBodyTemplate{\n\t\t\t\tPlainText: \"base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQp7eyAuUmVjb3ZlcnlVUkwgfX0K\",\n\t\t\t\tHTML:      \"base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4=\",\n\t\t\t},\n\t\t\tSubject: \"base64://UmVjb3ZlciBhY2Nlc3MgdG8geW91ciBhY2NvdW50\",\n\t\t}\n\t\tassert.Equal(t, courierTemplateConfig, c.CourierEmailTemplatesHelper(ctx, config.ViperKeyCourierTemplatesRecoveryValidEmail))\n\t})\n}\n\nfunc TestCleanup(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tp := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.WithConfigFiles(\"stub/.kratos.yaml\"))\n\n\tt.Run(\"group=cleanup config\", func(t *testing.T) {\n\t\tassert.Equal(t, p.DatabaseCleanupSleepTables(ctx), 1*time.Minute)\n\t\tp.MustSet(ctx, config.ViperKeyDatabaseCleanupSleepTables, \"1s\")\n\t\tassert.Equal(t, p.DatabaseCleanupSleepTables(ctx), time.Second)\n\t\tassert.Equal(t, p.DatabaseCleanupBatchSize(ctx), 100)\n\t\tp.MustSet(ctx, config.ViperKeyDatabaseCleanupBatchSize, \"1\")\n\t\tassert.Equal(t, p.DatabaseCleanupBatchSize(ctx), 1)\n\t})\n}\n\nconst (\n\tkeyPublicTLSCertBase64 = \"serve.public.tls.cert.base64\"\n\tkeyPublicTLSKeyBase64  = \"serve.public.tls.key.base64\"\n\tkeyPublicTLSCertPath   = \"serve.public.tls.cert.path\"\n\tkeyPublicTLSKeyPath    = \"serve.public.tls.key.path\"\n\tkeyAdminTLSCertBase64  = \"serve.admin.tls.cert.base64\"\n\tkeyAdminTLSKeyBase64   = \"serve.admin.tls.key.base64\"\n\tkeyAdminTLSCertPath    = \"serve.admin.tls.cert.path\"\n\tkeyAdminTLSKeyPath     = \"serve.admin.tls.key.path\"\n)\n"
  },
  {
    "path": "driver/config/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage config\n\nimport (\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/knadh/koanf/parsers/json\"\n\n\t\"github.com/ory/x/httprouterx\"\n)\n\nfunc RegisterConfigHashRoute(c Provider, router *httprouterx.RouterAdmin) {\n\trouter.GET(\"/health/config\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Set(\"Content-Type\", \"text/plain\")\n\t\tif revision := c.Config().GetProvider(r.Context()).String(\"revision\"); len(revision) > 0 {\n\t\t\t_, _ = fmt.Fprintf(w, \"%s\", revision)\n\t\t} else {\n\t\t\tbytes, _ := c.Config().GetProvider(r.Context()).Marshal(json.Parser())\n\t\t\t_, _ = fmt.Fprintf(w, \"%x\", sha256.Sum256(bytes))\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "driver/config/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage config_test\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/httprouterx\"\n)\n\ntype configProvider struct {\n\tcfg *config.Config\n}\n\nfunc (c *configProvider) Config() *config.Config {\n\treturn c.cfg\n}\n\nfunc TestNewConfigHashHandler(t *testing.T) {\n\tctx := context.Background()\n\tcfg := pkg.NewConfigurationWithDefaults(t)\n\trouter := httprouterx.NewTestRouterAdmin(t)\n\tconfig.RegisterConfigHashRoute(&configProvider{cfg: cfg}, router)\n\tts := contextx.NewConfigurableTestServer(router)\n\tt.Cleanup(ts.Close)\n\n\t// first request, get baseline hash\n\tres, err := ts.Client(ctx).Get(ts.URL + \"/health/config\")\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\trequire.Equal(t, 200, res.StatusCode)\n\tfirst, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\n\t// second request, no config change\n\tres, err = ts.Client(ctx).Get(ts.URL + \"/health/config\")\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\trequire.Equal(t, 200, res.StatusCode)\n\tsecond, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\tassert.Equal(t, first, second)\n\n\t// third request, with config change\n\tres, err = ts.Client(contextx.WithConfigValue(ctx, config.ViperKeySessionDomain, \"foobar\")).Get(ts.URL + \"/health/config\")\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\trequire.Equal(t, 200, res.StatusCode)\n\tthird, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\tassert.NotEqual(t, first, third)\n\n\t// fourth request, no config change\n\tres, err = ts.Client(ctx).Get(ts.URL + \"/health/config\")\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\trequire.Equal(t, 200, res.StatusCode)\n\tfourth, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\tassert.Equal(t, first, fourth)\n}\n"
  },
  {
    "path": "driver/config/stub/.defaults-password.yml",
    "content": "selfservice:\n  methods:\n    password:\n      enabled: true\n  flows:\n    recovery: false\n"
  },
  {
    "path": "driver/config/stub/.defaults-verification.yml",
    "content": "selfservice:\n  methods:\n    password:\n      enabled: true\n\n  flows:\n    settings:\n      privileged_session_max_age: 1m\n      after:\n        hooks:\n          profile:\n            - hook: show_verification_ui\n\n    verification:\n      enabled: true\n      lifespan: 5s\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/\n\n    logout:\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/auth/login\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/verification/identity.traits.schema.json\n"
  },
  {
    "path": "driver/config/stub/.defaults.yml",
    "content": "selfservice:\n\n"
  },
  {
    "path": "driver/config/stub/.identity.invalid.json",
    "content": "{\n  \"$id\": \"ory://identity-invalid-schema\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"IdentityInvalidSchema\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {},\n      \"required\": [\n        \"other\",\n        \"email\"\n      ],\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "driver/config/stub/.identity.other.json",
    "content": "{\n  \"$id\": \"ory://identity-other-schema\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"IdentityOtherSchema\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"other\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"title\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"other\",\n        \"email\"\n      ],\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "driver/config/stub/.identity.test.json",
    "content": "{\n  \"$id\": \"ory://identity-test-schema\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"IdentitySchema\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"name\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"first\": {\n              \"type\": \"string\"\n            },\n            \"last\": {\n              \"type\": \"string\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"name\"\n      ],\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "driver/config/stub/.kratos.courier.channels.yaml",
    "content": "courier:\n  smtp:\n    connection_uri: smtp://username:password@smtp.example.com:587\n  channels:\n    - id: phone\n      request_config:\n        url: https://ory.sh\n        method: GET\n        body: base64://ZnVuY3Rpb24oY3R4KSB7CkJvZHk6IGN0eC5ib2R5LApUbzogY3R4LnRvLEZyb206IGN0eC5mcm9tCn0=\n        headers:\n          Content-Type: application/x-www-form-urlencoded\n        auth:\n          type: basic_auth\n          config:\n            user: ABC\n            password: DEF\n"
  },
  {
    "path": "driver/config/stub/.kratos.courier.email.http.yaml",
    "content": "dsn: sqlite://foo.db?mode=memory&_fk=true\n\nselfservice:\n  default_browser_return_url: https://example.com/return_to\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n\ncourier:\n  delivery_strategy: http\n  http:\n    request_config:\n      url: https://example.com/email\n      body: file://some.jsonnet\n      header:\n        'Content-Type': 'application/json'\n      auth:\n        type: basic_auth\n        config:\n          user: YourUsername\n          password: YourPass\n"
  },
  {
    "path": "driver/config/stub/.kratos.courier.message_retries.yaml",
    "content": "courier:\n  message_retries: 10\n"
  },
  {
    "path": "driver/config/stub/.kratos.courier.remote.invalid.subject.yaml",
    "content": "dsn: sqlite://foo.db?mode=memory&_fk=true\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n\ncourier:\n  smtp:\n    connection_uri: smtp://stub-url\n  templates:\n    recovery:\n      invalid:\n        email:\n          body:\n            html: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu\n          # omit subject to verify it is validated\n      valid:\n        # omit template_root as it is not required on valid\n        email:\n          body:\n            plaintext: base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQp7eyAuUmVjb3ZlcnlVUkwgfX0K\n            html: base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4=\n          subject: base64://UmVjb3ZlciBhY2Nlc3MgdG8geW91ciBhY2NvdW50\n    # omit verification here to test templates override fallback\n  template_override_path: \"../../courier/template/courier/builtin/templates\"\n"
  },
  {
    "path": "driver/config/stub/.kratos.courier.remote.partial.templates.yaml",
    "content": "dsn: sqlite://foo.db?mode=memory&_fk=true\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n\ncourier:\n  smtp:\n    connection_uri: smtp://stub-url\n  templates:\n    recovery:\n      invalid:\n        email:\n          body:\n            plaintext: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu\n            html: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu\n    verification:\n      valid:\n        email:\n          subject: base64://VmVyaWZpY2F0aW9uIEVtYWls\n  template_override_path: \"../../courier/template/courier/builtin/templates\"\n"
  },
  {
    "path": "driver/config/stub/.kratos.courier.remote.templates.yaml",
    "content": "dsn: sqlite://foo.db?mode=memory&_fk=true\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n\ncourier:\n  smtp:\n    connection_uri: smtp://stub-url\n  templates:\n    recovery:\n      invalid:\n        email:\n          body:\n            plaintext: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu\n            html: base64://SGksCgp5b3UgKG9yIHNvbWVvbmUgZWxzZSkgZW50ZXJlZCB0aGlzIGVtYWlsIGFkZHJlc3Mgd2hlbiB0cnlpbmcgdG8gcmVjb3ZlciBhY2Nlc3MgdG8gYW4gYWNjb3VudC4KCkhvd2V2ZXIsIHRoaXMgZW1haWwgYWRkcmVzcyBpcyBub3Qgb24gb3VyIGRhdGFiYXNlIG9mIHJlZ2lzdGVyZWQgdXNlcnMgYW5kIHRoZXJlZm9yZSB0aGUgYXR0ZW1wdCBoYXMgZmFpbGVkLgoKSWYgdGhpcyB3YXMgeW91LCBjaGVjayBpZiB5b3Ugc2lnbmVkIHVwIHVzaW5nIGEgZGlmZmVyZW50IGFkZHJlc3MuCgpJZiB0aGlzIHdhcyBub3QgeW91LCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwu\n          subject: base64://QWNjb3VudCBBY2Nlc3MgQXR0ZW1wdGVk\n      valid:\n        email:\n          body:\n            plaintext: base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQp7eyAuUmVjb3ZlcnlVUkwgfX0K\n            html: base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4=\n          subject: base64://UmVjb3ZlciBhY2Nlc3MgdG8geW91ciBhY2NvdW50\n    # omit verification here to test templates override fallback\n  template_override_path: \"../../courier/template/courier/builtin/templates\"\n"
  },
  {
    "path": "driver/config/stub/.kratos.courier.sms.yaml",
    "content": "dsn: sqlite://foo.db?mode=memory&_fk=true\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n\ncourier:\n  smtp:\n    connection_uri: smtp://foo:bar@baz/\n  sms:\n    enabled: true\n    from: '+49123456789'\n    request_config:\n      url: https://api.twilio.com/2010-04-01/Accounts/YourAccountID/Messages.json\n      method: POST\n      body: base64://e30=\n      header:\n        'Content-Type': 'application/x-www-form-urlencoded'\n      auth:\n        type: basic_auth\n        config:\n          user: YourUsername\n          password: YourPass\n"
  },
  {
    "path": "driver/config/stub/.kratos.invalid.identities.yaml",
    "content": "dsn: sqlite://foo.db?mode=memory&_fk=true\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n\nidentity:\n  default_schema_id: other\n  schemas:\n    - id: other\n      url: file://stub/.identity.invalid.json\n\ncourier:\n  smtp:\n    connection_uri: smtp://foo:bar@baz/\n"
  },
  {
    "path": "driver/config/stub/.kratos.mock.identities.yaml",
    "content": "dsn: sqlite://foo.db?mode=memory&_fk=true\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: http://test.kratos.ory.sh/default-identity.schema.json\n    - id: other\n      url: http://test.kratos.ory.sh/other-identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtp://foo:bar@baz/\n"
  },
  {
    "path": "driver/config/stub/.kratos.notify-unknown-recipients.yml",
    "content": "selfservice:\n  flows:\n    recovery:\n      notify_unknown_recipients: true\n    verification:\n      notify_unknown_recipients: true\n"
  },
  {
    "path": "driver/config/stub/.kratos.oauth2_provider.yaml",
    "content": "oauth2_provider:\n  url: https://oauth2_provider/\n  headers:\n    Authorization: Basic\n  override_return_to: true\n"
  },
  {
    "path": "driver/config/stub/.kratos.webauthn.invalid.yaml",
    "content": "# serve controls the configuration for the http(s) daemon\nserve:\n  admin:\n    base_url: http://admin.kratos.ory.sh\n    port: 1234\n    host: admin.kratos.ory.sh\n  public:\n    base_url: http://public.kratos.ory.sh\n    port: 1235\n    host: public.kratos.ory.sh\n\ndsn: sqlite://foo.db?mode=memory&_fk=true\n\nlog:\n  level: debug\n\ncourier:\n  smtp:\n    connection_uri: smtp://foo:bar@baz/\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n    - id: other\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktb3RoZXItc2NoZW1hIiwKICAiJHNjaGVtYSI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLAogICJ0aXRsZSI6ICJJZGVudGl0eU90aGVyU2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm90aGVyIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImVtYWlsIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJ0aXRsZSI6ICJlbWFpbCIsCiAgICAgICAgICAib3J5LnNoL2tyYXRvcyI6IHsKICAgICAgICAgICAgImNyZWRlbnRpYWxzIjogewogICAgICAgICAgICAgICJwYXNzd29yZCI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJvdGhlciIsCiAgICAgICAgImVtYWlsIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n\nhashers:\n  argon2:\n    memory: 1MB\n    iterations: 2\n    parallelism: 4\n    salt_length: 16\n    key_length: 32\n    dedicated_memory: 1GB\n    expected_duration: 500ms\n    expected_deviation: 500ms\n\nsecrets:\n  cookie:\n    - session-key-7f8a9b77-1\n    - session-key-7f8a9b77-2\n  cipher:\n    - secret-thirty-two-character-long\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n  allowed_return_urls:\n    - http://return-to-1-test.ory.sh/\n    - http://return-to-2-test.ory.sh/\n    - http://*.wildcards.ory.sh\n    - http://*.sh\n    - http://*.com\n    - http://*.com.pl\n    - http://*\n    - /return-to-relative-test/\n  methods:\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    password:\n      enabled: true\n    oidc:\n      enabled: true\n      config:\n        providers:\n          - id: github\n            provider: github\n            client_id: a\n            client_secret: b\n            mapper_url: http://test.kratos.ory.sh/default-identity.schema.json\n    webauthn:\n      enabled: true\n      config:\n        rp:\n          id: https://example.com/webauthn\n          display_name: Webauthn\n          origin: https://origin-a.example.com\n          origins:\n            - https://origin-a.example.com\n            - https://origin-b.example.com\n            - https://origin-c.example.com\n  flows:\n    error:\n      ui_url: http://test.kratos.ory.sh/error\n\n    logout:\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh:4000/\n\n    recovery:\n      enabled: true\n      ui_url: http://test.kratos.ory.sh/recovery\n      lifespan: 98m\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh/dashboard\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_recovery_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    verification:\n      enabled: true\n      lifespan: 97m\n      ui_url: http://test.kratos.ory.sh/verification\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh/dashboard\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_verification_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    settings:\n      ui_url: http://test.kratos.ory.sh/settings\n      lifespan: 99m\n      privileged_session_max_age: 5m\n      after:\n        default_browser_return_url: https://self-service/settings/return_to\n        password:\n          default_browser_return_url: https://self-service/settings/password/return_to\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_settings_password_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        profile:\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_settings_profile_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_settings_global_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    login:\n      ui_url: http://test.kratos.ory.sh/login\n      lifespan: 99m\n      before:\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/before_login_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n      after:\n        default_browser_return_url: https://self-service/login/return_to\n        password:\n          default_browser_return_url: https://self-service/login/password/return_to\n          hooks:\n            - hook: revoke_active_sessions\n            - hook: require_verified_address\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_login_password_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n                auth:\n                  type: basic_auth\n                  config:\n                    user: test-user\n                    password: super-secret\n        oidc:\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_login_oidc_hook\n                method: GET\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n            - hook: revoke_active_sessions\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_login_global_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    registration:\n      enabled: true\n      ui_url: http://test.kratos.ory.sh/register\n      lifespan: 98m\n      before:\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/before_registration_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n      after:\n        default_browser_return_url: https://self-service/registration/return_to\n        password:\n          hooks:\n            - hook: session\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_registration_password_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_registration_global_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n              auth:\n                type: api_key\n                config:\n                  name: My-Key\n                  value: My-Key-Value\n                  in: header\n        oidc:\n          default_browser_return_url: https://self-service/registration/oidc/return_to\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_registration_oidc_hook\n                method: GET\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n            - hook: session\n"
  },
  {
    "path": "driver/config/stub/.kratos.webauthn.origin.yaml",
    "content": "# serve controls the configuration for the http(s) daemon\nserve:\n  admin:\n    base_url: http://admin.kratos.ory.sh\n    port: 1234\n    host: admin.kratos.ory.sh\n  public:\n    base_url: http://public.kratos.ory.sh\n    port: 1235\n    host: public.kratos.ory.sh\n\ndsn: sqlite://foo.db?mode=memory&_fk=true\n\nlog:\n  level: debug\n\ncourier:\n  smtp:\n    connection_uri: smtp://foo:bar@baz/\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n    - id: other\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktb3RoZXItc2NoZW1hIiwKICAiJHNjaGVtYSI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLAogICJ0aXRsZSI6ICJJZGVudGl0eU90aGVyU2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm90aGVyIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImVtYWlsIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJ0aXRsZSI6ICJlbWFpbCIsCiAgICAgICAgICAib3J5LnNoL2tyYXRvcyI6IHsKICAgICAgICAgICAgImNyZWRlbnRpYWxzIjogewogICAgICAgICAgICAgICJwYXNzd29yZCI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJvdGhlciIsCiAgICAgICAgImVtYWlsIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n\nhashers:\n  argon2:\n    memory: 1MB\n    iterations: 2\n    parallelism: 4\n    salt_length: 16\n    key_length: 32\n    dedicated_memory: 1GB\n    expected_duration: 500ms\n    expected_deviation: 500ms\n\nsecrets:\n  cookie:\n    - session-key-7f8a9b77-1\n    - session-key-7f8a9b77-2\n  cipher:\n    - secret-thirty-two-character-long\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n  allowed_return_urls:\n    - http://return-to-1-test.ory.sh/\n    - http://return-to-2-test.ory.sh/\n    - http://*.wildcards.ory.sh\n    - http://*.sh\n    - http://*.com\n    - http://*.com.pl\n    - http://*\n    - /return-to-relative-test/\n  methods:\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    password:\n      enabled: true\n    oidc:\n      enabled: true\n      config:\n        providers:\n          - id: github\n            provider: github\n            client_id: a\n            client_secret: b\n            mapper_url: http://test.kratos.ory.sh/default-identity.schema.json\n    webauthn:\n      enabled: true\n      config:\n        rp:\n          id: https://example.com/webauthn\n          display_name: Webauthn\n          origin: https://origin-a.example.com\n  flows:\n    error:\n      ui_url: http://test.kratos.ory.sh/error\n\n    logout:\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh:4000/\n\n    recovery:\n      enabled: true\n      ui_url: http://test.kratos.ory.sh/recovery\n      lifespan: 98m\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh/dashboard\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_recovery_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    verification:\n      enabled: true\n      lifespan: 97m\n      ui_url: http://test.kratos.ory.sh/verification\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh/dashboard\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_verification_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    settings:\n      ui_url: http://test.kratos.ory.sh/settings\n      lifespan: 99m\n      privileged_session_max_age: 5m\n      after:\n        default_browser_return_url: https://self-service/settings/return_to\n        password:\n          default_browser_return_url: https://self-service/settings/password/return_to\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_settings_password_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        profile:\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_settings_profile_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_settings_global_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    login:\n      ui_url: http://test.kratos.ory.sh/login\n      lifespan: 99m\n      before:\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/before_login_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n      after:\n        default_browser_return_url: https://self-service/login/return_to\n        password:\n          default_browser_return_url: https://self-service/login/password/return_to\n          hooks:\n            - hook: revoke_active_sessions\n            - hook: require_verified_address\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_login_password_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n                auth:\n                  type: basic_auth\n                  config:\n                    user: test-user\n                    password: super-secret\n        oidc:\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_login_oidc_hook\n                method: GET\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n            - hook: revoke_active_sessions\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_login_global_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    registration:\n      enabled: true\n      ui_url: http://test.kratos.ory.sh/register\n      lifespan: 98m\n      before:\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/before_registration_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n      after:\n        default_browser_return_url: https://self-service/registration/return_to\n        password:\n          hooks:\n            - hook: session\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_registration_password_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_registration_global_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n              auth:\n                type: api_key\n                config:\n                  name: My-Key\n                  value: My-Key-Value\n                  in: header\n        oidc:\n          default_browser_return_url: https://self-service/registration/oidc/return_to\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_registration_oidc_hook\n                method: GET\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n            - hook: session\n"
  },
  {
    "path": "driver/config/stub/.kratos.webauthn.origins.yaml",
    "content": "# serve controls the configuration for the http(s) daemon\nserve:\n  admin:\n    base_url: http://admin.kratos.ory.sh\n    port: 1234\n    host: admin.kratos.ory.sh\n  public:\n    base_url: http://public.kratos.ory.sh\n    port: 1235\n    host: public.kratos.ory.sh\n\ndsn: sqlite://foo.db?mode=memory&_fk=true\n\nlog:\n  level: debug\n\ncourier:\n  smtp:\n    connection_uri: smtp://foo:bar@baz/\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n    - id: other\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktb3RoZXItc2NoZW1hIiwKICAiJHNjaGVtYSI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLAogICJ0aXRsZSI6ICJJZGVudGl0eU90aGVyU2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm90aGVyIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImVtYWlsIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJ0aXRsZSI6ICJlbWFpbCIsCiAgICAgICAgICAib3J5LnNoL2tyYXRvcyI6IHsKICAgICAgICAgICAgImNyZWRlbnRpYWxzIjogewogICAgICAgICAgICAgICJwYXNzd29yZCI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJvdGhlciIsCiAgICAgICAgImVtYWlsIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n\nhashers:\n  argon2:\n    memory: 1MB\n    iterations: 2\n    parallelism: 4\n    salt_length: 16\n    key_length: 32\n    dedicated_memory: 1GB\n    expected_duration: 500ms\n    expected_deviation: 500ms\n\nsecrets:\n  cookie:\n    - session-key-7f8a9b77-1\n    - session-key-7f8a9b77-2\n  cipher:\n    - secret-thirty-two-character-long\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n  allowed_return_urls:\n    - http://return-to-1-test.ory.sh/\n    - http://return-to-2-test.ory.sh/\n    - http://*.wildcards.ory.sh\n    - http://*.sh\n    - http://*.com\n    - http://*.com.pl\n    - http://*\n    - /return-to-relative-test/\n  methods:\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    password:\n      enabled: true\n    oidc:\n      enabled: true\n      config:\n        providers:\n          - id: github\n            provider: github\n            client_id: a\n            client_secret: b\n            mapper_url: http://test.kratos.ory.sh/default-identity.schema.json\n    webauthn:\n      enabled: true\n      config:\n        rp:\n          id: https://example.com/webauthn\n          display_name: Webauthn\n          origins:\n            - https://origin-a.example.com\n            - https://origin-b.example.com\n            - https://origin-c.example.com\n  flows:\n    error:\n      ui_url: http://test.kratos.ory.sh/error\n\n    logout:\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh:4000/\n\n    recovery:\n      enabled: true\n      ui_url: http://test.kratos.ory.sh/recovery\n      lifespan: 98m\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh/dashboard\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_recovery_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    verification:\n      enabled: true\n      lifespan: 97m\n      ui_url: http://test.kratos.ory.sh/verification\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh/dashboard\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_verification_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    settings:\n      ui_url: http://test.kratos.ory.sh/settings\n      lifespan: 99m\n      privileged_session_max_age: 5m\n      after:\n        default_browser_return_url: https://self-service/settings/return_to\n        password:\n          default_browser_return_url: https://self-service/settings/password/return_to\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_settings_password_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        profile:\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_settings_profile_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_settings_global_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    login:\n      ui_url: http://test.kratos.ory.sh/login\n      lifespan: 99m\n      before:\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/before_login_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n      after:\n        default_browser_return_url: https://self-service/login/return_to\n        password:\n          default_browser_return_url: https://self-service/login/password/return_to\n          hooks:\n            - hook: revoke_active_sessions\n            - hook: require_verified_address\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_login_password_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n                auth:\n                  type: basic_auth\n                  config:\n                    user: test-user\n                    password: super-secret\n        oidc:\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_login_oidc_hook\n                method: GET\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n            - hook: revoke_active_sessions\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_login_global_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    registration:\n      enabled: true\n      ui_url: http://test.kratos.ory.sh/register\n      lifespan: 98m\n      before:\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/before_registration_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n      after:\n        default_browser_return_url: https://self-service/registration/return_to\n        password:\n          hooks:\n            - hook: session\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_registration_password_hook\n                method: POST\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_registration_global_hook\n              method: POST\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n              auth:\n                type: api_key\n                config:\n                  name: My-Key\n                  value: My-Key-Value\n                  in: header\n        oidc:\n          default_browser_return_url: https://self-service/registration/oidc/return_to\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_registration_oidc_hook\n                method: GET\n                headers: \n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n            - hook: session\n"
  },
  {
    "path": "driver/config/stub/.kratos.yaml",
    "content": "# serve controls the configuration for the http(s) daemon\nserve:\n  admin:\n    base_url: http://admin.kratos.ory.sh\n    port: 1234\n    host: admin.kratos.ory.sh\n  public:\n    base_url: http://public.kratos.ory.sh\n    port: 1235\n    host: public.kratos.ory.sh\n\ndsn: sqlite://foo.db?mode=memory&_fk=true\n\nlog:\n  level: debug\n\ncourier:\n  smtp:\n    connection_uri: smtp://foo:bar@baz/\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktdGVzdC1zY2hlbWEiLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInRpdGxlIjogIklkZW50aXR5U2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICJmaXJzdCI6IHsKICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJsYXN0IjogewogICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJuYW1lIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n    - id: other\n      url: base64://ewogICIkaWQiOiAib3J5Oi8vaWRlbnRpdHktb3RoZXItc2NoZW1hIiwKICAiJHNjaGVtYSI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA3L3NjaGVtYSMiLAogICJ0aXRsZSI6ICJJZGVudGl0eU90aGVyU2NoZW1hIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgIm90aGVyIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImVtYWlsIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJ0aXRsZSI6ICJlbWFpbCIsCiAgICAgICAgICAib3J5LnNoL2tyYXRvcyI6IHsKICAgICAgICAgICAgImNyZWRlbnRpYWxzIjogewogICAgICAgICAgICAgICJwYXNzd29yZCI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJvdGhlciIsCiAgICAgICAgImVtYWlsIgogICAgICBdLAogICAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgICB9CiAgfQp9\n\nhashers:\n  argon2:\n    memory: 1MB\n    iterations: 2\n    parallelism: 4\n    salt_length: 16\n    key_length: 32\n    dedicated_memory: 1GB\n    expected_duration: 500ms\n    expected_deviation: 500ms\n\nsecrets:\n  cookie:\n    - session-key-7f8a9b77-1\n    - session-key-7f8a9b77-2\n  cipher:\n    - secret-thirty-two-character-long\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nselfservice:\n  default_browser_return_url: http://return-to-3-test.ory.sh/\n  allowed_return_urls:\n    - http://return-to-1-test.ory.sh/\n    - http://return-to-2-test.ory.sh/\n    - http://*.wildcards.ory.sh\n    - http://*.sh\n    - http://*.com\n    - http://*.com.pl\n    - http://*\n    - /return-to-relative-test/\n  methods:\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    password:\n      enabled: true\n    oidc:\n      enabled: true\n      config:\n        providers:\n          - id: github\n            provider: github\n            client_id: a\n            client_secret: b\n            mapper_url: http://test.kratos.ory.sh/default-identity.schema.json\n    webauthn:\n      enabled: true\n      config:\n        rp:\n          id: example.com\n          display_name: Webauthn\n  flows:\n    error:\n      ui_url: http://test.kratos.ory.sh/error\n\n    logout:\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh:4000/\n\n    recovery:\n      enabled: true\n      ui_url: http://test.kratos.ory.sh/recovery\n      lifespan: 98m\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh/dashboard\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_recovery_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    verification:\n      enabled: true\n      lifespan: 97m\n      ui_url: http://test.kratos.ory.sh/verification\n      after:\n        default_browser_return_url: http://test.kratos.ory.sh/dashboard\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_verification_hook\n              method: GET\n              headers: \n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    settings:\n      ui_url: http://test.kratos.ory.sh/settings\n      lifespan: 99m\n      privileged_session_max_age: 5m\n      after:\n        default_browser_return_url: https://self-service/settings/return_to\n        password:\n          default_browser_return_url: https://self-service/settings/password/return_to\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_settings_password_hook\n                method: POST\n                headers:\n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        profile:\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_settings_profile_hook\n                method: POST\n                headers:\n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_settings_global_hook\n              method: POST\n              headers:\n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    login:\n      ui_url: http://test.kratos.ory.sh/login\n      lifespan: 99m\n      before:\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/before_login_hook\n              method: POST\n              headers:\n                X-Custom-Header: test\n      after:\n        default_browser_return_url: https://self-service/login/return_to\n        password:\n          default_browser_return_url: https://self-service/login/password/return_to\n          hooks:\n            - hook: revoke_active_sessions\n            - hook: require_verified_address\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_login_password_hook\n                method: POST\n                headers:\n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n                auth:\n                  type: basic_auth\n                  config:\n                    user: test-user\n                    password: super-secret\n        oidc:\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_login_oidc_hook\n                method: GET\n                headers:\n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n            - hook: revoke_active_sessions\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_login_global_hook\n              method: POST\n              headers:\n                X-Custom-Header: test\n              body: /path/to/template.jsonnet\n\n    registration:\n      enabled: true\n      ui_url: http://test.kratos.ory.sh/register\n      lifespan: 98m\n      before:\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/before_registration_hook\n              method: GET\n              headers:\n                X-Custom-Header: test\n      after:\n        default_browser_return_url: https://self-service/registration/return_to\n        password:\n          hooks:\n            - hook: session\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_registration_password_hook\n                method: POST\n                headers:\n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n        hooks:\n          - hook: web_hook\n            config:\n              url: https://test.kratos.ory.sh/after_registration_global_hook\n              method: POST\n              headers:\n                  X-Custom-Header: test\n              body: /path/to/template.jsonnet\n              auth:\n                type: api_key\n                config:\n                  name: My-Key\n                  value: My-Key-Value\n                  in: header\n        oidc:\n          default_browser_return_url: https://self-service/registration/oidc/return_to\n          hooks:\n            - hook: web_hook\n              config:\n                url: https://test.kratos.ory.sh/after_registration_oidc_hook\n                method: GET\n                headers:\n                  X-Custom-Header: test\n                body: /path/to/template.jsonnet\n            - hook: session\n"
  },
  {
    "path": "driver/factory.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\t\"io\"\n\n\t\"github.com/ory/x/servicelocatorx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/logrusx\"\n)\n\nfunc New(ctx context.Context, stdOutOrErr io.Writer, dOpts ...RegistryOption) (*RegistryDefault, error) {\n\tr, err := NewWithoutInit(ctx, stdOutOrErr, dOpts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tctxter := r.Contextualizer()\n\tif err := r.Init(ctx, ctxter, dOpts...); err != nil {\n\t\tr.Logger().WithError(err).Error(\"Unable to initialize service registry.\")\n\t\treturn nil, err\n\t}\n\n\treturn r, nil\n}\n\nfunc NewWithoutInit(ctx context.Context, stdOutOrErr io.Writer, dOpts ...RegistryOption) (*RegistryDefault, error) {\n\topts := newOptions(dOpts)\n\tsl := servicelocatorx.NewOptions(opts.serviceLocatorOptions...)\n\n\tl := sl.Logger()\n\tif l == nil {\n\t\tl = logrusx.New(\"Ory Kratos\", config.Version)\n\t}\n\n\tc := opts.config\n\tif c == nil {\n\t\tvar err error\n\t\tc, err = config.New(ctx, l, stdOutOrErr, sl.Contextualizer(), opts.configOptions...)\n\t\tif err != nil {\n\t\t\tl.WithError(err).Error(\"Unable to instantiate configuration.\")\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tr, err := NewRegistryFromDSN(ctx, c, l)\n\tif err != nil {\n\t\tl.WithError(err).Error(\"Unable to instantiate service registry.\")\n\t\treturn nil, err\n\t}\n\tr.slOptions = sl\n\tr.SetContextualizer(sl.Contextualizer())\n\n\treturn r, nil\n}\n"
  },
  {
    "path": "driver/factory_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n)\n\nfunc TestDriverNew(t *testing.T) {\n\tctx := context.Background()\n\tr, err := driver.New(\n\t\tcontext.Background(),\n\t\tos.Stderr,\n\t\tdriver.WithConfigOptions(\n\t\t\tconfigx.WithValue(config.ViperKeyDSN, config.DefaultSQLiteMemoryDSN),\n\t\t\tconfigx.SkipValidation(),\n\t\t))\n\trequire.NoError(t, err)\n\n\tassert.EqualValues(t, config.DefaultSQLiteMemoryDSN, r.Config().DSN(ctx))\n\tpingCtx, cancel := context.WithTimeout(ctx, 10*time.Second)\n\tt.Cleanup(cancel)\n\trequire.NoError(t, r.Persister().Ping(pingCtx))\n\n\tassert.NotEqual(t, uuid.Nil.String(), r.Persister().NetworkID(context.Background()).String())\n\n\tn, err := r.Persister().DetermineNetwork(context.Background())\n\trequire.NoError(t, err)\n\tassert.Equal(t, r.Persister().NetworkID(context.Background()), n.ID)\n}\n"
  },
  {
    "path": "driver/registry.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\t\"io/fs\"\n\n\t\"github.com/gorilla/sessions\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/ory/kratos/cipher\"\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/logout\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\tpassword2 \"github.com/ory/kratos/selfservice/strategy/password\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/healthx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/popx\"\n\t\"github.com/ory/x/servicelocatorx\"\n)\n\ntype Registry interface {\n\tInit(ctx context.Context, ctxer contextx.Contextualizer, opts ...RegistryOption) error\n\n\tSetLogger(l *logrusx.Logger)\n\tSetJSONNetVMProvider(jsonnetsecure.VMProvider)\n\n\tWithCSRFHandler(c nosurf.Handler)\n\tWithCSRFTokenGenerator(cg nosurfx.CSRFToken)\n\n\tHealthHandler(ctx context.Context) *healthx.Handler\n\tCookieManager(ctx context.Context) sessions.StoreExact\n\tContinuityCookieManager(ctx context.Context) sessions.StoreExact\n\n\tRegisterPublicRoutes(ctx context.Context, public *httprouterx.RouterPublic)\n\tRegisterAdminRoutes(ctx context.Context, admin *httprouterx.RouterAdmin)\n\tTracer(context.Context) *otelx.Tracer\n\tSetTracer(*otelx.Tracer)\n\n\tconfig.Provider\n\tCourierConfig() config.CourierConfigs\n\tSetConfig(c *config.Config)\n\tSetContextualizer(ctxer contextx.Contextualizer)\n\n\tnosurfx.CSRFProvider\n\thttpx.WriterProvider\n\tlogrusx.Provider\n\thttpx.ClientProvider\n\tjsonnetsecure.VMProvider\n\n\tcontinuity.ManagementProvider\n\tcontinuity.PersistenceProvider\n\n\tcipher.Provider\n\n\tcourier.Provider\n\n\tpersistence.Provider\n\n\terrorx.ManagementProvider\n\terrorx.HandlerProvider\n\terrorx.PersistenceProvider\n\n\thash.HashProvider\n\n\tidentity.HandlerProvider\n\tidentity.ValidationProvider\n\tidentity.PoolProvider\n\tidentity.PrivilegedPoolProvider\n\tidentity.ManagementProvider\n\tidentity.ActiveCredentialsCounterStrategyProvider\n\n\tcourier.HandlerProvider\n\tcourier.PersistenceProvider\n\n\tschema.HandlerProvider\n\tschema.IdentitySchemaProvider\n\n\tpassword2.ValidationProvider\n\n\tsession.HandlerProvider\n\tsession.ManagementProvider\n\tsession.PersistenceProvider\n\tsession.TokenizerProvider\n\n\tsettings.HandlerProvider\n\tsettings.ErrorHandlerProvider\n\tsettings.FlowPersistenceProvider\n\tsettings.StrategyProvider\n\n\tlogin.FlowPersistenceProvider\n\tlogin.ErrorHandlerProvider\n\tlogin.HooksProvider\n\tlogin.HookExecutorProvider\n\tlogin.HandlerProvider\n\tlogin.StrategyProvider\n\n\tlogout.HandlerProvider\n\n\tregistration.FlowPersistenceProvider\n\tregistration.ErrorHandlerProvider\n\tregistration.HooksProvider\n\tregistration.HookExecutorProvider\n\tregistration.HandlerProvider\n\tregistration.StrategyProvider\n\n\tverification.FlowPersistenceProvider\n\tverification.ErrorHandlerProvider\n\tverification.HandlerProvider\n\tverification.StrategyProvider\n\n\tsessiontokenexchange.PersistenceProvider\n\n\tlink.SenderProvider\n\tlink.VerificationTokenPersistenceProvider\n\tlink.RecoveryTokenPersistenceProvider\n\n\tcode.SenderProvider\n\tcode.RecoveryCodePersistenceProvider\n\n\trecovery.FlowPersistenceProvider\n\trecovery.ErrorHandlerProvider\n\trecovery.HandlerProvider\n\trecovery.StrategyProvider\n\n\tnosurfx.CSRFTokenGeneratorProvider\n}\n\nfunc NewRegistryFromDSN(ctx context.Context, c *config.Config, l *logrusx.Logger) (*RegistryDefault, error) {\n\treg := NewRegistryDefault()\n\n\ttracer, err := otelx.New(\"Ory Kratos\", l, c.Tracing(ctx))\n\tif err != nil {\n\t\tl.WithError(err).Fatalf(\"failed to initialize tracer\")\n\t\ttracer = otelx.NewNoop()\n\t}\n\treg.SetTracer(tracer)\n\treg.SetLogger(l)\n\treg.SetConfig(c)\n\n\treturn reg, nil\n}\n\ntype options struct {\n\tskipNetworkInit               bool\n\tconfig                        *config.Config\n\tconfigOptions                 []configx.OptionModifier\n\treplaceTracer                 func(*otelx.Tracer) *otelx.Tracer\n\treplaceIdentitySchemaProvider func(Registry) schema.IdentitySchemaProvider\n\tinspect                       func(Registry) error\n\textraMigrations               []fs.FS\n\textraGoMigrations             popx.Migrations\n\treplacementStrategies         []NewStrategy\n\textraHooks                    map[string]func(config.SelfServiceHook) any\n\textraHandlers                 []NewHandler\n\tdisableMigrationLogging       bool\n\tjsonnetPool                   jsonnetsecure.Pool\n\tserviceLocatorOptions         []servicelocatorx.Option\n\tdbOpts                        []func(details *pop.ConnectionDetails)\n}\n\ntype RegistryOption func(*options)\n\n// WithDBOptions adds database connection options that will be applied to the\n// underlying connection.\nfunc WithDBOptions(opts ...func(details *pop.ConnectionDetails)) RegistryOption {\n\treturn func(o *options) {\n\t\to.dbOpts = append(o.dbOpts, opts...)\n\t}\n}\n\nfunc SkipNetworkInit(o *options) {\n\to.skipNetworkInit = true\n}\n\nfunc WithJsonnetPool(pool jsonnetsecure.Pool) RegistryOption {\n\treturn func(o *options) {\n\t\to.jsonnetPool = pool\n\t}\n}\n\nfunc WithConfig(config *config.Config) RegistryOption {\n\treturn func(o *options) {\n\t\to.config = config\n\t}\n}\n\nfunc WithConfigOptions(opts ...configx.OptionModifier) RegistryOption {\n\treturn func(o *options) {\n\t\to.configOptions = append(o.configOptions, opts...)\n\t}\n}\n\nfunc WithIdentitySchemaProvider(f func(r Registry) schema.IdentitySchemaProvider) RegistryOption {\n\treturn func(o *options) {\n\t\to.replaceIdentitySchemaProvider = f\n\t}\n}\n\nfunc ReplaceTracer(f func(*otelx.Tracer) *otelx.Tracer) RegistryOption {\n\treturn func(o *options) {\n\t\to.replaceTracer = f\n\t}\n}\n\ntype NewStrategy func(deps any) any\n\n// WithReplaceStrategies adds a strategy to the registry. This is useful if you want to\n// add a custom strategy to the registry. Default strategies with the same\n// name/ID will be overwritten.\nfunc WithReplaceStrategies(s ...NewStrategy) RegistryOption {\n\treturn func(o *options) {\n\t\to.replacementStrategies = append(o.replacementStrategies, s...)\n\t}\n}\n\nfunc WithExtraHooks(hooks map[string]func(config.SelfServiceHook) any) RegistryOption {\n\treturn func(o *options) {\n\t\to.extraHooks = hooks\n\t}\n}\n\ntype NewHandler func(deps any) x.Handler\n\nfunc WithExtraHandlers(handlers ...NewHandler) RegistryOption {\n\treturn func(o *options) {\n\t\to.extraHandlers = handlers\n\t}\n}\n\nfunc Inspect(f func(reg Registry) error) RegistryOption {\n\treturn func(o *options) {\n\t\to.inspect = f\n\t}\n}\n\nfunc WithExtraMigrations(m ...fs.FS) RegistryOption {\n\treturn func(o *options) {\n\t\to.extraMigrations = append(o.extraMigrations, m...)\n\t}\n}\n\nfunc WithExtraGoMigrations(m ...popx.Migration) RegistryOption {\n\treturn func(o *options) {\n\t\to.extraGoMigrations = append(o.extraGoMigrations, m...)\n\t}\n}\n\nfunc WithDisabledMigrationLogging() RegistryOption {\n\treturn func(o *options) {\n\t\to.disableMigrationLogging = true\n\t}\n}\n\nfunc WithServiceLocatorOptions(opts ...servicelocatorx.Option) RegistryOption {\n\treturn func(o *options) {\n\t\to.serviceLocatorOptions = append(o.serviceLocatorOptions, opts...)\n\t}\n}\n\nfunc newOptions(os []RegistryOption) *options {\n\to := new(options)\n\tfor _, f := range os {\n\t\tf(o)\n\t}\n\treturn o\n}\n"
  },
  {
    "path": "driver/registry_default.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cenkalti/backoff\"\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/gorilla/sessions\"\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/lestrrat-go/jwx/jwk\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/urfave/negroni\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/cipher\"\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/persistence/sql\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/logout\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/kratos/selfservice/strategy/lookup\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/selfservice/strategy/passkey\"\n\t\"github.com/ory/kratos/selfservice/strategy/password\"\n\t\"github.com/ory/kratos/selfservice/strategy/profile\"\n\t\"github.com/ory/kratos/selfservice/strategy/totp\"\n\t\"github.com/ory/kratos/selfservice/strategy/webauthn\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/webauthnx\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/healthx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/jwksx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/popx\"\n\t\"github.com/ory/x/prometheusx\"\n\t\"github.com/ory/x/servicelocatorx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\ntype RegistryDefault struct {\n\tl *logrusx.Logger\n\tc *config.Config\n\n\tctxer contextx.Contextualizer\n\n\tinjectedSelfserviceHooks map[string]func(config.SelfServiceHook) interface{}\n\textraHandlerFactories    []NewHandler\n\textraHandlers            []x.Handler\n\tslOptions                *servicelocatorx.Options\n\n\tnosurf         nosurf.Handler\n\ttrc            *otelx.Tracer\n\twriter         herodot.Writer\n\thealthxHandler *healthx.Handler\n\n\tpersister       persistence.Persister\n\tmigrationStatus popx.MigrationStatuses\n\n\thookVerifier           *hook.Verifier\n\thookSessionIssuer      *hook.SessionIssuer\n\thookSessionDestroyer   *hook.SessionDestroyer\n\thookAddressVerifier    *hook.AddressVerifier\n\thookShowVerificationUI *hook.ShowVerificationUIHook\n\n\tidentityHandler        *identity.Handler\n\tidentityValidator      *identity.Validator\n\tidentityManager        *identity.Manager\n\tidentitySchemaProvider schema.IdentitySchemaProvider\n\n\tcourierHandler *courier.Handler\n\n\tcontinuityManager continuity.Manager\n\n\tschemaHandler *schema.Handler\n\n\tsessionHandler   *session.Handler\n\tsessionManager   session.Manager\n\tsessionTokenizer initOnce[*session.Tokenizer]\n\n\tpasswordHasher    initOnce[hash.Hasher]\n\tpasswordValidator initOnce[password.Validator]\n\n\tcrypter initOnce[cipher.Cipher]\n\n\terrorHandler *errorx.Handler\n\terrorManager *errorx.Manager\n\n\tselfserviceRegistrationExecutor            *registration.HookExecutor\n\tselfserviceRegistrationHandler             *registration.Handler\n\tseflserviceRegistrationErrorHandler        *registration.ErrorHandler\n\tselfserviceRegistrationRequestErrorHandler *registration.ErrorHandler\n\n\tselfserviceLoginExecutor            *login.HookExecutor\n\tselfserviceLoginHandler             *login.Handler\n\tselfserviceLoginRequestErrorHandler *login.ErrorHandler\n\n\tselfserviceSettingsHandler      *settings.Handler\n\tselfserviceSettingsErrorHandler *settings.ErrorHandler\n\tselfserviceSettingsExecutor     *settings.HookExecutor\n\n\tselfserviceVerifyErrorHandler   *verification.ErrorHandler\n\tselfserviceVerifyManager        *identity.Manager\n\tselfserviceVerifyHandler        *verification.Handler\n\tselfserviceVerificationExecutor *verification.HookExecutor\n\n\tselfserviceLinkSender *link.Sender\n\tselfserviceCodeSender *code.Sender\n\n\tselfserviceRecoveryErrorHandler *recovery.ErrorHandler\n\tselfserviceRecoveryHandler      *recovery.Handler\n\tselfserviceRecoveryExecutor     *recovery.HookExecutor\n\n\tselfserviceLogoutHandler *logout.Handler\n\n\tselfserviceStrategies            []any\n\treplacementSelfserviceStrategies []NewStrategy\n\n\thydra initOnce[hydra.Hydra]\n\n\tcsrfTokenGenerator nosurfx.CSRFToken\n\n\tjsonnetVMProvider initOnce[jsonnetsecure.VMProvider]\n\tjsonnetPool       jsonnetsecure.Pool\n\tjwkFetcher        initOnce[*jwksx.FetcherNext]\n}\n\nfunc (m *RegistryDefault) JsonnetVM(ctx context.Context) (jsonnetsecure.VM, error) {\n\treturn m.jsonnetVMProvider.Get(func() jsonnetsecure.VMProvider {\n\t\treturn &jsonnetsecure.DefaultProvider{Subcommand: \"jsonnet\", Pool: m.jsonnetPool}\n\t}).JsonnetVM(ctx)\n}\n\nfunc (m *RegistryDefault) Audit() *logrusx.Logger {\n\treturn m.Logger().WithField(\"audience\", \"audit\")\n}\n\nfunc (m *RegistryDefault) RegisterPublicRoutes(ctx context.Context, router *httprouterx.RouterPublic) {\n\tfor _, h := range m.ExtraHandlers() {\n\t\th.RegisterPublicRoutes(router)\n\t}\n\tm.LoginHandler().RegisterPublicRoutes(router)\n\tm.RegistrationHandler().RegisterPublicRoutes(router)\n\tm.LogoutHandler().RegisterPublicRoutes(router)\n\tm.SettingsHandler().RegisterPublicRoutes(router)\n\tm.IdentityHandler().RegisterPublicRoutes(router)\n\tm.CourierHandler().RegisterPublicRoutes(router)\n\tm.SessionHandler().RegisterPublicRoutes(router)\n\tm.SelfServiceErrorHandler().RegisterPublicRoutes(router)\n\tm.SchemaHandler().RegisterPublicRoutes(router)\n\n\tm.RecoveryHandler().RegisterPublicRoutes(router)\n\n\tm.VerificationHandler().RegisterPublicRoutes(router)\n\n\tm.HealthHandler(ctx).SetHealthRoutes(router, false)\n\twebauthnx.RegisterWebauthnRoute(router)\n\n\tfor _, s := range m.selfServiceStrategies() {\n\t\ts, ok := s.(x.PublicHandler)\n\t\tif ok {\n\t\t\ts.RegisterPublicRoutes(router)\n\t\t}\n\t}\n}\n\nfunc (m *RegistryDefault) RegisterAdminRoutes(ctx context.Context, router *httprouterx.RouterAdmin) {\n\tfor _, h := range m.ExtraHandlers() {\n\t\th.RegisterAdminRoutes(router)\n\t}\n\tm.RegistrationHandler().RegisterAdminRoutes(router)\n\tm.LoginHandler().RegisterAdminRoutes(router)\n\tm.LogoutHandler().RegisterAdminRoutes(router)\n\tm.SchemaHandler().RegisterAdminRoutes(router)\n\tm.SettingsHandler().RegisterAdminRoutes(router)\n\tm.IdentityHandler().RegisterAdminRoutes(router)\n\tm.CourierHandler().RegisterAdminRoutes(router)\n\tm.SelfServiceErrorHandler().RegisterAdminRoutes(router)\n\n\tm.RecoveryHandler().RegisterAdminRoutes(router)\n\tm.SessionHandler().RegisterAdminRoutes(router)\n\n\tm.VerificationHandler().RegisterAdminRoutes(router)\n\n\tm.HealthHandler(ctx).SetHealthRoutes(router, true)\n\tm.HealthHandler(ctx).SetVersionRoutes(router)\n\tprometheusx.SetMuxRoutes(router)\n\tconfig.RegisterConfigHashRoute(m, router)\n\n\tfor _, s := range m.selfServiceStrategies() {\n\t\ts, ok := s.(x.AdminHandler)\n\t\tif ok {\n\t\t\ts.RegisterAdminRoutes(router)\n\t\t}\n\t}\n}\n\nfunc (m *RegistryDefault) HTTPMiddlewares() []negroni.Handler {\n\treturn m.slOptions.HTTPMiddlewares()\n}\n\nfunc NewRegistryDefault() *RegistryDefault {\n\tr := &RegistryDefault{\n\t\ttrc:       otelx.NewNoop(),\n\t\tslOptions: &servicelocatorx.Options{},\n\t}\n\tr.initCheapMembers()\n\n\treturn r\n}\n\nfunc (m *RegistryDefault) SetLogger(l *logrusx.Logger) {\n\tm.l = l\n}\n\nfunc (m *RegistryDefault) SetJSONNetVMProvider(p jsonnetsecure.VMProvider) {\n\tm.jsonnetVMProvider.Set(p)\n}\n\nfunc (m *RegistryDefault) LogoutHandler() *logout.Handler {\n\tif m.selfserviceLogoutHandler == nil {\n\t\tm.selfserviceLogoutHandler = logout.NewHandler(m)\n\t}\n\treturn m.selfserviceLogoutHandler\n}\n\nfunc (m *RegistryDefault) HealthHandler(_ context.Context) *healthx.Handler {\n\tif m.healthxHandler == nil {\n\t\tm.healthxHandler = healthx.NewHandler(m.Writer(), config.Version,\n\t\t\thealthx.ReadyCheckers{\n\t\t\t\t\"database\": func(r *http.Request) error {\n\t\t\t\t\treturn m.PingContext(r.Context())\n\t\t\t\t},\n\t\t\t\t\"migrations\": func(r *http.Request) error {\n\t\t\t\t\tif m.migrationStatus != nil && !m.migrationStatus.HasPending() {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\n\t\t\t\t\tstatus, err := m.Persister().MigrationStatus(r.Context())\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\n\t\t\t\t\tif status.HasPending() {\n\t\t\t\t\t\treturn errors.Errorf(\"migrations have not yet been fully applied\")\n\t\t\t\t\t}\n\n\t\t\t\t\tm.migrationStatus = status\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t})\n\t}\n\n\treturn m.healthxHandler\n}\n\nfunc (m *RegistryDefault) WithCSRFHandler(c nosurf.Handler) {\n\tm.nosurf = c\n}\n\nfunc (m *RegistryDefault) CSRFHandler() nosurf.Handler {\n\tif m.nosurf == nil {\n\t\tpanic(\"csrf handler is not set\")\n\t}\n\treturn m.nosurf\n}\n\nfunc (m *RegistryDefault) Config() *config.Config {\n\tif m.c == nil {\n\t\tpanic(\"configuration not set\")\n\t}\n\treturn m.c\n}\n\nfunc (m *RegistryDefault) CourierConfig() config.CourierConfigs {\n\treturn m.Config()\n}\n\nfunc (m *RegistryDefault) selfServiceStrategies() []any {\n\tif len(m.selfserviceStrategies) == 0 {\n\t\tif m.replacementSelfserviceStrategies != nil {\n\t\t\t// Construct self-service strategies from the replacements\n\t\t\tfor _, newStrategy := range m.replacementSelfserviceStrategies {\n\t\t\t\tm.selfserviceStrategies = append(m.selfserviceStrategies, newStrategy(m))\n\t\t\t}\n\t\t} else {\n\t\t\t// Construct the default list of strategies\n\t\t\tm.selfserviceStrategies = []any{\n\t\t\t\tprofile.NewStrategy(m), // <- should remain first\n\t\t\t\tpassword.NewStrategy(m),\n\t\t\t\toidc.NewStrategy(m),\n\t\t\t\tcode.NewStrategy(m),\n\t\t\t\tlink.NewStrategy(m),\n\t\t\t\ttotp.NewStrategy(m),\n\t\t\t\tpasskey.NewStrategy(m),\n\t\t\t\twebauthn.NewStrategy(m),\n\t\t\t\tlookup.NewStrategy(m),\n\t\t\t\tidfirst.NewStrategy(m),\n\t\t\t}\n\t\t}\n\t}\n\n\treturn m.selfserviceStrategies\n}\n\nfunc (m *RegistryDefault) strategyRegistrationEnabled(ctx context.Context, id string) bool {\n\tif id == \"profile\" {\n\t\treturn true\n\t}\n\treturn m.Config().SelfServiceStrategy(ctx, id).Enabled\n}\n\nfunc (m *RegistryDefault) strategyLoginEnabled(ctx context.Context, id string) bool {\n\treturn m.Config().SelfServiceStrategy(ctx, id).Enabled\n}\n\nfunc (m *RegistryDefault) RegistrationStrategies(ctx context.Context, filters ...registration.StrategyFilter) (registrationStrategies registration.Strategies) {\nnextStrategy:\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(registration.Strategy); ok {\n\t\t\tfor _, filter := range filters {\n\t\t\t\tif !filter(s) {\n\t\t\t\t\tcontinue nextStrategy\n\t\t\t\t}\n\t\t\t}\n\t\t\tif m.strategyRegistrationEnabled(ctx, s.ID().String()) {\n\t\t\t\tregistrationStrategies = append(registrationStrategies, s)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (m *RegistryDefault) AllRegistrationStrategies() registration.Strategies {\n\tvar registrationStrategies []registration.Strategy\n\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(registration.Strategy); ok {\n\t\t\tregistrationStrategies = append(registrationStrategies, s)\n\t\t}\n\t}\n\treturn registrationStrategies\n}\n\nfunc (m *RegistryDefault) LoginStrategies(ctx context.Context, filters ...login.StrategyFilter) (loginStrategies login.Strategies) {\nnextStrategy:\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(login.Strategy); ok {\n\t\t\tfor _, filter := range filters {\n\t\t\t\tif !filter(s) {\n\t\t\t\t\tcontinue nextStrategy\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif m.strategyLoginEnabled(ctx, s.ID().String()) {\n\t\t\t\tloginStrategies = append(loginStrategies, s)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (m *RegistryDefault) AllLoginStrategies() login.Strategies {\n\tvar loginStrategies []login.Strategy\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(login.Strategy); ok {\n\t\t\tloginStrategies = append(loginStrategies, s)\n\t\t}\n\t}\n\treturn loginStrategies\n}\n\nfunc (m *RegistryDefault) ActiveCredentialsCounterStrategies(_ context.Context) (activeCredentialsCounterStrategies []identity.ActiveCredentialsCounter) {\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(identity.ActiveCredentialsCounter); ok {\n\t\t\tactiveCredentialsCounterStrategies = append(activeCredentialsCounterStrategies, s)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (m *RegistryDefault) IdentityValidator() *identity.Validator {\n\treturn m.identityValidator\n}\n\nfunc (m *RegistryDefault) SetConfig(c *config.Config) {\n\tm.c = c\n}\n\n// WithSelfserviceStrategies is only available in testing and overrides the\n// selfservice strategies with the given ones.\nfunc (m *RegistryDefault) WithSelfserviceStrategies(t testing.TB, strategies []any) Registry {\n\tif t == nil {\n\t\tpanic(\"Passing selfservice strategies is only supported in testing\")\n\t}\n\tm.selfserviceStrategies = strategies\n\treturn m\n}\n\nfunc (m *RegistryDefault) Writer() herodot.Writer {\n\tif m.writer == nil {\n\t\th := herodot.NewJSONWriter(m.Logger())\n\t\tm.writer = h\n\t}\n\treturn m.writer\n}\n\nfunc (m *RegistryDefault) Logger() *logrusx.Logger {\n\tif m.l == nil {\n\t\tm.l = logrusx.New(\"Ory Kratos\", config.Version)\n\t}\n\treturn m.l\n}\n\nfunc (m *RegistryDefault) IdentityHandler() *identity.Handler {\n\tif m.identityHandler == nil {\n\t\tm.identityHandler = identity.NewHandler(m)\n\t}\n\treturn m.identityHandler\n}\n\nfunc (m *RegistryDefault) CourierHandler() *courier.Handler {\n\tif m.courierHandler == nil {\n\t\tm.courierHandler = courier.NewHandler(m)\n\t}\n\treturn m.courierHandler\n}\n\nfunc (m *RegistryDefault) SchemaHandler() *schema.Handler {\n\tif m.schemaHandler == nil {\n\t\tm.schemaHandler = schema.NewHandler(m)\n\t}\n\treturn m.schemaHandler\n}\n\nfunc (m *RegistryDefault) SessionHandler() *session.Handler {\n\tif m.sessionHandler == nil {\n\t\tm.sessionHandler = session.NewHandler(m)\n\t}\n\treturn m.sessionHandler\n}\n\nfunc (m *RegistryDefault) Cipher(ctx context.Context) cipher.Cipher {\n\treturn m.crypter.Get(func() cipher.Cipher {\n\t\tswitch m.c.CipherAlgorithm(ctx) {\n\t\tcase \"xchacha20-poly1305\":\n\t\t\treturn cipher.NewCryptChaCha20(m.Config())\n\t\tcase \"aes\":\n\t\t\treturn cipher.NewCryptAES(m.Config())\n\t\tdefault:\n\t\t\tm.l.Logger.Warning(\"No encryption configuration found. The default algorithm (noop) will be used, resulting in sensitive data being stored in plaintext\")\n\t\t\treturn cipher.NewNoop()\n\t\t}\n\t})\n}\n\nfunc (m *RegistryDefault) Hasher(ctx context.Context) hash.Hasher {\n\treturn m.passwordHasher.Get(func() hash.Hasher {\n\t\tif m.c.HasherPasswordHashingAlgorithm(ctx) == \"bcrypt\" {\n\t\t\treturn hash.NewHasherBcrypt(m)\n\t\t}\n\t\treturn hash.NewHasherArgon2(m)\n\t})\n}\n\nfunc (m *RegistryDefault) PasswordValidator() password.Validator {\n\tm.passwordValidator.Get(func() password.Validator {\n\t\tval, err := password.NewDefaultPasswordValidatorStrategy(m)\n\t\tif err != nil {\n\t\t\tm.Logger().WithError(err).Fatal(\"could not initialize DefaultPasswordValidator\")\n\t\t}\n\t\treturn val\n\t})\n\treturn m.passwordValidator.value\n}\n\nfunc (m *RegistryDefault) SelfServiceErrorHandler() *errorx.Handler {\n\tif m.errorHandler == nil {\n\t\tm.errorHandler = errorx.NewHandler(m)\n\t}\n\treturn m.errorHandler\n}\n\nfunc (m *RegistryDefault) CookieManager(ctx context.Context) sessions.StoreExact {\n\tvar keys [][]byte\n\tfor _, k := range m.Config().SecretsSession(ctx) {\n\t\tencrypt := sha256.Sum256(k)\n\t\tkeys = append(keys, k, encrypt[:])\n\t}\n\n\tcs := sessions.NewCookieStore(keys...)\n\tcs.Options.Secure = m.Config().SessionCookieSecure(ctx)\n\tcs.Options.HttpOnly = true\n\n\tif domain := m.Config().SessionDomain(ctx); domain != \"\" {\n\t\tcs.Options.Domain = domain\n\t}\n\n\tif path := m.Config().SessionPath(ctx); path != \"\" {\n\t\tcs.Options.Path = path\n\t}\n\n\tif sameSite := m.Config().SessionSameSiteMode(ctx); sameSite != 0 {\n\t\tcs.Options.SameSite = sameSite\n\t}\n\n\tcs.Options.MaxAge = 0\n\tif m.Config().SessionPersistentCookie(ctx) {\n\t\tcs.Options.MaxAge = int(m.Config().SessionLifespan(ctx).Seconds())\n\t\tcs.MaxAge(cs.Options.MaxAge)\n\t}\n\treturn cs\n}\n\nfunc (m *RegistryDefault) ContinuityCookieManager(ctx context.Context) sessions.StoreExact {\n\t// To support hot reloading, this can not be instantiated only once.\n\tcs := sessions.NewCookieStore(m.Config().SecretsSession(ctx)...)\n\tcs.Options.Secure = m.Config().CookieSecure(ctx)\n\tcs.Options.HttpOnly = true\n\tcs.Options.SameSite = http.SameSiteLaxMode\n\treturn cs\n}\n\nfunc (m *RegistryDefault) Tracer(context.Context) *otelx.Tracer {\n\tif m.trc == nil {\n\t\treturn otelx.NewNoop()\n\t}\n\treturn m.trc\n}\n\nfunc (m *RegistryDefault) SetTracer(t *otelx.Tracer) {\n\tm.trc = t\n}\n\nfunc (m *RegistryDefault) SessionManager() session.Manager {\n\treturn m.sessionManager\n}\n\nfunc (m *RegistryDefault) Hydra() hydra.Hydra {\n\treturn m.hydra.Get(func() hydra.Hydra {\n\t\treturn hydra.NewDefaultHydra(m)\n\t})\n}\n\nfunc (m *RegistryDefault) SetHydra(h hydra.Hydra) {\n\tm.hydra.Set(h)\n}\n\nfunc (m *RegistryDefault) SelfServiceErrorManager() *errorx.Manager {\n\treturn m.errorManager\n}\n\nfunc (m *RegistryDefault) Init(ctx context.Context, ctxer contextx.Contextualizer, opts ...RegistryOption) error {\n\tif m.persister != nil {\n\t\t// The DSN connection can not be hot-reloaded!\n\t\tpanic(\"RegistryDefault.Init() must not be called more than once.\")\n\t}\n\n\to := newOptions(opts)\n\n\tm.jsonnetPool = o.jsonnetPool\n\n\tif o.replaceTracer != nil {\n\t\tm.trc = o.replaceTracer(m.trc)\n\t}\n\n\tif o.replacementStrategies != nil {\n\t\tm.replacementSelfserviceStrategies = o.replacementStrategies\n\t}\n\n\tif o.extraHooks != nil {\n\t\tm.WithHooks(o.extraHooks)\n\t}\n\tif o.extraHandlers != nil {\n\t\tm.WithExtraHandlers(o.extraHandlers)\n\t}\n\n\tif o.replaceIdentitySchemaProvider != nil {\n\t\tm.identitySchemaProvider = o.replaceIdentitySchemaProvider(m)\n\t}\n\n\tbc := backoff.NewExponentialBackOff()\n\tbc.MaxElapsedTime = time.Minute * 5\n\tbc.Reset()\n\terr := backoff.Retry(func() error {\n\t\tm.SetContextualizer(ctxer)\n\n\t\tpool, idlePool, connMaxLifetime, connMaxIdleTime, cleanedDSN := sqlcon.ParseConnectionOptions(m.l, m.Config().DSN(ctx))\n\t\tdbOpts := &pop.ConnectionDetails{\n\t\t\tURL:             sqlcon.FinalizeDSN(m.l, cleanedDSN),\n\t\t\tIdlePool:        idlePool,\n\t\t\tConnMaxLifetime: connMaxLifetime,\n\t\t\tConnMaxIdleTime: connMaxIdleTime,\n\t\t\tPool:            pool,\n\t\t\tTracerProvider:  m.Tracer(ctx).Provider(),\n\t\t}\n\n\t\tfor _, f := range o.dbOpts {\n\t\t\tf(dbOpts)\n\t\t}\n\n\t\tscheme, _, _ := sqlxx.ExtractSchemeFromDSN(dbOpts.URL)\n\t\tif !dbOpts.AllowMinPool && scheme == \"postgres\" && strings.Contains(dbOpts.URL, \"pool_min_conns=\") {\n\t\t\terr := errors.Errorf(\"attempting to use the option 'pool_min_conns' with Postgres, but the pgxpool connection pool is disabled, this will be rejected by the database: dsn=%s dbOpts.AllowMinPool=%v\", dbOpts.URL, dbOpts.AllowMinPool)\n\t\t\treturn backoff.Permanent(err)\n\t\t}\n\t\tif (scheme == \"sqlite\" || scheme == \"mysql\") && strings.Contains(dbOpts.URL, \"pool_min_conns=\") {\n\t\t\terr := errors.Errorf(\"attempting to use the option 'pool_min_conns' with %s, but connection pooling is not supported for this case: dsn=%s\", scheme, dbOpts.URL)\n\t\t\treturn backoff.Permanent(err)\n\t\t}\n\n\t\tm.Logger().\n\t\t\tWithField(\"pool\", pool).\n\t\t\tWithField(\"idlePool\", idlePool).\n\t\t\tWithField(\"connMaxLifetime\", connMaxLifetime).\n\t\t\tDebug(\"Connecting to SQL Database\")\n\t\tc, err := pop.NewConnection(dbOpts)\n\t\tif err != nil {\n\t\t\tm.Logger().WithError(err).Warnf(\"Unable to connect to database, retrying.\")\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tif err := c.Open(); err != nil {\n\t\t\tm.Logger().WithError(err).Warnf(\"Unable to open database, retrying.\")\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tp, err := sql.NewPersister(m, c,\n\t\t\tsql.WithExtraMigrations(o.extraMigrations...),\n\t\t\tsql.WithExtraGoMigrations(o.extraGoMigrations...),\n\t\t\tsql.WithDisabledLogging(o.disableMigrationLogging))\n\t\tif err != nil {\n\t\t\tm.Logger().WithError(err).Warnf(\"Unable to initialize persister, retrying.\")\n\t\t\treturn err\n\t\t}\n\n\t\tpingCtx, cancel := context.WithTimeout(ctx, 5*time.Second)\n\t\tdefer cancel()\n\t\tif err := c.Store.SQLDB().PingContext(pingCtx); err != nil {\n\t\t\tm.Logger().WithError(err).Warnf(\"Unable to ping database, retrying.\")\n\t\t\treturn err\n\t\t}\n\n\t\t// if dsn is memory we have to run the migrations on every start\n\t\tif dbal.IsMemorySQLite(m.Config().DSN(ctx)) || m.Config().DSN(ctx) == \"memory\" {\n\t\t\tm.Logger().Infoln(\"Ory Kratos is running migrations on every startup as DSN is memory. This means your data is lost when Kratos terminates.\")\n\t\t\tif err := p.MigrateUp(ctx); err != nil {\n\t\t\t\tm.Logger().WithError(err).Warnf(\"Unable to run migrations, retrying.\")\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif o.skipNetworkInit {\n\t\t\tm.persister = p\n\t\t\treturn nil\n\t\t}\n\n\t\tnet, err := p.DetermineNetwork(ctx)\n\t\tif err != nil {\n\t\t\tm.Logger().WithError(err).Warnf(\"Unable to determine network, retrying.\")\n\t\t\treturn err\n\t\t}\n\n\t\tm.persister = p.WithNetworkID(net.ID)\n\t\treturn nil\n\t}, bc)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif o.inspect != nil {\n\t\tif err := o.inspect(m); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (m *RegistryDefault) SetPersister(p persistence.Persister) {\n\tm.persister = p\n}\n\nfunc (m *RegistryDefault) Courier(ctx context.Context) (courier.Courier, error) {\n\treturn courier.NewCourier(ctx, m)\n}\n\nfunc (m *RegistryDefault) ContinuityManager() continuity.Manager {\n\treturn m.continuityManager\n}\n\nfunc (m *RegistryDefault) Persister() persistence.Persister                      { return m.persister }\nfunc (m *RegistryDefault) ContinuityPersister() continuity.Persister             { return m.persister }\nfunc (m *RegistryDefault) IdentityPool() identity.Pool                           { return m.persister }\nfunc (m *RegistryDefault) PrivilegedIdentityPool() identity.PrivilegedPool       { return m.persister }\nfunc (m *RegistryDefault) RegistrationFlowPersister() registration.FlowPersister { return m.persister }\nfunc (m *RegistryDefault) RecoveryFlowPersister() recovery.FlowPersister         { return m.persister }\nfunc (m *RegistryDefault) LoginFlowPersister() login.FlowPersister               { return m.persister }\nfunc (m *RegistryDefault) SettingsFlowPersister() settings.FlowPersister         { return m.persister }\nfunc (m *RegistryDefault) SelfServiceErrorPersister() errorx.Persister           { return m.persister }\nfunc (m *RegistryDefault) SessionPersister() session.Persister                   { return m.persister }\nfunc (m *RegistryDefault) CourierPersister() courier.Persister                   { return m.persister }\nfunc (m *RegistryDefault) RecoveryTokenPersister() link.RecoveryTokenPersister   { return m.persister }\nfunc (m *RegistryDefault) RecoveryCodePersister() code.RecoveryCodePersister     { return m.persister }\nfunc (m *RegistryDefault) LoginCodePersister() code.LoginCodePersister           { return m.persister }\nfunc (m *RegistryDefault) VerificationTokenPersister() link.VerificationTokenPersister {\n\treturn m.persister\n}\nfunc (m *RegistryDefault) VerificationCodePersister() code.VerificationCodePersister {\n\treturn m.persister\n}\nfunc (m *RegistryDefault) RegistrationCodePersister() code.RegistrationCodePersister {\n\treturn m.persister\n}\nfunc (m *RegistryDefault) TransactionalPersisterProvider() x.TransactionalPersister {\n\treturn m.persister\n}\n\nfunc (m *RegistryDefault) PingContext(ctx context.Context) error {\n\treturn m.persister.Ping(ctx)\n}\n\nfunc (m *RegistryDefault) Ping() error {\n\treturn m.persister.Ping(context.Background())\n}\n\nfunc (m *RegistryDefault) WithCSRFTokenGenerator(cg nosurfx.CSRFToken) {\n\tm.csrfTokenGenerator = cg\n}\n\nfunc (m *RegistryDefault) GenerateCSRFToken(r *http.Request) string {\n\tif m.csrfTokenGenerator == nil {\n\t\tm.csrfTokenGenerator = nosurfx.DefaultCSRFToken\n\t}\n\treturn m.csrfTokenGenerator(r)\n}\n\nfunc (m *RegistryDefault) IdentityManager() *identity.Manager {\n\treturn m.identityManager\n}\n\nfunc (m *RegistryDefault) HTTPClient(_ context.Context, opts ...httpx.ResilientOptions) *retryablehttp.Client {\n\topts = append([]httpx.ResilientOptions{\n\t\thttpx.ResilientClientWithLogger(m.Logger()),\n\t\thttpx.ResilientClientWithMaxRetry(2),\n\t\thttpx.ResilientClientWithConnectionTimeout(30 * time.Second),\n\t}, opts...)\n\n\t// One of the few exceptions, this usually should not be hot reloaded.\n\tif m.Config().ClientHTTPNoPrivateIPRanges(contextx.RootContext) {\n\t\topts = append(\n\t\t\topts,\n\t\t\thttpx.ResilientClientDisallowInternalIPs(),\n\t\t\t// One of the few exceptions, this usually should not be hot reloaded.\n\t\t\thttpx.ResilientClientAllowInternalIPRequestsTo(m.Config().ClientHTTPPrivateIPExceptionURLs(contextx.RootContext)...),\n\t\t)\n\t}\n\treturn httpx.NewResilientClient(opts...)\n}\n\nfunc (m *RegistryDefault) SetContextualizer(ctxer contextx.Contextualizer) {\n\tm.ctxer = ctxer\n}\n\nfunc (m *RegistryDefault) Contextualizer() contextx.Contextualizer {\n\tif m.ctxer == nil {\n\t\tpanic(\"registry Contextualizer not set\")\n\t}\n\treturn m.ctxer\n}\n\nfunc (m *RegistryDefault) JWKSFetcher() *jwksx.FetcherNext {\n\treturn m.jwkFetcher.Get(func() *jwksx.FetcherNext {\n\t\tmaxItems := int64(10_000_000)\n\t\tcache, _ := ristretto.NewCache(&ristretto.Config[[]byte, jwk.Set]{\n\t\t\tNumCounters:        maxItems * 10,\n\t\t\tMaxCost:            maxItems,\n\t\t\tBufferItems:        64,\n\t\t\tMetrics:            true,\n\t\t\tIgnoreInternalCost: true,\n\t\t\tCost: func(value jwk.Set) int64 {\n\t\t\t\treturn 1\n\t\t\t},\n\t\t})\n\t\treturn jwksx.NewFetcherNext(cache)\n\t})\n}\n\nfunc (m *RegistryDefault) SessionTokenizer() *session.Tokenizer {\n\treturn m.sessionTokenizer.Get(func() *session.Tokenizer { return session.NewTokenizer(m) })\n}\n\nfunc (m *RegistryDefault) ExtraHandlers() []x.Handler {\n\tif m.extraHandlers == nil {\n\t\tfor _, newHandler := range m.extraHandlerFactories {\n\t\t\tm.extraHandlers = append(m.extraHandlers, newHandler(m))\n\t\t}\n\t}\n\treturn m.extraHandlers\n}\n\n// initCheapMembers initializes members that are cheap to initialize.\nfunc (m *RegistryDefault) initCheapMembers() {\n\tm.identityValidator = identity.NewValidator(m)\n\tm.identityManager = identity.NewManager(m)\n\tm.sessionManager = session.NewManagerHTTP(m)\n\tm.errorManager = errorx.NewManager(m)\n\tm.continuityManager = continuity.NewManagerCookie(m)\n}\n\ntype initOnce[T any] struct {\n\tvalue T\n\tinit  sync.Once\n}\n\n// Set sets the value of the initOnce, marking it as initialized.\nfunc (o *initOnce[T]) Set(val T) {\n\t// We need to both set the value and run the init function to mark the value as\n\t// initialized.\n\to.init.Do(func() { o.value = val })\n\to.value = val\n}\n\n// Get returns the value of the initOnce, initializing it with newT if it has not\n// been initialized yet. `newT` is only called once, even if Get is called\n// concurrently from multiple goroutines or with different `newT` functions.\nfunc (o *initOnce[T]) Get(newT func() T) T {\n\to.init.Do(func() { o.value = newT() })\n\treturn o.value\n}\n"
  },
  {
    "path": "driver/registry_default_hooks.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/request\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n)\n\nfunc (m *RegistryDefault) HookVerifier() *hook.Verifier {\n\tif m.hookVerifier == nil {\n\t\tm.hookVerifier = hook.NewVerifier(m)\n\t}\n\treturn m.hookVerifier\n}\n\nfunc (m *RegistryDefault) HookSessionIssuer() *hook.SessionIssuer {\n\tif m.hookSessionIssuer == nil {\n\t\tm.hookSessionIssuer = hook.NewSessionIssuer(m)\n\t}\n\treturn m.hookSessionIssuer\n}\n\nfunc (m *RegistryDefault) HookSessionDestroyer() *hook.SessionDestroyer {\n\tif m.hookSessionDestroyer == nil {\n\t\tm.hookSessionDestroyer = hook.NewSessionDestroyer(m)\n\t}\n\treturn m.hookSessionDestroyer\n}\n\nfunc (m *RegistryDefault) HookAddressVerifier() *hook.AddressVerifier {\n\tif m.hookAddressVerifier == nil {\n\t\tm.hookAddressVerifier = hook.NewAddressVerifier(m)\n\t}\n\treturn m.hookAddressVerifier\n}\n\nfunc (m *RegistryDefault) HookShowVerificationUI() *hook.ShowVerificationUIHook {\n\tif m.hookShowVerificationUI == nil {\n\t\tm.hookShowVerificationUI = hook.NewShowVerificationUIHook(m)\n\t}\n\treturn m.hookShowVerificationUI\n}\n\nfunc (m *RegistryDefault) WithHooks(hooks map[string]func(config.SelfServiceHook) interface{}) {\n\tm.injectedSelfserviceHooks = hooks\n}\nfunc (m *RegistryDefault) WithExtraHandlers(handlers []NewHandler) {\n\tm.extraHandlerFactories = handlers\n}\n\nfunc getHooks[T any](m *RegistryDefault, credentialsType string, configs []config.SelfServiceHook) ([]T, error) {\n\thooks := make([]T, 0, len(configs))\n\n\tvar addSessionIssuer bool\nallHooksLoop:\n\tfor _, h := range configs {\n\t\tswitch h.Name {\n\t\tcase hook.KeySessionIssuer:\n\t\t\t// The session issuer hook always needs to come last.\n\t\t\taddSessionIssuer = true\n\t\tcase hook.KeySessionDestroyer:\n\t\t\tif h, ok := any(m.HookSessionDestroyer()).(T); ok {\n\t\t\t\thooks = append(hooks, h)\n\t\t\t}\n\t\tcase hook.KeyWebHook:\n\t\t\tcfg := request.Config{}\n\t\t\tif err := json.Unmarshal(h.Config, &cfg); err != nil {\n\t\t\t\tm.l.WithError(err).WithField(\"raw_config\", string(h.Config)).Error(\"failed to unmarshal hook configuration, ignoring hook\")\n\t\t\t\treturn nil, errors.WithStack(fmt.Errorf(\"failed to unmarshal webhook configuration for %s: %w\", credentialsType, err))\n\t\t\t}\n\t\t\tif h, ok := any(hook.NewWebHook(m, &cfg)).(T); ok {\n\t\t\t\thooks = append(hooks, h)\n\t\t\t}\n\t\tcase hook.KeyRequireVerifiedAddress:\n\t\t\tif h, ok := any(m.HookAddressVerifier()).(T); ok {\n\t\t\t\thooks = append(hooks, h)\n\t\t\t}\n\t\tcase hook.KeyVerificationUI:\n\t\t\tif h, ok := any(m.HookShowVerificationUI()).(T); ok {\n\t\t\t\thooks = append(hooks, h)\n\t\t\t}\n\t\tcase hook.KeyVerifier:\n\t\t\tif h, ok := any(m.HookVerifier()).(T); ok {\n\t\t\t\thooks = append(hooks, h)\n\t\t\t}\n\t\tdefault:\n\t\t\tfor name, m := range m.injectedSelfserviceHooks {\n\t\t\t\tif name == h.Name {\n\t\t\t\t\tif h, ok := m(h).(T); ok {\n\t\t\t\t\t\thooks = append(hooks, h)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue allHooksLoop\n\t\t\t\t}\n\t\t\t}\n\t\t\tm.l.\n\t\t\t\tWithField(\"for\", credentialsType).\n\t\t\t\tWithField(\"hook\", h.Name).\n\t\t\t\tWarn(\"A configuration for a non-existing hook was found and will be ignored.\")\n\t\t}\n\t}\n\tif addSessionIssuer {\n\t\tif h, ok := any(m.HookSessionIssuer()).(T); ok {\n\t\t\thooks = append(hooks, h)\n\t\t}\n\t}\n\n\treturn hooks, nil\n}\n"
  },
  {
    "path": "driver/registry_default_login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n)\n\nfunc (m *RegistryDefault) LoginHookExecutor() *login.HookExecutor {\n\tif m.selfserviceLoginExecutor == nil {\n\t\tm.selfserviceLoginExecutor = login.NewHookExecutor(m)\n\t}\n\treturn m.selfserviceLoginExecutor\n}\n\nfunc (m *RegistryDefault) PreLoginHooks(ctx context.Context) ([]login.PreHookExecutor, error) {\n\treturn getHooks[login.PreHookExecutor](m, \"\", m.Config().SelfServiceFlowLoginBeforeHooks(ctx))\n}\n\nfunc (m *RegistryDefault) PostLoginHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]login.PostHookExecutor, error) {\n\thooks, err := getHooks[login.PostHookExecutor](m, string(credentialsType), m.Config().SelfServiceFlowLoginAfterHooks(ctx, string(credentialsType)))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(hooks) > 0 {\n\t\treturn hooks, nil\n\t}\n\n\t// since we don't want merging hooks defined in a specific strategy and global hooks\n\t// global hooks are added only if no strategy specific hooks are defined\n\treturn getHooks[login.PostHookExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowLoginAfterHooks(ctx, config.HookGlobal))\n}\n\nfunc (m *RegistryDefault) LoginHandler() *login.Handler {\n\tif m.selfserviceLoginHandler == nil {\n\t\tm.selfserviceLoginHandler = login.NewHandler(m)\n\t}\n\n\treturn m.selfserviceLoginHandler\n}\n\nfunc (m *RegistryDefault) LoginFlowErrorHandler() *login.ErrorHandler {\n\tif m.selfserviceLoginRequestErrorHandler == nil {\n\t\tm.selfserviceLoginRequestErrorHandler = login.NewFlowErrorHandler(m)\n\t}\n\n\treturn m.selfserviceLoginRequestErrorHandler\n}\n"
  },
  {
    "path": "driver/registry_default_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n)\n\nfunc (m *RegistryDefault) RecoveryFlowErrorHandler() *recovery.ErrorHandler {\n\tif m.selfserviceRecoveryErrorHandler == nil {\n\t\tm.selfserviceRecoveryErrorHandler = recovery.NewErrorHandler(m)\n\t}\n\n\treturn m.selfserviceRecoveryErrorHandler\n}\n\nfunc (m *RegistryDefault) RecoveryHandler() *recovery.Handler {\n\tif m.selfserviceRecoveryHandler == nil {\n\t\tm.selfserviceRecoveryHandler = recovery.NewHandler(m)\n\t}\n\n\treturn m.selfserviceRecoveryHandler\n}\n\nfunc (m *RegistryDefault) RecoveryStrategies(ctx context.Context) (recoveryStrategies recovery.Strategies) {\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(recovery.Strategy); ok {\n\t\t\tif m.Config().SelfServiceStrategy(ctx, s.RecoveryStrategyID()).Enabled {\n\t\t\t\trecoveryStrategies = append(recoveryStrategies, s)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// GetActiveRecoveryStrategies returns the currently active recovery strategies.\n// It returns a list of all strategies and the specific primary strategy.\n// If no primary recovery strategy has been set, an error is returned.\nfunc (m *RegistryDefault) GetActiveRecoveryStrategies(ctx context.Context) (active recovery.Strategies, primary recovery.Strategy, err error) {\n\tas := m.Config().SelfServiceFlowRecoveryUse(ctx)\n\ts, ps, err := m.RecoveryStrategies(ctx).ActiveStrategies(as)\n\tif err != nil {\n\t\treturn nil, ps, errors.WithStack(herodot.ErrBadRequest.\n\t\t\tWithReasonf(\"You attempted recovery using %s, which is not enabled or does not exist. An administrator needs to enable this recovery method.\", as))\n\t}\n\treturn s, ps, nil\n}\n\nfunc (m *RegistryDefault) AllRecoveryStrategies() (recoveryStrategies recovery.Strategies) {\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(recovery.Strategy); ok {\n\t\t\trecoveryStrategies = append(recoveryStrategies, s)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (m *RegistryDefault) RecoveryExecutor() *recovery.HookExecutor {\n\tif m.selfserviceRecoveryExecutor == nil {\n\t\tm.selfserviceRecoveryExecutor = recovery.NewHookExecutor(m)\n\t}\n\treturn m.selfserviceRecoveryExecutor\n}\n\nfunc (m *RegistryDefault) PreRecoveryHooks(ctx context.Context) ([]recovery.PreHookExecutor, error) {\n\treturn getHooks[recovery.PreHookExecutor](m, \"\", m.Config().SelfServiceFlowRecoveryBeforeHooks(ctx))\n}\n\nfunc (m *RegistryDefault) PostRecoveryHooks(ctx context.Context) ([]recovery.PostHookExecutor, error) {\n\treturn getHooks[recovery.PostHookExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowRecoveryAfterHooks(ctx, config.HookGlobal))\n}\n\nfunc (m *RegistryDefault) CodeSender() *code.Sender {\n\tif m.selfserviceCodeSender == nil {\n\t\tm.selfserviceCodeSender = code.NewSender(m)\n\t}\n\n\treturn m.selfserviceCodeSender\n}\n"
  },
  {
    "path": "driver/registry_default_registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\t\"slices\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n)\n\nfunc (m *RegistryDefault) PostRegistrationPrePersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]registration.PostHookPrePersistExecutor, error) {\n\thooks, err := getHooks[registration.PostHookPrePersistExecutor](m, string(credentialsType), m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, string(credentialsType)))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn hooks, nil\n}\n\nfunc (m *RegistryDefault) PostRegistrationPostPersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]registration.PostHookPostPersistExecutor, error) {\n\thooks, err := getHooks[registration.PostHookPostPersistExecutor](m, string(credentialsType), m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, string(credentialsType)))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(hooks) == 0 {\n\t\t// since we don't want merging hooks defined in a specific strategy and\n\t\t// global hooks are added only if no strategy specific hooks are defined\n\t\thooks, err = getHooks[registration.PostHookPostPersistExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowRegistrationAfterHooks(ctx, config.HookGlobal))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// WARNING - If you remove this, no verification emails / sms will be sent post-registration.\n\tif m.Config().SelfServiceFlowVerificationEnabled(ctx) {\n\t\thooks = slices.Insert(hooks, 0, registration.PostHookPostPersistExecutor(m.HookVerifier()))\n\t}\n\n\treturn hooks, nil\n}\n\nfunc (m *RegistryDefault) PreRegistrationHooks(ctx context.Context) ([]registration.PreHookExecutor, error) {\n\treturn getHooks[registration.PreHookExecutor](m, \"\", m.Config().SelfServiceFlowRegistrationBeforeHooks(ctx))\n}\n\nfunc (m *RegistryDefault) RegistrationExecutor() *registration.HookExecutor {\n\tif m.selfserviceRegistrationExecutor == nil {\n\t\tm.selfserviceRegistrationExecutor = registration.NewHookExecutor(m)\n\t}\n\treturn m.selfserviceRegistrationExecutor\n}\n\nfunc (m *RegistryDefault) RegistrationHookExecutor() *registration.HookExecutor {\n\tif m.selfserviceRegistrationExecutor == nil {\n\t\tm.selfserviceRegistrationExecutor = registration.NewHookExecutor(m)\n\t}\n\treturn m.selfserviceRegistrationExecutor\n}\n\nfunc (m *RegistryDefault) RegistrationErrorHandler() *registration.ErrorHandler {\n\tif m.seflserviceRegistrationErrorHandler == nil {\n\t\tm.seflserviceRegistrationErrorHandler = registration.NewErrorHandler(m)\n\t}\n\treturn m.seflserviceRegistrationErrorHandler\n}\n\nfunc (m *RegistryDefault) RegistrationHandler() *registration.Handler {\n\tif m.selfserviceRegistrationHandler == nil {\n\t\tm.selfserviceRegistrationHandler = registration.NewHandler(m)\n\t}\n\n\treturn m.selfserviceRegistrationHandler\n}\n\nfunc (m *RegistryDefault) RegistrationFlowErrorHandler() *registration.ErrorHandler {\n\tif m.selfserviceRegistrationRequestErrorHandler == nil {\n\t\tm.selfserviceRegistrationRequestErrorHandler = registration.NewErrorHandler(m)\n\t}\n\n\treturn m.selfserviceRegistrationRequestErrorHandler\n}\n"
  },
  {
    "path": "driver/registry_default_schemas.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/schema\"\n)\n\nfunc (m *RegistryDefault) IdentityTraitsSchemas(ctx context.Context) (schema.IdentitySchemaList, error) {\n\tif m.identitySchemaProvider == nil {\n\t\tm.identitySchemaProvider = schema.NewDefaultIdentityTraitsProvider(m)\n\t}\n\treturn m.identitySchemaProvider.IdentityTraitsSchemas(ctx)\n}\n"
  },
  {
    "path": "driver/registry_default_schemas_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestRegistryDefault_IdentityTraitsSchemas(t *testing.T) {\n\tctx := context.Background()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tdefaultSchema := schema.Schema{\n\t\tID:     \"default\",\n\t\tURL:    urlx.ParseOrPanic(\"file://default.schema.json\"),\n\t\tRawURL: \"file://default.schema.json\",\n\t}\n\taltSchema := schema.Schema{\n\t\tID:     \"alt\",\n\t\tURL:    urlx.ParseOrPanic(\"file://other.schema.json\"),\n\t\tRawURL: \"file://other.schema.json\",\n\t}\n\n\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, []config.Schema{\n\t\t{ID: altSchema.ID, URL: altSchema.RawURL},\n\t\t{ID: defaultSchema.ID, URL: defaultSchema.RawURL},\n\t})\n\n\tss, err := reg.IdentityTraitsSchemas(context.Background())\n\trequire.NoError(t, err)\n\tassert.Equal(t, 2, ss.Total())\n\tassert.Contains(t, ss, defaultSchema)\n\tassert.Contains(t, ss, altSchema)\n}\n"
  },
  {
    "path": "driver/registry_default_sessiontokenexchange.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport \"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\nfunc (m *RegistryDefault) SessionTokenExchangePersister() sessiontokenexchange.Persister {\n\treturn m.Persister()\n}\n"
  },
  {
    "path": "driver/registry_default_settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\t\"slices\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n)\n\nfunc (m *RegistryDefault) PostSettingsPrePersistHooks(ctx context.Context, settingsType string) ([]settings.PostHookPrePersistExecutor, error) {\n\treturn getHooks[settings.PostHookPrePersistExecutor](m, settingsType, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, settingsType))\n}\n\nfunc (m *RegistryDefault) PreSettingsHooks(ctx context.Context) ([]settings.PreHookExecutor, error) {\n\treturn getHooks[settings.PreHookExecutor](m, \"\", m.Config().SelfServiceFlowSettingsBeforeHooks(ctx))\n}\n\nfunc (m *RegistryDefault) PostSettingsPostPersistHooks(ctx context.Context, settingsType string) ([]settings.PostHookPostPersistExecutor, error) {\n\thooks, err := getHooks[settings.PostHookPostPersistExecutor](m, settingsType, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, settingsType))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(hooks) == 0 {\n\t\t// since we don't want merging hooks defined in a specific strategy and\n\t\t// global hooks are added only if no strategy specific hooks are defined\n\t\thooks, err = getHooks[settings.PostHookPostPersistExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowSettingsAfterHooks(ctx, config.HookGlobal))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif m.Config().SelfServiceFlowVerificationEnabled(ctx) {\n\t\thooks = slices.Insert(hooks, 0, settings.PostHookPostPersistExecutor(m.HookVerifier()))\n\t}\n\n\treturn hooks, nil\n}\n\nfunc (m *RegistryDefault) SettingsHookExecutor() *settings.HookExecutor {\n\tif m.selfserviceSettingsExecutor == nil {\n\t\tm.selfserviceSettingsExecutor = settings.NewHookExecutor(m)\n\t}\n\treturn m.selfserviceSettingsExecutor\n}\n\nfunc (m *RegistryDefault) SettingsHandler() *settings.Handler {\n\tif m.selfserviceSettingsHandler == nil {\n\t\tm.selfserviceSettingsHandler = settings.NewHandler(m)\n\t}\n\treturn m.selfserviceSettingsHandler\n}\n\nfunc (m *RegistryDefault) SettingsFlowErrorHandler() *settings.ErrorHandler {\n\tif m.selfserviceSettingsErrorHandler == nil {\n\t\tm.selfserviceSettingsErrorHandler = settings.NewErrorHandler(m)\n\t}\n\treturn m.selfserviceSettingsErrorHandler\n}\n\nfunc (m *RegistryDefault) SettingsStrategies(ctx context.Context) (profileStrategies settings.Strategies) {\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(settings.Strategy); ok {\n\t\t\tif m.Config().SelfServiceStrategy(ctx, s.SettingsStrategyID()).Enabled {\n\t\t\t\tprofileStrategies = append(profileStrategies, s)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (m *RegistryDefault) AllSettingsStrategies() settings.Strategies {\n\tvar profileStrategies []settings.Strategy\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(settings.Strategy); ok {\n\t\t\tprofileStrategies = append(profileStrategies, s)\n\t\t}\n\t}\n\treturn profileStrategies\n}\n"
  },
  {
    "path": "driver/registry_default_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/request\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n)\n\nfunc TestDriverDefault_Hooks(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tt.Run(\"type=verification\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// BEFORE hooks\n\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []verification.PreHookExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc: \"No hooks configured\",\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []verification.PreHookExecutor {\n\t\t\t\t\treturn []verification.PreHookExecutor{}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Two web_hooks are configured\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceVerificationBeforeHooks: []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []verification.PreHookExecutor {\n\t\t\t\t\treturn []verification.PreHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"before/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\th, err := reg.PreVerificationHooks(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\n\t\t// AFTER hooks\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tprep   func(conf *config.Config)\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []verification.PostHookExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc:   \"No hooks configured\",\n\t\t\t\tprep: func(conf *config.Config) {},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []verification.PostHookExecutor {\n\t\t\t\t\treturn []verification.PostHookExecutor{}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Multiple web_hooks configured\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceVerificationAfter + \".hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []verification.PostHookExecutor {\n\t\t\t\t\treturn []verification.PostHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"after/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\th, err := reg.PostVerificationHooks(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"type=recovery\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// BEFORE hooks\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []recovery.PreHookExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc:     \"No hooks configured\",\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []recovery.PreHookExecutor { return []recovery.PreHookExecutor{} },\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Two web_hooks are configured\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceRecoveryBeforeHooks: []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []recovery.PreHookExecutor {\n\t\t\t\t\treturn []recovery.PreHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"before/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\th, err := reg.PreRecoveryHooks(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\n\t\t// AFTER hooks\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []recovery.PostHookExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc:     \"No hooks configured\",\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []recovery.PostHookExecutor { return []recovery.PostHookExecutor{} },\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Multiple web_hooks configured\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceRecoveryAfter + \".hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []recovery.PostHookExecutor {\n\t\t\t\t\treturn []recovery.PostHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"after/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\th, err := reg.PostRecoveryHooks(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"type=registration\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// BEFORE hooks\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []registration.PreHookExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc: \"No hooks configured\",\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []registration.PreHookExecutor {\n\t\t\t\t\treturn []registration.PreHookExecutor{}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Two web_hooks are configured\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceRegistrationBeforeHooks: []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []registration.PreHookExecutor {\n\t\t\t\t\treturn []registration.PreHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"before/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\th, err := reg.PreRegistrationHooks(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.EqualValues(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\n\t\t// AFTER hooks\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc: \"No hooks configured\",\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []registration.PostHookPostPersistExecutor{}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Only session hook configured for password strategy\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceVerificationEnabled: true,\n\t\t\t\t\tconfig.ViperKeySelfServiceRegistrationAfter + \".password.hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"session\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []registration.PostHookPostPersistExecutor{\n\t\t\t\t\t\thook.NewVerifier(reg),\n\t\t\t\t\t\thook.NewSessionIssuer(reg),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"A session hook and a web_hook are configured for password strategy\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceVerificationEnabled: true,\n\t\t\t\t\tconfig.ViperKeySelfServiceRegistrationAfter + \".password.hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"headers\": map[string]string{\"X-Custom-Header\": \"test\"}, \"url\": \"foo\", \"method\": \"POST\", \"body\": \"bar\"}},\n\t\t\t\t\t\t{\"hook\": \"session\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []registration.PostHookPostPersistExecutor{\n\t\t\t\t\t\thook.NewVerifier(reg),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{URL: \"foo\", Method: \"POST\", TemplateURI: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewSessionIssuer(reg),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Two web_hooks are configured on a global level\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceRegistrationAfter + \".hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []registration.PostHookPostPersistExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Hooks are configured on a global level, as well as on a strategy level\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceRegistrationAfter + \".password.hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"session\"},\n\t\t\t\t\t},\n\t\t\t\t\tconfig.ViperKeySelfServiceRegistrationAfter + \".hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t\tconfig.ViperKeySelfServiceVerificationEnabled: true,\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []registration.PostHookPostPersistExecutor{\n\t\t\t\t\t\thook.NewVerifier(reg),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewSessionIssuer(reg),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"show_verification_ui is configured\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceRegistrationAfter + \".hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"show_verification_ui\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []registration.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []registration.PostHookPostPersistExecutor{\n\t\t\t\t\t\thook.NewShowVerificationUIHook(reg),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"after/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\th, err := reg.PostRegistrationPostPersistHooks(ctx, identity.CredentialsTypePassword)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"type=login\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// BEFORE hooks\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []login.PreHookExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc:     \"No hooks configured\",\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []login.PreHookExecutor { return []login.PreHookExecutor{} },\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Two web_hooks are configured\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginBeforeHooks: []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []login.PreHookExecutor {\n\t\t\t\t\treturn []login.PreHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"before/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\th, err := reg.PreLoginHooks(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\n\t\t// AFTER hooks\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []login.PostHookExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc:     \"No hooks configured\",\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []login.PostHookExecutor { return []login.PostHookExecutor{} },\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Only revoke_active_sessions hook configured for password strategy\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginAfter + \".password.hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"revoke_active_sessions\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []login.PostHookExecutor {\n\t\t\t\t\treturn []login.PostHookExecutor{\n\t\t\t\t\t\thook.NewSessionDestroyer(reg),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Only require_verified_address hook configured for password strategy\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginAfter + \".password.hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"require_verified_address\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []login.PostHookExecutor {\n\t\t\t\t\treturn []login.PostHookExecutor{\n\t\t\t\t\t\thook.NewAddressVerifier(reg),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"A revoke_active_sessions hook, require_verified_address hook and a web_hook are configured for password strategy\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginAfter + \".password.hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"headers\": map[string]string{\"X-Custom-Header\": \"test\"}, \"url\": \"foo\", \"method\": \"POST\", \"body\": \"bar\"}},\n\t\t\t\t\t\t{\"hook\": \"require_verified_address\"},\n\t\t\t\t\t\t{\"hook\": \"revoke_active_sessions\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []login.PostHookExecutor {\n\t\t\t\t\treturn []login.PostHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{TemplateURI: \"bar\", Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewAddressVerifier(reg),\n\t\t\t\t\t\thook.NewSessionDestroyer(reg),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Two web_hooks are configured on a global level\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginAfter + \".hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []login.PostHookExecutor {\n\t\t\t\t\treturn []login.PostHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Hooks are configured on a global level, as well as on a strategy level\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginAfter + \".password.hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"revoke_active_sessions\"},\n\t\t\t\t\t\t{\"hook\": \"require_verified_address\"},\n\t\t\t\t\t},\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginAfter + \".hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []login.PostHookExecutor {\n\t\t\t\t\treturn []login.PostHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewSessionDestroyer(reg),\n\t\t\t\t\t\thook.NewAddressVerifier(reg),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"after/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\th, err := reg.PostLoginHooks(ctx, identity.CredentialsTypePassword)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"type=settings\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// BEFORE hooks\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []settings.PreHookExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc:     \"No hooks configured\",\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []settings.PreHookExecutor { return []settings.PreHookExecutor{} },\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Two web_hooks are configured\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceSettingsBeforeHooks: []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []settings.PreHookExecutor {\n\t\t\t\t\treturn []settings.PreHookExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"before/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\th, err := reg.PreSettingsHooks(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\n\t\t// AFTER hooks\n\t\tfor _, tc := range []struct {\n\t\t\tuc     string\n\t\t\tconfig map[string]any\n\t\t\texpect func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor\n\t\t}{\n\t\t\t{\n\t\t\t\tuc: \"No hooks configured\",\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []settings.PostHookPostPersistExecutor{}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Only verify hook configured for the strategy\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceVerificationEnabled: true,\n\t\t\t\t\t// I think this is a bug as there is a hook named verify defined for both profile and password\n\t\t\t\t\t// strategies. Instead of using it, the code makes use of the property used above and which\n\t\t\t\t\t// is defined in an entirely different flow (verification).\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []settings.PostHookPostPersistExecutor{\n\t\t\t\t\t\thook.NewVerifier(reg),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"A verify hook and a web_hook are configured for profile strategy\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceSettingsAfter + \".profile.hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"headers\": map[string]string{\"X-Custom-Header\": \"test\"}, \"url\": \"foo\", \"method\": \"POST\", \"body\": \"bar\"}},\n\t\t\t\t\t},\n\t\t\t\t\tconfig.ViperKeySelfServiceVerificationEnabled: true,\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []settings.PostHookPostPersistExecutor{\n\t\t\t\t\t\thook.NewVerifier(reg),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{TemplateURI: \"bar\", Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Two web_hooks are configured on a global level\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceSettingsAfter + \".hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"bar\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []settings.PostHookPostPersistExecutor{\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"POST\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"bar\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tuc: \"Hooks are configured on a global level, as well as on a strategy level\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceVerificationEnabled: true,\n\t\t\t\t\tconfig.ViperKeySelfServiceSettingsAfter + \".profile.hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"GET\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t\tconfig.ViperKeySelfServiceSettingsAfter + \".hooks\": []map[string]any{\n\t\t\t\t\t\t{\"hook\": \"web_hook\", \"config\": map[string]any{\"url\": \"foo\", \"method\": \"POST\", \"headers\": map[string]string{\"X-Custom-Header\": \"test\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: func(reg *driver.RegistryDefault) []settings.PostHookPostPersistExecutor {\n\t\t\t\t\treturn []settings.PostHookPostPersistExecutor{\n\t\t\t\t\t\thook.NewVerifier(reg),\n\t\t\t\t\t\thook.NewWebHook(reg, &request.Config{Method: \"GET\", URL: \"foo\", Headers: map[string]string{\"X-Custom-Header\": \"test\"}}),\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"after/uc=%s\", tc.uc), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\th, err := reg.PostSettingsPostPersistHooks(ctx, \"profile\")\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expect(reg), h)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestDriverDefault_Strategies(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tt.Run(\"case=registration\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfor _, tc := range []struct {\n\t\t\tname   string\n\t\t\tconfig map[string]any\n\t\t\texpect []string\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"no strategies\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": false,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     false,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"profile\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"only password\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     false,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"profile\", \"password\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"oidc and password\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".oidc.enabled\":     true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     false,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"profile\", \"password\", \"oidc\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"oidc, password and totp\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".oidc.enabled\":     true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".totp.enabled\":     true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     false,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"profile\", \"password\", \"oidc\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"password and code\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     true,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"profile\", \"password\", \"code\"},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"subcase=%s\", tc.name), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\ts := reg.RegistrationStrategies(ctx)\n\t\t\t\trequire.Len(t, s, len(tc.expect))\n\t\t\t\tfor k, e := range tc.expect {\n\t\t\t\t\tassert.Equal(t, e, s[k].ID().String())\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=login\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfor _, tc := range []struct {\n\t\t\tname   string\n\t\t\tconfig map[string]any\n\t\t\texpect []string\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"no strategies\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": false,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     false,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"only password\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     false,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"password\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"oidc and password\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".oidc.enabled\":     true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     false,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"password\", \"oidc\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"oidc, password and totp\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".oidc.enabled\":     true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".totp.enabled\":     true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     false,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"password\", \"oidc\", \"totp\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"password and code\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":     true,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"password\", \"code\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"code is enabled if passwordless_enabled is true\",\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\":          false,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\":              false,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.passwordless_enabled\": true,\n\t\t\t\t},\n\t\t\t\texpect: []string{\"code\"},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"run=%s\", tc.name), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\ts := reg.LoginStrategies(ctx)\n\t\t\t\trequire.Len(t, s, len(tc.expect))\n\t\t\t\tfor k, e := range tc.expect {\n\t\t\t\t\tassert.Equal(t, e, s[k].ID().String())\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=recovery\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfor k, tc := range []struct {\n\t\t\tconfig map[string]any\n\t\t\texpect []string\n\t\t}{\n\t\t\t{\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\": false,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".link.enabled\": false,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: map[string]any{\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".code.enabled\": true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".link.enabled\": true,\n\t\t\t\t}, expect: []string{\"code\", \"link\"},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"run=%d\", k), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tctx := contextx.WithConfigValues(ctx, tc.config)\n\n\t\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\ts := reg.RecoveryStrategies(ctx)\n\t\t\t\trequire.Len(t, s, len(tc.expect))\n\t\t\t\tfor k, e := range tc.expect {\n\t\t\t\t\tassert.Equal(t, e, s[k].RecoveryStrategyID())\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=settings\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tl := logrusx.New(\"\", \"\")\n\n\t\tfor k, tc := range []struct {\n\t\t\tconfigOptions []configx.OptionModifier\n\t\t\texpect        []string\n\t\t}{\n\t\t\t{\n\t\t\t\tconfigOptions: []configx.OptionModifier{configx.WithValues(map[string]any{\n\t\t\t\t\tconfig.ViperKeyDSN: config.DefaultSQLiteMemoryDSN,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": false,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".oidc.enabled\":     false,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".profile.enabled\":  false,\n\t\t\t\t})},\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfigOptions: []configx.OptionModifier{configx.WithValues(map[string]any{\n\t\t\t\t\tconfig.ViperKeyDSN: config.DefaultSQLiteMemoryDSN,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".profile.enabled\":  true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": false,\n\t\t\t\t})},\n\t\t\t\texpect: []string{\"profile\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfigOptions: []configx.OptionModifier{configx.WithValues(map[string]any{\n\t\t\t\t\tconfig.ViperKeyDSN: config.DefaultSQLiteMemoryDSN,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".profile.enabled\":  true,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".password.enabled\": false,\n\t\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".totp.enabled\":     true,\n\t\t\t\t})},\n\t\t\t\texpect: []string{\"profile\", \"totp\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfigOptions: []configx.OptionModifier{configx.WithValues(map[string]any{\n\t\t\t\t\tconfig.ViperKeyDSN: config.DefaultSQLiteMemoryDSN,\n\t\t\t\t})},\n\t\t\t\texpect: []string{\"profile\", \"password\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfigOptions: []configx.OptionModifier{\n\t\t\t\t\tconfigx.WithConfigFiles(\"../test/e2e/profiles/verification/.kratos.yml\"),\n\t\t\t\t\tconfigx.WithValue(config.ViperKeyDSN, config.DefaultSQLiteMemoryDSN),\n\t\t\t\t},\n\t\t\t\texpect: []string{\"profile\", \"password\"},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"run=%d\", k), func(t *testing.T) {\n\t\t\t\tconf := config.MustNew(t, l, &contextx.Default{}, append(tc.configOptions, configx.SkipValidation())...)\n\n\t\t\t\treg, err := driver.NewRegistryFromDSN(ctx, conf, l)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\ts := reg.SettingsStrategies(ctx)\n\t\t\t\trequire.Len(t, s, len(tc.expect))\n\n\t\t\t\tfor k, e := range tc.expect {\n\t\t\t\t\tassert.Equal(t, e, s[k].SettingsStrategyID())\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestDefaultRegistry_AllStrategies(t *testing.T) {\n\tt.Parallel()\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\n\tt.Run(\"case=all login strategies\", func(t *testing.T) {\n\t\texpects := []string{\"password\", \"oidc\", \"code\", \"totp\", \"passkey\", \"webauthn\", \"lookup_secret\", \"identifier_first\"}\n\t\ts := reg.AllLoginStrategies()\n\t\trequire.Len(t, s, len(expects))\n\t\tfor k, e := range expects {\n\t\t\tassert.Equal(t, e, s[k].ID().String())\n\t\t}\n\t})\n\n\tt.Run(\"case=all registration strategies\", func(t *testing.T) {\n\t\texpects := []string{\"profile\", \"password\", \"oidc\", \"code\", \"passkey\", \"webauthn\"}\n\t\ts := reg.AllRegistrationStrategies()\n\t\trequire.Len(t, s, len(expects))\n\t\tfor k, e := range expects {\n\t\t\tassert.Equal(t, e, s[k].ID().String())\n\t\t}\n\t})\n\n\tt.Run(\"case=all settings strategies\", func(t *testing.T) {\n\t\texpects := []string{\"profile\", \"password\", \"oidc\", \"totp\", \"passkey\", \"webauthn\", \"lookup_secret\"}\n\t\ts := reg.AllSettingsStrategies()\n\t\trequire.Len(t, s, len(expects))\n\t\tfor k, e := range expects {\n\t\t\tassert.Equal(t, e, s[k].SettingsStrategyID())\n\t\t}\n\t})\n\n\tt.Run(\"case=all recovery strategies\", func(t *testing.T) {\n\t\texpects := []string{\"code\", \"link\"}\n\t\ts := reg.AllRecoveryStrategies()\n\t\trequire.Len(t, s, len(expects))\n\t\tfor k, e := range expects {\n\t\t\tassert.Equal(t, e, s[k].RecoveryStrategyID())\n\t\t}\n\t})\n}\n\nfunc TestGetActiveRecoveryStrategy(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\n\tt.Run(\"returns error if active strategy is disabled\", func(t *testing.T) {\n\t\tctx := contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\"selfservice.methods.code.enabled\":    false,\n\t\t\tconfig.ViperKeySelfServiceRecoveryUse: \"code\",\n\t\t})\n\n\t\t_, _, err := reg.GetActiveRecoveryStrategies(ctx)\n\t\trequire.Error(t, err)\n\t})\n\n\tt.Run(\"returns active strategies\", func(t *testing.T) {\n\t\tfor _, sID := range []string{\n\t\t\t\"code\", \"link\",\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"strategy=%s\", sID), func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\tfmt.Sprintf(\"selfservice.methods.%s.enabled\", sID): true,\n\t\t\t\t\tconfig.ViperKeySelfServiceRecoveryUse:              sID,\n\t\t\t\t})\n\n\t\t\t\ts, ps, err := reg.GetActiveRecoveryStrategies(ctx)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, s, 1)\n\t\t\t\trequire.Equal(t, sID, ps.RecoveryStrategyID())\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestGetActiveVerificationStrategy(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\tt.Run(\"returns error if active strategy is disabled\", func(t *testing.T) {\n\t\tctx := contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\"selfservice.methods.code.enabled\":        false,\n\t\t\tconfig.ViperKeySelfServiceVerificationUse: \"code\",\n\t\t})\n\t\t_, _, err := reg.GetActiveVerificationStrategies(ctx)\n\t\trequire.Error(t, err)\n\t})\n\n\tt.Run(\"returns active strategy\", func(t *testing.T) {\n\t\tfor _, sID := range []string{\n\t\t\t\"code\", \"link\",\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"strategy=%s\", sID), func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\tfmt.Sprintf(\"selfservice.methods.%s.enabled\", sID): true,\n\t\t\t\t\tconfig.ViperKeySelfServiceVerificationUse:          sID,\n\t\t\t\t})\n\n\t\t\t\t_, s, err := reg.GetActiveVerificationStrategies(ctx)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, sID, s.VerificationStrategyID())\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "driver/registry_default_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n)\n\nfunc (m *RegistryDefault) VerificationFlowPersister() verification.FlowPersister {\n\treturn m.persister\n}\n\nfunc (m *RegistryDefault) VerificationFlowErrorHandler() *verification.ErrorHandler {\n\tif m.selfserviceVerifyErrorHandler == nil {\n\t\tm.selfserviceVerifyErrorHandler = verification.NewErrorHandler(m)\n\t}\n\n\treturn m.selfserviceVerifyErrorHandler\n}\n\nfunc (m *RegistryDefault) VerificationManager() *identity.Manager {\n\tif m.selfserviceVerifyManager == nil {\n\t\tm.selfserviceVerifyManager = identity.NewManager(m)\n\t}\n\n\treturn m.selfserviceVerifyManager\n}\n\nfunc (m *RegistryDefault) VerificationHandler() *verification.Handler {\n\tif m.selfserviceVerifyHandler == nil {\n\t\tm.selfserviceVerifyHandler = verification.NewHandler(m)\n\t}\n\n\treturn m.selfserviceVerifyHandler\n}\n\nfunc (m *RegistryDefault) LinkSender() *link.Sender {\n\tif m.selfserviceLinkSender == nil {\n\t\tm.selfserviceLinkSender = link.NewSender(m)\n\t}\n\n\treturn m.selfserviceLinkSender\n}\n\n// GetActiveVerificationStrategies returns the currently active verification strategies.\n// It returns a list of all strategies and the specific primary strategy.\n// If no primary verification strategy has been set, an error is returned.\nfunc (m *RegistryDefault) GetActiveVerificationStrategies(ctx context.Context) (active verification.Strategies, primary verification.PrimaryStrategy, err error) {\n\tas := m.Config().SelfServiceFlowVerificationUse(ctx)\n\ts, ps, err := m.VerificationStrategies(ctx).ActiveStrategies(as)\n\tif err != nil {\n\t\treturn nil, ps, errors.WithStack(herodot.ErrBadRequest.\n\t\t\tWithReasonf(\"The active verification strategy %s is not enabled. Please enable it in the configuration.\", as))\n\t}\n\treturn s, ps, nil\n}\n\nfunc (m *RegistryDefault) VerificationStrategies(ctx context.Context) (verificationStrategies verification.Strategies) {\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(verification.Strategy); ok {\n\t\t\tif m.Config().SelfServiceStrategy(ctx, s.VerificationStrategyID()).Enabled {\n\t\t\t\tverificationStrategies = append(verificationStrategies, s)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (m *RegistryDefault) AllVerificationStrategies() (verificationStrategies verification.Strategies) {\n\tfor _, strategy := range m.selfServiceStrategies() {\n\t\tif s, ok := strategy.(verification.Strategy); ok {\n\t\t\tverificationStrategies = append(verificationStrategies, s)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (m *RegistryDefault) VerificationExecutor() *verification.HookExecutor {\n\tif m.selfserviceVerificationExecutor == nil {\n\t\tm.selfserviceVerificationExecutor = verification.NewHookExecutor(m)\n\t}\n\treturn m.selfserviceVerificationExecutor\n}\n\nfunc (m *RegistryDefault) PreVerificationHooks(ctx context.Context) ([]verification.PreHookExecutor, error) {\n\treturn getHooks[verification.PreHookExecutor](m, \"\", m.Config().SelfServiceFlowVerificationBeforeHooks(ctx))\n}\n\nfunc (m *RegistryDefault) PostVerificationHooks(ctx context.Context) ([]verification.PostHookExecutor, error) {\n\treturn getHooks[verification.PostHookExecutor](m, config.HookGlobal, m.Config().SelfServiceFlowVerificationAfterHooks(ctx, config.HookGlobal))\n}\n"
  },
  {
    "path": "embedx/config.schema.json",
    "content": "{\n  \"$id\": \"https://github.com/ory/kratos/embedx/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Ory Kratos Configuration\",\n  \"type\": \"object\",\n  \"definitions\": {\n    \"baseUrl\": {\n      \"title\": \"Base URL\",\n      \"description\": \"The URL where the endpoint is exposed at. This domain is used to generate redirects, form URLs, and more.\",\n      \"type\": \"string\",\n      \"format\": \"uri-reference\",\n      \"examples\": [\n        \"https://my-app.com/\",\n        \"https://my-app.com/.ory/kratos/public\"\n      ]\n    },\n    \"socket\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Sets the permissions of the unix socket\",\n      \"properties\": {\n        \"owner\": {\n          \"type\": \"string\",\n          \"description\": \"Owner of unix socket. If empty, the owner will be the user running Kratos.\",\n          \"default\": \"\"\n        },\n        \"group\": {\n          \"type\": \"string\",\n          \"description\": \"Group of unix socket. If empty, the group will be the primary group of the user running Kratos.\",\n          \"default\": \"\"\n        },\n        \"mode\": {\n          \"type\": \"integer\",\n          \"description\": \"Mode of unix socket in numeric form\",\n          \"default\": 493,\n          \"minimum\": 0,\n          \"maximum\": 511\n        }\n      }\n    },\n    \"defaultReturnTo\": {\n      \"title\": \"Redirect browsers to set URL per default\",\n      \"description\": \"Ory Kratos redirects to this URL per default on completion of self-service flows and other browser interaction. Read this [article for more information on browser redirects](https://www.ory.sh/kratos/docs/concepts/browser-redirect-flow-completion).\",\n      \"type\": \"string\",\n      \"format\": \"uri-reference\",\n      \"examples\": [\"https://my-app.com/dashboard\", \"/dashboard\"]\n    },\n    \"selfServiceSessionRevokerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"revoke_active_sessions\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceSessionIssuerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"session\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceRequireVerifiedAddressHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"require_verified_address\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceVerificationHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"verification\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceShowVerificationUIHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"show_verification_ui\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"b2bSSOHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"enum\": [\"b2b_sso\", \"organization\"]\n        },\n        \"config\": {\n          \"type\": \"object\",\n          \"additionalProperties\": true\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\", \"config\"]\n    },\n    \"webHookAuthBasicAuthProperties\": {\n      \"properties\": {\n        \"type\": {\n          \"const\": \"basic_auth\"\n        },\n        \"config\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"user\": {\n              \"type\": \"string\",\n              \"description\": \"user name for basic auth\"\n            },\n            \"password\": {\n              \"type\": \"string\",\n              \"description\": \"password for basic auth\"\n            }\n          },\n          \"additionalProperties\": false,\n          \"required\": [\"user\", \"password\"]\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"type\", \"config\"]\n    },\n    \"httpRequestConfig\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"url\": {\n          \"title\": \"HTTP address of API endpoint\",\n          \"description\": \"This URL will be used to send the emails to.\",\n          \"examples\": [\"https://example.com/api/v1/email\"],\n          \"type\": \"string\",\n          \"pattern\": \"^https?://\"\n        },\n        \"method\": {\n          \"type\": \"string\",\n          \"description\": \"The HTTP method to use (GET, POST, etc). Defaults to POST.\",\n          \"default\": \"POST\"\n        },\n        \"headers\": {\n          \"type\": \"object\",\n          \"description\": \"The HTTP headers that must be applied to request\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          }\n        },\n        \"body\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"pattern\": \"^(http|https|file|base64)://\",\n          \"description\": \"URI pointing to the jsonnet template used for payload generation. Only used for those HTTP methods which support HTTP body payloads\",\n          \"default\": \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgcmVjaXBpZW50OiBjdHgucmVjaXBpZW50LAogIHRlbXBsYXRlX3R5cGU6IGN0eC50ZW1wbGF0ZV90eXBlLAogIHRvOiBpZiAidGVtcGxhdGVfZGF0YSIgaW4gY3R4ICYmICJ0byIgaW4gY3R4LnRlbXBsYXRlX2RhdGEgdGhlbiBjdHgudGVtcGxhdGVfZGF0YS50byBlbHNlIG51bGwsCiAgcmVjb3ZlcnlfY29kZTogaWYgInRlbXBsYXRlX2RhdGEiIGluIGN0eCAmJiAicmVjb3ZlcnlfY29kZSIgaW4gY3R4LnRlbXBsYXRlX2RhdGEgdGhlbiBjdHgudGVtcGxhdGVfZGF0YS5yZWNvdmVyeV9jb2RlIGVsc2UgbnVsbCwKICByZWNvdmVyeV91cmw6IGlmICJ0ZW1wbGF0ZV9kYXRhIiBpbiBjdHggJiYgInJlY292ZXJ5X3VybCIgaW4gY3R4LnRlbXBsYXRlX2RhdGEgdGhlbiBjdHgudGVtcGxhdGVfZGF0YS5yZWNvdmVyeV91cmwgZWxzZSBudWxsLAogIHZlcmlmaWNhdGlvbl91cmw6IGlmICJ0ZW1wbGF0ZV9kYXRhIiBpbiBjdHggJiYgInZlcmlmaWNhdGlvbl91cmwiIGluIGN0eC50ZW1wbGF0ZV9kYXRhIHRoZW4gY3R4LnRlbXBsYXRlX2RhdGEudmVyaWZpY2F0aW9uX3VybCBlbHNlIG51bGwsCiAgdmVyaWZpY2F0aW9uX2NvZGU6IGlmICJ0ZW1wbGF0ZV9kYXRhIiBpbiBjdHggJiYgInZlcmlmaWNhdGlvbl9jb2RlIiBpbiBjdHgudGVtcGxhdGVfZGF0YSB0aGVuIGN0eC50ZW1wbGF0ZV9kYXRhLnZlcmlmaWNhdGlvbl9jb2RlIGVsc2UgbnVsbCwKICBzdWJqZWN0OiBjdHguc3ViamVjdCwKICBib2R5OiBjdHguYm9keQp9Cg==\",\n          \"examples\": [\n            \"file:///path/to/body.jsonnet\",\n            \"file://./body.jsonnet\",\n            \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgaWRlbnRpdHlfaWQ6IGlmIGN0eFsiaWRlbnRpdHkiXSAhPSBudWxsIHRoZW4gY3R4LmlkZW50aXR5LmlkLAp9=\",\n            \"https://oryapis.com/default_body.jsonnet\"\n          ]\n        },\n        \"auth\": {\n          \"type\": \"object\",\n          \"title\": \"Auth mechanisms\",\n          \"description\": \"Define which auth mechanism to use for auth with the HTTP email provider\",\n          \"oneOf\": [\n            {\n              \"$ref\": \"#/definitions/webHookAuthApiKeyProperties\"\n            },\n            {\n              \"$ref\": \"#/definitions/webHookAuthBasicAuthProperties\"\n            }\n          ]\n        },\n        \"additionalProperties\": false\n      },\n      \"additionalProperties\": false\n    },\n    \"webHookAuthApiKeyProperties\": {\n      \"properties\": {\n        \"type\": {\n          \"const\": \"api_key\"\n        },\n        \"config\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"name\": {\n              \"type\": \"string\",\n              \"description\": \"The name of the api key\"\n            },\n            \"value\": {\n              \"type\": \"string\",\n              \"description\": \"The value of the api key\"\n            },\n            \"in\": {\n              \"type\": \"string\",\n              \"description\": \"How the api key should be transferred\",\n              \"enum\": [\"header\", \"cookie\"]\n            }\n          },\n          \"additionalProperties\": false,\n          \"required\": [\"name\", \"value\", \"in\"]\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"type\", \"config\"]\n    },\n    \"selfServiceWebHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"web_hook\"\n        },\n        \"config\": {\n          \"type\": \"object\",\n          \"title\": \"Web-Hook Configuration\",\n          \"description\": \"Define what the hook should do\",\n          \"properties\": {\n            \"id\": {\n              \"type\": \"string\",\n              \"description\": \"The ID of the hook. Used to identify the hook in logs and errors. For debugging purposes only.\"\n            },\n            \"response\": {\n              \"title\": \"Response Handling\",\n              \"description\": \"How the web hook should handle the response\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ignore\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Ignore the response from the web hook. If enabled the request will be made asynchronously which can be useful if you only wish to notify another system but do not parse the response.\",\n                  \"default\": false\n                },\n                \"parse\": {\n                  \"type\": \"boolean\",\n                  \"default\": false,\n                  \"description\": \"If enabled parses the response before saving the flow result. Set this value to true if you would like to modify the identity, for example identity metadata, before saving it during registration. When enabled, you may also abort the registration, verification, login or settings flow due to, for example, a validation flow. Head over to the [web hook documentation](https://www.ory.sh/docs/kratos/hooks/configure-hooks) for more information.\"\n                }\n              },\n              \"not\": {\n                \"properties\": {\n                  \"ignore\": {\n                    \"const\": true\n                  },\n                  \"parse\": {\n                    \"const\": true\n                  }\n                },\n                \"required\": [\"ignore\", \"parse\"]\n              }\n            },\n            \"url\": {\n              \"type\": \"string\",\n              \"description\": \"The URL the Web-Hook should call\",\n              \"format\": \"uri\"\n            },\n            \"method\": {\n              \"type\": \"string\",\n              \"description\": \"The HTTP method to use (GET, POST, etc).\"\n            },\n            \"headers\": {\n              \"type\": \"object\",\n              \"description\": \"The HTTP headers that must be applied to the Web-Hook\",\n              \"additionalProperties\": {\n                \"type\": \"string\"\n              }\n            },\n            \"body\": {\n              \"type\": \"string\",\n              \"oneOf\": [\n                {\n                  \"format\": \"uri\",\n                  \"pattern\": \"^(http|https|file|base64)://\",\n                  \"description\": \"URI pointing to the jsonnet template used for payload generation. Only used for those HTTP methods, which support HTTP body payloads\",\n                  \"examples\": [\n                    \"file:///path/to/body.jsonnet\",\n                    \"file://./body.jsonnet\",\n                    \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgaWRlbnRpdHlfaWQ6IGlmIGN0eFsiaWRlbnRpdHkiXSAhPSBudWxsIHRoZW4gY3R4LmlkZW50aXR5LmlkLAp9=\",\n                    \"https://oryapis.com/default_body.jsonnet\"\n                  ]\n                },\n                {\n                  \"description\": \"DEPRECATED: please use a URI instead (i.e. prefix your filepath with 'file://')\",\n                  \"not\": {\n                    \"pattern\": \"^(http|https|file|base64)://\"\n                  }\n                }\n              ]\n            },\n            \"can_interrupt\": {\n              \"type\": \"boolean\",\n              \"default\": false,\n              \"description\": \"Deprecated, please use `response.parse` instead. If enabled allows the web hook to interrupt / abort the self-service flow. It only applies to certain flows (registration/verification/login/settings) and requires a valid response format.\"\n            },\n            \"emit_analytics_event\": {\n              \"type\": \"boolean\",\n              \"default\": true,\n              \"description\": \"Emit tracing events for this webhook on delivery or error\"\n            },\n            \"auth\": {\n              \"type\": \"object\",\n              \"title\": \"Auth mechanisms\",\n              \"description\": \"Define which auth mechanism the Web-Hook should use\",\n              \"oneOf\": [\n                {\n                  \"$ref\": \"#/definitions/webHookAuthApiKeyProperties\"\n                },\n                {\n                  \"$ref\": \"#/definitions/webHookAuthBasicAuthProperties\"\n                }\n              ]\n            },\n            \"additionalProperties\": false\n          },\n          \"anyOf\": [\n            {\n              \"not\": {\n                \"properties\": {\n                  \"response\": {\n                    \"properties\": {\n                      \"ignore\": {\n                        \"const\": true\n                      }\n                    },\n                    \"required\": [\"ignore\"]\n                  }\n                },\n                \"required\": [\"response\"]\n              }\n            },\n            {\n              \"properties\": {\n                \"can_interrupt\": {\n                  \"const\": false\n                }\n              },\n              \"require\": [\"can_interrupt\"]\n            }\n          ],\n          \"additionalProperties\": false,\n          \"required\": [\"url\", \"method\"]\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\", \"config\"]\n    },\n    \"OIDCClaims\": {\n      \"title\": \"OpenID Connect claims\",\n      \"description\": \"The OpenID Connect claims and optionally their properties which should be included in the id_token or returned from the UserInfo Endpoint.\",\n      \"type\": \"object\",\n      \"examples\": [\n        {\n          \"id_token\": {\n            \"email\": null,\n            \"email_verified\": null\n          }\n        },\n        {\n          \"userinfo\": {\n            \"given_name\": {\n              \"essential\": true\n            },\n            \"nickname\": null,\n            \"email\": {\n              \"essential\": true\n            },\n            \"email_verified\": {\n              \"essential\": true\n            },\n            \"picture\": null,\n            \"http://example.info/claims/groups\": null\n          },\n          \"id_token\": {\n            \"auth_time\": {\n              \"essential\": true\n            },\n            \"acr\": {\n              \"values\": [\"urn:mace:incommon:iap:silver\"]\n            }\n          }\n        }\n      ],\n      \"patternProperties\": {\n        \"^userinfo$|^id_token$\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"patternProperties\": {\n            \".*\": {\n              \"oneOf\": [\n                {\n                  \"const\": null,\n                  \"description\": \"Indicates that this Claim is being requested in the default manner.\"\n                },\n                {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"essential\": {\n                      \"description\": \"Indicates whether the Claim being requested is an Essential Claim.\",\n                      \"type\": \"boolean\"\n                    },\n                    \"value\": {\n                      \"description\": \"Requests that the Claim be returned with a particular value.\",\n                      \"$comment\": \"There seem to be no constrains on value\"\n                    },\n                    \"values\": {\n                      \"description\": \"Requests that the Claim be returned with one of a set of values, with the values appearing in order of preference.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$comment\": \"There seem to be no constrains on individual items\"\n                      }\n                    }\n                  }\n                }\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"selfServiceOIDCProvider\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"examples\": [\"google\"]\n        },\n        \"provider\": {\n          \"title\": \"Provider\",\n          \"description\": \"Can be one of github, github-app, gitlab, generic, google, microsoft, discord, salesforce, slack, facebook, auth0, vk, yandex, apple, spotify, netid, dingtalk, patreon, amazon.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"github\",\n            \"github-app\",\n            \"gitlab\",\n            \"generic\",\n            \"google\",\n            \"microsoft\",\n            \"discord\",\n            \"salesforce\",\n            \"slack\",\n            \"facebook\",\n            \"auth0\",\n            \"vk\",\n            \"yandex\",\n            \"apple\",\n            \"spotify\",\n            \"netid\",\n            \"dingtalk\",\n            \"patreon\",\n            \"line\",\n            \"linkedin\",\n            \"linkedin_v2\",\n            \"lark\",\n            \"x\",\n            \"fedcm-test\",\n            \"amazon\",\n            \"uaepass\"\n          ],\n          \"examples\": [\"google\"]\n        },\n        \"label\": {\n          \"title\": \"Optional string which will be used when generating labels for UI buttons.\",\n          \"type\": \"string\"\n        },\n        \"client_id\": {\n          \"type\": \"string\"\n        },\n        \"client_secret\": {\n          \"type\": \"string\"\n        },\n        \"issuer_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com\"]\n        },\n        \"auth_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com/o/oauth2/v2/auth\"]\n        },\n        \"token_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://www.googleapis.com/oauth2/v4/token\"]\n        },\n        \"mapper_url\": {\n          \"title\": \"Jsonnet Mapper URL\",\n          \"description\": \"The URL where the jsonnet source is located for mapping the provider's data to Ory Kratos data.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/oidc.jsonnet\",\n            \"https://foo.bar.com/path/to/oidc.jsonnet\",\n            \"base64://bG9jYWwgc3ViamVjdCA9I...\"\n          ]\n        },\n        \"scope\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"examples\": [\"offline_access\", \"profile\"]\n          }\n        },\n        \"microsoft_tenant\": {\n          \"title\": \"Azure AD Tenant\",\n          \"description\": \"The Azure AD Tenant to use for authentication.\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"common\",\n            \"organizations\",\n            \"consumers\",\n            \"8eaef023-2b34-4da1-9baa-8bc8c9d6a490\",\n            \"contoso.onmicrosoft.com\"\n          ]\n        },\n        \"subject_source\": {\n          \"title\": \"Microsoft subject source\",\n          \"description\": \"Controls which source the subject identifier is taken from by microsoft provider. If set to `userinfo` (the default) then the identifier is taken from the `sub` field of OIDC ID token or data received from `/userinfo` standard OIDC endpoint. If set to `me` then the `id` field of data structure received from `https://graph.microsoft.com/v1.0/me` is taken as an identifier. If the value is `oid` then the the oid (Object ID) is taken to identify users across different services.\",\n          \"type\": \"string\",\n          \"enum\": [\"userinfo\", \"me\", \"oid\"],\n          \"default\": \"userinfo\",\n          \"examples\": [\"userinfo\"]\n        },\n        \"apple_team_id\": {\n          \"title\": \"Apple Developer Team ID\",\n          \"description\": \"Apple Developer Team ID needed for generating a JWT token for client secret\",\n          \"type\": \"string\",\n          \"examples\": [\"KP76DQS54M\"]\n        },\n        \"apple_private_key_id\": {\n          \"title\": \"Apple Private Key Identifier\",\n          \"description\": \"Sign In with Apple Private Key Identifier needed for generating a JWT token for client secret\",\n          \"type\": \"string\",\n          \"examples\": [\"UX56C66723\"]\n        },\n        \"apple_private_key\": {\n          \"title\": \"Apple Private Key\",\n          \"description\": \"Sign In with Apple Private Key needed for generating a JWT token for client secret\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"-----BEGIN PRIVATE KEY-----\\n........\\n-----END PRIVATE KEY-----\"\n          ]\n        },\n        \"requested_claims\": {\n          \"$ref\": \"#/definitions/OIDCClaims\"\n        },\n        \"organization_id\": {\n          \"title\": \"Organization ID\",\n          \"description\": \"The ID of the organization that this provider belongs to. Only effective in the Ory Network.\",\n          \"type\": \"string\",\n          \"examples\": [\"12345678-1234-1234-1234-123456789012\"]\n        },\n        \"additional_id_token_audiences\": {\n          \"title\": \"Additional client ids allowed when using ID token submission\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"examples\": [\"12345678-1234-1234-1234-123456789012\"]\n          }\n        },\n        \"claims_source\": {\n          \"title\": \"Claims source\",\n          \"description\": \"Can be either `userinfo` (calls the userinfo endpoint to get the claims) or `id_token` (takes the claims from the id token). It defaults to `id_token`\",\n          \"type\": \"string\",\n          \"enum\": [\"id_token\", \"userinfo\"],\n          \"default\": \"id_token\",\n          \"examples\": [\"id_token\", \"userinfo\"]\n        },\n        \"pkce\": {\n          \"title\": \"Proof Key for Code Exchange\",\n          \"description\": \"PKCE controls if the OpenID Connect OAuth2 flow should use PKCE (Proof Key for Code Exchange). IMPORTANT: If you set this to `force`, you must whitelist a different return URL for your OAuth2 client in the provider's configuration. Instead of <base-url>/self-service/methods/oidc/callback/<provider>, you must use <base-url>/self-service/methods/oidc/callback\",\n          \"type\": \"string\",\n          \"enum\": [\"auto\", \"never\", \"force\"],\n          \"default\": \"auto\"\n        },\n        \"fedcm_config_url\": {\n          \"title\": \"Federation Configuration URL\",\n          \"description\": \"The URL where the FedCM IdP configuration is located for the provider. This is only effective in the Ory Network.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://example.com/config.json\"]\n        },\n        \"net_id_token_origin_header\": {\n          \"title\": \"NetID Token Origin Header\",\n          \"description\": \"Contains the orgin header to be used when exchanging a NetID FedCM token for an ID token\",\n          \"type\": \"string\",\n          \"examples\": [\"https://example.com\"]\n        },\n        \"account_linking_mode\": {\n          \"title\": \"Account linking mode\",\n          \"description\": \"Controls how account conflicts are resolved for this provider. `confirm_with_existing_credential` (default) requires the user to verify their identity with an existing credential. `automatic` silently links accounts if the provider verifies email ownership. This is only effective in the Ory Network.\",\n          \"type\": \"string\",\n          \"enum\": [\"confirm_with_existing_credential\", \"automatic\"],\n          \"default\": \"confirm_with_existing_credential\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"id\", \"provider\", \"client_id\", \"mapper_url\"],\n      \"allOf\": [\n        {\n          \"if\": {\n            \"properties\": {\n              \"provider\": {\n                \"const\": \"microsoft\"\n              }\n            },\n            \"required\": [\"provider\"]\n          },\n          \"then\": {\n            \"required\": [\"microsoft_tenant\"]\n          },\n          \"else\": {\n            \"not\": {\n              \"properties\": {\n                \"microsoft_tenant\": {}\n              },\n              \"required\": [\"microsoft_tenant\"]\n            }\n          }\n        },\n        {\n          \"if\": {\n            \"properties\": {\n              \"provider\": {\n                \"const\": \"apple\"\n              }\n            },\n            \"required\": [\"provider\"]\n          },\n          \"then\": {\n            \"not\": {\n              \"properties\": {\n                \"client_secret\": {\n                  \"type\": \"string\",\n                  \"minLength\": 1\n                }\n              },\n              \"required\": [\"client_secret\"]\n            },\n            \"required\": [\n              \"apple_private_key_id\",\n              \"apple_private_key\",\n              \"apple_team_id\"\n            ]\n          },\n          \"else\": {\n            \"required\": [\"client_secret\"],\n            \"allOf\": [\n              {\n                \"not\": {\n                  \"properties\": {\n                    \"apple_team_id\": {\n                      \"type\": \"string\",\n                      \"minLength\": 1\n                    }\n                  },\n                  \"required\": [\"apple_team_id\"]\n                }\n              },\n              {\n                \"not\": {\n                  \"properties\": {\n                    \"apple_private_key_id\": {\n                      \"type\": \"string\",\n                      \"minLength\": 1\n                    }\n                  },\n                  \"required\": [\"apple_private_key_id\"]\n                }\n              },\n              {\n                \"not\": {\n                  \"properties\": {\n                    \"apple_private_key\": {\n                      \"type\": \"string\",\n                      \"minLength\": 1\n                    }\n                  },\n                  \"required\": [\"apple_private_key\"]\n                }\n              }\n            ]\n          }\n        }\n      ]\n    },\n    \"selfServiceHooks\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"anyOf\": [\n          {\n            \"$ref\": \"#/definitions/selfServiceWebHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/b2bSSOHook\"\n          }\n        ]\n      },\n      \"uniqueItems\": true,\n      \"additionalItems\": false\n    },\n    \"selfServiceAfterRecoveryHooks\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"anyOf\": [\n          {\n            \"$ref\": \"#/definitions/selfServiceWebHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n          }\n        ]\n      },\n      \"uniqueItems\": true,\n      \"additionalItems\": false\n    },\n    \"selfServiceAfterSettingsProfileMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceWebHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceShowVerificationUIHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/b2bSSOHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterSettingsAuthMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceWebHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterDefaultLoginMethodHooks\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"anyOf\": [\n          {\n            \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceRequireVerifiedAddressHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceWebHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceVerificationHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/selfServiceShowVerificationUIHook\"\n          },\n          {\n            \"$ref\": \"#/definitions/b2bSSOHook\"\n          }\n        ]\n      },\n      \"uniqueItems\": true,\n      \"additionalItems\": false\n    },\n    \"selfServiceAfterDefaultLoginMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethodHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterOIDCLoginMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceWebHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceRequireVerifiedAddressHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/b2bSSOHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterRegistrationMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionIssuerHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceWebHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/selfServiceShowVerificationUIHook\"\n              },\n              {\n                \"$ref\": \"#/definitions/b2bSSOHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"featureRequiredAal\": {\n      \"title\": \"Required Authenticator Assurance Level\",\n      \"description\": \"Sets what Authenticator Assurance Level (used for 2FA) is required to access this feature. If set to `highest_available` then this endpoint requires the highest AAL the identity has set up. If set to `aal1` then the identity can access this feature without 2FA.\",\n      \"type\": \"string\",\n      \"enum\": [\"aal1\", \"highest_available\"],\n      \"default\": \"highest_available\"\n    },\n    \"selfServiceAfterSettings\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"totp\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"webauthn\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"passkey\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"lookup_secret\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsAuthMethod\"\n        },\n        \"profile\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsProfileMethod\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeLogin\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterLogin\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"webauthn\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"passkey\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterOIDCLoginMethod\"\n        },\n        \"code\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"totp\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"lookup_secret\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethod\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceAfterDefaultLoginMethodHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeRegistration\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeSettings\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeRecovery\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceBeforeVerification\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterRegistration\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"webauthn\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"passkey\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"code\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterVerification\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceHooks\"\n        }\n      }\n    },\n    \"selfServiceAfterRecovery\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRecoveryHooks\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"tlsxSource\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"path\": {\n          \"title\": \"Path to PEM-encoded Fle\",\n          \"type\": \"string\",\n          \"examples\": [\"path/to/file.pem\"]\n        },\n        \"base64\": {\n          \"title\": \"Base64 Encoded Inline\",\n          \"description\": \"The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n          ]\n        }\n      }\n    },\n    \"tlsx\": {\n      \"title\": \"HTTPS\",\n      \"description\": \"Configure HTTP over TLS (HTTPS). All options can also be set using environment variables by replacing dots (`.`) with underscores (`_`) and uppercasing the key. For example, `some.prefix.tls.key.path` becomes `export SOME_PREFIX_TLS_KEY_PATH`. If all keys are left undefined, TLS will be disabled.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"key\": {\n          \"title\": \"Private Key (PEM)\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/tlsxSource\"\n            }\n          ]\n        },\n        \"cert\": {\n          \"title\": \"TLS Certificate (PEM)\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/tlsxSource\"\n            }\n          ]\n        }\n      }\n    },\n    \"courierTemplates\": {\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"invalid\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"email\": {\n              \"$ref\": \"#/definitions/emailCourierTemplate\"\n            }\n          },\n          \"required\": [\"email\"]\n        },\n        \"valid\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"email\": {\n              \"$ref\": \"#/definitions/emailCourierTemplate\"\n            },\n            \"sms\": {\n              \"$ref\": \"#/definitions/smsCourierTemplate\"\n            }\n          },\n          \"required\": [\"email\"]\n        }\n      }\n    },\n    \"smsCourierTemplate\": {\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"body\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"plaintext\": {\n              \"type\": \"string\",\n              \"description\": \"A template send to the SMS provider.\",\n              \"format\": \"uri\",\n              \"examples\": [\n                \"file://path/to/body.plaintext.gotmpl\",\n                \"https://foo.bar.com/path/to/body.plaintext.gotmpl\"\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"emailCourierTemplate\": {\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"body\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"plaintext\": {\n              \"type\": \"string\",\n              \"description\": \"The fallback template for email clients that do not support html.\",\n              \"format\": \"uri\",\n              \"examples\": [\n                \"file://path/to/body.plaintext.gotmpl\",\n                \"https://foo.bar.com/path/to/body.plaintext.gotmpl\",\n                \"base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQp7eyAuUmVjb3ZlcnlVUkwgfX0K\"\n              ]\n            },\n            \"html\": {\n              \"type\": \"string\",\n              \"description\": \"The default template used for sending out emails. The template can contain HTML \",\n              \"format\": \"uri\",\n              \"examples\": [\n                \"file://path/to/body.html.gotmpl\",\n                \"https://foo.bar.com/path/to/body.html.gotmpl\",\n                \"base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4\"\n              ]\n            }\n          }\n        },\n        \"subject\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/subject.gotmpl\",\n            \"https://foo.bar.com/path/to/subject.gotmpl\",\n            \"base64://e3sgZGVmaW5lIGFmLVpBIH19CkhhbGxvLAoKSGVyc3RlbCBqb3UgcmVrZW5pbmcgZGV1ciBoaWVyZGllIHNrYWtlbCB0ZSB2b2xnOgp7ey0gZW5kIC19fQoKe3sgZGVmaW5lIGVuLVVTIH19CkhpLAoKcGxlYXNlIHJlY292ZXIgYWNjZXNzIHRvIHlvdXIgYWNjb3VudCBieSBjbGlja2luZyB0aGUgZm9sbG93aW5nIGxpbms6Cnt7LSBlbmQgLX19Cgp7ey0gaWYgZXEgLmxhbmcgImFmLVpBIiAtfX0KCnt7IHRlbXBsYXRlICJhZi1aQSIgLiB9fQoKe3stIGVsc2UgLX19Cgp7eyB0ZW1wbGF0ZSAiZW4tVVMiIH19Cgp7ey0gZW5kIC19fQo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4\"\n          ]\n        }\n      }\n    }\n  },\n  \"properties\": {\n    \"selfservice\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default_browser_return_url\"],\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"allowed_return_urls\": {\n          \"title\": \"Allowed Return To URLs\",\n          \"description\": \"List of URLs that are allowed to be redirected to. A redirection request is made by appending `?return_to=...` to Login, Registration, and other self-service flows.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"format\": \"uri-reference\"\n          },\n          \"examples\": [\n            [\n              \"https://app.my-app.com/dashboard\",\n              \"/dashboard\",\n              \"https://www.my-app.com/\",\n              \"https://*.my-app.com/\"\n            ]\n          ]\n        },\n        \"flows\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"settings\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"URL of the Settings page.\",\n                  \"description\": \"URL where the Settings UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/user/settings\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/settings\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"privileged_session_max_age\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"required_aal\": {\n                  \"$ref\": \"#/definitions/featureRequiredAal\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterSettings\"\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeSettings\"\n                }\n              }\n            },\n            \"logout\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"after\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  }\n                }\n              }\n            },\n            \"registration\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable User Registration\",\n                  \"description\": \"If set to true will enable [User Registration](https://www.ory.sh/kratos/docs/self-service/flows/user-registration/).\",\n                  \"default\": true\n                },\n                \"login_hints\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Provide Login Hints on Failed Registration\",\n                  \"description\": \"When registration fails because an account with the given credentials or addresses previously signed up, provide login hints about available methods to sign in to the user.\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Registration UI URL\",\n                  \"description\": \"URL where the Registration UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/signup\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/registration\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeRegistration\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterRegistration\"\n                },\n                \"enable_legacy_one_step\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Disable two-step registration\",\n                  \"description\": \"Deprecated, please use `style` instead.\",\n                  \"deprecationMessage\": \"Deprecated, please use `style` instead.\",\n                  \"default\": false\n                },\n                \"style\": {\n                  \"title\": \"Registration Flow Style\",\n                  \"description\": \"The style of the registration flow. If set to `unified` the login flow will be a one-step process. If set to `profile_first` the registration flow will first ask for the profile information first, and then the credentials.\",\n                  \"type\": \"string\",\n                  \"enum\": [\"unified\", \"profile_first\"],\n                  \"default\": \"profile_first\"\n                }\n              }\n            },\n            \"login\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Login UI URL\",\n                  \"description\": \"URL where the Login UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/login\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/login\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"style\": {\n                  \"title\": \"Login Flow Style\",\n                  \"description\": \"The style of the login flow. If set to `unified` the login flow will be a one-step process. If set to `identifier_first` (experimental!) the login flow will first ask for the identifier and then the credentials.\",\n                  \"type\": \"string\",\n                  \"enum\": [\"unified\", \"identifier_first\"],\n                  \"default\": \"unified\"\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeLogin\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterLogin\"\n                }\n              }\n            },\n            \"verification\": {\n              \"title\": \"Email and Phone Verification and Account Activation Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Email/Phone Verification\",\n                  \"description\": \"If set to true will enable [Email and Phone Verification and Account Activation](https://www.ory.sh/kratos/docs/self-service/flows/verify-email-account-activation/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Verify UI URL\",\n                  \"description\": \"URL where the Ory Verify UI is hosted. This is the page where users activate and / or verify their email or telephone number. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/verification\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterVerification\"\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Verification Request Lifespan\",\n                  \"description\": \"Sets how long the verification request (for the UI interaction) is valid.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeVerification\"\n                },\n                \"use\": {\n                  \"title\": \"Verification Strategy\",\n                  \"description\": \"The strategy to use for verification requests\",\n                  \"type\": \"string\",\n                  \"enum\": [\"link\", \"code\"],\n                  \"default\": \"code\"\n                },\n                \"notify_unknown_recipients\": {\n                  \"title\": \"Notify unknown recipients\",\n                  \"description\": \"Whether to notify recipients, if verification was requested for their address.\",\n                  \"type\": \"boolean\",\n                  \"default\": false\n                }\n              }\n            },\n            \"recovery\": {\n              \"title\": \"Account Recovery Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Account Recovery\",\n                  \"description\": \"If set to true will enable [Account Recovery](https://www.ory.sh/kratos/docs/self-service/flows/password-reset-account-recovery/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Recovery UI URL\",\n                  \"description\": \"URL where the Ory Recovery UI is hosted. This is the page where users request and complete account recovery. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/recovery\"\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterRecovery\"\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Recovery Request Lifespan\",\n                  \"description\": \"Sets how long the recovery request is valid. If expired, the user has to redo the flow.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"before\": {\n                  \"$ref\": \"#/definitions/selfServiceBeforeRecovery\"\n                },\n                \"use\": {\n                  \"title\": \"Recovery Strategy\",\n                  \"description\": \"The strategy to use for recovery requests\",\n                  \"type\": \"string\",\n                  \"enum\": [\"link\", \"code\"],\n                  \"default\": \"code\"\n                },\n                \"notify_unknown_recipients\": {\n                  \"title\": \"Notify unknown recipients\",\n                  \"description\": \"Whether to notify recipients, if recovery was requested for their account.\",\n                  \"type\": \"boolean\",\n                  \"default\": false\n                }\n              }\n            },\n            \"error\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Ory Kratos Error UI URL\",\n                  \"description\": \"URL where the Ory Kratos Error UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/kratos-error\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/error\"\n                }\n              }\n            }\n          }\n        },\n        \"methods\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"b2b\": {\n              \"title\": \"Single Sign-On for B2B\",\n              \"description\": \"Single Sign-On for B2B allows your customers to bring their own (workforce) identity server (e.g. OneLogin). This feature is not available in the open source licensed code.\",\n              \"type\": \"object\",\n              \"properties\": {\n                \"config\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"organizations\": {\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"type\": \"object\",\n                        \"properties\": {\n                          \"id\": {\n                            \"type\": \"string\",\n                            \"description\": \"The ID of the organization.\",\n                            \"format\": \"uuid\",\n                            \"examples\": [\"00000000-0000-0000-0000-000000000000\"]\n                          },\n                          \"label\": {\n                            \"type\": \"string\",\n                            \"description\": \"The label of the organization.\",\n                            \"examples\": [\"ACME SSO\"]\n                          },\n                          \"domains\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                              \"type\": \"string\",\n                              \"format\": \"hostname\",\n                              \"examples\": [\"my-app.com\"],\n                              \"description\": \"If this domain matches the email's domain, this provider is shown.\"\n                            }\n                          }\n                        }\n                      }\n                    }\n                  }\n                }\n              },\n              \"additionalProperties\": false\n            },\n            \"profile\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Profile Management Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"link\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Link Method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"Link Configuration\",\n                  \"description\": \"Additional configuration for the link strategy.\",\n                  \"properties\": {\n                    \"base_url\": {\n                      \"title\": \"Override the base URL which should be used as the base for recovery and verification links.\",\n                      \"type\": \"string\",\n                      \"deprecationMessage\": \"This option has no effect, because the request URL is now used as the base URL.\",\n                      \"examples\": [\"https://my-app.com\"]\n                    },\n                    \"lifespan\": {\n                      \"title\": \"How long a link is valid for\",\n                      \"type\": \"string\",\n                      \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                      \"default\": \"1h\",\n                      \"examples\": [\"1h\", \"1m\", \"1s\"]\n                    }\n                  }\n                }\n              }\n            },\n            \"code\": {\n              \"type\": \"object\",\n              \"additionalProperties\": true,\n              \"anyOf\": [\n                {\n                  \"properties\": {\n                    \"passwordless_enabled\": {\n                      \"const\": true\n                    },\n                    \"mfa_enabled\": {\n                      \"const\": false\n                    }\n                  }\n                },\n                {\n                  \"properties\": {\n                    \"mfa_enabled\": {\n                      \"const\": true\n                    },\n                    \"passwordless_enabled\": {\n                      \"const\": false\n                    }\n                  }\n                },\n                {\n                  \"properties\": {\n                    \"mfa_enabled\": {\n                      \"const\": false\n                    },\n                    \"passwordless_enabled\": {\n                      \"const\": false\n                    }\n                  }\n                }\n              ],\n              \"properties\": {\n                \"passwordless_enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables login and registration with the code method.\",\n                  \"description\": \"If set to true, code.enabled will be set to true as well.\",\n                  \"default\": false\n                },\n                \"mfa_enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables login flows code method to fulfil MFA requests\",\n                  \"default\": false\n                },\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Code Method\",\n                  \"default\": true\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"Code Configuration\",\n                  \"description\": \"Additional configuration for the code strategy.\",\n                  \"properties\": {\n                    \"lifespan\": {\n                      \"title\": \"How long a code is valid for\",\n                      \"type\": \"string\",\n                      \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                      \"default\": \"1h\",\n                      \"examples\": [\"1h\", \"1m\", \"1s\"]\n                    },\n                    \"max_submissions\": {\n                      \"type\": \"integer\",\n                      \"title\": \"Maximum number of times the code can be submitted before a flow is invalidated\",\n                      \"minimum\": 1,\n                      \"maximum\": 255,\n                      \"default\": 5\n                    },\n                    \"missing_credential_fallback_enabled\": {\n                      \"type\": \"boolean\",\n                      \"title\": \"Enable Code OTP as a Fallback\",\n                      \"description\": \"Enabling this allows users to sign in with the code method, even if their identity schema or their credentials are not set up to use the code method. If enabled, a verified address (such as an email) will be used to send the code to the user. Use with caution and only if actually needed.\",\n                      \"default\": false\n                    }\n                  }\n                }\n              }\n            },\n            \"password\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Username/Email and Password Method\",\n                  \"default\": true\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"Password Configuration\",\n                  \"description\": \"Define how passwords are validated.\",\n                  \"properties\": {\n                    \"haveibeenpwned_host\": {\n                      \"title\": \"Custom haveibeenpwned host\",\n                      \"description\": \"Allows changing the default HIBP host to a self hosted version.\",\n                      \"type\": \"string\",\n                      \"default\": \"api.pwnedpasswords.com\"\n                    },\n                    \"haveibeenpwned_enabled\": {\n                      \"title\": \"Enable the HaveIBeenPwned API\",\n                      \"description\": \"If set to false the password validation does not utilize the Have I Been Pwnd API.\",\n                      \"type\": \"boolean\",\n                      \"default\": true\n                    },\n                    \"max_breaches\": {\n                      \"title\": \"Allow Password Breaches\",\n                      \"description\": \"Defines how often a password may have been breached before it is rejected.\",\n                      \"type\": \"integer\",\n                      \"minimum\": 0,\n                      \"maximum\": 100,\n                      \"default\": 0\n                    },\n                    \"ignore_network_errors\": {\n                      \"title\": \"Ignore Lookup Network Errors\",\n                      \"description\": \"If set to false the password validation fails when the network or the Have I Been Pwnd API is down.\",\n                      \"type\": \"boolean\",\n                      \"default\": true\n                    },\n                    \"min_password_length\": {\n                      \"title\": \"Minimum Password Length\",\n                      \"description\": \"Defines the minimum length of the password.\",\n                      \"type\": \"integer\",\n                      \"default\": 8,\n                      \"minimum\": 6\n                    },\n                    \"identifier_similarity_check_enabled\": {\n                      \"title\": \"Enable password-identifier similarity check\",\n                      \"description\": \"If set to false the password validation does not check for similarity between the password and the user identifier.\",\n                      \"type\": \"boolean\",\n                      \"default\": true\n                    },\n                    \"migrate_hook\": {\n                      \"type\": \"object\",\n                      \"additionalProperties\": false,\n                      \"properties\": {\n                        \"enabled\": {\n                          \"type\": \"boolean\",\n                          \"title\": \"Enable Password Migration\",\n                          \"description\": \"If set to true will enable password migration.\",\n                          \"default\": false\n                        },\n                        \"config\": {\n                          \"type\": \"object\",\n                          \"additionalProperties\": false,\n                          \"properties\": {\n                            \"url\": {\n                              \"type\": \"string\",\n                              \"description\": \"The URL the password migration hook should call\",\n                              \"format\": \"uri\"\n                            },\n                            \"method\": {\n                              \"type\": \"string\",\n                              \"description\": \"The HTTP method to use (GET, POST, etc).\",\n                              \"const\": \"POST\",\n                              \"default\": \"POST\"\n                            },\n                            \"headers\": {\n                              \"type\": \"object\",\n                              \"description\": \"The HTTP headers that must be applied to the password migration hook.\",\n                              \"additionalProperties\": {\n                                \"type\": \"string\"\n                              }\n                            },\n                            \"emit_analytics_event\": {\n                              \"type\": \"boolean\",\n                              \"default\": true,\n                              \"description\": \"Emit tracing events for this hook on delivery or error\"\n                            },\n                            \"auth\": {\n                              \"type\": \"object\",\n                              \"title\": \"Auth mechanisms\",\n                              \"description\": \"Define which auth mechanism the Web-Hook should use\",\n                              \"oneOf\": [\n                                {\n                                  \"$ref\": \"#/definitions/webHookAuthApiKeyProperties\"\n                                },\n                                {\n                                  \"$ref\": \"#/definitions/webHookAuthBasicAuthProperties\"\n                                }\n                              ]\n                            },\n                            \"body\": {\n                              \"type\": \"string\",\n                              \"format\": \"uri\",\n                              \"pattern\": \"^(http|https|file|base64)://\",\n                              \"description\": \"URI pointing to the jsonnet template used for payload generation. Only used for those HTTP methods, which support HTTP body payloads\",\n                              \"examples\": [\n                                \"file:///path/to/body.jsonnet\",\n                                \"file://./body.jsonnet\",\n                                \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgaWRlbnRpdHlfaWQ6IGlmIGN0eFsiaWRlbnRpdHkiXSAhPSBudWxsIHRoZW4gY3R4LmlkZW50aXR5LmlkLAp9=\",\n                                \"https://oryapis.com/default_body.jsonnet\"\n                              ]\n                            },\n                            \"additionalProperties\": false\n                          }\n                        }\n                      }\n                    }\n                  },\n                  \"additionalProperties\": false\n                }\n              }\n            },\n            \"totp\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables the TOTP method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"TOTP Configuration\",\n                  \"properties\": {\n                    \"issuer\": {\n                      \"title\": \"TOTP Issuer\",\n                      \"description\": \"The issuer (e.g. a domain name) will be shown in the TOTP app (e.g. Google Authenticator). It helps the user differentiate between different codes.\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"additionalProperties\": false\n                }\n              }\n            },\n            \"lookup_secret\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables the lookup secret method\",\n                  \"default\": false\n                }\n              }\n            },\n            \"webauthn\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables the WebAuthn method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"WebAuthn Configuration\",\n                  \"properties\": {\n                    \"passwordless\": {\n                      \"type\": \"boolean\",\n                      \"title\": \"Use For Passwordless Flows\",\n                      \"description\": \"If enabled will have the effect that WebAuthn is used for passwordless flows (as a first factor) and not for multi-factor set ups. With this set to true, users will see an option to sign up with WebAuthn on the registration screen.\"\n                    },\n                    \"rp\": {\n                      \"title\": \"Relying Party (RP) Config\",\n                      \"properties\": {\n                        \"display_name\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Display Name\",\n                          \"description\": \"An name to help the user identify this RP.\",\n                          \"examples\": [\"Ory Foundation\"]\n                        },\n                        \"id\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Identifier\",\n                          \"description\": \"The id must be a subset of the domain currently in the browser.\",\n                          \"examples\": [\"ory.sh\"]\n                        },\n                        \"origin\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Origin\",\n                          \"description\": \"An explicit RP origin. If left empty, this defaults to `id`, prepended with the current protocol schema (HTTP or HTTPS).\",\n                          \"format\": \"uri\",\n                          \"deprecationMessage\": \"This field is deprecated. Use `origins` instead.\",\n                          \"examples\": [\"https://www.ory.sh\"]\n                        },\n                        \"origins\": {\n                          \"type\": \"array\",\n                          \"title\": \"Relying Party Origins\",\n                          \"description\": \"A list of explicit RP origins. If left empty, this defaults to either `origin` or `id`, prepended with the current protocol schema (HTTP or HTTPS).\",\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"format\": \"uri\",\n                            \"examples\": [\n                              \"https://www.ory.sh\",\n                              \"https://auth.ory.sh\"\n                            ]\n                          }\n                        },\n                        \"icon\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Icon\",\n                          \"description\": \"An icon to help the user identify this RP.\",\n                          \"format\": \"uri\",\n                          \"deprecationMessage\": \"This field is deprecated and ignored due to security considerations.\",\n                          \"examples\": [\"https://www.ory.sh/an-icon.png\"]\n                        }\n                      },\n                      \"type\": \"object\",\n                      \"oneOf\": [\n                        {\n                          \"required\": [\"id\", \"display_name\"],\n                          \"properties\": {\n                            \"origin\": {\n                              \"not\": {}\n                            },\n                            \"origins\": {\n                              \"not\": {}\n                            }\n                          }\n                        },\n                        {\n                          \"required\": [\"id\", \"display_name\", \"origin\"],\n                          \"properties\": {\n                            \"origin\": {\n                              \"type\": \"string\"\n                            },\n                            \"origins\": {\n                              \"not\": {}\n                            }\n                          }\n                        },\n                        {\n                          \"required\": [\"id\", \"display_name\", \"origins\"],\n                          \"properties\": {\n                            \"origin\": {\n                              \"not\": {}\n                            },\n                            \"origins\": {\n                              \"type\": \"array\",\n                              \"items\": {\n                                \"type\": \"string\"\n                              }\n                            }\n                          }\n                        }\n                      ]\n                    }\n                  },\n                  \"additionalProperties\": false\n                }\n              },\n              \"if\": {\n                \"properties\": {\n                  \"enabled\": {\n                    \"const\": true\n                  }\n                },\n                \"required\": [\"enabled\"]\n              },\n              \"then\": {\n                \"required\": [\"config\"]\n              }\n            },\n            \"passkey\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables the Passkey method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"title\": \"Passkey Configuration\",\n                  \"properties\": {\n                    \"rp\": {\n                      \"title\": \"Relying Party (RP) Config\",\n                      \"properties\": {\n                        \"display_name\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Display Name\",\n                          \"description\": \"A name to help the user identify this RP.\",\n                          \"examples\": [\"Ory Foundation\"]\n                        },\n                        \"id\": {\n                          \"type\": \"string\",\n                          \"title\": \"Relying Party Identifier\",\n                          \"description\": \"The id must be a subset of the domain currently in the browser.\",\n                          \"examples\": [\"ory.sh\"]\n                        },\n                        \"origins\": {\n                          \"type\": \"array\",\n                          \"title\": \"Relying Party Origins\",\n                          \"description\": \"A list of explicit RP origins. If left empty, this defaults to either `origin` or `id`, prepended with the current protocol schema (HTTP or HTTPS).\",\n                          \"items\": {\n                            \"type\": \"string\",\n                            \"examples\": [\n                              \"https://www.ory.sh\",\n                              \"https://auth.ory.sh\"\n                            ]\n                          }\n                        }\n                      },\n                      \"type\": \"object\",\n                      \"required\": [\"display_name\", \"id\"]\n                    }\n                  },\n                  \"additionalProperties\": false\n                }\n              },\n              \"if\": {\n                \"properties\": {\n                  \"enabled\": {\n                    \"const\": true\n                  }\n                },\n                \"required\": [\"enabled\"]\n              },\n              \"then\": {\n                \"required\": [\"config\"]\n              }\n            },\n            \"oidc\": {\n              \"type\": \"object\",\n              \"title\": \"Specify OpenID Connect and OAuth2 Configuration\",\n              \"showEnvVarBlockForObject\": true,\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables OpenID Connect Method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"base_redirect_uri\": {\n                      \"type\": \"string\",\n                      \"title\": \"Base URL for OAuth2 Redirect URIs\",\n                      \"description\": \"Can be used to modify the base URL for OAuth2 Redirect URLs. If unset, the Public Base URL will be used.\",\n                      \"format\": \"uri\",\n                      \"examples\": [\"https://auth.myexample.org/\"]\n                    },\n                    \"providers\": {\n                      \"title\": \"OpenID Connect and OAuth2 Providers\",\n                      \"description\": \"A list and configuration of OAuth2 and OpenID Connect providers Ory Kratos should integrate with.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$ref\": \"#/definitions/selfServiceOIDCProvider\"\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    },\n    \"database\": {\n      \"type\": \"object\",\n      \"title\": \"Database related configuration\",\n      \"description\": \"Miscellaneous settings used in database related tasks (cleanup, etc.)\",\n      \"properties\": {\n        \"cleanup\": {\n          \"type\": \"object\",\n          \"title\": \"Database cleanup settings\",\n          \"description\": \"Settings that controls how the database cleanup process is configured (delays, batch size, etc.)\",\n          \"properties\": {\n            \"batch_size\": {\n              \"type\": \"integer\",\n              \"title\": \"Number of records to clean in one iteration\",\n              \"description\": \"Controls how many records should be purged from one table during database cleanup task\",\n              \"minimum\": 1,\n              \"default\": 100\n            },\n            \"sleep\": {\n              \"type\": \"object\",\n              \"title\": \"Delays between various database cleanup phases\",\n              \"description\": \"Configures delays between each step of the cleanup process. It is useful to tune the process so it will be efficient and performant.\",\n              \"properties\": {\n                \"tables\": {\n                  \"type\": \"string\",\n                  \"title\": \"Delay between each table cleanups\",\n                  \"description\": \"Controls the delay time between cleaning each table in one cleanup iteration\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1m\"\n                }\n              }\n            },\n            \"older_than\": {\n              \"type\": \"string\",\n              \"title\": \"Remove records older than\",\n              \"description\": \"Controls how old records do we want to leave\",\n              \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n              \"default\": \"0s\"\n            }\n          }\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"dsn\": {\n      \"type\": \"string\",\n      \"title\": \"Data Source Name\",\n      \"description\": \"DSN is used to specify the database credentials as a connection URI.\",\n      \"examples\": [\n        \"postgres://user: password@postgresd:5432/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"mysql://user:secret@tcp(mysqld:3306)/database?max_conns=20&max_idle_conns=4\",\n        \"cockroach://user@cockroachdb:26257/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc\"\n      ]\n    },\n    \"courier\": {\n      \"type\": \"object\",\n      \"title\": \"Courier configuration\",\n      \"description\": \"The courier is responsible for sending and delivering messages over email, sms, and other means.\",\n      \"properties\": {\n        \"templates\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"recovery\": {\n              \"$ref\": \"#/definitions/courierTemplates\"\n            },\n            \"recovery_code\": {\n              \"$ref\": \"#/definitions/courierTemplates\"\n            },\n            \"verification\": {\n              \"$ref\": \"#/definitions/courierTemplates\"\n            },\n            \"verification_code\": {\n              \"$ref\": \"#/definitions/courierTemplates\"\n            },\n            \"registration_code\": {\n              \"additionalProperties\": false,\n              \"type\": \"object\",\n              \"properties\": {\n                \"valid\": {\n                  \"additionalProperties\": false,\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"email\": {\n                      \"$ref\": \"#/definitions/emailCourierTemplate\"\n                    },\n                    \"sms\": {\n                      \"$ref\": \"#/definitions/smsCourierTemplate\"\n                    }\n                  },\n                  \"required\": [\"email\"]\n                }\n              }\n            },\n            \"login_code\": {\n              \"additionalProperties\": false,\n              \"type\": \"object\",\n              \"properties\": {\n                \"valid\": {\n                  \"additionalProperties\": false,\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"email\": {\n                      \"$ref\": \"#/definitions/emailCourierTemplate\"\n                    },\n                    \"sms\": {\n                      \"$ref\": \"#/definitions/smsCourierTemplate\"\n                    }\n                  },\n                  \"required\": [\"email\"]\n                }\n              }\n            }\n          }\n        },\n        \"template_override_path\": {\n          \"type\": \"string\",\n          \"title\": \"Override message templates\",\n          \"description\": \"You can override certain or all message templates by pointing this key to the path where the templates are located.\",\n          \"examples\": [\"/conf/courier-templates\"]\n        },\n        \"message_retries\": {\n          \"description\": \"Defines the maximum number of times the sending of a message is retried after it failed before it is marked as abandoned\",\n          \"type\": \"integer\",\n          \"default\": 5,\n          \"examples\": [10, 60]\n        },\n        \"worker\": {\n          \"description\": \"Configures the dispatch worker.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"pull_count\": {\n              \"description\": \"Defines how many messages are pulled from the queue at once.\",\n              \"type\": \"integer\",\n              \"default\": 10\n            },\n            \"pull_wait\": {\n              \"description\": \"Defines how long the worker waits before pulling messages from the queue again.\",\n              \"type\": \"string\",\n              \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n              \"default\": \"1s\"\n            }\n          }\n        },\n        \"delivery_strategy\": {\n          \"title\": \"Delivery Strategy\",\n          \"description\": \"Defines how emails will be sent, either through SMTP (default) or HTTP.\",\n          \"type\": \"string\",\n          \"enum\": [\"smtp\", \"http\"],\n          \"default\": \"smtp\"\n        },\n        \"http\": {\n          \"title\": \"HTTP Configuration\",\n          \"description\": \"Configures outgoing emails using HTTP.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"request_config\": {\n              \"$ref\": \"#/definitions/httpRequestConfig\"\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"smtp\": {\n          \"title\": \"SMTP Configuration\",\n          \"description\": \"Configures outgoing emails using the SMTP protocol.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"connection_uri\": {\n              \"title\": \"SMTP connection string\",\n              \"description\": \"This URI will be used to connect to the SMTP server. Use the scheme smtps for implicit TLS sessions or smtp for explicit StartTLS/cleartext sessions. Please note that TLS is always enforced with certificate trust verification by default for security reasons on both schemes. With the smtp scheme you can use the query parameter (`?disable_starttls=true`) to allow cleartext sessions or (`?disable_starttls=false`) to enforce StartTLS (default behaviour). Additionally, use the query parameter to allow (`?skip_ssl_verify=true`) or disallow (`?skip_ssl_verify=false`) self-signed TLS certificates (default behaviour) on both implicit and explicit TLS sessions.\",\n              \"examples\": [\n                \"smtps://foo:bar@my-mailserver:1234/?skip_ssl_verify=false\",\n                \"smtp://foo:bar@my-mailserver:1234/?disable_starttls=true (NOT RECOMMENDED: Cleartext smtp for devel and legacy infrastructure only)\",\n                \"smtp://foo:bar@my-mailserver:1234/ (Explicit StartTLS with certificate trust verification)\",\n                \"smtp://foo:bar@my-mailserver:1234/?skip_ssl_verify=true (NOT RECOMMENDED: Explicit StartTLS without certificate trust verification)\",\n                \"smtps://foo:bar@my-mailserver:1234/ (Implicit TLS with certificate trust verification)\",\n                \"smtps://foo:bar@my-mailserver:1234/?skip_ssl_verify=true (NOT RECOMMENDED: Implicit TLS without certificate trust verification)\",\n                \"smtps://subdomain.my-mailserver:1234/?server_name=my-mailserver (allows TLS to work if the server is hosted on a sudomain that uses a non-wildcard domain certificate)\"\n              ],\n              \"type\": \"string\",\n              \"pattern\": \"^smtps?://.*\"\n            },\n            \"client_cert_path\": {\n              \"title\": \"SMTP Client certificate path\",\n              \"description\": \"Path of the client X.509 certificate, in case of certificate based client authentication to the SMTP server.\",\n              \"type\": \"string\",\n              \"default\": \"\"\n            },\n            \"client_key_path\": {\n              \"title\": \"SMTP Client private key path\",\n              \"description\": \"Path of the client certificate private key, in case of certificate based client authentication to the SMTP server\",\n              \"type\": \"string\",\n              \"default\": \"\"\n            },\n            \"from_address\": {\n              \"title\": \"SMTP Sender Address\",\n              \"description\": \"The recipient of an email will see this as the sender address.\",\n              \"type\": \"string\",\n              \"format\": \"email\",\n              \"default\": \"no-reply@ory.kratos.sh\"\n            },\n            \"from_name\": {\n              \"title\": \"SMTP Sender Name\",\n              \"description\": \"The recipient of an email will see this as the sender name.\",\n              \"type\": \"string\",\n              \"examples\": [\"Bob\"]\n            },\n            \"headers\": {\n              \"title\": \"SMTP Headers\",\n              \"description\": \"These headers will be passed in the SMTP conversation -- e.g. when using the AWS SES SMTP interface for cross-account sending.\",\n              \"type\": \"object\",\n              \"additionalProperties\": {\n                \"type\": \"string\"\n              },\n              \"examples\": [\n                {\n                  \"X-SES-SOURCE-ARN\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\",\n                  \"X-SES-FROM-ARN\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\",\n                  \"X-SES-RETURN-PATH-ARN\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n                }\n              ]\n            },\n            \"local_name\": {\n              \"title\": \"SMTP HELO/EHLO name\",\n              \"description\": \"Identifier used in the SMTP HELO/EHLO command. Some SMTP relays require a unique identifier.\",\n              \"type\": \"string\",\n              \"default\": \"localhost\"\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"channels\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"title\": \"Courier channel configuration\",\n            \"type\": \"object\",\n            \"properties\": {\n              \"id\": {\n                \"type\": \"string\",\n                \"title\": \"Channel id\",\n                \"description\": \"The channel id. Corresponds to the .via property of the identity schema for recovery, verification, etc. Currently only sms is supported.\",\n                \"maxLength\": 32,\n                \"enum\": [\"sms\"]\n              },\n              \"type\": {\n                \"type\": \"string\",\n                \"title\": \"Channel type\",\n                \"description\": \"The channel type. Currently only http is supported.\",\n                \"enum\": [\"http\"]\n              },\n              \"request_config\": {\n                \"$ref\": \"#/definitions/httpRequestConfig\"\n              }\n            },\n            \"required\": [\"id\", \"request_config\"],\n            \"additionalProperties\": false\n          }\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"oauth2_provider\": {\n      \"title\": \"OAuth2 Provider Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"url\": {\n          \"title\": \"OAuth 2.0 Provider URL.\",\n          \"description\": \"If set, the login and registration flows will handle the Ory OAuth 2.0 & OpenID `login_challenge` query parameter to serve as an OpenID Connect Provider. This URL should point to Ory Hydra when you are not running on the Ory Network and be left untouched otherwise.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"https://some-slug.projects.oryapis.com\",\n            \"https://domain-of-ory-hydra:4445\"\n          ]\n        },\n        \"headers\": {\n          \"title\": \"HTTP Request Headers\",\n          \"description\": \"These headers will be passed in HTTP request to the OAuth2 Provider.\",\n          \"type\": \"object\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          },\n          \"examples\": [\n            {\n              \"Authorization\": \"Bearer some-token\"\n            }\n          ]\n        },\n        \"override_return_to\": {\n          \"title\": \"Persist OAuth2 request between flows\",\n          \"type\": \"boolean\",\n          \"default\": false,\n          \"description\": \"Override the return_to query parameter with the OAuth2 provider request URL when perfoming an OAuth2 login flow.\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"preview\": {\n      \"title\": \"Configure Preview Features\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"default_read_consistency_level\": {\n          \"type\": \"string\",\n          \"title\": \"Default Read Consistency Level\",\n          \"description\": \"The default consistency level to use when reading from the database. Defaults to `strong` to not break existing API contracts. Only set this to `eventual` if you can accept that other read APIs will suddenly return eventually consistent results. It is only effective in Ory Network.\",\n          \"enum\": [\"strong\", \"eventual\"],\n          \"default\": \"strong\"\n        }\n      }\n    },\n    \"serve\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"admin\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"request_log\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"disable_for_health\": {\n                  \"title\": \"Disable health endpoints request logging\",\n                  \"description\": \"Disable request logging for /health/alive and /health/ready endpoints\",\n                  \"type\": \"boolean\",\n                  \"default\": false\n                }\n              },\n              \"additionalProperties\": false\n            },\n            \"base_url\": {\n              \"title\": \"Admin Base URL\",\n              \"description\": \"The URL where the admin endpoint is exposed at.\",\n              \"type\": \"string\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://kratos.private-network:4434/\"]\n            },\n            \"host\": {\n              \"title\": \"Admin Host\",\n              \"description\": \"The host (interface) kratos' admin endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Admin Port\",\n              \"description\": \"The port kratos' admin endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4434],\n              \"default\": 4434\n            },\n            \"socket\": {\n              \"$ref\": \"#/definitions/socket\"\n            },\n            \"tls\": {\n              \"$ref\": \"#/definitions/tlsx\"\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"public\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"request_log\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"disable_for_health\": {\n                  \"title\": \"Disable health endpoints request logging\",\n                  \"description\": \"Disable request logging for /health/alive and /health/ready endpoints\",\n                  \"type\": \"boolean\",\n                  \"default\": false\n                }\n              },\n              \"additionalProperties\": false\n            },\n            \"cors\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"description\": \"Configures Cross Origin Resource Sharing for public endpoints.\",\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether CORS is enabled.\",\n                  \"default\": false\n                },\n                \"allowed_origins\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin.\",\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"minLength\": 1,\n                    \"not\": {\n                      \"type\": \"string\",\n                      \"description\": \"does match all strings that contain two or more (*)\",\n                      \"pattern\": \".*\\\\*.*\\\\*.*\"\n                    },\n                    \"anyOf\": [\n                      {\n                        \"type\": \"string\",\n                        \"format\": \"uri\"\n                      },\n                      {\n                        \"const\": \"*\"\n                      }\n                    ]\n                  },\n                  \"uniqueItems\": true,\n                  \"default\": [\"*\"],\n                  \"examples\": [\n                    [\n                      \"https://example.com\",\n                      \"https://*.example.com\",\n                      \"https://*.foo.example.com\"\n                    ]\n                  ]\n                },\n                \"allowed_methods\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of HTTP methods the user agent is allowed to use with cross-domain requests.\",\n                  \"default\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"enum\": [\n                      \"POST\",\n                      \"GET\",\n                      \"PUT\",\n                      \"PATCH\",\n                      \"DELETE\",\n                      \"CONNECT\",\n                      \"HEAD\",\n                      \"OPTIONS\",\n                      \"TRACE\"\n                    ]\n                  }\n                },\n                \"allowed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n                  \"default\": [\n                    \"Authorization\",\n                    \"Content-Type\",\n                    \"Max-Age\",\n                    \"X-Session-Token\",\n                    \"X-XSRF-TOKEN\",\n                    \"X-CSRF-TOKEN\"\n                  ],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"exposed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"Sets which headers are safe to expose to the API of a CORS API specification.\",\n                  \"default\": [\"Content-Type\"],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"allow_credentials\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\",\n                  \"default\": true\n                },\n                \"options_passthrough\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"TODO\",\n                  \"default\": false\n                },\n                \"max_age\": {\n                  \"type\": \"integer\",\n                  \"description\": \"Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request is preceded by a preflight request.\",\n                  \"default\": 0,\n                  \"minimum\": 0\n                },\n                \"debug\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Adds additional log output to debug server side CORS issues.\",\n                  \"default\": false\n                }\n              }\n            },\n            \"base_url\": {\n              \"$ref\": \"#/definitions/baseUrl\"\n            },\n            \"host\": {\n              \"title\": \"Public Host\",\n              \"description\": \"The host (interface) kratos' public endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Public Port\",\n              \"description\": \"The port kratos' public endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4433],\n              \"default\": 4433\n            },\n            \"socket\": {\n              \"$ref\": \"#/definitions/socket\"\n            },\n            \"tls\": {\n              \"$ref\": \"#/definitions/tlsx\"\n            }\n          },\n          \"additionalProperties\": false\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"tracing\": {\n      \"$ref\": \"ory://tracing-config\"\n    },\n    \"log\": {\n      \"title\": \"Log\",\n      \"description\": \"Configure logging using the following options. Logging will always be sent to stdout and stderr.\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"level\": {\n          \"description\": \"Debug enables stack traces on errors. Can also be set using environment variable LOG_LEVEL.\",\n          \"type\": \"string\",\n          \"default\": \"info\",\n          \"enum\": [\n            \"trace\",\n            \"debug\",\n            \"info\",\n            \"warning\",\n            \"error\",\n            \"fatal\",\n            \"panic\"\n          ]\n        },\n        \"leak_sensitive_values\": {\n          \"type\": \"boolean\",\n          \"title\": \"Leak Sensitive Log Values\",\n          \"description\": \"If set will leak sensitive values (e.g. emails) in the logs.\"\n        },\n        \"redaction_text\": {\n          \"type\": \"string\",\n          \"title\": \"Sensitive log value redaction text\",\n          \"description\": \"Text to use, when redacting sensitive log value.\"\n        },\n        \"format\": {\n          \"description\": \"The log format can either be text or JSON.\",\n          \"type\": \"string\",\n          \"enum\": [\"json\", \"text\"]\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"identity\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default_schema_id\": {\n          \"title\": \"The default Identity Schema\",\n          \"description\": \"This Identity Schema will be used as the default for self-service flows. Its ID needs to exist in the \\\"schemas\\\" list.\",\n          \"type\": \"string\",\n          \"default\": \"default\"\n        },\n        \"schemas\": {\n          \"type\": \"array\",\n          \"title\": \"All JSON Schemas for Identity Traits\",\n          \"description\": \"Note that identities that used the \\\"default_schema_url\\\" field in older kratos versions will be corrupted unless you specify their schema url with the id \\\"default\\\" in this list.\",\n          \"examples\": [\n            [\n              {\n                \"id\": \"customer\",\n                \"url\": \"base64://ewogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInR5cGUiOiAib2JqZWN0IiwKICAicHJvcGVydGllcyI6IHsKICAgICJiYXIiOiB7CiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0KICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJiYXIiCiAgXQp9\"\n              },\n              {\n                \"id\": \"employee\",\n                \"url\": \"https://foo.bar.com/path/to/employee.traits.schema.json\"\n              },\n              {\n                \"id\": \"employee-v2\",\n                \"url\": \"file://path/to/employee.v2.traits.schema.json\"\n              }\n            ]\n          ],\n          \"minItems\": 1,\n          \"items\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"id\": {\n                \"title\": \"The schema's ID.\",\n                \"type\": \"string\",\n                \"examples\": [\"employee\"]\n              },\n              \"url\": {\n                \"type\": \"string\",\n                \"title\": \"JSON Schema URL for identity traits schema\",\n                \"description\": \"URL for JSON Schema which describes a identity's traits. Can be a file path, a https URL, or a base64 encoded string.\",\n                \"format\": \"uri\",\n                \"examples\": [\n                  \"file://path/to/identity.traits.schema.json\",\n                  \"https://foo.bar.com/path/to/identity.traits.schema.json\",\n                  \"base64://ewogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInR5cGUiOiAib2JqZWN0IiwKICAicHJvcGVydGllcyI6IHsKICAgICJiYXIiOiB7CiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0KICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJiYXIiCiAgXQp9\"\n                ]\n              },\n              \"selfservice_selectable\": {\n                \"type\": \"boolean\",\n                \"title\": \"Is the schema enabled in self-service flows\",\n                \"description\": \"If set to true, this schema can be used explicity in self-service flows by setting `identity_schema` query parameter to the schema's ID.\",\n                \"default\": false\n              }\n            },\n            \"required\": [\"id\", \"url\"]\n          }\n        }\n      },\n      \"required\": [\"schemas\"],\n      \"additionalProperties\": false\n    },\n    \"secrets\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default\": {\n          \"type\": \"array\",\n          \"title\": \"Default Encryption Signing Secrets\",\n          \"description\": \"The first secret in the array is used for signing and encrypting things while all other keys are used to verify and decrypt older things that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        },\n        \"cookie\": {\n          \"type\": \"array\",\n          \"title\": \"Signing Keys for Cookies\",\n          \"description\": \"The first secret in the array is used for encrypting cookies while all other keys are used to decrypt older cookies that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        },\n        \"pagination\": {\n          \"type\": \"array\",\n          \"title\": \"Secrets to encrypt the pagination token\",\n          \"description\": \"To avoid clients reverse-engineering and relying on the implementation details of the pagination token, it is encrypted with these keys\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"minItems\": 1,\n          \"examples\": [\n            [\n              \"secret used for encryption\",\n              \"old secret kept for decryption\",\n              \"another old secret kept for decryption\"\n            ]\n          ]\n        },\n        \"cipher\": {\n          \"type\": \"array\",\n          \"title\": \"Secrets to use for encryption by cipher\",\n          \"description\": \"The first secret in the array is used for encryption data while all other keys are used to decrypt older data that were signed with.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 32,\n            \"maxLength\": 32\n          },\n          \"minItems\": 1\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"hashers\": {\n      \"title\": \"Hashing Algorithm Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"algorithm\": {\n          \"title\": \"Password hashing algorithm\",\n          \"description\": \"One of the values: argon2, bcrypt.\\nAny other hashes will be migrated to the set algorithm once an identity authenticates using their password.\",\n          \"type\": \"string\",\n          \"default\": \"bcrypt\",\n          \"enum\": [\"argon2\", \"bcrypt\"]\n        },\n        \"argon2\": {\n          \"title\": \"Configuration for the Argon2id hasher.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"memory\": {\n              \"type\": \"string\",\n              \"pattern\": \"^[0-9]+(B|KB|MB|GB|TB|PB|EB)\",\n              \"default\": \"128MB\"\n            },\n            \"iterations\": {\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"default\": 1\n            },\n            \"parallelism\": {\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"description\": \"Number of parallel workers, defaults to 2*runtime.NumCPU().\"\n            },\n            \"salt_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16,\n              \"default\": 16\n            },\n            \"key_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16,\n              \"default\": 32\n            },\n            \"expected_duration\": {\n              \"description\": \"The time a hashing operation (~login latency) should take.\",\n              \"type\": \"string\",\n              \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n              \"default\": \"500ms\"\n            },\n            \"expected_deviation\": {\n              \"description\": \"The standard deviation expected for hashing operations. If this value is exceeded you will be warned in the logs to adjust the parameters.\",\n              \"type\": \"string\",\n              \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n              \"default\": \"500ms\"\n            },\n            \"dedicated_memory\": {\n              \"description\": \"The memory dedicated for Kratos. As password hashing is very resource intense, Kratos will monitor the memory consumption and warn about high values.\",\n              \"type\": \"string\",\n              \"pattern\": \"^[0-9]+(B|KB|MB|GB|TB|PB|EB)\",\n              \"default\": \"1GB\"\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"bcrypt\": {\n          \"title\": \"Configuration for the Bcrypt hasher. Minimum is 4 when --dev flag is used and 12 otherwise.\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"required\": [\"cost\"],\n          \"properties\": {\n            \"cost\": {\n              \"type\": \"integer\",\n              \"minimum\": 4,\n              \"maximum\": 31,\n              \"default\": 12\n            }\n          }\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"ciphers\": {\n      \"title\": \"Cipher Algorithm Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"algorithm\": {\n          \"title\": \"ciphering algorithm\",\n          \"description\": \"One of the values: noop, aes, xchacha20-poly1305\",\n          \"type\": \"string\",\n          \"default\": \"noop\",\n          \"enum\": [\"noop\", \"aes\", \"xchacha20-poly1305\"]\n        }\n      }\n    },\n    \"cookies\": {\n      \"type\": \"object\",\n      \"title\": \"HTTP Cookie Configuration\",\n      \"description\": \"Configure the HTTP Cookies. Applies to both CSRF and session cookies.\",\n      \"properties\": {\n        \"domain\": {\n          \"title\": \"HTTP Cookie Domain\",\n          \"description\": \"Sets the cookie domain for session and CSRF cookies. Useful when dealing with subdomains. Use with care!\",\n          \"type\": \"string\"\n        },\n        \"path\": {\n          \"title\": \"HTTP  Cookie Path\",\n          \"description\": \"Sets the session and CSRF cookie path. Use with care!\",\n          \"type\": \"string\",\n          \"default\": \"/\"\n        },\n        \"secure\": {\n          \"title\": \"Session Cookie Secure Flag\",\n          \"description\": \"Sets the session secure flag. If unset, defaults to !dev mode.\",\n          \"type\": \"string\"\n        },\n        \"same_site\": {\n          \"title\": \"HTTP Cookie Same Site Configuration\",\n          \"description\": \"Sets the session and CSRF cookie SameSite.\",\n          \"type\": \"string\",\n          \"enum\": [\"Strict\", \"Lax\", \"None\"],\n          \"default\": \"Lax\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"session\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"whoami\": {\n          \"title\": \"WhoAmI / ToSession Settings\",\n          \"description\": \"Control how the `/sessions/whoami` endpoint is behaving.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"required_aal\": {\n              \"$ref\": \"#/definitions/featureRequiredAal\"\n            },\n            \"tokenizer\": {\n              \"title\": \"Tokenizer configuration\",\n              \"description\": \"Configure the tokenizer, responsible for converting a session into a token format such as JWT.\",\n              \"type\": \"object\",\n              \"properties\": {\n                \"templates\": {\n                  \"title\": \"Tokenizer templates\",\n                  \"description\": \"A list of different templates that govern how a session is converted to a token format.\",\n                  \"type\": \"object\",\n                  \"patternProperties\": {\n                    \"[a-zA-Z0-9-_.]+\": {\n                      \"type\": \"object\",\n                      \"required\": [\"jwks_url\"],\n                      \"properties\": {\n                        \"ttl\": {\n                          \"type\": \"string\",\n                          \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n                          \"default\": \"1m\",\n                          \"title\": \"Token time to live\"\n                        },\n                        \"claims_mapper_url\": {\n                          \"type\": \"string\",\n                          \"format\": \"uri\",\n                          \"title\": \"Jsonnet mapper URL\"\n                        },\n                        \"jwks_url\": {\n                          \"type\": \"string\",\n                          \"format\": \"uri\",\n                          \"title\": \"JSON Web Key Set URL\"\n                        },\n                        \"subject_source\": {\n                          \"type\": \"string\",\n                          \"title\": \"Subject source\",\n                          \"description\": \"The source of the subject claim in the token. Can be one of: `id`, or `external_id`.\",\n                          \"enum\": [\"id\", \"external_id\"],\n                          \"default\": \"id\"\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"lifespan\": {\n          \"title\": \"Session Lifespan\",\n          \"description\": \"Defines how long a session is active. Once that lifespan has been reached, the user needs to sign in again.\",\n          \"type\": \"string\",\n          \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n          \"default\": \"24h\",\n          \"examples\": [\"1h\", \"1m\", \"1s\"]\n        },\n        \"cookie\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"domain\": {\n              \"title\": \"Session Cookie Domain\",\n              \"description\": \"Sets the session cookie domain. Useful when dealing with subdomains. Use with care! Overrides `cookies.domain`.\",\n              \"type\": \"string\"\n            },\n            \"name\": {\n              \"title\": \"Session Cookie Name\",\n              \"description\": \"Sets the session cookie name. Use with care!\",\n              \"type\": \"string\",\n              \"default\": \"ory_kratos_session\"\n            },\n            \"persistent\": {\n              \"title\": \"Make Session Cookie Persistent\",\n              \"description\": \"If set to true will persist the cookie in the end-user's browser using the `max-age` parameter which is set to the `session.lifespan` value. Persistent cookies are not deleted when the browser is closed (e.g. on reboot or alt+f4). This option affects the Ory OAuth2 and OpenID Provider's remember feature as well.\",\n              \"type\": \"boolean\",\n              \"default\": true\n            },\n            \"path\": {\n              \"title\": \"Session Cookie Path\",\n              \"description\": \"Sets the session cookie path. Use with care! Overrides `cookies.path`.\",\n              \"type\": \"string\"\n            },\n            \"secure\": {\n              \"title\": \"Session Cookie Secure Flag\",\n              \"description\": \"Sets the session secure flag. If unset, defaults to !dev mode.\",\n              \"type\": \"string\"\n            },\n            \"same_site\": {\n              \"title\": \"Session Cookie SameSite Configuration\",\n              \"description\": \"Sets the session cookie SameSite. Overrides `cookies.same_site`.\",\n              \"type\": \"string\",\n              \"enum\": [\"Strict\", \"Lax\", \"None\"]\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"earliest_possible_extend\": {\n          \"title\": \"Earliest Possible Session Extension\",\n          \"description\": \"Sets when a session can be extended. Settings this value to `24h` will prevent the session from being extended before until 24 hours before it expires. This setting prevents excessive writes to the database. We highly recommend setting this value.\",\n          \"type\": \"string\",\n          \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\",\n          \"examples\": [\"1h\", \"1m\", \"1s\"]\n        }\n      }\n    },\n    \"security\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"account_enumeration\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"mitigate\": {\n              \"type\": \"boolean\",\n              \"default\": false,\n              \"description\": \"Mitigate account enumeration by making it harder to figure out if an identifier (email, phone number) exists or not. Enabling this setting degrades user experience. This setting does not mitigate all possible attack vectors yet.\"\n            }\n          }\n        }\n      }\n    },\n    \"version\": {\n      \"title\": \"The kratos version this config is written for.\",\n      \"description\": \"SemVer according to https://semver.org/ prefixed with `v` as in our releases.\",\n      \"type\": \"string\",\n      \"pattern\": \"^(v(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?)|$\",\n      \"examples\": [\"v0.5.0-alpha.1\"]\n    },\n    \"dev\": {\n      \"type\": \"boolean\"\n    },\n    \"help\": {\n      \"type\": \"boolean\"\n    },\n    \"sqa-opt-out\": {\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"description\": \"This is a CLI flag and environment variable and can not be set using the config file.\"\n    },\n    \"watch-courier\": {\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"description\": \"This is a CLI flag and environment variable and can not be set using the config file.\"\n    },\n    \"expose-metrics-port\": {\n      \"title\": \"Metrics port\",\n      \"description\": \"The port the courier's metrics endpoint listens on (0/disabled by default). This is a CLI flag and environment variable and can not be set using the config file.\",\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 65535,\n      \"examples\": [4434],\n      \"default\": 0\n    },\n    \"config\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"description\": \"This is a CLI flag and environment variable and can not be set using the config file.\"\n    },\n    \"clients\": {\n      \"title\": \"Global outgoing network settings\",\n      \"description\": \"Configure how outgoing network calls behave.\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"http\": {\n          \"title\": \"Global HTTP client configuration\",\n          \"description\": \"Configure how outgoing HTTP calls behave.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"disallow_private_ip_ranges\": {\n              \"title\": \"Disallow private IP ranges\",\n              \"description\": \"Disallow all outgoing HTTP calls to private IP ranges. This feature can help protect against SSRF attacks.\",\n              \"type\": \"boolean\",\n              \"default\": false\n            },\n            \"private_ip_exception_urls\": {\n              \"title\": \"Add exempt URLs to private IP ranges\",\n              \"description\": \"Allows the given URLs to be called despite them being in the private IP range. URLs need to have an exact and case-sensitive match to be excempt.\",\n              \"type\": \"array\",\n              \"items\": {\n                \"type\": \"string\",\n                \"format\": \"uri-reference\"\n              },\n              \"default\": []\n            }\n          }\n        },\n        \"web_hook\": {\n          \"title\": \"Global web_hook HTTP client configuration\",\n          \"description\": \"Configure the global HTTP client of the web_hook action.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"header_allowlist\": {\n              \"title\": \"Allowed request headers\",\n              \"description\": \"List of request headers that are forwarded to the web hook target in canonical form.\",\n              \"type\": \"array\",\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"default\": [\n                \"Accept\",\n                \"Accept-Encoding\",\n                \"Accept-Language\",\n                \"Content-Length\",\n                \"Content-Type\",\n                \"Origin\",\n                \"Priority\",\n                \"Referer\",\n                \"Sec-Ch-Ua\",\n                \"Sec-Ch-Ua-Mobile\",\n                \"Sec-Ch-Ua-Platform\",\n                \"Sec-Fetch-Dest\",\n                \"Sec-Fetch-Mode\",\n                \"Sec-Fetch-Site\",\n                \"Sec-Fetch-User\",\n                \"True-Client-Ip\",\n                \"User-Agent\"\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"feature_flags\": {\n      \"title\": \"Feature flags\",\n      \"properties\": {\n        \"cacheable_sessions\": {\n          \"type\": \"boolean\",\n          \"title\": \"Enable Ory Sessions caching\",\n          \"description\": \"If enabled allows Ory Sessions to be cached. Only effective in the Ory Network.\",\n          \"default\": false\n        },\n        \"cacheable_sessions_max_age\": {\n          \"title\": \"Set Ory Session Edge Caching maximum age\",\n          \"description\": \"Set how long Ory Sessions are cached on the edge. If unset, the session expiry will be used. Only effective in the Ory Network.\",\n          \"type\": \"string\",\n          \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))+$\"\n        },\n        \"use_continue_with_transitions\": {\n          \"type\": \"boolean\",\n          \"title\": \"Enable new flow transitions using `continue_with` items\",\n          \"description\": \"If enabled allows new flow transitions using `continue_with` items.\",\n          \"default\": false\n        },\n        \"choose_recovery_address\": {\n          \"type\": \"boolean\",\n          \"title\": \"Enable new recovery screens to pick which address to send a recovery code/link to\",\n          \"description\": \"If enabled, enable new recovery screens to pick which address to send a recovery code to, and can send a code via SMS. It is safe to toggle it back and forth, existing recovery flows will be handled with their respective logic. That is because it is decided at creation time whether a recovery flow is V1 or V2 and this cannot be changed afterwards. Thus, if a recovery flow is created with this flag enabled, it will be created as a recovery v2 flow. If this flag is disabled while this flow is still active, this flow will still be handled with the correct logic (v2).\",\n          \"default\": false\n        },\n        \"legacy_continue_with_verification_ui\": {\n          \"type\": \"boolean\",\n          \"title\": \"Always include show_verification_ui in continue_with\",\n          \"description\": \"If true, restores the legacy behavior of always including `show_verification_ui` in the registration flow's `continue_with` when verification is enabled. If set to false, `show_verification_ui` is only set in `continue_with` if the `show_verification_ui` hook is used. This flag will be removed in the future.\",\n          \"deprecationMessage\": \"This behavior is deprecated and will be removed in the future. Use the `show_verification_hook` in the post-registration hook instead.\",\n          \"default\": false\n        },\n        \"legacy_require_verified_login_error\": {\n          \"type\": \"boolean\",\n          \"title\": \"Return a form error if the login identifier is not verified\",\n          \"description\": \"If true, the login flow will return a form error if the login identifier is not verified, which restores legacy behavior. If this value is false, the `continue_with` array will contain a `show_verification_ui` hook instead.\",\n          \"deprecationMessage\": \"This behavior is deprecated and will be removed in the future. Please upgrade your SDKs.\",\n          \"default\": false\n        },\n        \"faster_session_extend\": {\n          \"type\": \"boolean\",\n          \"title\": \"Enable faster session extension\",\n          \"description\": \"If enabled allows faster session extension by skipping the session lookup. Disabling this feature will be deprecated in the future.\",\n          \"default\": false\n        },\n        \"password_profile_registration_node_group\": {\n          \"title\": \"Registration node group\",\n          \"description\": \"The node group to use for registration flows. Previously, the node group for the password method's profile fields was `password`. Going forward, it will be `default`. This switch can toggle between those two for backwards compatibility.\",\n          \"enum\": [\"password\", \"default\"],\n          \"default\": \"default\"\n        },\n        \"legacy_oidc_registration_node_group\": {\n          \"title\": \"Registration node group for OIDC\",\n          \"description\": \"The node group to use for registration flows. Previously, the node group for the oidc method's profile fields was `oidc`. Going forward, it will be `default`. This switch can toggle between those two for backwards compatibility and will be removed in the future.\",\n          \"default\": false,\n          \"type\": \"boolean\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"organizations\": {\n      \"title\": \"Organizations\",\n      \"description\": \"Please use selfservice.methods.b2b instead. This key will be removed. Only effective in the Ory Network.\",\n      \"type\": \"array\",\n      \"default\": []\n    },\n    \"enterprise\": {\n      \"title\": \"Enterprise features\",\n      \"description\": \"Specifies enterprise features. Only effective in the Ory Network or with a valid license.\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"identity_schema_fallback_url_template\": {\n          \"type\": \"string\",\n          \"title\": \"Fallback URL template for identity schemas\",\n          \"description\": \"A fallback URL template used when looking up identity schemas.\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"revision\": {\n      \"title\": \"Config revision\",\n      \"description\": \"Set a recognizable revision. This could be the commit time or a random value. This value is exposed at the `/health/config` endpoint and allows you to ensure that the correct config is loaded.\",\n      \"type\": \"string\"\n    }\n  },\n  \"allOf\": [\n    {\n      \"if\": {\n        \"properties\": {\n          \"selfservice\": {\n            \"properties\": {\n              \"flows\": {\n                \"oneOf\": [\n                  {\n                    \"properties\": {\n                      \"verification\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"verification\"]\n                  },\n                  {\n                    \"properties\": {\n                      \"recovery\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"recovery\"]\n                  }\n                ]\n              }\n            },\n            \"required\": [\"flows\"]\n          }\n        },\n        \"required\": [\"selfservice\"]\n      },\n      \"then\": {\n        \"required\": [\"courier\"]\n      }\n    },\n    {\n      \"if\": {\n        \"properties\": {\n          \"ciphers\": {\n            \"properties\": {\n              \"algorithm\": {\n                \"oneOf\": [\n                  {\n                    \"const\": \"aes\"\n                  },\n                  {\n                    \"const\": \"xchacha20-poly1305\"\n                  }\n                ]\n              }\n            },\n            \"required\": [\"algorithm\"]\n          }\n        },\n        \"required\": [\"ciphers\"]\n      },\n      \"then\": {\n        \"required\": [\"secrets\"],\n        \"properties\": {\n          \"secrets\": {\n            \"required\": [\"cipher\"]\n          }\n        }\n      }\n    }\n  ],\n  \"required\": [\"identity\", \"dsn\", \"selfservice\"],\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "embedx/embedx.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage embedx\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"io\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/otelx\"\n)\n\n//go:embed config.schema.json\nvar ConfigSchema []byte\n\n//go:embed identity_meta.schema.json\nvar IdentityMetaSchema []byte\n\n//go:embed identity_extension.schema.json\nvar IdentityExtensionSchema []byte\n\ntype SchemaType int\n\nconst (\n\tConfig SchemaType = iota + 1\n\tIdentityMeta\n\tIdentityExtension\n)\n\ntype Schema struct {\n\tid           string\n\tdata         []byte\n\tdependencies []*Schema\n}\n\nvar (\n\tidentityExt = &Schema{\n\t\tid:           gjson.GetBytes(IdentityExtensionSchema, \"$id\").Str,\n\t\tdata:         IdentityExtensionSchema,\n\t\tdependencies: nil,\n\t}\n\n\tschemas = map[SchemaType]*Schema{\n\t\tConfig: {\n\t\t\tid:           gjson.GetBytes(ConfigSchema, \"$id\").Str,\n\t\t\tdata:         ConfigSchema,\n\t\t\tdependencies: nil,\n\t\t},\n\t\tIdentityMeta: {\n\t\t\tid:   gjson.GetBytes(IdentityMetaSchema, \"$id\").Str,\n\t\t\tdata: IdentityMetaSchema,\n\t\t\tdependencies: []*Schema{\n\t\t\t\tidentityExt,\n\t\t\t},\n\t\t},\n\t\tIdentityExtension: identityExt,\n\t}\n)\n\nfunc getSchema(schema SchemaType) (*Schema, error) {\n\tif val, ok := schemas[schema]; ok {\n\t\treturn val, nil\n\t}\n\treturn nil, errors.Errorf(\"the specified schema type (%d) is not supported\", int(schema))\n}\n\nfunc (s SchemaType) GetSchemaID() string {\n\treturn schemas[s].id\n}\n\n// AddSchemaResources adds the specified schemas including their dependencies to the compiler.\n// The interface is specified instead of `jsonschema.Compiler` to allow the use of any jsonschema library fork or version.\nfunc AddSchemaResources(c interface {\n\tAddResource(url string, r io.Reader) error\n}, opts ...SchemaType) error {\n\tvar sc []*Schema\n\n\tfor _, o := range opts {\n\t\ts, err := getSchema(o)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsc = append(sc, s)\n\t}\n\n\tif err := addSchemaResources(c, sc); err != nil {\n\t\treturn err\n\t}\n\n\tif err := otelx.AddConfigSchema(c); err != nil {\n\t\treturn err\n\t}\n\n\treturn configx.AddSchemaResources(c)\n}\n\nfunc addSchemaResources(c interface {\n\tAddResource(url string, r io.Reader) error\n}, schemas []*Schema) error {\n\tfor _, s := range schemas {\n\t\tif err := c.AddResource(s.id, bytes.NewReader(s.data)); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tif s.dependencies != nil {\n\t\t\tif err := addSchemaResources(c, s.dependencies); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "embedx/embedx_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage embedx\n\nimport (\n\t\"context\"\n\t\"embed\"\n\t\"io/fs\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\nfunc TestAddSchemaResources(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\tdescription       string\n\t\tdependencies      []SchemaType\n\t\textraDependencies []SchemaType\n\t\tmustFail          bool\n\t\tfailMessage       string\n\t}{\n\t\t{\n\t\t\tdescription:       \"config schema\",\n\t\t\tdependencies:      []SchemaType{Config},\n\t\t\textraDependencies: nil,\n\t\t},\n\t\t{\n\t\t\tdescription:       \"identity schema with dependencies\",\n\t\t\tdependencies:      []SchemaType{IdentityMeta},\n\t\t\textraDependencies: []SchemaType{IdentityExtension},\n\t\t},\n\t\t{\n\t\t\tdescription:       \"multiple schemas\",\n\t\t\tdependencies:      []SchemaType{Config, IdentityMeta, IdentityExtension},\n\t\t\textraDependencies: nil,\n\t\t},\n\t\t{\n\t\t\tdescription:       \"verify dependencies are also loaded\",\n\t\t\tdependencies:      []SchemaType{IdentityMeta},\n\t\t\textraDependencies: []SchemaType{IdentityExtension},\n\t\t},\n\t\t{\n\t\t\tdescription:  \"must fail on unsupported schema types\",\n\t\t\tdependencies: []SchemaType{4, 10},\n\t\t\tmustFail:     true,\n\t\t\tfailMessage:  \"the specified schema type (4) is not supported\",\n\t\t},\n\t} {\n\t\tt.Run(\"case=\"+tc.description, func(t *testing.T) {\n\t\t\tc := jsonschema.NewCompiler()\n\t\t\terr := AddSchemaResources(c, tc.dependencies...)\n\n\t\t\tif tc.mustFail {\n\t\t\t\tassert.Errorf(t, err, \"an error must be thrown on `%s`\", tc.description)\n\t\t\t\tassert.EqualError(t, err, tc.failMessage)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\n\t\t\tif !tc.mustFail {\n\t\t\t\tfor _, s := range append(tc.dependencies, tc.extraDependencies...) {\n\t\t\t\t\t_, err := c.Compile(context.Background(), s.GetSchemaID())\n\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n//go:embed testdata/identity_meta.*\nvar identityMetaTestCases embed.FS\n\nfunc TestIdentityMetaSchema(t *testing.T) {\n\tc := jsonschema.NewCompiler()\n\terr := AddSchemaResources(c, IdentityMeta, IdentityExtension)\n\trequire.NoError(t, err)\n\n\tschema, err := c.Compile(context.Background(), IdentityMeta.GetSchemaID())\n\trequire.NoError(t, err)\n\n\trequire.NoError(t, fs.WalkDir(identityMetaTestCases, \".\", func(path string, d fs.DirEntry, err error) error {\n\t\tif d.IsDir() {\n\t\t\treturn nil\n\t\t}\n\n\t\tt.Run(\"case=\"+path, func(t *testing.T) {\n\t\t\tf, err := identityMetaTestCases.Open(path)\n\t\t\trequire.NoError(t, err)\n\n\t\t\terr = schema.Validate(f)\n\t\t\tif strings.HasSuffix(path, \"invalid.schema.json\") {\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\n\t\treturn nil\n\t}))\n}\n"
  },
  {
    "path": "embedx/identity_extension.schema.json",
    "content": "{\n  \"$id\": \"ory://identity-extension\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"allOf\": [\n    {\n      \"properties\": {\n        \"ory.sh/kratos\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"credentials\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"password\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"identifier\": {\n                      \"type\": \"boolean\"\n                    }\n                  }\n                },\n                \"webauthn\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"identifier\": {\n                      \"type\": \"boolean\"\n                    }\n                  }\n                },\n                \"passkey\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"display_name\": {\n                      \"type\": \"boolean\"\n                    }\n                  }\n                },\n                \"totp\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"account_name\": {\n                      \"type\": \"boolean\"\n                    }\n                  }\n                },\n                \"code\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"identifier\": {\n                      \"type\": \"boolean\"\n                    },\n                    \"via\": {\n                      \"type\": \"string\",\n                      \"enum\": [\"email\", \"sms\"]\n                    }\n                  }\n                }\n              }\n            },\n            \"verification\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"via\": {\n                  \"type\": \"string\",\n                  \"enum\": [\"email\", \"sms\"]\n                }\n              }\n            },\n            \"recovery\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"via\": {\n                  \"type\": \"string\",\n                  \"enum\": [\"email\", \"sms\"]\n                }\n              }\n            },\n            \"organizations\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"matcher\": {\n                  \"type\": \"string\",\n                  \"enum\": [\"email_domain\"]\n                }\n              }\n            }\n          }\n        }\n      }\n    },\n    {\n      \"patternProperties\": {\n        \".*\": {\n          \"$ref\": \"#\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "embedx/identity_meta.schema.json",
    "content": "{\n  \"$id\": \"ory://identity-meta\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"allOf\": [\n    {\n      \"$ref\": \"http://json-schema.org/draft-07/schema#\"\n    },\n    {\n      \"properties\": {\n        \"properties\": {\n          \"type\": \"object\",\n          \"required\": [\"traits\"],\n          \"properties\": {\n            \"traits\": {\n              \"type\": \"object\",\n              \"required\": [\"properties\"],\n              \"properties\": {\n                \"type\": {\n                  \"const\": \"object\"\n                },\n                \"properties\": {\n                  \"type\": \"object\",\n                  \"minProperties\": 1,\n                  \"patternProperties\": {\n                    \".*\": {\n                      \"type\": \"object\",\n                      \"if\": {\n                        \"properties\": {\n                          \"ory.sh/kratos\": {\n                            \"type\": \"object\",\n                            \"properties\": {\n                              \"verification\": {}\n                            },\n                            \"required\": [\"verification\"]\n                          }\n                        },\n                        \"required\": [\"ory.sh/kratos\"]\n                      },\n                      \"then\": {\n                        \"properties\": {\n                          \"format\": {\n                            \"enum\": [\n                              \"email\",\n                              \"tel\",\n                              \"date\",\n                              \"time\",\n                              \"date-time\",\n                              \"no-validate\"\n                            ]\n                          }\n                        }\n                      },\n                      \"allOf\": [\n                        {\n                          \"$ref\": \"ory://identity-extension\"\n                        }\n                      ]\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\"properties\"]\n    }\n  ]\n}\n"
  },
  {
    "path": "embedx/testdata/identity_meta.no_traits.invalid.schema.json",
    "content": "{}\n"
  },
  {
    "path": "embedx/testdata/identity_meta.simple.valid.schema.json",
    "content": "{\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "embedx/testdata/identity_meta.verification_format.invalid.schema.json",
    "content": "{\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"unknown\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "examples/go/identity/create/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\n\tory \"github.com/ory/client-go\"\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\t\"github.com/ory/kratos/x\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc createIdentity() *ory.Identity {\n\tctx := context.Background()\n\n\tidentity, res, err := client.IdentityAPI.CreateIdentity(ctx).\n\t\tCreateIdentityBody(ory.CreateIdentityBody{\n\t\t\tSchemaId: \"default\",\n\t\t\tTraits: map[string]interface{}{\n\t\t\t\t\"email\": \"dev+\" + x.NewUUID().String() + \"@ory.sh\",\n\t\t\t},\n\t\t}).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\treturn identity\n}\n\nfunc main() {\n\tpkg.PrintJSONPretty(createIdentity())\n}\n"
  },
  {
    "path": "examples/go/identity/create/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tclient = pkg.TestClient(t)\n\trequire.NotEmpty(t, createIdentity().Id)\n}\n"
  },
  {
    "path": "examples/go/identity/delete/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc deleteIdentity() {\n\tctx := context.Background()\n\n\tidentity := pkg.CreateIdentity(client)\n\n\tres, err := client.IdentityAPI.DeleteIdentity(ctx, identity.Id).Execute()\n\tpkg.SDKExitOnError(err, res)\n}\n\nfunc main() {\n\tdeleteIdentity()\n\tfmt.Println(\"Success\")\n}\n"
  },
  {
    "path": "examples/go/identity/delete/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\tdeleteIdentity()\n}\n"
  },
  {
    "path": "examples/go/identity/get/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\n\tory \"github.com/ory/client-go\"\n\t\"github.com/ory/kratos/examples/go/pkg\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc getIdentity() *ory.Identity {\n\tctx := context.Background()\n\tcreated := pkg.CreateIdentity(client)\n\n\tidentity, res, err := client.IdentityAPI.GetIdentity(ctx, created.Id).IncludeCredential([]string{\"password\"}).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\treturn identity\n}\n\nfunc main() {\n\tpkg.PrintJSONPretty(getIdentity())\n}\n"
  },
  {
    "path": "examples/go/identity/get/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tclient = pkg.TestClient(t)\n\trequire.NotEmpty(t, getIdentity().Id)\n}\n"
  },
  {
    "path": "examples/go/identity/update/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\n\tory \"github.com/ory/client-go\"\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\t\"github.com/ory/kratos/x\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc updateIdentity() *ory.Identity {\n\tctx := context.Background()\n\ttoUpdate := pkg.CreateIdentity(client)\n\n\tidentity, res, err := client.IdentityAPI.UpdateIdentity(ctx, toUpdate.Id).UpdateIdentityBody(ory.UpdateIdentityBody{\n\t\tTraits: map[string]interface{}{\n\t\t\t\"email\": \"dev+not-\" + x.NewUUID().String() + \"@ory.sh\",\n\t\t},\n\t}).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\treturn identity\n}\n\nfunc main() {\n\tpkg.PrintJSONPretty(updateIdentity())\n}\n"
  },
  {
    "path": "examples/go/identity/update/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\tidentity := updateIdentity()\n\trequire.NotEmpty(t, identity.Id)\n\tassert.Contains(t, identity.Traits.(map[string]interface{})[\"email\"].(string), \"dev+not\")\n}\n"
  },
  {
    "path": "examples/go/pkg/common.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage pkg\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/cookiejar\"\n\t\"os\"\n\t\"testing\"\n\n\tory \"github.com/ory/client-go\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc PrintJSONPretty(v interface{}) {\n\tout, _ := json.MarshalIndent(v, \"\", \"  \")\n\tfmt.Println(string(out))\n}\n\nfunc TestClient(t *testing.T) *ory.APIClient {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\treturn NewSDKForSelfHosted(publicURL)\n}\n\nfunc NewSDK(project string) *ory.APIClient {\n\treturn NewSDKForSelfHosted(fmt.Sprintf(\"https://%s.projects.oryapis.com/api/kratos/public\", project))\n}\n\nfunc NewSDKForSelfHosted(endpoint string) *ory.APIClient {\n\tconf := ory.NewConfiguration()\n\tconf.Servers = ory.ServerConfigurations{{URL: endpoint}}\n\tcj, _ := cookiejar.New(nil)\n\tconf.HTTPClient = &http.Client{Jar: cj}\n\treturn ory.NewAPIClient(conf)\n}\n\nfunc ExitOnError(err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\tout, _ := json.MarshalIndent(err, \"\", \"  \")\n\tfmt.Printf(\"%s\\n\\nAn error occurred: %+v\\n\", out, err)\n\tos.Exit(1)\n}\n\nfunc SDKExitOnError(err error, res *http.Response) {\n\tif err == nil {\n\t\treturn\n\t}\n\tvar body []byte\n\tif res != nil {\n\t\tbody, _ = json.MarshalIndent(json.RawMessage(x.MustReadAll(res.Body)), \"\", \"  \")\n\t}\n\tout, _ := json.MarshalIndent(err, \"\", \"  \")\n\tfmt.Printf(\"%s\\n\\nAn error occurred: %+v\\nbody: %s\\n\", out, err, body)\n\tos.Exit(1)\n}\n"
  },
  {
    "path": "examples/go/pkg/resources.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage pkg\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"strings\"\n\n\t\"github.com/gofrs/uuid\"\n\n\tory \"github.com/ory/client-go\"\n)\n\nfunc RandomCredentials() (email, password string) {\n\temail = \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\tpassword = strings.ReplaceAll(uuid.Must(uuid.NewV4()).String(), \"-\", \"\")\n\treturn\n}\n\n// CreateIdentityWithSession creates an identity and an Ory Session Token for it.\nfunc CreateIdentityWithSession(c *ory.APIClient, email, password string) (*ory.Session, string) {\n\tctx := context.Background()\n\n\tif email == \"\" {\n\t\temail, _ = RandomCredentials()\n\t}\n\n\tif password == \"\" {\n\t\t_, password = RandomCredentials()\n\t}\n\n\t// Initialize a registration flow\n\tflow, _, err := c.FrontendAPI.CreateNativeRegistrationFlow(ctx).Execute()\n\tExitOnError(err)\n\n\t// Submit the registration flow\n\tresult, res, err := c.FrontendAPI.UpdateRegistrationFlow(ctx).Flow(flow.Id).UpdateRegistrationFlowBody(\n\t\tory.UpdateRegistrationFlowWithPasswordMethodAsUpdateRegistrationFlowBody(&ory.UpdateRegistrationFlowWithPasswordMethod{\n\t\t\tMethod:   \"password\",\n\t\t\tPassword: password,\n\t\t\tTraits:   map[string]interface{}{\"email\": email},\n\t\t}),\n\t).Execute()\n\tSDKExitOnError(err, res)\n\n\tif result.Session == nil {\n\t\tlog.Fatalf(\"The server is expected to create sessions for new registrations.\")\n\t}\n\n\treturn result.Session, *result.SessionToken\n}\n\nfunc CreateIdentity(c *ory.APIClient) *ory.Identity {\n\tctx := context.Background()\n\n\temail, _ := RandomCredentials()\n\tidentity, _, err := c.IdentityAPI.CreateIdentity(ctx).CreateIdentityBody(ory.CreateIdentityBody{\n\t\tSchemaId: \"default\",\n\t\tTraits: map[string]interface{}{\n\t\t\t\"email\": email,\n\t\t}}).Execute()\n\tExitOnError(err)\n\treturn identity\n}\n"
  },
  {
    "path": "examples/go/pkg/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "examples/go/pkg/stub/kratos.yaml",
    "content": "version: v0.6.3-alpha.1\n\ndsn: memory\n\nserve:\n  public:\n    cors:\n      enabled: true\n\nselfservice:\n  default_browser_return_url: http://127.0.0.1:4455/\n  allowed_return_urls:\n    - http://127.0.0.1:4455\n\n  methods:\n    password:\n      enabled: true\n    link:\n      enabled: true\n\n  flows:\n    error:\n      ui_url: http://127.0.0.1:4455/error\n\n    settings:\n      ui_url: http://127.0.0.1:4455/settings\n      privileged_session_max_age: 15m\n\n    recovery:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/recovery\n      use: link\n\n    verification:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/verify\n      use: link\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/\n\n    logout:\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/auth/login\n\n    login:\n      ui_url: http://127.0.0.1:4455/auth/login\n      lifespan: 10m\n\n    registration:\n      lifespan: 10m\n      ui_url: http://127.0.0.1:4455/auth/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n  cipher:\n    - 32-LONG-SECRET-NOT-SECURE-AT-ALL\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nhashers:\n  argon2:\n    parallelism: 1\n    memory: 128MB\n    iterations: 2\n    salt_length: 16\n    key_length: 16\n\nidentity:\n  default_schema_id: default\n  schemas:\n    - id: default\n      url: file://../../pkg/stub/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\n"
  },
  {
    "path": "examples/go/selfservice/error/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\tory \"github.com/ory/client-go\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc getError() *ory.FlowError {\n\te, res, err := client.FrontendAPI.GetFlowError(context.Background()).Id(\"stub:500\").Execute()\n\tpkg.SDKExitOnError(err, res)\n\treturn e\n}\n\nfunc main() {\n\tpkg.PrintJSONPretty(\n\t\tgetError(),\n\t)\n}\n"
  },
  {
    "path": "examples/go/selfservice/error/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestError(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\n\te := getError()\n\trequire.NotEmpty(t, e.Id)\n}\n"
  },
  {
    "path": "examples/go/selfservice/login/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\tory \"github.com/ory/client-go\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc performLogin() *ory.SuccessfulNativeLogin {\n\tctx := context.Background()\n\n\t// Create a temporary user\n\temail, password := pkg.RandomCredentials()\n\t_, _ = pkg.CreateIdentityWithSession(client, email, password)\n\n\t// Initialize the flow\n\tflow, res, err := client.FrontendAPI.CreateNativeLoginFlow(ctx).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\t// If you want, print the flow here:\n\t//\n\t//\tpkg.PrintJSONPretty(flow)\n\n\t// Submit the form\n\tresult, res, err := client.FrontendAPI.UpdateLoginFlow(ctx).Flow(flow.Id).UpdateLoginFlowBody(\n\t\tory.UpdateLoginFlowWithPasswordMethodAsUpdateLoginFlowBody(&ory.UpdateLoginFlowWithPasswordMethod{\n\t\t\tMethod:             \"password\",\n\t\t\tPassword:           password,\n\t\t\tPasswordIdentifier: &email,\n\t\t}),\n\t).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\treturn result\n}\n\nfunc main() {\n\tpkg.PrintJSONPretty(\n\t\tperformLogin(),\n\t)\n}\n"
  },
  {
    "path": "examples/go/selfservice/login/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\n\tresult := performLogin()\n\trequire.NotEmpty(t, result.Session)\n}\n"
  },
  {
    "path": "examples/go/selfservice/logout/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\tory \"github.com/ory/client-go\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc performLogout() {\n\t// Create a temporary user\n\temail, password := pkg.RandomCredentials()\n\t_, sessionToken := pkg.CreateIdentityWithSession(client, email, password)\n\n\t// Log out using session token\n\tres, err := client.FrontendAPI.PerformNativeLogout(context.Background()).\n\t\tPerformNativeLogoutBody(ory.PerformNativeLogoutBody{SessionToken: sessionToken}).Execute()\n\tpkg.SDKExitOnError(err, res)\n}\n\nfunc main() {\n\tperformLogout()\n\tfmt.Println(\"Logout successful!\")\n}\n"
  },
  {
    "path": "examples/go/selfservice/logout/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\n\tperformLogout()\n}\n"
  },
  {
    "path": "examples/go/selfservice/recovery/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\tory \"github.com/ory/client-go\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc performRecovery(email string) *ory.RecoveryFlow {\n\tctx := context.Background()\n\n\t// Initialize the flow\n\tflow, res, err := client.FrontendAPI.CreateNativeRecoveryFlow(ctx).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\t// If you want, print the flow here:\n\t//\n\t//\tpkg.PrintJSONPretty(flow)\n\n\t// Submit the form\n\tafterSubmit, res, err := client.FrontendAPI.UpdateRecoveryFlow(ctx).Flow(flow.Id).\n\t\tUpdateRecoveryFlowBody(ory.UpdateRecoveryFlowWithLinkMethodAsUpdateRecoveryFlowBody(&ory.UpdateRecoveryFlowWithLinkMethod{\n\t\t\tEmail:  email,\n\t\t\tMethod: \"link\",\n\t\t})).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\treturn afterSubmit\n}\n\nfunc main() {\n\tpkg.PrintJSONPretty(\n\t\tperformRecovery(\"someone@foobar.com\"),\n\t)\n}\n"
  },
  {
    "path": "examples/go/selfservice/recovery/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\tory \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\n\tflow := performRecovery(\"dev+\" + uuid.Must(uuid.NewV4()).String() + \"@ory.sh\")\n\trequire.NotEmpty(t, flow.Id)\n\tassert.EqualValues(t, ory.RECOVERYFLOWSTATE_SENT_EMAIL, flow.State)\n}\n"
  },
  {
    "path": "examples/go/selfservice/registration/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\tory \"github.com/ory/client-go\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc initRegistration() *ory.SuccessfulNativeRegistration {\n\tctx := context.Background()\n\n\t// Initialize the flow\n\tflow, res, err := client.FrontendAPI.CreateNativeRegistrationFlow(ctx).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\t// If you want, print the flow here:\n\t//\n\t//\tpkg.PrintJSONPretty(flow)\n\n\temail, password := pkg.RandomCredentials()\n\n\t// Submit the registration flow\n\tresult, res, err := client.FrontendAPI.UpdateRegistrationFlow(ctx).Flow(flow.Id).UpdateRegistrationFlowBody(\n\t\tory.UpdateRegistrationFlowWithPasswordMethodAsUpdateRegistrationFlowBody(&ory.UpdateRegistrationFlowWithPasswordMethod{\n\t\t\tMethod:   \"password\",\n\t\t\tPassword: password,\n\t\t\tTraits:   map[string]interface{}{\"email\": email},\n\t\t}),\n\t).Execute()\n\tpkg.SDKExitOnError(err, res)\n\treturn result\n}\n\nfunc main() {\n\tpkg.PrintJSONPretty(\n\t\tinitRegistration(),\n\t)\n}\n"
  },
  {
    "path": "examples/go/selfservice/registration/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\n\tresult := initRegistration()\n\trequire.NotEmpty(t, result.Identity.Id)\n}\n"
  },
  {
    "path": "examples/go/selfservice/settings/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\tory \"github.com/ory/client-go\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nvar ctx = context.Background()\n\nfunc initFlow(email, password string) (string, *ory.SettingsFlow) {\n\t// Create a temporary user\n\t_, sessionToken := pkg.CreateIdentityWithSession(client, email, password)\n\n\tflow, res, err := client.FrontendAPI.CreateNativeSettingsFlow(context.Background()).XSessionToken(sessionToken).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\t// If you want, print the flow here:\n\t//\n\t//\tpkg.PrintJSONPretty(flow)\n\n\treturn sessionToken, flow\n}\n\nfunc changePassword(email, password string) *ory.SettingsFlow {\n\tsessionToken, flow := initFlow(email, password)\n\n\t// Submit the form\n\tresult, res, err := client.FrontendAPI.UpdateSettingsFlow(ctx).Flow(flow.Id).XSessionToken(sessionToken).UpdateSettingsFlowBody(\n\t\tory.UpdateSettingsFlowWithPasswordMethodAsUpdateSettingsFlowBody(&ory.UpdateSettingsFlowWithPasswordMethod{\n\t\t\tMethod:   \"password\",\n\t\t\tPassword: \"not-\" + password,\n\t\t}),\n\t).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\treturn result\n}\n\nfunc changeTraits(email, password string) *ory.SettingsFlow {\n\tsessionToken, flow := initFlow(email, password)\n\n\t// Submit the form\n\tresult, res, err := client.FrontendAPI.UpdateSettingsFlow(ctx).Flow(flow.Id).XSessionToken(sessionToken).UpdateSettingsFlowBody(\n\t\tory.UpdateSettingsFlowWithProfileMethodAsUpdateSettingsFlowBody(&ory.UpdateSettingsFlowWithProfileMethod{\n\t\t\tMethod: \"profile\",\n\t\t\tTraits: map[string]interface{}{\n\t\t\t\t\"email\": \"not-\" + email,\n\t\t\t},\n\t\t}),\n\t).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\treturn result\n}\n\nfunc main() {\n\tpkg.PrintJSONPretty(\n\t\tchangePassword(pkg.RandomCredentials()),\n\t)\n\tpkg.PrintJSONPretty(\n\t\tchangeTraits(pkg.RandomCredentials()),\n\t)\n}\n"
  },
  {
    "path": "examples/go/selfservice/settings/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\tory \"github.com/ory/kratos/pkg/httpclient\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSettings(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\n\temail, password := pkg.RandomCredentials()\n\tresult := changePassword(email, password)\n\trequire.NotEmpty(t, result.Id)\n\tassert.EqualValues(t, ory.SETTINGSFLOWSTATE_SUCCESS, result.State)\n\n\temail, password = pkg.RandomCredentials()\n\tresult = changeTraits(email, password)\n\trequire.NotEmpty(t, result.Id)\n\tassert.EqualValues(t, ory.SETTINGSFLOWSTATE_SUCCESS, result.State)\n\tassert.Equal(t, \"not-\"+email, result.Identity.Traits.(map[string]interface{})[\"email\"].(string))\n}\n"
  },
  {
    "path": "examples/go/selfservice/verification/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\tory \"github.com/ory/client-go\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc performVerification(email string) *ory.VerificationFlow {\n\tctx := context.Background()\n\n\t// Initialize the flow\n\tflow, res, err := client.FrontendAPI.CreateNativeVerificationFlow(ctx).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\t// If you want, print the flow here:\n\t//\n\t//\tpkg.PrintJSONPretty(flow)\n\n\t// Submit the form\n\tafterSubmit, res, err := client.FrontendAPI.UpdateVerificationFlow(ctx).Flow(flow.Id).\n\t\tUpdateVerificationFlowBody(ory.UpdateVerificationFlowWithLinkMethodAsUpdateVerificationFlowBody(&ory.UpdateVerificationFlowWithLinkMethod{\n\t\t\tEmail:  email,\n\t\t\tMethod: \"link\",\n\t\t})).Execute()\n\tpkg.SDKExitOnError(err, res)\n\n\treturn afterSubmit\n}\n\nfunc main() {\n\tpkg.PrintJSONPretty(\n\t\tperformVerification(\"someone@foobar.com\"),\n\t)\n}\n"
  },
  {
    "path": "examples/go/selfservice/verification/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\tory \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\n\tflow := performVerification(\"dev+\" + uuid.Must(uuid.NewV4()).String() + \"@ory.sh\")\n\trequire.NotEmpty(t, flow.Id)\n\tassert.EqualValues(t, ory.VERIFICATIONFLOWSTATE_SENT_EMAIL, flow.State)\n}\n"
  },
  {
    "path": "examples/go/session/tosession/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\tory \"github.com/ory/client-go\"\n)\n\n// If you use Open Source this would be:\n//\n// var client = pkg.NewSDKForSelfHosted(\"http://127.0.0.1:4433\")\nvar client = pkg.NewSDK(\"playground\")\n\nfunc toSession() *ory.Session {\n\t// Create a temporary user\n\temail, password := pkg.RandomCredentials()\n\t_, sessionToken := pkg.CreateIdentityWithSession(client, email, password)\n\n\tsession, res, err := client.FrontendAPI.ToSessionExecute(ory.FrontendAPIToSessionRequest{}.\n\t\tXSessionToken(sessionToken))\n\tpkg.SDKExitOnError(err, res)\n\treturn session\n}\n\nfunc main() {\n\tsession := toSession()\n\tpkg.PrintJSONPretty(session)\n}\n"
  },
  {
    "path": "examples/go/session/tosession/main_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/examples/go/pkg\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFunc(t *testing.T) {\n\tpublicURL, _ := testhelpers.StartE2EServer(t, \"../../pkg/stub/kratos.yaml\", nil)\n\tclient = pkg.NewSDKForSelfHosted(publicURL)\n\n\tsession := toSession()\n\trequire.NotEmpty(t, session.Id)\n\tassert.Contains(t, session.Identity.Traits.(map[string]interface{})[\"email\"].(string), \"dev+\")\n}\n"
  },
  {
    "path": "gen/oidc/v1/state.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.10\n// \tprotoc        (unknown)\n// source: oidc/v1/state.proto\n\npackage oidcv1\n\nimport (\n\treflect \"reflect\"\n\tsync \"sync\"\n\tunsafe \"unsafe\"\n\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype FlowKind int32\n\nconst (\n\tFlowKind_FLOW_KIND_UNSPECIFIED  FlowKind = 0\n\tFlowKind_FLOW_KIND_LOGIN        FlowKind = 1\n\tFlowKind_FLOW_KIND_REGISTRATION FlowKind = 2\n\tFlowKind_FLOW_KIND_SETTINGS     FlowKind = 3\n)\n\n// Enum value maps for FlowKind.\nvar (\n\tFlowKind_name = map[int32]string{\n\t\t0: \"FLOW_KIND_UNSPECIFIED\",\n\t\t1: \"FLOW_KIND_LOGIN\",\n\t\t2: \"FLOW_KIND_REGISTRATION\",\n\t\t3: \"FLOW_KIND_SETTINGS\",\n\t}\n\tFlowKind_value = map[string]int32{\n\t\t\"FLOW_KIND_UNSPECIFIED\":  0,\n\t\t\"FLOW_KIND_LOGIN\":        1,\n\t\t\"FLOW_KIND_REGISTRATION\": 2,\n\t\t\"FLOW_KIND_SETTINGS\":     3,\n\t}\n)\n\nfunc (x FlowKind) Enum() *FlowKind {\n\tp := new(FlowKind)\n\t*p = x\n\treturn p\n}\n\nfunc (x FlowKind) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (FlowKind) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_oidc_v1_state_proto_enumTypes[0].Descriptor()\n}\n\nfunc (FlowKind) Type() protoreflect.EnumType {\n\treturn &file_oidc_v1_state_proto_enumTypes[0]\n}\n\nfunc (x FlowKind) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use FlowKind.Descriptor instead.\nfunc (FlowKind) EnumDescriptor() ([]byte, []int) {\n\treturn file_oidc_v1_state_proto_rawDescGZIP(), []int{0}\n}\n\ntype State struct {\n\tstate                          protoimpl.MessageState `protogen:\"open.v1\"`\n\tFlowId                         []byte                 `protobuf:\"bytes,1,opt,name=flow_id,json=flowId,proto3\" json:\"flow_id,omitempty\"`\n\tSessionTokenExchangeCodeSha512 []byte                 `protobuf:\"bytes,2,opt,name=session_token_exchange_code_sha512,json=sessionTokenExchangeCodeSha512,proto3\" json:\"session_token_exchange_code_sha512,omitempty\"`\n\tProviderId                     string                 `protobuf:\"bytes,3,opt,name=provider_id,json=providerId,proto3\" json:\"provider_id,omitempty\"`\n\tPkceVerifier                   string                 `protobuf:\"bytes,4,opt,name=pkce_verifier,json=pkceVerifier,proto3\" json:\"pkce_verifier,omitempty\"`\n\tFlowKind                       FlowKind               `protobuf:\"varint,5,opt,name=flow_kind,json=flowKind,proto3,enum=oidc.v1.FlowKind\" json:\"flow_kind,omitempty\"`\n\tunknownFields                  protoimpl.UnknownFields\n\tsizeCache                      protoimpl.SizeCache\n}\n\nfunc (x *State) Reset() {\n\t*x = State{}\n\tmi := &file_oidc_v1_state_proto_msgTypes[0]\n\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\tms.StoreMessageInfo(mi)\n}\n\nfunc (x *State) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*State) ProtoMessage() {}\n\nfunc (x *State) ProtoReflect() protoreflect.Message {\n\tmi := &file_oidc_v1_state_proto_msgTypes[0]\n\tif x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use State.ProtoReflect.Descriptor instead.\nfunc (*State) Descriptor() ([]byte, []int) {\n\treturn file_oidc_v1_state_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *State) GetFlowId() []byte {\n\tif x != nil {\n\t\treturn x.FlowId\n\t}\n\treturn nil\n}\n\nfunc (x *State) GetSessionTokenExchangeCodeSha512() []byte {\n\tif x != nil {\n\t\treturn x.SessionTokenExchangeCodeSha512\n\t}\n\treturn nil\n}\n\nfunc (x *State) GetProviderId() string {\n\tif x != nil {\n\t\treturn x.ProviderId\n\t}\n\treturn \"\"\n}\n\nfunc (x *State) GetPkceVerifier() string {\n\tif x != nil {\n\t\treturn x.PkceVerifier\n\t}\n\treturn \"\"\n}\n\nfunc (x *State) GetFlowKind() FlowKind {\n\tif x != nil {\n\t\treturn x.FlowKind\n\t}\n\treturn FlowKind_FLOW_KIND_UNSPECIFIED\n}\n\nvar File_oidc_v1_state_proto protoreflect.FileDescriptor\n\nconst file_oidc_v1_state_proto_rawDesc = \"\" +\n\t\"\\n\" +\n\t\"\\x13oidc/v1/state.proto\\x12\\aoidc.v1\\\"\\xe2\\x01\\n\" +\n\t\"\\x05State\\x12\\x17\\n\" +\n\t\"\\aflow_id\\x18\\x01 \\x01(\\fR\\x06flowId\\x12J\\n\" +\n\t\"\\\"session_token_exchange_code_sha512\\x18\\x02 \\x01(\\fR\\x1esessionTokenExchangeCodeSha512\\x12\\x1f\\n\" +\n\t\"\\vprovider_id\\x18\\x03 \\x01(\\tR\\n\" +\n\t\"providerId\\x12#\\n\" +\n\t\"\\rpkce_verifier\\x18\\x04 \\x01(\\tR\\fpkceVerifier\\x12.\\n\" +\n\t\"\\tflow_kind\\x18\\x05 \\x01(\\x0e2\\x11.oidc.v1.FlowKindR\\bflowKind*n\\n\" +\n\t\"\\bFlowKind\\x12\\x19\\n\" +\n\t\"\\x15FLOW_KIND_UNSPECIFIED\\x10\\x00\\x12\\x13\\n\" +\n\t\"\\x0fFLOW_KIND_LOGIN\\x10\\x01\\x12\\x1a\\n\" +\n\t\"\\x16FLOW_KIND_REGISTRATION\\x10\\x02\\x12\\x16\\n\" +\n\t\"\\x12FLOW_KIND_SETTINGS\\x10\\x03B|\\n\" +\n\t\"\\vcom.oidc.v1B\\n\" +\n\t\"StateProtoP\\x01Z$github.com/ory/kratos/oidc/v1;oidcv1\\xa2\\x02\\x03OXX\\xaa\\x02\\aOidc.V1\\xca\\x02\\aOidc\\\\V1\\xe2\\x02\\x13Oidc\\\\V1\\\\GPBMetadata\\xea\\x02\\bOidc::V1b\\x06proto3\"\n\nvar (\n\tfile_oidc_v1_state_proto_rawDescOnce sync.Once\n\tfile_oidc_v1_state_proto_rawDescData []byte\n)\n\nfunc file_oidc_v1_state_proto_rawDescGZIP() []byte {\n\tfile_oidc_v1_state_proto_rawDescOnce.Do(func() {\n\t\tfile_oidc_v1_state_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_oidc_v1_state_proto_rawDesc), len(file_oidc_v1_state_proto_rawDesc)))\n\t})\n\treturn file_oidc_v1_state_proto_rawDescData\n}\n\nvar file_oidc_v1_state_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_oidc_v1_state_proto_msgTypes = make([]protoimpl.MessageInfo, 1)\nvar file_oidc_v1_state_proto_goTypes = []any{\n\t(FlowKind)(0), // 0: oidc.v1.FlowKind\n\t(*State)(nil), // 1: oidc.v1.State\n}\nvar file_oidc_v1_state_proto_depIdxs = []int32{\n\t0, // 0: oidc.v1.State.flow_kind:type_name -> oidc.v1.FlowKind\n\t1, // [1:1] is the sub-list for method output_type\n\t1, // [1:1] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_oidc_v1_state_proto_init() }\nfunc file_oidc_v1_state_proto_init() {\n\tif File_oidc_v1_state_proto != nil {\n\t\treturn\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: unsafe.Slice(unsafe.StringData(file_oidc_v1_state_proto_rawDesc), len(file_oidc_v1_state_proto_rawDesc)),\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   1,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_oidc_v1_state_proto_goTypes,\n\t\tDependencyIndexes: file_oidc_v1_state_proto_depIdxs,\n\t\tEnumInfos:         file_oidc_v1_state_proto_enumTypes,\n\t\tMessageInfos:      file_oidc_v1_state_proto_msgTypes,\n\t}.Build()\n\tFile_oidc_v1_state_proto = out.File\n\tfile_oidc_v1_state_proto_goTypes = nil\n\tfile_oidc_v1_state_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/ory/kratos\n\ngo 1.26\n\nreplace (\n\tgithub.com/coreos/go-oidc/v3 => github.com/ory/go-oidc/v3 v3.0.0-20250124100243-69986dfaf891\n\n\tgithub.com/go-swagger/go-swagger => github.com/aeneasr/go-swagger v0.19.1-0.20241013070044-bccef3a12e26 // See https://github.com/go-swagger/go-swagger/issues/3131\n\t// github.com/go-swagger/go-swagger => ../../go-swagger/go-swagger\n\n\tgithub.com/gorilla/sessions => github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2\n\tgithub.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.14.28\n\n\t// Use the internal httpclient which can be generated in this codebase but mark it as the\n\t// official SDK, allowing for the Ory CLI to consume Ory Kratos' CLI commands.\n\tgithub.com/ory/client-go => ./pkg/client-go\n\tgithub.com/ory/x => ./oryx\n)\n\nrequire (\n\tdario.cat/mergo v1.0.2\n\tgithub.com/Masterminds/sprig/v3 v3.3.0\n\tgithub.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0\n\tgithub.com/bradleyjkemp/cupaloy/v2 v2.8.0\n\tgithub.com/bwmarrin/discordgo v0.28.1\n\tgithub.com/coreos/go-oidc/v3 v3.11.0\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc\n\tgithub.com/dghubble/oauth1 v0.7.3\n\tgithub.com/dgraph-io/ristretto/v2 v2.2.0\n\tgithub.com/fatih/color v1.18.0\n\tgithub.com/ghodss/yaml v1.0.0\n\tgithub.com/go-crypt/crypt v0.2.25\n\tgithub.com/go-faker/faker/v4 v4.4.2\n\tgithub.com/go-openapi/strfmt v0.23.0\n\tgithub.com/go-webauthn/webauthn v0.11.2\n\tgithub.com/gobuffalo/httptest v1.5.2\n\tgithub.com/gofrs/uuid v4.4.0+incompatible\n\tgithub.com/golang-jwt/jwt/v4 v4.5.2\n\tgithub.com/golang-jwt/jwt/v5 v5.3.0\n\tgithub.com/golang/gddo v0.0.0-20190904175337-72a348e765d2\n\tgithub.com/golang/mock v1.6.0\n\tgithub.com/google/go-github/v38 v38.1.0\n\tgithub.com/google/go-jsonnet v0.21.0\n\tgithub.com/gorilla/sessions v1.3.0\n\tgithub.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69\n\tgithub.com/hashicorp/go-retryablehttp v0.7.8\n\tgithub.com/hashicorp/golang-lru/v2 v2.0.7\n\tgithub.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf\n\tgithub.com/jarcoal/httpmock v1.3.1\n\tgithub.com/jmoiron/sqlx v1.4.0\n\tgithub.com/knadh/koanf/parsers/json v0.1.0\n\tgithub.com/laher/mergefs v0.1.2-0.20230223191438-d16611b2f4e7 // indirect\n\tgithub.com/lestrrat-go/jwx/v2 v2.1.1\n\tgithub.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826\n\tgithub.com/montanaflynn/stats v0.7.1\n\tgithub.com/ory/analytics-go/v5 v5.0.1\n\tgithub.com/ory/client-go v0.0.0-00010101000000-000000000000\n\tgithub.com/ory/graceful v0.1.4-0.20230301144740-e222150c51d0\n\tgithub.com/ory/herodot v0.10.8\n\tgithub.com/ory/hydra-client-go/v2 v2.2.1\n\tgithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e\n\tgithub.com/ory/mail/v3 v3.0.0\n\tgithub.com/ory/nosurf v1.2.7\n\tgithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e\n\tgithub.com/ory/x v0.0.0-00010101000000-000000000000\n\tgithub.com/peterhellberg/link v1.2.0\n\tgithub.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/pquerna/otp v1.4.0\n\tgithub.com/rakutentech/jwk-go v1.2.0\n\tgithub.com/rs/cors v1.11.1\n\tgithub.com/samber/lo v1.46.0\n\tgithub.com/sirupsen/logrus v1.9.3\n\tgithub.com/slack-go/slack v0.13.1\n\tgithub.com/spf13/cobra v1.10.1\n\tgithub.com/spf13/pflag v1.0.10\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/urfave/negroni v1.0.0\n\tgithub.com/wI2L/jsondiff v0.6.0\n\tgithub.com/zmb3/spotify/v2 v2.4.2\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0\n\tgo.opentelemetry.io/otel v1.40.0\n\tgo.opentelemetry.io/otel/sdk v1.40.0\n\tgo.opentelemetry.io/otel/trace v1.40.0\n\tgolang.org/x/crypto v0.46.0\n\tgolang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect\n\tgolang.org/x/net v0.48.0\n\tgolang.org/x/oauth2 v0.34.0\n\tgolang.org/x/sync v0.19.0\n\tgolang.org/x/text v0.32.0\n\tgoogle.golang.org/grpc v1.79.3\n)\n\nrequire (\n\tgithub.com/cenkalti/backoff v2.2.1+incompatible\n\tgithub.com/gorilla/pat v1.0.2\n\tgithub.com/ian-kent/go-log v0.0.0-20160113211217-5731446c36ab\n\tgithub.com/mailhog/MailHog-Server v1.0.1\n\tgithub.com/mailhog/data v1.0.1\n\tgithub.com/mailhog/storage v1.0.1\n\tgithub.com/moby/moby/api v1.54.0\n\tgithub.com/ory/dockertest/v4 v4.0.0-beta.4\n)\n\nrequire (\n\tfilippo.io/edwards25519 v1.2.0 // indirect\n\tgithub.com/XSAM/otelsql v0.39.0 // indirect\n\tgithub.com/a8m/envsubst v1.4.2 // indirect\n\tgithub.com/alecthomas/participle/v2 v2.1.1 // indirect\n\tgithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect\n\tgithub.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect\n\tgithub.com/bmatcuk/doublestar v1.3.4 // indirect\n\tgithub.com/cenkalti/backoff/v5 v5.0.3 // indirect\n\tgithub.com/containerd/errdefs v1.0.0 // indirect\n\tgithub.com/containerd/errdefs/pkg v0.3.0 // indirect\n\tgithub.com/cortesi/modd v0.8.1 // indirect\n\tgithub.com/cortesi/moddwatch v0.1.0 // indirect\n\tgithub.com/cortesi/termlog v0.0.0-20210222042314-a1eec763abec // indirect\n\tgithub.com/dimchansky/utfbom v1.1.1 // indirect\n\tgithub.com/elliotchance/orderedmap v1.7.1 // indirect\n\tgithub.com/go-openapi/analysis v0.23.0 // indirect\n\tgithub.com/go-openapi/inflect v0.21.0 // indirect\n\tgithub.com/go-openapi/jsonreference v0.21.0 // indirect\n\tgithub.com/go-openapi/loads v0.22.0 // indirect\n\tgithub.com/go-openapi/runtime v0.28.0 // indirect\n\tgithub.com/go-openapi/spec v0.21.0 // indirect\n\tgithub.com/go-openapi/validate v0.24.0 // indirect\n\tgithub.com/go-swagger/go-swagger v0.31.0 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.4.0 // indirect\n\tgithub.com/gobuffalo/plush/v5 v5.0.7 // indirect\n\tgithub.com/gogo/googleapis v1.4.1 // indirect\n\tgithub.com/gorilla/context v1.1.2 // indirect\n\tgithub.com/gorilla/handlers v1.5.2 // indirect\n\tgithub.com/gorilla/mux v1.8.1 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0 // indirect\n\tgithub.com/ian-kent/envconf v0.0.0-20141026121121-c19809918c02 // indirect\n\tgithub.com/ian-kent/goose v0.0.0-20141221090059-c3541ea826ad // indirect\n\tgithub.com/ian-kent/linkio v0.0.0-20170807205755-97566b872887 // indirect\n\tgithub.com/jackc/pgx/v5 v5.7.5 // indirect\n\tgithub.com/jaegertracing/jaeger-idl v0.5.0 // indirect\n\tgithub.com/jessevdk/go-flags v1.6.1 // indirect\n\tgithub.com/jinzhu/copier v0.4.0 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/mailhog/MailHog v1.0.1 // indirect\n\tgithub.com/mailhog/MailHog-UI v1.0.1 // indirect\n\tgithub.com/mailhog/http v1.0.1 // indirect\n\tgithub.com/mailhog/mhsendmail v0.2.0 // indirect\n\tgithub.com/mailhog/smtp v1.0.1 // indirect\n\tgithub.com/mikefarah/yq/v4 v4.45.1 // indirect\n\tgithub.com/moby/moby/client v0.3.0 // indirect\n\tgithub.com/moby/sys/sequential v0.6.0 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/ogier/pflag v0.0.1 // indirect\n\tgithub.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect\n\tgithub.com/rjeczalik/notify v0.9.3 // indirect\n\tgithub.com/sagikazarmark/locafero v0.4.0 // indirect\n\tgithub.com/sagikazarmark/slog-shim v0.1.0 // indirect\n\tgithub.com/smartystreets/goconvey v1.8.1 // indirect\n\tgithub.com/sourcegraph/conc v0.3.0 // indirect\n\tgithub.com/spf13/afero v1.11.0 // indirect\n\tgithub.com/spf13/viper v1.18.2 // indirect\n\tgithub.com/ssoready/hyrumtoken v1.0.0 // indirect\n\tgithub.com/subosito/gotenv v1.6.0 // indirect\n\tgithub.com/t-k/fluent-logger-golang v1.0.0 // indirect\n\tgithub.com/tinylib/msgp v1.2.5 // indirect\n\tgithub.com/toqueteos/webbrowser v1.2.0 // indirect\n\tgithub.com/yuin/gopher-lua v1.1.1 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.2.1 // indirect\n\tgo.uber.org/multierr v1.11.0 // indirect\n\tgo.yaml.in/yaml/v2 v2.4.2 // indirect\n\tgolang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 // indirect\n\tgolang.org/x/term v0.38.0 // indirect\n\tgolang.org/x/tools v0.39.0 // indirect\n\tgopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect\n\tgopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 // indirect\n\tmvdan.cc/sh/v3 v3.6.0 // indirect\n)\n\nrequire (\n\tcode.dny.dev/ssrf v0.2.0 // indirect\n\tgithub.com/Masterminds/goutils v1.1.1 // indirect\n\tgithub.com/Masterminds/semver/v3 v3.4.0 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect\n\tgithub.com/avast/retry-go/v4 v4.6.1 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/boombuler/barcode v1.0.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/cockroachdb/cockroach-go/v2 v2.4.1\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/docker v28.3.3+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/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/evanphx/json-patch/v5 v5.9.11 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/felixge/fgprof v0.9.5 // 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.7.0 // indirect\n\tgithub.com/go-crypt/x v0.2.18 // indirect\n\tgithub.com/go-jose/go-jose/v3 v3.0.4 // indirect\n\tgithub.com/go-jose/go-jose/v4 v4.1.3 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-openapi/errors v0.22.2 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.21.2 // indirect\n\tgithub.com/go-openapi/swag v0.23.1 // indirect\n\tgithub.com/go-sql-driver/mysql v1.9.3 // indirect\n\tgithub.com/go-webauthn/x v0.1.14 // 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.3 // indirect\n\tgithub.com/gobuffalo/github_flavored_markdown v1.1.4 // indirect\n\tgithub.com/gobuffalo/helpers v0.6.10 // indirect\n\tgithub.com/gobuffalo/nulls v0.4.2 // indirect\n\tgithub.com/gobuffalo/plush/v4 v4.1.22 // indirect\n\tgithub.com/gobuffalo/tags/v3 v3.1.4 // indirect\n\tgithub.com/gobuffalo/validate/v3 v3.3.3 // indirect\n\tgithub.com/gobwas/glob v0.2.3 // indirect\n\tgithub.com/goccy/go-json v0.10.5 // indirect\n\tgithub.com/goccy/go-yaml v1.18.0 // indirect\n\tgithub.com/gofrs/flock v0.12.1 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/google/go-querystring v1.0.0 // indirect\n\tgithub.com/google/go-tpm v0.9.1 // indirect\n\tgithub.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/securecookie v1.1.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/huandu/xstrings v1.5.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/jackc/chunkreader/v2 v2.0.1 // indirect\n\tgithub.com/jackc/pgconn v1.14.3 // 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/puddle/v2 v2.2.2 // indirect\n\tgithub.com/joho/godotenv v1.5.1 // indirect\n\tgithub.com/josharian/intern v1.0.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/knadh/koanf/parsers/toml v0.1.0 // indirect\n\tgithub.com/knadh/koanf/parsers/yaml v0.1.0 // indirect\n\tgithub.com/knadh/koanf/providers/posflag v0.1.0 // indirect\n\tgithub.com/knadh/koanf/v2 v2.2.2 // indirect\n\tgithub.com/lestrrat-go/backoff/v2 v2.0.8 // indirect\n\tgithub.com/lestrrat-go/blackmagic v1.0.4 // 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/jwx v1.2.31\n\tgithub.com/lestrrat-go/option v1.0.1 // indirect\n\tgithub.com/lib/pq v1.10.9 // indirect\n\tgithub.com/magiconair/properties v1.8.9 // indirect\n\tgithub.com/mailru/easyjson v0.9.0 // 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.27 // indirect\n\tgithub.com/mitchellh/copystructure v1.2.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.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/nyaruka/phonenumbers v1.6.5\n\tgithub.com/oklog/ulid v1.3.1 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.1.1 // indirect\n\tgithub.com/openzipkin/zipkin-go v0.4.3 // indirect\n\tgithub.com/pelletier/go-toml v1.9.5 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.2.3 // indirect\n\tgithub.com/pkg/profile v1.7.0 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/prometheus/client_golang v1.23.0\n\tgithub.com/prometheus/client_model v0.6.2 // indirect\n\tgithub.com/prometheus/common v0.65.0 // indirect\n\tgithub.com/prometheus/procfs v0.17.0 // indirect\n\tgithub.com/rogpeppe/go-internal v1.14.1 // indirect\n\tgithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect\n\tgithub.com/segmentio/asm v1.2.0 // indirect\n\tgithub.com/segmentio/backo-go v1.1.0 // indirect\n\tgithub.com/sergi/go-diff v1.4.0 // indirect\n\tgithub.com/shopspring/decimal v1.4.0 // 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/cast v1.9.2 // indirect\n\tgithub.com/tidwall/match v1.1.1 // indirect\n\tgithub.com/tidwall/pretty v1.2.1 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgithub.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect\n\tgo.mongodb.org/mongo-driver v1.17.4 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.62.0 // indirect\n\tgo.opentelemetry.io/contrib/propagators/b3 v1.37.0 // indirect\n\tgo.opentelemetry.io/contrib/propagators/jaeger v1.37.0 // indirect\n\tgo.opentelemetry.io/contrib/samplers/jaegerremote v0.31.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect; / indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect; / indirect\n\tgo.opentelemetry.io/otel/exporters/zipkin v1.37.0 // indirect; / indirect\n\tgo.opentelemetry.io/otel/metric v1.40.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v1.7.1 // indirect\n\tgolang.org/x/mod v0.30.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect\n\tgoogle.golang.org/protobuf v1.36.10\n\tgopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tsigs.k8s.io/yaml v1.6.0 // indirect\n)\n\ntool (\n\tgithub.com/cortesi/modd/cmd/modd\n\tgithub.com/go-swagger/go-swagger/cmd/swagger\n\tgithub.com/mailhog/MailHog\n\tgithub.com/mikefarah/yq/v4\n\tgolang.org/x/tools/cmd/goimports\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ncode.dny.dev/ssrf v0.2.0 h1:wCBP990rQQ1CYfRpW+YK1+8xhwUjv189AQ3WMo1jQaI=\ncode.dny.dev/ssrf v0.2.0/go.mod h1:B+91l25OnyaLIeCx0WRJN5qfJ/4/ZTZxRXgm0lj/2w8=\ndario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=\ndario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\nfilippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\nfilippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=\nfilippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=\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/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=\ngithub.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=\ngithub.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=\ngithub.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=\ngithub.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=\ngithub.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=\ngithub.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/XSAM/otelsql v0.39.0 h1:4o374mEIMweaeevL7fd8Q3C710Xi2Jh/c8G4Qy9bvCY=\ngithub.com/XSAM/otelsql v0.39.0/go.mod h1:uMOXLUX+wkuAuP0AR3B45NXX7E9lJS2mERa8gqdU8R0=\ngithub.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=\ngithub.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=\ngithub.com/aeneasr/go-swagger v0.19.1-0.20241013070044-bccef3a12e26 h1:rwCKVbnpzxQ0F/AhO9FkXnrKqRmqej4epjhe1CpNkB0=\ngithub.com/aeneasr/go-swagger v0.19.1-0.20241013070044-bccef3a12e26/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/HoaZJ5edupq7po=\ngithub.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0=\ngithub.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=\ngithub.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8=\ngithub.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c=\ngithub.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=\ngithub.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=\ngithub.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=\ngithub.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=\ngithub.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=\ngithub.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=\ngithub.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=\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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=\ngithub.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=\ngithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=\ngithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=\ngithub.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=\ngithub.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=\ngithub.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=\ngithub.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=\ngithub.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=\ngithub.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=\ngithub.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=\ngithub.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=\ngithub.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=\ngithub.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\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/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=\ngithub.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=\ngithub.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cockroachdb/cockroach-go/v2 v2.4.1 h1:ACVT/zXsuK6waRPVYtDQpsM8pPA7IA/3fkgA02RR/Gw=\ngithub.com/cockroachdb/cockroach-go/v2 v2.4.1/go.mod h1:9U179XbCx4qFWtNhc7BiWLPfuyMVQ7qdAhfrwLz1vH0=\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/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=\ngithub.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=\ngithub.com/cortesi/modd v0.8.1 h1:0s8e10CJ6pxc6NQHYFrmUZOLP0X6v63ry+3na6Gq2Ow=\ngithub.com/cortesi/modd v0.8.1/go.mod h1:GDJFkhHnnW+SD1C+wHBlKe5Yh2IqiOb6Lu5t2/fjnS4=\ngithub.com/cortesi/moddwatch v0.1.0 h1:+TSMuplhKlKEPKsdUXNHd67aCqew+et15dJvRCxMd1M=\ngithub.com/cortesi/moddwatch v0.1.0/go.mod h1:PFkhcmmwsRMQ76IMjKbaIIMcGQt7BSMtFOp+pA0B2eo=\ngithub.com/cortesi/termlog v0.0.0-20210222042314-a1eec763abec h1:v7D8uHsIKsyjfyhhNdY4qivqN558Ejiq+CDXiUljZ+4=\ngithub.com/cortesi/termlog v0.0.0-20210222042314-a1eec763abec/go.mod h1:10Fm2kasJmcKf1FSMQGSWb976sfR29hejNtfS9AydB4=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=\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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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/dghubble/oauth1 v0.7.3 h1:EkEM/zMDMp3zOsX2DC/ZQ2vnEX3ELK0/l9kb+vs4ptE=\ngithub.com/dghubble/oauth1 v0.7.3/go.mod h1:oxTe+az9NSMIucDPDCCtzJGsPhciJV33xocHfcR2sVY=\ngithub.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=\ngithub.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=\ngithub.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=\ngithub.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=\ngithub.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=\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/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=\ngithub.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/elliotchance/orderedmap v1.7.1 h1:8SR2DB391dw0HVI9572ElrY+KU0Q89OCXYwWZx7aAZc=\ngithub.com/elliotchance/orderedmap v1.7.1/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\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.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=\ngithub.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\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/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=\ngithub.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=\ngithub.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\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.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=\ngithub.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-crypt/crypt v0.2.25 h1:uW/3n4/9zYSOOgY0Md9dMxGSqrjaMyLo1/IFrm2L5yw=\ngithub.com/go-crypt/crypt v0.2.25/go.mod h1:ny8BOunn+/kr99iq2LYSKA0MAsxNaxZUmKKL42vV1io=\ngithub.com/go-crypt/x v0.2.18 h1:KdUGj4D/PdzcIkOQhK36QHzH2YD5GWrsVQ7JgO73Q8Y=\ngithub.com/go-crypt/x v0.2.18/go.mod h1:z48dMLdgMGPPjrzni9ETtg2foPez3EytjCL43Ak4QZ8=\ngithub.com/go-faker/faker/v4 v4.4.2 h1:96WeU9QKEqRUVYdjHquY2/5bAqmVM0IfGKHV5mbfqmQ=\ngithub.com/go-faker/faker/v4 v4.4.2/go.mod h1:4K3v4AbKXYNHMQNaREMc9/kRB9j5JJzpFo6KHRvrcIw=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=\ngithub.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=\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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/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-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=\ngithub.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=\ngithub.com/go-openapi/errors v0.22.2 h1:rdxhzcBUazEcGccKqbY1Y7NS8FDcMyIRr0934jrYnZg=\ngithub.com/go-openapi/errors v0.22.2/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0=\ngithub.com/go-openapi/inflect v0.21.0 h1:FoBjBTQEcbg2cJUWX6uwL9OyIW8eqc9k4KhN4lfbeYk=\ngithub.com/go-openapi/inflect v0.21.0/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw=\ngithub.com/go-openapi/jsonpointer v0.21.2 h1:AqQaNADVwq/VnkCmQg6ogE+M3FOsKTytwges0JdwVuA=\ngithub.com/go-openapi/jsonpointer v0.21.2/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=\ngithub.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=\ngithub.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=\ngithub.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=\ngithub.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=\ngithub.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=\ngithub.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=\ngithub.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=\ngithub.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=\ngithub.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=\ngithub.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=\ngithub.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=\ngithub.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=\ngithub.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=\ngithub.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=\ngithub.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=\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-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=\ngithub.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\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.11.2 h1:Fgx0/wlmkClTKlnOsdOQ+K5HcHDsDcYIvtYmfhEOSUc=\ngithub.com/go-webauthn/webauthn v0.11.2/go.mod h1:aOtudaF94pM71g3jRwTYYwQTG1KyTILTcZqN1srkmD0=\ngithub.com/go-webauthn/x v0.1.14 h1:1wrB8jzXAofojJPAaRxnZhRgagvLGnLjhCAwg3kTpT0=\ngithub.com/go-webauthn/x v0.1.14/go.mod h1:UuVvFZ8/NbOnkDz3y1NaxtUN87pmtpC1PQ+/5BBQRdc=\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.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=\ngithub.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=\ngithub.com/gobuffalo/github_flavored_markdown v1.1.3/go.mod h1:IzgO5xS6hqkDmUh91BW/+Qxo/qYnvfzoz3A7uLkg77I=\ngithub.com/gobuffalo/github_flavored_markdown v1.1.4 h1:WacrEGPXUDX+BpU1GM/Y0ADgMzESKNWls9hOTG1MHVs=\ngithub.com/gobuffalo/github_flavored_markdown v1.1.4/go.mod h1:Vl9686qrVVQou4GrHRK/KOG3jCZOKLUqV8MMOAYtlso=\ngithub.com/gobuffalo/helpers v0.6.7/go.mod h1:j0u1iC1VqlCaJEEVkZN8Ia3TEzfj/zoXANqyJExTMTA=\ngithub.com/gobuffalo/helpers v0.6.10 h1:puKDCOrJ0EIq5ScnTRgKyvEZ05xQa+gwRGCpgoh6Ek8=\ngithub.com/gobuffalo/helpers v0.6.10/go.mod h1:r52L6VSnByLJFOmURp1irvzgSakk7RodChi1YbGwk8I=\ngithub.com/gobuffalo/httptest v1.5.2 h1:GpGy520SfY1QEmyPvaqmznTpG4gEQqQ82HtHqyNEreM=\ngithub.com/gobuffalo/httptest v1.5.2/go.mod h1:FA23yjsWLGj92mVV74Qtc8eqluc11VqcWr8/C1vxt4g=\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/plush/v4 v4.1.16/go.mod h1:6t7swVsarJ8qSLw1qyAH/KbrcSTwdun2ASEQkOznakg=\ngithub.com/gobuffalo/plush/v4 v4.1.22 h1:bPQr5PsiTg54UGMsfvnIAvFmUfxzD/ri+wbpu7PlmTM=\ngithub.com/gobuffalo/plush/v4 v4.1.22/go.mod h1:WiKHJx3qBvfaDVlrv8zT7NCd3dEMaVR/fVxW4wqV17M=\ngithub.com/gobuffalo/plush/v5 v5.0.7 h1:nI8sIt5tZAN2tCZHeaXkH7HAvxvvk3sJHG2TtrKeSHM=\ngithub.com/gobuffalo/plush/v5 v5.0.7/go.mod h1:C08u/VEqzzPBXFF/yqs40P/5Cvc/zlZsMzhCxXyWJmU=\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/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=\ngithub.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=\ngithub.com/goccy/go-json v0.10.5/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/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=\ngithub.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=\ngithub.com/gofrs/uuid v4.2.0+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/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=\ngithub.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=\ngithub.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\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/golang/gddo v0.0.0-20190904175337-72a348e765d2 h1:xisWqjiKEff2B0KfFYGpCqc3M3zdTz+OHQHRc09FeYk=\ngithub.com/golang/gddo v0.0.0-20190904175337-72a348e765d2/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=\ngithub.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\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-github/v38 v38.1.0 h1:C6h1FkaITcBFK7gAmq4eFzt6gbhEhk7L5z6R3Uva+po=\ngithub.com/google/go-github/v38 v38.1.0/go.mod h1:cStvrz/7nFr0FoENgG6GLbp53WaelXucT+BBz/3VKx4=\ngithub.com/google/go-jsonnet v0.21.0 h1:43Bk3K4zMRP/aAZm9Po2uSEjY6ALCkYUVIcz9HLGMvA=\ngithub.com/google/go-jsonnet v0.21.0/go.mod h1:tCGAu8cpUpEZcdGMmdOu37nh8bGgqubhI5v2iSk3KJQ=\ngithub.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/go-tpm v0.9.1 h1:0pGc4X//bAlmZzMKf8iz6IsDo1nYTbYJ6FZN/rg4zdM=\ngithub.com/google/go-tpm v0.9.1/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=\ngithub.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=\ngithub.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=\ngithub.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=\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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=\ngithub.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=\ngithub.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=\ngithub.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=\ngithub.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=\ngithub.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=\ngithub.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=\ngithub.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=\ngithub.com/gorilla/pat v1.0.2 h1:TDh/RulbnPxMQACcwbgMF5Bf00jaGoeYBNu+XUFuwtE=\ngithub.com/gorilla/pat v1.0.2/go.mod h1:ioQ7dFQ2KXmOmWLJs6vZAfRikcm2D2JyuLrL9b5wVCg=\ngithub.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=\ngithub.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=\ngithub.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69 h1:7xsUJsB2NrdcttQPa7JLEaGzvdbk7KvfrjgHZXOQRo0=\ngithub.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69/go.mod h1:YLEMZOtU+AZ7dhN9T/IpGhXVGly2bvkJQ+zxj3WeVQo=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=\ngithub.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=\ngithub.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=\ngithub.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=\ngithub.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=\ngithub.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=\ngithub.com/ian-kent/envconf v0.0.0-20141026121121-c19809918c02 h1:dU8zq210pt1b71X8xh9GOxC7uBHNtQ9BYC+Lb6SA/mA=\ngithub.com/ian-kent/envconf v0.0.0-20141026121121-c19809918c02/go.mod h1:1m5fo3aKG2moYtGHC4I2nFkXmG97+vCeaEIWC+mXTSI=\ngithub.com/ian-kent/go-log v0.0.0-20160113211217-5731446c36ab h1:OgrFrYWlVzY7Tc8rq7Y4ErlKo28igc70gbfJGTVWTJk=\ngithub.com/ian-kent/go-log v0.0.0-20160113211217-5731446c36ab/go.mod h1:6HitiSDIbT2r0dab4CoKoMAtR7tb0ORQ3OmjkjCZ+zk=\ngithub.com/ian-kent/goose v0.0.0-20141221090059-c3541ea826ad h1:5UZIY1lPvsBrRQRgyt00lJ1J6HH6CwWAVQB6azyAA1c=\ngithub.com/ian-kent/goose v0.0.0-20141221090059-c3541ea826ad/go.mod h1:VHyJj0/IJFmpYvVqWFIN2HgjCatXujj7XaLLyOMC23M=\ngithub.com/ian-kent/linkio v0.0.0-20170807205755-97566b872887 h1:LPaZmcRJS13h+igi07S26uKy0qxCa76u1+pArD+JGrY=\ngithub.com/ian-kent/linkio v0.0.0-20170807205755-97566b872887/go.mod h1:aE63iKqF9rMrshaEiYZroUYFZLaYoTuA7pBMsg3lJoY=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=\ngithub.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s=\ngithub.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4=\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 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-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/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-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=\ngithub.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=\ngithub.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=\ngithub.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/jaegertracing/jaeger-idl v0.5.0 h1:zFXR5NL3Utu7MhPg8ZorxtCBjHrL3ReM1VoB65FOFGE=\ngithub.com/jaegertracing/jaeger-idl v0.5.0/go.mod h1:ON90zFo9eoyXrt9F/KN8YeF3zxcnujaisMweFY/rg5k=\ngithub.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=\ngithub.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=\ngithub.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=\ngithub.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=\ngithub.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=\ngithub.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=\ngithub.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=\ngithub.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=\ngithub.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=\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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\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 v0.1.0 h1:dzSZl5pf5bBcW0Acnu20Djleto19T0CfHcvZ14NJ6fU=\ngithub.com/knadh/koanf/parsers/json v0.1.0/go.mod h1:ll2/MlXcZ2BfXD6YJcjVFzhG9P0TdJ207aIBKQhV2hY=\ngithub.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI=\ngithub.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18=\ngithub.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=\ngithub.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=\ngithub.com/knadh/koanf/providers/posflag v0.1.0 h1:mKJlLrKPcAP7Ootf4pBZWJ6J+4wHYujwipe7Ie3qW6U=\ngithub.com/knadh/koanf/providers/posflag v0.1.0/go.mod h1:SYg03v/t8ISBNrMBRMlojH8OsKowbkXV7giIbBVgbz0=\ngithub.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME=\ngithub.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c=\ngithub.com/knadh/koanf/v2 v2.2.2 h1:ghbduIkpFui3L587wavneC9e3WIliCgiCgdxYO/wd7A=\ngithub.com/knadh/koanf/v2 v2.2.2/go.mod h1:abWQc0cBXLSF/PSOMCB/SK+T13NXDsPvOksbpi5e/9Q=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\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/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/laher/mergefs v0.1.2-0.20230223191438-d16611b2f4e7 h1:PDeBswTUsSIT4QSrzLvlqKlGrANYa7TrXUwdBN9myU8=\ngithub.com/laher/mergefs v0.1.2-0.20230223191438-d16611b2f4e7/go.mod h1:FSY1hYy94on4Tz60waRMGdO1awwS23BacqJlqf9lJ9Q=\ngithub.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=\ngithub.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=\ngithub.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=\ngithub.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=\ngithub.com/lestrrat-go/blackmagic v1.0.4/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 v1.2.31 h1:/OM9oNl/fzyldpv5HKZ9m7bTywa7COUfg8gujd9nJ54=\ngithub.com/lestrrat-go/jwx v1.2.31/go.mod h1:eQJKoRwWcLg4PfD5CFA5gIZGxhPgoPYq9pZISdxLf0c=\ngithub.com/lestrrat-go/jwx/v2 v2.1.1 h1:Y2ltVl8J6izLYFs54BVcpXLv5msSW4o8eXwnzZLI32E=\ngithub.com/lestrrat-go/jwx/v2 v2.1.1/go.mod h1:4LvZg7oxu6Q5VJwn7Mk/UwooNRnTHUpXBj2C4j3HNx0=\ngithub.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=\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.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=\ngithub.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=\ngithub.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=\ngithub.com/mailhog/MailHog v1.0.1 h1:NDExFIj+JGzXT3kmG31r7Okrn78Sk/5p9lP/TV8OE4E=\ngithub.com/mailhog/MailHog v1.0.1/go.mod h1:QlN3aQB5Kx2ZoQy439EWkjWHyJrgqNDsjfknyQOBCOI=\ngithub.com/mailhog/MailHog-Server v1.0.1 h1:mK9inUHV2p6pO55cHZTCdZ8D4aXzd+M9wvqtU0XmWcM=\ngithub.com/mailhog/MailHog-Server v1.0.1/go.mod h1:ScCrImbapPxdrQ85qoxkMygsSiY74ohIj1knLzuN8J0=\ngithub.com/mailhog/MailHog-UI v1.0.1 h1:B3mVLiVLd4amNVQtiklr+srI3eMKiMzYSXXqDGa7JzA=\ngithub.com/mailhog/MailHog-UI v1.0.1/go.mod h1:zLEw2DaBMXpL6nmpdB8S5U1Y3MMSATlTcjUYSTAB7HQ=\ngithub.com/mailhog/data v1.0.1 h1:7I+opBvVdi4EMJaihXavM98jp/ovt4o6mz47446RAW8=\ngithub.com/mailhog/data v1.0.1/go.mod h1:tjR/iXRhbSUKHzAAMd99RygVaDB5rIDC/bmWc363MzU=\ngithub.com/mailhog/http v1.0.1 h1:i3sxAt7/WcdRXdKJZgiDRkWIAYScnrqqHQTZSxXkM0I=\ngithub.com/mailhog/http v1.0.1/go.mod h1:91oqUCI9ZoSDY2cTj4pWDJVBHCK1U762V2a4if4KlOw=\ngithub.com/mailhog/mhsendmail v0.2.0 h1:C5HUC4obHfXIkttLfGBUopYbsJmh+bnExGWHBpWQ8IA=\ngithub.com/mailhog/mhsendmail v0.2.0/go.mod h1:B0778+OoPEc5aEFqatEnSO4ZWl9FCTxvaY+c7OOQadM=\ngithub.com/mailhog/smtp v1.0.1 h1:igL3N/L+pWuGCqUaje21HX3VIVnqHoVlqWO0t+wJEYE=\ngithub.com/mailhog/smtp v1.0.1/go.mod h1:GMrAdv1hXro38xj5dsWPAk5ZiXJHFx9t7W9Yqsk0XUM=\ngithub.com/mailhog/storage v1.0.1 h1:uut2nlG5hIxbsl6f8DGznPAHwQLf3/7Na2t4gmrIais=\ngithub.com/mailhog/storage v1.0.1/go.mod h1:4EAUf5xaEVd7c/OhvSxOOwQ66jT6q2er+BDBQ0EVrew=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=\ngithub.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=\ngithub.com/mattn/go-colorable v0.1.8/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.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.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\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.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=\ngithub.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=\ngithub.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=\ngithub.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=\ngithub.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50=\ngithub.com/microcosm-cc/bluemonday v1.0.22/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mikefarah/yq/v4 v4.45.1 h1:EW+HjKEVa55pUYFJseEHEHdQ0+ulunY+q42zF3M7ZaQ=\ngithub.com/mikefarah/yq/v4 v4.45.1/go.mod h1:djgN2vD749hpjVNGYTShr5Kmv5LYljhCG3lUTuEe3LM=\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.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0=\ngithub.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=\ngithub.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs=\ngithub.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ=\ngithub.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=\ngithub.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=\ngithub.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=\ngithub.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=\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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=\ngithub.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=\ngithub.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=\ngithub.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=\ngithub.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\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/nyaruka/phonenumbers v1.6.5 h1:aBCaUhfpRA7hU6fsXk+p7KF1aNx4nQlq9hGeo2qdFg8=\ngithub.com/nyaruka/phonenumbers v1.6.5/go.mod h1:7gjs+Lchqm49adhAKB5cdcng5ZXgt6x7Jgvi0ZorUtU=\ngithub.com/ogier/pflag v0.0.1 h1:RW6JSWSu/RkSatfcLtogGfFgpim5p7ARQ10ECk5O750=\ngithub.com/ogier/pflag v0.0.1/go.mod h1:zkFki7tvTa0tafRvTBIZTvzYyAu6kQhPZFnshFFPE+g=\ngithub.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=\ngithub.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=\ngithub.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=\ngithub.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=\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/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=\ngithub.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=\ngithub.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=\ngithub.com/ory/analytics-go/v5 v5.0.1 h1:LX8T5B9FN8KZXOtxgN+R3I4THRRVB6+28IKgKBpXmAM=\ngithub.com/ory/analytics-go/v5 v5.0.1/go.mod h1:lWCiCjAaJkKfgR/BN5DCLMol8BjKS1x+4jxBxff/FF0=\ngithub.com/ory/dockertest/v4 v4.0.0-beta.4 h1:QcrNrobOP+5IjSDmS4//EuBtwiFuznQhi5xTe8oFSoM=\ngithub.com/ory/dockertest/v4 v4.0.0-beta.4/go.mod h1:p9kfE14tzK8+WU4F9YbIZlzhCzQ2pH7H1KIfBKrF3DM=\ngithub.com/ory/go-oidc/v3 v3.0.0-20250124100243-69986dfaf891 h1:HjpfYsY85wpheyMwR9EEk3347I0QsCllRMJShods3jc=\ngithub.com/ory/go-oidc/v3 v3.0.0-20250124100243-69986dfaf891/go.mod h1:Jxfv2TPRvdJuLfmkvokss8dkguhMmer2UvARU6SWy0Y=\ngithub.com/ory/graceful v0.1.4-0.20230301144740-e222150c51d0 h1:VMUeLRfQD14fOMvhpYZIIT4vtAqxYh+f3KnSqCeJ13o=\ngithub.com/ory/graceful v0.1.4-0.20230301144740-e222150c51d0/go.mod h1:hg2iCy+LCWOXahBZ+NQa4dk8J2govyQD79rrqrgMyY8=\ngithub.com/ory/herodot v0.10.8 h1:uUPsXd4FKTsNHHU+OS5gYrRNEMraU36st0kBeWiXsno=\ngithub.com/ory/herodot v0.10.8/go.mod h1:j6i246U6iX8TStYNKIVQxb2waweQvtOLi+b/9q+OULg=\ngithub.com/ory/hydra-client-go/v2 v2.2.1 h1:m1821pIX6ybG/3oSAn2wtrbBKNwe9q5A8fLljYuLpBk=\ngithub.com/ory/hydra-client-go/v2 v2.2.1/go.mod h1:K83R+iK40+5uF2uQ34yRUrf9izRvFsza9pG2Se5qMmk=\ngithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e h1:4tUrC7x4YWRVMFp+c64KACNSGchW1zXo4l6Pa9/1hA8=\ngithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e/go.mod h1:XWLxVK4un/iuIcrw+6lCeanbF3NZwO5k6RdLeu/loQk=\ngithub.com/ory/mail v2.3.1+incompatible/go.mod h1:87D9/1gB6ewElQoN0lXJ0ayfqcj3cW3qCTXh+5E9mfU=\ngithub.com/ory/mail/v3 v3.0.0 h1:8LFMRj473vGahFD/ntiotWEd4S80FKYFtiZTDfOQ+sM=\ngithub.com/ory/mail/v3 v3.0.0/go.mod h1:JGAVeZF8YAlxbaFDUHqRZAKBCSeW2w1vuxf28hFbZAw=\ngithub.com/ory/nosurf v1.2.7 h1:YrHrbSensQyU6r6HT/V5+HPdVEgrOTMJiLoJABSBOp4=\ngithub.com/ory/nosurf v1.2.7/go.mod h1:d4L3ZBa7Amv55bqxCBtCs63wSlyaiCkWVl4vKf3OUxA=\ngithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e h1:gsbAteu8HZYnkIF4WVBaxklvF/s5IbcxYcCi6qX93ms=\ngithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e/go.mod h1:PEqjxMcIV87rBhlyDDha76I7/w2W/FHenSq3V3X1A/A=\ngithub.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpixwHiuAwpp0Ock6khSVHkrv6lQQU=\ngithub.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=\ngithub.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=\ngithub.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=\ngithub.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=\ngithub.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=\ngithub.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=\ngithub.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=\ngithub.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=\ngithub.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=\ngithub.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=\ngithub.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=\ngithub.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=\ngithub.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=\ngithub.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\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.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=\ngithub.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=\ngithub.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=\ngithub.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=\ngithub.com/rakutentech/jwk-go v1.2.0 h1:vNJwedPkRR+32V5WGNj0JP4COes93BGERvzQLBjLy4c=\ngithub.com/rakutentech/jwk-go v1.2.0/go.mod h1:pI0bYVntqaJ27RCpaC75MTUacheW0Rk4+8XzWWe1OWM=\ngithub.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY=\ngithub.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=\ngithub.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=\ngithub.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=\ngithub.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=\ngithub.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=\ngithub.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ=\ngithub.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=\ngithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 h1:0b8DF5kR0PhRoRXDiEEdzrgBc8UqVY4JWLkQJCRsLME=\ngithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761/go.mod h1:/THDZYi7F/BsVEcYzYPqdcWFQ+1C2InkawTKfLOAnzg=\ngithub.com/segmentio/analytics-go v3.1.0+incompatible/go.mod h1:C7CYBtQWk4vRk2RyLu0qOcbHJ18E3F1HV2C/8JvKN48=\ngithub.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=\ngithub.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=\ngithub.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc=\ngithub.com/segmentio/backo-go v1.1.0 h1:cJIfHQUdmLsd8t9IXqf5J8SdrOMn9vMa7cIvOavHAhc=\ngithub.com/segmentio/backo-go v1.1.0/go.mod h1:ckenwdf+v/qbyhVdNPWHnqh2YdJBED1O9cidYyM5J18=\ngithub.com/segmentio/conf v1.2.0/go.mod h1:Y3B9O/PqqWqjyxyWWseyj/quPEtMu1zDp/kVbSWWaB0=\ngithub.com/segmentio/go-snakecase v1.1.0/go.mod h1:jk1miR5MS7Na32PZUykG89Arm+1BUSYhuGR6b7+hJto=\ngithub.com/segmentio/objconv v1.0.1/go.mod h1:auayaH5k3137Cl4SoXTgrzQcuQDmvuVtZgS0fb1Ahys=\ngithub.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=\ngithub.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=\ngithub.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=\ngithub.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=\ngithub.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/slack-go/slack v0.13.1 h1:6UkM3U1OnbhPsYeb1IMkQ6HSNOSikWluwOncJt4Tz/o=\ngithub.com/slack-go/slack v0.13.1/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=\ngithub.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=\ngithub.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=\ngithub.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=\ngithub.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=\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/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=\ngithub.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=\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/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=\ngithub.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=\ngithub.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=\ngithub.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=\ngithub.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=\ngithub.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=\ngithub.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=\ngithub.com/ssoready/hyrumtoken v1.0.0 h1:N/JPJDOuYS7qPSnOvZpPxNVXwtlT3kfzAMEcPrH8ywQ=\ngithub.com/ssoready/hyrumtoken v1.0.0/go.mod h1:h8q768r5Uv6iJKOwsNENIWWUP9kvmLykQox5m3SCpqc=\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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=\ngithub.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=\ngithub.com/t-k/fluent-logger-golang v1.0.0 h1:4IQzY+/l66Zkkhk9eB3LwF9vPkgKHJ1rpYdrRiap0EI=\ngithub.com/t-k/fluent-logger-golang v1.0.0/go.mod h1:6vC3Vzp9Kva0l5J9+YDY5/ROePwkAqwLK+KneCjSm4w=\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/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=\ngithub.com/tidwall/pretty v1.2.1/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/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=\ngithub.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=\ngithub.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=\ngithub.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=\ngithub.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=\ngithub.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=\ngithub.com/wI2L/jsondiff v0.6.0 h1:zrsH3FbfVa3JO9llxrcDy/XLkYPLgoMX6Mz3T2PP2AI=\ngithub.com/wI2L/jsondiff v0.6.0/go.mod h1:D6aQ5gKgPF9g17j+E9N7aasmU1O+XvfmWm1y8UMmNpw=\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/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=\ngithub.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=\ngithub.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=\ngithub.com/zmb3/spotify/v2 v2.4.2 h1:j3yNN5lKVEMZQItJF4MHCSZbfNWmXO+KaC+3RFaLlLc=\ngithub.com/zmb3/spotify/v2 v2.4.2/go.mod h1:XOV7BrThayFYB9AAfB+L0Q0wyxBuLCARk4fI/ZXCBW8=\ngo.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=\ngo.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.62.0 h1:wCeciVlAfb5DC8MQl/DlmAv/FVPNpQgFvI/71+hatuc=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.62.0/go.mod h1:WfEApdZDMlLUAev/0QQpr8EJ/z0VWDKYZ5tF5RH5T1U=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=\ngo.opentelemetry.io/contrib/propagators/b3 v1.37.0 h1:0aGKdIuVhy5l4GClAjl72ntkZJhijf2wg1S7b5oLoYA=\ngo.opentelemetry.io/contrib/propagators/b3 v1.37.0/go.mod h1:nhyrxEJEOQdwR15zXrCKI6+cJK60PXAkJ/jRyfhr2mg=\ngo.opentelemetry.io/contrib/propagators/jaeger v1.37.0 h1:pW+qDVo0jB0rLsNeaP85xLuz20cvsECUcN7TE+D8YTM=\ngo.opentelemetry.io/contrib/propagators/jaeger v1.37.0/go.mod h1:x7bd+t034hxLTve1hF9Yn9qQJlO/pP8H5pWIt7+gsFM=\ngo.opentelemetry.io/contrib/samplers/jaegerremote v0.31.0 h1:l8XCsDh7L6Z7PB+vlw1s4ufNab+ayT2RMNdvDE/UyPc=\ngo.opentelemetry.io/contrib/samplers/jaegerremote v0.31.0/go.mod h1:XAOSk4bqj5vtoiY08bexeiafzxdXeLlxKFnwscvn8Fc=\ngo.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=\ngo.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=\ngo.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=\ngo.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=\ngo.opentelemetry.io/otel/exporters/zipkin v1.37.0 h1:Z2apuaRnHEjzDAkpbWNPiksz1R0/FCIrJSjiMA43zwI=\ngo.opentelemetry.io/otel/exporters/zipkin v1.37.0/go.mod h1:ofGu/7fG+bpmjZoiPUUmYDJ4vXWxMT57HmGoegx49uw=\ngo.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=\ngo.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=\ngo.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=\ngo.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=\ngo.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=\ngo.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=\ngo.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=\ngo.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=\ngo.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=\ngo.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\ngo.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=\ngo.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=\ngo.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=\ngo.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=\ngolang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=\ngolang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=\ngolang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=\ngolang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE=\ngolang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\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.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=\ngolang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\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.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=\ngolang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.7.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.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=\ngolang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=\ngolang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=\ngolang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/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.3.0/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.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo=\ngolang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=\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-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=\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/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=\ngolang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=\ngolang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\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.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=\ngolang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=\ngolang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\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/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=\ngolang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=\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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=\ngoogle.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngoogle.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=\ngoogle.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\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/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=\ngopkg.in/go-playground/mold.v2 v2.2.0/go.mod h1:XMyyRsGtakkDPbxXbrA5VODo6bUXyvoDjLd5l3T0XoA=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=\ngopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=\ngopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=\ngopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE=\ngopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=\ngopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\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.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nmvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=\nmvdan.cc/sh/v3 v3.6.0 h1:gtva4EXJ0dFNvl5bHjcUEvws+KRcDslT8VKheTYkbGU=\nmvdan.cc/sh/v3 v3.6.0/go.mod h1:U4mhtBLZ32iWhif5/lD+ygy1zrgaQhUu+XFy7C8+TTA=\npgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=\npgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=\nsigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=\n"
  },
  {
    "path": "hash/hash_comparator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hash\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/hmac\"\n\t\"crypto/md5\"  //nolint:all // System compatibility for imported passwords\n\t\"crypto/sha1\" //nolint:all // System compatibility for imported passwords\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"crypto/subtle\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"hash\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/go-crypt/crypt\"\n\t\"github.com/go-crypt/crypt/algorithm/md5crypt\"\n\t\"github.com/go-crypt/crypt/algorithm/shacrypt\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"golang.org/x/crypto/argon2\"\n\t\"golang.org/x/crypto/bcrypt\"\n\n\t//nolint:staticcheck\n\t//lint:ignore SA1019 compatibility for imported passwords\n\t\"golang.org/x/crypto/md4\" //nolint:gosec // disable G115 G501 -- compatibility for imported passwords\n\t\"golang.org/x/crypto/pbkdf2\"\n\t\"golang.org/x/crypto/scrypt\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar ErrUnknownHashAlgorithm = errors.New(\"unknown hash algorithm\")\n\nfunc NewCryptDecoder() *crypt.Decoder {\n\tdecoder := crypt.NewDecoder()\n\n\t// The register function only returns an error if the decoder is nil or the algorithm is already registered.\n\t// This is never the case here, if it is, we did something horribly wrong.\n\tif err := md5crypt.RegisterDecoderCommon(decoder); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := shacrypt.RegisterDecoder(decoder); err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn decoder\n}\n\nvar CryptDecoder = NewCryptDecoder()\n\ntype SupportedHasher struct {\n\tComparator func(ctx context.Context, password, hash []byte) error\n\tName       string\n\tIs         func(hash []byte) bool\n}\n\nfunc AddSupportedHasher(s SupportedHasher) {\n\tsupportedHashers = append(supportedHashers, s)\n}\n\nvar supportedHashers = []SupportedHasher{\n\t{\n\t\tComparator: CompareMD5Crypt,\n\t\tName:       \"md5crypt\",\n\t\tIs:         IsMD5CryptHash,\n\t},\n\t{\n\t\tComparator: CompareBcrypt,\n\t\tName:       \"bcrypt\",\n\t\tIs:         IsBcryptHash,\n\t},\n\t{\n\t\tComparator: CompareSHA256Crypt,\n\t\tName:       \"sha256crypt\",\n\t\tIs:         IsSHA256CryptHash,\n\t},\n\t{\n\t\tComparator: CompareSHA512Crypt,\n\t\tName:       \"sha512crypt\",\n\t\tIs:         IsSHA512CryptHash,\n\t},\n\t{\n\t\tComparator: CompareArgon2id,\n\t\tName:       \"argon2id\",\n\t\tIs:         IsArgon2idHash,\n\t},\n\t{\n\t\tComparator: CompareArgon2i,\n\t\tName:       \"argon2i\",\n\t\tIs:         IsArgon2iHash,\n\t},\n\t{\n\t\tComparator: ComparePbkdf2,\n\t\tName:       \"pbkdf2\",\n\t\tIs:         IsPbkdf2Hash,\n\t},\n\t{\n\t\tComparator: CompareScrypt,\n\t\tName:       \"scrypt\",\n\t\tIs:         IsScryptHash,\n\t},\n\t{\n\t\tComparator: CompareSSHA,\n\t\tName:       \"ssha\",\n\t\tIs:         IsSSHAHash,\n\t},\n\t{\n\t\tComparator: CompareSHA,\n\t\tName:       \"sha\",\n\t\tIs:         IsSHAHash,\n\t},\n\t{\n\t\tComparator: CompareFirebaseScrypt,\n\t\tName:       \"firebasescrypt\",\n\t\tIs:         IsFirebaseScryptHash,\n\t},\n\t{\n\t\tComparator: CompareMD5,\n\t\tName:       \"md5\",\n\t\tIs:         IsMD5Hash,\n\t},\n\t{\n\t\tComparator: CompareHMAC,\n\t\tName:       \"hmac\",\n\t\tIs:         IsHMACHash,\n\t},\n}\n\nfunc Compare(ctx context.Context, password, hash []byte) (err error) {\n\tctx, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, \"hash.Compare\")\n\tdefer otelx.End(span, &err)\n\n\tfor _, h := range supportedHashers {\n\t\tif h.Is(hash) {\n\t\t\tspan.SetAttributes(attribute.String(\"hash.type\", h.Name))\n\t\t\treturn h.Comparator(ctx, password, hash)\n\t\t}\n\t}\n\n\tspan.SetAttributes(attribute.String(\"hash.type\", \"unknown\"))\n\treturn errors.WithStack(ErrUnknownHashAlgorithm)\n}\n\nfunc CompareMD5Crypt(_ context.Context, password, hash []byte) error {\n\t// the password has successfully been validated (has prefix `$md5-crypt`),\n\t// the decoder expect the module crypt identifier instead (`$1`), which means we need to replace the prefix\n\t// before decoding\n\thash = bytes.TrimPrefix(hash, []byte(\"$md5-crypt\"))\n\thash = append([]byte(\"$1\"), hash...)\n\n\treturn compareCryptHelper(password, string(hash))\n}\n\nfunc CompareBcrypt(ctx context.Context, password, hash []byte) error {\n\tif err := validateBcryptPasswordLength(password); err != nil {\n\t\treturn err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\terr := bcrypt.CompareHashAndPassword(hash, password)\n\tif err != nil {\n\t\tif errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {\n\t\t\treturn errors.WithStack(ErrMismatchedHashAndPassword)\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc CompareSHA256Crypt(_ context.Context, password, hash []byte) error {\n\thash = bytes.TrimPrefix(hash, []byte(\"$sha256-crypt\"))\n\thash = append([]byte(\"$5\"), hash...)\n\n\treturn compareCryptHelper(password, string(hash))\n}\n\nfunc CompareSHA512Crypt(_ context.Context, password, hash []byte) error {\n\thash = bytes.TrimPrefix(hash, []byte(\"$sha512-crypt\"))\n\thash = append([]byte(\"$6\"), hash...)\n\n\treturn compareCryptHelper(password, string(hash))\n}\n\nfunc CompareArgon2id(ctx context.Context, password, hash []byte) error {\n\t// Extract the parameters, salt and derived key from the encoded password\n\t// hash.\n\tp, salt, hash, err := decodeArgon2idHash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\t// Derive the key from the other password using the same parameters.\n\t//nolint:gosec // disable G115\n\totherHash := argon2.IDKey(password, salt, p.Iterations, uint32(p.Memory), p.Parallelism, p.KeyLength)\n\n\treturn comparePasswordHashConstantTime(hash, otherHash)\n}\n\nfunc CompareArgon2i(ctx context.Context, password, hash []byte) error {\n\t// Extract the parameters, salt and derived key from the encoded password\n\t// hash.\n\tp, salt, hash, err := decodeArgon2idHash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\t// Derive the key from the other password using the same parameters.\n\totherHash := argon2.Key(password, salt, p.Iterations, uint32(p.Memory), p.Parallelism, p.KeyLength) // #nosec G115 -- memory (KiB) would need to be 2^32 kibibyte to overflow uint32\n\n\treturn comparePasswordHashConstantTime(hash, otherHash)\n}\n\nfunc ComparePbkdf2(ctx context.Context, password, hash []byte) error {\n\t// Extract the parameters, salt and derived key from the encoded password\n\t// hash.\n\tp, salt, hash, err := decodePbkdf2Hash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\t// Derive the key from the other password using the same parameters.\n\totherHash := pbkdf2.Key(password, salt, int(p.Iterations), int(p.KeyLength), getPseudorandomFunctionForPbkdf2(p.Algorithm))\n\n\treturn comparePasswordHashConstantTime(hash, otherHash)\n}\n\nfunc CompareScrypt(ctx context.Context, password, hash []byte) error {\n\t// Extract the parameters, salt and derived key from the encoded password\n\t// hash.\n\tp, salt, hash, err := decodeScryptHash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\t// Derive the key from the other password using the same parameters.\n\totherHash, err := scrypt.Key(password, salt, int(p.Cost), int(p.Block), int(p.Parrellization), int(p.KeyLength))\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn comparePasswordHashConstantTime(hash, otherHash)\n}\n\nfunc CompareSSHA(ctx context.Context, password, hash []byte) error {\n\thasher, salt, hash, err := decodeSSHAHash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\traw := append(password[:], salt[:]...)\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\treturn CompareSHAHelper(hasher, raw, hash)\n}\n\nfunc CompareSHA(ctx context.Context, password, hash []byte) error {\n\thasher, pf, salt, hash, err := decodeSHAHash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tr := strings.NewReplacer(\"{SALT}\", string(salt), \"{PASSWORD}\", string(password))\n\traw := []byte(r.Replace(string(pf)))\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\treturn CompareSHAHelper(hasher, raw, hash)\n}\n\nfunc CompareFirebaseScrypt(ctx context.Context, password, hash []byte) error {\n\t// Extract the parameters, salt and derived key from the encoded password\n\t// hash.\n\tp, salt, saltSeparator, hash, signerKey, err := decodeFirebaseScryptHash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\t// Derive the key from the other password using the same parameters.\n\t// FirebaseScript algorithm implementation from https://github.com/Aoang/firebase-scrypt\n\tck, err := scrypt.Key(password, append(salt, saltSeparator...), int(p.Cost), int(p.Block), int(p.Parrellization), 32)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tvar block cipher.Block\n\tif block, err = aes.NewCipher(ck); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tcipherText := make([]byte, aes.BlockSize+len(signerKey))\n\tstream := cipher.NewCTR(block, cipherText[:aes.BlockSize])\n\tstream.XORKeyStream(cipherText[aes.BlockSize:], signerKey)\n\totherHash := cipherText[aes.BlockSize:]\n\n\treturn comparePasswordHashConstantTime(hash, otherHash)\n}\n\nfunc CompareMD5(ctx context.Context, password, hash []byte) error {\n\t// Extract the hash from the encoded password\n\tpf, salt, hash, err := decodeMD5Hash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\targ := password\n\tif salt != nil {\n\t\tr := strings.NewReplacer(\"{SALT}\", string(salt), \"{PASSWORD}\", string(password))\n\t\targ = []byte(r.Replace(string(pf)))\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\t//#nosec G401 -- compatibility for imported passwords\n\totherHash := md5.Sum(arg)\n\n\treturn comparePasswordHashConstantTime(hash, otherHash[:])\n}\n\nfunc CompareHMAC(ctx context.Context, password, hash []byte) error {\n\t// Extract the hash from the encoded password\n\thasher, hash, key, err := decodeHMACHash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\tmac := hmac.New(hasher, key)\n\t_, err = mac.Write(password)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\totherHash := []byte(hex.EncodeToString(mac.Sum(nil)))\n\n\treturn comparePasswordHashConstantTime(hash, otherHash)\n}\n\nvar (\n\tisMD5CryptHash       = regexp.MustCompile(`^\\$md5-crypt\\$`)\n\tisBcryptHash         = regexp.MustCompile(`^\\$2[abzy]?\\$`)\n\tisSHA256CryptHash    = regexp.MustCompile(`^\\$sha256-crypt\\$`)\n\tisSHA512CryptHash    = regexp.MustCompile(`^\\$sha512-crypt\\$`)\n\tisArgon2idHash       = regexp.MustCompile(`^\\$argon2id\\$`)\n\tisArgon2iHash        = regexp.MustCompile(`^\\$argon2i\\$`)\n\tisPbkdf2Hash         = regexp.MustCompile(`^\\$pbkdf2-sha[0-9]{1,3}\\$`)\n\tisScryptHash         = regexp.MustCompile(`^\\$scrypt\\$`)\n\tisSSHAHash           = regexp.MustCompile(`^{SSHA(256|512)?}.*`)\n\tisSHAHash            = regexp.MustCompile(`^\\$sha(1|256|512)\\$`)\n\tisFirebaseScryptHash = regexp.MustCompile(`^\\$firescrypt\\$`)\n\tisMD5Hash            = regexp.MustCompile(`^\\$md5\\$`)\n\tisHMACHash           = regexp.MustCompile(`^\\$hmac-(md4|md5|sha1|sha224|sha256|sha384|sha512)\\$`)\n)\n\nfunc IsMD5CryptHash(hash []byte) bool       { return isMD5CryptHash.Match(hash) }\nfunc IsBcryptHash(hash []byte) bool         { return isBcryptHash.Match(hash) }\nfunc IsSHA256CryptHash(hash []byte) bool    { return isSHA256CryptHash.Match(hash) }\nfunc IsSHA512CryptHash(hash []byte) bool    { return isSHA512CryptHash.Match(hash) }\nfunc IsArgon2idHash(hash []byte) bool       { return isArgon2idHash.Match(hash) }\nfunc IsArgon2iHash(hash []byte) bool        { return isArgon2iHash.Match(hash) }\nfunc IsPbkdf2Hash(hash []byte) bool         { return isPbkdf2Hash.Match(hash) }\nfunc IsScryptHash(hash []byte) bool         { return isScryptHash.Match(hash) }\nfunc IsSSHAHash(hash []byte) bool           { return isSSHAHash.Match(hash) }\nfunc IsSHAHash(hash []byte) bool            { return isSHAHash.Match(hash) }\nfunc IsFirebaseScryptHash(hash []byte) bool { return isFirebaseScryptHash.Match(hash) }\nfunc IsMD5Hash(hash []byte) bool            { return isMD5Hash.Match(hash) }\nfunc IsHMACHash(hash []byte) bool           { return isHMACHash.Match(hash) }\n\nfunc IsValidHashFormat(hash []byte) bool {\n\tfor _, h := range supportedHashers {\n\t\tif h.Is(hash) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc decodeArgon2idHash(encodedHash string) (p *config.Argon2, salt, hash []byte, err error) {\n\tparts := strings.Split(encodedHash, \"$\")\n\tif len(parts) != 6 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\n\tvar version int\n\t_, err = fmt.Sscanf(parts[2], \"v=%d\", &version)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tif version != argon2.Version {\n\t\treturn nil, nil, nil, ErrIncompatibleVersion\n\t}\n\n\tp = new(config.Argon2)\n\t_, err = fmt.Sscanf(parts[3], \"m=%d,t=%d,p=%d\", &p.Memory, &p.Iterations, &p.Parallelism)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tsalt, err = base64.RawStdEncoding.Strict().DecodeString(parts[4])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tp.SaltLength = uint32(len(salt)) // #nosec G115 -- salt would need to be 2^32 bytes long to overflow uint32\n\n\thash, err = base64.RawStdEncoding.Strict().DecodeString(parts[5])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tp.KeyLength = uint32(len(hash)) // #nosec G115 -- hash would need to be 2^32 bytes long to overflow uint32\n\n\treturn p, salt, hash, nil\n}\n\n// decodePbkdf2Hash decodes PBKDF2 encoded password hash.\n// format: $pbkdf2-<digest>$i=<iterations>,l=<length>$<salt>$<hash>\nfunc decodePbkdf2Hash(encodedHash string) (p *Pbkdf2, salt, hash []byte, err error) {\n\tparts := strings.Split(encodedHash, \"$\")\n\tif len(parts) != 5 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\n\tp = new(Pbkdf2)\n\tdigestParts := strings.SplitN(parts[1], \"-\", 2)\n\tif len(digestParts) != 2 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\tp.Algorithm = digestParts[1]\n\n\t_, err = fmt.Sscanf(parts[2], \"i=%d,l=%d\", &p.Iterations, &p.KeyLength)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tsalt, err = base64.RawStdEncoding.Strict().DecodeString(parts[3])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tp.SaltLength = uint32(len(salt)) // #nosec G115 -- salt would need to be 2^32 bytes long to overflow uint32\n\n\thash, err = base64.RawStdEncoding.Strict().DecodeString(parts[4])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tp.KeyLength = uint32(len(hash)) // #nosec G115 -- hash would need to be 2^32 bytes long to overflow uint32\n\n\treturn p, salt, hash, nil\n}\n\n// decodeScryptHash decodes Scrypt encoded password hash.\n// format: $scrypt$ln=<cost>,r=<block>,p=<parrrelization>$<salt>$<hash>\nfunc decodeScryptHash(encodedHash string) (p *Scrypt, salt, hash []byte, err error) {\n\tparts := strings.Split(encodedHash, \"$\")\n\tif len(parts) != 5 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\n\tp = new(Scrypt)\n\n\t_, err = fmt.Sscanf(parts[2], \"ln=%d,r=%d,p=%d\", &p.Cost, &p.Block, &p.Parrellization)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tsalt, err = base64.StdEncoding.Strict().DecodeString(parts[3])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tp.SaltLength = uint32(len(salt)) // #nosec G115 -- salt would need to be 2^32 bytes long to overflow uint32\n\n\thash, err = base64.StdEncoding.Strict().DecodeString(parts[4])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tp.KeyLength = uint32(len(hash)) // #nosec G115 -- hash would need to be 2^32 bytes long to overflow uint32\n\n\treturn p, salt, hash, nil\n}\n\n// decodeSHAHash decodes SHA[1|256|512] encoded password hash in custom PHC format.\n// format: $sha1$pf=<salting-format>$<salt>$<hash>\nfunc decodeSHAHash(encodedHash string) (hasher string, pf, salt, hash []byte, err error) {\n\tparts := strings.Split(encodedHash, \"$\")\n\n\tif len(parts) != 5 {\n\t\treturn \"\", nil, nil, nil, ErrInvalidHash\n\t}\n\n\thasher = parts[1]\n\n\t_, err = fmt.Sscanf(parts[2], \"pf=%s\", &pf)\n\tif err != nil {\n\t\treturn \"\", nil, nil, nil, err\n\t}\n\n\tpf, err = base64.StdEncoding.Strict().DecodeString(string(pf))\n\tif err != nil {\n\t\treturn \"\", nil, nil, nil, err\n\t}\n\n\tsalt, err = base64.StdEncoding.Strict().DecodeString(parts[3])\n\tif err != nil {\n\t\treturn \"\", nil, nil, nil, err\n\t}\n\n\thash, err = base64.StdEncoding.Strict().DecodeString(parts[4])\n\tif err != nil {\n\t\treturn \"\", nil, nil, nil, err\n\t}\n\n\treturn hasher, pf, salt, hash, nil\n}\n\n// CompareSHAHelper compares the raw password with the hash using the given hasher.\nfunc CompareSHAHelper(hasher string, raw []byte, hash []byte) error {\n\tvar sha []byte\n\n\tswitch hasher {\n\tcase \"sha1\":\n\t\tsum := sha1.Sum(raw) //#nosec G401 -- compatibility for imported passwords\n\t\tsha = sum[:]\n\tcase \"sha256\":\n\t\tsum := sha256.Sum256(raw)\n\t\tsha = sum[:]\n\tcase \"sha512\":\n\t\tsum := sha512.Sum512(raw)\n\t\tsha = sum[:]\n\tdefault:\n\t\treturn errors.WithStack(ErrMismatchedHashAndPassword)\n\t}\n\n\tencodedHash := []byte(base64.StdEncoding.EncodeToString(hash))\n\tnewEncodedHash := []byte(base64.StdEncoding.EncodeToString(sha))\n\n\treturn comparePasswordHashConstantTime(encodedHash, newEncodedHash)\n}\n\nfunc compareCryptHelper(password []byte, hash string) error {\n\tdigest, err := CryptDecoder.Decode(hash)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif digest.MatchBytes(password) {\n\t\treturn nil\n\t}\n\n\treturn errors.WithStack(ErrMismatchedHashAndPassword)\n}\n\nvar regexSSHA = regexp.MustCompile(`\\{([^}]*)\\}`)\n\n// decodeSSHAHash decodes SSHA[1|256|512] encoded password hash in usual {SSHA...} format.\nfunc decodeSSHAHash(encodedHash string) (hasher string, salt, hash []byte, err error) {\n\tmatch := regexSSHA.FindStringSubmatch(string(encodedHash))\n\n\tvar index_of_salt_begin int\n\tvar index_of_hash_begin int\n\n\tswitch match[1] {\n\tcase \"SSHA\":\n\t\thasher = \"sha1\"\n\t\tindex_of_hash_begin = 6\n\t\tindex_of_salt_begin = 20\n\n\tcase \"SSHA256\":\n\t\thasher = \"sha256\"\n\t\tindex_of_hash_begin = 9\n\t\tindex_of_salt_begin = 32\n\n\tcase \"SSHA512\":\n\t\thasher = \"sha512\"\n\t\tindex_of_hash_begin = 9\n\t\tindex_of_salt_begin = 64\n\n\tdefault:\n\t\treturn \"\", nil, nil, ErrInvalidHash\n\t}\n\n\tdecoded, err := base64.StdEncoding.DecodeString(string(encodedHash[index_of_hash_begin:]))\n\tif err != nil {\n\t\treturn \"\", nil, nil, ErrInvalidHash\n\t}\n\n\tif len(decoded) < index_of_salt_begin+1 {\n\t\treturn \"\", nil, nil, ErrInvalidHash\n\t}\n\n\tsalt = decoded[index_of_salt_begin:]\n\thash = decoded[:index_of_salt_begin]\n\n\treturn hasher, salt, hash, nil\n}\n\n// decodeFirebaseScryptHash decodes Firebase Scrypt encoded password hash.\n// format: $firescrypt$ln=<mem_cost>,r=<rounds>,p=<parallelization>$<salt>$<hash>$<salt_separator>$<signer_key>\nfunc decodeFirebaseScryptHash(encodedHash string) (p *Scrypt, salt, saltSeparator, hash, signerKey []byte, err error) {\n\tparts := strings.Split(encodedHash, \"$\")\n\tif len(parts) != 7 {\n\t\treturn nil, nil, nil, nil, nil, ErrInvalidHash\n\t}\n\n\tp = new(Scrypt)\n\n\t_, err = fmt.Sscanf(parts[2], \"ln=%d,r=%d,p=%d\", &p.Cost, &p.Block, &p.Parrellization)\n\tif err != nil {\n\t\treturn nil, nil, nil, nil, nil, err\n\t}\n\t// convert from firebase config \"mem_cost\" to\n\t// scrypt CPU/memory cost parameter, which must be a power of two greater than 1.\n\tp.Cost = 1 << p.Cost\n\n\tsalt, err = base64.StdEncoding.Strict().DecodeString(parts[3])\n\tif err != nil {\n\t\treturn nil, nil, nil, nil, nil, err\n\t}\n\tp.SaltLength = uint32(len(salt)) // #nosec G115 -- salt would need to be 2^32 bytes long to overflow uint32\n\n\thash, err = base64.StdEncoding.Strict().DecodeString(parts[4])\n\tif err != nil {\n\t\treturn nil, nil, nil, nil, nil, err\n\t}\n\t// Are all firebase script hashes of length 32?\n\tp.KeyLength = 32\n\n\tsaltSeparator, err = base64.StdEncoding.Strict().DecodeString(parts[5])\n\tif err != nil {\n\t\treturn nil, nil, nil, nil, nil, err\n\t}\n\n\tsignerKey, err = base64.StdEncoding.Strict().DecodeString(parts[6])\n\tif err != nil {\n\t\treturn nil, nil, nil, nil, nil, err\n\t}\n\n\treturn p, salt, saltSeparator, hash, signerKey, nil\n}\n\n// decodeMD5Hash decodes MD5 encoded password hash.\n// format without salt: $md5$<hash>\n// format with salt $md5$pf=<salting-format>$<salt>$<hash>\nfunc decodeMD5Hash(encodedHash string) (pf, salt, hash []byte, err error) {\n\tparts := strings.Split(encodedHash, \"$\")\n\n\tswitch len(parts) {\n\tcase 3:\n\t\thash, err := base64.StdEncoding.Strict().DecodeString(parts[2])\n\t\treturn nil, nil, hash, err\n\tcase 5:\n\t\t_, err = fmt.Sscanf(parts[2], \"pf=%s\", &pf)\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\n\t\tpf, err := base64.StdEncoding.Strict().DecodeString(string(pf))\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\n\t\tsalt, err = base64.StdEncoding.Strict().DecodeString(parts[3])\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\n\t\thash, err = base64.StdEncoding.Strict().DecodeString(parts[4])\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\n\t\treturn pf, salt, hash, nil\n\tdefault:\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n}\n\n// decodeHMACHash decodes HMAC encoded password hash.\n// format : $hmac-<hash function>$<hash>$<key>\nfunc decodeHMACHash(encodedHash string) (hasher func() hash.Hash, hash, key []byte, err error) {\n\tparts := strings.Split(encodedHash, \"$\")\n\n\tif len(parts) != 4 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\n\thashMatch := isHMACHash.FindStringSubmatch(encodedHash)\n\n\tif len(hashMatch) != 2 {\n\t\treturn nil, nil, nil, errors.WithStack(ErrUnknownHashAlgorithm)\n\t}\n\n\tswitch hashMatch[1] {\n\tcase \"md4\":\n\t\thasher = md4.New //#nosec G401 -- compatibility for imported passwords\n\tcase \"md5\":\n\t\thasher = md5.New //#nosec G401 -- compatibility for imported passwords\n\tcase \"sha1\":\n\t\thasher = sha1.New //#nosec G401 -- compatibility for imported passwords\n\tcase \"sha224\":\n\t\thasher = sha256.New224\n\tcase \"sha256\":\n\t\thasher = sha256.New\n\tcase \"sha384\":\n\t\thasher = sha512.New384\n\tcase \"sha512\":\n\t\thasher = sha512.New\n\tdefault:\n\t\treturn nil, nil, nil, errors.WithStack(ErrUnknownHashAlgorithm)\n\t}\n\n\thash, err = base64.StdEncoding.Strict().DecodeString(parts[2])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tkey, err = base64.StdEncoding.Strict().DecodeString(parts[3])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\treturn hasher, hash, key, nil\n}\n\nfunc comparePasswordHashConstantTime(hash, otherHash []byte) error {\n\t// use subtle.ConstantTimeCompare() to prevent timing attacks.\n\tif subtle.ConstantTimeCompare(hash, otherHash) == 1 {\n\t\treturn nil\n\t}\n\treturn errors.WithStack(ErrMismatchedHashAndPassword)\n}\n"
  },
  {
    "path": "hash/hasher.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hash\n\nimport (\n\t\"context\"\n)\n\n// Hasher provides methods for generating and comparing password hashes.\ntype Hasher interface {\n\t// Generate returns a hash derived from the password or an error if the hash method failed.\n\tGenerate(ctx context.Context, password []byte) ([]byte, error)\n\n\t// Understands returns whether the given hash can be understood by this hasher.\n\tUnderstands(hash []byte) bool\n}\n\ntype HashProvider interface {\n\tHasher(ctx context.Context) Hasher\n}\n\nconst tracingComponent = \"github.com/ory/kratos/hash\"\n"
  },
  {
    "path": "hash/hasher_argon2.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hash\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t\"github.com/inhies/go-bytesize\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/codes\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/crypto/argon2\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar (\n\tErrInvalidHash               = errors.New(\"the encoded hash is not in the correct format\")\n\tErrIncompatibleVersion       = errors.New(\"incompatible version of argon2\")\n\tErrMismatchedHashAndPassword = errors.New(\"passwords do not match\")\n)\n\ntype Argon2 struct {\n\tc Argon2Configuration\n}\n\ntype Argon2Configuration interface {\n\tconfig.Provider\n}\n\nfunc NewHasherArgon2(c Argon2Configuration) *Argon2 {\n\treturn &Argon2{c: c}\n}\n\nfunc toKB(mem bytesize.ByteSize) uint32 {\n\t//nolint:gosec // disable G115\n\treturn uint32(mem / bytesize.KB)\n}\n\nfunc (h *Argon2) Generate(ctx context.Context, password []byte) (_ []byte, err error) {\n\tconf := h.c.Config().HasherArgon2(ctx)\n\n\t_, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, \"hash.Generate\", trace.WithAttributes(\n\t\tattribute.String(\"hash.type\", \"argon2id\"),\n\t\tattribute.String(\"hash.config\", fmt.Sprintf(\"%#v\", conf)),\n\t))\n\tdefer otelx.End(span, &err)\n\n\tsalt := make([]byte, conf.SaltLength)\n\tif _, err := rand.Read(salt); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn nil, ctx.Err()\n\t}\n\n\t// Pass the plaintext password, salt and parameters to the argon2.IDKey\n\t// function. This will generate a hash of the password using the Argon2id\n\t// variant.\n\thash := argon2.IDKey(password, salt, conf.Iterations, toKB(conf.Memory), conf.Parallelism, conf.KeyLength)\n\n\tvar b bytes.Buffer\n\tif _, err := fmt.Fprintf(\n\t\t&b,\n\t\t\"$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s\",\n\t\targon2.Version, toKB(conf.Memory), conf.Iterations, conf.Parallelism,\n\t\tbase64.RawStdEncoding.EncodeToString(salt),\n\t\tbase64.RawStdEncoding.EncodeToString(hash),\n\t); err != nil {\n\t\tspan.RecordError(err)\n\t\tspan.SetStatus(codes.Error, err.Error())\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn b.Bytes(), nil\n}\n\nfunc (h *Argon2) Understands(hash []byte) bool {\n\treturn IsArgon2idHash(hash)\n}\n"
  },
  {
    "path": "hash/hasher_bcrypt.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hash\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/schema\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n\n\t\"github.com/ory/kratos/driver/config\"\n)\n\ntype Bcrypt struct {\n\tc BcryptConfiguration\n}\n\ntype BcryptConfiguration interface {\n\tconfig.Provider\n}\n\nfunc NewHasherBcrypt(c BcryptConfiguration) *Bcrypt {\n\treturn &Bcrypt{c: c}\n}\n\nfunc (h *Bcrypt) Generate(ctx context.Context, password []byte) (_ []byte, err error) {\n\tconf := h.c.Config().HasherBcrypt(ctx)\n\n\t_, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, \"hash.Generate\", trace.WithAttributes(\n\t\tattribute.String(\"hash.type\", \"bcrypt\"),\n\t\tattribute.String(\"hash.config\", fmt.Sprintf(\"%#v\", conf)),\n\t))\n\tdefer otelx.End(span, &err)\n\n\tif err := validateBcryptPasswordLength(password); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn nil, ctx.Err()\n\t}\n\n\thash, err := bcrypt.GenerateFromPassword(password, int(conf.Cost))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn hash, nil\n}\n\nfunc validateBcryptPasswordLength(password []byte) error {\n\t// Bcrypt truncates the password to the first 72 bytes, following the OpenBSD implementation,\n\t// so if password is longer than 72 bytes, function returns an error\n\t// See https://en.wikipedia.org/wiki/Bcrypt#User_input\n\tif len(password) > 72 {\n\t\treturn schema.NewPasswordPolicyViolationError(\n\t\t\t\"#/password\",\n\t\t\ttext.NewErrorValidationPasswordMaxLength(72, len(password)),\n\t\t)\n\t}\n\treturn nil\n}\n\nfunc (h *Bcrypt) Understands(hash []byte) bool {\n\treturn IsBcryptHash(hash)\n}\n"
  },
  {
    "path": "hash/hasher_pbkdf2.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hash\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/sha1\" //#nosec G505 -- compatibility for imported passwords\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"hash\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/codes\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"golang.org/x/crypto/pbkdf2\"\n\t\"golang.org/x/crypto/sha3\"\n\n\t\"github.com/ory/x/otelx\"\n)\n\ntype Pbkdf2 struct {\n\tAlgorithm  string\n\tIterations uint32\n\tSaltLength uint32\n\tKeyLength  uint32\n}\n\nfunc (h *Pbkdf2) Generate(ctx context.Context, password []byte) (_ []byte, err error) {\n\t_, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, \"hash.Generate\", trace.WithAttributes(\n\t\tattribute.String(\"hash.type\", \"pbkdf2\"),\n\t\tattribute.String(\"hash.config\", fmt.Sprintf(\"%#v\", h)),\n\t))\n\tdefer otelx.End(span, &err)\n\n\tsalt := make([]byte, h.SaltLength)\n\tif _, err := rand.Read(salt); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// ensure that the context is not canceled before doing the heavy lifting\n\tif ctx.Err() != nil {\n\t\treturn nil, ctx.Err()\n\t}\n\n\tkey := pbkdf2.Key(password, salt, int(h.Iterations), int(h.KeyLength), getPseudorandomFunctionForPbkdf2(h.Algorithm))\n\n\tvar b bytes.Buffer\n\tif _, err := fmt.Fprintf(\n\t\t&b,\n\t\t\"$pbkdf2-%s$i=%d,l=%d$%s$%s\",\n\t\th.Algorithm,\n\t\th.Iterations,\n\t\th.KeyLength,\n\t\tbase64.RawStdEncoding.EncodeToString(salt),\n\t\tbase64.RawStdEncoding.EncodeToString(key),\n\t); err != nil {\n\t\tspan.RecordError(err)\n\t\tspan.SetStatus(codes.Error, err.Error())\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn b.Bytes(), nil\n}\n\nfunc (h *Pbkdf2) Understands(hash []byte) bool {\n\treturn IsPbkdf2Hash(hash)\n}\n\nfunc getPseudorandomFunctionForPbkdf2(alg string) func() hash.Hash {\n\tswitch alg {\n\tcase \"sha1\":\n\t\treturn sha1.New\n\tcase \"sha224\":\n\t\treturn sha3.New224\n\tcase \"sha256\":\n\t\treturn sha256.New\n\tcase \"sha384\":\n\t\treturn sha3.New384\n\tcase \"sha512\":\n\t\treturn sha512.New\n\tdefault:\n\t\treturn sha256.New\n\t}\n}\n"
  },
  {
    "path": "hash/hasher_scrypt.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hash\n\ntype Scrypt struct {\n\tCost           uint32\n\tBlock          uint32\n\tParrellization uint32\n\tSaltLength     uint32\n\tKeyLength      uint32\n}\n"
  },
  {
    "path": "hash/hasher_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hash_test\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc mkpw(t *testing.T, length int) []byte {\n\tpw := make([]byte, length)\n\t_, err := rand.Read(pw)\n\trequire.NoError(t, err)\n\treturn pw\n}\n\nfunc TestArgonHasher(t *testing.T) {\n\tt.Parallel()\n\tctx := t.Context()\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\th := hash.NewHasherArgon2(reg)\n\tfor _, pwLength := range []int{\n\t\t8,\n\t\t16,\n\t\t32,\n\t\t64,\n\t\t128,\n\t} {\n\t\tpwLength := pwLength\n\t\tt.Run(fmt.Sprintf(\"length=%dchars\", pwLength), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tpw := mkpw(t, pwLength)\n\t\t\ths, err := h.Generate(ctx, pw)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.NotEqual(t, pw, hs)\n\n\t\t\tt.Logf(\"hash: %s\", hs)\n\t\t\trequire.NoError(t, hash.CompareArgon2id(ctx, pw, hs))\n\n\t\t\tmod := make([]byte, len(pw))\n\t\t\tcopy(mod, pw)\n\t\t\tmod[len(pw)-1] = ^pw[len(pw)-1]\n\t\t\trequire.Error(t, hash.CompareArgon2id(ctx, mod, hs))\n\t\t})\n\t}\n}\n\nfunc TestBcryptHasherGeneratesErrorWhenPasswordIsLong(t *testing.T) {\n\tt.Parallel()\n\tctx := t.Context()\n\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\thasher := hash.NewHasherBcrypt(reg)\n\n\tpassword := mkpw(t, 73)\n\tres, err := hasher.Generate(ctx, password)\n\n\tassert.Error(t, err, \"password is too long\")\n\tassert.Nil(t, res)\n}\n\nfunc TestBcryptHasherGeneratesHash(t *testing.T) {\n\tt.Parallel()\n\tctx := t.Context()\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\thasher := hash.NewHasherBcrypt(reg)\n\tfor _, pwLength := range []int{\n\t\t8,\n\t\t16,\n\t\t32,\n\t\t64,\n\t\t72,\n\t} {\n\t\tpwLength := pwLength\n\t\tt.Run(fmt.Sprintf(\"length=%dchars\", pwLength), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tpw := mkpw(t, pwLength)\n\t\t\ths, err := hasher.Generate(ctx, pw)\n\n\t\t\tassert.Nil(t, err)\n\t\t\tassert.True(t, hasher.Understands(hs))\n\n\t\t\t// Valid format: $2a$12$[22 character salt][31 character hash]\n\t\t\tassert.Equal(t, 60, len(string(hs)), \"invalid bcrypt hash length\")\n\t\t\tassert.Equal(t, \"$2a$12$\", string(hs)[:7], \"invalid bcrypt identifier\")\n\t\t})\n\t}\n}\n\nfunc TestComparatorBcryptFailsWhenPasswordIsTooLong(t *testing.T) {\n\tt.Parallel()\n\tctx := t.Context()\n\tpassword := mkpw(t, 73)\n\terr := hash.CompareBcrypt(ctx, password, []byte(\"hash\"))\n\n\tassert.Error(t, err, \"password is too long\")\n}\n\nfunc TestComparatorBcryptSuccess(t *testing.T) {\n\tt.Parallel()\n\tctx := t.Context()\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\thasher := hash.NewHasherBcrypt(reg)\n\tfor _, pwLength := range []int{\n\t\t8,\n\t\t16,\n\t\t32,\n\t\t64,\n\t\t72,\n\t} {\n\t\tpwLength := pwLength\n\t\tt.Run(fmt.Sprintf(\"length=%dchars\", pwLength), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tpw := mkpw(t, pwLength)\n\n\t\t\ths, err := hasher.Generate(ctx, pw)\n\n\t\t\tassert.Nil(t, err)\n\t\t\tassert.True(t, hasher.Understands(hs))\n\n\t\t\terr = hash.CompareBcrypt(ctx, pw, hs)\n\t\t\tassert.Nil(t, err, \"hash validation fails\")\n\t\t})\n\t}\n}\n\nfunc TestComparatorBcryptFail(t *testing.T) {\n\tt.Parallel()\n\tctx := t.Context()\n\tfor _, pwLength := range []int{\n\t\t8,\n\t\t16,\n\t\t32,\n\t\t64,\n\t\t72,\n\t} {\n\t\tpwLength := pwLength\n\t\tt.Run(fmt.Sprintf(\"length=%dchars\", pwLength), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tpw := mkpw(t, pwLength)\n\t\t\tmod := make([]byte, len(pw))\n\t\t\tcopy(mod, pw)\n\t\t\tmod[len(pw)-1] = ^pw[len(pw)-1]\n\n\t\t\terr := hash.CompareBcrypt(ctx, pw, mod)\n\t\t\tassert.Error(t, err)\n\t\t})\n\t}\n}\n\nfunc TestPbkdf2Hasher(t *testing.T) {\n\tt.Parallel()\n\tctx := t.Context()\n\tfor _, pwLength := range []int{\n\t\t8,\n\t\t16,\n\t\t32,\n\t\t64,\n\t\t128,\n\t} {\n\t\tpwLength := pwLength\n\t\tt.Run(fmt.Sprintf(\"length=%dchars\", pwLength), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tfor _, algorithm := range []string{\n\t\t\t\t\"sha1\",\n\t\t\t\t\"sha224\",\n\t\t\t\t\"sha256\",\n\t\t\t\t\"sha384\",\n\t\t\t\t\"sha512\",\n\t\t\t} {\n\t\t\t\talgorithm := algorithm\n\t\t\t\tt.Run(fmt.Sprintf(\"algorithm=%s\", algorithm), func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\t\t\t\t\thasher := &hash.Pbkdf2{\n\t\t\t\t\t\tAlgorithm:  algorithm,\n\t\t\t\t\t\tIterations: 100_000,\n\t\t\t\t\t\tSaltLength: 32,\n\t\t\t\t\t\tKeyLength:  32,\n\t\t\t\t\t}\n\t\t\t\t\tpw := mkpw(t, pwLength)\n\t\t\t\t\tt.Logf(\"%d\", pwLength)\n\t\t\t\t\ths, err := hasher.Generate(ctx, pw)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.NotEqual(t, pw, hs)\n\n\t\t\t\t\tt.Logf(\"hash: %s\", hs)\n\t\t\t\t\trequire.NoError(t, hash.ComparePbkdf2(ctx, pw, hs))\n\n\t\t\t\t\tassert.True(t, hasher.Understands(hs))\n\n\t\t\t\t\tmod := make([]byte, len(pw))\n\t\t\t\t\tcopy(mod, pw)\n\t\t\t\t\tmod[len(pw)-1] = ^pw[len(pw)-1]\n\t\t\t\t\trequire.Error(t, hash.ComparePbkdf2(ctx, mod, hs))\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompare(t *testing.T) {\n\tt.Parallel()\n\tctx := t.Context()\n\n\tt.Run(\"unknown\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$unknown$12$o6hx.Wog/wvFSkT/Bp/6DOxCtLRTDj7lm9on9suF/WaCGNVHbkfL6\")))\n\t})\n\n\tt.Run(\"bcrypt\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$2a$12$o6hx.Wog/wvFSkT/Bp/6DOxCtLRTDj7lm9on9suF/WaCGNVHbkfL6\")))\n\t\tassert.Nil(t, hash.CompareBcrypt(ctx, []byte(\"test\"), []byte(\"$2a$12$o6hx.Wog/wvFSkT/Bp/6DOxCtLRTDj7lm9on9suF/WaCGNVHbkfL6\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$2a$12$o6hx.Wog/wvFSkT/Bp/6DOxCtLRTDj7lm9on9suF/WaCGNVHbkfL7\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$2a$15$GRvRO2nrpYTEuPQX6AieaOlZ4.7nMGsXpt.QWMev1zrP86JNspZbO\")))\n\t\tassert.Nil(t, hash.CompareBcrypt(ctx, []byte(\"test\"), []byte(\"$2a$15$GRvRO2nrpYTEuPQX6AieaOlZ4.7nMGsXpt.QWMev1zrP86JNspZbO\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$2a$15$GRvRO2nrpYTEuPQX6AieaOlZ4.7nMGsXpt.QWMev1zrP86JNspZb1\")))\n\t})\n\n\tt.Run(\"Argon2\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\")))\n\t\tassert.Nil(t, hash.CompareArgon2id(ctx, []byte(\"test\"), []byte(\"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRN2\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$argon2i$v=19$m=65536,t=3,p=4$kk51rW/vxIVCYn+EG4kTSg$NyT88uraJ6im6dyha/M5jhXvpqlEdlS/9fEm7ScMb8c\")))\n\t\tassert.Nil(t, hash.CompareArgon2i(ctx, []byte(\"test\"), []byte(\"$argon2i$v=19$m=65536,t=3,p=4$kk51rW/vxIVCYn+EG4kTSg$NyT88uraJ6im6dyha/M5jhXvpqlEdlS/9fEm7ScMb8c\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$argon2i$v=19$m=65536,t=3,p=4$pZ+27D6B0bCi0DwSmANF1w$4RNCUu4Uyu7eTIvzIdSuKz+I9idJlX/ykn6J10/W0EU\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$argon2id$v=19$m=32,t=5,p=4$cm94YnRVOW5jZzFzcVE4bQ$fBxypOL0nP/zdPE71JtAV71i487LbX3fJI5PoTN6Lp4\")))\n\t\tassert.Nil(t, hash.CompareArgon2id(ctx, []byte(\"test\"), []byte(\"$argon2id$v=19$m=32,t=5,p=4$cm94YnRVOW5jZzFzcVE4bQ$fBxypOL0nP/zdPE71JtAV71i487LbX3fJI5PoTN6Lp4\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$argon2id$v=19$m=32,t=5,p=4$cm94YnRVOW5jZzFzcVE4bQ$fBxypOL0nP/zdPE71JtAV71i487LbX3fJI5PoTN6Lp5\")))\n\t})\n\n\tt.Run(\"pbkdf2\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha256$i=100000,l=32$1jP+5Zxpxgtee/iPxGgOz0RfE9/KJuDElP1ley4VxXc$QJxzfvdbHYBpydCbHoFg3GJEqMFULwskiuqiJctoYpI\")))\n\t\tassert.Nil(t, hash.ComparePbkdf2(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha256$i=100000,l=32$1jP+5Zxpxgtee/iPxGgOz0RfE9/KJuDElP1ley4VxXc$QJxzfvdbHYBpydCbHoFg3GJEqMFULwskiuqiJctoYpI\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha256$i=100000,l=32$1jP+5Zxpxgtee/iPxGgOz0RfE9/KJuDElP1ley4VxXc$QJxzfvdbHYBpydCbHoFg3GJEqMFULwskiuqiJctoYpp\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha512$i=100000,l=32$bdHBpn7OWOivJMVJypy2UqR0UnaD5prQXRZevj/05YU$+wArTfv1a+bNGO1iZrmEdVjhA+lL11wF4/IxpgYfPwc\")))\n\t\tassert.Nil(t, hash.ComparePbkdf2(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha512$i=100000,l=32$bdHBpn7OWOivJMVJypy2UqR0UnaD5prQXRZevj/05YU$+wArTfv1a+bNGO1iZrmEdVjhA+lL11wF4/IxpgYfPwc\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha512$i=100000,l=32$bdHBpn7OWOivJMVJypy2UqR0UnaD5prQXRZevj/05YU$+wArTfv1a+bNGO1iZrmEdVjhA+lL11wF4/IxpgYfPww\")))\n\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha256$1jP+5Zxpxgtee/iPxGgOz0RfE9/KJuDElP1ley4VxXc$QJxzfvdbHYBpydCbHoFg3GJEqMFULwskiuqiJctoYpI\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha256$aaaa$1jP+5Zxpxgtee/iPxGgOz0RfE9/KJuDElP1ley4VxXc$QJxzfvdbHYBpydCbHoFg3GJEqMFULwskiuqiJctoYpI\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha256$i=100000,l=32$1jP+5Zxpxgtee/iPxGgOz0RfE9/KJuDElP1ley4VxXcc$QJxzfvdbHYBpydCbHoFg3GJEqMFULwskiuqiJctoYpI\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha256$i=100000,l=32$1jP+5Zxpxgtee/iPxGgOz0RfE9/KJuDElP1ley4VxXc$QJxzfvdbHYBpydCbHoFg3GJEqMFULwskiuqiJctoYpII\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$pbkdf2-sha512$I=100000,l=32$bdHBpn7OWOivJMVJypy2UqR0UnaD5prQXRZevj/05YU$+wArTfv1a+bNGO1iZrmEdVjhA+lL11wF4/IxpgYfPwc\")))\n\t})\n\n\tt.Run(\"scrypt\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$scrypt$ln=16384,r=8,p=1$2npRo7P03Mt8keSoMbyD/tKFWyUzjiQf2svUaNDSrhA=$MiCzNcIplSMqSBrm4HckjYqYhaVPPjTARTzwB1cVNYE=\")))\n\t\tassert.Nil(t, hash.CompareScrypt(ctx, []byte(\"test\"), []byte(\"$scrypt$ln=16384,r=8,p=1$2npRo7P03Mt8keSoMbyD/tKFWyUzjiQf2svUaNDSrhA=$MiCzNcIplSMqSBrm4HckjYqYhaVPPjTARTzwB1cVNYE=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$scrypt$ln=16384,r=8,p=1$2npRo7P03Mt8keSoMbyD/tKFWyUzjiQf2svUaNDSrhA=$MiCzNcIplSMqSBrm4HckjYqYhaVPPjTARTzwB1cVNYF=\")))\n\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$scrypt$2npRo7P03Mt8keSoMbyD/tKFWyUzjiQf2svUaNDSrhA=$MiCzNcIplSMqSBrm4HckjYqYhaVPPjTARTzwB1cVNYE=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$scrypt$ln=16384,r=8,p=1$(2npRo7P03Mt8keSoMbyD/tKFWyUzjiQf2svUaNDSrhA=$MiCzNcIplSMqSBrm4HckjYqYhaVPPjTARTzwB1cVNYE=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$scrypt$ln=16384,r=8,p=1$(2npRo7P03Mt8keSoMbyD/tKFWyUzjiQf2svUaNDSrhA=$(MiCzNcIplSMqSBrm4HckjYqYhaVPPjTARTzwB1cVNYE=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$scrypt$ln=16385,r=8,p=1$2npRo7P03Mt8keSoMbyD/tKFWyUzjiQf2svUaNDSrhA=$MiCzNcIplSMqSBrm4HckjYqYhaVPPjTARTzwB1cVNYE=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"tesu\"), []byte(\"$scrypt$ln=16384,r=8,p=1$2npRo7P03Mt8keSoMbyD/tKFWyUzjiQf2svUaNDSrhA=$MiCzNcIplSMqSBrm4HckjYqYhaVPPjTARTzwB1cVNYE=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"tesu\"), []byte(\"$scrypt$ln=abc,r=8,p=1$2npRo7P03Mt8keSoMbyD/tKFWyUzjiQf2svUaNDSrhA=$MiCzNcIplSMqSBrm4HckjYqYhaVPPjTARTzwB1cVNYE=\")))\n\t})\n\n\tt.Run(\"firescrypt\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"8x4WjoDbSxJZdR\"), []byte(\"$firescrypt$ln=14,r=8,p=1$sPtDhWcd1MfdAw==$xbSou7FOl6mChCyzpCPIQ7tku7nsQMTFtyOZSXXd7tjBa4NtimOx7v42Gv2SfzPQu1oxM2/k4SsbOu73wlKe1A==$Bw==$YE0dO4bwD4JnJafh6lZZfkp1MtKzuKAXQcDCJNJNyeCHairWHKENOkbh3dzwaCdizzOspwr/FITUVlnOAwPKyw==\")))\n\t\tassert.Nil(t, hash.CompareFirebaseScrypt(ctx, []byte(\"8x4WjoDbSxJZdR\"), []byte(\"$firescrypt$ln=14,r=8,p=1$sPtDhWcd1MfdAw==$xbSou7FOl6mChCyzpCPIQ7tku7nsQMTFtyOZSXXd7tjBa4NtimOx7v42Gv2SfzPQu1oxM2/k4SsbOu73wlKe1A==$Bw==$YE0dO4bwD4JnJafh6lZZfkp1MtKzuKAXQcDCJNJNyeCHairWHKENOkbh3dzwaCdizzOspwr/FITUVlnOAwPKyw==\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"8x4WjoDbSxJZdR\"), []byte(\"$firescrypt$ln=14,r=8,p=1$sPtDhWcd1MfdAw==$xbSou7FOl6mChCyzpCPIQ7tku7nsQMTFtyOZSXXd7tjBa4NtimOx7v42Gv2SfzPQu1oxM2/k4SsbOu73wlKe1A==$Bw==$YE0dO4bwD4JnJafh6lZZfkp1MtKzuKAXQcDCJNJNyeCHairWHKENOkbh3dzwaCdizzOspwr/FITUVlnOAwPKyc==\")))\n\t})\n\n\tt.Run(\"SSHA\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test123\"), []byte(\"{SSHA}JFZFs0oHzxbMwkSJmYVeI8MnTDy/276a\")))\n\t\tassert.Nil(t, hash.CompareSSHA(ctx, []byte(\"test123\"), []byte(\"{SSHA}JFZFs0oHzxbMwkSJmYVeI8MnTDy/276a\")))\n\t\tassert.Error(t, hash.CompareSSHA(ctx, []byte(\"badtest\"), []byte(\"{SSHA}JFZFs0oHzxbMwkSJmYVeI8MnTDy/276a\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"\"), []byte(\"{SSHA}tooshort\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test123\"), []byte(\"{SSHA256}czO44OTV17PcF1cRxWrLZLy9xHd7CWyVYplr1rOhuMlx/7IK\")))\n\t\tassert.Nil(t, hash.CompareSSHA(ctx, []byte(\"test123\"), []byte(\"{SSHA256}czO44OTV17PcF1cRxWrLZLy9xHd7CWyVYplr1rOhuMlx/7IK\")))\n\t\tassert.Error(t, hash.CompareSSHA(ctx, []byte(\"badtest\"), []byte(\"{SSHA256}czO44OTV17PcF1cRxWrLZLy9xHd7CWyVYplr1rOhuMlx/7IK\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test123\"), []byte(\"{SSHA512}xPUl/px+1cG55rUH4rzcwxdOIPSB2TingLpiJJumN2xyDWN4Ix1WQG3ihnvHaWUE8MYNkvMi5rf0C9NYixHsE6Yh59M=\")))\n\t\tassert.Nil(t, hash.CompareSSHA(ctx, []byte(\"test123\"), []byte(\"{SSHA512}xPUl/px+1cG55rUH4rzcwxdOIPSB2TingLpiJJumN2xyDWN4Ix1WQG3ihnvHaWUE8MYNkvMi5rf0C9NYixHsE6Yh59M=\")))\n\t\tassert.Error(t, hash.CompareSSHA(ctx, []byte(\"badtest\"), []byte(\"{SSHA512}xPUl/px+1cG55rUH4rzcwxdOIPSB2TingLpiJJumN2xyDWN4Ix1WQG3ihnvHaWUE8MYNkvMi5rf0C9NYixHsE6Yh59M=\")))\n\t\tassert.Error(t, hash.CompareSSHA(ctx, []byte(\"test123\"), []byte(\"{SSHAnotExistent}xPUl/px+1cG55rUH4rzcwxdOIPSB2TingLpiJJumN2xyDWN4Ix1WQG3ihnvHaWUE8MYNkvMi5rf0C9NYixHsE6Yh59M=\")))\n\t})\n\n\tt.Run(\"sha1\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t//pf: {SALT}{PASSWORD}\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha1$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$2qU2SGWP8viTM1md3FiI3+rjWXQ=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"wrongpass\"), []byte(\"$sha1$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$2qU2SGWP8viTM1md3FiI3+rjWXQ=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"tset\"), []byte(\"$sha1$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$2qU2SGWP8viTM1md3FiI3+rjWXQ=\")))\n\t\t// wrong salt\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha1$pf=e1NBTFR9e1BBU1NXT1JEfQ==$cDJvb3ZrZGJ6cQ==$2qU2SGWP8viTM1md3FiI3+rjWXQ=\")))\n\t\t// salt not encoded\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha1$pf=e1NBTFR9e1BBU1NXT1JEfQ==$5opmkgz03r$2qU2SGWP8viTM1md3FiI3+rjWXQ=\")))\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"BwS^514g^cv@Z\"), []byte(\"$sha1$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$99h9net4BXl7qdTRaiGUobLROxM=\")))\n\t\t// no format string\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha1$pf=$NW9wbWtnejAzcg==$2qU2SGWP8viTM1md3FiI3+rjWXQ=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha1$$NW9wbWtnejAzcg==$2qU2SGWP8viTM1md3FiI3+rjWXQ=\")))\n\t\t// wrong number of parameters\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha1$NW9wbWtnejAzcg==$2qU2SGWP8viTM1md3FiI3+rjWXQ=\")))\n\t\t// pf: ??staticPrefix??{SALT}{PASSWORD}\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha1$pf=Pz9zdGF0aWNQcmVmaXg/P3tTQUxUfXtQQVNTV09SRH0=$NW9wbWtnejAzcg==$SAAxMUn7jxckQXkBmsVF0nHwqso=\")))\n\t\t// pf: {PASSWORD}%%{SALT}\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha1$pf=e1BBU1NXT1JEfSUle1NBTFR9$NW9wbWtnejAzcg==$YX0AW8/MW5ojUlnzTaR43ucHCog=\")))\n\t\t// pf: ${PASSWORD}${SALT}$\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha1$pf=JHtQQVNTV09SRH0ke1NBTFR9JA==$NW9wbWtnejAzcg==$iE5n1yjX3oAdxRHwZ4u57I4LpQo=\")))\n\t})\n\n\tt.Run(\"sha256\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t//pf: {SALT}{PASSWORD}\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha256$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$0gfRVLCvtBCk20udLDEY5vNhujWx7RGjwRIS1ebMsLY=\")))\n\t\tassert.Nil(t, hash.CompareSHA(ctx, []byte(\"test\"), []byte(\"$sha256$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$0gfRVLCvtBCk20udLDEY5vNhujWx7RGjwRIS1ebMsLY=\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"wrongpass\"), []byte(\"$sha256$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$0gfRVLCvtBCk20udLDEY5vNhujWx7RGjwRIS1ebMsLY=\")))\n\t\t//pf: {SALT}$${PASSWORD}\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha256$pf=e1NBTFR9JCR7UEFTU1dPUkR9$NW9wbWtnejAzcg==$HokCOi9OtiZaZRvnkgemV3B4UUHpI7kA8zq/EZWH2NY=\")))\n\t})\n\n\tt.Run(\"sha512\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t//pf: {SALT}{PASSWORD}\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha512$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$6ctpVuApMNp0CgBXcdHw/GC562eFEFGr4gpgANX8ZYsX+j5B19IkdmOY2Fytsz3QUwSWdGcUjbqwgJGTH0UYvw==\")))\n\t\tassert.Nil(t, hash.CompareSHA(ctx, []byte(\"test\"), []byte(\"$sha512$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$6ctpVuApMNp0CgBXcdHw/GC562eFEFGr4gpgANX8ZYsX+j5B19IkdmOY2Fytsz3QUwSWdGcUjbqwgJGTH0UYvw==\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"wrongpass\"), []byte(\"$sha512$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$6ctpVuApMNp0CgBXcdHw/GC562eFEFGr4gpgANX8ZYsX+j5B19IkdmOY2Fytsz3QUwSWdGcUjbqwgJGTH0UYvw==\")))\n\t\t//pf: {SALT}$${PASSWORD}\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha512$pf=e1NBTFR9JCR7UEFTU1dPUkR9$NW9wbWtnejAzcg==$1F9BPW8UtdJkZ9Dhlf+D4X4dJ9xfuH8y04EfuCP2k4aGPPq/aWxU9/xe3LydHmYW1/K3zu3NFO9ETVrZettz3w==\")))\n\t})\n\n\tt.Run(\"sha unknown\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$shaNotExistent$pf=e1NBTFR9e1BBU1NXT1JEfQ==$NW9wbWtnejAzcg==$6ctpVuApMNp0CgBXcdHw/GC562eFEFGr4gpgANX8ZYsX+j5B19IkdmOY2Fytsz3QUwSWdGcUjbqwgJGTH0UYvw==\")))\n\t})\n\n\tt.Run(\"md5\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$md5$CY9rzUYh03PK3k6DJie09g==\")))\n\t\tassert.Nil(t, hash.CompareMD5(ctx, []byte(\"test\"), []byte(\"$md5$CY9rzUYh03PK3k6DJie09g==\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$md5$WhBei51A4TKXgNYuoiZdig==\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$md5$Dk/E5LQLsx4yt8QbUbvpdg==\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5$ptoWyof5SobW+pbZu2QXoQ==\")))\n\t\tassert.Nil(t, hash.CompareMD5(ctx, []byte(\"ory\"), []byte(\"$md5$ptoWyof5SobW+pbZu2QXoQ==\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5$4skj967KRHFsnPFoL5dMMw==\")))\n\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5$$\")), hash.ErrInvalidHash)\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5$$$\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5$pf=$$\")))\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5$pf=MTIz$Z$\")), base64.CorruptInputError(0))\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5$pf=MTIz$Z$\")), base64.CorruptInputError(0))\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5$pf=MTIz$MTIz$Z\")), base64.CorruptInputError(0))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$md5$pf=e1NBTFR9e1BBU1NXT1JEfQ==$MTIz$q+RdKCgc+ipCAcm5ChQwlQ==\"))) // pf={SALT}{PASSWORD} salt=123\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$md5$pf=e1NBTFR9e1BBU1NXT1JEfQ==$MTIz$hh8ZTp1hGPPZQqcr4+UXSQ==\")))\n\n\t\tassert.Nil(t, hash.CompareMD5(ctx, []byte(\"test\"), []byte(\"$md5$pf=e1NBTFR9JCR7UEFTU1dPUkR9$MTIzNA==$ud392Z8rfZ+Ou7ZFXYLKbA==\"))) // pf={SALT}$${PASSWORD} salt=1234\n\t\tassert.Error(t, hash.CompareMD5(ctx, []byte(\"test1\"), []byte(\"$md5$pf=e1NBTFR9JCR7UEFTU1dPUkR9$MTIzNA==$ud392Z8rfZ+Ou7ZFXYLKbA==\")))\n\n\t\tassert.Nil(t, hash.CompareMD5(ctx, []byte(\"ory\"), []byte(\"$md5$pf=e1BBU1NXT1JEfXtTQUxUfSQ/$MTIzNDU2Nzg5$8PhwWanVRnpJAFK4NUjR0w==\"))) // pf={PASSWORD}{SALT}$? salt=123456789\n\t\tassert.Error(t, hash.CompareMD5(ctx, []byte(\"ory1\"), []byte(\"$md5$pf=e1BBU1NXT1JEfXtTQUxUfSQ/$MTIzNDU2Nzg5$8PhwWanVRnpJAFK4NUjR0w==\")))\n\t})\n\n\tt.Run(\"md5-crypt\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$md5-crypt$TVEiiKNb$SN6/pUaRQS/E8Jh46As2C/\")))\n\t\tassert.Nil(t, hash.CompareMD5Crypt(ctx, []byte(\"test\"), []byte(\"$md5-crypt$TVEiiKNb$SN6/pUaRQS/E8Jh46As2C/\")))\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$md5-crypt$$whuMjZj.HMFoaTaZRRtkO0\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$md5-crypt$xWMlm2eL$GGTOpgZu4p2k6ORprAu3b.\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5-crypt$xWMlm2eL$GGTOpgZu4p2k6ORprAu3b.\")))\n\t\tassert.Nil(t, hash.CompareMD5Crypt(ctx, []byte(\"ory\"), []byte(\"$md5-crypt$xWMlm2eL$GGTOpgZu4p2k6ORprAu3b.\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5-crypt$E7zjruqF$RTglYR1CzBHwwiTk9nVzx1\")))\n\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5-crypt$$\")), hash.ErrMismatchedHashAndPassword)\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$md5-crypt$$$\")))\n\t\t// per crypt(5), `md5crypt` can be run without a salt, but the salt section must still be present\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$md5-crypt$whuMjZj.HMFoaTaZRRtkO0\")), \"md5crypt decode error: provided encoded hash has an invalid format\")\n\t})\n\n\tt.Run(\"sha256-crypt\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha256-crypt$rounds=535000$05R.9KB6UC2kLI3w$Q/zslzx./JjkAVPTwp6th7nW5l7JU91Gte/UmIh.U78\")))\n\t\tassert.Nil(t, hash.CompareSHA256Crypt(ctx, []byte(\"test\"), []byte(\"$sha256-crypt$rounds=535000$05R.9KB6UC2kLI3w$Q/zslzx./JjkAVPTwp6th7nW5l7JU91Gte/UmIh.U78\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha256-crypt$rounds=535000$awpcR7lDlnK/S7WE$vHU7KkQwyjfGz6u4MUi7.lH9htK/l63HloTsX1ZMz.3\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$sha256-crypt$rounds=535000$awpcR7lDlnK/S7WE$vHU7KkQwyjfGz6u4MUi7.lH9htK/l63HloTsX1ZMz.3\")))\n\t\tassert.Nil(t, hash.CompareSHA256Crypt(ctx, []byte(\"ory\"), []byte(\"$sha256-crypt$rounds=535000$awpcR7lDlnK/S7WE$vHU7KkQwyjfGz6u4MUi7.lH9htK/l63HloTsX1ZMz.3\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$sha256-crypt$rounds=535000$T95kH8e37IGVdxzJ$gLeaNa6qRog.bx4Bzqp63ceWItH6nSAal6c3WmT5GHB\")))\n\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$sha256-crypt$$\")), \"shacrypt decode error: provided encoded hash has an invalid format\")\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$sha256-crypt$$$\")))\n\t})\n\n\tt.Run(\"sha512-crypt\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$sha512-crypt$rounds=656000$3LVbIAVxR//cRajw$uuNasMW.RYxlGzIRFU1Was70BPSa933AjxhZIGJdJBOlqJAHlgqa0yuiuq5JHF/ryNGryJkj87G9i3G2HPSXg1\")))\n\t\tassert.Nil(t, hash.CompareSHA512Crypt(ctx, []byte(\"test\"), []byte(\"$sha512-crypt$rounds=656000$3LVbIAVxR//cRajw$uuNasMW.RYxlGzIRFU1Was70BPSa933AjxhZIGJdJBOlqJAHlgqa0yuiuq5JHF/ryNGryJkj87G9i3G2HPSXg1\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$5$rounds=535000$awpcR7lDlnK/S7WE$vHU7KkQwyjfGz6u4MUi7.lH9htK/l63HloTsX1ZMz.3\")))\n\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$sha512-crypt$rounds=656000$0baQbxBrfpKqvizk$Q9cYk1MeNAlECPgpG3jjfNI2DumLqd0yHbxzLdxiX6nsSD5i9n0awcbiCf8R5DzpIYxeBPznPcb1wtzlgUKtH0\")))\n\t\tassert.Nil(t, hash.CompareSHA512Crypt(ctx, []byte(\"ory\"), []byte(\"$sha512-crypt$rounds=656000$0baQbxBrfpKqvizk$Q9cYk1MeNAlECPgpG3jjfNI2DumLqd0yHbxzLdxiX6nsSD5i9n0awcbiCf8R5DzpIYxeBPznPcb1wtzlgUKtH0\")))\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$sha512-crypt$rounds=656000$hNcDLFO63bkYVDZf$Mt9dhH0xqfxWZ6Pu8zXw.Ku5f15IRTweuaDcUc.ObXWGn7B1h8YIWLmArZd8psd2mrUVswCXLAVptmISr.8iI/\")))\n\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$sha512-crypt$$\")), \"shacrypt decode error: provided encoded hash has an invalid format\")\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$sha512-crypt$$$\")))\n\t})\n\n\tt.Run(\"hmac errors\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t//Missing Key\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-md5$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=\")), hash.ErrInvalidHash)\n\t\tassert.Error(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-md5$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=\")))\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-md5$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$\")), hash.ErrMismatchedHashAndPassword)\n\t\tassert.Error(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-md5$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$\")))\n\t\t//Missing Password Hash\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-md5$MTIzNDU=\")), hash.ErrInvalidHash)\n\t\tassert.Error(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-md5$MTIzNDU=\")))\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-md5$$MTIzNDU=\")), hash.ErrMismatchedHashAndPassword)\n\t\tassert.Error(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-md5$$MTIzNDU=\")))\n\t\t//Missing Password Hash and Key\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-md5$\")), hash.ErrInvalidHash)\n\t\tassert.Error(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-md5$\")))\n\t\t//Missing Hash Algorithm\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$MTIzNDU=\")), hash.ErrUnknownHashAlgorithm)\n\t\tassert.Error(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$MTIzNDU=\")))\n\t\t//Missing Invalid Hash Algorithm\n\t\tassert.ErrorIs(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-invalid$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$MTIzNDU=\")), hash.ErrUnknownHashAlgorithm)\n\t\tassert.Error(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-invalid$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$MTIzNDU=\")))\n\n\t})\n\n\tt.Run(\"hmac-md4\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t//Valid\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-md4$MWQ5ZTI4Nzc2Zjg4YmE2MTQ5YjQ0OTMyOGE4NWU4YjA=$MTIzNDU=\")))\n\t\tassert.Nil(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-md4$MWQ5ZTI4Nzc2Zjg4YmE2MTQ5YjQ0OTMyOGE4NWU4YjA=$MTIzNDU=\")))\n\t\t//Wrong Key\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-md4$MWQ5ZTI4Nzc2Zjg4YmE2MTQ5YjQ0OTMyOGE4NWU4YjA=$MTIzNA==\")), hash.ErrMismatchedHashAndPassword)\n\t\t//Different password\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$hmac-md4$MWQ5ZTI4Nzc2Zjg4YmE2MTQ5YjQ0OTMyOGE4NWU4YjA=$MTIzNDU=\")))\n\t})\n\n\tt.Run(\"hmac-md5\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t//Valid\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-md5$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$MTIzNDU=\")))\n\t\tassert.Nil(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-md5$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$MTIzNDU=\")))\n\n\t\t//Wrong Key\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-md5$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$MTIzNA==\")))\n\t\t//Different password\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$hmac-md5$ZmU4Njk3Zjc0MmQwODA0MDVkMTI3MGU2MTYzMzE2Zjk=$MTIzNDU=\")))\n\n\t})\n\n\tt.Run(\"hmac-sha1\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t//Valid\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha1$NDMyNjcxZTUyY2Y2YTBmYjZjZDE2NjQxYjAwNjFiZjAwOGEzNWM5MA==$MTIzNDU=\")))\n\t\tassert.Nil(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-sha1$NDMyNjcxZTUyY2Y2YTBmYjZjZDE2NjQxYjAwNjFiZjAwOGEzNWM5MA==$MTIzNDU=\")))\n\n\t\t//Wrong Key\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha1$NDMyNjcxZTUyY2Y2YTBmYjZjZDE2NjQxYjAwNjFiZjAwOGEzNWM5MA==$MTIzNA==\")))\n\t\t//Different password\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$hmac-sha1$NDMyNjcxZTUyY2Y2YTBmYjZjZDE2NjQxYjAwNjFiZjAwOGEzNWM5MA==$MTIzNDU=\")))\n\n\t})\n\n\tt.Run(\"hmac-sha224\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t//Valid\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha224$YmUwYmYzM2EwNGRlNDE0YjQzNjBhNmIyOThmNmIyYzI4OWQyMzk3MDUwZDFjMzliYjVmMDMyOTQ=$MTIzNDU=\")))\n\t\tassert.Nil(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-sha224$YmUwYmYzM2EwNGRlNDE0YjQzNjBhNmIyOThmNmIyYzI4OWQyMzk3MDUwZDFjMzliYjVmMDMyOTQ=$MTIzNDU=\")))\n\n\t\t//Wrong Key\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha224$YmUwYmYzM2EwNGRlNDE0YjQzNjBhNmIyOThmNmIyYzI4OWQyMzk3MDUwZDFjMzliYjVmMDMyOTQ=$MTIzNA==\")))\n\t\t//Different password\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$hmac-sha224$YmUwYmYzM2EwNGRlNDE0YjQzNjBhNmIyOThmNmIyYzI4OWQyMzk3MDUwZDFjMzliYjVmMDMyOTQ=$MTIzNDU=\")))\n\n\t})\n\n\tt.Run(\"hmac-sha256\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t//Valid\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha256$ZTAzMWJhMWMyOTM4YjFkMjgzZjkxOWExZGY5YWM2NmMxOTJhN2RkNzQ0MzJkNWZkNGFkYTI5OTk0MWJhMTA5Zg==$MTIzNDU=\")))\n\t\tassert.Nil(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-sha256$ZTAzMWJhMWMyOTM4YjFkMjgzZjkxOWExZGY5YWM2NmMxOTJhN2RkNzQ0MzJkNWZkNGFkYTI5OTk0MWJhMTA5Zg==$MTIzNDU=\")))\n\n\t\t//Wrong Key\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha256$ZTAzMWJhMWMyOTM4YjFkMjgzZjkxOWExZGY5YWM2NmMxOTJhN2RkNzQ0MzJkNWZkNGFkYTI5OTk0MWJhMTA5Zg==$MTIzNA==\")))\n\t\t//Different password\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$hmac-sha256$ZTAzMWJhMWMyOTM4YjFkMjgzZjkxOWExZGY5YWM2NmMxOTJhN2RkNzQ0MzJkNWZkNGFkYTI5OTk0MWJhMTA5Zg==$MTIzNDU=\")))\n\n\t})\n\n\tt.Run(\"hmac-sha384\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t//Valid\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha384$ZWEyMGM3NGE4Y2UzMTljNTdjZTlhZGQyYTZjNDE0MGQ4YjMwYWIwOWM4OTRiNWQ4MmZjODlhMzBhMmQzNGE5NmQ0NDY1NWRhYjQ2ZjhiYjBkNTRmYjk5YWZkZTA1MGY1$MTIzNDU=\")))\n\t\tassert.Nil(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-sha384$ZWEyMGM3NGE4Y2UzMTljNTdjZTlhZGQyYTZjNDE0MGQ4YjMwYWIwOWM4OTRiNWQ4MmZjODlhMzBhMmQzNGE5NmQ0NDY1NWRhYjQ2ZjhiYjBkNTRmYjk5YWZkZTA1MGY1$MTIzNDU=\")))\n\n\t\t//Wrong Key\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha384$ZWEyMGM3NGE4Y2UzMTljNTdjZTlhZGQyYTZjNDE0MGQ4YjMwYWIwOWM4OTRiNWQ4MmZjODlhMzBhMmQzNGE5NmQ0NDY1NWRhYjQ2ZjhiYjBkNTRmYjk5YWZkZTA1MGY1$MTIzNA==\")))\n\t\t//Different password\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$hmac-sha384$ZWEyMGM3NGE4Y2UzMTljNTdjZTlhZGQyYTZjNDE0MGQ4YjMwYWIwOWM4OTRiNWQ4MmZjODlhMzBhMmQzNGE5NmQ0NDY1NWRhYjQ2ZjhiYjBkNTRmYjk5YWZkZTA1MGY1$MTIzNDU=\")))\n\n\t})\n\n\tt.Run(\"hmac-sha512\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t//Valid\n\t\tassert.Nil(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha512$OTFmODY0ZTI1NmU0ZjVhYjhiMDViZGFmNGVmNGZmMGVlNTY4ODYwNWJhYTk4MTk2OTgyMzc3NzI1YTc4MzcxMTMzNzZmY2YxYTk5MGMxM2RiZDk2MGFmMmQ1YzRmODdlMGMwYTNkYjcyNjY0NjM4NGE4YzQ2MjNhZDZkN2UxZTE=$MTIzNDU=\")))\n\t\tassert.Nil(t, hash.CompareHMAC(ctx, []byte(\"test\"), []byte(\"$hmac-sha512$OTFmODY0ZTI1NmU0ZjVhYjhiMDViZGFmNGVmNGZmMGVlNTY4ODYwNWJhYTk4MTk2OTgyMzc3NzI1YTc4MzcxMTMzNzZmY2YxYTk5MGMxM2RiZDk2MGFmMmQ1YzRmODdlMGMwYTNkYjcyNjY0NjM4NGE4YzQ2MjNhZDZkN2UxZTE=$MTIzNDU=\")))\n\n\t\t//Wrong Key\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"test\"), []byte(\"$hmac-sha512$OTFmODY0ZTI1NmU0ZjVhYjhiMDViZGFmNGVmNGZmMGVlNTY4ODYwNWJhYTk4MTk2OTgyMzc3NzI1YTc4MzcxMTMzNzZmY2YxYTk5MGMxM2RiZDk2MGFmMmQ1YzRmODdlMGMwYTNkYjcyNjY0NjM4NGE4YzQ2MjNhZDZkN2UxZTE=$MTIzNA==\")))\n\t\t//Different password\n\t\tassert.Error(t, hash.Compare(ctx, []byte(\"ory\"), []byte(\"$hmac-sha512$OTFmODY0ZTI1NmU0ZjVhYjhiMDViZGFmNGVmNGZmMGVlNTY4ODYwNWJhYTk4MTk2OTgyMzc3NzI1YTc4MzcxMTMzNzZmY2YxYTk5MGMxM2RiZDk2MGFmMmQ1YzRmODdlMGMwYTNkYjcyNjY0NjM4NGE4YzQ2MjNhZDZkN2UxZTE=$MTIzNDU=\")))\n\n\t})\n}\n"
  },
  {
    "path": "hydra/fake.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hydra\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/ory/herodot\"\n\thydraclientgo \"github.com/ory/hydra-client-go/v2\"\n)\n\nconst (\n\tFakeInvalidLoginChallenge = \"2e98454e-031b-4870-9ad6-8517df1ce604\"\n\tFakeValidLoginChallenge   = \"5ff59a39-ecc5-467e-bb10-26644c0700ee\"\n\tFakePostLoginURL          = \"https://www.example.com/fake-post-login\"\n)\n\nvar ErrFakeAcceptLoginRequestFailed = errors.New(\"failed to accept login request\")\n\ntype FakeHydra struct {\n\tSkip       bool\n\tRequestURL string\n}\n\nvar _ Hydra = &FakeHydra{}\n\nfunc NewFake() *FakeHydra {\n\treturn &FakeHydra{\n\t\tRequestURL: \"https://www.ory.sh\",\n\t}\n}\n\nfunc (h *FakeHydra) AcceptLoginRequest(_ context.Context, params AcceptLoginRequestParams) (string, error) {\n\tif params.SessionID == \"\" {\n\t\treturn \"\", errors.New(\"session id must not be empty\")\n\t}\n\tswitch params.LoginChallenge {\n\tcase FakeInvalidLoginChallenge:\n\t\treturn \"\", ErrFakeAcceptLoginRequestFailed\n\tcase FakeValidLoginChallenge:\n\t\treturn FakePostLoginURL, nil\n\tdefault:\n\t\tpanic(\"unknown fake login_challenge \" + params.LoginChallenge)\n\t}\n}\n\nfunc (h *FakeHydra) GetLoginRequest(_ context.Context, loginChallenge string) (*hydraclientgo.OAuth2LoginRequest, error) {\n\tswitch loginChallenge {\n\tcase FakeInvalidLoginChallenge:\n\t\treturn nil, herodot.ErrBadRequest.WithReasonf(\"Unable to get OAuth 2.0 Login Challenge.\")\n\tcase FakeValidLoginChallenge:\n\t\treturn &hydraclientgo.OAuth2LoginRequest{\n\t\t\tRequestUrl: h.RequestURL,\n\t\t\tSkip:       h.Skip,\n\t\t}, nil\n\tdefault:\n\t\tpanic(\"unknown fake login_challenge \" + loginChallenge)\n\t}\n}\n"
  },
  {
    "path": "hydra/hydra.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hydra\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\thydraclientgo \"github.com/ory/hydra-client-go/v2\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/session\"\n)\n\ntype (\n\thydraDependencies interface {\n\t\tconfig.Provider\n\t\thttpx.ClientProvider\n\t}\n\tProvider interface {\n\t\tHydra() Hydra\n\t}\n\tAcceptLoginRequestParams struct {\n\t\tLoginChallenge        string\n\t\tIdentityID            string\n\t\tSessionID             string\n\t\tAuthenticationMethods session.AuthenticationMethods\n\t}\n\tHydra interface {\n\t\tAcceptLoginRequest(ctx context.Context, params AcceptLoginRequestParams) (string, error)\n\t\tGetLoginRequest(ctx context.Context, loginChallenge string) (*hydraclientgo.OAuth2LoginRequest, error)\n\t}\n\tDefaultHydra struct {\n\t\td hydraDependencies\n\t}\n)\n\nfunc NewDefaultHydra(d hydraDependencies) *DefaultHydra {\n\treturn &DefaultHydra{\n\t\td: d,\n\t}\n}\n\nfunc GetLoginChallengeID(conf *config.Config, r *http.Request) (sqlxx.NullString, error) {\n\tif !r.URL.Query().Has(\"login_challenge\") {\n\t\treturn \"\", nil\n\t} else if conf.OAuth2ProviderURL(r.Context()) == nil {\n\t\treturn \"\", errors.WithStack(herodot.ErrMisconfiguration.WithReason(\"refusing to parse login_challenge query parameter because \" + config.ViperKeyOAuth2ProviderURL + \" is invalid or unset\"))\n\t}\n\n\tloginChallenge := r.URL.Query().Get(\"login_challenge\")\n\tif loginChallenge == \"\" {\n\t\treturn \"\", errors.WithStack(herodot.ErrBadRequest.WithReason(\"the login_challenge parameter is present but empty\"))\n\t}\n\n\treturn sqlxx.NullString(loginChallenge), nil\n}\n\nfunc (h *DefaultHydra) getAdminURL(ctx context.Context) (string, error) {\n\tu := h.d.Config().OAuth2ProviderURL(ctx)\n\tif u == nil {\n\t\treturn \"\", errors.WithStack(herodot.ErrMisconfiguration.WithReason(config.ViperKeyOAuth2ProviderURL + \" is not configured\"))\n\t}\n\treturn u.String(), nil\n}\n\nfunc (h *DefaultHydra) getAdminAPIClient(ctx context.Context) (hydraclientgo.OAuth2API, error) {\n\turl, err := h.getAdminURL(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconfiguration := hydraclientgo.NewConfiguration()\n\tconfiguration.Servers = hydraclientgo.ServerConfigurations{{URL: url}}\n\n\tclient := h.d.HTTPClient(ctx).StandardClient()\n\tif header := h.d.Config().OAuth2ProviderHeader(ctx); header != nil {\n\t\tclient.Transport = httpx.WrapTransportWithHeader(client.Transport, header)\n\t}\n\n\tconfiguration.HTTPClient = client\n\treturn hydraclientgo.NewAPIClient(configuration).OAuth2API, nil\n}\n\nfunc (h *DefaultHydra) AcceptLoginRequest(ctx context.Context, params AcceptLoginRequestParams) (string, error) {\n\tremember := h.d.Config().SessionPersistentCookie(ctx)\n\trememberFor := int64(h.d.Config().SessionLifespan(ctx) / time.Second)\n\n\talr := hydraclientgo.NewAcceptOAuth2LoginRequest(params.IdentityID)\n\talr.IdentityProviderSessionId = &params.SessionID\n\talr.Remember = &remember\n\talr.RememberFor = &rememberFor\n\talr.Amr = []string{}\n\tfor _, r := range params.AuthenticationMethods {\n\t\talr.Amr = append(alr.Amr, string(r.Method))\n\t}\n\n\taa, err := h.getAdminAPIClient(ctx)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tresp, r, err := aa.AcceptOAuth2LoginRequest(ctx).LoginChallenge(params.LoginChallenge).AcceptOAuth2LoginRequest(*alr).Execute()\n\tif err != nil {\n\t\tinnerErr := herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"Unable to accept OAuth 2.0 Login Challenge.\")\n\t\tif r != nil {\n\t\t\tinnerErr = innerErr.\n\t\t\t\tWithDetail(\"status_code\", r.StatusCode).\n\t\t\t\tWithDebug(err.Error())\n\t\t}\n\n\t\tif openApiErr := new(hydraclientgo.GenericOpenAPIError); errors.As(err, &openApiErr) {\n\t\t\tswitch oauth2Err := openApiErr.Model().(type) {\n\t\t\tcase hydraclientgo.ErrorOAuth2:\n\t\t\t\tinnerErr = innerErr.WithDetail(\"oauth2_error_hint\", oauth2Err.GetErrorHint())\n\t\t\tcase *hydraclientgo.ErrorOAuth2:\n\t\t\t\tinnerErr = innerErr.WithDetail(\"oauth2_error_hint\", oauth2Err.GetErrorHint())\n\t\t\t}\n\t\t}\n\n\t\treturn \"\", errors.WithStack(innerErr)\n\t}\n\n\treturn resp.RedirectTo, nil\n}\n\nfunc (h *DefaultHydra) GetLoginRequest(ctx context.Context, loginChallenge string) (*hydraclientgo.OAuth2LoginRequest, error) {\n\tif loginChallenge == \"\" {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReason(\"invalid login_challenge\"))\n\t}\n\n\taa, err := h.getAdminAPIClient(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\thlr, r, err := aa.GetOAuth2LoginRequest(ctx).LoginChallenge(loginChallenge).Execute()\n\tif err != nil {\n\t\tvar innerErr *herodot.DefaultError\n\t\tif r == nil || r.StatusCode >= 500 {\n\t\t\tinnerErr = &herodot.ErrInternalServerError\n\t\t} else {\n\t\t\tinnerErr = &herodot.ErrBadRequest\n\t\t}\n\t\tinnerErr = innerErr.WithReasonf(\"Unable to get OAuth 2.0 Login Challenge.\")\n\t\tif r != nil {\n\t\t\tinnerErr = innerErr.\n\t\t\t\tWithDetail(\"status_code\", r.StatusCode).\n\t\t\t\tWithDebug(err.Error())\n\t\t}\n\n\t\tif openApiErr := new(hydraclientgo.GenericOpenAPIError); errors.As(err, &openApiErr) {\n\t\t\tswitch oauth2Err := openApiErr.Model().(type) {\n\t\t\tcase hydraclientgo.ErrorOAuth2:\n\t\t\t\tinnerErr = innerErr.WithDetail(\"oauth2_error_hint\", oauth2Err.GetErrorHint())\n\t\t\tcase *hydraclientgo.ErrorOAuth2:\n\t\t\t\tinnerErr = innerErr.WithDetail(\"oauth2_error_hint\", oauth2Err.GetErrorHint())\n\t\t\t}\n\t\t}\n\n\t\treturn nil, errors.WithStack(innerErr)\n\t}\n\n\treturn hlr, nil\n}\n"
  },
  {
    "path": "hydra/hydra_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hydra_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc requestFromChallenge(s string) *http.Request {\n\treturn &http.Request{URL: urlx.ParseOrPanic(\"https://hydra?login_challenge=\" + s)}\n}\n\nfunc TestGetLoginChallengeID(t *testing.T) {\n\tuuidChallenge := \"b346a452-e8fb-4828-8ef8-a4dbc98dc23a\"\n\tblobChallenge := \"1337deadbeefcafe\"\n\tdefaultConfig := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation())\n\tconfigWithHydra := config.MustNew(t, logrusx.New(\"\", \"\"), &contextx.Default{}, configx.SkipValidation(), configx.WithValues(map[string]interface{}{\n\t\tconfig.ViperKeyOAuth2ProviderURL: \"https://hydra\",\n\t}))\n\n\ttype args struct {\n\t\tconf *config.Config\n\t\tr    *http.Request\n\t}\n\ttests := []struct {\n\t\tname      string\n\t\targs      args\n\t\twant      string\n\t\tassertErr assert.ErrorAssertionFunc\n\t}{\n\t\t{\n\t\t\tname: \"no login challenge; hydra is not configured\",\n\t\t\targs: args{\n\t\t\t\tconf: defaultConfig,\n\t\t\t\tr:    &http.Request{URL: urlx.ParseOrPanic(\"https://hydra\")},\n\t\t\t},\n\t\t\twant:      \"\",\n\t\t\tassertErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tname: \"no login challenge; hydra is configured\",\n\t\t\targs: args{\n\t\t\t\tconf: configWithHydra,\n\t\t\t\tr:    &http.Request{URL: urlx.ParseOrPanic(\"https://hydra\")},\n\t\t\t},\n\t\t\twant:      \"\",\n\t\t\tassertErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tname: \"empty login challenge; hydra is configured\",\n\t\t\targs: args{\n\t\t\t\tconf: configWithHydra,\n\t\t\t\tr:    requestFromChallenge(\"\"),\n\t\t\t},\n\t\t\twant:      \"\",\n\t\t\tassertErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"login_challenge is present; Hydra is not configured\",\n\t\t\targs: args{\n\t\t\t\tconf: defaultConfig,\n\t\t\t\tr:    requestFromChallenge(uuidChallenge),\n\t\t\t},\n\t\t\twant:      \"\",\n\t\t\tassertErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"login_challenge is present; hydra is configured\",\n\t\t\targs: args{\n\t\t\t\tconf: configWithHydra,\n\t\t\t\tr:    requestFromChallenge(uuidChallenge),\n\t\t\t},\n\t\t\twant:      uuidChallenge,\n\t\t\tassertErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tname: \"login_challenge is present & non-uuid; hydra is configured\",\n\t\t\targs: args{\n\t\t\t\tconf: configWithHydra,\n\t\t\t\tr:    requestFromChallenge(blobChallenge),\n\t\t\t},\n\t\t\twant:      blobChallenge,\n\t\t\tassertErr: assert.NoError,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := hydra.GetLoginChallengeID(tt.args.conf, tt.args.r)\n\t\t\ttt.assertErr(t, err)\n\t\t\tassert.Equal(t, sqlxx.NullString(tt.want), got)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=PATCH_should_allow_to_update_credential_password-endpoint=admin.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"c6eaf1da-4c4e-5da0-aa91-77299464d869@ory.sh\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"foo\",\n        \"some-random-key\": \" some-random-value\"\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"c6eaf1da-4c4e-5da0-aa91-77299464d869@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=PATCH_should_allow_to_update_credential_password-endpoint=public.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"c6eaf1da-4c4e-5da0-aa91-77299464d869@ory.sh\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"foo\",\n        \"some-random-key\": \" some-random-value\"\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"c6eaf1da-4c4e-5da0-aa91-77299464d869@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=PATCH_should_allow_to_update_credential_password.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"c6eaf1da-4c4e-5da0-aa91-77299464d869@ory.sh\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"secret\",\n        \"some-random-key\": \" some-random-value\"\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"c6eaf1da-4c4e-5da0-aa91-77299464d869@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_cleartext_password_and_oidc_credentials.json",
    "content": "{\n  \"credentials\": {\n    \"oidc\": {\n      \"type\": \"oidc\",\n      \"config\": {\n        \"providers\": [\n          {\n            \"subject\": \"import-2\",\n            \"provider\": \"google\",\n            \"initial_id_token\": \"\",\n            \"initial_access_token\": \"\",\n            \"initial_refresh_token\": \"\"\n          },\n          {\n            \"subject\": \"import-2\",\n            \"provider\": \"github\",\n            \"initial_id_token\": \"\",\n            \"initial_access_token\": \"\",\n            \"initial_refresh_token\": \"\"\n          }\n        ]\n      },\n      \"version\": 0\n    },\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-2@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    },\n    \"saml\": {\n      \"type\": \"saml\",\n      \"identifiers\": [\n        \"okta:import-saml-2\",\n        \"onelogin:import-saml-2\"\n      ],\n      \"config\": {\n        \"providers\": [\n          {\n            \"subject\": \"import-saml-2\",\n            \"provider\": \"okta\",\n            \"initial_id_token\": \"\",\n            \"initial_access_token\": \"\",\n            \"initial_refresh_token\": \"\"\n          },\n          {\n            \"subject\": \"import-saml-2\",\n            \"provider\": \"onelogin\",\n            \"initial_id_token\": \"\",\n            \"initial_access_token\": \"\",\n            \"initial_refresh_token\": \"\"\n          }\n        ]\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-2@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=SSHA.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-6@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-6@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=SSHA256.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-7@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-7@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=SSHA512.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-8@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-8@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=argon2i.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-2@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-2@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=argon2id.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-3@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-3@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=bcrypt2.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-1@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-1@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=hmac.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-9@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-9@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=md5.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-5@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-5@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=pkbdf2.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-0@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-0@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_hashed_passwords-hash=scrypt.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-hash-4@ory.sh\"\n      ],\n      \"config\": {\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-hash-4@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_not-normalized_email.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"uppercased@ory.sh\"\n      ],\n      \"config\": {},\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"customer\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"UpperCased@ory.sh\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"value\": \"uppercased@ory.sh\",\n      \"verified\": true,\n      \"via\": \"email\",\n      \"status\": \"completed\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"value\": \"uppercased@ory.sh\",\n      \"via\": \"email\"\n    }\n  ],\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_organization_oidc_and_saml_credentials.json",
    "content": "{\n  \"credentials\": {\n    \"oidc\": {\n      \"type\": \"oidc\",\n      \"config\": {\n        \"providers\": [\n          {\n            \"subject\": \"import-org-3\",\n            \"provider\": \"google\",\n            \"initial_id_token\": \"\",\n            \"initial_access_token\": \"\",\n            \"initial_refresh_token\": \"\",\n            \"organization\": \"ad6a7dac-4eef-4f09-8e58-c099c14b6c36\"\n          },\n          {\n            \"subject\": \"import-org-3\",\n            \"provider\": \"github\",\n            \"initial_id_token\": \"\",\n            \"initial_access_token\": \"\",\n            \"initial_refresh_token\": \"\",\n            \"organization\": \"ad6a7dac-4eef-4f09-8e58-c099c14b6c36\"\n          }\n        ]\n      },\n      \"version\": 0\n    },\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-3@ory.sh\"\n      ],\n      \"config\": {},\n      \"version\": 0\n    },\n    \"saml\": {\n      \"type\": \"saml\",\n      \"identifiers\": [\n        \"okta:import-saml-org-3\",\n        \"onelogin:import-saml-org-3\"\n      ],\n      \"config\": {\n        \"providers\": [\n          {\n            \"subject\": \"import-saml-org-3\",\n            \"provider\": \"okta\",\n            \"initial_id_token\": \"\",\n            \"initial_access_token\": \"\",\n            \"initial_refresh_token\": \"\",\n            \"organization\": \"ad6a7dac-4eef-4f09-8e58-c099c14b6c36\"\n          },\n          {\n            \"subject\": \"import-saml-org-3\",\n            \"provider\": \"onelogin\",\n            \"initial_id_token\": \"\",\n            \"initial_access_token\": \"\",\n            \"initial_refresh_token\": \"\",\n            \"organization\": \"ad6a7dac-4eef-4f09-8e58-c099c14b6c36\"\n          }\n        ]\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-3@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-with_password_migration_hook_enabled.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"pw-migration-hook@ory.sh\"\n      ],\n      \"config\": {\n        \"use_password_migration_hook\": true\n      },\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"pw-migration-hook@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-without_any_credentials.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"import-1@ory.sh\"\n      ],\n      \"config\": {},\n      \"version\": 0\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"import-1@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_be_able_to_import_users-without_traits.json",
    "content": "{\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {},\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_delete_credential_of_a_specific_user_and_no_longer_be_able_to_retrieve_it-type=remove_webauthn_passwordless_and_multiple_fido_mfa_type-admin.json",
    "content": "{\n  \"credentials\": {\n    \"webauthn\": {\n      \"type\": \"webauthn\",\n      \"identifiers\": [],\n      \"config\": {\n        \"credentials\": [\n          {\n            \"public_key\": \"cFFFQ0F5WWdBU0ZZSU1KTFFoSnhRUnpobktQVGNQQ1VPRE9teFlEWW8yb2JybTliaHA1bHZTWjNJbGdnWGpoWnZKYVBVcUY5UFhxWnFUZFdZUFI3UitiMm4vV2krSXhLS1hzUzRyVT0=\",\n            \"attestation_type\": \"none\",\n            \"authenticator\": {\n              \"aaguid\": \"cmM0QUFqVzh4Z3BraXdzbDhmQlZBdz09\",\n              \"sign_count\": 0,\n              \"clone_warning\": false\n            },\n            \"display_name\": \"test\",\n            \"added_at\": \"2022-12-16T14:11:55Z\",\n            \"is_passwordless\": true\n          },\n          {\n            \"public_key\": \"cFFFQ0F5WWdBU0ZZSU1KTFFoSnhRUnpobktQVGNQQ1VPRE9teFlEWW8yb2JybTliaHA1bHZTWjNJbGdnWGpoWnZKYVBVcUY5UFhxWnFUZFdZUFI3UitiMm4vV2krSXhLS1hzUzRyVT0=\",\n            \"attestation_type\": \"none\",\n            \"authenticator\": {\n              \"aaguid\": \"cmM0QUFqVzh4Z3BraXdzbDhmQlZBdz09\",\n              \"sign_count\": 0,\n              \"clone_warning\": false\n            },\n            \"display_name\": \"test\",\n            \"added_at\": \"2022-12-16T14:11:55Z\",\n            \"is_passwordless\": true\n          }\n        ],\n        \"user_handle\": \"RWY1SmlNcE1Sd3V6YXVXcy85SjBnUT09\"\n      },\n      \"version\": 1\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {},\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_delete_credential_of_a_specific_user_and_no_longer_be_able_to_retrieve_it-type=remove_webauthn_passwordless_and_multiple_fido_mfa_type-public.json",
    "content": "{\n  \"credentials\": {\n    \"webauthn\": {\n      \"type\": \"webauthn\",\n      \"identifiers\": [],\n      \"config\": {\n        \"credentials\": [\n          {\n            \"public_key\": \"cFFFQ0F5WWdBU0ZZSU1KTFFoSnhRUnpobktQVGNQQ1VPRE9teFlEWW8yb2JybTliaHA1bHZTWjNJbGdnWGpoWnZKYVBVcUY5UFhxWnFUZFdZUFI3UitiMm4vV2krSXhLS1hzUzRyVT0=\",\n            \"attestation_type\": \"none\",\n            \"authenticator\": {\n              \"aaguid\": \"cmM0QUFqVzh4Z3BraXdzbDhmQlZBdz09\",\n              \"sign_count\": 0,\n              \"clone_warning\": false\n            },\n            \"display_name\": \"test\",\n            \"added_at\": \"2022-12-16T14:11:55Z\",\n            \"is_passwordless\": true\n          },\n          {\n            \"public_key\": \"cFFFQ0F5WWdBU0ZZSU1KTFFoSnhRUnpobktQVGNQQ1VPRE9teFlEWW8yb2JybTliaHA1bHZTWjNJbGdnWGpoWnZKYVBVcUY5UFhxWnFUZFdZUFI3UitiMm4vV2krSXhLS1hzUzRyVT0=\",\n            \"attestation_type\": \"none\",\n            \"authenticator\": {\n              \"aaguid\": \"cmM0QUFqVzh4Z3BraXdzbDhmQlZBdz09\",\n              \"sign_count\": 0,\n              \"clone_warning\": false\n            },\n            \"display_name\": \"test\",\n            \"added_at\": \"2022-12-16T14:11:55Z\",\n            \"is_passwordless\": true\n          }\n        ],\n        \"user_handle\": \"RWY1SmlNcE1Sd3V6YXVXcy85SjBnUT09\"\n      },\n      \"version\": 1\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {},\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_delete_credential_of_a_specific_user_and_no_longer_be_able_to_retrieve_it-type=remove_webauthn_passwordless_type-admin.json",
    "content": "{\n  \"credentials\": {\n    \"webauthn\": {\n      \"type\": \"webauthn\",\n      \"identifiers\": [],\n      \"config\": {\n        \"credentials\": [\n          {\n            \"added_at\": \"2022-12-16T14:11:55Z\",\n            \"public_key\": \"pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU=\",\n            \"display_name\": \"test\",\n            \"authenticator\": {\n              \"aaguid\": \"rc4AAjW8xgpkiwsl8fBVAw==\",\n              \"sign_count\": 0,\n              \"clone_warning\": false\n            },\n            \"is_passwordless\": true,\n            \"attestation_type\": \"none\"\n          }\n        ],\n        \"user_handle\": \"Ef5JiMpMRwuzauWs/9J0gQ==\"\n      },\n      \"version\": 1\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {},\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_delete_credential_of_a_specific_user_and_no_longer_be_able_to_retrieve_it-type=remove_webauthn_passwordless_type-public.json",
    "content": "{\n  \"credentials\": {\n    \"webauthn\": {\n      \"type\": \"webauthn\",\n      \"identifiers\": [],\n      \"config\": {\n        \"credentials\": [\n          {\n            \"added_at\": \"2022-12-16T14:11:55Z\",\n            \"public_key\": \"pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU=\",\n            \"display_name\": \"test\",\n            \"authenticator\": {\n              \"aaguid\": \"rc4AAjW8xgpkiwsl8fBVAw==\",\n              \"sign_count\": 0,\n              \"clone_warning\": false\n            },\n            \"is_passwordless\": true,\n            \"attestation_type\": \"none\"\n          }\n        ],\n        \"user_handle\": \"Ef5JiMpMRwuzauWs/9J0gQ==\"\n      },\n      \"version\": 1\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {},\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-case=should_list_all_identities_with_credentials-include_credential=oidc_should_include_OIDC_credentials_config.json",
    "content": "\"{\\\"providers\\\":[{\\\"initial_id_token\\\":\\\"id_token0\\\",\\\"initial_access_token\\\":\\\"access_token0\\\",\\\"initial_refresh_token\\\":\\\"refresh_token0\\\",\\\"subject\\\":\\\"foo\\\",\\\"provider\\\":\\\"bar\\\"},{\\\"initial_id_token\\\":\\\"id_token1\\\",\\\"initial_access_token\\\":\\\"access_token1\\\",\\\"initial_refresh_token\\\":\\\"refresh_token1\\\",\\\"subject\\\":\\\"baz\\\",\\\"provider\\\":\\\"zab\\\"}]}\"\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-suite=PATCH_identities-case=success-assert=identity_0.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"version\": 0\n    }\n  },\n  \"external_id\": \"external-id-Batch-Import-0\",\n  \"metadata_admin\": {\n    \"admin-0\": \"admin\"\n  },\n  \"metadata_public\": {\n    \"public-0\": \"public\"\n  },\n  \"organization_id\": null,\n  \"schema_id\": \"multiple_emails\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"emails\": [\n      \"Batch-Import-0-0@ory.sh\",\n      \"Batch-Import-0-1@ory.sh\",\n      \"Batch-Import-0-2@ory.sh\",\n      \"Batch-Import-0-3@ory.sh\"\n    ],\n    \"username\": \"Batch-Import-0-0@ory.sh\"\n  }\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-suite=PATCH_identities-case=success-assert=identity_1.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"version\": 0\n    }\n  },\n  \"metadata_admin\": {\n    \"admin-1\": \"admin\"\n  },\n  \"metadata_public\": {\n    \"public-1\": \"public\"\n  },\n  \"organization_id\": null,\n  \"schema_id\": \"multiple_emails\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"emails\": [\n      \"batch-import-1-0@ory.sh\",\n      \"batch-import-1-1@ory.sh\",\n      \"batch-import-1-2@ory.sh\",\n      \"batch-import-1-3@ory.sh\"\n    ],\n    \"username\": \"batch-import-1-0@ory.sh\"\n  }\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-suite=PATCH_identities-case=success-assert=identity_2.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"version\": 0\n    }\n  },\n  \"external_id\": \"external-id-batch-import-2\",\n  \"metadata_admin\": {\n    \"admin-2\": \"admin\"\n  },\n  \"metadata_public\": {\n    \"public-2\": \"public\"\n  },\n  \"organization_id\": null,\n  \"schema_id\": \"multiple_emails\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"emails\": [\n      \"batch-import-2-0@ory.sh\",\n      \"batch-import-2-1@ory.sh\",\n      \"batch-import-2-2@ory.sh\",\n      \"batch-import-2-3@ory.sh\"\n    ],\n    \"username\": \"batch-import-2-0@ory.sh\"\n  }\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-suite=PATCH_identities-case=success-assert=identity_3.json",
    "content": "{\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"version\": 0\n    }\n  },\n  \"metadata_admin\": {\n    \"admin-3\": \"admin\"\n  },\n  \"metadata_public\": {\n    \"public-3\": \"public\"\n  },\n  \"organization_id\": null,\n  \"schema_id\": \"multiple_emails\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"emails\": [\n      \"batch-import-3-0@ory.sh\",\n      \"batch-import-3-1@ory.sh\",\n      \"batch-import-3-2@ory.sh\",\n      \"batch-import-3-3@ory.sh\"\n    ],\n    \"username\": \"batch-import-3-0@ory.sh\"\n  }\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-suite=create_and_update-case=should_get_identity_with_credentials-case=should_get_identity_with_password_and_webauthn_credentials_included.json",
    "content": "{\n  \"credentials\": {\n    \"oidc\": {\n      \"type\": \"oidc\",\n      \"identifiers\": [\n        \"bar\",\n        \"baz\"\n      ],\n      \"version\": 0\n    },\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"bar\",\n        \"zab\"\n      ],\n      \"config\": {\n        \"some\": \"secret\"\n      },\n      \"version\": 0\n    },\n    \"webauthn\": {\n      \"type\": \"webauthn\",\n      \"identifiers\": [\n        \"bar\",\n        \"foo\"\n      ],\n      \"config\": {\n        \"some\": \"secret\",\n        \"user_handle\": \"rVIFaWRcTTuQLkXFmQWpgA==\"\n      },\n      \"version\": 1\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {},\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-suite=create_and_update-case=should_get_identity_with_credentials-case=should_get_identity_with_password_credentials_included.json",
    "content": "{\n  \"credentials\": {\n    \"oidc\": {\n      \"type\": \"oidc\",\n      \"identifiers\": [\n        \"bar\",\n        \"baz\"\n      ],\n      \"version\": 0\n    },\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"bar\",\n        \"zab\"\n      ],\n      \"config\": {\n        \"some\": \"secret\"\n      },\n      \"version\": 0\n    },\n    \"webauthn\": {\n      \"type\": \"webauthn\",\n      \"identifiers\": [\n        \"bar\",\n        \"foo\"\n      ],\n      \"version\": 1\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {},\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestHandler-suite=create_and_update-case=should_get_identity_with_credentials-case=should_get_identity_without_credentials_included.json",
    "content": "{\n  \"credentials\": {\n    \"oidc\": {\n      \"type\": \"oidc\",\n      \"identifiers\": [\n        \"bar\",\n        \"baz\"\n      ],\n      \"version\": 0\n    },\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"bar\",\n        \"zab\"\n      ],\n      \"version\": 0\n    },\n    \"webauthn\": {\n      \"type\": \"webauthn\",\n      \"identifiers\": [\n        \"bar\",\n        \"foo\"\n      ],\n      \"version\": 1\n    }\n  },\n  \"schema_id\": \"default\",\n  \"state\": \"active\",\n  \"traits\": {},\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-OIDC_new_credential_with_organization.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"github:12345\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"12345\",\n        \"provider\": \"github\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\",\n        \"organization\": \"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-OIDC_new_credential_without_organization.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"github:12345\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"12345\",\n        \"provider\": \"github\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-OIDC_update_credential_with_organization.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"google:67890\",\n    \"github:12345\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"67890\",\n        \"provider\": \"google\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      },\n      {\n        \"subject\": \"12345\",\n        \"provider\": \"github\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\",\n        \"organization\": \"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-OIDC_update_credential_without_organization.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"google:67890\",\n    \"github:12345\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"67890\",\n        \"provider\": \"google\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      },\n      {\n        \"subject\": \"12345\",\n        \"provider\": \"github\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-OIDC_update_with_multiple_providers.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"google:67890\",\n    \"github:12345\",\n    \"gitlab:abcdef\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"67890\",\n        \"provider\": \"google\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      },\n      {\n        \"subject\": \"12345\",\n        \"provider\": \"github\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\",\n        \"organization\": \"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"\n      },\n      {\n        \"subject\": \"abcdef\",\n        \"provider\": \"gitlab\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-SAML_new_credential_with_organization.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"okta:user123\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"user123\",\n        \"provider\": \"okta\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\",\n        \"organization\": \"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-SAML_new_credential_without_organization.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"okta:user123\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"user123\",\n        \"provider\": \"okta\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-SAML_update_credential_with_organization.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"onelogin:user456\",\n    \"okta:user123\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"user456\",\n        \"provider\": \"onelogin\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      },\n      {\n        \"subject\": \"user123\",\n        \"provider\": \"okta\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\",\n        \"organization\": \"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-SAML_update_credential_without_organization.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"onelogin:user456\",\n    \"okta:user123\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"user456\",\n        \"provider\": \"onelogin\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      },\n      {\n        \"subject\": \"user123\",\n        \"provider\": \"okta\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestImportCredentials-SAML_update_with_multiple_providers.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"onelogin:user456\",\n    \"okta:user123\",\n    \"auth0:user789\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"user456\",\n        \"provider\": \"onelogin\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      },\n      {\n        \"subject\": \"user123\",\n        \"provider\": \"okta\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\",\n        \"organization\": \"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"\n      },\n      {\n        \"subject\": \"user789\",\n        \"provider\": \"auth0\",\n        \"initial_id_token\": \"\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestMarshalIdentityWithAll.json",
    "content": "{\n  \"password\": {\n    \"type\": \"password\",\n    \"identifiers\": null,\n    \"config\": {\n      \"some\": \"secret\"\n    },\n    \"version\": 0,\n    \"created_at\": \"0001-01-01T00:00:00Z\",\n    \"updated_at\": \"0001-01-01T00:00:00Z\"\n  }\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=0.json",
    "content": "{\n  \"type\": \"password\",\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=1.json",
    "content": "{\n  \"type\": \"password\",\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=10.json",
    "content": "{\n  \"type\": \"code\",\n  \"config\": {\n    \"addresses\": [\n      {\n        \"channel\": \"sms\",\n        \"address\": \"+4917667111638\"\n      },\n      {\n        \"channel\": \"email\",\n        \"address\": \"foo@ory.sh\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=11.json",
    "content": "{\n  \"type\": \"code\",\n  \"config\": {\n    \"addresses\": [\n      {\n        \"channel\": \"sms\",\n        \"address\": \"+4917667111638\"\n      },\n      {\n        \"channel\": \"email\",\n        \"address\": \"foo@ory.sh\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=12.json",
    "content": "{\n  \"type\": \"code\",\n  \"config\": {\n    \"addresses\": [\n      {\n        \"channel\": \"sms\",\n        \"address\": \"+4917667111638\"\n      },\n      {\n        \"channel\": \"email\",\n        \"address\": \"bar@ory.sh\"\n      },\n      {\n        \"channel\": \"email\",\n        \"address\": \"foo@ory.sh\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=13.json",
    "content": "{\n  \"type\": \"code\",\n  \"config\": {\n    \"addresses\": [\n      {\n        \"channel\": \"email\",\n        \"address\": \"bar@ory.sh\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=2.json",
    "content": "{\n  \"type\": \"webauthn\",\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=3.json",
    "content": "{\n  \"type\": \"password\",\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=4.json",
    "content": "{\n  \"type\": \"webauthn\",\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=5.json",
    "content": "{\n  \"type\": \"webauthn\",\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=6.json",
    "content": "{\n  \"type\": \"code\",\n  \"config\": {\n    \"addresses\": [\n      {\n        \"channel\": \"email\",\n        \"address\": \"foo@ory.sh\"\n      }\n    ]\n  },\n  \"version\": 1,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=7.json",
    "content": "{\n  \"type\": \"code\",\n  \"config\": {\n    \"addresses\": [\n      {\n        \"channel\": \"email\",\n        \"address\": \"foo@ory.sh\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=8.json",
    "content": "{\n  \"type\": \"code\",\n  \"config\": {\n    \"addresses\": [\n      {\n        \"channel\": \"email\",\n        \"address\": \"foo@ory.sh\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestSchemaExtensionCredentials-case=9.json",
    "content": "{\n  \"type\": \"code\",\n  \"config\": {\n    \"addresses\": [\n      {\n        \"channel\": \"sms\",\n        \"address\": \"+4917667111638\"\n      },\n      {\n        \"channel\": \"email\",\n        \"address\": \"foo@ory.sh\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestToNode.json",
    "content": "{\n  \"type\": \"text\",\n  \"group\": \"lookup_secret\",\n  \"attributes\": {\n    \"text\": {\n      \"id\": 1050015,\n      \"text\": \"used, bar, baz, used\",\n      \"type\": \"info\",\n      \"context\": {\n        \"secrets\": [\n          {\n            \"id\": 1050014,\n            \"text\": \"Secret was used at 2021-08-17 11:32:38 +0000 UTC\",\n            \"type\": \"info\",\n            \"context\": {\n              \"used_at\": \"2021-08-17T11:32:38Z\",\n              \"used_at_unix\": 1629199958\n            }\n          },\n          {\n            \"id\": 1050009,\n            \"text\": \"bar\",\n            \"type\": \"info\",\n            \"context\": {\n              \"secret\": \"bar\"\n            }\n          },\n          {\n            \"id\": 1050009,\n            \"text\": \"baz\",\n            \"type\": \"info\",\n            \"context\": {\n              \"secret\": \"baz\"\n            }\n          },\n          {\n            \"id\": 1050014,\n            \"text\": \"Secret was used at 2021-08-17 11:32:48 +0000 UTC\",\n            \"type\": \"info\",\n            \"context\": {\n              \"used_at\": \"2021-08-17T11:32:48Z\",\n              \"used_at_unix\": 1629199968\n            }\n          }\n        ]\n      }\n    },\n    \"id\": \"lookup_secret_codes\",\n    \"node_type\": \"text\"\n  },\n  \"messages\": [],\n  \"meta\": {\n    \"label\": {\n      \"id\": 1050010,\n      \"text\": \"These are your back up recovery codes. Please keep them in a safe place!\",\n      \"type\": \"info\"\n    }\n  }\n}\n"
  },
  {
    "path": "identity/.snapshots/TestUpgradeCredentials-empty_credentials.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"schema_id\": \"\",\n  \"schema_url\": \"\",\n  \"state\": \"\",\n  \"traits\": null,\n  \"metadata_public\": null,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestUpgradeCredentials-type=code-from=v0_with_correct_value.json",
    "content": "{\n  \"id\": \"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\",\n  \"credentials\": {\n    \"code\": {\n      \"type\": \"code\",\n      \"identifiers\": [\n        \"hi@example.org\"\n      ],\n      \"config\": {\n        \"addresses\": [\n          {\n            \"channel\": \"email\",\n            \"address\": \"hi@example.org\"\n          }\n        ]\n      },\n      \"version\": 1,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"\",\n  \"schema_url\": \"\",\n  \"state\": \"\",\n  \"traits\": null,\n  \"metadata_public\": null,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestUpgradeCredentials-type=code-from=v0_with_email_empty_space_value-with_one_identifier.json",
    "content": "{\n  \"id\": \"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\",\n  \"credentials\": {\n    \"code\": {\n      \"type\": \"code\",\n      \"identifiers\": [\n        \"hi@example.org\"\n      ],\n      \"config\": {\n        \"addresses\": [\n          {\n            \"channel\": \"email\",\n            \"address\": \"hi@example.org\"\n          }\n        ]\n      },\n      \"version\": 1,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"\",\n  \"schema_url\": \"\",\n  \"state\": \"\",\n  \"traits\": null,\n  \"metadata_public\": null,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestUpgradeCredentials-type=code-from=v0_with_email_empty_space_value-with_two_identifiers.json",
    "content": "{\n  \"id\": \"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\",\n  \"credentials\": {\n    \"code\": {\n      \"type\": \"code\",\n      \"identifiers\": [\n        \"foo@example.org\",\n        \"bar@example.org\"\n      ],\n      \"config\": {\n        \"addresses\": [\n          {\n            \"channel\": \"email\",\n            \"address\": \"foo@example.org\"\n          },\n          {\n            \"channel\": \"email\",\n            \"address\": \"bar@example.org\"\n          }\n        ]\n      },\n      \"version\": 1,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"\",\n  \"schema_url\": \"\",\n  \"state\": \"\",\n  \"traits\": null,\n  \"metadata_public\": null,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestUpgradeCredentials-type=code-from=v0_with_empty_value.json",
    "content": "{\n  \"id\": \"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\",\n  \"credentials\": {\n    \"code\": {\n      \"type\": \"code\",\n      \"identifiers\": [\n        \"hi@example.org\"\n      ],\n      \"config\": {\n        \"addresses\": [\n          {\n            \"channel\": \"email\",\n            \"address\": \"hi@example.org\"\n          }\n        ]\n      },\n      \"version\": 1,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"\",\n  \"schema_url\": \"\",\n  \"state\": \"\",\n  \"traits\": null,\n  \"metadata_public\": null,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestUpgradeCredentials-type=code-from=v0_with_unknown_value.json",
    "content": "{\n  \"id\": \"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\",\n  \"credentials\": {\n    \"code\": {\n      \"type\": \"code\",\n      \"identifiers\": [\n        \"hi@example.org\"\n      ],\n      \"config\": {\n        \"addresses\": [\n          {\n            \"channel\": \"email\",\n            \"address\": \"hi@example.org\"\n          }\n        ]\n      },\n      \"version\": 1,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"\",\n  \"schema_url\": \"\",\n  \"state\": \"\",\n  \"traits\": null,\n  \"metadata_public\": null,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestUpgradeCredentials-type=code-from=v2_with_empty_value.json",
    "content": "{\n  \"id\": \"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\",\n  \"credentials\": {\n    \"code\": {\n      \"type\": \"code\",\n      \"identifiers\": [\n        \"foo@example.org\",\n        \"+12341234\"\n      ],\n      \"config\": {\n        \"addresses\": [\n          {\n            \"address\": \"foo@example.org\",\n            \"channel\": \"email\"\n          },\n          {\n            \"address\": \"+12341234\",\n            \"channel\": \"sms\"\n          }\n        ]\n      },\n      \"version\": 1,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"\",\n  \"schema_url\": \"\",\n  \"state\": \"\",\n  \"traits\": null,\n  \"metadata_public\": null,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestUpgradeCredentials-type=webauthn-from=v0.json",
    "content": "{\n  \"id\": \"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\",\n  \"credentials\": {\n    \"webauthn\": {\n      \"type\": \"webauthn\",\n      \"identifiers\": [\n        \"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\"\n      ],\n      \"config\": {\n        \"credentials\": [\n          {\n            \"id\": \"HQ4LaIJ9NiqS1r0CQpWY+K0gMvhOq4yk5BHuO/YlitcurSpBK7weDXOvBcuN4lvn6DAmjGfmj/J/6bpOmtdT8Q==\",\n            \"public_key\": \"pQECAyYgASFYILAYFLoH1T8bQMSbPrNBCMMS5U7OFWRwv2U+GkAoiBADIlggBv+8ni7XVZYBB8ufMbP/d9fDxbmOkVVHOgcJifnoOR4=\",\n            \"attestation_type\": \"none\",\n            \"authenticator\": {\n              \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n              \"sign_count\": 4,\n              \"clone_warning\": false\n            },\n            \"display_name\": \"asdf\",\n            \"added_at\": \"2022-02-28T16:40:39Z\",\n            \"is_passwordless\": false\n          },\n          {\n            \"id\": \"1Q4LaIJ9NiqS1r0CQpWY+K0gMvhOq4yk5BHuO/YlitcurSpBK7weDXOvBcuN4lvn6DAmjGfmj/J/6bpOmtdT8Q==\",\n            \"public_key\": \"pQECAyYgASFYILAYFLoH1T8bQMSbPrNBCMMS5U7OFWRwv2U+GkAoiBADIlggBv+8ni7XVZYBB8ufMbP/d9fDxbmOkVVHOgcJifnoOR4=\",\n            \"attestation_type\": \"none\",\n            \"authenticator\": {\n              \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n              \"sign_count\": 4,\n              \"clone_warning\": false\n            },\n            \"display_name\": \"asdf\",\n            \"added_at\": \"2022-02-28T16:40:39Z\",\n            \"is_passwordless\": false\n          }\n        ],\n        \"user_handle\": \"TWT6CCD8RQ2+vevXx7biSQ==\"\n      },\n      \"version\": 1,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"\",\n  \"schema_url\": \"\",\n  \"state\": \"\",\n  \"traits\": null,\n  \"metadata_public\": null,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestUpgradeCredentials-type=webauthn-from=v1.json",
    "content": "{\n  \"id\": \"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\",\n  \"credentials\": {\n    \"webauthn\": {\n      \"type\": \"webauthn\",\n      \"identifiers\": [],\n      \"config\": {\n        \"credentials\": [\n          {\n            \"id\": \"HQ4LaIJ9NiqS1r0CQpWY+K0gMvhOq4yk5BHuO/YlitcurSpBK7weDXOvBcuN4lvn6DAmjGfmj/J/6bpOmtdT8Q==\",\n            \"public_key\": \"pQECAyYgASFYILAYFLoH1T8bQMSbPrNBCMMS5U7OFWRwv2U+GkAoiBADIlggBv+8ni7XVZYBB8ufMbP/d9fDxbmOkVVHOgcJifnoOR4=\",\n            \"attestation_type\": \"none\",\n            \"authenticator\": {\n              \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n              \"sign_count\": 4,\n              \"clone_warning\": false\n            },\n            \"display_name\": \"asdf\",\n            \"added_at\": \"2022-02-28T16:40:39Z\",\n            \"is_passwordless\": true\n          }\n        ],\n        \"user_handle\": \"2gZaSs9fTEeGmsBlC4gfgg==\"\n      },\n      \"version\": 1,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"\",\n  \"schema_url\": \"\",\n  \"state\": \"\",\n  \"traits\": null,\n  \"metadata_public\": null,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=include-multi-credential=oidc.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"bar\",\n    \"baz\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=include-multi-credential=password.json",
    "content": "{\n  \"type\": \"password\",\n  \"identifiers\": [\n    \"zab\",\n    \"bar\"\n  ],\n  \"config\": {\n    \"some\": \"secret\"\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=include-multi-credential=saml.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"qux\",\n    \"quz\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=include-multi-credential=webauthn.json",
    "content": "{\n  \"type\": \"webauthn\",\n  \"identifiers\": [\n    \"foo\",\n    \"bar\"\n  ],\n  \"config\": {\n    \"some\": \"secret\"\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=include-webauthn-credential=oidc.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"bar\",\n    \"baz\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=include-webauthn-credential=password.json",
    "content": "{\n  \"type\": \"password\",\n  \"identifiers\": [\n    \"zab\",\n    \"bar\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=include-webauthn-credential=saml.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"qux\",\n    \"quz\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=include-webauthn-credential=webauthn.json",
    "content": "{\n  \"type\": \"webauthn\",\n  \"identifiers\": [\n    \"foo\",\n    \"bar\"\n  ],\n  \"config\": {\n    \"some\": \"secret\"\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=no-include-credential=oidc.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"bar\",\n    \"baz\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=no-include-credential=password.json",
    "content": "{\n  \"type\": \"password\",\n  \"identifiers\": [\n    \"zab\",\n    \"bar\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=no-include-credential=saml.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"qux\",\n    \"quz\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=no-include-credential=webauthn.json",
    "content": "{\n  \"type\": \"webauthn\",\n  \"identifiers\": [\n    \"foo\",\n    \"bar\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=oidc-credential=oidc.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"bar\",\n    \"baz\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"initial_id_token\": \"foo\",\n        \"initial_access_token\": \"\",\n        \"initial_refresh_token\": \"\",\n        \"subject\": \"bar\",\n        \"provider\": \"oidc1\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=oidc-credential=password.json",
    "content": "{\n  \"type\": \"password\",\n  \"identifiers\": [\n    \"zab\",\n    \"bar\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=oidc-credential=saml.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"qux\",\n    \"quz\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=oidc-credential=webauthn.json",
    "content": "{\n  \"type\": \"webauthn\",\n  \"identifiers\": [\n    \"foo\",\n    \"bar\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=saml-credential=oidc.json",
    "content": "{\n  \"type\": \"oidc\",\n  \"identifiers\": [\n    \"bar\",\n    \"baz\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=saml-credential=password.json",
    "content": "{\n  \"type\": \"password\",\n  \"identifiers\": [\n    \"zab\",\n    \"bar\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=saml-credential=saml.json",
    "content": "{\n  \"type\": \"saml\",\n  \"identifiers\": [\n    \"qux\",\n    \"quz\"\n  ],\n  \"config\": {\n    \"providers\": [\n      {\n        \"subject\": \"qux\",\n        \"provider\": \"saml1\"\n      }\n    ]\n  },\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/.snapshots/TestWithDeclassifiedCredentials-case=saml-credential=webauthn.json",
    "content": "{\n  \"type\": \"webauthn\",\n  \"identifiers\": [\n    \"foo\",\n    \"bar\"\n  ],\n  \"version\": 0,\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "identity/address.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nconst (\n\tAddressTypeEmail = \"email\"\n\tAddressTypeSMS   = \"sms\"\n)\n"
  },
  {
    "path": "identity/credentials.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/wI2L/jsondiff\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\n// In nearly all cases, credential types have a constant id.\n// To make it potentially faster, and not need any data in the database,\n// we define this constant map. In 99.999% of the cases, a look up in this map is a hit and it's fast.\n// In the unlikely event that this is a miss, e.g. for very old self-hosted deployments from a time where\n// credential type ids used to be dynamic, we do a database look-up.\nvar ConstantCredentialsTypeToId = map[CredentialsType]string{\n\tCredentialsTypePassword: \"78c1b41d-8341-4507-aa60-aff1d4369670\", // gitleaks:allow\n\tCredentialsTypeOIDC:     \"6fa5e2e0-bfce-4631-b62b-cf2b0252b289\", // gitleaks:allow\n\tCredentialsTypeTOTP:     \"5e29b036-aa47-457f-9fe6-aa8b854a752b\", // gitleaks:allow\n\tCredentialsTypeLookup:   \"567a0730-7f48-4dd7-a13d-df87a51c245f\", // gitleaks:allow\n\tCredentialsTypeWebAuthn: \"6b213fa0-e6ad-46cb-8878-b088d2ce2e3c\", // gitleaks:allow\n\tCredentialsTypeCodeAuth: \"14f3b7e2-8725-4068-be39-8a796485fd97\", // gitleaks:allow\n\tCredentialsTypePasskey:  \"8d0ca544-9bf6-45d3-bd75-0bbb3aeba3c7\", // gitleaks:allow\n\tCredentialsTypeSAML:     \"7bddcf6c-f50e-4a18-9b0f-429114c33419\", // gitleaks:allow\n}\n\n// Authenticator Assurance Level (AAL)\n//\n// The authenticator assurance level can be one of \"aal1\", \"aal2\", or \"aal3\". A higher number means that it is harder\n// for an attacker to compromise the account.\n//\n// Generally, \"aal1\" implies that one authentication factor was used while AAL2 implies that two factors (e.g.\n// password + TOTP) have been used.\n//\n// To learn more about these levels please head over to: https://www.ory.sh/kratos/docs/concepts/credentials\n//\n// swagger:model authenticatorAssuranceLevel\ntype AuthenticatorAssuranceLevel string\n\nconst (\n\tNoAuthenticatorAssuranceLevel AuthenticatorAssuranceLevel = \"aal0\"\n\tAuthenticatorAssuranceLevel1  AuthenticatorAssuranceLevel = \"aal1\"\n\tAuthenticatorAssuranceLevel2  AuthenticatorAssuranceLevel = \"aal2\"\n)\n\ntype NullableAuthenticatorAssuranceLevel struct {\n\tsql.NullString\n}\n\n// NewNullableAuthenticatorAssuranceLevel returns a new NullableAuthenticatorAssuranceLevel\nfunc NewNullableAuthenticatorAssuranceLevel(aal AuthenticatorAssuranceLevel) NullableAuthenticatorAssuranceLevel {\n\tswitch aal {\n\tcase NoAuthenticatorAssuranceLevel:\n\t\tfallthrough\n\tcase AuthenticatorAssuranceLevel1:\n\t\tfallthrough\n\tcase AuthenticatorAssuranceLevel2:\n\t\treturn NullableAuthenticatorAssuranceLevel{sql.NullString{\n\t\t\tString: string(aal),\n\t\t\tValid:  true,\n\t\t}}\n\tdefault:\n\t\treturn NullableAuthenticatorAssuranceLevel{sql.NullString{}}\n\t}\n}\n\n// ToAAL returns the AuthenticatorAssuranceLevel value of the given NullableAuthenticatorAssuranceLevel.\nfunc (n NullableAuthenticatorAssuranceLevel) ToAAL() (AuthenticatorAssuranceLevel, bool) {\n\tif !n.Valid {\n\t\treturn \"\", false\n\t}\n\tswitch n.String {\n\tcase string(NoAuthenticatorAssuranceLevel):\n\t\treturn NoAuthenticatorAssuranceLevel, true\n\tcase string(AuthenticatorAssuranceLevel1):\n\t\treturn AuthenticatorAssuranceLevel1, true\n\tcase string(AuthenticatorAssuranceLevel2):\n\t\treturn AuthenticatorAssuranceLevel2, true\n\tdefault:\n\t\treturn \"\", false\n\t}\n}\n\n// CredentialsType  represents several different credential types, like password credentials, passwordless credentials,\n// and so on.\n//\n// swagger:enum CredentialsType\ntype CredentialsType string\n\n// Please make sure to add all of these values to the test that ensures they are created during migration\nconst (\n\tCredentialsTypePassword CredentialsType = \"password\"\n\tCredentialsTypeOIDC     CredentialsType = \"oidc\"\n\tCredentialsTypeTOTP     CredentialsType = \"totp\"\n\tCredentialsTypeLookup   CredentialsType = \"lookup_secret\"\n\tCredentialsTypeWebAuthn CredentialsType = \"webauthn\"\n\tCredentialsTypeCodeAuth CredentialsType = \"code\"\n\tCredentialsTypePasskey  CredentialsType = \"passkey\"\n\tCredentialsTypeProfile  CredentialsType = \"profile\"\n\tCredentialsTypeSAML     CredentialsType = \"saml\"\n)\n\nfunc (c CredentialsType) String() string {\n\treturn string(c)\n}\n\nfunc (c CredentialsType) ToUiNodeGroup() node.UiNodeGroup {\n\tswitch c {\n\tcase CredentialsTypePassword:\n\t\treturn node.PasswordGroup\n\tcase CredentialsTypeOIDC, CredentialsTypeSAML:\n\t\treturn node.OpenIDConnectGroup\n\tcase CredentialsTypeTOTP:\n\t\treturn node.TOTPGroup\n\tcase CredentialsTypeWebAuthn:\n\t\treturn node.WebAuthnGroup\n\tcase CredentialsTypeLookup:\n\t\treturn node.LookupGroup\n\tcase CredentialsTypeCodeAuth:\n\t\treturn node.CodeGroup\n\tcase CredentialsTypePasskey:\n\t\treturn node.PasskeyGroup\n\tdefault:\n\t\treturn node.DefaultGroup\n\t}\n}\n\nvar AllCredentialTypes = []CredentialsType{\n\tCredentialsTypePassword,\n\tCredentialsTypeOIDC,\n\t// CredentialsTypeSAML, placeholder for the OEL version\n\tCredentialsTypeTOTP,\n\tCredentialsTypeLookup,\n\tCredentialsTypeWebAuthn,\n\tCredentialsTypeCodeAuth,\n\tCredentialsTypePasskey,\n}\n\nconst (\n\t// CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).\n\t// It is not used within the credentials object itself.\n\tCredentialsTypeRecoveryLink CredentialsType = \"link_recovery\"\n\tCredentialsTypeRecoveryCode CredentialsType = \"code_recovery\"\n)\n\n// ParseCredentialsType parses a string into a CredentialsType or returns false as the second argument.\nfunc ParseCredentialsType(in string) (CredentialsType, bool) {\n\tswitch t := CredentialsType(in); t {\n\tcase CredentialsTypePassword,\n\t\tCredentialsTypeOIDC,\n\t\tCredentialsTypeSAML,\n\t\tCredentialsTypeTOTP,\n\t\tCredentialsTypeLookup,\n\t\tCredentialsTypeWebAuthn,\n\t\tCredentialsTypeCodeAuth,\n\t\tCredentialsTypeRecoveryLink,\n\t\tCredentialsTypeRecoveryCode,\n\t\tCredentialsTypePasskey:\n\t\treturn t, true\n\t}\n\treturn \"\", false\n}\n\n// Credentials represents a specific credential type\n//\n// swagger:model identityCredentials\ntype Credentials struct {\n\tID uuid.UUID `json:\"-\" db:\"id\"`\n\n\t// Type discriminates between different types of credentials.\n\tType                     CredentialsType `json:\"type\" db:\"-\"`\n\tIdentityCredentialTypeID uuid.UUID       `json:\"-\" db:\"identity_credential_type_id\"`\n\n\t// Identifiers represent a list of unique identifiers this credential type matches.\n\tIdentifiers []string `json:\"identifiers\" db:\"-\"`\n\n\t// Config contains the concrete credential payload. This might contain the bcrypt-hashed password, the email\n\t// for passwordless authentication or access_token and refresh tokens from OpenID Connect flows.\n\tConfig sqlxx.JSONRawMessage `json:\"config,omitempty\" db:\"config\"`\n\n\t// Version refers to the version of the credential. Useful when changing the config schema.\n\tVersion int `json:\"version\" db:\"version\"`\n\n\tIdentityID uuid.UUID `json:\"-\" faker:\"-\" db:\"identity_id\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"created_at\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"updated_at\" db:\"updated_at\"`\n\tNID       uuid.UUID `json:\"-\"  faker:\"-\" db:\"nid\"`\n}\n\nfunc (c Credentials) TableName(context.Context) string {\n\treturn \"identity_credentials\"\n}\n\nfunc (c Credentials) GetID() uuid.UUID {\n\treturn c.ID\n}\n\n// Signature returns a unique string signature for the credential.\nfunc (c Credentials) Signature() string {\n\tsortedIdentifiers := slices.Clone(c.Identifiers)\n\tslices.Sort(sortedIdentifiers)\n\tidentifiersStr := strings.Join(sortedIdentifiers, \",\")\n\n\t// Normalize JSON config to remove whitespace and key ordering differences\n\tvar normalizedConfig any\n\tif len(c.Config) > 0 {\n\t\tif err := json.Unmarshal(c.Config, &normalizedConfig); err != nil {\n\t\t\t// there is not much we can do when unmarshal fails except use the raw value\n\t\t\tnormalizedConfig = c.Config\n\t\t}\n\t}\n\treturn fmt.Sprintf(\"%v|%v|%d|%+v|%v|%v\", c.Type, identifiersStr, c.Version, normalizedConfig, c.IdentityID, c.NID)\n}\n\ntype (\n\t// swagger:ignore\n\tCredentialIdentifier struct {\n\t\tID                        uuid.UUID  `db:\"id\"`\n\t\tIdentifier                string     `db:\"identifier\"`\n\t\tIdentityID                *uuid.UUID `json:\"-\" db:\"identity_id\"`\n\t\tIdentityCredentialsID     uuid.UUID  `json:\"-\" db:\"identity_credential_id\"`\n\t\tIdentityCredentialsTypeID uuid.UUID  `json:\"-\" db:\"identity_credential_type_id\"`\n\t\tCreatedAt                 time.Time  `json:\"created_at\" db:\"created_at\"`\n\t\tUpdatedAt                 time.Time  `json:\"updated_at\" db:\"updated_at\"`\n\t\tNID                       uuid.UUID  `json:\"-\"  faker:\"-\" db:\"nid\"`\n\t}\n\n\t// swagger:ignore\n\tCredentialsTypeTable struct {\n\t\tID   uuid.UUID       `json:\"-\" db:\"id\"`\n\t\tName CredentialsType `json:\"-\" db:\"name\"`\n\t}\n\n\t// swagger:ignore\n\tActiveCredentialsCounter interface {\n\t\tID() CredentialsType\n\t\tCountActiveFirstFactorCredentials(context.Context, map[CredentialsType]Credentials) (int, error)\n\t\tCountActiveMultiFactorCredentials(context.Context, map[CredentialsType]Credentials) (int, error)\n\t}\n\n\t// swagger:ignore\n\tActiveCredentialsCounterStrategyProvider interface {\n\t\tActiveCredentialsCounterStrategies(context.Context) []ActiveCredentialsCounter\n\t}\n)\n\nfunc (c CredentialsTypeTable) TableName(context.Context) string {\n\treturn \"identity_credential_types\"\n}\n\nfunc (c CredentialIdentifier) TableName(context.Context) string {\n\treturn \"identity_credential_identifiers\"\n}\n\nfunc CredentialsEqual(a, b map[CredentialsType]Credentials) bool {\n\tif len(a) != len(b) {\n\t\treturn false\n\t}\n\n\tif len(a) == 0 && len(b) == 0 {\n\t\treturn true\n\t}\n\n\tfor k, expect := range b {\n\t\tactual, found := a[k]\n\t\tif !found {\n\t\t\treturn false\n\t\t}\n\n\t\t// Try to normalize configs (remove spaces etc).\n\t\tpatch, err := jsondiff.CompareJSON(actual.Config, expect.Config)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\n\t\tif len(patch) > 0 {\n\t\t\treturn false\n\t\t}\n\n\t\texpectIdentifiers, actualIdentifiers := make(map[string]struct{}, len(expect.Identifiers)), make(map[string]struct{}, len(actual.Identifiers))\n\t\tfor _, i := range expect.Identifiers {\n\t\t\texpectIdentifiers[i] = struct{}{}\n\t\t}\n\t\tfor _, i := range actual.Identifiers {\n\t\t\tactualIdentifiers[i] = struct{}{}\n\t\t}\n\t\tif !reflect.DeepEqual(expectIdentifiers, actualIdentifiers) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n"
  },
  {
    "path": "identity/credentials_code.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/stringsx\"\n)\n\ntype CodeChannel string\n\n// Scan implements the sql.Scanner interface for CodeChannel\n// to support proper scanning from database values while removing\n// any trailing whitespace that might be present in\n// PostgreSQL and CockroachDB CHAR fields.\nfunc (c *CodeChannel) Scan(src interface{}) error {\n\tif src == nil {\n\t\t*c = \"\"\n\t\treturn nil\n\t}\n\n\tswitch s := src.(type) {\n\tcase string:\n\t\t*c = CodeChannel(strings.TrimSpace(s))\n\t\treturn nil\n\tcase []byte:\n\t\t*c = CodeChannel(strings.TrimSpace(string(s)))\n\t\treturn nil\n\tdefault:\n\t\treturn errors.Errorf(\"cannot scan %T into CodeChannel\", src)\n\t}\n}\n\nconst (\n\tCodeChannelEmail CodeChannel = AddressTypeEmail\n\tCodeChannelSMS   CodeChannel = AddressTypeSMS\n)\n\nfunc NewCodeChannel(value string) (CodeChannel, error) {\n\tswitch f := stringsx.SwitchExact(value); {\n\tcase f.AddCase(string(CodeChannelEmail)):\n\t\treturn CodeChannelEmail, nil\n\tcase f.AddCase(string(CodeChannelSMS)):\n\t\treturn CodeChannelSMS, nil\n\tdefault:\n\t\treturn \"\", errors.Wrap(ErrInvalidCodeAddressType, f.ToUnknownCaseErr().Error())\n\t}\n}\n\n// CredentialsCode represents a one time login/registration code\n//\n// swagger:model identityCredentialsCode\ntype CredentialsCode struct {\n\tAddresses []CredentialsCodeAddress `json:\"addresses\"`\n}\n\n// swagger:model identityCredentialsCodeAddress\ntype CredentialsCodeAddress struct {\n\t// The type of the address for this code\n\tChannel CodeChannel `json:\"channel\"`\n\n\t// The address for this code\n\tAddress string `json:\"address\"`\n}\n\nvar ErrInvalidCodeAddressType = herodot.ErrMisconfiguration.WithReasonf(\"The address type for sending OTP codes is not supported.\")\n\nfunc (c *CredentialsCodeAddress) UnmarshalJSON(data []byte) (err error) {\n\ttype alias CredentialsCodeAddress\n\tvar ac alias\n\tif err := json.Unmarshal(data, &ac); err != nil {\n\t\treturn err\n\t}\n\n\tac.Channel, err = NewCodeChannel(string(ac.Channel))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*c = CredentialsCodeAddress(ac)\n\treturn nil\n}\n"
  },
  {
    "path": "identity/credentials_code_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCredentialsCodeAddressUnmarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\tinput   string\n\t\twant    CredentialsCodeAddress\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:  \"valid email address\",\n\t\t\tinput: `{\"channel\": \"email\", \"address\": \"user@example.com\"}`,\n\t\t\twant: CredentialsCodeAddress{\n\t\t\t\tChannel: CodeChannelEmail,\n\t\t\t\tAddress: \"user@example.com\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:  \"valid SMS address\",\n\t\t\tinput: `{\"channel\": \"sms\", \"address\": \"+1234567890\"}`,\n\t\t\twant: CredentialsCodeAddress{\n\t\t\t\tChannel: CodeChannelSMS,\n\t\t\t\tAddress: \"+1234567890\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid address type\",\n\t\t\tinput:   `{\"channel\": \"invalid\", \"address\": \"user@example.com\"}`,\n\t\t\twant:    CredentialsCodeAddress{},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:    \"missing channel field\",\n\t\t\tinput:   `{\"address\": \"user@example.com\"}`,\n\t\t\twant:    CredentialsCodeAddress{},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid JSON structure\",\n\t\t\tinput:   `{\"channel\": \"email\", \"address\": \"user@example.com\"`,\n\t\t\twant:    CredentialsCodeAddress{},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar got CredentialsCodeAddress\n\t\t\terr := json.Unmarshal([]byte(tt.input), &got)\n\t\t\tif tt.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, tt.want, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNewCodeAddressType(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\tinput   string\n\t\twant    CodeChannel\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"valid email address type\",\n\t\t\tinput:   \"email\",\n\t\t\twant:    CodeChannelEmail,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"valid SMS address type\",\n\t\t\tinput:   \"sms\",\n\t\t\twant:    CodeChannelSMS,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid address type\",\n\t\t\tinput:   \"invalid\",\n\t\t\twant:    \"\",\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := NewCodeChannel(tt.input)\n\t\t\tif tt.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, tt.want, got)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/credentials_lookup.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"time\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/ory/x/sqlxx\"\n)\n\n// CredentialsConfig is the struct that is being used as part of the identity credentials.\ntype CredentialsLookupConfig struct {\n\t// List of recovery codes\n\tRecoveryCodes []RecoveryCode `json:\"recovery_codes\"`\n}\n\nfunc (c *CredentialsLookupConfig) ToNode() *node.Node {\n\tmessages := make([]text.Message, len(c.RecoveryCodes))\n\tformatted := make([]string, len(c.RecoveryCodes))\n\tfor k, code := range c.RecoveryCodes {\n\t\tif time.Time(code.UsedAt).IsZero() {\n\t\t\tmessages[k] = *text.NewInfoSelfServiceSettingsLookupSecret(code.Code)\n\t\t\tformatted[k] = code.Code\n\t\t} else {\n\t\t\tmessages[k] = *text.NewInfoSelfServiceSettingsLookupSecretUsed(time.Time(code.UsedAt).UTC())\n\t\t\tformatted[k] = \"used\"\n\t\t}\n\t}\n\n\treturn node.NewTextField(node.LookupCodes, text.NewInfoSelfServiceSettingsLookupSecretList(formatted, messages), node.LookupGroup).\n\t\tWithMetaLabel(text.NewInfoSelfServiceSettingsLookupSecretsLabel())\n}\n\ntype RecoveryCode struct {\n\t// A recovery code\n\tCode string `json:\"code\"`\n\n\t// UsedAt indicates whether and when a recovery code was used.\n\tUsedAt sqlxx.NullTime `json:\"used_at,omitempty\"`\n}\n"
  },
  {
    "path": "identity/credentials_lookup_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity_test\n\nimport (\n\t_ \"embed\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc TestToNode(t *testing.T) {\n\tc := identity.CredentialsLookupConfig{RecoveryCodes: []identity.RecoveryCode{\n\t\t{Code: \"foo\", UsedAt: sqlxx.NullTime(time.Unix(1629199958, 0).UTC())},\n\t\t{Code: \"bar\"},\n\t\t{Code: \"baz\"},\n\t\t{Code: \"oof\", UsedAt: sqlxx.NullTime(time.Unix(1629199968, 0).UTC())},\n\t}}\n\n\ttesthelpers.SnapshotTExcept(t, c.ToNode(), []string{})\n}\n"
  },
  {
    "path": "identity/credentials_migrate.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/pkg/errors\"\n)\n\nfunc UpgradeWebAuthnCredentials(i *Identity, c *Credentials) (err error) {\n\tif c.Type != CredentialsTypeWebAuthn {\n\t\treturn nil\n\t}\n\n\tversion := c.Version\n\tif version == 0 {\n\t\tif gjson.GetBytes(c.Config, \"user_handle\").String() == \"\" {\n\t\t\tid, err := json.Marshal(i.ID[:])\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\n\t\t\tc.Config, err = sjson.SetRawBytes(c.Config, \"user_handle\", id)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\t\t}\n\n\t\tvar index = -1\n\t\tvar err error\n\t\tgjson.GetBytes(c.Config, \"credentials\").ForEach(func(_, value gjson.Result) bool {\n\t\t\tindex++\n\n\t\t\tif value.Get(\"is_passwordless\").Exists() {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tc.Config, err = sjson.SetBytes(c.Config, fmt.Sprintf(\"credentials.%d.is_passwordless\", index), false)\n\t\t\treturn err == nil\n\t\t})\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tc.Version = 1\n\t}\n\treturn nil\n}\n\n// UpgradeCredentials migrates a set of older WebAuthn credentials to newer ones.\nfunc UpgradeCredentials(i *Identity) error {\n\tfor k := range i.Credentials {\n\t\tc := i.Credentials[k]\n\t\tif err := UpgradeWebAuthnCredentials(i, &c); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tif err := UpgradeCodeCredentials(&c); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\ti.Credentials[k] = c\n\t}\n\treturn nil\n}\n\nfunc UpgradeCodeCredentials(c *Credentials) (err error) {\n\tif c.Type != CredentialsTypeCodeAuth {\n\t\treturn nil\n\t}\n\n\tversion := c.Version\n\tif version == 0 {\n\t\taddressType := strings.ToLower(strings.TrimSpace(gjson.GetBytes(c.Config, \"address_type\").String()))\n\n\t\tchannel, err := NewCodeChannel(addressType)\n\t\tif err != nil {\n\t\t\t// We know that in some cases the address type can be empty. In this case, we default to email\n\t\t\t// as sms is a new addition to the address_type introduced in this PR.\n\t\t\tchannel = CodeChannelEmail\n\t\t}\n\n\t\tc.Config, err = sjson.DeleteBytes(c.Config, \"used_at\")\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tc.Config, err = sjson.DeleteBytes(c.Config, \"address_type\")\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tfor _, id := range c.Identifiers {\n\t\t\tif id == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tc.Config, err = sjson.SetBytes(c.Config, \"addresses.-1\", &CredentialsCodeAddress{\n\t\t\t\tAddress: id,\n\t\t\t\tChannel: channel,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\t\t}\n\n\t\t// This is needed because sjson adds spaces which can trip string comparisons.\n\t\tc.Config, err = json.Marshal(json.RawMessage(c.Config))\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tc.Version = 1\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "identity/credentials_migrate_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t_ \"embed\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/snapshotx\"\n)\n\n//go:embed stub/webauthn/v0.json\nvar webAuthnV0 []byte\n\n//go:embed stub/webauthn/v1.json\nvar webAuthnV1 []byte\n\nfunc TestUpgradeCredentials(t *testing.T) {\n\tt.Run(\"empty credentials\", func(t *testing.T) {\n\t\ti := &Identity{}\n\n\t\terr := UpgradeCredentials(i)\n\t\trequire.NoError(t, err)\n\t\twc := WithCredentialsAndAdminMetadataInJSON(*i)\n\t\tsnapshotx.SnapshotTExcept(t, &wc, nil)\n\t})\n\n\trun := func(t *testing.T, identifiers []string, config string, version int, credentialsType CredentialsType, expectedVersion int) {\n\t\tif identifiers == nil {\n\t\t\tidentifiers = []string{\"hi@example.org\"}\n\t\t}\n\t\ti := &Identity{\n\t\t\tID: uuid.FromStringOrNil(\"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\"),\n\t\t\tCredentials: map[CredentialsType]Credentials{\n\t\t\t\tcredentialsType: {\n\t\t\t\t\tIdentifiers: identifiers,\n\t\t\t\t\tType:        credentialsType,\n\t\t\t\t\tVersion:     version,\n\t\t\t\t\tConfig:      []byte(config),\n\t\t\t\t}},\n\t\t}\n\n\t\trequire.NoError(t, UpgradeCredentials(i))\n\t\twc := WithCredentialsAndAdminMetadataInJSON(*i)\n\t\tsnapshotx.SnapshotT(t, &wc)\n\t\tassert.Equal(t, expectedVersion, i.Credentials[credentialsType].Version)\n\t}\n\n\tt.Run(\"type=code\", func(t *testing.T) {\n\t\tt.Run(\"from=v0 with email empty space value\", func(t *testing.T) {\n\t\t\tt.Run(\"with one identifier\", func(t *testing.T) {\n\t\t\t\trun(t, nil, `{\"address_type\": \"email                               \", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`, 0, CredentialsTypeCodeAuth, 1)\n\t\t\t})\n\n\t\t\tt.Run(\"with two identifiers\", func(t *testing.T) {\n\t\t\t\trun(t, []string{\"foo@example.org\", \"bar@example.org\"}, `{\"address_type\": \"email                               \", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`, 0, CredentialsTypeCodeAuth, 1)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"from=v0 with empty value\", func(t *testing.T) {\n\t\t\trun(t, nil, `{\"address_type\": \"\", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`, 0, CredentialsTypeCodeAuth, 1)\n\t\t})\n\n\t\tt.Run(\"from=v0 with correct value\", func(t *testing.T) {\n\t\t\trun(t, nil, `{\"address_type\": \"email\", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`, 0, CredentialsTypeCodeAuth, 1)\n\t\t})\n\n\t\tt.Run(\"from=v0 with unknown value\", func(t *testing.T) {\n\t\t\trun(t, nil, `{\"address_type\": \"other\", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`, 0, CredentialsTypeCodeAuth, 1)\n\t\t})\n\n\t\tt.Run(\"from=v2 with empty value\", func(t *testing.T) {\n\t\t\trun(t, []string{\"foo@example.org\", \"+12341234\"}, `{\"addresses\": [{\"address\":\"foo@example.org\",\"channel\":\"email\"},{\"address\":\"+12341234\",\"channel\":\"sms\"}]}`, 1, CredentialsTypeCodeAuth, 1)\n\t\t})\n\t})\n\n\tt.Run(\"type=webauthn\", func(t *testing.T) {\n\t\tt.Run(\"from=v0\", func(t *testing.T) {\n\t\t\trun(t, []string{\"4d64fa08-20fc-450d-bebd-ebd7c7b6e249\"}, string(webAuthnV0), 0, CredentialsTypeWebAuthn, 1)\n\t\t})\n\n\t\tt.Run(\"from=v1\", func(t *testing.T) {\n\n\t\t\trun(t, []string{}, string(webAuthnV1), 1, CredentialsTypeWebAuthn, 1)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "identity/credentials_oidc.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\n// CredentialsOIDC is contains the configuration for credentials of the type oidc.\n//\n// swagger:model identityCredentialsOidc\ntype CredentialsOIDC struct {\n\tProviders []CredentialsOIDCProvider `json:\"providers\"`\n}\n\n// CredentialsOIDCProvider is contains a specific OpenID COnnect credential for a particular connection (e.g. Google).\n//\n// swagger:model identityCredentialsOidcProvider\ntype CredentialsOIDCProvider struct {\n\tSubject             string `json:\"subject\"`\n\tProvider            string `json:\"provider\"`\n\tInitialIDToken      string `json:\"initial_id_token\"`\n\tInitialAccessToken  string `json:\"initial_access_token\"`\n\tInitialRefreshToken string `json:\"initial_refresh_token\"`\n\tOrganization        string `json:\"organization,omitempty\"`\n\tUseAutoLink         bool   `json:\"use_auto_link,omitzero\"`\n}\n\n// swagger:ignore\ntype CredentialsOIDCEncryptedTokens struct {\n\tRefreshToken string `json:\"refresh_token,omitempty\"`\n\tIDToken      string `json:\"id_token,omitempty\"`\n\tAccessToken  string `json:\"access_token,omitempty\"`\n}\n\nfunc (c *CredentialsOIDCEncryptedTokens) GetRefreshToken() string {\n\tif c == nil {\n\t\treturn \"\"\n\t}\n\treturn c.RefreshToken\n}\n\nfunc (c *CredentialsOIDCEncryptedTokens) GetAccessToken() string {\n\tif c == nil {\n\t\treturn \"\"\n\t}\n\treturn c.AccessToken\n}\n\nfunc (c *CredentialsOIDCEncryptedTokens) GetIDToken() string {\n\tif c == nil {\n\t\treturn \"\"\n\t}\n\treturn c.IDToken\n}\n\n// NewCredentialsOIDC creates a new OIDC credential.\nfunc NewCredentialsOIDC(tokens *CredentialsOIDCEncryptedTokens, provider, subject, organization string) (*Credentials, error) {\n\treturn NewOIDCLikeCredentials(tokens, CredentialsTypeOIDC, provider, subject, organization)\n}\n\n// NewOIDCLikeCredentials creates a new OIDC-like credential.\nfunc NewOIDCLikeCredentials(tokens *CredentialsOIDCEncryptedTokens, t CredentialsType, provider, subject, organization string) (*Credentials, error) {\n\tif provider == \"\" {\n\t\treturn nil, errors.New(\"received empty provider in oidc credentials\")\n\t}\n\n\tif subject == \"\" {\n\t\treturn nil, errors.New(\"received empty provider in oidc credentials\")\n\t}\n\n\tvar b bytes.Buffer\n\tif err := json.NewEncoder(&b).Encode(CredentialsOIDC{\n\t\tProviders: []CredentialsOIDCProvider{\n\t\t\t{\n\t\t\t\tSubject:             subject,\n\t\t\t\tProvider:            provider,\n\t\t\t\tInitialIDToken:      tokens.GetIDToken(),\n\t\t\t\tInitialAccessToken:  tokens.GetAccessToken(),\n\t\t\t\tInitialRefreshToken: tokens.GetRefreshToken(),\n\t\t\t\tOrganization:        organization,\n\t\t\t},\n\t\t},\n\t}); err != nil {\n\t\treturn nil, errors.WithStack(x.PseudoPanic.\n\t\t\tWithDebugf(\"Unable to encode password options to JSON: %s\", err))\n\t}\n\n\treturn &Credentials{\n\t\tType:        t,\n\t\tIdentifiers: []string{OIDCUniqueID(provider, subject)},\n\t\tConfig:      b.Bytes(),\n\t}, nil\n}\n\nfunc (c *CredentialsOIDCProvider) GetTokens() *CredentialsOIDCEncryptedTokens {\n\treturn &CredentialsOIDCEncryptedTokens{\n\t\tRefreshToken: c.InitialRefreshToken,\n\t\tIDToken:      c.InitialIDToken,\n\t\tAccessToken:  c.InitialAccessToken,\n\t}\n}\n\nfunc OIDCUniqueID(provider, subject string) string {\n\treturn fmt.Sprintf(\"%s:%s\", provider, subject)\n}\n\nfunc (c *CredentialsOIDC) Organization() string {\n\tfor _, p := range c.Providers {\n\t\tif p.Organization != \"\" {\n\t\t\treturn p.Organization\n\t\t}\n\t}\n\n\treturn \"\"\n}\n"
  },
  {
    "path": "identity/credentials_oidc_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestNewCredentialsOIDC(t *testing.T) {\n\t_, err := NewCredentialsOIDC(new(CredentialsOIDCEncryptedTokens), \"\", \"not-empty\", \"\")\n\trequire.Error(t, err)\n\t_, err = NewCredentialsOIDC(new(CredentialsOIDCEncryptedTokens), \"not-empty\", \"\", \"\")\n\trequire.Error(t, err)\n\t_, err = NewCredentialsOIDC(new(CredentialsOIDCEncryptedTokens), \"not-empty\", \"not-empty\", \"\")\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "identity/credentials_password.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\n// CredentialsPassword is contains the configuration for credentials of the type password.\n//\n// swagger:model identityCredentialsPassword\ntype CredentialsPassword struct {\n\t// HashedPassword is a hash-representation of the password.\n\tHashedPassword string `json:\"hashed_password\"`\n\n\t// UsePasswordMigrationHook is set to true if the password should be migrated\n\t// using the password migration hook. If set, and the HashedPassword is empty, a\n\t// webhook will be called during login to migrate the password.\n\tUsePasswordMigrationHook bool `json:\"use_password_migration_hook,omitempty\"`\n}\n\nfunc (cp *CredentialsPassword) ShouldUsePasswordMigrationHook() bool {\n\treturn cp != nil && cp.HashedPassword == \"\" && cp.UsePasswordMigrationHook\n}\n"
  },
  {
    "path": "identity/credentials_password_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCredentialsPassword_ShouldUsePasswordMigrationHook(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tcp   *CredentialsPassword\n\t\twant bool\n\t}{{\n\t\tname: \"pw set\",\n\t\tcp: &CredentialsPassword{\n\t\t\tHashedPassword:           \"pw\",\n\t\t\tUsePasswordMigrationHook: true,\n\t\t},\n\t\twant: false,\n\t}, {\n\t\tname: \"pw not set\",\n\t\tcp: &CredentialsPassword{\n\t\t\tHashedPassword:           \"\",\n\t\t\tUsePasswordMigrationHook: true,\n\t\t},\n\t\twant: true,\n\t}, {\n\t\tname: \"nil\",\n\t\twant: false,\n\t}, {\n\t\tname: \"pw not set, hook not set\",\n\t\tcp: &CredentialsPassword{\n\t\t\tHashedPassword: \"\",\n\t\t},\n\t\twant: false,\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, tt.cp.ShouldUsePasswordMigrationHook(), \"ShouldUsePasswordMigrationHook()\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/credentials_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/mohae/deepcopy\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc TestCredentialsEqual(t *testing.T) {\n\toriginal := map[CredentialsType]Credentials{\n\t\t\"foo\": {Type: \"foo\", Identifiers: []string{\"bar\"}, Config: sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`)},\n\t}\n\n\tderived := deepcopy.Copy(original).(map[CredentialsType]Credentials)\n\tassert.EqualValues(t, original, derived)\n\tderived[\"foo\"].Identifiers[0] = \"baz\"\n\tassert.NotEqual(t, original, derived)\n}\n\nfunc TestAALOrder(t *testing.T) {\n\tassert.True(t, NoAuthenticatorAssuranceLevel < AuthenticatorAssuranceLevel1)\n\tassert.True(t, AuthenticatorAssuranceLevel1 < AuthenticatorAssuranceLevel2)\n}\n\nfunc TestParseCredentialsType(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\tinput    string\n\t\texpected CredentialsType\n\t}{\n\t\t{\"password\", CredentialsTypePassword},\n\t\t{\"oidc\", CredentialsTypeOIDC},\n\t\t{\"totp\", CredentialsTypeTOTP},\n\t\t{\"webauthn\", CredentialsTypeWebAuthn},\n\t\t{\"lookup_secret\", CredentialsTypeLookup},\n\t\t{\"link_recovery\", CredentialsTypeRecoveryLink},\n\t\t{\"code_recovery\", CredentialsTypeRecoveryCode},\n\t} {\n\t\tt.Run(\"case=\"+tc.input, func(t *testing.T) {\n\t\t\tactual, ok := ParseCredentialsType(tc.input)\n\t\t\trequire.True(t, ok)\n\t\t\tassert.Equal(t, tc.expected, actual)\n\t\t})\n\t}\n\n\tt.Run(\"case=unknown\", func(t *testing.T) {\n\t\t_, ok := ParseCredentialsType(\"unknown\")\n\t\trequire.False(t, ok)\n\t})\n}\n\nfunc TestCredentials_Hash(t *testing.T) {\n\tbaseID := uuid.Must(uuid.NewV4())\n\tbaseNID := uuid.Must(uuid.NewV4())\n\n\tfor _, tc := range []struct {\n\t\tname        string\n\t\tcred1       Credentials\n\t\tcred2       Credentials\n\t\texpectEqual bool\n\t\tdescription string\n\t}{\n\t\t{\n\t\t\tname: \"same json with different whitespace\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\",\"baz\":\"qux\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\n\t\t\t\t\"foo\": \"bar\",\n\t\t\t\t\"baz\": \"qux\"\n\t\t\t}`),\n\t\t\t\tVersion: 1,\n\t\t\t},\n\t\t\texpectEqual: true,\n\t\t\tdescription: \"hashes should be equal for same JSON with different whitespace\",\n\t\t},\n\t\t{\n\t\t\tname: \"same json with different key order\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypeCodeAuth,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"addresses\":[{\"address\":\"test@example.com\",\"channel\":\"email\"}]}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeCodeAuth,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"test@example.com\"}]}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\texpectEqual: true,\n\t\t\tdescription: \"hashes should be equal for same JSON with different key order\",\n\t\t},\n\t\t{\n\t\t\tname: \"nested json with different key order\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypeWebAuthn,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"credentials\":[{\"id\":\"abc\",\"public_key\":\"xyz\",\"type\":\"webauthn\"}]}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeWebAuthn,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"credentials\":[{\"type\":\"webauthn\",\"public_key\":\"xyz\",\"id\":\"abc\"}]}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\texpectEqual: true,\n\t\t\tdescription: \"hashes should be equal for nested JSON with different key order\",\n\t\t},\n\t\t{\n\t\t\tname: \"same identifiers in different order\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"a@example.com\", \"b@example.com\", \"c@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"c@example.com\", \"a@example.com\", \"b@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\texpectEqual: true,\n\t\t\tdescription: \"hashes should be equal for same identifiers in different order\",\n\t\t},\n\t\t{\n\t\t\tname: \"different json config\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"different\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\texpectEqual: false,\n\t\t\tdescription: \"hashes should be different for different JSON content\",\n\t\t},\n\t\t{\n\t\t\tname: \"different types\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\texpectEqual: false,\n\t\t\tdescription: \"hashes should be different for different types\",\n\t\t},\n\t\t{\n\t\t\tname: \"different identifiers\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test1@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test2@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\texpectEqual: false,\n\t\t\tdescription: \"hashes should be different for different identifiers\",\n\t\t},\n\t\t{\n\t\t\tname: \"different versions\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     2,\n\t\t\t},\n\t\t\texpectEqual: false,\n\t\t\tdescription: \"hashes should be different for different versions\",\n\t\t},\n\t\t{\n\t\t\tname: \"different identity IDs\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t\tIdentityID:  baseID,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t\tIdentityID:  uuid.Must(uuid.NewV4()),\n\t\t\t},\n\t\t\texpectEqual: false,\n\t\t\tdescription: \"hashes should be different for different identity IDs\",\n\t\t},\n\t\t{\n\t\t\tname: \"different NIDs\",\n\t\t\tcred1: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t\tNID:         baseNID,\n\t\t\t},\n\t\t\tcred2: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"test@example.com\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\tVersion:     1,\n\t\t\t\tNID:         uuid.Must(uuid.NewV4()),\n\t\t\t},\n\t\t\texpectEqual: false,\n\t\t\tdescription: \"hashes should be different for different NIDs\",\n\t\t},\n\t} {\n\t\tt.Run(\"case=\"+tc.name, func(t *testing.T) {\n\t\t\thash1 := tc.cred1.Signature()\n\t\t\thash2 := tc.cred2.Signature()\n\n\t\t\tif tc.expectEqual {\n\t\t\t\tassert.Equal(t, hash1, hash2, tc.description)\n\t\t\t} else {\n\t\t\t\tassert.NotEqual(t, hash1, hash2, tc.description)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/credentials_totp.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\n// CredentialsConfig is the struct that is being used as part of the identity credentials.\ntype CredentialsTOTPConfig struct {\n\t// TOTPURL is the TOTP URL\n\t//\n\t// For more details see: https://github.com/google/google-authenticator/wiki/Key-Uri-Format\n\tTOTPURL string `json:\"totp_url\"`\n}\n"
  },
  {
    "path": "identity/credentials_webauthn.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"time\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\n\t\"github.com/ory/kratos/x/webauthnx/aaguid\"\n)\n\n// CredentialsWebAuthnConfig is the struct that is being used as part of the identity credentials.\ntype CredentialsWebAuthnConfig struct {\n\t// List of webauthn credentials.\n\tCredentials CredentialsWebAuthn `json:\"credentials\"`\n\tUserHandle  []byte              `json:\"user_handle\"`\n}\n\ntype CredentialsWebAuthn []CredentialWebAuthn\n\nfunc CredentialFromWebAuthn(credential *webauthn.Credential, isPasswordless bool) *CredentialWebAuthn {\n\tcred := &CredentialWebAuthn{\n\t\tID:              credential.ID,\n\t\tPublicKey:       credential.PublicKey,\n\t\tIsPasswordless:  isPasswordless,\n\t\tAttestationType: credential.AttestationType,\n\t\tAddedAt:         time.Now().UTC().Round(time.Second),\n\t\tAuthenticator: &AuthenticatorWebAuthn{\n\t\t\tAAGUID:       credential.Authenticator.AAGUID,\n\t\t\tSignCount:    credential.Authenticator.SignCount,\n\t\t\tCloneWarning: credential.Authenticator.CloneWarning,\n\t\t},\n\t\tFlags: &CredentialWebAuthnFlags{\n\t\t\tUserPresent:    credential.Flags.UserPresent,\n\t\t\tUserVerified:   credential.Flags.UserVerified,\n\t\t\tBackupEligible: credential.Flags.BackupEligible,\n\t\t\tBackupState:    credential.Flags.BackupState,\n\t\t},\n\t}\n\tid := aaguid.Lookup(credential.Authenticator.AAGUID)\n\tif id != nil {\n\t\tcred.DisplayName = id.Name\n\t}\n\n\treturn cred\n}\n\nfunc (c CredentialsWebAuthn) ToWebAuthn() (result []webauthn.Credential) {\n\tfor k := range c {\n\t\tresult = append(result, *c[k].ToWebAuthn())\n\t}\n\treturn result\n}\n\n// PasswordlessOnly returns only passwordless credentials.\nfunc (c CredentialsWebAuthn) PasswordlessOnly(authenticatorResponseFlags *protocol.AuthenticatorFlags) (result []webauthn.Credential) {\n\tfor k, cc := range c {\n\t\t// Upgrade path for legacy webauthn credentials. Only possible if we are handling a response from an authenticator.\n\t\tif c[k].Flags == nil && authenticatorResponseFlags != nil {\n\t\t\tc[k].Flags = &CredentialWebAuthnFlags{\n\t\t\t\tBackupEligible: authenticatorResponseFlags.HasBackupEligible(),\n\t\t\t\tBackupState:    authenticatorResponseFlags.HasBackupState(),\n\t\t\t}\n\t\t}\n\n\t\tif cc.IsPasswordless {\n\t\t\tresult = append(result, *c[k].ToWebAuthn())\n\t\t}\n\t}\n\treturn result\n}\n\n// ToWebAuthnFiltered returns only the appropriate credentials for the requested\n// AAL. For AAL1, only passwordless credentials are returned, for AAL2, only\n// non-passwordless credentials are returned.\n//\n// authenticatorResponseFlags should be passed  if the response is from an authenticator. It will be used to\n// upgrade legacy webauthn credentials' BackupEligible and BackupState flags.\nfunc (c CredentialsWebAuthn) ToWebAuthnFiltered(aal AuthenticatorAssuranceLevel, authenticatorResponseFlags *protocol.AuthenticatorFlags) (result []webauthn.Credential) {\n\tfor k, cc := range c {\n\t\t// Upgrade path for legacy webauthn credentials. Only possible if we are handling a response from an authenticator.\n\t\tif c[k].Flags == nil && authenticatorResponseFlags != nil {\n\t\t\tc[k].Flags = &CredentialWebAuthnFlags{\n\t\t\t\tBackupEligible: authenticatorResponseFlags.HasBackupEligible(),\n\t\t\t\tBackupState:    authenticatorResponseFlags.HasBackupState(),\n\t\t\t}\n\t\t}\n\n\t\tif (aal == AuthenticatorAssuranceLevel1 && cc.IsPasswordless) ||\n\t\t\t(aal == AuthenticatorAssuranceLevel2 && !cc.IsPasswordless) {\n\t\t\tresult = append(result, *c[k].ToWebAuthn())\n\t\t}\n\t}\n\treturn result\n}\n\nfunc (c *CredentialWebAuthn) ToWebAuthn() *webauthn.Credential {\n\twc := &webauthn.Credential{\n\t\tID:              c.ID,\n\t\tPublicKey:       c.PublicKey,\n\t\tAttestationType: c.AttestationType,\n\t\tTransport:       c.Transport,\n\t}\n\n\tif c.Authenticator != nil {\n\t\twc.Authenticator = webauthn.Authenticator{\n\t\t\tAAGUID:       c.Authenticator.AAGUID,\n\t\t\tSignCount:    c.Authenticator.SignCount,\n\t\t\tCloneWarning: c.Authenticator.CloneWarning,\n\t\t}\n\t}\n\n\tif c.Flags != nil {\n\t\twc.Flags = webauthn.CredentialFlags{\n\t\t\tUserPresent:    c.Flags.UserPresent,\n\t\t\tUserVerified:   c.Flags.UserVerified,\n\t\t\tBackupEligible: c.Flags.BackupEligible,\n\t\t\tBackupState:    c.Flags.BackupState,\n\t\t}\n\t}\n\n\tif c.Attestation != nil {\n\t\twc.Attestation = webauthn.CredentialAttestation{\n\t\t\tClientDataJSON:     c.Attestation.ClientDataJSON,\n\t\t\tClientDataHash:     c.Attestation.ClientDataHash,\n\t\t\tAuthenticatorData:  c.Attestation.AuthenticatorData,\n\t\t\tPublicKeyAlgorithm: c.Attestation.PublicKeyAlgorithm,\n\t\t\tObject:             c.Attestation.Object,\n\t\t}\n\t}\n\n\treturn wc\n}\n\ntype CredentialWebAuthn struct {\n\tID              []byte                            `json:\"id\"`\n\tPublicKey       []byte                            `json:\"public_key\"`\n\tAttestationType string                            `json:\"attestation_type\"`\n\tAuthenticator   *AuthenticatorWebAuthn            `json:\"authenticator,omitempty\"`\n\tDisplayName     string                            `json:\"display_name\"`\n\tAddedAt         time.Time                         `json:\"added_at\"`\n\tIsPasswordless  bool                              `json:\"is_passwordless\"`\n\tFlags           *CredentialWebAuthnFlags          `json:\"flags,omitempty\"`\n\tTransport       []protocol.AuthenticatorTransport `json:\"transport,omitempty\"`\n\tAttestation     *CredentialWebAuthnAttestation    `json:\"attestation,omitempty\"`\n}\n\ntype CredentialWebAuthnFlags struct {\n\tUserPresent    bool `json:\"user_present\"`\n\tUserVerified   bool `json:\"user_verified\"`\n\tBackupEligible bool `json:\"backup_eligible\"`\n\tBackupState    bool `json:\"backup_state\"`\n}\n\ntype CredentialWebAuthnAttestation struct {\n\tClientDataJSON     []byte `json:\"client_dataJSON\"`\n\tClientDataHash     []byte `json:\"client_data_hash\"`\n\tAuthenticatorData  []byte `json:\"authenticator_data\"`\n\tPublicKeyAlgorithm int64  `json:\"public_key_algorithm\"`\n\tObject             []byte `json:\"object\"`\n}\n\ntype AuthenticatorWebAuthn struct {\n\tAAGUID       []byte `json:\"aaguid\"`\n\tSignCount    uint32 `json:\"sign_count\"`\n\tCloneWarning bool   `json:\"clone_warning\"`\n}\n"
  },
  {
    "path": "identity/credentials_webauthn_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCredentialConversion(t *testing.T) {\n\texpected := &webauthn.Credential{\n\t\tID:              []byte(\"abcdef\"),\n\t\tPublicKey:       []byte(\"foobar\"),\n\t\tAttestationType: \"test\",\n\t\tAuthenticator: webauthn.Authenticator{\n\t\t\tAAGUID:       []byte(\"baz\"),\n\t\t\tSignCount:    1,\n\t\t\tCloneWarning: false,\n\t\t},\n\t}\n\n\tactual := CredentialFromWebAuthn(expected, false).ToWebAuthn()\n\tassert.Equal(t, expected, actual)\n\n\tactualList := CredentialsWebAuthn{*CredentialFromWebAuthn(expected, false)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel2, nil)\n\tassert.Equal(t, []webauthn.Credential{*expected}, actualList)\n\n\tactualList = CredentialsWebAuthn{*CredentialFromWebAuthn(expected, true)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel1, nil)\n\tassert.Equal(t, []webauthn.Credential{*expected}, actualList)\n\n\tactualList = CredentialsWebAuthn{*CredentialFromWebAuthn(expected, true)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel2, nil)\n\tassert.Len(t, actualList, 0)\n\n\tactualList = CredentialsWebAuthn{*CredentialFromWebAuthn(expected, false)}.ToWebAuthnFiltered(AuthenticatorAssuranceLevel1, nil)\n\tassert.Len(t, actualList, 0)\n\n\tfromWebAuthn := CredentialFromWebAuthn(expected, true)\n\tassert.True(t, fromWebAuthn.IsPasswordless)\n\tfromWebAuthn = CredentialFromWebAuthn(expected, false)\n\tassert.False(t, fromWebAuthn.IsPasswordless)\n\n\texpected.Authenticator.AAGUID = uuid.Must(uuid.FromString(\"ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4\")).Bytes()\n\tfromWebAuthn = CredentialFromWebAuthn(expected, false)\n\tassert.Equal(t, \"Google Password Manager\", fromWebAuthn.DisplayName)\n}\n\nfunc TestPasswordlessOnly(t *testing.T) {\n\ta := *CredentialFromWebAuthn(&webauthn.Credential{ID: []byte(\"a\")}, false)\n\tb := *CredentialFromWebAuthn(&webauthn.Credential{ID: []byte(\"b\")}, false)\n\tc := *CredentialFromWebAuthn(&webauthn.Credential{ID: []byte(\"c\")}, true)\n\td := *CredentialFromWebAuthn(&webauthn.Credential{ID: []byte(\"d\")}, false)\n\te := *CredentialFromWebAuthn(&webauthn.Credential{ID: []byte(\"e\")}, true)\n\texpected := CredentialsWebAuthn{a, b, c, d, e}\n\n\tactual := expected.PasswordlessOnly(nil)\n\trequire.Len(t, actual, 2)\n\tassert.Equal(t, []webauthn.Credential{*c.ToWebAuthn(), *e.ToWebAuthn()}, actual)\n}\n"
  },
  {
    "path": "identity/error_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestErrDuplicateCredentials(t *testing.T) {\n\tinner := errors.New(\"inner error\")\n\terr := &ErrDuplicateCredentials{inner, nil, nil, \"\"}\n\tassert.ErrorIs(t, err, inner)\n}\n"
  },
  {
    "path": "identity/expandables.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport \"github.com/ory/x/sqlxx\"\n\ntype Expandable = sqlxx.Expandable\ntype Expandables = sqlxx.Expandables\n\nconst (\n\tExpandFieldVerifiableAddresses Expandable = \"VerifiableAddresses\"\n\tExpandFieldRecoveryAddresses   Expandable = \"RecoveryAddresses\"\n\tExpandFieldCredentials         Expandable = \"Credentials\"\n)\n\n// ExpandNothing expands nothing\nvar ExpandNothing = Expandables{}\n\n// ExpandDefault expands the default fields:\n//\n// - Verifiable addresses\n// - Recovery addresses\nvar ExpandDefault = Expandables{\n\tExpandFieldVerifiableAddresses,\n\tExpandFieldRecoveryAddresses,\n}\n\n// ExpandCredentials expands the identity's credentials.\nvar ExpandCredentials = Expandables{\n\tExpandFieldCredentials,\n}\n\n// ExpandEverything expands all the fields of an identity.\nvar ExpandEverything = Expandables{\n\tExpandFieldVerifiableAddresses,\n\tExpandFieldRecoveryAddresses,\n\tExpandFieldCredentials,\n}\n"
  },
  {
    "path": "identity/extension_credentials.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/samber/lo\"\n\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/stringslice\"\n)\n\ntype SchemaExtensionCredentials struct {\n\ti         *Identity\n\tv         map[CredentialsType][]string\n\taddresses []CredentialsCodeAddress\n\tl         sync.Mutex\n}\n\nfunc NewSchemaExtensionCredentials(i *Identity) *SchemaExtensionCredentials {\n\treturn &SchemaExtensionCredentials{i: i}\n}\n\nfunc (r *SchemaExtensionCredentials) setIdentifier(ct CredentialsType, value interface{}) {\n\tcred, ok := r.i.GetCredentials(ct)\n\tif !ok {\n\t\tcred = &Credentials{\n\t\t\tType:        ct,\n\t\t\tIdentifiers: []string{},\n\t\t\tConfig:      sqlxx.JSONRawMessage{},\n\t\t}\n\t}\n\n\tif r.v == nil {\n\t\tr.v = make(map[CredentialsType][]string)\n\t}\n\n\tr.v[ct] = stringslice.Unique(append(r.v[ct], strings.ToLower(fmt.Sprintf(\"%s\", value))))\n\tcred.Identifiers = r.v[ct]\n\tr.i.SetCredentials(ct, *cred)\n}\n\nfunc (r *SchemaExtensionCredentials) Run(ctx jsonschema.ValidationContext, s schema.ExtensionConfig, value interface{}) error {\n\tr.l.Lock()\n\tdefer r.l.Unlock()\n\n\tif s.Credentials.Password.Identifier {\n\t\tr.setIdentifier(CredentialsTypePassword, value)\n\t}\n\n\tif s.Credentials.WebAuthn.Identifier {\n\t\tr.setIdentifier(CredentialsTypeWebAuthn, value)\n\t}\n\n\tif s.Credentials.Code.Identifier {\n\t\tvia, err := NewCodeChannel(s.Credentials.Code.Via)\n\t\tif err != nil {\n\t\t\treturn ctx.Error(\"ory.sh~/kratos/credentials/code/via\", \"channel type %q must be one of %s\", s.Credentials.Code.Via, strings.Join([]string{\n\t\t\t\tstring(CodeChannelEmail),\n\t\t\t\tstring(CodeChannelSMS),\n\t\t\t}, \", \"))\n\t\t}\n\n\t\tcred := r.i.GetCredentialsOr(CredentialsTypeCodeAuth, &Credentials{\n\t\t\tType:        CredentialsTypeCodeAuth,\n\t\t\tIdentifiers: []string{},\n\t\t\tConfig:      sqlxx.JSONRawMessage(\"{}\"),\n\t\t\tVersion:     1,\n\t\t})\n\n\t\tvar conf CredentialsCode\n\t\tconf.Addresses = r.addresses\n\t\tvalue, err := x.NormalizeIdentifier(fmt.Sprintf(\"%s\", value), string(via))\n\t\tif err != nil {\n\t\t\treturn &jsonschema.ValidationError{Message: err.Error()}\n\t\t}\n\n\t\tconf.Addresses = append(conf.Addresses, CredentialsCodeAddress{\n\t\t\tChannel: via,\n\t\t\tAddress: value,\n\t\t})\n\n\t\tconf.Addresses = lo.UniqBy(conf.Addresses, func(item CredentialsCodeAddress) string {\n\t\t\treturn fmt.Sprintf(\"%x:%s\", item.Address, item.Channel)\n\t\t})\n\n\t\tsort.SliceStable(conf.Addresses, func(i, j int) bool {\n\t\t\tif conf.Addresses[i].Address == conf.Addresses[j].Address {\n\t\t\t\treturn conf.Addresses[i].Channel < conf.Addresses[j].Channel\n\t\t\t}\n\t\t\treturn conf.Addresses[i].Address < conf.Addresses[j].Address\n\t\t})\n\n\t\tif r.v == nil {\n\t\t\tr.v = make(map[CredentialsType][]string)\n\t\t}\n\n\t\tr.v[CredentialsTypeCodeAuth] = stringslice.Unique(append(r.v[CredentialsTypeCodeAuth],\n\t\t\tlo.Map(conf.Addresses, func(item CredentialsCodeAddress, _ int) string {\n\t\t\t\treturn item.Address\n\t\t\t})...,\n\t\t))\n\t\tr.addresses = conf.Addresses\n\n\t\tcred.Identifiers = r.v[CredentialsTypeCodeAuth]\n\t\tcred.Config, err = json.Marshal(conf)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tr.i.SetCredentials(CredentialsTypeCodeAuth, *cred)\n\t}\n\n\treturn nil\n}\n\nfunc (r *SchemaExtensionCredentials) Finish() error {\n\treturn nil\n}\n"
  },
  {
    "path": "identity/extension_credentials_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity_test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t_ \"github.com/ory/jsonschema/v3/fileloader\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc TestSchemaExtensionCredentials(t *testing.T) {\n\tfor k, tc := range []struct {\n\t\texpectErr           error\n\t\tschema              string\n\t\tdoc                 string\n\t\texpectedIdentifiers []string\n\t\texisting            *identity.Credentials\n\t\tct                  identity.CredentialsType\n\t}{\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"foo@ory.sh\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/schema.json\",\n\t\t\texpectedIdentifiers: []string{\"foo@ory.sh\"},\n\t\t\tct:                  identity.CredentialsTypePassword,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"emails\":[\"foo@ory.sh\",\"foo@ory.sh\",\"bar@ory.sh\"], \"username\": \"foobar\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/multi.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"foo@ory.sh\", \"bar@ory.sh\", \"foobar\"},\n\t\t\tct:                  identity.CredentialsTypePassword,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"emails\":[\"foo@ory.sh\",\"foo@ory.sh\",\"bar@ory.sh\"], \"username\": \"foobar\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/multi.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"foo@ory.sh\", \"bar@ory.sh\"},\n\t\t\tct:                  identity.CredentialsTypeWebAuthn,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"emails\":[\"FOO@ory.sh\",\"bar@ory.sh\"], \"username\": \"foobar\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/multi.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"foo@ory.sh\", \"bar@ory.sh\", \"foobar\"},\n\t\t\texisting: &identity.Credentials{\n\t\t\t\tIdentifiers: []string{\"not-foo@ory.sh\"},\n\t\t\t},\n\t\t\tct: identity.CredentialsTypePassword,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"foo@ory.sh\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/webauthn.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"foo@ory.sh\"},\n\t\t\tct:                  identity.CredentialsTypeWebAuthn,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"FOO@ory.sh\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/webauthn.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"foo@ory.sh\"},\n\t\t\texisting: &identity.Credentials{\n\t\t\t\tIdentifiers: []string{\"not-foo@ory.sh\"},\n\t\t\t},\n\t\t\tct: identity.CredentialsTypeWebAuthn,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"foo@ory.sh\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/code.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"foo@ory.sh\"},\n\t\t\tct:                  identity.CredentialsTypeCodeAuth,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"FOO@ory.sh\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/code.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"foo@ory.sh\"},\n\t\t\texisting: &identity.Credentials{\n\t\t\t\tIdentifiers: []string{\"not-foo@ory.sh\"},\n\t\t\t},\n\t\t\tct: identity.CredentialsTypeCodeAuth,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"FOO@ory.sh\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/code.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"foo@ory.sh\"},\n\t\t\texisting: &identity.Credentials{\n\t\t\t\tIdentifiers: []string{\"not-foo@ory.sh\", \"foo@ory.sh\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"not-foo@ory.sh\"}]}`),\n\t\t\t},\n\t\t\tct: identity.CredentialsTypeCodeAuth,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"FOO@ory.sh\",\"phone\":\"+49 176 671 11 638\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/code-phone-email.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"+4917667111638\", \"foo@ory.sh\"},\n\t\t\texisting: &identity.Credentials{\n\t\t\t\tIdentifiers: []string{\"not-foo@ory.sh\", \"foo@ory.sh\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"not-foo@ory.sh\"}]}`),\n\t\t\t},\n\t\t\tct: identity.CredentialsTypeCodeAuth,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"FOO@ory.sh\",\"phone\":\"+49 176 671 11 638\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/code-phone-email.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"+4917667111638\", \"foo@ory.sh\"},\n\t\t\texisting: &identity.Credentials{\n\t\t\t\tIdentifiers: []string{\"not-foo@ory.sh\", \"foo@ory.sh\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"not-foo@ory.sh\"}]}`),\n\t\t\t},\n\t\t\tct: identity.CredentialsTypeCodeAuth,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"FOO@ory.sh\",\"email2\":\"FOO@ory.sh\",\"phone\":\"+49 176 671 11 638\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/code-phone-email.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"+4917667111638\", \"foo@ory.sh\"},\n\t\t\texisting: &identity.Credentials{\n\t\t\t\tIdentifiers: []string{\"not-foo@ory.sh\", \"fOo@ory.sh\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"not-foo@ory.sh\"}]}`),\n\t\t\t},\n\t\t\tct: identity.CredentialsTypeCodeAuth,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"FOO@ory.sh\",\"email2\":\"FOO@ory.sh\",\"email3\":\"bar@ory.sh\",\"phone\":\"+49 176 671 11 638\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/code-phone-email.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"+4917667111638\", \"foo@ory.sh\", \"bar@ory.sh\"},\n\t\t\texisting: &identity.Credentials{\n\t\t\t\tIdentifiers: []string{\"not-foo@ory.sh\", \"fOo@ory.sh\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"not-foo@ory.sh\"}]}`),\n\t\t\t},\n\t\t\tct: identity.CredentialsTypeCodeAuth,\n\t\t},\n\t\t{\n\t\t\tdoc:                 `{\"email\":\"bar@ory.sh\"}`,\n\t\t\tschema:              \"file://./stub/extension/credentials/email.schema.json\",\n\t\t\texpectedIdentifiers: []string{\"bar@ory.sh\"},\n\t\t\texisting: &identity.Credentials{\n\t\t\t\tIdentifiers: []string{\"foo@ory.sh\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"foo@ory.sh\"}]}`),\n\t\t\t},\n\t\t\tct: identity.CredentialsTypeCodeAuth,\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tc := jsonschema.NewCompiler()\n\t\t\trunner, err := schema.NewExtensionRunner(t.Context())\n\t\t\trequire.NoError(t, err)\n\n\t\t\ti := new(identity.Identity)\n\t\t\te := identity.NewSchemaExtensionCredentials(i)\n\t\t\tif tc.existing != nil {\n\t\t\t\ti.SetCredentials(tc.ct, *tc.existing)\n\t\t\t}\n\n\t\t\trunner.AddRunner(e).Register(c)\n\t\t\terr = c.MustCompile(t.Context(), tc.schema).Validate(bytes.NewBufferString(tc.doc))\n\t\t\tif tc.expectErr != nil {\n\t\t\t\trequire.EqualError(t, err, tc.expectErr.Error())\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}\n\t\t\trequire.NoError(t, e.Finish())\n\n\t\t\tcredentials, ok := i.GetCredentials(tc.ct)\n\t\t\trequire.True(t, ok)\n\t\t\tassert.ElementsMatch(t, tc.expectedIdentifiers, credentials.Identifiers)\n\t\t\tsnapshotx.SnapshotT(t, credentials, snapshotx.ExceptPaths(\"identifiers\"))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/extension_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"fmt\"\n\t\"maps\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\n\t\"github.com/ory/kratos/schema\"\n)\n\ntype SchemaExtensionRecovery struct {\n\tl sync.Mutex\n\tv []RecoveryAddress\n\ti *Identity\n}\n\nfunc NewSchemaExtensionRecovery(i *Identity) *SchemaExtensionRecovery {\n\treturn &SchemaExtensionRecovery{i: i}\n}\n\nfunc (r *SchemaExtensionRecovery) Run(ctx jsonschema.ValidationContext, s schema.ExtensionConfig, value interface{}) error {\n\tr.l.Lock()\n\tdefer r.l.Unlock()\n\n\tvar address *RecoveryAddress\n\tswitch s.Recovery.Via {\n\tcase \"email\":\n\t\tformatString := \"email\"\n\t\tformatter, ok := jsonschema.Formats[formatString]\n\t\tif !ok {\n\t\t\tsupportedKeys := slices.Collect(maps.Keys(jsonschema.Formats))\n\t\t\treturn ctx.Error(\"format\", \"format %q is not supported. Supported formats are [%s]\", formatString, strings.Join(supportedKeys, \", \"))\n\t\t}\n\n\t\tif !formatter(value) {\n\t\t\treturn ctx.Error(\"format\", \"%q is not valid %q\", value, formatString)\n\t\t}\n\n\t\taddress = NewRecoveryEmailAddress(\n\t\t\tstrings.ToLower(strings.TrimSpace(\n\t\t\t\tfmt.Sprintf(\"%s\", value))), r.i.ID)\n\n\tcase \"sms\":\n\t\tformatString := \"tel\"\n\t\tformatter, ok := jsonschema.Formats[formatString]\n\t\tif !ok {\n\t\t\tsupportedKeys := slices.Collect(maps.Keys(jsonschema.Formats))\n\t\t\treturn ctx.Error(\"format\", \"format %q is not supported. Supported formats are [%s]\", formatString, strings.Join(supportedKeys, \", \"))\n\t\t}\n\n\t\tif !formatter(value) {\n\t\t\treturn ctx.Error(\"format\", \"%q is not valid %q\", value, formatString)\n\t\t}\n\n\t\taddress = NewRecoverySMSAddress(\n\t\t\tstrings.TrimSpace(\n\t\t\t\tfmt.Sprintf(\"%s\", value)), r.i.ID)\n\n\tcase \"\":\n\t\treturn nil\n\tdefault:\n\t\treturn ctx.Error(\"\", \"recovery.via has unknown value %q\", s.Recovery.Via)\n\t}\n\n\tif has := r.has(r.i.RecoveryAddresses, address); has != nil {\n\t\tif r.has(r.v, address) == nil {\n\t\t\tr.v = append(r.v, *has)\n\t\t}\n\t\treturn nil\n\t}\n\n\tif has := r.has(r.v, address); has == nil {\n\t\tr.v = append(r.v, *address)\n\t}\n\n\treturn nil\n}\n\nfunc (r *SchemaExtensionRecovery) has(haystack []RecoveryAddress, needle *RecoveryAddress) *RecoveryAddress {\n\tfor _, has := range haystack {\n\t\tif has.Value == needle.Value && has.Via == needle.Via {\n\t\t\treturn &has\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (r *SchemaExtensionRecovery) Finish() error {\n\tr.i.RecoveryAddresses = r.v\n\treturn nil\n}\n"
  },
  {
    "path": "identity/extension_recovery_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t_ \"github.com/ory/jsonschema/v3/fileloader\"\n\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSchemaExtensionRecovery(t *testing.T) {\n\tiid := x.NewUUID()\n\tfor k, tc := range []struct {\n\t\texpectErr   error\n\t\tschema      string\n\t\tdoc         string\n\t\texpect      []RecoveryAddress\n\t\texisting    []RecoveryAddress\n\t\tdescription string\n\t}{\n\t\t{\n\t\t\tdescription: \"valid email, no existing\",\n\t\t\tdoc:         `{\"username\":\"foo@ory.sh\"}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/email.schema.json\",\n\t\t\texpect: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"valid email, some existing, no overlap\",\n\t\t\tdoc:         `{\"username\":\"foo@ory.sh\"}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/email.schema.json\",\n\t\t\texpect: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t\texisting: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"bar@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"valid emails, some existing, overlap\",\n\t\t\tdoc:         `{\"emails\":[\"baz@ory.sh\",\"foo@ory.sh\"]}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/email.schema.json\",\n\t\t\texpect: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tValue:      \"baz@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t\texisting: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tValue:      \"bar@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"valid emails, no existing, overlap\",\n\t\t\tdoc:         `{\"emails\":[\"foo@ory.sh\",\"foo@ory.sh\",\"baz@ory.sh\"]}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/email.schema.json\",\n\t\t\texpect: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tValue:      \"baz@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t\texisting: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tValue:      \"bar@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"invalid email\",\n\t\t\tdoc:         `{\"emails\":[\"foo@ory.sh\",\"bar@ory.sh\"], \"username\": \"foobar\"}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/email.schema.json\",\n\t\t\texpectErr:   errors.New(\"I[#/username] S[#/properties/username/format] \\\"foobar\\\" is not valid \\\"email\\\"\"),\n\t\t},\n\t\t{\n\t\t\tdescription: \"valid emails, no existing\",\n\t\t\tdoc:         `{\"emails\":[\"foo@ory.sh\",\"bar@ory.sh\",\"bar@ory.sh\"], \"username\": \"foobar@ory.sh\"}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/email.schema.json\",\n\t\t\texpect: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tValue:      \"bar@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tValue:      \"foobar@ory.sh\",\n\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\n\t\t{\n\t\t\tdescription: \"valid phone number, no existing\",\n\t\t\tdoc:         `{\"telephoneNumber\":\"+68672098006\"}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/sms.schema.json\",\n\t\t\texpect: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"+68672098006\",\n\t\t\t\t\tVia:        AddressTypeSMS,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"valid phone number, some existing, no overlap\",\n\t\t\tdoc:         `{\"telephoneNumber\":\"+68672098006\"}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/sms.schema.json\",\n\t\t\texpect: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"+68672098006\",\n\t\t\t\t\tVia:        AddressTypeSMS,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t\texisting: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"+12 345 67890123\",\n\t\t\t\t\tVia:        AddressTypeSMS,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"valid phone number, some existing, overlap\",\n\t\t\tdoc:         `{\"telephoneNumber\":\"+68672098006\"}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/sms.schema.json\",\n\t\t\texpect: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"+68672098006\",\n\t\t\t\t\tVia:        AddressTypeSMS,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t\texisting: []RecoveryAddress{\n\t\t\t\t{\n\t\t\t\t\tValue:      \"+68672098006\",\n\t\t\t\t\tVia:        AddressTypeSMS,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tValue:      \"+33 07856952\",\n\t\t\t\t\tVia:        AddressTypeSMS,\n\t\t\t\t\tIdentityID: iid,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"invalid phone number\",\n\t\t\tdoc:         `{\"telephoneNumber\": \"foobar\"}`,\n\t\t\tschema:      \"file://./stub/extension/recovery/sms.schema.json\",\n\t\t\t// We get 2 errors: one from the JSON schema `format` validation and one from the Go validation.\n\t\t\texpectErr: errors.New(\"I[#/telephoneNumber] S[#/properties/telephoneNumber] validation failed\\n  I[#/telephoneNumber] S[#/properties/telephoneNumber/format] \\\"foobar\\\" is not valid \\\"tel\\\"\\n  I[#/telephoneNumber] S[#/properties/telephoneNumber/format] \\\"foobar\\\" is not valid \\\"tel\\\"\"),\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d description=%s\", k, tc.description), func(t *testing.T) {\n\t\t\tid := &Identity{ID: iid, RecoveryAddresses: tc.existing}\n\t\t\tc := jsonschema.NewCompiler()\n\t\t\trunner, err := schema.NewExtensionRunner(t.Context())\n\t\t\trequire.NoError(t, err)\n\n\t\t\te := NewSchemaExtensionRecovery(id)\n\t\t\trunner.AddRunner(e).Register(c)\n\n\t\t\terr = c.MustCompile(t.Context(), tc.schema).Validate(bytes.NewBufferString(tc.doc))\n\t\t\tif tc.expectErr != nil {\n\t\t\t\trequire.EqualError(t, err, tc.expectErr.Error())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.NoError(t, e.Finish())\n\n\t\t\taddresses := id.RecoveryAddresses\n\t\t\trequire.Len(t, addresses, len(tc.expect))\n\n\t\t\tfor _, actual := range addresses {\n\t\t\t\tvar found bool\n\t\t\t\tfor _, expect := range tc.expect {\n\t\t\t\t\tif reflect.DeepEqual(actual, expect) {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassert.True(t, found, \"%+v not in %+v\", actual, tc.expect)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/extension_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"fmt\"\n\t\"maps\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/schema\"\n)\n\nfunc init() {\n\tjsonschema.Formats[\"no-validate\"] = func(v interface{}) bool {\n\t\treturn true\n\t}\n}\n\ntype SchemaExtensionVerification struct {\n\tlifespan time.Duration\n\tl        sync.Mutex\n\tv        []VerifiableAddress\n\ti        *Identity\n}\n\nfunc NewSchemaExtensionVerification(i *Identity, lifespan time.Duration) *SchemaExtensionVerification {\n\treturn &SchemaExtensionVerification{i: i, lifespan: lifespan}\n}\n\nconst (\n\tChannelTypeEmail = \"email\"\n\tChannelTypeSMS   = \"sms\"\n)\n\nfunc (r *SchemaExtensionVerification) Run(ctx jsonschema.ValidationContext, s schema.ExtensionConfig, value interface{}) error {\n\tr.l.Lock()\n\tdefer r.l.Unlock()\n\n\tif s.Verification.Via == \"\" {\n\t\treturn nil\n\t}\n\n\tformat, ok := s.RawSchema[\"format\"]\n\tif !ok {\n\t\tformat = \"\"\n\t}\n\tformatString, ok := format.(string)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tif formatString == \"\" {\n\t\tswitch s.Verification.Via {\n\t\tcase ChannelTypeEmail:\n\t\t\tformatString = \"email\"\n\t\t\tformatter, ok := jsonschema.Formats[formatString]\n\t\t\tif !ok {\n\t\t\t\tsupportedKeys := slices.Collect(maps.Keys(jsonschema.Formats))\n\t\t\t\treturn ctx.Error(\"format\", \"format %q is not supported. Supported formats are [%s]\", formatString, strings.Join(supportedKeys, \", \"))\n\t\t\t}\n\n\t\t\tif !formatter(value) {\n\t\t\t\treturn ctx.Error(\"format\", \"%q is not valid %q\", value, formatString)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn ctx.Error(\"format\", \"no format specified. A format is required if verification is enabled. If this was intentional, please set \\\"format\\\" to \\\"no-validate\\\"\")\n\t\t}\n\t}\n\n\tvar normalized string\n\tswitch formatString {\n\tcase \"email\":\n\t\tnormalized = strings.ToLower(strings.TrimSpace(fmt.Sprintf(\"%s\", value)))\n\tdefault:\n\t\tnormalized = strings.TrimSpace(fmt.Sprintf(\"%s\", value))\n\t}\n\n\taddress := NewVerifiableAddress(normalized, r.i.ID, s.Verification.Via)\n\tr.appendAddress(address)\n\treturn nil\n}\n\nfunc (r *SchemaExtensionVerification) Finish() error {\n\tr.i.VerifiableAddresses = merge(r.v, r.i.VerifiableAddresses)\n\treturn nil\n}\n\n// merge merges the base with the overrides through comparison with `has`. It changes the base slice in place.\nfunc merge(base []VerifiableAddress, overrides []VerifiableAddress) []VerifiableAddress {\n\tfor i := range base {\n\t\tif override := has(overrides, &base[i]); override != nil {\n\t\t\tbase[i] = *override\n\t\t}\n\t}\n\n\treturn base\n}\n\nfunc (r *SchemaExtensionVerification) appendAddress(address *VerifiableAddress) {\n\tif h := has(r.i.VerifiableAddresses, address); h != nil {\n\t\tif has(r.v, address) == nil {\n\t\t\tr.v = append(r.v, *h)\n\t\t}\n\t\treturn\n\t}\n\n\tif has(r.v, address) == nil {\n\t\tr.v = append(r.v, *address)\n\t}\n}\n\nfunc has(haystack []VerifiableAddress, needle *VerifiableAddress) *VerifiableAddress {\n\tfor _, has := range haystack {\n\t\tif has.Value == needle.Value && has.Via == needle.Via {\n\t\t\treturn &has\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "identity/extension_verification_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t_ \"github.com/ory/jsonschema/v3/fileloader\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/x\"\n)\n\nconst (\n\temailSchemaPath                    = \"file://./stub/extension/verify/email.schema.json\"\n\tphoneSchemaPath                    = \"file://./stub/extension/verify/phone.schema.json\"\n\tmissingFormatSchemaPath            = \"file://./stub/extension/verify/missing-format.schema.json\"\n\tlegacyEmailMissingFormatSchemaPath = \"file://./stub/extension/verify/legacy-email-missing-format.schema.json\"\n\tnoValidateSchemaPath               = \"file://./stub/extension/verify/no-validate.schema.json\"\n)\n\nfunc TestSchemaExtensionVerification(t *testing.T) {\n\tnet.IP{}.IsPrivate()\n\tt.Run(\"address verification\", func(t *testing.T) {\n\t\tiid := x.NewUUID()\n\n\t\tfor _, tc := range []struct {\n\t\t\tname      string\n\t\t\tschema    string\n\t\t\texpectErr error\n\t\t\tdoc       string\n\t\t\texisting  []VerifiableAddress\n\t\t\texpect    []VerifiableAddress\n\t\t}{\n\t\t\t{\n\t\t\t\tname:   \"email:must create new address\",\n\t\t\t\tschema: emailSchemaPath,\n\t\t\t\tdoc:    `{\"username\":\"foo@ory.sh\"}`,\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"email:must create new address because new and existing doesn't match\",\n\t\t\t\tschema: emailSchemaPath,\n\t\t\t\tdoc:    `{\"username\":\"foo@ory.sh\"}`,\n\t\t\t\texisting: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"bar@ory.sh\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"email:must find existing address in case of match\",\n\t\t\t\tschema: emailSchemaPath,\n\t\t\t\tdoc:    `{\"emails\":[\"baz@ory.sh\",\"foo@ory.sh\"]}`,\n\t\t\t\texisting: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"bar@ory.sh\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"baz@ory.sh\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"email:must return only one address in case of duplication\",\n\t\t\t\tschema: emailSchemaPath,\n\t\t\t\tdoc:    `{\"emails\":[\"foo@ory.sh\",\"foo@ory.sh\",\"baz@ory.sh\"]}`,\n\t\t\t\texisting: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"bar@ory.sh\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"baz@ory.sh\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:      \"email:must return error for malformed input\",\n\t\t\t\tschema:    emailSchemaPath,\n\t\t\t\tdoc:       `{\"emails\":[\"foo@ory.sh\",\"bar@ory.sh\"], \"username\": \"foobar\"}`,\n\t\t\t\texpectErr: errors.New(\"I[#/username] S[#/properties/username/format] \\\"foobar\\\" is not valid \\\"email\\\"\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"email:must merge email addresses from multiple attributes\",\n\t\t\t\tschema: emailSchemaPath,\n\t\t\t\tdoc:    `{\"emails\":[\"foo@ory.sh\",\"bar@ory.sh\",\"bar@ory.sh\"], \"username\": \"foobar@ory.sh\"}`,\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"foo@ory.sh\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"bar@ory.sh\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"foobar@ory.sh\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"phone:must create new address\",\n\t\t\t\tschema: phoneSchemaPath,\n\t\t\t\tdoc:    `{\"username\":\"+18004444444\"}`,\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+18004444444\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"phone:must create new address because new and existing doesn't match\",\n\t\t\t\tschema: phoneSchemaPath,\n\t\t\t\tdoc:    `{\"username\":\"+18004444444\"}`,\n\t\t\t\texisting: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+442087599036\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+18004444444\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"phone:must find existing addresses in case of match\",\n\t\t\t\tschema: phoneSchemaPath,\n\t\t\t\tdoc:    `{\"phones\":[\"+18004444444\",\"+442087599036\"]}`,\n\t\t\t\texisting: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+442087599036\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+380634872774\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+442087599036\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+18004444444\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"phone:must return only one address in case of duplication\",\n\t\t\t\tschema: phoneSchemaPath,\n\t\t\t\tdoc:    `{\"phones\": [\"+18004444444\",\"+18004444444\",\"+442087599036\"]}`,\n\t\t\t\texisting: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+18004444444\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+380634872774\",\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusCompleted,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+18004444444\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+442087599036\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"phone:must merge phones from multiple attributes\",\n\t\t\t\tschema: phoneSchemaPath,\n\t\t\t\tdoc:    `{\"phones\": [\"+18004444444\",\"+18004444444\",\"+442087599036\"], \"username\": \"+380634872774\"}`,\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+18004444444\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+442087599036\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+380634872774\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:      \"phone:must return error for malformed input\",\n\t\t\t\tschema:    phoneSchemaPath,\n\t\t\t\tdoc:       `{\"phones\":[\"+18004444444\",\"+18004444444\",\"12112112\"], \"username\": \"+380634872774\"}`,\n\t\t\t\texpectErr: errors.New(\"I[#/phones/2] S[#/properties/phones/items/format] \\\"12112112\\\" is not valid \\\"tel\\\"\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:      \"missing format returns an error\",\n\t\t\t\tschema:    missingFormatSchemaPath,\n\t\t\t\tdoc:       `{\"phone\": \"+380634872774\"}`,\n\t\t\t\texpectErr: errors.New(\"I[#/phone] S[#/properties/phone/format] no format specified. A format is required if verification is enabled. If this was intentional, please set \\\"format\\\" to \\\"no-validate\\\"\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"missing format works for email if format is missing\",\n\t\t\t\tschema: legacyEmailMissingFormatSchemaPath,\n\t\t\t\tdoc:    `{\"email\": \"user@ory.sh\"}`,\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"user@ory.sh\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeEmail,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:   \"format: no-validate works\",\n\t\t\t\tschema: noValidateSchemaPath,\n\t\t\t\tdoc:    `{\"phone\": \"not a phone number\"}`,\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"not a phone number\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t// see https://github.com/ory/kratos/issues/3933\n\t\t\t\tname:   \"phone:should parse +16453331111\",\n\t\t\t\tschema: phoneSchemaPath,\n\t\t\t\tdoc:    `{\"phones\":[\"+16453331111\"]}`,\n\t\t\t\texpect: []VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tValue:      \"+16453331111\",\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\t\t\tVia:        ChannelTypeSMS,\n\t\t\t\t\t\tIdentityID: iid,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%v\", tc.name), func(t *testing.T) {\n\t\t\t\tid := &Identity{ID: iid, VerifiableAddresses: tc.existing}\n\n\t\t\t\tc := jsonschema.NewCompiler()\n\n\t\t\t\trunner, err := schema.NewExtensionRunner(t.Context())\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\te := NewSchemaExtensionVerification(id, time.Minute)\n\t\t\t\trunner.AddRunner(e).Register(c)\n\n\t\t\t\terr = c.MustCompile(t.Context(), tc.schema).Validate(bytes.NewBufferString(tc.doc))\n\t\t\t\tif tc.expectErr != nil {\n\t\t\t\t\trequire.EqualError(t, err, tc.expectErr.Error())\n\t\t\t\t\treturn\n\t\t\t\t} else {\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, e.Finish())\n\n\t\t\t\taddresses := id.VerifiableAddresses\n\t\t\t\trequire.Len(t, addresses, len(tc.expect))\n\n\t\t\t\tmustContainAddress(t, tc.expect, addresses)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc mustContainAddress(t *testing.T, expected, actual []VerifiableAddress) {\n\tfor _, act := range actual {\n\t\tvar found bool\n\t\tfor _, expect := range expected {\n\t\t\tif reflect.DeepEqual(act, expect) {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tassert.True(t, found, \"%+v not in %+v\", act, expected)\n\t}\n}\n\nfunc TestMergeVerifiableAddresses(t *testing.T) {\n\tfor _, tt := range []struct {\n\t\tname                      string\n\t\tbase, overrides, expected []VerifiableAddress\n\t}{\n\t\t{\n\t\t\tname: \"empty base\",\n\t\t\tbase: []VerifiableAddress{},\n\t\t\toverrides: []VerifiableAddress{{\n\t\t\t\tValue: \"override@ory.sh\",\n\t\t\t\tVia:   \"email\",\n\t\t\t}},\n\t\t\texpected: []VerifiableAddress{},\n\t\t}, {\n\t\t\tname: \"no overlap\",\n\t\t\tbase: []VerifiableAddress{{\n\t\t\t\tValue: \"base@ory.sh\",\n\t\t\t\tVia:   \"email\",\n\t\t\t}},\n\t\t\toverrides: []VerifiableAddress{{\n\t\t\t\tValue: \"override@ory.sh\",\n\t\t\t\tVia:   \"email\",\n\t\t\t}},\n\t\t\texpected: []VerifiableAddress{{\n\t\t\t\tValue: \"base@ory.sh\",\n\t\t\t\tVia:   \"email\",\n\t\t\t}},\n\t\t}, {\n\t\t\tname: \"overrides\",\n\t\t\tbase: []VerifiableAddress{\n\t\t\t\t{Value: \"base@ory.sh\", Via: \"email\"},\n\t\t\t\t{Value: \"common-1-is-overwritten@ory.sh\", Via: \"email\"},\n\t\t\t\t{Value: \"common-2-is-ignored@ory.sh\", Via: \"no match\"},\n\t\t\t},\n\t\t\toverrides: []VerifiableAddress{\n\t\t\t\t{Value: \"common-1-is-overwritten@ory.sh\", Via: \"email\", Verified: true, Status: VerifiableAddressStatusCompleted},\n\t\t\t\t{Value: \"common-2-is-ignored@ory.sh\", Via: \"wrong via\"},\n\t\t\t\t{Value: \"override-only-is-ignored@ory.sh\", Via: \"email\"},\n\t\t\t},\n\t\t\texpected: []VerifiableAddress{\n\t\t\t\t{Value: \"base@ory.sh\", Via: \"email\"},\n\t\t\t\t{Value: \"common-1-is-overwritten@ory.sh\", Via: \"email\", Verified: true, Status: VerifiableAddressStatusCompleted},\n\t\t\t\t{Value: \"common-2-is-ignored@ory.sh\", Via: \"no match\"},\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(\"case=\"+tt.name, func(t *testing.T) {\n\t\t\tassert.Equal(t, tt.expected, merge(tt.base, tt.overrides))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/crdbx\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\n\t\"github.com/ory/x/pagination/migrationpagination\"\n\t\"github.com/ory/x/pagination/pagepagination\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/ory/kratos/cipher\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/jsonx\"\n\t\"github.com/ory/x/openapix\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n)\n\nconst (\n\tRouteCollection     = \"/identities\"\n\tRouteItem           = RouteCollection + \"/{id}\"\n\tRouteCredentialItem = RouteItem + \"/credentials/{type}\"\n\n\tBatchPatchIdentitiesLimit             = 1000\n\tBatchPatchIdentitiesWithPasswordLimit = 200\n)\n\ntype (\n\tdependencies interface {\n\t\tPoolProvider\n\t\tPrivilegedPoolProvider\n\t\tManagementProvider\n\t\thttpx.WriterProvider\n\t\tconfig.Provider\n\t\tnosurfx.CSRFProvider\n\t\tcipher.Provider\n\t\thash.HashProvider\n\t}\n\tHandlerProvider interface {\n\t\tIdentityHandler() *Handler\n\t}\n\tHandler struct{ r dependencies }\n)\n\nfunc NewHandler(r dependencies) *Handler { return &Handler{r: r} }\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\th.r.CSRFHandler().IgnoreGlobs(\n\t\tRouteCollection,\n\t\tRouteCollection+\"/*\",\n\t\tRouteCollection+\"/*/credentials/*\",\n\t\thttprouterx.AdminPrefix+RouteCollection,\n\t\thttprouterx.AdminPrefix+RouteCollection+\"/*\",\n\t\thttprouterx.AdminPrefix+RouteCollection+\"/*/credentials/*\",\n\t)\n\n\tpublic.GET(RouteCollection, redir.RedirectToAdminRoute(h.r))\n\tpublic.GET(RouteCollection+\"/by/external/{externalID}\", redir.RedirectToAdminRoute(h.r))\n\tpublic.GET(RouteItem, redir.RedirectToAdminRoute(h.r))\n\tpublic.DELETE(RouteItem, redir.RedirectToAdminRoute(h.r))\n\tpublic.POST(RouteCollection, redir.RedirectToAdminRoute(h.r))\n\tpublic.PUT(RouteItem, redir.RedirectToAdminRoute(h.r))\n\tpublic.PATCH(RouteItem, redir.RedirectToAdminRoute(h.r))\n\tpublic.DELETE(RouteCredentialItem, redir.RedirectToAdminRoute(h.r))\n\n\tpublic.GET(httprouterx.AdminPrefix+RouteCollection, redir.RedirectToAdminRoute(h.r))\n\tpublic.GET(httprouterx.AdminPrefix+RouteCollection+\"/by/external/{externalID}\", redir.RedirectToAdminRoute(h.r))\n\tpublic.GET(httprouterx.AdminPrefix+RouteItem, redir.RedirectToAdminRoute(h.r))\n\tpublic.DELETE(httprouterx.AdminPrefix+RouteItem, redir.RedirectToAdminRoute(h.r))\n\tpublic.POST(httprouterx.AdminPrefix+RouteCollection, redir.RedirectToAdminRoute(h.r))\n\tpublic.PUT(httprouterx.AdminPrefix+RouteItem, redir.RedirectToAdminRoute(h.r))\n\tpublic.PATCH(httprouterx.AdminPrefix+RouteItem, redir.RedirectToAdminRoute(h.r))\n\tpublic.DELETE(httprouterx.AdminPrefix+RouteCredentialItem, redir.RedirectToAdminRoute(h.r))\n}\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(RouteCollection, h.list)\n\tadmin.GET(RouteItem, h.get)\n\tadmin.GET(RouteCollection+\"/by/external/{externalID}\", h.getByExternalID)\n\tadmin.DELETE(RouteItem, h.delete)\n\tadmin.PATCH(RouteItem, h.patch)\n\n\tadmin.POST(RouteCollection, h.create)\n\tadmin.PATCH(RouteCollection, h.batchPatchIdentities)\n\tadmin.PUT(RouteItem, h.update)\n\n\tadmin.DELETE(RouteCredentialItem, h.deleteIdentityCredentials)\n}\n\n// Paginated Identity List Response\n//\n// swagger:response listIdentities\ntype _ struct {\n\tmigrationpagination.ResponseHeaderAnnotation\n\n\t// List of identities\n\t//\n\t// in:body\n\tBody []Identity\n}\n\n// Paginated List Identity Parameters\n//\n// Note: Filters cannot be combined.\n//\n// swagger:parameters listIdentities\ntype _ struct {\n\tmigrationpagination.RequestParameters\n\n\t// Retrieve multiple identities by their IDs.\n\t//\n\t// This parameter has the following limitations:\n\t//\n\t// - Duplicate or non-existent IDs are ignored.\n\t// - The order of returned IDs may be different from the request.\n\t// - This filter does not support pagination. You must implement your own pagination as the maximum number of items returned by this endpoint may not exceed a certain threshold (currently 500).\n\t//\n\t// required: false\n\t// in: query\n\tIdsFilter []string `json:\"ids\"`\n\n\t// CredentialsIdentifier is the identifier (username, email) of the credentials to look up using exact match.\n\t// Only one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\n\t//\n\t// required: false\n\t// in: query\n\tCredentialsIdentifier string `json:\"credentials_identifier\"`\n\n\t// This is an EXPERIMENTAL parameter that WILL CHANGE. Do NOT rely on consistent, deterministic behavior.\n\t// THIS PARAMETER WILL BE REMOVED IN AN UPCOMING RELEASE WITHOUT ANY MIGRATION PATH.\n\t//\n\t// CredentialsIdentifierSimilar is the (partial) identifier (username, email) of the credentials to look up using similarity search.\n\t// Only one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\n\t//\n\t// required: false\n\t// in: query\n\tCredentialsIdentifierSimilar string `json:\"preview_credentials_identifier_similar\"`\n\n\t// Include Credentials in Response\n\t//\n\t// Include any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\n\t// the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\n\t//\n\t// required: false\n\t// in: query\n\tDeclassifyCredentials []string `json:\"include_credential\"`\n\n\t// List identities that belong to a specific organization.\n\t//\n\t// required: false\n\t// in: query\n\tOrganizationID string `json:\"organization_id\"`\n\n\tcrdbx.ConsistencyRequestParameters\n}\n\nfunc parseListIdentitiesParameters(r *http.Request) (params ListIdentityParameters, err error) {\n\tquery := r.URL.Query()\n\tvar requestedFilters int\n\n\tparams.Expand = ExpandDefault\n\n\tif ids := query[\"ids\"]; len(ids) > 0 {\n\t\trequestedFilters++\n\t\tfor _, v := range ids {\n\t\t\tid, err := uuid.FromString(v)\n\t\t\tif err != nil {\n\t\t\t\treturn params, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Invalid UUID value `%s` for parameter `ids`.\", v))\n\t\t\t}\n\t\t\tparams.IdsFilter = append(params.IdsFilter, id)\n\t\t}\n\t}\n\tif len(params.IdsFilter) > 500 {\n\t\treturn params, errors.WithStack(herodot.ErrBadRequest.WithReason(\"The number of ids to filter must not exceed 500.\"))\n\t}\n\n\tif orgID := query.Get(\"organization_id\"); orgID != \"\" {\n\t\trequestedFilters++\n\t\tparams.OrganizationID, err = uuid.FromString(orgID)\n\t\tif err != nil {\n\t\t\treturn params, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Invalid UUID value `%s` for parameter `organization_id`.\", orgID))\n\t\t}\n\t}\n\n\tif identifier := query.Get(\"credentials_identifier\"); identifier != \"\" {\n\t\trequestedFilters++\n\t\tparams.Expand = ExpandEverything\n\t\tparams.CredentialsIdentifier = identifier\n\t}\n\n\tif identifier := query.Get(\"preview_credentials_identifier_similar\"); identifier != \"\" {\n\t\trequestedFilters++\n\t\tparams.Expand = ExpandEverything\n\t\tparams.CredentialsIdentifierSimilar = identifier\n\t}\n\n\tfor _, v := range query[\"include_credential\"] {\n\t\tparams.Expand = ExpandEverything\n\t\ttc, ok := ParseCredentialsType(v)\n\t\tif !ok {\n\t\t\treturn params, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Invalid value `%s` for parameter `include_credential`.\", v))\n\t\t}\n\t\tparams.DeclassifyCredentials = append(params.DeclassifyCredentials, tc)\n\t}\n\n\tif requestedFilters > 1 {\n\t\treturn params, errors.WithStack(herodot.ErrBadRequest.WithReason(\"You cannot combine multiple filters in this API\"))\n\t}\n\n\tparams.KeySetPagination, params.PagePagination, err = x.ParseKeysetOrPagePagination(r)\n\tif err != nil {\n\t\treturn params, err\n\t}\n\tparams.ConsistencyLevel = crdbx.ConsistencyLevelFromRequest(r)\n\n\treturn params, nil\n}\n\n// swagger:route GET /admin/identities identity listIdentities\n//\n// # List Identities\n//\n// Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: listIdentities\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-medium\nfunc (h *Handler) list(w http.ResponseWriter, r *http.Request) {\n\tparams, err := parseListIdentitiesParameters(r)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tis, nextPage, err := h.r.IdentityPool().ListIdentities(r.Context(), params)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif params.PagePagination != nil {\n\t\ttotal := int64(len(is))\n\t\tif params.CredentialsIdentifier == \"\" {\n\t\t\ttotal, err = h.r.IdentityPool().CountIdentities(r.Context())\n\t\t\tif err != nil {\n\t\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tu := *r.URL\n\t\tpagepagination.PaginationHeader(w, &u, total, params.PagePagination.Page, params.PagePagination.ItemsPerPage)\n\t} else if nextPage != nil {\n\t\tu := *r.URL\n\t\tkeysetpagination.Header(w, &u, nextPage)\n\t}\n\n\t// Identities using the marshaler for including metadata_admin\n\tisam := make([]WithCredentialsAndAdminMetadataInJSON, len(is))\n\tfor i, identity := range is {\n\t\temit, err := identity.WithDeclassifiedCredentials(r.Context(), h.r, params.DeclassifyCredentials)\n\t\tif err != nil {\n\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\n\t\tisam[i] = WithCredentialsAndAdminMetadataInJSON(*emit)\n\t}\n\n\th.r.Writer().Write(w, r, isam)\n}\n\n// Get Identity Parameters\n//\n// swagger:parameters getIdentity\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getIdentity struct {\n\t// ID must be set to the ID of identity you want to get\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n\n\t// Include Credentials in Response\n\t//\n\t// Include any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\n\t// the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\n\t//\n\t// required: false\n\t// in: query\n\tDeclassifyCredentials []CredentialsType `json:\"include_credential\"`\n}\n\n// Get Identity By External ID Parameters\n//\n// swagger:parameters getIdentityByExternalID\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getIdentityByExternalID struct {\n\t// ExternalID must be set to the ID of identity you want to get\n\t//\n\t// required: true\n\t// in: path\n\tExternalID string `json:\"externalID\"`\n\n\t// Include Credentials in Response\n\t//\n\t// Include any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\n\t// the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\n\t//\n\t// required: false\n\t// in: query\n\tDeclassifyCredentials []CredentialsType `json:\"include_credential\"`\n}\n\n// swagger:route GET /admin/identities/{id} identity getIdentity\n//\n// # Get an Identity\n//\n// Return an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its ID. You can optionally\n// include credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: identity\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-low\nfunc (h *Handler) get(w http.ResponseWriter, r *http.Request) {\n\ti, err := h.r.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), x.ParseUUID(r.PathValue(\"id\")))\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tincludeCredentials := r.URL.Query()[\"include_credential\"]\n\tvar declassify []CredentialsType\n\tfor _, v := range includeCredentials {\n\t\ttc, ok := ParseCredentialsType(v)\n\t\tif ok {\n\t\t\tdeclassify = append(declassify, tc)\n\t\t} else {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Invalid value `%s` for parameter `include_credential`.\", declassify)))\n\t\t\treturn\n\t\t}\n\t}\n\n\temit, err := i.WithDeclassifiedCredentials(r.Context(), h.r, declassify)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\th.r.Writer().Write(w, r, WithCredentialsAndAdminMetadataInJSON(*emit))\n}\n\n// swagger:route GET /admin/identities/by/external/{externalID} identity getIdentityByExternalID\n//\n// # Get an Identity by its External ID\n//\n// Return an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its external ID. You can optionally\n// include credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: identity\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-medium\nfunc (h *Handler) getByExternalID(w http.ResponseWriter, r *http.Request) {\n\texternalID := r.PathValue(\"externalID\")\n\tif externalID == \"\" {\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReason(\"The external ID must not be empty.\")))\n\t\treturn\n\t}\n\ti, err := h.r.PrivilegedIdentityPool().FindIdentityByExternalID(r.Context(), externalID, ExpandEverything)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tincludeCredentials := r.URL.Query()[\"include_credential\"]\n\tvar declassify []CredentialsType\n\tfor _, v := range includeCredentials {\n\t\ttc, ok := ParseCredentialsType(v)\n\t\tif ok {\n\t\t\tdeclassify = append(declassify, tc)\n\t\t} else {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Invalid value `%s` for parameter `include_credential`.\", declassify)))\n\t\t\treturn\n\t\t}\n\t}\n\n\temit, err := i.WithDeclassifiedCredentials(r.Context(), h.r, declassify)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\th.r.Writer().Write(w, r, WithCredentialsAndAdminMetadataInJSON(*emit))\n}\n\n// Create Identity Parameters\n//\n// swagger:parameters createIdentity\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createIdentity struct {\n\t// in: body\n\tBody CreateIdentityBody\n}\n\n// Create Identity Body\n//\n// swagger:model createIdentityBody\ntype CreateIdentityBody struct {\n\t// SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\n\t//\n\t// required: true\n\tSchemaID string `json:\"schema_id\"`\n\n\t// Traits represent an identity's traits. The identity is able to create, modify, and delete traits\n\t// in a self-service manner. The input will always be validated against the JSON Schema defined\n\t// in `schema_url`.\n\t//\n\t// required: true\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// Credentials represents all credentials that can be used for authenticating this identity.\n\t//\n\t// Use this structure to import credentials for a user.\n\tCredentials *IdentityWithCredentials `json:\"credentials\"`\n\n\t// VerifiableAddresses contains all the addresses that can be verified by the user.\n\t//\n\t// Use this structure to import verified addresses for an identity. Please keep in mind\n\t// that the address needs to be represented in the Identity Schema or this field will be overwritten\n\t// on the next identity update.\n\tVerifiableAddresses []VerifiableAddress `json:\"verifiable_addresses\"`\n\n\t// RecoveryAddresses contains all the addresses that can be used to recover an identity.\n\t//\n\t// Use this structure to import recovery addresses for an identity. Please keep in mind\n\t// that the address needs to be represented in the Identity Schema or this field will be overwritten\n\t// on the next identity update.\n\tRecoveryAddresses []RecoveryAddress `json:\"recovery_addresses\"`\n\n\t// Store metadata about the identity which the identity itself can see when calling for example the\n\t// session endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\n\tMetadataPublic json.RawMessage `json:\"metadata_public\"`\n\n\t// Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/<id>`.\n\tMetadataAdmin json.RawMessage `json:\"metadata_admin,omitempty\"`\n\n\t// State is the identity's state.\n\t//\n\t// required: false\n\tState State `json:\"state\"`\n\n\t// OrganizationID is the ID of the organization to which the identity belongs.\n\t//\n\t// required: false\n\tOrganizationID uuid.NullUUID `json:\"organization_id\"`\n\n\t// ExternalID is an optional external ID of the identity. This is used to link\n\t// the identity to an external system. If set, the external ID must be unique\n\t// across all identities.\n\t//\n\t// required: false\n\tExternalID string `json:\"external_id,omitempty\"`\n}\n\n// Create Identity and Import Credentials\n//\n// swagger:model identityWithCredentials\ntype IdentityWithCredentials struct {\n\t// Password if set will import a password credential.\n\tPassword *AdminIdentityImportCredentialsPassword `json:\"password\"`\n\n\t// OIDC if set will import an OIDC credential.\n\tOIDC *AdminIdentityImportCredentialsOIDC `json:\"oidc\"`\n\n\t// OIDC if set will import an OIDC credential.\n\tSAML *AdminIdentityImportCredentialsSAML `json:\"saml\"`\n}\n\n// Create Identity and Import Password Credentials\n//\n// swagger:model identityWithCredentialsPassword\ntype AdminIdentityImportCredentialsPassword struct {\n\t// Configuration options for the import.\n\tConfig AdminIdentityImportCredentialsPasswordConfig `json:\"config\"`\n}\n\n// Create Identity and Import Password Credentials Configuration\n//\n// swagger:model identityWithCredentialsPasswordConfig\ntype AdminIdentityImportCredentialsPasswordConfig struct {\n\t// The hashed password in [PHC format](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities#hashed-passwords)\n\tHashedPassword string `json:\"hashed_password\"`\n\n\t// The password in plain text if no hash is available.\n\tPassword string `json:\"password\"`\n\n\t// If set to true, the password will be migrated using the password migration hook.\n\tUsePasswordMigrationHook bool `json:\"use_password_migration_hook,omitempty\"`\n}\n\n// Create Identity and Import Social Sign In Credentials\n//\n// swagger:model identityWithCredentialsOidc\ntype AdminIdentityImportCredentialsOIDC struct {\n\t// Configuration options for the import.\n\tConfig AdminIdentityImportCredentialsOIDCConfig `json:\"config\"`\n}\n\n// swagger:model identityWithCredentialsOidcConfig\ntype AdminIdentityImportCredentialsOIDCConfig struct {\n\t// A list of OpenID Connect Providers\n\tProviders []AdminCreateIdentityImportCredentialsOIDCProvider `json:\"providers\"`\n}\n\n// Create Identity and Import Social Sign In Credentials Configuration\n//\n// swagger:model identityWithCredentialsOidcConfigProvider\ntype AdminCreateIdentityImportCredentialsOIDCProvider struct {\n\t// The subject (`sub`) of the OpenID Connect connection. Usually the `sub` field of the ID Token.\n\t//\n\t// required: true\n\tSubject string `json:\"subject\"`\n\n\t// The OpenID Connect provider to link the subject to. Usually something like `google` or `github`.\n\t//\n\t// required: true\n\tProvider string `json:\"provider\"`\n\n\t// If set, this credential allows the user to sign in using the OpenID Connect provider without setting the subject first.\n\t//\n\t// required: false\n\tUseAutoLink bool `json:\"use_auto_link,omitempty\"`\n\n\t// The organization to assign for the provider.\n\tOrganization uuid.NullUUID `json:\"organization,omitempty\"`\n}\n\n// Payload to import SAML credentials\n//\n// swagger:model identityWithCredentialsSaml\ntype AdminIdentityImportCredentialsSAML struct {\n\t// Configuration options for the import.\n\tConfig AdminIdentityImportCredentialsSAMLConfig `json:\"config\"`\n}\n\n// Payload of SAML providers\n//\n// swagger:model identityWithCredentialsSamlConfig\ntype AdminIdentityImportCredentialsSAMLConfig struct {\n\t// A list of SAML Providers\n\tProviders []AdminCreateIdentityImportCredentialsSAMLProvider `json:\"providers\"`\n}\n\n// Payload of specific SAML provider\n//\n// swagger:model identityWithCredentialsSamlConfigProvider\ntype AdminCreateIdentityImportCredentialsSAMLProvider struct {\n\t// The unique subject of the SAML connection. This value must be immutable at the source.\n\t//\n\t// required: true\n\tSubject string `json:\"subject\"`\n\n\t// The SAML provider to link the subject to.\n\t//\n\t// required: true\n\tProvider string `json:\"provider\"`\n\n\t// The organization to assign for the provider.\n\tOrganization uuid.NullUUID `json:\"organization\"`\n}\n\n// swagger:route POST /admin/identities identity createIdentity\n//\n// # Create an Identity\n//\n// Create an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model).  This endpoint can also be used to\n// [import credentials](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities)\n// for instance passwords, social sign in configurations or multifactor methods.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  201: identity\n//\t  400: errorGeneric\n//\t  409: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) create(w http.ResponseWriter, r *http.Request) {\n\tvar cr CreateIdentityBody\n\tif err := jsonx.NewStrictDecoder(r.Body).Decode(&cr); err != nil {\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithError(err.Error())))\n\t\treturn\n\t}\n\n\ti, err := h.identityFromCreateIdentityBody(r.Context(), &cr)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif err := h.r.IdentityManager().Create(r.Context(), i); err != nil {\n\t\tif errors.Is(err, sqlcon.ErrUniqueViolation) {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrConflict.WithReason(\"This identity conflicts with another identity that already exists.\")))\n\t\t} else {\n\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t}\n\t\treturn\n\t}\n\n\th.r.Writer().WriteCreated(w, r,\n\t\turlx.AppendPaths(\n\t\t\th.r.Config().SelfAdminURL(r.Context()),\n\t\t\t\"identities\",\n\t\t\ti.ID.String(),\n\t\t).String(),\n\t\tWithCredentialsNoConfigAndAdminMetadataInJSON(*i),\n\t)\n}\n\nfunc (h *Handler) identityFromCreateIdentityBody(ctx context.Context, cr *CreateIdentityBody) (*Identity, error) {\n\tstateChangedAt := sqlxx.NullTime(time.Now())\n\tstate := StateActive\n\tif cr.State != \"\" {\n\t\tif err := cr.State.IsValid(); err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"%s\", err).WithWrap(err))\n\t\t}\n\t\tstate = cr.State\n\t}\n\n\ti := &Identity{\n\t\tSchemaID:            cr.SchemaID,\n\t\tTraits:              []byte(cr.Traits),\n\t\tState:               state,\n\t\tStateChangedAt:      &stateChangedAt,\n\t\tVerifiableAddresses: cr.VerifiableAddresses,\n\t\tRecoveryAddresses:   cr.RecoveryAddresses,\n\t\tMetadataAdmin:       []byte(cr.MetadataAdmin),\n\t\tMetadataPublic:      []byte(cr.MetadataPublic),\n\t\tOrganizationID:      cr.OrganizationID,\n\t\tExternalID:          sqlxx.NullString(cr.ExternalID),\n\t}\n\t// Lowercase all emails, because the schema extension will otherwise not find them.\n\tfor k := range i.VerifiableAddresses {\n\t\ti.VerifiableAddresses[k].Value = strings.ToLower(i.VerifiableAddresses[k].Value)\n\t}\n\tfor k := range i.RecoveryAddresses {\n\t\ti.RecoveryAddresses[k].Value = strings.ToLower(i.RecoveryAddresses[k].Value)\n\t}\n\n\tif err := h.importCredentials(ctx, i, cr.Credentials); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn i, nil\n}\n\n// swagger:route PATCH /admin/identities identity batchPatchIdentities\n//\n// # Create multiple identities\n//\n// Creates multiple [identities](https://www.ory.com/docs/kratos/concepts/identity-user-model).\n//\n// You can also use this endpoint to [import credentials](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities),\n// including passwords, social sign-in settings, and multi-factor authentication methods.\n//\n// If the patch includes hashed passwords you can import up to 1,000 identities per request.\n//\n// If the patch includes at least one plaintext password you can import up to 200 identities per request.\n//\n// Avoid importing large batches with plaintext passwords. They can cause timeouts as the passwords need to be hashed before they are stored.\n//\n// If at least one identity is imported successfully, the response status is 200 OK.\n// If all imports fail, the response is one of the following 4xx errors:\n// - 400 Bad Request: The request payload is invalid or improperly formatted.\n// - 409 Conflict: Duplicate identities or conflicting data were detected.\n//\n// If you get a 504 Gateway Timeout:\n// - Reduce the batch size\n// - Avoid duplicate identities\n// - Pre-hash passwords with BCrypt\n//\n// If the issue persists, contact support.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: batchPatchIdentitiesResponse\n//\t  400: errorGeneric\n//\t  409: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) batchPatchIdentities(w http.ResponseWriter, r *http.Request) {\n\tvar (\n\t\treq BatchPatchIdentitiesBody\n\t\tres batchPatchIdentitiesResponse\n\t)\n\tif err := jsonx.NewStrictDecoder(r.Body).Decode(&req); err != nil {\n\t\th.r.Writer().WriteErrorCode(w, r, http.StatusBadRequest, errors.WithStack(err))\n\t\treturn\n\t}\n\n\tif len(req.Identities) > BatchPatchIdentitiesLimit {\n\t\th.r.Writer().WriteErrorCode(w, r, http.StatusBadRequest,\n\t\t\terrors.WithStack(herodot.ErrBadRequest.WithReasonf(\n\t\t\t\t\"The maximum number of identities per request that can be created or deleted at once is %d.\",\n\t\t\t\tBatchPatchIdentitiesLimit)))\n\t\treturn\n\t}\n\n\tres.Identities = make([]*BatchIdentityPatchResponse, len(req.Identities))\n\t// Array to look up the index of the identity in the identities array.\n\tindexInIdentities := make([]*int, len(req.Identities))\n\tidentities := make([]*Identity, 0, len(req.Identities))\n\n\tvar withUnHashedPasswordCount int\n\tfor i, patch := range req.Identities {\n\t\tif patch.Create != nil {\n\t\t\tres.Identities[i] = &BatchIdentityPatchResponse{\n\t\t\t\tAction:  ActionCreate,\n\t\t\t\tPatchID: patch.ID,\n\t\t\t}\n\t\t\tidentity, err := h.identityFromCreateIdentityBody(r.Context(), patch.Create)\n\t\t\tif err != nil {\n\t\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tidentities = append(identities, identity)\n\t\t\tidx := len(identities) - 1\n\t\t\tindexInIdentities[i] = &idx\n\n\t\t\tif patch.Create.Credentials != nil && patch.Create.Credentials.Password != nil &&\n\t\t\t\tpatch.Create.Credentials.Password.Config.Password != \"\" {\n\t\t\t\twithUnHashedPasswordCount++\n\t\t\t}\n\t\t}\n\t}\n\n\tif withUnHashedPasswordCount > BatchPatchIdentitiesWithPasswordLimit {\n\t\th.r.Writer().WriteErrorCode(w, r, http.StatusBadRequest,\n\t\t\terrors.WithStack(herodot.ErrBadRequest.WithReasonf(\n\t\t\t\t\"The maximum number of identities per request that can be created with a plaintext password is %d.\",\n\t\t\t\tBatchPatchIdentitiesWithPasswordLimit)))\n\t\treturn\n\t}\n\n\terr := h.r.IdentityManager().CreateIdentities(r.Context(), identities)\n\tpartialErr := new(CreateIdentitiesError)\n\tif err != nil && !errors.As(err, &partialErr) {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\tfor resIdx, identitiesIdx := range indexInIdentities {\n\t\tif identitiesIdx != nil {\n\t\t\tident := identities[*identitiesIdx]\n\t\t\t// Check if the identity was created successfully.\n\t\t\tif failed := partialErr.Find(ident); failed != nil {\n\t\t\t\tres.Identities[resIdx].Action = ActionError\n\t\t\t\tres.Identities[resIdx].Error = failed.Error\n\t\t\t} else {\n\t\t\t\tres.Identities[resIdx].IdentityID = &ident.ID\n\t\t\t}\n\t\t}\n\t}\n\n\th.r.Writer().Write(w, r, &res)\n}\n\n// Update Identity Parameters\n//\n// swagger:parameters updateIdentity\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateIdentity struct {\n\t// ID must be set to the ID of identity you want to update\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n\n\t// in: body\n\tBody UpdateIdentityBody\n}\n\n// Update Identity Body\n//\n// swagger:model updateIdentityBody\ntype UpdateIdentityBody struct {\n\t// SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. If set\n\t// will update the Identity's SchemaID.\n\t//\n\t// required: true\n\tSchemaID string `json:\"schema_id\"`\n\n\t// Traits represent an identity's traits. The identity is able to create, modify, and delete traits\n\t// in a self-service manner. The input will always be validated against the JSON Schema defined\n\t// in `schema_id`.\n\t//\n\t// required: true\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// Credentials represents all credentials that can be used for authenticating this identity.\n\t//\n\t// Use this structure to import credentials for a user.\n\t// Note: this wil override completely identity's credentials. If used incorrectly, this can cause a user to lose\n\t// access to their account!\n\tCredentials *IdentityWithCredentials `json:\"credentials\"`\n\n\t// Store metadata about the identity which the identity itself can see when calling for example the\n\t// session endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\n\tMetadataPublic json.RawMessage `json:\"metadata_public\"`\n\n\t// Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/<id>`.\n\tMetadataAdmin json.RawMessage `json:\"metadata_admin,omitempty\"`\n\n\t// State is the identity's state.\n\t//\n\t// required: true\n\tState State `json:\"state\"`\n\n\t// ExternalID is an optional external ID of the identity. This is used to link\n\t// the identity to an external system. If set, the external ID must be unique\n\t// across all identities.\n\t//\n\t// required: false\n\tExternalID string `json:\"external_id,omitempty\"`\n}\n\n// swagger:route PUT /admin/identities/{id} identity updateIdentity\n//\n// # Update an Identity\n//\n// This endpoint updates an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model). The full identity\n// payload, except credentials, is expected. For partial updates, use the [patchIdentity](https://www.ory.sh/docs/reference/api#tag/identity/operation/patchIdentity) operation.\n//\n// A credential can be provided via the `credentials` field in the request body.\n// If provided, the credentials will be imported and added to the existing credentials of the identity.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: identity\n//\t  400: errorGeneric\n//\t  404: errorGeneric\n//\t  409: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) update(w http.ResponseWriter, r *http.Request) {\n\tvar ur UpdateIdentityBody\n\tif err := decoderx.Decode(r, &ur,\n\t\tdecoderx.HTTPJSONDecoder()); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tid := x.ParseUUID(r.PathValue(\"id\"))\n\tidentity, err := h.r.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), id)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif ur.SchemaID != \"\" {\n\t\tidentity.SchemaID = ur.SchemaID\n\t}\n\n\tif ur.State != \"\" && identity.State != ur.State {\n\t\tif err := ur.State.IsValid(); err != nil {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"%s\", err).WithWrap(err)))\n\t\t\treturn\n\t\t}\n\n\t\tstateChangedAt := sqlxx.NullTime(time.Now())\n\n\t\tidentity.State = ur.State\n\t\tidentity.StateChangedAt = &stateChangedAt\n\t}\n\n\tidentity.Traits = []byte(ur.Traits)\n\tidentity.MetadataPublic = []byte(ur.MetadataPublic)\n\tidentity.MetadataAdmin = []byte(ur.MetadataAdmin)\n\tidentity.ExternalID = sqlxx.NullString(ur.ExternalID)\n\n\t// Although this is PUT and not PATCH, if the Credentials are not supplied keep the old one\n\tif ur.Credentials != nil {\n\t\tif err := h.importCredentials(r.Context(), identity, ur.Credentials); err != nil {\n\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err := h.r.IdentityManager().Update(\n\t\tr.Context(),\n\t\tidentity,\n\t\tManagerAllowWriteProtectedTraits,\n\t); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.r.Writer().Write(w, r, WithCredentialsNoConfigAndAdminMetadataInJSON(*identity))\n}\n\n// Delete Identity Parameters\n//\n// swagger:parameters deleteIdentity\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype deleteIdentity struct {\n\t// ID is the identity's ID.\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n}\n\n// swagger:route DELETE /admin/identities/{id} identity deleteIdentity\n//\n// # Delete an Identity\n//\n// Calling this endpoint irrecoverably and permanently deletes the [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) given its ID. This action can not be undone.\n// This endpoint returns 204 when the identity was deleted or 404 if the identity was not found.\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  204: emptyResponse\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) delete(w http.ResponseWriter, r *http.Request) {\n\tif err := h.r.PrivilegedIdentityPool().DeleteIdentity(r.Context(), x.ParseUUID(r.PathValue(\"id\"))); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusNoContent)\n}\n\n// Patch Identity Parameters\n//\n// swagger:parameters patchIdentity\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype patchIdentity struct {\n\t// ID must be set to the ID of identity you want to update\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n\n\t// in: body\n\tBody openapix.JSONPatchDocument\n}\n\n// swagger:route PATCH /admin/identities/{id} identity patchIdentity\n//\n// # Patch an Identity\n//\n// Partially updates an [identity's](https://www.ory.sh/docs/kratos/concepts/identity-user-model) field using [JSON Patch](https://jsonpatch.com/).\n// The fields `id`, `stateChangedAt` and `credentials` can not be updated using this method.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: identity\n//\t  400: errorGeneric\n//\t  404: errorGeneric\n//\t  409: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) patch(w http.ResponseWriter, r *http.Request) {\n\trequestBody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tid := x.ParseUUID(r.PathValue(\"id\"))\n\tidentity, err := h.r.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), id)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\toldState := identity.State\n\n\tpatchedIdentity, err := jsonx.ApplyJSONPatch(requestBody, WithCredentialsAndAdminMetadataInJSON(*identity), \"/id\", \"/stateChangedAt\", \"/credentials\", \"/credentials/oidc/**\")\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(\n\t\t\therodot.\n\t\t\t\tErrBadRequest.\n\t\t\t\tWithReasonf(\"An error occured when applying the JSON patch\").\n\t\t\t\tWithErrorf(\"%v\", err).\n\t\t\t\tWithWrap(err),\n\t\t))\n\t\treturn\n\t}\n\n\tif oldState != patchedIdentity.State {\n\t\t// Check if the changed state was actually valid\n\t\tif err := patchedIdentity.State.IsValid(); err != nil {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(\n\t\t\t\therodot.\n\t\t\t\t\tErrBadRequest.\n\t\t\t\t\tWithReasonf(\"The supplied state ('%s') was not valid. Valid states are ('%s', '%s').\", string(patchedIdentity.State), StateActive, StateInactive).\n\t\t\t\t\tWithErrorf(\"%v\", err).\n\t\t\t\t\tWithWrap(err),\n\t\t\t))\n\t\t\treturn\n\t\t}\n\n\t\t// If the state changed, we need to update the timestamp of it\n\t\tstateChangedAt := sqlxx.NullTime(time.Now())\n\t\tpatchedIdentity.StateChangedAt = &stateChangedAt\n\t}\n\n\tupdatedIdentity := Identity(patchedIdentity)\n\n\tif err := h.r.IdentityManager().Update(\n\t\tr.Context(),\n\t\t&updatedIdentity,\n\t\tManagerAllowWriteProtectedTraits,\n\t); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.r.Writer().Write(w, r, WithCredentialsNoConfigAndAdminMetadataInJSON(updatedIdentity))\n}\n\n// Delete Credential Parameters\n//\n// swagger:parameters deleteIdentityCredentials\ntype _ struct {\n\t// ID is the identity's ID.\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n\n\t// Type is the type of credentials to delete.\n\t//\n\t// required: true\n\t// in: path\n\tType CredentialsType `json:\"type\"`\n\n\t// Identifier is the identifier of the OIDC/SAML credential to delete.\n\t// Find the identifier by calling the `GET /admin/identities/{id}?include_credential={oidc,saml}` endpoint.\n\t//\n\t// required: false\n\t// in: query\n\tIdentifier string `json:\"identifier\"`\n}\n\n// swagger:route DELETE /admin/identities/{id}/credentials/{type} identity deleteIdentityCredentials\n//\n// # Delete a credential for a specific identity\n//\n// Delete an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) credential by its type.\n// You cannot delete passkeys or code auth credentials through this API.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  204: emptyResponse\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) deleteIdentityCredentials(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\tidentity, err := h.r.PrivilegedIdentityPool().GetIdentityConfidential(ctx, x.ParseUUID(r.PathValue(\"id\")))\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tcred, ok := identity.GetCredentials(CredentialsType(r.PathValue(\"type\")))\n\tif !ok {\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrNotFound.WithReasonf(\"You tried to remove a %s but this user have no %s set up.\", r.PathValue(\"type\"), r.PathValue(\"type\"))))\n\t\treturn\n\t}\n\n\tswitch cred.Type {\n\tcase CredentialsTypeLookup, CredentialsTypeTOTP:\n\t\tidentity.DeleteCredentialsType(cred.Type)\n\tcase CredentialsTypeWebAuthn:\n\t\tif err = identity.deleteCredentialWebAuthFromIdentity(); err != nil {\n\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\tcase CredentialsTypePassword, CredentialsTypeOIDC, CredentialsTypeSAML:\n\t\tfirstFactor, err := h.r.IdentityManager().CountActiveFirstFactorCredentials(ctx, identity)\n\t\tif err != nil {\n\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\t\tif firstFactor < 2 {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReason(\"You cannot remove the last first factor credential.\")))\n\t\t\treturn\n\t\t}\n\t\tswitch cred.Type {\n\t\tcase CredentialsTypePassword:\n\t\t\tif err := identity.deleteCredentialPassword(); err != nil {\n\t\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\tcase CredentialsTypeOIDC, CredentialsTypeSAML:\n\t\t\tif err := identity.deleteCredentialOIDCSAMLFromIdentity(cred.Type, r.URL.Query().Get(\"identifier\")); err != nil {\n\t\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\tdefault:\n\t\t// A bunch of credential type deletions are not yet implemented, e.g. passkeys, etc.\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Credentials type %s cannot be deleted.\", cred.Type)))\n\t\treturn\n\t}\n\n\tif err := h.r.IdentityManager().Update(\n\t\tctx,\n\t\tidentity,\n\t\tManagerAllowWriteProtectedTraits,\n\t); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusNoContent)\n}\n"
  },
  {
    "path": "identity/handler_import.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc (h *Handler) importCredentials(ctx context.Context, i *Identity, creds *IdentityWithCredentials) error {\n\tif creds == nil {\n\t\treturn nil\n\t}\n\n\t// This method only support password and OIDC import at the moment.\n\t// If other methods are added please ensure that the available AAL is set correctly in the identity.\n\t//\n\t// It would actually be good if we would validate the identity post-creation to see if the credentials are working.\n\tif creds.Password != nil {\n\t\t// This method is somewhat hacky, because it does not set the credential's identifier. It relies on the\n\t\t// identity validation to set the identifier, which is called after this method.\n\t\t//\n\t\t// It would be good to make this explicit.\n\t\tif err := h.importPasswordCredentials(ctx, i, creds.Password); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif creds.OIDC != nil {\n\t\tif err := h.importOIDCCredentials(ctx, i, creds.OIDC); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif creds.SAML != nil {\n\t\tif err := h.importSAMLCredentials(ctx, i, creds.SAML); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (h *Handler) importPasswordCredentials(ctx context.Context, i *Identity, creds *AdminIdentityImportCredentialsPassword) (err error) {\n\tif creds.Config.UsePasswordMigrationHook {\n\t\treturn i.SetCredentialsWithConfig(CredentialsTypePassword, Credentials{}, CredentialsPassword{UsePasswordMigrationHook: true})\n\t}\n\n\t// In here we deliberately ignore any password policies as the point here is to import passwords, even if they\n\t// are not matching the policy, as the user needs to able to sign in with their old password.\n\thashed := []byte(creds.Config.HashedPassword)\n\tif len(creds.Config.Password) > 0 {\n\t\t// Importing a clear text password\n\t\thashed, err = h.r.Hasher(ctx).Generate(ctx, []byte(creds.Config.Password))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcreds.Config.HashedPassword = string(hashed)\n\t}\n\n\tif !(hash.IsValidHashFormat(hashed)) {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"The imported password does not match any known hash format. For more information see https://www.ory.sh/dr/2\"))\n\t}\n\n\treturn i.SetCredentialsWithConfig(CredentialsTypePassword, Credentials{}, CredentialsPassword{HashedPassword: string(hashed)})\n}\n\nfunc (h *Handler) importOIDCCredentials(_ context.Context, i *Identity, creds *AdminIdentityImportCredentialsOIDC) error {\n\tvar target CredentialsOIDC\n\tc, ok := i.GetCredentials(CredentialsTypeOIDC)\n\tif !ok {\n\t\tvar providers []CredentialsOIDCProvider\n\t\tvar ids []string\n\t\tfor _, p := range creds.Config.Providers {\n\t\t\tids = append(ids, OIDCUniqueID(p.Provider, p.Subject))\n\t\t\tprovider := CredentialsOIDCProvider{\n\t\t\t\tSubject:     p.Subject,\n\t\t\t\tProvider:    p.Provider,\n\t\t\t\tUseAutoLink: p.UseAutoLink,\n\t\t\t}\n\t\t\tif p.Organization.Valid {\n\t\t\t\tprovider.Organization = p.Organization.UUID.String()\n\t\t\t}\n\t\t\tproviders = append(providers, provider)\n\t\t}\n\n\t\treturn i.SetCredentialsWithConfig(\n\t\t\tCredentialsTypeOIDC,\n\t\t\tCredentials{Identifiers: ids},\n\t\t\tCredentialsOIDC{Providers: providers},\n\t\t)\n\t}\n\n\tif err := json.Unmarshal(c.Config, &target); err != nil {\n\t\treturn errors.WithStack(x.PseudoPanic.WithWrap(err))\n\t}\n\n\tfor _, p := range creds.Config.Providers {\n\t\tc.Identifiers = append(c.Identifiers, OIDCUniqueID(p.Provider, p.Subject))\n\t\tprovider := CredentialsOIDCProvider{\n\t\t\tSubject:     p.Subject,\n\t\t\tProvider:    p.Provider,\n\t\t\tUseAutoLink: p.UseAutoLink,\n\t\t}\n\t\tif p.Organization.Valid {\n\t\t\tprovider.Organization = p.Organization.UUID.String()\n\t\t}\n\t\ttarget.Providers = append(target.Providers, provider)\n\t}\n\treturn i.SetCredentialsWithConfig(CredentialsTypeOIDC, *c, &target)\n}\n\nfunc (h *Handler) importSAMLCredentials(_ context.Context, i *Identity, creds *AdminIdentityImportCredentialsSAML) error {\n\tvar target CredentialsOIDC\n\tc, ok := i.GetCredentials(CredentialsTypeSAML)\n\tif !ok {\n\t\tvar providers []CredentialsOIDCProvider\n\t\tvar ids []string\n\t\tfor _, p := range creds.Config.Providers {\n\t\t\tids = append(ids, OIDCUniqueID(p.Provider, p.Subject))\n\t\t\tprovider := CredentialsOIDCProvider{\n\t\t\t\tSubject:  p.Subject,\n\t\t\t\tProvider: p.Provider,\n\t\t\t}\n\t\t\tif p.Organization.Valid {\n\t\t\t\tprovider.Organization = p.Organization.UUID.String()\n\t\t\t}\n\t\t\tproviders = append(providers, provider)\n\t\t}\n\n\t\treturn i.SetCredentialsWithConfig(\n\t\t\tCredentialsTypeSAML,\n\t\t\tCredentials{Identifiers: ids},\n\t\t\tCredentialsOIDC{Providers: providers},\n\t\t)\n\t}\n\n\tif err := json.Unmarshal(c.Config, &target); err != nil {\n\t\treturn errors.WithStack(x.PseudoPanic.WithWrap(err))\n\t}\n\n\tfor _, p := range creds.Config.Providers {\n\t\tc.Identifiers = append(c.Identifiers, OIDCUniqueID(p.Provider, p.Subject))\n\t\tprovider := CredentialsOIDCProvider{\n\t\t\tSubject:  p.Subject,\n\t\t\tProvider: p.Provider,\n\t\t}\n\t\tif p.Organization.Valid {\n\t\t\tprovider.Organization = p.Organization.UUID.String()\n\t\t}\n\t\ttarget.Providers = append(target.Providers, provider)\n\t}\n\treturn i.SetCredentialsWithConfig(CredentialsTypeSAML, *c, &target)\n}\n"
  },
  {
    "path": "identity/handler_import_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/snapshotx\"\n)\n\nfunc TestImportCredentials(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\t// Setup handler with minimal mock requirements\n\th := &Handler{}\n\n\ttestCases := []struct {\n\t\tname          string\n\t\tsetupIdentity func() *Identity\n\t\tcredentials   interface{}\n\t\tcredType      CredentialsType\n\t}{\n\t\t{\n\t\t\tname: \"OIDC new credential without organization\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\treturn &Identity{}\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsOIDC{\n\t\t\t\tConfig: AdminIdentityImportCredentialsOIDCConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsOIDCProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider: \"github\",\n\t\t\t\t\t\t\tSubject:  \"12345\",\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\tcredType: CredentialsTypeOIDC,\n\t\t},\n\t\t{\n\t\t\tname: \"OIDC new credential with organization\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\treturn &Identity{}\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsOIDC{\n\t\t\t\tConfig: AdminIdentityImportCredentialsOIDCConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsOIDCProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider:     \"github\",\n\t\t\t\t\t\t\tSubject:      \"12345\",\n\t\t\t\t\t\t\tOrganization: uuid.NullUUID{UUID: uuid.FromStringOrNil(\"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"), Valid: true},\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\tcredType: CredentialsTypeOIDC,\n\t\t},\n\t\t{\n\t\t\tname: \"OIDC update credential without organization\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\ti := &Identity{}\n\t\t\t\t_ = i.SetCredentialsWithConfig(\n\t\t\t\t\tCredentialsTypeOIDC,\n\t\t\t\t\tCredentials{\n\t\t\t\t\t\tIdentifiers: []string{OIDCUniqueID(\"google\", \"67890\")},\n\t\t\t\t\t},\n\t\t\t\t\tCredentialsOIDC{\n\t\t\t\t\t\tProviders: []CredentialsOIDCProvider{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tProvider: \"google\",\n\t\t\t\t\t\t\t\tSubject:  \"67890\",\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)\n\t\t\t\treturn i\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsOIDC{\n\t\t\t\tConfig: AdminIdentityImportCredentialsOIDCConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsOIDCProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider: \"github\",\n\t\t\t\t\t\t\tSubject:  \"12345\",\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\tcredType: CredentialsTypeOIDC,\n\t\t},\n\t\t{\n\t\t\tname: \"OIDC update credential with organization\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\ti := &Identity{}\n\t\t\t\t_ = i.SetCredentialsWithConfig(\n\t\t\t\t\tCredentialsTypeOIDC,\n\t\t\t\t\tCredentials{\n\t\t\t\t\t\tIdentifiers: []string{OIDCUniqueID(\"google\", \"67890\")},\n\t\t\t\t\t},\n\t\t\t\t\tCredentialsOIDC{\n\t\t\t\t\t\tProviders: []CredentialsOIDCProvider{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tProvider: \"google\",\n\t\t\t\t\t\t\t\tSubject:  \"67890\",\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)\n\t\t\t\treturn i\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsOIDC{\n\t\t\t\tConfig: AdminIdentityImportCredentialsOIDCConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsOIDCProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider:     \"github\",\n\t\t\t\t\t\t\tSubject:      \"12345\",\n\t\t\t\t\t\t\tOrganization: uuid.NullUUID{UUID: uuid.FromStringOrNil(\"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"), Valid: true},\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\tcredType: CredentialsTypeOIDC,\n\t\t},\n\t\t{\n\t\t\tname: \"OIDC update with multiple providers\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\ti := &Identity{}\n\t\t\t\t_ = i.SetCredentialsWithConfig(\n\t\t\t\t\tCredentialsTypeOIDC,\n\t\t\t\t\tCredentials{\n\t\t\t\t\t\tIdentifiers: []string{OIDCUniqueID(\"google\", \"67890\")},\n\t\t\t\t\t},\n\t\t\t\t\tCredentialsOIDC{\n\t\t\t\t\t\tProviders: []CredentialsOIDCProvider{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tProvider: \"google\",\n\t\t\t\t\t\t\t\tSubject:  \"67890\",\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)\n\t\t\t\treturn i\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsOIDC{\n\t\t\t\tConfig: AdminIdentityImportCredentialsOIDCConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsOIDCProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider:     \"github\",\n\t\t\t\t\t\t\tSubject:      \"12345\",\n\t\t\t\t\t\t\tOrganization: uuid.NullUUID{UUID: uuid.FromStringOrNil(\"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"), Valid: true},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider: \"gitlab\",\n\t\t\t\t\t\t\tSubject:  \"abcdef\",\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\tcredType: CredentialsTypeOIDC,\n\t\t},\n\t\t{\n\t\t\tname: \"SAML new credential without organization\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\treturn &Identity{}\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsSAML{\n\t\t\t\tConfig: AdminIdentityImportCredentialsSAMLConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsSAMLProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider: \"okta\",\n\t\t\t\t\t\t\tSubject:  \"user123\",\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\tcredType: CredentialsTypeSAML,\n\t\t},\n\t\t{\n\t\t\tname: \"SAML new credential with organization\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\treturn &Identity{}\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsSAML{\n\t\t\t\tConfig: AdminIdentityImportCredentialsSAMLConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsSAMLProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider:     \"okta\",\n\t\t\t\t\t\t\tSubject:      \"user123\",\n\t\t\t\t\t\t\tOrganization: uuid.NullUUID{UUID: uuid.FromStringOrNil(\"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"), Valid: true},\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\tcredType: CredentialsTypeSAML,\n\t\t},\n\t\t{\n\t\t\tname: \"SAML update credential without organization\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\ti := &Identity{}\n\t\t\t\t_ = i.SetCredentialsWithConfig(\n\t\t\t\t\tCredentialsTypeSAML,\n\t\t\t\t\tCredentials{\n\t\t\t\t\t\tIdentifiers: []string{OIDCUniqueID(\"onelogin\", \"user456\")},\n\t\t\t\t\t},\n\t\t\t\t\tCredentialsOIDC{\n\t\t\t\t\t\tProviders: []CredentialsOIDCProvider{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tProvider: \"onelogin\",\n\t\t\t\t\t\t\t\tSubject:  \"user456\",\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)\n\t\t\t\treturn i\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsSAML{\n\t\t\t\tConfig: AdminIdentityImportCredentialsSAMLConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsSAMLProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider: \"okta\",\n\t\t\t\t\t\t\tSubject:  \"user123\",\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\tcredType: CredentialsTypeSAML,\n\t\t},\n\t\t{\n\t\t\tname: \"SAML update credential with organization\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\ti := &Identity{}\n\t\t\t\t_ = i.SetCredentialsWithConfig(\n\t\t\t\t\tCredentialsTypeSAML,\n\t\t\t\t\tCredentials{\n\t\t\t\t\t\tIdentifiers: []string{OIDCUniqueID(\"onelogin\", \"user456\")},\n\t\t\t\t\t},\n\t\t\t\t\tCredentialsOIDC{\n\t\t\t\t\t\tProviders: []CredentialsOIDCProvider{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tProvider: \"onelogin\",\n\t\t\t\t\t\t\t\tSubject:  \"user456\",\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)\n\t\t\t\treturn i\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsSAML{\n\t\t\t\tConfig: AdminIdentityImportCredentialsSAMLConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsSAMLProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider:     \"okta\",\n\t\t\t\t\t\t\tSubject:      \"user123\",\n\t\t\t\t\t\t\tOrganization: uuid.NullUUID{UUID: uuid.FromStringOrNil(\"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"), Valid: true},\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\tcredType: CredentialsTypeSAML,\n\t\t},\n\t\t{\n\t\t\tname: \"SAML update with multiple providers\",\n\t\t\tsetupIdentity: func() *Identity {\n\t\t\t\ti := &Identity{}\n\t\t\t\t_ = i.SetCredentialsWithConfig(\n\t\t\t\t\tCredentialsTypeSAML,\n\t\t\t\t\tCredentials{\n\t\t\t\t\t\tIdentifiers: []string{OIDCUniqueID(\"onelogin\", \"user456\")},\n\t\t\t\t\t},\n\t\t\t\t\tCredentialsOIDC{\n\t\t\t\t\t\tProviders: []CredentialsOIDCProvider{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tProvider: \"onelogin\",\n\t\t\t\t\t\t\t\tSubject:  \"user456\",\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)\n\t\t\t\treturn i\n\t\t\t},\n\t\t\tcredentials: &AdminIdentityImportCredentialsSAML{\n\t\t\t\tConfig: AdminIdentityImportCredentialsSAMLConfig{\n\t\t\t\t\tProviders: []AdminCreateIdentityImportCredentialsSAMLProvider{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider:     \"okta\",\n\t\t\t\t\t\t\tSubject:      \"user123\",\n\t\t\t\t\t\t\tOrganization: uuid.NullUUID{UUID: uuid.FromStringOrNil(\"e7e3cbae-04cc-45f3-ae52-ea749a2ffaff\"), Valid: true},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tProvider: \"auth0\",\n\t\t\t\t\t\t\tSubject:  \"user789\",\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\tcredType: CredentialsTypeSAML,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t// Setup a fresh identity for each test\n\t\t\ti := tc.setupIdentity()\n\t\t\tvar err error\n\n\t\t\t// Perform the import based on credential type\n\t\t\tswitch tc.credType {\n\t\t\tcase CredentialsTypeOIDC:\n\t\t\t\terr = h.importOIDCCredentials(ctx, i, tc.credentials.(*AdminIdentityImportCredentialsOIDC))\n\t\t\tcase CredentialsTypeSAML:\n\t\t\t\terr = h.importSAMLCredentials(ctx, i, tc.credentials.(*AdminIdentityImportCredentialsSAML))\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// Verify credential was set correctly\n\t\t\tcreds, ok := i.GetCredentials(tc.credType)\n\t\t\trequire.True(t, ok, \"credentials should be set\")\n\n\t\t\t// Verify the credentials contain proper identifiers and config\n\t\t\tassert.NotEmpty(t, creds.Identifiers)\n\t\t\tassert.NotEmpty(t, creds.Config)\n\n\t\t\t// Take a snapshot of the credentials\n\t\t\tsnapshotx.SnapshotT(t, creds)\n\n\t\t\t// Additional checks based on credential type\n\t\t\tswitch tc.credType {\n\t\t\tcase CredentialsTypeOIDC:\n\t\t\t\toidcCreds := tc.credentials.(*AdminIdentityImportCredentialsOIDC)\n\t\t\t\tfor _, p := range oidcCreds.Config.Providers {\n\t\t\t\t\tid := OIDCUniqueID(p.Provider, p.Subject)\n\t\t\t\t\tassert.Contains(t, creds.Identifiers, id)\n\t\t\t\t}\n\t\t\tcase CredentialsTypeSAML:\n\t\t\t\tsamlCreds := tc.credentials.(*AdminIdentityImportCredentialsSAML)\n\t\t\t\tfor _, p := range samlCreds.Config.Providers {\n\t\t\t\t\tid := OIDCUniqueID(p.Provider, p.Subject)\n\t\t\t\t\tassert.Contains(t, creds.Identifiers, id)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/peterhellberg/link\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\t\"golang.org/x/crypto/bcrypt\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/randx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nvar ignoreDefault = []string{\"id\", \"schema_url\", \"state_changed_at\", \"created_at\", \"updated_at\"}\n\nfunc TestHandler(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.IdentitySchemasConfig(map[string]string{\n\t\t\t\"default\":         \"file://./stub/identity.schema.json\",\n\t\t\t\"customer\":        \"file://./stub/handler/customer.schema.json\",\n\t\t\t\"multiple_emails\": \"file://./stub/handler/multiple_emails.schema.json\",\n\t\t\t\"employee\":        \"file://./stub/handler/employee.schema.json\",\n\t\t})),\n\t)\n\n\t// Start kratos server\n\tpublicTS, adminTS := testhelpers.NewKratosServerWithCSRF(t, reg)\n\n\tmockServerURL := urlx.ParseOrPanic(publicTS.URL)\n\tdefaultSchemaExternalURL := (&schema.Schema{ID: \"default\"}).SchemaURL(mockServerURL).String()\n\n\tgetFull := func(t *testing.T, base *httptest.Server, href string, expectCode int) (gjson.Result, *http.Response) {\n\t\tt.Helper()\n\t\tres, err := base.Client().Get(base.URL + href)\n\t\trequire.NoError(t, err)\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\trequire.EqualValuesf(t, expectCode, res.StatusCode, \"%s\", body)\n\t\treturn gjson.ParseBytes(body), res\n\t}\n\n\tget := func(t *testing.T, base *httptest.Server, href string, expectCode int) gjson.Result {\n\t\tt.Helper()\n\t\tres, _ := getFull(t, base, href, expectCode)\n\t\treturn res\n\t}\n\n\tremove := func(t *testing.T, base *httptest.Server, href string, expectCode int) {\n\t\tt.Helper()\n\t\treq, err := http.NewRequest(\"DELETE\", base.URL+href, nil)\n\t\trequire.NoError(t, err)\n\n\t\tres, err := base.Client().Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\trequire.EqualValues(t, expectCode, res.StatusCode, \"%s\", ioutilx.MustReadAll(res.Body))\n\t}\n\n\tsend := func(t *testing.T, base *httptest.Server, method, href string, expectCode int, send interface{}) gjson.Result {\n\t\tt.Helper()\n\t\tvar b bytes.Buffer\n\t\tswitch raw := send.(type) {\n\t\tcase json.RawMessage:\n\t\t\tb = *bytes.NewBuffer(raw)\n\t\tdefault:\n\t\t\tif send != nil {\n\t\t\t\trequire.NoError(t, json.NewEncoder(&b).Encode(send))\n\t\t\t}\n\t\t}\n\n\t\treq, err := http.NewRequest(method, base.URL+href, &b)\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\tres, err := base.Client().Do(req)\n\t\trequire.NoError(t, err)\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\trequire.EqualValues(t, expectCode, res.StatusCode, \"%s\", body)\n\t\treturn gjson.ParseBytes(body)\n\t}\n\n\ttype patch map[string]interface{}\n\n\tt.Run(\"case=should return an empty list\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tparsed := get(t, ts, \"/identities\", http.StatusOK)\n\t\t\t\trequire.True(t, parsed.IsArray(), \"%s\", parsed.Raw)\n\t\t\t\tassert.Len(t, parsed.Array(), 0)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should return 404 on a non-existing resource\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t_ = get(t, ts, \"/identities/does-not-exist\", http.StatusNotFound)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail to create an identity because schema id does not exist\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar i identity.CreateIdentityBody\n\t\t\t\ti.SchemaID = \"does-not-exist\"\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusBadRequest, &i)\n\t\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), \"does-not-exist\", \"%s\", res)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail to create an entity because schema is not validating\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar i identity.CreateIdentityBody\n\t\t\t\ti.Traits = []byte(`{\"bar\":123}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusBadRequest, &i)\n\t\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), \"I[#/traits/bar] S[#/properties/traits/properties/bar/type] expected string, but got number\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail to create an entity with schema_url set\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusBadRequest, json.RawMessage(`{\"schema_url\":\"12345\",\"traits\":{}}`))\n\t\t\t\tassert.Contains(t, res.Get(\"error.message\").String(), \"schema_url\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should create an identity without an ID\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar i identity.CreateIdentityBody\n\t\t\t\ti.Traits = []byte(`{\"bar\":\"baz\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &i)\n\t\t\t\tassert.NotEmpty(t, res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"traits.bar\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Empty(t, res.Get(\"credentials\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should create an identity with metadata\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar i identity.CreateIdentityBody\n\t\t\t\ti.Traits = []byte(`{\"bar\":\"baz\"}`)\n\t\t\t\ti.MetadataPublic = []byte(`{\"public\":\"baz\"}`)\n\t\t\t\ti.MetadataAdmin = []byte(`{\"admin\":\"baz\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &i)\n\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"metadata_admin.admin\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"metadata_public.public\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should create an identity with an organization ID\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\torgID := uuid.NullUUID{\n\t\t\t\t\tUUID:  x.NewUUID(),\n\t\t\t\t\tValid: true,\n\t\t\t\t}\n\t\t\t\ti := identity.CreateIdentityBody{\n\t\t\t\t\tTraits:         []byte(`{\"bar\":\"baz\"}`),\n\t\t\t\t\tOrganizationID: orgID,\n\t\t\t\t}\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &i)\n\t\t\t\tassert.EqualValues(t, orgID.UUID.String(), res.Get(\"organization_id\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should create an identity with an external ID\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\texternalID := x.NewUUID().String()\n\t\t\t\ti := identity.CreateIdentityBody{\n\t\t\t\t\tTraits:     []byte(`{\"bar\":\"baz\"}`),\n\t\t\t\t\tExternalID: externalID,\n\t\t\t\t}\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &i)\n\t\t\t\tassert.EqualValues(t, externalID, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\tres = get(t, ts, \"/identities/by/external/\"+externalID, http.StatusOK)\n\t\t\t\tassert.EqualValues(t, externalID, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should be able to import users\", func(t *testing.T) {\n\t\tignoreDefault := []string{\"id\", \"schema_url\", \"state_changed_at\", \"created_at\", \"updated_at\"}\n\t\tt.Run(\"without any credentials\", func(t *testing.T) {\n\t\t\tres := send(t, adminTS, \"POST\", \"/identities\", http.StatusCreated, identity.CreateIdentityBody{Traits: []byte(`{\"email\": \"import-1@ory.sh\"}`)})\n\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), uuid.FromStringOrNil(res.Get(\"id\").String()))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), snapshotx.ExceptNestedKeys(ignoreDefault...))\n\t\t})\n\n\t\tt.Run(\"without traits\", func(t *testing.T) {\n\t\t\tres := send(t, adminTS, \"POST\", \"/identities\", http.StatusCreated, json.RawMessage(\"{}\"))\n\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), uuid.FromStringOrNil(res.Get(\"id\").String()))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), snapshotx.ExceptNestedKeys(ignoreDefault...))\n\t\t})\n\n\t\tt.Run(\"with malformed traits\", func(t *testing.T) {\n\t\t\tsend(t, adminTS, \"POST\", \"/identities\", http.StatusBadRequest, json.RawMessage(`{\"traits\": not valid JSON}`))\n\t\t})\n\n\t\tt.Run(\"with cleartext password and oidc credentials\", func(t *testing.T) {\n\t\t\tres := send(t, adminTS, \"POST\", \"/identities\", http.StatusCreated, identity.CreateIdentityBody{\n\t\t\t\tTraits: []byte(`{\"email\": \"import-2@ory.sh\"}`),\n\t\t\t\tCredentials: &identity.IdentityWithCredentials{\n\t\t\t\t\tPassword: &identity.AdminIdentityImportCredentialsPassword{\n\t\t\t\t\t\tConfig: identity.AdminIdentityImportCredentialsPasswordConfig{\n\t\t\t\t\t\t\tPassword: \"123456\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tOIDC: &identity.AdminIdentityImportCredentialsOIDC{\n\t\t\t\t\t\tConfig: identity.AdminIdentityImportCredentialsOIDCConfig{\n\t\t\t\t\t\t\tProviders: []identity.AdminCreateIdentityImportCredentialsOIDCProvider{\n\t\t\t\t\t\t\t\t{Subject: \"import-2\", Provider: \"google\"},\n\t\t\t\t\t\t\t\t{Subject: \"import-2\", Provider: \"github\"},\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\tSAML: &identity.AdminIdentityImportCredentialsSAML{\n\t\t\t\t\t\tConfig: identity.AdminIdentityImportCredentialsSAMLConfig{\n\t\t\t\t\t\t\tProviders: []identity.AdminCreateIdentityImportCredentialsSAMLProvider{\n\t\t\t\t\t\t\t\t{Subject: \"import-saml-2\", Provider: \"okta\"},\n\t\t\t\t\t\t\t\t{Subject: \"import-saml-2\", Provider: \"onelogin\"},\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},\n\t\t\t})\n\n\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), uuid.FromStringOrNil(res.Get(\"id\").String()))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), snapshotx.ExceptNestedKeys(append(ignoreDefault, \"hashed_password\")...), snapshotx.ExceptPaths(\"credentials.oidc.identifiers\"))\n\n\t\t\tidentifiers := actual.Credentials[identity.CredentialsTypeOIDC].Identifiers\n\t\t\tassert.Len(t, identifiers, 2)\n\t\t\tassert.Contains(t, identifiers, \"google:import-2\")\n\t\t\tassert.Contains(t, identifiers, \"github:import-2\")\n\n\t\t\tidentifiers = actual.Credentials[identity.CredentialsTypeSAML].Identifiers\n\t\t\tassert.Len(t, identifiers, 2)\n\t\t\tassert.Contains(t, identifiers, \"okta:import-saml-2\")\n\t\t\tassert.Contains(t, identifiers, \"onelogin:import-saml-2\")\n\n\t\t\trequire.NoError(t, hash.Compare(t.Context(), []byte(\"123456\"), []byte(gjson.GetBytes(actual.Credentials[identity.CredentialsTypePassword].Config, \"hashed_password\").String())))\n\t\t})\n\n\t\tt.Run(\"with organization oidc and saml credentials\", func(t *testing.T) {\n\t\t\torg := \"ad6a7dac-4eef-4f09-8e58-c099c14b6c36\"\n\t\t\tres := send(t, adminTS, \"POST\", \"/identities\", http.StatusCreated, identity.CreateIdentityBody{\n\t\t\t\tTraits: []byte(`{\"email\": \"import-3@ory.sh\"}`),\n\t\t\t\tCredentials: &identity.IdentityWithCredentials{\n\t\t\t\t\tOIDC: &identity.AdminIdentityImportCredentialsOIDC{\n\t\t\t\t\t\tConfig: identity.AdminIdentityImportCredentialsOIDCConfig{\n\t\t\t\t\t\t\tProviders: []identity.AdminCreateIdentityImportCredentialsOIDCProvider{\n\t\t\t\t\t\t\t\t{Subject: \"import-org-3\", Provider: \"google\", Organization: uuid.NullUUID{Valid: true, UUID: uuid.FromStringOrNil(org)}},\n\t\t\t\t\t\t\t\t{Subject: \"import-org-3\", Provider: \"github\", Organization: uuid.NullUUID{Valid: true, UUID: uuid.FromStringOrNil(org)}},\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\tSAML: &identity.AdminIdentityImportCredentialsSAML{\n\t\t\t\t\t\tConfig: identity.AdminIdentityImportCredentialsSAMLConfig{\n\t\t\t\t\t\t\tProviders: []identity.AdminCreateIdentityImportCredentialsSAMLProvider{\n\t\t\t\t\t\t\t\t{Subject: \"import-saml-org-3\", Provider: \"okta\", Organization: uuid.NullUUID{Valid: true, UUID: uuid.FromStringOrNil(org)}},\n\t\t\t\t\t\t\t\t{Subject: \"import-saml-org-3\", Provider: \"onelogin\", Organization: uuid.NullUUID{Valid: true, UUID: uuid.FromStringOrNil(org)}},\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},\n\t\t\t})\n\n\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), uuid.FromStringOrNil(res.Get(\"id\").String()))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), snapshotx.ExceptNestedKeys(append(ignoreDefault, \"hashed_password\")...), snapshotx.ExceptPaths(\"credentials.oidc.identifiers\"))\n\n\t\t\tidentifiers := actual.Credentials[identity.CredentialsTypeOIDC].Identifiers\n\t\t\tassert.Len(t, identifiers, 2)\n\t\t\tassert.Contains(t, identifiers, \"google:import-org-3\")\n\t\t\tassert.Contains(t, identifiers, \"github:import-org-3\")\n\n\t\t\tidentifiers = actual.Credentials[identity.CredentialsTypeSAML].Identifiers\n\t\t\tassert.Len(t, identifiers, 2)\n\t\t\tassert.Contains(t, identifiers, \"okta:import-saml-org-3\")\n\t\t\tassert.Contains(t, identifiers, \"onelogin:import-saml-org-3\")\n\n\t\t\tassert.Empty(t, []byte(gjson.GetBytes(actual.Credentials[identity.CredentialsTypePassword].Config, \"hashed_password\").String()))\n\t\t})\n\n\t\tt.Run(\"with hashed passwords\", func(t *testing.T) {\n\t\t\tfor i, tt := range []struct{ name, hash, pass string }{\n\t\t\t\t{\n\t\t\t\t\tname: \"pkbdf2\",\n\t\t\t\t\thash: \"$pbkdf2-sha256$i=1000,l=128$e8/arsEf4cvQihdNgqj0Nw$5xQQKNTyeTHx2Ld5/JDE7A\",\n\t\t\t\t\tpass: \"123456\",\n\t\t\t\t}, {\n\t\t\t\t\tname: \"bcrypt2\",\n\t\t\t\t\thash: \"$2a$10$ZsCsoVQ3xfBG/K2z2XpBf.tm90GZmtOqtqWcB5.pYd5Eq8y7RlDyq\",\n\t\t\t\t\tpass: \"123456\",\n\t\t\t\t}, {\n\t\t\t\t\tname: \"argon2i\",\n\t\t\t\t\thash: \"$argon2i$v=19$m=65536,t=3,p=4$STVE4CQ9qQ1dK/j224VMbA$o8b+k5wdHgBqf7ES+aWG2K7Y9diQ6ahEhbW8zcstXGo\",\n\t\t\t\t\tpass: \"123456\",\n\t\t\t\t}, {\n\t\t\t\t\tname: \"argon2id\",\n\t\t\t\t\thash: \"$argon2id$v=19$m=16,t=2,p=1$bVI1aE1SaTV6SGQ3bzdXdw$fnjCcZYmEPOUOjYXsT92Cg\",\n\t\t\t\t\tpass: \"123456\",\n\t\t\t\t}, {\n\t\t\t\t\tname: \"scrypt\",\n\t\t\t\t\thash: \"$scrypt$ln=16384,r=8,p=1$ZtQva9xCHzlSELH/mA7Kj5KjH2tCrkbwYzdxknkL0QQ=$pnTcXKaWVT+FwFDdk3vO1K0J7ZgOxdSU1tCJNYmn8zI=\",\n\t\t\t\t\tpass: \"123456\",\n\t\t\t\t}, {\n\t\t\t\t\tname: \"md5\",\n\t\t\t\t\thash: \"$md5$4QrcOUm6Wau+VuBX8g+IPg==\",\n\t\t\t\t\tpass: \"123456\",\n\t\t\t\t}, {\n\t\t\t\t\tname: \"SSHA\",\n\t\t\t\t\thash: \"{SSHA}JFZFs0oHzxbMwkSJmYVeI8MnTDy/276a\",\n\t\t\t\t\tpass: \"test123\",\n\t\t\t\t}, {\n\t\t\t\t\tname: \"SSHA256\",\n\t\t\t\t\thash: \"{SSHA256}czO44OTV17PcF1cRxWrLZLy9xHd7CWyVYplr1rOhuMlx/7IK\",\n\t\t\t\t\tpass: \"test123\",\n\t\t\t\t}, {\n\t\t\t\t\tname: \"SSHA512\",\n\t\t\t\t\thash: \"{SSHA512}xPUl/px+1cG55rUH4rzcwxdOIPSB2TingLpiJJumN2xyDWN4Ix1WQG3ihnvHaWUE8MYNkvMi5rf0C9NYixHsE6Yh59M=\",\n\t\t\t\t\tpass: \"test123\",\n\t\t\t\t}, {\n\t\t\t\t\tname: \"hmac\",\n\t\t\t\t\thash: \"$hmac-sha256$YjhhZDA4YTNhNTQ3ZTM1ODI5YjgyMWI3NTM3MDMwMWRkOGM0YjA2YmRkNzc3MWY5YjU0MWE3NTkxNDA2ODcxOA==$MTIzNDU2\",\n\t\t\t\t\tpass: \"123456\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"hash=\"+tt.name, func(t *testing.T) {\n\t\t\t\t\ttraits := fmt.Sprintf(`{\"email\": \"import-hash-%d@ory.sh\"}`, i)\n\t\t\t\t\tres := send(t, adminTS, \"POST\", \"/identities\", http.StatusCreated, identity.CreateIdentityBody{\n\t\t\t\t\t\tTraits: []byte(traits),\n\t\t\t\t\t\tCredentials: &identity.IdentityWithCredentials{Password: &identity.AdminIdentityImportCredentialsPassword{\n\t\t\t\t\t\t\tConfig: identity.AdminIdentityImportCredentialsPasswordConfig{HashedPassword: tt.hash},\n\t\t\t\t\t\t}},\n\t\t\t\t\t})\n\t\t\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), uuid.FromStringOrNil(res.Get(\"id\").String()))\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), snapshotx.ExceptNestedKeys(ignoreDefault...), snapshotx.ExceptNestedKeys(\"hashed_password\"))\n\n\t\t\t\t\trequire.NoError(t, hash.Compare(t.Context(), []byte(tt.pass), []byte(gjson.GetBytes(actual.Credentials[identity.CredentialsTypePassword].Config, \"hashed_password\").String())))\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"with password migration hook enabled\", func(t *testing.T) {\n\t\t\tres := send(t, adminTS, \"POST\", \"/identities\", http.StatusCreated, identity.CreateIdentityBody{\n\t\t\t\tTraits: []byte(`{\"email\": \"pw-migration-hook@ory.sh\"}`),\n\t\t\t\tCredentials: &identity.IdentityWithCredentials{Password: &identity.AdminIdentityImportCredentialsPassword{\n\t\t\t\t\tConfig: identity.AdminIdentityImportCredentialsPasswordConfig{UsePasswordMigrationHook: true},\n\t\t\t\t}},\n\t\t\t})\n\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), uuid.FromStringOrNil(res.Get(\"id\").String()))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), snapshotx.ExceptNestedKeys(ignoreDefault...), snapshotx.ExceptNestedKeys(\"hashed_password\"))\n\n\t\t\tassert.True(t, gjson.GetBytes(actual.Credentials[identity.CredentialsTypePassword].Config, \"use_password_migration_hook\").Bool())\n\t\t})\n\n\t\tt.Run(\"with not-normalized email\", func(t *testing.T) {\n\t\t\tres := send(t, adminTS, \"POST\", \"/identities\", http.StatusCreated, identity.CreateIdentityBody{\n\t\t\t\tSchemaID: \"customer\",\n\t\t\t\tTraits:   []byte(`{\"email\": \"UpperCased@ory.sh\"}`),\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{{\n\t\t\t\t\tVerified: true,\n\t\t\t\t\tValue:    \"UpperCased@ory.sh\",\n\t\t\t\t\tVia:      identity.AddressTypeEmail,\n\t\t\t\t\tStatus:   identity.VerifiableAddressStatusCompleted,\n\t\t\t\t}},\n\t\t\t\tRecoveryAddresses: []identity.RecoveryAddress{{Value: \"UpperCased@ory.sh\"}},\n\t\t\t})\n\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), uuid.FromStringOrNil(res.Get(\"id\").String()))\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.Len(t, actual.VerifiableAddresses, 1)\n\t\t\tassert.True(t, actual.VerifiableAddresses[0].Verified)\n\t\t\tassert.Equal(t, \"uppercased@ory.sh\", actual.VerifiableAddresses[0].Value)\n\n\t\t\trequire.Len(t, actual.RecoveryAddresses, 1)\n\t\t\tassert.Equal(t, \"uppercased@ory.sh\", actual.RecoveryAddresses[0].Value)\n\n\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), snapshotx.ExceptNestedKeys(ignoreDefault...), snapshotx.ExceptNestedKeys(\"verified_at\"))\n\t\t})\n\t})\n\n\tt.Run(\"case=unable to set ID itself\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusBadRequest, json.RawMessage(`{\"id\":\"12345\",\"traits\":{}}`))\n\t\t\t\tassert.Contains(t, res.Raw, \"id\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"suite=create and batch list\", func(t *testing.T) {\n\t\tvar ids []uuid.UUID\n\t\tidentitiesAmount := 5\n\t\tlistAmount := 3\n\t\tt.Run(\"case=create multiple identities\", func(t *testing.T) {\n\t\t\tfor i := 0; i < identitiesAmount; i++ {\n\t\t\t\tres := send(t, adminTS, \"POST\", \"/identities\", http.StatusCreated, json.RawMessage(`{\"traits\": {\"bar\":\"baz\"}}`))\n\t\t\t\tassert.NotEmpty(t, res.Get(\"id\").String(), \"%s\", res.Raw)\n\n\t\t\t\tid := x.ParseUUID(res.Get(\"id\").String())\n\t\t\t\tids = append(ids, id)\n\t\t\t}\n\t\t\trequire.Len(t, ids, identitiesAmount)\n\t\t})\n\n\t\tt.Run(\"case=list few identities\", func(t *testing.T) {\n\t\t\tvals := url.Values{}\n\t\t\tvals.Add(\"ids\", ids[0].String()) // duplicate ID is deduplicated in result\n\t\t\tfor i := range listAmount {\n\t\t\t\tvals.Add(\"ids\", ids[i].String())\n\t\t\t}\n\t\t\tres := get(t, adminTS, \"/identities?\"+vals.Encode(), http.StatusOK)\n\n\t\t\tidentities := res.Array()\n\t\t\trequire.Len(t, identities, listAmount)\n\t\t})\n\t})\n\n\tt.Run(\"case=list identities by ID is capped at 500\", func(t *testing.T) {\n\t\tvals := url.Values{}\n\t\tfor range 501 {\n\t\t\tvals.Add(\"ids\", x.NewUUID().String())\n\t\t}\n\t\tres := get(t, adminTS, \"/identities?\"+vals.Encode(), http.StatusBadRequest)\n\t\tassert.Contains(t, res.Get(\"error.reason\").String(), \"must not exceed 500\")\n\t})\n\n\tt.Run(\"case=list identities cannot combine filters\", func(t *testing.T) {\n\t\tfilters := []string{\n\t\t\t\"ids=\" + x.NewUUID().String(),\n\t\t\t\"credentials_identifier=foo@bar.com\",\n\t\t\t\"preview_credentials_identifier_similar=bar.com\",\n\t\t\t\"organization_id=\" + x.NewUUID().String(),\n\t\t}\n\t\tfor i := range filters {\n\t\t\tfor j := range filters {\n\t\t\t\tif i == j {\n\t\t\t\t\tcontinue // OK to use the same filter multiple times. Behavior varies by filter, though.\n\t\t\t\t}\n\n\t\t\t\tu := \"/identities?\" + filters[i] + \"&\" + filters[j]\n\t\t\t\tres := get(t, adminTS, u, http.StatusBadRequest)\n\t\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), \"cannot combine multiple filters\")\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"case=malformed ids should return an error\", func(t *testing.T) {\n\t\tres := get(t, adminTS, \"/identities?ids=not-a-uuid\", http.StatusBadRequest)\n\t\tassert.Contains(t, res.Get(\"error.reason\").String(), \"Invalid UUID value `not-a-uuid` for parameter `ids`.\", \"%s\", res.Raw)\n\t})\n\n\tt.Run(\"suite=create and update\", func(t *testing.T) {\n\t\tvar i identity.Identity\n\t\tcreateOIDCorSAMLIdentity := func(t *testing.T, ct identity.CredentialsType, identifier, accessToken, refreshToken, idToken string, encrypt bool) string {\n\t\t\ttransform := func(token, suffix string) string {\n\t\t\t\tif !encrypt {\n\t\t\t\t\treturn token\n\t\t\t\t}\n\t\t\t\tif token == \"\" {\n\t\t\t\t\treturn \"\"\n\t\t\t\t}\n\t\t\t\tc, err := reg.Cipher(t.Context()).Encrypt(context.Background(), []byte(token+suffix))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\treturn c\n\t\t\t}\n\n\t\t\tiID := x.NewUUID()\n\t\t\ttoJSON := func(c identity.CredentialsOIDC) []byte {\n\t\t\t\tout, err := json.Marshal(&c)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\treturn out\n\t\t\t}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &identity.Identity{\n\t\t\t\tID:     iID,\n\t\t\t\tTraits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, identifier)),\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tct: {\n\t\t\t\t\t\tType:        ct,\n\t\t\t\t\t\tIdentifiers: []string{\"bar:\" + identifier},\n\t\t\t\t\t\tConfig: toJSON(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSubject:             \"foo\",\n\t\t\t\t\t\t\t\tProvider:            \"bar\",\n\t\t\t\t\t\t\t\tInitialAccessToken:  transform(accessToken, \"0\"),\n\t\t\t\t\t\t\t\tInitialRefreshToken: transform(refreshToken, \"0\"),\n\t\t\t\t\t\t\t\tInitialIDToken:      transform(idToken, \"0\"),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSubject:             \"baz\",\n\t\t\t\t\t\t\t\tProvider:            \"zab\",\n\t\t\t\t\t\t\t\tInitialAccessToken:  transform(accessToken, \"1\"),\n\t\t\t\t\t\t\t\tInitialRefreshToken: transform(refreshToken, \"1\"),\n\t\t\t\t\t\t\t\tInitialIDToken:      transform(idToken, \"1\"),\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\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\tIdentifiers: []string{identifier},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tID:         x.NewUUID(),\n\t\t\t\t\t\tValue:      identifier,\n\t\t\t\t\t\tVerified:   false,\n\t\t\t\t\t\tCreatedAt:  time.Now(),\n\t\t\t\t\t\tIdentityID: iID,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}))\n\t\t\treturn iID.String()\n\t\t}\n\n\t\tt.Run(\"case=should create an identity with an ID which is ignored\", func(t *testing.T) {\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, json.RawMessage(`{\"traits\": {\"bar\":\"baz\"}}`))\n\t\t\t\t\tstateChangedAt := sqlxx.NullTime(res.Get(\"state_changed_at\").Time())\n\n\t\t\t\t\ti.Traits = []byte(res.Get(\"traits\").Raw)\n\t\t\t\t\ti.ID = x.ParseUUID(res.Get(\"id\").String())\n\t\t\t\t\ti.StateChangedAt = &stateChangedAt\n\t\t\t\t\tassert.NotEmpty(t, res.Get(\"id\").String())\n\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"traits.bar\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.Empty(t, res.Get(\"credentials\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, defaultSchemaExternalURL, res.Get(\"schema_url\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, config.DefaultIdentityTraitsSchemaID, res.Get(\"schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, identity.StateActive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should be able to get the identity\", func(t *testing.T) {\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tres := get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"traits.bar\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, defaultSchemaExternalURL, res.Get(\"schema_url\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, config.DefaultIdentityTraitsSchemaID, res.Get(\"schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, identity.StateActive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.Empty(t, res.Get(\"credentials\").String(), \"%s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should return an empty array on a failed lookup with identifier\", func(t *testing.T) {\n\t\t\tres := get(t, adminTS, \"/identities?credentials_identifier=find.by.non.existing.identifier@bar.com\", http.StatusOK)\n\t\t\tassert.EqualValues(t, int64(0), res.Get(\"#\").Int(), \"%s\", res.Raw)\n\t\t})\n\n\t\tt.Run(\"case=should be able to lookup the identity using identifier\", func(t *testing.T) {\n\t\t\tident := &identity.Identity{\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\tIdentifiers: []string{\"find.by.identifier@bar.com\"},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`), // foobar\n\t\t\t\t\t},\n\t\t\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\t\tIdentifiers: []string{\"ProviderID:293b5d9b-1009-4600-a3e9-bd1845de22f2\"},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(\"{\\\"some\\\" : \\\"secret\\\"}\"),\n\t\t\t\t\t},\n\t\t\t\t\tidentity.CredentialsTypeSAML: {\n\t\t\t\t\t\tType:        identity.CredentialsTypeSAML,\n\t\t\t\t\t\tIdentifiers: []string{\"SAMLProviderID:0851ac66-88cc-4775-aee0-9b4c79fdbfb9\"},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(\"{\\\"saml\\\" : \\\"secret\\\"}\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tState:  identity.StateActive,\n\t\t\t\tTraits: identity.Traits(`{\"username\":\"find.by.identifier@bar.com\"}`),\n\t\t\t}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), ident))\n\n\t\t\tt.Run(\"type=password\", func(t *testing.T) {\n\t\t\t\tres := get(t, adminTS, \"/identities?credentials_identifier=FIND.BY.IDENTIFIER@bar.com\", http.StatusOK)\n\t\t\t\tassert.EqualValues(t, ident.ID.String(), res.Get(\"0.id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"find.by.identifier@bar.com\", res.Get(\"0.traits.username\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, defaultSchemaExternalURL, res.Get(\"0.schema_url\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, config.DefaultIdentityTraitsSchemaID, res.Get(\"0.schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateActive, res.Get(\"0.state\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"password\", res.Get(\"0.credentials.password.type\").String(), res.Raw)\n\t\t\t\tassert.EqualValues(t, \"1\", res.Get(\"0.credentials.password.identifiers.#\").String(), res.Raw)\n\t\t\t\tassert.EqualValues(t, \"find.by.identifier@bar.com\", res.Get(\"0.credentials.password.identifiers.0\").String(), res.Raw)\n\t\t\t})\n\n\t\t\tt.Run(\"type=oidc\", func(t *testing.T) {\n\t\t\t\tres := get(t, adminTS, \"/identities?credentials_identifier=ProviderID:293b5d9b-1009-4600-a3e9-bd1845de22f2\", http.StatusOK)\n\t\t\t\tassert.EqualValues(t, ident.ID.String(), res.Get(\"0.id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"find.by.identifier@bar.com\", res.Get(\"0.traits.username\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, defaultSchemaExternalURL, res.Get(\"0.schema_url\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, config.DefaultIdentityTraitsSchemaID, res.Get(\"0.schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateActive, res.Get(\"0.state\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"oidc\", res.Get(\"0.credentials.oidc.type\").String(), res.Raw)\n\t\t\t\trequire.Len(t, res.Get(\"0.credentials.oidc.identifiers\").Array(), 1, res.Raw)\n\t\t\t\tassert.EqualValues(t, \"ProviderID:293b5d9b-1009-4600-a3e9-bd1845de22f2\", res.Get(\"0.credentials.oidc.identifiers.0\").String(), res.Raw)\n\t\t\t})\n\t\t\tt.Run(\"type=oidc\", func(t *testing.T) {\n\t\t\t\tres := get(t, adminTS, \"/identities?credentials_identifier=SAMLProviderID:0851ac66-88cc-4775-aee0-9b4c79fdbfb9\", http.StatusOK)\n\t\t\t\tassert.EqualValues(t, ident.ID.String(), res.Get(\"0.id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"find.by.identifier@bar.com\", res.Get(\"0.traits.username\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, defaultSchemaExternalURL, res.Get(\"0.schema_url\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, config.DefaultIdentityTraitsSchemaID, res.Get(\"0.schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateActive, res.Get(\"0.state\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"saml\", res.Get(\"0.credentials.saml.type\").String(), res.Raw)\n\t\t\t\tassert.Len(t, res.Get(\"0.credentials.saml.identifiers\").Array(), 1, res.Raw)\n\t\t\t\tassert.EqualValues(t, \"SAMLProviderID:0851ac66-88cc-4775-aee0-9b4c79fdbfb9\", res.Get(\"0.credentials.saml.identifiers.0\").String(), res.Raw)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should get oidc credential\", func(t *testing.T) {\n\t\t\tid := createOIDCorSAMLIdentity(t, identity.CredentialsTypeOIDC, \"foo.oidc@bar.com\", \"access_token\", \"refresh_token\", \"id_token\", true)\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tres := get(t, ts, \"/identities/\"+id, http.StatusOK)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.oidc.config\").Exists(), \"credentials config should be omitted: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.password.config\").Exists(), \"credentials config should be omitted: %s\", res.Raw)\n\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+id+\"?include_credential=oidc\", http.StatusOK)\n\t\t\t\t\tassert.True(t, res.Get(\"credentials\").Exists(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.True(t, res.Get(\"credentials.password\").Exists(), \"password meta should be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.password.false\").Exists(), \"password credentials should not be included: %s\", res.Raw)\n\t\t\t\t\tassert.Equal(t, \"bar:foo.oidc@bar.com\", res.Get(\"credentials.oidc.identifiers.0\").Str)\n\n\t\t\t\t\tassert.True(t, res.Get(\"credentials.oidc.config\").Exists(), \"oidc credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"foo\", res.Get(\"credentials.oidc.config.providers.0.subject\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"bar\", res.Get(\"credentials.oidc.config.providers.0.provider\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"access_token0\", res.Get(\"credentials.oidc.config.providers.0.initial_access_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"refresh_token0\", res.Get(\"credentials.oidc.config.providers.0.initial_refresh_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"id_token0\", res.Get(\"credentials.oidc.config.providers.0.initial_id_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"credentials.oidc.config.providers.1.subject\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"zab\", res.Get(\"credentials.oidc.config.providers.1.provider\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"access_token1\", res.Get(\"credentials.oidc.config.providers.1.initial_access_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"refresh_token1\", res.Get(\"credentials.oidc.config.providers.1.initial_refresh_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"id_token1\", res.Get(\"credentials.oidc.config.providers.1.initial_id_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should get saml credential\", func(t *testing.T) {\n\t\t\tid := createOIDCorSAMLIdentity(t, identity.CredentialsTypeSAML, \"foo.saml@bar.com\", \"access_token\", \"refresh_token\", \"id_token\", true)\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tres := get(t, ts, \"/identities/\"+id, http.StatusOK)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.saml.config\").Exists(), \"credentials config should be omitted: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.password.config\").Exists(), \"credentials config should be omitted: %s\", res.Raw)\n\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+id+\"?include_credential=saml\", http.StatusOK)\n\t\t\t\t\tassert.True(t, res.Get(\"credentials\").Exists(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.True(t, res.Get(\"credentials.password\").Exists(), \"password meta should be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.password.false\").Exists(), \"password credentials should not be included: %s\", res.Raw)\n\n\t\t\t\t\tassert.Equal(t, \"bar:foo.saml@bar.com\", res.Get(\"credentials.saml.identifiers.0\").Str)\n\t\t\t\t\tassert.True(t, res.Get(\"credentials.saml.config\").Exists(), \"SAML config should be included: %s\", res.Raw)\n\n\t\t\t\t\tassert.True(t, res.Get(\"credentials.saml.config\").Exists(), \"saml credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"foo\", res.Get(\"credentials.saml.config.providers.0.subject\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"bar\", res.Get(\"credentials.saml.config.providers.0.provider\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.saml.config.providers.0.initial_access_token\").Exists(), \"SAML details should not be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.saml.config.providers.0.initial_refresh_token\").Exists(), \"SAML details should not be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.saml.config.providers.0.initial_id_token\").Exists(), \"SAML details should not be included: %s\", res.Raw)\n\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"credentials.saml.config.providers.1.subject\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"zab\", res.Get(\"credentials.saml.config.providers.1.provider\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.saml.config.providers.1.initial_access_token\").Exists(), \"SAML details should not be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.saml.config.providers.1.initial_refresh_token\").Exists(), \"SAML details should not be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.saml.config.providers.1.initial_id_token\").Exists(), \"SAML details should not be included: %s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should not fail on empty tokens\", func(t *testing.T) {\n\t\t\tid := createOIDCorSAMLIdentity(t, identity.CredentialsTypeOIDC, \"foo.oidc.empty-tokens@bar.com\", \"\", \"\", \"\", true)\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tres := get(t, ts, \"/identities/\"+id, http.StatusOK)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.oidc.config\").Exists(), \"credentials config should be omitted: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.password.config\").Exists(), \"credentials config should be omitted: %s\", res.Raw)\n\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+id+\"?include_credential=oidc\", http.StatusOK)\n\t\t\t\t\tassert.True(t, res.Get(\"credentials\").Exists(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.True(t, res.Get(\"credentials.password\").Exists(), \"password meta should be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.password.false\").Exists(), \"password credentials should not be included: %s\", res.Raw)\n\t\t\t\t\tassert.True(t, res.Get(\"credentials.oidc.config\").Exists(), \"oidc credentials should be included: %s\", res.Raw)\n\n\t\t\t\t\tassert.EqualValues(t, \"foo\", res.Get(\"credentials.oidc.config.providers.0.subject\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"bar\", res.Get(\"credentials.oidc.config.providers.0.provider\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"\", res.Get(\"credentials.oidc.config.providers.0.initial_access_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"\", res.Get(\"credentials.oidc.config.providers.0.initial_refresh_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"\", res.Get(\"credentials.oidc.config.providers.0.initial_id_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"credentials.oidc.config.providers.1.subject\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"zab\", res.Get(\"credentials.oidc.config.providers.1.provider\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"\", res.Get(\"credentials.oidc.config.providers.1.initial_access_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"\", res.Get(\"credentials.oidc.config.providers.1.initial_refresh_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"\", res.Get(\"credentials.oidc.config.providers.1.initial_id_token\").String(), \"credentials should be included: %s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should get identity with credentials\", func(t *testing.T) {\n\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\tcredentials := map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypePassword: {Identifiers: []string{\"zab\", \"bar\"}, Type: identity.CredentialsTypePassword, Config: sqlxx.JSONRawMessage(\"{\\\"some\\\" : \\\"secret\\\"}\")},\n\t\t\t\tidentity.CredentialsTypeOIDC:     {Type: identity.CredentialsTypeOIDC, Identifiers: []string{\"bar\", \"baz\"}, Config: sqlxx.JSONRawMessage(\"{\\\"some\\\" : \\\"secret\\\"}\")},\n\t\t\t\tidentity.CredentialsTypeWebAuthn: {Type: identity.CredentialsTypeWebAuthn, Identifiers: []string{\"foo\", \"bar\"}, Config: sqlxx.JSONRawMessage(\"{\\\"some\\\" : \\\"secret\\\", \\\"user_handle\\\": \\\"rVIFaWRcTTuQLkXFmQWpgA==\\\"}\")},\n\t\t\t}\n\t\t\ti.Credentials = credentials\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\t\texcludeKeys := snapshotx.ExceptNestedKeys(\"id\", \"created_at\", \"updated_at\", \"schema_url\", \"state_changed_at\")\n\t\t\tt.Run(\"case=should get identity without credentials included\", func(t *testing.T) {\n\t\t\t\tres := get(t, adminTS, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(res.Raw), excludeKeys)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should get identity with password credentials included\", func(t *testing.T) {\n\t\t\t\tres := get(t, adminTS, \"/identities/\"+i.ID.String()+\"?include_credential=password\", http.StatusOK)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(res.Raw), excludeKeys)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should get identity with password and webauthn credentials included\", func(t *testing.T) {\n\t\t\t\tres := get(t, adminTS, \"/identities/\"+i.ID.String()+\"?include_credential=password&include_credential=webauthn\", http.StatusOK)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(res.Raw), excludeKeys)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should pass if no oidc credentials are set\", func(t *testing.T) {\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, json.RawMessage(`{\"traits\": {\"bar\":\"baz\"}}`))\n\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+res.Get(\"id\").String(), http.StatusOK)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.oidc.config\").Exists(), \"credentials config should be omitted: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.password.config\").Exists(), \"credentials config should be omitted: %s\", res.Raw)\n\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+res.Get(\"id\").String()+\"?include_credential=oidc\", http.StatusOK)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.password\").Exists(), \"password credentials should not be included: %s\", res.Raw)\n\t\t\t\t\tassert.False(t, res.Get(\"credentials.oidc\").Exists(), \"oidc credentials should be included: %s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should return empty tokens if decryption fails\", func(t *testing.T) {\n\t\t\tid := createOIDCorSAMLIdentity(t, identity.CredentialsTypeOIDC, \"foo-failed.oidc@bar.com\", \"foo_token\", \"bar_token\", \"id_token\", false)\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tres := get(t, ts, \"/identities/\"+i.ID.String()+\"?include_credential=oidc\", http.StatusOK)\n\t\t\t\t\tassert.NotContains(t, res.Raw, \"identifier_credentials\", res.Raw)\n\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+id+\"?include_credential=oidc\", http.StatusOK)\n\t\t\t\t\tassert.Equal(t, \"bar:foo-failed.oidc@bar.com\", res.Get(\"credentials.oidc.identifiers.0\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.Equal(t, \"\", res.Get(\"credentials.oidc.config.providers.0.initial_access_token\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.Equal(t, \"\", res.Get(\"credentials.oidc.config.providers.0.initial_id_token\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.Equal(t, \"\", res.Get(\"credentials.oidc.config.providers.0.initial_refresh_token\").String(), \"%s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should return decrypted token\", func(t *testing.T) {\n\t\t\te, _ := reg.Cipher(t.Context()).Encrypt(context.Background(), []byte(\"foo_token\"))\n\t\t\tid := createOIDCorSAMLIdentity(t, identity.CredentialsTypeOIDC, \"foo-failed-2.oidc@bar.com\", e, \"bar_token\", \"id_token\", false)\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tt.Logf(\"no oidc token\")\n\t\t\t\t\tres := get(t, ts, \"/identities/\"+i.ID.String()+\"?include_credential=oidc\", http.StatusOK)\n\t\t\t\t\tassert.NotContains(t, res.Raw, \"identifier_credentials\", res.Raw)\n\n\t\t\t\t\tt.Logf(\"get oidc token\")\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+id+\"?include_credential=oidc\", http.StatusOK)\n\t\t\t\t\tassert.Equal(t, \"bar:foo-failed-2.oidc@bar.com\", res.Get(\"credentials.oidc.identifiers.0\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.Equal(t, \"foo_token\", res.Get(\"credentials.oidc.config.providers.0.initial_access_token\").String(), \"%s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should update an identity and persist the changes\", func(t *testing.T) {\n\t\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, x.NewUUID().String()))}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\texternalID := x.NewUUID().String()\n\t\t\t\t\tur := identity.UpdateIdentityBody{\n\t\t\t\t\t\tTraits:         []byte(`{\"bar\":\"baz\",\"foo\":\"baz\"}`),\n\t\t\t\t\t\tSchemaID:       i.SchemaID,\n\t\t\t\t\t\tState:          identity.StateInactive,\n\t\t\t\t\t\tMetadataPublic: []byte(`{\"public\":\"metadata\"}`),\n\t\t\t\t\t\tMetadataAdmin:  []byte(`{\"admin\":\"metadata\"}`),\n\t\t\t\t\t\tExternalID:     externalID,\n\t\t\t\t\t}\n\n\t\t\t\t\tres := send(t, ts, \"PUT\", \"/identities/\"+i.ID.String(), http.StatusOK, &ur)\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"traits.bar\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"traits.foo\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"metadata\", res.Get(\"metadata_admin.admin\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"metadata\", res.Get(\"metadata_public.public\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, identity.StateInactive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.NotEqualValues(t, i.StateChangedAt, sqlxx.NullTime(res.Get(\"state_changed_at\").Time()), \"%s\", res.Raw)\n\t\t\t\t\tassert.Equal(t, externalID, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"traits.bar\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"metadata\", res.Get(\"metadata_admin.admin\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"metadata\", res.Get(\"metadata_public.public\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, identity.StateInactive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.NotEqualValues(t, i.StateChangedAt, sqlxx.NullTime(res.Get(\"state_changed_at\").Time()), \"%s\", res.Raw)\n\t\t\t\t\tassert.Equal(t, externalID, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"case=should update an identity with credentials\", func(t *testing.T) {\n\t\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, x.NewUUID().String()))}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tcredentials := identity.IdentityWithCredentials{\n\t\t\t\t\t\tPassword: &identity.AdminIdentityImportCredentialsPassword{\n\t\t\t\t\t\t\tConfig: identity.AdminIdentityImportCredentialsPasswordConfig{\n\t\t\t\t\t\t\t\tPassword: \"pswd1234\",\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\tur := identity.UpdateIdentityBody{\n\t\t\t\t\t\tTraits:         []byte(`{\"bar\":\"baz\",\"foo\":\"baz\"}`),\n\t\t\t\t\t\tSchemaID:       i.SchemaID,\n\t\t\t\t\t\tState:          identity.StateInactive,\n\t\t\t\t\t\tMetadataPublic: []byte(`{\"public\":\"metadata\"}`),\n\t\t\t\t\t\tMetadataAdmin:  []byte(`{\"admin\":\"metadata\"}`),\n\t\t\t\t\t\tCredentials:    &credentials,\n\t\t\t\t\t}\n\n\t\t\t\t\tres := send(t, ts, \"PUT\", \"/identities/\"+i.ID.String(), http.StatusOK, &ur)\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"traits.bar\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"traits.foo\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"metadata\", res.Get(\"metadata_admin.admin\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"metadata\", res.Get(\"metadata_public.public\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, identity.StateInactive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.NotEqualValues(t, i.StateChangedAt, sqlxx.NullTime(res.Get(\"state_changed_at\").Time()), \"%s\", res.Raw)\n\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(\"traits.bar\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"metadata\", res.Get(\"metadata_admin.admin\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, \"metadata\", res.Get(\"metadata_public.public\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.EqualValues(t, identity.StateInactive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\t\tassert.NotEqualValues(t, i.StateChangedAt, sqlxx.NullTime(res.Get(\"state_changed_at\").Time()), \"%s\", res.Raw)\n\t\t\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), i.ID)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.NoError(t, hash.Compare(t.Context(), []byte(\"pswd1234\"), []byte(gjson.GetBytes(actual.Credentials[identity.CredentialsTypePassword].Config, \"hashed_password\").String())))\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should delete a user and no longer be able to retrieve it\", func(t *testing.T) {\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, json.RawMessage(`{\"traits\": {\"bar\":\"baz\"}}`))\n\t\t\t\t\tremove(t, ts, \"/identities/\"+res.Get(\"id\").String(), http.StatusNoContent)\n\t\t\t\t\t_ = get(t, ts, \"/identities/\"+res.Get(\"id\").String(), http.StatusNotFound)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should create an identity with linking marker\", func(t *testing.T) {\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\ttrait := x.NewUUID().String()\n\t\t\t\t\tpayload := `\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"traits\": {\n\t\t\t\t\t\t\t\t\"bar\": \"` + trait + `\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"credentials\": {\n\t\t\t\t\t\t\t\t\"oidc\": {\n\t\t\t\t\t\t\t\t\t\"config\": {\n\t\t\t\t\t\t\t\t\t\t\"providers\": [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"subject\": \"` + trait + `\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"provider\": \"bar\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"use_auto_link\": true\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}`\n\n\t\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, json.RawMessage(payload))\n\t\t\t\t\ti.ID = x.ParseUUID(res.Get(\"id\").String())\n\n\t\t\t\t\tidentRes := send(t, adminTS, \"GET\", fmt.Sprintf(\"/identities/%s?include_credential=oidc\", i.ID), http.StatusOK, nil)\n\n\t\t\t\t\tassert.True(t, identRes.Get(\"credentials.oidc.config.providers.0.use_auto_link\").Bool())\n\t\t\t\t\tassert.False(t, identRes.Get(\"credentials.oidc.config.providers.0.organization\").Exists())\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should create an identity without linking marker omitempty\", func(t *testing.T) {\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\ttrait := x.NewUUID().String()\n\t\t\t\t\tpayload := `\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"traits\": {\n\t\t\t\t\t\t\t\t\"bar\": \"` + trait + `\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"credentials\": {\n\t\t\t\t\t\t\t\t\"oidc\": {\n\t\t\t\t\t\t\t\t\t\"config\": {\n\t\t\t\t\t\t\t\t\t\t\"providers\": [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"subject\": \"` + trait + `\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"provider\": \"bar\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"use_auto_link\": false\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}`\n\t\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, json.RawMessage(payload))\n\t\t\t\t\tstateChangedAt := sqlxx.NullTime(res.Get(\"state_changed_at\").Time())\n\n\t\t\t\t\ti.Traits = []byte(res.Get(\"traits\").Raw)\n\t\t\t\t\ti.ID = x.ParseUUID(res.Get(\"id\").String())\n\t\t\t\t\ti.StateChangedAt = &stateChangedAt\n\t\t\t\t\tassert.NotEmpty(t, res.Get(\"id\").String())\n\n\t\t\t\t\ti, err := reg.Persister().GetIdentityConfidential(context.Background(), i.ID)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\trequire.False(t, gjson.GetBytes(i.Credentials[identity.CredentialsTypeOIDC].Config, \"providers.0.use_auto_link\").Exists())\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"suite=PATCH identities\", func(t *testing.T) {\n\t\tt.Run(\"case=fails with too many patches\", func(t *testing.T) {\n\t\t\ttooMany := make([]*identity.BatchIdentityPatch, identity.BatchPatchIdentitiesLimit+1)\n\t\t\tfor i := range tooMany {\n\t\t\t\ttooMany[i] = &identity.BatchIdentityPatch{Create: validCreateIdentityBody(t, \"too-many-patches\", i, false)}\n\t\t\t}\n\t\t\tres := send(t, adminTS, \"PATCH\", \"/identities\", http.StatusBadRequest,\n\t\t\t\t&identity.BatchPatchIdentitiesBody{Identities: tooMany})\n\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), strconv.Itoa(identity.BatchPatchIdentitiesLimit),\n\t\t\t\t\"the error reason should contain the limit\")\n\t\t})\n\t\tt.Run(\"case=fails with too many identity plain text password patches\", func(t *testing.T) {\n\t\t\ttooMany := make([]*identity.BatchIdentityPatch, identity.BatchPatchIdentitiesWithPasswordLimit+1)\n\t\t\tfor i := range tooMany {\n\t\t\t\ttooMany[i] = &identity.BatchIdentityPatch{Create: validCreateIdentityBody(t, \"too-many-patches\", i, true)}\n\t\t\t}\n\t\t\tres := send(t, adminTS, \"PATCH\", \"/identities\", http.StatusBadRequest,\n\t\t\t\t&identity.BatchPatchIdentitiesBody{Identities: tooMany})\n\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), strconv.Itoa(identity.BatchPatchIdentitiesWithPasswordLimit),\n\t\t\t\t\"the error reason should contain the limit\")\n\t\t})\n\t\tt.Run(\"case=fails some on a bad identity\", func(t *testing.T) {\n\t\t\t// Test setup: we have a list of valid identitiy patches and a list of invalid ones.\n\t\t\t// Each run adds one invalid patch to the list and sends it to the server.\n\t\t\t// --> we expect the server to fail only the bad patches in the list.\n\t\t\t// Finally, we send just valid patches\n\t\t\t// --> we expect the server to succeed all patches in the list.\n\n\t\t\tt.Run(\"case=invalid patches fail\", func(t *testing.T) {\n\t\t\t\tpatches := []*identity.BatchIdentityPatch{\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid\", 0, false)},\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid\", 1, false)},\n\t\t\t\t\t{Create: &identity.CreateIdentityBody{}}, // <-- invalid: missing all fields\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid\", 2, false)},\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid\", 0, false)}, // <-- duplicate\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid\", 3, false)},\n\t\t\t\t\t{Create: &identity.CreateIdentityBody{Traits: json.RawMessage(`\"invalid traits\"`)}}, // <-- invalid traits\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid\", 4, false)},\n\t\t\t\t\t{Create: &identity.CreateIdentityBody{SchemaID: \"nonexistent_schema\", Traits: json.RawMessage(`{}`)}}, // <-- invalid schema ID\n\t\t\t\t}\n\t\t\t\texpectedToPass := []*identity.BatchIdentityPatch{patches[0], patches[1], patches[3], patches[5], patches[7]}\n\n\t\t\t\t// Create unique IDs for each patch\n\t\t\t\tpatchIDs := make([]string, len(patches))\n\t\t\t\tfor i, p := range patches {\n\t\t\t\t\tid := uuid.NewV5(uuid.Nil, fmt.Sprintf(\"%d\", i))\n\t\t\t\t\tp.ID = &id\n\t\t\t\t\tpatchIDs[i] = id.String()\n\t\t\t\t}\n\n\t\t\t\treq := &identity.BatchPatchIdentitiesBody{Identities: patches}\n\t\t\t\tbody := send(t, adminTS, \"PATCH\", \"/identities\", http.StatusOK, req)\n\t\t\t\tvar actions []string\n\t\t\t\trequire.NoErrorf(t, json.Unmarshal(([]byte)(body.Get(\"identities.#.action\").Raw), &actions), \"%s\", body)\n\t\t\t\tassert.Equalf(t,\n\t\t\t\t\t[]string{\"create\", \"create\", \"error\", \"create\", \"error\", \"create\", \"error\", \"create\", \"error\"},\n\t\t\t\t\tactions, \"%s\", body)\n\n\t\t\t\t// Check that all patch IDs are returned\n\t\t\t\tfor i, gotPatchID := range body.Get(\"identities.#.patch_id\").Array() {\n\t\t\t\t\tassert.Equal(t, patchIDs[i], gotPatchID.String())\n\t\t\t\t}\n\n\t\t\t\t// Check specific errors\n\t\t\t\tassert.Equal(t, \"Bad Request\", body.Get(\"identities.2.error.status\").String())\n\t\t\t\tassert.Equal(t, \"Conflict\", body.Get(\"identities.4.error.status\").String())\n\t\t\t\tassert.Equal(t, \"Bad Request\", body.Get(\"identities.6.error.status\").String())\n\t\t\t\tassert.Equal(t, \"Bad Request\", body.Get(\"identities.8.error.status\").String())\n\n\t\t\t\t// Check that error reasons are specific, not generic\n\t\t\t\tassert.NotEqualf(t, \"The request was malformed or contained invalid parameters\",\n\t\t\t\t\tbody.Get(\"identities.2.error.reason\").String(), \"error reason should be specific, not generic: %s\", body)\n\t\t\t\tassert.NotEqualf(t, \"The request was malformed or contained invalid parameters\",\n\t\t\t\t\tbody.Get(\"identities.6.error.reason\").String(), \"error reason should be specific, not generic: %s\", body)\n\t\t\t\tassert.Containsf(t, body.Get(\"identities.8.error.reason\").String(),\n\t\t\t\t\t\"Unable to find JSON Schema ID: nonexistent_schema\", \"error reason should mention the missing schema ID: %s\", body)\n\n\t\t\t\tvar identityIDs []uuid.UUID\n\t\t\t\trequire.NoErrorf(t, json.Unmarshal(([]byte)(body.Get(\"identities.#.identity\").Raw), &identityIDs), \"%s\", body)\n\n\t\t\t\tactualIdentities, _, err := reg.Persister().ListIdentities(t.Context(), identity.ListIdentityParameters{IdsFilter: identityIDs})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tactualIdentityIDs := make([]uuid.UUID, len(actualIdentities))\n\t\t\t\tfor i, id := range actualIdentities {\n\t\t\t\t\tactualIdentityIDs[i] = id.ID\n\t\t\t\t}\n\t\t\t\tassert.ElementsMatchf(t, identityIDs, actualIdentityIDs, \"%s\", body)\n\n\t\t\t\texpectedTraits := make(map[string]string, len(expectedToPass))\n\t\t\t\tfor i, p := range expectedToPass {\n\t\t\t\t\texpectedTraits[identityIDs[i].String()] = string(p.Create.Traits)\n\t\t\t\t}\n\t\t\t\tactualTraits := make(map[string]string, len(actualIdentities))\n\t\t\t\tfor _, id := range actualIdentities {\n\t\t\t\t\tactualTraits[id.ID.String()] = string(id.Traits)\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(t, expectedTraits, actualTraits)\n\t\t\t})\n\n\t\t\tt.Run(\"case=per-item errors surface specific reasons\", func(t *testing.T) {\n\t\t\t\t// Regression test: batch endpoint must propagate specific error reasons\n\t\t\t\t// instead of replacing them with the generic herodot.ErrBadRequest message.\n\t\t\t\tuniqueSuffix := x.NewUUID().String()\n\t\t\t\tpatches := []*identity.BatchIdentityPatch{\n\t\t\t\t\t// 0: valid identity (should succeed)\n\t\t\t\t\t{Create: &identity.CreateIdentityBody{\n\t\t\t\t\t\tSchemaID: \"multiple_emails\",\n\t\t\t\t\t\tTraits:   json.RawMessage(fmt.Sprintf(`{\"emails\":[\"specific-err-%s@ory.sh\"],\"username\":\"specific-err-%s@ory.sh\"}`, uniqueSuffix, uniqueSuffix)),\n\t\t\t\t\t\tState:    \"active\",\n\t\t\t\t\t}},\n\t\t\t\t\t// 1: unknown schema ID\n\t\t\t\t\t{Create: &identity.CreateIdentityBody{\n\t\t\t\t\t\tSchemaID: \"user_v1\",\n\t\t\t\t\t\tTraits:   json.RawMessage(`{\"email\":\"test@ory.com\"}`),\n\t\t\t\t\t}},\n\t\t\t\t\t// 2: another unknown schema ID\n\t\t\t\t\t{Create: &identity.CreateIdentityBody{\n\t\t\t\t\t\tSchemaID: \"completely_made_up\",\n\t\t\t\t\t\tTraits:   json.RawMessage(`{}`),\n\t\t\t\t\t}},\n\t\t\t\t\t// 3: traits are not an object (schema validation error)\n\t\t\t\t\t{Create: &identity.CreateIdentityBody{\n\t\t\t\t\t\tSchemaID: \"employee\",\n\t\t\t\t\t\tTraits:   json.RawMessage(`\"just a string\"`),\n\t\t\t\t\t}},\n\t\t\t\t\t// 4: empty body (missing schema defaults to \"default\", but no traits)\n\t\t\t\t\t{Create: &identity.CreateIdentityBody{}},\n\t\t\t\t}\n\n\t\t\t\treq := &identity.BatchPatchIdentitiesBody{Identities: patches}\n\t\t\t\tbody := send(t, adminTS, \"PATCH\", \"/identities\", http.StatusOK, req)\n\n\t\t\t\tvar actions []string\n\t\t\t\trequire.NoErrorf(t, json.Unmarshal([]byte(body.Get(\"identities.#.action\").Raw), &actions), \"%s\", body)\n\t\t\t\tassert.Equalf(t, []string{\"create\", \"error\", \"error\", \"error\", \"error\"}, actions, \"%s\", body)\n\n\t\t\t\tgenericReason := \"The request was malformed or contained invalid parameters\"\n\n\t\t\t\t// Index 1: unknown schema \"user_v1\"\n\t\t\t\tassert.Equalf(t, float64(400), body.Get(\"identities.1.error.code\").Float(), \"%s\", body)\n\t\t\t\tassert.Containsf(t, body.Get(\"identities.1.error.reason\").String(),\n\t\t\t\t\t\"Unable to find JSON Schema ID: user_v1\",\n\t\t\t\t\t\"expected specific schema-not-found reason: %s\", body)\n\n\t\t\t\t// Index 2: unknown schema \"completely_made_up\"\n\t\t\t\tassert.Equalf(t, float64(400), body.Get(\"identities.2.error.code\").Float(), \"%s\", body)\n\t\t\t\tassert.Containsf(t, body.Get(\"identities.2.error.reason\").String(),\n\t\t\t\t\t\"Unable to find JSON Schema ID: completely_made_up\",\n\t\t\t\t\t\"expected specific schema-not-found reason: %s\", body)\n\n\t\t\t\t// Index 3: traits type mismatch (schema validation)\n\t\t\t\tassert.Equalf(t, float64(400), body.Get(\"identities.3.error.code\").Float(), \"%s\", body)\n\t\t\t\tassert.NotEqualf(t, genericReason, body.Get(\"identities.3.error.reason\").String(),\n\t\t\t\t\t\"schema validation error should have a specific reason: %s\", body)\n\n\t\t\t\t// Index 4: empty body (schema validation)\n\t\t\t\tassert.Equalf(t, float64(400), body.Get(\"identities.4.error.code\").Float(), \"%s\", body)\n\t\t\t\tassert.NotEqualf(t, genericReason, body.Get(\"identities.4.error.reason\").String(),\n\t\t\t\t\t\"empty body error should have a specific reason: %s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"valid patches succeed\", func(t *testing.T) {\n\t\t\t\tvalidPatches := []*identity.BatchIdentityPatch{\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid-patch\", 0, false)},\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid-patch\", 1, false)},\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid-patch\", 2, false)},\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid-patch\", 3, false)},\n\t\t\t\t\t{Create: validCreateIdentityBody(t, \"valid-patch\", 4, false)},\n\t\t\t\t}\n\t\t\t\treq := &identity.BatchPatchIdentitiesBody{Identities: validPatches}\n\t\t\t\tsend(t, adminTS, \"PATCH\", \"/identities\", http.StatusOK, req)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=external_id conflict returns per-item error\", func(t *testing.T) {\n\t\t\t// Regression test for https://github.com/ory-corp/cloud/issues/10580\n\t\t\t// When batch-importing identities with external_id and the external_id\n\t\t\t// already exists, the endpoint should return a per-item conflict error\n\t\t\t// instead of a 500 SQL syntax error.\n\t\t\texternalID1 := \"ext-conflict-\" + x.NewUUID().String()\n\t\t\texternalID2 := \"ext-conflict-\" + x.NewUUID().String()\n\n\t\t\t// First call: create two identities with unique external_ids.\n\t\t\tfirstPatches := []*identity.BatchIdentityPatch{\n\t\t\t\t{Create: &identity.CreateIdentityBody{\n\t\t\t\t\tSchemaID:   \"multiple_emails\",\n\t\t\t\t\tTraits:     json.RawMessage(fmt.Sprintf(`{\"emails\":[\"first-a-%s@ory.sh\"],\"username\":\"first-a-%s@ory.sh\"}`, externalID1, externalID1)),\n\t\t\t\t\tExternalID: externalID1,\n\t\t\t\t\tState:      \"active\",\n\t\t\t\t}},\n\t\t\t\t{Create: &identity.CreateIdentityBody{\n\t\t\t\t\tSchemaID:   \"multiple_emails\",\n\t\t\t\t\tTraits:     json.RawMessage(fmt.Sprintf(`{\"emails\":[\"first-b-%s@ory.sh\"],\"username\":\"first-b-%s@ory.sh\"}`, externalID2, externalID2)),\n\t\t\t\t\tExternalID: externalID2,\n\t\t\t\t\tState:      \"active\",\n\t\t\t\t}},\n\t\t\t}\n\t\t\tres := send(t, adminTS, \"PATCH\", \"/identities\", http.StatusOK,\n\t\t\t\t&identity.BatchPatchIdentitiesBody{Identities: firstPatches})\n\t\t\tactions := res.Get(\"identities.#.action\").Array()\n\t\t\trequire.Len(t, actions, 2)\n\t\t\tassert.Equal(t, \"create\", actions[0].String())\n\t\t\tassert.Equal(t, \"create\", actions[1].String())\n\n\t\t\t// Second call: different traits/emails, but reuse the same external_ids.\n\t\t\t// The only conflict is on external_id.\n\t\t\tsecondPatches := []*identity.BatchIdentityPatch{\n\t\t\t\t{Create: &identity.CreateIdentityBody{\n\t\t\t\t\tSchemaID:   \"multiple_emails\",\n\t\t\t\t\tTraits:     json.RawMessage(fmt.Sprintf(`{\"emails\":[\"second-a-%s@ory.sh\"],\"username\":\"second-a-%s@ory.sh\"}`, externalID1, externalID1)),\n\t\t\t\t\tExternalID: externalID1,\n\t\t\t\t\tState:      \"active\",\n\t\t\t\t}},\n\t\t\t\t{Create: &identity.CreateIdentityBody{\n\t\t\t\t\tSchemaID:   \"multiple_emails\",\n\t\t\t\t\tTraits:     json.RawMessage(fmt.Sprintf(`{\"emails\":[\"second-b-%s@ory.sh\"],\"username\":\"second-b-%s@ory.sh\"}`, externalID2, externalID2)),\n\t\t\t\t\tExternalID: externalID2,\n\t\t\t\t\tState:      \"active\",\n\t\t\t\t}},\n\t\t\t}\n\t\t\tres = send(t, adminTS, \"PATCH\", \"/identities\", http.StatusOK,\n\t\t\t\t&identity.BatchPatchIdentitiesBody{Identities: secondPatches})\n\t\t\tactions = res.Get(\"identities.#.action\").Array()\n\t\t\trequire.Lenf(t, actions, 2, \"%s\", res.Raw)\n\t\t\tassert.Equalf(t, \"error\", actions[0].String(), \"expected conflict error for duplicate external_id: %s\", res.Raw)\n\t\t\tassert.Equalf(t, \"Conflict\", res.Get(\"identities.0.error.status\").String(), \"%s\", res.Raw)\n\t\t\tassert.Equalf(t, \"error\", actions[1].String(), \"expected conflict error for duplicate external_id: %s\", res.Raw)\n\t\t\tassert.Equalf(t, \"Conflict\", res.Get(\"identities.1.error.status\").String(), \"%s\", res.Raw)\n\t\t})\n\n\t\tt.Run(\"case=ignores create nil bodies\", func(t *testing.T) {\n\t\t\tpatches := []*identity.BatchIdentityPatch{\n\t\t\t\t{Create: nil},\n\t\t\t\t{Create: validCreateIdentityBody(t, \"nil-batch-import\", 0, false)},\n\t\t\t\t{Create: nil},\n\t\t\t\t{Create: validCreateIdentityBody(t, \"nil-batch-import\", 1, false)},\n\t\t\t\t{Create: nil},\n\t\t\t\t{Create: validCreateIdentityBody(t, \"nil-batch-import\", 2, false)},\n\t\t\t\t{Create: nil},\n\t\t\t\t{Create: validCreateIdentityBody(t, \"nil-batch-import\", 3, false)},\n\t\t\t\t{Create: nil},\n\t\t\t}\n\t\t\treq := &identity.BatchPatchIdentitiesBody{Identities: patches}\n\t\t\tres := send(t, adminTS, \"PATCH\", \"/identities\", http.StatusOK, req)\n\t\t\tassert.Len(t, res.Get(\"identities\").Array(), len(patches))\n\t\t\tassert.Equal(t, \"null\", res.Get(\"identities.0\").Raw)\n\t\t\tassert.Equal(t, \"null\", res.Get(\"identities.2\").Raw)\n\t\t\tassert.Equal(t, \"null\", res.Get(\"identities.4\").Raw)\n\t\t\tassert.Equal(t, \"null\", res.Get(\"identities.6\").Raw)\n\t\t\tassert.Equal(t, \"null\", res.Get(\"identities.8\").Raw)\n\t\t})\n\n\t\tt.Run(\"case=success\", func(t *testing.T) {\n\t\t\tpatches := []*identity.BatchIdentityPatch{\n\t\t\t\t{Create: validCreateIdentityBody(t, \"Batch-Import\", 0, false)},\n\t\t\t\t{Create: validCreateIdentityBody(t, \"batch-import\", 1, false)},\n\t\t\t\t{Create: validCreateIdentityBody(t, \"batch-import\", 2, false)},\n\t\t\t\t{Create: validCreateIdentityBody(t, \"batch-import\", 3, false)},\n\t\t\t}\n\t\t\treq := &identity.BatchPatchIdentitiesBody{Identities: patches}\n\t\t\tres := send(t, adminTS, \"PATCH\", \"/identities\", http.StatusOK, req)\n\n\t\t\tassert.Len(t, res.Get(\"identities\").Array(), len(patches))\n\n\t\t\tfor i, patch := range patches {\n\t\t\t\tt.Run(fmt.Sprintf(\"assert=identity %d\", i), func(t *testing.T) {\n\t\t\t\t\tidentityID := res.Get(fmt.Sprintf(\"identities.%d.identity\", i)).String()\n\t\t\t\t\trequire.NotEmpty(t, identityID)\n\n\t\t\t\t\tres := get(t, adminTS, \"/identities/\"+identityID, http.StatusOK)\n\t\t\t\t\tsnapshotx.SnapshotT(t, res.Value(), snapshotx.ExceptNestedKeys(\n\t\t\t\t\t\t// All these keys change randomly, so we need to test them individually below\n\t\t\t\t\t\t\"id\", \"schema_url\",\n\t\t\t\t\t\t\"created_at\", \"updated_at\", \"state_changed_at\",\n\t\t\t\t\t\t\"verifiable_addresses\", \"recovery_addresses\", \"identifiers\"))\n\n\t\t\t\t\temails := gjson.Parse(strings.ToLower(gjson.GetBytes(patch.Create.Traits, \"emails\").Raw))\n\t\t\t\t\tassert.Equal(t, identityID, res.Get(\"id\").String())\n\t\t\t\t\tassert.EqualValues(t, patch.Create.Traits, res.Get(\"traits\").Raw)\n\t\t\t\t\tassertJSONArrayElementsMatch(t, emails, res.Get(\"credentials.password.identifiers\"))\n\t\t\t\t\tassertJSONArrayElementsMatch(t, emails, res.Get(\"recovery_addresses.#.value\"))\n\t\t\t\t\tassertJSONArrayElementsMatch(t, emails, res.Get(\"verifiable_addresses.#.value\"))\n\n\t\t\t\t\t// Test that the verified addresses are imported correctly\n\t\t\t\t\tassert.Len(t, res.Get(\"verifiable_addresses.#(verified=true)#\").Array(), 2)\n\t\t\t\t\tassert.Len(t, res.Get(\"verifiable_addresses.#(verified=false)#\").Array(), 2)\n\t\t\t\t\tassert.Len(t, res.Get(\"verifiable_addresses.#(status=pending)#\").Array(), 2)\n\t\t\t\t\tassert.Len(t, res.Get(\"verifiable_addresses.#(status=sent)#\").Array(), 1)\n\t\t\t\t\tassert.Len(t, res.Get(\"verifiable_addresses.#(status=completed)#\").Array(), 1)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"case=PATCH update of state should update state changed at timestamp\", func(t *testing.T) {\n\t\tid := x.NewUUID().String()\n\t\temail := \"UPPER\" + id + \"@ory.sh\"\n\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\": %q, \"email\": %q}`, id, email))}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"replace\", \"path\": \"/state\", \"value\": identity.StateInactive},\n\t\t\t\t}\n\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusOK, &patch)\n\t\t\t\tassert.EqualValues(t, id, res.Get(\"traits.subject\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"traits.email\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_admin.admin\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_public.public\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateInactive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.NotEqualValues(t, i.StateChangedAt, sqlxx.NullTime(res.Get(\"state_changed_at\").Time()), \"%s\", res.Raw)\n\n\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, id, res.Get(\"traits.subject\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"traits.email\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_admin.admin\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_public.public\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateInactive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.NotEqualValues(t, i.StateChangedAt, sqlxx.NullTime(res.Get(\"state_changed_at\").Time()), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH update external_id\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tid := x.NewUUID().String()\n\t\t\texternalID1 := x.NewUUID().String()\n\t\t\texternalID2 := x.NewUUID().String()\n\t\t\temail := \"UPPER\" + id + \"@ory.sh\"\n\t\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\": %q, \"email\": %q}`, id, email))}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tres := get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\tassert.Empty(t, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\n\t\t\t\tt.Run(\"set external_id works\", func(t *testing.T) {\n\t\t\t\t\tres = send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusOK,\n\t\t\t\t\t\t&[]patch{{\"op\": \"replace\", \"path\": \"/external_id\", \"value\": externalID1}})\n\t\t\t\t\tassert.EqualValues(t, externalID1, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t\tassert.EqualValues(t, externalID1, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tres = get(t, ts, \"/identities/by/external/\"+externalID1, http.StatusOK)\n\t\t\t\t\tassert.EqualValues(t, externalID1, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"set external_id to empty clears it\", func(t *testing.T) {\n\t\t\t\t\tres = send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusOK,\n\t\t\t\t\t\t&[]patch{{\"op\": \"replace\", \"path\": \"/external_id\", \"value\": \"\"}})\n\t\t\t\t\tassert.Empty(t, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t\tassert.Empty(t, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tres = get(t, ts, \"/identities/by/external/\"+externalID1, http.StatusNotFound)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"set external_id again works\", func(t *testing.T) {\n\t\t\t\t\tres = send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusOK,\n\t\t\t\t\t\t&[]patch{{\"op\": \"replace\", \"path\": \"/external_id\", \"value\": externalID2}})\n\t\t\t\t\tassert.EqualValues(t, externalID2, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t\tassert.EqualValues(t, externalID2, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\t\tres = get(t, ts, \"/identities/by/external/\"+externalID2, http.StatusOK)\n\t\t\t\t\tassert.EqualValues(t, externalID2, res.Get(\"external_id\").String(), \"%s\", res.Raw)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH update with uppercase emails should work\", func(t *testing.T) {\n\t\t// Regression test for https://github.com/ory/kratos/issues/3187\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\temail := \"UPPER\" + x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\tlowercaseEmail := strings.ToLower(email)\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + email + `\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\t\t\t\tassert.EqualValues(t, lowercaseEmail, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, lowercaseEmail, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tidentityID := res.Get(\"id\").String()\n\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\n\t\t\t\t\t\t\"op\":    \"replace\",\n\t\t\t\t\t\t\"path\":  \"/verifiable_addresses/0/verified\",\n\t\t\t\t\t\t\"value\": true,\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tres = send(t, ts, \"PATCH\", \"/identities/\"+identityID, http.StatusOK, &patch)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"traits.email\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_admin.admin\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_public.public\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateActive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\n\t\t\t\tres = get(t, ts, \"/identities/\"+identityID, http.StatusOK)\n\t\t\t\tassert.EqualValues(t, identityID, res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"traits.email\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_admin.admin\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_public.public\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateActive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH should update verified_at timestamp\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\temail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + email + `\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Falsef(t, res.Get(\"verifiable_addresses.0.verified\").Bool(), \"%s\", res.Raw)\n\t\t\t\tassert.Falsef(t, res.Get(\"verifiable_addresses.0.verified_at\").Exists(), \"%s\", res.Raw)\n\t\t\t\tidentityID := res.Get(\"id\").String()\n\n\t\t\t\t// set to verified, should also update verified_at timestamp\n\t\t\t\tpatch1 := []patch{\n\t\t\t\t\t{\n\t\t\t\t\t\t\"op\":    \"replace\",\n\t\t\t\t\t\t\"path\":  \"/verifiable_addresses/0/verified\",\n\t\t\t\t\t\t\"value\": true,\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tnow := time.Now()\n\n\t\t\t\tres = send(t, ts, \"PATCH\", \"/identities/\"+identityID, http.StatusOK, &patch1)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Truef(t, res.Get(\"verifiable_addresses.0.verified\").Bool(), \"%s\", res.Raw)\n\t\t\t\tassert.WithinDurationf(t, now, res.Get(\"verifiable_addresses.0.updated_at\").Time(), 5*time.Second, \"%s\", res.Raw)\n\t\t\t\tassert.WithinDurationf(t, now, res.Get(\"verifiable_addresses.0.verified_at\").Time(), 5*time.Second, \"%s\", res.Raw)\n\n\t\t\t\tres = get(t, ts, \"/identities/\"+identityID, http.StatusOK)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Truef(t, res.Get(\"verifiable_addresses.0.verified\").Bool(), \"%s\", res.Raw)\n\t\t\t\tassert.WithinDurationf(t, now, res.Get(\"verifiable_addresses.0.updated_at\").Time(), 5*time.Second, \"%s\", res.Raw)\n\t\t\t\tassert.WithinDurationf(t, now, res.Get(\"verifiable_addresses.0.verified_at\").Time(), 5*time.Second, \"%s\", res.Raw)\n\n\t\t\t\t// update only verified_at timestamp\n\t\t\t\tverifiedAt := time.Date(1999, 1, 7, 8, 23, 19, 0, time.UTC)\n\t\t\t\tpatch2 := []patch{\n\t\t\t\t\t{\n\t\t\t\t\t\t\"op\":    \"replace\",\n\t\t\t\t\t\t\"path\":  \"/verifiable_addresses/0/verified_at\",\n\t\t\t\t\t\t\"value\": verifiedAt.Format(time.RFC3339),\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tnow = time.Now()\n\t\t\t\tres = send(t, ts, \"PATCH\", \"/identities/\"+identityID, http.StatusOK, &patch2)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Truef(t, res.Get(\"verifiable_addresses.0.verified\").Bool(), \"%s\", res.Raw)\n\t\t\t\tassert.Equalf(t, verifiedAt, res.Get(\"verifiable_addresses.0.verified_at\").Time(), \"%s\", res.Raw)\n\t\t\t\tassert.WithinDurationf(t, now, res.Get(\"verifiable_addresses.0.updated_at\").Time(), 5*time.Second, \"%s\", res.Raw)\n\n\t\t\t\tres = get(t, ts, \"/identities/\"+identityID, http.StatusOK)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Truef(t, res.Get(\"verifiable_addresses.0.verified\").Bool(), \"%s\", res.Raw)\n\t\t\t\tassert.Equalf(t, verifiedAt, res.Get(\"verifiable_addresses.0.verified_at\").Time(), \"%s\", res.Raw)\n\t\t\t\tassert.WithinDurationf(t, now, res.Get(\"verifiable_addresses.0.updated_at\").Time(), 5*time.Second, \"%s\", res.Raw)\n\n\t\t\t\t// remove verified status\n\t\t\t\tpatch3 := []patch{\n\t\t\t\t\t{\n\t\t\t\t\t\t\"op\":    \"replace\",\n\t\t\t\t\t\t\"path\":  \"/verifiable_addresses/0/verified\",\n\t\t\t\t\t\t\"value\": false,\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tnow = time.Now()\n\n\t\t\t\tres = send(t, ts, \"PATCH\", \"/identities/\"+identityID, http.StatusOK, &patch3)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Falsef(t, res.Get(\"verifiable_addresses.0.verified\").Bool(), \"%s\", res.Raw)\n\t\t\t\tassert.Falsef(t, res.Get(\"verifiable_addresses.0.verified_at\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.WithinDurationf(t, now, res.Get(\"verifiable_addresses.0.updated_at\").Time(), 5*time.Second, \"%s\", res.Raw)\n\n\t\t\t\tres = get(t, ts, \"/identities/\"+identityID, http.StatusOK)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, email, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Falsef(t, res.Get(\"verifiable_addresses.0.verified\").Bool(), \"%s\", res.Raw)\n\t\t\t\tassert.Falsef(t, res.Get(\"verifiable_addresses.0.verified_at\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.WithinDurationf(t, now, res.Get(\"verifiable_addresses.0.updated_at\").Time(), 5*time.Second, \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH update should not persist if schema id is invalid\", func(t *testing.T) {\n\t\tsub := x.NewUUID().String()\n\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, sub))}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"replace\", \"path\": \"/schema_id\", \"value\": \"invalid-id\"},\n\t\t\t\t}\n\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusBadRequest, &patch)\n\t\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), \"invalid-id\", \"%s\", res.Raw)\n\n\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t// Assert that the schema ID is unchanged\n\t\t\t\tassert.EqualValues(t, i.SchemaID, res.Get(\"schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, sub, res.Get(\"traits.subject\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_admin.admin\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_public.public\").Exists(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH update should not persist if invalid state is supplied\", func(t *testing.T) {\n\t\tsub := x.NewUUID().String()\n\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, sub))}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"replace\", \"path\": \"/state\", \"value\": \"invalid-value\"},\n\t\t\t\t}\n\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusBadRequest, &patch)\n\t\t\t\tassert.EqualValues(t, \"The supplied state ('invalid-value') was not valid. Valid states are ('active', 'inactive').\", res.Get(\"error.reason\").String(), \"%s\", res.Raw)\n\n\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t// Assert that the schema ID is unchanged\n\t\t\t\tassert.EqualValues(t, i.SchemaID, res.Get(\"schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, sub, res.Get(\"traits.subject\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_admin.admin\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.False(t, res.Get(\"metadata_public.public\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.NotEqualValues(t, i.StateChangedAt, sqlxx.NullTime(res.Get(\"state_changed_at\").Time()), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH update should update nested fields\", func(t *testing.T) {\n\t\tsub := x.NewUUID().String()\n\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, sub))}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"replace\", \"path\": \"/traits/subject\", \"value\": \"patched-subject\"},\n\t\t\t\t}\n\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusOK, &patch)\n\t\t\t\tassert.EqualValues(t, \"patched-subject\", res.Get(\"traits.subject\").String(), \"%s\", res.Raw)\n\n\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t// Assert that the schema ID is unchanged\n\t\t\t\tassert.EqualValues(t, i.SchemaID, res.Get(\"schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"patched-subject\", res.Get(\"traits.subject\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH should fail if no JSON payload is sent\", func(t *testing.T) {\n\t\tsub := x.NewUUID().String()\n\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, sub))}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusBadRequest, nil)\n\t\t\t\tassert.Equal(t, res.Get(\"error.message\").Str, \"invalid state detected\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH should fail if credentials are updated\", func(t *testing.T) {\n\t\tsub := x.NewUUID().String()\n\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, sub))}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"replace\", \"path\": \"/credentials\", \"value\": \"patched-credentials\"},\n\t\t\t\t}\n\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusBadRequest, &patch)\n\n\t\t\t\tassert.EqualValues(t, \"patch includes denied path: /credentials\", res.Get(\"error.message\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH should fail if credential orgs are updated\", func(t *testing.T) {\n\t\temail := x.NewUUID().String() + \"@ory.sh\"\n\t\ti := &identity.Identity{Traits: identity.Traits(`{\"email\":\"` + email + `\"}`)}\n\t\ti.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\tIdentifiers: []string{email},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\": [{\"provider\": \"some-provider\"}]}`),\n\t\t})\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"replace\", \"path\": \"/credentials/oidc/config/providers/0/organization\", \"value\": \"foo\"},\n\t\t\t\t}\n\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusBadRequest, &patch)\n\n\t\t\t\tassert.EqualValues(t, \"patch includes denied path: /credentials/oidc/config/providers/0/organization\", res.Get(\"error.message\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH should allow to update credential password\", func(t *testing.T) {\n\t\temail := uuid.NewV5(uuid.Nil, t.Name()).String() + \"@ory.sh\"\n\t\ti := &identity.Identity{Traits: identity.Traits(`{\"email\":\"` + email + `\"}`)}\n\t\ti.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{email},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\": \"secret\", \"some-random-key\":\" some-random-value\"}`),\n\t\t})\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*i), snapshotx.ExceptNestedKeys(ignoreDefault...))\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"replace\", \"path\": \"/credentials/password/config/hashed_password\", \"value\": \"foo\"},\n\t\t\t\t}\n\n\t\t\t\tsend(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusOK, &patch)\n\n\t\t\t\tupdated, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), i.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, \"foo\",\n\t\t\t\t\tgjson.GetBytes(updated.Credentials[identity.CredentialsTypePassword].Config, \"hashed_password\").String())\n\t\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*updated), snapshotx.ExceptNestedKeys(ignoreDefault...))\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH should not invalidate credentials ory/cloud#148\", func(t *testing.T) {\n\t\t// see https://github.com/ory/cloud/issues/148\n\n\t\tcreateCredentials := func(t *testing.T) (*identity.Identity, string, string) {\n\t\t\tt.Helper()\n\t\t\temail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\tpassword := \"ljanf123akf\"\n\t\t\tp, err := reg.Hasher(t.Context()).Generate(context.Background(), []byte(password))\n\t\t\trequire.NoError(t, err)\n\t\t\ti := &identity.Identity{Traits: identity.Traits(`{\"email\":\"` + email + `\"}`)}\n\t\t\ti.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t\t})\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\t\treturn i, email, password\n\t\t}\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\ti, email, password := createCredentials(t)\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", email)\n\t\t\t\t\tv.Set(\"password\", password)\n\t\t\t\t}\n\n\t\t\t\t// verify login works initially\n\t\t\t\tloginResponse := testhelpers.SubmitLoginForm(t, true, ts.Client(), ts, values, false, true, 200, \"\")\n\t\t\t\trequire.NotEmpty(t, gjson.Get(loginResponse, \"session_token\").String(), \"expected to find a session token, found none\")\n\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"replace\", \"path\": \"/metadata_public\", \"value\": map[string]string{\"role\": \"user\"}},\n\t\t\t\t}\n\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusOK, &patch)\n\t\t\t\tassert.EqualValues(t, \"user\", res.Get(\"metadata_public.role\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.NotEqualValues(t, i.StateChangedAt, sqlxx.NullTime(res.Get(\"state_changed_at\").Time()), \"%s\", res.Raw)\n\n\t\t\t\tloginResponse = testhelpers.SubmitLoginForm(t, true, ts.Client(), ts, values, false, true, 200, \"\")\n\t\t\t\tmsgs := gjson.Get(loginResponse, \"ui.messages\")\n\t\t\t\trequire.Empty(t, msgs.Array(), \"expected to find no messages: %s\", msgs.String())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH should update metadata_admin correctly\", func(t *testing.T) {\n\t\ti := &identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, x.NewUUID().String()))}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"add\", \"path\": \"/metadata_admin\", \"value\": \"metadata admin\"},\n\t\t\t\t}\n\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusOK, &patch)\n\n\t\t\t\tassert.True(t, res.Get(\"metadata_admin\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"metadata admin\", res.Get(\"metadata_admin\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=PATCH should update nested metadata_admin fields correctly\", func(t *testing.T) {\n\t\tid := x.NewUUID().String()\n\t\ti := &identity.Identity{MetadataAdmin: sqlxx.NullJSONRawMessage(fmt.Sprintf(`{\"id\": \"%s\", \"allowed\": true}`, id))}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tpatch := []patch{\n\t\t\t\t\t{\"op\": \"replace\", \"path\": \"/metadata_admin/allowed\", \"value\": \"false\"},\n\t\t\t\t}\n\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/\"+i.ID.String(), http.StatusOK, &patch)\n\n\t\t\t\tassert.True(t, res.Get(\"metadata_admin.allowed\").Exists(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, false, res.Get(\"metadata_admin.allowed\").Bool(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, id, res.Get(\"metadata_admin.id\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should return entity with credentials metadata\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t// create identity with credentials\n\t\t\t\ti := identity.NewIdentity(\"\")\n\t\t\t\ti.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\t\tType:   identity.CredentialsTypePassword,\n\t\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\"secret\":\"pst\"}`),\n\t\t\t\t})\n\t\t\t\ti.Traits = identity.Traits(\"{}\")\n\n\t\t\t\trequire.NoError(t, reg.Persister().CreateIdentity(context.Background(), i))\n\t\t\t\tres := get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.True(t, res.Get(\"credentials\").Exists())\n\t\t\t\t// Should contain changed date\n\t\t\t\tassert.True(t, res.Get(\"credentials.password.updated_at\").Exists())\n\t\t\t\t// Should not contain secrets\n\t\t\t\tassert.False(t, res.Get(\"credentials.password.config\").Exists())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should not be able to create an identity with an invalid schema\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"unknown\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusBadRequest, &cr)\n\t\t\t\tassert.Contains(t, res.Raw, \"unknown\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should not be able to create an identity with an invalid state\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\"}`)\n\t\t\t\tcr.State = \"invalid-state\"\n\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusBadRequest, &cr)\n\t\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), `identity state is not valid`, \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should create an identity with a different schema\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\"}`)\n\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\t\t\t\tassert.JSONEq(t, string(cr.Traits), res.Get(\"traits\").Raw, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"employee\", res.Get(\"schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateActive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, mockServerURL.String()+\"/schemas/ZW1wbG95ZWU\", res.Get(\"schema_url\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should create an identity with an explicit active state\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\"}`)\n\t\t\t\tcr.State = identity.StateActive\n\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\t\t\t\tassert.JSONEq(t, string(cr.Traits), res.Get(\"traits\").Raw, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"employee\", res.Get(\"schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateActive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, mockServerURL.String()+\"/schemas/ZW1wbG95ZWU\", res.Get(\"schema_url\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should create an identity with an explicit inactive state\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\"}`)\n\t\t\t\tcr.State = identity.StateInactive\n\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\t\t\t\tassert.JSONEq(t, string(cr.Traits), res.Get(\"traits\").Raw, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"employee\", res.Get(\"schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, identity.StateInactive, res.Get(\"state\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, mockServerURL.String()+\"/schemas/ZW1wbG95ZWU\", res.Get(\"schema_url\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should create and sync metadata and update privileged traits\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\toriginalEmail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + originalEmail + `\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\t\t\t\tassert.EqualValues(t, originalEmail, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, originalEmail, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\n\t\t\t\tid := res.Get(\"id\").String()\n\t\t\t\tupdatedEmail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\tres = send(t, ts, \"PUT\", \"/identities/\"+id, http.StatusOK, &identity.UpdateIdentityBody{\n\t\t\t\t\tTraits: []byte(`{\"email\":\"` + updatedEmail + `\", \"department\": \"ory\"}`),\n\t\t\t\t})\n\n\t\t\t\tassert.EqualValues(t, \"employee\", res.Get(\"schema_id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, mockServerURL.String()+\"/schemas/ZW1wbG95ZWU\", res.Get(\"schema_url\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, updatedEmail, res.Get(\"traits.email\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, \"ory\", res.Get(\"traits.department\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, updatedEmail, res.Get(\"recovery_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, updatedEmail, res.Get(\"verifiable_addresses.0.value\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should update the schema id and fail because traits are invalid\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\", \"department\": \"ory\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\n\t\t\t\tid := res.Get(\"id\").String()\n\t\t\t\tres = send(t, ts, \"PUT\", \"/identities/\"+id, http.StatusBadRequest, &identity.UpdateIdentityBody{\n\t\t\t\t\tSchemaID: \"customer\",\n\t\t\t\t\tTraits:   cr.Traits,\n\t\t\t\t})\n\t\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), `additionalProperties \"department\" not allowed`, \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail to update identity if state is invalid\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\", \"department\": \"ory\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\n\t\t\t\tid := res.Get(\"id\").String()\n\t\t\t\tres = send(t, ts, \"PUT\", \"/identities/\"+id, http.StatusBadRequest, &identity.UpdateIdentityBody{\n\t\t\t\t\tState:  \"invalid-state\",\n\t\t\t\t\tTraits: []byte(`{\"email\":\"` + faker.Email() + `\", \"department\": \"ory\"}`),\n\t\t\t\t})\n\t\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), `identity state is not valid`, \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should update the schema id\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\", \"department\": \"ory\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\n\t\t\t\tid := res.Get(\"id\").String()\n\t\t\t\tres = send(t, ts, \"PUT\", \"/identities/\"+id, http.StatusOK, &identity.UpdateIdentityBody{\n\t\t\t\t\tSchemaID: \"customer\",\n\t\t\t\t\tTraits:   []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\", \"address\": \"ory street\"}`),\n\t\t\t\t})\n\t\t\t\tassert.EqualValues(t, \"ory street\", res.Get(\"traits.address\").String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should be able to update multiple identities\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tfor i := 0; i <= 5; i++ {\n\t\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\t\tcr.Traits = []byte(`{\"department\": \"ory\"}`)\n\t\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\n\t\t\t\t\tid := res.Get(\"id\").String()\n\t\t\t\t\t_ = send(t, ts, \"PUT\", \"/identities/\"+id, http.StatusOK, &identity.UpdateIdentityBody{\n\t\t\t\t\t\tSchemaID: \"employee\",\n\t\t\t\t\t\tTraits:   []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\"}`),\n\t\t\t\t\t})\n\n\t\t\t\t\t_ = send(t, ts, \"PUT\", \"/identities/\"+id, http.StatusOK, &identity.UpdateIdentityBody{\n\t\t\t\t\t\tSchemaID: \"employee\",\n\t\t\t\t\t\tTraits:   []byte(`{}`),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail to update identity if input json is empty or json file does not exist\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tvar cr identity.CreateIdentityBody\n\t\t\t\tcr.SchemaID = \"employee\"\n\t\t\t\tcr.Traits = []byte(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\", \"department\": \"ory\"}`)\n\t\t\t\tres := send(t, ts, \"POST\", \"/identities\", http.StatusCreated, &cr)\n\n\t\t\t\tid := res.Get(\"id\").String()\n\t\t\t\tres = send(t, ts, \"PUT\", \"/identities/\"+id, http.StatusBadRequest, nil)\n\t\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), `Unable to decode HTTP Request Body because its HTTP `+\n\t\t\t\t\t`Header \"Content-Length\" is zero`, \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should list all identities\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tres := get(t, ts, \"/identities\", http.StatusOK)\n\t\t\t\tassert.Empty(t, res.Get(\"#.credentials\").Array(), \"credentials config should be omitted\")\n\t\t\t\tassert.Equal(t, res.Get(\"#\").Int(), res.Get(\"#.metadata_public|#\").Int(), \"metadata_public config should be included\")\n\t\t\t\tassert.Equal(t, res.Get(\"#\").Int(), res.Get(\"#.metadata_admin|#\").Int(), \"metadata_admin config should be included\")\n\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(`#(traits.bar==\"baz\").traits.bar`).String())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"organizations\", func(t *testing.T) {\n\t\tt.Run(\"case=should list organization identities\", func(t *testing.T) {\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\torgID := uuid.Must(uuid.NewV4())\n\t\t\t\t\temail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), &identity.Identity{\n\t\t\t\t\t\tTraits:         identity.Traits(`{\"email\":\"` + email + `\"}`),\n\t\t\t\t\t\tOrganizationID: uuid.NullUUID{UUID: orgID, Valid: true},\n\t\t\t\t\t}))\n\n\t\t\t\t\tres := get(t, ts, \"/identities?organization_id=\"+orgID.String(), http.StatusOK)\n\t\t\t\t\tassert.Len(t, res.Array(), 1)\n\t\t\t\t\tassert.EqualValues(t, email, res.Get(`0.traits.email`).String(), \"%s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=malformed organization id should return an error\", func(t *testing.T) {\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tres := get(t, ts, \"/identities?organization_id=not-a-uuid\", http.StatusBadRequest)\n\t\t\t\t\tassert.Contains(t, res.Get(\"error.reason\").String(), \"Invalid UUID value `not-a-uuid` for parameter `organization_id`.\", \"%s\", res.Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=unknown organization id should return an empty list\", func(t *testing.T) {\n\t\t\tfor name, ts := range map[string]*httptest.Server{\"admin\": adminTS} {\n\t\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\t\tid := x.NewUUID()\n\t\t\t\t\tres := get(t, ts, \"/identities?organization_id=\"+id.String(), http.StatusOK)\n\t\t\t\t\tassert.Empty(t, res.Array())\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"case=should list all identities with credentials\", func(t *testing.T) {\n\t\tt.Run(\"include_credential=oidc should include OIDC credentials config\", func(t *testing.T) {\n\t\t\tres := get(t, adminTS, \"/identities?include_credential=oidc\", http.StatusOK)\n\t\t\trequire.True(t, res.IsArray())\n\t\t\trequire.GreaterOrEqual(t, len(res.Array()), 2)\n\t\t\tvar foundOIDC, foundSAML bool\n\t\t\tfor _, id := range res.Array() {\n\t\t\t\tif id.Get(\"credentials.oidc.identifiers.0\").Str == \"bar:foo.oidc@bar.com\" {\n\t\t\t\t\tfoundOIDC = true\n\t\t\t\t\tsnapshotx.SnapshotT(t, id.Get(\"credentials.oidc.config\").String())\n\t\t\t\t}\n\t\t\t\tif id.Get(\"credentials.saml.identifiers.0\").Str == \"bar:foo.saml@bar.com\" {\n\t\t\t\t\tfoundSAML = true\n\t\t\t\t\tassert.False(t, id.Get(\"credentials.saml.config\").Exists(), \"SAML config is not included\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tassert.True(t, foundOIDC, \"OIDC credential included\")\n\t\t\tassert.True(t, foundSAML, \"SAML credential included\")\n\t\t})\n\n\t\tt.Run(\"include_credential=saml should not include SAML tokens\", func(t *testing.T) {\n\t\t\tres := get(t, adminTS, \"/identities?include_credential=saml\", http.StatusOK)\n\t\t\tsamlProviders := res.Get(\"#.credentials.saml.config.providers|@flatten\")\n\t\t\tassert.Greaterf(t, samlProviders.Get(\"#.subject|#\").Int(), int64(0), \"SAML config should contain subject: %s\", samlProviders.Raw)\n\t\t\tassert.Zerof(t, res.Get(\"#.initial_id_token|#\").Int(), \"SAML config should not contain initial_id_token: %s\", samlProviders.Raw)\n\t\t})\n\t\tt.Run(\"include_credential=totp should not include OIDC credentials config\", func(t *testing.T) {\n\t\t\tres := get(t, adminTS, \"/identities?include_credential=totp\", http.StatusOK)\n\t\t\tassert.Empty(t, res.Get(\"#.credentials.oidc.config\").Array(), \"OIDC config should not be included\")\n\t\t\tassert.Empty(t, res.Get(\"#.credentials.saml.config\").Array(), \"SAML config should not be included\")\n\t\t})\n\t})\n\n\tt.Run(\"case=should not be able to list all identities with credentials due to wrong credentials type\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tres := get(t, ts, \"/identities?include_credential=XYZ\", http.StatusBadRequest)\n\t\t\t\tassert.Contains(t, res.Get(\"error.message\").String(), \"The request was malformed or contained invalid parameters\", \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should list all identities with eventual consistency\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tres := get(t, ts, \"/identities?consistency=eventual\", http.StatusOK)\n\t\t\t\tassert.EqualValues(t, \"baz\", res.Get(`#(traits.bar==\"baz\").traits.bar`).String(), \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should not be able to update an identity that does not exist yet\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tres := send(t, ts, \"PUT\", \"/identities/not-found\", http.StatusNotFound, json.RawMessage(`{\"traits\": {\"bar\":\"baz\"}}`))\n\t\t\t\tassert.Contains(t, res.Get(\"error.message\").String(), \"Unable to locate the resource\", \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should not be able to patch an identity that does not exist yet\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tres := send(t, ts, \"PATCH\", \"/identities/not-found\", http.StatusNotFound, json.RawMessage(`{\"traits\": {\"bar\":\"baz\"}}`))\n\t\t\t\tassert.Contains(t, res.Get(\"error.message\").String(), \"Unable to locate the resource\", \"%s\", res.Raw)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should return 404 for non-existing identities\", func(t *testing.T) {\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"endpoint=\"+name, func(t *testing.T) {\n\t\t\t\tremove(t, ts, \"/identities/\"+x.NewUUID().String(), http.StatusNotFound)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should delete credential of a specific user and no longer be able to retrieve it\", func(t *testing.T) {\n\t\ttype M = map[identity.CredentialsType]identity.Credentials\n\t\tcreateIdentity := func(creds M) func(*testing.T) *identity.Identity {\n\t\t\treturn func(t *testing.T) *identity.Identity {\n\t\t\t\ti := identity.NewIdentity(\"\")\n\t\t\t\tfor k, v := range creds {\n\t\t\t\t\tv.Type = k\n\t\t\t\t\tcreds[k] = v\n\t\t\t\t}\n\t\t\t\ti.Credentials = creds\n\t\t\t\ti.Traits = identity.Traits(\"{}\")\n\t\t\t\trequire.NoError(t, reg.Persister().CreateIdentity(context.Background(), i))\n\t\t\t\treturn i\n\t\t\t}\n\t\t}\n\t\tfor name, ts := range map[string]*httptest.Server{\"public\": publicTS, \"admin\": adminTS} {\n\t\t\tt.Run(\"type=remove unknown identity/\"+name, func(t *testing.T) {\n\t\t\t\tremove(t, ts, \"/identities/\"+x.NewUUID().String()+\"/credentials/azerty\", http.StatusNotFound)\n\t\t\t})\n\t\t\tt.Run(\"type=remove unknown type/\"+name, func(t *testing.T) {\n\t\t\t\ti := createIdentity(M{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tConfig:      []byte(`{\"hashed_password\":\"some_valid_hash\"}`),\n\t\t\t\t\t\tIdentifiers: []string{x.NewUUID().String()},\n\t\t\t\t\t},\n\t\t\t\t})(t)\n\t\t\t\tremove(t, ts, \"/identities/\"+i.ID.String()+\"/credentials/azerty\", http.StatusNotFound)\n\t\t\t})\n\t\t\tt.Run(\"type=deny to remove password type/\"+name, func(t *testing.T) {\n\t\t\t\ti := createIdentity(M{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tConfig:      []byte(`{\"hashed_password\":\"some_valid_hash\"}`),\n\t\t\t\t\t\tIdentifiers: []string{x.NewUUID().String()},\n\t\t\t\t\t},\n\t\t\t\t})(t)\n\t\t\t\tremove(t, ts, \"/identities/\"+i.ID.String()+\"/credentials/password\", http.StatusBadRequest)\n\t\t\t})\n\t\t\tt.Run(\"type=allow to remove password type/\"+name, func(t *testing.T) {\n\t\t\t\tsub := x.NewUUID().String()\n\t\t\t\tpwIdentifier := x.NewUUID().String()\n\t\t\t\ti := createIdentity(M{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tConfig:      []byte(`{\"hashed_password\":\"some_valid_hash\"}`),\n\t\t\t\t\t\tIdentifiers: []string{pwIdentifier},\n\t\t\t\t\t},\n\t\t\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\t\t\tConfig:      []byte(fmt.Sprintf(`{\"providers\":[{\"subject\":\"%s\",\"provider\":\"gh\"}]}`, sub)),\n\t\t\t\t\t\tIdentifiers: []string{identity.OIDCUniqueID(\"gh\", sub)},\n\t\t\t\t\t},\n\t\t\t\t})(t)\n\t\t\t\tremove(t, ts, \"/identities/\"+i.ID.String()+\"/credentials/password\", http.StatusNoContent)\n\t\t\t\tactual, creds, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePassword, pwIdentifier)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, \"{}\", string(creds.Config))\n\t\t\t\tassert.Equal(t, i.ID, actual.ID)\n\t\t\t})\n\t\t\tt.Run(\"type=remove oidc type/\"+name, func(t *testing.T) {\n\t\t\t\t// force ordering among github identifiers\n\t\t\t\tgithubSubject := \"0\" + randx.MustString(7, randx.Numeric)\n\t\t\t\tgithubSubject2 := \"1\" + randx.MustString(7, randx.Numeric)\n\t\t\t\tgoogleSubject := randx.MustString(8, randx.Numeric)\n\t\t\t\tinitialConfig := []byte(fmt.Sprintf(`{\n\t\t\t\t\t\"providers\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"subject\": %q,\n\t\t\t\t\t\t\t\"provider\": \"github\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"subject\": %q,\n\t\t\t\t\t\t\t\"provider\": \"github\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"subject\": %q,\n\t\t\t\t\t\t\t\"provider\": \"google\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}`, githubSubject, githubSubject2, googleSubject))\n\t\t\t\tidentifiers := []string{\n\t\t\t\t\tidentity.OIDCUniqueID(\"github\", githubSubject),\n\t\t\t\t\tidentity.OIDCUniqueID(\"github\", githubSubject2),\n\t\t\t\t\tidentity.OIDCUniqueID(\"google\", googleSubject),\n\t\t\t\t}\n\t\t\t\ti := createIdentity(M{\n\t\t\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\t\t\tIdentifiers: identifiers,\n\t\t\t\t\t\tConfig:      initialConfig,\n\t\t\t\t\t},\n\t\t\t\t})(t)\n\t\t\t\tres := get(t, ts, \"/identities/\"+i.ID.String()+\"?include_credential=oidc\", http.StatusOK)\n\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Len(t, res.Get(\"credentials.oidc.identifiers\").Array(), 3, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.oidc.identifiers.0\").String(), identifiers[0], \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.oidc.identifiers.1\").String(), identifiers[1], \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.oidc.identifiers.2\").String(), identifiers[2], \"%s\", res.Raw)\n\n\t\t\t\toidConfig := gjson.Parse(res.Get(\"credentials.oidc.config\").String())\n\t\t\t\tassert.Len(t, res.Get(\"credentials.oidc.identifiers\").Array(), 3, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.0.provider\").String(), \"github\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.0.subject\").String(), githubSubject, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.1.provider\").String(), \"github\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.1.subject\").String(), githubSubject2, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.2.provider\").String(), \"google\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.2.subject\").String(), googleSubject, \"%s\", res.Raw)\n\n\t\t\t\tremove(t, ts, \"/identities/\"+i.ID.String()+\"/credentials/oidc?identifier=\"+identifiers[1], http.StatusNoContent)\n\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String()+\"?include_credential=oidc\", http.StatusOK)\n\n\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Len(t, res.Get(\"credentials.oidc.identifiers\").Array(), 2, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.oidc.identifiers.0\").String(), identifiers[0], \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.oidc.identifiers.1\").String(), identifiers[2], \"%s\", res.Raw)\n\n\t\t\t\toidConfig = gjson.Parse(res.Get(\"credentials.oidc.config\").String())\n\t\t\t\tassert.Len(t, res.Get(\"credentials.oidc.identifiers\").Array(), 2, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.0.provider\").String(), \"github\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.0.subject\").String(), githubSubject, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.1.provider\").String(), \"google\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.1.subject\").String(), googleSubject, \"%s\", res.Raw)\n\t\t\t})\n\t\t\tt.Run(\"type=remove saml type/\"+name, func(t *testing.T) {\n\t\t\t\t// force ordering among identifiers\n\t\t\t\tentraSubject1 := \"0\" + randx.MustString(7, randx.Numeric)\n\t\t\t\tentraSubject2 := \"1\" + randx.MustString(7, randx.Numeric)\n\t\t\t\toktaSubject := randx.MustString(8, randx.Numeric)\n\t\t\t\tinitialConfig := []byte(fmt.Sprintf(`{\n\t\t\t\t\t\"providers\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"subject\": %q,\n\t\t\t\t\t\t\t\"provider\": \"entra\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"subject\": %q,\n\t\t\t\t\t\t\t\"provider\": \"entra\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"subject\": %q,\n\t\t\t\t\t\t\t\"provider\": \"okta\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}`, entraSubject1, entraSubject2, oktaSubject))\n\t\t\t\tidentifiers := []string{\n\t\t\t\t\tidentity.OIDCUniqueID(\"entra\", entraSubject1),\n\t\t\t\t\tidentity.OIDCUniqueID(\"entra\", entraSubject2),\n\t\t\t\t\tidentity.OIDCUniqueID(\"okta\", oktaSubject),\n\t\t\t\t}\n\t\t\t\ti := createIdentity(M{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tIdentifiers: []string{x.NewUUID().String()},\n\t\t\t\t\t\tConfig:      []byte(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`), // foobar\n\t\t\t\t\t},\n\t\t\t\t\tidentity.CredentialsTypeWebAuthn: {\n\t\t\t\t\t\tIdentifiers: []string{x.NewUUID().String()},\n\t\t\t\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":true}]}`),\n\t\t\t\t\t},\n\t\t\t\t\tidentity.CredentialsTypeSAML: {\n\t\t\t\t\t\tIdentifiers: identifiers,\n\t\t\t\t\t\tConfig:      initialConfig,\n\t\t\t\t\t},\n\t\t\t\t})(t)\n\t\t\t\tres := get(t, ts, \"/identities/\"+i.ID.String()+\"?include_credential=saml\", http.StatusOK)\n\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Len(t, res.Get(\"credentials.saml.identifiers\").Array(), 3, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.saml.identifiers.0\").String(), identifiers[0], \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.saml.identifiers.1\").String(), identifiers[1], \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.saml.identifiers.2\").String(), identifiers[2], \"%s\", res.Raw)\n\n\t\t\t\toidConfig := gjson.Parse(res.Get(\"credentials.saml.config\").String())\n\t\t\t\tassert.Len(t, res.Get(\"credentials.saml.identifiers\").Array(), 3, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.0.provider\").String(), \"entra\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.0.subject\").String(), entraSubject1, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.1.provider\").String(), \"entra\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.1.subject\").String(), entraSubject2, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.2.provider\").String(), \"okta\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.2.subject\").String(), oktaSubject, \"%s\", res.Raw)\n\n\t\t\t\tremove(t, ts, \"/identities/\"+i.ID.String()+\"/credentials/saml?identifier=\"+identifiers[1], http.StatusNoContent)\n\t\t\t\tres = get(t, ts, \"/identities/\"+i.ID.String()+\"?include_credential=saml\", http.StatusOK)\n\n\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\t\t\t\tassert.Len(t, res.Get(\"credentials.saml.identifiers\").Array(), 2, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.saml.identifiers.0\").String(), identifiers[0], \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, res.Get(\"credentials.saml.identifiers.1\").String(), identifiers[2], \"%s\", res.Raw)\n\n\t\t\t\toidConfig = gjson.Parse(res.Get(\"credentials.saml.config\").String())\n\t\t\t\tassert.Len(t, res.Get(\"credentials.saml.identifiers\").Array(), 2, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.0.provider\").String(), \"entra\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.0.subject\").String(), entraSubject1, \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.1.provider\").String(), \"okta\", \"%s\", res.Raw)\n\t\t\t\tassert.EqualValues(t, oidConfig.Get(\"providers.1.subject\").String(), oktaSubject, \"%s\", res.Raw)\n\t\t\t})\n\t\t\tt.Run(\"type=remove webauthn passwordless type/\"+name, func(t *testing.T) {\n\t\t\t\texpected := `{\"credentials\":[{\"id\":\"THTndqZP5Mjvae1BFvJMaMfEMm7O7HE1ju+7PBaYA7Y=\",\"added_at\":\"2022-12-16T14:11:55Z\",\"public_key\":\"pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU=\",\"display_name\":\"test\",\"authenticator\":{\"aaguid\":\"rc4AAjW8xgpkiwsl8fBVAw==\",\"sign_count\":0,\"clone_warning\":false},\"is_passwordless\":true,\"attestation_type\":\"none\"}],\"user_handle\":\"Ef5JiMpMRwuzauWs/9J0gQ==\"}`\n\t\t\t\ti := createIdentity(M{identity.CredentialsTypeWebAuthn: {Config: []byte(expected)}})(t)\n\t\t\t\tremove(t, ts, \"/identities/\"+i.ID.String()+\"/credentials/webauthn\", http.StatusNoContent)\n\t\t\t\t// Check that webauthn has not been deleted\n\t\t\t\tres := get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\n\t\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), uuid.FromStringOrNil(res.Get(\"id\").String()))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), snapshotx.ExceptNestedKeys(append(ignoreDefault, \"hashed_password\")...), snapshotx.ExceptPaths(\"credentials.oidc.identifiers\"))\n\t\t\t})\n\t\t\tt.Run(\"type=remove webauthn passwordless and multiple fido mfa type/\"+name, func(t *testing.T) {\n\t\t\t\tmessage, err := json.Marshal(identity.CredentialsWebAuthnConfig{\n\t\t\t\t\tCredentials: identity.CredentialsWebAuthn{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Passwordless 1\n\t\t\t\t\t\t\tID:          []byte(\"THTndqZP5Mjvae1BFvJMaMfEMm7O7HE1ju+7PBaYA7Y=\"),\n\t\t\t\t\t\t\tAddedAt:     time.Date(2022, 12, 16, 14, 11, 55, 0, time.UTC),\n\t\t\t\t\t\t\tPublicKey:   []byte(\"pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU=\"),\n\t\t\t\t\t\t\tDisplayName: \"test\",\n\t\t\t\t\t\t\tAuthenticator: &identity.AuthenticatorWebAuthn{\n\t\t\t\t\t\t\t\tAAGUID:       []byte(\"rc4AAjW8xgpkiwsl8fBVAw==\"),\n\t\t\t\t\t\t\t\tSignCount:    0,\n\t\t\t\t\t\t\t\tCloneWarning: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tIsPasswordless:  true,\n\t\t\t\t\t\t\tAttestationType: \"none\",\n\t\t\t\t\t\t}, {\n\t\t\t\t\t\t\t// Passwordless 2\n\t\t\t\t\t\t\tID:          []byte(\"THTndqZP5Mjvae1BFvJMaMfEMm7O7HE2ju+7PBaYA7Y=\"),\n\t\t\t\t\t\t\tAddedAt:     time.Date(2022, 12, 16, 14, 11, 55, 0, time.UTC),\n\t\t\t\t\t\t\tPublicKey:   []byte(\"pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU=\"),\n\t\t\t\t\t\t\tDisplayName: \"test\",\n\t\t\t\t\t\t\tAuthenticator: &identity.AuthenticatorWebAuthn{\n\t\t\t\t\t\t\t\tAAGUID:       []byte(\"rc4AAjW8xgpkiwsl8fBVAw==\"),\n\t\t\t\t\t\t\t\tSignCount:    0,\n\t\t\t\t\t\t\t\tCloneWarning: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tIsPasswordless:  true,\n\t\t\t\t\t\t\tAttestationType: \"none\",\n\t\t\t\t\t\t}, {\n\t\t\t\t\t\t\t// MFA 1\n\t\t\t\t\t\t\tID:          []byte(\"THTndqZP5Mjvae1BFvJMaMfEMm7O7HE3ju+7PBaYA7Y=\"),\n\t\t\t\t\t\t\tAddedAt:     time.Date(2022, 12, 16, 14, 11, 55, 0, time.UTC),\n\t\t\t\t\t\t\tPublicKey:   []byte(\"pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU=\"),\n\t\t\t\t\t\t\tDisplayName: \"test\",\n\t\t\t\t\t\t\tAuthenticator: &identity.AuthenticatorWebAuthn{\n\t\t\t\t\t\t\t\tAAGUID:       []byte(\"rc4AAjW8xgpkiwsl8fBVAw==\"),\n\t\t\t\t\t\t\t\tSignCount:    0,\n\t\t\t\t\t\t\t\tCloneWarning: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tIsPasswordless:  false,\n\t\t\t\t\t\t\tAttestationType: \"none\",\n\t\t\t\t\t\t}, {\n\t\t\t\t\t\t\t// MFA 2\n\t\t\t\t\t\t\tID:          []byte(\"THTndqZP5Mjvae1BFvJMaMfEMm7O7HE4ju+7PBaYA7Y=\"),\n\t\t\t\t\t\t\tAddedAt:     time.Date(2022, 12, 16, 14, 11, 55, 0, time.UTC),\n\t\t\t\t\t\t\tPublicKey:   []byte(\"pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU=\"),\n\t\t\t\t\t\t\tDisplayName: \"test\",\n\t\t\t\t\t\t\tAuthenticator: &identity.AuthenticatorWebAuthn{\n\t\t\t\t\t\t\t\tAAGUID:       []byte(\"rc4AAjW8xgpkiwsl8fBVAw==\"),\n\t\t\t\t\t\t\t\tSignCount:    0,\n\t\t\t\t\t\t\t\tCloneWarning: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tIsPasswordless:  false,\n\t\t\t\t\t\t\tAttestationType: \"none\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tUserHandle: []byte(\"Ef5JiMpMRwuzauWs/9J0gQ==\"),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\ti := createIdentity(M{identity.CredentialsTypeWebAuthn: {Config: message}})(t)\n\t\t\t\tremove(t, ts, \"/identities/\"+i.ID.String()+\"/credentials/webauthn\", http.StatusNoContent)\n\t\t\t\t// Check that webauthn has not been deleted\n\t\t\t\tres := get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\tassert.EqualValues(t, i.ID.String(), res.Get(\"id\").String(), \"%s\", res.Raw)\n\n\t\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), uuid.FromStringOrNil(res.Get(\"id\").String()))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tsnapshotx.SnapshotT(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), snapshotx.ExceptNestedKeys(append(ignoreDefault, \"hashed_password\")...), snapshotx.ExceptPaths(\"credentials.oidc.identifiers\"))\n\t\t\t})\n\t\t\tfor ct, ctConf := range map[identity.CredentialsType][]byte{\n\t\t\t\tidentity.CredentialsTypeLookup:   []byte(`{\"recovery_codes\": [{\"code\": \"aaa\"}]}`),\n\t\t\t\tidentity.CredentialsTypeTOTP:     []byte(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\tidentity.CredentialsTypeWebAuthn: []byte(`{\"credentials\":[{\"id\":\"THTndqZP5Mjvae1BFvJMaMfEMm7O7HE1ju+7PBaYA7Y=\",\"added_at\":\"2022-12-16T14:11:55Z\",\"public_key\":\"pQECAyYgASFYIMJLQhJxQRzhnKPTcPCUODOmxYDYo2obrm9bhp5lvSZ3IlggXjhZvJaPUqF9PXqZqTdWYPR7R+b2n/Wi+IxKKXsS4rU=\",\"display_name\":\"test\",\"authenticator\":{\"aaguid\":\"rc4AAjW8xgpkiwsl8fBVAw==\",\"sign_count\":0,\"clone_warning\":false},\"is_passwordless\":false,\"attestation_type\":\"none\"}],\"user_handle\":\"Ef5JiMpMRwuzauWs/9J0gQ==\"}`),\n\t\t\t} {\n\t\t\t\tt.Run(\"type=remove \"+string(ct)+\"/\"+name, func(t *testing.T) {\n\t\t\t\t\tfor _, tc := range []struct {\n\t\t\t\t\t\tdesc  string\n\t\t\t\t\t\texist bool\n\t\t\t\t\t\tsetup func(t *testing.T) *identity.Identity\n\t\t\t\t\t}{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdesc:  \"with\",\n\t\t\t\t\t\t\texist: true,\n\t\t\t\t\t\t\tsetup: createIdentity(M{\n\t\t\t\t\t\t\t\tidentity.CredentialsTypePassword: {Config: []byte(`{\"secret\":\"pst\"}`)},\n\t\t\t\t\t\t\t\tct:                               {Config: ctConf},\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdesc:  \"without\",\n\t\t\t\t\t\t\texist: false,\n\t\t\t\t\t\t\tsetup: createIdentity(M{\n\t\t\t\t\t\t\t\tidentity.CredentialsTypePassword: {Config: []byte(`{\"secret\":\"pst\"}`)},\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdesc:  \"multiple\",\n\t\t\t\t\t\t\texist: true,\n\t\t\t\t\t\t\tsetup: createIdentity(M{\n\t\t\t\t\t\t\t\tidentity.CredentialsTypePassword: {Config: []byte(`{\"secret\":\"pst\"}`)},\n\t\t\t\t\t\t\t\tidentity.CredentialsTypeOIDC:     {Config: []byte(`{\"id\":\"pst\"}`)},\n\t\t\t\t\t\t\t\tct:                               {Config: ctConf},\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\t\tt.Run(\"type=remove \"+string(ct)+\"/\"+name+\"/\"+tc.desc, func(t *testing.T) {\n\t\t\t\t\t\t\ti := tc.setup(t)\n\t\t\t\t\t\t\tcredName := string(ct)\n\t\t\t\t\t\t\t// Initial Querying\n\t\t\t\t\t\t\tresBefore := get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t\t\t\tassert.EqualValues(t, i.ID.String(), resBefore.Get(\"id\").String(), \"%s\", resBefore.Raw)\n\t\t\t\t\t\t\tassert.True(t, resBefore.Get(\"credentials\").Exists())\n\t\t\t\t\t\t\tif tc.exist {\n\t\t\t\t\t\t\t\tassert.True(t, resBefore.Get(\"credentials\").Get(credName).Exists())\n\t\t\t\t\t\t\t\t// Remove\n\t\t\t\t\t\t\t\tremove(t, ts, \"/identities/\"+i.ID.String()+\"/credentials/\"+credName, http.StatusNoContent)\n\t\t\t\t\t\t\t\t// Query back\n\t\t\t\t\t\t\t\tresAfter := get(t, ts, \"/identities/\"+i.ID.String(), http.StatusOK)\n\t\t\t\t\t\t\t\tassert.EqualValues(t, i.ID.String(), resAfter.Get(\"id\").String(), \"%s\", resAfter.Raw)\n\t\t\t\t\t\t\t\tassert.True(t, resAfter.Get(\"credentials\").Exists())\n\t\t\t\t\t\t\t\t// Check results\n\t\t\t\t\t\t\t\texpected := resBefore.Get(\"credentials\").Map()\n\t\t\t\t\t\t\t\tdelete(expected, credName)\n\t\t\t\t\t\t\t\texpectedKeys := x.Keys(expected)\n\t\t\t\t\t\t\t\tsort.Strings(expectedKeys)\n\t\t\t\t\t\t\t\tresult := resAfter.Get(\"credentials\").Map()\n\t\t\t\t\t\t\t\tresultKeys := x.Keys(result)\n\t\t\t\t\t\t\t\tsort.Strings(resultKeys)\n\t\t\t\t\t\t\t\tassert.Equal(t, resultKeys, expectedKeys)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tassert.False(t, resBefore.Get(\"credentials\").Get(credName).Exists())\n\t\t\t\t\t\t\t\tremove(t, ts, \"/identities/\"+i.ID.String()+\"/credentials/\"+credName, http.StatusNotFound)\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})\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"case=should paginate all identities\", func(t *testing.T) {\n\t\t// Start new server\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\t\tconfigx.WithValues(testhelpers.IdentitySchemasConfig(map[string]string{\n\t\t\t\t\"default\":         \"file://./stub/identity.schema.json\",\n\t\t\t\t\"customer\":        \"file://./stub/handler/customer.schema.json\",\n\t\t\t\t\"multiple_emails\": \"file://./stub/handler/multiple_emails.schema.json\",\n\t\t\t\t\"employee\":        \"file://./stub/handler/employee.schema.json\",\n\t\t\t})),\n\t\t)\n\t\t_, ts := testhelpers.NewKratosServerWithCSRF(t, reg)\n\n\t\tvar toCreate []*identity.Identity\n\t\tcount := 500\n\t\tfor range count {\n\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\ti.Traits = identity.Traits(`{\"email\":\"` + x.NewUUID().String() + `@ory.sh\"}`)\n\t\t\ttoCreate = append(toCreate, i)\n\t\t}\n\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentities(context.Background(), toCreate...))\n\n\t\tfor _, perPage := range []int{10, 50, 100, 500} {\n\t\t\tt.Run(fmt.Sprintf(\"perPage=%d\", perPage), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tbody, _ := getFull(t, ts, fmt.Sprintf(\"/identities?per_page=%d\", perPage), http.StatusOK)\n\t\t\t\tassert.Len(t, body.Array(), perPage)\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"iterate over next page\", func(t *testing.T) {\n\t\t\tperPage := 10\n\n\t\t\trun := func(t *testing.T, path string, knownIDs map[string]struct{}) (next *url.URL, res *http.Response) {\n\t\t\t\tt.Logf(\"Requesting %s\", path)\n\t\t\t\tbody, res := getFull(t, ts, path, http.StatusOK)\n\t\t\t\tfor _, i := range body.Array() {\n\t\t\t\t\tid := i.Get(\"id\").String()\n\t\t\t\t\t_, seen := knownIDs[id]\n\t\t\t\t\trequire.Falsef(t, seen, \"ID %s was previously returned from the API\", id)\n\t\t\t\t\tknownIDs[id] = struct{}{}\n\t\t\t\t}\n\t\t\t\tlinks := link.ParseResponse(res)\n\t\t\t\tif nextLink, ok := links[\"next\"]; ok {\n\t\t\t\t\tnext, err := url.Parse(nextLink.URI)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\treturn next, res\n\t\t\t\t}\n\t\t\t\treturn nil, res\n\t\t\t}\n\n\t\t\tt.Run(\"using token pagination\", func(t *testing.T) {\n\t\t\t\tknownIDs := make(map[string]struct{})\n\t\t\t\tvar pages int\n\t\t\t\tpath := fmt.Sprintf(\"/admin/identities?page_size=%d\", perPage)\n\t\t\t\tfor {\n\t\t\t\t\tpages++\n\t\t\t\t\tnext, res := run(t, path, knownIDs)\n\t\t\t\t\tassert.NotContains(t, res.Header, \"X-Total-Count\", \"not supported in token pagination\")\n\t\t\t\t\tif next == nil {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tassert.NotContains(t, next.Query(), \"page\")\n\t\t\t\t\tassert.NotContains(t, next.Query(), \"per_page\")\n\t\t\t\t\tpath = next.Path + \"?\" + next.Query().Encode()\n\t\t\t\t}\n\n\t\t\t\tassert.Len(t, knownIDs, count)\n\t\t\t\tassert.Equal(t, count/perPage, pages)\n\t\t\t})\n\n\t\t\tt.Run(\"using page pagination\", func(t *testing.T) {\n\t\t\t\tknownIDs := make(map[string]struct{})\n\t\t\t\tvar pages int\n\t\t\t\tpath := fmt.Sprintf(\"/admin/identities?page=0&per_page=%d\", perPage)\n\t\t\t\tfor {\n\t\t\t\t\tpages++\n\t\t\t\t\tnext, res := run(t, path, knownIDs)\n\t\t\t\t\tassert.Equal(t, strconv.Itoa(count), res.Header.Get(\"X-Total-Count\"))\n\t\t\t\t\tif next == nil {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tpath = next.Path + \"?\" + next.Query().Encode()\n\t\t\t\t}\n\n\t\t\t\tassert.Len(t, knownIDs, count)\n\t\t\t\tassert.Equal(t, count/perPage, pages)\n\t\t\t})\n\t\t})\n\t})\n}\n\nfunc validCreateIdentityBody(t *testing.T, prefix string, i int, plainPassword bool) *identity.CreateIdentityBody {\n\tvar (\n\t\tverifiableAddresses []identity.VerifiableAddress\n\t\trecoveryAddresses   []identity.RecoveryAddress\n\t)\n\ttraits := struct {\n\t\tEmails   []string `json:\"emails\"`\n\t\tUsername string   `json:\"username\"`\n\t}{}\n\n\tverificationStates := []identity.VerifiableAddressStatus{\n\t\tidentity.VerifiableAddressStatusPending,\n\t\tidentity.VerifiableAddressStatusSent,\n\t\tidentity.VerifiableAddressStatusCompleted,\n\t}\n\n\tfor j := range 4 {\n\t\temail := fmt.Sprintf(\"%s-%d-%d@ory.sh\", prefix, i, j)\n\t\ttraits.Emails = append(traits.Emails, email)\n\t\tverifiableAddresses = append(verifiableAddresses, identity.VerifiableAddress{\n\t\t\tValue:    email,\n\t\t\tVia:      identity.AddressTypeEmail,\n\t\t\tVerified: j%2 == 0,\n\t\t\tStatus:   verificationStates[j%len(verificationStates)],\n\t\t})\n\t\trecoveryAddresses = append(recoveryAddresses, identity.RecoveryAddress{\n\t\t\tValue: email,\n\t\t\tVia:   identity.AddressTypeEmail,\n\t\t})\n\t}\n\ttraits.Username = traits.Emails[0]\n\trawTraits, _ := json.Marshal(traits)\n\tconf := identity.AdminIdentityImportCredentialsPasswordConfig{\n\t\tPassword: fmt.Sprintf(\"password-%d\", i),\n\t}\n\tif !plainPassword {\n\t\tg, err := bcrypt.GenerateFromPassword([]byte(fmt.Sprintf(\"password-%d\", i)), 6)\n\t\trequire.NoError(t, err)\n\t\tconf.Password = string(g)\n\t}\n\texternalID := \"\"\n\tif i%2 == 0 {\n\t\texternalID = fmt.Sprintf(\"external-id-%s-%d\", prefix, i)\n\t}\n\treturn &identity.CreateIdentityBody{\n\t\tSchemaID: \"multiple_emails\",\n\t\tTraits:   rawTraits,\n\t\tCredentials: &identity.IdentityWithCredentials{\n\t\t\tPassword: &identity.AdminIdentityImportCredentialsPassword{\n\t\t\t\tConfig: conf,\n\t\t\t},\n\t\t},\n\t\tVerifiableAddresses: verifiableAddresses,\n\t\tRecoveryAddresses:   recoveryAddresses,\n\t\tMetadataPublic:      json.RawMessage(fmt.Sprintf(`{\"public-%d\":\"public\"}`, i)),\n\t\tMetadataAdmin:       json.RawMessage(fmt.Sprintf(`{\"admin-%d\":\"admin\"}`, i)),\n\t\tState:               \"active\",\n\t\tExternalID:          externalID,\n\t}\n}\n\nfunc assertJSONArrayElementsMatch(t *testing.T, expected, actual gjson.Result, msgAndArgs ...any) {\n\tt.Helper()\n\n\tvar expectedStrings, actualStrings []string\n\texpected.ForEach(func(_, value gjson.Result) bool {\n\t\texpectedStrings = append(expectedStrings, value.String())\n\t\treturn true\n\t})\n\tactual.ForEach(func(_, value gjson.Result) bool {\n\t\tactualStrings = append(actualStrings, value.String())\n\t\treturn true\n\t})\n\n\tassert.ElementsMatch(t, expectedStrings, actualStrings, msgAndArgs...)\n}\n"
  },
  {
    "path": "identity/identity.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"context\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"slices\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/samber/lo\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/cipher\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\n// An Identity's State\n//\n// The state can either be `active` or `inactive`.\n//\n// swagger:enum State\ntype State string\n\nconst (\n\tStateActive   State = \"active\"\n\tStateInactive State = \"inactive\"\n)\n\nfunc (lt State) IsValid() error {\n\tswitch lt {\n\tcase StateActive, StateInactive:\n\t\treturn nil\n\t}\n\treturn errors.New(\"identity state is not valid\")\n}\n\n// Identity represents an Ory Kratos identity\n//\n// An [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) represents a (human) user in Ory.\n//\n// swagger:model identity\ntype Identity struct {\n\t// ID is the identity's unique identifier.\n\t//\n\t// The Identity ID can not be changed and can not be chosen. This ensures future\n\t// compatibility and optimization for distributed stores such as CockroachDB.\n\t//\n\t// required: true\n\tID uuid.UUID `json:\"id\" faker:\"-\" db:\"id\"`\n\n\t// ExternalID is an optional external ID of the identity. This is used to link\n\t// the identity to an external system. If set, the external ID must be unique\n\t// across all identities.\n\t//\n\t// required: false\n\tExternalID sqlxx.NullString `json:\"external_id,omitempty\" faker:\"-\" db:\"external_id\"`\n\n\t// Credentials represents all credentials that can be used for authenticating this identity.\n\tCredentials map[CredentialsType]Credentials `json:\"credentials,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// InternalAvailableAAL defines the maximum available AAL for this identity.\n\t//\n\t// - If the user has at least one two-factor authentication method configured, the AAL will be 2.\n\t// - If the user has only a password configured, the AAL will be 1.\n\t//\n\t// This field is AAL2 as soon as a second factor credential is found. A first factor is not required for this\n\t// field to return `aal2`.\n\t//\n\t// This field is primarily used to determine whether the user needs to upgrade to AAL2 without having to check\n\t// all the credentials in the database. Use with caution!\n\tInternalAvailableAAL NullableAuthenticatorAssuranceLevel `json:\"-\" faker:\"-\" db:\"available_aal\"`\n\n\t// // IdentifierCredentials contains the access and refresh token for oidc identifier\n\t// IdentifierCredentials []IdentifierCredential `json:\"identifier_credentials,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\n\t//\n\t// required: true\n\tSchemaID string `json:\"schema_id\" faker:\"-\" db:\"schema_id\"`\n\n\t// SchemaURL is the URL of the endpoint where the identity's traits schema can be fetched from.\n\t//\n\t// format: url\n\t// required: true\n\tSchemaURL string `json:\"schema_url\" faker:\"-\" db:\"-\"`\n\n\t// State is the identity's state.\n\t//\n\t// This value has currently no effect.\n\tState State `json:\"state\" faker:\"-\" db:\"state\"`\n\n\t// StateChangedAt contains the last time when the identity's state changed.\n\tStateChangedAt *sqlxx.NullTime `json:\"state_changed_at,omitempty\" faker:\"-\" db:\"state_changed_at\"`\n\n\t// Traits represent an identity's traits. The identity is able to create, modify, and delete traits\n\t// in a self-service manner. The input will always be validated against the JSON Schema defined\n\t// in `schema_url`.\n\t//\n\t// required: true\n\tTraits Traits `json:\"traits\" faker:\"-\" db:\"traits\"`\n\n\t// VerifiableAddresses contains all the addresses that can be verified by the user.\n\t//\n\t// Extensions:\n\t// ---\n\t// x-omitempty: true\n\t// ---\n\tVerifiableAddresses []VerifiableAddress `json:\"verifiable_addresses,omitempty\" faker:\"-\" has_many:\"identity_verifiable_addresses\" fk_id:\"identity_id\" order_by:\"id asc\"`\n\n\t// RecoveryAddresses contains all the addresses that can be used to recover an identity.\n\t//\n\t// Extensions:\n\t// ---\n\t// x-omitempty: true\n\t// ---\n\tRecoveryAddresses []RecoveryAddress `json:\"recovery_addresses,omitempty\" faker:\"-\" has_many:\"identity_recovery_addresses\" fk_id:\"identity_id\" order_by:\"id asc\"`\n\n\t// Store metadata about the identity which the identity itself can see when calling for example the\n\t// session endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\n\tMetadataPublic sqlxx.NullJSONRawMessage `json:\"metadata_public\" faker:\"-\" db:\"metadata_public\"`\n\n\t// Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/<id>`.\n\tMetadataAdmin sqlxx.NullJSONRawMessage `json:\"metadata_admin,omitempty\" faker:\"-\" db:\"metadata_admin\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"created_at\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt      time.Time     `json:\"updated_at\" db:\"updated_at\"`\n\tNID            uuid.UUID     `json:\"-\"  faker:\"-\" db:\"nid\"`\n\tOrganizationID uuid.NullUUID `json:\"organization_id,omitempty\"  faker:\"-\" db:\"organization_id\"`\n}\n\nfunc (i *Identity) PageToken() keysetpagination.PageToken {\n\treturn keysetpagination.StringPageToken(i.ID.String())\n}\n\nfunc DefaultPageToken() keysetpagination.PageToken {\n\treturn keysetpagination.StringPageToken(uuid.Nil.String())\n}\n\n// Traits represent an identity's traits. The identity is able to create, modify, and delete traits\n// in a self-service manner. The input will always be validated against the JSON Schema defined\n// in `schema_url`.\n//\n// swagger:model identityTraits\ntype Traits json.RawMessage\n\nfunc (t *Traits) Scan(value any) error {\n\treturn sqlxx.JSONScan(t, value)\n}\n\nfunc (t Traits) Value() (driver.Value, error) {\n\treturn string(t), nil\n}\n\nfunc (t *Traits) String() string {\n\treturn string(*t)\n}\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (t Traits) MarshalJSON() ([]byte, error) {\n\tif t == nil {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn t, nil\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (t *Traits) UnmarshalJSON(data []byte) error {\n\tif t == nil {\n\t\treturn errors.New(\"json.RawMessage: UnmarshalJSON on nil pointer\")\n\t}\n\t*t = append((*t)[0:0], data...)\n\treturn nil\n}\n\nfunc (i Identity) TableName(context.Context) string {\n\treturn \"identities\"\n}\n\nfunc (i *Identity) IsActive() bool {\n\treturn i.State == StateActive\n}\n\nfunc (i *Identity) SetCredentials(t CredentialsType, c Credentials) {\n\tif i.Credentials == nil {\n\t\ti.Credentials = make(map[CredentialsType]Credentials)\n\t}\n\n\tc.Type = t\n\tc.IdentityID = i.ID\n\ti.Credentials[t] = c\n}\n\nfunc (i *Identity) SetCredentialsWithConfig(t CredentialsType, c Credentials, conf any) (err error) {\n\tif i.Credentials == nil {\n\t\ti.Credentials = make(map[CredentialsType]Credentials)\n\t}\n\n\tc.Config, err = json.Marshal(conf)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode %s credentials to JSON: %s\", t, err))\n\t}\n\n\tc.Type = t\n\ti.Credentials[t] = c\n\treturn nil\n}\n\nfunc (i *Identity) DeleteCredentialsType(t CredentialsType) {\n\tif i.Credentials == nil {\n\t\treturn\n\t}\n\n\tdelete(i.Credentials, t)\n}\n\n// GetCredentialsOr returns the credentials for a given CredentialsType. If the\n// credentials do not exist, the fallback is returned.\nfunc (i *Identity) GetCredentialsOr(t CredentialsType, fallback *Credentials) *Credentials {\n\tc, ok := i.GetCredentials(t)\n\tif !ok {\n\t\treturn fallback\n\t}\n\treturn c\n}\n\ntype CredentialsOptions func(c *Credentials)\n\nfunc WithAdditionalIdentifier(identifier string) CredentialsOptions {\n\treturn func(c *Credentials) {\n\t\tc.Identifiers = append(c.Identifiers, identifier)\n\t}\n}\n\nfunc (i *Identity) UpsertCredentialsConfig(t CredentialsType, conf []byte, version int, opt ...CredentialsOptions) {\n\tc, ok := i.GetCredentials(t)\n\tif !ok {\n\t\tc = &Credentials{}\n\t}\n\n\tfor _, optionFn := range opt {\n\t\toptionFn(c)\n\t}\n\n\tc.Type = t\n\tc.IdentityID = i.ID\n\tc.Config = conf\n\tc.Version = version\n\n\ti.SetCredentials(t, *c)\n}\n\nfunc (i *Identity) GetCredentials(t CredentialsType) (*Credentials, bool) {\n\tif c, ok := i.Credentials[t]; ok {\n\t\treturn &c, true\n\t}\n\n\treturn nil, false\n}\n\nfunc (i *Identity) ParseCredentials(t CredentialsType, config any) (*Credentials, error) {\n\tif c, ok := i.Credentials[t]; ok {\n\t\tif err := json.Unmarshal(c.Config, config); err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\treturn &c, nil\n\t}\n\n\treturn nil, herodot.ErrNotFound.WithReasonf(\"identity does not have credential type %s\", t)\n}\n\nfunc (i *Identity) CopyWithoutCredentials() *Identity {\n\tii := *i\n\tii.Credentials = nil\n\treturn &ii\n}\n\n// MergeOIDCCredentials merges the new credentials into the existing credentials.\n// If the provider already exists, the provider is replace and the identifier is\n// updated. This function requires the new credentials to have exactly one\n// provider and one identifier, as returned by identity.NewCredentialsOIDC.\nfunc (i *Identity) MergeOIDCCredentials(t CredentialsType, newCreds Credentials) (err error) {\n\tcreds, ok := i.Credentials[t]\n\tif !ok {\n\t\ti.SetCredentials(t, newCreds)\n\t\treturn nil\n\t}\n\n\tvar conf CredentialsOIDC\n\tif err = json.Unmarshal(creds.Config, &conf); err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode old %s credentials from JSON: %s\", creds.Config, err))\n\t}\n\n\tvar newConf CredentialsOIDC\n\tif err = json.Unmarshal(newCreds.Config, &newConf); err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode new %s credentials from JSON: %s\", newCreds.Config, err))\n\t}\n\n\tif len(newConf.Providers) != 1 {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected exactly one provider to merge credentials.\"))\n\t}\n\tnewProvider := newConf.Providers[0]\n\n\t// The identifier should have been set already, but we set it here just in case.\n\tif len(newCreds.Identifiers) != 1 {\n\t\tnewCreds.Identifiers = []string{OIDCUniqueID(newProvider.Provider, newProvider.Subject)}\n\t}\n\n\t// Delete `use_auto_link` providers and their identifiers\n\tvar obsoleteIdentifiers []string\n\tconf.Providers = slices.DeleteFunc(conf.Providers, func(p CredentialsOIDCProvider) bool {\n\t\tif p.Provider == newProvider.Provider && p.UseAutoLink {\n\t\t\tobsoleteIdentifiers = append(obsoleteIdentifiers, OIDCUniqueID(p.Provider, p.Subject))\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t})\n\tcreds.Identifiers = slices.DeleteFunc(creds.Identifiers, func(identifier string) bool {\n\t\treturn slices.Contains(obsoleteIdentifiers, identifier)\n\t})\n\n\tcreds.Identifiers = append(creds.Identifiers, newCreds.Identifiers...)\n\tconf.Providers = append(conf.Providers, newProvider)\n\n\treturn i.SetCredentialsWithConfig(t, creds, conf)\n}\n\nfunc NewIdentity(traitsSchemaID string) *Identity {\n\tif traitsSchemaID == \"\" {\n\t\ttraitsSchemaID = config.DefaultIdentityTraitsSchemaID\n\t}\n\n\tstateChangedAt := sqlxx.NullTime(time.Now().UTC())\n\treturn &Identity{\n\t\tID:                  uuid.Nil,\n\t\tCredentials:         map[CredentialsType]Credentials{},\n\t\tTraits:              Traits(\"{}\"),\n\t\tSchemaID:            traitsSchemaID,\n\t\tVerifiableAddresses: []VerifiableAddress{},\n\t\tState:               StateActive,\n\t\tStateChangedAt:      &stateChangedAt,\n\t}\n}\n\nfunc (i Identity) GetID() uuid.UUID {\n\treturn i.ID\n}\n\nfunc (i Identity) MarshalJSON() ([]byte, error) {\n\ttype localIdentity Identity\n\ti.Credentials = nil\n\ti.MetadataAdmin = nil\n\tresult, err := json.Marshal(localIdentity(i))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\nfunc (i *Identity) UnmarshalJSON(b []byte) error {\n\ttype localIdentity Identity\n\terr := json.Unmarshal(b, (*localIdentity)(i))\n\ti.Credentials = nil\n\ti.MetadataAdmin = nil\n\treturn err\n}\n\n// SetAvailableAAL sets the InternalAvailableAAL field based on the credentials stored in the identity.\n//\n// If a second factor is set up, the AAL will be set to 2. If only a first factor is set up, the AAL will be set to 1.\n//\n// A first factor is NOT required for the AAL to be set to 2 if a second factor is set up.\nfunc (i *Identity) SetAvailableAAL(ctx context.Context, m *Manager) (err error) {\n\tif c, err := m.CountActiveMultiFactorCredentials(ctx, i); err != nil {\n\t\treturn err\n\t} else if c > 0 {\n\t\ti.InternalAvailableAAL = NewNullableAuthenticatorAssuranceLevel(AuthenticatorAssuranceLevel2)\n\t\treturn nil\n\t}\n\n\tif c, err := m.CountActiveFirstFactorCredentials(ctx, i); err != nil {\n\t\treturn err\n\t} else if c > 0 {\n\t\ti.InternalAvailableAAL = NewNullableAuthenticatorAssuranceLevel(AuthenticatorAssuranceLevel1)\n\t\treturn nil\n\t}\n\n\ti.InternalAvailableAAL = NewNullableAuthenticatorAssuranceLevel(NoAuthenticatorAssuranceLevel)\n\treturn nil\n}\n\ntype WithAdminMetadataInJSON Identity\n\nfunc (i WithAdminMetadataInJSON) MarshalJSON() ([]byte, error) {\n\ttype localIdentity Identity\n\ti.Credentials = nil\n\treturn json.Marshal(localIdentity(i))\n}\n\ntype WithCredentialsAndAdminMetadataInJSON Identity\n\nfunc (i WithCredentialsAndAdminMetadataInJSON) MarshalJSON() ([]byte, error) {\n\ttype localIdentity Identity\n\treturn json.Marshal(localIdentity(i))\n}\n\ntype WithCredentialsNoConfigAndAdminMetadataInJSON Identity\n\nfunc (i WithCredentialsNoConfigAndAdminMetadataInJSON) MarshalJSON() ([]byte, error) {\n\ttype localIdentity Identity\n\tfor k, v := range i.Credentials {\n\t\tv.Config = nil\n\t\ti.Credentials[k] = v\n\t}\n\treturn json.Marshal(localIdentity(i))\n}\n\nfunc (i *Identity) Validate() error {\n\texpected := i.NID\n\tif expected == uuid.Nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Received empty nid.\"))\n\t}\n\n\ti.RecoveryAddresses = lo.Filter(i.RecoveryAddresses, func(v RecoveryAddress, key int) bool {\n\t\treturn v.NID == expected\n\t})\n\n\ti.VerifiableAddresses = lo.Filter(i.VerifiableAddresses, func(v VerifiableAddress, key int) bool {\n\t\treturn v.NID == expected\n\t})\n\n\tfor k := range i.Credentials {\n\t\tc := i.Credentials[k]\n\t\tif c.NID != expected {\n\t\t\tdelete(i.Credentials, k)\n\t\t\tcontinue\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// CollectVerifiableAddresses returns a slice of all verifiable addresses of the given identities.\nfunc CollectVerifiableAddresses(i []*Identity) (res []VerifiableAddress) {\n\tres = make([]VerifiableAddress, 0, len(i))\n\tfor _, id := range i {\n\t\tres = append(res, id.VerifiableAddresses...)\n\t}\n\n\treturn res\n}\n\n// CollectRecoveryAddresses returns a slice of all recovery addresses of the given identities.\nfunc CollectRecoveryAddresses(i []*Identity) (res []RecoveryAddress) {\n\tres = make([]RecoveryAddress, 0, len(i))\n\tfor _, id := range i {\n\t\tres = append(res, id.RecoveryAddresses...)\n\t}\n\n\treturn res\n}\n\nfunc (i *Identity) WithDeclassifiedCredentials(ctx context.Context, c cipher.Provider, includeCredentials []CredentialsType) (*Identity, error) {\n\tcredsToPublish := make(map[CredentialsType]Credentials, len(i.Credentials))\n\n\tfor ct, original := range i.Credentials {\n\t\tif !slices.Contains(includeCredentials, ct) {\n\t\t\ttoPublish := original\n\t\t\ttoPublish.Config = []byte{}\n\t\t\tcredsToPublish[ct] = toPublish\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch ct {\n\t\tcase CredentialsTypeOIDC, CredentialsTypeSAML:\n\t\t\ttoPublish := original\n\t\t\ttoPublish.Config = []byte{}\n\n\t\t\tvar i int\n\t\t\tvar err error\n\t\t\tgjson.GetBytes(original.Config, \"providers\").ForEach(func(_, v gjson.Result) bool {\n\t\t\t\tif ct == CredentialsTypeOIDC {\n\t\t\t\t\t// Don't expose these for SAML\n\t\t\t\t\tfor _, token := range []string{\"initial_id_token\", \"initial_access_token\", \"initial_refresh_token\"} {\n\t\t\t\t\t\tkey := fmt.Sprintf(\"%d.%s\", i, token)\n\t\t\t\t\t\tciphertext := v.Get(token).String()\n\n\t\t\t\t\t\tplaintext, decryptErr := c.Cipher(ctx).Decrypt(ctx, ciphertext)\n\t\t\t\t\t\tif decryptErr != nil {\n\t\t\t\t\t\t\tplaintext = []byte{}\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttoPublish.Config, err = sjson.SetBytes(toPublish.Config, \"providers.\"+key, string(plaintext))\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttoPublish.Config, err = sjson.SetBytes(toPublish.Config, fmt.Sprintf(\"providers.%d.subject\", i), v.Get(\"subject\").String())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\n\t\t\t\ttoPublish.Config, err = sjson.SetBytes(toPublish.Config, fmt.Sprintf(\"providers.%d.provider\", i), v.Get(\"provider\").String())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\n\t\t\t\tif org := v.Get(\"organization\").String(); org != \"\" {\n\t\t\t\t\ttoPublish.Config, err = sjson.SetBytes(toPublish.Config, fmt.Sprintf(\"providers.%d.organization\", i), org)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif useAutoLink := v.Get(\"use_auto_link\").Bool(); useAutoLink {\n\t\t\t\t\ttoPublish.Config, err = sjson.SetBytes(toPublish.Config, fmt.Sprintf(\"providers.%d.use_auto_link\", i), useAutoLink)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ti++\n\t\t\t\treturn true\n\t\t\t})\n\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tcredsToPublish[ct] = toPublish\n\t\tdefault:\n\t\t\tcredsToPublish[ct] = original\n\t\t}\n\t}\n\n\tii := *i\n\tii.Credentials = credsToPublish\n\treturn &ii, nil\n}\n\nfunc (i *Identity) deleteCredentialPassword() error {\n\tcred, ok := i.GetCredentials(CredentialsTypePassword)\n\tif !ok {\n\t\treturn errors.WithStack(herodot.ErrNotFound.WithReasonf(\"You tried to remove a password credential but this user has no such credential set up.\"))\n\t}\n\tcred.Config = []byte(\"{}\")\n\ti.SetCredentials(CredentialsTypePassword, *cred)\n\treturn nil\n}\n\nfunc (i *Identity) deleteCredentialWebAuthFromIdentity() error {\n\tcred, ok := i.GetCredentials(CredentialsTypeWebAuthn)\n\tif !ok {\n\t\t// This should never happend as it's checked earlier in the code;\n\t\t// But we never know...\n\t\treturn errors.WithStack(herodot.ErrNotFound.WithReasonf(\"You tried to remove a WebAuthn credential but this user has no such credential set up.\"))\n\t}\n\n\tvar cc CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(cred.Config, &cc); err != nil {\n\t\t// Database has been tampered or the json schema are incompatible (migration issue);\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\tupdated := make([]CredentialWebAuthn, 0)\n\tfor k, cred := range cc.Credentials {\n\t\tif cred.IsPasswordless {\n\t\t\tupdated = append(updated, cc.Credentials[k])\n\t\t}\n\t}\n\n\tif len(updated) == 0 {\n\t\ti.DeleteCredentialsType(CredentialsTypeWebAuthn)\n\t\treturn nil\n\t}\n\n\tcc.Credentials = updated\n\tmessage, err := json.Marshal(cc)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\tcred.Config = message\n\ti.SetCredentials(CredentialsTypeWebAuthn, *cred)\n\treturn nil\n}\n\nfunc (i *Identity) deleteCredentialOIDCSAMLFromIdentity(ct CredentialsType, identifierToDelete string) error {\n\tswitch ct {\n\tcase CredentialsTypeOIDC, CredentialsTypeSAML:\n\t\t// ok\n\tdefault:\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unexpected credential type encountered: got %q, expected [%s, %s]\", ct, CredentialsTypeOIDC, CredentialsTypeSAML))\n\t}\n\tif identifierToDelete == \"\" {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"You must provide an identifier to delete this credential.\"))\n\t}\n\t_, hasOIDC := i.GetCredentials(ct)\n\tif !hasOIDC {\n\t\treturn errors.WithStack(herodot.ErrNotFound.WithReasonf(\"You tried to remove a %s credential but this user has no such credential set up.\", ct))\n\t}\n\tvar oidcConfig CredentialsOIDC\n\tcreds, err := i.ParseCredentials(ct, &oidcConfig)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\tupdatedIdentifiers := make([]string, 0, len(oidcConfig.Providers))\n\tupdatedProviders := make([]CredentialsOIDCProvider, 0, len(oidcConfig.Providers))\n\tvar found bool\n\tfor _, cfg := range oidcConfig.Providers {\n\t\tif identifierToDelete == OIDCUniqueID(cfg.Provider, cfg.Subject) {\n\t\t\tfound = true\n\t\t\tcontinue\n\t\t}\n\t\tupdatedIdentifiers = append(updatedIdentifiers, OIDCUniqueID(cfg.Provider, cfg.Subject))\n\t\tupdatedProviders = append(updatedProviders, cfg)\n\t}\n\tif !found {\n\t\treturn errors.WithStack(herodot.ErrNotFound.WithReasonf(\"The identifier `%s` was not found among OIDC credentials.\", identifierToDelete))\n\t}\n\tcreds.Identifiers = updatedIdentifiers\n\tcreds.Config, err = json.Marshal(&CredentialsOIDC{Providers: updatedProviders})\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode identity credentials.\").WithDebug(err.Error()))\n\t}\n\ti.Credentials[ct] = *creds\n\treturn nil\n}\n\n// Patch Identities Parameters\n//\n// swagger:parameters batchPatchIdentities\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype batchPatchIdentitites struct {\n\t// in: body\n\tBody BatchPatchIdentitiesBody\n}\n\n// Patch Identities Body\n//\n// swagger:model patchIdentitiesBody\n//\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype BatchPatchIdentitiesBody struct {\n\t// Identities holds the list of patches to apply\n\t//\n\t// required\n\tIdentities []*BatchIdentityPatch `json:\"identities\"`\n\n\t// Future fields:\n\t// RemotePatchesURL string\n\t// Async bool\n}\n\n// Payload for patching an identity\n//\n// swagger:model identityPatch\n//\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype BatchIdentityPatch struct {\n\t// The identity to create.\n\tCreate *CreateIdentityBody `json:\"create\"`\n\n\t// The ID of this patch.\n\t//\n\t// The patch ID is optional. If specified, the ID will be returned in the\n\t// response, so consumers of this API can correlate the response with the\n\t// patch.\n\tID *uuid.UUID `json:\"patch_id\"`\n}\n\n// swagger:enum BatchPatchAction\n//\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype BatchPatchAction string\n\nconst (\n\t// Create this identity.\n\tActionCreate BatchPatchAction = \"create\"\n\n\t// Error indicates that the patch failed.\n\tActionError BatchPatchAction = \"error\"\n\n\t// Future actions:\n\t//\n\t// Delete this identity.\n\t// ActionDelete BatchPatchAction = \"delete\"\n\t//\n\t// ActionUpdate BatchPatchAction = \"update\"\n)\n\n// Patch identities response\n//\n// swagger:model batchPatchIdentitiesResponse\n//\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype batchPatchIdentitiesResponse struct {\n\t// The patch responses for the individual identities.\n\tIdentities []*BatchIdentityPatchResponse `json:\"identities\"`\n}\n\n// Response for a single identity patch\n//\n// swagger:model identityPatchResponse\n//\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype BatchIdentityPatchResponse struct {\n\t// The action for this specific patch\n\tAction BatchPatchAction `json:\"action\"`\n\n\t// The identity ID payload of this patch\n\tIdentityID *uuid.UUID `json:\"identity,omitempty\"`\n\n\t// The ID of this patch response, if an ID was specified in the patch.\n\tPatchID *uuid.UUID `json:\"patch_id,omitempty\"`\n\n\t// The error message, if the action was \"error\".\n\tError *herodot.DefaultError `json:\"error,omitempty\"`\n}\n"
  },
  {
    "path": "identity/identity_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype (\n\t// RecoveryAddressStatus must not exceed 16 characters as that is the limitation in the SQL Schema.\n\tRecoveryAddressStatus string\n\n\t// swagger:model recoveryIdentityAddress\n\tRecoveryAddress struct {\n\t\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t\t// required: true\n\t\tValue string `json:\"value\" db:\"value\"`\n\n\t\t// required: true\n\t\tVia string `json:\"via\" db:\"via\"`\n\n\t\t// IdentityID is a helper struct field for gobuffalo.pop.\n\t\tIdentityID uuid.UUID `json:\"-\" faker:\"-\" db:\"identity_id\"`\n\t\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\t\tCreatedAt time.Time `json:\"created_at\" faker:\"-\" db:\"created_at\"`\n\t\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\t\tUpdatedAt time.Time `json:\"updated_at\" faker:\"-\" db:\"updated_at\"`\n\t\tNID       uuid.UUID `json:\"-\"  faker:\"-\" db:\"nid\"`\n\t}\n)\n\nfunc (a RecoveryAddress) TableName() string { return \"identity_recovery_addresses\" }\nfunc (a RecoveryAddress) GetID() uuid.UUID  { return a.ID }\n\n// Signature returns a unique string representation for the recovery address.\nfunc (a RecoveryAddress) Signature() string {\n\treturn fmt.Sprintf(\"%v|%v|%v|%v\", a.Value, a.Via, a.IdentityID, a.NID)\n}\n\nfunc NewRecoveryEmailAddress(\n\tvalue string,\n\tidentity uuid.UUID,\n) *RecoveryAddress {\n\treturn &RecoveryAddress{\n\t\tValue:      value,\n\t\tVia:        AddressTypeEmail,\n\t\tIdentityID: identity,\n\t}\n}\n\nfunc NewRecoverySMSAddress(\n\tvalue string,\n\tidentity uuid.UUID,\n) *RecoveryAddress {\n\treturn &RecoveryAddress{\n\t\tValue:      value,\n\t\tVia:        AddressTypeSMS,\n\t\tIdentityID: identity,\n\t}\n}\n"
  },
  {
    "path": "identity/identity_recovery_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestNewRecoveryEmailAddress(t *testing.T) {\n\tiid := x.NewUUID()\n\ta := NewRecoveryEmailAddress(\"foo@ory.sh\", iid)\n\n\tassert.Equal(t, a.Value, \"foo@ory.sh\")\n\tassert.Equal(t, a.Via, AddressTypeEmail)\n\tassert.Equal(t, iid, a.IdentityID)\n\tassert.Equal(t, uuid.Nil, a.ID)\n}\n\n// TestRecoveryAddress_Hash tests that the hash considers all fields that are\n// written to the database (ignoring some well-known fields like the ID or\n// timestamps).\nfunc TestRecoveryAddress_Hash(t *testing.T) {\n\tcases := []struct {\n\t\tname string\n\t\ta    RecoveryAddress\n\t}{\n\t\t{\n\t\t\tname: \"full fields\",\n\t\t\ta: RecoveryAddress{\n\t\t\t\tID:         x.NewUUID(),\n\t\t\t\tValue:      \"foo@bar.me\",\n\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\tCreatedAt:  time.Now(),\n\t\t\t\tUpdatedAt:  time.Now(),\n\t\t\t\tIdentityID: x.NewUUID(),\n\t\t\t\tNID:        x.NewUUID(),\n\t\t\t},\n\t\t}, {\n\t\t\tname: \"empty fields\",\n\t\t\ta:    RecoveryAddress{},\n\t\t}, {\n\t\t\tname: \"email constructor\",\n\t\t\ta:    *NewRecoveryEmailAddress(\"foo@ory.sh\", x.NewUUID()),\n\t\t},\n\t\t{\n\t\t\tname: \"full fields\",\n\t\t\ta: RecoveryAddress{\n\t\t\t\tID:         x.NewUUID(),\n\t\t\t\tValue:      \"6502530000\",\n\t\t\t\tVia:        AddressTypeSMS,\n\t\t\t\tCreatedAt:  time.Now(),\n\t\t\t\tUpdatedAt:  time.Now(),\n\t\t\t\tIdentityID: x.NewUUID(),\n\t\t\t\tNID:        x.NewUUID(),\n\t\t\t},\n\t\t}, {\n\t\t\tname: \"SMS constructor\",\n\t\t\ta:    *NewRecoverySMSAddress(\"6502530000\", x.NewUUID()),\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(\"case=\"+tc.name, func(t *testing.T) {\n\t\t\tassert.Equal(t,\n\t\t\t\treflectiveHash(tc.a),\n\t\t\t\ttc.a.Signature(),\n\t\t\t)\n\t\t})\n\t}\n\n}\n"
  },
  {
    "path": "identity/identity_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/cipher\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc TestNewIdentity(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\tassert.Equal(t, uuid.Nil, i.ID)\n\tassert.NotEmpty(t, i.Traits)\n\tassert.NotNil(t, i.Credentials)\n\tassert.True(t, i.IsActive())\n}\n\nfunc TestIdentityCredentialsOr(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.Credentials = nil\n\n\texpected := &Credentials{ID: x.NewUUID(), Type: CredentialsTypePassword}\n\tassert.Equal(t, expected, i.GetCredentialsOr(CredentialsTypePassword, expected))\n\n\texpected = &Credentials{ID: x.NewUUID(), Type: CredentialsTypeWebAuthn}\n\ti.SetCredentials(CredentialsTypeWebAuthn, *expected)\n\n\tassert.Equal(t, expected, i.GetCredentialsOr(CredentialsTypeWebAuthn, nil))\n}\n\nfunc TestIdentityCredentialsOrCreate(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.Credentials = nil\n\n\texpected := &Credentials{Config: []byte(\"true\"), IdentityID: i.ID, Type: CredentialsTypePassword}\n\ti.UpsertCredentialsConfig(CredentialsTypePassword, []byte(\"true\"), 0)\n\tactual, ok := i.GetCredentials(CredentialsTypePassword)\n\tassert.True(t, ok)\n\tassert.Equal(t, expected, actual)\n}\n\nfunc TestIdentityCredentials(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.Credentials = nil\n\n\t// Shouldn't error if map is nil\n\ti.DeleteCredentialsType(CredentialsTypeTOTP)\n\n\texpectedTOTP := Credentials{ID: x.NewUUID(), Type: CredentialsTypeTOTP}\n\ti.SetCredentials(CredentialsTypeTOTP, expectedTOTP)\n\tactual, found := i.GetCredentials(CredentialsTypeTOTP)\n\tassert.True(t, found, \"should set and find the credential if map was nil\")\n\tassert.Equal(t, &expectedTOTP, actual)\n\n\texpectedPassword := Credentials{ID: x.NewUUID(), Type: CredentialsTypePassword}\n\ti.SetCredentials(CredentialsTypePassword, expectedPassword)\n\tactual, found = i.GetCredentials(CredentialsTypePassword)\n\tassert.True(t, found, \"should set and find the credential if map was not nil\")\n\tassert.Equal(t, &expectedPassword, actual)\n\n\texpectedOIDC := Credentials{ID: x.NewUUID()}\n\ti.SetCredentials(CredentialsTypeOIDC, expectedOIDC)\n\tactual, found = i.GetCredentials(CredentialsTypeOIDC)\n\tassert.True(t, found)\n\tassert.Equal(t, expectedOIDC.ID, actual.ID)\n\tassert.Equal(t, CredentialsTypeOIDC, actual.Type, \"should set the type if we forgot to set it in the credentials struct\")\n\n\ti.DeleteCredentialsType(CredentialsTypePassword)\n\t_, found = i.GetCredentials(CredentialsTypePassword)\n\tassert.False(t, found, \"should delete a credential properly\")\n\n\tactual, found = i.GetCredentials(CredentialsTypeTOTP)\n\tassert.True(t, found, \"but not alter other credentials\")\n\tassert.Equal(t, &expectedTOTP, actual)\n}\n\nfunc TestMarshalExcludesCredentials(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.Credentials = map[CredentialsType]Credentials{\n\t\tCredentialsTypePassword: {\n\t\t\tID: uuid.UUID{},\n\t\t},\n\t}\n\n\trawJSON, err := json.Marshal(i)\n\trequire.NoError(t, err)\n\n\tcreds := gjson.GetBytes(rawJSON, \"credentials\")\n\tassert.Falsef(t, creds.Exists(), \"Credentials should not be rendered to JSON, but got: %q\", creds.Raw)\n\n\t// To ensure the original identity is not changed / Unmarshal has no side effects:\n\trequire.NotEmpty(t, i.Credentials)\n}\n\nfunc TestMarshalExcludesCredentialsByReference(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.Credentials = map[CredentialsType]Credentials{\n\t\tCredentialsTypePassword: {\n\t\t\tID: uuid.UUID{},\n\t\t},\n\t}\n\n\tvar b bytes.Buffer\n\trequire.Nil(t, json.NewEncoder(&b).Encode(&i))\n\n\tassert.False(t, gjson.Get(b.String(), \"credentials\").Exists(), \"Credentials should not be rendered to json\")\n\n\t// To ensure the original identity is not changed / Unmarshal has no side effects:\n\trequire.NotEmpty(t, i.Credentials)\n}\n\nfunc TestMarshalIgnoresAdminMetadata(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.MetadataAdmin = []byte(`{\"admin\":\"bar\"}`)\n\ti.MetadataPublic = []byte(`{\"public\":\"bar\"}`)\n\n\tvar b bytes.Buffer\n\trequire.Nil(t, json.NewEncoder(&b).Encode(&i))\n\n\tassert.False(t, gjson.Get(b.String(), \"metadata_admin.admin\").Exists(), \"Admin metadata should not be rendered to json but got: %s\", b.String())\n\tassert.Equal(t, \"bar\", gjson.Get(b.String(), \"metadata_public.public\").String(), \"Public metadata should be rendered to json\")\n\n\t// To ensure the original identity is not changed / Unmarshal has no side effects:\n\trequire.NotEmpty(t, i.MetadataAdmin)\n\trequire.NotEmpty(t, i.MetadataPublic)\n}\n\nfunc TestUnMarshallIgnoresCredentials(t *testing.T) {\n\tt.Parallel()\n\n\tjsonText := `{\"id\":\"3234ad11-49c6-49e2-bfac-537f3e06cd85\",\"schema_id\":\"default\",\"schema_url\":\"\",\"traits\":{}, \"credentials\" : {\"password\":{\"type\":\"\",\"identifiers\":null,\"config\":null,\"updatedAt\":\"0001-01-01T00:00:00Z\"}}}`\n\tvar i Identity\n\terr := json.Unmarshal([]byte(jsonText), &i)\n\tassert.Nil(t, err)\n\n\tassert.Nil(t, i.Credentials)\n\tassert.Equal(t, \"3234ad11-49c6-49e2-bfac-537f3e06cd85\", i.ID.String())\n}\n\nfunc TestUnMarshallIgnoresAdminMetadata(t *testing.T) {\n\tt.Parallel()\n\n\tjsonText := `{\"id\":\"3234ad11-49c6-49e2-bfac-537f3e06cd85\",\"schema_id\":\"default\",\"schema_url\":\"\",\"traits\":{}, \"admin_metadata\" : {\"foo\":\"bar\"}}`\n\tvar i Identity\n\terr := json.Unmarshal([]byte(jsonText), &i)\n\tassert.Nil(t, err)\n\n\tassert.Nil(t, i.MetadataAdmin)\n}\n\nfunc TestMarshalIdentityWithCredentialsWhenCredentialsNil(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.Credentials = nil\n\n\tvar b bytes.Buffer\n\trequire.Nil(t, json.NewEncoder(&b).Encode(WithCredentialsNoConfigAndAdminMetadataInJSON(*i)))\n\n\tassert.False(t, gjson.Get(b.String(), \"credentials\").Exists())\n}\n\nfunc TestMarshalIdentityWithAdminMetadata(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.MetadataAdmin = []byte(`{\"some\":\"metadata\"}`)\n\n\tvar b bytes.Buffer\n\trequire.Nil(t, json.NewEncoder(&b).Encode(WithAdminMetadataInJSON(*i)))\n\tassert.Equal(t, \"metadata\", gjson.GetBytes(i.MetadataAdmin, \"some\").String(), \"Original metadata_admin should not be touched by marshalling\")\n}\n\nfunc TestMarshalIdentityWithCredentialsNoConfig(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\tcredentials := map[CredentialsType]Credentials{\n\t\tCredentialsTypePassword: {\n\t\t\tType:   CredentialsTypePassword,\n\t\t\tConfig: sqlxx.JSONRawMessage(`{\"some\":\"secret\"}`),\n\t\t},\n\t}\n\ti.Credentials = credentials\n\ti.MetadataAdmin = []byte(`{\"some\":\"metadata\"}`)\n\n\trawJSON, err := json.Marshal((*WithCredentialsNoConfigAndAdminMetadataInJSON)(i))\n\trequire.NoError(t, err)\n\n\tcredentialsInJSON := gjson.GetBytes(rawJSON, \"credentials\")\n\tassert.Truef(t, credentialsInJSON.Exists(), \"Credentials should be rendered to JSON, but got: %q\", credentialsInJSON.Raw)\n\n\tassert.JSONEq(t, `{\"password\":{\"type\":\"password\",\"identifiers\":null,\"updated_at\":\"0001-01-01T00:00:00Z\",\"created_at\":\"0001-01-01T00:00:00Z\",\"version\":0}}`, credentialsInJSON.Raw)\n\tassert.Equal(t, credentials, i.Credentials, \"Original credentials should not be touched by marshalling\")\n\tassert.Equal(t, \"metadata\", gjson.GetBytes(i.MetadataAdmin, \"some\").String(), \"Original metadata_admin should not be touched by marshalling\")\n}\n\nfunc TestMarshalIdentityWithAll(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\tcredentials := map[CredentialsType]Credentials{\n\t\tCredentialsTypePassword: {\n\t\t\tType:   CredentialsTypePassword,\n\t\t\tConfig: sqlxx.JSONRawMessage(\"{\\\"some\\\" : \\\"secret\\\"}\"),\n\t\t},\n\t}\n\ti.Credentials = credentials\n\ti.MetadataAdmin = []byte(`{\"some\":\"metadata\"}`)\n\n\tvar b bytes.Buffer\n\trequire.Nil(t, json.NewEncoder(&b).Encode(WithCredentialsAndAdminMetadataInJSON(*i)))\n\n\tcredentialsInJSON := gjson.Get(b.String(), \"credentials\")\n\tassert.True(t, credentialsInJSON.Exists())\n\n\tsnapshotx.SnapshotT(t, json.RawMessage(credentialsInJSON.Raw))\n\tassert.Equal(t, credentials, i.Credentials, \"Original credentials should not be touched by marshalling\")\n\tassert.Equal(t, \"metadata\", gjson.GetBytes(i.MetadataAdmin, \"some\").String(), \"Original credentials should not be touched by marshalling\")\n}\n\nfunc TestValidateNID(t *testing.T) {\n\tt.Parallel()\n\n\tnid := x.NewUUID()\n\tfor k, tc := range []struct {\n\t\ti           *Identity\n\t\texpect      *Identity\n\t\texpectedErr bool\n\t}{\n\t\t{i: &Identity{}, expectedErr: true},\n\t\t{i: &Identity{NID: nid}},\n\t\t{\n\t\t\ti:      &Identity{NID: nid, RecoveryAddresses: []RecoveryAddress{{NID: x.NewUUID()}}},\n\t\t\texpect: &Identity{NID: nid, VerifiableAddresses: []VerifiableAddress{}, RecoveryAddresses: []RecoveryAddress{}},\n\t\t},\n\t\t{\n\t\t\ti:      &Identity{NID: nid, VerifiableAddresses: []VerifiableAddress{{NID: x.NewUUID()}}},\n\t\t\texpect: &Identity{NID: nid, VerifiableAddresses: []VerifiableAddress{}, RecoveryAddresses: []RecoveryAddress{}},\n\t\t},\n\t\t{\n\t\t\ti:      &Identity{NID: nid, Credentials: map[CredentialsType]Credentials{CredentialsTypePassword: {NID: x.NewUUID()}}},\n\t\t\texpect: &Identity{NID: nid, Credentials: map[CredentialsType]Credentials{}, VerifiableAddresses: []VerifiableAddress{}, RecoveryAddresses: []RecoveryAddress{}},\n\t\t},\n\t\t{\n\t\t\ti:      &Identity{NID: nid, VerifiableAddresses: []VerifiableAddress{{NID: x.NewUUID()}}, RecoveryAddresses: []RecoveryAddress{{NID: nid}}},\n\t\t\texpect: &Identity{NID: nid, VerifiableAddresses: []VerifiableAddress{}, RecoveryAddresses: []RecoveryAddress{{NID: nid}}},\n\t\t},\n\t\t{\n\t\t\ti:      &Identity{NID: nid, VerifiableAddresses: []VerifiableAddress{{NID: nid}}, RecoveryAddresses: []RecoveryAddress{{NID: x.NewUUID()}}},\n\t\t\texpect: &Identity{NID: nid, VerifiableAddresses: []VerifiableAddress{{NID: nid}}, RecoveryAddresses: []RecoveryAddress{}},\n\t\t},\n\t\t{\n\t\t\ti:      &Identity{NID: nid, VerifiableAddresses: []VerifiableAddress{{NID: nid}}, RecoveryAddresses: []RecoveryAddress{{NID: nid}}},\n\t\t\texpect: &Identity{NID: nid, VerifiableAddresses: []VerifiableAddress{{NID: nid}}, RecoveryAddresses: []RecoveryAddress{{NID: nid}}},\n\t\t},\n\t\t{\n\t\t\ti:      &Identity{NID: nid, Credentials: map[CredentialsType]Credentials{CredentialsTypePassword: {NID: x.NewUUID()}}, RecoveryAddresses: []RecoveryAddress{{NID: nid}}, VerifiableAddresses: []VerifiableAddress{{NID: nid}}},\n\t\t\texpect: &Identity{NID: nid, Credentials: map[CredentialsType]Credentials{}, RecoveryAddresses: []RecoveryAddress{{NID: nid}}, VerifiableAddresses: []VerifiableAddress{{NID: nid}}},\n\t\t},\n\t\t{\n\t\t\ti:      &Identity{NID: nid, Credentials: map[CredentialsType]Credentials{CredentialsTypePassword: {NID: nid}}, RecoveryAddresses: []RecoveryAddress{{NID: nid}}, VerifiableAddresses: []VerifiableAddress{{NID: nid}}},\n\t\t\texpect: &Identity{NID: nid, Credentials: map[CredentialsType]Credentials{CredentialsTypePassword: {NID: nid}}, RecoveryAddresses: []RecoveryAddress{{NID: nid}}, VerifiableAddresses: []VerifiableAddress{{NID: nid}}},\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\terr := tc.i.Validate()\n\t\t\tif tc.expectedErr {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tif tc.expect != nil {\n\t\t\t\t\tassert.EqualValues(t, tc.expect, tc.i)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestRecoveryAddresses tests the CollectRecoveryAddresses are collected from all identities.\nfunc TestRecoveryAddresses(t *testing.T) {\n\tt.Parallel()\n\n\tvar addresses []RecoveryAddress\n\n\tfor i := range 10 {\n\t\taddresses = append(addresses, RecoveryAddress{\n\t\t\tValue: fmt.Sprintf(\"address-%d\", i),\n\t\t})\n\t}\n\n\tid1 := &Identity{RecoveryAddresses: addresses[:5]}\n\tid2 := &Identity{}\n\tid3 := &Identity{RecoveryAddresses: addresses[5:]}\n\n\tassert.Equal(t, addresses, CollectRecoveryAddresses([]*Identity{id1, id2, id3}))\n}\n\n// TestVerifiableAddresses tests the VerfifableAddresses are collected from all identities.\nfunc TestVerifiableAddresses(t *testing.T) {\n\tt.Parallel()\n\n\tvar addresses []VerifiableAddress\n\n\tfor i := 0; i < 10; i++ {\n\t\taddresses = append(addresses, VerifiableAddress{\n\t\t\tValue: fmt.Sprintf(\"address-%d\", i),\n\t\t})\n\t}\n\n\tid1 := &Identity{VerifiableAddresses: addresses[:5]}\n\tid2 := &Identity{}\n\tid3 := &Identity{VerifiableAddresses: addresses[5:]}\n\n\tassert.Equal(t, addresses, CollectVerifiableAddresses([]*Identity{id1, id2, id3}))\n}\n\ntype cipherProvider struct{}\n\nfunc (c *cipherProvider) Cipher(context.Context) cipher.Cipher {\n\treturn cipher.NewNoop()\n}\n\nfunc TestWithDeclassifiedCredentials(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\tcredentials := map[CredentialsType]Credentials{\n\t\tCredentialsTypePassword: {\n\t\t\tIdentifiers: []string{\"zab\", \"bar\"},\n\t\t\tType:        CredentialsTypePassword,\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"some\": \"secret\"}`),\n\t\t},\n\t\tCredentialsTypeOIDC: {\n\t\t\tType:        CredentialsTypeOIDC,\n\t\t\tIdentifiers: []string{\"bar\", \"baz\"},\n\t\t\t// hint:\n\t\t\t//\techo '666f6f' | xxd -r -p\n\t\t\tConfig: sqlxx.JSONRawMessage(`{\"providers\": [{\"subject\":\"bar\",\"provider\":\"oidc1\",\"initial_id_token\":\"666f6f\"}]}`),\n\t\t},\n\t\tCredentialsTypeSAML: {\n\t\t\tType:        CredentialsTypeSAML,\n\t\t\tIdentifiers: []string{\"qux\", \"quz\"},\n\t\t\t// hint:\n\t\t\t//\techo 'this should not appear in output' | xxd -ps -c 0\n\t\t\tConfig: sqlxx.JSONRawMessage(`{\"providers\": [{\"subject\":\"qux\",\"provider\":\"saml1\",\"initial_id_token\":\"746869732073686f756c64206e6f742061707065617220696e206f75747075740a\"}]}`),\n\t\t},\n\t\tCredentialsTypeWebAuthn: {\n\t\t\tType:        CredentialsTypeWebAuthn,\n\t\t\tIdentifiers: []string{\"foo\", \"bar\"},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"some\": \"secret\"}`),\n\t\t},\n\t}\n\ti.Credentials = credentials\n\n\tt.Run(\"case=no-include\", func(t *testing.T) {\n\t\tactualIdentity, err := i.WithDeclassifiedCredentials(t.Context(), &cipherProvider{}, nil)\n\t\trequire.NoError(t, err)\n\n\t\tfor ct, actual := range actualIdentity.Credentials {\n\t\t\tt.Run(\"credential=\"+string(ct), func(t *testing.T) {\n\t\t\t\tsnapshotx.SnapshotT(t, actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=include-webauthn\", func(t *testing.T) {\n\t\tactualIdentity, err := i.WithDeclassifiedCredentials(t.Context(), &cipherProvider{}, []CredentialsType{CredentialsTypeWebAuthn})\n\t\trequire.NoError(t, err)\n\n\t\tfor ct, actual := range actualIdentity.Credentials {\n\t\t\tt.Run(\"credential=\"+string(ct), func(t *testing.T) {\n\t\t\t\tsnapshotx.SnapshotT(t, actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=include-multi\", func(t *testing.T) {\n\t\tactualIdentity, err := i.WithDeclassifiedCredentials(t.Context(), &cipherProvider{}, []CredentialsType{CredentialsTypeWebAuthn, CredentialsTypePassword})\n\t\trequire.NoError(t, err)\n\n\t\tfor ct, actual := range actualIdentity.Credentials {\n\t\t\tt.Run(\"credential=\"+string(ct), func(t *testing.T) {\n\t\t\t\tsnapshotx.SnapshotT(t, actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=oidc\", func(t *testing.T) {\n\t\tactualIdentity, err := i.WithDeclassifiedCredentials(t.Context(), &cipherProvider{}, []CredentialsType{CredentialsTypeOIDC})\n\t\trequire.NoError(t, err)\n\n\t\tfor ct, actual := range actualIdentity.Credentials {\n\t\t\tt.Run(\"credential=\"+string(ct), func(t *testing.T) {\n\t\t\t\tsnapshotx.SnapshotT(t, actual)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"case=saml\", func(t *testing.T) {\n\t\tactualIdentity, err := i.WithDeclassifiedCredentials(t.Context(), &cipherProvider{}, []CredentialsType{CredentialsTypeSAML})\n\t\trequire.NoError(t, err)\n\n\t\tfor ct, actual := range actualIdentity.Credentials {\n\t\t\tt.Run(\"credential=\"+string(ct), func(t *testing.T) {\n\t\t\t\tsnapshotx.SnapshotT(t, actual)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestDeleteCredentialOIDCSAMLFromIdentity(t *testing.T) {\n\tt.Parallel()\n\n\ti := NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\n\terr := i.deleteCredentialOIDCSAMLFromIdentity(CredentialsTypeOIDC, \"\")\n\tassert.Error(t, err)\n\terr = i.deleteCredentialOIDCSAMLFromIdentity(CredentialsTypeOIDC, \"does-not-exist\")\n\tassert.Error(t, err)\n\terr = i.deleteCredentialOIDCSAMLFromIdentity(CredentialsTypeSAML, \"\")\n\tassert.Error(t, err)\n\terr = i.deleteCredentialOIDCSAMLFromIdentity(CredentialsTypeSAML, \"does-not-exist\")\n\tassert.Error(t, err)\n\n\tcredentials := map[CredentialsType]Credentials{\n\t\tCredentialsTypePassword: {\n\t\t\tIdentifiers: []string{\"zab\", \"bar\"},\n\t\t\tType:        CredentialsTypePassword,\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"some\" : \"secret\"}`),\n\t\t},\n\t\tCredentialsTypeOIDC: {\n\t\t\tType:        CredentialsTypeOIDC,\n\t\t\tIdentifiers: []string{\"bar:1234\", \"baz:5678\"},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\": [{\"provider\": \"bar\", \"subject\": \"1234\"}, {\"provider\": \"baz\", \"subject\": \"5678\"}]}`),\n\t\t},\n\t\tCredentialsTypeSAML: {\n\t\t\tType:        CredentialsTypeSAML,\n\t\t\tIdentifiers: []string{\"bar:1234\", \"baz:5678\"},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\": [{\"provider\": \"bar\", \"subject\": \"1234\"}, {\"provider\": \"baz\", \"subject\": \"5678\"}]}`),\n\t\t},\n\t\tCredentialsTypeWebAuthn: {\n\t\t\tType:        CredentialsTypeWebAuthn,\n\t\t\tIdentifiers: []string{\"foo\", \"bar\"},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"some\" : \"secret\"}`),\n\t\t},\n\t}\n\ti.Credentials = credentials\n\n\terr = i.deleteCredentialOIDCSAMLFromIdentity(CredentialsTypeOIDC, \"zab\")\n\tassert.Error(t, err)\n\terr = i.deleteCredentialOIDCSAMLFromIdentity(CredentialsTypeOIDC, \"foo\")\n\tassert.Error(t, err)\n\terr = i.deleteCredentialOIDCSAMLFromIdentity(CredentialsTypeOIDC, \"bar\")\n\tassert.Error(t, err, \"matches multiple OIDC credentials\")\n\n\trequire.NoError(t, i.deleteCredentialOIDCSAMLFromIdentity(CredentialsTypeOIDC, \"bar:1234\"))\n\n\tassert.Len(t, i.Credentials, 4)\n\n\tassert.Contains(t, i.Credentials, CredentialsTypePassword)\n\tassert.EqualValues(t, i.Credentials[CredentialsTypePassword].Identifiers, []string{\"zab\", \"bar\"})\n\n\tassert.Contains(t, i.Credentials, CredentialsTypeWebAuthn)\n\tassert.EqualValues(t, i.Credentials[CredentialsTypeWebAuthn].Identifiers, []string{\"foo\", \"bar\"})\n\n\tassert.Contains(t, i.Credentials, CredentialsTypeSAML)\n\tassert.EqualValues(t, i.Credentials[CredentialsTypeSAML].Identifiers, []string{\"bar:1234\", \"baz:5678\"})\n\n\tassert.Contains(t, i.Credentials, CredentialsTypeOIDC)\n\n\toidc, ok := i.GetCredentials(CredentialsTypeOIDC)\n\trequire.True(t, ok)\n\tassert.EqualValues(t, oidc.Identifiers, []string{\"baz:5678\"})\n\tvar cfg CredentialsOIDC\n\t_, err = i.ParseCredentials(CredentialsTypeOIDC, &cfg)\n\trequire.NoError(t, err)\n\tassert.EqualValues(t, CredentialsOIDC{Providers: []CredentialsOIDCProvider{{Provider: \"baz\", Subject: \"5678\"}}}, cfg)\n\n\trequire.NoError(t, i.deleteCredentialOIDCSAMLFromIdentity(CredentialsTypeSAML, \"baz:5678\"))\n\n\tassert.Len(t, i.Credentials, 4)\n\tassert.Contains(t, i.Credentials, CredentialsTypeOIDC)\n\tassert.EqualValues(t, i.Credentials[CredentialsTypeOIDC].Identifiers, []string{\"baz:5678\"})\n\n\tassert.Contains(t, i.Credentials, CredentialsTypeSAML)\n\tsaml, ok := i.GetCredentials(CredentialsTypeSAML)\n\trequire.True(t, ok)\n\tassert.EqualValues(t, saml.Identifiers, []string{\"bar:1234\"})\n\tvar samlCfg CredentialsOIDC\n\t_, err = i.ParseCredentials(CredentialsTypeSAML, &samlCfg)\n\trequire.NoError(t, err)\n\tassert.EqualValues(t, CredentialsOIDC{Providers: []CredentialsOIDCProvider{{Provider: \"bar\", Subject: \"1234\"}}}, samlCfg)\n}\n\nfunc TestMergeOIDCCredentials(t *testing.T) {\n\tt.Parallel()\n\n\tfor _, tc := range []struct {\n\t\tname           string\n\t\tidentity       *Identity\n\t\tnewCredentials Credentials\n\n\t\texpectedIdentity *Identity\n\t\tassertErr        assert.ErrorAssertionFunc\n\t}{\n\t\t{\n\t\t\tname:     \"adds OIDC credential if not exists\",\n\t\t\tidentity: &Identity{},\n\t\t\tnewCredentials: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"oidc:1234\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\":[{\"provider\":\"oidc\",\"subject\":\"1234\"}]}`),\n\t\t\t},\n\n\t\t\texpectedIdentity: &Identity{\n\t\t\t\tCredentials: map[CredentialsType]Credentials{\n\t\t\t\t\tCredentialsTypeOIDC: {\n\t\t\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\t\t\tIdentifiers: []string{\"oidc:1234\"},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\":[{\"provider\":\"oidc\",\"subject\":\"1234\"}]}`),\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: \"merges OIDC credential if exists\",\n\t\t\tidentity: &Identity{\n\t\t\t\tCredentials: map[CredentialsType]Credentials{\n\t\t\t\t\tCredentialsTypePassword: {\n\t\t\t\t\t\tType:        CredentialsTypePassword,\n\t\t\t\t\t\tIdentifiers: []string{\"user@example.com\"},\n\t\t\t\t\t},\n\t\t\t\t\tCredentialsTypeOIDC: {\n\t\t\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\t\t\tIdentifiers: []string{\"foo\", \"replace:1234\", \"bar\", \"baz\", \"replace:abc\", \"replace:dont-replace\"},\n\t\t\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\"providers\": [\n\t{\"provider\": \"replace\", \"subject\": \"1234\", \"use_auto_link\": true},\n\t{\"provider\": \"dont-touch\", \"subject\": \"foo\"},\n\t{\"provider\": \"replace\", \"subject\": \"abc\", \"use_auto_link\": true},\n\t{\"provider\": \"also-dont-touch\", \"subject\": \"bar\", \"use_auto_link\": true},\n\t{\"provider\": \"replace\", \"subject\": \"dont-replace\", \"use_auto_link\": false}\n]}`),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tnewCredentials: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\": [{\"provider\": \"replace\", \"subject\": \"new-subject\"}]}`),\n\t\t\t},\n\n\t\t\texpectedIdentity: &Identity{\n\t\t\t\tCredentials: map[CredentialsType]Credentials{\n\t\t\t\t\tCredentialsTypeOIDC: {\n\t\t\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\t\t\tIdentifiers: []string{\"foo\", \"bar\", \"baz\", \"replace:dont-replace\", \"replace:new-subject\"},\n\t\t\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\n  \"providers\" : [ {\n    \"subject\" : \"foo\",\n    \"provider\" : \"dont-touch\",\n    \"initial_id_token\" : \"\",\n    \"initial_access_token\" : \"\",\n    \"initial_refresh_token\" : \"\"\n  }, {\n    \"subject\" : \"bar\",\n    \"provider\" : \"also-dont-touch\",\n    \"initial_id_token\" : \"\",\n    \"initial_access_token\" : \"\",\n    \"initial_refresh_token\" : \"\",\n    \"use_auto_link\": true\n  }, {\n    \"subject\" : \"dont-replace\",\n    \"provider\" : \"replace\",\n    \"initial_id_token\" : \"\",\n    \"initial_access_token\" : \"\",\n    \"initial_refresh_token\" : \"\"\n  }, {\n    \"subject\" : \"new-subject\",\n    \"provider\" : \"replace\",\n    \"initial_id_token\" : \"\",\n    \"initial_access_token\" : \"\",\n    \"initial_refresh_token\" : \"\"\n  } ]\n}`),\n\t\t\t\t\t},\n\t\t\t\t\tCredentialsTypePassword: {\n\t\t\t\t\t\tType:        CredentialsTypePassword,\n\t\t\t\t\t\tIdentifiers: []string{\"user@example.com\"},\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: \"errs if new credential has no provider\",\n\t\t\tidentity: &Identity{\n\t\t\t\tCredentials: map[CredentialsType]Credentials{\n\t\t\t\t\tCredentialsTypeOIDC: {\n\t\t\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\t\t\tIdentifiers: []string{\"oidc:1234\"},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\": [{\"provider\": \"oidc\", \"subject\": \"1234\"}]}`),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tnewCredentials: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"oidc:1234\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\": []}`),\n\t\t\t},\n\n\t\t\tassertErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"errs if identity credentials are invalid\",\n\t\t\tidentity: &Identity{\n\t\t\t\tCredentials: map[CredentialsType]Credentials{\n\t\t\t\t\tCredentialsTypeOIDC: {\n\t\t\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\t\t\tIdentifiers: []string{\"oidc:1234\"},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(\"invalid\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tnewCredentials: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"oidc:1234\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\": [{\"provider\": \"replace\", \"subject\": \"new-subject\"}]}`),\n\t\t\t},\n\n\t\t\tassertErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"errs if new credential config is invalid\",\n\t\t\tidentity: &Identity{\n\t\t\t\tCredentials: map[CredentialsType]Credentials{\n\t\t\t\t\tCredentialsTypeOIDC: {\n\t\t\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\t\t\tIdentifiers: []string{\"oidc:1234\"},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\": [{\"provider\": \"oidc\", \"subject\": \"1234\"}]}`),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tnewCredentials: Credentials{\n\t\t\t\tType:        CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{\"oidc:1234\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`invalid`),\n\t\t\t},\n\n\t\t\tassertErr: assert.Error,\n\t\t},\n\t} {\n\t\tt.Run(\"case=\"+tc.name, func(t *testing.T) {\n\t\t\terr := tc.identity.MergeOIDCCredentials(CredentialsTypeOIDC, tc.newCredentials)\n\n\t\t\tif tc.assertErr != nil {\n\t\t\t\ttc.assertErr(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}\n\n\t\t\tif tc.expectedIdentity != nil {\n\t\t\t\tvar buf bytes.Buffer\n\t\t\t\trequire.NoError(t, json.Compact(&buf, tc.expectedIdentity.Credentials[CredentialsTypeOIDC].Config))\n\t\t\t\ttc.expectedIdentity.UpsertCredentialsConfig(CredentialsTypeOIDC, buf.Bytes(), 0)\n\t\t\t\tassert.EqualExportedValues(t, tc.expectedIdentity, tc.identity)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/identity_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/sqlxx\"\n)\n\nconst (\n\tVerifiableAddressStatusPending   VerifiableAddressStatus = \"pending\"\n\tVerifiableAddressStatusSent      VerifiableAddressStatus = \"sent\"\n\tVerifiableAddressStatusCompleted VerifiableAddressStatus = \"completed\"\n)\n\n// VerifiableAddressStatus must not exceed 16 characters as that is the limitation in the SQL Schema\n//\n// swagger:model identityVerifiableAddressStatus\ntype VerifiableAddressStatus string\n\n// VerifiableAddress is an identity's verifiable address\n//\n// swagger:model verifiableIdentityAddress\ntype VerifiableAddress struct {\n\t// The ID\n\t//\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// The address value\n\t//\n\t// example foo@user.com\n\t// required: true\n\tValue string `json:\"value\" db:\"value\"`\n\n\t// Indicates if the address has already been verified\n\t//\n\t// example: true\n\t// required: true\n\tVerified bool `json:\"verified\" db:\"verified\"`\n\n\t// The delivery method\n\t//\n\t// enum: email,sms\n\t// example: email\n\t// required: true\n\tVia string `json:\"via\" db:\"via\"`\n\n\t// The verified address status\n\t//\n\t// enum: pending,sent,completed\n\t// example: sent\n\t// required: true\n\tStatus VerifiableAddressStatus `json:\"status\" db:\"status\"`\n\n\t// When the address was verified\n\t//\n\t// example: 2014-01-01T23:28:56.782Z\n\t// required: false\n\tVerifiedAt *sqlxx.NullTime `json:\"verified_at,omitempty\" faker:\"-\" db:\"verified_at\"`\n\n\t// When this entry was created\n\t//\n\t// example: 2014-01-01T23:28:56.782Z\n\tCreatedAt time.Time `json:\"created_at\" faker:\"-\" db:\"created_at\"`\n\n\t// When this entry was last updated\n\t//\n\t// example: 2014-01-01T23:28:56.782Z\n\tUpdatedAt time.Time `json:\"updated_at\" faker:\"-\" db:\"updated_at\"`\n\n\t// IdentityID is a helper struct field for gobuffalo.pop.\n\tIdentityID uuid.UUID `json:\"-\" faker:\"-\" db:\"identity_id\"`\n\tNID        uuid.UUID `json:\"-\"  faker:\"-\" db:\"nid\"`\n}\n\nfunc (a VerifiableAddress) TableName() string {\n\treturn \"identity_verifiable_addresses\"\n}\n\nfunc NewVerifiableEmailAddress(value string, identity uuid.UUID) *VerifiableAddress {\n\treturn NewVerifiableAddress(value, identity, AddressTypeEmail)\n}\n\nfunc NewVerifiableAddress(value string, identity uuid.UUID, channel string) *VerifiableAddress {\n\treturn &VerifiableAddress{\n\t\tValue:      value,\n\t\tVerified:   false,\n\t\tStatus:     VerifiableAddressStatusPending,\n\t\tVia:        channel,\n\t\tIdentityID: identity,\n\t}\n}\n\nfunc (a VerifiableAddress) GetID() uuid.UUID {\n\treturn a.ID\n}\n\n// Signature returns a unique string representation for the recovery address.\nfunc (a VerifiableAddress) Signature() string {\n\treturn fmt.Sprintf(\"%v|%v|%v|%v|%v|%v|%v\", a.Value, a.Verified, a.Via, a.Status, a.VerifiedAt, a.IdentityID, a.NID)\n}\n"
  },
  {
    "path": "identity/identity_verification_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestNewVerifiableEmailAddress(t *testing.T) {\n\tiid := x.NewUUID()\n\ta := NewVerifiableEmailAddress(\"foo@ory.sh\", iid)\n\tvar nullTime *sqlxx.NullTime\n\n\tassert.Equal(t, a.Value, \"foo@ory.sh\")\n\tassert.Equal(t, a.Via, AddressTypeEmail)\n\tassert.Equal(t, a.Status, VerifiableAddressStatusPending)\n\tassert.Equal(t, a.Verified, false)\n\tassert.EqualValues(t, nullTime, a.VerifiedAt)\n\tassert.Equal(t, iid, a.IdentityID)\n\tassert.Equal(t, uuid.Nil, a.ID)\n}\n\nvar tagsIgnoredForHashing = map[string]struct{}{\n\t\"id\":         {},\n\t\"created_at\": {},\n\t\"updated_at\": {},\n\t// \"verified_at\": {}, // we explicitly want to be able to update just this field and nothing else\n}\n\nfunc reflectiveHash(record any) string {\n\tvar (\n\t\tval    = reflect.ValueOf(record)\n\t\ttyp    = reflect.TypeOf(record)\n\t\tvalues = []string{}\n\t)\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tdbTag, ok := typ.Field(i).Tag.Lookup(\"db\")\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ignore := tagsIgnoredForHashing[dbTag]; ignore {\n\t\t\tcontinue\n\t\t}\n\t\tif !val.Field(i).CanInterface() {\n\t\t\tcontinue\n\t\t}\n\t\tvalues = append(values, fmt.Sprintf(\"%v\", val.Field(i).Interface()))\n\t}\n\treturn strings.Join(values, \"|\")\n}\n\n// TestVerifiableAddress_Hash tests that the hash considers all fields that are\n// written to the database (ignoring some well-known fields like the ID or\n// timestamps).\nfunc TestVerifiableAddress_Hash(t *testing.T) {\n\tnow := sqlxx.NullTime(time.Now())\n\tcases := []struct {\n\t\tname string\n\t\ta    VerifiableAddress\n\t}{\n\t\t{\n\t\t\tname: \"full fields\",\n\t\t\ta: VerifiableAddress{\n\t\t\t\tID:         x.NewUUID(),\n\t\t\t\tValue:      \"foo@bar.me\",\n\t\t\t\tVerified:   false,\n\t\t\t\tVia:        AddressTypeEmail,\n\t\t\t\tStatus:     VerifiableAddressStatusPending,\n\t\t\t\tVerifiedAt: &now,\n\t\t\t\tCreatedAt:  time.Now(),\n\t\t\t\tUpdatedAt:  time.Now(),\n\t\t\t\tIdentityID: x.NewUUID(),\n\t\t\t\tNID:        x.NewUUID(),\n\t\t\t},\n\t\t}, {\n\t\t\tname: \"empty fields\",\n\t\t\ta:    VerifiableAddress{},\n\t\t}, {\n\t\t\tname: \"constructor\",\n\t\t\ta:    *NewVerifiableEmailAddress(\"foo@ory.sh\", x.NewUUID()),\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(\"case=\"+tc.name, func(t *testing.T) {\n\t\t\tassert.Equal(t,\n\t\t\t\treflectiveHash(tc.a),\n\t\t\t\ttc.a.Signature(),\n\t\t\t)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "identity/manager.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\n\tstderrors \"errors\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/mohae/deepcopy\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nvar ErrProtectedFieldModified = herodot.ErrForbidden.\n\tWithReasonf(`A field was modified that updates one or more credentials-related settings. This action was blocked because an unprivileged method was used to execute the update. This is either a configuration issue or a bug and should be reported to the system administrator.`)\n\ntype (\n\tmanagerDependencies interface {\n\t\tconfig.Provider\n\t\tPoolProvider\n\t\tPrivilegedPoolProvider\n\t\totelx.Provider\n\t\tcourier.Provider\n\t\tValidationProvider\n\t\tActiveCredentialsCounterStrategyProvider\n\t\tlogrusx.Provider\n\t}\n\tManagementProvider interface {\n\t\tIdentityManager() *Manager\n\t}\n\tManager struct {\n\t\tr managerDependencies\n\t}\n\n\tManagerOptions struct {\n\t\tExposeValidationErrors    bool\n\t\tAllowWriteProtectedTraits bool\n\t}\n\n\tManagerOption func(*ManagerOptions)\n)\n\nfunc NewManager(r managerDependencies) *Manager {\n\treturn &Manager{r: r}\n}\n\nfunc ManagerExposeValidationErrorsForInternalTypeAssertion(options *ManagerOptions) {\n\toptions.ExposeValidationErrors = true\n}\n\nfunc ManagerAllowWriteProtectedTraits(options *ManagerOptions) {\n\toptions.AllowWriteProtectedTraits = true\n}\n\nfunc newManagerOptions(opts []ManagerOption) *ManagerOptions {\n\tvar o ManagerOptions\n\tfor _, f := range opts {\n\t\tf(&o)\n\t}\n\treturn &o\n}\n\nfunc (m *Manager) Create(ctx context.Context, i *Identity, opts ...ManagerOption) (err error) {\n\tctx, span := m.r.Tracer(ctx).Tracer().Start(ctx, \"identity.Manager.Create\")\n\tdefer otelx.End(span, &err)\n\n\tif i.SchemaID == \"\" {\n\t\ti.SchemaID = m.r.Config().DefaultIdentityTraitsSchemaID(ctx)\n\t}\n\n\to := newManagerOptions(opts)\n\tif err := m.ValidateIdentity(ctx, i, o); err != nil {\n\t\treturn err\n\t}\n\n\tif err := m.r.PrivilegedIdentityPool().CreateIdentity(ctx, i); err != nil {\n\t\tif errors.Is(err, sqlcon.ErrUniqueViolation) {\n\t\t\treturn m.findExistingAuthMethod(ctx, err, i)\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (m *Manager) ConflictingIdentity(ctx context.Context, i *Identity) (found *Identity, foundConflictAddress string, conflictAddressType string, err error) {\n\tfor ct, cred := range i.Credentials {\n\t\tfor _, id := range cred.Identifiers {\n\t\t\tfound, _, err = m.r.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, ct, id)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// FindByCredentialsIdentifier does not expand identity credentials.\n\t\t\tif err = m.r.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, found, ExpandCredentials); err != nil {\n\t\t\t\treturn nil, \"\", \"\", err\n\t\t\t}\n\n\t\t\treturn found, id, ct.String(), nil\n\t\t}\n\t}\n\n\t// If the conflict is not in the identifiers table, it is coming from the verifiable or recovery address.\n\tfor _, va := range i.VerifiableAddresses {\n\t\tconflictingAddress, err := m.r.PrivilegedIdentityPool().FindVerifiableAddressByValue(ctx, va.Via, va.Value)\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\treturn nil, \"\", \"\", err\n\t\t}\n\n\t\tfoundConflictAddress = conflictingAddress.Value\n\t\tfound, err = m.r.PrivilegedIdentityPool().GetIdentity(ctx, conflictingAddress.IdentityID, ExpandCredentials)\n\t\tif err != nil {\n\t\t\treturn nil, \"\", \"\", err\n\t\t}\n\n\t\treturn found, foundConflictAddress, va.Via, nil\n\t}\n\n\t// Last option: check the recovery address\n\tfor _, va := range i.RecoveryAddresses {\n\t\tconflictingAddress, err := m.r.PrivilegedIdentityPool().FindRecoveryAddressByValue(ctx, va.Via, va.Value)\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\treturn nil, \"\", \"\", err\n\t\t}\n\n\t\tfoundConflictAddress = conflictingAddress.Value\n\t\tfound, err = m.r.PrivilegedIdentityPool().GetIdentity(ctx, conflictingAddress.IdentityID, ExpandCredentials)\n\t\tif err != nil {\n\t\t\treturn nil, \"\", \"\", err\n\t\t}\n\n\t\treturn found, foundConflictAddress, string(va.Via), nil\n\t}\n\n\treturn nil, \"\", \"\", sqlcon.ErrNoRows\n}\n\nfunc (m *Manager) findExistingAuthMethod(ctx context.Context, e error, i *Identity) (err error) {\n\tif !m.r.Config().SelfServiceFlowRegistrationLoginHints(ctx) {\n\t\treturn &ErrDuplicateCredentials{error: e}\n\t}\n\n\tfound, foundConflictAddress, conflictingAddressType, err := m.ConflictingIdentity(ctx, i)\n\tif err != nil {\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\treturn &ErrDuplicateCredentials{error: e}\n\t\t}\n\t\treturn err\n\t}\n\n\t// We need to sort the credentials for the error message to be deterministic.\n\tvar creds []Credentials\n\tfor _, cred := range found.Credentials {\n\t\tcreds = append(creds, cred)\n\t}\n\tsort.Slice(creds, func(i, j int) bool {\n\t\treturn creds[i].Type < creds[j].Type\n\t})\n\n\tduplicateCredErr := &ErrDuplicateCredentials{error: e}\n\t// OIDC credentials are not email addresses but the sub claim from the OIDC provider.\n\t// This is useless for the user, so in that case, we don't set the identifier hint.\n\tif conflictingAddressType != CredentialsTypeOIDC.String() {\n\t\tduplicateCredErr.SetIdentifierHint(strings.Trim(foundConflictAddress, \" \"))\n\t}\n\n\tfor _, cred := range creds {\n\t\tif cred.Config == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Basically, we only have password, oidc, and webauthn as first factor credentials.\n\t\t// We don't care about second factor, because they don't help the user understand how to sign\n\t\t// in to the first factor (obviously).\n\t\tswitch cred.Type {\n\t\tcase CredentialsTypePassword:\n\t\t\tif duplicateCredErr.IdentifierHint() == \"\" && len(cred.Identifiers) == 1 {\n\t\t\t\tduplicateCredErr.SetIdentifierHint(cred.Identifiers[0])\n\t\t\t}\n\n\t\t\tvar cfg CredentialsPassword\n\t\t\tif err := json.Unmarshal(cred.Config, &cfg); err != nil {\n\t\t\t\t// just ignore this credential if the config is invalid\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif cfg.HashedPassword == \"\" {\n\t\t\t\t// just ignore this credential if the hashed password is empty\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tduplicateCredErr.AddCredentialsType(cred.Type)\n\t\tcase CredentialsTypeCodeAuth:\n\t\t\tduplicateCredErr.AddCredentialsType(cred.Type)\n\t\tcase CredentialsTypeOIDC:\n\t\t\tvar cfg CredentialsOIDC\n\t\t\tif err := json.Unmarshal(cred.Config, &cfg); err != nil {\n\t\t\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to JSON decode identity credentials %s for identity %s.\", cred.Type, found.ID))\n\t\t\t}\n\n\t\t\tavailable := make([]string, 0, len(cfg.Providers))\n\t\t\tfor _, provider := range cfg.Providers {\n\t\t\t\tavailable = append(available, provider.Provider)\n\t\t\t}\n\n\t\t\tduplicateCredErr.AddCredentialsType(cred.Type)\n\t\t\tduplicateCredErr.availableOIDCProviders = available\n\t\tcase CredentialsTypeWebAuthn:\n\t\t\tvar cfg CredentialsWebAuthnConfig\n\t\t\tif err := json.Unmarshal(cred.Config, &cfg); err != nil {\n\t\t\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to JSON decode identity credentials %s for identity %s.\", cred.Type, found.ID))\n\t\t\t}\n\n\t\t\tif duplicateCredErr.IdentifierHint() == \"\" && len(cred.Identifiers) == 1 {\n\t\t\t\tduplicateCredErr.SetIdentifierHint(cred.Identifiers[0])\n\t\t\t}\n\t\t\tfor _, webauthn := range cfg.Credentials {\n\t\t\t\tif webauthn.IsPasswordless {\n\t\t\t\t\tduplicateCredErr.AddCredentialsType(cred.Type)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\tcase CredentialsTypePasskey:\n\t\t\tvar cfg CredentialsWebAuthnConfig\n\t\t\tif err := json.Unmarshal(cred.Config, &cfg); err != nil {\n\t\t\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to JSON decode identity credentials %s for identity %s.\", cred.Type, found.ID))\n\t\t\t}\n\n\t\t\tif duplicateCredErr.IdentifierHint() == \"\" && len(cred.Identifiers) == 1 {\n\t\t\t\tduplicateCredErr.SetIdentifierHint(cred.Identifiers[0])\n\t\t\t}\n\t\t\tfor _, webauthn := range cfg.Credentials {\n\t\t\t\tif webauthn.IsPasswordless {\n\t\t\t\t\tduplicateCredErr.AddCredentialsType(cred.Type)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn duplicateCredErr\n}\n\ntype ErrDuplicateCredentials struct {\n\terror\n\n\tavailableCredentials   []CredentialsType\n\tavailableOIDCProviders []string\n\tidentifierHint         string\n}\n\nvar _ schema.DuplicateCredentialsHinter = (*ErrDuplicateCredentials)(nil)\n\nfunc (e *ErrDuplicateCredentials) Unwrap() error {\n\treturn e.error\n}\n\nfunc (e *ErrDuplicateCredentials) AddCredentialsType(ct CredentialsType) {\n\te.availableCredentials = append(e.availableCredentials, ct)\n}\n\nfunc (e *ErrDuplicateCredentials) SetIdentifierHint(hint string) {\n\tif hint != \"\" {\n\t\te.identifierHint = hint\n\t}\n}\n\nfunc (e *ErrDuplicateCredentials) AvailableCredentials() []string {\n\tres := make([]string, len(e.availableCredentials))\n\tfor k, v := range e.availableCredentials {\n\t\tres[k] = string(v)\n\t}\n\tslices.Sort(res)\n\n\treturn res\n}\n\nfunc (e *ErrDuplicateCredentials) AvailableOIDCProviders() []string {\n\tif e.availableOIDCProviders == nil {\n\t\treturn []string{}\n\t}\n\tslices.Sort(e.availableOIDCProviders)\n\treturn e.availableOIDCProviders\n}\n\nfunc (e *ErrDuplicateCredentials) IdentifierHint() string {\n\treturn e.identifierHint\n}\n\nfunc (e *ErrDuplicateCredentials) HasHints() bool {\n\treturn len(e.availableCredentials) > 0 || len(e.availableOIDCProviders) > 0 || len(e.identifierHint) > 0\n}\n\ntype FailedIdentity struct {\n\tIdentity *Identity\n\tError    *herodot.DefaultError\n}\n\ntype CreateIdentitiesError struct {\n\tfailedIdentities map[*Identity]*herodot.DefaultError\n}\n\nfunc NewCreateIdentitiesError(capacity int) *CreateIdentitiesError {\n\treturn &CreateIdentitiesError{\n\t\tfailedIdentities: make(map[*Identity]*herodot.DefaultError, capacity),\n\t}\n}\n\nfunc (e *CreateIdentitiesError) Error() string {\n\te.init()\n\treturn fmt.Sprintf(\"create identities error: %d identities failed\", len(e.failedIdentities))\n}\n\nfunc (e *CreateIdentitiesError) Unwrap() []error {\n\te.init()\n\tvar errs []error\n\tfor _, err := range e.failedIdentities {\n\t\terrs = append(errs, err)\n\t}\n\treturn errs\n}\n\nfunc (e *CreateIdentitiesError) AddFailedIdentity(ident *Identity, err *herodot.DefaultError) {\n\te.init()\n\te.failedIdentities[ident] = err\n}\n\nfunc (e *CreateIdentitiesError) Merge(other *CreateIdentitiesError) {\n\te.init()\n\tfor k, v := range other.failedIdentities {\n\t\te.failedIdentities[k] = v\n\t}\n}\n\nfunc (e *CreateIdentitiesError) Contains(ident *Identity) bool {\n\te.init()\n\t_, found := e.failedIdentities[ident]\n\treturn found\n}\n\nfunc (e *CreateIdentitiesError) Find(ident *Identity) *FailedIdentity {\n\te.init()\n\tif err, found := e.failedIdentities[ident]; found {\n\t\treturn &FailedIdentity{Identity: ident, Error: err}\n\t}\n\n\treturn nil\n}\n\nfunc (e *CreateIdentitiesError) ErrOrNil() error {\n\tif e == nil || len(e.failedIdentities) == 0 {\n\t\treturn nil\n\t}\n\treturn e\n}\n\nfunc (e *CreateIdentitiesError) init() {\n\tif e.failedIdentities == nil {\n\t\te.failedIdentities = map[*Identity]*herodot.DefaultError{}\n\t}\n}\n\nfunc (m *Manager) CreateIdentities(ctx context.Context, identities []*Identity, opts ...ManagerOption) (err error) {\n\tctx, span := m.r.Tracer(ctx).Tracer().Start(ctx, \"identity.Manager.CreateIdentities\")\n\tdefer otelx.End(span, &err)\n\n\tcreateIdentitiesError := NewCreateIdentitiesError(len(identities))\n\tvalidIdentities := make([]*Identity, 0, len(identities))\n\tfor _, ident := range identities {\n\t\tif ident.SchemaID == \"\" {\n\t\t\tident.SchemaID = m.r.Config().DefaultIdentityTraitsSchemaID(ctx)\n\t\t}\n\n\t\to := newManagerOptions(opts)\n\t\tif err := m.ValidateIdentity(ctx, ident, o); err != nil {\n\t\t\treason := err.Error()\n\t\t\tif e, ok := stderrors.AsType[*herodot.DefaultError](err); ok {\n\t\t\t\treason = e.Reason()\n\t\t\t}\n\t\t\tcreateIdentitiesError.AddFailedIdentity(ident, herodot.ErrBadRequest.WithReason(reason).WithWrap(err))\n\t\t\tcontinue\n\t\t}\n\t\tvalidIdentities = append(validIdentities, ident)\n\t}\n\n\tif err := m.r.PrivilegedIdentityPool().CreateIdentities(ctx, validIdentities...); err != nil {\n\t\tif partialErr := new(CreateIdentitiesError); errors.As(err, &partialErr) {\n\t\t\tcreateIdentitiesError.Merge(partialErr)\n\t\t} else {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn createIdentitiesError.ErrOrNil()\n}\n\nfunc (m *Manager) requiresPrivilegedAccess(ctx context.Context, original, updated *Identity, o *ManagerOptions) (err error) {\n\t_, span := m.r.Tracer(ctx).Tracer().Start(ctx, \"identity.Manager.requiresPrivilegedAccess\")\n\tdefer otelx.End(span, &err)\n\n\tif !o.AllowWriteProtectedTraits {\n\t\tif !CredentialsEqual(updated.Credentials, original.Credentials) {\n\t\t\t// reset the identity\n\t\t\t*updated = *original\n\n\t\t\treturn errors.WithStack(ErrProtectedFieldModified)\n\t\t}\n\n\t\tif !reflect.DeepEqual(original.VerifiableAddresses, updated.VerifiableAddresses) &&\n\t\t\t/* prevent nil != []string{} */\n\t\t\tlen(original.VerifiableAddresses)+len(updated.VerifiableAddresses) != 0 {\n\t\t\t// reset the identity\n\t\t\t*updated = *original\n\t\t\treturn errors.WithStack(ErrProtectedFieldModified)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (m *Manager) Update(ctx context.Context, updated *Identity, opts ...ManagerOption) (err error) {\n\tctx, span := m.r.Tracer(ctx).Tracer().Start(ctx, \"identity.Manager.Update\")\n\tdefer otelx.End(span, &err)\n\n\to := newManagerOptions(opts)\n\tif err := m.ValidateIdentity(ctx, updated, o); err != nil {\n\t\treturn err\n\t}\n\n\toriginal, err := m.r.PrivilegedIdentityPool().GetIdentityConfidential(ctx, updated.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := m.requiresPrivilegedAccess(ctx, original, updated, o); err != nil {\n\t\treturn err\n\t}\n\n\treturn m.r.PrivilegedIdentityPool().UpdateIdentity(ctx, updated, DiffAgainst(original))\n}\n\nfunc (m *Manager) UpdateSchemaID(ctx context.Context, id uuid.UUID, schemaID string, opts ...ManagerOption) (err error) {\n\tctx, span := m.r.Tracer(ctx).Tracer().Start(ctx, \"identity.Manager.UpdateSchemaID\")\n\tdefer otelx.End(span, &err)\n\n\to := newManagerOptions(opts)\n\toriginal, err := m.r.PrivilegedIdentityPool().GetIdentityConfidential(ctx, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !o.AllowWriteProtectedTraits && original.SchemaID != schemaID {\n\t\treturn errors.WithStack(ErrProtectedFieldModified)\n\t}\n\n\toriginal.SchemaID = schemaID\n\tif err := m.ValidateIdentity(ctx, original, o); err != nil {\n\t\treturn err\n\t}\n\n\treturn m.r.PrivilegedIdentityPool().UpdateIdentity(ctx, original)\n}\n\nfunc (m *Manager) SetTraits(ctx context.Context, id uuid.UUID, traits Traits, opts ...ManagerOption) (_ *Identity, err error) {\n\tctx, span := m.r.Tracer(ctx).Tracer().Start(ctx, \"identity.Manager.SetTraits\")\n\tdefer otelx.End(span, &err)\n\n\to := newManagerOptions(opts)\n\toriginal, err := m.r.PrivilegedIdentityPool().GetIdentityConfidential(ctx, id)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// original is used to check whether protected traits were modified\n\tupdated := deepcopy.Copy(original).(*Identity)\n\tupdated.Traits = traits\n\tif err := m.ValidateIdentity(ctx, updated, o); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := m.requiresPrivilegedAccess(ctx, original, updated, o); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn updated, nil\n}\n\n// RefreshAvailableAAL refreshes the available AAL for the identity.\n//\n// This method is a no-op if everything is up-to date.\n//\n// Please make sure to load all credentials before using this method.\nfunc (m *Manager) RefreshAvailableAAL(ctx context.Context, i *Identity) (err error) {\n\tif len(i.Credentials) == 0 {\n\t\tif err := m.r.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, i, ExpandCredentials); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\taalBefore := i.InternalAvailableAAL\n\tif err := i.SetAvailableAAL(ctx, m); err != nil {\n\t\treturn err\n\t}\n\n\tif aalBefore.String != i.InternalAvailableAAL.String || aalBefore.Valid != i.InternalAvailableAAL.Valid {\n\t\treturn m.r.PrivilegedIdentityPool().UpdateIdentityColumns(ctx, i, \"available_aal\")\n\t}\n\n\treturn nil\n}\n\nfunc (m *Manager) UpdateTraits(ctx context.Context, id uuid.UUID, traits Traits, opts ...ManagerOption) (err error) {\n\tctx, span := m.r.Tracer(ctx).Tracer().Start(ctx, \"identity.Manager.UpdateTraits\")\n\tdefer otelx.End(span, &err)\n\n\tupdated, err := m.SetTraits(ctx, id, traits, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn m.r.PrivilegedIdentityPool().UpdateIdentity(ctx, updated)\n}\n\nfunc (m *Manager) ValidateIdentity(ctx context.Context, i *Identity, o *ManagerOptions) (err error) {\n\tif err := m.r.IdentityValidator().Validate(ctx, i); err != nil {\n\t\tvar validationErr *jsonschema.ValidationError\n\t\tif errors.As(err, &validationErr) && !o.ExposeValidationErrors {\n\t\t\treturn herodot.ErrBadRequest.WithReasonf(\"%s\", err).WithWrap(err)\n\t\t}\n\t\treturn err\n\t}\n\n\tif err := i.SetAvailableAAL(ctx, m); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (m *Manager) CountActiveFirstFactorCredentials(ctx context.Context, i *Identity) (count int, err error) {\n\t// This trace is more noisy than it's worth in diagnostic power.\n\t// ctx, span := m.r.Tracer(ctx).Tracer().Start(ctx, \"identity.Manager.CountActiveFirstFactorCredentials\")\n\t// defer otelx.End(span, &err)\n\n\tfor _, strategy := range m.r.ActiveCredentialsCounterStrategies(ctx) {\n\t\tcurrent, err := strategy.CountActiveFirstFactorCredentials(ctx, i.Credentials)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tcount += current\n\t}\n\treturn count, nil\n}\n\nfunc (m *Manager) CountActiveMultiFactorCredentials(ctx context.Context, i *Identity) (count int, err error) {\n\t// This trace is more noisy than it's worth in diagnostic power.\n\t// ctx, span := m.r.Tracer(ctx).Tracer().Start(ctx, \"identity.Manager.CountActiveMultiFactorCredentials\")\n\t// defer otelx.End(span, &err)\n\n\tfor _, strategy := range m.r.ActiveCredentialsCounterStrategies(ctx) {\n\t\tcurrent, err := strategy.CountActiveMultiFactorCredentials(ctx, i.Credentials)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tcount += current\n\t}\n\treturn count, nil\n}\n"
  },
  {
    "path": "identity/manager_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity_test\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t_ \"embed\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/x\"\n)\n\n//go:embed stub/aal.json\nvar refreshAALStubs []byte\n\nfunc TestManager(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(map[string]interface{}{\n\t\t\tconfig.ViperKeyCourierSMTPURL:                    \"smtp://foo@bar@dev.null/\",\n\t\t\tconfig.ViperKeySelfServiceRegistrationLoginHints: true,\n\t\t\tconfig.ViperKeyDefaultIdentitySchemaID:           \"default\",\n\t\t}),\n\t\tconfigx.WithValues(testhelpers.IdentitySchemasConfig(map[string]string{\n\t\t\t\"default\":   \"file://./stub/manager.schema.json\",\n\t\t\t\"extension\": \"file://./stub/extension.schema.json\",\n\t\t})),\n\t)\n\n\tt.Run(\"case=should fail to create because validation fails\", func(t *testing.T) {\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\ti.Traits = identity.Traits(`{\"email\":\"not an email\"}`)\n\t\trequire.Error(t, reg.IdentityManager().Create(t.Context(), i))\n\t})\n\n\tnewTraits := func(email string, unprotected string) identity.Traits {\n\t\treturn identity.Traits(fmt.Sprintf(`{\"email\":\"%[1]s\",\"email_verify\":\"%[1]s\",\"email_recovery\":\"%[1]s\",\"email_creds\":\"%[1]s\",\"unprotected\": \"%[2]s\"}`, email, unprotected))\n\t}\n\n\tcheckExtensionFields := func(i *identity.Identity, expected string) func(*testing.T) {\n\t\treturn func(t *testing.T) {\n\t\t\trequire.Len(t, i.VerifiableAddresses, 1)\n\t\t\tassert.EqualValues(t, expected, i.VerifiableAddresses[0].Value)\n\t\t\tassert.EqualValues(t, identity.AddressTypeEmail, i.VerifiableAddresses[0].Via)\n\n\t\t\trequire.Len(t, i.RecoveryAddresses, 1)\n\t\t\tassert.EqualValues(t, expected, i.RecoveryAddresses[0].Value)\n\t\t\tassert.EqualValues(t, identity.AddressTypeEmail, i.RecoveryAddresses[0].Via)\n\n\t\t\trequire.NotNil(t, i.Credentials[identity.CredentialsTypePassword])\n\t\t\tassert.Equal(t, []string{expected}, i.Credentials[identity.CredentialsTypePassword].Identifiers)\n\t\t}\n\t}\n\n\tcheckExtensionFieldsForIdentities := func(t *testing.T, expected string, original *identity.Identity) {\n\t\tfromStore, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), original.ID)\n\t\trequire.NoError(t, err)\n\t\tidentities := []identity.Identity{*original, *fromStore}\n\t\tfor k := range identities {\n\t\t\tt.Run(fmt.Sprintf(\"identity=%d\", k), checkExtensionFields(&identities[k], expected))\n\t\t}\n\t}\n\n\tt.Run(\"method=CreateIdentities\", func(t *testing.T) {\n\t\tt.Run(\"case=should set AAL to 2 if password and TOTP is set\", func(t *testing.T) {\n\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(email, \"\")\n\t\t\toriginal.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\tType: identity.CredentialsTypePassword,\n\t\t\t\t\t// By explicitly not setting the identifier, we mimic the behavior of the PATCH endpoint.\n\t\t\t\t\t// This tests a bug we introduced on the PATCH endpoint where the AAL value would not be correct.\n\t\t\t\t\tIdentifiers: []string{},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t\t},\n\t\t\t\tidentity.CredentialsTypeTOTP: {\n\t\t\t\t\tType: identity.CredentialsTypeTOTP,\n\t\t\t\t\t// By explicitly not setting the identifier, we mimic the behavior of the PATCH endpoint.\n\t\t\t\t\t// This tests a bug we introduced on the PATCH endpoint where the AAL value would not be correct.\n\t\t\t\t\tIdentifiers: []string{},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t},\n\t\t\t}\n\t\t\trequire.NoError(t, reg.IdentityManager().CreateIdentities(t.Context(), []*identity.Identity{original}))\n\t\t\tfromStore, err := reg.PrivilegedIdentityPool().GetIdentity(t.Context(), original.ID, identity.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tgot, ok := fromStore.InternalAvailableAAL.ToAAL()\n\t\t\trequire.True(t, ok)\n\t\t\tassert.Equal(t, identity.AuthenticatorAssuranceLevel2, got)\n\t\t})\n\t})\n\n\tt.Run(\"method=Create\", func(t *testing.T) {\n\t\tt.Run(\"case=should create identity and track extension fields\", func(t *testing.T) {\n\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(email, \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t\t\tcheckExtensionFieldsForIdentities(t, email, original)\n\t\t\tgot, ok := original.InternalAvailableAAL.ToAAL()\n\t\t\trequire.True(t, ok)\n\t\t\tassert.Equal(t, identity.NoAuthenticatorAssuranceLevel, got)\n\t\t})\n\n\t\tt.Run(\"case=correctly set AAL\", func(t *testing.T) {\n\t\t\tt.Run(\"case=should set AAL to 0 if no credentials are available\", func(t *testing.T) {\n\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\toriginal.Traits = newTraits(email, \"\")\n\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t\t\t\tgot, ok := original.InternalAvailableAAL.ToAAL()\n\t\t\t\trequire.True(t, ok)\n\t\t\t\tassert.Equal(t, identity.NoAuthenticatorAssuranceLevel, got)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should set AAL to 1 if password is set\", func(t *testing.T) {\n\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\toriginal.Traits = newTraits(email, \"\")\n\t\t\t\toriginal.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t\t\t\tgot, ok := original.InternalAvailableAAL.ToAAL()\n\t\t\t\trequire.True(t, ok)\n\t\t\t\tassert.Equal(t, identity.AuthenticatorAssuranceLevel1, got)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should set AAL to 2 if password and TOTP is set\", func(t *testing.T) {\n\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\toriginal.Traits = newTraits(email, \"\")\n\t\t\t\toriginal.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t\t\t},\n\t\t\t\t\tidentity.CredentialsTypeTOTP: {\n\t\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t\t\t\tgot, ok := original.InternalAvailableAAL.ToAAL()\n\t\t\t\trequire.True(t, ok)\n\t\t\t\tassert.Equal(t, identity.AuthenticatorAssuranceLevel2, got)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should set AAL to 2 if only TOTP is set\", func(t *testing.T) {\n\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\toriginal.Traits = newTraits(email, \"\")\n\t\t\t\toriginal.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypeTOTP: {\n\t\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t\t\t\tgot, ok := original.InternalAvailableAAL.ToAAL()\n\t\t\t\trequire.True(t, ok)\n\t\t\t\tassert.Equal(t, identity.AuthenticatorAssuranceLevel2, got)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should expose validation errors with option\", func(t *testing.T) {\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = identity.Traits(`{\"email\":\"not an email\"}`)\n\t\t\terr := reg.IdentityManager().Create(t.Context(), original, identity.ManagerExposeValidationErrorsForInternalTypeAssertion)\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Contains(t, err.Error(), \"\\\"not an email\\\" is not valid \\\"email\\\"\")\n\t\t})\n\n\t\tt.Run(\"case=should not expose validation errors without option\", func(t *testing.T) {\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = identity.Traits(`{\"email\":\"not an email\"}`)\n\t\t\terr := reg.IdentityManager().Create(t.Context(), original)\n\t\t\trequire.Error(t, err)\n\t\t\tassert.NotContains(t, err.Error(), \"\\\"not an email\\\" is not valid \\\"email\\\"\")\n\t\t})\n\n\t\tt.Run(\"case=should correctly hint at the duplicate credential\", func(t *testing.T) {\n\t\t\tcreateIdentity := func(email string, field string, creds map[identity.CredentialsType]identity.Credentials) *identity.Identity {\n\t\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\ti.Traits = identity.Traits(fmt.Sprintf(`{\"%s\":\"%s\"}`, field, email))\n\t\t\t\ti.Credentials = creds\n\t\t\t\treturn i\n\t\t\t}\n\n\t\t\tt.Run(\"case=credential identifier duplicate\", func(t *testing.T) {\n\t\t\t\tt.Run(\"type=password\", func(t *testing.T) {\n\t\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\t\tcreds := map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\n\t\t\t\t\tfirst := createIdentity(email, \"email_creds\", creds)\n\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), first))\n\n\t\t\t\t\tsecond := createIdentity(email, \"email_creds\", creds)\n\t\t\t\t\terr := reg.IdentityManager().Create(t.Context(), second)\n\t\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t\tverr := new(identity.ErrDuplicateCredentials)\n\t\t\t\t\tassert.ErrorAs(t, err, &verr)\n\t\t\t\t\tassert.EqualValues(t, []string{identity.CredentialsTypePassword.String()}, verr.AvailableCredentials())\n\t\t\t\t\tassert.Len(t, verr.AvailableOIDCProviders(), 0)\n\t\t\t\t\tassert.Equal(t, verr.IdentifierHint(), email)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=webauthn\", func(t *testing.T) {\n\t\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\t\tcreds := map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypeWebAuthn: {\n\t\t\t\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"credentials\": [{\"is_passwordless\":true}]}`),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\n\t\t\t\t\tfirst := createIdentity(email, \"email_webauthn\", creds)\n\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), first))\n\n\t\t\t\t\tsecond := createIdentity(email, \"email_webauthn\", nil)\n\t\t\t\t\terr := reg.IdentityManager().Create(t.Context(), second)\n\t\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t\tverr := new(identity.ErrDuplicateCredentials)\n\t\t\t\t\tassert.ErrorAs(t, err, &verr)\n\t\t\t\t\tassert.EqualValues(t, []string{identity.CredentialsTypeWebAuthn.String()}, verr.AvailableCredentials())\n\t\t\t\t\tassert.Len(t, verr.AvailableOIDCProviders(), 0)\n\t\t\t\t\tassert.Equal(t, verr.IdentifierHint(), email)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=oidc\", func(t *testing.T) {\n\t\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\t\tcreds := map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\t\t\t\tType: identity.CredentialsTypeOIDC,\n\t\t\t\t\t\t\t// Identifiers in OIDC are not email addresses, but a unique user ID.\n\t\t\t\t\t\t\tIdentifiers: []string{\"google:\" + uuid.Must(uuid.NewV4()).String()},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\":[{\"provider\": \"google\"},{\"provider\": \"github\"}]}`),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\n\t\t\t\t\tfirst := createIdentity(email, \"email_creds\", creds)\n\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), first))\n\n\t\t\t\t\tsecond := createIdentity(email, \"email_creds\", creds)\n\t\t\t\t\terr := reg.IdentityManager().Create(t.Context(), second)\n\t\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t\tverr := new(identity.ErrDuplicateCredentials)\n\t\t\t\t\tassert.ErrorAs(t, err, &verr)\n\t\t\t\t\tassert.ElementsMatch(t, []string{\"oidc\"}, verr.AvailableCredentials())\n\t\t\t\t\tassert.ElementsMatch(t, []string{\"google\", \"github\"}, verr.AvailableOIDCProviders())\n\t\t\t\t\t// The conflicting identifier is the oidc subject, which is not useful for the user\n\t\t\t\t\tassert.Equal(t, email, verr.IdentifierHint())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=password+oidc+webauthn\", func(t *testing.T) {\n\t\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\t\tcreds := map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\t\t\t\tType: identity.CredentialsTypeOIDC,\n\t\t\t\t\t\t\t// Identifiers in OIDC are not email addresses, but a unique user ID.\n\t\t\t\t\t\t\tIdentifiers: []string{\"google:\" + uuid.Must(uuid.NewV4()).String()},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\":[{\"provider\": \"google\"},{\"provider\": \"github\"}]}`),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tidentity.CredentialsTypeWebAuthn: {\n\t\t\t\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"credentials\": [{\"is_passwordless\":true}]}`),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\n\t\t\t\t\tfirst := createIdentity(email, \"email_creds\", creds)\n\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), first))\n\n\t\t\t\t\tsecond := createIdentity(email, \"email_creds\", creds)\n\t\t\t\t\terr := reg.IdentityManager().Create(t.Context(), second)\n\t\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t\tverr := new(identity.ErrDuplicateCredentials)\n\t\t\t\t\tassert.ErrorAs(t, err, &verr)\n\t\t\t\t\tassert.ElementsMatch(t, []string{\"password\", \"oidc\", \"webauthn\"}, verr.AvailableCredentials())\n\t\t\t\t\tassert.ElementsMatch(t, []string{\"google\", \"github\"}, verr.AvailableOIDCProviders())\n\t\t\t\t\tassert.Equal(t, email, verr.IdentifierHint())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=code\", func(t *testing.T) {\n\t\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\t\tcreds := map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypeCodeAuth: {\n\t\t\t\t\t\t\tType:        identity.CredentialsTypeCodeAuth,\n\t\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\n\t\t\t\t\tfirst := createIdentity(email, \"email_creds\", creds)\n\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), first))\n\n\t\t\t\t\tsecond := createIdentity(email, \"email_creds\", creds)\n\t\t\t\t\terr := reg.IdentityManager().Create(t.Context(), second)\n\t\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t\tverr := new(identity.ErrDuplicateCredentials)\n\t\t\t\t\tassert.ErrorAs(t, err, &verr)\n\t\t\t\t\tassert.EqualValues(t, []string{identity.CredentialsTypeCodeAuth.String()}, verr.AvailableCredentials())\n\t\t\t\t\tassert.Len(t, verr.AvailableOIDCProviders(), 0)\n\t\t\t\t\tassert.Equal(t, verr.IdentifierHint(), email)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\trunAddress := func(t *testing.T, field string) {\n\t\t\t\tt.Run(\"case=password duplicate\", func(t *testing.T) {\n\t\t\t\t\t// This test mimics a case where an existing user with email + password exists, and the\n\t\t\t\t\t// new user tries to sign up with a verification email (NOT email + password) that matches\n\t\t\t\t\t// this existing record. Here, the end result is that we want to show the\n\t\t\t\t\t// user: \"Sign up with email foo@bar.com and your password instead.\"\n\t\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\t\tcreds := map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\n\t\t\t\t\tfirst := createIdentity(email, field, creds)\n\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), first))\n\n\t\t\t\t\tsecond := createIdentity(email, field, nil)\n\t\t\t\t\terr := reg.IdentityManager().Create(t.Context(), second)\n\t\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t\tverr := new(identity.ErrDuplicateCredentials)\n\t\t\t\t\tassert.ErrorAs(t, err, &verr)\n\t\t\t\t\tassert.EqualValues(t, []string{identity.CredentialsTypePassword.String()}, verr.AvailableCredentials())\n\t\t\t\t\tassert.Len(t, verr.AvailableOIDCProviders(), 0)\n\t\t\t\t\tassert.Equal(t, verr.IdentifierHint(), email)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=OIDC duplicate\", func(t *testing.T) {\n\t\t\t\t\t// This test mimics a case where user signed up using Social Sign In exists, and the\n\t\t\t\t\t// new user tries to sign up with a verification email (NOT email + password) that matches\n\t\t\t\t\t// this existing record (for example by using another social sign in provider.\n\t\t\t\t\t// Here, the end result is that we want to show \"Sign in using google instead\".\n\t\t\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\t\t\tcreds := map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\t\t\t\tType: identity.CredentialsTypeOIDC,\n\t\t\t\t\t\t\t// Identifiers in OIDC are not email addresses, but a unique user ID.\n\t\t\t\t\t\t\tIdentifiers: []string{\"google:\" + uuid.Must(uuid.NewV4()).String()},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"providers\":[{\"provider\": \"google\"},{\"provider\": \"github\"}]}`),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\n\t\t\t\t\tfirst := createIdentity(email, field, creds)\n\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), first))\n\n\t\t\t\t\tsecond := createIdentity(email, field, nil)\n\t\t\t\t\terr := reg.IdentityManager().Create(t.Context(), second)\n\t\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t\tverr := new(identity.ErrDuplicateCredentials)\n\t\t\t\t\tassert.ErrorAs(t, err, &verr)\n\t\t\t\t\tassert.EqualValues(t, []string{identity.CredentialsTypeOIDC.String()}, verr.AvailableCredentials())\n\t\t\t\t\tassert.EqualValues(t, verr.AvailableOIDCProviders(), []string{\"github\", \"google\"})\n\t\t\t\t\tassert.Equal(t, verr.IdentifierHint(), email)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tt.Run(\"case=verifiable address\", func(t *testing.T) {\n\t\t\t\trunAddress(t, \"email_verify\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=recovery address\", func(t *testing.T) {\n\t\t\t\trunAddress(t, \"email_recovery\")\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"method=Update\", func(t *testing.T) {\n\t\tt.Run(\"case=should update identity and update extension fields\", func(t *testing.T) {\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(\"baz@ory.sh\", \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\n\t\t\toriginal.Traits = newTraits(\"bar@ory.sh\", \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), original, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\tcheckExtensionFieldsForIdentities(t, \"bar@ory.sh\", original)\n\t\t})\n\n\t\tt.Run(\"case=should set AAL to 1 if password is set\", func(t *testing.T) {\n\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(email, \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t\t\toriginal.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t\t},\n\t\t\t}\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), original, identity.ManagerAllowWriteProtectedTraits))\n\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel1, original.InternalAvailableAAL.String)\n\t\t})\n\n\t\tt.Run(\"case=should set AAL to 2 if password and TOTP is set\", func(t *testing.T) {\n\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(email, \"\")\n\t\t\toriginal.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t\t},\n\t\t\t}\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel1, original.InternalAvailableAAL.String)\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), original, identity.ManagerAllowWriteProtectedTraits))\n\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel1, original.InternalAvailableAAL.String, \"Updating without changes should not change AAL\")\n\t\t\toriginal.Credentials[identity.CredentialsTypeTOTP] = identity.Credentials{\n\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t}\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), original, identity.ManagerAllowWriteProtectedTraits))\n\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel2, original.InternalAvailableAAL.String)\n\t\t})\n\n\t\tt.Run(\"case=should set AAL to 2 if only TOTP is set\", func(t *testing.T) {\n\t\t\temail := uuid.Must(uuid.NewV4()).String() + \"@ory.sh\"\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(email, \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t\t\toriginal.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypeTOTP: {\n\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t},\n\t\t\t}\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), original, identity.ManagerAllowWriteProtectedTraits))\n\t\t\tassert.True(t, original.InternalAvailableAAL.Valid)\n\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel2, original.InternalAvailableAAL.String)\n\t\t})\n\n\t\tt.Run(\"case=should not update protected traits without option\", func(t *testing.T) {\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(\"email-update-1@ory.sh\", \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\n\t\t\toriginal.Traits = newTraits(\"email-update-2@ory.sh\", \"\")\n\t\t\terr := reg.IdentityManager().Update(t.Context(), original)\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Equal(t, identity.ErrProtectedFieldModified, errors.Cause(err))\n\n\t\t\tfromStore, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), original.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\t// As UpdateTraits takes only the ID as a parameter it cannot update the identity in place.\n\t\t\t// That is why we only check the identity in the store.\n\t\t\tcheckExtensionFields(fromStore, \"email-update-1@ory.sh\")(t)\n\t\t})\n\n\t\tt.Run(\"case=should update unprotected traits with multiple credential identifiers\", func(t *testing.T) {\n\t\t\toriginal := identity.NewIdentity(\"extension\")\n\t\t\toriginal.Traits = identity.Traits(`{\"email\": \"email-update-ewisdfuja@ory.sh\", \"names\": [\"username1\", \"username2\"], \"age\": 30}`)\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t\t\tassert.Len(t, original.Credentials[identity.CredentialsTypePassword].Identifiers, 3)\n\n\t\t\toriginal.Traits = identity.Traits(`{\"email\": \"email-update-ewisdfuja@ory.sh\", \"names\": [\"username1\", \"username2\"], \"age\": 31}`)\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), original))\n\n\t\t\tfromStore, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), original.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.JSONEq(t, string(original.Traits), string(fromStore.Traits))\n\t\t})\n\n\t\tt.Run(\"case=should update unprotected traits with verified user\", func(t *testing.T) {\n\t\t\temail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(email, \"initial\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\n\t\t\t// mock successful verification process\n\t\t\taddr := original.VerifiableAddresses[0]\n\t\t\taddr.Verified = true\n\t\t\taddr.VerifiedAt = new(sqlxx.NullTime(time.Now().UTC()))\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateVerifiableAddress(t.Context(), &addr))\n\n\t\t\t// reload to properly set the verified address\n\t\t\tvar err error\n\t\t\toriginal, err = reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), original.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\toriginal.Traits = newTraits(email, \"updated\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), original))\n\n\t\t\tfromStore, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), original.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.JSONEq(t, string(original.Traits), string(fromStore.Traits))\n\t\t})\n\n\t\tt.Run(\"case=changing recovery address removes it from the store\", func(t *testing.T) {\n\t\t\toriginalEmail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(originalEmail, \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\n\t\t\tfromStore, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), original.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tcheckExtensionFields(fromStore, originalEmail)(t)\n\n\t\t\tnewEmail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\toriginal.Traits = newTraits(newEmail, \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), original, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\tfromStore, err = reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), original.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tcheckExtensionFields(fromStore, newEmail)(t)\n\n\t\t\trecoveryAddresses, err := reg.PrivilegedIdentityPool().ListRecoveryAddresses(t.Context(), 0, 500)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar foundRecoveryAddress bool\n\t\t\tfor _, a := range recoveryAddresses {\n\t\t\t\tassert.NotEqual(t, a.Value, originalEmail)\n\t\t\t\tif a.Value == newEmail {\n\t\t\t\t\tfoundRecoveryAddress = true\n\t\t\t\t}\n\t\t\t}\n\t\t\trequire.True(t, foundRecoveryAddress)\n\n\t\t\tverifiableAddresses, err := reg.PrivilegedIdentityPool().ListVerifiableAddresses(t.Context(), 0, 500)\n\t\t\trequire.NoError(t, err)\n\t\t\tvar foundVerifiableAddress bool\n\t\t\tfor _, a := range verifiableAddresses {\n\t\t\t\tassert.NotEqual(t, a.Value, originalEmail)\n\t\t\t\tif a.Value == newEmail {\n\t\t\t\t\tfoundVerifiableAddress = true\n\t\t\t\t}\n\t\t\t}\n\t\t\trequire.True(t, foundVerifiableAddress)\n\t\t})\n\t})\n\n\tt.Run(\"method=CountActiveFirstFactorCredentials\", func(t *testing.T) {\n\t\tid := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\tcount, err := reg.IdentityManager().CountActiveFirstFactorCredentials(t.Context(), id)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 0, count)\n\n\t\tid.Credentials[identity.CredentialsTypePassword] = identity.Credentials{\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\tConfig:      []byte(`{\"hashed_password\":\"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`),\n\t\t}\n\n\t\tcount, err = reg.IdentityManager().CountActiveFirstFactorCredentials(t.Context(), id)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 1, count)\n\t})\n\n\tt.Run(\"method=CountActiveMultiFactorCredentials\", func(t *testing.T) {\n\t\tid := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\tcount, err := reg.IdentityManager().CountActiveMultiFactorCredentials(t.Context(), id)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 0, count)\n\n\t\tid.Credentials[identity.CredentialsTypePassword] = identity.Credentials{\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\tConfig:      []byte(`{\"hashed_password\":\"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`),\n\t\t}\n\n\t\tcount, err = reg.IdentityManager().CountActiveMultiFactorCredentials(t.Context(), id)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 0, count)\n\n\t\tid.Credentials[identity.CredentialsTypeWebAuthn] = identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":false}]}`),\n\t\t}\n\n\t\tcount, err = reg.IdentityManager().CountActiveMultiFactorCredentials(t.Context(), id)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 1, count)\n\t})\n\n\tt.Run(\"method=UpdateTraits\", func(t *testing.T) {\n\t\tt.Run(\"case=should update protected traits with option\", func(t *testing.T) {\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(\"email-updatetraits-1@ory.sh\", \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\n\t\t\trequire.NoError(t, reg.IdentityManager().UpdateTraits(\n\t\t\t\tt.Context(), original.ID, newTraits(\"email-updatetraits-2@ory.sh\", \"\"),\n\t\t\t\tidentity.ManagerAllowWriteProtectedTraits))\n\n\t\t\tfromStore, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), original.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\t// As UpdateTraits takes only the ID as a parameter it cannot update the identity in place.\n\t\t\t// That is why we only check the identity in the store.\n\t\t\tcheckExtensionFields(fromStore, \"email-updatetraits-2@ory.sh\")(t)\n\t\t})\n\n\t\tt.Run(\"case=should update identity and update extension fields\", func(t *testing.T) {\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = identity.Traits(`{\"email\":\"baz@ory.sh\",\"email_verify\":\"baz@ory.sh\",\"email_recovery\":\"baz@ory.sh\",\"email_creds\":\"baz@ory.sh\",\"unprotected\": \"foo\"}`)\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\n\t\t\t// These should all fail because they modify existing keys\n\t\t\trequire.Error(t, reg.IdentityManager().UpdateTraits(t.Context(), original.ID, identity.Traits(`{\"email\":\"not-baz@ory.sh\",\"email_verify\":\"baz@ory.sh\",\"email_recovery\":\"baz@ory.sh\",\"email_creds\":\"baz@ory.sh\",\"unprotected\": \"foo\"}`)))\n\t\t\trequire.Error(t, reg.IdentityManager().UpdateTraits(t.Context(), original.ID, identity.Traits(`{\"email\":\"baz@ory.sh\",\"email_verify\":\"not-baz@ory.sh\",\"email_recovery\":\"not-baz@ory.sh\",\"email_creds\":\"baz@ory.sh\",\"unprotected\": \"foo\"}`)))\n\t\t\trequire.Error(t, reg.IdentityManager().UpdateTraits(t.Context(), original.ID, identity.Traits(`{\"email\":\"baz@ory.sh\",\"email_verify\":\"baz@ory.sh\",\"email_recovery\":\"baz@ory.sh\",\"email_creds\":\"not-baz@ory.sh\",\"unprotected\": \"foo\"}`)))\n\n\t\t\trequire.NoError(t, reg.IdentityManager().UpdateTraits(t.Context(), original.ID, identity.Traits(`{\"email\":\"baz@ory.sh\",\"email_verify\":\"baz@ory.sh\",\"email_recovery\":\"baz@ory.sh\",\"email_creds\":\"baz@ory.sh\",\"unprotected\": \"bar\"}`)))\n\t\t\tcheckExtensionFieldsForIdentities(t, \"baz@ory.sh\", original)\n\n\t\t\tactual, err := reg.IdentityPool().GetIdentity(t.Context(), original.ID, identity.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.JSONEq(t, `{\"email\":\"baz@ory.sh\",\"email_verify\":\"baz@ory.sh\",\"email_recovery\":\"baz@ory.sh\",\"email_creds\":\"baz@ory.sh\",\"unprotected\": \"bar\"}`, string(actual.Traits))\n\t\t})\n\n\t\tt.Run(\"case=should not update protected traits without option\", func(t *testing.T) {\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(\"email-updatetraits-1@ory.sh\", \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\n\t\t\terr := reg.IdentityManager().UpdateTraits(\n\t\t\t\tt.Context(), original.ID, newTraits(\"email-updatetraits-2@ory.sh\", \"\"))\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Equal(t, identity.ErrProtectedFieldModified, errors.Cause(err))\n\n\t\t\tfromStore, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), original.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\t// As UpdateTraits takes only the ID as a parameter it cannot update the identity in place.\n\t\t\t// That is why we only check the identity in the store.\n\t\t\tcheckExtensionFields(fromStore, \"email-updatetraits-1@ory.sh\")(t)\n\t\t})\n\n\t\tt.Run(\"case=should always update updated_at field\", func(t *testing.T) {\n\t\t\toriginal := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\toriginal.Traits = newTraits(\"email-updatetraits-3@ory.sh\", \"\")\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\n\t\t\ttime.Sleep(time.Millisecond)\n\n\t\t\trequire.NoError(t, reg.IdentityManager().UpdateTraits(\n\t\t\t\tt.Context(), original.ID, newTraits(\"email-updatetraits-4@ory.sh\", \"\"),\n\t\t\t\tidentity.ManagerAllowWriteProtectedTraits))\n\n\t\t\tupdated, err := reg.IdentityPool().GetIdentity(t.Context(), original.ID, identity.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.NotEqual(t, original.UpdatedAt, updated.UpdatedAt, \"UpdatedAt field should be updated\")\n\t\t})\n\t})\n\n\tt.Run(\"method=RefreshAvailableAAL\", func(t *testing.T) {\n\t\tvar cases []struct {\n\t\t\tCredentials []identity.Credentials `json:\"credentials\"`\n\t\t\tDescription string                 `json:\"description\"`\n\t\t\tExpected    string                 `json:\"expected\"`\n\t\t}\n\t\trequire.NoError(t, json.Unmarshal(refreshAALStubs, &cases))\n\n\t\tfor k, tc := range cases {\n\t\t\tt.Run(\"case=\"+tc.Description, func(t *testing.T) {\n\t\t\t\temail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\tid := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\tid.Traits = identity.Traits(`{\"email\":\"` + email + `\"}`)\n\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), id))\n\t\t\t\tassert.EqualValues(t, identity.NoAuthenticatorAssuranceLevel, id.InternalAvailableAAL.String)\n\n\t\t\t\tfor _, c := range tc.Credentials {\n\t\t\t\t\tfor k := range c.Identifiers {\n\t\t\t\t\t\tswitch c.Identifiers[k] {\n\t\t\t\t\t\tcase \"{email}\":\n\t\t\t\t\t\t\tc.Identifiers[k] = email\n\t\t\t\t\t\tcase \"{id}\":\n\t\t\t\t\t\t\tc.Identifiers[k] = id.ID.String()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tid.SetCredentials(c.Type, c)\n\t\t\t\t}\n\n\t\t\t\t// We use the privileged pool here because we don't want to refresh AAL here but in the code below.\n\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(t.Context(), id))\n\n\t\t\t\texpand := identity.ExpandNothing\n\t\t\t\tif k%2 == 1 { // expand every other test case to test if RefreshAvailableAAL behaves correctly\n\t\t\t\t\texpand = identity.ExpandCredentials\n\t\t\t\t}\n\n\t\t\t\tactual, err := reg.IdentityPool().GetIdentity(t.Context(), id.ID, expand)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, reg.IdentityManager().RefreshAvailableAAL(t.Context(), actual))\n\t\t\t\tassert.NotEmpty(t, actual.Credentials)\n\t\t\t\tassert.EqualValues(t, tc.Expected, actual.InternalAvailableAAL.String)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"method=ConflictingIdentity\", func(t *testing.T) {\n\t\tconflicOnIdentifier := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\tconflicOnIdentifier.Traits = identity.Traits(`{\"email\":\"conflict-on-identifier@example.com\"}`)\n\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), conflicOnIdentifier))\n\n\t\tconflicOnVerifiableAddress := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\tconflicOnVerifiableAddress.Traits = identity.Traits(`{\"email\":\"user-va@example.com\", \"email_verify\":\"conflict-on-va@example.com\"}`)\n\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), conflicOnVerifiableAddress))\n\n\t\tconflicOnRecoveryAddress := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\tconflicOnRecoveryAddress.Traits = identity.Traits(`{\"email\":\"user-ra@example.com\", \"email_recovery\":\"conflict-on-ra@example.com\"}`)\n\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), conflicOnRecoveryAddress))\n\n\t\tt.Run(\"case=returns not found if no conflict\", func(t *testing.T) {\n\t\t\tfound, foundConflictAddress, addressType, err := reg.IdentityManager().ConflictingIdentity(t.Context(), &identity.Identity{\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {Identifiers: []string{\"no-conflict@example.com\"}},\n\t\t\t\t},\n\t\t\t})\n\t\t\tassert.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\tassert.Nil(t, found)\n\t\t\tassert.Empty(t, foundConflictAddress)\n\t\t\tassert.Empty(t, addressType)\n\t\t})\n\n\t\tt.Run(\"case=conflict on identifier\", func(t *testing.T) {\n\t\t\tfound, foundConflictAddress, addressType, err := reg.IdentityManager().ConflictingIdentity(t.Context(), &identity.Identity{\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {Identifiers: []string{\"conflict-on-identifier@example.com\"}},\n\t\t\t\t},\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, conflicOnIdentifier.ID, found.ID)\n\t\t\tassert.Equal(t, \"conflict-on-identifier@example.com\", foundConflictAddress)\n\t\t\tassert.EqualValues(t, string(identity.CredentialsTypePassword), addressType)\n\t\t})\n\n\t\tt.Run(\"case=conflict on verifiable address\", func(t *testing.T) {\n\t\t\tfound, foundConflictAddress, addressType, err := reg.IdentityManager().ConflictingIdentity(t.Context(), &identity.Identity{\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{{\n\t\t\t\t\tValue: \"conflict-on-va@example.com\",\n\t\t\t\t\tVia:   \"email\",\n\t\t\t\t}},\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, conflicOnVerifiableAddress.ID, found.ID)\n\t\t\tassert.Equal(t, \"conflict-on-va@example.com\", foundConflictAddress)\n\t\t\tassert.Equal(t, \"email\", addressType)\n\t\t})\n\t\tt.Run(\"case=conflict on recovery address\", func(t *testing.T) {\n\t\t\tfound, foundConflictAddress, addressType, err := reg.IdentityManager().ConflictingIdentity(t.Context(), &identity.Identity{\n\t\t\t\tRecoveryAddresses: []identity.RecoveryAddress{{\n\t\t\t\t\tValue: \"conflict-on-ra@example.com\",\n\t\t\t\t\tVia:   \"email\",\n\t\t\t\t}},\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, conflicOnRecoveryAddress.ID, found.ID)\n\t\t\tassert.Equal(t, \"conflict-on-ra@example.com\", foundConflictAddress)\n\t\t\tassert.Equal(t, \"email\", addressType)\n\t\t})\n\t})\n}\n\nfunc TestManagerNoDefaultNamedSchema(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]interface{}{\n\t\tconfig.ViperKeyDefaultIdentitySchemaID: \"user_v0\",\n\t\tconfig.ViperKeyIdentitySchemas: config.Schemas{\n\t\t\t{ID: \"user_v0\", URL: \"file://./stub/manager.schema.json\"},\n\t\t},\n\t\tconfig.ViperKeyPublicBaseURL: \"https://www.ory.sh/\",\n\t}))\n\n\tt.Run(\"case=should create identity with default schema\", func(t *testing.T) {\n\t\tstateChangedAt := sqlxx.NullTime(time.Now().UTC())\n\t\toriginal := &identity.Identity{\n\t\t\tSchemaID:       \"\",\n\t\t\tTraits:         []byte(identity.Traits(`{\"email\":\"foo@ory.sh\"}`)),\n\t\t\tState:          identity.StateActive,\n\t\t\tStateChangedAt: &stateChangedAt,\n\t\t}\n\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), original))\n\t})\n}\n"
  },
  {
    "path": "identity/pool.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/crdbx\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\nfunc NewUpdateIdentityOptions(opts []UpdateIdentityModifier) UpdateIdentityOptions {\n\tvar o UpdateIdentityOptions\n\tfor _, opt := range opts {\n\t\topt(&o)\n\t}\n\treturn o\n}\n\n// DiffAgainst instructs UpdateIdentity to attempt a minimal update of the\n// identity's data in the database by computing a diff against `existing` and\n// only updating what is necessary, rather than bulk-replacing everything. Use\n// with caution. If `existing` is different from what is stored in the database\n// at the time of the update, the results are undefined. An error is returned if\n// `existing` has a mismatching IdentityID or NID.\nfunc DiffAgainst(existing *Identity) UpdateIdentityModifier {\n\treturn func(o *UpdateIdentityOptions) {\n\t\to.fromDatabase = existing\n\t}\n}\n\nfunc (o UpdateIdentityOptions) FromDatabase() *Identity {\n\treturn o.fromDatabase\n}\n\ntype (\n\tListIdentityParameters struct {\n\t\tExpand                       Expandables\n\t\tIdsFilter                    []uuid.UUID\n\t\tCredentialsIdentifier        string\n\t\tCredentialsIdentifierSimilar string\n\t\tDeclassifyCredentials        []CredentialsType\n\t\tKeySetPagination             []keysetpagination.Option\n\t\tOrganizationID               uuid.UUID\n\t\tConsistencyLevel             crdbx.ConsistencyLevel\n\t\tStatementTransformer         func(string) string\n\n\t\t// DEPRECATED\n\t\tPagePagination *x.Page\n\t}\n\n\tUpdateIdentityModifier func(*UpdateIdentityOptions)\n\tUpdateIdentityOptions  struct {\n\t\tfromDatabase *Identity\n\t}\n\n\tPool interface {\n\t\t// ListIdentities lists all identities in the store given the page and itemsPerPage.\n\t\tListIdentities(ctx context.Context, params ListIdentityParameters) ([]Identity, *keysetpagination.Paginator, error)\n\n\t\t// CountIdentities counts the number of identities in the store.\n\t\tCountIdentities(ctx context.Context) (int64, error)\n\n\t\t// GetIdentity returns an identity by its id. Will return an error if the identity does not exist or backend\n\t\t// connectivity is broken.\n\t\tGetIdentity(context.Context, uuid.UUID, sqlxx.Expandables) (*Identity, error)\n\n\t\t// FindVerifiableAddressByValue returns a matching address or sql.ErrNoRows if no address could be found.\n\t\tFindVerifiableAddressByValue(ctx context.Context, via, address string) (*VerifiableAddress, error)\n\n\t\t// FindRecoveryAddressByValue returns a matching address or sql.ErrNoRows if no address could be found.\n\t\tFindRecoveryAddressByValue(ctx context.Context, via, address string) (*RecoveryAddress, error)\n\n\t\t// FindAllRecoveryAddressesForIdentityByRecoveryAddressValue finds all recovery addresses for an identity if at least one of its recovery addresses matches the provided value.\n\t\tFindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx context.Context, anyRecoveryAddress string) ([]RecoveryAddress, error)\n\t}\n\n\tPoolProvider interface {\n\t\tIdentityPool() Pool\n\t}\n\n\tPrivilegedPoolProvider interface {\n\t\tPrivilegedIdentityPool() PrivilegedPool\n\t}\n\n\tPrivilegedPool interface {\n\t\tPool\n\n\t\t// FindByCredentialsIdentifier returns an identity by querying for it's credential identifiers.\n\t\tFindByCredentialsIdentifier(ctx context.Context, ct CredentialsType, match string) (*Identity, *Credentials, error)\n\n\t\t// DeleteIdentity removes an identity by its id. Will return an error\n\t\t// if identity does not exists, or backend connectivity is broken.\n\t\tDeleteIdentity(context.Context, uuid.UUID) error\n\n\t\t// DeleteIdentities removes identities by its id. Will return an error\n\t\t// if any identity does not exists, or backend connectivity is broken.\n\t\tDeleteIdentities(context.Context, []uuid.UUID) error\n\n\t\t// UpdateVerifiableAddress updates an identity's verifiable address.\n\t\tUpdateVerifiableAddress(ctx context.Context, address *VerifiableAddress, updateColumns ...string) error\n\n\t\t// CreateIdentity creates an identity. It is capable of setting credentials without encoding. Will return an error\n\t\t// if identity exists, backend connectivity is broken, or trait validation fails.\n\t\tCreateIdentity(context.Context, *Identity) error\n\n\t\t// CreateIdentities creates multiple identities. It is capable of setting credentials without encoding. Will return an error\n\t\t// if identity exists, backend connectivity is broken, or trait validation fails.\n\t\tCreateIdentities(context.Context, ...*Identity) error\n\n\t\t// UpdateIdentity updates an identity including its confidential / privileged / protected data.\n\t\tUpdateIdentity(context.Context, *Identity, ...UpdateIdentityModifier) error\n\n\t\t// UpdateIdentityColumns updates targeted columns of an identity.\n\t\tUpdateIdentityColumns(ctx context.Context, i *Identity, columns ...string) error\n\n\t\t// GetIdentityConfidential returns the identity including it's raw credentials.\n\t\t//\n\t\t// This should only be used internally. Please be aware that this method uses HydrateIdentityAssociations\n\t\t// internally, which must not be executed as part of a transaction.\n\t\tGetIdentityConfidential(context.Context, uuid.UUID) (*Identity, error)\n\n\t\t// ListVerifiableAddresses lists all tracked verifiable addresses, regardless of whether they are already verified\n\t\t// or not.\n\t\tListVerifiableAddresses(ctx context.Context, page, itemsPerPage int) ([]VerifiableAddress, error)\n\n\t\t// ListRecoveryAddresses lists all tracked recovery addresses.\n\t\tListRecoveryAddresses(ctx context.Context, page, itemsPerPage int) ([]RecoveryAddress, error)\n\n\t\t// HydrateIdentityAssociations hydrates the associations of an identity.\n\t\t//\n\t\t// Please be aware that this method must not be called within a transaction if more than one element is expanded.\n\t\t// It may error with \"conn busy\" otherwise.\n\t\tHydrateIdentityAssociations(ctx context.Context, i *Identity, expandables Expandables) error\n\n\t\t// InjectTraitsSchemaURL sets the identity's traits JSON schema URL from the schema's ID.\n\t\tInjectTraitsSchemaURL(ctx context.Context, i *Identity) error\n\n\t\t// FindIdentityByCredentialIdentifier returns an identity by matching the identifier to any of the identity's credentials.\n\t\tFindIdentityByCredentialIdentifier(ctx context.Context, identifier string, caseSensitive bool, expandables Expandables) (*Identity, error)\n\n\t\t// FindIdentityByWebauthnUserHandle returns an identity matching a webauthn user handle.\n\t\tFindIdentityByWebauthnUserHandle(ctx context.Context, userHandle []byte) (*Identity, error)\n\n\t\t// FindIdentityByCredentialsIdentifier returns an identity by its external ID.\n\t\tFindIdentityByExternalID(ctx context.Context, externalID string, expand sqlxx.Expandables) (*Identity, error)\n\t}\n)\n\nfunc (p ListIdentityParameters) TransformStatement(statement string) string {\n\tif p.StatementTransformer != nil {\n\t\treturn p.StatementTransformer(statement)\n\t}\n\treturn statement\n}\n"
  },
  {
    "path": "identity/registry.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"net/url\"\n)\n\ntype Registry interface {\n\tIdentityPool() Pool\n}\n\ntype Configuration interface {\n\tSelfAdminURL() *url.URL\n\tDefaultIdentityTraitsSchemaURL() *url.URL\n}\n"
  },
  {
    "path": "identity/stub/aal.json",
    "content": "[\n  {\n    \"description\": \"password is available aal1\",\n    \"expected\": \"aal1\",\n    \"credentials\": [\n      {\n        \"type\": \"password\",\n        \"identifiers\": [\n          \"{email}\"\n        ],\n        \"config\": {\n          \"hashed_password\": \"$2a$fake\"\n        }\n      }\n    ]\n  },\n  {\n    \"description\": \"password without identifier is no credential and ergo aal0\",\n    \"expected\": \"aal0\",\n    \"credentials\": [\n      {\n        \"type\": \"password\",\n        \"config\": {\n          \"hashed_password\": \"$2a$fake\"\n        }\n      }\n    ]\n  },\n  {\n    \"description\": \"second factor totp returns available aal2 even if no password is set\",\n    \"expected\": \"aal2\",\n    \"credentials\": [\n      {\n        \"type\": \"totp\",\n        \"config\": {\n          \"totp_url\": \"totp://\"\n        }\n      }\n    ]\n  },\n  {\n    \"description\": \"second factor totp returns aal0 if totp credentials is not set up\",\n    \"expected\": \"aal0\",\n    \"credentials\": [\n      {\n        \"type\": \"totp\",\n        \"identifiers\": [\n          \"{email}\"\n        ],\n        \"config\": {}\n      }\n    ]\n  },\n  {\n    \"description\": \"password and totp is also available aal2\",\n    \"expected\": \"aal1\",\n    \"credentials\": [\n      {\n        \"type\": \"password\",\n        \"identifiers\": [\n          \"{email}\"\n        ],\n        \"config\": {\n          \"hashed_password\": \"$2a$fake\"\n        }\n      },\n      {\n        \"type\": \"totp\",\n        \"identifiers\": [\n          \"{email}\"\n        ],\n        \"config\": {}\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "identity/stub/expand.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"additionalProperties\": false,\n  \"properties\": {\n    \"traits\": {\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"email\": {\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"totp\": {\n                \"account_name\": true\n              }\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          },\n          \"title\": \"Email address\",\n          \"type\": \"string\",\n          \"maxLength\": 320\n        },\n        \"name\": {\n          \"minLength\": 1,\n          \"title\": \"Name\",\n          \"type\": \"string\",\n          \"maxLength\": 256\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"name\"\n      ],\n      \"type\": \"object\"\n    }\n  },\n  \"title\": \"Person\",\n  \"type\": \"object\"\n}\n"
  },
  {
    "path": "identity/stub/extension/credentials/code-phone-email.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          },\n          \"webauthn\": {\n            \"identifier\": true\n          },\n          \"code\": {\n            \"identifier\": true,\n            \"via\": \"email\"\n          }\n        }\n      }\n    },\n    \"email2\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          },\n          \"webauthn\": {\n            \"identifier\": true\n          },\n          \"code\": {\n            \"identifier\": true,\n            \"via\": \"email\"\n          }\n        }\n      }\n    },\n    \"email3\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          },\n          \"webauthn\": {\n            \"identifier\": true\n          },\n          \"code\": {\n            \"identifier\": true,\n            \"via\": \"email\"\n          }\n        }\n      }\n    },\n    \"phone\": {\n      \"type\": \"string\",\n      \"format\": \"tel\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          },\n          \"webauthn\": {\n            \"identifier\": true\n          },\n          \"code\": {\n            \"identifier\": true,\n            \"via\": \"sms\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/credentials/code.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          },\n          \"code\": {\n            \"identifier\": true,\n            \"via\": \"email\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/credentials/email.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/identity.email.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"title\": \"E-Mail\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          },\n          \"webauthn\": {\n            \"identifier\": true\n          },\n          \"totp\": {\n            \"account_name\": true\n          },\n          \"code\": {\n            \"identifier\": true,\n            \"via\": \"email\"\n          },\n          \"passkey\": {\n            \"display_name\": true\n          }\n        },\n        \"recovery\": {\n          \"via\": \"email\"\n        },\n        \"verification\": {\n          \"via\": \"email\"\n        }\n      },\n      \"maxLength\": 320\n    }\n  },\n  \"required\": [\n    \"email\"\n  ],\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "identity/stub/extension/credentials/multi.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"emails\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\",\n        \"format\": \"email\",\n        \"ory.sh/kratos\": {\n          \"credentials\": {\n            \"password\": {\n              \"identifier\": true\n            },\n            \"webauthn\": {\n              \"identifier\": true\n            }\n          }\n        }\n      }\n    },\n    \"username\": {\n      \"type\": \"string\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/credentials/schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "identity/stub/extension/credentials/webauthn.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          },\n          \"webauthn\": {\n            \"identifier\": true\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/recovery/email.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"emails\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\",\n        \"ory.sh/kratos\": {\n          \"recovery\": {\n            \"via\": \"email\"\n          }\n        }\n      }\n    },\n    \"username\": {\n      \"type\": \"string\",\n      \"ory.sh/kratos\": {\n        \"recovery\": {\n          \"via\": \"email\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/recovery/schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"emails\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\",\n        \"ory.sh/kratos\": {\n          \"recovery\": {\n            \"via\": \"email\"\n          }\n        }\n      }\n    },\n    \"username\": {\n      \"type\": \"string\",\n      \"ory.sh/kratos\": {\n        \"recovery\": {\n          \"via\": \"email\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/recovery/sms.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"telephoneNumber\": {\n      \"type\": \"string\",\n      \"format\": \"tel\",\n      \"title\": \"Telephone Number\",\n      \"minLength\": 3,\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          },\n          \"code\": {\n            \"identifier\": true,\n            \"via\": \"sms\"\n          }\n        },\n        \"verification\": {\n          \"via\": \"sms\"\n        },\n        \"recovery\": {\n          \"via\": \"sms\"\n        }\n      }\n    }\n  },\n  \"required\": [\n    \"telephoneNumber\"\n  ],\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "identity/stub/extension/verify/email.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"emails\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\",\n        \"ory.sh/kratos\": {\n          \"verification\": {\n            \"via\": \"email\"\n          }\n        }\n      }\n    },\n    \"username\": {\n      \"type\": \"string\",\n      \"ory.sh/kratos\": {\n        \"verification\": {\n          \"via\": \"email\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/verify/legacy-email-missing-format.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"ory.sh/kratos\": {\n        \"verification\": {\n          \"via\": \"email\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/verify/missing-format.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"phone\": {\n      \"type\": \"string\",\n      \"ory.sh/kratos\": {\n        \"verification\": {\n          \"via\": \"sms\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/verify/no-validate.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"phone\": {\n      \"type\": \"string\",\n      \"format\": \"noformat\",\n      \"ory.sh/kratos\": {\n        \"verification\": {\n          \"via\": \"sms\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension/verify/phone.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"phones\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\",\n        \"format\": \"tel\",\n        \"ory.sh/kratos\": {\n          \"verification\": {\n            \"via\": \"sms\"\n          }\n        }\n      }\n    },\n    \"username\": {\n      \"type\": \"string\",\n      \"format\": \"tel\",\n      \"ory.sh/kratos\": {\n        \"verification\": {\n          \"via\": \"sms\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/extension.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"names\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"ory.sh/kratos\": {\n              \"credentials\": {\n                \"password\": {\n                  \"identifier\": true\n                }\n              }\n            }\n          }\n        },\n        \"age\": {\n          \"description\": \"Age in years which must be equal to or greater than zero.\",\n          \"type\": \"integer\",\n          \"minimum\": 1\n        }\n      },\n      \"required\": [\n        \"email\"\n      ]\n    }\n  },\n  \"required\": [\n    \"traits\"\n  ],\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "identity/stub/handler/customer.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/customer.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"address\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/handler/employee.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/employee.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"department\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/handler/multiple_emails.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/customer.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"emails\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"ory.sh/kratos\": {\n              \"credentials\": {\n                \"password\": {\n                  \"identifier\": true\n                }\n              },\n              \"verification\": {\n                \"via\": \"email\"\n              },\n              \"recovery\": {\n                \"via\": \"email\"\n              }\n            }\n          }\n        },\n        \"username\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/identity-2.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/localhost-ref.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"$ref\": \"http://192.168.178.1:1234/\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "identity/stub/manager.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"email_creds\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"email_webauthn\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"email_verify\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"email_recovery\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"unprotected\": {\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "identity/stub/webauthn/v0.json",
    "content": "{\n  \"credentials\": [\n    {\n      \"id\": \"HQ4LaIJ9NiqS1r0CQpWY+K0gMvhOq4yk5BHuO/YlitcurSpBK7weDXOvBcuN4lvn6DAmjGfmj/J/6bpOmtdT8Q==\",\n      \"public_key\": \"pQECAyYgASFYILAYFLoH1T8bQMSbPrNBCMMS5U7OFWRwv2U+GkAoiBADIlggBv+8ni7XVZYBB8ufMbP/d9fDxbmOkVVHOgcJifnoOR4=\",\n      \"attestation_type\": \"none\",\n      \"authenticator\": {\n        \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n        \"sign_count\": 4,\n        \"clone_warning\": false\n      },\n      \"display_name\": \"asdf\",\n      \"added_at\": \"2022-02-28T16:40:39Z\"\n    },\n    {\n      \"id\": \"1Q4LaIJ9NiqS1r0CQpWY+K0gMvhOq4yk5BHuO/YlitcurSpBK7weDXOvBcuN4lvn6DAmjGfmj/J/6bpOmtdT8Q==\",\n      \"public_key\": \"pQECAyYgASFYILAYFLoH1T8bQMSbPrNBCMMS5U7OFWRwv2U+GkAoiBADIlggBv+8ni7XVZYBB8ufMbP/d9fDxbmOkVVHOgcJifnoOR4=\",\n      \"attestation_type\": \"none\",\n      \"authenticator\": {\n        \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n        \"sign_count\": 4,\n        \"clone_warning\": false\n      },\n      \"display_name\": \"asdf\",\n      \"added_at\": \"2022-02-28T16:40:39Z\"\n    }\n  ]\n}\n"
  },
  {
    "path": "identity/stub/webauthn/v1.json",
    "content": "{\n  \"credentials\": [\n    {\n      \"id\": \"HQ4LaIJ9NiqS1r0CQpWY+K0gMvhOq4yk5BHuO/YlitcurSpBK7weDXOvBcuN4lvn6DAmjGfmj/J/6bpOmtdT8Q==\",\n      \"public_key\": \"pQECAyYgASFYILAYFLoH1T8bQMSbPrNBCMMS5U7OFWRwv2U+GkAoiBADIlggBv+8ni7XVZYBB8ufMbP/d9fDxbmOkVVHOgcJifnoOR4=\",\n      \"attestation_type\": \"none\",\n      \"authenticator\": {\n        \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n        \"sign_count\": 4,\n        \"clone_warning\": false\n      },\n      \"display_name\": \"asdf\",\n      \"added_at\": \"2022-02-28T16:40:39Z\",\n      \"is_passwordless\": true\n    }\n  ],\n  \"user_handle\":\"2gZaSs9fTEeGmsBlC4gfgg==\"\n}\n"
  },
  {
    "path": "identity/test/pool.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence\"\n\tidpersistence \"github.com/ory/kratos/persistence/sql/identity\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/crdbx\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/randx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\n// assertContainsValues is a test helper that checks if a slice contains expected values and doesn't contain unexpected values.\nfunc assertContainsValues(t *testing.T, actual []string, shouldContain, shouldNotContain []string) {\n\tt.Helper()\n\tfor _, expected := range shouldContain {\n\t\tassert.Contains(t, actual, expected)\n\t}\n\tfor _, notExpected := range shouldNotContain {\n\t\tassert.NotContains(t, actual, notExpected)\n\t}\n}\n\nfunc TestPool(ctx context.Context, p persistence.Persister, m *identity.Manager, dbname string) func(t *testing.T) {\n\treturn func(t *testing.T) {\n\t\tnid, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\texampleServerURL := urlx.ParseOrPanic(\"http://example.com\")\n\t\texpandSchema := schema.Schema{\n\t\t\tID:     \"expandSchema\",\n\t\t\tURL:    urlx.ParseOrPanic(\"file://./stub/expand.schema.json\"),\n\t\t\tRawURL: \"file://./stub/expand.schema.json\",\n\t\t}\n\t\tdefaultSchema := schema.Schema{\n\t\t\tID:     config.DefaultIdentityTraitsSchemaID,\n\t\t\tURL:    urlx.ParseOrPanic(\"file://./stub/identity.schema.json\"),\n\t\t\tRawURL: \"file://./stub/identity.schema.json\",\n\t\t}\n\t\taltSchema := schema.Schema{\n\t\t\tID:     \"altSchema\",\n\t\t\tURL:    urlx.ParseOrPanic(\"file://./stub/identity-2.schema.json\"),\n\t\t\tRawURL: \"file://./stub/identity-2.schema.json\",\n\t\t}\n\t\tmultipleEmailsSchema := schema.Schema{\n\t\t\tID:     \"multiple_emails\",\n\t\t\tURL:    urlx.ParseOrPanic(\"file://./stub/handler/multiple_emails.schema.json\"),\n\t\t\tRawURL: \"file://./stub/identity-2.schema.json\",\n\t\t}\n\t\tctx := contextx.WithConfigValues(ctx, map[string]any{\n\t\t\tconfig.ViperKeyPublicBaseURL: exampleServerURL.String(),\n\t\t\tconfig.ViperKeyIdentitySchemas: []config.Schema{\n\t\t\t\t{\n\t\t\t\t\tID:  altSchema.ID,\n\t\t\t\t\tURL: altSchema.RawURL,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tID:  defaultSchema.ID,\n\t\t\t\t\tURL: defaultSchema.RawURL,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tID:  expandSchema.ID,\n\t\t\t\t\tURL: expandSchema.RawURL,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tID:  multipleEmailsSchema.ID,\n\t\t\t\t\tURL: multipleEmailsSchema.RawURL,\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\n\t\tt.Run(\"case=expand\", func(t *testing.T) {\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"DELETE FROM identities WHERE nid = ?\", nid).Exec())\n\t\t\tt.Cleanup(func() {\n\t\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"DELETE FROM identities WHERE nid = ?\", nid).Exec())\n\t\t\t})\n\n\t\t\texpected := identity.NewIdentity(expandSchema.ID)\n\t\t\texpected.Traits = identity.Traits(`{\"email\":\"` + uuid.Must(uuid.NewV4()).String() + \"@ory.sh\" + `\",\"name\":\"john doe\"}`)\n\t\t\trequire.NoError(t, m.ValidateIdentity(ctx, expected, new(identity.ManagerOptions)))\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\trequire.NoError(t, identity.UpgradeCredentials(expected))\n\n\t\t\tassert.NotEmpty(t, expected.RecoveryAddresses)\n\t\t\tassert.NotEmpty(t, expected.VerifiableAddresses)\n\t\t\tassert.NotEmpty(t, expected.Credentials)\n\t\t\tassert.NotEqual(t, uuid.Nil, expected.RecoveryAddresses[0].ID)\n\t\t\tassert.NotEqual(t, uuid.Nil, expected.VerifiableAddresses[0].ID)\n\n\t\t\trunner := func(t *testing.T, expand sqlxx.Expandables, cb func(*testing.T, *identity.Identity)) {\n\t\t\t\tassertion := func(t *testing.T, actual *identity.Identity) {\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected, actual, []string{\n\t\t\t\t\t\t\"verifiable_addresses\", \"recovery_addresses\", \"updated_at\", \"created_at\", \"credentials\", \"state_changed_at\",\n\t\t\t\t\t})\n\t\t\t\t\tcb(t, actual)\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"find\", func(t *testing.T) {\n\t\t\t\t\tactual, err := p.GetIdentity(ctx, expected.ID, expand)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassertion(t, actual)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"list/page-pagination\", func(t *testing.T) {\n\t\t\t\t\tactual, _, err := p.ListIdentities(ctx, identity.ListIdentityParameters{Expand: expand, PagePagination: &x.Page{Page: 0, ItemsPerPage: 10}})\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, actual, 1)\n\t\t\t\t\tassertion(t, &actual[0])\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"list/token-pagination\", func(t *testing.T) {\n\t\t\t\t\tactual, next, err := p.ListIdentities(ctx, identity.ListIdentityParameters{Expand: expand, KeySetPagination: []keysetpagination.Option{keysetpagination.WithSize(10)}})\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, actual, 1)\n\t\t\t\t\trequire.True(t, next.IsLast())\n\t\t\t\t\tassertion(t, &actual[0])\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tt.Run(\"expand=nothing\", func(t *testing.T) {\n\t\t\t\trunner(t, identity.ExpandNothing, func(t *testing.T, actual *identity.Identity) {\n\t\t\t\t\tassert.Empty(t, actual.RecoveryAddresses)\n\t\t\t\t\tassert.Empty(t, actual.VerifiableAddresses)\n\t\t\t\t\tassert.Empty(t, actual.Credentials)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"expand=credentials\", func(t *testing.T) {\n\t\t\t\trunner(t, identity.ExpandCredentials, func(t *testing.T, actual *identity.Identity) {\n\t\t\t\t\tassert.Empty(t, actual.RecoveryAddresses)\n\t\t\t\t\tassert.Empty(t, actual.VerifiableAddresses)\n\n\t\t\t\t\trequire.Len(t, actual.Credentials, 2)\n\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.Credentials[identity.CredentialsTypePassword], actual.Credentials[identity.CredentialsTypePassword], []string{\"updated_at\", \"created_at\"})\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.Credentials[identity.CredentialsTypeWebAuthn], actual.Credentials[identity.CredentialsTypeWebAuthn], []string{\"updated_at\", \"created_at\"})\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"expand=recovery address\", func(t *testing.T) {\n\t\t\t\trunner(t, sqlxx.Expandables{identity.ExpandFieldRecoveryAddresses}, func(t *testing.T, actual *identity.Identity) {\n\t\t\t\t\tassert.Empty(t, actual.Credentials)\n\t\t\t\t\tassert.Empty(t, actual.VerifiableAddresses)\n\n\t\t\t\t\trequire.Len(t, actual.RecoveryAddresses, 1)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.RecoveryAddresses, actual.RecoveryAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"expand=verification address\", func(t *testing.T) {\n\t\t\t\trunner(t, sqlxx.Expandables{identity.ExpandFieldVerifiableAddresses}, func(t *testing.T, actual *identity.Identity) {\n\t\t\t\t\tassert.Empty(t, actual.Credentials)\n\t\t\t\t\tassert.Empty(t, actual.RecoveryAddresses)\n\n\t\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 1)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.VerifiableAddresses, actual.VerifiableAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"expand=default\", func(t *testing.T) {\n\t\t\t\trunner(t, identity.ExpandDefault, func(t *testing.T, actual *identity.Identity) {\n\t\t\t\t\tassert.Empty(t, actual.Credentials)\n\n\t\t\t\t\trequire.Len(t, actual.RecoveryAddresses, 1)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.RecoveryAddresses, actual.RecoveryAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\n\t\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 1)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.VerifiableAddresses, actual.VerifiableAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"expand=everything\", func(t *testing.T) {\n\t\t\t\trunner(t, identity.ExpandEverything, func(t *testing.T, actual *identity.Identity) {\n\t\t\t\t\trequire.Len(t, actual.Credentials, 2)\n\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.Credentials[identity.CredentialsTypePassword], actual.Credentials[identity.CredentialsTypePassword], []string{\"updated_at\", \"created_at\"})\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.Credentials[identity.CredentialsTypeWebAuthn], actual.Credentials[identity.CredentialsTypeWebAuthn], []string{\"updated_at\", \"created_at\"})\n\n\t\t\t\t\trequire.Len(t, actual.RecoveryAddresses, 1)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.RecoveryAddresses, actual.RecoveryAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\n\t\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 1)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.VerifiableAddresses, actual.VerifiableAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"expand=load\", func(t *testing.T) {\n\t\t\t\trunner(t, identity.ExpandNothing, func(t *testing.T, actual *identity.Identity) {\n\t\t\t\t\trequire.NoError(t, p.HydrateIdentityAssociations(ctx, actual, identity.ExpandEverything))\n\n\t\t\t\t\trequire.Len(t, actual.Credentials, 2)\n\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.Credentials[identity.CredentialsTypePassword], actual.Credentials[identity.CredentialsTypePassword], []string{\"updated_at\", \"created_at\"})\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.Credentials[identity.CredentialsTypeWebAuthn], actual.Credentials[identity.CredentialsTypeWebAuthn], []string{\"updated_at\", \"created_at\"})\n\n\t\t\t\t\trequire.Len(t, actual.RecoveryAddresses, 1)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.RecoveryAddresses, actual.RecoveryAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\n\t\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 1)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.VerifiableAddresses, actual.VerifiableAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"confidential\", func(t *testing.T) {\n\t\t\t\t// confidential is like expand=all\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, expected.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassertx.EqualAsJSONExcept(t, expected, actual, []string{\n\t\t\t\t\t\"verifiable_addresses\", \"recovery_addresses\", \"updated_at\", \"created_at\", \"credentials\", \"state_changed_at\",\n\t\t\t\t})\n\t\t\t\trequire.Len(t, actual.Credentials, 2)\n\n\t\t\t\tassertx.EqualAsJSONExcept(t, expected.Credentials[identity.CredentialsTypePassword], actual.Credentials[identity.CredentialsTypePassword], []string{\"updated_at\", \"created_at\"})\n\t\t\t\tassertx.EqualAsJSONExcept(t, expected.Credentials[identity.CredentialsTypeWebAuthn], actual.Credentials[identity.CredentialsTypeWebAuthn], []string{\"updated_at\", \"created_at\"})\n\n\t\t\t\trequire.Len(t, actual.RecoveryAddresses, 1)\n\t\t\t\tassertx.EqualAsJSONExcept(t, expected.RecoveryAddresses, actual.RecoveryAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\n\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 1)\n\t\t\t\tassertx.EqualAsJSONExcept(t, expected.VerifiableAddresses, actual.VerifiableAddresses, []string{\"0.updated_at\", \"0.created_at\"})\n\t\t\t})\n\t\t})\n\n\t\tvar createdIDs []uuid.UUID\n\t\tpasswordIdentity := func(schemaID string, credentialsID string) *identity.Identity {\n\t\t\ti := identity.NewIdentity(schemaID)\n\t\t\ti.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\tType: identity.CredentialsTypePassword, Identifiers: []string{credentialsID},\n\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t})\n\t\t\treturn i\n\t\t}\n\n\t\toidcIdentity := func(schemaID string, credentialsID string) *identity.Identity {\n\t\t\ti := identity.NewIdentity(schemaID)\n\t\t\ti.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\tType: identity.CredentialsTypeOIDC, Identifiers: []string{credentialsID},\n\t\t\t\tConfig: sqlxx.JSONRawMessage(`{}`),\n\t\t\t})\n\t\t\treturn i\n\t\t}\n\n\t\tassertEqual := func(t *testing.T, expected, actual *identity.Identity) {\n\t\t\tassert.Empty(t, actual.Credentials)\n\t\t\trequire.Equal(t, expected.Traits, actual.Traits)\n\t\t\trequire.Equal(t, expected.ID, actual.ID)\n\t\t}\n\n\t\tt.Run(\"case=should create and set missing ID\", func(t *testing.T) {\n\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\ti.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\tType: identity.CredentialsTypeOIDC, Identifiers: []string{x.NewUUID().String()},\n\t\t\t\tConfig: sqlxx.JSONRawMessage(`{}`),\n\t\t\t})\n\t\t\ti.ID = uuid.Nil\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, i))\n\t\t\tassert.NotEqual(t, uuid.Nil, i.ID)\n\t\t\tassert.Equal(t, nid, i.NID)\n\t\t\tcreatedIDs = append(createdIDs, i.ID)\n\n\t\t\tcount, err := p.CountIdentities(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, int64(1), count)\n\n\t\t\tt.Run(\"different network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\tcount, err := p.CountIdentities(ctx)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, int64(0), count)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=create with default values\", func(t *testing.T) {\n\t\t\texpected := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\tactual, err := p.GetIdentity(ctx, expected.ID, identity.ExpandDefault)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, expected.ID, actual.ID)\n\t\t\tassert.Equal(t, config.DefaultIdentityTraitsSchemaID, actual.SchemaID)\n\t\t\tassert.Equal(t, defaultSchema.SchemaURL(exampleServerURL).String(), actual.SchemaURL)\n\t\t\tassertEqual(t, expected, actual)\n\n\t\t\tcount, err := p.CountIdentities(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, 2, count)\n\n\t\t\tt.Run(\"different network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetIdentity(ctx, expected.ID, identity.ExpandDefault)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\tcount, err := p.CountIdentities(ctx)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, int64(0), count)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should set external ID\", func(t *testing.T) {\n\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\ti.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\tType: identity.CredentialsTypeOIDC, Identifiers: []string{x.NewUUID().String()},\n\t\t\t\tConfig: sqlxx.JSONRawMessage(`{}`),\n\t\t\t})\n\t\t\ti.ID = uuid.Nil\n\t\t\texternalID := sqlxx.NullString(\"external-id-\" + randx.MustString(10, randx.AlphaNum))\n\t\t\ti.ExternalID = externalID\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, i))\n\t\t\tassert.NotEqual(t, uuid.Nil, i.ID)\n\t\t\tassert.Equal(t, nid, i.NID)\n\t\t\tassert.Equal(t, externalID, i.ExternalID)\n\t\t\tcreatedIDs = append(createdIDs, i.ID)\n\n\t\t\tt.Run(\"find by external ID\", func(t *testing.T) {\n\t\t\t\ti2, err := p.FindIdentityByExternalID(ctx, externalID.String(), identity.ExpandEverything)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, i.ID, i2.ID)\n\t\t\t})\n\n\t\t\tt.Run(\"must be unique\", func(t *testing.T) {\n\t\t\t\ti2 := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\ti2.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\t\tType: identity.CredentialsTypeOIDC, Identifiers: []string{x.NewUUID().String()},\n\t\t\t\t\tConfig: sqlxx.JSONRawMessage(`{}`),\n\t\t\t\t})\n\t\t\t\ti2.ExternalID = externalID\n\n\t\t\t\terr := new(herodot.DefaultError)\n\t\t\t\trequire.ErrorAs(t, p.CreateIdentity(ctx, i2), &err)\n\t\t\t\tassert.Equal(t, http.StatusConflict, err.CodeField)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=create with null AAL\", func(t *testing.T) {\n\t\t\texpected := passwordIdentity(\"\", \"id-\"+uuid.Must(uuid.NewV4()).String())\n\t\t\texpected.InternalAvailableAAL.Valid = false\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\tactual, err := p.GetIdentity(ctx, expected.ID, identity.ExpandDefault)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.False(t, actual.InternalAvailableAAL.Valid)\n\t\t})\n\n\t\tt.Run(\"suite=create multiple identities\", func(t *testing.T) {\n\t\t\tt.Run(\"create multiple identities\", func(t *testing.T) {\n\t\t\t\tidentities := make([]*identity.Identity, 100)\n\t\t\t\tfor i := range identities {\n\t\t\t\t\tidentities[i] = NewTestIdentity(4, \"persister-create-multiple\", i)\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentities(ctx, identities...))\n\t\t\t\tcreatedAt := time.Now().UTC()\n\n\t\t\t\tfor _, id := range identities {\n\t\t\t\t\tidFromDB, err := p.GetIdentity(ctx, id.ID, identity.ExpandEverything)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tcredFromDB := idFromDB.Credentials[identity.CredentialsTypePassword]\n\t\t\t\t\tassert.Equal(t, id.ID, idFromDB.ID)\n\t\t\t\t\tassert.Equal(t, id.SchemaID, idFromDB.SchemaID)\n\t\t\t\t\tassert.Equal(t, id.SchemaURL, idFromDB.SchemaURL)\n\t\t\t\t\tassert.Equal(t, id.State, idFromDB.State)\n\n\t\t\t\t\t// We test that the values are plausible in the handler test already.\n\t\t\t\t\tassert.Equal(t, len(id.VerifiableAddresses), len(idFromDB.VerifiableAddresses))\n\t\t\t\t\tassert.Equal(t, len(id.RecoveryAddresses), len(idFromDB.RecoveryAddresses))\n\n\t\t\t\t\tassert.Equal(t, id.Credentials[\"password\"].Identifiers, credFromDB.Identifiers)\n\t\t\t\t\tassert.WithinDuration(t, createdAt, credFromDB.CreatedAt, time.Minute)\n\t\t\t\t\tassert.WithinDuration(t, createdAt, credFromDB.UpdatedAt, time.Minute)\n\t\t\t\t\t// because of mysql precision\n\t\t\t\t\tassert.WithinDuration(t, id.CreatedAt, idFromDB.CreatedAt, time.Second)\n\t\t\t\t\tassert.WithinDuration(t, id.UpdatedAt, idFromDB.UpdatedAt, time.Second)\n\n\t\t\t\t\trequire.NoError(t, p.DeleteIdentity(ctx, id.ID))\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"create exactly the non-conflicting ones\", func(t *testing.T) {\n\t\t\t\tidentities := make([]*identity.Identity, 100)\n\t\t\t\tfor i := range identities {\n\t\t\t\t\tidentities[i] = NewTestIdentity(4, \"persister-create-multiple-2\", i%60)\n\t\t\t\t}\n\t\t\t\terr := p.CreateIdentities(ctx, identities...)\n\t\t\t\tif dbname == \"mysql\" {\n\t\t\t\t\t// partial inserts are not supported on mysql\n\t\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrUniqueViolation)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tcreatedAt := time.Now().UTC()\n\n\t\t\t\terrWithCtx := new(identity.CreateIdentitiesError)\n\t\t\t\trequire.ErrorAsf(t, err, &errWithCtx, \"%#v\", err)\n\n\t\t\t\tfor _, id := range identities[:60] {\n\t\t\t\t\trequire.NotZero(t, id.ID)\n\n\t\t\t\t\tidFromDB, err := p.GetIdentity(ctx, id.ID, identity.ExpandEverything)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tcredFromDB := idFromDB.Credentials[identity.CredentialsTypePassword]\n\t\t\t\t\tassert.Equal(t, id.ID, idFromDB.ID)\n\t\t\t\t\tassert.Equal(t, id.SchemaID, idFromDB.SchemaID)\n\t\t\t\t\tassert.Equal(t, id.SchemaURL, idFromDB.SchemaURL)\n\t\t\t\t\tassert.Equal(t, id.State, idFromDB.State)\n\n\t\t\t\t\t// We test that the values are plausible in the handler test already.\n\t\t\t\t\tassert.Equal(t, len(id.VerifiableAddresses), len(idFromDB.VerifiableAddresses))\n\t\t\t\t\tassert.Equal(t, len(id.RecoveryAddresses), len(idFromDB.RecoveryAddresses))\n\n\t\t\t\t\tassert.Equal(t, id.Credentials[\"password\"].Identifiers, credFromDB.Identifiers)\n\t\t\t\t\tassert.WithinDuration(t, createdAt, credFromDB.CreatedAt, time.Minute)\n\t\t\t\t\tassert.WithinDuration(t, createdAt, credFromDB.UpdatedAt, time.Minute)\n\t\t\t\t\t// because of mysql precision\n\t\t\t\t\tassert.WithinDuration(t, id.CreatedAt, idFromDB.CreatedAt, time.Second)\n\t\t\t\t\tassert.WithinDuration(t, id.UpdatedAt, idFromDB.UpdatedAt, time.Second)\n\n\t\t\t\t\trequire.NoError(t, p.DeleteIdentity(ctx, id.ID))\n\t\t\t\t}\n\n\t\t\t\tfor _, id := range identities[60:] {\n\t\t\t\t\tfailed := errWithCtx.Find(id)\n\t\t\t\t\tassert.NotNil(t, failed)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=external_id conflict in batch create returns partial error\", func(t *testing.T) {\n\t\t\t// Regression test for https://github.com/ory-corp/cloud/issues/10580\n\t\t\t// When all identities in a batch conflict on external_id, CreateIdentities\n\t\t\t// should return a CreateIdentitiesError, not a raw SQL error.\n\t\t\t// On Postgres, the bug causes: ERROR: syntax error at or near \")\" (SQLSTATE 42601)\n\t\t\t// because DeleteIdentities is called with an empty ID slice, generating IN ().\n\n\t\t\t// Step 1: Create two identities with external_ids.\n\t\t\tfirst := make([]*identity.Identity, 2)\n\t\t\tfor i := range first {\n\t\t\t\tfirst[i] = NewTestIdentity(1, \"ext-id-conflict-first\", i)\n\t\t\t\tfirst[i].ExternalID = sqlxx.NullString(fmt.Sprintf(\"ext-conflict-pool-%d\", i))\n\t\t\t}\n\t\t\trequire.NoError(t, p.CreateIdentities(ctx, first...))\n\t\t\tfor _, id := range first {\n\t\t\t\tcreatedIDs = append(createdIDs, id.ID)\n\t\t\t}\n\n\t\t\t// Step 2: Create new identities with different traits but same external_ids.\n\t\t\tsecond := make([]*identity.Identity, 2)\n\t\t\tfor i := range second {\n\t\t\t\tsecond[i] = NewTestIdentity(1, \"ext-id-conflict-second\", i)\n\t\t\t\tsecond[i].ExternalID = sqlxx.NullString(fmt.Sprintf(\"ext-conflict-pool-%d\", i))\n\t\t\t}\n\t\t\terr := p.CreateIdentities(ctx, second...)\n\t\t\tif dbname == \"mysql\" {\n\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrUniqueViolation)\n\t\t\t\treturn\n\t\t\t}\n\t\t\terrWithCtx := new(identity.CreateIdentitiesError)\n\t\t\trequire.ErrorAsf(t, err, &errWithCtx, \"expected CreateIdentitiesError, got: %#v\", err)\n\t\t\tfor _, id := range second {\n\t\t\t\tassert.NotNil(t, errWithCtx.Find(id), \"expected identity %s to be in the error\", id.ID)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should error when the identity ID does not exist\", func(t *testing.T) {\n\t\t\t_, err := p.GetIdentity(ctx, uuid.UUID{}, identity.ExpandNothing)\n\t\t\trequire.Error(t, err)\n\n\t\t\t_, err = p.GetIdentity(ctx, x.NewUUID(), identity.ExpandNothing)\n\t\t\trequire.Error(t, err)\n\n\t\t\t_, err = p.GetIdentityConfidential(ctx, x.NewUUID())\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tt.Run(\"case=run migrations when fetching credentials\", func(t *testing.T) {\n\t\t\texpected := func(schemaID string, credentialsID string) *identity.Identity {\n\t\t\t\ti := identity.NewIdentity(schemaID)\n\t\t\t\ti.SetCredentials(identity.CredentialsTypeWebAuthn, identity.Credentials{\n\t\t\t\t\tType: identity.CredentialsTypeWebAuthn, Identifiers: []string{credentialsID},\n\t\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\"credentials\":[{}]}`),\n\t\t\t\t})\n\t\t\t\treturn i\n\t\t\t}(altSchema.ID, \"webauthn\")\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\tactual, err := p.GetIdentityConfidential(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tc := actual.GetCredentialsOr(identity.CredentialsTypeWebAuthn, &identity.Credentials{})\n\t\t\tassert.True(t, gjson.GetBytes(c.Config, \"credentials.0.is_passwordless\").Exists())\n\t\t\tassert.Equal(t, base64.StdEncoding.EncodeToString(expected.ID[:]), gjson.GetBytes(c.Config, \"user_handle\").String())\n\t\t})\n\n\t\tt.Run(\"case=create and keep set values\", func(t *testing.T) {\n\t\t\texpected := passwordIdentity(altSchema.ID, x.NewUUID().String())\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\tactual, err := p.GetIdentity(ctx, expected.ID, identity.ExpandDefault)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, altSchema.ID, actual.SchemaID)\n\t\t\tassert.Equal(t, altSchema.SchemaURL(exampleServerURL).String(), actual.SchemaURL)\n\t\t\tassertEqual(t, expected, actual)\n\n\t\t\tactual, err = p.GetIdentityConfidential(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, expected.Traits, actual.Traits)\n\t\t\trequire.Equal(t, expected.ID, actual.ID)\n\n\t\t\tassert.NotEmpty(t, actual.Credentials)\n\t\t\tassert.NotEmpty(t, expected.Credentials)\n\n\t\t\tfor m, expected := range expected.Credentials {\n\t\t\t\tassert.Equal(t, expected.ID, actual.Credentials[m].ID)\n\t\t\t\tassert.JSONEq(t, string(expected.Config), string(actual.Credentials[m].Config))\n\t\t\t\tassert.Equal(t, expected.Identifiers, actual.Credentials[m].Identifiers)\n\t\t\t\tassert.Equal(t, expected.Type, actual.Credentials[m].Type)\n\t\t\t}\n\n\t\t\tt.Run(\"different network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetIdentity(ctx, expected.ID, identity.ExpandNothing)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t_, err = p.GetIdentityConfidential(ctx, expected.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=fail on duplicate credential identifiers if type is password\", func(t *testing.T) {\n\t\t\temail := randx.MustString(16, randx.AlphaLowerNum) + \"@bar.com\"\n\t\t\tinitial := passwordIdentity(\"\", email)\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\tfor _, transform := range []func(string) string{\n\t\t\t\tstrings.ToLower,\n\t\t\t\tfunc(s string) string { return s[:1] + strings.ToUpper(s[1:2]) + s[2:] },\n\t\t\t\tstrings.ToUpper,\n\t\t\t\tfunc(s string) string { left, right, _ := strings.Cut(s, \"@\"); return left + \"@\" + strings.Title(right) },\n\t\t\t} {\n\t\t\t\tids := transform(email)\n\t\t\t\texpected := passwordIdentity(\"\", ids)\n\t\t\t\terr := p.CreateIdentity(ctx, expected)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrUniqueViolation, \"%+v\", err)\n\n\t\t\t\t_, err = p.GetIdentity(ctx, expected.ID, identity.ExpandNothing)\n\t\t\t\trequire.Error(t, err)\n\n\t\t\t\tt.Run(\"succeeds on different network/id=\"+ids, func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\texpected := passwordIdentity(\"\", ids)\n\t\t\t\t\terr := p.CreateIdentity(ctx, expected)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t_, err = p.GetIdentity(ctx, expected.ID, identity.ExpandNothing)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=fail on duplicate credential identifiers if type is oidc\", func(t *testing.T) {\n\t\t\toidcID := randx.MustString(16, randx.AlphaLowerNum)\n\t\t\tinitial := oidcIdentity(\"\", oidcID)\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\texpected := oidcIdentity(\"\", oidcID)\n\t\t\trequire.Error(t, p.CreateIdentity(ctx, expected))\n\n\t\t\t_, err := p.GetIdentity(ctx, expected.ID, identity.ExpandNothing)\n\t\t\trequire.Error(t, err)\n\n\t\t\tsecond := oidcIdentity(\"\", strings.ToUpper(oidcID))\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, second), \"should work because oidc is not case-sensitive\")\n\t\t\tcreatedIDs = append(createdIDs, second.ID)\n\n\t\t\tt.Run(\"succeeds on different network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\texpected := oidcIdentity(\"\", oidcID)\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\n\t\t\t\t_, err = p.GetIdentity(ctx, expected.ID, identity.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=create with invalid traits data\", func(t *testing.T) {\n\t\t\texpected := oidcIdentity(\"\", x.NewUUID().String())\n\t\t\texpected.Traits = identity.Traits(`{\"bar\":123}`) // bar should be a string\n\t\t\terr := p.CreateIdentity(ctx, expected)\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Contains(t, fmt.Sprintf(\"%+v\", err.Error()), \"malformed\")\n\t\t})\n\n\t\tt.Run(\"case=get classified credentials\", func(t *testing.T) {\n\t\t\tinitial := oidcIdentity(\"\", x.NewUUID().String())\n\t\t\tinitial.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\tType: identity.CredentialsTypeOIDC, Identifiers: []string{\"aylmao-oidc\"},\n\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\"ay\":\"lmao\"}`),\n\t\t\t})\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\tinitial, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotEqual(t, uuid.Nil, initial.ID)\n\t\t\trequire.NotEmpty(t, initial.Credentials)\n\n\t\t\tt.Run(\"fails on different network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=update an identity and set credentials\", func(t *testing.T) {\n\t\t\tinitial := oidcIdentity(\"\", x.NewUUID().String())\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\tassert.Equal(t, config.DefaultIdentityTraitsSchemaID, initial.SchemaID)\n\t\t\tassert.Equal(t, defaultSchema.SchemaURL(exampleServerURL).String(), initial.SchemaURL)\n\n\t\t\texpected := initial.CopyWithoutCredentials()\n\t\t\texpected.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{\"ignore-me\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"oh\":\"nono\"}`),\n\t\t\t})\n\t\t\texpected.Traits = identity.Traits(`{\"update\":\"me\"}`)\n\t\t\texpected.SchemaID = altSchema.ID\n\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, expected))\n\n\t\t\tactual, err := p.GetIdentityConfidential(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, altSchema.ID, actual.SchemaID)\n\t\t\tassert.Equal(t, altSchema.SchemaURL(exampleServerURL).String(), actual.SchemaURL)\n\t\t\tassert.NotEmpty(t, actual.Credentials[identity.CredentialsTypePassword])\n\t\t\tassert.Empty(t, actual.Credentials[identity.CredentialsTypeOIDC])\n\n\t\t\tassert.Equal(t, expected.Credentials[identity.CredentialsTypeOIDC], actual.Credentials[identity.CredentialsTypeOIDC])\n\n\t\t\tt.Run(\"fails on different network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\trequire.ErrorIs(t, p.UpdateIdentity(ctx, expected), sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=fail to update because validation fails\", func(t *testing.T) {\n\t\t\tinitial := oidcIdentity(\"\", x.NewUUID().String())\n\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\tinitial.Traits = identity.Traits(`{\"bar\":123}`)\n\t\t\terr := p.UpdateIdentity(ctx, initial)\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"malformed\")\n\t\t})\n\n\t\tt.Run(\"case=update an identity column\", func(t *testing.T) {\n\t\t\tinitial := oidcIdentity(\"\", x.NewUUID().String())\n\t\t\tinitial.InternalAvailableAAL = identity.NewNullableAuthenticatorAssuranceLevel(identity.NoAuthenticatorAssuranceLevel)\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\tinitial.InternalAvailableAAL = identity.NewNullableAuthenticatorAssuranceLevel(identity.AuthenticatorAssuranceLevel1)\n\t\t\tinitial.State = identity.StateInactive\n\t\t\trequire.NoError(t, p.UpdateIdentityColumns(ctx, initial, \"available_aal\"))\n\n\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, string(identity.AuthenticatorAssuranceLevel1), actual.InternalAvailableAAL.String)\n\t\t\tassert.Equal(t, identity.StateActive, actual.State, \"the state remains unchanged\")\n\t\t})\n\n\t\tt.Run(\"case=should fail to insert identity because credentials from traits exist\", func(t *testing.T) {\n\t\t\temail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\tfirst := passwordIdentity(\"\", email)\n\t\t\tfirst.Traits = identity.Traits(`{}`)\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, first))\n\t\t\tcreatedIDs = append(createdIDs, first.ID)\n\n\t\t\tsecond := passwordIdentity(\"\", email)\n\t\t\trequire.Error(t, p.CreateIdentity(ctx, second))\n\n\t\t\tt.Run(\"passes on different network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, second))\n\t\t\t})\n\n\t\t\tt.Run(\"case=should fail to update identity because credentials exist\", func(t *testing.T) {\n\t\t\t\tfirst := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tfirst.Traits = identity.Traits(`{}`)\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, first))\n\t\t\t\tcreatedIDs = append(createdIDs, first.ID)\n\n\t\t\t\tc := first.Credentials[identity.CredentialsTypePassword]\n\t\t\t\tc.Identifiers = []string{email}\n\t\t\t\tfirst.Credentials[identity.CredentialsTypePassword] = c\n\t\t\t\trequire.Error(t, p.UpdateIdentity(ctx, first))\n\n\t\t\t\tt.Run(\"passes on different network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\tfirst := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\t\tfirst.Traits = identity.Traits(`{}`)\n\t\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, first))\n\n\t\t\t\t\tc := first.Credentials[identity.CredentialsTypePassword]\n\t\t\t\t\tc.Identifiers = []string{\"test-identity@ory.sh\"}\n\t\t\t\t\tfirst.Credentials[identity.CredentialsTypePassword] = c\n\t\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, first))\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should succeed to update credentials from traits\", func(t *testing.T) {\n\t\t\texpected := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\texpected.Traits = identity.Traits(`{\"email\":\"update-test-identity@ory.sh\"}`)\n\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, expected))\n\n\t\t\tactual, err := p.GetIdentityConfidential(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, expected.Credentials[identity.CredentialsTypePassword].Identifiers, actual.Credentials[identity.CredentialsTypePassword].Identifiers)\n\t\t})\n\n\t\tt.Run(\"case=delete an identity\", func(t *testing.T) {\n\t\t\texpected := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\n\t\t\tt.Run(\"fails on different network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\trequire.ErrorIs(t, p.DeleteIdentity(ctx, expected.ID), sqlcon.ErrNoRows)\n\n\t\t\t\tp = testhelpers.ExistingNetwork(t, p, nid)\n\t\t\t\t_, err := p.GetIdentity(ctx, expected.ID, identity.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t})\n\n\t\t\trequire.NoError(t, p.DeleteIdentity(ctx, expected.ID))\n\n\t\t\t_, err := p.GetIdentity(ctx, expected.ID, identity.ExpandNothing)\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tt.Run(\"case=create with empty credentials config\", func(t *testing.T) {\n\t\t\t// This test covers a case where the config value of a credentials setting is empty. This causes\n\t\t\t// issues with postgres' json field.\n\t\t\texpected := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\texpected.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{\"id-missing-creds-config\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(``),\n\t\t\t})\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\t\t})\n\n\t\tt.Run(\"case=list\", func(t *testing.T) {\n\t\t\tis, _, err := p.ListIdentities(ctx, identity.ListIdentityParameters{Expand: identity.ExpandDefault})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotEmpty(t, is)\n\t\t\trequire.Len(t, is, len(createdIDs))\n\t\t\tfor _, id := range createdIDs {\n\t\t\t\tvar found bool\n\t\t\t\tfor _, i := range is {\n\t\t\t\t\tif i.ID == id {\n\t\t\t\t\t\texpected, err := p.GetIdentity(ctx, id, identity.ExpandDefault)\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\tassertx.EqualAsJSON(t, expected, i)\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trequire.True(t, found, id)\n\t\t\t}\n\n\t\t\tt.Run(\"no results on other network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\tis, _, err := p.ListIdentities(ctx, identity.ListIdentityParameters{Expand: identity.ExpandDefault})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, is, 0)\n\t\t\t})\n\n\t\t\tt.Run(\"list some using ids filter\", func(t *testing.T) {\n\t\t\t\tfilterIds := createdIDs[:2]\n\n\t\t\t\tis, _, err := p.ListIdentities(ctx, identity.ListIdentityParameters{Expand: identity.ExpandDefault, IdsFilter: filterIds})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, is, len(filterIds))\n\t\t\t})\n\n\t\t\tt.Run(\"eventually consistent\", func(t *testing.T) {\n\t\t\t\tif dbname != \"cockroach\" {\n\t\t\t\t\tt.Skipf(\"Test only works with cockroachdb\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tid := x.NewUUID().String()\n\t\t\t\tanother := oidcIdentity(\"\", id)\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, another))\n\t\t\t\tcreatedIDs = append(createdIDs, another.ID)\n\n\t\t\t\tis, _, err := p.ListIdentities(ctx, identity.ListIdentityParameters{\n\t\t\t\t\tExpand:           identity.ExpandDefault,\n\t\t\t\t\tKeySetPagination: []keysetpagination.Option{keysetpagination.WithSize(25)},\n\t\t\t\t\tConsistencyLevel: crdbx.ConsistencyLevelStrong,\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, is, len(createdIDs))\n\n\t\t\t\tvar results []identity.Identity\n\t\t\t\t// It takes about 4.8 seconds to replicate the data.\n\t\t\t\tfor i := 0; i < 8; i++ {\n\t\t\t\t\ttime.Sleep(time.Second)\n\n\t\t\t\t\t// The error here is explicitly ignored because the table / schema might not yet be replicated.\n\t\t\t\t\t// This can lead to \"ERROR: cached plan must not change result type (SQLSTATE 0A000)\" whih is caused\n\t\t\t\t\t// because the prepared query exist but the schema is not yet replicated.\n\t\t\t\t\tis, _, _ := p.ListIdentities(ctx, identity.ListIdentityParameters{\n\t\t\t\t\t\tExpand:           identity.ExpandEverything,\n\t\t\t\t\t\tKeySetPagination: []keysetpagination.Option{keysetpagination.WithSize(25)},\n\t\t\t\t\t\tConsistencyLevel: crdbx.ConsistencyLevelEventual,\n\t\t\t\t\t})\n\n\t\t\t\t\tif len(is) == len(createdIDs) {\n\t\t\t\t\t\tresults = is\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trequire.NotZero(t, len(results))\n\t\t\t\trequire.Len(t, results, len(createdIDs), \"Could not find all identities after 8 seconds\")\n\n\t\t\t\tvar found bool\n\t\t\t\tfor _, i := range results {\n\t\t\t\t\tif i.ID == another.ID {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trequire.True(t, found, id, \"Unable to find created identity in eventually consistent results.\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=find identity by its credentials identifier\", func(t *testing.T) {\n\t\t\tvar expectedIdentifiers []string\n\t\t\tvar expectedIdentities []*identity.Identity\n\n\t\t\tfor _, c := range []identity.CredentialsType{\n\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\tidentity.CredentialsTypeWebAuthn,\n\t\t\t\tidentity.CredentialsTypeOIDC,\n\t\t\t} {\n\t\t\t\tidentityIdentifier := fmt.Sprintf(\"find-identity-by-identifier-%s@ory.sh\", c)\n\t\t\t\texpected := identity.NewIdentity(\"\")\n\t\t\t\texpected.SetCredentials(c, identity.Credentials{Type: c, Identifiers: []string{identityIdentifier}, Config: sqlxx.JSONRawMessage(`{}`)})\n\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\t\t\t\texpectedIdentifiers = append(expectedIdentifiers, identityIdentifier)\n\t\t\t\texpectedIdentities = append(expectedIdentities, expected)\n\t\t\t}\n\n\t\t\tcreate := identity.NewIdentity(\"\")\n\t\t\tcreate.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{Type: identity.CredentialsTypePassword, Identifiers: []string{\"find-identity-by-identifier-common@ory.sh\"}, Config: sqlxx.JSONRawMessage(`{}`)})\n\t\t\tcreate.SetCredentials(identity.CredentialsTypeWebAuthn, identity.Credentials{Type: identity.CredentialsTypeWebAuthn, Identifiers: []string{\"find-identity-by-identifier-common@ory.sh\"}, Config: sqlxx.JSONRawMessage(`{}`)})\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, create))\n\n\t\t\tactual, _, err := p.ListIdentities(ctx, identity.ListIdentityParameters{\n\t\t\t\tExpand: identity.ExpandEverything,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Greater(t, len(actual), 0)\n\n\t\t\tfor c, ct := range []identity.CredentialsType{\n\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\tidentity.CredentialsTypeWebAuthn,\n\t\t\t} {\n\t\t\t\tt.Run(ct.String(), func(t *testing.T) {\n\t\t\t\t\tactual, _, err := p.ListIdentities(ctx, identity.ListIdentityParameters{\n\t\t\t\t\t\t// Match is normalized\n\t\t\t\t\t\tCredentialsIdentifier: expectedIdentifiers[c],\n\t\t\t\t\t})\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\texpected := expectedIdentities[c]\n\t\t\t\t\trequire.Len(t, actual, 1)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected, actual[0], []string{\"credentials.config\", \"created_at\", \"updated_at\", \"state_changed_at\"})\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tt.Run(\"similarity search\", func(t *testing.T) {\n\t\t\t\tactual, _, err := p.ListIdentities(ctx, identity.ListIdentityParameters{\n\t\t\t\t\tCredentialsIdentifierSimilar: \"find-identity-by-identifier\",\n\t\t\t\t\tExpand:                       identity.ExpandCredentials,\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, actual, 4) // webauthn, common, password, oidc\n\n\t\t\touter:\n\t\t\t\tfor _, e := range append(expectedIdentities[:2], create) {\n\t\t\t\t\tfor _, a := range actual {\n\t\t\t\t\t\tif e.ID == a.ID {\n\t\t\t\t\t\t\tassertx.EqualAsJSONExcept(t, e, a, []string{\"credentials.config\", \"created_at\", \"updated_at\", \"state_changed_at\"})\n\t\t\t\t\t\t\tcontinue outer\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tactualCredentials := make([]map[identity.CredentialsType]identity.Credentials, len(actual))\n\t\t\t\t\tfor k, a := range actual {\n\t\t\t\t\t\tactualCredentials[k] = a.Credentials\n\t\t\t\t\t}\n\t\t\t\t\tt.Fatalf(\"expected identity %+v not found in actual result set %+v\", e.Credentials, actualCredentials)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"find by OIDC identifier\", func(t *testing.T) {\n\t\t\t\tactual, next, err := p.ListIdentities(ctx, identity.ListIdentityParameters{\n\t\t\t\t\tCredentialsIdentifier: \"find-identity-by-identifier-oidc@ory.sh\",\n\t\t\t\t\tExpand:                identity.ExpandEverything,\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, actual, 1)\n\t\t\t\tassert.True(t, next.IsLast())\n\t\t\t})\n\n\t\t\tt.Run(\"one result set even if multiple matches\", func(t *testing.T) {\n\t\t\t\tactual, next, err := p.ListIdentities(ctx, identity.ListIdentityParameters{\n\t\t\t\t\tCredentialsIdentifier: \"find-identity-by-identifier-common@ory.sh\",\n\t\t\t\t\tExpand:                identity.ExpandEverything,\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, actual, 1)\n\t\t\t\tassert.True(t, next.IsLast())\n\t\t\t})\n\n\t\t\tt.Run(\"non existing identifier\", func(t *testing.T) {\n\t\t\t\tactual, next, err := p.ListIdentities(ctx, identity.ListIdentityParameters{\n\t\t\t\t\tCredentialsIdentifier: \"find-identity-by-identifier-non-existing@ory.sh\",\n\t\t\t\t\tExpand:                identity.ExpandEverything,\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, actual, 0)\n\t\t\t\tassert.True(t, next.IsLast())\n\t\t\t})\n\n\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t_, on := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\tactual, next, err := on.ListIdentities(ctx, identity.ListIdentityParameters{\n\t\t\t\t\tCredentialsIdentifier: expectedIdentifiers[0],\n\t\t\t\t\tExpand:                identity.ExpandEverything,\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, actual, 0)\n\t\t\t\tassert.True(t, next.IsLast())\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=find identity by its credentials type and identifier\", func(t *testing.T) {\n\t\t\temail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\texpected := passwordIdentity(\"\", email)\n\t\t\texpected.Traits = identity.Traits(`{}`)\n\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\tactual, creds, err := p.FindByCredentialsIdentifier(ctx, identity.CredentialsTypePassword, email)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.EqualValues(t, expected.Credentials[identity.CredentialsTypePassword].ID, creds.ID)\n\t\t\tassert.EqualValues(t, expected.Credentials[identity.CredentialsTypePassword].Identifiers, creds.Identifiers)\n\t\t\tassert.JSONEq(t, string(expected.Credentials[identity.CredentialsTypePassword].Config), string(creds.Config))\n\t\t\t// assert.EqualValues(t, expected.Credentials[CredentialsTypePassword].CreatedAt.Unix(), creds.CreatedAt.Unix())\n\t\t\t// assert.EqualValues(t, expected.Credentials[CredentialsTypePassword].UpdatedAt.Unix(), creds.UpdatedAt.Unix())\n\n\t\t\trequire.Equal(t, expected.Traits, actual.Traits)\n\t\t\trequire.Equal(t, expected.ID, actual.ID)\n\t\t\trequire.NotNil(t, actual.Credentials[identity.CredentialsTypePassword])\n\t\t\tassert.EqualValues(t, expected.Credentials[identity.CredentialsTypePassword].ID, actual.Credentials[identity.CredentialsTypePassword].ID)\n\t\t\tassert.EqualValues(t, expected.Credentials[identity.CredentialsTypePassword].Identifiers, actual.Credentials[identity.CredentialsTypePassword].Identifiers)\n\t\t\tassert.JSONEq(t, string(expected.Credentials[identity.CredentialsTypePassword].Config), string(actual.Credentials[identity.CredentialsTypePassword].Config))\n\n\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, _, err := p.FindByCredentialsIdentifier(ctx, identity.CredentialsTypePassword, email)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=find identity by its webauthn credential user handle\", func(t *testing.T) {\n\t\t\texpected := identity.NewIdentity(\"\")\n\t\t\texpected.SetCredentials(identity.CredentialsTypeWebAuthn, identity.Credentials{\n\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\tIdentifiers: []string{\"find-webauth-user-handle-identifier@ory.sh\"},\n\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\n  \"credentials\": [\n    {\n      \"added_at\": \"2024-02-13T10:36:16Z\",\n      \"attestation_type\": \"none\",\n      \"authenticator\": {\n        \"aaguid\": \"+/wwBxVOTsyMC24CBVfXvQ==\",\n        \"clone_warning\": false,\n        \"sign_count\": 0\n      },\n      \"display_name\": \"Yubikey\",\n      \"id\": \"f2uGd/Bg1rGcGXtYp4MT4WcN+eA=\",\n      \"is_passwordless\": true,\n      \"public_key\": \"pQECAyYgASFYIBkNvUxvjdhuA36FworTmS/rxZR1I+NyRWBpoTYY/R+CIlggw+gFFrFoEi+rS82zq7+tDHAukBUJcFpQ7Z3NLBZH5vk=\"\n    }\n  ],\n  \"user_handle\": \"51z80nYJTSGmr6UBe1VGLg==\"\n}`),\n\t\t\t})\n\t\t\texpected.Traits = identity.Traits(`{}`)\n\t\t\tuserHandle := x.Must(base64.StdEncoding.DecodeString(\"51z80nYJTSGmr6UBe1VGLg==\"))\n\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\tactual, err := p.FindIdentityByWebauthnUserHandle(ctx, userHandle)\n\t\t\trequire.NoError(t, err)\n\n\t\t\texpected.Credentials = nil\n\t\t\tassertEqual(t, expected, actual)\n\n\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err = p.FindIdentityByWebauthnUserHandle(ctx, userHandle)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=find identity only by credentials identifier\", func(t *testing.T) {\n\t\t\temail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\texpected := passwordIdentity(\"\", email)\n\t\t\texpected.Traits = identity.Traits(`{}`)\n\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\tactual, err := p.FindIdentityByCredentialIdentifier(ctx, strings.ToUpper(email), false, identity.ExpandDefault)\n\t\t\trequire.NoError(t, err)\n\n\t\t\texpected.Credentials = nil\n\t\t\tassertEqual(t, expected, actual)\n\n\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.FindIdentityByCredentialIdentifier(ctx, strings.ToUpper(email), false, identity.ExpandDefault)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=find identity only by credentials identifier case sensitive\", func(t *testing.T) {\n\t\t\temail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\texpected := passwordIdentity(\"\", email)\n\t\t\texpected.Traits = identity.Traits(`{}`)\n\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\t_, err := p.FindIdentityByCredentialIdentifier(ctx, strings.ToUpper(email), true, identity.ExpandDefault)\n\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\tactual, err := p.FindIdentityByCredentialIdentifier(ctx, email, true, identity.ExpandDefault)\n\t\t\trequire.NoError(t, err)\n\n\t\t\texpected.Credentials = nil\n\t\t\tassertEqual(t, expected, actual)\n\n\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.FindIdentityByCredentialIdentifier(ctx, email, true, identity.ExpandDefault)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=find identity by its credentials respects cases\", func(t *testing.T) {\n\t\t\tbaseEmail := randx.MustString(16, randx.AlphaLowerNum)\n\t\t\tcaseSensitive := baseEmail + \"@ory.sh\"\n\t\t\tcaseInsensitiveWithSpaces := \" \" + strings.ToUpper(baseEmail) + \"@ORY.sh \"\n\n\t\t\texpected := identity.NewIdentity(\"\")\n\t\t\tfor _, c := range []identity.CredentialsType{\n\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\tidentity.CredentialsTypeOIDC,\n\t\t\t\tidentity.CredentialsTypeTOTP,\n\t\t\t\tidentity.CredentialsTypeLookup,\n\t\t\t\tidentity.CredentialsTypeWebAuthn,\n\t\t\t} {\n\t\t\t\texpected.SetCredentials(c, identity.Credentials{Type: c, Identifiers: []string{caseSensitive}, Config: sqlxx.JSONRawMessage(`{}`)})\n\t\t\t}\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\tt.Run(\"case sensitive\", func(t *testing.T) {\n\t\t\t\tfor _, ct := range []identity.CredentialsType{\n\t\t\t\t\tidentity.CredentialsTypeOIDC,\n\t\t\t\t\tidentity.CredentialsTypeTOTP,\n\t\t\t\t\tidentity.CredentialsTypeLookup,\n\t\t\t\t} {\n\t\t\t\t\tt.Run(ct.String(), func(t *testing.T) {\n\t\t\t\t\t\t_, _, err := p.FindByCredentialsIdentifier(ctx, ct, caseInsensitiveWithSpaces)\n\t\t\t\t\t\trequire.Error(t, err)\n\n\t\t\t\t\t\tactual, creds, err := p.FindByCredentialsIdentifier(ctx, ct, caseSensitive)\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected.Credentials[ct], creds, []string{\"created_at\", \"updated_at\", \"id\"})\n\t\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected, actual, []string{\"created_at\", \"state_changed_at\", \"updated_at\", \"id\"})\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"case insensitive\", func(t *testing.T) {\n\t\t\t\tfor _, ct := range []identity.CredentialsType{\n\t\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\t\tidentity.CredentialsTypeWebAuthn,\n\t\t\t\t} {\n\t\t\t\t\tt.Run(ct.String(), func(t *testing.T) {\n\t\t\t\t\t\tfor _, cs := range []string{caseSensitive, caseInsensitiveWithSpaces} {\n\t\t\t\t\t\t\tactual, creds, err := p.FindByCredentialsIdentifier(ctx, ct, cs)\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\tec := expected.Credentials[ct]\n\t\t\t\t\t\t\tec.Identifiers = []string{strings.ToLower(caseSensitive)}\n\t\t\t\t\t\t\tassertx.EqualAsJSONExcept(t, ec, creds, []string{\"created_at\", \"updated_at\", \"id\", \"config.user_handle\", \"config.credentials\", \"version\"})\n\t\t\t\t\t\t\tassertx.EqualAsJSONExcept(t, expected, actual, []string{\"created_at\", \"state_changed_at\", \"updated_at\", \"id\"})\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\n\t\tt.Run(\"case=find identity by its credentials case insensitive\", func(t *testing.T) {\n\t\t\tidentifier := x.NewUUID().String()\n\t\t\texpected := passwordIdentity(\"\", strings.ToUpper(identifier))\n\t\t\texpected.Traits = identity.Traits(`{}`)\n\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected))\n\t\t\tcreatedIDs = append(createdIDs, expected.ID)\n\n\t\t\tactual, creds, err := p.FindByCredentialsIdentifier(ctx, identity.CredentialsTypePassword, identifier)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.EqualValues(t, expected.Credentials[identity.CredentialsTypePassword].ID, creds.ID)\n\t\t\tassert.EqualValues(t, []string{strings.ToLower(identifier)}, creds.Identifiers)\n\t\t\tassert.JSONEq(t, string(expected.Credentials[identity.CredentialsTypePassword].Config), string(creds.Config))\n\n\t\t\trequire.Equal(t, expected.Traits, actual.Traits)\n\t\t\trequire.Equal(t, expected.ID, actual.ID)\n\t\t\trequire.NotNil(t, actual.Credentials[identity.CredentialsTypePassword])\n\t\t\tassert.EqualValues(t, expected.Credentials[identity.CredentialsTypePassword].ID, actual.Credentials[identity.CredentialsTypePassword].ID)\n\t\t\tassert.EqualValues(t, []string{strings.ToLower(identifier)}, actual.Credentials[identity.CredentialsTypePassword].Identifiers)\n\t\t\tassert.JSONEq(t, string(expected.Credentials[identity.CredentialsTypePassword].Config), string(actual.Credentials[identity.CredentialsTypePassword].Config))\n\n\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, _, err := p.FindByCredentialsIdentifier(ctx, identity.CredentialsTypePassword, identifier)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"suite=verifiable-address\", func(t *testing.T) {\n\t\t\tcreateIdentityWithAddresses := func(t *testing.T, email string) identity.VerifiableAddress {\n\t\t\t\tvar i identity.Identity\n\t\t\t\trequire.NoError(t, faker.FakeData(&i))\n\n\t\t\t\taddress := identity.NewVerifiableEmailAddress(email, i.ID)\n\t\t\t\ti.VerifiableAddresses = append(i.VerifiableAddresses, *address)\n\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, &i))\n\t\t\t\treturn i.VerifiableAddresses[0]\n\t\t\t}\n\n\t\t\tt.Run(\"case=not found\", func(t *testing.T) {\n\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, \"does-not-exist\")\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\n\t\t\ttransform := func(k int, value string) string {\n\t\t\t\tswitch k % 5 {\n\t\t\t\tcase 0:\n\t\t\t\t\tvalue = strings.ToLower(value)\n\t\t\t\tcase 1:\n\t\t\t\t\tvalue = strings.ToUpper(value)\n\t\t\t\tcase 2:\n\t\t\t\t\tvalue = \" \" + value\n\t\t\t\tcase 3:\n\t\t\t\t\tvalue = value + \" \"\n\t\t\t\t}\n\t\t\t\treturn value\n\t\t\t}\n\n\t\t\tt.Run(\"case=create and find\", func(t *testing.T) {\n\t\t\t\taddresses := make([]identity.VerifiableAddress, 15)\n\t\t\t\tfor k := range addresses {\n\t\t\t\t\tvalue := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\t\taddresses[k] = createIdentityWithAddresses(t, transform(k, value))\n\t\t\t\t\trequire.NotEmpty(t, addresses[k].ID)\n\t\t\t\t}\n\n\t\t\t\tcompare := func(t *testing.T, expected, actual identity.VerifiableAddress) {\n\t\t\t\t\tactual.CreatedAt = actual.CreatedAt.UTC().Truncate(time.Hour * 24)\n\t\t\t\t\tactual.UpdatedAt = actual.UpdatedAt.UTC().Truncate(time.Hour * 24)\n\t\t\t\t\texpected.CreatedAt = expected.CreatedAt.UTC().Truncate(time.Hour * 24)\n\t\t\t\t\texpected.UpdatedAt = expected.UpdatedAt.UTC().Truncate(time.Hour * 24)\n\t\t\t\t\texpected.Value = strings.TrimSpace(strings.ToLower(expected.Value))\n\t\t\t\t\tassert.EqualValues(t, expected, actual)\n\t\t\t\t}\n\n\t\t\t\tfor k, expected := range addresses {\n\t\t\t\t\tt.Run(\"method=FindVerifiableAddressByValue\", func(t *testing.T) {\n\t\t\t\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\t\t\t\tactual, err := p.FindVerifiableAddressByValue(ctx, expected.Via, transform(k+1, expected.Value))\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\tcompare(t, expected, *actual)\n\n\t\t\t\t\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, expected.Via, transform(k+1, expected.Value))\n\t\t\t\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\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}\n\t\t\t})\n\n\t\t\tt.Run(\"case=update\", func(t *testing.T) {\n\t\t\t\taddress := createIdentityWithAddresses(t, \"verification.TestPersister.Update@ory.sh \")\n\n\t\t\t\taddress.Value = \"new-codE \"\n\t\t\t\trequire.NoError(t, p.UpdateVerifiableAddress(ctx, &address))\n\n\t\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\trequire.ErrorIs(t, p.UpdateVerifiableAddress(ctx, &address), sqlcon.ErrNoRows)\n\t\t\t\t})\n\n\t\t\t\tactual, err := p.FindVerifiableAddressByValue(ctx, address.Via, address.Value)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, \"new-code\", actual.Value)\n\t\t\t})\n\n\t\t\tt.Run(\"case=create and update and find\", func(t *testing.T) {\n\t\t\t\tvar i identity.Identity\n\t\t\t\trequire.NoError(t, faker.FakeData(&i))\n\n\t\t\t\taddress := identity.NewVerifiableEmailAddress(\"verification.TestPersister.Update-Identity@ory.sh\", i.ID)\n\t\t\t\ti.VerifiableAddresses = append(i.VerifiableAddresses, *address)\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, &i))\n\n\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, \"verification.TestPersister.Update-Identity@ory.sh\")\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, \"verification.TestPersister.Update-Identity@ory.sh\")\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\n\t\t\t\taddress = identity.NewVerifiableEmailAddress(\"verification.TestPersister.Update-Identity-next@ory.sh\", i.ID)\n\t\t\t\ti.VerifiableAddresses = []identity.VerifiableAddress{*address}\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, &i))\n\n\t\t\t\t_, err = p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, \"verification.TestPersister.Update-Identity@ory.sh\")\n\t\t\t\trequire.EqualError(t, err, sqlcon.ErrNoRows.Error())\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, \"verification.TestPersister.Update-Identity@ory.sh\")\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\n\t\t\t\tactual, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, \"verification.TestPersister.Update-Identity-next@ory.sh\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, identity.AddressTypeEmail, actual.Via)\n\t\t\t\tassert.Equal(t, \"verification.testpersister.update-identity-next@ory.sh\", actual.Value)\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, \"verification.TestPersister.Update-Identity-next@ory.sh\")\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=create and update and find case insensitive\", func(t *testing.T) {\n\t\t\t\tvar i identity.Identity\n\t\t\t\trequire.NoError(t, faker.FakeData(&i))\n\n\t\t\t\taddress := identity.NewVerifiableEmailAddress(\"verification.TestPersister.Update-Identity-case-insensitive@ory.sh\", i.ID)\n\t\t\t\ti.VerifiableAddresses = append(i.VerifiableAddresses, *address)\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, &i))\n\n\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"verification.TestPersister.Update-Identity-case-insensitive@ory.sh\"))\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"verification.TestPersister.Update-Identity-case-insensitive@ory.sh\"))\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\n\t\t\t\taddress = identity.NewVerifiableEmailAddress(\"verification.TestPersister.Update-Identity-case-insensitive-next@ory.sh\", i.ID)\n\t\t\t\ti.VerifiableAddresses = []identity.VerifiableAddress{*address}\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, &i))\n\n\t\t\t\t_, err = p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"verification.TestPersister.Update-Identity-case-insensitive@ory.sh\"))\n\t\t\t\trequire.EqualError(t, err, sqlcon.ErrNoRows.Error())\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"verification.TestPersister.Update-Identity-case-insensitive@ory.sh\"))\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\n\t\t\t\tactual, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"verification.TestPersister.Update-Identity-case-insensitive-next@ory.sh\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, identity.AddressTypeEmail, actual.Via)\n\t\t\t\tassert.Equal(t, \"verification.testpersister.update-identity-case-insensitive-next@ory.sh\", actual.Value)\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, \"verification.TestPersister.Update-Identity-case-insensitive-next@ory.sh\")\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"suite=credential-types\", func(t *testing.T) {\n\t\t\tfor _, ct := range identity.AllCredentialTypes {\n\t\t\t\tt.Run(\"type=\"+ct.String(), func(t *testing.T) {\n\t\t\t\t\tid, err := idpersistence.FindIdentityCredentialsTypeByName(p.GetConnection(ctx), ct)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\trequire.NotEqual(t, uuid.Nil, id)\n\t\t\t\t\tname, err := idpersistence.FindIdentityCredentialsTypeByID(p.GetConnection(ctx), id)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tassert.Equal(t, ct, name)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t_, err := idpersistence.FindIdentityCredentialsTypeByName(p.GetConnection(ctx), \"unknown\")\n\t\t\trequire.Error(t, err)\n\n\t\t\t_, err = idpersistence.FindIdentityCredentialsTypeByID(p.GetConnection(ctx), x.NewUUID())\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tt.Run(\"suite=recovery-address\", func(t *testing.T) {\n\t\t\tsortAddresses := func(addresses []identity.RecoveryAddress) {\n\t\t\t\tslices.SortFunc(addresses, func(a, b identity.RecoveryAddress) int {\n\t\t\t\t\treturn strings.Compare(a.Value, b.Value)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tcreateIdentityWithAddresses := func(t *testing.T, email string) *identity.Identity {\n\t\t\t\tvar i identity.Identity\n\t\t\t\trequire.NoError(t, faker.FakeData(&i))\n\t\t\t\ti.Traits = []byte(`{\"email\":\"` + email + `\"}`)\n\t\t\t\taddress := identity.NewRecoveryEmailAddress(email, i.ID)\n\t\t\t\ti.RecoveryAddresses = append(i.RecoveryAddresses, *address)\n\n\t\t\t\taddressOther := identity.NewRecoveryEmailAddress(email+\"_other\", i.ID)\n\t\t\t\ti.RecoveryAddresses = append(i.RecoveryAddresses, *addressOther)\n\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, &i))\n\t\t\t\treturn &i\n\t\t\t}\n\n\t\t\tt.Run(\"case=not found\", func(t *testing.T) {\n\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, \"does-not-exist\")\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\tallAddresses, err := p.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx, \"does-not-exist\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, allAddresses, 0)\n\t\t\t})\n\n\t\t\tt.Run(\"case=create and find\", func(t *testing.T) {\n\t\t\t\taddresses := make([]identity.RecoveryAddress, 15)\n\t\t\t\tfor k := range addresses {\n\t\t\t\t\taddresses[k] = createIdentityWithAddresses(t, randx.MustString(16, randx.AlphaLowerNum)+\"@ory.sh\").RecoveryAddresses[0]\n\t\t\t\t\trequire.NotEmpty(t, addresses[k].ID)\n\t\t\t\t}\n\n\t\t\t\tcompare := func(t *testing.T, expected, actual identity.RecoveryAddress) {\n\t\t\t\t\tactual.CreatedAt = actual.CreatedAt.UTC().Truncate(time.Hour * 24)\n\t\t\t\t\tactual.UpdatedAt = actual.UpdatedAt.UTC().Truncate(time.Hour * 24)\n\t\t\t\t\texpected.CreatedAt = expected.CreatedAt.UTC().Truncate(time.Hour * 24)\n\t\t\t\t\texpected.UpdatedAt = expected.UpdatedAt.UTC().Truncate(time.Hour * 24)\n\t\t\t\t\tassert.EqualValues(t, expected, actual)\n\t\t\t\t}\n\n\t\t\t\tfor k, expected := range addresses {\n\t\t\t\t\tt.Run(\"method=FindVerifiableAddressByValue\", func(t *testing.T) {\n\t\t\t\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\t\t\t\tactual, err := p.FindRecoveryAddressByValue(ctx, expected.Via, expected.Value)\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\tcompare(t, expected, *actual)\n\n\t\t\t\t\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, expected.Via, expected.Value)\n\t\t\t\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"method=FindAllRecoveryAddressesForIdentityByRecoveryAddressValue\", func(t *testing.T) {\n\t\t\t\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\t\t\t\tallAddresses, err := p.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx, expected.Value)\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\trequire.Len(t, allAddresses, 2)\n\t\t\t\t\t\t\tsortAddresses(allAddresses)\n\t\t\t\t\t\t\trequire.Equal(t, expected.Value, allAddresses[0].Value)\n\t\t\t\t\t\t\trequire.Equal(t, expected.Value+\"_other\", allAddresses[1].Value)\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tt.Run(\"not if on another network\", func(t *testing.T) {\n\t\t\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t\t\tallAddresses, err := p.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx, expected.Value)\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\trequire.Len(t, allAddresses, 0)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"case=create and update and find\", func(t *testing.T) {\n\t\t\t\temail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\temailLower := strings.ToLower(email)\n\t\t\t\tid := createIdentityWithAddresses(t, email)\n\n\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, email)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tallAddresses, err := p.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx, emailLower)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, allAddresses, 2)\n\t\t\t\tsortAddresses(allAddresses)\n\t\t\t\trequire.Equal(t, allAddresses[0].Value, emailLower)\n\t\t\t\trequire.Equal(t, allAddresses[1].Value, emailLower+\"_other\")\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, email)\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t\tallAddresses, err := p.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx, emailLower)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, allAddresses, 0)\n\t\t\t\t})\n\n\t\t\t\temailNext := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\tid.RecoveryAddresses = []identity.RecoveryAddress{{Via: identity.AddressTypeEmail, Value: emailNext}, {Via: identity.AddressTypeEmail, Value: emailNext + \"_other\"}}\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, id))\n\n\t\t\t\t_, err = p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, email)\n\t\t\t\trequire.EqualError(t, err, sqlcon.ErrNoRows.Error())\n\n\t\t\t\tallAddresses, err = p.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx, emailLower)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, allAddresses, 0)\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, email)\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t\tallAddresses, err := p.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx, emailLower)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, allAddresses, 0)\n\t\t\t\t})\n\n\t\t\t\temailNextLower := strings.ToLower(emailNext)\n\t\t\t\tactual, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, emailNext)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, identity.AddressTypeEmail, actual.Via)\n\t\t\t\tassert.Equal(t, emailNextLower, actual.Value)\n\n\t\t\t\tallAddresses, err = p.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx, emailNextLower)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, allAddresses, 2)\n\t\t\t\tsortAddresses(allAddresses)\n\t\t\t\tassert.Equal(t, identity.AddressTypeEmail, allAddresses[0].Via)\n\t\t\t\tassert.Equal(t, emailNextLower, allAddresses[0].Value)\n\t\t\t\tassert.Equal(t, identity.AddressTypeEmail, allAddresses[1].Via)\n\t\t\t\tassert.Equal(t, emailNextLower+\"_other\", allAddresses[1].Value)\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, emailNext)\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t\tallAddresses, err := p.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx, emailNextLower)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, allAddresses, 0)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=create and update and find case insensitive\", func(t *testing.T) {\n\t\t\t\tid := createIdentityWithAddresses(t, \"recovery.TestPersister.Update-case-insensitive@ory.sh\")\n\n\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"recovery.TestPersister.Update-case-insensitive@ory.sh\"))\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"Recovery.TestPersister.Update-case-insensitive@ory.sh\"))\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\n\t\t\t\tid.RecoveryAddresses = []identity.RecoveryAddress{{Via: identity.AddressTypeEmail, Value: \"recovery.TestPersister.Update-case-insensitive-next@ory.sh\"}}\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, id))\n\n\t\t\t\t_, err = p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"recovery.TestPersister.Update-case-insensitive@ory.sh\"))\n\t\t\t\trequire.EqualError(t, err, sqlcon.ErrNoRows.Error())\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"recovery.TestPersister.Update-case-insensitive@ory.sh\"))\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\n\t\t\t\tactual, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"recovery.TestPersister.Update-case-insensitive-next@ory.sh\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, identity.AddressTypeEmail, actual.Via)\n\t\t\t\tassert.Equal(t, \"recovery.testpersister.update-case-insensitive-next@ory.sh\", actual.Value)\n\n\t\t\t\tt.Run(\"can not find if on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.FindRecoveryAddressByValue(ctx, identity.AddressTypeEmail, strings.ToUpper(\"recovery.TestPersister.Update-case-insensitive-next@ory.sh\"))\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"network reference isolation\", func(t *testing.T) {\n\t\t\tnid1, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\tnid2, _ := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\tvar m []identity.CredentialsTypeTable\n\t\t\trequire.NoError(t, p.GetConnection(ctx).All(&m))\n\n\t\t\tiid := x.NewUUID()\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at) VALUES (?, ?, 'default', '{}', ?, ?)\", iid, nid1, time.Now(), time.Now()).Exec())\n\n\t\t\tcid1, cid2 := x.NewUUID(), x.NewUUID()\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO identity_credentials (id, identity_id, nid, identity_credential_type_id, created_at, updated_at, config) VALUES (?, ?, ?, ?, ?, ?, '{}')\", cid1, iid, nid1, m[0].ID, time.Now(), time.Now()).Exec())\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO identity_credentials (id, identity_id, nid, identity_credential_type_id, created_at, updated_at, config) VALUES (?, ?, ?, ?, ?, ?, '{}')\", cid2, iid, nid2, m[0].ID, time.Now(), time.Now()).Exec())\n\n\t\t\tici1, ici2 := x.NewUUID(), x.NewUUID()\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO identity_credential_identifiers (id, identity_id, identity_credential_id, nid, identifier, created_at, updated_at, identity_credential_type_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\", ici1, iid, cid1, nid1, \"nid1\", time.Now(), time.Now(), m[0].ID).Exec())\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO identity_credential_identifiers (id, identity_id, identity_credential_id, nid, identifier, created_at, updated_at, identity_credential_type_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\", ici2, iid, cid2, nid2, \"nid2\", time.Now(), time.Now(), m[0].ID).Exec())\n\n\t\t\t_, err := p.GetIdentity(ctx, nid1, identity.ExpandNothing)\n\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t_, err = p.GetIdentityConfidential(ctx, nid1)\n\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\ti, c, err := p.FindByCredentialsIdentifier(ctx, m[0].Name, \"nid1\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"nid1\", c.Identifiers[0])\n\t\t\trequire.Len(t, i.Credentials, 1)\n\n\t\t\t_, _, err = p.FindByCredentialsIdentifier(ctx, m[0].Name, \"nid2\")\n\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\ti, err = p.GetIdentityConfidential(ctx, iid)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, i.Credentials, 1)\n\t\t\tassert.Equal(t, \"nid1\", i.Credentials[m[0].Name].Identifiers[0])\n\t\t})\n\n\t\tt.Run(\"suite=update-verifiable-addresses-edge-cases\", func(t *testing.T) {\n\t\t\tt.Run(\"case=add new verifiable addresses\", func(t *testing.T) {\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\toriginalEmail := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\tnew1Email := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\tnew2Email := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\tinitial.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\t{Value: originalEmail, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusPending},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.VerifiableAddresses, 1)\n\n\t\t\t\t// Add two new addresses\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tupdated.VerifiableAddresses = append(updated.VerifiableAddresses,\n\t\t\t\t\tidentity.VerifiableAddress{Value: new1Email, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusPending},\n\t\t\t\t\tidentity.VerifiableAddress{Value: new2Email, Via: identity.AddressTypeEmail, Verified: true, Status: identity.VerifiableAddressStatusCompleted},\n\t\t\t\t)\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 3)\n\n\t\t\t\tvalues := []string{actual.VerifiableAddresses[0].Value, actual.VerifiableAddresses[1].Value, actual.VerifiableAddresses[2].Value}\n\t\t\t\tassertContainsValues(t, values, []string{originalEmail, new1Email, new2Email}, nil)\n\n\t\t\t\t// Verify the new verified address has verified_at set\n\t\t\t\tfor _, addr := range actual.VerifiableAddresses {\n\t\t\t\t\tif addr.Value == new2Email {\n\t\t\t\t\t\tassert.True(t, addr.Verified)\n\t\t\t\t\t\tassert.NotNil(t, addr.VerifiedAt)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"case=remove all verifiable addresses\", func(t *testing.T) {\n\t\t\t\temail1 := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\temail2 := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\t{Value: email1, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusPending},\n\t\t\t\t\t{Value: email2, Via: identity.AddressTypeEmail, Verified: true, Status: identity.VerifiableAddressStatusCompleted},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.VerifiableAddresses, 2)\n\n\t\t\t\t// Remove all addresses\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tupdated.VerifiableAddresses = []identity.VerifiableAddress{}\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, actual.VerifiableAddresses, 0)\n\t\t\t})\n\n\t\t\tt.Run(\"case=remove some and add some verifiable addresses\", func(t *testing.T) {\n\t\t\t\tkeepEmail := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\tremoveEmail := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\taddEmail := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\t{Value: keepEmail, Via: identity.AddressTypeEmail, Verified: true, Status: identity.VerifiableAddressStatusCompleted},\n\t\t\t\t\t{Value: removeEmail, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusPending},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.VerifiableAddresses, 2)\n\n\t\t\t\t// Keep one, remove one, add one\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tvar keptAddress identity.VerifiableAddress\n\t\t\t\tfor _, addr := range updated.VerifiableAddresses {\n\t\t\t\t\tif addr.Value == keepEmail {\n\t\t\t\t\t\tkeptAddress = addr\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tupdated.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\tkeptAddress,\n\t\t\t\t\t{Value: addEmail, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusSent},\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 2)\n\n\t\t\t\tvalues := []string{actual.VerifiableAddresses[0].Value, actual.VerifiableAddresses[1].Value}\n\t\t\t\tassertContainsValues(t, values, []string{keepEmail, addEmail}, []string{removeEmail})\n\t\t\t})\n\n\t\t\tt.Run(\"case=update existing verifiable address properties\", func(t *testing.T) {\n\t\t\t\tchangeEmail := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\t{Value: changeEmail, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusPending},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\toldAddr := fromDB.VerifiableAddresses[0]\n\t\t\t\tassert.False(t, oldAddr.Verified)\n\t\t\t\tassert.Nil(t, oldAddr.VerifiedAt)\n\n\t\t\t\t// Change the address value - this should be treated as removal + addition\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tupdated.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\t{Value: changeEmail, Via: identity.AddressTypeEmail, Verified: true, Status: identity.VerifiableAddressStatusCompleted},\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 1)\n\t\t\t\tassert.Equal(t, changeEmail, actual.VerifiableAddresses[0].Value)\n\t\t\t\tassert.True(t, actual.VerifiableAddresses[0].Verified)\n\t\t\t\tassert.NotNil(t, actual.VerifiableAddresses[0].VerifiedAt)\n\t\t\t})\n\n\t\t\tt.Run(\"case=replace all verifiable addresses at once\", func(t *testing.T) {\n\t\t\t\told1Email := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\told2Email := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\told3Email := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\t{Value: old1Email, Via: identity.AddressTypeEmail, Verified: true, Status: identity.VerifiableAddressStatusCompleted},\n\t\t\t\t\t{Value: old2Email, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusPending},\n\t\t\t\t\t{Value: old3Email, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusSent},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.VerifiableAddresses, 3)\n\n\t\t\t\tnew1Email := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\tnew2Email := \"dev+\" + uuid.Must(uuid.NewV4()).String() + \"+@ory.com\"\n\t\t\t\t// Replace all addresses with new ones\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tupdated.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\t{Value: new1Email, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusPending},\n\t\t\t\t\t{Value: new2Email, Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusPending},\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 2)\n\n\t\t\t\tvalues := []string{actual.VerifiableAddresses[0].Value, actual.VerifiableAddresses[1].Value}\n\t\t\t\tassertContainsValues(t, values, []string{new1Email, new2Email}, []string{old1Email, old2Email, old3Email})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"suite=update-recovery-addresses-edge-cases\", func(t *testing.T) {\n\t\t\tt.Run(\"case=add new recovery addresses\", func(t *testing.T) {\n\t\t\t\tinitialEmail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\trecovery1Email := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\trecovery2Email := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.RecoveryAddresses = []identity.RecoveryAddress{\n\t\t\t\t\t{Value: initialEmail, Via: identity.AddressTypeEmail},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.RecoveryAddresses, 1)\n\n\t\t\t\t// Add two new addresses\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tupdated.RecoveryAddresses = append(updated.RecoveryAddresses,\n\t\t\t\t\tidentity.RecoveryAddress{Value: recovery1Email, Via: identity.AddressTypeEmail},\n\t\t\t\t\tidentity.RecoveryAddress{Value: recovery2Email, Via: identity.AddressTypeEmail},\n\t\t\t\t)\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.RecoveryAddresses, 3)\n\n\t\t\t\tvalues := []string{actual.RecoveryAddresses[0].Value, actual.RecoveryAddresses[1].Value, actual.RecoveryAddresses[2].Value}\n\t\t\t\tassertContainsValues(t, values, []string{initialEmail, recovery1Email, recovery2Email}, nil)\n\t\t\t})\n\n\t\t\tt.Run(\"case=remove all recovery addresses\", func(t *testing.T) {\n\t\t\t\tremove1Email := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\tremove2Email := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.RecoveryAddresses = []identity.RecoveryAddress{\n\t\t\t\t\t{Value: remove1Email, Via: identity.AddressTypeEmail},\n\t\t\t\t\t{Value: remove2Email, Via: identity.AddressTypeEmail},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.RecoveryAddresses, 2)\n\n\t\t\t\t// Remove all addresses\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tupdated.RecoveryAddresses = []identity.RecoveryAddress{}\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, actual.RecoveryAddresses, 0)\n\t\t\t})\n\n\t\t\tt.Run(\"case=remove some and add some recovery addresses\", func(t *testing.T) {\n\t\t\t\tkeepEmail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\tremoveEmail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\taddEmail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.RecoveryAddresses = []identity.RecoveryAddress{\n\t\t\t\t\t{Value: keepEmail, Via: identity.AddressTypeEmail},\n\t\t\t\t\t{Value: removeEmail, Via: identity.AddressTypeEmail},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.RecoveryAddresses, 2)\n\n\t\t\t\t// Keep one, remove one, add one\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tvar keptAddress identity.RecoveryAddress\n\t\t\t\tfor _, addr := range updated.RecoveryAddresses {\n\t\t\t\t\tif addr.Value == keepEmail {\n\t\t\t\t\t\tkeptAddress = addr\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tupdated.RecoveryAddresses = []identity.RecoveryAddress{\n\t\t\t\t\tkeptAddress,\n\t\t\t\t\t{Value: addEmail, Via: identity.AddressTypeEmail},\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.RecoveryAddresses, 2)\n\n\t\t\t\tvalues := []string{actual.RecoveryAddresses[0].Value, actual.RecoveryAddresses[1].Value}\n\t\t\t\tassertContainsValues(t, values, []string{keepEmail, addEmail}, []string{removeEmail})\n\t\t\t})\n\n\t\t\tt.Run(\"case=replace all recovery addresses at once\", func(t *testing.T) {\n\t\t\t\told1Email := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\told2Email := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\told3Email := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\tnew1Email := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\tnew2Email := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.RecoveryAddresses = []identity.RecoveryAddress{\n\t\t\t\t\t{Value: old1Email, Via: identity.AddressTypeEmail},\n\t\t\t\t\t{Value: old2Email, Via: identity.AddressTypeEmail},\n\t\t\t\t\t{Value: old3Email, Via: identity.AddressTypeEmail},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.RecoveryAddresses, 3)\n\n\t\t\t\t// Replace all addresses with new ones\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tupdated.RecoveryAddresses = []identity.RecoveryAddress{\n\t\t\t\t\t{Value: new1Email, Via: identity.AddressTypeEmail},\n\t\t\t\t\t{Value: new2Email, Via: identity.AddressTypeEmail},\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.RecoveryAddresses, 2)\n\n\t\t\t\tvalues := []string{actual.RecoveryAddresses[0].Value, actual.RecoveryAddresses[1].Value}\n\t\t\t\tassertContainsValues(t, values, []string{new1Email, new2Email}, []string{old1Email, old2Email, old3Email})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"suite=update-credentials-edge-cases\", func(t *testing.T) {\n\t\t\tt.Run(\"case=add new credential type\", func(t *testing.T) {\n\t\t\t\ttotpIdentifier := randx.MustString(16, randx.AlphaLowerNum)\n\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.Credentials, 1)\n\t\t\t\t_, hasPassword := fromDB.Credentials[identity.CredentialsTypePassword]\n\t\t\t\tassert.True(t, hasPassword)\n\t\t\t\toldPasswordCredID := fromDB.Credentials[identity.CredentialsTypePassword].ID\n\n\t\t\t\t// Add TOTP credential\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeTOTP, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\tIdentifiers: []string{totpIdentifier},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t})\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, initial, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.Credentials, 2)\n\t\t\t\t_, hasPassword = actual.Credentials[identity.CredentialsTypePassword]\n\t\t\t\t_, hasTOTP := actual.Credentials[identity.CredentialsTypeTOTP]\n\t\t\t\tassert.True(t, hasPassword)\n\t\t\t\tassert.True(t, hasTOTP)\n\t\t\t\tassert.Equal(t, []string{totpIdentifier}, actual.Credentials[identity.CredentialsTypeTOTP].Identifiers)\n\t\t\t\t// Verify that the password credential was not recreated (ID should remain the same)\n\t\t\t\tassert.Equal(t, oldPasswordCredID, actual.Credentials[identity.CredentialsTypePassword].ID, \"password credential should not be recreated when adding TOTP\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=remove all credentials\", func(t *testing.T) {\n\t\t\t\toidcIdentifier := randx.MustString(16, randx.AlphaLowerNum)\n\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{oidcIdentifier},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.Credentials, 2)\n\n\t\t\t\t// Remove all credentials\n\t\t\t\tinitial.Credentials = map[identity.CredentialsType]identity.Credentials{}\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, initial, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, actual.Credentials, 0)\n\t\t\t})\n\n\t\t\tt.Run(\"case=remove one credential type and keep others\", func(t *testing.T) {\n\t\t\t\toidcIdentifier := randx.MustString(16, randx.AlphaLowerNum)\n\t\t\t\ttotpIdentifier := randx.MustString(16, randx.AlphaLowerNum)\n\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{oidcIdentifier},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\t})\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeTOTP, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\tIdentifiers: []string{totpIdentifier},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.Credentials, 3)\n\t\t\t\toldOIDCCredID := fromDB.Credentials[identity.CredentialsTypeOIDC].ID\n\t\t\t\toldTOTPCredID := fromDB.Credentials[identity.CredentialsTypeTOTP].ID\n\n\t\t\t\t// Remove password credential, keep OIDC and TOTP\n\t\t\t\tdelete(initial.Credentials, identity.CredentialsTypePassword)\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, initial, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.Credentials, 2)\n\t\t\t\t_, hasPassword := actual.Credentials[identity.CredentialsTypePassword]\n\t\t\t\t_, hasOIDC := actual.Credentials[identity.CredentialsTypeOIDC]\n\t\t\t\t_, hasTOTP := actual.Credentials[identity.CredentialsTypeTOTP]\n\t\t\t\tassert.False(t, hasPassword)\n\t\t\t\tassert.True(t, hasOIDC)\n\t\t\t\tassert.True(t, hasTOTP)\n\t\t\t\t// Verify that OIDC and TOTP credentials were not recreated (IDs should remain the same)\n\t\t\t\tassert.Equal(t, oldOIDCCredID, actual.Credentials[identity.CredentialsTypeOIDC].ID, \"OIDC credential should not be recreated when removing password\")\n\t\t\t\tassert.Equal(t, oldTOTPCredID, actual.Credentials[identity.CredentialsTypeTOTP].ID, \"TOTP credential should not be recreated when removing password\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=update credential config and identifiers\", func(t *testing.T) {\n\t\t\t\toldEmail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\tnewEmail := randx.MustString(16, randx.AlphaLowerNum) + \"@ory.sh\"\n\t\t\t\tinitial := passwordIdentity(\"\", oldEmail)\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\toldCred := fromDB.Credentials[identity.CredentialsTypePassword]\n\n\t\t\t\t// Update password credential with new identifier and config\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\tIdentifiers: []string{newEmail},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"new\":\"config\"}`),\n\t\t\t\t})\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, initial, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tnewCred := actual.Credentials[identity.CredentialsTypePassword]\n\t\t\t\tassert.NotEqual(t, oldCred.ID, newCred.ID)\n\t\t\t\tassert.Equal(t, []string{newEmail}, newCred.Identifiers)\n\t\t\t\tassert.JSONEq(t, `{\"new\":\"config\"}`, string(newCred.Config))\n\t\t\t})\n\n\t\t\tt.Run(\"case=replace all credentials at once\", func(t *testing.T) {\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{\"oidc-replace\"},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\t})\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeTOTP, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\tIdentifiers: []string{\"totp-replace\"},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.Credentials, 3)\n\n\t\t\t\t// Replace all credentials with webauthn\n\t\t\t\tinitial.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypeWebAuthn: {\n\t\t\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\t\t\tIdentifiers: []string{\"webauthn-new\"},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"credentials\":[]}`),\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, initial, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.Credentials, 1)\n\t\t\t\t_, hasWebAuthn := actual.Credentials[identity.CredentialsTypeWebAuthn]\n\t\t\t\tassert.True(t, hasWebAuthn)\n\t\t\t\tassert.Equal(t, []string{\"webauthn-new\"}, actual.Credentials[identity.CredentialsTypeWebAuthn].Identifiers)\n\t\t\t})\n\n\t\t\tt.Run(\"case=update with no changes\", func(t *testing.T) {\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{\"oidc-no-change\"},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\t})\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeTOTP, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\tIdentifiers: []string{\"totp-no-change\"},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.Credentials, 3)\n\t\t\t\toldPasswordCredID := fromDB.Credentials[identity.CredentialsTypePassword].ID\n\t\t\t\toldOIDCCredID := fromDB.Credentials[identity.CredentialsTypeOIDC].ID\n\t\t\t\toldTOTPCredID := fromDB.Credentials[identity.CredentialsTypeTOTP].ID\n\n\t\t\t\t// Update without changing anything\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, initial, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\t// Verify no credentials were added or removed\n\t\t\t\trequire.Len(t, actual.Credentials, 3, \"credential count should not change when nothing is modified\")\n\t\t\t\t// Verify all credential IDs remained the same (nothing was recreated)\n\t\t\t\tassert.Equal(t, oldPasswordCredID, actual.Credentials[identity.CredentialsTypePassword].ID, \"password credential should not be recreated when nothing changes\")\n\t\t\t\tassert.Equal(t, oldOIDCCredID, actual.Credentials[identity.CredentialsTypeOIDC].ID, \"OIDC credential should not be recreated when nothing changes\")\n\t\t\t\tassert.Equal(t, oldTOTPCredID, actual.Credentials[identity.CredentialsTypeTOTP].ID, \"TOTP credential should not be recreated when nothing changes\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=update with json whitespace differences\", func(t *testing.T) {\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\t// Create with compact JSON\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{\"oidc-whitespace\"},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\",\"baz\":\"qux\"}`),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.Credentials, 2)\n\t\t\t\toldPasswordCredID := fromDB.Credentials[identity.CredentialsTypePassword].ID\n\t\t\t\toldOIDCCredID := fromDB.Credentials[identity.CredentialsTypeOIDC].ID\n\n\t\t\t\t// Update with same JSON but different whitespace formatting\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{\"oidc-whitespace\"},\n\t\t\t\t\t// Same JSON content but with different whitespace\n\t\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\n\t\t\t\t\t\t\"foo\": \"bar\",\n\t\t\t\t\t\t\"baz\": \"qux\"\n\t\t\t\t\t}`),\n\t\t\t\t})\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, initial, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\t// Verify no credentials were added or removed\n\t\t\t\trequire.Len(t, actual.Credentials, 2, \"credential count should not change\")\n\t\t\t\t// Verify credential IDs remained the same (nothing was recreated despite JSON formatting difference)\n\t\t\t\tassert.Equal(t, oldPasswordCredID, actual.Credentials[identity.CredentialsTypePassword].ID, \"password credential should not be recreated\")\n\t\t\t\tassert.Equal(t, oldOIDCCredID, actual.Credentials[identity.CredentialsTypeOIDC].ID, \"OIDC credential should not be recreated when JSON has different whitespace\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=update traits with fromDatabase parameter\", func(t *testing.T) {\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.Traits = identity.Traits(`{\"email\":\"initial@ory.sh\",\"name\":\"Initial Name\"}`)\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// Update traits using DiffAgainst\n\t\t\t\tupdated := fromDB.CopyWithoutCredentials()\n\t\t\t\tupdated.Traits = identity.Traits(`{\"email\":\"updated@ory.sh\",\"name\":\"Updated Name\"}`)\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentity(ctx, initial.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.JSONEq(t, `{\"email\":\"updated@ory.sh\",\"name\":\"Updated Name\"}`, string(actual.Traits))\n\t\t\t})\n\n\t\t\tt.Run(\"case=update without fromDatabase parameter\", func(t *testing.T) {\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{\"oidc-no-from-db\"},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, fromDB.Credentials, 2)\n\t\t\t\toldPasswordCredID := fromDB.Credentials[identity.CredentialsTypePassword].ID\n\t\t\t\toldOIDCCredID := fromDB.Credentials[identity.CredentialsTypeOIDC].ID\n\n\t\t\t\t// Update without providing fromDatabase - should fetch from DB internally\n\t\t\t\tupdated := *fromDB\n\t\t\t\tupdated.SetCredentials(identity.CredentialsTypeTOTP, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\tIdentifiers: []string{\"totp-no-from-db\"},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t})\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, &updated))\n\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.Credentials, 3)\n\t\t\t\t_, hasTOTP := actual.Credentials[identity.CredentialsTypeTOTP]\n\t\t\t\tassert.True(t, hasTOTP)\n\t\t\t\t// Verify that password and OIDC credentials were not recreated (IDs should remain the same)\n\t\t\t\tassert.Equal(t, oldPasswordCredID, actual.Credentials[identity.CredentialsTypePassword].ID, \"password credential should not be recreated when adding TOTP without fromDatabase\")\n\t\t\t\tassert.Equal(t, oldOIDCCredID, actual.Credentials[identity.CredentialsTypeOIDC].ID, \"OIDC credential should not be recreated when adding TOTP without fromDatabase\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"suite=update-combined-changes\", func(t *testing.T) {\n\t\t\tt.Run(\"case=update addresses and credentials simultaneously\", func(t *testing.T) {\n\t\t\t\tinitial := passwordIdentity(\"\", x.NewUUID().String())\n\t\t\t\tinitial.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\t{Value: \"combined-verify@ory.sh\", Via: identity.AddressTypeEmail, Verified: false, Status: identity.VerifiableAddressStatusPending},\n\t\t\t\t}\n\t\t\t\tinitial.RecoveryAddresses = []identity.RecoveryAddress{\n\t\t\t\t\t{Value: \"combined-recovery@ory.sh\", Via: identity.AddressTypeEmail},\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, initial))\n\t\t\t\tcreatedIDs = append(createdIDs, initial.ID)\n\n\t\t\t\tfromDB, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// Change everything at once\n\t\t\t\tupdated := *fromDB\n\t\t\t\tupdated.VerifiableAddresses = []identity.VerifiableAddress{\n\t\t\t\t\t{Value: \"combined-verify-new@ory.sh\", Via: identity.AddressTypeEmail, Verified: true, Status: identity.VerifiableAddressStatusCompleted},\n\t\t\t\t}\n\t\t\t\tupdated.RecoveryAddresses = []identity.RecoveryAddress{\n\t\t\t\t\t{Value: \"combined-recovery-new@ory.sh\", Via: identity.AddressTypeEmail},\n\t\t\t\t}\n\t\t\t\tupdated.SetCredentials(identity.CredentialsTypeTOTP, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\tIdentifiers: []string{\"combined-totp\"},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t})\n\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, &updated, identity.DiffAgainst(fromDB)))\n\n\t\t\t\tactual, err := p.GetIdentityConfidential(ctx, initial.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, actual.VerifiableAddresses, 1)\n\t\t\t\trequire.Len(t, actual.RecoveryAddresses, 1)\n\t\t\t\trequire.Len(t, actual.Credentials, 2)\n\n\t\t\t\tassert.Equal(t, \"combined-verify-new@ory.sh\", actual.VerifiableAddresses[0].Value)\n\t\t\t\tassert.Equal(t, \"combined-recovery-new@ory.sh\", actual.RecoveryAddresses[0].Value)\n\t\t\t\t_, hasTOTP := actual.Credentials[identity.CredentialsTypeTOTP]\n\t\t\t\tassert.True(t, hasTOTP)\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc NewTestIdentity(numAddresses int, prefix string, i int) *identity.Identity {\n\tvar (\n\t\tverifiableAddresses []identity.VerifiableAddress\n\t\trecoveryAddresses   []identity.RecoveryAddress\n\t)\n\ttraits := struct {\n\t\tEmails   []string `json:\"emails\"`\n\t\tUsername string   `json:\"username\"`\n\t}{}\n\n\tverificationStates := []identity.VerifiableAddressStatus{\n\t\tidentity.VerifiableAddressStatusPending,\n\t\tidentity.VerifiableAddressStatusSent,\n\t\tidentity.VerifiableAddressStatusCompleted,\n\t}\n\n\tfor j := 0; j < numAddresses; j++ {\n\t\temail := fmt.Sprintf(\"%s-%d-%d@ory.sh\", prefix, i, j)\n\t\ttraits.Emails = append(traits.Emails, email)\n\t\tverifiableAddresses = append(verifiableAddresses, identity.VerifiableAddress{\n\t\t\tValue:    email,\n\t\t\tVia:      identity.AddressTypeEmail,\n\t\t\tVerified: j%2 == 0,\n\t\t\tStatus:   verificationStates[j%len(verificationStates)],\n\t\t})\n\t\trecoveryAddresses = append(recoveryAddresses, identity.RecoveryAddress{\n\t\t\tValue: email,\n\t\t\tVia:   identity.AddressTypeEmail,\n\t\t})\n\t}\n\ttraits.Username = traits.Emails[0]\n\trawTraits, _ := json.Marshal(traits)\n\n\tid := &identity.Identity{\n\t\tSchemaID:            \"multiple_emails\",\n\t\tTraits:              rawTraits,\n\t\tVerifiableAddresses: verifiableAddresses,\n\t\tRecoveryAddresses:   recoveryAddresses,\n\t\tState:               \"active\",\n\t}\n\tid.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\tType:        identity.CredentialsTypePassword,\n\t\tIdentifiers: []string{traits.Username},\n\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t})\n\n\treturn id\n}\n"
  },
  {
    "path": "identity/validator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"context\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype (\n\tvalidatorDependencies interface {\n\t\tschema.IdentitySchemaProvider\n\t\tconfig.Provider\n\t}\n\tValidator struct {\n\t\tv *schema.Validator\n\t\td validatorDependencies\n\t}\n\tValidationProvider interface {\n\t\tIdentityValidator() *Validator\n\t}\n)\n\nfunc NewValidator(d validatorDependencies) *Validator {\n\treturn &Validator{v: schema.NewValidator(), d: d}\n}\n\nfunc (v *Validator) ValidateWithRunner(ctx context.Context, i *Identity, runners ...schema.ValidateExtension) error {\n\trunner, err := schema.NewExtensionRunner(ctx, schema.WithValidateRunners(runners...))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tss, err := v.d.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ts, err := ss.GetByID(i.SchemaID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(i.Traits) == 0 {\n\t\ti.Traits = []byte(`{}`)\n\t}\n\n\ttraits, err := sjson.SetRawBytes([]byte(`{}`), \"traits\", i.Traits)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithError(err.Error()))\n\t}\n\n\treturn v.v.Validate(ctx, s.URL.String(), traits, schema.WithExtensionRunner(runner))\n}\n\nfunc (v *Validator) Validate(ctx context.Context, i *Identity) error {\n\treturn otelx.WithSpan(ctx, \"identity.Validator.Validate\", func(ctx context.Context) error {\n\t\treturn v.ValidateWithRunner(ctx, i,\n\t\t\tNewSchemaExtensionCredentials(i),\n\t\t\tNewSchemaExtensionVerification(i, v.d.Config().SelfServiceFlowVerificationRequestLifespan(ctx)),\n\t\t\tNewSchemaExtensionRecovery(i),\n\t\t)\n\t})\n}\n"
  },
  {
    "path": "identity/validator_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity_test\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/jsonschema/v3/httploader\"\n\t\"github.com/ory/kratos/driver/config\"\n\t. \"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httpx\"\n)\n\nfunc TestSchemaValidatorDisallowsInternalNetworkRequests(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.IdentitySchemasConfig(map[string]string{\n\t\t\t\"localhost\":  \"https://localhost/schema/whatever\",\n\t\t\t\"privateRef\": \"file://stub/localhost-ref.schema.json\",\n\t\t\t\"inlineRef\":  \"base64://\" + base64.StdEncoding.EncodeToString([]byte(`{\"traits\": {}}`)),\n\t\t})),\n\t\tconfigx.WithValue(config.ViperKeyClientHTTPNoPrivateIPRanges, true),\n\t)\n\n\tv := NewValidator(reg)\n\n\tfor id, expectedErr := range map[string]string{\n\t\t\"localhost\":  \"is not a permitted destination\",\n\t\t\"privateRef\": \"is not a permitted destination\",\n\t\t\"inlineRef\":  \"\",\n\t} {\n\t\tt.Run(id, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\ti := &Identity{\n\t\t\t\tSchemaID: id,\n\t\t\t\tTraits:   Traits(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\t}\n\t\t\tctx := context.WithValue(t.Context(), httploader.ContextKey, reg.HTTPClient(t.Context()))\n\t\t\terr := v.Validate(ctx, i)\n\t\t\tif expectedErr == \"\" {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvar hErr *herodot.DefaultError\n\t\t\trequire.ErrorAs(t, err, &hErr)\n\t\t\tassert.Contains(t, hErr.Debug(), expectedErr)\n\t\t})\n\t}\n}\n\nfunc TestSchemaValidator(t *testing.T) {\n\tt.Parallel()\n\n\tctrl := gomock.NewController(t)\n\tdefer ctrl.Finish()\n\n\trouter := http.NewServeMux()\n\trouter.HandleFunc(\"GET /schema/{name}\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = w.Write([]byte(`{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n\t\"traits\": {\n\t  \"type\": \"object\",\n\t  \"properties\": {\n        \"` + r.PathValue(\"name\") + `\": {\n          \"type\": \"string\",\n          \"description\": \"The person's first name.\"\n        },\n        \"lastName\": {\n          \"type\": \"string\",\n          \"description\": \"The person's last name.\"\n        },\n        \"age\": {\n          \"description\": \"Age in years which must be equal to or greater than zero.\",\n          \"type\": \"integer\",\n          \"minimum\": 1\n        }\n\t  },\n\t  \"additionalProperties\": false\n\t}\n  },\n  \"additionalProperties\": false\n}`))\n\t})\n\n\tts := httptest.NewServer(router)\n\tdefer ts.Close()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.IdentitySchemasConfig(map[string]string{\n\t\t\t\"default\":         ts.URL + \"/schema/firstName\",\n\t\t\t\"whatever\":        ts.URL + \"/schema/whatever\",\n\t\t\t\"unreachable-url\": ts.URL + \"/404-not-found\",\n\t\t})),\n\t)\n\tv := NewValidator(reg)\n\n\tfor k, tc := range []struct {\n\t\ti   *Identity\n\t\terr string\n\t}{\n\t\t{\n\t\t\ti: &Identity{\n\t\t\t\tTraits: Traits(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\ti: &Identity{\n\t\t\t\tTraits: Traits(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": -1 }`),\n\t\t\t},\n\t\t\terr: \"I[#/traits/age] S[#/properties/traits/properties/age/minimum] must be >= 1 but found -1\",\n\t\t},\n\t\t{\n\t\t\ti: &Identity{\n\t\t\t\tTraits: Traits(`{ \"whatever\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\t},\n\t\t\terr: `I[#/traits] S[#/properties/traits/additionalProperties] additionalProperties \"whatever\" not allowed`,\n\t\t},\n\t\t{\n\t\t\ti: &Identity{\n\t\t\t\tSchemaID: \"whatever\",\n\t\t\t\tTraits:   Traits(`{ \"whatever\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\ti: &Identity{\n\t\t\t\tSchemaID: \"whatever\",\n\t\t\t\tTraits:   Traits(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\t},\n\t\t\terr: `I[#/traits] S[#/properties/traits/additionalProperties] additionalProperties \"firstName\" not allowed`,\n\t\t},\n\t\t{\n\t\t\ti: &Identity{\n\t\t\t\tSchemaID: \"unreachable-url\",\n\t\t\t\tTraits:   Traits(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\t},\n\t\t\terr: \"Invalid configuration\",\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tctx := context.WithValue(t.Context(), httploader.ContextKey, httpx.NewResilientClient())\n\t\t\terr := v.Validate(ctx, tc.i)\n\t\t\tif tc.err == \"\" {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t} else {\n\t\t\t\trequire.EqualError(t, err, tc.err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "install.sh",
    "content": "#!/bin/sh\nset -e\n# Code generated by godownloader on 2020-07-08T20:40:41Z. DO NOT EDIT.\n#\n\nusage() {\n  this=$1\n  cat <<EOF\n$this: download go binaries for ory/kratos\n\nUsage: $this [-b] bindir [-d] [tag]\n  -b sets bindir or installation directory, Defaults to ./bin\n  -d turns on debug logging\n   [tag] is a tag from\n   https://github.com/ory/kratos/releases\n   If tag is missing, then the latest will be used.\n\n Generated by godownloader\n  https://github.com/goreleaser/godownloader\n\nEOF\n  exit 2\n}\n\nparse_args() {\n  #BINDIR is ./bin unless set be ENV\n  # over-ridden by flag below\n\n  BINDIR=${BINDIR:-./bin}\n  while getopts \"b:dh?x\" arg; do\n    case \"$arg\" in\n      b) BINDIR=\"$OPTARG\" ;;\n      d) log_set_priority 10 ;;\n      h | \\?) usage \"$0\" ;;\n      x) set -x ;;\n    esac\n  done\n  shift $((OPTIND - 1))\n  TAG=$1\n}\n# this function wraps all the destructive operations\n# if a curl|bash cuts off the end of the script due to\n# network, either nothing will happen or will syntax error\n# out preventing half-done work\nexecute() {\n  tmpdir=$(mktemp -d)\n  log_debug \"downloading files into ${tmpdir}\"\n  http_download \"${tmpdir}/${TARBALL}\" \"${TARBALL_URL}\"\n  http_download \"${tmpdir}/${CHECKSUM}\" \"${CHECKSUM_URL}\"\n  hash_sha256_verify \"${tmpdir}/${TARBALL}\" \"${tmpdir}/${CHECKSUM}\"\n  srcdir=\"${tmpdir}\"\n  (cd \"${tmpdir}\" && untar \"${TARBALL}\")\n  test ! -d \"${BINDIR}\" && install -d \"${BINDIR}\"\n  for binexe in $BINARIES; do\n    if [ \"$OS\" = \"windows\" ]; then\n      binexe=\"${binexe}.exe\"\n    fi\n    install \"${srcdir}/${binexe}\" \"${BINDIR}/\"\n    log_info \"installed ${BINDIR}/${binexe}\"\n  done\n  rm -rf \"${tmpdir}\"\n}\nget_binaries() {\n  case \"$PLATFORM\" in\n    darwin/386) BINARIES=\"kratos\" ;;\n    darwin/amd64) BINARIES=\"kratos kratos\" ;;\n    darwin/arm64) BINARIES=\"kratos\" ;;\n    darwin/armv5) BINARIES=\"kratos\" ;;\n    darwin/armv6) BINARIES=\"kratos\" ;;\n    darwin/armv7) BINARIES=\"kratos\" ;;\n    freebsd/386) BINARIES=\"kratos\" ;;\n    freebsd/amd64) BINARIES=\"kratos\" ;;\n    freebsd/arm64) BINARIES=\"kratos\" ;;\n    freebsd/armv5) BINARIES=\"kratos\" ;;\n    freebsd/armv6) BINARIES=\"kratos\" ;;\n    freebsd/armv7) BINARIES=\"kratos\" ;;\n    linux/386) BINARIES=\"kratos\" ;;\n    linux/amd64) BINARIES=\"kratos kratos kratos\" ;;\n    linux/arm64) BINARIES=\"kratos\" ;;\n    linux/armv5) BINARIES=\"kratos\" ;;\n    linux/armv6) BINARIES=\"kratos\" ;;\n    linux/armv7) BINARIES=\"kratos\" ;;\n    windows/386) BINARIES=\"kratos\" ;;\n    windows/amd64) BINARIES=\"kratos kratos\" ;;\n    windows/arm64) BINARIES=\"kratos\" ;;\n    windows/armv5) BINARIES=\"kratos\" ;;\n    windows/armv6) BINARIES=\"kratos\" ;;\n    windows/armv7) BINARIES=\"kratos\" ;;\n    *)\n      log_crit \"platform $PLATFORM is not supported.  Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new\"\n      exit 1\n      ;;\n  esac\n}\ntag_to_version() {\n  if [ -z \"${TAG}\" ]; then\n    log_info \"checking GitHub for latest tag\"\n  else\n    log_info \"checking GitHub for tag '${TAG}'\"\n  fi\n  REALTAG=$(github_release \"$OWNER/$REPO\" \"${TAG}\") && true\n  if test -z \"$REALTAG\"; then\n    log_crit \"unable to find '${TAG}' - use 'latest' or see https://github.com/${PREFIX}/releases for details\"\n    exit 1\n  fi\n  # if version starts with 'v', remove it\n  TAG=\"$REALTAG\"\n  VERSION=${TAG#v}\n}\nadjust_format() {\n  # change format (tar.gz or zip) based on OS\n  case ${OS} in\n    windows) FORMAT=zip ;;\n  esac\n  true\n}\nadjust_os() {\n  # adjust archive name based on OS\n  case ${OS} in\n    386) OS=32bit ;;\n    amd64) OS=64bit ;;\n    arm) OS=arm32 ;;\n    darwin) OS=macos ;;\n  esac\n  true\n}\nadjust_arch() {\n  # adjust archive name based on ARCH\n  case ${ARCH} in\n    386) ARCH=32bit ;;\n    amd64) ARCH=64bit ;;\n    arm) ARCH=arm32 ;;\n    darwin) ARCH=macos ;;\n  esac\n  true\n}\n\ncat /dev/null <<EOF\n------------------------------------------------------------------------\nhttps://github.com/client9/shlib - portable posix shell functions\nPublic domain - http://unlicense.org\nhttps://github.com/client9/shlib/blob/master/LICENSE.md\nbut credit (and pull requests) appreciated.\n------------------------------------------------------------------------\nEOF\nis_command() {\n  command -v \"$1\" >/dev/null\n}\nechoerr() {\n  echo \"$@\" 1>&2\n}\nlog_prefix() {\n  echo \"$0\"\n}\n_logp=6\nlog_set_priority() {\n  _logp=\"$1\"\n}\nlog_priority() {\n  if test -z \"$1\"; then\n    echo \"$_logp\"\n    return\n  fi\n  [ \"$1\" -le \"$_logp\" ]\n}\nlog_tag() {\n  case $1 in\n    0) echo \"emerg\" ;;\n    1) echo \"alert\" ;;\n    2) echo \"crit\" ;;\n    3) echo \"err\" ;;\n    4) echo \"warning\" ;;\n    5) echo \"notice\" ;;\n    6) echo \"info\" ;;\n    7) echo \"debug\" ;;\n    *) echo \"$1\" ;;\n  esac\n}\nlog_debug() {\n  log_priority 7 || return 0\n  echoerr \"$(log_prefix)\" \"$(log_tag 7)\" \"$@\"\n}\nlog_info() {\n  log_priority 6 || return 0\n  echoerr \"$(log_prefix)\" \"$(log_tag 6)\" \"$@\"\n}\nlog_err() {\n  log_priority 3 || return 0\n  echoerr \"$(log_prefix)\" \"$(log_tag 3)\" \"$@\"\n}\nlog_crit() {\n  log_priority 2 || return 0\n  echoerr \"$(log_prefix)\" \"$(log_tag 2)\" \"$@\"\n}\nuname_os() {\n  os=$(uname -s | tr '[:upper:]' '[:lower:]')\n  case \"$os\" in\n    cygwin_nt*) os=\"windows\" ;;\n    mingw*) os=\"windows\" ;;\n    msys_nt*) os=\"windows\" ;;\n  esac\n  echo \"$os\"\n}\nuname_arch() {\n  arch=$(uname -m)\n  case $arch in\n    x86_64) arch=\"amd64\" ;;\n    x86) arch=\"386\" ;;\n    i686) arch=\"386\" ;;\n    i386) arch=\"386\" ;;\n    aarch64) arch=\"arm64\" ;;\n    armv5*) arch=\"armv5\" ;;\n    armv6*) arch=\"armv6\" ;;\n    armv7*) arch=\"armv7\" ;;\n  esac\n  echo ${arch}\n}\nuname_os_check() {\n  os=$(uname_os)\n  case \"$os\" in\n    darwin) return 0 ;;\n    dragonfly) return 0 ;;\n    freebsd) return 0 ;;\n    linux) return 0 ;;\n    android) return 0 ;;\n    nacl) return 0 ;;\n    netbsd) return 0 ;;\n    openbsd) return 0 ;;\n    plan9) return 0 ;;\n    solaris) return 0 ;;\n    windows) return 0 ;;\n  esac\n  log_crit \"uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib\"\n  return 1\n}\nuname_arch_check() {\n  arch=$(uname_arch)\n  case \"$arch\" in\n    386) return 0 ;;\n    amd64) return 0 ;;\n    arm64) return 0 ;;\n    armv5) return 0 ;;\n    armv6) return 0 ;;\n    armv7) return 0 ;;\n    ppc64) return 0 ;;\n    ppc64le) return 0 ;;\n    mips) return 0 ;;\n    mipsle) return 0 ;;\n    mips64) return 0 ;;\n    mips64le) return 0 ;;\n    s390x) return 0 ;;\n    amd64p32) return 0 ;;\n  esac\n  log_crit \"uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib\"\n  return 1\n}\nuntar() {\n  tarball=$1\n  case \"${tarball}\" in\n    *.tar.gz | *.tgz) tar --no-same-owner -xzf \"${tarball}\" ;;\n    *.tar) tar --no-same-owner -xf \"${tarball}\" ;;\n    *.zip) unzip \"${tarball}\" ;;\n    *)\n      log_err \"untar unknown archive format for ${tarball}\"\n      return 1\n      ;;\n  esac\n}\nhttp_download_curl() {\n  local_file=$1\n  source_url=$2\n  header=$3\n  if [ -z \"$header\" ]; then\n    code=$(curl --retry 7 --retry-connrefused -w '%{http_code}' -sL -o \"$local_file\" \"$source_url\")\n  else\n    code=$(curl --retry 7 --retry-connrefused -w '%{http_code}' -sL -H \"$header\" -o \"$local_file\" \"$source_url\")\n  fi\n  if [ \"$code\" != \"200\" ]; then\n    log_debug \"http_download_curl received HTTP status $code\"\n    return 1\n  fi\n  return 0\n}\nhttp_download_wget() {\n  local_file=$1\n  source_url=$2\n  header=$3\n  if [ -z \"$header\" ]; then\n    wget -q -O \"$local_file\" \"$source_url\"\n  else\n    wget -q --header \"$header\" -O \"$local_file\" \"$source_url\"\n  fi\n}\nhttp_download() {\n  log_debug \"http_download $2\"\n  if is_command curl; then\n    http_download_curl \"$@\"\n    return\n  elif is_command wget; then\n    http_download_wget \"$@\"\n    return\n  fi\n  log_crit \"http_download unable to find wget or curl\"\n  return 1\n}\nhttp_copy() {\n  tmp=$(mktemp)\n  http_download \"${tmp}\" \"$1\" \"$2\" || return 1\n  body=$(cat \"$tmp\")\n  rm -f \"${tmp}\"\n  echo \"$body\"\n}\ngithub_release() {\n  owner_repo=$1\n  version=$2\n  test -z \"$version\" && version=\"latest\"\n  giturl=\"https://github.com/${owner_repo}/releases/${version}\"\n  json=$(http_copy \"$giturl\" \"Accept:application/json\")\n  test -z \"$json\" && return 1\n  version=$(echo \"$json\" | tr -s '\\n' ' ' | sed 's/.*\"tag_name\":\"//' | sed 's/\".*//')\n  test -z \"$version\" && return 1\n  echo \"$version\"\n}\nhash_sha256() {\n  TARGET=${1:-/dev/stdin}\n  if is_command gsha256sum; then\n    hash=$(gsha256sum \"$TARGET\") || return 1\n    echo \"$hash\" | cut -d ' ' -f 1\n  elif is_command sha256sum; then\n    hash=$(sha256sum \"$TARGET\") || return 1\n    echo \"$hash\" | cut -d ' ' -f 1\n  elif is_command shasum; then\n    hash=$(shasum -a 256 \"$TARGET\" 2>/dev/null) || return 1\n    echo \"$hash\" | cut -d ' ' -f 1\n  elif is_command openssl; then\n    hash=$(openssl -dst openssl dgst -sha256 \"$TARGET\") || return 1\n    echo \"$hash\" | cut -d ' ' -f a\n  else\n    log_crit \"hash_sha256 unable to find command to compute sha-256 hash\"\n    return 1\n  fi\n}\nhash_sha256_verify() {\n  TARGET=$1\n  checksums=$2\n  if [ -z \"$checksums\" ]; then\n    log_err \"hash_sha256_verify checksum file not specified in arg2\"\n    return 1\n  fi\n  BASENAME=${TARGET##*/}\n  want=$(grep \"${BASENAME}\" \"${checksums}\" 2>/dev/null | tr '\\t' ' ' | cut -d ' ' -f 1)\n  if [ -z \"$want\" ]; then\n    log_err \"hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'\"\n    return 1\n  fi\n  got=$(hash_sha256 \"$TARGET\")\n  if [ \"$want\" != \"$got\" ]; then\n    log_err \"hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got\"\n    return 1\n  fi\n}\ncat /dev/null <<EOF\n------------------------------------------------------------------------\nEnd of functions from https://github.com/client9/shlib\n------------------------------------------------------------------------\nEOF\n\nPROJECT_NAME=\"kratos\"\nOWNER=ory\nREPO=\"kratos\"\nBINARY=kratos\nFORMAT=tar.gz\nOS=$(uname_os)\nARCH=$(uname_arch)\nPREFIX=\"$OWNER/$REPO\"\n\n# use in logging routines\nlog_prefix() {\n\techo \"$PREFIX\"\n}\nPLATFORM=\"${OS}/${ARCH}\"\nGITHUB_DOWNLOAD=https://github.com/${OWNER}/${REPO}/releases/download\n\nuname_os_check \"$OS\"\nuname_arch_check \"$ARCH\"\n\nparse_args \"$@\"\n\nget_binaries\n\ntag_to_version\n\nadjust_format\n\nadjust_os\n\nadjust_arch\n\nlog_info \"found version: ${VERSION} for ${TAG}/${OS}/${ARCH}\"\n\nNAME=${PROJECT_NAME}_${VERSION}-${OS}_${ARCH}\nTARBALL=${NAME}.${FORMAT}\nTARBALL_URL=${GITHUB_DOWNLOAD}/${TAG}/${TARBALL}\nCHECKSUM=checksums.txt\nCHECKSUM_URL=${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM}\n\n\nexecute\n"
  },
  {
    "path": "main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// package main is the entry point for kratos.\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/ory/kratos/cmd\"\n)\n\nfunc main() {\n\tos.Exit(cmd.Execute())\n}\n"
  },
  {
    "path": "openapitools.json",
    "content": "{\n  \"$schema\": \"node_modules/@openapitools/openapi-generator-cli/config.schema.json\",\n  \"spaces\": 2,\n  \"generator-cli\": {\n    \"version\": \"7.12.0\"\n  }\n}\n"
  },
  {
    "path": "oryx/.gitignore",
    "content": ".bin\nvendor\n.idea\ncoverage.txt\nnode_modules/\n**/*.pprof\n**/memstats.*.txt\n.vscode/settings.json\n"
  },
  {
    "path": "oryx/.goimportsignore",
    "content": "vendor/"
  },
  {
    "path": "oryx/.golangci.yml",
    "content": "version: \"2\"\n\nlinters:\n  enable:\n    - gosec\n    - errcheck\n    - ineffassign\n    - staticcheck\n    - unused\n"
  },
  {
    "path": "oryx/.nancy-ignore",
    "content": ""
  },
  {
    "path": "oryx/.prettierignore",
    "content": ".github/pull_request_template.md\nclidoc/testdata/\nhealthx/openapi/patch.yaml\n.snapshots\nfixtures\n"
  },
  {
    "path": "oryx/.reference-ignore",
    "content": "**/node_modules\ndocs\nCHANGELOG.md\n"
  },
  {
    "path": "oryx/.schemas/corsx/viper.schema.json",
    "content": "{\n  \"$id\": \"https://raw.githubusercontent.com/ory/x/master/.schemas/corsx/viper.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Cross Origin Resource Sharing (CORS)\",\n  \"description\": \"Configure [Cross Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) using the following options.\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"enabled\": {\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"title\": \"Enable CORS\",\n      \"description\": \"If set to true, CORS will be enabled and preflight-requests (OPTION) will be answered.\"\n    },\n    \"allowed_origins\": {\n      \"title\": \"Allowed Origins\",\n      \"description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality. Only one wildcard can be used per origin.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\",\n        \"minLength\": 1\n      },\n      \"default\": [\"*\"],\n      \"uniqueItems\": true,\n      \"examples\": [\n        \"https://example.com\",\n        \"https://*.example.com\",\n        \"https://*.foo.example.com\"\n      ]\n    },\n    \"allowed_methods\": {\n      \"type\": \"array\",\n      \"title\": \"Allowed HTTP Methods\",\n      \"description\": \"A list of methods the client is allowed to use with cross-domain requests.\",\n      \"items\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"GET\",\n          \"HEAD\",\n          \"POST\",\n          \"PUT\",\n          \"DELETE\",\n          \"CONNECT\",\n          \"TRACE\",\n          \"PATCH\"\n        ]\n      },\n      \"uniqueItems\": true,\n      \"default\": [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"]\n    },\n    \"allowed_headers\": {\n      \"description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n      \"title\": \"Allowed Request HTTP Headers\",\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"minLength\": 1,\n      \"uniqueItems\": true,\n      \"default\": [\"Authorization\", \"Content-Type\"]\n    },\n    \"exposed_headers\": {\n      \"description\": \"Indicates which headers are safe to expose to the API of a CORS API specification\",\n      \"title\": \"Allowed Response HTTP Headers\",\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"minLength\": 1,\n      \"uniqueItems\": true,\n      \"default\": [\"Content-Type\"]\n    },\n    \"allow_credentials\": {\n      \"type\": \"boolean\",\n      \"title\": \"Allow HTTP Credentials\",\n      \"default\": false,\n      \"description\": \"Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\"\n    },\n    \"max_age\": {\n      \"type\": \"number\",\n      \"default\": 0,\n      \"title\": \"Maximum Age\",\n      \"description\": \"Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0 which stands for no max age.\"\n    },\n    \"debug\": {\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"title\": \"Enable Debugging\",\n      \"description\": \"Set to true to debug server side CORS issues.\"\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "oryx/.schemas/logrusx/viper.schema.json",
    "content": "{\n  \"$id\": \"https://raw.githubusercontent.com/ory/x/master/.schemas/logrusx/viper.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Log\",\n  \"description\": \"Configure logging using the following options. Logging will always be sent to stdout and stderr.\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"level\": {\n      \"type\": \"string\",\n      \"default\": \"info\",\n      \"enum\": [\"panic\", \"fatal\", \"error\", \"warn\", \"info\", \"debug\"],\n      \"title\": \"Level\",\n      \"description\": \"Debug enables stack traces on errors. Can also be set using environment variable LOG_LEVEL.\"\n    },\n    \"format\": {\n      \"type\": \"string\",\n      \"default\": \"text\",\n      \"enum\": [\"text\", \"json\"],\n      \"title\": \"Format\",\n      \"description\": \"The log format can either be text or JSON.\"\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "oryx/.schemas/profilingx/viper.schema.json",
    "content": "{\n  \"$id\": \"https://raw.githubusercontent.com/ory/x/master/.schemas/profilingx/viper.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Profiling\",\n  \"description\": \"Enables CPU or memory profiling if set. For more details on profiling Go programs read [Profiling Go Programs](https://blog.golang.org/profiling-go-programs).\",\n  \"type\": \"string\",\n  \"enum\": [\"cpu\", \"mem\"]\n}\n"
  },
  {
    "path": "oryx/.schemas/tlsx/viper.schema.json",
    "content": "{\n  \"$id\": \"https://raw.githubusercontent.com/ory/x/master/.schemas/tlsx/viper.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"HTTPS\",\n  \"description\": \"Configure HTTP over TLS (HTTPS). All options can also be set using environment variables by replacing dots (`.`) with underscores (`_`) and uppercasing the key. For example, `some.prefix.tls.key.path` becomes `export SOME_PREFIX_TLS_KEY_PATH`. If all keys are left undefined, TLS will be disabled.\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"definitions\": {\n    \"source\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"path\": {\n          \"title\": \"Path to PEM-encoded Fle\",\n          \"type\": \"string\",\n          \"examples\": [\"path/to/file.pem\"]\n        },\n        \"base64\": {\n          \"title\": \"Base64 Encoded Inline\",\n          \"description\": \"The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n          ]\n        }\n      }\n    }\n  },\n  \"properties\": {\n    \"key\": {\n      \"title\": \"Private Key (PEM)\",\n      \"allOf\": [\n        {\n          \"$ref\": \"#/definitions/source\"\n        }\n      ]\n    },\n    \"cert\": {\n      \"title\": \"TLS Certificate (PEM)\",\n      \"allOf\": [\n        {\n          \"$ref\": \"#/definitions/source\"\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "oryx/Makefile",
    "content": "SHELL=/bin/bash -o pipefail\n\nexport PATH := .bin:${PATH}\n\n.bin/ory: Makefile\n\tcurl --retry 7 --retry-connrefused https://raw.githubusercontent.com/ory/meta/master/install.sh | bash -s -- -b .bin ory v0.2.2\n\ttouch .bin/ory\n\n.PHONY: format\nformat: .bin/ory node_modules\n\t.bin/ory dev headers copyright --type=open-source --exclude=clidoc/ --exclude=hasherx/mocks_pkdbf2_test.go --exclude=josex/ --exclude=hasherx/ --exclude=jsonnetsecure/jsonnet.go\n\tgo tool goimports -w -local github.com/ory .\n\tnpm exec -- prettier --write .\n\n.bin/golangci-lint: Makefile\n\tcurl --retry 7 --retry-connrefused -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b .bin v2.10.1\n\n.bin/licenses: Makefile\n\tcurl --retry 7 --retry-connrefused https://raw.githubusercontent.com/ory/ci/master/licenses/install | sh\n\nlicenses: .bin/licenses node_modules  # checks open-source licenses\n\t.bin/licenses\n\n.PHONY: test\ntest:\n\tmake resetdb\n\texport TEST_DATABASE_POSTGRESQL=postgres://postgres:secret@127.0.0.1:3445/hydra?sslmode=disable; export TEST_DATABASE_COCKROACHDB=cockroach://root@127.0.0.1:3446/defaultdb?sslmode=disable; export TEST_DATABASE_MYSQL='mysql://root:secret@tcp(127.0.0.1:3444)/mysql?parseTime=true&multiStatements=true'; go test -count=1 -tags sqlite ./...\n\n.PHONY: resetdb\nresetdb:\n\tdocker kill hydra_test_database_mysql || true\n\tdocker kill hydra_test_database_postgres || true\n\tdocker kill hydra_test_database_cockroach || true\n\tdocker rm -f hydra_test_database_mysql || true\n\tdocker rm -f hydra_test_database_postgres || true\n\tdocker rm -f hydra_test_database_cockroach || true\n\tdocker run --rm --name hydra_test_database_mysql -p 3444:3306 -e MYSQL_ROOT_PASSWORD=secret -d mysql:8.0\n\tdocker run --rm --name hydra_test_database_postgres -p 3445:5432 -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=hydra -d postgres:11.8\n\tdocker run --rm --name hydra_test_database_cockroach -p 3446:26257 -d cockroachdb/cockroach:latest-v25.4 start-single-node --insecure\n\n.PHONY: lint\nlint: .bin/golangci-lint\n\t.bin/golangci-lint run -v ./...\n\n.PHONY: migrations-render\nmigrations-render: .bin/ory\n\tory dev pop migration render networkx/migrations/templates networkx/migrations/sql\n\n.PHONY: migrations-render-replace\nmigrations-render-replace: .bin/ory\n\tory dev pop migration render -r networkx/migrations/templates networkx/migrations/sql\n\n.PHONY: mocks\nmocks:\n\tgo tool mockgen -package hasherx_test -destination hasherx/mocks_argon2_test.go github.com/ory/x/hasherx Argon2Configurator\n\tgo tool mockgen -package hasherx_test -destination hasherx/mocks_bcrypt_test.go github.com/ory/x/hasherx BCryptConfigurator\n\tgo tool mockgen -package hasherx_test -destination hasherx/mocks_pkdbf2_test.go github.com/ory/x/hasherx PBKDF2Configurator\n\nnode_modules: package-lock.json\n\tnpm ci\n\ttouch node_modules\n"
  },
  {
    "path": "oryx/assertx/assertx.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage assertx\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc PrettifyJSONPayload(t testing.TB, payload interface{}) string {\n\tt.Helper()\n\to, err := json.MarshalIndent(payload, \"\", \"  \")\n\trequire.NoErrorf(t, err, \"%+v\", payload)\n\treturn string(o)\n}\n\nfunc EqualAsJSON(t testing.TB, expected, actual interface{}, args ...interface{}) {\n\tt.Helper()\n\tvar eb, ab bytes.Buffer\n\tif len(args) == 0 {\n\t\targs = []interface{}{PrettifyJSONPayload(t, actual)}\n\t}\n\n\trequire.NoError(t, json.NewEncoder(&eb).Encode(expected), args...)\n\trequire.NoError(t, json.NewEncoder(&ab).Encode(actual), args...)\n\tassert.JSONEq(t, strings.TrimSpace(eb.String()), strings.TrimSpace(ab.String()), args...)\n}\n\nfunc EqualAsJSONExcept(t testing.TB, expected, actual interface{}, except []string, args ...interface{}) {\n\tt.Helper()\n\tvar eb, ab bytes.Buffer\n\tif len(args) == 0 {\n\t\targs = []interface{}{PrettifyJSONPayload(t, actual)}\n\t}\n\n\trequire.NoError(t, json.NewEncoder(&eb).Encode(expected), args...)\n\trequire.NoError(t, json.NewEncoder(&ab).Encode(actual), args...)\n\n\tvar err error\n\tebs, abs := eb.String(), ab.String()\n\tfor _, k := range except {\n\t\tebs, err = sjson.Delete(ebs, k)\n\t\trequire.NoError(t, err)\n\n\t\tabs, err = sjson.Delete(abs, k)\n\t\trequire.NoError(t, err)\n\t}\n\n\tassert.JSONEq(t, strings.TrimSpace(ebs), strings.TrimSpace(abs), args...)\n}\n"
  },
  {
    "path": "oryx/cachex/ristretto.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cachex\n\nimport (\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n)\n\n// RistrettoCollector collects Ristretto cache metrics.\ntype RistrettoCollector struct {\n\tprefix      string\n\tmetricsFunc func() *ristretto.Metrics\n}\n\n// NewRistrettoCollector creates a new RistrettoCollector.\n//\n// To use this collector, you need to register it with a Prometheus registry:\n//\n//\tfunc main() {\n//\t\tcache, _ := ristretto.NewCache(&ristretto.Config{\n//\t\t\tNumCounters: 1e7,\n//\t\t\tMaxCost:     1 << 30,\n//\t\t\tBufferItems: 64,\n//\t\t})\n//\t\tcollector := NewRistrettoCollector(\"prefix_\", func() *ristretto.Metrics {\n//\t\t\treturn cache.Metrics\n//\t\t})\n//\t\tprometheus.MustRegister(collector)\n//\t}\nfunc NewRistrettoCollector(prefix string, metricsFunc func() *ristretto.Metrics) *RistrettoCollector {\n\treturn &RistrettoCollector{\n\t\tprefix:      prefix,\n\t\tmetricsFunc: metricsFunc,\n\t}\n}\n\n// Describe sends the super-set of all possible descriptors of metrics\n// collected by this Collector.\nfunc (c *RistrettoCollector) Describe(ch chan<- *prometheus.Desc) {\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_hits\", \"Total number of cache hits\", nil, nil)\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_misses\", \"Total number of cache misses\", nil, nil)\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_ratio\", \"Cache hit ratio\", nil, nil)\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_keys_added\", \"Total number of keys added to the cache\", nil, nil)\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_cost_added\", \"Total cost of keys added to the cache\", nil, nil)\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_keys_evicted\", \"Total number of keys evicted from the cache\", nil, nil)\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_cost_evicted\", \"Total cost of keys evicted from the cache\", nil, nil)\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_sets_dropped\", \"Total number of sets dropped\", nil, nil)\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_sets_rejected\", \"Total number of sets rejected\", nil, nil)\n\tch <- prometheus.NewDesc(c.prefix+\"ristretto_gets_kept\", \"Total number of gets kept\", nil, nil)\n}\n\n// Collect is called by the Prometheus registry when collecting metrics.\nfunc (c *RistrettoCollector) Collect(ch chan<- prometheus.Metric) {\n\tmetrics := c.metricsFunc()\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_hits\", \"Total number of cache hits\", nil, nil), prometheus.GaugeValue, float64(metrics.Hits()))\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_misses\", \"Total number of cache misses\", nil, nil), prometheus.GaugeValue, float64(metrics.Misses()))\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_ratio\", \"Cache hit ratio\", nil, nil), prometheus.GaugeValue, metrics.Ratio())\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_keys_added\", \"Total number of keys added to the cache\", nil, nil), prometheus.GaugeValue, float64(metrics.KeysAdded()))\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_cost_added\", \"Total cost of keys added to the cache\", nil, nil), prometheus.GaugeValue, float64(metrics.CostAdded()))\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_keys_evicted\", \"Total number of keys evicted from the cache\", nil, nil), prometheus.GaugeValue, float64(metrics.KeysEvicted()))\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_cost_evicted\", \"Total cost of keys evicted from the cache\", nil, nil), prometheus.GaugeValue, float64(metrics.CostEvicted()))\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_sets_dropped\", \"Total number of sets dropped\", nil, nil), prometheus.GaugeValue, float64(metrics.SetsDropped()))\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_sets_rejected\", \"Total number of sets rejected\", nil, nil), prometheus.GaugeValue, float64(metrics.SetsRejected()))\n\tch <- prometheus.MustNewConstMetric(prometheus.NewDesc(c.prefix+\"ristretto_gets_kept\", \"Total number of gets kept\", nil, nil), prometheus.GaugeValue, float64(metrics.GetsKept()))\n}\n"
  },
  {
    "path": "oryx/castx/castx.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage castx\n\nimport (\n\t\"encoding/csv\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/spf13/cast\"\n)\n\n// ToFloatSlice casts an interface to a []float64 type.\nfunc ToFloatSlice(i interface{}) []float64 {\n\tf, _ := ToFloatSliceE(i)\n\treturn f\n}\n\n// ToFloatSliceE casts an interface to a []float64 type.\nfunc ToFloatSliceE(i interface{}) ([]float64, error) {\n\tif i == nil {\n\t\treturn []float64{}, fmt.Errorf(\"unable to cast %#v of type %T to []float64\", i, i)\n\t}\n\n\tswitch v := i.(type) {\n\tcase []float64:\n\t\treturn v, nil\n\t}\n\n\tkind := reflect.TypeOf(i).Kind()\n\tswitch kind {\n\tcase reflect.Slice, reflect.Array:\n\t\ts := reflect.ValueOf(i)\n\t\ta := make([]float64, s.Len())\n\t\tfor j := range a {\n\t\t\tval, err := cast.ToFloat64E(s.Index(j).Interface())\n\t\t\tif err != nil {\n\t\t\t\treturn []float64{}, fmt.Errorf(\"unable to cast %#v of type %T to []float64\", i, i)\n\t\t\t}\n\t\t\ta[j] = val\n\t\t}\n\t\treturn a, nil\n\tdefault:\n\t\treturn []float64{}, fmt.Errorf(\"unable to cast %#v of type %T to []float64\", i, i)\n\t}\n}\n\n// ToStringSlice casts an interface to a []string type and respects comma-separated values.\nfunc ToStringSlice(i interface{}) []string {\n\ts, _ := ToStringSliceE(i)\n\treturn s\n}\n\n// ToStringSliceE casts an interface to a []string type and respects comma-separated values.\nfunc ToStringSliceE(i interface{}) ([]string, error) {\n\tswitch s := i.(type) {\n\tcase string:\n\t\treturn parseCSV(s)\n\t}\n\n\treturn cast.ToStringSliceE(i)\n}\n\nfunc parseCSV(v string) ([]string, error) {\n\treturn csv.NewReader(strings.NewReader(v)).Read()\n}\n"
  },
  {
    "path": "oryx/clidoc/generate.go",
    "content": "package clidoc\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/spf13/cobra\"\n)\n\n// Generate generates markdown documentation for a cobra command and its children.\nfunc Generate(cmd *cobra.Command, args []string) error {\n\tif len(args) != 1 {\n\t\treturn errors.New(\"command expects one argument which is the path to the output directory\")\n\t}\n\n\treturn generate(cmd, args[0])\n}\n\nfunc trimExt(s string) string {\n\treturn strings.ReplaceAll(strings.TrimSuffix(s, filepath.Ext(s)), \"_\", \"-\")\n}\n\nfunc generate(cmd *cobra.Command, dir string) error {\n\tcmd.DisableAutoGenTag = true\n\tfor _, c := range cmd.Commands() {\n\t\tif !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {\n\t\t\tcontinue\n\t\t}\n\t\tif err := generate(c, dir); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tbasename := strings.Replace(cmd.CommandPath(), \" \", \"-\", -1)\n\tif err := os.MkdirAll(filepath.Join(dir), 0750); err != nil {\n\t\treturn err\n\t}\n\n\tfilename := filepath.Join(dir, basename) + \".md\"\n\tf, err := os.Create(filename) //#nosec:G304\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer (func() { _ = f.Close() })()\n\n\tif _, err := io.WriteString(f, fmt.Sprintf(`---\nid: %s\ntitle: %s\ndescription: %s\n---\n\n<!--\nThis file is auto-generated.\n\nTo improve this file please make your change against the appropriate \"./cmd/*.go\" file.\n-->\n`,\n\t\tbasename,\n\t\tcmd.CommandPath(),\n\t\tcmd.CommandPath(),\n\t)); err != nil {\n\t\treturn err\n\t}\n\n\tvar b bytes.Buffer\n\tif err := GenMarkdownCustom(cmd, &b, trimExt); err != nil {\n\t\treturn err\n\t}\n\n\t_, err = f.WriteString(b.String())\n\treturn err\n}\n"
  },
  {
    "path": "oryx/clidoc/md_docs.go",
    "content": "//Copyright 2015 Red Hat Inc. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage clidoc\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"html\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/ory/x/cmdx\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nfunc printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) error {\n\tflags := cmd.NonInheritedFlags()\n\tflags.SetOutput(buf)\n\tif flags.HasAvailableFlags() {\n\t\tbuf.WriteString(\"### Options\\n\\n```\\n\")\n\t\tflags.PrintDefaults()\n\t\tbuf.WriteString(\"```\\n\\n\")\n\t}\n\n\tparentFlags := cmd.InheritedFlags()\n\tparentFlags.SetOutput(buf)\n\tif parentFlags.HasAvailableFlags() {\n\t\tbuf.WriteString(\"### Options inherited from parent commands\\n\\n```\\n\")\n\t\tparentFlags.PrintDefaults()\n\t\tbuf.WriteString(\"```\\n\\n\")\n\t}\n\treturn nil\n}\n\n// GenMarkdown creates markdown output.\nfunc GenMarkdown(cmd *cobra.Command, w io.Writer) error {\n\treturn GenMarkdownCustom(cmd, w, func(s string) string { return s })\n}\n\n// GenMarkdownCustom creates custom markdown output.\nfunc GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error {\n\tcmd.InitDefaultHelpCmd()\n\tcmd.InitDefaultHelpFlag()\n\n\tbuf := new(bytes.Buffer)\n\tname := cmd.CommandPath()\n\n\tbuf.WriteString(\"## \" + html.EscapeString(name) + \"\\n\\n\")\n\tbuf.WriteString(fenceIndentedBlocks(cmd.Short) + \"\\n\\n\")\n\tif len(cmd.Long) > 0 {\n\t\tbuf.WriteString(\"### Synopsis\\n\\n\")\n\t\tlong, err := cmdx.TemplateCommandField(cmd, cmd.Long)\n\t\tif err != nil {\n\t\t\tbuf.WriteString(fmt.Sprintf(\"<!-- Error rendering cmd.Long: %s -->\\n\\n\", err.Error()))\n\t\t\tlong = cmd.Long\n\t\t}\n\t\tbuf.WriteString(fenceIndentedBlocks(long) + \"\\n\\n\")\n\t}\n\n\tif cmd.Runnable() {\n\t\tbuf.WriteString(fmt.Sprintf(\"```\\n%s\\n```\\n\\n\", cmd.UseLine()))\n\t}\n\n\tif len(cmd.Example) > 0 {\n\t\tbuf.WriteString(\"### Examples\\n\\n\")\n\t\texample, err := cmdx.TemplateCommandField(cmd, cmd.Example)\n\t\tif err != nil {\n\t\t\tbuf.WriteString(fmt.Sprintf(\"<!-- Error rendering cmd.Example: %s -->\\n\\n\", err.Error()))\n\t\t\texample = cmd.Example\n\t\t}\n\t\tbuf.WriteString(fmt.Sprintf(\"```\\n%s\\n```\\n\\n\", example))\n\t}\n\n\tif err := printOptions(buf, cmd, name); err != nil {\n\t\treturn err\n\t}\n\tif hasSeeAlso(cmd) {\n\t\tbuf.WriteString(\"### See also\\n\\n\")\n\t\tif cmd.HasParent() {\n\t\t\tparent := cmd.Parent()\n\t\t\tpname := parent.CommandPath()\n\t\t\tlink := pname + \".md\"\n\t\t\tlink = strings.Replace(link, \" \", \"_\", -1)\n\t\t\tshort := parent.Short\n\t\t\tif short != \"\" {\n\t\t\t\tshort = fmt.Sprintf(\" %s\", short)\n\t\t\t}\n\t\t\tbuf.WriteString(fmt.Sprintf(\"* [%s](%s)%s\\n\", pname, linkHandler(link), short))\n\t\t\tcmd.VisitParents(func(c *cobra.Command) {\n\t\t\t\tif c.DisableAutoGenTag {\n\t\t\t\t\tcmd.DisableAutoGenTag = c.DisableAutoGenTag\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tchildren := cmd.Commands()\n\t\tsort.Sort(byName(children))\n\n\t\tfor _, child := range children {\n\t\t\tif !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcname := name + \" \" + child.Name()\n\t\t\tlink := cname + \".md\"\n\t\t\tlink = strings.Replace(link, \" \", \"_\", -1)\n\t\t\tshort := child.Short\n\t\t\tif short != \"\" {\n\t\t\t\tshort = fmt.Sprintf(\" - %s\", short)\n\t\t\t}\n\t\t\tbuf.WriteString(fmt.Sprintf(\"* [%s](%s)\\t%s\\n\", cname, linkHandler(link), short))\n\t\t}\n\t\tbuf.WriteString(\"\\n\")\n\t}\n\n\t_, err := buf.WriteTo(w)\n\treturn err\n}\n\n// GenMarkdownTree will generate a markdown page for this command and all\n// descendants in the directory given. The header may be nil.\n// This function may not work correctly if your command names have `-` in them.\n// If you have `cmd` with two subcmds, `sub` and `sub-third`,\n// and `sub` has a subcommand called `third`, it is undefined which\n// help output will be in the file `cmd-sub-third.1`.\nfunc GenMarkdownTree(cmd *cobra.Command, dir string) error {\n\tidentity := func(s string) string { return s }\n\temptyStr := func(s string) string { return \"\" }\n\treturn GenMarkdownTreeCustom(cmd, dir, emptyStr, identity)\n}\n\n// GenMarkdownTreeCustom is the the same as GenMarkdownTree, but\n// with custom filePrepender and linkHandler.\nfunc GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {\n\tfor _, c := range cmd.Commands() {\n\t\tif !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {\n\t\t\tcontinue\n\t\t}\n\t\tif err := GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tbasename := strings.Replace(cmd.CommandPath(), \" \", \"_\", -1) + \".md\"\n\tfilename := filepath.Join(dir, basename)\n\tf, err := os.Create(filename) //#nosec:G304) //#nosec:G304\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer (func() { _ = f.Close() })()\n\n\tif _, err := io.WriteString(f, filePrepender(filename)); err != nil {\n\t\treturn err\n\t}\n\tif err := GenMarkdownCustom(cmd, f, linkHandler); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nvar indentedBlock = regexp.MustCompile(`(?m)^(?: {4}|\\t).*(?:\\n(?: {4}|\\t).*)*`)\n\nfunc fenceIndentedBlocks(s string) string {\n\treturn indentedBlock.ReplaceAllStringFunc(s, func(block string) string {\n\t\t// trim trailing newline to keep fence tidy\n\t\tblock = strings.TrimRight(block, \"\\n\")\n\n\t\t// de-indent exactly one level for nicer fenced output\n\t\tlines := strings.Split(block, \"\\n\")\n\t\tfor i, ln := range lines {\n\t\t\tswitch {\n\t\t\tcase strings.HasPrefix(ln, \"    \"):\n\t\t\t\tlines[i] = ln[4:]\n\t\t\tcase strings.HasPrefix(ln, \"\\t\"):\n\t\t\t\tlines[i] = ln[1:]\n\t\t\t}\n\t\t}\n\t\tb := strings.Join(lines, \"\\n\")\n\n\t\t// guard against already fenced content\n\t\tif strings.HasPrefix(strings.TrimSpace(b), \"```\") {\n\t\t\treturn block\n\t\t}\n\t\treturn \"```\\n\" + b + \"\\n```\"\n\t})\n}\n"
  },
  {
    "path": "oryx/clidoc/testdata/hydra-client-admin.md",
    "content": "---\nid: hydra-client-admin\ntitle: hydra client admin\ndescription: hydra client admin\n---\n\n<!--\nThis file is auto-generated.\n\nTo improve this file please make your change against the appropriate \"./cmd/*.go\" file.\n-->\n## hydra client admin\n\nFoo bar baz bar\n\n```\nshort with multiple\n```\n\n\n\n### Synopsis\n\nRun the admin server\n\n```\n<[some argument]>\n\t<[some argument]>\n\t\t<[some argument]>\n\t<[some argument]>\n<[some argument]>\n```\n\n\n```\nhydra client admin <args> [flags]\n```\n\n### Options\n\n```\n  -h, --help   help for admin\n```\n\n### See also\n\n* [hydra client](hydra-client) Run client commands\n\n"
  },
  {
    "path": "oryx/clidoc/testdata/hydra-client-public.md",
    "content": "---\nid: hydra-client-public\ntitle: hydra client public\ndescription: hydra client public\n---\n\n<!--\nThis file is auto-generated.\n\nTo improve this file please make your change against the appropriate \"./cmd/*.go\" file.\n-->\n## hydra client public\n\n\n\n### Synopsis\n\nRun the public server\n\n<[some argument]>\n\n\n```\nhydra client public <args> [flags]\n```\n\n### Options\n\n```\n  -h, --help   help for public\n```\n\n### See also\n\n* [hydra client](hydra-client) Run client commands\n\n"
  },
  {
    "path": "oryx/clidoc/testdata/hydra-client.md",
    "content": "---\nid: hydra-client\ntitle: hydra client\ndescription: hydra client\n---\n\n<!--\nThis file is auto-generated.\n\nTo improve this file please make your change against the appropriate \"./cmd/*.go\" file.\n-->\n## hydra client\n\nRun client commands\n\n### Synopsis\n\nManage OAuth2 clients\n\n<[some argument]>\n\n\n```\nhydra client [flags]\n```\n\n### Examples\n\n```\nhydra client --whatever\n```\n\n### Options\n\n```\n  -h, --help   help for client\n```\n\n### See also\n\n* [hydra](hydra)\n* [hydra client admin](hydra-client-admin)\t - Foo bar baz bar\n\n\tshort with multiple\n\n\n* [hydra client public](hydra-client-public)\t\n\n"
  },
  {
    "path": "oryx/clidoc/testdata/hydra-serve.md",
    "content": "---\nid: hydra-serve\ntitle: hydra serve\ndescription: hydra serve\n---\n\n<!--\nThis file is auto-generated.\n\nTo improve this file please make your change against the appropriate \"./cmd/*.go\" file.\n-->\n## hydra serve\n\n\n\n### Synopsis\n\nManage the server\n\n<[some argument]>\n\n\n```\nhydra serve [flags]\n```\n\n### Options\n\n```\n  -h, --help   help for serve\n```\n\n### See also\n\n* [hydra](hydra)\n\n"
  },
  {
    "path": "oryx/clidoc/testdata/hydra.md",
    "content": "---\nid: hydra\ntitle: hydra\ndescription: hydra\n---\n\n<!--\nThis file is auto-generated.\n\nTo improve this file please make your change against the appropriate \"./cmd/*.go\" file.\n-->\n## hydra\n\n\n\n### Synopsis\n\nA sample text\nroot\n\n<[some argument]>\n\n\n```\nhydra [flags]\n```\n\n### Options\n\n```\n  -h, --help   help for hydra\n```\n\n### See also\n\n* [hydra client](hydra-client)\t - Run client commands\n* [hydra serve](hydra-serve)\t\n\n"
  },
  {
    "path": "oryx/clidoc/util.go",
    "content": "// Copyright 2015 Red Hat Inc. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage clidoc\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\n// Test to see if we have a reason to print See Also information in docs\n// Basically this is a test for a parent command or a subcommand which is\n// both not deprecated and not the autogenerated help command.\nfunc hasSeeAlso(cmd *cobra.Command) bool {\n\tif cmd.HasParent() {\n\t\treturn true\n\t}\n\tfor _, c := range cmd.Commands() {\n\t\tif !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {\n\t\t\tcontinue\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\ntype byName []*cobra.Command\n\nfunc (s byName) Len() int           { return len(s) }\nfunc (s byName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }\nfunc (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }\n"
  },
  {
    "path": "oryx/cmdx/args.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/spf13/cobra\"\n)\n\n// MinArgs fatals if args does not satisfy min.\n// Deprecated: set cobra.MinimumNArgs on the cmd.Args field instead\nfunc MinArgs(cmd *cobra.Command, args []string, min int) {\n\tif len(args) < min {\n\t\tFatalf(`%s\n\nExpected at least %d command line arguments but only got %d.`, cmd.UsageString(), min, len(args))\n\t}\n}\n\n// ExactArgs fatals if args does not equal l.\n// Deprecated: set cobra.ExactArgs on the cmd.Args field instead\nfunc ExactArgs(cmd *cobra.Command, args []string, l int) {\n\tif len(args) < l {\n\t\tFatalf(`%s\n\nExpected exactly %d command line arguments but got %d.`, cmd.UsageString(), l, len(args))\n\t}\n}\n\n// RangeArgs fatals if args does not satisfy any of the lengths set in r.\n// Deprecated: set cobra.Ar on the cmd.RangeArgs field instead\nfunc RangeArgs(cmd *cobra.Command, args []string, r []int) {\n\tfor _, a := range r {\n\t\tif len(args) == a {\n\t\t\treturn\n\t\t}\n\t}\n\tFatalf(`%s\n\nExpected exact %v command line arguments but got %d.`, cmd.UsageString(), r, len(args))\n}\n\n// ZeroOrTwoArgs requires either no or 2 arguments.\nfunc ZeroOrTwoArgs(cmd *cobra.Command, args []string) error {\n\t// zero or exactly two args\n\tif len(args) != 0 && len(args) != 2 {\n\t\treturn fmt.Errorf(\"expected zero or two args, got %d: %+v\", len(args), args)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/cmdx/env.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\n// EnvVarExamplesHelpMessage returns a string containing documentation on how to use environment variables.\nfunc EnvVarExamplesHelpMessage(name string) string {\n\treturn `This command exposes a variety of controls via environment variables. Here are some examples on how to\nconfigure environment variables:\n\nLinux / macOS:\n\t$ export FOO=bar\n\t$ export BAZ=bar\n\t$ ` + name + ` ...\n\n\t$ FOO=bar BAZ=bar ` + name + ` ...\n\nDocker:\n\t$ docker run -e FOO=bar -e BAZ=bar ...\n\nWindows (cmd):\n\t> set FOO=bar\n\t> set BAZ=bar\n\t> ` + name + ` ...\n\nWindows (powershell):\n\t> $env:FOO = \"bar\"\n\t> $env:BAZ = \"bar\"\n\t> ` + name + `\n`\n}\n"
  },
  {
    "path": "oryx/cmdx/helper.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"testing\"\n\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/pflag\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/logrusx\"\n)\n\nvar (\n\t// ErrNilDependency is returned if a dependency is missing.\n\tErrNilDependency = fmt.Errorf(\"a dependency was expected to be defined but is nil. Please open an issue with the stack trace\")\n\t// ErrNoPrintButFail is returned to detect a failure state that was already reported to the user in some way\n\tErrNoPrintButFail = fmt.Errorf(\"this error should never be printed\")\n\n\tdebugStdout, debugStderr = io.Discard, io.Discard\n)\n\nfunc init() {\n\tif os.Getenv(\"DEBUG\") != \"\" {\n\t\tdebugStdout = os.Stdout\n\t\tdebugStderr = os.Stderr\n\t}\n}\n\n// FailSilently is supposed to be used within a commands RunE function.\n// It silences cobras error handling and returns the ErrNoPrintButFail error.\nfunc FailSilently(cmd *cobra.Command) error {\n\tcmd.SilenceErrors = true\n\tcmd.SilenceUsage = true\n\treturn errors.WithStack(ErrNoPrintButFail)\n}\n\n// Must fatals with the optional message if err is not nil.\n// Deprecated: do not use this function in commands, as it makes it impossible to test them. Instead, return the error.\nfunc Must(err error, message string, args ...interface{}) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\t_, _ = fmt.Fprintf(os.Stderr, message+\"\\n\", args...)\n\tos.Exit(1)\n}\n\n// CheckResponse fatals if err is nil or the response.StatusCode does not match the expectedStatusCode\n// Deprecated: do not use this function in commands, as it makes it impossible to test them. Instead, return the error.\nfunc CheckResponse(err error, expectedStatusCode int, response *http.Response) {\n\tMust(err, \"Command failed because error occurred: %s\", err)\n\n\tif response.StatusCode != expectedStatusCode {\n\t\tout, err := io.ReadAll(response.Body)\n\t\tif err != nil {\n\t\t\tout = []byte{}\n\t\t}\n\t\tpretty, err := json.MarshalIndent(json.RawMessage(out), \"\", \"\\t\")\n\t\tif err == nil {\n\t\t\tout = pretty\n\t\t}\n\n\t\tFatalf(\n\t\t\t`Command failed because status code %d was expected but code %d was received.\n\nResponse payload:\n\n%s`,\n\t\t\texpectedStatusCode,\n\t\t\tresponse.StatusCode,\n\t\t\tout,\n\t\t)\n\t}\n}\n\n// FormatResponse takes an object and prints a json.MarshalIdent version of it or fatals.\n// Deprecated: do not use this function in commands, as it makes it impossible to test them. Instead, return the error.\nfunc FormatResponse(o interface{}) string {\n\tout, err := json.MarshalIndent(o, \"\", \"\\t\")\n\tMust(err, `Command failed because an error occurred while prettifying output: %s`, err)\n\treturn string(out)\n}\n\n// Fatalf prints to os.Stderr and exists with code 1.\n// Deprecated: do not use this function in commands, as it makes it impossible to test them. Instead, return the error.\nfunc Fatalf(message string, args ...interface{}) {\n\tif len(args) > 0 {\n\t\t_, _ = fmt.Fprintf(os.Stderr, message+\"\\n\", args...)\n\t} else {\n\t\t_, _ = fmt.Fprintln(os.Stderr, message)\n\t}\n\tos.Exit(1)\n}\n\n// ExpectDependency expects every dependency to be not nil or it fatals.\n// Deprecated: do not use this function in commands, as it makes it impossible to test them. Instead, return the error.\nfunc ExpectDependency(logger *logrusx.Logger, dependencies ...interface{}) {\n\tif logger == nil {\n\t\tpanic(\"missing logger for dependency check\")\n\t}\n\tfor _, d := range dependencies {\n\t\tif d == nil {\n\t\t\tlogger.WithError(errors.WithStack(ErrNilDependency)).Fatalf(\"A fatal issue occurred.\")\n\t\t}\n\t}\n}\n\n// CallbackWriter will execute each callback once the message is received.\n// The full matched message is passed to the callback. An error returned from the callback is returned by Write.\ntype CallbackWriter struct {\n\tCallbacks map[string]func([]byte) error\n\tbuf       bytes.Buffer\n}\n\nfunc (c *CallbackWriter) Write(msg []byte) (int, error) {\n\tfor p, cb := range c.Callbacks {\n\t\tif bytes.Contains(msg, []byte(p)) {\n\t\t\tif err := cb(msg); err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t}\n\t}\n\treturn c.buf.Write(msg)\n}\n\nfunc (c *CallbackWriter) String() string {\n\treturn c.buf.String()\n}\n\nvar _ io.Writer = (*CallbackWriter)(nil)\n\nfunc prepareCmd(cmd *cobra.Command, stdIn io.Reader, stdOut, stdErr io.Writer, args []string) {\n\tcmd.SetIn(stdIn)\n\touts := []io.Writer{debugStdout}\n\tif stdOut != nil {\n\t\touts = append(outs, stdOut)\n\t}\n\tcmd.SetOut(io.MultiWriter(outs...))\n\terrs := []io.Writer{debugStderr}\n\tif stdErr != nil {\n\t\terrs = append(errs, stdErr)\n\t}\n\tcmd.SetErr(io.MultiWriter(errs...))\n\n\tif args == nil {\n\t\targs = []string{}\n\t}\n\tcmd.SetArgs(args)\n}\n\n// ExecBackgroundCtx runs the cobra command in the background.\nfunc ExecBackgroundCtx(ctx context.Context, cmd *cobra.Command, stdIn io.Reader, stdOut, stdErr io.Writer, args ...string) *errgroup.Group {\n\tprepareCmd(cmd, stdIn, stdOut, stdErr, args)\n\n\teg := &errgroup.Group{}\n\teg.Go(func() error {\n\t\tdefer cmd.SetIn(nil)\n\t\treturn cmd.ExecuteContext(ctx)\n\t})\n\n\treturn eg\n}\n\n// Exec runs the provided cobra command with the given reader as STD_IN and the given args.\n// Returns STD_OUT, STD_ERR and the error from the execution.\nfunc Exec(t testing.TB, cmd *cobra.Command, stdIn io.Reader, args ...string) (string, string, error) {\n\treturn ExecCtx(t.Context(), cmd, stdIn, args...)\n}\n\nfunc ExecCtx(ctx context.Context, cmd *cobra.Command, stdIn io.Reader, args ...string) (string, string, error) {\n\tstdOut, stdErr := &bytes.Buffer{}, &bytes.Buffer{}\n\n\tprepareCmd(cmd, stdIn, stdOut, stdErr, args)\n\n\t// needs to be on a separate line to ensure that the output buffers are read AFTER the command ran\n\terr := cmd.ExecuteContext(ctx)\n\n\treturn stdOut.String(), stdErr.String(), err\n}\n\n// ExecNoErr is a helper that assumes a successful run from Exec.\n// Returns STD_OUT.\nfunc ExecNoErr(t testing.TB, cmd *cobra.Command, args ...string) string {\n\treturn ExecNoErrCtx(t.Context(), t, cmd, args...)\n}\n\nfunc ExecNoErrCtx(ctx context.Context, t require.TestingT, cmd *cobra.Command, args ...string) string {\n\tstdOut, stdErr, err := ExecCtx(ctx, cmd, nil, args...)\n\tif err == nil {\n\t\trequire.Len(t, stdErr, 0, \"std_out: %s\\nstd_err: %s\", stdOut, stdErr)\n\t} else {\n\t\trequire.ErrorIsf(t, err, context.Canceled, \"std_out: %s\\nstd_err: %s\", stdOut, stdErr)\n\t}\n\treturn stdOut\n}\n\n// ExecExpectedErr is a helper that assumes a failing run from Exec returning ErrNoPrintButFail\n// Returns STD_ERR.\nfunc ExecExpectedErr(t testing.TB, cmd *cobra.Command, args ...string) string {\n\treturn ExecExpectedErrCtx(t.Context(), t, cmd, args...)\n}\n\nfunc ExecExpectedErrCtx(ctx context.Context, t require.TestingT, cmd *cobra.Command, args ...string) string {\n\tstdOut, stdErr, err := ExecCtx(ctx, cmd, nil, args...)\n\trequire.True(t, errors.Is(err, ErrNoPrintButFail), \"std_out: %s\\nstd_err: %s\", stdOut, stdErr)\n\trequire.Len(t, stdOut, 0, stdErr)\n\treturn stdErr\n}\n\ntype CommandExecuter struct {\n\tNew            func() *cobra.Command\n\tCtx            context.Context\n\tPersistentArgs []string\n}\n\nfunc (c *CommandExecuter) Exec(stdin io.Reader, args ...string) (string, string, error) {\n\treturn ExecCtx(c.Ctx, c.New(), stdin, append(c.PersistentArgs, args...)...)\n}\n\nfunc (c *CommandExecuter) ExecBackground(stdin io.Reader, stdOut, stdErr io.Writer, args ...string) *errgroup.Group {\n\treturn ExecBackgroundCtx(c.Ctx, c.New(), stdin, stdOut, stdErr, append(c.PersistentArgs, args...)...)\n}\n\nfunc (c *CommandExecuter) ExecNoErr(t require.TestingT, args ...string) string {\n\treturn ExecNoErrCtx(c.Ctx, t, c.New(), append(c.PersistentArgs, args...)...)\n}\n\nfunc (c *CommandExecuter) ExecExpectedErr(t require.TestingT, args ...string) string {\n\treturn ExecExpectedErrCtx(c.Ctx, t, c.New(), append(c.PersistentArgs, args...)...)\n}\n\ntype URL struct {\n\turl.URL\n}\n\nvar _ pflag.Value = (*URL)(nil)\n\nfunc (u *URL) Set(s string) error {\n\tuu, err := url.Parse(s)\n\tif err != nil {\n\t\treturn err\n\t}\n\tu.URL = *uu\n\treturn nil\n}\n\nfunc (*URL) Type() string {\n\treturn \"url\"\n}\n"
  },
  {
    "path": "oryx/cmdx/http.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/pflag\"\n\n\t\"github.com/ory/x/httpx\"\n)\n\nconst (\n\tenvKeyEndpoint    = \"ORY_SDK_URL\"\n\tFlagEndpoint      = \"endpoint\"\n\tFlagSkipTLSVerify = \"skip-tls-verify\"\n\tFlagHeaders       = \"http-header\"\n)\n\n// Remote returns the remote endpoint for the given command.\nfunc Remote(cmd *cobra.Command) (string, error) {\n\tendpoint, err := cmd.Flags().GetString(FlagEndpoint)\n\tif err != nil {\n\t\treturn \"\", errors.WithStack(err)\n\t}\n\n\tif endpoint != \"\" {\n\t\treturn strings.TrimRight(endpoint, \"/\"), nil\n\t} else if endpoint := os.Getenv(\"ORY_SDK_URL\"); endpoint != \"\" {\n\t\treturn strings.TrimRight(endpoint, \"/\"), nil\n\t}\n\n\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"To execute this command, the endpoint URL must point to the URL where Ory is located. To set the endpoint URL, use flag `--endpoint` or environment variable `ORY_SDK_URL`.\")\n\treturn \"\", FailSilently(cmd)\n}\n\n// RemoteURI returns the remote URI for the given command.\nfunc RemoteURI(cmd *cobra.Command) (*url.URL, error) {\n\tremote, err := Remote(cmd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tendpoint, err := url.ParseRequestURI(remote)\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not parse endpoint URL: %s\", err)\n\t\treturn nil, err\n\t}\n\n\treturn endpoint, nil\n}\n\n// NewClient creates a new HTTP client.\nfunc NewClient(cmd *cobra.Command) (*http.Client, *url.URL, error) {\n\tendpoint, err := cmd.Flags().GetString(FlagEndpoint)\n\tif err != nil {\n\t\treturn nil, nil, errors.WithStack(err)\n\t}\n\n\tif endpoint == \"\" {\n\t\tendpoint = os.Getenv(envKeyEndpoint)\n\t}\n\n\tif endpoint == \"\" {\n\t\treturn nil, nil, errors.Errorf(\"you have to set the remote endpoint, try --help for details\")\n\t}\n\n\tu, err := url.Parse(strings.TrimRight(endpoint, \"/\"))\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrapf(err, `could not parse the endpoint URL \"%s\"`, endpoint)\n\t}\n\n\thc := retryablehttp.NewClient().StandardClient()\n\thc.Timeout = time.Second * 10\n\n\trawHeaders, err := cmd.Flags().GetStringSlice(FlagHeaders)\n\tif err != nil {\n\t\treturn nil, nil, errors.WithStack(err)\n\t}\n\theader := http.Header{}\n\tfor _, h := range rawHeaders {\n\t\tparts := strings.Split(h, \":\")\n\t\tif len(parts) != 2 {\n\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"Unable to parse `--http-header` flag. Format of flag value is a `: ` delimited string like `--http-header 'Some-Header: some-values; other values`. Received: %v\", rawHeaders)\n\t\t\treturn nil, nil, FailSilently(cmd)\n\t\t}\n\n\t\tfor k := range parts {\n\t\t\tparts[k] = strings.TrimSpace(parts[k])\n\t\t}\n\n\t\theader.Add(parts[0], parts[1])\n\t}\n\n\tskipVerify, err := cmd.Flags().GetBool(FlagSkipTLSVerify)\n\tif err != nil {\n\t\treturn nil, nil, errors.WithStack(err)\n\t}\n\n\trt := httpx.NewTransportWithHeader(header)\n\trt.RoundTripper = &http.Transport{\n\t\tTLSClientConfig: &tls.Config{\n\t\t\tInsecureSkipVerify: skipVerify, //nolint:gosec // This is a false positive\n\t\t},\n\t}\n\thc.Transport = rt\n\treturn hc, u, nil\n}\n\n// RegisterHTTPClientFlags registers HTTP client configuration flags.\nfunc RegisterHTTPClientFlags(flags *pflag.FlagSet) {\n\tflags.StringP(FlagEndpoint, FlagEndpoint[:1], \"\", fmt.Sprintf(\"The API URL this command should target. Alternatively set using the %s environmental variable.\", envKeyEndpoint))\n\tflags.Bool(FlagSkipTLSVerify, false, \"Do not verify TLS certificates. Useful when dealing with self-signed certificates. Do not use in production!\")\n\tflags.StringSliceP(FlagHeaders, \"H\", []string{}, \"A list of additional HTTP headers to set. HTTP headers is separated by a `: `, for example: `-H 'Authorization: bearer some-token'`.\")\n}\n"
  },
  {
    "path": "oryx/cmdx/noise_printer.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/pflag\"\n)\n\ntype ConditionalPrinter struct {\n\tw     io.Writer\n\tprint bool\n}\n\nconst (\n\tFlagQuiet = \"quiet\"\n)\n\nfunc RegisterNoiseFlags(flags *pflag.FlagSet) {\n\tflags.BoolP(FlagQuiet, FlagQuiet[:1], false, \"Be quiet with output printing.\")\n}\n\n// NewLoudOutPrinter returns a ConditionalPrinter that\n// only prints to cmd.OutOrStdout when --quiet is not set\nfunc NewLoudOutPrinter(cmd *cobra.Command) *ConditionalPrinter {\n\tquiet, err := cmd.Flags().GetBool(FlagQuiet)\n\tif err != nil {\n\t\tFatalf(err.Error())\n\t}\n\n\treturn &ConditionalPrinter{\n\t\tw:     cmd.OutOrStdout(),\n\t\tprint: !quiet,\n\t}\n}\n\n// NewQuietOutPrinter returns a ConditionalPrinter that\n// only prints to cmd.OutOrStdout when --quiet is set\nfunc NewQuietOutPrinter(cmd *cobra.Command) *ConditionalPrinter {\n\tquiet, err := cmd.Flags().GetBool(FlagQuiet)\n\tif err != nil {\n\t\tFatalf(err.Error())\n\t}\n\n\treturn &ConditionalPrinter{\n\t\tw:     cmd.OutOrStdout(),\n\t\tprint: quiet,\n\t}\n}\n\n// NewLoudErrPrinter returns a ConditionalPrinter that\n// only prints to cmd.ErrOrStderr when --quiet is not set\nfunc NewLoudErrPrinter(cmd *cobra.Command) *ConditionalPrinter {\n\tquiet, err := cmd.Flags().GetBool(FlagQuiet)\n\tif err != nil {\n\t\tFatalf(err.Error())\n\t}\n\n\treturn &ConditionalPrinter{\n\t\tw:     cmd.ErrOrStderr(),\n\t\tprint: !quiet,\n\t}\n}\n\n// NewQuietErrPrinter returns a ConditionalPrinter that\n// only prints to cmd.ErrOrStderr when --quiet is set\nfunc NewQuietErrPrinter(cmd *cobra.Command) *ConditionalPrinter {\n\tquiet, err := cmd.Flags().GetBool(FlagQuiet)\n\tif err != nil {\n\t\tFatalf(err.Error())\n\t}\n\n\treturn &ConditionalPrinter{\n\t\tw:     cmd.ErrOrStderr(),\n\t\tprint: quiet,\n\t}\n}\n\n// NewLoudPrinter returns a ConditionalPrinter that\n// only prints to w when --quiet is not set\nfunc NewLoudPrinter(cmd *cobra.Command, w io.Writer) *ConditionalPrinter {\n\tquiet, err := cmd.Flags().GetBool(FlagQuiet)\n\tif err != nil {\n\t\tFatalf(err.Error())\n\t}\n\n\treturn &ConditionalPrinter{\n\t\tw:     w,\n\t\tprint: !quiet,\n\t}\n}\n\n// NewQuietPrinter returns a ConditionalPrinter that\n// only prints to w when --quiet is set\nfunc NewQuietPrinter(cmd *cobra.Command, w io.Writer) *ConditionalPrinter {\n\tquiet, err := cmd.Flags().GetBool(FlagQuiet)\n\tif err != nil {\n\t\tFatalf(err.Error())\n\t}\n\n\treturn &ConditionalPrinter{\n\t\tw:     w,\n\t\tprint: quiet,\n\t}\n}\n\nfunc NewConditionalPrinter(w io.Writer, print bool) *ConditionalPrinter {\n\treturn &ConditionalPrinter{\n\t\tw:     w,\n\t\tprint: print,\n\t}\n}\n\nfunc (p *ConditionalPrinter) Println(a ...interface{}) (n int, err error) {\n\tif p.print {\n\t\treturn fmt.Fprintln(p.w, a...)\n\t}\n\treturn\n}\n\nfunc (p *ConditionalPrinter) Print(a ...interface{}) (n int, err error) {\n\tif p.print {\n\t\treturn fmt.Fprint(p.w, a...)\n\t}\n\treturn\n}\n\nfunc (p *ConditionalPrinter) Printf(format string, a ...interface{}) (n int, err error) {\n\tif p.print {\n\t\treturn fmt.Fprintf(p.w, format, a...)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "oryx/cmdx/output.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport \"strconv\"\n\ntype (\n\t// OutputIder outputs an ID\n\tOutputIder string\n\t// OutputIderCollection outputs a list of IDs\n\tOutputIderCollection struct {\n\t\tItems []OutputIder\n\t}\n)\n\nfunc (OutputIder) Header() []string {\n\treturn []string{\"ID\"}\n}\n\nfunc (i OutputIder) Columns() []string {\n\treturn []string{string(i)}\n}\n\nfunc (i OutputIder) Interface() interface{} {\n\treturn i\n}\n\nfunc (OutputIderCollection) Header() []string {\n\treturn []string{\"ID\"}\n}\n\nfunc (c OutputIderCollection) Table() [][]string {\n\trows := make([][]string, len(c.Items))\n\tfor i, ident := range c.Items {\n\t\trows[i] = []string{string(ident)}\n\t}\n\treturn rows\n}\n\nfunc (c OutputIderCollection) Interface() interface{} {\n\treturn c.Items\n}\n\nfunc (c OutputIderCollection) Len() int {\n\treturn len(c.Items)\n}\n\ntype PaginatedList struct {\n\tCollection interface {\n\t\tTable\n\t\tIDs() []string\n\t} `json:\"-\"`\n\tItems         []interface{} `json:\"items\"`\n\tNextPageToken string        `json:\"next_page_token\"`\n\tIsLastPage    bool          `json:\"is_last_page\"`\n}\n\nfunc (r *PaginatedList) Header() []string {\n\treturn r.Collection.Header()\n}\n\nfunc (r *PaginatedList) Table() [][]string {\n\treturn append(\n\t\tr.Collection.Table(),\n\t\t[]string{},\n\t\t[]string{\"NEXT PAGE TOKEN\", r.NextPageToken},\n\t\t[]string{\"IS LAST PAGE\", strconv.FormatBool(r.IsLastPage)},\n\t)\n}\n\nfunc (r *PaginatedList) Interface() interface{} {\n\treturn r\n}\n\nfunc (r *PaginatedList) Len() int {\n\treturn r.Collection.Len() + 3\n}\n\nfunc (r *PaginatedList) IDs() []string {\n\treturn r.Collection.IDs()\n}\n\nvar _ Table = (*PaginatedList)(nil)\n"
  },
  {
    "path": "oryx/cmdx/pagination.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nconst (\n\tFlagPageSize  = \"page-size\"\n\tFlagPageToken = \"page-token\"\n)\n\nfunc RegisterTokenPaginationFlags(cmd *cobra.Command) (pageSize int, pageToken string) {\n\tcmd.Flags().StringVar(&pageToken, FlagPageToken, \"\", \"page token acquired from a previous response\")\n\tcmd.Flags().IntVar(&pageSize, FlagPageSize, 100, \"maximum number of items to return\")\n\treturn\n}\n\n// ParsePaginationArgs parses pagination arguments from the command line.\nfunc ParsePaginationArgs(cmd *cobra.Command, pageArg, perPageArg string) (page, perPage int64, err error) {\n\tif len(pageArg+perPageArg) > 0 {\n\t\tpage, err = strconv.ParseInt(pageArg, 0, 64)\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not parse page argument\\\"%s\\\": %s\", pageArg, err)\n\t\t\treturn 0, 0, FailSilently(cmd)\n\t\t}\n\n\t\tperPage, err = strconv.ParseInt(perPageArg, 0, 64)\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not parse per-page argument\\\"%s\\\": %s\", perPageArg, err)\n\t\t\treturn 0, 0, FailSilently(cmd)\n\t\t}\n\t}\n\treturn\n}\n\n// ParseTokenPaginationArgs parses token-based pagination arguments from the command line.\nfunc ParseTokenPaginationArgs(cmd *cobra.Command) (page string, perPage int, err error) {\n\tpageArg, err := cmd.Flags().GetString(FlagPageToken)\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not parse %s argument \\\"%s\\\": %s\", FlagPageToken, pageArg, err)\n\t\treturn \"\", 0, FailSilently(cmd)\n\t}\n\n\tperPageArg, err := cmd.Flags().GetInt(FlagPageSize)\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not parse %s argument \\\"%d\\\": %s\", FlagPageSize, perPageArg, err)\n\t\treturn \"\", 0, FailSilently(cmd)\n\t}\n\n\treturn pageArg, perPageArg, nil\n}\n"
  },
  {
    "path": "oryx/cmdx/printing.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"text/tabwriter\"\n\n\t\"github.com/go-openapi/jsonpointer\"\n\t\"github.com/goccy/go-yaml\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/pflag\"\n\t\"github.com/tidwall/gjson\"\n)\n\ntype (\n\tTableHeader interface {\n\t\tHeader() []string\n\t}\n\tTableRow interface {\n\t\tTableHeader\n\t\tColumns() []string\n\t\tInterface() interface{}\n\t}\n\tTable interface {\n\t\tTableHeader\n\t\tTable() [][]string\n\t\tInterface() interface{}\n\t\tLen() int\n\t}\n\tNil struct{}\n\n\tFormat string\n)\n\nconst (\n\tFormatQuiet       Format = \"quiet\"\n\tFormatTable       Format = \"table\"\n\tFormatJSON        Format = \"json\"\n\tFormatJSONPath    Format = \"jsonpath\"\n\tFormatJSONPointer Format = \"jsonpointer\"\n\tFormatJSONPretty  Format = \"json-pretty\"\n\tFormatYAML        Format = \"yaml\"\n\tFormatDefault     Format = \"default\"\n\n\tFlagFormat = \"format\"\n\n\tNone = \"<none>\"\n)\n\nfunc (Nil) String() string {\n\treturn \"null\"\n}\n\nfunc (Nil) Interface() interface{} {\n\treturn nil\n}\n\ntype printOptions struct {\n\tformat string\n}\n\ntype PrintOption func(*printOptions)\n\nfunc WithFormat(v string) PrintOption {\n\treturn func(o *printOptions) {\n\t\to.format = v\n\t}\n}\n\nfunc PrintErrors(cmd *cobra.Command, errs map[string]error) {\n\tfor src, err := range errs {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"%s: %s\\n\", src, err.Error())\n\t}\n}\n\nfunc PrintRow(cmd *cobra.Command, row TableRow) {\n\tPrintRowf(cmd.OutOrStdout(), row, WithFormat(getFormatValue(cmd)))\n}\n\nfunc PrintRowf(w io.Writer, row TableRow, opts ...PrintOption) {\n\to := &printOptions{}\n\tfor _, fn := range opts {\n\t\tfn(o)\n\t}\n\n\tswitch parseFormat(o.format) {\n\tcase FormatQuiet:\n\t\tif idAble, ok := row.(interface{ ID() string }); ok {\n\t\t\t_, _ = fmt.Fprintln(w, idAble.ID())\n\t\t\tbreak\n\t\t}\n\t\t_, _ = fmt.Fprintln(w, row.Columns()[0])\n\tcase FormatJSON:\n\t\tprintJSON(w, row.Interface(), false, \"\")\n\tcase FormatYAML:\n\t\tprintYAML(w, row.Interface())\n\tcase FormatJSONPretty:\n\t\tprintJSON(w, row.Interface(), true, \"\")\n\tcase FormatJSONPath:\n\t\tprintJSON(w, row.Interface(), true, getPath(o.format))\n\tcase FormatJSONPointer:\n\t\tprintJSON(w, filterJSONPointer(o.format, row.Interface()), true, \"\")\n\tcase FormatTable, FormatDefault:\n\t\tw := tabwriter.NewWriter(w, 0, 8, 1, '\\t', 0)\n\n\t\tfields := row.Columns()\n\t\tfor i, h := range row.Header() {\n\t\t\t_, _ = fmt.Fprintf(w, \"%s\\t%s\\t\\n\", h, fields[i])\n\t\t}\n\n\t\t_ = w.Flush()\n\t}\n}\n\nfunc filterJSONPointer(f string, data any) any {\n\t_, jsonptr, found := strings.Cut(f, \"=\")\n\tif !found {\n\t\t_, _ = fmt.Fprintf(os.Stderr,\n\t\t\t\"Format %s is missing a JSON pointer, e.g., --%s=%s=<jsonpointer>. The path syntax is described at https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-json-pointer-07.\",\n\t\t\tf, FlagFormat, f)\n\t\tos.Exit(1)\n\t}\n\tptr, err := jsonpointer.New(jsonptr)\n\tMust(err, \"invalid JSON pointer: %s\", err)\n\n\tresult, _, err := ptr.Get(data)\n\tMust(err, \"failed to apply JSON pointer: %s\", err)\n\n\treturn result\n}\n\nfunc PrintTable(cmd *cobra.Command, table Table) {\n\tPrintTablef(cmd.OutOrStdout(), table, WithFormat(getFormatValue(cmd)))\n}\n\nfunc PrintTablef(w io.Writer, table Table, opts ...PrintOption) {\n\to := &printOptions{}\n\tfor _, fn := range opts {\n\t\tfn(o)\n\t}\n\n\tswitch parseFormat(o.format) {\n\tcase FormatQuiet:\n\t\tif table.Len() == 0 {\n\t\t\tfmt.Fprintln(w)\n\t\t}\n\n\t\tif idAble, ok := table.(interface{ IDs() []string }); ok {\n\t\t\tfor _, row := range idAble.IDs() {\n\t\t\t\tfmt.Fprintln(w, row)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\n\t\tfor _, row := range table.Table() {\n\t\t\tfmt.Fprintln(w, row[0])\n\t\t}\n\tcase FormatJSON:\n\t\tprintJSON(w, table.Interface(), false, \"\")\n\tcase FormatJSONPretty:\n\t\tprintJSON(w, table.Interface(), true, \"\")\n\tcase FormatJSONPath:\n\t\tprintJSON(w, table.Interface(), true, getPath(o.format))\n\tcase FormatJSONPointer:\n\t\tprintJSON(w, filterJSONPointer(o.format, table.Interface()), true, \"\")\n\tcase FormatYAML:\n\t\tprintYAML(w, table.Interface())\n\tdefault:\n\t\tw := tabwriter.NewWriter(w, 0, 8, 1, '\\t', 0)\n\n\t\tfor _, h := range table.Header() {\n\t\t\tfmt.Fprintf(w, \"%s\\t\", h)\n\t\t}\n\t\tfmt.Fprintln(w)\n\n\t\tfor _, row := range table.Table() {\n\t\t\tfmt.Fprintln(w, strings.Join(row, \"\\t\")+\"\\t\")\n\t\t}\n\n\t\t_ = w.Flush()\n\t}\n}\n\ntype interfacer interface{ Interface() interface{} }\n\nfunc PrintJSONAble(cmd *cobra.Command, d interface{ String() string }) {\n\tPrintJSONAblef(cmd.OutOrStdout(), d, WithFormat(getFormatValue(cmd)))\n}\n\nfunc PrintJSONAblef(w io.Writer, d interface{ String() string }, opts ...PrintOption) {\n\tif d == nil {\n\t\td = Nil{}\n\t}\n\n\to := &printOptions{}\n\tfor _, fn := range opts {\n\t\tfn(o)\n\t}\n\n\tvar path string\n\tswitch parseFormat(o.format) {\n\tdefault:\n\t\t_, _ = fmt.Fprint(w, d.String())\n\tcase FormatJSON:\n\t\tvar v interface{} = d\n\t\tif i, ok := d.(interfacer); ok {\n\t\t\tv = i\n\t\t}\n\t\tprintJSON(w, v, false, \"\")\n\tcase FormatJSONPath:\n\t\tpath = getPath(o.format)\n\t\tfallthrough\n\tcase FormatJSONPretty:\n\t\tvar v interface{} = d\n\t\tif i, ok := d.(interfacer); ok {\n\t\t\tv = i\n\t\t}\n\t\tprintJSON(w, v, true, path)\n\tcase FormatJSONPointer:\n\t\tvar v interface{} = d\n\t\tif i, ok := d.(interfacer); ok {\n\t\t\tv = i\n\t\t}\n\t\tprintJSON(w, filterJSONPointer(o.format, v), true, \"\")\n\tcase FormatYAML:\n\t\tvar v interface{} = d\n\t\tif i, ok := d.(interfacer); ok {\n\t\t\tv = i\n\t\t}\n\t\tprintYAML(w, v)\n\t}\n}\n\nfunc getQuiet(cmd *cobra.Command) bool {\n\t// ignore the error here as we use this function also when the flag might not be registered\n\tq, _ := cmd.Flags().GetBool(FlagQuiet)\n\treturn q\n}\n\nfunc getFormatValue(cmd *cobra.Command) string {\n\tif getQuiet(cmd) {\n\t\treturn string(FormatQuiet)\n\t}\n\n\tf, _ := cmd.Flags().GetString(FlagFormat)\n\treturn f\n}\n\nfunc parseFormat(f string) Format {\n\tswitch {\n\tcase f == string(FormatQuiet):\n\t\treturn FormatQuiet\n\tcase f == string(FormatTable):\n\t\treturn FormatTable\n\tcase f == string(FormatJSON):\n\t\treturn FormatJSON\n\tcase strings.HasPrefix(f, string(FormatJSONPath)):\n\t\treturn FormatJSONPath\n\tcase strings.HasPrefix(f, string(FormatJSONPointer)):\n\t\treturn FormatJSONPointer\n\tcase f == string(FormatJSONPretty):\n\t\treturn FormatJSONPretty\n\tcase f == string(FormatYAML):\n\t\treturn FormatYAML\n\tdefault:\n\t\treturn FormatDefault\n\t}\n}\n\nfunc getPath(f string) string {\n\t_, path, found := strings.Cut(f, \"=\")\n\tif !found {\n\t\t_, _ = fmt.Fprintf(os.Stderr,\n\t\t\t\"Format %s is missing a path, e.g., --%s=%s=<path>. The path syntax is described at https://github.com/tidwall/gjson/blob/master/SYNTAX.md\",\n\t\t\tf, FlagFormat, f)\n\t\tos.Exit(1)\n\t}\n\n\treturn path\n}\n\nfunc printJSON(w io.Writer, v interface{}, pretty bool, path string) {\n\tif path != \"\" {\n\t\ttemp, err := json.Marshal(v)\n\t\tMust(err, \"Error encoding JSON: %s\", err)\n\t\tv = gjson.GetBytes(temp, path).Value()\n\t}\n\n\te := json.NewEncoder(w)\n\tif pretty {\n\t\te.SetIndent(\"\", \"  \")\n\t}\n\terr := e.Encode(v)\n\t// unexpected error\n\tMust(err, \"Error encoding JSON: %s\", err)\n}\n\nfunc printYAML(w io.Writer, v interface{}) {\n\tj, err := json.Marshal(v)\n\tMust(err, \"Error encoding JSON: %s\", err)\n\te, err := yaml.JSONToYAML(j)\n\tMust(err, \"Error encoding YAML: %s\", err)\n\t_, _ = w.Write(e)\n}\n\nfunc RegisterJSONFormatFlags(flags *pflag.FlagSet) {\n\tflags.String(FlagFormat, string(FormatDefault), fmt.Sprintf(\"Set the output format. One of %s, %s, %s, %s, %s and %s.\", FormatDefault, FormatJSON, FormatYAML, FormatJSONPretty, FormatJSONPath, FormatJSONPointer))\n}\n\nfunc RegisterFormatFlags(flags *pflag.FlagSet) {\n\tRegisterNoiseFlags(flags)\n\tflags.String(FlagFormat, string(FormatTable), fmt.Sprintf(\"Set the output format. One of %s, %s, %s, %s, %s and %s.\", FormatTable, FormatJSON, FormatYAML, FormatJSONPretty, FormatJSONPath, FormatJSONPointer))\n}\n\nfunc PrintOpenAPIError(cmd *cobra.Command, err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tvar be interface {\n\t\tBody() []byte\n\t}\n\tif !errors.As(err, &be) {\n\t\treturn err\n\t}\n\n\tbody := be.Body()\n\tdidPrettyPrint := false\n\tif message := gjson.GetBytes(body, \"error.message\"); message.Exists() {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"%s\\n\", message.String())\n\t\tdidPrettyPrint = true\n\t}\n\tif reason := gjson.GetBytes(body, \"error.reason\"); reason.Exists() {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"%s\\n\", reason.String())\n\t\tdidPrettyPrint = true\n\t}\n\n\tif didPrettyPrint {\n\t\treturn FailSilently(cmd)\n\t}\n\n\tif body, err := json.MarshalIndent(json.RawMessage(body), \"\", \"  \"); err == nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"%s\\nFailed to execute API request, see error above.\\n\", body)\n\t\treturn FailSilently(cmd)\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "oryx/cmdx/usage.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport (\n\t\"bytes\"\n\t\"text/template\"\n\n\t\"github.com/Masterminds/sprig/v3\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar usageTemplateFuncs = sprig.TxtFuncMap()\n\n// AddUsageTemplateFunc adds a template function to the usage template.\nfunc AddUsageTemplateFunc(name string, f interface{}) {\n\tusageTemplateFuncs[name] = f\n}\n\nconst (\n\thelpTemplate = `{{insertTemplate . (or .Long .Short) | trimTrailingWhitespaces}}\n\n{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`\n\tusageTemplate = `Usage:{{if .Runnable}}\n  {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}\n  {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}\n\nAliases:\n  {{.NameAndAliases}}{{end}}{{if .HasExample}}\n\nExamples:\n{{insertTemplate . .Example}}{{end}}{{if .HasAvailableSubCommands}}\n\nAvailable Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name \"help\"))}}\n  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\n\nFlags:\n{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\n\nGlobal Flags:\n{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}\n\nAdditional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}\n  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}\n\nUse \"{{.CommandPath}} [command] --help\" for more information about a command.{{end}}\n`\n)\n\n// EnableUsageTemplating enables gotemplates for usage strings, i.e. cmd.Short, cmd.Long, and cmd.Example.\n// The data for the template is the command itself. Especially useful are `.Root.Name` and `.CommandPath`.\n// This will be inherited by all subcommands, so enabling it on the root command is sufficient.\nfunc EnableUsageTemplating(cmds ...*cobra.Command) {\n\tcobra.AddTemplateFunc(\"insertTemplate\", TemplateCommandField)\n\tfor _, cmd := range cmds {\n\t\tcmd.SetHelpTemplate(helpTemplate)\n\t\tcmd.SetUsageTemplate(usageTemplate)\n\t}\n}\n\nfunc TemplateCommandField(cmd *cobra.Command, field string) (string, error) {\n\tt := template.New(\"\")\n\tt.Funcs(usageTemplateFuncs)\n\tt, err := t.Parse(field)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar out bytes.Buffer\n\tif err := t.Execute(&out, cmd); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn out.String(), nil\n}\n\n// DisableUsageTemplating resets the commands usage template to the default.\n// This can be used to undo the effects of EnableUsageTemplating, specifically for a subcommand.\nfunc DisableUsageTemplating(cmds ...*cobra.Command) {\n\tdefaultCmd := new(cobra.Command)\n\tfor _, cmd := range cmds {\n\t\tcmd.SetHelpTemplate(defaultCmd.HelpTemplate())\n\t\tcmd.SetUsageTemplate(defaultCmd.UsageTemplate())\n\t}\n}\n\n// AssertUsageTemplates asserts that the usage string of the commands are properly templated.\nfunc AssertUsageTemplates(t require.TestingT, cmd *cobra.Command) {\n\tvar usage, help string\n\trequire.NotPanics(t, func() {\n\t\tusage = cmd.UsageString()\n\n\t\tout, err := cmd.OutOrStdout(), cmd.ErrOrStderr()\n\t\tbb := new(bytes.Buffer)\n\n\t\tcmd.SetOut(bb)\n\t\tcmd.SetErr(bb)\n\t\trequire.NoError(t, cmd.Help())\n\t\thelp = bb.String()\n\n\t\tcmd.SetOut(out)\n\t\tcmd.SetErr(err)\n\t})\n\tassert.NotContains(t, usage, \"{{\")\n\tassert.NotContains(t, help, \"{{\")\n\tfor _, child := range cmd.Commands() {\n\t\tAssertUsageTemplates(t, child)\n\t}\n}\n"
  },
  {
    "path": "oryx/cmdx/user_input.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// asks for confirmation with the question string s and reads the answer\n// pass nil to use os.Stdin and os.Stdout\nfunc AskForConfirmation(s string, stdin io.Reader, stdout io.Writer) bool {\n\tif stdin == nil {\n\t\tstdin = os.Stdin\n\t}\n\tif stdout == nil {\n\t\tstdout = os.Stdout\n\t}\n\n\tok, err := AskScannerForConfirmation(s, bufio.NewReader(stdin), stdout)\n\tif err != nil {\n\t\tMust(err, \"Unable to confirm: %s\", err)\n\t}\n\n\treturn ok\n}\n\nfunc AskScannerForConfirmation(s string, reader *bufio.Reader, stdout io.Writer) (bool, error) {\n\tif stdout == nil {\n\t\tstdout = os.Stdout\n\t}\n\n\tfor {\n\t\t_, err := fmt.Fprintf(stdout, \"%s [y/n]: \", s)\n\t\tif err != nil {\n\t\t\treturn false, errors.Wrap(err, \"unable to print to stdout\")\n\t\t}\n\n\t\tresponse, err := reader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\treturn false, errors.Wrap(err, \"unable to read from stdin\")\n\t\t}\n\n\t\tresponse = strings.ToLower(strings.TrimSpace(response))\n\t\tif response == \"y\" || response == \"yes\" {\n\t\t\treturn true, nil\n\t\t} else if response == \"n\" || response == \"no\" {\n\t\t\treturn false, nil\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "oryx/cmdx/version.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage cmdx\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n)\n\n// Version returns a *cobra.Command that handles the `version` command.\nfunc Version(gitTag, gitHash, buildTime *string) *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"version\",\n\t\tShort: \"Show the build version, build time, and git hash\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tif len(*gitTag) == 0 {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"Unable to determine version because the build process did not properly configure it.\")\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"Version:\t\t\t%s\\n\", *gitTag)\n\t\t\t}\n\n\t\t\tif len(*gitHash) == 0 {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"Unable to determine build commit because the build process did not properly configure it.\")\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"Build Commit:\t%s\\n\", *gitHash)\n\t\t\t}\n\n\t\t\tif len(*buildTime) == 0 {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"Unable to determine build timestamp because the build process did not properly configure it.\")\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"Build Timestamp:\t%s\\n\", *buildTime)\n\t\t\t}\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "oryx/configx/.snapshots/TestKoanfSchemaDefaults.json",
    "content": "{}\n"
  },
  {
    "path": "oryx/configx/context.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport \"context\"\n\ntype contextKey int\n\nconst configContextKey contextKey = iota + 1\n\nfunc ContextWithConfigOptions(ctx context.Context, opts ...OptionModifier) context.Context {\n\treturn context.WithValue(ctx, configContextKey, opts)\n}\n\nfunc ConfigOptionsFromContext(ctx context.Context) []OptionModifier {\n\topts, ok := ctx.Value(configContextKey).([]OptionModifier)\n\tif !ok {\n\t\treturn []OptionModifier{}\n\t}\n\treturn opts\n}\n"
  },
  {
    "path": "oryx/configx/cors.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t_ \"embed\"\n\n\t\"github.com/rs/cors\"\n)\n\nconst CORSConfigSchemaID = \"ory://cors-config\"\n\n//go:embed cors.schema.json\nvar CORSConfigSchema []byte\n\nfunc (p *Provider) CORS(prefix string, defaults cors.Options) (cors.Options, bool) {\n\tprefix = cleanPrefix(prefix)\n\n\treturn cors.Options{\n\t\tAllowedOrigins:     p.StringsF(prefix+\"cors.allowed_origins\", defaults.AllowedOrigins),\n\t\tAllowedMethods:     p.StringsF(prefix+\"cors.allowed_methods\", defaults.AllowedMethods),\n\t\tAllowedHeaders:     p.StringsF(prefix+\"cors.allowed_headers\", defaults.AllowedHeaders),\n\t\tExposedHeaders:     p.StringsF(prefix+\"cors.exposed_headers\", defaults.ExposedHeaders),\n\t\tAllowCredentials:   p.BoolF(prefix+\"cors.allow_credentials\", defaults.AllowCredentials),\n\t\tOptionsPassthrough: p.BoolF(prefix+\"cors.options_passthrough\", defaults.OptionsPassthrough),\n\t\tMaxAge:             p.IntF(prefix+\"cors.max_age\", defaults.MaxAge),\n\t\tDebug:              p.BoolF(prefix+\"cors.debug\", defaults.Debug),\n\t}, p.Bool(prefix + \"cors.enabled\")\n}\n"
  },
  {
    "path": "oryx/configx/cors.schema.json",
    "content": "{\n  \"$id\": \"https://github.com/ory/x/configx/cors.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"title\": \"CORS\",\n  \"description\": \"Configures Cross Origin Resource Sharing for this endpoint.\",\n  \"properties\": {\n    \"enabled\": {\n      \"type\": \"boolean\",\n      \"default\": false\n    },\n    \"allowed_origins\": {\n      \"type\": \"array\",\n      \"description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: https://*.example.com). Only one wildcard can be used per origin.\",\n      \"items\": {\n        \"type\": \"string\",\n        \"minLength\": 1,\n        \"not\": {\n          \"type\": \"string\",\n          \"description\": \"matches all strings that contain two or more (*)\",\n          \"pattern\": \".*\\\\*.*\\\\*.*\"\n        },\n        \"anyOf\": [\n          {\n            \"type\": \"string\",\n            \"format\": \"uri\"\n          },\n          {\n            \"const\": \"*\"\n          }\n        ]\n      },\n      \"uniqueItems\": true,\n      \"examples\": [\n        [\n          \"https://example.com\",\n          \"https://*.example.com\",\n          \"https://*.foo.example.com\"\n        ]\n      ]\n    },\n    \"allowed_methods\": {\n      \"type\": \"array\",\n      \"description\": \"A list of HTTP methods the user agent is allowed to use with cross-domain requests.\",\n      \"items\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"POST\",\n          \"GET\",\n          \"PUT\",\n          \"PATCH\",\n          \"DELETE\",\n          \"CONNECT\",\n          \"HEAD\",\n          \"OPTIONS\",\n          \"TRACE\"\n        ]\n      }\n    },\n    \"allowed_headers\": {\n      \"type\": \"array\",\n      \"description\": \"A list of non-simple headers the client is allowed to use with cross-domain requests.\",\n      \"examples\": [\n        [\n          \"Authorization\",\n          \"Content-Type\",\n          \"Max-Age\",\n          \"X-Session-Token\",\n          \"X-XSRF-TOKEN\",\n          \"X-CSRF-TOKEN\"\n        ]\n      ],\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"exposed_headers\": {\n      \"type\": \"array\",\n      \"description\": \"Sets which headers are safe to expose to the API of a CORS API specification.\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"allow_credentials\": {\n      \"type\": \"boolean\",\n      \"description\": \"Sets whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\",\n      \"default\": true\n    },\n    \"options_passthrough\": {\n      \"type\": \"boolean\",\n      \"description\": \"TODO\",\n      \"default\": false\n    },\n    \"max_age\": {\n      \"type\": \"integer\",\n      \"description\": \"Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request is preceded by a preflight request.\",\n      \"default\": 0,\n      \"minimum\": 0\n    },\n    \"debug\": {\n      \"type\": \"boolean\",\n      \"description\": \"Adds additional log output to debug CORS issues.\",\n      \"default\": false\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/configx/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pkg/errors\"\n)\n\ntype ImmutableError struct {\n\tFrom interface{}\n\tTo   interface{}\n\tKey  string\n\terror\n}\n\nfunc NewImmutableError(key string, from, to interface{}) error {\n\treturn &ImmutableError{\n\t\tFrom:  from,\n\t\tTo:    to,\n\t\tKey:   key,\n\t\terror: errors.Errorf(\"immutable configuration key \\\"%s\\\" was changed from \\\"%v\\\" to \\\"%v\\\"\", key, from, to),\n\t}\n}\n\nfunc (e *ImmutableError) Error() string {\n\treturn fmt.Sprintf(\"immutable configuration key \\\"%s\\\" was changed from \\\"%v\\\" to \\\"%v\\\"\", e.Key, e.From, e.To)\n}\n"
  },
  {
    "path": "oryx/configx/helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/spf13/pflag\"\n)\n\n// RegisterFlags registers the config file flag.\nfunc RegisterFlags(flags *pflag.FlagSet) {\n\tflags.StringSliceP(\"config\", \"c\", []string{}, \"Path to one or more .json, .yaml, .yml, .toml config files. Values are loaded in the order provided, meaning that the last config file overwrites values from the previous config file.\")\n}\n\n// host = unix:/path/to/socket => port is discarded, otherwise format as host:port\nfunc GetAddress(host string, port int) string {\n\tif strings.HasPrefix(host, \"unix:\") {\n\t\treturn host\n\t}\n\treturn fmt.Sprintf(\"%s:%d\", host, port)\n}\n\nfunc (s *Serve) GetAddress() string {\n\treturn GetAddress(s.Host, s.Port)\n}\n\n// AddSchemaResources adds the config schema partials to the compiler.\n// The interface is specified instead of `jsonschema.Compiler` to allow the use of any jsonschema library fork or version.\nfunc AddSchemaResources(c interface {\n\tAddResource(url string, r io.Reader) error\n}) error {\n\tif err := c.AddResource(TLSConfigSchemaID, bytes.NewReader(TLSConfigSchema)); err != nil {\n\t\treturn err\n\t}\n\tif err := c.AddResource(ServeConfigSchemaID, bytes.NewReader(ServeConfigSchema)); err != nil {\n\t\treturn err\n\t}\n\treturn c.AddResource(CORSConfigSchemaID, bytes.NewReader(CORSConfigSchema))\n}\n\nfunc cleanPrefix(prefix string) string {\n\tif len(prefix) > 0 {\n\t\tprefix = strings.TrimRight(prefix, \".\") + \".\"\n\t}\n\treturn prefix\n}\n"
  },
  {
    "path": "oryx/configx/koanf_confmap.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/knadh/koanf/maps\"\n\t\"github.com/tidwall/gjson\"\n)\n\n// KoanfConfmap implements a raw map[string]interface{} provider.\ntype KoanfConfmap struct {\n\ttuples []tuple\n}\n\n// Provider returns a confmap Provider that takes a flat or nested\n// map[string]interface{}. If a delim is provided, it indicates that the\n// keys are flat and the map needs to be unflatted by delim.\nfunc NewKoanfConfmap(tuples []tuple) *KoanfConfmap {\n\treturn &KoanfConfmap{tuples: jsonify(tuples)}\n}\n\nfunc jsonify(tuples []tuple) []tuple {\n\tfor k, t := range tuples {\n\t\tvar parsed interface{}\n\t\tswitch vt := t.Value.(type) {\n\t\tcase string:\n\t\t\tif gjson.Valid(vt) && json.NewDecoder(bytes.NewBufferString(vt)).Decode(&parsed) == nil {\n\t\t\t\ttuples[k].Value = parsed\n\t\t\t}\n\t\t\tcontinue\n\t\tcase []byte:\n\t\t\tif gjson.ValidBytes(vt) && json.NewDecoder(bytes.NewBuffer(vt)).Decode(&parsed) == nil {\n\t\t\t\ttuples[k].Value = parsed\n\t\t\t}\n\t\t\tcontinue\n\t\tcase json.RawMessage:\n\t\t\tif gjson.ValidBytes(vt) && json.NewDecoder(bytes.NewBuffer(vt)).Decode(&parsed) == nil {\n\t\t\t\ttuples[k].Value = parsed\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t}\n\treturn tuples\n}\n\n// ReadBytes is not supported by the env provider.\nfunc (e *KoanfConfmap) ReadBytes() ([]byte, error) {\n\treturn nil, errors.New(\"confmap provider does not support this method\")\n}\n\n// Read returns the loaded map[string]interface{}.\nfunc (e *KoanfConfmap) Read() (map[string]interface{}, error) {\n\tvalues := map[string]interface{}{}\n\tfor _, t := range e.tuples {\n\t\tvalues[t.Key] = t.Value\n\t}\n\n\t// Ensure any nested values are properly converted as well\n\tcp := maps.Copy(values)\n\tmaps.IntfaceKeysToStrings(cp)\n\tcp = maps.Unflatten(cp, Delimiter)\n\n\treturn cp, nil\n}\n"
  },
  {
    "path": "oryx/configx/koanf_env.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"encoding/json\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\n\t\"github.com/spf13/cast\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/castx\"\n\t\"github.com/ory/x/jsonschemax\"\n)\n\nvar isNumRegex = regexp.MustCompile(\"^[0-9]+$\")\n\nfunc NewKoanfEnv(prefix string, rawSchema []byte, schema *jsonschema.Schema) (*Env, error) {\n\tpaths, err := getSchemaPaths(rawSchema, schema)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Env{\n\t\tpaths:  paths,\n\t\tprefix: prefix,\n\t}, nil\n}\n\n// Env implements an environment variables provider.\ntype Env struct {\n\tprefix string\n\tpaths  []jsonschemax.Path\n}\n\n// ReadBytes is not supported by the env provider.\nfunc (e *Env) ReadBytes() ([]byte, error) {\n\treturn nil, errors.New(\"env provider does not support this method\")\n}\n\n// Read reads all available environment variables into a key:value map\n// and returns it.\nfunc (e *Env) Read() (map[string]interface{}, error) {\n\t// Collect the environment variable keys.\n\tvar keys []string\n\tfor _, k := range os.Environ() {\n\t\tif e.prefix != \"\" {\n\t\t\tif strings.HasPrefix(k, e.prefix) {\n\t\t\t\tkeys = append(keys, k)\n\t\t\t}\n\t\t} else {\n\t\t\tkeys = append(keys, k)\n\t\t}\n\t}\n\n\traw := \"{}\"\n\tvar err error\n\tfor _, k := range keys {\n\t\tparts := strings.SplitN(k, \"=\", 2)\n\n\t\tkey, value := e.extract(parts[0], parts[1])\n\t\t// If the callback blanked the key, it should be omitted\n\t\tif key == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\traw, err = sjson.Set(raw, key, value)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t}\n\n\tvar m map[string]interface{}\n\tif err := json.Unmarshal([]byte(raw), &m); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn m, nil\n}\n\n// Watch is not supported.\nfunc (e *Env) Watch(cb func(event interface{}, err error)) error {\n\treturn errors.New(\"env provider does not support this method\")\n}\n\nfunc (e *Env) extract(key string, value string) (string, interface{}) {\n\tkey = strings.Replace(strings.ToLower(strings.TrimPrefix(key, e.prefix)), \"_\", \".\", -1)\n\n\tfor _, path := range e.paths {\n\t\tnormalized := strings.Replace(path.Name, \"_\", \".\", -1)\n\t\tname := path.Name\n\n\t\t// Crazy hack to get arrays working.\n\t\tvar indices []string\n\t\tsearchParts := strings.Split(normalized, \".\")\n\t\tkeyParts := strings.Split(key, \".\")\n\t\tif len(searchParts) == len(keyParts) {\n\t\t\tfor k, search := range searchParts {\n\t\t\t\tif search != keyParts[k] {\n\t\t\t\t\tindices = nil\n\t\t\t\t}\n\n\t\t\t\tif search != \"#\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif !isNumRegex.MatchString(keyParts[k]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tsearchParts[k] = keyParts[k]\n\t\t\t\tindices = append(indices, keyParts[k])\n\t\t\t}\n\t\t}\n\n\t\tif len(indices) > 0 {\n\t\t\tnormalized = strings.Join(searchParts, \".\")\n\t\t\tfor _, index := range indices {\n\t\t\t\tname = strings.Replace(name, \"#\", index, 1)\n\t\t\t}\n\t\t}\n\n\t\tif normalized == key {\n\t\t\tswitch path.TypeHint {\n\t\t\tcase jsonschemax.String:\n\t\t\t\treturn name, cast.ToString(value)\n\t\t\tcase jsonschemax.Float:\n\t\t\t\treturn name, cast.ToFloat64(value)\n\t\t\tcase jsonschemax.Int:\n\t\t\t\treturn name, cast.ToInt64(value)\n\t\t\tcase jsonschemax.Bool:\n\t\t\t\treturn name, cast.ToBool(value)\n\t\t\tcase jsonschemax.Nil:\n\t\t\t\treturn name, nil\n\t\t\tcase jsonschemax.BoolSlice:\n\t\t\t\tif !gjson.Valid(value) {\n\t\t\t\t\treturn name, cast.ToBoolSlice(value)\n\t\t\t\t}\n\t\t\t\tfallthrough\n\t\t\tcase jsonschemax.StringSlice:\n\t\t\t\tif !gjson.Valid(value) {\n\t\t\t\t\treturn name, castx.ToStringSlice(value)\n\t\t\t\t}\n\t\t\t\tfallthrough\n\t\t\tcase jsonschemax.IntSlice:\n\t\t\t\tif !gjson.Valid(value) {\n\t\t\t\t\treturn name, cast.ToIntSlice(value)\n\t\t\t\t}\n\t\t\t\tfallthrough\n\t\t\tcase jsonschemax.FloatSlice:\n\t\t\t\tif !gjson.Valid(value) {\n\t\t\t\t\treturn name, castx.ToFloatSlice(value)\n\t\t\t\t}\n\t\t\t\tfallthrough\n\t\t\tcase jsonschemax.JSON:\n\t\t\t\treturn name, decode(value)\n\t\t\tdefault:\n\t\t\t\treturn name, value\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"\", nil\n}\n\nfunc decode(value string) (v interface{}) {\n\tb := []byte(value)\n\tvar arr []interface{}\n\tif err := json.Unmarshal(b, &arr); err == nil {\n\t\treturn &arr\n\t}\n\th := map[string]interface{}{}\n\tif err := json.Unmarshal(b, &h); err == nil {\n\t\treturn &h\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/configx/koanf_file.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/knadh/koanf/parsers/json\"\n\t\"github.com/knadh/koanf/parsers/toml\"\n\t\"github.com/knadh/koanf/parsers/yaml\"\n\t\"github.com/knadh/koanf/v2\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/watcherx\"\n)\n\n// KoanfFile implements a KoanfFile provider.\ntype KoanfFile struct {\n\tsubKey string\n\tpath   string\n\tparser koanf.Parser\n}\n\n// NewKoanfFile returns a file provider.\nfunc NewKoanfFile(path string) (*KoanfFile, error) {\n\treturn NewKoanfFileSubKey(path, \"\")\n}\n\nfunc NewKoanfFileSubKey(path, subKey string) (*KoanfFile, error) {\n\tkf := &KoanfFile{\n\t\tpath:   filepath.Clean(path),\n\t\tsubKey: subKey,\n\t}\n\n\tswitch e := filepath.Ext(path); e {\n\tcase \".toml\":\n\t\tkf.parser = toml.Parser()\n\tcase \".json\":\n\t\tkf.parser = json.Parser()\n\tcase \".yaml\", \".yml\":\n\t\tkf.parser = yaml.Parser()\n\tdefault:\n\t\treturn nil, errors.Errorf(\"unknown config file extension: %s\", e)\n\t}\n\n\treturn kf, nil\n}\n\n// ReadBytes is not supported by KoanfFile.\nfunc (f *KoanfFile) ReadBytes() ([]byte, error) {\n\treturn nil, errors.New(\"file provider does not support this method\")\n}\n\n// Read reads the file and returns the parsed configuration.\nfunc (f *KoanfFile) Read() (map[string]interface{}, error) {\n\t//#nosec G304 -- false positive\n\tfc, err := os.ReadFile(f.path)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tv, err := f.parser.Unmarshal(fc)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif f.subKey == \"\" {\n\t\treturn v, nil\n\t}\n\n\tpath := strings.Split(f.subKey, Delimiter)\n\tfor i := range path {\n\t\tv = map[string]interface{}{\n\t\t\tpath[len(path)-1-i]: v,\n\t\t}\n\t}\n\n\treturn v, nil\n}\n\n// WatchChannel watches the file and triggers a callback when it changes. It is a\n// blocking function that internally spawns a goroutine to watch for changes.\nfunc (f *KoanfFile) WatchChannel(ctx context.Context, c watcherx.EventChannel) (watcherx.Watcher, error) {\n\treturn watcherx.WatchFile(ctx, f.path, c)\n}\n"
  },
  {
    "path": "oryx/configx/koanf_full_merge.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/x/jsonx\"\n)\n\nfunc MergeAllTypes(src, dst map[string]interface{}) error {\n\trawSrc, err := json.Marshal(src)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tdstSrc, err := json.Marshal(dst)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tkeys := jsonx.Flatten(rawSrc)\n\tfor key, value := range keys {\n\t\tdstSrc, err = sjson.SetBytes(dstSrc, key, value)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t}\n\n\treturn errors.WithStack(json.Unmarshal(dstSrc, &dst))\n}\n"
  },
  {
    "path": "oryx/configx/koanf_memory.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"context\"\n\n\t\"github.com/knadh/koanf/parsers/json\"\n\t\"github.com/knadh/koanf/v2\"\n\n\t\"github.com/pkg/errors\"\n\n\tstdjson \"encoding/json\"\n)\n\n// KoanfMemory implements a KoanfMemory provider.\ntype KoanfMemory struct {\n\tdoc stdjson.RawMessage\n\n\tctx    context.Context\n\tparser koanf.Parser\n}\n\n// NewKoanfMemory returns a file provider.\nfunc NewKoanfMemory(ctx context.Context, doc stdjson.RawMessage) *KoanfMemory {\n\treturn &KoanfMemory{\n\t\tctx:    ctx,\n\t\tdoc:    doc,\n\t\tparser: json.Parser(),\n\t}\n}\n\nfunc (f *KoanfMemory) SetDoc(doc stdjson.RawMessage) {\n\tf.doc = doc\n}\n\n// ReadBytes reads the contents of a file on disk and returns the bytes.\nfunc (f *KoanfMemory) ReadBytes() ([]byte, error) {\n\treturn nil, errors.New(\"file provider does not support this method\")\n}\n\n// Read is not supported by the file provider.\nfunc (f *KoanfMemory) Read() (map[string]interface{}, error) {\n\tv, err := f.parser.Unmarshal(f.doc)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn v, nil\n}\n"
  },
  {
    "path": "oryx/configx/koanf_schema_defaults.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"strings\"\n\n\t\"github.com/knadh/koanf/maps\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/x/jsonschemax\"\n)\n\ntype KoanfSchemaDefaults struct {\n\tkeys []jsonschemax.Path\n}\n\nfunc NewKoanfSchemaDefaults(rawSchema []byte, schema *jsonschema.Schema) (*KoanfSchemaDefaults, error) {\n\tkeys, err := getSchemaPaths(rawSchema, schema)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &KoanfSchemaDefaults{keys: keys}, nil\n}\n\nfunc (k *KoanfSchemaDefaults) ReadBytes() ([]byte, error) {\n\treturn nil, errors.New(\"schema defaults provider does not support this method\")\n}\n\nfunc (k *KoanfSchemaDefaults) Read() (map[string]interface{}, error) {\n\tvalues := map[string]interface{}{}\n\tfor _, key := range k.keys {\n\t\t// It's an array!\n\t\tif strings.Contains(key.Name, \"#\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tif key.Default != nil {\n\t\t\tvalues[key.Name] = key.Default\n\t\t}\n\t}\n\n\treturn maps.Unflatten(values, \".\"), nil\n}\n"
  },
  {
    "path": "oryx/configx/options.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/spf13/pflag\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"github.com/knadh/koanf/v2\"\n\n\t\"github.com/ory/x/watcherx\"\n)\n\ntype (\n\tOptionModifier func(p *Provider)\n)\n\nfunc WithContext(ctx context.Context) OptionModifier {\n\treturn func(p *Provider) {\n\t\tfor _, o := range ConfigOptionsFromContext(ctx) {\n\t\t\to(p)\n\t\t}\n\t}\n}\n\nfunc WithConfigFiles(files ...string) OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.files = append(p.files, files...)\n\t}\n}\n\nfunc WithImmutables(immutables ...string) OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.immutables = append(p.immutables, immutables...)\n\t}\n}\n\nfunc WithExceptImmutables(exceptImmutables ...string) OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.exceptImmutables = append(p.exceptImmutables, exceptImmutables...)\n\t}\n}\n\nfunc WithFlags(flags *pflag.FlagSet) OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.flags = flags\n\t}\n}\n\nfunc WithLogger(l *logrusx.Logger) OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.logger = l\n\t}\n}\n\nfunc SkipValidation() OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.skipValidation = true\n\t}\n}\n\nfunc DisableEnvLoading() OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.disableEnvLoading = true\n\t}\n}\n\nfunc WithValue(key string, value interface{}) OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.forcedValues = append(p.forcedValues, tuple{Key: key, Value: value})\n\t}\n}\n\nfunc WithValues(values map[string]interface{}) OptionModifier {\n\treturn func(p *Provider) {\n\t\tfor key, value := range values {\n\t\t\tp.forcedValues = append(p.forcedValues, tuple{Key: key, Value: value})\n\t\t}\n\t}\n}\n\nfunc WithBaseValues(values map[string]interface{}) OptionModifier {\n\treturn func(p *Provider) {\n\t\tfor key, value := range values {\n\t\t\tp.baseValues = append(p.baseValues, tuple{Key: key, Value: value})\n\t\t}\n\t}\n}\n\nfunc WithUserProviders(providers ...koanf.Provider) OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.userProviders = providers\n\t}\n}\n\n// DEPRECATED without replacement. This option is a no-op.\nfunc OmitKeysFromTracing(keys ...string) OptionModifier {\n\treturn func(*Provider) {}\n}\n\nfunc AttachWatcher(watcher func(event watcherx.Event, err error)) OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.onChanges = append(p.onChanges, watcher)\n\t}\n}\n\nfunc WithLogrusWatcher(l *logrusx.Logger) OptionModifier {\n\treturn AttachWatcher(LogrusWatcher(l))\n}\n\nfunc LogrusWatcher(l *logrusx.Logger) func(e watcherx.Event, err error) {\n\treturn func(e watcherx.Event, err error) {\n\t\tl.WithField(\"file\", e.Source()).\n\t\t\tWithField(\"event_type\", fmt.Sprintf(\"%T\", e)).\n\t\t\tInfo(\"A change to a configuration file was detected.\")\n\n\t\tif et := new(jsonschema.ValidationError); errors.As(err, &et) {\n\t\t\tl.WithField(\"event\", fmt.Sprintf(\"%#v\", et)).\n\t\t\t\tErrorf(\"The changed configuration is invalid and could not be loaded. Rolling back to the last working configuration revision. Please address the validation errors before restarting the process.\")\n\t\t} else if et := new(ImmutableError); errors.As(err, &et) {\n\t\t\tl.WithError(err).\n\t\t\t\tWithField(\"key\", et.Key).\n\t\t\t\tWithField(\"old_value\", fmt.Sprintf(\"%v\", et.From)).\n\t\t\t\tWithField(\"new_value\", fmt.Sprintf(\"%v\", et.To)).\n\t\t\t\tErrorf(\"A configuration value marked as immutable has changed. Rolling back to the last working configuration revision. To reload the values please restart the process.\")\n\t\t} else if err != nil {\n\t\t\tl.WithError(err).Errorf(\"An error occurred while watching config file %s\", e.Source())\n\t\t} else {\n\t\t\tl.WithField(\"file\", e.Source()).\n\t\t\t\tWithField(\"event_type\", fmt.Sprintf(\"%T\", e)).\n\t\t\t\tInfo(\"Configuration change processed successfully.\")\n\t\t}\n\t}\n}\n\nfunc WithStderrValidationReporter() OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.onValidationError = func(k *koanf.Koanf, err error) {\n\t\t\tp.printHumanReadableValidationErrors(k, os.Stderr, err)\n\t\t}\n\t}\n}\n\nfunc WithStandardValidationReporter(w io.Writer) OptionModifier {\n\treturn func(p *Provider) {\n\t\tp.onValidationError = func(k *koanf.Koanf, err error) {\n\t\t\tp.printHumanReadableValidationErrors(k, w, err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "oryx/configx/permission.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"os\"\n\t\"os/user\"\n\t\"strconv\"\n)\n\ntype UnixPermission struct {\n\tOwner string\n\tGroup string\n\tMode  os.FileMode\n}\n\nfunc (p *UnixPermission) SetPermission(file string) error {\n\tvar e error\n\te = os.Chmod(file, p.Mode)\n\tif e != nil {\n\t\treturn e\n\t}\n\n\tgid := -1\n\tuid := -1\n\n\tif p.Owner != \"\" {\n\t\tvar userObj *user.User\n\t\tuserObj, e = user.Lookup(p.Owner)\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t\tuid, e = strconv.Atoi(userObj.Uid)\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t}\n\tif p.Group != \"\" {\n\t\tvar group *user.Group\n\t\tgroup, e := user.LookupGroup(p.Group)\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t\tgid, e = strconv.Atoi(group.Gid)\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t}\n\n\te = os.Chown(file, uid, gid)\n\tif e != nil {\n\t\treturn e\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/configx/pflag.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"strings\"\n\n\t\"github.com/knadh/koanf/providers/posflag\"\n\t\"github.com/knadh/koanf/v2\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/spf13/pflag\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/x/jsonschemax\"\n)\n\ntype PFlagProvider struct {\n\tp     *posflag.Posflag\n\tpaths []jsonschemax.Path\n}\n\nfunc NewPFlagProvider(rawSchema []byte, schema *jsonschema.Schema, f *pflag.FlagSet, k *koanf.Koanf) (*PFlagProvider, error) {\n\tpaths, err := getSchemaPaths(rawSchema, schema)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &PFlagProvider{\n\t\tp:     posflag.Provider(f, \".\", k),\n\t\tpaths: paths,\n\t}, nil\n}\n\nfunc (p *PFlagProvider) ReadBytes() ([]byte, error) {\n\treturn nil, errors.New(\"pflag provider does not support this method\")\n}\n\nfunc (p *PFlagProvider) Read() (map[string]interface{}, error) {\n\tall, err := p.p.Read()\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tknownFlags := make(map[string]interface{}, len(all))\n\tfor k, v := range all {\n\t\tk = strings.ReplaceAll(k, \".\", \"-\")\n\t\tfor _, path := range p.paths {\n\t\t\tnormalized := strings.ReplaceAll(path.Name, \".\", \"-\")\n\t\t\tif k == normalized {\n\t\t\t\tknownFlags[k] = v\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn knownFlags, nil\n}\n\nvar _ koanf.Provider = (*PFlagProvider)(nil)\n"
  },
  {
    "path": "oryx/configx/provider.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/url\"\n\t\"os\"\n\t\"reflect\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/inhies/go-bytesize\"\n\t\"github.com/knadh/koanf/parsers/json\"\n\t\"github.com/knadh/koanf/providers/posflag\"\n\t\"github.com/knadh/koanf/v2\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/spf13/pflag\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/x/jsonschemax\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/watcherx\"\n)\n\ntype tuple struct {\n\tKey   string\n\tValue interface{}\n}\n\ntype Provider struct {\n\tl sync.RWMutex\n\t*koanf.Koanf\n\timmutables, exceptImmutables []string\n\n\tschema            []byte\n\tflags             *pflag.FlagSet\n\tvalidator         *jsonschema.Schema\n\tonChanges         []func(watcherx.Event, error)\n\tonValidationError func(k *koanf.Koanf, err error)\n\n\tforcedValues []tuple\n\tbaseValues   []tuple\n\tfiles        []string\n\n\tskipValidation    bool\n\tdisableEnvLoading bool\n\n\tlogger *logrusx.Logger\n\n\tproviders     []koanf.Provider\n\tuserProviders []koanf.Provider\n}\n\nconst (\n\tFlagConfig = \"config\"\n\tDelimiter  = \".\"\n)\n\n// RegisterConfigFlag registers the \"--config\" flag on pflag.FlagSet.\nfunc RegisterConfigFlag(flags *pflag.FlagSet, fallback []string) {\n\tflags.StringSliceP(FlagConfig, \"c\", fallback, \"Config files to load, overwriting in the order specified.\")\n}\n\n// New creates a new provider instance or errors.\n// Configuration values are loaded in the following order:\n//\n// 1. Defaults from the JSON Schema\n// 2. Config files (yaml, yml, toml, json)\n// 3. Command line flags\n// 4. Environment variables\n//\n// There will also be file-watchers started for all config files. To cancel the\n// watchers, cancel the context.\nfunc New(ctx context.Context, schema []byte, modifiers ...OptionModifier) (*Provider, error) {\n\tvalidator, err := getSchema(ctx, schema)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tl := logrus.New()\n\tl.Out = io.Discard\n\n\tp := &Provider{\n\t\tschema:            schema,\n\t\tvalidator:         validator,\n\t\tonValidationError: func(k *koanf.Koanf, err error) {},\n\t\tlogger:            logrusx.New(\"discarding config logger\", \"\", logrusx.UseLogger(l)),\n\t\tKoanf:             koanf.NewWithConf(koanf.Conf{Delim: Delimiter, StrictMerge: true}),\n\t}\n\n\tfor _, m := range modifiers {\n\t\tm(p)\n\t}\n\n\tproviders, err := p.createProviders(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tp.providers = providers\n\n\tk, err := p.newKoanf()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tp.replaceKoanf(k)\n\treturn p, nil\n}\n\nfunc (p *Provider) SkipValidation() bool {\n\treturn p.skipValidation\n}\n\nfunc (p *Provider) createProviders(ctx context.Context) (providers []koanf.Provider, err error) {\n\tdefaultsProvider, err := NewKoanfSchemaDefaults(p.schema, p.validator)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tproviders = append(providers, defaultsProvider)\n\n\t// Workaround for https://github.com/knadh/koanf/pull/47\n\tfor _, t := range p.baseValues {\n\t\tproviders = append(providers, NewKoanfConfmap([]tuple{t}))\n\t}\n\n\tpaths := p.files\n\tif p.flags != nil {\n\t\tp, _ := p.flags.GetStringSlice(FlagConfig)\n\t\tpaths = append(paths, p...)\n\t}\n\n\tp.logger.WithField(\"files\", paths).Debug(\"Adding config files.\")\n\n\tc := make(watcherx.EventChannel)\n\n\tdefer func() {\n\t\tif err == nil && len(paths) > 0 {\n\t\t\tgo p.watchForFileChanges(ctx, c)\n\t\t}\n\t}()\n\tfor _, path := range paths {\n\t\tfp, err := NewKoanfFile(path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif _, err := fp.WatchChannel(ctx, c); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tproviders = append(providers, fp)\n\t}\n\n\tproviders = append(providers, p.userProviders...)\n\n\tif p.flags != nil {\n\t\tpp, err := NewPFlagProvider(p.schema, p.validator, p.flags, p.Koanf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tproviders = append(providers, pp)\n\t}\n\n\tif !p.disableEnvLoading {\n\t\tenvProvider, err := NewKoanfEnv(\"\", p.schema, p.validator)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tproviders = append(providers, envProvider)\n\t}\n\n\t// Workaround for https://github.com/knadh/koanf/pull/47\n\tfor _, t := range p.forcedValues {\n\t\tproviders = append(providers, NewKoanfConfmap([]tuple{t}))\n\t}\n\n\treturn providers, nil\n}\n\nfunc (p *Provider) replaceKoanf(k *koanf.Koanf) {\n\tp.Koanf = k\n}\n\nfunc (p *Provider) validate(k *koanf.Koanf) error {\n\tif p.skipValidation {\n\t\treturn nil\n\t}\n\n\tout, err := k.Marshal(json.Parser())\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\tif err := p.validator.Validate(bytes.NewReader(out)); err != nil {\n\t\tp.onValidationError(k, err)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// newKoanf creates a new koanf instance with all the updated config\n//\n// This is unfortunately required due to several limitations / bugs in koanf:\n//\n// - https://github.com/knadh/koanf/issues/77\n// - https://github.com/knadh/koanf/pull/47\nfunc (p *Provider) newKoanf() (_ *koanf.Koanf, err error) {\n\tk := koanf.New(Delimiter)\n\n\tfor _, provider := range p.providers {\n\t\t// posflag.Posflag requires access to Koanf instance so we recreate the provider here which is a workaround\n\t\t// for posflag.Provider's API.\n\t\tif _, ok := provider.(*posflag.Posflag); ok {\n\t\t\tprovider = posflag.Provider(p.flags, \".\", k)\n\t\t}\n\n\t\tvar opts []koanf.Option\n\t\tif _, ok := provider.(*Env); ok {\n\t\t\topts = append(opts, koanf.WithMergeFunc(MergeAllTypes))\n\t\t}\n\n\t\tif err := k.Load(provider, nil, opts...); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif err := p.validate(k); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn k, nil\n}\n\n// SetTracer does nothing. DEPRECATED without replacement.\nfunc (p *Provider) SetTracer(_ context.Context, _ *otelx.Tracer) {\n}\n\nfunc (p *Provider) runOnChanges(e watcherx.Event, err error) {\n\tfor k := range p.onChanges {\n\t\tp.onChanges[k](e, err)\n\t}\n}\n\nfunc deleteOtherKeys(k *koanf.Koanf, keys []string) {\nouter:\n\tfor _, key := range k.Keys() {\n\t\tfor _, ik := range keys {\n\t\t\tif key == ik {\n\t\t\t\tcontinue outer\n\t\t\t}\n\t\t}\n\t\tk.Delete(key)\n\t}\n}\n\nfunc (p *Provider) reload(e watcherx.Event) {\n\tp.l.Lock()\n\n\tvar err error\n\tdefer func() {\n\t\t// we first want to unlock and then runOnChanges, so that the callbacks can actually use the Provider\n\t\tp.l.Unlock()\n\t\tp.runOnChanges(e, err)\n\t}()\n\n\tnk, err := p.newKoanf()\n\tif err != nil {\n\t\treturn // unlocks & runs changes in defer\n\t}\n\n\toldImmutables, newImmutables := p.Koanf.Copy(), nk.Copy()\n\tdeleteOtherKeys(oldImmutables, p.immutables)\n\tdeleteOtherKeys(newImmutables, p.immutables)\n\n\tfor _, key := range p.exceptImmutables {\n\t\toldImmutables.Delete(key)\n\t\tnewImmutables.Delete(key)\n\t}\n\tif !reflect.DeepEqual(oldImmutables.Raw(), newImmutables.Raw()) {\n\t\tfor _, key := range p.immutables {\n\t\t\tif !reflect.DeepEqual(oldImmutables.Get(key), newImmutables.Get(key)) {\n\t\t\t\terr = NewImmutableError(key, fmt.Sprintf(\"%v\", p.Koanf.Get(key)), fmt.Sprintf(\"%v\", nk.Get(key)))\n\t\t\t\treturn // unlocks & runs changes in defer\n\t\t\t}\n\t\t}\n\t}\n\n\tp.replaceKoanf(nk)\n\n\t// unlocks & runs changes in defer\n}\n\nfunc (p *Provider) watchForFileChanges(ctx context.Context, c watcherx.EventChannel) {\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase e := <-c:\n\t\t\tswitch et := e.(type) {\n\t\t\tcase *watcherx.ErrorEvent:\n\t\t\t\tp.runOnChanges(e, et)\n\t\t\tdefault:\n\t\t\t\tp.reload(e)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// DirtyPatch patches individual config keys without reloading the full config\n//\n// WARNING! This method is only useful to override existing keys in string or number\n// format. DO NOT use this method to override arrays, maps, or other complex types.\n//\n// This method DOES NOT validate the config against the config JSON schema. If you\n// need to validate the config, use the Set method instead.\n//\n// This method can not be used to remove keys from the config as that is not\n// possible without reloading the full config.\nfunc (p *Provider) DirtyPatch(key string, value any) error {\n\tp.l.Lock()\n\tdefer p.l.Unlock()\n\n\tt := tuple{Key: key, Value: value}\n\tkc := NewKoanfConfmap([]tuple{t})\n\n\tp.forcedValues = append(p.forcedValues, t)\n\tp.providers = append(p.providers, kc)\n\n\tif err := p.Koanf.Load(kc, nil, []koanf.Option{}...); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (p *Provider) Set(key string, value interface{}) error {\n\tp.l.Lock()\n\tdefer p.l.Unlock()\n\n\tp.forcedValues = append(p.forcedValues, tuple{Key: key, Value: value})\n\tp.providers = append(p.providers, NewKoanfConfmap([]tuple{{Key: key, Value: value}}))\n\n\tk, err := p.newKoanf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tp.replaceKoanf(k)\n\treturn nil\n}\n\nfunc (p *Provider) BoolF(key string, fallback bool) bool {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tif !p.Koanf.Exists(key) {\n\t\treturn fallback\n\t}\n\n\treturn p.Bool(key)\n}\n\nfunc (p *Provider) StringF(key string, fallback string) string {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tif !p.Koanf.Exists(key) {\n\t\treturn fallback\n\t}\n\n\treturn p.String(key)\n}\n\nfunc (p *Provider) StringsF(key string, fallback []string) (val []string) {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tif !p.Koanf.Exists(key) {\n\t\treturn fallback\n\t}\n\n\treturn p.Strings(key)\n}\n\nfunc (p *Provider) IntF(key string, fallback int) (val int) {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tif !p.Koanf.Exists(key) {\n\t\treturn fallback\n\t}\n\n\treturn p.Int(key)\n}\n\nfunc (p *Provider) Float64F(key string, fallback float64) (val float64) {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tif !p.Koanf.Exists(key) {\n\t\treturn fallback\n\t}\n\n\treturn p.Float64(key)\n}\n\nfunc (p *Provider) DurationF(key string, fallback time.Duration) (val time.Duration) {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tif !p.Koanf.Exists(key) {\n\t\treturn fallback\n\t}\n\n\treturn p.Duration(key)\n}\n\nfunc (p *Provider) ByteSizeF(key string, fallback bytesize.ByteSize) bytesize.ByteSize {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tif !p.Koanf.Exists(key) {\n\t\treturn fallback\n\t}\n\n\tswitch v := p.Koanf.Get(key).(type) {\n\tcase string:\n\t\t// this type usually comes from user input\n\t\tdec, err := bytesize.Parse(v)\n\t\tif err != nil {\n\t\t\tp.logger.WithField(\"key\", key).WithField(\"raw_value\", v).WithError(err).Warnf(\"error parsing byte size value, using fallback of %s\", fallback)\n\t\t\treturn fallback\n\t\t}\n\t\treturn dec\n\tcase float64:\n\t\t// this type comes from json.Unmarshal\n\t\treturn bytesize.ByteSize(v)\n\tcase bytesize.ByteSize:\n\t\treturn v\n\tdefault:\n\t\tp.logger.WithField(\"key\", key).WithField(\"raw_type\", fmt.Sprintf(\"%T\", v)).WithField(\"raw_value\", fmt.Sprintf(\"%+v\", v)).Errorf(\"error converting byte size value because of unknown type, using fallback of %s\", fallback)\n\t\treturn fallback\n\t}\n}\n\nfunc (p *Provider) GetF(key string, fallback interface{}) (val interface{}) {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tif !p.Exists(key) {\n\t\treturn fallback\n\t}\n\n\treturn p.Get(key)\n}\n\nfunc (p *Provider) TracingConfig(serviceName string) *otelx.Config {\n\treturn &otelx.Config{\n\t\tServiceName:           p.StringF(\"tracing.service_name\", serviceName),\n\t\tDeploymentEnvironment: p.StringF(\"tracing.deployment_environment\", \"\"),\n\t\tProvider:              p.String(\"tracing.provider\"),\n\t\tProviders: otelx.ProvidersConfig{\n\t\t\tJaeger: otelx.JaegerConfig{\n\t\t\t\tSampling: otelx.JaegerSampling{\n\t\t\t\t\tServerURL:    p.String(\"tracing.providers.jaeger.sampling.server_url\"),\n\t\t\t\t\tTraceIDRatio: p.Float64F(\"tracing.providers.jaeger.sampling.trace_id_ratio\", 1),\n\t\t\t\t},\n\t\t\t\tLocalAgentAddress: p.String(\"tracing.providers.jaeger.local_agent_address\"),\n\t\t\t},\n\t\t\tZipkin: otelx.ZipkinConfig{\n\t\t\t\tServerURL: p.String(\"tracing.providers.zipkin.server_url\"),\n\t\t\t\tSampling: otelx.ZipkinSampling{\n\t\t\t\t\tSamplingRatio: p.Float64(\"tracing.providers.zipkin.sampling.sampling_ratio\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tOTLP: otelx.OTLPConfig{\n\t\t\t\tServerURL: p.String(\"tracing.providers.otlp.server_url\"),\n\t\t\t\tInsecure:  p.Bool(\"tracing.providers.otlp.insecure\"),\n\t\t\t\tSampling: otelx.OTLPSampling{\n\t\t\t\t\tSamplingRatio: p.Float64F(\"tracing.providers.otlp.sampling.sampling_ratio\", 1),\n\t\t\t\t},\n\t\t\t\tAuthorizationHeader: p.String(\"tracing.providers.otlp.authorization_header\"),\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (p *Provider) RequestURIF(path string, fallback *url.URL) *url.URL {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tswitch t := p.Get(path).(type) {\n\tcase *url.URL:\n\t\treturn t\n\tcase url.URL:\n\t\treturn &t\n\tcase string:\n\t\tif parsed, err := url.ParseRequestURI(t); err == nil {\n\t\t\treturn parsed\n\t\t}\n\t}\n\n\treturn fallback\n}\n\nfunc (p *Provider) URIF(path string, fallback *url.URL) *url.URL {\n\tp.l.RLock()\n\tdefer p.l.RUnlock()\n\n\tswitch t := p.Get(path).(type) {\n\tcase *url.URL:\n\t\treturn t\n\tcase url.URL:\n\t\treturn &t\n\tcase string:\n\t\tif parsed, err := url.Parse(t); err == nil {\n\t\t\treturn parsed\n\t\t}\n\t}\n\n\treturn fallback\n}\n\n// PrintHumanReadableValidationErrors prints human readable validation errors. Duh.\nfunc (p *Provider) PrintHumanReadableValidationErrors(w io.Writer, err error) {\n\tp.printHumanReadableValidationErrors(p.Koanf, w, err)\n}\n\nfunc (p *Provider) printHumanReadableValidationErrors(k *koanf.Koanf, w io.Writer, err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\t_, _ = fmt.Fprintln(os.Stderr, \"\")\n\tconf, innerErr := k.Marshal(json.Parser())\n\tif innerErr != nil {\n\t\t_, _ = fmt.Fprintf(w, \"Unable to unmarshal configuration: %+v\", innerErr)\n\t}\n\n\tjsonschemax.FormatValidationErrorForCLI(w, conf, err)\n}\n"
  },
  {
    "path": "oryx/configx/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\nfunc newCompiler(schema []byte) (string, *jsonschema.Compiler, error) {\n\tid := gjson.GetBytes(schema, \"$id\").String()\n\tif id == \"\" {\n\t\tid = fmt.Sprintf(\"%s.json\", uuid.Must(uuid.NewV4()).String())\n\t}\n\n\tcompiler := jsonschema.NewCompiler()\n\tif err := compiler.AddResource(id, bytes.NewReader(schema)); err != nil {\n\t\treturn \"\", nil, errors.WithStack(err)\n\t}\n\n\t// DO NOT REMOVE THIS\n\tcompiler.ExtractAnnotations = true\n\n\tif err := otelx.AddConfigSchema(compiler); err != nil {\n\t\treturn \"\", nil, err\n\t}\n\tif err := logrusx.AddConfigSchema(compiler); err != nil {\n\t\treturn \"\", nil, err\n\t}\n\tif err := AddSchemaResources(compiler); err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\treturn id, compiler, nil\n}\n"
  },
  {
    "path": "oryx/configx/schema_cache.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\n\t\"github.com/dgraph-io/ristretto/v2\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\nvar schemaCacheConfig = &ristretto.Config[[]byte, *jsonschema.Schema]{\n\t// Hold up to 25 schemas in cache. Usually we only need one.\n\tMaxCost:            25,\n\tNumCounters:        250,\n\tBufferItems:        64,\n\tMetrics:            false,\n\tIgnoreInternalCost: true,\n\tCost: func(*jsonschema.Schema) int64 {\n\t\treturn 1\n\t},\n}\n\nvar schemaCache, _ = ristretto.NewCache(schemaCacheConfig)\n\nfunc getSchema(ctx context.Context, schema []byte) (*jsonschema.Schema, error) {\n\tkey := sha256.Sum256(schema)\n\tif val, found := schemaCache.Get(key[:]); found {\n\t\treturn val, nil\n\t}\n\n\tschemaID, comp, err := newCompiler(schema)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvalidator, err := comp.Compile(ctx, schemaID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tschemaCache.Set(key[:], validator, 1)\n\tschemaCache.Wait()\n\treturn validator, nil\n}\n"
  },
  {
    "path": "oryx/configx/schema_path_cache.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"crypto/sha256\"\n\n\t\"github.com/ory/x/jsonschemax\"\n\n\t\"github.com/dgraph-io/ristretto/v2\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\nvar schemaPathCacheConfig = &ristretto.Config[[]byte, []jsonschemax.Path]{\n\t// Hold up to 25 schemas in cache. Usually we only need one.\n\tMaxCost:            250,\n\tNumCounters:        2500,\n\tBufferItems:        64,\n\tMetrics:            false,\n\tIgnoreInternalCost: true,\n}\n\nvar schemaPathCache, _ = ristretto.NewCache[[]byte, []jsonschemax.Path](schemaPathCacheConfig)\n\nfunc getSchemaPaths(rawSchema []byte, schema *jsonschema.Schema) ([]jsonschemax.Path, error) {\n\tkey := sha256.Sum256(rawSchema)\n\tif val, found := schemaPathCache.Get(key[:]); found {\n\t\treturn val, nil\n\t}\n\n\tkeys, err := jsonschemax.ListPathsWithInitializedSchemaAndArraysIncluded(schema)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tschemaPathCache.Set(key[:], keys, 1)\n\tschemaPathCache.Wait()\n\treturn keys, nil\n}\n"
  },
  {
    "path": "oryx/configx/serve.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"crypto/tls\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"os\"\n\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/tlsx\"\n)\n\nconst (\n\tServeConfigSchemaID = \"ory://serve-config\"\n\tTLSConfigSchemaID   = \"ory://tls-config\"\n)\n\n//go:embed serve.schema.json\nvar ServeConfigSchema []byte\n\n//go:embed tls.schema.json\nvar TLSConfigSchema []byte\n\ntype (\n\tServe struct {\n\t\tHost, WriteListenFile string\n\t\tPort                  int\n\t\tBaseURL               *url.URL\n\t\tSocket                UnixPermission\n\t\tTLS                   TLS\n\t\tRequestLog            ServeRequestLog\n\t}\n\tTLS struct {\n\t\tEnabled                                  bool\n\t\tAllowTerminationFrom                     []string\n\t\tCertBase64, KeyBase64, CertPath, KeyPath string\n\t}\n\tServeRequestLog struct {\n\t\tDisableHealth bool\n\t}\n)\n\nfunc (p *Provider) Serve(prefix string, isDev bool, defaults Serve) *Serve {\n\tprefix = cleanPrefix(prefix)\n\n\tdefaults.Socket.Mode = cmp.Or(defaults.Socket.Mode, 0o755)\n\n\tserve := Serve{\n\t\tHost:            p.StringF(prefix+\"host\", defaults.Host),\n\t\tPort:            p.IntF(prefix+\"port\", defaults.Port),\n\t\tWriteListenFile: p.StringF(prefix+\"write_listen_file\", defaults.WriteListenFile),\n\t\tBaseURL:         p.URIF(prefix+\"base_url\", defaults.BaseURL),\n\t\tSocket: UnixPermission{\n\t\t\tOwner: p.StringF(prefix+\"socket.owner\", defaults.Socket.Owner),\n\t\t\tGroup: p.StringF(prefix+\"socket.group\", defaults.Socket.Group),\n\t\t\tMode:  os.FileMode(p.IntF(prefix+\"socket.mode\", int(defaults.Socket.Mode))),\n\t\t},\n\t\tTLS: p.TLS(prefix+\"tls\", defaults.TLS),\n\t\tRequestLog: ServeRequestLog{\n\t\t\tDisableHealth: p.BoolF(prefix+\"request_log.disable_for_health\", defaults.RequestLog.DisableHealth),\n\t\t},\n\t}\n\n\tif serve.BaseURL == nil {\n\t\tserve.BaseURL = &url.URL{\n\t\t\tScheme: \"http\",\n\t\t\tPath:   \"/\",\n\t\t}\n\t\tif !isDev || serve.TLS.Enabled {\n\t\t\tserve.BaseURL.Scheme = \"https\"\n\t\t}\n\t\thost := serve.Host\n\t\tif host == \"0.0.0.0\" || host == \"\" {\n\t\t\tvar err error\n\t\t\thost, err = os.Hostname()\n\t\t\tif err != nil {\n\t\t\t\tp.logger.WithError(err).Warn(\"Unable to get hostname from system, falling back to 127.0.0.1.\")\n\t\t\t\thost = \"127.0.0.1\"\n\t\t\t}\n\t\t}\n\t\tserve.BaseURL.Host = fmt.Sprintf(\"%s:%d\", host, serve.Port)\n\t}\n\n\treturn &serve\n}\n\nfunc (p *Provider) TLS(prefix string, defaults TLS) TLS {\n\tprefix = cleanPrefix(prefix)\n\n\treturn TLS{\n\t\tEnabled:              p.BoolF(prefix+\"enabled\", defaults.Enabled),\n\t\tAllowTerminationFrom: p.StringsF(prefix+\"allow_termination_from\", defaults.AllowTerminationFrom),\n\t\tCertBase64:           p.StringF(prefix+\"cert.base64\", defaults.CertBase64),\n\t\tKeyBase64:            p.StringF(prefix+\"key.base64\", defaults.KeyBase64),\n\t\tCertPath:             p.StringF(prefix+\"cert.path\", defaults.CertPath),\n\t\tKeyPath:              p.StringF(prefix+\"key.path\", defaults.KeyPath),\n\t}\n}\n\nfunc (t *TLS) GetCertFunc(ctx context.Context, l *logrusx.Logger, ifaceName string) (tlsx.CertFunc, error) {\n\tswitch {\n\tcase t.CertBase64 != \"\" && t.KeyBase64 != \"\":\n\t\tcert, err := tlsx.CertificateFromBase64(t.CertBase64, t.KeyBase64)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to load TLS certificate for interface %s: %w\", ifaceName, err)\n\t\t}\n\t\tl.Infof(\"Setting up HTTPS for %s\", ifaceName)\n\t\treturn func(*tls.ClientHelloInfo) (*tls.Certificate, error) { return &cert, nil }, nil\n\tcase t.CertPath != \"\" && t.KeyPath != \"\":\n\t\terrs := make(chan error, 1)\n\t\tgetCert, err := tlsx.GetCertificate(ctx, t.CertPath, t.KeyPath, errs)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to load TLS certificate for interface %s: %w\", ifaceName, err)\n\t\t}\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn\n\t\t\t\tcase err := <-errs:\n\t\t\t\t\tl.WithError(err).Error(\"Failed to reload TLS certificates, using previous certificates\")\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t\tl.Infof(\"Setting up HTTPS for %s (automatic certificate reloading active)\", ifaceName)\n\t\treturn getCert, nil\n\tdefault:\n\t\tl.Infof(\"TLS has not been configured for %s, skipping\", ifaceName)\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "oryx/configx/serve.schema.json",
    "content": "{\n  \"$id\": \"https://github.com/ory/x/configx/serve.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"request_log\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"disable_for_health\": {\n          \"title\": \"Disable health endpoints request logging\",\n          \"description\": \"Disable request logging for /health/alive and /health/ready endpoints\",\n          \"type\": \"boolean\",\n          \"default\": false\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"base_url\": {\n      \"title\": \"Base URL\",\n      \"description\": \"The URL where the endpoint is exposed at. This domain is used to generate redirects, form URLs, and more.\",\n      \"type\": \"string\",\n      \"format\": \"uri-reference\",\n      \"examples\": [\n        \"https://my-app.com/\",\n        \"https://my-app.com/.ory/kratos/public\",\n        \"https://auth.my-app.com/hydra\"\n      ]\n    },\n    \"host\": {\n      \"title\": \"Host\",\n      \"description\": \"The host (interface) that the endpoint listens on.\",\n      \"type\": \"string\",\n      \"default\": \"0.0.0.0\"\n    },\n    \"port\": {\n      \"title\": \"Port\",\n      \"description\": \"The port that the endpoint listens on.\",\n      \"type\": \"integer\",\n      \"minimum\": 1,\n      \"maximum\": 65535\n    },\n    \"socket\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Sets the permissions of the unix socket\",\n      \"properties\": {\n        \"owner\": {\n          \"type\": \"string\",\n          \"description\": \"Owner of unix socket. If empty, the owner will be the user running the service.\",\n          \"default\": \"\"\n        },\n        \"group\": {\n          \"type\": \"string\",\n          \"description\": \"Group of unix socket. If empty, the group will be the primary group of the user running the service.\",\n          \"default\": \"\"\n        },\n        \"mode\": {\n          \"type\": \"integer\",\n          \"description\": \"Mode of unix socket in numeric form\",\n          \"default\": 493,\n          \"minimum\": 0,\n          \"maximum\": 511\n        }\n      }\n    },\n    \"tls\": {\n      \"$ref\": \"ory://tls-config\"\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/configx/span.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage configx\n\nconst (\n\tLoadSpanOpName     = \"config-load\"\n\tUpdatedSpanOpName  = \"config-update\"\n\tSnapshotSpanOpName = \"config-snapshot\"\n)\n"
  },
  {
    "path": "oryx/configx/stub/benchmark/benchmark.yaml",
    "content": "# Please find the documentation for this file at\n# https://www.ory.sh/oathkeeper/docs/configuration\n\nlog:\n  level: debug\n  format: json\n\nprofiling: cpu\n\nserve:\n  proxy:\n    port: 1234\n    host: 127.0.0.1\n\n    timeout:\n      read: 1s\n      write: 2s\n      idle: 3s\n\n    cors:\n      enabled: true\n      allowed_origins:\n        - https://example.com\n        - https://*.example.com\n      allowed_methods:\n        - POST\n        - GET\n        - PUT\n        - PATCH\n        - DELETE\n      allowed_headers:\n        - Authorization\n        - Content-Type\n      exposed_headers:\n        - Content-Type\n      allow_credentials: true\n      max_age: 10\n      debug: true\n    tls:\n      key:\n        path: /path/to/key.pem\n        base64: LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3...\n      cert:\n        path: /path/to/cert.pem\n        base64: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\n\n  api:\n    port: 1235\n    host: 127.0.0.2\n\n    timeout:\n      read: 1s\n      write: 2s\n      idle: 3s\n\n    cors:\n      enabled: true\n      allowed_origins:\n        - https://example.org\n        - https://*.example.org\n      allowed_methods:\n        - GET\n        - PUT\n        - PATCH\n        - DELETE\n      allowed_headers:\n        - Authorization\n        - Content-Type\n      exposed_headers:\n        - Content-Type\n      allow_credentials: true\n      max_age: 10\n      debug: true\n    tls:\n      key:\n        path: /path/to/key.pem\n        base64: LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3...\n      cert:\n        path: /path/to/cert.pem\n        base64: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\n\n  prometheus:\n    port: 9000\n    host: localhost\n    metrics_path: /metrics\n    collapse_request_paths: true\n\n# Configures Access Rules\naccess_rules:\n  # Locations (list of URLs) where access rules should be fetched from on boot.\n  # It is expected that the documents at those locations return a JSON or YAML Array containing ORY Oathkeeper Access Rules.\n  repositories:\n    # If the URL Scheme is `file://`, the access rules (an array of access rules is expected) will be\n    # fetched from the local file system.\n    - file://path/to/rules.json\n    # If the URL Scheme is `inline://`, the access rules (an array of access rules is expected)\n    # are expected to be a base64 encoded (with padding!) JSON/YAML string (base64_encode(`[{\"id\":\"foo-rule\",\"authenticators\":[....]}]`)):\n    - inline://W3siaWQiOiJmb28tcnVsZSIsImF1dGhlbnRpY2F0b3JzIjpbXX1d\n    # If the URL Scheme is `http://` or `https://`, the access rules (an array of access rules is expected) will be\n    # fetched from the provided HTTP(s) location.\n    - https://path-to-my-rules/rules.json\n  # Optional fields describing matching strategy, defaults to \"regexp\".\n  matching_strategy: glob\n\nerrors:\n  fallback:\n    - json\n  handlers:\n    redirect:\n      enabled: true\n      config:\n        to: http://path-to/redirect\n    json:\n      enabled: true\n      config:\n        verbose: true\n        when:\n          - error:\n              - unauthorized\n              - forbidden\n              - internal_server_error\n            request:\n              header:\n                content_type:\n                  - application/json\n                accept:\n                  - application/json\n              cidr:\n                - 127.0.0.0/24\n\n# All authenticators can be configured under this configuration key\nauthenticators:\n  # Configures the anonymous authenticator\n  anonymous:\n    # Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.\n    enabled: true\n\n    config:\n      # Sets the anonymous username. Defaults to \"anonymous\". Common names include \"guest\", \"anon\", \"anonymous\", \"unknown\".\n      subject: guest\n\n  # Configures the cookie session authenticator\n  cookie_session:\n    # Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.\n    enabled: true\n\n    config:\n      # Sets the origin to proxy requests to. If the response is a 200 with body `{ \"subject\": \"...\", \"extra\": {} }`\n      # The request will pass the subject through successfully, otherwise it will be marked as unauthorized\n      check_session_url: https://session-store-host\n\n      # Sets a list of possible cookies to look for on incoming requests, and will fallthrough to the next authenticator if\n      # none of the passed cookies are set on the request\n      only:\n        - sessionid\n\n  # Configures the jwt authenticator\n  jwt:\n    # Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.\n    enabled: true\n\n    config:\n      # REQUIRED IF ENABLED - The URL where ORY Oathkeeper can retrieve JSON Web Keys from for validating the JSON Web\n      # Token. Usually something like \"https://my-keys.com/.well-known/jwks.json\". The response of that endpoint must\n      # return a JSON Web Key Set (JWKS).\n      jwks_urls:\n        - https://my-website.com/.well-known/jwks.json\n        - https://my-other-website.com/.well-known/jwks.json\n        - file://path/to/local/jwks.json\n\n      # Sets the strategy to be used to validate/match the scope. Supports \"hierarchic\", \"exact\", \"wildcard\", \"none\". Defaults\n      # to \"none\".\n      scope_strategy: wildcard\n\n  # Configures the noop authenticator\n  noop:\n    # Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.\n    enabled: true\n\n  # Configures the oauth2_client_credentials authenticator\n  oauth2_client_credentials:\n    # Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.\n    enabled: true\n\n    config:\n      # REQUIRED IF ENABLED - The OAuth 2.0 Token Endpoint that will be used to validate the client credentials.\n      token_url: https://my-website.com/oauth2/token\n\n  # Configures the oauth2_introspection authenticator\n  oauth2_introspection:\n    # Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.\n    enabled: true\n\n    config:\n      # REQUIRED IF ENABLED - The OAuth 2.0 Token Introspection endpoint.\n      introspection_url: https://my-website.com/oauth2/introspection\n\n      # Sets the strategy to be used to validate/match the token scope. Supports \"hierarchic\", \"exact\", \"wildcard\", \"none\". Defaults\n      # to \"none\".\n      scope_strategy: exact\n\n      # Enable pre-authorization in cases where the OAuth 2.0 Token Introspection endpoint is protected by OAuth 2.0 Bearer\n      # Tokens that can be retrieved using the OAuth 2.0 Client Credentials grant.\n      pre_authorization:\n        # Enable pre-authorization. Defaults to false.\n        enabled: true\n\n        # REQUIRED IF ENABLED - The OAuth 2.0 Client ID to be used for the OAuth 2.0 Client Credentials Grant.\n        client_id: some_id\n\n        # REQUIRED IF ENABLED - The OAuth 2.0 Client Secret to be used for the OAuth 2.0 Client Credentials Grant.\n        client_secret: some_secret\n\n        # The OAuth 2.0 Scope to be requested during the OAuth 2.0 Client Credentials Grant.\n        scope:\n          - foo\n          - bar\n\n        # REQUIRED IF ENABLED - The OAuth 2.0 Token Endpoint where the OAuth 2.0 Client Credentials Grant will be performed.\n        token_url: https://my-website.com/oauth2/token\n\n  # Configures the unauthorized authenticator\n  unauthorized:\n    # Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.\n    enabled: true\n\n# All authorizers can be configured under this configuration key\nauthorizers:\n  # Configures the allow authorizer\n  allow:\n    # Set enabled to true if the authorizer should be enabled and false to disable the authorizer. Defaults to false.\n    enabled: true\n\n  # Configures the deny authorizer\n  deny:\n    # Set enabled to true if the authorizer should be enabled and false to disable the authorizer. Defaults to false.\n    enabled: true\n\n  # Configures the keto_engine_acp_ory authorizer\n  keto_engine_acp_ory:\n    # Set enabled to true if the authorizer should be enabled and false to disable the authorizer. Defaults to false.\n    enabled: true\n\n    config:\n      # REQUIRED IF ENABLED - The base URL of ORY Keto, typically something like http(s)://<host>[:<port>]/\n      base_url: http://my-keto/\n      required_action: unknown\n      required_resource: unknown\n\n  # Configures the remote authorizer\n  remote:\n    # Set enabled to true if the authorizer should be enabled and false to disable the authorizer. Defaults to false.\n    enabled: true\n\n    config:\n      remote: https://host/path\n      headers: {}\n\n  # Configures the remote_json authorizer\n  remote_json:\n    # Set enabled to true if the authorizer should be enabled and false to disable the authorizer. Defaults to false.\n    enabled: true\n\n    config:\n      remote: https://host/path\n      payload: \"{}\"\n\n# All mutators can be configured under this configuration key\nmutators:\n  header:\n    enabled: true\n    config:\n      headers:\n        foo: bar\n\n  # Configures the cookie mutator\n  cookie:\n    # Set enabled to true if the mutator should be enabled and false to disable the mutator. Defaults to false.\n    enabled: true\n    config:\n      cookies:\n        foo: bar\n\n  # Configures the hydrator mutator\n  hydrator:\n    # Set enabled to true if the mutator should be enabled and false to disable the mutator. Defaults to false.\n    enabled: true\n\n    config:\n      api:\n        url: https://some-url/\n\n  # Configures the id_token mutator\n  id_token:\n    # Set enabled to true if the mutator should be enabled and false to disable the mutator. Defaults to false.\n    enabled: true\n    config:\n      # REQUIRED IF ENABLED - Sets the \"iss\" value of the ID Token.\n      issuer_url: https://my-oathkeeper/\n      # REQUIRED IF ENABLED - Sets the URL where keys should be fetched from. Supports remote locations (http, https) as\n      # well as local filesystem paths.\n      jwks_url: https://fetch-keys/from/this/location.json\n      # jwks_url: file:///from/this/absolute/location.json\n      # jwks_url: file://../from/this/relative/location.json\n\n      # Sets the time-to-live of the ID token. Defaults to one minute. Valid time units are: s (second), m (minute), h (hour).\n      ttl: 1h\n\n  # Configures the noop mutator\n  noop:\n    # Set enabled to true if the mutator should be enabled and false to disable the mutator. Defaults to false.\n    enabled: true\n"
  },
  {
    "path": "oryx/configx/stub/benchmark/schema.config.json",
    "content": "{\n  \"log\": {\n    \"level\": \"debug\",\n    \"format\": \"json\"\n  },\n  \"profiling\": \"cpu\",\n  \"serve\": {\n    \"proxy\": {\n      \"port\": 1234,\n      \"host\": \"127.0.0.1\",\n      \"timeout\": {\n        \"read\": \"1s\",\n        \"write\": \"2s\",\n        \"idle\": \"3s\"\n      },\n      \"cors\": {\n        \"enabled\": true,\n        \"allowed_origins\": [\"https://example.com\", \"https://*.example.com\"],\n        \"allowed_methods\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n        \"allowed_headers\": [\"Authorization\", \"Content-Type\"],\n        \"exposed_headers\": [\"Content-Type\"],\n        \"allow_credentials\": true,\n        \"max_age\": 10,\n        \"debug\": true\n      },\n      \"tls\": {\n        \"key\": {\n          \"path\": \"/path/to/key.pem\",\n          \"base64\": \"LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3...\"\n        },\n        \"cert\": {\n          \"path\": \"/path/to/cert.pem\",\n          \"base64\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n        }\n      }\n    },\n    \"api\": {\n      \"port\": 1235,\n      \"host\": \"127.0.0.2\",\n      \"cors\": {\n        \"enabled\": true,\n        \"allowed_origins\": [\"https://example.org\", \"https://*.example.org\"],\n        \"allowed_methods\": [\"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n        \"allowed_headers\": [\"Authorization\", \"Content-Type\"],\n        \"exposed_headers\": [\"Content-Type\"],\n        \"allow_credentials\": true,\n        \"max_age\": 10,\n        \"debug\": true\n      },\n      \"tls\": {\n        \"key\": {\n          \"path\": \"/path/to/key.pem\",\n          \"base64\": \"LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3...\"\n        },\n        \"cert\": {\n          \"path\": \"/path/to/cert.pem\",\n          \"base64\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n        }\n      }\n    }\n  },\n  \"access_rules\": {\n    \"repositories\": [\n      \"file://path/to/rules.json\",\n      \"inline://W3siaWQiOiJmb28tcnVsZSIsImF1dGhlbnRpY2F0b3JzIjpbXX1d\",\n      \"https://path-to-my-rules/rules.json\"\n    ],\n    \"matching_strategy\": \"glob\"\n  },\n  \"errors\": {\n    \"fallback\": [\"json\"],\n    \"handlers\": {\n      \"redirect\": {\n        \"enabled\": true,\n        \"config\": {\n          \"to\": \"http://path-to/redirect\"\n        }\n      },\n      \"json\": {\n        \"enabled\": true,\n        \"config\": {\n          \"verbose\": true,\n          \"when\": [\n            {\n              \"error\": [\"unauthorized\", \"forbidden\", \"internal_server_error\"],\n              \"request\": {\n                \"header\": {\n                  \"content_type\": [\"application/json\"],\n                  \"accept\": [\"application/json\"]\n                },\n                \"cidr\": [\"127.0.0.0/24\"]\n              }\n            }\n          ]\n        }\n      }\n    }\n  },\n  \"authenticators\": {\n    \"anonymous\": {\n      \"enabled\": true,\n      \"config\": {\n        \"subject\": \"guest\"\n      }\n    },\n    \"cookie_session\": {\n      \"enabled\": true,\n      \"config\": {\n        \"check_session_url\": \"https://session-store-host\",\n        \"only\": [\"sessionid\"]\n      }\n    },\n    \"jwt\": {\n      \"enabled\": true,\n      \"config\": {\n        \"jwks_urls\": [\n          \"https://my-website.com/.well-known/jwks.json\",\n          \"https://my-other-website.com/.well-known/jwks.json\",\n          \"file://path/to/local/jwks.json\"\n        ],\n        \"scope_strategy\": \"wildcard\"\n      }\n    },\n    \"noop\": {\n      \"enabled\": true\n    },\n    \"oauth2_client_credentials\": {\n      \"enabled\": true,\n      \"config\": {\n        \"token_url\": \"https://my-website.com/oauth2/token\"\n      }\n    },\n    \"oauth2_introspection\": {\n      \"enabled\": true,\n      \"config\": {\n        \"introspection_url\": \"https://my-website.com/oauth2/introspection\",\n        \"scope_strategy\": \"exact\",\n        \"pre_authorization\": {\n          \"enabled\": true,\n          \"client_id\": \"some_id\",\n          \"client_secret\": \"some_secret\",\n          \"scope\": [\"foo\", \"bar\"],\n          \"token_url\": \"https://my-website.com/oauth2/token\"\n        }\n      }\n    },\n    \"unauthorized\": {\n      \"enabled\": true\n    }\n  },\n  \"authorizers\": {\n    \"allow\": {\n      \"enabled\": true\n    },\n    \"deny\": {\n      \"enabled\": true\n    },\n    \"keto_engine_acp_ory\": {\n      \"enabled\": true,\n      \"config\": {\n        \"base_url\": \"http://my-keto/\",\n        \"required_action\": \"unknown\",\n        \"required_resource\": \"unknown\"\n      }\n    }\n  },\n  \"mutators\": {\n    \"header\": {\n      \"enabled\": false,\n      \"config\": {\n        \"headers\": {\n          \"foo\": \"bar\"\n        }\n      }\n    },\n    \"cookie\": {\n      \"enabled\": true,\n      \"config\": {\n        \"cookies\": {\n          \"foo\": \"bar\"\n        }\n      }\n    },\n    \"hydrator\": {\n      \"enabled\": true,\n      \"config\": {\n        \"api\": {\n          \"url\": \"https://some-url/\"\n        }\n      }\n    },\n    \"id_token\": {\n      \"enabled\": true,\n      \"config\": {\n        \"issuer_url\": \"https://my-oathkeeper/\",\n        \"jwks_url\": \"https://fetch-keys/from/this/location.json\",\n        \"ttl\": \"1h\"\n      }\n    },\n    \"noop\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/configx/stub/domain-aliases/config.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"properties\": {\n    \"domain_aliases\": {\n      \"title\": \"Domain Aliases\",\n      \"description\": \"Adds an alias domain. If a request with the hostname (FQDN) matching the hostname in the alias is found, that URL is used as the base URL.\",\n      \"type\": \"array\",\n      \"items\": [\n        {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"required\": [\"match_domain\", \"base_path\", \"scheme\"],\n          \"properties\": {\n            \"match_domain\": {\n              \"minLength\": 1,\n              \"title\": \"Matching Domain\",\n              \"description\": \"Sets the matching domain. If the domain matches with this entry, the accompanying base_url will be used.\",\n              \"type\": \"string\",\n              \"examples\": [\"localhost\", \"my-domain.com\"]\n            },\n            \"scheme\": {\n              \"title\": \"Scheme\",\n              \"description\": \"Sets the scheme, for example https or http.\",\n              \"type\": \"string\",\n              \"enum\": [\"http\", \"https\"]\n            },\n            \"base_path\": {\n              \"minLength\": 1,\n              \"title\": \"Base Path\",\n              \"description\": \"Sets the base path for the matched domain.\",\n              \"type\": \"string\",\n              \"default\": \"/\",\n              \"pattern\": \"^/.*$\",\n              \"examples\": [\"/\", \"/.ory/kratos\"]\n            }\n          }\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/configx/stub/from-files/a.yaml",
    "content": "version: v0.5.3-alpha.1\n\ndsn: memory\n\nserve:\n  public:\n    base_url: http://127.0.0.1:4433/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nselfservice:\n  default_browser_return_url: http://127.0.0.1:4455/\n  whitelisted_return_urls:\n    - http://127.0.0.1:4455\n\n  methods:\n    password:\n      enabled: true\n\n  flows:\n    error:\n      ui_url: http://127.0.0.1:4455/error\n\n    settings:\n      ui_url: http://127.0.0.1:4455/settings\n"
  },
  {
    "path": "oryx/configx/stub/from-files/b.yaml",
    "content": "selfservice:\n  flows:\n    settings:\n      privileged_session_max_age: 15m\n\n    recovery:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/recovery\n\n    verification:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/verify\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/\n\n    logout:\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/auth/login\n\n    login:\n      ui_url: http://127.0.0.1:4455/auth/login\n      lifespan: 10m\n\n    registration:\n      lifespan: 10m\n      ui_url: http://127.0.0.1:4455/auth/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n\nhashers:\n  argon2:\n    parallelism: 1\n    memory: 131072\n    iterations: 2\n    salt_length: 16\n    key_length: 16\n\nidentity:\n  default_schema_url: file:///etc/config/kratos/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\n"
  },
  {
    "path": "oryx/configx/stub/from-files/config.schema.json",
    "content": "{\n  \"$id\": \"https://github.com/ory/kratos/.schema/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"ORY Kratos Configuration\",\n  \"type\": \"object\",\n  \"definitions\": {\n    \"defaultReturnTo\": {\n      \"title\": \"Redirect browsers to set URL per default\",\n      \"description\": \"ORY Kratos redirects to this URL per default on completion of self-service flows and other browser interaction. Read this [article for more information on browser redirects](https://www.ory.sh/kratos/docs/concepts/browser-redirect-flow-completion).\",\n      \"type\": \"string\",\n      \"format\": \"uri-reference\",\n      \"minLength\": 1,\n      \"examples\": [\"https://my-app.com/dashboard\", \"/dashboard\"]\n    },\n    \"selfServiceSessionRevokerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"revoke_active_sessions\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceVerifyHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"verify\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceSessionIssuerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"session\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"OIDCClaims\": {\n      \"title\": \"OpenID Connect claims\",\n      \"description\": \"The OpenID Connect claims and optionally their properties which should be included in the id_token or returned from the UserInfo Endpoint.\",\n      \"type\": \"object\",\n      \"examples\": [\n        {\n          \"id_token\": {\n            \"email\": null,\n            \"email_verified\": null\n          }\n        },\n        {\n          \"userinfo\": {\n            \"given_name\": {\n              \"essential\": true\n            },\n            \"nickname\": null,\n            \"email\": {\n              \"essential\": true\n            },\n            \"email_verified\": {\n              \"essential\": true\n            },\n            \"picture\": null,\n            \"http://example.info/claims/groups\": null\n          },\n          \"id_token\": {\n            \"auth_time\": {\n              \"essential\": true\n            },\n            \"acr\": {\n              \"values\": [\"urn:mace:incommon:iap:silver\"]\n            }\n          }\n        }\n      ],\n      \"patternProperties\": {\n        \"^userinfo$|^id_token$\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"patternProperties\": {\n            \".*\": {\n              \"oneOf\": [\n                {\n                  \"const\": null,\n                  \"description\": \"Indicates that this Claim is being requested in the default manner.\"\n                },\n                {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"essential\": {\n                      \"description\": \"Indicates whether the Claim being requested is an Essential Claim.\",\n                      \"type\": \"boolean\"\n                    },\n                    \"value\": {\n                      \"description\": \"Requests that the Claim be returned with a particular value.\",\n                      \"$comment\": \"There seem to be no constrains on value\"\n                    },\n                    \"values\": {\n                      \"description\": \"Requests that the Claim be returned with one of a set of values, with the values appearing in order of preference.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$comment\": \"There seem to be no constrains on individual items\"\n                      }\n                    }\n                  }\n                }\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"selfServiceOIDCProvider\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"examples\": [\"google\"]\n        },\n        \"provider\": {\n          \"title\": \"Provider\",\n          \"description\": \"Can be one of github, gitlab, generic, google, microsoft, discord.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"github\",\n            \"gitlab\",\n            \"generic\",\n            \"google\",\n            \"microsoft\",\n            \"discord\"\n          ],\n          \"examples\": [\"google\"]\n        },\n        \"client_id\": {\n          \"type\": \"string\"\n        },\n        \"client_secret\": {\n          \"type\": \"string\"\n        },\n        \"issuer_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com\"]\n        },\n        \"auth_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com/o/oauth2/v2/auth\"]\n        },\n        \"token_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://www.googleapis.com/oauth2/v4/token\"]\n        },\n        \"mapper_url\": {\n          \"title\": \"Jsonnet Mapper URL\",\n          \"description\": \"The URL where the jsonnet source is located for mapping the provider's data to ORY Kratos data.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/oidc.jsonnet\",\n            \"https://foo.bar.com/path/to/oidc.jsonnet\",\n            \"base64://bG9jYWwgc3ViamVjdCA9I...\"\n          ]\n        },\n        \"scope\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"examples\": [\"offline_access\", \"profile\"]\n          }\n        },\n        \"tenant\": {\n          \"title\": \"Azure AD Tenant\",\n          \"description\": \"The Azure AD Tenant to use for authentication.\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"common\",\n            \"organizations\",\n            \"consumers\",\n            \"8eaef023-2b34-4da1-9baa-8bc8c9d6a490\",\n            \"contoso.onmicrosoft.com\"\n          ]\n        },\n        \"requested_claims\": {\n          \"$ref\": \"#/definitions/OIDCClaims\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\n        \"id\",\n        \"provider\",\n        \"client_id\",\n        \"client_secret\",\n        \"mapper_url\"\n      ],\n      \"if\": {\n        \"properties\": {\n          \"provider\": {\n            \"const\": \"microsoft\"\n          }\n        },\n        \"required\": [\"provider\"]\n      },\n      \"then\": {\n        \"required\": [\"tenant\"]\n      },\n      \"else\": {\n        \"not\": {\n          \"properties\": {\n            \"tenant\": {}\n          },\n          \"required\": [\"tenant\"]\n        }\n      }\n    },\n    \"selfServiceAfterSettingsMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceVerifyHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterLoginMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterRegistrationMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionIssuerHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterSettings\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsMethod\"\n        },\n        \"profile\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsMethod\"\n        }\n      }\n    },\n    \"selfServiceAfterLogin\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterLoginMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterLoginMethod\"\n        }\n      }\n    },\n    \"selfServiceAfterRegistration\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        }\n      }\n    }\n  },\n  \"properties\": {\n    \"selfservice\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default_browser_return_url\"],\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"whitelisted_return_urls\": {\n          \"title\": \"Whitelisted Return To URLs\",\n          \"description\": \"List of URLs that are allowed to be redirected to. A redirection request is made by appending `?return_to=...` to Login, Registration, and other self-service flows.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"format\": \"uri-reference\"\n          },\n          \"examples\": [\n            [\n              \"https://app.my-app.com/dashboard\",\n              \"/dashboard\",\n              \"https://www.my-app.com/\"\n            ]\n          ],\n          \"uniqueItems\": true\n        },\n        \"flows\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"settings\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"URL of the Settings page.\",\n                  \"description\": \"URL where the Settings UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/user/settings\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/settings\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"privileged_session_max_age\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterSettings\"\n                }\n              }\n            },\n            \"logout\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"after\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  }\n                }\n              }\n            },\n            \"registration\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Registration UI URL\",\n                  \"description\": \"URL where the Registration UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/signup\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/registration\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterRegistration\"\n                }\n              }\n            },\n            \"login\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Login UI URL\",\n                  \"description\": \"URL where the Login UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/login\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/login\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterLogin\"\n                }\n              }\n            },\n            \"verification\": {\n              \"title\": \"Email and Phone Verification and Account Activation Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Email/Phone Verification\",\n                  \"description\": \"If set to true will enable [Email and Phone Verification and Account Activation](https://www.ory.sh/kratos/docs/self-service/flows/verify-email-account-activation/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Verify UI URL\",\n                  \"description\": \"URL where the ORY Verify UI is hosted. This is the page where users activate and / or verify their email or telephone number. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/verification\"\n                },\n                \"after\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  },\n                  \"additionalProperties\": false\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Verification Request Lifespan\",\n                  \"description\": \"Sets how long the verification request (for the UI interaction) is valid.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                }\n              }\n            },\n            \"recovery\": {\n              \"title\": \"Account Recovery Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Account Recovery\",\n                  \"description\": \"If set to true will enable [Account Recovery](https://www.ory.sh/kratos/docs/self-service/flows/password-reset-account-recovery/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Recovery UI URL\",\n                  \"description\": \"URL where the ORY Recovery UI is hosted. This is the page where users request and complete account recovery. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/recovery\"\n                },\n                \"after\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  },\n                  \"additionalProperties\": false\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Recovery Request Lifespan\",\n                  \"description\": \"Sets how long the recovery request is valid. If expired, the user has to redo the flow.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                }\n              }\n            },\n            \"error\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"ORY Kratos Error UI URL\",\n                  \"description\": \"URL where the ORY Kratos Error UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/kratos-error\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/error\"\n                }\n              }\n            }\n          }\n        },\n        \"methods\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"profile\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Profile Management Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"link\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Link Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"password\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Username/Email and Password Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"oidc\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables OpenID Connect Method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"providers\": {\n                      \"title\": \"OpenID Connect and OAuth2 Providers\",\n                      \"description\": \"A list and configuration of OAuth2 and OpenID Connect providers ORY Kratos should integrate with.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$ref\": \"#/definitions/selfServiceOIDCProvider\"\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    },\n    \"dsn\": {\n      \"type\": \"string\",\n      \"title\": \"Data Source Name\",\n      \"description\": \"DSN is used to specify the database credentials as a connection URI.\",\n      \"examples\": [\n        \"postgres://user: password@postgresd:5432/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"mysql://user:secret@tcp(mysqld:3306)/database?max_conns=20&max_idle_conns=4\",\n        \"cockroach://user@cockroachdb:26257/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc\"\n      ]\n    },\n    \"courier\": {\n      \"type\": \"object\",\n      \"title\": \"Courier configuration\",\n      \"description\": \"The courier is responsible for sending and delivering messages over email, sms, and other means.\",\n      \"properties\": {\n        \"template_override_path\": {\n          \"type\": \"string\",\n          \"title\": \"Override message templates\",\n          \"description\": \"You can override certain or all message templates by pointing this key to the path where the templates are located.\",\n          \"examples\": [\"/conf/courier-templates\"]\n        },\n        \"smtp\": {\n          \"title\": \"SMTP Configuration\",\n          \"description\": \"Configures outgoing emails using the SMTP protocol.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"connection_uri\": {\n              \"title\": \"SMTP connection string\",\n              \"description\": \"This URI will be used to connect to the SMTP server. Use the query parameter to allow (`?skip_ssl_verify=true`) or disallow (`?skip_ssl_verify=false`) self-signed TLS certificates. Please keep in mind that any host other than localhost / 127.0.0.1 must use smtp over TLS (smtps) or the connection will not be possible.\",\n              \"examples\": [\n                \"smtps://foo:bar@my-mailserver:1234/?skip_ssl_verify=false\"\n              ],\n              \"type\": \"string\",\n              \"format\": \"uri\"\n            },\n            \"from_address\": {\n              \"title\": \"SMTP Sender Address\",\n              \"description\": \"The recipient of an email will see this as the sender address.\",\n              \"type\": \"string\",\n              \"format\": \"email\",\n              \"default\": \"no-reply@ory.kratos.sh\"\n            }\n          },\n          \"required\": [\"connection_uri\"],\n          \"additionalProperties\": false\n        }\n      },\n      \"required\": [\"smtp\"],\n      \"additionalProperties\": false\n    },\n    \"serve\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"admin\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"base_url\": {\n              \"title\": \"Admin Base URL\",\n              \"description\": \"The URL where the admin endpoint is exposed at.\",\n              \"type\": \"string\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://kratos.private-network:4434/\"]\n            },\n            \"host\": {\n              \"title\": \"Admin Host\",\n              \"description\": \"The host (interface) kratos' admin endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Admin Port\",\n              \"description\": \"The port kratos' admin endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4434],\n              \"default\": 4434\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"public\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"cors\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"description\": \"Configures Cross Origin Resource Sharing for public endpoints.\",\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether CORS is enabled.\",\n                  \"default\": false\n                },\n                \"allowed_origins\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin.\",\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"minLength\": 1,\n                    \"not\": {\n                      \"type\": \"string\",\n                      \"description\": \"does match all strings that contain two or more (*)\",\n                      \"pattern\": \".*\\\\*.*\\\\*.*\"\n                    },\n                    \"anyOf\": [\n                      {\n                        \"format\": \"uri\"\n                      },\n                      {\n                        \"const\": \"*\"\n                      }\n                    ]\n                  },\n                  \"uniqueItems\": true,\n                  \"default\": [\"*\"],\n                  \"examples\": [\n                    [\n                      \"https://example.com\",\n                      \"https://*.example.com\",\n                      \"https://*.foo.example.com\"\n                    ]\n                  ]\n                },\n                \"allowed_methods\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of HTTP methods the user agent is allowed to use with cross-domain requests.\",\n                  \"default\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"enum\": [\n                      \"POST\",\n                      \"GET\",\n                      \"PUT\",\n                      \"PATCH\",\n                      \"DELETE\",\n                      \"CONNECT\",\n                      \"HEAD\",\n                      \"OPTIONS\",\n                      \"TRACE\"\n                    ]\n                  }\n                },\n                \"allowed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n                  \"default\": [\n                    \"Authorization\",\n                    \"Content-Type\",\n                    \"X-Session-Token\"\n                  ],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"exposed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"Sets which headers are safe to expose to the API of a CORS API specification.\",\n                  \"default\": [\"Content-Type\"],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"allow_credentials\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\",\n                  \"default\": true\n                },\n                \"options_passthrough\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"TODO\",\n                  \"default\": false\n                },\n                \"max_age\": {\n                  \"type\": \"integer\",\n                  \"description\": \"Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request is preceded by a preflight request.\",\n                  \"default\": 0,\n                  \"minimum\": 0\n                },\n                \"debug\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Adds additional log output to debug server side CORS issues.\",\n                  \"default\": false\n                }\n              }\n            },\n            \"base_url\": {\n              \"title\": \"Public Base URL\",\n              \"description\": \"The URL where the public endpoint is exposed at.\",\n              \"type\": \"string\",\n              \"format\": \"uri-reference\",\n              \"examples\": [\n                \"https://my-app.com/.ory/kratos/public\",\n                \"/.ory/kratos/public/\"\n              ]\n            },\n            \"host\": {\n              \"title\": \"Public Host\",\n              \"description\": \"The host (interface) kratos' public endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Public Port\",\n              \"description\": \"The port kratos' public endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4433],\n              \"default\": 4433\n            }\n          },\n          \"additionalProperties\": false\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"log\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"level\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"trace\",\n            \"debug\",\n            \"info\",\n            \"warning\",\n            \"error\",\n            \"fatal\",\n            \"panic\"\n          ]\n        },\n        \"leak_sensitive_values\": {\n          \"type\": \"boolean\",\n          \"title\": \"Leak Sensitive Log Values\",\n          \"description\": \"If set will leak sensitive values (e.g. emails) in the logs.\"\n        },\n        \"redaction_text\": {\n          \"type\": \"string\",\n          \"title\": \"Sensitive log value redaction text\",\n          \"description\": \"Text to use, when redacting sensitive log value.\"\n        },\n        \"format\": {\n          \"type\": \"string\",\n          \"enum\": [\"json\", \"text\"]\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"identity\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default_schema_url\": {\n          \"title\": \"JSON Schema URL for default identity traits\",\n          \"description\": \"Path to the JSON Schema which describes a default identity's traits.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/identity.traits.schema.json\",\n            \"https://foo.bar.com/path/to/identity.traits.schema.json\"\n          ]\n        },\n        \"schemas\": {\n          \"type\": \"array\",\n          \"title\": \"Additional JSON Schemas for Identity Traits\",\n          \"examples\": [\n            [\n              {\n                \"id\": \"customer\",\n                \"url\": \"https://foo.bar.com/path/to/customer.traits.schema.json\"\n              },\n              {\n                \"id\": \"employee\",\n                \"url\": \"https://foo.bar.com/path/to/employee.traits.schema.json\"\n              },\n              {\n                \"id\": \"employee-v2\",\n                \"url\": \"https://foo.bar.com/path/to/employee.v2.traits.schema.json\"\n              }\n            ]\n          ],\n          \"items\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"id\": {\n                \"title\": \"The schema's ID.\",\n                \"type\": \"string\",\n                \"examples\": [\"employee\"]\n              },\n              \"url\": {\n                \"type\": \"string\",\n                \"title\": \"Path to the JSON Schema\",\n                \"format\": \"uri\",\n                \"examples\": [\n                  \"file://path/to/identity.traits.schema.json\",\n                  \"https://foo.bar.com/path/to/identity.traits.schema.json\"\n                ]\n              }\n            },\n            \"required\": [\"id\", \"url\"],\n            \"not\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"id\": {\n                  \"const\": \"default\"\n                }\n              },\n              \"additionalProperties\": true\n            }\n          }\n        }\n      },\n      \"required\": [\"default_schema_url\"],\n      \"additionalProperties\": false\n    },\n    \"secrets\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default\": {\n          \"type\": \"array\",\n          \"title\": \"Default Encryption Signing Secrets\",\n          \"description\": \"The first secret in the array is used for singing and encrypting things while all other keys are used to verify and decrypt older things that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        },\n        \"cookie\": {\n          \"type\": \"array\",\n          \"title\": \"Singing Keys for Cookies\",\n          \"description\": \"The first secret in the array is used for encrypting cookies while all other keys are used to decrypt older cookies that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"hashers\": {\n      \"title\": \"Hashing Algorithm Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"argon2\": {\n          \"title\": \"Configuration for the Argon2id hasher.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"memory\": {\n              \"type\": \"integer\",\n              \"minimum\": 16384\n            },\n            \"iterations\": {\n              \"type\": \"integer\",\n              \"minimum\": 1\n            },\n            \"parallelism\": {\n              \"type\": \"integer\",\n              \"minimum\": 1\n            },\n            \"salt_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16\n            },\n            \"key_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16\n            }\n          },\n          \"additionalProperties\": false\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"session\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"lifespan\": {\n          \"title\": \"Session Lifespan\",\n          \"description\": \"Defines how long a session is active. Once that lifespan has been reached, the user needs to sign in again.\",\n          \"type\": \"string\",\n          \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n          \"default\": \"24h\",\n          \"examples\": [\"1h\", \"1m\", \"1s\"]\n        },\n        \"cookie\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"domain\": {\n              \"title\": \"Session Cookie Domain\",\n              \"description\": \"Sets the session cookie domain. Useful when dealing with subdomains. Use with care!\",\n              \"type\": \"string\"\n            },\n            \"persistent\": {\n              \"title\": \"Make Session Cookie Persistent\",\n              \"description\": \"If set to true will persist the cookie in the end-user's browser using the `max-age` parameter which is set to the `session.lifespan` value. Persistent cookies are not deleted when the browser is closed (e.g. on reboot or alt+f4).\",\n              \"type\": \"boolean\",\n              \"default\": true\n            },\n            \"path\": {\n              \"title\": \"Session Cookie Path\",\n              \"description\": \"Sets the session cookie path. Use with care!\",\n              \"type\": \"string\",\n              \"default\": \"/\"\n            },\n            \"same_site\": {\n              \"title\": \"Cookie Same Site Configuration\",\n              \"type\": \"string\",\n              \"enum\": [\"Strict\", \"Lax\", \"None\"],\n              \"default\": \"Lax\"\n            }\n          },\n          \"additionalProperties\": false\n        }\n      }\n    },\n    \"version\": {\n      \"title\": \"The kratos version this config is written for.\",\n      \"description\": \"SemVer according to https://semver.org/ prefixed with `v` as in our releases.\",\n      \"type\": \"string\",\n      \"pattern\": \"^v(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\",\n      \"examples\": [\"v0.5.0-alpha.1\"]\n    }\n  },\n  \"allOf\": [\n    {\n      \"if\": {\n        \"properties\": {\n          \"selfservice\": {\n            \"properties\": {\n              \"flows\": {\n                \"oneOf\": [\n                  {\n                    \"properties\": {\n                      \"verification\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"verification\"]\n                  },\n                  {\n                    \"properties\": {\n                      \"recovery\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"recovery\"]\n                  }\n                ]\n              }\n            },\n            \"required\": [\"flows\"]\n          }\n        },\n        \"required\": [\"selfservice\"]\n      },\n      \"then\": {\n        \"required\": [\"courier\"]\n      }\n    }\n  ],\n  \"required\": [\"identity\", \"dsn\", \"selfservice\"]\n}\n"
  },
  {
    "path": "oryx/configx/stub/from-files/expected.json",
    "content": "{\n  \"courier\": {\n    \"smtp\": {\n      \"connection_uri\": \"smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\",\n      \"from_address\": \"no-reply@ory.kratos.sh\"\n    }\n  },\n  \"dsn\": \"sqlite:///var/lib/sqlite/db.sqlite?_fk=true\",\n  \"hashers\": {\n    \"argon2\": {\n      \"iterations\": 2,\n      \"key_length\": 16,\n      \"memory\": 131072,\n      \"parallelism\": 1,\n      \"salt_length\": 16\n    }\n  },\n  \"identity\": {\n    \"default_schema_url\": \"file:///etc/config/kratos/identity.schema.json\"\n  },\n  \"log\": {\n    \"format\": \"text\",\n    \"leak_sensitive_values\": true,\n    \"level\": \"debug\"\n  },\n  \"secrets\": {\n    \"cookie\": [\"PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\"]\n  },\n  \"selfservice\": {\n    \"default_browser_return_url\": \"http://127.0.0.1:4455/\",\n    \"flows\": {\n      \"error\": {\n        \"ui_url\": \"http://127.0.0.1:4455/error\"\n      },\n      \"login\": {\n        \"lifespan\": \"10m\",\n        \"ui_url\": \"http://127.0.0.1:4455/auth/login\"\n      },\n      \"logout\": {\n        \"after\": {\n          \"default_browser_return_url\": \"http://127.0.0.1:4455/auth/login\"\n        }\n      },\n      \"recovery\": {\n        \"enabled\": true,\n        \"lifespan\": \"1h\",\n        \"ui_url\": \"http://127.0.0.1:4455/recovery\"\n      },\n      \"registration\": {\n        \"after\": {\n          \"password\": {\n            \"hooks\": [\n              {\n                \"hook\": \"session\"\n              }\n            ]\n          }\n        },\n        \"lifespan\": \"10m\",\n        \"ui_url\": \"http://127.0.0.1:4455/auth/registration\"\n      },\n      \"settings\": {\n        \"lifespan\": \"1h\",\n        \"privileged_session_max_age\": \"15m\",\n        \"ui_url\": \"http://127.0.0.1:4455/settings\"\n      },\n      \"verification\": {\n        \"after\": {\n          \"default_browser_return_url\": \"http://127.0.0.1:4455/\"\n        },\n        \"enabled\": true,\n        \"lifespan\": \"1h\",\n        \"ui_url\": \"http://127.0.0.1:4455/verify\"\n      }\n    },\n    \"methods\": {\n      \"link\": {\n        \"enabled\": true\n      },\n      \"oidc\": {\n        \"enabled\": false\n      },\n      \"password\": {\n        \"enabled\": true\n      },\n      \"profile\": {\n        \"enabled\": true\n      }\n    },\n    \"whitelisted_return_urls\": [\"http://127.0.0.1:4455\"]\n  },\n  \"serve\": {\n    \"admin\": {\n      \"base_url\": \"http://kratos:4434/\",\n      \"host\": \"0.0.0.0\",\n      \"port\": 4434\n    },\n    \"public\": {\n      \"base_url\": \"http://127.0.0.1:4433/\",\n      \"cors\": {\n        \"allow_credentials\": true,\n        \"allowed_headers\": [\"Authorization\", \"Content-Type\", \"X-Session-Token\"],\n        \"allowed_methods\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n        \"allowed_origins\": [\"*\"],\n        \"debug\": false,\n        \"enabled\": true,\n        \"exposed_headers\": [\"Content-Type\"],\n        \"max_age\": 0,\n        \"options_passthrough\": false\n      },\n      \"host\": \"0.0.0.0\",\n      \"port\": 4433\n    }\n  },\n  \"session\": {\n    \"cookie\": {\n      \"path\": \"/\",\n      \"persistent\": true,\n      \"same_site\": \"Lax\"\n    },\n    \"lifespan\": \"24h\"\n  },\n  \"version\": \"v0.5.3-alpha.1\"\n}\n"
  },
  {
    "path": "oryx/configx/stub/hydra/config.schema.json",
    "content": "{\n  \"$id\": \"https://github.com/ory/hydra/docs/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"ORY Hydra Configuration\",\n  \"type\": \"object\",\n  \"definitions\": {\n    \"http_method\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"POST\",\n        \"GET\",\n        \"PUT\",\n        \"PATCH\",\n        \"DELETE\",\n        \"CONNECT\",\n        \"HEAD\",\n        \"OPTIONS\",\n        \"TRACE\"\n      ]\n    },\n    \"port_number\": {\n      \"type\": \"integer\",\n      \"description\": \"The port to listen on.\",\n      \"minimum\": 1,\n      \"maximum\": 65535\n    },\n    \"socket\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Sets the permissions of the unix socket\",\n      \"properties\": {\n        \"owner\": {\n          \"type\": \"string\",\n          \"description\": \"Owner of unix socket. If empty, the owner will be the user running hydra.\",\n          \"default\": \"\"\n        },\n        \"group\": {\n          \"type\": \"string\",\n          \"description\": \"Group of unix socket. If empty, the group will be the primary group of the user running hydra.\",\n          \"default\": \"\"\n        },\n        \"mode\": {\n          \"type\": \"integer\",\n          \"description\": \"Mode of unix socket in numeric form\",\n          \"default\": 493,\n          \"minimum\": 0,\n          \"maximum\": 511\n        }\n      }\n    },\n    \"cors\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Configures Cross Origin Resource Sharing for public endpoints.\",\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"description\": \"Sets whether CORS is enabled.\",\n          \"default\": false\n        },\n        \"allowed_origins\": {\n          \"type\": \"array\",\n          \"description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 1,\n            \"not\": {\n              \"type\": \"string\",\n              \"description\": \"does match all strings that contain two or more (*)\",\n              \"pattern\": \".*\\\\*.*\\\\*.*\"\n            },\n            \"anyOf\": [\n              {\n                \"format\": \"uri\"\n              },\n              {\n                \"const\": \"*\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"default\": [\"*\"],\n          \"examples\": [\n            [\n              \"https://example.com\",\n              \"https://*.example.com\",\n              \"https://*.foo.example.com\"\n            ]\n          ]\n        },\n        \"allowed_methods\": {\n          \"type\": \"array\",\n          \"description\": \"A list of HTTP methods the user agent is allowed to use with cross-domain requests.\",\n          \"default\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n          \"items\": {\n            \"type\": \"string\",\n            \"enum\": [\n              \"POST\",\n              \"GET\",\n              \"PUT\",\n              \"PATCH\",\n              \"DELETE\",\n              \"CONNECT\",\n              \"HEAD\",\n              \"OPTIONS\",\n              \"TRACE\"\n            ]\n          }\n        },\n        \"allowed_headers\": {\n          \"type\": \"array\",\n          \"description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n          \"default\": [\"Authorization\", \"Content-Type\"],\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"exposed_headers\": {\n          \"type\": \"array\",\n          \"description\": \"Sets which headers are safe to expose to the API of a CORS API specification.\",\n          \"default\": [\"Content-Type\"],\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"allow_credentials\": {\n          \"type\": \"boolean\",\n          \"description\": \"Sets whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\",\n          \"default\": true\n        },\n        \"options_passthrough\": {\n          \"type\": \"boolean\",\n          \"description\": \"TODO\",\n          \"default\": false\n        },\n        \"max_age\": {\n          \"type\": \"integer\",\n          \"description\": \"Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request is preceded by a preflight request.\",\n          \"default\": 0,\n          \"minimum\": 0\n        },\n        \"debug\": {\n          \"type\": \"boolean\",\n          \"description\": \"Adds additional log output to debug server side CORS issues.\",\n          \"default\": false\n        }\n      }\n    },\n    \"pem_file\": {\n      \"type\": \"object\",\n      \"oneOf\": [\n        {\n          \"properties\": {\n            \"path\": {\n              \"type\": \"string\",\n              \"description\": \"The path to the pem file.\",\n              \"examples\": [\"/path/to/file.pem\"]\n            }\n          },\n          \"additionalProperties\": false,\n          \"required\": [\"path\"]\n        },\n        {\n          \"properties\": {\n            \"base64\": {\n              \"type\": \"string\",\n              \"description\": \"The base64 encoded string (without padding).\",\n              \"contentEncoding\": \"base64\",\n              \"contentMediaType\": \"application/x-pem-file\",\n              \"examples\": [\"b3J5IGh5ZHJhIGlzIGF3ZXNvbWUK\"]\n            }\n          },\n          \"additionalProperties\": false,\n          \"required\": [\"base64\"]\n        }\n      ]\n    },\n    \"duration\": {\n      \"type\": \"string\",\n      \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n      \"examples\": [\"1h\"]\n    }\n  },\n  \"properties\": {\n    \"log\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Configures the logger\",\n      \"properties\": {\n        \"level\": {\n          \"type\": \"string\",\n          \"description\": \"Sets the log level.\",\n          \"enum\": [\"panic\", \"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"],\n          \"default\": \"info\"\n        },\n        \"leak_sensitive_values\": {\n          \"type\": \"boolean\",\n          \"description\": \"Logs sensitive values such as cookie and URL parameter.\",\n          \"default\": false\n        },\n        \"redaction_text\": {\n          \"type\": \"string\",\n          \"title\": \"Sensitive log value redaction text\",\n          \"description\": \"Text to use, when redacting sensitive log value.\"\n        },\n        \"format\": {\n          \"type\": \"string\",\n          \"description\": \"Sets the log format.\",\n          \"enum\": [\"json\", \"json_pretty\", \"text\"],\n          \"default\": \"text\"\n        }\n      }\n    },\n    \"serve\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Controls the configuration for the http(s) daemon(s).\",\n      \"properties\": {\n        \"public\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Controls the public daemon serving public API endpoints like /oauth2/auth, /oauth2/token, /.well-known/jwks.json\",\n          \"properties\": {\n            \"port\": {\n              \"default\": 4444,\n              \"allOf\": [\n                {\n                  \"$ref\": \"#/definitions/port_number\"\n                }\n              ]\n            },\n            \"host\": {\n              \"type\": \"string\",\n              \"description\": \"The interface or unix socket ORY Hydra should listen and handle public API requests on. Use the prefix \\\"unix:\\\" to specify a path to a unix socket. Leave empty to listen on all interfaces.\",\n              \"default\": \"\",\n              \"examples\": [\"localhost\"]\n            },\n            \"cors\": {\n              \"$ref\": \"#/definitions/cors\"\n            },\n            \"socket\": {\n              \"$ref\": \"#/definitions/socket\"\n            },\n            \"access_log\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"description\": \"Access Log configuration for public server.\",\n              \"properties\": {\n                \"disable_for_health\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Disable access log for health endpoints.\",\n                  \"default\": false\n                }\n              }\n            }\n          }\n        },\n        \"admin\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"port\": {\n              \"default\": 4445,\n              \"allOf\": [\n                {\n                  \"$ref\": \"#/definitions/port_number\"\n                }\n              ]\n            },\n            \"host\": {\n              \"type\": \"string\",\n              \"description\": \"The interface or unix socket ORY Hydra should listen and handle administrative API requests on. Use the prefix \\\"unix:\\\" to specify a path to a unix socket. Leave empty to listen on all interfaces.\",\n              \"default\": \"\",\n              \"examples\": [\"localhost\"]\n            },\n            \"cors\": {\n              \"$ref\": \"#/definitions/cors\"\n            },\n            \"socket\": {\n              \"$ref\": \"#/definitions/socket\"\n            },\n            \"access_log\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"description\": \"Access Log configuration for admin server.\",\n              \"properties\": {\n                \"disable_for_health\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Disable access log for health endpoints.\",\n                  \"default\": false\n                }\n              }\n            }\n          }\n        },\n        \"tls\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures HTTPS (HTTP over TLS). If configured, the server automatically supports HTTP/2.\",\n          \"properties\": {\n            \"key\": {\n              \"description\": \"Configures the private key (pem encoded).\",\n              \"allOf\": [\n                {\n                  \"$ref\": \"#/definitions/pem_file\"\n                }\n              ]\n            },\n            \"cert\": {\n              \"description\": \"Configures the private key (pem encoded).\",\n              \"allOf\": [\n                {\n                  \"$ref\": \"#/definitions/pem_file\"\n                }\n              ]\n            },\n            \"allow_termination_from\": {\n              \"type\": \"array\",\n              \"description\": \"Whitelist one or multiple CIDR address ranges and allow them to terminate TLS connections. Be aware that the X-Forwarded-Proto header must be set and must never be modifiable by anyone but your proxy / gateway / load balancer. Supports ipv4 and ipv6. Hydra serves http instead of https when this option is set.\",\n              \"items\": {\n                \"type\": \"string\",\n                \"oneOf\": [\n                  {\n                    \"pattern\": \"^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$\"\n                  },\n                  {\n                    \"pattern\": \"^([0-9]{1,3}\\\\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$\"\n                  }\n                ],\n                \"examples\": [\"127.0.0.1/32\"]\n              }\n            }\n          }\n        },\n        \"cookies\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"same_site_mode\": {\n              \"type\": \"string\",\n              \"description\": \"Specify the SameSite mode that cookies should be sent with.\",\n              \"enum\": [\"Strict\", \"Lax\", \"None\"],\n              \"default\": \"None\"\n            },\n            \"same_site_legacy_workaround\": {\n              \"type\": \"boolean\",\n              \"description\": \"Some older browser versions don’t work with SameSite=None. This option enables the workaround defined in https://web.dev/samesite-cookie-recipes/ which essentially stores a second cookie without SameSite as a fallback.\",\n              \"default\": false,\n              \"examples\": [true]\n            }\n          }\n        }\n      }\n    },\n    \"dsn\": {\n      \"type\": \"string\",\n      \"description\": \"Sets the data source name. This configures the backend where ORY Hydra persists data. If dsn is \\\"memory\\\", data will be written to memory and is lost when you restart this instance. ORY Hydra supports popular SQL databases. For more detailed configuration information go to: https://www.ory.sh/docs/hydra/dependencies-environment#sql\"\n    },\n    \"webfinger\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Configures ./well-known/ settings.\",\n      \"properties\": {\n        \"jwks\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures the /.well-known/jwks.json endpoint.\",\n          \"properties\": {\n            \"broadcast_keys\": {\n              \"type\": \"array\",\n              \"description\": \"A list of JSON Web Keys that should be exposed at that endpoint. This is usually the public key for verifying OpenID Connect ID Tokens. However, you might want to add additional keys here as well.\",\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"default\": [\"hydra.openid.id-token\"],\n              \"examples\": [\"hydra.jwt.access-token\"]\n            }\n          }\n        },\n        \"oidc_discovery\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures OpenID Connect Discovery (/.well-known/openid-configuration).\",\n          \"properties\": {\n            \"jwks_url\": {\n              \"type\": \"string\",\n              \"description\": \"Overwrites the JWKS URL\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://my-service.com/.well-known/jwks.json\"]\n            },\n            \"token_url\": {\n              \"type\": \"string\",\n              \"description\": \"Overwrites the OAuth2 Token URL\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://my-service.com/oauth2/token\"]\n            },\n            \"auth_url\": {\n              \"type\": \"string\",\n              \"description\": \"Overwrites the OAuth2 Auth URL\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://my-service.com/oauth2/auth\"]\n            },\n            \"client_registration_url\": {\n              \"description\": \"Sets the OpenID Connect Dynamic Client Registration Endpoint\",\n              \"type\": \"string\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://my-service.com/clients\"]\n            },\n            \"supported_claims\": {\n              \"type\": \"array\",\n              \"description\": \"A list of supported claims to be broadcasted. Claim \\\"sub\\\" is always included.\",\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"examples\": [[\"email\", \"username\"]]\n            },\n            \"supported_scope\": {\n              \"type\": \"array\",\n              \"description\": \"The scope OAuth 2.0 Clients may request. Scope `offline`, `offline_access`, and `openid` are always included.\",\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"examples\": [[\"email\", \"whatever\", \"read.photos\"]]\n            },\n            \"userinfo_url\": {\n              \"type\": \"string\",\n              \"description\": \"A URL of the userinfo endpoint to be advertised at the OpenID Connect Discovery endpoint /.well-known/openid-configuration. Defaults to ORY Hydra's userinfo endpoint at /userinfo. Set this value if you want to handle this endpoint yourself.\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://example.org/my-custom-userinfo-endpoint\"]\n            }\n          }\n        }\n      }\n    },\n    \"oidc\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Configures OpenID Connect features.\",\n      \"properties\": {\n        \"subject_identifiers\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures the Subject Identifier algorithm. For more information please head over to the documentation: https://www.ory.sh/docs/hydra/advanced#subject-identifier-algorithms\",\n          \"properties\": {\n            \"enabled\": {\n              \"type\": \"array\",\n              \"description\": \"A list of algorithms to enable.\",\n              \"items\": {\n                \"type\": \"string\",\n                \"enum\": [\"public\", \"pairwise\"]\n              }\n            },\n            \"pairwise\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"description\": \"Configures the pairwise algorithm.\",\n              \"properties\": {\n                \"salt\": {\n                  \"type\": \"string\"\n                }\n              },\n              \"required\": [\"salt\"]\n            }\n          },\n          \"if\": {\n            \"properties\": {\n              \"enabled\": {\n                \"contains\": {\n                  \"const\": \"pairwise\"\n                }\n              }\n            }\n          },\n          \"then\": {\n            \"required\": [\"pairwise\"]\n          },\n          \"else\": {\n            \"properties\": {\n              \"pairwise\": {\n                \"$comment\": \"This enforces pairwise to not be set if 'enabled' does not contain 'pairwise'\",\n                \"not\": {}\n              }\n            }\n          },\n          \"examples\": [\n            {\n              \"enabled\": [\"public\", \"pairwise\"],\n              \"pairwise\": {\n                \"salt\": \"some-random-salt\"\n              }\n            }\n          ]\n        },\n        \"dynamic_client_registration\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures OpenID Connect Dynamic Client Registration (exposed as admin endpoints /clients/...).\",\n          \"properties\": {\n            \"default_scope\": {\n              \"type\": \"array\",\n              \"description\": \"The OpenID Connect Dynamic Client Registration specification has no concept of whitelisting OAuth 2.0 Scope. If you want to expose Dynamic Client Registration, you should set the default scope enabled for newly registered clients. Keep in mind that users can overwrite this default by setting the \\\"scope\\\" key in the registration payload, effectively disabling the concept of whitelisted scopes.\",\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"examples\": [[\"openid\", \"offline\", \"offline_access\"]]\n            }\n          }\n        }\n      }\n    },\n    \"urls\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"self\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"issuer\": {\n              \"type\": \"string\",\n              \"description\": \"This value will be used as the \\\"issuer\\\" in access and ID tokens. It must be specified and using HTTPS protocol, unless --dangerous-force-http is set. This should typically be equal to the public value.\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://localhost:4444/\"]\n            },\n            \"public\": {\n              \"type\": \"string\",\n              \"description\": \"This is the base location of the public endpoints of your ORY Hydra installation. This should typically be equal to the issuer value. If left unspecified, it falls back to the issuer value.\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://localhost:4444/\"]\n            }\n          }\n        },\n        \"login\": {\n          \"type\": \"string\",\n          \"description\": \"Sets the login endpoint of the User Login & Consent flow. Defaults to an internal fallback URL showing an error.\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://my-login.app/login\"]\n        },\n        \"consent\": {\n          \"type\": \"string\",\n          \"description\": \"Sets the consent endpoint of the User Login & Consent flow. Defaults to an internal fallback URL showing an error.\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://my-consent.app/consent\"]\n        },\n        \"logout\": {\n          \"type\": \"string\",\n          \"description\": \"Sets the logout endpoint. Defaults to an internal fallback URL showing an error.\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://my-logout.app/logout\"]\n        },\n        \"error\": {\n          \"type\": \"string\",\n          \"description\": \"Sets the error endpoint. The error ui will be shown when an OAuth2 error occurs that which can not be sent back to the client. Defaults to an internal fallback URL showing an error.\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://my-error.app/error\"]\n        },\n        \"post_logout_redirect\": {\n          \"type\": \"string\",\n          \"description\": \"When a user agent requests to logout, it will be redirected to this url afterwards per default.\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://my-example.app/logout-successful\"]\n        }\n      }\n    },\n    \"strategies\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"scope\": {\n          \"type\": \"string\",\n          \"description\": \"Defines how scopes are matched. For more details have a look at https://github.com/ory/fosite#scopes\",\n          \"enum\": [\n            \"exact\",\n            \"wildcard\",\n            \"DEPRECATED_HIERARCHICAL_SCOPE_STRATEGY\"\n          ],\n          \"default\": \"wildcard\"\n        },\n        \"access_token\": {\n          \"type\": \"string\",\n          \"description\": \"Defines access token type. jwt is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens\",\n          \"enum\": [\"opaque\", \"jwt\"]\n        }\n      }\n    },\n    \"ttl\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"Configures time to live.\",\n      \"properties\": {\n        \"login_consent_request\": {\n          \"description\": \"Configures how long a user login and consent flow may take.\",\n          \"default\": \"1h\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/duration\"\n            }\n          ]\n        },\n        \"access_token\": {\n          \"description\": \"Configures how long access tokens are valid.\",\n          \"default\": \"1h\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/duration\"\n            }\n          ]\n        },\n        \"refresh_token\": {\n          \"description\": \"Configures how long refresh tokens are valid. Set to -1 for refresh tokens to never expire.\",\n          \"default\": \"720h\",\n          \"oneOf\": [\n            {\n              \"$ref\": \"#/definitions/duration\"\n            },\n            {\n              \"enum\": [\"-1\", -1]\n            }\n          ]\n        },\n        \"id_token\": {\n          \"description\": \"Configures how long id tokens are valid.\",\n          \"default\": \"1h\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/duration\"\n            }\n          ]\n        },\n        \"auth_code\": {\n          \"description\": \"Configures how long auth codes are valid.\",\n          \"default\": \"10m\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/duration\"\n            }\n          ]\n        }\n      }\n    },\n    \"oauth2\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"expose_internal_errors\": {\n          \"type\": \"boolean\",\n          \"description\": \"Set this to true if you want to share error debugging information with your OAuth 2.0 clients. Keep in mind that debug information is very valuable when dealing with errors, but might also expose database error codes and similar errors.\",\n          \"default\": false,\n          \"examples\": [true]\n        },\n        \"session\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"encrypt_at_rest\": {\n              \"type\": \"boolean\",\n              \"default\": true,\n              \"title\": \"Encrypt OAuth2 Session\",\n              \"description\": \"If set to true (default) ORY Hydra encrypt OAuth2 and OpenID Connect session data using AES-GCM and the system secret before persisting it in the database.\"\n            }\n          }\n        },\n        \"include_legacy_error_fields\": {\n          \"type\": \"boolean\",\n          \"description\": \"Set this to true if you want to include the `error_hint` and `error_debug` legacy fields in error responses. We recommend to set this to `false` unless you have clients using these fields.\",\n          \"default\": false,\n          \"examples\": [true]\n        },\n        \"hashers\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures hashing algorithms. Supports only BCrypt at the moment.\",\n          \"properties\": {\n            \"bcrypt\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"description\": \"Configures the BCrypt hashing algorithm used for hashing Client Secrets.\",\n              \"properties\": {\n                \"cost\": {\n                  \"type\": \"integer\",\n                  \"description\": \"Sets the BCrypt cost. The higher the value, the more CPU time is being used to generate hashes.\",\n                  \"default\": 10,\n                  \"minimum\": 4,\n                  \"maximum\": 31\n                }\n              }\n            }\n          }\n        },\n        \"pkce\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enforced\": {\n              \"type\": \"boolean\",\n              \"description\": \"Sets whether PKCE should be enforced for all clients.\",\n              \"examples\": [true]\n            },\n            \"enforced_for_public_clients\": {\n              \"type\": \"boolean\",\n              \"description\": \"Sets whether PKCE should be enforced for public clients.\",\n              \"examples\": [true]\n            }\n          }\n        },\n        \"client_credentials\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"default_grant_allowed_scope\": {\n              \"type\": \"boolean\",\n              \"description\": \"Defines how scopes are added if the request doesn't contains any scope\",\n              \"examples\": [false]\n            }\n          }\n        }\n      }\n    },\n    \"secrets\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"The secrets section configures secrets used for encryption and signing of several systems. All secrets can be rotated, for more information on this topic go to: https://www.ory.sh/docs/hydra/advanced#rotation-of-hmac-token-signing-and-database-and-cookie-encryption-keys\",\n      \"properties\": {\n        \"system\": {\n          \"description\": \"The system secret must be at least 16 characters long. If none is provided, one will be generated. They key is used to encrypt sensitive data using AES-GCM (256 bit) and validate HMAC signatures. The first item in the list is used for signing and encryption. The whole list is used for verifying signatures and decryption.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"examples\": [\n            [\n              \"this-is-the-primary-secret\",\n              \"this-is-an-old-secret\",\n              \"this-is-another-old-secret\"\n            ]\n          ]\n        },\n        \"cookie\": {\n          \"type\": \"array\",\n          \"description\": \"A secret that is used to encrypt cookie sessions. Defaults to secrets.system. It is recommended to use a separate secret in production. The first item in the list is used for signing and encryption. The whole list is used for verifying signatures and decryption.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"examples\": [\n            [\n              \"this-is-the-primary-secret\",\n              \"this-is-an-old-secret\",\n              \"this-is-another-old-secret\"\n            ]\n          ]\n        }\n      }\n    },\n    \"profiling\": {\n      \"type\": \"string\",\n      \"description\": \"Enables profiling if set. For more details on profiling, head over to: https://blog.golang.org/profiling-go-programs\",\n      \"enum\": [\"cpu\", \"mem\"],\n      \"examples\": [\"cpu\"]\n    },\n    \"tracing\": {\n      \"$ref\": \"ory://tracing-config\"\n    },\n    \"version\": {\n      \"type\": \"string\",\n      \"title\": \"The Hydra version this config is written for.\",\n      \"description\": \"SemVer according to https://semver.org/ prefixed with `v` as in our releases.\",\n      \"pattern\": \"^v(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n    },\n    \"cgroups\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"description\": \"ORY Hydra can respect Linux container CPU quota\",\n      \"properties\": {\n        \"v1\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures parameters using cgroups v1 hierarchy\",\n          \"properties\": {\n            \"auto_max_procs_enabled\": {\n              \"type\": \"boolean\",\n              \"description\": \"Set GOMAXPROCS automatically according to cgroups limits\",\n              \"default\": false,\n              \"examples\": [true]\n            }\n          }\n        }\n      }\n    }\n  },\n  \"required\": [\"dsn\"]\n}\n"
  },
  {
    "path": "oryx/configx/stub/hydra/expected.json",
    "content": "{\n  \"cgroups\": {\n    \"v1\": {\n      \"auto_max_procs_enabled\": false\n    }\n  },\n  \"dsn\": \"sqlite:///var/lib/sqlite/db.sqlite?_fk=true\",\n  \"log\": {\n    \"format\": \"text\",\n    \"leak_sensitive_values\": false,\n    \"level\": \"info\"\n  },\n  \"oauth2\": {\n    \"expose_internal_errors\": false,\n    \"hashers\": {\n      \"bcrypt\": {\n        \"cost\": 10\n      }\n    },\n    \"include_legacy_error_fields\": false,\n    \"session\": {\n      \"encrypt_at_rest\": true\n    }\n  },\n  \"oidc\": {\n    \"subject_identifiers\": {\n      \"enabled\": [\"pairwise\", \"public\"],\n      \"pairwise\": {\n        \"salt\": \"youReallyNeedToChangeThis\"\n      }\n    }\n  },\n  \"secrets\": {\n    \"system\": [\"youReallyNeedToChangeThis\"]\n  },\n  \"serve\": {\n    \"admin\": {\n      \"access_log\": {\n        \"disable_for_health\": false\n      },\n      \"cors\": {\n        \"allow_credentials\": true,\n        \"allowed_headers\": [\"Authorization\", \"Content-Type\"],\n        \"allowed_methods\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n        \"allowed_origins\": [\"*\"],\n        \"debug\": false,\n        \"enabled\": false,\n        \"exposed_headers\": [\"Content-Type\"],\n        \"max_age\": 0,\n        \"options_passthrough\": false\n      },\n      \"host\": \"\",\n      \"port\": 4445,\n      \"socket\": {\n        \"group\": \"\",\n        \"mode\": 493,\n        \"owner\": \"\"\n      }\n    },\n    \"cookies\": {\n      \"same_site_legacy_workaround\": false,\n      \"same_site_mode\": \"Lax\"\n    },\n    \"public\": {\n      \"access_log\": {\n        \"disable_for_health\": false\n      },\n      \"cors\": {\n        \"allow_credentials\": true,\n        \"allowed_headers\": [\"Authorization\", \"Content-Type\"],\n        \"allowed_methods\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n        \"allowed_origins\": [\"*\"],\n        \"debug\": false,\n        \"enabled\": false,\n        \"exposed_headers\": [\"Content-Type\"],\n        \"max_age\": 0,\n        \"options_passthrough\": false\n      },\n      \"host\": \"\",\n      \"port\": 4444,\n      \"socket\": {\n        \"group\": \"\",\n        \"mode\": 493,\n        \"owner\": \"\"\n      }\n    }\n  },\n  \"strategies\": {\n    \"scope\": \"wildcard\"\n  },\n  \"tracing\": {\n    \"provider\": \"jaeger\",\n    \"providers\": {\n      \"jaeger\": {\n        \"local_agent_address\": \"jaeger:6831\",\n        \"sampling\": {\n          \"server_url\": \"http://jaeger:5778/sampling\"\n        }\n      }\n    }\n  },\n  \"ttl\": {\n    \"access_token\": \"1h\",\n    \"auth_code\": \"10m\",\n    \"id_token\": \"1h\",\n    \"login_consent_request\": \"1h\",\n    \"refresh_token\": \"720h\"\n  },\n  \"urls\": {\n    \"consent\": \"http://127.0.0.1:3000/consent\",\n    \"login\": \"http://127.0.0.1:3000/login\",\n    \"logout\": \"http://127.0.0.1:3000/logout\",\n    \"self\": {\n      \"issuer\": \"http://127.0.0.1:4444\"\n    }\n  },\n  \"webfinger\": {\n    \"jwks\": {\n      \"broadcast_keys\": [\"hydra.openid.id-token\"]\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/configx/stub/hydra/hydra.yaml",
    "content": "serve:\n  cookies:\n    same_site_mode: Lax\n\nurls:\n  self:\n    issuer: http://127.0.0.1:4444\n  consent: http://127.0.0.1:3000/consent\n  login: http://127.0.0.1:3000/login\n  logout: http://127.0.0.1:3000/logout\n\nsecrets:\n  system:\n    - youReallyNeedToChangeThis\n\noidc:\n  subject_identifiers:\n    enabled:\n      - pairwise\n      - public\n    pairwise:\n      salt: youReallyNeedToChangeThis\n"
  },
  {
    "path": "oryx/configx/stub/kratos/config.schema.json",
    "content": "{\n  \"$id\": \"https://github.com/ory/kratos/.schema/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"ORY Kratos Configuration\",\n  \"type\": \"object\",\n  \"definitions\": {\n    \"defaultReturnTo\": {\n      \"title\": \"Redirect browsers to set URL per default\",\n      \"description\": \"ORY Kratos redirects to this URL per default on completion of self-service flows and other browser interaction. Read this [article for more information on browser redirects](https://www.ory.sh/kratos/docs/concepts/browser-redirect-flow-completion).\",\n      \"type\": \"string\",\n      \"format\": \"uri-reference\",\n      \"minLength\": 1,\n      \"examples\": [\"https://my-app.com/dashboard\", \"/dashboard\"]\n    },\n    \"selfServiceSessionRevokerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"revoke_active_sessions\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceVerifyHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"verify\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceSessionIssuerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"session\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"OIDCClaims\": {\n      \"title\": \"OpenID Connect claims\",\n      \"description\": \"The OpenID Connect claims and optionally their properties which should be included in the id_token or returned from the UserInfo Endpoint.\",\n      \"type\": \"object\",\n      \"examples\": [\n        {\n          \"id_token\": {\n            \"email\": null,\n            \"email_verified\": null\n          }\n        },\n        {\n          \"userinfo\": {\n            \"given_name\": {\n              \"essential\": true\n            },\n            \"nickname\": null,\n            \"email\": {\n              \"essential\": true\n            },\n            \"email_verified\": {\n              \"essential\": true\n            },\n            \"picture\": null,\n            \"http://example.info/claims/groups\": null\n          },\n          \"id_token\": {\n            \"auth_time\": {\n              \"essential\": true\n            },\n            \"acr\": {\n              \"values\": [\"urn:mace:incommon:iap:silver\"]\n            }\n          }\n        }\n      ],\n      \"patternProperties\": {\n        \"^userinfo$|^id_token$\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"patternProperties\": {\n            \".*\": {\n              \"oneOf\": [\n                {\n                  \"const\": null,\n                  \"description\": \"Indicates that this Claim is being requested in the default manner.\"\n                },\n                {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"essential\": {\n                      \"description\": \"Indicates whether the Claim being requested is an Essential Claim.\",\n                      \"type\": \"boolean\"\n                    },\n                    \"value\": {\n                      \"description\": \"Requests that the Claim be returned with a particular value.\",\n                      \"$comment\": \"There seem to be no constrains on value\"\n                    },\n                    \"values\": {\n                      \"description\": \"Requests that the Claim be returned with one of a set of values, with the values appearing in order of preference.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$comment\": \"There seem to be no constrains on individual items\"\n                      }\n                    }\n                  }\n                }\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"selfServiceOIDCProvider\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"examples\": [\"google\"]\n        },\n        \"provider\": {\n          \"title\": \"Provider\",\n          \"description\": \"Can be one of github, gitlab, generic, google, microsoft, discord.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"github\",\n            \"gitlab\",\n            \"generic\",\n            \"google\",\n            \"microsoft\",\n            \"discord\"\n          ],\n          \"examples\": [\"google\"]\n        },\n        \"client_id\": {\n          \"type\": \"string\"\n        },\n        \"client_secret\": {\n          \"type\": \"string\"\n        },\n        \"issuer_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com\"]\n        },\n        \"auth_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com/o/oauth2/v2/auth\"]\n        },\n        \"token_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://www.googleapis.com/oauth2/v4/token\"]\n        },\n        \"mapper_url\": {\n          \"title\": \"Jsonnet Mapper URL\",\n          \"description\": \"The URL where the jsonnet source is located for mapping the provider's data to ORY Kratos data.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/oidc.jsonnet\",\n            \"https://foo.bar.com/path/to/oidc.jsonnet\",\n            \"base64://bG9jYWwgc3ViamVjdCA9I...\"\n          ]\n        },\n        \"scope\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"examples\": [\"offline_access\", \"profile\"]\n          }\n        },\n        \"tenant\": {\n          \"title\": \"Azure AD Tenant\",\n          \"description\": \"The Azure AD Tenant to use for authentication.\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"common\",\n            \"organizations\",\n            \"consumers\",\n            \"8eaef023-2b34-4da1-9baa-8bc8c9d6a490\",\n            \"contoso.onmicrosoft.com\"\n          ]\n        },\n        \"requested_claims\": {\n          \"$ref\": \"#/definitions/OIDCClaims\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\n        \"id\",\n        \"provider\",\n        \"client_id\",\n        \"client_secret\",\n        \"mapper_url\"\n      ],\n      \"if\": {\n        \"properties\": {\n          \"provider\": {\n            \"const\": \"microsoft\"\n          }\n        },\n        \"required\": [\"provider\"]\n      },\n      \"then\": {\n        \"required\": [\"tenant\"]\n      },\n      \"else\": {\n        \"not\": {\n          \"properties\": {\n            \"tenant\": {}\n          },\n          \"required\": [\"tenant\"]\n        }\n      }\n    },\n    \"selfServiceAfterSettingsMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceVerifyHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterLoginMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterRegistrationMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionIssuerHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterSettings\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsMethod\"\n        },\n        \"profile\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsMethod\"\n        }\n      }\n    },\n    \"selfServiceAfterLogin\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterLoginMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterLoginMethod\"\n        }\n      }\n    },\n    \"selfServiceAfterRegistration\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        }\n      }\n    }\n  },\n  \"properties\": {\n    \"selfservice\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default_browser_return_url\"],\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"whitelisted_return_urls\": {\n          \"title\": \"Whitelisted Return To URLs\",\n          \"description\": \"List of URLs that are allowed to be redirected to. A redirection request is made by appending `?return_to=...` to Login, Registration, and other self-service flows.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"format\": \"uri-reference\"\n          },\n          \"examples\": [\n            [\n              \"https://app.my-app.com/dashboard\",\n              \"/dashboard\",\n              \"https://www.my-app.com/\"\n            ]\n          ],\n          \"uniqueItems\": true\n        },\n        \"flows\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"settings\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"URL of the Settings page.\",\n                  \"description\": \"URL where the Settings UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/user/settings\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/settings\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"privileged_session_max_age\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterSettings\"\n                }\n              }\n            },\n            \"logout\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"after\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  }\n                }\n              }\n            },\n            \"registration\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Registration UI URL\",\n                  \"description\": \"URL where the Registration UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/signup\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/registration\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterRegistration\"\n                }\n              }\n            },\n            \"login\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Login UI URL\",\n                  \"description\": \"URL where the Login UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/login\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/login\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterLogin\"\n                }\n              }\n            },\n            \"verification\": {\n              \"title\": \"Email and Phone Verification and Account Activation Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Email/Phone Verification\",\n                  \"description\": \"If set to true will enable [Email and Phone Verification and Account Activation](https://www.ory.sh/kratos/docs/self-service/flows/verify-email-account-activation/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Verify UI URL\",\n                  \"description\": \"URL where the ORY Verify UI is hosted. This is the page where users activate and / or verify their email or telephone number. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/verification\"\n                },\n                \"after\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  },\n                  \"additionalProperties\": false\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Verification Request Lifespan\",\n                  \"description\": \"Sets how long the verification request (for the UI interaction) is valid.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                }\n              }\n            },\n            \"recovery\": {\n              \"title\": \"Account Recovery Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Account Recovery\",\n                  \"description\": \"If set to true will enable [Account Recovery](https://www.ory.sh/kratos/docs/self-service/flows/password-reset-account-recovery/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Recovery UI URL\",\n                  \"description\": \"URL where the ORY Recovery UI is hosted. This is the page where users request and complete account recovery. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/recovery\"\n                },\n                \"after\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  },\n                  \"additionalProperties\": false\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Recovery Request Lifespan\",\n                  \"description\": \"Sets how long the recovery request is valid. If expired, the user has to redo the flow.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                }\n              }\n            },\n            \"error\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"ORY Kratos Error UI URL\",\n                  \"description\": \"URL where the ORY Kratos Error UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/kratos-error\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/error\"\n                }\n              }\n            }\n          }\n        },\n        \"methods\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"profile\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Profile Management Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"link\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Link Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"password\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Username/Email and Password Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"oidc\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables OpenID Connect Method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"providers\": {\n                      \"title\": \"OpenID Connect and OAuth2 Providers\",\n                      \"description\": \"A list and configuration of OAuth2 and OpenID Connect providers ORY Kratos should integrate with.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$ref\": \"#/definitions/selfServiceOIDCProvider\"\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    },\n    \"dsn\": {\n      \"type\": \"string\",\n      \"title\": \"Data Source Name\",\n      \"description\": \"DSN is used to specify the database credentials as a connection URI.\",\n      \"examples\": [\n        \"postgres://user: password@postgresd:5432/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"mysql://user:secret@tcp(mysqld:3306)/database?max_conns=20&max_idle_conns=4\",\n        \"cockroach://user@cockroachdb:26257/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc\"\n      ]\n    },\n    \"courier\": {\n      \"type\": \"object\",\n      \"title\": \"Courier configuration\",\n      \"description\": \"The courier is responsible for sending and delivering messages over email, sms, and other means.\",\n      \"properties\": {\n        \"template_override_path\": {\n          \"type\": \"string\",\n          \"title\": \"Override message templates\",\n          \"description\": \"You can override certain or all message templates by pointing this key to the path where the templates are located.\",\n          \"examples\": [\"/conf/courier-templates\"]\n        },\n        \"smtp\": {\n          \"title\": \"SMTP Configuration\",\n          \"description\": \"Configures outgoing emails using the SMTP protocol.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"connection_uri\": {\n              \"title\": \"SMTP connection string\",\n              \"description\": \"This URI will be used to connect to the SMTP server. Use the query parameter to allow (`?skip_ssl_verify=true`) or disallow (`?skip_ssl_verify=false`) self-signed TLS certificates. Please keep in mind that any host other than localhost / 127.0.0.1 must use smtp over TLS (smtps) or the connection will not be possible.\",\n              \"examples\": [\n                \"smtps://foo:bar@my-mailserver:1234/?skip_ssl_verify=false\"\n              ],\n              \"type\": \"string\",\n              \"format\": \"uri\"\n            },\n            \"from_address\": {\n              \"title\": \"SMTP Sender Address\",\n              \"description\": \"The recipient of an email will see this as the sender address.\",\n              \"type\": \"string\",\n              \"format\": \"email\",\n              \"default\": \"no-reply@ory.kratos.sh\"\n            }\n          },\n          \"required\": [\"connection_uri\"],\n          \"additionalProperties\": false\n        }\n      },\n      \"required\": [\"smtp\"],\n      \"additionalProperties\": false\n    },\n    \"serve\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"admin\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"base_url\": {\n              \"title\": \"Admin Base URL\",\n              \"description\": \"The URL where the admin endpoint is exposed at.\",\n              \"type\": \"string\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://kratos.private-network:4434/\"]\n            },\n            \"host\": {\n              \"title\": \"Admin Host\",\n              \"description\": \"The host (interface) kratos' admin endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Admin Port\",\n              \"description\": \"The port kratos' admin endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4434],\n              \"default\": 4434\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"public\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"cors\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"description\": \"Configures Cross Origin Resource Sharing for public endpoints.\",\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether CORS is enabled.\",\n                  \"default\": false\n                },\n                \"allowed_origins\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin.\",\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"minLength\": 1,\n                    \"not\": {\n                      \"type\": \"string\",\n                      \"description\": \"does match all strings that contain two or more (*)\",\n                      \"pattern\": \".*\\\\*.*\\\\*.*\"\n                    },\n                    \"anyOf\": [\n                      {\n                        \"format\": \"uri\"\n                      },\n                      {\n                        \"const\": \"*\"\n                      }\n                    ]\n                  },\n                  \"uniqueItems\": true,\n                  \"default\": [\"*\"],\n                  \"examples\": [\n                    [\n                      \"https://example.com\",\n                      \"https://*.example.com\",\n                      \"https://*.foo.example.com\"\n                    ]\n                  ]\n                },\n                \"allowed_methods\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of HTTP methods the user agent is allowed to use with cross-domain requests.\",\n                  \"default\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"enum\": [\n                      \"POST\",\n                      \"GET\",\n                      \"PUT\",\n                      \"PATCH\",\n                      \"DELETE\",\n                      \"CONNECT\",\n                      \"HEAD\",\n                      \"OPTIONS\",\n                      \"TRACE\"\n                    ]\n                  }\n                },\n                \"allowed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n                  \"default\": [\n                    \"Authorization\",\n                    \"Content-Type\",\n                    \"X-Session-Token\"\n                  ],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"exposed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"Sets which headers are safe to expose to the API of a CORS API specification.\",\n                  \"default\": [\"Content-Type\"],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"allow_credentials\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\",\n                  \"default\": true\n                },\n                \"options_passthrough\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"TODO\",\n                  \"default\": false\n                },\n                \"max_age\": {\n                  \"type\": \"integer\",\n                  \"description\": \"Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request is preceded by a preflight request.\",\n                  \"default\": 0,\n                  \"minimum\": 0\n                },\n                \"debug\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Adds additional log output to debug server side CORS issues.\",\n                  \"default\": false\n                }\n              }\n            },\n            \"base_url\": {\n              \"title\": \"Public Base URL\",\n              \"description\": \"The URL where the public endpoint is exposed at.\",\n              \"type\": \"string\",\n              \"format\": \"uri-reference\",\n              \"examples\": [\n                \"https://my-app.com/.ory/kratos/public\",\n                \"/.ory/kratos/public/\"\n              ]\n            },\n            \"host\": {\n              \"title\": \"Public Host\",\n              \"description\": \"The host (interface) kratos' public endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Public Port\",\n              \"description\": \"The port kratos' public endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4433],\n              \"default\": 4433\n            }\n          },\n          \"additionalProperties\": false\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"log\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"level\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"trace\",\n            \"debug\",\n            \"info\",\n            \"warning\",\n            \"error\",\n            \"fatal\",\n            \"panic\"\n          ]\n        },\n        \"leak_sensitive_values\": {\n          \"type\": \"boolean\",\n          \"title\": \"Leak Sensitive Log Values\",\n          \"description\": \"If set will leak sensitive values (e.g. emails) in the logs.\"\n        },\n        \"redaction_text\": {\n          \"type\": \"string\",\n          \"title\": \"Sensitive log value redaction text\",\n          \"description\": \"Text to use, when redacting sensitive log value.\"\n        },\n        \"format\": {\n          \"type\": \"string\",\n          \"enum\": [\"json\", \"text\"]\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"identity\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default_schema_url\": {\n          \"title\": \"JSON Schema URL for default identity traits\",\n          \"description\": \"Path to the JSON Schema which describes a default identity's traits.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/identity.traits.schema.json\",\n            \"https://foo.bar.com/path/to/identity.traits.schema.json\"\n          ]\n        },\n        \"schemas\": {\n          \"type\": \"array\",\n          \"title\": \"Additional JSON Schemas for Identity Traits\",\n          \"examples\": [\n            [\n              {\n                \"id\": \"customer\",\n                \"url\": \"https://foo.bar.com/path/to/customer.traits.schema.json\"\n              },\n              {\n                \"id\": \"employee\",\n                \"url\": \"https://foo.bar.com/path/to/employee.traits.schema.json\"\n              },\n              {\n                \"id\": \"employee-v2\",\n                \"url\": \"https://foo.bar.com/path/to/employee.v2.traits.schema.json\"\n              }\n            ]\n          ],\n          \"items\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"id\": {\n                \"title\": \"The schema's ID.\",\n                \"type\": \"string\",\n                \"examples\": [\"employee\"]\n              },\n              \"url\": {\n                \"type\": \"string\",\n                \"title\": \"Path to the JSON Schema\",\n                \"format\": \"uri\",\n                \"examples\": [\n                  \"file://path/to/identity.traits.schema.json\",\n                  \"https://foo.bar.com/path/to/identity.traits.schema.json\"\n                ]\n              }\n            },\n            \"required\": [\"id\", \"url\"],\n            \"not\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"id\": {\n                  \"const\": \"default\"\n                }\n              },\n              \"additionalProperties\": true\n            }\n          }\n        }\n      },\n      \"required\": [\"default_schema_url\"],\n      \"additionalProperties\": false\n    },\n    \"secrets\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default\": {\n          \"type\": \"array\",\n          \"title\": \"Default Encryption Signing Secrets\",\n          \"description\": \"The first secret in the array is used for singing and encrypting things while all other keys are used to verify and decrypt older things that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        },\n        \"cookie\": {\n          \"type\": \"array\",\n          \"title\": \"Singing Keys for Cookies\",\n          \"description\": \"The first secret in the array is used for encrypting cookies while all other keys are used to decrypt older cookies that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"hashers\": {\n      \"title\": \"Hashing Algorithm Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"argon2\": {\n          \"title\": \"Configuration for the Argon2id hasher.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"memory\": {\n              \"type\": \"integer\",\n              \"minimum\": 16384\n            },\n            \"iterations\": {\n              \"type\": \"integer\",\n              \"minimum\": 1\n            },\n            \"parallelism\": {\n              \"type\": \"integer\",\n              \"minimum\": 1\n            },\n            \"salt_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16\n            },\n            \"key_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16\n            }\n          },\n          \"additionalProperties\": false\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"session\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"lifespan\": {\n          \"title\": \"Session Lifespan\",\n          \"description\": \"Defines how long a session is active. Once that lifespan has been reached, the user needs to sign in again.\",\n          \"type\": \"string\",\n          \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n          \"default\": \"24h\",\n          \"examples\": [\"1h\", \"1m\", \"1s\"]\n        },\n        \"cookie\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"domain\": {\n              \"title\": \"Session Cookie Domain\",\n              \"description\": \"Sets the session cookie domain. Useful when dealing with subdomains. Use with care!\",\n              \"type\": \"string\"\n            },\n            \"persistent\": {\n              \"title\": \"Make Session Cookie Persistent\",\n              \"description\": \"If set to true will persist the cookie in the end-user's browser using the `max-age` parameter which is set to the `session.lifespan` value. Persistent cookies are not deleted when the browser is closed (e.g. on reboot or alt+f4).\",\n              \"type\": \"boolean\",\n              \"default\": true\n            },\n            \"path\": {\n              \"title\": \"Session Cookie Path\",\n              \"description\": \"Sets the session cookie path. Use with care!\",\n              \"type\": \"string\",\n              \"default\": \"/\"\n            },\n            \"same_site\": {\n              \"title\": \"Cookie Same Site Configuration\",\n              \"type\": \"string\",\n              \"enum\": [\"Strict\", \"Lax\", \"None\"],\n              \"default\": \"Lax\"\n            }\n          },\n          \"additionalProperties\": false\n        }\n      }\n    },\n    \"version\": {\n      \"title\": \"The kratos version this config is written for.\",\n      \"description\": \"SemVer according to https://semver.org/ prefixed with `v` as in our releases.\",\n      \"type\": \"string\",\n      \"pattern\": \"^v(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\",\n      \"examples\": [\"v0.5.0-alpha.1\"]\n    }\n  },\n  \"allOf\": [\n    {\n      \"if\": {\n        \"properties\": {\n          \"selfservice\": {\n            \"properties\": {\n              \"flows\": {\n                \"oneOf\": [\n                  {\n                    \"properties\": {\n                      \"verification\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"verification\"]\n                  },\n                  {\n                    \"properties\": {\n                      \"recovery\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"recovery\"]\n                  }\n                ]\n              }\n            },\n            \"required\": [\"flows\"]\n          }\n        },\n        \"required\": [\"selfservice\"]\n      },\n      \"then\": {\n        \"required\": [\"courier\"]\n      }\n    }\n  ],\n  \"required\": [\"identity\", \"dsn\", \"selfservice\"]\n}\n"
  },
  {
    "path": "oryx/configx/stub/kratos/expected.json",
    "content": "{\n  \"courier\": {\n    \"smtp\": {\n      \"connection_uri\": \"smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\",\n      \"from_address\": \"no-reply@ory.kratos.sh\"\n    }\n  },\n  \"dsn\": \"sqlite:///var/lib/sqlite/db.sqlite?_fk=true\",\n  \"hashers\": {\n    \"argon2\": {\n      \"iterations\": 2,\n      \"key_length\": 16,\n      \"memory\": 131072,\n      \"parallelism\": 1,\n      \"salt_length\": 16\n    }\n  },\n  \"identity\": {\n    \"default_schema_url\": \"file:///etc/config/kratos/identity.schema.json\"\n  },\n  \"log\": {\n    \"format\": \"text\",\n    \"leak_sensitive_values\": true,\n    \"level\": \"debug\"\n  },\n  \"secrets\": {\n    \"cookie\": [\"PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\"]\n  },\n  \"selfservice\": {\n    \"default_browser_return_url\": \"http://127.0.0.1:4455/\",\n    \"flows\": {\n      \"error\": {\n        \"ui_url\": \"http://127.0.0.1:4455/error\"\n      },\n      \"login\": {\n        \"lifespan\": \"10m\",\n        \"ui_url\": \"http://127.0.0.1:4455/auth/login\"\n      },\n      \"logout\": {\n        \"after\": {\n          \"default_browser_return_url\": \"http://127.0.0.1:4455/auth/login\"\n        }\n      },\n      \"recovery\": {\n        \"enabled\": true,\n        \"lifespan\": \"1h\",\n        \"ui_url\": \"http://127.0.0.1:4455/recovery\"\n      },\n      \"registration\": {\n        \"after\": {\n          \"password\": {\n            \"hooks\": [\n              {\n                \"hook\": \"session\"\n              }\n            ]\n          }\n        },\n        \"lifespan\": \"10m\",\n        \"ui_url\": \"http://127.0.0.1:4455/auth/registration\"\n      },\n      \"settings\": {\n        \"lifespan\": \"1h\",\n        \"privileged_session_max_age\": \"15m\",\n        \"ui_url\": \"http://127.0.0.1:4455/settings\"\n      },\n      \"verification\": {\n        \"after\": {\n          \"default_browser_return_url\": \"http://127.0.0.1:4455/\"\n        },\n        \"enabled\": true,\n        \"lifespan\": \"1h\",\n        \"ui_url\": \"http://127.0.0.1:4455/verify\"\n      }\n    },\n    \"methods\": {\n      \"link\": {\n        \"enabled\": true\n      },\n      \"oidc\": {\n        \"enabled\": true,\n        \"config\": {\n          \"providers\": [\n            {\n              \"id\": \"google\",\n              \"provider\": \"google\",\n              \"mapper_url\": \"file:///etc/config/kratos/oidc.google.jsonnet\",\n              \"client_id\": \"client@example.com\",\n              \"client_secret\": \"secret\"\n            }\n          ]\n        }\n      },\n      \"password\": {\n        \"enabled\": true\n      },\n      \"profile\": {\n        \"enabled\": true\n      }\n    },\n    \"whitelisted_return_urls\": [\"http://127.0.0.1:4455\"]\n  },\n  \"serve\": {\n    \"admin\": {\n      \"base_url\": \"http://kratos:4434/\",\n      \"host\": \"0.0.0.0\",\n      \"port\": 4434\n    },\n    \"public\": {\n      \"base_url\": \"http://127.0.0.1:4433/\",\n      \"cors\": {\n        \"allow_credentials\": true,\n        \"allowed_headers\": [\"Authorization\", \"Content-Type\", \"X-Session-Token\"],\n        \"allowed_methods\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n        \"allowed_origins\": [\"*\"],\n        \"debug\": false,\n        \"enabled\": true,\n        \"exposed_headers\": [\"Content-Type\"],\n        \"max_age\": 0,\n        \"options_passthrough\": false\n      },\n      \"host\": \"0.0.0.0\",\n      \"port\": 4433\n    }\n  },\n  \"session\": {\n    \"cookie\": {\n      \"path\": \"/\",\n      \"persistent\": true,\n      \"same_site\": \"Lax\"\n    },\n    \"lifespan\": \"24h\"\n  },\n  \"version\": \"v0.5.3-alpha.1\"\n}\n"
  },
  {
    "path": "oryx/configx/stub/kratos/kratos.yaml",
    "content": "version: v0.5.3-alpha.1\n\ndsn: memory\n\nserve:\n  public:\n    base_url: http://127.0.0.1:4433/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nselfservice:\n  default_browser_return_url: http://127.0.0.1:4455/\n  whitelisted_return_urls:\n    - http://127.0.0.1:4455\n\n  methods:\n    password:\n      enabled: true\n    oidc:\n      enabled: true\n\n  flows:\n    error:\n      ui_url: http://127.0.0.1:4455/error\n\n    settings:\n      ui_url: http://127.0.0.1:4455/settings\n      privileged_session_max_age: 15m\n\n    recovery:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/recovery\n\n    verification:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/verify\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/\n\n    logout:\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/auth/login\n\n    login:\n      ui_url: http://127.0.0.1:4455/auth/login\n      lifespan: 10m\n\n    registration:\n      lifespan: 10m\n      ui_url: http://127.0.0.1:4455/auth/registration\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n\nhashers:\n  argon2:\n    parallelism: 1\n    memory: 131072\n    iterations: 2\n    salt_length: 16\n    key_length: 16\n\nidentity:\n  default_schema_url: file:///etc/config/kratos/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\n"
  },
  {
    "path": "oryx/configx/stub/multi/a.yaml",
    "content": "version: v0.5.3-alpha.1\n\ndsn: memory\n\nserve:\n  public:\n    base_url: http://127.0.0.1:4433/\n    cors:\n      enabled: true\n  admin:\n    base_url: http://kratos:4434/\n\nselfservice:\n  default_browser_return_url: http://127.0.0.1:4455/\n  whitelisted_return_urls:\n    - http://127.0.0.1:4455\n\n  methods:\n    password:\n      enabled: true\n\n  flows:\n    error:\n      ui_url: http://127.0.0.1:4455/error\n\n    settings:\n      ui_url: http://127.0.0.1:4455/settings\n"
  },
  {
    "path": "oryx/configx/stub/multi/b.yaml",
    "content": "selfservice:\n  flows:\n    settings:\n      privileged_session_max_age: 15m\n\n    recovery:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/recovery\n\n    verification:\n      enabled: true\n      ui_url: http://127.0.0.1:4455/verify\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/\n\n    logout:\n      after:\n        default_browser_return_url: http://127.0.0.1:4455/auth/login\n\n    login:\n      ui_url: http://127.0.0.1:4455/auth/login\n      lifespan: 10m\n\n    registration:\n      lifespan: 10m\n      ui_url: http://127.0.0.1:4455/auth/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n\nlog:\n  level: debug\n  format: text\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n\nhashers:\n  argon2:\n    parallelism: 1\n    memory: 131072\n    iterations: 2\n    salt_length: 16\n    key_length: 16\n\nidentity:\n  default_schema_url: file:///etc/config/kratos/identity.schema.json\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\n"
  },
  {
    "path": "oryx/configx/stub/multi/config.schema.json",
    "content": "{\n  \"$id\": \"https://github.com/ory/kratos/.schema/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"ORY Kratos Configuration\",\n  \"type\": \"object\",\n  \"definitions\": {\n    \"defaultReturnTo\": {\n      \"title\": \"Redirect browsers to set URL per default\",\n      \"description\": \"ORY Kratos redirects to this URL per default on completion of self-service flows and other browser interaction. Read this [article for more information on browser redirects](https://www.ory.sh/kratos/docs/concepts/browser-redirect-flow-completion).\",\n      \"type\": \"string\",\n      \"format\": \"uri-reference\",\n      \"minLength\": 1,\n      \"examples\": [\"https://my-app.com/dashboard\", \"/dashboard\"]\n    },\n    \"selfServiceSessionRevokerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"revoke_active_sessions\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceVerifyHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"verify\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"selfServiceSessionIssuerHook\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"hook\": {\n          \"const\": \"session\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\"hook\"]\n    },\n    \"OIDCClaims\": {\n      \"title\": \"OpenID Connect claims\",\n      \"description\": \"The OpenID Connect claims and optionally their properties which should be included in the id_token or returned from the UserInfo Endpoint.\",\n      \"type\": \"object\",\n      \"examples\": [\n        {\n          \"id_token\": {\n            \"email\": null,\n            \"email_verified\": null\n          }\n        },\n        {\n          \"userinfo\": {\n            \"given_name\": {\n              \"essential\": true\n            },\n            \"nickname\": null,\n            \"email\": {\n              \"essential\": true\n            },\n            \"email_verified\": {\n              \"essential\": true\n            },\n            \"picture\": null,\n            \"http://example.info/claims/groups\": null\n          },\n          \"id_token\": {\n            \"auth_time\": {\n              \"essential\": true\n            },\n            \"acr\": {\n              \"values\": [\"urn:mace:incommon:iap:silver\"]\n            }\n          }\n        }\n      ],\n      \"patternProperties\": {\n        \"^userinfo$|^id_token$\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"patternProperties\": {\n            \".*\": {\n              \"oneOf\": [\n                {\n                  \"const\": null,\n                  \"description\": \"Indicates that this Claim is being requested in the default manner.\"\n                },\n                {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"essential\": {\n                      \"description\": \"Indicates whether the Claim being requested is an Essential Claim.\",\n                      \"type\": \"boolean\"\n                    },\n                    \"value\": {\n                      \"description\": \"Requests that the Claim be returned with a particular value.\",\n                      \"$comment\": \"There seem to be no constrains on value\"\n                    },\n                    \"values\": {\n                      \"description\": \"Requests that the Claim be returned with one of a set of values, with the values appearing in order of preference.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$comment\": \"There seem to be no constrains on individual items\"\n                      }\n                    }\n                  }\n                }\n              ]\n            }\n          }\n        }\n      }\n    },\n    \"selfServiceOIDCProvider\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"id\": {\n          \"type\": \"string\",\n          \"examples\": [\"google\"]\n        },\n        \"provider\": {\n          \"title\": \"Provider\",\n          \"description\": \"Can be one of github, gitlab, generic, google, microsoft, discord.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"github\",\n            \"gitlab\",\n            \"generic\",\n            \"google\",\n            \"microsoft\",\n            \"discord\"\n          ],\n          \"examples\": [\"google\"]\n        },\n        \"client_id\": {\n          \"type\": \"string\"\n        },\n        \"client_secret\": {\n          \"type\": \"string\"\n        },\n        \"issuer_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com\"]\n        },\n        \"auth_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://accounts.google.com/o/oauth2/v2/auth\"]\n        },\n        \"token_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://www.googleapis.com/oauth2/v4/token\"]\n        },\n        \"mapper_url\": {\n          \"title\": \"Jsonnet Mapper URL\",\n          \"description\": \"The URL where the jsonnet source is located for mapping the provider's data to ORY Kratos data.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/oidc.jsonnet\",\n            \"https://foo.bar.com/path/to/oidc.jsonnet\",\n            \"base64://bG9jYWwgc3ViamVjdCA9I...\"\n          ]\n        },\n        \"scope\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"examples\": [\"offline_access\", \"profile\"]\n          }\n        },\n        \"tenant\": {\n          \"title\": \"Azure AD Tenant\",\n          \"description\": \"The Azure AD Tenant to use for authentication.\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"common\",\n            \"organizations\",\n            \"consumers\",\n            \"8eaef023-2b34-4da1-9baa-8bc8c9d6a490\",\n            \"contoso.onmicrosoft.com\"\n          ]\n        },\n        \"requested_claims\": {\n          \"$ref\": \"#/definitions/OIDCClaims\"\n        }\n      },\n      \"additionalProperties\": false,\n      \"required\": [\n        \"id\",\n        \"provider\",\n        \"client_id\",\n        \"client_secret\",\n        \"mapper_url\"\n      ],\n      \"if\": {\n        \"properties\": {\n          \"provider\": {\n            \"const\": \"microsoft\"\n          }\n        },\n        \"required\": [\"provider\"]\n      },\n      \"then\": {\n        \"required\": [\"tenant\"]\n      },\n      \"else\": {\n        \"not\": {\n          \"properties\": {\n            \"tenant\": {}\n          },\n          \"required\": [\"tenant\"]\n        }\n      }\n    },\n    \"selfServiceAfterSettingsMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceVerifyHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterLoginMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionRevokerHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterRegistrationMethod\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"hooks\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/selfServiceSessionIssuerHook\"\n              }\n            ]\n          },\n          \"uniqueItems\": true,\n          \"additionalItems\": false\n        }\n      }\n    },\n    \"selfServiceAfterSettings\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsMethod\"\n        },\n        \"profile\": {\n          \"$ref\": \"#/definitions/selfServiceAfterSettingsMethod\"\n        }\n      }\n    },\n    \"selfServiceAfterLogin\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterLoginMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterLoginMethod\"\n        }\n      }\n    },\n    \"selfServiceAfterRegistration\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        },\n        \"oidc\": {\n          \"$ref\": \"#/definitions/selfServiceAfterRegistrationMethod\"\n        }\n      }\n    }\n  },\n  \"properties\": {\n    \"selfservice\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default_browser_return_url\"],\n      \"properties\": {\n        \"default_browser_return_url\": {\n          \"$ref\": \"#/definitions/defaultReturnTo\"\n        },\n        \"whitelisted_return_urls\": {\n          \"title\": \"Whitelisted Return To URLs\",\n          \"description\": \"List of URLs that are allowed to be redirected to. A redirection request is made by appending `?return_to=...` to Login, Registration, and other self-service flows.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"format\": \"uri-reference\"\n          },\n          \"examples\": [\n            [\n              \"https://app.my-app.com/dashboard\",\n              \"/dashboard\",\n              \"https://www.my-app.com/\"\n            ]\n          ],\n          \"uniqueItems\": true\n        },\n        \"flows\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"settings\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"URL of the Settings page.\",\n                  \"description\": \"URL where the Settings UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/user/settings\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/settings\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"privileged_session_max_age\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterSettings\"\n                }\n              }\n            },\n            \"logout\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"after\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  }\n                }\n              }\n            },\n            \"registration\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Registration UI URL\",\n                  \"description\": \"URL where the Registration UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/signup\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/registration\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterRegistration\"\n                }\n              }\n            },\n            \"login\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"Login UI URL\",\n                  \"description\": \"URL where the Login UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/login\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/login\"\n                },\n                \"lifespan\": {\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                },\n                \"after\": {\n                  \"$ref\": \"#/definitions/selfServiceAfterLogin\"\n                }\n              }\n            },\n            \"verification\": {\n              \"title\": \"Email and Phone Verification and Account Activation Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Email/Phone Verification\",\n                  \"description\": \"If set to true will enable [Email and Phone Verification and Account Activation](https://www.ory.sh/kratos/docs/self-service/flows/verify-email-account-activation/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Verify UI URL\",\n                  \"description\": \"URL where the ORY Verify UI is hosted. This is the page where users activate and / or verify their email or telephone number. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/verification\"\n                },\n                \"after\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  },\n                  \"additionalProperties\": false\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Verification Request Lifespan\",\n                  \"description\": \"Sets how long the verification request (for the UI interaction) is valid.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                }\n              }\n            },\n            \"recovery\": {\n              \"title\": \"Account Recovery Configuration\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enable Account Recovery\",\n                  \"description\": \"If set to true will enable [Account Recovery](https://www.ory.sh/kratos/docs/self-service/flows/password-reset-account-recovery/).\",\n                  \"default\": false\n                },\n                \"ui_url\": {\n                  \"title\": \"Recovery UI URL\",\n                  \"description\": \"URL where the ORY Recovery UI is hosted. This is the page where users request and complete account recovery. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/verify\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/recovery\"\n                },\n                \"after\": {\n                  \"type\": \"object\",\n                  \"properties\": {\n                    \"default_browser_return_url\": {\n                      \"$ref\": \"#/definitions/defaultReturnTo\"\n                    }\n                  },\n                  \"additionalProperties\": false\n                },\n                \"lifespan\": {\n                  \"title\": \"Self-Service Recovery Request Lifespan\",\n                  \"description\": \"Sets how long the recovery request is valid. If expired, the user has to redo the flow.\",\n                  \"type\": \"string\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"default\": \"1h\",\n                  \"examples\": [\"1h\", \"1m\", \"1s\"]\n                }\n              }\n            },\n            \"error\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"ui_url\": {\n                  \"title\": \"ORY Kratos Error UI URL\",\n                  \"description\": \"URL where the ORY Kratos Error UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\",\n                  \"type\": \"string\",\n                  \"format\": \"uri-reference\",\n                  \"examples\": [\"https://my-app.com/kratos-error\"],\n                  \"default\": \"https://www.ory.sh/kratos/docs/fallback/error\"\n                }\n              }\n            }\n          }\n        },\n        \"methods\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"profile\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Profile Management Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"link\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Link Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"password\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables Username/Email and Password Method\",\n                  \"default\": true\n                }\n              }\n            },\n            \"oidc\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"title\": \"Enables OpenID Connect Method\",\n                  \"default\": false\n                },\n                \"config\": {\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"providers\": {\n                      \"title\": \"OpenID Connect and OAuth2 Providers\",\n                      \"description\": \"A list and configuration of OAuth2 and OpenID Connect providers ORY Kratos should integrate with.\",\n                      \"type\": \"array\",\n                      \"items\": {\n                        \"$ref\": \"#/definitions/selfServiceOIDCProvider\"\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    },\n    \"dsn\": {\n      \"type\": \"string\",\n      \"title\": \"Data Source Name\",\n      \"description\": \"DSN is used to specify the database credentials as a connection URI.\",\n      \"examples\": [\n        \"postgres://user: password@postgresd:5432/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"mysql://user:secret@tcp(mysqld:3306)/database?max_conns=20&max_idle_conns=4\",\n        \"cockroach://user@cockroachdb:26257/database?sslmode=disable&max_conns=20&max_idle_conns=4\",\n        \"sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc\"\n      ]\n    },\n    \"courier\": {\n      \"type\": \"object\",\n      \"title\": \"Courier configuration\",\n      \"description\": \"The courier is responsible for sending and delivering messages over email, sms, and other means.\",\n      \"properties\": {\n        \"template_override_path\": {\n          \"type\": \"string\",\n          \"title\": \"Override message templates\",\n          \"description\": \"You can override certain or all message templates by pointing this key to the path where the templates are located.\",\n          \"examples\": [\"/conf/courier-templates\"]\n        },\n        \"smtp\": {\n          \"title\": \"SMTP Configuration\",\n          \"description\": \"Configures outgoing emails using the SMTP protocol.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"connection_uri\": {\n              \"title\": \"SMTP connection string\",\n              \"description\": \"This URI will be used to connect to the SMTP server. Use the query parameter to allow (`?skip_ssl_verify=true`) or disallow (`?skip_ssl_verify=false`) self-signed TLS certificates. Please keep in mind that any host other than localhost / 127.0.0.1 must use smtp over TLS (smtps) or the connection will not be possible.\",\n              \"examples\": [\n                \"smtps://foo:bar@my-mailserver:1234/?skip_ssl_verify=false\"\n              ],\n              \"type\": \"string\",\n              \"format\": \"uri\"\n            },\n            \"from_address\": {\n              \"title\": \"SMTP Sender Address\",\n              \"description\": \"The recipient of an email will see this as the sender address.\",\n              \"type\": \"string\",\n              \"format\": \"email\",\n              \"default\": \"no-reply@ory.kratos.sh\"\n            }\n          },\n          \"required\": [\"connection_uri\"],\n          \"additionalProperties\": false\n        }\n      },\n      \"required\": [\"smtp\"],\n      \"additionalProperties\": false\n    },\n    \"serve\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"admin\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"base_url\": {\n              \"title\": \"Admin Base URL\",\n              \"description\": \"The URL where the admin endpoint is exposed at.\",\n              \"type\": \"string\",\n              \"format\": \"uri\",\n              \"examples\": [\"https://kratos.private-network:4434/\"]\n            },\n            \"host\": {\n              \"title\": \"Admin Host\",\n              \"description\": \"The host (interface) kratos' admin endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Admin Port\",\n              \"description\": \"The port kratos' admin endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4434],\n              \"default\": 4434\n            }\n          },\n          \"additionalProperties\": false\n        },\n        \"public\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"cors\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"description\": \"Configures Cross Origin Resource Sharing for public endpoints.\",\n              \"properties\": {\n                \"enabled\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether CORS is enabled.\",\n                  \"default\": false\n                },\n                \"allowed_origins\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin.\",\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"minLength\": 1,\n                    \"not\": {\n                      \"type\": \"string\",\n                      \"description\": \"does match all strings that contain two or more (*)\",\n                      \"pattern\": \".*\\\\*.*\\\\*.*\"\n                    },\n                    \"anyOf\": [\n                      {\n                        \"format\": \"uri\"\n                      },\n                      {\n                        \"const\": \"*\"\n                      }\n                    ]\n                  },\n                  \"uniqueItems\": true,\n                  \"default\": [\"*\"],\n                  \"examples\": [\n                    [\n                      \"https://example.com\",\n                      \"https://*.example.com\",\n                      \"https://*.foo.example.com\"\n                    ]\n                  ]\n                },\n                \"allowed_methods\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of HTTP methods the user agent is allowed to use with cross-domain requests.\",\n                  \"default\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n                  \"items\": {\n                    \"type\": \"string\",\n                    \"enum\": [\n                      \"POST\",\n                      \"GET\",\n                      \"PUT\",\n                      \"PATCH\",\n                      \"DELETE\",\n                      \"CONNECT\",\n                      \"HEAD\",\n                      \"OPTIONS\",\n                      \"TRACE\"\n                    ]\n                  }\n                },\n                \"allowed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n                  \"default\": [\n                    \"Authorization\",\n                    \"Content-Type\",\n                    \"X-Session-Token\"\n                  ],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"exposed_headers\": {\n                  \"type\": \"array\",\n                  \"description\": \"Sets which headers are safe to expose to the API of a CORS API specification.\",\n                  \"default\": [\"Content-Type\"],\n                  \"items\": {\n                    \"type\": \"string\"\n                  }\n                },\n                \"allow_credentials\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Sets whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\",\n                  \"default\": true\n                },\n                \"options_passthrough\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"TODO\",\n                  \"default\": false\n                },\n                \"max_age\": {\n                  \"type\": \"integer\",\n                  \"description\": \"Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request is preceded by a preflight request.\",\n                  \"default\": 0,\n                  \"minimum\": 0\n                },\n                \"debug\": {\n                  \"type\": \"boolean\",\n                  \"description\": \"Adds additional log output to debug server side CORS issues.\",\n                  \"default\": false\n                }\n              }\n            },\n            \"base_url\": {\n              \"title\": \"Public Base URL\",\n              \"description\": \"The URL where the public endpoint is exposed at.\",\n              \"type\": \"string\",\n              \"format\": \"uri-reference\",\n              \"examples\": [\n                \"https://my-app.com/.ory/kratos/public\",\n                \"/.ory/kratos/public/\"\n              ]\n            },\n            \"host\": {\n              \"title\": \"Public Host\",\n              \"description\": \"The host (interface) kratos' public endpoint listens on.\",\n              \"type\": \"string\",\n              \"default\": \"0.0.0.0\"\n            },\n            \"port\": {\n              \"title\": \"Public Port\",\n              \"description\": \"The port kratos' public endpoint listens on.\",\n              \"type\": \"integer\",\n              \"minimum\": 1,\n              \"maximum\": 65535,\n              \"examples\": [4433],\n              \"default\": 4433\n            }\n          },\n          \"additionalProperties\": false\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"log\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"level\": {\n          \"type\": \"string\",\n          \"enum\": [\n            \"trace\",\n            \"debug\",\n            \"info\",\n            \"warning\",\n            \"error\",\n            \"fatal\",\n            \"panic\"\n          ]\n        },\n        \"leak_sensitive_values\": {\n          \"type\": \"boolean\",\n          \"title\": \"Leak Sensitive Log Values\",\n          \"description\": \"If set will leak sensitive values (e.g. emails) in the logs.\"\n        },\n        \"redaction_text\": {\n          \"type\": \"string\",\n          \"title\": \"Sensitive log value redaction text\",\n          \"description\": \"Text to use, when redacting sensitive log value.\"\n        },\n        \"format\": {\n          \"type\": \"string\",\n          \"enum\": [\"json\", \"text\"]\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"identity\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default_schema_url\": {\n          \"title\": \"JSON Schema URL for default identity traits\",\n          \"description\": \"Path to the JSON Schema which describes a default identity's traits.\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\n            \"file://path/to/identity.traits.schema.json\",\n            \"https://foo.bar.com/path/to/identity.traits.schema.json\"\n          ]\n        },\n        \"schemas\": {\n          \"type\": \"array\",\n          \"title\": \"Additional JSON Schemas for Identity Traits\",\n          \"examples\": [\n            [\n              {\n                \"id\": \"customer\",\n                \"url\": \"https://foo.bar.com/path/to/customer.traits.schema.json\"\n              },\n              {\n                \"id\": \"employee\",\n                \"url\": \"https://foo.bar.com/path/to/employee.traits.schema.json\"\n              },\n              {\n                \"id\": \"employee-v2\",\n                \"url\": \"https://foo.bar.com/path/to/employee.v2.traits.schema.json\"\n              }\n            ]\n          ],\n          \"items\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"id\": {\n                \"title\": \"The schema's ID.\",\n                \"type\": \"string\",\n                \"examples\": [\"employee\"]\n              },\n              \"url\": {\n                \"type\": \"string\",\n                \"title\": \"Path to the JSON Schema\",\n                \"format\": \"uri\",\n                \"examples\": [\n                  \"file://path/to/identity.traits.schema.json\",\n                  \"https://foo.bar.com/path/to/identity.traits.schema.json\"\n                ]\n              }\n            },\n            \"required\": [\"id\", \"url\"],\n            \"not\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"id\": {\n                  \"const\": \"default\"\n                }\n              },\n              \"additionalProperties\": true\n            }\n          }\n        }\n      },\n      \"required\": [\"default_schema_url\"],\n      \"additionalProperties\": false\n    },\n    \"secrets\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"default\": {\n          \"type\": \"array\",\n          \"title\": \"Default Encryption Signing Secrets\",\n          \"description\": \"The first secret in the array is used for singing and encrypting things while all other keys are used to verify and decrypt older things that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        },\n        \"cookie\": {\n          \"type\": \"array\",\n          \"title\": \"Singing Keys for Cookies\",\n          \"description\": \"The first secret in the array is used for encrypting cookies while all other keys are used to decrypt older cookies that were signed with that old secret.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 16\n          },\n          \"uniqueItems\": true\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"hashers\": {\n      \"title\": \"Hashing Algorithm Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"argon2\": {\n          \"title\": \"Configuration for the Argon2id hasher.\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"memory\": {\n              \"type\": \"integer\",\n              \"minimum\": 16384\n            },\n            \"iterations\": {\n              \"type\": \"integer\",\n              \"minimum\": 1\n            },\n            \"parallelism\": {\n              \"type\": \"integer\",\n              \"minimum\": 1\n            },\n            \"salt_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16\n            },\n            \"key_length\": {\n              \"type\": \"integer\",\n              \"minimum\": 16\n            }\n          },\n          \"additionalProperties\": false\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"session\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"lifespan\": {\n          \"title\": \"Session Lifespan\",\n          \"description\": \"Defines how long a session is active. Once that lifespan has been reached, the user needs to sign in again.\",\n          \"type\": \"string\",\n          \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n          \"default\": \"24h\",\n          \"examples\": [\"1h\", \"1m\", \"1s\"]\n        },\n        \"cookie\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"domain\": {\n              \"title\": \"Session Cookie Domain\",\n              \"description\": \"Sets the session cookie domain. Useful when dealing with subdomains. Use with care!\",\n              \"type\": \"string\"\n            },\n            \"persistent\": {\n              \"title\": \"Make Session Cookie Persistent\",\n              \"description\": \"If set to true will persist the cookie in the end-user's browser using the `max-age` parameter which is set to the `session.lifespan` value. Persistent cookies are not deleted when the browser is closed (e.g. on reboot or alt+f4).\",\n              \"type\": \"boolean\",\n              \"default\": true\n            },\n            \"path\": {\n              \"title\": \"Session Cookie Path\",\n              \"description\": \"Sets the session cookie path. Use with care!\",\n              \"type\": \"string\",\n              \"default\": \"/\"\n            },\n            \"same_site\": {\n              \"title\": \"Cookie Same Site Configuration\",\n              \"type\": \"string\",\n              \"enum\": [\"Strict\", \"Lax\", \"None\"],\n              \"default\": \"Lax\"\n            }\n          },\n          \"additionalProperties\": false\n        }\n      }\n    },\n    \"version\": {\n      \"title\": \"The kratos version this config is written for.\",\n      \"description\": \"SemVer according to https://semver.org/ prefixed with `v` as in our releases.\",\n      \"type\": \"string\",\n      \"pattern\": \"^v(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\",\n      \"examples\": [\"v0.5.0-alpha.1\"]\n    }\n  },\n  \"allOf\": [\n    {\n      \"if\": {\n        \"properties\": {\n          \"selfservice\": {\n            \"properties\": {\n              \"flows\": {\n                \"oneOf\": [\n                  {\n                    \"properties\": {\n                      \"verification\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"verification\"]\n                  },\n                  {\n                    \"properties\": {\n                      \"recovery\": {\n                        \"properties\": {\n                          \"enabled\": {\n                            \"const\": true\n                          }\n                        },\n                        \"required\": [\"enabled\"]\n                      }\n                    },\n                    \"required\": [\"recovery\"]\n                  }\n                ]\n              }\n            },\n            \"required\": [\"flows\"]\n          }\n        },\n        \"required\": [\"selfservice\"]\n      },\n      \"then\": {\n        \"required\": [\"courier\"]\n      }\n    }\n  ],\n  \"required\": [\"identity\", \"dsn\", \"selfservice\"]\n}\n"
  },
  {
    "path": "oryx/configx/stub/multi/expected.json",
    "content": "{\n  \"courier\": {\n    \"smtp\": {\n      \"connection_uri\": \"smtps://test:test@mailslurper:1025/?skip_ssl_verify=true\",\n      \"from_address\": \"no-reply@ory.kratos.sh\"\n    }\n  },\n  \"dsn\": \"sqlite:///var/lib/sqlite/db.sqlite?_fk=true\",\n  \"hashers\": {\n    \"argon2\": {\n      \"iterations\": 2,\n      \"key_length\": 16,\n      \"memory\": 131072,\n      \"parallelism\": 1,\n      \"salt_length\": 16\n    }\n  },\n  \"identity\": {\n    \"default_schema_url\": \"file:///etc/config/kratos/identity.schema.json\"\n  },\n  \"log\": {\n    \"format\": \"text\",\n    \"leak_sensitive_values\": true,\n    \"level\": \"debug\"\n  },\n  \"secrets\": {\n    \"cookie\": [\"PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\"]\n  },\n  \"selfservice\": {\n    \"default_browser_return_url\": \"http://127.0.0.1:4455/\",\n    \"flows\": {\n      \"error\": {\n        \"ui_url\": \"http://127.0.0.1:4455/error\"\n      },\n      \"login\": {\n        \"lifespan\": \"10m\",\n        \"ui_url\": \"http://127.0.0.1:4455/auth/login\"\n      },\n      \"logout\": {\n        \"after\": {\n          \"default_browser_return_url\": \"http://127.0.0.1:4455/auth/login\"\n        }\n      },\n      \"recovery\": {\n        \"enabled\": true,\n        \"lifespan\": \"1h\",\n        \"ui_url\": \"http://127.0.0.1:4455/recovery\"\n      },\n      \"registration\": {\n        \"after\": {\n          \"password\": {\n            \"hooks\": [\n              {\n                \"hook\": \"session\"\n              }\n            ]\n          }\n        },\n        \"lifespan\": \"10m\",\n        \"ui_url\": \"http://127.0.0.1:4455/auth/registration\"\n      },\n      \"settings\": {\n        \"lifespan\": \"1h\",\n        \"privileged_session_max_age\": \"15m\",\n        \"ui_url\": \"http://127.0.0.1:4455/settings\"\n      },\n      \"verification\": {\n        \"after\": {\n          \"default_browser_return_url\": \"http://127.0.0.1:4455/\"\n        },\n        \"enabled\": true,\n        \"lifespan\": \"1h\",\n        \"ui_url\": \"http://127.0.0.1:4455/verify\"\n      }\n    },\n    \"methods\": {\n      \"link\": {\n        \"enabled\": true\n      },\n      \"oidc\": {\n        \"enabled\": false\n      },\n      \"password\": {\n        \"enabled\": true\n      },\n      \"profile\": {\n        \"enabled\": true\n      }\n    },\n    \"whitelisted_return_urls\": [\"http://127.0.0.1:4455\"]\n  },\n  \"serve\": {\n    \"admin\": {\n      \"base_url\": \"http://kratos:4434/\",\n      \"host\": \"0.0.0.0\",\n      \"port\": 4434\n    },\n    \"public\": {\n      \"base_url\": \"http://127.0.0.1:4433/\",\n      \"cors\": {\n        \"allow_credentials\": true,\n        \"allowed_headers\": [\"Authorization\", \"Content-Type\", \"X-Session-Token\"],\n        \"allowed_methods\": [\"POST\", \"GET\", \"PUT\", \"PATCH\", \"DELETE\"],\n        \"allowed_origins\": [\"*\"],\n        \"debug\": false,\n        \"enabled\": true,\n        \"exposed_headers\": [\"Content-Type\"],\n        \"max_age\": 0,\n        \"options_passthrough\": false\n      },\n      \"host\": \"0.0.0.0\",\n      \"port\": 4433\n    }\n  },\n  \"session\": {\n    \"cookie\": {\n      \"path\": \"/\",\n      \"persistent\": true,\n      \"same_site\": \"Lax\"\n    },\n    \"lifespan\": \"24h\"\n  },\n  \"version\": \"v0.5.3-alpha.1\"\n}\n"
  },
  {
    "path": "oryx/configx/stub/nested-array/config.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"properties\": {\n    \"providers\": {\n      \"title\": \"OpenID Connect and OAuth2 Providers\",\n      \"description\": \"A list and configuration of OAuth2 and OpenID Connect providers ORY Kratos should integrate with.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"id\": {\n            \"type\": \"string\",\n            \"examples\": [\"google\"]\n          },\n          \"provider\": {\n            \"title\": \"Provider\",\n            \"description\": \"Can be one of github, gitlab, generic, google, microsoft, discord.\",\n            \"type\": \"string\",\n            \"enum\": [\n              \"github\",\n              \"gitlab\",\n              \"generic\",\n              \"google\",\n              \"microsoft\",\n              \"discord\"\n            ],\n            \"examples\": [\"google\"]\n          },\n          \"client_id\": {\n            \"type\": \"string\"\n          },\n          \"client_secret\": {\n            \"type\": \"string\"\n          },\n          \"issuer_url\": {\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"examples\": [\"https://accounts.google.com\"]\n          },\n          \"auth_url\": {\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"examples\": [\"https://accounts.google.com/o/oauth2/v2/auth\"]\n          },\n          \"token_url\": {\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"examples\": [\"https://www.googleapis.com/oauth2/v4/token\"]\n          },\n          \"mapper_url\": {\n            \"title\": \"Jsonnet Mapper URL\",\n            \"description\": \"The URL where the jsonnet source is located for mapping the provider's data to ORY Kratos data.\",\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"examples\": [\n              \"file://path/to/oidc.jsonnet\",\n              \"https://foo.bar.com/path/to/oidc.jsonnet\",\n              \"base64://bG9jYWwgc3ViamVjdCA9I...\"\n            ]\n          },\n          \"scope\": {\n            \"type\": \"array\",\n            \"items\": {\n              \"type\": \"string\",\n              \"examples\": [\"offline_access\", \"profile\"]\n            }\n          },\n          \"tenant\": {\n            \"title\": \"Azure AD Tenant\",\n            \"description\": \"The Azure AD Tenant to use for authentication.\",\n            \"type\": \"string\",\n            \"examples\": [\n              \"common\",\n              \"organizations\",\n              \"consumers\",\n              \"8eaef023-2b34-4da1-9baa-8bc8c9d6a490\",\n              \"contoso.onmicrosoft.com\"\n            ]\n          }\n        },\n        \"additionalProperties\": false,\n        \"required\": [],\n        \"if\": {\n          \"properties\": {\n            \"provider\": {\n              \"const\": \"microsoft\"\n            }\n          },\n          \"required\": [\"provider\"]\n        },\n        \"then\": {\n          \"required\": [\"tenant\"]\n        },\n        \"else\": {\n          \"not\": {\n            \"properties\": {\n              \"tenant\": {}\n            },\n            \"required\": [\"tenant\"]\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/configx/stub/nested-array/expected.json",
    "content": "{\n  \"providers\": [\n    {\n      \"id\": \"google\",\n      \"client_id\": \"client@example.com\"\n    },\n    {\n      \"client_id\": \"some@example.com\"\n    }\n  ]\n}\n"
  },
  {
    "path": "oryx/configx/stub/nested-array/kratos.yaml",
    "content": "providers:\n  - id: google\n"
  },
  {
    "path": "oryx/configx/stub/watch/config.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"config\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"dsn\": {\n      \"type\": \"string\"\n    },\n    \"foo\": {\n      \"const\": \"bar\"\n    },\n    \"bar\": {\n      \"type\": \"string\",\n      \"enum\": [\"foo\", \"bar\", \"baz\"]\n    }\n  },\n  \"required\": [\"dsn\"]\n}\n"
  },
  {
    "path": "oryx/configx/tls.schema.json",
    "content": "{\n  \"$id\": \"https://github.com/ory/x/tlsx/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"HTTPS\",\n  \"description\": \"Configure HTTP over TLS (HTTPS).\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"properties\": {\n    \"enabled\": {\n      \"type\": \"boolean\"\n    },\n    \"key\": {\n      \"title\": \"Private Key (PEM)\",\n      \"$ref\": \"#/definitions/source\"\n    },\n    \"cert\": {\n      \"title\": \"TLS Certificate (PEM)\",\n      \"$ref\": \"#/definitions/source\"\n    },\n    \"allow_termination_from\": {\n      \"type\": \"array\",\n      \"description\": \"Allow-list one or multiple CIDR address ranges and allow them to terminate TLS connections. Be aware that the X-Forwarded-Proto header must be set and must never be modifiable by anyone but your proxy / gateway / load balancer. Supports ipv4 and ipv6. The service serves http instead of https when this option is set.\",\n      \"items\": {\n        \"description\": \"CIDR address range.\",\n        \"type\": \"string\",\n        \"oneOf\": [\n          {\n            \"pattern\": \"^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$\"\n          },\n          {\n            \"pattern\": \"^([0-9]{1,3}\\\\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$\"\n          }\n        ],\n        \"examples\": [\"127.0.0.1/32\"]\n      }\n    }\n  },\n  \"definitions\": {\n    \"source\": {\n      \"type\": \"object\",\n      \"oneOf\": [\n        {\n          \"properties\": {\n            \"path\": {\n              \"title\": \"Path to PEM-encoded File\",\n              \"type\": \"string\",\n              \"examples\": [\"path/to/file.pem\"]\n            }\n          },\n          \"additionalProperties\": false\n        },\n        {\n          \"properties\": {\n            \"base64\": {\n              \"title\": \"Base64 Encoded Inline\",\n              \"description\": \"The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\",\n              \"type\": \"string\",\n              \"examples\": [\n                \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n              ]\n            }\n          },\n          \"additionalProperties\": false\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/contextx/contextual.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage contextx\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype (\n\tContextualizer interface {\n\t\t// Network returns the network id for the given context.\n\t\tNetwork(ctx context.Context, network uuid.UUID) uuid.UUID\n\n\t\t// Config returns the config for the given context.\n\t\tConfig(ctx context.Context, config *configx.Provider) *configx.Provider\n\t}\n\tProvider interface {\n\t\tContextualizer() Contextualizer\n\t}\n\tStatic struct {\n\t\tNID uuid.UUID\n\t\tC   *configx.Provider\n\t}\n)\n\nfunc (d *Static) Network(_ context.Context, nid uuid.UUID) uuid.UUID {\n\tif d.NID == uuid.Nil {\n\t\treturn nid\n\t}\n\treturn d.NID\n}\n\nfunc (d *Static) Config(_ context.Context, c *configx.Provider) *configx.Provider {\n\tif d.C == nil {\n\t\treturn c\n\t}\n\treturn d.C\n}\n"
  },
  {
    "path": "oryx/contextx/contextual_mock.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage contextx\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\n// TestContextualizer is a mock implementation of the Contextualizer interface.\ntype TestContextualizer struct{}\n\ntype contextKeyFake int\n\n// fakeNIDContext is a test key for NID.\nconst fakeNIDContext contextKeyFake = 1\n\n// SetNIDContext sets the nid for the given context.\nfunc SetNIDContext(ctx context.Context, nid uuid.UUID) context.Context {\n\treturn context.WithValue(ctx, fakeNIDContext, nid) //nolint:staticcheck\n}\n\n// Network returns the network id for the given context.\nfunc (d *TestContextualizer) Network(ctx context.Context, network uuid.UUID) uuid.UUID {\n\tnid, ok := ctx.Value(fakeNIDContext).(uuid.UUID)\n\tif !ok {\n\t\treturn network\n\t}\n\treturn nid\n}\n\n// Config returns the config for the given context.\nfunc (d *TestContextualizer) Config(ctx context.Context, config *configx.Provider) *configx.Provider {\n\treturn config\n}\n"
  },
  {
    "path": "oryx/contextx/default.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage contextx\n\nimport (\n\t\"context\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/configx\"\n)\n\ntype Default struct{}\n\nvar _ Contextualizer = (*Default)(nil)\n\nfunc (d *Default) Network(_ context.Context, network uuid.UUID) uuid.UUID {\n\tif network == uuid.Nil {\n\t\tpanic(\"nid must be not nil\")\n\t}\n\treturn network\n}\n\nfunc (d *Default) Config(_ context.Context, config *configx.Provider) *configx.Provider {\n\treturn config\n}\n"
  },
  {
    "path": "oryx/contextx/testhelpers.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage contextx\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/configx\"\n)\n\ntype (\n\tTestConfigProvider struct {\n\t\tConfigSchema []byte\n\t\tOptions      []configx.OptionModifier\n\t}\n\tcontextKey int\n)\n\nfunc NewTestConfigProvider(schema []byte, opts ...configx.OptionModifier) *TestConfigProvider {\n\treturn &TestConfigProvider{\n\t\tConfigSchema: schema,\n\t\tOptions:      opts,\n\t}\n}\n\nfunc (t *TestConfigProvider) Network(ctx context.Context, network uuid.UUID) uuid.UUID {\n\treturn (&Default{}).Network(ctx, network)\n}\n\nfunc (t *TestConfigProvider) Config(ctx context.Context, config *configx.Provider) *configx.Provider {\n\tvalues, ok := ctx.Value(contextConfigKey).([]map[string]any)\n\tif !ok {\n\t\treturn config\n\t}\n\n\topts := make([]configx.OptionModifier, 1, 1+len(values))\n\topts[0] = configx.WithValues(config.All())\n\tfor _, v := range values {\n\t\topts = append(opts, configx.WithValues(v))\n\t}\n\tconfig, err := configx.New(ctx, t.ConfigSchema, append(t.Options, opts...)...)\n\tif err != nil {\n\t\t// This is not production code. The provider is only used in tests.\n\t\tpanic(err)\n\t}\n\treturn config\n}\n\nconst contextConfigKey contextKey = 1\n\nvar (\n\t_ Contextualizer = (*TestConfigProvider)(nil)\n)\n\nfunc WithConfigValue(ctx context.Context, key string, value any) context.Context {\n\treturn WithConfigValues(ctx, map[string]any{key: value})\n}\n\nfunc WithConfigValues(ctx context.Context, setValues ...map[string]any) context.Context {\n\tvalues, ok := ctx.Value(contextConfigKey).([]map[string]any)\n\tif !ok {\n\t\tvalues = make([]map[string]any, 0)\n\t}\n\tnewValues := make([]map[string]any, len(values), len(values)+len(setValues))\n\tcopy(newValues, values)\n\tnewValues = append(newValues, setValues...)\n\n\treturn context.WithValue(ctx, contextConfigKey, newValues)\n}\n\ntype ConfigurableTestHandler struct {\n\tconfigs map[uuid.UUID][]map[string]any\n\thandler http.Handler\n}\n\nfunc NewConfigurableTestHandler(h http.Handler) *ConfigurableTestHandler {\n\treturn &ConfigurableTestHandler{\n\t\tconfigs: make(map[uuid.UUID][]map[string]any),\n\t\thandler: h,\n\t}\n}\n\nfunc (t *ConfigurableTestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tcID := r.Header.Get(\"Test-Config-Id\")\n\tif config, ok := t.configs[uuid.FromStringOrNil(cID)]; ok {\n\t\tr = r.WithContext(WithConfigValues(r.Context(), config...))\n\t}\n\tt.handler.ServeHTTP(w, r)\n}\n\nfunc (t *ConfigurableTestHandler) RegisterConfig(config ...map[string]any) uuid.UUID {\n\tid := uuid.Must(uuid.NewV4())\n\tt.configs[id] = config\n\treturn id\n}\n\nfunc (t *ConfigurableTestHandler) UseConfig(r *http.Request, id uuid.UUID) *http.Request {\n\tr.Header.Set(\"Test-Config-Id\", id.String())\n\treturn r\n}\n\nfunc (t *ConfigurableTestHandler) UseConfigValues(r *http.Request, values ...map[string]any) *http.Request {\n\treturn t.UseConfig(r, t.RegisterConfig(values...))\n}\n\ntype ConfigurableTestServer struct {\n\t*httptest.Server\n\thandler   *ConfigurableTestHandler\n\ttransport http.RoundTripper\n}\n\nfunc NewConfigurableTestServer(h http.Handler) *ConfigurableTestServer {\n\thandler := NewConfigurableTestHandler(h)\n\tserver := httptest.NewServer(handler)\n\n\tt := server.Client().Transport\n\tcts := &ConfigurableTestServer{\n\t\thandler:   handler,\n\t\tServer:    server,\n\t\ttransport: t,\n\t}\n\tserver.Client().Transport = cts\n\treturn cts\n}\n\nfunc (t *ConfigurableTestServer) RoundTrip(r *http.Request) (*http.Response, error) {\n\tconfig, ok := r.Context().Value(contextConfigKey).([]map[string]any)\n\tif ok && config != nil {\n\t\tr = t.handler.UseConfigValues(r, config...)\n\t}\n\treturn t.transport.RoundTrip(r)\n}\n\ntype AutoContextClient struct {\n\t*http.Client\n\ttransport http.RoundTripper\n\tctx       context.Context\n}\n\nfunc (t *ConfigurableTestServer) Client(ctx context.Context) *AutoContextClient {\n\tbaseClient := *t.Server.Client()\n\tautoClient := &AutoContextClient{\n\t\tClient:    &baseClient,\n\t\ttransport: t,\n\t\tctx:       ctx,\n\t}\n\tbaseClient.Transport = autoClient\n\treturn autoClient\n}\n\nfunc (c *AutoContextClient) RoundTrip(r *http.Request) (*http.Response, error) {\n\treturn c.transport.RoundTrip(r.WithContext(c.ctx))\n}\n"
  },
  {
    "path": "oryx/contextx/tree.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage contextx\n\nimport (\n\t\"context\"\n\t\"testing\"\n)\n\ntype ContextKey int\n\nconst (\n\tValidContextKey ContextKey = iota + 1\n)\n\nvar RootContext = context.WithValue(context.Background(), ValidContextKey, true)\n\nfunc TestRootContext(t testing.TB) context.Context {\n\treturn context.WithValue(t.Context(), ValidContextKey, true)\n}\n\nfunc IsRootContext(ctx context.Context) bool {\n\tis, ok := ctx.Value(ValidContextKey).(bool)\n\treturn is && ok\n}\n"
  },
  {
    "path": "oryx/corsx/check_origin.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage corsx\n\nimport \"strings\"\n\n// CheckOrigin is a function that can be used well with cors.Options.AllowOriginRequestFunc.\n// It checks whether the origin is allowed following the same behavior as github.com/rs/cors.\n//\n// Recommended usage for hot-reloadable origins:\n//\n//\tfunc (p *Config) cors(ctx context.Context, prefix string) (cors.Options, bool) {\n//\t\topts, enabled := p.GetProvider(ctx).CORS(prefix, cors.Options{\n//\t\t\tAllowedMethods:   []string{\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"},\n//\t\t\tAllowedHeaders:   []string{\"Authorization\", \"Content-Type\", \"Cookie\"},\n//\t\t\tExposedHeaders:   []string{\"Content-Type\", \"Set-Cookie\"},\n//\t\t\tAllowCredentials: true,\n//\t\t})\n//\t\topts.AllowOriginRequestFunc = func(r *http.Request, origin string) bool {\n//\t\t\t// load the origins from the config on every request to allow hot-reloading\n//\t\t\tallowedOrigins := p.GetProvider(r.Context()).Strings(prefix + \".cors.allowed_origins\")\n//\t\t\treturn corsx.CheckOrigin(allowedOrigins, origin)\n//\t\t}\n//\t\treturn opts, enabled\n//\t}\nfunc CheckOrigin(allowedOrigins []string, origin string) bool {\n\tif len(allowedOrigins) == 0 {\n\t\treturn true\n\t}\n\tfor _, o := range allowedOrigins {\n\t\tif o == \"*\" {\n\t\t\t// allow all origins\n\t\t\treturn true\n\t\t}\n\t\t// Note: for origins and methods matching, the spec requires a case-sensitive matching.\n\t\t// As it may be error-prone, we chose to ignore the spec here.\n\t\t// https://github.com/rs/cors/blob/066574eebbd0f5f1b6cd1154a160cc292ac1835e/cors.go#L132-L133\n\t\to = strings.ToLower(o)\n\t\tprefix, suffix, found := strings.Cut(o, \"*\")\n\t\tif !found {\n\t\t\t// not a pattern, check for equality\n\t\t\tif o == origin {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\t// inspired by https://github.com/rs/cors/blob/066574eebbd0f5f1b6cd1154a160cc292ac1835e/utils.go#L15\n\t\tif len(origin) >= len(prefix)+len(suffix) && strings.HasPrefix(origin, prefix) && strings.HasSuffix(origin, suffix) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "oryx/corsx/cmd.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage corsx\n\n// HelpMessage returns a string containing information on setting up this CORS middleware.\nfunc HelpMessage() string {\n\treturn `- CORS_ENABLED: Switch CORS support on (true) or off (false). Default is off (false).\n\n\tExample: CORS_ENABLED=true\n\n- CORS_ALLOWED_ORIGINS: A list of origins (comma separated values) a cross-domain request can be executed from.\n\tIf the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*)\n\tto replace 0 or more characters (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.\n\tOnly one wildcard can be used per origin. The default value is *.\n\n\tExample: CORS_ALLOWED_ORIGINS=http://*.domain.com,http://*.domain2.com\n\n- CORS_ALLOWED_METHODS: A list of methods  (comma separated values) the client is allowed to use with cross-domain\n\trequests. Default value is simple methods (GET and POST).\n\n\tExample: CORS_ALLOWED_METHODS=POST,GET,PUT\n\n- CORS_ALLOWED_CREDENTIALS: Indicates whether the request can include user credentials like cookies, HTTP authentication\n\tor client side SSL certificates.\n\n\tDefault: CORS_ALLOWED_CREDENTIALS=false\n\tExample: CORS_ALLOWED_CREDENTIALS=true\n\n- CORS_DEBUG: Debugging flag adds additional output to debug server side CORS issues.\n\n\tDefault: CORS_DEBUG=false\n\tExample: CORS_DEBUG=true\n\n- CORS_MAX_AGE: Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0\n\twhich stands for no max age.\n\n\tDefault: CORS_MAX_AGE=0\n\tExample: CORS_MAX_AGE=10\n\n- CORS_ALLOWED_HEADERS: A list of non simple headers (comma separated values) the client is allowed to use with\n\tcross-domain requests.\n\n- CORS_EXPOSED_HEADERS: Indicates which headers (comma separated values) are safe to expose to the API of a\n\tCORS API specification.`\n}\n"
  },
  {
    "path": "oryx/corsx/defaults.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage corsx\n\n// CORSRequestHeadersSafelist We add the safe list cors accept headers\n// https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header\nvar CORSRequestHeadersSafelist = []string{\"Accept\", \"Content-Type\", \"Content-Length\", \"Accept-Language\", \"Content-Language\"}\n\n// CORSResponseHeadersSafelist We add the safe list cors expose headers\n// https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_response_header\nvar CORSResponseHeadersSafelist = []string{\"Set-Cookie\", \"Cache-Control\", \"Expires\", \"Last-Modified\", \"Pragma\", \"Content-Length\", \"Content-Language\", \"Content-Type\"}\n\n// CORSDefaultAllowedMethods Default allowed methods\nvar CORSDefaultAllowedMethods = []string{\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"}\n\n// CORSRequestHeadersExtended Extended list of request headers\n// these will be concatenated with the safelist\nvar CORSRequestHeadersExtended = []string{\"Authorization\", \"X-CSRF-TOKEN\"}\n\n// CORSResponseHeadersExtended Extended list of response headers\n// these will be concatenated with the safelist\nvar CORSResponseHeadersExtended = []string{}\n\n// CORSDefaultMaxAge max age for cache of preflight request result\n// default is 5 seconds\n// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age\nvar CORSDefaultMaxAge = 5\n\n// CORSAllowCredentials default value for allow credentials\n// this is required for cookies to be sent by the browser\n// we always want this since we are using cookies for authentication most of the time\nvar CORSAllowCredentials = true\n"
  },
  {
    "path": "oryx/corsx/middleware.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage corsx\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/rs/cors\"\n\t\"github.com/urfave/negroni\"\n)\n\n// ContextualizedMiddleware is a context-aware CORS middleware. It allows hot-reloading CORS configuration using\n// the HTTP request context.\n//\n//\tn := negroni.New()\n//\tn.UseFunc(ContextualizedMiddleware(func(context.Context) (opts cors.Options, enabled bool) {\n//\t  panic(\"implement me\")\n//\t})\n//\t// ...\n//\n// Deprecated: because this is not really practical to use, you should use CheckOrigin as the cors.Options.AllowOriginRequestFunc instead.\nfunc ContextualizedMiddleware(provider func(context.Context) (opts cors.Options, enabled bool)) negroni.HandlerFunc {\n\treturn func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\toptions, enabled := provider(r.Context())\n\t\tif !enabled {\n\t\t\tnext(rw, r)\n\t\t\treturn\n\t\t}\n\n\t\tcors.New(options).Handler(next).ServeHTTP(rw, r)\n\t}\n}\n"
  },
  {
    "path": "oryx/corsx/normalize.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage corsx\n\nimport \"net/url\"\n\n// NormalizeOrigins normalizes the CORS origins.\nfunc NormalizeOrigins(origins []url.URL) []string {\n\tresults := make([]string, len(origins))\n\tfor k, o := range origins {\n\t\tresults[k] = o.Scheme + \"://\" + o.Host\n\t}\n\treturn results\n}\n\n// NormalizeOriginStrings normalizes the CORS origins from string representation\nfunc NormalizeOriginStrings(origins []string) ([]string, error) {\n\tresults := make([]string, len(origins))\n\tfor k, o := range origins {\n\t\tu, err := url.ParseRequestURI(o)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults[k] = u.Scheme + \"://\" + u.Host\n\t}\n\treturn results, nil\n}\n"
  },
  {
    "path": "oryx/crdbx/readonly.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage crdbx\n\nimport (\n\t\"github.com/ory/pop/v6\"\n\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\n// SetTransactionReadOnly sets the transaction to read only for CockroachDB.\nfunc SetTransactionReadOnly(c *pop.Connection) error {\n\tif c.Dialect.Name() != dbal.DriverCockroachDB {\n\t\t// Only CockroachDB supports this.\n\t\treturn nil\n\t}\n\n\treturn sqlcon.HandleError(c.RawQuery(\"SET TRANSACTION READ ONLY\").Exec())\n}\n"
  },
  {
    "path": "oryx/crdbx/staleness.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage crdbx\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ory/x/dbal\"\n\n\t\"github.com/ory/pop/v6\"\n\n\t\"github.com/ory/x/sqlcon\"\n)\n\n// Control API consistency guarantees\n//\n// swagger:model consistencyRequestParameters\ntype ConsistencyRequestParameters struct {\n\t// Read Consistency Level (preview)\n\t//\n\t// The read consistency level determines the consistency guarantee for reads:\n\t//\n\t// - strong (slow): The read is guaranteed to return the most recent data committed at the start of the read.\n\t// - eventual (very fast): The result will return data that is about 4.8 seconds old.\n\t//\n\t// The default consistency guarantee can be changed in the Ory Network Console or using the Ory CLI with\n\t// `ory patch project --replace '/previews/default_read_consistency_level=\"strong\"'`.\n\t//\n\t// Setting the default consistency level to `eventual` may cause regressions in the future as we add consistency\n\t// controls to more APIs. Currently, the following APIs will be affected by this setting:\n\t//\n\t// - `GET /admin/identities`\n\t//\n\t// This feature is in preview and only available in Ory Network.\n\t//\n\t// required: false\n\t// in: query\n\tConsistency ConsistencyLevel `json:\"consistency\"`\n}\n\n// ConsistencyLevel is the consistency level.\n// swagger:enum ConsistencyLevel\ntype ConsistencyLevel string\n\nconst (\n\t// ConsistencyLevelUnset is the unset / default consistency level.\n\tConsistencyLevelUnset ConsistencyLevel = \"\"\n\t// ConsistencyLevelStrong is the strong consistency level.\n\tConsistencyLevelStrong ConsistencyLevel = \"strong\"\n\t// ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\n\tConsistencyLevelEventual ConsistencyLevel = \"eventual\"\n)\n\n// ConsistencyLevelFromRequest extracts the consistency level from a request.\nfunc ConsistencyLevelFromRequest(r *http.Request) ConsistencyLevel {\n\treturn ConsistencyLevelFromString(r.URL.Query().Get(\"consistency\"))\n}\n\n// ConsistencyLevelFromString converts a string to a ConsistencyLevel.\n// If the string is not recognized or unset, ConsistencyLevelStrong is returned.\nfunc ConsistencyLevelFromString(in string) ConsistencyLevel {\n\tswitch in {\n\tcase string(ConsistencyLevelStrong):\n\t\treturn ConsistencyLevelStrong\n\tcase string(ConsistencyLevelEventual):\n\t\treturn ConsistencyLevelEventual\n\tcase string(ConsistencyLevelUnset):\n\t\treturn ConsistencyLevelUnset\n\t}\n\treturn ConsistencyLevelStrong\n}\n\n// SetTransactionConsistency sets the transaction consistency level for CockroachDB.\nfunc SetTransactionConsistency(c *pop.Connection, level ConsistencyLevel, fallback ConsistencyLevel) error {\n\tq := getTransactionConsistencyQuery(c.Dialect.Name(), level, fallback)\n\tif len(q) == 0 {\n\t\treturn nil\n\t}\n\n\treturn sqlcon.HandleError(c.RawQuery(q).Exec())\n}\n\nconst transactionFollowerReadTimestamp = \"SET TRANSACTION AS OF SYSTEM TIME follower_read_timestamp()\"\n\nfunc getTransactionConsistencyQuery(dialect string, level ConsistencyLevel, fallback ConsistencyLevel) string {\n\tif dialect != dbal.DriverCockroachDB {\n\t\t// Only CockroachDB supports this.\n\t\treturn \"\"\n\t}\n\n\tswitch level {\n\tcase ConsistencyLevelStrong:\n\t\t// Nothing to do\n\t\treturn \"\"\n\tcase ConsistencyLevelEventual:\n\t\t// Jumps to end of function\n\tcase ConsistencyLevelUnset:\n\t\tfallthrough\n\tdefault:\n\t\tif fallback != ConsistencyLevelEventual {\n\t\t\t// Nothing to do\n\t\t\treturn \"\"\n\t\t}\n\n\t\t// Jumps to end of function\n\t}\n\n\treturn transactionFollowerReadTimestamp\n}\n"
  },
  {
    "path": "oryx/dbal/canonicalize.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage dbal\n\nconst (\n\tDriverMySQL       = \"mysql\"\n\tDriverCockroachDB = \"cockroach\"\n)\n"
  },
  {
    "path": "oryx/dbal/driver.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage dbal\n\nimport (\n\t\"strings\"\n)\n\n// IsSQLite returns true if the connection is a SQLite string.\nfunc IsSQLite(dsn string) bool {\n\tscheme := strings.Split(dsn, \"://\")[0]\n\treturn scheme == \"sqlite\" || scheme == \"sqlite3\"\n}\n"
  },
  {
    "path": "oryx/dbal/dsn.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage dbal\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"regexp\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar sqliteMemoryRegexp = regexp.MustCompile(`^sqlite://file:.+\\?.*&?mode=memory($|&.*)|sqlite://(file:)?:memory:\\?.*$|^(:memory:|memory)$`)\n\n// IsMemorySQLite returns true if a given DSN string is pointing to a SQLite database.\n//\n// SQLite can be written in different styles depending on the use case\n// - just in memory\n// - shared connection\n// - shared but unique in the same process\n// see: https://sqlite.org/inmemorydb.html\nfunc IsMemorySQLite(dsn string) bool { return sqliteMemoryRegexp.MatchString(dsn) }\n\n// NewSQLiteTestDatabase creates a new, unique SQLite database\n// which is shared amongst all callers and identified by an individual file name.\n// The database file is created in the system's temporary directory, and not actively\n// removed to allow debugging in case of test failures.\nfunc NewSQLiteTestDatabase(t testing.TB) string {\n\tfn, err := os.MkdirTemp(\"\", \"sqlite-test-db-*\")\n\trequire.NoError(t, err)\n\treturn NewSQLiteDatabase(fn)\n}\n\n// NewSQLiteInMemoryDatabase creates a new in-memory, unique SQLite database\n// which is shared amongst all callers and identified by an individual file name.\nfunc NewSQLiteInMemoryDatabase(name string) string {\n\treturn fmt.Sprintf(\"sqlite://file:%s?_fk=true&mode=memory&cache=shared&_busy_timeout=100000\", name)\n}\n\n// NewSQLiteDatabase creates a new on-disk, unique SQLite database\n// which is shared amongst all callers and identified by an individual file name.\n// This is sometimes necessary over a in-memory database, for example when multiple tests/goroutines run in parallel\n// and access the same table.\n// This would result in a locking error from SQLite when running in-memory.\n// Additionally, shared cache mode is deprecated and discouraged, and the problem is better solved with the WAL,\n// according to official docs.\nfunc NewSQLiteDatabase(name string) string {\n\treturn fmt.Sprintf(\"sqlite://file:%s/db.sqlite?_fk=true&_journal_mode=WAL&_busy_timeout=100000\", name)\n}\n"
  },
  {
    "path": "oryx/dbal/testhelpers.go",
    "content": "// Copyright © 2026 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage dbal\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"testing\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/fsx\"\n\t\"github.com/ory/x/sqlcon/dockertest\"\n)\n\nvar hashDumpRegex = regexp.MustCompile(`-- migrations hash: ([^\\n]+)\\n`)\n\nfunc RestoreFromSchemaDump(t testing.TB, c *pop.Connection, migrations fs.FS, writeTo string) func(testing.TB) {\n\tnewHash, err := fsx.DirHash(migrations)\n\trequire.NoError(t, err)\n\n\tdumpFilename := filepath.Join(writeTo, c.Dialect.Name()+\"_dump.sql\")\n\n\tupdateDump := func(t testing.TB) {\n\t\tdump := dockertest.DumpSchema(t, c)\n\t\tf, err := os.OpenFile(dumpFilename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)\n\t\trequire.NoError(t, err)\n\t\tdefer f.Close()\n\n\t\t_, _ = fmt.Fprintf(f, \"-- migrations hash: %x\\n\\n%s\", newHash, dump)\n\t\tt.Fatal(\"database schema restored from migrations and dump updated, please re-run the test\")\n\t}\n\n\tdump, err := os.ReadFile(dumpFilename)\n\tif errors.Is(err, fs.ErrNotExist) {\n\t\treturn updateDump\n\t}\n\trequire.NoError(t, err)\n\n\tmatches := hashDumpRegex.FindSubmatch(dump)\n\tif len(matches) != 2 {\n\t\treturn updateDump\n\t}\n\n\tcurrentHash, err := hex.DecodeString(string(matches[1]))\n\trequire.NoError(t, err)\n\n\tif !bytes.Equal(newHash, currentHash) {\n\t\treturn updateDump\n\t}\n\n\trequire.NoError(t, c.RawQuery(string(dump)).Exec())\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/decoderx/http.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage decoderx\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/jsonschemax\"\n)\n\ntype (\n\thttpDecoderOptions struct {\n\t\tkeepRequestBody           bool\n\t\tallowedContentTypes       []string\n\t\tallowedHTTPMethods        []string\n\t\tjsonSchemaRef             string\n\t\tjsonSchemaCompiler        *jsonschema.Compiler\n\t\tjsonSchemaValidate        bool\n\t\tmaxCircularReferenceDepth uint8\n\t\thandleParseErrors         parseErrorStrategy\n\t\texpectJSONFlattened       bool\n\t\tqueryAndBody              bool\n\t}\n\n\t// HTTPDecoderOption configures the HTTP decoder.\n\tHTTPDecoderOption func(*httpDecoderOptions)\n\n\tparseErrorStrategy uint8\n)\n\nconst (\n\thttpContentTypeMultipartForm  = \"multipart/form-data\"\n\thttpContentTypeURLEncodedForm = \"application/x-www-form-urlencoded\"\n\thttpContentTypeJSON           = \"application/json\"\n)\n\nconst (\n\t// ParseErrorIgnoreConversionErrors will ignore any errors caused by strconv.Parse* and use the\n\t// raw form field value, which is a string, when such a parse error occurs.\n\t//\n\t// If the JSON Schema defines `{\"ratio\": {\"type\": \"number\"}}` but `ratio=foobar` then field\n\t// `ratio` will be handled as a string. If the destination struct is a `json.RawMessage`, then\n\t// the output will be `{\"ratio\": \"foobar\"}`.\n\tParseErrorIgnoreConversionErrors parseErrorStrategy = iota + 1\n\n\t// ParseErrorUseEmptyValueOnConversionErrors will ignore any parse errors caused by strconv.Parse* and use the\n\t// default value of the type to be casted, e.g. float64(0), string(\"\").\n\t//\n\t// If the JSON Schema defines `{\"ratio\": {\"type\": \"number\"}}` but `ratio=foobar` then field\n\t// `ratio` will receive the default value for the primitive type (here `0.0` for `number`).\n\t// If the destination struct is a `json.RawMessage`, then the output will be `{\"ratio\": 0.0}`.\n\tParseErrorUseEmptyValueOnConversionErrors\n\n\t// ParseErrorReturnOnConversionErrors will abort and return with an error if strconv.Parse* returns\n\t// an error.\n\t//\n\t// If the JSON Schema defines `{\"ratio\": {\"type\": \"number\"}}` but `ratio=foobar` the parser aborts\n\t// and returns an error, here: `strconv.ParseFloat: parsing \"foobar\"`.\n\tParseErrorReturnOnConversionErrors\n)\n\nvar errKeyNotFound = errors.New(\"key not found\")\n\n// HTTPFormDecoder configures the HTTP decoder to only accept form-data\n// (application/x-www-form-urlencoded, multipart/form-data)\nfunc HTTPFormDecoder() HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\to.allowedContentTypes = []string{httpContentTypeMultipartForm, httpContentTypeURLEncodedForm}\n\t}\n}\n\n// HTTPJSONDecoder configures the HTTP decoder to only accept JSON data\n// (application/json).\nfunc HTTPJSONDecoder() HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\to.allowedContentTypes = []string{httpContentTypeJSON}\n\t}\n}\n\n// HTTPKeepRequestBody configures the HTTP decoder to allow other\n// HTTP request body readers to read the body as well by keeping\n// the data in memory.\nfunc HTTPKeepRequestBody(keep bool) HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\to.keepRequestBody = keep\n\t}\n}\n\n// HTTPDecoderSetValidatePayloads sets if payloads should be validated or not.\nfunc HTTPDecoderSetValidatePayloads(validate bool) HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\to.jsonSchemaValidate = validate\n\t\to.keepRequestBody = true\n\t}\n}\n\n// HTTPDecoderJSONFollowsFormFormat if set tells the decoder that JSON follows the same conventions\n// as the form decoder, meaning `{\"foo.bar\": \"...\"}` is translated to `{\"foo\": {\"bar\": \"...\"}}`.\nfunc HTTPDecoderJSONFollowsFormFormat() HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\to.expectJSONFlattened = true\n\t\to.keepRequestBody = true\n\t}\n}\n\n// HTTPDecoderAllowedMethods sets the allowed HTTP methods. Defaults are POST, PUT, PATCH.\nfunc HTTPDecoderAllowedMethods(method ...string) HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\to.allowedHTTPMethods = method\n\t}\n}\n\n// HTTPDecoderUseQueryAndBody will check both the HTTP body and the HTTP query params when decoding.\n// Only relevant for non-GET operations.\nfunc HTTPDecoderUseQueryAndBody() HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\to.queryAndBody = true\n\t}\n}\n\n// HTTPDecoderSetIgnoreParseErrorsStrategy sets a strategy for dealing with strconv.Parse* errors:\n//\n// - decoderx.ParseErrorIgnoreConversionErrors will ignore any parse errors caused by strconv.Parse* and use the\n// raw form field value, which is a string, when such a parse error occurs. (default)\n// - decoderx.ParseErrorUseEmptyValueOnConversionErrors will ignore any parse errors caused by strconv.Parse* and use the\n// default value of the type to be casted, e.g. float64(0), string(\"\").\n// - decoderx.ParseErrorReturnOnConversionErrors will abort and return with an error if strconv.Parse* returns\n// an error.\nfunc HTTPDecoderSetIgnoreParseErrorsStrategy(strategy parseErrorStrategy) HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\to.handleParseErrors = strategy\n\t}\n}\n\n// HTTPDecoderSetMaxCircularReferenceDepth sets the maximum recursive reference resolution depth.\nfunc HTTPDecoderSetMaxCircularReferenceDepth(depth uint8) HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\to.maxCircularReferenceDepth = depth\n\t}\n}\n\n// HTTPJSONSchemaCompiler sets a JSON schema to be used for validation and type assertion of\n// incoming requests.\nfunc HTTPJSONSchemaCompiler(ref string, compiler *jsonschema.Compiler) HTTPDecoderOption {\n\treturn func(o *httpDecoderOptions) {\n\t\tif compiler == nil {\n\t\t\tcompiler = jsonschema.NewCompiler()\n\t\t}\n\t\tcompiler.ExtractAnnotations = true\n\t\to.jsonSchemaCompiler = compiler\n\t\to.jsonSchemaRef = ref\n\t\to.jsonSchemaValidate = true\n\t}\n}\n\n// HTTPRawJSONSchemaCompiler uses a JSON Schema Compiler with the provided JSON Schema in raw byte form.\nfunc HTTPRawJSONSchemaCompiler(raw []byte) (HTTPDecoderOption, error) {\n\tcompiler := jsonschema.NewCompiler()\n\tid := fmt.Sprintf(\"%x.json\", sha256.Sum256(raw))\n\tif err := compiler.AddResource(id, bytes.NewReader(raw)); err != nil {\n\t\treturn nil, err\n\t}\n\tcompiler.ExtractAnnotations = true\n\n\treturn func(o *httpDecoderOptions) {\n\t\to.jsonSchemaCompiler = compiler\n\t\to.jsonSchemaRef = id\n\t\to.jsonSchemaValidate = true\n\t}, nil\n}\n\n// MustHTTPRawJSONSchemaCompiler uses HTTPRawJSONSchemaCompiler and panics on error.\nfunc MustHTTPRawJSONSchemaCompiler(raw []byte) HTTPDecoderOption {\n\tf, err := HTTPRawJSONSchemaCompiler(raw)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc newHTTPDecoderOptions(fs []HTTPDecoderOption) *httpDecoderOptions {\n\to := &httpDecoderOptions{\n\t\tallowedContentTypes: []string{\n\t\t\thttpContentTypeMultipartForm, httpContentTypeURLEncodedForm, httpContentTypeJSON,\n\t\t},\n\t\tallowedHTTPMethods:        []string{\"POST\", \"PUT\", \"PATCH\"},\n\t\tmaxCircularReferenceDepth: 5,\n\t\thandleParseErrors:         ParseErrorIgnoreConversionErrors,\n\t}\n\n\tfor _, f := range fs {\n\t\tf(o)\n\t}\n\n\treturn o\n}\n\nfunc validateRequest(r *http.Request, c *httpDecoderOptions) error {\n\tmethod := strings.ToUpper(r.Method)\n\n\tif !slices.Contains(c.allowedHTTPMethods, method) {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to decode body because HTTP Request Method was \"%s\" but only %v are supported.`, method, c.allowedHTTPMethods))\n\t}\n\n\tif method != \"GET\" {\n\t\tif r.ContentLength == 0 {\n\t\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to decode HTTP Request Body because its HTTP Header \"Content-Length\" is zero.`))\n\t\t}\n\n\t\tif !httpx.HasContentType(r, c.allowedContentTypes...) {\n\t\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(`HTTP %s Request used unknown HTTP Header \"Content-Type: %s\", only %v are supported.`, method, r.Header.Get(\"Content-Type\"), c.allowedContentTypes))\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc validatePayload(ctx context.Context, raw json.RawMessage, c *httpDecoderOptions) error {\n\tif !c.jsonSchemaValidate {\n\t\treturn nil\n\t}\n\n\tif c.jsonSchemaCompiler == nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"JSON Schema Validation is required but no compiler was provided.\"))\n\t}\n\n\tschema, err := c.jsonSchemaCompiler.Compile(ctx, c.jsonSchemaRef)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to load JSON Schema from location: %s\", c.jsonSchemaRef).WithDebug(err.Error()))\n\t}\n\n\tif err := schema.Validate(bytes.NewBuffer(raw)); err != nil {\n\t\tif errors.As(err, new(*jsonschema.ValidationError)) {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to process JSON Schema and input: %s\", err).WithDebug(err.Error()))\n\t}\n\n\treturn nil\n}\n\n// Decode takes an HTTP Request Body and decodes it into destination.\nfunc Decode(r *http.Request, destination any, opts ...HTTPDecoderOption) error {\n\tc := newHTTPDecoderOptions(opts)\n\tif err := validateRequest(r, c); err != nil {\n\t\treturn err\n\t}\n\n\tif r.Method == \"GET\" {\n\t\treturn decodeForm(r, destination, c)\n\t} else if httpx.HasContentType(r, httpContentTypeJSON) {\n\t\tif c.expectJSONFlattened {\n\t\t\treturn decodeJSONForm(r, destination, c)\n\t\t}\n\t\treturn decodeJSON(r, destination, c)\n\t} else if httpx.HasContentType(r, httpContentTypeMultipartForm, httpContentTypeURLEncodedForm) {\n\t\treturn decodeForm(r, destination, c)\n\t}\n\n\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to determine decoder for content type: %s\", r.Header.Get(\"Content-Type\")))\n}\n\nfunc requestBody(r *http.Request, o *httpDecoderOptions) (reader io.ReadCloser, err error) {\n\tif strings.ToUpper(r.Method) == \"GET\" {\n\t\treturn io.NopCloser(bytes.NewBufferString(r.URL.Query().Encode())), nil\n\t}\n\n\tif !o.keepRequestBody {\n\t\treturn r.Body, nil\n\t}\n\n\tbodyBytes, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"unable to read body\")\n\t}\n\n\t_ = r.Body.Close() //  must close\n\tr.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))\n\n\treturn io.NopCloser(bytes.NewBuffer(bodyBytes)), nil\n}\n\nfunc decodeJSONForm(r *http.Request, destination interface{}, o *httpDecoderOptions) error {\n\tif o.jsonSchemaCompiler == nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode HTTP Form Body because no validation schema was provided. This is a code bug.\"))\n\t}\n\n\tpaths, err := jsonschemax.ListPathsWithRecursion(r.Context(), o.jsonSchemaRef, o.jsonSchemaCompiler, o.maxCircularReferenceDepth)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithTrace(err).WithReasonf(\"Unable to prepare JSON Schema for HTTP Post Body Form parsing: %s\", err).WithDebugf(\"%+v\", err))\n\t}\n\n\treader, err := requestBody(r, o)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar interim json.RawMessage\n\tif err := json.NewDecoder(reader).Decode(&interim); err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithError(err.Error()).WithReason(\"Unable to decode form as JSON.\"))\n\t}\n\n\tparsed := gjson.ParseBytes(interim)\n\tif !parsed.IsObject() {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Expected JSON sent in request body to be an object but got: %s\", parsed.Type.String()))\n\t}\n\n\tvalues := url.Values{}\n\tparsed.ForEach(func(k, v gjson.Result) bool {\n\t\tvalues.Set(k.String(), v.String())\n\t\treturn true\n\t})\n\n\tif o.queryAndBody {\n\t\t_ = r.ParseForm()\n\t\tfor k := range r.Form {\n\t\t\tvalues.Set(k, r.Form.Get(k))\n\t\t}\n\t}\n\n\traw, err := decodeURLValues(values, paths, o)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := json.Unmarshal(raw, destination); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn validatePayload(r.Context(), raw, o)\n}\n\nfunc decodeForm(r *http.Request, destination interface{}, o *httpDecoderOptions) error {\n\tif o.jsonSchemaCompiler == nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode HTTP Form Body because no validation schema was provided. This is a code bug.\"))\n\t}\n\n\treader, err := requestBody(r, o)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer func() {\n\t\tr.Body = reader\n\t}()\n\n\tif err := r.ParseForm(); err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to decode HTTP %s form body: %s\", strings.ToUpper(r.Method), err).WithDebug(err.Error()))\n\t}\n\n\tpaths, err := jsonschemax.ListPathsWithRecursion(r.Context(), o.jsonSchemaRef, o.jsonSchemaCompiler, o.maxCircularReferenceDepth)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithTrace(err).WithReasonf(\"Unable to prepare JSON Schema for HTTP Post Body Form parsing: %s\", err).WithDebugf(\"%+v\", err))\n\t}\n\n\tvalues := r.PostForm\n\tif r.Method == \"GET\" || o.queryAndBody {\n\t\tvalues = r.Form\n\t}\n\n\traw, err := decodeURLValues(values, paths, o)\n\tif err != nil && !errors.Is(err, errKeyNotFound) {\n\t\treturn err\n\t}\n\n\tif err := json.NewDecoder(bytes.NewReader(raw)).Decode(destination); err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to decode JSON payload: %s\", err))\n\t}\n\n\treturn validatePayload(r.Context(), raw, o)\n}\n\nfunc decodeURLValues(values url.Values, paths []jsonschemax.Path, o *httpDecoderOptions) (json.RawMessage, error) {\n\traw := json.RawMessage(`{}`)\n\tfor key := range values {\n\t\tfor _, path := range paths {\n\t\t\tif key == path.Name {\n\t\t\t\tvar err error\n\t\t\t\tswitch path.Type.(type) {\n\t\t\t\tcase []string:\n\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, values[key])\n\t\t\t\tcase []float64:\n\t\t\t\t\tfor k, v := range values[key] {\n\t\t\t\t\t\tvar f float64\n\t\t\t\t\t\tif f, err = strconv.ParseFloat(v, 64); err != nil {\n\t\t\t\t\t\t\tswitch o.handleParseErrors {\n\t\t\t\t\t\t\tcase ParseErrorIgnoreConversionErrors:\n\t\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name+\".\"+strconv.Itoa(k), v)\n\t\t\t\t\t\t\tcase ParseErrorUseEmptyValueOnConversionErrors:\n\t\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name+\".\"+strconv.Itoa(k), f)\n\t\t\t\t\t\t\tcase ParseErrorReturnOnConversionErrors:\n\t\t\t\t\t\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Expected value to be a number.\").\n\t\t\t\t\t\t\t\t\tWithDetail(\"parse_error\", err.Error()).\n\t\t\t\t\t\t\t\t\tWithDetail(\"name\", key).\n\t\t\t\t\t\t\t\t\tWithDetailf(\"index\", \"%d\", k).\n\t\t\t\t\t\t\t\t\tWithDetail(\"value\", v))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name+\".\"+strconv.Itoa(k), f)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase []bool:\n\t\t\t\t\tfor k, v := range values[key] {\n\t\t\t\t\t\tvar b bool\n\t\t\t\t\t\tif b, err = strconv.ParseBool(v); err != nil {\n\t\t\t\t\t\t\tswitch o.handleParseErrors {\n\t\t\t\t\t\t\tcase ParseErrorIgnoreConversionErrors:\n\t\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name+\".\"+strconv.Itoa(k), v)\n\t\t\t\t\t\t\tcase ParseErrorUseEmptyValueOnConversionErrors:\n\t\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name+\".\"+strconv.Itoa(k), b)\n\t\t\t\t\t\t\tcase ParseErrorReturnOnConversionErrors:\n\t\t\t\t\t\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Expected value to be a boolean.\").\n\t\t\t\t\t\t\t\t\tWithDetail(\"parse_error\", err.Error()).\n\t\t\t\t\t\t\t\t\tWithDetail(\"name\", key).\n\t\t\t\t\t\t\t\t\tWithDetailf(\"index\", \"%d\", k).\n\t\t\t\t\t\t\t\t\tWithDetail(\"value\", v))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name+\".\"+strconv.Itoa(k), b)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase []interface{}:\n\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, values[key])\n\t\t\t\tcase bool:\n\t\t\t\t\tv := values[key][len(values[key])-1]\n\t\t\t\t\tif len(v) == 0 {\n\t\t\t\t\t\tif !path.Required {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tv = \"false\"\n\t\t\t\t\t}\n\n\t\t\t\t\tvar b bool\n\t\t\t\t\tif b, err = strconv.ParseBool(v); err != nil {\n\t\t\t\t\t\tswitch o.handleParseErrors {\n\t\t\t\t\t\tcase ParseErrorIgnoreConversionErrors:\n\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, v)\n\t\t\t\t\t\tcase ParseErrorUseEmptyValueOnConversionErrors:\n\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, b)\n\t\t\t\t\t\tcase ParseErrorReturnOnConversionErrors:\n\t\t\t\t\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Expected value to be a boolean.\").\n\t\t\t\t\t\t\t\tWithDetail(\"parse_error\", err.Error()).\n\t\t\t\t\t\t\t\tWithDetail(\"name\", key).\n\t\t\t\t\t\t\t\tWithDetail(\"value\", values.Get(key)))\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, b)\n\t\t\t\t\t}\n\t\t\t\tcase float64:\n\t\t\t\t\tv := values.Get(key)\n\t\t\t\t\tif len(v) == 0 {\n\t\t\t\t\t\tif !path.Required {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tv = \"0.0\"\n\t\t\t\t\t}\n\n\t\t\t\t\tvar f float64\n\t\t\t\t\tif f, err = strconv.ParseFloat(v, 64); err != nil {\n\t\t\t\t\t\tswitch o.handleParseErrors {\n\t\t\t\t\t\tcase ParseErrorIgnoreConversionErrors:\n\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, v)\n\t\t\t\t\t\tcase ParseErrorUseEmptyValueOnConversionErrors:\n\t\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, f)\n\t\t\t\t\t\tcase ParseErrorReturnOnConversionErrors:\n\t\t\t\t\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Expected value to be a number.\").\n\t\t\t\t\t\t\t\tWithDetail(\"parse_error\", err.Error()).\n\t\t\t\t\t\t\t\tWithDetail(\"name\", key).\n\t\t\t\t\t\t\t\tWithDetail(\"value\", values.Get(key)))\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, f)\n\t\t\t\t\t}\n\t\t\t\tcase string:\n\t\t\t\t\tv := values.Get(key)\n\t\t\t\t\tif len(v) == 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, v)\n\t\t\t\tcase map[string]interface{}:\n\t\t\t\t\tv := values.Get(key)\n\t\t\t\t\tif len(v) == 0 && !path.Required {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\traw, err = sjson.SetRawBytes(raw, path.Name, []byte(v))\n\t\t\t\tcase []map[string]interface{}:\n\t\t\t\t\traw, err = sjson.SetBytes(raw, path.Name, values[key])\n\t\t\t\t}\n\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to type assert values from HTTP Post Body: %s\", err))\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, path := range paths {\n\t\tif path.TypeHint != jsonschemax.JSON {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !gjson.GetBytes(raw, path.Name).Exists() {\n\t\t\tvar err error\n\t\t\traw, err = sjson.SetRawBytes(raw, path.Name, []byte(`{}`))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, errors.WithStack(err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn raw, nil\n}\n\nfunc decodeJSON(r *http.Request, destination interface{}, o *httpDecoderOptions) error {\n\treader, err := requestBody(r, o)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\traw, err := io.ReadAll(reader)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to read HTTP POST body: %s\", err))\n\t}\n\n\tdc := json.NewDecoder(bytes.NewReader(raw))\n\tif err := dc.Decode(destination); err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to decode JSON payload: %s\", err).WithDebugf(\"Received request body: %s\", string(raw)))\n\t}\n\n\tif err := validatePayload(r.Context(), raw, o); err != nil {\n\t\tif o.expectJSONFlattened && strings.Contains(err.Error(), \"json: unknown field\") {\n\t\t\treturn decodeJSONForm(r, destination, o)\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/decoderx/stub/consent.json",
    "content": "{\n  \"$id\": \"https://example.com/ory.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"additionalProperties\": false,\n  \"properties\": {\n    \"traits\": {\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"consent\": {\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"tos\": {\n              \"description\": \"yyyymmdd of when this was accepted\",\n              \"title\": \"I accept the Terms of Service https://www.ory.sh/ptos\",\n              \"const\": true,\n              \"maxLength\": 30\n            },\n            \"inner\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"foo\": {\n                  \"type\": \"string\"\n                }\n              },\n              \"required\": [\"foo\"]\n            }\n          },\n          \"required\": [\"tos\"],\n          \"title\": \"Consent\",\n          \"type\": \"object\"\n        },\n        \"notrequired\": {\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"tos\": {\n              \"description\": \"yyyymmdd of when this was accepted\",\n              \"title\": \"I accept the Terms of Service https://www.ory.sh/ptos\",\n              \"const\": true,\n              \"maxLength\": 30\n            }\n          },\n          \"required\": [\"tos\"],\n          \"title\": \"Consent\",\n          \"type\": \"object\"\n        }\n      },\n      \"required\": [\"consent\"],\n      \"type\": \"object\"\n    }\n  },\n  \"title\": \"Person\",\n  \"type\": \"object\"\n}\n"
  },
  {
    "path": "oryx/decoderx/stub/dynamic-object.json",
    "content": "{\n  \"$id\": \"https://example.com/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"name\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"first\": {\n          \"type\": \"string\"\n        },\n        \"last\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"dynamic_object\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/decoderx/stub/nested.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"required\": [\"node\"],\n  \"properties\": {\n    \"node\": {\n      \"type\": \"object\",\n      \"required\": [\"node\"],\n      \"properties\": {\n        \"node\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"node\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"leaf\": {\n                  \"type\": \"string\"\n                }\n              },\n              \"required\": [\"leaf\"]\n            },\n            \"leaf\": {\n              \"type\": \"string\"\n            }\n          },\n          \"required\": [\"leaf\"]\n        },\n        \"leaf\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/decoderx/stub/person.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"name\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"first\": {\n          \"type\": \"string\"\n        },\n        \"last\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"age\": {\n      \"type\": \"integer\"\n    },\n    \"ratio\": {\n      \"type\": \"number\"\n    },\n    \"consent\": {\n      \"type\": \"boolean\"\n    },\n    \"newsletter\": {\n      \"type\": \"boolean\"\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/decoderx/stub/required-defaults.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"name\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"first\": {\n          \"type\": \"string\"\n        },\n        \"last\": {\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\"first\"]\n    },\n    \"name2\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"first\": {\n          \"type\": \"string\"\n        },\n        \"last\": {\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\"first\"]\n    },\n    \"age\": {\n      \"type\": \"integer\"\n    },\n    \"age2\": {\n      \"type\": \"integer\"\n    },\n    \"ratio\": {\n      \"type\": \"number\"\n    },\n    \"ratio2\": {\n      \"type\": \"number\"\n    },\n    \"consent\": {\n      \"type\": \"boolean\"\n    },\n    \"consent2\": {\n      \"type\": \"boolean\"\n    },\n    \"newsletter\": {\n      \"type\": \"boolean\"\n    },\n    \"newsletter2\": {\n      \"type\": \"boolean\"\n    }\n  },\n  \"required\": [\"age2\", \"ratio2\", \"consent2\", \"newsletter2\", \"name2\"]\n}\n"
  },
  {
    "path": "oryx/decoderx/stub/schema.json",
    "content": "{\n  \"$id\": \"https://example.com/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\"foo\"],\n  \"properties\": {\n    \"foo\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/errorsx/errors.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage errorsx\n\nimport (\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n)\n\n// Cause returns the underlying cause of the error, if possible.\n// An error value has a cause if it implements the following\n// interface:\n//\n//\ttype causer interface {\n//\t       Cause() error\n//\t}\n//\n// If the error does not implement Cause, the original error will\n// be returned. If the error is nil, nil will be returned without further\n// investigation.\n// Deprecated: you should probably use errors.As instead.\nfunc Cause(err error) error {\n\ttype causer interface {\n\t\tCause() error\n\t}\n\n\tfor err != nil {\n\t\tcause, ok := err.(causer)\n\t\tif !ok || cause.Cause() == nil {\n\t\t\tbreak\n\t\t}\n\t\terr = cause.Cause()\n\t}\n\treturn err\n}\n\n// WithStack mirror pkg/errors.WithStack but does not wrap existing stack\n// traces.\n// Deprecated: you should probably use errors.WithStack instead and only annotate stacks when it makes sense.\nfunc WithStack(err error) error {\n\tif e, ok := err.(StackTracer); ok && len(e.StackTrace()) > 0 {\n\t\treturn err\n\t}\n\n\treturn errors.WithStack(err)\n}\n\n// StatusCodeCarrier can be implemented by an error to support setting status codes in the error itself.\ntype StatusCodeCarrier interface {\n\t// StatusCode returns the status code of this error.\n\tStatusCode() int\n}\n\n// RequestIDCarrier can be implemented by an error to support error contexts.\ntype RequestIDCarrier interface {\n\t// RequestID returns the ID of the request that caused the error, if applicable.\n\tRequestID() string\n}\n\n// ReasonCarrier can be implemented by an error to support error contexts.\ntype ReasonCarrier interface {\n\t// Reason returns the reason for the error, if applicable.\n\tReason() string\n}\n\n// DebugCarrier can be implemented by an error to support error contexts.\ntype DebugCarrier interface {\n\t// Debug returns debugging information for the error, if applicable.\n\tDebug() string\n}\n\n// StatusCarrier can be implemented by an error to support error contexts.\ntype StatusCarrier interface {\n\t// ID returns the error id, if applicable.\n\tStatus() string\n}\n\n// DetailsCarrier can be implemented by an error to support error contexts.\ntype DetailsCarrier interface {\n\t// Details returns details on the error, if applicable.\n\tDetails() map[string]interface{}\n}\n\n// IDCarrier can be implemented by an error to support error contexts.\ntype IDCarrier interface {\n\t// ID returns application error ID on the error, if applicable.\n\tID() string\n}\n\ntype StackTracer interface {\n\tStackTrace() errors.StackTrace\n}\n\nfunc GetCodeFromHerodotError(err error) (code int, ok bool) {\n\therodotErr := &herodot.DefaultError{}\n\tisHerodot := errors.As(err, &herodotErr)\n\n\treturn herodotErr.CodeField, isHerodot\n}\n"
  },
  {
    "path": "oryx/fetcher/fetcher.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage fetcher\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\tstderrors \"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/stringsx\"\n)\n\n// Fetcher is able to load file contents from http, https, file, and base64 locations.\ntype Fetcher struct {\n\thc    *retryablehttp.Client\n\tlimit int64\n\tcache *ristretto.Cache[[]byte, []byte]\n\tttl   time.Duration\n}\n\ntype opts struct {\n\thc    *retryablehttp.Client\n\tlimit int64\n\tcache *ristretto.Cache[[]byte, []byte]\n\tttl   time.Duration\n}\n\nvar ErrUnknownScheme = stderrors.New(\"unknown scheme\")\n\n// WithClient sets the http.Client the fetcher uses.\nfunc WithClient(hc *retryablehttp.Client) Modifier {\n\treturn func(o *opts) {\n\t\to.hc = hc\n\t}\n}\n\n// WithMaxHTTPMaxBytes reads at most limit bytes from the HTTP response body,\n// returning bytes.ErrToLarge if the limit would be exceeded.\nfunc WithMaxHTTPMaxBytes(limit int64) Modifier {\n\treturn func(o *opts) {\n\t\to.limit = limit\n\t}\n}\n\nfunc WithCache(cache *ristretto.Cache[[]byte, []byte], ttl time.Duration) Modifier {\n\treturn func(o *opts) {\n\t\tif ttl < 0 {\n\t\t\treturn\n\t\t}\n\t\to.cache = cache\n\t\to.ttl = ttl\n\t}\n}\n\nfunc newOpts() *opts {\n\treturn &opts{\n\t\thc: httpx.NewResilientClient(),\n\t}\n}\n\ntype Modifier func(*opts)\n\n// NewFetcher creates a new fetcher instance.\nfunc NewFetcher(opts ...Modifier) *Fetcher {\n\to := newOpts()\n\tfor _, f := range opts {\n\t\tf(o)\n\t}\n\treturn &Fetcher{hc: o.hc, limit: o.limit, cache: o.cache, ttl: o.ttl}\n}\n\n// FetchContext fetches the file contents from the source and allows to pass a\n// context that is used for HTTP requests.\nfunc (f *Fetcher) FetchContext(ctx context.Context, source string) (*bytes.Buffer, error) {\n\tb, err := f.FetchBytes(ctx, source)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn bytes.NewBuffer(b), nil\n}\n\n// FetchBytes fetches the file contents from the source and allows to pass a\n// context that is used for HTTP requests.\nfunc (f *Fetcher) FetchBytes(ctx context.Context, source string) ([]byte, error) {\n\tswitch s := stringsx.SwitchPrefix(source); {\n\tcase s.HasPrefix(\"http://\", \"https://\"):\n\t\treturn f.fetchRemote(ctx, source)\n\tcase s.HasPrefix(\"file://\"):\n\t\treturn f.fetchFile(strings.TrimPrefix(source, \"file://\"))\n\tcase s.HasPrefix(\"base64://\"):\n\t\tsrc, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(source, \"base64://\"))\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrapf(err, \"base64decode: %s\", source)\n\t\t}\n\t\treturn src, nil\n\tdefault:\n\t\treturn nil, errors.Wrap(ErrUnknownScheme, s.ToUnknownPrefixErr().Error())\n\t}\n}\n\nfunc (f *Fetcher) fetchRemote(ctx context.Context, source string) (b []byte, err error) {\n\tif f.cache != nil {\n\t\tcacheKey := sha256.Sum256([]byte(source))\n\t\tif v, ok := f.cache.Get(cacheKey[:]); ok {\n\t\t\tb = make([]byte, len(v))\n\t\t\tcopy(b, v)\n\t\t\treturn b, nil\n\t\t}\n\t\tdefer func() {\n\t\t\tif err == nil && len(b) > 0 {\n\t\t\t\ttoCache := make([]byte, len(b))\n\t\t\t\tcopy(toCache, b)\n\t\t\t\tf.cache.SetWithTTL(cacheKey[:], toCache, int64(len(toCache)), f.ttl)\n\t\t\t}\n\t\t}()\n\t}\n\n\treq, err := retryablehttp.NewRequestWithContext(ctx, http.MethodGet, source, nil)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"new request: %s\", source)\n\t}\n\tres, err := f.hc.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, source)\n\t}\n\tdefer res.Body.Close()\n\n\tif res.StatusCode != http.StatusOK {\n\t\treturn nil, errors.Errorf(\"expected http response status code 200 but got %d when fetching: %s\", res.StatusCode, source)\n\t}\n\n\tif f.limit > 0 {\n\t\tvar buf bytes.Buffer\n\t\tn, err := io.Copy(&buf, io.LimitReader(res.Body, f.limit+1))\n\t\tif n > f.limit {\n\t\t\treturn nil, bytes.ErrTooLarge\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn buf.Bytes(), nil\n\t}\n\treturn io.ReadAll(res.Body)\n}\n\nfunc (f *Fetcher) fetchFile(source string) ([]byte, error) {\n\tfp, err := os.Open(source) // #nosec:G304\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"unable to open file: %s\", source)\n\t}\n\tdefer fp.Close()\n\tb, err := io.ReadAll(fp)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"unable to read file: %s\", source)\n\t}\n\tif err := fp.Close(); err != nil {\n\t\treturn nil, errors.Wrapf(err, \"unable to close file: %s\", source)\n\t}\n\treturn b, nil\n}\n"
  },
  {
    "path": "oryx/flagx/flagx.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flagx\n\nimport (\n\t\"time\"\n\n\t\"github.com/spf13/pflag\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/cmdx\"\n)\n\nfunc NewFlagSet(name string) *pflag.FlagSet {\n\treturn pflag.NewFlagSet(name, pflag.ContinueOnError)\n}\n\n// MustGetBool returns a bool flag or fatals if an error occurs.\n// Deprecated: just handle the error properly, this breaks command testing\nfunc MustGetBool(cmd *cobra.Command, name string) bool {\n\tok, err := cmd.Flags().GetBool(name)\n\tif err != nil {\n\t\tcmdx.Fatalf(err.Error())\n\t}\n\treturn ok\n}\n\n// MustGetString returns a string flag or fatals if an error occurs.\n// Deprecated: just handle the error properly, this breaks command testing\nfunc MustGetString(cmd *cobra.Command, name string) string {\n\ts, err := cmd.Flags().GetString(name)\n\tif err != nil {\n\t\tcmdx.Fatalf(err.Error())\n\t}\n\treturn s\n}\n\n// MustGetDuration returns a time.Duration flag or fatals if an error occurs.\n// Deprecated: just handle the error properly, this breaks command testing\nfunc MustGetDuration(cmd *cobra.Command, name string) time.Duration {\n\td, err := cmd.Flags().GetDuration(name)\n\tif err != nil {\n\t\tcmdx.Fatalf(err.Error())\n\t}\n\treturn d\n}\n\n// MustGetStringSlice returns a []string flag or fatals if an error occurs.\n// Deprecated: just handle the error properly, this breaks command testing\nfunc MustGetStringSlice(cmd *cobra.Command, name string) []string {\n\tss, err := cmd.Flags().GetStringSlice(name)\n\tif err != nil {\n\t\tcmdx.Fatalf(err.Error())\n\t}\n\treturn ss\n}\n\n// MustGetStringArray returns a []string flag or fatals if an error occurs.\n// Deprecated: just handle the error properly, this breaks command testing\nfunc MustGetStringArray(cmd *cobra.Command, name string) []string {\n\tss, err := cmd.Flags().GetStringArray(name)\n\tif err != nil {\n\t\tcmdx.Fatalf(err.Error())\n\t}\n\treturn ss\n}\n\n// MustGetStringToStringMap returns a map[string]string flag or fatals if an error occurs.\n// Deprecated: just handle the error properly, this breaks command testing\nfunc MustGetStringToStringMap(cmd *cobra.Command, name string) map[string]string {\n\tss, err := cmd.Flags().GetStringToString(name)\n\tif err != nil {\n\t\tcmdx.Fatalf(err.Error())\n\t}\n\treturn ss\n}\n\n// MustGetInt returns a int flag or fatals if an error occurs.\n// Deprecated: just handle the error properly, this breaks command testing\nfunc MustGetInt(cmd *cobra.Command, name string) int {\n\tss, err := cmd.Flags().GetInt(name)\n\tif err != nil {\n\t\tcmdx.Fatalf(err.Error())\n\t}\n\treturn ss\n}\n\n// MustGetUint8 returns a uint8 flag or fatals if an error occurs.\n// Deprecated: just handle the error properly, this breaks command testing\nfunc MustGetUint8(cmd *cobra.Command, name string) uint8 {\n\tv, err := cmd.Flags().GetUint8(name)\n\tif err != nil {\n\t\tcmdx.Fatalf(err.Error())\n\t}\n\treturn v\n}\n\n// MustGetUint32 returns a uint32 flag or fatals if an error occurs.\n// Deprecated: just handle the error properly, this breaks command testing\nfunc MustGetUint32(cmd *cobra.Command, name string) uint32 {\n\tv, err := cmd.Flags().GetUint32(name)\n\tif err != nil {\n\t\tcmdx.Fatalf(err.Error())\n\t}\n\treturn v\n}\n"
  },
  {
    "path": "oryx/fsx/dirhash.go",
    "content": "// Copyright © 2026 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage fsx\n\nimport (\n\t\"crypto/sha512\"\n\t\"io\"\n\t\"io/fs\"\n)\n\n// DirHash computes a directory hash from all files contained in any subdirectories.\nfunc DirHash(dir fs.FS) ([]byte, error) {\n\thash := sha512.New()\n\terr := fs.WalkDir(dir, \".\", func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif d.IsDir() {\n\t\t\treturn nil\n\t\t}\n\t\t_, _ = io.WriteString(hash, path) // hash write never errors\n\t\tf, err := dir.Open(path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, _ = io.Copy(hash, f) // hash write never errors\n\t\tif err = f.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\treturn hash.Sum(nil), err\n}\n"
  },
  {
    "path": "oryx/fsx/merge.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage fsx\n\nimport (\n\t\"io\"\n\t\"io/fs\"\n\t\"sort\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n)\n\ntype (\n\tmergedFS   []fs.FS\n\tmergedFile struct {\n\t\tfiles                 []fs.File\n\t\tunprocessedDirEntries dirEntries\n\t}\n\tmergedFileInfo []fs.FileInfo\n\tdirEntries     []fs.DirEntry\n)\n\nvar (\n\t_ fs.StatFS      = (mergedFS)(nil)\n\t_ fs.ReadDirFS   = (mergedFS)(nil)\n\t_ fs.ReadDirFile = (*mergedFile)(nil)\n\t_ fs.FileInfo    = (mergedFileInfo)(nil)\n\t_ sort.Interface = (dirEntries)(nil)\n)\n\n// Merge multiple filesystems. Later file systems are shadowed by previous ones.\nfunc Merge(fss ...fs.FS) fs.FS {\n\treturn mergedFS(fss)\n}\n\nfunc (m mergedFS) Open(name string) (fs.File, error) {\n\tvar file mergedFile\n\tfor _, fsys := range m {\n\t\tf, err := fsys.Open(name)\n\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\n\t\tfile.files = append(file.files, f)\n\t}\n\tif len(file.files) == 0 {\n\t\treturn nil, errors.WithStack(fs.ErrNotExist)\n\t}\n\n\treturn &file, nil\n}\n\nfunc (m mergedFS) Stat(name string) (fs.FileInfo, error) {\n\tfor i, fsys := range m {\n\t\tinfo, err := fs.Stat(fsys, name)\n\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn nil, errors.WithStack(err)\n\t\tcase info.IsDir():\n\t\t\tdirs := mergedFileInfo{info}\n\t\t\tfor j := i + 1; j < len(m); j++ {\n\t\t\t\tinfo, err := fs.Stat(m[j], name)\n\t\t\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tdirs = append(dirs, info)\n\t\t\t}\n\t\t\treturn dirs, nil\n\t\tdefault:\n\t\t\treturn info, nil\n\t\t}\n\t}\n\treturn nil, errors.WithStack(fs.ErrNotExist)\n}\n\nfunc (m mergedFS) ReadDir(name string) ([]fs.DirEntry, error) {\n\tvar entries dirEntries\n\n\tfor _, fsys := range m {\n\t\te, err := fs.ReadDir(fsys, name)\n\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tentries = append(entries, e...)\n\t}\n\tif len(entries) == 0 {\n\t\treturn nil, errors.WithStack(fs.ErrNotExist)\n\t}\n\n\tentries.clean()\n\treturn entries, nil\n}\n\nfunc (m mergedFileInfo) Name() string {\n\treturn m[0].Name()\n}\n\nfunc (m mergedFileInfo) Size() int64 {\n\treturn m[0].Size()\n}\n\nfunc (m mergedFileInfo) Mode() fs.FileMode {\n\treturn m[0].Mode()\n}\n\nfunc (m mergedFileInfo) ModTime() time.Time {\n\treturn m[0].ModTime()\n}\n\nfunc (m mergedFileInfo) IsDir() bool {\n\treturn m[0].IsDir()\n}\n\nfunc (m mergedFileInfo) Sys() interface{} {\n\treturn m\n}\n\nfunc (d dirEntries) Len() int {\n\treturn len(d)\n}\n\nfunc (d dirEntries) Less(i, j int) bool {\n\treturn d[i].Name() < d[j].Name()\n}\n\nfunc (d dirEntries) Swap(i, j int) {\n\td[i], d[j] = d[j], d[i]\n}\n\nfunc (d *dirEntries) clean() {\n\tsort.Sort(d)\n\n\tfor i := 1; i < len(*d); i++ {\n\t\tif (*d)[i-1].Name() == (*d)[i].Name() {\n\t\t\tif len(*d)-i == 1 {\n\t\t\t\t// remove the last entry; we're done\n\t\t\t\t*d = (*d)[:i]\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// remove the duplicate entry at index i\n\t\t\t*d = append((*d)[:i], (*d)[i+1:]...)\n\n\t\t\t// need to check the same index again\n\t\t\ti--\n\t\t}\n\t}\n}\n\nfunc (m *mergedFile) Stat() (fs.FileInfo, error) {\n\treturn m.files[0].Stat()\n}\n\nfunc (m *mergedFile) Read(bytes []byte) (int, error) {\n\treturn m.files[0].Read(bytes)\n}\n\nfunc (m *mergedFile) Close() error {\n\tvar firstErr error\n\tfor _, f := range m.files {\n\t\tif err := f.Close(); err != nil {\n\t\t\tif firstErr == nil {\n\t\t\t\tfirstErr = errors.WithStack(err)\n\t\t\t}\n\t\t}\n\t}\n\treturn firstErr\n}\n\nfunc (m *mergedFile) ReadDir(n int) ([]fs.DirEntry, error) {\n\tif m.unprocessedDirEntries != nil {\n\t\tif n <= 0 {\n\t\t\tentries := m.unprocessedDirEntries\n\t\t\tm.unprocessedDirEntries = nil\n\t\t\treturn entries, nil\n\t\t}\n\t\tif n >= len(m.unprocessedDirEntries) {\n\t\t\tentries := m.unprocessedDirEntries\n\t\t\tm.unprocessedDirEntries = nil\n\t\t\treturn entries, io.EOF\n\t\t}\n\n\t\tvar entries dirEntries\n\t\tentries, m.unprocessedDirEntries = m.unprocessedDirEntries[:n], m.unprocessedDirEntries[n:]\n\t\treturn entries, nil\n\t}\n\n\tvar entries dirEntries\n\tfor _, f := range m.files {\n\t\tif f, ok := f.(fs.ReadDirFile); ok {\n\t\t\te, err := f.ReadDir(-1)\n\t\t\tif err != nil && !errors.Is(err, fs.ErrNotExist) {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tentries = append(entries, e...)\n\t\t}\n\t}\n\tif entries == nil {\n\t\tif n > 0 {\n\t\t\treturn nil, io.EOF\n\t\t}\n\t\treturn nil, nil\n\t}\n\n\tentries.clean()\n\tif n <= 0 {\n\t\treturn entries, nil\n\t}\n\tif n >= len(entries) {\n\t\treturn entries, io.EOF\n\t}\n\n\tentries, m.unprocessedDirEntries = entries[:n], entries[n:]\n\treturn entries, nil\n}\n"
  },
  {
    "path": "oryx/go.mod",
    "content": "module github.com/ory/x\n\ngo 1.26\n\nrequire (\n\tcode.dny.dev/ssrf v0.2.0\n\tgithub.com/Masterminds/sprig/v3 v3.3.0\n\tgithub.com/auth0/go-jwt-middleware/v2 v2.3.0\n\tgithub.com/avast/retry-go/v4 v4.6.1\n\tgithub.com/bmatcuk/doublestar/v2 v2.0.4\n\tgithub.com/bradleyjkemp/cupaloy/v2 v2.8.0\n\tgithub.com/cockroachdb/cockroach-go/v2 v2.4.1\n\tgithub.com/dgraph-io/ristretto/v2 v2.2.0\n\tgithub.com/docker/docker v28.3.3+incompatible\n\tgithub.com/evanphx/json-patch/v5 v5.9.11\n\tgithub.com/fsnotify/fsnotify v1.9.0\n\tgithub.com/ghodss/yaml v1.0.0\n\tgithub.com/go-jose/go-jose/v3 v3.0.4\n\tgithub.com/go-openapi/jsonpointer v0.21.2\n\tgithub.com/go-openapi/runtime v0.28.0\n\tgithub.com/go-sql-driver/mysql v1.9.3\n\tgithub.com/gobuffalo/httptest v1.5.2\n\tgithub.com/gobwas/glob v0.2.3\n\tgithub.com/goccy/go-yaml v1.18.0\n\tgithub.com/gofrs/uuid v4.4.0+incompatible\n\tgithub.com/golang-jwt/jwt/v5 v5.3.0\n\tgithub.com/google/go-jsonnet v0.21.0\n\tgithub.com/gorilla/websocket v1.5.3\n\tgithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0\n\tgithub.com/hashicorp/go-retryablehttp v0.7.8\n\tgithub.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf\n\tgithub.com/jackc/pgconn v1.14.3\n\tgithub.com/jackc/pgx/v5 v5.7.5\n\tgithub.com/jackc/puddle/v2 v2.2.2\n\tgithub.com/jmoiron/sqlx v1.4.0\n\tgithub.com/knadh/koanf/maps v0.1.2\n\tgithub.com/knadh/koanf/parsers/json v0.1.0\n\tgithub.com/knadh/koanf/parsers/toml v0.1.0\n\tgithub.com/knadh/koanf/parsers/yaml v0.1.0\n\tgithub.com/knadh/koanf/providers/posflag v0.1.0\n\tgithub.com/knadh/koanf/providers/rawbytes v0.1.0\n\tgithub.com/knadh/koanf/v2 v2.2.2\n\tgithub.com/laher/mergefs v0.1.1\n\tgithub.com/lestrrat-go/jwx v1.2.31\n\tgithub.com/lib/pq v1.10.9\n\tgithub.com/mattn/go-sqlite3 v1.14.32\n\tgithub.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826\n\tgithub.com/ory/analytics-go/v5 v5.0.1\n\tgithub.com/ory/dockertest/v4 v4.0.0-beta.4\n\tgithub.com/ory/herodot v0.10.7\n\tgithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e\n\tgithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e\n\tgithub.com/pelletier/go-toml v1.9.5\n\tgithub.com/peterhellberg/link v1.2.0\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/pkg/profile v1.7.0\n\tgithub.com/prometheus/client_golang v1.23.0\n\tgithub.com/prometheus/client_model v0.6.2\n\tgithub.com/prometheus/common v0.65.0\n\tgithub.com/rakutentech/jwk-go v1.2.0\n\tgithub.com/rs/cors v1.11.1\n\tgithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761\n\tgithub.com/sirupsen/logrus v1.9.3\n\tgithub.com/spf13/cast v1.9.2\n\tgithub.com/spf13/cobra v1.10.1\n\tgithub.com/spf13/pflag v1.0.10\n\tgithub.com/ssoready/hyrumtoken v1.0.0\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/urfave/negroni v1.0.0\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.62.0\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0\n\tgo.opentelemetry.io/contrib/propagators/b3 v1.37.0\n\tgo.opentelemetry.io/contrib/propagators/jaeger v1.37.0\n\tgo.opentelemetry.io/contrib/samplers/jaegerremote v0.31.0\n\tgo.opentelemetry.io/otel v1.40.0\n\tgo.opentelemetry.io/otel/exporters/jaeger v1.17.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0\n\tgo.opentelemetry.io/otel/exporters/zipkin v1.37.0\n\tgo.opentelemetry.io/otel/sdk v1.40.0\n\tgo.opentelemetry.io/otel/trace v1.40.0\n\tgo.opentelemetry.io/proto/otlp v1.7.1\n\tgo.uber.org/goleak v1.3.0\n\tgo.uber.org/mock v0.5.2\n\tgolang.org/x/crypto v0.46.0\n\tgolang.org/x/oauth2 v0.34.0\n\tgolang.org/x/sync v0.19.0\n\tgoogle.golang.org/grpc v1.79.3\n\tgoogle.golang.org/protobuf v1.36.10\n)\n\nrequire (\n\tdario.cat/mergo v1.0.2 // indirect\n\tfilippo.io/edwards25519 v1.2.0 // indirect\n\tgithub.com/Masterminds/goutils v1.1.1 // indirect\n\tgithub.com/Masterminds/semver/v3 v3.4.0 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/XSAM/otelsql v0.39.0 // indirect\n\tgithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/cenkalti/backoff/v5 v5.0.3 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/containerd/errdefs v1.0.0 // indirect\n\tgithub.com/containerd/errdefs/pkg v0.3.0 // indirect\n\tgithub.com/containerd/log v0.1.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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/go-connections v0.6.0 // indirect\n\tgithub.com/docker/go-units v0.5.0 // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/fatih/color v1.18.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/felixge/fgprof v0.9.5 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-openapi/errors v0.22.2 // indirect\n\tgithub.com/go-openapi/strfmt v0.23.0 // indirect\n\tgithub.com/go-openapi/swag v0.23.1 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.4.0 // 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.3 // indirect\n\tgithub.com/gobuffalo/github_flavored_markdown v1.1.4 // indirect\n\tgithub.com/gobuffalo/helpers v0.6.10 // indirect\n\tgithub.com/gobuffalo/nulls v0.4.2 // indirect\n\tgithub.com/gobuffalo/plush/v4 v4.1.22 // indirect\n\tgithub.com/gobuffalo/plush/v5 v5.0.7 // indirect\n\tgithub.com/gobuffalo/tags/v3 v3.1.4 // indirect\n\tgithub.com/gobuffalo/validate/v3 v3.3.3 // indirect\n\tgithub.com/goccy/go-json v0.10.5 // indirect\n\tgithub.com/gofrs/flock v0.12.1 // indirect\n\tgithub.com/gogo/googleapis v1.4.1 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/huandu/xstrings v1.5.0 // 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/jaegertracing/jaeger-idl v0.5.0 // indirect\n\tgithub.com/jandelgado/gcov2lcov v1.1.1 // indirect\n\tgithub.com/joho/godotenv v1.5.1 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect\n\tgithub.com/lestrrat-go/backoff/v2 v2.0.8 // indirect\n\tgithub.com/lestrrat-go/blackmagic v1.0.4 // indirect\n\tgithub.com/lestrrat-go/httpcc v1.0.1 // indirect\n\tgithub.com/lestrrat-go/iter v1.0.2 // indirect\n\tgithub.com/lestrrat-go/option v1.0.1 // indirect\n\tgithub.com/mailru/easyjson v0.9.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.14 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/mitchellh/copystructure v1.2.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.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.54.0 // indirect\n\tgithub.com/moby/moby/client v0.3.0 // indirect\n\tgithub.com/moby/sys/atomicwriter v0.1.0 // indirect\n\tgithub.com/morikuni/aec v1.0.0 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/nyaruka/phonenumbers v1.6.5 // indirect\n\tgithub.com/oklog/ulid v1.3.1 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.1.1 // indirect\n\tgithub.com/openzipkin/zipkin-go v0.4.3 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/prometheus/procfs v0.17.0 // indirect\n\tgithub.com/rogpeppe/go-internal v1.14.1 // indirect\n\tgithub.com/segmentio/backo-go v1.1.0 // indirect\n\tgithub.com/sergi/go-diff v1.4.0 // indirect\n\tgithub.com/shopspring/decimal v1.4.0 // 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/tidwall/match v1.1.1 // indirect\n\tgithub.com/tidwall/pretty v1.2.1 // indirect\n\tgithub.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect\n\tgo.mongodb.org/mongo-driver v1.17.4 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.2.1 // indirect\n\tgo.opentelemetry.io/otel/metric v1.40.0 // indirect\n\tgo.yaml.in/yaml/v2 v2.4.2 // indirect\n\tgolang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect\n\tgolang.org/x/mod v0.30.0 // indirect\n\tgolang.org/x/net v0.48.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 // indirect\n\tgolang.org/x/text v0.32.0 // indirect\n\tgolang.org/x/time v0.12.0 // indirect\n\tgolang.org/x/tools v0.39.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tsigs.k8s.io/yaml v1.6.0 // indirect\n)\n\ntool (\n\tgithub.com/jandelgado/gcov2lcov\n\tgo.uber.org/mock/mockgen\n\tgolang.org/x/tools/cmd/goimports\n)\n"
  },
  {
    "path": "oryx/go.sum",
    "content": "code.dny.dev/ssrf v0.2.0 h1:wCBP990rQQ1CYfRpW+YK1+8xhwUjv189AQ3WMo1jQaI=\ncode.dny.dev/ssrf v0.2.0/go.mod h1:B+91l25OnyaLIeCx0WRJN5qfJ/4/ZTZxRXgm0lj/2w8=\ndario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=\ndario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=\nfilippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\nfilippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=\nfilippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=\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/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=\ngithub.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=\ngithub.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=\ngithub.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=\ngithub.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=\ngithub.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=\ngithub.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/XSAM/otelsql v0.39.0 h1:4o374mEIMweaeevL7fd8Q3C710Xi2Jh/c8G4Qy9bvCY=\ngithub.com/XSAM/otelsql v0.39.0/go.mod h1:uMOXLUX+wkuAuP0AR3B45NXX7E9lJS2mERa8gqdU8R0=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=\ngithub.com/auth0/go-jwt-middleware/v2 v2.3.0 h1:4QREj6cS3d8dS05bEm443jhnqQF97FX9sMBeWqnNRzE=\ngithub.com/auth0/go-jwt-middleware/v2 v2.3.0/go.mod h1:dL4ObBs1/dj4/W4cYxd8rqAdDGXYyd5rqbpMIxcbVrU=\ngithub.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=\ngithub.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=\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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bmatcuk/doublestar/v2 v2.0.4 h1:6I6oUiT/sU27eE2OFcWqBhL1SwjyvQuOssxT4a1yidI=\ngithub.com/bmatcuk/doublestar/v2 v2.0.4/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw=\ngithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=\ngithub.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=\ngithub.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=\ngithub.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=\ngithub.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=\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/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=\ngithub.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=\ngithub.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=\ngithub.com/cockroachdb/cockroach-go/v2 v2.4.1 h1:ACVT/zXsuK6waRPVYtDQpsM8pPA7IA/3fkgA02RR/Gw=\ngithub.com/cockroachdb/cockroach-go/v2 v2.4.1/go.mod h1:9U179XbCx4qFWtNhc7BiWLPfuyMVQ7qdAhfrwLz1vH0=\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/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=\ngithub.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=\ngithub.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=\ngithub.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=\ngithub.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\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/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=\ngithub.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\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.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\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/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=\ngithub.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=\ngithub.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\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/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=\ngithub.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/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-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=\ngithub.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=\ngithub.com/go-openapi/errors v0.22.2 h1:rdxhzcBUazEcGccKqbY1Y7NS8FDcMyIRr0934jrYnZg=\ngithub.com/go-openapi/errors v0.22.2/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0=\ngithub.com/go-openapi/jsonpointer v0.21.2 h1:AqQaNADVwq/VnkCmQg6ogE+M3FOsKTytwges0JdwVuA=\ngithub.com/go-openapi/jsonpointer v0.21.2/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=\ngithub.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=\ngithub.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=\ngithub.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=\ngithub.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=\ngithub.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=\ngithub.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=\ngithub.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=\ngithub.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=\ngithub.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=\ngithub.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=\ngithub.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=\ngithub.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=\ngithub.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=\ngithub.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=\ngithub.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=\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-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\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/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.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=\ngithub.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=\ngithub.com/gobuffalo/github_flavored_markdown v1.1.3/go.mod h1:IzgO5xS6hqkDmUh91BW/+Qxo/qYnvfzoz3A7uLkg77I=\ngithub.com/gobuffalo/github_flavored_markdown v1.1.4 h1:WacrEGPXUDX+BpU1GM/Y0ADgMzESKNWls9hOTG1MHVs=\ngithub.com/gobuffalo/github_flavored_markdown v1.1.4/go.mod h1:Vl9686qrVVQou4GrHRK/KOG3jCZOKLUqV8MMOAYtlso=\ngithub.com/gobuffalo/helpers v0.6.7/go.mod h1:j0u1iC1VqlCaJEEVkZN8Ia3TEzfj/zoXANqyJExTMTA=\ngithub.com/gobuffalo/helpers v0.6.10 h1:puKDCOrJ0EIq5ScnTRgKyvEZ05xQa+gwRGCpgoh6Ek8=\ngithub.com/gobuffalo/helpers v0.6.10/go.mod h1:r52L6VSnByLJFOmURp1irvzgSakk7RodChi1YbGwk8I=\ngithub.com/gobuffalo/httptest v1.5.2 h1:GpGy520SfY1QEmyPvaqmznTpG4gEQqQ82HtHqyNEreM=\ngithub.com/gobuffalo/httptest v1.5.2/go.mod h1:FA23yjsWLGj92mVV74Qtc8eqluc11VqcWr8/C1vxt4g=\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/plush/v4 v4.1.16/go.mod h1:6t7swVsarJ8qSLw1qyAH/KbrcSTwdun2ASEQkOznakg=\ngithub.com/gobuffalo/plush/v4 v4.1.22 h1:bPQr5PsiTg54UGMsfvnIAvFmUfxzD/ri+wbpu7PlmTM=\ngithub.com/gobuffalo/plush/v4 v4.1.22/go.mod h1:WiKHJx3qBvfaDVlrv8zT7NCd3dEMaVR/fVxW4wqV17M=\ngithub.com/gobuffalo/plush/v5 v5.0.7 h1:nI8sIt5tZAN2tCZHeaXkH7HAvxvvk3sJHG2TtrKeSHM=\ngithub.com/gobuffalo/plush/v5 v5.0.7/go.mod h1:C08u/VEqzzPBXFF/yqs40P/5Cvc/zlZsMzhCxXyWJmU=\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/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=\ngithub.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=\ngithub.com/goccy/go-json v0.10.5/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/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=\ngithub.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=\ngithub.com/gofrs/uuid v4.2.0+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/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=\ngithub.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\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/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\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-jsonnet v0.21.0 h1:43Bk3K4zMRP/aAZm9Po2uSEjY6ALCkYUVIcz9HLGMvA=\ngithub.com/google/go-jsonnet v0.21.0/go.mod h1:tCGAu8cpUpEZcdGMmdOu37nh8bGgqubhI5v2iSk3KJQ=\ngithub.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=\ngithub.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=\ngithub.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=\ngithub.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=\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/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=\ngithub.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=\ngithub.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=\ngithub.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=\ngithub.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=\ngithub.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=\ngithub.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=\ngithub.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=\ngithub.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=\ngithub.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s=\ngithub.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4=\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 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-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/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-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=\ngithub.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=\ngithub.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=\ngithub.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/jaegertracing/jaeger-idl v0.5.0 h1:zFXR5NL3Utu7MhPg8ZorxtCBjHrL3ReM1VoB65FOFGE=\ngithub.com/jaegertracing/jaeger-idl v0.5.0/go.mod h1:ON90zFo9eoyXrt9F/KN8YeF3zxcnujaisMweFY/rg5k=\ngithub.com/jandelgado/gcov2lcov v1.1.1 h1:CHUNoAglvb34DqmMoZchnzDbA3yjpzT8EoUvVqcAY+s=\ngithub.com/jandelgado/gcov2lcov v1.1.1/go.mod h1:tMVUlMVtS1po2SB8UkADWhOT5Y5Q13XOce2AYU69JuI=\ngithub.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=\ngithub.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=\ngithub.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=\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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\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 v0.1.0 h1:dzSZl5pf5bBcW0Acnu20Djleto19T0CfHcvZ14NJ6fU=\ngithub.com/knadh/koanf/parsers/json v0.1.0/go.mod h1:ll2/MlXcZ2BfXD6YJcjVFzhG9P0TdJ207aIBKQhV2hY=\ngithub.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI=\ngithub.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18=\ngithub.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=\ngithub.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=\ngithub.com/knadh/koanf/providers/posflag v0.1.0 h1:mKJlLrKPcAP7Ootf4pBZWJ6J+4wHYujwipe7Ie3qW6U=\ngithub.com/knadh/koanf/providers/posflag v0.1.0/go.mod h1:SYg03v/t8ISBNrMBRMlojH8OsKowbkXV7giIbBVgbz0=\ngithub.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME=\ngithub.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c=\ngithub.com/knadh/koanf/v2 v2.2.2 h1:ghbduIkpFui3L587wavneC9e3WIliCgiCgdxYO/wd7A=\ngithub.com/knadh/koanf/v2 v2.2.2/go.mod h1:abWQc0cBXLSF/PSOMCB/SK+T13NXDsPvOksbpi5e/9Q=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\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/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/laher/mergefs v0.1.1 h1:nV2bTS57vrmbMxeR6uvJpI8LyGl3QHj4bLBZO3aUV58=\ngithub.com/laher/mergefs v0.1.1/go.mod h1:FSY1hYy94on4Tz60waRMGdO1awwS23BacqJlqf9lJ9Q=\ngithub.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=\ngithub.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=\ngithub.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=\ngithub.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=\ngithub.com/lestrrat-go/blackmagic v1.0.4/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/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 v1.2.31 h1:/OM9oNl/fzyldpv5HKZ9m7bTywa7COUfg8gujd9nJ54=\ngithub.com/lestrrat-go/jwx v1.2.31/go.mod h1:eQJKoRwWcLg4PfD5CFA5gIZGxhPgoPYq9pZISdxLf0c=\ngithub.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=\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.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=\ngithub.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=\ngithub.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=\ngithub.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=\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.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.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=\ngithub.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=\ngithub.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=\ngithub.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50=\ngithub.com/microcosm-cc/bluemonday v1.0.22/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\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.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0=\ngithub.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=\ngithub.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs=\ngithub.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ=\ngithub.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=\ngithub.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=\ngithub.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=\ngithub.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=\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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=\ngithub.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=\ngithub.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\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/nyaruka/phonenumbers v1.6.5 h1:aBCaUhfpRA7hU6fsXk+p7KF1aNx4nQlq9hGeo2qdFg8=\ngithub.com/nyaruka/phonenumbers v1.6.5/go.mod h1:7gjs+Lchqm49adhAKB5cdcng5ZXgt6x7Jgvi0ZorUtU=\ngithub.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=\ngithub.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=\ngithub.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=\ngithub.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=\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/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=\ngithub.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=\ngithub.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=\ngithub.com/ory/analytics-go/v5 v5.0.1 h1:LX8T5B9FN8KZXOtxgN+R3I4THRRVB6+28IKgKBpXmAM=\ngithub.com/ory/analytics-go/v5 v5.0.1/go.mod h1:lWCiCjAaJkKfgR/BN5DCLMol8BjKS1x+4jxBxff/FF0=\ngithub.com/ory/dockertest/v4 v4.0.0-beta.4 h1:QcrNrobOP+5IjSDmS4//EuBtwiFuznQhi5xTe8oFSoM=\ngithub.com/ory/dockertest/v4 v4.0.0-beta.4/go.mod h1:p9kfE14tzK8+WU4F9YbIZlzhCzQ2pH7H1KIfBKrF3DM=\ngithub.com/ory/herodot v0.10.7 h1:CETBRP4LboLlQCSVTkyQix/a2bVh1rmNhhfxd45khCI=\ngithub.com/ory/herodot v0.10.7/go.mod h1:j6i246U6iX8TStYNKIVQxb2waweQvtOLi+b/9q+OULg=\ngithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e h1:4tUrC7x4YWRVMFp+c64KACNSGchW1zXo4l6Pa9/1hA8=\ngithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e/go.mod h1:XWLxVK4un/iuIcrw+6lCeanbF3NZwO5k6RdLeu/loQk=\ngithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e h1:gsbAteu8HZYnkIF4WVBaxklvF/s5IbcxYcCi6qX93ms=\ngithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e/go.mod h1:PEqjxMcIV87rBhlyDDha76I7/w2W/FHenSq3V3X1A/A=\ngithub.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=\ngithub.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=\ngithub.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=\ngithub.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=\ngithub.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=\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.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=\ngithub.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=\ngithub.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=\ngithub.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=\ngithub.com/rakutentech/jwk-go v1.2.0 h1:vNJwedPkRR+32V5WGNj0JP4COes93BGERvzQLBjLy4c=\ngithub.com/rakutentech/jwk-go v1.2.0/go.mod h1:pI0bYVntqaJ27RCpaC75MTUacheW0Rk4+8XzWWe1OWM=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=\ngithub.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 h1:0b8DF5kR0PhRoRXDiEEdzrgBc8UqVY4JWLkQJCRsLME=\ngithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761/go.mod h1:/THDZYi7F/BsVEcYzYPqdcWFQ+1C2InkawTKfLOAnzg=\ngithub.com/segmentio/analytics-go v3.1.0+incompatible/go.mod h1:C7CYBtQWk4vRk2RyLu0qOcbHJ18E3F1HV2C/8JvKN48=\ngithub.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc=\ngithub.com/segmentio/backo-go v1.1.0 h1:cJIfHQUdmLsd8t9IXqf5J8SdrOMn9vMa7cIvOavHAhc=\ngithub.com/segmentio/backo-go v1.1.0/go.mod h1:ckenwdf+v/qbyhVdNPWHnqh2YdJBED1O9cidYyM5J18=\ngithub.com/segmentio/conf v1.2.0/go.mod h1:Y3B9O/PqqWqjyxyWWseyj/quPEtMu1zDp/kVbSWWaB0=\ngithub.com/segmentio/go-snakecase v1.1.0/go.mod h1:jk1miR5MS7Na32PZUykG89Arm+1BUSYhuGR6b7+hJto=\ngithub.com/segmentio/objconv v1.0.1/go.mod h1:auayaH5k3137Cl4SoXTgrzQcuQDmvuVtZgS0fb1Ahys=\ngithub.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=\ngithub.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=\ngithub.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=\ngithub.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=\ngithub.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=\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/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=\ngithub.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=\ngithub.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=\ngithub.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/ssoready/hyrumtoken v1.0.0 h1:N/JPJDOuYS7qPSnOvZpPxNVXwtlT3kfzAMEcPrH8ywQ=\ngithub.com/ssoready/hyrumtoken v1.0.0/go.mod h1:h8q768r5Uv6iJKOwsNENIWWUP9kvmLykQox5m3SCpqc=\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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\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.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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\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/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/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=\ngithub.com/tidwall/pretty v1.2.1/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/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=\ngithub.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=\ngithub.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=\ngithub.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=\ngo.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.62.0 h1:wCeciVlAfb5DC8MQl/DlmAv/FVPNpQgFvI/71+hatuc=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.62.0/go.mod h1:WfEApdZDMlLUAev/0QQpr8EJ/z0VWDKYZ5tF5RH5T1U=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=\ngo.opentelemetry.io/contrib/propagators/b3 v1.37.0 h1:0aGKdIuVhy5l4GClAjl72ntkZJhijf2wg1S7b5oLoYA=\ngo.opentelemetry.io/contrib/propagators/b3 v1.37.0/go.mod h1:nhyrxEJEOQdwR15zXrCKI6+cJK60PXAkJ/jRyfhr2mg=\ngo.opentelemetry.io/contrib/propagators/jaeger v1.37.0 h1:pW+qDVo0jB0rLsNeaP85xLuz20cvsECUcN7TE+D8YTM=\ngo.opentelemetry.io/contrib/propagators/jaeger v1.37.0/go.mod h1:x7bd+t034hxLTve1hF9Yn9qQJlO/pP8H5pWIt7+gsFM=\ngo.opentelemetry.io/contrib/samplers/jaegerremote v0.31.0 h1:l8XCsDh7L6Z7PB+vlw1s4ufNab+ayT2RMNdvDE/UyPc=\ngo.opentelemetry.io/contrib/samplers/jaegerremote v0.31.0/go.mod h1:XAOSk4bqj5vtoiY08bexeiafzxdXeLlxKFnwscvn8Fc=\ngo.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=\ngo.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=\ngo.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=\ngo.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=\ngo.opentelemetry.io/otel/exporters/zipkin v1.37.0 h1:Z2apuaRnHEjzDAkpbWNPiksz1R0/FCIrJSjiMA43zwI=\ngo.opentelemetry.io/otel/exporters/zipkin v1.37.0/go.mod h1:ofGu/7fG+bpmjZoiPUUmYDJ4vXWxMT57HmGoegx49uw=\ngo.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=\ngo.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=\ngo.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=\ngo.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=\ngo.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=\ngo.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=\ngo.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=\ngo.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=\ngo.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=\ngo.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=\ngo.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=\ngo.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=\ngo.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=\ngo.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=\ngo.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=\ngolang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=\ngolang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=\ngolang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE=\ngolang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\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.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=\ngolang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=\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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\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.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=\ngolang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=\ngolang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=\ngolang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/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.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.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo=\ngolang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=\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.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.3/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.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=\ngolang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=\ngolang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=\ngolang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\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/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=\ngolang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=\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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=\ngoogle.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=\ngoogle.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=\ngoogle.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=\ngoogle.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\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/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs=\ngopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI=\ngopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=\ngopkg.in/go-playground/mold.v2 v2.2.0/go.mod h1:XMyyRsGtakkDPbxXbrA5VODo6bUXyvoDjLd5l3T0XoA=\ngopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\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=\npgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=\npgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=\nsigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=\nsigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=\n"
  },
  {
    "path": "oryx/hasherx/hash_comparator.go",
    "content": "package hasherx\n\nimport (\n\t\"context\"\n\t\"crypto/subtle\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"math\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/crypto/argon2\"\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"golang.org/x/crypto/pbkdf2\"\n)\n\nvar ErrUnknownHashAlgorithm = errors.New(\"unknown hash algorithm\")\n\n// Compare the given password with the given hash.\nfunc Compare(ctx context.Context, password []byte, hash []byte) error {\n\tswitch {\n\tcase IsBcryptHash(hash):\n\t\treturn CompareBcrypt(ctx, password, hash)\n\tcase IsArgon2idHash(hash):\n\t\treturn CompareArgon2id(ctx, password, hash)\n\tcase IsArgon2iHash(hash):\n\t\treturn CompareArgon2i(ctx, password, hash)\n\tcase IsPbkdf2Hash(hash):\n\t\treturn ComparePbkdf2(ctx, password, hash)\n\tdefault:\n\t\treturn errors.WithStack(ErrUnknownHashAlgorithm)\n\t}\n}\n\nfunc CompareBcrypt(_ context.Context, password []byte, hash []byte) error {\n\tif err := validateBcryptPasswordLength(password); err != nil {\n\t\treturn err\n\t}\n\n\terr := bcrypt.CompareHashAndPassword(hash, password)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc CompareArgon2id(_ context.Context, password []byte, hash []byte) error {\n\t// Extract the parameters, salt and derived key from the encoded password\n\t// hash.\n\tp, salt, hash, err := decodeArgon2idHash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmem := uint64(p.Memory)\n\tif mem > math.MaxUint32 {\n\t\treturn errors.WithStack(ErrInvalidHash)\n\t}\n\n\t// Derive the key from the other password using the same parameters.\n\totherHash := argon2.IDKey(password, salt, p.Iterations, uint32(mem), p.Parallelism, p.KeyLength)\n\n\t// Check that the contents of the hashed passwords are identical. Note\n\t// that we are using the subtle.ConstantTimeCompare() function for this\n\t// to help prevent timing attacks.\n\tif subtle.ConstantTimeCompare(hash, otherHash) == 1 {\n\t\treturn nil\n\t}\n\treturn errors.WithStack(ErrMismatchedHashAndPassword)\n}\n\nfunc CompareArgon2i(_ context.Context, password []byte, hash []byte) error {\n\t// Extract the parameters, salt and derived key from the encoded password\n\t// hash.\n\tp, salt, hash, err := decodeArgon2idHash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmem := uint64(p.Memory)\n\tif mem > math.MaxUint32 {\n\t\treturn errors.WithStack(ErrInvalidHash)\n\t}\n\n\t// Derive the key from the other password using the same parameters.\n\totherHash := argon2.Key(password, salt, p.Iterations, uint32(mem), p.Parallelism, p.KeyLength)\n\n\t// Check that the contents of the hashed passwords are identical. Note\n\t// that we are using the subtle.ConstantTimeCompare() function for this\n\t// to help prevent timing attacks.\n\tif subtle.ConstantTimeCompare(hash, otherHash) == 1 {\n\t\treturn nil\n\t}\n\treturn errors.WithStack(ErrMismatchedHashAndPassword)\n}\n\nfunc ComparePbkdf2(_ context.Context, password []byte, hash []byte) error {\n\t// Extract the parameters, salt and derived key from the encoded password\n\t// hash.\n\tp, salt, hash, err := decodePbkdf2Hash(string(hash))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Derive the key from the other password using the same parameters.\n\totherHash := pbkdf2.Key(password, salt, int(p.Iterations), int(p.KeyLength), getPseudorandomFunctionForPbkdf2(p.Algorithm))\n\n\t// Check that the contents of the hashed passwords are identical. Note\n\t// that we are using the subtle.ConstantTimeCompare() function for this\n\t// to help prevent timing attacks.\n\tif subtle.ConstantTimeCompare(hash, otherHash) == 1 {\n\t\treturn nil\n\t}\n\treturn errors.WithStack(ErrMismatchedHashAndPassword)\n}\n\nvar (\n\tisBcryptHash   = regexp.MustCompile(`^\\$2[abzy]?\\$`)\n\tisArgon2idHash = regexp.MustCompile(`^\\$argon2id\\$`)\n\tisArgon2iHash  = regexp.MustCompile(`^\\$argon2i\\$`)\n\tisPbkdf2Hash   = regexp.MustCompile(`^\\$pbkdf2-sha[0-9]{1,3}\\$`)\n)\n\nfunc IsBcryptHash(hash []byte) bool {\n\treturn isBcryptHash.Match(hash)\n}\n\nfunc IsArgon2idHash(hash []byte) bool {\n\treturn isArgon2idHash.Match(hash)\n}\n\nfunc IsArgon2iHash(hash []byte) bool {\n\treturn isArgon2iHash.Match(hash)\n}\n\nfunc IsPbkdf2Hash(hash []byte) bool {\n\treturn isPbkdf2Hash.Match(hash)\n}\n\nfunc decodeArgon2idHash(encodedHash string) (p *Argon2Config, salt, hash []byte, err error) {\n\tparts := strings.Split(encodedHash, \"$\")\n\tif len(parts) != 6 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\n\tvar version int\n\t_, err = fmt.Sscanf(parts[2], \"v=%d\", &version)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tif version != argon2.Version {\n\t\treturn nil, nil, nil, ErrIncompatibleVersion\n\t}\n\n\tp = new(Argon2Config)\n\t_, err = fmt.Sscanf(parts[3], \"m=%d,t=%d,p=%d\", &p.Memory, &p.Iterations, &p.Parallelism)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tsalt, err = base64.RawStdEncoding.Strict().DecodeString(parts[4])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tsaltLength := uint(len(salt))\n\tif saltLength > math.MaxUint32 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\tp.SaltLength = uint32(saltLength)\n\n\thash, err = base64.RawStdEncoding.Strict().DecodeString(parts[5])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tkeyLength := uint(len(hash))\n\tif keyLength > math.MaxUint32 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\tp.KeyLength = uint32(keyLength)\n\n\treturn p, salt, hash, nil\n}\n\n// decodePbkdf2Hash decodes PBKDF2 encoded password hash.\n// format: $pbkdf2-<digest>$i=<iterations>,l=<length>$<salt>$<hash>\nfunc decodePbkdf2Hash(encodedHash string) (p *PBKDF2Config, salt, hash []byte, err error) {\n\tparts := strings.Split(encodedHash, \"$\")\n\tif len(parts) != 5 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\n\tp = new(PBKDF2Config)\n\tdigestParts := strings.SplitN(parts[1], \"-\", 2)\n\tif len(digestParts) != 2 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\tp.Algorithm = digestParts[1]\n\n\t_, err = fmt.Sscanf(parts[2], \"i=%d,l=%d\", &p.Iterations, &p.KeyLength)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tsalt, err = base64.RawStdEncoding.Strict().DecodeString(parts[3])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tsaltLength := uint(len(salt))\n\tif saltLength > math.MaxUint32 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\tp.SaltLength = uint32(saltLength)\n\n\thash, err = base64.RawStdEncoding.Strict().DecodeString(parts[4])\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tkeyLength := uint(len(hash))\n\tif keyLength > math.MaxUint32 {\n\t\treturn nil, nil, nil, ErrInvalidHash\n\t}\n\tp.KeyLength = uint32(keyLength)\n\n\treturn p, salt, hash, nil\n}\n"
  },
  {
    "path": "oryx/hasherx/hasher.go",
    "content": "package hasherx\n\nimport (\n\t\"context\"\n)\n\n// Hasher provides methods for generating and comparing password hashes.\ntype Hasher interface {\n\t// Generate returns a hash derived from the password or an error if the hash method failed.\n\tGenerate(ctx context.Context, password []byte) ([]byte, error)\n\n\t// Understands returns whether the given hash can be understood by this hasher.\n\tUnderstands(hash []byte) bool\n}\n\ntype HashProvider interface {\n\tHasher() Hasher\n}\n\nconst tracingComponent = \"github.com/ory/kratos/hash\"\n"
  },
  {
    "path": "oryx/hasherx/hasher_argon2.go",
    "content": "package hasherx\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/inhies/go-bytesize\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/codes\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/crypto/argon2\"\n)\n\nvar (\n\tErrInvalidHash               = errors.New(\"the encoded hash is not in the correct format\")\n\tErrIncompatibleVersion       = errors.New(\"incompatible version of argon2\")\n\tErrMismatchedHashAndPassword = errors.New(\"passwords do not match\")\n)\n\ntype (\n\t// Argon2Config is the configuration for a Argon2 hasher.\n\tArgon2Config struct {\n\t\t// Memory is the amount of memory to use.\n\t\tMemory bytesize.ByteSize `json:\"memory\"`\n\n\t\t// Iterations is the number of iterations to use.\n\t\tIterations uint32 `json:\"iterations\"`\n\n\t\t// Parallelism is the number of threads to use.\n\t\tParallelism uint8 `json:\"parallelism\"`\n\n\t\t// SaltLength is the length of the salt to use.\n\t\tSaltLength uint32 `json:\"salt_length\"`\n\n\t\t// KeyLength is the length of the key to use.\n\t\tKeyLength uint32 `json:\"key_length\"`\n\n\t\t// ExpectedDuration is the expected duration of the hash.\n\t\tExpectedDuration time.Duration `json:\"expected_duration\"`\n\n\t\t// ExpectedDeviation is the expected deviation of the hash.\n\t\tExpectedDeviation time.Duration `json:\"expected_deviation\"`\n\n\t\t// DedicatedMemory is the amount of dedicated memory to use.\n\t\tDedicatedMemory bytesize.ByteSize `json:\"dedicated_memory\"`\n\t}\n\t// Argon2 is a hasher that uses the Argon2 algorithm.\n\tArgon2 struct {\n\t\tc Argon2Configurator\n\t}\n\t// Argon2Configurator is a function that returns the Argon2 configuration.\n\tArgon2Configurator interface {\n\t\tHasherArgon2Config(ctx context.Context) *Argon2Config\n\t}\n)\n\nfunc NewHasherArgon2(c Argon2Configurator) *Argon2 {\n\treturn &Argon2{c: c}\n}\n\nfunc toKB(mem bytesize.ByteSize) (uint32, error) {\n\tkb := uint64(mem / bytesize.KB)\n\tif kb > math.MaxUint32 {\n\t\treturn 0, errors.Errorf(\"memory %v is too large\", mem)\n\t}\n\treturn uint32(kb), nil\n}\n\n// Generate generates a hash for the given password.\nfunc (h *Argon2) Generate(ctx context.Context, password []byte) (_ []byte, err error) {\n\tctx, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, \"hash.Argon2.Generate\")\n\tdefer otelx.End(span, &err)\n\tp := h.c.HasherArgon2Config(ctx)\n\tspan.SetAttributes(attribute.String(\"argon2.config\", fmt.Sprintf(\"#%v\", p)))\n\n\tsalt := make([]byte, p.SaltLength)\n\tif _, err := rand.Read(salt); err != nil {\n\t\treturn nil, err\n\t}\n\n\tmem, err := toKB(p.Memory)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Pass the plaintext password, salt and parameters to the argon2.IDKey\n\t// function. This will generate a hash of the password using the Argon2id\n\t// variant.\n\thash := argon2.IDKey(password, salt, p.Iterations, mem, p.Parallelism, p.KeyLength)\n\n\tvar b bytes.Buffer\n\tif _, err := fmt.Fprintf(\n\t\t&b,\n\t\t\"$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s\",\n\t\targon2.Version, mem, p.Iterations, p.Parallelism,\n\t\tbase64.RawStdEncoding.EncodeToString(salt),\n\t\tbase64.RawStdEncoding.EncodeToString(hash),\n\t); err != nil {\n\t\tspan.RecordError(err)\n\t\tspan.SetStatus(codes.Error, err.Error())\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn b.Bytes(), nil\n}\n\n// Understands checks if the given hash is in the correct format.\nfunc (h *Argon2) Understands(hash []byte) bool {\n\treturn IsArgon2idHash(hash)\n}\n"
  },
  {
    "path": "oryx/hasherx/hasher_bcrypt.go",
    "content": "package hasherx\n\nimport (\n\t\"context\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"golang.org/x/crypto/bcrypt\"\n\n\t\"github.com/ory/x/otelx\"\n)\n\n// ErrBcryptPasswordLengthReached is returned when the password is longer than 72 bytes.\nvar ErrBcryptPasswordLengthReached = errors.Errorf(\"passwords are limited to a maximum length of 72 characters\")\n\ntype (\n\t// Bcrypt is a hasher that uses the bcrypt algorithm.\n\tBcrypt struct {\n\t\tc BCryptConfigurator\n\t}\n\t// BCryptConfig is the configuration for the bcrypt hasher.\n\tBCryptConfig struct {\n\t\tCost uint32 `json:\"cost\"`\n\t}\n\t// BCryptConfigurator is the interface that must be implemented by a configuration provider for the bcrypt hasher.\n\tBCryptConfigurator interface {\n\t\tHasherBcryptConfig(ctx context.Context) *BCryptConfig\n\t}\n)\n\nfunc NewHasherBcrypt(c BCryptConfigurator) *Bcrypt {\n\treturn &Bcrypt{c: c}\n}\n\n// Generate generates a hash for the given password.\nfunc (h *Bcrypt) Generate(ctx context.Context, password []byte) (hash []byte, err error) {\n\tctx, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, \"hash.Bcrypt.Generate\")\n\tdefer otelx.End(span, &err)\n\n\tif err := validateBcryptPasswordLength(password); err != nil {\n\t\treturn nil, err\n\t}\n\n\tcost := int(h.c.HasherBcryptConfig(ctx).Cost)\n\tspan.SetAttributes(attribute.Int(\"bcrypt.cost\", cost))\n\thash, err = bcrypt.GenerateFromPassword(password, cost)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn hash, nil\n}\n\nfunc validateBcryptPasswordLength(password []byte) error {\n\t// Bcrypt truncates the password to the first 72 bytes, following the OpenBSD implementation,\n\t// so if password is longer than 72 bytes, function returns an error\n\t// See https://en.wikipedia.org/wiki/Bcrypt#User_input\n\tif len(password) > 72 {\n\t\treturn ErrBcryptPasswordLengthReached\n\t}\n\treturn nil\n}\n\n// Understands checks if the given hash is in the correct format.\nfunc (h *Bcrypt) Understands(hash []byte) bool {\n\treturn IsBcryptHash(hash)\n}\n"
  },
  {
    "path": "oryx/hasherx/hasher_pbkdf2.go",
    "content": "package hasherx\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/sha1\" // #nosec G505 - compatibility for imported passwords\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"hash\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/codes\"\n\t\"golang.org/x/crypto/pbkdf2\"\n\t\"golang.org/x/crypto/sha3\"\n\n\t\"github.com/ory/x/otelx\"\n)\n\ntype (\n\t// PBKDF2 is a PBKDF2 hasher.\n\tPBKDF2 struct {\n\t\tc PBKDF2Configurator\n\t}\n\n\t// PBKDF2Config is the configuration for a PBKDF2 hasher.\n\tPBKDF2Config struct {\n\t\t// Algorithm can be one of sha1, sha224, sha256, sha384, sha512\n\t\tAlgorithm string\n\t\t// Iterations is the number of iterations to use.\n\t\tIterations uint32\n\t\t// KeyLength is the length of the salt.\n\t\tSaltLength uint32\n\t\t// KeyLength is the length of the key.\n\t\tKeyLength uint32\n\t}\n\n\t// PBKDF2Configurator is a configurator for a PBKDF2 hasher.\n\tPBKDF2Configurator interface {\n\t\tHasherPBKDF2Config(ctx context.Context) *PBKDF2Config\n\t}\n)\n\n// NewHasherPBKDF2 creates a new PBKDF2 hasher.\nfunc NewHasherPBKDF2(c PBKDF2Configurator) *PBKDF2 {\n\treturn &PBKDF2{c: c}\n}\n\n// Generate generates a hash for the given password.\nfunc (h *PBKDF2) Generate(ctx context.Context, password []byte) (hash []byte, err error) {\n\tctx, span := otel.GetTracerProvider().Tracer(\"\").Start(ctx, \"hash.PBKDF2.Generate\")\n\tdefer otelx.End(span, &err)\n\n\tconf := h.c.HasherPBKDF2Config(ctx)\n\tsalt := make([]byte, conf.SaltLength)\n\tif _, err := rand.Read(salt); err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey := pbkdf2.Key(password, salt, int(conf.Iterations), int(conf.KeyLength), getPseudorandomFunctionForPbkdf2(conf.Algorithm))\n\n\tvar b bytes.Buffer\n\tif _, err := fmt.Fprintf(\n\t\t&b,\n\t\t\"$pbkdf2-%s$i=%d,l=%d$%s$%s\",\n\t\tconf.Algorithm,\n\t\tconf.Iterations,\n\t\tconf.KeyLength,\n\t\tbase64.RawStdEncoding.EncodeToString(salt),\n\t\tbase64.RawStdEncoding.EncodeToString(key),\n\t); err != nil {\n\t\tspan.RecordError(err)\n\t\tspan.SetStatus(codes.Error, err.Error())\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn b.Bytes(), nil\n}\n\n// Understands checks if the given hash is in the correct format.\nfunc (h *PBKDF2) Understands(hash []byte) bool {\n\treturn IsPbkdf2Hash(hash)\n}\n\nfunc getPseudorandomFunctionForPbkdf2(alg string) func() hash.Hash {\n\tswitch alg {\n\tcase \"sha1\":\n\t\treturn sha1.New\n\tcase \"sha224\":\n\t\treturn sha3.New224\n\tcase \"sha256\":\n\t\treturn sha256.New\n\tcase \"sha384\":\n\t\treturn sha3.New384\n\tcase \"sha512\":\n\t\treturn sha512.New\n\tdefault:\n\t\treturn sha256.New\n\t}\n}\n"
  },
  {
    "path": "oryx/healthx/doc.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// Package healthx providers helpers for returning health status information via HTTP.\npackage healthx\n\nimport \"strings\"\n\n// The health status of the service.\n//\n// swagger:model healthStatus\ntype swaggerHealthStatus struct {\n\t// Status always contains \"ok\".\n\tStatus string `json:\"status\"`\n}\n\n// The not ready status of the service.\n//\n// swagger:model healthNotReadyStatus\ntype swaggerNotReadyStatus struct {\n\t// Errors contains a list of errors that caused the not ready status.\n\tErrors map[string]string `json:\"errors\"`\n}\n\nfunc (s swaggerNotReadyStatus) Error() string {\n\tvar errs []string\n\tfor _, err := range s.Errors {\n\t\terrs = append(errs, err)\n\t}\n\treturn strings.Join(errs, \"; \")\n}\n\n// swagger:model version\ntype swaggerVersion struct {\n\t// Version is the service's version.\n\tVersion string `json:\"version\"`\n}\n"
  },
  {
    "path": "oryx/healthx/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage healthx\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ory/herodot\"\n)\n\nconst (\n\t// AliveCheckPath is the path where information about the life state of the instance is provided.\n\tAliveCheckPath = \"/health/alive\"\n\t// ReadyCheckPath is the path where information about the ready state of the instance is provided.\n\tReadyCheckPath = \"/health/ready\"\n\t// VersionPath is the path where information about the software version of the instance is provided.\n\tVersionPath = \"/version\"\n)\n\n// RoutesToObserve returns a string of all the available routes of this module.\nfunc RoutesToObserve() []string {\n\treturn []string{\n\t\tAliveCheckPath,\n\t\tReadyCheckPath,\n\t\tVersionPath,\n\t}\n}\n\n// ReadyChecker should return an error if the component is not ready yet.\ntype ReadyChecker func(r *http.Request) error\n\n// ReadyCheckers is a map of ReadyCheckers.\ntype ReadyCheckers map[string]ReadyChecker\n\n// NoopReadyChecker is always ready.\nfunc NoopReadyChecker() error {\n\treturn nil\n}\n\n// Handler handles HTTP requests to health and version endpoints.\ntype Handler struct {\n\tH             herodot.Writer\n\tVersionString string\n\tReadyChecks   ReadyCheckers\n}\n\ntype options struct {\n\tmiddleware func(http.Handler) http.Handler\n}\n\ntype Options func(*options)\n\n// NewHandler instantiates a handler.\nfunc NewHandler(\n\th herodot.Writer,\n\tversion string,\n\treadyChecks ReadyCheckers,\n) *Handler {\n\treturn &Handler{\n\t\tH:             h,\n\t\tVersionString: version,\n\t\tReadyChecks:   readyChecks,\n\t}\n}\n\ntype router interface {\n\tHandler(method, path string, handler http.Handler)\n}\n\n// SetHealthRoutes registers this handler's routes for health checking.\nfunc (h *Handler) SetHealthRoutes(r router, shareErrors bool, opts ...Options) {\n\to := &options{}\n\taliveHandler := h.Alive()\n\treadyHandler := h.Ready(shareErrors)\n\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\n\tif o.middleware != nil {\n\t\taliveHandler = o.middleware(aliveHandler)\n\t\treadyHandler = o.middleware(readyHandler)\n\t}\n\n\tr.Handler(\"GET\", AliveCheckPath, aliveHandler)\n\tr.Handler(\"GET\", ReadyCheckPath, readyHandler)\n}\n\n// SetVersionRoutes registers this handler's routes for health checking.\nfunc (h *Handler) SetVersionRoutes(r router, opts ...Options) {\n\to := &options{}\n\tversionHandler := h.Version()\n\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\n\tif o.middleware != nil {\n\t\tversionHandler = o.middleware(versionHandler)\n\t}\n\n\tr.Handler(\"GET\", VersionPath, versionHandler)\n}\n\n// Alive returns an ok status if the instance is ready to handle HTTP requests.\n//\n// swagger:route GET /health/alive health isInstanceAlive\n//\n// # Check alive status\n//\n// This endpoint returns a 200 status code when the HTTP server is up running.\n// This status does currently not include checks whether the database connection is working.\n//\n// If the service supports TLS Edge Termination, this endpoint does not require the\n// `X-Forwarded-Proto` header to be set.\n//\n// Be aware that if you are running multiple nodes of this service, the health status will never\n// refer to the cluster state, only to a single instance.\n//\n//\tProduces:\n//\t- application/json\n//\t- text/plain\n//\n//\tResponses:\n//\t  200: healthStatus\n//\t  default: unexpectedError\nfunc (h *Handler) Alive() http.Handler {\n\treturn http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {\n\t\th.H.Write(rw, r, &swaggerHealthStatus{\n\t\t\tStatus: \"ok\",\n\t\t})\n\t})\n}\n\n// swagger:model unexpectedError\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype unexpectedError string\n\n// Ready returns an ok status if the instance is ready to handle HTTP requests and all ReadyCheckers are ok.\n//\n// swagger:route GET /health/ready health isInstanceReady\n//\n// # Check readiness status\n//\n// This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g.\n// the database) are responsive as well.\n//\n// If the service supports TLS Edge Termination, this endpoint does not require the\n// `X-Forwarded-Proto` header to be set.\n//\n// Be aware that if you are running multiple nodes of this service, the health status will never\n// refer to the cluster state, only to a single instance.\n//\n//\tProduces:\n//\t- application/json\n//\t- text/plain\n//\n//\tResponses:\n//\t  200: healthStatus\n//\t  503: healthNotReadyStatus\n//\t  default: unexpectedError\nfunc (h *Handler) Ready(shareErrors bool) http.Handler {\n\treturn http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {\n\t\tvar notReady = swaggerNotReadyStatus{\n\t\t\tErrors: map[string]string{},\n\t\t}\n\n\t\tfor n, c := range h.ReadyChecks {\n\t\t\tif err := c(r); err != nil {\n\t\t\t\tif shareErrors {\n\t\t\t\t\tnotReady.Errors[n] = err.Error()\n\t\t\t\t} else {\n\t\t\t\t\tnotReady.Errors[n] = \"error may contain sensitive information and was obfuscated\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif len(notReady.Errors) > 0 {\n\t\t\th.H.WriteErrorCode(rw, r, http.StatusServiceUnavailable, &notReady)\n\t\t\treturn\n\t\t}\n\n\t\th.H.Write(rw, r, &swaggerHealthStatus{\n\t\t\tStatus: \"ok\",\n\t\t})\n\t})\n}\n\n// Version returns this service's versions.\n//\n// swagger:route GET /version version getVersion\n//\n// # Get service version\n//\n// This endpoint returns the service version typically notated using semantic versioning.\n//\n// If the service supports TLS Edge Termination, this endpoint does not require the\n// `X-Forwarded-Proto` header to be set.\n//\n// Be aware that if you are running multiple nodes of this service, the health status will never\n// refer to the cluster state, only to a single instance.\n//\n//\t    Produces:\n//\t    - application/json\n//\n//\t\t   Responses:\n//\t\t\t\t200: version\nfunc (h *Handler) Version() http.Handler {\n\treturn http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {\n\t\th.H.Write(rw, r, &swaggerVersion{\n\t\t\tVersion: h.VersionString,\n\t\t})\n\t})\n}\n\n// WithMiddleware accepts a http.Handler to be run on the\n// route handlers\nfunc WithMiddleware(h func(http.Handler) http.Handler) Options {\n\treturn func(o *options) {\n\t\to.middleware = h\n\t}\n}\n"
  },
  {
    "path": "oryx/healthx/openapi/patch.yaml",
    "content": "- op: replace\n  path: /paths/~1health~1alive\n  value:\n    get:\n      description: |-\n        This endpoint returns a HTTP 200 status code when {{.ProjectHumanName}} is accepting incoming\n        HTTP requests. This status does currently not include checks whether the database connection is working.\n\n        If the service supports TLS Edge Termination, this endpoint does not require the\n        `X-Forwarded-Proto` header to be set.\n\n        Be aware that if you are running multiple nodes of this service, the health status will never\n        refer to the cluster state, only to a single instance.\n      operationId: isAlive\n      responses:\n        '200':\n          content:\n            application/json:\n              schema:\n                required:\n                  - status\n                type: object\n                properties:\n                  status:\n                    description: Always \"ok\".\n                    type: string\n          description: '{{.ProjectHumanName}} is ready to accept connections.'\n        default:\n          content:\n            text/plain:\n              schema:\n                type: string\n          description: Unexpected error\n      summary: Check HTTP Server Status\n      tags: {{ .HealthPathTags | toJson }}\n- op: replace\n  path: /paths/~1health~1ready\n  value:\n    get:\n      operationId: isReady\n      description: |-\n        This endpoint returns a HTTP 200 status code when {{.ProjectHumanName}} is up running and the environment dependencies (e.g.\n        the database) are responsive as well.\n\n        If the service supports TLS Edge Termination, this endpoint does not require the\n        `X-Forwarded-Proto` header to be set.\n\n        Be aware that if you are running multiple nodes of {{.ProjectHumanName}}, the health status will never\n        refer to the cluster state, only to a single instance.\n      responses:\n        '200':\n          content:\n            application/json:\n              schema:\n                required:\n                  - status\n                type: object\n                properties:\n                  status:\n                    description: Always \"ok\".\n                    type: string\n          description: '{{.ProjectHumanName}} is ready to accept requests.'\n        '503':\n          content:\n            application/json:\n              schema:\n                required:\n                  - errors\n                properties:\n                  errors:\n                    additionalProperties:\n                      type: string\n                    description: Errors contains a list of errors that caused the not ready status.\n                    type: object\n                type: object\n          description: Ory Kratos is not yet ready to accept requests.\n        default:\n          content:\n            text/plain:\n              schema:\n                type: string\n          description: Unexpected error\n      summary: Check HTTP Server and Database Status\n      tags: {{ .HealthPathTags | toJson }}\n- op: replace\n  path: /paths/~1version\n  value:\n    get:\n      description: |-\n        This endpoint returns the version of {{.ProjectHumanName}}.\n\n        If the service supports TLS Edge Termination, this endpoint does not require the\n        `X-Forwarded-Proto` header to be set.\n\n        Be aware that if you are running multiple nodes of this service, the version will never\n        refer to the cluster state, only to a single instance.\n      operationId: getVersion\n      responses:\n        '200':\n          content:\n            application/json:\n              schema:\n                type: object\n                required:\n                  - version\n                properties:\n                  version:\n                    description: The version of {{.ProjectHumanName}}.\n                    type: string\n          description: Returns the {{.ProjectHumanName}} version.\n      summary: Return Running Software Version.\n      tags: {{ .HealthPathTags | toJson }}\n"
  },
  {
    "path": "oryx/httprouterx/router.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httprouterx\n\nimport (\n\t\"net/http\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/ory/x/prometheusx\"\n)\n\nconst AdminPrefix = \"/admin\"\n\ntype (\n\trouter struct {\n\t\tmux     *http.ServeMux\n\t\tprefix  string\n\t\tmetrics *prometheusx.HTTPMetrics\n\t}\n\tRouterAdmin  struct{ router }\n\tRouterPublic struct{ router }\n\n\tRouter interface {\n\t\thttp.Handler\n\t\tGET(route string, handle http.HandlerFunc)\n\t\tHEAD(route string, handle http.HandlerFunc)\n\t\tPOST(route string, handle http.HandlerFunc)\n\t\tPUT(route string, handle http.HandlerFunc)\n\t\tPATCH(route string, handle http.HandlerFunc)\n\t\tDELETE(route string, handle http.HandlerFunc)\n\t\tHandler(method, route string, handler http.Handler)\n\t}\n)\n\nfunc newRouter(metrics *prometheusx.HTTPMetrics) *router {\n\treturn &router{\n\t\tmux:     http.NewServeMux(),\n\t\tmetrics: metrics,\n\t}\n}\n\n// NewRouter creates a new general purpose router. It should only be used when neither the admin nor the public router is applicable.\nfunc NewRouter(metrics *prometheusx.HTTPMetrics) Router { return newRouter(metrics) }\n\n// NewRouterAdmin creates a new admin router.\nfunc NewRouterAdmin(metrics *prometheusx.HTTPMetrics) *RouterAdmin {\n\treturn &RouterAdmin{router: *newRouter(metrics)}\n}\n\nfunc NewTestRouterAdmin(_ testing.TB) *RouterAdmin           { return NewRouterAdmin(nil) }\nfunc NewTestRouterAdminWithPrefix(_ testing.TB) *RouterAdmin { return NewRouterAdminWithPrefix(nil) }\nfunc NewTestRouterPublic(_ testing.TB) *RouterPublic         { return NewRouterPublic(nil) }\n\nfunc (r *RouterAdmin) ToPublic() *RouterPublic {\n\treturn &RouterPublic{router: router{\n\t\tmux:     r.mux,\n\t\tmetrics: r.metrics,\n\t\tprefix:  \"\", // do not copy the admin prefix\n\t}}\n}\n\n// NewRouterPublic returns a public router.\nfunc NewRouterPublic(metrics *prometheusx.HTTPMetrics) *RouterPublic {\n\treturn &RouterPublic{router: *newRouter(metrics)}\n}\n\n// NewRouterAdminWithPrefix creates a new router with the admin prefix.\nfunc NewRouterAdminWithPrefix(metricsManager *prometheusx.HTTPMetrics) *RouterAdmin {\n\tr := NewRouterAdmin(metricsManager)\n\tr.prefix = AdminPrefix\n\treturn r\n}\n\nfunc (r *router) GET(route string, handle http.HandlerFunc) {\n\tr.handle(http.MethodGet, route, handle)\n}\n\nfunc (r *router) HEAD(route string, handle http.HandlerFunc) {\n\tr.handle(http.MethodHead, route, handle)\n}\n\nfunc (r *router) POST(route string, handle http.HandlerFunc) {\n\tr.handle(http.MethodPost, route, handle)\n}\n\nfunc (r *router) PUT(route string, handle http.HandlerFunc) {\n\tr.handle(http.MethodPut, route, handle)\n}\n\nfunc (r *router) PATCH(route string, handle http.HandlerFunc) {\n\tr.handle(http.MethodPatch, route, handle)\n}\n\nfunc (r *router) DELETE(route string, handle http.HandlerFunc) {\n\tr.handle(http.MethodDelete, route, handle)\n}\n\nfunc (r *router) Handler(method, route string, handler http.Handler) {\n\tr.handle(method, route, handler)\n}\n\nfunc (r *router) handle(method string, route string, handler http.Handler) {\n\troute = path.Join(r.prefix, route)\n\tif r.metrics != nil {\n\t\thandler = r.metrics.Instrument(handler, prometheusx.GetLabelForPattern(route))\n\t}\n\tr.mux.Handle(method+\" \"+route, handler)\n}\n\nfunc (r *router) ServeHTTP(w http.ResponseWriter, req *http.Request) { r.mux.ServeHTTP(w, req) }\n\nfunc TrimTrailingSlashNegroni(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tr.URL.Path = strings.TrimSuffix(r.URL.Path, \"/\")\n\n\tnext(rw, r)\n}\n\nfunc NoCacheNegroni(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tif r.Method == \"GET\" {\n\t\trw.Header().Set(\"Cache-Control\", \"private, no-cache, no-store, must-revalidate\")\n\t}\n\n\tnext(rw, r)\n}\n\nfunc AddAdminPrefixIfNotPresentNegroni(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tif !strings.HasPrefix(r.URL.Path, AdminPrefix) {\n\t\tr.URL.Path = path.Join(AdminPrefix, r.URL.Path)\n\t}\n\n\tnext(rw, r)\n}\n"
  },
  {
    "path": "oryx/httpx/assert.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"net/http\"\n)\n\nfunc GetResponseMeta(w http.ResponseWriter) (status, size int) {\n\tswitch t := w.(type) {\n\tcase interface{ Status() int }:\n\t\tstatus = t.Status()\n\t}\n\n\tswitch t := w.(type) {\n\tcase interface{ Size() int }:\n\t\tsize = t.Size()\n\tcase interface{ Written() int64 }:\n\t\tsize = int(t.Written())\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "oryx/httpx/chan_handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport \"net/http\"\n\ntype chanHandler <-chan http.HandlerFunc\n\nvar _ http.Handler = chanHandler(nil)\n\nfunc (c chanHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\t(<-c)(w, r)\n}\n\n// NewChanHandler returns a new handler and corresponding channel for sending handler funcs.\n// Useful for testing. The argument buf specifies the channel capacity, so pass 0 for a sync handler.\nfunc NewChanHandler(buf int) (http.Handler, chan<- http.HandlerFunc) {\n\tc := make(chan http.HandlerFunc, buf)\n\treturn chanHandler(c), c\n}\n"
  },
  {
    "path": "oryx/httpx/client_info.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype GeoLocation struct {\n\tCity      string\n\tRegion    string\n\tCountry   string\n\tLatitude  *float64\n\tLongitude *float64\n}\n\nfunc GetClientIPAddressesWithoutInternalIPs(ipAddresses []string) (string, error) {\n\tvar res string\n\n\tfor i := len(ipAddresses) - 1; i >= 0; i-- {\n\t\tip := strings.TrimSpace(ipAddresses[i])\n\n\t\tif !net.ParseIP(ip).IsPrivate() {\n\t\t\tres = ip\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn res, nil\n}\n\nfunc ClientIP(r *http.Request) string {\n\tif trueClientIP := r.Header.Get(\"True-Client-IP\"); trueClientIP != \"\" {\n\t\treturn trueClientIP\n\t} else if cfConnectingIP := r.Header.Get(\"Cf-Connecting-IP\"); cfConnectingIP != \"\" {\n\t\treturn cfConnectingIP\n\t} else if realClientIP := r.Header.Get(\"X-Real-IP\"); realClientIP != \"\" {\n\t\treturn realClientIP\n\t} else if forwardedIP := r.Header.Get(\"X-Forwarded-For\"); forwardedIP != \"\" {\n\t\tip, _ := GetClientIPAddressesWithoutInternalIPs(strings.Split(forwardedIP, \",\"))\n\t\treturn ip\n\t} else {\n\t\treturn r.RemoteAddr\n\t}\n}\n\nfunc parseFloatHeaderValue(headerValue string) *float64 {\n\tif headerValue == \"\" {\n\t\treturn nil\n\t}\n\n\tval, err := strconv.ParseFloat(headerValue, 64)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\treturn &val\n}\n\nfunc ClientGeoLocation(r *http.Request) *GeoLocation {\n\treturn &GeoLocation{\n\t\tCity:      r.Header.Get(\"Cf-Ipcity\"),\n\t\tRegion:    r.Header.Get(\"Cf-Region-Code\"),\n\t\tCountry:   r.Header.Get(\"Cf-Ipcountry\"),\n\t\tLongitude: parseFloatHeaderValue(r.Header.Get(\"Cf-Iplongitude\")),\n\t\tLatitude:  parseFloatHeaderValue(r.Header.Get(\"Cf-Iplatitude\")),\n\t}\n}\n"
  },
  {
    "path": "oryx/httpx/content_type.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"mime\"\n\t\"net/http\"\n\t\"slices\"\n\t\"strings\"\n)\n\n// HasContentType determines whether the request `content-type` includes a\n// server-acceptable mime-type\n//\n// Failure should yield an HTTP 415 (`http.StatusUnsupportedMediaType`)\nfunc HasContentType(r *http.Request, mimetypes ...string) bool {\n\tcontentType := r.Header.Get(\"Content-Type\")\n\tif contentType == \"\" {\n\t\treturn slices.Contains(mimetypes, \"application/octet-stream\")\n\t}\n\n\tmediaType, _, err := mime.ParseMediaType(strings.TrimSpace(contentType))\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn slices.Contains(mimetypes, mediaType)\n}\n"
  },
  {
    "path": "oryx/httpx/gzip_server.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\ntype CompressionRequestReader struct {\n\tErrHandler func(w http.ResponseWriter, r *http.Request, err error)\n}\n\nfunc defaultCompressionErrorHandler(w http.ResponseWriter, r *http.Request, err error) {\n\thttp.Error(w, err.Error(), http.StatusBadRequest)\n}\n\nfunc NewCompressionRequestReader(eh func(w http.ResponseWriter, r *http.Request, err error)) *CompressionRequestReader {\n\tif eh == nil {\n\t\teh = defaultCompressionErrorHandler\n\t}\n\n\treturn &CompressionRequestReader{\n\t\tErrHandler: eh,\n\t}\n}\n\nfunc (c *CompressionRequestReader) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tfor _, enc := range strings.Split(r.Header.Get(\"Content-Encoding\"), \",\") {\n\t\tswitch enc = strings.TrimSpace(enc); enc {\n\t\tcase \"gzip\":\n\t\t\treader, err := gzip.NewReader(r.Body)\n\t\t\tif err != nil {\n\t\t\t\tc.ErrHandler(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tr.Body = io.NopCloser(reader)\n\t\tcase \"identity\", \"\":\n\t\t\t// nothing to do\n\t\tdefault:\n\t\t\tc.ErrHandler(w, r, fmt.Errorf(\"%s content encoding not supported\", enc))\n\t\t}\n\t}\n\n\tnext(w, r)\n}\n"
  },
  {
    "path": "oryx/httpx/private_ip_validator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"net/url\"\n\n\t\"code.dny.dev/ssrf\"\n\t\"github.com/pkg/errors\"\n)\n\n// ErrPrivateIPAddressDisallowed is returned when a private IP address is disallowed.\ntype ErrPrivateIPAddressDisallowed error\n\n// DisallowIPPrivateAddresses returns nil for a domain (with NS lookup), IP, or IPv6 address if it\n// does not resolve to a private IP subnet. This is a first level of defense against\n// SSRF attacks by disallowing any domain or IP to resolve to a private network range.\n//\n// Please keep in mind that validations for domains is valid only when looking up.\n// A malicious actor could easily update the DSN record post validation to point\n// to an internal IP\nfunc DisallowIPPrivateAddresses(ipOrHostnameOrURL string) error {\n\tlookup := func(hostname string) ([]net.IP, error) {\n\t\tlookup, err := net.LookupIP(hostname)\n\t\tif err != nil {\n\t\t\tif dnsErr := new(net.DNSError); errors.As(err, &dnsErr) && (dnsErr.IsNotFound || dnsErr.IsTemporary) {\n\t\t\t\t// If the hostname does not resolve, we can't validate it. So yeah,\n\t\t\t\t// I guess we're allowing it.\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\treturn lookup, nil\n\t}\n\n\tvar ips []net.IP\n\tip := net.ParseIP(ipOrHostnameOrURL)\n\tif ip == nil {\n\t\tif result, err := lookup(ipOrHostnameOrURL); err != nil {\n\t\t\treturn err\n\t\t} else if result != nil {\n\t\t\tips = append(ips, result...)\n\t\t}\n\n\t\tif parsed, err := url.Parse(ipOrHostnameOrURL); err == nil {\n\t\t\tif result, err := lookup(parsed.Hostname()); err != nil {\n\t\t\t\treturn err\n\t\t\t} else if result != nil {\n\t\t\t\tips = append(ips, result...)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tips = append(ips, ip)\n\t}\n\n\tfor _, ip := range ips {\n\t\tip, err := netip.ParseAddr(ip.String())\n\t\tif err != nil {\n\t\t\treturn ErrPrivateIPAddressDisallowed(errors.WithStack(err)) // should be unreacheable\n\t\t}\n\n\t\tif ip.Is4() {\n\t\t\tfor _, deny := range ssrf.IPv4DeniedPrefixes {\n\t\t\t\tif deny.Contains(ip) {\n\t\t\t\t\treturn ErrPrivateIPAddressDisallowed(fmt.Errorf(\"%s is not a public IP address\", ip))\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif !ssrf.IPv6GlobalUnicast.Contains(ip) {\n\t\t\t\treturn ErrPrivateIPAddressDisallowed(fmt.Errorf(\"%s is not a public IP address\", ip))\n\t\t\t}\n\t\t\tfor _, net := range ssrf.IPv6DeniedPrefixes {\n\t\t\t\tif net.Contains(ip) {\n\t\t\t\t\treturn ErrPrivateIPAddressDisallowed(fmt.Errorf(\"%s is not a public IP address\", ip))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/httpx/request.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// NewRequestJSON returns a new JSON *http.Request.\nfunc NewRequestJSON(method, url string, data interface{}) (*http.Request, error) {\n\tvar b bytes.Buffer\n\tif err := json.NewEncoder(&b).Encode(data); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\treq, err := http.NewRequest(method, url, &b)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treturn req, nil\n}\n\n// NewRequestForm returns a new POST Form *http.Request.\nfunc NewRequestForm(method, url string, data url.Values) (*http.Request, error) {\n\treq, err := http.NewRequest(method, url, strings.NewReader(data.Encode()))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treturn req, nil\n}\n\n// MustNewRequest returns a new *http.Request or fatals.\nfunc MustNewRequest(method, url string, body io.Reader, contentType string) *http.Request {\n\treq, err := http.NewRequest(method, url, body)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tif contentType != \"\" {\n\t\treq.Header.Set(\"Content-Type\", contentType)\n\t}\n\treturn req\n}\n"
  },
  {
    "path": "oryx/httpx/resilient_client.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/logrusx\"\n)\n\ntype resilientOptions struct {\n\tc                    *http.Client\n\tl                    interface{}\n\tretryWaitMin         time.Duration\n\tretryWaitMax         time.Duration\n\tretryMax             int\n\tnoInternalIPs        bool\n\tinternalIPExceptions []string\n}\n\nfunc newResilientOptions() *resilientOptions {\n\treturn &resilientOptions{\n\t\tc:            &http.Client{Timeout: time.Minute},\n\t\tretryWaitMin: 1 * time.Second,\n\t\tretryWaitMax: 30 * time.Second,\n\t\tretryMax:     4,\n\t\tl:            log.New(io.Discard, \"\", log.LstdFlags),\n\t}\n}\n\n// ResilientOptions is a set of options for the ResilientClient.\ntype ResilientOptions func(o *resilientOptions)\n\n// ResilientClientWithMaxRetry sets the maximum number of retries.\nfunc ResilientClientWithMaxRetry(retryMax int) ResilientOptions {\n\treturn func(o *resilientOptions) { o.retryMax = retryMax }\n}\n\n// ResilientClientWithMinxRetryWait sets the minimum wait time between retries.\nfunc ResilientClientWithMinxRetryWait(retryWaitMin time.Duration) ResilientOptions {\n\treturn func(o *resilientOptions) { o.retryWaitMin = retryWaitMin }\n}\n\n// ResilientClientWithMaxRetryWait sets the maximum wait time for a retry.\nfunc ResilientClientWithMaxRetryWait(retryWaitMax time.Duration) ResilientOptions {\n\treturn func(o *resilientOptions) { o.retryWaitMax = retryWaitMax }\n}\n\n// ResilientClientWithConnectionTimeout sets the connection timeout for the client.\nfunc ResilientClientWithConnectionTimeout(connTimeout time.Duration) ResilientOptions {\n\treturn func(o *resilientOptions) { o.c.Timeout = connTimeout }\n}\n\n// ResilientClientWithLogger sets the logger to be used by the client.\nfunc ResilientClientWithLogger(l *logrusx.Logger) ResilientOptions {\n\treturn func(o *resilientOptions) { o.l = l }\n}\n\n// ResilientClientDisallowInternalIPs disallows internal IPs from being used.\nfunc ResilientClientDisallowInternalIPs() ResilientOptions {\n\treturn func(o *resilientOptions) { o.noInternalIPs = true }\n}\n\n// ResilientClientAllowInternalIPRequestsTo allows requests to the glob-matching URLs even\n// if they are internal IPs.\nfunc ResilientClientAllowInternalIPRequestsTo(urlGlobs ...string) ResilientOptions {\n\treturn func(o *resilientOptions) { o.internalIPExceptions = urlGlobs }\n}\n\n// NewResilientClient creates a new ResilientClient.\nfunc NewResilientClient(opts ...ResilientOptions) *retryablehttp.Client {\n\to := newResilientOptions()\n\tfor _, f := range opts {\n\t\tf(o)\n\t}\n\n\tif o.noInternalIPs {\n\t\to.c.Transport = &noInternalIPRoundTripper{\n\t\t\tonWhitelist:          allowInternalAllowIPv6,\n\t\t\tnotOnWhitelist:       prohibitInternalAllowIPv6,\n\t\t\tinternalIPExceptions: o.internalIPExceptions,\n\t\t}\n\t} else {\n\t\to.c.Transport = allowInternalAllowIPv6\n\t}\n\n\tcl := retryablehttp.NewClient()\n\tcl.HTTPClient = o.c\n\tcl.Logger = o.l\n\tcl.RetryWaitMin = o.retryWaitMin\n\tcl.RetryWaitMax = o.retryWaitMax\n\tcl.RetryMax = o.retryMax\n\tcl.CheckRetry = retryablehttp.DefaultRetryPolicy\n\tcl.Backoff = retryablehttp.DefaultBackoff\n\treturn cl\n}\n\n// SetOAuth2 modifies the given client to enable OAuth2 authentication. Requests\n// with the client should always use the returned context.\n//\n//\tclient := http.NewResilientClient(opts...)\n//\tctx, client = httpx.SetOAuth2(ctx, client, oauth2Config, oauth2Token)\n//\treq, err := retryablehttp.NewRequestWithContext(ctx, ...)\n//\tif err != nil { /* ... */ }\n//\tres, err := client.Do(req)\nfunc SetOAuth2(ctx context.Context, cl *retryablehttp.Client, c OAuth2Config, t *oauth2.Token) (context.Context, *retryablehttp.Client) {\n\tctx = context.WithValue(ctx, oauth2.HTTPClient, cl.HTTPClient)\n\tcl.HTTPClient = c.Client(ctx, t)\n\treturn ctx, cl\n}\n\ntype (\n\tOAuth2Config interface {\n\t\tClient(context.Context, *oauth2.Token) *http.Client\n\t}\n\tClientProvider interface {\n\t\tHTTPClient(ctx context.Context, opts ...ResilientOptions) *retryablehttp.Client\n\t}\n\tWriterProvider interface {\n\t\tWriter() herodot.Writer\n\t}\n)\n"
  },
  {
    "path": "oryx/httpx/ssrf.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptrace\"\n\t\"net/netip\"\n\t\"time\"\n\n\t\"code.dny.dev/ssrf\"\n\t\"github.com/gobwas/glob\"\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\"\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n)\n\nvar _ http.RoundTripper = (*noInternalIPRoundTripper)(nil)\n\ntype noInternalIPRoundTripper struct {\n\tonWhitelist, notOnWhitelist http.RoundTripper\n\tinternalIPExceptions        []string\n}\n\n// RoundTrip implements http.RoundTripper.\nfunc (n noInternalIPRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {\n\tincoming := IncomingRequestURL(request)\n\tincoming.RawQuery = \"\"\n\tincoming.RawFragment = \"\"\n\tfor _, exception := range n.internalIPExceptions {\n\t\tcompiled, err := glob.Compile(exception, '.', '/')\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif compiled.Match(incoming.String()) {\n\t\t\treturn n.onWhitelist.RoundTrip(request)\n\t\t}\n\t}\n\n\treturn n.notOnWhitelist.RoundTrip(request)\n}\n\nvar (\n\tprohibitInternalAllowIPv6 http.RoundTripper\n\tallowInternalAllowIPv6    http.RoundTripper\n)\n\nfunc init() {\n\tt, d := newDefaultTransport()\n\td.Control = ssrf.New(\n\t\tssrf.WithAnyPort(),\n\t\tssrf.WithNetworks(\"tcp4\", \"tcp6\"),\n\t).Safe\n\tprohibitInternalAllowIPv6 = OTELTraceTransport(t)\n}\n\nfunc init() {\n\tt, d := newDefaultTransport()\n\td.Control = ssrf.New(\n\t\tssrf.WithAnyPort(),\n\t\tssrf.WithNetworks(\"tcp4\"),\n\t).Safe\n\tt.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\treturn d.DialContext(ctx, \"tcp4\", addr)\n\t}\n}\n\nfunc init() {\n\tt, d := newDefaultTransport()\n\td.Control = ssrf.New(\n\t\tssrf.WithAnyPort(),\n\t\tssrf.WithNetworks(\"tcp4\", \"tcp6\"),\n\t\tssrf.WithAllowedV4Prefixes(\n\t\t\tnetip.MustParsePrefix(\"10.0.0.0/8\"),     // Private-Use (RFC 1918)\n\t\t\tnetip.MustParsePrefix(\"127.0.0.0/8\"),    // Loopback (RFC 1122, Section 3.2.1.3))\n\t\t\tnetip.MustParsePrefix(\"169.254.0.0/16\"), // Link Local (RFC 3927)\n\t\t\tnetip.MustParsePrefix(\"172.16.0.0/12\"),  // Private-Use (RFC 1918)\n\t\t\tnetip.MustParsePrefix(\"192.168.0.0/16\"), // Private-Use (RFC 1918)\n\t\t),\n\t\tssrf.WithAllowedV6Prefixes(\n\t\t\tnetip.MustParsePrefix(\"::1/128\"),  // Loopback (RFC 4193)\n\t\t\tnetip.MustParsePrefix(\"fc00::/7\"), // Unique Local (RFC 4193)\n\t\t),\n\t).Safe\n\tallowInternalAllowIPv6 = OTELTraceTransport(t)\n}\n\nfunc init() {\n\tt, d := newDefaultTransport()\n\td.Control = ssrf.New(\n\t\tssrf.WithAnyPort(),\n\t\tssrf.WithNetworks(\"tcp4\"),\n\t\tssrf.WithAllowedV4Prefixes(\n\t\t\tnetip.MustParsePrefix(\"10.0.0.0/8\"),     // Private-Use (RFC 1918)\n\t\t\tnetip.MustParsePrefix(\"127.0.0.0/8\"),    // Loopback (RFC 1122, Section 3.2.1.3))\n\t\t\tnetip.MustParsePrefix(\"169.254.0.0/16\"), // Link Local (RFC 3927)\n\t\t\tnetip.MustParsePrefix(\"172.16.0.0/12\"),  // Private-Use (RFC 1918)\n\t\t\tnetip.MustParsePrefix(\"192.168.0.0/16\"), // Private-Use (RFC 1918)\n\t\t),\n\t\tssrf.WithAllowedV6Prefixes(\n\t\t\tnetip.MustParsePrefix(\"::1/128\"),  // Loopback (RFC 4193)\n\t\t\tnetip.MustParsePrefix(\"fc00::/7\"), // Unique Local (RFC 4193)\n\t\t),\n\t).Safe\n\tt.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\treturn d.DialContext(ctx, \"tcp4\", addr)\n\t}\n}\n\nfunc newDefaultTransport() (*http.Transport, *net.Dialer) {\n\tdialer := net.Dialer{\n\t\tTimeout:   30 * time.Second,\n\t\tKeepAlive: 30 * time.Second,\n\t}\n\treturn &http.Transport{\n\t\tProxy:                 http.ProxyFromEnvironment,\n\t\tDialContext:           dialer.DialContext,\n\t\tForceAttemptHTTP2:     true,\n\t\tMaxIdleConns:          100,\n\t\tIdleConnTimeout:       90 * time.Second,\n\t\tTLSHandshakeTimeout:   10 * time.Second,\n\t\tExpectContinueTimeout: 1 * time.Second,\n\t}, &dialer\n}\n\n// OTELTraceTransport wraps the given http.Transport with OpenTelemetry instrumentation.\nfunc OTELTraceTransport(t *http.Transport) http.RoundTripper {\n\treturn otelhttp.NewTransport(t, otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace {\n\t\treturn otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutHeaders(), otelhttptrace.WithoutSubSpans())\n\t}))\n}\n"
  },
  {
    "path": "oryx/httpx/transports.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport \"net/http\"\n\n// WrapTransportWithHeader wraps a http.Transport to always use the values from the given header.\nfunc WrapTransportWithHeader(parent http.RoundTripper, h http.Header) *TransportWithHeader {\n\treturn &TransportWithHeader{\n\t\tRoundTripper: parent,\n\t\th:            h,\n\t}\n}\n\n// NewTransportWithHeader returns a new http.Transport that always uses the values from the given header.\nfunc NewTransportWithHeader(h http.Header) *TransportWithHeader {\n\treturn &TransportWithHeader{\n\t\tRoundTripper: http.DefaultTransport,\n\t\th:            h,\n\t}\n}\n\n// TransportWithHeader is an http.RoundTripper that always uses the values from the given header.\ntype TransportWithHeader struct {\n\thttp.RoundTripper\n\th http.Header\n}\n\n// RoundTrip implements http.RoundTripper.\nfunc (ct *TransportWithHeader) RoundTrip(req *http.Request) (*http.Response, error) {\n\tfor k := range ct.h {\n\t\treq.Header.Set(k, ct.h.Get(k))\n\t}\n\treturn ct.RoundTripper.RoundTrip(req)\n}\n\n// NewTransportWithHost returns a new http.Transport that always uses the given host.\nfunc NewTransportWithHost(host string) *TransportWithHost {\n\treturn &TransportWithHost{\n\t\tRoundTripper: http.DefaultTransport,\n\t\thost:         host,\n\t}\n}\n\n// WrapRoundTripperWithHost wraps a http.RoundTripper that always uses the given host.\nfunc WrapRoundTripperWithHost(parent http.RoundTripper, host string) *TransportWithHost {\n\treturn &TransportWithHost{\n\t\tRoundTripper: parent,\n\t\thost:         host,\n\t}\n}\n\n// TransportWithHost is an http.RoundTripper that always uses the given host.\ntype TransportWithHost struct {\n\thttp.RoundTripper\n\thost string\n}\n\n// RoundTrip implements http.RoundTripper.\nfunc (ct *TransportWithHost) RoundTrip(req *http.Request) (*http.Response, error) {\n\treq.Host = ct.host\n\treturn ct.RoundTripper.RoundTrip(req)\n}\n"
  },
  {
    "path": "oryx/httpx/url.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"cmp\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\n// IncomingRequestURL returns the URL of the incoming HTTP request by looking at the host, TLS, and X-Forwarded-* headers.\nfunc IncomingRequestURL(r *http.Request) *url.URL {\n\tsource := *r.URL\n\tsource.Host = cmp.Or(source.Host, r.Header.Get(\"X-Forwarded-Host\"), r.Host)\n\n\tif proto := r.Header.Get(\"X-Forwarded-Proto\"); len(proto) > 0 {\n\t\tsource.Scheme = proto\n\t}\n\n\tif source.Scheme == \"\" {\n\t\tsource.Scheme = \"https\"\n\t\tif r.TLS == nil {\n\t\t\tsource.Scheme = \"http\"\n\t\t}\n\t}\n\n\treturn &source\n}\n"
  },
  {
    "path": "oryx/httpx/wait_for.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage httpx\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/avast/retry-go/v4\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n)\n\n// WaitForEndpoint waits for the endpoint to be available.\nfunc WaitForEndpoint(ctx context.Context, endpoint string, opts ...retry.Option) error {\n\treturn WaitForEndpointWithClient(ctx, http.DefaultClient, endpoint, opts...)\n}\n\n// WaitForEndpointWithClient waits for the endpoint to be available while using the given http.Client.\nfunc WaitForEndpointWithClient(ctx context.Context, client *http.Client, endpoint string, opts ...retry.Option) error {\n\treturn retry.Do(func() error {\n\t\treq, err := http.NewRequestWithContext(ctx, \"GET\", endpoint, nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tres, err := client.Do(req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer res.Body.Close()\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif gjson.GetBytes(body, \"status\").String() != \"ok\" {\n\t\t\treturn errors.Errorf(\"status is not yet ok: %s\", body)\n\t\t}\n\n\t\treturn nil\n\t},\n\t\tappend([]retry.Option{\n\t\t\tretry.DelayType(retry.BackOffDelay),\n\t\t\tretry.Delay(time.Second),\n\t\t\tretry.MaxDelay(time.Second * 2),\n\t\t\tretry.Attempts(20),\n\t\t}, opts...)...)\n}\n"
  },
  {
    "path": "oryx/ioutilx/pkger.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage ioutilx\n\nimport (\n\t\"io\"\n)\n\n// MustReadAll reads a reader or panics.\nfunc MustReadAll(r io.Reader) []byte {\n\tall, err := io.ReadAll(r)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn all\n}\n"
  },
  {
    "path": "oryx/ipx/cidr.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage ipx\n\nimport (\n\t\"iter\"\n\t\"net/netip\"\n)\n\nfunc Hosts(prefix netip.Prefix) iter.Seq[netip.Addr] {\n\tprefix = prefix.Masked()\n\treturn func(yield func(netip.Addr) bool) {\n\t\tif !prefix.IsValid() {\n\t\t\treturn\n\t\t}\n\t\tfor addr := prefix.Addr().Next(); prefix.Contains(addr); addr = addr.Next() {\n\t\t\tif !yield(addr) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "oryx/ipx/ip_validator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage ipx\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// IsAssociatedIPAllowedWhenSet is a wrapper for IsAssociatedIPAllowed which returns valid\n// when ipOrHostnameOrURL is empty.\nfunc IsAssociatedIPAllowedWhenSet(ipOrHostnameOrURL string) error {\n\tif ipOrHostnameOrURL == \"\" {\n\t\treturn nil\n\t}\n\treturn IsAssociatedIPAllowed(ipOrHostnameOrURL)\n}\n\n// AreAllAssociatedIPsAllowed fails if one of the pairs is failing.\nfunc AreAllAssociatedIPsAllowed(pairs map[string]string) error {\n\tg := new(errgroup.Group)\n\tfor key, ipOrHostnameOrURL := range pairs {\n\t\tkey := key\n\t\tipOrHostnameOrURL := ipOrHostnameOrURL\n\t\tg.Go(func() error {\n\t\t\treturn errors.Wrapf(IsAssociatedIPAllowed(ipOrHostnameOrURL), \"key %s validation is failing\", key)\n\t\t})\n\t}\n\treturn g.Wait()\n}\n\n// IsAssociatedIPAllowed returns nil for a domain (with NS lookup), IP, or IPv6 address if it\n// does not resolve to a private IP subnet. This is a first level of defense against\n// SSRF attacks by disallowing any domain or IP to resolve to a private network range.\n//\n// Please keep in mind that validations for domains is valid only when looking up.\n// A malicious actor could easily update the DSN record post validation to point\n// to an internal IP\nfunc IsAssociatedIPAllowed(ipOrHostnameOrURL string) error {\n\tlookup := func(hostname string) []net.IP {\n\t\tctx, cancel := context.WithTimeoutCause(context.Background(), 2*time.Second, errors.Errorf(\"failed to resolve %s within 2s\", ipOrHostnameOrURL))\n\t\tdefer cancel()\n\n\t\tlookup, err := net.DefaultResolver.LookupIPAddr(ctx, hostname)\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tips := make([]net.IP, len(lookup))\n\t\tfor i, ip := range lookup {\n\t\t\tips[i] = ip.IP\n\t\t}\n\t\treturn ips\n\t}\n\n\tvar ips []net.IP\n\tip := net.ParseIP(ipOrHostnameOrURL)\n\tif ip == nil {\n\t\tif result := lookup(ipOrHostnameOrURL); result != nil {\n\t\t\tips = append(ips, result...)\n\t\t}\n\n\t\tif parsed, err := url.Parse(ipOrHostnameOrURL); err == nil {\n\t\t\tif result := lookup(parsed.Hostname()); result != nil {\n\t\t\t\tips = append(ips, result...)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tips = append(ips, ip)\n\t}\n\n\tfor _, disabled := range []string{\n\t\t\"127.0.0.0/8\",\n\t\t\"10.0.0.0/8\",\n\t\t\"172.16.0.0/12\",\n\t\t\"192.168.0.0/16\",\n\t\t\"fd47:1ed0:805d:59f0::/64\",\n\t\t\"fc00::/7\",\n\t\t\"::1/128\",\n\t} {\n\t\t_, cidr, err := net.ParseCIDR(disabled)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, ip := range ips {\n\t\t\tif cidr.Contains(ip) {\n\t\t\t\treturn errors.Errorf(\"ip %s is in the %s range\", ip, disabled)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/josex/encoding.go",
    "content": "/*-\n * Copyright 2019 Square Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage josex\n\nimport \"io\"\n\n// Base64Reader wraps an input stream consisting of either standard or url-safe\n// base64 data, and maps it to a raw (unpadded) standard encoding. This can be used\n// to read any base64-encoded data as input, whether padded, unpadded, standard or\n// url-safe.\ntype Base64Reader struct {\n\tIn io.Reader\n}\n\nfunc (r Base64Reader) Read(p []byte) (n int, err error) {\n\tn, err = r.In.Read(p)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tfor i := range n {\n\t\tswitch p[i] {\n\t\t// Map - to +\n\t\tcase 0x2D:\n\t\t\tp[i] = 0x2B\n\t\t// Map _ to /\n\t\tcase 0x5F:\n\t\t\tp[i] = 0x2F\n\t\t// Strip =\n\t\tcase 0x3D:\n\t\t\tn = i\n\t\tdefault:\n\t\t}\n\t}\n\n\tif n == 0 {\n\t\terr = io.EOF\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "oryx/josex/generate.go",
    "content": "/*-\n * Copyright 2019 Square Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage josex\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/ed25519\"\n\t\"crypto/elliptic\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-jose/go-jose/v3\"\n)\n\n// NewSigningKey generates a keypair for corresponding SignatureAlgorithm.\nfunc NewSigningKey(alg jose.SignatureAlgorithm, bits int) (crypto.PublicKey, crypto.PrivateKey, error) {\n\tswitch alg {\n\tcase jose.ES256, jose.ES384, jose.ES512, jose.EdDSA:\n\t\tkeylen := map[jose.SignatureAlgorithm]int{\n\t\t\tjose.ES256: 256,\n\t\t\tjose.ES384: 384,\n\t\t\tjose.ES512: 521, // sic!\n\t\t\tjose.EdDSA: 256,\n\t\t}\n\t\tif bits != 0 && bits != keylen[alg] {\n\t\t\treturn nil, nil, errors.New(\"invalid elliptic curve key size, this algorithm does not support arbitrary size\")\n\t\t}\n\tcase jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:\n\t\tif bits == 0 {\n\t\t\tbits = 2048\n\t\t}\n\t\tif bits < 2048 {\n\t\t\treturn nil, nil, errors.New(\"invalid key size for RSA key, 2048 or more is required\")\n\t\t}\n\t}\n\tswitch alg {\n\tcase jose.ES256:\n\t\tkey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\treturn key.Public(), key, err\n\tcase jose.ES384:\n\t\tkey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\treturn key.Public(), key, err\n\tcase jose.ES512:\n\t\tkey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\treturn key.Public(), key, err\n\tcase jose.EdDSA:\n\t\tpub, key, err := ed25519.GenerateKey(rand.Reader)\n\t\treturn pub, key, err\n\tcase jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:\n\t\tkey, err := rsa.GenerateKey(rand.Reader, bits)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\treturn key.Public(), key, err\n\tdefault:\n\t\treturn nil, nil, fmt.Errorf(\"unknown algorithm %s for signing key\", alg)\n\t}\n}\n\n// NewEncryptionKey generates a keypair for corresponding KeyAlgorithm.\nfunc NewEncryptionKey(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.PrivateKey, error) {\n\tswitch alg {\n\tcase jose.RSA1_5, jose.RSA_OAEP, jose.RSA_OAEP_256:\n\t\tif bits == 0 {\n\t\t\tbits = 2048\n\t\t}\n\t\tif bits < 2048 {\n\t\t\treturn nil, nil, errors.New(\"invalid key size for RSA key, 2048 or more is required\")\n\t\t}\n\t\tkey, err := rsa.GenerateKey(rand.Reader, bits)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\treturn key.Public(), key, err\n\tcase jose.ECDH_ES, jose.ECDH_ES_A128KW, jose.ECDH_ES_A192KW, jose.ECDH_ES_A256KW:\n\t\tvar crv elliptic.Curve\n\t\tswitch bits {\n\t\tcase 0, 256:\n\t\t\tcrv = elliptic.P256()\n\t\tcase 384:\n\t\t\tcrv = elliptic.P384()\n\t\tcase 521:\n\t\t\tcrv = elliptic.P521()\n\t\tdefault:\n\t\t\treturn nil, nil, errors.New(\"invalid elliptic curve key size, use one of 256, 384, or 521\")\n\t\t}\n\t\tkey, err := ecdsa.GenerateKey(crv, rand.Reader)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\treturn key.Public(), key, err\n\tdefault:\n\t\treturn nil, nil, fmt.Errorf(\"unknown algorithm %s for encryption key\", alg)\n\t}\n}\n"
  },
  {
    "path": "oryx/josex/public.go",
    "content": "package josex\n\nimport (\n\t\"crypto\"\n\n\t\"github.com/go-jose/go-jose/v3\"\n)\n\n// ToPublicKey returns the public key of the given private key.\nfunc ToPublicKey(k *jose.JSONWebKey) jose.JSONWebKey {\n\tif key := k.Public(); key.Key != nil {\n\t\treturn key\n\t}\n\n\t// HSM workaround - jose does not understand crypto.Signer / HSM so we need to manually\n\t// extract the public key.\n\tswitch key := k.Key.(type) {\n\tcase crypto.Signer:\n\t\tnewKey := *k\n\t\tnewKey.Key = key.Public()\n\t\treturn newKey\n\tcase jose.OpaqueSigner:\n\t\tnewKey := *k\n\t\tnewKey.Key = key.Public().Key\n\t\treturn newKey\n\t}\n\n\treturn jose.JSONWebKey{}\n}\n"
  },
  {
    "path": "oryx/josex/utils.go",
    "content": "/*-\n * Copyright 2019 Square Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage josex\n\nimport (\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/go-jose/go-jose/v3\"\n)\n\n// LoadJSONWebKey returns a *jose.JSONWebKey for a given JSON string.\nfunc LoadJSONWebKey(json []byte, pub bool) (*jose.JSONWebKey, error) {\n\tvar jwk jose.JSONWebKey\n\terr := jwk.UnmarshalJSON(json)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !jwk.Valid() {\n\t\treturn nil, errors.New(\"invalid JWK key\")\n\t}\n\tif jwk.IsPublic() != pub {\n\t\treturn nil, errors.New(\"priv/pub JWK key mismatch\")\n\t}\n\treturn &jwk, nil\n}\n\n// LoadPublicKey loads a public key from PEM/DER/JWK-encoded data.\nfunc LoadPublicKey(data []byte) (interface{}, error) {\n\tinput := data\n\n\tblock, _ := pem.Decode(data)\n\tif block != nil {\n\t\tinput = block.Bytes\n\t}\n\n\t// Try to load SubjectPublicKeyInfo\n\tpub, err0 := x509.ParsePKIXPublicKey(input)\n\tif err0 == nil {\n\t\treturn pub, nil\n\t}\n\n\tcert, err1 := x509.ParseCertificate(input)\n\tif err1 == nil {\n\t\treturn cert.PublicKey, nil\n\t}\n\n\tjwk, err2 := LoadJSONWebKey(data, true)\n\tif err2 == nil {\n\t\treturn jwk, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"square/go-jose: parse error, got '%s', '%s' and '%s'\", err0, err1, err2)\n}\n\n// LoadPrivateKey loads a private key from PEM/DER/JWK-encoded data.\nfunc LoadPrivateKey(data []byte) (interface{}, error) {\n\tinput := data\n\n\tblock, _ := pem.Decode(data)\n\tif block != nil {\n\t\tinput = block.Bytes\n\t}\n\n\tvar priv interface{}\n\tpriv, err0 := x509.ParsePKCS1PrivateKey(input)\n\tif err0 == nil {\n\t\treturn priv, nil\n\t}\n\n\tpriv, err1 := x509.ParsePKCS8PrivateKey(input)\n\tif err1 == nil {\n\t\treturn priv, nil\n\t}\n\n\tpriv, err2 := x509.ParseECPrivateKey(input)\n\tif err2 == nil {\n\t\treturn priv, nil\n\t}\n\n\tjwk, err3 := LoadJSONWebKey(input, false)\n\tif err3 == nil {\n\t\treturn jwk, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"square/go-jose: parse error, got '%s', '%s', '%s' and '%s'\", err0, err1, err2, err3)\n}\n"
  },
  {
    "path": "oryx/jsonnetsecure/cmd/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/ory/x/jsonnetsecure\"\n)\n\nfunc main() {\n\tif err := jsonnetsecure.NewJsonnetCmd().ExecuteContext(context.Background()); err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(-1)\n\t}\n}\n"
  },
  {
    "path": "oryx/jsonnetsecure/cmd.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnetsecure\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/spf13/cobra\"\n)\n\nconst (\n\tGiB uint64 = 1024 * 1024 * 1024\n\t// Generous limit on virtual memory including the peak memory allocated by the Go runtime, the Jsonnet VM,\n\t// and the Jsonnet script.\n\t// This number was acquired by running:\n\t// Found by trial and error with:\n\t// `ulimit -Sv 1048576 && echo '{\"Snippet\": \"{user_id: std.repeat(\\'a\\', 1000)}\"}' | kratos jsonnet -0`\n\t// NOTE: Ideally we'd like to limit RSS but that is not possible on Linux with `ulimit/setrlimit(2)` - only with cgroups.\n\tvirtualMemoryLimitBytes = 2 * GiB\n)\n\nfunc NewJsonnetCmd() *cobra.Command {\n\tvar null bool\n\tcmd := &cobra.Command{\n\t\tUse:    \"jsonnet\",\n\t\tShort:  \"Run Jsonnet as a CLI command\",\n\t\tHidden: true,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\n\t\t\t// This could fail because current limits are lower than what we tried to set,\n\t\t\t// so we still continue in this case.\n\t\t\tSetVirtualMemoryLimit(virtualMemoryLimitBytes)\n\n\t\t\tif null {\n\t\t\t\treturn scan(cmd.OutOrStdout(), cmd.InOrStdin())\n\t\t\t}\n\n\t\t\tinput, err := io.ReadAll(cmd.InOrStdin())\n\t\t\tif err != nil {\n\t\t\t\treturn errors.Wrap(err, \"failed to read from stdin\")\n\t\t\t}\n\n\t\t\tjson, err := eval(input)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.Wrap(err, \"failed to evaluate jsonnet\")\n\t\t\t}\n\n\t\t\tif _, err := io.WriteString(cmd.OutOrStdout(), json); err != nil {\n\t\t\t\treturn errors.Wrap(err, \"failed to write json output\")\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\tcmd.Flags().BoolVarP(&null, \"null\", \"0\", false,\n\t\t`Read multiple snippets and parameters from stdin separated by null bytes.\nOutput will be in the same order as inputs, separated by null bytes.\nEvaluation errors will also be reported to stdout, separated by null bytes.\nNon-recoverable errors are written to stderr and the program will terminate with a non-zero exit code.`)\n\n\treturn cmd\n}\n\nfunc scan(w io.Writer, r io.Reader) error {\n\tscanner := bufio.NewScanner(r)\n\tscanner.Split(splitNull)\n\tfor scanner.Scan() {\n\t\tjson, err := eval(scanner.Bytes())\n\t\tif err != nil {\n\t\t\tjson = fmt.Sprintf(\"ERROR: %s\", err)\n\t\t}\n\t\tif _, err := fmt.Fprintf(w, \"%s%c\", json, 0); err != nil {\n\t\t\treturn errors.Wrap(err, \"failed to write json output\")\n\t\t}\n\t}\n\treturn errors.Wrap(scanner.Err(), \"failed to read from stdin\")\n}\n\nfunc eval(input []byte) (json string, err error) {\n\tvar params processParameters\n\tif err := params.Decode(input); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvm := MakeSecureVM()\n\n\tfor _, it := range params.ExtCodes {\n\t\tvm.ExtCode(it.Key, it.Value)\n\t}\n\tfor _, it := range params.ExtVars {\n\t\tvm.ExtVar(it.Key, it.Value)\n\t}\n\tfor _, it := range params.TLACodes {\n\t\tvm.TLACode(it.Key, it.Value)\n\t}\n\tfor _, it := range params.TLAVars {\n\t\tvm.TLAVar(it.Key, it.Value)\n\t}\n\n\treturn vm.EvaluateAnonymousSnippet(params.Filename, params.Snippet)\n}\n"
  },
  {
    "path": "oryx/jsonnetsecure/jsonnet.go",
    "content": "package jsonnetsecure\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/google/go-jsonnet\"\n)\n\ntype (\n\tVM interface {\n\t\tEvaluateAnonymousSnippet(filename string, snippet string) (json string, formattedErr error)\n\t\tExtCode(key string, val string)\n\t\tExtVar(key string, val string)\n\t\tTLACode(key string, val string)\n\t\tTLAVar(key string, val string)\n\t}\n\n\tkv struct {\n\t\tKey, Value string\n\t}\n\tprocessParameters struct {\n\t\tFilename, Snippet                    string\n\t\tTLACodes, TLAVars, ExtCodes, ExtVars []kv\n\t}\n\n\tvmOptions struct {\n\t\tjsonnetBinaryPath string\n\t\targs              []string\n\t\tctx               context.Context\n\t\tpool              *pool\n\t}\n\n\tOption func(o *vmOptions)\n)\n\nfunc (pp *processParameters) EncodeTo(w io.Writer) error {\n\treturn json.NewEncoder(w).Encode(pp)\n}\n\nfunc (pp *processParameters) Decode(d []byte) error {\n\treturn json.Unmarshal(d, pp)\n}\n\nfunc newVMOptions() *vmOptions {\n\tjsonnetBinaryPath, _ := os.Executable()\n\treturn &vmOptions{\n\t\tjsonnetBinaryPath: jsonnetBinaryPath,\n\t\tctx:               context.Background(),\n\t}\n}\n\nfunc WithContext(ctx context.Context) Option {\n\treturn func(o *vmOptions) {\n\t\to.ctx = ctx\n\t}\n}\n\nfunc WithProcessPool(p Pool) Option {\n\treturn func(o *vmOptions) {\n\t\tpool, _ := p.(*pool)\n\t\to.pool = pool\n\t}\n}\n\nfunc WithJsonnetBinary(jsonnetBinaryPath string) Option {\n\treturn func(o *vmOptions) {\n\t\to.jsonnetBinaryPath = jsonnetBinaryPath\n\t}\n}\n\nfunc WithProcessArgs(args ...string) Option {\n\treturn func(o *vmOptions) {\n\t\to.args = args\n\t}\n}\n\nfunc MakeSecureVM(opts ...Option) VM {\n\toptions := newVMOptions()\n\tfor _, o := range opts {\n\t\to(options)\n\t}\n\n\tif options.pool != nil {\n\t\treturn NewProcessPoolVM(options)\n\t} else {\n\t\tvm := jsonnet.MakeVM()\n\t\tvm.Importer(new(ErrorImporter))\n\t\treturn vm\n\t}\n}\n\n// ErrorImporter errors when calling \"import\".\ntype ErrorImporter struct{}\n\n// Import fetches data from a map entry.\n// All paths are treated as absolute keys.\nfunc (importer *ErrorImporter) Import(importedFrom, importedPath string) (contents jsonnet.Contents, foundAt string, err error) {\n\treturn jsonnet.Contents{}, \"\", fmt.Errorf(\"import not available %v\", importedPath)\n}\n\nfunc JsonnetTestBinary(t testing.TB) string {\n\tt.Helper()\n\n\t// We can force the usage of a given jsonnet executable.\n\t// Useful to test different versions, or run the tests under wine.\n\tif s := os.Getenv(\"ORY_JSONNET_PATH\"); s != \"\" {\n\t\treturn s\n\t}\n\n\tvar stderr bytes.Buffer\n\t// Using `t.TempDir()` results in permissions errors on Windows, sometimes.\n\toutPath := path.Join(os.TempDir(), \"jsonnet\")\n\tif runtime.GOOS == \"windows\" {\n\t\toutPath = outPath + \".exe\"\n\t}\n\tcmd := exec.Command(\"go\", \"build\", \"-o\", outPath, \"github.com/ory/x/jsonnetsecure/cmd\")\n\tcmd.Stderr = &stderr\n\n\tif err := cmd.Run(); err != nil || stderr.Len() != 0 {\n\t\tt.Fatalf(\"building the Go binary returned error: %v\\n%s\", err, stderr.String())\n\t}\n\n\treturn outPath\n}\n"
  },
  {
    "path": "oryx/jsonnetsecure/jsonnet_pool.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnetsecure\n\n// Known limitations/edge cases:\n// - The child process exiting early (e.g. crashing) or getting killed (e.g. reaching some OS limit)\n//   is not detected and no error will be returned in this case from `eval()`.\n// - Misbehaving jsonnet scripts in the middle of a batch being passed to the child process for evaluation may result in\n//   no error (as mentioned above), and other valid scripts in this batch may result\n//   in an error (because the output from the child process is truncated).\n//\n// Possible remediations:\n// - Do not pass a batch of scripts to a worker, only pass one script at a time (to isolate misbehaving scripts)\n// - Validate that the output is valid JSON (to detect truncated output)\n// - Detect the child process exiting (to return an error)\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"math\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/jackc/puddle/v2\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.27.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/x/otelx\"\n)\n\nconst (\n\tKiB                = 1024\n\tjsonnetOutputLimit = 512 * KiB\n\tjsonnetErrLimit    = 1 * KiB\n)\n\ntype (\n\tprocessPoolVM struct {\n\t\tpath   string\n\t\targs   []string\n\t\tctx    context.Context\n\t\tparams processParameters\n\t\tpool   *pool\n\t}\n\tPool interface {\n\t\tClose()\n\t\tprivate()\n\t}\n\tpool struct {\n\t\tpuddle *puddle.Pool[worker]\n\t}\n\tworker struct {\n\t\tcmd    *exec.Cmd\n\t\tstdin  chan<- []byte\n\t\tstdout <-chan string\n\t\tstderr <-chan string\n\t}\n\tcontextKeyType string\n)\n\nvar (\n\tErrProcessPoolClosed = errors.New(\"jsonnetsecure: process pool closed\")\n\n\t_ VM   = (*processPoolVM)(nil)\n\t_ Pool = (*pool)(nil)\n\n\tcontextValuePath contextKeyType = \"argc\"\n\tcontextValueArgs contextKeyType = \"argv\"\n)\n\nfunc NewProcessPool(size int) Pool {\n\tsize = max(5, min(size, math.MaxInt32))\n\tpud, err := puddle.NewPool(&puddle.Config[worker]{\n\t\tMaxSize:     int32(size), //nolint:gosec // disable G115 // because of the previous min/max, 5 <= size <= math.MaxInt32\n\t\tConstructor: newWorker,\n\t\tDestructor:  worker.destroy,\n\t})\n\tif err != nil {\n\t\tpanic(err) // this should never happen, see implementation of puddle.NewPool\n\t}\n\tfor range size {\n\t\t// warm pool\n\t\tgo pud.CreateResource(context.Background())\n\t}\n\tgo func() {\n\t\tfor {\n\t\t\ttime.Sleep(10 * time.Second)\n\t\t\tfor _, proc := range pud.AcquireAllIdle() {\n\t\t\t\tif proc.Value().cmd.ProcessState != nil {\n\t\t\t\t\tproc.Destroy()\n\t\t\t\t} else {\n\t\t\t\t\tproc.Release()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\treturn &pool{pud}\n}\n\nfunc (*pool) private() {}\n\nfunc (p *pool) Close() {\n\tp.puddle.Close()\n}\n\nfunc newWorker(ctx context.Context) (_ worker, err error) {\n\ttracer := trace.SpanFromContext(ctx).TracerProvider().Tracer(\"\")\n\tctx, span := tracer.Start(ctx, \"jsonnetsecure.newWorker\")\n\tdefer otelx.End(span, &err)\n\n\tpath, _ := ctx.Value(contextValuePath).(string)\n\tif path == \"\" {\n\t\treturn worker{}, errors.New(\"newWorker: missing binary path in context\")\n\t}\n\targs, _ := ctx.Value(contextValueArgs).([]string)\n\tcmd := exec.Command(path, append(args, \"-0\")...)\n\tcmd.Env = []string{\"GOMAXPROCS=1\"}\n\tcmd.WaitDelay = 100 * time.Millisecond\n\n\tspan.SetAttributes(semconv.ProcessCommand(cmd.Path), semconv.ProcessCommandArgs(cmd.Args...))\n\n\tstdin, err := cmd.StdinPipe()\n\tif err != nil {\n\t\treturn worker{}, errors.Wrap(err, \"newWorker: failed to create stdin pipe\")\n\t}\n\n\tin := make(chan []byte, 1)\n\tgo func(c <-chan []byte) {\n\t\tfor input := range c {\n\t\t\tif _, err := stdin.Write(append(input, 0)); err != nil {\n\t\t\t\tstdin.Close()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}(in)\n\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\treturn worker{}, errors.Wrap(err, \"newWorker: failed to create stdout pipe\")\n\t}\n\tstderr, err := cmd.StderrPipe()\n\tif err != nil {\n\t\treturn worker{}, errors.Wrap(err, \"newWorker: failed to create stderr pipe\")\n\t}\n\n\tif err := cmd.Start(); err != nil {\n\t\treturn worker{}, errors.Wrap(err, \"newWorker: failed to start process\")\n\t}\n\n\tspan.SetAttributes(semconv.ProcessPID(cmd.Process.Pid))\n\n\tscan := func(c chan<- string, r io.Reader) {\n\t\tdefer close(c)\n\t\t// NOTE: `bufio.Scanner` has its own internal limit of 64 KiB.\n\t\tscanner := bufio.NewScanner(r)\n\n\t\tscanner.Split(splitNull)\n\t\tfor scanner.Scan() {\n\t\t\tc <- scanner.Text()\n\t\t}\n\t\tif err := scanner.Err(); err != nil {\n\t\t\tc <- \"ERROR: scan: \" + err.Error()\n\t\t}\n\t}\n\tout := make(chan string, 1)\n\tgo scan(out, stdout)\n\terrs := make(chan string, 1)\n\tgo scan(errs, stderr)\n\n\tw := worker{\n\t\tcmd:    cmd,\n\t\tstdin:  in,\n\t\tstdout: out,\n\t\tstderr: errs,\n\t}\n\t_, err = w.eval(ctx, []byte(\"{}\")) // warm up\n\tif err != nil {\n\t\tw.destroy()\n\t\treturn worker{}, errors.Wrap(err, \"newWorker: warm up failed\")\n\t}\n\n\treturn w, nil\n}\n\nfunc (w worker) destroy() {\n\tclose(w.stdin)\n\tw.cmd.Process.Kill()\n\tw.cmd.Wait()\n}\n\nfunc (w worker) eval(ctx context.Context, processParams []byte) (output string, err error) {\n\ttracer := trace.SpanFromContext(ctx).TracerProvider().Tracer(\"\")\n\tctx, span := tracer.Start(ctx, \"jsonnetsecure.worker.eval\", trace.WithAttributes(\n\t\tsemconv.ProcessPID(w.cmd.Process.Pid)))\n\tdefer otelx.End(span, &err)\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn \"\", ctx.Err()\n\tcase w.stdin <- processParams:\n\t\tbreak\n\t}\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn \"\", ctx.Err()\n\tcase output := <-w.stdout:\n\t\treturn output, nil\n\tcase err := <-w.stderr:\n\t\treturn \"\", errors.New(err)\n\t}\n}\n\nfunc (vm *processPoolVM) EvaluateAnonymousSnippet(filename string, snippet string) (_ string, err error) {\n\ttracer := trace.SpanFromContext(vm.ctx).TracerProvider().Tracer(\"\")\n\tctx, span := tracer.Start(vm.ctx, \"jsonnetsecure.processPoolVM.EvaluateAnonymousSnippet\", trace.WithAttributes(attribute.String(\"filename\", filename)))\n\tdefer otelx.End(span, &err)\n\n\tparams := vm.params\n\tparams.Filename = filename\n\tparams.Snippet = snippet\n\tpp, err := json.Marshal(params)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"jsonnetsecure: marshal\")\n\t}\n\n\tctx = context.WithValue(ctx, contextValuePath, vm.path)\n\tctx = context.WithValue(ctx, contextValueArgs, vm.args)\n\tworker, err := vm.pool.puddle.Acquire(ctx)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"jsonnetsecure: acquire\")\n\t}\n\n\tctx, cancel := context.WithTimeoutCause(ctx, 1*time.Second, errors.Errorf(\"failed to run jsonnet within 1s: filename=%s\", filename))\n\tdefer cancel()\n\tresult, err := worker.Value().eval(ctx, pp)\n\tif err != nil {\n\t\tworker.Destroy()\n\t\treturn \"\", errors.Wrap(err, \"jsonnetsecure: eval\")\n\t} else {\n\t\tworker.Release()\n\t}\n\n\tif strings.HasPrefix(result, \"ERROR: \") {\n\t\treturn \"\", errors.New(\"jsonnetsecure: \" + result)\n\t}\n\n\treturn result, nil\n}\n\nfunc NewProcessPoolVM(opts *vmOptions) VM {\n\tctx := opts.ctx\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\treturn &processPoolVM{\n\t\tpath: opts.jsonnetBinaryPath,\n\t\targs: opts.args,\n\t\tctx:  ctx,\n\t\tpool: opts.pool,\n\t}\n}\n\nfunc (vm *processPoolVM) ExtCode(key string, val string) {\n\tvm.params.ExtCodes = append(vm.params.ExtCodes, kv{key, val})\n}\n\nfunc (vm *processPoolVM) ExtVar(key string, val string) {\n\tvm.params.ExtVars = append(vm.params.ExtVars, kv{key, val})\n}\n\nfunc (vm *processPoolVM) TLACode(key string, val string) {\n\tvm.params.TLACodes = append(vm.params.TLACodes, kv{key, val})\n}\n\nfunc (vm *processPoolVM) TLAVar(key string, val string) {\n\tvm.params.TLAVars = append(vm.params.TLAVars, kv{key, val})\n}\n"
  },
  {
    "path": "oryx/jsonnetsecure/limit_unix.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n//go:build !windows\n\npackage jsonnetsecure\n\nimport (\n\t\"fmt\"\n\t\"runtime/debug\"\n\t\"syscall\"\n\n\t\"github.com/pkg/errors\"\n)\n\nfunc SetVirtualMemoryLimit(limitBytes uint64) error {\n\t// Tell the Go runtime about the limit.\n\tdebug.SetMemoryLimit(int64(limitBytes)) //nolint:gosec // The number is a compile-time constant.\n\n\tlim := syscall.Rlimit{\n\t\tCur: limitBytes,\n\t\tMax: limitBytes,\n\t}\n\terr := syscall.Setrlimit(syscall.RLIMIT_AS, &lim)\n\tif err != nil {\n\t\treturn errors.WithStack(fmt.Errorf(\"failed to set virtual memory limit: %v\", err))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/jsonnetsecure/limit_windows.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n//go:build windows\n\npackage jsonnetsecure\n\nimport \"runtime/debug\"\n\nfunc SetVirtualMemoryLimit(limitBytes uint64) error {\n\t// Tell the Go runtime about the limit.\n\tdebug.SetMemoryLimit(int64(limitBytes)) //nolint:gosec // The number is a compile-time constant.\n\n\t// TODO No OS limit for now. Apparently there is a Windows-specific equivalent (Job control)?\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/jsonnetsecure/null.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnetsecure\n\nimport \"bytes\"\n\nfunc splitNull(data []byte, atEOF bool) (advance int, token []byte, err error) {\n\t// Look for a null byte; if found, return the position after it,\n\t// the data before it, and no error.\n\tif i := bytes.IndexByte(data, 0); i >= 0 {\n\t\treturn i + 1, data[0:i], nil\n\t}\n\n\t// If we're at EOF, we have a final, non-terminated word. Return it.\n\tif atEOF && len(data) != 0 {\n\t\treturn len(data), data, nil\n\t}\n\n\t// Request more data.\n\treturn 0, nil, nil\n}\n"
  },
  {
    "path": "oryx/jsonnetsecure/provider.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnetsecure\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"runtime\"\n\t\"testing\"\n)\n\ntype (\n\tVMProvider interface {\n\t\t// JsonnetVM creates a new secure process-isolated Jsonnet VM whose\n\t\t// execution is bound to the provided context, i.e.,\n\t\t// cancelling the context will terminate the VM process.\n\t\tJsonnetVM(context.Context) (VM, error)\n\t}\n\n\t// TestProvider provides a secure VM by running go build on github.com/ory/x/jsonnetsecure/cmd\n\tTestProvider struct {\n\t\tjsonnetBinary string\n\t\tpool          Pool\n\t}\n\n\t// DefaultProvider provides a secure VM by calling the currently\n\t// running the current binary with the provided subcommand.\n\tDefaultProvider struct {\n\t\tSubcommand string\n\t\tPool       Pool\n\t}\n)\n\nfunc NewTestProvider(t testing.TB) *TestProvider {\n\tpool := NewProcessPool(runtime.GOMAXPROCS(0))\n\tt.Cleanup(pool.Close)\n\treturn &TestProvider{JsonnetTestBinary(t), pool}\n}\n\nfunc (p *TestProvider) JsonnetVM(ctx context.Context) (VM, error) {\n\treturn MakeSecureVM(\n\t\tWithContext(ctx),\n\t\tWithProcessPool(p.pool),\n\t\tWithJsonnetBinary(p.jsonnetBinary),\n\t), nil\n}\n\nfunc (p *DefaultProvider) JsonnetVM(ctx context.Context) (VM, error) {\n\tself, err := os.Executable()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn MakeSecureVM(\n\t\tWithContext(ctx),\n\t\tWithJsonnetBinary(self),\n\t\tWithProcessArgs(p.Subcommand),\n\t\tWithProcessPool(p.Pool),\n\t), nil\n}\n"
  },
  {
    "path": "oryx/jsonnetsecure/stub/import.jsonnet",
    "content": "{ foo: 'bar' }\n"
  },
  {
    "path": "oryx/jsonnetx/format.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnetx\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/bmatcuk/doublestar/v2\"\n\t\"github.com/google/go-jsonnet/formatter\"\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/cmdx\"\n)\n\nfunc NewFormatCommand() *cobra.Command {\n\tvar verbose, write bool\n\tcmd := &cobra.Command{\n\t\tUse: \"format path/to/files/*.jsonnet [more/files.jsonnet, [supports/**/{foo,bar}.jsonnet]]\",\n\t\tLong: `Formats JSONNet files using the official JSONNet formatter.\n\nUse -w or --write to write output back to files instead of stdout.\n\n` + GlobHelp,\n\t\tArgs: cobra.MinimumNArgs(1),\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tfor _, pattern := range args {\n\t\t\t\tfiles, err := doublestar.Glob(pattern)\n\t\t\t\tif err != nil {\n\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Glob pattern %q is not valid: %s\\n\", pattern, err)\n\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t}\n\n\t\t\t\tfor _, file := range files {\n\t\t\t\t\tif fi, err := os.Stat(file); err != nil {\n\t\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Matching file %q could not be opened: %s\\n\", file, err)\n\t\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t\t} else if fi.IsDir() {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tif verbose {\n\t\t\t\t\t\tfmt.Printf(\"Processing file: %s\\n\", file)\n\t\t\t\t\t}\n\n\t\t\t\t\t//#nosec G304 -- false positive\n\t\t\t\t\tcontent, err := os.ReadFile(file)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Unable to read file %q: %s\\n\", file, err)\n\t\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t\t}\n\n\t\t\t\t\toutput, err := formatter.Format(file, string(content), formatter.DefaultOptions())\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"File %q could not be formatted: %s\", file, err)\n\t\t\t\t\t}\n\n\t\t\t\t\tif write {\n\t\t\t\t\t\terr := os.WriteFile(file, []byte(output), 0o644) // #nosec\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Unable to write file %q: %s\\n\", file, err)\n\t\t\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfmt.Println(output)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\tcmd.Flags().BoolVarP(&write, \"write\", \"w\", false, \"Write formatted output back to file.\")\n\tcmd.Flags().BoolVarP(&verbose, \"verbose\", \"v\", false, \"Verbose output.\")\n\treturn cmd\n}\n"
  },
  {
    "path": "oryx/jsonnetx/lint.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnetx\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/bmatcuk/doublestar/v2\"\n\t\"github.com/google/go-jsonnet\"\n\t\"github.com/google/go-jsonnet/linter\"\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/cmdx\"\n)\n\nfunc NewLintCommand() *cobra.Command {\n\tvar verbose bool\n\tcmd := &cobra.Command{\n\t\tUse: \"lint path/to/files/*.jsonnet [more/files.jsonnet, [supports/**/{foo,bar}.jsonnet]]\",\n\t\tLong: `Lints JSONNet files using the official JSONNet linter and exits with a status code of 1 when issues are detected.\n\n` + GlobHelp,\n\t\tArgs: cobra.MinimumNArgs(1),\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tfor _, pattern := range args {\n\t\t\t\tfiles, err := doublestar.Glob(pattern)\n\t\t\t\tif err != nil {\n\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Glob pattern %q is not valid: %s\\n\", pattern, err)\n\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t}\n\n\t\t\t\tfor _, file := range files {\n\t\t\t\t\tif fi, err := os.Stat(file); err != nil {\n\t\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Matching file %q could not be opened: %s\\n\", file, err)\n\t\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t\t} else if fi.IsDir() {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tif verbose {\n\t\t\t\t\t\tfmt.Printf(\"Processing file: %s\\n\", file)\n\t\t\t\t\t}\n\n\t\t\t\t\t//#nosec G304 -- false positive\n\t\t\t\t\tcontent, err := os.ReadFile(file)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Unable to read file %q: %s\\n\", file, err)\n\t\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t\t}\n\n\t\t\t\t\tif linter.LintSnippet(jsonnet.MakeVM(), cmd.ErrOrStderr(), []linter.Snippet{{FileName: file, Code: string(content)}}) {\n\t\t\t\t\t\treturn cmdx.FailSilently(cmd)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\tcmd.Flags().BoolVarP(&verbose, \"verbose\", \"v\", false, \"Verbose output.\")\n\treturn cmd\n}\n"
  },
  {
    "path": "oryx/jsonnetx/root.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonnetx\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\nconst GlobHelp = `Glob patterns supports the following special terms in the patterns:\n\t\n\tSpecial Terms | Meaning\n\t------------- | -------\n\t'*'           | matches any sequence of non-path-separators\n\t'**'          | matches any sequence of characters, including path separators\n\t'?'           | matches any single non-path-separator character\n\t'[class]'     | matches any single non-path-separator character against a class of characters ([see below](#character-classes))\n\t'{alt1,...}'  | matches a sequence of characters if one of the comma-separated alternatives matches\n\t\n\tAny character with a special meaning can be escaped with a backslash ('\\').\n\t\n\t#### Character Classes\n\t\n\tCharacter classes support the following:\n\t\n\tClass      | Meaning\n\t---------- | -------\n\t'[abc]'    | matches any single character within the set\n\t'[a-z]'    | matches any single character in the range\n\t'[^class]' | matches any single character which does *not* match the class\n`\n\n// RootCommand represents the jsonnet command\n// Deprecated: use NewRootCommand instead.\nvar RootCommand = &cobra.Command{\n\tUse:   \"jsonnet\",\n\tShort: \"Helpers for linting and formatting JSONNet code\",\n}\n\n// RegisterCommandRecursive adds all jsonnet helpers to the RootCommand\n// Deprecated: use NewRootCommand instead.\nfunc RegisterCommandRecursive(parent *cobra.Command) {\n\tparent.AddCommand(RootCommand)\n\n\tRootCommand.AddCommand(NewFormatCommand())\n\tRootCommand.AddCommand(NewLintCommand())\n}\n\nfunc NewRootCommand() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUse:   \"jsonnet\",\n\t\tShort: \"Helpers for linting and formatting JSONNet code\",\n\t}\n\tcmd.AddCommand(NewFormatCommand(), NewLintCommand())\n\treturn cmd\n}\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=0.json",
    "content": "[\n  {\n    \"Title\": \"Access Rules\",\n    \"Description\": \"Configure access rules. All sub-keys support configuration reloading without restarting.\",\n    \"Examples\": null,\n    \"Name\": \"access_rules\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Repositories\",\n    \"Description\": \"Locations (list of URLs) where access rules should be fetched from on boot. It is expected that the documents at those locations return a JSON or YAML Array containing ORY Oathkeeper Access Rules:\\n\\n- If the URL Scheme is `file://`, the access rules (an array of access rules is expected) will be fetched from the local file system.\\n- If the URL Scheme is `inline://`, the access rules (an array of access rules is expected) are expected to be a base64 encoded (with padding!) JSON/YAML string (base64_encode(`[{\\\"id\\\":\\\"foo-rule\\\",\\\"authenticators\\\":[....]}]`)).\\n- If the URL Scheme is `http://` or `https://`, the access rules (an array of access rules is expected) will be fetched from the provided HTTP(s) location.\",\n    \"Examples\": [\n      \"[\\\"file://path/to/rules.json\\\",\\\"inline://W3siaWQiOiJmb28tcnVsZSIsImF1dGhlbnRpY2F0b3JzIjpbXX1d\\\",\\\"https://path-to-my-rules/rules.json\\\"]\"\n    ],\n    \"Name\": \"access_rules.repositories\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"access_rules.repositories.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Authenticators\",\n    \"Description\": \"For more information on authenticators head over to: https://www.ory.sh/docs/oathkeeper/pipeline/authn\",\n    \"Examples\": null,\n    \"Name\": \"authenticators\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Anonymous\",\n    \"Description\": \"The [`anonymous` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#anonymous).\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.anonymous\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Anonymous Authenticator Configuration\",\n    \"Description\": \"This section is optional when the authenticator is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.anonymous.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Anonymous Subject\",\n    \"Description\": \"Sets the anonymous username.\",\n    \"Examples\": [\n      \"guest\",\n      \"anon\",\n      \"anonymous\",\n      \"unknown\"\n    ],\n    \"Name\": \"authenticators.anonymous.config.subject\",\n    \"Default\": \"anonymous\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authenticators.anonymous.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Cookie Session\",\n    \"Description\": \"The [`cookie_session` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#cookie_session).\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.cookie_session\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Cookie Session Authenticator Configuration\",\n    \"Description\": \"This section is optional when the authenticator is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.cookie_session.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Session Check URL\",\n    \"Description\": \"The origin to proxy requests to. If the response is a 200 with body `{ \\\"subject\\\": \\\"...\\\", \\\"extra\\\": {} }`. The request will pass the subject through successfully, otherwise it will be marked as unauthorized.\\n\\n\\u003eIf this authenticator is enabled, this value is required.\",\n    \"Examples\": [\n      \"https://session-store-host\"\n    ],\n    \"Name\": \"authenticators.cookie_session.config.check_session_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Only Cookies\",\n    \"Description\": \"A list of possible cookies to look for on incoming requests, and will fallthrough to the next authenticator if none of the passed cookies are set on the request.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.cookie_session.config.only\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.cookie_session.config.only.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authenticators.cookie_session.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"JSON Web Token (jwt)\",\n    \"Description\": \"The [`jwt` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#jwt).\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"JWT Authenticator Configuration\",\n    \"Description\": \"This section is optional when the authenticator is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.allowed_algorithms\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.allowed_algorithms.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"JSON Web Key URLs\",\n    \"Description\": \"URLs where ORY Oathkeeper can retrieve JSON Web Keys from for validating the JSON Web Token. Usually something like \\\"https://my-keys.com/.well-known/jwks.json\\\". The response of that endpoint must return a JSON Web Key Set (JWKS).\\n\\n\\u003eIf this authenticator is enabled, this value is required.\",\n    \"Examples\": [\n      \"https://my-website.com/.well-known/jwks.json\",\n      \"https://my-other-website.com/.well-known/jwks.json\",\n      \"file://path/to/local/jwks.json\"\n    ],\n    \"Name\": \"authenticators.jwt.config.jwks_urls\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.jwks_urls.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Required Token Scope\",\n    \"Description\": \"An array of OAuth 2.0 scopes that are required when accessing an endpoint protected by this handler.\\n If the token used in the Authorization header did not request that specific scope, the request is denied.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.required_scope\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.required_scope.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Scope Strategy\",\n    \"Description\": \"Sets the strategy validation algorithm.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.scope_strategy\",\n    \"Default\": \"none\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": [\n      \"hierarchic\",\n      \"exact\",\n      \"wildcard\",\n      \"none\"\n    ],\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Intended Audience\",\n    \"Description\": \"An array of audiences that are required when accessing an endpoint protected by this handler.\\n If the token used in the Authorization header is not intended for any of the requested audiences, the request is denied.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.target_audience\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.target_audience.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.token_from\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Header\",\n    \"Description\": \"The header (case insensitive) that must contain a token for request authentication. It can't be set along with query_parameter.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.token_from.header\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Query Parameter\",\n    \"Description\": \"The query parameter (case sensitive) that must contain a token for request authentication. It can't be set along with header.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.token_from.query_parameter\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.trusted_issuers\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.jwt.config.trusted_issuers.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authenticators.jwt.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"No Operation (noop)\",\n    \"Description\": \"The [`noop` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#noop).\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.noop\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authenticators.noop.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"OAuth 2.0 Client Credentials\",\n    \"Description\": \"The [`oauth2_client_credentials` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#oauth2_client_credentials).\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_client_credentials\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"OAuth 2.0 Client Credentials Authenticator Configuration\",\n    \"Description\": \"This section is optional when the authenticator is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_client_credentials.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Request Permissions (Token Scope)\",\n    \"Description\": \"Scopes is an array of OAuth 2.0 scopes that are required when accessing an endpoint protected by this rule.\\n If the token used in the Authorization header did not request that specific scope, the request is denied.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_client_credentials.config.required_scope\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_client_credentials.config.required_scope.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"The OAuth 2.0 Token Endpoint that will be used to validate the client credentials.\\n\\n\\u003eIf this authenticator is enabled, this value is required.\",\n    \"Examples\": [\n      \"https://my-website.com/oauth2/token\"\n    ],\n    \"Name\": \"authenticators.oauth2_client_credentials.config.token_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authenticators.oauth2_client_credentials.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"OAuth 2.0 Token Introspection\",\n    \"Description\": \"The [`oauth2_introspection` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#oauth2_introspection).\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"OAuth 2.0 Introspection Authenticator Configuration\",\n    \"Description\": \"This section is optional when the authenticator is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"OAuth 2.0 Introspection URL\",\n    \"Description\": \"The OAuth 2.0 Token Introspection endpoint URL.\\n\\n\\u003eIf this authenticator is enabled, this value is required.\",\n    \"Examples\": [\n      \"https://my-website.com/oauth2/introspection\"\n    ],\n    \"Name\": \"authenticators.oauth2_introspection.config.introspection_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Pre-Authorization\",\n    \"Description\": \"Enable pre-authorization in cases where the OAuth 2.0 Token Introspection endpoint is protected by OAuth 2.0 Bearer Tokens that can be retrieved using the OAuth 2.0 Client Credentials grant.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.pre_authorization\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"OAuth 2.0 Client ID\",\n    \"Description\": \"The OAuth 2.0 Client ID to be used for the OAuth 2.0 Client Credentials Grant.\\n\\n\\u003eIf pre-authorization is enabled, this value is required.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.pre_authorization.client_id\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"OAuth 2.0 Client Secret\",\n    \"Description\": \"The OAuth 2.0 Client Secret to be used for the OAuth 2.0 Client Credentials Grant.\\n\\n\\u003eIf pre-authorization is enabled, this value is required.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.pre_authorization.client_secret\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.pre_authorization.enabled\",\n    \"Default\": null,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": [\n      true\n    ],\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"OAuth 2.0 Scope\",\n    \"Description\": \"The OAuth 2.0 Scope to be requested during the OAuth 2.0 Client Credentials Grant.\",\n    \"Examples\": [\n      [\n        \"[\\\"foo\\\", \\\"bar\\\"]\"\n      ]\n    ],\n    \"Name\": \"authenticators.oauth2_introspection.config.pre_authorization.scope\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.pre_authorization.scope.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"OAuth 2.0 Token URL\",\n    \"Description\": \"The OAuth 2.0 Token Endpoint where the OAuth 2.0 Client Credentials Grant will be performed.\\n\\n\\u003eIf pre-authorization is enabled, this value is required.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.pre_authorization.token_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Required Scope\",\n    \"Description\": \"An array of OAuth 2.0 scopes that are required when accessing an endpoint protected by this handler.\\n If the token used in the Authorization header did not request that specific scope, the request is denied.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.required_scope\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.required_scope.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Scope Strategy\",\n    \"Description\": \"Sets the strategy validation algorithm.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.scope_strategy\",\n    \"Default\": \"none\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": [\n      \"hierarchic\",\n      \"exact\",\n      \"wildcard\",\n      \"none\"\n    ],\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Target Audience\",\n    \"Description\": \"An array of audiences that are required when accessing an endpoint protected by this handler.\\n If the token used in the Authorization header is not intended for any of the requested audiences, the request is denied.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.target_audience\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.target_audience.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Token From\",\n    \"Description\": \"The location of the token.\\n If not configured, the token will be received from a default location - 'Authorization' header.\\n One and only one location (header or query) must be specified.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.token_from\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Header\",\n    \"Description\": \"The header (case insensitive) that must contain a token for request authentication.\\n It can't be set along with query_parameter.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.token_from.header\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Query Parameter\",\n    \"Description\": \"The query parameter (case sensitive) that must contain a token for request authentication.\\n It can't be set along with header.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.token_from.query_parameter\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Trusted Issuers\",\n    \"Description\": \"The token must have been issued by one of the issuers listed in this array.\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.trusted_issuers\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.oauth2_introspection.config.trusted_issuers.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authenticators.oauth2_introspection.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Unauthorized\",\n    \"Description\": \"The [`unauthorized` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#unauthorized).\",\n    \"Examples\": null,\n    \"Name\": \"authenticators.unauthorized\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authenticators.unauthorized.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Authorizers\",\n    \"Description\": \"For more information on authorizers head over to: https://www.ory.sh/docs/oathkeeper/pipeline/authz\",\n    \"Examples\": null,\n    \"Name\": \"authorizers\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allow\",\n    \"Description\": \"The [`allow` authorizer](https://www.ory.sh/docs/oathkeeper/pipeline/authz#allow).\",\n    \"Examples\": null,\n    \"Name\": \"authorizers.allow\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authorizers.allow.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Deny\",\n    \"Description\": \"The [`deny` authorizer](https://www.ory.sh/docs/oathkeeper/pipeline/authz#allow).\",\n    \"Examples\": null,\n    \"Name\": \"authorizers.deny\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authorizers.deny.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"ORY Keto Access Control Policies Engine\",\n    \"Description\": \"The [`keto_engine_acp_ory` authorizer](https://www.ory.sh/docs/oathkeeper/pipeline/authz#keto_engine_acp_ory).\",\n    \"Examples\": null,\n    \"Name\": \"authorizers.keto_engine_acp_ory\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"ORY Keto Access Control Policy Authorizer Configuration\",\n    \"Description\": \"This section is optional when the authorizer is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"authorizers.keto_engine_acp_ory.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Base URL\",\n    \"Description\": \"The base URL of ORY Keto.\\n\\n\\u003eIf this authorizer is enabled, this value is required.\",\n    \"Examples\": [\n      \"http://my-keto/\"\n    ],\n    \"Name\": \"authorizers.keto_engine_acp_ory.config.base_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authorizers.keto_engine_acp_ory.config.flavor\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authorizers.keto_engine_acp_ory.config.required_action\",\n    \"Default\": \"unset\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authorizers.keto_engine_acp_ory.config.required_resource\",\n    \"Default\": \"unset\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"authorizers.keto_engine_acp_ory.config.subject\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"authorizers.keto_engine_acp_ory.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Log\",\n    \"Description\": \"Configure logging using the following options. Logging will always be sent to stdout and stderr.\",\n    \"Examples\": null,\n    \"Name\": \"log\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Format\",\n    \"Description\": \"The log format can either be text or JSON.\",\n    \"Examples\": null,\n    \"Name\": \"log.format\",\n    \"Default\": \"text\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": [\n      \"text\",\n      \"json\"\n    ],\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Level\",\n    \"Description\": \"Debug enables stack traces on errors. Can also be set using environment variable LOG_LEVEL.\",\n    \"Examples\": null,\n    \"Name\": \"log.level\",\n    \"Default\": \"info\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": [\n      \"panic\",\n      \"fatal\",\n      \"error\",\n      \"warn\",\n      \"info\",\n      \"debug\"\n    ],\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Mutators\",\n    \"Description\": \"For more information on mutators head over to: https://www.ory.sh/docs/oathkeeper/pipeline/mutator\",\n    \"Examples\": null,\n    \"Name\": \"mutators\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTP Cookie\",\n    \"Description\": \"The [`cookie` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#cookie).\",\n    \"Examples\": null,\n    \"Name\": \"mutators.cookie\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Cookie Mutator Configuration\",\n    \"Description\": \"This section is optional when the mutator is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"mutators.cookie.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.cookie.config.cookies\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"mutators.cookie.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTP Header\",\n    \"Description\": \"The [`header` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#header).\",\n    \"Examples\": null,\n    \"Name\": \"mutators.header\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Header Mutator Configuration\",\n    \"Description\": \"This section is optional when the mutator is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"mutators.header.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.header.config.headers\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"mutators.header.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Hydrator\",\n    \"Description\": \"The [`hydrator` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#hydrator).\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Hydrator Mutator Configuration\",\n    \"Description\": \"This section is optional when the mutator is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config.api\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config.api.auth\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config.api.auth.basic\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config.api.auth.basic.password\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config.api.auth.basic.username\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config.api.retry\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config.api.retry.delay_in_milliseconds\",\n    \"Default\": 3,\n    \"Type\": 0,\n    \"TypeHint\": 3,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": \"0\",\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config.api.retry.number_of_retries\",\n    \"Default\": 100,\n    \"Type\": 0,\n    \"TypeHint\": 2,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": \"0\",\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.hydrator.config.api.url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"mutators.hydrator.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"ID Token (JSON Web Token)\",\n    \"Description\": \"The [`id_token` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#id_token).\",\n    \"Examples\": null,\n    \"Name\": \"mutators.id_token\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"ID Token Mutator Configuration\",\n    \"Description\": \"This section is optional when the mutator is disabled.\",\n    \"Examples\": null,\n    \"Name\": \"mutators.id_token.config\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"mutators.id_token.config.claims\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Issuer URL\",\n    \"Description\": \"Sets the \\\"iss\\\" value of the ID Token.\\n\\n\\u003eIf this mutator is enabled, this value is required.\",\n    \"Examples\": null,\n    \"Name\": \"mutators.id_token.config.issuer_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"JSON Web Key URL\",\n    \"Description\": \"Sets the URL where keys should be fetched from. Supports remote locations (http, https) as well as local filesystem paths.\\n\\n\\u003eIf this mutator is enabled, this value is required.\",\n    \"Examples\": [\n      \"https://fetch-keys/from/this/location.json\",\n      \"file:///from/this/absolute/location.json\",\n      \"file://../from/this/relative/location.json\"\n    ],\n    \"Name\": \"mutators.id_token.config.jwks_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Expire After\",\n    \"Description\": \"Sets the time-to-live of the JSON Web Token.\",\n    \"Examples\": [\n      \"1h\",\n      \"1m\",\n      \"30s\"\n    ],\n    \"Name\": \"mutators.id_token.config.ttl\",\n    \"Default\": \"1m\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"mutators.id_token.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"No Operation (noop)\",\n    \"Description\": \"The [`noop` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#noop).\",\n    \"Examples\": null,\n    \"Name\": \"mutators.noop\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enabled\",\n    \"Description\": \"En-/disables this component.\",\n    \"Examples\": [\n      true\n    ],\n    \"Name\": \"mutators.noop.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Profiling\",\n    \"Description\": \"Enables CPU or memory profiling if set. For more details on profiling Go programs read [Profiling Go Programs](https://blog.golang.org/profiling-go-programs).\",\n    \"Examples\": null,\n    \"Name\": \"profiling\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": [\n      \"cpu\",\n      \"mem\"\n    ],\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTP(s)\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTP REST API\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.api\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Cross Origin Resource Sharing (CORS)\",\n    \"Description\": \"Configure [Cross Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) using the following options.\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allow HTTP Credentials\",\n    \"Description\": \"Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.allow_credentials\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allowed Request HTTP Headers\",\n    \"Description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.allowed_headers\",\n    \"Default\": [\n      \"Authorization\",\n      \"Content-Type\"\n    ],\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": 1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.allowed_headers.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allowed HTTP Methods\",\n    \"Description\": \"A list of methods the client is allowed to use with cross-domain requests.\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.allowed_methods\",\n    \"Default\": [\n      \"GET\",\n      \"POST\",\n      \"PUT\",\n      \"PATCH\",\n      \"DELETE\"\n    ],\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.allowed_methods.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": [\n      \"GET\",\n      \"HEAD\",\n      \"POST\",\n      \"PUT\",\n      \"DELETE\",\n      \"CONNECT\",\n      \"TRACE\",\n      \"PATCH\"\n    ],\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allowed Origins\",\n    \"Description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality. Only one wildcard can be used per origin.\",\n    \"Examples\": [\n      \"https://example.com\",\n      \"https://*.example.com\",\n      \"https://*.foo.example.com\"\n    ],\n    \"Name\": \"serve.api.cors.allowed_origins\",\n    \"Default\": [\n      \"*\"\n    ],\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.allowed_origins.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": 1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enable Debugging\",\n    \"Description\": \"Set to true to debug server side CORS issues.\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.debug\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enable CORS\",\n    \"Description\": \"If set to true, CORS will be enabled and preflight-requests (OPTION) will be answered.\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allowed Response HTTP Headers\",\n    \"Description\": \"Indicates which headers are safe to expose to the API of a CORS API specification\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.exposed_headers\",\n    \"Default\": [\n      \"Content-Type\"\n    ],\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": 1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.exposed_headers.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Maximum Age\",\n    \"Description\": \"Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0 which stands for no max age.\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.cors.max_age\",\n    \"Default\": 0,\n    \"Type\": 0,\n    \"TypeHint\": 2,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Host\",\n    \"Description\": \"The network interface to listen on.\",\n    \"Examples\": [\n      \"localhost\",\n      \"127.0.0.1\"\n    ],\n    \"Name\": \"serve.api.host\",\n    \"Default\": \"\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Port\",\n    \"Description\": \"The port to listen on.\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.port\",\n    \"Default\": 4456,\n    \"Type\": 0,\n    \"TypeHint\": 2,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTPS\",\n    \"Description\": \"Configure HTTP over TLS (HTTPS). All options can also be set using environment variables by replacing dots (`.`) with underscores (`_`) and uppercasing the key. For example, `some.prefix.tls.key.path` becomes `export SOME_PREFIX_TLS_KEY_PATH`. If all keys are left undefined, TLS will be disabled.\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.tls\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.tls.cert\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Base64 Encoded Inline\",\n    \"Description\": \"The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\",\n    \"Examples\": [\n      \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n    ],\n    \"Name\": \"serve.api.tls.cert.base64\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Path to PEM-encoded Fle\",\n    \"Description\": \"\",\n    \"Examples\": [\n      \"path/to/file.pem\"\n    ],\n    \"Name\": \"serve.api.tls.cert.path\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.api.tls.key\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Base64 Encoded Inline\",\n    \"Description\": \"The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\",\n    \"Examples\": [\n      \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n    ],\n    \"Name\": \"serve.api.tls.key.base64\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Path to PEM-encoded Fle\",\n    \"Description\": \"\",\n    \"Examples\": [\n      \"path/to/file.pem\"\n    ],\n    \"Name\": \"serve.api.tls.key.path\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTP Reverse Proxy\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Cross Origin Resource Sharing (CORS)\",\n    \"Description\": \"Configure [Cross Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) using the following options.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allow HTTP Credentials\",\n    \"Description\": \"Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.allow_credentials\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allowed Request HTTP Headers\",\n    \"Description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.allowed_headers\",\n    \"Default\": [\n      \"Authorization\",\n      \"Content-Type\"\n    ],\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": 1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.allowed_headers.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allowed HTTP Methods\",\n    \"Description\": \"A list of methods the client is allowed to use with cross-domain requests.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.allowed_methods\",\n    \"Default\": [\n      \"GET\",\n      \"POST\",\n      \"PUT\",\n      \"PATCH\",\n      \"DELETE\"\n    ],\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.allowed_methods.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": [\n      \"GET\",\n      \"HEAD\",\n      \"POST\",\n      \"PUT\",\n      \"DELETE\",\n      \"CONNECT\",\n      \"TRACE\",\n      \"PATCH\"\n    ],\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allowed Origins\",\n    \"Description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality. Only one wildcard can be used per origin.\",\n    \"Examples\": [\n      \"https://example.com\",\n      \"https://*.example.com\",\n      \"https://*.foo.example.com\"\n    ],\n    \"Name\": \"serve.proxy.cors.allowed_origins\",\n    \"Default\": [\n      \"*\"\n    ],\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.allowed_origins.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": 1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enable Debugging\",\n    \"Description\": \"Set to true to debug server side CORS issues.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.debug\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Enable CORS\",\n    \"Description\": \"If set to true, CORS will be enabled and preflight-requests (OPTION) will be answered.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.enabled\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Allowed Response HTTP Headers\",\n    \"Description\": \"Indicates which headers are safe to expose to the API of a CORS API specification\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.exposed_headers\",\n    \"Default\": [\n      \"Content-Type\"\n    ],\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": 1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.exposed_headers.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Maximum Age\",\n    \"Description\": \"Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0 which stands for no max age.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.cors.max_age\",\n    \"Default\": 0,\n    \"Type\": 0,\n    \"TypeHint\": 2,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Host\",\n    \"Description\": \"The network interface to listen on. Leave empty to listen on all interfaces.\",\n    \"Examples\": [\n      \"localhost\",\n      \"127.0.0.1\"\n    ],\n    \"Name\": \"serve.proxy.host\",\n    \"Default\": \"\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Port\",\n    \"Description\": \"The port to listen on.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.port\",\n    \"Default\": 4455,\n    \"Type\": 0,\n    \"TypeHint\": 2,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTP Timeouts\",\n    \"Description\": \"Control the reverse proxy's HTTP timeouts.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.timeout\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTP Idle Timeout\",\n    \"Description\": \" The maximum amount of time to wait for any action of a request session, reading data or writing the response.\",\n    \"Examples\": [\n      \"5s\",\n      \"5m\",\n      \"5h\"\n    ],\n    \"Name\": \"serve.proxy.timeout.idle\",\n    \"Default\": \"120s\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTP Read Timeout\",\n    \"Description\": \"The maximum duration for reading the entire request, including the body.\",\n    \"Examples\": [\n      \"5s\",\n      \"5m\",\n      \"5h\"\n    ],\n    \"Name\": \"serve.proxy.timeout.read\",\n    \"Default\": \"5s\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTP Write Timeout\",\n    \"Description\": \"The maximum duration before timing out writes of the response. Increase this parameter to prevent unexpected closing a client connection if an upstream request is responding slowly.\",\n    \"Examples\": [\n      \"5s\",\n      \"5m\",\n      \"5h\"\n    ],\n    \"Name\": \"serve.proxy.timeout.write\",\n    \"Default\": \"120s\",\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"HTTPS\",\n    \"Description\": \"Configure HTTP over TLS (HTTPS). All options can also be set using environment variables by replacing dots (`.`) with underscores (`_`) and uppercasing the key. For example, `some.prefix.tls.key.path` becomes `export SOME_PREFIX_TLS_KEY_PATH`. If all keys are left undefined, TLS will be disabled.\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.tls\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.tls.cert\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Base64 Encoded Inline\",\n    \"Description\": \"The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\",\n    \"Examples\": [\n      \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n    ],\n    \"Name\": \"serve.proxy.tls.cert.base64\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Path to PEM-encoded Fle\",\n    \"Description\": \"\",\n    \"Examples\": [\n      \"path/to/file.pem\"\n    ],\n    \"Name\": \"serve.proxy.tls.cert.path\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"serve.proxy.tls.key\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Base64 Encoded Inline\",\n    \"Description\": \"The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\",\n    \"Examples\": [\n      \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n    ],\n    \"Name\": \"serve.proxy.tls.key.base64\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Path to PEM-encoded Fle\",\n    \"Description\": \"\",\n    \"Examples\": [\n      \"path/to/file.pem\"\n    ],\n    \"Name\": \"serve.proxy.tls.key.path\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=1.json",
    "content": "[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers.#\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers.#.id\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=2.json",
    "content": "[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"dsn\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=3.json",
    "content": "[\n  {\n    \"Title\": \"OpenID Connect and OAuth2 Providers\",\n    \"Description\": \"A list and configuration of OAuth2 and OpenID Connect providers ORY Kratos should integrate with.\",\n    \"Examples\": null,\n    \"Name\": \"providers\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers.#\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": [\n      \"https://accounts.google.com/o/oauth2/v2/auth\"\n    ],\n    \"Name\": \"providers.#.auth_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers.#.client_id\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers.#.client_secret\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": [\n      \"google\"\n    ],\n    \"Name\": \"providers.#.id\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": [\n      \"https://accounts.google.com\"\n    ],\n    \"Name\": \"providers.#.issuer_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Jsonnet Mapper URL\",\n    \"Description\": \"The URL where the jsonnet source is located for mapping the provider's data to ORY Kratos data.\",\n    \"Examples\": [\n      \"file://path/to/oidc.jsonnet\",\n      \"https://foo.bar.com/path/to/oidc.jsonnet\",\n      \"base64://bG9jYWwgc3ViamVjdCA9I...\"\n    ],\n    \"Name\": \"providers.#.mapper_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Provider\",\n    \"Description\": \"Can be one of github, gitlab, generic, google, microsoft, discord.\",\n    \"Examples\": [\n      \"google\"\n    ],\n    \"Name\": \"providers.#.provider\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": [\n      \"github\",\n      \"gitlab\",\n      \"generic\",\n      \"google\",\n      \"microsoft\",\n      \"discord\"\n    ],\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers.#.scope\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": [\n      \"offline_access\",\n      \"profile\"\n    ],\n    \"Name\": \"providers.#.scope.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"Azure AD Tenant\",\n    \"Description\": \"The Azure AD Tenant to use for authentication.\",\n    \"Examples\": [\n      \"common\",\n      \"organizations\",\n      \"consumers\",\n      \"8eaef023-2b34-4da1-9baa-8bc8c9d6a490\",\n      \"contoso.onmicrosoft.com\"\n    ],\n    \"Name\": \"providers.#.tenant\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": [\n      \"https://www.googleapis.com/oauth2/v4/token\"\n    ],\n    \"Name\": \"providers.#.token_url\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"uri\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=4.json",
    "content": "[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar\",\n    \"Default\": \"asdf\",\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": true,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"foo\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"list\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"list.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=5.json",
    "content": "[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar\",\n    \"Default\": \"asdf\",\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": true,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"foo\",\n    \"Default\": false,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"list\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"list.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=6.json",
    "content": "[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": {\n      \"foobar\": \"bar\"\n    }\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"foo\",\n    \"Default\": null,\n    \"Type\": false,\n    \"TypeHint\": 4,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=7.json",
    "content": "[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=8.json",
    "content": "[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"baz\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"baz.#\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 8,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"baz.#.#\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPaths-case=9.json",
    "content": "[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"baz\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"baz.#\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"baz.#.foo\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/.snapshots/TestListPathsWithRecursion-case=0.json",
    "content": "[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foo\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foo.bar\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foo.bar.foo\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foo.bar.foo.bar\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foo.bar.foo.bar.foo\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foo.bar.foo.bar.foos\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": 1,\n    \"MaxLength\": 10,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foo.bar.foo.bars\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"email\",\n    \"Pattern\": \".*\",\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foo.bar.foos\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": 1,\n    \"MaxLength\": 10,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foo.bars\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"email\",\n    \"Pattern\": \".*\",\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": true,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"bar.foos\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": 1,\n    \"MaxLength\": 10,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n"
  },
  {
    "path": "oryx/jsonschemax/README.md",
    "content": "# JSON Schema Helpers\n\nThis package contains utilities for working with JSON Schemas.\n\n## Listing all Possible JSON Schema Paths\n\nUsing `jsonschemax.ListPaths()` you can get a list of all possible JSON paths in\na JSON Schema.\n\n```go\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/x/jsonschemax\"\n)\n\nvar schema = \"...\"\n\nfunc main() {\n\tc := jsonschema.NewCompiler()\n\t_ = c.AddResource(\"test.json\", bytes.NewBufferString(schema))\n\tpaths, _ := jsonschemax.ListPaths(\"test.json\", c)\n\tfmt.Printf(\"%+v\", paths)\n}\n```\n\nAll keys are delimited using `.`. Please note that arrays are denoted with `#`\nwhen `ListPathsWithArraysIncluded` is used. For example, the JSON Schema\n\n```json\n{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"properties\": {\n    \"providers\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"id\": {\n            \"type\": \"string\"\n          }\n        }\n      }\n    }\n  }\n}\n```\n\nResults in paths:\n\n```json\n[\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers\",\n    \"Default\": null,\n    \"Type\": [],\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers.#\",\n    \"Default\": null,\n    \"Type\": {},\n    \"TypeHint\": 5,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  },\n  {\n    \"Title\": \"\",\n    \"Description\": \"\",\n    \"Examples\": null,\n    \"Name\": \"providers.#.id\",\n    \"Default\": null,\n    \"Type\": \"\",\n    \"TypeHint\": 1,\n    \"Format\": \"\",\n    \"Pattern\": null,\n    \"Enum\": null,\n    \"Constant\": null,\n    \"ReadOnly\": false,\n    \"MinLength\": -1,\n    \"MaxLength\": -1,\n    \"Required\": false,\n    \"Minimum\": null,\n    \"Maximum\": null,\n    \"MultipleOf\": null,\n    \"CustomProperties\": null\n  }\n]\n```\n"
  },
  {
    "path": "oryx/jsonschemax/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonschemax\n\nimport (\n\t\"github.com/ory/jsonschema/v3\"\n)\n\n// ErrorType is the schema error type.\ntype ErrorType int\n\nconst (\n\t// ErrorTypeMissing represents a validation that failed because a value is missing.\n\tErrorTypeMissing ErrorType = iota + 1\n)\n\n// Error represents a schema error.\ntype Error struct {\n\t// Type is the error type.\n\tType ErrorType\n\n\t// DocumentPointer is the JSON Pointer in the document.\n\tDocumentPointer string\n\n\t// SchemaPointer is the JSON Pointer in the schema.\n\tSchemaPointer string\n\n\t// DocumentFieldName is a pointer to the document in dot-notation: fo.bar.baz\n\tDocumentFieldName string\n}\n\n// NewFromSanthoshError converts github.com/santhosh-tekuri/jsonschema.ValidationError to Error.\nfunc NewFromSanthoshError(validationError jsonschema.ValidationError) *Error {\n\treturn &Error{\n\t\t// DocumentPointer:   JSONPointerToDotNotation(validationError.InstancePtr),\n\t\t// SchemaPointer:     JSONPointerToDotNotation(validationError.SchemaPtr),\n\t\t// DocumentFieldName: JSONPointerToDotNotation(validationError.InstancePtr),\n\t}\n}\n"
  },
  {
    "path": "oryx/jsonschemax/keys.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonschemax\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"regexp\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\ntype (\n\tbyName       []Path\n\tPathEnhancer interface {\n\t\tEnhancePath(Path) map[string]interface{}\n\t}\n\tTypeHint int\n)\n\nfunc (s byName) Len() int           { return len(s) }\nfunc (s byName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }\nfunc (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name }\n\nconst (\n\tString TypeHint = iota + 1\n\tFloat\n\tInt\n\tBool\n\tJSON\n\tNil\n\n\tBoolSlice\n\tStringSlice\n\tIntSlice\n\tFloatSlice\n)\n\n// Path represents a JSON Schema Path.\ntype Path struct {\n\t// Title of the path.\n\tTitle string\n\n\t// Description of the path.\n\tDescription string\n\n\t// Examples of the path.\n\tExamples []interface{}\n\n\t// Name is the JSON path name.\n\tName string\n\n\t// Default is the default value of that path.\n\tDefault interface{}\n\n\t// Type is a prototype (e.g. float64(0)) of the path type.\n\tType interface{}\n\n\tTypeHint\n\n\t// Format is the format of the path if defined\n\tFormat string\n\n\t// Pattern is the pattern of the path if defined\n\tPattern *regexp.Regexp\n\n\t// Enum are the allowed enum values\n\tEnum []interface{}\n\n\t// first element in slice is constant value. note: slice is used to capture nil constant.\n\tConstant []interface{}\n\n\t// ReadOnly is whether the value is readonly\n\tReadOnly bool\n\n\t// -1 if not specified\n\tMinLength int\n\tMaxLength int\n\n\t// Required if set indicates this field is required.\n\tRequired bool\n\n\tMinimum *big.Float\n\tMaximum *big.Float\n\n\tMultipleOf *big.Float\n\n\tCustomProperties map[string]interface{}\n}\n\n// ListPathsBytes works like ListPathsWithRecursion but prepares the JSON Schema itself.\nfunc ListPathsBytes(ctx context.Context, raw json.RawMessage, maxRecursion int16) ([]Path, error) {\n\tcompiler := jsonschema.NewCompiler()\n\tcompiler.ExtractAnnotations = true\n\tid := fmt.Sprintf(\"%x.json\", sha256.Sum256(raw))\n\tif err := compiler.AddResource(id, bytes.NewReader(raw)); err != nil {\n\t\treturn nil, err\n\t}\n\tcompiler.ExtractAnnotations = true\n\treturn runPathsFromCompiler(ctx, id, compiler, maxRecursion, false)\n}\n\n// ListPathsWithRecursion will follow circular references until maxRecursion is reached, without\n// returning an error.\nfunc ListPathsWithRecursion(ctx context.Context, ref string, compiler *jsonschema.Compiler, maxRecursion uint8) ([]Path, error) {\n\treturn runPathsFromCompiler(ctx, ref, compiler, int16(maxRecursion), false)\n}\n\n// ListPaths lists all paths of a JSON Schema. Will return an error\n// if circular references are found.\nfunc ListPaths(ctx context.Context, ref string, compiler *jsonschema.Compiler) ([]Path, error) {\n\treturn runPathsFromCompiler(ctx, ref, compiler, -1, false)\n}\n\n// ListPathsWithArraysIncluded lists all paths of a JSON Schema. Will return an error\n// if circular references are found.\n// Includes arrays with `#`.\nfunc ListPathsWithArraysIncluded(ctx context.Context, ref string, compiler *jsonschema.Compiler) ([]Path, error) {\n\treturn runPathsFromCompiler(ctx, ref, compiler, -1, true)\n}\n\n// ListPathsWithInitializedSchema loads the paths from the schema without compiling it.\n//\n// You MUST ensure that the compiler was using `ExtractAnnotations = true`.\nfunc ListPathsWithInitializedSchema(schema *jsonschema.Schema) ([]Path, error) {\n\treturn runPaths(schema, -1, false)\n}\n\n// ListPathsWithInitializedSchemaAndArraysIncluded loads the paths from the schema without compiling it.\n//\n// You MUST ensure that the compiler was using `ExtractAnnotations = true`.\n// Includes arrays with `#`.\nfunc ListPathsWithInitializedSchemaAndArraysIncluded(schema *jsonschema.Schema) ([]Path, error) {\n\treturn runPaths(schema, -1, true)\n}\n\nfunc runPathsFromCompiler(ctx context.Context, ref string, compiler *jsonschema.Compiler, maxRecursion int16, includeArrays bool) ([]Path, error) {\n\tif compiler == nil {\n\t\tcompiler = jsonschema.NewCompiler()\n\t}\n\n\tcompiler.ExtractAnnotations = true\n\n\tschema, err := compiler.Compile(ctx, ref)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn runPaths(schema, maxRecursion, includeArrays)\n}\n\nfunc runPaths(schema *jsonschema.Schema, maxRecursion int16, includeArrays bool) ([]Path, error) {\n\tpointers := map[string]bool{}\n\tpaths, err := listPaths(schema, nil, nil, pointers, 0, maxRecursion, includeArrays)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tsort.Stable(paths)\n\treturn makeUnique(paths)\n}\n\nfunc makeUnique(in byName) (byName, error) {\n\tcache := make(map[string]Path)\n\tfor _, p := range in {\n\t\tvc, ok := cache[p.Name]\n\t\tif !ok {\n\t\t\tcache[p.Name] = p\n\t\t\tcontinue\n\t\t}\n\n\t\tif fmt.Sprintf(\"%T\", p.Type) != fmt.Sprintf(\"%T\", p.Type) {\n\t\t\treturn nil, errors.Errorf(\"multiple types %+v are not supported for path: %s\", []interface{}{p.Type, vc.Type}, p.Name)\n\t\t}\n\n\t\tif vc.Default == nil {\n\t\t\tcache[p.Name] = p\n\t\t}\n\t}\n\n\tk := 0\n\tout := make([]Path, len(cache))\n\tfor _, v := range cache {\n\t\tout[k] = v\n\t\tk++\n\t}\n\n\tpaths := byName(out)\n\tsort.Sort(paths)\n\treturn paths, nil\n}\n\nfunc appendPointer(in map[string]bool, pointer *jsonschema.Schema) map[string]bool {\n\tout := make(map[string]bool)\n\tfor k, v := range in {\n\t\tout[k] = v\n\t}\n\tout[fmt.Sprintf(\"%p\", pointer)] = true\n\treturn out\n}\n\nfunc listPaths(schema *jsonschema.Schema, parent *jsonschema.Schema, parents []string, pointers map[string]bool, currentRecursion int16, maxRecursion int16, includeArrays bool) (byName, error) {\n\tvar pathType interface{}\n\tvar pathTypeHint TypeHint\n\tvar paths []Path\n\t_, isCircular := pointers[fmt.Sprintf(\"%p\", schema)]\n\n\tif len(schema.Constant) > 0 {\n\t\tswitch schema.Constant[0].(type) {\n\t\tcase float64, json.Number:\n\t\t\tpathType = float64(0)\n\t\t\tpathTypeHint = Float\n\t\tcase int8, int16, int, int64:\n\t\t\tpathType = int64(0)\n\t\t\tpathTypeHint = Int\n\t\tcase string:\n\t\t\tpathType = \"\"\n\t\t\tpathTypeHint = String\n\t\tcase bool:\n\t\t\tpathType = false\n\t\t\tpathTypeHint = Bool\n\t\tdefault:\n\t\t\tpathType = schema.Constant[0]\n\t\t\tpathTypeHint = JSON\n\t\t}\n\t} else if len(schema.Types) == 1 {\n\t\tswitch schema.Types[0] {\n\t\tcase \"null\":\n\t\t\tpathType = nil\n\t\t\tpathTypeHint = Nil\n\t\tcase \"boolean\":\n\t\t\tpathType = false\n\t\t\tpathTypeHint = Bool\n\t\tcase \"number\":\n\t\t\tpathType = float64(0)\n\t\t\tpathTypeHint = Float\n\t\tcase \"integer\":\n\t\t\tpathType = float64(0)\n\t\t\tpathTypeHint = Int\n\t\tcase \"string\":\n\t\t\tpathType = \"\"\n\t\t\tpathTypeHint = String\n\t\tcase \"array\":\n\t\t\tpathType = []interface{}{}\n\t\t\tif schema.Items != nil {\n\t\t\t\tvar itemSchemas []*jsonschema.Schema\n\t\t\t\tswitch t := schema.Items.(type) {\n\t\t\t\tcase []*jsonschema.Schema:\n\t\t\t\t\titemSchemas = t\n\t\t\t\tcase *jsonschema.Schema:\n\t\t\t\t\titemSchemas = []*jsonschema.Schema{t}\n\t\t\t\t}\n\t\t\t\tvar types []string\n\t\t\t\tfor _, is := range itemSchemas {\n\t\t\t\t\ttypes = append(types, is.Types...)\n\t\t\t\t\tif is.Ref != nil {\n\t\t\t\t\t\ttypes = append(types, is.Ref.Types...)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttypes = slices.Compact(types)\n\t\t\t\tif len(types) == 1 {\n\t\t\t\t\tswitch types[0] {\n\t\t\t\t\tcase \"boolean\":\n\t\t\t\t\t\tpathType = []bool{}\n\t\t\t\t\t\tpathTypeHint = BoolSlice\n\t\t\t\t\tcase \"number\":\n\t\t\t\t\t\tpathType = []float64{}\n\t\t\t\t\t\tpathTypeHint = FloatSlice\n\t\t\t\t\tcase \"integer\":\n\t\t\t\t\t\tpathType = []float64{}\n\t\t\t\t\t\tpathTypeHint = IntSlice\n\t\t\t\t\tcase \"string\":\n\t\t\t\t\t\tpathType = []string{}\n\t\t\t\t\t\tpathTypeHint = StringSlice\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpathType = []interface{}{}\n\t\t\t\t\t\tpathTypeHint = JSON\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"object\":\n\t\t\tpathType = map[string]interface{}{}\n\t\t\tpathTypeHint = JSON\n\t\t}\n\t} else if len(schema.Types) > 2 {\n\t\tpathType = nil\n\t\tpathTypeHint = JSON\n\t}\n\n\tvar def interface{} = schema.Default\n\tif v, ok := def.(json.Number); ok {\n\t\tdef, _ = v.Float64()\n\t}\n\n\tif (pathType != nil || schema.Default != nil) && len(parents) > 0 {\n\t\tname := parents[len(parents)-1]\n\t\tvar required bool\n\t\tif parent != nil {\n\t\t\tfor _, r := range parent.Required {\n\t\t\t\tif r == name {\n\t\t\t\t\trequired = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpath := Path{\n\t\t\tName:        strings.Join(parents, \".\"),\n\t\t\tDefault:     def,\n\t\t\tType:        pathType,\n\t\t\tTypeHint:    pathTypeHint,\n\t\t\tFormat:      schema.Format,\n\t\t\tPattern:     schema.Pattern,\n\t\t\tEnum:        schema.Enum,\n\t\t\tConstant:    schema.Constant,\n\t\t\tMinLength:   schema.MinLength,\n\t\t\tMaxLength:   schema.MaxLength,\n\t\t\tMinimum:     schema.Minimum,\n\t\t\tMaximum:     schema.Maximum,\n\t\t\tMultipleOf:  schema.MultipleOf,\n\t\t\tReadOnly:    schema.ReadOnly,\n\t\t\tTitle:       schema.Title,\n\t\t\tDescription: schema.Description,\n\t\t\tExamples:    schema.Examples,\n\t\t\tRequired:    required,\n\t\t}\n\n\t\tfor _, e := range schema.Extensions {\n\t\t\tif enhancer, ok := e.(PathEnhancer); ok {\n\t\t\t\tpath.CustomProperties = enhancer.EnhancePath(path)\n\t\t\t}\n\t\t}\n\t\tpaths = append(paths, path)\n\t}\n\n\tif isCircular {\n\t\tif maxRecursion == -1 {\n\t\t\treturn nil, errors.Errorf(\"detected circular dependency in schema path: %s\", strings.Join(parents, \".\"))\n\t\t} else if currentRecursion > maxRecursion {\n\t\t\treturn paths, nil\n\t\t}\n\t\tcurrentRecursion++\n\t}\n\n\tif schema.Ref != nil {\n\t\tpath, err := listPaths(schema.Ref, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tif schema.Not != nil {\n\t\tpath, err := listPaths(schema.Not, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tif schema.If != nil {\n\t\tpath, err := listPaths(schema.If, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tif schema.Then != nil {\n\t\tpath, err := listPaths(schema.Then, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tif schema.Else != nil {\n\t\tpath, err := listPaths(schema.Else, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tfor _, sub := range schema.AllOf {\n\t\tpath, err := listPaths(sub, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tfor _, sub := range schema.AnyOf {\n\t\tpath, err := listPaths(sub, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tfor _, sub := range schema.OneOf {\n\t\tpath, err := listPaths(sub, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tfor name, sub := range schema.Properties {\n\t\tpath, err := listPaths(sub, schema, append(parents, name), appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tif schema.Items != nil && includeArrays {\n\t\tswitch t := schema.Items.(type) {\n\t\tcase []*jsonschema.Schema:\n\t\t\tfor _, sub := range t {\n\t\t\t\tpath, err := listPaths(sub, schema, append(parents, \"#\"), appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tpaths = append(paths, path...)\n\t\t\t}\n\t\tcase *jsonschema.Schema:\n\t\t\tpath, err := listPaths(t, schema, append(parents, \"#\"), appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tpaths = append(paths, path...)\n\t\t}\n\t}\n\n\treturn paths, nil\n}\n"
  },
  {
    "path": "oryx/jsonschemax/pointer.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonschemax\n\nimport (\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// JSONPointerToDotNotation converts JSON Pointer \"#/foo/bar\" to dot-notation \"foo.bar\".\nfunc JSONPointerToDotNotation(pointer string) (string, error) {\n\tif !strings.HasPrefix(pointer, \"#/\") {\n\t\treturn pointer, errors.Errorf(\"remote JSON pointers are not supported: %s\", pointer)\n\t}\n\n\tvar path []string\n\tfor _, item := range strings.Split(strings.TrimPrefix(pointer, \"#/\"), \"/\") {\n\t\titem = strings.Replace(item, \"~1\", \"/\", -1)\n\t\titem = strings.Replace(item, \"~0\", \"~\", -1)\n\t\titem, err := url.PathUnescape(item)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tpath = append(path, strings.ReplaceAll(item, \".\", \"\\\\.\"))\n\t}\n\n\treturn strings.Join(path, \".\"), nil\n}\n"
  },
  {
    "path": "oryx/jsonschemax/print.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonschemax\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\nfunc FormatValidationErrorForCLI(w io.Writer, conf []byte, err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tif e := new(jsonschema.ValidationError); errors.As(err, &e) {\n\t\t_, _ = fmt.Fprintln(w, \"The configuration contains values or keys which are invalid:\")\n\t\tpointer, validation := FormatError(e)\n\n\t\tif pointer == \"#\" {\n\t\t\tif len(e.Causes) == 0 {\n\t\t\t\t_, _ = fmt.Fprintln(w, \"(root)\")\n\t\t\t\t_, _ = fmt.Fprintln(w, \"^-- \"+validation)\n\t\t\t\t_, _ = fmt.Fprintln(w, \"\")\n\t\t\t}\n\t\t} else {\n\t\t\tspaces := make([]string, len(pointer)+3)\n\t\t\t_, _ = fmt.Fprintf(w, \"%s: %+v\", pointer, gjson.GetBytes(conf, pointer).Value())\n\t\t\t_, _ = fmt.Fprintln(w, \"\")\n\t\t\t_, _ = fmt.Fprintf(w, \"%s^-- %s\", strings.Join(spaces, \" \"), validation)\n\t\t\t_, _ = fmt.Fprintln(w, \"\")\n\t\t\t_, _ = fmt.Fprintln(w, \"\")\n\t\t}\n\n\t\tfor _, cause := range e.Causes {\n\t\t\tFormatValidationErrorForCLI(w, conf, cause)\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc FormatError(e *jsonschema.ValidationError) (string, string) {\n\tvar (\n\t\terr     error\n\t\tpointer string\n\t\tmessage string\n\t)\n\n\tpointer = e.InstancePtr\n\tmessage = e.Message\n\tswitch ctx := e.Context.(type) {\n\tcase *jsonschema.ValidationErrorContextRequired:\n\t\tif len(ctx.Missing) > 0 {\n\t\t\tmessage = \"one or more required properties are missing\"\n\t\t\tpointer = ctx.Missing[0]\n\t\t}\n\t}\n\n\t// We can ignore the error as it will simply echo the pointer.\n\tpointer, err = JSONPointerToDotNotation(pointer)\n\tif err != nil {\n\t\tpointer = e.InstancePtr\n\t}\n\n\treturn pointer, message\n}\n"
  },
  {
    "path": "oryx/jsonschemax/stub/.config.yaml",
    "content": "dsn: memory\nitems:\n  - id: 1\n"
  },
  {
    "path": "oryx/jsonschemax/stub/.oathkeeper.schema.json",
    "content": "{\n  \"$id\": \"https://raw.githubusercontent.com/ory/oathkeeper/v0.32.1-beta.1/.schemas/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"ORY Oathkeeper Configuration\",\n  \"type\": \"object\",\n  \"definitions\": {\n    \"tlsxSource\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"path\": {\n          \"title\": \"Path to PEM-encoded Fle\",\n          \"type\": \"string\",\n          \"examples\": [\"path/to/file.pem\"]\n        },\n        \"base64\": {\n          \"title\": \"Base64 Encoded Inline\",\n          \"description\": \"The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\",\n          \"type\": \"string\",\n          \"examples\": [\n            \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr...\"\n          ]\n        }\n      }\n    },\n    \"tlsx\": {\n      \"title\": \"HTTPS\",\n      \"description\": \"Configure HTTP over TLS (HTTPS). All options can also be set using environment variables by replacing dots (`.`) with underscores (`_`) and uppercasing the key. For example, `some.prefix.tls.key.path` becomes `export SOME_PREFIX_TLS_KEY_PATH`. If all keys are left undefined, TLS will be disabled.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"key\": {\n          \"title\": \"Private Key (PEM)\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/tlsxSource\"\n            }\n          ]\n        },\n        \"cert\": {\n          \"title\": \"TLS Certificate (PEM)\",\n          \"allOf\": [\n            {\n              \"$ref\": \"#/definitions/tlsxSource\"\n            }\n          ]\n        }\n      }\n    },\n    \"cors\": {\n      \"title\": \"Cross Origin Resource Sharing (CORS)\",\n      \"description\": \"Configure [Cross Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) using the following options.\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"enabled\": {\n          \"type\": \"boolean\",\n          \"default\": false,\n          \"title\": \"Enable CORS\",\n          \"description\": \"If set to true, CORS will be enabled and preflight-requests (OPTION) will be answered.\"\n        },\n        \"allowed_origins\": {\n          \"title\": \"Allowed Origins\",\n          \"description\": \"A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality. Only one wildcard can be used per origin.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"minLength\": 1\n          },\n          \"default\": [\"*\"],\n          \"uniqueItems\": true,\n          \"examples\": [\n            \"https://example.com\",\n            \"https://*.example.com\",\n            \"https://*.foo.example.com\"\n          ]\n        },\n        \"allowed_methods\": {\n          \"type\": \"array\",\n          \"title\": \"Allowed HTTP Methods\",\n          \"description\": \"A list of methods the client is allowed to use with cross-domain requests.\",\n          \"items\": {\n            \"type\": \"string\",\n            \"enum\": [\n              \"GET\",\n              \"HEAD\",\n              \"POST\",\n              \"PUT\",\n              \"DELETE\",\n              \"CONNECT\",\n              \"TRACE\",\n              \"PATCH\"\n            ]\n          },\n          \"uniqueItems\": true,\n          \"default\": [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"]\n        },\n        \"allowed_headers\": {\n          \"description\": \"A list of non simple headers the client is allowed to use with cross-domain requests.\",\n          \"title\": \"Allowed Request HTTP Headers\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"minLength\": 1,\n          \"uniqueItems\": true,\n          \"default\": [\"Authorization\", \"Content-Type\"]\n        },\n        \"exposed_headers\": {\n          \"description\": \"Indicates which headers are safe to expose to the API of a CORS API specification\",\n          \"title\": \"Allowed Response HTTP Headers\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          },\n          \"minLength\": 1,\n          \"uniqueItems\": true,\n          \"default\": [\"Content-Type\"]\n        },\n        \"allow_credentials\": {\n          \"type\": \"boolean\",\n          \"title\": \"Allow HTTP Credentials\",\n          \"default\": false,\n          \"description\": \"Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\"\n        },\n        \"max_age\": {\n          \"type\": \"number\",\n          \"default\": 0,\n          \"title\": \"Maximum Age\",\n          \"description\": \"Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0 which stands for no max age.\"\n        },\n        \"debug\": {\n          \"type\": \"boolean\",\n          \"default\": false,\n          \"title\": \"Enable Debugging\",\n          \"description\": \"Set to true to debug server side CORS issues.\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"handlerSwitch\": {\n      \"title\": \"Enabled\",\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"examples\": [true],\n      \"description\": \"En-/disables this component.\"\n    },\n    \"scopeStrategy\": {\n      \"title\": \"Scope Strategy\",\n      \"type\": \"string\",\n      \"enum\": [\"hierarchic\", \"exact\", \"wildcard\", \"none\"],\n      \"default\": \"none\",\n      \"description\": \"Sets the strategy validation algorithm.\"\n    },\n    \"configAuthenticatorsAnonymous\": {\n      \"type\": \"object\",\n      \"title\": \"Anonymous Authenticator Configuration\",\n      \"description\": \"This section is optional when the authenticator is disabled.\",\n      \"properties\": {\n        \"subject\": {\n          \"type\": \"string\",\n          \"title\": \"Anonymous Subject\",\n          \"examples\": [\"guest\", \"anon\", \"anonymous\", \"unknown\"],\n          \"default\": \"anonymous\",\n          \"description\": \"Sets the anonymous username.\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"configAuthenticatorsCookieSession\": {\n      \"type\": \"object\",\n      \"title\": \"Cookie Session Authenticator Configuration\",\n      \"description\": \"This section is optional when the authenticator is disabled.\",\n      \"properties\": {\n        \"check_session_url\": {\n          \"title\": \"Session Check URL\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"description\": \"The origin to proxy requests to. If the response is a 200 with body `{ \\\"subject\\\": \\\"...\\\", \\\"extra\\\": {} }`. The request will pass the subject through successfully, otherwise it will be marked as unauthorized.\\n\\n>If this authenticator is enabled, this value is required.\",\n          \"examples\": [\"https://session-store-host\"]\n        },\n        \"only\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"additionalItems\": false\n          },\n          \"title\": \"Only Cookies\",\n          \"description\": \"A list of possible cookies to look for on incoming requests, and will fallthrough to the next authenticator if none of the passed cookies are set on the request.\"\n        }\n      },\n      \"required\": [\"check_session_url\"],\n      \"additionalProperties\": false\n    },\n    \"configAuthenticatorsJwt\": {\n      \"type\": \"object\",\n      \"title\": \"JWT Authenticator Configuration\",\n      \"description\": \"This section is optional when the authenticator is disabled.\",\n      \"required\": [\"jwks_urls\"],\n      \"properties\": {\n        \"required_scope\": {\n          \"type\": \"array\",\n          \"title\": \"Required Token Scope\",\n          \"description\": \"An array of OAuth 2.0 scopes that are required when accessing an endpoint protected by this handler.\\n If the token used in the Authorization header did not request that specific scope, the request is denied.\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"target_audience\": {\n          \"title\": \"Intended Audience\",\n          \"type\": \"array\",\n          \"description\": \"An array of audiences that are required when accessing an endpoint protected by this handler.\\n If the token used in the Authorization header is not intended for any of the requested audiences, the request is denied.\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"trusted_issuers\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"allowed_algorithms\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"jwks_urls\": {\n          \"title\": \"JSON Web Key URLs\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"format\": \"uri\"\n          },\n          \"description\": \"URLs where ORY Oathkeeper can retrieve JSON Web Keys from for validating the JSON Web Token. Usually something like \\\"https://my-keys.com/.well-known/jwks.json\\\". The response of that endpoint must return a JSON Web Key Set (JWKS).\\n\\n>If this authenticator is enabled, this value is required.\",\n          \"examples\": [\n            \"https://my-website.com/.well-known/jwks.json\",\n            \"https://my-other-website.com/.well-known/jwks.json\",\n            \"file://path/to/local/jwks.json\"\n          ]\n        },\n        \"scope_strategy\": {\n          \"$ref\": \"#/definitions/scopeStrategy\"\n        },\n        \"token_from\": {\n          \"title\": \"Token From\",\n          \"description\": \"The location of the token.\\n If not configured, the token will be received from a default location - 'Authorization' header.\\n One and only one location (header or query) must be specified.\",\n          \"oneOf\": [\n            {\n              \"type\": \"object\",\n              \"required\": [\"header\"],\n              \"properties\": {\n                \"header\": {\n                  \"title\": \"Header\",\n                  \"type\": \"string\",\n                  \"description\": \"The header (case insensitive) that must contain a token for request authentication. It can't be set along with query_parameter.\"\n                }\n              }\n            },\n            {\n              \"type\": \"object\",\n              \"required\": [\"query_parameter\"],\n              \"properties\": {\n                \"query_parameter\": {\n                  \"title\": \"Query Parameter\",\n                  \"type\": \"string\",\n                  \"description\": \"The query parameter (case sensitive) that must contain a token for request authentication. It can't be set along with header.\"\n                }\n              }\n            }\n          ]\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"configAuthenticatorsOauth2ClientCredentials\": {\n      \"type\": \"object\",\n      \"title\": \"OAuth 2.0 Client Credentials Authenticator Configuration\",\n      \"description\": \"This section is optional when the authenticator is disabled.\",\n      \"properties\": {\n        \"token_url\": {\n          \"type\": \"string\",\n          \"description\": \"The OAuth 2.0 Token Endpoint that will be used to validate the client credentials.\\n\\n>If this authenticator is enabled, this value is required.\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://my-website.com/oauth2/token\"]\n        },\n        \"required_scope\": {\n          \"type\": \"array\",\n          \"title\": \"Request Permissions (Token Scope)\",\n          \"description\": \"Scopes is an array of OAuth 2.0 scopes that are required when accessing an endpoint protected by this rule.\\n If the token used in the Authorization header did not request that specific scope, the request is denied.\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      },\n      \"required\": [\"token_url\"],\n      \"additionalProperties\": false\n    },\n    \"configAuthenticatorsOauth2Introspection\": {\n      \"type\": \"object\",\n      \"title\": \"OAuth 2.0 Introspection Authenticator Configuration\",\n      \"description\": \"This section is optional when the authenticator is disabled.\",\n      \"properties\": {\n        \"introspection_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"examples\": [\"https://my-website.com/oauth2/introspection\"],\n          \"title\": \"OAuth 2.0 Introspection URL\",\n          \"description\": \"The OAuth 2.0 Token Introspection endpoint URL.\\n\\n>If this authenticator is enabled, this value is required.\"\n        },\n        \"scope_strategy\": {\n          \"$ref\": \"#/definitions/scopeStrategy\"\n        },\n        \"pre_authorization\": {\n          \"title\": \"Pre-Authorization\",\n          \"description\": \"Enable pre-authorization in cases where the OAuth 2.0 Token Introspection endpoint is protected by OAuth 2.0 Bearer Tokens that can be retrieved using the OAuth 2.0 Client Credentials grant.\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"required\": [\"client_id\", \"client_secret\", \"token_url\"],\n          \"properties\": {\n            \"enabled\": {\n              \"const\": true\n            },\n            \"client_id\": {\n              \"type\": \"string\",\n              \"title\": \"OAuth 2.0 Client ID\",\n              \"description\": \"The OAuth 2.0 Client ID to be used for the OAuth 2.0 Client Credentials Grant.\\n\\n>If pre-authorization is enabled, this value is required.\"\n            },\n            \"client_secret\": {\n              \"type\": \"string\",\n              \"title\": \"OAuth 2.0 Client Secret\",\n              \"description\": \"The OAuth 2.0 Client Secret to be used for the OAuth 2.0 Client Credentials Grant.\\n\\n>If pre-authorization is enabled, this value is required.\"\n            },\n            \"token_url\": {\n              \"type\": \"string\",\n              \"format\": \"uri\",\n              \"title\": \"OAuth 2.0 Token URL\",\n              \"description\": \"The OAuth 2.0 Token Endpoint where the OAuth 2.0 Client Credentials Grant will be performed.\\n\\n>If pre-authorization is enabled, this value is required.\"\n            },\n            \"scope\": {\n              \"type\": \"array\",\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"title\": \"OAuth 2.0 Scope\",\n              \"description\": \"The OAuth 2.0 Scope to be requested during the OAuth 2.0 Client Credentials Grant.\",\n              \"examples\": [[\"[\\\"foo\\\", \\\"bar\\\"]\"]]\n            }\n          }\n        },\n        \"required_scope\": {\n          \"title\": \"Required Scope\",\n          \"description\": \"An array of OAuth 2.0 scopes that are required when accessing an endpoint protected by this handler.\\n If the token used in the Authorization header did not request that specific scope, the request is denied.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"target_audience\": {\n          \"title\": \"Target Audience\",\n          \"description\": \"An array of audiences that are required when accessing an endpoint protected by this handler.\\n If the token used in the Authorization header is not intended for any of the requested audiences, the request is denied.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"trusted_issuers\": {\n          \"title\": \"Trusted Issuers\",\n          \"description\": \"The token must have been issued by one of the issuers listed in this array.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"token_from\": {\n          \"title\": \"Token From\",\n          \"description\": \"The location of the token.\\n If not configured, the token will be received from a default location - 'Authorization' header.\\n One and only one location (header or query) must be specified.\",\n          \"type\": \"object\",\n          \"oneOf\": [\n            {\n              \"required\": [\"header\"],\n              \"properties\": {\n                \"header\": {\n                  \"title\": \"Header\",\n                  \"type\": \"string\",\n                  \"description\": \"The header (case insensitive) that must contain a token for request authentication.\\n It can't be set along with query_parameter.\"\n                }\n              }\n            },\n            {\n              \"required\": [\"query_parameter\"],\n              \"properties\": {\n                \"query_parameter\": {\n                  \"title\": \"Query Parameter\",\n                  \"type\": \"string\",\n                  \"description\": \"The query parameter (case sensitive) that must contain a token for request authentication.\\n It can't be set along with header.\"\n                }\n              }\n            }\n          ]\n        }\n      },\n      \"required\": [\"introspection_url\"],\n      \"additionalProperties\": false\n    },\n    \"configAuthorizersKetoEngineAcpOry\": {\n      \"type\": \"object\",\n      \"title\": \"ORY Keto Access Control Policy Authorizer Configuration\",\n      \"description\": \"This section is optional when the authorizer is disabled.\",\n      \"properties\": {\n        \"base_url\": {\n          \"title\": \"Base URL\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"description\": \"The base URL of ORY Keto.\\n\\n>If this authorizer is enabled, this value is required.\",\n          \"examples\": [\"http://my-keto/\"]\n        },\n        \"required_action\": {\n          \"type\": \"string\",\n          \"default\": \"unset\"\n        },\n        \"required_resource\": {\n          \"type\": \"string\",\n          \"default\": \"unset\"\n        },\n        \"subject\": {\n          \"type\": \"string\"\n        },\n        \"flavor\": {\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\"base_url\", \"required_action\", \"required_resource\"],\n      \"additionalProperties\": false\n    },\n    \"configMutatorsCookie\": {\n      \"type\": \"object\",\n      \"title\": \"Cookie Mutator Configuration\",\n      \"description\": \"This section is optional when the mutator is disabled.\",\n      \"required\": [\"cookies\"],\n      \"properties\": {\n        \"cookies\": {\n          \"type\": \"object\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          }\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"configMutatorsHeader\": {\n      \"type\": \"object\",\n      \"title\": \"Header Mutator Configuration\",\n      \"description\": \"This section is optional when the mutator is disabled.\",\n      \"required\": [\"headers\"],\n      \"properties\": {\n        \"headers\": {\n          \"type\": \"object\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          }\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"configMutatorsHydrator\": {\n      \"type\": \"object\",\n      \"title\": \"Hydrator Mutator Configuration\",\n      \"description\": \"This section is optional when the mutator is disabled.\",\n      \"properties\": {\n        \"api\": {\n          \"additionalProperties\": false,\n          \"required\": [\"url\"],\n          \"type\": \"object\",\n          \"properties\": {\n            \"url\": {\n              \"type\": \"string\",\n              \"format\": \"uri\"\n            },\n            \"auth\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"basic\": {\n                  \"required\": [\"username\", \"password\"],\n                  \"type\": \"object\",\n                  \"additionalProperties\": false,\n                  \"properties\": {\n                    \"username\": {\n                      \"type\": \"string\"\n                    },\n                    \"password\": {\n                      \"type\": \"string\"\n                    }\n                  }\n                }\n              }\n            },\n            \"retry\": {\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"number_of_retries\": {\n                  \"type\": \"number\",\n                  \"minimum\": 0,\n                  \"default\": 100\n                },\n                \"delay_in_milliseconds\": {\n                  \"type\": \"integer\",\n                  \"minimum\": 0,\n                  \"default\": 3\n                }\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\"api\"],\n      \"additionalProperties\": false\n    },\n    \"configMutatorsIdToken\": {\n      \"type\": \"object\",\n      \"title\": \"ID Token Mutator Configuration\",\n      \"description\": \"This section is optional when the mutator is disabled.\",\n      \"required\": [\"jwks_url\", \"issuer_url\"],\n      \"properties\": {\n        \"claims\": {\n          \"type\": \"string\"\n        },\n        \"issuer_url\": {\n          \"type\": \"string\",\n          \"title\": \"Issuer URL\",\n          \"description\": \"Sets the \\\"iss\\\" value of the ID Token.\\n\\n>If this mutator is enabled, this value is required.\"\n        },\n        \"jwks_url\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"title\": \"JSON Web Key URL\",\n          \"description\": \"Sets the URL where keys should be fetched from. Supports remote locations (http, https) as well as local filesystem paths.\\n\\n>If this mutator is enabled, this value is required.\",\n          \"examples\": [\n            \"https://fetch-keys/from/this/location.json\",\n            \"file:///from/this/absolute/location.json\",\n            \"file://../from/this/relative/location.json\"\n          ]\n        },\n        \"ttl\": {\n          \"type\": \"string\",\n          \"title\": \"Expire After\",\n          \"description\": \"Sets the time-to-live of the JSON Web Token.\",\n          \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n          \"default\": \"1m\",\n          \"examples\": [\"1h\", \"1m\", \"30s\"]\n        }\n      },\n      \"additionalProperties\": false\n    }\n  },\n  \"properties\": {\n    \"serve\": {\n      \"title\": \"HTTP(s)\",\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"api\": {\n          \"type\": \"object\",\n          \"title\": \"HTTP REST API\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"port\": {\n              \"type\": \"number\",\n              \"default\": 4456,\n              \"title\": \"Port\",\n              \"description\": \"The port to listen on.\"\n            },\n            \"host\": {\n              \"type\": \"string\",\n              \"default\": \"\",\n              \"examples\": [\"localhost\", \"127.0.0.1\"],\n              \"title\": \"Host\",\n              \"description\": \"The network interface to listen on.\"\n            },\n            \"cors\": {\n              \"$ref\": \"#/definitions/cors\"\n            },\n            \"tls\": {\n              \"$ref\": \"#/definitions/tlsx\"\n            }\n          }\n        },\n        \"proxy\": {\n          \"type\": \"object\",\n          \"title\": \"HTTP Reverse Proxy\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"port\": {\n              \"type\": \"number\",\n              \"default\": 4455,\n              \"title\": \"Port\",\n              \"description\": \"The port to listen on.\"\n            },\n            \"host\": {\n              \"type\": \"string\",\n              \"default\": \"\",\n              \"examples\": [\"localhost\", \"127.0.0.1\"],\n              \"title\": \"Host\",\n              \"description\": \"The network interface to listen on. Leave empty to listen on all interfaces.\"\n            },\n            \"timeout\": {\n              \"title\": \"HTTP Timeouts\",\n              \"description\": \"Control the reverse proxy's HTTP timeouts.\",\n              \"type\": \"object\",\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"read\": {\n                  \"title\": \"HTTP Read Timeout\",\n                  \"type\": \"string\",\n                  \"default\": \"5s\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"description\": \"The maximum duration for reading the entire request, including the body.\",\n                  \"examples\": [\"5s\", \"5m\", \"5h\"]\n                },\n                \"write\": {\n                  \"title\": \"HTTP Write Timeout\",\n                  \"type\": \"string\",\n                  \"default\": \"120s\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"description\": \"The maximum duration before timing out writes of the response. Increase this parameter to prevent unexpected closing a client connection if an upstream request is responding slowly.\",\n                  \"examples\": [\"5s\", \"5m\", \"5h\"]\n                },\n                \"idle\": {\n                  \"title\": \"HTTP Idle Timeout\",\n                  \"type\": \"string\",\n                  \"default\": \"120s\",\n                  \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n                  \"description\": \" The maximum amount of time to wait for any action of a request session, reading data or writing the response.\",\n                  \"examples\": [\"5s\", \"5m\", \"5h\"]\n                }\n              }\n            },\n            \"cors\": {\n              \"$ref\": \"#/definitions/cors\"\n            },\n            \"tls\": {\n              \"$ref\": \"#/definitions/tlsx\"\n            }\n          }\n        }\n      }\n    },\n    \"access_rules\": {\n      \"title\": \"Access Rules\",\n      \"description\": \"Configure access rules. All sub-keys support configuration reloading without restarting.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"repositories\": {\n          \"title\": \"Repositories\",\n          \"description\": \"Locations (list of URLs) where access rules should be fetched from on boot. It is expected that the documents at those locations return a JSON or YAML Array containing ORY Oathkeeper Access Rules:\\n\\n- If the URL Scheme is `file://`, the access rules (an array of access rules is expected) will be fetched from the local file system.\\n- If the URL Scheme is `inline://`, the access rules (an array of access rules is expected) are expected to be a base64 encoded (with padding!) JSON/YAML string (base64_encode(`[{\\\"id\\\":\\\"foo-rule\\\",\\\"authenticators\\\":[....]}]`)).\\n- If the URL Scheme is `http://` or `https://`, the access rules (an array of access rules is expected) will be fetched from the provided HTTP(s) location.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"format\": \"uri\"\n          },\n          \"examples\": [\n            \"[\\\"file://path/to/rules.json\\\",\\\"inline://W3siaWQiOiJmb28tcnVsZSIsImF1dGhlbnRpY2F0b3JzIjpbXX1d\\\",\\\"https://path-to-my-rules/rules.json\\\"]\"\n          ]\n        }\n      }\n    },\n    \"authenticators\": {\n      \"title\": \"Authenticators\",\n      \"type\": \"object\",\n      \"description\": \"For more information on authenticators head over to: https://www.ory.sh/docs/oathkeeper/pipeline/authn\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"anonymous\": {\n          \"title\": \"Anonymous\",\n          \"description\": \"The [`anonymous` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#anonymous).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configAuthenticatorsAnonymous\"\n            }\n          }\n        },\n        \"noop\": {\n          \"title\": \"No Operation (noop)\",\n          \"description\": \"The [`noop` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#noop).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            }\n          }\n        },\n        \"unauthorized\": {\n          \"title\": \"Unauthorized\",\n          \"description\": \"The [`unauthorized` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#unauthorized).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            }\n          }\n        },\n        \"cookie_session\": {\n          \"title\": \"Cookie Session\",\n          \"description\": \"The [`cookie_session` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#cookie_session).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configAuthenticatorsCookieSession\"\n            }\n          },\n          \"oneOf\": [\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": true\n                }\n              },\n              \"required\": [\"config\"]\n            },\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": false\n                }\n              }\n            }\n          ]\n        },\n        \"jwt\": {\n          \"title\": \"JSON Web Token (jwt)\",\n          \"description\": \"The [`jwt` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#jwt).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configAuthenticatorsJwt\"\n            }\n          },\n          \"oneOf\": [\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": true\n                }\n              },\n              \"required\": [\"config\"]\n            },\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": false\n                }\n              }\n            }\n          ]\n        },\n        \"oauth2_client_credentials\": {\n          \"title\": \"OAuth 2.0 Client Credentials\",\n          \"description\": \"The [`oauth2_client_credentials` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#oauth2_client_credentials).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configAuthenticatorsOauth2ClientCredentials\"\n            }\n          },\n          \"oneOf\": [\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": true\n                }\n              },\n              \"required\": [\"config\"]\n            },\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": false\n                }\n              }\n            }\n          ]\n        },\n        \"oauth2_introspection\": {\n          \"title\": \"OAuth 2.0 Token Introspection\",\n          \"description\": \"The [`oauth2_introspection` authenticator](https://www.ory.sh/docs/oathkeeper/pipeline/authn#oauth2_introspection).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configAuthenticatorsOauth2Introspection\"\n            }\n          },\n          \"oneOf\": [\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": true\n                }\n              },\n              \"required\": [\"config\"]\n            },\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": false\n                }\n              }\n            }\n          ]\n        }\n      }\n    },\n    \"authorizers\": {\n      \"title\": \"Authorizers\",\n      \"type\": \"object\",\n      \"description\": \"For more information on authorizers head over to: https://www.ory.sh/docs/oathkeeper/pipeline/authz\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"allow\": {\n          \"title\": \"Allow\",\n          \"description\": \"The [`allow` authorizer](https://www.ory.sh/docs/oathkeeper/pipeline/authz#allow).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            }\n          }\n        },\n        \"deny\": {\n          \"title\": \"Deny\",\n          \"description\": \"The [`deny` authorizer](https://www.ory.sh/docs/oathkeeper/pipeline/authz#allow).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            }\n          }\n        },\n        \"keto_engine_acp_ory\": {\n          \"title\": \"ORY Keto Access Control Policies Engine\",\n          \"description\": \"The [`keto_engine_acp_ory` authorizer](https://www.ory.sh/docs/oathkeeper/pipeline/authz#keto_engine_acp_ory).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configAuthorizersKetoEngineAcpOry\"\n            }\n          },\n          \"oneOf\": [\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": true\n                }\n              },\n              \"required\": [\"config\"]\n            },\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": false\n                }\n              }\n            }\n          ]\n        }\n      }\n    },\n    \"mutators\": {\n      \"title\": \"Mutators\",\n      \"type\": \"object\",\n      \"description\": \"For more information on mutators head over to: https://www.ory.sh/docs/oathkeeper/pipeline/mutator\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"noop\": {\n          \"title\": \"No Operation (noop)\",\n          \"description\": \"The [`noop` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#noop).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            }\n          }\n        },\n        \"cookie\": {\n          \"title\": \"HTTP Cookie\",\n          \"description\": \"The [`cookie` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#cookie).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configMutatorsCookie\"\n            }\n          },\n          \"oneOf\": [\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": true\n                }\n              },\n              \"required\": [\"config\"]\n            },\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": false\n                }\n              }\n            }\n          ]\n        },\n        \"header\": {\n          \"title\": \"HTTP Header\",\n          \"description\": \"The [`header` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#header).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configMutatorsHeader\"\n            }\n          },\n          \"oneOf\": [\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": true\n                }\n              },\n              \"required\": [\"config\"]\n            },\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": false\n                }\n              }\n            }\n          ]\n        },\n        \"hydrator\": {\n          \"title\": \"Hydrator\",\n          \"description\": \"The [`hydrator` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#hydrator).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configMutatorsHydrator\"\n            }\n          },\n          \"oneOf\": [\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": true\n                }\n              },\n              \"required\": [\"config\"]\n            },\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": false\n                }\n              }\n            }\n          ]\n        },\n        \"id_token\": {\n          \"title\": \"ID Token (JSON Web Token)\",\n          \"description\": \"The [`id_token` mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#id_token).\",\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"properties\": {\n            \"enabled\": {\n              \"$ref\": \"#/definitions/handlerSwitch\"\n            },\n            \"config\": {\n              \"$ref\": \"#/definitions/configMutatorsIdToken\"\n            }\n          },\n          \"oneOf\": [\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": true\n                }\n              },\n              \"required\": [\"config\"]\n            },\n            {\n              \"properties\": {\n                \"enabled\": {\n                  \"const\": false\n                }\n              }\n            }\n          ]\n        }\n      }\n    },\n    \"log\": {\n      \"title\": \"Log\",\n      \"description\": \"Configure logging using the following options. Logging will always be sent to stdout and stderr.\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"level\": {\n          \"type\": \"string\",\n          \"default\": \"info\",\n          \"enum\": [\"panic\", \"fatal\", \"error\", \"warn\", \"info\", \"debug\"],\n          \"title\": \"Level\",\n          \"description\": \"Debug enables stack traces on errors. Can also be set using environment variable LOG_LEVEL.\"\n        },\n        \"format\": {\n          \"type\": \"string\",\n          \"default\": \"text\",\n          \"enum\": [\"text\", \"json\"],\n          \"title\": \"Format\",\n          \"description\": \"The log format can either be text or JSON.\"\n        }\n      },\n      \"additionalProperties\": false\n    },\n    \"profiling\": {\n      \"title\": \"Profiling\",\n      \"description\": \"Enables CPU or memory profiling if set. For more details on profiling Go programs read [Profiling Go Programs](https://blog.golang.org/profiling-go-programs).\",\n      \"type\": \"string\",\n      \"enum\": [\"cpu\", \"mem\"]\n    }\n  },\n  \"required\": [],\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "oryx/jsonschemax/stub/config.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/config.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"config\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"dsn\": {\n      \"type\": \"string\"\n    }\n  },\n  \"required\": [\"dsn\"]\n}\n"
  },
  {
    "path": "oryx/jsonschemax/stub/json/.project-stub-name.json",
    "content": "{\n  \"serve\": {\n    \"admin\": {\n      \"port\": 1\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/jsonschemax/stub/nested-array.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"properties\": {\n    \"providers\": {\n      \"title\": \"OpenID Connect and OAuth2 Providers\",\n      \"description\": \"A list and configuration of OAuth2 and OpenID Connect providers ORY Kratos should integrate with.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"id\": {\n            \"type\": \"string\",\n            \"examples\": [\"google\"]\n          },\n          \"provider\": {\n            \"title\": \"Provider\",\n            \"description\": \"Can be one of github, gitlab, generic, google, microsoft, discord.\",\n            \"type\": \"string\",\n            \"enum\": [\n              \"github\",\n              \"gitlab\",\n              \"generic\",\n              \"google\",\n              \"microsoft\",\n              \"discord\"\n            ],\n            \"examples\": [\"google\"]\n          },\n          \"client_id\": {\n            \"type\": \"string\"\n          },\n          \"client_secret\": {\n            \"type\": \"string\"\n          },\n          \"issuer_url\": {\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"examples\": [\"https://accounts.google.com\"]\n          },\n          \"auth_url\": {\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"examples\": [\"https://accounts.google.com/o/oauth2/v2/auth\"]\n          },\n          \"token_url\": {\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"examples\": [\"https://www.googleapis.com/oauth2/v4/token\"]\n          },\n          \"mapper_url\": {\n            \"title\": \"Jsonnet Mapper URL\",\n            \"description\": \"The URL where the jsonnet source is located for mapping the provider's data to ORY Kratos data.\",\n            \"type\": \"string\",\n            \"format\": \"uri\",\n            \"examples\": [\n              \"file://path/to/oidc.jsonnet\",\n              \"https://foo.bar.com/path/to/oidc.jsonnet\",\n              \"base64://bG9jYWwgc3ViamVjdCA9I...\"\n            ]\n          },\n          \"scope\": {\n            \"type\": \"array\",\n            \"items\": {\n              \"type\": \"string\",\n              \"examples\": [\"offline_access\", \"profile\"]\n            }\n          },\n          \"tenant\": {\n            \"title\": \"Azure AD Tenant\",\n            \"description\": \"The Azure AD Tenant to use for authentication.\",\n            \"type\": \"string\",\n            \"examples\": [\n              \"common\",\n              \"organizations\",\n              \"consumers\",\n              \"8eaef023-2b34-4da1-9baa-8bc8c9d6a490\",\n              \"contoso.onmicrosoft.com\"\n            ]\n          }\n        },\n        \"additionalProperties\": false,\n        \"required\": [],\n        \"if\": {\n          \"properties\": {\n            \"provider\": {\n              \"const\": \"microsoft\"\n            }\n          },\n          \"required\": [\"provider\"]\n        },\n        \"then\": {\n          \"required\": [\"tenant\"]\n        },\n        \"else\": {\n          \"not\": {\n            \"properties\": {\n              \"tenant\": {}\n            },\n            \"required\": [\"tenant\"]\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/jsonschemax/stub/nested-simple-array.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"properties\": {\n    \"providers\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"id\": {\n            \"type\": \"string\"\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/jsonschemax/stub/toml/.project-stub-name.toml",
    "content": "[serve]\n\n  [serve.admin]\n  port = \"2\""
  },
  {
    "path": "oryx/jsonschemax/stub/yaml/.project-stub-name.yaml",
    "content": "# serve controls the configuration for the http(s) daemon\nserve:\n  admin:\n    port: 3\n"
  },
  {
    "path": "oryx/jsonschemax/stub/yml/.project-stub-name.yml",
    "content": "serve:\n  admin:\n    port: 4\n"
  },
  {
    "path": "oryx/jsonx/.snapshots/TestEmbedSources-fixtures-fixture=1.json.json",
    "content": "\"foo\"\n"
  },
  {
    "path": "oryx/jsonx/.snapshots/TestEmbedSources-fixtures-fixture=2.json.json",
    "content": "{\n  \"some\": \"key\"\n}\n"
  },
  {
    "path": "oryx/jsonx/.snapshots/TestEmbedSources-fixtures-fixture=3.json.json",
    "content": "{\n  \"some_key\": 1234\n}\n"
  },
  {
    "path": "oryx/jsonx/.snapshots/TestEmbedSources-fixtures-fixture=4.json.json",
    "content": "{\n  \"nested\": {\n    \"object\": {\n      \"source\": \"base64://aGVsbG8gd29ybGQ=\"\n    },\n    \"array\": [\n      {\n        \"nested\": {\n          \"source\": \"base64://aGVsbG8gd29ybGQ=\"\n        }\n      },\n      \"base64://aGVsbG8gd29ybGQ=\"\n    ]\n  }\n}\n"
  },
  {
    "path": "oryx/jsonx/.snapshots/TestEmbedSources-fixtures-fixture=5.json.json",
    "content": "\"https://gist.githubusercontent.com/aeneasr/eb4612d295f613ee44bada6e30e2a856/raw/29edbda41bcb27492a1ac56926e03dee9480708f/hello-world.txt\"\n"
  },
  {
    "path": "oryx/jsonx/.snapshots/TestEmbedSources-fixtures-fixture=6.json.json",
    "content": "{\n  \"nested\": {\n    \"object\": {\n      \"ignore_this_key\": \"https://gist.githubusercontent.com/aeneasr/eb4612d295f613ee44bada6e30e2a856/raw/29edbda41bcb27492a1ac56926e03dee9480708f/hello-world.txt\"\n    },\n    \"array\": [\n      {\n        \"nested\": {\n          \"source\": \"base64://aGVsbG8gd29ybGQ=\"\n        }\n      },\n      \"base64://aGVsbG8gd29ybGQ=\"\n    ]\n  }\n}\n"
  },
  {
    "path": "oryx/jsonx/.snapshots/TestEmbedSources-only_embeds_base64.json",
    "content": "{\n  \"key\": \"https://foobar.com\",\n  \"bar\": \"base64://YXNkZg==\"\n}\n"
  },
  {
    "path": "oryx/jsonx/debug.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonx\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"slices\"\n)\n\n// Anonymize takes a JSON byte array and anonymizes its content by\n// recursively replacing all values with a string indicating their type.\n//\n// It recurses into nested objects and arrays, but ignores the \"schemas\" and \"id\".\nfunc Anonymize(data []byte, except ...string) []byte {\n\tobj := make(map[string]any)\n\tif err := json.Unmarshal(data, &obj); err != nil {\n\t\treturn []byte(fmt.Sprintf(`{\"error\": \"invalid JSON\", \"message\": %q}`, err.Error()))\n\t}\n\n\tanonymize(obj, except...)\n\n\tout, err := json.MarshalIndent(obj, \"\", \"  \")\n\tif err != nil {\n\t\treturn []byte(fmt.Sprintf(`{\"error\": \"could not marshal JSON shape\", \"message\": %q}`, err.Error()))\n\t}\n\n\treturn out\n}\n\nfunc anonymize(obj map[string]any, except ...string) {\n\tfor k, v := range obj {\n\t\tif slices.Contains(except, k) {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch v := v.(type) {\n\t\tcase []any:\n\t\t\tfor elIdx, el := range v {\n\t\t\t\tswitch el := el.(type) {\n\t\t\t\tcase map[string]any:\n\t\t\t\t\tanonymize(el)\n\t\t\t\t\tv[elIdx] = el\n\t\t\t\tdefault:\n\t\t\t\t\tv[elIdx] = jsonType(el)\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase map[string]any:\n\t\t\tanonymize(v)\n\t\t\tobj[k] = v\n\t\tdefault:\n\t\t\tobj[k] = jsonType(v)\n\t\t}\n\t}\n}\n\nfunc jsonType(v any) string {\n\tswitch v := v.(type) {\n\tcase string:\n\t\treturn \"string\"\n\tcase float64:\n\t\treturn \"number\"\n\tcase bool:\n\t\treturn \"boolean\"\n\tcase nil:\n\t\treturn \"null\"\n\tcase []any:\n\t\treturn \"array\"\n\tcase map[string]any:\n\t\treturn \"object\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"%T\", v)\n\t}\n}\n"
  },
  {
    "path": "oryx/jsonx/decoder.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonx\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// NewStrictDecoder is a shorthand for json.Decoder.DisallowUnknownFields\nfunc NewStrictDecoder(b io.Reader) *json.Decoder {\n\td := json.NewDecoder(b)\n\td.DisallowUnknownFields()\n\treturn d\n}\n"
  },
  {
    "path": "oryx/jsonx/embed.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonx\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/x/osx\"\n)\n\ntype options struct {\n\tignoreKeys  []string\n\tonlySchemes []string\n}\n\ntype OptionsModifier func(*options)\n\nfunc newOptions(o []OptionsModifier) *options {\n\topt := &options{}\n\tfor _, f := range o {\n\t\tf(opt)\n\t}\n\treturn opt\n}\n\nfunc WithIgnoreKeys(keys ...string) OptionsModifier {\n\treturn func(o *options) {\n\t\to.ignoreKeys = keys\n\t}\n}\n\nfunc WithOnlySchemes(scheme ...string) OptionsModifier {\n\treturn func(o *options) {\n\t\to.onlySchemes = scheme\n\t}\n}\n\nfunc EmbedSources(in json.RawMessage, opts ...OptionsModifier) (out json.RawMessage, err error) {\n\tout = make([]byte, len(in))\n\tcopy(out, in)\n\tif err := embed(gjson.ParseBytes(in), nil, &out, newOptions(opts)); err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc embed(parsed gjson.Result, parents []string, result *json.RawMessage, o *options) (err error) {\n\tif parsed.IsObject() {\n\t\tparsed.ForEach(func(k, v gjson.Result) bool {\n\t\t\terr = embed(v, append(parents, strings.ReplaceAll(k.String(), \".\", \"\\\\.\")), result, o)\n\t\t\treturn err == nil\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if parsed.IsArray() {\n\t\tfor kk, vv := range parsed.Array() {\n\t\t\tif err = embed(vv, append(parents, strconv.Itoa(kk)), result, o); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t} else if parsed.Type != gjson.String {\n\t\treturn nil\n\t}\n\n\tif len(parents) > 0 && slices.Contains(o.ignoreKeys, parents[len(parents)-1]) {\n\t\treturn nil\n\t}\n\n\tloc, err := url.ParseRequestURI(parsed.String())\n\tif err != nil {\n\t\t// Not a URL, return\n\t\treturn nil\n\t}\n\n\tif len(o.onlySchemes) == 0 {\n\t\tif loc.Scheme != \"file\" && loc.Scheme != \"http\" && loc.Scheme != \"https\" && loc.Scheme != \"base64\" {\n\t\t\t// Not a known pattern, ignore\n\t\t\treturn nil\n\t\t}\n\t} else if !slices.Contains(o.onlySchemes, loc.Scheme) {\n\t\t// Not a known pattern, ignore\n\t\treturn nil\n\t}\n\n\tcontents, err := osx.ReadFileFromAllSources(loc.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tencoded := base64.StdEncoding.EncodeToString(contents)\n\tkey := strings.Join(parents, \".\")\n\tif key == \"\" {\n\t\tkey = \"@\"\n\t}\n\n\tinterim, err := sjson.SetBytes(*result, key, \"base64://\"+encoded)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*result = interim\n\treturn\n}\n"
  },
  {
    "path": "oryx/jsonx/fixture/embed/1.json",
    "content": "\"foo\"\n"
  },
  {
    "path": "oryx/jsonx/fixture/embed/2.json",
    "content": "{\n  \"some\": \"key\"\n}\n"
  },
  {
    "path": "oryx/jsonx/fixture/embed/3.json",
    "content": "{\n  \"some_key\": 1234\n}\n"
  },
  {
    "path": "oryx/jsonx/fixture/embed/4.json",
    "content": "{\n  \"nested\": {\n    \"object\": {\n      \"source\": \"https://gist.githubusercontent.com/aeneasr/eb4612d295f613ee44bada6e30e2a856/raw/29edbda41bcb27492a1ac56926e03dee9480708f/hello-world.txt\"\n    },\n    \"array\": [\n      {\n        \"nested\": {\n          \"source\": \"https://gist.githubusercontent.com/aeneasr/eb4612d295f613ee44bada6e30e2a856/raw/29edbda41bcb27492a1ac56926e03dee9480708f/hello-world.txt\"\n        }\n      },\n      \"https://gist.githubusercontent.com/aeneasr/eb4612d295f613ee44bada6e30e2a856/raw/29edbda41bcb27492a1ac56926e03dee9480708f/hello-world.txt\"\n    ]\n  }\n}\n"
  },
  {
    "path": "oryx/jsonx/fixture/embed/5.json",
    "content": "\"https://gist.githubusercontent.com/aeneasr/eb4612d295f613ee44bada6e30e2a856/raw/29edbda41bcb27492a1ac56926e03dee9480708f/hello-world.txt\"\n"
  },
  {
    "path": "oryx/jsonx/fixture/embed/6.json",
    "content": "{\n  \"nested\": {\n    \"object\": {\n      \"ignore_this_key\": \"https://gist.githubusercontent.com/aeneasr/eb4612d295f613ee44bada6e30e2a856/raw/29edbda41bcb27492a1ac56926e03dee9480708f/hello-world.txt\"\n    },\n    \"array\": [\n      {\n        \"nested\": {\n          \"source\": \"https://gist.githubusercontent.com/aeneasr/eb4612d295f613ee44bada6e30e2a856/raw/29edbda41bcb27492a1ac56926e03dee9480708f/hello-world.txt\"\n        }\n      },\n      \"https://gist.githubusercontent.com/aeneasr/eb4612d295f613ee44bada6e30e2a856/raw/29edbda41bcb27492a1ac56926e03dee9480708f/hello-world.txt\"\n    ]\n  }\n}\n"
  },
  {
    "path": "oryx/jsonx/flatten.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonx\n\nimport (\n\t\"encoding/json\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/tidwall/gjson\"\n)\n\n// Flatten flattens a JSON object using dot notation.\nfunc Flatten(raw json.RawMessage) map[string]interface{} {\n\tparsed := gjson.ParseBytes(raw)\n\tif !parsed.IsObject() {\n\t\treturn nil\n\t}\n\n\tflattened := make(map[string]interface{})\n\tflatten(parsed, nil, flattened)\n\treturn flattened\n}\n\nfunc flatten(parsed gjson.Result, parents []string, flattened map[string]interface{}) {\n\tif parsed.IsObject() {\n\t\tparsed.ForEach(func(k, v gjson.Result) bool {\n\t\t\tflatten(v, append(parents, strings.ReplaceAll(k.String(), \".\", \"\\\\.\")), flattened)\n\t\t\treturn true\n\t\t})\n\t} else if parsed.IsArray() {\n\t\tfor kk, vv := range parsed.Array() {\n\t\t\tflatten(vv, append(parents, strconv.Itoa(kk)), flattened)\n\t\t}\n\t} else {\n\t\tflattened[strings.Join(parents, \".\")] = parsed.Value()\n\t}\n}\n"
  },
  {
    "path": "oryx/jsonx/get.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonx\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n)\n\nfunc jsonKey(f reflect.StructField) *string {\n\tif jsonTag := f.Tag.Get(\"json\"); jsonTag != \"\" {\n\t\tif jsonTag == \"-\" {\n\t\t\treturn nil\n\t\t}\n\t\treturn &strings.Split(jsonTag, \",\")[0]\n\t} else if f.Anonymous {\n\t\treturn nil\n\t} else if f.IsExported() {\n\t\treturn &f.Name\n\t}\n\treturn nil\n}\n\n// AllValidJSONKeys returns all JSON keys from the struct or *struct type.\n// It does not return keys from nested slices, but embedded/nested structs.\nfunc AllValidJSONKeys(s interface{}) (keys []string) {\n\tt := reflect.TypeOf(s)\n\tv := reflect.ValueOf(s)\n\tif t.Kind() == reflect.Pointer {\n\t\tt = t.Elem()\n\t\tv = v.Elem()\n\t}\n\tfor i := range t.NumField() {\n\t\tf := t.Field(i)\n\t\tjKey := jsonKey(f)\n\t\tif k := f.Type.Kind(); k == reflect.Struct || k == reflect.Pointer {\n\t\t\tsubKeys := AllValidJSONKeys(v.Field(i).Interface())\n\t\t\tfor _, subKey := range subKeys {\n\t\t\t\tif jKey != nil {\n\t\t\t\t\tkeys = append(keys, *jKey+\".\"+subKey)\n\t\t\t\t} else {\n\t\t\t\t\tkeys = append(keys, subKey)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if jKey != nil {\n\t\t\tkeys = append(keys, *jKey)\n\t\t}\n\t}\n\treturn keys\n}\n\n// ParseEnsureKeys returns a result that has the GetRequireValidKey function.\nfunc ParseEnsureKeys(original interface{}, raw []byte) *Result {\n\treturn &Result{\n\t\tkeys:   AllValidJSONKeys(original),\n\t\tresult: gjson.ParseBytes(raw),\n\t}\n}\n\ntype Result struct {\n\tresult gjson.Result\n\tkeys   []string\n}\n\n// GetRequireValidKey ensures that the key is valid before returning the result.\nfunc (r *Result) GetRequireValidKey(t require.TestingT, key string) gjson.Result {\n\trequire.Contains(t, r.keys, key)\n\treturn r.result.Get(key)\n}\n\nfunc GetRequireValidKey(t require.TestingT, original interface{}, raw []byte, key string) gjson.Result {\n\treturn ParseEnsureKeys(original, raw).GetRequireValidKey(t, key)\n}\n"
  },
  {
    "path": "oryx/jsonx/helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonx\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMarshalJSONString(t *testing.T, i interface{}) string {\n\tout, err := json.Marshal(i)\n\trequire.NoError(t, err)\n\treturn string(out)\n}\n"
  },
  {
    "path": "oryx/jsonx/patch.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jsonx\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\tjsonpatch \"github.com/evanphx/json-patch/v5\"\n\t\"github.com/gobwas/glob\"\n\t\"github.com/pkg/errors\"\n)\n\nvar opAllowList = map[string]struct{}{\n\t\"add\":     {},\n\t\"remove\":  {},\n\t\"replace\": {},\n}\n\nfunc isUnsupported(op jsonpatch.Operation) bool {\n\t_, ok := opAllowList[op.Kind()]\n\n\treturn !ok\n}\n\nfunc isElementAccess(path string) bool {\n\tif path == \"\" {\n\t\treturn false\n\t}\n\telements := strings.Split(path, \"/\")\n\tlastElement := elements[len(elements)-1:][0]\n\tif lastElement == \"-\" {\n\t\treturn true\n\t}\n\tif _, err := strconv.Atoi(lastElement); err == nil {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// ApplyJSONPatch applies a JSON patch to an object and returns the modified\n// object. The original object is not modified. It returns an error if the patch\n// is invalid or if the patch includes paths that are denied. denyPaths is a\n// list of path globs (interpreted with [glob.Compile] that are not allowed to\n// be patched.\nfunc ApplyJSONPatch[T any](p json.RawMessage, object T, denyPaths ...string) (result T, err error) {\n\tpatch, err := jsonpatch.DecodePatch(p)\n\tif err != nil {\n\t\treturn result, errors.WithStack(err)\n\t}\n\n\tdenyPattern := fmt.Sprintf(\"{%s}\", strings.ToLower(strings.Join(denyPaths, \",\")))\n\tmatcher, err := glob.Compile(denyPattern, '/')\n\tif err != nil {\n\t\treturn result, errors.WithStack(err)\n\t}\n\n\tfor _, op := range patch {\n\t\t// Some operations are buggy, see https://github.com/evanphx/json-patch/pull/158\n\t\tif isUnsupported(op) {\n\t\t\treturn result, errors.Errorf(\"unsupported operation: %s\", op.Kind())\n\t\t}\n\t\tpath, err := op.Path()\n\t\tif err != nil {\n\t\t\treturn result, errors.Errorf(\"error parsing patch operations: %v\", err)\n\t\t}\n\t\tif matcher.Match(strings.ToLower(path)) {\n\t\t\treturn result, errors.Errorf(\"patch includes denied path: %s\", path)\n\t\t}\n\n\t\t// JSON patch officially rejects replacing paths that don't exist, but we want to be more tolerant.\n\t\t// Therefore, we will ensure that all paths that we want to replace exist in the original document.\n\t\tif op.Kind() == \"replace\" && !isElementAccess(path) {\n\t\t\top[\"op\"] = new(json.RawMessage(`\"add\"`))\n\t\t}\n\t}\n\n\toriginal, err := json.Marshal(object)\n\tif err != nil {\n\t\treturn result, errors.WithStack(err)\n\t}\n\n\toptions := jsonpatch.NewApplyOptions()\n\toptions.EnsurePathExistsOnAdd = true\n\n\tmodified, err := patch.ApplyWithOptions(original, options)\n\tif err != nil {\n\t\treturn result, errors.WithStack(err)\n\t}\n\n\terr = json.Unmarshal(modified, &result)\n\treturn result, errors.WithStack(err)\n}\n"
  },
  {
    "path": "oryx/jsonx/stub/random.json",
    "content": "{\n  \"floating\": [\n    -1273085434,\n    953442581,\n    {\n      \"ready\": \"silent\",\n      \"worker\": \"situation\",\n      \"joy\": \"difference\",\n      \"probably\": -413625494,\n      \"gray\": {\n        \"parent\": \"pull\",\n        \"shore\": -738396277,\n        \"usually\": 1050049449,\n        \"hold\": [\n          [\n            181518765,\n            [\n              {\n                \"steam\": {\n                  \"box\": false,\n                  \"cry\": 1463961818,\n                  \"appropriate\": 1249911539,\n                  \"through\": 695239749,\n                  \"ago\": true,\n                  \"entirely\": -851427469\n                },\n                \"leather\": \"across\",\n                \"flies\": -1571371799,\n                \"over\": 512666854,\n                \"thank\": true,\n                \"shaking\": true\n              },\n              \"hit\",\n              -648178744.4899056,\n              1225027271,\n              -1481507228,\n              true\n            ],\n            -2114582277,\n            1390060204.9360588,\n            1615602630.9049141,\n            \"darkness\"\n          ],\n          63427197.713988304,\n          -580344963.961421,\n          \"stems\",\n          1016960217.612642,\n          1240918909\n        ],\n        \"buy\": true,\n        \"wonder\": false\n      },\n      \"little\": \"cloud\"\n    },\n    \"grade\",\n    false,\n    \"thou\"\n  ],\n  \"wagon\": -1722583702,\n  \"shop\": 1294397217,\n  \"spend\": \"greatest\",\n  \"product\": \"whale\",\n  \"fall\": \"to\"\n}\n"
  },
  {
    "path": "oryx/jwksx/.snapshots/TestFetcherNext-case=resolve_multiple_source_urls-case=succeeds_with_forced_kid.json",
    "content": "{\n  \"alg\": \"HS256\",\n  \"k\": \"Y2hhbmdlbWVjaGFuZ2VtZWNoYW5nZW1lY2hhbmdlbWU\",\n  \"kid\": \"8d5f5ad0674ec2f2960b1a34f33370a0f71471fa0e3ef0c0a692977d276dafe8\",\n  \"kty\": \"oct\",\n  \"use\": \"sig\"\n}\n"
  },
  {
    "path": "oryx/jwksx/.snapshots/TestFetcherNext-case=resolve_single_source_url-case=with_cache.json",
    "content": "{\n  \"alg\": \"HS256\",\n  \"k\": \"Y2hhbmdlbWVjaGFuZ2VtZWNoYW5nZW1lY2hhbmdlbWU\",\n  \"kid\": \"7d5f5ad0674ec2f2960b1a34f33370a0f71471fa0e3ef0c0a692977d276dafe8\",\n  \"kty\": \"oct\",\n  \"use\": \"sig\"\n}\n"
  },
  {
    "path": "oryx/jwksx/.snapshots/TestFetcherNext-case=resolve_single_source_url-case=with_cache_and_TTL.json",
    "content": "{\n  \"alg\": \"HS256\",\n  \"k\": \"Y2hhbmdlbWVjaGFuZ2VtZWNoYW5nZW1lY2hhbmdlbWU\",\n  \"kid\": \"7d5f5ad0674ec2f2960b1a34f33370a0f71471fa0e3ef0c0a692977d276dafe8\",\n  \"kty\": \"oct\",\n  \"use\": \"sig\"\n}\n"
  },
  {
    "path": "oryx/jwksx/.snapshots/TestFetcherNext-case=resolve_single_source_url-case=with_forced_key.json",
    "content": "{\n  \"alg\": \"HS256\",\n  \"k\": \"Y2hhbmdlbWVjaGFuZ2VtZWNoYW5nZW1lY2hhbmdlbWU\",\n  \"kid\": \"7d5f5ad0674ec2f2960b1a34f33370a0f71471fa0e3ef0c0a692977d276dafe8\",\n  \"kty\": \"oct\",\n  \"use\": \"sig\"\n}\n"
  },
  {
    "path": "oryx/jwksx/.snapshots/TestFetcherNext-case=resolve_single_source_url-case=without_cache.json",
    "content": "{\n  \"alg\": \"HS256\",\n  \"k\": \"Y2hhbmdlbWVjaGFuZ2VtZWNoYW5nZW1lY2hhbmdlbWU\",\n  \"kid\": \"7d5f5ad0674ec2f2960b1a34f33370a0f71471fa0e3ef0c0a692977d276dafe8\",\n  \"kty\": \"oct\",\n  \"use\": \"sig\"\n}\n"
  },
  {
    "path": "oryx/jwksx/fetcher.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jwksx\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"sync\"\n\n\t\"github.com/go-jose/go-jose/v3\"\n\t\"github.com/pkg/errors\"\n)\n\n// Fetcher is a small helper for fetching JSON Web Keys from remote endpoints.\n//\n// DEPRECATED: Use FetcherNext instead.\ntype Fetcher struct {\n\tsync.RWMutex\n\tremote string\n\tc      *http.Client\n\tkeys   map[string]jose.JSONWebKey\n}\n\n// NewFetcher returns a new fetcher that can download JSON Web Keys from remote endpoints.\n//\n// DEPRECATED: Use FetcherNext instead.\nfunc NewFetcher(remote string) *Fetcher {\n\treturn &Fetcher{\n\t\tremote: remote,\n\t\tc:      http.DefaultClient,\n\t\tkeys:   make(map[string]jose.JSONWebKey),\n\t}\n}\n\n// GetKey retrieves a JSON Web Key from the cache, fetches it from a remote if it is not yet cached or returns an error.\n//\n// DEPRECATED: Use FetcherNext instead.\nfunc (f *Fetcher) GetKey(kid string) (*jose.JSONWebKey, error) {\n\tf.RLock()\n\tif k, ok := f.keys[kid]; ok {\n\t\tf.RUnlock()\n\t\treturn &k, nil\n\t}\n\tf.RUnlock()\n\n\tres, err := f.c.Get(f.remote)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tdefer res.Body.Close()\n\tif res.StatusCode != http.StatusOK {\n\t\treturn nil, errors.Errorf(\"expected status code 200 but got %d when requesting %s\", res.StatusCode, f.remote)\n\t}\n\n\tvar set jose.JSONWebKeySet\n\tif err := json.NewDecoder(res.Body).Decode(&set); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tfor _, k := range set.Keys {\n\t\tf.Lock()\n\t\tf.keys[k.KeyID] = k\n\t\tf.Unlock()\n\t}\n\n\tf.RLock()\n\tdefer f.RUnlock()\n\tif k, ok := f.keys[kid]; ok {\n\t\treturn &k, nil\n\t}\n\n\treturn nil, errors.Errorf(\"unable to find JSON Web Key with ID: %s\", kid)\n}\n"
  },
  {
    "path": "oryx/jwksx/fetcher_v2.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jwksx\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"time\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/ory/x/fetcher\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/lestrrat-go/jwx/jwk\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nvar ErrUnableToFindKeyID = errors.New(\"specified JWK kid can not be found in the JWK sets\")\n\ntype (\n\tfetcherNextOptions struct {\n\t\tforceKID   string\n\t\tcacheTTL   time.Duration\n\t\tuseCache   bool\n\t\thttpClient *retryablehttp.Client\n\t}\n\t// FetcherNext is a JWK fetcher that can be used to fetch JWKs from multiple locations.\n\tFetcherNext struct {\n\t\tcache *ristretto.Cache[[]byte, jwk.Set]\n\t}\n\t// FetcherNextOption is a functional option for the FetcherNext.\n\tFetcherNextOption func(*fetcherNextOptions)\n)\n\n// NewFetcherNext returns a new FetcherNext instance.\nfunc NewFetcherNext(cache *ristretto.Cache[[]byte, jwk.Set]) *FetcherNext {\n\treturn &FetcherNext{\n\t\tcache: cache,\n\t}\n}\n\n// WithForceKID forces the key ID to be used. Required when multiple JWK sets are configured.\nfunc WithForceKID(kid string) FetcherNextOption {\n\treturn func(o *fetcherNextOptions) {\n\t\to.forceKID = kid\n\t}\n}\n\n// WithCacheTTL sets the cache TTL. If not set, the TTL is unlimited.\nfunc WithCacheTTL(ttl time.Duration) FetcherNextOption {\n\treturn func(o *fetcherNextOptions) {\n\t\to.cacheTTL = ttl\n\t}\n}\n\n// WithCacheEnabled enables the cache.\nfunc WithCacheEnabled() FetcherNextOption {\n\treturn func(o *fetcherNextOptions) {\n\t\to.useCache = true\n\t}\n}\n\n// WithHTTPClient will use the given HTTP client to fetch the JSON Web Keys.\nfunc WithHTTPClient(c *retryablehttp.Client) FetcherNextOption {\n\treturn func(o *fetcherNextOptions) {\n\t\to.httpClient = c\n\t}\n}\n\nfunc (f *FetcherNext) ResolveKey(ctx context.Context, locations string, modifiers ...FetcherNextOption) (jwk.Key, error) {\n\treturn f.ResolveKeyFromLocations(ctx, []string{locations}, modifiers...)\n}\n\nfunc (f *FetcherNext) ResolveKeyFromLocations(ctx context.Context, locations []string, modifiers ...FetcherNextOption) (jwk.Key, error) {\n\topts := new(fetcherNextOptions)\n\tfor _, m := range modifiers {\n\t\tm(opts)\n\t}\n\n\tif len(locations) > 1 && opts.forceKID == \"\" {\n\t\treturn nil, errors.Errorf(\"a key ID must be specified when multiple JWK sets are configured\")\n\t}\n\n\tset := jwk.NewSet()\n\teg := new(errgroup.Group)\n\tfor k := range locations {\n\t\tlocation := locations[k]\n\t\teg.Go(func() error {\n\t\t\tremoteSet, err := f.fetch(ctx, location, opts)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\titerator := remoteSet.Iterate(ctx)\n\t\t\tfor iterator.Next(ctx) {\n\t\t\t\t// Pair().Value is always of type jwk.Key when generated by Iterate.\n\t\t\t\tset.Add(iterator.Pair().Value.(jwk.Key))\n\t\t\t}\n\n\t\t\treturn nil\n\t\t})\n\t}\n\n\tif err := eg.Wait(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif opts.forceKID != \"\" {\n\t\tkey, found := set.LookupKeyID(opts.forceKID)\n\t\tif !found {\n\t\t\treturn nil, errors.WithStack(ErrUnableToFindKeyID)\n\t\t}\n\n\t\treturn key, nil\n\t}\n\n\t// No KID was forced? Use the first key we can find.\n\tkey, found := set.Get(0)\n\tif !found {\n\t\treturn nil, errors.WithStack(ErrUnableToFindKeyID)\n\t}\n\n\treturn key, nil\n}\n\n// fetch fetches the JWK set from the given location and if enabled, may use the cache to look up the JWK set.\nfunc (f *FetcherNext) fetch(ctx context.Context, location string, opts *fetcherNextOptions) (_ jwk.Set, err error) {\n\ttracer := trace.SpanFromContext(ctx).TracerProvider().Tracer(\"\")\n\tctx, span := tracer.Start(ctx, \"jwksx.FetcherNext.fetch\", trace.WithAttributes(attribute.String(\"location\", location)))\n\tdefer otelx.End(span, &err)\n\n\tcacheKey := sha256.Sum256([]byte(location))\n\tif opts.useCache {\n\t\tif result, found := f.cache.Get(cacheKey[:]); found {\n\t\t\treturn result, nil\n\t\t}\n\t}\n\n\tvar fopts []fetcher.Modifier\n\tif opts.httpClient != nil {\n\t\tfopts = append(fopts, fetcher.WithClient(opts.httpClient))\n\t}\n\n\tresult, err := fetcher.NewFetcher(fopts...).FetchContext(ctx, location)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tset, err := jwk.ParseReader(result)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReason(\"failed to parse JWK set\").WithWrap(err))\n\t}\n\n\tif opts.useCache {\n\t\tf.cache.SetWithTTL(cacheKey[:], set, 1, opts.cacheTTL)\n\t}\n\n\treturn set, nil\n}\n"
  },
  {
    "path": "oryx/jwksx/generator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jwksx\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/elliptic\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"io\"\n\n\t\"github.com/go-jose/go-jose/v3\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/crypto/ed25519\"\n)\n\n// GenerateSigningKeys generates a JSON Web Key Set for signing.\nfunc GenerateSigningKeys(id, alg string, bits int) (*jose.JSONWebKeySet, error) {\n\tif id == \"\" {\n\t\tid = uuid.Must(uuid.NewV4()).String()\n\t}\n\n\tkey, err := generate(jose.SignatureAlgorithm(alg), bits)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &jose.JSONWebKeySet{\n\t\tKeys: []jose.JSONWebKey{\n\t\t\t{\n\t\t\t\tAlgorithm:    alg,\n\t\t\t\tUse:          \"sig\",\n\t\t\t\tKey:          key,\n\t\t\t\tKeyID:        id,\n\t\t\t\tCertificates: []*x509.Certificate{},\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\n// GenerateSigningKeysAvailableAlgorithms lists available algorithms that are supported by GenerateSigningKeys.\nfunc GenerateSigningKeysAvailableAlgorithms() []string {\n\treturn []string{\n\t\tstring(jose.HS256), string(jose.HS384), string(jose.HS512),\n\t\tstring(jose.ES256), string(jose.ES384), string(jose.ES512), string(jose.EdDSA),\n\t\tstring(jose.RS256), string(jose.RS384), string(jose.RS512), string(jose.PS256), string(jose.PS384), string(jose.PS512),\n\t}\n}\n\n// generate generates keypair for corresponding SignatureAlgorithm.\nfunc generate(alg jose.SignatureAlgorithm, bits int) (crypto.PrivateKey, error) {\n\tswitch alg {\n\tcase jose.ES256, jose.ES384, jose.ES512, jose.EdDSA:\n\t\tkeylen := map[jose.SignatureAlgorithm]int{\n\t\t\tjose.ES256: 256,\n\t\t\tjose.ES384: 384,\n\t\t\tjose.ES512: 521, // sic!\n\t\t\tjose.EdDSA: 256,\n\t\t}\n\t\tif bits != 0 && bits != keylen[alg] {\n\t\t\treturn nil, errors.Errorf(`jwksx: \"%s\" does not support arbitrary key length`, alg)\n\t\t}\n\tcase jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:\n\t\tif bits == 0 {\n\t\t\tbits = 2048\n\t\t}\n\t\tif bits < 2048 {\n\t\t\treturn nil, errors.Errorf(`jwksx: key size must be at least 2048 bit for algorithm \"%s\"`, alg)\n\t\t}\n\tcase jose.HS256:\n\t\tif bits == 0 {\n\t\t\tbits = 256\n\t\t}\n\t\tif bits < 256 {\n\t\t\treturn nil, errors.Errorf(`jwksx: key size must be at least 256 bit for algorithm \"%s\"`, alg)\n\t\t}\n\tcase jose.HS384:\n\t\tif bits == 0 {\n\t\t\tbits = 384\n\t\t}\n\t\tif bits < 384 {\n\t\t\treturn nil, errors.Errorf(`jwksx: key size must be at least 2038448 bit for algorithm \"%s\"`, alg)\n\t\t}\n\tcase jose.HS512:\n\t\tif bits == 0 {\n\t\t\tbits = 1024\n\t\t}\n\t\tif bits < 512 {\n\t\t\treturn nil, errors.Errorf(`jwksx: key size must be at least 512 bit for algorithm \"%s\"`, alg)\n\t\t}\n\t}\n\n\tswitch alg {\n\tcase jose.ES256:\n\t\t// The cryptographic operations are implemented using constant-time algorithms.\n\t\tkey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)\n\t\treturn key, errors.Wrapf(err, \"jwks: unable to generate key\")\n\tcase jose.ES384:\n\t\t// NB: The cryptographic operations do not use constant-time algorithms.\n\t\tkey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)\n\t\treturn key, errors.Wrapf(err, \"jwks: unable to generate key\")\n\tcase jose.ES512:\n\t\t// NB: The cryptographic operations do not use constant-time algorithms.\n\t\tkey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)\n\t\treturn key, errors.Wrapf(err, \"jwks: unable to generate key\")\n\tcase jose.EdDSA:\n\t\t_, key, err := ed25519.GenerateKey(rand.Reader)\n\t\treturn key, errors.Wrapf(err, \"jwks: unable to generate key\")\n\tcase jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:\n\t\tkey, err := rsa.GenerateKey(rand.Reader, bits)\n\t\treturn key, errors.Wrapf(err, \"jwks: unable to generate key\")\n\tcase jose.HS256, jose.HS384, jose.HS512:\n\t\tif bits%8 != 0 {\n\t\t\treturn nil, errors.Errorf(`jwksx: key size must be a multiple of 8 for algorithm \"%s\" but got: %d`, alg, bits)\n\t\t}\n\n\t\tkey := make([]byte, bits/8)\n\t\tif _, err := io.ReadFull(rand.Reader, key); err != nil {\n\t\t\treturn nil, errors.Wrapf(err, \"jwks: unable to generate key\")\n\t\t}\n\t\treturn key, nil\n\tdefault:\n\t\treturn nil, errors.Errorf(`jwksx: available algorithms are \"%+v\" but unknown algorithm was requested: \"%s\"`, GenerateSigningKeysAvailableAlgorithms(), alg)\n\t}\n}\n"
  },
  {
    "path": "oryx/jwtmiddleware/middleware.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jwtmiddleware\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\n\tjwtmiddleware \"github.com/auth0/go-jwt-middleware/v2\"\n\t\"github.com/urfave/negroni\"\n\n\t\"github.com/ory/x/jwksx\"\n)\n\ntype Middleware struct {\n\to   *middlewareOptions\n\twku string\n\tjm  *jwtmiddleware.JWTMiddleware\n}\n\ntype middlewareOptions struct {\n\tDebug         bool\n\tExcludePaths  []string\n\tSigningMethod jwt.SigningMethod\n\tErrorWriter   herodot.Writer\n}\n\ntype MiddlewareOption func(*middlewareOptions)\n\nfunc SessionFromContext(ctx context.Context) (json.RawMessage, error) {\n\traw := ctx.Value(jwtmiddleware.ContextKey{})\n\tif raw == nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUnauthorized.WithReasonf(\"Could not find credentials in the request.\"))\n\t}\n\n\ttoken, ok := raw.(*jwt.Token)\n\tif !ok {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithDebugf(`Expected context key \"%T\" to transport value of type *jwt.MapClaims but got type: %T`, jwtmiddleware.ContextKey{}, raw))\n\t}\n\n\tsession, err := json.Marshal(token.Claims)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithDebugf(\"Unable to encode session data: %s\", err))\n\t}\n\n\treturn session, nil\n}\n\nfunc MiddlewareDebugEnabled() MiddlewareOption {\n\treturn func(o *middlewareOptions) {\n\t\to.Debug = true\n\t}\n}\n\nfunc MiddlewareExcludePaths(paths ...string) MiddlewareOption {\n\treturn func(o *middlewareOptions) {\n\t\to.ExcludePaths = append(o.ExcludePaths, paths...)\n\t}\n}\n\nfunc MiddlewareAllowSigningMethod(method jwt.SigningMethod) MiddlewareOption {\n\treturn func(o *middlewareOptions) {\n\t\to.SigningMethod = method\n\t}\n}\n\nfunc MiddlewareErrorWriter(w herodot.Writer) MiddlewareOption {\n\treturn func(o *middlewareOptions) {\n\t\to.ErrorWriter = w\n\t}\n}\n\nfunc NewMiddleware(\n\twellKnownURL string,\n\topts ...MiddlewareOption,\n) *Middleware {\n\tc := &middlewareOptions{\n\t\tSigningMethod: jwt.SigningMethodES256,\n\t\tErrorWriter:   herodot.NewJSONWriter(nil),\n\t}\n\n\tfor _, o := range opts {\n\t\to(c)\n\t}\n\tjc := jwksx.NewFetcher(wellKnownURL)\n\treturn &Middleware{\n\t\to:   c,\n\t\twku: wellKnownURL,\n\t\tjm: jwtmiddleware.New(\n\t\t\tfunc(ctx context.Context, rawToken string) (any, error) {\n\t\t\t\treturn jwt.NewParser(\n\t\t\t\t\tjwt.WithValidMethods([]string{c.SigningMethod.Alg()}),\n\t\t\t\t).Parse(rawToken, func(token *jwt.Token) (interface{}, error) {\n\t\t\t\t\tif raw, ok := token.Header[\"kid\"]; !ok {\n\t\t\t\t\t\treturn nil, errors.New(`jwt from authorization HTTP header is missing value for \"kid\" in token header`)\n\t\t\t\t\t} else if kid, ok := raw.(string); !ok {\n\t\t\t\t\t\treturn nil, fmt.Errorf(`jwt from authorization HTTP header is expecting string value for \"kid\" in tokenWithoutKid header but got: %T`, raw)\n\t\t\t\t\t} else if k, err := jc.GetKey(kid); err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn k.Key, nil\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\tjwtmiddleware.WithCredentialsOptional(false),\n\t\t\tjwtmiddleware.WithTokenExtractor(func(r *http.Request) (string, error) {\n\t\t\t\t// wrapping the extractor to get a herodot.ErrorContainer\n\t\t\t\ttoken, err := jwtmiddleware.AuthHeaderTokenExtractor(r)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn \"\", herodot.ErrUnauthorized.WithReason(err.Error())\n\t\t\t\t}\n\t\t\t\treturn token, nil\n\t\t\t}),\n\t\t\tjwtmiddleware.WithErrorHandler(func(w http.ResponseWriter, r *http.Request, err error) {\n\t\t\t\tswitch {\n\t\t\t\tcase errors.Is(err, jwtmiddleware.ErrJWTInvalid):\n\t\t\t\t\treason := \"The token is invalid or expired.\"\n\t\t\t\t\tif err := errors.Unwrap(err); err != nil {\n\t\t\t\t\t\treason = err.Error()\n\t\t\t\t\t}\n\t\t\t\t\tc.ErrorWriter.WriteError(w, r, errors.WithStack(herodot.ErrUnauthorized.WithReason(reason)))\n\t\t\t\tcase errors.Is(err, jwtmiddleware.ErrJWTMissing):\n\t\t\t\t\tc.ErrorWriter.WriteError(w, r, errors.WithStack(herodot.ErrUnauthorized.WithReason(\"The token is missing.\")))\n\t\t\t\tdefault:\n\t\t\t\t\tc.ErrorWriter.WriteError(w, r, err)\n\t\t\t\t}\n\t\t\t}),\n\t\t),\n\t}\n}\n\n// Deprecated: use Middleware as a negroni.Handler directly instead.\nfunc (h *Middleware) NegroniHandler() negroni.Handler {\n\treturn negroni.HandlerFunc(h.ServeHTTP)\n}\n\nfunc (h *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tfor _, excluded := range h.o.ExcludePaths {\n\t\tif strings.HasPrefix(r.URL.Path, excluded) {\n\t\t\tnext(w, r)\n\t\t\treturn\n\t\t}\n\t}\n\n\th.jm.CheckJWT(next).ServeHTTP(w, r)\n}\n"
  },
  {
    "path": "oryx/jwtmiddleware/stub/jwks.json",
    "content": "{\n  \"use\": \"sig\",\n  \"kty\": \"EC\",\n  \"kid\": \"b71ff5bd-a016-4ac0-9f3f-a172552578ea\",\n  \"crv\": \"P-256\",\n  \"alg\": \"ES256\",\n  \"x\": \"7fVj_SeCx3TnkHANRWrpEho9BcYkU953LHUvKsSF5Wo\",\n  \"y\": \"2A9D_AAFPiJQLSJQ_h600Fy9jUrg9Q88gNPPZwHDb7o\",\n  \"d\": \"sRl-e-tGEVsNBF8FgEado9NAEipxhAFXGMryWDgbUMo\"\n}\n"
  },
  {
    "path": "oryx/jwtx/claims.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage jwtx\n\nimport (\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/mapx\"\n)\n\n// Claims represents a JSON Web Token's standard claims.\ntype Claims struct {\n\t// Audience identifies the recipients that the JWT is intended for.\n\tAudience []string `json:\"aud\"`\n\n\t// Issuer identifies the principal that issued the JWT.\n\tIssuer string `json:\"iss\"`\n\n\t// Subject identifies the principal that is the subject of the JWT.\n\tSubject string `json:\"sub\"`\n\n\t// ExpiresAt identifies the expiration time on or after which the JWT most not be accepted for processing.\n\tExpiresAt time.Time `json:\"exp\"`\n\n\t// IssuedAt identifies the time at which the JWT was issued.\n\tIssuedAt time.Time `json:\"iat\"`\n\n\t// NotBefore identifies the time before which the JWT must not be accepted for processing.\n\tNotBefore time.Time `json:\"nbf\"`\n\n\t// JTI provides a unique identifier for the JWT.\n\tJTI string `json:\"jti\"`\n}\n\n// ParseMapStringInterfaceClaims converts map[string]interface{} to *Claims.\nfunc ParseMapStringInterfaceClaims(claims map[string]interface{}) *Claims {\n\tc := make(map[interface{}]interface{})\n\tfor k, v := range claims {\n\t\tc[k] = v\n\t}\n\treturn ParseMapInterfaceInterfaceClaims(c)\n}\n\n// ParseMapInterfaceInterfaceClaims converts map[interface{}]interface{} to *Claims.\nfunc ParseMapInterfaceInterfaceClaims(claims map[interface{}]interface{}) *Claims {\n\tresult := &Claims{\n\t\tIssuer:  mapx.GetStringDefault(claims, \"iss\", \"\"),\n\t\tSubject: mapx.GetStringDefault(claims, \"sub\", \"\"),\n\t\tJTI:     mapx.GetStringDefault(claims, \"jti\", \"\"),\n\t}\n\n\tif aud, err := mapx.GetString(claims, \"aud\"); err == nil {\n\t\tresult.Audience = []string{aud}\n\t} else if errors.Is(err, mapx.ErrKeyCanNotBeTypeAsserted) {\n\t\tif aud, err := mapx.GetStringSlice(claims, \"aud\"); err == nil {\n\t\t\tresult.Audience = aud\n\t\t} else {\n\t\t\tresult.Audience = []string{}\n\t\t}\n\t} else {\n\t\tresult.Audience = []string{}\n\t}\n\n\tif exp, err := mapx.GetTime(claims, \"exp\"); err == nil {\n\t\tresult.ExpiresAt = exp\n\t}\n\n\tif iat, err := mapx.GetTime(claims, \"iat\"); err == nil {\n\t\tresult.IssuedAt = iat\n\t}\n\n\tif nbf, err := mapx.GetTime(claims, \"nbf\"); err == nil {\n\t\tresult.NotBefore = nbf\n\t}\n\n\treturn result\n}\n"
  },
  {
    "path": "oryx/logrusx/config.schema.json",
    "content": "{\n  \"$id\": \"ory://logging-config\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Log\",\n  \"description\": \"Configure logging using the following options. Logs will always be sent to stdout and stderr.\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"level\": {\n      \"title\": \"Level\",\n      \"description\": \"The level of log entries to show. Debug enables stack traces on errors.\",\n      \"type\": \"string\",\n      \"default\": \"info\",\n      \"enum\": [\"panic\", \"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"]\n    },\n    \"format\": {\n      \"title\": \"Log Format\",\n      \"description\": \"The output format of log messages.\",\n      \"type\": \"string\",\n      \"default\": \"text\",\n      \"enum\": [\"json\", \"json_pretty\", \"gelf\", \"text\"]\n    },\n    \"leak_sensitive_values\": {\n      \"type\": \"boolean\",\n      \"title\": \"Leak Sensitive Log Values\",\n      \"description\": \"If set will leak sensitive values (e.g. emails) in the logs.\",\n      \"default\": false\n    },\n    \"redaction_text\": {\n      \"type\": \"string\",\n      \"title\": \"Sensitive log value redaction text\",\n      \"description\": \"Text to use, when redacting sensitive log value.\"\n    },\n    \"additional_redacted_headers\": {\n      \"type\": \"array\",\n      \"title\": \"Additional redacted headers\",\n      \"description\": \"List of HTTP headers which will be redacted.\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "oryx/logrusx/helper.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage logrusx\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/sirupsen/logrus\"\n\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/x/errorsx\"\n)\n\ntype (\n\tLogger struct {\n\t\t*logrus.Entry\n\t\tleakSensitive             bool\n\t\tredactionText             string\n\t\tadditionalRedactedHeaders map[string]struct{}\n\t\topts                      []Option\n\t}\n\tProvider interface {\n\t\tLogger() *Logger\n\t}\n)\n\nvar opts = otelhttptrace.WithPropagators(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))\n\nfunc (l *Logger) Logrus() *logrus.Logger {\n\treturn l.Entry.Logger\n}\n\nfunc (l *Logger) NewEntry() *Logger {\n\tll := *l\n\tll.Entry = logrus.NewEntry(l.Logger)\n\treturn &ll\n}\n\nfunc (l *Logger) WithContext(ctx context.Context) *Logger {\n\tll := *l\n\tll.Entry = l.Logger.WithContext(ctx)\n\treturn &ll\n}\n\nfunc (l *Logger) HTTPHeadersRedacted(h http.Header) map[string]any {\n\theaders := map[string]any{}\n\n\tfor key, value := range h {\n\t\tswitch keyLower := strings.ToLower(key); keyLower {\n\t\tcase \"authorization\", \"cookie\", \"set-cookie\", \"x-session-token\":\n\t\t\theaders[keyLower] = l.maybeRedact(value)\n\t\tcase \"location\":\n\t\t\tlocationURL, err := url.Parse(h.Get(\"Location\"))\n\t\t\tif err != nil {\n\t\t\t\theaders[keyLower] = l.maybeRedact(value)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif l.leakSensitive {\n\t\t\t\theaders[keyLower] = locationURL.String()\n\t\t\t} else {\n\t\t\t\tlocationURL.RawQuery = \"\"\n\t\t\t\tlocationURL.Fragment = \"\"\n\t\t\t\theaders[keyLower] = locationURL.Redacted()\n\t\t\t}\n\t\tdefault:\n\t\t\tif _, ok := l.additionalRedactedHeaders[keyLower]; ok {\n\t\t\t\theaders[keyLower] = l.maybeRedact(value)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\theaders[keyLower] = h.Get(key)\n\t\t}\n\t}\n\n\treturn headers\n}\n\nfunc (l *Logger) WithRequest(r *http.Request) *Logger {\n\theaders := l.HTTPHeadersRedacted(r.Header)\n\tif ua := r.UserAgent(); len(ua) > 0 {\n\t\theaders[\"user-agent\"] = ua\n\t}\n\n\tscheme := \"https\"\n\tif r.TLS == nil {\n\t\tscheme = \"http\"\n\t}\n\n\tll := l.WithField(\"http_request\", map[string]any{\n\t\t\"remote\":  r.RemoteAddr,\n\t\t\"method\":  r.Method,\n\t\t\"path\":    r.URL.EscapedPath(),\n\t\t\"query\":   l.maybeRedact(r.URL.RawQuery),\n\t\t\"scheme\":  scheme,\n\t\t\"host\":    r.Host,\n\t\t\"headers\": headers,\n\t})\n\n\tspanCtx := trace.SpanContextFromContext(r.Context())\n\tif !spanCtx.IsValid() {\n\t\t_, _, spanCtx = otelhttptrace.Extract(r.Context(), r, opts)\n\t}\n\tif spanCtx.IsValid() {\n\t\ttraces := make(map[string]string, 2)\n\t\tif spanCtx.HasTraceID() {\n\t\t\ttraces[\"trace_id\"] = spanCtx.TraceID().String()\n\t\t}\n\t\tif spanCtx.HasSpanID() {\n\t\t\ttraces[\"span_id\"] = spanCtx.SpanID().String()\n\t\t}\n\t\tll = ll.WithField(\"otel\", traces)\n\t}\n\treturn ll\n}\n\nfunc (l *Logger) WithSpanFromContext(ctx context.Context) *Logger {\n\tspanCtx := trace.SpanContextFromContext(ctx)\n\tif !spanCtx.IsValid() {\n\t\treturn l\n\t}\n\n\ttraces := make(map[string]string, 2)\n\tif spanCtx.HasTraceID() {\n\t\ttraces[\"trace_id\"] = spanCtx.TraceID().String()\n\t}\n\tif spanCtx.HasSpanID() {\n\t\ttraces[\"span_id\"] = spanCtx.SpanID().String()\n\t}\n\treturn l.WithField(\"otel\", traces)\n}\n\nfunc (l *Logger) Logf(level logrus.Level, format string, args ...any) {\n\tif !l.leakSensitive {\n\t\tfor i, arg := range args {\n\t\t\tswitch urlArg := arg.(type) {\n\t\t\tcase url.URL:\n\t\t\t\turlCopy := url.URL{Scheme: urlArg.Scheme, Host: urlArg.Host, Path: urlArg.Path}\n\t\t\t\targs[i] = urlCopy\n\t\t\tcase *url.URL:\n\t\t\t\turlCopy := url.URL{Scheme: urlArg.Scheme, Host: urlArg.Host, Path: urlArg.Path}\n\t\t\t\targs[i] = &urlCopy\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n\tl.Entry.Logf(level, format, args...)\n}\n\nfunc (l *Logger) Tracef(format string, args ...any) {\n\tl.Logf(logrus.TraceLevel, format, args...)\n}\n\nfunc (l *Logger) Debugf(format string, args ...any) {\n\tl.Logf(logrus.DebugLevel, format, args...)\n}\n\nfunc (l *Logger) Infof(format string, args ...any) {\n\tl.Logf(logrus.InfoLevel, format, args...)\n}\n\nfunc (l *Logger) Warnf(format string, args ...any) {\n\tl.Logf(logrus.WarnLevel, format, args...)\n}\n\nfunc (l *Logger) Errorf(format string, args ...any) {\n\tl.Logf(logrus.ErrorLevel, format, args...)\n}\n\nfunc (l *Logger) Fatalf(format string, args ...any) {\n\tl.Logf(logrus.FatalLevel, format, args...)\n\tl.Entry.Logger.Exit(1)\n}\n\nfunc (l *Logger) Panicf(format string, args ...any) {\n\tl.Logf(logrus.PanicLevel, format, args...)\n}\n\nfunc (l *Logger) WithFields(f logrus.Fields) *Logger {\n\tll := *l\n\tll.Entry = l.Entry.WithFields(f)\n\treturn &ll\n}\n\nfunc (l *Logger) WithField(key string, value any) *Logger {\n\tll := *l\n\tll.Entry = l.Entry.WithField(key, value)\n\treturn &ll\n}\n\nfunc (l *Logger) maybeRedact(value any) any {\n\tif fmt.Sprintf(\"%v\", value) == \"\" || value == nil {\n\t\treturn nil\n\t}\n\tif !l.leakSensitive {\n\t\treturn l.redactionText\n\t}\n\treturn value\n}\n\nfunc (l *Logger) WithSensitiveField(key string, value any) *Logger {\n\treturn l.WithField(key, l.maybeRedact(value))\n}\n\nfunc (l *Logger) WithError(err error) *Logger {\n\tif err == nil {\n\t\treturn l\n\t}\n\n\tctx := map[string]any{\"message\": err.Error()}\n\tif l.Entry.Logger.IsLevelEnabled(logrus.DebugLevel) {\n\t\tif e, ok := err.(errorsx.StackTracer); ok {\n\t\t\tctx[\"stack_trace\"] = fmt.Sprintf(\"%+v\", e.StackTrace())\n\t\t} else {\n\t\t\tctx[\"stack_trace\"] = fmt.Sprintf(\"stack trace could not be recovered from error type %s\", reflect.TypeOf(err))\n\t\t}\n\t}\n\tif c := errorsx.ReasonCarrier(nil); errors.As(err, &c) {\n\t\tctx[\"reason\"] = c.Reason()\n\t}\n\tif c := errorsx.RequestIDCarrier(nil); errors.As(err, &c) && c.RequestID() != \"\" {\n\t\tctx[\"request_id\"] = c.RequestID()\n\t}\n\tif c := errorsx.DetailsCarrier(nil); errors.As(err, &c) && c.Details() != nil {\n\t\tctx[\"details\"] = c.Details()\n\t}\n\tif c := errorsx.StatusCarrier(nil); errors.As(err, &c) && c.Status() != \"\" {\n\t\tctx[\"status\"] = c.Status()\n\t}\n\tif c := errorsx.StatusCodeCarrier(nil); errors.As(err, &c) && c.StatusCode() != 0 {\n\t\tctx[\"status_code\"] = c.StatusCode()\n\t}\n\tif c := errorsx.DebugCarrier(nil); errors.As(err, &c) {\n\t\tctx[\"debug\"] = c.Debug()\n\t}\n\n\treturn l.WithField(\"error\", ctx)\n}\n\nfunc (l *Logger) StdLogger(lvl logrus.Level) *log.Logger {\n\treturn log.New(writer{l.Logger, lvl}, \"\", 0)\n}\n\ntype writer struct {\n\tl   *logrus.Logger\n\tlvl logrus.Level\n}\n\nfunc (w writer) Write(p []byte) (n int, err error) {\n\tw.l.Log(w.lvl, strings.TrimSuffix(string(p), \"\\n\"))\n\treturn len(p), nil\n}\n"
  },
  {
    "path": "oryx/logrusx/logrus.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage logrusx\n\nimport (\n\t\"bytes\"\n\t\"cmp\"\n\t_ \"embed\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus\"\n\n\tgelf \"github.com/seatgeek/logrus-gelf-formatter\"\n\n\t\"github.com/ory/x/stringsx\"\n)\n\ntype (\n\toptions struct {\n\t\tl                         *logrus.Logger\n\t\tlevel                     *logrus.Level\n\t\tformatter                 logrus.Formatter\n\t\tformat                    string\n\t\treportCaller              bool\n\t\texitFunc                  func(int)\n\t\tleakSensitive             bool\n\t\tredactionText             string\n\t\tadditionalRedactedHeaders []string\n\t\thooks                     []logrus.Hook\n\t\tc                         configurator\n\t}\n\tOption           func(*options)\n\tnullConfigurator struct{}\n\tconfigurator     interface {\n\t\tBool(key string) bool\n\t\tString(key string) string\n\t\tStrings(path string) []string\n\t}\n)\n\n//go:embed config.schema.json\nvar ConfigSchema string\n\nconst ConfigSchemaID = \"ory://logging-config\"\n\n// AddConfigSchema adds the logging schema to the compiler.\n// The interface is specified instead of `jsonschema.Compiler` to allow the use of any jsonschema library fork or version.\nfunc AddConfigSchema(c interface {\n\tAddResource(url string, r io.Reader) error\n},\n) error {\n\treturn c.AddResource(ConfigSchemaID, bytes.NewBufferString(ConfigSchema))\n}\n\nfunc newLogger(parent *logrus.Logger, o *options) *logrus.Logger {\n\tl := parent\n\tif l == nil {\n\t\tl = logrus.New()\n\t}\n\n\tif o.exitFunc != nil {\n\t\tl.ExitFunc = o.exitFunc\n\t}\n\n\tfor _, hook := range o.hooks {\n\t\tl.AddHook(hook)\n\t}\n\n\tsetLevel(l, o)\n\tsetFormatter(l, o)\n\n\tl.ReportCaller = o.reportCaller || l.IsLevelEnabled(logrus.TraceLevel)\n\treturn l\n}\n\nfunc setLevel(l *logrus.Logger, o *options) {\n\tif o.level != nil {\n\t\tl.Level = *o.level\n\t} else {\n\t\tvar err error\n\t\tl.Level, err = logrus.ParseLevel(cmp.Or(\n\t\t\to.c.String(\"log.level\"),\n\t\t\tos.Getenv(\"LOG_LEVEL\")))\n\t\tif err != nil {\n\t\t\tl.Level = logrus.InfoLevel\n\t\t}\n\t}\n}\n\nfunc setFormatter(l *logrus.Logger, o *options) {\n\tif o.formatter != nil {\n\t\tl.Formatter = o.formatter\n\t} else {\n\t\tvar unknownFormat bool // we first have to set the formatter before we can complain about the unknown format\n\n\t\tformat := stringsx.SwitchExact(cmp.Or(o.format, o.c.String(\"log.format\"), os.Getenv(\"LOG_FORMAT\")))\n\t\tswitch {\n\t\tcase format.AddCase(\"json\"):\n\t\t\tl.Formatter = &logrus.JSONFormatter{PrettyPrint: false, TimestampFormat: time.RFC3339Nano, DisableHTMLEscape: true}\n\t\tcase format.AddCase(\"json_pretty\"):\n\t\t\tl.Formatter = &logrus.JSONFormatter{PrettyPrint: true, TimestampFormat: time.RFC3339Nano, DisableHTMLEscape: true}\n\t\tcase format.AddCase(\"gelf\"):\n\t\t\tl.Formatter = new(gelf.GelfFormatter)\n\t\tdefault:\n\t\t\tunknownFormat = true\n\t\t\tfallthrough\n\t\tcase format.AddCase(\"text\", \"\"):\n\t\t\tl.Formatter = &logrus.TextFormatter{\n\t\t\t\tDisableQuote:     true,\n\t\t\t\tDisableTimestamp: false,\n\t\t\t\tFullTimestamp:    true,\n\t\t\t}\n\t\t}\n\n\t\tif unknownFormat {\n\t\t\tl.WithError(format.ToUnknownCaseErr()).Warn(\"got unknown \\\"log.format\\\", falling back to \\\"text\\\"\")\n\t\t}\n\t}\n}\n\nfunc ForceLevel(level logrus.Level) Option {\n\treturn func(o *options) {\n\t\to.level = &level\n\t}\n}\n\nfunc ForceFormatter(formatter logrus.Formatter) Option {\n\treturn func(o *options) {\n\t\to.formatter = formatter\n\t}\n}\n\nfunc WithConfigurator(c configurator) Option {\n\treturn func(o *options) {\n\t\to.c = c\n\t}\n}\n\nfunc ForceFormat(format string) Option {\n\treturn func(o *options) {\n\t\to.format = format\n\t}\n}\n\nfunc WithHook(hook logrus.Hook) Option {\n\treturn func(o *options) {\n\t\to.hooks = append(o.hooks, hook)\n\t}\n}\n\nfunc WithExitFunc(exitFunc func(int)) Option {\n\treturn func(o *options) {\n\t\to.exitFunc = exitFunc\n\t}\n}\n\nfunc ReportCaller(reportCaller bool) Option {\n\treturn func(o *options) {\n\t\to.reportCaller = reportCaller\n\t}\n}\n\nfunc UseLogger(l *logrus.Logger) Option {\n\treturn func(o *options) {\n\t\to.l = l\n\t}\n}\n\nfunc LeakSensitive() Option {\n\treturn func(o *options) {\n\t\to.leakSensitive = true\n\t}\n}\n\nfunc RedactionText(text string) Option {\n\treturn func(o *options) {\n\t\to.redactionText = text\n\t}\n}\n\nfunc WithAdditionalRedactedHeaders(headers []string) Option {\n\treturn func(o *options) {\n\t\to.additionalRedactedHeaders = headers\n\t}\n}\n\nfunc toHeaderMap(headers []string) map[string]struct{} {\n\tm := make(map[string]struct{}, len(headers))\n\tfor _, h := range headers {\n\t\tm[strings.ToLower(h)] = struct{}{}\n\t}\n\treturn m\n}\n\nfunc (c *nullConfigurator) Bool(_ string) bool        { return false }\nfunc (c *nullConfigurator) String(_ string) string    { return \"\" }\nfunc (c *nullConfigurator) Strings(_ string) []string { return []string{} }\n\nfunc newOptions(opts []Option) *options {\n\to := new(options)\n\to.c = new(nullConfigurator)\n\tfor _, f := range opts {\n\t\tf(o)\n\t}\n\treturn o\n}\n\n// New creates a new logger with all the important fields set.\nfunc New(name string, version string, opts ...Option) *Logger {\n\to := newOptions(opts)\n\treturn &Logger{\n\t\topts:          opts,\n\t\tleakSensitive: o.leakSensitive || o.c.Bool(\"log.leak_sensitive_values\"),\n\t\tredactionText: cmp.Or(o.redactionText, `Value is sensitive and has been redacted. To see the value set config key \"log.leak_sensitive_values = true\" or environment variable \"LOG_LEAK_SENSITIVE_VALUES=true\".`),\n\t\tadditionalRedactedHeaders: toHeaderMap(func() []string {\n\t\t\tif len(o.additionalRedactedHeaders) > 0 {\n\t\t\t\treturn o.additionalRedactedHeaders\n\t\t\t}\n\t\t\treturn o.c.Strings(\"log.additional_redacted_headers\")\n\t\t}()),\n\t\tEntry: newLogger(o.l, o).WithFields(logrus.Fields{\n\t\t\t\"audience\": \"application\", \"service_name\": name, \"service_version\": version,\n\t\t}),\n\t}\n}\n\nfunc NewT(t testing.TB, opts ...Option) *Logger {\n\topts = append(opts, LeakSensitive(), WithExitFunc(func(code int) {\n\t\tt.Fatalf(\"Logger exited with code %d\", code)\n\t}))\n\tl := New(t.Name(), \"test\", opts...)\n\tl.Logger.Out = t.Output()\n\treturn l\n}\n\nfunc (l *Logger) UseConfig(c configurator) {\n\tl.leakSensitive = l.leakSensitive || c.Bool(\"log.leak_sensitive_values\")\n\tl.redactionText = cmp.Or(c.String(\"log.redaction_text\"), l.redactionText)\n\tnewHeaders := toHeaderMap(c.Strings(\"log.additional_redacted_headers\"))\n\tfor k := range newHeaders {\n\t\tl.additionalRedactedHeaders[k] = struct{}{}\n\t}\n\to := newOptions(append(l.opts, WithConfigurator(c)))\n\tsetLevel(l.Entry.Logger, o)\n\tsetFormatter(l.Entry.Logger, o)\n}\n\nfunc (l *Logger) ReportError(r *http.Request, code int, err error, args ...interface{}) {\n\tlogger := l.WithError(err).WithRequest(r).WithField(\"http_response\", map[string]interface{}{\n\t\t\"status_code\": code,\n\t})\n\tswitch {\n\tcase code < 500:\n\t\tlogger.Info(args...)\n\tdefault:\n\t\tlogger.Error(args...)\n\t}\n}\n"
  },
  {
    "path": "oryx/mapx/type_assert.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage mapx\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\t\"time\"\n)\n\n// ErrKeyDoesNotExist is returned when the key does not exist in the map.\nvar ErrKeyDoesNotExist = errors.New(\"key is not present in map\")\n\n// ErrKeyCanNotBeTypeAsserted is returned when the key can not be type asserted.\nvar ErrKeyCanNotBeTypeAsserted = errors.New(\"key could not be type asserted\")\n\n// GetString returns a string for a given key in values.\nfunc GetString[K comparable](values map[K]any, key K) (string, error) {\n\tif v, ok := values[key]; !ok {\n\t\treturn \"\", ErrKeyDoesNotExist\n\t} else if sv, ok := v.(string); !ok {\n\t\treturn \"\", ErrKeyCanNotBeTypeAsserted\n\t} else {\n\t\treturn sv, nil\n\t}\n}\n\n// GetStringSlice returns a string slice for a given key in values.\nfunc GetStringSlice[K comparable](values map[K]any, key K) ([]string, error) {\n\tv, ok := values[key]\n\tif !ok {\n\t\treturn nil, ErrKeyDoesNotExist\n\t}\n\tswitch v := v.(type) {\n\tcase []string:\n\t\treturn v, nil\n\tcase []any:\n\t\tvs := make([]string, len(v))\n\t\tfor k, v := range v {\n\t\t\tvar ok bool\n\t\t\tvs[k], ok = v.(string)\n\t\t\tif !ok {\n\t\t\t\treturn nil, ErrKeyCanNotBeTypeAsserted\n\t\t\t}\n\t\t}\n\t\treturn vs, nil\n\t}\n\treturn nil, ErrKeyCanNotBeTypeAsserted\n}\n\n// GetTime returns a string slice for a given key in values.\nfunc GetTime[K comparable](values map[K]any, key K) (time.Time, error) {\n\tv, ok := values[key]\n\tif !ok {\n\t\treturn time.Time{}, ErrKeyDoesNotExist\n\t}\n\n\tswitch v := v.(type) {\n\tcase time.Time:\n\t\treturn v, nil\n\tcase int64:\n\t\treturn time.Unix(v, 0), nil\n\tcase int32:\n\t\treturn time.Unix(int64(v), 0), nil\n\tcase int:\n\t\treturn time.Unix(int64(v), 0), nil\n\tcase float64:\n\t\tif v < math.MinInt64 || v > math.MaxInt64 {\n\t\t\treturn time.Time{}, errors.New(\"value is out of range\")\n\t\t}\n\t\treturn time.Unix(int64(v), 0), nil\n\tcase float32:\n\t\tif v < math.MinInt64 || v > math.MaxInt64 {\n\t\t\treturn time.Time{}, errors.New(\"value is out of range\")\n\t\t}\n\t\treturn time.Unix(int64(v), 0), nil\n\t}\n\n\treturn time.Time{}, ErrKeyCanNotBeTypeAsserted\n}\n\n// GetInt64 returns an int64 for a given key in values.\nfunc GetInt64[K comparable](values map[K]any, key K) (int64, error) {\n\tv, ok := values[key]\n\tif !ok {\n\t\treturn 0, ErrKeyDoesNotExist\n\t}\n\tswitch v := v.(type) {\n\tcase json.Number:\n\t\treturn v.Int64()\n\tcase int64:\n\t\treturn v, nil\n\tcase int:\n\t\treturn int64(v), nil\n\tcase int32:\n\t\treturn int64(v), nil\n\tcase uint:\n\t\tvv := uint64(v)\n\t\tif vv > math.MaxInt64 {\n\t\t\treturn 0, errors.New(\"value is out of range\")\n\t\t}\n\t\treturn int64(vv), nil\n\tcase uint32:\n\t\treturn int64(v), nil\n\tcase uint64:\n\t\tif v > math.MaxInt64 {\n\t\t\treturn 0, errors.New(\"value is out of range\")\n\t\t}\n\t\treturn int64(v), nil\n\t}\n\treturn 0, ErrKeyCanNotBeTypeAsserted\n}\n\n// GetInt32 returns an int32 for a given key in values.\nfunc GetInt32[K comparable](values map[K]any, key K) (int32, error) {\n\tv, err := GetInt64(values, key)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif v > math.MaxInt32 || v < math.MinInt32 {\n\t\treturn 0, errors.New(\"value is out of range\")\n\t}\n\treturn int32(v), nil\n}\n\n// GetInt returns an int for a given key in values.\nfunc GetInt[K comparable](values map[K]any, key K) (int, error) {\n\tv, err := GetInt64(values, key)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif v > math.MaxInt || v < math.MinInt {\n\t\treturn 0, errors.New(\"value is out of range\")\n\t}\n\treturn int(v), nil\n}\n\n// GetFloat64Default returns a float64 or the default value for a given key in values.\nfunc GetFloat64Default[K comparable](values map[K]any, key K, defaultValue float64) float64 {\n\tf, err := GetFloat64(values, key)\n\tif err != nil {\n\t\treturn defaultValue\n\t}\n\treturn f\n}\n\n// GetFloat64 returns a float64 for a given key in values.\nfunc GetFloat64[K comparable](values map[K]any, key K) (float64, error) {\n\tv, ok := values[key]\n\tif !ok {\n\t\treturn 0, ErrKeyDoesNotExist\n\t}\n\tswitch v := v.(type) {\n\tcase json.Number:\n\t\treturn v.Float64()\n\tcase float32:\n\t\treturn float64(v), nil\n\tcase float64:\n\t\treturn v, nil\n\t}\n\treturn 0, ErrKeyCanNotBeTypeAsserted\n}\n\n// GetStringDefault returns a string or the default value for a given key in values.\nfunc GetStringDefault[K comparable](values map[K]any, key K, defaultValue string) string {\n\tif s, err := GetString(values, key); err == nil {\n\t\treturn s\n\t}\n\treturn defaultValue\n}\n\n// GetStringSliceDefault returns a string slice or the default value for a given key in values.\nfunc GetStringSliceDefault[K comparable](values map[K]any, key K, defaultValue []string) []string {\n\tif s, err := GetStringSlice(values, key); err == nil {\n\t\treturn s\n\t}\n\treturn defaultValue\n}\n"
  },
  {
    "path": "oryx/metricsx/metrics.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage metricsx\n\nimport (\n\t\"runtime\"\n\t\"sync\"\n)\n\n// MemoryStatistics is a JSON-able version of runtime.MemStats\ntype MemoryStatistics struct {\n\tsync.Mutex\n\t// Alloc is bytes of allocated heap objects.\n\tAlloc uint64 `json:\"alloc\"`\n\t// TotalAlloc is cumulative bytes allocated for heap objects.\n\tTotalAlloc uint64 `json:\"totalAlloc\"`\n\t// Sys is the total bytes of memory obtained from the OS.\n\tSys uint64 `json:\"sys\"`\n\t// Lookups is the number of pointer lookups performed by the\n\t// runtime.\n\tLookups uint64 `json:\"lookups\"`\n\t// Mallocs is the cumulative count of heap objects allocated.\n\t// The number of live objects is Mallocs - Frees.\n\tMallocs uint64 `json:\"mallocs\"`\n\t// Frees is the cumulative count of heap objects freed.\n\tFrees uint64 `json:\"frees\"`\n\t// HeapAlloc is bytes of allocated heap objects.\n\tHeapAlloc uint64 `json:\"heapAlloc\"`\n\t// HeapSys is bytes of heap memory obtained from the OS.\n\tHeapSys uint64 `json:\"heapSys\"`\n\t// HeapIdle is bytes in idle (unused) spans.\n\tHeapIdle uint64 `json:\"heapIdle\"`\n\t// HeapInuse is bytes in in-use spans.\n\tHeapInuse uint64 `json:\"heapInuse\"`\n\t// HeapReleased is bytes of physical memory returned to the OS.\n\tHeapReleased uint64 `json:\"heapReleased\"`\n\t// HeapObjects is the number of allocated heap objects.\n\tHeapObjects uint64 `json:\"heapObjects\"`\n\t// NumGC is the number of completed GC cycles.\n\tNumGC uint32 `json:\"numGC\"`\n}\n\n// ToMap converts to a map[string]interface{}.\nfunc (ms *MemoryStatistics) ToMap() map[string]interface{} {\n\treturn map[string]interface{}{\n\t\t\"alloc\":          ms.Alloc,\n\t\t\"totalAlloc\":     ms.TotalAlloc,\n\t\t\"sys\":            ms.Sys,\n\t\t\"lookups\":        ms.Lookups,\n\t\t\"mallocs\":        ms.Mallocs,\n\t\t\"frees\":          ms.Frees,\n\t\t\"heapAlloc\":      ms.HeapAlloc,\n\t\t\"heapSys\":        ms.HeapSys,\n\t\t\"heapIdle\":       ms.HeapIdle,\n\t\t\"heapInuse\":      ms.HeapInuse,\n\t\t\"heapReleased\":   ms.HeapReleased,\n\t\t\"heapObjects\":    ms.HeapObjects,\n\t\t\"numGC\":          ms.NumGC,\n\t\t\"nonInteraction\": 1,\n\t}\n}\n\n// Update takes the most recent stats from runtime.\nfunc (ms *MemoryStatistics) Update() {\n\tvar m runtime.MemStats\n\truntime.ReadMemStats(&m)\n\n\tms.Lock()\n\tdefer ms.Unlock()\n\tms.Alloc = m.Alloc\n\tms.TotalAlloc = m.TotalAlloc\n\tms.Sys = m.Sys\n\tms.Lookups = m.Lookups\n\tms.Mallocs = m.Mallocs\n\tms.Frees = m.Frees\n\tms.HeapAlloc = m.HeapAlloc\n\tms.HeapSys = m.HeapSys\n\tms.HeapIdle = m.HeapIdle\n\tms.HeapInuse = m.HeapInuse\n\tms.HeapReleased = m.HeapReleased\n\tms.HeapObjects = m.HeapObjects\n\tms.NumGC = m.NumGC\n}\n"
  },
  {
    "path": "oryx/metricsx/middleware.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage metricsx\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"math\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/resilience\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/analytics-go/v5\"\n)\n\nconst (\n\tXForwardedHostHeader = \"X-Forwarded-Host\"\n\tAuthorityHeader      = \":authority\"\n)\n\nvar (\n\tinstance     *Service\n\tlock         sync.Mutex\n\tknownHeaders = []string{AuthorityHeader, XForwardedHostHeader}\n)\n\n// Service helps with providing context on metrics.\ntype Service struct {\n\toptOut     bool\n\tinstanceId string\n\n\to *Options\n\n\tc analytics.Client\n\tl *logrusx.Logger\n\n\tmem *MemoryStatistics\n}\n\n// Hash returns a hashed string of the value.\nfunc Hash(value string) string {\n\tsha := sha256.Sum256([]byte(value))\n\treturn hex.EncodeToString(sha[:])\n}\n\n// Options configures the metrics service.\ntype Options struct {\n\t// Service represents the service name, for example \"ory-hydra\".\n\tService string\n\n\t// DeploymentId represents the cluster id, typically a hash of some unique configuration properties.\n\tDeploymentId string\n\n\t// DBDialect specifies the database dialect in use (e.g., \"postgres\", \"mysql\", \"sqlite\").\n\tDBDialect string\n\n\t// When this instance was started\n\tStartTime time.Time\n\n\t// IsDevelopment should be true if we assume that we're in a development environment.\n\tIsDevelopment bool\n\n\t// WriteKey is the segment API key.\n\tWriteKey string\n\n\t// WhitelistedPaths represents a list of paths that can be transmitted in clear text to segment.\n\tWhitelistedPaths []string\n\n\t// BuildVersion represents the build version.\n\tBuildVersion string\n\n\t// BuildHash represents the build git hash.\n\tBuildHash string\n\n\t// BuildTime represents the build time.\n\tBuildTime string\n\n\t// Hostname is a public URL configured for the service, used to derive hosted name for telemetry.\n\tHostname string\n\n\t// Config overrides the analytics.Config. If nil, sensible defaults will be used.\n\tConfig *analytics.Config\n\n\t// MemoryInterval sets how often memory statistics should be transmitted. Defaults to every 12 hours.\n\tMemoryInterval time.Duration\n}\n\ntype void struct{}\n\nfunc (v *void) Logf(format string, args ...interface{}) {\n}\n\nfunc (v *void) Errorf(format string, args ...interface{}) {\n}\n\n// New returns a new metrics service. If one has been instantiated already, no new instance will be created.\nfunc New(\n\tcmd *cobra.Command,\n\tl *logrusx.Logger,\n\tc *configx.Provider,\n\to *Options,\n) *Service {\n\tlock.Lock()\n\tdefer lock.Unlock()\n\n\tif instance != nil {\n\t\treturn instance\n\t}\n\n\to.StartTime = time.Now()\n\n\tif o.BuildTime == \"\" {\n\t\to.BuildTime = \"unknown\"\n\t}\n\n\tif o.BuildVersion == \"\" {\n\t\to.BuildVersion = \"unknown\"\n\t}\n\n\tif o.BuildHash == \"\" {\n\t\to.BuildHash = \"unknown\"\n\t}\n\n\tif o.Config == nil {\n\t\to.Config = &analytics.Config{\n\t\t\tInterval: time.Hour * 6,\n\t\t}\n\t}\n\n\to.Config.Logger = new(void)\n\n\tif o.MemoryInterval < time.Minute {\n\t\to.MemoryInterval = time.Hour * 12\n\t}\n\n\tsegment, err := analytics.NewWithConfig(o.WriteKey, *o.Config)\n\tif err != nil {\n\t\tl.WithError(err).Fatalf(\"Unable to initialise software quality assurance features.\")\n\t\treturn nil\n\t}\n\n\toptOut, err := cmd.Flags().GetBool(\"sqa-opt-out\")\n\tif err != nil {\n\t\tcmdx.Must(err, `Unable to get command line flag \"sqa-opt-out\": %s`, err)\n\t}\n\n\tif !optOut {\n\t\toptOut = c.Bool(\"sqa.opt_out\")\n\t}\n\n\tif !optOut {\n\t\toptOut = c.Bool(\"sqa_opt_out\")\n\t}\n\n\tif !optOut {\n\t\toptOut, _ = strconv.ParseBool(os.Getenv(\"SQA_OPT_OUT\"))\n\t}\n\n\tif !optOut {\n\t\toptOut, _ = strconv.ParseBool(os.Getenv(\"SQA-OPT-OUT\"))\n\t}\n\n\tif !optOut {\n\t\tl.Info(\"Software quality assurance features are enabled. Learn more at: https://www.ory.sh/docs/ecosystem/sqa\")\n\t}\n\n\tm := &Service{\n\t\toptOut:     optOut,\n\t\tinstanceId: uuid.Must(uuid.NewV4()).String(),\n\t\to:          o,\n\t\tc:          segment,\n\t\tl:          l,\n\t\tmem:        new(MemoryStatistics),\n\t}\n\n\tinstance = m\n\n\tgo m.Identify()\n\tgo m.Track()\n\n\treturn m\n}\n\n// Identify enables reporting to segment.\nfunc (sw *Service) Identify() {\n\tIdentifySend(sw, true)\n\n\t// User has not opt-out then make identify to be sent every 6 hours\n\tif !sw.optOut {\n\t\tfor range time.Tick(time.Hour * 6) {\n\t\t\tIdentifySend(sw, false)\n\t\t}\n\t}\n}\n\nfunc IdentifySend(sw *Service, startup bool) {\n\tif err := resilience.Retry(sw.l, time.Minute*5, time.Hour*6, func() error {\n\t\treturn sw.c.Enqueue(analytics.Identify{\n\t\t\tInstanceId:   sw.instanceId,\n\t\t\tDeploymentId: sw.o.DeploymentId,\n\t\t\tProject:      sw.o.Service,\n\n\t\t\tDatabaseDialect:  sw.o.DBDialect,\n\t\t\tProductVersion:   sw.o.BuildVersion,\n\t\t\tProductBuild:     sw.o.BuildHash,\n\t\t\tUptimeDeployment: 0,\n\t\t\tUptimeInstance:   math.Round(time.Since(sw.o.StartTime).Seconds()),\n\t\t\tIsDevelopment:    sw.o.IsDevelopment,\n\t\t\tIsOptOut:         sw.optOut,\n\t\t\tStartup:          startup,\n\t\t})\n\t}); err != nil {\n\t\tsw.l.WithError(err).Debug(\"Could not commit anonymized environment information\")\n\t}\n}\n\n// Track commits memory statistics to segment.\nfunc (sw *Service) Track() {\n\tif sw.optOut {\n\t\treturn\n\t}\n\n\tfor {\n\t\tsw.mem.Update()\n\t\tif err := sw.c.Enqueue(analytics.Track{\n\t\t\tInstanceId:   sw.instanceId,\n\t\t\tDeploymentId: sw.o.DeploymentId,\n\t\t\tProject:      sw.o.Service,\n\n\t\t\tCPU:            runtime.NumCPU(),\n\t\t\tOsName:         runtime.GOOS,\n\t\t\tOsArchitecture: runtime.GOARCH,\n\t\t\tAlloc:          sw.mem.Alloc,\n\t\t\tTotalAlloc:     sw.mem.TotalAlloc,\n\t\t\tFrees:          sw.mem.Frees,\n\t\t\tMallocs:        sw.mem.Mallocs,\n\t\t\tLookups:        sw.mem.Lookups,\n\t\t\tSys:            sw.mem.Sys,\n\t\t\tNumGC:          sw.mem.NumGC,\n\t\t\tHeapAlloc:      sw.mem.HeapAlloc,\n\t\t\tHeapInuse:      sw.mem.HeapInuse,\n\t\t\tHeapIdle:       sw.mem.HeapIdle,\n\t\t\tHeapObjects:    sw.mem.HeapObjects,\n\t\t\tHeapReleased:   sw.mem.HeapReleased,\n\t\t\tHeapSys:        sw.mem.HeapSys,\n\t\t}); err != nil {\n\t\t\tsw.l.WithError(err).Debug(\"Could not commit anonymized telemetry data\")\n\t\t}\n\t\ttime.Sleep(sw.o.MemoryInterval)\n\t}\n}\n\n// ServeHTTP is a middleware for sending meta information to segment.\nfunc (sw *Service) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tvar start time.Time\n\tif !sw.optOut {\n\t\tstart = time.Now()\n\t}\n\n\tnext(rw, r)\n\n\tif sw.optOut {\n\t\treturn\n\t}\n\n\tlatency := time.Since(start).Milliseconds()\n\tpath := sw.anonymizePath(r.URL.Path)\n\thost := urlx.ExtractPublicAddress(sw.o.Hostname, r.Header.Get(XForwardedHostHeader), r.Host)\n\n\t// Collecting request info\n\tstat, _ := httpx.GetResponseMeta(rw)\n\n\tif err := sw.c.Enqueue(analytics.Page{\n\t\tInstanceId:     sw.instanceId,\n\t\tDeploymentId:   sw.o.DeploymentId,\n\t\tProject:        sw.o.Service,\n\t\tUrlHost:        host,\n\t\tUrlPath:        path,\n\t\tRequestCode:    stat,\n\t\tRequestLatency: int(latency),\n\t}); err != nil {\n\t\tsw.l.WithError(err).Debug(\"Could not commit anonymized telemetry data\")\n\t\t// do nothing...\n\t}\n}\n\nfunc (sw *Service) UnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {\n\tvar start time.Time\n\tif !sw.optOut {\n\t\tstart = time.Now()\n\t}\n\n\tresp, err := handler(ctx, req)\n\n\tif sw.optOut {\n\t\treturn resp, err\n\t}\n\n\tlatency := time.Since(start).Milliseconds()\n\n\thosts := []string{sw.o.Hostname}\n\tif md, ok := metadata.FromIncomingContext(ctx); ok {\n\t\tfor _, h := range knownHeaders {\n\t\t\tif v := md.Get(h); len(v) > 0 {\n\t\t\t\thosts = append(hosts, v[0])\n\t\t\t}\n\t\t}\n\t}\n\thost := urlx.ExtractPublicAddress(hosts...)\n\n\tif err := sw.c.Enqueue(analytics.Page{\n\t\tInstanceId:     sw.instanceId,\n\t\tDeploymentId:   sw.o.DeploymentId,\n\t\tProject:        sw.o.Service,\n\t\tUrlHost:        host,\n\t\tUrlPath:        info.FullMethod,\n\t\tRequestCode:    int(status.Code(err)),\n\t\tRequestLatency: int(latency),\n\t}); err != nil {\n\t\tsw.l.WithError(err).Debug(\"Could not commit anonymized telemetry data\")\n\t\t// do nothing...\n\t}\n\n\treturn resp, err\n}\n\nfunc (sw *Service) StreamInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {\n\t// this needs a bit of thought, but we don't have streaming RPCs currently anyway\n\tsw.l.Info(\"The telemetry stream interceptor is not yet implemented!\")\n\treturn handler(srv, stream)\n}\n\nfunc (sw *Service) Close() error {\n\treturn sw.c.Close()\n}\n\nfunc (sw *Service) anonymizePath(path string) string {\n\tpaths := sw.o.WhitelistedPaths\n\tpath = strings.ToLower(path)\n\n\tfor _, p := range paths {\n\t\tp = strings.ToLower(p)\n\t\tif path == p {\n\t\t\treturn p\n\t\t} else if len(path) > len(p) && path[:len(p)+1] == p+\"/\" {\n\t\t\treturn p\n\t\t}\n\t}\n\n\treturn \"/\"\n}\n\nfunc (sw *Service) anonymizeQuery(query url.Values, salt string) string {\n\tfor _, q := range query {\n\t\tfor i, s := range q {\n\t\t\tif s != \"\" {\n\t\t\t\ts = Hash(s + \"|\" + salt)\n\t\t\t\tq[i] = s\n\t\t\t}\n\t\t}\n\t}\n\treturn query.Encode()\n}\n"
  },
  {
    "path": "oryx/migratest/refresh.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n//go:build refresh\n// +build refresh\n\npackage migratest\n\nimport (\n\t\"encoding/json\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc WriteFixtureOnError(t *testing.T, err error, actual interface{}, location string) {\n\tcontent, err := json.MarshalIndent(actual, \"\", \"  \")\n\trequire.NoError(t, err)\n\trequire.NoError(t, os.MkdirAll(filepath.Dir(location), 0777))\n\trequire.NoError(t, os.WriteFile(location, content, 0666))\n}\n"
  },
  {
    "path": "oryx/migratest/run.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage migratest\n\nimport (\n\t\"encoding/json\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc ContainsExpectedIds(t *testing.T, path string, ids []string) {\n\tfiles, err := os.ReadDir(path)\n\trequire.NoError(t, err)\n\n\tfor _, f := range files {\n\t\tif filepath.Ext(f.Name()) == \".json\" {\n\t\t\texpected := strings.TrimSuffix(filepath.Base(f.Name()), \".json\")\n\t\t\tassert.Contains(t, ids, expected)\n\t\t}\n\t}\n}\n\nfunc CompareWithFixture(t *testing.T, actual interface{}, prefix string, id string) {\n\tlocation := filepath.Join(\"fixtures\", prefix, id+\".json\")\n\t//#nosec G304 -- false positive\n\texpected, err := os.ReadFile(location)\n\tWriteFixtureOnError(t, err, actual, location)\n\n\tactualJSON, err := json.Marshal(actual)\n\trequire.NoError(t, err)\n\n\tif !assert.JSONEq(t, string(expected), string(actualJSON)) {\n\t\tWriteFixtureOnError(t, nil, actual, location)\n\t}\n}\n"
  },
  {
    "path": "oryx/migratest/strict.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n//go:build !refresh\n// +build !refresh\n\npackage migratest\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc WriteFixtureOnError(t *testing.T, err error, actual interface{}, location string) {\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "oryx/networkx/listener.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage networkx\n\nimport (\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/ory/x/configx\"\n)\n\nfunc AddressIsUnixSocket(address string) bool {\n\treturn strings.HasPrefix(address, \"unix:\")\n}\n\nfunc MakeListener(address string, socketPermission *configx.UnixPermission) (net.Listener, error) {\n\tif AddressIsUnixSocket(address) {\n\t\taddr := strings.TrimPrefix(address, \"unix:\")\n\t\tl, err := net.Listen(\"unix\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\terr = socketPermission.SetPermission(addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn l, nil\n\t}\n\treturn net.Listen(\"tcp\", address)\n}\n"
  },
  {
    "path": "oryx/networkx/manager.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage networkx\n\nimport (\n\t\"embed\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\n// Migrations of the network manager. Apply by merging with your local migrations using\n// fsx.Merge() and then passing all to the migration box.\n//\n//go:embed migrations/sql/*.sql\nvar Migrations embed.FS\n\nfunc Determine(c *pop.Connection) (*Network, error) {\n\tvar p Network\n\tif err := sqlcon.HandleError(c.Q().Order(\"created_at ASC\").First(&p)); err != nil {\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\tnp := NewNetwork()\n\t\t\tif err := c.Create(np); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn np, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\treturn &p, nil\n}\n"
  },
  {
    "path": "oryx/networkx/migrations/sql/20150100000001000000_networks.cockroach.down.sql",
    "content": "DROP TABLE \"networks\";"
  },
  {
    "path": "oryx/networkx/migrations/sql/20150100000001000000_networks.cockroach.up.sql",
    "content": "CREATE TABLE \"networks\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/networkx/migrations/sql/20150100000001000000_networks.mysql.down.sql",
    "content": "DROP TABLE `networks`;"
  },
  {
    "path": "oryx/networkx/migrations/sql/20150100000001000000_networks.mysql.up.sql",
    "content": "CREATE TABLE `networks` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/networkx/migrations/sql/20150100000001000000_networks.postgres.down.sql",
    "content": "DROP TABLE \"networks\";"
  },
  {
    "path": "oryx/networkx/migrations/sql/20150100000001000000_networks.postgres.up.sql",
    "content": "CREATE TABLE \"networks\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/networkx/migrations/sql/20150100000001000000_networks.sqlite3.down.sql",
    "content": "DROP TABLE \"networks\";"
  },
  {
    "path": "oryx/networkx/migrations/sql/20150100000001000000_networks.sqlite3.up.sql",
    "content": "CREATE TABLE \"networks\" (\n\"id\" TEXT PRIMARY KEY,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "oryx/networkx/migrations/templates/20150100000001_networks.down.fizz",
    "content": "drop_table(\"networks\")\n"
  },
  {
    "path": "oryx/networkx/migrations/templates/20150100000001_networks.up.fizz",
    "content": "create_table(\"networks\") {\n  t.Column(\"id\", \"uuid\", {primary: true})\n}\n"
  },
  {
    "path": "oryx/networkx/network.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage networkx\n\nimport (\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype Network struct {\n\tID uuid.UUID `json:\"id\" db:\"id\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" db:\"updated_at\"`\n}\n\nfunc (p Network) TableName() string {\n\treturn \"networks\"\n}\n\nfunc NewNetwork() *Network {\n\treturn &Network{\n\t\tID: uuid.Must(uuid.NewV4()),\n\t}\n}\n"
  },
  {
    "path": "oryx/openapix/doc.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// Package openapi contains definitions commonly used in Ory's APIs\n// such as pagination, JSON patches, and more.\npackage openapix\n"
  },
  {
    "path": "oryx/openapix/jsonpatch.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage openapix\n\n// A JSONPatchDocument request\n//\n// swagger:model jsonPatchDocument\ntype JSONPatchDocument []JSONPatch\n\n// A JSONPatch document as defined by RFC 6902\n//\n// swagger:model jsonPatch\ntype JSONPatch struct {\n\t// The operation to be performed. One of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\".\n\t//\n\t// required: true\n\t// example: replace\n\tOp string `json:\"op\"`\n\n\t// The path to the target path. Uses JSON pointer notation.\n\t//\n\t// Learn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\n\t//\n\t// required: true\n\t// example: /name\n\tPath string `json:\"path\"`\n\n\t// The value to be used within the operations.\n\t//\n\t// Learn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\n\t//\n\t// example: foobar\n\tValue interface{} `json:\"value\"`\n\n\t// This field is used together with operation \"move\" and uses JSON Pointer notation.\n\t//\n\t// Learn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\n\t//\n\t// example: /name\n\tFrom string `json:\"from\"`\n}\n"
  },
  {
    "path": "oryx/openapix/pagination.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage openapix\n\n// swagger:model tokenPaginationHeaders\ntype TokenPaginationHeaders struct {\n\t// The link header contains pagination links.\n\t//\n\t// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// in: header\n\tLink string `json:\"link\"`\n\n\t// The total number of clients.\n\t//\n\t// in: header\n\tXTotalCount string `json:\"x-total-count\"`\n}\n\n// swagger:model tokenPagination\ntype TokenPaginationParams struct {\n\t// Items per page\n\t//\n\t// This is the number of items per page to return.\n\t// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\t// default: 250\n\t// min: 1\n\t// max: 1000\n\tPageSize int `json:\"page_size\"`\n\n\t// Next Page Token\n\t//\n\t// The next page token.\n\t// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\tPageToken string `json:\"page_token\"`\n}\n"
  },
  {
    "path": "oryx/osx/env.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage osx\n\nimport (\n\t\"cmp\"\n\t\"os\"\n)\n\n// GetenvDefault returns an environment variable or the default value if it is empty.\nfunc GetenvDefault(key string, def string) string {\n\treturn cmp.Or(os.Getenv(key), def)\n}\n"
  },
  {
    "path": "oryx/osx/file.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage osx\n\nimport (\n\t\"encoding/base64\"\n\t\"io\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/httpx\"\n)\n\ntype options struct {\n\tdisableFileLoader            bool\n\tdisableHTTPLoader            bool\n\tdisableBase64Loader          bool\n\tbase64enc                    *base64.Encoding\n\tdisableResilientBase64Loader bool\n\thc                           *retryablehttp.Client\n}\n\ntype Option func(o *options)\n\nfunc (o *options) apply(opts []Option) *options {\n\tfor _, f := range opts {\n\t\tf(o)\n\t}\n\treturn o\n}\n\nfunc newOptions() *options {\n\treturn &options{\n\t\tdisableFileLoader:   false,\n\t\tdisableHTTPLoader:   false,\n\t\tdisableBase64Loader: false,\n\t\tbase64enc:           base64.RawURLEncoding,\n\t\thc:                  httpx.NewResilientClient(),\n\t}\n}\n\n// WithDisabledFileLoader disables the file loader.\nfunc WithDisabledFileLoader() Option {\n\treturn func(o *options) {\n\t\to.disableFileLoader = true\n\t}\n}\n\n// WithEnabledFileLoader enables the file loader.\nfunc WithEnabledFileLoader() Option {\n\treturn func(o *options) {\n\t\to.disableFileLoader = false\n\t}\n}\n\n// WithDisabledHTTPLoader disables the HTTP loader.\nfunc WithDisabledHTTPLoader() Option {\n\treturn func(o *options) {\n\t\to.disableHTTPLoader = true\n\t}\n}\n\n// WithEnabledHTTPLoader enables the HTTP loader.\nfunc WithEnabledHTTPLoader() Option {\n\treturn func(o *options) {\n\t\to.disableHTTPLoader = false\n\t}\n}\n\n// WithDisabledBase64Loader disables the base64 loader.\nfunc WithDisabledBase64Loader() Option {\n\treturn func(o *options) {\n\t\to.disableBase64Loader = true\n\t}\n}\n\n// WithEnabledBase64Loader disables the base64 loader.\nfunc WithEnabledBase64Loader() Option {\n\treturn func(o *options) {\n\t\to.disableBase64Loader = false\n\t}\n}\n\n// WithBase64Encoding sets the base64 encoding.\nfunc WithBase64Encoding(enc *base64.Encoding) Option {\n\treturn func(o *options) {\n\t\to.base64enc = enc\n\t}\n}\n\n// WithoutResilientBase64Encoding sets the base64 encoding.\nfunc WithoutResilientBase64Encoding() Option {\n\treturn func(o *options) {\n\t\to.disableResilientBase64Loader = true\n\t}\n}\n\n// WithHTTPClient sets the HTTP client.\nfunc WithHTTPClient(hc *retryablehttp.Client) Option {\n\treturn func(o *options) {\n\t\to.hc = hc\n\t}\n}\n\n// RestrictedReadFile works similar to ReadFileFromAllSources but has all\n// sources disabled per default. You need to enable the loaders you wish to use\n// explicitly.\nfunc RestrictedReadFile(source string, opts ...Option) (bytes []byte, err error) {\n\to := newOptions()\n\to.disableFileLoader = true\n\to.disableBase64Loader = true\n\to.disableHTTPLoader = true\n\treturn readFile(source, o.apply(opts))\n}\n\n// ReadFileFromAllSources reads a file from base64, http, https, and file sources.\n//\n// Using options, you can disable individual loaders. For example, the following will\n// return an error:\n//\n//\tReadFileFromAllSources(\"https://foo.bar/baz.txt\", WithDisabledHTTPLoader())\n//\n// Possible formats are:\n//\n// - /path/to/file\n// - file:///path/to/file\n// - https://host.com/path/to/file\n// - http://host.com/path/to/file\n// - base64://<base64 encoded string>\n//\n// For more options, check:\n//\n// - WithDisabledFileLoader\n// - WithDisabledHTTPLoader\n// - WithDisabledBase64Loader\n// - WithBase64Encoding\n// - WithHTTPClient\nfunc ReadFileFromAllSources(source string, opts ...Option) (bytes []byte, err error) {\n\treturn readFile(source, newOptions().apply(opts))\n}\n\nfunc readFile(source string, o *options) (bytes []byte, err error) {\n\tparsed, err := url.Parse(source)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to parse URL\")\n\t}\n\n\tswitch parsed.Scheme {\n\tcase \"\":\n\t\tif o.disableFileLoader {\n\t\t\treturn nil, errors.New(\"file loader disabled\")\n\t\t}\n\n\t\t//#nosec G304 -- false positive\n\t\tbytes, err = os.ReadFile(source)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"unable to read the file\")\n\t\t}\n\tcase \"file\":\n\t\tif o.disableFileLoader {\n\t\t\treturn nil, errors.New(\"file loader disabled\")\n\t\t}\n\n\t\t//#nosec G304 -- false positive\n\t\tbytes, err = os.ReadFile(parsed.Host + parsed.Path)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"unable to read the file\")\n\t\t}\n\tcase \"http\", \"https\":\n\t\tif o.disableHTTPLoader {\n\t\t\treturn nil, errors.New(\"http(s) loader disabled\")\n\t\t}\n\t\tresp, err := o.hc.Get(parsed.String())\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"unable to load remote file\")\n\t\t}\n\t\tdefer resp.Body.Close()\n\n\t\tbytes, err = io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"unable to read the HTTP response body\")\n\t\t}\n\tcase \"base64\":\n\t\tif o.disableBase64Loader {\n\t\t\treturn nil, errors.New(\"base64 loader disabled\")\n\t\t}\n\n\t\tif o.disableResilientBase64Loader {\n\t\t\tbytes, err = o.base64enc.DecodeString(strings.TrimPrefix(source, \"base64://\"))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, errors.Wrap(err, \"unable to base64 decode the location\")\n\t\t\t}\n\t\t\treturn bytes, nil\n\t\t}\n\n\t\tfor _, enc := range []*base64.Encoding{\n\t\t\tbase64.StdEncoding,\n\t\t\tbase64.URLEncoding,\n\t\t\tbase64.RawURLEncoding,\n\t\t\tbase64.RawStdEncoding,\n\t\t} {\n\t\t\tbytes, err = enc.DecodeString(strings.TrimPrefix(source, \"base64://\"))\n\t\t\tif err == nil {\n\t\t\t\treturn bytes, nil\n\t\t\t}\n\t\t}\n\n\t\treturn nil, errors.Wrap(err, \"unable to base64 decode the location\")\n\tdefault:\n\t\treturn nil, errors.Errorf(\"unsupported source `%s`\", parsed.Scheme)\n\t}\n\n\treturn bytes, nil\n\n}\n"
  },
  {
    "path": "oryx/osx/stub/text.txt",
    "content": "hello world"
  },
  {
    "path": "oryx/otelx/attribute.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage otelx\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n)\n\nconst nullString = \"<null>\"\n\nfunc StringAttrs(attrs map[string]string) []attribute.KeyValue {\n\ts := make([]attribute.KeyValue, 0, len(attrs))\n\tfor k, v := range attrs {\n\t\ts = append(s, attribute.String(k, v))\n\t}\n\treturn s\n}\n\nfunc AutoInt[I int | int32 | int64](k string, v I) attribute.KeyValue {\n\t// Internally, the OpenTelemetry SDK uses int64 for all integer values anyway.\n\treturn attribute.Int64(k, int64(v))\n}\n\nfunc Nullable[V any, VN *V | sql.Null[V], A func(string, V) attribute.KeyValue](a A, k string, v VN) attribute.KeyValue {\n\tswitch v := any(v).(type) {\n\tcase *V:\n\t\tif v == nil {\n\t\t\treturn attribute.String(k, nullString)\n\t\t}\n\t\treturn a(k, *v)\n\tcase sql.Null[V]:\n\t\tif !v.Valid {\n\t\t\treturn attribute.String(k, nullString)\n\t\t}\n\t\treturn a(k, v.V)\n\t}\n\t// This should never happen, as the type switch above is exhaustive to the generic type VN.\n\treturn attribute.String(k, fmt.Sprintf(\"<got unsupported type %T>\", v))\n}\n\nfunc NullString[V *string | sql.Null[string]](k string, v V) attribute.KeyValue {\n\treturn Nullable(attribute.String, k, v)\n}\n\nfunc NullStringer(k string, v fmt.Stringer) attribute.KeyValue {\n\tif v == nil {\n\t\treturn attribute.String(k, nullString)\n\t}\n\treturn attribute.String(k, v.String())\n}\n"
  },
  {
    "path": "oryx/otelx/config.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage otelx\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"io\"\n)\n\ntype JaegerConfig struct {\n\tLocalAgentAddress string         `json:\"local_agent_address\"`\n\tSampling          JaegerSampling `json:\"sampling\"`\n}\n\ntype ZipkinConfig struct {\n\tServerURL string         `json:\"server_url\"`\n\tSampling  ZipkinSampling `json:\"sampling\"`\n}\n\ntype OTLPConfig struct {\n\tServerURL           string       `json:\"server_url\"`\n\tInsecure            bool         `json:\"insecure\"`\n\tSampling            OTLPSampling `json:\"sampling\"`\n\tAuthorizationHeader string       `json:\"authorization_header\"`\n}\n\ntype JaegerSampling struct {\n\tServerURL    string  `json:\"server_url\"`\n\tTraceIDRatio float64 `json:\"trace_id_ratio\"`\n}\n\ntype ZipkinSampling struct {\n\tSamplingRatio float64 `json:\"sampling_ratio\"`\n}\n\ntype OTLPSampling struct {\n\tSamplingRatio float64 `json:\"sampling_ratio\"`\n}\n\ntype ProvidersConfig struct {\n\tJaeger JaegerConfig `json:\"jaeger\"`\n\tZipkin ZipkinConfig `json:\"zipkin\"`\n\tOTLP   OTLPConfig   `json:\"otlp\"`\n}\n\ntype Config struct {\n\tServiceName           string          `json:\"service_name\"`\n\tDeploymentEnvironment string          `json:\"deployment_environment\"`\n\tProvider              string          `json:\"provider\"`\n\tProviders             ProvidersConfig `json:\"providers\"`\n}\n\n//go:embed config.schema.json\nvar ConfigSchema []byte\n\nconst ConfigSchemaID = \"ory://tracing-config\"\n\n// AddConfigSchema adds the tracing schema to the compiler.\n// The interface is specified instead of `jsonschema.Compiler` to allow the use of any jsonschema library fork or version.\nfunc AddConfigSchema(c interface {\n\tAddResource(url string, r io.Reader) error\n},\n) error {\n\treturn c.AddResource(ConfigSchemaID, bytes.NewReader(ConfigSchema))\n}\n"
  },
  {
    "path": "oryx/otelx/config.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"$id\": \"ory://tracing-config\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"description\": \"Configure distributed tracing using OpenTelemetry\",\n  \"properties\": {\n    \"provider\": {\n      \"type\": \"string\",\n      \"description\": \"Set this to the tracing backend you wish to use. Supports Jaeger, Zipkin, and OTEL.\",\n      \"enum\": [\"jaeger\", \"otel\", \"zipkin\"],\n      \"examples\": [\"jaeger\"]\n    },\n    \"service_name\": {\n      \"type\": \"string\",\n      \"description\": \"Specifies the service name to use on the tracer.\",\n      \"examples\": [\"Ory Hydra\", \"Ory Kratos\", \"Ory Keto\", \"Ory Oathkeeper\"]\n    },\n    \"deployment_environment\": {\n      \"type\": \"string\",\n      \"description\": \"Specifies the deployment environment to use on the tracer.\",\n      \"examples\": [\"development\", \"staging\", \"production\"]\n    },\n    \"providers\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"jaeger\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures the jaeger tracing backend.\",\n          \"properties\": {\n            \"local_agent_address\": {\n              \"type\": \"string\",\n              \"description\": \"The address of the jaeger-agent where spans should be sent to.\",\n              \"anyOf\": [\n                {\n                  \"title\": \"IPv6 Address and Port\",\n                  \"pattern\": \"^\\\\[(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\\\\]:([0-9]*)$\"\n                },\n                {\n                  \"title\": \"IPv4 Address and Port\",\n                  \"pattern\": \"^([0-9]{1,3}\\\\.){3}[0-9]{1,3}:([0-9]*)$\"\n                },\n                {\n                  \"title\": \"Hostname and Port\",\n                  \"pattern\": \"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\\\-]*[a-zA-Z0-9])\\\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\\\-]*[A-Za-z0-9]):([0-9]*)$\"\n                }\n              ],\n              \"examples\": [\"127.0.0.1:6831\"]\n            },\n            \"sampling\": {\n              \"type\": \"object\",\n              \"propertyNames\": {\n                \"enum\": [\"server_url\", \"trace_id_ratio\"]\n              },\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"server_url\": {\n                  \"type\": \"string\",\n                  \"description\": \"The address of jaeger-agent's HTTP sampling server\",\n                  \"format\": \"uri\",\n                  \"examples\": [\"http://localhost:5778/sampling\"]\n                },\n                \"trace_id_ratio\": {\n                  \"type\": \"number\",\n                  \"description\": \"Trace Id ratio sample\",\n                  \"examples\": [0.5]\n                }\n              }\n            }\n          }\n        },\n        \"zipkin\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures the zipkin tracing backend.\",\n          \"properties\": {\n            \"server_url\": {\n              \"type\": \"string\",\n              \"description\": \"The address of the Zipkin server where spans should be sent to.\",\n              \"format\": \"uri\",\n              \"examples\": [\"http://localhost:9411/api/v2/spans\"]\n            },\n            \"sampling\": {\n              \"type\": \"object\",\n              \"propertyNames\": {\n                \"enum\": [\"sampling_ratio\"]\n              },\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"sampling_ratio\": {\n                  \"type\": \"number\",\n                  \"description\": \"Sampling ratio for spans.\",\n                  \"examples\": [0.4]\n                }\n              }\n            }\n          }\n        },\n        \"otlp\": {\n          \"type\": \"object\",\n          \"additionalProperties\": false,\n          \"description\": \"Configures the OTLP tracing backend.\",\n          \"properties\": {\n            \"server_url\": {\n              \"type\": \"string\",\n              \"description\": \"The endpoint of the OTLP exporter (HTTP) where spans should be sent to.\",\n              \"anyOf\": [\n                {\n                  \"title\": \"IPv6 Address and Port\",\n                  \"pattern\": \"^\\\\[(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\\\\]:([0-9]*)$\"\n                },\n                {\n                  \"title\": \"IPv4 Address and Port\",\n                  \"pattern\": \"^([0-9]{1,3}\\\\.){3}[0-9]{1,3}:([0-9]*)$\"\n                },\n                {\n                  \"title\": \"Hostname and Port\",\n                  \"pattern\": \"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\\\-]*[a-zA-Z0-9])\\\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\\\-]*[A-Za-z0-9]):([0-9]*)$\"\n                }\n              ],\n              \"examples\": [\"localhost:4318\"]\n            },\n            \"insecure\": {\n              \"type\": \"boolean\",\n              \"description\": \"Will use HTTP if set to true; defaults to HTTPS.\"\n            },\n            \"sampling\": {\n              \"type\": \"object\",\n              \"propertyNames\": {\n                \"enum\": [\"sampling_ratio\"]\n              },\n              \"additionalProperties\": false,\n              \"properties\": {\n                \"sampling_ratio\": {\n                  \"type\": \"number\",\n                  \"description\": \"Sampling ratio for spans.\",\n                  \"examples\": [0.4]\n                }\n              }\n            },\n            \"authorization_header\": {\n              \"type\": \"string\",\n              \"examples\": [\"Bearer 2389s8fs9d8fus9f\"]\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "oryx/otelx/jaeger.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage otelx\n\nimport (\n\t\"net\"\n\n\t\"go.opentelemetry.io/contrib/propagators/b3\"\n\tjaegerPropagator \"go.opentelemetry.io/contrib/propagators/jaeger\"\n\t\"go.opentelemetry.io/contrib/samplers/jaegerremote\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/jaeger\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.27.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\n// SetupJaeger configures and returns a Jaeger tracer.\n//\n// The returned tracer will by default attempt to send spans to a local Jaeger agent.\n// Optionally, [otelx.JaegerConfig.LocalAgentAddress] can be set to specify a different target.\n//\n// By default, unless a parent sampler has taken a sampling decision, every span is sampled.\n// [otelx.JaegerSampling.TraceIDRatio] may be used to customize the sampling probability,\n// optionally alongside [otelx.JaegerSampling.ServerURL] to consult a remote server\n// for the sampling strategy to be used.\nfunc SetupJaeger(t *Tracer, tracerName string, c *Config) (trace.Tracer, error) {\n\thost, port, err := net.SplitHostPort(c.Providers.Jaeger.LocalAgentAddress)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\texp, err := jaeger.New(\n\t\tjaeger.WithAgentEndpoint(\n\t\t\tjaeger.WithAgentHost(host), jaeger.WithAgentPort(port),\n\t\t),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttpOpts := []sdktrace.TracerProviderOption{\n\t\tsdktrace.WithBatcher(exp),\n\t\tsdktrace.WithResource(resource.NewWithAttributes(\n\t\t\tsemconv.SchemaURL,\n\t\t\tsemconv.ServiceName(c.ServiceName),\n\t\t\tsemconv.DeploymentEnvironmentName(c.DeploymentEnvironment))),\n\t}\n\n\tsamplingServerURL := c.Providers.Jaeger.Sampling.ServerURL\n\ttraceIdRatio := c.Providers.Jaeger.Sampling.TraceIDRatio\n\n\tsampler := sdktrace.TraceIDRatioBased(traceIdRatio)\n\n\tif samplingServerURL != \"\" {\n\t\tsampler = jaegerremote.New(\n\t\t\t\"jaegerremote\",\n\t\t\tjaegerremote.WithSamplingServerURL(samplingServerURL),\n\t\t\tjaegerremote.WithInitialSampler(sampler),\n\t\t)\n\t}\n\n\t// Respect any sampling decision taken by the client.\n\tsampler = sdktrace.ParentBased(sampler)\n\ttpOpts = append(tpOpts, sdktrace.WithSampler(sampler))\n\n\ttp := sdktrace.NewTracerProvider(tpOpts...)\n\totel.SetTracerProvider(tp)\n\n\t// At the moment, software across our cloud stack only support Zipkin (B3)\n\t// and Jaeger propagation formats. Proposals for standardized formats for\n\t// context propagation are in the works (ref: https://www.w3.org/TR/trace-context/\n\t// and https://www.w3.org/TR/baggage/).\n\t//\n\t// Simply add propagation.TraceContext{} and propagation.Baggage{}\n\t// here to enable those as well.\n\tprop := propagation.NewCompositeTextMapPropagator(\n\t\tpropagation.TraceContext{},\n\t\tjaegerPropagator.Jaeger{},\n\t\tb3.New(b3.WithInjectEncoding(b3.B3MultipleHeader|b3.B3SingleHeader)),\n\t\tpropagation.Baggage{},\n\t)\n\totel.SetTextMapPropagator(prop)\n\treturn tp.Tracer(tracerName), nil\n}\n"
  },
  {
    "path": "oryx/otelx/middleware.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage otelx\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n)\n\nvar WithDefaultFilters otelhttp.Option = otelhttp.WithFilter(func(r *http.Request) bool {\n\treturn !(strings.HasPrefix(r.URL.Path, \"/health\") ||\n\t\tstrings.HasPrefix(r.URL.Path, \"/admin/health\") ||\n\t\tstrings.HasPrefix(r.URL.Path, \"/metrics\") ||\n\t\tstrings.HasPrefix(r.URL.Path, \"/admin/metrics\"))\n})\n\ntype contextKey int\n\nconst callbackContextKey contextKey = iota\n\nfunc SpanNameRecorderMiddleware(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tdefer func() {\n\t\t\tcb, _ := r.Context().Value(callbackContextKey).(func(string))\n\t\t\tif cb == nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif r.Pattern != \"\" {\n\t\t\t\tcb(r.Pattern)\n\t\t\t}\n\t\t}()\n\t\tnext.ServeHTTP(w, r)\n\t})\n}\n\nfunc SpanNameRecorderNegroniFunc(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tdefer func() {\n\t\tcb, _ := r.Context().Value(callbackContextKey).(func(string))\n\t\tif cb == nil {\n\t\t\treturn\n\t\t}\n\t\tif r.Pattern != \"\" {\n\t\t\tcb(r.Pattern)\n\t\t}\n\t}()\n\tnext(w, r)\n}\n\nfunc NewMiddleware(next http.Handler, operation string, opts ...otelhttp.Option) http.Handler {\n\tmyOpts := []otelhttp.Option{\n\t\tWithDefaultFilters,\n\t\totelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {\n\t\t\treturn cmp.Or(r.Pattern, operation, r.Method+\" \"+r.URL.Path)\n\t\t}),\n\t}\n\thandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tcallback := func(s string) {\n\t\t\tr.Pattern = cmp.Or(r.Pattern, s)\n\t\t}\n\t\tctx := context.WithValue(r.Context(), callbackContextKey, callback)\n\t\tr2 := r.WithContext(ctx)\n\t\tnext.ServeHTTP(w, r2)\n\t\tr.Pattern = cmp.Or(r2.Pattern, r.Pattern) // best-effort in case callback never is called\n\t})\n\treturn otelhttp.NewHandler(handler, operation, append(myOpts, opts...)...)\n}\n"
  },
  {
    "path": "oryx/otelx/otel.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage otelx\n\nimport (\n\t\"context\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"go.opentelemetry.io/otel/trace/embedded\"\n\t\"go.opentelemetry.io/otel/trace/noop\"\n\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/stringsx\"\n)\n\ntype (\n\tTracer struct {\n\t\ttracer trace.Tracer\n\t}\n\tProvider interface {\n\t\tTracer(ctx context.Context) *Tracer\n\t}\n)\n\n// New creates a new tracer. If name is empty, a default tracer name is used\n// instead. See: https://godocs.io/go.opentelemetry.io/otel/sdk/trace#TracerProvider.Tracer\nfunc New(name string, l *logrusx.Logger, c *Config) (*Tracer, error) {\n\tt := &Tracer{}\n\n\tif err := t.setup(name, l, c); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn t, nil\n}\n\n// NewNoop creates a new no-op tracer.\nfunc NewNoop() *Tracer {\n\ttp := noop.NewTracerProvider()\n\tt := &Tracer{tracer: tp.Tracer(\"\")}\n\treturn t\n}\n\n// setup constructs the tracer based on the given configuration.\nfunc (t *Tracer) setup(name string, l *logrusx.Logger, c *Config) error {\n\tswitch f := stringsx.SwitchExact(c.Provider); {\n\tcase f.AddCase(\"jaeger\"):\n\t\ttracer, err := SetupJaeger(t, name, c)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tt.tracer = tracer\n\t\tl.Infof(\"Jaeger tracer configured! Sending spans to %s\", c.Providers.Jaeger.LocalAgentAddress)\n\tcase f.AddCase(\"zipkin\"):\n\t\ttracer, err := SetupZipkin(t, name, c)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tt.tracer = tracer\n\t\tl.Infof(\"Zipkin tracer configured! Sending spans to %s\", c.Providers.Zipkin.ServerURL)\n\tcase f.AddCase(\"otel\"):\n\t\ttracer, err := SetupOTLP(t, name, c)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tt.tracer = tracer\n\t\tl.Infof(\"OTLP tracer configured! Sending spans to %s\", c.Providers.OTLP.ServerURL)\n\tcase f.AddCase(\"\"):\n\t\tl.Infof(\"No tracer configured - skipping tracing setup\")\n\t\tt.tracer = noop.NewTracerProvider().Tracer(name)\n\tdefault:\n\t\treturn f.ToUnknownCaseErr()\n\t}\n\n\treturn nil\n}\n\n// IsLoaded returns true if the tracer has been loaded.\nfunc (t *Tracer) IsLoaded() bool {\n\tif t == nil || t.tracer == nil {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Tracer returns the underlying OpenTelemetry tracer.\nfunc (t *Tracer) Tracer() trace.Tracer {\n\treturn t.tracer\n}\n\n// WithOTLP returns a new tracer with the underlying OpenTelemetry Tracer\n// replaced.\nfunc (t *Tracer) WithOTLP(other trace.Tracer) *Tracer {\n\treturn &Tracer{other}\n}\n\n// Provider returns a TracerProvider which in turn yields this tracer unmodified.\nfunc (t *Tracer) Provider() trace.TracerProvider {\n\treturn tracerProvider{t: t.Tracer()}\n}\n\ntype tracerProvider struct {\n\tembedded.TracerProvider\n\tt trace.Tracer\n}\n\nvar _ trace.TracerProvider = tracerProvider{}\n\n// Tracer implements trace.TracerProvider.\nfunc (tp tracerProvider) Tracer(name string, options ...trace.TracerOption) trace.Tracer {\n\treturn tp.t\n}\n"
  },
  {
    "path": "oryx/otelx/otlp.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage otelx\n\nimport (\n\t\"context\"\n\n\t\"go.opentelemetry.io/contrib/propagators/b3\"\n\tjaegerPropagator \"go.opentelemetry.io/contrib/propagators/jaeger\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp\"\n\t\"go.opentelemetry.io/otel/propagation\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.27.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\nfunc SetupOTLP(t *Tracer, tracerName string, c *Config) (trace.Tracer, error) {\n\tctx := context.Background()\n\n\tclientOpts := []otlptracehttp.Option{\n\t\totlptracehttp.WithEndpoint(c.Providers.OTLP.ServerURL),\n\t}\n\n\tif c.Providers.OTLP.Insecure {\n\t\tclientOpts = append(clientOpts, otlptracehttp.WithInsecure())\n\t}\n\n\tif c.Providers.OTLP.AuthorizationHeader != \"\" {\n\t\tclientOpts = append(clientOpts,\n\t\t\totlptracehttp.WithHeaders(map[string]string{\"Authorization\": c.Providers.OTLP.AuthorizationHeader}),\n\t\t)\n\t}\n\n\texp, err := otlptrace.New(\n\t\tctx, otlptracehttp.NewClient(clientOpts...),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttpOpts := []sdktrace.TracerProviderOption{\n\t\tsdktrace.WithBatcher(exp),\n\t\tsdktrace.WithResource(resource.NewWithAttributes(\n\t\t\tsemconv.SchemaURL,\n\t\t\tsemconv.ServiceName(c.ServiceName),\n\t\t\tsemconv.DeploymentEnvironmentName(c.DeploymentEnvironment),\n\t\t)),\n\t\tsdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(\n\t\t\tc.Providers.OTLP.Sampling.SamplingRatio,\n\t\t))),\n\t}\n\n\ttp := sdktrace.NewTracerProvider(tpOpts...)\n\totel.SetTracerProvider(tp)\n\n\totel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(\n\t\tpropagation.TraceContext{},\n\t\tjaegerPropagator.Jaeger{},\n\t\tb3.New(b3.WithInjectEncoding(b3.B3MultipleHeader|b3.B3SingleHeader)),\n\t\tpropagation.Baggage{},\n\t))\n\n\treturn tp.Tracer(tracerName), nil\n}\n"
  },
  {
    "path": "oryx/otelx/semconv/context.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage semconv\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/httpx\"\n)\n\ntype contextKey int\n\nconst contextKeyAttributes contextKey = iota\n\nfunc ContextWithAttributes(ctx context.Context, attrs ...attribute.KeyValue) context.Context {\n\texisting, _ := ctx.Value(contextKeyAttributes).([]attribute.KeyValue)\n\treturn context.WithValue(ctx, contextKeyAttributes, append(existing, attrs...))\n}\n\nfunc AttributesFromContext(ctx context.Context) []attribute.KeyValue {\n\tfromCtx, _ := ctx.Value(contextKeyAttributes).([]attribute.KeyValue)\n\tuniq := make(map[attribute.Key]struct{})\n\tattrs := make([]attribute.KeyValue, 0)\n\tfor i := len(fromCtx) - 1; i >= 0; i-- {\n\t\tif _, ok := uniq[fromCtx[i].Key]; !ok {\n\t\t\tuniq[fromCtx[i].Key] = struct{}{}\n\t\t\tattrs = append(attrs, fromCtx[i])\n\t\t}\n\t}\n\treverse(attrs)\n\treturn attrs\n}\n\nfunc Middleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tctx := ContextWithAttributes(r.Context(),\n\t\tappend(\n\t\t\tAttrGeoLocation(*httpx.ClientGeoLocation(r)),\n\t\t\tAttrClientIP(httpx.ClientIP(r)),\n\t\t)...,\n\t)\n\n\tnext(rw, r.WithContext(ctx))\n}\n\nfunc reverse[S ~[]E, E any](s S) {\n\tfor i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {\n\t\ts[i], s[j] = s[j], s[i]\n\t}\n}\n"
  },
  {
    "path": "oryx/otelx/semconv/deprecated.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage semconv\n\nimport (\n\t\"context\"\n\n\totelattr \"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\n// NewDeprecatedFeatureUsedEvent creates a new event indicating that a deprecated feature was used.\n// It returns the event name and a trace.EventOption that can be used to\n// add the event to a span.\n//\n//\tspan.AddEvent(NewDeprecatedFeatureUsedEvent(ctx, \"deprecated-feature-id\", otelattr.String(\"key\", \"value\")))\nfunc NewDeprecatedFeatureUsedEvent(ctx context.Context, deprecatedCodeFeatureID string, attrs ...otelattr.KeyValue) (string, trace.EventOption) {\n\treturn DeprecatedFeatureUsed.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tappend(\n\t\t\t\t\tattrs,\n\t\t\t\t\tAttributesFromContext(ctx)...,\n\t\t\t\t),\n\t\t\t\tAttrDeprecatedFeatureID(deprecatedCodeFeatureID),\n\t\t\t)...,\n\t\t)\n}\n\nconst (\n\tAttributeKeyDeprecatedCodePathIDAttributeKey AttributeKey = \"DeprecatedFeatureID\"\n\tDeprecatedFeatureUsed                        Event        = \"DeprecatedFeatureUsed\"\n)\n\nfunc AttrDeprecatedFeatureID(id string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyDeprecatedCodePathIDAttributeKey.String(), id)\n}\n"
  },
  {
    "path": "oryx/otelx/semconv/events.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// Package semconv contains OpenTelemetry semantic convention constants.\npackage semconv\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\totelattr \"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/httpx\"\n)\n\ntype Event string\n\nfunc (e Event) String() string {\n\treturn string(e)\n}\n\ntype AttributeKey string\n\nfunc (a AttributeKey) String() string {\n\treturn string(a)\n}\n\nconst (\n\tAttributeKeyIdentityID           AttributeKey = \"IdentityID\"\n\tAttributeKeyNID                  AttributeKey = \"ProjectID\"\n\tAttributeKeyClientIP             AttributeKey = \"ClientIP\"\n\tAttributeKeyGeoLocationCity      AttributeKey = \"GeoLocationCity\"\n\tAttributeKeyGeoLocationRegion    AttributeKey = \"GeoLocationRegion\"\n\tAttributeKeyGeoLocationCountry   AttributeKey = \"GeoLocationCountry\"\n\tAttributeKeyGeoLocationLatitude  AttributeKey = \"GeoLocationLatitude\"\n\tAttributeKeyGeoLocationLongitude AttributeKey = \"GeoLocationLongitude\"\n\tAttributeKeyWorkspace            AttributeKey = \"WorkspaceID\"\n\tAttributeKeySubscriptionID       AttributeKey = \"SubscriptionID\"\n\tAttributeKeyProjectEnvironment   AttributeKey = \"ProjectEnvironment\"\n\tAttributeKeyWorkspaceAPIKeyID    AttributeKey = \"WorkspaceAPIKeyID\"\n\tAttributeKeyProjectAPIKeyID      AttributeKey = \"ProjectAPIKeyID\"\n)\n\nfunc AttrIdentityID[V string | uuid.UUID](val V) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyIdentityID.String(), uuidOrString(val))\n}\n\nfunc AttrNID(val uuid.UUID) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyNID.String(), val.String())\n}\n\nfunc AttrWorkspace(val uuid.UUID) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyWorkspace.String(), val.String())\n}\n\nfunc AttrSubscription(val uuid.UUID) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeySubscriptionID.String(), val.String())\n}\n\nfunc AttrProjectEnvironment(val string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyProjectEnvironment.String(), val)\n}\n\nfunc AttrClientIP(val string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyClientIP.String(), val)\n}\n\nfunc AttrGeoLocation(val httpx.GeoLocation) []otelattr.KeyValue {\n\tgeoLocationAttributes := make([]otelattr.KeyValue, 0, 3)\n\n\tif val.City != \"\" {\n\t\tgeoLocationAttributes = append(geoLocationAttributes, otelattr.String(AttributeKeyGeoLocationCity.String(), val.City))\n\t}\n\tif val.Country != \"\" {\n\t\tgeoLocationAttributes = append(geoLocationAttributes, otelattr.String(AttributeKeyGeoLocationCountry.String(), val.Country))\n\t}\n\tif val.Region != \"\" {\n\t\tgeoLocationAttributes = append(geoLocationAttributes, otelattr.String(AttributeKeyGeoLocationRegion.String(), val.Region))\n\t}\n\tif val.Latitude != nil {\n\t\tgeoLocationAttributes = append(geoLocationAttributes, otelattr.Float64(AttributeKeyGeoLocationLatitude.String(), *val.Latitude))\n\t}\n\tif val.Longitude != nil {\n\t\tgeoLocationAttributes = append(geoLocationAttributes, otelattr.Float64(AttributeKeyGeoLocationLongitude.String(), *val.Longitude))\n\t}\n\n\treturn geoLocationAttributes\n}\n\nfunc AttrWorkspaceAPIKeyID[V string | uuid.UUID](val V) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyWorkspaceAPIKeyID.String(), uuidOrString(val))\n}\n\nfunc AttrProjectAPIKeyID[V string | uuid.UUID](val V) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyProjectAPIKeyID.String(), uuidOrString(val))\n}\n\nfunc uuidOrString[V string | uuid.UUID](val V) string {\n\tswitch val := any(val).(type) {\n\tcase string:\n\t\treturn val\n\tcase uuid.UUID:\n\t\treturn val.String()\n\t}\n\tpanic(\"unreachable\")\n}\n"
  },
  {
    "path": "oryx/otelx/semconv/warning.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage semconv\n\nimport (\n\t\"context\"\n\n\totelattr \"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\n// NewWarning creates a new warning event with the given ID and attributes.\n// It returns the event name and a trace.EventOption that can be used to\n// add the event to a span.\n//\n//\tspan.AddEvent(NewWarning(ctx, \"warning-id\", otelattr.String(\"key\", \"value\")))\nfunc NewWarning(ctx context.Context, id string, attrs ...otelattr.KeyValue) (string, trace.EventOption) {\n\treturn Warning.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tappend(\n\t\t\t\t\tattrs,\n\t\t\t\t\tAttributesFromContext(ctx)...,\n\t\t\t\t),\n\t\t\t\totelattr.String(AttributeWarningID.String(), id),\n\t\t\t)...,\n\t\t)\n}\n\nconst (\n\tWarning            Event        = \"Warning\"\n\tAttributeWarningID AttributeKey = \"WarningID\"\n)\n\nfunc AttrWarningID(id string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeWarningID.String(), id)\n}\n"
  },
  {
    "path": "oryx/otelx/withspan.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage otelx\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\n\tpkgerrors \"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/codes\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.27.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\n// WithSpan wraps execution of f in a span identified by name.\n//\n// If f returns an error or panics, the span status will be set to the error\n// state. The error (or panic) will be propagated unmodified.\n//\n// f will be wrapped in a child span by default. To make a new root span\n// instead, pass the trace.WithNewRoot() option.\nfunc WithSpan(ctx context.Context, name string, f func(context.Context) error, opts ...trace.SpanStartOption) (err error) {\n\tctx, span := trace.SpanFromContext(ctx).TracerProvider().Tracer(\"\").Start(ctx, name, opts...)\n\tdefer func() {\n\t\tdefer span.End()\n\t\tif r := recover(); r != nil {\n\t\t\tsetErrorStatusPanic(span, r)\n\t\t\tpanic(r)\n\t\t} else if err != nil {\n\t\t\tspan.SetStatus(codes.Error, err.Error())\n\t\t\tsetErrorTags(span, err)\n\t\t}\n\t}()\n\treturn f(ctx)\n}\n\n// End finishes span, and automatically sets the error state if *err is not nil\n// or during panicking.\n//\n// Usage:\n//\n//\tfunc Divide(ctx context.Context, numerator, denominator int) (ratio int, err error) {\n//\t\tctx, span := tracer.Start(ctx, \"Divide\")\n//\t\tdefer otelx.End(span, &err)\n//\t\tif denominator == 0 {\n//\t\t\treturn 0, errors.New(\"cannot divide by zero\")\n//\t\t}\n//\t\treturn numerator / denominator, nil\n//\t}\n//\n// During a panic, we don't fully conform to OpenTelemetry's semantic\n// conventions because that would require us to emit a span event to attach the\n// stacktrace and error type, and we don't want to do that. Instead, we set the\n// tags on the span directly.\n// https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/\n//\n// For improved compatibility with Datadog, we also set some additional tags as\n// documented here:\n// https://docs.datadoghq.com/standard-attributes/?product=apm&search=error\nfunc End(span trace.Span, err *error) {\n\tdefer span.End()\n\tif r := recover(); r != nil {\n\t\tsetErrorStatusPanic(span, r)\n\t\tpanic(r)\n\t}\n\tif err == nil || *err == nil {\n\t\treturn\n\t}\n\tspan.SetStatus(codes.Error, (*err).Error())\n\tsetErrorTags(span, *err)\n}\n\nfunc setErrorStatusPanic(span trace.Span, recovered any) {\n\tspan.SetAttributes(\n\t\t// OpenTelemetry says to add these attributes to an event, not the span\n\t\t// itself. We don't want to do that, so we're adding them to the span\n\t\t// directly.\n\t\tsemconv.ExceptionEscaped(true),\n\t\t// OpenTelemetry describes \"exception.stacktrace\"  We don't love that,\n\t\t// though, so we're using \"error.stack\" instead, like DataDog).\n\t\tattribute.String(\"error.stack\", stacktrace()),\n\t)\n\tif t := reflect.TypeOf(recovered); t != nil {\n\t\tspan.SetAttributes(semconv.ExceptionType(t.String()))\n\t}\n\tswitch e := recovered.(type) {\n\tcase error:\n\t\tspan.SetStatus(codes.Error, \"panic: \"+e.Error())\n\t\tsetErrorTags(span, e)\n\tcase string, fmt.Stringer:\n\t\tspan.SetStatus(codes.Error, fmt.Sprintf(\"panic: %v\", e))\n\tdefault:\n\t\tspan.SetStatus(codes.Error, \"panic\")\n\tcase nil:\n\t\t// nothing\n\t}\n}\n\nfunc setErrorTags(span trace.Span, err error) {\n\tspan.SetAttributes(\n\t\tattribute.String(\"error\", err.Error()),\n\t\tattribute.String(\"error.message\", err.Error()),                        // DataDog compat\n\t\tattribute.String(\"error.type\", fmt.Sprintf(\"%T\", errors.Unwrap(err))), // the innermost error type is the most useful here\n\t)\n\tif e := interface{ StackTrace() pkgerrors.StackTrace }(nil); errors.As(err, &e) {\n\t\tspan.SetAttributes(attribute.String(\"error.stack\", fmt.Sprintf(\"%+v\", e.StackTrace())))\n\t}\n\tif e := interface{ Reason() string }(nil); errors.As(err, &e) {\n\t\tspan.SetAttributes(attribute.String(\"error.reason\", e.Reason()))\n\t}\n\tif e := interface{ Debug() string }(nil); errors.As(err, &e) {\n\t\tspan.SetAttributes(attribute.String(\"error.debug\", e.Debug()))\n\t}\n\tif e := interface{ ID() string }(nil); errors.As(err, &e) {\n\t\tspan.SetAttributes(attribute.String(\"error.id\", e.ID()))\n\t}\n\tif e := interface{ Details() map[string]interface{} }(nil); errors.As(err, &e) {\n\t\tfor k, v := range e.Details() {\n\t\t\tspan.SetAttributes(attribute.String(\"error.details.\"+k, fmt.Sprintf(\"%v\", v)))\n\t\t}\n\t}\n}\n\nfunc stacktrace() string {\n\tpc := make([]uintptr, 5)\n\tn := runtime.Callers(4, pc)\n\tif n == 0 {\n\t\treturn \"\"\n\t}\n\tpc = pc[:n]\n\tframes := runtime.CallersFrames(pc)\n\n\tvar builder strings.Builder\n\tfor {\n\t\tframe, more := frames.Next()\n\t\tfmt.Fprintf(&builder, \"%s\\n\\t%s:%d\\n\", frame.Function, frame.File, frame.Line)\n\t\tif !more {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn builder.String()\n}\n"
  },
  {
    "path": "oryx/otelx/zipkin.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage otelx\n\nimport (\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/zipkin\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.27.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\nfunc SetupZipkin(t *Tracer, tracerName string, c *Config) (trace.Tracer, error) {\n\texp, err := zipkin.New(c.Providers.Zipkin.ServerURL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttpOpts := []sdktrace.TracerProviderOption{\n\t\tsdktrace.WithBatcher(exp),\n\t\tsdktrace.WithResource(resource.NewWithAttributes(\n\t\t\tsemconv.SchemaURL,\n\t\t\tsemconv.ServiceName(c.ServiceName),\n\t\t\tsemconv.DeploymentEnvironmentName(c.DeploymentEnvironment),\n\t\t)),\n\t\tsdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(\n\t\t\tc.Providers.Zipkin.Sampling.SamplingRatio,\n\t\t))),\n\t}\n\n\ttp := sdktrace.NewTracerProvider(tpOpts...)\n\totel.SetTracerProvider(tp)\n\n\treturn tp.Tracer(tracerName), nil\n}\n"
  },
  {
    "path": "oryx/package.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n"
  },
  {
    "path": "oryx/package.json",
    "content": "{\n  \"private\": true,\n  \"prettier\": \"ory-prettier-styles\",\n  \"devDependencies\": {\n    \"license-checker\": \"^25.0.1\",\n    \"ory-prettier-styles\": \"1.3.0\",\n    \"prettier\": \"2.8.8\"\n  }\n}\n"
  },
  {
    "path": "oryx/pagination/README.md",
    "content": "# pagination\n\nA simple helper for dealing with pagination.\n\n```\ngo get github.com/ory/pagination\n```\n\n## Example\n\n```go\npackage main\n\nimport (\n\t\"github.com/ory/pagination\"\n    \"net/http\"\n    \"net/url\"\n    \"fmt\"\n)\n\nfunc main() {\n\tu, _ := url.Parse(\"http://localhost/foo?offset=0&limit=10\")\n    limit, offset := pagination.Parse(&http.Request{URL: u}, 5, 5, 10)\n\n    items := []string{\"a\", \"b\", \"c\", \"d\"}\n    start, end := pagination.Index(limit, offset, len(items))\n    fmt.Printf(\"Got items: %v\", items[start:end])\n}\n```\n"
  },
  {
    "path": "oryx/pagination/header.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage pagination\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc header(u *url.URL, rel string, limit, offset int64) string {\n\tq := u.Query()\n\tq.Set(\"limit\", fmt.Sprintf(\"%d\", limit))\n\tq.Set(\"offset\", fmt.Sprintf(\"%d\", offset))\n\tu.RawQuery = q.Encode()\n\treturn fmt.Sprintf(\"<%s>; rel=\\\"%s\\\"\", u.String(), rel)\n}\n\ntype formatter func(location *url.URL, rel string, itemsPerPage int64, offset int64) string\n\n// HeaderWithFormatter adds an HTTP header for pagination which uses a custom formatter for generating the URL links.\nfunc HeaderWithFormatter(w http.ResponseWriter, u *url.URL, total int64, page, itemsPerPage int, f formatter) {\n\tif itemsPerPage <= 0 {\n\t\titemsPerPage = 1\n\t}\n\n\titemsPerPage64 := int64(itemsPerPage)\n\toffset := int64(page) * itemsPerPage64\n\n\t// lastOffset will either equal the offset required to contain the remainder,\n\t// or the limit.\n\tvar lastOffset int64\n\tif total%itemsPerPage64 == 0 {\n\t\tlastOffset = total - itemsPerPage64\n\t} else {\n\t\tlastOffset = (total / itemsPerPage64) * itemsPerPage64\n\t}\n\n\tw.Header().Set(\"X-Total-Count\", strconv.FormatInt(total, 10))\n\n\t// Check for last page\n\tif offset >= lastOffset {\n\t\tif total == 0 {\n\t\t\tw.Header().Set(\"Link\", strings.Join([]string{\n\t\t\t\tf(u, \"first\", itemsPerPage64, 0),\n\t\t\t\tf(u, \"next\", itemsPerPage64, ((offset/itemsPerPage64)+1)*itemsPerPage64),\n\t\t\t\tf(u, \"prev\", itemsPerPage64, ((offset/itemsPerPage64)-1)*itemsPerPage64),\n\t\t\t}, \",\"))\n\t\t\treturn\n\t\t}\n\n\t\tif total <= itemsPerPage64 {\n\t\t\tw.Header().Set(\"link\", f(u, \"first\", total, 0))\n\t\t\treturn\n\t\t}\n\n\t\tw.Header().Set(\"Link\", strings.Join([]string{\n\t\t\tf(u, \"first\", itemsPerPage64, 0),\n\t\t\tf(u, \"prev\", itemsPerPage64, lastOffset-itemsPerPage64),\n\t\t}, \",\"))\n\t\treturn\n\t}\n\n\tif offset < itemsPerPage64 {\n\t\tw.Header().Set(\"Link\", strings.Join([]string{\n\t\t\tf(u, \"next\", itemsPerPage64, itemsPerPage64),\n\t\t\tf(u, \"last\", itemsPerPage64, lastOffset),\n\t\t}, \",\"))\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Link\", strings.Join([]string{\n\t\tf(u, \"first\", itemsPerPage64, 0),\n\t\tf(u, \"next\", itemsPerPage64, ((offset/itemsPerPage64)+1)*itemsPerPage64),\n\t\tf(u, \"prev\", itemsPerPage64, ((offset/itemsPerPage64)-1)*itemsPerPage64),\n\t\tf(u, \"last\", itemsPerPage64, lastOffset),\n\t}, \",\"))\n}\n\n// Header adds an http header for pagination using a responsewriter where backwards compatibility is required.\n// The header will contain links any combination of the first, last, next, or previous (prev) pages in a paginated list (given a limit and an offset, and optionally a total).\n// If total is not set, then no \"last\" page will be calculated.\n// If no limit is provided, then it will default to 1.\nfunc Header(w http.ResponseWriter, u *url.URL, total int, limit, offset int) {\n\tvar page int\n\tif limit == 0 {\n\t\tlimit = 1\n\t}\n\n\tpage = int(math.Floor(float64(offset) / float64(limit)))\n\tHeaderWithFormatter(w, u, int64(total), page, limit, header)\n}\n"
  },
  {
    "path": "oryx/pagination/items.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage pagination\n\n// MaxItemsPerPage is used to prevent DoS attacks against large lists by limiting the items per page to 500.\nfunc MaxItemsPerPage(max, is int) int {\n\tif is > max {\n\t\treturn max\n\t}\n\treturn is\n}\n"
  },
  {
    "path": "oryx/pagination/keysetpagination/header.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage keysetpagination\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Pagination Request Parameters\n//\n// The `Link` HTTP header contains multiple links (`first`, `next`) formatted as:\n// `<https://{project-slug}.projects.oryapis.com/admin/sessions?page_size=250&page_token=>; rel=\"first\"`\n//\n// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n//\n// swagger:model keysetPaginationRequestParameters\ntype RequestParameters struct {\n\t// Items per Page\n\t//\n\t// This is the number of items per page to return.\n\t// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\t// default: 250\n\t// min: 1\n\t// max: 1000\n\tPageSize int `json:\"page_size\"`\n\n\t// Next Page Token\n\t//\n\t// The next page token.\n\t// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\tPageToken string `json:\"page_token\"`\n}\n\n// Pagination Response Header\n//\n// The `Link` HTTP header contains multiple links (`first`, `next`) formatted as:\n// `<https://{project-slug}.projects.oryapis.com/admin/sessions?page_size=250&page_token=>; rel=\"first\"`\n//\n// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n//\n// swagger:model keysetPaginationResponseHeaders\ntype ResponseHeaders struct {\n\t// The Link HTTP Header\n\t//\n\t// The `Link` header contains a comma-delimited list of links to the following pages:\n\t//\n\t// - first: The first page of results.\n\t// - next: The next page of results.\n\t//\n\t// Pages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted. Examples:\n\t//\n\t//\t</admin/sessions?page_size=250&page_token={last_item_uuid}; rel=\"first\",/admin/sessions?page_size=250&page_token=>; rel=\"next\"\n\t//\n\tLink string `json:\"link\"`\n}\n\nfunc header(u *url.URL, rel, token string, size int) string {\n\tq := u.Query()\n\tq.Set(\"page_token\", token)\n\tq.Set(\"page_size\", strconv.Itoa(size))\n\tu.RawQuery = q.Encode()\n\treturn fmt.Sprintf(\"<%s>; rel=\\\"%s\\\"\", u.String(), rel)\n}\n\n// Header adds the Link header for the page encoded by the paginator.\n// It contains links to the first and next page, if one exists.\nfunc Header(w http.ResponseWriter, u *url.URL, p *Paginator) {\n\tsize := p.Size()\n\tlink := []string{header(u, \"first\", p.defaultToken.Encode(), size)}\n\tif !p.isLast {\n\t\tlink = append(link, header(u, \"next\", p.Token().Encode(), size))\n\t}\n\tw.Header().Set(\"Link\", strings.Join(link, \",\"))\n}\n\n// Parse returns the pagination options from the URL query.\nfunc Parse(q url.Values, p PageTokenConstructor) ([]Option, error) {\n\tvar opts []Option\n\tif pt := cmp.Or(q[\"page_token\"]...); pt != \"\" {\n\t\tpageToken, err := url.QueryUnescape(pt)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\tparsed, err := p(pageToken)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\topts = append(opts, WithToken(parsed))\n\t}\n\tif ps := cmp.Or(q[\"page_size\"]...); ps != \"\" {\n\t\tsize, err := strconv.Atoi(ps)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\topts = append(opts, WithSize(size))\n\t}\n\treturn opts, nil\n}\n"
  },
  {
    "path": "oryx/pagination/keysetpagination/page_token.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage keysetpagination\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype PageToken = interface {\n\tParse(string) map[string]string\n\tEncode() string\n}\n\nvar _ PageToken = new(StringPageToken)\nvar _ PageToken = new(MapPageToken)\n\ntype StringPageToken string\n\nfunc (s StringPageToken) Parse(idField string) map[string]string {\n\treturn map[string]string{idField: string(s)}\n}\n\nfunc (s StringPageToken) Encode() string {\n\treturn string(s)\n}\n\nfunc NewStringPageToken(s string) (PageToken, error) {\n\treturn StringPageToken(s), nil\n}\n\ntype MapPageToken map[string]string\n\nfunc (m MapPageToken) Parse(_ string) map[string]string {\n\treturn map[string]string(m)\n}\n\nconst pageTokenColumnDelim = \"/\"\n\nfunc (m MapPageToken) Encode() string {\n\telems := make([]string, 0, len(m))\n\tfor k, v := range m {\n\t\telems = append(elems, fmt.Sprintf(\"%s=%s\", k, v))\n\t}\n\n\t// For now: use Base64 instead of URL escaping, as the Timestamp format we need to use can contain a `+` sign,\n\t// which represents a space in URLs, so it's not properly encoded by the Go library.\n\treturn base64.RawStdEncoding.EncodeToString([]byte(strings.Join(elems, pageTokenColumnDelim)))\n}\n\nfunc NewMapPageToken(s string) (PageToken, error) {\n\tb, err := base64.RawStdEncoding.DecodeString(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttokens := strings.Split(string(b), pageTokenColumnDelim)\n\n\tr := map[string]string{}\n\n\tfor _, p := range tokens {\n\t\tif columnName, value, found := strings.Cut(p, \"=\"); found {\n\t\t\tr[columnName] = value\n\t\t}\n\t}\n\n\treturn MapPageToken(r), nil\n}\n\nvar _ PageTokenConstructor = NewMapPageToken\nvar _ PageTokenConstructor = NewStringPageToken\n\ntype PageTokenConstructor = func(string) (PageToken, error)\n"
  },
  {
    "path": "oryx/pagination/keysetpagination/paginator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage keysetpagination\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/pop/v6/columns\"\n)\n\ntype (\n\tItem = interface{ PageToken() PageToken }\n\n\tOrder string\n\n\tcolumnOrdering struct {\n\t\tname  string\n\t\torder Order\n\t}\n\tPaginator struct {\n\t\ttoken, defaultToken        PageToken\n\t\tsize, defaultSize, maxSize int\n\t\tisLast                     bool\n\t\tadditionalColumn           columnOrdering\n\t}\n\tOption func(*Paginator) *Paginator\n)\n\nvar ErrUnknownOrder = errors.New(\"unknown order\")\n\nconst (\n\tOrderDescending Order = \"DESC\"\n\tOrderAscending  Order = \"ASC\"\n\n\tDefaultSize    = 100\n\tDefaultMaxSize = 500\n)\n\nfunc (o Order) extract() (string, string, error) {\n\tswitch o {\n\tcase OrderAscending:\n\t\treturn \">\", string(o), nil\n\tcase OrderDescending:\n\t\treturn \"<\", string(o), nil\n\tdefault:\n\t\treturn \"\", \"\", ErrUnknownOrder\n\t}\n}\n\nfunc (p *Paginator) Token() PageToken {\n\tif p.token == nil {\n\t\treturn p.defaultToken\n\t}\n\treturn p.token\n}\n\nfunc (p *Paginator) Size() int {\n\tsize := p.size\n\tif size <= 0 {\n\t\tsize = p.defaultSize\n\t\tif size == 0 {\n\t\t\tsize = 100\n\t\t}\n\t}\n\tif size > p.maxSize {\n\t\tsize = p.maxSize\n\t}\n\treturn size\n}\n\nfunc (p *Paginator) IsLast() bool {\n\treturn p.isLast\n}\n\nfunc (p *Paginator) ToOptions() []Option {\n\topts := make([]Option, 0, 7)\n\tif p.token != nil {\n\t\topts = append(opts, WithToken(p.token))\n\t}\n\tif p.defaultToken != nil {\n\t\topts = append(opts, WithDefaultToken(p.defaultToken))\n\t}\n\tif p.size > 0 {\n\t\topts = append(opts, WithSize(p.size))\n\t}\n\tif p.defaultSize != DefaultSize {\n\t\topts = append(opts, WithDefaultSize(p.defaultSize))\n\t}\n\tif p.maxSize != DefaultMaxSize {\n\t\topts = append(opts, WithMaxSize(p.maxSize))\n\t}\n\tif p.additionalColumn.name != \"\" {\n\t\topts = append(opts, WithColumn(p.additionalColumn.name, p.additionalColumn.order))\n\t}\n\tif p.isLast {\n\t\topts = append(opts, withIsLast(p.isLast))\n\t}\n\treturn opts\n}\n\nfunc (p *Paginator) multipleOrderFieldsQuery(q *pop.Query, idField string, cols map[string]*columns.Column, quoteAndContextualize func(string) string) {\n\ttokenParts := p.Token().Parse(idField)\n\tidValue := tokenParts[idField]\n\n\tcolumn, ok := cols[p.additionalColumn.name]\n\tif !ok {\n\t\tq.Where(fmt.Sprintf(`%s > ?`, quoteAndContextualize(idField)), idValue)\n\t\treturn\n\t}\n\n\tquoteName := quoteAndContextualize(column.Name)\n\n\tvalue, ok := tokenParts[column.Name]\n\n\tif !ok {\n\t\tq.Where(fmt.Sprintf(`%s > ?`, quoteAndContextualize(idField)), idValue)\n\t\treturn\n\t}\n\n\tsign, keyword, err := p.additionalColumn.order.extract()\n\tif err != nil {\n\t\tq.Where(fmt.Sprintf(`%s > ?`, quoteAndContextualize(idField)), idValue)\n\t\treturn\n\t}\n\n\tq.\n\t\tWhere(fmt.Sprintf(\"(%s %s ? OR (%s = ? AND %s > ?))\", quoteName, sign, quoteName, quoteAndContextualize(idField)), value, value, idValue).\n\t\tOrder(fmt.Sprintf(\"%s %s\", quoteName, keyword))\n\n}\n\n// Paginate returns a function that paginates a pop.Query.\n// Usage:\n//\n//\tq := c.Where(\"foo = ?\", foo).Scope(keysetpagination.Paginate[MyItemType](paginator))\n//\n// This function works regardless of whether your type implements the Item\n// interface with pointer or value receivers. To understand the type parameters,\n// see this document:\n// https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#pointer-method-example\nfunc Paginate[I any, PI interface {\n\tItem\n\t*I\n}](p *Paginator) pop.ScopeFunc {\n\tmodel := pop.Model{Value: new(I)}\n\tid := model.IDField()\n\ttableName := model.Alias()\n\treturn func(q *pop.Query) *pop.Query {\n\t\tquote := q.Connection.Dialect.Quote\n\t\teid := quote(tableName) + \".\" + quote(id)\n\n\t\tquoteAndContextualize := func(name string) string {\n\t\t\treturn quote(tableName) + \".\" + quote(name)\n\t\t}\n\t\tp.multipleOrderFieldsQuery(q, id, model.Columns().Cols, quoteAndContextualize)\n\n\t\treturn q.\n\t\t\tLimit(p.Size() + 1).\n\t\t\t// we always need to order by the id field last\n\t\t\tOrder(fmt.Sprintf(`%s ASC`, eid))\n\t}\n}\n\n// Result removes the last item (if applicable) and returns the paginator for the next page.\n//\n// This function works regardless of whether your type implements the Item\n// interface with pointer or value receivers. To understand the type parameters,\n// see this document:\n// https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#pointer-method-example\nfunc Result[I any, PI interface {\n\tItem\n\t*I\n}](items []I, p *Paginator) ([]I, *Paginator) {\n\tif len(items) > p.Size() {\n\t\titems = items[:p.Size()]\n\t\treturn items, &Paginator{\n\t\t\ttoken:        PI(&items[len(items)-1]).PageToken(),\n\t\t\tdefaultToken: p.defaultToken,\n\t\t\tsize:         p.size,\n\t\t\tdefaultSize:  p.defaultSize,\n\t\t\tmaxSize:      p.maxSize,\n\t\t}\n\t}\n\treturn items, &Paginator{\n\t\tdefaultToken: p.defaultToken,\n\t\tsize:         p.size,\n\t\tdefaultSize:  p.defaultSize,\n\t\tmaxSize:      p.maxSize,\n\t\tisLast:       true,\n\t}\n}\n\nfunc WithDefaultToken(t PageToken) Option {\n\treturn func(opts *Paginator) *Paginator {\n\t\topts.defaultToken = t\n\t\treturn opts\n\t}\n}\n\nfunc WithDefaultSize(size int) Option {\n\treturn func(opts *Paginator) *Paginator {\n\t\topts.defaultSize = size\n\t\treturn opts\n\t}\n}\n\nfunc WithMaxSize(size int) Option {\n\treturn func(opts *Paginator) *Paginator {\n\t\topts.maxSize = size\n\t\treturn opts\n\t}\n}\n\nfunc WithToken(t PageToken) Option {\n\treturn func(opts *Paginator) *Paginator {\n\t\topts.token = t\n\t\treturn opts\n\t}\n}\n\nfunc WithSize(size int) Option {\n\treturn func(opts *Paginator) *Paginator {\n\t\topts.size = size\n\t\treturn opts\n\t}\n}\n\nfunc WithColumn(name string, order Order) Option {\n\treturn func(opts *Paginator) *Paginator {\n\t\topts.additionalColumn = columnOrdering{\n\t\t\tname:  name,\n\t\t\torder: order,\n\t\t}\n\t\treturn opts\n\t}\n}\n\nfunc withIsLast(isLast bool) Option {\n\treturn func(opts *Paginator) *Paginator {\n\t\topts.isLast = isLast\n\t\treturn opts\n\t}\n}\n\nfunc GetPaginator(modifiers ...Option) *Paginator {\n\topts := &Paginator{\n\t\t// these can still be overridden by the modifiers, but they should never be unset\n\t\tmaxSize:     DefaultMaxSize,\n\t\tdefaultSize: DefaultSize,\n\t}\n\tfor _, f := range modifiers {\n\t\topts = f(opts)\n\t}\n\treturn opts\n}\n"
  },
  {
    "path": "oryx/pagination/keysetpagination/parse_header.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage keysetpagination\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/peterhellberg/link\"\n)\n\n// PaginationResult represents a parsed result of the link HTTP header.\ntype PaginationResult struct {\n\t// NextToken is the next page token. If it's empty, there is no next page.\n\tNextToken string\n\n\t// FirstToken is the first page token.\n\tFirstToken string\n}\n\n// ParseHeader parses the response header's Link.\nfunc ParseHeader(r *http.Response) *PaginationResult {\n\tlinks := link.ParseResponse(r)\n\treturn &PaginationResult{\n\t\tNextToken:  findRel(links, \"next\"),\n\t\tFirstToken: findRel(links, \"first\"),\n\t}\n}\n\nfunc findRel(links link.Group, rel string) string {\n\tfor idx, l := range links {\n\t\tif idx == rel {\n\t\t\tparsed, err := url.Parse(l.URI)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\treturn parsed.Query().Get(\"page_token\")\n\t\t}\n\t}\n\n\treturn \"\"\n}\n"
  },
  {
    "path": "oryx/pagination/keysetpagination_v2/.snapshots/TestPageToken-Marshal_snapshot.json",
    "content": "{\n  \"e\": \"2023-01-01T01:00:00Z\",\n  \"c\": [\n    {\n      \"n\": \"id\",\n      \"v\": \"token\"\n    },\n    {\n      \"n\": \"string\",\n      \"o\": 1,\n      \"v\": \"str\"\n    },\n    {\n      \"n\": \"string-zero\",\n      \"o\": 1,\n      \"v\": \"\"\n    },\n    {\n      \"n\": \"time\",\n      \"vt\": \"2023-01-01T00:00:00Z\"\n    },\n    {\n      \"n\": \"uuid\",\n      \"vu\": \"b652bbbd-1a84-4fb7-9437-6b78ba2686db\"\n    },\n    {\n      \"n\": \"nulluuid\",\n      \"vu\": \"b652bbbd-1a84-4fb7-9437-6b78ba2686da\"\n    },\n    {\n      \"n\": \"int64\",\n      \"vi\": 64\n    },\n    {\n      \"n\": \"int64-zero\",\n      \"vi\": 0\n    },\n    {\n      \"n\": \"empty-nullint64\",\n      \"vi\": 0\n    },\n    {\n      \"n\": \"nullint64\",\n      \"vi\": 64\n    },\n    {\n      \"n\": \"nullstring\",\n      \"v\": \"str\"\n    },\n    {\n      \"n\": \"empty-nullstring\",\n      \"v\": \"\"\n    },\n    {\n      \"n\": \"null-nulluuid\",\n      \"vn\": true\n    },\n    {\n      \"n\": \"null-nullstring\",\n      \"vn\": true\n    },\n    {\n      \"n\": \"null-nullint64\",\n      \"vn\": true\n    }\n  ]\n}\n"
  },
  {
    "path": "oryx/pagination/keysetpagination_v2/page_token.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage keysetpagination\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/ssoready/hyrumtoken\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar fallbackEncryptionKey = &[32]byte{}\n\ntype (\n\tPageToken struct {\n\t\ttestNow func() time.Time\n\t\tcols    []Column\n\t}\n\tjsonPageToken = struct {\n\t\tExpiresAt time.Time    `json:\"e\"`\n\t\tCols      []jsonColumn `json:\"c\"`\n\t}\n\n\tjsonColumn = struct {\n\t\tName          string `json:\"n\"`\n\t\tOrder         Order  `json:\"o,omitempty\"`\n\t\tNullable      bool   `json:\"nl,omitempty\"`\n\t\tHasConstraint bool   `json:\"hc,omitempty\"`\n\n\t\tValueAny   any        `json:\"v,omitempty\"`\n\t\tValueTime  *time.Time `json:\"vt,omitempty\"`\n\t\tValueUUID  *uuid.UUID `json:\"vu,omitempty\"`\n\t\tValueInt64 *int64     `json:\"vi,omitempty\"`\n\t\tValueNull  bool       `json:\"vn,omitempty\"`\n\t}\n\tColumn struct {\n\t\tName     string\n\t\tOrder    Order\n\t\tValue    any\n\t\tNullable bool\n\t\t// HasConstraint marks if the column is already constrained by WHERE condition.\n\t\tHasConstraint bool\n\t}\n)\n\nfunc (t PageToken) Columns() []Column { return t.cols }\n\n// Encrypt encrypts the page token using the first key in the provided keyset.\n// It uses a fallback key if no keys are provided.\nfunc (t PageToken) Encrypt(keys [][32]byte) string {\n\tkey := fallbackEncryptionKey\n\tif len(keys) > 0 {\n\t\tkey = &keys[0]\n\t}\n\treturn hyrumtoken.Marshal(key, t)\n}\n\nfunc (t PageToken) MarshalJSON() ([]byte, error) {\n\tnow := time.Now\n\tif t.testNow != nil {\n\t\tnow = t.testNow\n\t}\n\ttoEncode := jsonPageToken{\n\t\tExpiresAt: now().Add(time.Hour).UTC(),\n\t\tCols:      make([]jsonColumn, len(t.cols)),\n\t}\n\tfor i, col := range t.cols {\n\t\ttoEncode.Cols[i] = jsonColumn{\n\t\t\tName:          col.Name,\n\t\t\tOrder:         col.Order,\n\t\t\tNullable:      col.Nullable,\n\t\t\tHasConstraint: col.HasConstraint,\n\t\t}\n\t\tswitch v := col.Value.(type) {\n\t\tcase time.Time:\n\t\t\ttoEncode.Cols[i].ValueTime = new(v)\n\t\tcase uuid.UUID:\n\t\t\ttoEncode.Cols[i].ValueUUID = new(v)\n\t\tcase uuid.NullUUID:\n\t\t\tif v.Valid {\n\t\t\t\ttoEncode.Cols[i].ValueUUID = new(v.UUID)\n\t\t\t} else {\n\t\t\t\ttoEncode.Cols[i].ValueNull = true\n\t\t\t}\n\t\tcase sql.NullString:\n\t\t\tif v.Valid {\n\t\t\t\ttoEncode.Cols[i].ValueAny = new(v.String)\n\t\t\t} else {\n\t\t\t\ttoEncode.Cols[i].ValueNull = true\n\t\t\t}\n\t\tcase int64:\n\t\t\ttoEncode.Cols[i].ValueInt64 = new(v)\n\t\tcase sql.NullInt64:\n\t\t\tif v.Valid {\n\t\t\t\ttoEncode.Cols[i].ValueInt64 = new(v.Int64)\n\t\t\t} else {\n\t\t\t\ttoEncode.Cols[i].ValueNull = true\n\t\t\t}\n\t\tdefault:\n\t\t\ttoEncode.Cols[i].ValueAny = v\n\t\t}\n\t}\n\treturn json.Marshal(toEncode)\n}\n\nvar ErrPageTokenExpired = herodot.ErrBadRequest.WithReason(\"page token expired, do not persist page tokens\")\n\nfunc (t *PageToken) UnmarshalJSON(data []byte) error {\n\trawToken := jsonPageToken{}\n\tif err := json.Unmarshal(data, &rawToken); err != nil {\n\t\treturn err\n\t}\n\tt.cols = make([]Column, len(rawToken.Cols))\n\tfor i, col := range rawToken.Cols {\n\t\tt.cols[i] = Column{\n\t\t\tName:          col.Name,\n\t\t\tOrder:         col.Order,\n\t\t\tNullable:      col.Nullable,\n\t\t\tHasConstraint: col.HasConstraint,\n\t\t}\n\t\tswitch {\n\t\tcase col.ValueNull:\n\t\t\tt.cols[i].Value = nil\n\t\tcase col.ValueAny != nil:\n\t\t\tt.cols[i].Value = col.ValueAny\n\t\tcase col.ValueUUID != nil:\n\t\t\tt.cols[i].Value = *col.ValueUUID\n\t\tcase col.ValueTime != nil:\n\t\t\tt.cols[i].Value = *col.ValueTime\n\t\tcase col.ValueInt64 != nil:\n\t\t\tt.cols[i].Value = *col.ValueInt64\n\t\t}\n\t}\n\n\tnow := time.Now\n\tif t.testNow != nil {\n\t\tnow = t.testNow\n\t}\n\tif rawToken.ExpiresAt.Before(now().UTC()) {\n\t\treturn errors.WithStack(ErrPageTokenExpired)\n\t}\n\treturn nil\n}\n\nfunc NewPageToken(cols ...Column) PageToken { return PageToken{cols: cols} }\n"
  },
  {
    "path": "oryx/pagination/keysetpagination_v2/paginator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage keysetpagination\n\nimport (\n\t\"cmp\"\n\t\"reflect\"\n\n\t\"github.com/jmoiron/sqlx/reflectx\"\n\t\"github.com/ory/herodot\"\n)\n\nvar ErrInvalidPaginationToken = herodot.ErrBadRequest.WithReason(\"invalid pagination token\")\n\ntype (\n\tPaginator struct {\n\t\ttoken, defaultToken        PageToken\n\t\tsize, defaultSize, maxSize int\n\t\tisLast                     bool\n\t}\n\tOption func(*Paginator)\n)\n\nconst (\n\tDefaultSize    = 100\n\tDefaultMaxSize = 500\n)\n\nvar dbStructTagMapper = reflectx.NewMapper(\"db\")\n\nfunc (p *Paginator) DefaultToken() PageToken { return p.defaultToken }\nfunc (p *Paginator) IsLast() bool            { return p.isLast }\n\nfunc (p *Paginator) PageToken() PageToken {\n\tif p.token.cols != nil {\n\t\treturn p.token\n\t}\n\treturn p.defaultToken\n}\n\nfunc (p *Paginator) Size() int {\n\tdefaultSize := cmp.Or(p.defaultSize, DefaultSize)\n\tmaxSize := cmp.Or(p.maxSize, DefaultMaxSize)\n\n\tsize := p.size\n\tif size <= 0 {\n\t\tsize = defaultSize\n\t}\n\tif size > maxSize {\n\t\tsize = maxSize\n\t}\n\n\treturn size\n}\n\nfunc (p *Paginator) ToOptions() []Option {\n\topts := make([]Option, 0, 6)\n\tif p.token.cols != nil {\n\t\topts = append(opts, WithToken(p.token))\n\t}\n\tif p.defaultToken.cols != nil {\n\t\topts = append(opts, WithDefaultToken(p.defaultToken))\n\t}\n\tif p.size > 0 {\n\t\topts = append(opts, WithSize(p.size))\n\t}\n\tif p.defaultSize != DefaultSize {\n\t\topts = append(opts, WithDefaultSize(p.defaultSize))\n\t}\n\tif p.maxSize != DefaultMaxSize {\n\t\topts = append(opts, WithMaxSize(p.maxSize))\n\t}\n\tif p.isLast {\n\t\topts = append(opts, withIsLast(p.isLast))\n\t}\n\treturn opts\n}\n\n// Result removes the last item (if applicable) and returns the paginator for the next page.\nfunc Result[I any](items []I, p *Paginator) ([]I, *Paginator) {\n\treturn ResultFunc(items, p, func(last I, colName string) any {\n\t\tlastItemVal := reflect.ValueOf(last)\n\t\treturn dbStructTagMapper.FieldByName(lastItemVal, colName).Interface()\n\t})\n}\n\n// ResultFunc removes the last item (if applicable) and returns the paginator for the next page.\n// The extractor function is used to extract the column values from the last item.\nfunc ResultFunc[I any](items []I, p *Paginator, extractor func(last I, colName string) any) ([]I, *Paginator) {\n\tif len(items) <= p.Size() {\n\t\treturn items, &Paginator{\n\t\t\tisLast: true,\n\n\t\t\tdefaultToken: p.defaultToken,\n\t\t\tsize:         p.size,\n\t\t\tdefaultSize:  p.defaultSize,\n\t\t\tmaxSize:      p.maxSize,\n\t\t}\n\t}\n\n\titems = items[:p.Size()]\n\tlastItem := items[len(items)-1]\n\n\tcurrentCols := p.PageToken().Columns()\n\tnewCols := make([]Column, len(currentCols))\n\tfor i, col := range currentCols {\n\t\tnewCols[i] = Column{\n\t\t\tName:          col.Name,\n\t\t\tOrder:         col.Order,\n\t\t\tNullable:      col.Nullable,\n\t\t\tHasConstraint: col.HasConstraint,\n\t\t\tValue:         extractor(lastItem, col.Name),\n\t\t}\n\t}\n\n\treturn items, &Paginator{\n\t\ttoken:        NewPageToken(newCols...),\n\t\tdefaultToken: p.defaultToken,\n\t\tsize:         p.size,\n\t\tdefaultSize:  p.defaultSize,\n\t\tmaxSize:      p.maxSize,\n\t}\n}\n\nfunc WithSize(size int) Option {\n\treturn func(p *Paginator) { p.size = size }\n}\n\nfunc WithDefaultSize(size int) Option {\n\treturn func(p *Paginator) { p.defaultSize = size }\n}\n\nfunc WithMaxSize(size int) Option {\n\treturn func(p *Paginator) { p.maxSize = size }\n}\n\nfunc WithToken(t PageToken) Option {\n\treturn func(p *Paginator) { p.token = t }\n}\n\nfunc WithDefaultToken(t PageToken) Option {\n\treturn func(p *Paginator) { p.defaultToken = t }\n}\n\nfunc withIsLast(isLast bool) Option {\n\treturn func(p *Paginator) { p.isLast = isLast }\n}\n\nfunc NewPaginator(modifiers ...Option) (*Paginator, error) {\n\tp := &Paginator{\n\t\t// these can still be overridden by the modifiers, but they should never be unset\n\t\tmaxSize:     DefaultMaxSize,\n\t\tdefaultSize: DefaultSize,\n\t}\n\tfor _, f := range modifiers {\n\t\tf(p)\n\t}\n\tif err := p.validatePageToken(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn p, nil\n}\n\n// validatePageToken ensures the page token columns strictly match DefaultToken's schema in order.\nfunc (p *Paginator) validatePageToken() error {\n\ttokenCols := p.PageToken().Columns()\n\tif len(tokenCols) == 0 {\n\t\treturn nil\n\t}\n\n\tif columnsMatch(tokenCols, p.defaultToken.Columns()) {\n\t\treturn nil\n\t}\n\treturn ErrInvalidPaginationToken\n}\n\nfunc columnsMatch(tokenCols, templateCols []Column) bool {\n\tif len(templateCols) == 0 || len(tokenCols) != len(templateCols) {\n\t\treturn false\n\t}\n\tfor i, col := range tokenCols {\n\t\tdef := templateCols[i]\n\t\tif def.Name != col.Name || def.Order != col.Order || def.Nullable != col.Nullable {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "oryx/pagination/keysetpagination_v2/parse_header.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage keysetpagination\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/peterhellberg/link\"\n)\n\n// ParseHeader parses the response header's Link and returns the first and next page tokens.\nfunc ParseHeader(r *http.Response) (first, next string, isLast bool) {\n\tlinks := link.ParseResponse(r)\n\tfirst, _ = findRel(links, \"first\")\n\tnext, hasNext := findRel(links, \"next\")\n\treturn first, next, !hasNext\n}\n\nfunc findRel(links link.Group, rel string) (string, bool) {\n\tfor idx, l := range links {\n\t\tif idx == rel {\n\t\t\tparsed, err := url.Parse(l.URI)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tq := parsed.Query()\n\n\t\t\treturn q.Get(\"page_token\"), q.Has(\"page_token\")\n\t\t}\n\t}\n\n\treturn \"\", false\n}\n"
  },
  {
    "path": "oryx/pagination/keysetpagination_v2/query_builder.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage keysetpagination\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/ory/pop/v6\"\n)\n\ntype Order int\n\nconst (\n\tOrderAscending Order = iota\n\tOrderDescending\n)\n\nfunc (o Order) extract() (string, string) {\n\tswitch o {\n\tcase OrderAscending:\n\t\treturn \">\", \"ASC\"\n\tcase OrderDescending:\n\t\treturn \"<\", \"DESC\"\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"keyset pagination: unknown order %d\", o))\n\t}\n}\n\n// Paginate returns a function that paginates a pop.Query.\n// Usage:\n//\n//\tq := c.Where(\"foo = ?\", foo).Scope(keysetpagination.Paginate[MyItemType](paginator))\nfunc Paginate[I any](p *Paginator) pop.ScopeFunc {\n\tmodel := pop.Model{Value: *new(I)}\n\ttableName := model.Alias()\n\treturn func(q *pop.Query) *pop.Query {\n\t\tquoteAndContextualize := func(name string) string {\n\t\t\tquote := q.Connection.Dialect.Quote\n\t\t\treturn quote(tableName) + \".\" + quote(name)\n\t\t}\n\t\tdialect := q.Connection.Dialect.Name()\n\t\twhere, args, order := BuildWhereAndOrder(p.PageToken().Columns(), quoteAndContextualize, dialect)\n\t\t// IMPORTANT: Ensures correct query logic by grouping conditions.\n\t\t// Without parentheses, `WHERE otherCond AND pageCond1 OR pageCond2` would be\n\t\t// evaluated as `(otherCond = ? AND pageCond1) OR pageCond2`, potentially returning\n\t\t// rows that do not match `otherCond`.\n\t\t// We fix it by forcing the query to be: `WHERE otherCond AND (paginationCond1 OR paginationCond2)`.\n\t\twhere = \"(\" + where + \")\"\n\n\t\treturn q.\n\t\t\tWhere(where, args...).\n\t\t\tOrder(order).\n\t\t\tLimit(p.Size() + 1)\n\t}\n}\n\nfunc BuildWhereAndOrder(columns []Column, quote func(string) string, dialect string) (string, []any, string) {\n\tvar whereBuilder, orderByBuilder, prevEqual strings.Builder\n\n\tkeysetCols := make([]Column, 0, len(columns))\n\n\t// ORDER BY includes all columns (even constrained ones)\n\tfor i, part := range columns {\n\t\tcolumn := quote(part.Name)\n\t\t_, keyword := part.Order.extract()\n\n\t\tif i > 0 {\n\t\t\torderByBuilder.WriteString(\", \")\n\t\t}\n\n\t\torderByBuilder.WriteString(column + \" \" + keyword)\n\n\t\t// Postgres orders NULLs differently depending on sort direction;\n\t\t// (ASC → NULLS LAST, DESC → NULLS FIRST), which does not match the\n\t\t// assumptions of our keyset pagination logic and other supported DBs.\n\t\t// We therefore make NULL ordering explicit on Postgres to keep pagination\n\t\t// stable and consistent with sqlite/mysql/cockroachdb.\n\t\tif dialect == \"postgres\" && part.Nullable {\n\t\t\tif part.Order == OrderAscending {\n\t\t\t\torderByBuilder.WriteString(\" NULLS FIRST\")\n\t\t\t} else {\n\t\t\t\torderByBuilder.WriteString(\" NULLS LAST\")\n\t\t\t}\n\t\t}\n\n\t\t// Build keyset WHERE only from unconstrained columns\n\t\tif !part.HasConstraint {\n\t\t\tkeysetCols = append(keysetCols, part)\n\t\t}\n\t}\n\n\t// If everything is constrained, no keyset predicate is needed.\n\tif len(keysetCols) == 0 {\n\t\treturn \"\", nil, orderByBuilder.String()\n\t}\n\n\targs := make([]any, 0, len(keysetCols)*(len(keysetCols)+1)/2)\n\tprevEqualArgs := make([]any, 0, len(keysetCols))\n\n\twhereBuilder.WriteRune('(')\n\n\tfor i, part := range keysetCols {\n\t\tcolumn := quote(part.Name)\n\t\tsign, _ := part.Order.extract()\n\n\t\tif i > 0 {\n\t\t\twhereBuilder.WriteString(\") OR (\")\n\t\t}\n\n\t\twhereBuilder.WriteString(prevEqual.String())\n\t\tif prevEqual.Len() > 0 {\n\t\t\twhereBuilder.WriteString(\" AND \")\n\t\t}\n\n\t\tisNull := part.Nullable && isSQLNull(part.Value)\n\n\t\tif !part.Nullable {\n\t\t\twhereBuilder.WriteString(column + \" \" + sign + \" ?\")\n\t\t} else if !isNull {\n\t\t\twhereBuilder.WriteString(column + \" IS NOT NULL AND \" + column + \" \" + sign + \" ?\")\n\t\t} else {\n\t\t\twhereBuilder.WriteString(column + \" IS NOT NULL\")\n\t\t}\n\n\t\tif i > 0 {\n\t\t\tprevEqual.WriteString(\" AND \")\n\t\t}\n\n\t\tif !part.Nullable {\n\t\t\tprevEqual.WriteString(column + \" = ?\")\n\t\t\tprevEqualArgs = append(prevEqualArgs, part.Value)\n\t\t} else if !isNull {\n\t\t\tprevEqual.WriteString(column + \" = ?\")\n\t\t\tprevEqualArgs = append(prevEqualArgs, part.Value)\n\t\t} else {\n\t\t\tprevEqual.WriteString(column + \" IS NULL\")\n\t\t}\n\n\t\targs = append(args, prevEqualArgs...)\n\t}\n\n\twhereBuilder.WriteRune(')')\n\n\treturn whereBuilder.String(), args, orderByBuilder.String()\n}\n\n// isSQLNull reports whether v represents a SQL NULL value.\nfunc isSQLNull(v any) bool {\n\tif v == nil {\n\t\treturn true\n\t}\n\n\tif valuer, ok := v.(driver.Valuer); ok {\n\t\tval, err := valuer.Value()\n\t\treturn err == nil && val == nil\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "oryx/pagination/keysetpagination_v2/request_params.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage keysetpagination\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/ssoready/hyrumtoken\"\n)\n\n// Pagination Request Parameters\n//\n// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n//\n// swagger:model keysetPaginationRequestParameters\ntype RequestParameters struct {\n\t// Items per Page\n\t//\n\t// This is the number of items per page to return.\n\t// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\t// default: 250\n\t// min: 1\n\t// max: 1000\n\tPageSize int `json:\"page_size\"`\n\n\t// Next Page Token\n\t//\n\t// The next page token.\n\t// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\tPageToken string `json:\"page_token\"`\n}\n\n// Pagination Response Header\n//\n// The `Link` HTTP header contains multiple links (`first`, `next`) formatted as:\n// `<https://{project-slug}.projects.oryapis.com/admin/sessions?page_size=250&page_token=>; rel=\"first\"`\n//\n// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n//\n// swagger:model keysetPaginationResponseHeaders\ntype ResponseHeaders struct {\n\t// The Link HTTP Header\n\t//\n\t// The `Link` header contains a comma-delimited list of links to the following pages:\n\t//\n\t// - first: The first page of results.\n\t// - next: The next page of results.\n\t//\n\t// Pages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted. Examples:\n\t//\n\t//\t</admin/sessions?page_size=250&page_token={last_item_uuid}; rel=\"first\",/admin/sessions?page_size=250&page_token=>; rel=\"next\"\n\t//\n\tLink string `json:\"link\"`\n}\n\n// SetLinkHeader adds the Link header for the page encoded by the paginator.\n// It contains links to the first and next page, if one exists.\nfunc SetLinkHeader(w http.ResponseWriter, keys [][32]byte, u *url.URL, p *Paginator) {\n\tsize := p.Size()\n\tlink := []string{linkPart(u, \"first\", p.DefaultToken().Encrypt(keys), size)}\n\tif !p.isLast {\n\t\tlink = append(link, linkPart(u, \"next\", p.PageToken().Encrypt(keys), size))\n\t}\n\tw.Header().Set(\"Link\", strings.Join(link, \",\"))\n}\n\nfunc linkPart(u *url.URL, rel, token string, size int) string {\n\tq := u.Query()\n\tq.Set(\"page_token\", token)\n\tq.Set(\"page_size\", strconv.Itoa(size))\n\tu.RawQuery = q.Encode()\n\treturn fmt.Sprintf(\"<%s>; rel=%q\", u.String(), rel)\n}\n\n// ParseQueryParams extracts the pagination options from the URL query.\nfunc ParseQueryParams(keys [][32]byte, q url.Values) ([]Option, error) {\n\tvar opts []Option\n\tif t := cmp.Or(q[\"page_token\"]...); t != \"\" {\n\t\traw, err := url.QueryUnescape(t)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\ttoken, err := ParsePageToken(keys, raw)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\topts = append(opts, WithToken(token))\n\t}\n\tif s := cmp.Or(q[\"page_size\"]...); s != \"\" {\n\t\tsize, err := strconv.Atoi(s)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\topts = append(opts, WithSize(size))\n\t}\n\treturn opts, nil\n}\n\n// ParsePageToken parses a page token from the given raw string using the provided keys.\n// It panics if no keys are provided.\nfunc ParsePageToken(keys [][32]byte, raw string) (t PageToken, err error) {\n\tfor i := range keys {\n\t\terr = errors.WithStack(hyrumtoken.Unmarshal(&keys[i], raw, &t))\n\t\tif err == nil {\n\t\t\treturn\n\t\t}\n\t}\n\t// as a last resort, try the fallback key\n\terr = hyrumtoken.Unmarshal(fallbackEncryptionKey, raw, &t)\n\treturn t, errors.WithStack(err)\n}\n"
  },
  {
    "path": "oryx/pagination/limit.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// Package pagination provides helpers for dealing with pagination.\npackage pagination\n\n// Index uses limit, offset, and a slice's length to compute start and end indices for said slice.\nfunc Index(limit, offset, length int) (start, end int) {\n\tif offset > length {\n\t\treturn length, length\n\t} else if limit+offset > length {\n\t\treturn offset, length\n\t}\n\n\treturn offset, offset + limit\n}\n"
  },
  {
    "path": "oryx/pagination/migrationpagination/.snapshots/TestPaginationHeader-Create_next_and_last,_but_not_previous_or_first_if_at_the_beginning.json",
    "content": "[\n  \"\\u003chttp://example.com?page=1\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiI1MCIsInYiOjJ9\\u0026per_page=50\\u003e\",\n  \"rel=\\\"next\\\",\\u003chttp://example.com?page=2\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiIxMDAiLCJ2IjoyfQ\\u0026per_page=50\\u003e\",\n  \"rel=\\\"last\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/migrationpagination/.snapshots/TestPaginationHeader-Create_only_first_if_the_limits_provided_exceeds_the_number_of_clients_found.json",
    "content": "[\n  \"\\u003chttp://example.com?page=0\\u0026page_size=5\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u0026per_page=5\\u003e\",\n  \"rel=\\\"first\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/migrationpagination/.snapshots/TestPaginationHeader-Create_previous,_next,_first,_and_last_if_in_the_middle.json",
    "content": "[\n  \"\\u003chttp://example.com?page=0\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u0026per_page=50\\u003e\",\n  \"rel=\\\"first\\\",\\u003chttp://example.com?page=4\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiIyMDAiLCJ2IjoyfQ\\u0026per_page=50\\u003e\",\n  \"rel=\\\"next\\\",\\u003chttp://example.com?page=2\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiIxMDAiLCJ2IjoyfQ\\u0026per_page=50\\u003e\",\n  \"rel=\\\"prev\\\",\\u003chttp://example.com?page=5\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiIyNTAiLCJ2IjoyfQ\\u0026per_page=50\\u003e\",\n  \"rel=\\\"last\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/migrationpagination/.snapshots/TestPaginationHeader-Create_previous,_next,_first,_but_not_last_if_in_the_middle_and_no_total_was_provided.json",
    "content": "[\n  \"\\u003chttp://example.com?page=0\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u0026per_page=50\\u003e\",\n  \"rel=\\\"first\\\",\\u003chttp://example.com?page=4\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiIyMDAiLCJ2IjoyfQ\\u0026per_page=50\\u003e\",\n  \"rel=\\\"next\\\",\\u003chttp://example.com?page=2\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiIxMDAiLCJ2IjoyfQ\\u0026per_page=50\\u003e\",\n  \"rel=\\\"prev\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/migrationpagination/.snapshots/TestPaginationHeader-Create_previous_and_first_but_not_next_or_last_if_at_the_end.json",
    "content": "[\n  \"\\u003chttp://example.com?page=0\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u0026per_page=50\\u003e\",\n  \"rel=\\\"first\\\",\\u003chttp://example.com?page=1\\u0026page_size=50\\u0026page_token=eyJvZmZzZXQiOiI1MCIsInYiOjJ9\\u0026per_page=50\\u003e\",\n  \"rel=\\\"prev\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/migrationpagination/.snapshots/TestPaginationHeader-Header_should_default_limit_to_1_no_limit_was_provided.json",
    "content": "[\n  \"\\u003chttp://example.com?page=0\\u0026page_size=1\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u0026per_page=1\\u003e\",\n  \"rel=\\\"first\\\",\\u003chttp://example.com?page=21\\u0026page_size=1\\u0026page_token=eyJvZmZzZXQiOiIyMSIsInYiOjJ9\\u0026per_page=1\\u003e\",\n  \"rel=\\\"next\\\",\\u003chttp://example.com?page=19\\u0026page_size=1\\u0026page_token=eyJvZmZzZXQiOiIxOSIsInYiOjJ9\\u0026per_page=1\\u003e\",\n  \"rel=\\\"prev\\\",\\u003chttp://example.com?page=99\\u0026page_size=1\\u0026page_token=eyJvZmZzZXQiOiI5OSIsInYiOjJ9\\u0026per_page=1\\u003e\",\n  \"rel=\\\"last\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/migrationpagination/header.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage migrationpagination\n\n// swagger:model mixedPaginationRequestParameters\ntype RequestParameters struct {\n\t// Deprecated Items per Page\n\t//\n\t// DEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\n\t//\n\t// This is the number of items per page.\n\t//\n\t// required: false\n\t// in: query\n\t// default: 250\n\t// min: 1\n\t// max: 1000\n\tPerPage int `json:\"per_page\"`\n\n\t// Deprecated Pagination Page\n\t//\n\t// DEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\n\t//\n\t// This value is currently an integer, but it is not sequential. The value is not the page number, but a\n\t// reference. The next page can be any number and some numbers might return an empty list.\n\t//\n\t// For example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist.\n\t// The first page can be retrieved by omitting this parameter. Following page pointers will be returned in the\n\t// `Link` header.\n\t//\n\t// required: false\n\t// in: query\n\tPage int `json:\"page\"`\n\n\t// Page Size\n\t//\n\t// This is the number of items per page to return. For details on pagination please head over to the\n\t// [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\t// default: 250\n\t// min: 1\n\t// max: 500\n\tPageSize int `json:\"page_size\"`\n\n\t// Next Page Token\n\t//\n\t// The next page token. For details on pagination please head over to the\n\t// [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\tPageToken string `json:\"page_token\"`\n}\n\n// Pagination Response Header\n//\n// The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n// `<https://{project-slug}.projects.oryapis.com/admin/clients?page_size={limit}&page_token={offset}>; rel=\"{page}\"`\n//\n// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n//\n// swagger:model mixedPagePaginationResponseHeaders\ntype ResponseHeaderAnnotation struct {\n\t// The Link HTTP Header\n\t//\n\t// The `Link` header contains a comma-delimited list of links to the following pages:\n\t//\n\t// - first: The first page of results.\n\t// - next: The next page of results.\n\t// - prev: The previous page of results.\n\t// - last: The last page of results.\n\t//\n\t// Pages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted.\n\t//\n\t// The header value may look like follows:\n\t//\n\t//\t</clients?limit=5&offset=0>; rel=\"first\",</clients?limit=5&offset=15>; rel=\"next\",</clients?limit=5&offset=5>; rel=\"prev\",</clients?limit=5&offset=20>; rel=\"last\"\n\tLink string `json:\"link\"`\n\n\t// The X-Total-Count HTTP Header\n\t//\n\t// The `X-Total-Count` header contains the total number of items in the collection.\n\t//\n\t// DEPRECATED: This header will be removed eventually. Please use the `Link` header\n\t// instead to check whether you are on the last page.\n\tTotalCount int `json:\"x-total-count\"`\n}\n"
  },
  {
    "path": "oryx/pagination/migrationpagination/pagination.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage migrationpagination\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/ory/x/pagination\"\n\t\"github.com/ory/x/pagination/pagepagination\"\n\t\"github.com/ory/x/pagination/tokenpagination\"\n)\n\ntype Paginator struct {\n\tp *pagepagination.PagePaginator\n\tt *tokenpagination.TokenPaginator\n}\n\nfunc NewPaginator(p *pagepagination.PagePaginator, t *tokenpagination.TokenPaginator) *Paginator {\n\treturn &Paginator{p: p, t: t}\n}\n\nfunc NewDefaultPaginator() *Paginator {\n\treturn &Paginator{p: new(pagepagination.PagePaginator), t: new(tokenpagination.TokenPaginator)}\n}\n\nfunc (p *Paginator) ParsePagination(r *http.Request) (page, itemsPerPage int) {\n\tif r.URL.Query().Has(\"page_token\") || r.URL.Query().Has(\"page_size\") {\n\t\treturn p.t.ParsePagination(r)\n\t}\n\treturn p.p.ParsePagination(r)\n}\n\nfunc header(u *url.URL, rel string, itemsPerPage, offset int64) string {\n\tq := u.Query()\n\tq.Set(\"page_size\", fmt.Sprintf(\"%d\", itemsPerPage))\n\tq.Set(\"page_token\", tokenpagination.Encode(offset))\n\tq.Set(\"per_page\", fmt.Sprintf(\"%d\", itemsPerPage))\n\tq.Set(\"page\", fmt.Sprintf(\"%d\", offset/itemsPerPage))\n\tu.RawQuery = q.Encode()\n\treturn fmt.Sprintf(\"<%s>; rel=\\\"%s\\\"\", u.String(), rel)\n}\n\nfunc PaginationHeader(w http.ResponseWriter, u *url.URL, total int64, page, itemsPerPage int) {\n\tpagination.HeaderWithFormatter(w, u, total, page, itemsPerPage, header)\n}\n"
  },
  {
    "path": "oryx/pagination/pagepagination/header.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage pagepagination\n\n// Pagination Request Parameters\n//\n// The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n// `<https://{project-slug}.projects.oryapis.com/admin/clients?page_size={limit}&page_token={offset}>; rel=\"{page}\"`\n//\n// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n//\n// swagger:model pagePaginationRequestParameters\ntype RequestParameters struct {\n\t// Legacy Items per Page\n\t//\n\t// A DEPRECATED alias for `page_size`. Please transition to using `page_size` going forward.\n\t//\n\t// required: false\n\t// in: query\n\t// default: 250\n\t// min: 1\n\t// max: 1000\n\tPerPage int `json:\"per_page\"`\n\n\t// Legacy Pagination Page\n\t//\n\t// A DEPRECATED alias for `page_token`. Please transition to using `page_token` going forward.\n\t//\n\t// required: false\n\t// in: query\n\t// default: 1\n\t// min: 1\n\tPage int `json:\"page\"`\n\n\t// Items per Page\n\t//\n\t// This is the number of items per page to return. For details on pagination please head over to the\n\t// [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\t// default: 250\n\t// min: 1\n\t// max: 500\n\tPageSize int `json:\"page_size\"`\n\n\t// Next Page Token\n\t//\n\t// The next page token. For details on pagination please head over to the\n\t// [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\tPageToken string `json:\"page_token\"`\n}\n\n// swagger:model pagePaginationResponseHeaders\ntype ResponseHeaderAnnotation struct {\n\t// The Link HTTP Header\n\t//\n\t// The `Link` header contains a comma-delimited list of links to the following pages:\n\t//\n\t// - first: The first page of results.\n\t// - next: The next page of results.\n\t// - prev: The previous page of results.\n\t// - last: The last page of results.\n\t//\n\t// Pages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted.\n\t//\n\t// This header will include the `per_page` and `page` parameters for legacy reasons, but these parameters will eventually be removed.\n\t//\n\t//\tExample: Link: </clients?page_size=5&page_token=0>; rel=\"first\",</clients?page_size=5&page_token=15>; rel=\"next\",</clients?page_size=5&page_token=5>; rel=\"prev\",</clients?page_size=5&page_token=20>; rel=\"last\"\n\tLink string `json:\"link\"`\n\n\t// The X-Total-Count HTTP Header\n\t//\n\t// The `X-Total-Count` header contains the total number of items in the collection.\n\t//\n\t// Example: 123\n\tTotalCount int `json:\"x-total-count\"`\n}\n"
  },
  {
    "path": "oryx/pagination/pagepagination/pagination.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage pagepagination\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\n\t\"github.com/ory/x/pagination\"\n)\n\ntype PagePaginator struct {\n\tMaxItems     int\n\tDefaultItems int\n}\n\nfunc (p *PagePaginator) defaults() {\n\tif p.MaxItems == 0 {\n\t\tp.MaxItems = 1000\n\t}\n\n\tif p.DefaultItems == 0 {\n\t\tp.DefaultItems = 250\n\t}\n}\n\n// ParsePagination parses limit and page from *http.Request with given limits and defaults.\nfunc (p *PagePaginator) ParsePagination(r *http.Request) (page, itemsPerPage int) {\n\tp.defaults()\n\n\tif offsetParam := r.URL.Query().Get(\"page\"); offsetParam == \"\" {\n\t\tpage = 0\n\t} else {\n\t\tif offset, err := strconv.ParseInt(offsetParam, 10, 0); err != nil {\n\t\t\tpage = 0\n\t\t} else {\n\t\t\tpage = int(offset)\n\t\t}\n\t}\n\n\tif limitParam := r.URL.Query().Get(\"per_page\"); limitParam == \"\" {\n\t\titemsPerPage = p.DefaultItems\n\t} else {\n\t\tif limit, err := strconv.ParseInt(limitParam, 10, 0); err != nil {\n\t\t\titemsPerPage = p.DefaultItems\n\t\t} else {\n\t\t\titemsPerPage = int(limit)\n\t\t}\n\t}\n\n\tif itemsPerPage > p.MaxItems {\n\t\titemsPerPage = p.MaxItems\n\t}\n\n\tif itemsPerPage < 1 {\n\t\titemsPerPage = 1\n\t}\n\n\tif page < 0 {\n\t\tpage = 0\n\t}\n\n\treturn\n}\n\nfunc header(u *url.URL, rel string, limit, offset int64) string {\n\tq := u.Query()\n\tq.Set(\"per_page\", fmt.Sprintf(\"%d\", limit))\n\tq.Set(\"page\", fmt.Sprintf(\"%d\", offset/limit))\n\tu.RawQuery = q.Encode()\n\treturn fmt.Sprintf(\"<%s>; rel=\\\"%s\\\"\", u.String(), rel)\n}\n\nfunc PaginationHeader(w http.ResponseWriter, u *url.URL, total int64, page, itemsPerPage int) {\n\tpagination.HeaderWithFormatter(w, u, total, page, itemsPerPage, header)\n}\n"
  },
  {
    "path": "oryx/pagination/paginationplanner/planner.go",
    "content": "// Copyright © 2026 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage paginationplanner\n\nimport (\n\t\"github.com/pkg/errors\"\n\n\tkeysetpagination \"github.com/ory/x/pagination/keysetpagination_v2\"\n)\n\n// PaginationPlanner chooses the best [PaginationPlan] for a queriedColumns.\n// Plan is eligible when its required constraint set is satisfied (and an optional Condition matches).\n// If no plan matches, the FallbackPlan is used.\ntype PaginationPlanner struct {\n\tPlans        []PaginationPlan\n\tFallbackPlan PaginationPlan\n}\n\nfunc NewPaginationPlanner(fallbackPlan PaginationPlan, plans []PaginationPlan) (*PaginationPlanner, error) {\n\tif len(fallbackPlan.DefaultPageToken.Columns()) == 0 {\n\t\treturn nil, errors.New(\"plan must define at least one PageTokenColumn\")\n\t}\n\n\tfor i := range plans {\n\t\tplan := &plans[i]\n\t\tif len(plan.ApplicableQueries) == 0 {\n\t\t\treturn nil, errors.New(\"plan must define at least one ApplicableQueries\")\n\t\t}\n\t\tif len(plan.DefaultPageToken.Columns()) == 0 {\n\t\t\treturn nil, errors.New(\"plan must define a DefaultPageToken\")\n\t\t}\n\t\tplan.populateInternals()\n\t}\n\n\treturn &PaginationPlanner{\n\t\tPlans:        plans,\n\t\tFallbackPlan: fallbackPlan,\n\t}, nil\n}\n\n// GetPaginator selects the first eligible plan for the given queriedColumns constraints.\n// Eligibility requires an exact ColumnSet match and (if set) Condition match.\n// If none match, the FallbackPlan is used for building the Paginator.\nfunc (p *PaginationPlanner) GetPaginator(q Query, pageOpts ...keysetpagination.Option) (*keysetpagination.Paginator, error) {\n\tif len(q) == 0 {\n\t\treturn keysetpagination.NewPaginator(append(pageOpts, keysetpagination.WithDefaultToken(p.FallbackPlan.DefaultPageToken))...)\n\t}\n\n\tplan := p.pickPlan(q)\n\n\torigCols := plan.DefaultPageToken.Columns()\n\n\t// Make a fresh copy so we don't mutate the original backing array.\n\tcols := make([]keysetpagination.Column, len(origCols))\n\tcopy(cols, origCols)\n\n\tfor i, col := range cols {\n\t\tif val, ok := q.colByName(col.Name); ok && val.IsConstrained() {\n\t\t\tcols[i].HasConstraint = true\n\t\t}\n\t}\n\n\tdefaultToken := keysetpagination.WithDefaultToken(keysetpagination.NewPageToken(cols...))\n\treturn keysetpagination.NewPaginator(append(pageOpts, defaultToken)...)\n}\n\nfunc (p *PaginationPlanner) pickPlan(q Query) PaginationPlan {\n\tconstrainedCols := q.constrainedCols()\n\n\tfor _, plan := range p.Plans {\n\t\tif _, ok := plan.applicableQueries[constrainedCols]; !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif plan.Condition != nil && !plan.Condition(q) {\n\t\t\tcontinue\n\t\t}\n\t\treturn plan\n\t}\n\n\treturn p.FallbackPlan\n}\n\ntype PaginationPlan struct {\n\tName string\n\n\t// DefaultPageToken defines the columns that the pagination will be made of\n\tDefaultPageToken keysetpagination.PageToken\n\n\t// ApplicableQueries defines the exact sets of columns that this pagination plan is applicable for.\n\tApplicableQueries [][]Column\n\tapplicableQueries map[uint]struct{}\n\n\t// Condition further restricts the Plan eligibility for given queriedColumns.\n\t// This can be used for plan-specific logic, e.g. verifying partial-index predicates match.\n\tCondition func(q Query) bool\n}\n\nfunc (pp *PaginationPlan) populateInternals() {\n\tpp.applicableQueries = make(map[uint]struct{}, len(pp.ApplicableQueries))\n\tfor _, cols := range pp.ApplicableQueries {\n\t\tvar colSet uint\n\t\tfor _, col := range cols {\n\t\t\tcolSet |= col.bit\n\t\t}\n\t\tpp.applicableQueries[colSet] = struct{}{}\n\t}\n}\n\ntype Table struct {\n\tnextBit   uint\n\tusedNames map[string]struct{}\n}\n\nfunc NewTable() *Table {\n\treturn &Table{\n\t\tnextBit:   1,\n\t\tusedNames: make(map[string]struct{}),\n\t}\n}\n\nfunc (t *Table) NewColumn(name string) Column {\n\tdefer func() { t.nextBit = t.nextBit << 1 }()\n\tif _, exists := t.usedNames[name]; exists {\n\t\tpanic(\"column name already used: \" + name)\n\t}\n\tt.usedNames[name] = struct{}{}\n\treturn Column{\n\t\tname: name,\n\t\tbit:  t.nextBit,\n\t}\n}\n\ntype Column struct {\n\tbit  uint\n\tname string\n}\n\nfunc (c Column) Name() string {\n\treturn c.name\n}\n\ntype ColumnConstraint uint8\n\nconst (\n\tcolUnconstrained    ColumnConstraint = iota\n\tcolConstraintEq                      // col = ?\n\tcolConstraintIsNull                  // col IS NULL\n)\n\nfunc (cs ColumnConstraint) IsConstrained() bool {\n\treturn cs == colConstraintEq || cs == colConstraintIsNull\n}\n\nfunc (cs ColumnConstraint) IsNull() bool {\n\treturn cs == colConstraintIsNull\n}\n\n// Query is used to track which column constraints are set.\ntype Query map[Column]ColumnConstraint\n\nfunc NewQuery() Query { return make(Query) }\n\nfunc (qc Query) SetEq(cols ...Column) Query {\n\treturn qc.set(colConstraintEq, cols...)\n}\n\nfunc (qc Query) SetIsNull(cols ...Column) Query {\n\treturn qc.set(colConstraintIsNull, cols...)\n}\n\nfunc (qc Query) set(c ColumnConstraint, cols ...Column) Query {\n\tfor _, col := range cols {\n\t\tqc[col] = c\n\t}\n\treturn qc\n}\n\nfunc (qc Query) colByName(name string) (ColumnConstraint, bool) {\n\tfor col, constraint := range qc {\n\t\tif col.name == name {\n\t\t\treturn constraint, true\n\t\t}\n\t}\n\treturn colUnconstrained, false\n}\n\nfunc (qc Query) constrainedCols() uint {\n\tvar cols uint\n\tfor col, state := range qc {\n\t\tif !state.IsConstrained() {\n\t\t\tcontinue\n\t\t}\n\t\tcols |= col.bit\n\t}\n\treturn cols\n}\n"
  },
  {
    "path": "oryx/pagination/parse.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage pagination\n\nimport (\n\t\"net/http\"\n\t\"strconv\"\n)\n\n// Parse parses limit and offset from *http.Request with given limits and defaults.\nfunc Parse(r *http.Request, defaultLimit, defaultOffset, maxLimit int) (int, int) {\n\tvar offset, limit int\n\n\tif offsetParam := r.URL.Query().Get(\"offset\"); offsetParam == \"\" {\n\t\toffset = defaultOffset\n\t} else {\n\t\tif offset64, err := strconv.ParseInt(offsetParam, 10, 64); err != nil {\n\t\t\toffset = defaultOffset\n\t\t} else {\n\t\t\toffset = int(offset64)\n\t\t}\n\t}\n\n\tif limitParam := r.URL.Query().Get(\"limit\"); limitParam == \"\" {\n\t\tlimit = defaultLimit\n\t} else {\n\t\tif limit64, err := strconv.ParseInt(limitParam, 10, 64); err != nil {\n\t\t\tlimit = defaultLimit\n\t\t} else {\n\t\t\tlimit = int(limit64)\n\t\t}\n\t}\n\n\tif limit > maxLimit {\n\t\tlimit = maxLimit\n\t}\n\n\tif limit < 0 {\n\t\tlimit = 0\n\t}\n\n\tif offset < 0 {\n\t\toffset = 0\n\t}\n\n\treturn limit, offset\n}\n"
  },
  {
    "path": "oryx/pagination/tokenpagination/.snapshots/TestPaginationHeader-Create_next_and_last,_but_not_previous_or_first_if_at_the_beginning.json",
    "content": "[\n  \"\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiI1MCIsInYiOjJ9\\u003e\",\n  \"rel=\\\"next\\\",\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiIxMDAiLCJ2IjoyfQ\\u003e\",\n  \"rel=\\\"last\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/tokenpagination/.snapshots/TestPaginationHeader-Create_only_first_if_the_limits_provided_exceeds_the_number_of_clients_found.json",
    "content": "[\n  \"\\u003chttp://example.com?page_size=5\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u003e\",\n  \"rel=\\\"first\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/tokenpagination/.snapshots/TestPaginationHeader-Create_previous,_next,_first,_and_last_if_in_the_middle.json",
    "content": "[\n  \"\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u003e\",\n  \"rel=\\\"first\\\",\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiIyMDAiLCJ2IjoyfQ\\u003e\",\n  \"rel=\\\"next\\\",\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiIxMDAiLCJ2IjoyfQ\\u003e\",\n  \"rel=\\\"prev\\\",\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiIyNTAiLCJ2IjoyfQ\\u003e\",\n  \"rel=\\\"last\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/tokenpagination/.snapshots/TestPaginationHeader-Create_previous,_next,_first,_but_not_last_if_in_the_middle_and_no_total_was_provided.json",
    "content": "[\n  \"\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u003e\",\n  \"rel=\\\"first\\\",\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiIyMDAiLCJ2IjoyfQ\\u003e\",\n  \"rel=\\\"next\\\",\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiIxMDAiLCJ2IjoyfQ\\u003e\",\n  \"rel=\\\"prev\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/tokenpagination/.snapshots/TestPaginationHeader-Create_previous_and_first_but_not_next_or_last_if_at_the_end.json",
    "content": "[\n  \"\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u003e\",\n  \"rel=\\\"first\\\",\\u003chttp://example.com?page_size=50\\u0026page_token=eyJvZmZzZXQiOiI1MCIsInYiOjJ9\\u003e\",\n  \"rel=\\\"prev\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/tokenpagination/.snapshots/TestPaginationHeader-Header_should_default_limit_to_1_no_limit_was_provided.json",
    "content": "[\n  \"\\u003chttp://example.com?page_size=1\\u0026page_token=eyJvZmZzZXQiOiIwIiwidiI6Mn0\\u003e\",\n  \"rel=\\\"first\\\",\\u003chttp://example.com?page_size=1\\u0026page_token=eyJvZmZzZXQiOiIyMSIsInYiOjJ9\\u003e\",\n  \"rel=\\\"next\\\",\\u003chttp://example.com?page_size=1\\u0026page_token=eyJvZmZzZXQiOiIxOSIsInYiOjJ9\\u003e\",\n  \"rel=\\\"prev\\\",\\u003chttp://example.com?page_size=1\\u0026page_token=eyJvZmZzZXQiOiI5OSIsInYiOjJ9\\u003e\",\n  \"rel=\\\"last\\\"\"\n]\n"
  },
  {
    "path": "oryx/pagination/tokenpagination/header.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage tokenpagination\n\n// Pagination Request Parameters\n//\n// The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n// `<https://{project-slug}.projects.oryapis.com/admin/clients?page_size={limit}&page_token={offset}>; rel=\"{page}\"`\n//\n// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n//\n// swagger:model tokenPaginationRequestParameters\ntype RequestParameters struct {\n\t// Items per Page\n\t//\n\t// This is the number of items per page to return.\n\t// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\t// default: 250\n\t// min: 1\n\t// max: 500\n\tPageSize int `json:\"page_size\"`\n\n\t// Next Page Token\n\t//\n\t// The next page token.\n\t// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\t//\n\t// required: false\n\t// in: query\n\tPageToken string `json:\"page_token\"`\n}\n\n// Pagination Response Header\n//\n// The `Link` HTTP header contains multiple links (`first`, `next`, `last`, `previous`) formatted as:\n// `<https://{project-slug}.projects.oryapis.com/admin/clients?page_size={limit}&page_token={offset}>; rel=\"{page}\"`\n//\n// For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n//\n// swagger:model tokenPaginationResponseHeaders\ntype ResponseHeaders struct {\n\t// The Link HTTP Header\n\t//\n\t// The `Link` header contains a comma-delimited list of links to the following pages:\n\t//\n\t// - first: The first page of results.\n\t// - next: The next page of results.\n\t// - prev: The previous page of results.\n\t// - last: The last page of results.\n\t//\n\t// Pages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted. Examples:\n\t//\n\t//\t</clients?page_size=5&page_token=0>; rel=\"first\",</clients?page_size=5&page_token=15>; rel=\"next\",</clients?page_size=5&page_token=5>; rel=\"prev\",</clients?page_size=5&page_token=20>; rel=\"last\"\n\t//\n\tLink string `json:\"link\"`\n\n\t// The X-Total-Count HTTP Header\n\t//\n\t// The `X-Total-Count` header contains the total number of items in the collection.\n\tTotalCount int `json:\"x-total-count\"`\n}\n"
  },
  {
    "path": "oryx/pagination/tokenpagination/pagination.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage tokenpagination\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/pagination\"\n\n\t\"github.com/ory/herodot\"\n)\n\nfunc Encode(offset int64) string {\n\treturn base64.RawURLEncoding.EncodeToString([]byte(fmt.Sprintf(`{\"offset\":\"%d\",\"v\":2}`, offset)))\n}\n\nfunc decode(s string) (int, error) {\n\tb, err := base64.RawURLEncoding.DecodeString(s)\n\tif err != nil {\n\t\treturn 0, errors.WithStack(herodot.ErrBadRequest.WithWrap(err).WithReasonf(\"Unable to parse pagination token: %s\", err))\n\t}\n\n\treturn int(gjson.Get(string(b), \"offset\").Int()), nil\n}\n\ntype TokenPaginator struct {\n\tMaxItems     int\n\tDefaultItems int\n}\n\nfunc (p *TokenPaginator) defaults() {\n\tif p.MaxItems == 0 {\n\t\tp.MaxItems = 1000\n\t}\n\n\tif p.DefaultItems == 0 {\n\t\tp.DefaultItems = 250\n\t}\n}\n\n// ParsePagination parses limit and page from *http.Request with given limits and defaults.\nfunc (p *TokenPaginator) ParsePagination(r *http.Request) (page, itemsPerPage int) {\n\tp.defaults()\n\n\tvar offset int\n\tif offsetParam := r.URL.Query().Get(\"page_token\"); len(offsetParam) > 0 {\n\t\toffset, _ = decode(offsetParam)\n\t}\n\n\tif gotLimit, err := strconv.ParseInt(r.URL.Query().Get(\"page_size\"), 10, 0); err == nil {\n\t\titemsPerPage = int(gotLimit)\n\t} else {\n\t\titemsPerPage = p.DefaultItems\n\t}\n\n\tif itemsPerPage > p.MaxItems {\n\t\titemsPerPage = p.MaxItems\n\t}\n\n\tif itemsPerPage < 1 {\n\t\titemsPerPage = 1\n\t}\n\n\tif offset > 0 {\n\t\tpage = offset / itemsPerPage\n\t}\n\n\tif page < 0 {\n\t\tpage = 0\n\t}\n\n\treturn\n}\n\nfunc header(u *url.URL, rel string, itemsPerPage, offset int64) string {\n\tq := u.Query()\n\tq.Set(\"page_size\", fmt.Sprintf(\"%d\", itemsPerPage))\n\tq.Set(\"page_token\", Encode(offset))\n\tu.RawQuery = q.Encode()\n\treturn fmt.Sprintf(\"<%s>; rel=\\\"%s\\\"\", u.String(), rel)\n}\n\nfunc PaginationHeader(w http.ResponseWriter, u *url.URL, total int64, page, itemsPerPage int) {\n\tpagination.HeaderWithFormatter(w, u, total, page, itemsPerPage, header)\n}\n"
  },
  {
    "path": "oryx/pointerx/pointerx.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage pointerx\n\n// Deref returns the input values de-referenced value, or zero value if nil.\nfunc Deref[T any](p *T) T {\n\tif p == nil {\n\t\tvar zero T\n\t\treturn zero\n\t}\n\treturn *p\n}\n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-final_status.txt",
    "content": "stdout: Version\t\t\tName\t\t\t\t\tStatus\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\n20191100000002000000\trequests\t\t\t\tApplied\t\n20191100000002000001\trequests\t\t\t\tApplied\t\n20191100000002000002\trequests\t\t\t\tApplied\t\n20191100000002000003\trequests\t\t\t\tApplied\t\n20191100000002000004\trequests\t\t\t\tApplied\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tApplied\t\n20201201161451000000\tcredential_types_values\t\t\tApplied\t\n20201201161451000001\tcredential_types_values\t\t\tApplied\t\n\nstderr: \n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-migrate_down_but_do_not_confirm.txt",
    "content": "stdout: The migration plan is as follows:\nVersion\t\t\tName\t\t\t\t\tStatus\t\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\t\n20191100000002000000\trequests\t\t\t\tApplied\t\t\n20191100000002000001\trequests\t\t\t\tApplied\t\t\n20191100000002000002\trequests\t\t\t\tApplied\t\t\n20191100000002000003\trequests\t\t\t\tApplied\t\t\n20191100000002000004\trequests\t\t\t\tApplied\t\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tRollback\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tRollback\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tPending\t\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tPending\t\t\n20201201161451000000\tcredential_types_values\t\t\tPending\t\t\n20201201161451000001\tcredential_types_values\t\t\tPending\t\t\n\nThe SQL statements to be executed from top to bottom are:\n\n------------ 20200831110752000019 - identity_verifiable_address_remove_code ------------\nUPDATE identity_verifiable_addresses SET code = substr(hex(randomblob(32)), 0, 32) WHERE code IS NULL\n\n------------ 20200831110752000018 - identity_verifiable_address_remove_code ------------\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL\n\nDo you wish to execute this migration plan? [y/n]: ------------ WARNING ------------\nMigration aborted.\n\nstderr: To skip the next question use flag --yes (at your own risk).\n\n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-migrate_down_but_no_steps.txt",
    "content": "stdout: The migration plan is as follows:\nVersion\t\t\tName\t\t\t\t\tStatus\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\n20191100000002000000\trequests\t\t\t\tApplied\t\n20191100000002000001\trequests\t\t\t\tApplied\t\n20191100000002000002\trequests\t\t\t\tApplied\t\n20191100000002000003\trequests\t\t\t\tApplied\t\n20191100000002000004\trequests\t\t\t\tApplied\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tPending\t\n20201201161451000000\tcredential_types_values\t\t\tPending\t\n20201201161451000001\tcredential_types_values\t\t\tPending\t\n\nstderr: \nThere are apparently no migrations to roll back.\nPlease provide the --steps argument with a value larger than 0.\n\n\n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-migrate_down_four_steps.txt",
    "content": "stdout: The migration plan is as follows:\nVersion\t\t\tName\t\t\t\t\tStatus\t\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\t\n20191100000002000000\trequests\t\t\t\tApplied\t\t\n20191100000002000001\trequests\t\t\t\tApplied\t\t\n20191100000002000002\trequests\t\t\t\tApplied\t\t\n20191100000002000003\trequests\t\t\t\tApplied\t\t\n20191100000002000004\trequests\t\t\t\tApplied\t\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tRollback\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tRollback\t\n20201201161451000000\tcredential_types_values\t\t\tRollback\t\n20201201161451000001\tcredential_types_values\t\t\tRollback\t\n\nThe SQL statements to be executed from top to bottom are:\n\n------------ 20201201161451000001 - credential_types_values ------------\n\n\n------------ 20201201161451000000 - credential_types_values ------------\nDELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';\n\n------------ 20200831110752000021 - identity_verifiable_address_remove_code ------------\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" TEXT\n\n------------ 20200831110752000020 - identity_verifiable_address_remove_code ------------\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" DATETIME\n\n------------ SUCCESS ------------\nSuccessfully applied migrations!\n\nstderr: \n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-migrate_down_two_steps.txt",
    "content": "stdout: The migration plan is as follows:\nVersion\t\t\tName\t\t\t\t\tStatus\t\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\t\n20191100000002000000\trequests\t\t\t\tApplied\t\t\n20191100000002000001\trequests\t\t\t\tApplied\t\t\n20191100000002000002\trequests\t\t\t\tApplied\t\t\n20191100000002000003\trequests\t\t\t\tApplied\t\t\n20191100000002000004\trequests\t\t\t\tApplied\t\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tRollback\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tRollback\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tPending\t\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tPending\t\t\n20201201161451000000\tcredential_types_values\t\t\tPending\t\t\n20201201161451000001\tcredential_types_values\t\t\tPending\t\t\n\nThe SQL statements to be executed from top to bottom are:\n\n------------ 20200831110752000019 - identity_verifiable_address_remove_code ------------\nUPDATE identity_verifiable_addresses SET code = substr(hex(randomblob(32)), 0, 32) WHERE code IS NULL\n\n------------ 20200831110752000018 - identity_verifiable_address_remove_code ------------\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL\n\nDo you wish to execute this migration plan? [y/n]: ------------ SUCCESS ------------\nSuccessfully applied migrations!\n\nstderr: To skip the next question use flag --yes (at your own risk).\n\n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-migrate_rollbacks_up_again.txt",
    "content": "stdout: The migration plan is as follows:\nVersion\t\t\tName\t\t\t\t\tStatus\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\n20191100000002000000\trequests\t\t\t\tApplied\t\n20191100000002000001\trequests\t\t\t\tApplied\t\n20191100000002000002\trequests\t\t\t\tApplied\t\n20191100000002000003\trequests\t\t\t\tApplied\t\n20191100000002000004\trequests\t\t\t\tApplied\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tPending\t\n20201201161451000000\tcredential_types_values\t\t\tPending\t\n20201201161451000001\tcredential_types_values\t\t\tPending\t\n\nThe SQL statements to be executed from top to bottom are:\n\n------------ 20200831110752000018 - identity_verifiable_address_remove_code ------------\n\n\n------------ 20200831110752000019 - identity_verifiable_address_remove_code ------------\n\n\n------------ 20200831110752000020 - identity_verifiable_address_remove_code ------------\n\n\n------------ 20200831110752000021 - identity_verifiable_address_remove_code ------------\n\n\n------------ 20201201161451000000 - credential_types_values ------------\nINSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password')\n\n------------ 20201201161451000001 - credential_types_values ------------\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');\n\nDo you wish to execute this migration plan? [y/n]: ------------ SUCCESS ------------\nSuccessfully applied migrations!\n\nstderr: To skip the next question use flag --yes (at your own risk).\n\n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-migrate_rollbacks_up_without_confirm.txt",
    "content": "stdout: The migration plan is as follows:\nVersion\t\t\tName\t\t\t\t\tStatus\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\n20191100000002000000\trequests\t\t\t\tApplied\t\n20191100000002000001\trequests\t\t\t\tApplied\t\n20191100000002000002\trequests\t\t\t\tApplied\t\n20191100000002000003\trequests\t\t\t\tApplied\t\n20191100000002000004\trequests\t\t\t\tApplied\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tApplied\t\n20201201161451000000\tcredential_types_values\t\t\tApplied\t\n20201201161451000001\tcredential_types_values\t\t\tApplied\t\n\nThe SQL statements to be executed from top to bottom are:\n\nDo you wish to execute this migration plan? [y/n]: ------------ WARNING ------------\nMigration aborted.\n\nstderr: To skip the next question use flag --yes (at your own risk).\n\n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-migrate_up.txt",
    "content": "stdout: The migration plan is as follows:\nVersion\t\t\tName\t\t\t\t\tStatus\t\n20191100000001000000\tidentities\t\t\t\tPending\t\n20191100000001000001\tidentities\t\t\t\tPending\t\n20191100000001000002\tidentities\t\t\t\tPending\t\n20191100000001000003\tidentities\t\t\t\tPending\t\n20191100000001000004\tidentities\t\t\t\tPending\t\n20191100000001000005\tidentities\t\t\t\tPending\t\n20191100000002000000\trequests\t\t\t\tPending\t\n20191100000002000001\trequests\t\t\t\tPending\t\n20191100000002000002\trequests\t\t\t\tPending\t\n20191100000002000003\trequests\t\t\t\tPending\t\n20191100000002000004\trequests\t\t\t\tPending\t\n20191100000003000000\tsessions\t\t\t\tPending\t\n20191100000004000000\terrors\t\t\t\t\tPending\t\n20191100000006000000\tcourier\t\t\t\t\tPending\t\n20191100000007000000\terrors\t\t\t\t\tPending\t\n20191100000007000001\terrors\t\t\t\t\tPending\t\n20191100000007000002\terrors\t\t\t\t\tPending\t\n20191100000007000003\terrors\t\t\t\t\tPending\t\n20191100000008000000\tselfservice_verification\t\tPending\t\n20191100000008000001\tselfservice_verification\t\tPending\t\n20191100000008000002\tselfservice_verification\t\tPending\t\n20191100000008000003\tselfservice_verification\t\tPending\t\n20191100000008000004\tselfservice_verification\t\tPending\t\n20191100000008000005\tselfservice_verification\t\tPending\t\n20191100000010000000\terrors\t\t\t\t\tPending\t\n20191100000010000001\terrors\t\t\t\t\tPending\t\n20191100000010000002\terrors\t\t\t\t\tPending\t\n20191100000010000003\terrors\t\t\t\t\tPending\t\n20191100000010000004\terrors\t\t\t\t\tPending\t\n20191100000011000000\tcourier_body_type\t\t\tPending\t\n20191100000011000001\tcourier_body_type\t\t\tPending\t\n20191100000011000002\tcourier_body_type\t\t\tPending\t\n20191100000011000003\tcourier_body_type\t\t\tPending\t\n20191100000012000000\tlogin_request_forced\t\t\tPending\t\n20191100000012000001\tlogin_request_forced\t\t\tPending\t\n20191100000012000002\tlogin_request_forced\t\t\tPending\t\n20191100000012000003\tlogin_request_forced\t\t\tPending\t\n20200317160354000000\tcreate_profile_request_forms\t\tPending\t\n20200317160354000001\tcreate_profile_request_forms\t\tPending\t\n20200317160354000002\tcreate_profile_request_forms\t\tPending\t\n20200317160354000003\tcreate_profile_request_forms\t\tPending\t\n20200317160354000004\tcreate_profile_request_forms\t\tPending\t\n20200317160354000005\tcreate_profile_request_forms\t\tPending\t\n20200317160354000006\tcreate_profile_request_forms\t\tPending\t\n20200401183443000000\tcontinuity_containers\t\t\tPending\t\n20200402142539000000\trename_profile_flows\t\t\tPending\t\n20200402142539000001\trename_profile_flows\t\t\tPending\t\n20200402142539000002\trename_profile_flows\t\t\tPending\t\n20200519101057000000\tcreate_recovery_addresses\t\tPending\t\n20200519101057000001\tcreate_recovery_addresses\t\tPending\t\n20200519101057000002\tcreate_recovery_addresses\t\tPending\t\n20200519101057000003\tcreate_recovery_addresses\t\tPending\t\n20200519101057000004\tcreate_recovery_addresses\t\tPending\t\n20200519101057000005\tcreate_recovery_addresses\t\tPending\t\n20200519101057000006\tcreate_recovery_addresses\t\tPending\t\n20200519101057000007\tcreate_recovery_addresses\t\tPending\t\n20200601101000000000\tcreate_messages\t\t\t\tPending\t\n20200601101000000001\tcreate_messages\t\t\t\tPending\t\n20200601101000000002\tcreate_messages\t\t\t\tPending\t\n20200601101000000003\tcreate_messages\t\t\t\tPending\t\n20200605111551000000\tmessages\t\t\t\tPending\t\n20200605111551000001\tmessages\t\t\t\tPending\t\n20200605111551000002\tmessages\t\t\t\tPending\t\n20200605111551000003\tmessages\t\t\t\tPending\t\n20200605111551000004\tmessages\t\t\t\tPending\t\n20200605111551000005\tmessages\t\t\t\tPending\t\n20200605111551000006\tmessages\t\t\t\tPending\t\n20200605111551000007\tmessages\t\t\t\tPending\t\n20200605111551000008\tmessages\t\t\t\tPending\t\n20200605111551000009\tmessages\t\t\t\tPending\t\n20200605111551000010\tmessages\t\t\t\tPending\t\n20200605111551000011\tmessages\t\t\t\tPending\t\n20200607165100000000\tsettings\t\t\t\tPending\t\n20200607165100000001\tsettings\t\t\t\tPending\t\n20200607165100000002\tsettings\t\t\t\tPending\t\n20200607165100000003\tsettings\t\t\t\tPending\t\n20200607165100000004\tsettings\t\t\t\tPending\t\n20200705105359000000\trename_identities_schema\t\tPending\t\n20200810141652000000\tflow_type\t\t\t\tPending\t\n20200810141652000001\tflow_type\t\t\t\tPending\t\n20200810141652000002\tflow_type\t\t\t\tPending\t\n20200810141652000003\tflow_type\t\t\t\tPending\t\n20200810141652000004\tflow_type\t\t\t\tPending\t\n20200810141652000005\tflow_type\t\t\t\tPending\t\n20200810141652000006\tflow_type\t\t\t\tPending\t\n20200810141652000007\tflow_type\t\t\t\tPending\t\n20200810141652000008\tflow_type\t\t\t\tPending\t\n20200810141652000009\tflow_type\t\t\t\tPending\t\n20200810141652000010\tflow_type\t\t\t\tPending\t\n20200810141652000011\tflow_type\t\t\t\tPending\t\n20200810141652000012\tflow_type\t\t\t\tPending\t\n20200810141652000013\tflow_type\t\t\t\tPending\t\n20200810141652000014\tflow_type\t\t\t\tPending\t\n20200810141652000015\tflow_type\t\t\t\tPending\t\n20200810141652000016\tflow_type\t\t\t\tPending\t\n20200810141652000017\tflow_type\t\t\t\tPending\t\n20200810141652000018\tflow_type\t\t\t\tPending\t\n20200810141652000019\tflow_type\t\t\t\tPending\t\n20200810161022000000\tflow_rename\t\t\t\tPending\t\n20200810161022000001\tflow_rename\t\t\t\tPending\t\n20200810161022000002\tflow_rename\t\t\t\tPending\t\n20200810161022000003\tflow_rename\t\t\t\tPending\t\n20200810161022000004\tflow_rename\t\t\t\tPending\t\n20200810161022000005\tflow_rename\t\t\t\tPending\t\n20200810161022000006\tflow_rename\t\t\t\tPending\t\n20200810161022000007\tflow_rename\t\t\t\tPending\t\n20200810161022000008\tflow_rename\t\t\t\tPending\t\n20200810162450000000\tflow_fields_rename\t\t\tPending\t\n20200810162450000001\tflow_fields_rename\t\t\tPending\t\n20200810162450000002\tflow_fields_rename\t\t\tPending\t\n20200810162450000003\tflow_fields_rename\t\t\tPending\t\n20200812124254000000\tadd_session_token\t\t\tPending\t\n20200812124254000001\tadd_session_token\t\t\tPending\t\n20200812124254000002\tadd_session_token\t\t\tPending\t\n20200812124254000003\tadd_session_token\t\t\tPending\t\n20200812124254000004\tadd_session_token\t\t\tPending\t\n20200812124254000005\tadd_session_token\t\t\tPending\t\n20200812124254000006\tadd_session_token\t\t\tPending\t\n20200812124254000007\tadd_session_token\t\t\tPending\t\n20200812160551000000\tadd_session_revoke\t\t\tPending\t\n20200812160551000001\tadd_session_revoke\t\t\tPending\t\n20200812160551000002\tadd_session_revoke\t\t\tPending\t\n20200812160551000003\tadd_session_revoke\t\t\tPending\t\n20200812160551000004\tadd_session_revoke\t\t\tPending\t\n20200812160551000005\tadd_session_revoke\t\t\tPending\t\n20200812160551000006\tadd_session_revoke\t\t\tPending\t\n20200812160551000007\tadd_session_revoke\t\t\tPending\t\n20200830121710000000\tupdate_recovery_token\t\t\tPending\t\n20200830130642000000\tadd_verification_methods\t\tPending\t\n20200830130642000001\tadd_verification_methods\t\tPending\t\n20200830130642000002\tadd_verification_methods\t\tPending\t\n20200830130642000003\tadd_verification_methods\t\tPending\t\n20200830130642000004\tadd_verification_methods\t\tPending\t\n20200830130642000005\tadd_verification_methods\t\tPending\t\n20200830130642000006\tadd_verification_methods\t\tPending\t\n20200830130642000007\tadd_verification_methods\t\tPending\t\n20200830130642000008\tadd_verification_methods\t\tPending\t\n20200830130642000009\tadd_verification_methods\t\tPending\t\n20200830130642000010\tadd_verification_methods\t\tPending\t\n20200830130643000000\tadd_verification_methods\t\tPending\t\n20200830130644000000\tadd_verification_methods\t\tPending\t\n20200830130644000001\tadd_verification_methods\t\tPending\t\n20200830130645000000\tadd_verification_methods\t\tPending\t\n20200830130646000000\tadd_verification_methods\t\tPending\t\n20200830130646000001\tadd_verification_methods\t\tPending\t\n20200830130646000002\tadd_verification_methods\t\tPending\t\n20200830130646000003\tadd_verification_methods\t\tPending\t\n20200830130646000004\tadd_verification_methods\t\tPending\t\n20200830130646000005\tadd_verification_methods\t\tPending\t\n20200830130646000006\tadd_verification_methods\t\tPending\t\n20200830130646000007\tadd_verification_methods\t\tPending\t\n20200830130646000008\tadd_verification_methods\t\tPending\t\n20200830130646000009\tadd_verification_methods\t\tPending\t\n20200830130646000010\tadd_verification_methods\t\tPending\t\n20200830130646000011\tadd_verification_methods\t\tPending\t\n20200830154602000000\tadd_verification_token\t\t\tPending\t\n20200830154602000001\tadd_verification_token\t\t\tPending\t\n20200830154602000002\tadd_verification_token\t\t\tPending\t\n20200830154602000003\tadd_verification_token\t\t\tPending\t\n20200830154602000004\tadd_verification_token\t\t\tPending\t\n20200830172221000000\trecovery_token_expires\t\t\tPending\t\n20200830172221000001\trecovery_token_expires\t\t\tPending\t\n20200830172221000002\trecovery_token_expires\t\t\tPending\t\n20200830172221000003\trecovery_token_expires\t\t\tPending\t\n20200830172221000004\trecovery_token_expires\t\t\tPending\t\n20200830172221000005\trecovery_token_expires\t\t\tPending\t\n20200830172221000006\trecovery_token_expires\t\t\tPending\t\n20200830172221000007\trecovery_token_expires\t\t\tPending\t\n20200830172221000008\trecovery_token_expires\t\t\tPending\t\n20200830172221000009\trecovery_token_expires\t\t\tPending\t\n20200830172221000010\trecovery_token_expires\t\t\tPending\t\n20200830172221000011\trecovery_token_expires\t\t\tPending\t\n20200830172221000012\trecovery_token_expires\t\t\tPending\t\n20200830172221000013\trecovery_token_expires\t\t\tPending\t\n20200830172221000014\trecovery_token_expires\t\t\tPending\t\n20200830172221000015\trecovery_token_expires\t\t\tPending\t\n20200830172221000016\trecovery_token_expires\t\t\tPending\t\n20200830172221000017\trecovery_token_expires\t\t\tPending\t\n20200830172221000018\trecovery_token_expires\t\t\tPending\t\n20200830172221000019\trecovery_token_expires\t\t\tPending\t\n20200830172221000020\trecovery_token_expires\t\t\tPending\t\n20200830172221000021\trecovery_token_expires\t\t\tPending\t\n20200830172221000022\trecovery_token_expires\t\t\tPending\t\n20200830172221000023\trecovery_token_expires\t\t\tPending\t\n20200830172221000024\trecovery_token_expires\t\t\tPending\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tPending\t\n20201201161451000000\tcredential_types_values\t\t\tPending\t\n20201201161451000001\tcredential_types_values\t\t\tPending\t\n\nThe SQL statements to be executed from top to bottom are:\n\n------------ 20191100000001000000 - identities ------------\nCREATE TABLE \"identities\" (\n\"id\" TEXT PRIMARY KEY,\n\"traits_schema_id\" TEXT NOT NULL,\n\"traits\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)\n\n------------ 20191100000001000001 - identities ------------\nCREATE TABLE \"identity_credential_types\" (\n\"id\" TEXT PRIMARY KEY,\n\"name\" TEXT NOT NULL\n)\n\n------------ 20191100000001000002 - identities ------------\nCREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name)\n\n------------ 20191100000001000003 - identities ------------\nCREATE TABLE \"identity_credentials\" (\n\"id\" TEXT PRIMARY KEY,\n\"config\" TEXT NOT NULL,\n\"identity_credential_type_id\" char(36) NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade,\nFOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON DELETE cascade\n)\n\n------------ 20191100000001000004 - identities ------------\nCREATE TABLE \"identity_credential_identifiers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON DELETE cascade\n)\n\n------------ 20191100000001000005 - identities ------------\nCREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);\n\n------------ 20191100000002000000 - requests ------------\nCREATE TABLE \"selfservice_login_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)\n\n------------ 20191100000002000001 - requests ------------\nCREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_login_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_login_request_id) REFERENCES selfservice_login_requests (id) ON DELETE cascade\n)\n\n------------ 20191100000002000002 - requests ------------\nCREATE TABLE \"selfservice_registration_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)\n\n------------ 20191100000002000003 - requests ------------\nCREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_registration_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_registration_request_id) REFERENCES selfservice_registration_requests (id) ON DELETE cascade\n)\n\n------------ 20191100000002000004 - requests ------------\nCREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);\n\n------------ 20191100000003000000 - sessions ------------\nCREATE TABLE \"sessions\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);\n\n------------ 20191100000004000000 - errors ------------\nCREATE TABLE \"selfservice_errors\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\n\n------------ 20191100000006000000 - courier ------------\nCREATE TABLE \"courier_messages\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\n\n------------ 20191100000007000000 - errors ------------\nALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" TEXT NOT NULL DEFAULT '';\n\n------------ 20191100000007000001 - errors ------------\n\n\n------------ 20191100000007000002 - errors ------------\n\n\n------------ 20191100000007000003 - errors ------------\n\n\n------------ 20191100000008000000 - selfservice_verification ------------\nCREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"code\" TEXT NOT NULL,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n)\n\n------------ 20191100000008000001 - selfservice_verification ------------\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code)\n\n------------ 20191100000008000002 - selfservice_verification ------------\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code)\n\n------------ 20191100000008000003 - selfservice_verification ------------\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value)\n\n------------ 20191100000008000004 - selfservice_verification ------------\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value)\n\n------------ 20191100000008000005 - selfservice_verification ------------\nCREATE TABLE \"selfservice_verification_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\n\n------------ 20191100000010000000 - errors ------------\nCREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n)\n\n------------ 20191100000010000001 - errors ------------\nINSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\"\n\n------------ 20191100000010000002 - errors ------------\nDROP TABLE \"selfservice_errors\"\n\n------------ 20191100000010000003 - errors ------------\nALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";\n\n------------ 20191100000010000004 - errors ------------\n\n\n------------ 20191100000011000000 - courier_body_type ------------\nCREATE TABLE \"_courier_messages_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)\n\n------------ 20191100000011000001 - courier_body_type ------------\nINSERT INTO \"_courier_messages_tmp\" (id, type, status, body, subject, recipient, created_at, updated_at) SELECT id, type, status, body, subject, recipient, created_at, updated_at FROM \"courier_messages\"\n\n------------ 20191100000011000002 - courier_body_type ------------\nDROP TABLE \"courier_messages\"\n\n------------ 20191100000011000003 - courier_body_type ------------\nALTER TABLE \"_courier_messages_tmp\" RENAME TO \"courier_messages\";\n\n------------ 20191100000012000000 - login_request_forced ------------\nALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';\n\n------------ 20191100000012000001 - login_request_forced ------------\n\n\n------------ 20191100000012000002 - login_request_forced ------------\n\n\n------------ 20191100000012000003 - login_request_forced ------------\n\n\n------------ 20200317160354000000 - create_profile_request_forms ------------\nCREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_profile_management_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)\n\n------------ 20200317160354000001 - create_profile_request_forms ------------\nALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" TEXT\n\n------------ 20200317160354000002 - create_profile_request_forms ------------\nINSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests\n\n------------ 20200317160354000003 - create_profile_request_forms ------------\nCREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)\n\n------------ 20200317160354000004 - create_profile_request_forms ------------\nINSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method) SELECT id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method FROM \"selfservice_profile_management_requests\"\n\n------------ 20200317160354000005 - create_profile_request_forms ------------\n\nDROP TABLE \"selfservice_profile_management_requests\"\n\n------------ 20200317160354000006 - create_profile_request_forms ------------\nALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";\n\n------------ 20200401183443000000 - continuity_containers ------------\nCREATE TABLE \"continuity_containers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identity_id\" char(36),\n\"name\" TEXT NOT NULL,\n\"payload\" TEXT,\n\"expires_at\" DATETIME NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);\n\n------------ 20200402142539000000 - rename_profile_flows ------------\nALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\"\n\n------------ 20200402142539000001 - rename_profile_flows ------------\nALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\"\n\n------------ 20200402142539000002 - rename_profile_flows ------------\nALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";\n\n------------ 20200519101057000000 - create_recovery_addresses ------------\nCREATE TABLE \"identity_recovery_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"via\" TEXT NOT NULL,\n\"value\" TEXT NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n)\n\n------------ 20200519101057000001 - create_recovery_addresses ------------\nCREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value)\n\n------------ 20200519101057000002 - create_recovery_addresses ------------\nCREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value)\n\n------------ 20200519101057000003 - create_recovery_addresses ------------\nCREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON DELETE cascade\n)\n\n------------ 20200519101057000004 - create_recovery_addresses ------------\nCREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"config\" TEXT NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n)\n\n------------ 20200519101057000005 - create_recovery_addresses ------------\nCREATE TABLE \"identity_recovery_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n)\n\n------------ 20200519101057000006 - create_recovery_addresses ------------\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token)\n\n------------ 20200519101057000007 - create_recovery_addresses ------------\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);\n\n------------ 20200601101000000000 - create_messages ------------\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" TEXT;\n\n------------ 20200601101000000001 - create_messages ------------\n\n\n------------ 20200601101000000002 - create_messages ------------\n\n\n------------ 20200601101000000003 - create_messages ------------\n\n\n------------ 20200605111551000000 - messages ------------\nALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" TEXT\n\n------------ 20200605111551000001 - messages ------------\nALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" TEXT\n\n------------ 20200605111551000002 - messages ------------\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" TEXT;\n\n------------ 20200605111551000003 - messages ------------\n\n\n------------ 20200605111551000004 - messages ------------\n\n\n------------ 20200605111551000005 - messages ------------\n\n\n------------ 20200605111551000006 - messages ------------\n\n\n------------ 20200605111551000007 - messages ------------\n\n\n------------ 20200605111551000008 - messages ------------\n\n\n------------ 20200605111551000009 - messages ------------\n\n\n------------ 20200605111551000010 - messages ------------\n\n\n------------ 20200605111551000011 - messages ------------\n\n\n------------ 20200607165100000000 - settings ------------\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form'\n\n------------ 20200607165100000001 - settings ------------\nCREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)\n\n------------ 20200607165100000002 - settings ------------\nINSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state FROM \"selfservice_settings_requests\"\n\n------------ 20200607165100000003 - settings ------------\n\nDROP TABLE \"selfservice_settings_requests\"\n\n------------ 20200607165100000004 - settings ------------\nALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";\n\n------------ 20200705105359000000 - rename_identities_schema ------------\nALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";\n\n------------ 20200810141652000000 - flow_type ------------\nALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser'\n\n------------ 20200810141652000001 - flow_type ------------\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser'\n\n------------ 20200810141652000002 - flow_type ------------\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser'\n\n------------ 20200810141652000003 - flow_type ------------\nALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser'\n\n------------ 20200810141652000004 - flow_type ------------\nALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';\n\n------------ 20200810141652000005 - flow_type ------------\n\n\n------------ 20200810141652000006 - flow_type ------------\n\n\n------------ 20200810141652000007 - flow_type ------------\n\n\n------------ 20200810141652000008 - flow_type ------------\n\n\n------------ 20200810141652000009 - flow_type ------------\n\n\n------------ 20200810141652000010 - flow_type ------------\n\n\n------------ 20200810141652000011 - flow_type ------------\n\n\n------------ 20200810141652000012 - flow_type ------------\n\n\n------------ 20200810141652000013 - flow_type ------------\n\n\n------------ 20200810141652000014 - flow_type ------------\n\n\n------------ 20200810141652000015 - flow_type ------------\n\n\n------------ 20200810141652000016 - flow_type ------------\n\n\n------------ 20200810141652000017 - flow_type ------------\n\n\n------------ 20200810141652000018 - flow_type ------------\n\n\n------------ 20200810141652000019 - flow_type ------------\n\n\n------------ 20200810161022000000 - flow_rename ------------\nALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\"\n\n------------ 20200810161022000001 - flow_rename ------------\nALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\"\n\n------------ 20200810161022000002 - flow_rename ------------\nALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\"\n\n------------ 20200810161022000003 - flow_rename ------------\nALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\"\n\n------------ 20200810161022000004 - flow_rename ------------\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\"\n\n------------ 20200810161022000005 - flow_rename ------------\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\"\n\n------------ 20200810161022000006 - flow_rename ------------\nALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\"\n\n------------ 20200810161022000007 - flow_rename ------------\nALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\"\n\n------------ 20200810161022000008 - flow_rename ------------\nALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";\n\n------------ 20200810162450000000 - flow_fields_rename ------------\nALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\"\n\n------------ 20200810162450000001 - flow_fields_rename ------------\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\"\n\n------------ 20200810162450000002 - flow_fields_rename ------------\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\"\n\n------------ 20200810162450000003 - flow_fields_rename ------------\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";\n\n------------ 20200812124254000000 - add_session_token ------------\nDELETE FROM sessions\n\n------------ 20200812124254000001 - add_session_token ------------\nALTER TABLE \"sessions\" ADD COLUMN \"token\" TEXT\n\n------------ 20200812124254000002 - add_session_token ------------\nCREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)\n\n------------ 20200812124254000003 - add_session_token ------------\nINSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token FROM \"sessions\"\n\n------------ 20200812124254000004 - add_session_token ------------\nDROP TABLE \"sessions\"\n\n------------ 20200812124254000005 - add_session_token ------------\nALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\"\n\n------------ 20200812124254000006 - add_session_token ------------\nCREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token)\n\n------------ 20200812124254000007 - add_session_token ------------\nCREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);\n\n------------ 20200812160551000000 - add_session_revoke ------------\nALTER TABLE \"sessions\" ADD COLUMN \"active\" NUMERIC DEFAULT 'false';\n\n------------ 20200812160551000001 - add_session_revoke ------------\n\n\n------------ 20200812160551000002 - add_session_revoke ------------\n\n\n------------ 20200812160551000003 - add_session_revoke ------------\n\n\n------------ 20200812160551000004 - add_session_revoke ------------\n\n\n------------ 20200812160551000005 - add_session_revoke ------------\n\n\n------------ 20200812160551000006 - add_session_revoke ------------\n\n\n------------ 20200812160551000007 - add_session_revoke ------------\n\n\n------------ 20200830121710000000 - update_recovery_token ------------\nALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";\n\n------------ 20200830130642000000 - add_verification_methods ------------\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form';\n\n------------ 20200830130642000001 - add_verification_methods ------------\n\n\n------------ 20200830130642000002 - add_verification_methods ------------\n\n\n------------ 20200830130642000003 - add_verification_methods ------------\n\n\n------------ 20200830130642000004 - add_verification_methods ------------\n\n\n------------ 20200830130642000005 - add_verification_methods ------------\n\n\n------------ 20200830130642000006 - add_verification_methods ------------\n\n\n------------ 20200830130642000007 - add_verification_methods ------------\n\n\n------------ 20200830130642000008 - add_verification_methods ------------\n\n\n------------ 20200830130642000009 - add_verification_methods ------------\n\n\n------------ 20200830130642000010 - add_verification_methods ------------\n\n\n------------ 20200830130643000000 - add_verification_methods ------------\nUPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;\n\n------------ 20200830130644000000 - add_verification_methods ------------\nCREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_verification_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)\n\n------------ 20200830130644000001 - add_verification_methods ------------\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" TEXT;\n\n------------ 20200830130645000000 - add_verification_methods ------------\nINSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;\n\n------------ 20200830130646000000 - add_verification_methods ------------\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n)\n\n------------ 20200830130646000001 - add_verification_methods ------------\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\"\n\n------------ 20200830130646000002 - add_verification_methods ------------\n\nDROP TABLE \"selfservice_verification_flows\"\n\n------------ 20200830130646000003 - add_verification_methods ------------\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\"\n\n------------ 20200830130646000004 - add_verification_methods ------------\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n)\n\n------------ 20200830130646000005 - add_verification_methods ------------\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\"\n\n------------ 20200830130646000006 - add_verification_methods ------------\n\nDROP TABLE \"selfservice_verification_flows\"\n\n------------ 20200830130646000007 - add_verification_methods ------------\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\"\n\n------------ 20200830130646000008 - add_verification_methods ------------\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n)\n\n------------ 20200830130646000009 - add_verification_methods ------------\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\"\n\n------------ 20200830130646000010 - add_verification_methods ------------\n\nDROP TABLE \"selfservice_verification_flows\"\n\n------------ 20200830130646000011 - add_verification_methods ------------\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";\n\n------------ 20200830154602000000 - add_verification_token ------------\nCREATE TABLE \"identity_verification_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL,\n\"identity_verifiable_address_id\" char(36) NOT NULL,\n\"selfservice_verification_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON DELETE cascade\n)\n\n------------ 20200830154602000001 - add_verification_token ------------\nCREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token)\n\n------------ 20200830154602000002 - add_verification_token ------------\nCREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token)\n\n------------ 20200830154602000003 - add_verification_token ------------\nCREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id)\n\n------------ 20200830154602000004 - add_verification_token ------------\nCREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);\n\n------------ 20200830172221000000 - recovery_token_expires ------------\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00'\n\n------------ 20200830172221000001 - recovery_token_expires ------------\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00'\n\n------------ 20200830172221000002 - recovery_token_expires ------------\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\"\n\n------------ 20200830172221000003 - recovery_token_expires ------------\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\"\n\n------------ 20200830172221000004 - recovery_token_expires ------------\nCREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)\n\n------------ 20200830172221000005 - recovery_token_expires ------------\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token)\n\n------------ 20200830172221000006 - recovery_token_expires ------------\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token)\n\n------------ 20200830172221000007 - recovery_token_expires ------------\nINSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at FROM \"identity_recovery_tokens\"\n\n------------ 20200830172221000008 - recovery_token_expires ------------\nDROP TABLE \"identity_recovery_tokens\"\n\n------------ 20200830172221000009 - recovery_token_expires ------------\nALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";\n\n------------ 20200830172221000010 - recovery_token_expires ------------\n\n\n------------ 20200830172221000011 - recovery_token_expires ------------\n\n\n------------ 20200830172221000012 - recovery_token_expires ------------\n\n\n------------ 20200830172221000013 - recovery_token_expires ------------\n\n\n------------ 20200830172221000014 - recovery_token_expires ------------\n\n\n------------ 20200830172221000015 - recovery_token_expires ------------\n\n\n------------ 20200830172221000016 - recovery_token_expires ------------\n\n\n------------ 20200830172221000017 - recovery_token_expires ------------\n\n\n------------ 20200830172221000018 - recovery_token_expires ------------\n\n\n------------ 20200830172221000019 - recovery_token_expires ------------\n\n\n------------ 20200830172221000020 - recovery_token_expires ------------\n\n\n------------ 20200830172221000021 - recovery_token_expires ------------\n\n\n------------ 20200830172221000022 - recovery_token_expires ------------\n\n\n------------ 20200830172221000023 - recovery_token_expires ------------\n\n\n------------ 20200830172221000024 - recovery_token_expires ------------\n\n\n------------ 20200831110752000000 - identity_verifiable_address_remove_code ------------\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_code_uq_idx\"\n\n------------ 20200831110752000001 - identity_verifiable_address_remove_code ------------\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_code_idx\"\n\n------------ 20200831110752000002 - identity_verifiable_address_remove_code ------------\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\"\n\n------------ 20200831110752000003 - identity_verifiable_address_remove_code ------------\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\"\n\n------------ 20200831110752000004 - identity_verifiable_address_remove_code ------------\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)\n\n------------ 20200831110752000005 - identity_verifiable_address_remove_code ------------\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)\n\n------------ 20200831110752000006 - identity_verifiable_address_remove_code ------------\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)\n\n------------ 20200831110752000007 - identity_verifiable_address_remove_code ------------\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\"\n\n------------ 20200831110752000008 - identity_verifiable_address_remove_code ------------\n\nDROP TABLE \"identity_verifiable_addresses\"\n\n------------ 20200831110752000009 - identity_verifiable_address_remove_code ------------\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\"\n\n------------ 20200831110752000010 - identity_verifiable_address_remove_code ------------\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\"\n\n------------ 20200831110752000011 - identity_verifiable_address_remove_code ------------\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\"\n\n------------ 20200831110752000012 - identity_verifiable_address_remove_code ------------\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)\n\n------------ 20200831110752000013 - identity_verifiable_address_remove_code ------------\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)\n\n------------ 20200831110752000014 - identity_verifiable_address_remove_code ------------\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)\n\n------------ 20200831110752000015 - identity_verifiable_address_remove_code ------------\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\"\n\n------------ 20200831110752000016 - identity_verifiable_address_remove_code ------------\n\nDROP TABLE \"identity_verifiable_addresses\"\n\n------------ 20200831110752000017 - identity_verifiable_address_remove_code ------------\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";\n\n------------ 20200831110752000018 - identity_verifiable_address_remove_code ------------\n\n\n------------ 20200831110752000019 - identity_verifiable_address_remove_code ------------\n\n\n------------ 20200831110752000020 - identity_verifiable_address_remove_code ------------\n\n\n------------ 20200831110752000021 - identity_verifiable_address_remove_code ------------\n\n\n------------ 20201201161451000000 - credential_types_values ------------\nINSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password')\n\n------------ 20201201161451000001 - credential_types_values ------------\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');\n\n------------ SUCCESS ------------\nSuccessfully applied migrations!\n\nstderr: \n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-status_migrated.txt",
    "content": "stdout: Version\t\t\tName\t\t\t\t\tStatus\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\n20191100000002000000\trequests\t\t\t\tApplied\t\n20191100000002000001\trequests\t\t\t\tApplied\t\n20191100000002000002\trequests\t\t\t\tApplied\t\n20191100000002000003\trequests\t\t\t\tApplied\t\n20191100000002000004\trequests\t\t\t\tApplied\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tApplied\t\n20201201161451000000\tcredential_types_values\t\t\tApplied\t\n20201201161451000001\tcredential_types_values\t\t\tApplied\t\n\nstderr: \n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-status_pre.txt",
    "content": "stdout: Version\t\t\tName\t\t\t\t\tStatus\t\n20191100000001000000\tidentities\t\t\t\tPending\t\n20191100000001000001\tidentities\t\t\t\tPending\t\n20191100000001000002\tidentities\t\t\t\tPending\t\n20191100000001000003\tidentities\t\t\t\tPending\t\n20191100000001000004\tidentities\t\t\t\tPending\t\n20191100000001000005\tidentities\t\t\t\tPending\t\n20191100000002000000\trequests\t\t\t\tPending\t\n20191100000002000001\trequests\t\t\t\tPending\t\n20191100000002000002\trequests\t\t\t\tPending\t\n20191100000002000003\trequests\t\t\t\tPending\t\n20191100000002000004\trequests\t\t\t\tPending\t\n20191100000003000000\tsessions\t\t\t\tPending\t\n20191100000004000000\terrors\t\t\t\t\tPending\t\n20191100000006000000\tcourier\t\t\t\t\tPending\t\n20191100000007000000\terrors\t\t\t\t\tPending\t\n20191100000007000001\terrors\t\t\t\t\tPending\t\n20191100000007000002\terrors\t\t\t\t\tPending\t\n20191100000007000003\terrors\t\t\t\t\tPending\t\n20191100000008000000\tselfservice_verification\t\tPending\t\n20191100000008000001\tselfservice_verification\t\tPending\t\n20191100000008000002\tselfservice_verification\t\tPending\t\n20191100000008000003\tselfservice_verification\t\tPending\t\n20191100000008000004\tselfservice_verification\t\tPending\t\n20191100000008000005\tselfservice_verification\t\tPending\t\n20191100000010000000\terrors\t\t\t\t\tPending\t\n20191100000010000001\terrors\t\t\t\t\tPending\t\n20191100000010000002\terrors\t\t\t\t\tPending\t\n20191100000010000003\terrors\t\t\t\t\tPending\t\n20191100000010000004\terrors\t\t\t\t\tPending\t\n20191100000011000000\tcourier_body_type\t\t\tPending\t\n20191100000011000001\tcourier_body_type\t\t\tPending\t\n20191100000011000002\tcourier_body_type\t\t\tPending\t\n20191100000011000003\tcourier_body_type\t\t\tPending\t\n20191100000012000000\tlogin_request_forced\t\t\tPending\t\n20191100000012000001\tlogin_request_forced\t\t\tPending\t\n20191100000012000002\tlogin_request_forced\t\t\tPending\t\n20191100000012000003\tlogin_request_forced\t\t\tPending\t\n20200317160354000000\tcreate_profile_request_forms\t\tPending\t\n20200317160354000001\tcreate_profile_request_forms\t\tPending\t\n20200317160354000002\tcreate_profile_request_forms\t\tPending\t\n20200317160354000003\tcreate_profile_request_forms\t\tPending\t\n20200317160354000004\tcreate_profile_request_forms\t\tPending\t\n20200317160354000005\tcreate_profile_request_forms\t\tPending\t\n20200317160354000006\tcreate_profile_request_forms\t\tPending\t\n20200401183443000000\tcontinuity_containers\t\t\tPending\t\n20200402142539000000\trename_profile_flows\t\t\tPending\t\n20200402142539000001\trename_profile_flows\t\t\tPending\t\n20200402142539000002\trename_profile_flows\t\t\tPending\t\n20200519101057000000\tcreate_recovery_addresses\t\tPending\t\n20200519101057000001\tcreate_recovery_addresses\t\tPending\t\n20200519101057000002\tcreate_recovery_addresses\t\tPending\t\n20200519101057000003\tcreate_recovery_addresses\t\tPending\t\n20200519101057000004\tcreate_recovery_addresses\t\tPending\t\n20200519101057000005\tcreate_recovery_addresses\t\tPending\t\n20200519101057000006\tcreate_recovery_addresses\t\tPending\t\n20200519101057000007\tcreate_recovery_addresses\t\tPending\t\n20200601101000000000\tcreate_messages\t\t\t\tPending\t\n20200601101000000001\tcreate_messages\t\t\t\tPending\t\n20200601101000000002\tcreate_messages\t\t\t\tPending\t\n20200601101000000003\tcreate_messages\t\t\t\tPending\t\n20200605111551000000\tmessages\t\t\t\tPending\t\n20200605111551000001\tmessages\t\t\t\tPending\t\n20200605111551000002\tmessages\t\t\t\tPending\t\n20200605111551000003\tmessages\t\t\t\tPending\t\n20200605111551000004\tmessages\t\t\t\tPending\t\n20200605111551000005\tmessages\t\t\t\tPending\t\n20200605111551000006\tmessages\t\t\t\tPending\t\n20200605111551000007\tmessages\t\t\t\tPending\t\n20200605111551000008\tmessages\t\t\t\tPending\t\n20200605111551000009\tmessages\t\t\t\tPending\t\n20200605111551000010\tmessages\t\t\t\tPending\t\n20200605111551000011\tmessages\t\t\t\tPending\t\n20200607165100000000\tsettings\t\t\t\tPending\t\n20200607165100000001\tsettings\t\t\t\tPending\t\n20200607165100000002\tsettings\t\t\t\tPending\t\n20200607165100000003\tsettings\t\t\t\tPending\t\n20200607165100000004\tsettings\t\t\t\tPending\t\n20200705105359000000\trename_identities_schema\t\tPending\t\n20200810141652000000\tflow_type\t\t\t\tPending\t\n20200810141652000001\tflow_type\t\t\t\tPending\t\n20200810141652000002\tflow_type\t\t\t\tPending\t\n20200810141652000003\tflow_type\t\t\t\tPending\t\n20200810141652000004\tflow_type\t\t\t\tPending\t\n20200810141652000005\tflow_type\t\t\t\tPending\t\n20200810141652000006\tflow_type\t\t\t\tPending\t\n20200810141652000007\tflow_type\t\t\t\tPending\t\n20200810141652000008\tflow_type\t\t\t\tPending\t\n20200810141652000009\tflow_type\t\t\t\tPending\t\n20200810141652000010\tflow_type\t\t\t\tPending\t\n20200810141652000011\tflow_type\t\t\t\tPending\t\n20200810141652000012\tflow_type\t\t\t\tPending\t\n20200810141652000013\tflow_type\t\t\t\tPending\t\n20200810141652000014\tflow_type\t\t\t\tPending\t\n20200810141652000015\tflow_type\t\t\t\tPending\t\n20200810141652000016\tflow_type\t\t\t\tPending\t\n20200810141652000017\tflow_type\t\t\t\tPending\t\n20200810141652000018\tflow_type\t\t\t\tPending\t\n20200810141652000019\tflow_type\t\t\t\tPending\t\n20200810161022000000\tflow_rename\t\t\t\tPending\t\n20200810161022000001\tflow_rename\t\t\t\tPending\t\n20200810161022000002\tflow_rename\t\t\t\tPending\t\n20200810161022000003\tflow_rename\t\t\t\tPending\t\n20200810161022000004\tflow_rename\t\t\t\tPending\t\n20200810161022000005\tflow_rename\t\t\t\tPending\t\n20200810161022000006\tflow_rename\t\t\t\tPending\t\n20200810161022000007\tflow_rename\t\t\t\tPending\t\n20200810161022000008\tflow_rename\t\t\t\tPending\t\n20200810162450000000\tflow_fields_rename\t\t\tPending\t\n20200810162450000001\tflow_fields_rename\t\t\tPending\t\n20200810162450000002\tflow_fields_rename\t\t\tPending\t\n20200810162450000003\tflow_fields_rename\t\t\tPending\t\n20200812124254000000\tadd_session_token\t\t\tPending\t\n20200812124254000001\tadd_session_token\t\t\tPending\t\n20200812124254000002\tadd_session_token\t\t\tPending\t\n20200812124254000003\tadd_session_token\t\t\tPending\t\n20200812124254000004\tadd_session_token\t\t\tPending\t\n20200812124254000005\tadd_session_token\t\t\tPending\t\n20200812124254000006\tadd_session_token\t\t\tPending\t\n20200812124254000007\tadd_session_token\t\t\tPending\t\n20200812160551000000\tadd_session_revoke\t\t\tPending\t\n20200812160551000001\tadd_session_revoke\t\t\tPending\t\n20200812160551000002\tadd_session_revoke\t\t\tPending\t\n20200812160551000003\tadd_session_revoke\t\t\tPending\t\n20200812160551000004\tadd_session_revoke\t\t\tPending\t\n20200812160551000005\tadd_session_revoke\t\t\tPending\t\n20200812160551000006\tadd_session_revoke\t\t\tPending\t\n20200812160551000007\tadd_session_revoke\t\t\tPending\t\n20200830121710000000\tupdate_recovery_token\t\t\tPending\t\n20200830130642000000\tadd_verification_methods\t\tPending\t\n20200830130642000001\tadd_verification_methods\t\tPending\t\n20200830130642000002\tadd_verification_methods\t\tPending\t\n20200830130642000003\tadd_verification_methods\t\tPending\t\n20200830130642000004\tadd_verification_methods\t\tPending\t\n20200830130642000005\tadd_verification_methods\t\tPending\t\n20200830130642000006\tadd_verification_methods\t\tPending\t\n20200830130642000007\tadd_verification_methods\t\tPending\t\n20200830130642000008\tadd_verification_methods\t\tPending\t\n20200830130642000009\tadd_verification_methods\t\tPending\t\n20200830130642000010\tadd_verification_methods\t\tPending\t\n20200830130643000000\tadd_verification_methods\t\tPending\t\n20200830130644000000\tadd_verification_methods\t\tPending\t\n20200830130644000001\tadd_verification_methods\t\tPending\t\n20200830130645000000\tadd_verification_methods\t\tPending\t\n20200830130646000000\tadd_verification_methods\t\tPending\t\n20200830130646000001\tadd_verification_methods\t\tPending\t\n20200830130646000002\tadd_verification_methods\t\tPending\t\n20200830130646000003\tadd_verification_methods\t\tPending\t\n20200830130646000004\tadd_verification_methods\t\tPending\t\n20200830130646000005\tadd_verification_methods\t\tPending\t\n20200830130646000006\tadd_verification_methods\t\tPending\t\n20200830130646000007\tadd_verification_methods\t\tPending\t\n20200830130646000008\tadd_verification_methods\t\tPending\t\n20200830130646000009\tadd_verification_methods\t\tPending\t\n20200830130646000010\tadd_verification_methods\t\tPending\t\n20200830130646000011\tadd_verification_methods\t\tPending\t\n20200830154602000000\tadd_verification_token\t\t\tPending\t\n20200830154602000001\tadd_verification_token\t\t\tPending\t\n20200830154602000002\tadd_verification_token\t\t\tPending\t\n20200830154602000003\tadd_verification_token\t\t\tPending\t\n20200830154602000004\tadd_verification_token\t\t\tPending\t\n20200830172221000000\trecovery_token_expires\t\t\tPending\t\n20200830172221000001\trecovery_token_expires\t\t\tPending\t\n20200830172221000002\trecovery_token_expires\t\t\tPending\t\n20200830172221000003\trecovery_token_expires\t\t\tPending\t\n20200830172221000004\trecovery_token_expires\t\t\tPending\t\n20200830172221000005\trecovery_token_expires\t\t\tPending\t\n20200830172221000006\trecovery_token_expires\t\t\tPending\t\n20200830172221000007\trecovery_token_expires\t\t\tPending\t\n20200830172221000008\trecovery_token_expires\t\t\tPending\t\n20200830172221000009\trecovery_token_expires\t\t\tPending\t\n20200830172221000010\trecovery_token_expires\t\t\tPending\t\n20200830172221000011\trecovery_token_expires\t\t\tPending\t\n20200830172221000012\trecovery_token_expires\t\t\tPending\t\n20200830172221000013\trecovery_token_expires\t\t\tPending\t\n20200830172221000014\trecovery_token_expires\t\t\tPending\t\n20200830172221000015\trecovery_token_expires\t\t\tPending\t\n20200830172221000016\trecovery_token_expires\t\t\tPending\t\n20200830172221000017\trecovery_token_expires\t\t\tPending\t\n20200830172221000018\trecovery_token_expires\t\t\tPending\t\n20200830172221000019\trecovery_token_expires\t\t\tPending\t\n20200830172221000020\trecovery_token_expires\t\t\tPending\t\n20200830172221000021\trecovery_token_expires\t\t\tPending\t\n20200830172221000022\trecovery_token_expires\t\t\tPending\t\n20200830172221000023\trecovery_token_expires\t\t\tPending\t\n20200830172221000024\trecovery_token_expires\t\t\tPending\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tPending\t\n20201201161451000000\tcredential_types_values\t\t\tPending\t\n20201201161451000001\tcredential_types_values\t\t\tPending\t\n\nstderr: \n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-status_two_steps_rolled_back.txt",
    "content": "stdout: Version\t\t\tName\t\t\t\t\tStatus\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\n20191100000002000000\trequests\t\t\t\tApplied\t\n20191100000002000001\trequests\t\t\t\tApplied\t\n20191100000002000002\trequests\t\t\t\tApplied\t\n20191100000002000003\trequests\t\t\t\tApplied\t\n20191100000002000004\trequests\t\t\t\tApplied\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tPending\t\n20201201161451000000\tcredential_types_values\t\t\tPending\t\n20201201161451000001\tcredential_types_values\t\t\tPending\t\n\nstderr: \n"
  },
  {
    "path": "oryx/popx/.snapshots/TestMigrateSQLUp-status_two_versions_rolled_back.txt",
    "content": "stdout: Version\t\t\tName\t\t\t\t\tStatus\t\n20191100000001000000\tidentities\t\t\t\tApplied\t\n20191100000001000001\tidentities\t\t\t\tApplied\t\n20191100000001000002\tidentities\t\t\t\tApplied\t\n20191100000001000003\tidentities\t\t\t\tApplied\t\n20191100000001000004\tidentities\t\t\t\tApplied\t\n20191100000001000005\tidentities\t\t\t\tApplied\t\n20191100000002000000\trequests\t\t\t\tApplied\t\n20191100000002000001\trequests\t\t\t\tApplied\t\n20191100000002000002\trequests\t\t\t\tApplied\t\n20191100000002000003\trequests\t\t\t\tApplied\t\n20191100000002000004\trequests\t\t\t\tApplied\t\n20191100000003000000\tsessions\t\t\t\tApplied\t\n20191100000004000000\terrors\t\t\t\t\tApplied\t\n20191100000006000000\tcourier\t\t\t\t\tApplied\t\n20191100000007000000\terrors\t\t\t\t\tApplied\t\n20191100000007000001\terrors\t\t\t\t\tApplied\t\n20191100000007000002\terrors\t\t\t\t\tApplied\t\n20191100000007000003\terrors\t\t\t\t\tApplied\t\n20191100000008000000\tselfservice_verification\t\tApplied\t\n20191100000008000001\tselfservice_verification\t\tApplied\t\n20191100000008000002\tselfservice_verification\t\tApplied\t\n20191100000008000003\tselfservice_verification\t\tApplied\t\n20191100000008000004\tselfservice_verification\t\tApplied\t\n20191100000008000005\tselfservice_verification\t\tApplied\t\n20191100000010000000\terrors\t\t\t\t\tApplied\t\n20191100000010000001\terrors\t\t\t\t\tApplied\t\n20191100000010000002\terrors\t\t\t\t\tApplied\t\n20191100000010000003\terrors\t\t\t\t\tApplied\t\n20191100000010000004\terrors\t\t\t\t\tApplied\t\n20191100000011000000\tcourier_body_type\t\t\tApplied\t\n20191100000011000001\tcourier_body_type\t\t\tApplied\t\n20191100000011000002\tcourier_body_type\t\t\tApplied\t\n20191100000011000003\tcourier_body_type\t\t\tApplied\t\n20191100000012000000\tlogin_request_forced\t\t\tApplied\t\n20191100000012000001\tlogin_request_forced\t\t\tApplied\t\n20191100000012000002\tlogin_request_forced\t\t\tApplied\t\n20191100000012000003\tlogin_request_forced\t\t\tApplied\t\n20200317160354000000\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000001\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000002\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000003\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000004\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000005\tcreate_profile_request_forms\t\tApplied\t\n20200317160354000006\tcreate_profile_request_forms\t\tApplied\t\n20200401183443000000\tcontinuity_containers\t\t\tApplied\t\n20200402142539000000\trename_profile_flows\t\t\tApplied\t\n20200402142539000001\trename_profile_flows\t\t\tApplied\t\n20200402142539000002\trename_profile_flows\t\t\tApplied\t\n20200519101057000000\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000001\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000002\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000003\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000004\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000005\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000006\tcreate_recovery_addresses\t\tApplied\t\n20200519101057000007\tcreate_recovery_addresses\t\tApplied\t\n20200601101000000000\tcreate_messages\t\t\t\tApplied\t\n20200601101000000001\tcreate_messages\t\t\t\tApplied\t\n20200601101000000002\tcreate_messages\t\t\t\tApplied\t\n20200601101000000003\tcreate_messages\t\t\t\tApplied\t\n20200605111551000000\tmessages\t\t\t\tApplied\t\n20200605111551000001\tmessages\t\t\t\tApplied\t\n20200605111551000002\tmessages\t\t\t\tApplied\t\n20200605111551000003\tmessages\t\t\t\tApplied\t\n20200605111551000004\tmessages\t\t\t\tApplied\t\n20200605111551000005\tmessages\t\t\t\tApplied\t\n20200605111551000006\tmessages\t\t\t\tApplied\t\n20200605111551000007\tmessages\t\t\t\tApplied\t\n20200605111551000008\tmessages\t\t\t\tApplied\t\n20200605111551000009\tmessages\t\t\t\tApplied\t\n20200605111551000010\tmessages\t\t\t\tApplied\t\n20200605111551000011\tmessages\t\t\t\tApplied\t\n20200607165100000000\tsettings\t\t\t\tApplied\t\n20200607165100000001\tsettings\t\t\t\tApplied\t\n20200607165100000002\tsettings\t\t\t\tApplied\t\n20200607165100000003\tsettings\t\t\t\tApplied\t\n20200607165100000004\tsettings\t\t\t\tApplied\t\n20200705105359000000\trename_identities_schema\t\tApplied\t\n20200810141652000000\tflow_type\t\t\t\tApplied\t\n20200810141652000001\tflow_type\t\t\t\tApplied\t\n20200810141652000002\tflow_type\t\t\t\tApplied\t\n20200810141652000003\tflow_type\t\t\t\tApplied\t\n20200810141652000004\tflow_type\t\t\t\tApplied\t\n20200810141652000005\tflow_type\t\t\t\tApplied\t\n20200810141652000006\tflow_type\t\t\t\tApplied\t\n20200810141652000007\tflow_type\t\t\t\tApplied\t\n20200810141652000008\tflow_type\t\t\t\tApplied\t\n20200810141652000009\tflow_type\t\t\t\tApplied\t\n20200810141652000010\tflow_type\t\t\t\tApplied\t\n20200810141652000011\tflow_type\t\t\t\tApplied\t\n20200810141652000012\tflow_type\t\t\t\tApplied\t\n20200810141652000013\tflow_type\t\t\t\tApplied\t\n20200810141652000014\tflow_type\t\t\t\tApplied\t\n20200810141652000015\tflow_type\t\t\t\tApplied\t\n20200810141652000016\tflow_type\t\t\t\tApplied\t\n20200810141652000017\tflow_type\t\t\t\tApplied\t\n20200810141652000018\tflow_type\t\t\t\tApplied\t\n20200810141652000019\tflow_type\t\t\t\tApplied\t\n20200810161022000000\tflow_rename\t\t\t\tApplied\t\n20200810161022000001\tflow_rename\t\t\t\tApplied\t\n20200810161022000002\tflow_rename\t\t\t\tApplied\t\n20200810161022000003\tflow_rename\t\t\t\tApplied\t\n20200810161022000004\tflow_rename\t\t\t\tApplied\t\n20200810161022000005\tflow_rename\t\t\t\tApplied\t\n20200810161022000006\tflow_rename\t\t\t\tApplied\t\n20200810161022000007\tflow_rename\t\t\t\tApplied\t\n20200810161022000008\tflow_rename\t\t\t\tApplied\t\n20200810162450000000\tflow_fields_rename\t\t\tApplied\t\n20200810162450000001\tflow_fields_rename\t\t\tApplied\t\n20200810162450000002\tflow_fields_rename\t\t\tApplied\t\n20200810162450000003\tflow_fields_rename\t\t\tApplied\t\n20200812124254000000\tadd_session_token\t\t\tApplied\t\n20200812124254000001\tadd_session_token\t\t\tApplied\t\n20200812124254000002\tadd_session_token\t\t\tApplied\t\n20200812124254000003\tadd_session_token\t\t\tApplied\t\n20200812124254000004\tadd_session_token\t\t\tApplied\t\n20200812124254000005\tadd_session_token\t\t\tApplied\t\n20200812124254000006\tadd_session_token\t\t\tApplied\t\n20200812124254000007\tadd_session_token\t\t\tApplied\t\n20200812160551000000\tadd_session_revoke\t\t\tApplied\t\n20200812160551000001\tadd_session_revoke\t\t\tApplied\t\n20200812160551000002\tadd_session_revoke\t\t\tApplied\t\n20200812160551000003\tadd_session_revoke\t\t\tApplied\t\n20200812160551000004\tadd_session_revoke\t\t\tApplied\t\n20200812160551000005\tadd_session_revoke\t\t\tApplied\t\n20200812160551000006\tadd_session_revoke\t\t\tApplied\t\n20200812160551000007\tadd_session_revoke\t\t\tApplied\t\n20200830121710000000\tupdate_recovery_token\t\t\tApplied\t\n20200830130642000000\tadd_verification_methods\t\tApplied\t\n20200830130642000001\tadd_verification_methods\t\tApplied\t\n20200830130642000002\tadd_verification_methods\t\tApplied\t\n20200830130642000003\tadd_verification_methods\t\tApplied\t\n20200830130642000004\tadd_verification_methods\t\tApplied\t\n20200830130642000005\tadd_verification_methods\t\tApplied\t\n20200830130642000006\tadd_verification_methods\t\tApplied\t\n20200830130642000007\tadd_verification_methods\t\tApplied\t\n20200830130642000008\tadd_verification_methods\t\tApplied\t\n20200830130642000009\tadd_verification_methods\t\tApplied\t\n20200830130642000010\tadd_verification_methods\t\tApplied\t\n20200830130643000000\tadd_verification_methods\t\tApplied\t\n20200830130644000000\tadd_verification_methods\t\tApplied\t\n20200830130644000001\tadd_verification_methods\t\tApplied\t\n20200830130645000000\tadd_verification_methods\t\tApplied\t\n20200830130646000000\tadd_verification_methods\t\tApplied\t\n20200830130646000001\tadd_verification_methods\t\tApplied\t\n20200830130646000002\tadd_verification_methods\t\tApplied\t\n20200830130646000003\tadd_verification_methods\t\tApplied\t\n20200830130646000004\tadd_verification_methods\t\tApplied\t\n20200830130646000005\tadd_verification_methods\t\tApplied\t\n20200830130646000006\tadd_verification_methods\t\tApplied\t\n20200830130646000007\tadd_verification_methods\t\tApplied\t\n20200830130646000008\tadd_verification_methods\t\tApplied\t\n20200830130646000009\tadd_verification_methods\t\tApplied\t\n20200830130646000010\tadd_verification_methods\t\tApplied\t\n20200830130646000011\tadd_verification_methods\t\tApplied\t\n20200830154602000000\tadd_verification_token\t\t\tApplied\t\n20200830154602000001\tadd_verification_token\t\t\tApplied\t\n20200830154602000002\tadd_verification_token\t\t\tApplied\t\n20200830154602000003\tadd_verification_token\t\t\tApplied\t\n20200830154602000004\tadd_verification_token\t\t\tApplied\t\n20200830172221000000\trecovery_token_expires\t\t\tApplied\t\n20200830172221000001\trecovery_token_expires\t\t\tApplied\t\n20200830172221000002\trecovery_token_expires\t\t\tApplied\t\n20200830172221000003\trecovery_token_expires\t\t\tApplied\t\n20200830172221000004\trecovery_token_expires\t\t\tApplied\t\n20200830172221000005\trecovery_token_expires\t\t\tApplied\t\n20200830172221000006\trecovery_token_expires\t\t\tApplied\t\n20200830172221000007\trecovery_token_expires\t\t\tApplied\t\n20200830172221000008\trecovery_token_expires\t\t\tApplied\t\n20200830172221000009\trecovery_token_expires\t\t\tApplied\t\n20200830172221000010\trecovery_token_expires\t\t\tApplied\t\n20200830172221000011\trecovery_token_expires\t\t\tApplied\t\n20200830172221000012\trecovery_token_expires\t\t\tApplied\t\n20200830172221000013\trecovery_token_expires\t\t\tApplied\t\n20200830172221000014\trecovery_token_expires\t\t\tApplied\t\n20200830172221000015\trecovery_token_expires\t\t\tApplied\t\n20200830172221000016\trecovery_token_expires\t\t\tApplied\t\n20200830172221000017\trecovery_token_expires\t\t\tApplied\t\n20200830172221000018\trecovery_token_expires\t\t\tApplied\t\n20200830172221000019\trecovery_token_expires\t\t\tApplied\t\n20200830172221000020\trecovery_token_expires\t\t\tApplied\t\n20200830172221000021\trecovery_token_expires\t\t\tApplied\t\n20200830172221000022\trecovery_token_expires\t\t\tApplied\t\n20200830172221000023\trecovery_token_expires\t\t\tApplied\t\n20200830172221000024\trecovery_token_expires\t\t\tApplied\t\n20200831110752000000\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000001\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000002\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000003\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000004\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000005\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000006\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000007\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000008\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000009\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000010\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000011\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000012\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000013\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000014\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000015\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000016\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000017\tidentity_verifiable_address_remove_code\tApplied\t\n20200831110752000018\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000019\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000020\tidentity_verifiable_address_remove_code\tPending\t\n20200831110752000021\tidentity_verifiable_address_remove_code\tPending\t\n20201201161451000000\tcredential_types_values\t\t\tPending\t\n20201201161451000001\tcredential_types_values\t\t\tPending\t\n\nstderr: \n"
  },
  {
    "path": "oryx/popx/cmd.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/errorsx\"\n\t\"github.com/ory/x/flagx\"\n)\n\ntype MigrationProvider interface {\n\tMigrationStatus(context.Context) (MigrationStatuses, error)\n\tMigrateUp(context.Context) error\n\tMigrateDown(context.Context, int) error\n}\n\ntype MigrationPreparer interface {\n\tPrepareMigration(context.Context) error\n}\n\nfunc RegisterMigrateSQLUpFlags(cmd *cobra.Command) *cobra.Command {\n\tcmd.Flags().BoolP(\"yes\", \"y\", false, \"If set all confirmation requests are accepted without user interaction.\")\n\treturn cmd\n}\n\nfunc NewMigrateSQLUpCmd(runE func(cmd *cobra.Command, args []string) error) *cobra.Command {\n\treturn RegisterMigrateSQLUpFlags(&cobra.Command{\n\t\tUse:   \"up [database_url]\",\n\t\tArgs:  cobra.RangeArgs(0, 1),\n\t\tShort: \"Apply all pending SQL migrations\",\n\t\tLong: `This command applies all pending SQL migrations for Ory {{ title .Root.Name }}.\n\n:::warning\n\nBefore running this command, create a backup of your database. This command can be destructive as it may apply changes that cannot be easily reverted. Run this command close to the SQL instance (same VPC / same machine).\n\n:::\n\nIt is recommended to review the migrations before running them. You can do this by running the command without the --yes flag:\n\n\tDSN=... {{ .CommandPath }} -e`,\n\t\tExample: `Apply all pending migrations:\n\tDSN=... {{ .CommandPath }} -e\n\nApply all pending migrations:\n\tDSN=... {{ .CommandPath }} -e --yes`,\n\t\tRunE: runE,\n\t})\n}\n\nfunc MigrateSQLUp(cmd *cobra.Command, p MigrationProvider) (err error) {\n\t// convert migration tables\n\tif prep, ok := p.(MigrationPreparer); ok {\n\t\tif err := prep.PrepareMigration(cmd.Context()); err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not convert the migration table:\\n%+v\\n\", err)\n\t\t\treturn cmdx.FailSilently(cmd)\n\t\t}\n\t}\n\n\t// print migration status\n\t_, _ = fmt.Fprintln(cmd.OutOrStdout(), \"The migration plan is as follows:\")\n\n\t// print migration status\n\tstatus, err := p.MigrationStatus(cmd.Context())\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not get the migration status:\\n%+v\\n\", errorsx.WithStack(err))\n\t\treturn cmdx.FailSilently(cmd)\n\t}\n\tcmdx.PrintTable(cmd, status)\n\n\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"\\nThe SQL statements to be executed from top to bottom are:\\n\\n\")\n\tfor i := range status {\n\t\tif status[i].State == Pending {\n\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"------------ %s - %s ------------\\n\", status[i].Version, status[i].Name)\n\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"%s\\n\\n\", status[i].ContentUp)\n\t\t}\n\t}\n\n\tif !flagx.MustGetBool(cmd, \"yes\") {\n\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"To skip the next question use flag --yes (at your own risk).\")\n\t\tif !cmdx.AskForConfirmation(\"Do you wish to execute this migration plan?\", cmd.InOrStdin(), cmd.OutOrStdout()) {\n\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"------------ WARNING ------------\\n\")\n\t\t\t_, _ = fmt.Fprintln(cmd.OutOrStdout(), \"Migration aborted.\")\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// apply migrations\n\tif err := p.MigrateUp(cmd.Context()); err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"------------ ERROR ------------\\n\")\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not apply migrations:\\n%+v\\n\", errorsx.WithStack(err))\n\t\treturn cmdx.FailSilently(cmd)\n\t}\n\n\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"------------ SUCCESS ------------\\n\")\n\t_, _ = fmt.Fprintln(cmd.OutOrStdout(), \"Successfully applied migrations!\")\n\treturn nil\n}\n\nfunc RegisterMigrateSQLDownFlags(cmd *cobra.Command) *cobra.Command {\n\tcmd.Flags().BoolP(\"yes\", \"y\", false, \"If set all confirmation requests are accepted without user interaction.\")\n\tcmd.Flags().Int(\"steps\", 0, \"The number of migrations to roll back.\")\n\treturn cmd\n}\n\nfunc NewMigrateSQLDownCmd(runE func(cmd *cobra.Command, args []string) error) *cobra.Command {\n\treturn RegisterMigrateSQLDownFlags(&cobra.Command{\n\t\tUse:   \"down [database_url]\",\n\t\tArgs:  cobra.RangeArgs(0, 1),\n\t\tShort: \"Rollback the last applied SQL migrations\",\n\t\tLong: `This command rolls back the last applied SQL migrations for Ory {{ title .Root.Name }}.\n\n:::warning\n\nBefore running this command, create a backup of your database. This command can be destructive as it may revert changes made by previous migrations. Run this command close to the SQL instance (same VPC / same machine).\n\n:::\n\nIt is recommended to review the migrations before running them. You can do this by running the command without the --yes flag:\n\n\tDSN=... {{ .CommandPath }} -e`,\n\t\tExample: `See the current migration status:\n\tDSN=... {{ .CommandPath }} -e\n\nRollback the last 10 migrations:\n\t{{ .CommandPath }} $DSN --steps 10\n\nRollback the last 10 migrations without confirmation:\n\tDSN=... {{ .CommandPath }} -e --yes --steps 10`,\n\t\tRunE: runE,\n\t})\n}\n\nfunc MigrateSQLDown(cmd *cobra.Command, p MigrationProvider) (err error) {\n\tsteps := flagx.MustGetInt(cmd, \"steps\")\n\tif steps < 0 {\n\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"Flag --steps must be larger than 0.\")\n\t\treturn cmdx.FailSilently(cmd)\n\t}\n\n\t// convert migration tables\n\tif prep, ok := p.(MigrationPreparer); ok {\n\t\tif err := prep.PrepareMigration(cmd.Context()); err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not convert the migration table:\\n%+v\\n\", err)\n\t\t\treturn cmdx.FailSilently(cmd)\n\t\t}\n\t}\n\n\tstatus, err := p.MigrationStatus(cmd.Context())\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not get the migration status:\\n%+v\\n\", errorsx.WithStack(err))\n\t\treturn cmdx.FailSilently(cmd)\n\t}\n\n\t// Now we need to rollback the last `steps` migrations that have a status of \"Applied\":\n\tvar count int\n\tvar rollingBack int\n\tfor i := len(status) - 1; i >= 0; i-- {\n\t\tif status[i].State == Applied {\n\t\t\tcount++\n\t\t\tif steps > 0 && count <= steps {\n\t\t\t\tstatus[i].State = \"Rollback\"\n\t\t\t\trollingBack++\n\t\t\t}\n\t\t}\n\t}\n\n\t// print migration status\n\t_, _ = fmt.Fprintln(cmd.OutOrStdout(), \"The migration plan is as follows:\")\n\tcmdx.PrintTable(cmd, status)\n\n\tif rollingBack < 1 {\n\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"\")\n\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"There are apparently no migrations to roll back.\")\n\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"Please provide the --steps argument with a value larger than 0.\")\n\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"\")\n\t\treturn cmdx.FailSilently(cmd)\n\t}\n\n\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"\\nThe SQL statements to be executed from top to bottom are:\\n\\n\")\n\n\tfor i := len(status) - 1; i >= 0; i-- {\n\t\tif status[i].State == \"Rollback\" {\n\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"------------ %s - %s ------------\\n\", status[i].Version, status[i].Name)\n\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"%s\\n\\n\", status[i].ContentDown)\n\t\t}\n\t}\n\n\tif !flagx.MustGetBool(cmd, \"yes\") {\n\t\t_, _ = fmt.Fprintln(cmd.ErrOrStderr(), \"To skip the next question use flag --yes (at your own risk).\")\n\t\tif !cmdx.AskForConfirmation(\"Do you wish to execute this migration plan?\", cmd.InOrStdin(), cmd.OutOrStdout()) {\n\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"------------ WARNING ------------\\n\")\n\t\t\t_, _ = fmt.Fprintln(cmd.OutOrStdout(), \"Migration aborted.\")\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// apply migrations\n\tif err := p.MigrateDown(cmd.Context(), rollingBack); err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"------------ ERROR ------------\\n\")\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not apply migrations:\\n%+v\\n\", errorsx.WithStack(err))\n\t\treturn cmdx.FailSilently(cmd)\n\t}\n\n\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"------------ SUCCESS ------------\\n\")\n\t_, _ = fmt.Fprintln(cmd.OutOrStdout(), \"Successfully applied migrations!\")\n\treturn nil\n}\n\nfunc RegisterMigrateStatusFlags(cmd *cobra.Command) *cobra.Command {\n\tcmdx.RegisterFormatFlags(cmd.PersistentFlags())\n\tcmd.Flags().BoolP(\"read-from-env\", \"e\", false, \"If set, reads the database connection string from the environment variable DSN or config file key dsn.\")\n\tcmd.Flags().Bool(\"block\", false, \"Block until all migrations have been applied\")\n\treturn cmd\n}\n\nfunc NewMigrateSQLStatusCmd(runE func(cmd *cobra.Command, args []string) error) *cobra.Command {\n\treturn RegisterMigrateStatusFlags(&cobra.Command{\n\t\tUse:   \"status [database_url]\",\n\t\tShort: \"Display the current migration status\",\n\t\tLong: `This command shows the current migration status for Ory {{ title .Root.Name }}.\n\nYou can use this command to check which migrations have been applied and which are pending.\n\nTo block until all migrations are applied, use the --block flag:\n\n\tDSN=... {{ .CommandPath }} -e --block`,\n\t\tExample: `See the current migration status:\n\tDSN=... {{ .CommandPath }} -e\n\nBlock until all migrations are applied:\n\tDSN=... {{ .CommandPath }} -e --block`,\n\t\tRunE: runE,\n\t})\n}\n\nfunc MigrateStatus(cmd *cobra.Command, p MigrationProvider) (err error) {\n\tblock := flagx.MustGetBool(cmd, \"block\")\n\tctx := cmd.Context()\n\ts, err := p.MigrationStatus(ctx)\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not get migration status: %+v\\n\", err)\n\t\treturn cmdx.FailSilently(cmd)\n\t}\n\n\tfor block && s.HasPending() {\n\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \"Waiting for migrations to finish...\\n\")\n\t\tfor _, m := range s {\n\t\t\tif m.State == Pending {\n\t\t\t\t_, _ = fmt.Fprintf(cmd.OutOrStdout(), \" - %s\\n\", m.Name)\n\t\t\t}\n\t\t}\n\t\ttime.Sleep(time.Second)\n\t\ts, err = p.MigrationStatus(ctx)\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(cmd.ErrOrStderr(), \"Could not get migration status: %+v\\n\", err)\n\t\t\treturn cmdx.FailSilently(cmd)\n\t\t}\n\t}\n\n\tcmdx.PrintTable(cmd, s)\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/popx/db_columns.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ory/pop/v6\"\n)\n\nfunc DBColumns[T any](quoter Quoter) string {\n\treturn (&pop.Model{Value: new(T)}).Columns().QuotedString(quoter)\n}\n\n// IndexHint returns the table name including the index hint, if the database\n// supports it.\nfunc IndexHint(conn *pop.Connection, table string, index string) string {\n\tif conn.Dialect.Name() == \"cockroach\" {\n\t\treturn table + \"@\" + index\n\t}\n\treturn table\n}\n\nfunc DBColumnsExcluding[T any](quoter Quoter, exclude ...string) string {\n\tcols := (&pop.Model{Value: new(T)}).Columns()\n\tfor _, e := range exclude {\n\t\tcols.Remove(e)\n\t}\n\treturn cols.QuotedString(quoter)\n}\n\ntype (\n\tAliasQuoter struct {\n\t\tAlias  string\n\t\tQuoter Quoter\n\t}\n\tQuoter interface {\n\t\tQuote(key string) string\n\t}\n)\n\nfunc (pq *AliasQuoter) Quote(key string) string {\n\treturn fmt.Sprintf(\"%s.%s\", pq.Quoter.Quote(pq.Alias), pq.Quoter.Quote(key))\n}\n"
  },
  {
    "path": "oryx/popx/loggers.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/pop/v6/logging\"\n)\n\nfunc formatter(lvl logging.Level, s string, args ...interface{}) string {\n\tif pop.Debug == false {\n\t\treturn \"\"\n\t}\n\n\tif lvl == logging.SQL {\n\t\tif len(args) > 0 {\n\t\t\txargs := make([]string, len(args))\n\t\t\tfor i, a := range args {\n\t\t\t\tswitch a.(type) {\n\t\t\t\tcase string:\n\t\t\t\t\txargs[i] = fmt.Sprintf(\"%q\", a)\n\t\t\t\tdefault:\n\t\t\t\t\txargs[i] = fmt.Sprintf(\"%v\", a)\n\t\t\t\t}\n\t\t\t}\n\t\t\ts = fmt.Sprintf(\"%s - %s | %s\", lvl, s, xargs)\n\t\t} else {\n\t\t\ts = fmt.Sprintf(\"%s - %s\", lvl, s)\n\t\t}\n\t} else {\n\t\ts = fmt.Sprintf(s, args...)\n\t\ts = fmt.Sprintf(\"%s - %s\", lvl, s)\n\t}\n\treturn s\n}\n\nfunc TestingLogger(t testing.TB) func(lvl logging.Level, s string, args ...interface{}) {\n\treturn func(lvl logging.Level, s string, args ...interface{}) {\n\t\tif line := formatter(lvl, s, args...); len(line) > 0 {\n\t\t\tt.Log(line)\n\t\t}\n\t}\n}\n\nfunc NullLogger() func(lvl logging.Level, s string, args ...interface{}) {\n\treturn func(lvl logging.Level, s string, args ...interface{}) {\n\t\t// do nothing\n\t}\n}\n"
  },
  {
    "path": "oryx/popx/match.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/pop/v6\"\n)\n\nvar MigrationFileRegexp = regexp.MustCompile(\n\t`^(\\d+)_([^.]+)(\\.[a-z0-9]+)?(\\.autocommit)?\\.(up|down)\\.(sql)$`,\n)\n\nconst (\n\t// Human-readable constants for the regex capture groups\n\tversionIdx = iota + 1\n\tnameIdx\n\tdbTypeIdx\n\tautocommitIdx\n\tdirectionIdx\n\ttypeIdx\n)\n\n// match holds the information parsed from a migration filename.\ntype match struct {\n\tVersion    string\n\tName       string\n\tDBType     string\n\tDirection  string\n\tType       string\n\tAutocommit bool\n}\n\n// parseMigrationFilename parses a migration filename.\nfunc parseMigrationFilename(filename string) (*match, error) {\n\tmatches := MigrationFileRegexp.FindAllStringSubmatch(filename, -1)\n\tif len(matches) == 0 {\n\t\treturn nil, nil\n\t}\n\tm := matches[0]\n\n\tvar (\n\t\tautocommit bool\n\t\tdbType     string\n\t)\n\n\tif m[dbTypeIdx] == \".autocommit\" {\n\t\t// A special case where autocommit group moves forward to the 3rd index.\n\t\tautocommit = true\n\t\tdbType = \"all\"\n\t} else if m[dbTypeIdx] == \"\" {\n\t\tdbType = \"all\"\n\t} else {\n\t\tdbType = pop.CanonicalDialect(m[dbTypeIdx][1:])\n\t\tif !pop.DialectSupported(dbType) {\n\t\t\treturn nil, errors.Errorf(\"unsupported dialect %s\", dbType)\n\t\t}\n\t}\n\n\tif m[typeIdx] == \"fizz\" && dbType != \"all\" {\n\t\treturn nil, errors.Errorf(\"invalid database type %q, expected \\\"all\\\" because fizz is database type independent\", dbType)\n\t}\n\n\tif m[autocommitIdx] == \".autocommit\" {\n\t\tautocommit = true\n\t} else if m[autocommitIdx] != \"\" {\n\t\treturn nil, errors.Errorf(\"invalid autocommit flag %q\", m[autocommitIdx])\n\t}\n\n\treturn &match{\n\t\tVersion:    m[versionIdx],\n\t\tName:       m[nameIdx],\n\t\tDBType:     dbType,\n\t\tAutocommit: autocommit,\n\t\tDirection:  m[directionIdx],\n\t\tType:       m[typeIdx],\n\t}, nil\n}\n"
  },
  {
    "path": "oryx/popx/migration_box.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"path\"\n\t\"regexp\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/pop/v6\"\n\n\t\"github.com/ory/x/logrusx\"\n)\n\ntype (\n\t// MigrationBox is a embed migration box.\n\tMigrationBox struct {\n\t\tc                   *pop.Connection\n\t\tmigrationsUp        Migrations\n\t\tmigrationsDown      Migrations\n\t\tperMigrationTimeout time.Duration\n\t\tl                   *logrusx.Logger\n\t\tmigrationContent    MigrationContent\n\t}\n\tMigrationContent   func(mf Migration, c *pop.Connection, r []byte, usingTemplate bool) (string, error)\n\tMigrationBoxOption func(*MigrationBox)\n)\n\nfunc WithTemplateValues(v map[string]interface{}) MigrationBoxOption {\n\treturn func(m *MigrationBox) {\n\t\tm.migrationContent = ParameterizedMigrationContent(v)\n\t}\n}\n\nfunc WithMigrationContentMiddleware(middleware func(content string, err error) (string, error)) MigrationBoxOption {\n\treturn func(m *MigrationBox) {\n\t\tprev := m.migrationContent\n\t\tm.migrationContent = func(mf Migration, c *pop.Connection, r []byte, usingTemplate bool) (string, error) {\n\t\t\treturn middleware(prev(mf, c, r, usingTemplate))\n\t\t}\n\t}\n}\n\n// WithGoMigrations adds migrations that have a custom migration runner.\n// TEST THEM THOROUGHLY!\n// It will be very hard to fix a buggy migration.\nfunc WithGoMigrations(migrations Migrations) MigrationBoxOption {\n\treturn func(mb *MigrationBox) {\n\t\tfor _, m := range migrations {\n\t\t\tswitch m.Direction {\n\t\t\tcase \"up\":\n\t\t\t\tmb.migrationsUp = append(mb.migrationsUp, m)\n\t\t\tcase \"down\":\n\t\t\t\tmb.migrationsDown = append(mb.migrationsDown, m)\n\t\t\tdefault:\n\t\t\t\tpanic(fmt.Sprintf(\"unknown migration direction %q for %q\", m.Direction, m.Version))\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc WithPerMigrationTimeout(timeout time.Duration) MigrationBoxOption {\n\treturn func(m *MigrationBox) {\n\t\tm.perMigrationTimeout = timeout\n\t}\n}\n\nvar testdataPattern = regexp.MustCompile(`^(\\d+)_testdata(|\\.[a-zA-Z0-9]+).sql$`)\n\n// WithTestdata adds testdata to the migration box.\nfunc WithTestdata(t *testing.T, testdata fs.FS) MigrationBoxOption {\n\treturn func(m *MigrationBox) {\n\t\trequire.NoError(t, fs.WalkDir(testdata, \".\", func(path string, info fs.DirEntry, err error) error {\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\t\t\tif !info.Type().IsRegular() {\n\t\t\t\tt.Logf(\"skipping testdata entry that is not a file: %s\", path)\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tmatch := testdataPattern.FindStringSubmatch(info.Name())\n\t\t\tif len(match) != 2 && len(match) != 3 {\n\t\t\t\tt.Logf(`WARNING! Found a test migration which does not match the test data pattern: %s`, info.Name())\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tversion := match[1]\n\t\t\tflavor := \"all\"\n\t\t\tif len(match) == 3 && len(match[2]) > 0 {\n\t\t\t\tflavor = pop.CanonicalDialect(strings.TrimPrefix(match[2], \".\"))\n\t\t\t}\n\n\t\t\t// t.Logf(\"Found test migration \\\"%s\\\" (%s, %+v): %s\", flavor, match, err, info.Name())\n\n\t\t\tm.migrationsUp = append(m.migrationsUp, Migration{\n\t\t\t\tVersion:   version + \"9\", // run testdata after version\n\t\t\t\tPath:      path,\n\t\t\t\tName:      info.Name(),\n\t\t\t\tDBType:    flavor,\n\t\t\t\tDirection: \"up\",\n\t\t\t\tType:      \"sql\",\n\t\t\t\tRunner: func(m Migration, c *pop.Connection) error {\n\t\t\t\t\tb, err := fs.ReadFile(testdata, m.Path)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t\t}\n\t\t\t\t\tif isMigrationEmpty(string(b)) {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t\t_, err = c.Store.SQLDB().Exec(string(b))\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tm.migrationsDown = append(m.migrationsDown, Migration{\n\t\t\t\tVersion:   version + \"9\", // run testdata after version\n\t\t\t\tPath:      path,\n\t\t\t\tName:      info.Name(),\n\t\t\t\tDBType:    flavor,\n\t\t\t\tDirection: \"down\",\n\t\t\t\tType:      \"sql\",\n\t\t\t\tRunner:    func(m Migration, _ *pop.Connection) error { return nil },\n\t\t\t})\n\n\t\t\treturn nil\n\t\t}))\n\t}\n}\n\nvar emptySQLReplace = regexp.MustCompile(`(?m)^(\\s*--.*|\\s*)$`)\n\nfunc isMigrationEmpty(content string) bool {\n\treturn len(strings.ReplaceAll(emptySQLReplace.ReplaceAllString(content, \"\"), \"\\n\", \"\")) == 0\n}\n\ntype queryExecutor interface {\n\tExec(query string, args ...any) (sql.Result, error)\n}\n\n// NewMigrationBox creates a new migration box.\nfunc NewMigrationBox(dir fs.FS, c *pop.Connection, l *logrusx.Logger, opts ...MigrationBoxOption) (*MigrationBox, error) {\n\tmb := &MigrationBox{\n\t\tc:                c,\n\t\tl:                l,\n\t\tmigrationContent: ParameterizedMigrationContent(nil),\n\t}\n\n\tfor _, o := range opts {\n\t\to(mb)\n\t}\n\n\ttxRunner := func(b []byte) func(Migration, *pop.Connection) error {\n\t\treturn func(mf Migration, c *pop.Connection) error {\n\t\t\tcontent, err := mb.migrationContent(mf, c, b, true)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"error processing %s\", mf.Path)\n\t\t\t}\n\t\t\tif isMigrationEmpty(content) {\n\t\t\t\tl.WithField(\"migration\", mf.Path).Trace(\"This is usually ok - ignoring migration because content is empty. This is ok!\")\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tvar q queryExecutor = c.Store.SQLDB()\n\t\t\tif c.TX != nil {\n\t\t\t\tq = c.TX\n\t\t\t}\n\n\t\t\tif _, err = q.Exec(content); err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"error executing %s, sql: %s\", mf.Path, content)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\n\terr := mb.findMigrations(dir, txRunner)\n\tif err != nil {\n\t\treturn mb, err\n\t}\n\n\tif err := mb.check(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn mb, nil\n}\n\nfunc (mb *MigrationBox) findMigrations(\n\tdir fs.FS,\n\trunner func([]byte) func(m Migration, c *pop.Connection) error,\n) error {\n\terr := fs.WalkDir(dir, \".\", func(p string, info fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tif !info.Type().IsRegular() {\n\t\t\tmb.l.Tracef(\"ignoring non file: %s\", info.Name())\n\t\t\treturn nil\n\t\t}\n\n\t\tif path.Ext(info.Name()) != \".sql\" {\n\t\t\tmb.l.Tracef(\"ignoring non SQL file: %s\", info.Name())\n\t\t\treturn nil\n\t\t}\n\n\t\tdetails, err := parseMigrationFilename(info.Name())\n\t\tif err != nil {\n\t\t\tif strings.HasPrefix(err.Error(), \"unsupported dialect\") {\n\t\t\t\tmb.l.Tracef(\"This is usually ok - ignoring migration file %s because dialect is not supported: %s\", info.Name(), err.Error())\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tif details == nil {\n\t\t\treturn errors.Errorf(\"Found a migration file that does not match the file pattern: filename=%s pattern=%s\", info.Name(), MigrationFileRegexp)\n\t\t}\n\n\t\tcontent, err := fs.ReadFile(dir, p)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tmf := Migration{\n\t\t\tPath:       p,\n\t\t\tVersion:    details.Version,\n\t\t\tName:       details.Name,\n\t\t\tDBType:     details.DBType,\n\t\t\tDirection:  details.Direction,\n\t\t\tType:       details.Type,\n\t\t\tContent:    string(content),\n\t\t\tAutocommit: details.Autocommit,\n\t\t}\n\n\t\tmf.Runner = runner(content)\n\n\t\tswitch details.Direction {\n\t\tcase \"up\":\n\t\t\tmb.migrationsUp = append(mb.migrationsUp, mf)\n\t\tcase \"down\":\n\t\t\tmb.migrationsDown = append(mb.migrationsDown, mf)\n\t\tdefault:\n\t\t\treturn errors.Errorf(\"unknown migration direction %q for %q\", details.Direction, info.Name())\n\t\t}\n\t\treturn nil\n\t})\n\n\t// Sort descending.\n\tsort.Sort(mb.migrationsDown)\n\tslices.Reverse(mb.migrationsDown)\n\n\t// Sort ascending.\n\tsort.Sort(mb.migrationsUp)\n\n\treturn errors.WithStack(err)\n}\n\n// hasDownMigrationWithVersion checks if there is a migration with the given\n// version.\nfunc (mb *MigrationBox) hasDownMigrationWithVersion(version string) bool {\n\tfor _, down := range mb.migrationsDown {\n\t\tif version == down.Version {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// check checks that every \"up\" migration has a corresponding \"down\" migration.\nfunc (mb *MigrationBox) check() error {\n\tfor _, up := range mb.migrationsUp {\n\t\tif !mb.hasDownMigrationWithVersion(up.Version) {\n\t\t\treturn errors.Errorf(\"migration %s has no corresponding down migration\", up.Version)\n\t\t}\n\t}\n\n\tfor _, n := range mb.migrationsUp {\n\t\tif err := n.Valid(); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t}\n\tfor _, n := range mb.migrationsDown {\n\t\tif err := n.Valid(); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/popx/migration_content.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"bytes\"\n\t\"text/template\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/pop/v6\"\n)\n\nfunc ParameterizedMigrationContent(params map[string]interface{}) func(mf Migration, c *pop.Connection, r []byte, usingTemplate bool) (string, error) {\n\treturn func(mf Migration, c *pop.Connection, b []byte, usingTemplate bool) (string, error) {\n\t\tcontent := \"\"\n\t\tif usingTemplate {\n\t\t\tt := template.New(\"migration\")\n\t\t\tt.Funcs(SQLTemplateFuncs)\n\t\t\tt, err := t.Parse(string(b))\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", errors.Wrapf(err, \"could not parse template %s\", mf.Path)\n\t\t\t}\n\t\t\tvar bb bytes.Buffer\n\t\t\terr = t.Execute(&bb, struct {\n\t\t\t\tIsSQLite       bool\n\t\t\t\tIsCockroach    bool\n\t\t\t\tIsMySQL        bool\n\t\t\t\tIsMariaDB      bool\n\t\t\t\tIsPostgreSQL   bool\n\t\t\t\tDialectDetails *pop.ConnectionDetails\n\t\t\t\tParameters     map[string]interface{}\n\t\t\t}{\n\t\t\t\tIsSQLite:       c.Dialect.Name() == \"sqlite3\",\n\t\t\t\tIsCockroach:    c.Dialect.Name() == \"cockroach\",\n\t\t\t\tIsMySQL:        c.Dialect.Name() == \"mysql\",\n\t\t\t\tIsMariaDB:      c.Dialect.Name() == \"mariadb\",\n\t\t\t\tIsPostgreSQL:   c.Dialect.Name() == \"postgres\",\n\t\t\t\tDialectDetails: c.Dialect.Details(),\n\t\t\t\tParameters:     params,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", errors.Wrapf(err, \"could not execute migration template %s\", mf.Path)\n\t\t\t}\n\t\t\tcontent = bb.String()\n\t\t} else {\n\t\t\tcontent = string(b)\n\t\t}\n\n\t\treturn content, nil\n\t}\n}\n"
  },
  {
    "path": "oryx/popx/migration_info.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/pop/v6\"\n)\n\n// Migration handles the data for a given database migration\ntype Migration struct {\n\t// Path to the migration (./migrations/123_create_widgets.up.sql)\n\tPath string\n\t// Version of the migration (123)\n\tVersion string\n\t// Name of the migration (create_widgets)\n\tName string\n\t// Direction of the migration (up|down)\n\tDirection string\n\t// Type of migration (sql|go)\n\tType string\n\t// DB type (all|postgres|mysql...)\n\tDBType string\n\t// Runner function to run/execute the migration. Will be wrapped in a\n\t// database transaction. Mutually exclusive with RunnerNoTx\n\tRunner func(Migration, *pop.Connection) error\n\t// Content is the raw content of the migration file\n\tContent string\n\t// Autocommit indicates whether the migration should be run in autocommit mode\n\tAutocommit bool\n}\n\nfunc (m Migration) Valid() error {\n\tif m.Runner == nil {\n\t\treturn errors.Errorf(\"no runner defined for %s\", m.Path)\n\t}\n\n\treturn nil\n}\n\n// Migrations is a collection of Migration\ntype Migrations []Migration\n\nfunc (mfs Migrations) Len() int           { return len(mfs) }\nfunc (mfs Migrations) Less(i, j int) bool { return compareMigration(mfs[i], mfs[j]) < 0 }\nfunc (mfs Migrations) Swap(i, j int)      { mfs[i], mfs[j] = mfs[j], mfs[i] }\n\nfunc compareMigration(a, b Migration) int {\n\tif a.Version != b.Version {\n\t\treturn strings.Compare(a.Version, b.Version)\n\t}\n\t// Force \"all\" to be greater.\n\tif a.DBType == \"all\" && b.DBType != \"all\" {\n\t\treturn 1\n\t} else if a.DBType != \"all\" && b.DBType == \"all\" {\n\t\treturn -1\n\t}\n\treturn strings.Compare(a.DBType, b.DBType)\n}\n\n// specificity returns an integer representing how specific the migration is.\n// Higher numbers indicate a more specific migration.\nfunc specificity(m *Migration) int {\n\tspecificity := 0\n\tif m.DBType != \"all\" {\n\t\tspecificity += 1\n\t}\n\n\treturn specificity\n}\n\n// isApplicable checks whether the migration is applicable for the given dialect.\nfunc isApplicable(m Migration, dialect string) bool {\n\treturn m.DBType == dialect || m.DBType == \"all\"\n}\n\nfunc (mfs Migrations) sortAndFilter(dialect string) Migrations {\n\tbyVersion := make(map[string]Migration, len(mfs))\n\tfor _, migration := range mfs {\n\t\tif !isApplicable(migration, dialect) {\n\t\t\tcontinue\n\t\t}\n\t\tif previousMigration, ok := byVersion[migration.Version]; ok {\n\t\t\tif specificity(&migration) < specificity(&previousMigration) {\n\t\t\t\t// Previous migration is more specific, skip this one.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tbyVersion[migration.Version] = migration\n\t}\n\n\tfiltered := make(Migrations, 0, len(byVersion))\n\tfor k := range byVersion {\n\t\tfiltered = append(filtered, byVersion[k])\n\t}\n\tsort.Sort(filtered)\n\treturn filtered\n}\n\nfunc (mfs Migrations) find(version, dbType string) *Migration {\n\tvar candidate *Migration\n\tfor _, m := range mfs {\n\t\tif m.Version == version {\n\t\t\tswitch m.DBType {\n\t\t\tcase \"all\":\n\t\t\t\t// there might still be a more specific migration for the dbType\n\t\t\t\tcandidate = &m\n\t\t\tcase dbType:\n\t\t\t\treturn &m\n\t\t\t}\n\t\t}\n\t}\n\treturn candidate\n}\n"
  },
  {
    "path": "oryx/popx/migrator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/cockroachdb/cockroach-go/v2/crdb\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nconst (\n\tPending          = \"Pending\"\n\tApplied          = \"Applied\"\n\ttracingComponent = \"github.com/ory/x/popx\"\n)\n\nfunc (mb *MigrationBox) shouldNotUseTransaction(m Migration) bool {\n\treturn m.Autocommit || mb.c.Dialect.Name() == \"cockroach\" || mb.c.Dialect.Name() == \"mysql\"\n}\n\n// Up runs pending \"up\" migrations and applies them to the database.\nfunc (mb *MigrationBox) Up(ctx context.Context) error {\n\t_, err := mb.UpTo(ctx, 0)\n\treturn errors.WithStack(err)\n}\n\n// UpTo runs up to step \"up\" migrations and applies them to the database.\n// If step <= 0 all pending migrations are run.\nfunc (mb *MigrationBox) UpTo(ctx context.Context, step int) (applied int, err error) {\n\tctx, span := startSpan(ctx, MigrationUpOpName, trace.WithAttributes(attribute.Int(\"step\", step)))\n\tdefer otelx.End(span, &err)\n\n\tc := mb.c.WithContext(ctx)\n\terr = mb.exec(ctx, func() error {\n\t\tmtn := sanitizedMigrationTableName(c)\n\t\tmfs := mb.migrationsUp.sortAndFilter(c.Dialect.Name())\n\t\tfor _, mi := range mfs {\n\t\t\tl := mb.l.WithField(\"version\", mi.Version).WithField(\"migration_name\", mi.Name).WithField(\"migration_file\", mi.Path)\n\n\t\t\tappliedMigrations := make([]string, 0, 2)\n\t\t\tlegacyVersion := mi.Version\n\t\t\tif len(legacyVersion) > 14 {\n\t\t\t\tlegacyVersion = legacyVersion[:14]\n\t\t\t}\n\t\t\terr := c.RawQuery(fmt.Sprintf(\"SELECT version FROM %s WHERE version IN (?, ?)\", mtn), mi.Version, legacyVersion).All(&appliedMigrations)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"problem checking for migration version %s\", mi.Version)\n\t\t\t}\n\n\t\t\tif slices.Contains(appliedMigrations, mi.Version) {\n\t\t\t\tl.Debug(\"Migration has already been applied, skipping.\")\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif slices.Contains(appliedMigrations, legacyVersion) {\n\t\t\t\tl.WithField(\"legacy_version\", legacyVersion).WithField(\"migration_table\", mtn).Debug(\"Migration has already been applied in a legacy migration run. Updating version in migration table.\")\n\t\t\t\tif err := mb.isolatedTransaction(ctx, \"init-migrate\", func(conn *pop.Connection) error {\n\t\t\t\t\t// We do not want to remove the legacy migration version or subsequent migrations might be applied twice.\n\t\t\t\t\t//\n\t\t\t\t\t// Do not activate the following - it is just for reference.\n\t\t\t\t\t//\n\t\t\t\t\t// if _, err := tx.Store.Exec(fmt.Sprintf(\"DELETE FROM %s WHERE version = ?\", mtn), legacyVersion); err != nil {\n\t\t\t\t\t//\treturn errors.Wrapf(err, \"problem removing legacy version %s\", mi.Version)\n\t\t\t\t\t// }\n\n\t\t\t\t\t// #nosec G201 - mtn is a system-wide const\n\t\t\t\t\terr := conn.RawQuery(fmt.Sprintf(\"INSERT INTO %s (version) VALUES (?)\", mtn), mi.Version).Exec()\n\t\t\t\t\treturn errors.Wrapf(err, \"problem inserting migration version %s\", mi.Version)\n\t\t\t\t}); err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tl.Info(\"Migration has not yet been applied, running migration.\")\n\n\t\t\tif err := mi.Valid(); err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\n\t\t\tnoTx := mb.shouldNotUseTransaction(mi)\n\t\t\tif noTx {\n\t\t\t\tl.Info(\"NOT running migrations inside a transaction\")\n\t\t\t\tif err := mi.Runner(mi, c); err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\n\t\t\t\t// #nosec G201 - mtn is a system-wide const\n\t\t\t\tif err := c.RawQuery(fmt.Sprintf(\"INSERT INTO %s (version) VALUES (?)\", mtn), mi.Version).Exec(); err != nil {\n\t\t\t\t\treturn errors.Wrapf(err, \"problem inserting migration version %s. YOUR DATABASE MAY BE IN AN INCONSISTENT STATE! MANUAL INTERVENTION REQUIRED!\", mi.Version)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := mb.isolatedTransaction(ctx, \"up\", func(conn *pop.Connection) error {\n\t\t\t\t\tif err := mi.Runner(mi, conn); err != nil {\n\t\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t\t}\n\n\t\t\t\t\t// #nosec G201 - mtn is a system-wide const\n\t\t\t\t\tif err := conn.RawQuery(fmt.Sprintf(\"INSERT INTO %s (version) VALUES (?)\", mtn), mi.Version).Exec(); err != nil {\n\t\t\t\t\t\treturn errors.Wrapf(err, \"problem inserting migration version %s\", mi.Version)\n\t\t\t\t\t}\n\t\t\t\t\treturn nil\n\t\t\t\t}); err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tl.WithField(\"autocommit\", noTx).Infof(\"> %s applied successfully\", mi.Name)\n\t\t\tapplied++\n\t\t\tif step > 0 && applied >= step {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif applied == 0 {\n\t\t\tmb.l.Infof(\"Migrations already up to date, nothing to apply\")\n\t\t} else {\n\t\t\tmb.l.Infof(\"Successfully applied %d migrations.\", applied)\n\t\t}\n\t\treturn nil\n\t})\n\treturn applied, errors.WithStack(err)\n}\n\n// Down runs pending \"down\" migrations and rolls back the\n// database by the specified number of steps.\n// If step <= 0, all down migrations are run.\nfunc (mb *MigrationBox) Down(ctx context.Context, steps int) (err error) {\n\tctx, span := startSpan(ctx, MigrationDownOpName, trace.WithAttributes(attribute.Int(\"steps\", steps)))\n\tdefer otelx.End(span, &err)\n\n\tif steps <= 0 {\n\t\tsteps = math.MaxInt\n\t}\n\n\tc := mb.c.WithContext(ctx)\n\treturn errors.WithStack(mb.exec(ctx, func() (err error) {\n\t\tmtn := sanitizedMigrationTableName(c)\n\t\tcount, err := c.Count(mtn)\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, \"migration down: unable count existing migration\")\n\t\t}\n\t\tsteps = min(steps, count)\n\n\t\tmfs := mb.migrationsDown.sortAndFilter(c.Dialect.Name())\n\t\tslices.Reverse(mfs)\n\t\tif len(mfs) > count {\n\t\t\t// skip all migrations that were not yet applied\n\t\t\tmfs = mfs[len(mfs)-count:]\n\t\t}\n\n\t\treverted := 0\n\t\tdefer func() {\n\t\t\tmigrationsToRevertCount := min(steps, len(mfs))\n\t\t\tmb.l.Debugf(\"Successfully reverted %d/%d migrations.\", reverted, migrationsToRevertCount)\n\t\t\tif err != nil {\n\t\t\t\tmb.l.WithError(err).Error(\"Problem reverting migrations.\")\n\t\t\t}\n\t\t}()\n\t\tfor i, mi := range mfs {\n\t\t\tif i >= steps {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tl := mb.l.WithField(\"version\", mi.Version).WithField(\"migration_name\", mi.Name).WithField(\"migration_file\", mi.Path)\n\t\t\tl.Debugf(\"handling migration %s\", mi.Name)\n\t\t\texists, err := c.Where(\"version = ?\", mi.Version).Exists(mtn)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"problem checking for migration version %s\", mi.Version)\n\t\t\t}\n\n\t\t\tif !exists && len(mi.Version) > 14 {\n\t\t\t\tlegacyVersion := mi.Version[:14]\n\t\t\t\tlegacyVersionExists, err := c.Where(\"version = ?\", legacyVersion).Exists(mtn)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.Wrapf(err, \"problem checking for legacy migration version %s\", legacyVersion)\n\t\t\t\t}\n\n\t\t\t\tif !legacyVersionExists {\n\t\t\t\t\treturn errors.Errorf(\"neither normal (%s) nor legacy migration (%s) exist\", mi.Version, legacyVersion)\n\t\t\t\t}\n\t\t\t} else if !exists {\n\t\t\t\treturn errors.Errorf(\"migration version %s does not exist\", mi.Version)\n\t\t\t}\n\n\t\t\tif err := mi.Valid(); err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\n\t\t\tif mb.shouldNotUseTransaction(mi) {\n\t\t\t\terr := mi.Runner(mi, c)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\n\t\t\t\t// #nosec G201 - mtn is a system-wide const\n\t\t\t\tif err := c.RawQuery(fmt.Sprintf(\"DELETE FROM %s WHERE version = ?\", mtn), mi.Version).Exec(); err != nil {\n\t\t\t\t\treturn errors.Wrapf(err, \"problem deleting migration version %s. YOUR DATABASE MAY BE IN AN INCONSISTENT STATE! MANUAL INTERVENTION REQUIRED!\", mi.Version)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := mb.isolatedTransaction(ctx, \"down\", func(conn *pop.Connection) error {\n\t\t\t\t\terr := mi.Runner(mi, conn)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t\t}\n\n\t\t\t\t\t// #nosec G201 - mtn is a system-wide const\n\t\t\t\t\tif err := conn.RawQuery(fmt.Sprintf(\"DELETE FROM %s WHERE version = ?\", mtn), mi.Version).Exec(); err != nil {\n\t\t\t\t\t\treturn errors.Wrapf(err, \"problem deleting migration version %s\", mi.Version)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t}); err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tl.Infof(\"%s applied successfully\", mi.Name)\n\t\t\treverted++\n\t\t}\n\t\treturn nil\n\t}))\n}\n\nfunc (mb *MigrationBox) createTransactionalMigrationTable(ctx context.Context, c *pop.Connection, l *logrusx.Logger) error {\n\tmtn := sanitizedMigrationTableName(c)\n\n\tif err := mb.createMigrationStatusTableTransaction(ctx, []string{\n\t\tfmt.Sprintf(`CREATE TABLE %s (version VARCHAR (48) NOT NULL, version_self INT NOT NULL DEFAULT 0)`, mtn),\n\t\tfmt.Sprintf(`CREATE UNIQUE INDEX %s_version_idx ON %s (version)`, mtn, mtn),\n\t\tfmt.Sprintf(`CREATE INDEX %s_version_self_idx ON %s (version_self)`, mtn, mtn),\n\t}); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tl.WithField(\"migration_table\", mtn).Debug(\"Transactional migration table created successfully.\")\n\n\treturn nil\n}\n\nfunc (mb *MigrationBox) migrateToTransactionalMigrationTable(ctx context.Context, c *pop.Connection, l *logrusx.Logger) error {\n\t// This means the new pop migrator has also not yet been applied, do that now.\n\tmtn := sanitizedMigrationTableName(c)\n\n\twithOn := fmt.Sprintf(\" ON %s\", mtn)\n\tif c.Dialect.Name() != \"mysql\" {\n\t\twithOn = \"\"\n\t}\n\n\tinterimTable := fmt.Sprintf(\"%s_transactional\", mtn)\n\tworkload := [][]string{\n\t\t{\n\t\t\tfmt.Sprintf(`DROP INDEX %s_version_idx%s`, mtn, withOn),\n\t\t\tfmt.Sprintf(`CREATE TABLE %s (version VARCHAR (48) NOT NULL, version_self INT NOT NULL DEFAULT 0)`, interimTable),\n\t\t\tfmt.Sprintf(`CREATE UNIQUE INDEX %s_version_idx ON %s (version)`, mtn, interimTable),\n\t\t\tfmt.Sprintf(`CREATE INDEX %s_version_self_idx ON %s (version_self)`, mtn, interimTable),\n\t\t\t// #nosec G201 - mtn is a system-wide const\n\t\t\tfmt.Sprintf(`INSERT INTO %s (version) SELECT version FROM %s`, interimTable, mtn),\n\t\t\tfmt.Sprintf(`ALTER TABLE %s RENAME TO %s_pop_legacy`, mtn, mtn),\n\t\t},\n\t\t{\n\t\t\tfmt.Sprintf(`ALTER TABLE %s RENAME TO %s`, interimTable, mtn),\n\t\t},\n\t}\n\n\tif err := mb.createMigrationStatusTableTransaction(ctx, workload...); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tl.WithField(\"migration_table\", mtn).Debug(\"Successfully migrated legacy schema_migration to new transactional schema_migration table.\")\n\n\treturn nil\n}\n\nfunc (mb *MigrationBox) isolatedTransaction(ctx context.Context, direction string, fn func(c *pop.Connection) error) (err error) {\n\tctx, span := startSpan(ctx, MigrationRunTransactionOpName, trace.WithAttributes(attribute.String(\"migration_direction\", direction)))\n\tdefer otelx.End(span, &err)\n\n\tif mb.perMigrationTimeout > 0 {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeoutCause(ctx, mb.perMigrationTimeout, errors.Errorf(\"failed to run all SQL migrations: direction=%s timeout=%s\", direction, mb.perMigrationTimeout))\n\t\tdefer cancel()\n\t}\n\n\treturn Transaction(ctx, mb.c.WithContext(ctx), func(ctx context.Context, connection *pop.Connection) error {\n\t\treturn errors.WithStack(fn(connection))\n\t})\n}\n\nfunc (mb *MigrationBox) createMigrationStatusTableTransaction(ctx context.Context, transactions ...[]string) error {\n\tfor _, statements := range transactions {\n\t\t// CockroachDB does not support transactional schema changes, so we have to run\n\t\t// the statements outside of a transaction.\n\t\tif mb.c.Dialect.Name() == \"cockroach\" || mb.c.Dialect.Name() == \"mysql\" {\n\t\t\tfor _, statement := range statements {\n\t\t\t\tif err := mb.c.WithContext(ctx).RawQuery(statement).Exec(); err != nil {\n\t\t\t\t\treturn errors.Wrapf(err, \"unable to execute statement: %s\", statement)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif err := mb.isolatedTransaction(ctx, \"init\", func(conn *pop.Connection) error {\n\t\t\t\tfor _, statement := range statements {\n\t\t\t\t\tif err := conn.WithContext(ctx).RawQuery(statement).Exec(); err != nil {\n\t\t\t\t\t\treturn errors.Wrapf(err, \"unable to execute statement: %s\", statement)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}); err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// CreateSchemaMigrations sets up a table to track migrations. This is an idempotent\n// operation.\nfunc (mb *MigrationBox) CreateSchemaMigrations(ctx context.Context) error {\n\tctx, span := startSpan(ctx, MigrationInitOpName)\n\tdefer span.End()\n\n\tc := mb.c.WithContext(ctx)\n\n\tmtn := sanitizedMigrationTableName(c)\n\tmb.l.WithField(\"migration_table\", mtn).Debug(\"Checking if legacy migration table exists.\")\n\t_, err := c.Store.Exec(fmt.Sprintf(\"select version from %s\", mtn))\n\tif err != nil {\n\t\tmb.l.WithError(err).WithField(\"migration_table\", mtn).Debug(\"An error occurred while checking for the legacy migration table, maybe it does not exist yet? Trying to create.\")\n\t\t// This means that the legacy pop migrator has not yet been applied\n\t\treturn errors.WithStack(mb.createTransactionalMigrationTable(ctx, c, mb.l))\n\t}\n\n\tmb.l.WithField(\"migration_table\", mtn).Debug(\"A migration table exists, checking if it is a transactional migration table.\")\n\t_, err = c.Store.Exec(fmt.Sprintf(\"select version, version_self from %s\", mtn))\n\tif err != nil {\n\t\tmb.l.WithError(err).WithField(\"migration_table\", mtn).Debug(\"An error occurred while checking for the transactional migration table, maybe it does not exist yet? Trying to create.\")\n\t\treturn errors.WithStack(mb.migrateToTransactionalMigrationTable(ctx, c, mb.l))\n\t}\n\n\tmb.l.WithField(\"migration_table\", mtn).Debug(\"Migration tables exist and are up to date.\")\n\treturn nil\n}\n\ntype MigrationStatus struct {\n\tState       string `json:\"state\"`\n\tVersion     string `json:\"version\"`\n\tName        string `json:\"name\"`\n\tContentUp   string `json:\"content\"`\n\tContentDown string `json:\"content_down\"`\n}\n\ntype MigrationStatuses []MigrationStatus\n\nvar _ cmdx.Table = (MigrationStatuses)(nil)\n\nfunc (m MigrationStatuses) Header() []string {\n\treturn []string{\"Version\", \"Name\", \"Status\"}\n}\n\nfunc (m MigrationStatuses) Table() [][]string {\n\tt := make([][]string, len(m))\n\tfor i, s := range m {\n\t\tt[i] = []string{s.Version, s.Name, s.State}\n\t}\n\treturn t\n}\n\nfunc (m MigrationStatuses) Interface() interface{} {\n\treturn m\n}\n\nfunc (m MigrationStatuses) Len() int {\n\treturn len(m)\n}\n\nfunc (m MigrationStatuses) IDs() []string {\n\tids := make([]string, len(m))\n\tfor i, s := range m {\n\t\tids[i] = s.Version\n\t}\n\treturn ids\n}\n\nfunc (m MigrationStatuses) HasPending() bool {\n\tfor _, mm := range m {\n\t\tif mm.State == Pending {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc sanitizedMigrationTableName(con *pop.Connection) string {\n\treturn regexp.MustCompile(`\\W`).ReplaceAllString(con.MigrationTableName(), \"\")\n}\n\nfunc errIsTableNotFound(err error) bool {\n\treturn strings.Contains(err.Error(), \"no such table:\") || // sqlite\n\t\tstrings.Contains(err.Error(), \"Error 1146\") || // MySQL\n\t\tstrings.Contains(err.Error(), \"SQLSTATE 42P01\") // PostgreSQL / CockroachDB\n}\n\n// Status prints out the status of applied/pending migrations.\nfunc (mb *MigrationBox) Status(ctx context.Context) (MigrationStatuses, error) {\n\tctx, span := startSpan(ctx, MigrationStatusOpName)\n\tdefer span.End()\n\n\tcon := mb.c.WithContext(ctx)\n\n\tmigrationsUp := mb.migrationsUp.sortAndFilter(con.Dialect.Name())\n\n\tif len(migrationsUp) == 0 {\n\t\treturn nil, errors.Errorf(\"unable to find any migrations for dialect: %s\", con.Dialect.Name())\n\t}\n\n\talreadyApplied := make([]string, 0, len(migrationsUp))\n\terr := con.RawQuery(fmt.Sprintf(\"SELECT version FROM %s\", sanitizedMigrationTableName(con))).All(&alreadyApplied)\n\tif err != nil {\n\t\tif errIsTableNotFound(err) {\n\t\t\t// This means that no migrations have been applied and we need to apply all of them first!\n\t\t\t//\n\t\t\t// It also means that we can ignore this state and act as if no migrations have been applied yet.\n\t\t} else {\n\t\t\t// On any other error, we fail.\n\t\t\treturn nil, errors.Wrap(err, \"problem with migration\")\n\t\t}\n\t}\n\n\tstatuses := make(MigrationStatuses, len(migrationsUp))\n\tfor k, mf := range migrationsUp {\n\t\tdownContent := \"-- error: no down migration defined for this migration\"\n\t\tif mDown := mb.migrationsDown.find(mf.Version, con.Dialect.Name()); mDown != nil {\n\t\t\tdownContent = mDown.Content\n\t\t}\n\t\tstatuses[k] = MigrationStatus{\n\t\t\tState:       Pending,\n\t\t\tVersion:     mf.Version,\n\t\t\tName:        mf.Name,\n\t\t\tContentUp:   mf.Content,\n\t\t\tContentDown: downContent,\n\t\t}\n\n\t\tif slices.ContainsFunc(alreadyApplied, func(applied string) bool {\n\t\t\treturn applied == mf.Version || (len(mf.Version) > 14 && applied == mf.Version[:14])\n\t\t}) {\n\t\t\tstatuses[k].State = Applied\n\t\t\tcontinue\n\t\t}\n\t}\n\n\treturn statuses, nil\n}\n\n// DumpMigrationSchema will generate a file of the current database schema\nfunc (mb *MigrationBox) DumpMigrationSchema(ctx context.Context) error {\n\tc := mb.c.WithContext(ctx)\n\tschema := \"schema.sql\"\n\tf, err := os.Create(schema) //#nosec:G304\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\terr = c.Dialect.DumpSchema(f)\n\tif err != nil {\n\t\t_ = os.RemoveAll(schema)\n\t\treturn errors.WithStack(err)\n\t}\n\treturn nil\n}\n\nfunc (mb *MigrationBox) exec(ctx context.Context, fn func() error) error {\n\tnow := time.Now()\n\tdefer mb.printTimer(now)\n\n\terr := mb.CreateSchemaMigrations(ctx)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"migrator: problem creating schema migrations\")\n\t}\n\n\tif mb.c.Dialect.Name() == \"sqlite3\" {\n\t\tif err := mb.c.RawQuery(\"PRAGMA foreign_keys=OFF\").Exec(); err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\t}\n\n\tif mb.c.Dialect.Name() == \"cockroach\" {\n\t\touter := fn\n\t\tfn = func() error {\n\t\t\treturn errors.WithStack(crdb.Execute(outer))\n\t\t}\n\t}\n\n\tif err := fn(); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif mb.c.Dialect.Name() == \"sqlite3\" {\n\t\tif err := mb.c.RawQuery(\"PRAGMA foreign_keys=ON\").Exec(); err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (mb *MigrationBox) printTimer(timerStart time.Time) {\n\tdiff := time.Since(timerStart).Seconds()\n\tif diff > 60 {\n\t\tmb.l.Debugf(\"%.4f minutes\", diff/60)\n\t} else {\n\t\tmb.l.Debugf(\"%.4f seconds\", diff)\n\t}\n}\n"
  },
  {
    "path": "oryx/popx/span.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"context\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\nconst (\n\tMigrationStatusOpName         = \"migration-status\"\n\tMigrationInitOpName           = \"migration-init\"\n\tMigrationUpOpName             = \"migration-up\"\n\tMigrationRunTransactionOpName = \"migration-run-transaction\"\n\tMigrationDownOpName           = \"migration-down\"\n)\n\nfunc startSpan(ctx context.Context, opName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {\n\treturn trace.SpanFromContext(ctx).TracerProvider().Tracer(tracingComponent).Start(ctx, opName, opts...)\n}\n"
  },
  {
    "path": "oryx/popx/sql_template_funcs.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/pkg/errors\"\n)\n\nvar SQLTemplateFuncs = map[string]interface{}{\n\t\"identifier\": Identifier,\n}\n\nvar identifierPattern = regexp.MustCompile(\"^[a-zA-Z][a-zA-Z0-9_]*$\")\n\nfunc Identifier(i string) (string, error) {\n\tif !identifierPattern.MatchString(i) {\n\t\treturn \"\", errors.Errorf(\"invalid SQL identifier '%s'\", i)\n\t}\n\treturn i, nil\n}\n"
  },
  {
    "path": "oryx/popx/stub/migrations/check/valid/123_a.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/check/valid/123_a.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/check/valid/123_a.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000001_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identity_credentials\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identity_credential_types\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identities\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000001_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"traits_schema_id\" VARCHAR (2048) NOT NULL,\n\"traits\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"identity_credential_types\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"name\" VARCHAR (32) NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"identity_credentials\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"config\" json NOT NULL,\n\"identity_credential_type_id\" UUID NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_credentials_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_credentials_identity_credential_types_id_fk\" FOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"identity_credential_identifiers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identifier\" VARCHAR (255) NOT NULL,\n\"identity_credential_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_credential_identifiers_identity_credentials_id_fk\" FOREIGN KEY (\"identity_credential_id\") REFERENCES \"identity_credentials\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000001_identities.mysql.down.sql",
    "content": "DROP TABLE `identity_credential_identifiers`;\nDROP TABLE `identity_credentials`;\nDROP TABLE `identity_credential_types`;\nDROP TABLE `identities`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000001_identities.mysql.up.sql",
    "content": "CREATE TABLE `identities` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`traits_schema_id` VARCHAR (2048) NOT NULL,\n`traits` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nCREATE TABLE `identity_credential_types` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`name` VARCHAR (32) NOT NULL\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_credential_types_name_idx` ON `identity_credential_types` (`name`);\nCREATE TABLE `identity_credentials` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`config` JSON NOT NULL,\n`identity_credential_type_id` char(36) NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade,\nFOREIGN KEY (`identity_credential_type_id`) REFERENCES `identity_credential_types` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `identity_credential_identifiers` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`identifier` VARCHAR (255) NOT NULL,\n`identity_credential_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_credential_id`) REFERENCES `identity_credentials` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_credential_identifiers_identifier_idx` ON `identity_credential_identifiers` (`identifier`);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000001_identities.postgres.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";\nDROP TABLE \"identity_credentials\";\nDROP TABLE \"identity_credential_types\";\nDROP TABLE \"identities\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000001_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"traits_schema_id\" VARCHAR (2048) NOT NULL,\n\"traits\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nCREATE TABLE \"identity_credential_types\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"name\" VARCHAR (32) NOT NULL\n);\nCREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name);\nCREATE TABLE \"identity_credentials\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"config\" jsonb NOT NULL,\n\"identity_credential_type_id\" UUID NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"identity_credential_identifiers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identifier\" VARCHAR (255) NOT NULL,\n\"identity_credential_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_credential_id\") REFERENCES \"identity_credentials\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000001_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";\nDROP TABLE \"identity_credentials\";\nDROP TABLE \"identity_credential_types\";\nDROP TABLE \"identities\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000001_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" TEXT PRIMARY KEY,\n\"traits_schema_id\" TEXT NOT NULL,\n\"traits\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nCREATE TABLE \"identity_credential_types\" (\n\"id\" TEXT PRIMARY KEY,\n\"name\" TEXT NOT NULL\n);\nCREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name);\nCREATE TABLE \"identity_credentials\" (\n\"id\" TEXT PRIMARY KEY,\n\"config\" TEXT NOT NULL,\n\"identity_credential_type_id\" char(36) NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade,\nFOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON DELETE cascade\n);\nCREATE TABLE \"identity_credential_identifiers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000002_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_login_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_registration_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_registration_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_profile_management_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000002_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_login_request_methods_selfservice_login_requests_id_fk\" FOREIGN KEY (\"selfservice_login_request_id\") REFERENCES \"selfservice_login_requests\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_registration_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_registration_request_methods_selfservice_registration_requests_id_fk\" FOREIGN KEY (\"selfservice_registration_request_id\") REFERENCES \"selfservice_registration_requests\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" json NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_profile_management_requests_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000002_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_login_request_methods`;\nDROP TABLE `selfservice_login_requests`;\nDROP TABLE `selfservice_registration_request_methods`;\nDROP TABLE `selfservice_registration_requests`;\nDROP TABLE `selfservice_profile_management_requests`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000002_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_login_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`active_method` VARCHAR (32) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_login_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_login_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_login_request_id`) REFERENCES `selfservice_login_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_registration_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`active_method` VARCHAR (32) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_registration_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_registration_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_registration_request_id`) REFERENCES `selfservice_registration_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_profile_management_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`form` JSON NOT NULL,\n`update_successful` bool NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000002_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\";\nDROP TABLE \"selfservice_login_requests\";\nDROP TABLE \"selfservice_registration_request_methods\";\nDROP TABLE \"selfservice_registration_requests\";\nDROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000002_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nCREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_login_request_id\") REFERENCES \"selfservice_login_requests\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"selfservice_registration_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nCREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_registration_request_id\") REFERENCES \"selfservice_registration_requests\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" jsonb NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000002_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\";\nDROP TABLE \"selfservice_login_requests\";\nDROP TABLE \"selfservice_registration_request_methods\";\nDROP TABLE \"selfservice_registration_requests\";\nDROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000002_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nCREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_login_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_login_request_id) REFERENCES selfservice_login_requests (id) ON DELETE cascade\n);\nCREATE TABLE \"selfservice_registration_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nCREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_registration_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_registration_request_id) REFERENCES selfservice_registration_requests (id) ON DELETE cascade\n);\nCREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000003_sessions.cockroach.down.sql",
    "content": "DROP TABLE \"sessions\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000003_sessions.cockroach.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"authenticated_at\" timestamp NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"sessions_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000003_sessions.mysql.down.sql",
    "content": "DROP TABLE `sessions`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000003_sessions.mysql.up.sql",
    "content": "CREATE TABLE `sessions` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`authenticated_at` DATETIME NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000003_sessions.postgres.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000003_sessions.postgres.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"authenticated_at\" timestamp NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000003_sessions.sqlite3.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000003_sessions.sqlite3.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000004_errors.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000004_errors.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"errors\" json NOT NULL,\n\"seen_at\" timestamp NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000004_errors.mysql.down.sql",
    "content": "DROP TABLE `selfservice_errors`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000004_errors.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_errors` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`errors` JSON NOT NULL,\n`seen_at` DATETIME NOT NULL,\n`was_seen` bool NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000004_errors.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000004_errors.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"errors\" jsonb NOT NULL,\n\"seen_at\" timestamp NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000004_errors.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000004_errors.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000005_identities.mysql.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255);\n"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000005_identities.mysql.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000006_courier.cockroach.down.sql",
    "content": "DROP TABLE \"courier_messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000006_courier.cockroach.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"type\" int NOT NULL,\n\"status\" int NOT NULL,\n\"body\" VARCHAR (255) NOT NULL,\n\"subject\" VARCHAR (255) NOT NULL,\n\"recipient\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000006_courier.mysql.down.sql",
    "content": "DROP TABLE `courier_messages`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000006_courier.mysql.up.sql",
    "content": "CREATE TABLE `courier_messages` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`type` INTEGER NOT NULL,\n`status` INTEGER NOT NULL,\n`body` VARCHAR (255) NOT NULL,\n`subject` VARCHAR (255) NOT NULL,\n`recipient` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000006_courier.postgres.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000006_courier.postgres.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"type\" int NOT NULL,\n\"status\" int NOT NULL,\n\"body\" VARCHAR (255) NOT NULL,\n\"subject\" VARCHAR (255) NOT NULL,\n\"recipient\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000006_courier.sqlite3.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000006_courier.sqlite3.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000007_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"csrf_token\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000007_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" VARCHAR (255) NOT NULL DEFAULT '';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000007_errors.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_errors` DROP COLUMN `csrf_token`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000007_errors.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` ADD COLUMN `csrf_token` VARCHAR (255) NOT NULL DEFAULT \"\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000007_errors.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"csrf_token\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000007_errors.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" VARCHAR (255) NOT NULL DEFAULT '';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000007_errors.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nINSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at) SELECT id, errors, seen_at, was_seen, created_at, updated_at FROM \"selfservice_errors\";\n\nDROP TABLE \"selfservice_errors\";\nALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000007_errors.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" TEXT NOT NULL DEFAULT '';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000008_selfservice_verification.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identity_verifiable_addresses\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000008_selfservice_verification.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"code\" VARCHAR (32) NOT NULL,\n\"status\" VARCHAR (16) NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"verified_at\" timestamp,\n\"expires_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_verifiable_addresses_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_verification_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" json NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000008_selfservice_verification.mysql.down.sql",
    "content": "DROP TABLE `selfservice_verification_requests`;\nDROP TABLE `identity_verifiable_addresses`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000008_selfservice_verification.mysql.up.sql",
    "content": "CREATE TABLE `identity_verifiable_addresses` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`code` VARCHAR (32) NOT NULL,\n`status` VARCHAR (16) NOT NULL,\n`via` VARCHAR (16) NOT NULL,\n`verified` bool NOT NULL,\n`value` VARCHAR (400) NOT NULL,\n`verified_at` DATETIME,\n`expires_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses` (`code`);\nCREATE INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses` (`code`);\nCREATE UNIQUE INDEX `identity_verifiable_addresses_status_via_uq_idx` ON `identity_verifiable_addresses` (`via`, `value`);\nCREATE INDEX `identity_verifiable_addresses_status_via_idx` ON `identity_verifiable_addresses` (`via`, `value`);\nCREATE TABLE `selfservice_verification_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`form` JSON NOT NULL,\n`via` VARCHAR (16) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`success` bool NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000008_selfservice_verification.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\";\nDROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000008_selfservice_verification.postgres.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"code\" VARCHAR (32) NOT NULL,\n\"status\" VARCHAR (16) NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"verified_at\" timestamp,\n\"expires_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);\nCREATE TABLE \"selfservice_verification_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" jsonb NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000008_selfservice_verification.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\";\nDROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000008_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"code\" TEXT NOT NULL,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);\nCREATE TABLE \"selfservice_verification_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000009_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255);\n"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000009_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000010_errors.cockroach.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;\nALTER TABLE \"selfservice_errors\" RENAME COLUMN \"seen_at\" TO \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_errors\" ADD COLUMN \"seen_at\" timestamp;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"selfservice_errors\" SET \"seen_at\" = \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_errors\" DROP COLUMN \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000010_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" RENAME COLUMN \"seen_at\" TO \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_errors\" ADD COLUMN \"seen_at\" timestamp;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"selfservice_errors\" SET \"seen_at\" = \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_errors\" DROP COLUMN \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000010_errors.mysql.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;\nALTER TABLE `selfservice_errors` MODIFY `seen_at` DATETIME;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000010_errors.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` MODIFY `seen_at` DATETIME;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000010_errors.postgres.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;\nALTER TABLE \"selfservice_errors\" ALTER COLUMN \"seen_at\" TYPE timestamp, ALTER COLUMN \"seen_at\" DROP NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000010_errors.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ALTER COLUMN \"seen_at\" TYPE timestamp, ALTER COLUMN \"seen_at\" DROP NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000010_errors.sqlite3.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;\nCREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n);\nINSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\";\nDROP TABLE \"selfservice_errors\";\nALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000010_errors.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n);\nINSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\";\nDROP TABLE \"selfservice_errors\";\nALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000011_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" RENAME COLUMN \"body\" TO \"_body_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"courier_messages\" ADD COLUMN \"body\" text;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"courier_messages\" SET \"body\" = \"_body_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"courier_messages\" ALTER COLUMN \"body\" SET NOT NULL;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"courier_messages\" DROP COLUMN \"_body_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000011_courier_body_type.mysql.up.sql",
    "content": "ALTER TABLE `courier_messages` MODIFY `body` text NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000011_courier_body_type.postgres.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ALTER COLUMN \"body\" TYPE text, ALTER COLUMN \"body\" SET NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000011_courier_body_type.sqlite3.up.sql",
    "content": "CREATE TABLE \"_courier_messages_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nINSERT INTO \"_courier_messages_tmp\" (id, type, status, body, subject, recipient, created_at, updated_at) SELECT id, type, status, body, subject, recipient, created_at, updated_at FROM \"courier_messages\";\nDROP TABLE \"courier_messages\";\nALTER TABLE \"_courier_messages_tmp\" RENAME TO \"courier_messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000012_login_request_forced.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"forced\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000012_login_request_forced.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000012_login_request_forced.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `forced`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000012_login_request_forced.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `forced` bool NOT NULL DEFAULT false;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000012_login_request_forced.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"forced\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000012_login_request_forced.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000012_login_request_forced.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nINSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at FROM \"selfservice_login_requests\";\n\nDROP TABLE \"selfservice_login_requests\";\nALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20191100000012_login_request_forced.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200317160354_create_profile_request_forms.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"form\" json NOT NULL DEFAULT '{}';COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_profile_management_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"active_method\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200317160354_create_profile_request_forms.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_profile_management_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nINSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;\nALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"form\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200317160354_create_profile_request_forms.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` ADD COLUMN `form` JSON;\nUPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t);\nALTER TABLE `selfservice_profile_management_requests` MODIFY `form` JSON;\nDROP TABLE `selfservice_profile_management_request_methods`;\nALTER TABLE `selfservice_profile_management_requests` DROP COLUMN `active_method`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200317160354_create_profile_request_forms.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_profile_management_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_profile_management_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nALTER TABLE `selfservice_profile_management_requests` ADD COLUMN `active_method` VARCHAR (32);\nINSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;\nALTER TABLE `selfservice_profile_management_requests` DROP COLUMN `form`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200317160354_create_profile_request_forms.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"form\" jsonb;\nUPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t);\nALTER TABLE \"selfservice_profile_management_requests\" ALTER COLUMN \"form\" TYPE jsonb, ALTER COLUMN \"form\" DROP NOT NULL;\nDROP TABLE \"selfservice_profile_management_request_methods\";\nALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"active_method\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200317160354_create_profile_request_forms.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_profile_management_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" VARCHAR (32);\nINSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;\nALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"form\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200317160354_create_profile_request_forms.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_request_methods\";\nCREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"update_successful\" bool NOT NULL DEFAULT 'false',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, update_successful) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, update_successful FROM \"selfservice_profile_management_requests\";\n\nDROP TABLE \"selfservice_profile_management_requests\";\nALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200317160354_create_profile_request_forms.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_profile_management_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" TEXT;\nINSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;\nCREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method) SELECT id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method FROM \"selfservice_profile_management_requests\";\n\nDROP TABLE \"selfservice_profile_management_requests\";\nALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200401183443_continuity_containers.cockroach.down.sql",
    "content": "DROP TABLE \"continuity_containers\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200401183443_continuity_containers.cockroach.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identity_id\" UUID,\n\"name\" VARCHAR (255) NOT NULL,\n\"payload\" json,\n\"expires_at\" timestamp NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"continuity_containers_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200401183443_continuity_containers.mysql.down.sql",
    "content": "DROP TABLE `continuity_containers`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200401183443_continuity_containers.mysql.up.sql",
    "content": "CREATE TABLE `continuity_containers` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`identity_id` char(36),\n`name` VARCHAR (255) NOT NULL,\n`payload` JSON,\n`expires_at` DATETIME NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200401183443_continuity_containers.postgres.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200401183443_continuity_containers.postgres.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identity_id\" UUID,\n\"name\" VARCHAR (255) NOT NULL,\n\"payload\" jsonb,\n\"expires_at\" timestamp NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200401183443_continuity_containers.sqlite3.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200401183443_continuity_containers.sqlite3.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identity_id\" char(36),\n\"name\" TEXT NOT NULL,\n\"payload\" TEXT,\n\"expires_at\" DATETIME NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200402142539_rename_profile_flows.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200402142539_rename_profile_flows.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200402142539_rename_profile_flows.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_request_methods` CHANGE `selfservice_settings_request_id` `selfservice_profile_management_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_settings_request_methods` RENAME TO `selfservice_profile_management_request_methods`;\nALTER TABLE `selfservice_settings_requests` RENAME TO `selfservice_profile_management_requests`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200402142539_rename_profile_flows.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_request_methods` CHANGE `selfservice_profile_management_request_id` `selfservice_settings_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_profile_management_request_methods` RENAME TO `selfservice_settings_request_methods`;\nALTER TABLE `selfservice_profile_management_requests` RENAME TO `selfservice_settings_requests`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200402142539_rename_profile_flows.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\";\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\";\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200402142539_rename_profile_flows.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\";\nALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\";\nALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200402142539_rename_profile_flows.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\";\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\";\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200402142539_rename_profile_flows.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\";\nALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\";\nALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101057_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_recovery_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_recovery_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identity_recovery_addresses\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101057_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"via\" VARCHAR (16) NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_recovery_addresses_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"messages\" json,\n\"active_method\" VARCHAR (32),\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"state\" VARCHAR (32) NOT NULL,\n\"recovered_identity_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_recovery_requests_identities_id_fk\" FOREIGN KEY (\"recovered_identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"config\" json NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_recovery_request_methods_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"identity_recovery_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"identity_recovery_address_id\" UUID NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_recovery_tokens_identity_recovery_addresses_id_fk\" FOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101057_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `identity_recovery_tokens`;\nDROP TABLE `selfservice_recovery_request_methods`;\nDROP TABLE `selfservice_recovery_requests`;\nDROP TABLE `identity_recovery_addresses`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101057_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `identity_recovery_addresses` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`via` VARCHAR (16) NOT NULL,\n`value` VARCHAR (400) NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_recovery_addresses_status_via_uq_idx` ON `identity_recovery_addresses` (`via`, `value`);\nCREATE INDEX `identity_recovery_addresses_status_via_idx` ON `identity_recovery_addresses` (`via`, `value`);\nCREATE TABLE `selfservice_recovery_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`messages` JSON,\n`active_method` VARCHAR (32),\n`csrf_token` VARCHAR (255) NOT NULL,\n`state` VARCHAR (32) NOT NULL,\n`recovered_identity_id` char(36),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`recovered_identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_recovery_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`config` JSON NOT NULL,\n`selfservice_recovery_request_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_recovery_request_id`) REFERENCES `selfservice_recovery_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `identity_recovery_tokens` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`token` VARCHAR (64) NOT NULL,\n`used` bool NOT NULL DEFAULT false,\n`used_at` DATETIME,\n`identity_recovery_address_id` char(36) NOT NULL,\n`selfservice_recovery_request_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_recovery_address_id`) REFERENCES `identity_recovery_addresses` (`id`) ON DELETE cascade,\nFOREIGN KEY (`selfservice_recovery_request_id`) REFERENCES `selfservice_recovery_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_recovery_addresses_code_uq_idx` ON `identity_recovery_tokens` (`token`);\nCREATE INDEX `identity_recovery_addresses_code_idx` ON `identity_recovery_tokens` (`token`);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101057_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";\nDROP TABLE \"selfservice_recovery_request_methods\";\nDROP TABLE \"selfservice_recovery_requests\";\nDROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101057_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"via\" VARCHAR (16) NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);\nCREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);\nCREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"messages\" jsonb,\n\"active_method\" VARCHAR (32),\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"state\" VARCHAR (32) NOT NULL,\n\"recovered_identity_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"recovered_identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"config\" jsonb NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"identity_recovery_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"identity_recovery_address_id\" UUID NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101057_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";\nDROP TABLE \"selfservice_recovery_request_methods\";\nDROP TABLE \"selfservice_recovery_requests\";\nDROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101057_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"via\" TEXT NOT NULL,\n\"value\" TEXT NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);\nCREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);\nCREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON DELETE cascade\n);\nCREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"config\" TEXT NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n);\nCREATE TABLE \"identity_recovery_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101058_create_recovery_addresses.mysql.down.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64);\n"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200519101058_create_recovery_addresses.mysql.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101000_create_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101000_create_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" json;COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101000_create_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101000_create_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101000_create_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101000_create_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101000_create_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"update_successful\" bool NOT NULL DEFAULT 'false',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful FROM \"selfservice_settings_requests\";\n\nDROP TABLE \"selfservice_settings_requests\";\nALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101000_create_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101001_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200601101001_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(32) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200605111551_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200605111551_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" json;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" json;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" json;COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200605111551_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` DROP COLUMN `messages`;\nALTER TABLE `selfservice_login_requests` DROP COLUMN `messages`;\nALTER TABLE `selfservice_registration_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200605111551_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` ADD COLUMN `messages` JSON;\nALTER TABLE `selfservice_login_requests` ADD COLUMN `messages` JSON;\nALTER TABLE `selfservice_registration_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200605111551_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"messages\";\nALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"messages\";\nALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200605111551_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" jsonb;\nALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" jsonb;\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200605111551_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"via\" TEXT NOT NULL DEFAULT 'email',\n\"success\" bool NOT NULL DEFAULT 'FALSE'\n);\nINSERT INTO \"_selfservice_verification_requests_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, via, success) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, via, success FROM \"selfservice_verification_requests\";\n\nDROP TABLE \"selfservice_verification_requests\";\nALTER TABLE \"_selfservice_verification_requests_tmp\" RENAME TO \"selfservice_verification_requests\";\nCREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false'\n);\nINSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced FROM \"selfservice_login_requests\";\n\nDROP TABLE \"selfservice_login_requests\";\nALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";\nCREATE TABLE \"_selfservice_registration_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nINSERT INTO \"_selfservice_registration_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at FROM \"selfservice_registration_requests\";\n\nDROP TABLE \"selfservice_registration_requests\";\nALTER TABLE \"_selfservice_registration_requests_tmp\" RENAME TO \"selfservice_registration_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200605111551_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" TEXT;\nALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" TEXT;\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200607165100_settings.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"state\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200607165100_settings.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"update_successful\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200607165100_settings.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `state`;\nALTER TABLE `selfservice_settings_requests` ADD COLUMN `update_successful` bool NOT NULL DEFAULT false;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200607165100_settings.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `state` VARCHAR (255) NOT NULL DEFAULT 'show_form';\nALTER TABLE `selfservice_settings_requests` DROP COLUMN `update_successful`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200607165100_settings.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"state\";\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200607165100_settings.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';\nALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"update_successful\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200607165100_settings.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages FROM \"selfservice_settings_requests\";\n\nDROP TABLE \"selfservice_settings_requests\";\nALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200607165100_settings.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form';\nCREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state FROM \"selfservice_settings_requests\";\n\nDROP TABLE \"selfservice_settings_requests\";\nALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200705105359_rename_identities_schema.cockroach.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200705105359_rename_identities_schema.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200705105359_rename_identities_schema.mysql.down.sql",
    "content": "ALTER TABLE `identities` CHANGE `schema_id` `traits_schema_id` varchar(2048) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200705105359_rename_identities_schema.mysql.up.sql",
    "content": "ALTER TABLE `identities` CHANGE `traits_schema_id` `schema_id` varchar(2048) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200705105359_rename_identities_schema.postgres.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200705105359_rename_identities_schema.postgres.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200705105359_rename_identities_schema.sqlite3.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200705105359_rename_identities_schema.sqlite3.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810141652_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810141652_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810141652_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `type`;\nALTER TABLE `selfservice_registration_requests` DROP COLUMN `type`;\nALTER TABLE `selfservice_settings_requests` DROP COLUMN `type`;\nALTER TABLE `selfservice_recovery_requests` DROP COLUMN `type`;\nALTER TABLE `selfservice_verification_requests` DROP COLUMN `type`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810141652_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE `selfservice_registration_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE `selfservice_settings_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE `selfservice_recovery_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE `selfservice_verification_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810141652_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"type\";\nALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"type\";\nALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"type\";\nALTER TABLE \"selfservice_recovery_requests\" DROP COLUMN \"type\";\nALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810141652_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810141652_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"messages\" TEXT\n);\nINSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages FROM \"selfservice_login_requests\";\n\nDROP TABLE \"selfservice_login_requests\";\nALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";\nCREATE TABLE \"_selfservice_registration_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT\n);\nINSERT INTO \"_selfservice_registration_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages FROM \"selfservice_registration_requests\";\n\nDROP TABLE \"selfservice_registration_requests\";\nALTER TABLE \"_selfservice_registration_requests_tmp\" RENAME TO \"selfservice_registration_requests\";\nCREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state FROM \"selfservice_settings_requests\";\n\nDROP TABLE \"selfservice_settings_requests\";\nALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";\nCREATE TABLE \"_selfservice_recovery_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_recovery_requests_tmp\" (id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at FROM \"selfservice_recovery_requests\";\n\nDROP TABLE \"selfservice_recovery_requests\";\nALTER TABLE \"_selfservice_recovery_requests_tmp\" RENAME TO \"selfservice_recovery_requests\";\nCREATE TABLE \"_selfservice_verification_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"via\" TEXT NOT NULL DEFAULT 'email',\n\"success\" bool NOT NULL DEFAULT 'FALSE'\n);\nINSERT INTO \"_selfservice_verification_requests_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, via, success) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, via, success FROM \"selfservice_verification_requests\";\n\nDROP TABLE \"selfservice_verification_requests\";\nALTER TABLE \"_selfservice_verification_requests_tmp\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810141652_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810161022_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810161022_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810161022_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` RENAME TO `selfservice_login_requests`;\nALTER TABLE `selfservice_login_flow_methods` RENAME TO `selfservice_login_request_methods`;\nALTER TABLE `selfservice_registration_flow_methods` RENAME TO `selfservice_registration_request_methods`;\nALTER TABLE `selfservice_registration_flows` RENAME TO `selfservice_registration_requests`;\nALTER TABLE `selfservice_settings_flow_methods` RENAME TO `selfservice_settings_request_methods`;\nALTER TABLE `selfservice_settings_flows` RENAME TO `selfservice_settings_requests`;\nALTER TABLE `selfservice_recovery_flow_methods` RENAME TO `selfservice_recovery_request_methods`;\nALTER TABLE `selfservice_recovery_flows` RENAME TO `selfservice_recovery_requests`;\nALTER TABLE `selfservice_verification_flows` RENAME TO `selfservice_verification_requests`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810161022_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_request_methods` RENAME TO `selfservice_login_flow_methods`;\nALTER TABLE `selfservice_login_requests` RENAME TO `selfservice_login_flows`;\nALTER TABLE `selfservice_registration_request_methods` RENAME TO `selfservice_registration_flow_methods`;\nALTER TABLE `selfservice_registration_requests` RENAME TO `selfservice_registration_flows`;\nALTER TABLE `selfservice_settings_request_methods` RENAME TO `selfservice_settings_flow_methods`;\nALTER TABLE `selfservice_settings_requests` RENAME TO `selfservice_settings_flows`;\nALTER TABLE `selfservice_recovery_request_methods` RENAME TO `selfservice_recovery_flow_methods`;\nALTER TABLE `selfservice_recovery_requests` RENAME TO `selfservice_recovery_flows`;\nALTER TABLE `selfservice_verification_requests` RENAME TO `selfservice_verification_flows`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810161022_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\";\nALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\";\nALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\";\nALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\";\nALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\";\nALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810161022_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\";\nALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\";\nALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\";\nALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\";\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\";\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\";\nALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\";\nALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\";\nALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810161022_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\";\nALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\";\nALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\";\nALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\";\nALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\";\nALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810161022_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\";\nALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\";\nALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\";\nALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\";\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\";\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\";\nALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\";\nALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\";\nALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810162450_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810162450_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810162450_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` CHANGE `selfservice_login_flow_id` `selfservice_login_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_registration_flow_methods` CHANGE `selfservice_registration_flow_id` `selfservice_registration_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_settings_flow_methods` CHANGE `selfservice_settings_flow_id` `selfservice_settings_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_recovery_flow_methods` CHANGE `selfservice_recovery_flow_id` `selfservice_recovery_request_id` char(36) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810162450_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` CHANGE `selfservice_login_request_id` `selfservice_login_flow_id` char(36) NOT NULL;\nALTER TABLE `selfservice_registration_flow_methods` CHANGE `selfservice_registration_request_id` `selfservice_registration_flow_id` char(36) NOT NULL;\nALTER TABLE `selfservice_recovery_flow_methods` CHANGE `selfservice_recovery_request_id` `selfservice_recovery_flow_id` char(36) NOT NULL;\nALTER TABLE `selfservice_settings_flow_methods` CHANGE `selfservice_settings_request_id` `selfservice_settings_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810162450_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810162450_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810162450_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200810162450_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812124254_add_session_token.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"token\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812124254_add_session_token.cockroach.up.sql",
    "content": "DELETE FROM sessions;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"sessions\" RENAME COLUMN \"token\" TO \"_token_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"sessions\" SET \"token\" = \"_token_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"sessions\" DROP COLUMN \"_token_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812124254_add_session_token.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `token`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812124254_add_session_token.mysql.up.sql",
    "content": "DELETE FROM sessions;\nALTER TABLE `sessions` ADD COLUMN `token` VARCHAR (32);\nALTER TABLE `sessions` MODIFY `token` VARCHAR (32);\nCREATE UNIQUE INDEX `sessions_token_uq_idx` ON `sessions` (`token`);\nCREATE INDEX `sessions_token_idx` ON `sessions` (`token`);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812124254_add_session_token.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"token\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812124254_add_session_token.postgres.up.sql",
    "content": "DELETE FROM sessions;\nALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32);\nALTER TABLE \"sessions\" ALTER COLUMN \"token\" TYPE VARCHAR (32), ALTER COLUMN \"token\" DROP NOT NULL;\nCREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token);\nCREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812124254_add_session_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";\nDROP INDEX IF EXISTS \"sessions_token_idx\";\nCREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at FROM \"sessions\";\n\nDROP TABLE \"sessions\";\nALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812124254_add_session_token.sqlite3.up.sql",
    "content": "DELETE FROM sessions;\nALTER TABLE \"sessions\" ADD COLUMN \"token\" TEXT;\nCREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token FROM \"sessions\";\nDROP TABLE \"sessions\";\nALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";\nCREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token);\nCREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812160551_add_session_revoke.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"active\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812160551_add_session_revoke.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" boolean DEFAULT 'false';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812160551_add_session_revoke.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `active`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812160551_add_session_revoke.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `active` boolean DEFAULT false;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812160551_add_session_revoke.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"active\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812160551_add_session_revoke.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" boolean DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812160551_add_session_revoke.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";\nDROP INDEX IF EXISTS \"sessions_token_uq_idx\";\nCREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token);\nCREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token);\nINSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token FROM \"sessions\";\n\nDROP TABLE \"sessions\";\nALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200812160551_add_session_revoke.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" NUMERIC DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830121710_update_recovery_token.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830121710_update_recovery_token.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830121710_update_recovery_token.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` CHANGE `selfservice_recovery_flow_id` `selfservice_recovery_request_id` char(36) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830121710_update_recovery_token.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` CHANGE `selfservice_recovery_request_id` `selfservice_recovery_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830121710_update_recovery_token.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830121710_update_recovery_token.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830121710_update_recovery_token.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830121710_update_recovery_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130642_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"form\" json NOT NULL DEFAULT '{}';COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_verification_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"active_method\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"state\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" VARCHAR (16) NOT NULL DEFAULT 'email';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130642_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130642_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `form` JSON;\nUPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t);\nALTER TABLE `selfservice_verification_flows` MODIFY `form` JSON;\nDROP TABLE `selfservice_verification_flow_methods`;\nALTER TABLE `selfservice_verification_flows` DROP COLUMN `active_method`;\nALTER TABLE `selfservice_verification_flows` DROP COLUMN `state`;\nALTER TABLE `selfservice_verification_flows` ADD COLUMN `via` VARCHAR (16) NOT NULL DEFAULT 'email';\nALTER TABLE `selfservice_verification_flows` ADD COLUMN `success` bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130642_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `state` VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130642_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"form\" jsonb;\nUPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t);\nALTER TABLE \"selfservice_verification_flows\" ALTER COLUMN \"form\" TYPE jsonb, ALTER COLUMN \"form\" DROP NOT NULL;\nDROP TABLE \"selfservice_verification_flow_methods\";\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"active_method\";\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"state\";\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" VARCHAR (16) NOT NULL DEFAULT 'email';\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130642_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130642_add_verification_methods.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\";\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form'\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser'\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" TEXT NOT NULL DEFAULT 'email';\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130642_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130643_add_verification_methods.cockroach.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130643_add_verification_methods.mysql.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130643_add_verification_methods.postgres.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130643_add_verification_methods.sqlite3.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130644_add_verification_methods.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130644_add_verification_methods.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_verification_flow_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_verification_flow_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nALTER TABLE `selfservice_verification_flows` ADD COLUMN `active_method` VARCHAR (32);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130644_add_verification_methods.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" VARCHAR (32);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130644_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_verification_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" TEXT;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130645_add_verification_methods.cockroach.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130645_add_verification_methods.mysql.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130645_add_verification_methods.postgres.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130645_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130646_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"form\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"via\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"success\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130646_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `form`;\nALTER TABLE `selfservice_verification_flows` DROP COLUMN `via`;\nALTER TABLE `selfservice_verification_flows` DROP COLUMN `success`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130646_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"form\";\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"via\";\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"success\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830130646_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830154602_add_verification_token.cockroach.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830154602_add_verification_token.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"expires_at\" timestamp NOT NULL,\n\"issued_at\" timestamp NOT NULL,\n\"identity_verifiable_address_id\" UUID NOT NULL,\n\"selfservice_verification_flow_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_verification_tokens_identity_verifiable_addresses_id_fk\" FOREIGN KEY (\"identity_verifiable_address_id\") REFERENCES \"identity_verifiable_addresses\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_verification_tokens_selfservice_verification_flows_id_fk\" FOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flows\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830154602_add_verification_token.mysql.down.sql",
    "content": "DROP TABLE `identity_verification_tokens`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830154602_add_verification_token.mysql.up.sql",
    "content": "CREATE TABLE `identity_verification_tokens` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`token` VARCHAR (64) NOT NULL,\n`used` bool NOT NULL DEFAULT false,\n`used_at` DATETIME,\n`expires_at` DATETIME NOT NULL,\n`issued_at` DATETIME NOT NULL,\n`identity_verifiable_address_id` char(36) NOT NULL,\n`selfservice_verification_flow_id` char(36),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_verifiable_address_id`) REFERENCES `identity_verifiable_addresses` (`id`) ON DELETE cascade,\nFOREIGN KEY (`selfservice_verification_flow_id`) REFERENCES `selfservice_verification_flows` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_verification_tokens_token_uq_idx` ON `identity_verification_tokens` (`token`);\nCREATE INDEX `identity_verification_tokens_token_idx` ON `identity_verification_tokens` (`token`);\nCREATE INDEX `identity_verification_tokens_verifiable_address_id_idx` ON `identity_verification_tokens` (`identity_verifiable_address_id`);\nCREATE INDEX `identity_verification_tokens_verification_flow_id_idx` ON `identity_verification_tokens` (`selfservice_verification_flow_id`);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830154602_add_verification_token.postgres.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830154602_add_verification_token.postgres.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"expires_at\" timestamp NOT NULL,\n\"issued_at\" timestamp NOT NULL,\n\"identity_verifiable_address_id\" UUID NOT NULL,\n\"selfservice_verification_flow_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_verifiable_address_id\") REFERENCES \"identity_verifiable_addresses\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flows\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);\nCREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830154602_add_verification_token.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830154602_add_verification_token.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL,\n\"identity_verifiable_address_id\" char(36) NOT NULL,\n\"selfservice_verification_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);\nCREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830172221_recovery_token_expires.cockroach.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;\nALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"selfservice_recovery_flow_id\" UUID;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"identity_recovery_tokens\" SET \"selfservice_recovery_flow_id\" = \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" SET NOT NULL;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flows\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"expires_at\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"issued_at\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830172221_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"selfservice_recovery_flow_id\" UUID;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"identity_recovery_tokens\" SET \"selfservice_recovery_flow_id\" = \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flows\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE;COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830172221_recovery_token_expires.mysql.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;\nALTER TABLE `identity_recovery_tokens` MODIFY `selfservice_recovery_flow_id` char(36) NOT NULL;\nALTER TABLE `identity_recovery_tokens` DROP COLUMN `expires_at`;\nALTER TABLE `identity_recovery_tokens` DROP COLUMN `issued_at`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830172221_recovery_token_expires.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD COLUMN `expires_at` DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE `identity_recovery_tokens` ADD COLUMN `issued_at` DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE `identity_recovery_tokens` MODIFY `selfservice_recovery_flow_id` char(36);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830172221_recovery_token_expires.postgres.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;\nALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" TYPE UUID, ALTER COLUMN \"selfservice_recovery_flow_id\" SET NOT NULL;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"expires_at\";\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"issued_at\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830172221_recovery_token_expires.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" TYPE UUID, ALTER COLUMN \"selfservice_recovery_flow_id\" DROP NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830172221_recovery_token_expires.sqlite3.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";\nCREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nINSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at FROM \"identity_recovery_tokens\";\nDROP TABLE \"identity_recovery_tokens\";\nALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";\nCREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nINSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, issued_at FROM \"identity_recovery_tokens\";\n\nDROP TABLE \"identity_recovery_tokens\";\nALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";\nCREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nINSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at FROM \"identity_recovery_tokens\";\n\nDROP TABLE \"identity_recovery_tokens\";\nALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200830172221_recovery_token_expires.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";\nCREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nINSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at FROM \"identity_recovery_tokens\";\nDROP TABLE \"identity_recovery_tokens\";\nALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE identity_verifiable_addresses SET code = substr(md5(uuid_v4()), 0, 32) WHERE code IS NULL;\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;\nALTER TABLE \"identity_verifiable_addresses\" RENAME COLUMN \"code\" TO \"_code_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"identity_verifiable_addresses\" SET \"code\" = \"_code_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"code\" SET NOT NULL;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"_code_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" RENAME COLUMN \"expires_at\" TO \"_expires_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"identity_verifiable_addresses\" SET \"expires_at\" = \"_expires_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"_expires_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_uq_idx\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_code_idx\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"code\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"expires_at\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` ADD COLUMN `code` VARCHAR (32);\nALTER TABLE `identity_verifiable_addresses` ADD COLUMN `expires_at` DATETIME;\nUPDATE identity_verifiable_addresses SET code = LEFT(SHA2(RANDOM_BYTES(32), 256), 32) WHERE code IS NULL;\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;\nALTER TABLE `identity_verifiable_addresses` MODIFY `code` VARCHAR (32) NOT NULL;\nALTER TABLE `identity_verifiable_addresses` MODIFY `expires_at` DATETIME;\nCREATE UNIQUE INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses` (`code`);\nCREATE INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses` (`code`);\n"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses`;\nDROP INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses`;\nALTER TABLE `identity_verifiable_addresses` DROP COLUMN `code`;\nALTER TABLE `identity_verifiable_addresses` DROP COLUMN `expires_at`;"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32);\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp;\nUPDATE identity_verifiable_addresses SET code = substr(md5(random()::text), 0, 32) WHERE code IS NULL;\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;\nALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"code\" TYPE VARCHAR (32), ALTER COLUMN \"code\" SET NOT NULL;\nALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"expires_at\" TYPE timestamp, ALTER COLUMN \"expires_at\" DROP NOT NULL;\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_code_uq_idx\";\nDROP INDEX \"identity_verifiable_addresses_code_idx\";\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"code\";\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"expires_at\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" TEXT;\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" DATETIME;\nUPDATE identity_verifiable_addresses SET code = substr(hex(randomblob(32)), 0, 32) WHERE code IS NULL;\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"code\" TEXT NOT NULL,\n\"expires_at\" DATETIME,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at FROM \"identity_verifiable_addresses\";\nDROP TABLE \"identity_verifiable_addresses\";\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"code\" TEXT NOT NULL,\n\"expires_at\" DATETIME,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at FROM \"identity_verifiable_addresses\";\nDROP TABLE \"identity_verifiable_addresses\";\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_uq_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_code_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\";\n\nDROP TABLE \"identity_verifiable_addresses\";\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\";\n\nDROP TABLE \"identity_verifiable_addresses\";\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20201201161451_credential_types_values.cockroach.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20201201161451_credential_types_values.cockroach.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20201201161451_credential_types_values.mysql.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20201201161451_credential_types_values.mysql.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20201201161451_credential_types_values.postgres.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20201201161451_credential_types_values.postgres.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20201201161451_credential_types_values.sqlite3.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "oryx/popx/stub/migrations/legacy/20201201161451_credential_types_values.sqlite3.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "oryx/popx/stub/migrations/notx/20241031_notx.autocommit.down.sql",
    "content": "BEGIN;ROLLBACK;"
  },
  {
    "path": "oryx/popx/stub/migrations/notx/20241031_notx.autocommit.up.sql",
    "content": "BEGIN;ROLLBACK;"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000001_identities.down.fizz",
    "content": "drop_table(\"identity_credential_identifiers\")\ndrop_table(\"identity_credentials\")\ndrop_table(\"identity_credential_types\")\ndrop_table(\"identities\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000001_identities.up.fizz",
    "content": "create_table(\"identities\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"traits_schema_id\", \"string\", {\"size\": 2048})\n  t.Column(\"traits\", \"json\")\n}\n\ncreate_table(\"identity_credential_types\") {\n \tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"name\", \"string\", { \"size\": 32 })\n\n  t.DisableTimestamps()\n}\n\nadd_index(\"identity_credential_types\", \"name\", {\"unique\": true})\n\ncreate_table(\"identity_credentials\") {\n \tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"config\", \"json\")\n\n  t.Column(\"identity_credential_type_id\", \"uuid\")\n  t.Column(\"identity_id\", \"uuid\")\n\n  t.ForeignKey(\"identity_id\", {\"identities\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n  t.ForeignKey(\"identity_credential_type_id\", {\"identity_credential_types\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\ncreate_table(\"identity_credential_identifiers\") {\n \tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"identifier\", \"string\", {\"size\": 255})\n  t.Column(\"identity_credential_id\", \"uuid\")\n  t.ForeignKey(\"identity_credential_id\", {\"identity_credentials\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\nadd_index(\"identity_credential_identifiers\", \"identifier\", {\"unique\": true})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000002_requests.down.fizz",
    "content": "drop_table(\"selfservice_login_request_methods\")\ndrop_table(\"selfservice_login_requests\")\n\ndrop_table(\"selfservice_registration_request_methods\")\ndrop_table(\"selfservice_registration_requests\")\n\ndrop_table(\"selfservice_profile_management_requests\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000002_requests.up.fizz",
    "content": "create_table(\"selfservice_login_requests\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"request_url\", \"string\", {\"size\": 2048})\n  t.Column(\"issued_at\", \"timestamp\", { \"default_raw\": \"CURRENT_TIMESTAMP\" })\n  t.Column(\"expires_at\", \"timestamp\")\n  t.Column(\"active_method\", \"string\", {\"size\": 32})\n  t.Column(\"csrf_token\", \"string\")\n}\n\ncreate_table(\"selfservice_login_request_methods\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"method\", \"string\", {\"size\": 32})\n  t.Column(\"selfservice_login_request_id\", \"uuid\")\n  t.Column(\"config\", \"json\")\n\n  t.ForeignKey(\"selfservice_login_request_id\", {\"selfservice_login_requests\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\ncreate_table(\"selfservice_registration_requests\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"request_url\", \"string\", {\"size\": 2048})\n  t.Column(\"issued_at\", \"timestamp\", { \"default_raw\": \"CURRENT_TIMESTAMP\" })\n  t.Column(\"expires_at\", \"timestamp\")\n  t.Column(\"active_method\", \"string\", {\"size\": 32})\n  t.Column(\"csrf_token\", \"string\")\n}\n\ncreate_table(\"selfservice_registration_request_methods\") {\n  t.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"method\", \"string\", {\"size\": 32})\n  t.Column(\"selfservice_registration_request_id\", \"uuid\")\n  t.Column(\"config\", \"json\")\n\n  t.ForeignKey(\"selfservice_registration_request_id\", {\"selfservice_registration_requests\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\ncreate_table(\"selfservice_profile_management_requests\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"request_url\", \"string\", {\"size\": 2048})\n  t.Column(\"issued_at\", \"timestamp\", { \"default_raw\": \"CURRENT_TIMESTAMP\" })\n  t.Column(\"expires_at\", \"timestamp\")\n  t.Column(\"form\", \"json\")\n  t.Column(\"update_successful\", \"bool\")\n  t.Column(\"identity_id\", \"uuid\")\n\n  t.ForeignKey(\"identity_id\", {\"identities\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000003_sessions.down.fizz",
    "content": "drop_table(\"sessions\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000003_sessions.up.fizz",
    "content": "create_table(\"sessions\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"issued_at\", \"timestamp\", { \"default_raw\": \"CURRENT_TIMESTAMP\" })\n  t.Column(\"expires_at\", \"timestamp\")\n  t.Column(\"authenticated_at\", \"timestamp\")\n  t.Column(\"identity_id\", \"uuid\")\n\n  t.ForeignKey(\"identity_id\", {\"identities\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000004_errors.down.fizz",
    "content": "drop_table(\"selfservice_errors\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000004_errors.up.fizz",
    "content": "create_table(\"selfservice_errors\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"errors\", \"json\")\n  t.Column(\"seen_at\", \"timestamp\")\n  t.Column(\"was_seen\", \"bool\")\n}\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000005_identities.mysql.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255);\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000005_identities.mysql.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000006_courier.down.fizz",
    "content": "drop_table(\"courier_messages\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000006_courier.up.fizz",
    "content": "create_table(\"courier_messages\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\n  t.Column(\"type\", \"int\")\n  t.Column(\"status\", \"int\")\n\n  t.Column(\"body\", \"string\")\n  t.Column(\"subject\", \"string\")\n  t.Column(\"recipient\", \"string\")\n}\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000007_errors.down.fizz",
    "content": "drop_column(\"selfservice_errors\", \"csrf_token\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000007_errors.up.fizz",
    "content": "add_column(\"selfservice_errors\", \"csrf_token\", \"string\", {\"default\": \"\"})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000008_selfservice_verification.down.fizz",
    "content": "drop_table(\"selfservice_verification_requests\")\ndrop_table(\"identity_verifiable_addresses\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000008_selfservice_verification.up.fizz",
    "content": "create_table(\"identity_verifiable_addresses\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\n  t.Column(\"code\", \"string\", {\"size\": 32})\n  t.Column(\"status\", \"string\", {\"size\": 16})\n  t.Column(\"via\", \"string\", {\"size\": 16})\n  t.Column(\"verified\", \"bool\")\n\n  t.Column(\"value\", \"string\", {\"size\": 400})\n\n  t.Column(\"verified_at\", \"timestamp\", {\"null\": true})\n  t.Column(\"expires_at\", \"timestamp\", { \"default_raw\": \"CURRENT_TIMESTAMP\" })\n\n  t.Column(\"identity_id\", \"uuid\")\n  t.ForeignKey(\"identity_id\", {\"identities\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\nadd_index(\"identity_verifiable_addresses\", [\"code\"], { \"unique\": true, \"name\": \"identity_verifiable_addresses_code_uq_idx\" })\nadd_index(\"identity_verifiable_addresses\", [\"code\"], { \"name\": \"identity_verifiable_addresses_code_idx\" })\n\nadd_index(\"identity_verifiable_addresses\", [\"via\", \"value\"], { \"unique\": true, \"name\": \"identity_verifiable_addresses_status_via_uq_idx\" })\nadd_index(\"identity_verifiable_addresses\", [\"via\", \"value\"], { \"name\": \"identity_verifiable_addresses_status_via_idx\" })\n\ncreate_table(\"selfservice_verification_requests\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\n  t.Column(\"request_url\", \"string\", {\"size\": 2048})\n  t.Column(\"issued_at\", \"timestamp\", { \"default_raw\": \"CURRENT_TIMESTAMP\" })\n  t.Column(\"expires_at\", \"timestamp\")\n\n  t.Column(\"form\", \"json\")\n  t.Column(\"via\", \"string\", {\"size\": 16})\n  t.Column(\"csrf_token\", \"string\")\n  t.Column(\"success\", \"bool\")\n}\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000009_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255);\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000009_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000010_errors.down.fizz",
    "content": "sql(\"UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;\")\nchange_column(\"selfservice_errors\", \"seen_at\", \"timestamp\", { null: false })\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000010_errors.up.fizz",
    "content": "change_column(\"selfservice_errors\", \"seen_at\", \"timestamp\", { \"null\": true })\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000011_courier_body_type.down.fizz",
    "content": "<%#\n\nDo nothing because the change will not be able to preserve data and the change is insignificant as it's compatible\nwith both code bases (prior and after this change).\n\nWARNING: https://github.com/gobuffalo/fizz/issues/45#issuecomment-586833728\n%>\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000011_courier_body_type.up.fizz",
    "content": "change_column(\"courier_messages\", \"body\", \"text\", {})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000012_login_request_forced.down.fizz",
    "content": "drop_column(\"selfservice_login_requests\", \"forced\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20191100000012_login_request_forced.up.fizz",
    "content": "add_column(\"selfservice_login_requests\", \"forced\", \"bool\", {\"default\": false})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200317160354_create_profile_request_forms.down.fizz",
    "content": "{{ if or .IsPostgreSQL .IsMySQL .IsMariaDB }}\n  add_column(\"selfservice_profile_management_requests\", \"form\", \"json\", { \"null\": true })\n  sql(\"UPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t);\")\n  change_column(\"selfservice_profile_management_requests\", \"form\", \"json\", { \"null\": false })\n{{ end }}\n\n{{ if .IsCockroach }}\n  add_column(\"selfservice_profile_management_requests\", \"form\", \"json\", { \"default\": \"{}\" })\n{{ end }}\n\ndrop_table(\"selfservice_profile_management_request_methods\")\ndrop_column(\"selfservice_profile_management_requests\", \"active_method\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200317160354_create_profile_request_forms.up.fizz",
    "content": "create_table(\"selfservice_profile_management_request_methods\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"method\", \"string\", {\"size\": 32})\n  t.Column(\"selfservice_profile_management_request_id\", \"uuid\")\n  t.Column(\"config\", \"json\")\n}\n\nadd_column(\"selfservice_profile_management_requests\", \"active_method\", \"string\", {\"size\": 32, null: true})\n\nsql(\"INSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;\")\n\ndrop_column(\"selfservice_profile_management_requests\", \"form\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200401183443_continuity_containers.down.fizz",
    "content": "drop_table(\"continuity_containers\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200401183443_continuity_containers.up.fizz",
    "content": "create_table(\"continuity_containers\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\n\tt.Column(\"identity_id\", \"uuid\", {null: true})\n\n  t.Column(\"name\", \"string\")\n  t.Column(\"payload\", \"json\", {null: true})\n  t.Column(\"expires_at\", \"timestamp\")\n\n  t.ForeignKey(\"identity_id\", {\"identities\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200402142539_rename_profile_flows.down.fizz",
    "content": "rename_column(\"selfservice_settings_request_methods\", \"selfservice_settings_request_id\", \"selfservice_profile_management_request_id\")\n\nrename_table(\"selfservice_settings_request_methods\", \"selfservice_profile_management_request_methods\")\nrename_table(\"selfservice_settings_requests\", \"selfservice_profile_management_requests\")\n\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200402142539_rename_profile_flows.up.fizz",
    "content": "rename_column(\"selfservice_profile_management_request_methods\", \"selfservice_profile_management_request_id\", \"selfservice_settings_request_id\")\n\nrename_table(\"selfservice_profile_management_request_methods\", \"selfservice_settings_request_methods\")\nrename_table(\"selfservice_profile_management_requests\", \"selfservice_settings_requests\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200519101057_create_recovery_addresses.down.fizz",
    "content": "drop_table(\"identity_recovery_tokens\")\ndrop_table(\"selfservice_recovery_request_methods\")\ndrop_table(\"selfservice_recovery_requests\")\ndrop_table(\"identity_recovery_addresses\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200519101057_create_recovery_addresses.up.fizz",
    "content": "create_table(\"identity_recovery_addresses\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\n  t.Column(\"via\", \"string\", {\"size\": 16})\n  t.Column(\"value\", \"string\", {\"size\": 400})\n\n  t.Column(\"identity_id\", \"uuid\")\n  t.ForeignKey(\"identity_id\", {\"identities\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\nadd_index(\"identity_recovery_addresses\", [\"via\", \"value\"], { \"unique\": true, \"name\": \"identity_recovery_addresses_status_via_uq_idx\" })\nadd_index(\"identity_recovery_addresses\", [\"via\", \"value\"], { \"name\": \"identity_recovery_addresses_status_via_idx\" })\n\ncreate_table(\"selfservice_recovery_requests\") {\n  t.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"request_url\", \"string\", {\"size\": 2048})\n  t.Column(\"issued_at\", \"timestamp\", { \"default_raw\": \"CURRENT_TIMESTAMP\" })\n  t.Column(\"expires_at\", \"timestamp\")\n  t.Column(\"messages\", \"json\", {\"null\": true})\n  t.Column(\"active_method\", \"string\", {\"size\": 32, \"null\": true})\n  t.Column(\"csrf_token\", \"string\")\n  t.Column(\"state\", \"string\", {\"size\": 32})\n\n  t.Column(\"recovered_identity_id\", \"uuid\", { \"null\": true })\n  t.ForeignKey(\"recovered_identity_id\", {\"identities\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\ncreate_table(\"selfservice_recovery_request_methods\") {\n  t.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"method\", \"string\", {\"size\": 32})\n  t.Column(\"config\", \"json\")\n\n  t.Column(\"selfservice_recovery_request_id\", \"uuid\")\n  t.ForeignKey(\"selfservice_recovery_request_id\", {\"selfservice_recovery_requests\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\ncreate_table(\"identity_recovery_tokens\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\n  t.Column(\"token\", \"string\", {\"size\": 64})\n  t.Column(\"used\", \"bool\", {\"default\": false})\n  t.Column(\"used_at\", \"timestamp\", {\"null\": true})\n\n  t.Column(\"identity_recovery_address_id\", \"uuid\")\n  t.ForeignKey(\"identity_recovery_address_id\", {\"identity_recovery_addresses\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n\n  t.Column(\"selfservice_recovery_request_id\", \"uuid\")\n  t.ForeignKey(\"selfservice_recovery_request_id\", {\"selfservice_recovery_requests\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\nadd_index(\"identity_recovery_tokens\", [\"token\"], { \"unique\": true, \"name\": \"identity_recovery_addresses_code_uq_idx\" })\nadd_index(\"identity_recovery_tokens\", [\"token\"], { \"name\": \"identity_recovery_addresses_code_idx\" })\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200519101058_create_recovery_addresses.mysql.down.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64);\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200519101058_create_recovery_addresses.mysql.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200601101000_create_messages.down.fizz",
    "content": "drop_column(\"selfservice_settings_requests\", \"messages\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200601101000_create_messages.up.fizz",
    "content": "add_column(\"selfservice_settings_requests\", \"messages\", \"json\", {\"null\": true})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200601101001_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200601101001_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(32) BINARY;\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200605111551_messages.down.fizz",
    "content": "drop_column(\"selfservice_verification_requests\", \"messages\")\ndrop_column(\"selfservice_login_requests\", \"messages\")\ndrop_column(\"selfservice_registration_requests\", \"messages\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200605111551_messages.up.fizz",
    "content": "add_column(\"selfservice_verification_requests\", \"messages\", \"json\", {\"null\": true})\nadd_column(\"selfservice_login_requests\", \"messages\", \"json\", {\"null\": true})\nadd_column(\"selfservice_registration_requests\", \"messages\", \"json\", {\"null\": true})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200607165100_settings.down.fizz",
    "content": "drop_column(\"selfservice_settings_requests\", \"state\")\nadd_column(\"selfservice_settings_requests\", \"update_successful\", \"bool\", {\"default\": false})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200607165100_settings.up.fizz",
    "content": "add_column(\"selfservice_settings_requests\", \"state\", \"string\", {\"default\": \"show_form\"})\ndrop_column(\"selfservice_settings_requests\", \"update_successful\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200705105359_rename_identities_schema.down.fizz",
    "content": "rename_column(\"identities\", \"schema_id\", \"traits_schema_id\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200705105359_rename_identities_schema.up.fizz",
    "content": "rename_column(\"identities\", \"traits_schema_id\", \"schema_id\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200810141652_flow_type.down.fizz",
    "content": "drop_column(\"selfservice_login_requests\", \"type\")\ndrop_column(\"selfservice_registration_requests\", \"type\")\ndrop_column(\"selfservice_settings_requests\", \"type\")\ndrop_column(\"selfservice_recovery_requests\", \"type\")\ndrop_column(\"selfservice_verification_requests\", \"type\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200810141652_flow_type.up.fizz",
    "content": "add_column(\"selfservice_login_requests\", \"type\", \"string\", {\"default\": \"browser\", \"size\": 16})\nadd_column(\"selfservice_registration_requests\", \"type\", \"string\", {\"default\": \"browser\", \"size\": 16})\nadd_column(\"selfservice_settings_requests\", \"type\", \"string\", {\"default\": \"browser\", \"size\": 16})\nadd_column(\"selfservice_recovery_requests\", \"type\", \"string\", {\"default\": \"browser\", \"size\": 16})\nadd_column(\"selfservice_verification_requests\", \"type\", \"string\", {\"default\": \"browser\", \"size\": 16})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200810161022_flow_rename.down.fizz",
    "content": "rename_table(\"selfservice_login_flows\", \"selfservice_login_requests\")\nrename_table(\"selfservice_login_flow_methods\", \"selfservice_login_request_methods\")\n\nrename_table(\"selfservice_registration_flow_methods\", \"selfservice_registration_request_methods\")\nrename_table(\"selfservice_registration_flows\", \"selfservice_registration_requests\")\n\nrename_table(\"selfservice_settings_flow_methods\", \"selfservice_settings_request_methods\")\nrename_table(\"selfservice_settings_flows\", \"selfservice_settings_requests\")\n\nrename_table(\"selfservice_recovery_flow_methods\", \"selfservice_recovery_request_methods\")\nrename_table(\"selfservice_recovery_flows\", \"selfservice_recovery_requests\")\n\nrename_table(\"selfservice_verification_flows\", \"selfservice_verification_requests\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200810161022_flow_rename.up.fizz",
    "content": "rename_table(\"selfservice_login_request_methods\", \"selfservice_login_flow_methods\")\nrename_table(\"selfservice_login_requests\", \"selfservice_login_flows\")\n\nrename_table(\"selfservice_registration_request_methods\", \"selfservice_registration_flow_methods\")\nrename_table(\"selfservice_registration_requests\", \"selfservice_registration_flows\")\n\nrename_table(\"selfservice_settings_request_methods\", \"selfservice_settings_flow_methods\")\nrename_table(\"selfservice_settings_requests\", \"selfservice_settings_flows\")\n\nrename_table(\"selfservice_recovery_request_methods\", \"selfservice_recovery_flow_methods\")\nrename_table(\"selfservice_recovery_requests\", \"selfservice_recovery_flows\")\n\nrename_table(\"selfservice_verification_requests\", \"selfservice_verification_flows\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200810162450_flow_fields_rename.down.fizz",
    "content": "rename_column(\"selfservice_login_flow_methods\", \"selfservice_login_flow_id\", \"selfservice_login_request_id\")\n\nrename_column(\"selfservice_registration_flow_methods\", \"selfservice_registration_flow_id\", \"selfservice_registration_request_id\")\n\nrename_column(\"selfservice_settings_flow_methods\", \"selfservice_settings_flow_id\", \"selfservice_settings_request_id\")\n\nrename_column(\"selfservice_recovery_flow_methods\", \"selfservice_recovery_flow_id\", \"selfservice_recovery_request_id\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200810162450_flow_fields_rename.up.fizz",
    "content": "rename_column(\"selfservice_login_flow_methods\", \"selfservice_login_request_id\", \"selfservice_login_flow_id\")\n\nrename_column(\"selfservice_registration_flow_methods\", \"selfservice_registration_request_id\", \"selfservice_registration_flow_id\")\n\nrename_column(\"selfservice_recovery_flow_methods\", \"selfservice_recovery_request_id\", \"selfservice_recovery_flow_id\")\n\nrename_column(\"selfservice_settings_flow_methods\", \"selfservice_settings_request_id\", \"selfservice_settings_flow_id\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200812124254_add_session_token.down.fizz",
    "content": "drop_column(\"sessions\", \"token\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200812124254_add_session_token.up.fizz",
    "content": "sql(\"DELETE FROM sessions\")\n\nadd_column(\"sessions\", \"token\", \"string\", {\"size\": 32, \"null\": true})\nchange_column(\"sessions\", \"token\", \"string\", {\"size\": 32, \"null\": false})\n\nadd_index(\"sessions\", \"token\", {\"unique\": true, \"name\": \"sessions_token_uq_idx\"})\nadd_index(\"sessions\", \"token\", {\"name\": \"sessions_token_idx\" })\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200812160551_add_session_revoke.down.fizz",
    "content": "drop_column(\"sessions\", \"active\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200812160551_add_session_revoke.up.fizz",
    "content": "add_column(\"sessions\", \"active\", \"boolean\", {\"null\": false, \"default\": false})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830121710_update_recovery_token.down.fizz",
    "content": "rename_column(\"identity_recovery_tokens\", \"selfservice_recovery_flow_id\", \"selfservice_recovery_request_id\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830121710_update_recovery_token.up.fizz",
    "content": "rename_column(\"identity_recovery_tokens\", \"selfservice_recovery_request_id\", \"selfservice_recovery_flow_id\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130642_add_verification_methods.down.fizz",
    "content": "{{ if or .IsPostgreSQL .IsMySQL .IsMariaDB }}\n  add_column(\"selfservice_verification_flows\", \"form\", \"json\", { \"null\": true })\n  sql(\"UPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t);\")\n  change_column(\"selfservice_verification_flows\", \"form\", \"json\", { \"null\": false })\n{{ end }}\n\n{{ if .IsCockroach }}\n  add_column(\"selfservice_verification_flows\", \"form\", \"json\", { \"default\": \"{}\" })\n{{ end }}\n\ndrop_table(\"selfservice_verification_flow_methods\")\ndrop_column(\"selfservice_verification_flows\", \"active_method\")\ndrop_column(\"selfservice_verification_flows\", \"state\")\n\nadd_column(\"selfservice_verification_flows\", \"via\", \"string\", {\"size\": 16, \"default\": \"email\"})\nadd_column(\"selfservice_verification_flows\", \"success\", \"bool\", {\"default_raw\": \"FALSE\"})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130642_add_verification_methods.up.fizz",
    "content": "add_column(\"selfservice_verification_flows\", \"state\", \"string\", {\"default\": \"show_form\"})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130643_add_verification_methods.down.fizz",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130643_add_verification_methods.up.fizz",
    "content": "sql(\"UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130644_add_verification_methods.down.fizz",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130644_add_verification_methods.up.fizz",
    "content": "create_table(\"selfservice_verification_flow_methods\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n  t.Column(\"method\", \"string\", {\"size\": 32})\n  t.Column(\"selfservice_verification_flow_id\", \"uuid\")\n  t.Column(\"config\", \"json\")\n}\n\nadd_column(\"selfservice_verification_flows\", \"active_method\", \"string\", {\"size\": 32, null: true})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130645_add_verification_methods.down.fizz",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130645_add_verification_methods.up.fizz",
    "content": "sql(\"INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130646_add_verification_methods.down.fizz",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830130646_add_verification_methods.up.fizz",
    "content": "drop_column(\"selfservice_verification_flows\", \"form\")\ndrop_column(\"selfservice_verification_flows\", \"via\")\ndrop_column(\"selfservice_verification_flows\", \"success\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830154602_add_verification_token.down.fizz",
    "content": "drop_table(\"identity_verification_tokens\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830154602_add_verification_token.up.fizz",
    "content": "create_table(\"identity_verification_tokens\") {\n\tt.Column(\"id\", \"uuid\", {primary: true})\n\n  t.Column(\"token\", \"string\", {\"size\": 64})\n  t.Column(\"used\", \"bool\", {\"default\": false})\n  t.Column(\"used_at\", \"timestamp\", {\"null\": true})\n  t.Column(\"expires_at\", \"timestamp\")\n  t.Column(\"issued_at\", \"timestamp\")\n\n  t.Column(\"identity_verifiable_address_id\", \"uuid\")\n  t.ForeignKey(\"identity_verifiable_address_id\", {\"identity_verifiable_addresses\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n\n  t.Column(\"selfservice_verification_flow_id\", \"uuid\", {\"null\": true})\n  t.ForeignKey(\"selfservice_verification_flow_id\", {\"selfservice_verification_flows\": [\"id\"]}, {\"on_delete\": \"cascade\"})\n}\n\nadd_index(\"identity_verification_tokens\", [\"token\"], { \"unique\": true, \"name\": \"identity_verification_tokens_token_uq_idx\" })\nadd_index(\"identity_verification_tokens\", [\"token\"], { \"name\": \"identity_verification_tokens_token_idx\" })\n\nadd_index(\"identity_verification_tokens\", [\"identity_verifiable_address_id\"], { \"name\": \"identity_verification_tokens_verifiable_address_id_idx\" })\nadd_index(\"identity_verification_tokens\", [\"selfservice_verification_flow_id\"], { \"name\": \"identity_verification_tokens_verification_flow_id_idx\" })\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830172221_recovery_token_expires.down.fizz",
    "content": "sql(\"DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL\")\nchange_column(\"identity_recovery_tokens\", \"selfservice_recovery_flow_id\", \"uuid\")\ndrop_column(\"identity_recovery_tokens\", \"expires_at\")\ndrop_column(\"identity_recovery_tokens\", \"issued_at\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200830172221_recovery_token_expires.up.fizz",
    "content": "add_column(\"identity_recovery_tokens\", \"expires_at\", \"timestamp\", { \"default\": \"2000-01-01 00:00:00\" })\nadd_column(\"identity_recovery_tokens\", \"issued_at\", \"timestamp\", { \"default\": \"2000-01-01 00:00:00\" })\nchange_column(\"identity_recovery_tokens\", \"selfservice_recovery_flow_id\", \"uuid\", {\"null\": true})\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200831110752_identity_verifiable_address_remove_code.down.fizz",
    "content": "add_column(\"identity_verifiable_addresses\", \"code\", \"string\", {\"size\": 32, \"null\": true})\nadd_column(\"identity_verifiable_addresses\", \"expires_at\", \"timestamp\", { \"null\": true })\n\n{{ if .IsSQLite }}\n  sql(\"UPDATE identity_verifiable_addresses SET code = substr(hex(randomblob(32)), 0, 32) WHERE code IS NULL\")\n  sql(\"UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL\")\n{{ end }}\n\n{{ if or .IsMySQL .IsMariaDB }}\n  sql(\"UPDATE identity_verifiable_addresses SET code = LEFT(SHA2(RANDOM_BYTES(32), 256), 32) WHERE code IS NULL\")\n  sql(\"UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL\")\n{{ end }}\n\n{{ if .IsPostgreSQL }}\n  sql(\"UPDATE identity_verifiable_addresses SET code = substr(md5(random()::text), 0, 32) WHERE code IS NULL\")\n  sql(\"UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL\")\n{{ end }}\n\n{{ if .IsCockroach }}\n  sql(\"UPDATE identity_verifiable_addresses SET code = substr(md5(uuid_v4()), 0, 32) WHERE code IS NULL\")\n  sql(\"UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL\")\n{{ end }}\n\nchange_column(\"identity_verifiable_addresses\", \"code\", \"string\", {\"size\": 32})\nchange_column(\"identity_verifiable_addresses\", \"expires_at\", \"timestamp\", { \"null\": false })\n\nadd_index(\"identity_verifiable_addresses\", [\"code\"], { \"unique\": true, \"name\": \"identity_verifiable_addresses_code_uq_idx\" })\nadd_index(\"identity_verifiable_addresses\", [\"code\"], { \"name\": \"identity_verifiable_addresses_code_idx\" })\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20200831110752_identity_verifiable_address_remove_code.up.fizz",
    "content": "drop_index(\"identity_verifiable_addresses\", \"identity_verifiable_addresses_code_uq_idx\")\ndrop_index(\"identity_verifiable_addresses\", \"identity_verifiable_addresses_code_idx\")\n\ndrop_column(\"identity_verifiable_addresses\", \"code\")\ndrop_column(\"identity_verifiable_addresses\", \"expires_at\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20201201161451_credential_types_values.down.fizz",
    "content": "sql(\"DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc'\")\n"
  },
  {
    "path": "oryx/popx/stub/migrations/source/20201201161451_credential_types_values.up.fizz",
    "content": "sql(\"INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password')\")\nsql(\"INSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc')\")\n\n"
  },
  {
    "path": "oryx/popx/stub/migrations/templating/0_sql_create_tablename_template.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/templating/0_sql_create_tablename_template.up.sql",
    "content": "CREATE TABLE {{ identifier .Parameters.tableName }} ( \"id\" UUID NOT NULL, PRIMARY KEY (\"id\"));\n"
  },
  {
    "path": "oryx/popx/stub/migrations/testdata/20220513_testdata.invalid",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/testdata/20220513_testdata.sql",
    "content": "INSERT INTO testdata (Data) VALUES ('testdata');"
  },
  {
    "path": "oryx/popx/stub/migrations/testdata/20220514_testdata.sql",
    "content": "-- empty migrations should not error"
  },
  {
    "path": "oryx/popx/stub/migrations/testdata/invalid",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/testdata/invalid_testdata.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/testdata_migrations/20220513_create_table.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/testdata_migrations/20220513_create_table.up.sql",
    "content": "CREATE TABLE \"testdata\" (\n  \"data\" character varying(255) NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000000_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identities\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000000_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"traits_schema_id\" VARCHAR (2048) NOT NULL,\n\"traits\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000000_identities.mysql.down.sql",
    "content": "DROP TABLE `identities`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000000_identities.mysql.up.sql",
    "content": "CREATE TABLE `identities` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`traits_schema_id` VARCHAR (2048) NOT NULL,\n`traits` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000000_identities.postgres.down.sql",
    "content": "DROP TABLE \"identities\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000000_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"traits_schema_id\" VARCHAR (2048) NOT NULL,\n\"traits\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000000_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identities\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000000_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" TEXT PRIMARY KEY,\n\"traits_schema_id\" TEXT NOT NULL,\n\"traits\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000001_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identity_credential_types\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000001_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_credential_types\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"name\" VARCHAR (32) NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000001_identities.mysql.down.sql",
    "content": "DROP TABLE `identity_credential_types`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000001_identities.mysql.up.sql",
    "content": "CREATE TABLE `identity_credential_types` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`name` VARCHAR (32) NOT NULL\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000001_identities.postgres.down.sql",
    "content": "DROP TABLE \"identity_credential_types\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000001_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identity_credential_types\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"name\" VARCHAR (32) NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000001_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_credential_types\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000001_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_credential_types\" (\n\"id\" TEXT PRIMARY KEY,\n\"name\" TEXT NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000002_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identity_credentials\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000002_identities.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000002_identities.mysql.down.sql",
    "content": "DROP TABLE `identity_credentials`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000002_identities.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_credential_types_name_idx` ON `identity_credential_types` (`name`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000002_identities.postgres.down.sql",
    "content": "DROP TABLE \"identity_credentials\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000002_identities.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000002_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_credentials\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000002_identities.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000003_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000003_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_credentials\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"config\" json NOT NULL,\n\"identity_credential_type_id\" UUID NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_credentials_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_credentials_identity_credential_types_id_fk\" FOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000003_identities.mysql.down.sql",
    "content": "DROP TABLE `identity_credential_identifiers`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000003_identities.mysql.up.sql",
    "content": "CREATE TABLE `identity_credentials` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`config` JSON NOT NULL,\n`identity_credential_type_id` char(36) NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade,\nFOREIGN KEY (`identity_credential_type_id`) REFERENCES `identity_credential_types` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000003_identities.postgres.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000003_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identity_credentials\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"config\" jsonb NOT NULL,\n\"identity_credential_type_id\" UUID NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000003_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000003_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_credentials\" (\n\"id\" TEXT PRIMARY KEY,\n\"config\" TEXT NOT NULL,\n\"identity_credential_type_id\" char(36) NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade,\nFOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000004_identities.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000004_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_credential_identifiers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identifier\" VARCHAR (255) NOT NULL,\n\"identity_credential_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_credential_identifiers_identity_credentials_id_fk\" FOREIGN KEY (\"identity_credential_id\") REFERENCES \"identity_credentials\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000004_identities.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000004_identities.mysql.up.sql",
    "content": "CREATE TABLE `identity_credential_identifiers` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`identifier` VARCHAR (255) NOT NULL,\n`identity_credential_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_credential_id`) REFERENCES `identity_credentials` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000004_identities.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000004_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identity_credential_identifiers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identifier\" VARCHAR (255) NOT NULL,\n\"identity_credential_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_credential_id\") REFERENCES \"identity_credentials\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000004_identities.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000004_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_credential_identifiers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000005_identities.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000005_identities.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000005_identities.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000005_identities.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_credential_identifiers_identifier_idx` ON `identity_credential_identifiers` (`identifier`);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000005_identities.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000005_identities.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000005_identities.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000001000005_identities.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000000_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000000_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000000_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_profile_management_requests`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000000_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_login_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`active_method` VARCHAR (32) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000000_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000000_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000000_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000000_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000001_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000001_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_login_request_methods_selfservice_login_requests_id_fk\" FOREIGN KEY (\"selfservice_login_request_id\") REFERENCES \"selfservice_login_requests\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000001_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_registration_requests`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000001_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_login_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_login_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_login_request_id`) REFERENCES `selfservice_login_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000001_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000001_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_login_request_id\") REFERENCES \"selfservice_login_requests\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000001_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000001_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_login_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_login_request_id) REFERENCES selfservice_login_requests (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000002_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_registration_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000002_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000002_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_registration_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000002_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_registration_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`active_method` VARCHAR (32) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000002_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_registration_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000002_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000002_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_registration_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000002_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000003_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000003_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_registration_request_methods_selfservice_registration_requests_id_fk\" FOREIGN KEY (\"selfservice_registration_request_id\") REFERENCES \"selfservice_registration_requests\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000003_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_login_requests`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000003_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_registration_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_registration_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_registration_request_id`) REFERENCES `selfservice_registration_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000003_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000003_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_registration_request_id\") REFERENCES \"selfservice_registration_requests\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000003_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000003_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_registration_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_registration_request_id) REFERENCES selfservice_registration_requests (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000004_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000004_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" json NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_profile_management_requests_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000004_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_login_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000004_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_profile_management_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`form` JSON NOT NULL,\n`update_successful` bool NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000004_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000004_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" jsonb NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000004_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000002000004_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000003000000_sessions.cockroach.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000003000000_sessions.cockroach.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"authenticated_at\" timestamp NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"sessions_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000003000000_sessions.mysql.down.sql",
    "content": "DROP TABLE `sessions`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000003000000_sessions.mysql.up.sql",
    "content": "CREATE TABLE `sessions` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`authenticated_at` DATETIME NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000003000000_sessions.postgres.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000003000000_sessions.postgres.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"authenticated_at\" timestamp NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000003000000_sessions.sqlite3.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000003000000_sessions.sqlite3.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000004000000_errors.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000004000000_errors.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"errors\" json NOT NULL,\n\"seen_at\" timestamp NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000004000000_errors.mysql.down.sql",
    "content": "DROP TABLE `selfservice_errors`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000004000000_errors.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_errors` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`errors` JSON NOT NULL,\n`seen_at` DATETIME NOT NULL,\n`was_seen` bool NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000004000000_errors.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000004000000_errors.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"errors\" jsonb NOT NULL,\n\"seen_at\" timestamp NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000004000000_errors.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000004000000_errors.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000005000000_identities.mysql.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000005000000_identities.mysql.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000005000001_identities.mysql.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000005000001_identities.mysql.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000006000000_courier.cockroach.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000006000000_courier.cockroach.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"type\" int NOT NULL,\n\"status\" int NOT NULL,\n\"body\" VARCHAR (255) NOT NULL,\n\"subject\" VARCHAR (255) NOT NULL,\n\"recipient\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000006000000_courier.mysql.down.sql",
    "content": "DROP TABLE `courier_messages`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000006000000_courier.mysql.up.sql",
    "content": "CREATE TABLE `courier_messages` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`type` INTEGER NOT NULL,\n`status` INTEGER NOT NULL,\n`body` VARCHAR (255) NOT NULL,\n`subject` VARCHAR (255) NOT NULL,\n`recipient` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000006000000_courier.postgres.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000006000000_courier.postgres.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"type\" int NOT NULL,\n\"status\" int NOT NULL,\n\"body\" VARCHAR (255) NOT NULL,\n\"subject\" VARCHAR (255) NOT NULL,\n\"recipient\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000006000000_courier.sqlite3.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000006000000_courier.sqlite3.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000000_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"csrf_token\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000000_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" VARCHAR (255) NOT NULL DEFAULT '';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000000_errors.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_errors` DROP COLUMN `csrf_token`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000000_errors.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` ADD COLUMN `csrf_token` VARCHAR (255) NOT NULL DEFAULT \"\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000000_errors.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"csrf_token\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000000_errors.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" VARCHAR (255) NOT NULL DEFAULT '';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000000_errors.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000000_errors.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" TEXT NOT NULL DEFAULT '';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000001_errors.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_errors\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000001_errors.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000002_errors.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at) SELECT id, errors, seen_at, was_seen, created_at, updated_at FROM \"selfservice_errors\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000002_errors.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000003_errors.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000007000003_errors.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000000_selfservice_verification.cockroach.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000000_selfservice_verification.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"code\" VARCHAR (32) NOT NULL,\n\"status\" VARCHAR (16) NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"verified_at\" timestamp,\n\"expires_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_verifiable_addresses_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000000_selfservice_verification.mysql.down.sql",
    "content": "DROP TABLE `identity_verifiable_addresses`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000000_selfservice_verification.mysql.up.sql",
    "content": "CREATE TABLE `identity_verifiable_addresses` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`code` VARCHAR (32) NOT NULL,\n`status` VARCHAR (16) NOT NULL,\n`via` VARCHAR (16) NOT NULL,\n`verified` bool NOT NULL,\n`value` VARCHAR (400) NOT NULL,\n`verified_at` DATETIME,\n`expires_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000000_selfservice_verification.postgres.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000000_selfservice_verification.postgres.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"code\" VARCHAR (32) NOT NULL,\n\"status\" VARCHAR (16) NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"verified_at\" timestamp,\n\"expires_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000000_selfservice_verification.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000000_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"code\" TEXT NOT NULL,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000001_selfservice_verification.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000001_selfservice_verification.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000001_selfservice_verification.mysql.down.sql",
    "content": "DROP TABLE `selfservice_verification_requests`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000001_selfservice_verification.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses` (`code`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000001_selfservice_verification.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000001_selfservice_verification.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000001_selfservice_verification.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000001_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000002_selfservice_verification.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000002_selfservice_verification.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000002_selfservice_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000002_selfservice_verification.mysql.up.sql",
    "content": "CREATE INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses` (`code`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000002_selfservice_verification.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000002_selfservice_verification.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000002_selfservice_verification.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000002_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000003_selfservice_verification.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000003_selfservice_verification.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000003_selfservice_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000003_selfservice_verification.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_verifiable_addresses_status_via_uq_idx` ON `identity_verifiable_addresses` (`via`, `value`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000003_selfservice_verification.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000003_selfservice_verification.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000003_selfservice_verification.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000003_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000004_selfservice_verification.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000004_selfservice_verification.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000004_selfservice_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000004_selfservice_verification.mysql.up.sql",
    "content": "CREATE INDEX `identity_verifiable_addresses_status_via_idx` ON `identity_verifiable_addresses` (`via`, `value`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000004_selfservice_verification.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000004_selfservice_verification.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000004_selfservice_verification.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000004_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000005_selfservice_verification.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000005_selfservice_verification.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" json NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000005_selfservice_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000005_selfservice_verification.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_verification_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`form` JSON NOT NULL,\n`via` VARCHAR (16) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`success` bool NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000005_selfservice_verification.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000005_selfservice_verification.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" jsonb NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000005_selfservice_verification.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000008000005_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000009000000_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000009000000_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000009000001_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000009000001_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000000_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"_seen_at_tmp\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000000_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" RENAME COLUMN \"seen_at\" TO \"_seen_at_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000000_errors.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_errors` MODIFY `seen_at` DATETIME;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000000_errors.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` MODIFY `seen_at` DATETIME;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000000_errors.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ALTER COLUMN \"seen_at\" TYPE timestamp, ALTER COLUMN \"seen_at\" DROP NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000000_errors.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ALTER COLUMN \"seen_at\" TYPE timestamp, ALTER COLUMN \"seen_at\" DROP NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000000_errors.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000000_errors.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000001_errors.cockroach.down.sql",
    "content": "UPDATE \"selfservice_errors\" SET \"seen_at\" = \"_seen_at_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000001_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"seen_at\" timestamp"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000001_errors.mysql.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000001_errors.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000001_errors.postgres.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000001_errors.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000001_errors.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_errors\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000001_errors.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000002_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"seen_at\" timestamp"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000002_errors.cockroach.up.sql",
    "content": "UPDATE \"selfservice_errors\" SET \"seen_at\" = \"_seen_at_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000002_errors.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000002_errors.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_errors\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000003_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" RENAME COLUMN \"seen_at\" TO \"_seen_at_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000003_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"_seen_at_tmp\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000003_errors.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000003_errors.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000004_errors.cockroach.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000004_errors.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000004_errors.sqlite3.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000010000004_errors.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000000_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000000_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" RENAME COLUMN \"body\" TO \"_body_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000000_courier_body_type.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000000_courier_body_type.mysql.up.sql",
    "content": "ALTER TABLE `courier_messages` MODIFY `body` text NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000000_courier_body_type.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000000_courier_body_type.postgres.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ALTER COLUMN \"body\" TYPE text, ALTER COLUMN \"body\" SET NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000000_courier_body_type.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000000_courier_body_type.sqlite3.up.sql",
    "content": "CREATE TABLE \"_courier_messages_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000001_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000001_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"body\" text"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000001_courier_body_type.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000001_courier_body_type.sqlite3.up.sql",
    "content": "INSERT INTO \"_courier_messages_tmp\" (id, type, status, body, subject, recipient, created_at, updated_at) SELECT id, type, status, body, subject, recipient, created_at, updated_at FROM \"courier_messages\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000002_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000002_courier_body_type.cockroach.up.sql",
    "content": "UPDATE \"courier_messages\" SET \"body\" = \"_body_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000002_courier_body_type.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000002_courier_body_type.sqlite3.up.sql",
    "content": "DROP TABLE \"courier_messages\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000003_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000003_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ALTER COLUMN \"body\" SET NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000003_courier_body_type.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000003_courier_body_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"_courier_messages_tmp\" RENAME TO \"courier_messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000004_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000011000004_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP COLUMN \"_body_tmp\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000000_login_request_forced.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"forced\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000000_login_request_forced.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000000_login_request_forced.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `forced`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000000_login_request_forced.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `forced` bool NOT NULL DEFAULT false;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000000_login_request_forced.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"forced\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000000_login_request_forced.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000000_login_request_forced.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000000_login_request_forced.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000001_login_request_forced.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000001_login_request_forced.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000002_login_request_forced.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at FROM \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000002_login_request_forced.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000003_login_request_forced.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20191100000012000003_login_request_forced.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000000_create_profile_request_forms.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"active_method\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000000_create_profile_request_forms.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_profile_management_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000000_create_profile_request_forms.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` DROP COLUMN `active_method`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000000_create_profile_request_forms.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_profile_management_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_profile_management_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000000_create_profile_request_forms.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"active_method\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000000_create_profile_request_forms.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_profile_management_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000000_create_profile_request_forms.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000000_create_profile_request_forms.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_profile_management_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000001_create_profile_request_forms.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000001_create_profile_request_forms.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000001_create_profile_request_forms.mysql.down.sql",
    "content": "DROP TABLE `selfservice_profile_management_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000001_create_profile_request_forms.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` ADD COLUMN `active_method` VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000001_create_profile_request_forms.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000001_create_profile_request_forms.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000001_create_profile_request_forms.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_profile_management_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000001_create_profile_request_forms.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" TEXT"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000002_create_profile_request_forms.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"form\" json NOT NULL DEFAULT '{}'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000002_create_profile_request_forms.cockroach.up.sql",
    "content": "INSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000002_create_profile_request_forms.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` MODIFY `form` JSON"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000002_create_profile_request_forms.mysql.up.sql",
    "content": "INSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000002_create_profile_request_forms.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ALTER COLUMN \"form\" TYPE jsonb, ALTER COLUMN \"form\" DROP NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000002_create_profile_request_forms.postgres.up.sql",
    "content": "INSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000002_create_profile_request_forms.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, update_successful) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, update_successful FROM \"selfservice_profile_management_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000002_create_profile_request_forms.sqlite3.up.sql",
    "content": "INSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000003_create_profile_request_forms.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000003_create_profile_request_forms.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"form\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000003_create_profile_request_forms.mysql.down.sql",
    "content": "UPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000003_create_profile_request_forms.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` DROP COLUMN `form`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000003_create_profile_request_forms.postgres.down.sql",
    "content": "UPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000003_create_profile_request_forms.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"form\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000003_create_profile_request_forms.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"update_successful\" bool NOT NULL DEFAULT 'false',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000003_create_profile_request_forms.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000004_create_profile_request_forms.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` ADD COLUMN `form` JSON"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000004_create_profile_request_forms.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000004_create_profile_request_forms.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"form\" jsonb"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000004_create_profile_request_forms.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000004_create_profile_request_forms.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000004_create_profile_request_forms.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method) SELECT id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method FROM \"selfservice_profile_management_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000005_create_profile_request_forms.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000005_create_profile_request_forms.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_profile_management_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000006_create_profile_request_forms.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200317160354000006_create_profile_request_forms.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200401183443000000_continuity_containers.cockroach.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200401183443000000_continuity_containers.cockroach.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identity_id\" UUID,\n\"name\" VARCHAR (255) NOT NULL,\n\"payload\" json,\n\"expires_at\" timestamp NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"continuity_containers_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200401183443000000_continuity_containers.mysql.down.sql",
    "content": "DROP TABLE `continuity_containers`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200401183443000000_continuity_containers.mysql.up.sql",
    "content": "CREATE TABLE `continuity_containers` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`identity_id` char(36),\n`name` VARCHAR (255) NOT NULL,\n`payload` JSON,\n`expires_at` DATETIME NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200401183443000000_continuity_containers.postgres.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200401183443000000_continuity_containers.postgres.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identity_id\" UUID,\n\"name\" VARCHAR (255) NOT NULL,\n\"payload\" jsonb,\n\"expires_at\" timestamp NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200401183443000000_continuity_containers.sqlite3.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200401183443000000_continuity_containers.sqlite3.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identity_id\" char(36),\n\"name\" TEXT NOT NULL,\n\"payload\" TEXT,\n\"expires_at\" DATETIME NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000000_rename_profile_flows.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000000_rename_profile_flows.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000000_rename_profile_flows.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` RENAME TO `selfservice_profile_management_requests`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000000_rename_profile_flows.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_request_methods` CHANGE `selfservice_profile_management_request_id` `selfservice_settings_request_id` char(36) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000000_rename_profile_flows.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000000_rename_profile_flows.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000000_rename_profile_flows.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000000_rename_profile_flows.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000001_rename_profile_flows.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000001_rename_profile_flows.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000001_rename_profile_flows.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_request_methods` RENAME TO `selfservice_profile_management_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000001_rename_profile_flows.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_request_methods` RENAME TO `selfservice_settings_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000001_rename_profile_flows.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000001_rename_profile_flows.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000001_rename_profile_flows.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000001_rename_profile_flows.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000002_rename_profile_flows.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000002_rename_profile_flows.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000002_rename_profile_flows.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_request_methods` CHANGE `selfservice_settings_request_id` `selfservice_profile_management_request_id` char(36) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000002_rename_profile_flows.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` RENAME TO `selfservice_settings_requests`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000002_rename_profile_flows.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000002_rename_profile_flows.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000002_rename_profile_flows.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200402142539000002_rename_profile_flows.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000000_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000000_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"via\" VARCHAR (16) NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_recovery_addresses_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000000_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `identity_recovery_addresses`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000000_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `identity_recovery_addresses` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`via` VARCHAR (16) NOT NULL,\n`value` VARCHAR (400) NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000000_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000000_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"via\" VARCHAR (16) NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000000_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000000_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"via\" TEXT NOT NULL,\n\"value\" TEXT NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000001_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000001_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000001_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `selfservice_recovery_requests`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000001_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_recovery_addresses_status_via_uq_idx` ON `identity_recovery_addresses` (`via`, `value`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000001_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000001_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000001_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000001_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000002_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000002_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000002_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `selfservice_recovery_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000002_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE INDEX `identity_recovery_addresses_status_via_idx` ON `identity_recovery_addresses` (`via`, `value`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000002_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000002_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000002_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000002_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000003_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000003_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"messages\" json,\n\"active_method\" VARCHAR (32),\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"state\" VARCHAR (32) NOT NULL,\n\"recovered_identity_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_recovery_requests_identities_id_fk\" FOREIGN KEY (\"recovered_identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000003_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `identity_recovery_tokens`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000003_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_recovery_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`messages` JSON,\n`active_method` VARCHAR (32),\n`csrf_token` VARCHAR (255) NOT NULL,\n`state` VARCHAR (32) NOT NULL,\n`recovered_identity_id` char(36),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`recovered_identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000003_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000003_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"messages\" jsonb,\n\"active_method\" VARCHAR (32),\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"state\" VARCHAR (32) NOT NULL,\n\"recovered_identity_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"recovered_identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000003_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000003_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000004_create_recovery_addresses.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000004_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"config\" json NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_recovery_request_methods_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000004_create_recovery_addresses.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000004_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_recovery_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`config` JSON NOT NULL,\n`selfservice_recovery_request_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_recovery_request_id`) REFERENCES `selfservice_recovery_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000004_create_recovery_addresses.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000004_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"config\" jsonb NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000004_create_recovery_addresses.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000004_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"config\" TEXT NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000005_create_recovery_addresses.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000005_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_recovery_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"identity_recovery_address_id\" UUID NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_recovery_tokens_identity_recovery_addresses_id_fk\" FOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000005_create_recovery_addresses.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000005_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `identity_recovery_tokens` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`token` VARCHAR (64) NOT NULL,\n`used` bool NOT NULL DEFAULT false,\n`used_at` DATETIME,\n`identity_recovery_address_id` char(36) NOT NULL,\n`selfservice_recovery_request_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_recovery_address_id`) REFERENCES `identity_recovery_addresses` (`id`) ON DELETE cascade,\nFOREIGN KEY (`selfservice_recovery_request_id`) REFERENCES `selfservice_recovery_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000005_create_recovery_addresses.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000005_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"identity_recovery_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"identity_recovery_address_id\" UUID NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000005_create_recovery_addresses.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000005_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_recovery_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000006_create_recovery_addresses.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000006_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000006_create_recovery_addresses.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000006_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_recovery_addresses_code_uq_idx` ON `identity_recovery_tokens` (`token`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000006_create_recovery_addresses.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000006_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000006_create_recovery_addresses.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000006_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000007_create_recovery_addresses.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000007_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000007_create_recovery_addresses.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000007_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE INDEX `identity_recovery_addresses_code_idx` ON `identity_recovery_tokens` (`token`);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000007_create_recovery_addresses.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000007_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000007_create_recovery_addresses.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101057000007_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101058000000_create_recovery_addresses.mysql.down.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101058000000_create_recovery_addresses.mysql.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101058000001_create_recovery_addresses.mysql.down.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200519101058000001_create_recovery_addresses.mysql.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000000_create_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000000_create_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000000_create_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000000_create_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000000_create_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000000_create_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000000_create_messages.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000000_create_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000001_create_messages.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000001_create_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000002_create_messages.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful FROM \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000002_create_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000003_create_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"update_successful\" bool NOT NULL DEFAULT 'false',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101000000003_create_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101001000000_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101001000000_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(32) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101001000001_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200601101001000001_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(32) BINARY"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000000_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000000_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" json"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000000_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000000_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` ADD COLUMN `messages` JSON"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000000_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000000_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" jsonb"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000000_messages.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_registration_requests_tmp\" RENAME TO \"selfservice_registration_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000000_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" TEXT"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000001_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"messages\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000001_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" json"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000001_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `messages`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000001_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `messages` JSON"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000001_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"messages\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000001_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" jsonb"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000001_messages.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000001_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" TEXT"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000002_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"messages\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000002_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000002_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` DROP COLUMN `messages`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000002_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000002_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"messages\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000002_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000002_messages.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_registration_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at FROM \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000002_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000003_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_registration_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000003_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000004_messages.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000004_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000005_messages.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000005_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000006_messages.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced FROM \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000006_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000007_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false'\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000007_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000008_messages.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_requests_tmp\" RENAME TO \"selfservice_verification_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000008_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000009_messages.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_verification_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000009_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000010_messages.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_requests_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, via, success) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, via, success FROM \"selfservice_verification_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000010_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000011_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"via\" TEXT NOT NULL DEFAULT 'email',\n\"success\" bool NOT NULL DEFAULT 'FALSE'\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200605111551000011_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000000_settings.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000000_settings.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000000_settings.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `update_successful` bool NOT NULL DEFAULT false;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000000_settings.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `state` VARCHAR (255) NOT NULL DEFAULT 'show_form'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000000_settings.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000000_settings.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000000_settings.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000000_settings.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000001_settings.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"state\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000001_settings.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"update_successful\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000001_settings.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `state`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000001_settings.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `update_successful`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000001_settings.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"state\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000001_settings.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"update_successful\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000001_settings.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000001_settings.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000002_settings.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000002_settings.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state FROM \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000003_settings.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages FROM \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000003_settings.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000004_settings.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200607165100000004_settings.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200705105359000000_rename_identities_schema.cockroach.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200705105359000000_rename_identities_schema.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200705105359000000_rename_identities_schema.mysql.down.sql",
    "content": "ALTER TABLE `identities` CHANGE `schema_id` `traits_schema_id` varchar(2048) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200705105359000000_rename_identities_schema.mysql.up.sql",
    "content": "ALTER TABLE `identities` CHANGE `traits_schema_id` `schema_id` varchar(2048) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200705105359000000_rename_identities_schema.postgres.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200705105359000000_rename_identities_schema.postgres.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200705105359000000_rename_identities_schema.sqlite3.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200705105359000000_rename_identities_schema.sqlite3.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000000_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000000_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000000_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` DROP COLUMN `type`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000000_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000000_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000000_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000000_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_requests_tmp\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000000_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000001_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" DROP COLUMN \"type\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000001_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000001_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_requests` DROP COLUMN `type`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000001_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000001_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" DROP COLUMN \"type\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000001_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000001_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_verification_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000001_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000002_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"type\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000002_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000002_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `type`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000002_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000002_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"type\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000002_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000002_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_requests_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, via, success) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, via, success FROM \"selfservice_verification_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000002_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000003_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"type\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000003_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000003_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` DROP COLUMN `type`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000003_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000003_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"type\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000003_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000003_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"via\" TEXT NOT NULL DEFAULT 'email',\n\"success\" bool NOT NULL DEFAULT 'FALSE'\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000003_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000004_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"type\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000004_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000004_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `type`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000004_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000004_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"type\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000004_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000004_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_recovery_requests_tmp\" RENAME TO \"selfservice_recovery_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000004_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000005_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_recovery_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000005_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000006_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_recovery_requests_tmp\" (id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at FROM \"selfservice_recovery_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000006_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000007_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_recovery_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000007_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000008_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000008_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000009_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000009_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000010_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state FROM \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000010_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000011_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000011_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000012_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_registration_requests_tmp\" RENAME TO \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000012_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000013_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000013_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000014_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_registration_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages FROM \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000014_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000015_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_registration_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000015_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000016_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000016_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000017_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000017_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000018_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages FROM \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000018_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000019_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"messages\" TEXT\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810141652000019_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000000_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000000_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000000_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` RENAME TO `selfservice_verification_requests`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000000_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_request_methods` RENAME TO `selfservice_login_flow_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000000_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000000_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000000_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000000_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000001_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000001_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000001_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` RENAME TO `selfservice_recovery_requests`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000001_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` RENAME TO `selfservice_login_flows`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000001_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000001_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000001_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000001_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000002_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000002_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000002_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_flow_methods` RENAME TO `selfservice_recovery_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000002_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_request_methods` RENAME TO `selfservice_registration_flow_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000002_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000002_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000002_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000002_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000003_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000003_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000003_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` RENAME TO `selfservice_settings_requests`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000003_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` RENAME TO `selfservice_registration_flows`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000003_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000003_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000003_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000003_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000004_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000004_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000004_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flow_methods` RENAME TO `selfservice_settings_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000004_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_request_methods` RENAME TO `selfservice_settings_flow_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000004_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000004_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000004_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000004_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000005_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000005_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000005_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` RENAME TO `selfservice_registration_requests`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000005_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` RENAME TO `selfservice_settings_flows`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000005_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000005_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000005_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000005_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000006_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000006_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000006_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flow_methods` RENAME TO `selfservice_registration_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000006_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_request_methods` RENAME TO `selfservice_recovery_flow_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000006_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000006_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000006_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000006_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000007_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000007_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000007_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` RENAME TO `selfservice_login_request_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000007_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_requests` RENAME TO `selfservice_recovery_flows`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000007_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000007_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000007_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000007_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000008_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000008_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000008_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` RENAME TO `selfservice_login_requests`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000008_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` RENAME TO `selfservice_verification_flows`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000008_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000008_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000008_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810161022000008_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000000_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000000_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000000_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_flow_methods` CHANGE `selfservice_recovery_flow_id` `selfservice_recovery_request_id` char(36) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000000_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` CHANGE `selfservice_login_request_id` `selfservice_login_flow_id` char(36) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000000_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000000_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000000_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000000_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000001_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000001_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000001_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flow_methods` CHANGE `selfservice_settings_flow_id` `selfservice_settings_request_id` char(36) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000001_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flow_methods` CHANGE `selfservice_registration_request_id` `selfservice_registration_flow_id` char(36) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000001_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000001_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000001_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000001_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000002_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000002_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000002_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flow_methods` CHANGE `selfservice_registration_flow_id` `selfservice_registration_request_id` char(36) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000002_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_flow_methods` CHANGE `selfservice_recovery_request_id` `selfservice_recovery_flow_id` char(36) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000002_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000002_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000002_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000002_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000003_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000003_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000003_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` CHANGE `selfservice_login_flow_id` `selfservice_login_request_id` char(36) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000003_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flow_methods` CHANGE `selfservice_settings_request_id` `selfservice_settings_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000003_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000003_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000003_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200810162450000003_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000000_add_session_token.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"token\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000000_add_session_token.cockroach.up.sql",
    "content": "DELETE FROM sessions"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000000_add_session_token.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `token`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000000_add_session_token.mysql.up.sql",
    "content": "DELETE FROM sessions"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000000_add_session_token.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"token\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000000_add_session_token.postgres.up.sql",
    "content": "DELETE FROM sessions"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000000_add_session_token.sqlite3.down.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000000_add_session_token.sqlite3.up.sql",
    "content": "DELETE FROM sessions"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000001_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000001_add_session_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000001_add_session_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000001_add_session_token.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `token` VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000001_add_session_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000001_add_session_token.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000001_add_session_token.sqlite3.down.sql",
    "content": "\nDROP TABLE \"sessions\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000001_add_session_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"token\" TEXT"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000002_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000002_add_session_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" RENAME COLUMN \"token\" TO \"_token_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000002_add_session_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000002_add_session_token.mysql.up.sql",
    "content": "ALTER TABLE `sessions` MODIFY `token` VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000002_add_session_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000002_add_session_token.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ALTER COLUMN \"token\" TYPE VARCHAR (32), ALTER COLUMN \"token\" DROP NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000002_add_session_token.sqlite3.down.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at FROM \"sessions\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000002_add_session_token.sqlite3.up.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000003_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000003_add_session_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000003_add_session_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000003_add_session_token.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `sessions_token_uq_idx` ON `sessions` (`token`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000003_add_session_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000003_add_session_token.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000003_add_session_token.sqlite3.down.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000003_add_session_token.sqlite3.up.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token FROM \"sessions\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000004_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000004_add_session_token.cockroach.up.sql",
    "content": "UPDATE \"sessions\" SET \"token\" = \"_token_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000004_add_session_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000004_add_session_token.mysql.up.sql",
    "content": "CREATE INDEX `sessions_token_idx` ON `sessions` (`token`);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000004_add_session_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000004_add_session_token.postgres.up.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000004_add_session_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000004_add_session_token.sqlite3.up.sql",
    "content": "DROP TABLE \"sessions\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000005_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000005_add_session_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"_token_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000005_add_session_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000005_add_session_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000006_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000006_add_session_token.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000006_add_session_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000006_add_session_token.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000007_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000007_add_session_token.cockroach.up.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000007_add_session_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812124254000007_add_session_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000000_add_session_revoke.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"active\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000000_add_session_revoke.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" boolean DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000000_add_session_revoke.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `active`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000000_add_session_revoke.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `active` boolean DEFAULT false;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000000_add_session_revoke.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"active\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000000_add_session_revoke.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" boolean DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000000_add_session_revoke.sqlite3.down.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000000_add_session_revoke.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" NUMERIC DEFAULT 'false';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000001_add_session_revoke.sqlite3.down.sql",
    "content": "\nDROP TABLE \"sessions\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000001_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000002_add_session_revoke.sqlite3.down.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token FROM \"sessions\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000002_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000003_add_session_revoke.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000003_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000004_add_session_revoke.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000004_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000005_add_session_revoke.sqlite3.down.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000005_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000006_add_session_revoke.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000006_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000007_add_session_revoke.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200812160551000007_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830121710000000_update_recovery_token.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830121710000000_update_recovery_token.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830121710000000_update_recovery_token.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` CHANGE `selfservice_recovery_flow_id` `selfservice_recovery_request_id` char(36) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830121710000000_update_recovery_token.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` CHANGE `selfservice_recovery_request_id` `selfservice_recovery_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830121710000000_update_recovery_token.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830121710000000_update_recovery_token.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830121710000000_update_recovery_token.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830121710000000_update_recovery_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000000_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000000_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000000_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `success` bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000000_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `state` VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000000_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000000_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000000_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000000_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000001_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" VARCHAR (16) NOT NULL DEFAULT 'email'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000001_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000001_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `via` VARCHAR (16) NOT NULL DEFAULT 'email'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000001_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000001_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" VARCHAR (16) NOT NULL DEFAULT 'email'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000001_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000001_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" TEXT NOT NULL DEFAULT 'email'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000001_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000002_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"state\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000002_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000002_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `state`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000002_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000002_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"state\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000002_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000002_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000002_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000003_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"active_method\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000003_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000003_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `active_method`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000003_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000003_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"active_method\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000003_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000003_add_verification_methods.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000003_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000004_add_verification_methods.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000004_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000004_add_verification_methods.mysql.down.sql",
    "content": "DROP TABLE `selfservice_verification_flow_methods`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000004_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000004_add_verification_methods.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000004_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000004_add_verification_methods.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type FROM \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000004_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000005_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"form\" json NOT NULL DEFAULT '{}'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000005_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000005_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` MODIFY `form` JSON"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000005_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000005_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ALTER COLUMN \"form\" TYPE jsonb, ALTER COLUMN \"form\" DROP NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000005_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000005_add_verification_methods.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser'\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000005_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000006_add_verification_methods.mysql.down.sql",
    "content": "UPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000006_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000006_add_verification_methods.postgres.down.sql",
    "content": "UPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000006_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000006_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000006_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000007_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `form` JSON"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000007_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000007_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"form\" jsonb"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000007_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000007_add_verification_methods.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000007_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000008_add_verification_methods.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state FROM \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000008_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000009_add_verification_methods.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form'\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000009_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000010_add_verification_methods.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130642000010_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130643000000_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130643000000_add_verification_methods.cockroach.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130643000000_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130643000000_add_verification_methods.mysql.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130643000000_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130643000000_add_verification_methods.postgres.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130643000000_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130643000000_add_verification_methods.sqlite3.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000000_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000000_add_verification_methods.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000000_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000000_add_verification_methods.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_verification_flow_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_verification_flow_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000000_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000000_add_verification_methods.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000000_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000000_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_verification_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000001_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000001_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" VARCHAR (32);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000001_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000001_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `active_method` VARCHAR (32);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000001_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000001_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" VARCHAR (32);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000001_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130644000001_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" TEXT;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130645000000_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130645000000_add_verification_methods.cockroach.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130645000000_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130645000000_add_verification_methods.mysql.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130645000000_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130645000000_add_verification_methods.postgres.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130645000000_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130645000000_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000000_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000000_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"form\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000000_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000000_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `form`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000000_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000000_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"form\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000000_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000000_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000001_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000001_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"via\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000001_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000001_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `via`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000001_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000001_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"via\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000001_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000001_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000002_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000002_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"success\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000002_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000002_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `success`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000002_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000002_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"success\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000002_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000002_add_verification_methods.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000003_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000003_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000004_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000004_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000005_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000005_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000006_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000006_add_verification_methods.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000007_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000007_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000008_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000008_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000009_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000009_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000010_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000010_add_verification_methods.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000011_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830130646000011_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000000_add_verification_token.cockroach.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000000_add_verification_token.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"expires_at\" timestamp NOT NULL,\n\"issued_at\" timestamp NOT NULL,\n\"identity_verifiable_address_id\" UUID NOT NULL,\n\"selfservice_verification_flow_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_verification_tokens_identity_verifiable_addresses_id_fk\" FOREIGN KEY (\"identity_verifiable_address_id\") REFERENCES \"identity_verifiable_addresses\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_verification_tokens_selfservice_verification_flows_id_fk\" FOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flows\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000000_add_verification_token.mysql.down.sql",
    "content": "DROP TABLE `identity_verification_tokens`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000000_add_verification_token.mysql.up.sql",
    "content": "CREATE TABLE `identity_verification_tokens` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`token` VARCHAR (64) NOT NULL,\n`used` bool NOT NULL DEFAULT false,\n`used_at` DATETIME,\n`expires_at` DATETIME NOT NULL,\n`issued_at` DATETIME NOT NULL,\n`identity_verifiable_address_id` char(36) NOT NULL,\n`selfservice_verification_flow_id` char(36),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_verifiable_address_id`) REFERENCES `identity_verifiable_addresses` (`id`) ON DELETE cascade,\nFOREIGN KEY (`selfservice_verification_flow_id`) REFERENCES `selfservice_verification_flows` (`id`) ON DELETE cascade\n) ENGINE=InnoDB"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000000_add_verification_token.postgres.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000000_add_verification_token.postgres.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"expires_at\" timestamp NOT NULL,\n\"issued_at\" timestamp NOT NULL,\n\"identity_verifiable_address_id\" UUID NOT NULL,\n\"selfservice_verification_flow_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_verifiable_address_id\") REFERENCES \"identity_verifiable_addresses\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flows\" (\"id\") ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000000_add_verification_token.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000000_add_verification_token.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL,\n\"identity_verifiable_address_id\" char(36) NOT NULL,\n\"selfservice_verification_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON DELETE cascade\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000001_add_verification_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000001_add_verification_token.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000001_add_verification_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000001_add_verification_token.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_verification_tokens_token_uq_idx` ON `identity_verification_tokens` (`token`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000001_add_verification_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000001_add_verification_token.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000001_add_verification_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000001_add_verification_token.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000002_add_verification_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000002_add_verification_token.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000002_add_verification_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000002_add_verification_token.mysql.up.sql",
    "content": "CREATE INDEX `identity_verification_tokens_token_idx` ON `identity_verification_tokens` (`token`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000002_add_verification_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000002_add_verification_token.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000002_add_verification_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000002_add_verification_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000003_add_verification_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000003_add_verification_token.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000003_add_verification_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000003_add_verification_token.mysql.up.sql",
    "content": "CREATE INDEX `identity_verification_tokens_verifiable_address_id_idx` ON `identity_verification_tokens` (`identity_verifiable_address_id`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000003_add_verification_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000003_add_verification_token.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000003_add_verification_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000003_add_verification_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000004_add_verification_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000004_add_verification_token.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000004_add_verification_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000004_add_verification_token.mysql.up.sql",
    "content": "CREATE INDEX `identity_verification_tokens_verification_flow_id_idx` ON `identity_verification_tokens` (`selfservice_verification_flow_id`);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000004_add_verification_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000004_add_verification_token.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000004_add_verification_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830154602000004_add_verification_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000000_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"issued_at\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000000_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000000_recovery_token_expires.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` DROP COLUMN `issued_at`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000000_recovery_token_expires.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD COLUMN `expires_at` DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000000_recovery_token_expires.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"issued_at\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000000_recovery_token_expires.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000000_recovery_token_expires.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000000_recovery_token_expires.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000001_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"expires_at\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000001_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000001_recovery_token_expires.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` DROP COLUMN `expires_at`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000001_recovery_token_expires.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD COLUMN `issued_at` DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000001_recovery_token_expires.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"expires_at\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000001_recovery_token_expires.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000001_recovery_token_expires.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000001_recovery_token_expires.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00'"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000002_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flows\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000002_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000002_recovery_token_expires.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` MODIFY `selfservice_recovery_flow_id` char(36) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000002_recovery_token_expires.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` MODIFY `selfservice_recovery_flow_id` char(36);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000002_recovery_token_expires.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" TYPE UUID, ALTER COLUMN \"selfservice_recovery_flow_id\" SET NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000002_recovery_token_expires.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" TYPE UUID, ALTER COLUMN \"selfservice_recovery_flow_id\" DROP NOT NULL;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000002_recovery_token_expires.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at FROM \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000002_recovery_token_expires.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000003_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_selfservice_recovery_flow_id_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000003_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"_selfservice_recovery_flow_id_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000003_recovery_token_expires.mysql.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000003_recovery_token_expires.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000003_recovery_token_expires.postgres.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000003_recovery_token_expires.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000003_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000003_recovery_token_expires.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000004_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" SET NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000004_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"selfservice_recovery_flow_id\" UUID"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000004_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000004_recovery_token_expires.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000005_recovery_token_expires.cockroach.down.sql",
    "content": "UPDATE \"identity_recovery_tokens\" SET \"selfservice_recovery_flow_id\" = \"_selfservice_recovery_flow_id_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000005_recovery_token_expires.cockroach.up.sql",
    "content": "UPDATE \"identity_recovery_tokens\" SET \"selfservice_recovery_flow_id\" = \"_selfservice_recovery_flow_id_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000005_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000005_recovery_token_expires.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000006_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"selfservice_recovery_flow_id\" UUID"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000006_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_selfservice_recovery_flow_id_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000006_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000006_recovery_token_expires.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000007_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"_selfservice_recovery_flow_id_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000007_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flows\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000007_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000007_recovery_token_expires.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at FROM \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000008_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000008_recovery_token_expires.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000008_recovery_token_expires.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000008_recovery_token_expires.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000009_recovery_token_expires.cockroach.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000009_recovery_token_expires.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000009_recovery_token_expires.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000009_recovery_token_expires.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000010_recovery_token_expires.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, issued_at FROM \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000010_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000011_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000011_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000012_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000012_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000013_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000013_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000014_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000014_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000015_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000015_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000016_recovery_token_expires.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000016_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000017_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000017_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000018_recovery_token_expires.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at FROM \"identity_recovery_tokens\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000018_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000019_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000019_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000020_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000020_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000021_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000021_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000022_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000022_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000023_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000023_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000024_recovery_token_expires.sqlite3.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200830172221000024_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000000_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000000_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000000_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "CREATE INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses` (`code`);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000000_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000000_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000000_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_code_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000000_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000000_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000001_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000001_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000001_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "CREATE UNIQUE INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses` (`code`)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000001_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000001_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000001_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_code_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000001_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000001_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000002_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"_expires_at_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000002_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"code\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000002_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` MODIFY `expires_at` DATETIME"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000002_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` DROP COLUMN `code`"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000002_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"expires_at\" TYPE timestamp, ALTER COLUMN \"expires_at\" DROP NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000002_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"code\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000002_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000002_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000003_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "UPDATE \"identity_verifiable_addresses\" SET \"expires_at\" = \"_expires_at_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000003_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"expires_at\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000003_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` MODIFY `code` VARCHAR (32) NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000003_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` DROP COLUMN `expires_at`;"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000003_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"code\" TYPE VARCHAR (32), ALTER COLUMN \"code\" SET NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000003_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"expires_at\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000003_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000003_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000004_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000004_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000004_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000004_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000004_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000004_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000004_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at FROM \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000004_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000005_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" RENAME COLUMN \"expires_at\" TO \"_expires_at_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000005_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000005_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET code = LEFT(SHA2(RANDOM_BYTES(32), 256), 32) WHERE code IS NULL\n"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000005_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000005_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET code = substr(md5(random()::text), 0, 32) WHERE code IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000005_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000005_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000005_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000006_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"_code_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000006_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000006_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` ADD COLUMN `expires_at` DATETIME"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000006_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000006_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000006_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000006_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000006_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000007_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"code\" SET NOT NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000007_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000007_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` ADD COLUMN `code` VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000007_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000007_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000007_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000007_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"code\" TEXT NOT NULL,\n\"expires_at\" DATETIME,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000007_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000008_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "UPDATE \"identity_verifiable_addresses\" SET \"code\" = \"_code_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000008_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000008_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000008_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "\nDROP TABLE \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000009_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000009_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000009_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000009_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000010_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" RENAME COLUMN \"code\" TO \"_code_tmp\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000010_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000010_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000010_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000011_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000011_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000011_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000011_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000012_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET code = substr(md5(uuid_v4()), 0, 32) WHERE code IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000012_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000012_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at FROM \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000012_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000013_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000013_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000013_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000013_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000014_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000014_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000014_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000014_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000015_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"code\" TEXT NOT NULL,\n\"expires_at\" DATETIME,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n)"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000015_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000016_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000016_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "\nDROP TABLE \"identity_verifiable_addresses\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000017_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000017_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000018_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000018_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000019_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET code = substr(hex(randomblob(32)), 0, 32) WHERE code IS NULL"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000019_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000020_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" DATETIME"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000020_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000021_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" TEXT"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20200831110752000021_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000000_credential_types_values.cockroach.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000000_credential_types_values.cockroach.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password')"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000000_credential_types_values.mysql.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000000_credential_types_values.mysql.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password')"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000000_credential_types_values.postgres.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000000_credential_types_values.postgres.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password')"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000000_credential_types_values.sqlite3.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000000_credential_types_values.sqlite3.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password')"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000001_credential_types_values.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000001_credential_types_values.cockroach.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000001_credential_types_values.mysql.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000001_credential_types_values.mysql.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000001_credential_types_values.postgres.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000001_credential_types_values.postgres.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000001_credential_types_values.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "oryx/popx/stub/migrations/transactional/20201201161451000001_credential_types_values.sqlite3.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "oryx/popx/transaction.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage popx\n\nimport (\n\t\"context\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/cockroachdb/cockroach-go/v2/crdb\"\n\t\"github.com/jackc/pgconn\"\n\tpgxconn \"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jmoiron/sqlx\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\n\t\"github.com/ory/pop/v6\"\n)\n\ntype transactionContextKey int\n\nconst transactionKey transactionContextKey = 0\n\nfunc WithTransaction(ctx context.Context, tx *pop.Connection) context.Context {\n\treturn context.WithValue(ctx, transactionKey, tx)\n}\n\nfunc InTransaction(ctx context.Context) bool {\n\treturn ctx.Value(transactionKey) != nil\n}\n\nfunc Transaction(ctx context.Context, connection *pop.Connection, callback func(context.Context, *pop.Connection) error) error {\n\tc := ctx.Value(transactionKey)\n\tif c != nil {\n\t\tif conn, ok := c.(*pop.Connection); ok {\n\t\t\treturn errors.WithStack(callback(ctx, conn.WithContext(ctx)))\n\t\t}\n\t}\n\n\tif connection.Dialect.Name() == \"cockroach\" {\n\t\treturn connection.WithContext(ctx).Dialect.Lock(func() error {\n\t\t\ttransaction, err := connection.NewTransaction()\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\n\t\t\tattempt := 0\n\t\t\treturn errors.WithStack(crdb.ExecuteInTx(ctx, sqlxTxAdapter{transaction.TX.Tx}, func() error {\n\t\t\t\tattempt++\n\t\t\t\tif attempt > 1 {\n\t\t\t\t\tcaller := caller()\n\t\t\t\t\ttransactionRetries.WithLabelValues(caller).Inc()\n\t\t\t\t}\n\t\t\t\treturn errors.WithStack(callback(WithTransaction(ctx, transaction), transaction))\n\t\t\t}))\n\t\t})\n\t}\n\tif strings.HasPrefix(connection.Dialect.Name(), \"postgres\") {\n\t\tvar err error\n\t\tfor attempt := range 10 {\n\t\t\t_ = attempt\n\t\t\terr = connection.WithContext(ctx).Transaction(func(tx *pop.Connection) error {\n\t\t\t\treturn callback(WithTransaction(ctx, tx), tx)\n\t\t\t})\n\t\t\tif err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif e := new(pgconn.PgError); errors.As(err, &e) && e.Code == \"40001\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif e := new(pgxconn.PgError); errors.As(err, &e) && e.Code == \"40001\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\treturn err\n\t}\n\n\treturn errors.WithStack(connection.WithContext(ctx).Transaction(func(tx *pop.Connection) error {\n\t\treturn errors.WithStack(callback(WithTransaction(ctx, tx), tx))\n\t}))\n}\n\nfunc GetConnection(ctx context.Context, connection *pop.Connection) *pop.Connection {\n\tc := ctx.Value(transactionKey)\n\tif c != nil {\n\t\tif conn, ok := c.(*pop.Connection); ok {\n\t\t\treturn conn.WithContext(ctx)\n\t\t}\n\t}\n\treturn connection.WithContext(ctx)\n}\n\ntype sqlxTxAdapter struct {\n\t*sqlx.Tx\n}\n\nvar _ crdb.Tx = sqlxTxAdapter{}\n\nfunc (s sqlxTxAdapter) Exec(ctx context.Context, query string, args ...interface{}) error {\n\t_, err := s.Tx.ExecContext(ctx, query, args...)\n\treturn errors.WithStack(err)\n}\n\nfunc (s sqlxTxAdapter) Commit(ctx context.Context) error {\n\treturn errors.WithStack(s.Tx.Commit())\n}\n\nfunc (s sqlxTxAdapter) Rollback(ctx context.Context) error {\n\treturn errors.WithStack(s.Tx.Rollback())\n}\n\nvar (\n\ttransactionRetries = prometheus.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"ory_x_popx_cockroach_transaction_retries_total\",\n\t\tHelp: \"Counts the number of automatic CockroachDB transaction retries\",\n\t}, []string{\"caller\"})\n\tTransactionRetries prometheus.Collector = transactionRetries\n\t_                                       = transactionRetries.WithLabelValues(unknownCaller) // make sure the metric is always present\n\tunknownCaller                           = \"unknown\"\n)\n\nfunc caller() string {\n\tpc := make([]uintptr, 3)\n\t// The number stack frames to skip was determined by putting a breakpoint in\n\t// ory/kratos and looking for the topmost frame which isn't from ory/x or\n\t// ory/pop.\n\tn := runtime.Callers(8, pc)\n\tif n == 0 {\n\t\treturn unknownCaller\n\t}\n\tpc = pc[:n]\n\tframes := runtime.CallersFrames(pc)\n\tfor {\n\t\tframe, more := frames.Next()\n\t\tif frame.Function != \"\" {\n\t\t\treturn frame.Function\n\t\t}\n\t\tif !more {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn unknownCaller\n}\n"
  },
  {
    "path": "oryx/profilex/profiling.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage profilex\n\nimport (\n\t\"os\"\n\n\t\"github.com/pkg/profile\"\n)\n\ntype noop struct{}\n\n// Stop is a noop.\nfunc (p *noop) Stop() {}\n\n// Profile parses the PROFILING environment variable and executes the proper profiling task.\nfunc Profile() interface {\n\tStop()\n} {\n\tswitch os.Getenv(\"PROFILING\") {\n\tcase \"cpu\":\n\t\treturn profile.Start(profile.CPUProfile, profile.NoShutdownHook)\n\tcase \"mem\":\n\t\treturn profile.Start(profile.MemProfile, profile.NoShutdownHook)\n\tcase \"mutex\":\n\t\treturn profile.Start(profile.MutexProfile, profile.NoShutdownHook)\n\tcase \"block\":\n\t\treturn profile.Start(profile.BlockProfile, profile.NoShutdownHook)\n\t}\n\treturn new(noop)\n}\n\n// HelpMessage returns a string explaining how profiling works.\nfunc HelpMessage() string {\n\treturn `- PROFILING: Set \"PROFILING=cpu\" to enable cpu profiling and \"PROFILING=mem\" to enable memory profiling.\n\tIt is not possible to do both at the same time. Profiling is disabled per default.\n\n\tExample: PROFILING=cpu`\n}\n"
  },
  {
    "path": "oryx/prometheusx/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage prometheusx\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n)\n\nconst (\n\tMetricsPrometheusPath = \"/metrics/prometheus\"\n)\n\ntype muxrouter interface {\n\tGET(path string, handle http.HandlerFunc)\n}\n\n// SetMuxRoutes registers the prometheus handler.\nfunc SetMuxRoutes(mux muxrouter) {\n\tmux.GET(MetricsPrometheusPath, promhttp.Handler().ServeHTTP)\n}\n"
  },
  {
    "path": "oryx/prometheusx/metrics.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage prometheusx\n\nimport (\n\t\"net/http\"\n\t\"regexp\"\n\t\"strings\"\n\n\tgrpcPrometheus \"github.com/grpc-ecosystem/go-grpc-prometheus\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n\t\"github.com/urfave/negroni\"\n)\n\ntype HTTPMetrics struct {\n\tresponseTime    *prometheus.HistogramVec\n\ttotalRequests   *prometheus.CounterVec\n\tduration        *prometheus.HistogramVec\n\tresponseSize    *prometheus.HistogramVec\n\trequestSize     *prometheus.HistogramVec\n\thandlerStatuses *prometheus.CounterVec\n}\n\nconst HTTPPrefix = \"http\"\n\nfunc NewHTTPMetrics(app, metricsPrefix, version, hash, date string) *HTTPMetrics {\n\tlabels := map[string]string{\n\t\t\"app\":       app,\n\t\t\"version\":   version,\n\t\t\"hash\":      hash,\n\t\t\"buildTime\": date,\n\t}\n\n\tif metricsPrefix != \"\" {\n\t\tmetricsPrefix += \"_\"\n\t}\n\n\tpm := &HTTPMetrics{\n\t\tresponseTime: prometheus.NewHistogramVec(\n\t\t\tprometheus.HistogramOpts{\n\t\t\t\tName:        metricsPrefix + \"response_time_seconds\",\n\t\t\t\tHelp:        \"Description\",\n\t\t\t\tConstLabels: labels,\n\t\t\t},\n\t\t\t[]string{\"endpoint\"},\n\t\t),\n\t\ttotalRequests: prometheus.NewCounterVec(prometheus.CounterOpts{\n\t\t\tName:        metricsPrefix + \"requests_total\",\n\t\t\tHelp:        \"number of requests\",\n\t\t\tConstLabels: labels,\n\t\t}, []string{\"code\", \"method\", \"endpoint\"}),\n\t\tduration: prometheus.NewHistogramVec(prometheus.HistogramOpts{\n\t\t\tName:        metricsPrefix + \"requests_duration_seconds\",\n\t\t\tHelp:        \"duration of a requests in seconds\",\n\t\t\tConstLabels: labels,\n\t\t}, []string{\"code\", \"method\", \"endpoint\"}),\n\t\tresponseSize: prometheus.NewHistogramVec(prometheus.HistogramOpts{\n\t\t\tName:        metricsPrefix + \"response_size_bytes\",\n\t\t\tHelp:        \"size of the responses in bytes\",\n\t\t\tConstLabels: labels,\n\t\t}, []string{\"code\", \"method\"}),\n\t\trequestSize: prometheus.NewHistogramVec(prometheus.HistogramOpts{\n\t\t\tName:        metricsPrefix + \"requests_size_bytes\",\n\t\t\tHelp:        \"size of the requests in bytes\",\n\t\t\tConstLabels: labels,\n\t\t}, []string{\"code\", \"method\"}),\n\t\thandlerStatuses: prometheus.NewCounterVec(prometheus.CounterOpts{\n\t\t\tName:        metricsPrefix + \"requests_statuses_total\",\n\t\t\tHelp:        \"count number of responses per status\",\n\t\t\tConstLabels: labels,\n\t\t}, []string{\"method\", \"status_bucket\"}),\n\t}\n\n\terr := prometheus.Register(pm)\n\tif e := new(prometheus.AlreadyRegisteredError); errors.As(err, e) {\n\t\treturn pm\n\t} else if err != nil {\n\t\tpanic(err)\n\t}\n\n\tgrpcPrometheus.EnableHandlingTimeHistogram()\n\n\treturn pm\n}\n\n// Describe implements prometheus Collector interface.\nfunc (h *HTTPMetrics) Describe(in chan<- *prometheus.Desc) {\n\th.duration.Describe(in)\n\th.totalRequests.Describe(in)\n\th.requestSize.Describe(in)\n\th.responseSize.Describe(in)\n\th.handlerStatuses.Describe(in)\n\th.responseTime.Describe(in)\n}\n\n// Collect implements prometheus Collector interface.\nfunc (h *HTTPMetrics) Collect(in chan<- prometheus.Metric) {\n\th.duration.Collect(in)\n\th.totalRequests.Collect(in)\n\th.requestSize.Collect(in)\n\th.responseSize.Collect(in)\n\th.handlerStatuses.Collect(in)\n\th.responseTime.Collect(in)\n}\n\nfunc (h *HTTPMetrics) instrumentHandlerStatusBucket(next http.Handler) http.HandlerFunc {\n\treturn func(rw http.ResponseWriter, r *http.Request) {\n\t\trr := negroni.NewResponseWriter(rw)\n\t\tnext.ServeHTTP(rr, r)\n\n\t\tstatusBucket := \"unknown\"\n\t\tswitch status := rr.Status(); {\n\t\tcase status >= 200 && status <= 299:\n\t\t\tstatusBucket = \"2xx\"\n\t\tcase status >= 300 && status <= 399:\n\t\t\tstatusBucket = \"3xx\"\n\t\tcase status >= 400 && status <= 499:\n\t\t\tstatusBucket = \"4xx\"\n\t\tcase status >= 500 && status <= 599:\n\t\t\tstatusBucket = \"5xx\"\n\t\t}\n\n\t\th.handlerStatuses.With(prometheus.Labels{\"method\": r.Method, \"status_bucket\": statusBucket}).\n\t\t\tInc()\n\t}\n}\n\n// Instrument will instrument any http.Handler with custom metrics\nfunc (h *HTTPMetrics) Instrument(next http.Handler, endpoint string) http.Handler {\n\tlabels := prometheus.Labels{}\n\tlabelsWithEndpoint := prometheus.Labels{\"endpoint\": endpoint}\n\twrapped := promhttp.InstrumentHandlerResponseSize(h.responseSize.MustCurryWith(labels), next)\n\twrapped = promhttp.InstrumentHandlerCounter(h.totalRequests.MustCurryWith(labelsWithEndpoint), wrapped)\n\twrapped = promhttp.InstrumentHandlerDuration(h.duration.MustCurryWith(labelsWithEndpoint), wrapped)\n\twrapped = promhttp.InstrumentHandlerDuration(h.responseTime.MustCurryWith(prometheus.Labels{\"endpoint\": endpoint}), wrapped)\n\twrapped = promhttp.InstrumentHandlerRequestSize(h.requestSize.MustCurryWith(labels), wrapped)\n\twrapped = h.instrumentHandlerStatusBucket(wrapped)\n\n\treturn wrapped\n}\n\nvar paramPlaceHolderRE = regexp.MustCompile(`\\{[a-zA-Z0-9_-]+}`)\n\nfunc GetLabelForPattern(pattern string) string {\n\treturn paramPlaceHolderRE.ReplaceAllString(strings.TrimSuffix(pattern, \"/{$}\"), \"{param}\")\n}\n"
  },
  {
    "path": "oryx/proxy/proxy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage proxy\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/rs/cors\"\n\t\"go.opentelemetry.io/otel\"\n)\n\ntype (\n\tRespMiddleware func(resp *http.Response, config *HostConfig, body []byte) ([]byte, error)\n\tReqMiddleware  func(req *httputil.ProxyRequest, config *HostConfig, body []byte) ([]byte, error)\n\tHostMapper     func(ctx context.Context, r *http.Request) (context.Context, *HostConfig, error)\n\toptions        struct {\n\t\thostMapper      HostMapper\n\t\tonResError      func(*http.Response, error) error\n\t\tonReqError      func(*http.Request, error)\n\t\trespMiddlewares []RespMiddleware\n\t\treqMiddlewares  []ReqMiddleware\n\t\ttransport       http.RoundTripper\n\t\terrHandler      func(http.ResponseWriter, *http.Request, error)\n\t}\n\tHostConfig struct {\n\t\t// CorsEnabled is a flag to enable or disable CORS\n\t\t// Default: false\n\t\tCorsEnabled bool\n\t\t// CorsOptions allows to configure CORS\n\t\t// If left empty, no CORS headers will be set even when CorsEnabled is true\n\t\tCorsOptions *cors.Options\n\t\t// CookieDomain is the host under which cookies are set.\n\t\t// If left empty, no cookie domain will be set\n\t\tCookieDomain string\n\t\t// UpstreamHost is the next upstream host the proxy will pass the request to.\n\t\t// e.g. fluffy-bear-afiu23iaysd.oryapis.com\n\t\tUpstreamHost string\n\t\t// UpstreamScheme is the protocol used by the upstream service.\n\t\tUpstreamScheme string\n\t\t// TargetHost is the final target of the request. Should be the same as UpstreamHost\n\t\t// if the request is directly passed to the target service.\n\t\tTargetHost string\n\t\t// TargetScheme is the final target's scheme\n\t\t// (i.e. the scheme the target thinks it is running under)\n\t\tTargetScheme string\n\t\t// PathPrefix is a prefix that is prepended on the original host,\n\t\t// but removed before forwarding.\n\t\tPathPrefix string\n\t\t// TrustForwardedHosts is a flag that indicates whether the proxy should trust the\n\t\t// X-Forwarded-* headers or not.\n\t\tTrustForwardedHeaders bool\n\t\t// originalHost the original hostname the request is coming from.\n\t\t// This value will be maintained internally by the proxy.\n\t\toriginalHost string\n\t\t// originalScheme is the original scheme of the request.\n\t\t// This value will be maintained internally by the proxy.\n\t\toriginalScheme string\n\t\t// ForceOriginalSchemeHTTP forces the original scheme to be https if enabled.\n\t\tForceOriginalSchemeHTTPS bool\n\t}\n\tOptions    func(*options)\n\tcontextKey string\n)\n\nconst (\n\thostConfigKey contextKey = \"host config\"\n)\n\nfunc (c *HostConfig) setScheme(r *httputil.ProxyRequest) {\n\tif c.ForceOriginalSchemeHTTPS {\n\t\tc.originalScheme = \"https\"\n\t} else if forwardedProto := r.In.Header.Get(\"X-Forwarded-Proto\"); forwardedProto != \"\" {\n\t\tc.originalScheme = forwardedProto\n\t} else if r.In.TLS == nil {\n\t\tc.originalScheme = \"http\"\n\t} else {\n\t\tc.originalScheme = \"https\"\n\t}\n}\n\nfunc (c *HostConfig) setHost(r *httputil.ProxyRequest) {\n\tif forwardedHost := r.In.Header.Get(\"X-Forwarded-Host\"); forwardedHost != \"\" {\n\t\tc.originalHost = forwardedHost\n\t} else {\n\t\tc.originalHost = r.In.Host\n\t}\n}\n\n// rewriter is a custom internal function for altering a http.Request\nfunc rewriter(o *options) func(*httputil.ProxyRequest) {\n\treturn func(r *httputil.ProxyRequest) {\n\t\tctx := r.Out.Context()\n\t\tctx, span := otel.GetTracerProvider().Tracer(\"\").Start(ctx, \"x.proxy\")\n\t\tdefer span.End()\n\n\t\t_, c, err := o.getHostConfig(ctx, r.In)\n\t\tif err != nil {\n\t\t\to.onReqError(r.Out, err)\n\t\t\treturn\n\t\t}\n\n\t\tif c.TrustForwardedHeaders {\n\t\t\theaders := []string{\n\t\t\t\t\"X-Forwarded-Host\",\n\t\t\t\t\"X-Forwarded-Proto\",\n\t\t\t\t\"X-Forwarded-For\",\n\t\t\t}\n\t\t\tfor _, h := range headers {\n\t\t\t\tif v := r.In.Header.Get(h); v != \"\" {\n\t\t\t\t\tr.Out.Header.Set(h, v)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tc.setScheme(r)\n\t\tc.setHost(r)\n\n\t\theaderRequestRewrite(r.Out, c)\n\n\t\tvar body []byte\n\t\tvar cb *compressableBody\n\n\t\tif r.Out.ContentLength != 0 {\n\t\t\tbody, cb, err = readBody(r.Out.Header, r.Out.Body)\n\t\t\tif err != nil {\n\t\t\t\to.onReqError(r.Out, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tfor _, m := range o.reqMiddlewares {\n\t\t\tif body, err = m(r, c, body); err != nil {\n\t\t\t\to.onReqError(r.Out, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tn, err := cb.Write(body)\n\t\tif err != nil {\n\t\t\to.onReqError(r.Out, err)\n\t\t\treturn\n\t\t}\n\n\t\tr.Out.Header.Del(\"Content-Length\")\n\t\tr.Out.ContentLength = int64(n)\n\t\tr.Out.Body = cb\n\t}\n}\n\n// modifyResponse is a custom internal function for altering a http.Response\nfunc modifyResponse(o *options) func(*http.Response) error {\n\treturn func(r *http.Response) error {\n\t\t_, c, err := o.getHostConfig(r.Request.Context(), r.Request)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := headerResponseRewrite(r, c); err != nil {\n\t\t\treturn o.onResError(r, err)\n\t\t}\n\n\t\tbody, cb, err := bodyResponseRewrite(r, c)\n\t\tif err != nil {\n\t\t\treturn o.onResError(r, err)\n\t\t}\n\n\t\tfor _, m := range o.respMiddlewares {\n\t\t\tif body, err = m(r, c, body); err != nil {\n\t\t\t\treturn o.onResError(r, err)\n\t\t\t}\n\t\t}\n\n\t\tn, err := cb.Write(body)\n\t\tif err != nil {\n\t\t\treturn o.onResError(r, err)\n\t\t}\n\n\t\tn, t, err := handleWebsocketResponse(n, cb, r.Body)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tr.Header.Del(\"Content-Length\")\n\t\tr.ContentLength = int64(n)\n\t\tr.Body = t\n\t\treturn nil\n\t}\n}\n\nfunc WithOnError(onReqErr func(*http.Request, error), onResErr func(*http.Response, error) error) Options {\n\treturn func(o *options) {\n\t\to.onReqError = onReqErr\n\t\to.onResError = onResErr\n\t}\n}\n\nfunc WithReqMiddleware(middlewares ...ReqMiddleware) Options {\n\treturn func(o *options) {\n\t\to.reqMiddlewares = append(o.reqMiddlewares, middlewares...)\n\t}\n}\n\nfunc WithRespMiddleware(middlewares ...RespMiddleware) Options {\n\treturn func(o *options) {\n\t\to.respMiddlewares = append(o.respMiddlewares, middlewares...)\n\t}\n}\n\nfunc WithTransport(t http.RoundTripper) Options {\n\treturn func(o *options) {\n\t\to.transport = t\n\t}\n}\n\nfunc WithErrorHandler(eh func(w http.ResponseWriter, r *http.Request, err error)) Options {\n\treturn func(o *options) {\n\t\to.errHandler = eh\n\t}\n}\n\nfunc (o *options) getHostConfig(ctx context.Context, r *http.Request) (context.Context, *HostConfig, error) {\n\tif cached, ok := ctx.Value(hostConfigKey).(*HostConfig); ok && cached != nil {\n\t\treturn ctx, cached, nil\n\t}\n\tctx, c, err := o.hostMapper(ctx, r)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\t// cache the host config in the request context\n\t// this will be passed on to the request and response proxy functions\n\tctx = context.WithValue(ctx, hostConfigKey, c)\n\treturn ctx, c, nil\n}\n\nfunc (o *options) beforeProxyMiddleware(h http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {\n\t\t// get the hostmapper configurations before the request is proxied\n\t\tctx, c, err := o.getHostConfig(request.Context(), request)\n\t\tif err != nil {\n\t\t\to.onReqError(request, err)\n\t\t\treturn\n\t\t}\n\n\t\t// Add our Cors middleware.\n\t\t// This middleware will only trigger if the host config has cors enabled on that request.\n\t\tif c.CorsEnabled && c.CorsOptions != nil {\n\t\t\tcors.New(*c.CorsOptions).HandlerFunc(writer, request)\n\t\t}\n\n\t\th.ServeHTTP(writer, request.WithContext(ctx))\n\t})\n}\n\nfunc defaultErrorHandler(w http.ResponseWriter, r *http.Request, err error) {\n\tswitch {\n\tcase errors.Is(err, context.Canceled):\n\t\tw.WriteHeader(499) // http://nginx.org/en/docs/dev/development_guide.html\n\tcase isTimeoutError(err):\n\t\tw.WriteHeader(http.StatusGatewayTimeout)\n\tdefault:\n\t\tlog.Printf(\"http: proxy error: %v\", err)\n\t\tw.WriteHeader(http.StatusBadGateway)\n\t}\n}\n\nfunc isTimeoutError(err error) bool {\n\tvar te interface{ Timeout() bool } = nil\n\treturn errors.As(err, &te) && te.Timeout() || errors.Is(err, context.DeadlineExceeded)\n}\n\n// New creates a new Proxy\n// A Proxy sets up a middleware with custom request and response modification handlers\nfunc New(hostMapper HostMapper, opts ...Options) http.Handler {\n\to := &options{\n\t\thostMapper: hostMapper,\n\t\tonReqError: func(*http.Request, error) {},\n\t\tonResError: func(_ *http.Response, err error) error { return err },\n\t\ttransport:  http.DefaultTransport,\n\t\terrHandler: defaultErrorHandler,\n\t}\n\n\tfor _, op := range opts {\n\t\top(o)\n\t}\n\n\trp := &httputil.ReverseProxy{\n\t\tRewrite:        rewriter(o),\n\t\tModifyResponse: modifyResponse(o),\n\t\tTransport:      o.transport,\n\t\tErrorHandler:   o.errHandler,\n\t}\n\n\treturn o.beforeProxyMiddleware(rp)\n}\n"
  },
  {
    "path": "oryx/proxy/rewrites.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage proxy\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n)\n\ntype compressableBody struct {\n\tbuf bytes.Buffer\n\tw   io.WriteCloser\n}\n\n// we require a read and write for websocket connections\nvar _ io.ReadWriteCloser = new(compressableBody)\n\nfunc (b *compressableBody) Close() error {\n\tif b != nil {\n\t\tb.buf.Reset()\n\t\tif b.w != nil {\n\t\t\treturn b.w.Close()\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (b *compressableBody) Write(d []byte) (int, error) {\n\tif b == nil {\n\t\t// this happens when the body is empty\n\t\treturn 0, nil\n\t}\n\n\tvar w io.Writer = &b.buf\n\tif b.w != nil {\n\t\tw = b.w\n\t\tdefer b.w.Close()\n\t}\n\treturn w.Write(d)\n}\n\nfunc (b *compressableBody) Read(p []byte) (n int, err error) {\n\tif b == nil {\n\t\t// this happens when the body is empty\n\t\treturn 0, io.EOF\n\t}\n\treturn b.buf.Read(p)\n}\n\nfunc headerRequestRewrite(req *http.Request, c *HostConfig) {\n\treq.URL.Scheme = c.UpstreamScheme\n\treq.URL.Host = c.UpstreamHost\n\treq.URL.Path = strings.TrimPrefix(req.URL.Path, c.PathPrefix)\n\n\tif _, ok := req.Header[\"User-Agent\"]; !ok {\n\t\t// explicitly disable User-Agent so it's not set to default value\n\t\treq.Header.Set(\"User-Agent\", \"\")\n\t}\n}\n\nfunc headerResponseRewrite(resp *http.Response, c *HostConfig) error {\n\tredir, err := resp.Location()\n\tif err != nil {\n\t\tif !errors.Is(err, http.ErrNoLocation) {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t} else if redir.Host == c.TargetHost {\n\t\tredir.Scheme = c.originalScheme\n\t\tredir.Host = c.originalHost\n\t\tredir.Path = path.Join(c.PathPrefix, redir.Path)\n\t\tresp.Header.Set(\"Location\", redir.String())\n\t}\n\n\tReplaceCookieDomainAndSecure(resp, c.TargetHost, c.CookieDomain, c.originalScheme == \"https\")\n\n\treturn nil\n}\n\n// ReplaceCookieDomainAndSecure replaces the domain of all matching Set-Cookie headers in the response.\nfunc ReplaceCookieDomainAndSecure(resp *http.Response, original, replacement string, secure bool) {\n\toriginal, replacement = stripPort(original), stripPort(replacement) // cookies don't distinguish ports\n\n\tcookies := resp.Cookies()\n\tresp.Header.Del(\"Set-Cookie\")\n\tfor _, co := range cookies {\n\t\tco.Domain = replacement\n\t\tco.Secure = secure\n\t\tif !secure {\n\t\t\tco.SameSite = http.SameSiteLaxMode\n\t\t}\n\t\tresp.Header.Add(\"Set-Cookie\", co.String())\n\t}\n}\n\nfunc bodyResponseRewrite(resp *http.Response, c *HostConfig) ([]byte, *compressableBody, error) {\n\tif resp.ContentLength == 0 {\n\t\treturn nil, nil, nil\n\t}\n\n\tbody, cb, err := readBody(resp.Header, resp.Body)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif c.TargetScheme == \"\" {\n\t\tc.TargetScheme = \"https\"\n\t}\n\n\treturn bytes.ReplaceAll(body, []byte(c.TargetScheme+\"://\"+c.TargetHost), []byte(c.originalScheme+\"://\"+c.originalHost+c.PathPrefix)), cb, nil\n}\n\nfunc readBody(h http.Header, body io.ReadCloser) ([]byte, *compressableBody, error) {\n\tdefer body.Close()\n\n\tcb := &compressableBody{}\n\n\tswitch h.Get(\"Content-Encoding\") {\n\tcase \"gzip\":\n\t\tvar err error\n\t\tbody, err = gzip.NewReader(body)\n\t\tif err != nil {\n\t\t\treturn nil, nil, errors.WithStack(err)\n\t\t}\n\n\t\tcb.w = gzip.NewWriter(&cb.buf)\n\tdefault:\n\t\t// do nothing, we can read directly\n\t}\n\n\tb, err := io.ReadAll(body)\n\tif err != nil {\n\t\treturn nil, nil, errors.WithStack(err)\n\t}\n\treturn b, cb, nil\n}\n\nfunc handleWebsocketResponse(n int, cb *compressableBody, body io.ReadCloser) (int, io.ReadWriteCloser, error) {\n\tvar err error\n\treadWriteCloser, ok := body.(io.ReadWriteCloser)\n\tif ok {\n\t\tif cb != nil {\n\t\t\tn, err = readWriteCloser.Write(cb.buf.Bytes())\n\t\t\tif err != nil {\n\t\t\t\treturn 0, nil, errors.WithStack(err)\n\t\t\t}\n\t\t}\n\t\treturn n, readWriteCloser, nil\n\t}\n\treturn n, cb, nil\n}\n\n// stripPort removes the optional port from the host.\nfunc stripPort(host string) string {\n\treturn (&url.URL{Host: host}).Hostname()\n}\n"
  },
  {
    "path": "oryx/proxy/stubs/auth.example.com.json",
    "content": "{\n  \"ui\": {\n    \"action\": \"https://auth.example.com\"\n  },\n  \"callbacks\": [\n    \"https://auth.example.com/path/to/resource\",\n    \"https://auth.example.com/path?q=https://localhost:8000\"\n  ]\n}\n"
  },
  {
    "path": "oryx/randx/README.md",
    "content": "`randx.RuneSequence` generates even distributions for the given character set\nand length. All results are therefore also evenly distributed.\n\n## AlphaNum\n\n[Alphabet and Numeric](../docs/alpha_num.png)\n\n## AlphaNum\n\n[Alphabet and Numeric](../docs/num.png)\n"
  },
  {
    "path": "oryx/randx/sequence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage randx\n\nimport (\n\t\"crypto/rand\"\n\t\"math/big\"\n)\n\nvar rander = rand.Reader // random function\n\nvar (\n\t// AlphaNum contains runes [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789].\n\tAlphaNum = []rune(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\")\n\t// Alpha contains runes [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ].\n\tAlpha = []rune(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n\t// AlphaLowerNum contains runes [abcdefghijklmnopqrstuvwxyz0123456789].\n\tAlphaLowerNum = []rune(\"abcdefghijklmnopqrstuvwxyz0123456789\")\n\t// AlphaUpperNum contains runes [ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789].\n\tAlphaUpperNum = []rune(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\")\n\t// AlphaLower contains runes [abcdefghijklmnopqrstuvwxyz].\n\tAlphaLower = []rune(\"abcdefghijklmnopqrstuvwxyz\")\n\t// AlphaUpperVowels contains runes [AEIOUY].\n\tAlphaUpperVowels = []rune(\"AEIOUY\")\n\t// AlphaUpperNoVowels contains runes [BCDFGHJKLMNPQRSTVWXZ].\n\tAlphaUpperNoVowels = []rune(\"BCDFGHJKLMNPQRSTVWXZ\")\n\t// AlphaUpper contains runes [ABCDEFGHIJKLMNOPQRSTUVWXYZ].\n\tAlphaUpper = []rune(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n\t// Numeric contains runes [0123456789].\n\tNumeric = []rune(\"0123456789\")\n\t// AlphaNumNoAmbiguous is equivalent to AlphaNum but without visually ambiguous characters [0Oo1IlB8S5Z2].\n\tAlphaNumNoAmbiguous = []rune(\"abcdefghijkmnpqrstuvwxyzACDEFGHJKLMNPQRTUVWXY34679\")\n)\n\n// RuneSequence returns a random sequence using the defined allowed runes.\nfunc RuneSequence(l int, allowedRunes []rune) (seq []rune, err error) {\n\tc := big.NewInt(int64(len(allowedRunes)))\n\tseq = make([]rune, l)\n\n\tfor i := 0; i < l; i++ {\n\t\tr, err := rand.Int(rander, c)\n\t\tif err != nil {\n\t\t\treturn seq, err\n\t\t}\n\t\trn := allowedRunes[r.Uint64()]\n\t\tseq[i] = rn\n\t}\n\n\treturn seq, nil\n}\n\n// MustString returns a random string sequence using the defined runes. Panics on error.\nfunc MustString(l int, allowedRunes []rune) string {\n\tseq, err := RuneSequence(l, allowedRunes)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn string(seq)\n}\n"
  },
  {
    "path": "oryx/randx/strength/go.mod",
    "content": "module github.com/ory/x/randx/strength\n\ngo 1.26\n\nreplace github.com/ory/x => ../..\n\nrequire (\n\tgithub.com/ory/x v0.0.729\n\tgonum.org/v1/plot v0.16.0\n)\n\nrequire (\n\tcodeberg.org/go-fonts/liberation v0.5.0 // indirect\n\tcodeberg.org/go-latex/latex v0.1.0 // indirect\n\tcodeberg.org/go-pdf/fpdf v0.11.1 // indirect\n\tgit.sr.ht/~sbinet/gg v0.6.0 // indirect\n\tgithub.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect\n\tgithub.com/campoy/embedmd v1.0.0 // indirect\n\tgithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgolang.org/x/image v0.30.0 // indirect\n\tgolang.org/x/text v0.31.0 // indirect\n)\n"
  },
  {
    "path": "oryx/randx/strength/go.sum",
    "content": "codeberg.org/go-fonts/dejavu v0.4.0 h1:2yn58Vkh4CFK3ipacWUAIE3XVBGNa0y1bc95Bmfx91I=\ncodeberg.org/go-fonts/dejavu v0.4.0/go.mod h1:abni088lmhQJvso2Lsb7azCKzwkfcnttl6tL1UTWKzg=\ncodeberg.org/go-fonts/latin-modern v0.4.0 h1:vkRCc1y3whKA7iL9Ep0fSGVuJfqjix0ica9UflHORO8=\ncodeberg.org/go-fonts/latin-modern v0.4.0/go.mod h1:BF68mZznJ9QHn+hic9ks2DaFl4sR5YhfM6xTYaP9vNw=\ncodeberg.org/go-fonts/liberation v0.5.0 h1:SsKoMO1v1OZmzkG2DY+7ZkCL9U+rrWI09niOLfQ5Bo0=\ncodeberg.org/go-fonts/liberation v0.5.0/go.mod h1:zS/2e1354/mJ4pGzIIaEtm/59VFCFnYC7YV6YdGl5GU=\ncodeberg.org/go-latex/latex v0.1.0 h1:hoGO86rIbWVyjtlDLzCqZPjNykpWQ9YuTZqAzPcfL3c=\ncodeberg.org/go-latex/latex v0.1.0/go.mod h1:LA0q/AyWIYrqVd+A9Upkgsb+IqPcmSTKc9Dny04MHMw=\ncodeberg.org/go-pdf/fpdf v0.11.1 h1:U8+coOTDVLxHIXZgGvkfQEi/q0hYHYvEHFuGNX2GzGs=\ncodeberg.org/go-pdf/fpdf v0.11.1/go.mod h1:Y0DGRAdZ0OmnZPvjbMp/1bYxmIPxm0ws4tfoPOc4LjU=\ngit.sr.ht/~sbinet/cmpimg v0.1.0 h1:E0zPRk2muWuCqSKSVZIWsgtU9pjsw3eKHi8VmQeScxo=\ngit.sr.ht/~sbinet/cmpimg v0.1.0/go.mod h1:FU12psLbF4TfNXkKH2ZZQ29crIqoiqTZmeQ7dkp/pxE=\ngit.sr.ht/~sbinet/gg v0.6.0 h1:RIzgkizAk+9r7uPzf/VfbJHBMKUr0F5hRFxTUGMnt38=\ngit.sr.ht/~sbinet/gg v0.6.0/go.mod h1:uucygbfC9wVPQIfrmwM2et0imr8L7KQWywX0xpFMm94=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=\ngithub.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=\ngithub.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw=\ngithub.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=\ngithub.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=\ngithub.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\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/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=\ngolang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\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=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngonum.org/v1/plot v0.16.0 h1:dK28Qx/Ky4VmPUN/2zeW0ELyM6ucDnBAj5yun7M9n1g=\ngonum.org/v1/plot v0.16.0/go.mod h1:Xz6U1yDMi6Ni6aaXILqmVIb6Vro8E+K7Q/GeeH+Pn0c=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=\nrsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=\nrsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=\n"
  },
  {
    "path": "oryx/randx/strength/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\n\t\"gonum.org/v1/plot\"\n\t\"gonum.org/v1/plot/plotter\"\n\t\"gonum.org/v1/plot/plotutil\"\n\t\"gonum.org/v1/plot/vg\"\n\n\t\"github.com/ory/x/randx\"\n)\n\nconst iterations = 1000 * 100\n\ntype generate func(int, []rune) ([]rune, error)\n\nfunc main() {\n\tdraw(measureDistribution(iterations, randx.AlphaNum, randx.RuneSequence), \"AlphaNum Distribution\", \"docs/alpha_num.png\")\n\tdraw(measureDistribution(iterations, randx.Numeric, randx.RuneSequence), \"Num Distribution\", \"docs/num.png\")\n\tdraw(measureResultDistribution(100, 6, randx.Numeric, randx.RuneSequence), \"Num Distribution\", \"docs/result_num.png\")\n}\n\nfunc measureResultDistribution(iterations int, length int, characters []rune, fn generate) map[string]int {\n\tdist := make(map[string]int)\n\tfor index := 1; index <= iterations; index++ {\n\t\t// status output to cli\n\t\tif index%1000 == 0 {\n\t\t\tfmt.Printf(\"\\r%d / %d\", index, iterations)\n\t\t}\n\t\traw, err := fn(length, characters)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdist[string(raw)] = dist[string(raw)] + 1\n\t}\n\treturn dist\n}\n\nfunc measureDistribution(iterations int, characters []rune, fn generate) map[string]int {\n\tdist := make(map[string]int)\n\tfor index := 1; index <= iterations; index++ {\n\t\t// status output to cli\n\t\tif index%1000 == 0 {\n\t\t\tfmt.Printf(\"\\r%d / %d\", index, iterations)\n\t\t}\n\t\traw, err := fn(100, characters)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfor _, s := range raw {\n\t\t\tc := string(s)\n\t\t\ti := dist[c]\n\t\t\tdist[c] = i + 1\n\t\t}\n\t}\n\treturn dist\n}\n\nfunc draw(distribution map[string]int, title, filename string) {\n\tkeys, values := orderMap(distribution)\n\tgroup := plotter.Values{}\n\tfor _, v := range values {\n\t\tgroup = append(group, float64(v))\n\t}\n\n\tp := plot.New()\n\tp.Title.Text = title\n\tp.Y.Label.Text = \"N\"\n\n\tbars, err := plotter.NewBarChart(group, vg.Points(4))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tbars.LineStyle.Width = vg.Length(0)\n\tbars.Color = plotutil.Color(0)\n\n\tp.Add(bars)\n\tp.NominalX(keys...)\n\n\tif err := p.Save(300*vg.Millimeter, 150*vg.Millimeter, filename); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc orderMap(m map[string]int) (keys []string, values []int) {\n\tkeys = []string{}\n\tvalues = []int{}\n\tfor k := range m {\n\t\tkeys = append(keys, k)\n\t}\n\tsort.Strings(keys)\n\tfor _, key := range keys {\n\t\tvalues = append(values, m[key])\n\t}\n\treturn keys, values\n}\n"
  },
  {
    "path": "oryx/reqlog/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 Dan Buch and contributors\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": "oryx/reqlog/external_latency.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage reqlog\n\nimport (\n\t\"context\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nfunc withEnableExternalLatencyMeasurement(ctx context.Context) context.Context {\n\treturn context.WithValue(ctx, externalLatencyKey, new(int64))\n}\n\nfunc AccumulateExternalLatency(ctx context.Context, dur time.Duration) {\n\ttotal, ok := ctx.Value(externalLatencyKey).(*int64)\n\tif !ok {\n\t\treturn\n\t}\n\tatomic.AddInt64(total, int64(dur))\n}\n\nfunc getExternalLatency(ctx context.Context) time.Duration {\n\ttotal, ok := ctx.Value(externalLatencyKey).(*int64)\n\tif !ok {\n\t\treturn 0\n\t}\n\treturn time.Duration(atomic.LoadInt64(total))\n}\n\ntype contextKey int\n\nconst externalLatencyKey contextKey = 1\n"
  },
  {
    "path": "oryx/reqlog/middleware.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage reqlog\n\nimport (\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/urfave/negroni\"\n\n\t\"github.com/ory/x/logrusx\"\n)\n\ntype timer interface {\n\tNow() time.Time\n\tSince(time.Time) time.Duration\n}\n\ntype realClock struct{}\n\nfunc (rc *realClock) Now() time.Time {\n\treturn time.Now()\n}\n\nfunc (rc *realClock) Since(t time.Time) time.Duration {\n\treturn time.Since(t)\n}\n\n// Middleware is a middleware handler that logs the request as it goes in and the response as it goes out.\ntype Middleware struct {\n\t// Logger is the log.Logger instance used to log messages with the Logger middleware\n\tLogger *logrusx.Logger\n\t// Name is the name of the application as recorded in latency metrics\n\tName   string\n\tBefore func(*logrusx.Logger, *http.Request, string) *logrusx.Logger\n\tAfter  func(*logrusx.Logger, *http.Request, negroni.ResponseWriter, time.Duration, string) *logrusx.Logger\n\n\tlogStarting bool\n\n\tclock timer\n\n\tlogLevel logrus.Level\n\n\t// Silence log for specific URL paths\n\tsilencePaths map[string]bool\n\n\tsync.RWMutex\n}\n\n// NewMiddleware returns a reqlog middleware with default settings\nfunc NewMiddleware() *Middleware {\n\treturn NewCustomMiddleware(logrus.InfoLevel, &logrus.TextFormatter{}, \"web\")\n}\n\n// NewCustomMiddleware returns a reqlog middleware with the given level and formatter\nfunc NewCustomMiddleware(level logrus.Level, formatter logrus.Formatter, name string) *Middleware {\n\tlog := logrusx.New(name, \"\", logrusx.ForceFormatter(formatter), logrusx.ForceLevel(level))\n\treturn &Middleware{\n\t\tLogger: log,\n\t\tName:   name,\n\t\tBefore: DefaultBefore,\n\t\tAfter:  DefaultAfter,\n\n\t\tlogLevel:     logrus.InfoLevel,\n\t\tlogStarting:  true,\n\t\tclock:        &realClock{},\n\t\tsilencePaths: map[string]bool{},\n\t}\n}\n\n// NewMiddlewareFromLogger returns a reqlog middleware which writes to a given logrus logger.\nfunc NewMiddlewareFromLogger(logger *logrusx.Logger, name string) *Middleware {\n\treturn &Middleware{\n\t\tLogger: logger,\n\t\tName:   name,\n\t\tBefore: DefaultBefore,\n\t\tAfter:  DefaultAfter,\n\n\t\tlogLevel:     logrus.InfoLevel,\n\t\tlogStarting:  true,\n\t\tclock:        &realClock{},\n\t\tsilencePaths: map[string]bool{},\n\t}\n}\n\n// SetLogStarting accepts a bool to control the logging of \"started handling\n// request\" prior to passing to the next middleware\nfunc (m *Middleware) SetLogStarting(v bool) {\n\tm.logStarting = v\n}\n\n// ExcludePaths adds new URL paths to be ignored during logging. The URL u is parsed, hence the returned error\nfunc (m *Middleware) ExcludePaths(paths ...string) *Middleware {\n\tfor _, path := range paths {\n\t\tm.Lock()\n\t\tm.silencePaths[path] = true\n\t\tm.Unlock()\n\t}\n\treturn m\n}\n\nfunc (m *Middleware) Wrap(handler http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {\n\t\tm.ServeHTTP(rw, r, handler.ServeHTTP)\n\t})\n}\n\nfunc (m *Middleware) WrapFunc(handler http.HandlerFunc) http.HandlerFunc {\n\treturn func(rw http.ResponseWriter, r *http.Request) {\n\t\tm.ServeHTTP(rw, r, handler)\n\t}\n}\n\nfunc (m *Middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tif m.Before == nil {\n\t\tm.Before = DefaultBefore\n\t}\n\n\tif m.After == nil {\n\t\tm.After = DefaultAfter\n\t}\n\n\tlogLevel := m.logLevel\n\tm.RLock()\n\tif _, ok := m.silencePaths[r.URL.Path]; ok {\n\t\tlogLevel = logrus.TraceLevel\n\t}\n\tm.RUnlock()\n\n\tstart := m.clock.Now()\n\n\t// Try to get the real IP\n\tremoteAddr := r.RemoteAddr\n\tif realIP := r.Header.Get(\"X-Real-IP\"); realIP != \"\" {\n\t\tremoteAddr = realIP\n\t}\n\n\tentry := m.Logger.NewEntry()\n\n\tentry = m.Before(entry, r, remoteAddr)\n\n\tif m.logStarting {\n\t\tentry.Log(logLevel, \"started handling request\")\n\t}\n\n\tnrw, ok := rw.(negroni.ResponseWriter)\n\tif !ok {\n\t\tnrw = negroni.NewResponseWriter(rw)\n\t}\n\n\tr = r.WithContext(withEnableExternalLatencyMeasurement(r.Context()))\n\tnext(nrw, r)\n\n\tlatency := m.clock.Since(start)\n\n\tm.After(entry, r, nrw, latency, m.Name).Log(logLevel, \"completed handling request\")\n}\n\n// BeforeFunc is the func type used to modify or replace the *logrusx.Logger prior\n// to calling the next func in the middleware chain\ntype BeforeFunc func(*logrusx.Logger, *http.Request, string) *logrusx.Logger\n\n// AfterFunc is the func type used to modify or replace the *logrusx.Logger after\n// calling the next func in the middleware chain\ntype AfterFunc func(*logrusx.Logger, negroni.ResponseWriter, time.Duration, string) *logrusx.Logger\n\n// DefaultBefore is the default func assigned to *Middleware.Before\nfunc DefaultBefore(entry *logrusx.Logger, req *http.Request, remoteAddr string) *logrusx.Logger {\n\treturn entry.WithRequest(req)\n}\n\n// DefaultAfter is the default func assigned to *Middleware.After\nfunc DefaultAfter(entry *logrusx.Logger, req *http.Request, res negroni.ResponseWriter, latency time.Duration, name string) *logrusx.Logger {\n\te := entry.WithRequest(req).WithField(\"http_response\", map[string]any{\n\t\t\"status\":      res.Status(),\n\t\t\"size\":        res.Size(),\n\t\t\"text_status\": http.StatusText(res.Status()),\n\t\t\"took\":        latency,\n\t\t\"headers\":     entry.HTTPHeadersRedacted(res.Header()),\n\t})\n\tif el := getExternalLatency(req.Context()); el > 0 {\n\t\te = e.WithFields(map[string]any{\n\t\t\t\"took_internal\": latency - el,\n\t\t\t\"took_external\": el,\n\t\t})\n\t}\n\treturn e\n}\n"
  },
  {
    "path": "oryx/resilience/retry.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// Package resilience provides helpers for dealing with resilience.\npackage resilience\n\nimport (\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/logrusx\"\n)\n\n// Retry executes a f until no error is returned or failAfter is reached.\nfunc Retry(logger *logrusx.Logger, maxWait time.Duration, failAfter time.Duration, f func() error) (err error) {\n\tvar lastStart time.Time\n\terr = errors.New(\"did not connect\")\n\tloopWait := time.Millisecond * 100\n\tretryStart := time.Now().UTC()\n\tfor retryStart.Add(failAfter).After(time.Now().UTC()) {\n\t\tlastStart = time.Now().UTC()\n\t\tif err = f(); err == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tif lastStart.Add(maxWait * 2).Before(time.Now().UTC()) {\n\t\t\tretryStart = time.Now().UTC()\n\t\t}\n\n\t\tlogger.WithError(err).Infof(\"Retrying in %f seconds...\", loopWait.Seconds())\n\t\ttime.Sleep(loopWait)\n\t\tloopWait = loopWait * time.Duration(int64(2))\n\t\tif loopWait > maxWait {\n\t\t\tloopWait = maxWait\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "oryx/safecast/safecast.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage safecast\n\nimport \"math\"\n\n// Clamp if needed.\nfunc Uint64ToInt64(in uint64) int64 {\n\tif in > math.MaxInt64 {\n\t\treturn math.MaxInt64\n\t}\n\treturn int64(in)\n}\n"
  },
  {
    "path": "oryx/serverx/404.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage serverx\n\nimport (\n\t_ \"embed\"\n\t\"net/http\"\n\n\t\"github.com/ory/herodot/httputil\"\n)\n\n//go:embed 404.html\nvar page404HTML string\n\n//go:embed 404.json\nvar page404JSON string\n\n// DefaultNotFoundHandler is a default handler for handling 404 errors.\nvar DefaultNotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\tvar contentType, body string\n\tswitch httputil.NegotiateContentType(r, []string{\n\t\t\"text/html\",\n\t\t\"text/plain\",\n\t\t\"application/json\",\n\t}, \"text/html\") {\n\tcase \"text/plain\":\n\t\tcontentType = \"text/plain\"\n\t\tbody = \"Error 404 - The requested route does not exist. Make sure you are using the right path, domain, and port.\"\n\tcase \"application/json\":\n\t\tcontentType = \"application/json\"\n\t\tbody = page404JSON\n\tdefault:\n\t\tfallthrough\n\tcase \"text/html\":\n\t\tcontentType = \"text/html\"\n\t\tbody = page404HTML\n\t}\n\n\tw.Header().Set(\"Content-Type\", contentType+\"; charset=utf-8\")\n\tw.WriteHeader(http.StatusNotFound)\n\t_, _ = w.Write([]byte(body))\n})\n"
  },
  {
    "path": "oryx/serverx/404.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <title>404 - Route not found</title>\n    <style>\n      * {\n        transition: all 0.6s;\n      }\n      html {\n        height: 100%;\n      }\n      body {\n        font-family: sans-serif;\n        color: #888;\n        margin: 0;\n      }\n      #main {\n        display: table;\n        width: 100%;\n        height: 100vh;\n        text-align: center;\n      }\n      .fof {\n        display: table-cell;\n        vertical-align: middle;\n      }\n      .fof h1 {\n        font-size: 50px;\n        display: inline-block;\n        padding-right: 12px;\n        margin-bottom: 12px;\n        animation: type 0.5s alternate infinite;\n      }\n      @keyframes type {\n        from {\n          box-shadow: inset -3px 0 0 #888;\n        }\n        to {\n          box-shadow: inset -3px 0 0 transparent;\n        }\n      }\n    </style>\n  </head>\n  <body translate=\"no\">\n    <div id=\"main\">\n      <div class=\"fof\">\n        <h1>Error 404</h1>\n        <p>\n          The requested route does not exist. Make sure you are using the right\n          path, domain, and port.\n        </p>\n      </div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "oryx/serverx/404.json",
    "content": "{\n  \"error\": {\n    \"code\": 404,\n    \"message\": \"Not Found\",\n    \"reason\": \"The requested route does not exist. Make sure you are using the right path, domain, and port.\"\n  }\n}\n"
  },
  {
    "path": "oryx/servicelocatorx/options.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage servicelocatorx\n\nimport (\n\t\"github.com/urfave/negroni\"\n\t\"google.golang.org/grpc\"\n\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/logrusx\"\n)\n\ntype (\n\tOptions struct {\n\t\tlogger                 *logrusx.Logger\n\t\tcontextualizer         contextx.Contextualizer\n\t\thttpMiddlewares        []negroni.Handler\n\t\tgrpcUnaryInterceptors  []grpc.UnaryServerInterceptor\n\t\tgrpcStreamInterceptors []grpc.StreamServerInterceptor\n\t}\n\tOption func(o *Options)\n)\n\nfunc WithLogger(l *logrusx.Logger) Option {\n\treturn func(o *Options) {\n\t\to.logger = l\n\t}\n}\n\nfunc WithContextualizer(ctxer contextx.Contextualizer) Option {\n\treturn func(o *Options) {\n\t\to.contextualizer = ctxer\n\t}\n}\n\nfunc WithHTTPMiddlewares(m ...negroni.Handler) Option {\n\treturn func(o *Options) {\n\t\to.httpMiddlewares = m\n\t}\n}\n\nfunc WithGRPCUnaryInterceptors(i ...grpc.UnaryServerInterceptor) Option {\n\treturn func(o *Options) {\n\t\to.grpcUnaryInterceptors = i\n\t}\n}\n\nfunc WithGRPCStreamInterceptors(i ...grpc.StreamServerInterceptor) Option {\n\treturn func(o *Options) {\n\t\to.grpcStreamInterceptors = i\n\t}\n}\n\nfunc (o *Options) Logger() *logrusx.Logger {\n\treturn o.logger\n}\n\nfunc (o *Options) Contextualizer() contextx.Contextualizer {\n\treturn o.contextualizer\n}\n\nfunc (o *Options) HTTPMiddlewares() []negroni.Handler {\n\treturn o.httpMiddlewares\n}\n\nfunc (o *Options) GRPCUnaryInterceptors() []grpc.UnaryServerInterceptor {\n\treturn o.grpcUnaryInterceptors\n}\n\nfunc (o *Options) GRPCStreamInterceptors() []grpc.StreamServerInterceptor {\n\treturn o.grpcStreamInterceptors\n}\n\nfunc NewOptions(options ...Option) *Options {\n\to := &Options{\n\t\tcontextualizer: &contextx.Default{},\n\t}\n\tfor _, opt := range options {\n\t\topt(o)\n\t}\n\treturn o\n}\n"
  },
  {
    "path": "oryx/snapshotx/.snapshots/TestDeleteMatches-file=1.json-fn.json",
    "content": "{\n  \"foo\": {\n    \"other\": \"fdsa\"\n  },\n  \"nested\": {\n    \"nested\": {\n      \"arr\": [\n        {\n        },\n        {\n        }\n      ]\n    }\n  },\n  \"arr\": [\n    {\n    },\n    {\n      \"arr\": [\n        {\n        },\n        {\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "oryx/snapshotx/.snapshots/TestDeleteMatches-file=2.json-fn.json",
    "content": "{\n  \"created_at\": \"1234\",\n  \"updated_at\": \"1234\",\n  \"nested\": {\n    \"created_at\": 1234,\n    \"nested\": {\n      \"created_at\": 1234,\n      \"arr\": [\n        {\n          \"created_at\": 1234\n        },\n        {\n          \"updated_at\": 1234\n        }\n      ]\n    }\n  },\n  \"arr\": [\n    {\n      \"created_at\": 1234\n    },\n    {\n      \"updated_at\": 1234,\n      \"arr\": [\n        {\n          \"created_at\": 1234\n        },\n        {\n          \"updated_at\": 1234\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "oryx/snapshotx/.snapshots/TestDeleteMatches-file=3.json-fn.json",
    "content": "{\n  \"updated_at\": \"1234\",\n  \"nested\": {\n    \"nested\": {\n      \"arr\": [\n        {\n        },\n        {\n          \"updated_at\": 1234\n        }\n      ]\n    }\n  },\n  \"arr\": [\n    {\n    },\n    {\n      \"updated_at\": 1234,\n      \"arr\": [\n        {\n        },\n        {\n          \"updated_at\": 1234\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "oryx/snapshotx/fixtures/1.json",
    "content": "{\n  \"ignore_nested\": [\n    \"updated_at\",\n    \"created_at\"\n  ],\n  \"ignore_exact\": [\n    \"foo.id\"\n  ],\n  \"content\": {\n    \"foo\": {\n      \"id\": \"asdf\",\n      \"other\": \"fdsa\"\n    },\n    \"created_at\": \"1234\",\n    \"updated_at\": \"1234\",\n    \"nested\":{\n      \"created_at\": 1234,\n      \"nested\": {\n        \"created_at\": 1234,\n        \"arr\": [\n          {\n            \"created_at\": 1234\n          },\n          {\n            \"updated_at\": 1234\n          }\n        ]\n      }\n    },\n    \"arr\": [\n      {\n        \"created_at\": 1234\n      },\n      {\n        \"updated_at\": 1234,\n        \"arr\": [\n          {\n            \"created_at\": 1234\n          },\n          {\n            \"updated_at\": 1234\n          }\n        ]\n      }\n    ]\n  }\n}"
  },
  {
    "path": "oryx/snapshotx/fixtures/2.json",
    "content": "{\n  \"ignore_nested\": [\n  ],\n  \"content\": {\n    \"created_at\": \"1234\",\n    \"updated_at\": \"1234\",\n    \"nested\":{\n      \"created_at\": 1234,\n      \"nested\": {\n        \"created_at\": 1234,\n        \"arr\": [\n          {\n            \"created_at\": 1234\n          },\n          {\n            \"updated_at\": 1234\n          }\n        ]\n      }\n    },\n    \"arr\": [\n      {\n        \"created_at\": 1234\n      },\n      {\n        \"updated_at\": 1234,\n        \"arr\": [\n          {\n            \"created_at\": 1234\n          },\n          {\n            \"updated_at\": 1234\n          }\n        ]\n      }\n    ]\n  }\n}"
  },
  {
    "path": "oryx/snapshotx/fixtures/3.json",
    "content": "{\n  \"ignore_nested\": [\n    \"created_at\"\n  ],\n  \"content\": {\n    \"created_at\": \"1234\",\n    \"updated_at\": \"1234\",\n    \"nested\": {\n      \"created_at\": 1234,\n      \"nested\": {\n        \"created_at\": 1234,\n        \"arr\": [\n          {\n            \"created_at\": 1234\n          },\n          {\n            \"updated_at\": 1234\n          }\n        ]\n      }\n    },\n    \"arr\": [\n      {\n        \"created_at\": 1234\n      },\n      {\n        \"updated_at\": 1234,\n        \"arr\": [\n          {\n            \"created_at\": 1234\n          },\n          {\n            \"updated_at\": 1234\n          }\n        ]\n      }\n    ]\n  }\n}"
  },
  {
    "path": "oryx/snapshotx/snapshot.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage snapshotx\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/bradleyjkemp/cupaloy/v2\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n)\n\ntype (\n\tOpt     = func(*options)\n\toptions struct {\n\t\tmodifiers []func(t *testing.T, raw []byte) []byte\n\t\tname      string\n\t}\n)\n\nfunc ExceptPaths(keys ...string) Opt {\n\treturn func(o *options) {\n\t\to.modifiers = append(o.modifiers, func(t *testing.T, raw []byte) []byte {\n\t\t\tfor _, key := range keys {\n\t\t\t\tvar err error\n\t\t\t\traw, err = sjson.DeleteBytes(raw, key)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}\n\t\t\treturn raw\n\t\t})\n\t}\n}\n\nfunc ExceptNestedKeys(nestedKeys ...string) Opt {\n\treturn func(o *options) {\n\t\to.modifiers = append(o.modifiers, func(t *testing.T, raw []byte) []byte {\n\t\t\tparsed := gjson.ParseBytes(raw)\n\t\t\trequire.True(t, parsed.IsObject() || parsed.IsArray())\n\t\t\treturn deleteMatches(t, \"\", parsed, nestedKeys, []string{}, raw)\n\t\t})\n\t}\n}\n\nfunc WithReplacement(str, replace string) Opt {\n\treturn func(o *options) {\n\t\to.modifiers = append(o.modifiers, func(t *testing.T, raw []byte) []byte {\n\t\t\treturn bytes.ReplaceAll(raw, []byte(str), []byte(replace))\n\t\t})\n\t}\n}\n\nfunc WithName(name string) Opt {\n\treturn func(o *options) {\n\t\to.name = name\n\t}\n}\n\nfunc newOptions(opts ...Opt) *options {\n\to := &options{}\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\treturn o\n}\n\nfunc (o *options) applyModifiers(t *testing.T, compare []byte) []byte {\n\tfor _, modifier := range o.modifiers {\n\t\tcompare = modifier(t, compare)\n\t}\n\treturn compare\n}\n\nvar snapshot = cupaloy.New(cupaloy.SnapshotFileExtension(\".json\"))\n\nfunc SnapshotTJSON[C ~string | ~[]byte](t *testing.T, compare C, opts ...Opt) {\n\tSnapshotT(t, json.RawMessage(compare), opts...)\n}\n\nfunc SnapshotT(t *testing.T, actual any, opts ...Opt) {\n\tt.Helper()\n\tcompare, err := json.MarshalIndent(actual, \"\", \"  \")\n\trequire.NoErrorf(t, err, \"%+v\", actual)\n\n\to := newOptions(opts...)\n\tcompare = o.applyModifiers(t, compare)\n\n\tif o.name == \"\" {\n\t\tsnapshot.SnapshotT(t, compare)\n\t} else {\n\t\tname := strings.ReplaceAll(t.Name()+\"_\"+o.name, \"/\", \"-\")\n\t\trequire.NoError(t, snapshot.SnapshotWithName(name, compare))\n\t}\n}\n\n// SnapshotTExcept is deprecated in favor of SnapshotT with Opt.\n//\n// DEPRECATED: please use SnapshotT instead\nfunc SnapshotTExcept(t *testing.T, actual interface{}, except []string) {\n\tt.Helper()\n\tcompare, err := json.MarshalIndent(actual, \"\", \"  \")\n\trequire.NoError(t, err, \"%+v\", actual)\n\tfor _, e := range except {\n\t\tcompare, err = sjson.DeleteBytes(compare, e)\n\t\trequire.NoError(t, err, \"%s\", e)\n\t}\n\n\tsnapshot.SnapshotT(t, compare)\n}\n\nfunc deleteMatches(t *testing.T, key string, result gjson.Result, matches []string, parents []string, content []byte) []byte {\n\tpath := parents\n\tif key != \"\" {\n\t\tpath = append(parents, key)\n\t}\n\n\tif result.IsObject() {\n\t\tresult.ForEach(func(key, value gjson.Result) bool {\n\t\t\tcontent = deleteMatches(t, key.String(), value, matches, path, content)\n\t\t\treturn true\n\t\t})\n\t} else if result.IsArray() {\n\t\tvar i int\n\t\tresult.ForEach(func(_, value gjson.Result) bool {\n\t\t\tcontent = deleteMatches(t, fmt.Sprintf(\"%d\", i), value, matches, path, content)\n\t\t\ti++\n\t\t\treturn true\n\t\t})\n\t}\n\n\tif slices.Contains(matches, key) {\n\t\tcontent, err := sjson.DeleteBytes(content, strings.Join(path, \".\"))\n\t\trequire.NoError(t, err)\n\t\treturn content\n\t}\n\n\treturn content\n}\n"
  },
  {
    "path": "oryx/sqlcon/connector.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// Package sqlcon provides helpers for dealing with SQL connectivity.\npackage sqlcon\n\nimport (\n\t\"runtime\"\n\t\"strings\"\n)\n\n// GetDriverName returns the driver name of a given DSN.\nfunc GetDriverName(dsn string) string {\n\treturn strings.Split(dsn, \"://\")[0]\n}\nfunc maxParallelism() int {\n\tmaxProcs := runtime.GOMAXPROCS(0)\n\tnumCPU := runtime.NumCPU()\n\tif maxProcs < numCPU {\n\t\treturn maxProcs\n\t}\n\treturn numCPU\n}\n"
  },
  {
    "path": "oryx/sqlcon/dockertest/cockroach.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage dockertest\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cockroachdb/cockroach-go/v2/testserver\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc NewLocalTestCRDBServer(t testing.TB) string {\n\tts, err := testserver.NewTestServer(testserver.CustomVersionOpt(\"25.3.3\"))\n\trequire.NoError(t, err)\n\tt.Cleanup(ts.Stop)\n\n\trequire.NoError(t, ts.WaitForInit())\n\n\tts.PGURL().Scheme = \"cockroach\"\n\treturn ts.PGURL().String()\n}\n"
  },
  {
    "path": "oryx/sqlcon/dockertest/test_helper.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage dockertest\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/api/types/filters\"\n\t\"github.com/docker/docker/client\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/pop/v6\"\n\n\t\"github.com/ory/dockertest/v4\"\n\t\"github.com/ory/x/stringsx\"\n)\n\nfunc ConnectPop(t require.TestingT, url string) (c *pop.Connection) {\n\trequire.EventuallyWithT(t, func(t *assert.CollectT) {\n\t\tvar err error\n\t\tc, err = pop.NewConnection(&pop.ConnectionDetails{\n\t\t\tURL: url,\n\t\t})\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, c.Open())\n\t\trequire.NoError(t, c.RawQuery(\"select version()\").Exec())\n\t}, 5*time.Minute, time.Second*5, \"could not connect to database at %s\", url)\n\treturn\n}\n\n// ## PostgreSQL ##\n\nfunc startPostgreSQL(t testing.TB, version string) dockertest.Resource {\n\tpool := dockertest.NewPoolT(t, \"\")\n\treturn pool.RunT(t, \"postgres\",\n\t\tdockertest.WithTag(cmp.Or(version, \"16\")),\n\t\tdockertest.WithoutReuse(),\n\t\tdockertest.WithEnv([]string{\"PGUSER=postgres\", \"POSTGRES_PASSWORD=secret\", \"POSTGRES_DB=postgres\"}),\n\t)\n}\n\n// RunTestPostgreSQL runs a PostgreSQL database and returns the URL to it.\n// If a docker container is started for the database, the container be removed\n// at the end of the test.\nfunc RunTestPostgreSQL(t testing.TB) string {\n\tif dsn := os.Getenv(\"TEST_DATABASE_POSTGRESQL\"); dsn != \"\" {\n\t\tt.Logf(\"Skipping Docker setup because environment variable TEST_DATABASE_POSTGRESQL is set to: %s\", dsn)\n\t\treturn dsn\n\t}\n\tr := startPostgreSQL(t, \"\")\n\treturn fmt.Sprintf(\"postgres://postgres:secret@127.0.0.1:%s/postgres?sslmode=disable\", r.GetPort(\"5432/tcp\"))\n}\n\n// RunTestPostgreSQLWithVersion connects to a PostgreSQL database.\nfunc RunTestPostgreSQLWithVersion(t testing.TB, version string) string {\n\tif dsn := os.Getenv(\"TEST_DATABASE_POSTGRESQL\"); dsn != \"\" {\n\t\treturn dsn\n\t}\n\tr := startPostgreSQL(t, version)\n\treturn fmt.Sprintf(\"postgres://postgres:secret@127.0.0.1:%s/postgres?sslmode=disable\", r.GetPort(\"5432/tcp\"))\n}\n\n// ## MySQL ##\n\nfunc startMySQL(t testing.TB, version string) dockertest.Resource {\n\tpool := dockertest.NewPoolT(t, \"\")\n\treturn pool.RunT(t, \"mysql\",\n\t\tdockertest.WithTag(cmp.Or(version, \"8.0\")),\n\t\tdockertest.WithoutReuse(),\n\t\tdockertest.WithEnv([]string{\n\t\t\t\"MYSQL_ROOT_PASSWORD=secret\",\n\t\t\t\"MYSQL_ROOT_HOST=%\",\n\t\t}),\n\t)\n}\n\n// RunTestMySQL runs a MySQL database and returns the URL to it.\n// If a docker container is started for the database, the container be removed\n// at the end of the test.\nfunc RunTestMySQL(t testing.TB) string {\n\tif dsn := os.Getenv(\"TEST_DATABASE_MYSQL\"); dsn != \"\" {\n\t\tt.Logf(\"Skipping Docker setup because environment variable TEST_DATABASE_MYSQL is set to: %s\", dsn)\n\t\treturn dsn\n\t}\n\tr := startMySQL(t, \"\")\n\treturn fmt.Sprintf(\"mysql://root:secret@tcp(localhost:%s)/mysql?parseTime=true&multiStatements=true\", r.GetPort(\"3306/tcp\"))\n}\n\n// RunTestMySQLWithVersion runs a MySQL database in the specified version and returns the URL to it.\n// If a docker container is started for the database, the container be removed\n// at the end of the test.\nfunc RunTestMySQLWithVersion(t testing.TB, version string) string {\n\tif dsn := os.Getenv(\"TEST_DATABASE_MYSQL\"); dsn != \"\" {\n\t\tt.Logf(\"Skipping Docker setup because environment variable TEST_DATABASE_MYSQL is set to: %s\", dsn)\n\t\treturn dsn\n\t}\n\tr := startMySQL(t, version)\n\treturn fmt.Sprintf(\"mysql://root:secret@tcp(localhost:%s)/mysql?parseTime=true&multiStatements=true\", r.GetPort(\"3306/tcp\"))\n}\n\n// ## CockroachDB\n\nfunc startCockroachDB(t testing.TB, version string) dockertest.Resource {\n\tpool := dockertest.NewPoolT(t, \"\")\n\treturn pool.RunT(t, \"cockroachdb/cockroach\",\n\t\tdockertest.WithTag(cmp.Or(version, \"latest-v25.4\")),\n\t\tdockertest.WithoutReuse(),\n\t\tdockertest.WithCmd([]string{\"start-single-node\", \"--insecure\"}),\n\t)\n}\n\n// RunTestCockroachDB runs a CockroachDB database and returns the URL to it.\n// If a docker container is started for the database, the container be removed\n// at the end of the test.\nfunc RunTestCockroachDB(t testing.TB) string {\n\treturn RunTestCockroachDBWithVersion(t, \"\")\n}\n\n// RunTestCockroachDBWithVersion runs a CockroachDB database and returns the URL to it.\n// If a docker container is started for the database, the container be removed\n// at the end of the test.\nfunc RunTestCockroachDBWithVersion(t testing.TB, version string) string {\n\tif dsn := os.Getenv(\"TEST_DATABASE_COCKROACHDB\"); dsn != \"\" {\n\t\tt.Logf(\"Skipping Docker setup because environment variable TEST_DATABASE_COCKROACHDB is set to: %s\", dsn)\n\t\treturn dsn\n\t}\n\tr := startCockroachDB(t, version)\n\treturn fmt.Sprintf(\"cockroach://root@localhost:%s/defaultdb?sslmode=disable\", r.GetPort(\"26257/tcp\"))\n}\n\nfunc DumpSchema(t testing.TB, c *pop.Connection) string {\n\tname, database, port := c.Dialect.Name(), c.Dialect.Details().Database, c.Dialect.Details().Port\n\tt.Logf(\"Dumping schema for dialect %s, database %s on port %s\", name, database, port)\n\n\tvar cmd []string\n\tvar appendToDump string\n\tswitch dialects := stringsx.SwitchExact(name); {\n\tcase dialects.AddCase(\"sqlite3\"):\n\t\treturn dumpSQLiteSchema(t, c)\n\tcase dialects.AddCase(\"postgres\"):\n\t\tcmd = []string{\"pg_dump\", \"--username\", \"postgres\", \"--schema-only\", \"--dbname\", database}\n\t\t// we need to set the search path because the postgres dump always unsets it\n\t\tappendToDump = \"SET search_path TO public;\\n\"\n\tcase dialects.AddCase(\"mysql\"):\n\t\tcmd = []string{\"mysqldump\", \"--user\", \"root\", \"--password=secret\", \"--no-data\", database}\n\tcase dialects.AddCase(\"cockroach\"):\n\t\tcmd = []string{\"cockroach\", \"sql\", \"--insecure\", \"--database\", database, \"--execute\", \"SHOW CREATE ALL TABLES; SHOW CREATE ALL TYPES;\", \"--format\", \"raw\"}\n\tdefault:\n\t\tt.Log(dialects.ToUnknownCaseErr())\n\t\tt.FailNow()\n\t\treturn \"\"\n\t}\n\n\tcli, err := client.NewClientWithOpts(client.FromEnv)\n\trequire.NoError(t, err)\n\tcontainers, err := cli.ContainerList(t.Context(), container.ListOptions{\n\t\tFilters: filters.NewArgs(filters.Arg(\"publish\", port)),\n\t})\n\trequire.NoError(t, err)\n\trequire.Lenf(t, containers, 1, \"expected exactly one %s container with port %s\", name, port)\n\n\tprocess, err := cli.ContainerExecCreate(t.Context(), containers[0].ID, container.ExecOptions{\n\t\tTty:          true,\n\t\tAttachStdout: true,\n\t\tCmd:          cmd,\n\t})\n\trequire.NoError(t, err)\n\n\tresp, err := cli.ContainerExecAttach(t.Context(), process.ID, container.ExecAttachOptions{\n\t\tTty: true,\n\t})\n\trequire.NoError(t, err)\n\tdump, err := io.ReadAll(resp.Reader)\n\trequire.NoErrorf(t, err, \"%s\", dump)\n\n\td := string(dump) + appendToDump\n\td = regexp.MustCompile(`(--|#|\\\\|mysqldump|SHOW CREATE)[^\\n]*\\n`).ReplaceAllLiteralString(d, \"\") // comments and other non-schema lines\n\td = strings.ReplaceAll(d, \"\\r\\n\", \"\\n\")\n\td = regexp.MustCompile(`\\n\\n+`).ReplaceAllLiteralString(d, \"\\n\\n\")\n\treturn d\n}\n\nfunc dumpSQLiteSchema(t testing.TB, c *pop.Connection) string {\n\tvar sqls []string\n\trequire.NoError(t, c.RawQuery(\"SELECT sql FROM sqlite_master WHERE type IN ('table', 'index', 'trigger', 'view') AND name NOT LIKE 'sqlite_%' ORDER BY name\").All(&sqls))\n\treturn strings.Join(sqls, \";\\n\") + \";\\n\"\n}\n"
  },
  {
    "path": "oryx/sqlcon/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sqlcon\n\nimport (\n\t\"database/sql\"\n\t\"net/http\"\n\n\t\"github.com/go-sql-driver/mysql\"\n\t\"github.com/jackc/pgconn\"\n\tpgxconn \"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/lib/pq\"\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/grpc/codes\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar (\n\t// ErrUniqueViolation is returned when^a SQL INSERT / UPDATE command returns a conflict.\n\tErrUniqueViolation = &herodot.DefaultError{\n\t\tCodeField:     http.StatusConflict,\n\t\tGRPCCodeField: codes.AlreadyExists,\n\t\tStatusField:   http.StatusText(http.StatusConflict),\n\t\tErrorField:    \"Unable to insert or update resource because a resource with that value exists already\",\n\t}\n\t// ErrNoRows is returned when a SQL SELECT statement returns no rows.\n\tErrNoRows = &herodot.DefaultError{\n\t\tCodeField:     http.StatusNotFound,\n\t\tGRPCCodeField: codes.NotFound,\n\t\tStatusField:   http.StatusText(http.StatusNotFound),\n\t\tErrorField:    \"Unable to locate the resource\",\n\t}\n\t// ErrConcurrentUpdate is returned when the database is unable to serialize access due to a concurrent update.\n\tErrConcurrentUpdate = &herodot.DefaultError{\n\t\tCodeField:     http.StatusBadRequest,\n\t\tGRPCCodeField: codes.Aborted,\n\t\tStatusField:   http.StatusText(http.StatusBadRequest),\n\t\tErrorField:    \"Unable to serialize access due to a concurrent update in another session\",\n\t}\n\tErrNoSuchTable = &herodot.DefaultError{\n\t\tCodeField:     http.StatusInternalServerError,\n\t\tGRPCCodeField: codes.Internal,\n\t\tStatusField:   http.StatusText(http.StatusInternalServerError),\n\t\tErrorField:    \"Unable to locate the table\",\n\t}\n\tErrNoSuchColumn = &herodot.DefaultError{\n\t\tCodeField:     http.StatusInternalServerError,\n\t\tGRPCCodeField: codes.Internal,\n\t\tStatusField:   http.StatusText(http.StatusInternalServerError),\n\t\tErrorField:    \"Unable to locate the column\",\n\t}\n)\n\nfunc handlePostgres(err error, sqlState string) error {\n\tswitch sqlState {\n\tcase \"23505\": // \"unique_violation\"\n\t\treturn errors.WithStack(ErrUniqueViolation.WithWrap(err))\n\tcase \"40001\", // \"serialization_failure\" in CRDB\n\t\t\"CR000\": // \"serialization_failure\"\n\t\treturn errors.WithStack(ErrConcurrentUpdate.WithWrap(err))\n\tcase \"42P01\": // \"no such table\"\n\t\treturn errors.WithStack(ErrNoSuchTable.WithWrap(err))\n\tcase \"42703\": // \"no such column\"\n\t\treturn errors.WithStack(ErrNoSuchColumn.WithWrap(err))\n\t}\n\treturn errors.WithStack(err)\n}\n\ntype stater interface {\n\tSQLState() string\n}\n\n// HandleError returns the right sqlcon.Err* depending on the input error.\nfunc HandleError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tvar st stater\n\tif errors.Is(err, sql.ErrNoRows) {\n\t\treturn errors.WithStack(ErrNoRows)\n\t} else if errors.As(err, &st) {\n\t\treturn errors.WithStack(handlePostgres(err, st.SQLState()))\n\t} else if e := new(pq.Error); errors.As(err, &e) {\n\t\treturn errors.WithStack(handlePostgres(err, string(e.Code)))\n\t} else if e := new(pgconn.PgError); errors.As(err, &e) {\n\t\treturn errors.WithStack(handlePostgres(err, e.Code))\n\t} else if e := new(pgxconn.PgError); errors.As(err, &e) {\n\t\treturn errors.WithStack(handlePostgres(err, e.Code))\n\t} else if e := new(mysql.MySQLError); errors.As(err, &e) {\n\t\tswitch e.Number {\n\t\tcase 1062: // https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html#error_er_dup_entry\n\t\t\treturn errors.WithStack(ErrUniqueViolation.WithWrap(err))\n\t\tcase 1146: // https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html#error_er_no_such_table\n\t\t\treturn errors.WithStack(ErrNoSuchTable.WithWrap(e))\n\t\tcase 1054: // https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html#error_er_bad_field_error\n\t\t\treturn errors.WithStack(ErrNoSuchColumn.WithWrap(e))\n\t\t}\n\t}\n\n\tif err := handleSqlite(err); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn errors.WithStack(err)\n}\n"
  },
  {
    "path": "oryx/sqlcon/error_nosqlite.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n//go:build !sqlite\n// +build !sqlite\n\npackage sqlcon\n\n// handleSqlite handles the error iff (if and only if) it is an sqlite error\nfunc handleSqlite(_ error) error {\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/sqlcon/error_sqlite.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n//go:build sqlite\n// +build sqlite\n\npackage sqlcon\n\nimport (\n\t\"strings\"\n\n\t\"github.com/mattn/go-sqlite3\"\n\t\"github.com/pkg/errors\"\n)\n\n// handleSqlite handles the error iff (if and only if) it is an sqlite error\nfunc handleSqlite(err error) error {\n\tif e := new(sqlite3.Error); errors.As(err, e) {\n\t\tswitch e.ExtendedCode {\n\t\tcase sqlite3.ErrConstraintUnique:\n\t\t\tfallthrough\n\t\tcase sqlite3.ErrConstraintPrimaryKey:\n\t\t\treturn errors.WithStack(ErrUniqueViolation.WithWrap(err))\n\n\t\t}\n\n\t\tswitch e.Code {\n\t\tcase sqlite3.ErrError:\n\t\t\tif strings.Contains(err.Error(), \"no such table\") {\n\t\t\t\treturn errors.WithStack(ErrNoSuchTable.WithWrap(err))\n\t\t\t}\n\t\tcase sqlite3.ErrLocked:\n\t\t\treturn errors.WithStack(ErrConcurrentUpdate.WithWrap(err))\n\t\t}\n\n\t\tif strings.HasPrefix(e.Error(), \"no such column:\") ||\n\t\t\tstrings.Contains(e.Error(), \"has no column named\") {\n\t\t\treturn errors.WithStack(ErrNoSuchColumn.WithWrap(err))\n\t\t}\n\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/sqlcon/message.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sqlcon\n\n// HelpMessage returns a string explaining how to set up SQL using environment variables.\nfunc HelpMessage() string {\n\treturn `- DATABASE_URL: A DSN to a persistent backend. Various backends are supported:\n\n  - Changes are lost on process death (ephemeral storage):\n\n\t- Memory: If DATABASE_URL is \"memory\", data will be written to memory and is lost when you restart this instance.\n\t  Example: DATABASE_URL=memory\n\n  - Changes are kept after process death (persistent storage):\n\n    - SQL Databases: Officially, PostgreSQL, MySQL and CockroachDB are supported. This project works best with PostgreSQL.\n\n\t  - PostgreSQL: If DATABASE_URL is a DSN starting with postgres://, PostgreSQL will be used as storage backend.\n\t\tExample: DATABASE_URL=postgres://user:password@host:123/database\n\n\t\tAdditionally, the following query/DSN parameters are supported:\n\n      \t* max_conns (number): Sets the maximum number of open connections to the database. Defaults to the number of CPU cores times 2.\n\t\t* max_idle_conns (number): Sets the maximum number of connections in the idle. Defaults to the number of CPU cores.\n        * max_conn_lifetime (duratino): Sets the maximum amount of time (\"ms\", \"s\", \"m\", \"h\") a connection may be reused.\n\t\t  Defaults to 0s (disabled).\n\t\t* sslmode (string): Whether or not to use SSL (default is require)\n\t\t  * disable - No SSL\n\t\t  * require - Always SSL (skip verification)\n\t\t  * verify-ca - Always SSL (verify that the certificate presented by the\n\t\t    server was signed by a trusted CA)\n\t\t  * verify-full - Always SSL (verify that the certification presented by\n\t\t    the server was signed by a trusted CA and the server host name\n\t\t    matches the one in the certificate)\n\t\t* fallback_application_name (string): An application_name to fall back to if one isn't provided.\n\t\t* connect_timeout (number): Maximum wait for connection, in seconds. Zero or\n\t\t  not specified means wait indefinitely.\n\t\t* sslcert (string): Cert file location. The file must contain PEM encoded data.\n\t\t* sslkey (string): Key file location. The file must contain PEM encoded data.\n\t\t* sslrootcert (string): The location of the root certificate file. The file\n\t\t  must contain PEM encoded data.\n\t\tExample: DATABASE_URL=postgres://user:password@host:123/database?sslmode=verify-full\n\n\t  - MySQL: If DATABASE_URL is a DSN starting with mysql:// MySQL will be used as storage backend.\n\t\tBe aware that the ?parseTime=true parameter is mandatory, or timestamps will not work.\n\t\tExample: DATABASE_URL=mysql://user:password@tcp(host:123)/database?parseTime=true\n\n\t\tAdditionally, the following query/DSN parameters are supported:\n\t\t* collation (string): Sets the collation used for client-server interaction on connection. In contrast to charset, \n\t\t  collation does not issue additional queries. If the specified collation is unavailable on the target server,\n\t\t  the connection will fail.\n\t\t* loc (string): Sets the location for time.Time values. Note that this sets the location for time.Time values\n\t\t  but does not change MySQL's time_zone setting. For that set the time_zone DSN parameter. Please keep in mind,\n\t\t  that param values must be url.QueryEscape'ed. Alternatively you can manually replace the / with %2F.\n\t\t  For example US/Pacific would be loc=US%2FPacific.\n\t\t* maxAllowedPacket (number): Max packet size allowed in bytes. The default value is 4 MiB and should be\n\t\t  adjusted to match the server settings. maxAllowedPacket=0 can be used to automatically fetch the max_allowed_packet variable from server on every connection.\n\t\t* readTimeout (duration): I/O read timeout. The value must be a decimal number with a unit suffix\n\t\t  (\"ms\", \"s\", \"m\", \"h\"), such as \"30s\", \"0.5m\" or \"1m30s\".\n\t\t* timeout (duration): Timeout for establishing connections, aka dial timeout. The value must be a decimal number with a unit suffix\n\t\t  (\"ms\", \"s\", \"m\", \"h\"), such as \"30s\", \"0.5m\" or \"1m30s\".\n\t\t* tls (bool / string): tls=true enables TLS / SSL encrypted connection to the server. Use skip-verify if\n\t\t  you want to use a self-signed or invalid certificate (server side).\n\t\t* writeTimeout (duration): I/O write timeout. The value must be a decimal number with a unit suffix\n\t\t  (\"ms\", \"s\", \"m\", \"h\"), such as \"30s\", \"0.5m\" or \"1m30s\".\n\t\tExample: DATABASE_URL=mysql://user:password@tcp(host:123)/database?parseTime=true&writeTimeout=123s\n\n\t  - CockroachDB: If DATABASE_URL is a DSN starting with cockroach://, CockroachDB will be used as storage backend.\n\t\tExample: DATABASE_URL=cockroach://user:password@host:123/database\n\n\t\tAdditionally, the following query/DSN parameters are supported:\n\t\t* sslmode (string): Whether or not to use SSL (default is require)\n\t\t  * disable - No SSL\n\t\t  * require - Always SSL (skip verification)\n\t\t  * verify-ca - Always SSL (verify that the certificate presented by the\n\t\t    server was signed by a trusted CA)\n\t\t  * verify-full - Always SSL (verify that the certification presented by\n\t\t    the server was signed by a trusted CA and the server host name\n\t\t    matches the one in the certificate)\n\t\t* application_name (string): An initial value for the application_name session variable.\n\t\t* sslcert (string): Cert file location. The file must contain PEM encoded data.\n\t\t* sslkey (string): Key file location. The file must contain PEM encoded data.\n\t\t* sslrootcert (string): The location of the root certificate file. The file\n\t\t  must contain PEM encoded data.\n\t\tExample: DATABASE_URL=cockroach://user:password@host:123/database?sslmode=verify-full`\n}\n"
  },
  {
    "path": "oryx/sqlcon/parse_opts.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sqlcon\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ory/x/logrusx\"\n)\n\n// ParseConnectionOptions parses values for max_conns, max_idle_conns, max_conn_lifetime from DSNs.\n// It also returns the URI without those query parameters.\nfunc ParseConnectionOptions(l *logrusx.Logger, dsn string) (maxConns int, maxIdleConns int, maxConnLifetime, maxIdleConnTime time.Duration, cleanedDSN string) {\n\tmaxConns = maxParallelism() * 2\n\tmaxIdleConns = maxParallelism()\n\tcleanedDSN = dsn\n\n\thostPath, rawQuery, ok := strings.Cut(dsn, \"?\")\n\tif !ok {\n\t\tl.\n\t\t\tWithField(\"sql_max_connections\", maxConns).\n\t\t\tWithField(\"sql_max_idle_connections\", maxIdleConns).\n\t\t\tWithField(\"sql_max_connection_lifetime\", maxConnLifetime).\n\t\t\tWithField(\"sql_max_idle_connection_time\", maxIdleConnTime).\n\t\t\tDebugf(\"No SQL connection options have been defined, falling back to default connection options.\")\n\t\treturn\n\t}\n\n\tquery, err := url.ParseQuery(rawQuery)\n\tif err != nil {\n\t\tl.\n\t\t\tWithField(\"sql_max_connections\", maxConns).\n\t\t\tWithField(\"sql_max_idle_connections\", maxIdleConns).\n\t\t\tWithField(\"sql_max_connection_lifetime\", maxConnLifetime).\n\t\t\tWithField(\"sql_max_idle_connection_time\", maxIdleConnTime).\n\t\t\tWithError(err).\n\t\t\tWarnf(\"Unable to parse SQL DSN query, falling back to default connection options.\")\n\t\treturn\n\t}\n\n\tif v := query.Get(\"max_conns\"); v != \"\" {\n\t\ts, err := strconv.ParseInt(v, 10, 64)\n\t\tif err != nil {\n\t\t\tl.WithError(err).Warnf(`SQL DSN query parameter \"max_conns\" value %v could not be parsed to int, falling back to default value %d`, v, maxConns)\n\t\t} else {\n\t\t\tmaxConns = int(s)\n\t\t}\n\t\tquery.Del(\"max_conns\")\n\t}\n\n\tif v := query.Get(\"max_idle_conns\"); v != \"\" {\n\t\ts, err := strconv.ParseInt(v, 10, 64)\n\t\tif err != nil {\n\t\t\tl.WithError(err).Warnf(`SQL DSN query parameter \"max_idle_conns\" value %v could not be parsed to int, falling back to default value %d`, v, maxIdleConns)\n\t\t} else {\n\t\t\tmaxIdleConns = int(s)\n\t\t}\n\t\tquery.Del(\"max_idle_conns\")\n\t}\n\n\tif v := query.Get(\"max_conn_lifetime\"); v != \"\" {\n\t\ts, err := time.ParseDuration(v)\n\t\tif err != nil {\n\t\t\tl.WithError(err).Warnf(`SQL DSN query parameter \"max_conn_lifetime\" value %v could not be parsed to duration, falling back to default value %d`, v, maxConnLifetime)\n\t\t} else {\n\t\t\tmaxConnLifetime = s\n\t\t}\n\t\tquery.Del(\"max_conn_lifetime\")\n\t}\n\n\tif v := query.Get(\"max_conn_idle_time\"); v != \"\" {\n\t\ts, err := time.ParseDuration(v)\n\t\tif err != nil {\n\t\t\tl.WithError(err).Warnf(`SQL DSN query parameter \"max_conn_idle_time\" value %v could not be parsed to duration, falling back to default value %d`, v, maxIdleConnTime)\n\t\t} else {\n\t\t\tmaxIdleConnTime = s\n\t\t}\n\t\tquery.Del(\"max_conn_idle_time\")\n\t}\n\tcleanedDSN = fmt.Sprintf(\"%s?%s\", hostPath, query.Encode())\n\n\treturn\n}\n\n// FinalizeDSN will return a finalized DSN URI.\nfunc FinalizeDSN(l *logrusx.Logger, dsn string) string {\n\tif !strings.HasPrefix(dsn, \"mysql://\") {\n\t\treturn dsn\n\t}\n\n\tvar q url.Values\n\thostPath, query, ok := strings.Cut(dsn, \"?\")\n\n\tif !ok {\n\t\tq = make(url.Values)\n\t} else {\n\t\tvar err error\n\t\tq, err = url.ParseQuery(query)\n\t\tif err != nil {\n\t\t\tl.WithError(err).Warnf(\"Unable to parse SQL DSN query, could not finalize the DSN URI.\")\n\t\t\treturn dsn\n\t\t}\n\t}\n\n\tq.Set(\"multiStatements\", \"true\")\n\tq.Set(\"parseTime\", \"true\")\n\n\t// This causes an UPDATE to return the number of matching rows instead of\n\t// the number of rows changed. This ensures compatibility with PostgreSQL and SQLite behavior.\n\tq.Set(\"clientFoundRows\", \"true\")\n\n\treturn fmt.Sprintf(\"%s?%s\", hostPath, q.Encode())\n}\n"
  },
  {
    "path": "oryx/sqlxx/batch/.snapshots/Test_buildInsertQueryArgs-case=cockroach.json",
    "content": "{\n  \"TableName\": \"\\\"test_models\\\"\",\n  \"ColumnsDecl\": \"\\\"created_at\\\", \\\"id\\\", \\\"int\\\", \\\"nid\\\", \\\"null_time_ptr\\\", \\\"string\\\", \\\"updated_at\\\"\",\n  \"Columns\": [\n    \"created_at\",\n    \"id\",\n    \"int\",\n    \"nid\",\n    \"null_time_ptr\",\n    \"string\",\n    \"updated_at\"\n  ],\n  \"Placeholders\": \"(?, ?, ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?)\"\n}\n"
  },
  {
    "path": "oryx/sqlxx/batch/.snapshots/Test_buildInsertQueryArgs-case=testModel.json",
    "content": "{\n  \"TableName\": \"\\\"test_models\\\"\",\n  \"ColumnsDecl\": \"\\\"created_at\\\", \\\"id\\\", \\\"int\\\", \\\"nid\\\", \\\"null_time_ptr\\\", \\\"string\\\", \\\"updated_at\\\"\",\n  \"Columns\": [\n    \"created_at\",\n    \"id\",\n    \"int\",\n    \"nid\",\n    \"null_time_ptr\",\n    \"string\",\n    \"updated_at\"\n  ],\n  \"Placeholders\": \"(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?)\"\n}\n"
  },
  {
    "path": "oryx/sqlxx/batch/.snapshots/Test_buildInsertQueryValues-case=testModel-case=cockroach.json",
    "content": "[\n  \"0001-01-01T00:00:00Z\",\n  \"0001-01-01T00:00:00Z\",\n  \"string\",\n  42,\n  null,\n  {\n    \"ID\": \"00000000-0000-0000-0000-000000000000\",\n    \"NID\": \"00000000-0000-0000-0000-000000000000\",\n    \"String\": \"string\",\n    \"Int\": 42,\n    \"NullTimePtr\": null,\n    \"created_at\": \"0001-01-01T00:00:00Z\",\n    \"updated_at\": \"0001-01-01T00:00:00Z\"\n  }\n]\n"
  },
  {
    "path": "oryx/sqlxx/batch/create.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage batch\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/jmoiron/sqlx/reflectx\"\n\n\t\"github.com/ory/x/dbal\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/pop/v6\"\n\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/x/sqlxx\"\n)\n\ntype (\n\tinsertQueryArgs struct {\n\t\tTableName    string\n\t\tColumnsDecl  string\n\t\tColumns      []string\n\t\tPlaceholders string\n\t}\n\tquoter interface {\n\t\tQuote(key string) string\n\t}\n\tTracerConnection struct {\n\t\tTracer     *otelx.Tracer\n\t\tConnection *pop.Connection\n\t}\n)\n\nfunc buildInsertQueryArgs[T any](ctx context.Context, dialect string, mapper *reflectx.Mapper, quoter quoter, models []*T) insertQueryArgs {\n\tvar (\n\t\tv     T\n\t\tmodel = pop.NewModel(v, ctx)\n\n\t\tcolumns        []string\n\t\tquotedColumns  []string\n\t\tplaceholders   []string\n\t\tplaceholderRow []string\n\t)\n\n\tfor _, col := range model.Columns().Cols {\n\t\tcolumns = append(columns, col.Name)\n\t\tplaceholderRow = append(placeholderRow, \"?\")\n\t}\n\n\t// We sort for the sole reason that the test snapshots are deterministic.\n\tsort.Strings(columns)\n\n\tfor _, col := range columns {\n\t\tquotedColumns = append(quotedColumns, quoter.Quote(col))\n\t}\n\n\t// We generate a list (for every row one) of VALUE statements here that\n\t// will be substituted by their column values later:\n\t//\n\t//\t(?, ?, ?, ?),\n\t//\t(?, ?, ?, ?),\n\t//\t(?, ?, ?, ?)\n\tfor _, m := range models {\n\t\tm := reflect.ValueOf(m)\n\n\t\tpl := make([]string, len(placeholderRow))\n\t\tcopy(pl, placeholderRow)\n\n\t\t// There is a special case - when using CockroachDB we want to generate\n\t\t// UUIDs using \"gen_random_uuid()\" which ends up in a VALUE statement of:\n\t\t//\n\t\t//\t(gen_random_uuid(), ?, ?, ?),\n\t\tfor k := range placeholderRow {\n\t\t\tif columns[k] != \"id\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfield := mapper.FieldByName(m, columns[k])\n\t\t\tval, ok := field.Interface().(uuid.UUID)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif val == uuid.Nil && dialect == dbal.DriverCockroachDB {\n\t\t\t\tpl[k] = \"gen_random_uuid()\"\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tplaceholders = append(placeholders, fmt.Sprintf(\"(%s)\", strings.Join(pl, \", \")))\n\t}\n\n\treturn insertQueryArgs{\n\t\tTableName:    quoter.Quote(model.TableName()),\n\t\tColumnsDecl:  strings.Join(quotedColumns, \", \"),\n\t\tColumns:      columns,\n\t\tPlaceholders: strings.Join(placeholders, \",\\n\"),\n\t}\n}\n\nfunc buildInsertQueryValues[T any](dialect string, mapper *reflectx.Mapper, columns []string, models []*T, nowFunc func() time.Time) (values []any, err error) {\n\tfor _, m := range models {\n\t\tm := reflect.ValueOf(m)\n\n\t\tnow := nowFunc()\n\t\t// Append model fields to args\n\t\tfor _, c := range columns {\n\t\t\tfield := mapper.FieldByName(m, c)\n\n\t\t\tswitch c {\n\t\t\tcase \"created_at\":\n\t\t\t\tif pop.IsZeroOfUnderlyingType(field.Interface()) {\n\t\t\t\t\tfield.Set(reflect.ValueOf(now))\n\t\t\t\t}\n\t\t\tcase \"updated_at\":\n\t\t\t\tfield.Set(reflect.ValueOf(now))\n\t\t\tcase \"id\":\n\t\t\t\tif value, ok := field.Interface().(uuid.UUID); ok && value != uuid.Nil {\n\t\t\t\t\tbreak // breaks switch, not for\n\t\t\t\t} else if value, ok := field.Interface().(string); ok && len(value) > 0 {\n\t\t\t\t\tbreak // breaks switch, not for\n\t\t\t\t} else if dialect == dbal.DriverCockroachDB {\n\t\t\t\t\t// This is a special case:\n\t\t\t\t\t// 1. We're using cockroach\n\t\t\t\t\t// 2. It's the primary key field (\"ID\")\n\t\t\t\t\t// 3. A UUID was not yet set.\n\t\t\t\t\t//\n\t\t\t\t\t// If all these conditions meet, the VALUE statement will look as such:\n\t\t\t\t\t//\n\t\t\t\t\t//\t(gen_random_uuid(), ?, ?, ?, ...)\n\t\t\t\t\t//\n\t\t\t\t\t// For that reason, we do not add the ID value to the list of arguments,\n\t\t\t\t\t// because one of the arguments is using a built-in and thus doesn't need a value.\n\t\t\t\t\tcontinue // break switch, not for\n\t\t\t\t}\n\n\t\t\t\tid, err := uuid.NewV4()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tfield.Set(reflect.ValueOf(id))\n\t\t\t}\n\n\t\t\tvalues = append(values, field.Interface())\n\n\t\t\t// Special-handling for *sqlxx.NullTime: mapper.FieldByName sets this to a zero time.Time,\n\t\t\t// but we want a nil pointer instead.\n\t\t\tif i, ok := field.Interface().(*sqlxx.NullTime); ok {\n\t\t\t\tif time.Time(*i).IsZero() {\n\t\t\t\t\tfield.Set(reflect.Zero(field.Type()))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn values, nil\n}\n\ntype createOptions struct {\n\tonConflict string\n}\n\ntype option func(*createOptions)\n\nfunc OnConflictDoNothing() func(*createOptions) {\n\treturn func(o *createOptions) {\n\t\to.onConflict = \"ON CONFLICT DO NOTHING\"\n\t}\n}\n\n// CreateFromSlice is a helper around Create that accepts a slice of models\n// instead of a slice of model pointers.\nfunc CreateFromSlice[T any](ctx context.Context, p *TracerConnection, models []T, opts ...option) (err error) {\n\tvar ptrs []*T\n\tfor k := range models {\n\t\tptrs = append(ptrs, &models[k])\n\t}\n\treturn Create(ctx, p, ptrs, opts...)\n}\n\n// Create batch-inserts the given models into the database using a single INSERT statement.\n// The models are either all created or none.\nfunc Create[T any](ctx context.Context, p *TracerConnection, models []*T, opts ...option) (err error) {\n\tctx, span := p.Tracer.Tracer().Start(ctx, \"persistence.sql.batch.Create\")\n\tdefer otelx.End(span, &err)\n\n\tif len(models) == 0 {\n\t\treturn nil\n\t}\n\n\toptions := &createOptions{}\n\tfor _, opt := range opts {\n\t\topt(options)\n\t}\n\n\tvar v T\n\tmodel := pop.NewModel(v, ctx)\n\n\tconn := p.Connection\n\tquoter, ok := conn.Dialect.(quoter)\n\tif !ok {\n\t\treturn errors.Errorf(\"store is not a quoter: %T\", conn.Store)\n\t}\n\n\tqueryArgs := buildInsertQueryArgs(ctx, conn.Dialect.Name(), conn.TX.Mapper, quoter, models)\n\tvalues, err := buildInsertQueryValues(conn.Dialect.Name(), conn.TX.Mapper, queryArgs.Columns, models, func() time.Time { return time.Now().UTC().Truncate(time.Microsecond) })\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar returningClause string\n\tif conn.Dialect.Name() != dbal.DriverMySQL {\n\t\t// PostgreSQL, CockroachDB, SQLite support RETURNING.\n\t\treturningClause = fmt.Sprintf(\"RETURNING %s\", model.IDField())\n\t}\n\n\tquery := conn.Dialect.TranslateSQL(fmt.Sprintf(\n\t\t\"INSERT INTO %s (%s) VALUES\\n%s\\n%s\\n%s\",\n\t\tqueryArgs.TableName,\n\t\tqueryArgs.ColumnsDecl,\n\t\tqueryArgs.Placeholders,\n\t\toptions.onConflict,\n\t\treturningClause,\n\t))\n\n\trows, err := conn.TX.QueryContext(ctx, query, values...)\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\tdefer rows.Close()\n\n\t// Hydrate the models from the RETURNING clause.\n\t//\n\t// Databases not supporting RETURNING will just return 0 rows.\n\tcount := 0\n\tfor rows.Next() {\n\t\tif err := setModelID(rows, pop.NewModel(models[count], ctx)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcount++\n\t}\n\n\tif err := rows.Err(); err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\n\treturn sqlcon.HandleError(err)\n}\n\n// setModelID was copy & pasted from pop. It basically sets\n// the primary key to the given value read from the SQL row.\nfunc setModelID(row *sql.Rows, model *pop.Model) error {\n\tel := reflect.ValueOf(model.Value).Elem()\n\tfbn := el.FieldByName(\"ID\")\n\tif !fbn.IsValid() {\n\t\treturn errors.New(\"model does not have a field named id\")\n\t}\n\n\tpkt, err := model.PrimaryKeyType()\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tswitch pkt {\n\tcase \"UUID\":\n\t\tvar id uuid.UUID\n\t\tif err := row.Scan(&id); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tfbn.Set(reflect.ValueOf(id))\n\tdefault:\n\t\tvar id interface{}\n\t\tif err := row.Scan(&id); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tv := reflect.ValueOf(id)\n\t\tswitch fbn.Kind() {\n\t\tcase reflect.Int, reflect.Int64:\n\t\t\tfbn.SetInt(v.Int())\n\t\tdefault:\n\t\t\tfbn.Set(reflect.ValueOf(id))\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "oryx/sqlxx/expand.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sqlxx\n\n// Expandable controls what fields to expand for projects.\ntype Expandable string\n\n// Expandables is a list of Expandable values.\ntype Expandables []Expandable\n\n// String returns a string representation of the Expandable.\nfunc (e Expandable) String() string {\n\treturn string(e)\n}\n\n// ToEager returns the fields used by pop's Eager command.\nfunc (e Expandables) ToEager() []string {\n\tvar s []string\n\tfor _, e := range e {\n\t\ts = append(s, e.String())\n\t}\n\treturn s\n}\n\n// Has returns true if the Expandable is in the list.\nfunc (e Expandables) Has(search Expandable) bool {\n\tfor _, e := range e {\n\t\tif e == search {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "oryx/sqlxx/sqlxx.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sqlxx\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n)\n\nfunc keys(t any, exclude []string) []string {\n\ttt := reflect.TypeOf(t)\n\tif tt.Kind() == reflect.Pointer {\n\t\ttt = tt.Elem()\n\t}\n\tks := make([]string, 0, tt.NumField())\n\tfor i := range tt.NumField() {\n\t\tf := tt.Field(i)\n\t\tkey, _, _ := strings.Cut(f.Tag.Get(\"db\"), \",\")\n\t\tif key != \"\" && key != \"-\" && !slices.Contains(exclude, key) {\n\t\t\tks = append(ks, key)\n\t\t}\n\t}\n\treturn ks\n}\n\n// NamedInsertArguments returns columns and arguments for SQL INSERT statements based on a struct's tags. Does\n// not work with nested structs or maps!\n//\n//\ttype st struct {\n//\t\tFoo string `db:\"foo\"`\n//\t\tBar string `db:\"bar,omitempty\"`\n//\t\tBaz string `db:\"-\"`\n//\t\tZab string\n//\t}\n//\tcolumns, arguments := NamedInsertArguments(new(st))\n//\tquery := fmt.Sprintf(\"INSERT INTO foo (%s) VALUES (%s)\", columns, arguments)\n//\t// INSERT INTO foo (foo, bar) VALUES (:foo, :bar)\nfunc NamedInsertArguments(t any, exclude ...string) (columns string, arguments string) {\n\tkeys := keys(t, exclude)\n\treturn strings.Join(keys, \", \"),\n\t\t\":\" + strings.Join(keys, \", :\")\n}\n\n// NamedUpdateArguments returns columns and arguments for SQL UPDATE statements based on a struct's tags. Does\n// not work with nested structs or maps!\n//\n//\ttype st struct {\n//\t\tFoo string `db:\"foo\"`\n//\t\tBar string `db:\"bar,omitempty\"`\n//\t\tBaz string `db:\"-\"`\n//\t\tZab string\n//\t}\n//\tquery := fmt.Sprintf(\"UPDATE foo SET %s\", NamedUpdateArguments(new(st)))\n//\t// UPDATE foo SET foo=:foo, bar=:bar\nfunc NamedUpdateArguments(t any, exclude ...string) string {\n\tkeys := keys(t, exclude)\n\tstatements := make([]string, len(keys))\n\n\tfor k, key := range keys {\n\t\tstatements[k] = fmt.Sprintf(\"%s=:%s\", key, key)\n\t}\n\n\treturn strings.Join(statements, \", \")\n}\n\nfunc OnConflictDoNothing(dialect string, columnNoop string) string {\n\tif dialect == \"mysql\" {\n\t\treturn fmt.Sprintf(\" ON DUPLICATE KEY UPDATE `%s` = `%s` \", columnNoop, columnNoop)\n\t} else {\n\t\treturn ` ON CONFLICT DO NOTHING `\n\t}\n}\n\n// ExtractSchemeFromDSN returns the scheme (e.g. `mysql`, `postgres`, etc) component in a DSN string,\n// as well as the remaining part of the DSN after the scheme separator.\n// It is an error to not have a scheme present.\n// This makes sense in the context of a DSN to be able to identify which database is in use.\nfunc ExtractSchemeFromDSN(dsn string) (string, string, error) {\n\tscheme, afterSchemeSeparator, schemeSeparatorFound := strings.Cut(dsn, \"://\")\n\tif !schemeSeparatorFound {\n\t\treturn \"\", \"\", errors.New(\"invalid DSN: missing scheme separator\")\n\t}\n\tif scheme == \"\" {\n\t\treturn \"\", \"\", errors.New(\"invalid DSN: empty scheme\")\n\t}\n\n\treturn scheme, afterSchemeSeparator, nil\n}\n\n// ExtractDbNameFromDSN returns the database name component in a DSN string.\nfunc ExtractDbNameFromDSN(dsn string) (string, error) {\n\t_, afterScheme, err := ExtractSchemeFromDSN(dsn)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t_, afterSlash, slashFound := strings.Cut(afterScheme, \"/\")\n\tif !slashFound {\n\t\treturn \"\", nil\n\t}\n\n\tdbName, _, _ := strings.Cut(afterSlash, \"?\")\n\treturn dbName, nil\n}\n\n// ReplaceSchemeInDSN replaces the scheme (e.g. `mysql`, `postgres`, etc) in a DSN string with another one.\n// This is necessary for example when using `cockroach` as a scheme, but using the postgres driver to connect to the database,\n// and this driver only accepts `postgres` as a scheme.\nfunc ReplaceSchemeInDSN(dsn string, newScheme string) (string, error) {\n\t_, afterSchemeSeparator, err := ExtractSchemeFromDSN(dsn)\n\tif err != nil {\n\t\treturn \"\", errors.WithStack(err)\n\t}\n\n\treturn newScheme + \"://\" + afterSchemeSeparator, nil\n}\n\n// DSNRedacted parses a database DSN and returns a redacted form as a string.\n// It replaces any password with \"xxxxx\" just like `url.Redacted()`.\n// Only the password is redacted, not the username.\n// This function is necessary because MySQL uses a DSN format not compatible with `url.Parse`.\n// Additionally and as a consequence of the point above, the scheme is expected to be present and non-empty.\n// This function is less strict that `url.Parse` in the case of MySQL.\n// It also does not escape any characters in the username, whereas `url.String()`/`url.Redacted` does.\nfunc DSNRedacted(dsn string) (string, error) {\n\tscheme, afterSchemeSeparator, err := ExtractSchemeFromDSN(dsn)\n\tif err != nil {\n\t\treturn \"\", errors.WithStack(err)\n\t}\n\n\t// If this is not MySQL, we simply delegate the work to `url.Parse`.\n\tif scheme != \"mysql\" {\n\t\tu, err := url.Parse(dsn)\n\t\tif err != nil {\n\t\t\treturn \"\", errors.WithStack(err)\n\t\t}\n\t\treturn u.Redacted(), nil\n\t}\n\n\t// MySQL has a weird DSN syntax not conforming to a standard URL, of the form:\n\t// `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]`\n\t// We only need to parse up to `@` in order to redact the password. The rest is left as-is.\n\n\tusernamePassword, afterUsernamePassword, usernamePasswordSeparatorFound := strings.Cut(afterSchemeSeparator, \"@\")\n\tif !usernamePasswordSeparatorFound {\n\t\tafterUsernamePassword = afterSchemeSeparator\n\t}\n\n\tusername, password, hasPassword := strings.Cut(usernamePassword, \":\")\n\t// We only insert a redacted password in the final result if a password was provided in the input.\n\t// This behavior matches the one of `url.Redacted()`.\n\tif hasPassword {\n\t\tpassword = \":xxxxx\"\n\t}\n\n\tres := scheme + \"://\"\n\tif usernamePasswordSeparatorFound {\n\t\tres += username + password + \"@\"\n\t}\n\tres += afterUsernamePassword\n\treturn res, nil\n}\n"
  },
  {
    "path": "oryx/sqlxx/types.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sqlxx\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Duration represents a JSON and SQL compatible time.Duration.\n// swagger:type string\ntype Duration time.Duration\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (ns Duration) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(time.Duration(ns).String())\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (ns *Duration) UnmarshalJSON(data []byte) error {\n\tvar s string\n\tif err := json.Unmarshal(data, &s); err != nil {\n\t\treturn err\n\t}\n\n\tp, err := time.ParseDuration(s)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*ns = Duration(p)\n\treturn nil\n}\n\n// StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.\ntype StringSliceJSONFormat []string\n\n// Scan implements the Scanner interface.\nfunc (m *StringSliceJSONFormat) Scan(value interface{}) error {\n\tvar val string\n\tswitch v := value.(type) {\n\tcase nil:\n\t\t*m = StringSliceJSONFormat{}\n\t\treturn nil\n\tcase string:\n\t\tval = v\n\tcase []byte:\n\t\tval = string(v)\n\tdefault:\n\t\treturn errors.Errorf(\"cannot scan %#v into StringSliceJSONFormat\", value)\n\t}\n\tif len(val) == 0 {\n\t\tval = \"[]\"\n\t}\n\n\tif parsed := gjson.Parse(val); parsed.Type == gjson.Null {\n\t\tval = \"[]\"\n\t} else if !parsed.IsArray() {\n\t\treturn errors.Errorf(\"expected JSON value to be an array but got type: %s\", parsed.Type.String())\n\t}\n\n\treturn errors.WithStack(json.Unmarshal([]byte(val), &m))\n}\n\n// Value implements the driver Valuer interface.\nfunc (m StringSliceJSONFormat) Value() (driver.Value, error) {\n\tif len(m) == 0 {\n\t\treturn \"[]\", nil\n\t}\n\n\tencoded, err := json.Marshal(&m)\n\treturn string(encoded), errors.WithStack(err)\n}\n\n// StringSlicePipeDelimiter de/encodes the string slice to/from a SQL string.\ntype StringSlicePipeDelimiter []string\n\n// Scan implements the Scanner interface.\nfunc (n *StringSlicePipeDelimiter) Scan(value interface{}) error {\n\tvar s sql.NullString\n\tif err := s.Scan(value); err != nil {\n\t\treturn err\n\t}\n\t*n = scanStringSlice('|', s.String)\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (n StringSlicePipeDelimiter) Value() (driver.Value, error) {\n\treturn valueStringSlice('|', n), nil\n}\n\nfunc scanStringSlice(delimiter rune, value interface{}) []string {\n\tescaped := false\n\ts := fmt.Sprintf(\"%s\", value)\n\tsplitted := strings.FieldsFunc(s, func(r rune) bool {\n\t\tif r == '\\\\' {\n\t\t\tescaped = !escaped\n\t\t} else if escaped && r != delimiter {\n\t\t\tescaped = false\n\t\t}\n\t\treturn !escaped && r == delimiter\n\t})\n\tfor k, v := range splitted {\n\t\tsplitted[k] = strings.ReplaceAll(v, \"\\\\\"+string(delimiter), string(delimiter))\n\t}\n\treturn splitted\n}\n\nfunc valueStringSlice(delimiter rune, value []string) string {\n\treplace := make([]string, len(value))\n\tfor k, v := range value {\n\t\treplace[k] = strings.ReplaceAll(v, string(delimiter), \"\\\\\"+string(delimiter))\n\t}\n\treturn strings.Join(replace, string(delimiter))\n}\n\n// NullBool represents a bool that may be null.\n// NullBool implements the Scanner interface so\n// swagger:type bool\n// swagger:model nullBool\ntype NullBool struct {\n\tBool  bool\n\tValid bool // Valid is true if Bool is not NULL\n}\n\n// Scan implements the Scanner interface.\nfunc (ns *NullBool) Scan(value interface{}) error {\n\td := sql.NullBool{}\n\tif err := d.Scan(value); err != nil {\n\t\treturn err\n\t}\n\n\tns.Bool = d.Bool\n\tns.Valid = d.Valid\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (ns NullBool) Value() (driver.Value, error) {\n\tif !ns.Valid {\n\t\treturn nil, nil\n\t}\n\treturn ns.Bool, nil\n}\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (ns NullBool) MarshalJSON() ([]byte, error) {\n\tif !ns.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn json.Marshal(ns.Bool)\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (ns *NullBool) UnmarshalJSON(data []byte) error {\n\tif ns == nil {\n\t\treturn errors.New(\"json.RawMessage: UnmarshalJSON on nil pointer\")\n\t}\n\tif len(data) == 0 || string(data) == \"null\" {\n\t\treturn nil\n\t}\n\tns.Valid = true\n\treturn errors.WithStack(json.Unmarshal(data, &ns.Bool))\n}\n\n// FalsyNullBool represents a bool that may be null.\n// It JSON decodes to false if null.\n//\n// swagger:type bool\n// swagger:model falsyNullBool\ntype FalsyNullBool struct {\n\tBool  bool\n\tValid bool // Valid is true if Bool is not NULL\n}\n\n// Scan implements the Scanner interface.\nfunc (ns *FalsyNullBool) Scan(value interface{}) error {\n\td := sql.NullBool{}\n\tif err := d.Scan(value); err != nil {\n\t\treturn err\n\t}\n\n\tns.Bool = d.Bool\n\tns.Valid = d.Valid\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (ns FalsyNullBool) Value() (driver.Value, error) {\n\tif !ns.Valid {\n\t\treturn nil, nil\n\t}\n\treturn ns.Bool, nil\n}\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (ns FalsyNullBool) MarshalJSON() ([]byte, error) {\n\tif !ns.Valid {\n\t\treturn []byte(\"false\"), nil\n\t}\n\treturn json.Marshal(ns.Bool)\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (ns *FalsyNullBool) UnmarshalJSON(data []byte) error {\n\tif ns == nil {\n\t\treturn errors.New(\"json.RawMessage: UnmarshalJSON on nil pointer\")\n\t}\n\tif len(data) == 0 || string(data) == \"null\" {\n\t\treturn nil\n\t}\n\tns.Valid = true\n\treturn errors.WithStack(json.Unmarshal(data, &ns.Bool))\n}\n\n// swagger:type string\n// swagger:model nullString\ntype NullString string\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (ns NullString) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(string(ns))\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (ns *NullString) UnmarshalJSON(data []byte) error {\n\tif ns == nil {\n\t\treturn errors.New(\"json.RawMessage: UnmarshalJSON on nil pointer\")\n\t}\n\tif len(data) == 0 {\n\t\treturn nil\n\t}\n\treturn errors.WithStack(json.Unmarshal(data, (*string)(ns)))\n}\n\n// Scan implements the Scanner interface.\nfunc (ns *NullString) Scan(value interface{}) error {\n\tvar v sql.NullString\n\tif err := (&v).Scan(value); err != nil {\n\t\treturn err\n\t}\n\t*ns = NullString(v.String)\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (ns NullString) Value() (driver.Value, error) {\n\tif len(ns) == 0 {\n\t\treturn sql.NullString{}.Value()\n\t}\n\treturn sql.NullString{Valid: true, String: string(ns)}.Value()\n}\n\n// String implements the Stringer interface.\nfunc (ns NullString) String() string {\n\treturn string(ns)\n}\n\n// NullTime implements sql.NullTime functionality.\n//\n// swagger:model nullTime\n// required: false\ntype NullTime time.Time\n\n// Scan implements the Scanner interface.\nfunc (ns *NullTime) Scan(value interface{}) error {\n\tvar v sql.NullTime\n\tif err := (&v).Scan(value); err != nil {\n\t\treturn err\n\t}\n\t*ns = NullTime(v.Time)\n\treturn nil\n}\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (ns NullTime) MarshalJSON() ([]byte, error) {\n\tvar t *time.Time\n\tif !time.Time(ns).IsZero() {\n\t\ttt := time.Time(ns)\n\t\tt = &tt\n\t}\n\treturn json.Marshal(t)\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (ns *NullTime) UnmarshalJSON(data []byte) error {\n\tvar t time.Time\n\tif err := json.Unmarshal(data, &t); err != nil {\n\t\treturn err\n\t}\n\t*ns = NullTime(t)\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (ns NullTime) Value() (driver.Value, error) {\n\treturn sql.NullTime{Valid: !time.Time(ns).IsZero(), Time: time.Time(ns)}.Value()\n}\n\n// MapStringInterface represents a map[string]interface that works well with JSON, SQL, and Swagger.\ntype MapStringInterface map[string]interface{}\n\n// Scan implements the Scanner interface.\nfunc (n *MapStringInterface) Scan(value interface{}) error {\n\tv := fmt.Sprintf(\"%s\", value)\n\tif len(v) == 0 {\n\t\treturn nil\n\t}\n\treturn errors.WithStack(json.Unmarshal([]byte(v), n))\n}\n\n// Value implements the driver Valuer interface.\nfunc (n MapStringInterface) Value() (driver.Value, error) {\n\tvalue, err := json.Marshal(n)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\treturn string(value), nil\n}\n\n// JSONArrayRawMessage represents a json.RawMessage which only accepts arrays that works well with JSON, SQL, and Swagger.\ntype JSONArrayRawMessage json.RawMessage\n\n// Scan implements the Scanner interface.\nfunc (m *JSONArrayRawMessage) Scan(value interface{}) error {\n\tval := fmt.Sprintf(\"%s\", value)\n\tif len(val) == 0 {\n\t\tval = \"[]\"\n\t}\n\n\tif parsed := gjson.Parse(val); parsed.Type == gjson.Null {\n\t\tval = \"[]\"\n\t} else if !parsed.IsArray() {\n\t\treturn errors.Errorf(\"expected JSON value to be an array but got type: %s\", parsed.Type.String())\n\t}\n\n\t*m = []byte(val)\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (m JSONArrayRawMessage) Value() (driver.Value, error) {\n\tif len(m) == 0 {\n\t\treturn \"[]\", nil\n\t}\n\n\tif parsed := gjson.ParseBytes(m); parsed.Type == gjson.Null {\n\t\treturn \"[]\", nil\n\t} else if !parsed.IsArray() {\n\t\treturn nil, errors.Errorf(\"expected JSON value to be an array but got type: %s\", parsed.Type.String())\n\t}\n\n\treturn string(m), nil\n}\n\n// JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger.\ntype JSONRawMessage json.RawMessage\n\n// Scan implements the Scanner interface.\nfunc (m *JSONRawMessage) Scan(value interface{}) error {\n\tswitch v := value.(type) {\n\tcase []byte:\n\t\t*m = slices.Clone(v)\n\tcase string:\n\t\t*m = JSONRawMessage(v)\n\tcase nil:\n\t\t*m = JSONRawMessage(\"null\")\n\tdefault:\n\t\treturn errors.Errorf(\"cannot scan %T into JSONRawMessage\", value)\n\t}\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (m JSONRawMessage) Value() (driver.Value, error) {\n\tif len(m) == 0 {\n\t\treturn \"null\", nil\n\t}\n\treturn string(m), nil\n}\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (m JSONRawMessage) MarshalJSON() ([]byte, error) {\n\tif len(m) == 0 {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn m, nil\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (m *JSONRawMessage) UnmarshalJSON(data []byte) error {\n\tif m == nil {\n\t\treturn errors.New(\"json.RawMessage: UnmarshalJSON on nil pointer\")\n\t}\n\t*m = append((*m)[0:0], data...)\n\treturn nil\n}\n\n// NullJSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger and is NULLable-\n//\n// swagger:model nullJsonRawMessage\ntype NullJSONRawMessage json.RawMessage\n\n// Scan implements the Scanner interface.\nfunc (m *NullJSONRawMessage) Scan(value any) error {\n\treturn (*JSONRawMessage)(m).Scan(value)\n}\n\n// Value implements the driver Valuer interface.\nfunc (m NullJSONRawMessage) Value() (driver.Value, error) {\n\tif len(m) == 0 || string(m) == \"null\" {\n\t\treturn nil, nil\n\t}\n\treturn string(m), nil\n}\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (m NullJSONRawMessage) MarshalJSON() ([]byte, error) {\n\tif len(m) == 0 {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn m, nil\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (m *NullJSONRawMessage) UnmarshalJSON(data []byte) error {\n\tif m == nil {\n\t\treturn errors.New(\"json.RawMessage: UnmarshalJSON on nil pointer\")\n\t}\n\t*m = append((*m)[0:0], data...)\n\treturn nil\n}\n\n// JSONScan is a generic helper for retrieving a SQL JSON-encoded value.\nfunc JSONScan(dst, value any) error {\n\t// Note: raw is a string (not []byte) because the MySQL driver reuses byte slices across scans.\n\t// Using strings avoids the need to manually copy the byte slice.\n\tvar raw string\n\tswitch v := value.(type) {\n\tcase nil:\n\t\traw = \"null\"\n\tcase string:\n\t\traw = v\n\tcase []byte:\n\t\traw = string(v)\n\tdefault:\n\t\treturn fmt.Errorf(\"unable to scan type %T as JSON into %T\", value, dst)\n\t}\n\tif err := json.Unmarshal([]byte(raw), dst); err != nil {\n\t\treturn fmt.Errorf(\"unable to decode JSON payload into %T: %w\", dst, err)\n\t}\n\treturn nil\n}\n\n// NullInt64 represents an int64 that may be null.\n// swagger:model nullInt64\ntype NullInt64 struct {\n\tInt   int64\n\tValid bool // Valid is true if Duration is not NULL\n}\n\n// Scan implements the Scanner interface.\nfunc (ns *NullInt64) Scan(value interface{}) error {\n\td := sql.NullInt64{}\n\tif err := d.Scan(value); err != nil {\n\t\treturn err\n\t}\n\n\tns.Int = d.Int64\n\tns.Valid = d.Valid\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (ns NullInt64) Value() (driver.Value, error) {\n\tif !ns.Valid {\n\t\treturn nil, nil\n\t}\n\treturn ns.Int, nil\n}\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (ns NullInt64) MarshalJSON() ([]byte, error) {\n\tif !ns.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn json.Marshal(ns.Int)\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (ns *NullInt64) UnmarshalJSON(data []byte) error {\n\tif ns == nil {\n\t\treturn errors.New(\"json.RawMessage: UnmarshalJSON on nil pointer\")\n\t}\n\tif len(data) == 0 || string(data) == \"null\" {\n\t\treturn nil\n\t}\n\tns.Valid = true\n\treturn errors.WithStack(json.Unmarshal(data, &ns.Int))\n}\n\n// NullDuration represents a nullable JSON and SQL compatible time.Duration.\n//\n// swagger:type string\n// swagger:model nullDuration\ntype NullDuration struct {\n\tDuration time.Duration\n\tValid    bool\n}\n\n// Scan implements the Scanner interface.\nfunc (ns *NullDuration) Scan(value interface{}) error {\n\td := sql.NullInt64{}\n\tif err := d.Scan(value); err != nil {\n\t\treturn err\n\t}\n\n\tns.Duration = time.Duration(d.Int64)\n\tns.Valid = d.Valid\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (ns NullDuration) Value() (driver.Value, error) {\n\tif !ns.Valid {\n\t\treturn nil, nil\n\t}\n\treturn int64(ns.Duration), nil\n}\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (ns NullDuration) MarshalJSON() ([]byte, error) {\n\tif !ns.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\treturn json.Marshal(ns.Duration.String())\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (ns *NullDuration) UnmarshalJSON(data []byte) error {\n\tif ns == nil {\n\t\treturn errors.New(\"json.RawMessage: UnmarshalJSON on nil pointer\")\n\t}\n\n\tif len(data) == 0 || string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar s string\n\tif err := json.Unmarshal(data, &s); err != nil {\n\t\treturn err\n\t}\n\n\tp, err := time.ParseDuration(s)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tns.Duration = p\n\tns.Valid = true\n\treturn nil\n}\n\nfunc (ns Duration) IsZero() bool                { return ns == 0 }\nfunc (m StringSliceJSONFormat) IsZero() bool    { return len(m) == 0 }\nfunc (n StringSlicePipeDelimiter) IsZero() bool { return len(n) == 0 }\nfunc (ns NullBool) IsZero() bool                { return !ns.Valid || !ns.Bool }\nfunc (ns FalsyNullBool) IsZero() bool           { return !ns.Valid || !ns.Bool }\nfunc (ns NullString) IsZero() bool              { return len(ns) == 0 }\nfunc (ns NullTime) IsZero() bool                { return time.Time(ns).IsZero() }\nfunc (n MapStringInterface) IsZero() bool       { return len(n) == 0 }\nfunc (m JSONArrayRawMessage) IsZero() bool      { return len(m) == 0 || string(m) == \"[]\" }\nfunc (m JSONRawMessage) IsZero() bool           { return len(m) == 0 || string(m) == \"null\" }\nfunc (m NullJSONRawMessage) IsZero() bool       { return len(m) == 0 || string(m) == \"null\" }\nfunc (ns NullInt64) IsZero() bool               { return !ns.Valid || ns.Int == 0 }\nfunc (ns NullDuration) IsZero() bool            { return !ns.Valid || ns.Duration == 0 }\n"
  },
  {
    "path": "oryx/stringslice/unique.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage stringslice\n\n// Unique returns the given string slice with unique values, preserving order.\n// Consider using slices.Compact with slices.Sort instead when you don't care about order.\nfunc Unique(i []string) []string {\n\tu := make([]string, 0, len(i))\n\tm := make(map[string]struct{}, len(i))\n\n\tfor _, val := range i {\n\t\tif _, ok := m[val]; !ok {\n\t\t\tm[val] = struct{}{}\n\t\t\tu = append(u, val)\n\t\t}\n\t}\n\n\treturn u\n}\n"
  },
  {
    "path": "oryx/stringsx/case.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage stringsx\n\nimport \"unicode\"\n\n// ToLowerInitial converts a string's first character to lower case.\nfunc ToLowerInitial(s string) string {\n\tif s == \"\" {\n\t\treturn \"\"\n\t}\n\ta := []rune(s)\n\ta[0] = unicode.ToLower(a[0])\n\treturn string(a)\n}\n\n// ToUpperInitial converts a string's first character to upper case.\nfunc ToUpperInitial(s string) string {\n\tif s == \"\" {\n\t\treturn \"\"\n\t}\n\ta := []rune(s)\n\ta[0] = unicode.ToUpper(a[0])\n\treturn string(a)\n}\n"
  },
  {
    "path": "oryx/stringsx/split.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage stringsx\n\nimport \"strings\"\n\n// Splitx is a special case of strings.Split\n// which returns an empty slice if the string is empty\nfunc Splitx(s, sep string) []string {\n\tif s == \"\" {\n\t\treturn []string{}\n\t}\n\n\treturn strings.Split(s, sep)\n}\n"
  },
  {
    "path": "oryx/stringsx/switch_case.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage stringsx\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n)\n\ntype (\n\tRegisteredCases struct {\n\t\tcases  []string\n\t\tactual string\n\t}\n\terrUnknownCase struct {\n\t\t*RegisteredCases\n\t}\n\tRegisteredPrefixes struct {\n\t\tprefixes []string\n\t\tactual   string\n\t}\n\terrUnknownPrefix struct {\n\t\t*RegisteredPrefixes\n\t}\n)\n\nvar (\n\tErrUnknownCase   = errUnknownCase{}\n\tErrUnknownPrefix = errUnknownPrefix{}\n)\n\nfunc SwitchExact(actual string) *RegisteredCases {\n\treturn &RegisteredCases{\n\t\tactual: actual,\n\t}\n}\n\nfunc SwitchPrefix(actual string) *RegisteredPrefixes {\n\treturn &RegisteredPrefixes{\n\t\tactual: actual,\n\t}\n}\n\nfunc (r *RegisteredCases) AddCase(cases ...string) bool {\n\tr.cases = append(r.cases, cases...)\n\treturn slices.Contains(cases, r.actual)\n}\n\nfunc (r *RegisteredPrefixes) HasPrefix(prefixes ...string) bool {\n\tr.prefixes = append(r.prefixes, prefixes...)\n\treturn slices.ContainsFunc(prefixes, func(s string) bool {\n\t\treturn strings.HasPrefix(r.actual, s)\n\t})\n}\n\nfunc (r *RegisteredCases) String() string {\n\treturn \"[\" + strings.Join(r.cases, \", \") + \"]\"\n}\n\nfunc (r *RegisteredPrefixes) String() string {\n\treturn \"[\" + strings.Join(r.prefixes, \", \") + \"]\"\n}\n\nfunc (r *RegisteredCases) ToUnknownCaseErr() error {\n\treturn errUnknownCase{r}\n}\n\nfunc (r *RegisteredPrefixes) ToUnknownPrefixErr() error {\n\treturn errUnknownPrefix{r}\n}\n\nfunc (e errUnknownCase) Error() string {\n\treturn fmt.Sprintf(\"expected one of %s but got %s\", e.String(), e.actual)\n}\n\nfunc (e errUnknownCase) Is(err error) bool {\n\t_, ok := err.(errUnknownCase)\n\treturn ok\n}\n\nfunc (e errUnknownPrefix) Error() string {\n\treturn fmt.Sprintf(\"expected %s to have one of the prefixes %s\", e.actual, e.String())\n}\n\nfunc (e errUnknownPrefix) Is(err error) bool {\n\t_, ok := err.(errUnknownPrefix)\n\treturn ok\n}\n"
  },
  {
    "path": "oryx/stringsx/truncate.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage stringsx\n\nimport \"unicode/utf8\"\n\n// TruncateByteLen returns string truncated at the end with the length specified\nfunc TruncateByteLen(s string, length int) string {\n\tif length <= 0 || len(s) <= length {\n\t\treturn s\n\t}\n\n\tres := s[:length]\n\n\t// in case we cut in the middle of an utf8 rune, we have to remove the last byte as well until it fits\n\tfor !utf8.ValidString(res) {\n\t\tres = res[:len(res)-1]\n\t}\n\treturn res\n}\n"
  },
  {
    "path": "oryx/swaggerx/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage swaggerx\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/go-openapi/runtime\"\n)\n\nfunc FormatSwaggerError(err error) string {\n\tvar e *runtime.APIError\n\tif errors.As(err, &e) {\n\t\tbody, err := json.MarshalIndent(e, \"\\t\", \"  \")\n\t\tif err != nil {\n\t\t\tbody = []byte(fmt.Sprintf(\"%+v\", e.Response))\n\t\t}\n\n\t\tswitch e.Code {\n\t\tcase http.StatusForbidden:\n\t\t\treturn fmt.Sprintf(\"The service responded with status code 403 indicating that you lack permission to access the resource. The full error details are:\\n\\n\\t%s\\n\\n\", body)\n\t\tcase http.StatusUnauthorized:\n\t\t\treturn fmt.Sprintf(\"The service responded with status code 401 indicating that you forgot to include credentials (e.g. token, TLS certificate, ...) in the HTTP request.  The full error details are:\\n\\n\\t%s\\n\\n\", body)\n\t\tcase http.StatusNotFound:\n\t\t\treturn fmt.Sprintf(\"The service responded with status code 404 indicating that the resource does not exist. Check that the URL is correct (are you using the correct admin/public/... endpoint?) and that the resource exists. The full error details are:\\n\\n\\t%s\\n\\n\", body)\n\t\tdefault:\n\t\t\treturn fmt.Sprintf(\"Unable to complete operation %s because the server responded with status code %d:\\n\\n\\t%s\\n\", e.OperationName, e.Code, body)\n\t\t}\n\t}\n\treturn fmt.Sprintf(\"%+v\", err)\n}\n"
  },
  {
    "path": "oryx/testingx/helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// Package testingx contains helper functions and extensions used when writing tests in Ory.\npackage testingx\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\n// RepoRootPath returns the absolute path of the closest parent directory that has a go.mod file relative to the caller.\nfunc RepoRootPath(t require.TestingT) (repoRoot string) {\n\t_, fpath, _, _ := runtime.Caller(1)\n\tfor dir := filepath.Dir(filepath.FromSlash(fpath)); dir != filepath.Dir(dir); dir = filepath.Dir(dir) {\n\t\tmodPath := filepath.Join(dir, \"go.mod\")\n\t\tif _, err := os.Stat(modPath); err == nil {\n\t\t\trepoRoot = dir\n\t\t\tbreak\n\t\t}\n\t}\n\trequire.NotEmptyf(t, repoRoot, \"could not determine repo root using path: %q\", fpath)\n\treturn repoRoot\n}\n"
  },
  {
    "path": "oryx/tlsx/cert.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage tlsx\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/ed25519\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/base64\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/big\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/watcherx\"\n)\n\n// ErrNoCertificatesConfigured is returned when no TLS configuration was found.\nvar ErrNoCertificatesConfigured = errors.New(\"no tls configuration was found\")\n\n// ErrInvalidCertificateConfiguration is returned when an invalid TLS configuration was found.\nvar ErrInvalidCertificateConfiguration = errors.New(\"tls configuration is invalid\")\n\n// HTTPSCertificate returns loads a HTTP over TLS Certificate by looking at environment variables.\nfunc HTTPSCertificate() ([]tls.Certificate, error) {\n\tprefix := \"HTTPS_TLS\"\n\treturn Certificate(\n\t\tos.Getenv(prefix+\"_CERT\"), os.Getenv(prefix+\"_KEY\"),\n\t\tos.Getenv(prefix+\"_CERT_PATH\"), os.Getenv(prefix+\"_KEY_PATH\"),\n\t)\n}\n\n// HTTPSCertificateHelpMessage returns a help message for configuring HTTP over TLS Certificates.\nfunc HTTPSCertificateHelpMessage() string {\n\treturn CertificateHelpMessage(\"HTTPS_TLS\")\n}\n\n// CertificateHelpMessage returns a help message for configuring TLS Certificates.\nfunc CertificateHelpMessage(prefix string) string {\n\treturn `- ` + prefix + `_CERT_PATH: The path to the TLS certificate (pem encoded).\n\tExample: ` + prefix + `_CERT_PATH=~/cert.pem\n\n- ` + prefix + `_KEY_PATH: The path to the TLS private key (pem encoded).\n\tExample: ` + prefix + `_KEY_PATH=~/key.pem\n\n- ` + prefix + `_CERT: Base64 encoded (without padding) string of the TLS certificate (PEM encoded) to be used for HTTP over TLS (HTTPS).\n\tExample: ` + prefix + `_CERT=\"-----BEGIN CERTIFICATE-----\\nMIIDZTCCAk2gAwIBAgIEV5xOtDANBgkqhkiG9w0BAQ0FADA0MTIwMAYDVQQDDClP...\"\n\n- ` + prefix + `_KEY: Base64 encoded (without padding) string of the private key (PEM encoded) to be used for HTTP over TLS (HTTPS).\n\tExample: ` + prefix + `_KEY=\"-----BEGIN ENCRYPTED PRIVATE KEY-----\\nMIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDg...\"\n`\n}\n\n// CertificateFromBase64 loads a TLS certificate from a base64-encoded string of\n// the PEM representations of the cert and key.\nfunc CertificateFromBase64(certBase64, keyBase64 string) (tls.Certificate, error) {\n\tcertPEM, err := base64.StdEncoding.DecodeString(certBase64)\n\tif err != nil {\n\t\treturn tls.Certificate{}, fmt.Errorf(\"unable to base64 decode the TLS certificate: %v\", err)\n\t}\n\tkeyPEM, err := base64.StdEncoding.DecodeString(keyBase64)\n\tif err != nil {\n\t\treturn tls.Certificate{}, fmt.Errorf(\"unable to base64 decode the TLS private key: %v\", err)\n\t}\n\tcert, err := tls.X509KeyPair(certPEM, keyPEM)\n\tif err != nil {\n\t\treturn tls.Certificate{}, fmt.Errorf(\"unable to load X509 key pair: %v\", err)\n\t}\n\treturn cert, nil\n}\n\n// [deprecated] Certificate returns a TLS Certificate by looking at its\n// arguments. If both certPEMBase64 and keyPEMBase64 are not empty and contain\n// base64-encoded PEM representations of a cert and key, respectively, that key\n// pair is returned. Otherwise, if certPath and keyPath point to PEM files, the\n// key pair is loaded from those. Returns ErrNoCertificatesConfigured if all\n// arguments are empty, and ErrInvalidCertificateConfiguration if the arguments\n// are inconsistent.\n//\n// This function is deprecated. Use CertificateFromBase64 or GetCertificate\n// instead.\nfunc Certificate(\n\tcertPEMBase64, keyPEMBase64 string,\n\tcertPath, keyPath string,\n) ([]tls.Certificate, error) {\n\tif certPEMBase64 == \"\" && keyPEMBase64 == \"\" && certPath == \"\" && keyPath == \"\" {\n\t\treturn nil, errors.WithStack(ErrNoCertificatesConfigured)\n\t}\n\n\tif certPEMBase64 != \"\" && keyPEMBase64 != \"\" {\n\t\tcert, err := CertificateFromBase64(certPEMBase64, keyPEMBase64)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\treturn []tls.Certificate{cert}, nil\n\t}\n\n\tif certPath != \"\" && keyPath != \"\" {\n\t\tcert, err := tls.LoadX509KeyPair(certPath, keyPath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to load X509 key pair from files: %v\", err)\n\t\t}\n\t\treturn []tls.Certificate{cert}, nil\n\t}\n\n\treturn nil, errors.WithStack(ErrInvalidCertificateConfiguration)\n}\n\ntype CertFunc = func(*tls.ClientHelloInfo) (*tls.Certificate, error)\n\n// GetCertificate returns a function for use with\n// \"net/tls\".Config.GetCertificate.\n//\n// The certificate and private key are read from the specified filesystem paths.\n// The certificate file is watched for changes, upon which the cert+key are\n// reloaded in the background. Errors during reloading are deduplicated and\n// reported through the errs channel if it is not nil. When the provided context\n// is canceled, background reloading stops and the errs channel is closed.\n//\n// The returned function always yields the latest successfully loaded\n// certificate; ClientHelloInfo is unused.\nfunc GetCertificate(\n\tctx context.Context,\n\tcertPath, keyPath string,\n\terrs chan<- error,\n) (CertFunc, error) {\n\tif certPath == \"\" || keyPath == \"\" {\n\t\treturn nil, errors.WithStack(ErrNoCertificatesConfigured)\n\t}\n\tcert, err := tls.LoadX509KeyPair(certPath, keyPath)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(fmt.Errorf(\"unable to load X509 key pair from files: %v\", err))\n\t}\n\tvar store atomic.Value\n\tstore.Store(&cert)\n\n\tevents := make(chan watcherx.Event)\n\t// The cert could change without the key changing, but not the other way around.\n\t// Hence, we only watch the cert.\n\t_, err = watcherx.WatchFile(ctx, certPath, events)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tgo func() {\n\t\tif errs != nil {\n\t\t\tdefer close(errs)\n\t\t}\n\t\tvar lastReportedError string\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn\n\n\t\t\tcase event := <-events:\n\t\t\t\tvar err error\n\t\t\t\tswitch event := event.(type) {\n\t\t\t\tcase *watcherx.ChangeEvent:\n\t\t\t\t\tvar cert tls.Certificate\n\t\t\t\t\tcert, err = tls.LoadX509KeyPair(certPath, keyPath)\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tstore.Store(&cert)\n\t\t\t\t\t\tlastReportedError = \"\"\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\terr = fmt.Errorf(\"unable to load X509 key pair from files: %v\", err)\n\n\t\t\t\tcase *watcherx.ErrorEvent:\n\t\t\t\t\terr = fmt.Errorf(\"file watch: %v\", event)\n\t\t\t\tdefault:\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif err.Error() == lastReportedError { // same message as before: don't spam the error channel\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// fresh error\n\t\t\t\tselect {\n\t\t\t\tcase errs <- errors.WithStack(err):\n\t\t\t\t\tlastReportedError = err.Error()\n\t\t\t\tcase <-time.After(500 * time.Millisecond):\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\tif cert, ok := store.Load().(*tls.Certificate); ok {\n\t\t\treturn cert, nil\n\t\t}\n\t\treturn nil, errors.WithStack(ErrNoCertificatesConfigured)\n\t}, nil\n}\n\n// PublicKey returns the public key for a given private key, or nil.\nfunc PublicKey(key crypto.PrivateKey) interface{ Equal(x crypto.PublicKey) bool } {\n\tswitch k := key.(type) {\n\tcase *rsa.PrivateKey:\n\t\treturn &k.PublicKey\n\tcase *ecdsa.PrivateKey:\n\t\treturn &k.PublicKey\n\tcase ed25519.PrivateKey:\n\t\treturn k.Public().(ed25519.PublicKey)\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// CreateSelfSignedTLSCertificate creates a self-signed TLS certificate.\nfunc CreateSelfSignedTLSCertificate(key interface{}, opts ...CertificateOpts) (*tls.Certificate, error) {\n\tc, err := CreateSelfSignedCertificate(key, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tblock, err := PEMBlockForKey(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpemCert := pem.EncodeToMemory(&pem.Block{Type: \"CERTIFICATE\", Bytes: c.Raw})\n\tpemKey := pem.EncodeToMemory(block)\n\tcert, err := tls.X509KeyPair(pemCert, pemKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &cert, nil\n}\n\n// CreateSelfSignedCertificate creates a self-signed x509 certificate.\nfunc CreateSelfSignedCertificate(key interface{}, opts ...CertificateOpts) (cert *x509.Certificate, err error) {\n\tserialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)\n\tserialNumber, err := rand.Int(rand.Reader, serialNumberLimit)\n\tif err != nil {\n\t\treturn cert, errors.Errorf(\"failed to generate serial number: %s\", err)\n\t}\n\n\tcertificate := &x509.Certificate{\n\t\tSerialNumber: serialNumber,\n\t\tSubject: pkix.Name{\n\t\t\tOrganization: []string{\"ORY GmbH\"},\n\t\t\tCommonName:   \"ORY\",\n\t\t},\n\t\tIssuer: pkix.Name{\n\t\t\tOrganization: []string{\"ORY GmbH\"},\n\t\t\tCommonName:   \"ORY\",\n\t\t},\n\t\tNotBefore:             time.Now().UTC(),\n\t\tNotAfter:              time.Now().UTC().Add(time.Hour * 24 * 31),\n\t\tKeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,\n\t\tExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},\n\t\tBasicConstraintsValid: true,\n\t\tIsCA:                  true,\n\t\tDNSNames:              []string{\"localhost\"},\n\t}\n\tfor _, opt := range opts {\n\t\topt(certificate)\n\t}\n\n\tder, err := x509.CreateCertificate(rand.Reader, certificate, certificate, PublicKey(key), key)\n\tif err != nil {\n\t\treturn cert, errors.Errorf(\"failed to create certificate: %s\", err)\n\t}\n\n\tcert, err = x509.ParseCertificate(der)\n\tif err != nil {\n\t\treturn cert, errors.Errorf(\"failed to encode private key: %s\", err)\n\t}\n\treturn cert, nil\n}\n\n// PEMBlockForKey returns a PEM-encoded block for key.\nfunc PEMBlockForKey(key interface{}) (*pem.Block, error) {\n\tb, err := x509.MarshalPKCS8PrivateKey(key)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\treturn &pem.Block{Type: \"PRIVATE KEY\", Bytes: b}, nil\n}\n\n// NewClientCert creates a new client TLS certificate signed by the given CA.\nfunc NewClientCert(CAcert *x509.Certificate, CAkey crypto.PrivateKey, opts ...CertificateOpts) (*tls.Certificate, error) {\n\tif !slices.Contains(CAcert.ExtKeyUsage, x509.ExtKeyUsageClientAuth) {\n\t\treturn nil, errors.Errorf(\"the CA certificate does not have the client authentication extended key usage (OID 1.3.6.1.5.5.7.3.2) set\")\n\t}\n\tserialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)\n\tserialNumber, err := rand.Int(rand.Reader, serialNumberLimit)\n\tif err != nil {\n\t\treturn nil, errors.Errorf(\"failed to generate serial number: %s\", err)\n\t}\n\n\tkey, err := rsa.GenerateKey(rand.Reader, 3072)\n\tif err != nil {\n\t\treturn nil, errors.Errorf(\"failed to generate private key: %s\", err)\n\t}\n\n\ttemplate := &x509.Certificate{\n\t\tSerialNumber: serialNumber,\n\t\tSubject: pkix.Name{\n\t\t\tOrganization: []string{\"Ory GmbH\"},\n\t\t\tCommonName:   \"ORY\",\n\t\t},\n\t\tIssuer:                CAcert.Subject,\n\t\tNotBefore:             time.Now().UTC(),\n\t\tNotAfter:              CAcert.NotAfter,\n\t\tKeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,\n\t\tExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},\n\t\tBasicConstraintsValid: true,\n\t\tIsCA:                  false,\n\t}\n\tfor _, opt := range opts {\n\t\topt(template)\n\t}\n\n\tder, err := x509.CreateCertificate(rand.Reader, template, CAcert, PublicKey(key), CAkey)\n\tif err != nil {\n\t\treturn nil, errors.Errorf(\"failed to create certificate: %s\", err)\n\t}\n\n\tpemCert := pem.EncodeToMemory(&pem.Block{Type: \"CERTIFICATE\", Bytes: der})\n\tpemBlock, err := PEMBlockForKey(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpemKey := pem.EncodeToMemory(pemBlock)\n\n\tcert, err := tls.X509KeyPair(pemCert, pemKey)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\treturn &cert, nil\n}\n\ntype CertificateOpts func(*x509.Certificate)\n\n// CreateSelfSignedCertificateForTest writes a new, self-signed TLS\n// certificate+key (in PEM format) to a temporary location on disk and returns\n// the paths to both, and the respective contents in base64 encoding. The\n// files are automatically cleaned up when the given *testing.T concludes its\n// tests.\nfunc CreateSelfSignedCertificateForTest(t testing.TB) (certPath, keyPath, certBase64, keyBase64 string) {\n\ttmpDir := t.TempDir()\n\n\tprivateKey, err := rsa.GenerateKey(rand.Reader, 2048)\n\trequire.NoError(t, err)\n\n\tcert, err := CreateSelfSignedCertificate(privateKey)\n\trequire.NoError(t, err)\n\n\t// write cert\n\tcertFile, err := os.Create(filepath.Join(tmpDir, \"cert.pem\"))\n\trequire.NoError(t, err)\n\tcertPath = certFile.Name()\n\n\tvar buf bytes.Buffer\n\tenc := base64.NewEncoder(base64.StdEncoding, &buf)\n\trequire.NoErrorf(t, pem.Encode(\n\t\tio.MultiWriter(enc, certFile),\n\t\t&pem.Block{Type: \"CERTIFICATE\", Bytes: cert.Raw},\n\t), \"Failed to write data to %q\", certPath)\n\trequire.NoError(t, enc.Close())\n\trequire.NoErrorf(t, certFile.Close(), \"Error closing %q\", certPath)\n\tcertBase64 = buf.String()\n\n\t// write key\n\tkeyFile, err := os.Create(filepath.Join(tmpDir, \"key.pem\"))\n\trequire.NoError(t, err)\n\tkeyPath = keyFile.Name()\n\tbuf.Reset()\n\tenc = base64.NewEncoder(base64.StdEncoding, &buf)\n\n\tprivBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)\n\trequire.NoError(t, err)\n\n\trequire.NoErrorf(t, pem.Encode(\n\t\tio.MultiWriter(enc, keyFile),\n\t\t&pem.Block{Type: \"PRIVATE KEY\", Bytes: privBytes},\n\t), \"Failed to write data to %q\", keyPath)\n\trequire.NoError(t, enc.Close())\n\trequire.NoErrorf(t, keyFile.Close(), \"Error closing %q\", keyPath)\n\tkeyBase64 = buf.String()\n\n\treturn\n}\n"
  },
  {
    "path": "oryx/tlsx/termination.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage tlsx\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/urfave/negroni\"\n\n\t\"github.com/ory/x/healthx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/prometheusx\"\n)\n\ntype dependencies interface {\n\tlogrusx.Provider\n\thttpx.WriterProvider\n}\n\n// EnforceTLSRequests creates a middleware that enforces TLS for incoming HTTP requests.\n// It allows termination (non-HTTPS traffic) from specific CIDR ranges provided in the `allowTerminationFrom` slice.\n// If the request is not secure and does not match the allowed CIDR ranges, an error response is returned.\n// The middleware also validates the `X-Forwarded-Proto` header to ensure it is set to \"https\".\nfunc EnforceTLSRequests(d dependencies, allowTerminationFrom []string) (negroni.Handler, error) {\n\tnetworks := make([]*net.IPNet, 0, len(allowTerminationFrom))\n\tfor _, rn := range allowTerminationFrom {\n\t\t_, network, err := net.ParseCIDR(rn)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\tnetworks = append(networks, network)\n\t}\n\n\treturn negroni.HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\tif r.TLS != nil ||\n\t\t\tr.URL.Path == healthx.AliveCheckPath ||\n\t\t\tr.URL.Path == healthx.ReadyCheckPath ||\n\t\t\tr.URL.Path == prometheusx.MetricsPrometheusPath {\n\t\t\tnext(rw, r)\n\t\t\treturn\n\t\t}\n\n\t\tif len(networks) == 0 {\n\t\t\td.Logger().WithRequest(r).WithError(errors.New(\"TLS termination is not enabled\")).Error(\"Could not serve http connection\")\n\t\t\td.Writer().WriteErrorCode(rw, r, http.StatusBadGateway, errors.New(\"can not serve request over insecure http\"))\n\t\t\treturn\n\t\t}\n\n\t\tif err := matchesRange(r, networks); err != nil {\n\t\t\td.Logger().WithRequest(r).WithError(err).Warnln(\"Could not serve http connection\")\n\t\t\td.Writer().WriteErrorCode(rw, r, http.StatusBadGateway, errors.New(\"can not serve request over insecure http\"))\n\t\t\treturn\n\t\t}\n\n\t\tproto := r.Header.Get(\"X-Forwarded-Proto\")\n\t\tif proto == \"\" {\n\t\t\td.Logger().WithRequest(r).WithError(errors.New(\"X-Forwarded-Proto header is missing\")).Error(\"Could not serve http connection\")\n\t\t\td.Writer().WriteErrorCode(rw, r, http.StatusBadGateway, errors.New(\"can not serve request over insecure http\"))\n\t\t\treturn\n\t\t} else if proto != \"https\" {\n\t\t\td.Logger().WithRequest(r).WithError(errors.New(\"X-Forwarded-Proto header is missing\")).Error(\"Could not serve http connection\")\n\t\t\td.Writer().WriteErrorCode(rw, r, http.StatusBadGateway, errors.Errorf(\"expected X-Forwarded-Proto header to be https but got: %s\", proto))\n\t\t\treturn\n\t\t}\n\n\t\tnext(rw, r)\n\t}), nil\n}\n\nfunc matchesRange(r *http.Request, networks []*net.IPNet) error {\n\tremoteIP, _, err := net.SplitHostPort(r.RemoteAddr)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tcheck := []string{remoteIP}\n\tfor fwd := range strings.SplitSeq(r.Header.Get(\"X-Forwarded-For\"), \",\") {\n\t\tcheck = append(check, strings.TrimSpace(fwd))\n\t}\n\n\tfor _, ipNet := range networks {\n\t\tfor _, ip := range check {\n\t\t\taddr := net.ParseIP(ip)\n\t\t\tif ipNet.Contains(addr) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\treturn errors.Errorf(\"neither remote address nor any x-forwarded-for values match CIDR ranges %+v: %v, ranges, check)\", networks, check)\n}\n"
  },
  {
    "path": "oryx/urlx/copy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage urlx\n\nimport \"net/url\"\n\n// Copy returns a copy of the input url.\nfunc Copy(src *url.URL) *url.URL {\n\tvar out = new(url.URL)\n\t*out = *src\n\treturn out\n}\n\n// CopyWithQuery returns a copy of the input url with the given query parameters\nfunc CopyWithQuery(src *url.URL, query url.Values) *url.URL {\n\tout := Copy(src)\n\tq := out.Query()\n\tfor k := range query {\n\t\tq.Set(k, query.Get(k))\n\t}\n\tout.RawQuery = q.Encode()\n\treturn out\n}\n"
  },
  {
    "path": "oryx/urlx/extract.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage urlx\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// hostCache caches DNS lookup results for hostnames to avoid repeated lookups.\n// The cache is thread-safe and stores true/false whether a hostname resolves to public IPs.\ntype hostCache struct {\n\tmu    sync.RWMutex\n\tcache map[string]bool\n}\n\n// get retrieves a cached value for a hostname. Returns value and whether it was found.\nfunc (hc *hostCache) get(hostname string) (bool, bool) {\n\thc.mu.RLock()\n\tdefer hc.mu.RUnlock()\n\tisPublic, found := hc.cache[hostname]\n\treturn isPublic, found\n}\n\n// set stores the lookup result for a hostname.\nfunc (hc *hostCache) set(hostname string, isPublic bool) {\n\thc.mu.Lock()\n\tdefer hc.mu.Unlock()\n\thc.cache[hostname] = isPublic\n}\n\n// localCache lives for the lifetime of the main process. The cache\n// size is not expected to grow more than a few hundred bytes.\nvar localCache = &hostCache{\n\tcache: make(map[string]bool),\n}\n\n// ExtractPublicAddress iterates over parameters and extracts the first public\n// address found. Parameter values are assumed to be in priority order. Returns\n// an empty string if only private addresses are available.\nfunc ExtractPublicAddress(values ...string) string {\n\tfor _, value := range values {\n\t\tif value == \"\" || value == \"*\" {\n\t\t\tcontinue\n\t\t}\n\t\thost := value\n\n\t\t// parse URL addresses\n\t\tif u, err := url.Parse(value); err == nil && len(u.Host) > 1 {\n\t\t\thost = removeWildcardsFromHostname(u.Host)\n\t\t}\n\n\t\t// strip port on both URL and non-URL addresses\n\t\thostname, _, err := net.SplitHostPort(host)\n\t\tif err != nil {\n\t\t\thostname = host\n\t\t}\n\n\t\t// for IP addresses\n\t\tif ip := net.ParseIP(hostname); ip != nil {\n\t\t\tif !isPrivateIP(ip) {\n\t\t\t\treturn host\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// for hostnames, first check cache\n\t\tif isPublic, found := localCache.get(hostname); found {\n\t\t\tif isPublic {\n\t\t\t\treturn host\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// otherwise, perform DNS lookup & cache result\n\t\tisPublic := isPublicHostname(hostname)\n\t\tlocalCache.set(hostname, isPublic)\n\t\tif isPublic {\n\t\t\treturn host\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\n// isPrivateIP checks if an IP address is private (RFC 1918/4193).\nfunc isPrivateIP(ip net.IP) bool {\n\treturn ip.IsPrivate() ||\n\t\tip.IsLoopback() ||\n\t\tip.IsLinkLocalUnicast() ||\n\t\tip.IsLinkLocalMulticast() ||\n\t\tip.IsUnspecified() // 0.0.0.0 or ::\n}\n\n// isPublicHostname performs DNS lookup to determine if hostname resolves to public IPs.\n// Returns true if at least one resolved IP is public, false if all are private or lookup fails.\nfunc isPublicHostname(hostname string) bool {\n\t// avoid DNS lookup if localhost\n\tlower := strings.ToLower(hostname)\n\tif lower == \"localhost\" {\n\t\treturn false\n\t}\n\n\tctx, cancel := context.WithTimeoutCause(context.Background(), 2*time.Second, errors.Errorf(\"failed to resolve DNS for %s within 2s\", hostname))\n\tdefer cancel()\n\n\tips, err := net.DefaultResolver.LookupIPAddr(ctx, hostname)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tfor _, ip := range ips {\n\t\tif !isPrivateIP(ip.IP) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// removeWildcardsFromHostname removes wildcard segments from a hostname string\n// by splitting on dots and filtering out asterisk-only segments.\nfunc removeWildcardsFromHostname(hostname string) string {\n\tsep := strings.Split(hostname, \".\")\n\tclean := make([]string, 0, len(sep))\n\tfor _, s := range sep {\n\t\tif s != \"*\" && s != \"\" {\n\t\t\tclean = append(clean, s)\n\t\t}\n\t}\n\treturn strings.Join(clean, \".\")\n}\n"
  },
  {
    "path": "oryx/urlx/join.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage urlx\n\nimport (\n\t\"net/url\"\n\t\"path\"\n\n\t\"github.com/ory/x/cmdx\"\n)\n\n// MustJoin joins the paths of two URLs. Fatals if first is not a DSN.\nfunc MustJoin(first string, parts ...string) string {\n\tu, err := url.Parse(first)\n\tif err != nil {\n\t\tcmdx.Fatalf(\"Unable to parse %s: %s\", first, err)\n\t}\n\treturn AppendPaths(u, parts...).String()\n}\n\n// AppendPaths appends the provided paths to the url.\n// Paths are intentionally *not* URL encoded.\n// The caller is responsible for url encoding, possibly selectively, the required path components with `url.PathEscape`.\nfunc AppendPaths(u *url.URL, paths ...string) (ep *url.URL) {\n\tep = Copy(u)\n\tif len(paths) == 0 {\n\t\treturn ep\n\t}\n\n\tep.Path = path.Join(append([]string{ep.Path}, paths...)...)\n\n\tlast := paths[len(paths)-1]\n\tif last != \"\" && last[len(last)-1] == '/' {\n\t\tep.Path = ep.Path + \"/\"\n\t}\n\n\treturn ep\n}\n\n// SetQuery appends the provided url values to the DSN's query string.\nfunc SetQuery(u *url.URL, query url.Values) (ep *url.URL) {\n\tep = Copy(u)\n\tq := ep.Query()\n\n\tfor k := range query {\n\t\tq.Set(k, query.Get(k))\n\t}\n\n\tep.RawQuery = q.Encode()\n\treturn ep\n}\n"
  },
  {
    "path": "oryx/urlx/parse.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage urlx\n\nimport (\n\t\"net/url\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/ory/x/logrusx\"\n)\n\n// winPathRegex is a regex for [DRIVE-LETTER]:\nvar winPathRegex = regexp.MustCompile(\"^[A-Za-z]:.*\")\n\n// Parse parses rawURL into a URL structure with special handling for file:// URLs\n//\n// File URLs with relative paths (file://../file, ../file) will be returned as a\n// url.URL object without the Scheme set to \"file\". This is because the file\n// scheme does not support relative paths. Make sure to check for\n// both \"file\" or \"\" (an empty string) in URL.Scheme if you are looking for\n// a file path.\n//\n// Use the companion function GetURLFilePath() to get a file path suitable\n// for the current operating system.\nfunc Parse(rawURL string) (*url.URL, error) {\n\tlcRawURL := strings.ToLower(rawURL)\n\tif strings.HasPrefix(lcRawURL, \"file:///\") {\n\t\treturn url.Parse(rawURL)\n\t}\n\n\t// Normally the first part after file:// is a hostname, but since\n\t// this is often misused we interpret the URL like a normal path\n\t// by removing the \"file://\" from the beginning (if it exists)\n\trawURL = trimPrefixIC(rawURL, \"file://\")\n\n\tif winPathRegex.MatchString(rawURL) {\n\t\t// Windows path\n\t\treturn url.Parse(\"file:///\" + rawURL)\n\t}\n\n\tif strings.HasPrefix(lcRawURL, \"\\\\\\\\\") {\n\t\t// Windows UNC path\n\t\t// We extract the hostname and create an appropriate file:// URL\n\t\t// based on the hostname and the path\n\t\thost, path := extractUNCPathParts(rawURL)\n\t\t// It is safe to replace the \\ with / here because this is POSIX style path\n\t\treturn url.Parse(\"file://\" + host + strings.ReplaceAll(path, \"\\\\\", \"/\"))\n\t}\n\n\tparsed, err := url.Parse(rawURL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Since go1.19:\n\t//\n\t// > The URL type now distinguishes between URLs with no authority and URLs with an empty authority.\n\t// > For example, http:///path has an empty authority (host), while http:/path has none.\n\t//\n\t// See https://golang.org/doc/go1.19#net/url for more details.\n\tparsed.OmitHost = false\n\treturn parsed, nil\n}\n\n// ParseOrPanic parses a url or panics.\nfunc ParseOrPanic(in string) *url.URL {\n\tout, err := url.Parse(in)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\treturn out\n}\n\n// ParseOrFatal parses a url or fatals.\nfunc ParseOrFatal(l *logrusx.Logger, in string) *url.URL {\n\tout, err := url.Parse(in)\n\tif err != nil {\n\t\tl.WithError(err).Fatalf(\"Unable to parse url: %s\", in)\n\t}\n\treturn out\n}\n\n// ParseRequestURIOrPanic parses a request uri or panics.\nfunc ParseRequestURIOrPanic(in string) *url.URL {\n\tout, err := url.ParseRequestURI(in)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\treturn out\n}\n\n// ParseRequestURIOrFatal parses a request uri or fatals.\nfunc ParseRequestURIOrFatal(l *logrusx.Logger, in string) *url.URL {\n\tout, err := url.ParseRequestURI(in)\n\tif err != nil {\n\t\tl.WithError(err).Fatalf(\"Unable to parse url: %s\", in)\n\t}\n\treturn out\n}\n\nfunc extractUNCPathParts(uncPath string) (host, path string) {\n\tparts := strings.Split(strings.TrimPrefix(uncPath, \"\\\\\\\\\"), \"\\\\\")\n\thost = parts[0]\n\tif len(parts) > 0 {\n\t\tpath = \"\\\\\" + strings.Join(parts[1:], \"\\\\\")\n\t}\n\treturn host, path\n}\n\n// trimPrefixIC returns s without the provided leading prefix string using\n// case insensitive matching.\n// If s doesn't start with prefix, s is returned unchanged.\nfunc trimPrefixIC(s, prefix string) string {\n\tif strings.HasPrefix(strings.ToLower(s), prefix) {\n\t\treturn s[len(prefix):]\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "oryx/urlx/path.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n//go:build !windows\n// +build !windows\n\npackage urlx\n\nimport (\n\t\"net/url\"\n)\n\n// GetURLFilePath returns the path of a URL that is compatible with the runtime os filesystem\nfunc GetURLFilePath(u *url.URL) string {\n\tif u == nil {\n\t\treturn \"\"\n\t}\n\treturn u.Path\n}\n"
  },
  {
    "path": "oryx/urlx/path_windows.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n//go:build windows\n// +build windows\n\npackage urlx\n\nimport (\n\t\"net/url\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\n// GetURLFilePath returns the path of a URL that is compatible with the runtime os filesystem\nfunc GetURLFilePath(u *url.URL) string {\n\tif u == nil {\n\t\treturn \"\"\n\t}\n\tif !(u.Scheme == \"file\" || u.Scheme == \"\") {\n\t\treturn u.Path\n\t}\n\n\tfPath := u.Path\n\tif u.Host != \"\" {\n\t\t// Make UNC Path\n\t\tfPath = \"\\\\\\\\\" + u.Host + filepath.FromSlash(fPath)\n\t\treturn fPath\n\t}\n\tfPathTrimmed := strings.TrimLeft(fPath, \"/\")\n\tif winPathRegex.MatchString(fPathTrimmed) {\n\t\t// On Windows we should remove the initial path separator in case this\n\t\t// is a normal path (for example: \"\\c:\\\" -> \"c:\\\"\")\n\t\tfPath = fPathTrimmed\n\t}\n\treturn filepath.FromSlash(fPath)\n}\n"
  },
  {
    "path": "oryx/uuidx/uuid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage uuidx\n\nimport \"github.com/gofrs/uuid\"\n\n// NewV4 returns a new randomly generated UUID or panics.\nfunc NewV4() uuid.UUID {\n\treturn uuid.Must(uuid.NewV4())\n}\n"
  },
  {
    "path": "oryx/watcherx/definitions.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage watcherx\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n)\n\ntype (\n\terrSchemeUnknown struct {\n\t\tscheme string\n\t}\n\tEventChannel chan Event\n\tWatcher      interface {\n\t\t// DispatchNow fires the watcher and causes an event.\n\t\t//\n\t\t// WARNING: The returned channel must be read or no further events will\n\t\t// be propagated due to a deadlock.\n\t\tDispatchNow() (<-chan int, error)\n\t}\n\tdispatcher struct {\n\t\ttrigger chan struct{}\n\t\tdone    chan int\n\t}\n)\n\nvar (\n\t// ErrSchemeUnknown is just for checking with errors.Is()\n\tErrSchemeUnknown     = &errSchemeUnknown{}\n\tErrWatcherNotRunning = fmt.Errorf(\"watcher is not running\")\n)\n\nfunc (e *errSchemeUnknown) Is(other error) bool {\n\t_, ok := other.(*errSchemeUnknown)\n\treturn ok\n}\n\nfunc (e *errSchemeUnknown) Error() string {\n\treturn fmt.Sprintf(\"unknown scheme '%s' to watch\", e.scheme)\n}\n\nfunc newDispatcher() *dispatcher {\n\treturn &dispatcher{\n\t\ttrigger: make(chan struct{}),\n\t\tdone:    make(chan int),\n\t}\n}\n\nfunc (d *dispatcher) DispatchNow() (<-chan int, error) {\n\tif d.trigger == nil {\n\t\treturn nil, ErrWatcherNotRunning\n\t}\n\td.trigger <- struct{}{}\n\treturn d.done, nil\n}\n\nfunc Watch(ctx context.Context, u *url.URL, c EventChannel) (Watcher, error) {\n\tswitch u.Scheme {\n\t// see urlx.Parse for why the empty string is also file\n\tcase \"file\", \"\":\n\t\treturn WatchFile(ctx, u.Path, c)\n\t}\n\treturn nil, &errSchemeUnknown{u.Scheme}\n}\n"
  },
  {
    "path": "oryx/watcherx/directory.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage watcherx\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/pkg/errors\"\n)\n\nfunc WatchDirectory(ctx context.Context, dir string, c EventChannel) (Watcher, error) {\n\tw, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tsubDirs := make(map[string]struct{})\n\tif err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tif info.IsDir() {\n\t\t\tif err := w.Add(path); err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\t\t\tsubDirs[path] = struct{}{}\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\n\tdw := &directoryWatcher{\n\t\tdispatcher: newDispatcher(),\n\t\tc:          c,\n\t\tdir:        dir,\n\t\tsubDirs:    subDirs,\n\t\tw:          w,\n\t}\n\tgo dw.streamDirectoryEvents(ctx)\n\treturn dw, nil\n}\n\ntype directoryWatcher struct {\n\t*dispatcher\n\tc       EventChannel\n\tdir     string\n\tsubDirs map[string]struct{}\n\tw       *fsnotify.Watcher\n}\n\nfunc (w *directoryWatcher) handleEvent(ctx context.Context, e fsnotify.Event) {\n\tif e.Has(fsnotify.Remove) {\n\t\tif _, ok := w.subDirs[e.Name]; ok {\n\t\t\t// we do not want any event on deletion of a directory\n\t\t\tdelete(w.subDirs, e.Name)\n\t\t\treturn\n\t\t}\n\t\tw.maybeSend(ctx, &RemoveEvent{\n\t\t\tsource: source(e.Name),\n\t\t})\n\t\treturn\n\t} else if e.Has(fsnotify.Write | fsnotify.Create) {\n\t\tif stats, err := os.Stat(e.Name); err != nil {\n\t\t\tw.maybeSend(ctx, &ErrorEvent{\n\t\t\t\terror:  errors.WithStack(err),\n\t\t\t\tsource: source(e.Name),\n\t\t\t})\n\t\t\treturn\n\t\t} else if stats.IsDir() {\n\t\t\tif err := w.w.Add(e.Name); err != nil {\n\t\t\t\tw.maybeSend(ctx, &ErrorEvent{\n\t\t\t\t\terror:  errors.WithStack(err),\n\t\t\t\t\tsource: source(e.Name),\n\t\t\t\t})\n\t\t\t}\n\t\t\tw.subDirs[e.Name] = struct{}{}\n\t\t\treturn\n\t\t}\n\n\t\t//#nosec G304 -- false positive\n\t\tdata, err := os.ReadFile(e.Name)\n\t\tif err != nil {\n\t\t\tw.maybeSend(ctx, &ErrorEvent{\n\t\t\t\terror:  err,\n\t\t\t\tsource: source(e.Name),\n\t\t\t})\n\t\t} else {\n\t\t\tw.maybeSend(ctx, &ChangeEvent{\n\t\t\t\tdata:   data,\n\t\t\t\tsource: source(e.Name),\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc (w *directoryWatcher) maybeSend(ctx context.Context, e Event) bool {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn false\n\tcase w.c <- e:\n\t\treturn true\n\t}\n}\n\nfunc (w *directoryWatcher) streamDirectoryEvents(ctx context.Context) {\n\tdefer func() {\n\t\tclose(w.done)\n\t\tclose(w.c)\n\t\t_ = w.w.Close()\n\t}()\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase e := <-w.w.Events:\n\t\t\tw.handleEvent(ctx, e)\n\t\tcase <-w.trigger:\n\t\t\tvar eventsSent int\n\n\t\t\tif err := filepath.Walk(w.dir, func(path string, info os.FileInfo, err error) error {\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif !info.IsDir() {\n\t\t\t\t\t//#nosec G304 -- false positive\n\t\t\t\t\tdata, err := os.ReadFile(path)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tif !w.maybeSend(ctx, &ErrorEvent{\n\t\t\t\t\t\t\terror:  err,\n\t\t\t\t\t\t\tsource: source(path),\n\t\t\t\t\t\t}) {\n\t\t\t\t\t\t\treturn errors.WithStack(context.Canceled)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif !w.maybeSend(ctx, &ChangeEvent{\n\t\t\t\t\t\t\tdata:   data,\n\t\t\t\t\t\t\tsource: source(path),\n\t\t\t\t\t\t}) {\n\t\t\t\t\t\t\treturn errors.WithStack(context.Canceled)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\teventsSent++\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}); err != nil {\n\t\t\t\tif !w.maybeSend(ctx, &ErrorEvent{\n\t\t\t\t\terror:  err,\n\t\t\t\t\tsource: source(w.dir),\n\t\t\t\t}) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\teventsSent++\n\t\t\t}\n\n\t\t\tw.done <- eventsSent\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "oryx/watcherx/event.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage watcherx\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n)\n\ntype (\n\tEvent interface {\n\t\t// MarshalJSON is required to work multiple times\n\t\tjson.Marshaler\n\n\t\tReader() io.Reader\n\t\tSource() string\n\t\tString() string\n\t\tsetSource(string)\n\t}\n\tsource     string\n\tErrorEvent struct {\n\t\terror\n\t\tsource\n\t}\n\tChangeEvent struct {\n\t\tdata []byte\n\t\tsource\n\t}\n\tRemoveEvent struct {\n\t\tsource\n\t}\n\tserialEventType string\n\tserialEvent     struct {\n\t\tType   serialEventType `json:\"type\"`\n\t\tData   []byte          `json:\"data\"`\n\t\tSource source          `json:\"source\"`\n\t}\n)\n\nfunc NewErrorEvent(err error, source_ string) *ErrorEvent {\n\treturn &ErrorEvent{\n\t\terror:  err,\n\t\tsource: source(source_),\n\t}\n}\n\nconst (\n\tserialTypeChange serialEventType = \"change\"\n\tserialTypeRemove serialEventType = \"remove\"\n\tserialTypeError  serialEventType = \"error\"\n)\n\nfunc (e *ErrorEvent) Reader() io.Reader {\n\treturn bytes.NewBufferString(e.Error())\n}\n\nfunc (e *ErrorEvent) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(serialEvent{\n\t\tType:   serialTypeError,\n\t\tData:   []byte(e.Error()),\n\t\tSource: e.source,\n\t})\n}\n\nfunc (e *ErrorEvent) String() string {\n\treturn fmt.Sprintf(\"error: %+v; source: %s\", e.error, e.source)\n}\n\nfunc (e source) Source() string {\n\treturn string(e)\n}\n\nfunc (e *source) setSource(nsrc string) {\n\t*e = source(nsrc)\n}\n\nfunc (e *ChangeEvent) Reader() io.Reader {\n\treturn bytes.NewBuffer(e.data)\n}\n\nfunc (e *ChangeEvent) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(serialEvent{\n\t\tType:   serialTypeChange,\n\t\tData:   e.data,\n\t\tSource: e.source,\n\t})\n}\n\nfunc (e *ChangeEvent) String() string {\n\treturn fmt.Sprintf(\"data: %s; source: %s\", e.data, e.source)\n}\n\nfunc (e *RemoveEvent) Reader() io.Reader {\n\treturn nil\n}\n\nfunc (e *RemoveEvent) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(serialEvent{\n\t\tType:   serialTypeRemove,\n\t\tSource: e.source,\n\t})\n}\n\nfunc (e *RemoveEvent) String() string {\n\treturn fmt.Sprintf(\"removed source: %s\", e.source)\n}\n"
  },
  {
    "path": "oryx/watcherx/file.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage watcherx\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/pkg/errors\"\n)\n\n// WatchFile spawns a background goroutine to watch file, reporting any changes\n// to c. Watching stops when ctx is canceled.\nfunc WatchFile(ctx context.Context, file string, c EventChannel) (Watcher, error) {\n\twatcher, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tdir := filepath.Dir(file)\n\tif err := watcher.Add(dir); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\tresolvedFile, err := filepath.EvalSymlinks(file)\n\tif err != nil {\n\t\tif pathError := new(os.PathError); !errors.As(err, &pathError) {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\t// The file does not exist. The watcher should still watch the directory\n\t\t// to get notified about file creation.\n\t\tresolvedFile = \"\"\n\t} else if resolvedFile != file {\n\t\t// If `resolvedFile` != `file` then `file` is a symlink and we have to explicitly watch the referenced file.\n\t\t// This is because fsnotify follows symlinks and watches the destination file, not the symlink\n\t\t// itself. That is at least the case for unix systems. See: https://github.com/fsnotify/fsnotify/issues/199\n\t\tif err := watcher.Add(file); err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t}\n\td := newDispatcher()\n\tgo streamFileEvents(ctx, watcher, c, d.trigger, d.done, file, resolvedFile)\n\treturn d, nil\n}\n\n// streamFileEvents watches for file changes and supports symlinks which requires several workarounds due to limitations of fsnotify.\n// Argument `resolvedFile` is the resolved symlink path of the file, or it is the watchedFile name itself. If `resolvedFile` is empty, then the watchedFile does not exist.\nfunc streamFileEvents(ctx context.Context, watcher *fsnotify.Watcher, c EventChannel, sendNow <-chan struct{}, sendNowDone chan<- int, watchedFile, resolvedFile string) {\n\teventSource := source(watchedFile)\n\tremoveDirectFileWatcher := func() {\n\t\t_ = watcher.Remove(watchedFile)\n\t}\n\taddDirectFileWatcher := func() {\n\t\t// check if the watchedFile (symlink) exists\n\t\t// if it does not the dir watcher will notify us when it gets created\n\t\tif _, err := os.Lstat(watchedFile); err == nil {\n\t\t\tif err := watcher.Add(watchedFile); err != nil {\n\t\t\t\tc <- &ErrorEvent{\n\t\t\t\t\terror:  errors.WithStack(err),\n\t\t\t\t\tsource: eventSource,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tdefer watcher.Close()\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase <-sendNow:\n\t\t\tif resolvedFile == \"\" {\n\t\t\t\t// The file does not exist. Announce this by sending a RemoveEvent.\n\t\t\t\tc <- &RemoveEvent{eventSource}\n\t\t\t} else {\n\t\t\t\t// The file does exist. Announce the current content by sending a ChangeEvent.\n\t\t\t\t// #nosec G304 -- false positive\n\t\t\t\tdata, err := os.ReadFile(watchedFile)\n\t\t\t\tif err != nil {\n\t\t\t\t\tselect {\n\t\t\t\t\tcase c <- &ErrorEvent{\n\t\t\t\t\t\terror:  errors.WithStack(err),\n\t\t\t\t\t\tsource: eventSource,\n\t\t\t\t\t}:\n\t\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tselect {\n\t\t\t\tcase c <- &ChangeEvent{\n\t\t\t\t\tdata:   data,\n\t\t\t\t\tsource: eventSource,\n\t\t\t\t}:\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// in any of the above cases we send exactly one event\n\t\t\tselect {\n\t\t\tcase sendNowDone <- 1:\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn\n\t\t\t}\n\t\tcase e, ok := <-watcher.Events:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// filter events to only watch watchedFile\n\t\t\t// e.Name contains the name of the watchedFile (regardless whether it is a symlink), not the resolved file name\n\t\t\tif filepath.Clean(e.Name) == watchedFile {\n\t\t\t\trecentlyResolvedFile, err := filepath.EvalSymlinks(watchedFile)\n\t\t\t\t// when there is no error the file exists and any symlinks can be resolved\n\t\t\t\tif err != nil {\n\t\t\t\t\t// check if the watchedFile (or the file behind the symlink) was removed\n\t\t\t\t\tif _, ok := err.(*os.PathError); ok {\n\t\t\t\t\t\tselect {\n\t\t\t\t\t\tcase c <- &RemoveEvent{eventSource}:\n\t\t\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tremoveDirectFileWatcher()\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tselect {\n\t\t\t\t\tcase c <- &ErrorEvent{\n\t\t\t\t\t\terror:  errors.WithStack(err),\n\t\t\t\t\t\tsource: eventSource,\n\t\t\t\t\t}:\n\t\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// This catches following three cases:\n\t\t\t\t// 1. the watchedFile was written or created\n\t\t\t\t// 2. the watchedFile is a symlink and has changed (k8s config map updates)\n\t\t\t\t// 3. the watchedFile behind the symlink was written or created\n\t\t\t\tswitch {\n\t\t\t\tcase recentlyResolvedFile != resolvedFile:\n\t\t\t\t\tresolvedFile = recentlyResolvedFile\n\t\t\t\t\t// watch the symlink again to update the actually watched file\n\t\t\t\t\tremoveDirectFileWatcher()\n\t\t\t\t\taddDirectFileWatcher()\n\t\t\t\t\t// we fallthrough because we also want to read the file in this case\n\t\t\t\t\tfallthrough\n\t\t\t\tcase e.Has(fsnotify.Write | fsnotify.Create):\n\t\t\t\t\t// #nosec G304 -- false positive\n\t\t\t\t\tdata, err := os.ReadFile(watchedFile)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tselect {\n\t\t\t\t\t\tcase c <- &ErrorEvent{\n\t\t\t\t\t\t\terror:  errors.WithStack(err),\n\t\t\t\t\t\t\tsource: eventSource,\n\t\t\t\t\t\t}:\n\t\t\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tselect {\n\t\t\t\t\tcase c <- &ChangeEvent{\n\t\t\t\t\t\tdata:   data,\n\t\t\t\t\t\tsource: eventSource,\n\t\t\t\t\t}:\n\t\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "oryx/watcherx/integrationtest/.dockerignore",
    "content": "event_logger.yml\nconfigmap.yml\n\nMakefile\nREADME.md\n\neventlog_snapshot\n"
  },
  {
    "path": "oryx/watcherx/integrationtest/.gitignore",
    "content": "tmp_snapshot\n"
  },
  {
    "path": "oryx/watcherx/integrationtest/Dockerfile",
    "content": "FROM golang:1.26-alpine3.23 AS builder\n\nRUN apk -U --no-cache add build-base\n\nWORKDIR /go/src/github.com/ory/x\n\nADD go.mod go.mod\nADD go.sum go.sum\n\nRUN go mod download\n\nADD . .\n\nRUN go build -o /usr/bin/eventlogger ./watcherx/integrationtest\n\nFROM alpine:3.22\n\nCOPY --from=builder /usr/bin/eventlogger /usr/bin/eventlogger\n\nENTRYPOINT [\"eventlogger\"]\nCMD [\"/etc/config/mock-config\"]\n"
  },
  {
    "path": "oryx/watcherx/integrationtest/Makefile",
    "content": "SHELL=/bin/bash -euo pipefail\n\nCLUSTER_NAME=watcherx-integration-test\nSNAPSHOT_FILE=eventlog_snapshot\n\ndefine generate_snapshot\n\tsleep 5\n\tmake update\n\tsleep 1\n\tkubectl logs eventlogger --context kind-${CLUSTER_NAME} >> $(1)\n\tmake apply\n\tsleep 1\n\tkubectl logs eventlogger --context kind-${CLUSTER_NAME} >> $(1)\n\tmake update\n\tsleep 1\n\tkubectl logs eventlogger --context kind-${CLUSTER_NAME} >> $(1)\nendef\n\n.PHONY: build\nbuild:\n\tdocker build -f Dockerfile -t eventlogger:latest ../..\n\n.PHONY: create\ncreate:\n\tkind create cluster --name ${CLUSTER_NAME} --wait 1m || true\n\n.PHONY: load\nload:\n\tkind load docker-image eventlogger:latest --name ${CLUSTER_NAME}\n\n.PHONY: apply\napply:\n\tkubectl apply -f configmap.yml -f event_logger.yml --context kind-${CLUSTER_NAME}\n\n.PHONY: delete\ndelete:\n\tkind delete cluster --name ${CLUSTER_NAME}\n\n.PHONY: setup\nsetup: build create load apply\n\n.PHONY: snapshot\nsnapshot: setup container-restart\n\trm ${SNAPSHOT_FILE}\n\t${call generate_snapshot,$(SNAPSHOT_FILE)}\n\n.PHONY: check\ncheck: setup container-restart\n\trm tmp_snapshot || true\n\t${call generate_snapshot,tmp_snapshot}\n\tdiff tmp_snapshot ${SNAPSHOT_FILE}\n\n.PHONY: logs\nlogs:\n\tkubectl logs eventlogger --context kind-${CLUSTER_NAME}\n\n.PHONY: container-restart\ncontainer-restart:\n\tkubectl delete -f event_logger.yml --context kind-${CLUSTER_NAME}\n\tkubectl apply -f event_logger.yml --context kind-${CLUSTER_NAME}\n\n.PHONY: update\nupdate:\n\tcat configmap.yml | sed 's/somevalue/othervalue/' | kubectl apply -f - --context kind-${CLUSTER_NAME}\n\tcat event_logger.yml | sed 's/somevalue/othervalue/' | kubectl apply -f - --context kind-${CLUSTER_NAME}\n"
  },
  {
    "path": "oryx/watcherx/integrationtest/README.md",
    "content": "# Integration Test for watcherx/FileWatcher\n\nAs kubernetes has a special way to change mounted config map values we want to\nmake sure our file watcher is compatible with that.\n\n## Perquisites\n\nThe versions are the ones that definitely work.\n\n- kind (v0.8.1)\n- kubectl (v1.18.5)\n- docker (v19.03.12-ce)\n- make (v4.3)\n\n## Structure\n\nThe `main.go` just logs all events it gets. It is deployed to a kind kubernetes\ncluster together with a configmap that gets updated during the test. For details\non the test steps have a look at the `Makefile`.\n\n## Running\n\nTo generate the log snapshot run `make snapshot`. That snapshot should be\ncommitted. To check if the FileWatcher works run `make check`. For debugging\npurposes single steps of the setup have descriptive make target names and can be\nrun separately. It is safe to delete the cluster at any point or rerun snapshot\ngeneration.\n"
  },
  {
    "path": "oryx/watcherx/integrationtest/configmap.yml",
    "content": "kind: ConfigMap\napiVersion: v1\nmetadata:\n  name: changing-config\ndata:\n  mock-config: somevalue\n"
  },
  {
    "path": "oryx/watcherx/integrationtest/event_logger.yml",
    "content": "kind: Pod\napiVersion: v1\nmetadata:\n  name: eventlogger\n  annotations:\n    variant: somevalue\nspec:\n  containers:\n    - name: eventlogger\n      image: eventlogger:latest\n      imagePullPolicy: Never\n      volumeMounts:\n        - name: changing-config\n          mountPath: /etc/config\n  restartPolicy: Never\n  volumes:\n    - name: changing-config\n      configMap:\n        name: changing-config\n"
  },
  {
    "path": "oryx/watcherx/integrationtest/eventlog_snapshot",
    "content": "watching file /etc/config/mock-config\ngot change event:\nData: othervalue,\nSrc: /etc/config/mock-config\nwatching file /etc/config/mock-config\ngot change event:\nData: othervalue,\nSrc: /etc/config/mock-config\ngot change event:\nData: somevalue,\nSrc: /etc/config/mock-config\nwatching file /etc/config/mock-config\ngot change event:\nData: othervalue,\nSrc: /etc/config/mock-config\ngot change event:\nData: somevalue,\nSrc: /etc/config/mock-config\ngot change event:\nData: othervalue,\nSrc: /etc/config/mock-config\n"
  },
  {
    "path": "oryx/watcherx/integrationtest/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/ory/x/watcherx\"\n)\n\nfunc main() {\n\tif len(os.Args) != 2 {\n\t\t_, _ = fmt.Fprintf(os.Stderr, \"expected 1 comand line argument but got %d\\n\", len(os.Args)-1)\n\t\tos.Exit(1)\n\t}\n\tc := make(chan watcherx.Event)\n\t_, err := watcherx.WatchFile(context.Background(), os.Args[1], c)\n\tif err != nil {\n\t\t_, _ = fmt.Fprintf(os.Stderr, \"could not initialize file watcher: %+v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Printf(\"watching file %s\\n\", os.Args[1])\n\tfor {\n\t\tswitch e := (<-c).(type) {\n\t\tcase *watcherx.ChangeEvent:\n\t\t\tvar data []byte\n\t\t\tdata, err = io.ReadAll(e.Reader())\n\t\t\tif err != nil {\n\t\t\t\t_, _ = fmt.Fprintf(os.Stderr, \"could not read data: %+v\\n\", err)\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tfmt.Printf(\"got change event:\\nData: %s,\\nSrc: %s\\n\", data, e.Source())\n\t\tcase *watcherx.RemoveEvent:\n\t\t\tfmt.Printf(\"got remove event:\\nSrc: %s\\n\", e.Source())\n\t\tcase *watcherx.ErrorEvent:\n\t\t\tfmt.Printf(\"got error event:\\nError: %s\\n\", e.Error())\n\t\tdefault:\n\t\t\tfmt.Println(\"got unknown event\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "oryx/watcherx/test_helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage watcherx\n\nimport (\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc KubernetesAtomicWrite(t *testing.T, dir, fileName, content string) {\n\t// atomic write according to https://github.com/kubernetes/kubernetes/blob/master/pkg/volume/util/atomic_writer.go\n\tconst (\n\t\tdataDirName    = \"..data\"\n\t\tnewDataDirName = \"..data_tmp\"\n\t)\n\t// (2)\n\tdataDirPath := filepath.Join(dir, dataDirName)\n\toldTsDir, err := os.Readlink(dataDirPath)\n\tif err != nil {\n\t\trequire.True(t, os.IsNotExist(err), \"%+v\", err)\n\t\t// although Readlink() returns \"\" on err, don't be fragile by relying on it (since it's not specified in docs)\n\t\t// empty oldTsDir indicates that it didn't exist\n\t\toldTsDir = \"\"\n\t}\n\toldTsPath := filepath.Join(dir, oldTsDir)\n\n\t// (3) we are not interested in the case where a file gets deleted as we just operate on one file\n\t// (4) we assume the file needs an update\n\n\t// (5)\n\ttsDir, err := os.MkdirTemp(dir, time.Now().UTC().Format(\"..2006_01_02_15_04_05.\"))\n\trequire.NoError(t, err)\n\ttsDirName := filepath.Base(tsDir)\n\n\t// (6)\n\trequire.NoError(\n\t\tt,\n\t\tos.WriteFile(path.Join(tsDir, fileName), []byte(content), 0600),\n\t)\n\n\t// (7)\n\t_, err = os.Readlink(filepath.Join(dir, fileName))\n\tif err != nil && os.IsNotExist(err) {\n\t\t// The link into the data directory for this path doesn't exist; create it\n\t\trequire.NoError(\n\t\t\tt,\n\t\t\tos.Symlink(filepath.Join(dataDirName, fileName), filepath.Join(dir, fileName)),\n\t\t)\n\t}\n\n\t// (8)\n\tnewDataDirPath := filepath.Join(dir, newDataDirName)\n\trequire.NoError(\n\t\tt,\n\t\tos.Symlink(tsDirName, newDataDirPath),\n\t)\n\n\t// (9)\n\tif runtime.GOOS == \"windows\" {\n\t\trequire.NoError(t, os.Remove(dataDirPath))\n\t\trequire.NoError(t, os.Symlink(tsDirName, dataDirPath))\n\t\trequire.NoError(t, os.Remove(newDataDirPath))\n\t} else {\n\t\trequire.NoError(t, os.Rename(newDataDirPath, dataDirPath))\n\t}\n\n\t// (10) in our case there is nothing to remove\n\n\t// (11)\n\tif len(oldTsDir) > 0 {\n\t\trequire.NoError(t, os.RemoveAll(oldTsPath))\n\t}\n}\n"
  },
  {
    "path": "otp/otp.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage otp\n\nimport (\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/randx\"\n)\n\n// Entropy sets the number of characters used for generating verification codes. This must not be\n// changed to another value as we only have 32 characters available in the SQL schema.\nconst Entropy = 32\n\nfunc New() (string, error) {\n\tcode, err := randx.RuneSequence(Entropy, randx.AlphaNum)\n\tif err != nil {\n\t\treturn \"\", errors.WithStack(err)\n\t}\n\treturn string(code), nil\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": true,\n  \"scripts\": {\n    \"openapi-generator-cli\": \"openapi-generator-cli\",\n    \"wait-on\": \"wait-on\"\n  },\n  \"prettier\": \"ory-prettier-styles\",\n  \"dependencies\": {\n    \"@openapitools/openapi-generator-cli\": \"2.30.2\",\n    \"yamljs\": \"0.3.0\"\n  },\n  \"devDependencies\": {\n    \"license-checker\": \"25.0.1\",\n    \"ory-prettier-styles\": \"1.3.0\",\n    \"prettier\": \"3.8.1\",\n    \"prettier-plugin-packagejson\": \"3.0.2\",\n    \"process\": \"0.11.10\",\n    \"wait-on\": \"9.0.1\"\n  },\n  \"overrides\": {\n    \"axios\": \">=1.13.5\",\n    \"glob\": \">=11.1.0\"\n  }\n}\n"
  },
  {
    "path": "persistence/reference.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage persistence\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/x/networkx\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/pop/v6\"\n\n\t\"github.com/ory/x/popx\"\n\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/kratos/session\"\n)\n\ntype Provider interface {\n\tPersister() Persister\n\tSetPersister(Persister)\n}\n\ntype Persister interface {\n\tcontinuity.Persister\n\tidentity.PrivilegedPool\n\tregistration.FlowPersister\n\tlogin.FlowPersister\n\tsettings.FlowPersister\n\tcourier.Persister\n\tsession.Persister\n\tsessiontokenexchange.Persister\n\terrorx.Persister\n\tverification.FlowPersister\n\trecovery.FlowPersister\n\tlink.RecoveryTokenPersister\n\tlink.VerificationTokenPersister\n\tcode.RecoveryCodePersister\n\tcode.VerificationCodePersister\n\tcode.RegistrationCodePersister\n\tcode.LoginCodePersister\n\n\tCleanupDatabase(context.Context, time.Duration, time.Duration, int) error\n\tClose(context.Context) error\n\tPing(context.Context) error\n\tMigrationStatus(context.Context) (popx.MigrationStatuses, error)\n\tMigrateDown(ctx context.Context, steps int) error\n\tMigrateUp(context.Context) error\n\tMigrationBox() *popx.MigrationBox\n\tGetConnection(context.Context) *pop.Connection\n\tConnection(ctx context.Context) *pop.Connection\n\tx.TransactionalPersister\n\tNetworker\n}\n\ntype Networker interface {\n\tWithNetworkID(nid uuid.UUID) Persister\n\tNetworkID(ctx context.Context) uuid.UUID\n\tDetermineNetwork(ctx context.Context) (*networkx.Network, error)\n}\n"
  },
  {
    "path": "persistence/sql/.soda.yml",
    "content": "development:\n  url: sqlite://a/b\n\nsqlite:\n  url: sqlite://a/b\n\npostgres:\n  url: postgres://a/b\n\nmysql:\n  url: mysql://tcp(a)/b?parseTime=true&multiStatements=true\n\ncockroach:\n  url: crdb://a/b\n"
  },
  {
    "path": "persistence/sql/README.md",
    "content": "# SQL Migrations\n\nMigrations consist of one `up` and one `down` file.\nTo create these SQL migrations, copy the last migration in `./persistence/sql/migrations/sql` and change the timestamp to the current timestamp and the name to the desired name.\n\nIf some logic is different for one of the database systems, add the id after the name to the file name.\nThe content of that file will override the content of the \"general\" file for that particular DB system.\n\nExample:\n\n`20220802103909000000_courier_send_count.up.sql`\nand\n`20220802103909000000_courier_send_count.down.sql`\n\nWith for example cockroach specific behavior:\n\n`20220802103909000000_courier_send_count.cockroach.up.sql`\nand\n`20220802103909000000_courier_send_count.cockroach.down.sql`\n\nReplace `cockroach` with `mysql`, `postgres` or `sqlite` if applicable.\n\n## Old Way\n\nTo create SQL migrations, target each database individually and run\n\n```\n$ dialect=mysql  # or postgres|cockroach|sqlite\n$ name=\n$ ory dev pop migration create -d=$dialect ./persistence/sql/migrations/templates $name\n$ soda generate sql -e mysql -c ./persistence/sql/.soda.yml -p ./persistence/sql/migrations/templates [name]\n$ soda generate sql -e sqlite -c ./persistence/sql/.soda.yml -p ./persistence/sql/migrations/templates [name]\n$ soda generate sql -e postgres -c ./persistence/sql/.soda.yml -p ./persistence/sql/migrations/templates [name]\n$ soda generate sql -e cockroach -c ./persistence/sql/.soda.yml -p ./persistence/sql/migrations/templates [name]\n```\n\nand remove the `sqlite` part from the newly generated file to create a SQL migrations that works with all\naforementioned databases.\n\n## Rendering Migrations\n\nBecause migrations needs to be backwards compatible, and because fizz migrations might change, we render\nfizz migrations to raw SQL statements using `make migrations-render`.\n\nThe concrete migrations being applied can be found in [`./migrations/sql`](./migrations/sql).\n"
  },
  {
    "path": "persistence/sql/batch/.snapshots/Test_buildInsertQueryArgs-case=Identities.json",
    "content": "{\n  \"TableName\": \"\\\"identities\\\"\",\n  \"ColumnsDecl\": \"\\\"available_aal\\\", \\\"created_at\\\", \\\"external_id\\\", \\\"id\\\", \\\"metadata_admin\\\", \\\"metadata_public\\\", \\\"nid\\\", \\\"organization_id\\\", \\\"schema_id\\\", \\\"state\\\", \\\"state_changed_at\\\", \\\"traits\\\", \\\"updated_at\\\"\",\n  \"Columns\": [\n    \"available_aal\",\n    \"created_at\",\n    \"external_id\",\n    \"id\",\n    \"metadata_admin\",\n    \"metadata_public\",\n    \"nid\",\n    \"organization_id\",\n    \"schema_id\",\n    \"state\",\n    \"state_changed_at\",\n    \"traits\",\n    \"updated_at\"\n  ],\n  \"Placeholders\": \"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\"\n}\n"
  },
  {
    "path": "persistence/sql/batch/.snapshots/Test_buildInsertQueryArgs-case=RecoveryAddress#01.json",
    "content": "{\n  \"TableName\": \"\\\"identity_recovery_addresses\\\"\",\n  \"ColumnsDecl\": \"\\\"created_at\\\", \\\"id\\\", \\\"identity_id\\\", \\\"nid\\\", \\\"updated_at\\\", \\\"value\\\", \\\"via\\\"\",\n  \"Columns\": [\n    \"created_at\",\n    \"id\",\n    \"identity_id\",\n    \"nid\",\n    \"updated_at\",\n    \"value\",\n    \"via\"\n  ],\n  \"Placeholders\": \"(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?)\"\n}\n"
  },
  {
    "path": "persistence/sql/batch/.snapshots/Test_buildInsertQueryArgs-case=RecoveryAddress.json",
    "content": "{\n  \"TableName\": \"\\\"identity_recovery_addresses\\\"\",\n  \"ColumnsDecl\": \"\\\"created_at\\\", \\\"id\\\", \\\"identity_id\\\", \\\"nid\\\", \\\"updated_at\\\", \\\"value\\\", \\\"via\\\"\",\n  \"Columns\": [\n    \"created_at\",\n    \"id\",\n    \"identity_id\",\n    \"nid\",\n    \"updated_at\",\n    \"value\",\n    \"via\"\n  ],\n  \"Placeholders\": \"(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?)\"\n}\n"
  },
  {
    "path": "persistence/sql/batch/.snapshots/Test_buildInsertQueryArgs-case=cockroach.json",
    "content": "{\n  \"TableName\": \"\\\"test_models\\\"\",\n  \"ColumnsDecl\": \"\\\"created_at\\\", \\\"id\\\", \\\"int\\\", \\\"nid\\\", \\\"null_time_ptr\\\", \\\"string\\\", \\\"traits\\\", \\\"updated_at\\\"\",\n  \"Columns\": [\n    \"created_at\",\n    \"id\",\n    \"int\",\n    \"nid\",\n    \"null_time_ptr\",\n    \"string\",\n    \"traits\",\n    \"updated_at\"\n  ],\n  \"Placeholders\": \"(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?, ?),\\n(?, gen_random_uuid(), ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?)\"\n}\n"
  },
  {
    "path": "persistence/sql/batch/.snapshots/Test_buildInsertQueryArgs-case=testModel.json",
    "content": "{\n  \"TableName\": \"\\\"test_models\\\"\",\n  \"ColumnsDecl\": \"\\\"created_at\\\", \\\"id\\\", \\\"int\\\", \\\"nid\\\", \\\"null_time_ptr\\\", \\\"string\\\", \\\"traits\\\", \\\"updated_at\\\"\",\n  \"Columns\": [\n    \"created_at\",\n    \"id\",\n    \"int\",\n    \"nid\",\n    \"null_time_ptr\",\n    \"string\",\n    \"traits\",\n    \"updated_at\"\n  ],\n  \"Placeholders\": \"(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?),\\n(?, ?, ?, ?, ?, ?, ?, ?)\"\n}\n"
  },
  {
    "path": "persistence/sql/batch/.snapshots/Test_buildInsertQueryValues-case=testModel-case=cockroach.json",
    "content": "[\n  \"2023-01-01T00:00:00Z\",\n  \"2023-01-01T00:00:00Z\",\n  \"string\",\n  42,\n  null,\n  {\n    \"foo\": \"bar\"\n  }\n]\n"
  },
  {
    "path": "persistence/sql/batch/create.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage batch\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/jmoiron/sqlx/reflectx\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\ntype (\n\tinsertQueryArgs struct {\n\t\tTableName    string\n\t\tColumnsDecl  string\n\t\tColumns      []string\n\t\tPlaceholders string\n\t}\n\tquoter interface {\n\t\tQuote(key string) string\n\t}\n\tTracerConnection struct {\n\t\tTracer     *otelx.Tracer\n\t\tConnection *pop.Connection\n\t}\n\n\t// PartialConflictError represents a partial conflict during [Create]. It always\n\t// wraps a [sqlcon.ErrUniqueViolation], so that the caller can either abort the\n\t// whole transaction, or handle the partial success.\n\tPartialConflictError[T any] struct {\n\t\tFailed []*T\n\t}\n)\n\nfunc (p *PartialConflictError[T]) Error() string {\n\treturn fmt.Sprintf(\"partial conflict error: %d models failed to insert\", len(p.Failed))\n}\n\nfunc (p *PartialConflictError[T]) ErrOrNil() error {\n\tif len(p.Failed) == 0 {\n\t\treturn nil\n\t}\n\treturn p\n}\n\nfunc (p *PartialConflictError[T]) Unwrap() error {\n\tif len(p.Failed) == 0 {\n\t\treturn nil\n\t}\n\treturn sqlcon.ErrUniqueViolation\n}\n\nfunc buildInsertQueryArgs[T any](ctx context.Context, models []*T, opts *createOpts) insertQueryArgs {\n\tvar (\n\t\tv     T\n\t\tmodel = pop.NewModel(v, ctx)\n\n\t\tcolumns        []string\n\t\tquotedColumns  []string\n\t\tplaceholders   []string\n\t\tplaceholderRow []string\n\t)\n\n\tfor _, col := range model.Columns().Cols {\n\t\tcolumns = append(columns, col.Name)\n\t\tplaceholderRow = append(placeholderRow, \"?\")\n\t}\n\n\t// We sort for the sole reason that the test snapshots are deterministic.\n\tsort.Strings(columns)\n\n\tfor _, col := range columns {\n\t\tquotedColumns = append(quotedColumns, opts.quoter.Quote(col))\n\t}\n\n\t// We generate a list (for every row one) of VALUE statements here that\n\t// will be substituted by their column values later:\n\t//\n\t//\t(?, ?, ?, ?),\n\t//\t(?, ?, ?, ?),\n\t//\t(?, ?, ?, ?)\n\tfor _, m := range models {\n\t\tm := reflect.ValueOf(m)\n\n\t\tpl := make([]string, len(placeholderRow))\n\t\tcopy(pl, placeholderRow)\n\n\t\t// There is a special case - when using CockroachDB we want to generate\n\t\t// UUIDs using \"gen_random_uuid()\" which ends up in a VALUE statement of:\n\t\t//\n\t\t//\t(gen_random_uuid(), ?, ?, ?),\n\t\tfor k := range placeholderRow {\n\t\t\tif columns[k] != \"id\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfield := opts.mapper.FieldByName(m, columns[k])\n\t\t\tval, ok := field.Interface().(uuid.UUID)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif val == uuid.Nil && opts.dialect == dbal.DriverCockroachDB && !opts.partialInserts {\n\t\t\t\tpl[k] = \"gen_random_uuid()\"\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tplaceholders = append(placeholders, fmt.Sprintf(\"(%s)\", strings.Join(pl, \", \")))\n\t}\n\n\treturn insertQueryArgs{\n\t\tTableName:    opts.quoter.Quote(model.TableName()),\n\t\tColumnsDecl:  strings.Join(quotedColumns, \", \"),\n\t\tColumns:      columns,\n\t\tPlaceholders: strings.Join(placeholders, \",\\n\"),\n\t}\n}\n\nfunc buildInsertQueryValues[T any](columns []string, models []*T, opts *createOpts) (values []any, err error) {\n\tfor _, m := range models {\n\t\tm := reflect.ValueOf(m)\n\n\t\tnow := opts.now()\n\t\t// Append model fields to args\n\t\tfor _, c := range columns {\n\t\t\tfield := opts.mapper.FieldByName(m, c)\n\n\t\t\tswitch c {\n\t\t\tcase \"created_at\":\n\t\t\t\tif pop.IsZeroOfUnderlyingType(field.Interface()) {\n\t\t\t\t\tfield.Set(reflect.ValueOf(now))\n\t\t\t\t}\n\t\t\tcase \"updated_at\":\n\t\t\t\tfield.Set(reflect.ValueOf(now))\n\t\t\tcase \"id\":\n\t\t\t\tif field.Interface().(uuid.UUID) != uuid.Nil {\n\t\t\t\t\tbreak // breaks switch, not for\n\t\t\t\t} else if opts.dialect == dbal.DriverCockroachDB && !opts.partialInserts {\n\t\t\t\t\t// This is a special case:\n\t\t\t\t\t// 1. We're using cockroach\n\t\t\t\t\t// 2. It's the primary key field (\"ID\")\n\t\t\t\t\t// 3. A UUID was not yet set.\n\t\t\t\t\t//\n\t\t\t\t\t// If all these conditions meet, the VALUE statement will look as such:\n\t\t\t\t\t//\n\t\t\t\t\t//\t(gen_random_uuid(), ?, ?, ?, ...)\n\t\t\t\t\t//\n\t\t\t\t\t// For that reason, we do not add the ID value to the list of arguments,\n\t\t\t\t\t// because one of the arguments is using a built-in and thus doesn't need a value.\n\t\t\t\t\tcontinue // break switch, not for\n\t\t\t\t}\n\n\t\t\t\tid, err := uuid.NewV4()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tfield.Set(reflect.ValueOf(id))\n\t\t\t}\n\n\t\t\tvalues = append(values, field.Interface())\n\n\t\t\t// Special-handling for *sqlxx.NullTime: mapper.FieldByName sets this to a zero time.Time,\n\t\t\t// but we want a nil pointer instead.\n\t\t\tif i, ok := field.Interface().(*sqlxx.NullTime); ok {\n\t\t\t\tif time.Time(*i).IsZero() {\n\t\t\t\t\tfield.Set(reflect.Zero(field.Type()))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn values, nil\n}\n\ntype createOpts struct {\n\tpartialInserts bool\n\tdialect        string\n\tmapper         *reflectx.Mapper\n\tquoter         quoter\n\tnow            func() time.Time\n}\n\ntype CreateOpts func(*createOpts)\n\n// WithPartialInserts allows to insert only the models that do not conflict with\n// an existing record. WithPartialInserts will also generate the IDs for the\n// models before inserting them, so that the successful inserts can be correlated\n// with the input models.\n//\n// In particular, WithPartialInserts does not work with MySQL, because it does\n// not support the \"RETURNING\" clause.\n//\n// WithPartialInserts does not work with CockroachDB and gen_random_uuid(),\n// because then the successful inserts cannot be correlated with the input\n// models. Note: gen_random_uuid() will skip the UNIQUE constraint check, which\n// needs to hit all regions in a distributed setup. Therefore, WithPartialInserts\n// should not be used to insert models for only a single identity.\nvar WithPartialInserts CreateOpts = func(o *createOpts) {\n\to.partialInserts = true\n}\n\nfunc newCreateOpts(conn *pop.Connection, opts ...CreateOpts) *createOpts {\n\to := new(createOpts)\n\to.dialect = conn.Dialect.Name()\n\to.mapper = conn.TX.Mapper\n\to.quoter = conn.Dialect.(quoter)\n\to.now = func() time.Time { return time.Now().UTC().Truncate(time.Microsecond) }\n\tfor _, f := range opts {\n\t\tf(o)\n\t}\n\treturn o\n}\n\n// Create batch-inserts the given models into the database using a single INSERT\n// statement. By default, the models are either all created or none. If\n// [WithPartialInserts] is passed as an option, partial inserts are supported,\n// and the models that could not be inserted are returned in an\n// [PartialConflictError].\nfunc Create[T any](ctx context.Context, p *TracerConnection, models []*T, opts ...CreateOpts) (err error) {\n\tctx, span := p.Tracer.Tracer().Start(ctx, \"persistence.sql.batch.Create\",\n\t\ttrace.WithAttributes(attribute.Int(\"count\", len(models))))\n\tdefer otelx.End(span, &err)\n\n\tif len(models) == 0 {\n\t\treturn nil\n\t}\n\n\tvar v T\n\tmodel := pop.NewModel(v, ctx)\n\n\tconn := p.Connection\n\toptions := newCreateOpts(conn, opts...)\n\n\tqueryArgs := buildInsertQueryArgs(ctx, models, options)\n\tvalues, err := buildInsertQueryValues(queryArgs.Columns, models, options)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar returningClause string\n\tif conn.Dialect.Name() != dbal.DriverMySQL {\n\t\t// PostgreSQL, CockroachDB, SQLite support RETURNING.\n\t\tif options.partialInserts {\n\t\t\treturningClause = fmt.Sprintf(\"ON CONFLICT DO NOTHING RETURNING %s\", model.IDField())\n\t\t} else {\n\t\t\treturningClause = fmt.Sprintf(\"RETURNING %s\", model.IDField())\n\t\t}\n\t}\n\n\tquery := conn.Dialect.TranslateSQL(fmt.Sprintf(\n\t\t\"INSERT INTO %s (%s) VALUES\\n%s\\n%s\",\n\t\tqueryArgs.TableName,\n\t\tqueryArgs.ColumnsDecl,\n\t\tqueryArgs.Placeholders,\n\t\treturningClause,\n\t))\n\n\trows, err := conn.TX.QueryContext(ctx, query, values...)\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\tdefer func() { _ = rows.Close() }()\n\n\t// MySQL, which does not support RETURNING, also does not have ON CONFLICT DO\n\t// NOTHING, meaning that MySQL will always fail the whole transaction on a single\n\t// record conflict.\n\tif conn.Dialect.Name() == dbal.DriverMySQL {\n\t\treturn nil\n\t}\n\n\tif options.partialInserts {\n\t\treturn handlePartialInserts(queryArgs, values, models, rows)\n\t} else {\n\t\treturn handleFullInserts(models, rows)\n\t}\n}\n\nfunc handleFullInserts[T any](models []*T, rows *sql.Rows) error {\n\t// Hydrate the models from the RETURNING clause.\n\tfor i := 0; rows.Next(); i++ {\n\t\tvar id uuid.UUID\n\t\tif err := rows.Scan(&id); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tif err := setModelID(id, models[i]); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\n\treturn nil\n}\n\nfunc handlePartialInserts[T any](queryArgs insertQueryArgs, values []any, models []*T, rows *sql.Rows) error {\n\t// Hydrate the models from the RETURNING clause.\n\tidsInDB := make(map[uuid.UUID]struct{})\n\tfor rows.Next() {\n\t\tvar id uuid.UUID\n\t\tif err := rows.Scan(&id); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tidsInDB[id] = struct{}{}\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\n\tidIdx := slices.Index(queryArgs.Columns, \"id\")\n\tif idIdx == -1 {\n\t\treturn errors.New(\"id column not found\")\n\t}\n\tvar idValues []uuid.UUID\n\tfor i := idIdx; i < len(values); i += len(queryArgs.Columns) {\n\t\tidValues = append(idValues, values[i].(uuid.UUID))\n\t}\n\n\tvar partialConflictError PartialConflictError[T]\n\tfor i, id := range idValues {\n\t\tif _, ok := idsInDB[id]; !ok {\n\t\t\tpartialConflictError.Failed = append(partialConflictError.Failed, models[i])\n\t\t} else {\n\t\t\tif err := setModelID(id, models[i]); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn partialConflictError.ErrOrNil()\n}\n\n// setModelID sets the id field of the model to the id.\nfunc setModelID(id uuid.UUID, model any) error {\n\tel := reflect.ValueOf(model).Elem()\n\tidField := el.FieldByName(\"ID\")\n\tif !idField.IsValid() {\n\t\treturn errors.New(\"model does not have a field named id\")\n\t}\n\tidField.Set(reflect.ValueOf(id))\n\n\treturn nil\n}\n"
  },
  {
    "path": "persistence/sql/batch/create_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage batch\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/jmoiron/sqlx/reflectx\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\ntype (\n\ttestModel struct {\n\t\tID          uuid.UUID       `db:\"id\"`\n\t\tNID         uuid.UUID       `db:\"nid\"`\n\t\tString      string          `db:\"string\"`\n\t\tInt         int             `db:\"int\"`\n\t\tTraits      identity.Traits `db:\"traits\"`\n\t\tNullTimePtr *sqlxx.NullTime `db:\"null_time_ptr\"`\n\t\tCreatedAt   time.Time       `json:\"created_at\" db:\"created_at\"`\n\t\tUpdatedAt   time.Time       `json:\"updated_at\" db:\"updated_at\"`\n\t}\n\ttestQuoter struct{}\n)\n\nfunc (i testModel) TableName(ctx context.Context) string {\n\treturn \"test_models\"\n}\n\nfunc (tq testQuoter) Quote(s string) string { return fmt.Sprintf(\"%q\", s) }\n\nfunc makeModels[T any]() []*T {\n\tmodels := make([]*T, 10)\n\tfor k := range models {\n\t\tmodels[k] = new(T)\n\t}\n\treturn models\n}\n\nfunc Test_buildInsertQueryArgs(t *testing.T) {\n\tctx := context.Background()\n\tt.Run(\"case=testModel\", func(t *testing.T) {\n\t\tmodels := makeModels[testModel]()\n\t\topts := &createOpts{\n\t\t\tdialect: \"other\",\n\t\t\tquoter:  testQuoter{},\n\t\t\tmapper:  reflectx.NewMapper(\"db\")}\n\t\targs := buildInsertQueryArgs(ctx, models, opts)\n\t\tsnapshotx.SnapshotT(t, args)\n\n\t\tquery := fmt.Sprintf(\"INSERT INTO %s (%s) VALUES\\n%s\", args.TableName, args.ColumnsDecl, args.Placeholders)\n\t\tassert.Equal(t, `INSERT INTO \"test_models\" (\"created_at\", \"id\", \"int\", \"nid\", \"null_time_ptr\", \"string\", \"traits\", \"updated_at\") VALUES\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?)`, query)\n\t})\n\n\tt.Run(\"case=Identities\", func(t *testing.T) {\n\t\tmodels := makeModels[identity.Identity]()\n\t\topts := &createOpts{\n\t\t\tdialect: \"other\",\n\t\t\tquoter:  testQuoter{},\n\t\t\tmapper:  reflectx.NewMapper(\"db\")}\n\t\targs := buildInsertQueryArgs(ctx, models, opts)\n\t\tsnapshotx.SnapshotT(t, args)\n\t})\n\n\tt.Run(\"case=RecoveryAddress\", func(t *testing.T) {\n\t\tmodels := makeModels[identity.RecoveryAddress]()\n\t\topts := &createOpts{\n\t\t\tdialect: \"other\",\n\t\t\tquoter:  testQuoter{},\n\t\t\tmapper:  reflectx.NewMapper(\"db\")}\n\t\targs := buildInsertQueryArgs(ctx, models, opts)\n\t\tsnapshotx.SnapshotT(t, args)\n\t})\n\n\tt.Run(\"case=RecoveryAddress\", func(t *testing.T) {\n\t\tmodels := makeModels[identity.RecoveryAddress]()\n\t\topts := &createOpts{\n\t\t\tdialect: \"other\",\n\t\t\tquoter:  testQuoter{},\n\t\t\tmapper:  reflectx.NewMapper(\"db\")}\n\t\targs := buildInsertQueryArgs(ctx, models, opts)\n\t\tsnapshotx.SnapshotT(t, args)\n\t})\n\n\tt.Run(\"case=cockroach\", func(t *testing.T) {\n\t\tmodels := makeModels[testModel]()\n\t\tfor k := range models {\n\t\t\tif k%3 == 0 {\n\t\t\t\tmodels[k].ID = uuid.FromStringOrNil(fmt.Sprintf(\"ae0125a9-2786-4ada-82d2-d169cf75047%d\", k))\n\t\t\t}\n\t\t}\n\t\topts := &createOpts{\n\t\t\tdialect: dbal.DriverCockroachDB,\n\t\t\tquoter:  testQuoter{},\n\t\t\tmapper:  reflectx.NewMapper(\"db\")}\n\t\targs := buildInsertQueryArgs(ctx, models, opts)\n\t\tsnapshotx.SnapshotT(t, args)\n\t})\n}\n\nfunc Test_buildInsertQueryValues(t *testing.T) {\n\tt.Run(\"case=testModel\", func(t *testing.T) {\n\t\tmodel := &testModel{\n\t\t\tString: \"string\",\n\t\t\tInt:    42,\n\t\t\tTraits: []byte(`{\"foo\": \"bar\"}`),\n\t\t}\n\n\t\tfrozenTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)\n\t\topts := &createOpts{\n\t\t\tmapper: reflectx.NewMapper(\"db\"),\n\t\t\tquoter: testQuoter{},\n\t\t\tnow:    func() time.Time { return frozenTime },\n\t\t}\n\n\t\tt.Run(\"case=cockroach\", func(t *testing.T) {\n\t\t\topts.dialect = dbal.DriverCockroachDB\n\t\t\tvalues, err := buildInsertQueryValues(\n\t\t\t\t[]string{\"created_at\", \"updated_at\", \"id\", \"string\", \"int\", \"null_time_ptr\", \"traits\"},\n\t\t\t\t[]*testModel{model},\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\t\t\tsnapshotx.SnapshotT(t, values)\n\t\t})\n\n\t\tt.Run(\"case=others\", func(t *testing.T) {\n\t\t\topts.dialect = \"other\"\n\t\t\tvalues, err := buildInsertQueryValues(\n\t\t\t\t[]string{\"created_at\", \"updated_at\", \"id\", \"string\", \"int\", \"null_time_ptr\", \"traits\"},\n\t\t\t\t[]*testModel{model},\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, frozenTime, model.CreatedAt)\n\t\t\tassert.Equal(t, model.CreatedAt, values[0])\n\n\t\t\tassert.Equal(t, frozenTime, model.UpdatedAt)\n\t\t\tassert.Equal(t, model.UpdatedAt, values[1])\n\n\t\t\tassert.NotZero(t, model.ID)\n\t\t\tassert.Equal(t, model.ID, values[2])\n\n\t\t\tassert.Equal(t, model.String, values[3])\n\t\t\tassert.Equal(t, model.Int, values[4])\n\n\t\t\tassert.Nil(t, model.NullTimePtr)\n\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "persistence/sql/batch/test_persister.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage batch\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc TestPersister(ctx context.Context, tracer *otelx.Tracer, p persistence.Persister) func(t *testing.T) {\n\treturn func(t *testing.T) {\n\t\tt.Run(\"method=batch.Create\", func(t *testing.T) {\n\t\t\tident1 := identity.NewIdentity(\"\")\n\t\t\tident1.NID = p.NetworkID(ctx)\n\t\t\tident2 := identity.NewIdentity(\"\")\n\t\t\tident2.NID = p.NetworkID(ctx)\n\n\t\t\t// Create two identities\n\t\t\t_ = p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {\n\t\t\t\tconn := &TracerConnection{\n\t\t\t\t\tTracer:     tracer,\n\t\t\t\t\tConnection: tx,\n\t\t\t\t}\n\n\t\t\t\terr := Create(ctx, conn, []*identity.Identity{ident1, ident2})\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\treturn nil\n\t\t\t})\n\n\t\t\trequire.NotEqual(t, uuid.Nil, ident1.ID)\n\t\t\trequire.NotEqual(t, uuid.Nil, ident2.ID)\n\n\t\t\t// Create conflicting verifiable addresses\n\t\t\taddresses := []*identity.VerifiableAddress{{\n\t\t\t\tValue:      \"foo.1@bar.de\",\n\t\t\t\tIdentityID: ident1.ID,\n\t\t\t\tNID:        ident1.NID,\n\t\t\t}, {\n\t\t\t\tValue:      \"foo.2@bar.de\",\n\t\t\t\tIdentityID: ident1.ID,\n\t\t\t\tNID:        ident1.NID,\n\t\t\t}, {\n\t\t\t\tValue:      \"conflict@bar.de\",\n\t\t\t\tIdentityID: ident1.ID,\n\t\t\t\tNID:        ident1.NID,\n\t\t\t}, {\n\t\t\t\tValue:      \"foo.3@bar.de\",\n\t\t\t\tIdentityID: ident1.ID,\n\t\t\t\tNID:        ident1.NID,\n\t\t\t}, {\n\t\t\t\tValue:      \"conflict@bar.de\",\n\t\t\t\tIdentityID: ident1.ID,\n\t\t\t\tNID:        ident1.NID,\n\t\t\t}, {\n\t\t\t\tValue:      \"foo.4@bar.de\",\n\t\t\t\tIdentityID: ident1.ID,\n\t\t\t\tNID:        ident1.NID,\n\t\t\t}}\n\n\t\t\tt.Run(\"case=fails all without partial inserts\", func(t *testing.T) {\n\t\t\t\t_ = p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {\n\t\t\t\t\tconn := &TracerConnection{\n\t\t\t\t\t\tTracer:     tracer,\n\t\t\t\t\t\tConnection: tx,\n\t\t\t\t\t}\n\t\t\t\t\terr := Create(ctx, conn, addresses)\n\t\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrUniqueViolation)\n\t\t\t\t\tif partial := new(PartialConflictError[identity.VerifiableAddress]); errors.As(err, &partial) {\n\t\t\t\t\t\trequire.NoError(t, partial, \"expected no partial error\")\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=return partial error with partial inserts\", func(t *testing.T) {\n\t\t\t\t_ = p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {\n\t\t\t\t\tconn := &TracerConnection{\n\t\t\t\t\t\tTracer:     tracer,\n\t\t\t\t\t\tConnection: tx,\n\t\t\t\t\t}\n\n\t\t\t\t\terr := Create(ctx, conn, addresses, WithPartialInserts)\n\t\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrUniqueViolation)\n\n\t\t\t\t\tif conn.Connection.Dialect.Name() != dbal.DriverMySQL {\n\t\t\t\t\t\t// MySQL does not support partial errors.\n\t\t\t\t\t\tpartialErr := new(PartialConflictError[identity.VerifiableAddress])\n\t\t\t\t\t\trequire.ErrorAs(t, err, &partialErr)\n\t\t\t\t\t\tassert.Len(t, partialErr.Failed, 1)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "persistence/sql/devices/persister_devices.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage devices\n\nimport (\n\t\"context\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/popx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nvar _ session.DevicePersister = (*DevicePersister)(nil)\n\ntype DevicePersister struct {\n\tctxer contextx.Provider\n\tc     *pop.Connection\n\tnid   uuid.UUID\n}\n\nfunc NewPersister(r contextx.Provider, c *pop.Connection) *DevicePersister {\n\treturn &DevicePersister{\n\t\tctxer: r,\n\t\tc:     c,\n\t}\n}\n\nfunc (p *DevicePersister) NetworkID(ctx context.Context) uuid.UUID {\n\treturn p.ctxer.Contextualizer().Network(ctx, p.nid)\n}\n\nfunc (p DevicePersister) WithNetworkID(nid uuid.UUID) session.DevicePersister {\n\tp.nid = nid\n\treturn &p\n}\n\nfunc (p *DevicePersister) CreateDevice(ctx context.Context, d *session.Device) error {\n\td.NID = p.NetworkID(ctx)\n\treturn sqlcon.HandleError(popx.GetConnection(ctx, p.c.WithContext(ctx)).Create(d))\n}\n"
  },
  {
    "path": "persistence/sql/identity/persister_identity.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage identity\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"maps\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/otp\"\n\t\"github.com/ory/kratos/persistence/sql/batch\"\n\t\"github.com/ory/kratos/persistence/sql/update\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/crdbx\"\n\t\"github.com/ory/x/errorsx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/popx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nvar (\n\t_ identity.Pool           = new(IdentityPersister)\n\t_ identity.PrivilegedPool = new(IdentityPersister)\n)\n\ntype dependencies interface {\n\tschema.IdentitySchemaProvider\n\tidentity.ValidationProvider\n\tlogrusx.Provider\n\tconfig.Provider\n\tcontextx.Provider\n\totelx.Provider\n}\n\ntype IdentityPersister struct {\n\tr   dependencies\n\tc   *pop.Connection\n\tnid uuid.UUID\n}\n\nfunc NewPersister(r dependencies, c *pop.Connection) *IdentityPersister {\n\treturn &IdentityPersister{\n\t\tc: c,\n\t\tr: r,\n\t}\n}\n\nfunc (p *IdentityPersister) NetworkID(ctx context.Context) uuid.UUID {\n\treturn p.r.Contextualizer().Network(ctx, p.nid)\n}\n\nfunc (p IdentityPersister) WithNetworkID(nid uuid.UUID) identity.PrivilegedPool {\n\tp.nid = nid\n\treturn &p\n}\n\nfunc WithTransaction(ctx context.Context, tx *pop.Connection) context.Context {\n\treturn popx.WithTransaction(ctx, tx)\n}\n\nfunc (p *IdentityPersister) Transaction(ctx context.Context, callback func(ctx context.Context, connection *pop.Connection) error) error {\n\treturn popx.Transaction(ctx, p.c.WithContext(ctx), callback)\n}\n\nfunc (p *IdentityPersister) GetConnection(ctx context.Context) *pop.Connection {\n\treturn popx.GetConnection(ctx, p.c.WithContext(ctx))\n}\n\nfunc (p *IdentityPersister) ListVerifiableAddresses(ctx context.Context, page, itemsPerPage int) (a []identity.VerifiableAddress, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ListVerifiableAddresses\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Int(\"per_page\", itemsPerPage),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tif err := p.GetConnection(ctx).Where(\"nid = ?\", p.NetworkID(ctx)).Order(\"id DESC\").Paginate(page, x.MaxItemsPerPage(itemsPerPage)).All(&a); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn a, nil\n}\n\nfunc (p *IdentityPersister) ListRecoveryAddresses(ctx context.Context, page, itemsPerPage int) (a []identity.RecoveryAddress, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ListRecoveryAddresses\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Int(\"per_page\", itemsPerPage),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tif err := p.GetConnection(ctx).Where(\"nid = ?\", p.NetworkID(ctx)).Order(\"id DESC\").Paginate(page, x.MaxItemsPerPage(itemsPerPage)).All(&a); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn a, nil\n}\n\nfunc stringToLowerTrim(match string) string {\n\treturn strings.ToLower(strings.TrimSpace(match))\n}\n\nfunc NormalizeIdentifier(ct identity.CredentialsType, match string) string {\n\tswitch ct {\n\tcase identity.CredentialsTypeLookup:\n\t\t// lookup credentials are case-sensitive\n\t\treturn match\n\tcase identity.CredentialsTypeTOTP:\n\t\t// totp credentials are case-sensitive\n\t\treturn match\n\tcase identity.CredentialsTypeOIDC, identity.CredentialsTypeSAML:\n\t\t// OIDC credentials are case-sensitive\n\t\treturn match\n\tcase identity.CredentialsTypePassword, identity.CredentialsTypeCodeAuth, identity.CredentialsTypeWebAuthn:\n\t\treturn stringToLowerTrim(match)\n\tdefault:\n\t\treturn match\n\t}\n}\n\nfunc (p *IdentityPersister) FindIdentityByCredentialIdentifier(ctx context.Context, identifier string, caseSensitive bool, expand identity.Expandables) (_ *identity.Identity, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.FindIdentityByCredentialIdentifier\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tvar find struct {\n\t\tIdentityID uuid.UUID `db:\"identity_id\"`\n\t}\n\n\tif !caseSensitive {\n\t\tidentifier = NormalizeIdentifier(identity.CredentialsTypePassword, identifier)\n\t}\n\n\tnid := p.NetworkID(ctx)\n\tif err := p.GetConnection(ctx).RawQuery(`\nSELECT ic.identity_id\nFROM identity_credentials ic\nINNER JOIN identity_credential_identifiers ici\n\tON ic.id = ici.identity_credential_id\nWHERE ici.identifier = ?\nAND ic.nid = ?\nAND ici.nid = ?\nLIMIT 1`,\n\t\tidentifier,\n\t\tnid,\n\t\tnid,\n\t).First(&find); err != nil {\n\t\tif errors.Is(err, sql.ErrNoRows) {\n\t\t\treturn nil, sqlcon.HandleError(err)\n\t\t}\n\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn p.GetIdentity(ctx, find.IdentityID, expand)\n}\n\nfunc (p *IdentityPersister) FindByCredentialsIdentifier(ctx context.Context, ct identity.CredentialsType, match string) (_ *identity.Identity, _ *identity.Credentials, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.FindByCredentialsIdentifier\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tnid := p.NetworkID(ctx)\n\n\tvar find struct {\n\t\tIdentityID uuid.UUID `db:\"identity_id\"`\n\t}\n\n\t// Force case-insensitivity and trimming for identifiers\n\tmatch = NormalizeIdentifier(ct, match)\n\n\t// Fast path: if the credential type ID is a known constant, skip the join with\n\t// identity_credential_types. In the rare case the constant is stale (old\n\t// self-hosted deployments where IDs were dynamic), the query returns no rows and\n\t// we fall through to the join-based query below.\n\tvar found bool\n\tif typeID, ok := identity.ConstantCredentialsTypeToId[ct]; ok {\n\t\tif err := p.GetConnection(ctx).RawQuery(`\n\t\t\tSELECT\n\t\t\t\tic.identity_id\n\t\t\tFROM identity_credentials ic\n\t\t\t\t\tINNER JOIN identity_credential_identifiers ici\n\t\t\t\t\t\tON ic.id = ici.identity_credential_id AND ici.identity_credential_type_id = ?\n\t\t\tWHERE ici.identifier = ?\n\t\t\tAND ic.nid = ?\n\t\t\tAND ici.nid = ?\n\t\t\tLIMIT 1`, // pop doesn't understand how to add a limit clause to this query\n\t\t\ttypeID,\n\t\t\tmatch,\n\t\t\tnid,\n\t\t\tnid,\n\t\t).First(&find); err == nil {\n\t\t\tfound = true\n\t\t}\n\t}\n\n\tif !found {\n\t\tif err := p.GetConnection(ctx).RawQuery(`\n\t\t\tSELECT\n\t\t\t\tic.identity_id\n\t\t\tFROM identity_credentials ic\n\t\t\t\t\tINNER JOIN identity_credential_types ict\n\t\t\t\t\t\tON ic.identity_credential_type_id = ict.id\n\t\t\t\t\tINNER JOIN identity_credential_identifiers ici\n\t\t\t\t\t\tON ic.id = ici.identity_credential_id AND ici.identity_credential_type_id = ict.id\n\t\t\tWHERE ici.identifier = ?\n\t\t\tAND ic.nid = ?\n\t\t\tAND ici.nid = ?\n\t\t\tAND ict.name = ?\n\t\t\tLIMIT 1`, // pop doesn't understand how to add a limit clause to this query\n\t\t\tmatch,\n\t\t\tnid,\n\t\t\tnid,\n\t\t\tct,\n\t\t).First(&find); err != nil {\n\t\t\tif errors.Is(err, sql.ErrNoRows) {\n\t\t\t\treturn nil, nil, sqlcon.HandleError(err) // herodot.ErrNotFound.WithTrace(err).WithReasonf(`No identity matching credentials identifier \"%s\" could be found.`, match)\n\t\t\t}\n\n\t\t\treturn nil, nil, sqlcon.HandleError(err)\n\t\t}\n\t}\n\n\tspan.SetAttributes(attribute.String(\"identity.id\", find.IdentityID.String()))\n\n\ti, err := p.GetIdentityConfidential(ctx, find.IdentityID)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tcreds, ok := i.GetCredentials(ct)\n\tif !ok {\n\t\treturn nil, nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"The SQL adapter failed to return the appropriate credentials_type \\\"%s\\\". This is a bug in the code.\", ct))\n\t}\n\n\treturn i, creds, nil\n}\n\nfunc (p *IdentityPersister) FindIdentityByWebauthnUserHandle(ctx context.Context, userHandle []byte) (_ *identity.Identity, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.FindIdentityByWebauthnUserHandle\")\n\tdefer otelx.End(span, &err)\n\n\tvar id identity.Identity\n\n\tvar jsonPath string\n\tcon := p.GetConnection(ctx)\n\tswitch con.Dialect.Name() {\n\tcase \"sqlite\", \"mysql\":\n\t\tjsonPath = \"$.user_handle\"\n\tdefault:\n\t\tjsonPath = \"user_handle\"\n\t}\n\n\tcolumns := popx.DBColumns[identity.Identity](&popx.AliasQuoter{Alias: \"identities\", Quoter: con.Dialect})\n\n\tif err := con.RawQuery(fmt.Sprintf(`\nSELECT %s\nFROM identities\nINNER JOIN identity_credentials\n    ON  identities.id = identity_credentials.identity_id\n    AND identities.nid = identity_credentials.nid\n    AND identity_credentials.identity_credential_type_id = (\n        SELECT id\n        FROM identity_credential_types\n        WHERE name = ?\n     )\nWHERE identity_credentials.config ->> '%s' = ? AND identity_credentials.config ->> '%s' IS NOT NULL\n  AND identities.nid = ?\nLIMIT 1`, columns,\n\t\tjsonPath, jsonPath),\n\t\tidentity.CredentialsTypeWebAuthn,\n\t\tbase64.StdEncoding.EncodeToString(userHandle),\n\t\tp.NetworkID(ctx),\n\t).First(&id); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &id, nil\n}\n\nfunc (p *IdentityPersister) createIdentityCredentials(ctx context.Context, identities ...*identity.Identity) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.createIdentityCredentials\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Int(\"num_identities\", len(identities)),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tvar (\n\t\tnid         = p.NetworkID(ctx)\n\t\ttraceConn   = &batch.TracerConnection{Tracer: p.r.Tracer(ctx), Connection: p.GetConnection(ctx)}\n\t\tcredentials []*identity.Credentials\n\t\tidentifiers []*identity.CredentialIdentifier\n\t)\n\n\tvar opts []batch.CreateOpts\n\tif len(identities) > 1 {\n\t\topts = append(opts, batch.WithPartialInserts)\n\t}\n\n\tfor _, ident := range identities {\n\t\tfor k := range ident.Credentials {\n\t\t\tcred := ident.Credentials[k]\n\n\t\t\tif len(cred.Config) == 0 {\n\t\t\t\tcred.Config = sqlxx.JSONRawMessage(\"{}\")\n\t\t\t}\n\n\t\t\tct, err := FindIdentityCredentialsTypeByName(p.GetConnection(ctx), cred.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tcred.ID, err = uuid.NewV4()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcred.IdentityID = ident.ID\n\t\t\tcred.NID = nid\n\t\t\tcred.IdentityCredentialTypeID = ct\n\t\t\tcredentials = append(credentials, &cred)\n\n\t\t\tident.Credentials[k] = cred\n\t\t}\n\t}\n\tif err = batch.Create(ctx, traceConn, credentials, opts...); err != nil {\n\t\treturn err\n\t}\n\n\tfor _, cred := range credentials {\n\t\tfor _, identifier := range cred.Identifiers {\n\t\t\t// Force case-insensitivity and trimming for identifiers\n\t\t\tidentifier = NormalizeIdentifier(cred.Type, identifier)\n\n\t\t\tif identifier == \"\" {\n\t\t\t\treturn errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\n\t\t\t\t\t\"Unable to create identity credentials with missing or empty identifier.\"))\n\t\t\t}\n\n\t\t\tct, err := FindIdentityCredentialsTypeByName(p.GetConnection(ctx), cred.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tidentifiers = append(identifiers, &identity.CredentialIdentifier{\n\t\t\t\tIdentifier:                identifier,\n\t\t\t\tIdentityID:                new(cred.IdentityID),\n\t\t\t\tIdentityCredentialsID:     cred.ID,\n\t\t\t\tIdentityCredentialsTypeID: ct,\n\t\t\t\tNID:                       p.NetworkID(ctx),\n\t\t\t})\n\t\t}\n\t}\n\n\tif err = batch.Create(ctx, traceConn, identifiers, opts...); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (p *IdentityPersister) createVerifiableAddresses(ctx context.Context, conn *pop.Connection, identities ...*identity.Identity) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.createVerifiableAddresses\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Int(\"num_identities\", len(identities)),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\twork := make([]*identity.VerifiableAddress, 0, len(identities))\n\tfor _, id := range identities {\n\t\tfor i := range id.VerifiableAddresses {\n\t\t\twork = append(work, &id.VerifiableAddresses[i])\n\t\t}\n\t}\n\tvar opts []batch.CreateOpts\n\tif len(identities) > 1 {\n\t\topts = append(opts, batch.WithPartialInserts)\n\t}\n\n\treturn batch.Create(ctx, &batch.TracerConnection{Tracer: p.r.Tracer(ctx), Connection: conn}, work, opts...)\n}\n\ntype differ interface {\n\tSignature() string\n\tGetID() uuid.UUID\n}\n\nfunc updateAssociationWith[T differ](ctx context.Context, p *IdentityPersister, fromDatabase, updateTo []T,\n) (result []T, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.updateAssociationWith\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\ttoKeep, toCreate, toRemoveIDs := diffAssociations(fromDatabase, updateTo)\n\n\t// Subtle: we delete the old associations from the DB first, because else\n\t// they could cause UNIQUE constraints to fail on insert.\n\t// Foreign key cascade will take care of deleting dependent records.\n\tif len(toRemoveIDs) > 0 {\n\t\tif err := p.GetConnection(ctx).Where(\"id IN (?)\", toRemoveIDs).Where(\"nid = ?\", p.NetworkID(ctx)).Delete(new(T)); err != nil {\n\t\t\treturn nil, sqlcon.HandleError(err)\n\t\t}\n\t}\n\n\tif len(toCreate) > 0 {\n\t\tif err := batch.Create(ctx,\n\t\t\t&batch.TracerConnection{\n\t\t\t\tTracer:     p.r.Tracer(ctx),\n\t\t\t\tConnection: p.GetConnection(ctx),\n\t\t\t},\n\t\t\ttoCreate,\n\t\t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tresult = make([]T, 0, len(toKeep)+len(toCreate))\n\tfor _, v := range toKeep {\n\t\tresult = append(result, *v)\n\t}\n\tfor _, v := range toCreate {\n\t\tresult = append(result, *v)\n\t}\n\n\treturn result, nil\n}\n\nfunc updateAssociation[T differ](ctx context.Context, p *IdentityPersister, i *identity.Identity, inID []T,\n) (result []T, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.updateAssociation\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"identity.id\", i.ID),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tvar inDB []T\n\tif err := p.GetConnection(ctx).\n\t\tWhere(\"identity_id = ? AND nid = ?\", i.ID, p.NetworkID(ctx)).\n\t\tAll(&inDB); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn updateAssociationWith(ctx, p, inDB, inID)\n}\n\nfunc (p *IdentityPersister) updateCredentialsAssociation(ctx context.Context, identityID uuid.UUID, fromDatabase []identity.Credentials, updateTo []identity.Credentials) (result map[identity.CredentialsType]identity.Credentials, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.updateCredentialsAssociation\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"identity.id\", identityID),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tnid := p.NetworkID(ctx)\n\n\t// Normalize new credentials by ensuring IdentityID, NID, and identifiers are set before hashing\n\tfor i := range updateTo {\n\t\tupdateTo[i].IdentityID = identityID\n\t\tupdateTo[i].NID = nid\n\t\t// Normalize identifiers to match what's stored in the database (make a copy to avoid modifying original)\n\t\tnormalizedIdentifiers := make([]string, len(updateTo[i].Identifiers))\n\t\tfor j, identifier := range updateTo[i].Identifiers {\n\t\t\tnormalizedIdentifiers[j] = NormalizeIdentifier(updateTo[i].Type, identifier)\n\t\t}\n\t\tupdateTo[i].Identifiers = normalizedIdentifiers\n\t}\n\n\tcredsToKeep, newCreds, credsToDeleteIDs := diffAssociations(fromDatabase, updateTo)\n\n\tif len(credsToDeleteIDs) > 0 {\n\t\t// Delete the credential and its identifiers.\n\t\tconn := p.GetConnection(ctx)\n\t\tq := \"DELETE FROM identity_credentials WHERE nid = ? AND id IN (?)\"\n\t\tif conn.Dialect.Name() == \"cockroach\" {\n\t\t\tq = \"DELETE FROM identity_credentials@primary WHERE nid = ? AND id IN (?)\"\n\t\t}\n\t\tif err := conn.RawQuery(q, nid, credsToDeleteIDs).Exec(); err != nil {\n\t\t\treturn nil, sqlcon.HandleError(err)\n\t\t}\n\t}\n\n\t// Create new credentials that aren't already in the database\n\tcredsToCreate := make(map[identity.CredentialsType]identity.Credentials, len(newCreds))\n\tfor _, c := range newCreds {\n\t\tcredsToCreate[c.Type] = *c\n\t}\n\n\tif len(credsToCreate) > 0 {\n\t\tif err := p.createIdentityCredentials(ctx, &identity.Identity{\n\t\t\tID:          identityID,\n\t\t\tCredentials: credsToCreate,\n\t\t}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tresult = make(map[identity.CredentialsType]identity.Credentials, len(credsToKeep)+len(credsToCreate))\n\tfor _, c := range credsToKeep {\n\t\tresult[c.Type] = *c\n\t}\n\tmaps.Copy(result, credsToCreate)\n\n\treturn result, nil\n}\n\nfunc diffAssociations[T differ](fromDatabase, updateTo []T) (unchanged, toCreate []*T, toRemoveIDs []uuid.UUID) {\n\tnewAssocs := make(map[string]*T, len(updateTo))\n\toldAssocs := make(map[string]*T, len(fromDatabase))\n\tfor i, a := range updateTo {\n\t\tnewAssocs[a.Signature()] = &updateTo[i]\n\t}\n\tfor i, a := range fromDatabase {\n\t\toldAssocs[a.Signature()] = &fromDatabase[i]\n\t}\n\n\ttoRemoveIDs = make([]uuid.UUID, 0, len(fromDatabase))\n\ttoCreate = make([]*T, 0, len(updateTo))\n\tunchanged = make([]*T, 0, len(updateTo))\n\n\tfor h, a := range oldAssocs {\n\t\tif _, found := newAssocs[h]; found {\n\t\t\tdelete(newAssocs, h)\n\t\t\tunchanged = append(unchanged, a)\n\t\t} else {\n\t\t\ttoRemoveIDs = append(toRemoveIDs, (*a).GetID())\n\t\t}\n\t}\n\n\tfor _, a := range newAssocs {\n\t\ttoCreate = append(toCreate, a)\n\t}\n\n\treturn\n}\n\nfunc (p *IdentityPersister) normalizeAllAddressess(ctx context.Context, identities ...*identity.Identity) {\n\tfor _, id := range identities {\n\t\tp.normalizeRecoveryAddresses(ctx, id)\n\t\tp.normalizeVerifiableAddresses(ctx, id)\n\t}\n}\n\nfunc (p *IdentityPersister) normalizeVerifiableAddresses(ctx context.Context, id *identity.Identity) {\n\tfor k := range id.VerifiableAddresses {\n\t\tv := id.VerifiableAddresses[k]\n\n\t\tv.IdentityID = id.ID\n\t\tv.NID = p.NetworkID(ctx)\n\t\tv.Value = stringToLowerTrim(v.Value)\n\t\tv.Via = cmp.Or(v.Via, identity.AddressTypeEmail)\n\t\tif len(v.Status) == 0 {\n\t\t\tif v.Verified {\n\t\t\t\tv.Status = identity.VerifiableAddressStatusCompleted\n\t\t\t} else {\n\t\t\t\tv.Status = identity.VerifiableAddressStatusPending\n\t\t\t}\n\t\t}\n\n\t\t// If verified is true but no timestamp is set, we default to time.Now\n\t\tif v.Verified && (v.VerifiedAt == nil || time.Time(*v.VerifiedAt).IsZero()) {\n\t\t\tv.VerifiedAt = new(sqlxx.NullTime(time.Now()))\n\t\t}\n\t\tif !v.Verified {\n\t\t\tv.VerifiedAt = nil\n\t\t}\n\n\t\tid.VerifiableAddresses[k] = v\n\t}\n}\n\nfunc (p *IdentityPersister) normalizeRecoveryAddresses(ctx context.Context, id *identity.Identity) {\n\tfor k := range id.RecoveryAddresses {\n\t\tid.RecoveryAddresses[k].IdentityID = id.ID\n\t\tid.RecoveryAddresses[k].NID = p.NetworkID(ctx)\n\t\tid.RecoveryAddresses[k].Value = stringToLowerTrim(id.RecoveryAddresses[k].Value)\n\t\tid.RecoveryAddresses[k].Via = cmp.Or(id.RecoveryAddresses[k].Via, identity.AddressTypeEmail)\n\t}\n}\n\nfunc (p *IdentityPersister) createRecoveryAddresses(ctx context.Context, conn *pop.Connection, identities ...*identity.Identity) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.createRecoveryAddresses\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Int(\"num_identities\", len(identities)),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\t// https://go.dev/play/p/b1kU5Bme2Fr\n\twork := make([]*identity.RecoveryAddress, 0, len(identities))\n\tfor _, id := range identities {\n\t\tfor i := range id.RecoveryAddresses {\n\t\t\twork = append(work, &id.RecoveryAddresses[i])\n\t\t}\n\t}\n\n\tvar opts []batch.CreateOpts\n\tif len(identities) > 1 {\n\t\topts = append(opts, batch.WithPartialInserts)\n\t}\n\n\treturn batch.Create(ctx, &batch.TracerConnection{Tracer: p.r.Tracer(ctx), Connection: conn}, work, opts...)\n}\n\nfunc (p *IdentityPersister) CountIdentities(ctx context.Context) (n int64, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CountIdentities\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tcount, err := p.c.WithContext(ctx).Where(\"nid = ?\", p.NetworkID(ctx)).Count(new(identity.Identity))\n\tif err != nil {\n\t\treturn 0, sqlcon.HandleError(err)\n\t}\n\tspan.SetAttributes(attribute.Int(\"num_identities\", count))\n\treturn int64(count), nil\n}\n\nfunc (p *IdentityPersister) CreateIdentity(ctx context.Context, ident *identity.Identity) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateIdentity\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\treturn p.CreateIdentities(ctx, ident)\n}\n\nfunc (p *IdentityPersister) CreateIdentities(ctx context.Context, identities ...*identity.Identity) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateIdentities\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Int(\"identities.count\", len(identities)),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tfor _, ident := range identities {\n\t\tident.NID = p.NetworkID(ctx)\n\n\t\tif ident.SchemaID == \"\" {\n\t\t\tident.SchemaID = p.r.Config().DefaultIdentityTraitsSchemaID(ctx)\n\t\t}\n\n\t\tstateChangedAt := sqlxx.NullTime(time.Now())\n\t\tident.StateChangedAt = &stateChangedAt\n\t\tif ident.State == \"\" {\n\t\t\tident.State = identity.StateActive\n\t\t}\n\n\t\tif len(ident.Traits) == 0 {\n\t\t\tident.Traits = identity.Traits(\"{}\")\n\t\t}\n\n\t\tif err = p.InjectTraitsSchemaURL(ctx, ident); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err = p.validateIdentity(ctx, ident); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar succeededIDs []uuid.UUID\n\tvar partialErr *identity.CreateIdentitiesError\n\tif err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {\n\t\tconn := &batch.TracerConnection{\n\t\t\tTracer:     p.r.Tracer(ctx),\n\t\t\tConnection: tx,\n\t\t}\n\n\t\tsucceededIDs = make([]uuid.UUID, 0, len(identities))\n\t\tfailedIdentityIDs := make(map[uuid.UUID]struct{ created bool })\n\t\tpartialErr = nil\n\t\tcreatedIdentities := make([]*identity.Identity, 0, len(identities))\n\n\t\tvar opts []batch.CreateOpts\n\t\tif len(identities) > 1 {\n\t\t\topts = append(opts, batch.WithPartialInserts)\n\t\t}\n\t\tif err := batch.Create(ctx, conn, identities, opts...); err != nil {\n\t\t\tif partialErr := new(batch.PartialConflictError[identity.Identity]); errors.As(err, &partialErr) {\n\t\t\t\tfor _, k := range partialErr.Failed {\n\t\t\t\t\tfailedIdentityIDs[k.ID] = struct{ created bool }{false}\n\t\t\t\t}\n\n\t\t\t\t// Mark all created identities that were not in the failed list as created.\n\t\t\t\tfor _, ident := range identities {\n\t\t\t\t\tif _, ok := failedIdentityIDs[ident.ID]; !ok {\n\t\t\t\t\t\tcreatedIdentities = append(createdIdentities, ident)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t}\n\t\t} else {\n\t\t\t// If no errors occurred, we can safely assume all identities were created.\n\t\t\tcreatedIdentities = identities\n\t\t}\n\n\t\tp.normalizeAllAddressess(ctx, createdIdentities...)\n\n\t\tif err = p.createVerifiableAddresses(ctx, tx, createdIdentities...); err != nil {\n\t\t\tif partialErr := new(batch.PartialConflictError[identity.VerifiableAddress]); errors.As(err, &partialErr) {\n\t\t\t\tfor _, k := range partialErr.Failed {\n\t\t\t\t\tfailedIdentityIDs[k.IdentityID] = struct{ created bool }{true}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t}\n\t\t}\n\t\tif err = p.createRecoveryAddresses(ctx, tx, createdIdentities...); err != nil {\n\t\t\tif partialErr := new(batch.PartialConflictError[identity.RecoveryAddress]); errors.As(err, &partialErr) {\n\t\t\t\tfor _, k := range partialErr.Failed {\n\t\t\t\t\tfailedIdentityIDs[k.IdentityID] = struct{ created bool }{true}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t}\n\t\t}\n\t\tif err = p.createIdentityCredentials(ctx, createdIdentities...); err != nil {\n\t\t\tif partialErr := new(batch.PartialConflictError[identity.Credentials]); errors.As(err, &partialErr) {\n\t\t\t\tfor _, k := range partialErr.Failed {\n\t\t\t\t\tfailedIdentityIDs[k.IdentityID] = struct{ created bool }{true}\n\t\t\t\t}\n\t\t\t} else if partialErr := new(batch.PartialConflictError[identity.CredentialIdentifier]); errors.As(err, &partialErr) {\n\t\t\t\tfor _, k := range partialErr.Failed {\n\t\t\t\t\tcredID := k.IdentityCredentialsID\n\t\t\t\t\tfor _, ident := range identities {\n\t\t\t\t\t\tfor _, cred := range ident.Credentials {\n\t\t\t\t\t\t\tif cred.ID == credID {\n\t\t\t\t\t\t\t\tfailedIdentityIDs[ident.ID] = struct{ created bool }{true}\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}\n\t\t\t} else {\n\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t}\n\t\t}\n\n\t\t// If any of the batch inserts failed on conflict, let's delete the corresponding\n\t\t// identity and return a list of failed identities in the error.\n\t\tif len(failedIdentityIDs) > 0 {\n\t\t\tpartialErr = identity.NewCreateIdentitiesError(len(failedIdentityIDs))\n\t\t\tidsToBeRemoved := make([]uuid.UUID, 0, len(failedIdentityIDs))\n\n\t\t\tfor _, ident := range identities {\n\t\t\t\tif info, ok := failedIdentityIDs[ident.ID]; ok {\n\t\t\t\t\tpartialErr.AddFailedIdentity(ident, sqlcon.ErrUniqueViolation)\n\t\t\t\t\tif info.created {\n\t\t\t\t\t\tidsToBeRemoved = append(idsToBeRemoved, ident.ID)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsucceededIDs = append(succeededIDs, ident.ID)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Manually roll back by deleting the identities that were inserted before the\n\t\t\t// error occurred.\n\t\t\tif err := p.DeleteIdentities(ctx, idsToBeRemoved); err != nil {\n\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t} else {\n\t\t\t// No failures: report all identities as created.\n\t\t\tfor _, ident := range identities {\n\t\t\t\tsucceededIDs = append(succeededIDs, ident.ID)\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\t// Report succeeded identities as created.\n\tfor _, identID := range succeededIDs {\n\t\tspan.AddEvent(events.NewIdentityCreated(ctx, identID))\n\t}\n\n\treturn partialErr.ErrOrNil()\n}\n\nfunc (p *IdentityPersister) HydrateIdentityAssociations(ctx context.Context, i *identity.Identity, expand identity.Expandables) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.HydrateIdentityAssociations\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"identity.id\", i.ID),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tnid := p.NetworkID(ctx)\n\n\teg, ctx := errgroup.WithContext(ctx)\n\tif expand.Has(identity.ExpandFieldRecoveryAddresses) {\n\t\teg.Go(func() error {\n\t\t\t// We use WithContext to get a copy of the connection struct, which solves the race detector\n\t\t\t// from complaining incorrectly.\n\t\t\t//\n\t\t\t// https://github.com/ory/pop/issues/723\n\t\t\tif err := p.GetConnection(ctx).WithContext(ctx).\n\t\t\t\tWhere(\"identity_id = ? AND nid = ?\", i.ID, nid).\n\t\t\t\tOrder(\"id ASC\").\n\t\t\t\tAll(&i.RecoveryAddresses); err != nil {\n\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t}\n\n\tif expand.Has(identity.ExpandFieldVerifiableAddresses) {\n\t\teg.Go(func() error {\n\t\t\t// We use WithContext to get a copy of the connection struct, which solves the race detector\n\t\t\t// from complaining incorrectly.\n\t\t\t//\n\t\t\t// https://github.com/ory/pop/issues/723\n\t\t\tif err := p.GetConnection(ctx).WithContext(ctx).\n\t\t\t\tOrder(\"id ASC\").\n\t\t\t\tWhere(\"identity_id = ? AND nid = ?\", i.ID, nid).\n\t\t\t\tAll(&i.VerifiableAddresses); err != nil {\n\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t}\n\n\tif expand.Has(identity.ExpandFieldCredentials) {\n\t\teg.Go(func() (err error) {\n\t\t\t// We use WithContext to get a copy of the connection struct, which solves the race detector\n\t\t\t// from complaining incorrectly.\n\t\t\t//\n\t\t\t// https://github.com/ory/pop/issues/723\n\t\t\tcreds, err := QueryForCredentials(p.GetConnection(ctx).WithContext(ctx),\n\t\t\t\tWhere{\"identity_credentials.identity_id = ?\", []interface{}{i.ID}},\n\t\t\t\tWhere{\"identity_credentials.nid = ?\", []interface{}{nid}})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ti.Credentials = creds[i.ID]\n\t\t\treturn\n\t\t})\n\t}\n\n\tif err := eg.Wait(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := i.Validate(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := identity.UpgradeCredentials(i); err != nil {\n\t\treturn err\n\t}\n\n\treturn p.InjectTraitsSchemaURL(ctx, i)\n}\n\ntype queryCredentials struct {\n\tIdentifier string `db:\"cred_identifier\"`\n\tidentity.Credentials\n}\n\nfunc (queryCredentials) TableName() string {\n\treturn \"identity_credentials\"\n}\n\ntype Where struct {\n\tCondition string\n\tArgs      []interface{}\n}\n\n// QueryForCredentials queries for identity credentials with custom WHERE\n// clauses, returning the results resolved by the owning identity's UUID.\nfunc QueryForCredentials(con *pop.Connection, where ...Where) (credentialsPerIdentity map[uuid.UUID](map[identity.CredentialsType]identity.Credentials), err error) {\n\t// This query has been meticulously crafted to be as fast as possible.\n\t// If you touch it, you will likely introduce a performance regression.\n\tq := con.Select(\n\t\t\"COALESCE(identity_credential_identifiers.identifier, '') cred_identifier\",\n\t\t\"identity_credentials.id\",\n\t\t\"identity_credentials.identity_credential_type_id\",\n\t\t\"identity_credentials.identity_id\",\n\t\t\"identity_credentials.nid\",\n\t\t\"identity_credentials.config\",\n\t\t\"identity_credentials.version\",\n\t\t\"identity_credentials.created_at\",\n\t\t\"identity_credentials.updated_at\",\n\t).LeftJoin(identifiersTableNameWithIndexHint(con),\n\t\t\"identity_credential_identifiers.identity_credential_id = identity_credentials.id AND identity_credential_identifiers.nid = identity_credentials.nid\",\n\t)\n\tfor _, w := range where {\n\t\tq = q.Where(\"(\"+w.Condition+\")\", w.Args...)\n\t}\n\tvar results []queryCredentials\n\tif err := q.All(&results); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\t// assemble\n\tcredentialsPerIdentity = map[uuid.UUID](map[identity.CredentialsType]identity.Credentials){}\n\tfor _, res := range results {\n\n\t\tres.Type, err = FindIdentityCredentialsTypeByID(con, res.IdentityCredentialTypeID)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tcredentials, ok := credentialsPerIdentity[res.IdentityID]\n\t\tif !ok {\n\t\t\tcredentialsPerIdentity[res.IdentityID] = make(map[identity.CredentialsType]identity.Credentials)\n\t\t\tcredentials = credentialsPerIdentity[res.IdentityID]\n\t\t}\n\t\tidentifiers := credentials[res.Type].Identifiers\n\t\tif res.Identifier != \"\" {\n\t\t\tidentifiers = append(identifiers, res.Identifier)\n\t\t}\n\t\tif identifiers == nil {\n\t\t\tidentifiers = make([]string, 0)\n\t\t}\n\t\tres.Identifiers = identifiers\n\t\tcredentials[res.Type] = res.Credentials\n\t}\n\n\t// We need deterministic ordering for testing, but sorting in the\n\t// database can be expensive under certain circumstances.\n\tfor _, creds := range credentialsPerIdentity {\n\t\tfor k := range creds {\n\t\t\tsort.Strings(creds[k].Identifiers)\n\t\t}\n\t}\n\treturn credentialsPerIdentity, nil\n}\n\nfunc identifiersTableNameWithIndexHint(con *pop.Connection) string {\n\tici := \"identity_credential_identifiers\"\n\tswitch con.Dialect.Name() {\n\tcase \"cockroach\":\n\t\tici += \"@identity_credential_identifiers_ici_nid_i_idx\"\n\tcase \"sqlite3\":\n\t\tici += \" INDEXED BY identity_credential_identifiers_ici_nid_i_idx\"\n\tcase \"mysql\":\n\t\tici += \" USE INDEX(identity_credential_identifiers_ici_nid_i_idx)\"\n\tdefault:\n\t\t// good luck 🤷‍♂️\n\t}\n\treturn ici\n}\n\nfunc paginationAttributes(params *identity.ListIdentityParameters, paginator *keysetpagination.Paginator) []attribute.KeyValue {\n\tattrs := []attribute.KeyValue{\n\t\tattribute.StringSlice(\"expand\", params.Expand.ToEager()),\n\t\tattribute.Bool(\"use:credential_identifier_filter\", params.CredentialsIdentifier != \"\"),\n\t\tattribute.Bool(\"use:credential_identifier_similar_filter\", params.CredentialsIdentifierSimilar != \"\"),\n\t}\n\tif params.PagePagination != nil {\n\t\tattrs = append(attrs,\n\t\t\tattribute.Int(\"page\", params.PagePagination.Page),\n\t\t\tattribute.Int(\"per_page\", params.PagePagination.ItemsPerPage))\n\t} else {\n\t\tattrs = append(attrs,\n\t\t\tattribute.String(\"page_token\", paginator.Token().Encode()),\n\t\t\tattribute.Int(\"page_size\", paginator.Size()))\n\t}\n\treturn attrs\n}\n\n// getCredentialTypeIDs returns a map of credential types to their respective IDs.\n//\n// If a credential type is not found, an error is returned.\nfunc (p *IdentityPersister) getCredentialTypeIDs(ctx context.Context, credentialTypes []identity.CredentialsType) (map[identity.CredentialsType]uuid.UUID, error) {\n\tresult := map[identity.CredentialsType]uuid.UUID{}\n\n\tfor _, ct := range credentialTypes {\n\t\ttypeID, err := FindIdentityCredentialsTypeByName(p.GetConnection(ctx), ct)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresult[ct] = typeID\n\t}\n\n\treturn result, nil\n}\n\nfunc (p *IdentityPersister) ListIdentities(ctx context.Context, params identity.ListIdentityParameters) (_ []identity.Identity, nextPage *keysetpagination.Paginator, err error) {\n\tpaginator := keysetpagination.GetPaginator(append(\n\t\tparams.KeySetPagination,\n\t\tkeysetpagination.WithDefaultToken(identity.DefaultPageToken()),\n\t\tkeysetpagination.WithDefaultSize(250),\n\t\tkeysetpagination.WithColumn(\"id\", \"ASC\"))...)\n\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ListIdentities\", trace.WithAttributes(append(\n\t\tpaginationAttributes(&params, paginator),\n\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx)))...))\n\tdefer otelx.End(span, &err)\n\n\tif _, err := uuid.FromString(paginator.Token().Parse(\"id\")[\"id\"]); err != nil {\n\t\treturn nil, nil, errors.WithStack(x.PageTokenInvalid)\n\t}\n\n\tnid := p.NetworkID(ctx)\n\tvar is []identity.Identity\n\n\tif err = p.Transaction(ctx, func(ctx context.Context, con *pop.Connection) error {\n\t\tis = make([]identity.Identity, 0) // Make sure we reset this to 0 in case of retries.\n\t\tnextPage = nil\n\n\t\tif err := crdbx.SetTransactionReadOnly(con); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := crdbx.SetTransactionConsistency(con, params.ConsistencyLevel, p.r.Config().DefaultConsistencyLevel(ctx)); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tjoins := \"\"\n\t\twheres := \"identities.nid = ? AND identities.id > ?\"\n\t\targs := []any{nid, paginator.Token().Encode()}\n\t\tlimit := fmt.Sprintf(\"LIMIT %d\", paginator.Size()+1)\n\t\tif params.PagePagination != nil {\n\t\t\twheres = \"identities.nid = ?\"\n\t\t\targs = []any{nid}\n\t\t\tpaginator := pop.NewPaginator(params.PagePagination.Page+1, params.PagePagination.ItemsPerPage)\n\t\t\tlimit = fmt.Sprintf(\"LIMIT %d OFFSET %d\", paginator.PerPage, paginator.Offset)\n\t\t}\n\t\tidentifier := params.CredentialsIdentifier\n\t\tidentifierOperator := \"=\"\n\t\tif identifier == \"\" && params.CredentialsIdentifierSimilar != \"\" {\n\t\t\tidentifier = x.EscapeLikePattern(params.CredentialsIdentifierSimilar) + \"%\"\n\t\t\tidentifierOperator = \"LIKE\"\n\t\t}\n\n\t\tif len(identifier) > 0 {\n\t\t\ttypes, err := p.getCredentialTypeIDs(ctx, []identity.CredentialsType{\n\t\t\t\tidentity.CredentialsTypeWebAuthn,\n\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\tidentity.CredentialsTypeCodeAuth,\n\t\t\t\tidentity.CredentialsTypeOIDC,\n\t\t\t\tidentity.CredentialsTypeSAML,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// When filtering by credentials identifier, we most likely are looking for a username or email. It is therefore\n\t\t\t// important to normalize the identifier before querying the database.\n\n\t\t\tjoins = params.TransformStatement(`\n\t\t\tINNER JOIN identity_credentials ic ON ic.identity_id = identities.id AND ic.nid = identities.nid\n\t\t\tINNER JOIN identity_credential_identifiers ici ON ici.identity_credential_id = ic.id AND ici.nid = ic.nid\n`)\n\n\t\t\twheres += fmt.Sprintf(`\n\t\t\tAND ic.nid = ? AND ici.nid = ?\n\t\t\tAND ((ici.identity_credential_type_id IN (?, ?, ?) AND ici.identifier %s ?)\n              OR (ici.identity_credential_type_id IN (?, ?) AND ici.identifier %s ?))\n\t\t\t`, identifierOperator, identifierOperator)\n\t\t\targs = append(args,\n\t\t\t\tnid, nid,\n\t\t\t\ttypes[identity.CredentialsTypeWebAuthn], types[identity.CredentialsTypePassword], types[identity.CredentialsTypeCodeAuth],\n\t\t\t\tNormalizeIdentifier(identity.CredentialsTypePassword, identifier),\n\t\t\t\ttypes[identity.CredentialsTypeOIDC], types[identity.CredentialsTypeSAML],\n\t\t\t\tidentifier,\n\t\t\t)\n\t\t}\n\n\t\tif len(params.IdsFilter) > 0 {\n\t\t\twheres += `\n\t\t\t\tAND identities.id in (?)\n\t\t\t`\n\t\t\targs = append(args, params.IdsFilter)\n\t\t} else if !params.OrganizationID.IsNil() {\n\t\t\twheres += `\n\t\t\t\tAND identities.organization_id = ?\n\t\t\t`\n\t\t\targs = append(args, params.OrganizationID.String())\n\t\t}\n\n\t\tcolumns := popx.DBColumns[identity.Identity](&popx.AliasQuoter{Alias: \"identities\", Quoter: con.Dialect})\n\n\t\tquery := fmt.Sprintf(`\n\t\tSELECT DISTINCT %s\n\t\tFROM identities AS identities\n\t\t%s\n\t\tWHERE\n\t\t%s\n\t\tORDER BY identities.id ASC\n\t\t%s`,\n\t\t\tcolumns,\n\t\t\tjoins, wheres, limit)\n\n\t\tif err := con.RawQuery(query, args...).All(&is); err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\n\t\tif params.PagePagination == nil {\n\t\t\tis, nextPage = keysetpagination.Result(is, paginator)\n\t\t}\n\n\t\tif len(is) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tidentitiesByID := make(map[uuid.UUID]*identity.Identity, len(is))\n\t\tidentityIDs := make([]any, len(is))\n\t\tfor k := range is {\n\t\t\tidentitiesByID[is[k].ID] = &is[k]\n\t\t\tidentityIDs[k] = is[k].ID\n\t\t}\n\n\t\tfor _, e := range params.Expand {\n\t\t\tswitch e {\n\t\t\tcase identity.ExpandFieldCredentials:\n\t\t\t\tcreds, err := QueryForCredentials(con,\n\t\t\t\t\tWhere{\"identity_credentials.nid = ?\", []interface{}{nid}},\n\t\t\t\t\tWhere{\"identity_credentials.identity_id IN (?)\", identityIDs})\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tfor k := range is {\n\t\t\t\t\tis[k].Credentials = creds[is[k].ID]\n\t\t\t\t}\n\t\t\tcase identity.ExpandFieldVerifiableAddresses:\n\t\t\t\taddrs := make([]identity.VerifiableAddress, 0)\n\t\t\t\tif err := con.Where(\"identity_id IN (?)\", identityIDs).Where(\"nid = ?\", nid).Order(\"id\").All(&addrs); err != nil {\n\t\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t\t}\n\t\t\t\tfor _, addr := range addrs {\n\t\t\t\t\tidentitiesByID[addr.IdentityID].VerifiableAddresses = append(identitiesByID[addr.IdentityID].VerifiableAddresses, addr)\n\t\t\t\t}\n\t\t\tcase identity.ExpandFieldRecoveryAddresses:\n\t\t\t\taddrs := make([]identity.RecoveryAddress, 0)\n\t\t\t\tif err := con.Where(\"identity_id IN (?)\", identityIDs).Where(\"nid = ?\", nid).Order(\"id\").All(&addrs); err != nil {\n\t\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t\t}\n\t\t\t\tfor _, addr := range addrs {\n\t\t\t\t\tidentitiesByID[addr.IdentityID].RecoveryAddresses = append(identitiesByID[addr.IdentityID].RecoveryAddresses, addr)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tschemaCache := map[string]string{}\n\tfor k := range is {\n\t\ti := &is[k]\n\n\t\tif u, ok := schemaCache[i.SchemaID]; ok {\n\t\t\ti.SchemaURL = u\n\t\t} else {\n\t\t\tif err := p.InjectTraitsSchemaURL(ctx, i); err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t\tschemaCache[i.SchemaID] = i.SchemaURL\n\t\t}\n\n\t\tif err := i.Validate(); err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\n\t\tif err := identity.UpgradeCredentials(i); err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\n\t\tis[k] = *i\n\t}\n\n\treturn is, nextPage, nil\n}\n\nfunc (p *IdentityPersister) UpdateIdentityColumns(ctx context.Context, i *identity.Identity, columns ...string) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpdateIdentityColumns\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"identity.id\", i.ID),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tif err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {\n\t\t_, err := tx.Where(\"id = ? AND nid = ?\", i.ID, p.NetworkID(ctx)).UpdateQuery(i, columns...)\n\t\treturn sqlcon.HandleError(err)\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\tspan.AddEvent(events.NewIdentityUpdated(ctx, i.ID))\n\treturn nil\n}\n\nfunc (p *IdentityPersister) UpdateIdentity(ctx context.Context, i *identity.Identity, mods ...identity.UpdateIdentityModifier) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpdateIdentity\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"identity.id\", i.ID),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tif err := p.validateIdentity(ctx, i); err != nil {\n\t\treturn err\n\t}\n\n\to := identity.NewUpdateIdentityOptions(mods)\n\n\tspan.SetAttributes(attribute.Bool(\"update.minimize_diff\", o.FromDatabase() != nil))\n\n\ti.NID = p.NetworkID(ctx)\n\ti.UpdatedAt = time.Now().UTC().Truncate(time.Microsecond)\n\tif err := sqlcon.HandleError(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {\n\t\t// This returns \"ErrNoRows\" if the identity does not exist\n\t\tif err := update.Generic(WithTransaction(ctx, tx), tx, p.r.Tracer(ctx).Tracer(), i); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvar identityCreds map[identity.CredentialsType]identity.Credentials\n\t\tp.normalizeAllAddressess(ctx, i)\n\t\tif o.FromDatabase() != nil {\n\t\t\tif o.FromDatabase().ID != i.ID {\n\t\t\t\treturn errors.New(\"mismatched identity ID: this is a bug\")\n\t\t\t}\n\t\t\tvar err error\n\t\t\ti.RecoveryAddresses, err = updateAssociationWith(ctx, p, o.FromDatabase().RecoveryAddresses, i.RecoveryAddresses)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ti.VerifiableAddresses, err = updateAssociationWith(ctx, p, o.FromDatabase().VerifiableAddresses, i.VerifiableAddresses)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tidentityCreds = o.FromDatabase().Credentials\n\t\t} else {\n\t\t\ti.RecoveryAddresses, err = updateAssociation(ctx, p, i, i.RecoveryAddresses)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ti.VerifiableAddresses, err = updateAssociation(ctx, p, i, i.VerifiableAddresses)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tcreds, err := QueryForCredentials(tx,\n\t\t\t\tWhere{\"identity_credentials.identity_id = ?\", []interface{}{i.ID}},\n\t\t\t\tWhere{\"identity_credentials.nid = ?\", []interface{}{p.NetworkID(ctx)}})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif c, found := creds[i.ID]; found {\n\t\t\t\tidentityCreds = c\n\t\t\t}\n\t\t}\n\n\t\toldCredentials := make([]identity.Credentials, 0, len(identityCreds))\n\t\tfor _, cred := range identityCreds {\n\t\t\toldCredentials = append(oldCredentials, cred)\n\t\t}\n\n\t\t// Convert new credentials map to slice\n\t\tnewCredentials := make([]identity.Credentials, 0, len(i.Credentials))\n\t\tfor _, cred := range i.Credentials {\n\t\t\tnewCredentials = append(newCredentials, cred)\n\t\t}\n\n\t\ti.Credentials, err = p.updateCredentialsAssociation(ctx, i.ID, oldCredentials, newCredentials)\n\t\treturn err\n\t})); err != nil {\n\t\treturn err\n\t}\n\n\tspan.AddEvent(events.NewIdentityUpdated(ctx, i.ID))\n\treturn nil\n}\n\nfunc (p *IdentityPersister) DeleteIdentity(ctx context.Context, id uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteIdentity\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"identity.id\", id),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\ttableName := new(identity.Identity).TableName(ctx)\n\tif p.c.Dialect.Name() == \"cockroach\" {\n\t\ttableName += \"@primary\"\n\t}\n\tnid := p.NetworkID(ctx)\n\tcount, err := p.GetConnection(ctx).RawQuery(fmt.Sprintf(\"DELETE FROM %s WHERE id = ? AND nid = ?\", tableName),\n\t\tid,\n\t\tnid,\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\tif count == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\tspan.AddEvent(events.NewIdentityDeleted(ctx, id))\n\treturn nil\n}\n\nfunc (p *IdentityPersister) DeleteIdentities(ctx context.Context, ids []uuid.UUID) (err error) {\n\t// This function is only used internally to cleanup partially created identities,\n\t// when creating a batch of identities at once and some failed to be fully created.\n\t// This act should not be observable externally and thus we do not emit an event.\n\n\tif len(ids) == 0 {\n\t\treturn nil\n\t}\n\n\tstringIDs := make([]string, len(ids))\n\tfor k, id := range ids {\n\t\tstringIDs[k] = id.String()\n\t}\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteIdentites\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.StringSlice(\"identity.ids\", stringIDs),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tplaceholders := strings.TrimSuffix(strings.Repeat(\"?, \", len(ids)), \", \")\n\targs := make([]any, 0, len(ids)+1)\n\tfor _, id := range ids {\n\t\targs = append(args, id)\n\t}\n\targs = append(args, p.NetworkID(ctx))\n\n\ttableName := new(identity.Identity).TableName(ctx)\n\tif p.c.Dialect.Name() == \"cockroach\" {\n\t\ttableName += \"@primary\"\n\t}\n\tcount, err := p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %s WHERE id IN (%s) AND nid = ?\",\n\t\ttableName,\n\t\tplaceholders,\n\t),\n\t\targs...,\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\tif count != len(ids) {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\treturn nil\n}\n\nfunc (p *IdentityPersister) GetIdentity(ctx context.Context, id uuid.UUID, expand identity.Expandables) (_ *identity.Identity, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetIdentity\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"identity.id\", id),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx)),\n\t\t\tattribute.StringSlice(\"expand\", expand.ToEager())))\n\tdefer otelx.End(span, &err)\n\n\tvar i identity.Identity\n\tif err := p.GetConnection(ctx).Where(\"id = ? AND nid = ?\", id, p.NetworkID(ctx)).First(&i); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\tif err := p.HydrateIdentityAssociations(ctx, &i, expand); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &i, nil\n}\n\nfunc (p *IdentityPersister) GetIdentityConfidential(ctx context.Context, id uuid.UUID) (res *identity.Identity, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetIdentityConfidential\")\n\tdefer otelx.End(span, &err)\n\n\treturn p.GetIdentity(ctx, id, identity.ExpandEverything)\n}\n\nfunc (p *IdentityPersister) FindIdentityByExternalID(ctx context.Context, externalID string, expand identity.Expandables) (res *identity.Identity, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.FindIdentityByExternalID\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.String(\"identity.external_id\", externalID),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tvar i identity.Identity\n\tif err := p.GetConnection(ctx).Where(\"external_id = ? AND nid = ?\", externalID, p.NetworkID(ctx)).First(&i); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\tif err := p.HydrateIdentityAssociations(ctx, &i, expand); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &i, nil\n}\n\nfunc (p *IdentityPersister) FindVerifiableAddressByValue(ctx context.Context, via string, value string) (_ *identity.VerifiableAddress, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.FindVerifiableAddressByValue\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\totelx.End(span, &err)\n\n\tvar address identity.VerifiableAddress\n\tif err := p.GetConnection(ctx).Where(\"nid = ? AND via = ? AND value = ?\", p.NetworkID(ctx), via, stringToLowerTrim(value)).First(&address); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &address, nil\n}\n\nfunc (p *IdentityPersister) FindRecoveryAddressByValue(ctx context.Context, via, value string) (_ *identity.RecoveryAddress, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.FindRecoveryAddressByValue\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tvar address identity.RecoveryAddress\n\tif err := p.GetConnection(ctx).Where(\"nid = ? AND via = ? AND value = ?\", p.NetworkID(ctx), via, stringToLowerTrim(value)).First(&address); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &address, nil\n}\n\n// FindAllRecoveryAddressesForIdentityByRecoveryAddressValue returns all\n// recovery addresses for an identity if at least one of those addresses matches\n// the provided value.\nfunc (p *IdentityPersister) FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(ctx context.Context, anyRecoveryAddress string) (recoveryAddresses []identity.RecoveryAddress, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.FindAllRecoveryAddressesForIdentityByRecoveryAddressValue\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\t// SQL explanation:\n\t// 1. Find a row (`B`) with the value matching `anyRecoveryAddress`.\n\t//    This row has an identity id (`B.identity_id`).\n\t// 2. Find all rows (`A`) with this identity id.\n\t//    Meaning: find all recovery addresses for this identity.\n\t//    The result set includes the user provided address (`anyRecoveryAddress`).\n\t//    NOTE: Should we exclude from the result set the login address for more security?\n\t//\n\t// This is all done in one query with a self-join.\n\t// We also bound the results for safety.\n\terr = p.GetConnection(ctx).RawQuery(\n\t\t`\nSELECT A.id, A.via, A.value, A.identity_id, A.created_at, A.updated_at, A.nid\nFROM identity_recovery_addresses A\nJOIN identity_recovery_addresses B\nON A.identity_id = B.identity_id\nAND A.nid = B.nid\nWHERE B.value = ?\nAND A.nid = ?\nLIMIT 10\n\t\t`,\n\t\tstringToLowerTrim(anyRecoveryAddress),\n\t\tp.NetworkID(ctx),\n\t).\n\t\tAll(&recoveryAddresses)\n\tif err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\treturn recoveryAddresses, nil\n}\n\nfunc (p *IdentityPersister) VerifyAddress(ctx context.Context, code string) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.VerifyAddress\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tnewCode, err := otp.New()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcount, err := p.GetConnection(ctx).RawQuery(\n\t\t// #nosec G201 -- TableName is static\n\t\tfmt.Sprintf(\n\t\t\t\"UPDATE %s SET status = ?, verified = true, verified_at = ?, code = ? WHERE nid = ? AND code = ? AND expires_at > ?\",\n\t\t\tnew(identity.VerifiableAddress).TableName(),\n\t\t),\n\t\tidentity.VerifiableAddressStatusCompleted,\n\t\ttime.Now().UTC().Round(time.Second),\n\t\tnewCode,\n\t\tp.NetworkID(ctx),\n\t\tcode,\n\t\ttime.Now().UTC(),\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\n\tif count == 0 {\n\t\treturn sqlcon.HandleError(sqlcon.ErrNoRows)\n\t}\n\n\treturn nil\n}\n\nfunc (p *IdentityPersister) UpdateVerifiableAddress(ctx context.Context, address *identity.VerifiableAddress, updateColumns ...string) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpdateVerifiableAddress\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"identity.id\", address.IdentityID),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx)),\n\t\t\tattribute.StringSlice(\"columns\", updateColumns)))\n\tdefer otelx.End(span, &err)\n\n\taddress.NID = p.NetworkID(ctx)\n\taddress.Value = stringToLowerTrim(address.Value)\n\treturn update.Generic(ctx, p.GetConnection(ctx), p.r.Tracer(ctx).Tracer(), address, updateColumns...)\n}\n\nfunc (p *IdentityPersister) validateIdentity(ctx context.Context, i *identity.Identity) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.validateIdentity\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.Stringer(\"identity.id\", i.ID),\n\t\t\tattribute.Stringer(\"network.id\", p.NetworkID(ctx))))\n\tdefer otelx.End(span, &err)\n\n\tif err := p.r.IdentityValidator().ValidateWithRunner(ctx, i); err != nil {\n\t\tif _, ok := errorsx.Cause(err).(*jsonschema.ValidationError); ok {\n\t\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"%s\", err))\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (p *IdentityPersister) InjectTraitsSchemaURL(ctx context.Context, i *identity.Identity) (err error) {\n\t// This trace is more noisy than it's worth in diagnostic power.\n\t// ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.InjectTraitsSchemaURL\")\n\t// defer otelx.End(span, &err)\n\n\tss, err := p.r.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\ts, err := ss.GetByID(i.SchemaID)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\n\t\t\t`The JSON Schema \"%s\" for this identity's traits could not be found.`, i.SchemaID))\n\t}\n\ti.SchemaURL = s.SchemaURL(p.r.Config().SelfPublicURL(ctx)).String()\n\treturn nil\n}\n\nvar (\n\tcredentialTypesID   = sync.Map{}\n\tcredentialTypesName = sync.Map{}\n)\n\nfunc FindIdentityCredentialsTypeByID(con *pop.Connection, id uuid.UUID) (identity.CredentialsType, error) {\n\tfor name, cid := range identity.ConstantCredentialsTypeToId {\n\t\tif id.String() == cid {\n\t\t\treturn name, nil\n\t\t}\n\t}\n\n\tresult, found := credentialTypesID.Load(id)\n\tif !found {\n\t\tif err := loadCredentialTypes(con); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tresult, found = credentialTypesID.Load(id)\n\t}\n\n\tif !found {\n\t\treturn \"\", errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"The SQL adapter failed to return the appropriate credentials_type for id %q. This is a bug in the code.\", id))\n\t}\n\n\treturn result.(identity.CredentialsType), nil\n}\n\nfunc FindIdentityCredentialsTypeByName(con *pop.Connection, ct identity.CredentialsType) (uuid.UUID, error) {\n\tid, ok := identity.ConstantCredentialsTypeToId[ct]\n\tif ok {\n\t\treturn uuid.Must(uuid.FromString(id)), nil\n\t}\n\n\tresult, found := credentialTypesName.Load(ct)\n\tif !found {\n\t\tif err := loadCredentialTypes(con); err != nil {\n\t\t\treturn uuid.Nil, err\n\t\t}\n\n\t\tresult, found = credentialTypesName.Load(ct)\n\t}\n\n\tif !found {\n\t\treturn uuid.Nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"The SQL adapter failed to return the appropriate credentials_type for name %q. This is a bug in the code.\", ct))\n\t}\n\n\treturn result.(uuid.UUID), nil\n}\n\nfunc loadCredentialTypes(con *pop.Connection) (err error) {\n\tctx, span := trace.SpanFromContext(con.Context()).TracerProvider().Tracer(\"\").Start(con.Context(), \"persistence.sql.identity.loadCredentialTypes\")\n\tdefer otelx.End(span, &err)\n\t_ = ctx\n\n\tvar tt []identity.CredentialsTypeTable\n\tif err := con.WithContext(ctx).All(&tt); err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\n\tfor _, t := range tt {\n\t\tcredentialTypesID.Store(t.ID, t.Name)\n\t\tcredentialTypesName.Store(t.Name, t.ID)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/0149ce5f-76a8-4efe-b2e3-431b8c6cceb6.json",
    "content": "{\n  \"id\": \"0149ce5f-76a8-4efe-b2e3-431b8c6cceb6\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"bazbar@ory.sh\"\n  },\n  \"metadata_public\": {\n    \"foo\": \"bar\"\n  },\n  \"metadata_admin\": {\n    \"baz\": \"bar\"\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/0149ce5f-76a8-4efe-b2e3-431b8c6cceb7.json",
    "content": "{\n  \"id\": \"0149ce5f-76a8-4efe-b2e3-431b8c6cceb7\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"bazbarbar@ory.sh\"\n  },\n  \"metadata_public\": {\n    \"foo\": \"bar\"\n  },\n  \"metadata_admin\": {\n    \"baz\": \"bar\"\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/196d8c1e-4f04-40f0-94b3-5ec43996b28a.json",
    "content": "{\n  \"id\": \"196d8c1e-4f04-40f0-94b3-5ec43996b28a\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"foobar@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/28ff0031-190b-4253-bd15-14308dec013e.json",
    "content": "{\n  \"id\": \"28ff0031-190b-4253-bd15-14308dec013e\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"bazbarbarfoo@ory.sh\"\n  },\n  \"metadata_public\": {\n    \"foo\": \"bar\"\n  },\n  \"metadata_admin\": {\n    \"baz\": \"bar\"\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/2ae6a5a7-2983-49e7-a4d8-7740b37c88cb.json",
    "content": "{\n  \"id\": \"2ae6a5a7-2983-49e7-a4d8-7740b37c88cb\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"d7b10@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/308929d3-41a2-43fe-a33c-75308539d841.json",
    "content": "{\n  \"id\": \"308929d3-41a2-43fe-a33c-75308539d841\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"bazbar@ory.sh\"\n  },\n  \"metadata_public\": {\n    \"foo\": \"bar\"\n  },\n  \"metadata_admin\": {\n    \"baz\": \"bar\"\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/359963ec-b09b-4ea0-aece-fb4dd95f304a.json",
    "content": "{\n  \"id\": \"359963ec-b09b-4ea0-aece-fb4dd95f304a\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"d7b11@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/5ff66179-c240-4703-b0d8-494592cefff5.json",
    "content": "{\n  \"id\": \"5ff66179-c240-4703-b0d8-494592cefff5\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"foo2@ory.sh\",\n        \"foo@ory.sh\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=131072,t=2,p=1$lQFPaKxXqPL56/mU7vRi4w$6aldHyBnURt8sP8+xu41Ng\"\n      },\n      \"version\": 0,\n      \"created_at\": \"2013-10-07T08:23:19Z\",\n      \"updated_at\": \"2013-10-07T08:23:19Z\"\n    },\n    \"totp\": {\n      \"type\": \"totp\",\n      \"identifiers\": [],\n      \"config\": {\n        \"totp_url\": \"otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP\\u0026issuer=Example\"\n      },\n      \"version\": 0,\n      \"created_at\": \"2013-10-07T08:23:19Z\",\n      \"updated_at\": \"2013-10-07T08:23:19Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"bazbar@ory.sh\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"45e867e9-2745-4f16-8dd4-84334a252b61\",\n      \"value\": \"foo@ory.sh\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"2013-10-07T08:23:19Z\",\n      \"updated_at\": \"2013-10-07T08:23:19Z\"\n    }\n  ],\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/a251ebc2-880c-4f76-a8f3-38e6940eab0e.json",
    "content": "{\n  \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"foo1@ory.sh\",\n        \"foobar@ory.sh\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=131072,t=2,p=1$lQFPaKxXqPL56/mU7vRi4w$6aldHyBnURt8sP8+xu41Ng\"\n      },\n      \"version\": 0,\n      \"created_at\": \"2013-10-07T08:23:19Z\",\n      \"updated_at\": \"2013-10-07T08:23:19Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"foobar@ory.sh\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n      \"value\": \"foobar+without-code@ory.sh\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"2013-10-07T08:23:19Z\",\n      \"updated_at\": \"2013-10-07T08:23:19Z\"\n    },\n    {\n      \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n      \"value\": \"foobar@ory.sh\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"2013-10-07T08:23:19Z\",\n      \"updated_at\": \"2013-10-07T08:23:19Z\"\n    },\n    {\n      \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n      \"value\": \"foobar+11345642c6c0@ory.sh\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"2013-10-07T08:23:19Z\",\n      \"updated_at\": \"2013-10-07T08:23:19Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n      \"value\": \"foobar@ory.sh\",\n      \"via\": \"email\",\n      \"created_at\": \"2013-10-07T08:23:19Z\",\n      \"updated_at\": \"2013-10-07T08:23:19Z\"\n    }\n  ],\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/d7b9addb-ac15-4bc2-9fa5-562e0bf48755.json",
    "content": "{\n  \"id\": \"d7b9addb-ac15-4bc2-9fa5-562e0bf48755\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"d7b9@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity/ed253b2c-48ed-4c58-9b6f-1dc963c30a66.json",
    "content": "{\n  \"id\": \"ed253b2c-48ed-4c58-9b6f-1dc963c30a66\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"bazbar@ory.sh\"\n  },\n  \"metadata_public\": null,\n  \"metadata_admin\": null,\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity_recovery_address/b8293f1c-010f-45d9-b809-f3fc5365ba80.json",
    "content": "{\n  \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n  \"value\": \"foobar@ory.sh\",\n  \"via\": \"email\",\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity_verification_address/45e867e9-2745-4f16-8dd4-84334a252b61.json",
    "content": "{\n  \"id\": \"45e867e9-2745-4f16-8dd4-84334a252b61\",\n  \"value\": \"foo@ory.sh\",\n  \"verified\": false,\n  \"via\": \"email\",\n  \"status\": \"pending\",\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity_verification_address/b2d59320-8564-4400-a39f-a22a497a23f1.json",
    "content": "{\n  \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n  \"value\": \"foobar+without-code@ory.sh\",\n  \"verified\": false,\n  \"via\": \"email\",\n  \"status\": \"pending\",\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity_verification_address/c2427b6d-312b-46d9-9285-536db7ae11fd.json",
    "content": "{\n  \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n  \"value\": \"foobar@ory.sh\",\n  \"verified\": false,\n  \"via\": \"email\",\n  \"status\": \"pending\",\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/identity_verification_address/d4718a67-aec2-418d-8173-6ebc7bde3b86.json",
    "content": "{\n  \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n  \"value\": \"foobar+11345642c6c0@ory.sh\",\n  \"verified\": false,\n  \"via\": \"email\",\n  \"status\": \"pending\",\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_code/bd292366-af32-4ba6-bdf0-11d6d1a217f3.json",
    "content": "{\n  \"id\": \"bd292366-af32-4ba6-bdf0-11d6d1a217f3\",\n  \"expires_at\": \"2022-08-18T08:28:18Z\",\n  \"issued_at\": \"2022-08-18T07:28:18Z\",\n  \"identity_id\": \"28ff0031-190b-4253-bd15-14308dec013e\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/00b1517f-2467-4aaf-b0a5-82b4a27dcaf5.json",
    "content": "{\n  \"id\": \"00b1517f-2467-4aaf-b0a5-82b4a27dcaf5\",\n  \"organization_id\": null,\n  \"oauth2_login_challenge\": \"challenge data\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/0bc96cc9-dda4-4700-9e42-35731f2af91e.json",
    "content": "{\n  \"id\": \"0bc96cc9-dda4-4700-9e42-35731f2af91e\",\n  \"organization_id\": null,\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/1fb23c75-b809-42cc-8984-6ca2d0a1192f.json",
    "content": "{\n  \"id\": \"1fb23c75-b809-42cc-8984-6ca2d0a1192f\",\n  \"organization_id\": null,\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/202c1981-1e25-47f0-8764-75ad506c2bec.json",
    "content": "{\n  \"id\": \"202c1981-1e25-47f0-8764-75ad506c2bec\",\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/349c945a-60f8-436a-a301-7a42c92604f9.json",
    "content": "{\n  \"id\": \"349c945a-60f8-436a-a301-7a42c92604f9\",\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login?login_challenge=3caddfd599034bce83ffcae36f42dff7\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/38caf592-b042-4551-b92f-8d5223c2a4e2.json",
    "content": "{\n  \"id\": \"38caf592-b042-4551-b92f-8d5223c2a4e2\",\n  \"organization_id\": null,\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/3a9ea34f-0f12-469b-9417-3ae5795a7baa.json",
    "content": "{\n  \"id\": \"3a9ea34f-0f12-469b-9417-3ae5795a7baa\",\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/43c99182-bb67-47e1-b564-bb23bd8d4393.json",
    "content": "{\n  \"id\": \"43c99182-bb67-47e1-b564-bb23bd8d4393\",\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login?prompt=login\\u0026return_to=http%3A%2F%2F127.0.0.1%3A4455%2F.ory%2Fkratos%2Fpublic%2Fself-service%2Fbrowser%2Fflows%2Fsettings%2Fstrategies%2Fprofile%3Frequest%3D74fd6c53-7651-453e-90b8-2c5adbf911bb\",\n  \"return_to\": \"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?request=74fd6c53-7651-453e-90b8-2c5adbf911bb\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": true,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/47edd3a8-0998-4779-9469-f4b8ee4430df.json",
    "content": "{\n  \"id\": \"47edd3a8-0998-4779-9469-f4b8ee4430df\",\n  \"organization_id\": null,\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/56d94e8b-8a5d-4b7f-8a6e-3259d2b2903e.json",
    "content": "{\n  \"id\": \"56d94e8b-8a5d-4b7f-8a6e-3259d2b2903e\",\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/6d387820-f2f4-4f9f-9980-a90d89e7811f.json",
    "content": "{\n  \"id\": \"6d387820-f2f4-4f9f-9980-a90d89e7811f\",\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/916ded11-aa64-4a27-b06e-96e221a509d7.json",
    "content": "{\n  \"id\": \"916ded11-aa64-4a27-b06e-96e221a509d7\",\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/99974ce6-388c-4669-a95a-7757ee724020.json",
    "content": "{\n  \"id\": \"99974ce6-388c-4669-a95a-7757ee724020\",\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/b1fac7fb-d016-4a06-a7fe-e4eab2a0429f.json",
    "content": "{\n  \"id\": \"b1fac7fb-d016-4a06-a7fe-e4eab2a0429f\",\n  \"organization_id\": null,\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/cccccccc-dda4-4700-9e42-35731f2af911.json",
    "content": "{\n  \"id\": \"cccccccc-dda4-4700-9e42-35731f2af911\",\n  \"organization_id\": null,\n  \"oauth2_login_challenge\": \"challenge data\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/cccccccc-dda4-4700-9e42-35731f2af91e.json",
    "content": "{\n  \"id\": \"cccccccc-dda4-4700-9e42-35731f2af91e\",\n  \"organization_id\": null,\n  \"oauth2_login_challenge\": \"challenge data\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/login_flow/d6aa1f23-88c9-4b9b-a850-392f48c7f9e8.json",
    "content": "{\n  \"id\": \"d6aa1f23-88c9-4b9b-a850-392f48c7f9e8\",\n  \"organization_id\": null,\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/login\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"created_at\": \"2013-10-07T08:23:19Z\",\n  \"updated_at\": \"2013-10-07T08:23:19Z\",\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_code/8f75f5d9-9cb4-4848-9a73-9344f686f8a6.json",
    "content": "{\n  \"id\": \"8f75f5d9-9cb4-4848-9a73-9344f686f8a6\",\n  \"recovery_address\": null,\n  \"expires_at\": \"2022-08-18T08:28:18Z\",\n  \"issued_at\": \"2022-08-18T07:28:18Z\",\n  \"identity_id\": \"308929d3-41a2-43fe-a33c-75308539d841\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_flow/0d14427f-e16d-43a5-8695-8278bf85d4eb.json",
    "content": "{\n  \"id\": \"0d14427f-e16d-43a5-8695-8278bf85d4eb\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_flow/13178936-095a-466b-abe0-36d977d3dc18.json",
    "content": "{\n  \"id\": \"13178936-095a-466b-abe0-36d977d3dc18\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_flow/4963f305-e874-4a68-8424-a00bec679e7b.json",
    "content": "{\n  \"id\": \"4963f305-e874-4a68-8424-a00bec679e7b\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/recovery\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_flow/68fb4010-84a9-4d1e-9f92-2705978ee891.json",
    "content": "{\n  \"id\": \"68fb4010-84a9-4d1e-9f92-2705978ee891\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_flow/68fb4010-84a9-4d1e-9f92-2705978ee89e.json",
    "content": "{\n  \"id\": \"68fb4010-84a9-4d1e-9f92-2705978ee89e\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/recovery\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_flow/87e871e1-a45f-4ed0-ba4e-a03063c774dc.json",
    "content": "{\n  \"id\": \"87e871e1-a45f-4ed0-ba4e-a03063c774dc\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_token/1b667e6d-8fda-4194-a765-08185185d7e4.json",
    "content": "{\n  \"id\": \"1b667e6d-8fda-4194-a765-08185185d7e4\",\n  \"recovery_address\": null,\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"identity_id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_token/5529d454-2946-404e-b681-d950f8657fd0.json",
    "content": "{\n  \"id\": \"5529d454-2946-404e-b681-d950f8657fd0\",\n  \"recovery_address\": null,\n  \"expires_at\": \"2000-01-01T00:00:00Z\",\n  \"issued_at\": \"2000-01-01T00:00:00Z\",\n  \"identity_id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/recovery_token/77ca3f5c-cd39-488b-9f1d-cc7166d14bdc.json",
    "content": "{\n  \"id\": \"77ca3f5c-cd39-488b-9f1d-cc7166d14bdc\",\n  \"recovery_address\": null,\n  \"expires_at\": \"2000-01-01T00:00:00Z\",\n  \"issued_at\": \"2000-01-01T00:00:00Z\",\n  \"identity_id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_code/f1f66a69-ce02-4a12-9591-9e02dda30a0d.json",
    "content": "{\n  \"id\": \"f1f66a69-ce02-4a12-9591-9e02dda30a0d\",\n  \"expires_at\": \"2022-08-18T08:28:18Z\",\n  \"issued_at\": \"2022-08-18T07:28:18Z\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/05a7f09d-4ef3-41fb-958a-6ad74584b36a.json",
    "content": "{\n  \"id\": \"05a7f09d-4ef3-41fb-958a-6ad74584b36a\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/22d58184-b97d-44a5-bbaf-0aa8b4000d81.json",
    "content": "{\n  \"id\": \"22d58184-b97d-44a5-bbaf-0aa8b4000d81\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/2bf132e0-5d40-4df9-9a11-9106e5333735.json",
    "content": "{\n  \"id\": \"2bf132e0-5d40-4df9-9a11-9106e5333735\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/696e7022-c466-44f6-89c6-8cf93c06a62a.json",
    "content": "{\n  \"id\": \"696e7022-c466-44f6-89c6-8cf93c06a62a\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/69c80296-36cd-4afc-921a-15369cac5bf0.json",
    "content": "{\n  \"id\": \"69c80296-36cd-4afc-921a-15369cac5bf0\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge=\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/87fa3f43-5155-42b4-a1ad-174c2595fdaf.json",
    "content": "{\n  \"id\": \"87fa3f43-5155-42b4-a1ad-174c2595fdaf\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/8ef215a9-e8d5-43b3-9aa3-cb4333562e36.json",
    "content": "{\n  \"id\": \"8ef215a9-e8d5-43b3-9aa3-cb4333562e36\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/8f32efdc-f6fc-4c27-a3c2-579d109eff60.json",
    "content": "{\n  \"id\": \"8f32efdc-f6fc-4c27-a3c2-579d109eff60\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/9edcf051-1cd0-44cc-bd2f-6ac21f0c24dd.json",
    "content": "{\n  \"id\": \"9edcf051-1cd0-44cc-bd2f-6ac21f0c24dd\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/e2150cdc-23ac-4940-a240-6c79c27ab029.json",
    "content": "{\n  \"id\": \"e2150cdc-23ac-4940-a240-6c79c27ab029\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/ef18b06e-4700-4021-9949-ef783cd86be1.json",
    "content": "{\n  \"id\": \"ef18b06e-4700-4021-9949-ef783cd86be1\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge=\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/ef18b06e-4700-4021-9949-ef783cd86be8.json",
    "content": "{\n  \"id\": \"ef18b06e-4700-4021-9949-ef783cd86be8\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration?login_challenge=\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/registration_flow/f1b5ed18-113a-4a98-aae7-d4eba007199c.json",
    "content": "{\n  \"id\": \"f1b5ed18-113a-4a98-aae7-d4eba007199c\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"password\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"organization_id\": null,\n  \"state\": \"\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/session/068f6bb6-d15f-436d-94f7-b3fd0489c9ef.json",
    "content": "{\n  \"id\": \"068f6bb6-d15f-436d-94f7-b3fd0489c9ef\",\n  \"active\": false,\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticated_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticator_assurance_level\": \"aal2\",\n  \"authentication_methods\": [\n    {\n      \"method\": \"password\",\n      \"aal\": \"\",\n      \"completed_at\": \"0001-01-01T00:00:00Z\"\n    },\n    {\n      \"method\": \"totp\",\n      \"aal\": \"\",\n      \"completed_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"identity\": {\n    \"id\": \"5ff66179-c240-4703-b0d8-494592cefff5\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"bazbar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"45e867e9-2745-4f16-8dd4-84334a252b61\",\n        \"value\": \"foo@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"devices\": []\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/session/7458af86-c1d8-401c-978a-8da89133f78b.json",
    "content": "{\n  \"id\": \"7458af86-c1d8-401c-978a-8da89133f78b\",\n  \"active\": true,\n  \"expires_at\": \"2080-10-07T08:23:19Z\",\n  \"authenticated_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticator_assurance_level\": \"aal2\",\n  \"authentication_methods\": [\n    {\n      \"method\": \"password\",\n      \"aal\": \"\",\n      \"completed_at\": \"0001-01-01T00:00:00Z\"\n    },\n    {\n      \"method\": \"totp\",\n      \"aal\": \"\",\n      \"completed_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"identity\": {\n    \"id\": \"5ff66179-c240-4703-b0d8-494592cefff5\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"bazbar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"45e867e9-2745-4f16-8dd4-84334a252b61\",\n        \"value\": \"foo@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"devices\": []\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/session/7458af86-c1d8-401c-978a-8da89133f98b.json",
    "content": "{\n  \"id\": \"7458af86-c1d8-401c-978a-8da89133f98b\",\n  \"active\": false,\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticated_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticator_assurance_level\": \"aal2\",\n  \"authentication_methods\": [\n    {\n      \"method\": \"password\",\n      \"aal\": \"\",\n      \"completed_at\": \"0001-01-01T00:00:00Z\"\n    },\n    {\n      \"method\": \"totp\",\n      \"aal\": \"\",\n      \"completed_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"identity\": {\n    \"id\": \"5ff66179-c240-4703-b0d8-494592cefff5\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"bazbar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"45e867e9-2745-4f16-8dd4-84334a252b61\",\n        \"value\": \"foo@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"devices\": [\n    {\n      \"id\": \"884f556e-eb3a-4b9f-bee3-11763642c6c0\",\n      \"ip_address\": \"54.155.246.232\",\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      \"location\": \"Munich, Germany\"\n    }\n  ]\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/session/8571e374-38f2-4f46-8ad3-b9d914e174d3.json",
    "content": "{\n  \"id\": \"8571e374-38f2-4f46-8ad3-b9d914e174d3\",\n  \"active\": false,\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticated_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticator_assurance_level\": \"aal1\",\n  \"authentication_methods\": [\n    {\n      \"method\": \"v0.6_legacy_session\",\n      \"aal\": \"\",\n      \"completed_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"identity\": {\n    \"id\": \"5ff66179-c240-4703-b0d8-494592cefff5\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"bazbar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"45e867e9-2745-4f16-8dd4-84334a252b61\",\n        \"value\": \"foo@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"devices\": []\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/session/dcde5aaa-f789-4d3d-ae1f-76da8d57e67c.json",
    "content": "{\n  \"id\": \"dcde5aaa-f789-4d3d-ae1f-76da8d57e67c\",\n  \"active\": false,\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticated_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticator_assurance_level\": \"aal1\",\n  \"authentication_methods\": [\n    {\n      \"method\": \"v0.6_legacy_session\",\n      \"aal\": \"\",\n      \"completed_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"identity\": {\n    \"id\": \"5ff66179-c240-4703-b0d8-494592cefff5\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"bazbar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"45e867e9-2745-4f16-8dd4-84334a252b61\",\n        \"value\": \"foo@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"devices\": []\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/session/f38cdebe-e567-42c9-a562-1bd4dee40998.json",
    "content": "{\n  \"id\": \"f38cdebe-e567-42c9-a562-1bd4dee40998\",\n  \"active\": false,\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticated_at\": \"2013-10-07T08:23:19Z\",\n  \"authenticator_assurance_level\": \"aal1\",\n  \"authentication_methods\": [\n    {\n      \"method\": \"v0.6_legacy_session\",\n      \"aal\": \"\",\n      \"completed_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"identity\": {\n    \"id\": \"5ff66179-c240-4703-b0d8-494592cefff5\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"bazbar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"45e867e9-2745-4f16-8dd4-84334a252b61\",\n        \"value\": \"foo@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"devices\": []\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/194c5b05-0487-4a11-bcbc-f301c9ff9678.json",
    "content": "{\n  \"id\": \"194c5b05-0487-4a11-bcbc-f301c9ff9678\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/19ede218-928c-4e02-ab49-b76e12b34f31.json",
    "content": "{\n  \"id\": \"19ede218-928c-4e02-ab49-b76e12b34f31\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"active\": \"profile\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/19ede218-928c-4e02-ab49-b76e12b34f32.json",
    "content": "{\n  \"id\": \"19ede218-928c-4e02-ab49-b76e12b34f32\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings\",\n  \"active\": \"profile\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/21c5f714-3089-49d2-b387-f244d4dd9e00.json",
    "content": "{\n  \"id\": \"21c5f714-3089-49d2-b387-f244d4dd9e00\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"active\": \"profile\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/74fd6c53-7651-453e-90b8-2c5adbf911bb.json",
    "content": "{\n  \"id\": \"74fd6c53-7651-453e-90b8-2c5adbf911bb\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"5ff66179-c240-4703-b0d8-494592cefff5\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"bazbar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"45e867e9-2745-4f16-8dd4-84334a252b61\",\n        \"value\": \"foo@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/77fe4fb3-2d4e-4532-b568-c44b0aece0aa.json",
    "content": "{\n  \"id\": \"77fe4fb3-2d4e-4532-b568-c44b0aece0aa\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"active\": \"profile\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/8248bb5d-8ef7-45e3-8e07-9e2003dd5352.json",
    "content": "{\n  \"id\": \"8248bb5d-8ef7-45e3-8e07-9e2003dd5352\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"active\": \"profile\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/90b4f970-b9ae-42bc-a0a7-73ec750e0aa1.json",
    "content": "{\n  \"id\": \"90b4f970-b9ae-42bc-a0a7-73ec750e0aa1\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"active\": \"profile\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/a79bfcf1-68ae-49de-8b23-4f96921b8341.json",
    "content": "{\n  \"id\": \"a79bfcf1-68ae-49de-8b23-4f96921b8341\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"active\": \"profile\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/aeba85bd-1a8c-44bf-8fc3-3be83c01a3dc.json",
    "content": "{\n  \"id\": \"aeba85bd-1a8c-44bf-8fc3-3be83c01a3dc\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"active\": \"profile\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/settings_flow/cdfd1eed-34a4-491d-ad0a-7579d3a0a7ba.json",
    "content": "{\n  \"id\": \"cdfd1eed-34a4-491d-ad0a-7579d3a0a7ba\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/settings\",\n  \"active\": \"profile\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"identity\": {\n    \"id\": \"a251ebc2-880c-4f76-a8f3-38e6940eab0e\",\n    \"schema_id\": \"default\",\n    \"schema_url\": \"https://www.ory.sh/schemas/ZGVmYXVsdA\",\n    \"state\": \"active\",\n    \"traits\": {\n      \"email\": \"foobar@ory.sh\"\n    },\n    \"verifiable_addresses\": [\n      {\n        \"id\": \"b2d59320-8564-4400-a39f-a22a497a23f1\",\n        \"value\": \"foobar+without-code@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"c2427b6d-312b-46d9-9285-536db7ae11fd\",\n        \"value\": \"foobar@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      },\n      {\n        \"id\": \"d4718a67-aec2-418d-8173-6ebc7bde3b86\",\n        \"value\": \"foobar+11345642c6c0@ory.sh\",\n        \"verified\": false,\n        \"via\": \"email\",\n        \"status\": \"pending\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"recovery_addresses\": [\n      {\n        \"id\": \"b8293f1c-010f-45d9-b809-f3fc5365ba80\",\n        \"value\": \"foobar@ory.sh\",\n        \"via\": \"email\",\n        \"created_at\": \"2013-10-07T08:23:19Z\",\n        \"updated_at\": \"2013-10-07T08:23:19Z\"\n      }\n    ],\n    \"metadata_public\": null,\n    \"created_at\": \"2013-10-07T08:23:19Z\",\n    \"updated_at\": \"2013-10-07T08:23:19Z\",\n    \"organization_id\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/29b2c16e-2955-4faa-bd16-33af098cdf83.json",
    "content": "{\n  \"id\": \"29b2c16e-2955-4faa-bd16-33af098cdf83\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"passed_challenge\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/3631e880-ce59-4cbd-a705-0d825eea590d.json",
    "content": "{\n  \"id\": \"3631e880-ce59-4cbd-a705-0d825eea590d\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"passed_challenge\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/42f31e47-65e1-4be9-80ea-e5d8ed64b236.json",
    "content": "{\n  \"id\": \"42f31e47-65e1-4be9-80ea-e5d8ed64b236\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"passed_challenge\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/5385c962-0295-4575-9b1b-d7eef13c0eda.json",
    "content": "{\n  \"id\": \"5385c962-0295-4575-9b1b-d7eef13c0eda\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/registration\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/6aae3159-b880-4cfb-a863-03b114b1371b.json",
    "content": "{\n  \"id\": \"6aae3159-b880-4cfb-a863-03b114b1371b\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/7be6c72c-c868-4b61-a1f0-1130603665d1.json",
    "content": "{\n  \"id\": \"7be6c72c-c868-4b61-a1f0-1130603665d1\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/7be6c72c-c868-4b61-a1f0-1130603665d8.json",
    "content": "{\n  \"id\": \"7be6c72c-c868-4b61-a1f0-1130603665d8\",\n  \"type\": \"api\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/81f74e5d-1fa5-4e1b-a9bf-e95119260471.json",
    "content": "{\n  \"id\": \"81f74e5d-1fa5-4e1b-a9bf-e95119260471\",\n  \"type\": \"api\",\n  \"expires_at\": \"2022-11-03T08:23:19Z\",\n  \"issued_at\": \"2022-11-03T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/81f74e5d-1fa5-4e1b-a9bf-e9511926047c.json",
    "content": "{\n  \"id\": \"81f74e5d-1fa5-4e1b-a9bf-e9511926047c\",\n  \"type\": \"api\",\n  \"expires_at\": \"2022-11-03T08:23:19Z\",\n  \"issued_at\": \"2022-11-03T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/a8e2b810-a561-4a9e-bbd8-37f649bc26fa.json",
    "content": "{\n  \"id\": \"a8e2b810-a561-4a9e-bbd8-37f649bc26fa\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"passed_challenge\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_flow/b37d34c2-4290-4be4-9e3a-c6263ee77082.json",
    "content": "{\n  \"id\": \"b37d34c2-4290-4be4-9e3a-c6263ee77082\",\n  \"type\": \"browser\",\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\",\n  \"request_url\": \"http://kratos:4433/self-service/browser/flows/verification/email\",\n  \"ui\": {\n    \"action\": \"\",\n    \"method\": \"\",\n    \"nodes\": null\n  },\n  \"state\": \"show_form\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_token/ee56574d-2f0c-43f6-8d26-0062938ae330.json",
    "content": "{\n  \"id\": \"ee56574d-2f0c-43f6-8d26-0062938ae330\",\n  \"verification_address\": null,\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_token/ee56574d-2f1c-43f6-8d26-0062938ae330.json",
    "content": "{\n  \"id\": \"ee56574d-2f1c-43f6-8d26-0062938ae330\",\n  \"verification_address\": null,\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/fixtures/verification_token/f81fd924-23bb-4cdf-8fa0-56253eff6cc9.json",
    "content": "{\n  \"id\": \"f81fd924-23bb-4cdf-8fa0-56253eff6cc9\",\n  \"verification_address\": null,\n  \"expires_at\": \"2013-10-07T08:23:19Z\",\n  \"issued_at\": \"2013-10-07T08:23:19Z\"\n}\n"
  },
  {
    "path": "persistence/sql/migratest/migration_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage migratest\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/bradleyjkemp/cupaloy/v2\"\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence/sql\"\n\tgomigrations \"github.com/ory/kratos/persistence/sql/migrations/go\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/fsx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/migratest\"\n\t\"github.com/ory/x/networkx\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/popx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlcon/dockertest\"\n)\n\nfunc snapshotFor(paths ...string) *cupaloy.Config {\n\treturn cupaloy.New(\n\t\tcupaloy.CreateNewAutomatically(true),\n\t\tcupaloy.FailOnUpdate(true),\n\t\tcupaloy.SnapshotFileExtension(\".json\"),\n\t\tcupaloy.SnapshotSubdirectory(filepath.Join(paths...)),\n\t)\n}\n\nfunc CompareWithFixture(t *testing.T, actual interface{}, prefix string, id string) {\n\ts := snapshotFor(\"fixtures\", prefix)\n\tactualJSON, err := json.MarshalIndent(actual, \"\", \"  \")\n\trequire.NoError(t, err)\n\terr = s.SnapshotWithName(id, actualJSON)\n\tassert.NoErrorf(t, err, \"actual = %s\", string(actualJSON))\n}\n\nfunc TestMigrations_SQLite(t *testing.T) {\n\tt.Parallel()\n\tsqlite, err := pop.NewConnection(&pop.ConnectionDetails{\n\t\tURL: dbal.NewSQLiteTestDatabase(t),\n\t})\n\trequire.NoError(t, err)\n\trequire.NoError(t, sqlite.Open())\n\n\ttestDatabase(t, \"sqlite\", sqlite)\n}\n\nfunc TestMigrations_Postgres(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping testing in short mode\")\n\t}\n\tt.Parallel()\n\ttestDatabase(t, \"postgres\", dockertest.ConnectPop(t, dockertest.RunTestPostgreSQLWithVersion(t, \"16\")))\n}\n\nfunc TestMigrations_Mysql(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping testing in short mode\")\n\t}\n\tt.Parallel()\n\ttestDatabase(t, \"mysql\", dockertest.ConnectPop(t, dockertest.RunTestMySQLWithVersion(t, \"8.4\")))\n}\n\nfunc TestMigrations_Cockroach(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping testing in short mode\")\n\t}\n\tt.Parallel()\n\ttestDatabase(t, \"cockroach\", dockertest.ConnectPop(t, dockertest.RunTestCockroachDBWithVersion(t, \"latest-v25.4\")))\n}\n\nfunc testDatabase(t *testing.T, db string, c *pop.Connection) {\n\tctx := context.Background()\n\tl := logrusx.New(\"\", \"\", logrusx.ForceLevel(logrus.DebugLevel))\n\n\turl := c.URL()\n\t// workaround for https://github.com/ory/pop/issues/538\n\tswitch db {\n\tcase \"mysql\":\n\t\turl = \"mysql://\" + url\n\tcase \"sqlite\":\n\t\turl = \"sqlite3://\" + url\n\tcase \"cockroach\":\n\t\turl = \"cockroach\" + strings.TrimPrefix(url, \"postgres\")\n\t}\n\tif db != \"sqlite\" {\n\t\tdbName := \"testdb\" + strings.ReplaceAll(x.NewUUID().String(), \"-\", \"\")\n\t\trequire.NoError(t, c.RawQuery(\"CREATE DATABASE \"+dbName).Exec())\n\t\turl = regexp.MustCompile(`/[a-z0-9]+\\?`).ReplaceAllString(url, \"/\"+dbName+\"?\")\n\t}\n\n\tt.Logf(\"URL: %s\", url)\n\tvar err error\n\tc, err = pop.NewConnection(&pop.ConnectionDetails{URL: url})\n\trequire.NoError(t, err)\n\trequire.NoError(t, c.Open())\n\n\ttm, err := popx.NewMigrationBox(\n\t\tfsx.Merge(sql.Migrations, networkx.Migrations),\n\t\tc, l,\n\t\tpopx.WithGoMigrations(gomigrations.All),\n\t\tpopx.WithTestdata(t, os.DirFS(\"./testdata\")),\n\t)\n\trequire.NoError(t, err)\n\n\terr = tm.Up(ctx) // for easy breakpointing\n\t// _ = tm.DumpMigrationSchema(ctx) // uncomment to get the current state of the database after migrations have run\n\tif !assert.NoError(t, err) {\n\t\tassert.NoError(t, tm.DumpMigrationSchema(ctx))\n\t\tt.FailNow()\n\t}\n\n\topts := driver.WithConfigOptions(\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeyDSN:             url,\n\t\t\tconfig.ViperKeyPublicBaseURL:   \"https://www.ory.sh/\",\n\t\t\tconfig.ViperKeyIdentitySchemas: config.Schemas{{ID: \"default\", URL: \"file://stub/default.schema.json\"}},\n\t\t\tconfig.ViperKeySecretsDefault:  []string{\"secret\"},\n\t\t}),\n\t\tconfigx.SkipValidation(),\n\t)\n\n\tt.Run(\"suite=fixtures\", func(t *testing.T) {\n\t\tt.Run(\"case=identity\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\td, err := driver.New(\n\t\t\t\tcontext.Background(),\n\t\t\t\tos.Stderr,\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tids, _, err := d.PrivilegedIdentityPool().ListIdentities(context.Background(), identity.ListIdentityParameters{Expand: identity.ExpandEverything, KeySetPagination: []keysetpagination.Option{keysetpagination.WithSize(1000)}})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\tvar found []string\n\t\t\tfor y, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tactual := &ids[y]\n\n\t\t\t\tfor _, a := range actual.VerifiableAddresses {\n\t\t\t\t\tCompareWithFixture(t, a, \"identity_verification_address\", a.ID.String())\n\t\t\t\t}\n\n\t\t\t\tfor _, a := range actual.RecoveryAddresses {\n\t\t\t\t\tCompareWithFixture(t, a, \"identity_recovery_address\", a.ID.String())\n\t\t\t\t}\n\n\t\t\t\tCompareWithFixture(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), \"identity\", id.ID.String())\n\t\t\t}\n\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"identity\"), found)\n\t\t})\n\n\t\tt.Run(\"case=identity_get\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\td, err := driver.New(\n\t\t\t\tcontext.Background(),\n\t\t\t\tos.Stderr,\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tids, _, err := d.PrivilegedIdentityPool().ListIdentities(context.Background(), identity.ListIdentityParameters{Expand: identity.ExpandNothing, KeySetPagination: []keysetpagination.Option{keysetpagination.WithSize(1000)}})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tactual, err := d.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), id.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tfound = append(found, actual.ID.String())\n\n\t\t\t\tCompareWithFixture(t, identity.WithCredentialsAndAdminMetadataInJSON(*actual), \"identity\", id.ID.String())\n\t\t\t}\n\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"identity\"), found)\n\t\t})\n\n\t\tt.Run(\"case=verification_token\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []link.VerificationToken\n\n\t\t\trequire.NoError(t, c.All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\tfor _, id := range ids {\n\t\t\t\tCompareWithFixture(t, id, \"verification_token\", id.ID.String())\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=session\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []session.Session\n\t\t\trequire.NoError(t, c.Select(\"id\").All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\td, err := driver.New(\n\t\t\t\tcontext.Background(),\n\t\t\t\tos.Stderr,\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tactual, err := d.SessionPersister().GetSession(context.Background(), id.ID, session.ExpandEverything)\n\t\t\t\trequire.NoErrorf(t, err, \"Trying to get session: %s\", id.ID)\n\t\t\t\trequire.NotEmpty(t, actual.LogoutToken, \"check if migrations have generated a logout token for existing sessions\")\n\t\t\t\tCompareWithFixture(t, actual, \"session\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"session\"), found)\n\t\t})\n\n\t\tt.Run(\"case=login\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []login.Flow\n\t\t\trequire.NoError(t, c.Select(\"id\").All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\td, err := driver.New(\n\t\t\t\tcontext.Background(),\n\t\t\t\tos.Stderr,\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tactual, err := d.LoginFlowPersister().GetLoginFlow(context.Background(), id.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tCompareWithFixture(t, actual, \"login_flow\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"login_flow\"), found)\n\t\t})\n\n\t\tt.Run(\"case=registration\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []registration.Flow\n\t\t\trequire.NoError(t, c.Select(\"id\").All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\td, err := driver.New(\n\t\t\t\tcontext.Background(),\n\t\t\t\tos.Stderr,\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tactual, err := d.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), id.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tCompareWithFixture(t, actual, \"registration_flow\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"registration_flow\"), found)\n\t\t})\n\n\t\tt.Run(\"case=settings_flow\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []settings.Flow\n\t\t\trequire.NoError(t, c.Select(\"id\").All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\td, err := driver.New(\n\t\t\t\tcontext.Background(),\n\t\t\t\tos.Stderr,\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tactual, err := d.SettingsFlowPersister().GetSettingsFlow(context.Background(), id.ID)\n\t\t\t\trequire.NoError(t, err, id.ID.String())\n\t\t\t\tCompareWithFixture(t, actual, \"settings_flow\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"settings_flow\"), found)\n\t\t})\n\n\t\tt.Run(\"case=recovery_flow\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []recovery.Flow\n\t\t\trequire.NoError(t, c.Select(\"id\").All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\td, err := driver.New(\n\t\t\t\tcontext.Background(),\n\t\t\t\tos.Stderr,\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tactual, err := d.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), id.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tCompareWithFixture(t, actual, \"recovery_flow\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"recovery_flow\"), found)\n\t\t})\n\n\t\tt.Run(\"case=verification_flow\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []verification.Flow\n\t\t\trequire.NoError(t, c.Select(\"id\").All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\td, err := driver.New(\n\t\t\t\tcontext.Background(),\n\t\t\t\tos.Stderr,\n\t\t\t\topts,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tactual, err := d.VerificationFlowPersister().GetVerificationFlow(context.Background(), id.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tCompareWithFixture(t, actual, \"verification_flow\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"verification_flow\"), found)\n\t\t})\n\n\t\tt.Run(\"case=recovery_token\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []link.RecoveryToken\n\t\t\trequire.NoError(t, c.All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tCompareWithFixture(t, id, \"recovery_token\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"recovery_token\"), found)\n\t\t})\n\n\t\tt.Run(\"case=recovery_code\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []code.RecoveryCode\n\t\t\trequire.NoError(t, c.All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tCompareWithFixture(t, id, \"recovery_code\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"recovery_code\"), found)\n\t\t})\n\n\t\tt.Run(\"case=registration_code\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []code.RegistrationCode\n\t\t\trequire.NoError(t, c.All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tCompareWithFixture(t, id, \"registration_code\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"registration_code\"), found)\n\t\t})\n\n\t\tt.Run(\"case=login_code\", func(t *testing.T) {\n\n\t\t\tt.Parallel()\n\n\t\t\tvar ids []code.LoginCode\n\t\t\trequire.NoError(t, c.All(&ids))\n\t\t\trequire.NotEmpty(t, ids)\n\n\t\t\tvar found []string\n\t\t\tfor _, id := range ids {\n\t\t\t\tfound = append(found, id.ID.String())\n\t\t\t\tCompareWithFixture(t, id, \"login_code\", id.ID.String())\n\t\t\t}\n\t\t\tmigratest.ContainsExpectedIds(t, filepath.Join(\"fixtures\", \"login_code\"), found)\n\t\t})\n\t})\n\n\tt.Run(\"suite=constraints\", func(t *testing.T) {\n\t\tt.Cleanup(func() {\n\t\t\t// clean up test duplicates - remove identity_credential_identifiers 10985ed1-5b6e-4012-ac10-03d87df65618 - otherwise down migration later fails.\n\t\t\trequire.NoError(t, c.RawQuery(\"DELETE FROM identity_credential_identifiers WHERE identifier = '10985ed1-5b6e-4012-ac10-03d87df65618'\").Exec())\n\t\t})\n\n\t\td, err := driver.New(\n\t\t\tcontext.Background(),\n\t\t\tos.Stderr,\n\t\t\topts,\n\t\t)\n\t\trequire.NoError(t, err)\n\n\t\tsr, err := d.SettingsFlowPersister().GetSettingsFlow(context.Background(), x.ParseUUID(\"a79bfcf1-68ae-49de-8b23-4f96921b8341\"))\n\t\trequire.NoError(t, err)\n\n\t\trequire.NoError(t, d.PrivilegedIdentityPool().DeleteIdentity(context.Background(), sr.IdentityID))\n\n\t\t_, err = d.SettingsFlowPersister().GetSettingsFlow(context.Background(), x.ParseUUID(\"a79bfcf1-68ae-49de-8b23-4f96921b8341\"))\n\t\trequire.Error(t, err)\n\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t})\n\n\tt.Run(\"suite=down\", func(t *testing.T) {\n\t\terr = tm.Down(ctx, -1) // for easy breakpointing\n\t\tif !assert.NoError(t, err) {\n\t\t\tassert.NoError(t, tm.DumpMigrationSchema(ctx))\n\t\t\tt.FailNow()\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "persistence/sql/migratest/stub/default.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n\n    \"email\": {\n      \"type\": \"string\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          }\n        },\n        \"verification\": {\n          \"via\": \"email\"\n        },\n        \"recovery\": {\n          \"via\": \"email\"\n        }\n      }\n    }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20150100000001_testdata.sql",
    "content": "INSERT INTO networks (id, created_at, updated_at) VALUES ('884f556e-eb3a-4b9f-bee3-11345642c6c0', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000001_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000002_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000003_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000004_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000005_testdata.mysql.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000006_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000007_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000008_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000009_testdata.mysql.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000010_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000011_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20191100000012_testdata.sql",
    "content": "-- no test data up until v0.3.0\n\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200317160354_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200401183443_testdata.sql",
    "content": "-- no test data up until v0.3.0\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200402142539_testdata.sql",
    "content": "INSERT INTO identities (id, traits_schema_id, traits, created_at, updated_at) VALUES ('a251ebc2-880c-4f76-a8f3-38e6940eab0e', 'default', '{\"email\":\"foobar@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO identities (id, traits_schema_id, traits, created_at, updated_at) VALUES ('5ff66179-c240-4703-b0d8-494592cefff5', 'default', '{\"email\":\"bazbar@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO continuity_containers (id, identity_id, name, payload, expires_at, created_at, updated_at) VALUES ('50ba09d3-481b-4060-844a-9541a9cec39c', '5ff66179-c240-4703-b0d8-494592cefff5', 'ory_kratos_settings_profile', '{\"traits\":{\"email\":\"baz@ory.sh\"},\"request_id\":\"\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('98f45a85-0782-49f1-a7b5-8f83d160b4a5', 1, 2, 'Hi, Verify your account by opening the following link:\n\n<a href=\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/swmcFweNFSfvTSTKecmZjO6I8x0hxzZS\">http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/swmcFweNFSfvTSTKecmZjO6I8x0hxzZS</a>', 'Please verify your email address', 'foo@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('7a9d6df4-3b60-4cae-996e-d15d78b0fc36', 1, 2, 'Hi, Verify your account by opening the following link: <a href=\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/u9ZcBr5HbRTR8f53Qj2Ng3KR8Mv1Zjdb\">http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/u9ZcBr5HbRTR8f53Qj2Ng3KR8Mv1Zjdb</a>', 'Please verify your email address', 'foobar@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('77fdc5e0-2260-49da-8aae-c36ba255d05b', 1, 2, 'Hi, Verify your account by opening the following link: <a href=\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/SQcSX0Jx6IVEDKqAuaLZLNEw00J4vlig\">http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/SQcSX0Jx6IVEDKqAuaLZLNEw00J4vlig</a>', 'Please verify your email address', 'foobar@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('a3ea4c30-0c6e-47b1-99ba-8fa69282e166', 1, 2, 'Hi, Verify your account by opening the following link: <a href=\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/AtsREGbtXu0RlIcwv3RPpxHEZNEcq3R9\">http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/AtsREGbtXu0RlIcwv3RPpxHEZNEcq3R9</a>', 'Please verify your email address', 'foo@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO identity_credential_types (id, name) VALUES ('22bff9ae-f5aa-45d7-803b-97ec0b4e7b32', 'password');\nINSERT INTO identity_credential_types (id, name) VALUES ('8071b37b-0d54-4c6f-8234-72cffb4ce784', 'totp');\n\nINSERT INTO identity_credentials (id, config, identity_credential_type_id, identity_id, created_at, updated_at) VALUES ('35b60ecf-30f9-42d6-bf5d-47ad41148691', '{\"hashed_password\":\"$argon2id$v=19$m=131072,t=2,p=1$lQFPaKxXqPL56/mU7vRi4w$6aldHyBnURt8sP8+xu41Ng\"}', '22bff9ae-f5aa-45d7-803b-97ec0b4e7b32', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO identity_credentials (id, config, identity_credential_type_id, identity_id, created_at, updated_at) VALUES ('74ac2d31-bccb-442f-a792-7b8bb14817f8', '{\"hashed_password\":\"$argon2id$v=19$m=131072,t=2,p=1$lQFPaKxXqPL56/mU7vRi4w$6aldHyBnURt8sP8+xu41Ng\"}', '22bff9ae-f5aa-45d7-803b-97ec0b4e7b32', '5ff66179-c240-4703-b0d8-494592cefff5', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO identity_credential_identifiers (id, identifier, identity_credential_id, created_at, updated_at) VALUES ('f63a74f9-12da-439d-a8ce-b0157cd163b1', 'foobar@ory.sh', '35b60ecf-30f9-42d6-bf5d-47ad41148691', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO identity_credential_identifiers (id, identifier, identity_credential_id, created_at, updated_at) VALUES ('1f51d652-8504-4d26-8615-989fd76b7ebf', 'foo@ory.sh', '74ac2d31-bccb-442f-a792-7b8bb14817f8', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO identity_verifiable_addresses (id, code, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at) VALUES ('c2427b6d-312b-46d9-9285-536db7ae11fd', 'SQcSX0Jx6IVEDKqAuaLZLNEw00J4vlig', 'pending', 'email', false, 'foobar@ory.sh', null, '2013-10-07 08:23:19', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO identity_verifiable_addresses (id, code, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at) VALUES ('45e867e9-2745-4f16-8dd4-84334a252b61', 'AtsREGbtXu0RlIcwv3RPpxHEZNEcq3R9', 'pending', 'email', false, 'foo@ory.sh', null, '2013-10-07 08:23:19', '5ff66179-c240-4703-b0d8-494592cefff5', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_login_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) VALUES ('3a9ea34f-0f12-469b-9417-3ae5795a7baa', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false);\nINSERT INTO selfservice_login_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) VALUES ('916ded11-aa64-4a27-b06e-96e221a509d7', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'JdXCIeZGYglwUTjEC5PpKO5LFDEbeVOketqK5hdfZgFX379dVpLcRyRs+lteQJ1PxTMlyN0+btkz+5iVm4miIg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false);\nINSERT INTO selfservice_login_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) VALUES ('99974ce6-388c-4669-a95a-7757ee724020', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'OXDnJU8yP+iXXsf2TduzRYrDLfksjC0vRdU+nN5bdo+YeQt5xGWKbqEX/gggqE1ntM7qavC/qqzaR2B1Pk88Ew==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false);\nINSERT INTO selfservice_login_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) VALUES ('6d387820-f2f4-4f9f-9980-a90d89e7811f', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'Nrq+T0uNsSL8ztjmnbIB2V6NodqifoG+HZ4q6NSAnnWXs1ITwNoEpMqH4Rjwwf/7YIBmSX5NBj2CDHQBNJTU6Q==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false);\nINSERT INTO selfservice_login_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) VALUES ('202c1981-1e25-47f0-8764-75ad506c2bec', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', '7esV9o+i9BJCvpBz3HXWxBPI2802lrGtt3+dBIwBZ/1M4vmqBPVBlHT3qY2xBijmLcUcXuqlNi4o7cPtbBUtYQ==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false);\nINSERT INTO selfservice_login_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) VALUES ('43c99182-bb67-47e1-b564-bb23bd8d4393', 'http://kratos:4433/self-service/browser/flows/login?prompt=login&return_to=http%3A%2F%2F127.0.0.1%3A4455%2F.ory%2Fkratos%2Fpublic%2Fself-service%2Fbrowser%2Fflows%2Fsettings%2Fstrategies%2Fprofile%3Frequest%3D74fd6c53-7651-453e-90b8-2c5adbf911bb', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'dUD3MWqAmTdyoz2xwC9g5Dpv0g1aGUqAHOOsjy+IOh0vfPDqpKSudgZNSq9dqdxft4M0DN411z+5qSCMre5lmg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', true);\n\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('9944614a-8f43-421d-aa7d-f051391e463f', 'password', '3a9ea34f-0f12-469b-9417-3ae5795a7baa', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login/strategies/password?request=3a9ea34f-0f12-469b-9417-3ae5795a7baa\",\"method\":\"POST\",\"fields\":[{\"name\":\"identifier\",\"type\":\"text\",\"required\":true,\"value\":\"\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('a2c9f597-6eb2-4ea5-972b-4f4ffa828a6c', 'oidc', '3a9ea34f-0f12-469b-9417-3ae5795a7baa', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/3a9ea34f-0f12-469b-9417-3ae5795a7baa\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('227aa2c4-acf3-489f-a239-7f18d4bc2828', 'password', '916ded11-aa64-4a27-b06e-96e221a509d7', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login/strategies/password?request=916ded11-aa64-4a27-b06e-96e221a509d7\",\"method\":\"POST\",\"fields\":[{\"name\":\"identifier\",\"type\":\"text\",\"required\":true,\"value\":\"\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"JdXCIeZGYglwUTjEC5PpKO5LFDEbeVOketqK5hdfZgFX379dVpLcRyRs+lteQJ1PxTMlyN0+btkz+5iVm4miIg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('bd498938-863f-41de-a60f-69af4220db36', 'oidc', '916ded11-aa64-4a27-b06e-96e221a509d7', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/916ded11-aa64-4a27-b06e-96e221a509d7\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"JdXCIeZGYglwUTjEC5PpKO5LFDEbeVOketqK5hdfZgFX379dVpLcRyRs+lteQJ1PxTMlyN0+btkz+5iVm4miIg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('9cdeaa86-1242-4c62-99b6-575d900d4c47', 'password', '99974ce6-388c-4669-a95a-7757ee724020', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login/strategies/password?request=99974ce6-388c-4669-a95a-7757ee724020\",\"method\":\"POST\",\"fields\":[{\"name\":\"identifier\",\"type\":\"text\",\"required\":true,\"value\":\"\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"OXDnJU8yP+iXXsf2TduzRYrDLfksjC0vRdU+nN5bdo+YeQt5xGWKbqEX/gggqE1ntM7qavC/qqzaR2B1Pk88Ew==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('b370b9bf-b785-4c8a-a013-245adc8db117', 'oidc', '99974ce6-388c-4669-a95a-7757ee724020', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/99974ce6-388c-4669-a95a-7757ee724020\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"OXDnJU8yP+iXXsf2TduzRYrDLfksjC0vRdU+nN5bdo+YeQt5xGWKbqEX/gggqE1ntM7qavC/qqzaR2B1Pk88Ew==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('df9bc6af-5b62-4f38-8bac-618937a67ff8', 'password', '6d387820-f2f4-4f9f-9980-a90d89e7811f', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login/strategies/password?request=6d387820-f2f4-4f9f-9980-a90d89e7811f\",\"method\":\"POST\",\"fields\":[{\"name\":\"identifier\",\"type\":\"text\",\"required\":true,\"value\":\"\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"Nrq+T0uNsSL8ztjmnbIB2V6NodqifoG+HZ4q6NSAnnWXs1ITwNoEpMqH4Rjwwf/7YIBmSX5NBj2CDHQBNJTU6Q==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('9cb49264-492d-49d2-8b19-ed1b7506d5ba', 'oidc', '6d387820-f2f4-4f9f-9980-a90d89e7811f', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/6d387820-f2f4-4f9f-9980-a90d89e7811f\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"Nrq+T0uNsSL8ztjmnbIB2V6NodqifoG+HZ4q6NSAnnWXs1ITwNoEpMqH4Rjwwf/7YIBmSX5NBj2CDHQBNJTU6Q==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('ccc3dbc3-302c-4f55-8f30-891975843ed4', 'password', '202c1981-1e25-47f0-8764-75ad506c2bec', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login/strategies/password?request=202c1981-1e25-47f0-8764-75ad506c2bec\",\"method\":\"POST\",\"fields\":[{\"name\":\"identifier\",\"type\":\"text\",\"required\":true,\"value\":\"\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"7esV9o+i9BJCvpBz3HXWxBPI2802lrGtt3+dBIwBZ/1M4vmqBPVBlHT3qY2xBijmLcUcXuqlNi4o7cPtbBUtYQ==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('49a44013-0650-4393-bad0-d3aaa7396fd6', 'oidc', '202c1981-1e25-47f0-8764-75ad506c2bec', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/202c1981-1e25-47f0-8764-75ad506c2bec\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"7esV9o+i9BJCvpBz3HXWxBPI2802lrGtt3+dBIwBZ/1M4vmqBPVBlHT3qY2xBijmLcUcXuqlNi4o7cPtbBUtYQ==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('6ba34380-19e5-473d-9181-e8f7def63235', 'oidc', '43c99182-bb67-47e1-b564-bb23bd8d4393', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/43c99182-bb67-47e1-b564-bb23bd8d4393\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"dUD3MWqAmTdyoz2xwC9g5Dpv0g1aGUqAHOOsjy+IOh0vfPDqpKSudgZNSq9dqdxft4M0DN411z+5qSCMre5lmg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_login_request_methods (id, method, selfservice_login_request_id, config, created_at, updated_at) VALUES ('32477a6c-b6a5-482f-a218-287dc1208d8e', 'password', '43c99182-bb67-47e1-b564-bb23bd8d4393', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login/strategies/password?request=43c99182-bb67-47e1-b564-bb23bd8d4393\",\"method\":\"POST\",\"fields\":[{\"name\":\"identifier\",\"type\":\"text\",\"required\":true,\"value\":\"foo@ory.sh\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"dUD3MWqAmTdyoz2xwC9g5Dpv0g1aGUqAHOOsjy+IOh0vfPDqpKSudgZNSq9dqdxft4M0DN411z+5qSCMre5lmg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_registration_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) VALUES ('87fa3f43-5155-42b4-a1ad-174c2595fdaf', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) VALUES ('2bf132e0-5d40-4df9-9a11-9106e5333735', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'eyxQ2bNdAWBqzkZpRbzPQDpbDtpdqkkz2b7awuCdg6EJJi2lA4m/Lj7zhPYQb7snESM/I5vtdE6Qn8ixbEtHgg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) VALUES ('05a7f09d-4ef3-41fb-958a-6ad74584b36a', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'R3+y/kB6m+17C3HRkAYQJ+KOIpruSQnbplm+t3JiQd7mdl6iyy0ua01CSC/9de4F3IPlCTJ6jlg5y+BeknYLQg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) VALUES ('22d58184-b97d-44a5-bbaf-0aa8b4000d81', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'vtujrgwX3K+icdz8iHW3WD4VYd+QIGCb1NqjWcF+wj4f0k/yh0BpKZQ45QLlBkl6ABimTEwT5xhLSP2wIWqIog==', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_registration_request_methods (id, method, selfservice_registration_request_id, config, created_at, updated_at) VALUES ('634a863d-2f86-40f3-aa82-29ac211e2484', 'password', '87fa3f43-5155-42b4-a1ad-174c2595fdaf', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/registration/strategies/password?request=87fa3f43-5155-42b4-a1ad-174c2595fdaf\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"KeEp3OT+pHSXJpZAP+C1hR3yr4eIjUBDhg9tc/F1WI1b61SgVCoaOsMbVN9qM8HiNoqefk7KfT7PLn8AfaOcrg==\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true,\"errors\":[{\"message\":\"the password does not fulfill the password policy because: the password has been found in data breaches and must no longer be used.\"}]},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo@ory.sh\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_request_methods (id, method, selfservice_registration_request_id, config, created_at, updated_at) VALUES ('08ce0a27-50ee-4e9a-aa75-2a842fb6825b', 'oidc', '87fa3f43-5155-42b4-a1ad-174c2595fdaf', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/87fa3f43-5155-42b4-a1ad-174c2595fdaf\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_request_methods (id, method, selfservice_registration_request_id, config, created_at, updated_at) VALUES ('fd51365b-b954-4a2a-9367-98a37cabf86c', 'password', '2bf132e0-5d40-4df9-9a11-9106e5333735', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/registration/strategies/password?request=2bf132e0-5d40-4df9-9a11-9106e5333735\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"eyxQ2bNdAWBqzkZpRbzPQDpbDtpdqkkz2b7awuCdg6EJJi2lA4m/Lj7zhPYQb7snESM/I5vtdE6Qn8ixbEtHgg==\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"traits.email\",\"type\":\"email\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_request_methods (id, method, selfservice_registration_request_id, config, created_at, updated_at) VALUES ('c65e1f49-d458-42a8-940b-8dc956804616', 'oidc', '2bf132e0-5d40-4df9-9a11-9106e5333735', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/2bf132e0-5d40-4df9-9a11-9106e5333735\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"eyxQ2bNdAWBqzkZpRbzPQDpbDtpdqkkz2b7awuCdg6EJJi2lA4m/Lj7zhPYQb7snESM/I5vtdE6Qn8ixbEtHgg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_request_methods (id, method, selfservice_registration_request_id, config, created_at, updated_at) VALUES ('39f85951-6d5d-43c8-afeb-a93d041f38e4', 'password', '05a7f09d-4ef3-41fb-958a-6ad74584b36a', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/registration/strategies/password?request=05a7f09d-4ef3-41fb-958a-6ad74584b36a\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"R3+y/kB6m+17C3HRkAYQJ+KOIpruSQnbplm+t3JiQd7mdl6iyy0ua01CSC/9de4F3IPlCTJ6jlg5y+BeknYLQg==\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"traits.email\",\"type\":\"email\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_request_methods (id, method, selfservice_registration_request_id, config, created_at, updated_at) VALUES ('8555b5d4-7f0c-4353-bddb-0601bc1c7387', 'oidc', '05a7f09d-4ef3-41fb-958a-6ad74584b36a', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/05a7f09d-4ef3-41fb-958a-6ad74584b36a\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"R3+y/kB6m+17C3HRkAYQJ+KOIpruSQnbplm+t3JiQd7mdl6iyy0ua01CSC/9de4F3IPlCTJ6jlg5y+BeknYLQg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_request_methods (id, method, selfservice_registration_request_id, config, created_at, updated_at) VALUES ('3573b093-4ea1-4f7c-bd90-2bd44275c36f', 'password', '22d58184-b97d-44a5-bbaf-0aa8b4000d81', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/registration/strategies/password?request=22d58184-b97d-44a5-bbaf-0aa8b4000d81\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"vtujrgwX3K+icdz8iHW3WD4VYd+QIGCb1NqjWcF+wj4f0k/yh0BpKZQ45QLlBkl6ABimTEwT5xhLSP2wIWqIog==\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"traits.email\",\"type\":\"email\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_registration_request_methods (id, method, selfservice_registration_request_id, config, created_at, updated_at) VALUES ('c8853b14-a511-4367-a530-b79a514c701b', 'oidc', '22d58184-b97d-44a5-bbaf-0aa8b4000d81', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/auth/22d58184-b97d-44a5-bbaf-0aa8b4000d81\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"vtujrgwX3K+icdz8iHW3WD4VYd+QIGCb1NqjWcF+wj4f0k/yh0BpKZQ45QLlBkl6ABimTEwT5xhLSP2wIWqIog==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_settings_requests (id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method) VALUES ('21c5f714-3089-49d2-b387-f244d4dd9e00', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'profile');\nINSERT INTO selfservice_settings_requests (id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method) VALUES ('194c5b05-0487-4a11-bcbc-f301c9ff9678', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', true, 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', null);\nINSERT INTO selfservice_settings_requests (id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method) VALUES ('74fd6c53-7651-453e-90b8-2c5adbf911bb', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, '5ff66179-c240-4703-b0d8-494592cefff5', '2013-10-07 08:23:19', '2013-10-07 08:23:19', null);\n\nINSERT INTO selfservice_settings_request_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at) VALUES ('71896798-3844-4269-bdab-9a9f1f6160f3', 'profile', '21c5f714-3089-49d2-b387-f244d4dd9e00', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?request=21c5f714-3089-49d2-b387-f244d4dd9e00\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"yDwSg0quCmc4kBl7lBqYwGh4W8awrc+TpeWiigZs3iemRCwqeDhGdrW3sIv8T7u742pN+Kryx/NrdRpEXcT9qA==\"},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo\",\"errors\":[{\"message\":\"validation failed\"},{\"message\":\"foo is not valid email\"},{\"message\":\"foo is not valid email\"}]}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_settings_request_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at) VALUES ('e8cfd338-5112-4311-b2ec-97ebc07fd7e9', 'password', '21c5f714-3089-49d2-b387-f244d4dd9e00', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/password?request=21c5f714-3089-49d2-b387-f244d4dd9e00\",\"method\":\"POST\",\"fields\":[{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"Yohg7gsfztSw5KSEt/7+Tnbfc3QBmFA7zvqPg7DuhdgM8F5HOYmCxT3DDXTfq901/c1lShvHWFsAajdN60amVw==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_settings_request_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at) VALUES ('61d63a2d-ad74-433a-bcfb-2c93ae7b4d9c', 'oidc', '21c5f714-3089-49d2-b387-f244d4dd9e00', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/settings/connections?request=21c5f714-3089-49d2-b387-f244d4dd9e00\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"Yohg7gsfztSw5KSEt/7+Tnbfc3QBmFA7zvqPg7DuhdgM8F5HOYmCxT3DDXTfq901/c1lShvHWFsAajdN60amVw==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_settings_request_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at) VALUES ('1966b9ba-1fe2-46b9-97f9-e4f86cc8f15d', 'password', '194c5b05-0487-4a11-bcbc-f301c9ff9678', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/password?request=194c5b05-0487-4a11-bcbc-f301c9ff9678\",\"method\":\"POST\",\"fields\":[{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"4apNE3S+VLL1UPu8GhyLOhdnhkzLZxrwTqZ/dx5xf3eP0nO6RigYo3h3UkxySahBnHWQctE4EpCANse5Rdlc+A==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_settings_request_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at) VALUES ('3095bd65-910b-4110-87fc-893e443df30c', 'oidc', '194c5b05-0487-4a11-bcbc-f301c9ff9678', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/settings/connections?request=194c5b05-0487-4a11-bcbc-f301c9ff9678\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"4apNE3S+VLL1UPu8GhyLOhdnhkzLZxrwTqZ/dx5xf3eP0nO6RigYo3h3UkxySahBnHWQctE4EpCANse5Rdlc+A==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_settings_request_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at) VALUES ('76da60ae-d629-48ed-92b2-5be892adbef7', 'profile', '194c5b05-0487-4a11-bcbc-f301c9ff9678', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?request=194c5b05-0487-4a11-bcbc-f301c9ff9678\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"IoLeYNbYJdALHIKvrgK7iYyMHA8r/rhG+6mr3z0nleRM+uDJ5E5pwYY7K1/GV5jyB54KMTGhsCY1ORMRZo+2aw==\"},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foobar@ory.sh\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_settings_request_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at) VALUES ('5ce98190-d1e8-4034-a361-7a3e686551eb', 'profile', '74fd6c53-7651-453e-90b8-2c5adbf911bb', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?request=74fd6c53-7651-453e-90b8-2c5adbf911bb\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"G/2FjcQNeTJHOFrPP0ytFfC/AWeUDDB4DQs6SsMRMjxBwYJWCilOczPWLdGiyhGufVPnZhAgrceoQbZJQXdtuw==\"},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"baz@ory.sh\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_settings_request_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at) VALUES ('fb401162-9db6-4be4-b44c-01ce724dd817', 'password', '74fd6c53-7651-453e-90b8-2c5adbf911bb', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/password?request=74fd6c53-7651-453e-90b8-2c5adbf911bb\",\"method\":\"POST\",\"fields\":[{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"4G7nFBBjlvSMDzqi3HXROK2ijGojM/2jXmoVHQPAz7i6UuDP3kehtfjhTbxB822DIE5qa6cfYBz7IJkegaaQPw==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_settings_request_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at) VALUES ('9068a486-20f2-42f1-b5b8-368aedbb7b89', 'oidc', '74fd6c53-7651-453e-90b8-2c5adbf911bb', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/settings/connections?request=74fd6c53-7651-453e-90b8-2c5adbf911bb\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"4G7nFBBjlvSMDzqi3HXROK2ijGojM/2jXmoVHQPAz7i6UuDP3kehtfjhTbxB822DIE5qa6cfYBz7IJkegaaQPw==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_verification_requests (id, request_url, issued_at, expires_at, form, via, csrf_token, success, created_at, updated_at) VALUES ('a8e2b810-a561-4a9e-bbd8-37f649bc26fa', 'http://kratos:4433/self-service/browser/flows/verification/email', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'null', 'email', '8xoIMa1+UkDqTt+tIHmIEHztQkk0AWk2PJhWWYDmB6dSE+RtJinnxtwH5lNNCnYyQuCF2ugy7rWjCgiwYPJNOw==', true, '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO selfservice_verification_requests (id, request_url, issued_at, expires_at, form, via, csrf_token, success, created_at, updated_at) VALUES ('b37d34c2-4290-4be4-9e3a-c6263ee77082', 'http://kratos:4433/self-service/browser/flows/verification/email', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/complete?request=b37d34c2-4290-4be4-9e3a-c6263ee77082\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"w+Amwv/g+QE/RaFUuc5/z7qnfFC2fxro9w9kUvoqhyBi6cqedLdMhwkMmKrUvYHthKq7w2pMnWtonTq7Gj7NvA==\"},{\"name\":\"to_verify\",\"type\":\"email\",\"required\":true}]}', 'email', 'w+Amwv/g+QE/RaFUuc5/z7qnfFC2fxro9w9kUvoqhyBi6cqedLdMhwkMmKrUvYHthKq7w2pMnWtonTq7Gj7NvA==', false, '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200519101057_testdata.sql",
    "content": "INSERT INTO identity_recovery_addresses (id, via, value, identity_id, created_at, updated_at)\nVALUES ('b8293f1c-010f-45d9-b809-f3fc5365ba80', 'email', 'foobar@ory.sh', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_recovery_requests (id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at)\nVALUES ('13178936-095a-466b-abe0-36d977d3dc18', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]', 'link', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', 'choose_method', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_recovery_request_methods (id, method, selfservice_recovery_request_id, config, created_at, updated_at)\nVALUES ('921462a6-8af6-4cda-97b4-dc0930ed271b', 'link', '13178936-095a-466b-abe0-36d977d3dc18', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?request=21c5f714-3089-49d2-b387-f244d4dd9e00\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"yDwSg0quCmc4kBl7lBqYwGh4W8awrc+TpeWiigZs3iemRCwqeDhGdrW3sIv8T7u742pN+Kryx/NrdRpEXcT9qA==\"},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo\",\"errors\":[{\"message\":\"validation failed\"},{\"message\":\"foo is not valid email\"},{\"message\":\"foo is not valid email\"}]}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO identity_recovery_tokens (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_request_id, created_at, updated_at)\nVALUES ('5529d454-2946-404e-b681-d950f8657fd0', 'd40c167d-a7f2-41a6-86b2-a5483001a010', false, null, 'b8293f1c-010f-45d9-b809-f3fc5365ba80', '13178936-095a-466b-abe0-36d977d3dc18', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200519101058_testdata.mysql.sql",
    "content": "-- empty\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200601101000_testdata.sql",
    "content": "INSERT INTO selfservice_settings_requests (id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method, messages) VALUES\n('a79bfcf1-68ae-49de-8b23-4f96921b8341', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'profile', '[]');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200601101001_testdata.mysql.sql",
    "content": "-- empty\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200605111551_testdata.sql",
    "content": "INSERT INTO selfservice_login_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages) VALUES ('56d94e8b-8a5d-4b7f-8a6e-3259d2b2903e', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, '[]');\nINSERT INTO selfservice_registration_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages) VALUES ('9edcf051-1cd0-44cc-bd2f-6ac21f0c24dd', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]');\nINSERT INTO selfservice_verification_requests (id, request_url, issued_at, expires_at, form, via, csrf_token, success, created_at, updated_at, messages) VALUES ('29b2c16e-2955-4faa-bd16-33af098cdf83', 'http://kratos:4433/self-service/browser/flows/verification/email', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'null', 'email', '8xoIMa1+UkDqTt+tIHmIEHztQkk0AWk2PJhWWYDmB6dSE+RtJinnxtwH5lNNCnYyQuCF2ugy7rWjCgiwYPJNOw==', true, '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200607165100_testdata.sql",
    "content": "INSERT INTO selfservice_settings_requests (id, request_url, issued_at, expires_at, state, identity_id, created_at, updated_at, active_method, messages) VALUES\n('77fe4fb3-2d4e-4532-b568-c44b0aece0aa', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'show_form', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'profile', '[]');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200705105359_testdata.sql",
    "content": "INSERT INTO identities (id, schema_id, traits, created_at, updated_at) VALUES ('d7b9addb-ac15-4bc2-9fa5-562e0bf48755', 'default', '{\"email\":\"d7b9@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200810141652_testdata.sql",
    "content": "INSERT INTO selfservice_login_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages, type) VALUES ('47edd3a8-0998-4779-9469-f4b8ee4430df', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, '[]', 'api');\nINSERT INTO selfservice_registration_requests (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages, type) VALUES ('696e7022-c466-44f6-89c6-8cf93c06a62a', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]', 'api');\nINSERT INTO selfservice_verification_requests (id, request_url, issued_at, expires_at, form, via, csrf_token, success, created_at, updated_at, messages, type) VALUES ('3631e880-ce59-4cbd-a705-0d825eea590d', 'http://kratos:4433/self-service/browser/flows/verification/email', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'null', 'email', '8xoIMa1+UkDqTt+tIHmIEHztQkk0AWk2PJhWWYDmB6dSE+RtJinnxtwH5lNNCnYyQuCF2ugy7rWjCgiwYPJNOw==', true, '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]', 'api');\nINSERT INTO selfservice_settings_requests (id, request_url, issued_at, expires_at, state, identity_id, created_at, updated_at, active_method, messages) VALUES\n('cdfd1eed-34a4-491d-ad0a-7579d3a0a7ba', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'show_form', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'profile', '[]');\nINSERT INTO selfservice_recovery_requests (id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type)\nVALUES ('87e871e1-a45f-4ed0-ba4e-a03063c774dc', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]', 'link', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', 'choose_method', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200810161022_testdata.sql",
    "content": "INSERT INTO selfservice_login_flows (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages, type)\nVALUES ('b1fac7fb-d016-4a06-a7fe-e4eab2a0429f', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, '[]', 'api');\nINSERT INTO selfservice_login_flow_methods (id, method, selfservice_login_request_id, config, created_at, updated_at)\nVALUES ('8ca78d66-772a-4408-804e-f5d3d9fe696e', 'password', 'b1fac7fb-d016-4a06-a7fe-e4eab2a0429f', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login/strategies/password?request=3a9ea34f-0f12-469b-9417-3ae5795a7baa\",\"method\":\"POST\",\"fields\":[{\"name\":\"identifier\",\"type\":\"text\",\"required\":true,\"value\":\"\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_registration_flows (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages, type)\nVALUES ('8ef215a9-e8d5-43b3-9aa3-cb4333562e36', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]', 'api');\nINSERT INTO selfservice_registration_flow_methods (id, method, selfservice_registration_request_id, config, created_at, updated_at)\nVALUES ('356019d1-900e-4c6d-b3f1-564c86930979', 'password', '8ef215a9-e8d5-43b3-9aa3-cb4333562e36', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/registration/strategies/password?request=87fa3f43-5155-42b4-a1ad-174c2595fdaf\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"KeEp3OT+pHSXJpZAP+C1hR3yr4eIjUBDhg9tc/F1WI1b61SgVCoaOsMbVN9qM8HiNoqefk7KfT7PLn8AfaOcrg==\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true,\"errors\":[{\"message\":\"the password does not fulfill the password policy because: the password has been found in data breaches and must no longer be used.\"}]},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo@ory.sh\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_settings_flows (id, request_url, issued_at, expires_at, state, identity_id, created_at, updated_at, active_method, messages)\nVALUES ('90b4f970-b9ae-42bc-a0a7-73ec750e0aa1', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'show_form', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'profile', '[]');\nINSERT INTO selfservice_settings_flow_methods (id, method, selfservice_settings_request_id, config, created_at, updated_at)\nVALUES ('547e8444-b3b0-4de7-9fdf-b80d6a8de15f', 'profile', '90b4f970-b9ae-42bc-a0a7-73ec750e0aa1', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?request=21c5f714-3089-49d2-b387-f244d4dd9e00\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"yDwSg0quCmc4kBl7lBqYwGh4W8awrc+TpeWiigZs3iemRCwqeDhGdrW3sIv8T7u742pN+Kryx/NrdRpEXcT9qA==\"},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo\",\"errors\":[{\"message\":\"validation failed\"},{\"message\":\"foo is not valid email\"},{\"message\":\"foo is not valid email\"}]}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_verification_flows (id, request_url, issued_at, expires_at, form, via, csrf_token, success, created_at, updated_at, messages, type)\nVALUES ('42f31e47-65e1-4be9-80ea-e5d8ed64b236', 'http://kratos:4433/self-service/browser/flows/verification/email', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'null', 'email', '8xoIMa1+UkDqTt+tIHmIEHztQkk0AWk2PJhWWYDmB6dSE+RtJinnxtwH5lNNCnYyQuCF2ugy7rWjCgiwYPJNOw==', true, '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]', 'api');\n\nINSERT INTO selfservice_recovery_flows (id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type)\nVALUES ('0d14427f-e16d-43a5-8695-8278bf85d4eb', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]', 'link', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', 'choose_method', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api');\nINSERT INTO selfservice_recovery_flow_methods (id, method, selfservice_recovery_request_id, config, created_at, updated_at)\nVALUES ('1a45ce9b-b527-4535-ad31-b808e481d280', 'link', '0d14427f-e16d-43a5-8695-8278bf85d4eb', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?request=21c5f714-3089-49d2-b387-f244d4dd9e00\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"yDwSg0quCmc4kBl7lBqYwGh4W8awrc+TpeWiigZs3iemRCwqeDhGdrW3sIv8T7u742pN+Kryx/NrdRpEXcT9qA==\"},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo\",\"errors\":[{\"message\":\"validation failed\"},{\"message\":\"foo is not valid email\"},{\"message\":\"foo is not valid email\"}]}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200810162450_testdata.sql",
    "content": "INSERT INTO selfservice_login_flow_methods (id, method, selfservice_login_flow_id, config, created_at, updated_at)\nVALUES ('12aca2ae-248d-448c-a33c-5efb1b165238', 'password', 'b1fac7fb-d016-4a06-a7fe-e4eab2a0429f', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login/strategies/password?request=3a9ea34f-0f12-469b-9417-3ae5795a7baa\",\"method\":\"POST\",\"fields\":[{\"name\":\"identifier\",\"type\":\"text\",\"required\":true,\"value\":\"\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true},{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_registration_flow_methods (id, method, selfservice_registration_flow_id, config, created_at, updated_at)\nVALUES ('4849e60b-bd37-4e51-9361-3b6c27f7b9f8', 'password', '8ef215a9-e8d5-43b3-9aa3-cb4333562e36', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/registration/strategies/password?request=87fa3f43-5155-42b4-a1ad-174c2595fdaf\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"KeEp3OT+pHSXJpZAP+C1hR3yr4eIjUBDhg9tc/F1WI1b61SgVCoaOsMbVN9qM8HiNoqefk7KfT7PLn8AfaOcrg==\"},{\"name\":\"password\",\"type\":\"password\",\"required\":true,\"errors\":[{\"message\":\"the password does not fulfill the password policy because: the password has been found in data breaches and must no longer be used.\"}]},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo@ory.sh\"}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_settings_flow_methods (id, method, selfservice_settings_flow_id, config, created_at, updated_at)\nVALUES ('d3501777-80cb-4fc6-b1ec-04c8d4d5b187', 'profile', '90b4f970-b9ae-42bc-a0a7-73ec750e0aa1', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?request=21c5f714-3089-49d2-b387-f244d4dd9e00\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"yDwSg0quCmc4kBl7lBqYwGh4W8awrc+TpeWiigZs3iemRCwqeDhGdrW3sIv8T7u742pN+Kryx/NrdRpEXcT9qA==\"},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo\",\"errors\":[{\"message\":\"validation failed\"},{\"message\":\"foo is not valid email\"},{\"message\":\"foo is not valid email\"}]}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_recovery_flow_methods (id, method, selfservice_recovery_flow_id, config, created_at, updated_at)\nVALUES ('1d53af7f-8379-4ad0-9e00-16a2963bea83', 'link', '0d14427f-e16d-43a5-8695-8278bf85d4eb', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?request=21c5f714-3089-49d2-b387-f244d4dd9e00\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"yDwSg0quCmc4kBl7lBqYwGh4W8awrc+TpeWiigZs3iemRCwqeDhGdrW3sIv8T7u742pN+Kryx/NrdRpEXcT9qA==\"},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo\",\"errors\":[{\"message\":\"validation failed\"},{\"message\":\"foo is not valid email\"},{\"message\":\"foo is not valid email\"}]}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200812124254_testdata.sql",
    "content": "INSERT INTO sessions (id, issued_at, expires_at, authenticated_at,created_at,updated_at, token, identity_id)\nVALUES ('8571e374-38f2-4f46-8ad3-b9d914e174d3','2013-10-07 08:23:19','2013-10-07 08:23:19','2013-10-07 08:23:19','2013-10-07 08:23:19','2013-10-07 08:23:19', '1001ba7ddd644cb68478e8947e4jfha', '5ff66179-c240-4703-b0d8-494592cefff5')\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200812160551_testdata.sql",
    "content": "INSERT INTO sessions (id, issued_at, expires_at, authenticated_at,created_at,updated_at, token, identity_id, active)\nVALUES ('f38cdebe-e567-42c9-a562-1bd4dee40998','2013-10-07 08:23:19','2013-10-07 08:23:19','2013-10-07 08:23:19','2013-10-07 08:23:19','2013-10-07 08:23:19', '1001ba7ddd644cb68478e8947e4jfhb', '5ff66179-c240-4703-b0d8-494592cefff5', true)\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200830121710_testdata.sql",
    "content": "INSERT INTO identity_recovery_tokens (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at)\nVALUES ('77ca3f5c-cd39-488b-9f1d-cc7166d14bdc', 'd4c66613-c4cd-47c7-8e26-a70ecc5f5b5f', false, null, 'b8293f1c-010f-45d9-b809-f3fc5365ba80', '13178936-095a-466b-abe0-36d977d3dc18', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200830130642_testdata.sql",
    "content": "-- nothing do to\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200830130643_testdata.sql",
    "content": "-- nothing do to\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200830130644_testdata.sql",
    "content": "-- nothing do to\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200830130645_testdata.sql",
    "content": "-- nothing do to\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200830130646_testdata.sql",
    "content": "INSERT INTO selfservice_verification_flows (id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, created_at, updated_at)\nVALUES ('5385c962-0295-4575-9b1b-d7eef13c0eda', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '[]', 'link', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', 'choose_method', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at)\nVALUES ('42021826-ad73-44f1-9365-6e2ba455c883', 'link', '5385c962-0295-4575-9b1b-d7eef13c0eda', '{\"action\":\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/settings/strategies/profile?flow=21c5f714-3089-49d2-b387-f244d4dd9e00\",\"method\":\"POST\",\"fields\":[{\"name\":\"csrf_token\",\"type\":\"hidden\",\"required\":true,\"value\":\"yDwSg0quCmc4kBl7lBqYwGh4W8awrc+TpeWiigZs3iemRCwqeDhGdrW3sIv8T7u742pN+Kryx/NrdRpEXcT9qA==\"},{\"name\":\"traits.email\",\"type\":\"text\",\"value\":\"foo\",\"errors\":[{\"message\":\"validation failed\"},{\"message\":\"foo is not valid email\"},{\"message\":\"foo is not valid email\"}]}]}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200830154602_testdata.sql",
    "content": "INSERT INTO identity_verification_tokens (id, token, used, used_at, identity_verifiable_address_id, selfservice_verification_flow_id, created_at, updated_at, expires_at, issued_at)\nVALUES ('ee56574d-2f0c-43f6-8d26-0062938ae330', '1001ba7ddd644cb68478e8947e4jfhc', false, null, '45e867e9-2745-4f16-8dd4-84334a252b61', '5385c962-0295-4575-9b1b-d7eef13c0eda', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO identity_verification_tokens (id, token, used, used_at, identity_verifiable_address_id, selfservice_verification_flow_id, created_at, updated_at, expires_at, issued_at)\nVALUES ('f81fd924-23bb-4cdf-8fa0-56253eff6cc9', '1001ba7ddd644cb68478e8947e4jfhd', false, null, '45e867e9-2745-4f16-8dd4-84334a252b61', NULL, '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200830172221_testdata.sql",
    "content": "INSERT INTO identity_recovery_tokens (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at)\nVALUES ('1b667e6d-8fda-4194-a765-08185185d7e4', '1001ba7ddd644cb68478e8947e4jfhe', false, null, 'b8293f1c-010f-45d9-b809-f3fc5365ba80', '13178936-095a-466b-abe0-36d977d3dc18', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20200831110752_testdata.sql",
    "content": "INSERT INTO identity_verifiable_addresses (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at) VALUES\n('b2d59320-8564-4400-a39f-a22a497a23f1', 'pending', 'email', false, 'foobar+without-code@ory.sh', null, 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20201201161451_testdata.sql",
    "content": "SELECT *\nFROM identity_credential_types\nWHERE name = 'password' OR name = 'oidc';\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210118113234_testdata.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migratest/testdata/20210126114619_testdata.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migratest/testdata/20210307130558_testdata.sql",
    "content": "INSERT INTO courier_messages (id, type, status, body, subject, recipient, created_at, updated_at) VALUES ('34948489-31dc-454a-ab3b-b2dcc75a787f', 1, 2, 'Hi, Verify your account by opening the following link: <a href=\"http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/AtsREGbtXu0RlIcwv3RPpxHEZNEcq3R9\">http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/AtsREGbtXu0RlIcwv3RPpxHEZNEcq3R9</a>', 'Please verify your email address', 'foo@ory.sh', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210307130559_testdata.sql",
    "content": "INSERT INTO courier_messages (id, status, type, recipient, body, subject, template_type, template_data, created_at, updated_at) VALUES\n('b2d59320-8564-4400-a39f-a22a497a23f1', 1, 1, 'foo@bar.com', 'body', 'subject', 'recovery_invalid', 'binary_data', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210311102338_testdata.sql",
    "content": "INSERT INTO selfservice_login_flows (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui)\nVALUES ('0bc96cc9-dda4-4700-9e42-35731f2af91e', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'api', '{}');\n\nINSERT INTO selfservice_registration_flows (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui)\nVALUES ('e2150cdc-23ac-4940-a240-6c79c27ab029', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api', '{}');\n\nINSERT INTO selfservice_settings_flows (id, request_url, issued_at, expires_at, state, identity_id, created_at, updated_at, active_method, ui)\nVALUES ('aeba85bd-1a8c-44bf-8fc3-3be83c01a3dc', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'show_form', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'profile', '{}');\n\nINSERT INTO selfservice_verification_flows (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, ui)\nVALUES ('6aae3159-b880-4cfb-a863-03b114b1371b', 'http://kratos:4433/self-service/browser/flows/verification/email', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '8xoIMa1+UkDqTt+tIHmIEHztQkk0AWk2PJhWWYDmB6dSE+RtJinnxtwH5lNNCnYyQuCF2ugy7rWjCgiwYPJNOw==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api', '{}');\n\nINSERT INTO selfservice_recovery_flows (id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type, ui)\nVALUES ('4963f305-e874-4a68-8424-a00bec679e7b', 'http://kratos:4433/self-service/browser/flows/recovery', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'link', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', 'choose_method', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api', '{}');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210410175418_testdata.sql",
    "content": "INSERT INTO selfservice_login_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui)\nVALUES ('d6aa1f23-88c9-4b9b-a850-392f48c7f9e8', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '', 'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'api', '{}');\n\nINSERT INTO selfservice_registration_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui)\nVALUES ('f1b5ed18-113a-4a98-aae7-d4eba007199c', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api', '{}');\n\nINSERT INTO selfservice_settings_flows (id, nid, request_url, issued_at, expires_at, state, identity_id, created_at, updated_at, active_method, ui)\nVALUES ('19ede218-928c-4e02-ab49-b76e12b34f31', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'show_form', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'profile', '{}');\n\nINSERT INTO selfservice_verification_flows (id, nid, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, ui)\nVALUES ('7be6c72c-c868-4b61-a1f0-1130603665d8', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'http://kratos:4433/self-service/browser/flows/verification/email', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '8xoIMa1+UkDqTt+tIHmIEHztQkk0AWk2PJhWWYDmB6dSE+RtJinnxtwH5lNNCnYyQuCF2ugy7rWjCgiwYPJNOw==', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api', '{}');\n\nINSERT INTO selfservice_recovery_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type, ui)\nVALUES ('68fb4010-84a9-4d1e-9f92-2705978ee89e', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'http://kratos:4433/self-service/browser/flows/recovery', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'link', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', 'choose_method', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api', '{}');\n\nINSERT INTO courier_messages (id, nid, status, type, recipient, body, subject, template_type, template_data, created_at, updated_at) VALUES\n('b821adf0-a067-4b3c-9f90-cac496d02a92', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 1, 1, 'foo@bar.com', 'body', 'subject', 'recovery_invalid', 'binary_data', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO identity_verifiable_addresses (id, nid, status, via, verified, value, verified_at, identity_id, created_at, updated_at) VALUES\n('d4718a67-aec2-418d-8173-6ebc7bde3b86', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'pending', 'email', false, 'foobar+11345642c6c0@ory.sh', null, 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\nINSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at) VALUES ('196d8c1e-4f04-40f0-94b3-5ec43996b28a', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'default', '{\"email\":\"foobar@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\nINSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at) VALUES ('ed253b2c-48ed-4c58-9b6f-1dc963c30a66', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'default', '{\"email\":\"bazbar@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210504121624_testdata.sql",
    "content": "INSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at, state) VALUES ('2ae6a5a7-2983-49e7-a4d8-7740b37c88cb','884f556e-eb3a-4b9f-bee3-11345642c6c0', 'default', '{\"email\":\"d7b10@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'active');\nINSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at) VALUES ('359963ec-b09b-4ea0-aece-fb4dd95f304a','884f556e-eb3a-4b9f-bee3-11345642c6c0', 'default', '{\"email\":\"d7b11@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210618103120_testdata.sql",
    "content": "INSERT INTO sessions (id, nid, issued_at, expires_at, authenticated_at,created_at,updated_at, token, identity_id, active, logout_token)\nVALUES ('dcde5aaa-f789-4d3d-ae1f-76da8d57e67c','884f556e-eb3a-4b9f-bee3-11345642c6c0','2013-10-07 08:23:19','2013-10-07 08:23:19','2013-10-07 08:23:19','2013-10-07 08:23:19','2013-10-07 08:23:19', 'eVwBt7UWPweVwBt7UWPw', '5ff66179-c240-4703-b0d8-494592cefff5', true, '123eVwBt7UWPweVwBt7UWPw')\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210805112414_testdata.sql",
    "content": "INSERT INTO selfservice_settings_flows (id, nid, request_url, issued_at, expires_at, state, identity_id, created_at, updated_at, active_method, ui, internal_context)\nVALUES ('8248bb5d-8ef7-45e3-8e07-9e2003dd5352',  '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'http://kratos:4433/self-service/browser/flows/settings', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'show_form', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'profile', '{}', '{\"foo\":\"bar\"}');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210805122535_testdata.sql",
    "content": "-- nothing to do here because the schema did not change\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210810153530_testdata.sql",
    "content": "INSERT INTO sessions (id, nid, issued_at, expires_at, authenticated_at, created_at, updated_at, token, identity_id,\n                      active, logout_token, aal, authentication_methods)\nVALUES ('7458af86-c1d8-401c-978a-8da89133f78b', '884f556e-eb3a-4b9f-bee3-11345642c6c0', '2013-10-07 08:23:19',\n        '2080-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19',\n        'eVwBt7UAAAAVwBt7UWPw', '5ff66179-c240-4703-b0d8-494592cefff5', true, '123eVwBt7UAAAeVwBt7UWPw', 'aal2',\n        '[{\"method\":\"password\"},{\"method\":\"totp\"}]');\n\nINSERT INTO selfservice_login_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, created_at,\n                                     updated_at, forced, type, ui, requested_aal)\nVALUES ('1fb23c75-b809-42cc-8984-6ca2d0a1192f', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '',\n        'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'api', '{}', 'aal2');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210813150152_testdata.sql",
    "content": "-- nothing to do here because the schema did not change\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210816113956_testdata.sql",
    "content": "-- nothing to do here because the schema did not change\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210816142650_testdata.sql",
    "content": "INSERT INTO selfservice_login_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, created_at,\n                                     updated_at, forced, type, ui, requested_aal, internal_context)\nVALUES ('38caf592-b042-4551-b92f-8d5223c2a4e2', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/login', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '',\n        'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'api', '{}', 'aal2', '{\"foo\":\"bar\"}');\n\nINSERT INTO selfservice_registration_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token,\n                                            created_at, updated_at, type, ui, internal_context)\nVALUES ('8f32efdc-f6fc-4c27-a3c2-579d109eff60', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/registration', '2013-10-07 08:23:19', '2013-10-07 08:23:19',\n        'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api', '{}', '{\"foo\":\"bar\"}');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210817181232_testdata.sql",
    "content": "INSERT INTO identity_credential_identifiers (id, nid, identifier, identity_credential_id, created_at, updated_at, identity_credential_type_id)\nVALUES ('2672f198-4795-437c-8e14-56459b1d941a', '884f556e-eb3a-4b9f-bee3-11345642c6c0','foo1@ory.sh', '35b60ecf-30f9-42d6-bf5d-47ad41148691',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', '22bff9ae-f5aa-45d7-803b-97ec0b4e7b32');\n\nINSERT INTO identity_credential_identifiers (id, nid, identifier, identity_credential_id, created_at, updated_at, identity_credential_type_id)\nVALUES ('10985ed1-5b6e-4012-ac10-03d87df65618', '884f556e-eb3a-4b9f-bee3-11345642c6c0','foo2@ory.sh', '74ac2d31-bccb-442f-a792-7b8bb14817f8',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', '6b213fa0-e6ad-46cb-8878-b088d2ce2e3c');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210829131458_testdata.sql",
    "content": "-- empty because it is just an SQL insert\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20210913095309_testdata.sql",
    "content": "UPDATE identity_recovery_tokens SET identity_id = 'a251ebc2-880c-4f76-a8f3-38e6940eab0e' WHERE id = '5529d454-2946-404e-b681-d950f8657fd0';\nUPDATE identity_recovery_tokens SET identity_id = 'a251ebc2-880c-4f76-a8f3-38e6940eab0e' WHERE id = '77ca3f5c-cd39-488b-9f1d-cc7166d14bdc';\nUPDATE identity_recovery_tokens SET identity_id = 'a251ebc2-880c-4f76-a8f3-38e6940eab0e' WHERE id = '1b667e6d-8fda-4194-a765-08185185d7e4';\n\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20220118104539_testdata.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migratest/testdata/20220301102701_testdata.sql",
    "content": "INSERT INTO identity_credentials (id, nid, config, identity_credential_type_id, identity_id, created_at, updated_at,\n                                  version)\nVALUES ('4cefc264-4291-4abc-8f26-cc0217874f14', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        '{\"totp_url\":\"otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example\"}',\n        '8071b37b-0d54-4c6f-8234-72cffb4ce784', '5ff66179-c240-4703-b0d8-494592cefff5', '2013-10-07 08:23:19',\n        '2013-10-07 08:23:19', 0);\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20220420102701_testdata.sql",
    "content": "INSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at, metadata_public, metadata_admin) VALUES ('308929d3-41a2-43fe-a33c-75308539d841', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'default', '{\"email\":\"bazbar@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '{\"foo\":\"bar\"}', '{\"baz\":\"bar\"}');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20220607000001_testdata.sql",
    "content": "INSERT INTO selfservice_login_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui, requested_aal, internal_context, oauth2_login_challenge)\nVALUES ('349c945a-60f8-436a-a301-7a42c92604f9', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/login?login_challenge=3caddfd599034bce83ffcae36f42dff7', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '',\n        'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'browser', '{}', 'aal2', '{\"foo\":\"bar\"}', '3caddfd5-9903-4bce-83ff-cae36f42dff7');\n\nINSERT INTO selfservice_registration_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui, internal_context, oauth2_login_challenge)\nVALUES ('ef18b06e-4700-4021-9949-ef783cd86be8', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/registration?login_challenge=', '2013-10-07 08:23:19', '2013-10-07 08:23:19',\n        'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'browser', '{}', '{\"foo\":\"bar\"}', '3caddfd5-9903-4bce-83ff-cae36f42dff7');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20220902141902_testdata.sql",
    "content": "INSERT INTO identity_verification_tokens (id, token, used, used_at, identity_verifiable_address_id, selfservice_verification_flow_id, created_at, updated_at, expires_at, issued_at, nid)\nVALUES ('ee56574d-2f1c-43f6-8d26-0062938ae330', '1001ba7ddd644cb68478e8947e4jfhe', false, null, '45e867e9-2745-4f16-8dd4-84334a252b61', '5385c962-0295-4575-9b1b-d7eef13c0eda', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '884f556e-eb3a-4b9f-bee3-11345642c6c0');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20220907132836_testdata.sql",
    "content": "INSERT INTO sessions (id, nid, issued_at, expires_at, authenticated_at, created_at, updated_at, token, identity_id,\n                      active, logout_token, aal, authentication_methods)\nVALUES ('7458af86-c1d8-401c-978a-8da89133f98b', '884f556e-eb3a-4b9f-bee3-11345642c6c0', '2013-10-07 08:23:19',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19',\n        'eVwBt7UAAAAVwBt7XAMw', '5ff66179-c240-4703-b0d8-494592cefff5', true, '123eVwBt7UAAAeVwBt7XAMw', 'aal2',\n        '[{\"method\":\"password\"},{\"method\":\"totp\"}]');\n\nINSERT INTO session_devices (id, nid, session_id, ip_address, user_agent, location, created_at, updated_at)\nVALUES ('884f556e-eb3a-4b9f-bee3-11763642c6c0', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        '7458af86-c1d8-401c-978a-8da89133f98b', '54.155.246.232',\n        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36',\n        'Munich, Germany', '2022-08-07 08:23:19', '2022-08-09 08:35:19');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20220929124401_testdata.sql",
    "content": "INSERT INTO identity_recovery_codes (id, code, used_at, identity_recovery_address_id, code_type, expires_at, issued_at, selfservice_recovery_flow_id, created_at, updated_at, nid, identity_id) VALUES (\n    '8f75f5d9-9cb4-4848-9a73-9344f686f8a6',\n    '7eb71370d8497734ec78dfe613bf0f08967e206d2b5c2fc1243be823cfcd57a7',\n    null,\n    'b8293f1c-010f-45d9-b809-f3fc5365ba80',\n    1,\n    '2022-08-18 08:28:18',\n    '2022-08-18 07:28:18',\n    '68fb4010-84a9-4d1e-9f92-2705978ee89e',\n    '2022-08-18 07:28:18',\n    '2022-08-18 07:28:18',\n    '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n    '308929d3-41a2-43fe-a33c-75308539d841'\n)"
  },
  {
    "path": "persistence/sql/migratest/testdata/20221103120601_testdata.sql",
    "content": "INSERT INTO\n    selfservice_verification_flows (\n        id,\n        nid,\n        request_url,\n        issued_at,\n        expires_at,\n        csrf_token,\n        created_at,\n        updated_at,\n        type,\n        ui,\n        submit_count\n    )\nVALUES\n    (\n        '81f74e5d-1fa5-4e1b-a9bf-e9511926047c',\n        '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/verification/email',\n        '2022-11-03 08:23:19',\n        '2022-11-03 08:23:19',\n        '8xoIMa1+UkDqTt+tIHmIEHztQkk0AWk2PJhWWYDmB6dSE+RtJinnxtwH5lNNCnYyQuCF2ugy7rWjCgiwYPJNOw==',\n        '2022-11-03 08:23:19',\n        '2022-11-03 08:23:19',\n        'api',\n        '{}',\n        0\n    );\n\nINSERT INTO\n    identity_verification_codes (\n        id,\n        code_hmac,\n        used_at,\n        identity_verifiable_address_id,\n        expires_at,\n        issued_at,\n        selfservice_verification_flow_id,\n        created_at,\n        updated_at,\n        nid\n    )\nVALUES\n    (\n        '5ab4e72b-332a-48ab-b1e9-3b1ef1ba5b60',\n        '7eb71370d8497734ec78dfe613bf0f08967e206d2b5c2fc1243be823cfcd57a7',\n        null,\n        '45e867e9-2745-4f16-8dd4-84334a252b61',\n        '2022-11-03 08:28:18',\n        '2022-11-03 08:28:18',\n        '81f74e5d-1fa5-4e1b-a9bf-e9511926047c',\n        '2022-11-03 08:28:18',\n        '2022-11-03 08:28:18',\n        '884f556e-eb3a-4b9f-bee3-11345642c6c0'\n    );"
  },
  {
    "path": "persistence/sql/migratest/testdata/20221205095201_testdata.sql",
    "content": "INSERT INTO\n    courier_messages (\n        id,\n        type,\n        status,\n        body,\n        subject,\n        recipient,\n        created_at,\n        updated_at,\n        template_type,\n        nid,\n        send_count\n    )\nVALUES\n    (\n        'd9d4401c-08a1-434c-8ab5-4a7edefde351',\n        1,\n        2,\n        'Hi, Verify your account by opening the following link: <a href=http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/u9ZcBr5HbRTR8f53Qj2Ng3KR8Mv1Zjdb>http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/verification/email/confirm/u9ZcBr5HbRTR8f53Qj2Ng3KR8Mv1Zjdb</a>',\n        'Please verify your email address',\n        'foobar@ory.sh',\n        '2013-10-07 08:23:19',\n        '2013-10-07 08:23:19',\n        'verification_valid',\n        '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        4\n    );\n\nINSERT INTO\n    courier_message_dispatches\nVALUES\n    (\n        'ea0c9a03-44ce-43a8-8c5d-52f468991cb9',\n        'd9d4401c-08a1-434c-8ab5-4a7edefde351',\n        'success',\n        '{}',\n        '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        '2013-10-07 08:23:19',\n        '2013-10-07 08:23:19'\n    )\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20230313141439_testdata.sql",
    "content": "INSERT INTO sessions (id, nid, issued_at, expires_at, authenticated_at, created_at, updated_at, token, identity_id,\n                      active, logout_token, aal, authentication_methods)\nVALUES ('068f6bb6-d15f-436d-94f7-b3fd0489c9ef', '884f556e-eb3a-4b9f-bee3-11345642c6c0', '2013-10-07 08:23:19',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '2013-10-07 08:23:19',\n        'ory_lo_5e5aad0f7a4143452df3d23733a68e3', '5ff66179-c240-4703-b0d8-494592cefff5', true, 'ory_st_5e5aad0f7a4143452df3d23733a68e2', 'aal2',\n        '[{\"method\":\"password\"},{\"method\":\"totp\"}]');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20230614205200_testdata.sql",
    "content": "INSERT INTO selfservice_login_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, created_at,\n                                     updated_at, forced, type, ui, internal_context, oauth2_login_challenge_data)\nVALUES ('cccccccc-dda4-4700-9e42-35731f2af91e',\n        '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/login',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', '',\n        'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'api', '{}', '{\"foo\":\"bar\"}', 'challenge data');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20230705000000_testdata.sql",
    "content": "INSERT INTO selfservice_login_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, created_at,\n                                     updated_at, forced, type, ui, internal_context, oauth2_login_challenge_data)\nVALUES ('cccccccc-dda4-4700-9e42-35731f2af911',\n        '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', '',\n        'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'api', '{}', '{\"foo\":\"bar\"}', 'challenge data');\n\nINSERT INTO selfservice_verification_flows (id,\n                                            nid,\n                                            request_url,\n                                            issued_at,\n                                            expires_at,\n                                            csrf_token,\n                                            created_at,\n                                            updated_at,\n                                            type,\n                                            ui,\n                                            submit_count)\nVALUES ('81f74e5d-1fa5-4e1b-a9bf-e95119260471',\n        '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email/self-service/browser/flows/verification/email',\n        '2022-11-03 08:23:19',\n        '2022-11-03 08:23:19',\n        '8xoIMa1+UkDqTt+tIHmIEHztQkk0AWk2PJhWWYDmB6dSE+RtJinnxtwH5lNNCnYyQuCF2ugy7rWjCgiwYPJNOw==',\n        '2022-11-03 08:23:19',\n        '2022-11-03 08:23:19',\n        'api',\n        '{}',\n        0);\n\n\nINSERT INTO selfservice_registration_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token,\n                                            created_at, updated_at, type, ui, internal_context, oauth2_login_challenge)\nVALUES ('ef18b06e-4700-4021-9949-ef783cd86be1', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge=',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19',\n        'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'browser', '{}', '{\"foo\":\"bar\"}',\n        '3caddfd5-9903-4bce-83ff-cae36f42dff7');\n\nINSERT INTO selfservice_settings_flows (id, nid, request_url, issued_at, expires_at, state, identity_id, created_at,\n                                        updated_at, active_method, ui,internal_context)\nVALUES ('19ede218-928c-4e02-ab49-b76e12b34f32', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings/self-service/browser/flows/settings',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'show_form', 'a251ebc2-880c-4f76-a8f3-38e6940eab0e',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'profile', '{}', '{}');\n\nINSERT INTO selfservice_verification_flows (id, nid, request_url, issued_at, expires_at, csrf_token, created_at,\n                                            updated_at, type, ui)\nVALUES ('7be6c72c-c868-4b61-a1f0-1130603665d1', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/verification/email', '2013-10-07 08:23:19',\n        '2013-10-07 08:23:19',\n        '8xoIMa1+UkDqTt+tIHmIEHztQkk0AWk2PJhWWYDmB6dSE+RtJinnxtwH5lNNCnYyQuCF2ugy7rWjCgiwYPJNOw==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api', '{}');\n\nINSERT INTO selfservice_recovery_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token, state,\n                                        recovered_identity_id, created_at, updated_at, type, ui)\nVALUES ('68fb4010-84a9-4d1e-9f92-2705978ee891', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery/self-service/browser/flows/recovery',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'link',\n        'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==', 'choose_method',\n        'a251ebc2-880c-4f76-a8f3-38e6940eab0e', '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'api', '{}');\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20230706000000_testdata.sql",
    "content": "INSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at, metadata_public, metadata_admin,\n                        available_aal)\nVALUES ('0149ce5f-76a8-4efe-b2e3-431b8c6cceb6', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'default',\n        '{\"email\":\"bazbar@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '{\"foo\":\"bar\"}', '{\"baz\":\"bar\"}',\n        'aal1');\n\nINSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at, metadata_public, metadata_admin,\n                        available_aal)\nVALUES ('0149ce5f-76a8-4efe-b2e3-431b8c6cceb7', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'default',\n        '{\"email\":\"bazbarbar@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '{\"foo\":\"bar\"}', '{\"baz\":\"bar\"}',\n        NULL);\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20230707133700_testdata.sql",
    "content": "INSERT INTO selfservice_login_flows (id,nid, request_url, issued_at, expires_at, active_method, csrf_token, created_at,\n                                     updated_at, forced, type, ui, internal_context, oauth2_login_challenge_data, state)\nVALUES ('00b1517f-2467-4aaf-b0a5-82b4a27dcaf5',\n        '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login/self-service/browser/flows/login',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', '',\n        'fpeVSZ9ZH7YvUkhXsOVEIssxbfauh5lcoQSYxTcN0XkMneg1L42h+HtvisjlNjBF4ElcD2jApCHoJYq2u9sVWg==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', false, 'api', '{}', '{\"foo\":\"bar\"}', 'challenge data', 'choose_method');\n\nINSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at, metadata_public, metadata_admin,\n                        available_aal)\nVALUES ('28ff0031-190b-4253-bd15-14308dec013e', '884f556e-eb3a-4b9f-bee3-11345642c6c0', 'default',\n        '{\"email\":\"bazbarbarfoo@ory.sh\"}', '2013-10-07 08:23:19', '2013-10-07 08:23:19', '{\"foo\":\"bar\"}', '{\"baz\":\"bar\"}',\n        NULL);\n\nINSERT INTO identity_login_codes (id, code, address, address_type, used_at, expires_at, issued_at, selfservice_login_flow_id, identity_id,\n                                  created_at, updated_at, nid)\nVALUES ('bd292366-af32-4ba6-bdf0-11d6d1a217f3',\n'7eb71370d8497734ec78dfe613bf0f08967e206d2b5c2fc1243be823cfcd57a7',\n'bazbarbarfoo@ory.com',\n'email',\nnull,\n'2022-08-18 08:28:18',\n'2022-08-18 07:28:18',\n'00b1517f-2467-4aaf-b0a5-82b4a27dcaf5',\n'28ff0031-190b-4253-bd15-14308dec013e',\n'2022-08-18 07:28:18',\n'2022-08-18 07:28:18',\n'884f556e-eb3a-4b9f-bee3-11345642c6c0'\n)\n"
  },
  {
    "path": "persistence/sql/migratest/testdata/20230707133701_testdata.sql",
    "content": "INSERT INTO selfservice_registration_flows (id, nid, request_url, issued_at, expires_at, active_method, csrf_token,\n                                            created_at, updated_at, type, ui, internal_context, oauth2_login_challenge, state)\nVALUES ('69c80296-36cd-4afc-921a-15369cac5bf0', '884f556e-eb3a-4b9f-bee3-11345642c6c0',\n        'http://kratos:4433/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge/self-service/browser/flows/registration?login_challenge=',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19',\n        'password', 'vYYuhWXBfXKzBC+BlnbDmXfBKsUWY6SU/v04gHF9GYzPjFP51RXDPOc57R7Dpbf+XLkbPNAkmem33Crz/avdrw==',\n        '2013-10-07 08:23:19', '2013-10-07 08:23:19', 'browser', '{}', '{\"foo\":\"bar\"}',\n        '3caddfd5-9903-4bce-83ff-cae36f42dff7', 'choose_method');\n\nINSERT INTO identity_registration_codes (id, address, address_type, code, used_at, expires_at, issued_at, selfservice_registration_flow_id,\n                                  created_at, updated_at, nid)\nVALUES ('f1f66a69-ce02-4a12-9591-9e02dda30a0d',\n'example@example.com',\n'email',\n'7eb71370d8497734ec78dfe613bf0f08967e206d2b5c2fc1243be823cfcd57a7',\nnull,\n'2022-08-18 08:28:18',\n'2022-08-18 07:28:18',\n'69c80296-36cd-4afc-921a-15369cac5bf0',\n'2022-08-18 07:28:18',\n'2022-08-18 07:28:18',\n'884f556e-eb3a-4b9f-bee3-11345642c6c0'\n)\n"
  },
  {
    "path": "persistence/sql/migrations/go/20251105000000000000_identity_id_not_null_fks.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage gomigrations\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/popx\"\n)\n\nvar backfillIdentityID = []popx.Migration{\n\t{\n\t\tVersion:    \"20251105000000000001\",\n\t\tPath:       path(),\n\t\tName:       \"Backfill column identity_id in identity_credential_identifiers\",\n\t\tDirection:  \"up\",\n\t\tType:       \"go\",\n\t\tDBType:     \"cockroach\",\n\t\tAutocommit: true,\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\t_, err := c.Store.Exec(\"CREATE INDEX IF NOT EXISTS ici_identity_id_backfill_storing ON identity_credential_identifiers (identity_id ASC) STORING (identity_credential_id) NOT VISIBLE\")\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\t\t\tfor {\n\t\t\t\tres, err := c.Store.Exec(`\n\t\t\t\t\tUPDATE\n\t\t\t\t\t\tidentity_credential_identifiers@ici_identity_id_backfill_storing ici\n\t\t\t\t\tSET\n\t\t\t\t\t\tidentity_id = ic.identity_id\n\t\t\t\t\tFROM\n\t\t\t\t\t\tidentity_credentials ic\n\t\t\t\t\tWHERE\n\t\t\t\t\t\tici.identity_credential_id = ic.id\n\t\t\t\t\t\t-- AND ici.nid = ic.nid -- not needed because JOIN predicate is on primary key\n\t\t\t\t\t\tAND ici.identity_id IS NULL\n\t\t\t\t\tLIMIT 10000`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tn, err := res.RowsAffected()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tif n == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"Backfilled column identity_id for %d rows in identity_credential_identifiers table\\n\", n)\n\t\t\t}\n\t\t\t_, err = c.Store.Exec(\"DROP INDEX identity_credential_identifiers@ici_identity_id_backfill_storing\")\n\t\t\treturn errors.WithStack(err)\n\t\t},\n\t},\n\t{\n\t\tVersion:    \"20251105000000000001\",\n\t\tPath:       path(),\n\t\tName:       \"Backfill column identity_id in identity_credential_identifiers\",\n\t\tDirection:  \"up\",\n\t\tType:       \"go\",\n\t\tDBType:     \"postgres\",\n\t\tAutocommit: true,\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\tfor {\n\t\t\t\tres, err := c.Store.Exec(`\n\t\t\t\t\tWITH to_update AS (\n\t\t\t\t\t\tSELECT ici.id, ic.identity_id\n\t\t\t\t\t\tFROM identity_credential_identifiers ici\n\t\t\t\t\t\tJOIN identity_credentials ic ON ici.identity_credential_id = ic.id AND ici.nid = ic.nid\n\t\t\t\t\t\tWHERE ici.identity_id IS NULL\n\t\t\t\t\t\tLIMIT 10000\n\t\t\t\t\t)\n\t\t\t\t\tUPDATE identity_credential_identifiers ici\n\t\t\t\t\tSET identity_id = to_update.identity_id\n\t\t\t\t\tFROM to_update\n\t\t\t\t\tWHERE ici.id = to_update.id`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tn, err := res.RowsAffected()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tif n == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"Backfilled column identity_id for %d rows in identity_credential_identifiers table\\n\", n)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t},\n\t{\n\t\tVersion:    \"20251105000000000001\",\n\t\tPath:       path(),\n\t\tName:       \"Backfill column identity_id in identity_credential_identifiers\",\n\t\tDirection:  \"up\",\n\t\tType:       \"go\",\n\t\tDBType:     \"mysql\",\n\t\tAutocommit: true,\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\tfor {\n\t\t\t\tres, err := c.Store.Exec(`\n\t\t\t\t\tUPDATE identity_credential_identifiers ici\n\t\t\t\t\tJOIN (\n\t\t\t\t\t\tSELECT ici2.id\n\t\t\t\t\t\tFROM identity_credential_identifiers ici2\n\t\t\t\t\t\tJOIN identity_credentials ic ON ici2.identity_credential_id = ic.id AND ici2.nid = ic.nid\n\t\t\t\t\t\tWHERE ici2.identity_id IS NULL\n\t\t\t\t\t\tLIMIT 10000\n\t\t\t\t\t) t ON ici.id = t.id\n\t\t\t\t\tJOIN identity_credentials ic ON ici.identity_credential_id = ic.id\n\t\t\t\t\tSET ici.identity_id = ic.identity_id`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tn, err := res.RowsAffected()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tif n == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"Backfilled column identity_id for %d rows in identity_credential_identifiers table\\n\", n)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t},\n\t{\n\t\tVersion:    \"20251105000000000001\",\n\t\tPath:       path(),\n\t\tName:       \"Backfill column identity_id in identity_credential_identifiers (noop)\",\n\t\tDirection:  \"up\",\n\t\tType:       \"go\",\n\t\tDBType:     \"sqlite3\",\n\t\tAutocommit: false,\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\treturn nil // nothing, see 20251104000000000000_identifiers_devices_identity_id.sqlite.up.sql\n\t\t},\n\t},\n\t{\n\t\tVersion:   \"20251105000000000001\",\n\t\tPath:      path(),\n\t\tName:      \"Revert backfill column identity_id in identity_credential_identifiers (noop)\",\n\t\tDirection: \"down\",\n\t\tType:      \"go\",\n\t\tDBType:    \"all\",\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\treturn nil\n\t\t},\n\t},\n\t{\n\t\tVersion:    \"20251105000000000002\",\n\t\tPath:       path(),\n\t\tName:       \"Backfill column identity_id in session_devices\",\n\t\tDirection:  \"up\",\n\t\tType:       \"go\",\n\t\tDBType:     \"cockroach\",\n\t\tAutocommit: true,\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\tfor {\n\t\t\t\tres, err := c.Store.Exec(`\n\t\t\t\t\tUPDATE\n\t\t\t\t\t\tsession_devices sd\n\t\t\t\t\tSET\n\t\t\t\t\t\tidentity_id = s.identity_id\n\t\t\t\t\tFROM\n\t\t\t\t\t\tsessions s\n\t\t\t\t\tWHERE\n\t\t\t\t\t\tsd.session_id = s.id\n\t\t\t\t\t\tAND sd.nid = s.nid\n\t\t\t\t\t\tAND sd.identity_id IS NULL\n\t\t\t\t\tLIMIT 10000`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tn, err := res.RowsAffected()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tif n == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"Backfilled column identity_id for %d rows in session_devices table\\n\", n)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t},\n\t{\n\t\tVersion:    \"20251105000000000002\",\n\t\tPath:       path(),\n\t\tName:       \"Backfill column identity_id in session_devices\",\n\t\tDirection:  \"up\",\n\t\tType:       \"go\",\n\t\tDBType:     \"postgres\",\n\t\tAutocommit: true,\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\tfor {\n\t\t\t\tres, err := c.Store.Exec(`\n\t\t\t\t\tWITH to_update AS (\n\t\t\t\t\t\tSELECT sd.id, s.identity_id\n\t\t\t\t\t\tFROM session_devices sd\n\t\t\t\t\t\tJOIN sessions s ON sd.session_id = s.id AND sd.nid = s.nid\n\t\t\t\t\t\tWHERE sd.identity_id IS NULL\n\t\t\t\t\t\tLIMIT 10000\n\t\t\t\t\t)\n\t\t\t\t\tUPDATE session_devices sd\n\t\t\t\t\tSET identity_id = to_update.identity_id\n\t\t\t\t\tFROM to_update\n\t\t\t\t\tWHERE sd.id = to_update.id`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tn, err := res.RowsAffected()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tif n == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"Backfilled column identity_id for %d rows in session_devices table\\n\", n)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t},\n\t{\n\t\tVersion:    \"20251105000000000002\",\n\t\tPath:       path(),\n\t\tName:       \"Backfill column identity_id in session_devices\",\n\t\tDirection:  \"up\",\n\t\tType:       \"go\",\n\t\tDBType:     \"mysql\",\n\t\tAutocommit: true,\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\tfor {\n\t\t\t\tres, err := c.Store.Exec(`\n\t\t\t\t\tUPDATE session_devices sd\n\t\t\t\t\tJOIN (\n\t\t\t\t\t\tSELECT sd2.id\n\t\t\t\t\t\tFROM session_devices sd2\n\t\t\t\t\t\tJOIN sessions s2 ON sd2.session_id = s2.id AND sd2.nid = s2.nid\n\t\t\t\t\t\tWHERE sd2.identity_id IS NULL\n\t\t\t\t\t\tLIMIT 10000\n\t\t\t\t\t) t ON sd.id = t.id\n\t\t\t\t\tJOIN sessions s ON sd.session_id = s.id\n\t\t\t\t\tSET sd.identity_id = s.identity_id`)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tn, err := res.RowsAffected()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tif n == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"Backfilled column identity_id for %d rows in session_devices table\\n\", n)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t},\n\t{\n\t\tVersion:    \"20251105000000000002\",\n\t\tPath:       path(),\n\t\tName:       \"Backfill column identity_id in session_devices (noop)\",\n\t\tDirection:  \"up\",\n\t\tType:       \"go\",\n\t\tDBType:     \"sqlite3\",\n\t\tAutocommit: false,\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\treturn nil // nothing, see 20251104000000000000_identifiers_devices_identity_id.sqlite.up.sql\n\t\t},\n\t},\n\n\t{\n\t\tVersion:    \"20251105000000000002\",\n\t\tPath:       path(),\n\t\tName:       \"Revert backfill column identity_id in session_devices (noop)\",\n\t\tDirection:  \"down\",\n\t\tType:       \"go\",\n\t\tDBType:     \"all\",\n\t\tAutocommit: false,\n\t\tRunner: func(m popx.Migration, c *pop.Connection) error {\n\t\t\treturn nil\n\t\t},\n\t},\n}\n"
  },
  {
    "path": "persistence/sql/migrations/go/gomigrations.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage gomigrations\n\nimport (\n\t\"embed\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"slices\"\n)\n\nvar (\n\tAll = slices.Concat(\n\t\tbackfillIdentityID,\n\t)\n\n\t//go:embed *.go\n\tSrc embed.FS\n)\n\nfunc path() string {\n\t_, file, line, _ := runtime.Caller(1)\n\treturn fmt.Sprintf(\"%s:%d\", filepath.Base(file), line)\n}\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000001_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identity_credentials\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identity_credential_types\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identities\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000001_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"traits_schema_id\" VARCHAR (2048) NOT NULL,\n\"traits\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"identity_credential_types\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"name\" VARCHAR (32) NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"identity_credentials\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"config\" json NOT NULL,\n\"identity_credential_type_id\" UUID NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_credentials_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_credentials_identity_credential_types_id_fk\" FOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"identity_credential_identifiers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identifier\" VARCHAR (255) NOT NULL,\n\"identity_credential_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_credential_identifiers_identity_credentials_id_fk\" FOREIGN KEY (\"identity_credential_id\") REFERENCES \"identity_credentials\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000001_identities.mysql.down.sql",
    "content": "DROP TABLE `identity_credential_identifiers`;\nDROP TABLE `identity_credentials`;\nDROP TABLE `identity_credential_types`;\nDROP TABLE `identities`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000001_identities.mysql.up.sql",
    "content": "CREATE TABLE `identities` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`traits_schema_id` VARCHAR (2048) NOT NULL,\n`traits` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nCREATE TABLE `identity_credential_types` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`name` VARCHAR (32) NOT NULL\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_credential_types_name_idx` ON `identity_credential_types` (`name`);\nCREATE TABLE `identity_credentials` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`config` JSON NOT NULL,\n`identity_credential_type_id` char(36) NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade,\nFOREIGN KEY (`identity_credential_type_id`) REFERENCES `identity_credential_types` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `identity_credential_identifiers` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`identifier` VARCHAR (255) NOT NULL,\n`identity_credential_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_credential_id`) REFERENCES `identity_credentials` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_credential_identifiers_identifier_idx` ON `identity_credential_identifiers` (`identifier`);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000001_identities.postgres.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";\nDROP TABLE \"identity_credentials\";\nDROP TABLE \"identity_credential_types\";\nDROP TABLE \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000001_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"traits_schema_id\" VARCHAR (2048) NOT NULL,\n\"traits\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nCREATE TABLE \"identity_credential_types\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"name\" VARCHAR (32) NOT NULL\n);\nCREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name);\nCREATE TABLE \"identity_credentials\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"config\" jsonb NOT NULL,\n\"identity_credential_type_id\" UUID NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"identity_credential_identifiers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identifier\" VARCHAR (255) NOT NULL,\n\"identity_credential_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_credential_id\") REFERENCES \"identity_credentials\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000001_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";\nDROP TABLE \"identity_credentials\";\nDROP TABLE \"identity_credential_types\";\nDROP TABLE \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000001_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" TEXT PRIMARY KEY,\n\"traits_schema_id\" TEXT NOT NULL,\n\"traits\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nCREATE TABLE \"identity_credential_types\" (\n\"id\" TEXT PRIMARY KEY,\n\"name\" TEXT NOT NULL\n);\nCREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name);\nCREATE TABLE \"identity_credentials\" (\n\"id\" TEXT PRIMARY KEY,\n\"config\" TEXT NOT NULL,\n\"identity_credential_type_id\" char(36) NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade,\nFOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON DELETE cascade\n);\nCREATE TABLE \"identity_credential_identifiers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000002_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_login_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_registration_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_registration_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_profile_management_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000002_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_login_request_methods_selfservice_login_requests_id_fk\" FOREIGN KEY (\"selfservice_login_request_id\") REFERENCES \"selfservice_login_requests\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_registration_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_registration_request_methods_selfservice_registration_requests_id_fk\" FOREIGN KEY (\"selfservice_registration_request_id\") REFERENCES \"selfservice_registration_requests\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" json NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_profile_management_requests_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000002_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_login_request_methods`;\nDROP TABLE `selfservice_login_requests`;\nDROP TABLE `selfservice_registration_request_methods`;\nDROP TABLE `selfservice_registration_requests`;\nDROP TABLE `selfservice_profile_management_requests`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000002_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_login_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`active_method` VARCHAR (32) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_login_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_login_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_login_request_id`) REFERENCES `selfservice_login_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_registration_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`active_method` VARCHAR (32) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_registration_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_registration_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_registration_request_id`) REFERENCES `selfservice_registration_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_profile_management_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`form` JSON NOT NULL,\n`update_successful` bool NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000002_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\";\nDROP TABLE \"selfservice_login_requests\";\nDROP TABLE \"selfservice_registration_request_methods\";\nDROP TABLE \"selfservice_registration_requests\";\nDROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000002_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nCREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_login_request_id\") REFERENCES \"selfservice_login_requests\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"selfservice_registration_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nCREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_registration_request_id\") REFERENCES \"selfservice_registration_requests\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" jsonb NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000002_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\";\nDROP TABLE \"selfservice_login_requests\";\nDROP TABLE \"selfservice_registration_request_methods\";\nDROP TABLE \"selfservice_registration_requests\";\nDROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000002_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nCREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_login_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_login_request_id) REFERENCES selfservice_login_requests (id) ON DELETE cascade\n);\nCREATE TABLE \"selfservice_registration_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nCREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_registration_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_registration_request_id) REFERENCES selfservice_registration_requests (id) ON DELETE cascade\n);\nCREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000003_sessions.cockroach.down.sql",
    "content": "DROP TABLE \"sessions\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000003_sessions.cockroach.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"authenticated_at\" timestamp NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"sessions_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000003_sessions.mysql.down.sql",
    "content": "DROP TABLE `sessions`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000003_sessions.mysql.up.sql",
    "content": "CREATE TABLE `sessions` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`authenticated_at` DATETIME NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000003_sessions.postgres.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000003_sessions.postgres.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"authenticated_at\" timestamp NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000003_sessions.sqlite3.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000003_sessions.sqlite3.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000004_errors.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000004_errors.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"errors\" json NOT NULL,\n\"seen_at\" timestamp NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000004_errors.mysql.down.sql",
    "content": "DROP TABLE `selfservice_errors`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000004_errors.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_errors` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`errors` JSON NOT NULL,\n`seen_at` DATETIME NOT NULL,\n`was_seen` bool NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000004_errors.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000004_errors.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"errors\" jsonb NOT NULL,\n\"seen_at\" timestamp NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000004_errors.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000004_errors.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000005_identities.mysql.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255);\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000005_identities.mysql.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255) BINARY;\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000006_courier.cockroach.down.sql",
    "content": "DROP TABLE \"courier_messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000006_courier.cockroach.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"type\" int NOT NULL,\n\"status\" int NOT NULL,\n\"body\" VARCHAR (255) NOT NULL,\n\"subject\" VARCHAR (255) NOT NULL,\n\"recipient\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000006_courier.mysql.down.sql",
    "content": "DROP TABLE `courier_messages`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000006_courier.mysql.up.sql",
    "content": "CREATE TABLE `courier_messages` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`type` INTEGER NOT NULL,\n`status` INTEGER NOT NULL,\n`body` VARCHAR (255) NOT NULL,\n`subject` VARCHAR (255) NOT NULL,\n`recipient` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000006_courier.postgres.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000006_courier.postgres.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"type\" int NOT NULL,\n\"status\" int NOT NULL,\n\"body\" VARCHAR (255) NOT NULL,\n\"subject\" VARCHAR (255) NOT NULL,\n\"recipient\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000006_courier.sqlite3.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000006_courier.sqlite3.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000007_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"csrf_token\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000007_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" VARCHAR (255) NOT NULL DEFAULT '';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000007_errors.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_errors` DROP COLUMN `csrf_token`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000007_errors.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` ADD COLUMN `csrf_token` VARCHAR (255) NOT NULL DEFAULT \"\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000007_errors.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"csrf_token\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000007_errors.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" VARCHAR (255) NOT NULL DEFAULT '';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000007_errors.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nINSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at) SELECT id, errors, seen_at, was_seen, created_at, updated_at FROM \"selfservice_errors\";\n\nDROP TABLE \"selfservice_errors\";\nALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000007_errors.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" TEXT NOT NULL DEFAULT '';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000008_selfservice_verification.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identity_verifiable_addresses\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000008_selfservice_verification.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"code\" VARCHAR (32) NOT NULL,\n\"status\" VARCHAR (16) NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"verified_at\" timestamp,\n\"expires_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_verifiable_addresses_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_verification_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" json NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000008_selfservice_verification.mysql.down.sql",
    "content": "DROP TABLE `selfservice_verification_requests`;\nDROP TABLE `identity_verifiable_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000008_selfservice_verification.mysql.up.sql",
    "content": "CREATE TABLE `identity_verifiable_addresses` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`code` VARCHAR (32) NOT NULL,\n`status` VARCHAR (16) NOT NULL,\n`via` VARCHAR (16) NOT NULL,\n`verified` bool NOT NULL,\n`value` VARCHAR (400) NOT NULL,\n`verified_at` DATETIME,\n`expires_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses` (`code`);\nCREATE INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses` (`code`);\nCREATE UNIQUE INDEX `identity_verifiable_addresses_status_via_uq_idx` ON `identity_verifiable_addresses` (`via`, `value`);\nCREATE INDEX `identity_verifiable_addresses_status_via_idx` ON `identity_verifiable_addresses` (`via`, `value`);\nCREATE TABLE `selfservice_verification_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`form` JSON NOT NULL,\n`via` VARCHAR (16) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`success` bool NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000008_selfservice_verification.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\";\nDROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000008_selfservice_verification.postgres.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"code\" VARCHAR (32) NOT NULL,\n\"status\" VARCHAR (16) NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"verified_at\" timestamp,\n\"expires_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);\nCREATE TABLE \"selfservice_verification_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" jsonb NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000008_selfservice_verification.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\";\nDROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000008_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"code\" TEXT NOT NULL,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);\nCREATE TABLE \"selfservice_verification_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000009_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255);\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000009_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY;\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000010_errors.cockroach.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;\nALTER TABLE \"selfservice_errors\" RENAME COLUMN \"seen_at\" TO \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_errors\" ADD COLUMN \"seen_at\" timestamp;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"selfservice_errors\" SET \"seen_at\" = \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_errors\" DROP COLUMN \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000010_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" RENAME COLUMN \"seen_at\" TO \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_errors\" ADD COLUMN \"seen_at\" timestamp;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"selfservice_errors\" SET \"seen_at\" = \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_errors\" DROP COLUMN \"_seen_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000010_errors.mysql.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;\nALTER TABLE `selfservice_errors` MODIFY `seen_at` DATETIME;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000010_errors.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` MODIFY `seen_at` DATETIME;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000010_errors.postgres.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;\nALTER TABLE \"selfservice_errors\" ALTER COLUMN \"seen_at\" TYPE timestamp, ALTER COLUMN \"seen_at\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000010_errors.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ALTER COLUMN \"seen_at\" TYPE timestamp, ALTER COLUMN \"seen_at\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000010_errors.sqlite3.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;\nCREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n);\nINSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\";\nDROP TABLE \"selfservice_errors\";\nALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000010_errors.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n);\nINSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\";\nDROP TABLE \"selfservice_errors\";\nALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000011_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" RENAME COLUMN \"body\" TO \"_body_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"courier_messages\" ADD COLUMN \"body\" text;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"courier_messages\" SET \"body\" = \"_body_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"courier_messages\" ALTER COLUMN \"body\" SET NOT NULL;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"courier_messages\" DROP COLUMN \"_body_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000011_courier_body_type.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000011_courier_body_type.mysql.up.sql",
    "content": "ALTER TABLE `courier_messages` MODIFY `body` text NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000011_courier_body_type.postgres.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ALTER COLUMN \"body\" TYPE text, ALTER COLUMN \"body\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000011_courier_body_type.sqlite3.up.sql",
    "content": "CREATE TABLE \"_courier_messages_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nINSERT INTO \"_courier_messages_tmp\" (id, type, status, body, subject, recipient, created_at, updated_at) SELECT id, type, status, body, subject, recipient, created_at, updated_at FROM \"courier_messages\";\nDROP TABLE \"courier_messages\";\nALTER TABLE \"_courier_messages_tmp\" RENAME TO \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000012_login_request_forced.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"forced\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000012_login_request_forced.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000012_login_request_forced.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `forced`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000012_login_request_forced.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `forced` bool NOT NULL DEFAULT false;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000012_login_request_forced.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"forced\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000012_login_request_forced.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000012_login_request_forced.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nINSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at FROM \"selfservice_login_requests\";\n\nDROP TABLE \"selfservice_login_requests\";\nALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20191100000012_login_request_forced.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200317160354_create_profile_request_forms.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"form\" json NOT NULL DEFAULT '{}';COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_profile_management_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"active_method\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200317160354_create_profile_request_forms.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_profile_management_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nINSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;\nALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"form\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200317160354_create_profile_request_forms.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` ADD COLUMN `form` JSON;\nUPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t);\nALTER TABLE `selfservice_profile_management_requests` MODIFY `form` JSON;\nDROP TABLE `selfservice_profile_management_request_methods`;\nALTER TABLE `selfservice_profile_management_requests` DROP COLUMN `active_method`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200317160354_create_profile_request_forms.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_profile_management_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_profile_management_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nALTER TABLE `selfservice_profile_management_requests` ADD COLUMN `active_method` VARCHAR (32);\nINSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;\nALTER TABLE `selfservice_profile_management_requests` DROP COLUMN `form`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200317160354_create_profile_request_forms.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"form\" jsonb;\nUPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t);\nALTER TABLE \"selfservice_profile_management_requests\" ALTER COLUMN \"form\" TYPE jsonb, ALTER COLUMN \"form\" DROP NOT NULL;\nDROP TABLE \"selfservice_profile_management_request_methods\";\nALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"active_method\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200317160354_create_profile_request_forms.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_profile_management_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" VARCHAR (32);\nINSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;\nALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"form\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200317160354_create_profile_request_forms.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_request_methods\";\nCREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"update_successful\" bool NOT NULL DEFAULT 'false',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, update_successful) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, update_successful FROM \"selfservice_profile_management_requests\";\n\nDROP TABLE \"selfservice_profile_management_requests\";\nALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200317160354_create_profile_request_forms.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_profile_management_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" TEXT;\nINSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;\nCREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method) SELECT id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method FROM \"selfservice_profile_management_requests\";\n\nDROP TABLE \"selfservice_profile_management_requests\";\nALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200401183443_continuity_containers.cockroach.down.sql",
    "content": "DROP TABLE \"continuity_containers\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200401183443_continuity_containers.cockroach.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identity_id\" UUID,\n\"name\" VARCHAR (255) NOT NULL,\n\"payload\" json,\n\"expires_at\" timestamp NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"continuity_containers_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200401183443_continuity_containers.mysql.down.sql",
    "content": "DROP TABLE `continuity_containers`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200401183443_continuity_containers.mysql.up.sql",
    "content": "CREATE TABLE `continuity_containers` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`identity_id` char(36),\n`name` VARCHAR (255) NOT NULL,\n`payload` JSON,\n`expires_at` DATETIME NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200401183443_continuity_containers.postgres.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200401183443_continuity_containers.postgres.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identity_id\" UUID,\n\"name\" VARCHAR (255) NOT NULL,\n\"payload\" jsonb,\n\"expires_at\" timestamp NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200401183443_continuity_containers.sqlite3.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200401183443_continuity_containers.sqlite3.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identity_id\" char(36),\n\"name\" TEXT NOT NULL,\n\"payload\" TEXT,\n\"expires_at\" DATETIME NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200402142539_rename_profile_flows.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200402142539_rename_profile_flows.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200402142539_rename_profile_flows.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_request_methods` CHANGE `selfservice_settings_request_id` `selfservice_profile_management_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_settings_request_methods` RENAME TO `selfservice_profile_management_request_methods`;\nALTER TABLE `selfservice_settings_requests` RENAME TO `selfservice_profile_management_requests`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200402142539_rename_profile_flows.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_request_methods` CHANGE `selfservice_profile_management_request_id` `selfservice_settings_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_profile_management_request_methods` RENAME TO `selfservice_settings_request_methods`;\nALTER TABLE `selfservice_profile_management_requests` RENAME TO `selfservice_settings_requests`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200402142539_rename_profile_flows.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\";\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\";\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200402142539_rename_profile_flows.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\";\nALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\";\nALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200402142539_rename_profile_flows.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\";\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\";\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200402142539_rename_profile_flows.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\";\nALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\";\nALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101057_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_recovery_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_recovery_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"identity_recovery_addresses\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101057_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"via\" VARCHAR (16) NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_recovery_addresses_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"messages\" json,\n\"active_method\" VARCHAR (32),\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"state\" VARCHAR (32) NOT NULL,\n\"recovered_identity_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_recovery_requests_identities_id_fk\" FOREIGN KEY (\"recovered_identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"config\" json NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_recovery_request_methods_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE TABLE \"identity_recovery_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"identity_recovery_address_id\" UUID NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_recovery_tokens_identity_recovery_addresses_id_fk\" FOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101057_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `identity_recovery_tokens`;\nDROP TABLE `selfservice_recovery_request_methods`;\nDROP TABLE `selfservice_recovery_requests`;\nDROP TABLE `identity_recovery_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101057_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `identity_recovery_addresses` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`via` VARCHAR (16) NOT NULL,\n`value` VARCHAR (400) NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_recovery_addresses_status_via_uq_idx` ON `identity_recovery_addresses` (`via`, `value`);\nCREATE INDEX `identity_recovery_addresses_status_via_idx` ON `identity_recovery_addresses` (`via`, `value`);\nCREATE TABLE `selfservice_recovery_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`messages` JSON,\n`active_method` VARCHAR (32),\n`csrf_token` VARCHAR (255) NOT NULL,\n`state` VARCHAR (32) NOT NULL,\n`recovered_identity_id` char(36),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`recovered_identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `selfservice_recovery_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`config` JSON NOT NULL,\n`selfservice_recovery_request_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_recovery_request_id`) REFERENCES `selfservice_recovery_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE TABLE `identity_recovery_tokens` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`token` VARCHAR (64) NOT NULL,\n`used` bool NOT NULL DEFAULT false,\n`used_at` DATETIME,\n`identity_recovery_address_id` char(36) NOT NULL,\n`selfservice_recovery_request_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_recovery_address_id`) REFERENCES `identity_recovery_addresses` (`id`) ON DELETE cascade,\nFOREIGN KEY (`selfservice_recovery_request_id`) REFERENCES `selfservice_recovery_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_recovery_addresses_code_uq_idx` ON `identity_recovery_tokens` (`token`);\nCREATE INDEX `identity_recovery_addresses_code_idx` ON `identity_recovery_tokens` (`token`);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101057_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";\nDROP TABLE \"selfservice_recovery_request_methods\";\nDROP TABLE \"selfservice_recovery_requests\";\nDROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101057_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"via\" VARCHAR (16) NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);\nCREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);\nCREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"messages\" jsonb,\n\"active_method\" VARCHAR (32),\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"state\" VARCHAR (32) NOT NULL,\n\"recovered_identity_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"recovered_identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"config\" jsonb NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);\nCREATE TABLE \"identity_recovery_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"identity_recovery_address_id\" UUID NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101057_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";\nDROP TABLE \"selfservice_recovery_request_methods\";\nDROP TABLE \"selfservice_recovery_requests\";\nDROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101057_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"via\" TEXT NOT NULL,\n\"value\" TEXT NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);\nCREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);\nCREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON DELETE cascade\n);\nCREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"config\" TEXT NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n);\nCREATE TABLE \"identity_recovery_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101058_create_recovery_addresses.mysql.down.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64);\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200519101058_create_recovery_addresses.mysql.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64) BINARY;\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101000_create_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101000_create_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" json;COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101000_create_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101000_create_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101000_create_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101000_create_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101000_create_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"update_successful\" bool NOT NULL DEFAULT 'false',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful FROM \"selfservice_settings_requests\";\n\nDROP TABLE \"selfservice_settings_requests\";\nALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101000_create_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101001_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY;\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200601101001_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(32) BINARY;\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200605111551_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"messages\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200605111551_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" json;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" json;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" json;COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200605111551_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` DROP COLUMN `messages`;\nALTER TABLE `selfservice_login_requests` DROP COLUMN `messages`;\nALTER TABLE `selfservice_registration_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200605111551_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` ADD COLUMN `messages` JSON;\nALTER TABLE `selfservice_login_requests` ADD COLUMN `messages` JSON;\nALTER TABLE `selfservice_registration_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200605111551_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"messages\";\nALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"messages\";\nALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200605111551_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" jsonb;\nALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" jsonb;\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200605111551_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"via\" TEXT NOT NULL DEFAULT 'email',\n\"success\" bool NOT NULL DEFAULT 'FALSE'\n);\nINSERT INTO \"_selfservice_verification_requests_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, via, success) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, via, success FROM \"selfservice_verification_requests\";\n\nDROP TABLE \"selfservice_verification_requests\";\nALTER TABLE \"_selfservice_verification_requests_tmp\" RENAME TO \"selfservice_verification_requests\";\nCREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false'\n);\nINSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced FROM \"selfservice_login_requests\";\n\nDROP TABLE \"selfservice_login_requests\";\nALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";\nCREATE TABLE \"_selfservice_registration_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nINSERT INTO \"_selfservice_registration_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at FROM \"selfservice_registration_requests\";\n\nDROP TABLE \"selfservice_registration_requests\";\nALTER TABLE \"_selfservice_registration_requests_tmp\" RENAME TO \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200605111551_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" TEXT;\nALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" TEXT;\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200607165100_settings.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"state\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200607165100_settings.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"update_successful\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200607165100_settings.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `state`;\nALTER TABLE `selfservice_settings_requests` ADD COLUMN `update_successful` bool NOT NULL DEFAULT false;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200607165100_settings.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `state` VARCHAR (255) NOT NULL DEFAULT 'show_form';\nALTER TABLE `selfservice_settings_requests` DROP COLUMN `update_successful`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200607165100_settings.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"state\";\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200607165100_settings.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';\nALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"update_successful\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200607165100_settings.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages FROM \"selfservice_settings_requests\";\n\nDROP TABLE \"selfservice_settings_requests\";\nALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200607165100_settings.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form';\nCREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state FROM \"selfservice_settings_requests\";\n\nDROP TABLE \"selfservice_settings_requests\";\nALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200705105359_rename_identities_schema.cockroach.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200705105359_rename_identities_schema.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200705105359_rename_identities_schema.mysql.down.sql",
    "content": "ALTER TABLE `identities` CHANGE `schema_id` `traits_schema_id` varchar(2048) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200705105359_rename_identities_schema.mysql.up.sql",
    "content": "ALTER TABLE `identities` CHANGE `traits_schema_id` `schema_id` varchar(2048) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200705105359_rename_identities_schema.postgres.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200705105359_rename_identities_schema.postgres.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200705105359_rename_identities_schema.sqlite3.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200705105359_rename_identities_schema.sqlite3.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810141652_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"type\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810141652_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810141652_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `type`;\nALTER TABLE `selfservice_registration_requests` DROP COLUMN `type`;\nALTER TABLE `selfservice_settings_requests` DROP COLUMN `type`;\nALTER TABLE `selfservice_recovery_requests` DROP COLUMN `type`;\nALTER TABLE `selfservice_verification_requests` DROP COLUMN `type`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810141652_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE `selfservice_registration_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE `selfservice_settings_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE `selfservice_recovery_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE `selfservice_verification_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810141652_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"type\";\nALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"type\";\nALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"type\";\nALTER TABLE \"selfservice_recovery_requests\" DROP COLUMN \"type\";\nALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810141652_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810141652_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"messages\" TEXT\n);\nINSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages FROM \"selfservice_login_requests\";\n\nDROP TABLE \"selfservice_login_requests\";\nALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";\nCREATE TABLE \"_selfservice_registration_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT\n);\nINSERT INTO \"_selfservice_registration_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages FROM \"selfservice_registration_requests\";\n\nDROP TABLE \"selfservice_registration_requests\";\nALTER TABLE \"_selfservice_registration_requests_tmp\" RENAME TO \"selfservice_registration_requests\";\nCREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state FROM \"selfservice_settings_requests\";\n\nDROP TABLE \"selfservice_settings_requests\";\nALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";\nCREATE TABLE \"_selfservice_recovery_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_selfservice_recovery_requests_tmp\" (id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, messages, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at FROM \"selfservice_recovery_requests\";\n\nDROP TABLE \"selfservice_recovery_requests\";\nALTER TABLE \"_selfservice_recovery_requests_tmp\" RENAME TO \"selfservice_recovery_requests\";\nCREATE TABLE \"_selfservice_verification_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"via\" TEXT NOT NULL DEFAULT 'email',\n\"success\" bool NOT NULL DEFAULT 'FALSE'\n);\nINSERT INTO \"_selfservice_verification_requests_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, via, success) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, via, success FROM \"selfservice_verification_requests\";\n\nDROP TABLE \"selfservice_verification_requests\";\nALTER TABLE \"_selfservice_verification_requests_tmp\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810141652_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';\nALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810161022_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810161022_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810161022_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` RENAME TO `selfservice_login_requests`;\nALTER TABLE `selfservice_login_flow_methods` RENAME TO `selfservice_login_request_methods`;\nALTER TABLE `selfservice_registration_flow_methods` RENAME TO `selfservice_registration_request_methods`;\nALTER TABLE `selfservice_registration_flows` RENAME TO `selfservice_registration_requests`;\nALTER TABLE `selfservice_settings_flow_methods` RENAME TO `selfservice_settings_request_methods`;\nALTER TABLE `selfservice_settings_flows` RENAME TO `selfservice_settings_requests`;\nALTER TABLE `selfservice_recovery_flow_methods` RENAME TO `selfservice_recovery_request_methods`;\nALTER TABLE `selfservice_recovery_flows` RENAME TO `selfservice_recovery_requests`;\nALTER TABLE `selfservice_verification_flows` RENAME TO `selfservice_verification_requests`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810161022_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_request_methods` RENAME TO `selfservice_login_flow_methods`;\nALTER TABLE `selfservice_login_requests` RENAME TO `selfservice_login_flows`;\nALTER TABLE `selfservice_registration_request_methods` RENAME TO `selfservice_registration_flow_methods`;\nALTER TABLE `selfservice_registration_requests` RENAME TO `selfservice_registration_flows`;\nALTER TABLE `selfservice_settings_request_methods` RENAME TO `selfservice_settings_flow_methods`;\nALTER TABLE `selfservice_settings_requests` RENAME TO `selfservice_settings_flows`;\nALTER TABLE `selfservice_recovery_request_methods` RENAME TO `selfservice_recovery_flow_methods`;\nALTER TABLE `selfservice_recovery_requests` RENAME TO `selfservice_recovery_flows`;\nALTER TABLE `selfservice_verification_requests` RENAME TO `selfservice_verification_flows`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810161022_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\";\nALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\";\nALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\";\nALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\";\nALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\";\nALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810161022_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\";\nALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\";\nALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\";\nALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\";\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\";\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\";\nALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\";\nALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\";\nALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810161022_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\";\nALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\";\nALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\";\nALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\";\nALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\";\nALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810161022_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\";\nALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\";\nALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\";\nALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\";\nALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\";\nALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\";\nALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\";\nALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\";\nALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810162450_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810162450_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810162450_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` CHANGE `selfservice_login_flow_id` `selfservice_login_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_registration_flow_methods` CHANGE `selfservice_registration_flow_id` `selfservice_registration_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_settings_flow_methods` CHANGE `selfservice_settings_flow_id` `selfservice_settings_request_id` char(36) NOT NULL;\nALTER TABLE `selfservice_recovery_flow_methods` CHANGE `selfservice_recovery_flow_id` `selfservice_recovery_request_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810162450_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` CHANGE `selfservice_login_request_id` `selfservice_login_flow_id` char(36) NOT NULL;\nALTER TABLE `selfservice_registration_flow_methods` CHANGE `selfservice_registration_request_id` `selfservice_registration_flow_id` char(36) NOT NULL;\nALTER TABLE `selfservice_recovery_flow_methods` CHANGE `selfservice_recovery_request_id` `selfservice_recovery_flow_id` char(36) NOT NULL;\nALTER TABLE `selfservice_settings_flow_methods` CHANGE `selfservice_settings_request_id` `selfservice_settings_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810162450_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810162450_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810162450_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200810162450_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\";\nALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\";\nALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";\nALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812124254_add_session_token.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"token\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812124254_add_session_token.cockroach.up.sql",
    "content": "DELETE FROM sessions;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"sessions\" RENAME COLUMN \"token\" TO \"_token_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"sessions\" SET \"token\" = \"_token_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"sessions\" DROP COLUMN \"_token_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812124254_add_session_token.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `token`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812124254_add_session_token.mysql.up.sql",
    "content": "DELETE FROM sessions;\nALTER TABLE `sessions` ADD COLUMN `token` VARCHAR (32);\nALTER TABLE `sessions` MODIFY `token` VARCHAR (32);\nCREATE UNIQUE INDEX `sessions_token_uq_idx` ON `sessions` (`token`);\nCREATE INDEX `sessions_token_idx` ON `sessions` (`token`);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812124254_add_session_token.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"token\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812124254_add_session_token.postgres.up.sql",
    "content": "DELETE FROM sessions;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32);\nALTER TABLE \"sessions\" ALTER COLUMN \"token\" TYPE VARCHAR (32), ALTER COLUMN \"token\" DROP NOT NULL;\nCREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token);\nCREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812124254_add_session_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";\nDROP INDEX IF EXISTS \"sessions_token_idx\";\nCREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at FROM \"sessions\";\n\nDROP TABLE \"sessions\";\nALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812124254_add_session_token.sqlite3.up.sql",
    "content": "DELETE FROM sessions;\nALTER TABLE \"sessions\" ADD COLUMN \"token\" TEXT;\nCREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nINSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token FROM \"sessions\";\nDROP TABLE \"sessions\";\nALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";\nCREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token);\nCREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812160551_add_session_revoke.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"active\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812160551_add_session_revoke.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" boolean DEFAULT 'false';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812160551_add_session_revoke.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `active`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812160551_add_session_revoke.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `active` boolean DEFAULT false;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812160551_add_session_revoke.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"active\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812160551_add_session_revoke.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" boolean DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812160551_add_session_revoke.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";\nDROP INDEX IF EXISTS \"sessions_token_uq_idx\";\nCREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token);\nCREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token);\nINSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token FROM \"sessions\";\n\nDROP TABLE \"sessions\";\nALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200812160551_add_session_revoke.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" NUMERIC DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830121710_update_recovery_token.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830121710_update_recovery_token.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830121710_update_recovery_token.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` CHANGE `selfservice_recovery_flow_id` `selfservice_recovery_request_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830121710_update_recovery_token.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` CHANGE `selfservice_recovery_request_id` `selfservice_recovery_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830121710_update_recovery_token.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830121710_update_recovery_token.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830121710_update_recovery_token.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830121710_update_recovery_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130642_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"form\" json NOT NULL DEFAULT '{}';COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP TABLE \"selfservice_verification_flow_methods\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"active_method\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"state\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" VARCHAR (16) NOT NULL DEFAULT 'email';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130642_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130642_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `form` JSON;\nUPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t);\nALTER TABLE `selfservice_verification_flows` MODIFY `form` JSON;\nDROP TABLE `selfservice_verification_flow_methods`;\nALTER TABLE `selfservice_verification_flows` DROP COLUMN `active_method`;\nALTER TABLE `selfservice_verification_flows` DROP COLUMN `state`;\nALTER TABLE `selfservice_verification_flows` ADD COLUMN `via` VARCHAR (16) NOT NULL DEFAULT 'email';\nALTER TABLE `selfservice_verification_flows` ADD COLUMN `success` bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130642_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `state` VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130642_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"form\" jsonb;\nUPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t);\nALTER TABLE \"selfservice_verification_flows\" ALTER COLUMN \"form\" TYPE jsonb, ALTER COLUMN \"form\" DROP NOT NULL;\nDROP TABLE \"selfservice_verification_flow_methods\";\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"active_method\";\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"state\";\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" VARCHAR (16) NOT NULL DEFAULT 'email';\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130642_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130642_add_verification_methods.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\";\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form'\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser'\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" TEXT NOT NULL DEFAULT 'email';\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130642_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130643_add_verification_methods.cockroach.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130643_add_verification_methods.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130643_add_verification_methods.mysql.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130643_add_verification_methods.postgres.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130643_add_verification_methods.sqlite3.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130644_add_verification_methods.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130644_add_verification_methods.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130644_add_verification_methods.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_verification_flow_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_verification_flow_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;\nALTER TABLE `selfservice_verification_flows` ADD COLUMN `active_method` VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130644_add_verification_methods.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130644_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_verification_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);\nALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130645_add_verification_methods.cockroach.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130645_add_verification_methods.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130645_add_verification_methods.mysql.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130645_add_verification_methods.postgres.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130645_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130646_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"form\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"via\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"success\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130646_add_verification_methods.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130646_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `form`;\nALTER TABLE `selfservice_verification_flows` DROP COLUMN `via`;\nALTER TABLE `selfservice_verification_flows` DROP COLUMN `success`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130646_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"form\";\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"via\";\nALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"success\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830130646_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";\nCREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);\nINSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\";\n\nDROP TABLE \"selfservice_verification_flows\";\nALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830154602_add_verification_token.cockroach.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830154602_add_verification_token.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"expires_at\" timestamp NOT NULL,\n\"issued_at\" timestamp NOT NULL,\n\"identity_verifiable_address_id\" UUID NOT NULL,\n\"selfservice_verification_flow_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_verification_tokens_identity_verifiable_addresses_id_fk\" FOREIGN KEY (\"identity_verifiable_address_id\") REFERENCES \"identity_verifiable_addresses\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_verification_tokens_selfservice_verification_flows_id_fk\" FOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flows\" (\"id\") ON DELETE cascade\n);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830154602_add_verification_token.mysql.down.sql",
    "content": "DROP TABLE `identity_verification_tokens`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830154602_add_verification_token.mysql.up.sql",
    "content": "CREATE TABLE `identity_verification_tokens` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`token` VARCHAR (64) NOT NULL,\n`used` bool NOT NULL DEFAULT false,\n`used_at` DATETIME,\n`expires_at` DATETIME NOT NULL,\n`issued_at` DATETIME NOT NULL,\n`identity_verifiable_address_id` char(36) NOT NULL,\n`selfservice_verification_flow_id` char(36),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_verifiable_address_id`) REFERENCES `identity_verifiable_addresses` (`id`) ON DELETE cascade,\nFOREIGN KEY (`selfservice_verification_flow_id`) REFERENCES `selfservice_verification_flows` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;\nCREATE UNIQUE INDEX `identity_verification_tokens_token_uq_idx` ON `identity_verification_tokens` (`token`);\nCREATE INDEX `identity_verification_tokens_token_idx` ON `identity_verification_tokens` (`token`);\nCREATE INDEX `identity_verification_tokens_verifiable_address_id_idx` ON `identity_verification_tokens` (`identity_verifiable_address_id`);\nCREATE INDEX `identity_verification_tokens_verification_flow_id_idx` ON `identity_verification_tokens` (`selfservice_verification_flow_id`);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830154602_add_verification_token.postgres.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830154602_add_verification_token.postgres.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"expires_at\" timestamp NOT NULL,\n\"issued_at\" timestamp NOT NULL,\n\"identity_verifiable_address_id\" UUID NOT NULL,\n\"selfservice_verification_flow_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_verifiable_address_id\") REFERENCES \"identity_verifiable_addresses\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flows\" (\"id\") ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);\nCREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830154602_add_verification_token.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830154602_add_verification_token.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL,\n\"identity_verifiable_address_id\" char(36) NOT NULL,\n\"selfservice_verification_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON DELETE cascade\n);\nCREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);\nCREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830172221_recovery_token_expires.cockroach.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;\nALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"selfservice_recovery_flow_id\" UUID;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"identity_recovery_tokens\" SET \"selfservice_recovery_flow_id\" = \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" SET NOT NULL;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flows\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"expires_at\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"issued_at\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830172221_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"selfservice_recovery_flow_id\" UUID;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"identity_recovery_tokens\" SET \"selfservice_recovery_flow_id\" = \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_selfservice_recovery_flow_id_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flows\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE;COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830172221_recovery_token_expires.mysql.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;\nALTER TABLE `identity_recovery_tokens` MODIFY `selfservice_recovery_flow_id` char(36) NOT NULL;\nALTER TABLE `identity_recovery_tokens` DROP COLUMN `expires_at`;\nALTER TABLE `identity_recovery_tokens` DROP COLUMN `issued_at`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830172221_recovery_token_expires.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD COLUMN `expires_at` DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE `identity_recovery_tokens` ADD COLUMN `issued_at` DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE `identity_recovery_tokens` MODIFY `selfservice_recovery_flow_id` char(36);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830172221_recovery_token_expires.postgres.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;\nALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" TYPE UUID, ALTER COLUMN \"selfservice_recovery_flow_id\" SET NOT NULL;\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"expires_at\";\nALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"issued_at\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830172221_recovery_token_expires.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" TYPE UUID, ALTER COLUMN \"selfservice_recovery_flow_id\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830172221_recovery_token_expires.sqlite3.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";\nCREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nINSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at FROM \"identity_recovery_tokens\";\nDROP TABLE \"identity_recovery_tokens\";\nALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";\nCREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nINSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, issued_at FROM \"identity_recovery_tokens\";\n\nDROP TABLE \"identity_recovery_tokens\";\nALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";\nCREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nINSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at FROM \"identity_recovery_tokens\";\n\nDROP TABLE \"identity_recovery_tokens\";\nALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200830172221_recovery_token_expires.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';\nALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";\nDROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";\nCREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nCREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);\nINSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at FROM \"identity_recovery_tokens\";\nDROP TABLE \"identity_recovery_tokens\";\nALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE identity_verifiable_addresses SET code = substr(md5(uuid_v4()), 0, 32) WHERE code IS NULL;\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;\nALTER TABLE \"identity_verifiable_addresses\" RENAME COLUMN \"code\" TO \"_code_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32);COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"identity_verifiable_addresses\" SET \"code\" = \"_code_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"code\" SET NOT NULL;COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"_code_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" RENAME COLUMN \"expires_at\" TO \"_expires_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp;COMMIT TRANSACTION;BEGIN TRANSACTION;\nUPDATE \"identity_verifiable_addresses\" SET \"expires_at\" = \"_expires_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"_expires_at_tmp\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);COMMIT TRANSACTION;BEGIN TRANSACTION;\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_uq_idx\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_code_idx\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"code\";COMMIT TRANSACTION;BEGIN TRANSACTION;\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"expires_at\";COMMIT TRANSACTION;BEGIN TRANSACTION;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` ADD COLUMN `code` VARCHAR (32);\nALTER TABLE `identity_verifiable_addresses` ADD COLUMN `expires_at` DATETIME;\nUPDATE identity_verifiable_addresses SET code = LEFT(SHA2(RANDOM_BYTES(32), 256), 32) WHERE code IS NULL;\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;\nALTER TABLE `identity_verifiable_addresses` MODIFY `code` VARCHAR (32) NOT NULL;\nALTER TABLE `identity_verifiable_addresses` MODIFY `expires_at` DATETIME;\nCREATE UNIQUE INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses` (`code`);\nCREATE INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses` (`code`);\n"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses`;\nDROP INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses`;\nALTER TABLE `identity_verifiable_addresses` DROP COLUMN `code`;\nALTER TABLE `identity_verifiable_addresses` DROP COLUMN `expires_at`;"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32);\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp;\nUPDATE identity_verifiable_addresses SET code = substr(md5(random()::text), 0, 32) WHERE code IS NULL;\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;\nALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"code\" TYPE VARCHAR (32), ALTER COLUMN \"code\" SET NOT NULL;\nALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"expires_at\" TYPE timestamp, ALTER COLUMN \"expires_at\" DROP NOT NULL;\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_code_uq_idx\";\nDROP INDEX \"identity_verifiable_addresses_code_idx\";\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"code\";\nALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"expires_at\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" TEXT;\nALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" DATETIME;\nUPDATE identity_verifiable_addresses SET code = substr(hex(randomblob(32)), 0, 32) WHERE code IS NULL;\nUPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"code\" TEXT NOT NULL,\n\"expires_at\" DATETIME,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at FROM \"identity_verifiable_addresses\";\nDROP TABLE \"identity_verifiable_addresses\";\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"code\" TEXT NOT NULL,\n\"expires_at\" DATETIME,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at FROM \"identity_verifiable_addresses\";\nDROP TABLE \"identity_verifiable_addresses\";\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);\nCREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/legacy/20200831110752_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_uq_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_code_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\";\n\nDROP TABLE \"identity_verifiable_addresses\";\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";\nDROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";\nCREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\nCREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nCREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);\nINSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\";\n\nDROP TABLE \"identity_verifiable_addresses\";\nALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/legacy/20201201161451_credential_types_values.cockroach.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20201201161451_credential_types_values.cockroach.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "persistence/sql/migrations/legacy/20201201161451_credential_types_values.mysql.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20201201161451_credential_types_values.mysql.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "persistence/sql/migrations/legacy/20201201161451_credential_types_values.postgres.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20201201161451_credential_types_values.postgres.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "persistence/sql/migrations/legacy/20201201161451_credential_types_values.sqlite3.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "persistence/sql/migrations/legacy/20201201161451_credential_types_values.sqlite3.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');\nINSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "persistence/sql/migrations/sql/20150100000001000000_networks.cockroach.down.sql",
    "content": "DROP TABLE \"networks\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20150100000001000000_networks.cockroach.up.sql",
    "content": "CREATE TABLE \"networks\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20150100000001000000_networks.mysql.down.sql",
    "content": "DROP TABLE `networks`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20150100000001000000_networks.mysql.up.sql",
    "content": "CREATE TABLE `networks` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20150100000001000000_networks.postgres.down.sql",
    "content": "DROP TABLE \"networks\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20150100000001000000_networks.postgres.up.sql",
    "content": "CREATE TABLE \"networks\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20150100000001000000_networks.sqlite3.down.sql",
    "content": "DROP TABLE \"networks\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20150100000001000000_networks.sqlite3.up.sql",
    "content": "CREATE TABLE \"networks\" (\n\"id\" TEXT PRIMARY KEY,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000000_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000000_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"traits_schema_id\" VARCHAR (2048) NOT NULL,\n\"traits\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000000_identities.mysql.down.sql",
    "content": "DROP TABLE `identities`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000000_identities.mysql.up.sql",
    "content": "CREATE TABLE `identities` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`traits_schema_id` VARCHAR (2048) NOT NULL,\n`traits` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000000_identities.postgres.down.sql",
    "content": "DROP TABLE \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000000_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"traits_schema_id\" VARCHAR (2048) NOT NULL,\n\"traits\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000000_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000000_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identities\" (\n\"id\" TEXT PRIMARY KEY,\n\"traits_schema_id\" TEXT NOT NULL,\n\"traits\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000001_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identity_credential_types\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000001_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_credential_types\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"name\" VARCHAR (32) NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000001_identities.mysql.down.sql",
    "content": "DROP TABLE `identity_credential_types`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000001_identities.mysql.up.sql",
    "content": "CREATE TABLE `identity_credential_types` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`name` VARCHAR (32) NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000001_identities.postgres.down.sql",
    "content": "DROP TABLE \"identity_credential_types\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000001_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identity_credential_types\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"name\" VARCHAR (32) NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000001_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_credential_types\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000001_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_credential_types\" (\n\"id\" TEXT PRIMARY KEY,\n\"name\" TEXT NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000002_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identity_credentials\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000002_identities.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000002_identities.mysql.down.sql",
    "content": "DROP TABLE `identity_credentials`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000002_identities.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_credential_types_name_idx` ON `identity_credential_types` (`name`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000002_identities.postgres.down.sql",
    "content": "DROP TABLE \"identity_credentials\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000002_identities.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000002_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_credentials\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000002_identities.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_types_name_idx\" ON \"identity_credential_types\" (name);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000003_identities.cockroach.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000003_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_credentials\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"config\" json NOT NULL,\n\"identity_credential_type_id\" UUID NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_credentials_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_credentials_identity_credential_types_id_fk\" FOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000003_identities.mysql.down.sql",
    "content": "DROP TABLE `identity_credential_identifiers`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000003_identities.mysql.up.sql",
    "content": "CREATE TABLE `identity_credentials` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`config` JSON NOT NULL,\n`identity_credential_type_id` char(36) NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade,\nFOREIGN KEY (`identity_credential_type_id`) REFERENCES `identity_credential_types` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000003_identities.postgres.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000003_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identity_credentials\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"config\" jsonb NOT NULL,\n\"identity_credential_type_id\" UUID NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000003_identities.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000003_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_credentials\" (\n\"id\" TEXT PRIMARY KEY,\n\"config\" TEXT NOT NULL,\n\"identity_credential_type_id\" char(36) NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade,\nFOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000004_identities.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000004_identities.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_credential_identifiers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identifier\" VARCHAR (255) NOT NULL,\n\"identity_credential_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_credential_identifiers_identity_credentials_id_fk\" FOREIGN KEY (\"identity_credential_id\") REFERENCES \"identity_credentials\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000004_identities.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000004_identities.mysql.up.sql",
    "content": "CREATE TABLE `identity_credential_identifiers` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`identifier` VARCHAR (255) NOT NULL,\n`identity_credential_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_credential_id`) REFERENCES `identity_credentials` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000004_identities.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000004_identities.postgres.up.sql",
    "content": "CREATE TABLE \"identity_credential_identifiers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identifier\" VARCHAR (255) NOT NULL,\n\"identity_credential_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_credential_id\") REFERENCES \"identity_credentials\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000004_identities.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000004_identities.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_credential_identifiers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000005_identities.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000005_identities.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000005_identities.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000005_identities.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_credential_identifiers_identifier_idx` ON `identity_credential_identifiers` (`identifier`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000005_identities.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000005_identities.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000005_identities.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000001000005_identities.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000000_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000000_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000000_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_profile_management_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000000_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_login_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`active_method` VARCHAR (32) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000000_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000000_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000000_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000000_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_login_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000001_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000001_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_login_request_methods_selfservice_login_requests_id_fk\" FOREIGN KEY (\"selfservice_login_request_id\") REFERENCES \"selfservice_login_requests\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000001_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_registration_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000001_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_login_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_login_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_login_request_id`) REFERENCES `selfservice_login_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000001_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000001_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_login_request_id\") REFERENCES \"selfservice_login_requests\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000001_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000001_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_login_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_login_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_login_request_id) REFERENCES selfservice_login_requests (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000002_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_registration_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000002_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000002_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_registration_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000002_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_registration_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`active_method` VARCHAR (32) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000002_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_registration_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000002_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"active_method\" VARCHAR (32) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000002_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_registration_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000002_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000003_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000003_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_registration_request_methods_selfservice_registration_requests_id_fk\" FOREIGN KEY (\"selfservice_registration_request_id\") REFERENCES \"selfservice_registration_requests\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000003_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_login_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000003_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_registration_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_registration_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_registration_request_id`) REFERENCES `selfservice_registration_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000003_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000003_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_registration_request_id\") REFERENCES \"selfservice_registration_requests\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000003_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000003_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_registration_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_registration_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_registration_request_id) REFERENCES selfservice_registration_requests (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000004_requests.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000004_requests.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" json NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_profile_management_requests_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000004_requests.mysql.down.sql",
    "content": "DROP TABLE `selfservice_login_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000004_requests.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_profile_management_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`form` JSON NOT NULL,\n`update_successful` bool NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000004_requests.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000004_requests.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" jsonb NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000004_requests.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_login_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000002000004_requests.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000003000000_sessions.cockroach.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000003000000_sessions.cockroach.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"authenticated_at\" timestamp NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"sessions_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000003000000_sessions.mysql.down.sql",
    "content": "DROP TABLE `sessions`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000003000000_sessions.mysql.up.sql",
    "content": "CREATE TABLE `sessions` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`authenticated_at` DATETIME NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000003000000_sessions.postgres.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000003000000_sessions.postgres.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"authenticated_at\" timestamp NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000003000000_sessions.sqlite3.down.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000003000000_sessions.sqlite3.up.sql",
    "content": "CREATE TABLE \"sessions\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000004000000_errors.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000004000000_errors.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"errors\" json NOT NULL,\n\"seen_at\" timestamp NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000004000000_errors.mysql.down.sql",
    "content": "DROP TABLE `selfservice_errors`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000004000000_errors.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_errors` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`errors` JSON NOT NULL,\n`seen_at` DATETIME NOT NULL,\n`was_seen` bool NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000004000000_errors.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000004000000_errors.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"errors\" jsonb NOT NULL,\n\"seen_at\" timestamp NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000004000000_errors.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000004000000_errors.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_errors\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME NOT NULL,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000005000000_identities.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000005000000_identities.mysql.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255) BINARY;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000005000001_identities.mysql.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers MODIFY COLUMN identifier VARCHAR(255);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000005000001_identities.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000006000000_courier.cockroach.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000006000000_courier.cockroach.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"type\" int NOT NULL,\n\"status\" int NOT NULL,\n\"body\" VARCHAR (255) NOT NULL,\n\"subject\" VARCHAR (255) NOT NULL,\n\"recipient\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000006000000_courier.mysql.down.sql",
    "content": "DROP TABLE `courier_messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000006000000_courier.mysql.up.sql",
    "content": "CREATE TABLE `courier_messages` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`type` INTEGER NOT NULL,\n`status` INTEGER NOT NULL,\n`body` VARCHAR (255) NOT NULL,\n`subject` VARCHAR (255) NOT NULL,\n`recipient` VARCHAR (255) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000006000000_courier.postgres.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000006000000_courier.postgres.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"type\" int NOT NULL,\n\"status\" int NOT NULL,\n\"body\" VARCHAR (255) NOT NULL,\n\"subject\" VARCHAR (255) NOT NULL,\n\"recipient\" VARCHAR (255) NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000006000000_courier.sqlite3.down.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000006000000_courier.sqlite3.up.sql",
    "content": "CREATE TABLE \"courier_messages\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000000_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"csrf_token\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000000_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" VARCHAR (255) NOT NULL DEFAULT '';"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000000_errors.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_errors` DROP COLUMN `csrf_token`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000000_errors.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` ADD COLUMN `csrf_token` VARCHAR (255) NOT NULL DEFAULT \"\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000000_errors.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"csrf_token\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000000_errors.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" VARCHAR (255) NOT NULL DEFAULT '';"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000000_errors.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000000_errors.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"csrf_token\" TEXT NOT NULL DEFAULT '';"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000001_errors.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000001_errors.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000002_errors.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at) SELECT id, errors, seen_at, was_seen, created_at, updated_at FROM \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000002_errors.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000003_errors.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000007000003_errors.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000000_selfservice_verification.cockroach.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000000_selfservice_verification.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"code\" VARCHAR (32) NOT NULL,\n\"status\" VARCHAR (16) NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"verified_at\" timestamp,\n\"expires_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_verifiable_addresses_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000000_selfservice_verification.mysql.down.sql",
    "content": "DROP TABLE `identity_verifiable_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000000_selfservice_verification.mysql.up.sql",
    "content": "CREATE TABLE `identity_verifiable_addresses` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`code` VARCHAR (32) NOT NULL,\n`status` VARCHAR (16) NOT NULL,\n`via` VARCHAR (16) NOT NULL,\n`verified` bool NOT NULL,\n`value` VARCHAR (400) NOT NULL,\n`verified_at` DATETIME,\n`expires_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000000_selfservice_verification.postgres.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000000_selfservice_verification.postgres.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"code\" VARCHAR (32) NOT NULL,\n\"status\" VARCHAR (16) NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"verified_at\" timestamp,\n\"expires_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000000_selfservice_verification.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000000_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_verifiable_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"code\" TEXT NOT NULL,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000001_selfservice_verification.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000001_selfservice_verification.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000001_selfservice_verification.mysql.down.sql",
    "content": "DROP TABLE `selfservice_verification_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000001_selfservice_verification.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses` (`code`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000001_selfservice_verification.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000001_selfservice_verification.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000001_selfservice_verification.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000001_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000002_selfservice_verification.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000002_selfservice_verification.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000002_selfservice_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000002_selfservice_verification.mysql.up.sql",
    "content": "CREATE INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses` (`code`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000002_selfservice_verification.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000002_selfservice_verification.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000002_selfservice_verification.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000002_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000003_selfservice_verification.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000003_selfservice_verification.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000003_selfservice_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000003_selfservice_verification.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_verifiable_addresses_status_via_uq_idx` ON `identity_verifiable_addresses` (`via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000003_selfservice_verification.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000003_selfservice_verification.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000003_selfservice_verification.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000003_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000004_selfservice_verification.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000004_selfservice_verification.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000004_selfservice_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000004_selfservice_verification.mysql.up.sql",
    "content": "CREATE INDEX `identity_verifiable_addresses_status_via_idx` ON `identity_verifiable_addresses` (`via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000004_selfservice_verification.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000004_selfservice_verification.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000004_selfservice_verification.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000004_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000005_selfservice_verification.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000005_selfservice_verification.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" json NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000005_selfservice_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000005_selfservice_verification.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_verification_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`form` JSON NOT NULL,\n`via` VARCHAR (16) NOT NULL,\n`csrf_token` VARCHAR (255) NOT NULL,\n`success` bool NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000005_selfservice_verification.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000005_selfservice_verification.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"form\" jsonb NOT NULL,\n\"via\" VARCHAR (16) NOT NULL,\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000005_selfservice_verification.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000008000005_selfservice_verification.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"form\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000009000000_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000009000000_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000009000001_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000009000001_verification.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000000_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"_seen_at_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000000_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" RENAME COLUMN \"seen_at\" TO \"_seen_at_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000000_errors.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_errors` MODIFY `seen_at` DATETIME;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000000_errors.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` MODIFY `seen_at` DATETIME;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000000_errors.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ALTER COLUMN \"seen_at\" TYPE timestamp, ALTER COLUMN \"seen_at\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000000_errors.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ALTER COLUMN \"seen_at\" TYPE timestamp, ALTER COLUMN \"seen_at\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000000_errors.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000000_errors.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000001_errors.cockroach.down.sql",
    "content": "UPDATE \"selfservice_errors\" SET \"seen_at\" = \"_seen_at_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000001_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"seen_at\" timestamp;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000001_errors.mysql.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000001_errors.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000001_errors.postgres.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000001_errors.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000001_errors.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000001_errors.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000002_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"seen_at\" timestamp;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000002_errors.cockroach.up.sql",
    "content": "UPDATE \"selfservice_errors\" SET \"seen_at\" = \"_seen_at_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000002_errors.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000002_errors.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000003_errors.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" RENAME COLUMN \"seen_at\" TO \"_seen_at_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000003_errors.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"_seen_at_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000003_errors.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000003_errors.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000004_errors.cockroach.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000004_errors.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000004_errors.sqlite3.down.sql",
    "content": "UPDATE selfservice_errors SET seen_at = '1980-01-01 00:00:00' WHERE seen_at = NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000010000004_errors.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000000_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000000_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" RENAME COLUMN \"body\" TO \"_body_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000000_courier_body_type.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000000_courier_body_type.mysql.up.sql",
    "content": "ALTER TABLE `courier_messages` MODIFY `body` text NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000000_courier_body_type.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000000_courier_body_type.postgres.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ALTER COLUMN \"body\" TYPE text, ALTER COLUMN \"body\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000000_courier_body_type.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000000_courier_body_type.sqlite3.up.sql",
    "content": "CREATE TABLE \"_courier_messages_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000001_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000001_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"body\" text;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000001_courier_body_type.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000001_courier_body_type.sqlite3.up.sql",
    "content": "INSERT INTO \"_courier_messages_tmp\" (id, type, status, body, subject, recipient, created_at, updated_at) SELECT id, type, status, body, subject, recipient, created_at, updated_at FROM \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000002_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000002_courier_body_type.cockroach.up.sql",
    "content": "UPDATE \"courier_messages\" SET \"body\" = \"_body_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000002_courier_body_type.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000002_courier_body_type.sqlite3.up.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000003_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000003_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ALTER COLUMN \"body\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000003_courier_body_type.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000003_courier_body_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"_courier_messages_tmp\" RENAME TO \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000004_courier_body_type.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000011000004_courier_body_type.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP COLUMN \"_body_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000000_login_request_forced.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"forced\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000000_login_request_forced.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000000_login_request_forced.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `forced`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000000_login_request_forced.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `forced` bool NOT NULL DEFAULT false;"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000000_login_request_forced.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"forced\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000000_login_request_forced.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000000_login_request_forced.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000000_login_request_forced.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"forced\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000001_login_request_forced.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000001_login_request_forced.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000002_login_request_forced.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at FROM \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000002_login_request_forced.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000003_login_request_forced.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20191100000012000003_login_request_forced.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000000_create_profile_request_forms.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"active_method\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000000_create_profile_request_forms.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_profile_management_request_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000000_create_profile_request_forms.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` DROP COLUMN `active_method`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000000_create_profile_request_forms.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_profile_management_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_profile_management_request_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000000_create_profile_request_forms.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"active_method\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000000_create_profile_request_forms.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_profile_management_request_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000000_create_profile_request_forms.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000000_create_profile_request_forms.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_profile_management_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_profile_management_request_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000001_create_profile_request_forms.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000001_create_profile_request_forms.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000001_create_profile_request_forms.mysql.down.sql",
    "content": "DROP TABLE `selfservice_profile_management_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000001_create_profile_request_forms.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` ADD COLUMN `active_method` VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000001_create_profile_request_forms.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000001_create_profile_request_forms.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000001_create_profile_request_forms.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000001_create_profile_request_forms.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"active_method\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000002_create_profile_request_forms.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"form\" json NOT NULL DEFAULT '{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000002_create_profile_request_forms.cockroach.up.sql",
    "content": "INSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000002_create_profile_request_forms.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` MODIFY `form` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000002_create_profile_request_forms.mysql.up.sql",
    "content": "INSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000002_create_profile_request_forms.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ALTER COLUMN \"form\" TYPE jsonb, ALTER COLUMN \"form\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000002_create_profile_request_forms.postgres.up.sql",
    "content": "INSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000002_create_profile_request_forms.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, update_successful, form) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, update_successful, form FROM \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000002_create_profile_request_forms.sqlite3.up.sql",
    "content": "INSERT INTO selfservice_profile_management_request_methods (id, method, selfservice_profile_management_request_id, config) SELECT id, 'traits', id, form FROM selfservice_profile_management_requests;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000003_create_profile_request_forms.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000003_create_profile_request_forms.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"form\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000003_create_profile_request_forms.mysql.down.sql",
    "content": "UPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000003_create_profile_request_forms.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` DROP COLUMN `form`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000003_create_profile_request_forms.postgres.down.sql",
    "content": "UPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000003_create_profile_request_forms.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" DROP COLUMN \"form\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000003_create_profile_request_forms.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"update_successful\" bool NOT NULL DEFAULT 'false',\n\"form\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000003_create_profile_request_forms.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"update_successful\" bool NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000004_create_profile_request_forms.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` ADD COLUMN `form` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000004_create_profile_request_forms.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000004_create_profile_request_forms.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"form\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000004_create_profile_request_forms.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000004_create_profile_request_forms.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000004_create_profile_request_forms.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method) SELECT id, request_url, issued_at, expires_at, update_successful, identity_id, created_at, updated_at, active_method FROM \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000005_create_profile_request_forms.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000005_create_profile_request_forms.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000006_create_profile_request_forms.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000006_create_profile_request_forms.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_profile_management_requests_tmp\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000007_create_profile_request_forms.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_profile_management_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful, form) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful, form FROM \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000007_create_profile_request_forms.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000008_create_profile_request_forms.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_profile_management_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"update_successful\" bool NOT NULL DEFAULT 'false',\n\"form\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000008_create_profile_request_forms.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000009_create_profile_request_forms.sqlite3.down.sql",
    "content": "UPDATE selfservice_profile_management_requests SET form=(SELECT * FROM (SELECT m.config FROM selfservice_profile_management_requests AS r INNER JOIN selfservice_profile_management_request_methods AS m ON r.id=m.selfservice_profile_management_request_id) as t);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000009_create_profile_request_forms.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000010_create_profile_request_forms.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" ADD COLUMN \"form\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200317160354000010_create_profile_request_forms.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200401183443000000_continuity_containers.cockroach.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200401183443000000_continuity_containers.cockroach.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identity_id\" UUID,\n\"name\" VARCHAR (255) NOT NULL,\n\"payload\" json,\n\"expires_at\" timestamp NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"continuity_containers_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200401183443000000_continuity_containers.mysql.down.sql",
    "content": "DROP TABLE `continuity_containers`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200401183443000000_continuity_containers.mysql.up.sql",
    "content": "CREATE TABLE `continuity_containers` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`identity_id` char(36),\n`name` VARCHAR (255) NOT NULL,\n`payload` JSON,\n`expires_at` DATETIME NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200401183443000000_continuity_containers.postgres.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200401183443000000_continuity_containers.postgres.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"identity_id\" UUID,\n\"name\" VARCHAR (255) NOT NULL,\n\"payload\" jsonb,\n\"expires_at\" timestamp NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200401183443000000_continuity_containers.sqlite3.down.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200401183443000000_continuity_containers.sqlite3.up.sql",
    "content": "CREATE TABLE \"continuity_containers\" (\n\"id\" TEXT PRIMARY KEY,\n\"identity_id\" char(36),\n\"name\" TEXT NOT NULL,\n\"payload\" TEXT,\n\"expires_at\" DATETIME NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000000_rename_profile_flows.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000000_rename_profile_flows.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000000_rename_profile_flows.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` RENAME TO `selfservice_profile_management_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000000_rename_profile_flows.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_request_methods` CHANGE `selfservice_profile_management_request_id` `selfservice_settings_request_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000000_rename_profile_flows.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000000_rename_profile_flows.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000000_rename_profile_flows.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_profile_management_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000000_rename_profile_flows.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME COLUMN \"selfservice_profile_management_request_id\" TO \"selfservice_settings_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000001_rename_profile_flows.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000001_rename_profile_flows.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000001_rename_profile_flows.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_request_methods` RENAME TO `selfservice_profile_management_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000001_rename_profile_flows.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_request_methods` RENAME TO `selfservice_settings_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000001_rename_profile_flows.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000001_rename_profile_flows.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000001_rename_profile_flows.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_profile_management_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000001_rename_profile_flows.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_request_methods\" RENAME TO \"selfservice_settings_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000002_rename_profile_flows.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000002_rename_profile_flows.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000002_rename_profile_flows.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_request_methods` CHANGE `selfservice_settings_request_id` `selfservice_profile_management_request_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000002_rename_profile_flows.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_profile_management_requests` RENAME TO `selfservice_settings_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000002_rename_profile_flows.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000002_rename_profile_flows.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000002_rename_profile_flows.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_profile_management_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200402142539000002_rename_profile_flows.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_profile_management_requests\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000000_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000000_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"via\" VARCHAR (16) NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_recovery_addresses_identities_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000000_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `identity_recovery_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000000_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `identity_recovery_addresses` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`via` VARCHAR (16) NOT NULL,\n`value` VARCHAR (400) NOT NULL,\n`identity_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000000_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000000_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"via\" VARCHAR (16) NOT NULL,\n\"value\" VARCHAR (400) NOT NULL,\n\"identity_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000000_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000000_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_recovery_addresses\" (\n\"id\" TEXT PRIMARY KEY,\n\"via\" TEXT NOT NULL,\n\"value\" TEXT NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000001_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000001_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000001_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `selfservice_recovery_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000001_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_recovery_addresses_status_via_uq_idx` ON `identity_recovery_addresses` (`via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000001_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000001_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000001_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000001_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000002_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000002_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000002_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `selfservice_recovery_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000002_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE INDEX `identity_recovery_addresses_status_via_idx` ON `identity_recovery_addresses` (`via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000002_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000002_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000002_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_recovery_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000002_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000003_create_recovery_addresses.cockroach.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000003_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"messages\" json,\n\"active_method\" VARCHAR (32),\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"state\" VARCHAR (32) NOT NULL,\n\"recovered_identity_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_recovery_requests_identities_id_fk\" FOREIGN KEY (\"recovered_identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000003_create_recovery_addresses.mysql.down.sql",
    "content": "DROP TABLE `identity_recovery_tokens`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000003_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_recovery_requests` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`request_url` VARCHAR (2048) NOT NULL,\n`issued_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n`expires_at` DATETIME NOT NULL,\n`messages` JSON,\n`active_method` VARCHAR (32),\n`csrf_token` VARCHAR (255) NOT NULL,\n`state` VARCHAR (32) NOT NULL,\n`recovered_identity_id` char(36),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`recovered_identity_id`) REFERENCES `identities` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000003_create_recovery_addresses.postgres.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000003_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"request_url\" VARCHAR (2048) NOT NULL,\n\"issued_at\" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" timestamp NOT NULL,\n\"messages\" jsonb,\n\"active_method\" VARCHAR (32),\n\"csrf_token\" VARCHAR (255) NOT NULL,\n\"state\" VARCHAR (32) NOT NULL,\n\"recovered_identity_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"recovered_identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000003_create_recovery_addresses.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000003_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_requests\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n\"expires_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000004_create_recovery_addresses.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000004_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"config\" json NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_recovery_request_methods_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000004_create_recovery_addresses.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000004_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_recovery_request_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`config` JSON NOT NULL,\n`selfservice_recovery_request_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_recovery_request_id`) REFERENCES `selfservice_recovery_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000004_create_recovery_addresses.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000004_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"config\" jsonb NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000004_create_recovery_addresses.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000004_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_recovery_request_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"config\" TEXT NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000005_create_recovery_addresses.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000005_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_recovery_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"identity_recovery_address_id\" UUID NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_recovery_tokens_identity_recovery_addresses_id_fk\" FOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000005_create_recovery_addresses.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000005_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE TABLE `identity_recovery_tokens` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`token` VARCHAR (64) NOT NULL,\n`used` bool NOT NULL DEFAULT false,\n`used_at` DATETIME,\n`identity_recovery_address_id` char(36) NOT NULL,\n`selfservice_recovery_request_id` char(36) NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_recovery_address_id`) REFERENCES `identity_recovery_addresses` (`id`) ON DELETE cascade,\nFOREIGN KEY (`selfservice_recovery_request_id`) REFERENCES `selfservice_recovery_requests` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000005_create_recovery_addresses.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000005_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE TABLE \"identity_recovery_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"identity_recovery_address_id\" UUID NOT NULL,\n\"selfservice_recovery_request_id\" UUID NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"selfservice_recovery_request_id\") REFERENCES \"selfservice_recovery_requests\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000005_create_recovery_addresses.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000005_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_recovery_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_request_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_recovery_request_id) REFERENCES selfservice_recovery_requests (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000006_create_recovery_addresses.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000006_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000006_create_recovery_addresses.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000006_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_recovery_addresses_code_uq_idx` ON `identity_recovery_tokens` (`token`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000006_create_recovery_addresses.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000006_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000006_create_recovery_addresses.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000006_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000007_create_recovery_addresses.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000007_create_recovery_addresses.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000007_create_recovery_addresses.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000007_create_recovery_addresses.mysql.up.sql",
    "content": "CREATE INDEX `identity_recovery_addresses_code_idx` ON `identity_recovery_tokens` (`token`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000007_create_recovery_addresses.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000007_create_recovery_addresses.postgres.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000007_create_recovery_addresses.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101057000007_create_recovery_addresses.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"identity_recovery_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101058000000_create_recovery_addresses.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101058000000_create_recovery_addresses.mysql.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64) BINARY;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101058000001_create_recovery_addresses.mysql.down.sql",
    "content": "ALTER TABLE identity_recovery_tokens MODIFY COLUMN token VARCHAR(64);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200519101058000001_create_recovery_addresses.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000000_create_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000000_create_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000000_create_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000000_create_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000000_create_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000000_create_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000000_create_messages.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000000_create_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000001_create_messages.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000001_create_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000002_create_messages.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, update_successful FROM \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000002_create_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000003_create_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"update_successful\" bool NOT NULL DEFAULT 'false',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101000000003_create_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101001000000_verification.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101001000000_verification.mysql.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(32) BINARY;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101001000001_verification.mysql.down.sql",
    "content": "ALTER TABLE identity_verifiable_addresses MODIFY COLUMN code VARCHAR(255) BINARY;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200601101001000001_verification.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000000_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000000_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000000_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000000_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000000_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000000_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000000_messages.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_registration_requests_tmp\" RENAME TO \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000000_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000001_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000001_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000001_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000001_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000001_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000001_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000001_messages.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000001_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000002_messages.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000002_messages.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000002_messages.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000002_messages.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000002_messages.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000002_messages.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000002_messages.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_registration_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at FROM \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000002_messages.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000003_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_registration_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000003_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000004_messages.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000004_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000005_messages.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000005_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000006_messages.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced FROM \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000006_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000007_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false'\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000007_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000008_messages.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_requests_tmp\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000008_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000009_messages.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000009_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000010_messages.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_requests_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, nid, form, via, success) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, nid, form, via, success FROM \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000010_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000011_messages.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_verification_flows_nid_idx\" ON \"_selfservice_verification_requests_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000011_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000012_messages.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\n\"form\" TEXT,\n\"via\" TEXT NOT NULL DEFAULT 'email',\n\"success\" bool NOT NULL DEFAULT 'FALSE'\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000012_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000013_messages.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_verification_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200605111551000013_messages.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000000_settings.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000000_settings.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000000_settings.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `update_successful` bool NOT NULL DEFAULT false;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000000_settings.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `state` VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000000_settings.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000000_settings.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000000_settings.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"update_successful\" bool NOT NULL DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000000_settings.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000001_settings.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"state\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000001_settings.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"update_successful\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000001_settings.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `state`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000001_settings.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `update_successful`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000001_settings.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"state\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000001_settings.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"update_successful\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000001_settings.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000001_settings.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000002_settings.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000002_settings.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages, state FROM \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000003_settings.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, messages FROM \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000003_settings.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000004_settings.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"messages\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200607165100000004_settings.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200705105359000000_rename_identities_schema.cockroach.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200705105359000000_rename_identities_schema.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200705105359000000_rename_identities_schema.mysql.down.sql",
    "content": "ALTER TABLE `identities` CHANGE `schema_id` `traits_schema_id` varchar(2048) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200705105359000000_rename_identities_schema.mysql.up.sql",
    "content": "ALTER TABLE `identities` CHANGE `traits_schema_id` `schema_id` varchar(2048) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200705105359000000_rename_identities_schema.postgres.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200705105359000000_rename_identities_schema.postgres.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200705105359000000_rename_identities_schema.sqlite3.down.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"schema_id\" TO \"traits_schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200705105359000000_rename_identities_schema.sqlite3.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"traits_schema_id\" TO \"schema_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000000_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000000_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000000_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` DROP COLUMN `type`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000000_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000000_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000000_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000000_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_requests_tmp\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000000_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000001_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000001_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000001_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_requests` DROP COLUMN `type`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000001_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000001_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000001_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000001_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000001_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000002_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000002_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000002_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` DROP COLUMN `type`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000002_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000002_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000002_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000002_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_requests_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, nid, messages, form, via, success) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, nid, messages, form, via, success FROM \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000002_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000003_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000003_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000003_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` DROP COLUMN `type`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000003_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000003_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000003_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000003_flow_type.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_verification_flows_nid_idx\" ON \"_selfservice_verification_requests_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000003_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000004_flow_type.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000004_flow_type.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000004_flow_type.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_requests` DROP COLUMN `type`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000004_flow_type.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` ADD COLUMN `type` VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000004_flow_type.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" DROP COLUMN \"type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000004_flow_type.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" VARCHAR (16) NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000004_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\n\"messages\" TEXT,\n\"form\" TEXT,\n\"via\" TEXT NOT NULL DEFAULT 'email',\n\"success\" bool NOT NULL DEFAULT 'FALSE'\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000004_flow_type.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" ADD COLUMN \"type\" TEXT NOT NULL DEFAULT 'browser';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000005_flow_type.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_verification_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000005_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000006_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_recovery_requests_tmp\" RENAME TO \"selfservice_recovery_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000006_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000007_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_recovery_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000007_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000008_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_recovery_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, nid, messages) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, nid, messages FROM \"selfservice_recovery_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000008_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000009_flow_type.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_recovery_flows_nid_idx\" ON \"_selfservice_recovery_requests_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000009_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000010_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_recovery_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\n\"messages\" TEXT,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000010_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000011_flow_type.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_recovery_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000011_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000012_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_settings_requests_tmp\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000012_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000013_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000013_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000014_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_settings_requests_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, messages) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, messages FROM \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000014_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000015_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"messages\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000015_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000016_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_registration_requests_tmp\" RENAME TO \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000016_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000017_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000017_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000018_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_registration_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, messages FROM \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000018_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000019_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_registration_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000019_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000020_flow_type.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_requests_tmp\" RENAME TO \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000020_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000021_flow_type.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000021_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000022_flow_type.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_requests_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, messages FROM \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000022_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000023_flow_type.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_requests_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"messages\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810141652000023_flow_type.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000000_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000000_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000000_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` RENAME TO `selfservice_verification_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000000_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_request_methods` RENAME TO `selfservice_login_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000000_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000000_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000000_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" RENAME TO \"selfservice_verification_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000000_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_request_methods\" RENAME TO \"selfservice_login_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000001_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000001_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000001_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` RENAME TO `selfservice_recovery_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000001_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_requests` RENAME TO `selfservice_login_flows`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000001_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000001_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000001_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" RENAME TO \"selfservice_recovery_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000001_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_requests\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000002_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000002_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000002_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_flow_methods` RENAME TO `selfservice_recovery_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000002_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_request_methods` RENAME TO `selfservice_registration_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000002_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000002_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000002_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME TO \"selfservice_recovery_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000002_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_request_methods\" RENAME TO \"selfservice_registration_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000003_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000003_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000003_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` RENAME TO `selfservice_settings_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000003_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_requests` RENAME TO `selfservice_registration_flows`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000003_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000003_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000003_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" RENAME TO \"selfservice_settings_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000003_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_requests\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000004_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000004_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000004_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flow_methods` RENAME TO `selfservice_settings_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000004_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_request_methods` RENAME TO `selfservice_settings_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000004_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000004_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000004_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME TO \"selfservice_settings_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000004_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_request_methods\" RENAME TO \"selfservice_settings_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000005_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000005_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000005_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` RENAME TO `selfservice_registration_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000005_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_requests` RENAME TO `selfservice_settings_flows`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000005_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000005_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000005_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" RENAME TO \"selfservice_registration_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000005_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_requests\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000006_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000006_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000006_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flow_methods` RENAME TO `selfservice_registration_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000006_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_request_methods` RENAME TO `selfservice_recovery_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000006_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000006_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000006_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME TO \"selfservice_registration_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000006_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_request_methods\" RENAME TO \"selfservice_recovery_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000007_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000007_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000007_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` RENAME TO `selfservice_login_request_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000007_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_requests` RENAME TO `selfservice_recovery_flows`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000007_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000007_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000007_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME TO \"selfservice_login_request_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000007_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_requests\" RENAME TO \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000008_flow_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000008_flow_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000008_flow_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` RENAME TO `selfservice_login_requests`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000008_flow_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_requests` RENAME TO `selfservice_verification_flows`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000008_flow_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000008_flow_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000008_flow_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME TO \"selfservice_login_requests\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810161022000008_flow_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_requests\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000000_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000000_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000000_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_flow_methods` CHANGE `selfservice_recovery_flow_id` `selfservice_recovery_request_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000000_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` CHANGE `selfservice_login_request_id` `selfservice_login_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000000_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000000_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000000_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000000_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_request_id\" TO \"selfservice_login_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000001_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000001_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000001_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flow_methods` CHANGE `selfservice_settings_flow_id` `selfservice_settings_request_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000001_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flow_methods` CHANGE `selfservice_registration_request_id` `selfservice_registration_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000001_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000001_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000001_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_flow_id\" TO \"selfservice_settings_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000001_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_request_id\" TO \"selfservice_registration_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000002_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000002_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000002_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flow_methods` CHANGE `selfservice_registration_flow_id` `selfservice_registration_request_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000002_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_flow_methods` CHANGE `selfservice_recovery_request_id` `selfservice_recovery_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000002_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000002_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000002_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flow_methods\" RENAME COLUMN \"selfservice_registration_flow_id\" TO \"selfservice_registration_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000002_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flow_methods\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000003_flow_fields_rename.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000003_flow_fields_rename.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000003_flow_fields_rename.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flow_methods` CHANGE `selfservice_login_flow_id` `selfservice_login_request_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000003_flow_fields_rename.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flow_methods` CHANGE `selfservice_settings_request_id` `selfservice_settings_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000003_flow_fields_rename.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000003_flow_fields_rename.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000003_flow_fields_rename.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flow_methods\" RENAME COLUMN \"selfservice_login_flow_id\" TO \"selfservice_login_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200810162450000003_flow_fields_rename.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flow_methods\" RENAME COLUMN \"selfservice_settings_request_id\" TO \"selfservice_settings_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000000_add_session_token.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"token\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000000_add_session_token.cockroach.up.sql",
    "content": "DELETE FROM sessions;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000000_add_session_token.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `token`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000000_add_session_token.mysql.up.sql",
    "content": "DELETE FROM sessions;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000000_add_session_token.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"token\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000000_add_session_token.postgres.up.sql",
    "content": "DELETE FROM sessions;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000000_add_session_token.sqlite3.down.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000000_add_session_token.sqlite3.up.sql",
    "content": "DELETE FROM sessions;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000001_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000001_add_session_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000001_add_session_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000001_add_session_token.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `token` VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000001_add_session_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000001_add_session_token.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000001_add_session_token.sqlite3.down.sql",
    "content": "\nDROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000001_add_session_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"token\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000002_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000002_add_session_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" RENAME COLUMN \"token\" TO \"_token_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000002_add_session_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000002_add_session_token.mysql.up.sql",
    "content": "ALTER TABLE `sessions` MODIFY `token` VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000002_add_session_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000002_add_session_token.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ALTER COLUMN \"token\" TYPE VARCHAR (32), ALTER COLUMN \"token\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000002_add_session_token.sqlite3.down.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, nid) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, nid FROM \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000002_add_session_token.sqlite3.up.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000003_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000003_add_session_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"token\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000003_add_session_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000003_add_session_token.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `sessions_token_uq_idx` ON `sessions` (`token`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000003_add_session_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000003_add_session_token.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000003_add_session_token.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"_sessions_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000003_add_session_token.sqlite3.up.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token FROM \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000004_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000004_add_session_token.cockroach.up.sql",
    "content": "UPDATE \"sessions\" SET \"token\" = \"_token_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000004_add_session_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000004_add_session_token.mysql.up.sql",
    "content": "CREATE INDEX `sessions_token_idx` ON `sessions` (`token`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000004_add_session_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000004_add_session_token.postgres.up.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000004_add_session_token.sqlite3.down.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000004_add_session_token.sqlite3.up.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000005_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000005_add_session_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"_token_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000005_add_session_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000005_add_session_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000006_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000006_add_session_token.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000006_add_session_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000006_add_session_token.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000007_add_session_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000007_add_session_token.cockroach.up.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000007_add_session_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812124254000007_add_session_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"sessions\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000000_add_session_revoke.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"active\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000000_add_session_revoke.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" boolean DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000000_add_session_revoke.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `active`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000000_add_session_revoke.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `active` boolean DEFAULT false;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000000_add_session_revoke.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"active\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000000_add_session_revoke.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" boolean DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000000_add_session_revoke.sqlite3.down.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000000_add_session_revoke.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"active\" NUMERIC DEFAULT 'false';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000001_add_session_revoke.sqlite3.down.sql",
    "content": "\nDROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000001_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000002_add_session_revoke.sqlite3.down.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, nid) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, nid FROM \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000002_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000003_add_session_revoke.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000003_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000004_add_session_revoke.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000004_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000005_add_session_revoke.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"_sessions_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000005_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000006_add_session_revoke.sqlite3.down.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\n\"nid\" char(36),\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000006_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000007_add_session_revoke.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000007_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000008_add_session_revoke.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000008_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000009_add_session_revoke.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200812160551000009_add_session_revoke.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830121710000000_update_recovery_token.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830121710000000_update_recovery_token.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830121710000000_update_recovery_token.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` CHANGE `selfservice_recovery_flow_id` `selfservice_recovery_request_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830121710000000_update_recovery_token.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` CHANGE `selfservice_recovery_request_id` `selfservice_recovery_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830121710000000_update_recovery_token.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830121710000000_update_recovery_token.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830121710000000_update_recovery_token.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"selfservice_recovery_request_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830121710000000_update_recovery_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_request_id\" TO \"selfservice_recovery_flow_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000000_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000000_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000000_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `success` bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000000_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `state` VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000000_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000000_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000000_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"success\" bool NOT NULL DEFAULT FALSE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000000_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'show_form';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000001_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" VARCHAR (16) NOT NULL DEFAULT 'email';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000001_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000001_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `via` VARCHAR (16) NOT NULL DEFAULT 'email';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000001_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000001_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" VARCHAR (16) NOT NULL DEFAULT 'email';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000001_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000001_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"via\" TEXT NOT NULL DEFAULT 'email';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000001_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000002_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"state\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000002_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000002_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `state`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000002_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000002_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"state\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000002_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000002_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000002_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000003_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"active_method\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000003_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000003_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `active_method`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000003_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000003_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"active_method\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000003_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000003_add_verification_methods.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000003_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000004_add_verification_methods.cockroach.down.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000004_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000004_add_verification_methods.mysql.down.sql",
    "content": "DROP TABLE `selfservice_verification_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000004_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000004_add_verification_methods.postgres.down.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000004_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000004_add_verification_methods.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, nid, messages, form) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, nid, messages, form FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000004_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000005_add_verification_methods.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"form\" json NOT NULL DEFAULT '{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000005_add_verification_methods.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000005_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` MODIFY `form` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000005_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000005_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ALTER COLUMN \"form\" TYPE jsonb, ALTER COLUMN \"form\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000005_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000005_add_verification_methods.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_verification_flows_nid_idx\" ON \"_selfservice_verification_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000005_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000006_add_verification_methods.mysql.down.sql",
    "content": "UPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000006_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000006_add_verification_methods.postgres.down.sql",
    "content": "UPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000006_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000006_add_verification_methods.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"nid\" char(36),\n\"messages\" TEXT,\n\"form\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000006_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000007_add_verification_methods.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `form` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000007_add_verification_methods.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000007_add_verification_methods.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"form\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000007_add_verification_methods.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000007_add_verification_methods.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_verification_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000007_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000008_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000008_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000009_add_verification_methods.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000009_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000010_add_verification_methods.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, nid, messages, form) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, nid, messages, form FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000010_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000011_add_verification_methods.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_verification_flows_nid_idx\" ON \"_selfservice_verification_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000011_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000012_add_verification_methods.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"nid\" char(36),\n\"messages\" TEXT,\n\"form\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000012_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000013_add_verification_methods.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_verification_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000013_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000014_add_verification_methods.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000014_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000015_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000015_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000016_add_verification_methods.sqlite3.down.sql",
    "content": "DROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000016_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000017_add_verification_methods.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method, nid, messages, form) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method, nid, messages, form FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000017_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000018_add_verification_methods.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_verification_flows_nid_idx\" ON \"_selfservice_verification_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000018_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000019_add_verification_methods.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT,\n\"nid\" char(36),\n\"messages\" TEXT,\n\"form\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000019_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000020_add_verification_methods.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_verification_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000020_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000021_add_verification_methods.sqlite3.down.sql",
    "content": "UPDATE selfservice_verification_flows SET form=(SELECT * FROM (SELECT m.config FROM selfservice_verification_flows AS r INNER JOIN selfservice_verification_flow_methods AS m ON r.id=m.selfservice_verification_flow_id) as t);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000021_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000022_add_verification_methods.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"form\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130642000022_add_verification_methods.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130643000000_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130643000000_add_verification_methods.cockroach.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130643000000_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130643000000_add_verification_methods.mysql.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130643000000_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130643000000_add_verification_methods.postgres.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130643000000_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130643000000_add_verification_methods.sqlite3.up.sql",
    "content": "UPDATE selfservice_verification_flows SET state='passed_challenge' WHERE success IS TRUE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000000_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000000_add_verification_methods.cockroach.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000000_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000000_add_verification_methods.mysql.up.sql",
    "content": "CREATE TABLE `selfservice_verification_flow_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_verification_flow_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000000_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000000_add_verification_methods.postgres.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000000_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000000_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_verification_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000001_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000001_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000001_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000001_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `active_method` VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000001_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000001_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000001_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130644000001_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"active_method\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130645000000_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130645000000_add_verification_methods.cockroach.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130645000000_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130645000000_add_verification_methods.mysql.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130645000000_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130645000000_add_verification_methods.postgres.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130645000000_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130645000000_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO selfservice_verification_flow_methods (id, method, selfservice_verification_flow_id, config, created_at, updated_at) SELECT id, 'link', id, form, created_at, updated_at FROM selfservice_verification_flows;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000000_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000000_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"form\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000000_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000000_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `form`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000000_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000000_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"form\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000000_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000000_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"via\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000001_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000001_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"via\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000001_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000001_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `via`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000001_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000001_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"via\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000001_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000001_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, via, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000002_add_verification_methods.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000002_add_verification_methods.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"success\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000002_add_verification_methods.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000002_add_verification_methods.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `success`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000002_add_verification_methods.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000002_add_verification_methods.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"success\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000002_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000002_add_verification_methods.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000003_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000003_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000004_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000004_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"success\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000005_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000005_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, success, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000006_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000006_add_verification_methods.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000007_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000007_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000008_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000008_add_verification_methods.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"messages\" TEXT,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000009_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000009_add_verification_methods.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, messages, type, state, active_method FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000010_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000010_add_verification_methods.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000011_add_verification_methods.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830130646000011_add_verification_methods.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000000_add_verification_token.cockroach.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000000_add_verification_token.cockroach.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"expires_at\" timestamp NOT NULL,\n\"issued_at\" timestamp NOT NULL,\n\"identity_verifiable_address_id\" UUID NOT NULL,\n\"selfservice_verification_flow_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"identity_verification_tokens_identity_verifiable_addresses_id_fk\" FOREIGN KEY (\"identity_verifiable_address_id\") REFERENCES \"identity_verifiable_addresses\" (\"id\") ON DELETE cascade,\nCONSTRAINT \"identity_verification_tokens_selfservice_verification_flows_id_fk\" FOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flows\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000000_add_verification_token.mysql.down.sql",
    "content": "DROP TABLE `identity_verification_tokens`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000000_add_verification_token.mysql.up.sql",
    "content": "CREATE TABLE `identity_verification_tokens` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`token` VARCHAR (64) NOT NULL,\n`used` bool NOT NULL DEFAULT false,\n`used_at` DATETIME,\n`expires_at` DATETIME NOT NULL,\n`issued_at` DATETIME NOT NULL,\n`identity_verifiable_address_id` char(36) NOT NULL,\n`selfservice_verification_flow_id` char(36),\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`identity_verifiable_address_id`) REFERENCES `identity_verifiable_addresses` (`id`) ON DELETE cascade,\nFOREIGN KEY (`selfservice_verification_flow_id`) REFERENCES `selfservice_verification_flows` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000000_add_verification_token.postgres.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000000_add_verification_token.postgres.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"token\" VARCHAR (64) NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" timestamp,\n\"expires_at\" timestamp NOT NULL,\n\"issued_at\" timestamp NOT NULL,\n\"identity_verifiable_address_id\" UUID NOT NULL,\n\"selfservice_verification_flow_id\" UUID,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"identity_verifiable_address_id\") REFERENCES \"identity_verifiable_addresses\" (\"id\") ON DELETE cascade,\nFOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flows\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000000_add_verification_token.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000000_add_verification_token.sqlite3.up.sql",
    "content": "CREATE TABLE \"identity_verification_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL,\n\"identity_verifiable_address_id\" char(36) NOT NULL,\n\"selfservice_verification_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON DELETE cascade,\nFOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000001_add_verification_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000001_add_verification_token.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000001_add_verification_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000001_add_verification_token.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_verification_tokens_token_uq_idx` ON `identity_verification_tokens` (`token`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000001_add_verification_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000001_add_verification_token.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000001_add_verification_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000001_add_verification_token.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000002_add_verification_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000002_add_verification_token.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000002_add_verification_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000002_add_verification_token.mysql.up.sql",
    "content": "CREATE INDEX `identity_verification_tokens_token_idx` ON `identity_verification_tokens` (`token`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000002_add_verification_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000002_add_verification_token.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000002_add_verification_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000002_add_verification_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_token_idx\" ON \"identity_verification_tokens\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000003_add_verification_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000003_add_verification_token.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000003_add_verification_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000003_add_verification_token.mysql.up.sql",
    "content": "CREATE INDEX `identity_verification_tokens_verifiable_address_id_idx` ON `identity_verification_tokens` (`identity_verifiable_address_id`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000003_add_verification_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000003_add_verification_token.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000003_add_verification_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000003_add_verification_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000004_add_verification_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000004_add_verification_token.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000004_add_verification_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000004_add_verification_token.mysql.up.sql",
    "content": "CREATE INDEX `identity_verification_tokens_verification_flow_id_idx` ON `identity_verification_tokens` (`selfservice_verification_flow_id`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000004_add_verification_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000004_add_verification_token.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000004_add_verification_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830154602000004_add_verification_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000000_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"issued_at\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000000_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000000_recovery_token_expires.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` DROP COLUMN `issued_at`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000000_recovery_token_expires.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD COLUMN `expires_at` DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000000_recovery_token_expires.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"issued_at\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000000_recovery_token_expires.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000000_recovery_token_expires.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000000_recovery_token_expires.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000001_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"expires_at\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000001_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000001_recovery_token_expires.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` DROP COLUMN `expires_at`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000001_recovery_token_expires.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD COLUMN `issued_at` DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000001_recovery_token_expires.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"expires_at\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000001_recovery_token_expires.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" timestamp NOT NULL DEFAULT '2000-01-01 00:00:00';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000001_recovery_token_expires.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000001_recovery_token_expires.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00';"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000002_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flows\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000002_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000002_recovery_token_expires.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` MODIFY `selfservice_recovery_flow_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000002_recovery_token_expires.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` MODIFY `selfservice_recovery_flow_id` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000002_recovery_token_expires.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" TYPE UUID, ALTER COLUMN \"selfservice_recovery_flow_id\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000002_recovery_token_expires.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" TYPE UUID, ALTER COLUMN \"selfservice_recovery_flow_id\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000002_recovery_token_expires.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, nid) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, nid FROM \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000002_recovery_token_expires.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000003_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_selfservice_recovery_flow_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000003_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"_selfservice_recovery_flow_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000003_recovery_token_expires.mysql.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000003_recovery_token_expires.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000003_recovery_token_expires.postgres.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000003_recovery_token_expires.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000003_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000003_recovery_token_expires.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000004_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"selfservice_recovery_flow_id\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000004_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"selfservice_recovery_flow_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000004_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000004_recovery_token_expires.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000005_recovery_token_expires.cockroach.down.sql",
    "content": "UPDATE \"identity_recovery_tokens\" SET \"selfservice_recovery_flow_id\" = \"_selfservice_recovery_flow_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000005_recovery_token_expires.cockroach.up.sql",
    "content": "UPDATE \"identity_recovery_tokens\" SET \"selfservice_recovery_flow_id\" = \"_selfservice_recovery_flow_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000005_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_tokens_nid_idx\" ON \"_identity_recovery_tokens_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000005_recovery_token_expires.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000006_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"selfservice_recovery_flow_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000006_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_selfservice_recovery_flow_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000006_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000006_recovery_token_expires.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000007_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"selfservice_recovery_flow_id\" TO \"_selfservice_recovery_flow_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000007_recovery_token_expires.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\" FOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flows\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000007_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000007_recovery_token_expires.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at FROM \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000008_recovery_token_expires.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_selfservice_recovery_requests_id_fk\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000008_recovery_token_expires.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000008_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000008_recovery_token_expires.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000009_recovery_token_expires.cockroach.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000009_recovery_token_expires.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000009_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_tokens_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000009_recovery_token_expires.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000010_recovery_token_expires.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000010_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000011_recovery_token_expires.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000011_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000012_recovery_token_expires.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, issued_at, nid) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, issued_at, nid FROM \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000012_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000013_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000013_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000014_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000014_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000015_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_tokens_nid_idx\" ON \"_identity_recovery_tokens_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000015_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000016_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"nid\" char(36),\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000016_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000017_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000017_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000018_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000018_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000019_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_tokens_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000019_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000020_recovery_token_expires.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000020_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000021_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000021_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000022_recovery_token_expires.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid FROM \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000022_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000023_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000023_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000024_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000024_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000025_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_tokens_nid_idx\" ON \"_identity_recovery_tokens_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000025_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000026_recovery_token_expires.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"nid\" char(36),\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000026_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000027_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000027_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000028_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000028_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000029_recovery_token_expires.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_tokens_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000029_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000030_recovery_token_expires.sqlite3.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE selfservice_recovery_flow_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200830172221000030_recovery_token_expires.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000000_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000000_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000000_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "CREATE INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses` (`code`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000000_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000000_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000000_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000000_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_code_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000000_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000001_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000001_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000001_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "CREATE UNIQUE INDEX `identity_verifiable_addresses_code_uq_idx` ON `identity_verifiable_addresses` (`code`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000001_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_code_idx` ON `identity_verifiable_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000001_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000001_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000001_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_code_uq_idx\" ON \"identity_verifiable_addresses\" (code);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000001_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000002_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"_expires_at_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000002_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"code\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000002_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` MODIFY `expires_at` DATETIME;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000002_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` DROP COLUMN `code`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000002_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"expires_at\" TYPE timestamp, ALTER COLUMN \"expires_at\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000002_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"code\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000002_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000002_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000003_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "UPDATE \"identity_verifiable_addresses\" SET \"expires_at\" = \"_expires_at_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000003_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"expires_at\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000003_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` MODIFY `code` VARCHAR (32) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000003_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` DROP COLUMN `expires_at`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000003_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"code\" TYPE VARCHAR (32), ALTER COLUMN \"code\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000003_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"expires_at\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000003_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000003_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000004_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000004_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000004_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000004_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000004_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000004_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000004_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at FROM \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000004_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000005_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" RENAME COLUMN \"expires_at\" TO \"_expires_at_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000005_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000005_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET code = LEFT(SHA2(RANDOM_BYTES(32), 256), 32) WHERE code IS NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000005_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000005_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET code = substr(md5(random()::text), 0, 32) WHERE code IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000005_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000005_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000005_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000006_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"_code_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000006_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000006_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` ADD COLUMN `expires_at` DATETIME;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000006_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000006_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000006_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000006_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000006_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000007_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"code\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000007_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000007_identity_verifiable_address_remove_code.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` ADD COLUMN `code` VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000007_identity_verifiable_address_remove_code.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000007_identity_verifiable_address_remove_code.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000007_identity_verifiable_address_remove_code.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000007_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"code\" TEXT NOT NULL,\n\"expires_at\" DATETIME,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000007_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, expires_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000008_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "UPDATE \"identity_verifiable_addresses\" SET \"code\" = \"_code_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000008_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000008_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000008_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "\nDROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000009_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000009_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000009_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000009_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000010_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" RENAME COLUMN \"code\" TO \"_code_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000010_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000010_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000010_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000011_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000011_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000011_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000011_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000012_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET code = substr(md5(uuid_v4()), 0, 32) WHERE code IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000012_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000012_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, code, expires_at FROM \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000012_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000013_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" timestamp;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000013_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000013_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000013_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000014_identity_verifiable_address_remove_code.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000014_identity_verifiable_address_remove_code.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000014_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000014_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000015_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"code\" TEXT NOT NULL,\n\"expires_at\" DATETIME,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000015_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000016_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000016_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "\nDROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000017_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000017_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000018_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET expires_at = CURRENT_TIMESTAMP WHERE expires_at IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000018_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000019_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "UPDATE identity_verifiable_addresses SET code = substr(hex(randomblob(32)), 0, 32) WHERE code IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000019_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000020_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"expires_at\" DATETIME;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000020_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000021_identity_verifiable_address_remove_code.sqlite3.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"code\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20200831110752000021_identity_verifiable_address_remove_code.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000000_credential_types_values.cockroach.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000000_credential_types_values.cockroach.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000000_credential_types_values.mysql.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000000_credential_types_values.mysql.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000000_credential_types_values.postgres.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000000_credential_types_values.postgres.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000000_credential_types_values.sqlite3.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'password' OR name = 'oidc';"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000000_credential_types_values.sqlite3.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '78c1b41d-8341-4507-aa60-aff1d4369670', 'password' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'password');"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000001_credential_types_values.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000001_credential_types_values.cockroach.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000001_credential_types_values.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000001_credential_types_values.mysql.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000001_credential_types_values.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000001_credential_types_values.postgres.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000001_credential_types_values.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20201201161451000001_credential_types_values.sqlite3.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6fa5e2e0-bfce-4631-b62b-cf2b0252b289', 'oidc' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'oidc');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130558000000_courier_status_index.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"courier_messages_status_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130558000000_courier_status_index.cockroach.up.sql",
    "content": "CREATE INDEX \"courier_messages_status_idx\" ON \"courier_messages\" (status);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130558000000_courier_status_index.mysql.down.sql",
    "content": "DROP INDEX `courier_messages_status_idx` ON `courier_messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130558000000_courier_status_index.mysql.up.sql",
    "content": "CREATE INDEX `courier_messages_status_idx` ON `courier_messages` (`status`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130558000000_courier_status_index.postgres.down.sql",
    "content": "DROP INDEX \"courier_messages_status_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130558000000_courier_status_index.postgres.up.sql",
    "content": "CREATE INDEX \"courier_messages_status_idx\" ON \"courier_messages\" (status);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130558000000_courier_status_index.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"courier_messages_status_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130558000000_courier_status_index.sqlite3.up.sql",
    "content": "CREATE INDEX \"courier_messages_status_idx\" ON \"courier_messages\" (status);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000000_courier_message_template.cockroach.down.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP COLUMN \"template_data\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000000_courier_message_template.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"template_type\" VARCHAR (255) NOT NULL DEFAULT '';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000000_courier_message_template.mysql.down.sql",
    "content": "ALTER TABLE `courier_messages` DROP COLUMN `template_data`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000000_courier_message_template.mysql.up.sql",
    "content": "ALTER TABLE `courier_messages` ADD COLUMN `template_type` VARCHAR (255) NOT NULL DEFAULT \"\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000000_courier_message_template.postgres.down.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP COLUMN \"template_data\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000000_courier_message_template.postgres.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"template_type\" VARCHAR (255) NOT NULL DEFAULT '';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000000_courier_message_template.sqlite3.down.sql",
    "content": "ALTER TABLE \"_courier_messages_tmp\" RENAME TO \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000000_courier_message_template.sqlite3.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"template_type\" TEXT NOT NULL DEFAULT '';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000001_courier_message_template.cockroach.down.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP COLUMN \"template_type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000001_courier_message_template.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"template_data\" BYTES;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000001_courier_message_template.mysql.down.sql",
    "content": "ALTER TABLE `courier_messages` DROP COLUMN `template_type`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000001_courier_message_template.mysql.up.sql",
    "content": "ALTER TABLE `courier_messages` ADD COLUMN `template_data` BLOB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000001_courier_message_template.postgres.down.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP COLUMN \"template_type\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000001_courier_message_template.postgres.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"template_data\" bytea;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000001_courier_message_template.sqlite3.down.sql",
    "content": "\nDROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000001_courier_message_template.sqlite3.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"template_data\" BLOB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000002_courier_message_template.sqlite3.down.sql",
    "content": "INSERT INTO \"_courier_messages_tmp\" (id, type, status, body, subject, recipient, created_at, updated_at) SELECT id, type, status, body, subject, recipient, created_at, updated_at FROM \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000002_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000003_courier_message_template.sqlite3.down.sql",
    "content": "CREATE INDEX \"courier_messages_status_idx\" ON \"_courier_messages_tmp\" (status);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000003_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000004_courier_message_template.sqlite3.down.sql",
    "content": "CREATE TABLE \"_courier_messages_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000004_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000005_courier_message_template.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"courier_messages_status_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000005_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000006_courier_message_template.sqlite3.down.sql",
    "content": "ALTER TABLE \"_courier_messages_tmp\" RENAME TO \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000006_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000007_courier_message_template.sqlite3.down.sql",
    "content": "\nDROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000007_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000008_courier_message_template.sqlite3.down.sql",
    "content": "INSERT INTO \"_courier_messages_tmp\" (id, type, status, body, subject, recipient, created_at, updated_at, template_data) SELECT id, type, status, body, subject, recipient, created_at, updated_at, template_data FROM \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000008_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000009_courier_message_template.sqlite3.down.sql",
    "content": "CREATE INDEX \"courier_messages_status_idx\" ON \"_courier_messages_tmp\" (status);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000009_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000010_courier_message_template.sqlite3.down.sql",
    "content": "CREATE TABLE \"_courier_messages_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"template_data\" BLOB\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000010_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000011_courier_message_template.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"courier_messages_status_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210307130559000011_courier_message_template.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000000_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000000_form_refactoring.cockroach.up.sql",
    "content": "DROP TABLE \"selfservice_login_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000000_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000000_form_refactoring.mysql.up.sql",
    "content": "DROP TABLE `selfservice_login_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000000_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000000_form_refactoring.postgres.up.sql",
    "content": "DROP TABLE \"selfservice_login_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000000_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000000_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_login_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000001_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000001_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000001_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `ui`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000001_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000001_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000001_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000001_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000001_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_login_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"type\" TEXT NOT NULL DEFAULT 'browser'\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000002_form_refactoring.cockroach.down.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_verification_flow_methods_selfservice_verification_flow_methods_id_fk\" FOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000002_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000002_form_refactoring.mysql.down.sql",
    "content": "CREATE TABLE `selfservice_verification_flow_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_verification_flow_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_verification_flow_id`) REFERENCES `selfservice_verification_flow_methods` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000002_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` ADD COLUMN `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000002_form_refactoring.postgres.down.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_verification_flow_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_verification_flow_id\") REFERENCES \"selfservice_verification_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000002_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"ui\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000002_form_refactoring.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000002_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_login_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type FROM \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000003_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000003_form_refactoring.cockroach.up.sql",
    "content": "UPDATE selfservice_login_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000003_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000003_form_refactoring.mysql.up.sql",
    "content": "UPDATE selfservice_login_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000003_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000003_form_refactoring.postgres.up.sql",
    "content": "UPDATE selfservice_login_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000003_form_refactoring.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method, nid) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method, nid FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000003_form_refactoring.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000004_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000004_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME COLUMN \"ui\" TO \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000004_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` DROP COLUMN `ui`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000004_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` MODIFY `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000004_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000004_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ALTER COLUMN \"ui\" TYPE jsonb, ALTER COLUMN \"ui\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000004_form_refactoring.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_verification_flows_nid_idx\" ON \"_selfservice_verification_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000004_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_login_flows_tmp\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000005_form_refactoring.cockroach.down.sql",
    "content": "CREATE TABLE \"selfservice_recovery_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_recovery_flow_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_recovery_flow_methods_selfservice_recovery_flow_methods_id_fk\" FOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000005_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000005_form_refactoring.mysql.down.sql",
    "content": "CREATE TABLE `selfservice_recovery_flow_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_recovery_flow_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_recovery_flow_id`) REFERENCES `selfservice_recovery_flow_methods` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000005_form_refactoring.mysql.up.sql",
    "content": "DROP TABLE `selfservice_registration_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000005_form_refactoring.postgres.down.sql",
    "content": "CREATE TABLE \"selfservice_recovery_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_recovery_flow_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_recovery_flow_id\") REFERENCES \"selfservice_recovery_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000005_form_refactoring.postgres.up.sql",
    "content": "DROP TABLE \"selfservice_registration_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000005_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT,\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000005_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"ui\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000006_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000006_form_refactoring.cockroach.up.sql",
    "content": "UPDATE \"selfservice_login_flows\" SET \"ui\" = \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000006_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000006_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000006_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000006_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000006_form_refactoring.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_verification_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000006_form_refactoring.sqlite3.up.sql",
    "content": "UPDATE selfservice_login_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000007_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000007_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000007_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` DROP COLUMN `ui`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000007_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` ADD COLUMN `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000007_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000007_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"ui\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000007_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"selfservice_verification_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_verification_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flow_methods (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000007_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_login_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000008_form_refactoring.cockroach.down.sql",
    "content": "CREATE TABLE \"selfservice_settings_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_settings_flow_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_settings_flow_methods_selfservice_settings_flow_methods_id_fk\" FOREIGN KEY (\"selfservice_settings_flow_id\") REFERENCES \"selfservice_settings_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000008_form_refactoring.cockroach.up.sql",
    "content": "DROP TABLE \"selfservice_registration_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000008_form_refactoring.mysql.down.sql",
    "content": "CREATE TABLE `selfservice_settings_flow_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_settings_flow_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_settings_flow_id`) REFERENCES `selfservice_settings_flow_methods` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000008_form_refactoring.mysql.up.sql",
    "content": "UPDATE selfservice_registration_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000008_form_refactoring.postgres.down.sql",
    "content": "CREATE TABLE \"selfservice_settings_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_settings_flow_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_settings_flow_id\") REFERENCES \"selfservice_settings_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000008_form_refactoring.postgres.up.sql",
    "content": "UPDATE selfservice_registration_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000008_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000008_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_login_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui FROM \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000009_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000009_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000009_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000009_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` MODIFY `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000009_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000009_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ALTER COLUMN \"ui\" TYPE jsonb, ALTER COLUMN \"ui\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000009_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_recovery_flows_tmp\" RENAME TO \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000009_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000010_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000010_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000010_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` DROP COLUMN `ui`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000010_form_refactoring.mysql.up.sql",
    "content": "DROP TABLE `selfservice_settings_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000010_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000010_form_refactoring.postgres.up.sql",
    "content": "DROP TABLE \"selfservice_settings_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000010_form_refactoring.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000010_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_login_flows_tmp\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000011_form_refactoring.cockroach.down.sql",
    "content": "CREATE TABLE \"selfservice_registration_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_flow_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_registration_flow_methods_selfservice_registration_flow_methods_id_fk\" FOREIGN KEY (\"selfservice_registration_flow_id\") REFERENCES \"selfservice_registration_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000011_form_refactoring.cockroach.up.sql",
    "content": "UPDATE selfservice_registration_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000011_form_refactoring.mysql.down.sql",
    "content": "CREATE TABLE `selfservice_registration_flow_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_registration_flow_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_registration_flow_id`) REFERENCES `selfservice_registration_flow_methods` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000011_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000011_form_refactoring.postgres.down.sql",
    "content": "CREATE TABLE \"selfservice_registration_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_registration_flow_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_registration_flow_id\") REFERENCES \"selfservice_registration_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000011_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000011_form_refactoring.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_recovery_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type, nid) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type, nid FROM \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000011_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_registration_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000012_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"messages\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000012_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" RENAME COLUMN \"ui\" TO \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000012_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` ADD COLUMN `messages` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000012_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` ADD COLUMN `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000012_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"messages\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000012_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"ui\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000012_form_refactoring.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_recovery_flows_nid_idx\" ON \"_selfservice_recovery_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000012_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_registration_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser'\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000013_form_refactoring.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000013_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000013_form_refactoring.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` DROP COLUMN `ui`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000013_form_refactoring.mysql.up.sql",
    "content": "UPDATE selfservice_settings_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000013_form_refactoring.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"ui\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000013_form_refactoring.postgres.up.sql",
    "content": "UPDATE selfservice_settings_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000013_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_recovery_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"nid\" char(36),\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000013_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_registration_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type FROM \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000014_form_refactoring.cockroach.down.sql",
    "content": "CREATE TABLE \"selfservice_login_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_flow_id\" UUID NOT NULL,\n\"config\" json NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nCONSTRAINT \"selfservice_login_flow_methods_selfservice_login_flow_methods_id_fk\" FOREIGN KEY (\"selfservice_login_flow_id\") REFERENCES \"selfservice_login_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000014_form_refactoring.cockroach.up.sql",
    "content": "UPDATE \"selfservice_registration_flows\" SET \"ui\" = \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000014_form_refactoring.mysql.down.sql",
    "content": "CREATE TABLE `selfservice_login_flow_methods` (\n`id` char(36) NOT NULL,\nPRIMARY KEY(`id`),\n`method` VARCHAR (32) NOT NULL,\n`selfservice_login_flow_id` char(36) NOT NULL,\n`config` JSON NOT NULL,\n`created_at` DATETIME NOT NULL,\n`updated_at` DATETIME NOT NULL,\nFOREIGN KEY (`selfservice_login_flow_id`) REFERENCES `selfservice_login_flow_methods` (`id`) ON DELETE cascade\n) ENGINE=InnoDB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000014_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` MODIFY `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000014_form_refactoring.postgres.down.sql",
    "content": "CREATE TABLE \"selfservice_login_flow_methods\" (\n\"id\" UUID NOT NULL,\nPRIMARY KEY(\"id\"),\n\"method\" VARCHAR (32) NOT NULL,\n\"selfservice_login_flow_id\" UUID NOT NULL,\n\"config\" jsonb NOT NULL,\n\"created_at\" timestamp NOT NULL,\n\"updated_at\" timestamp NOT NULL,\nFOREIGN KEY (\"selfservice_login_flow_id\") REFERENCES \"selfservice_login_flow_methods\" (\"id\") ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000014_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ALTER COLUMN \"ui\" TYPE jsonb, ALTER COLUMN \"ui\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000014_form_refactoring.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_recovery_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000014_form_refactoring.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000015_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000015_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000015_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000015_form_refactoring.mysql.up.sql",
    "content": "DROP TABLE `selfservice_recovery_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000015_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000015_form_refactoring.postgres.up.sql",
    "content": "DROP TABLE \"selfservice_recovery_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000015_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"selfservice_recovery_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_recovery_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flow_methods (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000015_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_registration_flows_tmp\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000016_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000016_form_refactoring.cockroach.up.sql",
    "content": "DROP TABLE \"selfservice_settings_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000016_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000016_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000016_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000016_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000016_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000016_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"ui\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000017_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000017_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000017_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000017_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` ADD COLUMN `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000017_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000017_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"ui\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000017_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_settings_flows_tmp\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000017_form_refactoring.sqlite3.up.sql",
    "content": "UPDATE selfservice_registration_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000018_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000018_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000018_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000018_form_refactoring.mysql.up.sql",
    "content": "UPDATE selfservice_recovery_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000018_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000018_form_refactoring.postgres.up.sql",
    "content": "UPDATE selfservice_recovery_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000018_form_refactoring.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000018_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_registration_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000019_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000019_form_refactoring.cockroach.up.sql",
    "content": "UPDATE selfservice_settings_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000019_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000019_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` MODIFY `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000019_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000019_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ALTER COLUMN \"ui\" TYPE jsonb, ALTER COLUMN \"ui\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000019_form_refactoring.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_settings_flows_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type FROM \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000019_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_registration_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui FROM \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000020_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000020_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" RENAME COLUMN \"ui\" TO \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000020_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000020_form_refactoring.mysql.up.sql",
    "content": "DROP TABLE `selfservice_verification_flow_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000020_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000020_form_refactoring.postgres.up.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000020_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000020_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000021_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000021_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000021_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000021_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` DROP COLUMN `messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000021_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000021_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000021_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"selfservice_settings_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_settings_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_settings_flow_id) REFERENCES selfservice_settings_flow_methods (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000021_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_registration_flows_tmp\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000022_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000022_form_refactoring.cockroach.up.sql",
    "content": "UPDATE \"selfservice_settings_flows\" SET \"ui\" = \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000022_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000022_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000022_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000022_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"ui\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000022_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000022_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_settings_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000023_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000023_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000023_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000023_form_refactoring.mysql.up.sql",
    "content": "UPDATE selfservice_verification_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000023_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000023_form_refactoring.postgres.up.sql",
    "content": "UPDATE selfservice_verification_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000023_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_registration_flows_tmp\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000023_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_settings_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000024_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000024_form_refactoring.cockroach.up.sql",
    "content": "DROP TABLE \"selfservice_recovery_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000024_form_refactoring.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000024_form_refactoring.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` MODIFY `ui` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000024_form_refactoring.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000024_form_refactoring.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ALTER COLUMN \"ui\" TYPE jsonb, ALTER COLUMN \"ui\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000024_form_refactoring.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000024_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_settings_flows_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type FROM \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000025_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000025_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000025_form_refactoring.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_registration_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type FROM \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000025_form_refactoring.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000026_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000026_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000026_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_registration_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser'\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000026_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_settings_flows_tmp\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000027_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000027_form_refactoring.cockroach.up.sql",
    "content": "UPDATE selfservice_recovery_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000027_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"selfservice_registration_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_registration_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_registration_flow_id) REFERENCES selfservice_registration_flow_methods (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000027_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"ui\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000028_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000028_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" RENAME COLUMN \"ui\" TO \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000028_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"messages\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000028_form_refactoring.sqlite3.up.sql",
    "content": "UPDATE selfservice_settings_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000029_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000029_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000029_form_refactoring.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_flows_tmp\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000029_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_settings_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000030_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000030_form_refactoring.cockroach.up.sql",
    "content": "UPDATE \"selfservice_recovery_flows\" SET \"ui\" = \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000030_form_refactoring.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000030_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_settings_flows_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui FROM \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000031_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000031_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" DROP COLUMN \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000031_form_refactoring.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type FROM \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000031_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000032_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000032_form_refactoring.cockroach.up.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000032_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"type\" TEXT NOT NULL DEFAULT 'browser'\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000032_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_settings_flows_tmp\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000033_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000033_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000033_form_refactoring.sqlite3.down.sql",
    "content": "CREATE TABLE \"selfservice_login_flow_methods\" (\n\"id\" TEXT PRIMARY KEY,\n\"method\" TEXT NOT NULL,\n\"selfservice_login_flow_id\" char(36) NOT NULL,\n\"config\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (selfservice_login_flow_id) REFERENCES selfservice_login_flow_methods (id) ON DELETE cascade\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000033_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_recovery_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000034_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000034_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000034_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000034_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_recovery_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000035_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000035_form_refactoring.cockroach.up.sql",
    "content": "UPDATE selfservice_verification_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000035_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000035_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_recovery_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type FROM \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000036_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000036_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" RENAME COLUMN \"ui\" TO \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000036_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000036_form_refactoring.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000037_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000037_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"ui\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000037_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000037_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_recovery_flows_tmp\" RENAME TO \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000038_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000038_form_refactoring.cockroach.up.sql",
    "content": "UPDATE \"selfservice_verification_flows\" SET \"ui\" = \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000038_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000038_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"ui\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000039_form_refactoring.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000039_form_refactoring.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"_ui_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000039_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000039_form_refactoring.sqlite3.up.sql",
    "content": "UPDATE selfservice_recovery_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000040_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000040_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_recovery_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000041_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000041_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_recovery_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type, ui) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type, ui FROM \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000042_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000042_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000043_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000043_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_recovery_flows_tmp\" RENAME TO \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000044_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000044_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_verification_flow_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000045_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000045_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000046_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000046_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000047_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000047_form_refactoring.sqlite3.up.sql",
    "content": "\nDROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000048_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000048_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000049_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000049_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"ui\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000050_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000050_form_refactoring.sqlite3.up.sql",
    "content": "UPDATE selfservice_verification_flows SET ui='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000051_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000051_form_refactoring.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT,\n\"ui\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000052_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000052_form_refactoring.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method, ui) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method, ui FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000053_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000053_form_refactoring.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000054_form_refactoring.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210311102338000054_form_refactoring.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000000_network.cockroach.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000000_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000000_network.mysql.down.sql",
    "content": "CREATE INDEX `identity_verifiable_addresses_status_via_idx` ON `identity_verifiable_addresses` (`via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000000_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000000_network.postgres.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000000_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000000_network.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000000_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000001_network.cockroach.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000001_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD CONSTRAINT \"selfservice_login_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000001_network.mysql.down.sql",
    "content": "CREATE UNIQUE INDEX `identity_verifiable_addresses_status_via_uq_idx` ON `identity_verifiable_addresses` (`via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000001_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` ADD CONSTRAINT `selfservice_login_flows_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000001_network.postgres.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000001_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD CONSTRAINT \"selfservice_login_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000001_network.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000001_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_login_flows DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000002_network.cockroach.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000002_network.cockroach.up.sql",
    "content": "UPDATE selfservice_login_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000002_network.mysql.down.sql",
    "content": "CREATE INDEX `identity_recovery_addresses_status_via_idx` ON `identity_recovery_addresses` (`via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000002_network.mysql.up.sql",
    "content": "UPDATE selfservice_login_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000002_network.postgres.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000002_network.postgres.up.sql",
    "content": "UPDATE selfservice_login_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000002_network.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000002_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_login_flows ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000003_network.cockroach.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000003_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP CONSTRAINT \"selfservice_login_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000003_network.mysql.down.sql",
    "content": "CREATE UNIQUE INDEX `identity_recovery_addresses_status_via_uq_idx` ON `identity_recovery_addresses` (`via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000003_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000003_network.postgres.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000003_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000003_network.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000003_network.sqlite3.up.sql",
    "content": "UPDATE selfservice_login_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000004_network.cockroach.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000004_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000004_network.mysql.down.sql",
    "content": "CREATE UNIQUE INDEX `identity_credential_identifiers_identifier_idx` ON `identity_credential_identifiers` (`identifier`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000004_network.mysql.up.sql",
    "content": "CREATE INDEX `selfservice_login_flows_nid_idx` ON `selfservice_login_flows` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000004_network.postgres.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000004_network.postgres.up.sql",
    "content": "CREATE INDEX \"selfservice_login_flows_nid_idx\" ON \"selfservice_login_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000004_network.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"identity_credential_identifiers\" (identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000004_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_login_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000005_network.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000005_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000005_network.mysql.down.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000005_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000005_network.postgres.down.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000005_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000005_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_credential_identifiers_tmp\" RENAME TO \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000005_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_login_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui, nid) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui, nid FROM \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000006_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_identifier_nid_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000006_network.cockroach.up.sql",
    "content": "UPDATE \"selfservice_login_flows\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000006_network.mysql.down.sql",
    "content": "DROP INDEX `identity_credential_identifiers_identifier_nid_uq_idx` ON `identity_credential_identifiers`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000006_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` ADD CONSTRAINT `selfservice_registration_flows_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000006_network.postgres.down.sql",
    "content": "DROP INDEX \"identity_credential_identifiers_identifier_nid_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000006_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD CONSTRAINT \"selfservice_registration_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000006_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000006_network.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000007_network.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000007_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000007_network.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000007_network.mysql.up.sql",
    "content": "UPDATE selfservice_registration_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000007_network.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000007_network.postgres.up.sql",
    "content": "UPDATE selfservice_registration_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000007_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_credential_identifiers_tmp\" (id, identifier, identity_credential_id, created_at, updated_at) SELECT id, identifier, identity_credential_id, created_at, updated_at FROM \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000007_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_login_flows_tmp\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000008_network.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_credentials\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000008_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD CONSTRAINT \"selfservice_login_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000008_network.mysql.down.sql",
    "content": "ALTER TABLE `identity_credentials` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000008_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000008_network.postgres.down.sql",
    "content": "ALTER TABLE \"identity_credentials\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000008_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000008_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_credential_identifiers_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000008_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"selfservice_login_flows_nid_idx\" ON \"selfservice_login_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000009_network.cockroach.down.sql",
    "content": "ALTER TABLE \"identities\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000009_network.cockroach.up.sql",
    "content": "CREATE INDEX \"selfservice_login_flows_nid_idx\" ON \"selfservice_login_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000009_network.mysql.down.sql",
    "content": "ALTER TABLE `identities` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000009_network.mysql.up.sql",
    "content": "CREATE INDEX `selfservice_registration_flows_nid_idx` ON `selfservice_registration_flows` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000009_network.postgres.down.sql",
    "content": "ALTER TABLE \"identities\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000009_network.postgres.up.sql",
    "content": "CREATE INDEX \"selfservice_registration_flows_nid_idx\" ON \"selfservice_registration_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000009_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_identifier_nid_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000009_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000010_network.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000010_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000010_network.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_errors` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000010_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000010_network.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000010_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000010_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000010_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_registration_flows DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000011_network.cockroach.down.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000011_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD CONSTRAINT \"selfservice_registration_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000011_network.mysql.down.sql",
    "content": "ALTER TABLE `courier_messages` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000011_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` ADD CONSTRAINT `selfservice_settings_flows_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000011_network.postgres.down.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000011_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD CONSTRAINT \"selfservice_settings_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000011_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000011_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_registration_flows ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000012_network.cockroach.down.sql",
    "content": "ALTER TABLE \"continuity_containers\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000012_network.cockroach.up.sql",
    "content": "UPDATE selfservice_registration_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000012_network.mysql.down.sql",
    "content": "ALTER TABLE `continuity_containers` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000012_network.mysql.up.sql",
    "content": "UPDATE selfservice_settings_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000012_network.postgres.down.sql",
    "content": "ALTER TABLE \"continuity_containers\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000012_network.postgres.up.sql",
    "content": "UPDATE selfservice_settings_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000012_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at FROM \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000012_network.sqlite3.up.sql",
    "content": "UPDATE selfservice_registration_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000013_network.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000013_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP CONSTRAINT \"selfservice_registration_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000013_network.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000013_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000013_network.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000013_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000013_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000013_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_registration_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000014_network.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000014_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000014_network.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000014_network.mysql.up.sql",
    "content": "CREATE INDEX `selfservice_settings_flows_nid_idx` ON `selfservice_settings_flows` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000014_network.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000014_network.postgres.up.sql",
    "content": "CREATE INDEX \"selfservice_settings_flows_nid_idx\" ON \"selfservice_settings_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000014_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_credentials_tmp\" RENAME TO \"identity_credentials\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000014_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_registration_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui, nid) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui, nid FROM \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000015_network.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000015_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000015_network.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` DROP COLUMN `nid`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000015_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000015_network.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"nid\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000015_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000015_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identity_credentials\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000015_network.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000016_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000016_network.cockroach.up.sql",
    "content": "UPDATE \"selfservice_registration_flows\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000016_network.mysql.down.sql",
    "content": "DROP INDEX `identity_recovery_addresses_status_via_idx` ON `identity_recovery_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000016_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` ADD CONSTRAINT `selfservice_errors_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000016_network.postgres.down.sql",
    "content": "DROP INDEX \"identity_recovery_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000016_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD CONSTRAINT \"selfservice_errors_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000016_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_credentials_tmp\" (id, config, identity_credential_type_id, identity_id, created_at, updated_at) SELECT id, config, identity_credential_type_id, identity_id, created_at, updated_at FROM \"identity_credentials\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000016_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_registration_flows_tmp\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000017_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000017_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000017_network.mysql.down.sql",
    "content": "DROP INDEX `identity_recovery_addresses_status_via_uq_idx` ON `identity_recovery_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000017_network.mysql.up.sql",
    "content": "UPDATE selfservice_errors SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000017_network.postgres.down.sql",
    "content": "DROP INDEX \"identity_recovery_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000017_network.postgres.up.sql",
    "content": "UPDATE selfservice_errors SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000017_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_credentials_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"config\" TEXT NOT NULL,\n\"identity_credential_type_id\" char(36) NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000017_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"selfservice_registration_flows_nid_idx\" ON \"selfservice_registration_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000018_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000018_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD CONSTRAINT \"selfservice_registration_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000018_network.mysql.down.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_status_via_idx` ON `identity_verifiable_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000018_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_errors` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000018_network.postgres.down.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000018_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000018_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identities_tmp\" RENAME TO \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000018_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000019_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000019_network.cockroach.up.sql",
    "content": "CREATE INDEX \"selfservice_registration_flows_nid_idx\" ON \"selfservice_registration_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000019_network.mysql.down.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_status_via_uq_idx` ON `identity_verifiable_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000019_network.mysql.up.sql",
    "content": "CREATE INDEX `selfservice_errors_nid_idx` ON `selfservice_errors` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000019_network.postgres.down.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000019_network.postgres.up.sql",
    "content": "CREATE INDEX \"selfservice_errors_nid_idx\" ON \"selfservice_errors\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000019_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000019_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_settings_flows DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000020_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000020_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000020_network.mysql.down.sql",
    "content": "DROP INDEX `identity_credential_identifiers_nid_idx` ON `identity_credential_identifiers`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000020_network.mysql.up.sql",
    "content": "ALTER TABLE `continuity_containers` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000020_network.postgres.down.sql",
    "content": "DROP INDEX \"identity_credential_identifiers_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000020_network.postgres.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000020_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_identities_tmp\" (id, schema_id, traits, created_at, updated_at) SELECT id, schema_id, traits, created_at, updated_at FROM \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000020_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_settings_flows ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000021_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000021_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD CONSTRAINT \"selfservice_settings_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000021_network.mysql.down.sql",
    "content": "DROP INDEX `identity_recovery_addresses_nid_idx` ON `identity_recovery_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000021_network.mysql.up.sql",
    "content": "ALTER TABLE `continuity_containers` ADD CONSTRAINT `continuity_containers_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000021_network.postgres.down.sql",
    "content": "DROP INDEX \"identity_recovery_addresses_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000021_network.postgres.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" ADD CONSTRAINT \"continuity_containers_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000021_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identities_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"schema_id\" TEXT NOT NULL,\n\"traits\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000021_network.sqlite3.up.sql",
    "content": "UPDATE selfservice_settings_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000022_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000022_network.cockroach.up.sql",
    "content": "UPDATE selfservice_settings_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000022_network.mysql.down.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_nid_idx` ON `identity_verifiable_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000022_network.mysql.up.sql",
    "content": "UPDATE continuity_containers SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000022_network.postgres.down.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000022_network.postgres.up.sql",
    "content": "UPDATE continuity_containers SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000022_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000022_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_settings_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36),\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000023_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credentials_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000023_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP CONSTRAINT \"selfservice_settings_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000023_network.mysql.down.sql",
    "content": "DROP INDEX `identity_credentials_nid_idx` ON `identity_credentials`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000023_network.mysql.up.sql",
    "content": "ALTER TABLE `continuity_containers` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000023_network.postgres.down.sql",
    "content": "DROP INDEX \"identity_credentials_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000023_network.postgres.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000023_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000023_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_settings_flows_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui, nid) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui, nid FROM \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000024_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identities_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000024_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000024_network.mysql.down.sql",
    "content": "DROP INDEX `identities_nid_idx` ON `identities`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000024_network.mysql.up.sql",
    "content": "CREATE INDEX `continuity_containers_nid_idx` ON `continuity_containers` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000024_network.postgres.down.sql",
    "content": "DROP INDEX \"identities_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000024_network.postgres.up.sql",
    "content": "CREATE INDEX \"continuity_containers_nid_idx\" ON \"continuity_containers\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000024_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token FROM \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000024_network.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000025_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_errors_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000025_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000025_network.mysql.down.sql",
    "content": "DROP INDEX `selfservice_errors_nid_idx` ON `selfservice_errors`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000025_network.mysql.up.sql",
    "content": "ALTER TABLE `courier_messages` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000025_network.postgres.down.sql",
    "content": "DROP INDEX \"selfservice_errors_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000025_network.postgres.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000025_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT ''\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000025_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_settings_flows_tmp\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000026_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"courier_messages_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000026_network.cockroach.up.sql",
    "content": "UPDATE \"selfservice_settings_flows\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000026_network.mysql.down.sql",
    "content": "DROP INDEX `courier_messages_nid_idx` ON `courier_messages`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000026_network.mysql.up.sql",
    "content": "ALTER TABLE `courier_messages` ADD CONSTRAINT `courier_messages_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000026_network.postgres.down.sql",
    "content": "DROP INDEX \"courier_messages_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000026_network.postgres.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD CONSTRAINT \"courier_messages_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000026_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_courier_messages_tmp\" RENAME TO \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000026_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"selfservice_settings_flows_nid_idx\" ON \"selfservice_settings_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000027_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"continuity_containers_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000027_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000027_network.mysql.down.sql",
    "content": "DROP INDEX `continuity_containers_nid_idx` ON `continuity_containers`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000027_network.mysql.up.sql",
    "content": "UPDATE courier_messages SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000027_network.postgres.down.sql",
    "content": "DROP INDEX \"continuity_containers_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000027_network.postgres.up.sql",
    "content": "UPDATE courier_messages SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000027_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000027_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000028_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_settings_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000028_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD CONSTRAINT \"selfservice_settings_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000028_network.mysql.down.sql",
    "content": "DROP INDEX `selfservice_settings_flows_nid_idx` ON `selfservice_settings_flows`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000028_network.mysql.up.sql",
    "content": "ALTER TABLE `courier_messages` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000028_network.postgres.down.sql",
    "content": "DROP INDEX \"selfservice_settings_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000028_network.postgres.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000028_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_courier_messages_tmp\" (id, type, status, body, subject, recipient, created_at, updated_at, template_type, template_data) SELECT id, type, status, body, subject, recipient, created_at, updated_at, template_type, template_data FROM \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000028_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_errors DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000029_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_registration_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000029_network.cockroach.up.sql",
    "content": "CREATE INDEX \"selfservice_settings_flows_nid_idx\" ON \"selfservice_settings_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000029_network.mysql.down.sql",
    "content": "DROP INDEX `selfservice_registration_flows_nid_idx` ON `selfservice_registration_flows`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000029_network.mysql.up.sql",
    "content": "CREATE INDEX `courier_messages_nid_idx` ON `courier_messages` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000029_network.postgres.down.sql",
    "content": "DROP INDEX \"selfservice_registration_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000029_network.postgres.up.sql",
    "content": "CREATE INDEX \"courier_messages_nid_idx\" ON \"courier_messages\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000029_network.sqlite3.down.sql",
    "content": "CREATE INDEX \"courier_messages_status_idx\" ON \"_courier_messages_tmp\" (status);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000029_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_errors ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000030_network.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_login_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000030_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000030_network.mysql.down.sql",
    "content": "DROP INDEX `selfservice_login_flows_nid_idx` ON `selfservice_login_flows`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000030_network.mysql.up.sql",
    "content": "ALTER TABLE `identities` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000030_network.postgres.down.sql",
    "content": "DROP INDEX \"selfservice_login_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000030_network.postgres.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000030_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_courier_messages_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"template_type\" TEXT NOT NULL DEFAULT '',\n\"template_data\" BLOB\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000030_network.sqlite3.up.sql",
    "content": "UPDATE selfservice_errors SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000031_network.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP CONSTRAINT \"identity_credential_identifiers_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000031_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD CONSTRAINT \"selfservice_errors_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000031_network.mysql.down.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` DROP FOREIGN KEY `identity_credential_identifiers_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000031_network.mysql.up.sql",
    "content": "ALTER TABLE `identities` ADD CONSTRAINT `identities_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000031_network.postgres.down.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP CONSTRAINT \"identity_credential_identifiers_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000031_network.postgres.up.sql",
    "content": "ALTER TABLE \"identities\" ADD CONSTRAINT \"identities_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000031_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"courier_messages_status_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000031_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_errors_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"errors\" TEXT NOT NULL,\n\"seen_at\" DATETIME,\n\"was_seen\" bool NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL DEFAULT '',\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000032_network.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" DROP CONSTRAINT \"identity_recovery_addresses_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000032_network.cockroach.up.sql",
    "content": "UPDATE selfservice_errors SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000032_network.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_addresses` DROP FOREIGN KEY `identity_recovery_addresses_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000032_network.mysql.up.sql",
    "content": "UPDATE identities SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000032_network.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" DROP CONSTRAINT \"identity_recovery_addresses_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000032_network.postgres.up.sql",
    "content": "UPDATE identities SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000032_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_continuity_containers_tmp\" RENAME TO \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000032_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_errors_tmp\" (id, errors, seen_at, was_seen, created_at, updated_at, csrf_token, nid) SELECT id, errors, seen_at, was_seen, created_at, updated_at, csrf_token, nid FROM \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000033_network.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP CONSTRAINT \"identity_verifiable_addresses_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000033_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP CONSTRAINT \"selfservice_errors_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000033_network.mysql.down.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` DROP FOREIGN KEY `identity_verifiable_addresses_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000033_network.mysql.up.sql",
    "content": "ALTER TABLE `identities` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000033_network.postgres.down.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP CONSTRAINT \"identity_verifiable_addresses_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000033_network.postgres.up.sql",
    "content": "ALTER TABLE \"identities\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000033_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000033_network.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000034_network.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_credentials\" DROP CONSTRAINT \"identity_credentials_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000034_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000034_network.mysql.down.sql",
    "content": "ALTER TABLE `identity_credentials` DROP FOREIGN KEY `identity_credentials_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000034_network.mysql.up.sql",
    "content": "CREATE INDEX `identities_nid_idx` ON `identities` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000034_network.postgres.down.sql",
    "content": "ALTER TABLE \"identity_credentials\" DROP CONSTRAINT \"identity_credentials_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000034_network.postgres.up.sql",
    "content": "CREATE INDEX \"identities_nid_idx\" ON \"identities\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000034_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_continuity_containers_tmp\" (id, identity_id, name, payload, expires_at, created_at, updated_at) SELECT id, identity_id, name, payload, expires_at, created_at, updated_at FROM \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000034_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_errors_tmp\" RENAME TO \"selfservice_errors\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000035_network.cockroach.down.sql",
    "content": "ALTER TABLE \"identities\" DROP CONSTRAINT \"identities_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000035_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000035_network.mysql.down.sql",
    "content": "ALTER TABLE `identities` DROP FOREIGN KEY `identities_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000035_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_credentials` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000035_network.postgres.down.sql",
    "content": "ALTER TABLE \"identities\" DROP CONSTRAINT \"identities_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000035_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000035_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_continuity_containers_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"identity_id\" char(36),\n\"name\" TEXT NOT NULL,\n\"payload\" TEXT,\n\"expires_at\" DATETIME NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000035_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"selfservice_errors_nid_idx\" ON \"selfservice_errors\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000036_network.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP CONSTRAINT \"selfservice_errors_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000036_network.cockroach.up.sql",
    "content": "UPDATE \"selfservice_errors\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000036_network.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_errors` DROP FOREIGN KEY `selfservice_errors_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000036_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_credentials` ADD CONSTRAINT `identity_credentials_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000036_network.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP CONSTRAINT \"selfservice_errors_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000036_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" ADD CONSTRAINT \"identity_credentials_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000036_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_settings_flows_tmp\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000036_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000037_network.cockroach.down.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP CONSTRAINT \"courier_messages_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000037_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000037_network.mysql.down.sql",
    "content": "ALTER TABLE `courier_messages` DROP FOREIGN KEY `courier_messages_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000037_network.mysql.up.sql",
    "content": "UPDATE identity_credentials SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000037_network.postgres.down.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP CONSTRAINT \"courier_messages_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000037_network.postgres.up.sql",
    "content": "UPDATE identity_credentials SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000037_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000037_network.sqlite3.up.sql",
    "content": "ALTER TABLE continuity_containers DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000038_network.cockroach.down.sql",
    "content": "ALTER TABLE \"continuity_containers\" DROP CONSTRAINT \"continuity_containers_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000038_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_errors\" ADD CONSTRAINT \"selfservice_errors_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000038_network.mysql.down.sql",
    "content": "ALTER TABLE `continuity_containers` DROP FOREIGN KEY `continuity_containers_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000038_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_credentials` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000038_network.postgres.down.sql",
    "content": "ALTER TABLE \"continuity_containers\" DROP CONSTRAINT \"continuity_containers_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000038_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000038_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_settings_flows_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui FROM \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000038_network.sqlite3.up.sql",
    "content": "ALTER TABLE continuity_containers ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000039_network.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP CONSTRAINT \"selfservice_settings_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000039_network.cockroach.up.sql",
    "content": "CREATE INDEX \"selfservice_errors_nid_idx\" ON \"selfservice_errors\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000039_network.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` DROP FOREIGN KEY `selfservice_settings_flows_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000039_network.mysql.up.sql",
    "content": "CREATE INDEX `identity_credentials_nid_idx` ON `identity_credentials` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000039_network.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP CONSTRAINT \"selfservice_settings_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000039_network.postgres.up.sql",
    "content": "CREATE INDEX \"identity_credentials_nid_idx\" ON \"identity_credentials\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000039_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000039_network.sqlite3.up.sql",
    "content": "UPDATE continuity_containers SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000040_network.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP CONSTRAINT \"selfservice_registration_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000040_network.cockroach.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000040_network.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` DROP FOREIGN KEY `selfservice_registration_flows_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000040_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000040_network.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP CONSTRAINT \"selfservice_registration_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000040_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000040_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_registration_flows_tmp\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000040_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_continuity_containers_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"identity_id\" char(36),\n\"name\" TEXT NOT NULL,\n\"payload\" TEXT,\n\"expires_at\" DATETIME NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000041_network.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP CONSTRAINT \"selfservice_login_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000041_network.cockroach.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" ADD CONSTRAINT \"continuity_containers_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000041_network.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` DROP FOREIGN KEY `selfservice_login_flows_nid_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000041_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` ADD CONSTRAINT `identity_credential_identifiers_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000041_network.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP CONSTRAINT \"selfservice_login_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000041_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD CONSTRAINT \"identity_credential_identifiers_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000041_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000041_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_continuity_containers_tmp\" (id, identity_id, name, payload, expires_at, created_at, updated_at, nid) SELECT id, identity_id, name, payload, expires_at, created_at, updated_at, nid FROM \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000042_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000042_network.cockroach.up.sql",
    "content": "UPDATE continuity_containers SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000042_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000042_network.mysql.up.sql",
    "content": "UPDATE identity_credential_identifiers SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000042_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000042_network.postgres.up.sql",
    "content": "UPDATE identity_credential_identifiers SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000042_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_registration_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui FROM \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000042_network.sqlite3.up.sql",
    "content": "DROP TABLE \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000043_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000043_network.cockroach.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" DROP CONSTRAINT \"continuity_containers_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000043_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000043_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000043_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000043_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000043_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_registration_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000043_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_continuity_containers_tmp\" RENAME TO \"continuity_containers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000044_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000044_network.cockroach.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000044_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000044_network.mysql.up.sql",
    "content": "CREATE INDEX `identity_credential_identifiers_nid_idx` ON `identity_credential_identifiers` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000044_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000044_network.postgres.up.sql",
    "content": "CREATE INDEX \"identity_credential_identifiers_nid_idx\" ON \"identity_credential_identifiers\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000044_network.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_flows_tmp\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000044_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"continuity_containers_nid_idx\" ON \"continuity_containers\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000045_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000045_network.cockroach.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000045_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000045_network.mysql.up.sql",
    "content": "DROP INDEX `identity_credential_identifiers_identifier_idx` ON `identity_credential_identifiers`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000045_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000045_network.postgres.up.sql",
    "content": "DROP INDEX \"identity_credential_identifiers_identifier_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000045_network.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000045_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000046_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000046_network.cockroach.up.sql",
    "content": "UPDATE \"continuity_containers\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000046_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000046_network.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_credential_identifiers_identifier_nid_uq_idx` ON `identity_credential_identifiers` (`nid`, `identifier`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000046_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000046_network.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_uq_idx\" ON \"identity_credential_identifiers\" (nid, identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000046_network.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui FROM \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000046_network.sqlite3.up.sql",
    "content": "ALTER TABLE courier_messages DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000047_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000047_network.cockroach.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000047_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000047_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000047_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000047_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000047_network.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000047_network.sqlite3.up.sql",
    "content": "ALTER TABLE courier_messages ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000048_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000048_network.cockroach.up.sql",
    "content": "ALTER TABLE \"continuity_containers\" ADD CONSTRAINT \"continuity_containers_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000048_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000048_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` ADD CONSTRAINT `selfservice_recovery_flows_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000048_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000048_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD CONSTRAINT \"selfservice_recovery_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000048_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000048_network.sqlite3.up.sql",
    "content": "UPDATE courier_messages SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000049_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000049_network.cockroach.up.sql",
    "content": "CREATE INDEX \"continuity_containers_nid_idx\" ON \"continuity_containers\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000049_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000049_network.mysql.up.sql",
    "content": "UPDATE selfservice_recovery_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000049_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000049_network.postgres.up.sql",
    "content": "UPDATE selfservice_recovery_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000049_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000049_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"courier_messages_status_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000050_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000050_network.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000050_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000050_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_recovery_flows` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000050_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000050_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000050_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000050_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_courier_messages_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"type\" INTEGER NOT NULL,\n\"status\" INTEGER NOT NULL,\n\"body\" TEXT NOT NULL,\n\"subject\" TEXT NOT NULL,\n\"recipient\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"template_type\" TEXT NOT NULL DEFAULT '',\n\"template_data\" BLOB,\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000051_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000051_network.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD CONSTRAINT \"courier_messages_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000051_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000051_network.mysql.up.sql",
    "content": "CREATE INDEX `selfservice_recovery_flows_nid_idx` ON `selfservice_recovery_flows` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000051_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000051_network.postgres.up.sql",
    "content": "CREATE INDEX \"selfservice_recovery_flows_nid_idx\" ON \"selfservice_recovery_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000051_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000051_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"courier_messages_status_idx\" ON \"_courier_messages_tmp\" (status);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000052_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000052_network.cockroach.up.sql",
    "content": "UPDATE courier_messages SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000052_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000052_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_addresses` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000052_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000052_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000052_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000052_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_courier_messages_tmp\" (id, type, status, body, subject, recipient, created_at, updated_at, template_type, template_data, nid) SELECT id, type, status, body, subject, recipient, created_at, updated_at, template_type, template_data, nid FROM \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000053_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000053_network.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP CONSTRAINT \"courier_messages_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000053_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000053_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_addresses` ADD CONSTRAINT `identity_recovery_addresses_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000053_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000053_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" ADD CONSTRAINT \"identity_recovery_addresses_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000053_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000053_network.sqlite3.up.sql",
    "content": "DROP TABLE \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000054_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000054_network.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000054_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000054_network.mysql.up.sql",
    "content": "UPDATE identity_recovery_addresses SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000054_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000054_network.postgres.up.sql",
    "content": "UPDATE identity_recovery_addresses SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000054_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000054_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_courier_messages_tmp\" RENAME TO \"courier_messages\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000055_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000055_network.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000055_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000055_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_addresses` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000055_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000055_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000055_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credentials_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000055_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"courier_messages_nid_idx\" ON \"courier_messages\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000056_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000056_network.cockroach.up.sql",
    "content": "UPDATE \"courier_messages\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000056_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000056_network.mysql.up.sql",
    "content": "CREATE INDEX `identity_recovery_addresses_nid_idx` ON `identity_recovery_addresses` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000056_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000056_network.postgres.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_nid_idx\" ON \"identity_recovery_addresses\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000056_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identities_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000056_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000057_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000057_network.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000057_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000057_network.mysql.up.sql",
    "content": "DROP INDEX `identity_recovery_addresses_status_via_uq_idx` ON `identity_recovery_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000057_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000057_network.postgres.up.sql",
    "content": "DROP INDEX \"identity_recovery_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000057_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_errors_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000057_network.sqlite3.up.sql",
    "content": "ALTER TABLE identities DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000058_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000058_network.cockroach.up.sql",
    "content": "ALTER TABLE \"courier_messages\" ADD CONSTRAINT \"courier_messages_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000058_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000058_network.mysql.up.sql",
    "content": "DROP INDEX `identity_recovery_addresses_status_via_idx` ON `identity_recovery_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000058_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000058_network.postgres.up.sql",
    "content": "DROP INDEX \"identity_recovery_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000058_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"courier_messages_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000058_network.sqlite3.up.sql",
    "content": "ALTER TABLE identities ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000059_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000059_network.cockroach.up.sql",
    "content": "CREATE INDEX \"courier_messages_nid_idx\" ON \"courier_messages\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000059_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000059_network.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_recovery_addresses_status_via_uq_idx` ON `identity_recovery_addresses` (`nid`, `via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000059_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000059_network.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000059_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"continuity_containers_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000059_network.sqlite3.up.sql",
    "content": "UPDATE identities SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000060_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000060_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000060_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000060_network.mysql.up.sql",
    "content": "CREATE INDEX `identity_recovery_addresses_status_via_idx` ON `identity_recovery_addresses` (`nid`, `via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000060_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000060_network.postgres.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000060_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_settings_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000060_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identities_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"schema_id\" TEXT NOT NULL,\n\"traits\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000061_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000061_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" ADD CONSTRAINT \"identities_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000061_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000061_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000061_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000061_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000061_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_registration_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000061_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_identities_tmp\" (id, schema_id, traits, created_at, updated_at, nid) SELECT id, schema_id, traits, created_at, updated_at, nid FROM \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000062_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000062_network.cockroach.up.sql",
    "content": "UPDATE identities SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000062_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000062_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD CONSTRAINT `identity_recovery_tokens_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000062_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000062_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000062_network.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_login_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000062_network.sqlite3.up.sql",
    "content": "DROP TABLE \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000063_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000063_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" DROP CONSTRAINT \"identities_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000063_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000063_network.mysql.up.sql",
    "content": "UPDATE identity_recovery_tokens SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000063_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000063_network.postgres.up.sql",
    "content": "UPDATE identity_recovery_tokens SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000063_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000063_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identities_tmp\" RENAME TO \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000064_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000064_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000064_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000064_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000064_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000064_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000064_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000064_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identities_nid_idx\" ON \"identities\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000065_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000065_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000065_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000065_network.mysql.up.sql",
    "content": "CREATE INDEX `identity_recovery_tokens_nid_idx` ON `identity_recovery_tokens` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000065_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000065_network.postgres.up.sql",
    "content": "CREATE INDEX \"identity_recovery_tokens_nid_idx\" ON \"identity_recovery_tokens\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000065_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000065_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000066_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000066_network.cockroach.up.sql",
    "content": "UPDATE \"identities\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000066_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000066_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000066_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000066_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000066_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000066_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_credentials DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000067_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000067_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000067_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000067_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` ADD CONSTRAINT `selfservice_verification_flows_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000067_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000067_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD CONSTRAINT \"selfservice_verification_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000067_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000067_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_credentials ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000068_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000068_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" ADD CONSTRAINT \"identities_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000068_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000068_network.mysql.up.sql",
    "content": "UPDATE selfservice_verification_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000068_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000068_network.postgres.up.sql",
    "content": "UPDATE selfservice_verification_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000068_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000068_network.sqlite3.up.sql",
    "content": "UPDATE identity_credentials SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000069_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000069_network.cockroach.up.sql",
    "content": "CREATE INDEX \"identities_nid_idx\" ON \"identities\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000069_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000069_network.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_verification_flows` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000069_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000069_network.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000069_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000069_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_credentials_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"config\" TEXT NOT NULL,\n\"identity_credential_type_id\" char(36) NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000070_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000070_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000070_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000070_network.mysql.up.sql",
    "content": "CREATE INDEX `selfservice_verification_flows_nid_idx` ON `selfservice_verification_flows` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000070_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000070_network.postgres.up.sql",
    "content": "CREATE INDEX \"selfservice_verification_flows_nid_idx\" ON \"selfservice_verification_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000070_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000070_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_credentials_tmp\" (id, config, identity_credential_type_id, identity_id, created_at, updated_at, nid) SELECT id, config, identity_credential_type_id, identity_id, created_at, updated_at, nid FROM \"identity_credentials\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000071_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000071_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" ADD CONSTRAINT \"identity_credentials_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000071_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000071_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000071_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000071_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000071_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000071_network.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_credentials\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000072_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000072_network.cockroach.up.sql",
    "content": "UPDATE identity_credentials SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000072_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000072_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` ADD CONSTRAINT `identity_verifiable_addresses_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000072_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000072_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD CONSTRAINT \"identity_verifiable_addresses_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000072_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000072_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_credentials_tmp\" RENAME TO \"identity_credentials\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000073_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000073_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" DROP CONSTRAINT \"identity_credentials_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000073_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000073_network.mysql.up.sql",
    "content": "UPDATE identity_verifiable_addresses SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000073_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000073_network.postgres.up.sql",
    "content": "UPDATE identity_verifiable_addresses SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000073_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000073_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_credentials_nid_idx\" ON \"identity_credentials\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000074_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000074_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000074_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000074_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_verifiable_addresses` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000074_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000074_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000074_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000074_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000075_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000075_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000075_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000075_network.mysql.up.sql",
    "content": "CREATE INDEX `identity_verifiable_addresses_nid_idx` ON `identity_verifiable_addresses` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000075_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000075_network.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_nid_idx\" ON \"identity_verifiable_addresses\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000075_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000075_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000076_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000076_network.cockroach.up.sql",
    "content": "UPDATE \"identity_credentials\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000076_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000076_network.mysql.up.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_status_via_uq_idx` ON `identity_verifiable_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000076_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000076_network.postgres.up.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000076_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000076_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000077_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000077_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000077_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000077_network.mysql.up.sql",
    "content": "DROP INDEX `identity_verifiable_addresses_status_via_idx` ON `identity_verifiable_addresses`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000077_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000077_network.postgres.up.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000077_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000077_network.sqlite3.up.sql",
    "content": "UPDATE identity_credential_identifiers SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000078_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000078_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credentials\" ADD CONSTRAINT \"identity_credentials_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000078_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000078_network.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_verifiable_addresses_status_via_uq_idx` ON `identity_verifiable_addresses` (`nid`, `via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000078_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000078_network.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000078_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000078_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_identifier_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000079_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000079_network.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_credentials_nid_idx\" ON \"identity_credentials\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000079_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000079_network.mysql.up.sql",
    "content": "CREATE INDEX `identity_verifiable_addresses_status_via_idx` ON `identity_verifiable_addresses` (`nid`, `via`, `value`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000079_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000079_network.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000079_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000079_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_credential_identifiers_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000080_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000080_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000080_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000080_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_verification_tokens` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000080_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000080_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000080_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000080_network.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_idx\" ON \"_identity_credential_identifiers_tmp\" (identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000081_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000081_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD CONSTRAINT \"identity_credential_identifiers_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000081_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000081_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_verification_tokens` ADD CONSTRAINT `identity_verification_tokens_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000081_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000081_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" ADD CONSTRAINT \"identity_verification_tokens_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000081_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000081_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_credential_identifiers_tmp\" (id, identifier, identity_credential_id, created_at, updated_at, nid) SELECT id, identifier, identity_credential_id, created_at, updated_at, nid FROM \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000082_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000082_network.cockroach.up.sql",
    "content": "UPDATE identity_credential_identifiers SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000082_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000082_network.mysql.up.sql",
    "content": "UPDATE identity_verification_tokens SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000082_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000082_network.postgres.up.sql",
    "content": "UPDATE identity_verification_tokens SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000082_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000082_network.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000083_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000083_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP CONSTRAINT \"identity_credential_identifiers_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000083_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000083_network.mysql.up.sql",
    "content": "ALTER TABLE `identity_verification_tokens` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000083_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000083_network.postgres.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000083_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000083_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_credential_identifiers_tmp\" RENAME TO \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000084_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000084_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000084_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000084_network.mysql.up.sql",
    "content": "CREATE INDEX `identity_verification_tokens_nid_idx` ON `identity_verification_tokens` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000084_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000084_network.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_nid_idx\" ON \"identity_verification_tokens\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000084_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000084_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_credential_identifiers_nid_idx\" ON \"identity_credential_identifiers\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000085_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000085_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000085_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000085_network.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000085_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000085_network.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000085_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000085_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_identifier_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000086_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000086_network.cockroach.up.sql",
    "content": "UPDATE \"identity_credential_identifiers\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000086_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000086_network.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD CONSTRAINT `sessions_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000086_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000086_network.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD CONSTRAINT \"sessions_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000086_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000086_network.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_uq_idx\" ON \"identity_credential_identifiers\" (nid, identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000087_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000087_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000087_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000087_network.mysql.up.sql",
    "content": "UPDATE sessions SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000087_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000087_network.postgres.up.sql",
    "content": "UPDATE sessions SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000087_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000087_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000088_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000088_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD CONSTRAINT \"identity_credential_identifiers_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000088_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000088_network.mysql.up.sql",
    "content": "ALTER TABLE `sessions` MODIFY `nid` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000088_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000088_network.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ALTER COLUMN \"nid\" TYPE UUID, ALTER COLUMN \"nid\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000088_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000088_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_recovery_flows DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000089_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000089_network.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_credential_identifiers_nid_idx\" ON \"identity_credential_identifiers\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000089_network.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000089_network.mysql.up.sql",
    "content": "CREATE INDEX `sessions_nid_idx` ON `sessions` (`id`, `nid`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000089_network.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000089_network.postgres.up.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"sessions\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000089_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000089_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_recovery_flows ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000090_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000090_network.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_identifier_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000090_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000090_network.sqlite3.up.sql",
    "content": "UPDATE selfservice_recovery_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000091_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000091_network.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_uq_idx\" ON \"identity_credential_identifiers\" (nid, identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000091_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000091_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_recovery_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"csrf_token\" TEXT NOT NULL,\n\"state\" TEXT NOT NULL,\n\"recovered_identity_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36),\nFOREIGN KEY (recovered_identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000092_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000092_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000092_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000092_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_recovery_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type, ui, nid) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, state, recovered_identity_id, created_at, updated_at, type, ui, nid FROM \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000093_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000093_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD CONSTRAINT \"selfservice_recovery_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000093_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000093_network.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000094_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000094_network.cockroach.up.sql",
    "content": "UPDATE selfservice_recovery_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000094_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000094_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_recovery_flows_tmp\" RENAME TO \"selfservice_recovery_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000095_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000095_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" DROP CONSTRAINT \"selfservice_recovery_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000095_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000095_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"selfservice_recovery_flows_nid_idx\" ON \"selfservice_recovery_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000096_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000096_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000096_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000096_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000097_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000097_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000097_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000097_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_recovery_addresses DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000098_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000098_network.cockroach.up.sql",
    "content": "UPDATE \"selfservice_recovery_flows\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000098_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000098_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_recovery_addresses ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000099_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000099_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000099_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000099_network.sqlite3.up.sql",
    "content": "UPDATE identity_recovery_addresses SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000100_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000100_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_recovery_flows\" ADD CONSTRAINT \"selfservice_recovery_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000100_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000100_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000101_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000101_network.cockroach.up.sql",
    "content": "CREATE INDEX \"selfservice_recovery_flows_nid_idx\" ON \"selfservice_recovery_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000101_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000101_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000102_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000102_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000102_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000102_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_recovery_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"via\" TEXT NOT NULL,\n\"value\" TEXT NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000103_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000103_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" ADD CONSTRAINT \"identity_recovery_addresses_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000103_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000103_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"_identity_recovery_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000104_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000104_network.cockroach.up.sql",
    "content": "UPDATE identity_recovery_addresses SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000104_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000104_network.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"_identity_recovery_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000105_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000105_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" DROP CONSTRAINT \"identity_recovery_addresses_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000105_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000105_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_recovery_addresses_tmp\" (id, via, value, identity_id, created_at, updated_at, nid) SELECT id, via, value, identity_id, created_at, updated_at, nid FROM \"identity_recovery_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000106_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000106_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000106_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000106_network.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_recovery_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000107_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000107_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000107_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000107_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_recovery_addresses_tmp\" RENAME TO \"identity_recovery_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000108_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000108_network.cockroach.up.sql",
    "content": "UPDATE \"identity_recovery_addresses\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000108_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000108_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_nid_idx\" ON \"identity_recovery_addresses\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000109_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000109_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000109_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000109_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000110_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000110_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_addresses\" ADD CONSTRAINT \"identity_recovery_addresses_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000110_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000110_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000111_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000111_network.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_nid_idx\" ON \"identity_recovery_addresses\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000111_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000111_network.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000112_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000112_network.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000112_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000112_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000113_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000113_network.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000113_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000113_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000114_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000114_network.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_status_via_uq_idx\" ON \"identity_recovery_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000114_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000114_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000115_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000115_network.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_status_via_idx\" ON \"identity_recovery_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000115_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000115_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000116_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000116_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000116_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000116_network.sqlite3.up.sql",
    "content": "UPDATE identity_recovery_tokens SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000117_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000117_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000117_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000117_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000118_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000118_network.cockroach.up.sql",
    "content": "UPDATE identity_recovery_tokens SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000118_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000118_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000119_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000119_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000119_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000119_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"nid\" char(36),\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000120_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000120_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000120_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000120_network.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000121_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000121_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000121_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000121_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000122_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000122_network.cockroach.up.sql",
    "content": "UPDATE \"identity_recovery_tokens\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000122_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000122_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid FROM \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000123_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000123_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000123_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000123_network.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000124_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000124_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000124_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000124_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000125_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000125_network.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_recovery_tokens_nid_idx\" ON \"identity_recovery_tokens\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000125_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000125_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_tokens_nid_idx\" ON \"identity_recovery_tokens\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000126_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000126_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000126_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000126_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000127_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000127_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD CONSTRAINT \"selfservice_verification_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000127_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000127_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_verification_flows DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000128_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000128_network.cockroach.up.sql",
    "content": "UPDATE selfservice_verification_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000128_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000128_network.sqlite3.up.sql",
    "content": "ALTER TABLE selfservice_verification_flows ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000129_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000129_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP CONSTRAINT \"selfservice_verification_flows_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000129_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000129_network.sqlite3.up.sql",
    "content": "UPDATE selfservice_verification_flows SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000130_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000130_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000130_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000130_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_verification_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"active_method\" TEXT,\n\"ui\" TEXT,\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000131_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000131_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000131_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000131_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_verification_flows_tmp\" (id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method, ui, nid) SELECT id, request_url, issued_at, expires_at, csrf_token, created_at, updated_at, type, state, active_method, ui, nid FROM \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000132_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000132_network.cockroach.up.sql",
    "content": "UPDATE \"selfservice_verification_flows\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000132_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000132_network.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000133_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000133_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000133_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000133_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_verification_flows_tmp\" RENAME TO \"selfservice_verification_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000134_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000134_network.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_verification_flows\" ADD CONSTRAINT \"selfservice_verification_flows_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000134_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000134_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"selfservice_verification_flows_nid_idx\" ON \"selfservice_verification_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000135_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000135_network.cockroach.up.sql",
    "content": "CREATE INDEX \"selfservice_verification_flows_nid_idx\" ON \"selfservice_verification_flows\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000135_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000135_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000136_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000136_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000136_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000136_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000137_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000137_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD CONSTRAINT \"identity_verifiable_addresses_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000137_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000137_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_verifiable_addresses ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000138_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000138_network.cockroach.up.sql",
    "content": "UPDATE identity_verifiable_addresses SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000138_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000138_network.sqlite3.up.sql",
    "content": "UPDATE identity_verifiable_addresses SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000139_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000139_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP CONSTRAINT \"identity_verifiable_addresses_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000139_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000139_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000140_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000140_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000140_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000140_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000141_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000141_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000141_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000141_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_verifiable_addresses_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"status\" TEXT NOT NULL,\n\"via\" TEXT NOT NULL,\n\"verified\" bool NOT NULL,\n\"value\" TEXT NOT NULL,\n\"verified_at\" DATETIME,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000142_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000142_network.cockroach.up.sql",
    "content": "UPDATE \"identity_verifiable_addresses\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000142_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000142_network.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000143_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000143_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000143_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000143_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"_identity_verifiable_addresses_tmp\" (via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000144_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000144_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verifiable_addresses\" ADD CONSTRAINT \"identity_verifiable_addresses_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000144_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000144_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_verifiable_addresses_tmp\" (id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, nid) SELECT id, status, via, verified, value, verified_at, identity_id, created_at, updated_at, nid FROM \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000145_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000145_network.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_nid_idx\" ON \"identity_verifiable_addresses\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000145_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000145_network.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000146_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000146_network.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000146_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000146_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_verifiable_addresses_tmp\" RENAME TO \"identity_verifiable_addresses\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000147_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000147_network.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000147_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000147_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_nid_idx\" ON \"identity_verifiable_addresses\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000148_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000148_network.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000148_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000148_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000149_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000149_network.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000149_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000149_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_status_via_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000150_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000150_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000150_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000150_network.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verifiable_addresses_status_via_uq_idx\" ON \"identity_verifiable_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000151_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000151_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" ADD CONSTRAINT \"identity_verification_tokens_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000151_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000151_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_status_via_idx\" ON \"identity_verifiable_addresses\" (nid, via, value);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000152_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000152_network.cockroach.up.sql",
    "content": "UPDATE identity_verification_tokens SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000152_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000152_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000153_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000153_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" DROP CONSTRAINT \"identity_verification_tokens_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000153_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000153_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_verification_tokens DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000154_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000154_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000154_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000154_network.sqlite3.up.sql",
    "content": "ALTER TABLE identity_verification_tokens ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000155_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000155_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000155_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000155_network.sqlite3.up.sql",
    "content": "UPDATE identity_verification_tokens SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000156_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000156_network.cockroach.up.sql",
    "content": "UPDATE \"identity_verification_tokens\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000156_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000156_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verification_tokens_verification_flow_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000157_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000157_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000157_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000157_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verification_tokens_verifiable_address_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000158_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000158_network.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_verification_tokens\" ADD CONSTRAINT \"identity_verification_tokens_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000158_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000158_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verification_tokens_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000159_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000159_network.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_nid_idx\" ON \"identity_verification_tokens\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000159_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000159_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verification_tokens_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000160_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000160_network.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000160_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000160_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_verification_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL,\n\"identity_verifiable_address_id\" char(36) NOT NULL,\n\"selfservice_verification_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000161_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000161_network.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD CONSTRAINT \"sessions_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000161_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000161_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"_identity_verification_tokens_tmp\" (selfservice_verification_flow_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000162_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000162_network.cockroach.up.sql",
    "content": "UPDATE sessions SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000162_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000162_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"_identity_verification_tokens_tmp\" (identity_verifiable_address_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000163_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000163_network.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" DROP CONSTRAINT \"sessions_nid_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000163_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000163_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_token_idx\" ON \"_identity_verification_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000164_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000164_network.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" RENAME COLUMN \"nid\" TO \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000164_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000164_network.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"_identity_verification_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000165_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000165_network.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"nid\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000165_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000165_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_verification_tokens_tmp\" (id, token, used, used_at, expires_at, issued_at, identity_verifiable_address_id, selfservice_verification_flow_id, created_at, updated_at, nid) SELECT id, token, used, used_at, expires_at, issued_at, identity_verifiable_address_id, selfservice_verification_flow_id, created_at, updated_at, nid FROM \"identity_verification_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000166_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000166_network.cockroach.up.sql",
    "content": "UPDATE \"sessions\" SET \"nid\" = \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000166_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000166_network.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_verification_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000167_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000167_network.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"_nid_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000167_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000167_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_verification_tokens_tmp\" RENAME TO \"identity_verification_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000168_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000168_network.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD CONSTRAINT \"sessions_nid_fk_idx\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000168_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000168_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verification_tokens_nid_idx\" ON \"identity_verification_tokens\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000169_network.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000169_network.cockroach.up.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"sessions\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000169_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000169_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"nid\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000170_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000170_network.sqlite3.up.sql",
    "content": "ALTER TABLE sessions DROP COLUMN nid;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000171_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000171_network.sqlite3.up.sql",
    "content": "ALTER TABLE sessions ADD COLUMN nid CHAR(36) NULL REFERENCES networks(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000172_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000172_network.sqlite3.up.sql",
    "content": "UPDATE sessions SET nid = (SELECT id FROM networks LIMIT 1);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000173_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000173_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000174_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000174_network.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000175_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000175_network.sqlite3.up.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\n\"active\" NUMERIC DEFAULT 'false',\n\"nid\" char(36),\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000176_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000176_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000177_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000177_network.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000178_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000178_network.sqlite3.up.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid FROM \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000179_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000179_network.sqlite3.up.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000180_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000180_network.sqlite3.up.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000181_network.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210410175418000181_network.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"sessions\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000000_add_identity_states.cockroach.down.sql",
    "content": "ALTER TABLE \"identities\" DROP COLUMN \"state_changed_at\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000000_add_identity_states.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'active';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000000_add_identity_states.mysql.down.sql",
    "content": "ALTER TABLE `identities` DROP COLUMN `state_changed_at`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000000_add_identity_states.mysql.up.sql",
    "content": "ALTER TABLE `identities` ADD COLUMN `state` VARCHAR (255) NOT NULL DEFAULT 'active';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000000_add_identity_states.postgres.down.sql",
    "content": "ALTER TABLE \"identities\" DROP COLUMN \"state_changed_at\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000000_add_identity_states.postgres.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"state\" VARCHAR (255) NOT NULL DEFAULT 'active';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000000_add_identity_states.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identities_tmp\" RENAME TO \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000000_add_identity_states.sqlite3.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"state\" TEXT NOT NULL DEFAULT 'active';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000001_add_identity_states.cockroach.down.sql",
    "content": "ALTER TABLE \"identities\" DROP COLUMN \"state\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000001_add_identity_states.cockroach.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"state_changed_at\" timestamp;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000001_add_identity_states.mysql.down.sql",
    "content": "ALTER TABLE `identities` DROP COLUMN `state`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000001_add_identity_states.mysql.up.sql",
    "content": "ALTER TABLE `identities` ADD COLUMN `state_changed_at` DATETIME;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000001_add_identity_states.postgres.down.sql",
    "content": "ALTER TABLE \"identities\" DROP COLUMN \"state\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000001_add_identity_states.postgres.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"state_changed_at\" timestamp;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000001_add_identity_states.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000001_add_identity_states.sqlite3.up.sql",
    "content": "ALTER TABLE \"identities\" ADD COLUMN \"state_changed_at\" DATETIME;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000002_add_identity_states.sqlite3.down.sql",
    "content": "INSERT INTO \"_identities_tmp\" (id, schema_id, traits, created_at, updated_at, nid) SELECT id, schema_id, traits, created_at, updated_at, nid FROM \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000002_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000003_add_identity_states.sqlite3.down.sql",
    "content": "CREATE INDEX \"identities_nid_idx\" ON \"_identities_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000003_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000004_add_identity_states.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identities_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"schema_id\" TEXT NOT NULL,\n\"traits\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000004_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000005_add_identity_states.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identities_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000005_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000006_add_identity_states.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identities_tmp\" RENAME TO \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000006_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000007_add_identity_states.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000007_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000008_add_identity_states.sqlite3.down.sql",
    "content": "INSERT INTO \"_identities_tmp\" (id, schema_id, traits, created_at, updated_at, nid, state_changed_at) SELECT id, schema_id, traits, created_at, updated_at, nid, state_changed_at FROM \"identities\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000008_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000009_add_identity_states.sqlite3.down.sql",
    "content": "CREATE INDEX \"identities_nid_idx\" ON \"_identities_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000009_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000010_add_identity_states.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identities_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"schema_id\" TEXT NOT NULL,\n\"traits\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\n\"state_changed_at\" DATETIME\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000010_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000011_add_identity_states.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identities_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210504121624000011_add_identity_states.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000000_logout_token.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"logout_token\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000000_logout_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"logout_token\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000000_logout_token.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `logout_token`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000000_logout_token.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `logout_token` VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000000_logout_token.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"logout_token\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000000_logout_token.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"logout_token\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000000_logout_token.sqlite3.down.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000000_logout_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"logout_token\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000001_logout_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000001_logout_token.cockroach.up.sql",
    "content": "UPDATE sessions SET logout_token = token;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000001_logout_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000001_logout_token.mysql.up.sql",
    "content": "UPDATE sessions SET logout_token = token;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000001_logout_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000001_logout_token.postgres.up.sql",
    "content": "UPDATE sessions SET logout_token = token;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000001_logout_token.sqlite3.down.sql",
    "content": "\nDROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000001_logout_token.sqlite3.up.sql",
    "content": "UPDATE sessions SET logout_token = token;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000002_logout_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000002_logout_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" RENAME COLUMN \"logout_token\" TO \"_logout_token_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000002_logout_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000002_logout_token.mysql.up.sql",
    "content": "ALTER TABLE `sessions` MODIFY `logout_token` VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000002_logout_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000002_logout_token.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ALTER COLUMN \"logout_token\" TYPE VARCHAR (32), ALTER COLUMN \"logout_token\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000002_logout_token.sqlite3.down.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid FROM \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000002_logout_token.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000003_logout_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000003_logout_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"logout_token\" VARCHAR (32);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000003_logout_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000003_logout_token.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `sessions_logout_token_uq_idx` ON `sessions` (`logout_token`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000003_logout_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000003_logout_token.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_logout_token_uq_idx\" ON \"sessions\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000003_logout_token.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"_sessions_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000003_logout_token.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000004_logout_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000004_logout_token.cockroach.up.sql",
    "content": "UPDATE \"sessions\" SET \"logout_token\" = \"_logout_token_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000004_logout_token.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000004_logout_token.mysql.up.sql",
    "content": "CREATE INDEX `sessions_logout_token_idx` ON `sessions` (`logout_token`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000004_logout_token.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000004_logout_token.postgres.up.sql",
    "content": "CREATE INDEX \"sessions_logout_token_idx\" ON \"sessions\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000004_logout_token.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000004_logout_token.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000005_logout_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000005_logout_token.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"_logout_token_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000005_logout_token.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000005_logout_token.sqlite3.up.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\n\"active\" NUMERIC DEFAULT 'false',\n\"nid\" char(36),\n\"logout_token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000006_logout_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000006_logout_token.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_logout_token_uq_idx\" ON \"sessions\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000006_logout_token.sqlite3.down.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\n\"active\" NUMERIC DEFAULT 'false',\n\"nid\" char(36),\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000006_logout_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"_sessions_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000007_logout_token.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000007_logout_token.cockroach.up.sql",
    "content": "CREATE INDEX \"sessions_logout_token_idx\" ON \"sessions\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000007_logout_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000007_logout_token.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000008_logout_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000008_logout_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000009_logout_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000009_logout_token.sqlite3.up.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid, logout_token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid, logout_token FROM \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000010_logout_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_logout_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000010_logout_token.sqlite3.up.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000011_logout_token.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_logout_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000011_logout_token.sqlite3.up.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000012_logout_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000012_logout_token.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_logout_token_uq_idx\" ON \"sessions\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000013_logout_token.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210618103120000013_logout_token.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_logout_token_idx\" ON \"sessions\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000000_settings_flow_context.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"internal_context\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000000_settings_flow_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"internal_context\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000000_settings_flow_context.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` DROP COLUMN `internal_context`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000000_settings_flow_context.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` ADD COLUMN `internal_context` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000000_settings_flow_context.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"internal_context\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000000_settings_flow_context.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"internal_context\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000000_settings_flow_context.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_settings_flows_tmp\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000000_settings_flow_context.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"internal_context\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000001_settings_flow_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000001_settings_flow_context.cockroach.up.sql",
    "content": "UPDATE selfservice_settings_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000001_settings_flow_context.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000001_settings_flow_context.mysql.up.sql",
    "content": "UPDATE selfservice_settings_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000001_settings_flow_context.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000001_settings_flow_context.postgres.up.sql",
    "content": "UPDATE selfservice_settings_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000001_settings_flow_context.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000001_settings_flow_context.sqlite3.up.sql",
    "content": "UPDATE selfservice_settings_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000002_settings_flow_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000002_settings_flow_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" RENAME COLUMN \"internal_context\" TO \"_internal_context_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000002_settings_flow_context.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000002_settings_flow_context.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_settings_flows` MODIFY `internal_context` JSON NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000002_settings_flow_context.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000002_settings_flow_context.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ALTER COLUMN \"internal_context\" TYPE jsonb, ALTER COLUMN \"internal_context\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000002_settings_flow_context.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_settings_flows_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui, nid) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui, nid FROM \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000002_settings_flow_context.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_settings_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000003_settings_flow_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000003_settings_flow_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ADD COLUMN \"internal_context\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000003_settings_flow_context.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_settings_flows_nid_idx\" ON \"_selfservice_settings_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000003_settings_flow_context.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_settings_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36),\n\"internal_context\" TEXT NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000004_settings_flow_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000004_settings_flow_context.cockroach.up.sql",
    "content": "UPDATE \"selfservice_settings_flows\" SET \"internal_context\" = \"_internal_context_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000004_settings_flow_context.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_settings_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"active_method\" TEXT,\n\"state\" TEXT NOT NULL DEFAULT 'show_form',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36),\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000004_settings_flow_context.sqlite3.up.sql",
    "content": "CREATE INDEX \"selfservice_settings_flows_nid_idx\" ON \"_selfservice_settings_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000005_settings_flow_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000005_settings_flow_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" ALTER COLUMN \"internal_context\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000005_settings_flow_context.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_settings_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000005_settings_flow_context.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_settings_flows_tmp\" (id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui, nid, internal_context) SELECT id, request_url, issued_at, expires_at, identity_id, created_at, updated_at, active_method, state, type, ui, nid, internal_context FROM \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000006_settings_flow_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000006_settings_flow_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_settings_flows\" DROP COLUMN \"_internal_context_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000006_settings_flow_context.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000006_settings_flow_context.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000007_settings_flow_context.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210805112414000007_settings_flow_context.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_settings_flows_tmp\" RENAME TO \"selfservice_settings_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805122535000000_credential_types_totp.cockroach.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'totp';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805122535000000_credential_types_totp.cockroach.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '5e29b036-aa47-457f-9fe6-aa8b854a752b', 'totp' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'totp');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805122535000000_credential_types_totp.mysql.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'totp';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805122535000000_credential_types_totp.mysql.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '5e29b036-aa47-457f-9fe6-aa8b854a752b', 'totp' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'totp');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805122535000000_credential_types_totp.postgres.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'totp';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805122535000000_credential_types_totp.postgres.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '5e29b036-aa47-457f-9fe6-aa8b854a752b', 'totp' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'totp');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805122535000000_credential_types_totp.sqlite3.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'totp';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210805122535000000_credential_types_totp.sqlite3.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '5e29b036-aa47-457f-9fe6-aa8b854a752b', 'totp' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'totp');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000000_aal.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"requested_aal\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000000_aal.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"aal\" VARCHAR (4) NOT NULL DEFAULT 'aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000000_aal.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` DROP COLUMN `requested_aal`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000000_aal.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `aal` VARCHAR (4) NOT NULL DEFAULT 'aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000000_aal.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"requested_aal\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000000_aal.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"aal\" VARCHAR (4) NOT NULL DEFAULT 'aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000000_aal.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_flows_tmp\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000000_aal.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"aal\" TEXT NOT NULL DEFAULT 'aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000001_aal.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"aal\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000001_aal.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"authentication_methods\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000001_aal.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `aal`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000001_aal.mysql.up.sql",
    "content": "ALTER TABLE `sessions` ADD COLUMN `authentication_methods` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000001_aal.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"aal\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000001_aal.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"authentication_methods\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000001_aal.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000001_aal.sqlite3.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"authentication_methods\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000002_aal.cockroach.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"authentication_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000002_aal.cockroach.up.sql",
    "content": "UPDATE sessions SET authentication_methods='[]';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000002_aal.mysql.down.sql",
    "content": "ALTER TABLE `sessions` DROP COLUMN `authentication_methods`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000002_aal.mysql.up.sql",
    "content": "UPDATE sessions SET authentication_methods='[]';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000002_aal.postgres.down.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"authentication_methods\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000002_aal.postgres.up.sql",
    "content": "UPDATE sessions SET authentication_methods='[]';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000002_aal.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui, nid) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui, nid FROM \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000002_aal.sqlite3.up.sql",
    "content": "UPDATE sessions SET authentication_methods='[]';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000003_aal.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000003_aal.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" RENAME COLUMN \"authentication_methods\" TO \"_authentication_methods_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000003_aal.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000003_aal.mysql.up.sql",
    "content": "ALTER TABLE `sessions` MODIFY `authentication_methods` JSON NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000003_aal.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000003_aal.postgres.up.sql",
    "content": "ALTER TABLE \"sessions\" ALTER COLUMN \"authentication_methods\" TYPE jsonb, ALTER COLUMN \"authentication_methods\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000003_aal.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_login_flows_nid_idx\" ON \"_selfservice_login_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000003_aal.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_logout_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000004_aal.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000004_aal.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ADD COLUMN \"authentication_methods\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000004_aal.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000004_aal.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` ADD COLUMN `requested_aal` VARCHAR (4) NOT NULL DEFAULT 'aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000004_aal.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000004_aal.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"requested_aal\" VARCHAR (4) NOT NULL DEFAULT 'aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000004_aal.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000004_aal.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_logout_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000005_aal.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000005_aal.cockroach.up.sql",
    "content": "UPDATE \"sessions\" SET \"authentication_methods\" = \"_authentication_methods_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000005_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_login_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000005_aal.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000006_aal.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000006_aal.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" ALTER COLUMN \"authentication_methods\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000006_aal.sqlite3.down.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000006_aal.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000007_aal.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000007_aal.cockroach.up.sql",
    "content": "ALTER TABLE \"sessions\" DROP COLUMN \"_authentication_methods_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000007_aal.sqlite3.down.sql",
    "content": "\nDROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000007_aal.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000008_aal.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000008_aal.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"requested_aal\" VARCHAR (4) NOT NULL DEFAULT 'aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000008_aal.sqlite3.down.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid, logout_token) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid, logout_token FROM \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000008_aal.sqlite3.up.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\n\"active\" NUMERIC DEFAULT 'false',\n\"nid\" char(36),\n\"logout_token\" TEXT,\n\"aal\" TEXT NOT NULL DEFAULT 'aal1',\n\"authentication_methods\" TEXT NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000009_aal.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_logout_token_idx\" ON \"_sessions_tmp\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000009_aal.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_logout_token_idx\" ON \"_sessions_tmp\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000010_aal.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_logout_token_uq_idx\" ON \"_sessions_tmp\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000010_aal.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_logout_token_uq_idx\" ON \"_sessions_tmp\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000011_aal.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000011_aal.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000012_aal.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000012_aal.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000013_aal.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"_sessions_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000013_aal.sqlite3.up.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"_sessions_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000014_aal.sqlite3.down.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\n\"active\" NUMERIC DEFAULT 'false',\n\"nid\" char(36),\n\"logout_token\" TEXT,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000014_aal.sqlite3.up.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid, logout_token, aal, authentication_methods) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid, logout_token, aal, authentication_methods FROM \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000015_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_logout_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000015_aal.sqlite3.up.sql",
    "content": "DROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000016_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_logout_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000016_aal.sqlite3.up.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000017_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000017_aal.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"requested_aal\" TEXT NOT NULL DEFAULT 'aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000018_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000018_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000019_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000019_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000020_aal.sqlite3.down.sql",
    "content": "ALTER TABLE \"_sessions_tmp\" RENAME TO \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000020_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000021_aal.sqlite3.down.sql",
    "content": "\nDROP TABLE \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000021_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000022_aal.sqlite3.down.sql",
    "content": "INSERT INTO \"_sessions_tmp\" (id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid, logout_token, aal) SELECT id, issued_at, expires_at, authenticated_at, identity_id, created_at, updated_at, token, active, nid, logout_token, aal FROM \"sessions\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000022_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000023_aal.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_logout_token_idx\" ON \"_sessions_tmp\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000023_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000024_aal.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_logout_token_uq_idx\" ON \"_sessions_tmp\" (logout_token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000024_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000025_aal.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_token_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000025_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000026_aal.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"sessions_token_uq_idx\" ON \"_sessions_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000026_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000027_aal.sqlite3.down.sql",
    "content": "CREATE INDEX \"sessions_nid_idx\" ON \"_sessions_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000027_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000028_aal.sqlite3.down.sql",
    "content": "CREATE TABLE \"_sessions_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"authenticated_at\" DATETIME NOT NULL,\n\"identity_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"token\" TEXT,\n\"active\" NUMERIC DEFAULT 'false',\n\"nid\" char(36),\n\"logout_token\" TEXT,\n\"aal\" TEXT NOT NULL DEFAULT 'aal1',\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000028_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000029_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_logout_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000029_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000030_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_logout_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000030_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000031_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000031_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000032_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_token_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000032_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000033_aal.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"sessions_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210810153530000033_aal.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210813150152000000_credential_types_lookup.cockroach.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'lookup_secret';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210813150152000000_credential_types_lookup.cockroach.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '567a0730-7f48-4dd7-a13d-df87a51c245f', 'lookup_secret' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'lookup_secret');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210813150152000000_credential_types_lookup.mysql.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'lookup_secret';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210813150152000000_credential_types_lookup.mysql.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '567a0730-7f48-4dd7-a13d-df87a51c245f', 'lookup_secret' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'lookup_secret');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210813150152000000_credential_types_lookup.postgres.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'lookup_secret';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210813150152000000_credential_types_lookup.postgres.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '567a0730-7f48-4dd7-a13d-df87a51c245f', 'lookup_secret' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'lookup_secret');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210813150152000000_credential_types_lookup.sqlite3.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'lookup_secret';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210813150152000000_credential_types_lookup.sqlite3.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '567a0730-7f48-4dd7-a13d-df87a51c245f', 'lookup_secret' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'lookup_secret');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816113956000000_webauthn.cockroach.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'webauthn';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816113956000000_webauthn.cockroach.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6b213fa0-e6ad-46cb-8878-b088d2ce2e3c', 'webauthn' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'webauthn');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816113956000000_webauthn.mysql.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'webauthn';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816113956000000_webauthn.mysql.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6b213fa0-e6ad-46cb-8878-b088d2ce2e3c', 'webauthn' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'webauthn');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816113956000000_webauthn.postgres.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'webauthn';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816113956000000_webauthn.postgres.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6b213fa0-e6ad-46cb-8878-b088d2ce2e3c', 'webauthn' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'webauthn');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816113956000000_webauthn.sqlite3.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'webauthn';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816113956000000_webauthn.sqlite3.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '6b213fa0-e6ad-46cb-8878-b088d2ce2e3c', 'webauthn' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'webauthn');"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000000_flow_internal_context.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"internal_context\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000000_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"internal_context\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000000_flow_internal_context.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` DROP COLUMN `internal_context`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000000_flow_internal_context.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` ADD COLUMN `internal_context` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000000_flow_internal_context.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"internal_context\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000000_flow_internal_context.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"internal_context\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000000_flow_internal_context.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_registration_flows_tmp\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000000_flow_internal_context.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"internal_context\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000001_flow_internal_context.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"internal_context\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000001_flow_internal_context.cockroach.up.sql",
    "content": "UPDATE selfservice_login_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000001_flow_internal_context.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` DROP COLUMN `internal_context`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000001_flow_internal_context.mysql.up.sql",
    "content": "UPDATE selfservice_login_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000001_flow_internal_context.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"internal_context\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000001_flow_internal_context.postgres.up.sql",
    "content": "UPDATE selfservice_login_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000001_flow_internal_context.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000001_flow_internal_context.sqlite3.up.sql",
    "content": "UPDATE selfservice_login_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000002_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000002_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" RENAME COLUMN \"internal_context\" TO \"_internal_context_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000002_flow_internal_context.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000002_flow_internal_context.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` MODIFY `internal_context` JSON NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000002_flow_internal_context.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000002_flow_internal_context.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ALTER COLUMN \"internal_context\" TYPE jsonb, ALTER COLUMN \"internal_context\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000002_flow_internal_context.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_registration_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui, nid) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui, nid FROM \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000002_flow_internal_context.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_login_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000003_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000003_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"internal_context\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000003_flow_internal_context.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000003_flow_internal_context.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` ADD COLUMN `internal_context` JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000003_flow_internal_context.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000003_flow_internal_context.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"internal_context\" jsonb;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000003_flow_internal_context.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_registration_flows_nid_idx\" ON \"_selfservice_registration_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000003_flow_internal_context.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_login_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36),\n\"requested_aal\" TEXT NOT NULL DEFAULT 'aal1',\n\"internal_context\" TEXT NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000004_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000004_flow_internal_context.cockroach.up.sql",
    "content": "UPDATE \"selfservice_login_flows\" SET \"internal_context\" = \"_internal_context_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000004_flow_internal_context.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000004_flow_internal_context.mysql.up.sql",
    "content": "UPDATE selfservice_registration_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000004_flow_internal_context.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000004_flow_internal_context.postgres.up.sql",
    "content": "UPDATE selfservice_registration_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000004_flow_internal_context.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_registration_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36)\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000004_flow_internal_context.sqlite3.up.sql",
    "content": "CREATE INDEX \"selfservice_login_flows_nid_idx\" ON \"_selfservice_login_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000005_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000005_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ALTER COLUMN \"internal_context\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000005_flow_internal_context.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000005_flow_internal_context.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_registration_flows` MODIFY `internal_context` JSON NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000005_flow_internal_context.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000005_flow_internal_context.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ALTER COLUMN \"internal_context\" TYPE jsonb, ALTER COLUMN \"internal_context\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000005_flow_internal_context.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_registration_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000005_flow_internal_context.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_login_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui, nid, requested_aal, internal_context) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui, nid, requested_aal, internal_context FROM \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000006_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000006_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"_internal_context_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000006_flow_internal_context.sqlite3.down.sql",
    "content": "ALTER TABLE \"_selfservice_login_flows_tmp\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000006_flow_internal_context.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000007_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000007_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"internal_context\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000007_flow_internal_context.sqlite3.down.sql",
    "content": "\nDROP TABLE \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000007_flow_internal_context.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_login_flows_tmp\" RENAME TO \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000008_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000008_flow_internal_context.cockroach.up.sql",
    "content": "UPDATE selfservice_registration_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000008_flow_internal_context.sqlite3.down.sql",
    "content": "INSERT INTO \"_selfservice_login_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui, nid, requested_aal) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, forced, type, ui, nid, requested_aal FROM \"selfservice_login_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000008_flow_internal_context.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"internal_context\" TEXT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000009_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000009_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" RENAME COLUMN \"internal_context\" TO \"_internal_context_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000009_flow_internal_context.sqlite3.down.sql",
    "content": "CREATE INDEX \"selfservice_login_flows_nid_idx\" ON \"_selfservice_login_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000009_flow_internal_context.sqlite3.up.sql",
    "content": "UPDATE selfservice_registration_flows SET internal_context='{}';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000010_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000010_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"internal_context\" json;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000010_flow_internal_context.sqlite3.down.sql",
    "content": "CREATE TABLE \"_selfservice_login_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"forced\" bool NOT NULL DEFAULT 'false',\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36),\n\"requested_aal\" TEXT NOT NULL DEFAULT 'aal1'\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000010_flow_internal_context.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_registration_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000011_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000011_flow_internal_context.cockroach.up.sql",
    "content": "UPDATE \"selfservice_registration_flows\" SET \"internal_context\" = \"_internal_context_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000011_flow_internal_context.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"selfservice_login_flows_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000011_flow_internal_context.sqlite3.up.sql",
    "content": "CREATE TABLE \"_selfservice_registration_flows_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"request_url\" TEXT NOT NULL,\n\"issued_at\" DATETIME NOT NULL DEFAULT 'CURRENT_TIMESTAMP',\n\"expires_at\" DATETIME NOT NULL,\n\"active_method\" TEXT NOT NULL,\n\"csrf_token\" TEXT NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"type\" TEXT NOT NULL DEFAULT 'browser',\n\"ui\" TEXT,\n\"nid\" char(36),\n\"internal_context\" TEXT NOT NULL\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000012_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000012_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" ALTER COLUMN \"internal_context\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000012_flow_internal_context.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000012_flow_internal_context.sqlite3.up.sql",
    "content": "CREATE INDEX \"selfservice_registration_flows_nid_idx\" ON \"_selfservice_registration_flows_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000013_flow_internal_context.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000013_flow_internal_context.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"_internal_context_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000013_flow_internal_context.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000013_flow_internal_context.sqlite3.up.sql",
    "content": "INSERT INTO \"_selfservice_registration_flows_tmp\" (id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui, nid, internal_context) SELECT id, request_url, issued_at, expires_at, active_method, csrf_token, created_at, updated_at, type, ui, nid, internal_context FROM \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000014_flow_internal_context.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000014_flow_internal_context.sqlite3.up.sql",
    "content": "DROP TABLE \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000015_flow_internal_context.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210816142650000015_flow_internal_context.sqlite3.up.sql",
    "content": "ALTER TABLE \"_selfservice_registration_flows_tmp\" RENAME TO \"selfservice_registration_flows\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000000_unique_credentials.cockroach.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_uq_idx\" ON \"identity_credential_identifiers\" (nid, identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000000_unique_credentials.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_identifier_nid_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000000_unique_credentials.mysql.down.sql",
    "content": "CREATE UNIQUE INDEX `identity_credential_identifiers_identifier_nid_uq_idx` ON `identity_credential_identifiers` (`nid`, `identifier`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000000_unique_credentials.mysql.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers DROP FOREIGN KEY identity_credential_identifiers_nid_fk_idx;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000000_unique_credentials.postgres.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_uq_idx\" ON \"identity_credential_identifiers\" (nid, identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000000_unique_credentials.postgres.up.sql",
    "content": "DROP INDEX \"identity_credential_identifiers_identifier_nid_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000000_unique_credentials.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_uq_idx\" ON \"identity_credential_identifiers\" (nid, identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000000_unique_credentials.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_identifier_nid_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000001_unique_credentials.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP COLUMN \"identity_credential_type_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000001_unique_credentials.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD COLUMN \"identity_credential_type_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000001_unique_credentials.mysql.down.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` DROP COLUMN `identity_credential_type_id`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000001_unique_credentials.mysql.up.sql",
    "content": "DROP INDEX `identity_credential_identifiers_identifier_nid_uq_idx` ON `identity_credential_identifiers`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000001_unique_credentials.postgres.down.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP COLUMN \"identity_credential_type_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000001_unique_credentials.postgres.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD COLUMN \"identity_credential_type_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000001_unique_credentials.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_credential_identifiers_tmp\" RENAME TO \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000001_unique_credentials.sqlite3.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD COLUMN \"identity_credential_type_id\" char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000002_unique_credentials.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_identifier_nid_type_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000002_unique_credentials.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD CONSTRAINT \"identity_credential_identifiers_type_id_fk_idx\" FOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000002_unique_credentials.mysql.down.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` ADD CONSTRAINT `identity_credential_identifiers_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000002_unique_credentials.mysql.up.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` ADD CONSTRAINT `identity_credential_identifiers_nid_fk_idx` FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000002_unique_credentials.postgres.down.sql",
    "content": "DROP INDEX \"identity_credential_identifiers_identifier_nid_type_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000002_unique_credentials.postgres.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD CONSTRAINT \"identity_credential_identifiers_type_id_fk_idx\" FOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000002_unique_credentials.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000002_unique_credentials.sqlite3.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers DROP COLUMN identity_credential_type_id;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000003_unique_credentials.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000003_unique_credentials.cockroach.up.sql",
    "content": "UPDATE identity_credential_identifiers SET identity_credential_type_id = (SELECT  ict.id FROM identity_credential_types as ict JOIN identity_credentials AS ic ON (ic.identity_credential_type_id = ict.id) WHERE ic.id = identity_credential_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000003_unique_credentials.mysql.down.sql",
    "content": "DROP INDEX `identity_credential_identifiers_identifier_nid_type_uq_idx` ON `identity_credential_identifiers`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000003_unique_credentials.mysql.up.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` ADD COLUMN `identity_credential_type_id` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000003_unique_credentials.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000003_unique_credentials.postgres.up.sql",
    "content": "UPDATE identity_credential_identifiers SET identity_credential_type_id = (SELECT  ict.id FROM identity_credential_types as ict JOIN identity_credentials AS ic ON (ic.identity_credential_type_id = ict.id) WHERE ic.id = identity_credential_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000003_unique_credentials.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_credential_identifiers_tmp\" (id, identifier, identity_credential_id, created_at, updated_at, nid) SELECT id, identifier, identity_credential_id, created_at, updated_at, nid FROM \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000003_unique_credentials.sqlite3.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers ADD COLUMN identity_credential_type_id CHAR(36) NULL REFERENCES identity_credential_types(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000004_unique_credentials.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000004_unique_credentials.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP CONSTRAINT \"identity_credential_identifiers_type_id_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000004_unique_credentials.mysql.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers DROP FOREIGN KEY identity_credential_identifiers_type_id_fk_idx;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000004_unique_credentials.mysql.up.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` ADD CONSTRAINT `identity_credential_identifiers_type_id_fk_idx` FOREIGN KEY (`identity_credential_type_id`) REFERENCES `identity_credential_types` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000004_unique_credentials.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000004_unique_credentials.postgres.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ALTER COLUMN \"identity_credential_type_id\" TYPE UUID, ALTER COLUMN \"identity_credential_type_id\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000004_unique_credentials.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_credential_identifiers_nid_idx\" ON \"_identity_credential_identifiers_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000004_unique_credentials.sqlite3.up.sql",
    "content": "UPDATE identity_credential_identifiers SET identity_credential_type_id = (SELECT  ict.id FROM identity_credential_types as ict JOIN identity_credentials AS ic ON (ic.identity_credential_type_id = ict.id) WHERE ic.id = identity_credential_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000005_unique_credentials.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000005_unique_credentials.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" RENAME COLUMN \"identity_credential_type_id\" TO \"_identity_credential_type_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000005_unique_credentials.mysql.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers DROP FOREIGN KEY identity_credential_identifiers_nid_fk_idx;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000005_unique_credentials.mysql.up.sql",
    "content": "UPDATE identity_credential_identifiers SET identity_credential_type_id = (SELECT  ict.id FROM identity_credential_types as ict JOIN identity_credentials AS ic ON (ic.identity_credential_type_id = ict.id) WHERE ic.id = identity_credential_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000005_unique_credentials.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000005_unique_credentials.postgres.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_type_uq_idx\" ON \"identity_credential_identifiers\" (nid, identity_credential_type_id, identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000005_unique_credentials.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_credential_identifiers_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000005_unique_credentials.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000006_unique_credentials.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000006_unique_credentials.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD COLUMN \"identity_credential_type_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000006_unique_credentials.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000006_unique_credentials.mysql.up.sql",
    "content": "ALTER TABLE `identity_credential_identifiers` MODIFY `identity_credential_type_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000006_unique_credentials.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000006_unique_credentials.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_credential_identifiers_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\n\"identity_credential_type_id\" char(36) NOT NULL,\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000007_unique_credentials.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000007_unique_credentials.cockroach.up.sql",
    "content": "UPDATE \"identity_credential_identifiers\" SET \"identity_credential_type_id\" = \"_identity_credential_type_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000007_unique_credentials.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000007_unique_credentials.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX `identity_credential_identifiers_identifier_nid_type_uq_idx` ON `identity_credential_identifiers` (`nid`, `identity_credential_type_id`, `identifier`);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000007_unique_credentials.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_identifier_nid_type_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000007_unique_credentials.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_credential_identifiers_nid_idx\" ON \"_identity_credential_identifiers_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000008_unique_credentials.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000008_unique_credentials.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ALTER COLUMN \"identity_credential_type_id\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000008_unique_credentials.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000008_unique_credentials.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_credential_identifiers_tmp\" (id, identifier, identity_credential_id, created_at, updated_at, nid, identity_credential_type_id) SELECT id, identifier, identity_credential_id, created_at, updated_at, nid, identity_credential_type_id FROM \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000009_unique_credentials.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000009_unique_credentials.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" DROP COLUMN \"_identity_credential_type_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000009_unique_credentials.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000009_unique_credentials.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000010_unique_credentials.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000010_unique_credentials.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_credential_identifiers\" ADD CONSTRAINT \"identity_credential_identifiers_type_id_fk_idx\" FOREIGN KEY (\"identity_credential_type_id\") REFERENCES \"identity_credential_types\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000010_unique_credentials.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000010_unique_credentials.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_credential_identifiers_tmp\" RENAME TO \"identity_credential_identifiers\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000011_unique_credentials.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000011_unique_credentials.cockroach.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_type_uq_idx\" ON \"identity_credential_identifiers\" (nid, identity_credential_type_id, identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000011_unique_credentials.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210817181232000011_unique_credentials.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_type_uq_idx\" ON \"identity_credential_identifiers\" (nid, identity_credential_type_id, identifier);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210829131458000000_session_aal_legacy.cockroach.down.sql",
    "content": "UPDATE sessions SET authentication_methods='[]' WHERE authentication_methods='[{\"method\":\"v0.6_legacy_session\"}]' AND aal='aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210829131458000000_session_aal_legacy.cockroach.up.sql",
    "content": "UPDATE sessions SET authentication_methods='[{\"method\":\"v0.6_legacy_session\"}]' WHERE authentication_methods='[]' AND aal='aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210829131458000000_session_aal_legacy.mysql.down.sql",
    "content": "UPDATE sessions SET authentication_methods='[]' WHERE authentication_methods='[{\"method\":\"v0.6_legacy_session\"}]' AND aal='aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210829131458000000_session_aal_legacy.mysql.up.sql",
    "content": "UPDATE sessions SET authentication_methods='[{\"method\":\"v0.6_legacy_session\"}]' WHERE JSON_LENGTH(authentication_methods)=0 AND aal='aal1';\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20210829131458000000_session_aal_legacy.postgres.down.sql",
    "content": "UPDATE sessions SET authentication_methods='[]' WHERE authentication_methods='[{\"method\":\"v0.6_legacy_session\"}]' AND aal='aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210829131458000000_session_aal_legacy.postgres.up.sql",
    "content": "UPDATE sessions SET authentication_methods='[{\"method\":\"v0.6_legacy_session\"}]' WHERE authentication_methods='[]' AND aal='aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210829131458000000_session_aal_legacy.sqlite3.down.sql",
    "content": "UPDATE sessions SET authentication_methods='[]' WHERE authentication_methods='[{\"method\":\"v0.6_legacy_session\"}]' AND aal='aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210829131458000000_session_aal_legacy.sqlite3.up.sql",
    "content": "UPDATE sessions SET authentication_methods='[{\"method\":\"v0.6_legacy_session\"}]' WHERE authentication_methods='[]' AND aal='aal1';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"identity_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_identity_recovery_addresses_id_fk\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` DROP COLUMN `identity_id`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` MODIFY `identity_recovery_address_id` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"identity_id\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"identity_recovery_address_id\" TYPE UUID, ALTER COLUMN \"identity_recovery_address_id\" DROP NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_tokens_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_identity_id_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"identity_recovery_address_id\" TO \"_identity_recovery_address_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` DROP FOREIGN KEY `identity_recovery_tokens_identity_id_fk_idx`;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD COLUMN `identity_id` char(36);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_identity_id_fk_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"identity_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.sqlite3.down.sql",
    "content": "\nDROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_identity_recovery_addresses_id_fk\" FOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"identity_recovery_address_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` MODIFY `identity_recovery_address_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.mysql.up.sql",
    "content": "UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = '' OR identity_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.postgres.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"identity_recovery_address_id\" TYPE UUID, ALTER COLUMN \"identity_recovery_address_id\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.postgres.up.sql",
    "content": "UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = '00000000-0000-0000-0000-000000000000' OR identity_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid FROM \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.sqlite3.up.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_identity_recovery_address_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.cockroach.up.sql",
    "content": "UPDATE \"identity_recovery_tokens\" SET \"identity_recovery_address_id\" = \"_identity_recovery_address_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.mysql.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` MODIFY `identity_id` char(36) NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.postgres.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"identity_id\" TYPE UUID, ALTER COLUMN \"identity_id\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_tokens_nid_idx\" ON \"_identity_recovery_tokens_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.sqlite3.up.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36),\n\"selfservice_recovery_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"nid\" char(36),\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"identity_recovery_address_id\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_identity_recovery_address_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.mysql.up.sql",
    "content": "ALTER TABLE `identity_recovery_tokens` ADD CONSTRAINT `identity_recovery_tokens_identity_id_fk_idx` FOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.postgres.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_identity_id_fk_idx\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_tokens_nid_idx\" ON \"_identity_recovery_tokens_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.cockroach.down.sql",
    "content": "UPDATE \"identity_recovery_tokens\" SET \"identity_recovery_address_id\" = \"_identity_recovery_address_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_identity_recovery_addresses_id_fk\" FOREIGN KEY (\"identity_recovery_address_id\") REFERENCES \"identity_recovery_addresses\" (\"id\") ON UPDATE NO ACTION ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"identity_recovery_address_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"identity_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"nid\" char(36),\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.sqlite3.up.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"identity_recovery_address_id\" TO \"_identity_recovery_address_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.cockroach.up.sql",
    "content": "UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = '00000000-0000-0000-0000-000000000000' OR identity_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_tokens_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.sqlite3.up.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid FROM \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.cockroach.down.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP CONSTRAINT \"identity_recovery_tokens_identity_recovery_addresses_id_fk\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" RENAME COLUMN \"identity_id\" TO \"_identity_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.sqlite3.up.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.cockroach.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD COLUMN \"identity_id\" UUID;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.sqlite3.up.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.cockroach.up.sql",
    "content": "UPDATE \"identity_recovery_tokens\" SET \"identity_id\" = \"_identity_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.sqlite3.down.sql",
    "content": "ALTER TABLE \"_identity_recovery_tokens_tmp\" RENAME TO \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.sqlite3.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens ADD COLUMN identity_id CHAR(36) NULL REFERENCES identities(id) ON DELETE CASCADE ON UPDATE RESTRICT;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ALTER COLUMN \"identity_id\" SET NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.sqlite3.down.sql",
    "content": "DROP TABLE \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.sqlite3.up.sql",
    "content": "UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = '' OR identity_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" DROP COLUMN \"_identity_id_tmp\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.sqlite3.down.sql",
    "content": "INSERT INTO \"_identity_recovery_tokens_tmp\" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid, identity_id) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid, identity_id FROM \"identity_recovery_tokens\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.sqlite3.up.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL AND identity_id = '';"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.cockroach.up.sql",
    "content": "ALTER TABLE \"identity_recovery_tokens\" ADD CONSTRAINT \"identity_recovery_tokens_identity_id_fk_idx\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_tokens_nid_idx\" ON \"_identity_recovery_tokens_tmp\" (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000014_identity_recovery_tokens.sqlite3.down.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_code_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000014_identity_recovery_tokens.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000015_identity_recovery_tokens.sqlite3.down.sql",
    "content": "CREATE UNIQUE INDEX \"identity_recovery_addresses_code_uq_idx\" ON \"_identity_recovery_tokens_tmp\" (token);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000015_identity_recovery_tokens.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000016_identity_recovery_tokens.sqlite3.down.sql",
    "content": "CREATE TABLE \"_identity_recovery_tokens_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"identity_recovery_address_id\" char(36) NOT NULL,\n\"selfservice_recovery_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"expires_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"issued_at\" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00',\n\"nid\" char(36),\n\"identity_id\" CHAR(36),\nFOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE\n);"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000016_identity_recovery_tokens.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000017_identity_recovery_tokens.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_tokens_nid_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000017_identity_recovery_tokens.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000018_identity_recovery_tokens.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000018_identity_recovery_tokens.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000019_identity_recovery_tokens.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_code_uq_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000019_identity_recovery_tokens.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000020_identity_recovery_tokens.sqlite3.down.sql",
    "content": "DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20210913095309000020_identity_recovery_tokens.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000000_identity_fk_indexes.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_nid_identity_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000000_identity_fk_indexes.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_credentials_nid_identity_id_idx\" ON \"identity_credentials\" (identity_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000000_identity_fk_indexes.postgres.down.sql",
    "content": "DROP INDEX \"identity_verifiable_addresses_nid_identity_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000000_identity_fk_indexes.postgres.up.sql",
    "content": "CREATE INDEX \"identity_credentials_nid_identity_id_idx\" ON \"identity_credentials\" (identity_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000000_identity_fk_indexes.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_verifiable_addresses_nid_identity_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000000_identity_fk_indexes.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_credentials_nid_identity_id_idx\" ON \"identity_credentials\" (identity_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000001_identity_fk_indexes.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_nid_identity_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000001_identity_fk_indexes.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_credential_identifiers_nid_identity_credential_id_idx\" ON \"identity_credential_identifiers\" (identity_credential_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000001_identity_fk_indexes.postgres.down.sql",
    "content": "DROP INDEX \"identity_recovery_addresses_nid_identity_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000001_identity_fk_indexes.postgres.up.sql",
    "content": "CREATE INDEX \"identity_credential_identifiers_nid_identity_credential_id_idx\" ON \"identity_credential_identifiers\" (identity_credential_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000001_identity_fk_indexes.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_addresses_nid_identity_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000001_identity_fk_indexes.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_credential_identifiers_nid_identity_credential_id_idx\" ON \"identity_credential_identifiers\" (identity_credential_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000002_identity_fk_indexes.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_nid_identity_credential_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000002_identity_fk_indexes.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_nid_identity_id_idx\" ON \"identity_recovery_addresses\" (identity_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000002_identity_fk_indexes.postgres.down.sql",
    "content": "DROP INDEX \"identity_credential_identifiers_nid_identity_credential_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000002_identity_fk_indexes.postgres.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_nid_identity_id_idx\" ON \"identity_recovery_addresses\" (identity_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000002_identity_fk_indexes.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credential_identifiers_nid_identity_credential_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000002_identity_fk_indexes.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_recovery_addresses_nid_identity_id_idx\" ON \"identity_recovery_addresses\" (identity_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000003_identity_fk_indexes.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credentials_nid_identity_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000003_identity_fk_indexes.cockroach.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_nid_identity_id_idx\" ON \"identity_verifiable_addresses\" (identity_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000003_identity_fk_indexes.postgres.down.sql",
    "content": "DROP INDEX \"identity_credentials_nid_identity_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000003_identity_fk_indexes.postgres.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_nid_identity_id_idx\" ON \"identity_verifiable_addresses\" (identity_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000003_identity_fk_indexes.sqlite3.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_credentials_nid_identity_id_idx\";"
  },
  {
    "path": "persistence/sql/migrations/sql/20220118104539000003_identity_fk_indexes.sqlite3.up.sql",
    "content": "CREATE INDEX \"identity_verifiable_addresses_nid_identity_id_idx\" ON \"identity_verifiable_addresses\" (identity_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000000_identity_credentials_version.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000000_identity_credentials_version.cockroach.up.sql",
    "content": "ALTER TABLE identity_credentials ADD version INT NOT NULL DEFAULT '0';"
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000000_identity_credentials_version.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000000_identity_credentials_version.mysql.up.sql",
    "content": "ALTER TABLE identity_credentials ADD version INT NOT NULL DEFAULT '0';"
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000000_identity_credentials_version.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000000_identity_credentials_version.postgres.up.sql",
    "content": "ALTER TABLE identity_credentials ADD version INT NOT NULL DEFAULT '0';"
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000000_identity_credentials_version.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000000_identity_credentials_version.sqlite3.up.sql",
    "content": "ALTER TABLE identity_credentials ADD version INT NOT NULL DEFAULT '0';"
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000001_identity_credentials_version.cockroach.down.sql",
    "content": "ALTER TABLE identity_credentials DROP COLUMN version;"
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000001_identity_credentials_version.cockroach.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000001_identity_credentials_version.mysql.down.sql",
    "content": "ALTER TABLE identity_credentials DROP COLUMN version;"
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000001_identity_credentials_version.mysql.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000001_identity_credentials_version.postgres.down.sql",
    "content": "ALTER TABLE identity_credentials DROP COLUMN version;"
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000001_identity_credentials_version.postgres.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000001_identity_credentials_version.sqlite3.down.sql",
    "content": "ALTER TABLE identity_credentials DROP COLUMN version;"
  },
  {
    "path": "persistence/sql/migrations/sql/20220301102701000001_identity_credentials_version.sqlite3.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220420102701000000_identity_metadata.down.sql",
    "content": "ALTER TABLE identities DROP COLUMN metadata_public;\nALTER TABLE identities DROP COLUMN metadata_admin;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220420102701000000_identity_metadata.mysql.up.sql",
    "content": "ALTER TABLE identities ADD metadata_public JSON NULL;\nALTER TABLE identities ADD metadata_admin JSON NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220420102701000000_identity_metadata.up.sql",
    "content": "ALTER TABLE identities ADD metadata_public jsonb NULL;\nALTER TABLE identities ADD metadata_admin jsonb NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220512102703000000_missing_indices.down.sql",
    "content": "CREATE INDEX sessions_nid_idx ON sessions (id, nid);\nCREATE INDEX sessions_token_idx ON sessions (token);\nCREATE INDEX sessions_logout_token_idx ON sessions (logout_token);\n\nCREATE INDEX identities_nid_idx ON identities (id, nid);\n\nCREATE INDEX continuity_containers_nid_idx ON continuity_containers (id, nid);\n\nCREATE INDEX courier_messages_nid_idx ON courier_messages (id, nid);\n\nCREATE INDEX identity_credential_identifiers_nid_idx ON identity_credential_identifiers (id, nid);\n\nCREATE INDEX identity_credentials_nid_idx ON identity_credentials (id, nid);\n\nCREATE INDEX identity_recovery_addresses_nid_idx ON identity_recovery_addresses (id, nid);\n\nCREATE INDEX identity_recovery_tokens_nid_idx ON identity_recovery_tokens (id, nid);\nCREATE INDEX identity_recovery_addresses_code_idx ON identity_recovery_tokens (token);\n\nCREATE INDEX identity_verifiable_addresses_nid_idx ON identity_verifiable_addresses (id, nid);\n\nCREATE INDEX identity_verification_tokens_nid_idx ON identity_verification_tokens (id, nid);\nCREATE INDEX identity_verification_tokens_token_idx ON identity_verification_tokens (token);\n\nCREATE INDEX selfservice_login_flows_nid_idx ON selfservice_login_flows (id,nid);\nCREATE INDEX selfservice_recovery_flows_nid_idx ON selfservice_recovery_flows (id,nid);\nCREATE INDEX selfservice_registration_flows_nid_idx ON selfservice_registration_flows (id,nid);\nCREATE INDEX selfservice_settings_flows_nid_idx ON selfservice_settings_flows (id,nid);\nCREATE INDEX selfservice_verification_flows_nid_idx ON selfservice_verification_flows (id,nid);\n\nDROP INDEX sessions_identity_id_nid_idx;\nDROP INDEX sessions_nid_id_identity_id_idx;\nDROP INDEX sessions_id_nid_idx;\nDROP INDEX sessions_token_nid_idx;\n\nDROP INDEX identities_id_nid_idx;\nDROP INDEX identities_nid_id_idx;\nDROP INDEX continuity_containers_nid_id_idx;\nDROP INDEX continuity_containers_id_nid_idx;\nDROP INDEX courier_messages_nid_id_idx;\nDROP INDEX courier_messages_id_nid_idx;\nDROP INDEX identity_credential_identifiers_nid_id_idx;\nDROP INDEX identity_credential_identifiers_id_nid_idx;\nDROP INDEX identity_credentials_nid_id_idx;\nDROP INDEX identity_credentials_id_nid_idx;\nDROP INDEX identity_recovery_addresses_nid_id_idx;\nDROP INDEX identity_recovery_addresses_id_nid_idx;\nDROP INDEX identity_recovery_tokens_nid_id_idx;\nDROP INDEX identity_recovery_tokens_id_nid_idx;\nDROP INDEX identity_recovery_tokens_selfservice_recovery_flow_id_idx;\nDROP INDEX identity_recovery_tokens_identity_recovery_address_id_idx;\nDROP INDEX identity_verification_tokens_nid_id_idx;\nDROP INDEX identity_verification_tokens_id_nid_idx;\nDROP INDEX identity_verification_tokens_token_nid_used_idx;\nDROP INDEX selfservice_login_flows_nid_id_idx;\nDROP INDEX selfservice_login_flows_id_nid_idx;\nDROP INDEX selfservice_recovery_flows_nid_id_idx;\nDROP INDEX selfservice_recovery_flows_id_nid_idx;\nDROP INDEX selfservice_registration_flows_nid_id_idx;\nDROP INDEX selfservice_registration_flows_id_nid_idx;\nDROP INDEX selfservice_settings_flows_nid_id_idx;\nDROP INDEX selfservice_settings_flows_id_nid_idx;\nDROP INDEX selfservice_verification_flows_nid_id_idx;\nDROP INDEX selfservice_verification_flows_id_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220512102703000000_missing_indices.mysql.down.sql",
    "content": "-- This file has a couple more indexes added which MySQL needs for its FK constraints. Other\n-- databases generate those indices automatically.\nCREATE INDEX sessions_nid_idx ON sessions (id, nid);\nCREATE INDEX sessions_nid_mysqlfk_idx ON sessions (nid);\n\nCREATE INDEX sessions_token_idx ON sessions (token);\nCREATE INDEX sessions_mysql_identity_id_idx ON sessions (identity_id);\nCREATE INDEX sessions_logout_token_idx ON sessions (logout_token);\n\nCREATE INDEX identities_nid_idx ON identities (id, nid);\nCREATE INDEX identities_nid_mysqlfk_idx ON identities (nid);\n\nCREATE INDEX continuity_containers_nid_idx ON continuity_containers (id, nid);\nCREATE INDEX continuity_containers_mysqlfk_idx ON continuity_containers (nid);\n\nCREATE INDEX courier_messages_nid_idx ON courier_messages (id, nid);\nCREATE INDEX courier_messages_mysqlfk_idx ON courier_messages (nid);\n\nCREATE INDEX identity_credential_identifiers_nid_idx ON identity_credential_identifiers (id, nid);\nCREATE INDEX identity_credential_identifiers_mysqlfk_idx ON identity_credential_identifiers (nid);\n\nCREATE INDEX identity_credentials_nid_idx ON identity_credentials (id, nid);\nCREATE INDEX identity_credentials_mysqlfk_idx ON identity_credentials (nid);\n\nCREATE INDEX identity_recovery_addresses_nid_idx ON identity_recovery_addresses (id, nid);\nCREATE INDEX identity_recovery_addresses_nid_mysqlfk_idx ON identity_recovery_addresses (nid);\n\nCREATE INDEX identity_recovery_tokens_nid_idx ON identity_recovery_tokens (id, nid);\nCREATE INDEX identity_recovery_tokens_nid_mysqlfk_idx ON identity_recovery_tokens (nid);\nCREATE INDEX identity_recovery_addresses_code_idx ON identity_recovery_tokens (token);\nCREATE INDEX identity_recovery_tokens_srf_id_mysqlfk_idx ON identity_recovery_tokens (selfservice_recovery_flow_id);\nCREATE INDEX identity_recovery_tokens_ira_id_mysqlfk_idx ON identity_recovery_tokens (identity_recovery_address_id);\n\nCREATE INDEX identity_verifiable_addresses_nid_idx ON identity_verifiable_addresses (id, nid);\nCREATE INDEX identity_verifiable_addresses_nid_mysqlfk_idx ON identity_verifiable_addresses (nid);\n\nCREATE INDEX identity_verification_tokens_nid_idx ON identity_verification_tokens (id, nid);\nCREATE INDEX identity_verification_tokens_nid_mysqlfk_idx ON identity_verification_tokens (nid);\nCREATE INDEX identity_verification_tokens_token_idx ON identity_verification_tokens (token);\n\nCREATE INDEX selfservice_login_flows_nid_idx ON selfservice_login_flows (id, nid);\nCREATE INDEX selfservice_login_flows_nid_mysqlfk_idx ON selfservice_login_flows (nid);\n\nCREATE INDEX selfservice_recovery_flows_nid_idx ON selfservice_recovery_flows (id, nid);\nCREATE INDEX selfservice_recovery_flows_nid_mysqlfk_idx ON selfservice_recovery_flows (nid);\n\nCREATE INDEX selfservice_registration_flows_nid_idx ON selfservice_registration_flows (id, nid);\nCREATE INDEX selfservice_registration_flows_nid_mysqlfk_idx ON selfservice_registration_flows (nid);\n\nCREATE INDEX selfservice_settings_flows_nid_idx ON selfservice_settings_flows (id, nid);\nCREATE INDEX selfservice_settings_flows_nid_mysqlfk_idx ON selfservice_settings_flows (nid);\n\nCREATE INDEX selfservice_verification_flows_nid_idx ON selfservice_verification_flows (id, nid);\nCREATE INDEX selfservice_verification_flows_nid_mysqlfk_idx ON selfservice_verification_flows (nid);\n\n\nDROP INDEX sessions_nid_id_identity_id_idx ON sessions;\nDROP INDEX sessions_id_nid_idx ON sessions;\nDROP INDEX sessions_token_nid_idx ON sessions;\n\nDROP INDEX sessions_identity_id_nid_idx ON sessions;\nDROP INDEX identities_id_nid_idx ON identities;\nDROP INDEX identities_nid_id_idx ON identities;\nDROP INDEX continuity_containers_nid_id_idx ON continuity_containers;\nDROP INDEX continuity_containers_id_nid_idx ON continuity_containers;\nDROP INDEX courier_messages_nid_id_idx ON courier_messages;\nDROP INDEX courier_messages_id_nid_idx ON courier_messages;\nDROP INDEX identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers;\nDROP INDEX identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers;\nDROP INDEX identity_credentials_nid_id_idx ON identity_credentials;\nDROP INDEX identity_credentials_id_nid_idx ON identity_credentials;\nDROP INDEX identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses;\nDROP INDEX identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses;\nDROP INDEX identity_recovery_tokens_nid_id_idx ON identity_recovery_tokens;\nDROP INDEX identity_recovery_tokens_id_nid_idx ON identity_recovery_tokens;\nDROP INDEX identity_recovery_tokens_selfservice_recovery_flow_id_idx ON identity_recovery_tokens;\nDROP INDEX identity_recovery_tokens_identity_recovery_address_id_idx ON identity_recovery_tokens;\nDROP INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens;\nDROP INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens;\nDROP INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens;\nDROP INDEX selfservice_login_flows_nid_id_idx ON selfservice_login_flows;\nDROP INDEX selfservice_login_flows_id_nid_idx ON selfservice_login_flows;\nDROP INDEX selfservice_recovery_flows_nid_id_idx ON selfservice_recovery_flows;\nDROP INDEX selfservice_recovery_flows_id_nid_idx ON selfservice_recovery_flows;\nDROP INDEX selfservice_registration_flows_nid_id_idx ON selfservice_registration_flows;\nDROP INDEX selfservice_registration_flows_id_nid_idx ON selfservice_registration_flows;\nDROP INDEX selfservice_settings_flows_nid_id_idx ON selfservice_settings_flows;\nDROP INDEX selfservice_settings_flows_id_nid_idx ON selfservice_settings_flows;\nDROP INDEX selfservice_verification_flows_nid_id_idx ON selfservice_verification_flows;\nDROP INDEX selfservice_verification_flows_id_nid_idx ON selfservice_verification_flows;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220512102703000000_missing_indices.mysql.up.sql",
    "content": "CREATE INDEX sessions_identity_id_nid_idx ON sessions (identity_id, nid);\n\nCREATE INDEX identities_id_nid_idx ON identities (id, nid);\nCREATE INDEX identities_nid_id_idx ON identities (nid, id);\nDROP INDEX identities_nid_idx ON identities;\n\nCREATE INDEX continuity_containers_nid_id_idx ON continuity_containers (nid, id);\nCREATE INDEX continuity_containers_id_nid_idx ON continuity_containers (id, nid);\nDROP INDEX continuity_containers_nid_idx ON continuity_containers;\n\nCREATE INDEX courier_messages_nid_id_idx ON courier_messages (nid, id);\nCREATE INDEX courier_messages_id_nid_idx ON courier_messages (id, nid);\nDROP INDEX courier_messages_nid_idx ON courier_messages;\n\nCREATE INDEX identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid, id);\nCREATE INDEX identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id, nid);\nDROP INDEX identity_credential_identifiers_nid_idx ON identity_credential_identifiers;\n\nCREATE INDEX identity_credentials_nid_id_idx ON identity_credentials (nid, id);\nCREATE INDEX identity_credentials_id_nid_idx ON identity_credentials (id, nid);\nDROP INDEX identity_credentials_nid_idx ON identity_credentials;\n\nCREATE INDEX identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses (nid, id);\nCREATE INDEX identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses (id, nid);\nDROP INDEX identity_recovery_addresses_nid_idx ON identity_recovery_addresses;\n\nCREATE INDEX identity_recovery_tokens_nid_id_idx ON identity_recovery_tokens (nid, id);\nCREATE INDEX identity_recovery_tokens_id_nid_idx ON identity_recovery_tokens (id, nid);\nCREATE INDEX identity_recovery_tokens_selfservice_recovery_flow_id_idx ON identity_recovery_tokens (selfservice_recovery_flow_id);\nCREATE INDEX identity_recovery_tokens_identity_recovery_address_id_idx ON identity_recovery_tokens (identity_recovery_address_id);\nCREATE INDEX identity_recovery_tokens_token_nid_used_idx ON identity_recovery_tokens (nid, token, used);\nDROP INDEX identity_recovery_tokens_nid_idx ON identity_recovery_tokens;\nDROP INDEX identity_recovery_addresses_code_idx ON identity_recovery_tokens;\n\nCREATE INDEX identity_verifiable_addresses_nid_id_idx ON identity_verifiable_addresses (nid, id);\nCREATE INDEX identity_verifiable_addresses_id_nid_idx ON identity_verifiable_addresses (id, nid);\nDROP INDEX identity_verifiable_addresses_nid_idx ON identity_verifiable_addresses;\n\nCREATE INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid, id);\nCREATE INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id, nid);\nCREATE INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens (nid, token, used);\nDROP INDEX identity_verification_tokens_nid_idx ON identity_verification_tokens;\nDROP INDEX identity_verification_tokens_token_idx ON identity_verification_tokens;\n\nCREATE INDEX selfservice_login_flows_nid_id_idx ON selfservice_login_flows (nid, id);\nCREATE INDEX selfservice_login_flows_id_nid_idx ON selfservice_login_flows (id, nid);\nDROP INDEX selfservice_login_flows_nid_idx ON selfservice_login_flows;\n\nCREATE INDEX selfservice_recovery_flows_nid_id_idx ON selfservice_recovery_flows (nid, id);\nCREATE INDEX selfservice_recovery_flows_id_nid_idx ON selfservice_recovery_flows (id, nid);\nDROP INDEX selfservice_recovery_flows_nid_idx ON selfservice_recovery_flows;\n\nCREATE INDEX selfservice_registration_flows_nid_id_idx ON selfservice_registration_flows (nid, id);\nCREATE INDEX selfservice_registration_flows_id_nid_idx ON selfservice_registration_flows (id, nid);\nDROP INDEX selfservice_registration_flows_nid_idx ON selfservice_registration_flows;\n\nCREATE INDEX selfservice_settings_flows_nid_id_idx ON selfservice_settings_flows (nid, id);\nCREATE INDEX selfservice_settings_flows_id_nid_idx ON selfservice_settings_flows (id, nid);\nDROP INDEX selfservice_settings_flows_nid_idx ON selfservice_settings_flows;\n\nCREATE INDEX selfservice_verification_flows_nid_id_idx ON selfservice_verification_flows (nid, id);\nCREATE INDEX selfservice_verification_flows_id_nid_idx ON selfservice_verification_flows (id, nid);\nDROP INDEX selfservice_verification_flows_nid_idx ON selfservice_verification_flows;\n\nCREATE INDEX sessions_nid_id_identity_id_idx ON sessions (nid, identity_id, id);\nCREATE INDEX sessions_id_nid_idx ON sessions (id, nid);\nCREATE INDEX sessions_token_nid_idx ON sessions (nid, token);\nDROP INDEX sessions_nid_idx ON sessions;\nDROP INDEX sessions_token_idx ON sessions;\nDROP INDEX sessions_logout_token_idx ON sessions;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220512102703000000_missing_indices.up.sql",
    "content": "CREATE INDEX sessions_identity_id_nid_idx ON sessions (identity_id, nid);\n\nCREATE INDEX identities_id_nid_idx ON identities (id, nid);\nCREATE INDEX identities_nid_id_idx ON identities (nid, id);\nDROP INDEX identities_nid_idx;\n\nCREATE INDEX continuity_containers_nid_id_idx ON continuity_containers (nid, id);\nCREATE INDEX continuity_containers_id_nid_idx ON continuity_containers (id, nid);\nDROP INDEX continuity_containers_nid_idx;\n\nCREATE INDEX courier_messages_nid_id_idx ON courier_messages (nid, id);\nCREATE INDEX courier_messages_id_nid_idx ON courier_messages (id, nid);\nDROP INDEX courier_messages_nid_idx;\n\nCREATE INDEX identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid, id);\nCREATE INDEX identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id, nid);\nDROP INDEX identity_credential_identifiers_nid_idx;\n\nCREATE INDEX identity_credentials_nid_id_idx ON identity_credentials (nid, id);\nCREATE INDEX identity_credentials_id_nid_idx ON identity_credentials (id, nid);\nDROP INDEX identity_credentials_nid_idx;\n\nCREATE INDEX identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses (nid, id);\nCREATE INDEX identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses (id, nid);\nDROP INDEX identity_recovery_addresses_nid_idx;\n\nCREATE INDEX identity_recovery_tokens_nid_id_idx ON identity_recovery_tokens (nid, id);\nCREATE INDEX identity_recovery_tokens_id_nid_idx ON identity_recovery_tokens (id, nid);\nCREATE INDEX identity_recovery_tokens_selfservice_recovery_flow_id_idx ON identity_recovery_tokens (selfservice_recovery_flow_id);\nCREATE INDEX identity_recovery_tokens_identity_recovery_address_id_idx ON identity_recovery_tokens (identity_recovery_address_id);\nCREATE INDEX identity_recovery_tokens_token_nid_used_idx ON identity_recovery_tokens (nid, token, used);\nDROP INDEX identity_recovery_addresses_code_idx;\nDROP INDEX identity_recovery_tokens_nid_idx;\n\nCREATE INDEX identity_verifiable_addresses_nid_id_idx ON identity_verifiable_addresses (nid, id);\nCREATE INDEX identity_verifiable_addresses_id_nid_idx ON identity_verifiable_addresses (id, nid);\nDROP INDEX identity_verifiable_addresses_nid_idx;\n\nCREATE INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid, id);\nCREATE INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id, nid);\nCREATE INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens (nid, token, used);\nDROP INDEX identity_verification_tokens_nid_idx;\nDROP INDEX identity_verification_tokens_token_idx;\n\nCREATE INDEX selfservice_login_flows_nid_id_idx ON selfservice_login_flows (nid, id);\nCREATE INDEX selfservice_login_flows_id_nid_idx ON selfservice_login_flows (id, nid);\nDROP INDEX selfservice_login_flows_nid_idx;\n\nCREATE INDEX selfservice_recovery_flows_nid_id_idx ON selfservice_recovery_flows (nid, id);\nCREATE INDEX selfservice_recovery_flows_id_nid_idx ON selfservice_recovery_flows (id, nid);\nDROP INDEX selfservice_recovery_flows_nid_idx;\n\nCREATE INDEX selfservice_registration_flows_nid_id_idx ON selfservice_registration_flows (nid, id);\nCREATE INDEX selfservice_registration_flows_id_nid_idx ON selfservice_registration_flows (id, nid);\nDROP INDEX selfservice_registration_flows_nid_idx;\n\nCREATE INDEX selfservice_settings_flows_nid_id_idx ON selfservice_settings_flows (nid, id);\nCREATE INDEX selfservice_settings_flows_id_nid_idx ON selfservice_settings_flows (id, nid);\nDROP INDEX selfservice_settings_flows_nid_idx;\n\nCREATE INDEX selfservice_verification_flows_nid_id_idx ON selfservice_verification_flows (nid, id);\nCREATE INDEX selfservice_verification_flows_id_nid_idx ON selfservice_verification_flows (id, nid);\nDROP INDEX selfservice_verification_flows_nid_idx;\n\nCREATE INDEX sessions_nid_id_identity_id_idx ON sessions (nid, identity_id, id);\nCREATE INDEX sessions_id_nid_idx ON sessions (id, nid);\nCREATE INDEX sessions_token_nid_idx ON sessions (nid, token);\n\nDROP INDEX sessions_nid_idx;\nDROP INDEX sessions_token_idx;\nDROP INDEX sessions_logout_token_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220607000001000000_hydra_login_challenge.cockroach.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"oauth2_login_challenge\";\nALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"oauth2_login_challenge\";\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220607000001000000_hydra_login_challenge.cockroach.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"oauth2_login_challenge\" UUID NULL;\nALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"oauth2_login_challenge\" UUID NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220607000001000000_hydra_login_challenge.mysql.down.sql",
    "content": "ALTER TABLE `selfservice_login_flows` DROP COLUMN `oauth2_login_challenge`;\nALTER TABLE `selfservice_registration_flows` DROP COLUMN `oauth2_login_challenge`;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220607000001000000_hydra_login_challenge.mysql.up.sql",
    "content": "ALTER TABLE `selfservice_login_flows` ADD COLUMN `oauth2_login_challenge` CHAR(36) NULL;\nALTER TABLE `selfservice_registration_flows` ADD COLUMN `oauth2_login_challenge` CHAR(36) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220607000001000000_hydra_login_challenge.postgres.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"oauth2_login_challenge\";\nALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"oauth2_login_challenge\";\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220607000001000000_hydra_login_challenge.postgres.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"oauth2_login_challenge\" UUID NULL;\nALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"oauth2_login_challenge\" UUID NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220607000001000000_hydra_login_challenge.sqlite3.down.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"oauth2_login_challenge\";\nALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"oauth2_login_challenge\";\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220607000001000000_hydra_login_challenge.sqlite3.up.sql",
    "content": "ALTER TABLE \"selfservice_login_flows\" ADD COLUMN \"oauth2_login_challenge\" CHAR(36) NULL;\nALTER TABLE \"selfservice_registration_flows\" ADD COLUMN \"oauth2_login_challenge\" CHAR(36) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220610155809000000_identity_address_casing.cockroach.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220610155809000000_identity_address_casing.cockroach.up.sql",
    "content": "UPDATE identity_recovery_addresses SET value = LOWER(value) WHERE TRUE;\nUPDATE identity_verifiable_addresses SET value = LOWER(value) WHERE TRUE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220610155809000000_identity_address_casing.mysql.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220610155809000000_identity_address_casing.mysql.up.sql",
    "content": "UPDATE identity_recovery_addresses SET value = LOWER(value) WHERE TRUE;\nUPDATE identity_verifiable_addresses SET value = LOWER(value) WHERE TRUE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220610155809000000_identity_address_casing.postgres.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220610155809000000_identity_address_casing.postgres.up.sql",
    "content": "UPDATE identity_recovery_addresses SET value = LOWER(value) WHERE TRUE;\nUPDATE identity_verifiable_addresses SET value = LOWER(value) WHERE TRUE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220610155809000000_identity_address_casing.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220610155809000000_identity_address_casing.sqlite3.up.sql",
    "content": "UPDATE identity_recovery_addresses SET value = LOWER(value) WHERE TRUE;\nUPDATE identity_verifiable_addresses SET value = LOWER(value) WHERE TRUE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220802103909000000_courier_send_count.down.sql",
    "content": "ALTER TABLE courier_messages DROP COLUMN send_count;"
  },
  {
    "path": "persistence/sql/migrations/sql/20220802103909000000_courier_send_count.up.sql",
    "content": "ALTER TABLE courier_messages\nADD send_count INT NOT NULL DEFAULT 0;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220824165300000000_add_flow_type_to_identity_recovery_tokens.down.sql",
    "content": "ALTER TABLE identity_recovery_tokens\nDROP token_type;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220824165300000000_add_flow_type_to_identity_recovery_tokens.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens\nADD token_type int NOT NULL DEFAULT 0;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220824165300000001_populate_flow_type_in_recovery_tokens.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220824165300000001_populate_flow_type_in_recovery_tokens.up.sql",
    "content": "UPDATE identity_recovery_tokens\nSET token_type = 1\nWHERE selfservice_recovery_flow_id IS NULL;\n\nUPDATE identity_recovery_tokens\nSET token_type = 2\nWHERE selfservice_recovery_flow_id IS NOT NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220824165300000002_add_flow_type_check_constraint.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220824165300000002_add_flow_type_check_constraint.sqlite3.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220824165300000002_add_flow_type_check_constraint.sqlite3.up.sql",
    "content": "-- SQLITE does not support Check constraints in all cases\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220824165300000002_add_flow_type_check_constraint.up.sql",
    "content": "ALTER TABLE identity_recovery_tokens \nADD CONSTRAINT identity_recovery_tokens_token_type_ck CHECK (token_type = 1 OR token_type = 2);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220825134336000000_delete_verification_token_without_flow_id.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20220825134336000000_delete_verification_token_without_flow_id.up.sql",
    "content": "DELETE FROM identity_verification_tokens\nWHERE selfservice_verification_flow_id IS NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.down.sql",
    "content": "ALTER TABLE identity_verification_tokens\nALTER selfservice_verification_flow_id DROP NOT NULL;\n\nDROP INDEX identity_verification_tokens_token_nid_used_flow_id_idx;\nCREATE INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens (nid, token, used);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.mysql.down.sql",
    "content": "ALTER TABLE identity_verification_tokens\nMODIFY selfservice_verification_flow_id CHAR(36) NULL;\n\nDROP INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens;\nCREATE INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens (nid, token, used);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.mysql.up.sql",
    "content": "ALTER TABLE identity_verification_tokens\nMODIFY selfservice_verification_flow_id CHAR(36) NOT NULL;\n\nDROP INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens;\nCREATE INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid, token, used, selfservice_verification_flow_id);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.sqlite3.down.sql",
    "content": "ALTER TABLE identity_verification_tokens\nRENAME TO identity_verification_tokens_;\n\nCREATE TABLE \"identity_verification_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL,\n\"identity_verifiable_address_id\" char(36) NOT NULL,\n\"selfservice_verification_flow_id\" char(36),\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\n\nDROP INDEX identity_verification_tokens_id_nid_idx;\nDROP INDEX identity_verification_tokens_nid_id_idx;\nDROP INDEX identity_verification_tokens_token_nid_used_flow_id_idx;\nDROP INDEX identity_verification_tokens_token_uq_idx;\nDROP INDEX identity_verification_tokens_verifiable_address_id_idx;\nDROP INDEX identity_verification_tokens_verification_flow_id_idx;\n\nCREATE INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id, nid);\nCREATE INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid, id);\nCREATE INDEX identity_verification_tokens_token_nid_used_idx ON identity_verification_tokens (nid, token, used);\nCREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);\nCREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);\n\nINSERT INTO identity_verification_tokens\nSELECT * FROM identity_verification_tokens_;\n\nDROP TABLE identity_verification_tokens_;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.sqlite3.up.sql",
    "content": "ALTER TABLE identity_verification_tokens\nRENAME TO identity_verification_tokens_;\n\nCREATE TABLE \"identity_verification_tokens\" (\n\"id\" TEXT PRIMARY KEY,\n\"token\" TEXT NOT NULL,\n\"used\" bool NOT NULL DEFAULT 'false',\n\"used_at\" DATETIME,\n\"expires_at\" DATETIME NOT NULL,\n\"issued_at\" DATETIME NOT NULL,\n\"identity_verifiable_address_id\" char(36) NOT NULL,\n\"selfservice_verification_flow_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\nFOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE,\nFOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\n\nDROP INDEX identity_verification_tokens_id_nid_idx;\nDROP INDEX identity_verification_tokens_nid_id_idx;\nDROP INDEX identity_verification_tokens_token_nid_used_idx;\nDROP INDEX identity_verification_tokens_token_uq_idx;\nDROP INDEX identity_verification_tokens_verifiable_address_id_idx;\nDROP INDEX identity_verification_tokens_verification_flow_id_idx;\n\nCREATE INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id, nid);\nCREATE INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid, id);\nCREATE INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid, token, used, selfservice_verification_flow_id);\nCREATE UNIQUE INDEX \"identity_verification_tokens_token_uq_idx\" ON \"identity_verification_tokens\" (token);\nCREATE INDEX \"identity_verification_tokens_verifiable_address_id_idx\" ON \"identity_verification_tokens\" (identity_verifiable_address_id);\nCREATE INDEX \"identity_verification_tokens_verification_flow_id_idx\" ON \"identity_verification_tokens\" (selfservice_verification_flow_id);\n\nINSERT INTO identity_verification_tokens\nSELECT * FROM identity_verification_tokens_;\n\nDROP TABLE identity_verification_tokens_;"
  },
  {
    "path": "persistence/sql/migrations/sql/20220825134336000001_not_null_constraint_verification_token_flow_id.up.sql",
    "content": "ALTER TABLE identity_verification_tokens\nALTER selfservice_verification_flow_id SET NOT NULL;\n\nDROP INDEX identity_verification_tokens_token_nid_used_idx;\nCREATE INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid, token, used, selfservice_verification_flow_id);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220901123209000000_recovery_code.down.sql",
    "content": "DROP TABLE identity_recovery_codes;\n\nALTER TABLE selfservice_recovery_flows DROP submit_count;\n\nALTER TABLE selfservice_recovery_flows DROP skip_csrf_check;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220901123209000000_recovery_code.mysql.down.sql",
    "content": "DROP TABLE identity_recovery_codes;\n\nALTER TABLE selfservice_recovery_flows DROP submit_count;\n\nALTER TABLE selfservice_recovery_flows DROP skip_csrf_check;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220901123209000000_recovery_code.mysql.up.sql",
    "content": "CREATE TABLE identity_recovery_codes\n(\n    id CHAR(36) NOT NULL PRIMARY KEY,\n    code VARCHAR (64) NOT NULL, -- HMACed value of the actual code\n    used_at timestamp NULL DEFAULT NULL,\n    identity_recovery_address_id CHAR(36),\n    code_type int NOT NULL,\n    expires_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    issued_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    selfservice_recovery_flow_id CHAR(36),\n    created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    nid CHAR(36) NOT NULL,\n    identity_id CHAR(36) NOT NULL,\n    CONSTRAINT identity_recovery_codes_identity_recovery_addresses_id_fk \n        FOREIGN KEY (identity_recovery_address_id)\n        REFERENCES identity_recovery_addresses (id)\n        ON DELETE cascade,\n    CONSTRAINT identity_recovery_codes_selfservice_recovery_flows_id_fk \n        FOREIGN KEY (selfservice_recovery_flow_id) \n        REFERENCES selfservice_recovery_flows (id)\n        ON DELETE cascade,\n    CONSTRAINT identity_recovery_tokens_identity_id_fk \n        FOREIGN KEY (identity_id) \n        REFERENCES identities (id)\n        ON UPDATE RESTRICT ON DELETE CASCADE,\n    CONSTRAINT identity_recovery_codes_networks_id_fk\n        FOREIGN KEY (nid)\n        REFERENCES networks (id)\n        ON UPDATE RESTRICT ON DELETE CASCADE\n);\n\nCREATE INDEX identity_recovery_codes_nid_flow_id_idx ON identity_recovery_codes (nid, selfservice_recovery_flow_id);\nCREATE INDEX identity_recovery_codes_id_nid_idx ON identity_recovery_codes (id, nid);\n\nALTER TABLE selfservice_recovery_flows ADD submit_count int NOT NULL DEFAULT 0;\nALTER TABLE selfservice_recovery_flows ADD skip_csrf_check boolean NOT NULL DEFAULT FALSE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220901123209000000_recovery_code.up.sql",
    "content": "CREATE TABLE identity_recovery_codes\n(\n    id UUID NOT NULL PRIMARY KEY,\n    code VARCHAR (64) NOT NULL, -- HMACed value of the actual code\n    used_at timestamp NULL DEFAULT NULL,\n    identity_recovery_address_id UUID,\n    code_type INT NOT NULL,\n    expires_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    issued_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    selfservice_recovery_flow_id UUID NOT NULL,\n    created_at timestamp NOT NULL,\n    updated_at timestamp NOT NULL,\n    nid UUID NOT NULL,\n    identity_id UUID NOT NULL,\n    CONSTRAINT identity_recovery_codes_identity_recovery_addresses_id_fk \n        FOREIGN KEY (identity_recovery_address_id)\n        REFERENCES identity_recovery_addresses (id)\n        ON DELETE cascade,\n    CONSTRAINT identity_recovery_codes_selfservice_recovery_flows_id_fk \n        FOREIGN KEY (selfservice_recovery_flow_id) \n        REFERENCES selfservice_recovery_flows (id)\n        ON DELETE cascade,\n    CONSTRAINT identity_recovery_codes_identity_id_fk \n        FOREIGN KEY (identity_id) \n        REFERENCES identities (id)\n        ON UPDATE RESTRICT ON DELETE CASCADE,\n    CONSTRAINT identity_recovery_codes_networks_id_fk\n        FOREIGN KEY (nid)\n        REFERENCES networks (id)\n        ON UPDATE RESTRICT ON DELETE CASCADE\n);\n\nCREATE INDEX identity_recovery_codes_nid_flow_id_idx ON identity_recovery_codes (nid, selfservice_recovery_flow_id);\nCREATE INDEX identity_recovery_codes_id_nid_idx ON identity_recovery_codes (id, nid);\n\nALTER TABLE selfservice_recovery_flows ADD submit_count int NOT NULL DEFAULT 0;\nALTER TABLE selfservice_recovery_flows ADD skip_csrf_check boolean NOT NULL DEFAULT FALSE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220907132836000000_add_session_devices_table.down.sql",
    "content": "DROP TABLE \"session_devices\";\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220907132836000000_add_session_devices_table.mysql.down.sql",
    "content": "DROP TABLE session_devices;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220907132836000000_add_session_devices_table.mysql.up.sql",
    "content": "CREATE TABLE `session_devices`\n(\n  `id`         char(36) NOT NULL,\n  PRIMARY KEY (`id`),\n  `ip_address` VARCHAR(50)  DEFAULT '',\n  `user_agent` VARCHAR(512) DEFAULT '',\n  `location`   VARCHAR(512) DEFAULT '',\n  `session_id` char(36) NOT NULL,\n  `nid`        char(36) NOT NULL,\n  `created_at` DATETIME NOT NULL,\n  `updated_at` DATETIME NOT NULL,\n  FOREIGN KEY (`session_id`) REFERENCES `sessions` (`id`) ON DELETE cascade,\n  FOREIGN KEY (`nid`) REFERENCES `networks` (`id`) ON DELETE cascade,\n  CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent)\n) ENGINE = InnoDB;\nCREATE INDEX `session_devices_id_nid_idx` ON `session_devices` (`id`, `nid`);\nCREATE INDEX `session_devices_session_id_nid_idx` ON `session_devices` (`session_id`, `nid`);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20220907132836000000_add_session_devices_table.up.sql",
    "content": "CREATE TABLE \"session_devices\"\n(\n  \"id\"         UUID PRIMARY KEY NOT NULL,\n  \"ip_address\" VARCHAR(50)  DEFAULT '',\n  \"user_agent\" VARCHAR(512) DEFAULT '',\n  \"location\"   VARCHAR(512) DEFAULT '',\n  \"nid\"        UUID             NOT NULL,\n  \"session_id\" UUID             NOT NULL,\n  \"created_at\" timestamp        NOT NULL,\n  \"updated_at\" timestamp        NOT NULL,\n  CONSTRAINT \"session_metadata_sessions_id_fk\" FOREIGN KEY (\"session_id\") REFERENCES \"sessions\" (\"id\") ON DELETE cascade,\n  CONSTRAINT \"session_metadata_nid_fk\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON DELETE cascade,\n  CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent)\n);\nCREATE INDEX \"session_devices_id_nid_idx\" ON \"session_devices\" (id, nid);\nCREATE INDEX \"session_devices_session_id_nid_idx\" ON \"session_devices\" (session_id, nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20221024182336000000_verification_code.down.sql",
    "content": "DROP TABLE identity_verification_codes;\n\nALTER TABLE\n  selfservice_verification_flows DROP COLUMN submit_count;"
  },
  {
    "path": "persistence/sql/migrations/sql/20221024182336000000_verification_code.mysql.up.sql",
    "content": "CREATE TABLE identity_verification_codes (\n    id CHAR(36) NOT NULL PRIMARY KEY,\n    code_hmac VARCHAR (64) NOT NULL,\n    -- HMACed value of the actual code\n    used_at timestamp NULL DEFAULT NULL,\n    identity_verifiable_address_id CHAR(36),\n    expires_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    issued_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    selfservice_verification_flow_id CHAR(36) NOT NULL,\n    created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    nid CHAR(36) NOT NULL,\n    CONSTRAINT identity_verification_codes_identity_verifiable_addresses_id_fk FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON DELETE cascade,\n    CONSTRAINT identity_verification_codes_selfservice_verification_flows_id_fk FOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON DELETE cascade,\n    CONSTRAINT identity_verification_codes_networks_id_fk FOREIGN KEY (nid) REFERENCES networks (id) ON UPDATE RESTRICT ON DELETE CASCADE\n);\n\nALTER TABLE\n    selfservice_verification_flows\nADD\n    COLUMN submit_count INT NOT NULL DEFAULT 0;\n\nCREATE INDEX identity_verification_codes_nid_flow_id_idx ON identity_verification_codes (nid, selfservice_verification_flow_id);\n\nCREATE INDEX identity_verification_codes_id_nid_idx ON identity_verification_codes (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20221024182336000000_verification_code.up.sql",
    "content": "CREATE TABLE identity_verification_codes (\n    id UUID NOT NULL PRIMARY KEY,\n    code_hmac VARCHAR (64) NOT NULL,\n    -- HMACed value of the actual code\n    used_at timestamp NULL DEFAULT NULL,\n    identity_verifiable_address_id UUID,\n    expires_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    issued_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    selfservice_verification_flow_id UUID NOT NULL,\n    created_at timestamp NOT NULL,\n    updated_at timestamp NOT NULL,\n    nid UUID NOT NULL,\n    CONSTRAINT identity_verification_codes_identity_verifiable_addresses_id_fk FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses (id) ON DELETE cascade,\n    CONSTRAINT identity_verification_codes_selfservice_verification_flows_id_fk FOREIGN KEY (selfservice_verification_flow_id) REFERENCES selfservice_verification_flows (id) ON DELETE cascade,\n    CONSTRAINT identity_verification_codes_networks_id_fk FOREIGN KEY (nid) REFERENCES networks (id) ON UPDATE RESTRICT ON DELETE CASCADE\n);\n\nALTER TABLE\n    selfservice_verification_flows\nADD\n    COLUMN submit_count INT NOT NULL DEFAULT 0;\n\nCREATE INDEX identity_verification_codes_nid_flow_id_idx ON identity_verification_codes (nid, selfservice_verification_flow_id);\n\nCREATE INDEX identity_verification_codes_id_nid_idx ON identity_verification_codes (id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20221205092803000000_add_courier_send_attempts_table.down.sql",
    "content": "DROP TABLE courier_message_dispatches;"
  },
  {
    "path": "persistence/sql/migrations/sql/20221205092803000000_add_courier_send_attempts_table.mysql.up.sql",
    "content": "CREATE TABLE courier_message_dispatches (\n  id CHAR(36) PRIMARY KEY,\n  message_id CHAR(36) NOT NULL,\n  status VARCHAR(7) NOT NULL,\n  error JSON,\n  nid CHAR(36) NOT NULL,\n  created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  CONSTRAINT courier_message_dispatches_message_id_fk FOREIGN KEY (message_id) REFERENCES courier_messages (id) ON DELETE cascade,\n  CONSTRAINT courier_message_dispatches_nid_fk FOREIGN KEY (nid) REFERENCES networks (id) ON DELETE cascade\n);\n\nCREATE INDEX courier_message_dispatches_id_message_id_nid_idx ON courier_message_dispatches (id, message_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20221205092803000000_add_courier_send_attempts_table.up.sql",
    "content": "CREATE TABLE courier_message_dispatches (\n  id UUID PRIMARY KEY,\n  message_id UUID NOT NULL,\n  status VARCHAR(7) NOT NULL,\n  error JSON,\n  nid UUID NOT NULL,\n  created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  CONSTRAINT courier_message_dispatches_message_id_fk FOREIGN KEY (message_id) REFERENCES courier_messages (id) ON DELETE cascade,\n  CONSTRAINT courier_message_dispatches_nid_fk FOREIGN KEY (nid) REFERENCES networks (id) ON DELETE cascade\n);\n\nCREATE INDEX courier_message_dispatches_id_message_id_nid_idx ON courier_message_dispatches (id, message_id, nid);"
  },
  {
    "path": "persistence/sql/migrations/sql/20221214101328000000_identity_delete_indices.down.sql",
    "content": "DROP INDEX IF EXISTS \"identity_recovery_codes_identity_id_nid_idx\";\n\nDROP INDEX IF EXISTS \"identity_verification_codes_verifiable_address_nid_idx\";\n\nDROP INDEX IF EXISTS \"selfservice_settings_flows_identity_id_nid_idx\";\n\nDROP INDEX IF EXISTS \"continuity_containers_identity_id_nid_idx\";\n\nDROP INDEX IF EXISTS \"selfservice_recovery_flows_recovered_identity_id_nid_idx\";\n\nDROP INDEX IF EXISTS \"identity_recovery_tokens_identity_id_nid_idx\";\n\nDROP INDEX IF EXISTS \"identity_recovery_codes_identity_recovery_address_id_nid_idx\";\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20221214101328000000_identity_delete_indices.mysql.down.sql",
    "content": "-- MySQL requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan.\n-- In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order.\n-- Such an index is created on the referencing table automatically if it does not exist. This index might be silently dropped later\n-- if you create another index that can be used to enforce the foreign key constraint.\n\n-- from https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html\n\n-- -> The indexes in question already existed. We have to create new ones that are just the foreign key to restore the previous state.\n\nALTER TABLE identity_recovery_codes ADD INDEX (identity_id);\n\nDROP INDEX identity_recovery_codes_identity_id_nid_idx ON identity_recovery_codes;\n\nALTER TABLE identity_verification_codes ADD INDEX (identity_verifiable_address_id);\n\nDROP INDEX identity_verification_codes_verifiable_address_nid_idx ON identity_verification_codes;\n\nALTER TABLE selfservice_settings_flows ADD INDEX (identity_id);\n\nDROP INDEX selfservice_settings_flows_identity_id_nid_idx ON selfservice_settings_flows;\n\nALTER TABLE continuity_containers ADD INDEX (identity_id);\n\nDROP INDEX continuity_containers_identity_id_nid_idx ON continuity_containers;\n\nALTER TABLE selfservice_recovery_flows ADD INDEX (recovered_identity_id);\n\nDROP INDEX selfservice_recovery_flows_recovered_identity_id_nid_idx ON selfservice_recovery_flows;\n\nALTER TABLE identity_recovery_tokens ADD INDEX (identity_id);\n\nDROP INDEX identity_recovery_tokens_identity_id_nid_idx ON identity_recovery_tokens;\n\nALTER TABLE identity_recovery_codes ADD INDEX (identity_recovery_address_id);\n\nDROP INDEX identity_recovery_codes_identity_recovery_address_id_nid_idx ON identity_recovery_codes;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20221214101328000000_identity_delete_indices.mysql.up.sql",
    "content": "-- MySQL requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan.\n-- In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order.\n-- Such an index is created on the referencing table automatically if it does not exist. This index might be silently dropped later\n-- if you create another index that can be used to enforce the foreign key constraint.\n\n-- from https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html\n\n-- -> We create new indexes to be consistent with the other databases. However, dropping those will be a bit different.\n\nCREATE INDEX identity_recovery_codes_identity_id_nid_idx ON identity_recovery_codes (identity_id, nid);\n\nCREATE INDEX identity_verification_codes_verifiable_address_nid_idx ON identity_verification_codes (identity_verifiable_address_id, nid);\n\nCREATE INDEX selfservice_settings_flows_identity_id_nid_idx ON selfservice_settings_flows (identity_id, nid);\n\nCREATE INDEX continuity_containers_identity_id_nid_idx ON continuity_containers (identity_id, nid);\n\nCREATE INDEX selfservice_recovery_flows_recovered_identity_id_nid_idx ON selfservice_recovery_flows (recovered_identity_id, nid);\n\nCREATE INDEX identity_recovery_tokens_identity_id_nid_idx ON identity_recovery_tokens (identity_id, nid);\n\nCREATE INDEX identity_recovery_codes_identity_recovery_address_id_nid_idx ON identity_recovery_codes (identity_recovery_address_id, nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20221214101328000000_identity_delete_indices.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS \"identity_recovery_codes_identity_id_nid_idx\" ON \"identity_recovery_codes\" (identity_id, nid);\n\nCREATE INDEX IF NOT EXISTS \"identity_verification_codes_verifiable_address_nid_idx\" ON \"identity_verification_codes\" (identity_verifiable_address_id, nid);\n\nCREATE INDEX IF NOT EXISTS \"selfservice_settings_flows_identity_id_nid_idx\" ON \"selfservice_settings_flows\" (identity_id, nid);\n\nCREATE INDEX IF NOT EXISTS \"continuity_containers_identity_id_nid_idx\" ON \"continuity_containers\" (identity_id, nid);\n\nCREATE INDEX IF NOT EXISTS \"selfservice_recovery_flows_recovered_identity_id_nid_idx\" ON \"selfservice_recovery_flows\" (recovered_identity_id, nid);\n\nCREATE INDEX IF NOT EXISTS \"identity_recovery_tokens_identity_id_nid_idx\" ON \"identity_recovery_tokens\" (identity_id, nid);\n\nCREATE INDEX IF NOT EXISTS \"identity_recovery_codes_identity_recovery_address_id_nid_idx\" ON \"identity_recovery_codes\" (identity_recovery_address_id, nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20221220124639000000_errors_index.down.sql",
    "content": "CREATE INDEX selfservice_errors_nid_idx ON selfservice_errors (id, nid);\n\nDROP INDEX selfservice_errors_errors_nid_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20221220124639000000_errors_index.mysql.down.sql",
    "content": "CREATE INDEX selfservice_errors_nid_idx ON selfservice_errors (id, nid);\n\n-- needed for foreign key constraint, was there before implicitly\nCREATE INDEX selfservice_errors_nid_only_idx ON selfservice_errors (nid);\n\nDROP INDEX selfservice_errors_errors_nid_id_idx ON selfservice_errors;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20221220124639000000_errors_index.mysql.up.sql",
    "content": "CREATE INDEX selfservice_errors_errors_nid_id_idx ON selfservice_errors (nid, id);\n\n-- This index is not needed anymore, because the primary ID index together with the new index cover all queries.\nDROP INDEX selfservice_errors_nid_idx ON selfservice_errors;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20221220124639000000_errors_index.up.sql",
    "content": "CREATE INDEX selfservice_errors_errors_nid_id_idx ON selfservice_errors (nid, id);\n\n-- This index is not needed anymore, because the primary ID index together with the new index cover all queries.\nDROP INDEX selfservice_errors_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230104193739000000_courier_list_index.down.sql",
    "content": "DROP INDEX courier_messages_nid_created_at_id_idx;\n\nDROP INDEX courier_messages_nid_status_created_at_id_idx;\n\nDROP INDEX courier_messages_nid_recipient_created_at_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230104193739000000_courier_list_index.mysql.down.sql",
    "content": "DROP INDEX courier_messages_nid_created_at_id_idx ON courier_messages;\n\nDROP INDEX courier_messages_nid_status_created_at_id_idx ON courier_messages;\n\nDROP INDEX courier_messages_nid_recipient_created_at_id_idx ON courier_messages;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230104193739000000_courier_list_index.up.sql",
    "content": "CREATE INDEX courier_messages_nid_created_at_id_idx ON courier_messages (nid, created_at DESC);\n\nCREATE INDEX courier_messages_nid_status_created_at_id_idx ON courier_messages (nid, status, created_at DESC);\n\nCREATE INDEX courier_messages_nid_recipient_created_at_id_idx ON courier_messages (nid, recipient, created_at DESC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230216142104000000_session_devices_index_drop.cockroach.down.sql",
    "content": "CREATE UNIQUE INDEX IF NOT EXISTS unique_session_device ON session_devices (nid, session_id, ip_address, user_agent);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230216142104000000_session_devices_index_drop.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS session_devices@unique_session_device CASCADE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230216142104000000_session_devices_index_drop.down.sql",
    "content": "CREATE UNIQUE INDEX IF NOT EXISTS unique_session_device ON session_devices (nid, session_id, ip_address, user_agent);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230216142104000000_session_devices_index_drop.mysql.down.sql",
    "content": "CREATE UNIQUE INDEX unique_session_device ON session_devices (nid, session_id, ip_address, user_agent);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230216142104000000_session_devices_index_drop.mysql.up.sql",
    "content": "ALTER TABLE session_devices DROP FOREIGN KEY session_devices_ibfk_2;\nALTER TABLE session_devices DROP INDEX unique_session_device;\nALTER TABLE session_devices ADD CONSTRAINT session_devices_ibfk_2 FOREIGN KEY (nid) REFERENCES networks(id) ON DELETE CASCADE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230216142104000000_session_devices_index_drop.up.sql",
    "content": "DROP INDEX IF EXISTS session_devices.unique_session_device;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000000_session_token_length.cockroach.down.sql",
    "content": "-- Downsizing is not yet supported in CockroachDB. Since this migration has no real-world impact on the application, we can safely\n-- not execute it.\n--\n-- ALTER TABLE sessions ALTER COLUMN token TYPE varchar(32);\n-- ALTER TABLE sessions ALTER COLUMN logout_token TYPE varchar(32);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000000_session_token_length.down.sql",
    "content": "ALTER TABLE sessions ALTER COLUMN token TYPE varchar(32);\nALTER TABLE sessions ALTER COLUMN logout_token TYPE varchar(32);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000000_session_token_length.mysql.down.sql",
    "content": "ALTER TABLE sessions MODIFY COLUMN token varchar(32) NULL;\nALTER TABLE sessions MODIFY COLUMN logout_token varchar(32) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000000_session_token_length.mysql.up.sql",
    "content": "ALTER TABLE sessions MODIFY COLUMN token varchar(39) NULL;\nALTER TABLE sessions MODIFY COLUMN logout_token varchar(39) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000000_session_token_length.sqlite3.down.sql",
    "content": "DROP INDEX sessions_token_uq_idx;\nDROP INDEX sessions_logout_token_uq_idx;\nDROP INDEX sessions_token_nid_idx;\n\nALTER TABLE sessions RENAME COLUMN token TO old_token;\nALTER TABLE sessions RENAME COLUMN logout_token TO old_logout_token;\n\nALTER TABLE sessions\n  ADD COLUMN token varchar(32) NULL;\nALTER TABLE sessions\n  ADD COLUMN logout_token varchar(32) NULL;\n\nUPDATE sessions\nSET token = old_token\nWHERE true;\n\nUPDATE sessions\nSET logout_token = old_logout_token\nWHERE true;\n\nALTER TABLE sessions\n  DROP COLUMN old_token;\n\nALTER TABLE sessions\n  DROP COLUMN old_logout_token;\n\nCREATE UNIQUE INDEX sessions_token_uq_idx ON sessions (logout_token);\nCREATE UNIQUE INDEX sessions_logout_token_uq_idx ON sessions (token);\nCREATE INDEX sessions_token_nid_idx ON sessions (nid, token);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000000_session_token_length.sqlite3.up.sql",
    "content": "DROP INDEX sessions_token_uq_idx;\nDROP INDEX sessions_logout_token_uq_idx;\nDROP INDEX sessions_token_nid_idx;\n\nALTER TABLE sessions RENAME COLUMN token TO old_token;\nALTER TABLE sessions RENAME COLUMN logout_token TO old_logout_token;\nALTER TABLE sessions\n  ADD COLUMN token varchar(39) NULL;\nALTER TABLE sessions\n  ADD COLUMN logout_token varchar(39) NULL;\n\nUPDATE sessions\nSET token = old_token\nWHERE true;\n\nUPDATE sessions\nSET logout_token = old_logout_token\nWHERE true;\n\nALTER TABLE sessions\n  DROP COLUMN old_token;\nALTER TABLE sessions\n  DROP COLUMN old_logout_token;\n\nCREATE UNIQUE INDEX sessions_token_uq_idx ON sessions (logout_token);\nCREATE UNIQUE INDEX sessions_logout_token_uq_idx ON sessions (token);\nCREATE INDEX sessions_token_nid_idx ON sessions (nid, token);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000000_session_token_length.up.sql",
    "content": "ALTER TABLE sessions ALTER COLUMN token TYPE varchar(39);\nALTER TABLE sessions ALTER COLUMN logout_token TYPE varchar(39);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000001_session_token_length.down.sql",
    "content": "UPDATE sessions SET token = LEFT(token, 32) WHERE TRUE;\nUPDATE sessions SET logout_token = LEFT(logout_token, 32) WHERE TRUE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000001_session_token_length.sqlite3.down.sql",
    "content": "UPDATE sessions SET token = substr(token, 0, 32) WHERE TRUE;\nUPDATE sessions SET logout_token = substr(logout_token, 0, 32) WHERE TRUE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230313141439000001_session_token_length.up.sql",
    "content": "--\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230322144139000001_missing_login_index.down.sql",
    "content": "DROP INDEX identity_credential_identifiers_nid_i_ici_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230322144139000001_missing_login_index.mysql.down.sql",
    "content": "DROP INDEX identity_credential_identifiers_nid_i_ici_idx ON identity_credential_identifiers;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230322144139000001_missing_login_index.up.sql",
    "content": "CREATE INDEX identity_credential_identifiers_nid_i_ici_idx ON identity_credential_identifiers (nid, identifier, identity_credential_id);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230405000000000001_create_session_token_exchanges.down.sql",
    "content": "DROP TABLE session_token_exchanges;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230405000000000001_create_session_token_exchanges.mysql.down.sql",
    "content": "DROP TABLE session_token_exchanges;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230405000000000001_create_session_token_exchanges.mysql.up.sql",
    "content": "CREATE TABLE session_token_exchanges (\n    id CHAR(36) NOT NULL PRIMARY KEY,\n    nid CHAR(36) NOT NULL,\n    flow_id CHAR(36) NOT NULL,\n    session_id CHAR(36) DEFAULT NULL,\n    init_code VARCHAR(64) NOT NULL,\n    return_to_code VARCHAR(64) NOT NULL,\n\n    created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Relevant query:\n--   SELECT * from session_token_exchanges\n--   WHERE nid = ? AND code = ? AND code <> '' AND session_id IS NOT NULL\nCREATE INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code, nid);\n\n-- Relevant query:\n--   UPDATE session_token_exchanges SET session_id = ? WHERE flow_id = ? AND nid = ?\nCREATE INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id, nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230405000000000001_create_session_token_exchanges.up.sql",
    "content": "CREATE TABLE session_token_exchanges (\n    \"id\" UUID NOT NULL PRIMARY KEY,\n    \"nid\" UUID NOT NULL,\n    \"flow_id\" UUID NOT NULL,\n    \"session_id\" UUID DEFAULT NULL,\n    \"init_code\" VARCHAR(64) NOT NULL,\n    \"return_to_code\" VARCHAR(64) NOT NULL,\n\n\n    \"created_at\" timestamp NOT NULL,\n    \"updated_at\" timestamp NOT NULL\n);\n\n-- Relevant query:\n--   SELECT * from session_token_exchanges\n--   WHERE nid = ? AND code = ? AND code <> '' AND session_id IS NOT NULL\nCREATE INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code, nid);\n\n-- Relevant query:\n--   UPDATE session_token_exchanges SET session_id = ? WHERE flow_id = ? AND nid = ?\nCREATE INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id, nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230614000001000000_hydra_login_challenge_format.down.sql",
    "content": "-- ALTER TABLE selfservice_login_flows ADD COLUMN oauth2_login_challenge UUID NULL;\n-- ALTER TABLE selfservice_registration_flows ADD COLUMN oauth2_login_challenge UUID NULL;\n\nALTER TABLE selfservice_login_flows DROP COLUMN oauth2_login_challenge_data;\nALTER TABLE selfservice_registration_flows DROP COLUMN oauth2_login_challenge_data;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230614000001000000_hydra_login_challenge_format.mysql.down.sql",
    "content": "-- ALTER TABLE selfservice_login_flows ADD COLUMN oauth2_login_challenge CHAR(36) NULL;\n-- ALTER TABLE selfservice_registration_flows ADD COLUMN oauth2_login_challenge CHAR(36) NULL;\n\nALTER TABLE selfservice_login_flows DROP COLUMN oauth2_login_challenge_data;\nALTER TABLE selfservice_registration_flows DROP COLUMN oauth2_login_challenge_data;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230614000001000000_hydra_login_challenge_format.sqlite3.down.sql",
    "content": "-- ALTER TABLE `selfservice_login_flows` ADD COLUMN `oauth2_login_challenge` CHAR(36) NULL;\n-- ALTER TABLE `selfservice_registration_flows` ADD COLUMN `oauth2_login_challenge` CHAR(36) NULL;\n\nALTER TABLE \"selfservice_login_flows\" DROP COLUMN \"oauth2_login_challenge_data\";\nALTER TABLE \"selfservice_registration_flows\" DROP COLUMN \"oauth2_login_challenge_data\";\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230614000001000000_hydra_login_challenge_format.up.sql",
    "content": "-- Drop columns in a later migration.\n-- ALTER TABLE selfservice_login_flows DROP COLUMN oauth2_login_challenge;\n-- ALTER TABLE selfservice_registration_flows DROP COLUMN oauth2_login_challenge;\n\nALTER TABLE selfservice_login_flows ADD COLUMN oauth2_login_challenge_data TEXT NULL;\nALTER TABLE selfservice_registration_flows ADD COLUMN oauth2_login_challenge_data TEXT NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230619000000000001_sessions_add_sorted_indices.down.sql",
    "content": "CREATE INDEX sessions_identity_id_nid_idx\n  ON sessions (identity_id, nid);\n\nDROP INDEX sessions_identity_id_nid_sorted_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230619000000000001_sessions_add_sorted_indices.mysql.down.sql",
    "content": "CREATE INDEX sessions_identity_id_nid_idx\n  ON sessions (identity_id, nid);\n\nDROP INDEX sessions_identity_id_nid_sorted_idx\n  ON sessions;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230619000000000001_sessions_add_sorted_indices.mysql.up.sql",
    "content": "CREATE INDEX sessions_identity_id_nid_sorted_idx\n  ON sessions (identity_id, nid, authenticated_at DESC);\n\nDROP INDEX sessions_identity_id_nid_idx\n  ON sessions;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230619000000000001_sessions_add_sorted_indices.up.sql",
    "content": "CREATE INDEX sessions_identity_id_nid_sorted_idx\n  ON sessions (identity_id, nid, authenticated_at DESC);\n\nDROP INDEX sessions_identity_id_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230626000000000001_identity_credential_identifiers_nid_identity_credential_id_idx.down.sql",
    "content": "-- nothing\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230626000000000001_identity_credential_identifiers_nid_identity_credential_id_idx.mysql.down.sql",
    "content": "-- nothing: needed for foreign key anyways\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230626000000000001_identity_credential_identifiers_nid_identity_credential_id_idx.mysql.up.sql",
    "content": "CREATE INDEX identity_credential_identifiers_nid_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id, nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230626000000000001_identity_credential_identifiers_nid_identity_credential_id_idx.up.sql",
    "content": "-- index already exists\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230703143600000001_selfservice_registration_login_flows_state.down.sql",
    "content": "ALTER table selfservice_registration_flows DROP COLUMN state;\nALTER table selfservice_login_flows DROP COLUMN state;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230703143600000001_selfservice_registration_login_flows_state.up.sql",
    "content": "ALTER table selfservice_login_flows ADD state VARCHAR(255) NULL;\nALTER table selfservice_registration_flows ADD state VARCHAR(255) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230705000000000001_cookie_flow_request_url.cockroach.down.sql",
    "content": "UPDATE selfservice_login_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_recovery_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_registration_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_settings_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_verification_flows SET request_url = substr(request_url, 1, 1024);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230705000000000001_cookie_flow_request_url.down.sql",
    "content": "UPDATE selfservice_login_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_recovery_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_registration_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_settings_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_verification_flows SET request_url = substr(request_url, 1, 1024);\n\nALTER TABLE selfservice_login_flows ALTER COLUMN request_url TYPE VARCHAR(1024);\nALTER TABLE selfservice_recovery_flows ALTER COLUMN request_url TYPE VARCHAR(1024);\nALTER TABLE selfservice_registration_flows ALTER COLUMN request_url TYPE VARCHAR(1024);\nALTER TABLE selfservice_settings_flows ALTER COLUMN request_url TYPE VARCHAR(1024);\nALTER TABLE selfservice_verification_flows ALTER COLUMN request_url TYPE VARCHAR(1024);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230705000000000001_cookie_flow_request_url.mysql.down.sql",
    "content": "UPDATE selfservice_login_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_recovery_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_registration_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_settings_flows SET request_url = substr(request_url, 1, 1024);\nUPDATE selfservice_verification_flows SET request_url = substr(request_url, 1, 1024);\n\nALTER TABLE selfservice_login_flows MODIFY request_url VARCHAR(1024);\nALTER TABLE selfservice_recovery_flows MODIFY request_url VARCHAR(1024);\nALTER TABLE selfservice_registration_flows MODIFY request_url VARCHAR(1024);\nALTER TABLE selfservice_settings_flows MODIFY request_url VARCHAR(1024);\nALTER TABLE selfservice_verification_flows MODIFY request_url VARCHAR(1024);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230705000000000001_cookie_flow_request_url.mysql.up.sql",
    "content": "ALTER TABLE selfservice_login_flows MODIFY request_url TEXT;\nALTER TABLE selfservice_recovery_flows MODIFY request_url TEXT;\nALTER TABLE selfservice_registration_flows MODIFY request_url TEXT;\nALTER TABLE selfservice_settings_flows MODIFY request_url TEXT;\nALTER TABLE selfservice_verification_flows MODIFY request_url TEXT;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230705000000000001_cookie_flow_request_url.sqlite.down.sql",
    "content": "ALTER TABLE selfservice_login_flows\n  ADD COLUMN request_url_new VARCHAR(1024) NOT NULL DEFAULT '';\nALTER TABLE selfservice_recovery_flows\n  ADD COLUMN request_url_new VARCHAR(1024) NOT NULL DEFAULT '';\nALTER TABLE selfservice_registration_flows\n  ADD COLUMN request_url_new VARCHAR(1024) NOT NULL DEFAULT '';\nALTER TABLE selfservice_settings_flows\n  ADD COLUMN request_url_new VARCHAR(1024) NOT NULL DEFAULT '';\nALTER TABLE selfservice_verification_flows\n  ADD COLUMN request_url_new VARCHAR(1024) NOT NULL DEFAULT '';\n\nUPDATE selfservice_login_flows\nSET request_url_new = substr(request_url, 1, 1024);\nUPDATE selfservice_recovery_flows\nSET request_url_new = substr(request_url, 1, 1024);\nUPDATE selfservice_registration_flows\nSET request_url_new = substr(request_url, 1, 1024);\nUPDATE selfservice_settings_flows\nSET request_url_new = substr(request_url, 1, 1024);\nUPDATE selfservice_verification_flows\nSET request_url_new = substr(request_url, 1, 1024);\n\nALTER TABLE selfservice_login_flows\n  DROP COLUMN request_url;\nALTER TABLE selfservice_login_flows RENAME COLUMN request_url_new TO request_url;\n\nALTER TABLE selfservice_recovery_flows\n  DROP COLUMN request_url;\nALTER TABLE selfservice_recovery_flows RENAME COLUMN request_url_new TO request_url;\n\nALTER TABLE selfservice_registration_flows\n  DROP COLUMN request_url;\nALTER TABLE selfservice_registration_flows RENAME COLUMN request_url_new TO request_url;\n\nALTER TABLE selfservice_settings_flows\n  DROP COLUMN request_url;\nALTER TABLE selfservice_settings_flows RENAME COLUMN request_url_new TO request_url;\n\nALTER TABLE selfservice_verification_flows\n  DROP COLUMN request_url;\nALTER TABLE selfservice_verification_flows RENAME COLUMN request_url_new TO request_url;\n\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230705000000000001_cookie_flow_request_url.sqlite.up.sql",
    "content": "ALTER TABLE selfservice_login_flows ADD COLUMN request_url_new TEXT NOT NULL DEFAULT '';\nALTER TABLE selfservice_recovery_flows ADD COLUMN request_url_new TEXT NOT NULL DEFAULT '';\nALTER TABLE selfservice_registration_flows ADD COLUMN request_url_new TEXT NOT NULL DEFAULT '';\nALTER TABLE selfservice_settings_flows ADD COLUMN request_url_new TEXT NOT NULL DEFAULT '';\nALTER TABLE selfservice_verification_flows ADD COLUMN request_url_new TEXT NOT NULL DEFAULT '';\n\nUPDATE selfservice_login_flows SET request_url_new = request_url;\nUPDATE selfservice_recovery_flows SET request_url_new = request_url;\nUPDATE selfservice_registration_flows SET request_url_new = request_url;\nUPDATE selfservice_settings_flows SET request_url_new = request_url;\nUPDATE selfservice_verification_flows SET request_url_new = request_url;\n\nALTER TABLE selfservice_login_flows DROP COLUMN request_url;\nALTER TABLE selfservice_login_flows RENAME COLUMN request_url_new TO request_url;\n\nALTER TABLE selfservice_recovery_flows DROP COLUMN request_url;\nALTER TABLE selfservice_recovery_flows RENAME COLUMN request_url_new TO request_url;\n\nALTER TABLE selfservice_registration_flows DROP COLUMN request_url;\nALTER TABLE selfservice_registration_flows RENAME COLUMN request_url_new TO request_url;\n\nALTER TABLE selfservice_settings_flows DROP COLUMN request_url;\nALTER TABLE selfservice_settings_flows RENAME COLUMN request_url_new TO request_url;\n\nALTER TABLE selfservice_verification_flows DROP COLUMN request_url;\nALTER TABLE selfservice_verification_flows RENAME COLUMN request_url_new TO request_url;\n\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230705000000000001_cookie_flow_request_url.up.sql",
    "content": "ALTER TABLE selfservice_login_flows ALTER COLUMN request_url TYPE TEXT;\nALTER TABLE selfservice_recovery_flows ALTER COLUMN request_url TYPE TEXT;\nALTER TABLE selfservice_registration_flows ALTER COLUMN request_url TYPE TEXT;\nALTER TABLE selfservice_settings_flows ALTER COLUMN request_url TYPE TEXT;\nALTER TABLE selfservice_verification_flows ALTER COLUMN request_url TYPE TEXT;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230706000000000001_available_aal.down.sql",
    "content": "ALTER TABLE identities DROP COLUMN available_aal;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230706000000000001_available_aal.up.sql",
    "content": "ALTER TABLE identities ADD COLUMN available_aal VARCHAR(4) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230707133700000000_identity_login_code.down.sql",
    "content": "DROP TABLE identity_login_codes;\n\nALTER TABLE selfservice_login_flows DROP submit_count;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230707133700000000_identity_login_code.mysql.up.sql",
    "content": "CREATE TABLE identity_login_codes\n(\n    id CHAR(36) NOT NULL PRIMARY KEY,\n    code VARCHAR(64) NOT NULL, -- HMACed value of the actual code\n    address VARCHAR(255) NOT NULL,\n    address_type CHAR(36) NOT NULL,\n    used_at timestamp NULL DEFAULT NULL,\n    expires_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    issued_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    selfservice_login_flow_id CHAR(36),\n    identity_id CHAR(36) NOT NULL,\n    created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    nid CHAR(36) NOT NULL,\n    CONSTRAINT identity_login_codes_selfservice_login_flows_id_fk\n        FOREIGN KEY (selfservice_login_flow_id)\n        REFERENCES selfservice_login_flows (id)\n        ON DELETE cascade,\n    CONSTRAINT identity_login_codes_networks_id_fk\n        FOREIGN KEY (nid)\n        REFERENCES networks (id)\n        ON UPDATE RESTRICT ON DELETE CASCADE\n);\n\nCREATE INDEX identity_login_codes_nid_flow_id_idx ON identity_login_codes (nid, selfservice_login_flow_id);\nCREATE INDEX identity_login_codes_id_nid_idx ON identity_login_codes (id, nid);\n\n\nALTER TABLE selfservice_login_flows ADD submit_count int NOT NULL DEFAULT 0;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230707133700000000_identity_login_code.up.sql",
    "content": "CREATE TABLE identity_login_codes\n(\n    id UUID NOT NULL PRIMARY KEY,\n    code VARCHAR(64) NOT NULL, -- HMACed value of the actual code\n    address VARCHAR(255) NOT NULL,\n    address_type CHAR(36) NOT NULL,\n    used_at timestamp NULL DEFAULT NULL,\n    expires_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    issued_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    selfservice_login_flow_id UUID NOT NULL,\n    identity_id UUID NOT NULL,\n    created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    nid UUID NOT NULL,\n    CONSTRAINT identity_login_codes_selfservice_login_flows_id_fk\n        FOREIGN KEY (selfservice_login_flow_id)\n        REFERENCES selfservice_login_flows (id)\n        ON DELETE cascade,\n    CONSTRAINT identity_login_codes_networks_id_fk\n        FOREIGN KEY (nid)\n        REFERENCES networks (id)\n        ON UPDATE RESTRICT ON DELETE CASCADE\n);\n\nCREATE INDEX identity_login_codes_nid_flow_id_idx ON identity_login_codes (nid, selfservice_login_flow_id);\nCREATE INDEX identity_login_codes_id_nid_idx ON identity_login_codes (id, nid);\n\nALTER TABLE selfservice_login_flows ADD submit_count int NOT NULL DEFAULT 0;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230707133700000001_identity_registration_code.down.sql",
    "content": "DROP TABLE identity_registration_codes;\n\nALTER TABLE selfservice_registration_flows DROP submit_count;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230707133700000001_identity_registration_code.mysql.up.sql",
    "content": "CREATE TABLE identity_registration_codes\n(\n    id CHAR(36) NOT NULL PRIMARY KEY,\n    code VARCHAR(64) NOT NULL, -- HMACed value of the actual code\n    address VARCHAR(255) NOT NULL,\n    address_type CHAR(36) NOT NULL,\n    used_at timestamp NULL DEFAULT NULL,\n    expires_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    issued_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    selfservice_registration_flow_id CHAR(36),\n    created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    nid CHAR(36) NOT NULL,\n    CONSTRAINT identity_registration_codes_selfservice_registration_flows_id_fk\n        FOREIGN KEY (selfservice_registration_flow_id)\n        REFERENCES selfservice_registration_flows (id)\n        ON DELETE cascade,\n    CONSTRAINT identity_registration_codes_networks_id_fk\n        FOREIGN KEY (nid)\n        REFERENCES networks (id)\n        ON UPDATE RESTRICT ON DELETE CASCADE\n);\n\nCREATE INDEX identity_registration_codes_nid_flow_id_idx ON identity_registration_codes (nid, selfservice_registration_flow_id);\nCREATE INDEX identity_registration_codes_id_nid_idx ON identity_registration_codes (id, nid);\n\nALTER TABLE selfservice_registration_flows ADD submit_count int NOT NULL DEFAULT 0;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230707133700000001_identity_registration_code.up.sql",
    "content": "CREATE TABLE identity_registration_codes\n(\n    id UUID NOT NULL PRIMARY KEY,\n    code VARCHAR(64) NOT NULL, -- HMACed value of the actual code\n    address VARCHAR(255) NOT NULL,\n    address_type CHAR(36) NOT NULL,\n    used_at timestamp NULL DEFAULT NULL,\n    expires_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    issued_at timestamp NOT NULL DEFAULT '2000-01-01 00:00:00',\n    selfservice_registration_flow_id UUID NOT NULL,\n    created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    nid UUID NOT NULL,\n    CONSTRAINT identity_registration_codes_selfservice_registration_flows_id_fk\n        FOREIGN KEY (selfservice_registration_flow_id)\n        REFERENCES selfservice_registration_flows (id)\n        ON DELETE cascade,\n    CONSTRAINT identity_registration_codes_networks_id_fk\n        FOREIGN KEY (nid)\n        REFERENCES networks (id)\n        ON UPDATE RESTRICT ON DELETE CASCADE\n);\n\nCREATE INDEX identity_registration_codes_nid_flow_id_idx ON identity_registration_codes (nid, selfservice_registration_flow_id);\nCREATE INDEX identity_registration_codes_id_nid_idx ON identity_registration_codes (id, nid);\n\nALTER TABLE selfservice_registration_flows ADD submit_count int NOT NULL DEFAULT 0;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230712173852000000_credential_types_code.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'code';\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230712173852000000_credential_types_code.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name) SELECT '14f3b7e2-8725-4068-be39-8a796485fd97', 'code' WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'code');\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230811000000000001_verification_add_oauth2_login_challenge.down.sql",
    "content": "ALTER TABLE selfservice_verification_flows DROP COLUMN oauth2_login_challenge;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230811000000000001_verification_add_oauth2_login_challenge.up.sql",
    "content": "ALTER TABLE selfservice_verification_flows ADD COLUMN oauth2_login_challenge TEXT NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230818000000000001_verification_add_oauth2_login_challenge_identity_id.down.sql",
    "content": "ALTER TABLE selfservice_verification_flows DROP COLUMN session_id;\n\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230818000000000001_verification_add_oauth2_login_challenge_identity_id.mysql.up.sql",
    "content": "ALTER TABLE selfservice_verification_flows ADD COLUMN session_id VARCHAR(36);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230818000000000001_verification_add_oauth2_login_challenge_identity_id.up.sql",
    "content": "ALTER TABLE selfservice_verification_flows ADD COLUMN session_id UUID;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230823000000000001_verification_add_oauth2_login_challenge_params.down.sql",
    "content": "ALTER TABLE selfservice_verification_flows DROP COLUMN identity_id;\nALTER TABLE selfservice_verification_flows DROP COLUMN authentication_methods;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230823000000000001_verification_add_oauth2_login_challenge_params.mysql.up.sql",
    "content": "ALTER TABLE selfservice_verification_flows ADD COLUMN identity_id VARCHAR(36);\nALTER TABLE selfservice_verification_flows ADD COLUMN authentication_methods JSON;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230823000000000001_verification_add_oauth2_login_challenge_params.sqlite.up.sql",
    "content": "ALTER TABLE selfservice_verification_flows ADD COLUMN identity_id VARCHAR(36);\nALTER TABLE selfservice_verification_flows ADD COLUMN authentication_methods TEXT;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230823000000000001_verification_add_oauth2_login_challenge_params.up.sql",
    "content": "ALTER TABLE selfservice_verification_flows ADD COLUMN identity_id UUID;\nALTER TABLE selfservice_verification_flows ADD COLUMN authentication_methods JSON;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230907085000000000_add_organization_id.down.sql",
    "content": "alter table selfservice_login_flows drop column organization_id;\nalter table selfservice_registration_flows drop column organization_id;\nalter table identities drop column organization_id;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230907085000000000_add_organization_id.mysql.up.sql",
    "content": "alter table selfservice_login_flows add column organization_id char(36) null;\nalter table selfservice_registration_flows add column organization_id char(36) null;\nalter table identities add column organization_id char(36) null;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230907085000000000_add_organization_id.up.sql",
    "content": "alter table selfservice_login_flows add column organization_id uuid null;\nalter table selfservice_registration_flows add column organization_id uuid null;\nalter table identities add column organization_id uuid null;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230920171028000000_identity_search_index.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS identity_credential_identifiers_nid_identifier_gin;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230920171028000000_identity_search_index.cockroach.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_identifier_gin ON identity_credential_identifiers USING GIN (nid, identifier gin_trgm_ops);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230920171028000000_identity_search_index.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20230920171028000000_identity_search_index.postgres.down.sql",
    "content": "DROP INDEX identity_credential_identifiers_nid_identifier_gin;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230920171028000000_identity_search_index.postgres.up.sql",
    "content": "CREATE EXTENSION IF NOT EXISTS pg_trgm;\nCREATE EXTENSION IF NOT EXISTS btree_gin;\n\nCREATE INDEX identity_credential_identifiers_nid_identifier_gin ON identity_credential_identifiers USING GIN (nid, identifier gin_trgm_ops);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20230920171028000000_identity_search_index.up.sql",
    "content": "\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20231108111100000000_credential_types_passkey.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'passkey';"
  },
  {
    "path": "persistence/sql/migrations/sql/20231108111100000000_credential_types_passkey.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name)\nSELECT '8d0ca544-9bf6-45d3-bd75-0bbb3aeba3c7', 'passkey'\nWHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'passkey');"
  },
  {
    "path": "persistence/sql/migrations/sql/20231130094628000000_courier_message_channel.down.sql",
    "content": "ALTER TABLE\n  courier_messages DROP column channel;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20231130094628000000_courier_message_channel.up.sql",
    "content": "ALTER TABLE\n  courier_messages\nADD\n  column channel VARCHAR(32) NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20240119094628000000_sessions_created_at_index.down.sql",
    "content": "DROP INDEX sessions_nid_created_at_id_idx;"
  },
  {
    "path": "persistence/sql/migrations/sql/20240119094628000000_sessions_created_at_index.mysql.down.sql",
    "content": "DROP INDEX sessions_nid_created_at_id_idx ON sessions;"
  },
  {
    "path": "persistence/sql/migrations/sql/20240119094628000000_sessions_created_at_index.up.sql",
    "content": "CREATE INDEX sessions_nid_created_at_id_idx ON sessions (nid, created_at DESC, id ASC);"
  },
  {
    "path": "persistence/sql/migrations/sql/20240213095000000000_identity_credentials_user_handle_index.cockroach.down.sql",
    "content": "DROP INDEX identity_credentials_user_handle_idx;"
  },
  {
    "path": "persistence/sql/migrations/sql/20240213095000000000_identity_credentials_user_handle_index.cockroach.up.sql",
    "content": "CREATE INVERTED INDEX identity_credentials_user_handle_idx\n    ON identity_credentials (config)\n    WHERE config ->> 'user_handle' IS NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20240213095000000000_identity_credentials_user_handle_index.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240213095000000000_identity_credentials_user_handle_index.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240214113828000000_courier_dispatch_indices.down.sql",
    "content": "CREATE INDEX IF NOT EXISTS courier_message_dispatches_id_message_id_nid_idx ON courier_message_dispatches (id ASC, message_id ASC, nid ASC);\n\nDROP INDEX courier_message_dispatches_message_id_idx;\nDROP INDEX courier_message_dispatches_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240214113828000000_courier_dispatch_indices.mysql.down.sql",
    "content": "CREATE INDEX courier_message_dispatches_id_message_id_nid_idx ON courier_message_dispatches (id ASC, message_id ASC, nid ASC);\n\n-- These can't be removed because of foreign key constraints which disallow index deletion in MySQL.\n\n-- DROP INDEX courier_message_dispatches_message_id_idx ON courier_message_dispatches;\n-- DROP INDEX courier_message_dispatches_nid_idx ON courier_message_dispatches;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240214113828000000_courier_dispatch_indices.mysql.up.sql",
    "content": "-- Remove unused index\nDROP INDEX courier_message_dispatches_id_message_id_nid_idx ON courier_message_dispatches;\n\n-- For pop eager load\nCREATE INDEX courier_message_dispatches_message_id_idx ON courier_message_dispatches (message_id, created_at DESC);\n\n-- For delete by nid\nCREATE INDEX courier_message_dispatches_nid_idx ON courier_message_dispatches (nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240214113828000000_courier_dispatch_indices.up.sql",
    "content": "-- Remove unused index\nDROP INDEX courier_message_dispatches_id_message_id_nid_idx;\n\n-- For pop eager load\nCREATE INDEX IF NOT EXISTS courier_message_dispatches_message_id_idx ON courier_message_dispatches (message_id, created_at DESC);\n\n-- For delete by nid\nCREATE INDEX IF NOT EXISTS courier_message_dispatches_nid_idx ON courier_message_dispatches (nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240221000000000000_identity_recovery_codes_flow_id_idx.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS identity_login_codes@identity_login_codes_identity_id_idx;\nDROP INDEX IF EXISTS identity_login_codes@identity_login_codes_flow_id_idx;\nDROP INDEX IF EXISTS identity_recovery_codes@identity_recovery_codes_flow_id_idx;\nDROP INDEX IF EXISTS identity_registration_codes@identity_registration_codes_flow_id_idx;\nDROP INDEX IF EXISTS identity_verification_codes@identity_verification_codes_flow_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240221000000000000_identity_recovery_codes_flow_id_idx.down.sql",
    "content": "DROP INDEX IF EXISTS identity_login_codes.identity_login_codes_identity_id_idx;\nDROP INDEX IF EXISTS identity_login_codes.identity_login_codes_flow_id_idx;\nDROP INDEX IF EXISTS identity_recovery_codes.identity_recovery_codes_flow_id_idx;\nDROP INDEX IF EXISTS identity_registration_codes.identity_registration_codes_flow_id_idx;\nDROP INDEX IF EXISTS identity_verification_codes.identity_verification_codes_flow_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240221000000000000_identity_recovery_codes_flow_id_idx.mysql.down.sql",
    "content": "ALTER TABLE `identity_recovery_codes`\n    DROP FOREIGN KEY `identity_recovery_codes_identity_id_fk`,\n    ADD CONSTRAINT `identity_recovery_tokens_identity_id_fk` FOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;\n\nALTER TABLE `identity_login_codes`\n   DROP FOREIGN KEY `identity_login_codes_identity_id_fk`;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240221000000000000_identity_recovery_codes_flow_id_idx.mysql.up.sql",
    "content": "-- This FK was previously misnamed.\nALTER TABLE `identity_recovery_codes`\n    DROP FOREIGN KEY `identity_recovery_tokens_identity_id_fk`,\n    ADD CONSTRAINT `identity_recovery_codes_identity_id_fk` FOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;\n\n-- Missing FK\nALTER TABLE `identity_login_codes`\n    ADD CONSTRAINT `identity_login_codes_identity_id_fk` FOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;\n\n-- MySQL has created the remaining indices automatically together with the foreign key constraints.\n\n-- CREATE INDEX identity_login_codes_identity_id_idx ON identity_login_codes (identity_id ASC);\n-- CREATE INDEX identity_login_codes_flow_id_idx ON identity_login_codes (selfservice_login_flow_id ASC);\n-- CREATE INDEX identity_registration_codes_flow_id_idx ON identity_registration_codes (selfservice_registration_flow_id ASC);\n-- CREATE INDEX identity_recovery_codes_flow_id_idx ON identity_recovery_codes (selfservice_recovery_flow_id ASC);\n-- CREATE INDEX identity_verification_codes_flow_id_idx ON identity_verification_codes (selfservice_verification_flow_id ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240221000000000000_identity_recovery_codes_flow_id_idx.sqlite.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_login_codes_identity_id_idx ON identity_login_codes (identity_id ASC);\nCREATE INDEX IF NOT EXISTS identity_login_codes_flow_id_idx ON identity_login_codes (selfservice_login_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_registration_codes_flow_id_idx ON identity_registration_codes (selfservice_registration_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_codes_flow_id_idx ON identity_recovery_codes (selfservice_recovery_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_verification_codes_flow_id_idx ON identity_verification_codes (selfservice_verification_flow_id ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240221000000000000_identity_recovery_codes_flow_id_idx.up.sql",
    "content": "ALTER TABLE identity_login_codes\n    ADD CONSTRAINT identity_login_codes_identity_id_fk FOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE CASCADE ON UPDATE RESTRICT;\n\nCREATE INDEX IF NOT EXISTS identity_login_codes_identity_id_idx ON identity_login_codes (identity_id ASC);\nCREATE INDEX IF NOT EXISTS identity_login_codes_flow_id_idx ON identity_login_codes (selfservice_login_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_registration_codes_flow_id_idx ON identity_registration_codes (selfservice_registration_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_codes_flow_id_idx ON identity_recovery_codes (selfservice_recovery_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_verification_codes_flow_id_idx ON identity_verification_codes (selfservice_verification_flow_id ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240318143139000000_drop_identity_search_index.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240318143139000000_drop_identity_search_index.postgres.down.sql",
    "content": "CREATE EXTENSION IF NOT EXISTS pg_trgm;\nCREATE EXTENSION IF NOT EXISTS btree_gin;\n\nCREATE INDEX identity_credential_identifiers_nid_identifier_gin ON identity_credential_identifiers USING GIN (nid, identifier gin_trgm_ops);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240318143139000000_drop_identity_search_index.postgres.up.sql",
    "content": "DROP INDEX identity_credential_identifiers_nid_identifier_gin;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240318143139000000_drop_identity_search_index.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240325153839000000_drop_identity_search_index.cockroach.down.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_identifier_gin ON identity_credential_identifiers USING GIN (nid, identifier gin_trgm_ops);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240325153839000000_drop_identity_search_index.cockroach.up.sql",
    "content": "DROP INDEX IF EXISTS identity_credential_identifiers_nid_identifier_gin;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240325153839000000_drop_identity_search_index.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240325153839000000_drop_identity_search_index.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240425095000000000_identity_credentials_fix_user_handle_index.cockroach.down.sql",
    "content": "DROP INDEX identity_credentials_config_user_handle_idx;"
  },
  {
    "path": "persistence/sql/migrations/sql/20240425095000000000_identity_credentials_fix_user_handle_index.cockroach.up.sql",
    "content": "CREATE INDEX identity_credentials_config_user_handle_idx\n    ON identity_credentials ((config ->> 'user_handle'))\n    WHERE config ->> 'user_handle' IS NOT NULL\n;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240425095000000000_identity_credentials_fix_user_handle_index.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240425095000000000_identity_credentials_fix_user_handle_index.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240425095000000001_identity_credentials_fix_user_handle_index.cockroach.down.sql",
    "content": "CREATE INVERTED INDEX identity_credentials_user_handle_idx\n    ON identity_credentials (config)\n    WHERE config ->> 'user_handle' IS NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20240425095000000001_identity_credentials_fix_user_handle_index.cockroach.up.sql",
    "content": "DROP INDEX identity_credentials_user_handle_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240425095000000001_identity_credentials_fix_user_handle_index.down.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240425095000000001_identity_credentials_fix_user_handle_index.up.sql",
    "content": ""
  },
  {
    "path": "persistence/sql/migrations/sql/20240923095000000001_organization_id_index.down.sql",
    "content": "DROP INDEX identities_nid_organization_id_idx;"
  },
  {
    "path": "persistence/sql/migrations/sql/20240923095000000001_organization_id_index.mysql.down.sql",
    "content": "DROP INDEX identities_nid_organization_id_idx ON identities;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20240923095000000001_organization_id_index.up.sql",
    "content": "CREATE INDEX identities_nid_organization_id_idx ON identities (organization_id);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.down.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credentials_id_nid_idx ON identity_credentials (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_credentials_nid_id_idx ON identity_credentials (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS identity_credentials_nid_identity_id_idx ON identity_credentials (identity_id ASC, nid ASC);\n\nDROP INDEX IF EXISTS identity_credentials_identity_id_idx;\nDROP INDEX IF EXISTS identity_credentials_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.down.sql",
    "content": "CREATE INDEX identity_credentials_id_nid_idx ON identity_credentials (id ASC, nid ASC);\nCREATE INDEX identity_credentials_nid_id_idx ON identity_credentials (nid ASC, id ASC);\n\nDROP INDEX identity_credentials_nid_idx ON identity_credentials;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.mysql.up.sql",
    "content": "CREATE INDEX identity_credentials_nid_idx ON identity_credentials (nid ASC);\n\nDROP INDEX identity_credentials_id_nid_idx ON identity_credentials;\nDROP INDEX identity_credentials_nid_id_idx ON identity_credentials;\n\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000001_drop_unused_indices_identity_credentials.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credentials_identity_id_idx ON identity_credentials (identity_id ASC);\nCREATE INDEX IF NOT EXISTS identity_credentials_nid_idx ON identity_credentials (nid ASC);\n\nDROP INDEX IF EXISTS identity_credentials_id_nid_idx;\nDROP INDEX IF EXISTS identity_credentials_nid_id_idx;\nDROP INDEX IF EXISTS identity_credentials_nid_identity_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.down.sql",
    "content": "CREATE INDEX IF NOT EXISTS sessions_nid_id_identity_id_idx ON sessions(nid ASC, identity_id ASC, id ASC);\nCREATE INDEX IF NOT EXISTS sessions_id_nid_idx ON sessions(id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS sessions_token_nid_idx ON sessions(nid ASC, token ASC);\nCREATE INDEX IF NOT EXISTS sessions_identity_id_nid_sorted_idx ON sessions(identity_id ASC, nid ASC, authenticated_at DESC);\nCREATE INDEX IF NOT EXISTS sessions_nid_created_at_id_idx ON sessions(nid ASC, created_at DESC, id ASC);\n\nDROP INDEX IF EXISTS sessions_list_idx;\nDROP INDEX IF EXISTS sessions_list_active_idx;\nDROP INDEX IF EXISTS sessions_list_identity_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.down.sql",
    "content": "CREATE INDEX sessions_nid_id_identity_id_idx ON sessions(nid ASC, identity_id ASC, id ASC);\nCREATE INDEX sessions_id_nid_idx ON sessions(id ASC, nid ASC);\nCREATE INDEX sessions_token_nid_idx ON sessions(nid ASC, token ASC);\nCREATE INDEX sessions_identity_id_nid_sorted_idx ON sessions(identity_id ASC, nid ASC, authenticated_at DESC);\nCREATE INDEX sessions_nid_created_at_id_idx ON sessions(nid ASC, created_at DESC, id ASC);\n\nDROP INDEX sessions_list_idx ON sessions;\nDROP INDEX sessions_list_active_idx ON sessions;\nDROP INDEX sessions_list_identity_idx ON sessions;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.mysql.up.sql",
    "content": "CREATE INDEX sessions_list_idx ON sessions (nid ASC, created_at DESC, id ASC);\nCREATE INDEX sessions_list_active_idx ON sessions (nid ASC, expires_at ASC, active ASC, created_at DESC, id ASC);\nCREATE INDEX sessions_list_identity_idx ON sessions (identity_id ASC, nid ASC, created_at DESC);\n\nDROP INDEX sessions_nid_id_identity_id_idx ON sessions;\nDROP INDEX sessions_id_nid_idx ON sessions;\nDROP INDEX sessions_token_nid_idx ON sessions;\nDROP INDEX sessions_identity_id_nid_sorted_idx ON sessions;\nDROP INDEX sessions_nid_created_at_id_idx ON sessions;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000002_drop_unused_indices_sessions.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS sessions_list_idx ON sessions (nid ASC, created_at DESC, id ASC);\nCREATE INDEX IF NOT EXISTS sessions_list_active_idx ON sessions (nid ASC, expires_at ASC, active ASC, created_at DESC, id ASC);\nCREATE INDEX IF NOT EXISTS sessions_list_identity_idx ON sessions (identity_id ASC, nid ASC, created_at DESC);\n\nDROP INDEX IF EXISTS sessions_nid_id_identity_id_idx;\nDROP INDEX IF EXISTS sessions_id_nid_idx;\nDROP INDEX IF EXISTS sessions_token_nid_idx;\nDROP INDEX IF EXISTS sessions_identity_id_nid_sorted_idx;\nDROP INDEX IF EXISTS sessions_nid_created_at_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.down.sql",
    "content": "-- THIS IS COCKROACH ONLY\nALTER INDEX identity_credential_identifiers_identifier_nid_type_uq_idx RENAME TO identity_credential_identifiers_identifier_nid_type_uq_idx_deleteme;\nCREATE UNIQUE INDEX IF NOT EXISTS identity_credential_identifiers_identifier_nid_type_uq_idx ON identity_credential_identifiers(nid ASC, identity_credential_type_id ASC, identifier ASC);\nDROP INDEX IF EXISTS identity_credential_identifiers_identifier_nid_type_uq_idx_deleteme;\n--\n\nCREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC, nid ASC);\n\nDROP INDEX IF EXISTS identity_credential_identifiers_identity_credential_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.cockroach.up.sql",
    "content": "-- THIS IS COCKROACH ONLY\nALTER INDEX identity_credential_identifiers_identifier_nid_type_uq_idx RENAME TO identity_credential_identifiers_identifier_nid_type_uq_idx_deleteme;\nCREATE UNIQUE INDEX IF NOT EXISTS identity_credential_identifiers_identifier_nid_type_uq_idx ON identity_credential_identifiers (nid ASC, identity_credential_type_id ASC, identifier ASC) STORING (identity_credential_id);\nDROP INDEX IF EXISTS identity_credential_identifiers_identifier_nid_type_uq_idx_deleteme;\n--\n\nCREATE INDEX IF NOT EXISTS identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC);\n\nDROP INDEX IF EXISTS identity_credential_identifiers_nid_id_idx;\nDROP INDEX IF EXISTS identity_credential_identifiers_id_nid_idx;\nDROP INDEX IF EXISTS identity_credential_identifiers_nid_identity_credential_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.down.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC, nid ASC);\n\nDROP INDEX IF EXISTS identity_credential_identifiers_identity_credential_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.down.sql",
    "content": "CREATE INDEX identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid ASC, id ASC);\nCREATE INDEX identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id ASC, nid ASC);\nCREATE INDEX identity_credential_identifiers_nid_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC, nid ASC);\n\nDROP INDEX identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.mysql.up.sql",
    "content": "CREATE INDEX identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC);\n\nDROP INDEX identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers;\nDROP INDEX identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers;\nDROP INDEX identity_credential_identifiers_nid_identity_credential_id_idx ON identity_credential_identifiers;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241023142500000003_drop_unused_indices_credential_identifiers.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers (identity_credential_id ASC);\n\nDROP INDEX IF EXISTS identity_credential_identifiers_nid_id_idx;\nDROP INDEX IF EXISTS identity_credential_identifiers_id_nid_idx;\nDROP INDEX IF EXISTS identity_credential_identifiers_nid_identity_credential_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241029102200000001_self_service.down.sql",
    "content": "CREATE INDEX IF NOT EXISTS selfservice_login_flows_nid_id_idx ON selfservice_login_flows (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS selfservice_login_flows_id_nid_idx ON selfservice_login_flows (id ASC, nid ASC);\nDROP INDEX IF EXISTS selfservice_login_flows_nid_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_errors_errors_nid_id_idx ON selfservice_errors (nid ASC, id ASC);\nDROP INDEX IF EXISTS selfservice_errors_nid_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_recovery_flows_nid_id_idx ON selfservice_recovery_flows (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS selfservice_recovery_flows_id_nid_idx ON selfservice_recovery_flows (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS selfservice_recovery_flows_recovered_identity_id_nid_idx ON selfservice_recovery_flows (recovered_identity_id ASC, nid ASC);\nDROP INDEX IF EXISTS selfservice_recovery_flows_nid_idx;\nDROP INDEX IF EXISTS selfservice_recovery_flows_recovered_identity_id_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_registration_flows_nid_id_idx ON selfservice_registration_flows (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS selfservice_registration_flows_id_nid_idx ON selfservice_registration_flows (id ASC, nid ASC);\nDROP INDEX IF EXISTS selfservice_registration_flows_nid_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_settings_flows_nid_id_idx ON selfservice_settings_flows (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS selfservice_settings_flows_id_nid_idx ON selfservice_settings_flows (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS selfservice_settings_flows_identity_id_nid_idx ON selfservice_settings_flows (identity_id ASC, nid ASC);\nDROP INDEX IF EXISTS selfservice_settings_flows_nid_idx;\nDROP INDEX IF EXISTS selfservice_settings_flows_identity_id_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_verification_flows_nid_id_idx ON selfservice_verification_flows (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS selfservice_verification_flows_id_nid_idx ON selfservice_verification_flows (id ASC, nid ASC);\nDROP INDEX IF EXISTS selfservice_verification_flows_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.down.sql",
    "content": "CREATE INDEX selfservice_login_flows_nid_id_idx ON selfservice_login_flows (nid ASC, id ASC);\nCREATE INDEX selfservice_login_flows_id_nid_idx ON selfservice_login_flows (id ASC, nid ASC);\nDROP INDEX selfservice_login_flows_nid_idx ON selfservice_login_flows;\n\nCREATE INDEX selfservice_errors_errors_nid_id_idx ON selfservice_errors (nid ASC, id ASC);\nDROP INDEX selfservice_errors_nid_idx ON selfservice_errors;\n\nCREATE INDEX selfservice_recovery_flows_nid_id_idx ON selfservice_recovery_flows (nid ASC, id ASC);\nCREATE INDEX selfservice_recovery_flows_id_nid_idx ON selfservice_recovery_flows (id ASC, nid ASC);\nCREATE INDEX selfservice_recovery_flows_recovered_identity_id_nid_idx ON selfservice_recovery_flows (recovered_identity_id ASC, nid ASC);\nDROP INDEX selfservice_recovery_flows_nid_idx ON selfservice_recovery_flows;\nDROP INDEX selfservice_recovery_flows_recovered_identity_id_idx ON selfservice_recovery_flows;\n\nCREATE INDEX selfservice_registration_flows_nid_id_idx ON selfservice_registration_flows (nid ASC, id ASC);\nCREATE INDEX selfservice_registration_flows_id_nid_idx ON selfservice_registration_flows (id ASC, nid ASC);\nDROP INDEX selfservice_registration_flows_nid_idx ON selfservice_registration_flows;\n\nCREATE INDEX selfservice_settings_flows_nid_id_idx ON selfservice_settings_flows (nid ASC, id ASC);\nCREATE INDEX selfservice_settings_flows_id_nid_idx ON selfservice_settings_flows (id ASC, nid ASC);\nCREATE INDEX selfservice_settings_flows_identity_id_nid_idx ON selfservice_settings_flows (identity_id ASC, nid ASC);\nDROP INDEX selfservice_settings_flows_nid_idx ON selfservice_settings_flows;\nDROP INDEX selfservice_settings_flows_identity_id_idx ON selfservice_settings_flows;\n\nCREATE INDEX selfservice_verification_flows_nid_id_idx ON selfservice_verification_flows (nid ASC, id ASC);\nCREATE INDEX selfservice_verification_flows_id_nid_idx ON selfservice_verification_flows (id ASC, nid ASC);\nDROP INDEX selfservice_verification_flows_nid_idx ON selfservice_verification_flows;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241029102200000001_self_service.mysql.up.sql",
    "content": "CREATE INDEX selfservice_login_flows_nid_idx ON selfservice_login_flows (nid ASC);\nDROP INDEX selfservice_login_flows_nid_id_idx ON selfservice_login_flows;\nDROP INDEX selfservice_login_flows_id_nid_idx ON selfservice_login_flows;\n\nCREATE INDEX selfservice_errors_nid_idx ON selfservice_errors (nid ASC);\nDROP INDEX selfservice_errors_errors_nid_id_idx ON selfservice_errors;\n\nCREATE INDEX selfservice_recovery_flows_nid_idx ON selfservice_recovery_flows (nid ASC);\nCREATE INDEX selfservice_recovery_flows_recovered_identity_id_idx ON selfservice_recovery_flows (recovered_identity_id ASC);\nDROP INDEX selfservice_recovery_flows_nid_id_idx ON selfservice_recovery_flows;\nDROP INDEX selfservice_recovery_flows_id_nid_idx ON selfservice_recovery_flows;\nDROP INDEX selfservice_recovery_flows_recovered_identity_id_nid_idx ON selfservice_recovery_flows;\n\nCREATE INDEX selfservice_registration_flows_nid_idx ON selfservice_registration_flows (nid ASC);\nDROP INDEX selfservice_registration_flows_nid_id_idx ON selfservice_registration_flows;\nDROP INDEX selfservice_registration_flows_id_nid_idx ON selfservice_registration_flows;\n\nCREATE INDEX selfservice_settings_flows_nid_idx ON selfservice_settings_flows (nid ASC);\nCREATE INDEX selfservice_settings_flows_identity_id_idx ON selfservice_settings_flows (identity_id ASC);\nDROP INDEX selfservice_settings_flows_nid_id_idx ON selfservice_settings_flows;\nDROP INDEX selfservice_settings_flows_id_nid_idx ON selfservice_settings_flows;\nDROP INDEX selfservice_settings_flows_identity_id_nid_idx ON selfservice_settings_flows;\n\nCREATE INDEX selfservice_verification_flows_nid_idx ON selfservice_verification_flows (nid ASC);\nDROP INDEX selfservice_verification_flows_nid_id_idx ON selfservice_verification_flows;\nDROP INDEX selfservice_verification_flows_id_nid_idx ON selfservice_verification_flows;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241029102200000001_self_service.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS selfservice_login_flows_nid_idx ON selfservice_login_flows (nid ASC);\nDROP INDEX IF EXISTS selfservice_login_flows_nid_id_idx;\nDROP INDEX IF EXISTS selfservice_login_flows_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_errors_nid_idx ON selfservice_errors (nid ASC);\nDROP INDEX IF EXISTS selfservice_errors_errors_nid_id_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_recovery_flows_recovered_identity_id_idx ON selfservice_recovery_flows (recovered_identity_id ASC);\nCREATE INDEX IF NOT EXISTS selfservice_recovery_flows_nid_idx ON selfservice_recovery_flows (nid ASC);\nDROP INDEX IF EXISTS selfservice_recovery_flows_nid_id_idx;\nDROP INDEX IF EXISTS selfservice_recovery_flows_id_nid_idx;\nDROP INDEX IF EXISTS selfservice_recovery_flows_recovered_identity_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_registration_flows_nid_idx ON selfservice_registration_flows (nid ASC);\nDROP INDEX IF EXISTS selfservice_registration_flows_nid_id_idx;\nDROP INDEX IF EXISTS selfservice_registration_flows_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_settings_flows_nid_idx ON selfservice_settings_flows (nid ASC);\nCREATE INDEX IF NOT EXISTS selfservice_settings_flows_identity_id_idx ON selfservice_settings_flows (identity_id ASC);\nDROP INDEX IF EXISTS selfservice_settings_flows_nid_id_idx;\nDROP INDEX IF EXISTS selfservice_settings_flows_id_nid_idx;\nDROP INDEX IF EXISTS selfservice_settings_flows_identity_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS selfservice_verification_flows_nid_idx ON selfservice_verification_flows (nid ASC);\nDROP INDEX IF EXISTS selfservice_verification_flows_nid_id_idx;\nDROP INDEX IF EXISTS selfservice_verification_flows_id_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.down.sql",
    "content": "CREATE INDEX IF NOT EXISTS identities_id_nid_idx ON identities (id ASC, nid ASC);\n\nCREATE INDEX IF NOT EXISTS identity_recovery_addresses_status_via_idx ON identity_recovery_addresses (nid ASC, via ASC, value ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_addresses_nid_identity_id_idx ON identity_recovery_addresses (identity_id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses (id ASC, nid ASC);\nDROP INDEX IF EXISTS identity_recovery_addresses_identity_id_idx;\n\nCREATE INDEX IF NOT EXISTS identity_verifiable_addresses_status_via_idx ON identity_verifiable_addresses (nid ASC, via ASC, value ASC);\nCREATE INDEX IF NOT EXISTS identity_verifiable_addresses_nid_identity_id_idx ON identity_verifiable_addresses (identity_id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_verifiable_addresses_nid_id_idx ON identity_verifiable_addresses (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS identity_verifiable_addresses_id_nid_idx ON identity_verifiable_addresses (id ASC, nid ASC);\nDROP INDEX IF EXISTS identity_verifiable_addresses_identity_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241029153900000001_identities.autocommit.up.sql",
    "content": "DROP INDEX IF EXISTS  identities_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses(identity_id ASC);\nDROP INDEX IF EXISTS identity_recovery_addresses_status_via_idx;\nDROP INDEX IF EXISTS identity_recovery_addresses_nid_identity_id_idx;\nDROP INDEX IF EXISTS identity_recovery_addresses_nid_id_idx;\nDROP INDEX IF EXISTS identity_recovery_addresses_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses (identity_id ASC);\nDROP INDEX IF EXISTS identity_verifiable_addresses_status_via_idx;\nDROP INDEX IF EXISTS identity_verifiable_addresses_nid_identity_id_idx;\nDROP INDEX IF EXISTS identity_verifiable_addresses_nid_id_idx;\nDROP INDEX IF EXISTS identity_verifiable_addresses_id_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241029153900000001_identities.mysql.down.sql",
    "content": "CREATE INDEX identities_id_nid_idx ON identities (id ASC, nid ASC);\n\nCREATE INDEX identity_recovery_addresses_status_via_idx ON identity_recovery_addresses (nid ASC, via ASC, value ASC);\n-- While this index did not exist in the past, it is needed in MySQL for foreign key relations. We accept\n-- that this index is \"unaccounted\" for if we execute down and then up migrations on MySQL.\nCREATE INDEX identity_recovery_addresses_identity_id_fk_idx ON identity_recovery_addresses (identity_id ASC);\nCREATE INDEX identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses (nid ASC, id ASC);\nCREATE INDEX identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses (id ASC, nid ASC);\nDROP INDEX identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses;\n\nCREATE INDEX identity_verifiable_addresses_status_via_idx ON identity_verifiable_addresses (nid ASC, via ASC, value ASC);\n-- While this index did not exist in the past, it is needed in MySQL for foreign key relations. We accept\n-- that this index is \"unaccounted\" for if we execute down and then up migrations on MySQL.\nCREATE INDEX identity_verifiable_addresses_identity_id_fk_idx ON identity_verifiable_addresses (identity_id ASC);\nCREATE INDEX identity_verifiable_addresses_nid_id_idx ON identity_verifiable_addresses (nid ASC, id ASC);\nCREATE INDEX identity_verifiable_addresses_id_nid_idx ON identity_verifiable_addresses (id ASC, nid ASC);\nDROP INDEX identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241029153900000001_identities.mysql.up.sql",
    "content": "DROP INDEX identities_id_nid_idx ON identities;\n\nCREATE INDEX identity_recovery_addresses_identity_id_idx ON identity_recovery_addresses (identity_id ASC);\nDROP INDEX identity_recovery_addresses_status_via_idx ON identity_recovery_addresses;\n-- DROP INDEX identity_recovery_addresses_nid_identity_id_idx ON identity_recovery_addresses;\nDROP INDEX identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses;\nDROP INDEX identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses;\n\nCREATE INDEX identity_verifiable_addresses_identity_id_idx ON identity_verifiable_addresses (identity_id ASC);\nDROP INDEX identity_verifiable_addresses_status_via_idx ON identity_verifiable_addresses;\n-- DROP INDEX identity_verifiable_addresses_nid_identity_id_idx ON identity_verifiable_addresses;\nDROP INDEX identity_verifiable_addresses_nid_id_idx ON identity_verifiable_addresses;\nDROP INDEX identity_verifiable_addresses_id_nid_idx ON identity_verifiable_addresses;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.down.sql",
    "content": "CREATE INDEX IF NOT EXISTS session_devices_id_nid_idx ON session_devices (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS session_devices_session_id_nid_idx ON session_devices (session_id ASC, nid ASC);\nDROP INDEX IF EXISTS session_devices_nid_idx;\nDROP INDEX IF EXISTS session_devices_session_id_idx;\n\nCREATE INDEX IF NOT EXISTS session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id ASC, nid ASC);\nDROP INDEX IF EXISTS session_token_exchanges_flow_id_nid_init_code_idx;\nDROP INDEX IF EXISTS session_token_exchanges_nid_init_code_idx;\n\nCREATE INDEX IF NOT EXISTS courier_messages_status_idx ON  courier_messages (status ASC);\nCREATE INDEX IF NOT EXISTS courier_messages_nid_id_idx ON  courier_messages (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS courier_messages_id_nid_idx ON  courier_messages (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS courier_messages_nid_created_at_id_idx ON  courier_messages (nid ASC, created_at DESC);\nDROP INDEX IF EXISTS courier_messages_status_id_idx;\nDROP INDEX IF EXISTS courier_messages_nid_id_created_at_idx;\n\nCREATE INDEX IF NOT EXISTS continuity_containers_nid_id_idx ON continuity_containers (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS continuity_containers_id_nid_idx ON continuity_containers (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS continuity_containers_identity_id_nid_idx ON continuity_containers (identity_id ASC, nid ASC);\nDROP INDEX IF EXISTS continuity_containers_identity_id_idx;\nDROP INDEX IF EXISTS continuity_containers_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_verification_codes_nid_flow_id_idx ON identity_verification_codes (nid ASC, selfservice_verification_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_verification_codes_id_nid_idx ON identity_verification_codes (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_verification_codes_verifiable_address_nid_idx ON identity_verification_codes (identity_verifiable_address_id ASC, nid ASC);\nDROP INDEX IF EXISTS identity_verification_codes_identity_verifiable_address_id_idx;\nDROP INDEX IF EXISTS identity_verification_codes_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid ASC, token ASC, used ASC, selfservice_verification_flow_id ASC);\nDROP INDEX IF EXISTS identity_verification_tokens_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_registration_codes_nid_flow_id_idx ON identity_registration_codes (nid ASC, selfservice_registration_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_registration_codes_id_nid_idx ON identity_registration_codes (id ASC, nid ASC);\nDROP INDEX IF EXISTS identity_registration_codes_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_recovery_tokens_nid_id_idx  ON identity_recovery_tokens (nid ASC, id ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_tokens_id_nid_idx  ON identity_recovery_tokens (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_tokens_token_nid_used_idx  ON identity_recovery_tokens (nid ASC, token ASC, used ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_tokens_identity_id_nid_idx  ON identity_recovery_tokens (identity_id ASC, nid ASC);\nDROP INDEX IF EXISTS identity_recovery_tokens_identity_id_idx;\nDROP INDEX IF EXISTS identity_recovery_tokens_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_recovery_codes_nid_flow_id_idx ON identity_recovery_codes (nid ASC, selfservice_recovery_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_codes_id_nid_idx ON identity_recovery_codes (id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_codes_identity_id_nid_idx ON identity_recovery_codes (identity_id ASC, nid ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_codes_identity_recovery_address_id_nid_idx ON identity_recovery_codes (identity_recovery_address_id ASC, nid ASC);\nDROP INDEX IF EXISTS identity_recovery_codes_identity_recovery_address_id_idx;\nDROP INDEX IF EXISTS identity_recovery_codes_identity_id_idx;\nDROP INDEX IF EXISTS identity_recovery_codes_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_login_codes_nid_flow_id_idx ON identity_login_codes (nid ASC, selfservice_login_flow_id ASC);\nCREATE INDEX IF NOT EXISTS identity_login_codes_id_nid_idx ON identity_login_codes (id ASC, nid ASC);\nDROP INDEX IF EXISTS identity_login_codes_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.autocommit.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS session_devices_nid_idx ON session_devices (nid ASC);\nCREATE INDEX IF NOT EXISTS session_devices_session_id_idx ON session_devices (session_id ASC);\nDROP INDEX IF EXISTS session_devices_id_nid_idx;\nDROP INDEX IF EXISTS session_devices_session_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS session_token_exchanges_flow_id_nid_init_code_idx ON session_token_exchanges (flow_id ASC, nid ASC, init_code ASC);\nCREATE INDEX IF NOT EXISTS session_token_exchanges_nid_init_code_idx ON session_token_exchanges (nid ASC, init_code ASC);\nDROP INDEX IF EXISTS session_token_exchanges_nid_code_idx;\nDROP INDEX IF EXISTS session_token_exchanges_nid_flow_id_idx;\n\nCREATE INDEX IF NOT EXISTS courier_messages_status_id_idx ON courier_messages (status ASC, id ASC);\nCREATE INDEX IF NOT EXISTS courier_messages_nid_id_created_at_idx ON courier_messages (nid ASC, id ASC, created_at DESC);\nDROP INDEX IF EXISTS courier_messages_status_idx;\nDROP INDEX IF EXISTS courier_messages_nid_id_idx;\nDROP INDEX IF EXISTS courier_messages_id_nid_idx;\nDROP INDEX IF EXISTS courier_messages_nid_created_at_id_idx;\n\nCREATE INDEX IF NOT EXISTS continuity_containers_identity_id_idx ON continuity_containers (identity_id ASC);\nCREATE INDEX IF NOT EXISTS continuity_containers_nid_idx ON continuity_containers (nid ASC);\nDROP INDEX IF EXISTS continuity_containers_nid_id_idx;\nDROP INDEX IF EXISTS continuity_containers_id_nid_idx;\nDROP INDEX IF EXISTS continuity_containers_identity_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_verification_codes_identity_verifiable_address_id_idx ON identity_verification_codes (identity_verifiable_address_id ASC);\nCREATE INDEX IF NOT EXISTS identity_verification_codes_nid_idx ON identity_verification_codes (nid ASC);\nDROP INDEX IF EXISTS identity_verification_codes_nid_flow_id_idx;\nDROP INDEX IF EXISTS identity_verification_codes_id_nid_idx;\nDROP INDEX IF EXISTS identity_verification_codes_verifiable_address_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_verification_tokens_nid_idx ON identity_verification_tokens (nid ASC);\nDROP INDEX IF EXISTS identity_verification_tokens_nid_id_idx;\nDROP INDEX IF EXISTS identity_verification_tokens_id_nid_idx;\nDROP INDEX IF EXISTS identity_verification_tokens_token_nid_used_flow_id_idx;\n\nCREATE INDEX IF NOT EXISTS identity_registration_codes_nid_idx ON identity_registration_codes (nid ASC);\nDROP INDEX IF EXISTS identity_registration_codes_nid_flow_id_idx;\nDROP INDEX IF EXISTS identity_registration_codes_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_recovery_tokens_identity_id_idx ON identity_recovery_tokens (identity_id ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_tokens_nid_idx ON identity_recovery_tokens (nid ASC);\nDROP INDEX IF EXISTS identity_recovery_tokens_nid_id_idx;\nDROP INDEX IF EXISTS identity_recovery_tokens_id_nid_idx;\nDROP INDEX IF EXISTS identity_recovery_tokens_token_nid_used_idx;\nDROP INDEX IF EXISTS identity_recovery_tokens_identity_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_recovery_codes_identity_recovery_address_id_idx ON identity_recovery_codes (identity_recovery_address_id ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_codes_identity_id_idx ON identity_recovery_codes (identity_id ASC);\nCREATE INDEX IF NOT EXISTS identity_recovery_codes_nid_idx ON identity_recovery_codes (nid ASC);\nDROP INDEX IF EXISTS identity_recovery_codes_nid_flow_id_idx;\nDROP INDEX IF EXISTS identity_recovery_codes_id_nid_idx;\nDROP INDEX IF EXISTS identity_recovery_codes_identity_id_nid_idx;\nDROP INDEX IF EXISTS identity_recovery_codes_identity_recovery_address_id_nid_idx;\n\nCREATE INDEX IF NOT EXISTS identity_login_codes_nid_idx ON identity_login_codes (nid ASC);\nDROP INDEX IF EXISTS identity_login_codes_nid_flow_id_idx;\nDROP INDEX IF EXISTS identity_login_codes_id_nid_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.down.sql",
    "content": "CREATE INDEX session_devices_id_nid_idx ON session_devices (nid ASC, id ASC); -- the original index is id, nid - but then we can't drop session_devices_nid_idx\nCREATE INDEX session_devices_session_id_nid_idx ON session_devices (session_id ASC, nid ASC);\nDROP INDEX session_devices_nid_idx ON session_devices;\nDROP INDEX session_devices_session_id_idx ON session_devices;\n\nCREATE INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code ASC, nid ASC);\nCREATE INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id ASC, nid ASC);\nDROP INDEX session_token_exchanges_flow_id_nid_init_code_idx ON session_token_exchanges;\nDROP INDEX session_token_exchanges_nid_init_code_idx ON session_token_exchanges;\n\nCREATE INDEX courier_messages_status_idx ON courier_messages (status ASC);\nCREATE INDEX courier_messages_nid_id_idx ON courier_messages (nid ASC, id ASC);\nCREATE INDEX courier_messages_id_nid_idx ON courier_messages (id ASC, nid ASC);\nCREATE INDEX courier_messages_nid_created_at_id_idx ON courier_messages (nid ASC, created_at DESC);\nDROP INDEX courier_messages_status_id_idx ON courier_messages;\nDROP INDEX courier_messages_nid_id_created_at_idx ON courier_messages;\n\nCREATE INDEX continuity_containers_nid_id_idx ON continuity_containers (nid ASC, id ASC);\nCREATE INDEX continuity_containers_id_nid_idx ON continuity_containers (id ASC, nid ASC);\nCREATE INDEX continuity_containers_identity_id_nid_idx ON continuity_containers (identity_id ASC, nid ASC);\nDROP INDEX continuity_containers_identity_id_idx ON continuity_containers;\nDROP INDEX continuity_containers_nid_idx ON continuity_containers;\n\nCREATE INDEX identity_verification_codes_nid_flow_id_idx ON identity_verification_codes (nid ASC, selfservice_verification_flow_id ASC);\nCREATE INDEX identity_verification_codes_id_nid_idx ON identity_verification_codes (id ASC, nid ASC);\nCREATE INDEX identity_verification_codes_verifiable_address_nid_idx ON identity_verification_codes (identity_verifiable_address_id ASC, nid ASC);\nDROP INDEX identity_verification_codes_verifiable_address_idx ON identity_verification_codes;\nDROP INDEX identity_verification_codes_nid_idx ON identity_verification_codes;\n\nCREATE INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens (nid ASC, id ASC);\nCREATE INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens (id ASC, nid ASC);\nCREATE INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens (nid ASC, token ASC, used ASC, selfservice_verification_flow_id ASC);\nDROP INDEX identity_verification_tokens_nid_idx ON identity_verification_tokens;\n\nCREATE INDEX identity_registration_codes_nid_flow_id_idx ON identity_registration_codes (nid ASC, selfservice_registration_flow_id ASC);\nCREATE INDEX identity_registration_codes_id_nid_idx ON identity_registration_codes (id ASC, nid ASC);\nDROP INDEX identity_registration_codes_nid_idx ON identity_registration_codes;\n\nCREATE INDEX identity_recovery_tokens_nid_id_idx ON identity_recovery_tokens (nid ASC, id ASC);\nCREATE INDEX identity_recovery_tokens_id_nid_idx ON identity_recovery_tokens (id ASC, nid ASC);\nCREATE INDEX identity_recovery_tokens_token_nid_used_idx ON identity_recovery_tokens (nid ASC, token ASC, used ASC);\nCREATE INDEX identity_recovery_tokens_identity_id_nid_idx ON identity_recovery_tokens (identity_id ASC, nid ASC);\nDROP INDEX identity_recovery_tokens_identity_id_idx ON identity_recovery_tokens;\nDROP INDEX identity_recovery_tokens_nid_idx ON identity_recovery_tokens;\n\nCREATE INDEX identity_recovery_codes_nid_flow_id_idx ON identity_recovery_codes (nid ASC, selfservice_recovery_flow_id ASC);\nCREATE INDEX identity_recovery_codes_id_nid_idx ON identity_recovery_codes (id ASC, nid ASC);\nCREATE INDEX identity_recovery_codes_identity_id_nid_idx ON identity_recovery_codes (identity_id ASC, nid ASC);\nCREATE INDEX identity_recovery_codes_identity_recovery_address_id_nid_idx ON identity_recovery_codes (identity_recovery_address_id ASC, nid ASC);\nDROP INDEX identity_recovery_codes_address_id_idx ON identity_recovery_codes;\nDROP INDEX identity_recovery_codes_identity_id_idx ON identity_recovery_codes;\nDROP INDEX identity_recovery_codes_nid_idx ON identity_recovery_codes;\n\nCREATE INDEX identity_login_codes_nid_flow_id_idx ON identity_login_codes (nid ASC, selfservice_login_flow_id ASC);\nCREATE INDEX identity_login_codes_id_nid_idx ON identity_login_codes (id ASC, nid ASC);\nDROP INDEX identity_login_codes_nid_idx ON identity_login_codes;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241031094100000001_remaining_unused_indices.mysql.up.sql",
    "content": "CREATE INDEX session_devices_nid_idx ON session_devices (nid ASC);\nCREATE INDEX session_devices_session_id_idx ON session_devices (session_id ASC);\nDROP INDEX session_devices_id_nid_idx ON session_devices;\nDROP INDEX session_devices_session_id_nid_idx ON session_devices;\n\nCREATE INDEX session_token_exchanges_flow_id_nid_init_code_idx ON session_token_exchanges (flow_id ASC, nid ASC, init_code ASC);\nCREATE INDEX session_token_exchanges_nid_init_code_idx ON session_token_exchanges (nid ASC, init_code ASC);\nDROP INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges;\nDROP INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges;\n\nCREATE INDEX courier_messages_status_id_idx ON courier_messages (status ASC, id ASC);\nCREATE INDEX courier_messages_nid_id_created_at_idx ON courier_messages (nid ASC, id ASC, created_at DESC);\nDROP INDEX courier_messages_status_idx ON courier_messages;\nDROP INDEX courier_messages_nid_id_idx ON courier_messages;\nDROP INDEX courier_messages_id_nid_idx ON courier_messages;\nDROP INDEX courier_messages_nid_created_at_id_idx ON courier_messages;\n\nCREATE INDEX continuity_containers_identity_id_idx ON continuity_containers (identity_id ASC);\nCREATE INDEX continuity_containers_nid_idx ON continuity_containers (nid ASC);\nDROP INDEX continuity_containers_nid_id_idx ON continuity_containers;\nDROP INDEX continuity_containers_id_nid_idx ON continuity_containers;\nDROP INDEX continuity_containers_identity_id_nid_idx ON continuity_containers;\n\nCREATE INDEX identity_verification_codes_verifiable_address_idx ON identity_verification_codes (identity_verifiable_address_id ASC);\nCREATE INDEX identity_verification_codes_nid_idx ON identity_verification_codes (nid ASC);\nDROP INDEX identity_verification_codes_nid_flow_id_idx ON identity_verification_codes;\nDROP INDEX identity_verification_codes_id_nid_idx ON identity_verification_codes;\nDROP INDEX identity_verification_codes_verifiable_address_nid_idx ON identity_verification_codes;\n\nCREATE INDEX identity_verification_tokens_nid_idx ON identity_verification_tokens (nid ASC);\nDROP INDEX identity_verification_tokens_nid_id_idx ON identity_verification_tokens;\nDROP INDEX identity_verification_tokens_id_nid_idx ON identity_verification_tokens;\nDROP INDEX identity_verification_tokens_token_nid_used_flow_id_idx ON identity_verification_tokens;\n\nCREATE INDEX identity_registration_codes_nid_idx ON identity_registration_codes (nid ASC);\nDROP INDEX identity_registration_codes_nid_flow_id_idx ON identity_registration_codes;\nDROP INDEX identity_registration_codes_id_nid_idx ON identity_registration_codes;\n\nCREATE INDEX identity_recovery_tokens_identity_id_idx ON identity_recovery_tokens (identity_id ASC);\nCREATE INDEX identity_recovery_tokens_nid_idx ON identity_recovery_tokens (nid ASC);\nDROP INDEX identity_recovery_tokens_nid_id_idx ON identity_recovery_tokens;\nDROP INDEX identity_recovery_tokens_id_nid_idx ON identity_recovery_tokens;\nDROP INDEX identity_recovery_tokens_token_nid_used_idx ON identity_recovery_tokens;\nDROP INDEX identity_recovery_tokens_identity_id_nid_idx ON identity_recovery_tokens;\n\nCREATE INDEX identity_recovery_codes_address_id_idx ON identity_recovery_codes (identity_recovery_address_id ASC);\nCREATE INDEX identity_recovery_codes_identity_id_idx ON identity_recovery_codes (identity_id ASC);\nCREATE INDEX identity_recovery_codes_nid_idx ON identity_recovery_codes (nid ASC);\nDROP INDEX identity_recovery_codes_nid_flow_id_idx ON identity_recovery_codes;\nDROP INDEX identity_recovery_codes_id_nid_idx ON identity_recovery_codes;\nDROP INDEX identity_recovery_codes_identity_id_nid_idx ON identity_recovery_codes;\nDROP INDEX identity_recovery_codes_identity_recovery_address_id_nid_idx ON identity_recovery_codes;\n\nCREATE INDEX identity_login_codes_nid_idx ON identity_login_codes (nid ASC);\nDROP INDEX identity_login_codes_nid_flow_id_idx ON identity_login_codes;\nDROP INDEX identity_login_codes_id_nid_idx ON identity_login_codes;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.down.sql",
    "content": "ALTER TABLE session_token_exchanges DROP CONSTRAINT session_token_exchanges_nid_fk;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241031094100000002_foreign_key.autocommit.up.sql",
    "content": "ALTER TABLE session_token_exchanges ADD CONSTRAINT session_token_exchanges_nid_fk FOREIGN KEY (nid) REFERENCES networks (id) ON DELETE CASCADE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.down.sql",
    "content": "-- Step 1: Create a temporary table without the nid column and foreign key constraint\nCREATE TABLE session_token_exchanges_temp\n(\n  id             TEXT        NOT NULL,\n  flow_id        TEXT        NOT NULL,\n  session_id     TEXT,\n  init_code      VARCHAR(64) NOT NULL,\n  return_to_code VARCHAR(64) NOT NULL,\n  created_at     TIMESTAMP   NOT NULL,\n  updated_at     TIMESTAMP   NOT NULL,\n  PRIMARY KEY (id)\n);\n\n-- Step 2: Copy data from the original table to the temporary table (excluding the nid column)\nINSERT INTO session_token_exchanges_temp (id, flow_id, session_id, init_code, return_to_code, created_at, updated_at)\nSELECT id, flow_id, session_id, init_code, return_to_code, created_at, updated_at\nFROM session_token_exchanges;\n\n-- Step 3: Drop the original table\nDROP TABLE session_token_exchanges;\n\n-- Step 4: Rename the temporary table to the original table name\nALTER TABLE session_token_exchanges_temp RENAME TO session_token_exchanges;\n\n-- Step 5: Recreate indexes as needed (excluding nid)\nCREATE INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code);\nCREATE INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241031094100000002_foreign_key.sqlite.up.sql",
    "content": "-- Step 1: Create a temporary table with the new column and foreign key constraint\nCREATE TABLE session_token_exchanges_temp\n(\n  id             TEXT        NOT NULL,\n  nid            TEXT        NOT NULL,\n  flow_id        TEXT        NOT NULL,\n  session_id     TEXT,\n  init_code      VARCHAR(64) NOT NULL,\n  return_to_code VARCHAR(64) NOT NULL,\n  created_at     TIMESTAMP   NOT NULL,\n  updated_at     TIMESTAMP   NOT NULL,\n  PRIMARY KEY (id),\n  FOREIGN KEY (nid) REFERENCES networks (id) ON DELETE CASCADE\n);\n\n-- Step 2: Copy data from the original table to the temporary table\nINSERT INTO session_token_exchanges_temp (id, nid, flow_id, session_id, init_code, return_to_code, created_at,\n                                          updated_at)\nSELECT id,\n       nid,\n       flow_id,\n       session_id,\n       init_code,\n       return_to_code,\n       created_at,\n       updated_at\nFROM session_token_exchanges;\n\n-- Step 3: Drop the original table\nDROP TABLE session_token_exchanges;\n\n-- Step 4: Rename the temporary table to the original table name\nALTER TABLE session_token_exchanges_temp RENAME TO session_token_exchanges;\n\n-- Step 5: Recreate indexes as needed\nCREATE INDEX session_token_exchanges_nid_code_idx ON session_token_exchanges (init_code, nid);\nCREATE INDEX session_token_exchanges_nid_flow_id_idx ON session_token_exchanges (flow_id, nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.down.sql",
    "content": "DROP INDEX IF EXISTS identity_credential_identifiers_nid_ici_i_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241106142200000001_identities.autocommit.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_ici_i_idx\n    ON identity_credential_identifiers (nid ASC, identity_credential_id ASC, identifier ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241106142200000001_identities.mysql.down.sql",
    "content": "DROP INDEX identity_credential_identifiers_nid_ici_i_idx ON identity_credential_identifiers;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241106142200000001_identities.mysql.up.sql",
    "content": "CREATE INDEX identity_credential_identifiers_nid_ici_i_idx\n    ON identity_credential_identifiers (nid ASC, identity_credential_id ASC, identifier ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.down.sql",
    "content": "\nDROP INDEX IF EXISTS identity_credential_identifiers_ici_nid_i_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241106142200000002_identities.autocommit.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credential_identifiers_ici_nid_i_idx\n    ON identity_credential_identifiers (identity_credential_id ASC, nid ASC, identifier ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241106142200000002_identities.mysql.down.sql",
    "content": "DROP INDEX identity_credential_identifiers_ici_nid_i_idx ON identity_credential_identifiers;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241106142200000002_identities.mysql.up.sql",
    "content": "CREATE INDEX identity_credential_identifiers_ici_nid_i_idx\n  ON identity_credential_identifiers (identity_credential_id ASC, nid ASC, identifier ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.down.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credential_identifiers_nid_ici_i_idx\n  ON identity_credential_identifiers (nid ASC, identity_credential_id ASC, identifier ASC);\n\nCREATE INDEX IF NOT EXISTS identity_credential_identifiers_identity_credential_id_idx\n  ON identity_credential_identifiers (identity_credential_id ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241108105000000001_index_cleanup.autocommit.up.sql",
    "content": "-- This index is replaced by identity_credential_identifiers_ici_nid_i_idx (included in the previous OEL release)\nDROP INDEX IF EXISTS identity_credential_identifiers_nid_ici_i_idx;\n\n-- This index is replaced by identity_credential_identifiers_ici_nid_i_idx (included in the previous OEL release)\nDROP INDEX IF EXISTS identity_credential_identifiers_identity_credential_id_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.down.sql",
    "content": "CREATE INDEX identity_credential_identifiers_nid_ici_i_idx\n  ON identity_credential_identifiers (nid ASC, identity_credential_id ASC, identifier ASC);\n\nCREATE INDEX identity_credential_identifiers_identity_credential_id_idx\n  ON identity_credential_identifiers (identity_credential_id ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241108105000000001_index_cleanup.mysql.up.sql",
    "content": "-- This index is replaced by identity_credential_identifiers_ici_nid_i_idx (included in the previous OEL release)\nDROP INDEX identity_credential_identifiers_nid_ici_i_idx ON identity_credential_identifiers;\n\n-- This index is replaced by identity_credential_identifiers_ici_nid_i_idx (included in the previous OEL release)\nDROP INDEX identity_credential_identifiers_identity_credential_id_idx ON identity_credential_identifiers;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20241203105600000000_saml_credential_type.down.sql",
    "content": "DELETE FROM identity_credential_types WHERE name = 'saml';"
  },
  {
    "path": "persistence/sql/migrations/sql/20241203105600000000_saml_credential_type.up.sql",
    "content": "INSERT INTO identity_credential_types (id, name)\nSELECT '7bddcf6c-f50e-4a18-9b0f-429114c33419', 'saml'\n    WHERE NOT EXISTS ( SELECT * FROM identity_credential_types WHERE name = 'saml');"
  },
  {
    "path": "persistence/sql/migrations/sql/20250505150900000000_code_address_type.autocommit.down.sql",
    "content": "ALTER TABLE identity_login_codes ALTER COLUMN address_type TYPE CHAR(36);\nALTER TABLE identity_registration_codes ALTER COLUMN address_type TYPE CHAR(36);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20250505150900000000_code_address_type.autocommit.up.sql",
    "content": "ALTER TABLE identity_login_codes ALTER COLUMN address_type TYPE VARCHAR(36);\nALTER TABLE identity_registration_codes ALTER COLUMN address_type TYPE VARCHAR(36);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20250505150900000000_code_address_type.mysql.down.sql",
    "content": "ALTER TABLE identity_login_codes MODIFY address_type CHAR(36);\nALTER TABLE identity_registration_codes MODIFY address_type CHAR(36);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20250505150900000000_code_address_type.mysql.up.sql",
    "content": "ALTER TABLE identity_login_codes MODIFY address_type VARCHAR(36);\nALTER TABLE identity_registration_codes MODIFY address_type VARCHAR(36);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20250505150900000000_code_address_type.sqlite.down.sql",
    "content": "ALTER TABLE identity_login_codes ADD COLUMN address_type_old CHAR(36);\nUPDATE identity_login_codes SET address_type_old = address_type;\nALTER TABLE identity_login_codes DROP COLUMN address_type;\nALTER TABLE identity_login_codes RENAME COLUMN address_type_old TO address_type;\n\nALTER TABLE identity_registration_codes ADD COLUMN address_type_old CHAR(36);\nUPDATE identity_registration_codes SET address_type_old = address_type;\nALTER TABLE identity_registration_codes DROP COLUMN address_type;\nALTER TABLE identity_registration_codes RENAME COLUMN address_type_old TO address_type;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20250505150900000000_code_address_type.sqlite.up.sql",
    "content": "ALTER TABLE identity_login_codes ADD COLUMN address_type_old VARCHAR(36);\nUPDATE identity_login_codes SET address_type_old = address_type;\nALTER TABLE identity_login_codes DROP COLUMN address_type;\nALTER TABLE identity_login_codes RENAME COLUMN address_type_old TO address_type;\n\nALTER TABLE identity_registration_codes ADD COLUMN address_type_old VARCHAR(36);\nUPDATE identity_registration_codes SET address_type_old = address_type;\nALTER TABLE identity_registration_codes DROP COLUMN address_type;\nALTER TABLE identity_registration_codes RENAME COLUMN address_type_old TO address_type;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20250708190000000000_identities_external_id.autocommit.down.sql",
    "content": "ALTER TABLE identities DROP COLUMN external_id;"
  },
  {
    "path": "persistence/sql/migrations/sql/20250708190000000000_identities_external_id.autocommit.up.sql",
    "content": "ALTER TABLE identities ADD COLUMN external_id VARCHAR(64) NULL CHECK (external_id IS NULL OR external_id != '');"
  },
  {
    "path": "persistence/sql/migrations/sql/20250708190000000000_identities_external_id.cockroach.autocommit.down.sql",
    "content": "ALTER TABLE identities DROP COLUMN IF EXISTS external_id;"
  },
  {
    "path": "persistence/sql/migrations/sql/20250708190000000000_identities_external_id.cockroach.autocommit.up.sql",
    "content": "ALTER TABLE identities ADD COLUMN IF NOT EXISTS external_id VARCHAR(64) NULL CHECK (external_id IS NULL OR external_id != '');"
  },
  {
    "path": "persistence/sql/migrations/sql/20250708190000000001_identities_external_id_index.cockroach.autocommit.up.sql",
    "content": "CREATE UNIQUE INDEX IF NOT EXISTS identities_nid_external_id_idx\n    ON identities (external_id, nid) USING HASH WHERE external_id IS NOT NULL AND external_id != '';"
  },
  {
    "path": "persistence/sql/migrations/sql/20250708190000000001_identities_external_id_index.down.sql",
    "content": "DROP INDEX IF EXISTS identities_nid_external_id_idx;"
  },
  {
    "path": "persistence/sql/migrations/sql/20250708190000000001_identities_external_id_index.mysql.down.sql",
    "content": "DROP INDEX identities_nid_external_id_idx ON identities;"
  },
  {
    "path": "persistence/sql/migrations/sql/20250708190000000001_identities_external_id_index.mysql.up.sql",
    "content": "CREATE UNIQUE INDEX identities_nid_external_id_idx\n    ON identities (nid, external_id);"
  },
  {
    "path": "persistence/sql/migrations/sql/20250708190000000001_identities_external_id_index.up.sql",
    "content": "CREATE UNIQUE INDEX IF NOT EXISTS identities_nid_external_id_idx\n    ON identities (nid, external_id) WHERE external_id IS NOT NULL;"
  },
  {
    "path": "persistence/sql/migrations/sql/20250710085000000000_add_schema_id.cockroach.autocommit.down.sql",
    "content": "ALTER TABLE selfservice_login_flows DROP COLUMN IF EXISTS identity_schema_id;\nALTER TABLE selfservice_registration_flows DROP COLUMN IF EXISTS identity_schema_id;"
  },
  {
    "path": "persistence/sql/migrations/sql/20250710085000000000_add_schema_id.cockroach.autocommit.up.sql",
    "content": "ALTER TABLE selfservice_login_flows ADD COLUMN IF NOT EXISTS identity_schema_id VARCHAR(128) NULL;\nALTER TABLE selfservice_registration_flows ADD COLUMN IF NOT EXISTS identity_schema_id VARCHAR(128) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20250710085000000000_add_schema_id.down.sql",
    "content": "ALTER TABLE selfservice_login_flows DROP COLUMN identity_schema_id;\nALTER TABLE selfservice_registration_flows DROP COLUMN identity_schema_id;"
  },
  {
    "path": "persistence/sql/migrations/sql/20250710085000000000_add_schema_id.up.sql",
    "content": "ALTER TABLE selfservice_login_flows ADD COLUMN identity_schema_id VARCHAR(128) NULL;\nALTER TABLE selfservice_registration_flows ADD COLUMN identity_schema_id VARCHAR(128) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251104000000000000_identifiers_devices_identity_id.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers DROP COLUMN identity_id;\nALTER TABLE session_devices DROP COLUMN identity_id;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251104000000000000_identifiers_devices_identity_id.mysql.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers ADD COLUMN identity_id char(36) NULL;\nALTER TABLE session_devices ADD COLUMN identity_id char(36) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251104000000000000_identifiers_devices_identity_id.sqlite.down.sql",
    "content": "-- For SQLite, we do all operations in a single migration for simplicity.\n\nCREATE TABLE \"_identity_credential_identifiers_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\n\"identity_credential_type_id\" char(36) NOT NULL,\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON UPDATE NO ACTION ON DELETE CASCADE\n);\n\nINSERT INTO _identity_credential_identifiers_tmp (id, identifier, identity_credential_id, created_at, updated_at, nid, identity_credential_type_id)\n    SELECT id, identifier, identity_credential_id, created_at, updated_at, nid, identity_credential_type_id\n    FROM identity_credential_identifiers;\n\nDROP TABLE identity_credential_identifiers;\nALTER TABLE \"_identity_credential_identifiers_tmp\" RENAME TO \"identity_credential_identifiers\";\n\n\nCREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_type_uq_idx\" ON \"identity_credential_identifiers\" (nid, identity_credential_type_id, identifier);\nCREATE INDEX identity_credential_identifiers_nid_i_ici_idx ON \"identity_credential_identifiers\" (nid, identifier, identity_credential_id);\nCREATE INDEX identity_credential_identifiers_ici_nid_i_idx ON \"identity_credential_identifiers\" (identity_credential_id ASC, nid ASC, identifier ASC);\n\n\nCREATE TABLE IF NOT EXISTS \"_session_devices_tmp\"\n(\n  \"id\"         UUID PRIMARY KEY NOT NULL,\n  \"ip_address\" VARCHAR(50)  DEFAULT '',\n  \"user_agent\" VARCHAR(512) DEFAULT '',\n  \"location\"   VARCHAR(512) DEFAULT '',\n  \"nid\"        UUID             NOT NULL,\n  \"session_id\" UUID             NOT NULL,\n  \"created_at\" timestamp        NOT NULL,\n  \"updated_at\" timestamp        NOT NULL,\n  CONSTRAINT \"session_metadata_sessions_id_fk\" FOREIGN KEY (\"session_id\") REFERENCES \"sessions\" (\"id\") ON DELETE cascade,\n  CONSTRAINT \"session_metadata_nid_fk\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON DELETE cascade,\n  CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent)\n);\n\nINSERT INTO \"_session_devices_tmp\" (id, ip_address, user_agent, location, nid, session_id, created_at, updated_at)\n    SELECT id, ip_address, user_agent, location, nid, session_id, created_at, updated_at\n    FROM session_devices;\n\nDROP TABLE session_devices;\nALTER TABLE \"_session_devices_tmp\" RENAME TO \"session_devices\";\n\nCREATE INDEX session_devices_nid_idx ON session_devices (nid ASC);\nCREATE INDEX session_devices_session_id_idx ON session_devices (session_id ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251104000000000000_identifiers_devices_identity_id.sqlite.up.sql",
    "content": "-- For SQLite, we do all operations in a single migration for simplicity.\n\nCREATE TABLE IF NOT EXISTS \"_identity_credential_identifiers_tmp\" (\n\"id\" TEXT PRIMARY KEY,\n\"identifier\" TEXT NOT NULL,\n\"identity_credential_id\" char(36) NOT NULL,\n\"created_at\" DATETIME NOT NULL,\n\"updated_at\" DATETIME NOT NULL,\n\"nid\" char(36),\n\"identity_credential_type_id\" char(36) NOT NULL,\n\"identity_id\" char(36) NOT NULL,\nFOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE,\nFOREIGN KEY (nid) REFERENCES networks (id) ON UPDATE RESTRICT ON DELETE CASCADE,\nFOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON UPDATE RESTRICT ON DELETE CASCADE,\nFOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON UPDATE RESTRICT ON DELETE CASCADE\n);\n\n\nINSERT INTO _identity_credential_identifiers_tmp (id, identifier, identity_credential_id, created_at, updated_at, nid, identity_credential_type_id, identity_id)\n    SELECT ici.id, ici.identifier, ici.identity_credential_id, ici.created_at, ici.updated_at, ici.nid, ici.identity_credential_type_id, ic.identity_id\n    FROM identity_credential_identifiers ici\n        INNER JOIN identity_credentials ic ON ici.identity_credential_id = ic.id AND ici.nid = ic.nid;\n\nDROP TABLE identity_credential_identifiers;\nALTER TABLE \"_identity_credential_identifiers_tmp\" RENAME TO \"identity_credential_identifiers\";\n\nCREATE UNIQUE INDEX \"identity_credential_identifiers_identifier_nid_type_uq_idx\" ON \"identity_credential_identifiers\" (nid, identity_credential_type_id, identifier);\nCREATE INDEX identity_credential_identifiers_nid_i_ici_idx ON \"identity_credential_identifiers\" (nid, identifier, identity_credential_id);\nCREATE INDEX identity_credential_identifiers_ici_nid_i_idx ON \"identity_credential_identifiers\" (identity_credential_id ASC, nid ASC, identifier ASC);\n\n\nCREATE TABLE IF NOT EXISTS \"_session_devices_tmp\"\n(\n  \"id\"         UUID PRIMARY KEY NOT NULL,\n  \"identity_id\" UUID NOT NULL,\n  \"ip_address\" VARCHAR(50)  DEFAULT '',\n  \"user_agent\" VARCHAR(512) DEFAULT '',\n  \"location\"   VARCHAR(512) DEFAULT '',\n  \"nid\"        UUID             NOT NULL,\n  \"session_id\" UUID             NOT NULL,\n  \"created_at\" timestamp        NOT NULL,\n  \"updated_at\" timestamp        NOT NULL,\n  CONSTRAINT \"session_metadata_sessions_id_fk\" FOREIGN KEY (\"session_id\") REFERENCES \"sessions\" (\"id\") ON DELETE cascade,\n  CONSTRAINT \"session_metadata_nid_fk\" FOREIGN KEY (\"nid\") REFERENCES \"networks\" (\"id\") ON DELETE cascade,\n  CONSTRAINT \"session_devices_identity_id_fk\" FOREIGN KEY (\"identity_id\") REFERENCES \"identities\" (\"id\") ON DELETE cascade,\n  CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent)\n);\n\nINSERT INTO \"_session_devices_tmp\" (id, identity_id, ip_address, user_agent, location, nid, session_id, created_at, updated_at)\n    SELECT sd.id, s.identity_id, sd.ip_address, sd.user_agent, sd.location, sd.nid, sd.session_id, sd.created_at, sd.updated_at\n    FROM session_devices sd JOIN sessions s ON sd.session_id = s.id;\n\nDROP TABLE session_devices;\nALTER TABLE \"_session_devices_tmp\" RENAME TO \"session_devices\";\n\nCREATE INDEX session_devices_nid_idx ON session_devices (nid ASC);\nCREATE INDEX session_devices_session_id_idx ON session_devices (session_id ASC);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251104000000000000_identifiers_devices_identity_id.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers ADD COLUMN identity_id UUID NULL;\nALTER TABLE session_devices ADD COLUMN identity_id UUID NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000003_identity_id_not_null_fks.cockroach.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers\n    ALTER identity_id SET NOT NULL,\n    ADD CONSTRAINT IF NOT EXISTS \"identity_credential_identifiers_identities_id_fk\" FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE;\n\n\nALTER TABLE session_devices\n    ALTER identity_id SET NOT NULL,\n    ADD CONSTRAINT IF NOT EXISTS \"session_devices_identities_id_fk\" FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000003_identity_id_not_null_fks.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers\n    DROP CONSTRAINT identity_credential_identifiers_identities_id_fk,\n    ALTER identity_id DROP NOT NULL;\n\nALTER TABLE session_devices\n    DROP CONSTRAINT session_devices_identities_id_fk,\n    ALTER identity_id DROP NOT NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000003_identity_id_not_null_fks.mysql.down.sql",
    "content": "ALTER TABLE identity_credential_identifiers\n    DROP CONSTRAINT `identity_credential_identifiers_identity_id_fk`,\n    MODIFY identity_id char(36) NULL;\n\nALTER TABLE session_devices\n    DROP CONSTRAINT `session_devices_identity_id_fk`,\n    MODIFY identity_id char(36) NULL;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000003_identity_id_not_null_fks.mysql.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers\n    MODIFY identity_id char(36) NOT NULL,\n    ADD CONSTRAINT `identity_credential_identifiers_identity_id_fk` FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE;\n\nALTER TABLE session_devices\n    MODIFY identity_id char(36) NOT NULL,\n    ADD CONSTRAINT `session_devices_identity_id_fk` FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000003_identity_id_not_null_fks.postgres.up.sql",
    "content": "ALTER TABLE identity_credential_identifiers\n    ALTER identity_id SET NOT NULL,\n    ADD CONSTRAINT \"identity_credential_identifiers_identities_id_fk\" FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE;\n\n\nALTER TABLE session_devices\n    ALTER identity_id SET NOT NULL,\n    ADD CONSTRAINT \"session_devices_identities_id_fk\" FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000003_identity_id_not_null_fks.sqlite.down.sql",
    "content": "-- Nothing, all done in 20251104000000000000_identifiers_devices_identity_id.sqlite.down.sql\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000003_identity_id_not_null_fks.sqlite.up.sql",
    "content": "-- Nothing, all done in 20251104000000000000_identifiers_devices_identity_id.sqlite.up.sql\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000004_identity_id_not_null_fks.cockroach.down.sql",
    "content": "DROP INDEX IF EXISTS identity_credential_identifiers@identity_credential_identifiers_identities_id_fk_idx;  \nDROP INDEX IF EXISTS session_devices@session_devices_identities_id_fk_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000004_identity_id_not_null_fks.cockroach.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credential_identifiers_identities_id_fk_idx ON identity_credential_identifiers (identity_id);\nCREATE INDEX IF NOT EXISTS session_devices_identities_id_fk_idx ON session_devices (identity_id);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000004_identity_id_not_null_fks.mysql.down.sql",
    "content": "-- Cannot drop index '...': needed in a foreign key constraint\n\n-- DROP INDEX identity_credential_identifiers_identity_id_fk_idx ON identity_credential_identifiers;\n-- DROP INDEX session_devices_identity_id_fk_idx ON session_devices;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000004_identity_id_not_null_fks.mysql.up.sql",
    "content": "CREATE INDEX identity_credential_identifiers_identity_id_fk_idx ON identity_credential_identifiers (identity_id);\nCREATE INDEX session_devices_identity_id_fk_idx ON session_devices (identity_id);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000004_identity_id_not_null_fks.postgres.down.sql",
    "content": "DROP INDEX IF EXISTS identity_credential_identifiers_identities_id_fk_idx;  \nDROP INDEX IF EXISTS session_devices_identities_id_fk_idx;\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000004_identity_id_not_null_fks.postgres.up.sql",
    "content": "CREATE INDEX IF NOT EXISTS identity_credential_identifiers_identities_id_fk_idx ON identity_credential_identifiers (identity_id);\nCREATE INDEX IF NOT EXISTS session_devices_identities_id_fk_idx ON session_devices (identity_id);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000004_identity_id_not_null_fks.sqlite.down.sql",
    "content": "-- Nothing, all done in 20251104000000000000_identifiers_devices_identity_id.sqlite.down.sql\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251105000000000004_identity_id_not_null_fks.sqlite.up.sql",
    "content": "-- Nothing, all done in 20251104000000000000_identifiers_devices_identity_id.sqlite.up.sql\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20251215000000000000_courier_messages_add_request_headers.down.sql",
    "content": "ALTER TABLE courier_messages DROP COLUMN IF EXISTS request_headers;"
  },
  {
    "path": "persistence/sql/migrations/sql/20251215000000000000_courier_messages_add_request_headers.mysql.down.sql",
    "content": "ALTER TABLE courier_messages DROP COLUMN request_headers;"
  },
  {
    "path": "persistence/sql/migrations/sql/20251215000000000000_courier_messages_add_request_headers.mysql.up.sql",
    "content": "ALTER TABLE courier_messages\n    ADD COLUMN request_headers JSON;"
  },
  {
    "path": "persistence/sql/migrations/sql/20251215000000000000_courier_messages_add_request_headers.sqlite.down.sql",
    "content": "ALTER TABLE courier_messages DROP COLUMN request_headers;"
  },
  {
    "path": "persistence/sql/migrations/sql/20251215000000000000_courier_messages_add_request_headers.sqlite.up.sql",
    "content": "ALTER TABLE courier_messages\n    ADD COLUMN request_headers JSONB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20251215000000000000_courier_messages_add_request_headers.up.sql",
    "content": "ALTER TABLE courier_messages\n    ADD COLUMN IF NOT EXISTS request_headers JSONB;"
  },
  {
    "path": "persistence/sql/migrations/sql/20260114175904000000_drop_identity_credentials_nid_idx.down.sql",
    "content": "CREATE INDEX identity_credentials_nid_idx ON identity_credentials (nid);\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20260114175904000000_drop_identity_credentials_nid_idx.mysql.down.sql",
    "content": "-- CREATE INDEX identity_credentials_nid_idx ON identity_credentials (nid);\n-- didn't actually drop it in the up migration because \"needed in a foreign key constraint\"\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20260114175904000000_drop_identity_credentials_nid_idx.mysql.up.sql",
    "content": "-- DROP INDEX identity_credentials_nid_idx ON identity_credentials;\n-- can't drop because \"needed in a foreign key constraint\"\n"
  },
  {
    "path": "persistence/sql/migrations/sql/20260114175904000000_drop_identity_credentials_nid_idx.up.sql",
    "content": "DROP INDEX IF EXISTS identity_credentials_nid_idx;\n"
  },
  {
    "path": "persistence/sql/persister.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"embed\"\n\t\"io/fs\"\n\t\"slices\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/sirupsen/logrus\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/persistence/sql/devices\"\n\tidpersistence \"github.com/ory/kratos/persistence/sql/identity\"\n\tgomigrations \"github.com/ory/kratos/persistence/sql/migrations/go\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/fsx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/networkx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/popx\"\n)\n\nvar _ persistence.Persister = new(Persister)\n\n//go:embed migrations/sql/*.sql\nvar Migrations embed.FS\n\ntype (\n\tpersisterDependencies interface {\n\t\tlogrusx.Provider\n\t\tconfig.Provider\n\t\tcontextx.Provider\n\t\totelx.Provider\n\t\tschema.IdentitySchemaProvider\n\t\tidentity.ValidationProvider\n\t}\n\tPersister struct {\n\t\tnid uuid.UUID\n\t\tc   *pop.Connection\n\t\tmb  *popx.MigrationBox\n\t\tmbs popx.MigrationStatuses\n\t\tr   persisterDependencies\n\n\t\tidentity.PrivilegedPool\n\t\tsession.DevicePersister\n\t}\n)\n\ntype options struct {\n\textraMigrations   []fs.FS\n\textraGoMigrations popx.Migrations\n\tdisableLogging    bool\n}\n\ntype Option = func(o *options)\n\nfunc WithExtraMigrations(fss ...fs.FS) Option {\n\treturn func(o *options) {\n\t\to.extraMigrations = fss\n\t}\n}\n\nfunc WithExtraGoMigrations(ms ...popx.Migration) Option {\n\treturn func(o *options) {\n\t\to.extraGoMigrations = ms\n\t}\n}\n\nfunc WithDisabledLogging(v bool) Option {\n\treturn func(o *options) {\n\t\to.disableLogging = v\n\t}\n}\n\nfunc NewPersister(r persisterDependencies, c *pop.Connection, opts ...Option) (*Persister, error) {\n\to := &options{}\n\tfor _, f := range opts {\n\t\tf(o)\n\t}\n\tlogger := r.Logger()\n\tif o.disableLogging {\n\t\tlogger.Logrus().SetLevel(logrus.WarnLevel)\n\t}\n\tm, err := popx.NewMigrationBox(\n\t\tfsx.Merge(append([]fs.FS{Migrations, networkx.Migrations}, o.extraMigrations...)...),\n\t\tc, logger,\n\t\tpopx.WithGoMigrations(slices.Concat(gomigrations.All, o.extraGoMigrations)),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Persister{\n\t\tc:               c,\n\t\tmb:              m,\n\t\tr:               r,\n\t\tPrivilegedPool:  idpersistence.NewPersister(r, c),\n\t\tDevicePersister: devices.NewPersister(r, c),\n\t}, nil\n}\n\nfunc (p *Persister) NetworkID(ctx context.Context) uuid.UUID {\n\treturn p.r.Contextualizer().Network(ctx, p.nid)\n}\n\nfunc (p Persister) WithNetworkID(nid uuid.UUID) persistence.Persister {\n\tp.nid = nid\n\tif pp, ok := p.PrivilegedPool.(interface {\n\t\tWithNetworkID(uuid.UUID) identity.PrivilegedPool\n\t}); ok {\n\t\tp.PrivilegedPool = pp.WithNetworkID(nid)\n\t}\n\tif dp, ok := p.DevicePersister.(interface {\n\t\tWithNetworkID(uuid.UUID) session.DevicePersister\n\t}); ok {\n\t\tp.DevicePersister = dp.WithNetworkID(nid)\n\t}\n\treturn &p\n}\n\nfunc (p *Persister) DetermineNetwork(ctx context.Context) (*networkx.Network, error) {\n\treturn networkx.Determine(p.Connection(ctx))\n}\n\nfunc (p *Persister) Connection(ctx context.Context) *pop.Connection {\n\treturn p.c.WithContext(ctx)\n}\n\nfunc (p *Persister) MigrationStatus(ctx context.Context) (_ popx.MigrationStatuses, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.MigrationStatus\")\n\tdefer otelx.End(span, &err)\n\n\tif p.mbs != nil {\n\t\treturn p.mbs, nil\n\t}\n\n\tstatus, err := p.mb.Status(ctx)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif !status.HasPending() {\n\t\tp.mbs = status\n\t}\n\n\treturn status, nil\n}\n\nfunc (p *Persister) MigrateDown(ctx context.Context, steps int) error {\n\treturn p.mb.Down(ctx, steps)\n}\n\nfunc (p *Persister) MigrateUp(ctx context.Context) error {\n\treturn p.mb.Up(ctx)\n}\n\nfunc (p *Persister) MigrationBox() *popx.MigrationBox {\n\treturn p.mb\n}\n\nfunc (p *Persister) Close(ctx context.Context) error {\n\treturn errors.WithStack(p.GetConnection(ctx).Close())\n}\n\nfunc (p *Persister) Ping(ctx context.Context) error {\n\treturn errors.WithStack(p.c.Store.SQLDB().PingContext(ctx))\n}\n\nfunc (p *Persister) CleanupDatabase(ctx context.Context, wait time.Duration, older time.Duration, batchSize int) error {\n\tcurrentTime := time.Now().Add(-older)\n\tp.r.Logger().Printf(\"Cleaning up records older than %s\\n\", currentTime)\n\n\tp.r.Logger().Println(\"Cleaning up expired sessions\")\n\tif err := p.DeleteExpiredSessions(ctx, currentTime, batchSize); err != nil {\n\t\treturn err\n\t}\n\ttime.Sleep(wait)\n\n\tp.r.Logger().Println(\"Cleaning up expired continuity containers\")\n\tif err := p.DeleteExpiredContinuitySessions(ctx, currentTime, batchSize); err != nil {\n\t\treturn err\n\t}\n\ttime.Sleep(wait)\n\n\tp.r.Logger().Println(\"Cleaning up expired login flows\")\n\tif err := p.DeleteExpiredLoginFlows(ctx, currentTime, batchSize); err != nil {\n\t\treturn err\n\t}\n\ttime.Sleep(wait)\n\n\tp.r.Logger().Println(\"Cleaning up expired recovery flows\")\n\tif err := p.DeleteExpiredRecoveryFlows(ctx, currentTime, batchSize); err != nil {\n\t\treturn err\n\t}\n\ttime.Sleep(wait)\n\n\tp.r.Logger().Println(\"Cleaning up expired registration flows\")\n\tif err := p.DeleteExpiredRegistrationFlows(ctx, currentTime, batchSize); err != nil {\n\t\treturn err\n\t}\n\ttime.Sleep(wait)\n\n\tp.r.Logger().Println(\"Cleaning up expired settings flows\")\n\tif err := p.DeleteExpiredSettingsFlows(ctx, currentTime, batchSize); err != nil {\n\t\treturn err\n\t}\n\ttime.Sleep(wait)\n\n\tp.r.Logger().Println(\"Cleaning up expired verification flows\")\n\tif err := p.DeleteExpiredVerificationFlows(ctx, currentTime, batchSize); err != nil {\n\t\treturn err\n\t}\n\ttime.Sleep(wait)\n\n\tp.r.Logger().Println(\"Cleaning up expired session token exchangers\")\n\tif err := p.DeleteExpiredExchangers(ctx, currentTime, batchSize); err != nil {\n\t\treturn err\n\t}\n\ttime.Sleep(wait)\n\n\tp.r.Logger().Println(\"Successfully cleaned up the latest batch of the SQL database! \" +\n\t\t\"This should be re-run periodically, to be sure that all expired data is purged.\")\n\treturn nil\n}\n"
  },
  {
    "path": "persistence/sql/persister_cleanup_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestPersister_Cleanup(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\tctx := context.Background()\n\n\tt.Run(\"case=should not throw error on cleanup\", func(t *testing.T) {\n\t\tassert.Nil(t, p.CleanupDatabase(ctx, 0, 0, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n\n\tt.Run(\"case=should throw error on cleanup\", func(t *testing.T) {\n\t\trequire.NoError(t, p.GetConnection(ctx).Close())\n\t\tassert.Error(t, p.CleanupDatabase(ctx, 0, 0, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n}\n\nfunc TestPersister_Continuity_Cleanup(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\tcurrentTime := time.Now()\n\tctx := context.Background()\n\n\tt.Run(\"case=should not throw error on cleanup continuity sessions\", func(t *testing.T) {\n\t\tassert.Nil(t, p.DeleteExpiredContinuitySessions(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n\n\tt.Run(\"case=should throw error on cleanup continuity sessions\", func(t *testing.T) {\n\t\trequire.NoError(t, p.GetConnection(ctx).Close())\n\t\tassert.Error(t, p.DeleteExpiredContinuitySessions(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n}\n\nfunc TestPersister_Login_Cleanup(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\tcurrentTime := time.Now()\n\tctx := context.Background()\n\n\tt.Run(\"case=should not throw error on cleanup login flows\", func(t *testing.T) {\n\t\tassert.Nil(t, p.DeleteExpiredLoginFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n\n\tt.Run(\"case=should throw error on cleanup login flows\", func(t *testing.T) {\n\t\trequire.NoError(t, p.GetConnection(ctx).Close())\n\t\tassert.Error(t, p.DeleteExpiredLoginFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n}\n\nfunc TestPersister_Recovery_Cleanup(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\tcurrentTime := time.Now()\n\tctx := context.Background()\n\n\tt.Run(\"case=should not throw error on cleanup recovery flows\", func(t *testing.T) {\n\t\tassert.Nil(t, p.DeleteExpiredRecoveryFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n\n\tt.Run(\"case=should throw error on cleanup recovery flows\", func(t *testing.T) {\n\t\trequire.NoError(t, p.GetConnection(ctx).Close())\n\t\tassert.Error(t, p.DeleteExpiredRecoveryFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n}\n\nfunc TestPersister_Registration_Cleanup(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\tcurrentTime := time.Now()\n\tctx := context.Background()\n\n\tt.Run(\"case=should not throw error on cleanup registration flows\", func(t *testing.T) {\n\t\tassert.Nil(t, p.DeleteExpiredRegistrationFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n\n\tt.Run(\"case=should throw error on cleanup registration flows\", func(t *testing.T) {\n\t\trequire.NoError(t, p.GetConnection(ctx).Close())\n\t\tassert.Error(t, p.DeleteExpiredRegistrationFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n}\n\nfunc TestPersister_Session_Cleanup(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\tcurrentTime := time.Now()\n\tctx := context.Background()\n\n\tt.Run(\"case=should not throw error on cleanup sessions\", func(t *testing.T) {\n\t\tassert.Nil(t, p.DeleteExpiredSessions(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n\n\tt.Run(\"case=should throw error on cleanup sessions\", func(t *testing.T) {\n\t\trequire.NoError(t, p.GetConnection(ctx).Close())\n\t\tassert.Error(t, p.DeleteExpiredSessions(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n}\n\nfunc TestPersister_Settings_Cleanup(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\tcurrentTime := time.Now()\n\tctx := context.Background()\n\n\tt.Run(\"case=should not throw error on cleanup setting flows\", func(t *testing.T) {\n\t\tassert.Nil(t, p.DeleteExpiredSettingsFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n\n\tt.Run(\"case=should throw error on cleanup setting flows\", func(t *testing.T) {\n\t\trequire.NoError(t, p.GetConnection(ctx).Close())\n\t\tassert.Error(t, p.DeleteExpiredSettingsFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n}\n\nfunc TestPersister_Verification_Cleanup(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\tcurrentTime := time.Now()\n\tctx := context.Background()\n\n\tt.Run(\"case=should not throw error on cleanup verification flows\", func(t *testing.T) {\n\t\tassert.Nil(t, p.DeleteExpiredVerificationFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n\n\tt.Run(\"case=should throw error on cleanup verification flows\", func(t *testing.T) {\n\t\trequire.NoError(t, p.GetConnection(ctx).Close())\n\t\tassert.Error(t, p.DeleteExpiredVerificationFlows(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n}\n\nfunc TestPersister_SessionTokenExchange_Cleanup(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\tcurrentTime := time.Now()\n\tctx := context.Background()\n\n\tt.Run(\"case=should not throw error on cleanup session token exchangers\", func(t *testing.T) {\n\t\tassert.Nil(t, p.DeleteExpiredExchangers(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n\n\tt.Run(\"case=should throw error on cleanup session token exchangers if DB is closed\", func(t *testing.T) {\n\t\trequire.NoError(t, p.GetConnection(ctx).Close())\n\t\tassert.Error(t, p.DeleteExpiredExchangers(ctx, currentTime, reg.Config().DatabaseCleanupBatchSize(ctx)))\n\t})\n}\n"
  },
  {
    "path": "persistence/sql/persister_code.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"crypto/subtle\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\ntype oneTimeCodeProvider interface {\n\tGetID() uuid.UUID\n\tValidate() error\n\tTableName(ctx context.Context) string\n\tGetHMACCode() string\n}\n\ntype codeOptions struct {\n\tIdentityID *uuid.UUID\n}\n\ntype codeOption func(o *codeOptions)\n\nfunc withCheckIdentityID(id uuid.UUID) codeOption {\n\treturn func(o *codeOptions) {\n\t\to.IdentityID = &id\n\t}\n}\n\nfunc useOneTimeCode[P any, U interface {\n\t*P\n\toneTimeCodeProvider\n}](ctx context.Context, p *Persister, flowID uuid.UUID, userProvidedCode, flowTableName, foreignKeyName string, opts ...codeOption,\n) (target U, err error) {\n\tmaxSubmissions := p.r.Config().SelfServiceCodeMethodMaxSubmissions(ctx)\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.useOneTimeCode\", trace.WithAttributes(attribute.Int(\"max_submissions\", maxSubmissions)))\n\tdefer otelx.End(span, &err)\n\n\to := new(codeOptions)\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\n\t// Before we do anything else, increment the submit count and check if we're\n\t// being brute-forced. This is a separate statement/transaction to the rest\n\t// of the operations so that it is correct for all transaction isolation\n\t// levels.\n\tsubmitCount, err := incrementOTPCodeSubmitCount(ctx, p, flowID, flowTableName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif submitCount > maxSubmissions {\n\t\treturn nil, errors.WithStack(code.ErrCodeSubmittedTooOften)\n\t}\n\n\tnid := p.NetworkID(ctx)\n\n\tif err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {\n\t\tvar codes []U\n\t\tcodesQuery := tx.Where(fmt.Sprintf(\"nid = ? AND %s = ?\", foreignKeyName), nid, flowID)\n\t\tif o.IdentityID != nil {\n\t\t\tcodesQuery = codesQuery.Where(\"identity_id = ?\", *o.IdentityID)\n\t\t}\n\n\t\tif err := sqlcon.HandleError(codesQuery.All(&codes)); err != nil {\n\t\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\t\treturn errors.WithStack(code.ErrCodeNotFound)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\tsecrets:\n\t\tfor _, secret := range p.r.Config().SecretsSession(ctx) {\n\t\t\tsuppliedCode := []byte(hmacValueWithSecret(userProvidedCode, secret))\n\t\t\tfor i := range codes {\n\t\t\t\tc := codes[i]\n\t\t\t\tif subtle.ConstantTimeCompare([]byte(c.GetHMACCode()), suppliedCode) == 0 {\n\t\t\t\t\t// Not the supplied code\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttarget = c\n\t\t\t\tbreak secrets\n\t\t\t}\n\t\t}\n\n\t\tif target.Validate() != nil {\n\t\t\t// Return no error, as that would roll back the transaction. We re-validate the code after the transaction.\n\t\t\treturn nil\n\t\t}\n\n\t\t//#nosec G201 -- TableName is static\n\t\treturn tx.RawQuery(fmt.Sprintf(\"UPDATE %s SET used_at = ? WHERE id = ? AND nid = ?\", target.TableName(ctx)), time.Now().UTC(), target.GetID(), nid).Exec()\n\t}); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\tif err := target.Validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn target, nil\n}\n\nfunc incrementOTPCodeSubmitCount(ctx context.Context, p *Persister, flowID uuid.UUID, flowTableName string) (submitCount int, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.incrementOTPCodeSubmitCount\",\n\t\ttrace.WithAttributes(attribute.Stringer(\"flow_id\", flowID), attribute.String(\"flow_table_name\", flowTableName)))\n\tdefer otelx.End(span, &err)\n\tdefer func() {\n\t\tspan.SetAttributes(attribute.Int(\"submit_count\", submitCount))\n\t}()\n\n\tnid := p.NetworkID(ctx)\n\n\t// The branch below is a marginal performance optimization for databases\n\t// supporting RETURNING (one query instead of two). There is no real\n\t// security difference here, but there is an observable difference in\n\t// behavior.\n\t//\n\t// Databases supporting RETURNING will perform the increment+select\n\t// atomically. That means that always exactly 5 attempts will be allowed for\n\t// each flow, no matter how many concurrent attempts are made.\n\t//\n\t// Databases without support for RETURNING (MySQL) will perform the UPDATE\n\t// and SELECT in two queries, which are not atomic. The effect is that there\n\t// will still never be more than 5 attempts for each flow, but there may be\n\t// fewer before we reject. Under normal operation, this is never a problem\n\t// because a human will never submit their code as quickly as would be\n\t// required to trigger this race condition.\n\t//\n\t// In a very strict sense of the word, the MySQL implementation is even more\n\t// secure than the RETURNING implementation. But we're ok either way :)\n\tif p.c.Dialect.Name() == \"mysql\" {\n\t\t//#nosec G201 -- TableName is static\n\t\tqUpdate := fmt.Sprintf(\"UPDATE %s SET submit_count = submit_count + 1 WHERE id = ? AND nid = ?\", flowTableName)\n\t\tif err := p.GetConnection(ctx).RawQuery(qUpdate, flowID, nid).Exec(); err != nil {\n\t\t\treturn 0, sqlcon.HandleError(err)\n\t\t}\n\t\t//#nosec G201 -- TableName is static\n\t\tqSelect := fmt.Sprintf(\"SELECT submit_count FROM %s WHERE id = ? AND nid = ?\", flowTableName)\n\t\terr = sqlcon.HandleError(p.GetConnection(ctx).RawQuery(qSelect, flowID, nid).First(&submitCount))\n\t} else {\n\t\t//#nosec G201 -- TableName is static\n\t\tq := fmt.Sprintf(\"UPDATE %s SET submit_count = submit_count + 1 WHERE id = ? AND nid = ? RETURNING submit_count\", flowTableName)\n\t\terr = sqlcon.HandleError(p.Connection(ctx).RawQuery(q, flowID, nid).First(&submitCount))\n\t}\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\treturn 0, errors.WithStack(code.ErrCodeNotFound)\n\t}\n\n\treturn submitCount, err\n}\n"
  },
  {
    "path": "persistence/sql/persister_continuity.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/kratos/continuity\"\n)\n\nvar _ continuity.Persister = new(Persister)\n\nfunc (p *Persister) SaveContinuitySession(ctx context.Context, c *continuity.Container) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.SaveContinuitySession\")\n\tdefer otelx.End(span, &err)\n\n\tc.NID = p.NetworkID(ctx)\n\treturn sqlcon.HandleError(p.GetConnection(ctx).Create(c))\n}\n\nfunc (p *Persister) SetContinuitySessionExpiry(ctx context.Context, id uuid.UUID, expiresAt time.Time) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.SetContinuitySessionExpiry\")\n\tdefer otelx.End(span, &err)\n\n\tif rows, err := p.GetConnection(ctx).\n\t\tWhere(\"id = ? AND nid = ?\", id, p.NetworkID(ctx)).\n\t\tUpdateQuery(&continuity.Container{\n\t\t\tExpiresAt: expiresAt,\n\t\t}, \"expires_at\"); err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t} else if rows == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\n\treturn nil\n}\n\nfunc (p *Persister) GetContinuitySession(ctx context.Context, id uuid.UUID) (_ *continuity.Container, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetContinuitySession\")\n\tdefer otelx.End(span, &err)\n\n\tvar c continuity.Container\n\tif err := p.GetConnection(ctx).Where(\"id = ? AND nid = ?\", id, p.NetworkID(ctx)).First(&c); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\treturn &c, nil\n}\n\nfunc (p *Persister) DeleteContinuitySession(ctx context.Context, id uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteContinuitySession\")\n\tdefer otelx.End(span, &err)\n\n\tif count, err := p.GetConnection(ctx).RawQuery(\n\t\t//#nosec G201 -- TableName is static\n\t\tfmt.Sprintf(\"DELETE FROM %s WHERE id=? AND nid=?\",\n\t\t\tcontinuity.Container{}.TableName()), id, p.NetworkID(ctx)).ExecWithCount(); err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t} else if count == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\treturn nil\n}\n\nfunc (p *Persister) DeleteExpiredContinuitySessions(ctx context.Context, expiresAt time.Time, limit int) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteExpiredContinuitySessions\")\n\tdefer otelx.End(span, &err)\n\t//#nosec G201 -- TableName is static\n\terr = p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %[1]s WHERE id in (SELECT id FROM (SELECT id FROM %[1]s c WHERE expires_at <= ? and nid = ? ORDER BY expires_at ASC LIMIT ?) AS s)\",\n\t\tcontinuity.Container{}.TableName(),\n\t),\n\t\texpiresAt,\n\t\tp.NetworkID(ctx),\n\t\tlimit,\n\t).Exec()\n\n\treturn sqlcon.HandleError(err)\n}\n"
  },
  {
    "path": "persistence/sql/persister_courier.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/persistence/sql/update\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/otelx\"\n\tkeysetpagination \"github.com/ory/x/pagination/keysetpagination_v2\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/uuidx\"\n)\n\nvar _ courier.Persister = new(Persister)\n\nfunc (p *Persister) AddMessage(ctx context.Context, m *courier.Message) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.AddMessage\")\n\tdefer otelx.End(span, &err)\n\n\tm.NID = p.NetworkID(ctx)\n\tm.Status = courier.MessageStatusQueued\n\treturn sqlcon.HandleError(p.GetConnection(ctx).Create(m)) // do not create eager to avoid identity injection.\n}\n\nfunc (p *Persister) ListMessages(ctx context.Context, filter courier.ListCourierMessagesParameters, opts []keysetpagination.Option) (_ []courier.Message, _ *keysetpagination.Paginator, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ListMessages\")\n\tdefer otelx.End(span, &err)\n\n\tq := p.GetConnection(ctx).Where(\"nid=?\", p.NetworkID(ctx))\n\n\tif filter.Status != nil {\n\t\tq = q.Where(\"status=?\", *filter.Status)\n\t}\n\n\tif filter.Recipient != \"\" {\n\t\tq = q.Where(\"recipient=?\", filter.Recipient)\n\t}\n\n\topts = append(opts, keysetpagination.WithDefaultToken(courier.Message{}.DefaultPageToken()))\n\topts = append(opts, keysetpagination.WithDefaultSize(10))\n\tpaginator, err := keysetpagination.NewPaginator(opts...)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tmessages := make([]courier.Message, paginator.Size())\n\tif err := q.Scope(keysetpagination.Paginate[courier.Message](paginator)).\n\t\tAll(&messages); err != nil {\n\t\treturn nil, nil, sqlcon.HandleError(err)\n\t}\n\n\tmessages, nextPage := keysetpagination.Result(messages, paginator)\n\n\treturn messages, nextPage, nil\n}\n\nfunc (p *Persister) NextMessages(ctx context.Context, limit uint8) (messages []courier.Message, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.NextMessages\")\n\tdefer otelx.End(span, &err)\n\n\tif err := p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {\n\t\tvar m []courier.Message\n\t\tif err := tx.\n\t\t\tWhere(\"nid = ? AND status = ?\",\n\t\t\t\tp.NetworkID(ctx),\n\t\t\t\tcourier.MessageStatusQueued,\n\t\t\t).\n\t\t\tOrder(\"created_at ASC\").\n\t\t\tLimit(int(limit)).\n\t\t\tAll(&m); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(m) == 0 {\n\t\t\treturn sql.ErrNoRows\n\t\t}\n\n\t\tfor i := range m {\n\t\t\tmessage := &m[i]\n\t\t\tmessage.Status = courier.MessageStatusProcessing\n\t\t\tif err := update.Generic(ctx, p.GetConnection(ctx), p.r.Tracer(ctx).Tracer(), message, \"status\"); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tmessages = m\n\t\treturn nil\n\t}); err != nil {\n\t\tif errors.Cause(err) == sql.ErrNoRows {\n\t\t\treturn nil, errors.WithStack(courier.ErrQueueEmpty)\n\t\t}\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\tif len(messages) == 0 {\n\t\treturn nil, errors.WithStack(courier.ErrQueueEmpty)\n\t}\n\n\treturn messages, nil\n}\n\nfunc (p *Persister) LatestQueuedMessage(ctx context.Context) (_ *courier.Message, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.LatestQueuedMessage\")\n\tdefer otelx.End(span, &err)\n\n\tvar m courier.Message\n\tif err := p.GetConnection(ctx).\n\t\tWhere(\"nid = ? AND status = ?\",\n\t\t\tp.NetworkID(ctx),\n\t\t\tcourier.MessageStatusQueued,\n\t\t).\n\t\tOrder(\"created_at DESC\").\n\t\tFirst(&m); err != nil {\n\t\tif errors.Is(err, sql.ErrNoRows) {\n\t\t\treturn nil, errors.WithStack(courier.ErrQueueEmpty)\n\t\t}\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &m, nil\n}\n\nfunc (p *Persister) SetMessageStatus(ctx context.Context, id uuid.UUID, ms courier.MessageStatus) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.SetMessageStatus\")\n\tdefer otelx.End(span, &err)\n\n\tcount, err := p.GetConnection(ctx).RawQuery(\n\t\t\"UPDATE courier_messages SET status = ? WHERE id = ? AND nid = ?\",\n\t\tms,\n\t\tid,\n\t\tp.NetworkID(ctx),\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\n\tif count == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\n\treturn nil\n}\n\nfunc (p *Persister) IncrementMessageSendCount(ctx context.Context, id uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.IncrementMessageSendCount\")\n\tdefer otelx.End(span, &err)\n\n\tcount, err := p.GetConnection(ctx).RawQuery(\n\t\t\"UPDATE courier_messages SET send_count = send_count + 1 WHERE id = ? AND nid = ?\",\n\t\tid,\n\t\tp.NetworkID(ctx),\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\n\tif count == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\n\treturn nil\n}\n\nfunc (p *Persister) FetchMessage(ctx context.Context, msgID uuid.UUID) (_ *courier.Message, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.FetchMessage\")\n\tdefer otelx.End(span, &err)\n\n\tvar message courier.Message\n\tif err := p.GetConnection(ctx).\n\t\tWhere(\"id = ? AND nid = ?\", msgID, p.NetworkID(ctx)).\n\t\tEager().\n\t\tFirst(&message); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &message, nil\n}\n\nfunc (p *Persister) RecordDispatch(ctx context.Context, msgID uuid.UUID, status courier.CourierMessageDispatchStatus, err error) error {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.RecordDispatch\")\n\tdefer otelx.End(span, &err)\n\n\tdispatch := courier.MessageDispatch{\n\t\tID:        uuidx.NewV4(),\n\t\tMessageID: msgID,\n\t\tStatus:    status,\n\t\tNID:       p.NetworkID(ctx),\n\t}\n\n\tif err != nil {\n\t\t// We use herodot as a carrier for the error's data\n\t\ther := herodot.ToDefaultError(err, \"\")\n\t\tcontent, mErr := json.Marshal(her)\n\t\tif mErr != nil {\n\t\t\treturn errors.WithStack(mErr)\n\t\t}\n\t\tdispatch.Error = content\n\t}\n\n\tif err := p.GetConnection(ctx).Create(&dispatch); err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "persistence/sql/persister_errorx.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nvar _ errorx.Persister = new(Persister)\n\nfunc (p *Persister) CreateErrorContainer(ctx context.Context, csrfToken string, errs error) (containerID uuid.UUID, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateErrorContainer\")\n\tdefer otelx.End(span, &err)\n\n\tmessage, err := encodeSelfServiceErrors(errs)\n\tif err != nil {\n\t\treturn uuid.Nil, err\n\t}\n\n\tc := &errorx.ErrorContainer{\n\t\tID:        uuid.Nil,\n\t\tNID:       p.NetworkID(ctx),\n\t\tCSRFToken: csrfToken,\n\t\tErrors:    message,\n\t\tWasSeen:   false,\n\t}\n\n\tif err := p.GetConnection(ctx).Create(c); err != nil {\n\t\treturn uuid.Nil, sqlcon.HandleError(err)\n\t}\n\n\tspan.SetAttributes(attribute.String(\"id\", c.ID.String()))\n\n\treturn c.ID, nil\n}\n\nfunc (p *Persister) ReadErrorContainer(ctx context.Context, id uuid.UUID) (_ *errorx.ErrorContainer, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ReadErrorContainer\")\n\tdefer otelx.End(span, &err)\n\n\tvar ec errorx.ErrorContainer\n\tif err := p.Transaction(ctx, func(ctx context.Context, c *pop.Connection) error {\n\t\tif err := c.Where(\"id = ? AND nid = ?\", id, p.NetworkID(ctx)).First(&ec); err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\n\t\tif err := c.RawQuery(\n\t\t\t\"UPDATE selfservice_errors SET was_seen = true, seen_at = ? WHERE id = ? AND nid = ?\",\n\t\t\ttime.Now().UTC(), id, p.NetworkID(ctx)).Exec(); err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &ec, nil\n}\n\nfunc (p *Persister) ClearErrorContainers(ctx context.Context, olderThan time.Duration, force bool) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ClearErrorContainers\")\n\tdefer otelx.End(span, &err)\n\n\tif force {\n\t\terr = p.GetConnection(ctx).RawQuery(\n\t\t\t\"DELETE FROM selfservice_errors WHERE nid = ? AND seen_at < ? AND seen_at IS NOT NULL\",\n\t\t\tp.NetworkID(ctx), time.Now().UTC().Add(-olderThan)).Exec()\n\t} else {\n\t\terr = p.GetConnection(ctx).RawQuery(\n\t\t\t\"DELETE FROM selfservice_errors WHERE nid = ? AND was_seen=true AND seen_at < ? AND seen_at IS NOT NULL\",\n\t\t\tp.NetworkID(ctx), time.Now().UTC().Add(-olderThan)).Exec()\n\t}\n\n\treturn sqlcon.HandleError(err)\n}\n\nfunc encodeSelfServiceErrors(e error) ([]byte, error) {\n\tif e == nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithDebug(\"A nil error was passed to the error manager which is most likely a code bug.\"))\n\t}\n\n\tif c := new(herodot.DefaultError); errors.As(e, &c) {\n\t\te = c\n\t} else if c := new(jsonschema.ValidationError); errors.As(e, &c) {\n\t\te = c\n\t} else {\n\t\te = herodot.ToDefaultError(e, \"\")\n\t}\n\n\tenc, err := json.Marshal(e)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Unable to encode error messages.\").WithDebug(err.Error()))\n\t}\n\n\treturn enc, nil\n}\n"
  },
  {
    "path": "persistence/sql/persister_hmac.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"crypto/hmac\"\n\t\"crypto/sha512\"\n\t\"fmt\"\n)\n\nfunc (p *Persister) hmacValue(ctx context.Context, value string) string {\n\treturn hmacValueWithSecret(value, p.r.Config().SecretsSession(ctx)[0])\n}\n\nfunc hmacValueWithSecret(value string, secret []byte) string {\n\th := hmac.New(sha512.New512_256, secret)\n\t_, _ = h.Write([]byte(value))\n\treturn fmt.Sprintf(\"%x\", h.Sum(nil))\n}\n"
  },
  {
    "path": "persistence/sql/persister_hmac_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/embedx\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype logRegistryOnly struct {\n\tl *logrusx.Logger\n\tc *config.Config\n}\n\nfunc (l *logRegistryOnly) Config() *config.Config {\n\treturn l.c\n}\n\nfunc (l *logRegistryOnly) Contextualizer() contextx.Contextualizer {\n\t// TODO implement me\n\tpanic(\"implement me\")\n}\n\nfunc (l *logRegistryOnly) Logger() *logrusx.Logger {\n\tif l.l == nil {\n\t\tl.l = logrusx.New(\"kratos\", \"testing\")\n\t}\n\treturn l.l\n}\n\nfunc (l *logRegistryOnly) Audit() *logrusx.Logger {\n\tpanic(\"implement me\")\n}\n\nfunc (l *logRegistryOnly) Tracer(context.Context) *otelx.Tracer {\n\treturn otelx.NewNoop()\n}\n\nfunc (l *logRegistryOnly) IdentityTraitsSchemas(context.Context) (schema.IdentitySchemaList, error) {\n\tpanic(\"implement me\")\n}\n\nfunc (l *logRegistryOnly) IdentityValidator() *identity.Validator {\n\tpanic(\"implement me\")\n}\n\nvar _ persisterDependencies = &logRegistryOnly{}\n\nfunc TestPersisterHMAC(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tbaseSecret := \"foobarbaz\"\n\tbaseSecretBytes := []byte(baseSecret)\n\topts := []configx.OptionModifier{configx.SkipValidation(), configx.WithValue(config.ViperKeySecretsDefault, []string{baseSecret})}\n\tconf := config.MustNew(t, logrusx.New(\"\", \"\"), contextx.NewTestConfigProvider(embedx.ConfigSchema, opts...), opts...)\n\tc, err := pop.NewConnection(&pop.ConnectionDetails{URL: \"sqlite://foo?mode=memory\"})\n\trequire.NoError(t, err)\n\tp, err := NewPersister(&logRegistryOnly{c: conf}, c)\n\trequire.NoError(t, err)\n\n\tt.Run(\"case=behaves deterministically\", func(t *testing.T) {\n\t\tassert.Equal(t, hmacValueWithSecret(\"hashme\", baseSecretBytes), p.hmacValue(ctx, \"hashme\"))\n\t\tassert.NotEqual(t, hmacValueWithSecret(\"notme\", baseSecretBytes), p.hmacValue(ctx, \"hashme\"))\n\t\tassert.NotEqual(t, hmacValueWithSecret(\"hashme\", baseSecretBytes), p.hmacValue(ctx, \"notme\"))\n\t})\n\n\thash := p.hmacValue(ctx, \"hashme\")\n\tnewSecret := \"not\" + baseSecret\n\n\tt.Run(\"case=with only new sectet\", func(t *testing.T) {\n\t\tctx = contextx.WithConfigValue(ctx, config.ViperKeySecretsDefault, []string{newSecret})\n\t\tassert.NotEqual(t, hmacValueWithSecret(\"hashme\", baseSecretBytes), p.hmacValue(ctx, \"hashme\"))\n\t\tassert.Equal(t, hmacValueWithSecret(\"hashme\", []byte(newSecret)), p.hmacValue(ctx, \"hashme\"))\n\t})\n\n\tt.Run(\"case=with new and old secret\", func(t *testing.T) {\n\t\tctx = contextx.WithConfigValue(ctx, config.ViperKeySecretsDefault, []string{newSecret, baseSecret})\n\t\tassert.Equal(t, hmacValueWithSecret(\"hashme\", []byte(newSecret)), p.hmacValue(ctx, \"hashme\"))\n\t\tassert.NotEqual(t, hash, p.hmacValue(ctx, \"hashme\"))\n\t})\n}\n"
  },
  {
    "path": "persistence/sql/persister_login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/persistence/sql/update\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nvar _ login.FlowPersister = new(Persister)\n\nfunc (p *Persister) CreateLoginFlow(ctx context.Context, r *login.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateLoginFlow\")\n\tdefer otelx.End(span, &err)\n\n\tr.NID = p.NetworkID(ctx)\n\tr.EnsureInternalContext()\n\treturn p.GetConnection(ctx).Create(r)\n}\n\nfunc (p *Persister) UpdateLoginFlow(ctx context.Context, r *login.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpdateLoginFlow\")\n\tdefer otelx.End(span, &err)\n\n\tr.EnsureInternalContext()\n\tcp := *r\n\tcp.NID = p.NetworkID(ctx)\n\treturn update.Generic(ctx, p.GetConnection(ctx), p.r.Tracer(ctx).Tracer(), cp)\n}\n\nfunc (p *Persister) GetLoginFlow(ctx context.Context, id uuid.UUID) (_ *login.Flow, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetLoginFlow\")\n\tdefer otelx.End(span, &err)\n\n\tconn := p.GetConnection(ctx)\n\n\tvar r login.Flow\n\tif err := conn.Where(\"id = ? AND nid = ?\", id, p.NetworkID(ctx)).First(&r); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &r, nil\n}\n\nfunc (p *Persister) ForceLoginFlow(ctx context.Context, id uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ForceLoginFlow\")\n\tdefer otelx.End(span, &err)\n\n\treturn p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error {\n\t\tlr, err := p.GetLoginFlow(ctx, id)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tlr.Refresh = true\n\t\treturn tx.Save(lr, \"nid\")\n\t})\n}\n\nfunc (p *Persister) DeleteExpiredLoginFlows(ctx context.Context, expiresAt time.Time, limit int) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteExpiredLoginFlows\")\n\tdefer otelx.End(span, &err)\n\t//#nosec G201 -- TableName is static\n\terr = p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %[1]s WHERE id in (SELECT id FROM (SELECT id FROM %[1]s WHERE expires_at <= ? and nid = ? ORDER BY expires_at ASC LIMIT ?) AS s)\",\n\t\tlogin.Flow{}.TableName(),\n\t),\n\t\texpiresAt,\n\t\tp.NetworkID(ctx),\n\t\tlimit,\n\t).Exec()\n\n\treturn sqlcon.HandleError(err)\n}\n"
  },
  {
    "path": "persistence/sql/persister_login_code.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc (p *Persister) CreateLoginCode(ctx context.Context, params *code.CreateLoginCodeParams) (_ *code.LoginCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateLoginCode\")\n\tdefer otelx.End(span, &err)\n\n\tnow := time.Now().UTC()\n\tloginCode := &code.LoginCode{\n\t\tIdentityID:  params.IdentityID,\n\t\tAddress:     params.Address,\n\t\tAddressType: params.AddressType,\n\t\tCodeHMAC:    p.hmacValue(ctx, params.RawCode),\n\t\tIssuedAt:    now,\n\t\tExpiresAt:   now.UTC().Add(p.r.Config().SelfServiceCodeMethodLifespan(ctx)),\n\t\tFlowID:      params.FlowID,\n\t\tNID:         p.NetworkID(ctx),\n\t\tID:          uuid.Nil,\n\t}\n\n\tif err := p.GetConnection(ctx).Create(loginCode); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn loginCode, nil\n}\n\nfunc (p *Persister) UseLoginCode(ctx context.Context, flowID uuid.UUID, identityID uuid.UUID, userProvidedCode string) (_ *code.LoginCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UseLoginCode\")\n\tdefer otelx.End(span, &err)\n\n\tcodeRow, err := useOneTimeCode[code.LoginCode](ctx, p, flowID, userProvidedCode, login.Flow{}.TableName(), \"selfservice_login_flow_id\", withCheckIdentityID(identityID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn codeRow, nil\n}\n\nfunc (p *Persister) GetUsedLoginCode(ctx context.Context, flowID uuid.UUID) (_ *code.LoginCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetUsedLoginCode\")\n\tdefer otelx.End(span, &err)\n\n\tvar loginCode code.LoginCode\n\tif err := p.Connection(ctx).Where(\"selfservice_login_flow_id = ? AND nid = ? AND used_at IS NOT NULL\", flowID, p.NetworkID(ctx)).First(&loginCode); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\treturn &loginCode, nil\n}\n\nfunc (p *Persister) DeleteLoginCodesOfFlow(ctx context.Context, flowID uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteLoginCodesOfFlow\")\n\tdefer otelx.End(span, &err)\n\n\treturn p.GetConnection(ctx).Where(\"selfservice_login_flow_id = ? AND nid = ?\", flowID, p.NetworkID(ctx)).Delete(&code.LoginCode{})\n}\n"
  },
  {
    "path": "persistence/sql/persister_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence/sql/update\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nvar (\n\t_ recovery.FlowPersister      = new(Persister)\n\t_ link.RecoveryTokenPersister = new(Persister)\n)\n\nfunc (p *Persister) CreateRecoveryFlow(ctx context.Context, r *recovery.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateRecoveryFlow\")\n\tdefer otelx.End(span, &err)\n\n\tr.NID = p.NetworkID(ctx)\n\treturn p.GetConnection(ctx).Create(r)\n}\n\nfunc (p *Persister) GetRecoveryFlow(ctx context.Context, id uuid.UUID) (_ *recovery.Flow, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetRecoveryFlow\")\n\tdefer otelx.End(span, &err)\n\n\tvar r recovery.Flow\n\tif err := p.GetConnection(ctx).Where(\"id = ? AND nid = ?\", id, p.NetworkID(ctx)).First(&r); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &r, nil\n}\n\nfunc (p *Persister) UpdateRecoveryFlow(ctx context.Context, r *recovery.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpdateRecoveryFlow\")\n\tdefer otelx.End(span, &err)\n\n\tcp := *r\n\tcp.NID = p.NetworkID(ctx)\n\treturn update.Generic(ctx, p.GetConnection(ctx), p.r.Tracer(ctx).Tracer(), cp)\n}\n\nfunc (p *Persister) CreateRecoveryToken(ctx context.Context, token *link.RecoveryToken) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateRecoveryToken\")\n\tdefer otelx.End(span, &err)\n\n\tt := token.Token\n\ttoken.Token = p.hmacValue(ctx, t)\n\ttoken.NID = p.NetworkID(ctx)\n\n\t// This should not create the request eagerly because otherwise we might accidentally create an address that isn't\n\t// supposed to be in the database.\n\tif err := p.GetConnection(ctx).Create(token); err != nil {\n\t\treturn err\n\t}\n\n\ttoken.Token = t\n\treturn nil\n}\n\nfunc (p *Persister) UseRecoveryToken(ctx context.Context, fID uuid.UUID, token string) (_ *link.RecoveryToken, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UseRecoveryToken\")\n\tdefer otelx.End(span, &err)\n\n\tvar rt link.RecoveryToken\n\n\tnid := p.NetworkID(ctx)\n\tif err := sqlcon.HandleError(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) (err error) {\n\t\tfor _, secret := range p.r.Config().SecretsSession(ctx) {\n\t\t\tif err = tx.Where(\"token = ? AND nid = ? AND NOT used AND selfservice_recovery_flow_id = ?\", hmacValueWithSecret(token, secret), nid, fID).First(&rt); err != nil {\n\t\t\t\tif !errors.Is(sqlcon.HandleError(err), sqlcon.ErrNoRows) {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvar ra identity.RecoveryAddress\n\t\tif err := tx.Where(\"id = ? AND nid = ?\", rt.RecoveryAddressID, nid).First(&ra); err != nil {\n\t\t\tif !errors.Is(sqlcon.HandleError(err), sqlcon.ErrNoRows) {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\trt.RecoveryAddress = &ra\n\n\t\t//#nosec G201 -- TableName is static\n\t\treturn tx.RawQuery(fmt.Sprintf(\"UPDATE %s SET used=true, used_at=? WHERE id=? AND nid = ?\", rt.TableName(ctx)), time.Now().UTC(), rt.ID, nid).Exec()\n\t})); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &rt, nil\n}\n\nfunc (p *Persister) DeleteRecoveryToken(ctx context.Context, token string) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteRecoveryToken\")\n\tdefer otelx.End(span, &err)\n\n\t//#nosec G201 -- TableName is static\n\treturn p.GetConnection(ctx).RawQuery(fmt.Sprintf(\"DELETE FROM %s WHERE token=? AND nid = ?\", new(link.RecoveryToken).TableName(ctx)), token, p.NetworkID(ctx)).Exec()\n}\n\nfunc (p *Persister) DeleteExpiredRecoveryFlows(ctx context.Context, expiresAt time.Time, limit int) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteExpiredRecoveryFlows\")\n\tdefer otelx.End(span, &err)\n\t//#nosec G201 -- TableName is static\n\terr = p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %[1]s WHERE id in (SELECT id FROM (SELECT id FROM %[1]s c WHERE expires_at <= ? and nid = ? ORDER BY expires_at ASC LIMIT ?) AS s)\",\n\t\trecovery.Flow{}.TableName(),\n\t),\n\t\texpiresAt,\n\t\tp.NetworkID(ctx),\n\t\tlimit,\n\t).Exec()\n\n\treturn sqlcon.HandleError(err)\n}\n"
  },
  {
    "path": "persistence/sql/persister_recovery_code.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc (p *Persister) CreateRecoveryCode(ctx context.Context, params *code.CreateRecoveryCodeParams) (_ *code.RecoveryCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateRecoveryCode\")\n\tdefer otelx.End(span, &err)\n\n\tnow := time.Now()\n\trecoveryCode := &code.RecoveryCode{\n\t\tID:         uuid.Nil,\n\t\tCodeHMAC:   p.hmacValue(ctx, params.RawCode),\n\t\tExpiresAt:  now.UTC().Add(params.ExpiresIn),\n\t\tIssuedAt:   now,\n\t\tCodeType:   params.CodeType,\n\t\tFlowID:     params.FlowID,\n\t\tNID:        p.NetworkID(ctx),\n\t\tIdentityID: params.IdentityID,\n\t}\n\n\tif params.RecoveryAddress != nil {\n\t\trecoveryCode.RecoveryAddress = params.RecoveryAddress\n\t\trecoveryCode.RecoveryAddressID = uuid.NullUUID{\n\t\t\tUUID:  params.RecoveryAddress.ID,\n\t\t\tValid: true,\n\t\t}\n\t}\n\n\t// This should not create the request eagerly because otherwise we might accidentally create an address that isn't\n\t// supposed to be in the database.\n\tif err := p.GetConnection(ctx).Create(recoveryCode); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn recoveryCode, nil\n}\n\n// UseRecoveryCode attempts to \"use\" the supplied code in the flow\n//\n// If the supplied code matched a code from the flow, no error is returned\n// If an invalid code was submitted with this flow more than 5 times, an error is returned\nfunc (p *Persister) UseRecoveryCode(ctx context.Context, flowID uuid.UUID, userProvidedCode string) (_ *code.RecoveryCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UseRecoveryCode\")\n\tdefer otelx.End(span, &err)\n\n\tcodeRow, err := useOneTimeCode[code.RecoveryCode](ctx, p, flowID, userProvidedCode, recovery.Flow{}.TableName(), \"selfservice_recovery_flow_id\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ra identity.RecoveryAddress\n\tif err := sqlcon.HandleError(p.GetConnection(ctx).Where(\"id = ? AND nid = ?\", codeRow.RecoveryAddressID, p.NetworkID(ctx)).First(&ra)); err != nil {\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\t// This is ok, it can happen when an administrator initiates account recovery. This works even if the\n\t\t\t// user has no recovery address!\n\t\t} else {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tcodeRow.RecoveryAddress = &ra\n\n\treturn codeRow, nil\n}\n\nfunc (p *Persister) DeleteRecoveryCodesOfFlow(ctx context.Context, flowID uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteRecoveryCodesOfFlow\")\n\tdefer otelx.End(span, &err)\n\n\treturn p.GetConnection(ctx).Where(\"selfservice_recovery_flow_id = ? AND nid = ?\", flowID, p.NetworkID(ctx)).Delete(&code.RecoveryCode{})\n}\n"
  },
  {
    "path": "persistence/sql/persister_registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/kratos/persistence/sql/update\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n)\n\nfunc (p *Persister) CreateRegistrationFlow(ctx context.Context, r *registration.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateRegistrationFlow\")\n\tdefer otelx.End(span, &err)\n\n\tr.NID = p.NetworkID(ctx)\n\tr.EnsureInternalContext()\n\treturn p.GetConnection(ctx).Create(r)\n}\n\nfunc (p *Persister) UpdateRegistrationFlow(ctx context.Context, r *registration.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpdateRegistrationFlow\")\n\tdefer otelx.End(span, &err)\n\n\tr.EnsureInternalContext()\n\tcp := *r\n\tcp.NID = p.NetworkID(ctx)\n\treturn update.Generic(ctx, p.GetConnection(ctx), p.r.Tracer(ctx).Tracer(), cp)\n}\n\nfunc (p *Persister) GetRegistrationFlow(ctx context.Context, id uuid.UUID) (_ *registration.Flow, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetRegistrationFlow\")\n\tdefer otelx.End(span, &err)\n\n\tvar r registration.Flow\n\tif err := p.GetConnection(ctx).Where(\"id = ? AND nid = ?\",\n\t\tid, p.NetworkID(ctx)).First(&r); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &r, nil\n}\n\nfunc (p *Persister) DeleteExpiredRegistrationFlows(ctx context.Context, expiresAt time.Time, limit int) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteExpiredRegistrationFlows\")\n\tdefer otelx.End(span, &err)\n\t//#nosec G201 -- TableName is static\n\terr = p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %[1]s WHERE id in (SELECT id FROM (SELECT id FROM %[1]s c WHERE expires_at <= ? and nid = ? ORDER BY expires_at ASC LIMIT ?) AS s)\",\n\t\tregistration.Flow{}.TableName(),\n\t),\n\t\texpiresAt,\n\t\tp.NetworkID(ctx),\n\t\tlimit,\n\t).Exec()\n\n\treturn sqlcon.HandleError(err)\n}\n"
  },
  {
    "path": "persistence/sql/persister_registration_code.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4/pkg/slice\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc (p *Persister) CreateRegistrationCode(ctx context.Context, params *code.CreateRegistrationCodeParams) (_ *code.RegistrationCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateRegistrationCode\")\n\tdefer otelx.End(span, &err)\n\n\tnow := time.Now().UTC()\n\tregistrationCode := &code.RegistrationCode{\n\t\tAddress:     params.Address,\n\t\tAddressType: params.AddressType,\n\t\tCodeHMAC:    p.hmacValue(ctx, params.RawCode),\n\t\tIssuedAt:    now,\n\t\tExpiresAt:   now.UTC().Add(p.r.Config().SelfServiceCodeMethodLifespan(ctx)),\n\t\tFlowID:      params.FlowID,\n\t\tNID:         p.NetworkID(ctx),\n\t\tID:          uuid.Nil,\n\t}\n\n\tif err := p.GetConnection(ctx).Create(registrationCode); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn registrationCode, nil\n}\n\nfunc (p *Persister) UseRegistrationCode(ctx context.Context, flowID uuid.UUID, userProvidedCode string, addresses ...string) (_ *code.RegistrationCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UseRegistrationCode\")\n\tdefer otelx.End(span, &err)\n\n\tcodeRow, err := useOneTimeCode[code.RegistrationCode](ctx, p, flowID, userProvidedCode, registration.Flow{}.TableName(), \"selfservice_registration_flow_id\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// ensure that the identifiers extracted from the traits are contained in the registration code\n\tif !slice.Contains(addresses, codeRow.Address) {\n\t\treturn nil, errors.WithStack(code.ErrCodeNotFound)\n\t}\n\n\treturn codeRow, nil\n}\n\nfunc (p *Persister) GetUsedRegistrationCode(ctx context.Context, flowID uuid.UUID) (_ *code.RegistrationCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetUsedRegistrationCode\")\n\tdefer otelx.End(span, &err)\n\n\tvar registrationCode code.RegistrationCode\n\tif err := p.Connection(ctx).Where(\"selfservice_registration_flow_id = ? AND used_at IS NOT NULL AND nid = ?\", flowID, p.NetworkID(ctx)).First(&registrationCode); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &registrationCode, nil\n}\n\nfunc (p *Persister) DeleteRegistrationCodesOfFlow(ctx context.Context, flowID uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteRegistrationCodesOfFlow\")\n\tdefer otelx.End(span, &err)\n\n\treturn p.GetConnection(ctx).Where(\"selfservice_registration_flow_id = ? AND nid = ?\", flowID, p.NetworkID(ctx)).Delete(&code.RegistrationCode{})\n}\n"
  },
  {
    "path": "persistence/sql/persister_session.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/stringsx\"\n)\n\nvar _ session.Persister = new(Persister)\n\nconst (\n\tSessionDeviceUserAgentMaxLength = 512\n\tSessionDeviceLocationMaxLength  = 512\n\tpaginationMaxItemsSize          = 1000\n\tpaginationDefaultItemsSize      = 250\n)\n\nfunc (p *Persister) GetSession(ctx context.Context, sid uuid.UUID, expandables session.Expandables) (_ *session.Session, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetSession\")\n\tdefer otelx.End(span, &err)\n\n\tvar s session.Session\n\ts.Devices = make([]session.Device, 0)\n\tnid := p.NetworkID(ctx)\n\n\tq := p.GetConnection(ctx).Q()\n\t// if len(expandables) > 0 {\n\tif expandables.Has(session.ExpandSessionDevices) {\n\t\tq = q.Eager(expandables.ToEager()...)\n\t}\n\n\tif err := q.Where(\"id = ? AND nid = ?\", sid, nid).First(&s); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\tif expandables.Has(session.ExpandSessionIdentity) {\n\t\t// This is needed because of how identities are fetched from the store (if we use eager not all fields are\n\t\t// available!).\n\t\ti, err := p.GetIdentity(ctx, s.IdentityID, identity.ExpandDefault)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ts.Identity = i\n\t}\n\n\ts.Active = s.IsActive()\n\treturn &s, nil\n}\n\nfunc (p *Persister) ListSessions(ctx context.Context, active *bool, paginatorOpts []keysetpagination.Option, expandables session.Expandables) (_ []session.Session, _ *keysetpagination.Paginator, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ListSessions\")\n\tdefer otelx.End(span, &err)\n\n\ts := make([]session.Session, 0)\n\tnid := p.NetworkID(ctx)\n\n\tpaginatorOpts = append(paginatorOpts, keysetpagination.WithDefaultSize(paginationDefaultItemsSize))\n\tpaginatorOpts = append(paginatorOpts, keysetpagination.WithMaxSize(paginationMaxItemsSize))\n\tpaginatorOpts = append(paginatorOpts, keysetpagination.WithDefaultToken(new(session.Session).DefaultPageToken()))\n\tpaginatorOpts = append(paginatorOpts, keysetpagination.WithColumn(\"created_at\", \"DESC\"))\n\tpaginator := keysetpagination.GetPaginator(paginatorOpts...)\n\n\tif _, err := uuid.FromString(paginator.Token().Parse(\"id\")[\"id\"]); err != nil {\n\t\treturn nil, nil, errors.WithStack(x.PageTokenInvalid)\n\t}\n\n\tif err := p.Transaction(ctx, func(ctx context.Context, c *pop.Connection) error {\n\t\tq := c.Where(\"nid = ?\", nid)\n\t\tif active != nil {\n\t\t\tif *active {\n\t\t\t\tq.Where(\"active = ? AND expires_at >= ?\", *active, time.Now().UTC())\n\t\t\t} else {\n\t\t\t\tq.Where(\"(active = ? OR expires_at < ?)\", *active, time.Now().UTC())\n\t\t\t}\n\t\t}\n\n\t\tif len(expandables) > 0 {\n\t\t\tq = q.EagerPreload(expandables.ToEager()...)\n\t\t}\n\n\t\t// Get the paginated list of matching items\n\t\tif err := q.Scope(keysetpagination.Paginate[session.Session](paginator)).All(&s); err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\n\t\treturn nil\n\t}); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tfor k := range s {\n\t\tif s[k].Identity == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif err := p.InjectTraitsSchemaURL(ctx, s[k].Identity); err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t}\n\n\ts, nextPage := keysetpagination.Result(s, paginator)\n\treturn s, nextPage, nil\n}\n\n// ListSessionsByIdentity retrieves sessions for an identity from the store.\nfunc (p *Persister) ListSessionsByIdentity(\n\tctx context.Context,\n\tiID uuid.UUID,\n\tactive *bool,\n\tpage, perPage int,\n\texcept uuid.UUID,\n\texpandables session.Expandables,\n) (_ []session.Session, _ int64, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ListSessionsByIdentity\")\n\tdefer otelx.End(span, &err)\n\n\ts := make([]session.Session, 0)\n\tt := int64(0)\n\tnid := p.NetworkID(ctx)\n\n\tif err := p.Transaction(ctx, func(ctx context.Context, c *pop.Connection) error {\n\t\tq := c.Where(\"identity_id = ? AND nid = ?\", iID, nid)\n\t\tif except != uuid.Nil {\n\t\t\tq = q.Where(\"id != ?\", except)\n\t\t}\n\t\tif active != nil {\n\t\t\tif *active {\n\t\t\t\tq.Where(\"active = ? AND expires_at >= ?\", *active, time.Now().UTC())\n\t\t\t} else {\n\t\t\t\tq.Where(\"(active = ? OR expires_at < ?)\", *active, time.Now().UTC())\n\t\t\t}\n\t\t}\n\n\t\tif len(expandables) > 0 {\n\t\t\tq = q.EagerPreload(expandables.ToEager()...)\n\t\t}\n\n\t\t// Get the total count of matching items\n\t\ttotal, err := q.Count(new(session.Session))\n\t\tif err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\t\tt = int64(total)\n\n\t\tq.Order(\"created_at DESC\")\n\n\t\t// Get the paginated list of matching items\n\t\tif err := q.Paginate(page, perPage).All(&s); err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\treturn s, t, nil\n}\n\n// ExtendSession updates the expiry of a session.\nfunc (p *Persister) ExtendSession(ctx context.Context, sessionID uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.ExtendSession\")\n\tdefer otelx.End(span, &err)\n\n\tnid := p.NetworkID(ctx)\n\ts := new(session.Session)\n\tvar didRefresh bool\n\tif err := errors.WithStack(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) (err error) {\n\t\tlockBehavior := \"\"\n\t\tif tx.Dialect.Name() == dbal.DriverCockroachDB {\n\t\t\t// SKIP LOCKED returns no rows if the row is locked by another transaction.\n\t\t\tlockBehavior = \"FOR UPDATE SKIP LOCKED\"\n\t\t}\n\n\t\tif err := tx.\n\t\t\tWhere(\n\t\t\t\t// We make use of the fact that CRDB supports FOR UPDATE as part of the WHERE clause.\n\t\t\t\tfmt.Sprintf(\"id = ? AND nid = ? %s\", lockBehavior),\n\t\t\t\tsessionID, nid,\n\t\t\t).First(s); err != nil {\n\n\t\t\t// This is a special case for CockroachDB. If the row is locked, we do not see the session. Therefor we return\n\t\t\t// a 404 not found error indicating to the user that the session might already be updated by someone else.\n\t\t\tif errors.Is(err, sqlcon.ErrNoRows) && tx.Dialect.Name() == dbal.DriverCockroachDB {\n\t\t\t\treturn errors.WithStack(herodot.ErrNotFound.WithReason(\"The session you are trying to extend is already being extended by another request or does not exist.\"))\n\t\t\t}\n\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\n\t\tif !s.CanBeRefreshed(ctx, p.r.Config()) {\n\t\t\t// This prevents excessive writes to the database.\n\t\t\treturn nil\n\t\t}\n\n\t\tdidRefresh = true\n\t\ts = s.Refresh(ctx, p.r.Config())\n\n\t\tif _, err := tx.Where(\"id = ? AND nid = ?\", sessionID, nid).UpdateQuery(s, \"expires_at\"); err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\n\t\treturn nil\n\t})); err != nil {\n\t\treturn err\n\t}\n\n\tif didRefresh {\n\t\ttrace.SpanFromContext(ctx).AddEvent(events.NewSessionLifespanExtended(ctx, s.ID, s.IdentityID, s.ExpiresAt))\n\t}\n\n\treturn nil\n}\n\n// UpsertSession creates a session if not found else updates.\n// This operation also inserts Session device records when a session is being created.\n// The update operation skips updating Session device records since only one record would need to be updated in this case.\nfunc (p *Persister) UpsertSession(ctx context.Context, s *session.Session) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpsertSession\")\n\tdefer otelx.End(span, &err)\n\n\ts.NID = p.NetworkID(ctx)\n\tif s.Identity != nil {\n\t\ts.IdentityID = s.Identity.ID\n\t} else if s.IdentityID.IsNil() {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"cannot upsert session without an identity or identity ID set\"))\n\t}\n\n\tvar updated bool\n\tdefer func() {\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif updated {\n\t\t\ttrace.SpanFromContext(ctx).AddEvent(events.NewSessionChanged(ctx, string(s.AuthenticatorAssuranceLevel), s.ID, s.IdentityID))\n\t\t} else {\n\t\t\ttrace.SpanFromContext(ctx).AddEvent(events.NewSessionIssued(ctx, string(s.AuthenticatorAssuranceLevel), s.ID, s.IdentityID))\n\t\t}\n\t}()\n\n\treturn errors.WithStack(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) (err error) {\n\t\tupdated = false\n\t\texists := false\n\t\tif !s.ID.IsNil() {\n\t\t\texists, err = tx.Where(\"id = ? AND nid = ?\", s.ID, s.NID).Exists(new(session.Session))\n\t\t\tif err != nil {\n\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t}\n\t\t}\n\n\t\tif exists {\n\t\t\t// This must not be eager or identities will be created / updated\n\t\t\t// Only update session and not corresponding session device records\n\t\t\tif err := tx.Update(s, \"issued_at\", \"identity_id\", \"nid\"); err != nil {\n\t\t\t\treturn sqlcon.HandleError(err)\n\t\t\t}\n\t\t\tupdated = true\n\t\t\treturn nil\n\t\t}\n\n\t\t// This must not be eager or identities will be created / updated\n\t\tif err := sqlcon.HandleError(tx.Create(s)); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor i := range s.Devices {\n\t\t\tdevice := &(s.Devices[i])\n\t\t\tdevice.SessionID = s.ID\n\t\t\tdevice.NID = s.NID\n\t\t\tdevice.IdentityID = new(s.IdentityID)\n\n\t\t\tif device.Location != nil {\n\t\t\t\tdevice.Location = new(stringsx.TruncateByteLen(*device.Location, SessionDeviceLocationMaxLength))\n\t\t\t}\n\t\t\tif device.UserAgent != nil {\n\t\t\t\tdevice.UserAgent = new(stringsx.TruncateByteLen(*device.UserAgent, SessionDeviceUserAgentMaxLength))\n\t\t\t}\n\n\t\t\tif err := p.CreateDevice(ctx, device); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}))\n}\n\nfunc (p *Persister) DeleteSession(ctx context.Context, sid uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteSession\")\n\tdefer otelx.End(span, &err)\n\n\tnid := p.NetworkID(ctx)\n\t//#nosec G201 -- TableName is static\n\tcount, err := p.GetConnection(ctx).RawQuery(fmt.Sprintf(\"DELETE FROM %s WHERE id = ? AND nid = ?\", session.Session{}.TableName()),\n\t\tsid,\n\t\tnid,\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\tif count == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\treturn nil\n}\n\nfunc (p *Persister) DeleteSessionsByIdentity(ctx context.Context, identityID uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteSessionsByIdentity\")\n\tdefer otelx.End(span, &err)\n\n\t//#nosec G201 -- TableName is static\n\tcount, err := p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %s WHERE identity_id = ? AND nid = ?\",\n\t\tsession.Session{}.TableName(),\n\t),\n\t\tidentityID,\n\t\tp.NetworkID(ctx),\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\tif count == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\treturn nil\n}\n\nfunc (p *Persister) GetSessionByToken(ctx context.Context, token string, expand session.Expandables, identityExpand identity.Expandables) (res *session.Session, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetSessionByToken\")\n\tdefer otelx.End(span, &err)\n\n\tvar s session.Session\n\ts.Devices = make([]session.Device, 0)\n\tnid := p.NetworkID(ctx)\n\n\tcon := p.GetConnection(ctx)\n\tif err := con.Where(\"token = ? AND nid = ?\", token, nid).First(&s); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\tvar (\n\t\ti  *identity.Identity\n\t\tsd []session.Device\n\t)\n\n\teg, ctx := errgroup.WithContext(ctx)\n\tif expand.Has(session.ExpandSessionDevices) {\n\t\teg.Go(func() error {\n\t\t\treturn sqlcon.HandleError(con.WithContext(ctx).\n\t\t\t\tWhere(\"session_id = ? AND nid = ?\", s.ID, nid).All(&sd))\n\t\t})\n\t}\n\n\t// This is needed because of how identities are fetched from the store (if we use eager not all fields are\n\t// available!).\n\tif expand.Has(session.ExpandSessionIdentity) {\n\t\teg.Go(func() (err error) {\n\t\t\ti, err = p.GetIdentity(ctx, s.IdentityID, identityExpand)\n\t\t\treturn err\n\t\t})\n\t}\n\n\tif err := eg.Wait(); err != nil {\n\t\treturn nil, err\n\t}\n\n\ts.Identity = i\n\ts.Devices = sd\n\n\treturn &s, nil\n}\n\nfunc (p *Persister) DeleteSessionByToken(ctx context.Context, token string) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteSessionByToken\")\n\tdefer otelx.End(span, &err)\n\n\t//#nosec G201 -- TableName is static\n\tcount, err := p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %s WHERE token = ? AND nid = ?\",\n\t\tsession.Session{}.TableName(),\n\t),\n\t\ttoken,\n\t\tp.NetworkID(ctx),\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\tif count == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\treturn nil\n}\n\nfunc (p *Persister) RevokeSessionByToken(ctx context.Context, token string) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.RevokeSessionByToken\")\n\tdefer otelx.End(span, &err)\n\n\t//#nosec G201 -- TableName is static\n\tcount, err := p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"UPDATE %s SET active = false WHERE token = ? AND nid = ?\",\n\t\tsession.Session{}.TableName(),\n\t),\n\t\ttoken,\n\t\tp.NetworkID(ctx),\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\tif count == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\treturn nil\n}\n\n// RevokeSessionById revokes a given session\nfunc (p *Persister) RevokeSessionById(ctx context.Context, sID uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.RevokeSessionById\")\n\tdefer otelx.End(span, &err)\n\n\t//#nosec G201 -- TableName is static\n\tcount, err := p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"UPDATE %s SET active = false WHERE id = ? AND nid = ?\",\n\t\tsession.Session{}.TableName(),\n\t),\n\t\tsID,\n\t\tp.NetworkID(ctx),\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\tif count == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\treturn nil\n}\n\n// RevokeSession revokes a given session. If the session does not exist or was not modified,\n// it effectively has been revoked already, and therefore that case does not return an error.\nfunc (p *Persister) RevokeSession(ctx context.Context, iID, sID uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.RevokeSession\")\n\tdefer otelx.End(span, &err)\n\n\t//#nosec G201 -- TableName is static\n\terr = p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"UPDATE %s SET active = false WHERE id = ? AND identity_id = ? AND nid = ?\",\n\t\tsession.Session{}.TableName(),\n\t),\n\t\tsID,\n\t\tiID,\n\t\tp.NetworkID(ctx),\n\t).Exec()\n\tif err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t}\n\treturn nil\n}\n\n// RevokeSessionsIdentityExcept marks all except the given session of an identity inactive.\nfunc (p *Persister) RevokeSessionsIdentityExcept(ctx context.Context, iID, sID uuid.UUID) (res int, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.RevokeSessionsIdentityExcept\")\n\tdefer otelx.End(span, &err)\n\n\t//#nosec G201 -- TableName is static\n\tcount, err := p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"UPDATE %s SET active = false WHERE identity_id = ? AND id != ? AND nid = ?\",\n\t\tsession.Session{}.TableName(),\n\t),\n\t\tiID,\n\t\tsID,\n\t\tp.NetworkID(ctx),\n\t).ExecWithCount()\n\tif err != nil {\n\t\treturn 0, sqlcon.HandleError(err)\n\t}\n\treturn count, nil\n}\n\nfunc (p *Persister) DeleteExpiredSessions(ctx context.Context, expiresAt time.Time, limit int) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteExpiredSessions\")\n\tdefer otelx.End(span, &err)\n\n\t//#nosec G201 -- TableName is static\n\terr = p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %[1]s WHERE id in (SELECT id FROM (SELECT id FROM %[1]s c WHERE expires_at <= ? and nid = ? ORDER BY expires_at ASC LIMIT ?) AS s)\",\n\t\tsession.Session{}.TableName(),\n\t),\n\t\texpiresAt,\n\t\tp.NetworkID(ctx),\n\t\tlimit,\n\t).Exec()\n\n\treturn sqlcon.HandleError(err)\n}\n"
  },
  {
    "path": "persistence/sql/persister_sessiontokenexchanger.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/randx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nvar _ sessiontokenexchange.Persister = new(Persister)\n\nfunc updateLimitClause(conn *pop.Connection) string {\n\t// Not all databases support limiting in update clauses.\n\tswitch conn.Dialect.Name() {\n\tcase \"sqlite3\", \"postgres\":\n\t\treturn \"\"\n\tdefault:\n\t\treturn \"LIMIT 1\"\n\t}\n}\n\nfunc (p *Persister) CreateSessionTokenExchanger(ctx context.Context, flowID uuid.UUID) (e *sessiontokenexchange.Exchanger, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateSessionTokenExchanger\")\n\tdefer otelx.End(span, &err)\n\n\te = &sessiontokenexchange.Exchanger{\n\t\tNID:          p.NetworkID(ctx),\n\t\tFlowID:       flowID,\n\t\tInitCode:     randx.MustString(64, randx.AlphaNum),\n\t\tReturnToCode: randx.MustString(64, randx.AlphaNum),\n\t}\n\n\treturn e, p.GetConnection(ctx).Create(e)\n}\n\nfunc (p *Persister) GetExchangerFromCode(ctx context.Context, initCode string, returnToCode string) (e *sessiontokenexchange.Exchanger, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetExchangerFromCode\")\n\tdefer otelx.End(span, &err)\n\n\te = new(sessiontokenexchange.Exchanger)\n\tconn := p.GetConnection(ctx)\n\tif err = conn.Where(`\nnid = ? AND\ninit_code = ? AND init_code <> '' AND\nreturn_to_code = ? AND return_to_code <> '' AND\nsession_id IS NOT NULL`,\n\t\tp.NetworkID(ctx), initCode, returnToCode).First(e); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn e, nil\n}\n\nfunc (p *Persister) UpdateSessionOnExchanger(ctx context.Context, flowID uuid.UUID, sessionID uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpdateSessionOnExchanger\")\n\tdefer otelx.End(span, &err)\n\n\tconn := p.GetConnection(ctx)\n\tquery := fmt.Sprintf(\"UPDATE %s SET session_id = ? WHERE flow_id = ? AND nid = ? %s\",\n\t\tconn.Dialect.Quote(new(sessiontokenexchange.Exchanger).TableName()),\n\t\tupdateLimitClause(conn),\n\t)\n\n\treturn sqlcon.HandleError(conn.RawQuery(query, sessionID, flowID, p.NetworkID(ctx)).Exec())\n}\n\nfunc (p *Persister) CodeForFlow(ctx context.Context, flowID uuid.UUID) (codes *sessiontokenexchange.Codes, found bool, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CodeForFlow\")\n\tdefer otelx.End(span, &err)\n\n\tvar e sessiontokenexchange.Exchanger\n\tswitch err = sqlcon.HandleError(p.GetConnection(ctx).\n\t\tWhere(\"flow_id = ? AND nid = ? AND init_code <> '' and return_to_code <> ''\", flowID, p.NetworkID(ctx)).\n\t\tFirst(&e)); {\n\tcase err == nil:\n\t\treturn &sessiontokenexchange.Codes{\n\t\t\tInitCode:     e.InitCode,\n\t\t\tReturnToCode: e.ReturnToCode,\n\t\t}, true, nil\n\tcase errors.Is(err, sqlcon.ErrNoRows):\n\t\treturn nil, false, nil\n\tdefault:\n\t\treturn nil, false, err\n\t}\n}\n\nfunc (p *Persister) MoveToNewFlow(ctx context.Context, oldFlow, newFlow uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.MoveToNewFlow\")\n\tdefer otelx.End(span, &err)\n\n\tconn := p.GetConnection(ctx)\n\tquery := fmt.Sprintf(\"UPDATE %s SET flow_id = ? WHERE flow_id = ? AND nid = ? %s\",\n\t\tconn.Dialect.Quote(new(sessiontokenexchange.Exchanger).TableName()),\n\t\tupdateLimitClause(conn),\n\t)\n\n\treturn sqlcon.HandleError(conn.RawQuery(query, newFlow, oldFlow, p.NetworkID(ctx)).Exec())\n}\n\nfunc (p *Persister) DeleteExpiredExchangers(ctx context.Context, at time.Time, limit int) error {\n\texpiredAfter := at.Add(1 * time.Hour)\n\tconn := p.GetConnection(ctx)\n\n\t//#nosec G201 -- TableName is static\n\terr := conn.RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %[1]s WHERE id in (SELECT id FROM (SELECT id FROM %[1]s c WHERE created_at <= ? and nid = ? ORDER BY created_at ASC LIMIT ?) AS s)\",\n\t\tsessiontokenexchange.Exchanger{}.TableName(),\n\t),\n\t\texpiredAfter,\n\t\tp.NetworkID(ctx),\n\t\tlimit,\n\t).Exec()\n\n\treturn sqlcon.HandleError(err)\n}\n"
  },
  {
    "path": "persistence/sql/persister_settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence/sql/update\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n)\n\nvar _ settings.FlowPersister = new(Persister)\n\nfunc (p *Persister) CreateSettingsFlow(ctx context.Context, r *settings.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateSettingsFlow\")\n\tdefer otelx.End(span, &err)\n\n\tr.NID = p.NetworkID(ctx)\n\tr.EnsureInternalContext()\n\treturn sqlcon.HandleError(p.GetConnection(ctx).Create(r))\n}\n\nfunc (p *Persister) GetSettingsFlow(ctx context.Context, id uuid.UUID) (_ *settings.Flow, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetSettingsFlow\")\n\tdefer otelx.End(span, &err)\n\n\tvar r settings.Flow\n\n\terr = p.GetConnection(ctx).Where(\"id = ? AND nid = ?\", id, p.NetworkID(ctx)).First(&r)\n\tif err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\tr.Identity, err = p.GetIdentity(ctx, r.IdentityID, identity.ExpandDefault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &r, nil\n}\n\nfunc (p *Persister) UpdateSettingsFlow(ctx context.Context, r *settings.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpdateSettingsFlow\")\n\tdefer otelx.End(span, &err)\n\n\tr.EnsureInternalContext()\n\tcp := *r\n\tcp.NID = p.NetworkID(ctx)\n\treturn update.Generic(ctx, p.GetConnection(ctx), p.r.Tracer(ctx).Tracer(), cp)\n}\n\nfunc (p *Persister) DeleteExpiredSettingsFlows(ctx context.Context, expiresAt time.Time, limit int) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteExpiredSettingsFlows\")\n\tdefer otelx.End(span, &err)\n\t//#nosec G201 -- TableName is static\n\terr = p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %[1]s WHERE id in (SELECT id FROM (SELECT id FROM %[1]s c WHERE expires_at <= ? and nid = ? ORDER BY expires_at ASC LIMIT ?) AS s)\",\n\t\tsettings.Flow{}.TableName(),\n\t),\n\t\texpiresAt,\n\t\tp.NetworkID(ctx),\n\t\tlimit,\n\t).Exec()\n\n\treturn sqlcon.HandleError(err)\n}\n"
  },
  {
    "path": "persistence/sql/persister_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cockroachdb/cockroach-go/v2/testserver\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/sync/errgroup\"\n\n\tcontinuity \"github.com/ory/kratos/continuity/test\"\n\t\"github.com/ory/kratos/corpx\"\n\tcourier \"github.com/ory/kratos/courier/test\"\n\t\"github.com/ory/kratos/driver/config\"\n\tri \"github.com/ory/kratos/identity\"\n\tidentity \"github.com/ory/kratos/identity/test\"\n\t\"github.com/ory/kratos/persistence/sql\"\n\t\"github.com/ory/kratos/persistence/sql/batch\"\n\tsqltesthelpers \"github.com/ory/kratos/persistence/sql/testhelpers\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\terrorx \"github.com/ory/kratos/selfservice/errorx/test\"\n\tlf \"github.com/ory/kratos/selfservice/flow/login\"\n\tlogin \"github.com/ory/kratos/selfservice/flow/login/test\"\n\trecovery \"github.com/ory/kratos/selfservice/flow/recovery/test\"\n\tregistration \"github.com/ory/kratos/selfservice/flow/registration/test\"\n\tsettings \"github.com/ory/kratos/selfservice/flow/settings/test\"\n\tverification \"github.com/ory/kratos/selfservice/flow/verification/test\"\n\tsessiontokenexchange \"github.com/ory/kratos/selfservice/sessiontokenexchange/test\"\n\tcode \"github.com/ory/kratos/selfservice/strategy/code/test\"\n\tlink \"github.com/ory/kratos/selfservice/strategy/link/test\"\n\tsession \"github.com/ory/kratos/session/test\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/pop/v6/logging\"\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/popx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlcon/dockertest\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n\tpop.SetNowFunc(func() time.Time {\n\t\treturn time.Now().UTC().Round(time.Second)\n\t})\n}\n\nfunc TestMain(m *testing.M) {\n\tos.Exit(m.Run())\n}\n\nfunc pl(t testing.TB) func(lvl logging.Level, s string, args ...interface{}) {\n\treturn func(lvl logging.Level, s string, args ...interface{}) {\n\t\tif pop.Debug == false {\n\t\t\treturn\n\t\t}\n\n\t\tif lvl == logging.SQL {\n\t\t\tif len(args) > 0 {\n\t\t\t\txargs := make([]string, len(args))\n\t\t\t\tfor i, a := range args {\n\t\t\t\t\tswitch a.(type) {\n\t\t\t\t\tcase string:\n\t\t\t\t\t\txargs[i] = fmt.Sprintf(\"%q\", a)\n\t\t\t\t\tdefault:\n\t\t\t\t\t\txargs[i] = fmt.Sprintf(\"%v\", a)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ts = fmt.Sprintf(\"%s - %s | %s\", lvl, s, xargs)\n\t\t\t} else {\n\t\t\t\ts = fmt.Sprintf(\"%s - %s\", lvl, s)\n\t\t\t}\n\t\t} else {\n\t\t\ts = fmt.Sprintf(s, args...)\n\t\t\ts = fmt.Sprintf(\"%s - %s\", lvl, s)\n\t\t}\n\t\tt.Log(s)\n\t}\n}\n\nfunc createCleanDatabases(t testing.TB) map[string]string {\n\tconns := map[string]string{\n\t\t\"sqlite\": dbal.NewSQLiteTestDatabase(t),\n\t}\n\tconnsMtx := sync.Mutex{}\n\n\tif !testing.Short() {\n\t\tfuncs := map[string]func(t testing.TB) string{\n\t\t\t\"postgres\": func(t testing.TB) string {\n\t\t\t\treturn dockertest.RunTestPostgreSQLWithVersion(t, \"16\")\n\t\t\t},\n\t\t\t\"mysql\": func(t testing.TB) string {\n\t\t\t\treturn dockertest.RunTestMySQLWithVersion(t, \"8.4\")\n\t\t\t},\n\t\t\t\"cockroach\": newLocalTestCRDBServer,\n\t\t}\n\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(len(funcs))\n\n\t\tfor k, f := range funcs {\n\t\t\tgo func(s string, f func(t testing.TB) string) {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tdb := f(t)\n\t\t\t\tconnsMtx.Lock()\n\t\t\t\tconns[s] = db\n\t\t\t\tconnsMtx.Unlock()\n\t\t\t}(k, f)\n\t\t}\n\n\t\twg.Wait()\n\t}\n\n\tps := make(map[string]string, len(conns))\n\tpsMtx := sync.Mutex{}\n\n\tvar wg sync.WaitGroup\n\twg.Add(len(conns))\n\tfor name, dsn := range conns {\n\t\tgo func(name, dsn string) {\n\t\t\tdefer wg.Done()\n\n\t\t\tif name != \"sqlite\" {\n\t\t\t\trequire.EventuallyWithT(t, func(t *assert.CollectT) {\n\t\t\t\t\tc, err := pop.NewConnection(&pop.ConnectionDetails{URL: dsn})\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.NoError(t, c.Open())\n\t\t\t\t\tdbName := \"testdb\" + strings.ReplaceAll(x.NewUUID().String(), \"-\", \"\")\n\t\t\t\t\trequire.NoError(t, c.RawQuery(\"CREATE DATABASE \"+dbName).Exec())\n\t\t\t\t\tdsn = regexp.MustCompile(`/[a-z0-9]+\\?`).ReplaceAllString(dsn, \"/\"+dbName+\"?\")\n\t\t\t\t}, 20*time.Second, 100*time.Millisecond)\n\t\t\t}\n\n\t\t\tt.Logf(\"Connecting to %s: %s\", name, dsn)\n\n\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\tp := reg.Persister().(*sql.Persister)\n\n\t\t\tt.Logf(\"Applying %s migrations\", name)\n\t\t\tpop.SetLogger(pl(t))\n\t\t\trequire.NoError(t, p.MigrateUp(context.Background()))\n\t\t\tt.Logf(\"%s migrations applied\", name)\n\t\t\tstatus, err := p.MigrationStatus(context.Background())\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.False(t, status.HasPending())\n\n\t\t\tpsMtx.Lock()\n\t\t\tps[name] = dsn\n\t\t\tpsMtx.Unlock()\n\n\t\t\tt.Logf(\"Database %s initialized successfully\", name)\n\t\t}(name, dsn)\n\t}\n\n\twg.Wait()\n\treturn ps\n}\n\nfunc TestPersister(t *testing.T) {\n\tt.Parallel()\n\n\tconns := createCleanDatabases(t)\n\tctx := testhelpers.WithDefaultIdentitySchema(context.Background(), \"file://./stub/identity.schema.json\")\n\n\tfor name, dsn := range conns {\n\t\tt.Run(fmt.Sprintf(\"database=%s\", name), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tt.Logf(\"DSN: %s\", dsn)\n\n\t\t\tt.Run(\"racy identity creation\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tvar wg sync.WaitGroup\n\n\t\t\t\tfor i := range 10 {\n\t\t\t\t\twg.Add(1)\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\tdefer wg.Done()\n\n\t\t\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t\t\t_, ps := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\t\t\tid := ri.NewIdentity(\"\")\n\t\t\t\t\t\tid.SetCredentials(ri.CredentialsTypePassword, ri.Credentials{\n\t\t\t\t\t\t\tType:        ri.CredentialsTypePassword,\n\t\t\t\t\t\t\tIdentifiers: []string{fmt.Sprintf(\"racy identity %d\", i)},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"foo\":\"bar\"}`),\n\t\t\t\t\t\t})\n\t\t\t\t\t\tid.Traits = ri.Traits(\"{}\")\n\n\t\t\t\t\t\trequire.NoError(t, ps.CreateIdentity(ctx, id))\n\t\t\t\t\t}()\n\t\t\t\t}\n\n\t\t\t\twg.Wait()\n\t\t\t})\n\n\t\t\tt.Run(\"case=credential types exist\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tfor _, ct := range []ri.CredentialsType{ri.CredentialsTypeOIDC, ri.CredentialsTypePassword} {\n\t\t\t\t\trequire.NoError(t, p.(*sql.Persister).Connection(context.Background()).Where(\"name = ?\", ct).First(&ri.CredentialsTypeTable{}))\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"contract=identity.TestPool\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tidentity.TestPool(ctx, p, reg.IdentityManager(), name)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=registration.TestFlowPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tregistration.TestFlowPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=errorx.TestPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\terrorx.TestPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=login.TestFlowPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tlogin.TestFlowPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=settings.TestFlowPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tsettings.TestFlowPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=session.TestPersister\", func(t *testing.T) {\n\t\t\t\t// Don't run this in parallel on SQLite as it causes table locks.\n\t\t\t\tif name != \"sqlite\" {\n\t\t\t\t\tt.Parallel()\n\t\t\t\t}\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tsession.TestPersister(ctx, reg.Config(), p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=sessiontokenexchange.TestPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tsessiontokenexchange.TestPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=courier.TestPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tupsert, insert := sqltesthelpers.DefaultNetworkWrapper(p)\n\t\t\t\tcourier.TestPersister(ctx, upsert, insert)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=verification.TestFlowPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tverification.TestFlowPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=recovery.TestFlowPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\trecovery.TestFlowPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=link.TestPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tlink.TestPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=code.TestPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tcode.TestPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=continuity.TestPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tcontinuity.TestPersister(ctx, p)(t)\n\t\t\t})\n\t\t\tt.Run(\"contract=batch.TestPersister\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, reg := pkg.NewRegistryDefaultWithDSN(t, dsn)\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, reg.Persister())\n\t\t\t\tbatch.TestPersister(ctx, reg.Tracer(ctx), p)(t)\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc getErr(args ...interface{}) error {\n\tif len(args) == 0 {\n\t\treturn nil\n\t}\n\tlastArg := args[len(args)-1]\n\tif e, ok := lastArg.(error); ok {\n\t\treturn e\n\t}\n\treturn nil\n}\n\nfunc TestPersister_Transaction(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := reg.Persister()\n\n\tt.Run(\"case=should not create identity because callback returned error\", func(t *testing.T) {\n\t\ti := &ri.Identity{\n\t\t\tID:     x.NewUUID(),\n\t\t\tState:  ri.StateActive,\n\t\t\tTraits: ri.Traits(`{}`),\n\t\t}\n\t\terrMessage := \"failing because why not\"\n\t\terr := p.Transaction(context.Background(), func(_ context.Context, connection *pop.Connection) error {\n\t\t\trequire.NoError(t, connection.Create(i))\n\t\t\treturn errors.New(errMessage)\n\t\t})\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), errMessage)\n\t\t_, err = p.GetIdentity(context.Background(), i.ID, ri.ExpandNothing)\n\t\trequire.Error(t, err)\n\t\tassert.Equal(t, sqlcon.ErrNoRows.Error(), err.Error())\n\t})\n\n\tt.Run(\"case=functions should use the context connection\", func(t *testing.T) {\n\t\tc := p.GetConnection(context.Background())\n\t\terrMessage := \"some stupid error you can't debug\"\n\t\tlr := &lf.Flow{\n\t\t\tID: x.NewUUID(),\n\t\t}\n\t\terr := c.Transaction(func(tx *pop.Connection) error {\n\t\t\tctx := popx.WithTransaction(context.Background(), tx)\n\t\t\trequire.NoError(t, p.CreateLoginFlow(ctx, lr), \"%+v\", lr)\n\t\t\trequire.NoError(t, getErr(p.GetLoginFlow(ctx, lr.ID)), \"%+v\", lr)\n\t\t\treturn errors.New(errMessage)\n\t\t})\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), errMessage)\n\t\t_, err = p.GetLoginFlow(context.Background(), lr.ID)\n\t\trequire.Error(t, err)\n\t\tassert.Equal(t, sqlcon.ErrNoRows.Error(), err.Error())\n\t})\n}\n\nfunc Benchmark_BatchCreateIdentities(b *testing.B) {\n\tconns := createCleanDatabases(b)\n\tctx := context.Background()\n\tbatchSizes := []int{1, 10, 100, 500, 800, 900, 1000, 2000, 3000}\n\tparallelRequests := []int{1, 4, 8, 16}\n\n\tfor name, dsn := range conns {\n\t\t_, reg := pkg.NewRegistryDefaultWithDSN(b, dsn)\n\t\tb.Run(fmt.Sprintf(\"database=%s\", name), func(b *testing.B) {\n\t\t\tconf := reg.Config()\n\t\t\t_, p := testhelpers.NewNetwork(b, ctx, reg.Persister())\n\t\t\tmultipleEmailsSchema := schema.Schema{\n\t\t\t\tID:     \"multiple_emails\",\n\t\t\t\tURL:    urlx.ParseOrPanic(\"file://./stub/handler/multiple_emails.schema.json\"),\n\t\t\t\tRawURL: \"file://./stub/identity-2.schema.json\",\n\t\t\t}\n\t\t\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, []config.Schema{\n\t\t\t\t{\n\t\t\t\t\tID:  multipleEmailsSchema.ID,\n\t\t\t\t\tURL: multipleEmailsSchema.RawURL,\n\t\t\t\t},\n\t\t\t})\n\t\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, \"http://localhost/\")\n\n\t\t\trun := 0\n\t\t\tfor _, batchSize := range batchSizes {\n\t\t\t\tb.Run(fmt.Sprintf(\"batch-size=%d\", batchSize), func(b *testing.B) {\n\t\t\t\t\tfor _, paralellism := range parallelRequests {\n\t\t\t\t\t\tb.Run(fmt.Sprintf(\"parallelism=%d\", paralellism), func(b *testing.B) {\n\t\t\t\t\t\t\tstart := time.Now()\n\t\t\t\t\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t\t\t\t\twg := new(errgroup.Group)\n\t\t\t\t\t\t\t\tfor paralell := 0; paralell < paralellism; paralell++ {\n\t\t\t\t\t\t\t\t\tparalell := paralell\n\t\t\t\t\t\t\t\t\twg.Go(func() error {\n\t\t\t\t\t\t\t\t\t\tidentities := make([]*ri.Identity, batchSize)\n\t\t\t\t\t\t\t\t\t\tprefix := fmt.Sprintf(\"bench-insert-run-%d\", run+paralell)\n\t\t\t\t\t\t\t\t\t\tfor j := range identities {\n\t\t\t\t\t\t\t\t\t\t\tidentities[j] = identity.NewTestIdentity(1, prefix, j)\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\treturn p.CreateIdentities(ctx, identities...)\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tassert.NoError(b, wg.Wait())\n\t\t\t\t\t\t\t\trun += paralellism\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tend := time.Now()\n\t\t\t\t\t\t\tb.ReportMetric(float64(paralellism*batchSize*b.N), \"identites_created\")\n\t\t\t\t\t\t\tb.ReportMetric(float64(paralellism*batchSize*b.N)/end.Sub(start).Seconds(), \"identities/s\")\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\nfunc newLocalTestCRDBServer(t testing.TB) string {\n\tts, err := testserver.NewTestServer(testserver.CustomVersionOpt(\"v25.4.1\"))\n\trequire.NoError(t, err)\n\tt.Cleanup(ts.Stop)\n\n\trequire.NoError(t, ts.WaitForInit())\n\n\tts.PGURL().Scheme = \"cockroach\"\n\treturn ts.PGURL().String()\n}\n"
  },
  {
    "path": "persistence/sql/persister_transaction_helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/x/popx\"\n\n\t\"github.com/ory/pop/v6\"\n)\n\nfunc (p *Persister) Transaction(ctx context.Context, callback func(ctx context.Context, connection *pop.Connection) error) error {\n\treturn popx.Transaction(ctx, p.c.WithContext(ctx), callback)\n}\n\nfunc (p *Persister) GetConnection(ctx context.Context) *pop.Connection {\n\treturn popx.GetConnection(ctx, p.c.WithContext(ctx))\n}\n"
  },
  {
    "path": "persistence/sql/persister_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence/sql/update\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nvar _ verification.FlowPersister = new(Persister)\n\nfunc (p *Persister) CreateVerificationFlow(ctx context.Context, r *verification.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateVerificationFlow\")\n\tdefer otelx.End(span, &err)\n\n\tr.NID = p.NetworkID(ctx)\n\t// This should not create the request eagerly because otherwise we might accidentally create an address\n\t// that isn't supposed to be in the database.\n\treturn p.GetConnection(ctx).Create(r)\n}\n\nfunc (p *Persister) GetVerificationFlow(ctx context.Context, id uuid.UUID) (_ *verification.Flow, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.GetVerificationFlow\")\n\tdefer otelx.End(span, &err)\n\n\tvar r verification.Flow\n\tif err := p.GetConnection(ctx).Where(\"id = ? AND nid = ?\", id, p.NetworkID(ctx)).First(&r); err != nil {\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\n\treturn &r, nil\n}\n\nfunc (p *Persister) UpdateVerificationFlow(ctx context.Context, r *verification.Flow) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UpdateVerificationFlow\")\n\tdefer otelx.End(span, &err)\n\n\tcp := *r\n\tcp.NID = p.NetworkID(ctx)\n\treturn update.Generic(ctx, p.GetConnection(ctx), p.r.Tracer(ctx).Tracer(), cp)\n}\n\nfunc (p *Persister) CreateVerificationToken(ctx context.Context, token *link.VerificationToken) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateVerificationToken\")\n\tdefer otelx.End(span, &err)\n\n\tt := token.Token\n\ttoken.Token = p.hmacValue(ctx, t)\n\ttoken.NID = p.NetworkID(ctx)\n\n\t// This should not create the request eagerly because otherwise we might accidentally create an address that isn't\n\t// supposed to be in the database.\n\tif err := p.GetConnection(ctx).Create(token); err != nil {\n\t\treturn err\n\t}\n\ttoken.Token = t\n\treturn nil\n}\n\nfunc (p *Persister) UseVerificationToken(ctx context.Context, fID uuid.UUID, token string) (_ *link.VerificationToken, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UseVerificationToken\")\n\tdefer otelx.End(span, &err)\n\n\tvar rt link.VerificationToken\n\n\tnid := p.NetworkID(ctx)\n\tif err := sqlcon.HandleError(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) (err error) {\n\t\tfor _, secret := range p.r.Config().SecretsSession(ctx) {\n\t\t\tif err = tx.Where(\"token = ? AND nid = ? AND NOT used AND selfservice_verification_flow_id = ?\", hmacValueWithSecret(token, secret), nid, fID).First(&rt); err != nil {\n\t\t\t\tif !errors.Is(sqlcon.HandleError(err), sqlcon.ErrNoRows) {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvar va identity.VerifiableAddress\n\t\tif err := tx.Where(\"id = ? AND nid = ?\", rt.VerifiableAddressID, nid).First(&va); err != nil {\n\t\t\treturn sqlcon.HandleError(err)\n\t\t}\n\n\t\trt.VerifiableAddress = &va\n\n\t\t//#nosec G201 -- TableName is static\n\t\treturn tx.RawQuery(fmt.Sprintf(\"UPDATE %s SET used=true, used_at=? WHERE id=? AND nid = ?\", rt.TableName(ctx)), time.Now().UTC(), rt.ID, nid).Exec()\n\t})); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &rt, nil\n}\n\nfunc (p *Persister) DeleteVerificationToken(ctx context.Context, token string) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteVerificationToken\")\n\tdefer otelx.End(span, &err)\n\n\tnid := p.NetworkID(ctx)\n\t//#nosec G201 -- TableName is static\n\treturn p.GetConnection(ctx).RawQuery(fmt.Sprintf(\"DELETE FROM %s WHERE token=? AND nid = ?\", new(link.VerificationToken).TableName(ctx)), token, nid).Exec()\n}\n\nfunc (p *Persister) DeleteExpiredVerificationFlows(ctx context.Context, expiresAt time.Time, limit int) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteExpiredVerificationFlows\")\n\tdefer otelx.End(span, &err)\n\t//#nosec G201 -- TableName is static\n\terr = p.GetConnection(ctx).RawQuery(fmt.Sprintf(\n\t\t\"DELETE FROM %[1]s WHERE id in (SELECT id FROM (SELECT id FROM %[1]s c WHERE expires_at <= ? and nid = ? ORDER BY expires_at ASC LIMIT ?) AS s)\",\n\t\tverification.Flow{}.TableName(),\n\t),\n\t\texpiresAt,\n\t\tp.NetworkID(ctx),\n\t\tlimit,\n\t).Exec()\n\n\treturn sqlcon.HandleError(err)\n}\n"
  },
  {
    "path": "persistence/sql/persister_verification_code.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sql\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc (p *Persister) CreateVerificationCode(ctx context.Context, params *code.CreateVerificationCodeParams) (_ *code.VerificationCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.CreateVerificationCode\")\n\tdefer otelx.End(span, &err)\n\n\tnow := time.Now().UTC()\n\tverificationCode := &code.VerificationCode{\n\t\tID:        uuid.Nil,\n\t\tCodeHMAC:  p.hmacValue(ctx, params.RawCode),\n\t\tExpiresAt: now.Add(params.ExpiresIn),\n\t\tIssuedAt:  now,\n\t\tFlowID:    params.FlowID,\n\t\tNID:       p.NetworkID(ctx),\n\t}\n\n\tif params.VerifiableAddress == nil {\n\t\treturn nil, errors.WithStack(herodot.ErrNotFound.WithReason(\"can't create a verification code without a verifiable address\"))\n\t}\n\n\tverificationCode.VerifiableAddress = params.VerifiableAddress\n\tverificationCode.VerifiableAddressID = uuid.NullUUID{\n\t\tUUID:  params.VerifiableAddress.ID,\n\t\tValid: true,\n\t}\n\n\t// This should not create the request eagerly because otherwise we might accidentally create an address that isn't\n\t// supposed to be in the database.\n\tif err := p.GetConnection(ctx).Create(verificationCode); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn verificationCode, nil\n}\n\nfunc (p *Persister) UseVerificationCode(ctx context.Context, flowID uuid.UUID, userProvidedCode string) (_ *code.VerificationCode, err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.UseVerificationCode\")\n\tdefer otelx.End(span, &err)\n\n\tcodeRow, err := useOneTimeCode[code.VerificationCode](ctx, p, flowID, userProvidedCode, verification.Flow{}.TableName(), \"selfservice_verification_flow_id\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar va identity.VerifiableAddress\n\tif err := p.Connection(ctx).Where(\"id = ? AND nid = ?\", codeRow.VerifiableAddressID, p.NetworkID(ctx)).First(&va); err != nil {\n\t\t// This should fail on not found errors too, because the verifiable address must exist for the flow to work.\n\t\treturn nil, sqlcon.HandleError(err)\n\t}\n\tcodeRow.VerifiableAddress = &va\n\n\treturn codeRow, nil\n}\n\nfunc (p *Persister) DeleteVerificationCodesOfFlow(ctx context.Context, fID uuid.UUID) (err error) {\n\tctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, \"persistence.sql.DeleteVerificationCodesOfFlow\")\n\tdefer otelx.End(span, &err)\n\n\treturn p.GetConnection(ctx).Where(\"selfservice_verification_flow_id = ? AND nid = ?\", fID, p.NetworkID(ctx)).Delete(&code.VerificationCode{})\n}\n"
  },
  {
    "path": "persistence/sql/stub/expand.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"additionalProperties\": false,\n  \"properties\": {\n    \"traits\": {\n      \"additionalProperties\": false,\n      \"properties\": {\n        \"email\": {\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"totp\": {\n                \"account_name\": true\n              }\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          },\n          \"title\": \"Email address\",\n          \"type\": \"string\",\n          \"maxLength\": 320\n        },\n        \"name\": {\n          \"minLength\": 1,\n          \"title\": \"Name\",\n          \"type\": \"string\",\n          \"maxLength\": 256\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"name\"\n      ],\n      \"type\": \"object\"\n    }\n  },\n  \"title\": \"Person\",\n  \"type\": \"object\"\n}\n"
  },
  {
    "path": "persistence/sql/stub/identity-2.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "persistence/sql/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "persistence/sql/testhelpers/network.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tdb \"github.com/gofrs/uuid\"\n\n\tcourier \"github.com/ory/kratos/courier/test\"\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n)\n\nfunc DefaultNetworkWrapper(p persistence.Persister) (courier.NetworkWrapper, courier.NetworkWrapper) {\n\treturn func(t *testing.T, ctx context.Context) (db.UUID, courier.PersisterWrapper) {\n\t\t\treturn testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\t\t}, func(t *testing.T, ctx context.Context) (db.UUID, courier.PersisterWrapper) {\n\t\t\treturn testhelpers.NewNetwork(t, ctx, p)\n\t\t}\n}\n"
  },
  {
    "path": "persistence/sql/update/update.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage update\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/pop/v6/columns\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc Generic(ctx context.Context, c *pop.Connection, tracer trace.Tracer, v any, columnNames ...string) (err error) {\n\tctx, span := tracer.Start(ctx, \"persistence.sql.update\")\n\tdefer otelx.End(span, &err)\n\n\tquoter, ok := c.Dialect.(interface{ Quote(key string) string })\n\tif !ok {\n\t\treturn errors.Errorf(\"store is not a quoter: %T\", c.Store)\n\t}\n\n\tmodel := pop.NewModel(v, ctx)\n\ttn := model.TableName()\n\n\tcols := columns.Columns{}\n\tif len(columnNames) > 0 && tn == model.TableName() {\n\t\tcols = columns.NewColumnsWithAlias(tn, model.As, model.IDField())\n\t\tcols.Add(columnNames...)\n\t} else {\n\t\tcols = columns.ForStructWithAlias(v, tn, model.As, model.IDField())\n\t}\n\n\t//#nosec G201 -- TableName is static\n\tstmt := fmt.Sprintf(\"UPDATE %s AS %s SET %s WHERE (%s) AND %s.nid = :nid\",\n\t\tquoter.Quote(model.TableName()),\n\t\tmodel.Alias(),\n\t\tcols.Writeable().QuotedUpdateString(quoter),\n\t\tmodel.WhereNamedID(),\n\t\tmodel.Alias(),\n\t)\n\n\tif result, err := c.Store.NamedExecContext(ctx, stmt, v); err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t} else if affected, err := result.RowsAffected(); err != nil {\n\t\treturn sqlcon.HandleError(err)\n\t} else if affected == 0 {\n\t\treturn errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/client-go/.gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n*.test\n*.prof\n"
  },
  {
    "path": "pkg/client-go/.openapi-generator/FILES",
    "content": ".gitignore\n.openapi-generator-ignore\n.travis.yml\nREADME.md\napi/openapi.yaml\napi_courier.go\napi_frontend.go\napi_identity.go\napi_metadata.go\nclient.go\nconfiguration.go\ndocs/AuthenticatorAssuranceLevel.md\ndocs/BatchPatchIdentitiesResponse.md\ndocs/ConsistencyRequestParameters.md\ndocs/ContinueWith.md\ndocs/ContinueWithRecoveryUi.md\ndocs/ContinueWithRecoveryUiFlow.md\ndocs/ContinueWithRedirectBrowserTo.md\ndocs/ContinueWithSetOrySessionToken.md\ndocs/ContinueWithSettingsUi.md\ndocs/ContinueWithSettingsUiFlow.md\ndocs/ContinueWithVerificationUi.md\ndocs/ContinueWithVerificationUiFlow.md\ndocs/CourierAPI.md\ndocs/CourierMessageStatus.md\ndocs/CourierMessageType.md\ndocs/CreateFedcmFlowResponse.md\ndocs/CreateIdentityBody.md\ndocs/CreateRecoveryCodeForIdentityBody.md\ndocs/CreateRecoveryLinkForIdentityBody.md\ndocs/DeleteMySessionsCount.md\ndocs/ErrorAuthenticatorAssuranceLevelNotSatisfied.md\ndocs/ErrorBrowserLocationChangeRequired.md\ndocs/ErrorFlowReplaced.md\ndocs/ErrorGeneric.md\ndocs/FlowError.md\ndocs/FrontendAPI.md\ndocs/GenericError.md\ndocs/GetVersion200Response.md\ndocs/HealthNotReadyStatus.md\ndocs/HealthStatus.md\ndocs/Identity.md\ndocs/IdentityAPI.md\ndocs/IdentityCredentials.md\ndocs/IdentityCredentialsCode.md\ndocs/IdentityCredentialsCodeAddress.md\ndocs/IdentityCredentialsOidc.md\ndocs/IdentityCredentialsOidcProvider.md\ndocs/IdentityCredentialsPassword.md\ndocs/IdentityPatch.md\ndocs/IdentityPatchResponse.md\ndocs/IdentitySchemaContainer.md\ndocs/IdentityWithCredentials.md\ndocs/IdentityWithCredentialsOidc.md\ndocs/IdentityWithCredentialsOidcConfig.md\ndocs/IdentityWithCredentialsOidcConfigProvider.md\ndocs/IdentityWithCredentialsPassword.md\ndocs/IdentityWithCredentialsPasswordConfig.md\ndocs/IdentityWithCredentialsSaml.md\ndocs/IdentityWithCredentialsSamlConfig.md\ndocs/IdentityWithCredentialsSamlConfigProvider.md\ndocs/IsAlive200Response.md\ndocs/IsReady503Response.md\ndocs/JsonPatch.md\ndocs/LoginFlow.md\ndocs/LoginFlowState.md\ndocs/LogoutFlow.md\ndocs/Message.md\ndocs/MessageDispatch.md\ndocs/MetadataAPI.md\ndocs/NeedsPrivilegedSessionError.md\ndocs/OAuth2Client.md\ndocs/OAuth2ConsentRequestOpenIDConnectContext.md\ndocs/OAuth2LoginRequest.md\ndocs/PatchIdentitiesBody.md\ndocs/PerformNativeLogoutBody.md\ndocs/Provider.md\ndocs/RecoveryCodeForIdentity.md\ndocs/RecoveryFlow.md\ndocs/RecoveryFlowState.md\ndocs/RecoveryIdentityAddress.md\ndocs/RecoveryLinkForIdentity.md\ndocs/RegistrationFlow.md\ndocs/RegistrationFlowState.md\ndocs/SelfServiceFlowExpiredError.md\ndocs/Session.md\ndocs/SessionAuthenticationMethod.md\ndocs/SessionDevice.md\ndocs/SettingsFlow.md\ndocs/SettingsFlowState.md\ndocs/SuccessfulCodeExchangeResponse.md\ndocs/SuccessfulNativeLogin.md\ndocs/SuccessfulNativeRegistration.md\ndocs/TokenPagination.md\ndocs/TokenPaginationHeaders.md\ndocs/UiContainer.md\ndocs/UiNode.md\ndocs/UiNodeAnchorAttributes.md\ndocs/UiNodeAttributes.md\ndocs/UiNodeDivisionAttributes.md\ndocs/UiNodeImageAttributes.md\ndocs/UiNodeInputAttributes.md\ndocs/UiNodeMeta.md\ndocs/UiNodeScriptAttributes.md\ndocs/UiNodeTextAttributes.md\ndocs/UiText.md\ndocs/UpdateFedcmFlowBody.md\ndocs/UpdateIdentityBody.md\ndocs/UpdateLoginFlowBody.md\ndocs/UpdateLoginFlowWithCodeMethod.md\ndocs/UpdateLoginFlowWithIdentifierFirstMethod.md\ndocs/UpdateLoginFlowWithLookupSecretMethod.md\ndocs/UpdateLoginFlowWithOidcMethod.md\ndocs/UpdateLoginFlowWithPasskeyMethod.md\ndocs/UpdateLoginFlowWithPasswordMethod.md\ndocs/UpdateLoginFlowWithSamlMethod.md\ndocs/UpdateLoginFlowWithTotpMethod.md\ndocs/UpdateLoginFlowWithWebAuthnMethod.md\ndocs/UpdateRecoveryFlowBody.md\ndocs/UpdateRecoveryFlowWithCodeMethod.md\ndocs/UpdateRecoveryFlowWithLinkMethod.md\ndocs/UpdateRegistrationFlowBody.md\ndocs/UpdateRegistrationFlowWithCodeMethod.md\ndocs/UpdateRegistrationFlowWithOidcMethod.md\ndocs/UpdateRegistrationFlowWithPasskeyMethod.md\ndocs/UpdateRegistrationFlowWithPasswordMethod.md\ndocs/UpdateRegistrationFlowWithProfileMethod.md\ndocs/UpdateRegistrationFlowWithSamlMethod.md\ndocs/UpdateRegistrationFlowWithWebAuthnMethod.md\ndocs/UpdateSettingsFlowBody.md\ndocs/UpdateSettingsFlowWithLookupMethod.md\ndocs/UpdateSettingsFlowWithOidcMethod.md\ndocs/UpdateSettingsFlowWithPasskeyMethod.md\ndocs/UpdateSettingsFlowWithPasswordMethod.md\ndocs/UpdateSettingsFlowWithProfileMethod.md\ndocs/UpdateSettingsFlowWithSamlMethod.md\ndocs/UpdateSettingsFlowWithTotpMethod.md\ndocs/UpdateSettingsFlowWithWebAuthnMethod.md\ndocs/UpdateVerificationFlowBody.md\ndocs/UpdateVerificationFlowWithCodeMethod.md\ndocs/UpdateVerificationFlowWithLinkMethod.md\ndocs/VerifiableIdentityAddress.md\ndocs/VerificationFlow.md\ndocs/VerificationFlowState.md\ndocs/Version.md\ngit_push.sh\ngo.mod\ngo.sum\nmodel_authenticator_assurance_level.go\nmodel_batch_patch_identities_response.go\nmodel_consistency_request_parameters.go\nmodel_continue_with.go\nmodel_continue_with_recovery_ui.go\nmodel_continue_with_recovery_ui_flow.go\nmodel_continue_with_redirect_browser_to.go\nmodel_continue_with_set_ory_session_token.go\nmodel_continue_with_settings_ui.go\nmodel_continue_with_settings_ui_flow.go\nmodel_continue_with_verification_ui.go\nmodel_continue_with_verification_ui_flow.go\nmodel_courier_message_status.go\nmodel_courier_message_type.go\nmodel_create_fedcm_flow_response.go\nmodel_create_identity_body.go\nmodel_create_recovery_code_for_identity_body.go\nmodel_create_recovery_link_for_identity_body.go\nmodel_delete_my_sessions_count.go\nmodel_error_authenticator_assurance_level_not_satisfied.go\nmodel_error_browser_location_change_required.go\nmodel_error_flow_replaced.go\nmodel_error_generic.go\nmodel_flow_error.go\nmodel_generic_error.go\nmodel_get_version_200_response.go\nmodel_health_not_ready_status.go\nmodel_health_status.go\nmodel_identity.go\nmodel_identity_credentials.go\nmodel_identity_credentials_code.go\nmodel_identity_credentials_code_address.go\nmodel_identity_credentials_oidc.go\nmodel_identity_credentials_oidc_provider.go\nmodel_identity_credentials_password.go\nmodel_identity_patch.go\nmodel_identity_patch_response.go\nmodel_identity_schema_container.go\nmodel_identity_with_credentials.go\nmodel_identity_with_credentials_oidc.go\nmodel_identity_with_credentials_oidc_config.go\nmodel_identity_with_credentials_oidc_config_provider.go\nmodel_identity_with_credentials_password.go\nmodel_identity_with_credentials_password_config.go\nmodel_identity_with_credentials_saml.go\nmodel_identity_with_credentials_saml_config.go\nmodel_identity_with_credentials_saml_config_provider.go\nmodel_is_alive_200_response.go\nmodel_is_ready_503_response.go\nmodel_json_patch.go\nmodel_login_flow.go\nmodel_login_flow_state.go\nmodel_logout_flow.go\nmodel_message.go\nmodel_message_dispatch.go\nmodel_needs_privileged_session_error.go\nmodel_o_auth2_client.go\nmodel_o_auth2_consent_request_open_id_connect_context.go\nmodel_o_auth2_login_request.go\nmodel_patch_identities_body.go\nmodel_perform_native_logout_body.go\nmodel_provider.go\nmodel_recovery_code_for_identity.go\nmodel_recovery_flow.go\nmodel_recovery_flow_state.go\nmodel_recovery_identity_address.go\nmodel_recovery_link_for_identity.go\nmodel_registration_flow.go\nmodel_registration_flow_state.go\nmodel_self_service_flow_expired_error.go\nmodel_session.go\nmodel_session_authentication_method.go\nmodel_session_device.go\nmodel_settings_flow.go\nmodel_settings_flow_state.go\nmodel_successful_code_exchange_response.go\nmodel_successful_native_login.go\nmodel_successful_native_registration.go\nmodel_token_pagination.go\nmodel_token_pagination_headers.go\nmodel_ui_container.go\nmodel_ui_node.go\nmodel_ui_node_anchor_attributes.go\nmodel_ui_node_attributes.go\nmodel_ui_node_division_attributes.go\nmodel_ui_node_image_attributes.go\nmodel_ui_node_input_attributes.go\nmodel_ui_node_meta.go\nmodel_ui_node_script_attributes.go\nmodel_ui_node_text_attributes.go\nmodel_ui_text.go\nmodel_update_fedcm_flow_body.go\nmodel_update_identity_body.go\nmodel_update_login_flow_body.go\nmodel_update_login_flow_with_code_method.go\nmodel_update_login_flow_with_identifier_first_method.go\nmodel_update_login_flow_with_lookup_secret_method.go\nmodel_update_login_flow_with_oidc_method.go\nmodel_update_login_flow_with_passkey_method.go\nmodel_update_login_flow_with_password_method.go\nmodel_update_login_flow_with_saml_method.go\nmodel_update_login_flow_with_totp_method.go\nmodel_update_login_flow_with_web_authn_method.go\nmodel_update_recovery_flow_body.go\nmodel_update_recovery_flow_with_code_method.go\nmodel_update_recovery_flow_with_link_method.go\nmodel_update_registration_flow_body.go\nmodel_update_registration_flow_with_code_method.go\nmodel_update_registration_flow_with_oidc_method.go\nmodel_update_registration_flow_with_passkey_method.go\nmodel_update_registration_flow_with_password_method.go\nmodel_update_registration_flow_with_profile_method.go\nmodel_update_registration_flow_with_saml_method.go\nmodel_update_registration_flow_with_web_authn_method.go\nmodel_update_settings_flow_body.go\nmodel_update_settings_flow_with_lookup_method.go\nmodel_update_settings_flow_with_oidc_method.go\nmodel_update_settings_flow_with_passkey_method.go\nmodel_update_settings_flow_with_password_method.go\nmodel_update_settings_flow_with_profile_method.go\nmodel_update_settings_flow_with_saml_method.go\nmodel_update_settings_flow_with_totp_method.go\nmodel_update_settings_flow_with_web_authn_method.go\nmodel_update_verification_flow_body.go\nmodel_update_verification_flow_with_code_method.go\nmodel_update_verification_flow_with_link_method.go\nmodel_verifiable_identity_address.go\nmodel_verification_flow.go\nmodel_verification_flow_state.go\nmodel_version.go\nresponse.go\ntest/api_courier_test.go\ntest/api_frontend_test.go\ntest/api_identity_test.go\ntest/api_metadata_test.go\nutils.go\n"
  },
  {
    "path": "pkg/client-go/.openapi-generator/VERSION",
    "content": "7.12.0\n"
  },
  {
    "path": "pkg/client-go/.openapi-generator-ignore",
    "content": "# OpenAPI Generator Ignore\n# Generated by openapi-generator https://github.com/openapitools/openapi-generator\n\n# Use this file to prevent files from being overwritten by the generator.\n# The patterns follow closely to .gitignore or .dockerignore.\n\n# As an example, the C# client generator defines ApiClient.cs.\n# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:\n#ApiClient.cs\n\n# You can match any string of characters against a directory, file or extension with a single asterisk (*):\n#foo/*/qux\n# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux\n\n# You can recursively match patterns against a directory, file or extension with a double asterisk (**):\n#foo/**/qux\n# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux\n\n# You can also negate patterns with an exclamation (!).\n# For example, you can ignore all files in a docs folder with the file extension .md:\n#docs/*.md\n# Then explicitly reverse the ignore rule for a single file:\n#!docs/README.md\n"
  },
  {
    "path": "pkg/client-go/.travis.yml",
    "content": "language: go\n\ninstall:\n  - go get -d -v .\n\nscript:\n  - go build -v ./\n\n"
  },
  {
    "path": "pkg/client-go/README.md",
    "content": "# Go API client for client\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\n\n## Overview\nThis API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project.  By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client.\n\n- API version: \n- Package version: 1.0.0\n- Generator version: 7.12.0\n- Build package: org.openapitools.codegen.languages.GoClientCodegen\n\n## Installation\n\nInstall the following dependencies:\n\n```sh\ngo get github.com/stretchr/testify/assert\ngo get golang.org/x/net/context\n```\n\nPut the package under your project folder and add the following in import:\n\n```go\nimport client \"github.com/ory/client-go\"\n```\n\nTo use a proxy, set the environment variable `HTTP_PROXY`:\n\n```go\nos.Setenv(\"HTTP_PROXY\", \"http://proxy_name:proxy_port\")\n```\n\n## Configuration of Server URL\n\nDefault configuration comes with `Servers` field that contains server objects as defined in the OpenAPI specification.\n\n### Select Server Configuration\n\nFor using other server than the one defined on index 0 set context value `client.ContextServerIndex` of type `int`.\n\n```go\nctx := context.WithValue(context.Background(), client.ContextServerIndex, 1)\n```\n\n### Templated Server URL\n\nTemplated server URL is formatted using default variables from configuration or from context value `client.ContextServerVariables` of type `map[string]string`.\n\n```go\nctx := context.WithValue(context.Background(), client.ContextServerVariables, map[string]string{\n\t\"basePath\": \"v2\",\n})\n```\n\nNote, enum values are always validated and all unused variables are silently ignored.\n\n### URLs Configuration per Operation\n\nEach operation can use different server URL defined using `OperationServers` map in the `Configuration`.\nAn operation is uniquely identified by `\"{classname}Service.{nickname}\"` string.\nSimilar rules for overriding default operation server index and variables applies by using `client.ContextOperationServerIndices` and `client.ContextOperationServerVariables` context maps.\n\n```go\nctx := context.WithValue(context.Background(), client.ContextOperationServerIndices, map[string]int{\n\t\"{classname}Service.{nickname}\": 2,\n})\nctx = context.WithValue(context.Background(), client.ContextOperationServerVariables, map[string]map[string]string{\n\t\"{classname}Service.{nickname}\": {\n\t\t\"port\": \"8443\",\n\t},\n})\n```\n\n## Documentation for API Endpoints\n\nAll URIs are relative to *http://localhost*\n\nClass | Method | HTTP request | Description\n------------ | ------------- | ------------- | -------------\n*CourierAPI* | [**GetCourierMessage**](docs/CourierAPI.md#getcouriermessage) | **Get** /admin/courier/messages/{id} | Get a Message\n*CourierAPI* | [**ListCourierMessages**](docs/CourierAPI.md#listcouriermessages) | **Get** /admin/courier/messages | List Messages\n*FrontendAPI* | [**CreateBrowserLoginFlow**](docs/FrontendAPI.md#createbrowserloginflow) | **Get** /self-service/login/browser | Create Login Flow for Browsers\n*FrontendAPI* | [**CreateBrowserLogoutFlow**](docs/FrontendAPI.md#createbrowserlogoutflow) | **Get** /self-service/logout/browser | Create a Logout URL for Browsers\n*FrontendAPI* | [**CreateBrowserRecoveryFlow**](docs/FrontendAPI.md#createbrowserrecoveryflow) | **Get** /self-service/recovery/browser | Create Recovery Flow for Browsers\n*FrontendAPI* | [**CreateBrowserRegistrationFlow**](docs/FrontendAPI.md#createbrowserregistrationflow) | **Get** /self-service/registration/browser | Create Registration Flow for Browsers\n*FrontendAPI* | [**CreateBrowserSettingsFlow**](docs/FrontendAPI.md#createbrowsersettingsflow) | **Get** /self-service/settings/browser | Create Settings Flow for Browsers\n*FrontendAPI* | [**CreateBrowserVerificationFlow**](docs/FrontendAPI.md#createbrowserverificationflow) | **Get** /self-service/verification/browser | Create Verification Flow for Browser Clients\n*FrontendAPI* | [**CreateFedcmFlow**](docs/FrontendAPI.md#createfedcmflow) | **Get** /self-service/fed-cm/parameters | Get FedCM Parameters\n*FrontendAPI* | [**CreateNativeLoginFlow**](docs/FrontendAPI.md#createnativeloginflow) | **Get** /self-service/login/api | Create Login Flow for Native Apps\n*FrontendAPI* | [**CreateNativeRecoveryFlow**](docs/FrontendAPI.md#createnativerecoveryflow) | **Get** /self-service/recovery/api | Create Recovery Flow for Native Apps\n*FrontendAPI* | [**CreateNativeRegistrationFlow**](docs/FrontendAPI.md#createnativeregistrationflow) | **Get** /self-service/registration/api | Create Registration Flow for Native Apps\n*FrontendAPI* | [**CreateNativeSettingsFlow**](docs/FrontendAPI.md#createnativesettingsflow) | **Get** /self-service/settings/api | Create Settings Flow for Native Apps\n*FrontendAPI* | [**CreateNativeVerificationFlow**](docs/FrontendAPI.md#createnativeverificationflow) | **Get** /self-service/verification/api | Create Verification Flow for Native Apps\n*FrontendAPI* | [**DisableMyOtherSessions**](docs/FrontendAPI.md#disablemyothersessions) | **Delete** /sessions | Disable my other sessions\n*FrontendAPI* | [**DisableMySession**](docs/FrontendAPI.md#disablemysession) | **Delete** /sessions/{id} | Disable one of my sessions\n*FrontendAPI* | [**ExchangeSessionToken**](docs/FrontendAPI.md#exchangesessiontoken) | **Get** /sessions/token-exchange | Exchange Session Token\n*FrontendAPI* | [**GetFlowError**](docs/FrontendAPI.md#getflowerror) | **Get** /self-service/errors | Get User-Flow Errors\n*FrontendAPI* | [**GetLoginFlow**](docs/FrontendAPI.md#getloginflow) | **Get** /self-service/login/flows | Get Login Flow\n*FrontendAPI* | [**GetRecoveryFlow**](docs/FrontendAPI.md#getrecoveryflow) | **Get** /self-service/recovery/flows | Get Recovery Flow\n*FrontendAPI* | [**GetRegistrationFlow**](docs/FrontendAPI.md#getregistrationflow) | **Get** /self-service/registration/flows | Get Registration Flow\n*FrontendAPI* | [**GetSettingsFlow**](docs/FrontendAPI.md#getsettingsflow) | **Get** /self-service/settings/flows | Get Settings Flow\n*FrontendAPI* | [**GetVerificationFlow**](docs/FrontendAPI.md#getverificationflow) | **Get** /self-service/verification/flows | Get Verification Flow\n*FrontendAPI* | [**GetWebAuthnJavaScript**](docs/FrontendAPI.md#getwebauthnjavascript) | **Get** /.well-known/ory/webauthn.js | Get WebAuthn JavaScript\n*FrontendAPI* | [**ListMySessions**](docs/FrontendAPI.md#listmysessions) | **Get** /sessions | Get My Active Sessions\n*FrontendAPI* | [**PerformNativeLogout**](docs/FrontendAPI.md#performnativelogout) | **Delete** /self-service/logout/api | Perform Logout for Native Apps\n*FrontendAPI* | [**ToSession**](docs/FrontendAPI.md#tosession) | **Get** /sessions/whoami | Check Who the Current HTTP Session Belongs To\n*FrontendAPI* | [**UpdateFedcmFlow**](docs/FrontendAPI.md#updatefedcmflow) | **Post** /self-service/fed-cm/token | Submit a FedCM token\n*FrontendAPI* | [**UpdateLoginFlow**](docs/FrontendAPI.md#updateloginflow) | **Post** /self-service/login | Submit a Login Flow\n*FrontendAPI* | [**UpdateLogoutFlow**](docs/FrontendAPI.md#updatelogoutflow) | **Get** /self-service/logout | Update Logout Flow\n*FrontendAPI* | [**UpdateRecoveryFlow**](docs/FrontendAPI.md#updaterecoveryflow) | **Post** /self-service/recovery | Update Recovery Flow\n*FrontendAPI* | [**UpdateRegistrationFlow**](docs/FrontendAPI.md#updateregistrationflow) | **Post** /self-service/registration | Update Registration Flow\n*FrontendAPI* | [**UpdateSettingsFlow**](docs/FrontendAPI.md#updatesettingsflow) | **Post** /self-service/settings | Complete Settings Flow\n*FrontendAPI* | [**UpdateVerificationFlow**](docs/FrontendAPI.md#updateverificationflow) | **Post** /self-service/verification | Complete Verification Flow\n*IdentityAPI* | [**BatchPatchIdentities**](docs/IdentityAPI.md#batchpatchidentities) | **Patch** /admin/identities | Create multiple identities\n*IdentityAPI* | [**CreateIdentity**](docs/IdentityAPI.md#createidentity) | **Post** /admin/identities | Create an Identity\n*IdentityAPI* | [**CreateRecoveryCodeForIdentity**](docs/IdentityAPI.md#createrecoverycodeforidentity) | **Post** /admin/recovery/code | Create a Recovery Code\n*IdentityAPI* | [**CreateRecoveryLinkForIdentity**](docs/IdentityAPI.md#createrecoverylinkforidentity) | **Post** /admin/recovery/link | Create a Recovery Link\n*IdentityAPI* | [**DeleteIdentity**](docs/IdentityAPI.md#deleteidentity) | **Delete** /admin/identities/{id} | Delete an Identity\n*IdentityAPI* | [**DeleteIdentityCredentials**](docs/IdentityAPI.md#deleteidentitycredentials) | **Delete** /admin/identities/{id}/credentials/{type} | Delete a credential for a specific identity\n*IdentityAPI* | [**DeleteIdentitySessions**](docs/IdentityAPI.md#deleteidentitysessions) | **Delete** /admin/identities/{id}/sessions | Delete &amp; Invalidate an Identity&#39;s Sessions\n*IdentityAPI* | [**DisableSession**](docs/IdentityAPI.md#disablesession) | **Delete** /admin/sessions/{id} | Deactivate a Session\n*IdentityAPI* | [**ExtendSession**](docs/IdentityAPI.md#extendsession) | **Patch** /admin/sessions/{id}/extend | Extend a Session\n*IdentityAPI* | [**GetIdentity**](docs/IdentityAPI.md#getidentity) | **Get** /admin/identities/{id} | Get an Identity\n*IdentityAPI* | [**GetIdentityByExternalID**](docs/IdentityAPI.md#getidentitybyexternalid) | **Get** /admin/identities/by/external/{externalID} | Get an Identity by its External ID\n*IdentityAPI* | [**GetIdentitySchema**](docs/IdentityAPI.md#getidentityschema) | **Get** /schemas/{id} | Get Identity JSON Schema\n*IdentityAPI* | [**GetSession**](docs/IdentityAPI.md#getsession) | **Get** /admin/sessions/{id} | Get Session\n*IdentityAPI* | [**ListIdentities**](docs/IdentityAPI.md#listidentities) | **Get** /admin/identities | List Identities\n*IdentityAPI* | [**ListIdentitySchemas**](docs/IdentityAPI.md#listidentityschemas) | **Get** /schemas | Get all Identity Schemas\n*IdentityAPI* | [**ListIdentitySessions**](docs/IdentityAPI.md#listidentitysessions) | **Get** /admin/identities/{id}/sessions | List an Identity&#39;s Sessions\n*IdentityAPI* | [**ListSessions**](docs/IdentityAPI.md#listsessions) | **Get** /admin/sessions | List All Sessions\n*IdentityAPI* | [**PatchIdentity**](docs/IdentityAPI.md#patchidentity) | **Patch** /admin/identities/{id} | Patch an Identity\n*IdentityAPI* | [**UpdateIdentity**](docs/IdentityAPI.md#updateidentity) | **Put** /admin/identities/{id} | Update an Identity\n*MetadataAPI* | [**GetVersion**](docs/MetadataAPI.md#getversion) | **Get** /version | Return Running Software Version.\n*MetadataAPI* | [**IsAlive**](docs/MetadataAPI.md#isalive) | **Get** /health/alive | Check HTTP Server Status\n*MetadataAPI* | [**IsReady**](docs/MetadataAPI.md#isready) | **Get** /health/ready | Check HTTP Server and Database Status\n\n\n## Documentation For Models\n\n - [AuthenticatorAssuranceLevel](docs/AuthenticatorAssuranceLevel.md)\n - [BatchPatchIdentitiesResponse](docs/BatchPatchIdentitiesResponse.md)\n - [ConsistencyRequestParameters](docs/ConsistencyRequestParameters.md)\n - [ContinueWith](docs/ContinueWith.md)\n - [ContinueWithRecoveryUi](docs/ContinueWithRecoveryUi.md)\n - [ContinueWithRecoveryUiFlow](docs/ContinueWithRecoveryUiFlow.md)\n - [ContinueWithRedirectBrowserTo](docs/ContinueWithRedirectBrowserTo.md)\n - [ContinueWithSetOrySessionToken](docs/ContinueWithSetOrySessionToken.md)\n - [ContinueWithSettingsUi](docs/ContinueWithSettingsUi.md)\n - [ContinueWithSettingsUiFlow](docs/ContinueWithSettingsUiFlow.md)\n - [ContinueWithVerificationUi](docs/ContinueWithVerificationUi.md)\n - [ContinueWithVerificationUiFlow](docs/ContinueWithVerificationUiFlow.md)\n - [CourierMessageStatus](docs/CourierMessageStatus.md)\n - [CourierMessageType](docs/CourierMessageType.md)\n - [CreateFedcmFlowResponse](docs/CreateFedcmFlowResponse.md)\n - [CreateIdentityBody](docs/CreateIdentityBody.md)\n - [CreateRecoveryCodeForIdentityBody](docs/CreateRecoveryCodeForIdentityBody.md)\n - [CreateRecoveryLinkForIdentityBody](docs/CreateRecoveryLinkForIdentityBody.md)\n - [DeleteMySessionsCount](docs/DeleteMySessionsCount.md)\n - [ErrorAuthenticatorAssuranceLevelNotSatisfied](docs/ErrorAuthenticatorAssuranceLevelNotSatisfied.md)\n - [ErrorBrowserLocationChangeRequired](docs/ErrorBrowserLocationChangeRequired.md)\n - [ErrorFlowReplaced](docs/ErrorFlowReplaced.md)\n - [ErrorGeneric](docs/ErrorGeneric.md)\n - [FlowError](docs/FlowError.md)\n - [GenericError](docs/GenericError.md)\n - [GetVersion200Response](docs/GetVersion200Response.md)\n - [HealthNotReadyStatus](docs/HealthNotReadyStatus.md)\n - [HealthStatus](docs/HealthStatus.md)\n - [Identity](docs/Identity.md)\n - [IdentityCredentials](docs/IdentityCredentials.md)\n - [IdentityCredentialsCode](docs/IdentityCredentialsCode.md)\n - [IdentityCredentialsCodeAddress](docs/IdentityCredentialsCodeAddress.md)\n - [IdentityCredentialsOidc](docs/IdentityCredentialsOidc.md)\n - [IdentityCredentialsOidcProvider](docs/IdentityCredentialsOidcProvider.md)\n - [IdentityCredentialsPassword](docs/IdentityCredentialsPassword.md)\n - [IdentityPatch](docs/IdentityPatch.md)\n - [IdentityPatchResponse](docs/IdentityPatchResponse.md)\n - [IdentitySchemaContainer](docs/IdentitySchemaContainer.md)\n - [IdentityWithCredentials](docs/IdentityWithCredentials.md)\n - [IdentityWithCredentialsOidc](docs/IdentityWithCredentialsOidc.md)\n - [IdentityWithCredentialsOidcConfig](docs/IdentityWithCredentialsOidcConfig.md)\n - [IdentityWithCredentialsOidcConfigProvider](docs/IdentityWithCredentialsOidcConfigProvider.md)\n - [IdentityWithCredentialsPassword](docs/IdentityWithCredentialsPassword.md)\n - [IdentityWithCredentialsPasswordConfig](docs/IdentityWithCredentialsPasswordConfig.md)\n - [IdentityWithCredentialsSaml](docs/IdentityWithCredentialsSaml.md)\n - [IdentityWithCredentialsSamlConfig](docs/IdentityWithCredentialsSamlConfig.md)\n - [IdentityWithCredentialsSamlConfigProvider](docs/IdentityWithCredentialsSamlConfigProvider.md)\n - [IsAlive200Response](docs/IsAlive200Response.md)\n - [IsReady503Response](docs/IsReady503Response.md)\n - [JsonPatch](docs/JsonPatch.md)\n - [LoginFlow](docs/LoginFlow.md)\n - [LoginFlowState](docs/LoginFlowState.md)\n - [LogoutFlow](docs/LogoutFlow.md)\n - [Message](docs/Message.md)\n - [MessageDispatch](docs/MessageDispatch.md)\n - [NeedsPrivilegedSessionError](docs/NeedsPrivilegedSessionError.md)\n - [OAuth2Client](docs/OAuth2Client.md)\n - [OAuth2ConsentRequestOpenIDConnectContext](docs/OAuth2ConsentRequestOpenIDConnectContext.md)\n - [OAuth2LoginRequest](docs/OAuth2LoginRequest.md)\n - [PatchIdentitiesBody](docs/PatchIdentitiesBody.md)\n - [PerformNativeLogoutBody](docs/PerformNativeLogoutBody.md)\n - [Provider](docs/Provider.md)\n - [RecoveryCodeForIdentity](docs/RecoveryCodeForIdentity.md)\n - [RecoveryFlow](docs/RecoveryFlow.md)\n - [RecoveryFlowState](docs/RecoveryFlowState.md)\n - [RecoveryIdentityAddress](docs/RecoveryIdentityAddress.md)\n - [RecoveryLinkForIdentity](docs/RecoveryLinkForIdentity.md)\n - [RegistrationFlow](docs/RegistrationFlow.md)\n - [RegistrationFlowState](docs/RegistrationFlowState.md)\n - [SelfServiceFlowExpiredError](docs/SelfServiceFlowExpiredError.md)\n - [Session](docs/Session.md)\n - [SessionAuthenticationMethod](docs/SessionAuthenticationMethod.md)\n - [SessionDevice](docs/SessionDevice.md)\n - [SettingsFlow](docs/SettingsFlow.md)\n - [SettingsFlowState](docs/SettingsFlowState.md)\n - [SuccessfulCodeExchangeResponse](docs/SuccessfulCodeExchangeResponse.md)\n - [SuccessfulNativeLogin](docs/SuccessfulNativeLogin.md)\n - [SuccessfulNativeRegistration](docs/SuccessfulNativeRegistration.md)\n - [TokenPagination](docs/TokenPagination.md)\n - [TokenPaginationHeaders](docs/TokenPaginationHeaders.md)\n - [UiContainer](docs/UiContainer.md)\n - [UiNode](docs/UiNode.md)\n - [UiNodeAnchorAttributes](docs/UiNodeAnchorAttributes.md)\n - [UiNodeAttributes](docs/UiNodeAttributes.md)\n - [UiNodeDivisionAttributes](docs/UiNodeDivisionAttributes.md)\n - [UiNodeImageAttributes](docs/UiNodeImageAttributes.md)\n - [UiNodeInputAttributes](docs/UiNodeInputAttributes.md)\n - [UiNodeMeta](docs/UiNodeMeta.md)\n - [UiNodeScriptAttributes](docs/UiNodeScriptAttributes.md)\n - [UiNodeTextAttributes](docs/UiNodeTextAttributes.md)\n - [UiText](docs/UiText.md)\n - [UpdateFedcmFlowBody](docs/UpdateFedcmFlowBody.md)\n - [UpdateIdentityBody](docs/UpdateIdentityBody.md)\n - [UpdateLoginFlowBody](docs/UpdateLoginFlowBody.md)\n - [UpdateLoginFlowWithCodeMethod](docs/UpdateLoginFlowWithCodeMethod.md)\n - [UpdateLoginFlowWithIdentifierFirstMethod](docs/UpdateLoginFlowWithIdentifierFirstMethod.md)\n - [UpdateLoginFlowWithLookupSecretMethod](docs/UpdateLoginFlowWithLookupSecretMethod.md)\n - [UpdateLoginFlowWithOidcMethod](docs/UpdateLoginFlowWithOidcMethod.md)\n - [UpdateLoginFlowWithPasskeyMethod](docs/UpdateLoginFlowWithPasskeyMethod.md)\n - [UpdateLoginFlowWithPasswordMethod](docs/UpdateLoginFlowWithPasswordMethod.md)\n - [UpdateLoginFlowWithSamlMethod](docs/UpdateLoginFlowWithSamlMethod.md)\n - [UpdateLoginFlowWithTotpMethod](docs/UpdateLoginFlowWithTotpMethod.md)\n - [UpdateLoginFlowWithWebAuthnMethod](docs/UpdateLoginFlowWithWebAuthnMethod.md)\n - [UpdateRecoveryFlowBody](docs/UpdateRecoveryFlowBody.md)\n - [UpdateRecoveryFlowWithCodeMethod](docs/UpdateRecoveryFlowWithCodeMethod.md)\n - [UpdateRecoveryFlowWithLinkMethod](docs/UpdateRecoveryFlowWithLinkMethod.md)\n - [UpdateRegistrationFlowBody](docs/UpdateRegistrationFlowBody.md)\n - [UpdateRegistrationFlowWithCodeMethod](docs/UpdateRegistrationFlowWithCodeMethod.md)\n - [UpdateRegistrationFlowWithOidcMethod](docs/UpdateRegistrationFlowWithOidcMethod.md)\n - [UpdateRegistrationFlowWithPasskeyMethod](docs/UpdateRegistrationFlowWithPasskeyMethod.md)\n - [UpdateRegistrationFlowWithPasswordMethod](docs/UpdateRegistrationFlowWithPasswordMethod.md)\n - [UpdateRegistrationFlowWithProfileMethod](docs/UpdateRegistrationFlowWithProfileMethod.md)\n - [UpdateRegistrationFlowWithSamlMethod](docs/UpdateRegistrationFlowWithSamlMethod.md)\n - [UpdateRegistrationFlowWithWebAuthnMethod](docs/UpdateRegistrationFlowWithWebAuthnMethod.md)\n - [UpdateSettingsFlowBody](docs/UpdateSettingsFlowBody.md)\n - [UpdateSettingsFlowWithLookupMethod](docs/UpdateSettingsFlowWithLookupMethod.md)\n - [UpdateSettingsFlowWithOidcMethod](docs/UpdateSettingsFlowWithOidcMethod.md)\n - [UpdateSettingsFlowWithPasskeyMethod](docs/UpdateSettingsFlowWithPasskeyMethod.md)\n - [UpdateSettingsFlowWithPasswordMethod](docs/UpdateSettingsFlowWithPasswordMethod.md)\n - [UpdateSettingsFlowWithProfileMethod](docs/UpdateSettingsFlowWithProfileMethod.md)\n - [UpdateSettingsFlowWithSamlMethod](docs/UpdateSettingsFlowWithSamlMethod.md)\n - [UpdateSettingsFlowWithTotpMethod](docs/UpdateSettingsFlowWithTotpMethod.md)\n - [UpdateSettingsFlowWithWebAuthnMethod](docs/UpdateSettingsFlowWithWebAuthnMethod.md)\n - [UpdateVerificationFlowBody](docs/UpdateVerificationFlowBody.md)\n - [UpdateVerificationFlowWithCodeMethod](docs/UpdateVerificationFlowWithCodeMethod.md)\n - [UpdateVerificationFlowWithLinkMethod](docs/UpdateVerificationFlowWithLinkMethod.md)\n - [VerifiableIdentityAddress](docs/VerifiableIdentityAddress.md)\n - [VerificationFlow](docs/VerificationFlow.md)\n - [VerificationFlowState](docs/VerificationFlowState.md)\n - [Version](docs/Version.md)\n\n\n## Documentation For Authorization\n\n\nAuthentication schemes defined for the API:\n### oryAccessToken\n\n- **Type**: API key\n- **API key parameter name**: Authorization\n- **Location**: HTTP header\n\nNote, each API key must be added to a map of `map[string]APIKey` where the key is: oryAccessToken and passed in as the auth context for each request.\n\nExample\n\n```go\nauth := context.WithValue(\n\t\tcontext.Background(),\n\t\tclient.ContextAPIKeys,\n\t\tmap[string]client.APIKey{\n\t\t\t\"oryAccessToken\": {Key: \"API_KEY_STRING\"},\n\t\t},\n\t)\nr, err := client.Service.Operation(auth, args)\n```\n\n\n## Documentation for Utility Methods\n\nDue to the fact that model structure members are all pointers, this package contains\na number of utility functions to easily obtain pointers to values of basic types.\nEach of these functions takes a value of the given basic type and returns a pointer to it:\n\n* `PtrBool`\n* `PtrInt`\n* `PtrInt32`\n* `PtrInt64`\n* `PtrFloat`\n* `PtrFloat32`\n* `PtrFloat64`\n* `PtrString`\n* `PtrTime`\n\n## Author\n\noffice@ory.sh\n\n"
  },
  {
    "path": "pkg/client-go/api_courier.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype CourierAPI interface {\n\n\t/*\n\t\tGetCourierMessage Get a Message\n\n\t\tGets a specific messages by the given ID.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id MessageID is the ID of the message.\n\t\t@return CourierAPIGetCourierMessageRequest\n\t*/\n\tGetCourierMessage(ctx context.Context, id string) CourierAPIGetCourierMessageRequest\n\n\t// GetCourierMessageExecute executes the request\n\t//  @return Message\n\tGetCourierMessageExecute(r CourierAPIGetCourierMessageRequest) (*Message, *http.Response, error)\n\n\t/*\n\t\tListCourierMessages List Messages\n\n\t\tLists all messages by given status and recipient.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return CourierAPIListCourierMessagesRequest\n\t*/\n\tListCourierMessages(ctx context.Context) CourierAPIListCourierMessagesRequest\n\n\t// ListCourierMessagesExecute executes the request\n\t//  @return []Message\n\tListCourierMessagesExecute(r CourierAPIListCourierMessagesRequest) ([]Message, *http.Response, error)\n}\n\n// CourierAPIService CourierAPI service\ntype CourierAPIService service\n\ntype CourierAPIGetCourierMessageRequest struct {\n\tctx        context.Context\n\tApiService CourierAPI\n\tid         string\n}\n\nfunc (r CourierAPIGetCourierMessageRequest) Execute() (*Message, *http.Response, error) {\n\treturn r.ApiService.GetCourierMessageExecute(r)\n}\n\n/*\nGetCourierMessage Get a Message\n\nGets a specific messages by the given ID.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id MessageID is the ID of the message.\n\t@return CourierAPIGetCourierMessageRequest\n*/\nfunc (a *CourierAPIService) GetCourierMessage(ctx context.Context, id string) CourierAPIGetCourierMessageRequest {\n\treturn CourierAPIGetCourierMessageRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Message\nfunc (a *CourierAPIService) GetCourierMessageExecute(r CourierAPIGetCourierMessageRequest) (*Message, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Message\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"CourierAPIService.GetCourierMessage\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/courier/messages/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype CourierAPIListCourierMessagesRequest struct {\n\tctx        context.Context\n\tApiService CourierAPI\n\tpageSize   *int64\n\tpageToken  *string\n\tstatus     *CourierMessageStatus\n\trecipient  *string\n}\n\n// Items per Page  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r CourierAPIListCourierMessagesRequest) PageSize(pageSize int64) CourierAPIListCourierMessagesRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r CourierAPIListCourierMessagesRequest) PageToken(pageToken string) CourierAPIListCourierMessagesRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Status filters out messages based on status. If no value is provided, it doesn&#39;t take effect on filter.\nfunc (r CourierAPIListCourierMessagesRequest) Status(status CourierMessageStatus) CourierAPIListCourierMessagesRequest {\n\tr.status = &status\n\treturn r\n}\n\n// Recipient filters out messages based on recipient. If no value is provided, it doesn&#39;t take effect on filter.\nfunc (r CourierAPIListCourierMessagesRequest) Recipient(recipient string) CourierAPIListCourierMessagesRequest {\n\tr.recipient = &recipient\n\treturn r\n}\n\nfunc (r CourierAPIListCourierMessagesRequest) Execute() ([]Message, *http.Response, error) {\n\treturn r.ApiService.ListCourierMessagesExecute(r)\n}\n\n/*\nListCourierMessages List Messages\n\nLists all messages by given status and recipient.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return CourierAPIListCourierMessagesRequest\n*/\nfunc (a *CourierAPIService) ListCourierMessages(ctx context.Context) CourierAPIListCourierMessagesRequest {\n\treturn CourierAPIListCourierMessagesRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Message\nfunc (a *CourierAPIService) ListCourierMessagesExecute(r CourierAPIListCourierMessagesRequest) ([]Message, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Message\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"CourierAPIService.ListCourierMessages\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/courier/messages\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\tif r.status != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"status\", r.status, \"form\", \"\")\n\t}\n\tif r.recipient != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"recipient\", r.recipient, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n"
  },
  {
    "path": "pkg/client-go/api_frontend.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype FrontendAPI interface {\n\n\t/*\n\t\t\tCreateBrowserLoginFlow Create Login Flow for Browsers\n\n\t\t\tThis endpoint initializes a browser-based user login flow. This endpoint will set the appropriate\n\t\tcookies and anti-CSRF measures required for browser-based flows.\n\n\t\tIf this endpoint is opened as a link in the browser, it will be redirected to\n\t\t`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\n\t\texists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\n\t\t`?refresh=true` was set.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\n\t\tThe optional query parameter login_challenge is set when using Kratos with\n\t\tHydra in an OAuth2 flow. See the oauth2_provider.url configuration\n\t\toption.\n\n\t\tThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserLoginFlowRequest\n\t*/\n\tCreateBrowserLoginFlow(ctx context.Context) FrontendAPICreateBrowserLoginFlowRequest\n\n\t// CreateBrowserLoginFlowExecute executes the request\n\t//  @return LoginFlow\n\tCreateBrowserLoginFlowExecute(r FrontendAPICreateBrowserLoginFlowRequest) (*LoginFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserLogoutFlow Create a Logout URL for Browsers\n\n\t\t\tThis endpoint initializes a browser-based user logout flow and a URL which can be used to log out the user.\n\n\t\tThis endpoint is NOT INTENDED for API clients and only works\n\t\twith browsers (Chrome, Firefox, ...). For API clients you can\n\t\tcall the `/self-service/logout/api` URL directly with the Ory Session Token.\n\n\t\tThe URL is only valid for the currently signed in user. If no user is signed in, this endpoint returns\n\t\ta 401 error.\n\n\t\tWhen calling this endpoint from a backend, please ensure to properly forward the HTTP cookies.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserLogoutFlowRequest\n\t*/\n\tCreateBrowserLogoutFlow(ctx context.Context) FrontendAPICreateBrowserLogoutFlowRequest\n\n\t// CreateBrowserLogoutFlowExecute executes the request\n\t//  @return LogoutFlow\n\tCreateBrowserLogoutFlowExecute(r FrontendAPICreateBrowserLogoutFlowRequest) (*LogoutFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserRecoveryFlow Create Recovery Flow for Browsers\n\n\t\t\tThis endpoint initializes a browser-based account recovery flow. Once initialized, the browser will be redirected to\n\t\t`selfservice.flows.recovery.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\n\t\texists, the browser is returned to the configured return URL.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects\n\t\tor a 400 bad request error if the user is already authenticated.\n\n\t\tThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\n\t\tMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserRecoveryFlowRequest\n\t*/\n\tCreateBrowserRecoveryFlow(ctx context.Context) FrontendAPICreateBrowserRecoveryFlowRequest\n\n\t// CreateBrowserRecoveryFlowExecute executes the request\n\t//  @return RecoveryFlow\n\tCreateBrowserRecoveryFlowExecute(r FrontendAPICreateBrowserRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserRegistrationFlow Create Registration Flow for Browsers\n\n\t\t\tThis endpoint initializes a browser-based user registration flow. This endpoint will set the appropriate\n\t\tcookies and anti-CSRF measures required for browser-based flows.\n\n\t\tIf this endpoint is opened as a link in the browser, it will be redirected to\n\t\t`selfservice.flows.registration.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\n\t\texists already, the browser will be redirected to `urls.default_redirect_url`.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the registration flow without a redirect.\n\n\t\tThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserRegistrationFlowRequest\n\t*/\n\tCreateBrowserRegistrationFlow(ctx context.Context) FrontendAPICreateBrowserRegistrationFlowRequest\n\n\t// CreateBrowserRegistrationFlowExecute executes the request\n\t//  @return RegistrationFlow\n\tCreateBrowserRegistrationFlowExecute(r FrontendAPICreateBrowserRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserSettingsFlow Create Settings Flow for Browsers\n\n\t\t\tThis endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\n\t\t`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\n\t\tOry Kratos Session Cookie is included in the request, a login flow will be initialized.\n\n\t\tIf this endpoint is opened as a link in the browser, it will be redirected to\n\t\t`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid user session\n\t\twas set, the browser will be redirected to the login endpoint.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the settings flow without any redirects\n\t\tor a 401 forbidden error if no valid session was set.\n\n\t\tDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`session_inactive`: No Ory Session was found - sign in a user first.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\n\t\tThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\n\t\tMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserSettingsFlowRequest\n\t*/\n\tCreateBrowserSettingsFlow(ctx context.Context) FrontendAPICreateBrowserSettingsFlowRequest\n\n\t// CreateBrowserSettingsFlowExecute executes the request\n\t//  @return SettingsFlow\n\tCreateBrowserSettingsFlowExecute(r FrontendAPICreateBrowserSettingsFlowRequest) (*SettingsFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserVerificationFlow Create Verification Flow for Browser Clients\n\n\t\t\tThis endpoint initializes a browser-based account verification flow. Once initialized, the browser will be redirected to\n\t\t`selfservice.flows.verification.ui_url` with the flow ID set as the query parameter `?flow=`.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects.\n\n\t\tThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\n\n\t\tMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserVerificationFlowRequest\n\t*/\n\tCreateBrowserVerificationFlow(ctx context.Context) FrontendAPICreateBrowserVerificationFlowRequest\n\n\t// CreateBrowserVerificationFlowExecute executes the request\n\t//  @return VerificationFlow\n\tCreateBrowserVerificationFlowExecute(r FrontendAPICreateBrowserVerificationFlowRequest) (*VerificationFlow, *http.Response, error)\n\n\t/*\n\t\tCreateFedcmFlow Get FedCM Parameters\n\n\t\tThis endpoint returns a list of all available FedCM providers. It is only supported on the Ory Network.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return FrontendAPICreateFedcmFlowRequest\n\t*/\n\tCreateFedcmFlow(ctx context.Context) FrontendAPICreateFedcmFlowRequest\n\n\t// CreateFedcmFlowExecute executes the request\n\t//  @return CreateFedcmFlowResponse\n\tCreateFedcmFlowExecute(r FrontendAPICreateFedcmFlowRequest) (*CreateFedcmFlowResponse, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeLoginFlow Create Login Flow for Native Apps\n\n\t\t\tThis endpoint initiates a login flow for native apps that do not use a browser, such as mobile devices, smart TVs, and so on.\n\n\t\tIf a valid provided session cookie or session token is provided, a 400 Bad Request error\n\t\twill be returned unless the URL query parameter `?refresh=true` is set.\n\n\t\tTo fetch an existing login flow call `/self-service/login/flows?flow=<flow_id>`.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks, including CSRF login attacks.\n\n\t\tIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeLoginFlowRequest\n\t*/\n\tCreateNativeLoginFlow(ctx context.Context) FrontendAPICreateNativeLoginFlowRequest\n\n\t// CreateNativeLoginFlowExecute executes the request\n\t//  @return LoginFlow\n\tCreateNativeLoginFlowExecute(r FrontendAPICreateNativeLoginFlowRequest) (*LoginFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeRecoveryFlow Create Recovery Flow for Native Apps\n\n\t\t\tThis endpoint initiates a recovery flow for API clients such as mobile devices, smart TVs, and so on.\n\n\t\tIf a valid provided session cookie or session token is provided, a 400 Bad Request error.\n\n\t\tOn an existing recovery flow, use the `getRecoveryFlow` API endpoint.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeRecoveryFlowRequest\n\t*/\n\tCreateNativeRecoveryFlow(ctx context.Context) FrontendAPICreateNativeRecoveryFlowRequest\n\n\t// CreateNativeRecoveryFlowExecute executes the request\n\t//  @return RecoveryFlow\n\tCreateNativeRecoveryFlowExecute(r FrontendAPICreateNativeRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeRegistrationFlow Create Registration Flow for Native Apps\n\n\t\t\tThis endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\n\n\t\tIf a valid provided session cookie or session token is provided, a 400 Bad Request error\n\t\twill be returned unless the URL query parameter `?refresh=true` is set.\n\n\t\tTo fetch an existing registration flow call `/self-service/registration/flows?flow=<flow_id>`.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks.\n\n\t\tIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeRegistrationFlowRequest\n\t*/\n\tCreateNativeRegistrationFlow(ctx context.Context) FrontendAPICreateNativeRegistrationFlowRequest\n\n\t// CreateNativeRegistrationFlowExecute executes the request\n\t//  @return RegistrationFlow\n\tCreateNativeRegistrationFlowExecute(r FrontendAPICreateNativeRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeSettingsFlow Create Settings Flow for Native Apps\n\n\t\t\tThis endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\n\t\tYou must provide a valid Ory Kratos Session Token for this endpoint to respond with HTTP 200 OK.\n\n\t\tTo fetch an existing settings flow call `/self-service/settings/flows?flow=<flow_id>`.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks.\n\n\t\tDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor or change the configuration.\n\n\t\tIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`session_inactive`: No Ory Session was found - sign in a user first.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeSettingsFlowRequest\n\t*/\n\tCreateNativeSettingsFlow(ctx context.Context) FrontendAPICreateNativeSettingsFlowRequest\n\n\t// CreateNativeSettingsFlowExecute executes the request\n\t//  @return SettingsFlow\n\tCreateNativeSettingsFlowExecute(r FrontendAPICreateNativeSettingsFlowRequest) (*SettingsFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeVerificationFlow Create Verification Flow for Native Apps\n\n\t\t\tThis endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\n\n\t\tTo fetch an existing verification flow call `/self-service/verification/flows?flow=<flow_id>`.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeVerificationFlowRequest\n\t*/\n\tCreateNativeVerificationFlow(ctx context.Context) FrontendAPICreateNativeVerificationFlowRequest\n\n\t// CreateNativeVerificationFlowExecute executes the request\n\t//  @return VerificationFlow\n\tCreateNativeVerificationFlowExecute(r FrontendAPICreateNativeVerificationFlowRequest) (*VerificationFlow, *http.Response, error)\n\n\t/*\n\t\t\tDisableMyOtherSessions Disable my other sessions\n\n\t\t\tCalling this endpoint invalidates all except the current session that belong to the logged-in user.\n\t\tSession data are not deleted.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIDisableMyOtherSessionsRequest\n\t*/\n\tDisableMyOtherSessions(ctx context.Context) FrontendAPIDisableMyOtherSessionsRequest\n\n\t// DisableMyOtherSessionsExecute executes the request\n\t//  @return DeleteMySessionsCount\n\tDisableMyOtherSessionsExecute(r FrontendAPIDisableMyOtherSessionsRequest) (*DeleteMySessionsCount, *http.Response, error)\n\n\t/*\n\t\t\tDisableMySession Disable one of my sessions\n\n\t\t\tCalling this endpoint invalidates the specified session. The current session cannot be revoked.\n\t\tSession data are not deleted.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the session's ID.\n\t\t\t@return FrontendAPIDisableMySessionRequest\n\t*/\n\tDisableMySession(ctx context.Context, id string) FrontendAPIDisableMySessionRequest\n\n\t// DisableMySessionExecute executes the request\n\tDisableMySessionExecute(r FrontendAPIDisableMySessionRequest) (*http.Response, error)\n\n\t/*\n\t\tExchangeSessionToken Exchange Session Token\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return FrontendAPIExchangeSessionTokenRequest\n\t*/\n\tExchangeSessionToken(ctx context.Context) FrontendAPIExchangeSessionTokenRequest\n\n\t// ExchangeSessionTokenExecute executes the request\n\t//  @return SuccessfulNativeLogin\n\tExchangeSessionTokenExecute(r FrontendAPIExchangeSessionTokenRequest) (*SuccessfulNativeLogin, *http.Response, error)\n\n\t/*\n\t\t\tGetFlowError Get User-Flow Errors\n\n\t\t\tThis endpoint returns the error associated with a user-facing self service errors.\n\n\t\tThis endpoint supports stub values to help you implement the error UI:\n\n\t\t`?id=stub:500` - returns a stub 500 (Internal Server Error) error.\n\n\t\tMore information can be found at [Ory Kratos User User Facing Error Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetFlowErrorRequest\n\t*/\n\tGetFlowError(ctx context.Context) FrontendAPIGetFlowErrorRequest\n\n\t// GetFlowErrorExecute executes the request\n\t//  @return FlowError\n\tGetFlowErrorExecute(r FrontendAPIGetFlowErrorRequest) (*FlowError, *http.Response, error)\n\n\t/*\n\t\t\tGetLoginFlow Get Login Flow\n\n\t\t\tThis endpoint returns a login flow's context with, for example, error details and other information.\n\n\t\tBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n\t\tFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\n\t\tIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n\t\tand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/login', async function (req, res) {\n\t\tconst flow = await client.getLoginFlow(req.header('cookie'), req.query['flow'])\n\n\t\tres.render('login', flow)\n\t\t})\n\t\t```\n\n\t\tThis request may fail due to several reasons. The `error.id` can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`self_service_flow_expired`: The flow is expired and you should request a new one.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetLoginFlowRequest\n\t*/\n\tGetLoginFlow(ctx context.Context) FrontendAPIGetLoginFlowRequest\n\n\t// GetLoginFlowExecute executes the request\n\t//  @return LoginFlow\n\tGetLoginFlowExecute(r FrontendAPIGetLoginFlowRequest) (*LoginFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetRecoveryFlow Get Recovery Flow\n\n\t\t\tThis endpoint returns a recovery flow's context with, for example, error details and other information.\n\n\t\tBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n\t\tFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\n\t\tIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n\t\tand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/recovery', async function (req, res) {\n\t\tconst flow = await client.getRecoveryFlow(req.header('Cookie'), req.query['flow'])\n\n\t\tres.render('recovery', flow)\n\t\t})\n\t\t```\n\n\t\tMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetRecoveryFlowRequest\n\t*/\n\tGetRecoveryFlow(ctx context.Context) FrontendAPIGetRecoveryFlowRequest\n\n\t// GetRecoveryFlowExecute executes the request\n\t//  @return RecoveryFlow\n\tGetRecoveryFlowExecute(r FrontendAPIGetRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetRegistrationFlow Get Registration Flow\n\n\t\t\tThis endpoint returns a registration flow's context with, for example, error details and other information.\n\n\t\tBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n\t\tFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\n\t\tIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n\t\tand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/registration', async function (req, res) {\n\t\tconst flow = await client.getRegistrationFlow(req.header('cookie'), req.query['flow'])\n\n\t\tres.render('registration', flow)\n\t\t})\n\t\t```\n\n\t\tThis request may fail due to several reasons. The `error.id` can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`self_service_flow_expired`: The flow is expired and you should request a new one.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetRegistrationFlowRequest\n\t*/\n\tGetRegistrationFlow(ctx context.Context) FrontendAPIGetRegistrationFlowRequest\n\n\t// GetRegistrationFlowExecute executes the request\n\t//  @return RegistrationFlow\n\tGetRegistrationFlowExecute(r FrontendAPIGetRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetSettingsFlow Get Settings Flow\n\n\t\t\tWhen accessing this endpoint through Ory Kratos' Public API you must ensure that either the Ory Kratos Session Cookie\n\t\tor the Ory Kratos Session Token are set.\n\n\t\tDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor or change the configuration.\n\n\t\tYou can access this endpoint without credentials when using Ory Kratos' Admin API.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`session_inactive`: No Ory Session was found - sign in a user first.\n\t\t`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\n\t\tidentity logged in instead.\n\n\t\tMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetSettingsFlowRequest\n\t*/\n\tGetSettingsFlow(ctx context.Context) FrontendAPIGetSettingsFlowRequest\n\n\t// GetSettingsFlowExecute executes the request\n\t//  @return SettingsFlow\n\tGetSettingsFlowExecute(r FrontendAPIGetSettingsFlowRequest) (*SettingsFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetVerificationFlow Get Verification Flow\n\n\t\t\tThis endpoint returns a verification flow's context with, for example, error details and other information.\n\n\t\tBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n\t\tFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\n\t\tIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n\t\tand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/recovery', async function (req, res) {\n\t\tconst flow = await client.getVerificationFlow(req.header('cookie'), req.query['flow'])\n\n\t\tres.render('verification', flow)\n\t\t})\n\t\t```\n\n\t\tMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetVerificationFlowRequest\n\t*/\n\tGetVerificationFlow(ctx context.Context) FrontendAPIGetVerificationFlowRequest\n\n\t// GetVerificationFlowExecute executes the request\n\t//  @return VerificationFlow\n\tGetVerificationFlowExecute(r FrontendAPIGetVerificationFlowRequest) (*VerificationFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetWebAuthnJavaScript Get WebAuthn JavaScript\n\n\t\t\tThis endpoint provides JavaScript which is needed in order to perform WebAuthn login and registration.\n\n\t\tIf you are building a JavaScript Browser App (e.g. in ReactJS or AngularJS) you will need to load this file:\n\n\t\t```html\n\t\t<script src=\"https://public-kratos.example.org/.well-known/ory/webauthn.js\" type=\"script\" async />\n\t\t```\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetWebAuthnJavaScriptRequest\n\t*/\n\tGetWebAuthnJavaScript(ctx context.Context) FrontendAPIGetWebAuthnJavaScriptRequest\n\n\t// GetWebAuthnJavaScriptExecute executes the request\n\t//  @return string\n\tGetWebAuthnJavaScriptExecute(r FrontendAPIGetWebAuthnJavaScriptRequest) (string, *http.Response, error)\n\n\t/*\n\t\t\tListMySessions Get My Active Sessions\n\n\t\t\tThis endpoints returns all other active sessions that belong to the logged-in user.\n\t\tThe current session can be retrieved by calling the `/sessions/whoami` endpoint.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIListMySessionsRequest\n\t*/\n\tListMySessions(ctx context.Context) FrontendAPIListMySessionsRequest\n\n\t// ListMySessionsExecute executes the request\n\t//  @return []Session\n\tListMySessionsExecute(r FrontendAPIListMySessionsRequest) ([]Session, *http.Response, error)\n\n\t/*\n\t\t\tPerformNativeLogout Perform Logout for Native Apps\n\n\t\t\tUse this endpoint to log out an identity using an Ory Session Token. If the Ory Session Token was successfully\n\t\trevoked, the server returns a 204 No Content response. A 204 No Content response is also sent when\n\t\tthe Ory Session Token has been revoked already before.\n\n\t\tIf the Ory Session Token is malformed or does not exist a 403 Forbidden response will be returned.\n\n\t\tThis endpoint does not remove any HTTP\n\t\tCookies - use the Browser-Based Self-Service Logout Flow instead.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIPerformNativeLogoutRequest\n\t*/\n\tPerformNativeLogout(ctx context.Context) FrontendAPIPerformNativeLogoutRequest\n\n\t// PerformNativeLogoutExecute executes the request\n\tPerformNativeLogoutExecute(r FrontendAPIPerformNativeLogoutRequest) (*http.Response, error)\n\n\t/*\n\t\t\tToSession Check Who the Current HTTP Session Belongs To\n\n\t\t\tUses the HTTP Headers in the GET request to determine (e.g. by using checking the cookies) who is authenticated.\n\t\tReturns a session object in the body or 401 if the credentials are invalid or no credentials were sent.\n\t\tWhen the request it successful it adds the user ID to the 'X-Kratos-Authenticated-Identity-Id' header\n\t\tin the response.\n\n\t\tIf you call this endpoint from a server-side application, you must forward the HTTP Cookie Header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/protected-endpoint', async function (req, res) {\n\t\tconst session = await client.toSession(undefined, req.header('cookie'))\n\n\t\tconsole.log(session)\n\t\t})\n\t\t```\n\n\t\tWhen calling this endpoint from a non-browser application (e.g. mobile app) you must include the session token:\n\n\t\t```js\n\t\tpseudo-code example\n\t\t...\n\t\tconst session = await client.toSession(\"the-session-token\")\n\n\t\tconsole.log(session)\n\t\t```\n\n\t\tWhen using a token template, the token is included in the `tokenized` field of the session.\n\n\t\t```js\n\t\tpseudo-code example\n\t\t...\n\t\tconst session = await client.toSession(\"the-session-token\", { tokenize_as: \"example-jwt-template\" })\n\n\t\tconsole.log(session.tokenized) // The JWT\n\t\t```\n\n\t\tDepending on your configuration this endpoint might return a 403 status code if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor or change the configuration.\n\n\t\tThis endpoint is useful for:\n\n\t\tAJAX calls. Remember to send credentials and set up CORS correctly!\n\t\tReverse proxies and API Gateways\n\t\tServer-side calls - use the `X-Session-Token` header!\n\n\t\tThis endpoint authenticates users by checking:\n\n\t\tif the `Cookie` HTTP header was set containing an Ory Kratos Session Cookie;\n\t\tif the `Authorization: bearer <ory-session-token>` HTTP header was set with a valid Ory Kratos Session Token;\n\t\tif the `X-Session-Token` HTTP header was set with a valid Ory Kratos Session Token.\n\n\t\tIf none of these headers are set or the cookie or token are invalid, the endpoint returns a HTTP 401 status code.\n\n\t\tAs explained above, this request may fail due to several reasons. The `error.id` can be one of:\n\n\t\t`session_inactive`: No active session was found in the request (e.g. no Ory Session Cookie / Ory Session Token).\n\t\t`session_aal2_required`: An active session was found but it does not fulfil the Authenticator Assurance Level, implying that the session must (e.g.) authenticate the second factor.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIToSessionRequest\n\t*/\n\tToSession(ctx context.Context) FrontendAPIToSessionRequest\n\n\t// ToSessionExecute executes the request\n\t//  @return Session\n\tToSessionExecute(r FrontendAPIToSessionRequest) (*Session, *http.Response, error)\n\n\t/*\n\t\t\tUpdateFedcmFlow Submit a FedCM token\n\n\t\t\tUse this endpoint to submit a token from a FedCM provider through\n\t\t`navigator.credentials.get` and log the user in. The parameters from\n\t\t`navigator.credentials.get` must have come from `GET\n\t\tself-service/fed-cm/parameters`.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateFedcmFlowRequest\n\t*/\n\tUpdateFedcmFlow(ctx context.Context) FrontendAPIUpdateFedcmFlowRequest\n\n\t// UpdateFedcmFlowExecute executes the request\n\t//  @return SuccessfulNativeLogin\n\tUpdateFedcmFlowExecute(r FrontendAPIUpdateFedcmFlowRequest) (*SuccessfulNativeLogin, *http.Response, error)\n\n\t/*\n\t\t\tUpdateLoginFlow Submit a Login Flow\n\n\t\t\tUse this endpoint to complete a login flow. This endpoint\n\t\tbehaves differently for API and browser flows.\n\n\t\tAPI flows expect `application/json` to be sent in the body and responds with\n\t\tHTTP 200 and a application/json body with the session token on success;\n\t\tHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\n\t\tHTTP 400 on form validation errors.\n\n\t\tBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\n\t\ta HTTP 303 redirect to the post/after login URL or the `return_to` value if it was set and if the login succeeded;\n\t\ta HTTP 303 redirect to the login UI URL with the flow ID containing the validation errors otherwise.\n\n\t\tBrowser flows with an accept header of `application/json` will not redirect but instead respond with\n\t\tHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\n\t\tHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\n\t\tHTTP 400 on form validation errors.\n\n\t\tIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\t\t`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\n\t\tMost likely used in Social Sign In flows.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateLoginFlowRequest\n\t*/\n\tUpdateLoginFlow(ctx context.Context) FrontendAPIUpdateLoginFlowRequest\n\n\t// UpdateLoginFlowExecute executes the request\n\t//  @return SuccessfulNativeLogin\n\tUpdateLoginFlowExecute(r FrontendAPIUpdateLoginFlowRequest) (*SuccessfulNativeLogin, *http.Response, error)\n\n\t/*\n\t\t\tUpdateLogoutFlow Update Logout Flow\n\n\t\t\tThis endpoint logs out an identity in a self-service manner.\n\n\t\tIf the `Accept` HTTP header is not set to `application/json`, the browser will be redirected (HTTP 303 See Other)\n\t\tto the `return_to` parameter of the initial request or fall back to `urls.default_return_to`.\n\n\t\tIf the `Accept` HTTP header is set to `application/json`, a 204 No Content response\n\t\twill be sent on successful logout instead.\n\n\t\tThis endpoint is NOT INTENDED for API clients and only works\n\t\twith browsers (Chrome, Firefox, ...). For API clients you can\n\t\tcall the `/self-service/logout/api` URL directly with the Ory Session Token.\n\n\t\tMore information can be found at [Ory Kratos User Logout Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-logout).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateLogoutFlowRequest\n\t*/\n\tUpdateLogoutFlow(ctx context.Context) FrontendAPIUpdateLogoutFlowRequest\n\n\t// UpdateLogoutFlowExecute executes the request\n\tUpdateLogoutFlowExecute(r FrontendAPIUpdateLogoutFlowRequest) (*http.Response, error)\n\n\t/*\n\t\t\tUpdateRecoveryFlow Update Recovery Flow\n\n\t\t\tUse this endpoint to update a recovery flow. This endpoint\n\t\tbehaves differently for API and browser flows and has several states:\n\n\t\t`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\n\t\tand works with API- and Browser-initiated flows.\n\t\tFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid.\n\t\tand a HTTP 303 See Other redirect with a fresh recovery flow if the flow was otherwise invalid (e.g. expired).\n\t\tFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Recovery UI URL with the Recovery Flow ID appended.\n\t\t`sent_email` is the success state after `choose_method` for the `link` method and allows the user to request another recovery email. It\n\t\tworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n\t\t`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a recovery link\")\n\t\tdoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n\t\t(if the link was valid) and instructs the user to update their password, or a redirect to the Recover UI URL with\n\t\ta new Recovery Flow ID which contains an error message that the recovery link was invalid.\n\n\t\tMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateRecoveryFlowRequest\n\t*/\n\tUpdateRecoveryFlow(ctx context.Context) FrontendAPIUpdateRecoveryFlowRequest\n\n\t// UpdateRecoveryFlowExecute executes the request\n\t//  @return RecoveryFlow\n\tUpdateRecoveryFlowExecute(r FrontendAPIUpdateRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error)\n\n\t/*\n\t\t\tUpdateRegistrationFlow Update Registration Flow\n\n\t\t\tUse this endpoint to complete a registration flow by sending an identity's traits and password. This endpoint\n\t\tbehaves differently for API and browser flows.\n\n\t\tAPI flows expect `application/json` to be sent in the body and respond with\n\t\tHTTP 200 and a application/json body with the created identity success - if the session hook is configured the\n\t\t`session` and `session_token` will also be included;\n\t\tHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\n\t\tHTTP 400 on form validation errors.\n\n\t\tBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\n\t\ta HTTP 303 redirect to the post/after registration URL or the `return_to` value if it was set and if the registration succeeded;\n\t\ta HTTP 303 redirect to the registration UI URL with the flow ID containing the validation errors otherwise.\n\n\t\tBrowser flows with an accept header of `application/json` will not redirect but instead respond with\n\t\tHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\n\t\tHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\n\t\tHTTP 400 on form validation errors.\n\n\t\tIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\t\t`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\n\t\tMost likely used in Social Sign In flows.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateRegistrationFlowRequest\n\t*/\n\tUpdateRegistrationFlow(ctx context.Context) FrontendAPIUpdateRegistrationFlowRequest\n\n\t// UpdateRegistrationFlowExecute executes the request\n\t//  @return SuccessfulNativeRegistration\n\tUpdateRegistrationFlowExecute(r FrontendAPIUpdateRegistrationFlowRequest) (*SuccessfulNativeRegistration, *http.Response, error)\n\n\t/*\n\t\t\tUpdateSettingsFlow Complete Settings Flow\n\n\t\t\tUse this endpoint to complete a settings flow by sending an identity's updated password. This endpoint\n\t\tbehaves differently for API and browser flows.\n\n\t\tAPI-initiated flows expect `application/json` to be sent in the body and respond with\n\t\tHTTP 200 and an application/json body with the session token on success;\n\t\tHTTP 303 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\n\t\tHTTP 400 on form validation errors.\n\t\tHTTP 401 when the endpoint is called without a valid session token.\n\t\tHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\n\t\tImplies that the user needs to re-authenticate.\n\n\t\tBrowser flows without HTTP Header `Accept` or with `Accept: text/*` respond with\n\t\ta HTTP 303 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\n\t\ta HTTP 303 redirect to the Settings UI URL with the flow ID containing the validation errors otherwise.\n\t\ta HTTP 303 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\n\n\t\tBrowser flows with HTTP Header `Accept: application/json` respond with\n\t\tHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\n\t\tHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\n\t\tHTTP 401 when the endpoint is called without a valid session cookie.\n\t\tHTTP 403 when the page is accessed without a session cookie or the session's AAL is too low.\n\t\tHTTP 400 on form validation errors.\n\n\t\tDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n\n\t\tIf this endpoint is called with a `Accept: application/json` HTTP header, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_refresh_required`: The identity requested to change something that needs a privileged session. Redirect\n\t\tthe identity to the login init endpoint with query parameters `?refresh=true&return_to=<the-current-browser-url>`,\n\t\tor initiate a refresh login flow otherwise.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`session_inactive`: No Ory Session was found - sign in a user first.\n\t\t`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\n\t\tidentity logged in instead.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\t\t`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\n\t\tMost likely used in Social Sign In flows.\n\n\t\tMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateSettingsFlowRequest\n\t*/\n\tUpdateSettingsFlow(ctx context.Context) FrontendAPIUpdateSettingsFlowRequest\n\n\t// UpdateSettingsFlowExecute executes the request\n\t//  @return SettingsFlow\n\tUpdateSettingsFlowExecute(r FrontendAPIUpdateSettingsFlowRequest) (*SettingsFlow, *http.Response, error)\n\n\t/*\n\t\t\tUpdateVerificationFlow Complete Verification Flow\n\n\t\t\tUse this endpoint to complete a verification flow. This endpoint\n\t\tbehaves differently for API and browser flows and has several states:\n\n\t\t`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\n\t\tand works with API- and Browser-initiated flows.\n\t\tFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\n\t\tand a HTTP 303 See Other redirect with a fresh verification flow if the flow was otherwise invalid (e.g. expired).\n\t\tFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Verification UI URL with the Verification Flow ID appended.\n\t\t`sent_email` is the success state after `choose_method` when using the `link` method and allows the user to request another verification email. It\n\t\tworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n\t\t`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a verification link\")\n\t\tdoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n\t\t(if the link was valid) and instructs the user to update their password, or a redirect to the Verification UI URL with\n\t\ta new Verification Flow ID which contains an error message that the verification link was invalid.\n\n\t\tMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateVerificationFlowRequest\n\t*/\n\tUpdateVerificationFlow(ctx context.Context) FrontendAPIUpdateVerificationFlowRequest\n\n\t// UpdateVerificationFlowExecute executes the request\n\t//  @return VerificationFlow\n\tUpdateVerificationFlowExecute(r FrontendAPIUpdateVerificationFlowRequest) (*VerificationFlow, *http.Response, error)\n}\n\n// FrontendAPIService FrontendAPI service\ntype FrontendAPIService service\n\ntype FrontendAPICreateBrowserLoginFlowRequest struct {\n\tctx            context.Context\n\tApiService     FrontendAPI\n\trefresh        *bool\n\taal            *string\n\treturnTo       *string\n\tcookie         *string\n\tloginChallenge *string\n\torganization   *string\n\tvia            *string\n\tidentitySchema *string\n}\n\n// Refresh a login session  If set to true, this will refresh an existing login session by asking the user to sign in again. This will reset the authenticated_at time of the session.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Refresh(refresh bool) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.refresh = &refresh\n\treturn r\n}\n\n// Request a Specific AuthenticationMethod Assurance Level  Use this parameter to upgrade an existing session&#39;s authenticator assurance level (AAL). This allows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password, the AAL is 1. If you wish to \\&quot;upgrade\\&quot; the session&#39;s security by asking the user to perform TOTP / WebAuth/ ... you would set this to \\&quot;aal2\\&quot;.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Aal(aal string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.aal = &aal\n\treturn r\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Cookie(cookie string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\n// An optional Hydra login challenge. If present, Kratos will cooperate with Ory Hydra to act as an OAuth2 identity provider.  The value for this parameter comes from &#x60;login_challenge&#x60; URL Query parameter sent to your application (e.g. &#x60;/login?login_challenge&#x3D;abcde&#x60;).\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) LoginChallenge(loginChallenge string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.loginChallenge = &loginChallenge\n\treturn r\n}\n\n// An optional organization ID that should be used for logging this user in. This parameter is only effective in the Ory Network.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Organization(organization string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.organization = &organization\n\treturn r\n}\n\n// Via should contain the identity&#39;s credential the code should be sent to. Only relevant in aal2 flows.  DEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice of MFA credentials to choose from to perform the second factor instead.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Via(via string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.via = &via\n\treturn r\n}\n\n// An optional identity schema to use for the login flow.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) IdentitySchema(identitySchema string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.identitySchema = &identitySchema\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Execute() (*LoginFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserLoginFlowExecute(r)\n}\n\n/*\nCreateBrowserLoginFlow Create Login Flow for Browsers\n\nThis endpoint initializes a browser-based user login flow. This endpoint will set the appropriate\ncookies and anti-CSRF measures required for browser-based flows.\n\nIf this endpoint is opened as a link in the browser, it will be redirected to\n`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\n`?refresh=true` was set.\n\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\nThe optional query parameter login_challenge is set when using Kratos with\nHydra in an OAuth2 flow. See the oauth2_provider.url configuration\noption.\n\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserLoginFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserLoginFlow(ctx context.Context) FrontendAPICreateBrowserLoginFlowRequest {\n\treturn FrontendAPICreateBrowserLoginFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return LoginFlow\nfunc (a *FrontendAPIService) CreateBrowserLoginFlowExecute(r FrontendAPICreateBrowserLoginFlowRequest) (*LoginFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *LoginFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserLoginFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/login/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.refresh != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"refresh\", r.refresh, \"form\", \"\")\n\t}\n\tif r.aal != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"aal\", r.aal, \"form\", \"\")\n\t}\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\tif r.loginChallenge != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"login_challenge\", r.loginChallenge, \"form\", \"\")\n\t}\n\tif r.organization != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization\", r.organization, \"form\", \"\")\n\t}\n\tif r.via != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"via\", r.via, \"form\", \"\")\n\t}\n\tif r.identitySchema != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identity_schema\", r.identitySchema, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserLogoutFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tcookie     *string\n\treturnTo   *string\n}\n\n// HTTP Cookies  If you call this endpoint from a backend, please include the original Cookie header in the request.\nfunc (r FrontendAPICreateBrowserLogoutFlowRequest) Cookie(cookie string) FrontendAPICreateBrowserLogoutFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\n// Return to URL  The URL to which the browser should be redirected to after the logout has been performed.\nfunc (r FrontendAPICreateBrowserLogoutFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserLogoutFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserLogoutFlowRequest) Execute() (*LogoutFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserLogoutFlowExecute(r)\n}\n\n/*\nCreateBrowserLogoutFlow Create a Logout URL for Browsers\n\nThis endpoint initializes a browser-based user logout flow and a URL which can be used to log out the user.\n\nThis endpoint is NOT INTENDED for API clients and only works\nwith browsers (Chrome, Firefox, ...). For API clients you can\ncall the `/self-service/logout/api` URL directly with the Ory Session Token.\n\nThe URL is only valid for the currently signed in user. If no user is signed in, this endpoint returns\na 401 error.\n\nWhen calling this endpoint from a backend, please ensure to properly forward the HTTP cookies.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserLogoutFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserLogoutFlow(ctx context.Context) FrontendAPICreateBrowserLogoutFlowRequest {\n\treturn FrontendAPICreateBrowserLogoutFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return LogoutFlow\nfunc (a *FrontendAPIService) CreateBrowserLogoutFlowExecute(r FrontendAPICreateBrowserLogoutFlowRequest) (*LogoutFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *LogoutFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserLogoutFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/logout/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 500 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserRecoveryFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\treturnTo   *string\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserRecoveryFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserRecoveryFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserRecoveryFlowRequest) Execute() (*RecoveryFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserRecoveryFlowExecute(r)\n}\n\n/*\nCreateBrowserRecoveryFlow Create Recovery Flow for Browsers\n\nThis endpoint initializes a browser-based account recovery flow. Once initialized, the browser will be redirected to\n`selfservice.flows.recovery.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\nexists, the browser is returned to the configured return URL.\n\nIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects\nor a 400 bad request error if the user is already authenticated.\n\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserRecoveryFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserRecoveryFlow(ctx context.Context) FrontendAPICreateBrowserRecoveryFlowRequest {\n\treturn FrontendAPICreateBrowserRecoveryFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryFlow\nfunc (a *FrontendAPIService) CreateBrowserRecoveryFlowExecute(r FrontendAPICreateBrowserRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserRecoveryFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/recovery/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserRegistrationFlowRequest struct {\n\tctx                       context.Context\n\tApiService                FrontendAPI\n\treturnTo                  *string\n\tloginChallenge            *string\n\tafterVerificationReturnTo *string\n\torganization              *string\n\tidentitySchema            *string\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// Ory OAuth 2.0 Login Challenge.  If set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.  The value for this parameter comes from &#x60;login_challenge&#x60; URL Query parameter sent to your application (e.g. &#x60;/registration?login_challenge&#x3D;abcde&#x60;).  This feature is compatible with Ory Hydra when not running on the Ory Network.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) LoginChallenge(loginChallenge string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.loginChallenge = &loginChallenge\n\treturn r\n}\n\n// The URL to return the browser to after the verification flow was completed.  After the registration flow is completed, the user will be sent a verification email. Upon completing the verification flow, this URL will be used to override the default &#x60;selfservice.flows.verification.after.default_redirect_to&#x60; value.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) AfterVerificationReturnTo(afterVerificationReturnTo string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.afterVerificationReturnTo = &afterVerificationReturnTo\n\treturn r\n}\n\n// An optional organization ID that should be used to register this user. This parameter is only effective in the Ory Network.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) Organization(organization string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.organization = &organization\n\treturn r\n}\n\n// An optional identity schema to use for the registration flow.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) IdentitySchema(identitySchema string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.identitySchema = &identitySchema\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) Execute() (*RegistrationFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserRegistrationFlowExecute(r)\n}\n\n/*\nCreateBrowserRegistrationFlow Create Registration Flow for Browsers\n\nThis endpoint initializes a browser-based user registration flow. This endpoint will set the appropriate\ncookies and anti-CSRF measures required for browser-based flows.\n\nIf this endpoint is opened as a link in the browser, it will be redirected to\n`selfservice.flows.registration.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\nexists already, the browser will be redirected to `urls.default_redirect_url`.\n\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\nIf this endpoint is called via an AJAX request, the response contains the registration flow without a redirect.\n\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserRegistrationFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserRegistrationFlow(ctx context.Context) FrontendAPICreateBrowserRegistrationFlowRequest {\n\treturn FrontendAPICreateBrowserRegistrationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RegistrationFlow\nfunc (a *FrontendAPIService) CreateBrowserRegistrationFlowExecute(r FrontendAPICreateBrowserRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RegistrationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserRegistrationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/registration/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\tif r.loginChallenge != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"login_challenge\", r.loginChallenge, \"form\", \"\")\n\t}\n\tif r.afterVerificationReturnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"after_verification_return_to\", r.afterVerificationReturnTo, \"form\", \"\")\n\t}\n\tif r.organization != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization\", r.organization, \"form\", \"\")\n\t}\n\tif r.identitySchema != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identity_schema\", r.identitySchema, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserSettingsFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\treturnTo   *string\n\tcookie     *string\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserSettingsFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserSettingsFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPICreateBrowserSettingsFlowRequest) Cookie(cookie string) FrontendAPICreateBrowserSettingsFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserSettingsFlowRequest) Execute() (*SettingsFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserSettingsFlowExecute(r)\n}\n\n/*\nCreateBrowserSettingsFlow Create Settings Flow for Browsers\n\nThis endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\nOry Kratos Session Cookie is included in the request, a login flow will be initialized.\n\nIf this endpoint is opened as a link in the browser, it will be redirected to\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid user session\nwas set, the browser will be redirected to the login endpoint.\n\nIf this endpoint is called via an AJAX request, the response contains the settings flow without any redirects\nor a 401 forbidden error if no valid session was set.\n\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`session_inactive`: No Ory Session was found - sign in a user first.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\nMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserSettingsFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserSettingsFlow(ctx context.Context) FrontendAPICreateBrowserSettingsFlowRequest {\n\treturn FrontendAPICreateBrowserSettingsFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SettingsFlow\nfunc (a *FrontendAPIService) CreateBrowserSettingsFlowExecute(r FrontendAPICreateBrowserSettingsFlowRequest) (*SettingsFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SettingsFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserSettingsFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/settings/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserVerificationFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\treturnTo   *string\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserVerificationFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserVerificationFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserVerificationFlowExecute(r)\n}\n\n/*\nCreateBrowserVerificationFlow Create Verification Flow for Browser Clients\n\nThis endpoint initializes a browser-based account verification flow. Once initialized, the browser will be redirected to\n`selfservice.flows.verification.ui_url` with the flow ID set as the query parameter `?flow=`.\n\nIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects.\n\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\n\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserVerificationFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserVerificationFlow(ctx context.Context) FrontendAPICreateBrowserVerificationFlowRequest {\n\treturn FrontendAPICreateBrowserVerificationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return VerificationFlow\nfunc (a *FrontendAPIService) CreateBrowserVerificationFlowExecute(r FrontendAPICreateBrowserVerificationFlowRequest) (*VerificationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *VerificationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserVerificationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/verification/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateFedcmFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n}\n\nfunc (r FrontendAPICreateFedcmFlowRequest) Execute() (*CreateFedcmFlowResponse, *http.Response, error) {\n\treturn r.ApiService.CreateFedcmFlowExecute(r)\n}\n\n/*\nCreateFedcmFlow Get FedCM Parameters\n\nThis endpoint returns a list of all available FedCM providers. It is only supported on the Ory Network.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateFedcmFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateFedcmFlow(ctx context.Context) FrontendAPICreateFedcmFlowRequest {\n\treturn FrontendAPICreateFedcmFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return CreateFedcmFlowResponse\nfunc (a *FrontendAPIService) CreateFedcmFlowExecute(r FrontendAPICreateFedcmFlowRequest) (*CreateFedcmFlowResponse, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *CreateFedcmFlowResponse\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateFedcmFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/fed-cm/parameters\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeLoginFlowRequest struct {\n\tctx                            context.Context\n\tApiService                     FrontendAPI\n\trefresh                        *bool\n\taal                            *string\n\txSessionToken                  *string\n\treturnSessionTokenExchangeCode *bool\n\treturnTo                       *string\n\torganization                   *string\n\tvia                            *string\n\tidentitySchema                 *string\n}\n\n// Refresh a login session  If set to true, this will refresh an existing login session by asking the user to sign in again. This will reset the authenticated_at time of the session.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Refresh(refresh bool) FrontendAPICreateNativeLoginFlowRequest {\n\tr.refresh = &refresh\n\treturn r\n}\n\n// Request a Specific AuthenticationMethod Assurance Level  Use this parameter to upgrade an existing session&#39;s authenticator assurance level (AAL). This allows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password, the AAL is 1. If you wish to \\&quot;upgrade\\&quot; the session&#39;s security by asking the user to perform TOTP / WebAuth/ ... you would set this to \\&quot;aal2\\&quot;.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Aal(aal string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.aal = &aal\n\treturn r\n}\n\n// The Session Token of the Identity performing the settings flow.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) XSessionToken(xSessionToken string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token after the login flow has been completed.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) ReturnSessionTokenExchangeCode(returnSessionTokenExchangeCode bool) FrontendAPICreateNativeLoginFlowRequest {\n\tr.returnSessionTokenExchangeCode = &returnSessionTokenExchangeCode\n\treturn r\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) ReturnTo(returnTo string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// An optional organization ID that should be used for logging this user in. This parameter is only effective in the Ory Network.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Organization(organization string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.organization = &organization\n\treturn r\n}\n\n// Via should contain the identity&#39;s credential the code should be sent to. Only relevant in aal2 flows.  DEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice of MFA credentials to choose from to perform the second factor instead.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Via(via string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.via = &via\n\treturn r\n}\n\n// An optional identity schema to use for the login flow.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) IdentitySchema(identitySchema string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.identitySchema = &identitySchema\n\treturn r\n}\n\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Execute() (*LoginFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeLoginFlowExecute(r)\n}\n\n/*\nCreateNativeLoginFlow Create Login Flow for Native Apps\n\nThis endpoint initiates a login flow for native apps that do not use a browser, such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\nwill be returned unless the URL query parameter `?refresh=true` is set.\n\nTo fetch an existing login flow call `/self-service/login/flows?flow=<flow_id>`.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks, including CSRF login attacks.\n\nIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeLoginFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeLoginFlow(ctx context.Context) FrontendAPICreateNativeLoginFlowRequest {\n\treturn FrontendAPICreateNativeLoginFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return LoginFlow\nfunc (a *FrontendAPIService) CreateNativeLoginFlowExecute(r FrontendAPICreateNativeLoginFlowRequest) (*LoginFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *LoginFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeLoginFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/login/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.refresh != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"refresh\", r.refresh, \"form\", \"\")\n\t}\n\tif r.aal != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"aal\", r.aal, \"form\", \"\")\n\t}\n\tif r.returnSessionTokenExchangeCode != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_session_token_exchange_code\", r.returnSessionTokenExchangeCode, \"form\", \"\")\n\t}\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\tif r.organization != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization\", r.organization, \"form\", \"\")\n\t}\n\tif r.via != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"via\", r.via, \"form\", \"\")\n\t}\n\tif r.identitySchema != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identity_schema\", r.identitySchema, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeRecoveryFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n}\n\nfunc (r FrontendAPICreateNativeRecoveryFlowRequest) Execute() (*RecoveryFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeRecoveryFlowExecute(r)\n}\n\n/*\nCreateNativeRecoveryFlow Create Recovery Flow for Native Apps\n\nThis endpoint initiates a recovery flow for API clients such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error.\n\nOn an existing recovery flow, use the `getRecoveryFlow` API endpoint.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeRecoveryFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeRecoveryFlow(ctx context.Context) FrontendAPICreateNativeRecoveryFlowRequest {\n\treturn FrontendAPICreateNativeRecoveryFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryFlow\nfunc (a *FrontendAPIService) CreateNativeRecoveryFlowExecute(r FrontendAPICreateNativeRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeRecoveryFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/recovery/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeRegistrationFlowRequest struct {\n\tctx                            context.Context\n\tApiService                     FrontendAPI\n\treturnSessionTokenExchangeCode *bool\n\treturnTo                       *string\n\torganization                   *string\n\tidentitySchema                 *string\n}\n\n// EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token after the login flow has been completed.\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) ReturnSessionTokenExchangeCode(returnSessionTokenExchangeCode bool) FrontendAPICreateNativeRegistrationFlowRequest {\n\tr.returnSessionTokenExchangeCode = &returnSessionTokenExchangeCode\n\treturn r\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) ReturnTo(returnTo string) FrontendAPICreateNativeRegistrationFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// An optional organization ID that should be used to register this user. This parameter is only effective in the Ory Network.\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) Organization(organization string) FrontendAPICreateNativeRegistrationFlowRequest {\n\tr.organization = &organization\n\treturn r\n}\n\n// An optional identity schema to use for the registration flow.\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) IdentitySchema(identitySchema string) FrontendAPICreateNativeRegistrationFlowRequest {\n\tr.identitySchema = &identitySchema\n\treturn r\n}\n\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) Execute() (*RegistrationFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeRegistrationFlowExecute(r)\n}\n\n/*\nCreateNativeRegistrationFlow Create Registration Flow for Native Apps\n\nThis endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\nwill be returned unless the URL query parameter `?refresh=true` is set.\n\nTo fetch an existing registration flow call `/self-service/registration/flows?flow=<flow_id>`.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeRegistrationFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeRegistrationFlow(ctx context.Context) FrontendAPICreateNativeRegistrationFlowRequest {\n\treturn FrontendAPICreateNativeRegistrationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RegistrationFlow\nfunc (a *FrontendAPIService) CreateNativeRegistrationFlowExecute(r FrontendAPICreateNativeRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RegistrationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeRegistrationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/registration/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnSessionTokenExchangeCode != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_session_token_exchange_code\", r.returnSessionTokenExchangeCode, \"form\", \"\")\n\t}\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\tif r.organization != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization\", r.organization, \"form\", \"\")\n\t}\n\tif r.identitySchema != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identity_schema\", r.identitySchema, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeSettingsFlowRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\txSessionToken *string\n}\n\n// The Session Token of the Identity performing the settings flow.\nfunc (r FrontendAPICreateNativeSettingsFlowRequest) XSessionToken(xSessionToken string) FrontendAPICreateNativeSettingsFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\nfunc (r FrontendAPICreateNativeSettingsFlowRequest) Execute() (*SettingsFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeSettingsFlowExecute(r)\n}\n\n/*\nCreateNativeSettingsFlow Create Settings Flow for Native Apps\n\nThis endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\nYou must provide a valid Ory Kratos Session Token for this endpoint to respond with HTTP 200 OK.\n\nTo fetch an existing settings flow call `/self-service/settings/flows?flow=<flow_id>`.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor or change the configuration.\n\nIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`session_inactive`: No Ory Session was found - sign in a user first.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeSettingsFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeSettingsFlow(ctx context.Context) FrontendAPICreateNativeSettingsFlowRequest {\n\treturn FrontendAPICreateNativeSettingsFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SettingsFlow\nfunc (a *FrontendAPIService) CreateNativeSettingsFlowExecute(r FrontendAPICreateNativeSettingsFlowRequest) (*SettingsFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SettingsFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeSettingsFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/settings/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeVerificationFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\treturnTo   *string\n}\n\n// A URL contained in the return_to key of the verification flow. This piece of data has no effect on the actual logic of the flow and is purely informational.\nfunc (r FrontendAPICreateNativeVerificationFlowRequest) ReturnTo(returnTo string) FrontendAPICreateNativeVerificationFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r FrontendAPICreateNativeVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeVerificationFlowExecute(r)\n}\n\n/*\nCreateNativeVerificationFlow Create Verification Flow for Native Apps\n\nThis endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\n\nTo fetch an existing verification flow call `/self-service/verification/flows?flow=<flow_id>`.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeVerificationFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeVerificationFlow(ctx context.Context) FrontendAPICreateNativeVerificationFlowRequest {\n\treturn FrontendAPICreateNativeVerificationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return VerificationFlow\nfunc (a *FrontendAPIService) CreateNativeVerificationFlowExecute(r FrontendAPICreateNativeVerificationFlowRequest) (*VerificationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *VerificationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeVerificationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/verification/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIDisableMyOtherSessionsRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\txSessionToken *string\n\tcookie        *string\n}\n\n// Set the Session Token when calling from non-browser clients. A session token has a format of &#x60;MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj&#x60;.\nfunc (r FrontendAPIDisableMyOtherSessionsRequest) XSessionToken(xSessionToken string) FrontendAPIDisableMyOtherSessionsRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that scenario you must include the HTTP Cookie Header which originally was included in the request to your server. An example of a session in the HTTP Cookie Header is: &#x60;ory_kratos_session&#x3D;a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f&#x3D;&#x3D;&#x60;.  It is ok if more than one cookie are included here as all other cookies will be ignored.\nfunc (r FrontendAPIDisableMyOtherSessionsRequest) Cookie(cookie string) FrontendAPIDisableMyOtherSessionsRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIDisableMyOtherSessionsRequest) Execute() (*DeleteMySessionsCount, *http.Response, error) {\n\treturn r.ApiService.DisableMyOtherSessionsExecute(r)\n}\n\n/*\nDisableMyOtherSessions Disable my other sessions\n\nCalling this endpoint invalidates all except the current session that belong to the logged-in user.\nSession data are not deleted.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIDisableMyOtherSessionsRequest\n*/\nfunc (a *FrontendAPIService) DisableMyOtherSessions(ctx context.Context) FrontendAPIDisableMyOtherSessionsRequest {\n\treturn FrontendAPIDisableMyOtherSessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return DeleteMySessionsCount\nfunc (a *FrontendAPIService) DisableMyOtherSessionsExecute(r FrontendAPIDisableMyOtherSessionsRequest) (*DeleteMySessionsCount, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodDelete\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *DeleteMySessionsCount\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.DisableMyOtherSessions\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIDisableMySessionRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\tid            string\n\txSessionToken *string\n\tcookie        *string\n}\n\n// Set the Session Token when calling from non-browser clients. A session token has a format of &#x60;MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj&#x60;.\nfunc (r FrontendAPIDisableMySessionRequest) XSessionToken(xSessionToken string) FrontendAPIDisableMySessionRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that scenario you must include the HTTP Cookie Header which originally was included in the request to your server. An example of a session in the HTTP Cookie Header is: &#x60;ory_kratos_session&#x3D;a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f&#x3D;&#x3D;&#x60;.  It is ok if more than one cookie are included here as all other cookies will be ignored.\nfunc (r FrontendAPIDisableMySessionRequest) Cookie(cookie string) FrontendAPIDisableMySessionRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIDisableMySessionRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DisableMySessionExecute(r)\n}\n\n/*\nDisableMySession Disable one of my sessions\n\nCalling this endpoint invalidates the specified session. The current session cannot be revoked.\nSession data are not deleted.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the session's ID.\n\t@return FrontendAPIDisableMySessionRequest\n*/\nfunc (a *FrontendAPIService) DisableMySession(ctx context.Context, id string) FrontendAPIDisableMySessionRequest {\n\treturn FrontendAPIDisableMySessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\nfunc (a *FrontendAPIService) DisableMySessionExecute(r FrontendAPIDisableMySessionRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.DisableMySession\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype FrontendAPIExchangeSessionTokenRequest struct {\n\tctx          context.Context\n\tApiService   FrontendAPI\n\tinitCode     *string\n\treturnToCode *string\n}\n\n// The part of the code return when initializing the flow.\nfunc (r FrontendAPIExchangeSessionTokenRequest) InitCode(initCode string) FrontendAPIExchangeSessionTokenRequest {\n\tr.initCode = &initCode\n\treturn r\n}\n\n// The part of the code returned by the return_to URL.\nfunc (r FrontendAPIExchangeSessionTokenRequest) ReturnToCode(returnToCode string) FrontendAPIExchangeSessionTokenRequest {\n\tr.returnToCode = &returnToCode\n\treturn r\n}\n\nfunc (r FrontendAPIExchangeSessionTokenRequest) Execute() (*SuccessfulNativeLogin, *http.Response, error) {\n\treturn r.ApiService.ExchangeSessionTokenExecute(r)\n}\n\n/*\nExchangeSessionToken Exchange Session Token\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIExchangeSessionTokenRequest\n*/\nfunc (a *FrontendAPIService) ExchangeSessionToken(ctx context.Context) FrontendAPIExchangeSessionTokenRequest {\n\treturn FrontendAPIExchangeSessionTokenRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SuccessfulNativeLogin\nfunc (a *FrontendAPIService) ExchangeSessionTokenExecute(r FrontendAPIExchangeSessionTokenRequest) (*SuccessfulNativeLogin, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SuccessfulNativeLogin\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.ExchangeSessionToken\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions/token-exchange\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.initCode == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"initCode is required and must be specified\")\n\t}\n\tif r.returnToCode == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"returnToCode is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"init_code\", r.initCode, \"form\", \"\")\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to_code\", r.returnToCode, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetFlowErrorRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n}\n\n// Error is the error&#39;s ID\nfunc (r FrontendAPIGetFlowErrorRequest) Id(id string) FrontendAPIGetFlowErrorRequest {\n\tr.id = &id\n\treturn r\n}\n\nfunc (r FrontendAPIGetFlowErrorRequest) Execute() (*FlowError, *http.Response, error) {\n\treturn r.ApiService.GetFlowErrorExecute(r)\n}\n\n/*\nGetFlowError Get User-Flow Errors\n\nThis endpoint returns the error associated with a user-facing self service errors.\n\nThis endpoint supports stub values to help you implement the error UI:\n\n`?id=stub:500` - returns a stub 500 (Internal Server Error) error.\n\nMore information can be found at [Ory Kratos User User Facing Error Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetFlowErrorRequest\n*/\nfunc (a *FrontendAPIService) GetFlowError(ctx context.Context) FrontendAPIGetFlowErrorRequest {\n\treturn FrontendAPIGetFlowErrorRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return FlowError\nfunc (a *FrontendAPIService) GetFlowErrorExecute(r FrontendAPIGetFlowErrorRequest) (*FlowError, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *FlowError\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetFlowError\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/errors\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 500 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetLoginFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n\tcookie     *string\n}\n\n// The Login Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/login?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetLoginFlowRequest) Id(id string) FrontendAPIGetLoginFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIGetLoginFlowRequest) Cookie(cookie string) FrontendAPIGetLoginFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetLoginFlowRequest) Execute() (*LoginFlow, *http.Response, error) {\n\treturn r.ApiService.GetLoginFlowExecute(r)\n}\n\n/*\nGetLoginFlow Get Login Flow\n\nThis endpoint returns a login flow's context with, for example, error details and other information.\n\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\nand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/login', async function (req, res) {\nconst flow = await client.getLoginFlow(req.header('cookie'), req.query['flow'])\n\nres.render('login', flow)\n})\n```\n\nThis request may fail due to several reasons. The `error.id` can be one of:\n\n`session_already_available`: The user is already signed in.\n`self_service_flow_expired`: The flow is expired and you should request a new one.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetLoginFlowRequest\n*/\nfunc (a *FrontendAPIService) GetLoginFlow(ctx context.Context) FrontendAPIGetLoginFlowRequest {\n\treturn FrontendAPIGetLoginFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return LoginFlow\nfunc (a *FrontendAPIService) GetLoginFlowExecute(r FrontendAPIGetLoginFlowRequest) (*LoginFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *LoginFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetLoginFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/login/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetRecoveryFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n\tcookie     *string\n}\n\n// The Flow ID  The value for this parameter comes from &#x60;request&#x60; URL Query parameter sent to your application (e.g. &#x60;/recovery?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetRecoveryFlowRequest) Id(id string) FrontendAPIGetRecoveryFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIGetRecoveryFlowRequest) Cookie(cookie string) FrontendAPIGetRecoveryFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetRecoveryFlowRequest) Execute() (*RecoveryFlow, *http.Response, error) {\n\treturn r.ApiService.GetRecoveryFlowExecute(r)\n}\n\n/*\nGetRecoveryFlow Get Recovery Flow\n\nThis endpoint returns a recovery flow's context with, for example, error details and other information.\n\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\nand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/recovery', async function (req, res) {\nconst flow = await client.getRecoveryFlow(req.header('Cookie'), req.query['flow'])\n\nres.render('recovery', flow)\n})\n```\n\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetRecoveryFlowRequest\n*/\nfunc (a *FrontendAPIService) GetRecoveryFlow(ctx context.Context) FrontendAPIGetRecoveryFlowRequest {\n\treturn FrontendAPIGetRecoveryFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryFlow\nfunc (a *FrontendAPIService) GetRecoveryFlowExecute(r FrontendAPIGetRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetRecoveryFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/recovery/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetRegistrationFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n\tcookie     *string\n}\n\n// The Registration Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/registration?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetRegistrationFlowRequest) Id(id string) FrontendAPIGetRegistrationFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIGetRegistrationFlowRequest) Cookie(cookie string) FrontendAPIGetRegistrationFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetRegistrationFlowRequest) Execute() (*RegistrationFlow, *http.Response, error) {\n\treturn r.ApiService.GetRegistrationFlowExecute(r)\n}\n\n/*\nGetRegistrationFlow Get Registration Flow\n\nThis endpoint returns a registration flow's context with, for example, error details and other information.\n\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\nand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/registration', async function (req, res) {\nconst flow = await client.getRegistrationFlow(req.header('cookie'), req.query['flow'])\n\nres.render('registration', flow)\n})\n```\n\nThis request may fail due to several reasons. The `error.id` can be one of:\n\n`session_already_available`: The user is already signed in.\n`self_service_flow_expired`: The flow is expired and you should request a new one.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetRegistrationFlowRequest\n*/\nfunc (a *FrontendAPIService) GetRegistrationFlow(ctx context.Context) FrontendAPIGetRegistrationFlowRequest {\n\treturn FrontendAPIGetRegistrationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RegistrationFlow\nfunc (a *FrontendAPIService) GetRegistrationFlowExecute(r FrontendAPIGetRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RegistrationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetRegistrationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/registration/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetSettingsFlowRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\tid            *string\n\txSessionToken *string\n\tcookie        *string\n}\n\n// ID is the Settings Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/settings?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetSettingsFlowRequest) Id(id string) FrontendAPIGetSettingsFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// The Session Token  When using the SDK in an app without a browser, please include the session token here.\nfunc (r FrontendAPIGetSettingsFlowRequest) XSessionToken(xSessionToken string) FrontendAPIGetSettingsFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIGetSettingsFlowRequest) Cookie(cookie string) FrontendAPIGetSettingsFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetSettingsFlowRequest) Execute() (*SettingsFlow, *http.Response, error) {\n\treturn r.ApiService.GetSettingsFlowExecute(r)\n}\n\n/*\nGetSettingsFlow Get Settings Flow\n\nWhen accessing this endpoint through Ory Kratos' Public API you must ensure that either the Ory Kratos Session Cookie\nor the Ory Kratos Session Token are set.\n\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor or change the configuration.\n\nYou can access this endpoint without credentials when using Ory Kratos' Admin API.\n\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`session_inactive`: No Ory Session was found - sign in a user first.\n`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\nidentity logged in instead.\n\nMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetSettingsFlowRequest\n*/\nfunc (a *FrontendAPIService) GetSettingsFlow(ctx context.Context) FrontendAPIGetSettingsFlowRequest {\n\treturn FrontendAPIGetSettingsFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SettingsFlow\nfunc (a *FrontendAPIService) GetSettingsFlowExecute(r FrontendAPIGetSettingsFlowRequest) (*SettingsFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SettingsFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetSettingsFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/settings/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetVerificationFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n\tcookie     *string\n}\n\n// The Flow ID  The value for this parameter comes from &#x60;request&#x60; URL Query parameter sent to your application (e.g. &#x60;/verification?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetVerificationFlowRequest) Id(id string) FrontendAPIGetVerificationFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK on the server side you must include the HTTP Cookie Header originally sent to your HTTP handler here.\nfunc (r FrontendAPIGetVerificationFlowRequest) Cookie(cookie string) FrontendAPIGetVerificationFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) {\n\treturn r.ApiService.GetVerificationFlowExecute(r)\n}\n\n/*\nGetVerificationFlow Get Verification Flow\n\nThis endpoint returns a verification flow's context with, for example, error details and other information.\n\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\nand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/recovery', async function (req, res) {\nconst flow = await client.getVerificationFlow(req.header('cookie'), req.query['flow'])\n\nres.render('verification', flow)\n})\n```\n\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetVerificationFlowRequest\n*/\nfunc (a *FrontendAPIService) GetVerificationFlow(ctx context.Context) FrontendAPIGetVerificationFlowRequest {\n\treturn FrontendAPIGetVerificationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return VerificationFlow\nfunc (a *FrontendAPIService) GetVerificationFlowExecute(r FrontendAPIGetVerificationFlowRequest) (*VerificationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *VerificationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetVerificationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/verification/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetWebAuthnJavaScriptRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n}\n\nfunc (r FrontendAPIGetWebAuthnJavaScriptRequest) Execute() (string, *http.Response, error) {\n\treturn r.ApiService.GetWebAuthnJavaScriptExecute(r)\n}\n\n/*\nGetWebAuthnJavaScript Get WebAuthn JavaScript\n\nThis endpoint provides JavaScript which is needed in order to perform WebAuthn login and registration.\n\nIf you are building a JavaScript Browser App (e.g. in ReactJS or AngularJS) you will need to load this file:\n\n```html\n<script src=\"https://public-kratos.example.org/.well-known/ory/webauthn.js\" type=\"script\" async />\n```\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetWebAuthnJavaScriptRequest\n*/\nfunc (a *FrontendAPIService) GetWebAuthnJavaScript(ctx context.Context) FrontendAPIGetWebAuthnJavaScriptRequest {\n\treturn FrontendAPIGetWebAuthnJavaScriptRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return string\nfunc (a *FrontendAPIService) GetWebAuthnJavaScriptExecute(r FrontendAPIGetWebAuthnJavaScriptRequest) (string, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue string\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetWebAuthnJavaScript\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/.well-known/ory/webauthn.js\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIListMySessionsRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\tperPage       *int64\n\tpage          *int64\n\tpageSize      *int64\n\tpageToken     *string\n\txSessionToken *string\n\tcookie        *string\n}\n\n// Deprecated Items per Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This is the number of items per page.\nfunc (r FrontendAPIListMySessionsRequest) PerPage(perPage int64) FrontendAPIListMySessionsRequest {\n\tr.perPage = &perPage\n\treturn r\n}\n\n// Deprecated Pagination Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This value is currently an integer, but it is not sequential. The value is not the page number, but a reference. The next page can be any number and some numbers might return an empty list.  For example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist. The first page can be retrieved by omitting this parameter. Following page pointers will be returned in the &#x60;Link&#x60; header.\nfunc (r FrontendAPIListMySessionsRequest) Page(page int64) FrontendAPIListMySessionsRequest {\n\tr.page = &page\n\treturn r\n}\n\n// Page Size  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r FrontendAPIListMySessionsRequest) PageSize(pageSize int64) FrontendAPIListMySessionsRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r FrontendAPIListMySessionsRequest) PageToken(pageToken string) FrontendAPIListMySessionsRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Set the Session Token when calling from non-browser clients. A session token has a format of &#x60;MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj&#x60;.\nfunc (r FrontendAPIListMySessionsRequest) XSessionToken(xSessionToken string) FrontendAPIListMySessionsRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that scenario you must include the HTTP Cookie Header which originally was included in the request to your server. An example of a session in the HTTP Cookie Header is: &#x60;ory_kratos_session&#x3D;a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f&#x3D;&#x3D;&#x60;.  It is ok if more than one cookie are included here as all other cookies will be ignored.\nfunc (r FrontendAPIListMySessionsRequest) Cookie(cookie string) FrontendAPIListMySessionsRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIListMySessionsRequest) Execute() ([]Session, *http.Response, error) {\n\treturn r.ApiService.ListMySessionsExecute(r)\n}\n\n/*\nListMySessions Get My Active Sessions\n\nThis endpoints returns all other active sessions that belong to the logged-in user.\nThe current session can be retrieved by calling the `/sessions/whoami` endpoint.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIListMySessionsRequest\n*/\nfunc (a *FrontendAPIService) ListMySessions(ctx context.Context) FrontendAPIListMySessionsRequest {\n\treturn FrontendAPIListMySessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Session\nfunc (a *FrontendAPIService) ListMySessionsExecute(r FrontendAPIListMySessionsRequest) ([]Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.ListMySessions\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.perPage != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"per_page\", r.perPage, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.perPage = &defaultValue\n\t}\n\tif r.page != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page\", r.page, \"form\", \"\")\n\t}\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIPerformNativeLogoutRequest struct {\n\tctx                     context.Context\n\tApiService              FrontendAPI\n\tperformNativeLogoutBody *PerformNativeLogoutBody\n}\n\nfunc (r FrontendAPIPerformNativeLogoutRequest) PerformNativeLogoutBody(performNativeLogoutBody PerformNativeLogoutBody) FrontendAPIPerformNativeLogoutRequest {\n\tr.performNativeLogoutBody = &performNativeLogoutBody\n\treturn r\n}\n\nfunc (r FrontendAPIPerformNativeLogoutRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.PerformNativeLogoutExecute(r)\n}\n\n/*\nPerformNativeLogout Perform Logout for Native Apps\n\nUse this endpoint to log out an identity using an Ory Session Token. If the Ory Session Token was successfully\nrevoked, the server returns a 204 No Content response. A 204 No Content response is also sent when\nthe Ory Session Token has been revoked already before.\n\nIf the Ory Session Token is malformed or does not exist a 403 Forbidden response will be returned.\n\nThis endpoint does not remove any HTTP\nCookies - use the Browser-Based Self-Service Logout Flow instead.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIPerformNativeLogoutRequest\n*/\nfunc (a *FrontendAPIService) PerformNativeLogout(ctx context.Context) FrontendAPIPerformNativeLogoutRequest {\n\treturn FrontendAPIPerformNativeLogoutRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\nfunc (a *FrontendAPIService) PerformNativeLogoutExecute(r FrontendAPIPerformNativeLogoutRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.PerformNativeLogout\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/logout/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.performNativeLogoutBody == nil {\n\t\treturn nil, reportError(\"performNativeLogoutBody is required and must be specified\")\n\t}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.performNativeLogoutBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype FrontendAPIToSessionRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\txSessionToken *string\n\tcookie        *string\n\ttokenizeAs    *string\n}\n\n// Set the Session Token when calling from non-browser clients. A session token has a format of &#x60;MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj&#x60;.\nfunc (r FrontendAPIToSessionRequest) XSessionToken(xSessionToken string) FrontendAPIToSessionRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that scenario you must include the HTTP Cookie Header which originally was included in the request to your server. An example of a session in the HTTP Cookie Header is: &#x60;ory_kratos_session&#x3D;a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f&#x3D;&#x3D;&#x60;.  It is ok if more than one cookie are included here as all other cookies will be ignored.\nfunc (r FrontendAPIToSessionRequest) Cookie(cookie string) FrontendAPIToSessionRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\n// Returns the session additionally as a token (such as a JWT)  The value of this parameter has to be a valid, configured Ory Session token template. For more information head over to [the documentation](http://ory.sh/docs/identities/session-to-jwt-cors).\nfunc (r FrontendAPIToSessionRequest) TokenizeAs(tokenizeAs string) FrontendAPIToSessionRequest {\n\tr.tokenizeAs = &tokenizeAs\n\treturn r\n}\n\nfunc (r FrontendAPIToSessionRequest) Execute() (*Session, *http.Response, error) {\n\treturn r.ApiService.ToSessionExecute(r)\n}\n\n/*\nToSession Check Who the Current HTTP Session Belongs To\n\nUses the HTTP Headers in the GET request to determine (e.g. by using checking the cookies) who is authenticated.\nReturns a session object in the body or 401 if the credentials are invalid or no credentials were sent.\nWhen the request it successful it adds the user ID to the 'X-Kratos-Authenticated-Identity-Id' header\nin the response.\n\nIf you call this endpoint from a server-side application, you must forward the HTTP Cookie Header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/protected-endpoint', async function (req, res) {\nconst session = await client.toSession(undefined, req.header('cookie'))\n\nconsole.log(session)\n})\n```\n\nWhen calling this endpoint from a non-browser application (e.g. mobile app) you must include the session token:\n\n```js\npseudo-code example\n...\nconst session = await client.toSession(\"the-session-token\")\n\nconsole.log(session)\n```\n\nWhen using a token template, the token is included in the `tokenized` field of the session.\n\n```js\npseudo-code example\n...\nconst session = await client.toSession(\"the-session-token\", { tokenize_as: \"example-jwt-template\" })\n\nconsole.log(session.tokenized) // The JWT\n```\n\nDepending on your configuration this endpoint might return a 403 status code if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor or change the configuration.\n\nThis endpoint is useful for:\n\nAJAX calls. Remember to send credentials and set up CORS correctly!\nReverse proxies and API Gateways\nServer-side calls - use the `X-Session-Token` header!\n\nThis endpoint authenticates users by checking:\n\nif the `Cookie` HTTP header was set containing an Ory Kratos Session Cookie;\nif the `Authorization: bearer <ory-session-token>` HTTP header was set with a valid Ory Kratos Session Token;\nif the `X-Session-Token` HTTP header was set with a valid Ory Kratos Session Token.\n\nIf none of these headers are set or the cookie or token are invalid, the endpoint returns a HTTP 401 status code.\n\nAs explained above, this request may fail due to several reasons. The `error.id` can be one of:\n\n`session_inactive`: No active session was found in the request (e.g. no Ory Session Cookie / Ory Session Token).\n`session_aal2_required`: An active session was found but it does not fulfil the Authenticator Assurance Level, implying that the session must (e.g.) authenticate the second factor.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIToSessionRequest\n*/\nfunc (a *FrontendAPIService) ToSession(ctx context.Context) FrontendAPIToSessionRequest {\n\treturn FrontendAPIToSessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Session\nfunc (a *FrontendAPIService) ToSessionExecute(r FrontendAPIToSessionRequest) (*Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.ToSession\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions/whoami\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.tokenizeAs != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"tokenize_as\", r.tokenizeAs, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateFedcmFlowRequest struct {\n\tctx                 context.Context\n\tApiService          FrontendAPI\n\tupdateFedcmFlowBody *UpdateFedcmFlowBody\n}\n\nfunc (r FrontendAPIUpdateFedcmFlowRequest) UpdateFedcmFlowBody(updateFedcmFlowBody UpdateFedcmFlowBody) FrontendAPIUpdateFedcmFlowRequest {\n\tr.updateFedcmFlowBody = &updateFedcmFlowBody\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateFedcmFlowRequest) Execute() (*SuccessfulNativeLogin, *http.Response, error) {\n\treturn r.ApiService.UpdateFedcmFlowExecute(r)\n}\n\n/*\nUpdateFedcmFlow Submit a FedCM token\n\nUse this endpoint to submit a token from a FedCM provider through\n`navigator.credentials.get` and log the user in. The parameters from\n`navigator.credentials.get` must have come from `GET\nself-service/fed-cm/parameters`.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateFedcmFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateFedcmFlow(ctx context.Context) FrontendAPIUpdateFedcmFlowRequest {\n\treturn FrontendAPIUpdateFedcmFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SuccessfulNativeLogin\nfunc (a *FrontendAPIService) UpdateFedcmFlowExecute(r FrontendAPIUpdateFedcmFlowRequest) (*SuccessfulNativeLogin, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SuccessfulNativeLogin\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateFedcmFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/fed-cm/token\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.updateFedcmFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateFedcmFlowBody is required and must be specified\")\n\t}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateFedcmFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v LoginFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateLoginFlowRequest struct {\n\tctx                 context.Context\n\tApiService          FrontendAPI\n\tflow                *string\n\tupdateLoginFlowBody *UpdateLoginFlowBody\n\txSessionToken       *string\n\tcookie              *string\n}\n\n// The Login Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/login?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateLoginFlowRequest) Flow(flow string) FrontendAPIUpdateLoginFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateLoginFlowRequest) UpdateLoginFlowBody(updateLoginFlowBody UpdateLoginFlowBody) FrontendAPIUpdateLoginFlowRequest {\n\tr.updateLoginFlowBody = &updateLoginFlowBody\n\treturn r\n}\n\n// The Session Token of the Identity performing the settings flow.\nfunc (r FrontendAPIUpdateLoginFlowRequest) XSessionToken(xSessionToken string) FrontendAPIUpdateLoginFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateLoginFlowRequest) Cookie(cookie string) FrontendAPIUpdateLoginFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateLoginFlowRequest) Execute() (*SuccessfulNativeLogin, *http.Response, error) {\n\treturn r.ApiService.UpdateLoginFlowExecute(r)\n}\n\n/*\nUpdateLoginFlow Submit a Login Flow\n\nUse this endpoint to complete a login flow. This endpoint\nbehaves differently for API and browser flows.\n\nAPI flows expect `application/json` to be sent in the body and responds with\nHTTP 200 and a application/json body with the session token on success;\nHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\nHTTP 400 on form validation errors.\n\nBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\na HTTP 303 redirect to the post/after login URL or the `return_to` value if it was set and if the login succeeded;\na HTTP 303 redirect to the login UI URL with the flow ID containing the validation errors otherwise.\n\nBrowser flows with an accept header of `application/json` will not redirect but instead respond with\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\nHTTP 400 on form validation errors.\n\nIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\nMost likely used in Social Sign In flows.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateLoginFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateLoginFlow(ctx context.Context) FrontendAPIUpdateLoginFlowRequest {\n\treturn FrontendAPIUpdateLoginFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SuccessfulNativeLogin\nfunc (a *FrontendAPIService) UpdateLoginFlowExecute(r FrontendAPIUpdateLoginFlowRequest) (*SuccessfulNativeLogin, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SuccessfulNativeLogin\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateLoginFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/login\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateLoginFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateLoginFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateLoginFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v LoginFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateLogoutFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\ttoken      *string\n\treturnTo   *string\n\tcookie     *string\n}\n\n// A Valid Logout Token  If you do not have a logout token because you only have a session cookie, call &#x60;/self-service/logout/browser&#x60; to generate a URL for this endpoint.\nfunc (r FrontendAPIUpdateLogoutFlowRequest) Token(token string) FrontendAPIUpdateLogoutFlowRequest {\n\tr.token = &token\n\treturn r\n}\n\n// The URL to return to after the logout was completed.\nfunc (r FrontendAPIUpdateLogoutFlowRequest) ReturnTo(returnTo string) FrontendAPIUpdateLogoutFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateLogoutFlowRequest) Cookie(cookie string) FrontendAPIUpdateLogoutFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateLogoutFlowRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.UpdateLogoutFlowExecute(r)\n}\n\n/*\nUpdateLogoutFlow Update Logout Flow\n\nThis endpoint logs out an identity in a self-service manner.\n\nIf the `Accept` HTTP header is not set to `application/json`, the browser will be redirected (HTTP 303 See Other)\nto the `return_to` parameter of the initial request or fall back to `urls.default_return_to`.\n\nIf the `Accept` HTTP header is set to `application/json`, a 204 No Content response\nwill be sent on successful logout instead.\n\nThis endpoint is NOT INTENDED for API clients and only works\nwith browsers (Chrome, Firefox, ...). For API clients you can\ncall the `/self-service/logout/api` URL directly with the Ory Session Token.\n\nMore information can be found at [Ory Kratos User Logout Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-logout).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateLogoutFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateLogoutFlow(ctx context.Context) FrontendAPIUpdateLogoutFlowRequest {\n\treturn FrontendAPIUpdateLogoutFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\nfunc (a *FrontendAPIService) UpdateLogoutFlowExecute(r FrontendAPIUpdateLogoutFlowRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodGet\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateLogoutFlow\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/logout\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.token != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"token\", r.token, \"form\", \"\")\n\t}\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateRecoveryFlowRequest struct {\n\tctx                    context.Context\n\tApiService             FrontendAPI\n\tflow                   *string\n\tupdateRecoveryFlowBody *UpdateRecoveryFlowBody\n\ttoken                  *string\n\tcookie                 *string\n}\n\n// The Recovery Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/recovery?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) Flow(flow string) FrontendAPIUpdateRecoveryFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) UpdateRecoveryFlowBody(updateRecoveryFlowBody UpdateRecoveryFlowBody) FrontendAPIUpdateRecoveryFlowRequest {\n\tr.updateRecoveryFlowBody = &updateRecoveryFlowBody\n\treturn r\n}\n\n// Recovery Token  The recovery token which completes the recovery request. If the token is invalid (e.g. expired) an error will be shown to the end-user.  This parameter is usually set in a link and not used by any direct API call.\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) Token(token string) FrontendAPIUpdateRecoveryFlowRequest {\n\tr.token = &token\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) Cookie(cookie string) FrontendAPIUpdateRecoveryFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) Execute() (*RecoveryFlow, *http.Response, error) {\n\treturn r.ApiService.UpdateRecoveryFlowExecute(r)\n}\n\n/*\nUpdateRecoveryFlow Update Recovery Flow\n\nUse this endpoint to update a recovery flow. This endpoint\nbehaves differently for API and browser flows and has several states:\n\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\nand works with API- and Browser-initiated flows.\nFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid.\nand a HTTP 303 See Other redirect with a fresh recovery flow if the flow was otherwise invalid (e.g. expired).\nFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Recovery UI URL with the Recovery Flow ID appended.\n`sent_email` is the success state after `choose_method` for the `link` method and allows the user to request another recovery email. It\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a recovery link\")\ndoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n(if the link was valid) and instructs the user to update their password, or a redirect to the Recover UI URL with\na new Recovery Flow ID which contains an error message that the recovery link was invalid.\n\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateRecoveryFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateRecoveryFlow(ctx context.Context) FrontendAPIUpdateRecoveryFlowRequest {\n\treturn FrontendAPIUpdateRecoveryFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryFlow\nfunc (a *FrontendAPIService) UpdateRecoveryFlowExecute(r FrontendAPIUpdateRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateRecoveryFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/recovery\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateRecoveryFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateRecoveryFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\tif r.token != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"token\", r.token, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateRecoveryFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v RecoveryFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateRegistrationFlowRequest struct {\n\tctx                        context.Context\n\tApiService                 FrontendAPI\n\tflow                       *string\n\tupdateRegistrationFlowBody *UpdateRegistrationFlowBody\n\tcookie                     *string\n}\n\n// The Registration Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/registration?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateRegistrationFlowRequest) Flow(flow string) FrontendAPIUpdateRegistrationFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateRegistrationFlowRequest) UpdateRegistrationFlowBody(updateRegistrationFlowBody UpdateRegistrationFlowBody) FrontendAPIUpdateRegistrationFlowRequest {\n\tr.updateRegistrationFlowBody = &updateRegistrationFlowBody\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateRegistrationFlowRequest) Cookie(cookie string) FrontendAPIUpdateRegistrationFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateRegistrationFlowRequest) Execute() (*SuccessfulNativeRegistration, *http.Response, error) {\n\treturn r.ApiService.UpdateRegistrationFlowExecute(r)\n}\n\n/*\nUpdateRegistrationFlow Update Registration Flow\n\nUse this endpoint to complete a registration flow by sending an identity's traits and password. This endpoint\nbehaves differently for API and browser flows.\n\nAPI flows expect `application/json` to be sent in the body and respond with\nHTTP 200 and a application/json body with the created identity success - if the session hook is configured the\n`session` and `session_token` will also be included;\nHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\nHTTP 400 on form validation errors.\n\nBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\na HTTP 303 redirect to the post/after registration URL or the `return_to` value if it was set and if the registration succeeded;\na HTTP 303 redirect to the registration UI URL with the flow ID containing the validation errors otherwise.\n\nBrowser flows with an accept header of `application/json` will not redirect but instead respond with\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\nHTTP 400 on form validation errors.\n\nIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\nMost likely used in Social Sign In flows.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateRegistrationFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateRegistrationFlow(ctx context.Context) FrontendAPIUpdateRegistrationFlowRequest {\n\treturn FrontendAPIUpdateRegistrationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SuccessfulNativeRegistration\nfunc (a *FrontendAPIService) UpdateRegistrationFlowExecute(r FrontendAPIUpdateRegistrationFlowRequest) (*SuccessfulNativeRegistration, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SuccessfulNativeRegistration\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateRegistrationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/registration\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateRegistrationFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateRegistrationFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateRegistrationFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v RegistrationFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateSettingsFlowRequest struct {\n\tctx                    context.Context\n\tApiService             FrontendAPI\n\tflow                   *string\n\tupdateSettingsFlowBody *UpdateSettingsFlowBody\n\txSessionToken          *string\n\tcookie                 *string\n}\n\n// The Settings Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/settings?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateSettingsFlowRequest) Flow(flow string) FrontendAPIUpdateSettingsFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateSettingsFlowRequest) UpdateSettingsFlowBody(updateSettingsFlowBody UpdateSettingsFlowBody) FrontendAPIUpdateSettingsFlowRequest {\n\tr.updateSettingsFlowBody = &updateSettingsFlowBody\n\treturn r\n}\n\n// The Session Token of the Identity performing the settings flow.\nfunc (r FrontendAPIUpdateSettingsFlowRequest) XSessionToken(xSessionToken string) FrontendAPIUpdateSettingsFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateSettingsFlowRequest) Cookie(cookie string) FrontendAPIUpdateSettingsFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateSettingsFlowRequest) Execute() (*SettingsFlow, *http.Response, error) {\n\treturn r.ApiService.UpdateSettingsFlowExecute(r)\n}\n\n/*\nUpdateSettingsFlow Complete Settings Flow\n\nUse this endpoint to complete a settings flow by sending an identity's updated password. This endpoint\nbehaves differently for API and browser flows.\n\nAPI-initiated flows expect `application/json` to be sent in the body and respond with\nHTTP 200 and an application/json body with the session token on success;\nHTTP 303 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\nHTTP 400 on form validation errors.\nHTTP 401 when the endpoint is called without a valid session token.\nHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\nImplies that the user needs to re-authenticate.\n\nBrowser flows without HTTP Header `Accept` or with `Accept: text/*` respond with\na HTTP 303 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\na HTTP 303 redirect to the Settings UI URL with the flow ID containing the validation errors otherwise.\na HTTP 303 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\n\nBrowser flows with HTTP Header `Accept: application/json` respond with\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\nHTTP 401 when the endpoint is called without a valid session cookie.\nHTTP 403 when the page is accessed without a session cookie or the session's AAL is too low.\nHTTP 400 on form validation errors.\n\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n\nIf this endpoint is called with a `Accept: application/json` HTTP header, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_refresh_required`: The identity requested to change something that needs a privileged session. Redirect\nthe identity to the login init endpoint with query parameters `?refresh=true&return_to=<the-current-browser-url>`,\nor initiate a refresh login flow otherwise.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`session_inactive`: No Ory Session was found - sign in a user first.\n`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\nidentity logged in instead.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\nMost likely used in Social Sign In flows.\n\nMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateSettingsFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateSettingsFlow(ctx context.Context) FrontendAPIUpdateSettingsFlowRequest {\n\treturn FrontendAPIUpdateSettingsFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SettingsFlow\nfunc (a *FrontendAPIService) UpdateSettingsFlowExecute(r FrontendAPIUpdateSettingsFlowRequest) (*SettingsFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SettingsFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateSettingsFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/settings\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateSettingsFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateSettingsFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateSettingsFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v SettingsFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateVerificationFlowRequest struct {\n\tctx                        context.Context\n\tApiService                 FrontendAPI\n\tflow                       *string\n\tupdateVerificationFlowBody *UpdateVerificationFlowBody\n\ttoken                      *string\n\tcookie                     *string\n}\n\n// The Verification Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/verification?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateVerificationFlowRequest) Flow(flow string) FrontendAPIUpdateVerificationFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateVerificationFlowRequest) UpdateVerificationFlowBody(updateVerificationFlowBody UpdateVerificationFlowBody) FrontendAPIUpdateVerificationFlowRequest {\n\tr.updateVerificationFlowBody = &updateVerificationFlowBody\n\treturn r\n}\n\n// Verification Token  The verification token which completes the verification request. If the token is invalid (e.g. expired) an error will be shown to the end-user.  This parameter is usually set in a link and not used by any direct API call.\nfunc (r FrontendAPIUpdateVerificationFlowRequest) Token(token string) FrontendAPIUpdateVerificationFlowRequest {\n\tr.token = &token\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateVerificationFlowRequest) Cookie(cookie string) FrontendAPIUpdateVerificationFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) {\n\treturn r.ApiService.UpdateVerificationFlowExecute(r)\n}\n\n/*\nUpdateVerificationFlow Complete Verification Flow\n\nUse this endpoint to complete a verification flow. This endpoint\nbehaves differently for API and browser flows and has several states:\n\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\nand works with API- and Browser-initiated flows.\nFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\nand a HTTP 303 See Other redirect with a fresh verification flow if the flow was otherwise invalid (e.g. expired).\nFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Verification UI URL with the Verification Flow ID appended.\n`sent_email` is the success state after `choose_method` when using the `link` method and allows the user to request another verification email. It\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a verification link\")\ndoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n(if the link was valid) and instructs the user to update their password, or a redirect to the Verification UI URL with\na new Verification Flow ID which contains an error message that the verification link was invalid.\n\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateVerificationFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateVerificationFlow(ctx context.Context) FrontendAPIUpdateVerificationFlowRequest {\n\treturn FrontendAPIUpdateVerificationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return VerificationFlow\nfunc (a *FrontendAPIService) UpdateVerificationFlowExecute(r FrontendAPIUpdateVerificationFlowRequest) (*VerificationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *VerificationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateVerificationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/verification\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateVerificationFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateVerificationFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\tif r.token != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"token\", r.token, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateVerificationFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v VerificationFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n"
  },
  {
    "path": "pkg/client-go/api_identity.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n)\n\ntype IdentityAPI interface {\n\n\t/*\n\t\t\tBatchPatchIdentities Create multiple identities\n\n\t\t\tCreates multiple [identities](https://www.ory.com/docs/kratos/concepts/identity-user-model).\n\n\t\tYou can also use this endpoint to [import credentials](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities),\n\t\tincluding passwords, social sign-in settings, and multi-factor authentication methods.\n\n\t\tIf the patch includes hashed passwords you can import up to 1,000 identities per request.\n\n\t\tIf the patch includes at least one plaintext password you can import up to 200 identities per request.\n\n\t\tAvoid importing large batches with plaintext passwords. They can cause timeouts as the passwords need to be hashed before they are stored.\n\n\t\tIf at least one identity is imported successfully, the response status is 200 OK.\n\t\tIf all imports fail, the response is one of the following 4xx errors:\n\t\t400 Bad Request: The request payload is invalid or improperly formatted.\n\t\t409 Conflict: Duplicate identities or conflicting data were detected.\n\n\t\tIf you get a 504 Gateway Timeout:\n\t\tReduce the batch size\n\t\tAvoid duplicate identities\n\t\tPre-hash passwords with BCrypt\n\n\t\tIf the issue persists, contact support.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return IdentityAPIBatchPatchIdentitiesRequest\n\t*/\n\tBatchPatchIdentities(ctx context.Context) IdentityAPIBatchPatchIdentitiesRequest\n\n\t// BatchPatchIdentitiesExecute executes the request\n\t//  @return BatchPatchIdentitiesResponse\n\tBatchPatchIdentitiesExecute(r IdentityAPIBatchPatchIdentitiesRequest) (*BatchPatchIdentitiesResponse, *http.Response, error)\n\n\t/*\n\t\t\tCreateIdentity Create an Identity\n\n\t\t\tCreate an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model).  This endpoint can also be used to\n\t\t[import credentials](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities)\n\t\tfor instance passwords, social sign in configurations or multifactor methods.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return IdentityAPICreateIdentityRequest\n\t*/\n\tCreateIdentity(ctx context.Context) IdentityAPICreateIdentityRequest\n\n\t// CreateIdentityExecute executes the request\n\t//  @return Identity\n\tCreateIdentityExecute(r IdentityAPICreateIdentityRequest) (*Identity, *http.Response, error)\n\n\t/*\n\t\t\tCreateRecoveryCodeForIdentity Create a Recovery Code\n\n\t\t\tThis endpoint creates a recovery code which should be given to the user in order for them to recover\n\t\t(or activate) their account.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return IdentityAPICreateRecoveryCodeForIdentityRequest\n\t*/\n\tCreateRecoveryCodeForIdentity(ctx context.Context) IdentityAPICreateRecoveryCodeForIdentityRequest\n\n\t// CreateRecoveryCodeForIdentityExecute executes the request\n\t//  @return RecoveryCodeForIdentity\n\tCreateRecoveryCodeForIdentityExecute(r IdentityAPICreateRecoveryCodeForIdentityRequest) (*RecoveryCodeForIdentity, *http.Response, error)\n\n\t/*\n\t\t\tCreateRecoveryLinkForIdentity Create a Recovery Link\n\n\t\t\tThis endpoint creates a recovery link which should be given to the user in order for them to recover\n\t\t(or activate) their account.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return IdentityAPICreateRecoveryLinkForIdentityRequest\n\t*/\n\tCreateRecoveryLinkForIdentity(ctx context.Context) IdentityAPICreateRecoveryLinkForIdentityRequest\n\n\t// CreateRecoveryLinkForIdentityExecute executes the request\n\t//  @return RecoveryLinkForIdentity\n\tCreateRecoveryLinkForIdentityExecute(r IdentityAPICreateRecoveryLinkForIdentityRequest) (*RecoveryLinkForIdentity, *http.Response, error)\n\n\t/*\n\t\t\tDeleteIdentity Delete an Identity\n\n\t\t\tCalling this endpoint irrecoverably and permanently deletes the [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) given its ID. This action can not be undone.\n\t\tThis endpoint returns 204 when the identity was deleted or 404 if the identity was not found.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the identity's ID.\n\t\t\t@return IdentityAPIDeleteIdentityRequest\n\t*/\n\tDeleteIdentity(ctx context.Context, id string) IdentityAPIDeleteIdentityRequest\n\n\t// DeleteIdentityExecute executes the request\n\tDeleteIdentityExecute(r IdentityAPIDeleteIdentityRequest) (*http.Response, error)\n\n\t/*\n\t\t\tDeleteIdentityCredentials Delete a credential for a specific identity\n\n\t\t\tDelete an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) credential by its type.\n\t\tYou cannot delete passkeys or code auth credentials through this API.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the identity's ID.\n\t\t\t@param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\t\t\t@return IdentityAPIDeleteIdentityCredentialsRequest\n\t*/\n\tDeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIDeleteIdentityCredentialsRequest\n\n\t// DeleteIdentityCredentialsExecute executes the request\n\tDeleteIdentityCredentialsExecute(r IdentityAPIDeleteIdentityCredentialsRequest) (*http.Response, error)\n\n\t/*\n\t\tDeleteIdentitySessions Delete & Invalidate an Identity's Sessions\n\n\t\tCalling this endpoint irrecoverably and permanently deletes and invalidates all sessions that belong to the given Identity.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id ID is the identity's ID.\n\t\t@return IdentityAPIDeleteIdentitySessionsRequest\n\t*/\n\tDeleteIdentitySessions(ctx context.Context, id string) IdentityAPIDeleteIdentitySessionsRequest\n\n\t// DeleteIdentitySessionsExecute executes the request\n\tDeleteIdentitySessionsExecute(r IdentityAPIDeleteIdentitySessionsRequest) (*http.Response, error)\n\n\t/*\n\t\tDisableSession Deactivate a Session\n\n\t\tCalling this endpoint deactivates the specified session. Session data is not deleted.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id ID is the session's ID.\n\t\t@return IdentityAPIDisableSessionRequest\n\t*/\n\tDisableSession(ctx context.Context, id string) IdentityAPIDisableSessionRequest\n\n\t// DisableSessionExecute executes the request\n\tDisableSessionExecute(r IdentityAPIDisableSessionRequest) (*http.Response, error)\n\n\t/*\n\t\t\tExtendSession Extend a Session\n\n\t\t\tCalling this endpoint extends the given session ID. If `session.earliest_possible_extend` is set it\n\t\twill only extend the session after the specified time has passed.\n\n\t\tThis endpoint returns per default a 204 No Content response on success. Older Ory Network projects may\n\t\treturn a 200 OK response with the session in the body. Returning the session as part of the response\n\t\twill be deprecated in the future and should not be relied upon.\n\n\t\tThis endpoint ignores consecutive requests to extend the same session and returns a 404 error in those\n\t\tscenarios. This endpoint also returns 404 errors if the session does not exist.\n\n\t\tRetrieve the session ID from the `/sessions/whoami` endpoint / `toSession` SDK method.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the session's ID.\n\t\t\t@return IdentityAPIExtendSessionRequest\n\t*/\n\tExtendSession(ctx context.Context, id string) IdentityAPIExtendSessionRequest\n\n\t// ExtendSessionExecute executes the request\n\t//  @return Session\n\tExtendSessionExecute(r IdentityAPIExtendSessionRequest) (*Session, *http.Response, error)\n\n\t/*\n\t\t\tGetIdentity Get an Identity\n\n\t\t\tReturn an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its ID. You can optionally\n\t\tinclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID must be set to the ID of identity you want to get\n\t\t\t@return IdentityAPIGetIdentityRequest\n\t*/\n\tGetIdentity(ctx context.Context, id string) IdentityAPIGetIdentityRequest\n\n\t// GetIdentityExecute executes the request\n\t//  @return Identity\n\tGetIdentityExecute(r IdentityAPIGetIdentityRequest) (*Identity, *http.Response, error)\n\n\t/*\n\t\t\tGetIdentityByExternalID Get an Identity by its External ID\n\n\t\t\tReturn an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its external ID. You can optionally\n\t\tinclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param externalID ExternalID must be set to the ID of identity you want to get\n\t\t\t@return IdentityAPIGetIdentityByExternalIDRequest\n\t*/\n\tGetIdentityByExternalID(ctx context.Context, externalID string) IdentityAPIGetIdentityByExternalIDRequest\n\n\t// GetIdentityByExternalIDExecute executes the request\n\t//  @return Identity\n\tGetIdentityByExternalIDExecute(r IdentityAPIGetIdentityByExternalIDRequest) (*Identity, *http.Response, error)\n\n\t/*\n\t\tGetIdentitySchema Get Identity JSON Schema\n\n\t\tReturn a specific identity schema.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id ID must be set to the ID of schema you want to get\n\t\t@return IdentityAPIGetIdentitySchemaRequest\n\t*/\n\tGetIdentitySchema(ctx context.Context, id string) IdentityAPIGetIdentitySchemaRequest\n\n\t// GetIdentitySchemaExecute executes the request\n\t//  @return map[string]interface{}\n\tGetIdentitySchemaExecute(r IdentityAPIGetIdentitySchemaRequest) (map[string]interface{}, *http.Response, error)\n\n\t/*\n\t\t\tGetSession Get Session\n\n\t\t\tThis endpoint is useful for:\n\n\t\tGetting a session object with all specified expandables that exist in an administrative context.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the session's ID.\n\t\t\t@return IdentityAPIGetSessionRequest\n\t*/\n\tGetSession(ctx context.Context, id string) IdentityAPIGetSessionRequest\n\n\t// GetSessionExecute executes the request\n\t//  @return Session\n\tGetSessionExecute(r IdentityAPIGetSessionRequest) (*Session, *http.Response, error)\n\n\t/*\n\t\tListIdentities List Identities\n\n\t\tLists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return IdentityAPIListIdentitiesRequest\n\t*/\n\tListIdentities(ctx context.Context) IdentityAPIListIdentitiesRequest\n\n\t// ListIdentitiesExecute executes the request\n\t//  @return []Identity\n\tListIdentitiesExecute(r IdentityAPIListIdentitiesRequest) ([]Identity, *http.Response, error)\n\n\t/*\n\t\tListIdentitySchemas Get all Identity Schemas\n\n\t\tReturns a list of all identity schemas currently in use.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return IdentityAPIListIdentitySchemasRequest\n\t*/\n\tListIdentitySchemas(ctx context.Context) IdentityAPIListIdentitySchemasRequest\n\n\t// ListIdentitySchemasExecute executes the request\n\t//  @return []IdentitySchemaContainer\n\tListIdentitySchemasExecute(r IdentityAPIListIdentitySchemasRequest) ([]IdentitySchemaContainer, *http.Response, error)\n\n\t/*\n\t\tListIdentitySessions List an Identity's Sessions\n\n\t\tThis endpoint returns all sessions that belong to the given Identity.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id ID is the identity's ID.\n\t\t@return IdentityAPIListIdentitySessionsRequest\n\t*/\n\tListIdentitySessions(ctx context.Context, id string) IdentityAPIListIdentitySessionsRequest\n\n\t// ListIdentitySessionsExecute executes the request\n\t//  @return []Session\n\tListIdentitySessionsExecute(r IdentityAPIListIdentitySessionsRequest) ([]Session, *http.Response, error)\n\n\t/*\n\t\tListSessions List All Sessions\n\n\t\tListing all sessions that exist.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return IdentityAPIListSessionsRequest\n\t*/\n\tListSessions(ctx context.Context) IdentityAPIListSessionsRequest\n\n\t// ListSessionsExecute executes the request\n\t//  @return []Session\n\tListSessionsExecute(r IdentityAPIListSessionsRequest) ([]Session, *http.Response, error)\n\n\t/*\n\t\t\tPatchIdentity Patch an Identity\n\n\t\t\tPartially updates an [identity's](https://www.ory.sh/docs/kratos/concepts/identity-user-model) field using [JSON Patch](https://jsonpatch.com/).\n\t\tThe fields `id`, `stateChangedAt` and `credentials` can not be updated using this method.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID must be set to the ID of identity you want to update\n\t\t\t@return IdentityAPIPatchIdentityRequest\n\t*/\n\tPatchIdentity(ctx context.Context, id string) IdentityAPIPatchIdentityRequest\n\n\t// PatchIdentityExecute executes the request\n\t//  @return Identity\n\tPatchIdentityExecute(r IdentityAPIPatchIdentityRequest) (*Identity, *http.Response, error)\n\n\t/*\n\t\t\tUpdateIdentity Update an Identity\n\n\t\t\tThis endpoint updates an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model). The full identity\n\t\tpayload, except credentials, is expected. For partial updates, use the [patchIdentity](https://www.ory.sh/docs/reference/api#tag/identity/operation/patchIdentity) operation.\n\n\t\tA credential can be provided via the `credentials` field in the request body.\n\t\tIf provided, the credentials will be imported and added to the existing credentials of the identity.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID must be set to the ID of identity you want to update\n\t\t\t@return IdentityAPIUpdateIdentityRequest\n\t*/\n\tUpdateIdentity(ctx context.Context, id string) IdentityAPIUpdateIdentityRequest\n\n\t// UpdateIdentityExecute executes the request\n\t//  @return Identity\n\tUpdateIdentityExecute(r IdentityAPIUpdateIdentityRequest) (*Identity, *http.Response, error)\n}\n\n// IdentityAPIService IdentityAPI service\ntype IdentityAPIService service\n\ntype IdentityAPIBatchPatchIdentitiesRequest struct {\n\tctx                 context.Context\n\tApiService          IdentityAPI\n\tpatchIdentitiesBody *PatchIdentitiesBody\n}\n\nfunc (r IdentityAPIBatchPatchIdentitiesRequest) PatchIdentitiesBody(patchIdentitiesBody PatchIdentitiesBody) IdentityAPIBatchPatchIdentitiesRequest {\n\tr.patchIdentitiesBody = &patchIdentitiesBody\n\treturn r\n}\n\nfunc (r IdentityAPIBatchPatchIdentitiesRequest) Execute() (*BatchPatchIdentitiesResponse, *http.Response, error) {\n\treturn r.ApiService.BatchPatchIdentitiesExecute(r)\n}\n\n/*\nBatchPatchIdentities Create multiple identities\n\nCreates multiple [identities](https://www.ory.com/docs/kratos/concepts/identity-user-model).\n\nYou can also use this endpoint to [import credentials](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities),\nincluding passwords, social sign-in settings, and multi-factor authentication methods.\n\nIf the patch includes hashed passwords you can import up to 1,000 identities per request.\n\nIf the patch includes at least one plaintext password you can import up to 200 identities per request.\n\nAvoid importing large batches with plaintext passwords. They can cause timeouts as the passwords need to be hashed before they are stored.\n\nIf at least one identity is imported successfully, the response status is 200 OK.\nIf all imports fail, the response is one of the following 4xx errors:\n400 Bad Request: The request payload is invalid or improperly formatted.\n409 Conflict: Duplicate identities or conflicting data were detected.\n\nIf you get a 504 Gateway Timeout:\nReduce the batch size\nAvoid duplicate identities\nPre-hash passwords with BCrypt\n\nIf the issue persists, contact support.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPIBatchPatchIdentitiesRequest\n*/\nfunc (a *IdentityAPIService) BatchPatchIdentities(ctx context.Context) IdentityAPIBatchPatchIdentitiesRequest {\n\treturn IdentityAPIBatchPatchIdentitiesRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return BatchPatchIdentitiesResponse\nfunc (a *IdentityAPIService) BatchPatchIdentitiesExecute(r IdentityAPIBatchPatchIdentitiesRequest) (*BatchPatchIdentitiesResponse, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPatch\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *BatchPatchIdentitiesResponse\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.BatchPatchIdentities\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.patchIdentitiesBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 409 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPICreateIdentityRequest struct {\n\tctx                context.Context\n\tApiService         IdentityAPI\n\tcreateIdentityBody *CreateIdentityBody\n}\n\nfunc (r IdentityAPICreateIdentityRequest) CreateIdentityBody(createIdentityBody CreateIdentityBody) IdentityAPICreateIdentityRequest {\n\tr.createIdentityBody = &createIdentityBody\n\treturn r\n}\n\nfunc (r IdentityAPICreateIdentityRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.CreateIdentityExecute(r)\n}\n\n/*\nCreateIdentity Create an Identity\n\nCreate an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model).  This endpoint can also be used to\n[import credentials](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities)\nfor instance passwords, social sign in configurations or multifactor methods.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPICreateIdentityRequest\n*/\nfunc (a *IdentityAPIService) CreateIdentity(ctx context.Context) IdentityAPICreateIdentityRequest {\n\treturn IdentityAPICreateIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) CreateIdentityExecute(r IdentityAPICreateIdentityRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.CreateIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.createIdentityBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 409 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPICreateRecoveryCodeForIdentityRequest struct {\n\tctx                               context.Context\n\tApiService                        IdentityAPI\n\tcreateRecoveryCodeForIdentityBody *CreateRecoveryCodeForIdentityBody\n}\n\nfunc (r IdentityAPICreateRecoveryCodeForIdentityRequest) CreateRecoveryCodeForIdentityBody(createRecoveryCodeForIdentityBody CreateRecoveryCodeForIdentityBody) IdentityAPICreateRecoveryCodeForIdentityRequest {\n\tr.createRecoveryCodeForIdentityBody = &createRecoveryCodeForIdentityBody\n\treturn r\n}\n\nfunc (r IdentityAPICreateRecoveryCodeForIdentityRequest) Execute() (*RecoveryCodeForIdentity, *http.Response, error) {\n\treturn r.ApiService.CreateRecoveryCodeForIdentityExecute(r)\n}\n\n/*\nCreateRecoveryCodeForIdentity Create a Recovery Code\n\nThis endpoint creates a recovery code which should be given to the user in order for them to recover\n(or activate) their account.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPICreateRecoveryCodeForIdentityRequest\n*/\nfunc (a *IdentityAPIService) CreateRecoveryCodeForIdentity(ctx context.Context) IdentityAPICreateRecoveryCodeForIdentityRequest {\n\treturn IdentityAPICreateRecoveryCodeForIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryCodeForIdentity\nfunc (a *IdentityAPIService) CreateRecoveryCodeForIdentityExecute(r IdentityAPICreateRecoveryCodeForIdentityRequest) (*RecoveryCodeForIdentity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryCodeForIdentity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.CreateRecoveryCodeForIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/recovery/code\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.createRecoveryCodeForIdentityBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPICreateRecoveryLinkForIdentityRequest struct {\n\tctx                               context.Context\n\tApiService                        IdentityAPI\n\treturnTo                          *string\n\tcreateRecoveryLinkForIdentityBody *CreateRecoveryLinkForIdentityBody\n}\n\nfunc (r IdentityAPICreateRecoveryLinkForIdentityRequest) ReturnTo(returnTo string) IdentityAPICreateRecoveryLinkForIdentityRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r IdentityAPICreateRecoveryLinkForIdentityRequest) CreateRecoveryLinkForIdentityBody(createRecoveryLinkForIdentityBody CreateRecoveryLinkForIdentityBody) IdentityAPICreateRecoveryLinkForIdentityRequest {\n\tr.createRecoveryLinkForIdentityBody = &createRecoveryLinkForIdentityBody\n\treturn r\n}\n\nfunc (r IdentityAPICreateRecoveryLinkForIdentityRequest) Execute() (*RecoveryLinkForIdentity, *http.Response, error) {\n\treturn r.ApiService.CreateRecoveryLinkForIdentityExecute(r)\n}\n\n/*\nCreateRecoveryLinkForIdentity Create a Recovery Link\n\nThis endpoint creates a recovery link which should be given to the user in order for them to recover\n(or activate) their account.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPICreateRecoveryLinkForIdentityRequest\n*/\nfunc (a *IdentityAPIService) CreateRecoveryLinkForIdentity(ctx context.Context) IdentityAPICreateRecoveryLinkForIdentityRequest {\n\treturn IdentityAPICreateRecoveryLinkForIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryLinkForIdentity\nfunc (a *IdentityAPIService) CreateRecoveryLinkForIdentityExecute(r IdentityAPICreateRecoveryLinkForIdentityRequest) (*RecoveryLinkForIdentity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryLinkForIdentity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.CreateRecoveryLinkForIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/recovery/link\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.createRecoveryLinkForIdentityBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIDeleteIdentityRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIDeleteIdentityRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DeleteIdentityExecute(r)\n}\n\n/*\nDeleteIdentity Delete an Identity\n\nCalling this endpoint irrecoverably and permanently deletes the [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) given its ID. This action can not be undone.\nThis endpoint returns 204 when the identity was deleted or 404 if the identity was not found.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the identity's ID.\n\t@return IdentityAPIDeleteIdentityRequest\n*/\nfunc (a *IdentityAPIService) DeleteIdentity(ctx context.Context, id string) IdentityAPIDeleteIdentityRequest {\n\treturn IdentityAPIDeleteIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\nfunc (a *IdentityAPIService) DeleteIdentityExecute(r IdentityAPIDeleteIdentityRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.DeleteIdentity\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype IdentityAPIDeleteIdentityCredentialsRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n\ttype_      string\n\tidentifier *string\n}\n\n// Identifier is the identifier of the OIDC/SAML credential to delete. Find the identifier by calling the &#x60;GET /admin/identities/{id}?include_credential&#x3D;{oidc,saml}&#x60; endpoint.\nfunc (r IdentityAPIDeleteIdentityCredentialsRequest) Identifier(identifier string) IdentityAPIDeleteIdentityCredentialsRequest {\n\tr.identifier = &identifier\n\treturn r\n}\n\nfunc (r IdentityAPIDeleteIdentityCredentialsRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DeleteIdentityCredentialsExecute(r)\n}\n\n/*\nDeleteIdentityCredentials Delete a credential for a specific identity\n\nDelete an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) credential by its type.\nYou cannot delete passkeys or code auth credentials through this API.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the identity's ID.\n\t@param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\t@return IdentityAPIDeleteIdentityCredentialsRequest\n*/\nfunc (a *IdentityAPIService) DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIDeleteIdentityCredentialsRequest {\n\treturn IdentityAPIDeleteIdentityCredentialsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t\ttype_:      type_,\n\t}\n}\n\n// Execute executes the request\nfunc (a *IdentityAPIService) DeleteIdentityCredentialsExecute(r IdentityAPIDeleteIdentityCredentialsRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.DeleteIdentityCredentials\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}/credentials/{type}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"type\"+\"}\", url.PathEscape(parameterValueToString(r.type_, \"type_\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.identifier != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identifier\", r.identifier, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype IdentityAPIDeleteIdentitySessionsRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIDeleteIdentitySessionsRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DeleteIdentitySessionsExecute(r)\n}\n\n/*\nDeleteIdentitySessions Delete & Invalidate an Identity's Sessions\n\nCalling this endpoint irrecoverably and permanently deletes and invalidates all sessions that belong to the given Identity.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the identity's ID.\n\t@return IdentityAPIDeleteIdentitySessionsRequest\n*/\nfunc (a *IdentityAPIService) DeleteIdentitySessions(ctx context.Context, id string) IdentityAPIDeleteIdentitySessionsRequest {\n\treturn IdentityAPIDeleteIdentitySessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\nfunc (a *IdentityAPIService) DeleteIdentitySessionsExecute(r IdentityAPIDeleteIdentitySessionsRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.DeleteIdentitySessions\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}/sessions\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype IdentityAPIDisableSessionRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIDisableSessionRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DisableSessionExecute(r)\n}\n\n/*\nDisableSession Deactivate a Session\n\nCalling this endpoint deactivates the specified session. Session data is not deleted.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the session's ID.\n\t@return IdentityAPIDisableSessionRequest\n*/\nfunc (a *IdentityAPIService) DisableSession(ctx context.Context, id string) IdentityAPIDisableSessionRequest {\n\treturn IdentityAPIDisableSessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\nfunc (a *IdentityAPIService) DisableSessionExecute(r IdentityAPIDisableSessionRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.DisableSession\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/sessions/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype IdentityAPIExtendSessionRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIExtendSessionRequest) Execute() (*Session, *http.Response, error) {\n\treturn r.ApiService.ExtendSessionExecute(r)\n}\n\n/*\nExtendSession Extend a Session\n\nCalling this endpoint extends the given session ID. If `session.earliest_possible_extend` is set it\nwill only extend the session after the specified time has passed.\n\nThis endpoint returns per default a 204 No Content response on success. Older Ory Network projects may\nreturn a 200 OK response with the session in the body. Returning the session as part of the response\nwill be deprecated in the future and should not be relied upon.\n\nThis endpoint ignores consecutive requests to extend the same session and returns a 404 error in those\nscenarios. This endpoint also returns 404 errors if the session does not exist.\n\nRetrieve the session ID from the `/sessions/whoami` endpoint / `toSession` SDK method.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the session's ID.\n\t@return IdentityAPIExtendSessionRequest\n*/\nfunc (a *IdentityAPIService) ExtendSession(ctx context.Context, id string) IdentityAPIExtendSessionRequest {\n\treturn IdentityAPIExtendSessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Session\nfunc (a *IdentityAPIService) ExtendSessionExecute(r IdentityAPIExtendSessionRequest) (*Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPatch\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ExtendSession\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/sessions/{id}/extend\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIGetIdentityRequest struct {\n\tctx               context.Context\n\tApiService        IdentityAPI\n\tid                string\n\tincludeCredential *[]string\n}\n\n// Include Credentials in Response  Include any credential, for example &#x60;password&#x60; or &#x60;oidc&#x60;, in the response. When set to &#x60;oidc&#x60;, This will return the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\nfunc (r IdentityAPIGetIdentityRequest) IncludeCredential(includeCredential []string) IdentityAPIGetIdentityRequest {\n\tr.includeCredential = &includeCredential\n\treturn r\n}\n\nfunc (r IdentityAPIGetIdentityRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.GetIdentityExecute(r)\n}\n\n/*\nGetIdentity Get an Identity\n\nReturn an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its ID. You can optionally\ninclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID must be set to the ID of identity you want to get\n\t@return IdentityAPIGetIdentityRequest\n*/\nfunc (a *IdentityAPIService) GetIdentity(ctx context.Context, id string) IdentityAPIGetIdentityRequest {\n\treturn IdentityAPIGetIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) GetIdentityExecute(r IdentityAPIGetIdentityRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.GetIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.includeCredential != nil {\n\t\tt := *r.includeCredential\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIGetIdentityByExternalIDRequest struct {\n\tctx               context.Context\n\tApiService        IdentityAPI\n\texternalID        string\n\tincludeCredential *[]string\n}\n\n// Include Credentials in Response  Include any credential, for example &#x60;password&#x60; or &#x60;oidc&#x60;, in the response. When set to &#x60;oidc&#x60;, This will return the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\nfunc (r IdentityAPIGetIdentityByExternalIDRequest) IncludeCredential(includeCredential []string) IdentityAPIGetIdentityByExternalIDRequest {\n\tr.includeCredential = &includeCredential\n\treturn r\n}\n\nfunc (r IdentityAPIGetIdentityByExternalIDRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.GetIdentityByExternalIDExecute(r)\n}\n\n/*\nGetIdentityByExternalID Get an Identity by its External ID\n\nReturn an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its external ID. You can optionally\ninclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param externalID ExternalID must be set to the ID of identity you want to get\n\t@return IdentityAPIGetIdentityByExternalIDRequest\n*/\nfunc (a *IdentityAPIService) GetIdentityByExternalID(ctx context.Context, externalID string) IdentityAPIGetIdentityByExternalIDRequest {\n\treturn IdentityAPIGetIdentityByExternalIDRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\texternalID: externalID,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) GetIdentityByExternalIDExecute(r IdentityAPIGetIdentityByExternalIDRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.GetIdentityByExternalID\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/by/external/{externalID}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"externalID\"+\"}\", url.PathEscape(parameterValueToString(r.externalID, \"externalID\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.includeCredential != nil {\n\t\tt := *r.includeCredential\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIGetIdentitySchemaRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIGetIdentitySchemaRequest) Execute() (map[string]interface{}, *http.Response, error) {\n\treturn r.ApiService.GetIdentitySchemaExecute(r)\n}\n\n/*\nGetIdentitySchema Get Identity JSON Schema\n\nReturn a specific identity schema.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID must be set to the ID of schema you want to get\n\t@return IdentityAPIGetIdentitySchemaRequest\n*/\nfunc (a *IdentityAPIService) GetIdentitySchema(ctx context.Context, id string) IdentityAPIGetIdentitySchemaRequest {\n\treturn IdentityAPIGetIdentitySchemaRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return map[string]interface{}\nfunc (a *IdentityAPIService) GetIdentitySchemaExecute(r IdentityAPIGetIdentitySchemaRequest) (map[string]interface{}, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue map[string]interface{}\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.GetIdentitySchema\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/schemas/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIGetSessionRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n\texpand     *[]string\n}\n\n// ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session. Example - ?expand&#x3D;Identity&amp;expand&#x3D;Devices If no value is provided, the expandable properties are skipped.\nfunc (r IdentityAPIGetSessionRequest) Expand(expand []string) IdentityAPIGetSessionRequest {\n\tr.expand = &expand\n\treturn r\n}\n\nfunc (r IdentityAPIGetSessionRequest) Execute() (*Session, *http.Response, error) {\n\treturn r.ApiService.GetSessionExecute(r)\n}\n\n/*\nGetSession Get Session\n\nThis endpoint is useful for:\n\nGetting a session object with all specified expandables that exist in an administrative context.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the session's ID.\n\t@return IdentityAPIGetSessionRequest\n*/\nfunc (a *IdentityAPIService) GetSession(ctx context.Context, id string) IdentityAPIGetSessionRequest {\n\treturn IdentityAPIGetSessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Session\nfunc (a *IdentityAPIService) GetSessionExecute(r IdentityAPIGetSessionRequest) (*Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.GetSession\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/sessions/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.expand != nil {\n\t\tt := *r.expand\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"expand\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"expand\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIListIdentitiesRequest struct {\n\tctx                                 context.Context\n\tApiService                          IdentityAPI\n\tperPage                             *int64\n\tpage                                *int64\n\tpageSize                            *int64\n\tpageToken                           *string\n\tconsistency                         *string\n\tids                                 *[]string\n\tcredentialsIdentifier               *string\n\tpreviewCredentialsIdentifierSimilar *string\n\tincludeCredential                   *[]string\n\torganizationId                      *string\n}\n\n// Deprecated Items per Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This is the number of items per page.\nfunc (r IdentityAPIListIdentitiesRequest) PerPage(perPage int64) IdentityAPIListIdentitiesRequest {\n\tr.perPage = &perPage\n\treturn r\n}\n\n// Deprecated Pagination Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This value is currently an integer, but it is not sequential. The value is not the page number, but a reference. The next page can be any number and some numbers might return an empty list.  For example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist. The first page can be retrieved by omitting this parameter. Following page pointers will be returned in the &#x60;Link&#x60; header.\nfunc (r IdentityAPIListIdentitiesRequest) Page(page int64) IdentityAPIListIdentitiesRequest {\n\tr.page = &page\n\treturn r\n}\n\n// Page Size  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitiesRequest) PageSize(pageSize int64) IdentityAPIListIdentitiesRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitiesRequest) PageToken(pageToken string) IdentityAPIListIdentitiesRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Read Consistency Level (preview)  The read consistency level determines the consistency guarantee for reads:  strong (slow): The read is guaranteed to return the most recent data committed at the start of the read. eventual (very fast): The result will return data that is about 4.8 seconds old.  The default consistency guarantee can be changed in the Ory Network Console or using the Ory CLI with &#x60;ory patch project --replace &#39;/previews/default_read_consistency_level&#x3D;\\&quot;strong\\&quot;&#39;&#x60;.  Setting the default consistency level to &#x60;eventual&#x60; may cause regressions in the future as we add consistency controls to more APIs. Currently, the following APIs will be affected by this setting:  &#x60;GET /admin/identities&#x60;  This feature is in preview and only available in Ory Network.  ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level. strong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level. eventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\nfunc (r IdentityAPIListIdentitiesRequest) Consistency(consistency string) IdentityAPIListIdentitiesRequest {\n\tr.consistency = &consistency\n\treturn r\n}\n\n// Retrieve multiple identities by their IDs.  This parameter has the following limitations:  Duplicate or non-existent IDs are ignored. The order of returned IDs may be different from the request. This filter does not support pagination. You must implement your own pagination as the maximum number of items returned by this endpoint may not exceed a certain threshold (currently 500).\nfunc (r IdentityAPIListIdentitiesRequest) Ids(ids []string) IdentityAPIListIdentitiesRequest {\n\tr.ids = &ids\n\treturn r\n}\n\n// CredentialsIdentifier is the identifier (username, email) of the credentials to look up using exact match. Only one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\nfunc (r IdentityAPIListIdentitiesRequest) CredentialsIdentifier(credentialsIdentifier string) IdentityAPIListIdentitiesRequest {\n\tr.credentialsIdentifier = &credentialsIdentifier\n\treturn r\n}\n\n// This is an EXPERIMENTAL parameter that WILL CHANGE. Do NOT rely on consistent, deterministic behavior. THIS PARAMETER WILL BE REMOVED IN AN UPCOMING RELEASE WITHOUT ANY MIGRATION PATH.  CredentialsIdentifierSimilar is the (partial) identifier (username, email) of the credentials to look up using similarity search. Only one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\nfunc (r IdentityAPIListIdentitiesRequest) PreviewCredentialsIdentifierSimilar(previewCredentialsIdentifierSimilar string) IdentityAPIListIdentitiesRequest {\n\tr.previewCredentialsIdentifierSimilar = &previewCredentialsIdentifierSimilar\n\treturn r\n}\n\n// Include Credentials in Response  Include any credential, for example &#x60;password&#x60; or &#x60;oidc&#x60;, in the response. When set to &#x60;oidc&#x60;, This will return the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\nfunc (r IdentityAPIListIdentitiesRequest) IncludeCredential(includeCredential []string) IdentityAPIListIdentitiesRequest {\n\tr.includeCredential = &includeCredential\n\treturn r\n}\n\n// List identities that belong to a specific organization.\nfunc (r IdentityAPIListIdentitiesRequest) OrganizationId(organizationId string) IdentityAPIListIdentitiesRequest {\n\tr.organizationId = &organizationId\n\treturn r\n}\n\nfunc (r IdentityAPIListIdentitiesRequest) Execute() ([]Identity, *http.Response, error) {\n\treturn r.ApiService.ListIdentitiesExecute(r)\n}\n\n/*\nListIdentities List Identities\n\nLists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPIListIdentitiesRequest\n*/\nfunc (a *IdentityAPIService) ListIdentities(ctx context.Context) IdentityAPIListIdentitiesRequest {\n\treturn IdentityAPIListIdentitiesRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Identity\nfunc (a *IdentityAPIService) ListIdentitiesExecute(r IdentityAPIListIdentitiesRequest) ([]Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ListIdentities\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.perPage != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"per_page\", r.perPage, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.perPage = &defaultValue\n\t}\n\tif r.page != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page\", r.page, \"form\", \"\")\n\t}\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\tif r.consistency != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"consistency\", r.consistency, \"form\", \"\")\n\t}\n\tif r.ids != nil {\n\t\tt := *r.ids\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"ids\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"ids\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\tif r.credentialsIdentifier != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"credentials_identifier\", r.credentialsIdentifier, \"form\", \"\")\n\t}\n\tif r.previewCredentialsIdentifierSimilar != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"preview_credentials_identifier_similar\", r.previewCredentialsIdentifierSimilar, \"form\", \"\")\n\t}\n\tif r.includeCredential != nil {\n\t\tt := *r.includeCredential\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\tif r.organizationId != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization_id\", r.organizationId, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIListIdentitySchemasRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tperPage    *int64\n\tpage       *int64\n\tpageSize   *int64\n\tpageToken  *string\n}\n\n// Deprecated Items per Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This is the number of items per page.\nfunc (r IdentityAPIListIdentitySchemasRequest) PerPage(perPage int64) IdentityAPIListIdentitySchemasRequest {\n\tr.perPage = &perPage\n\treturn r\n}\n\n// Deprecated Pagination Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This value is currently an integer, but it is not sequential. The value is not the page number, but a reference. The next page can be any number and some numbers might return an empty list.  For example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist. The first page can be retrieved by omitting this parameter. Following page pointers will be returned in the &#x60;Link&#x60; header.\nfunc (r IdentityAPIListIdentitySchemasRequest) Page(page int64) IdentityAPIListIdentitySchemasRequest {\n\tr.page = &page\n\treturn r\n}\n\n// Page Size  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitySchemasRequest) PageSize(pageSize int64) IdentityAPIListIdentitySchemasRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitySchemasRequest) PageToken(pageToken string) IdentityAPIListIdentitySchemasRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\nfunc (r IdentityAPIListIdentitySchemasRequest) Execute() ([]IdentitySchemaContainer, *http.Response, error) {\n\treturn r.ApiService.ListIdentitySchemasExecute(r)\n}\n\n/*\nListIdentitySchemas Get all Identity Schemas\n\nReturns a list of all identity schemas currently in use.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPIListIdentitySchemasRequest\n*/\nfunc (a *IdentityAPIService) ListIdentitySchemas(ctx context.Context) IdentityAPIListIdentitySchemasRequest {\n\treturn IdentityAPIListIdentitySchemasRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []IdentitySchemaContainer\nfunc (a *IdentityAPIService) ListIdentitySchemasExecute(r IdentityAPIListIdentitySchemasRequest) ([]IdentitySchemaContainer, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []IdentitySchemaContainer\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ListIdentitySchemas\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/schemas\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.perPage != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"per_page\", r.perPage, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.perPage = &defaultValue\n\t}\n\tif r.page != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page\", r.page, \"form\", \"\")\n\t}\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIListIdentitySessionsRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n\tperPage    *int64\n\tpage       *int64\n\tpageSize   *int64\n\tpageToken  *string\n\tactive     *bool\n}\n\n// Deprecated Items per Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This is the number of items per page.\nfunc (r IdentityAPIListIdentitySessionsRequest) PerPage(perPage int64) IdentityAPIListIdentitySessionsRequest {\n\tr.perPage = &perPage\n\treturn r\n}\n\n// Deprecated Pagination Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This value is currently an integer, but it is not sequential. The value is not the page number, but a reference. The next page can be any number and some numbers might return an empty list.  For example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist. The first page can be retrieved by omitting this parameter. Following page pointers will be returned in the &#x60;Link&#x60; header.\nfunc (r IdentityAPIListIdentitySessionsRequest) Page(page int64) IdentityAPIListIdentitySessionsRequest {\n\tr.page = &page\n\treturn r\n}\n\n// Page Size  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitySessionsRequest) PageSize(pageSize int64) IdentityAPIListIdentitySessionsRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitySessionsRequest) PageToken(pageToken string) IdentityAPIListIdentitySessionsRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\nfunc (r IdentityAPIListIdentitySessionsRequest) Active(active bool) IdentityAPIListIdentitySessionsRequest {\n\tr.active = &active\n\treturn r\n}\n\nfunc (r IdentityAPIListIdentitySessionsRequest) Execute() ([]Session, *http.Response, error) {\n\treturn r.ApiService.ListIdentitySessionsExecute(r)\n}\n\n/*\nListIdentitySessions List an Identity's Sessions\n\nThis endpoint returns all sessions that belong to the given Identity.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the identity's ID.\n\t@return IdentityAPIListIdentitySessionsRequest\n*/\nfunc (a *IdentityAPIService) ListIdentitySessions(ctx context.Context, id string) IdentityAPIListIdentitySessionsRequest {\n\treturn IdentityAPIListIdentitySessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Session\nfunc (a *IdentityAPIService) ListIdentitySessionsExecute(r IdentityAPIListIdentitySessionsRequest) ([]Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ListIdentitySessions\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}/sessions\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.perPage != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"per_page\", r.perPage, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.perPage = &defaultValue\n\t}\n\tif r.page != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page\", r.page, \"form\", \"\")\n\t}\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\tif r.active != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"active\", r.active, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIListSessionsRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tpageSize   *int64\n\tpageToken  *string\n\tactive     *bool\n\texpand     *[]string\n}\n\n// Items per Page  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListSessionsRequest) PageSize(pageSize int64) IdentityAPIListSessionsRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListSessionsRequest) PageToken(pageToken string) IdentityAPIListSessionsRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\nfunc (r IdentityAPIListSessionsRequest) Active(active bool) IdentityAPIListSessionsRequest {\n\tr.active = &active\n\treturn r\n}\n\n// ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session. If no value is provided, the expandable properties are skipped.\nfunc (r IdentityAPIListSessionsRequest) Expand(expand []string) IdentityAPIListSessionsRequest {\n\tr.expand = &expand\n\treturn r\n}\n\nfunc (r IdentityAPIListSessionsRequest) Execute() ([]Session, *http.Response, error) {\n\treturn r.ApiService.ListSessionsExecute(r)\n}\n\n/*\nListSessions List All Sessions\n\nListing all sessions that exist.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPIListSessionsRequest\n*/\nfunc (a *IdentityAPIService) ListSessions(ctx context.Context) IdentityAPIListSessionsRequest {\n\treturn IdentityAPIListSessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Session\nfunc (a *IdentityAPIService) ListSessionsExecute(r IdentityAPIListSessionsRequest) ([]Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ListSessions\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/sessions\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\tif r.active != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"active\", r.active, \"form\", \"\")\n\t}\n\tif r.expand != nil {\n\t\tt := *r.expand\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"expand\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"expand\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIPatchIdentityRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n\tjsonPatch  *[]JsonPatch\n}\n\nfunc (r IdentityAPIPatchIdentityRequest) JsonPatch(jsonPatch []JsonPatch) IdentityAPIPatchIdentityRequest {\n\tr.jsonPatch = &jsonPatch\n\treturn r\n}\n\nfunc (r IdentityAPIPatchIdentityRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.PatchIdentityExecute(r)\n}\n\n/*\nPatchIdentity Patch an Identity\n\nPartially updates an [identity's](https://www.ory.sh/docs/kratos/concepts/identity-user-model) field using [JSON Patch](https://jsonpatch.com/).\nThe fields `id`, `stateChangedAt` and `credentials` can not be updated using this method.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID must be set to the ID of identity you want to update\n\t@return IdentityAPIPatchIdentityRequest\n*/\nfunc (a *IdentityAPIService) PatchIdentity(ctx context.Context, id string) IdentityAPIPatchIdentityRequest {\n\treturn IdentityAPIPatchIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) PatchIdentityExecute(r IdentityAPIPatchIdentityRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPatch\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.PatchIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.jsonPatch\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 409 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIUpdateIdentityRequest struct {\n\tctx                context.Context\n\tApiService         IdentityAPI\n\tid                 string\n\tupdateIdentityBody *UpdateIdentityBody\n}\n\nfunc (r IdentityAPIUpdateIdentityRequest) UpdateIdentityBody(updateIdentityBody UpdateIdentityBody) IdentityAPIUpdateIdentityRequest {\n\tr.updateIdentityBody = &updateIdentityBody\n\treturn r\n}\n\nfunc (r IdentityAPIUpdateIdentityRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.UpdateIdentityExecute(r)\n}\n\n/*\nUpdateIdentity Update an Identity\n\nThis endpoint updates an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model). The full identity\npayload, except credentials, is expected. For partial updates, use the [patchIdentity](https://www.ory.sh/docs/reference/api#tag/identity/operation/patchIdentity) operation.\n\nA credential can be provided via the `credentials` field in the request body.\nIf provided, the credentials will be imported and added to the existing credentials of the identity.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID must be set to the ID of identity you want to update\n\t@return IdentityAPIUpdateIdentityRequest\n*/\nfunc (a *IdentityAPIService) UpdateIdentity(ctx context.Context, id string) IdentityAPIUpdateIdentityRequest {\n\treturn IdentityAPIUpdateIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) UpdateIdentityExecute(r IdentityAPIUpdateIdentityRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPut\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.UpdateIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateIdentityBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 409 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n"
  },
  {
    "path": "pkg/client-go/api_metadata.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\ntype MetadataAPI interface {\n\n\t/*\n\t\t\tGetVersion Return Running Software Version.\n\n\t\t\tThis endpoint returns the version of Ory Kratos.\n\n\t\tIf the service supports TLS Edge Termination, this endpoint does not require the\n\t\t`X-Forwarded-Proto` header to be set.\n\n\t\tBe aware that if you are running multiple nodes of this service, the version will never\n\t\trefer to the cluster state, only to a single instance.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return MetadataAPIGetVersionRequest\n\t*/\n\tGetVersion(ctx context.Context) MetadataAPIGetVersionRequest\n\n\t// GetVersionExecute executes the request\n\t//  @return GetVersion200Response\n\tGetVersionExecute(r MetadataAPIGetVersionRequest) (*GetVersion200Response, *http.Response, error)\n\n\t/*\n\t\t\tIsAlive Check HTTP Server Status\n\n\t\t\tThis endpoint returns a HTTP 200 status code when Ory Kratos is accepting incoming\n\t\tHTTP requests. This status does currently not include checks whether the database connection is working.\n\n\t\tIf the service supports TLS Edge Termination, this endpoint does not require the\n\t\t`X-Forwarded-Proto` header to be set.\n\n\t\tBe aware that if you are running multiple nodes of this service, the health status will never\n\t\trefer to the cluster state, only to a single instance.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return MetadataAPIIsAliveRequest\n\t*/\n\tIsAlive(ctx context.Context) MetadataAPIIsAliveRequest\n\n\t// IsAliveExecute executes the request\n\t//  @return IsAlive200Response\n\tIsAliveExecute(r MetadataAPIIsAliveRequest) (*IsAlive200Response, *http.Response, error)\n\n\t/*\n\t\t\tIsReady Check HTTP Server and Database Status\n\n\t\t\tThis endpoint returns a HTTP 200 status code when Ory Kratos is up running and the environment dependencies (e.g.\n\t\tthe database) are responsive as well.\n\n\t\tIf the service supports TLS Edge Termination, this endpoint does not require the\n\t\t`X-Forwarded-Proto` header to be set.\n\n\t\tBe aware that if you are running multiple nodes of Ory Kratos, the health status will never\n\t\trefer to the cluster state, only to a single instance.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return MetadataAPIIsReadyRequest\n\t*/\n\tIsReady(ctx context.Context) MetadataAPIIsReadyRequest\n\n\t// IsReadyExecute executes the request\n\t//  @return IsAlive200Response\n\tIsReadyExecute(r MetadataAPIIsReadyRequest) (*IsAlive200Response, *http.Response, error)\n}\n\n// MetadataAPIService MetadataAPI service\ntype MetadataAPIService service\n\ntype MetadataAPIGetVersionRequest struct {\n\tctx        context.Context\n\tApiService MetadataAPI\n}\n\nfunc (r MetadataAPIGetVersionRequest) Execute() (*GetVersion200Response, *http.Response, error) {\n\treturn r.ApiService.GetVersionExecute(r)\n}\n\n/*\nGetVersion Return Running Software Version.\n\nThis endpoint returns the version of Ory Kratos.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the version will never\nrefer to the cluster state, only to a single instance.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return MetadataAPIGetVersionRequest\n*/\nfunc (a *MetadataAPIService) GetVersion(ctx context.Context) MetadataAPIGetVersionRequest {\n\treturn MetadataAPIGetVersionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return GetVersion200Response\nfunc (a *MetadataAPIService) GetVersionExecute(r MetadataAPIGetVersionRequest) (*GetVersion200Response, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *GetVersion200Response\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"MetadataAPIService.GetVersion\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/version\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype MetadataAPIIsAliveRequest struct {\n\tctx        context.Context\n\tApiService MetadataAPI\n}\n\nfunc (r MetadataAPIIsAliveRequest) Execute() (*IsAlive200Response, *http.Response, error) {\n\treturn r.ApiService.IsAliveExecute(r)\n}\n\n/*\nIsAlive Check HTTP Server Status\n\nThis endpoint returns a HTTP 200 status code when Ory Kratos is accepting incoming\nHTTP requests. This status does currently not include checks whether the database connection is working.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return MetadataAPIIsAliveRequest\n*/\nfunc (a *MetadataAPIService) IsAlive(ctx context.Context) MetadataAPIIsAliveRequest {\n\treturn MetadataAPIIsAliveRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return IsAlive200Response\nfunc (a *MetadataAPIService) IsAliveExecute(r MetadataAPIIsAliveRequest) (*IsAlive200Response, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *IsAlive200Response\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"MetadataAPIService.IsAlive\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/health/alive\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\", \"text/plain\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v string\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype MetadataAPIIsReadyRequest struct {\n\tctx        context.Context\n\tApiService MetadataAPI\n}\n\nfunc (r MetadataAPIIsReadyRequest) Execute() (*IsAlive200Response, *http.Response, error) {\n\treturn r.ApiService.IsReadyExecute(r)\n}\n\n/*\nIsReady Check HTTP Server and Database Status\n\nThis endpoint returns a HTTP 200 status code when Ory Kratos is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of Ory Kratos, the health status will never\nrefer to the cluster state, only to a single instance.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return MetadataAPIIsReadyRequest\n*/\nfunc (a *MetadataAPIService) IsReady(ctx context.Context) MetadataAPIIsReadyRequest {\n\treturn MetadataAPIIsReadyRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return IsAlive200Response\nfunc (a *MetadataAPIService) IsReadyExecute(r MetadataAPIIsReadyRequest) (*IsAlive200Response, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *IsAlive200Response\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"MetadataAPIService.IsReady\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/health/ready\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\", \"text/plain\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 503 {\n\t\t\tvar v IsReady503Response\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v string\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n"
  },
  {
    "path": "pkg/client-go/client.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"encoding/xml\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n)\n\nvar (\n\tJsonCheck       = regexp.MustCompile(`(?i:(?:application|text)/(?:[^;]+\\+)?json)`)\n\tXmlCheck        = regexp.MustCompile(`(?i:(?:application|text)/(?:[^;]+\\+)?xml)`)\n\tqueryParamSplit = regexp.MustCompile(`(^|&)([^&]+)`)\n\tqueryDescape    = strings.NewReplacer(\"%5B\", \"[\", \"%5D\", \"]\")\n)\n\n// APIClient manages communication with the Ory Identities API API v\n// In most cases there should be only one, shared, APIClient.\ntype APIClient struct {\n\tcfg    *Configuration\n\tcommon service // Reuse a single struct instead of allocating one for each service on the heap.\n\n\t// API Services\n\n\tCourierAPI CourierAPI\n\n\tFrontendAPI FrontendAPI\n\n\tIdentityAPI IdentityAPI\n\n\tMetadataAPI MetadataAPI\n}\n\ntype service struct {\n\tclient *APIClient\n}\n\n// NewAPIClient creates a new API client. Requires a userAgent string describing your application.\n// optionally a custom http.Client to allow for advanced features such as caching.\nfunc NewAPIClient(cfg *Configuration) *APIClient {\n\tif cfg.HTTPClient == nil {\n\t\tcfg.HTTPClient = http.DefaultClient\n\t}\n\n\tc := &APIClient{}\n\tc.cfg = cfg\n\tc.common.client = c\n\n\t// API Services\n\tc.CourierAPI = (*CourierAPIService)(&c.common)\n\tc.FrontendAPI = (*FrontendAPIService)(&c.common)\n\tc.IdentityAPI = (*IdentityAPIService)(&c.common)\n\tc.MetadataAPI = (*MetadataAPIService)(&c.common)\n\n\treturn c\n}\n\nfunc atoi(in string) (int, error) {\n\treturn strconv.Atoi(in)\n}\n\n// selectHeaderContentType select a content type from the available list.\nfunc selectHeaderContentType(contentTypes []string) string {\n\tif len(contentTypes) == 0 {\n\t\treturn \"\"\n\t}\n\tif contains(contentTypes, \"application/json\") {\n\t\treturn \"application/json\"\n\t}\n\treturn contentTypes[0] // use the first content type specified in 'consumes'\n}\n\n// selectHeaderAccept join all accept types and return\nfunc selectHeaderAccept(accepts []string) string {\n\tif len(accepts) == 0 {\n\t\treturn \"\"\n\t}\n\n\tif contains(accepts, \"application/json\") {\n\t\treturn \"application/json\"\n\t}\n\n\treturn strings.Join(accepts, \",\")\n}\n\n// contains is a case insensitive match, finding needle in a haystack\nfunc contains(haystack []string, needle string) bool {\n\tfor _, a := range haystack {\n\t\tif strings.EqualFold(a, needle) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Verify optional parameters are of the correct type.\nfunc typeCheckParameter(obj interface{}, expected string, name string) error {\n\t// Make sure there is an object.\n\tif obj == nil {\n\t\treturn nil\n\t}\n\n\t// Check the type is as expected.\n\tif reflect.TypeOf(obj).String() != expected {\n\t\treturn fmt.Errorf(\"expected %s to be of type %s but received %s\", name, expected, reflect.TypeOf(obj).String())\n\t}\n\treturn nil\n}\n\nfunc parameterValueToString(obj interface{}, key string) string {\n\tif reflect.TypeOf(obj).Kind() != reflect.Ptr {\n\t\tif actualObj, ok := obj.(interface{ GetActualInstanceValue() interface{} }); ok {\n\t\t\treturn fmt.Sprintf(\"%v\", actualObj.GetActualInstanceValue())\n\t\t}\n\n\t\treturn fmt.Sprintf(\"%v\", obj)\n\t}\n\tvar param, ok = obj.(MappedNullable)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\tdataMap, err := param.ToMap()\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn fmt.Sprintf(\"%v\", dataMap[key])\n}\n\n// parameterAddToHeaderOrQuery adds the provided object to the request header or url query\n// supporting deep object syntax\nfunc parameterAddToHeaderOrQuery(headerOrQueryParams interface{}, keyPrefix string, obj interface{}, style string, collectionType string) {\n\tvar v = reflect.ValueOf(obj)\n\tvar value = \"\"\n\tif v == reflect.ValueOf(nil) {\n\t\tvalue = \"null\"\n\t} else {\n\t\tswitch v.Kind() {\n\t\tcase reflect.Invalid:\n\t\t\tvalue = \"invalid\"\n\n\t\tcase reflect.Struct:\n\t\t\tif t, ok := obj.(MappedNullable); ok {\n\t\t\t\tdataMap, err := t.ToMap()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, dataMap, style, collectionType)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif t, ok := obj.(time.Time); ok {\n\t\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, t.Format(time.RFC3339Nano), style, collectionType)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvalue = v.Type().String() + \" value\"\n\t\tcase reflect.Slice:\n\t\t\tvar indValue = reflect.ValueOf(obj)\n\t\t\tif indValue == reflect.ValueOf(nil) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvar lenIndValue = indValue.Len()\n\t\t\tfor i := 0; i < lenIndValue; i++ {\n\t\t\t\tvar arrayValue = indValue.Index(i)\n\t\t\t\tvar keyPrefixForCollectionType = keyPrefix\n\t\t\t\tif style == \"deepObject\" {\n\t\t\t\t\tkeyPrefixForCollectionType = keyPrefix + \"[\" + strconv.Itoa(i) + \"]\"\n\t\t\t\t}\n\t\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefixForCollectionType, arrayValue.Interface(), style, collectionType)\n\t\t\t}\n\t\t\treturn\n\n\t\tcase reflect.Map:\n\t\t\tvar indValue = reflect.ValueOf(obj)\n\t\t\tif indValue == reflect.ValueOf(nil) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\titer := indValue.MapRange()\n\t\t\tfor iter.Next() {\n\t\t\t\tk, v := iter.Key(), iter.Value()\n\t\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, fmt.Sprintf(\"%s[%s]\", keyPrefix, k.String()), v.Interface(), style, collectionType)\n\t\t\t}\n\t\t\treturn\n\n\t\tcase reflect.Interface:\n\t\t\tfallthrough\n\t\tcase reflect.Ptr:\n\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, v.Elem().Interface(), style, collectionType)\n\t\t\treturn\n\n\t\tcase reflect.Int, reflect.Int8, reflect.Int16,\n\t\t\treflect.Int32, reflect.Int64:\n\t\t\tvalue = strconv.FormatInt(v.Int(), 10)\n\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16,\n\t\t\treflect.Uint32, reflect.Uint64, reflect.Uintptr:\n\t\t\tvalue = strconv.FormatUint(v.Uint(), 10)\n\t\tcase reflect.Float32, reflect.Float64:\n\t\t\tvalue = strconv.FormatFloat(v.Float(), 'g', -1, 32)\n\t\tcase reflect.Bool:\n\t\t\tvalue = strconv.FormatBool(v.Bool())\n\t\tcase reflect.String:\n\t\t\tvalue = v.String()\n\t\tdefault:\n\t\t\tvalue = v.Type().String() + \" value\"\n\t\t}\n\t}\n\n\tswitch valuesMap := headerOrQueryParams.(type) {\n\tcase url.Values:\n\t\tif collectionType == \"csv\" && valuesMap.Get(keyPrefix) != \"\" {\n\t\t\tvaluesMap.Set(keyPrefix, valuesMap.Get(keyPrefix)+\",\"+value)\n\t\t} else {\n\t\t\tvaluesMap.Add(keyPrefix, value)\n\t\t}\n\t\tbreak\n\tcase map[string]string:\n\t\tvaluesMap[keyPrefix] = value\n\t\tbreak\n\t}\n}\n\n// helper for converting interface{} parameters to json strings\nfunc parameterToJson(obj interface{}) (string, error) {\n\tjsonBuf, err := json.Marshal(obj)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(jsonBuf), err\n}\n\n// callAPI do the request.\nfunc (c *APIClient) callAPI(request *http.Request) (*http.Response, error) {\n\tif c.cfg.Debug {\n\t\tdump, err := httputil.DumpRequestOut(request, true)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlog.Printf(\"\\n%s\\n\", string(dump))\n\t}\n\n\tresp, err := c.cfg.HTTPClient.Do(request)\n\tif err != nil {\n\t\treturn resp, err\n\t}\n\n\tif c.cfg.Debug {\n\t\tdump, err := httputil.DumpResponse(resp, true)\n\t\tif err != nil {\n\t\t\treturn resp, err\n\t\t}\n\t\tlog.Printf(\"\\n%s\\n\", string(dump))\n\t}\n\treturn resp, err\n}\n\n// Allow modification of underlying config for alternate implementations and testing\n// Caution: modifying the configuration while live can cause data races and potentially unwanted behavior\nfunc (c *APIClient) GetConfig() *Configuration {\n\treturn c.cfg\n}\n\ntype formFile struct {\n\tfileBytes    []byte\n\tfileName     string\n\tformFileName string\n}\n\n// prepareRequest build the request\nfunc (c *APIClient) prepareRequest(\n\tctx context.Context,\n\tpath string, method string,\n\tpostBody interface{},\n\theaderParams map[string]string,\n\tqueryParams url.Values,\n\tformParams url.Values,\n\tformFiles []formFile) (localVarRequest *http.Request, err error) {\n\n\tvar body *bytes.Buffer\n\n\t// Detect postBody type and post.\n\tif postBody != nil {\n\t\tcontentType := headerParams[\"Content-Type\"]\n\t\tif contentType == \"\" {\n\t\t\tcontentType = detectContentType(postBody)\n\t\t\theaderParams[\"Content-Type\"] = contentType\n\t\t}\n\n\t\tbody, err = setBody(postBody, contentType)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// add form parameters and file if available.\n\tif strings.HasPrefix(headerParams[\"Content-Type\"], \"multipart/form-data\") && len(formParams) > 0 || (len(formFiles) > 0) {\n\t\tif body != nil {\n\t\t\treturn nil, errors.New(\"Cannot specify postBody and multipart form at the same time.\")\n\t\t}\n\t\tbody = &bytes.Buffer{}\n\t\tw := multipart.NewWriter(body)\n\n\t\tfor k, v := range formParams {\n\t\t\tfor _, iv := range v {\n\t\t\t\tif strings.HasPrefix(k, \"@\") { // file\n\t\t\t\t\terr = addFile(w, k[1:], iv)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t} else { // form value\n\t\t\t\t\tw.WriteField(k, iv)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor _, formFile := range formFiles {\n\t\t\tif len(formFile.fileBytes) > 0 && formFile.fileName != \"\" {\n\t\t\t\tw.Boundary()\n\t\t\t\tpart, err := w.CreateFormFile(formFile.formFileName, filepath.Base(formFile.fileName))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\t_, err = part.Write(formFile.fileBytes)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Set the Boundary in the Content-Type\n\t\theaderParams[\"Content-Type\"] = w.FormDataContentType()\n\n\t\t// Set Content-Length\n\t\theaderParams[\"Content-Length\"] = fmt.Sprintf(\"%d\", body.Len())\n\t\tw.Close()\n\t}\n\n\tif strings.HasPrefix(headerParams[\"Content-Type\"], \"application/x-www-form-urlencoded\") && len(formParams) > 0 {\n\t\tif body != nil {\n\t\t\treturn nil, errors.New(\"Cannot specify postBody and x-www-form-urlencoded form at the same time.\")\n\t\t}\n\t\tbody = &bytes.Buffer{}\n\t\tbody.WriteString(formParams.Encode())\n\t\t// Set Content-Length\n\t\theaderParams[\"Content-Length\"] = fmt.Sprintf(\"%d\", body.Len())\n\t}\n\n\t// Setup path and query parameters\n\turl, err := url.Parse(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Override request host, if applicable\n\tif c.cfg.Host != \"\" {\n\t\turl.Host = c.cfg.Host\n\t}\n\n\t// Override request scheme, if applicable\n\tif c.cfg.Scheme != \"\" {\n\t\turl.Scheme = c.cfg.Scheme\n\t}\n\n\t// Adding Query Param\n\tquery := url.Query()\n\tfor k, v := range queryParams {\n\t\tfor _, iv := range v {\n\t\t\tquery.Add(k, iv)\n\t\t}\n\t}\n\n\t// Encode the parameters.\n\turl.RawQuery = queryParamSplit.ReplaceAllStringFunc(query.Encode(), func(s string) string {\n\t\tpieces := strings.Split(s, \"=\")\n\t\tpieces[0] = queryDescape.Replace(pieces[0])\n\t\treturn strings.Join(pieces, \"=\")\n\t})\n\n\t// Generate a new request\n\tif body != nil {\n\t\tlocalVarRequest, err = http.NewRequest(method, url.String(), body)\n\t} else {\n\t\tlocalVarRequest, err = http.NewRequest(method, url.String(), nil)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// add header parameters, if any\n\tif len(headerParams) > 0 {\n\t\theaders := http.Header{}\n\t\tfor h, v := range headerParams {\n\t\t\theaders[h] = []string{v}\n\t\t}\n\t\tlocalVarRequest.Header = headers\n\t}\n\n\t// Add the user agent to the request.\n\tlocalVarRequest.Header.Add(\"User-Agent\", c.cfg.UserAgent)\n\n\tif ctx != nil {\n\t\t// add context to the request\n\t\tlocalVarRequest = localVarRequest.WithContext(ctx)\n\n\t\t// Walk through any authentication.\n\n\t}\n\n\tfor header, value := range c.cfg.DefaultHeader {\n\t\tlocalVarRequest.Header.Add(header, value)\n\t}\n\treturn localVarRequest, nil\n}\n\nfunc (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) {\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\tif s, ok := v.(*string); ok {\n\t\t*s = string(b)\n\t\treturn nil\n\t}\n\tif f, ok := v.(*os.File); ok {\n\t\tf, err = os.CreateTemp(\"\", \"HttpClientFile\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = f.Write(b)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = f.Seek(0, io.SeekStart)\n\t\treturn\n\t}\n\tif f, ok := v.(**os.File); ok {\n\t\t*f, err = os.CreateTemp(\"\", \"HttpClientFile\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = (*f).Write(b)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = (*f).Seek(0, io.SeekStart)\n\t\treturn\n\t}\n\tif XmlCheck.MatchString(contentType) {\n\t\tif err = xml.Unmarshal(b, v); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\tif JsonCheck.MatchString(contentType) {\n\t\tif actualObj, ok := v.(interface{ GetActualInstance() interface{} }); ok { // oneOf, anyOf schemas\n\t\t\tif unmarshalObj, ok := actualObj.(interface{ UnmarshalJSON([]byte) error }); ok { // make sure it has UnmarshalJSON defined\n\t\t\t\tif err = unmarshalObj.UnmarshalJSON(b); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn errors.New(\"Unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined\")\n\t\t\t}\n\t\t} else if err = json.Unmarshal(b, v); err != nil { // simple model\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\treturn errors.New(\"undefined response type\")\n}\n\n// Add a file to the multipart request\nfunc addFile(w *multipart.Writer, fieldName, path string) error {\n\tfile, err := os.Open(filepath.Clean(path))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = file.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpart, err := w.CreateFormFile(fieldName, filepath.Base(path))\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = io.Copy(part, file)\n\n\treturn err\n}\n\n// Set request body from an interface{}\nfunc setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) {\n\tif bodyBuf == nil {\n\t\tbodyBuf = &bytes.Buffer{}\n\t}\n\n\tif reader, ok := body.(io.Reader); ok {\n\t\t_, err = bodyBuf.ReadFrom(reader)\n\t} else if fp, ok := body.(*os.File); ok {\n\t\t_, err = bodyBuf.ReadFrom(fp)\n\t} else if b, ok := body.([]byte); ok {\n\t\t_, err = bodyBuf.Write(b)\n\t} else if s, ok := body.(string); ok {\n\t\t_, err = bodyBuf.WriteString(s)\n\t} else if s, ok := body.(*string); ok {\n\t\t_, err = bodyBuf.WriteString(*s)\n\t} else if JsonCheck.MatchString(contentType) {\n\t\terr = json.NewEncoder(bodyBuf).Encode(body)\n\t} else if XmlCheck.MatchString(contentType) {\n\t\tvar bs []byte\n\t\tbs, err = xml.Marshal(body)\n\t\tif err == nil {\n\t\t\tbodyBuf.Write(bs)\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif bodyBuf.Len() == 0 {\n\t\terr = fmt.Errorf(\"invalid body type %s\\n\", contentType)\n\t\treturn nil, err\n\t}\n\treturn bodyBuf, nil\n}\n\n// detectContentType method is used to figure out `Request.Body` content type for request header\nfunc detectContentType(body interface{}) string {\n\tcontentType := \"text/plain; charset=utf-8\"\n\tkind := reflect.TypeOf(body).Kind()\n\n\tswitch kind {\n\tcase reflect.Struct, reflect.Map, reflect.Ptr:\n\t\tcontentType = \"application/json; charset=utf-8\"\n\tcase reflect.String:\n\t\tcontentType = \"text/plain; charset=utf-8\"\n\tdefault:\n\t\tif b, ok := body.([]byte); ok {\n\t\t\tcontentType = http.DetectContentType(b)\n\t\t} else if kind == reflect.Slice {\n\t\t\tcontentType = \"application/json; charset=utf-8\"\n\t\t}\n\t}\n\n\treturn contentType\n}\n\n// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go\ntype cacheControl map[string]string\n\nfunc parseCacheControl(headers http.Header) cacheControl {\n\tcc := cacheControl{}\n\tccHeader := headers.Get(\"Cache-Control\")\n\tfor _, part := range strings.Split(ccHeader, \",\") {\n\t\tpart = strings.Trim(part, \" \")\n\t\tif part == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.ContainsRune(part, '=') {\n\t\t\tkeyval := strings.Split(part, \"=\")\n\t\t\tcc[strings.Trim(keyval[0], \" \")] = strings.Trim(keyval[1], \",\")\n\t\t} else {\n\t\t\tcc[part] = \"\"\n\t\t}\n\t}\n\treturn cc\n}\n\n// CacheExpires helper function to determine remaining time before repeating a request.\nfunc CacheExpires(r *http.Response) time.Time {\n\t// Figure out when the cache expires.\n\tvar expires time.Time\n\tnow, err := time.Parse(time.RFC1123, r.Header.Get(\"date\"))\n\tif err != nil {\n\t\treturn time.Now()\n\t}\n\trespCacheControl := parseCacheControl(r.Header)\n\n\tif maxAge, ok := respCacheControl[\"max-age\"]; ok {\n\t\tlifetime, err := time.ParseDuration(maxAge + \"s\")\n\t\tif err != nil {\n\t\t\texpires = now\n\t\t} else {\n\t\t\texpires = now.Add(lifetime)\n\t\t}\n\t} else {\n\t\texpiresHeader := r.Header.Get(\"Expires\")\n\t\tif expiresHeader != \"\" {\n\t\t\texpires, err = time.Parse(time.RFC1123, expiresHeader)\n\t\t\tif err != nil {\n\t\t\t\texpires = now\n\t\t\t}\n\t\t}\n\t}\n\treturn expires\n}\n\nfunc strlen(s string) int {\n\treturn utf8.RuneCountInString(s)\n}\n\n// GenericOpenAPIError Provides access to the body, error and model on returned errors.\ntype GenericOpenAPIError struct {\n\tbody  []byte\n\terror string\n\tmodel interface{}\n}\n\n// Error returns non-empty string if there was an error.\nfunc (e GenericOpenAPIError) Error() string {\n\treturn e.error\n}\n\n// Body returns the raw bytes of the response\nfunc (e GenericOpenAPIError) Body() []byte {\n\treturn e.body\n}\n\n// Model returns the unpacked model of the error\nfunc (e GenericOpenAPIError) Model() interface{} {\n\treturn e.model\n}\n\n// format error message using title and detail when model implements rfc7807\nfunc formatErrorMessage(status string, v interface{}) string {\n\tstr := \"\"\n\tmetaValue := reflect.ValueOf(v).Elem()\n\n\tif metaValue.Kind() == reflect.Struct {\n\t\tfield := metaValue.FieldByName(\"Title\")\n\t\tif field != (reflect.Value{}) {\n\t\t\tstr = fmt.Sprintf(\"%s\", field.Interface())\n\t\t}\n\n\t\tfield = metaValue.FieldByName(\"Detail\")\n\t\tif field != (reflect.Value{}) {\n\t\t\tstr = fmt.Sprintf(\"%s (%s)\", str, field.Interface())\n\t\t}\n\t}\n\n\treturn strings.TrimSpace(fmt.Sprintf(\"%s %s\", status, str))\n}\n"
  },
  {
    "path": "pkg/client-go/configuration.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// contextKeys are used to identify the type of value in the context.\n// Since these are string, it is possible to get a short description of the\n// context key for logging and debugging using key.String().\n\ntype contextKey string\n\nfunc (c contextKey) String() string {\n\treturn \"auth \" + string(c)\n}\n\nvar (\n\t// ContextAPIKeys takes a string apikey as authentication for the request\n\tContextAPIKeys = contextKey(\"apiKeys\")\n\n\t// ContextServerIndex uses a server configuration from the index.\n\tContextServerIndex = contextKey(\"serverIndex\")\n\n\t// ContextOperationServerIndices uses a server configuration from the index mapping.\n\tContextOperationServerIndices = contextKey(\"serverOperationIndices\")\n\n\t// ContextServerVariables overrides a server configuration variables.\n\tContextServerVariables = contextKey(\"serverVariables\")\n\n\t// ContextOperationServerVariables overrides a server configuration variables using operation specific values.\n\tContextOperationServerVariables = contextKey(\"serverOperationVariables\")\n)\n\n// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth\ntype BasicAuth struct {\n\tUserName string `json:\"userName,omitempty\"`\n\tPassword string `json:\"password,omitempty\"`\n}\n\n// APIKey provides API key based authentication to a request passed via context using ContextAPIKey\ntype APIKey struct {\n\tKey    string\n\tPrefix string\n}\n\n// ServerVariable stores the information about a server variable\ntype ServerVariable struct {\n\tDescription  string\n\tDefaultValue string\n\tEnumValues   []string\n}\n\n// ServerConfiguration stores the information about a server\ntype ServerConfiguration struct {\n\tURL         string\n\tDescription string\n\tVariables   map[string]ServerVariable\n}\n\n// ServerConfigurations stores multiple ServerConfiguration items\ntype ServerConfigurations []ServerConfiguration\n\n// Configuration stores the configuration of the API client\ntype Configuration struct {\n\tHost             string            `json:\"host,omitempty\"`\n\tScheme           string            `json:\"scheme,omitempty\"`\n\tDefaultHeader    map[string]string `json:\"defaultHeader,omitempty\"`\n\tUserAgent        string            `json:\"userAgent,omitempty\"`\n\tDebug            bool              `json:\"debug,omitempty\"`\n\tServers          ServerConfigurations\n\tOperationServers map[string]ServerConfigurations\n\tHTTPClient       *http.Client\n}\n\n// NewConfiguration returns a new Configuration object\nfunc NewConfiguration() *Configuration {\n\tcfg := &Configuration{\n\t\tDefaultHeader: make(map[string]string),\n\t\tUserAgent:     \"OpenAPI-Generator/1.0.0/go\",\n\t\tDebug:         false,\n\t\tServers: ServerConfigurations{\n\t\t\t{\n\t\t\t\tURL:         \"\",\n\t\t\t\tDescription: \"No description provided\",\n\t\t\t},\n\t\t},\n\t\tOperationServers: map[string]ServerConfigurations{},\n\t}\n\treturn cfg\n}\n\n// AddDefaultHeader adds a new HTTP header to the default header in the request\nfunc (c *Configuration) AddDefaultHeader(key string, value string) {\n\tc.DefaultHeader[key] = value\n}\n\n// URL formats template on a index using given variables\nfunc (sc ServerConfigurations) URL(index int, variables map[string]string) (string, error) {\n\tif index < 0 || len(sc) <= index {\n\t\treturn \"\", fmt.Errorf(\"index %v out of range %v\", index, len(sc)-1)\n\t}\n\tserver := sc[index]\n\turl := server.URL\n\n\t// go through variables and replace placeholders\n\tfor name, variable := range server.Variables {\n\t\tif value, ok := variables[name]; ok {\n\t\t\tfound := bool(len(variable.EnumValues) == 0)\n\t\t\tfor _, enumValue := range variable.EnumValues {\n\t\t\t\tif value == enumValue {\n\t\t\t\t\tfound = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !found {\n\t\t\t\treturn \"\", fmt.Errorf(\"the variable %s in the server URL has invalid value %v. Must be %v\", name, value, variable.EnumValues)\n\t\t\t}\n\t\t\turl = strings.Replace(url, \"{\"+name+\"}\", value, -1)\n\t\t} else {\n\t\t\turl = strings.Replace(url, \"{\"+name+\"}\", variable.DefaultValue, -1)\n\t\t}\n\t}\n\treturn url, nil\n}\n\n// ServerURL returns URL based on server settings\nfunc (c *Configuration) ServerURL(index int, variables map[string]string) (string, error) {\n\treturn c.Servers.URL(index, variables)\n}\n\nfunc getServerIndex(ctx context.Context) (int, error) {\n\tsi := ctx.Value(ContextServerIndex)\n\tif si != nil {\n\t\tif index, ok := si.(int); ok {\n\t\t\treturn index, nil\n\t\t}\n\t\treturn 0, reportError(\"Invalid type %T should be int\", si)\n\t}\n\treturn 0, nil\n}\n\nfunc getServerOperationIndex(ctx context.Context, endpoint string) (int, error) {\n\tosi := ctx.Value(ContextOperationServerIndices)\n\tif osi != nil {\n\t\tif operationIndices, ok := osi.(map[string]int); !ok {\n\t\t\treturn 0, reportError(\"Invalid type %T should be map[string]int\", osi)\n\t\t} else {\n\t\t\tindex, ok := operationIndices[endpoint]\n\t\t\tif ok {\n\t\t\t\treturn index, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn getServerIndex(ctx)\n}\n\nfunc getServerVariables(ctx context.Context) (map[string]string, error) {\n\tsv := ctx.Value(ContextServerVariables)\n\tif sv != nil {\n\t\tif variables, ok := sv.(map[string]string); ok {\n\t\t\treturn variables, nil\n\t\t}\n\t\treturn nil, reportError(\"ctx value of ContextServerVariables has invalid type %T should be map[string]string\", sv)\n\t}\n\treturn nil, nil\n}\n\nfunc getServerOperationVariables(ctx context.Context, endpoint string) (map[string]string, error) {\n\tosv := ctx.Value(ContextOperationServerVariables)\n\tif osv != nil {\n\t\tif operationVariables, ok := osv.(map[string]map[string]string); !ok {\n\t\t\treturn nil, reportError(\"ctx value of ContextOperationServerVariables has invalid type %T should be map[string]map[string]string\", osv)\n\t\t} else {\n\t\t\tvariables, ok := operationVariables[endpoint]\n\t\t\tif ok {\n\t\t\t\treturn variables, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn getServerVariables(ctx)\n}\n\n// ServerURLWithContext returns a new server URL given an endpoint\nfunc (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint string) (string, error) {\n\tsc, ok := c.OperationServers[endpoint]\n\tif !ok {\n\t\tsc = c.Servers\n\t}\n\n\tif ctx == nil {\n\t\treturn sc.URL(0, nil)\n\t}\n\n\tindex, err := getServerOperationIndex(ctx, endpoint)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvariables, err := getServerOperationVariables(ctx, endpoint)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn sc.URL(index, variables)\n}\n"
  },
  {
    "path": "pkg/client-go/git_push.sh",
    "content": "#!/bin/sh\n# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/\n#\n# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl \"minor update\" \"gitlab.com\"\n\ngit_user_id=$1\ngit_repo_id=$2\nrelease_note=$3\ngit_host=$4\n\nif [ \"$git_host\" = \"\" ]; then\n    git_host=\"github.com\"\n    echo \"[INFO] No command line input provided. Set \\$git_host to $git_host\"\nfi\n\nif [ \"$git_user_id\" = \"\" ]; then\n    git_user_id=\"ory\"\n    echo \"[INFO] No command line input provided. Set \\$git_user_id to $git_user_id\"\nfi\n\nif [ \"$git_repo_id\" = \"\" ]; then\n    git_repo_id=\"client-go\"\n    echo \"[INFO] No command line input provided. Set \\$git_repo_id to $git_repo_id\"\nfi\n\nif [ \"$release_note\" = \"\" ]; then\n    release_note=\"Minor update\"\n    echo \"[INFO] No command line input provided. Set \\$release_note to $release_note\"\nfi\n\n# Initialize the local directory as a Git repository\ngit init\n\n# Adds the files in the local repository and stages them for commit.\ngit add .\n\n# Commits the tracked changes and prepares them to be pushed to a remote repository.\ngit commit -m \"$release_note\"\n\n# Sets the new remote\ngit_remote=$(git remote)\nif [ \"$git_remote\" = \"\" ]; then # git remote not defined\n\n    if [ \"$GIT_TOKEN\" = \"\" ]; then\n        echo \"[INFO] \\$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment.\"\n        git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git\n    else\n        git remote add origin https://${git_user_id}:\"${GIT_TOKEN}\"@${git_host}/${git_user_id}/${git_repo_id}.git\n    fi\n\nfi\n\ngit pull origin master\n\n# Pushes (Forces) the changes in the local repository up to the remote repository\necho \"Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git\"\ngit push origin master 2>&1 | grep -v 'To https'\n"
  },
  {
    "path": "pkg/client-go/go.mod",
    "content": "module github.com/ory/client-go\n\ngo 1.18\n"
  },
  {
    "path": "pkg/client-go/go.sum",
    "content": ""
  },
  {
    "path": "pkg/client-go/model_authenticator_assurance_level.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// AuthenticatorAssuranceLevel The authenticator assurance level can be one of \\\"aal1\\\", \\\"aal2\\\", or \\\"aal3\\\". A higher number means that it is harder for an attacker to compromise the account.  Generally, \\\"aal1\\\" implies that one authentication factor was used while AAL2 implies that two factors (e.g. password + TOTP) have been used.  To learn more about these levels please head over to: https://www.ory.sh/kratos/docs/concepts/credentials\ntype AuthenticatorAssuranceLevel string\n\n// List of authenticatorAssuranceLevel\nconst (\n\tAUTHENTICATORASSURANCELEVEL_AAL0 AuthenticatorAssuranceLevel = \"aal0\"\n\tAUTHENTICATORASSURANCELEVEL_AAL1 AuthenticatorAssuranceLevel = \"aal1\"\n\tAUTHENTICATORASSURANCELEVEL_AAL2 AuthenticatorAssuranceLevel = \"aal2\"\n\tAUTHENTICATORASSURANCELEVEL_AAL3 AuthenticatorAssuranceLevel = \"aal3\"\n)\n\n// All allowed values of AuthenticatorAssuranceLevel enum\nvar AllowedAuthenticatorAssuranceLevelEnumValues = []AuthenticatorAssuranceLevel{\n\t\"aal0\",\n\t\"aal1\",\n\t\"aal2\",\n\t\"aal3\",\n}\n\nfunc (v *AuthenticatorAssuranceLevel) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := AuthenticatorAssuranceLevel(value)\n\tfor _, existing := range AllowedAuthenticatorAssuranceLevelEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid AuthenticatorAssuranceLevel\", value)\n}\n\n// NewAuthenticatorAssuranceLevelFromValue returns a pointer to a valid AuthenticatorAssuranceLevel\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewAuthenticatorAssuranceLevelFromValue(v string) (*AuthenticatorAssuranceLevel, error) {\n\tev := AuthenticatorAssuranceLevel(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for AuthenticatorAssuranceLevel: valid values are %v\", v, AllowedAuthenticatorAssuranceLevelEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v AuthenticatorAssuranceLevel) IsValid() bool {\n\tfor _, existing := range AllowedAuthenticatorAssuranceLevelEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to authenticatorAssuranceLevel value\nfunc (v AuthenticatorAssuranceLevel) Ptr() *AuthenticatorAssuranceLevel {\n\treturn &v\n}\n\ntype NullableAuthenticatorAssuranceLevel struct {\n\tvalue *AuthenticatorAssuranceLevel\n\tisSet bool\n}\n\nfunc (v NullableAuthenticatorAssuranceLevel) Get() *AuthenticatorAssuranceLevel {\n\treturn v.value\n}\n\nfunc (v *NullableAuthenticatorAssuranceLevel) Set(val *AuthenticatorAssuranceLevel) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableAuthenticatorAssuranceLevel) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableAuthenticatorAssuranceLevel) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableAuthenticatorAssuranceLevel(val *AuthenticatorAssuranceLevel) *NullableAuthenticatorAssuranceLevel {\n\treturn &NullableAuthenticatorAssuranceLevel{value: val, isSet: true}\n}\n\nfunc (v NullableAuthenticatorAssuranceLevel) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableAuthenticatorAssuranceLevel) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_batch_patch_identities_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the BatchPatchIdentitiesResponse type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &BatchPatchIdentitiesResponse{}\n\n// BatchPatchIdentitiesResponse Patch identities response\ntype BatchPatchIdentitiesResponse struct {\n\t// The patch responses for the individual identities.\n\tIdentities           []IdentityPatchResponse `json:\"identities,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _BatchPatchIdentitiesResponse BatchPatchIdentitiesResponse\n\n// NewBatchPatchIdentitiesResponse instantiates a new BatchPatchIdentitiesResponse object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewBatchPatchIdentitiesResponse() *BatchPatchIdentitiesResponse {\n\tthis := BatchPatchIdentitiesResponse{}\n\treturn &this\n}\n\n// NewBatchPatchIdentitiesResponseWithDefaults instantiates a new BatchPatchIdentitiesResponse object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewBatchPatchIdentitiesResponseWithDefaults() *BatchPatchIdentitiesResponse {\n\tthis := BatchPatchIdentitiesResponse{}\n\treturn &this\n}\n\n// GetIdentities returns the Identities field value if set, zero value otherwise.\nfunc (o *BatchPatchIdentitiesResponse) GetIdentities() []IdentityPatchResponse {\n\tif o == nil || IsNil(o.Identities) {\n\t\tvar ret []IdentityPatchResponse\n\t\treturn ret\n\t}\n\treturn o.Identities\n}\n\n// GetIdentitiesOk returns a tuple with the Identities field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *BatchPatchIdentitiesResponse) GetIdentitiesOk() ([]IdentityPatchResponse, bool) {\n\tif o == nil || IsNil(o.Identities) {\n\t\treturn nil, false\n\t}\n\treturn o.Identities, true\n}\n\n// HasIdentities returns a boolean if a field has been set.\nfunc (o *BatchPatchIdentitiesResponse) HasIdentities() bool {\n\tif o != nil && !IsNil(o.Identities) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentities gets a reference to the given []IdentityPatchResponse and assigns it to the Identities field.\nfunc (o *BatchPatchIdentitiesResponse) SetIdentities(v []IdentityPatchResponse) {\n\to.Identities = v\n}\n\nfunc (o BatchPatchIdentitiesResponse) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o BatchPatchIdentitiesResponse) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Identities) {\n\t\ttoSerialize[\"identities\"] = o.Identities\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *BatchPatchIdentitiesResponse) UnmarshalJSON(data []byte) (err error) {\n\tvarBatchPatchIdentitiesResponse := _BatchPatchIdentitiesResponse{}\n\n\terr = json.Unmarshal(data, &varBatchPatchIdentitiesResponse)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = BatchPatchIdentitiesResponse(varBatchPatchIdentitiesResponse)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"identities\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableBatchPatchIdentitiesResponse struct {\n\tvalue *BatchPatchIdentitiesResponse\n\tisSet bool\n}\n\nfunc (v NullableBatchPatchIdentitiesResponse) Get() *BatchPatchIdentitiesResponse {\n\treturn v.value\n}\n\nfunc (v *NullableBatchPatchIdentitiesResponse) Set(val *BatchPatchIdentitiesResponse) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableBatchPatchIdentitiesResponse) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableBatchPatchIdentitiesResponse) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableBatchPatchIdentitiesResponse(val *BatchPatchIdentitiesResponse) *NullableBatchPatchIdentitiesResponse {\n\treturn &NullableBatchPatchIdentitiesResponse{value: val, isSet: true}\n}\n\nfunc (v NullableBatchPatchIdentitiesResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableBatchPatchIdentitiesResponse) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_consistency_request_parameters.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the ConsistencyRequestParameters type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ConsistencyRequestParameters{}\n\n// ConsistencyRequestParameters Control API consistency guarantees\ntype ConsistencyRequestParameters struct {\n\t// Read Consistency Level (preview)  The read consistency level determines the consistency guarantee for reads:  strong (slow): The read is guaranteed to return the most recent data committed at the start of the read. eventual (very fast): The result will return data that is about 4.8 seconds old.  The default consistency guarantee can be changed in the Ory Network Console or using the Ory CLI with `ory patch project --replace '/previews/default_read_consistency_level=\\\"strong\\\"'`.  Setting the default consistency level to `eventual` may cause regressions in the future as we add consistency controls to more APIs. Currently, the following APIs will be affected by this setting:  `GET /admin/identities`  This feature is in preview and only available in Ory Network.  ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level. strong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level. eventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\n\tConsistency          *string `json:\"consistency,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ConsistencyRequestParameters ConsistencyRequestParameters\n\n// NewConsistencyRequestParameters instantiates a new ConsistencyRequestParameters object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewConsistencyRequestParameters() *ConsistencyRequestParameters {\n\tthis := ConsistencyRequestParameters{}\n\treturn &this\n}\n\n// NewConsistencyRequestParametersWithDefaults instantiates a new ConsistencyRequestParameters object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewConsistencyRequestParametersWithDefaults() *ConsistencyRequestParameters {\n\tthis := ConsistencyRequestParameters{}\n\treturn &this\n}\n\n// GetConsistency returns the Consistency field value if set, zero value otherwise.\nfunc (o *ConsistencyRequestParameters) GetConsistency() string {\n\tif o == nil || IsNil(o.Consistency) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Consistency\n}\n\n// GetConsistencyOk returns a tuple with the Consistency field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ConsistencyRequestParameters) GetConsistencyOk() (*string, bool) {\n\tif o == nil || IsNil(o.Consistency) {\n\t\treturn nil, false\n\t}\n\treturn o.Consistency, true\n}\n\n// HasConsistency returns a boolean if a field has been set.\nfunc (o *ConsistencyRequestParameters) HasConsistency() bool {\n\tif o != nil && !IsNil(o.Consistency) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConsistency gets a reference to the given string and assigns it to the Consistency field.\nfunc (o *ConsistencyRequestParameters) SetConsistency(v string) {\n\to.Consistency = &v\n}\n\nfunc (o ConsistencyRequestParameters) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ConsistencyRequestParameters) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Consistency) {\n\t\ttoSerialize[\"consistency\"] = o.Consistency\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ConsistencyRequestParameters) UnmarshalJSON(data []byte) (err error) {\n\tvarConsistencyRequestParameters := _ConsistencyRequestParameters{}\n\n\terr = json.Unmarshal(data, &varConsistencyRequestParameters)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ConsistencyRequestParameters(varConsistencyRequestParameters)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"consistency\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableConsistencyRequestParameters struct {\n\tvalue *ConsistencyRequestParameters\n\tisSet bool\n}\n\nfunc (v NullableConsistencyRequestParameters) Get() *ConsistencyRequestParameters {\n\treturn v.value\n}\n\nfunc (v *NullableConsistencyRequestParameters) Set(val *ConsistencyRequestParameters) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableConsistencyRequestParameters) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableConsistencyRequestParameters) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableConsistencyRequestParameters(val *ConsistencyRequestParameters) *NullableConsistencyRequestParameters {\n\treturn &NullableConsistencyRequestParameters{value: val, isSet: true}\n}\n\nfunc (v NullableConsistencyRequestParameters) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableConsistencyRequestParameters) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_continue_with.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// ContinueWith - struct for ContinueWith\ntype ContinueWith struct {\n\tContinueWithRecoveryUi         *ContinueWithRecoveryUi\n\tContinueWithRedirectBrowserTo  *ContinueWithRedirectBrowserTo\n\tContinueWithSetOrySessionToken *ContinueWithSetOrySessionToken\n\tContinueWithSettingsUi         *ContinueWithSettingsUi\n\tContinueWithVerificationUi     *ContinueWithVerificationUi\n}\n\n// ContinueWithRecoveryUiAsContinueWith is a convenience function that returns ContinueWithRecoveryUi wrapped in ContinueWith\nfunc ContinueWithRecoveryUiAsContinueWith(v *ContinueWithRecoveryUi) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithRecoveryUi: v,\n\t}\n}\n\n// ContinueWithRedirectBrowserToAsContinueWith is a convenience function that returns ContinueWithRedirectBrowserTo wrapped in ContinueWith\nfunc ContinueWithRedirectBrowserToAsContinueWith(v *ContinueWithRedirectBrowserTo) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithRedirectBrowserTo: v,\n\t}\n}\n\n// ContinueWithSetOrySessionTokenAsContinueWith is a convenience function that returns ContinueWithSetOrySessionToken wrapped in ContinueWith\nfunc ContinueWithSetOrySessionTokenAsContinueWith(v *ContinueWithSetOrySessionToken) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithSetOrySessionToken: v,\n\t}\n}\n\n// ContinueWithSettingsUiAsContinueWith is a convenience function that returns ContinueWithSettingsUi wrapped in ContinueWith\nfunc ContinueWithSettingsUiAsContinueWith(v *ContinueWithSettingsUi) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithSettingsUi: v,\n\t}\n}\n\n// ContinueWithVerificationUiAsContinueWith is a convenience function that returns ContinueWithVerificationUi wrapped in ContinueWith\nfunc ContinueWithVerificationUiAsContinueWith(v *ContinueWithVerificationUi) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithVerificationUi: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *ContinueWith) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'redirect_browser_to'\n\tif jsonDict[\"action\"] == \"redirect_browser_to\" {\n\t\t// try to unmarshal JSON data into ContinueWithRedirectBrowserTo\n\t\terr = json.Unmarshal(data, &dst.ContinueWithRedirectBrowserTo)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithRedirectBrowserTo, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithRedirectBrowserTo = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithRedirectBrowserTo: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'set_ory_session_token'\n\tif jsonDict[\"action\"] == \"set_ory_session_token\" {\n\t\t// try to unmarshal JSON data into ContinueWithSetOrySessionToken\n\t\terr = json.Unmarshal(data, &dst.ContinueWithSetOrySessionToken)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithSetOrySessionToken, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithSetOrySessionToken = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithSetOrySessionToken: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'show_recovery_ui'\n\tif jsonDict[\"action\"] == \"show_recovery_ui\" {\n\t\t// try to unmarshal JSON data into ContinueWithRecoveryUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithRecoveryUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithRecoveryUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithRecoveryUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithRecoveryUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'show_settings_ui'\n\tif jsonDict[\"action\"] == \"show_settings_ui\" {\n\t\t// try to unmarshal JSON data into ContinueWithSettingsUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithSettingsUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithSettingsUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithSettingsUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithSettingsUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'show_verification_ui'\n\tif jsonDict[\"action\"] == \"show_verification_ui\" {\n\t\t// try to unmarshal JSON data into ContinueWithVerificationUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithVerificationUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithVerificationUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithVerificationUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithVerificationUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithRecoveryUi'\n\tif jsonDict[\"action\"] == \"continueWithRecoveryUi\" {\n\t\t// try to unmarshal JSON data into ContinueWithRecoveryUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithRecoveryUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithRecoveryUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithRecoveryUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithRecoveryUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithRedirectBrowserTo'\n\tif jsonDict[\"action\"] == \"continueWithRedirectBrowserTo\" {\n\t\t// try to unmarshal JSON data into ContinueWithRedirectBrowserTo\n\t\terr = json.Unmarshal(data, &dst.ContinueWithRedirectBrowserTo)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithRedirectBrowserTo, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithRedirectBrowserTo = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithRedirectBrowserTo: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithSetOrySessionToken'\n\tif jsonDict[\"action\"] == \"continueWithSetOrySessionToken\" {\n\t\t// try to unmarshal JSON data into ContinueWithSetOrySessionToken\n\t\terr = json.Unmarshal(data, &dst.ContinueWithSetOrySessionToken)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithSetOrySessionToken, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithSetOrySessionToken = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithSetOrySessionToken: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithSettingsUi'\n\tif jsonDict[\"action\"] == \"continueWithSettingsUi\" {\n\t\t// try to unmarshal JSON data into ContinueWithSettingsUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithSettingsUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithSettingsUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithSettingsUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithSettingsUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithVerificationUi'\n\tif jsonDict[\"action\"] == \"continueWithVerificationUi\" {\n\t\t// try to unmarshal JSON data into ContinueWithVerificationUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithVerificationUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithVerificationUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithVerificationUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithVerificationUi: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src ContinueWith) MarshalJSON() ([]byte, error) {\n\tif src.ContinueWithRecoveryUi != nil {\n\t\treturn json.Marshal(&src.ContinueWithRecoveryUi)\n\t}\n\n\tif src.ContinueWithRedirectBrowserTo != nil {\n\t\treturn json.Marshal(&src.ContinueWithRedirectBrowserTo)\n\t}\n\n\tif src.ContinueWithSetOrySessionToken != nil {\n\t\treturn json.Marshal(&src.ContinueWithSetOrySessionToken)\n\t}\n\n\tif src.ContinueWithSettingsUi != nil {\n\t\treturn json.Marshal(&src.ContinueWithSettingsUi)\n\t}\n\n\tif src.ContinueWithVerificationUi != nil {\n\t\treturn json.Marshal(&src.ContinueWithVerificationUi)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *ContinueWith) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.ContinueWithRecoveryUi != nil {\n\t\treturn obj.ContinueWithRecoveryUi\n\t}\n\n\tif obj.ContinueWithRedirectBrowserTo != nil {\n\t\treturn obj.ContinueWithRedirectBrowserTo\n\t}\n\n\tif obj.ContinueWithSetOrySessionToken != nil {\n\t\treturn obj.ContinueWithSetOrySessionToken\n\t}\n\n\tif obj.ContinueWithSettingsUi != nil {\n\t\treturn obj.ContinueWithSettingsUi\n\t}\n\n\tif obj.ContinueWithVerificationUi != nil {\n\t\treturn obj.ContinueWithVerificationUi\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj ContinueWith) GetActualInstanceValue() interface{} {\n\tif obj.ContinueWithRecoveryUi != nil {\n\t\treturn *obj.ContinueWithRecoveryUi\n\t}\n\n\tif obj.ContinueWithRedirectBrowserTo != nil {\n\t\treturn *obj.ContinueWithRedirectBrowserTo\n\t}\n\n\tif obj.ContinueWithSetOrySessionToken != nil {\n\t\treturn *obj.ContinueWithSetOrySessionToken\n\t}\n\n\tif obj.ContinueWithSettingsUi != nil {\n\t\treturn *obj.ContinueWithSettingsUi\n\t}\n\n\tif obj.ContinueWithVerificationUi != nil {\n\t\treturn *obj.ContinueWithVerificationUi\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableContinueWith struct {\n\tvalue *ContinueWith\n\tisSet bool\n}\n\nfunc (v NullableContinueWith) Get() *ContinueWith {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWith) Set(val *ContinueWith) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWith) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWith) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWith(val *ContinueWith) *NullableContinueWith {\n\treturn &NullableContinueWith{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWith) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWith) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_continue_with_recovery_ui.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithRecoveryUi type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithRecoveryUi{}\n\n// ContinueWithRecoveryUi Indicates, that the UI flow could be continued by showing a recovery ui\ntype ContinueWithRecoveryUi struct {\n\t// Action will always be `show_recovery_ui` show_recovery_ui ContinueWithActionShowRecoveryUIString\n\tAction               string                     `json:\"action\"`\n\tFlow                 ContinueWithRecoveryUiFlow `json:\"flow\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithRecoveryUi ContinueWithRecoveryUi\n\n// NewContinueWithRecoveryUi instantiates a new ContinueWithRecoveryUi object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithRecoveryUi(action string, flow ContinueWithRecoveryUiFlow) *ContinueWithRecoveryUi {\n\tthis := ContinueWithRecoveryUi{}\n\tthis.Action = action\n\tthis.Flow = flow\n\treturn &this\n}\n\n// NewContinueWithRecoveryUiWithDefaults instantiates a new ContinueWithRecoveryUi object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithRecoveryUiWithDefaults() *ContinueWithRecoveryUi {\n\tthis := ContinueWithRecoveryUi{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithRecoveryUi) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRecoveryUi) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithRecoveryUi) SetAction(v string) {\n\to.Action = v\n}\n\n// GetFlow returns the Flow field value\nfunc (o *ContinueWithRecoveryUi) GetFlow() ContinueWithRecoveryUiFlow {\n\tif o == nil {\n\t\tvar ret ContinueWithRecoveryUiFlow\n\t\treturn ret\n\t}\n\n\treturn o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRecoveryUi) GetFlowOk() (*ContinueWithRecoveryUiFlow, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Flow, true\n}\n\n// SetFlow sets field value\nfunc (o *ContinueWithRecoveryUi) SetFlow(v ContinueWithRecoveryUiFlow) {\n\to.Flow = v\n}\n\nfunc (o ContinueWithRecoveryUi) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithRecoveryUi) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"flow\"] = o.Flow\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithRecoveryUi) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"flow\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithRecoveryUi := _ContinueWithRecoveryUi{}\n\n\terr = json.Unmarshal(data, &varContinueWithRecoveryUi)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithRecoveryUi(varContinueWithRecoveryUi)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"flow\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithRecoveryUi struct {\n\tvalue *ContinueWithRecoveryUi\n\tisSet bool\n}\n\nfunc (v NullableContinueWithRecoveryUi) Get() *ContinueWithRecoveryUi {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithRecoveryUi) Set(val *ContinueWithRecoveryUi) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithRecoveryUi) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithRecoveryUi) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithRecoveryUi(val *ContinueWithRecoveryUi) *NullableContinueWithRecoveryUi {\n\treturn &NullableContinueWithRecoveryUi{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithRecoveryUi) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithRecoveryUi) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_continue_with_recovery_ui_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithRecoveryUiFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithRecoveryUiFlow{}\n\n// ContinueWithRecoveryUiFlow struct for ContinueWithRecoveryUiFlow\ntype ContinueWithRecoveryUiFlow struct {\n\t// The ID of the recovery flow\n\tId string `json:\"id\"`\n\t// The URL of the recovery flow  If this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\n\tUrl                  *string `json:\"url,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithRecoveryUiFlow ContinueWithRecoveryUiFlow\n\n// NewContinueWithRecoveryUiFlow instantiates a new ContinueWithRecoveryUiFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithRecoveryUiFlow(id string) *ContinueWithRecoveryUiFlow {\n\tthis := ContinueWithRecoveryUiFlow{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewContinueWithRecoveryUiFlowWithDefaults instantiates a new ContinueWithRecoveryUiFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithRecoveryUiFlowWithDefaults() *ContinueWithRecoveryUiFlow {\n\tthis := ContinueWithRecoveryUiFlow{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *ContinueWithRecoveryUiFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRecoveryUiFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *ContinueWithRecoveryUiFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetUrl returns the Url field value if set, zero value otherwise.\nfunc (o *ContinueWithRecoveryUiFlow) GetUrl() string {\n\tif o == nil || IsNil(o.Url) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Url\n}\n\n// GetUrlOk returns a tuple with the Url field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRecoveryUiFlow) GetUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.Url) {\n\t\treturn nil, false\n\t}\n\treturn o.Url, true\n}\n\n// HasUrl returns a boolean if a field has been set.\nfunc (o *ContinueWithRecoveryUiFlow) HasUrl() bool {\n\tif o != nil && !IsNil(o.Url) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUrl gets a reference to the given string and assigns it to the Url field.\nfunc (o *ContinueWithRecoveryUiFlow) SetUrl(v string) {\n\to.Url = &v\n}\n\nfunc (o ContinueWithRecoveryUiFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithRecoveryUiFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.Url) {\n\t\ttoSerialize[\"url\"] = o.Url\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithRecoveryUiFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithRecoveryUiFlow := _ContinueWithRecoveryUiFlow{}\n\n\terr = json.Unmarshal(data, &varContinueWithRecoveryUiFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithRecoveryUiFlow(varContinueWithRecoveryUiFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"url\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithRecoveryUiFlow struct {\n\tvalue *ContinueWithRecoveryUiFlow\n\tisSet bool\n}\n\nfunc (v NullableContinueWithRecoveryUiFlow) Get() *ContinueWithRecoveryUiFlow {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithRecoveryUiFlow) Set(val *ContinueWithRecoveryUiFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithRecoveryUiFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithRecoveryUiFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithRecoveryUiFlow(val *ContinueWithRecoveryUiFlow) *NullableContinueWithRecoveryUiFlow {\n\treturn &NullableContinueWithRecoveryUiFlow{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithRecoveryUiFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithRecoveryUiFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_continue_with_redirect_browser_to.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithRedirectBrowserTo type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithRedirectBrowserTo{}\n\n// ContinueWithRedirectBrowserTo Indicates, that the UI flow could be continued by showing a recovery ui\ntype ContinueWithRedirectBrowserTo struct {\n\t// Action will always be `redirect_browser_to` redirect_browser_to ContinueWithActionRedirectBrowserToString\n\tAction string `json:\"action\"`\n\t// The URL to redirect the browser to\n\tRedirectBrowserTo    string `json:\"redirect_browser_to\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithRedirectBrowserTo ContinueWithRedirectBrowserTo\n\n// NewContinueWithRedirectBrowserTo instantiates a new ContinueWithRedirectBrowserTo object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithRedirectBrowserTo(action string, redirectBrowserTo string) *ContinueWithRedirectBrowserTo {\n\tthis := ContinueWithRedirectBrowserTo{}\n\tthis.Action = action\n\tthis.RedirectBrowserTo = redirectBrowserTo\n\treturn &this\n}\n\n// NewContinueWithRedirectBrowserToWithDefaults instantiates a new ContinueWithRedirectBrowserTo object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithRedirectBrowserToWithDefaults() *ContinueWithRedirectBrowserTo {\n\tthis := ContinueWithRedirectBrowserTo{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithRedirectBrowserTo) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRedirectBrowserTo) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithRedirectBrowserTo) SetAction(v string) {\n\to.Action = v\n}\n\n// GetRedirectBrowserTo returns the RedirectBrowserTo field value\nfunc (o *ContinueWithRedirectBrowserTo) GetRedirectBrowserTo() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RedirectBrowserTo\n}\n\n// GetRedirectBrowserToOk returns a tuple with the RedirectBrowserTo field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRedirectBrowserTo) GetRedirectBrowserToOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RedirectBrowserTo, true\n}\n\n// SetRedirectBrowserTo sets field value\nfunc (o *ContinueWithRedirectBrowserTo) SetRedirectBrowserTo(v string) {\n\to.RedirectBrowserTo = v\n}\n\nfunc (o ContinueWithRedirectBrowserTo) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithRedirectBrowserTo) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"redirect_browser_to\"] = o.RedirectBrowserTo\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithRedirectBrowserTo) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"redirect_browser_to\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithRedirectBrowserTo := _ContinueWithRedirectBrowserTo{}\n\n\terr = json.Unmarshal(data, &varContinueWithRedirectBrowserTo)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithRedirectBrowserTo(varContinueWithRedirectBrowserTo)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"redirect_browser_to\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithRedirectBrowserTo struct {\n\tvalue *ContinueWithRedirectBrowserTo\n\tisSet bool\n}\n\nfunc (v NullableContinueWithRedirectBrowserTo) Get() *ContinueWithRedirectBrowserTo {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithRedirectBrowserTo) Set(val *ContinueWithRedirectBrowserTo) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithRedirectBrowserTo) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithRedirectBrowserTo) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithRedirectBrowserTo(val *ContinueWithRedirectBrowserTo) *NullableContinueWithRedirectBrowserTo {\n\treturn &NullableContinueWithRedirectBrowserTo{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithRedirectBrowserTo) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithRedirectBrowserTo) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_continue_with_set_ory_session_token.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithSetOrySessionToken type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithSetOrySessionToken{}\n\n// ContinueWithSetOrySessionToken Indicates that a session was issued, and the application should use this token for authenticated requests\ntype ContinueWithSetOrySessionToken struct {\n\t// Action will always be `set_ory_session_token` set_ory_session_token ContinueWithActionSetOrySessionTokenString\n\tAction string `json:\"action\"`\n\t// Token is the token of the session\n\tOrySessionToken      string `json:\"ory_session_token\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithSetOrySessionToken ContinueWithSetOrySessionToken\n\n// NewContinueWithSetOrySessionToken instantiates a new ContinueWithSetOrySessionToken object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithSetOrySessionToken(action string, orySessionToken string) *ContinueWithSetOrySessionToken {\n\tthis := ContinueWithSetOrySessionToken{}\n\tthis.Action = action\n\tthis.OrySessionToken = orySessionToken\n\treturn &this\n}\n\n// NewContinueWithSetOrySessionTokenWithDefaults instantiates a new ContinueWithSetOrySessionToken object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithSetOrySessionTokenWithDefaults() *ContinueWithSetOrySessionToken {\n\tthis := ContinueWithSetOrySessionToken{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithSetOrySessionToken) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSetOrySessionToken) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithSetOrySessionToken) SetAction(v string) {\n\to.Action = v\n}\n\n// GetOrySessionToken returns the OrySessionToken field value\nfunc (o *ContinueWithSetOrySessionToken) GetOrySessionToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.OrySessionToken\n}\n\n// GetOrySessionTokenOk returns a tuple with the OrySessionToken field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSetOrySessionToken) GetOrySessionTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.OrySessionToken, true\n}\n\n// SetOrySessionToken sets field value\nfunc (o *ContinueWithSetOrySessionToken) SetOrySessionToken(v string) {\n\to.OrySessionToken = v\n}\n\nfunc (o ContinueWithSetOrySessionToken) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithSetOrySessionToken) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"ory_session_token\"] = o.OrySessionToken\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithSetOrySessionToken) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"ory_session_token\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithSetOrySessionToken := _ContinueWithSetOrySessionToken{}\n\n\terr = json.Unmarshal(data, &varContinueWithSetOrySessionToken)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithSetOrySessionToken(varContinueWithSetOrySessionToken)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"ory_session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithSetOrySessionToken struct {\n\tvalue *ContinueWithSetOrySessionToken\n\tisSet bool\n}\n\nfunc (v NullableContinueWithSetOrySessionToken) Get() *ContinueWithSetOrySessionToken {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithSetOrySessionToken) Set(val *ContinueWithSetOrySessionToken) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithSetOrySessionToken) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithSetOrySessionToken) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithSetOrySessionToken(val *ContinueWithSetOrySessionToken) *NullableContinueWithSetOrySessionToken {\n\treturn &NullableContinueWithSetOrySessionToken{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithSetOrySessionToken) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithSetOrySessionToken) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_continue_with_settings_ui.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithSettingsUi type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithSettingsUi{}\n\n// ContinueWithSettingsUi Indicates, that the UI flow could be continued by showing a settings ui\ntype ContinueWithSettingsUi struct {\n\t// Action will always be `show_settings_ui` show_settings_ui ContinueWithActionShowSettingsUIString\n\tAction               string                     `json:\"action\"`\n\tFlow                 ContinueWithSettingsUiFlow `json:\"flow\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithSettingsUi ContinueWithSettingsUi\n\n// NewContinueWithSettingsUi instantiates a new ContinueWithSettingsUi object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithSettingsUi(action string, flow ContinueWithSettingsUiFlow) *ContinueWithSettingsUi {\n\tthis := ContinueWithSettingsUi{}\n\tthis.Action = action\n\tthis.Flow = flow\n\treturn &this\n}\n\n// NewContinueWithSettingsUiWithDefaults instantiates a new ContinueWithSettingsUi object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithSettingsUiWithDefaults() *ContinueWithSettingsUi {\n\tthis := ContinueWithSettingsUi{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithSettingsUi) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSettingsUi) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithSettingsUi) SetAction(v string) {\n\to.Action = v\n}\n\n// GetFlow returns the Flow field value\nfunc (o *ContinueWithSettingsUi) GetFlow() ContinueWithSettingsUiFlow {\n\tif o == nil {\n\t\tvar ret ContinueWithSettingsUiFlow\n\t\treturn ret\n\t}\n\n\treturn o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSettingsUi) GetFlowOk() (*ContinueWithSettingsUiFlow, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Flow, true\n}\n\n// SetFlow sets field value\nfunc (o *ContinueWithSettingsUi) SetFlow(v ContinueWithSettingsUiFlow) {\n\to.Flow = v\n}\n\nfunc (o ContinueWithSettingsUi) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithSettingsUi) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"flow\"] = o.Flow\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithSettingsUi) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"flow\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithSettingsUi := _ContinueWithSettingsUi{}\n\n\terr = json.Unmarshal(data, &varContinueWithSettingsUi)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithSettingsUi(varContinueWithSettingsUi)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"flow\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithSettingsUi struct {\n\tvalue *ContinueWithSettingsUi\n\tisSet bool\n}\n\nfunc (v NullableContinueWithSettingsUi) Get() *ContinueWithSettingsUi {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithSettingsUi) Set(val *ContinueWithSettingsUi) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithSettingsUi) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithSettingsUi) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithSettingsUi(val *ContinueWithSettingsUi) *NullableContinueWithSettingsUi {\n\treturn &NullableContinueWithSettingsUi{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithSettingsUi) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithSettingsUi) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_continue_with_settings_ui_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithSettingsUiFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithSettingsUiFlow{}\n\n// ContinueWithSettingsUiFlow struct for ContinueWithSettingsUiFlow\ntype ContinueWithSettingsUiFlow struct {\n\t// The ID of the settings flow\n\tId string `json:\"id\"`\n\t// The URL of the settings flow  If this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\n\tUrl                  *string `json:\"url,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithSettingsUiFlow ContinueWithSettingsUiFlow\n\n// NewContinueWithSettingsUiFlow instantiates a new ContinueWithSettingsUiFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithSettingsUiFlow(id string) *ContinueWithSettingsUiFlow {\n\tthis := ContinueWithSettingsUiFlow{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewContinueWithSettingsUiFlowWithDefaults instantiates a new ContinueWithSettingsUiFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithSettingsUiFlowWithDefaults() *ContinueWithSettingsUiFlow {\n\tthis := ContinueWithSettingsUiFlow{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *ContinueWithSettingsUiFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSettingsUiFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *ContinueWithSettingsUiFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetUrl returns the Url field value if set, zero value otherwise.\nfunc (o *ContinueWithSettingsUiFlow) GetUrl() string {\n\tif o == nil || IsNil(o.Url) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Url\n}\n\n// GetUrlOk returns a tuple with the Url field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSettingsUiFlow) GetUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.Url) {\n\t\treturn nil, false\n\t}\n\treturn o.Url, true\n}\n\n// HasUrl returns a boolean if a field has been set.\nfunc (o *ContinueWithSettingsUiFlow) HasUrl() bool {\n\tif o != nil && !IsNil(o.Url) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUrl gets a reference to the given string and assigns it to the Url field.\nfunc (o *ContinueWithSettingsUiFlow) SetUrl(v string) {\n\to.Url = &v\n}\n\nfunc (o ContinueWithSettingsUiFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithSettingsUiFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.Url) {\n\t\ttoSerialize[\"url\"] = o.Url\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithSettingsUiFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithSettingsUiFlow := _ContinueWithSettingsUiFlow{}\n\n\terr = json.Unmarshal(data, &varContinueWithSettingsUiFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithSettingsUiFlow(varContinueWithSettingsUiFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"url\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithSettingsUiFlow struct {\n\tvalue *ContinueWithSettingsUiFlow\n\tisSet bool\n}\n\nfunc (v NullableContinueWithSettingsUiFlow) Get() *ContinueWithSettingsUiFlow {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithSettingsUiFlow) Set(val *ContinueWithSettingsUiFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithSettingsUiFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithSettingsUiFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithSettingsUiFlow(val *ContinueWithSettingsUiFlow) *NullableContinueWithSettingsUiFlow {\n\treturn &NullableContinueWithSettingsUiFlow{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithSettingsUiFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithSettingsUiFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_continue_with_verification_ui.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithVerificationUi type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithVerificationUi{}\n\n// ContinueWithVerificationUi Indicates, that the UI flow could be continued by showing a verification ui\ntype ContinueWithVerificationUi struct {\n\t// Action will always be `show_verification_ui` show_verification_ui ContinueWithActionShowVerificationUIString\n\tAction               string                         `json:\"action\"`\n\tFlow                 ContinueWithVerificationUiFlow `json:\"flow\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithVerificationUi ContinueWithVerificationUi\n\n// NewContinueWithVerificationUi instantiates a new ContinueWithVerificationUi object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithVerificationUi(action string, flow ContinueWithVerificationUiFlow) *ContinueWithVerificationUi {\n\tthis := ContinueWithVerificationUi{}\n\tthis.Action = action\n\tthis.Flow = flow\n\treturn &this\n}\n\n// NewContinueWithVerificationUiWithDefaults instantiates a new ContinueWithVerificationUi object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithVerificationUiWithDefaults() *ContinueWithVerificationUi {\n\tthis := ContinueWithVerificationUi{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithVerificationUi) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUi) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithVerificationUi) SetAction(v string) {\n\to.Action = v\n}\n\n// GetFlow returns the Flow field value\nfunc (o *ContinueWithVerificationUi) GetFlow() ContinueWithVerificationUiFlow {\n\tif o == nil {\n\t\tvar ret ContinueWithVerificationUiFlow\n\t\treturn ret\n\t}\n\n\treturn o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUi) GetFlowOk() (*ContinueWithVerificationUiFlow, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Flow, true\n}\n\n// SetFlow sets field value\nfunc (o *ContinueWithVerificationUi) SetFlow(v ContinueWithVerificationUiFlow) {\n\to.Flow = v\n}\n\nfunc (o ContinueWithVerificationUi) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithVerificationUi) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"flow\"] = o.Flow\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithVerificationUi) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"flow\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithVerificationUi := _ContinueWithVerificationUi{}\n\n\terr = json.Unmarshal(data, &varContinueWithVerificationUi)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithVerificationUi(varContinueWithVerificationUi)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"flow\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithVerificationUi struct {\n\tvalue *ContinueWithVerificationUi\n\tisSet bool\n}\n\nfunc (v NullableContinueWithVerificationUi) Get() *ContinueWithVerificationUi {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithVerificationUi) Set(val *ContinueWithVerificationUi) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithVerificationUi) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithVerificationUi) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithVerificationUi(val *ContinueWithVerificationUi) *NullableContinueWithVerificationUi {\n\treturn &NullableContinueWithVerificationUi{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithVerificationUi) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithVerificationUi) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_continue_with_verification_ui_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithVerificationUiFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithVerificationUiFlow{}\n\n// ContinueWithVerificationUiFlow struct for ContinueWithVerificationUiFlow\ntype ContinueWithVerificationUiFlow struct {\n\t// The ID of the verification flow\n\tId string `json:\"id\"`\n\t// The URL of the verification flow  If this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\n\tUrl *string `json:\"url,omitempty\"`\n\t// The address that should be verified in this flow\n\tVerifiableAddress    string `json:\"verifiable_address\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithVerificationUiFlow ContinueWithVerificationUiFlow\n\n// NewContinueWithVerificationUiFlow instantiates a new ContinueWithVerificationUiFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithVerificationUiFlow(id string, verifiableAddress string) *ContinueWithVerificationUiFlow {\n\tthis := ContinueWithVerificationUiFlow{}\n\tthis.Id = id\n\tthis.VerifiableAddress = verifiableAddress\n\treturn &this\n}\n\n// NewContinueWithVerificationUiFlowWithDefaults instantiates a new ContinueWithVerificationUiFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithVerificationUiFlowWithDefaults() *ContinueWithVerificationUiFlow {\n\tthis := ContinueWithVerificationUiFlow{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *ContinueWithVerificationUiFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUiFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *ContinueWithVerificationUiFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetUrl returns the Url field value if set, zero value otherwise.\nfunc (o *ContinueWithVerificationUiFlow) GetUrl() string {\n\tif o == nil || IsNil(o.Url) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Url\n}\n\n// GetUrlOk returns a tuple with the Url field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUiFlow) GetUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.Url) {\n\t\treturn nil, false\n\t}\n\treturn o.Url, true\n}\n\n// HasUrl returns a boolean if a field has been set.\nfunc (o *ContinueWithVerificationUiFlow) HasUrl() bool {\n\tif o != nil && !IsNil(o.Url) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUrl gets a reference to the given string and assigns it to the Url field.\nfunc (o *ContinueWithVerificationUiFlow) SetUrl(v string) {\n\to.Url = &v\n}\n\n// GetVerifiableAddress returns the VerifiableAddress field value\nfunc (o *ContinueWithVerificationUiFlow) GetVerifiableAddress() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.VerifiableAddress\n}\n\n// GetVerifiableAddressOk returns a tuple with the VerifiableAddress field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUiFlow) GetVerifiableAddressOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.VerifiableAddress, true\n}\n\n// SetVerifiableAddress sets field value\nfunc (o *ContinueWithVerificationUiFlow) SetVerifiableAddress(v string) {\n\to.VerifiableAddress = v\n}\n\nfunc (o ContinueWithVerificationUiFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithVerificationUiFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.Url) {\n\t\ttoSerialize[\"url\"] = o.Url\n\t}\n\ttoSerialize[\"verifiable_address\"] = o.VerifiableAddress\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithVerificationUiFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"verifiable_address\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithVerificationUiFlow := _ContinueWithVerificationUiFlow{}\n\n\terr = json.Unmarshal(data, &varContinueWithVerificationUiFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithVerificationUiFlow(varContinueWithVerificationUiFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"url\")\n\t\tdelete(additionalProperties, \"verifiable_address\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithVerificationUiFlow struct {\n\tvalue *ContinueWithVerificationUiFlow\n\tisSet bool\n}\n\nfunc (v NullableContinueWithVerificationUiFlow) Get() *ContinueWithVerificationUiFlow {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithVerificationUiFlow) Set(val *ContinueWithVerificationUiFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithVerificationUiFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithVerificationUiFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithVerificationUiFlow(val *ContinueWithVerificationUiFlow) *NullableContinueWithVerificationUiFlow {\n\treturn &NullableContinueWithVerificationUiFlow{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithVerificationUiFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithVerificationUiFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_courier_message_status.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// CourierMessageStatus A Message's Status\ntype CourierMessageStatus string\n\n// List of courierMessageStatus\nconst (\n\tCOURIERMESSAGESTATUS_QUEUED     CourierMessageStatus = \"queued\"\n\tCOURIERMESSAGESTATUS_SENT       CourierMessageStatus = \"sent\"\n\tCOURIERMESSAGESTATUS_PROCESSING CourierMessageStatus = \"processing\"\n\tCOURIERMESSAGESTATUS_ABANDONED  CourierMessageStatus = \"abandoned\"\n)\n\n// All allowed values of CourierMessageStatus enum\nvar AllowedCourierMessageStatusEnumValues = []CourierMessageStatus{\n\t\"queued\",\n\t\"sent\",\n\t\"processing\",\n\t\"abandoned\",\n}\n\nfunc (v *CourierMessageStatus) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := CourierMessageStatus(value)\n\tfor _, existing := range AllowedCourierMessageStatusEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid CourierMessageStatus\", value)\n}\n\n// NewCourierMessageStatusFromValue returns a pointer to a valid CourierMessageStatus\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewCourierMessageStatusFromValue(v string) (*CourierMessageStatus, error) {\n\tev := CourierMessageStatus(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for CourierMessageStatus: valid values are %v\", v, AllowedCourierMessageStatusEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v CourierMessageStatus) IsValid() bool {\n\tfor _, existing := range AllowedCourierMessageStatusEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to courierMessageStatus value\nfunc (v CourierMessageStatus) Ptr() *CourierMessageStatus {\n\treturn &v\n}\n\ntype NullableCourierMessageStatus struct {\n\tvalue *CourierMessageStatus\n\tisSet bool\n}\n\nfunc (v NullableCourierMessageStatus) Get() *CourierMessageStatus {\n\treturn v.value\n}\n\nfunc (v *NullableCourierMessageStatus) Set(val *CourierMessageStatus) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCourierMessageStatus) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCourierMessageStatus) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCourierMessageStatus(val *CourierMessageStatus) *NullableCourierMessageStatus {\n\treturn &NullableCourierMessageStatus{value: val, isSet: true}\n}\n\nfunc (v NullableCourierMessageStatus) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCourierMessageStatus) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_courier_message_type.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// CourierMessageType It can either be `email` or `phone`\ntype CourierMessageType string\n\n// List of courierMessageType\nconst (\n\tCOURIERMESSAGETYPE_EMAIL CourierMessageType = \"email\"\n\tCOURIERMESSAGETYPE_PHONE CourierMessageType = \"phone\"\n)\n\n// All allowed values of CourierMessageType enum\nvar AllowedCourierMessageTypeEnumValues = []CourierMessageType{\n\t\"email\",\n\t\"phone\",\n}\n\nfunc (v *CourierMessageType) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := CourierMessageType(value)\n\tfor _, existing := range AllowedCourierMessageTypeEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid CourierMessageType\", value)\n}\n\n// NewCourierMessageTypeFromValue returns a pointer to a valid CourierMessageType\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewCourierMessageTypeFromValue(v string) (*CourierMessageType, error) {\n\tev := CourierMessageType(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for CourierMessageType: valid values are %v\", v, AllowedCourierMessageTypeEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v CourierMessageType) IsValid() bool {\n\tfor _, existing := range AllowedCourierMessageTypeEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to courierMessageType value\nfunc (v CourierMessageType) Ptr() *CourierMessageType {\n\treturn &v\n}\n\ntype NullableCourierMessageType struct {\n\tvalue *CourierMessageType\n\tisSet bool\n}\n\nfunc (v NullableCourierMessageType) Get() *CourierMessageType {\n\treturn v.value\n}\n\nfunc (v *NullableCourierMessageType) Set(val *CourierMessageType) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCourierMessageType) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCourierMessageType) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCourierMessageType(val *CourierMessageType) *NullableCourierMessageType {\n\treturn &NullableCourierMessageType{value: val, isSet: true}\n}\n\nfunc (v NullableCourierMessageType) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCourierMessageType) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_create_fedcm_flow_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the CreateFedcmFlowResponse type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &CreateFedcmFlowResponse{}\n\n// CreateFedcmFlowResponse Contains a list of all available FedCM providers.\ntype CreateFedcmFlowResponse struct {\n\tCsrfToken            *string    `json:\"csrf_token,omitempty\"`\n\tProviders            []Provider `json:\"providers,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _CreateFedcmFlowResponse CreateFedcmFlowResponse\n\n// NewCreateFedcmFlowResponse instantiates a new CreateFedcmFlowResponse object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewCreateFedcmFlowResponse() *CreateFedcmFlowResponse {\n\tthis := CreateFedcmFlowResponse{}\n\treturn &this\n}\n\n// NewCreateFedcmFlowResponseWithDefaults instantiates a new CreateFedcmFlowResponse object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewCreateFedcmFlowResponseWithDefaults() *CreateFedcmFlowResponse {\n\tthis := CreateFedcmFlowResponse{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *CreateFedcmFlowResponse) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateFedcmFlowResponse) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *CreateFedcmFlowResponse) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *CreateFedcmFlowResponse) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetProviders returns the Providers field value if set, zero value otherwise.\nfunc (o *CreateFedcmFlowResponse) GetProviders() []Provider {\n\tif o == nil || IsNil(o.Providers) {\n\t\tvar ret []Provider\n\t\treturn ret\n\t}\n\treturn o.Providers\n}\n\n// GetProvidersOk returns a tuple with the Providers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateFedcmFlowResponse) GetProvidersOk() ([]Provider, bool) {\n\tif o == nil || IsNil(o.Providers) {\n\t\treturn nil, false\n\t}\n\treturn o.Providers, true\n}\n\n// HasProviders returns a boolean if a field has been set.\nfunc (o *CreateFedcmFlowResponse) HasProviders() bool {\n\tif o != nil && !IsNil(o.Providers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProviders gets a reference to the given []Provider and assigns it to the Providers field.\nfunc (o *CreateFedcmFlowResponse) SetProviders(v []Provider) {\n\to.Providers = v\n}\n\nfunc (o CreateFedcmFlowResponse) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o CreateFedcmFlowResponse) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.Providers) {\n\t\ttoSerialize[\"providers\"] = o.Providers\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *CreateFedcmFlowResponse) UnmarshalJSON(data []byte) (err error) {\n\tvarCreateFedcmFlowResponse := _CreateFedcmFlowResponse{}\n\n\terr = json.Unmarshal(data, &varCreateFedcmFlowResponse)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = CreateFedcmFlowResponse(varCreateFedcmFlowResponse)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"providers\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableCreateFedcmFlowResponse struct {\n\tvalue *CreateFedcmFlowResponse\n\tisSet bool\n}\n\nfunc (v NullableCreateFedcmFlowResponse) Get() *CreateFedcmFlowResponse {\n\treturn v.value\n}\n\nfunc (v *NullableCreateFedcmFlowResponse) Set(val *CreateFedcmFlowResponse) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCreateFedcmFlowResponse) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCreateFedcmFlowResponse) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCreateFedcmFlowResponse(val *CreateFedcmFlowResponse) *NullableCreateFedcmFlowResponse {\n\treturn &NullableCreateFedcmFlowResponse{value: val, isSet: true}\n}\n\nfunc (v NullableCreateFedcmFlowResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCreateFedcmFlowResponse) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_create_identity_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the CreateIdentityBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &CreateIdentityBody{}\n\n// CreateIdentityBody Create Identity Body\ntype CreateIdentityBody struct {\n\tCredentials *IdentityWithCredentials `json:\"credentials,omitempty\"`\n\t// ExternalID is an optional external ID of the identity. This is used to link the identity to an external system. If set, the external ID must be unique across all identities.\n\tExternalId *string `json:\"external_id,omitempty\"`\n\t// Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/<id>`.\n\tMetadataAdmin interface{} `json:\"metadata_admin,omitempty\"`\n\t// Store metadata about the identity which the identity itself can see when calling for example the session endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\n\tMetadataPublic interface{}    `json:\"metadata_public,omitempty\"`\n\tOrganizationId NullableString `json:\"organization_id,omitempty\"`\n\t// RecoveryAddresses contains all the addresses that can be used to recover an identity.  Use this structure to import recovery addresses for an identity. Please keep in mind that the address needs to be represented in the Identity Schema or this field will be overwritten on the next identity update.\n\tRecoveryAddresses []RecoveryIdentityAddress `json:\"recovery_addresses,omitempty\"`\n\t// SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\n\tSchemaId string `json:\"schema_id\"`\n\t// State is the identity's state. active StateActive inactive StateInactive\n\tState *string `json:\"state,omitempty\"`\n\t// Traits represent an identity's traits. The identity is able to create, modify, and delete traits in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_url`.\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// VerifiableAddresses contains all the addresses that can be verified by the user.  Use this structure to import verified addresses for an identity. Please keep in mind that the address needs to be represented in the Identity Schema or this field will be overwritten on the next identity update.\n\tVerifiableAddresses  []VerifiableIdentityAddress `json:\"verifiable_addresses,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _CreateIdentityBody CreateIdentityBody\n\n// NewCreateIdentityBody instantiates a new CreateIdentityBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewCreateIdentityBody(schemaId string, traits map[string]interface{}) *CreateIdentityBody {\n\tthis := CreateIdentityBody{}\n\tthis.SchemaId = schemaId\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewCreateIdentityBodyWithDefaults instantiates a new CreateIdentityBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewCreateIdentityBodyWithDefaults() *CreateIdentityBody {\n\tthis := CreateIdentityBody{}\n\treturn &this\n}\n\n// GetCredentials returns the Credentials field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetCredentials() IdentityWithCredentials {\n\tif o == nil || IsNil(o.Credentials) {\n\t\tvar ret IdentityWithCredentials\n\t\treturn ret\n\t}\n\treturn *o.Credentials\n}\n\n// GetCredentialsOk returns a tuple with the Credentials field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetCredentialsOk() (*IdentityWithCredentials, bool) {\n\tif o == nil || IsNil(o.Credentials) {\n\t\treturn nil, false\n\t}\n\treturn o.Credentials, true\n}\n\n// HasCredentials returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasCredentials() bool {\n\tif o != nil && !IsNil(o.Credentials) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCredentials gets a reference to the given IdentityWithCredentials and assigns it to the Credentials field.\nfunc (o *CreateIdentityBody) SetCredentials(v IdentityWithCredentials) {\n\to.Credentials = &v\n}\n\n// GetExternalId returns the ExternalId field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetExternalId() string {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExternalId\n}\n\n// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetExternalIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\treturn nil, false\n\t}\n\treturn o.ExternalId, true\n}\n\n// HasExternalId returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasExternalId() bool {\n\tif o != nil && !IsNil(o.ExternalId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.\nfunc (o *CreateIdentityBody) SetExternalId(v string) {\n\to.ExternalId = &v\n}\n\n// GetMetadataAdmin returns the MetadataAdmin field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *CreateIdentityBody) GetMetadataAdmin() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataAdmin\n}\n\n// GetMetadataAdminOk returns a tuple with the MetadataAdmin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *CreateIdentityBody) GetMetadataAdminOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataAdmin) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataAdmin, true\n}\n\n// HasMetadataAdmin returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasMetadataAdmin() bool {\n\tif o != nil && !IsNil(o.MetadataAdmin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataAdmin gets a reference to the given interface{} and assigns it to the MetadataAdmin field.\nfunc (o *CreateIdentityBody) SetMetadataAdmin(v interface{}) {\n\to.MetadataAdmin = v\n}\n\n// GetMetadataPublic returns the MetadataPublic field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *CreateIdentityBody) GetMetadataPublic() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataPublic\n}\n\n// GetMetadataPublicOk returns a tuple with the MetadataPublic field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *CreateIdentityBody) GetMetadataPublicOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataPublic) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataPublic, true\n}\n\n// HasMetadataPublic returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasMetadataPublic() bool {\n\tif o != nil && !IsNil(o.MetadataPublic) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataPublic gets a reference to the given interface{} and assigns it to the MetadataPublic field.\nfunc (o *CreateIdentityBody) SetMetadataPublic(v interface{}) {\n\to.MetadataPublic = v\n}\n\n// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *CreateIdentityBody) GetOrganizationId() string {\n\tif o == nil || IsNil(o.OrganizationId.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OrganizationId.Get()\n}\n\n// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *CreateIdentityBody) GetOrganizationIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.OrganizationId.Get(), o.OrganizationId.IsSet()\n}\n\n// HasOrganizationId returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasOrganizationId() bool {\n\tif o != nil && o.OrganizationId.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganizationId gets a reference to the given NullableString and assigns it to the OrganizationId field.\nfunc (o *CreateIdentityBody) SetOrganizationId(v string) {\n\to.OrganizationId.Set(&v)\n}\n\n// SetOrganizationIdNil sets the value for OrganizationId to be an explicit nil\nfunc (o *CreateIdentityBody) SetOrganizationIdNil() {\n\to.OrganizationId.Set(nil)\n}\n\n// UnsetOrganizationId ensures that no value is present for OrganizationId, not even an explicit nil\nfunc (o *CreateIdentityBody) UnsetOrganizationId() {\n\to.OrganizationId.Unset()\n}\n\n// GetRecoveryAddresses returns the RecoveryAddresses field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetRecoveryAddresses() []RecoveryIdentityAddress {\n\tif o == nil || IsNil(o.RecoveryAddresses) {\n\t\tvar ret []RecoveryIdentityAddress\n\t\treturn ret\n\t}\n\treturn o.RecoveryAddresses\n}\n\n// GetRecoveryAddressesOk returns a tuple with the RecoveryAddresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetRecoveryAddressesOk() ([]RecoveryIdentityAddress, bool) {\n\tif o == nil || IsNil(o.RecoveryAddresses) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoveryAddresses, true\n}\n\n// HasRecoveryAddresses returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasRecoveryAddresses() bool {\n\tif o != nil && !IsNil(o.RecoveryAddresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoveryAddresses gets a reference to the given []RecoveryIdentityAddress and assigns it to the RecoveryAddresses field.\nfunc (o *CreateIdentityBody) SetRecoveryAddresses(v []RecoveryIdentityAddress) {\n\to.RecoveryAddresses = v\n}\n\n// GetSchemaId returns the SchemaId field value\nfunc (o *CreateIdentityBody) GetSchemaId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SchemaId\n}\n\n// GetSchemaIdOk returns a tuple with the SchemaId field value\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetSchemaIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SchemaId, true\n}\n\n// SetSchemaId sets field value\nfunc (o *CreateIdentityBody) SetSchemaId(v string) {\n\to.SchemaId = v\n}\n\n// GetState returns the State field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetState() string {\n\tif o == nil || IsNil(o.State) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.State\n}\n\n// GetStateOk returns a tuple with the State field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetStateOk() (*string, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn o.State, true\n}\n\n// HasState returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasState() bool {\n\tif o != nil && !IsNil(o.State) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetState gets a reference to the given string and assigns it to the State field.\nfunc (o *CreateIdentityBody) SetState(v string) {\n\to.State = &v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *CreateIdentityBody) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *CreateIdentityBody) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetVerifiableAddresses returns the VerifiableAddresses field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetVerifiableAddresses() []VerifiableIdentityAddress {\n\tif o == nil || IsNil(o.VerifiableAddresses) {\n\t\tvar ret []VerifiableIdentityAddress\n\t\treturn ret\n\t}\n\treturn o.VerifiableAddresses\n}\n\n// GetVerifiableAddressesOk returns a tuple with the VerifiableAddresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetVerifiableAddressesOk() ([]VerifiableIdentityAddress, bool) {\n\tif o == nil || IsNil(o.VerifiableAddresses) {\n\t\treturn nil, false\n\t}\n\treturn o.VerifiableAddresses, true\n}\n\n// HasVerifiableAddresses returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasVerifiableAddresses() bool {\n\tif o != nil && !IsNil(o.VerifiableAddresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVerifiableAddresses gets a reference to the given []VerifiableIdentityAddress and assigns it to the VerifiableAddresses field.\nfunc (o *CreateIdentityBody) SetVerifiableAddresses(v []VerifiableIdentityAddress) {\n\to.VerifiableAddresses = v\n}\n\nfunc (o CreateIdentityBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o CreateIdentityBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Credentials) {\n\t\ttoSerialize[\"credentials\"] = o.Credentials\n\t}\n\tif !IsNil(o.ExternalId) {\n\t\ttoSerialize[\"external_id\"] = o.ExternalId\n\t}\n\tif o.MetadataAdmin != nil {\n\t\ttoSerialize[\"metadata_admin\"] = o.MetadataAdmin\n\t}\n\tif o.MetadataPublic != nil {\n\t\ttoSerialize[\"metadata_public\"] = o.MetadataPublic\n\t}\n\tif o.OrganizationId.IsSet() {\n\t\ttoSerialize[\"organization_id\"] = o.OrganizationId.Get()\n\t}\n\tif !IsNil(o.RecoveryAddresses) {\n\t\ttoSerialize[\"recovery_addresses\"] = o.RecoveryAddresses\n\t}\n\ttoSerialize[\"schema_id\"] = o.SchemaId\n\tif !IsNil(o.State) {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.VerifiableAddresses) {\n\t\ttoSerialize[\"verifiable_addresses\"] = o.VerifiableAddresses\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *CreateIdentityBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"schema_id\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarCreateIdentityBody := _CreateIdentityBody{}\n\n\terr = json.Unmarshal(data, &varCreateIdentityBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = CreateIdentityBody(varCreateIdentityBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"credentials\")\n\t\tdelete(additionalProperties, \"external_id\")\n\t\tdelete(additionalProperties, \"metadata_admin\")\n\t\tdelete(additionalProperties, \"metadata_public\")\n\t\tdelete(additionalProperties, \"organization_id\")\n\t\tdelete(additionalProperties, \"recovery_addresses\")\n\t\tdelete(additionalProperties, \"schema_id\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"verifiable_addresses\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableCreateIdentityBody struct {\n\tvalue *CreateIdentityBody\n\tisSet bool\n}\n\nfunc (v NullableCreateIdentityBody) Get() *CreateIdentityBody {\n\treturn v.value\n}\n\nfunc (v *NullableCreateIdentityBody) Set(val *CreateIdentityBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCreateIdentityBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCreateIdentityBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCreateIdentityBody(val *CreateIdentityBody) *NullableCreateIdentityBody {\n\treturn &NullableCreateIdentityBody{value: val, isSet: true}\n}\n\nfunc (v NullableCreateIdentityBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCreateIdentityBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_create_recovery_code_for_identity_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the CreateRecoveryCodeForIdentityBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &CreateRecoveryCodeForIdentityBody{}\n\n// CreateRecoveryCodeForIdentityBody Create Recovery Code for Identity Request Body\ntype CreateRecoveryCodeForIdentityBody struct {\n\t// Code Expires In  The recovery code will expire after that amount of time has passed. Defaults to the configuration value of `selfservice.methods.code.config.lifespan`.\n\tExpiresIn *string `json:\"expires_in,omitempty\" validate:\"regexp=^([0-9]+(ns|us|ms|s|m|h))*$\"`\n\t// The flow type can either be `api` or `browser`.\n\tFlowType *string `json:\"flow_type,omitempty\"`\n\t// Identity to Recover  The identity's ID you wish to recover.\n\tIdentityId           string `json:\"identity_id\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _CreateRecoveryCodeForIdentityBody CreateRecoveryCodeForIdentityBody\n\n// NewCreateRecoveryCodeForIdentityBody instantiates a new CreateRecoveryCodeForIdentityBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewCreateRecoveryCodeForIdentityBody(identityId string) *CreateRecoveryCodeForIdentityBody {\n\tthis := CreateRecoveryCodeForIdentityBody{}\n\tthis.IdentityId = identityId\n\treturn &this\n}\n\n// NewCreateRecoveryCodeForIdentityBodyWithDefaults instantiates a new CreateRecoveryCodeForIdentityBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewCreateRecoveryCodeForIdentityBodyWithDefaults() *CreateRecoveryCodeForIdentityBody {\n\tthis := CreateRecoveryCodeForIdentityBody{}\n\treturn &this\n}\n\n// GetExpiresIn returns the ExpiresIn field value if set, zero value otherwise.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetExpiresIn() string {\n\tif o == nil || IsNil(o.ExpiresIn) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExpiresIn\n}\n\n// GetExpiresInOk returns a tuple with the ExpiresIn field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetExpiresInOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExpiresIn) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresIn, true\n}\n\n// HasExpiresIn returns a boolean if a field has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) HasExpiresIn() bool {\n\tif o != nil && !IsNil(o.ExpiresIn) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresIn gets a reference to the given string and assigns it to the ExpiresIn field.\nfunc (o *CreateRecoveryCodeForIdentityBody) SetExpiresIn(v string) {\n\to.ExpiresIn = &v\n}\n\n// GetFlowType returns the FlowType field value if set, zero value otherwise.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetFlowType() string {\n\tif o == nil || IsNil(o.FlowType) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.FlowType\n}\n\n// GetFlowTypeOk returns a tuple with the FlowType field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetFlowTypeOk() (*string, bool) {\n\tif o == nil || IsNil(o.FlowType) {\n\t\treturn nil, false\n\t}\n\treturn o.FlowType, true\n}\n\n// HasFlowType returns a boolean if a field has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) HasFlowType() bool {\n\tif o != nil && !IsNil(o.FlowType) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFlowType gets a reference to the given string and assigns it to the FlowType field.\nfunc (o *CreateRecoveryCodeForIdentityBody) SetFlowType(v string) {\n\to.FlowType = &v\n}\n\n// GetIdentityId returns the IdentityId field value\nfunc (o *CreateRecoveryCodeForIdentityBody) GetIdentityId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.IdentityId\n}\n\n// GetIdentityIdOk returns a tuple with the IdentityId field value\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetIdentityIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IdentityId, true\n}\n\n// SetIdentityId sets field value\nfunc (o *CreateRecoveryCodeForIdentityBody) SetIdentityId(v string) {\n\to.IdentityId = v\n}\n\nfunc (o CreateRecoveryCodeForIdentityBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o CreateRecoveryCodeForIdentityBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ExpiresIn) {\n\t\ttoSerialize[\"expires_in\"] = o.ExpiresIn\n\t}\n\tif !IsNil(o.FlowType) {\n\t\ttoSerialize[\"flow_type\"] = o.FlowType\n\t}\n\ttoSerialize[\"identity_id\"] = o.IdentityId\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *CreateRecoveryCodeForIdentityBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identity_id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarCreateRecoveryCodeForIdentityBody := _CreateRecoveryCodeForIdentityBody{}\n\n\terr = json.Unmarshal(data, &varCreateRecoveryCodeForIdentityBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = CreateRecoveryCodeForIdentityBody(varCreateRecoveryCodeForIdentityBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"expires_in\")\n\t\tdelete(additionalProperties, \"flow_type\")\n\t\tdelete(additionalProperties, \"identity_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableCreateRecoveryCodeForIdentityBody struct {\n\tvalue *CreateRecoveryCodeForIdentityBody\n\tisSet bool\n}\n\nfunc (v NullableCreateRecoveryCodeForIdentityBody) Get() *CreateRecoveryCodeForIdentityBody {\n\treturn v.value\n}\n\nfunc (v *NullableCreateRecoveryCodeForIdentityBody) Set(val *CreateRecoveryCodeForIdentityBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCreateRecoveryCodeForIdentityBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCreateRecoveryCodeForIdentityBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCreateRecoveryCodeForIdentityBody(val *CreateRecoveryCodeForIdentityBody) *NullableCreateRecoveryCodeForIdentityBody {\n\treturn &NullableCreateRecoveryCodeForIdentityBody{value: val, isSet: true}\n}\n\nfunc (v NullableCreateRecoveryCodeForIdentityBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCreateRecoveryCodeForIdentityBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_create_recovery_link_for_identity_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the CreateRecoveryLinkForIdentityBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &CreateRecoveryLinkForIdentityBody{}\n\n// CreateRecoveryLinkForIdentityBody Create Recovery Link for Identity Request Body\ntype CreateRecoveryLinkForIdentityBody struct {\n\t// Link Expires In  The recovery link will expire after that amount of time has passed. Defaults to the configuration value of `selfservice.methods.code.config.lifespan`.\n\tExpiresIn *string `json:\"expires_in,omitempty\" validate:\"regexp=^[0-9]+(ns|us|ms|s|m|h)$\"`\n\t// Identity to Recover  The identity's ID you wish to recover.\n\tIdentityId           string `json:\"identity_id\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _CreateRecoveryLinkForIdentityBody CreateRecoveryLinkForIdentityBody\n\n// NewCreateRecoveryLinkForIdentityBody instantiates a new CreateRecoveryLinkForIdentityBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewCreateRecoveryLinkForIdentityBody(identityId string) *CreateRecoveryLinkForIdentityBody {\n\tthis := CreateRecoveryLinkForIdentityBody{}\n\tthis.IdentityId = identityId\n\treturn &this\n}\n\n// NewCreateRecoveryLinkForIdentityBodyWithDefaults instantiates a new CreateRecoveryLinkForIdentityBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewCreateRecoveryLinkForIdentityBodyWithDefaults() *CreateRecoveryLinkForIdentityBody {\n\tthis := CreateRecoveryLinkForIdentityBody{}\n\treturn &this\n}\n\n// GetExpiresIn returns the ExpiresIn field value if set, zero value otherwise.\nfunc (o *CreateRecoveryLinkForIdentityBody) GetExpiresIn() string {\n\tif o == nil || IsNil(o.ExpiresIn) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExpiresIn\n}\n\n// GetExpiresInOk returns a tuple with the ExpiresIn field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryLinkForIdentityBody) GetExpiresInOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExpiresIn) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresIn, true\n}\n\n// HasExpiresIn returns a boolean if a field has been set.\nfunc (o *CreateRecoveryLinkForIdentityBody) HasExpiresIn() bool {\n\tif o != nil && !IsNil(o.ExpiresIn) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresIn gets a reference to the given string and assigns it to the ExpiresIn field.\nfunc (o *CreateRecoveryLinkForIdentityBody) SetExpiresIn(v string) {\n\to.ExpiresIn = &v\n}\n\n// GetIdentityId returns the IdentityId field value\nfunc (o *CreateRecoveryLinkForIdentityBody) GetIdentityId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.IdentityId\n}\n\n// GetIdentityIdOk returns a tuple with the IdentityId field value\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryLinkForIdentityBody) GetIdentityIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IdentityId, true\n}\n\n// SetIdentityId sets field value\nfunc (o *CreateRecoveryLinkForIdentityBody) SetIdentityId(v string) {\n\to.IdentityId = v\n}\n\nfunc (o CreateRecoveryLinkForIdentityBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o CreateRecoveryLinkForIdentityBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ExpiresIn) {\n\t\ttoSerialize[\"expires_in\"] = o.ExpiresIn\n\t}\n\ttoSerialize[\"identity_id\"] = o.IdentityId\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *CreateRecoveryLinkForIdentityBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identity_id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarCreateRecoveryLinkForIdentityBody := _CreateRecoveryLinkForIdentityBody{}\n\n\terr = json.Unmarshal(data, &varCreateRecoveryLinkForIdentityBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = CreateRecoveryLinkForIdentityBody(varCreateRecoveryLinkForIdentityBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"expires_in\")\n\t\tdelete(additionalProperties, \"identity_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableCreateRecoveryLinkForIdentityBody struct {\n\tvalue *CreateRecoveryLinkForIdentityBody\n\tisSet bool\n}\n\nfunc (v NullableCreateRecoveryLinkForIdentityBody) Get() *CreateRecoveryLinkForIdentityBody {\n\treturn v.value\n}\n\nfunc (v *NullableCreateRecoveryLinkForIdentityBody) Set(val *CreateRecoveryLinkForIdentityBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCreateRecoveryLinkForIdentityBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCreateRecoveryLinkForIdentityBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCreateRecoveryLinkForIdentityBody(val *CreateRecoveryLinkForIdentityBody) *NullableCreateRecoveryLinkForIdentityBody {\n\treturn &NullableCreateRecoveryLinkForIdentityBody{value: val, isSet: true}\n}\n\nfunc (v NullableCreateRecoveryLinkForIdentityBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCreateRecoveryLinkForIdentityBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_delete_my_sessions_count.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the DeleteMySessionsCount type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &DeleteMySessionsCount{}\n\n// DeleteMySessionsCount Deleted Session Count\ntype DeleteMySessionsCount struct {\n\t// The number of sessions that were revoked.\n\tCount                *int64 `json:\"count,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _DeleteMySessionsCount DeleteMySessionsCount\n\n// NewDeleteMySessionsCount instantiates a new DeleteMySessionsCount object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewDeleteMySessionsCount() *DeleteMySessionsCount {\n\tthis := DeleteMySessionsCount{}\n\treturn &this\n}\n\n// NewDeleteMySessionsCountWithDefaults instantiates a new DeleteMySessionsCount object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewDeleteMySessionsCountWithDefaults() *DeleteMySessionsCount {\n\tthis := DeleteMySessionsCount{}\n\treturn &this\n}\n\n// GetCount returns the Count field value if set, zero value otherwise.\nfunc (o *DeleteMySessionsCount) GetCount() int64 {\n\tif o == nil || IsNil(o.Count) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Count\n}\n\n// GetCountOk returns a tuple with the Count field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *DeleteMySessionsCount) GetCountOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Count) {\n\t\treturn nil, false\n\t}\n\treturn o.Count, true\n}\n\n// HasCount returns a boolean if a field has been set.\nfunc (o *DeleteMySessionsCount) HasCount() bool {\n\tif o != nil && !IsNil(o.Count) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCount gets a reference to the given int64 and assigns it to the Count field.\nfunc (o *DeleteMySessionsCount) SetCount(v int64) {\n\to.Count = &v\n}\n\nfunc (o DeleteMySessionsCount) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o DeleteMySessionsCount) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Count) {\n\t\ttoSerialize[\"count\"] = o.Count\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *DeleteMySessionsCount) UnmarshalJSON(data []byte) (err error) {\n\tvarDeleteMySessionsCount := _DeleteMySessionsCount{}\n\n\terr = json.Unmarshal(data, &varDeleteMySessionsCount)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = DeleteMySessionsCount(varDeleteMySessionsCount)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"count\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableDeleteMySessionsCount struct {\n\tvalue *DeleteMySessionsCount\n\tisSet bool\n}\n\nfunc (v NullableDeleteMySessionsCount) Get() *DeleteMySessionsCount {\n\treturn v.value\n}\n\nfunc (v *NullableDeleteMySessionsCount) Set(val *DeleteMySessionsCount) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableDeleteMySessionsCount) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableDeleteMySessionsCount) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableDeleteMySessionsCount(val *DeleteMySessionsCount) *NullableDeleteMySessionsCount {\n\treturn &NullableDeleteMySessionsCount{value: val, isSet: true}\n}\n\nfunc (v NullableDeleteMySessionsCount) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableDeleteMySessionsCount) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_error_authenticator_assurance_level_not_satisfied.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the ErrorAuthenticatorAssuranceLevelNotSatisfied type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ErrorAuthenticatorAssuranceLevelNotSatisfied{}\n\n// ErrorAuthenticatorAssuranceLevelNotSatisfied struct for ErrorAuthenticatorAssuranceLevelNotSatisfied\ntype ErrorAuthenticatorAssuranceLevelNotSatisfied struct {\n\tError *GenericError `json:\"error,omitempty\"`\n\t// Points to where to redirect the user to next.\n\tRedirectBrowserTo    *string `json:\"redirect_browser_to,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ErrorAuthenticatorAssuranceLevelNotSatisfied ErrorAuthenticatorAssuranceLevelNotSatisfied\n\n// NewErrorAuthenticatorAssuranceLevelNotSatisfied instantiates a new ErrorAuthenticatorAssuranceLevelNotSatisfied object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewErrorAuthenticatorAssuranceLevelNotSatisfied() *ErrorAuthenticatorAssuranceLevelNotSatisfied {\n\tthis := ErrorAuthenticatorAssuranceLevelNotSatisfied{}\n\treturn &this\n}\n\n// NewErrorAuthenticatorAssuranceLevelNotSatisfiedWithDefaults instantiates a new ErrorAuthenticatorAssuranceLevelNotSatisfied object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewErrorAuthenticatorAssuranceLevelNotSatisfiedWithDefaults() *ErrorAuthenticatorAssuranceLevelNotSatisfied {\n\tthis := ErrorAuthenticatorAssuranceLevelNotSatisfied{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) GetError() GenericError {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) GetErrorOk() (*GenericError, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given GenericError and assigns it to the Error field.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) SetError(v GenericError) {\n\to.Error = &v\n}\n\n// GetRedirectBrowserTo returns the RedirectBrowserTo field value if set, zero value otherwise.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) GetRedirectBrowserTo() string {\n\tif o == nil || IsNil(o.RedirectBrowserTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RedirectBrowserTo\n}\n\n// GetRedirectBrowserToOk returns a tuple with the RedirectBrowserTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) GetRedirectBrowserToOk() (*string, bool) {\n\tif o == nil || IsNil(o.RedirectBrowserTo) {\n\t\treturn nil, false\n\t}\n\treturn o.RedirectBrowserTo, true\n}\n\n// HasRedirectBrowserTo returns a boolean if a field has been set.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) HasRedirectBrowserTo() bool {\n\tif o != nil && !IsNil(o.RedirectBrowserTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRedirectBrowserTo gets a reference to the given string and assigns it to the RedirectBrowserTo field.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) SetRedirectBrowserTo(v string) {\n\to.RedirectBrowserTo = &v\n}\n\nfunc (o ErrorAuthenticatorAssuranceLevelNotSatisfied) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ErrorAuthenticatorAssuranceLevelNotSatisfied) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.RedirectBrowserTo) {\n\t\ttoSerialize[\"redirect_browser_to\"] = o.RedirectBrowserTo\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) UnmarshalJSON(data []byte) (err error) {\n\tvarErrorAuthenticatorAssuranceLevelNotSatisfied := _ErrorAuthenticatorAssuranceLevelNotSatisfied{}\n\n\terr = json.Unmarshal(data, &varErrorAuthenticatorAssuranceLevelNotSatisfied)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ErrorAuthenticatorAssuranceLevelNotSatisfied(varErrorAuthenticatorAssuranceLevelNotSatisfied)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"redirect_browser_to\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableErrorAuthenticatorAssuranceLevelNotSatisfied struct {\n\tvalue *ErrorAuthenticatorAssuranceLevelNotSatisfied\n\tisSet bool\n}\n\nfunc (v NullableErrorAuthenticatorAssuranceLevelNotSatisfied) Get() *ErrorAuthenticatorAssuranceLevelNotSatisfied {\n\treturn v.value\n}\n\nfunc (v *NullableErrorAuthenticatorAssuranceLevelNotSatisfied) Set(val *ErrorAuthenticatorAssuranceLevelNotSatisfied) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableErrorAuthenticatorAssuranceLevelNotSatisfied) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableErrorAuthenticatorAssuranceLevelNotSatisfied) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableErrorAuthenticatorAssuranceLevelNotSatisfied(val *ErrorAuthenticatorAssuranceLevelNotSatisfied) *NullableErrorAuthenticatorAssuranceLevelNotSatisfied {\n\treturn &NullableErrorAuthenticatorAssuranceLevelNotSatisfied{value: val, isSet: true}\n}\n\nfunc (v NullableErrorAuthenticatorAssuranceLevelNotSatisfied) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableErrorAuthenticatorAssuranceLevelNotSatisfied) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_error_browser_location_change_required.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the ErrorBrowserLocationChangeRequired type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ErrorBrowserLocationChangeRequired{}\n\n// ErrorBrowserLocationChangeRequired struct for ErrorBrowserLocationChangeRequired\ntype ErrorBrowserLocationChangeRequired struct {\n\tError *ErrorGeneric `json:\"error,omitempty\"`\n\t// Points to where to redirect the user to next.\n\tRedirectBrowserTo    *string `json:\"redirect_browser_to,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ErrorBrowserLocationChangeRequired ErrorBrowserLocationChangeRequired\n\n// NewErrorBrowserLocationChangeRequired instantiates a new ErrorBrowserLocationChangeRequired object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewErrorBrowserLocationChangeRequired() *ErrorBrowserLocationChangeRequired {\n\tthis := ErrorBrowserLocationChangeRequired{}\n\treturn &this\n}\n\n// NewErrorBrowserLocationChangeRequiredWithDefaults instantiates a new ErrorBrowserLocationChangeRequired object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewErrorBrowserLocationChangeRequiredWithDefaults() *ErrorBrowserLocationChangeRequired {\n\tthis := ErrorBrowserLocationChangeRequired{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *ErrorBrowserLocationChangeRequired) GetError() ErrorGeneric {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret ErrorGeneric\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorBrowserLocationChangeRequired) GetErrorOk() (*ErrorGeneric, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *ErrorBrowserLocationChangeRequired) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given ErrorGeneric and assigns it to the Error field.\nfunc (o *ErrorBrowserLocationChangeRequired) SetError(v ErrorGeneric) {\n\to.Error = &v\n}\n\n// GetRedirectBrowserTo returns the RedirectBrowserTo field value if set, zero value otherwise.\nfunc (o *ErrorBrowserLocationChangeRequired) GetRedirectBrowserTo() string {\n\tif o == nil || IsNil(o.RedirectBrowserTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RedirectBrowserTo\n}\n\n// GetRedirectBrowserToOk returns a tuple with the RedirectBrowserTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorBrowserLocationChangeRequired) GetRedirectBrowserToOk() (*string, bool) {\n\tif o == nil || IsNil(o.RedirectBrowserTo) {\n\t\treturn nil, false\n\t}\n\treturn o.RedirectBrowserTo, true\n}\n\n// HasRedirectBrowserTo returns a boolean if a field has been set.\nfunc (o *ErrorBrowserLocationChangeRequired) HasRedirectBrowserTo() bool {\n\tif o != nil && !IsNil(o.RedirectBrowserTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRedirectBrowserTo gets a reference to the given string and assigns it to the RedirectBrowserTo field.\nfunc (o *ErrorBrowserLocationChangeRequired) SetRedirectBrowserTo(v string) {\n\to.RedirectBrowserTo = &v\n}\n\nfunc (o ErrorBrowserLocationChangeRequired) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ErrorBrowserLocationChangeRequired) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.RedirectBrowserTo) {\n\t\ttoSerialize[\"redirect_browser_to\"] = o.RedirectBrowserTo\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ErrorBrowserLocationChangeRequired) UnmarshalJSON(data []byte) (err error) {\n\tvarErrorBrowserLocationChangeRequired := _ErrorBrowserLocationChangeRequired{}\n\n\terr = json.Unmarshal(data, &varErrorBrowserLocationChangeRequired)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ErrorBrowserLocationChangeRequired(varErrorBrowserLocationChangeRequired)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"redirect_browser_to\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableErrorBrowserLocationChangeRequired struct {\n\tvalue *ErrorBrowserLocationChangeRequired\n\tisSet bool\n}\n\nfunc (v NullableErrorBrowserLocationChangeRequired) Get() *ErrorBrowserLocationChangeRequired {\n\treturn v.value\n}\n\nfunc (v *NullableErrorBrowserLocationChangeRequired) Set(val *ErrorBrowserLocationChangeRequired) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableErrorBrowserLocationChangeRequired) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableErrorBrowserLocationChangeRequired) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableErrorBrowserLocationChangeRequired(val *ErrorBrowserLocationChangeRequired) *NullableErrorBrowserLocationChangeRequired {\n\treturn &NullableErrorBrowserLocationChangeRequired{value: val, isSet: true}\n}\n\nfunc (v NullableErrorBrowserLocationChangeRequired) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableErrorBrowserLocationChangeRequired) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_error_flow_replaced.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the ErrorFlowReplaced type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ErrorFlowReplaced{}\n\n// ErrorFlowReplaced Is sent when a flow is replaced by a different flow of the same class\ntype ErrorFlowReplaced struct {\n\tError *GenericError `json:\"error,omitempty\"`\n\t// The flow ID that should be used for the new flow as it contains the correct messages.\n\tUseFlowId            *string `json:\"use_flow_id,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ErrorFlowReplaced ErrorFlowReplaced\n\n// NewErrorFlowReplaced instantiates a new ErrorFlowReplaced object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewErrorFlowReplaced() *ErrorFlowReplaced {\n\tthis := ErrorFlowReplaced{}\n\treturn &this\n}\n\n// NewErrorFlowReplacedWithDefaults instantiates a new ErrorFlowReplaced object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewErrorFlowReplacedWithDefaults() *ErrorFlowReplaced {\n\tthis := ErrorFlowReplaced{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *ErrorFlowReplaced) GetError() GenericError {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorFlowReplaced) GetErrorOk() (*GenericError, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *ErrorFlowReplaced) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given GenericError and assigns it to the Error field.\nfunc (o *ErrorFlowReplaced) SetError(v GenericError) {\n\to.Error = &v\n}\n\n// GetUseFlowId returns the UseFlowId field value if set, zero value otherwise.\nfunc (o *ErrorFlowReplaced) GetUseFlowId() string {\n\tif o == nil || IsNil(o.UseFlowId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.UseFlowId\n}\n\n// GetUseFlowIdOk returns a tuple with the UseFlowId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorFlowReplaced) GetUseFlowIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.UseFlowId) {\n\t\treturn nil, false\n\t}\n\treturn o.UseFlowId, true\n}\n\n// HasUseFlowId returns a boolean if a field has been set.\nfunc (o *ErrorFlowReplaced) HasUseFlowId() bool {\n\tif o != nil && !IsNil(o.UseFlowId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUseFlowId gets a reference to the given string and assigns it to the UseFlowId field.\nfunc (o *ErrorFlowReplaced) SetUseFlowId(v string) {\n\to.UseFlowId = &v\n}\n\nfunc (o ErrorFlowReplaced) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ErrorFlowReplaced) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.UseFlowId) {\n\t\ttoSerialize[\"use_flow_id\"] = o.UseFlowId\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ErrorFlowReplaced) UnmarshalJSON(data []byte) (err error) {\n\tvarErrorFlowReplaced := _ErrorFlowReplaced{}\n\n\terr = json.Unmarshal(data, &varErrorFlowReplaced)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ErrorFlowReplaced(varErrorFlowReplaced)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"use_flow_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableErrorFlowReplaced struct {\n\tvalue *ErrorFlowReplaced\n\tisSet bool\n}\n\nfunc (v NullableErrorFlowReplaced) Get() *ErrorFlowReplaced {\n\treturn v.value\n}\n\nfunc (v *NullableErrorFlowReplaced) Set(val *ErrorFlowReplaced) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableErrorFlowReplaced) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableErrorFlowReplaced) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableErrorFlowReplaced(val *ErrorFlowReplaced) *NullableErrorFlowReplaced {\n\treturn &NullableErrorFlowReplaced{value: val, isSet: true}\n}\n\nfunc (v NullableErrorFlowReplaced) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableErrorFlowReplaced) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_error_generic.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ErrorGeneric type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ErrorGeneric{}\n\n// ErrorGeneric The standard Ory JSON API error format.\ntype ErrorGeneric struct {\n\tError                GenericError `json:\"error\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ErrorGeneric ErrorGeneric\n\n// NewErrorGeneric instantiates a new ErrorGeneric object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewErrorGeneric(error_ GenericError) *ErrorGeneric {\n\tthis := ErrorGeneric{}\n\tthis.Error = error_\n\treturn &this\n}\n\n// NewErrorGenericWithDefaults instantiates a new ErrorGeneric object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewErrorGenericWithDefaults() *ErrorGeneric {\n\tthis := ErrorGeneric{}\n\treturn &this\n}\n\n// GetError returns the Error field value\nfunc (o *ErrorGeneric) GetError() GenericError {\n\tif o == nil {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\n\treturn o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value\n// and a boolean to check if the value has been set.\nfunc (o *ErrorGeneric) GetErrorOk() (*GenericError, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Error, true\n}\n\n// SetError sets field value\nfunc (o *ErrorGeneric) SetError(v GenericError) {\n\to.Error = v\n}\n\nfunc (o ErrorGeneric) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ErrorGeneric) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"error\"] = o.Error\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ErrorGeneric) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"error\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarErrorGeneric := _ErrorGeneric{}\n\n\terr = json.Unmarshal(data, &varErrorGeneric)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ErrorGeneric(varErrorGeneric)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableErrorGeneric struct {\n\tvalue *ErrorGeneric\n\tisSet bool\n}\n\nfunc (v NullableErrorGeneric) Get() *ErrorGeneric {\n\treturn v.value\n}\n\nfunc (v *NullableErrorGeneric) Set(val *ErrorGeneric) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableErrorGeneric) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableErrorGeneric) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableErrorGeneric(val *ErrorGeneric) *NullableErrorGeneric {\n\treturn &NullableErrorGeneric{value: val, isSet: true}\n}\n\nfunc (v NullableErrorGeneric) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableErrorGeneric) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_flow_error.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the FlowError type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &FlowError{}\n\n// FlowError struct for FlowError\ntype FlowError struct {\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time             `json:\"created_at,omitempty\"`\n\tError     map[string]interface{} `json:\"error,omitempty\"`\n\t// ID of the error container.\n\tId string `json:\"id\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            *time.Time `json:\"updated_at,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _FlowError FlowError\n\n// NewFlowError instantiates a new FlowError object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewFlowError(id string) *FlowError {\n\tthis := FlowError{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewFlowErrorWithDefaults instantiates a new FlowError object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewFlowErrorWithDefaults() *FlowError {\n\tthis := FlowError{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *FlowError) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *FlowError) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *FlowError) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *FlowError) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *FlowError) GetError() map[string]interface{} {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *FlowError) GetErrorOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *FlowError) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given map[string]interface{} and assigns it to the Error field.\nfunc (o *FlowError) SetError(v map[string]interface{}) {\n\to.Error = v\n}\n\n// GetId returns the Id field value\nfunc (o *FlowError) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *FlowError) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *FlowError) SetId(v string) {\n\to.Id = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *FlowError) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *FlowError) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *FlowError) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *FlowError) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\nfunc (o FlowError) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o FlowError) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *FlowError) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarFlowError := _FlowError{}\n\n\terr = json.Unmarshal(data, &varFlowError)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = FlowError(varFlowError)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableFlowError struct {\n\tvalue *FlowError\n\tisSet bool\n}\n\nfunc (v NullableFlowError) Get() *FlowError {\n\treturn v.value\n}\n\nfunc (v *NullableFlowError) Set(val *FlowError) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableFlowError) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableFlowError) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableFlowError(val *FlowError) *NullableFlowError {\n\treturn &NullableFlowError{value: val, isSet: true}\n}\n\nfunc (v NullableFlowError) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableFlowError) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_generic_error.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the GenericError type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &GenericError{}\n\n// GenericError struct for GenericError\ntype GenericError struct {\n\t// The status code\n\tCode *int64 `json:\"code,omitempty\"`\n\t// Debug information  This field is often not exposed to protect against leaking sensitive information.\n\tDebug *string `json:\"debug,omitempty\"`\n\t// Further error details\n\tDetails map[string]interface{} `json:\"details,omitempty\"`\n\t// The error ID  Useful when trying to identify various errors in application logic.\n\tId *string `json:\"id,omitempty\"`\n\t// Error message  The error's message.\n\tMessage string `json:\"message\"`\n\t// A human-readable reason for the error\n\tReason *string `json:\"reason,omitempty\"`\n\t// The request ID  The request ID is often exposed internally in order to trace errors across service architectures. This is often a UUID.\n\tRequest *string `json:\"request,omitempty\"`\n\t// The status description\n\tStatus               *string `json:\"status,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _GenericError GenericError\n\n// NewGenericError instantiates a new GenericError object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewGenericError(message string) *GenericError {\n\tthis := GenericError{}\n\tthis.Message = message\n\treturn &this\n}\n\n// NewGenericErrorWithDefaults instantiates a new GenericError object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewGenericErrorWithDefaults() *GenericError {\n\tthis := GenericError{}\n\treturn &this\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *GenericError) GetCode() int64 {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetCodeOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *GenericError) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given int64 and assigns it to the Code field.\nfunc (o *GenericError) SetCode(v int64) {\n\to.Code = &v\n}\n\n// GetDebug returns the Debug field value if set, zero value otherwise.\nfunc (o *GenericError) GetDebug() string {\n\tif o == nil || IsNil(o.Debug) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Debug\n}\n\n// GetDebugOk returns a tuple with the Debug field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetDebugOk() (*string, bool) {\n\tif o == nil || IsNil(o.Debug) {\n\t\treturn nil, false\n\t}\n\treturn o.Debug, true\n}\n\n// HasDebug returns a boolean if a field has been set.\nfunc (o *GenericError) HasDebug() bool {\n\tif o != nil && !IsNil(o.Debug) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDebug gets a reference to the given string and assigns it to the Debug field.\nfunc (o *GenericError) SetDebug(v string) {\n\to.Debug = &v\n}\n\n// GetDetails returns the Details field value if set, zero value otherwise.\nfunc (o *GenericError) GetDetails() map[string]interface{} {\n\tif o == nil || IsNil(o.Details) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Details\n}\n\n// GetDetailsOk returns a tuple with the Details field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetDetailsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Details) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Details, true\n}\n\n// HasDetails returns a boolean if a field has been set.\nfunc (o *GenericError) HasDetails() bool {\n\tif o != nil && !IsNil(o.Details) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDetails gets a reference to the given map[string]interface{} and assigns it to the Details field.\nfunc (o *GenericError) SetDetails(v map[string]interface{}) {\n\to.Details = v\n}\n\n// GetId returns the Id field value if set, zero value otherwise.\nfunc (o *GenericError) GetId() string {\n\tif o == nil || IsNil(o.Id) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.Id) {\n\t\treturn nil, false\n\t}\n\treturn o.Id, true\n}\n\n// HasId returns a boolean if a field has been set.\nfunc (o *GenericError) HasId() bool {\n\tif o != nil && !IsNil(o.Id) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetId gets a reference to the given string and assigns it to the Id field.\nfunc (o *GenericError) SetId(v string) {\n\to.Id = &v\n}\n\n// GetMessage returns the Message field value\nfunc (o *GenericError) GetMessage() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Message\n}\n\n// GetMessageOk returns a tuple with the Message field value\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetMessageOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Message, true\n}\n\n// SetMessage sets field value\nfunc (o *GenericError) SetMessage(v string) {\n\to.Message = v\n}\n\n// GetReason returns the Reason field value if set, zero value otherwise.\nfunc (o *GenericError) GetReason() string {\n\tif o == nil || IsNil(o.Reason) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Reason\n}\n\n// GetReasonOk returns a tuple with the Reason field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetReasonOk() (*string, bool) {\n\tif o == nil || IsNil(o.Reason) {\n\t\treturn nil, false\n\t}\n\treturn o.Reason, true\n}\n\n// HasReason returns a boolean if a field has been set.\nfunc (o *GenericError) HasReason() bool {\n\tif o != nil && !IsNil(o.Reason) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReason gets a reference to the given string and assigns it to the Reason field.\nfunc (o *GenericError) SetReason(v string) {\n\to.Reason = &v\n}\n\n// GetRequest returns the Request field value if set, zero value otherwise.\nfunc (o *GenericError) GetRequest() string {\n\tif o == nil || IsNil(o.Request) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Request\n}\n\n// GetRequestOk returns a tuple with the Request field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetRequestOk() (*string, bool) {\n\tif o == nil || IsNil(o.Request) {\n\t\treturn nil, false\n\t}\n\treturn o.Request, true\n}\n\n// HasRequest returns a boolean if a field has been set.\nfunc (o *GenericError) HasRequest() bool {\n\tif o != nil && !IsNil(o.Request) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequest gets a reference to the given string and assigns it to the Request field.\nfunc (o *GenericError) SetRequest(v string) {\n\to.Request = &v\n}\n\n// GetStatus returns the Status field value if set, zero value otherwise.\nfunc (o *GenericError) GetStatus() string {\n\tif o == nil || IsNil(o.Status) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetStatusOk() (*string, bool) {\n\tif o == nil || IsNil(o.Status) {\n\t\treturn nil, false\n\t}\n\treturn o.Status, true\n}\n\n// HasStatus returns a boolean if a field has been set.\nfunc (o *GenericError) HasStatus() bool {\n\tif o != nil && !IsNil(o.Status) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetStatus gets a reference to the given string and assigns it to the Status field.\nfunc (o *GenericError) SetStatus(v string) {\n\to.Status = &v\n}\n\nfunc (o GenericError) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o GenericError) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\tif !IsNil(o.Debug) {\n\t\ttoSerialize[\"debug\"] = o.Debug\n\t}\n\tif !IsNil(o.Details) {\n\t\ttoSerialize[\"details\"] = o.Details\n\t}\n\tif !IsNil(o.Id) {\n\t\ttoSerialize[\"id\"] = o.Id\n\t}\n\ttoSerialize[\"message\"] = o.Message\n\tif !IsNil(o.Reason) {\n\t\ttoSerialize[\"reason\"] = o.Reason\n\t}\n\tif !IsNil(o.Request) {\n\t\ttoSerialize[\"request\"] = o.Request\n\t}\n\tif !IsNil(o.Status) {\n\t\ttoSerialize[\"status\"] = o.Status\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *GenericError) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"message\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarGenericError := _GenericError{}\n\n\terr = json.Unmarshal(data, &varGenericError)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = GenericError(varGenericError)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"debug\")\n\t\tdelete(additionalProperties, \"details\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"message\")\n\t\tdelete(additionalProperties, \"reason\")\n\t\tdelete(additionalProperties, \"request\")\n\t\tdelete(additionalProperties, \"status\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableGenericError struct {\n\tvalue *GenericError\n\tisSet bool\n}\n\nfunc (v NullableGenericError) Get() *GenericError {\n\treturn v.value\n}\n\nfunc (v *NullableGenericError) Set(val *GenericError) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableGenericError) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableGenericError) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableGenericError(val *GenericError) *NullableGenericError {\n\treturn &NullableGenericError{value: val, isSet: true}\n}\n\nfunc (v NullableGenericError) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableGenericError) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_get_version_200_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the GetVersion200Response type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &GetVersion200Response{}\n\n// GetVersion200Response struct for GetVersion200Response\ntype GetVersion200Response struct {\n\t// The version of Ory Kratos.\n\tVersion              string `json:\"version\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _GetVersion200Response GetVersion200Response\n\n// NewGetVersion200Response instantiates a new GetVersion200Response object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewGetVersion200Response(version string) *GetVersion200Response {\n\tthis := GetVersion200Response{}\n\tthis.Version = version\n\treturn &this\n}\n\n// NewGetVersion200ResponseWithDefaults instantiates a new GetVersion200Response object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewGetVersion200ResponseWithDefaults() *GetVersion200Response {\n\tthis := GetVersion200Response{}\n\treturn &this\n}\n\n// GetVersion returns the Version field value\nfunc (o *GetVersion200Response) GetVersion() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Version\n}\n\n// GetVersionOk returns a tuple with the Version field value\n// and a boolean to check if the value has been set.\nfunc (o *GetVersion200Response) GetVersionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Version, true\n}\n\n// SetVersion sets field value\nfunc (o *GetVersion200Response) SetVersion(v string) {\n\to.Version = v\n}\n\nfunc (o GetVersion200Response) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o GetVersion200Response) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"version\"] = o.Version\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *GetVersion200Response) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"version\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarGetVersion200Response := _GetVersion200Response{}\n\n\terr = json.Unmarshal(data, &varGetVersion200Response)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = GetVersion200Response(varGetVersion200Response)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"version\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableGetVersion200Response struct {\n\tvalue *GetVersion200Response\n\tisSet bool\n}\n\nfunc (v NullableGetVersion200Response) Get() *GetVersion200Response {\n\treturn v.value\n}\n\nfunc (v *NullableGetVersion200Response) Set(val *GetVersion200Response) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableGetVersion200Response) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableGetVersion200Response) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableGetVersion200Response(val *GetVersion200Response) *NullableGetVersion200Response {\n\treturn &NullableGetVersion200Response{value: val, isSet: true}\n}\n\nfunc (v NullableGetVersion200Response) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableGetVersion200Response) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_health_not_ready_status.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the HealthNotReadyStatus type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &HealthNotReadyStatus{}\n\n// HealthNotReadyStatus struct for HealthNotReadyStatus\ntype HealthNotReadyStatus struct {\n\t// Errors contains a list of errors that caused the not ready status.\n\tErrors               *map[string]string `json:\"errors,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _HealthNotReadyStatus HealthNotReadyStatus\n\n// NewHealthNotReadyStatus instantiates a new HealthNotReadyStatus object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewHealthNotReadyStatus() *HealthNotReadyStatus {\n\tthis := HealthNotReadyStatus{}\n\treturn &this\n}\n\n// NewHealthNotReadyStatusWithDefaults instantiates a new HealthNotReadyStatus object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewHealthNotReadyStatusWithDefaults() *HealthNotReadyStatus {\n\tthis := HealthNotReadyStatus{}\n\treturn &this\n}\n\n// GetErrors returns the Errors field value if set, zero value otherwise.\nfunc (o *HealthNotReadyStatus) GetErrors() map[string]string {\n\tif o == nil || IsNil(o.Errors) {\n\t\tvar ret map[string]string\n\t\treturn ret\n\t}\n\treturn *o.Errors\n}\n\n// GetErrorsOk returns a tuple with the Errors field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *HealthNotReadyStatus) GetErrorsOk() (*map[string]string, bool) {\n\tif o == nil || IsNil(o.Errors) {\n\t\treturn nil, false\n\t}\n\treturn o.Errors, true\n}\n\n// HasErrors returns a boolean if a field has been set.\nfunc (o *HealthNotReadyStatus) HasErrors() bool {\n\tif o != nil && !IsNil(o.Errors) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetErrors gets a reference to the given map[string]string and assigns it to the Errors field.\nfunc (o *HealthNotReadyStatus) SetErrors(v map[string]string) {\n\to.Errors = &v\n}\n\nfunc (o HealthNotReadyStatus) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o HealthNotReadyStatus) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Errors) {\n\t\ttoSerialize[\"errors\"] = o.Errors\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *HealthNotReadyStatus) UnmarshalJSON(data []byte) (err error) {\n\tvarHealthNotReadyStatus := _HealthNotReadyStatus{}\n\n\terr = json.Unmarshal(data, &varHealthNotReadyStatus)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = HealthNotReadyStatus(varHealthNotReadyStatus)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"errors\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableHealthNotReadyStatus struct {\n\tvalue *HealthNotReadyStatus\n\tisSet bool\n}\n\nfunc (v NullableHealthNotReadyStatus) Get() *HealthNotReadyStatus {\n\treturn v.value\n}\n\nfunc (v *NullableHealthNotReadyStatus) Set(val *HealthNotReadyStatus) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableHealthNotReadyStatus) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableHealthNotReadyStatus) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableHealthNotReadyStatus(val *HealthNotReadyStatus) *NullableHealthNotReadyStatus {\n\treturn &NullableHealthNotReadyStatus{value: val, isSet: true}\n}\n\nfunc (v NullableHealthNotReadyStatus) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableHealthNotReadyStatus) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_health_status.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the HealthStatus type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &HealthStatus{}\n\n// HealthStatus struct for HealthStatus\ntype HealthStatus struct {\n\t// Status always contains \\\"ok\\\".\n\tStatus               *string `json:\"status,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _HealthStatus HealthStatus\n\n// NewHealthStatus instantiates a new HealthStatus object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewHealthStatus() *HealthStatus {\n\tthis := HealthStatus{}\n\treturn &this\n}\n\n// NewHealthStatusWithDefaults instantiates a new HealthStatus object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewHealthStatusWithDefaults() *HealthStatus {\n\tthis := HealthStatus{}\n\treturn &this\n}\n\n// GetStatus returns the Status field value if set, zero value otherwise.\nfunc (o *HealthStatus) GetStatus() string {\n\tif o == nil || IsNil(o.Status) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *HealthStatus) GetStatusOk() (*string, bool) {\n\tif o == nil || IsNil(o.Status) {\n\t\treturn nil, false\n\t}\n\treturn o.Status, true\n}\n\n// HasStatus returns a boolean if a field has been set.\nfunc (o *HealthStatus) HasStatus() bool {\n\tif o != nil && !IsNil(o.Status) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetStatus gets a reference to the given string and assigns it to the Status field.\nfunc (o *HealthStatus) SetStatus(v string) {\n\to.Status = &v\n}\n\nfunc (o HealthStatus) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o HealthStatus) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Status) {\n\t\ttoSerialize[\"status\"] = o.Status\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *HealthStatus) UnmarshalJSON(data []byte) (err error) {\n\tvarHealthStatus := _HealthStatus{}\n\n\terr = json.Unmarshal(data, &varHealthStatus)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = HealthStatus(varHealthStatus)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"status\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableHealthStatus struct {\n\tvalue *HealthStatus\n\tisSet bool\n}\n\nfunc (v NullableHealthStatus) Get() *HealthStatus {\n\treturn v.value\n}\n\nfunc (v *NullableHealthStatus) Set(val *HealthStatus) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableHealthStatus) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableHealthStatus) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableHealthStatus(val *HealthStatus) *NullableHealthStatus {\n\treturn &NullableHealthStatus{value: val, isSet: true}\n}\n\nfunc (v NullableHealthStatus) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableHealthStatus) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the Identity type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Identity{}\n\n// Identity An [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) represents a (human) user in Ory.\ntype Identity struct {\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// Credentials represents all credentials that can be used for authenticating this identity.\n\tCredentials *map[string]IdentityCredentials `json:\"credentials,omitempty\"`\n\t// ExternalID is an optional external ID of the identity. This is used to link the identity to an external system. If set, the external ID must be unique across all identities.\n\tExternalId *string `json:\"external_id,omitempty\"`\n\t// ID is the identity's unique identifier.  The Identity ID can not be changed and can not be chosen. This ensures future compatibility and optimization for distributed stores such as CockroachDB.\n\tId string `json:\"id\"`\n\t// NullJSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger and is NULLable-\n\tMetadataAdmin interface{} `json:\"metadata_admin,omitempty\"`\n\t// NullJSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger and is NULLable-\n\tMetadataPublic interface{}    `json:\"metadata_public,omitempty\"`\n\tOrganizationId NullableString `json:\"organization_id,omitempty\"`\n\t// RecoveryAddresses contains all the addresses that can be used to recover an identity.\n\tRecoveryAddresses []RecoveryIdentityAddress `json:\"recovery_addresses,omitempty\"`\n\t// SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\n\tSchemaId string `json:\"schema_id\"`\n\t// SchemaURL is the URL of the endpoint where the identity's traits schema can be fetched from.  format: url\n\tSchemaUrl string `json:\"schema_url\"`\n\t// State is the identity's state.  This value has currently no effect. active StateActive inactive StateInactive\n\tState          *string    `json:\"state,omitempty\"`\n\tStateChangedAt *time.Time `json:\"state_changed_at,omitempty\"`\n\t// Traits represent an identity's traits. The identity is able to create, modify, and delete traits in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_url`.\n\tTraits interface{} `json:\"traits\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\"`\n\t// VerifiableAddresses contains all the addresses that can be verified by the user.\n\tVerifiableAddresses  []VerifiableIdentityAddress `json:\"verifiable_addresses,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Identity Identity\n\n// NewIdentity instantiates a new Identity object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentity(id string, schemaId string, schemaUrl string, traits interface{}) *Identity {\n\tthis := Identity{}\n\tthis.Id = id\n\tthis.SchemaId = schemaId\n\tthis.SchemaUrl = schemaUrl\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewIdentityWithDefaults instantiates a new Identity object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithDefaults() *Identity {\n\tthis := Identity{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *Identity) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *Identity) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *Identity) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetCredentials returns the Credentials field value if set, zero value otherwise.\nfunc (o *Identity) GetCredentials() map[string]IdentityCredentials {\n\tif o == nil || IsNil(o.Credentials) {\n\t\tvar ret map[string]IdentityCredentials\n\t\treturn ret\n\t}\n\treturn *o.Credentials\n}\n\n// GetCredentialsOk returns a tuple with the Credentials field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetCredentialsOk() (*map[string]IdentityCredentials, bool) {\n\tif o == nil || IsNil(o.Credentials) {\n\t\treturn nil, false\n\t}\n\treturn o.Credentials, true\n}\n\n// HasCredentials returns a boolean if a field has been set.\nfunc (o *Identity) HasCredentials() bool {\n\tif o != nil && !IsNil(o.Credentials) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCredentials gets a reference to the given map[string]IdentityCredentials and assigns it to the Credentials field.\nfunc (o *Identity) SetCredentials(v map[string]IdentityCredentials) {\n\to.Credentials = &v\n}\n\n// GetExternalId returns the ExternalId field value if set, zero value otherwise.\nfunc (o *Identity) GetExternalId() string {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExternalId\n}\n\n// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetExternalIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\treturn nil, false\n\t}\n\treturn o.ExternalId, true\n}\n\n// HasExternalId returns a boolean if a field has been set.\nfunc (o *Identity) HasExternalId() bool {\n\tif o != nil && !IsNil(o.ExternalId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.\nfunc (o *Identity) SetExternalId(v string) {\n\to.ExternalId = &v\n}\n\n// GetId returns the Id field value\nfunc (o *Identity) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *Identity) SetId(v string) {\n\to.Id = v\n}\n\n// GetMetadataAdmin returns the MetadataAdmin field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *Identity) GetMetadataAdmin() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataAdmin\n}\n\n// GetMetadataAdminOk returns a tuple with the MetadataAdmin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *Identity) GetMetadataAdminOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataAdmin) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataAdmin, true\n}\n\n// HasMetadataAdmin returns a boolean if a field has been set.\nfunc (o *Identity) HasMetadataAdmin() bool {\n\tif o != nil && !IsNil(o.MetadataAdmin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataAdmin gets a reference to the given interface{} and assigns it to the MetadataAdmin field.\nfunc (o *Identity) SetMetadataAdmin(v interface{}) {\n\to.MetadataAdmin = v\n}\n\n// GetMetadataPublic returns the MetadataPublic field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *Identity) GetMetadataPublic() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataPublic\n}\n\n// GetMetadataPublicOk returns a tuple with the MetadataPublic field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *Identity) GetMetadataPublicOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataPublic) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataPublic, true\n}\n\n// HasMetadataPublic returns a boolean if a field has been set.\nfunc (o *Identity) HasMetadataPublic() bool {\n\tif o != nil && !IsNil(o.MetadataPublic) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataPublic gets a reference to the given interface{} and assigns it to the MetadataPublic field.\nfunc (o *Identity) SetMetadataPublic(v interface{}) {\n\to.MetadataPublic = v\n}\n\n// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *Identity) GetOrganizationId() string {\n\tif o == nil || IsNil(o.OrganizationId.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OrganizationId.Get()\n}\n\n// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *Identity) GetOrganizationIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.OrganizationId.Get(), o.OrganizationId.IsSet()\n}\n\n// HasOrganizationId returns a boolean if a field has been set.\nfunc (o *Identity) HasOrganizationId() bool {\n\tif o != nil && o.OrganizationId.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganizationId gets a reference to the given NullableString and assigns it to the OrganizationId field.\nfunc (o *Identity) SetOrganizationId(v string) {\n\to.OrganizationId.Set(&v)\n}\n\n// SetOrganizationIdNil sets the value for OrganizationId to be an explicit nil\nfunc (o *Identity) SetOrganizationIdNil() {\n\to.OrganizationId.Set(nil)\n}\n\n// UnsetOrganizationId ensures that no value is present for OrganizationId, not even an explicit nil\nfunc (o *Identity) UnsetOrganizationId() {\n\to.OrganizationId.Unset()\n}\n\n// GetRecoveryAddresses returns the RecoveryAddresses field value if set, zero value otherwise.\nfunc (o *Identity) GetRecoveryAddresses() []RecoveryIdentityAddress {\n\tif o == nil || IsNil(o.RecoveryAddresses) {\n\t\tvar ret []RecoveryIdentityAddress\n\t\treturn ret\n\t}\n\treturn o.RecoveryAddresses\n}\n\n// GetRecoveryAddressesOk returns a tuple with the RecoveryAddresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetRecoveryAddressesOk() ([]RecoveryIdentityAddress, bool) {\n\tif o == nil || IsNil(o.RecoveryAddresses) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoveryAddresses, true\n}\n\n// HasRecoveryAddresses returns a boolean if a field has been set.\nfunc (o *Identity) HasRecoveryAddresses() bool {\n\tif o != nil && !IsNil(o.RecoveryAddresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoveryAddresses gets a reference to the given []RecoveryIdentityAddress and assigns it to the RecoveryAddresses field.\nfunc (o *Identity) SetRecoveryAddresses(v []RecoveryIdentityAddress) {\n\to.RecoveryAddresses = v\n}\n\n// GetSchemaId returns the SchemaId field value\nfunc (o *Identity) GetSchemaId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SchemaId\n}\n\n// GetSchemaIdOk returns a tuple with the SchemaId field value\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetSchemaIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SchemaId, true\n}\n\n// SetSchemaId sets field value\nfunc (o *Identity) SetSchemaId(v string) {\n\to.SchemaId = v\n}\n\n// GetSchemaUrl returns the SchemaUrl field value\nfunc (o *Identity) GetSchemaUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SchemaUrl\n}\n\n// GetSchemaUrlOk returns a tuple with the SchemaUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetSchemaUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SchemaUrl, true\n}\n\n// SetSchemaUrl sets field value\nfunc (o *Identity) SetSchemaUrl(v string) {\n\to.SchemaUrl = v\n}\n\n// GetState returns the State field value if set, zero value otherwise.\nfunc (o *Identity) GetState() string {\n\tif o == nil || IsNil(o.State) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.State\n}\n\n// GetStateOk returns a tuple with the State field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetStateOk() (*string, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn o.State, true\n}\n\n// HasState returns a boolean if a field has been set.\nfunc (o *Identity) HasState() bool {\n\tif o != nil && !IsNil(o.State) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetState gets a reference to the given string and assigns it to the State field.\nfunc (o *Identity) SetState(v string) {\n\to.State = &v\n}\n\n// GetStateChangedAt returns the StateChangedAt field value if set, zero value otherwise.\nfunc (o *Identity) GetStateChangedAt() time.Time {\n\tif o == nil || IsNil(o.StateChangedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.StateChangedAt\n}\n\n// GetStateChangedAtOk returns a tuple with the StateChangedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetStateChangedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.StateChangedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.StateChangedAt, true\n}\n\n// HasStateChangedAt returns a boolean if a field has been set.\nfunc (o *Identity) HasStateChangedAt() bool {\n\tif o != nil && !IsNil(o.StateChangedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetStateChangedAt gets a reference to the given time.Time and assigns it to the StateChangedAt field.\nfunc (o *Identity) SetStateChangedAt(v time.Time) {\n\to.StateChangedAt = &v\n}\n\n// GetTraits returns the Traits field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *Identity) GetTraits() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *Identity) GetTraitsOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn nil, false\n\t}\n\treturn &o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *Identity) SetTraits(v interface{}) {\n\to.Traits = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *Identity) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *Identity) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *Identity) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetVerifiableAddresses returns the VerifiableAddresses field value if set, zero value otherwise.\nfunc (o *Identity) GetVerifiableAddresses() []VerifiableIdentityAddress {\n\tif o == nil || IsNil(o.VerifiableAddresses) {\n\t\tvar ret []VerifiableIdentityAddress\n\t\treturn ret\n\t}\n\treturn o.VerifiableAddresses\n}\n\n// GetVerifiableAddressesOk returns a tuple with the VerifiableAddresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetVerifiableAddressesOk() ([]VerifiableIdentityAddress, bool) {\n\tif o == nil || IsNil(o.VerifiableAddresses) {\n\t\treturn nil, false\n\t}\n\treturn o.VerifiableAddresses, true\n}\n\n// HasVerifiableAddresses returns a boolean if a field has been set.\nfunc (o *Identity) HasVerifiableAddresses() bool {\n\tif o != nil && !IsNil(o.VerifiableAddresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVerifiableAddresses gets a reference to the given []VerifiableIdentityAddress and assigns it to the VerifiableAddresses field.\nfunc (o *Identity) SetVerifiableAddresses(v []VerifiableIdentityAddress) {\n\to.VerifiableAddresses = v\n}\n\nfunc (o Identity) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Identity) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Credentials) {\n\t\ttoSerialize[\"credentials\"] = o.Credentials\n\t}\n\tif !IsNil(o.ExternalId) {\n\t\ttoSerialize[\"external_id\"] = o.ExternalId\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\tif o.MetadataAdmin != nil {\n\t\ttoSerialize[\"metadata_admin\"] = o.MetadataAdmin\n\t}\n\tif o.MetadataPublic != nil {\n\t\ttoSerialize[\"metadata_public\"] = o.MetadataPublic\n\t}\n\tif o.OrganizationId.IsSet() {\n\t\ttoSerialize[\"organization_id\"] = o.OrganizationId.Get()\n\t}\n\tif !IsNil(o.RecoveryAddresses) {\n\t\ttoSerialize[\"recovery_addresses\"] = o.RecoveryAddresses\n\t}\n\ttoSerialize[\"schema_id\"] = o.SchemaId\n\ttoSerialize[\"schema_url\"] = o.SchemaUrl\n\tif !IsNil(o.State) {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.StateChangedAt) {\n\t\ttoSerialize[\"state_changed_at\"] = o.StateChangedAt\n\t}\n\tif o.Traits != nil {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\tif !IsNil(o.VerifiableAddresses) {\n\t\ttoSerialize[\"verifiable_addresses\"] = o.VerifiableAddresses\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Identity) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"schema_id\",\n\t\t\"schema_url\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIdentity := _Identity{}\n\n\terr = json.Unmarshal(data, &varIdentity)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Identity(varIdentity)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"credentials\")\n\t\tdelete(additionalProperties, \"external_id\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"metadata_admin\")\n\t\tdelete(additionalProperties, \"metadata_public\")\n\t\tdelete(additionalProperties, \"organization_id\")\n\t\tdelete(additionalProperties, \"recovery_addresses\")\n\t\tdelete(additionalProperties, \"schema_id\")\n\t\tdelete(additionalProperties, \"schema_url\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"state_changed_at\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"verifiable_addresses\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentity struct {\n\tvalue *Identity\n\tisSet bool\n}\n\nfunc (v NullableIdentity) Get() *Identity {\n\treturn v.value\n}\n\nfunc (v *NullableIdentity) Set(val *Identity) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentity) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentity) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentity(val *Identity) *NullableIdentity {\n\treturn &NullableIdentity{value: val, isSet: true}\n}\n\nfunc (v NullableIdentity) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentity) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_credentials.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n)\n\n// checks if the IdentityCredentials type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentials{}\n\n// IdentityCredentials Credentials represents a specific credential type\ntype IdentityCredentials struct {\n\tConfig map[string]interface{} `json:\"config,omitempty\"`\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// Identifiers represent a list of unique identifiers this credential type matches.\n\tIdentifiers []string `json:\"identifiers,omitempty\"`\n\t// Type discriminates between different types of credentials. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\tType *string `json:\"type,omitempty\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\"`\n\t// Version refers to the version of the credential. Useful when changing the config schema.\n\tVersion              *int64 `json:\"version,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentials IdentityCredentials\n\n// NewIdentityCredentials instantiates a new IdentityCredentials object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentials() *IdentityCredentials {\n\tthis := IdentityCredentials{}\n\treturn &this\n}\n\n// NewIdentityCredentialsWithDefaults instantiates a new IdentityCredentials object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsWithDefaults() *IdentityCredentials {\n\tthis := IdentityCredentials{}\n\treturn &this\n}\n\n// GetConfig returns the Config field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetConfig() map[string]interface{} {\n\tif o == nil || IsNil(o.Config) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Config\n}\n\n// GetConfigOk returns a tuple with the Config field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetConfigOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Config) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Config, true\n}\n\n// HasConfig returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasConfig() bool {\n\tif o != nil && !IsNil(o.Config) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfig gets a reference to the given map[string]interface{} and assigns it to the Config field.\nfunc (o *IdentityCredentials) SetConfig(v map[string]interface{}) {\n\to.Config = v\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *IdentityCredentials) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetIdentifiers returns the Identifiers field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetIdentifiers() []string {\n\tif o == nil || IsNil(o.Identifiers) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.Identifiers\n}\n\n// GetIdentifiersOk returns a tuple with the Identifiers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetIdentifiersOk() ([]string, bool) {\n\tif o == nil || IsNil(o.Identifiers) {\n\t\treturn nil, false\n\t}\n\treturn o.Identifiers, true\n}\n\n// HasIdentifiers returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasIdentifiers() bool {\n\tif o != nil && !IsNil(o.Identifiers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentifiers gets a reference to the given []string and assigns it to the Identifiers field.\nfunc (o *IdentityCredentials) SetIdentifiers(v []string) {\n\to.Identifiers = v\n}\n\n// GetType returns the Type field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetType() string {\n\tif o == nil || IsNil(o.Type) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetTypeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Type) {\n\t\treturn nil, false\n\t}\n\treturn o.Type, true\n}\n\n// HasType returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasType() bool {\n\tif o != nil && !IsNil(o.Type) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetType gets a reference to the given string and assigns it to the Type field.\nfunc (o *IdentityCredentials) SetType(v string) {\n\to.Type = &v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *IdentityCredentials) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetVersion returns the Version field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetVersion() int64 {\n\tif o == nil || IsNil(o.Version) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Version\n}\n\n// GetVersionOk returns a tuple with the Version field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetVersionOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Version) {\n\t\treturn nil, false\n\t}\n\treturn o.Version, true\n}\n\n// HasVersion returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasVersion() bool {\n\tif o != nil && !IsNil(o.Version) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVersion gets a reference to the given int64 and assigns it to the Version field.\nfunc (o *IdentityCredentials) SetVersion(v int64) {\n\to.Version = &v\n}\n\nfunc (o IdentityCredentials) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentials) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Config) {\n\t\ttoSerialize[\"config\"] = o.Config\n\t}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Identifiers) {\n\t\ttoSerialize[\"identifiers\"] = o.Identifiers\n\t}\n\tif !IsNil(o.Type) {\n\t\ttoSerialize[\"type\"] = o.Type\n\t}\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\tif !IsNil(o.Version) {\n\t\ttoSerialize[\"version\"] = o.Version\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentials) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentials := _IdentityCredentials{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentials)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentials(varIdentityCredentials)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"config\")\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"identifiers\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"version\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentials struct {\n\tvalue *IdentityCredentials\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentials) Get() *IdentityCredentials {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentials) Set(val *IdentityCredentials) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentials) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentials) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentials(val *IdentityCredentials) *NullableIdentityCredentials {\n\treturn &NullableIdentityCredentials{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentials) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentials) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_credentials_code.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsCode type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsCode{}\n\n// IdentityCredentialsCode CredentialsCode represents a one time login/registration code\ntype IdentityCredentialsCode struct {\n\tAddresses            []IdentityCredentialsCodeAddress `json:\"addresses,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentialsCode IdentityCredentialsCode\n\n// NewIdentityCredentialsCode instantiates a new IdentityCredentialsCode object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsCode() *IdentityCredentialsCode {\n\tthis := IdentityCredentialsCode{}\n\treturn &this\n}\n\n// NewIdentityCredentialsCodeWithDefaults instantiates a new IdentityCredentialsCode object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsCodeWithDefaults() *IdentityCredentialsCode {\n\tthis := IdentityCredentialsCode{}\n\treturn &this\n}\n\n// GetAddresses returns the Addresses field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsCode) GetAddresses() []IdentityCredentialsCodeAddress {\n\tif o == nil || IsNil(o.Addresses) {\n\t\tvar ret []IdentityCredentialsCodeAddress\n\t\treturn ret\n\t}\n\treturn o.Addresses\n}\n\n// GetAddressesOk returns a tuple with the Addresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsCode) GetAddressesOk() ([]IdentityCredentialsCodeAddress, bool) {\n\tif o == nil || IsNil(o.Addresses) {\n\t\treturn nil, false\n\t}\n\treturn o.Addresses, true\n}\n\n// HasAddresses returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsCode) HasAddresses() bool {\n\tif o != nil && !IsNil(o.Addresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAddresses gets a reference to the given []IdentityCredentialsCodeAddress and assigns it to the Addresses field.\nfunc (o *IdentityCredentialsCode) SetAddresses(v []IdentityCredentialsCodeAddress) {\n\to.Addresses = v\n}\n\nfunc (o IdentityCredentialsCode) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsCode) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Addresses) {\n\t\ttoSerialize[\"addresses\"] = o.Addresses\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsCode) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsCode := _IdentityCredentialsCode{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsCode)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsCode(varIdentityCredentialsCode)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"addresses\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsCode struct {\n\tvalue *IdentityCredentialsCode\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsCode) Get() *IdentityCredentialsCode {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsCode) Set(val *IdentityCredentialsCode) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsCode) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsCode) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsCode(val *IdentityCredentialsCode) *NullableIdentityCredentialsCode {\n\treturn &NullableIdentityCredentialsCode{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsCode) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsCode) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_credentials_code_address.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsCodeAddress type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsCodeAddress{}\n\n// IdentityCredentialsCodeAddress struct for IdentityCredentialsCodeAddress\ntype IdentityCredentialsCodeAddress struct {\n\t// The address for this code\n\tAddress              *string `json:\"address,omitempty\"`\n\tChannel              *string `json:\"channel,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentialsCodeAddress IdentityCredentialsCodeAddress\n\n// NewIdentityCredentialsCodeAddress instantiates a new IdentityCredentialsCodeAddress object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsCodeAddress() *IdentityCredentialsCodeAddress {\n\tthis := IdentityCredentialsCodeAddress{}\n\treturn &this\n}\n\n// NewIdentityCredentialsCodeAddressWithDefaults instantiates a new IdentityCredentialsCodeAddress object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsCodeAddressWithDefaults() *IdentityCredentialsCodeAddress {\n\tthis := IdentityCredentialsCodeAddress{}\n\treturn &this\n}\n\n// GetAddress returns the Address field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsCodeAddress) GetAddress() string {\n\tif o == nil || IsNil(o.Address) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Address\n}\n\n// GetAddressOk returns a tuple with the Address field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsCodeAddress) GetAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.Address) {\n\t\treturn nil, false\n\t}\n\treturn o.Address, true\n}\n\n// HasAddress returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsCodeAddress) HasAddress() bool {\n\tif o != nil && !IsNil(o.Address) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAddress gets a reference to the given string and assigns it to the Address field.\nfunc (o *IdentityCredentialsCodeAddress) SetAddress(v string) {\n\to.Address = &v\n}\n\n// GetChannel returns the Channel field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsCodeAddress) GetChannel() string {\n\tif o == nil || IsNil(o.Channel) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Channel\n}\n\n// GetChannelOk returns a tuple with the Channel field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsCodeAddress) GetChannelOk() (*string, bool) {\n\tif o == nil || IsNil(o.Channel) {\n\t\treturn nil, false\n\t}\n\treturn o.Channel, true\n}\n\n// HasChannel returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsCodeAddress) HasChannel() bool {\n\tif o != nil && !IsNil(o.Channel) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetChannel gets a reference to the given string and assigns it to the Channel field.\nfunc (o *IdentityCredentialsCodeAddress) SetChannel(v string) {\n\to.Channel = &v\n}\n\nfunc (o IdentityCredentialsCodeAddress) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsCodeAddress) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Address) {\n\t\ttoSerialize[\"address\"] = o.Address\n\t}\n\tif !IsNil(o.Channel) {\n\t\ttoSerialize[\"channel\"] = o.Channel\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsCodeAddress) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsCodeAddress := _IdentityCredentialsCodeAddress{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsCodeAddress)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsCodeAddress(varIdentityCredentialsCodeAddress)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"address\")\n\t\tdelete(additionalProperties, \"channel\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsCodeAddress struct {\n\tvalue *IdentityCredentialsCodeAddress\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsCodeAddress) Get() *IdentityCredentialsCodeAddress {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsCodeAddress) Set(val *IdentityCredentialsCodeAddress) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsCodeAddress) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsCodeAddress) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsCodeAddress(val *IdentityCredentialsCodeAddress) *NullableIdentityCredentialsCodeAddress {\n\treturn &NullableIdentityCredentialsCodeAddress{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsCodeAddress) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsCodeAddress) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_credentials_oidc.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsOidc type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsOidc{}\n\n// IdentityCredentialsOidc struct for IdentityCredentialsOidc\ntype IdentityCredentialsOidc struct {\n\tProviders            []IdentityCredentialsOidcProvider `json:\"providers,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentialsOidc IdentityCredentialsOidc\n\n// NewIdentityCredentialsOidc instantiates a new IdentityCredentialsOidc object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsOidc() *IdentityCredentialsOidc {\n\tthis := IdentityCredentialsOidc{}\n\treturn &this\n}\n\n// NewIdentityCredentialsOidcWithDefaults instantiates a new IdentityCredentialsOidc object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsOidcWithDefaults() *IdentityCredentialsOidc {\n\tthis := IdentityCredentialsOidc{}\n\treturn &this\n}\n\n// GetProviders returns the Providers field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidc) GetProviders() []IdentityCredentialsOidcProvider {\n\tif o == nil || IsNil(o.Providers) {\n\t\tvar ret []IdentityCredentialsOidcProvider\n\t\treturn ret\n\t}\n\treturn o.Providers\n}\n\n// GetProvidersOk returns a tuple with the Providers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidc) GetProvidersOk() ([]IdentityCredentialsOidcProvider, bool) {\n\tif o == nil || IsNil(o.Providers) {\n\t\treturn nil, false\n\t}\n\treturn o.Providers, true\n}\n\n// HasProviders returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidc) HasProviders() bool {\n\tif o != nil && !IsNil(o.Providers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProviders gets a reference to the given []IdentityCredentialsOidcProvider and assigns it to the Providers field.\nfunc (o *IdentityCredentialsOidc) SetProviders(v []IdentityCredentialsOidcProvider) {\n\to.Providers = v\n}\n\nfunc (o IdentityCredentialsOidc) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsOidc) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Providers) {\n\t\ttoSerialize[\"providers\"] = o.Providers\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsOidc) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsOidc := _IdentityCredentialsOidc{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsOidc)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsOidc(varIdentityCredentialsOidc)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"providers\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsOidc struct {\n\tvalue *IdentityCredentialsOidc\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsOidc) Get() *IdentityCredentialsOidc {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsOidc) Set(val *IdentityCredentialsOidc) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsOidc) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsOidc) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsOidc(val *IdentityCredentialsOidc) *NullableIdentityCredentialsOidc {\n\treturn &NullableIdentityCredentialsOidc{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsOidc) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsOidc) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_credentials_oidc_provider.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsOidcProvider type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsOidcProvider{}\n\n// IdentityCredentialsOidcProvider struct for IdentityCredentialsOidcProvider\ntype IdentityCredentialsOidcProvider struct {\n\tInitialAccessToken   *string `json:\"initial_access_token,omitempty\"`\n\tInitialIdToken       *string `json:\"initial_id_token,omitempty\"`\n\tInitialRefreshToken  *string `json:\"initial_refresh_token,omitempty\"`\n\tOrganization         *string `json:\"organization,omitempty\"`\n\tProvider             *string `json:\"provider,omitempty\"`\n\tSubject              *string `json:\"subject,omitempty\"`\n\tUseAutoLink          *bool   `json:\"use_auto_link,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentialsOidcProvider IdentityCredentialsOidcProvider\n\n// NewIdentityCredentialsOidcProvider instantiates a new IdentityCredentialsOidcProvider object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsOidcProvider() *IdentityCredentialsOidcProvider {\n\tthis := IdentityCredentialsOidcProvider{}\n\treturn &this\n}\n\n// NewIdentityCredentialsOidcProviderWithDefaults instantiates a new IdentityCredentialsOidcProvider object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsOidcProviderWithDefaults() *IdentityCredentialsOidcProvider {\n\tthis := IdentityCredentialsOidcProvider{}\n\treturn &this\n}\n\n// GetInitialAccessToken returns the InitialAccessToken field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialAccessToken() string {\n\tif o == nil || IsNil(o.InitialAccessToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.InitialAccessToken\n}\n\n// GetInitialAccessTokenOk returns a tuple with the InitialAccessToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialAccessTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.InitialAccessToken) {\n\t\treturn nil, false\n\t}\n\treturn o.InitialAccessToken, true\n}\n\n// HasInitialAccessToken returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasInitialAccessToken() bool {\n\tif o != nil && !IsNil(o.InitialAccessToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetInitialAccessToken gets a reference to the given string and assigns it to the InitialAccessToken field.\nfunc (o *IdentityCredentialsOidcProvider) SetInitialAccessToken(v string) {\n\to.InitialAccessToken = &v\n}\n\n// GetInitialIdToken returns the InitialIdToken field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialIdToken() string {\n\tif o == nil || IsNil(o.InitialIdToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.InitialIdToken\n}\n\n// GetInitialIdTokenOk returns a tuple with the InitialIdToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialIdTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.InitialIdToken) {\n\t\treturn nil, false\n\t}\n\treturn o.InitialIdToken, true\n}\n\n// HasInitialIdToken returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasInitialIdToken() bool {\n\tif o != nil && !IsNil(o.InitialIdToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetInitialIdToken gets a reference to the given string and assigns it to the InitialIdToken field.\nfunc (o *IdentityCredentialsOidcProvider) SetInitialIdToken(v string) {\n\to.InitialIdToken = &v\n}\n\n// GetInitialRefreshToken returns the InitialRefreshToken field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialRefreshToken() string {\n\tif o == nil || IsNil(o.InitialRefreshToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.InitialRefreshToken\n}\n\n// GetInitialRefreshTokenOk returns a tuple with the InitialRefreshToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialRefreshTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.InitialRefreshToken) {\n\t\treturn nil, false\n\t}\n\treturn o.InitialRefreshToken, true\n}\n\n// HasInitialRefreshToken returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasInitialRefreshToken() bool {\n\tif o != nil && !IsNil(o.InitialRefreshToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetInitialRefreshToken gets a reference to the given string and assigns it to the InitialRefreshToken field.\nfunc (o *IdentityCredentialsOidcProvider) SetInitialRefreshToken(v string) {\n\to.InitialRefreshToken = &v\n}\n\n// GetOrganization returns the Organization field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetOrganization() string {\n\tif o == nil || IsNil(o.Organization) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Organization\n}\n\n// GetOrganizationOk returns a tuple with the Organization field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetOrganizationOk() (*string, bool) {\n\tif o == nil || IsNil(o.Organization) {\n\t\treturn nil, false\n\t}\n\treturn o.Organization, true\n}\n\n// HasOrganization returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasOrganization() bool {\n\tif o != nil && !IsNil(o.Organization) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganization gets a reference to the given string and assigns it to the Organization field.\nfunc (o *IdentityCredentialsOidcProvider) SetOrganization(v string) {\n\to.Organization = &v\n}\n\n// GetProvider returns the Provider field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetProvider() string {\n\tif o == nil || IsNil(o.Provider) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetProviderOk() (*string, bool) {\n\tif o == nil || IsNil(o.Provider) {\n\t\treturn nil, false\n\t}\n\treturn o.Provider, true\n}\n\n// HasProvider returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasProvider() bool {\n\tif o != nil && !IsNil(o.Provider) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProvider gets a reference to the given string and assigns it to the Provider field.\nfunc (o *IdentityCredentialsOidcProvider) SetProvider(v string) {\n\to.Provider = &v\n}\n\n// GetSubject returns the Subject field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetSubject() string {\n\tif o == nil || IsNil(o.Subject) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetSubjectOk() (*string, bool) {\n\tif o == nil || IsNil(o.Subject) {\n\t\treturn nil, false\n\t}\n\treturn o.Subject, true\n}\n\n// HasSubject returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasSubject() bool {\n\tif o != nil && !IsNil(o.Subject) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSubject gets a reference to the given string and assigns it to the Subject field.\nfunc (o *IdentityCredentialsOidcProvider) SetSubject(v string) {\n\to.Subject = &v\n}\n\n// GetUseAutoLink returns the UseAutoLink field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetUseAutoLink() bool {\n\tif o == nil || IsNil(o.UseAutoLink) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.UseAutoLink\n}\n\n// GetUseAutoLinkOk returns a tuple with the UseAutoLink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetUseAutoLinkOk() (*bool, bool) {\n\tif o == nil || IsNil(o.UseAutoLink) {\n\t\treturn nil, false\n\t}\n\treturn o.UseAutoLink, true\n}\n\n// HasUseAutoLink returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasUseAutoLink() bool {\n\tif o != nil && !IsNil(o.UseAutoLink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUseAutoLink gets a reference to the given bool and assigns it to the UseAutoLink field.\nfunc (o *IdentityCredentialsOidcProvider) SetUseAutoLink(v bool) {\n\to.UseAutoLink = &v\n}\n\nfunc (o IdentityCredentialsOidcProvider) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsOidcProvider) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.InitialAccessToken) {\n\t\ttoSerialize[\"initial_access_token\"] = o.InitialAccessToken\n\t}\n\tif !IsNil(o.InitialIdToken) {\n\t\ttoSerialize[\"initial_id_token\"] = o.InitialIdToken\n\t}\n\tif !IsNil(o.InitialRefreshToken) {\n\t\ttoSerialize[\"initial_refresh_token\"] = o.InitialRefreshToken\n\t}\n\tif !IsNil(o.Organization) {\n\t\ttoSerialize[\"organization\"] = o.Organization\n\t}\n\tif !IsNil(o.Provider) {\n\t\ttoSerialize[\"provider\"] = o.Provider\n\t}\n\tif !IsNil(o.Subject) {\n\t\ttoSerialize[\"subject\"] = o.Subject\n\t}\n\tif !IsNil(o.UseAutoLink) {\n\t\ttoSerialize[\"use_auto_link\"] = o.UseAutoLink\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsOidcProvider) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsOidcProvider := _IdentityCredentialsOidcProvider{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsOidcProvider)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsOidcProvider(varIdentityCredentialsOidcProvider)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"initial_access_token\")\n\t\tdelete(additionalProperties, \"initial_id_token\")\n\t\tdelete(additionalProperties, \"initial_refresh_token\")\n\t\tdelete(additionalProperties, \"organization\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\tdelete(additionalProperties, \"use_auto_link\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsOidcProvider struct {\n\tvalue *IdentityCredentialsOidcProvider\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsOidcProvider) Get() *IdentityCredentialsOidcProvider {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsOidcProvider) Set(val *IdentityCredentialsOidcProvider) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsOidcProvider) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsOidcProvider) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsOidcProvider(val *IdentityCredentialsOidcProvider) *NullableIdentityCredentialsOidcProvider {\n\treturn &NullableIdentityCredentialsOidcProvider{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsOidcProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsOidcProvider) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_credentials_password.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsPassword type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsPassword{}\n\n// IdentityCredentialsPassword struct for IdentityCredentialsPassword\ntype IdentityCredentialsPassword struct {\n\t// HashedPassword is a hash-representation of the password.\n\tHashedPassword *string `json:\"hashed_password,omitempty\"`\n\t// UsePasswordMigrationHook is set to true if the password should be migrated using the password migration hook. If set, and the HashedPassword is empty, a webhook will be called during login to migrate the password.\n\tUsePasswordMigrationHook *bool `json:\"use_password_migration_hook,omitempty\"`\n\tAdditionalProperties     map[string]interface{}\n}\n\ntype _IdentityCredentialsPassword IdentityCredentialsPassword\n\n// NewIdentityCredentialsPassword instantiates a new IdentityCredentialsPassword object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsPassword() *IdentityCredentialsPassword {\n\tthis := IdentityCredentialsPassword{}\n\treturn &this\n}\n\n// NewIdentityCredentialsPasswordWithDefaults instantiates a new IdentityCredentialsPassword object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsPasswordWithDefaults() *IdentityCredentialsPassword {\n\tthis := IdentityCredentialsPassword{}\n\treturn &this\n}\n\n// GetHashedPassword returns the HashedPassword field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsPassword) GetHashedPassword() string {\n\tif o == nil || IsNil(o.HashedPassword) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.HashedPassword\n}\n\n// GetHashedPasswordOk returns a tuple with the HashedPassword field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsPassword) GetHashedPasswordOk() (*string, bool) {\n\tif o == nil || IsNil(o.HashedPassword) {\n\t\treturn nil, false\n\t}\n\treturn o.HashedPassword, true\n}\n\n// HasHashedPassword returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsPassword) HasHashedPassword() bool {\n\tif o != nil && !IsNil(o.HashedPassword) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetHashedPassword gets a reference to the given string and assigns it to the HashedPassword field.\nfunc (o *IdentityCredentialsPassword) SetHashedPassword(v string) {\n\to.HashedPassword = &v\n}\n\n// GetUsePasswordMigrationHook returns the UsePasswordMigrationHook field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsPassword) GetUsePasswordMigrationHook() bool {\n\tif o == nil || IsNil(o.UsePasswordMigrationHook) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.UsePasswordMigrationHook\n}\n\n// GetUsePasswordMigrationHookOk returns a tuple with the UsePasswordMigrationHook field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsPassword) GetUsePasswordMigrationHookOk() (*bool, bool) {\n\tif o == nil || IsNil(o.UsePasswordMigrationHook) {\n\t\treturn nil, false\n\t}\n\treturn o.UsePasswordMigrationHook, true\n}\n\n// HasUsePasswordMigrationHook returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsPassword) HasUsePasswordMigrationHook() bool {\n\tif o != nil && !IsNil(o.UsePasswordMigrationHook) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUsePasswordMigrationHook gets a reference to the given bool and assigns it to the UsePasswordMigrationHook field.\nfunc (o *IdentityCredentialsPassword) SetUsePasswordMigrationHook(v bool) {\n\to.UsePasswordMigrationHook = &v\n}\n\nfunc (o IdentityCredentialsPassword) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsPassword) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.HashedPassword) {\n\t\ttoSerialize[\"hashed_password\"] = o.HashedPassword\n\t}\n\tif !IsNil(o.UsePasswordMigrationHook) {\n\t\ttoSerialize[\"use_password_migration_hook\"] = o.UsePasswordMigrationHook\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsPassword) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsPassword := _IdentityCredentialsPassword{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsPassword)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsPassword(varIdentityCredentialsPassword)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"hashed_password\")\n\t\tdelete(additionalProperties, \"use_password_migration_hook\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsPassword struct {\n\tvalue *IdentityCredentialsPassword\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsPassword) Get() *IdentityCredentialsPassword {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsPassword) Set(val *IdentityCredentialsPassword) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsPassword) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsPassword) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsPassword(val *IdentityCredentialsPassword) *NullableIdentityCredentialsPassword {\n\treturn &NullableIdentityCredentialsPassword{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsPassword) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsPassword) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_patch.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityPatch type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityPatch{}\n\n// IdentityPatch Payload for patching an identity\ntype IdentityPatch struct {\n\tCreate *CreateIdentityBody `json:\"create,omitempty\"`\n\t// The ID of this patch.  The patch ID is optional. If specified, the ID will be returned in the response, so consumers of this API can correlate the response with the patch.\n\tPatchId              *string `json:\"patch_id,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityPatch IdentityPatch\n\n// NewIdentityPatch instantiates a new IdentityPatch object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityPatch() *IdentityPatch {\n\tthis := IdentityPatch{}\n\treturn &this\n}\n\n// NewIdentityPatchWithDefaults instantiates a new IdentityPatch object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityPatchWithDefaults() *IdentityPatch {\n\tthis := IdentityPatch{}\n\treturn &this\n}\n\n// GetCreate returns the Create field value if set, zero value otherwise.\nfunc (o *IdentityPatch) GetCreate() CreateIdentityBody {\n\tif o == nil || IsNil(o.Create) {\n\t\tvar ret CreateIdentityBody\n\t\treturn ret\n\t}\n\treturn *o.Create\n}\n\n// GetCreateOk returns a tuple with the Create field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatch) GetCreateOk() (*CreateIdentityBody, bool) {\n\tif o == nil || IsNil(o.Create) {\n\t\treturn nil, false\n\t}\n\treturn o.Create, true\n}\n\n// HasCreate returns a boolean if a field has been set.\nfunc (o *IdentityPatch) HasCreate() bool {\n\tif o != nil && !IsNil(o.Create) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreate gets a reference to the given CreateIdentityBody and assigns it to the Create field.\nfunc (o *IdentityPatch) SetCreate(v CreateIdentityBody) {\n\to.Create = &v\n}\n\n// GetPatchId returns the PatchId field value if set, zero value otherwise.\nfunc (o *IdentityPatch) GetPatchId() string {\n\tif o == nil || IsNil(o.PatchId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PatchId\n}\n\n// GetPatchIdOk returns a tuple with the PatchId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatch) GetPatchIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.PatchId) {\n\t\treturn nil, false\n\t}\n\treturn o.PatchId, true\n}\n\n// HasPatchId returns a boolean if a field has been set.\nfunc (o *IdentityPatch) HasPatchId() bool {\n\tif o != nil && !IsNil(o.PatchId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPatchId gets a reference to the given string and assigns it to the PatchId field.\nfunc (o *IdentityPatch) SetPatchId(v string) {\n\to.PatchId = &v\n}\n\nfunc (o IdentityPatch) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityPatch) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Create) {\n\t\ttoSerialize[\"create\"] = o.Create\n\t}\n\tif !IsNil(o.PatchId) {\n\t\ttoSerialize[\"patch_id\"] = o.PatchId\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityPatch) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityPatch := _IdentityPatch{}\n\n\terr = json.Unmarshal(data, &varIdentityPatch)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityPatch(varIdentityPatch)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"create\")\n\t\tdelete(additionalProperties, \"patch_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityPatch struct {\n\tvalue *IdentityPatch\n\tisSet bool\n}\n\nfunc (v NullableIdentityPatch) Get() *IdentityPatch {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityPatch) Set(val *IdentityPatch) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityPatch) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityPatch) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityPatch(val *IdentityPatch) *NullableIdentityPatch {\n\treturn &NullableIdentityPatch{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityPatch) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityPatch) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_patch_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityPatchResponse type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityPatchResponse{}\n\n// IdentityPatchResponse Response for a single identity patch\ntype IdentityPatchResponse struct {\n\t// The action for this specific patch create ActionCreate  Create this identity. error ActionError  Error indicates that the patch failed.\n\tAction *string     `json:\"action,omitempty\"`\n\tError  interface{} `json:\"error,omitempty\"`\n\t// The identity ID payload of this patch\n\tIdentity *string `json:\"identity,omitempty\"`\n\t// The ID of this patch response, if an ID was specified in the patch.\n\tPatchId              *string `json:\"patch_id,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityPatchResponse IdentityPatchResponse\n\n// NewIdentityPatchResponse instantiates a new IdentityPatchResponse object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityPatchResponse() *IdentityPatchResponse {\n\tthis := IdentityPatchResponse{}\n\treturn &this\n}\n\n// NewIdentityPatchResponseWithDefaults instantiates a new IdentityPatchResponse object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityPatchResponseWithDefaults() *IdentityPatchResponse {\n\tthis := IdentityPatchResponse{}\n\treturn &this\n}\n\n// GetAction returns the Action field value if set, zero value otherwise.\nfunc (o *IdentityPatchResponse) GetAction() string {\n\tif o == nil || IsNil(o.Action) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatchResponse) GetActionOk() (*string, bool) {\n\tif o == nil || IsNil(o.Action) {\n\t\treturn nil, false\n\t}\n\treturn o.Action, true\n}\n\n// HasAction returns a boolean if a field has been set.\nfunc (o *IdentityPatchResponse) HasAction() bool {\n\tif o != nil && !IsNil(o.Action) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAction gets a reference to the given string and assigns it to the Action field.\nfunc (o *IdentityPatchResponse) SetAction(v string) {\n\to.Action = &v\n}\n\n// GetError returns the Error field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *IdentityPatchResponse) GetError() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *IdentityPatchResponse) GetErrorOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn &o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *IdentityPatchResponse) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given interface{} and assigns it to the Error field.\nfunc (o *IdentityPatchResponse) SetError(v interface{}) {\n\to.Error = v\n}\n\n// GetIdentity returns the Identity field value if set, zero value otherwise.\nfunc (o *IdentityPatchResponse) GetIdentity() string {\n\tif o == nil || IsNil(o.Identity) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Identity\n}\n\n// GetIdentityOk returns a tuple with the Identity field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatchResponse) GetIdentityOk() (*string, bool) {\n\tif o == nil || IsNil(o.Identity) {\n\t\treturn nil, false\n\t}\n\treturn o.Identity, true\n}\n\n// HasIdentity returns a boolean if a field has been set.\nfunc (o *IdentityPatchResponse) HasIdentity() bool {\n\tif o != nil && !IsNil(o.Identity) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentity gets a reference to the given string and assigns it to the Identity field.\nfunc (o *IdentityPatchResponse) SetIdentity(v string) {\n\to.Identity = &v\n}\n\n// GetPatchId returns the PatchId field value if set, zero value otherwise.\nfunc (o *IdentityPatchResponse) GetPatchId() string {\n\tif o == nil || IsNil(o.PatchId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PatchId\n}\n\n// GetPatchIdOk returns a tuple with the PatchId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatchResponse) GetPatchIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.PatchId) {\n\t\treturn nil, false\n\t}\n\treturn o.PatchId, true\n}\n\n// HasPatchId returns a boolean if a field has been set.\nfunc (o *IdentityPatchResponse) HasPatchId() bool {\n\tif o != nil && !IsNil(o.PatchId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPatchId gets a reference to the given string and assigns it to the PatchId field.\nfunc (o *IdentityPatchResponse) SetPatchId(v string) {\n\to.PatchId = &v\n}\n\nfunc (o IdentityPatchResponse) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityPatchResponse) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Action) {\n\t\ttoSerialize[\"action\"] = o.Action\n\t}\n\tif o.Error != nil {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.Identity) {\n\t\ttoSerialize[\"identity\"] = o.Identity\n\t}\n\tif !IsNil(o.PatchId) {\n\t\ttoSerialize[\"patch_id\"] = o.PatchId\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityPatchResponse) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityPatchResponse := _IdentityPatchResponse{}\n\n\terr = json.Unmarshal(data, &varIdentityPatchResponse)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityPatchResponse(varIdentityPatchResponse)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"identity\")\n\t\tdelete(additionalProperties, \"patch_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityPatchResponse struct {\n\tvalue *IdentityPatchResponse\n\tisSet bool\n}\n\nfunc (v NullableIdentityPatchResponse) Get() *IdentityPatchResponse {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityPatchResponse) Set(val *IdentityPatchResponse) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityPatchResponse) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityPatchResponse) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityPatchResponse(val *IdentityPatchResponse) *NullableIdentityPatchResponse {\n\treturn &NullableIdentityPatchResponse{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityPatchResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityPatchResponse) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_schema_container.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IdentitySchemaContainer type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentitySchemaContainer{}\n\n// IdentitySchemaContainer An Identity JSON Schema Container\ntype IdentitySchemaContainer struct {\n\t// The ID of the Identity JSON Schema\n\tId string `json:\"id\"`\n\t// The actual Identity JSON Schema\n\tSchema               map[string]interface{} `json:\"schema\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentitySchemaContainer IdentitySchemaContainer\n\n// NewIdentitySchemaContainer instantiates a new IdentitySchemaContainer object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentitySchemaContainer(id string, schema map[string]interface{}) *IdentitySchemaContainer {\n\tthis := IdentitySchemaContainer{}\n\tthis.Id = id\n\tthis.Schema = schema\n\treturn &this\n}\n\n// NewIdentitySchemaContainerWithDefaults instantiates a new IdentitySchemaContainer object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentitySchemaContainerWithDefaults() *IdentitySchemaContainer {\n\tthis := IdentitySchemaContainer{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *IdentitySchemaContainer) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentitySchemaContainer) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *IdentitySchemaContainer) SetId(v string) {\n\to.Id = v\n}\n\n// GetSchema returns the Schema field value\nfunc (o *IdentitySchemaContainer) GetSchema() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Schema\n}\n\n// GetSchemaOk returns a tuple with the Schema field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentitySchemaContainer) GetSchemaOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Schema, true\n}\n\n// SetSchema sets field value\nfunc (o *IdentitySchemaContainer) SetSchema(v map[string]interface{}) {\n\to.Schema = v\n}\n\nfunc (o IdentitySchemaContainer) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentitySchemaContainer) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"schema\"] = o.Schema\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentitySchemaContainer) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"schema\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIdentitySchemaContainer := _IdentitySchemaContainer{}\n\n\terr = json.Unmarshal(data, &varIdentitySchemaContainer)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentitySchemaContainer(varIdentitySchemaContainer)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"schema\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentitySchemaContainer struct {\n\tvalue *IdentitySchemaContainer\n\tisSet bool\n}\n\nfunc (v NullableIdentitySchemaContainer) Get() *IdentitySchemaContainer {\n\treturn v.value\n}\n\nfunc (v *NullableIdentitySchemaContainer) Set(val *IdentitySchemaContainer) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentitySchemaContainer) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentitySchemaContainer) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentitySchemaContainer(val *IdentitySchemaContainer) *NullableIdentitySchemaContainer {\n\treturn &NullableIdentitySchemaContainer{value: val, isSet: true}\n}\n\nfunc (v NullableIdentitySchemaContainer) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentitySchemaContainer) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_with_credentials.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentials type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentials{}\n\n// IdentityWithCredentials Create Identity and Import Credentials\ntype IdentityWithCredentials struct {\n\tOidc                 *IdentityWithCredentialsOidc     `json:\"oidc,omitempty\"`\n\tPassword             *IdentityWithCredentialsPassword `json:\"password,omitempty\"`\n\tSaml                 *IdentityWithCredentialsSaml     `json:\"saml,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentials IdentityWithCredentials\n\n// NewIdentityWithCredentials instantiates a new IdentityWithCredentials object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentials() *IdentityWithCredentials {\n\tthis := IdentityWithCredentials{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsWithDefaults instantiates a new IdentityWithCredentials object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsWithDefaults() *IdentityWithCredentials {\n\tthis := IdentityWithCredentials{}\n\treturn &this\n}\n\n// GetOidc returns the Oidc field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentials) GetOidc() IdentityWithCredentialsOidc {\n\tif o == nil || IsNil(o.Oidc) {\n\t\tvar ret IdentityWithCredentialsOidc\n\t\treturn ret\n\t}\n\treturn *o.Oidc\n}\n\n// GetOidcOk returns a tuple with the Oidc field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentials) GetOidcOk() (*IdentityWithCredentialsOidc, bool) {\n\tif o == nil || IsNil(o.Oidc) {\n\t\treturn nil, false\n\t}\n\treturn o.Oidc, true\n}\n\n// HasOidc returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentials) HasOidc() bool {\n\tif o != nil && !IsNil(o.Oidc) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOidc gets a reference to the given IdentityWithCredentialsOidc and assigns it to the Oidc field.\nfunc (o *IdentityWithCredentials) SetOidc(v IdentityWithCredentialsOidc) {\n\to.Oidc = &v\n}\n\n// GetPassword returns the Password field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentials) GetPassword() IdentityWithCredentialsPassword {\n\tif o == nil || IsNil(o.Password) {\n\t\tvar ret IdentityWithCredentialsPassword\n\t\treturn ret\n\t}\n\treturn *o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentials) GetPasswordOk() (*IdentityWithCredentialsPassword, bool) {\n\tif o == nil || IsNil(o.Password) {\n\t\treturn nil, false\n\t}\n\treturn o.Password, true\n}\n\n// HasPassword returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentials) HasPassword() bool {\n\tif o != nil && !IsNil(o.Password) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPassword gets a reference to the given IdentityWithCredentialsPassword and assigns it to the Password field.\nfunc (o *IdentityWithCredentials) SetPassword(v IdentityWithCredentialsPassword) {\n\to.Password = &v\n}\n\n// GetSaml returns the Saml field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentials) GetSaml() IdentityWithCredentialsSaml {\n\tif o == nil || IsNil(o.Saml) {\n\t\tvar ret IdentityWithCredentialsSaml\n\t\treturn ret\n\t}\n\treturn *o.Saml\n}\n\n// GetSamlOk returns a tuple with the Saml field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentials) GetSamlOk() (*IdentityWithCredentialsSaml, bool) {\n\tif o == nil || IsNil(o.Saml) {\n\t\treturn nil, false\n\t}\n\treturn o.Saml, true\n}\n\n// HasSaml returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentials) HasSaml() bool {\n\tif o != nil && !IsNil(o.Saml) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSaml gets a reference to the given IdentityWithCredentialsSaml and assigns it to the Saml field.\nfunc (o *IdentityWithCredentials) SetSaml(v IdentityWithCredentialsSaml) {\n\to.Saml = &v\n}\n\nfunc (o IdentityWithCredentials) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentials) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Oidc) {\n\t\ttoSerialize[\"oidc\"] = o.Oidc\n\t}\n\tif !IsNil(o.Password) {\n\t\ttoSerialize[\"password\"] = o.Password\n\t}\n\tif !IsNil(o.Saml) {\n\t\ttoSerialize[\"saml\"] = o.Saml\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentials) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentials := _IdentityWithCredentials{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentials)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentials(varIdentityWithCredentials)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"oidc\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"saml\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentials struct {\n\tvalue *IdentityWithCredentials\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentials) Get() *IdentityWithCredentials {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentials) Set(val *IdentityWithCredentials) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentials) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentials) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentials(val *IdentityWithCredentials) *NullableIdentityWithCredentials {\n\treturn &NullableIdentityWithCredentials{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentials) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentials) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_with_credentials_oidc.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsOidc type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsOidc{}\n\n// IdentityWithCredentialsOidc Create Identity and Import Social Sign In Credentials\ntype IdentityWithCredentialsOidc struct {\n\tConfig               *IdentityWithCredentialsOidcConfig `json:\"config,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsOidc IdentityWithCredentialsOidc\n\n// NewIdentityWithCredentialsOidc instantiates a new IdentityWithCredentialsOidc object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsOidc() *IdentityWithCredentialsOidc {\n\tthis := IdentityWithCredentialsOidc{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsOidcWithDefaults instantiates a new IdentityWithCredentialsOidc object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsOidcWithDefaults() *IdentityWithCredentialsOidc {\n\tthis := IdentityWithCredentialsOidc{}\n\treturn &this\n}\n\n// GetConfig returns the Config field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsOidc) GetConfig() IdentityWithCredentialsOidcConfig {\n\tif o == nil || IsNil(o.Config) {\n\t\tvar ret IdentityWithCredentialsOidcConfig\n\t\treturn ret\n\t}\n\treturn *o.Config\n}\n\n// GetConfigOk returns a tuple with the Config field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidc) GetConfigOk() (*IdentityWithCredentialsOidcConfig, bool) {\n\tif o == nil || IsNil(o.Config) {\n\t\treturn nil, false\n\t}\n\treturn o.Config, true\n}\n\n// HasConfig returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsOidc) HasConfig() bool {\n\tif o != nil && !IsNil(o.Config) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfig gets a reference to the given IdentityWithCredentialsOidcConfig and assigns it to the Config field.\nfunc (o *IdentityWithCredentialsOidc) SetConfig(v IdentityWithCredentialsOidcConfig) {\n\to.Config = &v\n}\n\nfunc (o IdentityWithCredentialsOidc) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsOidc) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Config) {\n\t\ttoSerialize[\"config\"] = o.Config\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsOidc) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsOidc := _IdentityWithCredentialsOidc{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsOidc)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsOidc(varIdentityWithCredentialsOidc)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"config\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsOidc struct {\n\tvalue *IdentityWithCredentialsOidc\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsOidc) Get() *IdentityWithCredentialsOidc {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsOidc) Set(val *IdentityWithCredentialsOidc) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsOidc) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsOidc) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsOidc(val *IdentityWithCredentialsOidc) *NullableIdentityWithCredentialsOidc {\n\treturn &NullableIdentityWithCredentialsOidc{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsOidc) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsOidc) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_with_credentials_oidc_config.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsOidcConfig type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsOidcConfig{}\n\n// IdentityWithCredentialsOidcConfig struct for IdentityWithCredentialsOidcConfig\ntype IdentityWithCredentialsOidcConfig struct {\n\t// A list of OpenID Connect Providers\n\tProviders            []IdentityWithCredentialsOidcConfigProvider `json:\"providers,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsOidcConfig IdentityWithCredentialsOidcConfig\n\n// NewIdentityWithCredentialsOidcConfig instantiates a new IdentityWithCredentialsOidcConfig object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsOidcConfig() *IdentityWithCredentialsOidcConfig {\n\tthis := IdentityWithCredentialsOidcConfig{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsOidcConfigWithDefaults instantiates a new IdentityWithCredentialsOidcConfig object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsOidcConfigWithDefaults() *IdentityWithCredentialsOidcConfig {\n\tthis := IdentityWithCredentialsOidcConfig{}\n\treturn &this\n}\n\n// GetProviders returns the Providers field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsOidcConfig) GetProviders() []IdentityWithCredentialsOidcConfigProvider {\n\tif o == nil || IsNil(o.Providers) {\n\t\tvar ret []IdentityWithCredentialsOidcConfigProvider\n\t\treturn ret\n\t}\n\treturn o.Providers\n}\n\n// GetProvidersOk returns a tuple with the Providers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidcConfig) GetProvidersOk() ([]IdentityWithCredentialsOidcConfigProvider, bool) {\n\tif o == nil || IsNil(o.Providers) {\n\t\treturn nil, false\n\t}\n\treturn o.Providers, true\n}\n\n// HasProviders returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsOidcConfig) HasProviders() bool {\n\tif o != nil && !IsNil(o.Providers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProviders gets a reference to the given []IdentityWithCredentialsOidcConfigProvider and assigns it to the Providers field.\nfunc (o *IdentityWithCredentialsOidcConfig) SetProviders(v []IdentityWithCredentialsOidcConfigProvider) {\n\to.Providers = v\n}\n\nfunc (o IdentityWithCredentialsOidcConfig) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsOidcConfig) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Providers) {\n\t\ttoSerialize[\"providers\"] = o.Providers\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsOidcConfig) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsOidcConfig := _IdentityWithCredentialsOidcConfig{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsOidcConfig)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsOidcConfig(varIdentityWithCredentialsOidcConfig)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"providers\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsOidcConfig struct {\n\tvalue *IdentityWithCredentialsOidcConfig\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfig) Get() *IdentityWithCredentialsOidcConfig {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfig) Set(val *IdentityWithCredentialsOidcConfig) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfig) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfig) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsOidcConfig(val *IdentityWithCredentialsOidcConfig) *NullableIdentityWithCredentialsOidcConfig {\n\treturn &NullableIdentityWithCredentialsOidcConfig{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfig) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfig) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_with_credentials_oidc_config_provider.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IdentityWithCredentialsOidcConfigProvider type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsOidcConfigProvider{}\n\n// IdentityWithCredentialsOidcConfigProvider Create Identity and Import Social Sign In Credentials Configuration\ntype IdentityWithCredentialsOidcConfigProvider struct {\n\tOrganization NullableString `json:\"organization,omitempty\"`\n\t// The OpenID Connect provider to link the subject to. Usually something like `google` or `github`.\n\tProvider string `json:\"provider\"`\n\t// The subject (`sub`) of the OpenID Connect connection. Usually the `sub` field of the ID Token.\n\tSubject string `json:\"subject\"`\n\t// If set, this credential allows the user to sign in using the OpenID Connect provider without setting the subject first.\n\tUseAutoLink          *bool `json:\"use_auto_link,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsOidcConfigProvider IdentityWithCredentialsOidcConfigProvider\n\n// NewIdentityWithCredentialsOidcConfigProvider instantiates a new IdentityWithCredentialsOidcConfigProvider object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsOidcConfigProvider(provider string, subject string) *IdentityWithCredentialsOidcConfigProvider {\n\tthis := IdentityWithCredentialsOidcConfigProvider{}\n\tthis.Provider = provider\n\tthis.Subject = subject\n\treturn &this\n}\n\n// NewIdentityWithCredentialsOidcConfigProviderWithDefaults instantiates a new IdentityWithCredentialsOidcConfigProvider object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsOidcConfigProviderWithDefaults() *IdentityWithCredentialsOidcConfigProvider {\n\tthis := IdentityWithCredentialsOidcConfigProvider{}\n\treturn &this\n}\n\n// GetOrganization returns the Organization field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetOrganization() string {\n\tif o == nil || IsNil(o.Organization.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Organization.Get()\n}\n\n// GetOrganizationOk returns a tuple with the Organization field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetOrganizationOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.Organization.Get(), o.Organization.IsSet()\n}\n\n// HasOrganization returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) HasOrganization() bool {\n\tif o != nil && o.Organization.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganization gets a reference to the given NullableString and assigns it to the Organization field.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetOrganization(v string) {\n\to.Organization.Set(&v)\n}\n\n// SetOrganizationNil sets the value for Organization to be an explicit nil\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetOrganizationNil() {\n\to.Organization.Set(nil)\n}\n\n// UnsetOrganization ensures that no value is present for Organization, not even an explicit nil\nfunc (o *IdentityWithCredentialsOidcConfigProvider) UnsetOrganization() {\n\to.Organization.Unset()\n}\n\n// GetProvider returns the Provider field value\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetSubject returns the Subject field value\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetSubject() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetSubjectOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Subject, true\n}\n\n// SetSubject sets field value\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetSubject(v string) {\n\to.Subject = v\n}\n\n// GetUseAutoLink returns the UseAutoLink field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetUseAutoLink() bool {\n\tif o == nil || IsNil(o.UseAutoLink) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.UseAutoLink\n}\n\n// GetUseAutoLinkOk returns a tuple with the UseAutoLink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetUseAutoLinkOk() (*bool, bool) {\n\tif o == nil || IsNil(o.UseAutoLink) {\n\t\treturn nil, false\n\t}\n\treturn o.UseAutoLink, true\n}\n\n// HasUseAutoLink returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) HasUseAutoLink() bool {\n\tif o != nil && !IsNil(o.UseAutoLink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUseAutoLink gets a reference to the given bool and assigns it to the UseAutoLink field.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetUseAutoLink(v bool) {\n\to.UseAutoLink = &v\n}\n\nfunc (o IdentityWithCredentialsOidcConfigProvider) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsOidcConfigProvider) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif o.Organization.IsSet() {\n\t\ttoSerialize[\"organization\"] = o.Organization.Get()\n\t}\n\ttoSerialize[\"provider\"] = o.Provider\n\ttoSerialize[\"subject\"] = o.Subject\n\tif !IsNil(o.UseAutoLink) {\n\t\ttoSerialize[\"use_auto_link\"] = o.UseAutoLink\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsOidcConfigProvider) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"provider\",\n\t\t\"subject\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIdentityWithCredentialsOidcConfigProvider := _IdentityWithCredentialsOidcConfigProvider{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsOidcConfigProvider)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsOidcConfigProvider(varIdentityWithCredentialsOidcConfigProvider)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"organization\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\tdelete(additionalProperties, \"use_auto_link\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsOidcConfigProvider struct {\n\tvalue *IdentityWithCredentialsOidcConfigProvider\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfigProvider) Get() *IdentityWithCredentialsOidcConfigProvider {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfigProvider) Set(val *IdentityWithCredentialsOidcConfigProvider) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfigProvider) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfigProvider) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsOidcConfigProvider(val *IdentityWithCredentialsOidcConfigProvider) *NullableIdentityWithCredentialsOidcConfigProvider {\n\treturn &NullableIdentityWithCredentialsOidcConfigProvider{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfigProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfigProvider) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_with_credentials_password.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsPassword type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsPassword{}\n\n// IdentityWithCredentialsPassword Create Identity and Import Password Credentials\ntype IdentityWithCredentialsPassword struct {\n\tConfig               *IdentityWithCredentialsPasswordConfig `json:\"config,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsPassword IdentityWithCredentialsPassword\n\n// NewIdentityWithCredentialsPassword instantiates a new IdentityWithCredentialsPassword object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsPassword() *IdentityWithCredentialsPassword {\n\tthis := IdentityWithCredentialsPassword{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsPasswordWithDefaults instantiates a new IdentityWithCredentialsPassword object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsPasswordWithDefaults() *IdentityWithCredentialsPassword {\n\tthis := IdentityWithCredentialsPassword{}\n\treturn &this\n}\n\n// GetConfig returns the Config field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsPassword) GetConfig() IdentityWithCredentialsPasswordConfig {\n\tif o == nil || IsNil(o.Config) {\n\t\tvar ret IdentityWithCredentialsPasswordConfig\n\t\treturn ret\n\t}\n\treturn *o.Config\n}\n\n// GetConfigOk returns a tuple with the Config field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsPassword) GetConfigOk() (*IdentityWithCredentialsPasswordConfig, bool) {\n\tif o == nil || IsNil(o.Config) {\n\t\treturn nil, false\n\t}\n\treturn o.Config, true\n}\n\n// HasConfig returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsPassword) HasConfig() bool {\n\tif o != nil && !IsNil(o.Config) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfig gets a reference to the given IdentityWithCredentialsPasswordConfig and assigns it to the Config field.\nfunc (o *IdentityWithCredentialsPassword) SetConfig(v IdentityWithCredentialsPasswordConfig) {\n\to.Config = &v\n}\n\nfunc (o IdentityWithCredentialsPassword) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsPassword) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Config) {\n\t\ttoSerialize[\"config\"] = o.Config\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsPassword) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsPassword := _IdentityWithCredentialsPassword{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsPassword)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsPassword(varIdentityWithCredentialsPassword)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"config\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsPassword struct {\n\tvalue *IdentityWithCredentialsPassword\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsPassword) Get() *IdentityWithCredentialsPassword {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsPassword) Set(val *IdentityWithCredentialsPassword) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsPassword) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsPassword) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsPassword(val *IdentityWithCredentialsPassword) *NullableIdentityWithCredentialsPassword {\n\treturn &NullableIdentityWithCredentialsPassword{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsPassword) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsPassword) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_with_credentials_password_config.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsPasswordConfig type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsPasswordConfig{}\n\n// IdentityWithCredentialsPasswordConfig Create Identity and Import Password Credentials Configuration\ntype IdentityWithCredentialsPasswordConfig struct {\n\t// The hashed password in [PHC format](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities#hashed-passwords)\n\tHashedPassword *string `json:\"hashed_password,omitempty\"`\n\t// The password in plain text if no hash is available.\n\tPassword *string `json:\"password,omitempty\"`\n\t// If set to true, the password will be migrated using the password migration hook.\n\tUsePasswordMigrationHook *bool `json:\"use_password_migration_hook,omitempty\"`\n\tAdditionalProperties     map[string]interface{}\n}\n\ntype _IdentityWithCredentialsPasswordConfig IdentityWithCredentialsPasswordConfig\n\n// NewIdentityWithCredentialsPasswordConfig instantiates a new IdentityWithCredentialsPasswordConfig object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsPasswordConfig() *IdentityWithCredentialsPasswordConfig {\n\tthis := IdentityWithCredentialsPasswordConfig{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsPasswordConfigWithDefaults instantiates a new IdentityWithCredentialsPasswordConfig object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsPasswordConfigWithDefaults() *IdentityWithCredentialsPasswordConfig {\n\tthis := IdentityWithCredentialsPasswordConfig{}\n\treturn &this\n}\n\n// GetHashedPassword returns the HashedPassword field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetHashedPassword() string {\n\tif o == nil || IsNil(o.HashedPassword) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.HashedPassword\n}\n\n// GetHashedPasswordOk returns a tuple with the HashedPassword field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetHashedPasswordOk() (*string, bool) {\n\tif o == nil || IsNil(o.HashedPassword) {\n\t\treturn nil, false\n\t}\n\treturn o.HashedPassword, true\n}\n\n// HasHashedPassword returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) HasHashedPassword() bool {\n\tif o != nil && !IsNil(o.HashedPassword) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetHashedPassword gets a reference to the given string and assigns it to the HashedPassword field.\nfunc (o *IdentityWithCredentialsPasswordConfig) SetHashedPassword(v string) {\n\to.HashedPassword = &v\n}\n\n// GetPassword returns the Password field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetPassword() string {\n\tif o == nil || IsNil(o.Password) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetPasswordOk() (*string, bool) {\n\tif o == nil || IsNil(o.Password) {\n\t\treturn nil, false\n\t}\n\treturn o.Password, true\n}\n\n// HasPassword returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) HasPassword() bool {\n\tif o != nil && !IsNil(o.Password) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPassword gets a reference to the given string and assigns it to the Password field.\nfunc (o *IdentityWithCredentialsPasswordConfig) SetPassword(v string) {\n\to.Password = &v\n}\n\n// GetUsePasswordMigrationHook returns the UsePasswordMigrationHook field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetUsePasswordMigrationHook() bool {\n\tif o == nil || IsNil(o.UsePasswordMigrationHook) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.UsePasswordMigrationHook\n}\n\n// GetUsePasswordMigrationHookOk returns a tuple with the UsePasswordMigrationHook field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetUsePasswordMigrationHookOk() (*bool, bool) {\n\tif o == nil || IsNil(o.UsePasswordMigrationHook) {\n\t\treturn nil, false\n\t}\n\treturn o.UsePasswordMigrationHook, true\n}\n\n// HasUsePasswordMigrationHook returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) HasUsePasswordMigrationHook() bool {\n\tif o != nil && !IsNil(o.UsePasswordMigrationHook) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUsePasswordMigrationHook gets a reference to the given bool and assigns it to the UsePasswordMigrationHook field.\nfunc (o *IdentityWithCredentialsPasswordConfig) SetUsePasswordMigrationHook(v bool) {\n\to.UsePasswordMigrationHook = &v\n}\n\nfunc (o IdentityWithCredentialsPasswordConfig) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsPasswordConfig) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.HashedPassword) {\n\t\ttoSerialize[\"hashed_password\"] = o.HashedPassword\n\t}\n\tif !IsNil(o.Password) {\n\t\ttoSerialize[\"password\"] = o.Password\n\t}\n\tif !IsNil(o.UsePasswordMigrationHook) {\n\t\ttoSerialize[\"use_password_migration_hook\"] = o.UsePasswordMigrationHook\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsPasswordConfig) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsPasswordConfig := _IdentityWithCredentialsPasswordConfig{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsPasswordConfig)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsPasswordConfig(varIdentityWithCredentialsPasswordConfig)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"hashed_password\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"use_password_migration_hook\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsPasswordConfig struct {\n\tvalue *IdentityWithCredentialsPasswordConfig\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsPasswordConfig) Get() *IdentityWithCredentialsPasswordConfig {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsPasswordConfig) Set(val *IdentityWithCredentialsPasswordConfig) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsPasswordConfig) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsPasswordConfig) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsPasswordConfig(val *IdentityWithCredentialsPasswordConfig) *NullableIdentityWithCredentialsPasswordConfig {\n\treturn &NullableIdentityWithCredentialsPasswordConfig{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsPasswordConfig) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsPasswordConfig) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_with_credentials_saml.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsSaml type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsSaml{}\n\n// IdentityWithCredentialsSaml Payload to import SAML credentials\ntype IdentityWithCredentialsSaml struct {\n\tConfig               *IdentityWithCredentialsSamlConfig `json:\"config,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsSaml IdentityWithCredentialsSaml\n\n// NewIdentityWithCredentialsSaml instantiates a new IdentityWithCredentialsSaml object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsSaml() *IdentityWithCredentialsSaml {\n\tthis := IdentityWithCredentialsSaml{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsSamlWithDefaults instantiates a new IdentityWithCredentialsSaml object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsSamlWithDefaults() *IdentityWithCredentialsSaml {\n\tthis := IdentityWithCredentialsSaml{}\n\treturn &this\n}\n\n// GetConfig returns the Config field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsSaml) GetConfig() IdentityWithCredentialsSamlConfig {\n\tif o == nil || IsNil(o.Config) {\n\t\tvar ret IdentityWithCredentialsSamlConfig\n\t\treturn ret\n\t}\n\treturn *o.Config\n}\n\n// GetConfigOk returns a tuple with the Config field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsSaml) GetConfigOk() (*IdentityWithCredentialsSamlConfig, bool) {\n\tif o == nil || IsNil(o.Config) {\n\t\treturn nil, false\n\t}\n\treturn o.Config, true\n}\n\n// HasConfig returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsSaml) HasConfig() bool {\n\tif o != nil && !IsNil(o.Config) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfig gets a reference to the given IdentityWithCredentialsSamlConfig and assigns it to the Config field.\nfunc (o *IdentityWithCredentialsSaml) SetConfig(v IdentityWithCredentialsSamlConfig) {\n\to.Config = &v\n}\n\nfunc (o IdentityWithCredentialsSaml) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsSaml) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Config) {\n\t\ttoSerialize[\"config\"] = o.Config\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsSaml) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsSaml := _IdentityWithCredentialsSaml{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsSaml)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsSaml(varIdentityWithCredentialsSaml)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"config\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsSaml struct {\n\tvalue *IdentityWithCredentialsSaml\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsSaml) Get() *IdentityWithCredentialsSaml {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsSaml) Set(val *IdentityWithCredentialsSaml) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsSaml) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsSaml) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsSaml(val *IdentityWithCredentialsSaml) *NullableIdentityWithCredentialsSaml {\n\treturn &NullableIdentityWithCredentialsSaml{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsSaml) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsSaml) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_with_credentials_saml_config.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsSamlConfig type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsSamlConfig{}\n\n// IdentityWithCredentialsSamlConfig Payload of SAML providers\ntype IdentityWithCredentialsSamlConfig struct {\n\t// A list of SAML Providers\n\tProviders            []IdentityWithCredentialsSamlConfigProvider `json:\"providers,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsSamlConfig IdentityWithCredentialsSamlConfig\n\n// NewIdentityWithCredentialsSamlConfig instantiates a new IdentityWithCredentialsSamlConfig object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsSamlConfig() *IdentityWithCredentialsSamlConfig {\n\tthis := IdentityWithCredentialsSamlConfig{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsSamlConfigWithDefaults instantiates a new IdentityWithCredentialsSamlConfig object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsSamlConfigWithDefaults() *IdentityWithCredentialsSamlConfig {\n\tthis := IdentityWithCredentialsSamlConfig{}\n\treturn &this\n}\n\n// GetProviders returns the Providers field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsSamlConfig) GetProviders() []IdentityWithCredentialsSamlConfigProvider {\n\tif o == nil || IsNil(o.Providers) {\n\t\tvar ret []IdentityWithCredentialsSamlConfigProvider\n\t\treturn ret\n\t}\n\treturn o.Providers\n}\n\n// GetProvidersOk returns a tuple with the Providers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsSamlConfig) GetProvidersOk() ([]IdentityWithCredentialsSamlConfigProvider, bool) {\n\tif o == nil || IsNil(o.Providers) {\n\t\treturn nil, false\n\t}\n\treturn o.Providers, true\n}\n\n// HasProviders returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsSamlConfig) HasProviders() bool {\n\tif o != nil && !IsNil(o.Providers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProviders gets a reference to the given []IdentityWithCredentialsSamlConfigProvider and assigns it to the Providers field.\nfunc (o *IdentityWithCredentialsSamlConfig) SetProviders(v []IdentityWithCredentialsSamlConfigProvider) {\n\to.Providers = v\n}\n\nfunc (o IdentityWithCredentialsSamlConfig) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsSamlConfig) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Providers) {\n\t\ttoSerialize[\"providers\"] = o.Providers\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsSamlConfig) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsSamlConfig := _IdentityWithCredentialsSamlConfig{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsSamlConfig)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsSamlConfig(varIdentityWithCredentialsSamlConfig)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"providers\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsSamlConfig struct {\n\tvalue *IdentityWithCredentialsSamlConfig\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfig) Get() *IdentityWithCredentialsSamlConfig {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfig) Set(val *IdentityWithCredentialsSamlConfig) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfig) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfig) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsSamlConfig(val *IdentityWithCredentialsSamlConfig) *NullableIdentityWithCredentialsSamlConfig {\n\treturn &NullableIdentityWithCredentialsSamlConfig{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfig) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfig) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_identity_with_credentials_saml_config_provider.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IdentityWithCredentialsSamlConfigProvider type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsSamlConfigProvider{}\n\n// IdentityWithCredentialsSamlConfigProvider Payload of specific SAML provider\ntype IdentityWithCredentialsSamlConfigProvider struct {\n\tOrganization NullableString `json:\"organization,omitempty\"`\n\t// The SAML provider to link the subject to.\n\tProvider string `json:\"provider\"`\n\t// The unique subject of the SAML connection. This value must be immutable at the source.\n\tSubject              string `json:\"subject\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsSamlConfigProvider IdentityWithCredentialsSamlConfigProvider\n\n// NewIdentityWithCredentialsSamlConfigProvider instantiates a new IdentityWithCredentialsSamlConfigProvider object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsSamlConfigProvider(provider string, subject string) *IdentityWithCredentialsSamlConfigProvider {\n\tthis := IdentityWithCredentialsSamlConfigProvider{}\n\tthis.Provider = provider\n\tthis.Subject = subject\n\treturn &this\n}\n\n// NewIdentityWithCredentialsSamlConfigProviderWithDefaults instantiates a new IdentityWithCredentialsSamlConfigProvider object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsSamlConfigProviderWithDefaults() *IdentityWithCredentialsSamlConfigProvider {\n\tthis := IdentityWithCredentialsSamlConfigProvider{}\n\treturn &this\n}\n\n// GetOrganization returns the Organization field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetOrganization() string {\n\tif o == nil || IsNil(o.Organization.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Organization.Get()\n}\n\n// GetOrganizationOk returns a tuple with the Organization field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetOrganizationOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.Organization.Get(), o.Organization.IsSet()\n}\n\n// HasOrganization returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsSamlConfigProvider) HasOrganization() bool {\n\tif o != nil && o.Organization.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganization gets a reference to the given NullableString and assigns it to the Organization field.\nfunc (o *IdentityWithCredentialsSamlConfigProvider) SetOrganization(v string) {\n\to.Organization.Set(&v)\n}\n\n// SetOrganizationNil sets the value for Organization to be an explicit nil\nfunc (o *IdentityWithCredentialsSamlConfigProvider) SetOrganizationNil() {\n\to.Organization.Set(nil)\n}\n\n// UnsetOrganization ensures that no value is present for Organization, not even an explicit nil\nfunc (o *IdentityWithCredentialsSamlConfigProvider) UnsetOrganization() {\n\to.Organization.Unset()\n}\n\n// GetProvider returns the Provider field value\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *IdentityWithCredentialsSamlConfigProvider) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetSubject returns the Subject field value\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetSubject() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetSubjectOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Subject, true\n}\n\n// SetSubject sets field value\nfunc (o *IdentityWithCredentialsSamlConfigProvider) SetSubject(v string) {\n\to.Subject = v\n}\n\nfunc (o IdentityWithCredentialsSamlConfigProvider) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsSamlConfigProvider) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif o.Organization.IsSet() {\n\t\ttoSerialize[\"organization\"] = o.Organization.Get()\n\t}\n\ttoSerialize[\"provider\"] = o.Provider\n\ttoSerialize[\"subject\"] = o.Subject\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsSamlConfigProvider) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"provider\",\n\t\t\"subject\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIdentityWithCredentialsSamlConfigProvider := _IdentityWithCredentialsSamlConfigProvider{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsSamlConfigProvider)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsSamlConfigProvider(varIdentityWithCredentialsSamlConfigProvider)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"organization\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsSamlConfigProvider struct {\n\tvalue *IdentityWithCredentialsSamlConfigProvider\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfigProvider) Get() *IdentityWithCredentialsSamlConfigProvider {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfigProvider) Set(val *IdentityWithCredentialsSamlConfigProvider) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfigProvider) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfigProvider) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsSamlConfigProvider(val *IdentityWithCredentialsSamlConfigProvider) *NullableIdentityWithCredentialsSamlConfigProvider {\n\treturn &NullableIdentityWithCredentialsSamlConfigProvider{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfigProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfigProvider) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_is_alive_200_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IsAlive200Response type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IsAlive200Response{}\n\n// IsAlive200Response struct for IsAlive200Response\ntype IsAlive200Response struct {\n\t// Always \\\"ok\\\".\n\tStatus               string `json:\"status\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IsAlive200Response IsAlive200Response\n\n// NewIsAlive200Response instantiates a new IsAlive200Response object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIsAlive200Response(status string) *IsAlive200Response {\n\tthis := IsAlive200Response{}\n\tthis.Status = status\n\treturn &this\n}\n\n// NewIsAlive200ResponseWithDefaults instantiates a new IsAlive200Response object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIsAlive200ResponseWithDefaults() *IsAlive200Response {\n\tthis := IsAlive200Response{}\n\treturn &this\n}\n\n// GetStatus returns the Status field value\nfunc (o *IsAlive200Response) GetStatus() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value\n// and a boolean to check if the value has been set.\nfunc (o *IsAlive200Response) GetStatusOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Status, true\n}\n\n// SetStatus sets field value\nfunc (o *IsAlive200Response) SetStatus(v string) {\n\to.Status = v\n}\n\nfunc (o IsAlive200Response) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IsAlive200Response) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"status\"] = o.Status\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IsAlive200Response) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"status\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIsAlive200Response := _IsAlive200Response{}\n\n\terr = json.Unmarshal(data, &varIsAlive200Response)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IsAlive200Response(varIsAlive200Response)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"status\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIsAlive200Response struct {\n\tvalue *IsAlive200Response\n\tisSet bool\n}\n\nfunc (v NullableIsAlive200Response) Get() *IsAlive200Response {\n\treturn v.value\n}\n\nfunc (v *NullableIsAlive200Response) Set(val *IsAlive200Response) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIsAlive200Response) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIsAlive200Response) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIsAlive200Response(val *IsAlive200Response) *NullableIsAlive200Response {\n\treturn &NullableIsAlive200Response{value: val, isSet: true}\n}\n\nfunc (v NullableIsAlive200Response) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIsAlive200Response) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_is_ready_503_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IsReady503Response type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IsReady503Response{}\n\n// IsReady503Response struct for IsReady503Response\ntype IsReady503Response struct {\n\t// Errors contains a list of errors that caused the not ready status.\n\tErrors               map[string]string `json:\"errors\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IsReady503Response IsReady503Response\n\n// NewIsReady503Response instantiates a new IsReady503Response object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIsReady503Response(errors map[string]string) *IsReady503Response {\n\tthis := IsReady503Response{}\n\tthis.Errors = errors\n\treturn &this\n}\n\n// NewIsReady503ResponseWithDefaults instantiates a new IsReady503Response object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIsReady503ResponseWithDefaults() *IsReady503Response {\n\tthis := IsReady503Response{}\n\treturn &this\n}\n\n// GetErrors returns the Errors field value\nfunc (o *IsReady503Response) GetErrors() map[string]string {\n\tif o == nil {\n\t\tvar ret map[string]string\n\t\treturn ret\n\t}\n\n\treturn o.Errors\n}\n\n// GetErrorsOk returns a tuple with the Errors field value\n// and a boolean to check if the value has been set.\nfunc (o *IsReady503Response) GetErrorsOk() (*map[string]string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Errors, true\n}\n\n// SetErrors sets field value\nfunc (o *IsReady503Response) SetErrors(v map[string]string) {\n\to.Errors = v\n}\n\nfunc (o IsReady503Response) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IsReady503Response) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"errors\"] = o.Errors\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IsReady503Response) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"errors\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIsReady503Response := _IsReady503Response{}\n\n\terr = json.Unmarshal(data, &varIsReady503Response)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IsReady503Response(varIsReady503Response)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"errors\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIsReady503Response struct {\n\tvalue *IsReady503Response\n\tisSet bool\n}\n\nfunc (v NullableIsReady503Response) Get() *IsReady503Response {\n\treturn v.value\n}\n\nfunc (v *NullableIsReady503Response) Set(val *IsReady503Response) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIsReady503Response) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIsReady503Response) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIsReady503Response(val *IsReady503Response) *NullableIsReady503Response {\n\treturn &NullableIsReady503Response{value: val, isSet: true}\n}\n\nfunc (v NullableIsReady503Response) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIsReady503Response) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_json_patch.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the JsonPatch type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &JsonPatch{}\n\n// JsonPatch A JSONPatch document as defined by RFC 6902\ntype JsonPatch struct {\n\t// This field is used together with operation \\\"move\\\" and uses JSON Pointer notation.  Learn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\n\tFrom *string `json:\"from,omitempty\"`\n\t// The operation to be performed. One of \\\"add\\\", \\\"remove\\\", \\\"replace\\\", \\\"move\\\", \\\"copy\\\", or \\\"test\\\".\n\tOp string `json:\"op\"`\n\t// The path to the target path. Uses JSON pointer notation.  Learn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\n\tPath string `json:\"path\"`\n\t// The value to be used within the operations.  Learn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\n\tValue                interface{} `json:\"value,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _JsonPatch JsonPatch\n\n// NewJsonPatch instantiates a new JsonPatch object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewJsonPatch(op string, path string) *JsonPatch {\n\tthis := JsonPatch{}\n\tthis.Op = op\n\tthis.Path = path\n\treturn &this\n}\n\n// NewJsonPatchWithDefaults instantiates a new JsonPatch object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewJsonPatchWithDefaults() *JsonPatch {\n\tthis := JsonPatch{}\n\treturn &this\n}\n\n// GetFrom returns the From field value if set, zero value otherwise.\nfunc (o *JsonPatch) GetFrom() string {\n\tif o == nil || IsNil(o.From) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.From\n}\n\n// GetFromOk returns a tuple with the From field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *JsonPatch) GetFromOk() (*string, bool) {\n\tif o == nil || IsNil(o.From) {\n\t\treturn nil, false\n\t}\n\treturn o.From, true\n}\n\n// HasFrom returns a boolean if a field has been set.\nfunc (o *JsonPatch) HasFrom() bool {\n\tif o != nil && !IsNil(o.From) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFrom gets a reference to the given string and assigns it to the From field.\nfunc (o *JsonPatch) SetFrom(v string) {\n\to.From = &v\n}\n\n// GetOp returns the Op field value\nfunc (o *JsonPatch) GetOp() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Op\n}\n\n// GetOpOk returns a tuple with the Op field value\n// and a boolean to check if the value has been set.\nfunc (o *JsonPatch) GetOpOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Op, true\n}\n\n// SetOp sets field value\nfunc (o *JsonPatch) SetOp(v string) {\n\to.Op = v\n}\n\n// GetPath returns the Path field value\nfunc (o *JsonPatch) GetPath() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Path\n}\n\n// GetPathOk returns a tuple with the Path field value\n// and a boolean to check if the value has been set.\nfunc (o *JsonPatch) GetPathOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Path, true\n}\n\n// SetPath sets field value\nfunc (o *JsonPatch) SetPath(v string) {\n\to.Path = v\n}\n\n// GetValue returns the Value field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *JsonPatch) GetValue() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Value\n}\n\n// GetValueOk returns a tuple with the Value field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *JsonPatch) GetValueOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Value) {\n\t\treturn nil, false\n\t}\n\treturn &o.Value, true\n}\n\n// HasValue returns a boolean if a field has been set.\nfunc (o *JsonPatch) HasValue() bool {\n\tif o != nil && !IsNil(o.Value) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetValue gets a reference to the given interface{} and assigns it to the Value field.\nfunc (o *JsonPatch) SetValue(v interface{}) {\n\to.Value = v\n}\n\nfunc (o JsonPatch) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o JsonPatch) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.From) {\n\t\ttoSerialize[\"from\"] = o.From\n\t}\n\ttoSerialize[\"op\"] = o.Op\n\ttoSerialize[\"path\"] = o.Path\n\tif o.Value != nil {\n\t\ttoSerialize[\"value\"] = o.Value\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *JsonPatch) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"op\",\n\t\t\"path\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarJsonPatch := _JsonPatch{}\n\n\terr = json.Unmarshal(data, &varJsonPatch)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = JsonPatch(varJsonPatch)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"from\")\n\t\tdelete(additionalProperties, \"op\")\n\t\tdelete(additionalProperties, \"path\")\n\t\tdelete(additionalProperties, \"value\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableJsonPatch struct {\n\tvalue *JsonPatch\n\tisSet bool\n}\n\nfunc (v NullableJsonPatch) Get() *JsonPatch {\n\treturn v.value\n}\n\nfunc (v *NullableJsonPatch) Set(val *JsonPatch) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableJsonPatch) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableJsonPatch) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableJsonPatch(val *JsonPatch) *NullableJsonPatch {\n\treturn &NullableJsonPatch{value: val, isSet: true}\n}\n\nfunc (v NullableJsonPatch) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableJsonPatch) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_login_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the LoginFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &LoginFlow{}\n\n// LoginFlow This object represents a login flow. A login flow is initiated at the \\\"Initiate Login API / Browser Flow\\\" endpoint by a client.  Once a login flow is completed successfully, a session cookie or session token will be issued.\ntype LoginFlow struct {\n\t// The active login method  If set contains the login method used. If the flow is new, it is unset. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\tActive *string `json:\"active,omitempty\"`\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in, a new flow has to be initiated.\n\tExpiresAt time.Time `json:\"expires_at\"`\n\t// ID represents the flow's unique ID. When performing the login flow, this represents the id in the login UI's query parameter: http://<selfservice.flows.login.ui_url>/?flow=<flow_id>\n\tId string `json:\"id\"`\n\t// IdentitySchema optionally holds the ID of the identity schema that is used for this flow. This value can be set by the user when creating the flow and should be retained when the flow is saved or converted to another flow.\n\tIdentitySchema *string `json:\"identity_schema,omitempty\"`\n\t// IssuedAt is the time (UTC) when the flow started.\n\tIssuedAt time.Time `json:\"issued_at\"`\n\t// Ory OAuth 2.0 Login Challenge.  This value is set using the `login_challenge` query parameter of the registration and login endpoints. If set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\n\tOauth2LoginChallenge *string             `json:\"oauth2_login_challenge,omitempty\"`\n\tOauth2LoginRequest   *OAuth2LoginRequest `json:\"oauth2_login_request,omitempty\"`\n\tOrganizationId       NullableString      `json:\"organization_id,omitempty\"`\n\t// Refresh stores whether this login flow should enforce re-authentication.\n\tRefresh *bool `json:\"refresh,omitempty\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl   string                       `json:\"request_url\"`\n\tRequestedAal *AuthenticatorAssuranceLevel `json:\"requested_aal,omitempty\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the login flow has been completed. This is only set if the client has requested a session token exchange code, and if the flow is of type \\\"api\\\", and only on creating the login flow.\n\tSessionTokenExchangeCode *string `json:\"session_token_exchange_code,omitempty\"`\n\t// State represents the state of this request:  choose_method: ask the user to choose a method to sign in with sent_email: the email has been sent to the user passed_challenge: the request was successful and the login challenge was passed.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the login to hooks and email templates\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType string      `json:\"type\"`\n\tUi   UiContainer `json:\"ui\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            *time.Time `json:\"updated_at,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _LoginFlow LoginFlow\n\n// NewLoginFlow instantiates a new LoginFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewLoginFlow(expiresAt time.Time, id string, issuedAt time.Time, requestUrl string, state interface{}, type_ string, ui UiContainer) *LoginFlow {\n\tthis := LoginFlow{}\n\tthis.ExpiresAt = expiresAt\n\tthis.Id = id\n\tthis.IssuedAt = issuedAt\n\tthis.RequestUrl = requestUrl\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewLoginFlowWithDefaults instantiates a new LoginFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewLoginFlowWithDefaults() *LoginFlow {\n\tthis := LoginFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *LoginFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *LoginFlow) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetExpiresAt returns the ExpiresAt field value\nfunc (o *LoginFlow) GetExpiresAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.ExpiresAt, true\n}\n\n// SetExpiresAt sets field value\nfunc (o *LoginFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = v\n}\n\n// GetId returns the Id field value\nfunc (o *LoginFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *LoginFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIdentitySchema returns the IdentitySchema field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetIdentitySchema() string {\n\tif o == nil || IsNil(o.IdentitySchema) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdentitySchema\n}\n\n// GetIdentitySchemaOk returns a tuple with the IdentitySchema field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetIdentitySchemaOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdentitySchema) {\n\t\treturn nil, false\n\t}\n\treturn o.IdentitySchema, true\n}\n\n// HasIdentitySchema returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasIdentitySchema() bool {\n\tif o != nil && !IsNil(o.IdentitySchema) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentitySchema gets a reference to the given string and assigns it to the IdentitySchema field.\nfunc (o *LoginFlow) SetIdentitySchema(v string) {\n\to.IdentitySchema = &v\n}\n\n// GetIssuedAt returns the IssuedAt field value\nfunc (o *LoginFlow) GetIssuedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IssuedAt, true\n}\n\n// SetIssuedAt sets field value\nfunc (o *LoginFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = v\n}\n\n// GetOauth2LoginChallenge returns the Oauth2LoginChallenge field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetOauth2LoginChallenge() string {\n\tif o == nil || IsNil(o.Oauth2LoginChallenge) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Oauth2LoginChallenge\n}\n\n// GetOauth2LoginChallengeOk returns a tuple with the Oauth2LoginChallenge field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetOauth2LoginChallengeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Oauth2LoginChallenge) {\n\t\treturn nil, false\n\t}\n\treturn o.Oauth2LoginChallenge, true\n}\n\n// HasOauth2LoginChallenge returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasOauth2LoginChallenge() bool {\n\tif o != nil && !IsNil(o.Oauth2LoginChallenge) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOauth2LoginChallenge gets a reference to the given string and assigns it to the Oauth2LoginChallenge field.\nfunc (o *LoginFlow) SetOauth2LoginChallenge(v string) {\n\to.Oauth2LoginChallenge = &v\n}\n\n// GetOauth2LoginRequest returns the Oauth2LoginRequest field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetOauth2LoginRequest() OAuth2LoginRequest {\n\tif o == nil || IsNil(o.Oauth2LoginRequest) {\n\t\tvar ret OAuth2LoginRequest\n\t\treturn ret\n\t}\n\treturn *o.Oauth2LoginRequest\n}\n\n// GetOauth2LoginRequestOk returns a tuple with the Oauth2LoginRequest field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetOauth2LoginRequestOk() (*OAuth2LoginRequest, bool) {\n\tif o == nil || IsNil(o.Oauth2LoginRequest) {\n\t\treturn nil, false\n\t}\n\treturn o.Oauth2LoginRequest, true\n}\n\n// HasOauth2LoginRequest returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasOauth2LoginRequest() bool {\n\tif o != nil && !IsNil(o.Oauth2LoginRequest) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOauth2LoginRequest gets a reference to the given OAuth2LoginRequest and assigns it to the Oauth2LoginRequest field.\nfunc (o *LoginFlow) SetOauth2LoginRequest(v OAuth2LoginRequest) {\n\to.Oauth2LoginRequest = &v\n}\n\n// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *LoginFlow) GetOrganizationId() string {\n\tif o == nil || IsNil(o.OrganizationId.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OrganizationId.Get()\n}\n\n// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *LoginFlow) GetOrganizationIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.OrganizationId.Get(), o.OrganizationId.IsSet()\n}\n\n// HasOrganizationId returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasOrganizationId() bool {\n\tif o != nil && o.OrganizationId.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganizationId gets a reference to the given NullableString and assigns it to the OrganizationId field.\nfunc (o *LoginFlow) SetOrganizationId(v string) {\n\to.OrganizationId.Set(&v)\n}\n\n// SetOrganizationIdNil sets the value for OrganizationId to be an explicit nil\nfunc (o *LoginFlow) SetOrganizationIdNil() {\n\to.OrganizationId.Set(nil)\n}\n\n// UnsetOrganizationId ensures that no value is present for OrganizationId, not even an explicit nil\nfunc (o *LoginFlow) UnsetOrganizationId() {\n\to.OrganizationId.Unset()\n}\n\n// GetRefresh returns the Refresh field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetRefresh() bool {\n\tif o == nil || IsNil(o.Refresh) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.Refresh\n}\n\n// GetRefreshOk returns a tuple with the Refresh field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetRefreshOk() (*bool, bool) {\n\tif o == nil || IsNil(o.Refresh) {\n\t\treturn nil, false\n\t}\n\treturn o.Refresh, true\n}\n\n// HasRefresh returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasRefresh() bool {\n\tif o != nil && !IsNil(o.Refresh) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRefresh gets a reference to the given bool and assigns it to the Refresh field.\nfunc (o *LoginFlow) SetRefresh(v bool) {\n\to.Refresh = &v\n}\n\n// GetRequestUrl returns the RequestUrl field value\nfunc (o *LoginFlow) GetRequestUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RequestUrl, true\n}\n\n// SetRequestUrl sets field value\nfunc (o *LoginFlow) SetRequestUrl(v string) {\n\to.RequestUrl = v\n}\n\n// GetRequestedAal returns the RequestedAal field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetRequestedAal() AuthenticatorAssuranceLevel {\n\tif o == nil || IsNil(o.RequestedAal) {\n\t\tvar ret AuthenticatorAssuranceLevel\n\t\treturn ret\n\t}\n\treturn *o.RequestedAal\n}\n\n// GetRequestedAalOk returns a tuple with the RequestedAal field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetRequestedAalOk() (*AuthenticatorAssuranceLevel, bool) {\n\tif o == nil || IsNil(o.RequestedAal) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestedAal, true\n}\n\n// HasRequestedAal returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasRequestedAal() bool {\n\tif o != nil && !IsNil(o.RequestedAal) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestedAal gets a reference to the given AuthenticatorAssuranceLevel and assigns it to the RequestedAal field.\nfunc (o *LoginFlow) SetRequestedAal(v AuthenticatorAssuranceLevel) {\n\to.RequestedAal = &v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *LoginFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetSessionTokenExchangeCode returns the SessionTokenExchangeCode field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetSessionTokenExchangeCode() string {\n\tif o == nil || IsNil(o.SessionTokenExchangeCode) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionTokenExchangeCode\n}\n\n// GetSessionTokenExchangeCodeOk returns a tuple with the SessionTokenExchangeCode field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetSessionTokenExchangeCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionTokenExchangeCode) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionTokenExchangeCode, true\n}\n\n// HasSessionTokenExchangeCode returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasSessionTokenExchangeCode() bool {\n\tif o != nil && !IsNil(o.SessionTokenExchangeCode) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionTokenExchangeCode gets a reference to the given string and assigns it to the SessionTokenExchangeCode field.\nfunc (o *LoginFlow) SetSessionTokenExchangeCode(v string) {\n\to.SessionTokenExchangeCode = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *LoginFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *LoginFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *LoginFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *LoginFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *LoginFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *LoginFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *LoginFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *LoginFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *LoginFlow) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\nfunc (o LoginFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o LoginFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.IdentitySchema) {\n\t\ttoSerialize[\"identity_schema\"] = o.IdentitySchema\n\t}\n\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\tif !IsNil(o.Oauth2LoginChallenge) {\n\t\ttoSerialize[\"oauth2_login_challenge\"] = o.Oauth2LoginChallenge\n\t}\n\tif !IsNil(o.Oauth2LoginRequest) {\n\t\ttoSerialize[\"oauth2_login_request\"] = o.Oauth2LoginRequest\n\t}\n\tif o.OrganizationId.IsSet() {\n\t\ttoSerialize[\"organization_id\"] = o.OrganizationId.Get()\n\t}\n\tif !IsNil(o.Refresh) {\n\t\ttoSerialize[\"refresh\"] = o.Refresh\n\t}\n\ttoSerialize[\"request_url\"] = o.RequestUrl\n\tif !IsNil(o.RequestedAal) {\n\t\ttoSerialize[\"requested_aal\"] = o.RequestedAal\n\t}\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif !IsNil(o.SessionTokenExchangeCode) {\n\t\ttoSerialize[\"session_token_exchange_code\"] = o.SessionTokenExchangeCode\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *LoginFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"expires_at\",\n\t\t\"id\",\n\t\t\"issued_at\",\n\t\t\"request_url\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarLoginFlow := _LoginFlow{}\n\n\terr = json.Unmarshal(data, &varLoginFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = LoginFlow(varLoginFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"identity_schema\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"oauth2_login_challenge\")\n\t\tdelete(additionalProperties, \"oauth2_login_request\")\n\t\tdelete(additionalProperties, \"organization_id\")\n\t\tdelete(additionalProperties, \"refresh\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"requested_aal\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"session_token_exchange_code\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableLoginFlow struct {\n\tvalue *LoginFlow\n\tisSet bool\n}\n\nfunc (v NullableLoginFlow) Get() *LoginFlow {\n\treturn v.value\n}\n\nfunc (v *NullableLoginFlow) Set(val *LoginFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableLoginFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableLoginFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableLoginFlow(val *LoginFlow) *NullableLoginFlow {\n\treturn &NullableLoginFlow{value: val, isSet: true}\n}\n\nfunc (v NullableLoginFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableLoginFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_login_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// LoginFlowState The experimental state represents the state of a login flow. This field is EXPERIMENTAL and subject to change!\ntype LoginFlowState string\n\n// List of loginFlowState\nconst (\n\tLOGINFLOWSTATE_CHOOSE_METHOD    LoginFlowState = \"choose_method\"\n\tLOGINFLOWSTATE_SENT_EMAIL       LoginFlowState = \"sent_email\"\n\tLOGINFLOWSTATE_PASSED_CHALLENGE LoginFlowState = \"passed_challenge\"\n)\n\n// All allowed values of LoginFlowState enum\nvar AllowedLoginFlowStateEnumValues = []LoginFlowState{\n\t\"choose_method\",\n\t\"sent_email\",\n\t\"passed_challenge\",\n}\n\nfunc (v *LoginFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := LoginFlowState(value)\n\tfor _, existing := range AllowedLoginFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid LoginFlowState\", value)\n}\n\n// NewLoginFlowStateFromValue returns a pointer to a valid LoginFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewLoginFlowStateFromValue(v string) (*LoginFlowState, error) {\n\tev := LoginFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for LoginFlowState: valid values are %v\", v, AllowedLoginFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v LoginFlowState) IsValid() bool {\n\tfor _, existing := range AllowedLoginFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to loginFlowState value\nfunc (v LoginFlowState) Ptr() *LoginFlowState {\n\treturn &v\n}\n\ntype NullableLoginFlowState struct {\n\tvalue *LoginFlowState\n\tisSet bool\n}\n\nfunc (v NullableLoginFlowState) Get() *LoginFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableLoginFlowState) Set(val *LoginFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableLoginFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableLoginFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableLoginFlowState(val *LoginFlowState) *NullableLoginFlowState {\n\treturn &NullableLoginFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableLoginFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableLoginFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_logout_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the LogoutFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &LogoutFlow{}\n\n// LogoutFlow Logout Flow\ntype LogoutFlow struct {\n\t// LogoutToken can be used to perform logout using AJAX.\n\tLogoutToken string `json:\"logout_token\"`\n\t// LogoutURL can be opened in a browser to sign the user out.  format: uri\n\tLogoutUrl            string `json:\"logout_url\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _LogoutFlow LogoutFlow\n\n// NewLogoutFlow instantiates a new LogoutFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewLogoutFlow(logoutToken string, logoutUrl string) *LogoutFlow {\n\tthis := LogoutFlow{}\n\tthis.LogoutToken = logoutToken\n\tthis.LogoutUrl = logoutUrl\n\treturn &this\n}\n\n// NewLogoutFlowWithDefaults instantiates a new LogoutFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewLogoutFlowWithDefaults() *LogoutFlow {\n\tthis := LogoutFlow{}\n\treturn &this\n}\n\n// GetLogoutToken returns the LogoutToken field value\nfunc (o *LogoutFlow) GetLogoutToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.LogoutToken\n}\n\n// GetLogoutTokenOk returns a tuple with the LogoutToken field value\n// and a boolean to check if the value has been set.\nfunc (o *LogoutFlow) GetLogoutTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.LogoutToken, true\n}\n\n// SetLogoutToken sets field value\nfunc (o *LogoutFlow) SetLogoutToken(v string) {\n\to.LogoutToken = v\n}\n\n// GetLogoutUrl returns the LogoutUrl field value\nfunc (o *LogoutFlow) GetLogoutUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.LogoutUrl\n}\n\n// GetLogoutUrlOk returns a tuple with the LogoutUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *LogoutFlow) GetLogoutUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.LogoutUrl, true\n}\n\n// SetLogoutUrl sets field value\nfunc (o *LogoutFlow) SetLogoutUrl(v string) {\n\to.LogoutUrl = v\n}\n\nfunc (o LogoutFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o LogoutFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"logout_token\"] = o.LogoutToken\n\ttoSerialize[\"logout_url\"] = o.LogoutUrl\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *LogoutFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"logout_token\",\n\t\t\"logout_url\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarLogoutFlow := _LogoutFlow{}\n\n\terr = json.Unmarshal(data, &varLogoutFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = LogoutFlow(varLogoutFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"logout_token\")\n\t\tdelete(additionalProperties, \"logout_url\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableLogoutFlow struct {\n\tvalue *LogoutFlow\n\tisSet bool\n}\n\nfunc (v NullableLogoutFlow) Get() *LogoutFlow {\n\treturn v.value\n}\n\nfunc (v *NullableLogoutFlow) Set(val *LogoutFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableLogoutFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableLogoutFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableLogoutFlow(val *LogoutFlow) *NullableLogoutFlow {\n\treturn &NullableLogoutFlow{value: val, isSet: true}\n}\n\nfunc (v NullableLogoutFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableLogoutFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_message.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the Message type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Message{}\n\n// Message struct for Message\ntype Message struct {\n\tBody    string  `json:\"body\"`\n\tChannel *string `json:\"channel,omitempty\"`\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"created_at\"`\n\t// Dispatches store information about the attempts of delivering a message May contain an error if any happened, or just the `success` state.\n\tDispatches []MessageDispatch    `json:\"dispatches,omitempty\"`\n\tId         string               `json:\"id\"`\n\tRecipient  string               `json:\"recipient\"`\n\tSendCount  int64                `json:\"send_count\"`\n\tStatus     CourierMessageStatus `json:\"status\"`\n\tSubject    string               `json:\"subject\"`\n\t//  recovery_invalid TypeRecoveryInvalid recovery_valid TypeRecoveryValid recovery_code_invalid TypeRecoveryCodeInvalid recovery_code_valid TypeRecoveryCodeValid verification_invalid TypeVerificationInvalid verification_valid TypeVerificationValid verification_code_invalid TypeVerificationCodeInvalid verification_code_valid TypeVerificationCodeValid stub TypeTestStub login_code_valid TypeLoginCodeValid registration_code_valid TypeRegistrationCodeValid\n\tTemplateType string             `json:\"template_type\"`\n\tType         CourierMessageType `json:\"type\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            time.Time `json:\"updated_at\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Message Message\n\n// NewMessage instantiates a new Message object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewMessage(body string, createdAt time.Time, id string, recipient string, sendCount int64, status CourierMessageStatus, subject string, templateType string, type_ CourierMessageType, updatedAt time.Time) *Message {\n\tthis := Message{}\n\tthis.Body = body\n\tthis.CreatedAt = createdAt\n\tthis.Id = id\n\tthis.Recipient = recipient\n\tthis.SendCount = sendCount\n\tthis.Status = status\n\tthis.Subject = subject\n\tthis.TemplateType = templateType\n\tthis.Type = type_\n\tthis.UpdatedAt = updatedAt\n\treturn &this\n}\n\n// NewMessageWithDefaults instantiates a new Message object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewMessageWithDefaults() *Message {\n\tthis := Message{}\n\treturn &this\n}\n\n// GetBody returns the Body field value\nfunc (o *Message) GetBody() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Body\n}\n\n// GetBodyOk returns a tuple with the Body field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetBodyOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Body, true\n}\n\n// SetBody sets field value\nfunc (o *Message) SetBody(v string) {\n\to.Body = v\n}\n\n// GetChannel returns the Channel field value if set, zero value otherwise.\nfunc (o *Message) GetChannel() string {\n\tif o == nil || IsNil(o.Channel) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Channel\n}\n\n// GetChannelOk returns a tuple with the Channel field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetChannelOk() (*string, bool) {\n\tif o == nil || IsNil(o.Channel) {\n\t\treturn nil, false\n\t}\n\treturn o.Channel, true\n}\n\n// HasChannel returns a boolean if a field has been set.\nfunc (o *Message) HasChannel() bool {\n\tif o != nil && !IsNil(o.Channel) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetChannel gets a reference to the given string and assigns it to the Channel field.\nfunc (o *Message) SetChannel(v string) {\n\to.Channel = &v\n}\n\n// GetCreatedAt returns the CreatedAt field value\nfunc (o *Message) GetCreatedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.CreatedAt, true\n}\n\n// SetCreatedAt sets field value\nfunc (o *Message) SetCreatedAt(v time.Time) {\n\to.CreatedAt = v\n}\n\n// GetDispatches returns the Dispatches field value if set, zero value otherwise.\nfunc (o *Message) GetDispatches() []MessageDispatch {\n\tif o == nil || IsNil(o.Dispatches) {\n\t\tvar ret []MessageDispatch\n\t\treturn ret\n\t}\n\treturn o.Dispatches\n}\n\n// GetDispatchesOk returns a tuple with the Dispatches field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetDispatchesOk() ([]MessageDispatch, bool) {\n\tif o == nil || IsNil(o.Dispatches) {\n\t\treturn nil, false\n\t}\n\treturn o.Dispatches, true\n}\n\n// HasDispatches returns a boolean if a field has been set.\nfunc (o *Message) HasDispatches() bool {\n\tif o != nil && !IsNil(o.Dispatches) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDispatches gets a reference to the given []MessageDispatch and assigns it to the Dispatches field.\nfunc (o *Message) SetDispatches(v []MessageDispatch) {\n\to.Dispatches = v\n}\n\n// GetId returns the Id field value\nfunc (o *Message) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *Message) SetId(v string) {\n\to.Id = v\n}\n\n// GetRecipient returns the Recipient field value\nfunc (o *Message) GetRecipient() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Recipient\n}\n\n// GetRecipientOk returns a tuple with the Recipient field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetRecipientOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Recipient, true\n}\n\n// SetRecipient sets field value\nfunc (o *Message) SetRecipient(v string) {\n\to.Recipient = v\n}\n\n// GetSendCount returns the SendCount field value\nfunc (o *Message) GetSendCount() int64 {\n\tif o == nil {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\n\treturn o.SendCount\n}\n\n// GetSendCountOk returns a tuple with the SendCount field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetSendCountOk() (*int64, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SendCount, true\n}\n\n// SetSendCount sets field value\nfunc (o *Message) SetSendCount(v int64) {\n\to.SendCount = v\n}\n\n// GetStatus returns the Status field value\nfunc (o *Message) GetStatus() CourierMessageStatus {\n\tif o == nil {\n\t\tvar ret CourierMessageStatus\n\t\treturn ret\n\t}\n\n\treturn o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetStatusOk() (*CourierMessageStatus, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Status, true\n}\n\n// SetStatus sets field value\nfunc (o *Message) SetStatus(v CourierMessageStatus) {\n\to.Status = v\n}\n\n// GetSubject returns the Subject field value\nfunc (o *Message) GetSubject() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetSubjectOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Subject, true\n}\n\n// SetSubject sets field value\nfunc (o *Message) SetSubject(v string) {\n\to.Subject = v\n}\n\n// GetTemplateType returns the TemplateType field value\nfunc (o *Message) GetTemplateType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.TemplateType\n}\n\n// GetTemplateTypeOk returns a tuple with the TemplateType field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetTemplateTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.TemplateType, true\n}\n\n// SetTemplateType sets field value\nfunc (o *Message) SetTemplateType(v string) {\n\to.TemplateType = v\n}\n\n// GetType returns the Type field value\nfunc (o *Message) GetType() CourierMessageType {\n\tif o == nil {\n\t\tvar ret CourierMessageType\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetTypeOk() (*CourierMessageType, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *Message) SetType(v CourierMessageType) {\n\to.Type = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value\nfunc (o *Message) GetUpdatedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.UpdatedAt, true\n}\n\n// SetUpdatedAt sets field value\nfunc (o *Message) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = v\n}\n\nfunc (o Message) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Message) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"body\"] = o.Body\n\tif !IsNil(o.Channel) {\n\t\ttoSerialize[\"channel\"] = o.Channel\n\t}\n\ttoSerialize[\"created_at\"] = o.CreatedAt\n\tif !IsNil(o.Dispatches) {\n\t\ttoSerialize[\"dispatches\"] = o.Dispatches\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"recipient\"] = o.Recipient\n\ttoSerialize[\"send_count\"] = o.SendCount\n\ttoSerialize[\"status\"] = o.Status\n\ttoSerialize[\"subject\"] = o.Subject\n\ttoSerialize[\"template_type\"] = o.TemplateType\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Message) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"body\",\n\t\t\"created_at\",\n\t\t\"id\",\n\t\t\"recipient\",\n\t\t\"send_count\",\n\t\t\"status\",\n\t\t\"subject\",\n\t\t\"template_type\",\n\t\t\"type\",\n\t\t\"updated_at\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarMessage := _Message{}\n\n\terr = json.Unmarshal(data, &varMessage)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Message(varMessage)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"body\")\n\t\tdelete(additionalProperties, \"channel\")\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"dispatches\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"recipient\")\n\t\tdelete(additionalProperties, \"send_count\")\n\t\tdelete(additionalProperties, \"status\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\tdelete(additionalProperties, \"template_type\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableMessage struct {\n\tvalue *Message\n\tisSet bool\n}\n\nfunc (v NullableMessage) Get() *Message {\n\treturn v.value\n}\n\nfunc (v *NullableMessage) Set(val *Message) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableMessage) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableMessage) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableMessage(val *Message) *NullableMessage {\n\treturn &NullableMessage{value: val, isSet: true}\n}\n\nfunc (v NullableMessage) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableMessage) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_message_dispatch.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the MessageDispatch type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &MessageDispatch{}\n\n// MessageDispatch MessageDispatch represents an attempt of sending a courier message It contains the status of the attempt (failed or successful) and the error if any occured\ntype MessageDispatch struct {\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time              `json:\"created_at\"`\n\tError     map[string]interface{} `json:\"error,omitempty\"`\n\t// The ID of this message dispatch\n\tId string `json:\"id\"`\n\t// The ID of the message being dispatched\n\tMessageId string `json:\"message_id\"`\n\t// The status of this dispatch Either \\\"failed\\\" or \\\"success\\\" failed CourierMessageDispatchStatusFailed success CourierMessageDispatchStatusSuccess\n\tStatus string `json:\"status\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            time.Time `json:\"updated_at\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _MessageDispatch MessageDispatch\n\n// NewMessageDispatch instantiates a new MessageDispatch object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewMessageDispatch(createdAt time.Time, id string, messageId string, status string, updatedAt time.Time) *MessageDispatch {\n\tthis := MessageDispatch{}\n\tthis.CreatedAt = createdAt\n\tthis.Id = id\n\tthis.MessageId = messageId\n\tthis.Status = status\n\tthis.UpdatedAt = updatedAt\n\treturn &this\n}\n\n// NewMessageDispatchWithDefaults instantiates a new MessageDispatch object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewMessageDispatchWithDefaults() *MessageDispatch {\n\tthis := MessageDispatch{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value\nfunc (o *MessageDispatch) GetCreatedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.CreatedAt, true\n}\n\n// SetCreatedAt sets field value\nfunc (o *MessageDispatch) SetCreatedAt(v time.Time) {\n\to.CreatedAt = v\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *MessageDispatch) GetError() map[string]interface{} {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetErrorOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *MessageDispatch) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given map[string]interface{} and assigns it to the Error field.\nfunc (o *MessageDispatch) SetError(v map[string]interface{}) {\n\to.Error = v\n}\n\n// GetId returns the Id field value\nfunc (o *MessageDispatch) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *MessageDispatch) SetId(v string) {\n\to.Id = v\n}\n\n// GetMessageId returns the MessageId field value\nfunc (o *MessageDispatch) GetMessageId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.MessageId\n}\n\n// GetMessageIdOk returns a tuple with the MessageId field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetMessageIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.MessageId, true\n}\n\n// SetMessageId sets field value\nfunc (o *MessageDispatch) SetMessageId(v string) {\n\to.MessageId = v\n}\n\n// GetStatus returns the Status field value\nfunc (o *MessageDispatch) GetStatus() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetStatusOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Status, true\n}\n\n// SetStatus sets field value\nfunc (o *MessageDispatch) SetStatus(v string) {\n\to.Status = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value\nfunc (o *MessageDispatch) GetUpdatedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.UpdatedAt, true\n}\n\n// SetUpdatedAt sets field value\nfunc (o *MessageDispatch) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = v\n}\n\nfunc (o MessageDispatch) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o MessageDispatch) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"created_at\"] = o.CreatedAt\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"message_id\"] = o.MessageId\n\ttoSerialize[\"status\"] = o.Status\n\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *MessageDispatch) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"created_at\",\n\t\t\"id\",\n\t\t\"message_id\",\n\t\t\"status\",\n\t\t\"updated_at\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarMessageDispatch := _MessageDispatch{}\n\n\terr = json.Unmarshal(data, &varMessageDispatch)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = MessageDispatch(varMessageDispatch)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"message_id\")\n\t\tdelete(additionalProperties, \"status\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableMessageDispatch struct {\n\tvalue *MessageDispatch\n\tisSet bool\n}\n\nfunc (v NullableMessageDispatch) Get() *MessageDispatch {\n\treturn v.value\n}\n\nfunc (v *NullableMessageDispatch) Set(val *MessageDispatch) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableMessageDispatch) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableMessageDispatch) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableMessageDispatch(val *MessageDispatch) *NullableMessageDispatch {\n\treturn &NullableMessageDispatch{value: val, isSet: true}\n}\n\nfunc (v NullableMessageDispatch) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableMessageDispatch) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_needs_privileged_session_error.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the NeedsPrivilegedSessionError type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &NeedsPrivilegedSessionError{}\n\n// NeedsPrivilegedSessionError struct for NeedsPrivilegedSessionError\ntype NeedsPrivilegedSessionError struct {\n\tError *GenericError `json:\"error,omitempty\"`\n\t// Points to where to redirect the user to next.\n\tRedirectBrowserTo    string `json:\"redirect_browser_to\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _NeedsPrivilegedSessionError NeedsPrivilegedSessionError\n\n// NewNeedsPrivilegedSessionError instantiates a new NeedsPrivilegedSessionError object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewNeedsPrivilegedSessionError(redirectBrowserTo string) *NeedsPrivilegedSessionError {\n\tthis := NeedsPrivilegedSessionError{}\n\tthis.RedirectBrowserTo = redirectBrowserTo\n\treturn &this\n}\n\n// NewNeedsPrivilegedSessionErrorWithDefaults instantiates a new NeedsPrivilegedSessionError object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewNeedsPrivilegedSessionErrorWithDefaults() *NeedsPrivilegedSessionError {\n\tthis := NeedsPrivilegedSessionError{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *NeedsPrivilegedSessionError) GetError() GenericError {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *NeedsPrivilegedSessionError) GetErrorOk() (*GenericError, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *NeedsPrivilegedSessionError) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given GenericError and assigns it to the Error field.\nfunc (o *NeedsPrivilegedSessionError) SetError(v GenericError) {\n\to.Error = &v\n}\n\n// GetRedirectBrowserTo returns the RedirectBrowserTo field value\nfunc (o *NeedsPrivilegedSessionError) GetRedirectBrowserTo() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RedirectBrowserTo\n}\n\n// GetRedirectBrowserToOk returns a tuple with the RedirectBrowserTo field value\n// and a boolean to check if the value has been set.\nfunc (o *NeedsPrivilegedSessionError) GetRedirectBrowserToOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RedirectBrowserTo, true\n}\n\n// SetRedirectBrowserTo sets field value\nfunc (o *NeedsPrivilegedSessionError) SetRedirectBrowserTo(v string) {\n\to.RedirectBrowserTo = v\n}\n\nfunc (o NeedsPrivilegedSessionError) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o NeedsPrivilegedSessionError) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\ttoSerialize[\"redirect_browser_to\"] = o.RedirectBrowserTo\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *NeedsPrivilegedSessionError) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"redirect_browser_to\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarNeedsPrivilegedSessionError := _NeedsPrivilegedSessionError{}\n\n\terr = json.Unmarshal(data, &varNeedsPrivilegedSessionError)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = NeedsPrivilegedSessionError(varNeedsPrivilegedSessionError)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"redirect_browser_to\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableNeedsPrivilegedSessionError struct {\n\tvalue *NeedsPrivilegedSessionError\n\tisSet bool\n}\n\nfunc (v NullableNeedsPrivilegedSessionError) Get() *NeedsPrivilegedSessionError {\n\treturn v.value\n}\n\nfunc (v *NullableNeedsPrivilegedSessionError) Set(val *NeedsPrivilegedSessionError) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableNeedsPrivilegedSessionError) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableNeedsPrivilegedSessionError) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableNeedsPrivilegedSessionError(val *NeedsPrivilegedSessionError) *NullableNeedsPrivilegedSessionError {\n\treturn &NullableNeedsPrivilegedSessionError{value: val, isSet: true}\n}\n\nfunc (v NullableNeedsPrivilegedSessionError) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableNeedsPrivilegedSessionError) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_o_auth2_client.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n)\n\n// checks if the OAuth2Client type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &OAuth2Client{}\n\n// OAuth2Client struct for OAuth2Client\ntype OAuth2Client struct {\n\t// OAuth 2.0 Access Token Strategy  AccessTokenStrategy is the strategy used to generate access tokens. Valid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens Setting the stragegy here overrides the global setting in `strategies.access_token`.\n\tAccessTokenStrategy *string  `json:\"access_token_strategy,omitempty\"`\n\tAllowedCorsOrigins  []string `json:\"allowed_cors_origins,omitempty\"`\n\tAudience            []string `json:\"audience,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tAuthorizationCodeGrantAccessTokenLifespan *string `json:\"authorization_code_grant_access_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tAuthorizationCodeGrantIdTokenLifespan *string `json:\"authorization_code_grant_id_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tAuthorizationCodeGrantRefreshTokenLifespan *string `json:\"authorization_code_grant_refresh_token_lifespan,omitempty\"`\n\t// OpenID Connect Back-Channel Logout Session Required  Boolean value specifying whether the RP requires that a sid (session ID) Claim be included in the Logout Token to identify the RP session with the OP when the backchannel_logout_uri is used. If omitted, the default value is false.\n\tBackchannelLogoutSessionRequired *bool `json:\"backchannel_logout_session_required,omitempty\"`\n\t// OpenID Connect Back-Channel Logout URI  RP URL that will cause the RP to log itself out when sent a Logout Token by the OP.\n\tBackchannelLogoutUri *string `json:\"backchannel_logout_uri,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tClientCredentialsGrantAccessTokenLifespan *string `json:\"client_credentials_grant_access_token_lifespan,omitempty\"`\n\t// OAuth 2.0 Client ID  The ID is immutable. If no ID is provided, a UUID4 will be generated.\n\tClientId *string `json:\"client_id,omitempty\"`\n\t// OAuth 2.0 Client Name  The human-readable name of the client to be presented to the end-user during authorization.\n\tClientName *string `json:\"client_name,omitempty\"`\n\t// OAuth 2.0 Client Secret  The secret will be included in the create request as cleartext, and then never again. The secret is kept in hashed format and is not recoverable once lost.\n\tClientSecret *string `json:\"client_secret,omitempty\"`\n\t// OAuth 2.0 Client Secret Expires At  The field is currently not supported and its value is always 0.\n\tClientSecretExpiresAt *int64 `json:\"client_secret_expires_at,omitempty\"`\n\t// OAuth 2.0 Client URI  ClientURI is a URL string of a web page providing information about the client. If present, the server SHOULD display this URL to the end-user in a clickable fashion.\n\tClientUri *string  `json:\"client_uri,omitempty\"`\n\tContacts  []string `json:\"contacts,omitempty\"`\n\t// OAuth 2.0 Client Creation Date  CreatedAt returns the timestamp of the client's creation.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// OpenID Connect Front-Channel Logout Session Required  Boolean value specifying whether the RP requires that iss (issuer) and sid (session ID) query parameters be included to identify the RP session with the OP when the frontchannel_logout_uri is used. If omitted, the default value is false.\n\tFrontchannelLogoutSessionRequired *bool `json:\"frontchannel_logout_session_required,omitempty\"`\n\t// OpenID Connect Front-Channel Logout URI  RP URL that will cause the RP to log itself out when rendered in an iframe by the OP. An iss (issuer) query parameter and a sid (session ID) query parameter MAY be included by the OP to enable the RP to validate the request and to determine which of the potentially multiple sessions is to be logged out; if either is included, both MUST be.\n\tFrontchannelLogoutUri *string  `json:\"frontchannel_logout_uri,omitempty\"`\n\tGrantTypes            []string `json:\"grant_types,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tImplicitGrantAccessTokenLifespan *string `json:\"implicit_grant_access_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tImplicitGrantIdTokenLifespan *string `json:\"implicit_grant_id_token_lifespan,omitempty\"`\n\t// OAuth 2.0 Client JSON Web Key Set  Client's JSON Web Key Set [JWK] document, passed by value. The semantics of the jwks parameter are the same as the jwks_uri parameter, other than that the JWK Set is passed by value, rather than by reference. This parameter is intended only to be used by Clients that, for some reason, are unable to use the jwks_uri parameter, for instance, by native applications that might not have a location to host the contents of the JWK Set. If a Client can use jwks_uri, it MUST NOT use jwks. One significant downside of jwks is that it does not enable key rotation (which jwks_uri does, as described in Section 10 of OpenID Connect Core 1.0 [OpenID.Core]). The jwks_uri and jwks parameters MUST NOT be used together.\n\tJwks interface{} `json:\"jwks,omitempty\"`\n\t// OAuth 2.0 Client JSON Web Key Set URL  URL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains the signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the Client's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing and encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both signatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.\n\tJwksUri *string `json:\"jwks_uri,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tJwtBearerGrantAccessTokenLifespan *string `json:\"jwt_bearer_grant_access_token_lifespan,omitempty\"`\n\t// OAuth 2.0 Client Logo URI  A URL string referencing the client's logo.\n\tLogoUri  *string     `json:\"logo_uri,omitempty\"`\n\tMetadata interface{} `json:\"metadata,omitempty\"`\n\t// OAuth 2.0 Client Owner  Owner is a string identifying the owner of the OAuth 2.0 Client.\n\tOwner *string `json:\"owner,omitempty\"`\n\t// OAuth 2.0 Client Policy URI  PolicyURI is a URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data.\n\tPolicyUri              *string  `json:\"policy_uri,omitempty\"`\n\tPostLogoutRedirectUris []string `json:\"post_logout_redirect_uris,omitempty\"`\n\tRedirectUris           []string `json:\"redirect_uris,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tRefreshTokenGrantAccessTokenLifespan *string `json:\"refresh_token_grant_access_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tRefreshTokenGrantIdTokenLifespan *string `json:\"refresh_token_grant_id_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tRefreshTokenGrantRefreshTokenLifespan *string `json:\"refresh_token_grant_refresh_token_lifespan,omitempty\"`\n\t// OpenID Connect Dynamic Client Registration Access Token  RegistrationAccessToken can be used to update, get, or delete the OAuth2 Client. It is sent when creating a client using Dynamic Client Registration.\n\tRegistrationAccessToken *string `json:\"registration_access_token,omitempty\"`\n\t// OpenID Connect Dynamic Client Registration URL  RegistrationClientURI is the URL used to update, get, or delete the OAuth2 Client.\n\tRegistrationClientUri *string `json:\"registration_client_uri,omitempty\"`\n\t// OpenID Connect Request Object Signing Algorithm  JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.\n\tRequestObjectSigningAlg *string  `json:\"request_object_signing_alg,omitempty\"`\n\tRequestUris             []string `json:\"request_uris,omitempty\"`\n\tResponseTypes           []string `json:\"response_types,omitempty\"`\n\t// OAuth 2.0 Client Scope  Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens.\n\tScope *string `json:\"scope,omitempty\"`\n\t// OpenID Connect Sector Identifier URI  URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values.\n\tSectorIdentifierUri *string `json:\"sector_identifier_uri,omitempty\"`\n\t// SkipConsent skips the consent screen for this client. This field can only be set from the admin API.\n\tSkipConsent *bool `json:\"skip_consent,omitempty\"`\n\t// SkipLogoutConsent skips the logout consent screen for this client. This field can only be set from the admin API.\n\tSkipLogoutConsent *bool `json:\"skip_logout_consent,omitempty\"`\n\t// OpenID Connect Subject Type  The `subject_types_supported` Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`.\n\tSubjectType *string `json:\"subject_type,omitempty\"`\n\t// OAuth 2.0 Token Endpoint Authentication Method  Requested Client Authentication method for the Token Endpoint. The options are:  `client_secret_basic`: (default) Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` encoded in the HTTP Authorization header. `client_secret_post`: Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` in the HTTP body. `private_key_jwt`: Use JSON Web Tokens to authenticate the client. `none`: Used for public clients (native apps, mobile apps) which can not have secrets.\n\tTokenEndpointAuthMethod *string `json:\"token_endpoint_auth_method,omitempty\"`\n\t// OAuth 2.0 Token Endpoint Signing Algorithm  Requested Client Authentication signing algorithm for the Token Endpoint.\n\tTokenEndpointAuthSigningAlg *string `json:\"token_endpoint_auth_signing_alg,omitempty\"`\n\t// OAuth 2.0 Client Terms of Service URI  A URL string pointing to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client.\n\tTosUri *string `json:\"tos_uri,omitempty\"`\n\t// OAuth 2.0 Client Last Update Date  UpdatedAt returns the timestamp of the last update.\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\"`\n\t// OpenID Connect Request Userinfo Signed Response Algorithm  JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type.\n\tUserinfoSignedResponseAlg *string `json:\"userinfo_signed_response_alg,omitempty\"`\n\tAdditionalProperties      map[string]interface{}\n}\n\ntype _OAuth2Client OAuth2Client\n\n// NewOAuth2Client instantiates a new OAuth2Client object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewOAuth2Client() *OAuth2Client {\n\tthis := OAuth2Client{}\n\treturn &this\n}\n\n// NewOAuth2ClientWithDefaults instantiates a new OAuth2Client object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewOAuth2ClientWithDefaults() *OAuth2Client {\n\tthis := OAuth2Client{}\n\treturn &this\n}\n\n// GetAccessTokenStrategy returns the AccessTokenStrategy field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAccessTokenStrategy() string {\n\tif o == nil || IsNil(o.AccessTokenStrategy) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.AccessTokenStrategy\n}\n\n// GetAccessTokenStrategyOk returns a tuple with the AccessTokenStrategy field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAccessTokenStrategyOk() (*string, bool) {\n\tif o == nil || IsNil(o.AccessTokenStrategy) {\n\t\treturn nil, false\n\t}\n\treturn o.AccessTokenStrategy, true\n}\n\n// HasAccessTokenStrategy returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAccessTokenStrategy() bool {\n\tif o != nil && !IsNil(o.AccessTokenStrategy) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAccessTokenStrategy gets a reference to the given string and assigns it to the AccessTokenStrategy field.\nfunc (o *OAuth2Client) SetAccessTokenStrategy(v string) {\n\to.AccessTokenStrategy = &v\n}\n\n// GetAllowedCorsOrigins returns the AllowedCorsOrigins field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAllowedCorsOrigins() []string {\n\tif o == nil || IsNil(o.AllowedCorsOrigins) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.AllowedCorsOrigins\n}\n\n// GetAllowedCorsOriginsOk returns a tuple with the AllowedCorsOrigins field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAllowedCorsOriginsOk() ([]string, bool) {\n\tif o == nil || IsNil(o.AllowedCorsOrigins) {\n\t\treturn nil, false\n\t}\n\treturn o.AllowedCorsOrigins, true\n}\n\n// HasAllowedCorsOrigins returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAllowedCorsOrigins() bool {\n\tif o != nil && !IsNil(o.AllowedCorsOrigins) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAllowedCorsOrigins gets a reference to the given []string and assigns it to the AllowedCorsOrigins field.\nfunc (o *OAuth2Client) SetAllowedCorsOrigins(v []string) {\n\to.AllowedCorsOrigins = v\n}\n\n// GetAudience returns the Audience field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAudience() []string {\n\tif o == nil || IsNil(o.Audience) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.Audience\n}\n\n// GetAudienceOk returns a tuple with the Audience field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAudienceOk() ([]string, bool) {\n\tif o == nil || IsNil(o.Audience) {\n\t\treturn nil, false\n\t}\n\treturn o.Audience, true\n}\n\n// HasAudience returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAudience() bool {\n\tif o != nil && !IsNil(o.Audience) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAudience gets a reference to the given []string and assigns it to the Audience field.\nfunc (o *OAuth2Client) SetAudience(v []string) {\n\to.Audience = v\n}\n\n// GetAuthorizationCodeGrantAccessTokenLifespan returns the AuthorizationCodeGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.AuthorizationCodeGrantAccessTokenLifespan\n}\n\n// GetAuthorizationCodeGrantAccessTokenLifespanOk returns a tuple with the AuthorizationCodeGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthorizationCodeGrantAccessTokenLifespan, true\n}\n\n// HasAuthorizationCodeGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAuthorizationCodeGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.AuthorizationCodeGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthorizationCodeGrantAccessTokenLifespan gets a reference to the given string and assigns it to the AuthorizationCodeGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetAuthorizationCodeGrantAccessTokenLifespan(v string) {\n\to.AuthorizationCodeGrantAccessTokenLifespan = &v\n}\n\n// GetAuthorizationCodeGrantIdTokenLifespan returns the AuthorizationCodeGrantIdTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantIdTokenLifespan() string {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantIdTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.AuthorizationCodeGrantIdTokenLifespan\n}\n\n// GetAuthorizationCodeGrantIdTokenLifespanOk returns a tuple with the AuthorizationCodeGrantIdTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantIdTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantIdTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthorizationCodeGrantIdTokenLifespan, true\n}\n\n// HasAuthorizationCodeGrantIdTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAuthorizationCodeGrantIdTokenLifespan() bool {\n\tif o != nil && !IsNil(o.AuthorizationCodeGrantIdTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthorizationCodeGrantIdTokenLifespan gets a reference to the given string and assigns it to the AuthorizationCodeGrantIdTokenLifespan field.\nfunc (o *OAuth2Client) SetAuthorizationCodeGrantIdTokenLifespan(v string) {\n\to.AuthorizationCodeGrantIdTokenLifespan = &v\n}\n\n// GetAuthorizationCodeGrantRefreshTokenLifespan returns the AuthorizationCodeGrantRefreshTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantRefreshTokenLifespan() string {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantRefreshTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.AuthorizationCodeGrantRefreshTokenLifespan\n}\n\n// GetAuthorizationCodeGrantRefreshTokenLifespanOk returns a tuple with the AuthorizationCodeGrantRefreshTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantRefreshTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantRefreshTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthorizationCodeGrantRefreshTokenLifespan, true\n}\n\n// HasAuthorizationCodeGrantRefreshTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAuthorizationCodeGrantRefreshTokenLifespan() bool {\n\tif o != nil && !IsNil(o.AuthorizationCodeGrantRefreshTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthorizationCodeGrantRefreshTokenLifespan gets a reference to the given string and assigns it to the AuthorizationCodeGrantRefreshTokenLifespan field.\nfunc (o *OAuth2Client) SetAuthorizationCodeGrantRefreshTokenLifespan(v string) {\n\to.AuthorizationCodeGrantRefreshTokenLifespan = &v\n}\n\n// GetBackchannelLogoutSessionRequired returns the BackchannelLogoutSessionRequired field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetBackchannelLogoutSessionRequired() bool {\n\tif o == nil || IsNil(o.BackchannelLogoutSessionRequired) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.BackchannelLogoutSessionRequired\n}\n\n// GetBackchannelLogoutSessionRequiredOk returns a tuple with the BackchannelLogoutSessionRequired field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetBackchannelLogoutSessionRequiredOk() (*bool, bool) {\n\tif o == nil || IsNil(o.BackchannelLogoutSessionRequired) {\n\t\treturn nil, false\n\t}\n\treturn o.BackchannelLogoutSessionRequired, true\n}\n\n// HasBackchannelLogoutSessionRequired returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasBackchannelLogoutSessionRequired() bool {\n\tif o != nil && !IsNil(o.BackchannelLogoutSessionRequired) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetBackchannelLogoutSessionRequired gets a reference to the given bool and assigns it to the BackchannelLogoutSessionRequired field.\nfunc (o *OAuth2Client) SetBackchannelLogoutSessionRequired(v bool) {\n\to.BackchannelLogoutSessionRequired = &v\n}\n\n// GetBackchannelLogoutUri returns the BackchannelLogoutUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetBackchannelLogoutUri() string {\n\tif o == nil || IsNil(o.BackchannelLogoutUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.BackchannelLogoutUri\n}\n\n// GetBackchannelLogoutUriOk returns a tuple with the BackchannelLogoutUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetBackchannelLogoutUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.BackchannelLogoutUri) {\n\t\treturn nil, false\n\t}\n\treturn o.BackchannelLogoutUri, true\n}\n\n// HasBackchannelLogoutUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasBackchannelLogoutUri() bool {\n\tif o != nil && !IsNil(o.BackchannelLogoutUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetBackchannelLogoutUri gets a reference to the given string and assigns it to the BackchannelLogoutUri field.\nfunc (o *OAuth2Client) SetBackchannelLogoutUri(v string) {\n\to.BackchannelLogoutUri = &v\n}\n\n// GetClientCredentialsGrantAccessTokenLifespan returns the ClientCredentialsGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientCredentialsGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.ClientCredentialsGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientCredentialsGrantAccessTokenLifespan\n}\n\n// GetClientCredentialsGrantAccessTokenLifespanOk returns a tuple with the ClientCredentialsGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientCredentialsGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientCredentialsGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientCredentialsGrantAccessTokenLifespan, true\n}\n\n// HasClientCredentialsGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientCredentialsGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.ClientCredentialsGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientCredentialsGrantAccessTokenLifespan gets a reference to the given string and assigns it to the ClientCredentialsGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetClientCredentialsGrantAccessTokenLifespan(v string) {\n\to.ClientCredentialsGrantAccessTokenLifespan = &v\n}\n\n// GetClientId returns the ClientId field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientId() string {\n\tif o == nil || IsNil(o.ClientId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientId\n}\n\n// GetClientIdOk returns a tuple with the ClientId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientId) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientId, true\n}\n\n// HasClientId returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientId() bool {\n\tif o != nil && !IsNil(o.ClientId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientId gets a reference to the given string and assigns it to the ClientId field.\nfunc (o *OAuth2Client) SetClientId(v string) {\n\to.ClientId = &v\n}\n\n// GetClientName returns the ClientName field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientName() string {\n\tif o == nil || IsNil(o.ClientName) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientName\n}\n\n// GetClientNameOk returns a tuple with the ClientName field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientNameOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientName) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientName, true\n}\n\n// HasClientName returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientName() bool {\n\tif o != nil && !IsNil(o.ClientName) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientName gets a reference to the given string and assigns it to the ClientName field.\nfunc (o *OAuth2Client) SetClientName(v string) {\n\to.ClientName = &v\n}\n\n// GetClientSecret returns the ClientSecret field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientSecret() string {\n\tif o == nil || IsNil(o.ClientSecret) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientSecret\n}\n\n// GetClientSecretOk returns a tuple with the ClientSecret field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientSecretOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientSecret) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientSecret, true\n}\n\n// HasClientSecret returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientSecret() bool {\n\tif o != nil && !IsNil(o.ClientSecret) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientSecret gets a reference to the given string and assigns it to the ClientSecret field.\nfunc (o *OAuth2Client) SetClientSecret(v string) {\n\to.ClientSecret = &v\n}\n\n// GetClientSecretExpiresAt returns the ClientSecretExpiresAt field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientSecretExpiresAt() int64 {\n\tif o == nil || IsNil(o.ClientSecretExpiresAt) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.ClientSecretExpiresAt\n}\n\n// GetClientSecretExpiresAtOk returns a tuple with the ClientSecretExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientSecretExpiresAtOk() (*int64, bool) {\n\tif o == nil || IsNil(o.ClientSecretExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientSecretExpiresAt, true\n}\n\n// HasClientSecretExpiresAt returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientSecretExpiresAt() bool {\n\tif o != nil && !IsNil(o.ClientSecretExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientSecretExpiresAt gets a reference to the given int64 and assigns it to the ClientSecretExpiresAt field.\nfunc (o *OAuth2Client) SetClientSecretExpiresAt(v int64) {\n\to.ClientSecretExpiresAt = &v\n}\n\n// GetClientUri returns the ClientUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientUri() string {\n\tif o == nil || IsNil(o.ClientUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientUri\n}\n\n// GetClientUriOk returns a tuple with the ClientUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientUri) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientUri, true\n}\n\n// HasClientUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientUri() bool {\n\tif o != nil && !IsNil(o.ClientUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientUri gets a reference to the given string and assigns it to the ClientUri field.\nfunc (o *OAuth2Client) SetClientUri(v string) {\n\to.ClientUri = &v\n}\n\n// GetContacts returns the Contacts field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetContacts() []string {\n\tif o == nil || IsNil(o.Contacts) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.Contacts\n}\n\n// GetContactsOk returns a tuple with the Contacts field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetContactsOk() ([]string, bool) {\n\tif o == nil || IsNil(o.Contacts) {\n\t\treturn nil, false\n\t}\n\treturn o.Contacts, true\n}\n\n// HasContacts returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasContacts() bool {\n\tif o != nil && !IsNil(o.Contacts) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContacts gets a reference to the given []string and assigns it to the Contacts field.\nfunc (o *OAuth2Client) SetContacts(v []string) {\n\to.Contacts = v\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *OAuth2Client) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetFrontchannelLogoutSessionRequired returns the FrontchannelLogoutSessionRequired field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetFrontchannelLogoutSessionRequired() bool {\n\tif o == nil || IsNil(o.FrontchannelLogoutSessionRequired) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.FrontchannelLogoutSessionRequired\n}\n\n// GetFrontchannelLogoutSessionRequiredOk returns a tuple with the FrontchannelLogoutSessionRequired field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetFrontchannelLogoutSessionRequiredOk() (*bool, bool) {\n\tif o == nil || IsNil(o.FrontchannelLogoutSessionRequired) {\n\t\treturn nil, false\n\t}\n\treturn o.FrontchannelLogoutSessionRequired, true\n}\n\n// HasFrontchannelLogoutSessionRequired returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasFrontchannelLogoutSessionRequired() bool {\n\tif o != nil && !IsNil(o.FrontchannelLogoutSessionRequired) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFrontchannelLogoutSessionRequired gets a reference to the given bool and assigns it to the FrontchannelLogoutSessionRequired field.\nfunc (o *OAuth2Client) SetFrontchannelLogoutSessionRequired(v bool) {\n\to.FrontchannelLogoutSessionRequired = &v\n}\n\n// GetFrontchannelLogoutUri returns the FrontchannelLogoutUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetFrontchannelLogoutUri() string {\n\tif o == nil || IsNil(o.FrontchannelLogoutUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.FrontchannelLogoutUri\n}\n\n// GetFrontchannelLogoutUriOk returns a tuple with the FrontchannelLogoutUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetFrontchannelLogoutUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.FrontchannelLogoutUri) {\n\t\treturn nil, false\n\t}\n\treturn o.FrontchannelLogoutUri, true\n}\n\n// HasFrontchannelLogoutUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasFrontchannelLogoutUri() bool {\n\tif o != nil && !IsNil(o.FrontchannelLogoutUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFrontchannelLogoutUri gets a reference to the given string and assigns it to the FrontchannelLogoutUri field.\nfunc (o *OAuth2Client) SetFrontchannelLogoutUri(v string) {\n\to.FrontchannelLogoutUri = &v\n}\n\n// GetGrantTypes returns the GrantTypes field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetGrantTypes() []string {\n\tif o == nil || IsNil(o.GrantTypes) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.GrantTypes\n}\n\n// GetGrantTypesOk returns a tuple with the GrantTypes field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetGrantTypesOk() ([]string, bool) {\n\tif o == nil || IsNil(o.GrantTypes) {\n\t\treturn nil, false\n\t}\n\treturn o.GrantTypes, true\n}\n\n// HasGrantTypes returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasGrantTypes() bool {\n\tif o != nil && !IsNil(o.GrantTypes) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetGrantTypes gets a reference to the given []string and assigns it to the GrantTypes field.\nfunc (o *OAuth2Client) SetGrantTypes(v []string) {\n\to.GrantTypes = v\n}\n\n// GetImplicitGrantAccessTokenLifespan returns the ImplicitGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetImplicitGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.ImplicitGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ImplicitGrantAccessTokenLifespan\n}\n\n// GetImplicitGrantAccessTokenLifespanOk returns a tuple with the ImplicitGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetImplicitGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.ImplicitGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.ImplicitGrantAccessTokenLifespan, true\n}\n\n// HasImplicitGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasImplicitGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.ImplicitGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetImplicitGrantAccessTokenLifespan gets a reference to the given string and assigns it to the ImplicitGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetImplicitGrantAccessTokenLifespan(v string) {\n\to.ImplicitGrantAccessTokenLifespan = &v\n}\n\n// GetImplicitGrantIdTokenLifespan returns the ImplicitGrantIdTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetImplicitGrantIdTokenLifespan() string {\n\tif o == nil || IsNil(o.ImplicitGrantIdTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ImplicitGrantIdTokenLifespan\n}\n\n// GetImplicitGrantIdTokenLifespanOk returns a tuple with the ImplicitGrantIdTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetImplicitGrantIdTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.ImplicitGrantIdTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.ImplicitGrantIdTokenLifespan, true\n}\n\n// HasImplicitGrantIdTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasImplicitGrantIdTokenLifespan() bool {\n\tif o != nil && !IsNil(o.ImplicitGrantIdTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetImplicitGrantIdTokenLifespan gets a reference to the given string and assigns it to the ImplicitGrantIdTokenLifespan field.\nfunc (o *OAuth2Client) SetImplicitGrantIdTokenLifespan(v string) {\n\to.ImplicitGrantIdTokenLifespan = &v\n}\n\n// GetJwks returns the Jwks field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *OAuth2Client) GetJwks() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Jwks\n}\n\n// GetJwksOk returns a tuple with the Jwks field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *OAuth2Client) GetJwksOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Jwks) {\n\t\treturn nil, false\n\t}\n\treturn &o.Jwks, true\n}\n\n// HasJwks returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasJwks() bool {\n\tif o != nil && !IsNil(o.Jwks) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetJwks gets a reference to the given interface{} and assigns it to the Jwks field.\nfunc (o *OAuth2Client) SetJwks(v interface{}) {\n\to.Jwks = v\n}\n\n// GetJwksUri returns the JwksUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetJwksUri() string {\n\tif o == nil || IsNil(o.JwksUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.JwksUri\n}\n\n// GetJwksUriOk returns a tuple with the JwksUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetJwksUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.JwksUri) {\n\t\treturn nil, false\n\t}\n\treturn o.JwksUri, true\n}\n\n// HasJwksUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasJwksUri() bool {\n\tif o != nil && !IsNil(o.JwksUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetJwksUri gets a reference to the given string and assigns it to the JwksUri field.\nfunc (o *OAuth2Client) SetJwksUri(v string) {\n\to.JwksUri = &v\n}\n\n// GetJwtBearerGrantAccessTokenLifespan returns the JwtBearerGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetJwtBearerGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.JwtBearerGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.JwtBearerGrantAccessTokenLifespan\n}\n\n// GetJwtBearerGrantAccessTokenLifespanOk returns a tuple with the JwtBearerGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetJwtBearerGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.JwtBearerGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.JwtBearerGrantAccessTokenLifespan, true\n}\n\n// HasJwtBearerGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasJwtBearerGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.JwtBearerGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetJwtBearerGrantAccessTokenLifespan gets a reference to the given string and assigns it to the JwtBearerGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetJwtBearerGrantAccessTokenLifespan(v string) {\n\to.JwtBearerGrantAccessTokenLifespan = &v\n}\n\n// GetLogoUri returns the LogoUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetLogoUri() string {\n\tif o == nil || IsNil(o.LogoUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.LogoUri\n}\n\n// GetLogoUriOk returns a tuple with the LogoUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetLogoUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.LogoUri) {\n\t\treturn nil, false\n\t}\n\treturn o.LogoUri, true\n}\n\n// HasLogoUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasLogoUri() bool {\n\tif o != nil && !IsNil(o.LogoUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLogoUri gets a reference to the given string and assigns it to the LogoUri field.\nfunc (o *OAuth2Client) SetLogoUri(v string) {\n\to.LogoUri = &v\n}\n\n// GetMetadata returns the Metadata field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *OAuth2Client) GetMetadata() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Metadata\n}\n\n// GetMetadataOk returns a tuple with the Metadata field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *OAuth2Client) GetMetadataOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Metadata) {\n\t\treturn nil, false\n\t}\n\treturn &o.Metadata, true\n}\n\n// HasMetadata returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasMetadata() bool {\n\tif o != nil && !IsNil(o.Metadata) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadata gets a reference to the given interface{} and assigns it to the Metadata field.\nfunc (o *OAuth2Client) SetMetadata(v interface{}) {\n\to.Metadata = v\n}\n\n// GetOwner returns the Owner field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetOwner() string {\n\tif o == nil || IsNil(o.Owner) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Owner\n}\n\n// GetOwnerOk returns a tuple with the Owner field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetOwnerOk() (*string, bool) {\n\tif o == nil || IsNil(o.Owner) {\n\t\treturn nil, false\n\t}\n\treturn o.Owner, true\n}\n\n// HasOwner returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasOwner() bool {\n\tif o != nil && !IsNil(o.Owner) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOwner gets a reference to the given string and assigns it to the Owner field.\nfunc (o *OAuth2Client) SetOwner(v string) {\n\to.Owner = &v\n}\n\n// GetPolicyUri returns the PolicyUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetPolicyUri() string {\n\tif o == nil || IsNil(o.PolicyUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PolicyUri\n}\n\n// GetPolicyUriOk returns a tuple with the PolicyUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetPolicyUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.PolicyUri) {\n\t\treturn nil, false\n\t}\n\treturn o.PolicyUri, true\n}\n\n// HasPolicyUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasPolicyUri() bool {\n\tif o != nil && !IsNil(o.PolicyUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPolicyUri gets a reference to the given string and assigns it to the PolicyUri field.\nfunc (o *OAuth2Client) SetPolicyUri(v string) {\n\to.PolicyUri = &v\n}\n\n// GetPostLogoutRedirectUris returns the PostLogoutRedirectUris field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetPostLogoutRedirectUris() []string {\n\tif o == nil || IsNil(o.PostLogoutRedirectUris) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.PostLogoutRedirectUris\n}\n\n// GetPostLogoutRedirectUrisOk returns a tuple with the PostLogoutRedirectUris field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetPostLogoutRedirectUrisOk() ([]string, bool) {\n\tif o == nil || IsNil(o.PostLogoutRedirectUris) {\n\t\treturn nil, false\n\t}\n\treturn o.PostLogoutRedirectUris, true\n}\n\n// HasPostLogoutRedirectUris returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasPostLogoutRedirectUris() bool {\n\tif o != nil && !IsNil(o.PostLogoutRedirectUris) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPostLogoutRedirectUris gets a reference to the given []string and assigns it to the PostLogoutRedirectUris field.\nfunc (o *OAuth2Client) SetPostLogoutRedirectUris(v []string) {\n\to.PostLogoutRedirectUris = v\n}\n\n// GetRedirectUris returns the RedirectUris field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRedirectUris() []string {\n\tif o == nil || IsNil(o.RedirectUris) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.RedirectUris\n}\n\n// GetRedirectUrisOk returns a tuple with the RedirectUris field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRedirectUrisOk() ([]string, bool) {\n\tif o == nil || IsNil(o.RedirectUris) {\n\t\treturn nil, false\n\t}\n\treturn o.RedirectUris, true\n}\n\n// HasRedirectUris returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRedirectUris() bool {\n\tif o != nil && !IsNil(o.RedirectUris) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRedirectUris gets a reference to the given []string and assigns it to the RedirectUris field.\nfunc (o *OAuth2Client) SetRedirectUris(v []string) {\n\to.RedirectUris = v\n}\n\n// GetRefreshTokenGrantAccessTokenLifespan returns the RefreshTokenGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRefreshTokenGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.RefreshTokenGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RefreshTokenGrantAccessTokenLifespan\n}\n\n// GetRefreshTokenGrantAccessTokenLifespanOk returns a tuple with the RefreshTokenGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRefreshTokenGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.RefreshTokenGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.RefreshTokenGrantAccessTokenLifespan, true\n}\n\n// HasRefreshTokenGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRefreshTokenGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.RefreshTokenGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRefreshTokenGrantAccessTokenLifespan gets a reference to the given string and assigns it to the RefreshTokenGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetRefreshTokenGrantAccessTokenLifespan(v string) {\n\to.RefreshTokenGrantAccessTokenLifespan = &v\n}\n\n// GetRefreshTokenGrantIdTokenLifespan returns the RefreshTokenGrantIdTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRefreshTokenGrantIdTokenLifespan() string {\n\tif o == nil || IsNil(o.RefreshTokenGrantIdTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RefreshTokenGrantIdTokenLifespan\n}\n\n// GetRefreshTokenGrantIdTokenLifespanOk returns a tuple with the RefreshTokenGrantIdTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRefreshTokenGrantIdTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.RefreshTokenGrantIdTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.RefreshTokenGrantIdTokenLifespan, true\n}\n\n// HasRefreshTokenGrantIdTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRefreshTokenGrantIdTokenLifespan() bool {\n\tif o != nil && !IsNil(o.RefreshTokenGrantIdTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRefreshTokenGrantIdTokenLifespan gets a reference to the given string and assigns it to the RefreshTokenGrantIdTokenLifespan field.\nfunc (o *OAuth2Client) SetRefreshTokenGrantIdTokenLifespan(v string) {\n\to.RefreshTokenGrantIdTokenLifespan = &v\n}\n\n// GetRefreshTokenGrantRefreshTokenLifespan returns the RefreshTokenGrantRefreshTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRefreshTokenGrantRefreshTokenLifespan() string {\n\tif o == nil || IsNil(o.RefreshTokenGrantRefreshTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RefreshTokenGrantRefreshTokenLifespan\n}\n\n// GetRefreshTokenGrantRefreshTokenLifespanOk returns a tuple with the RefreshTokenGrantRefreshTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRefreshTokenGrantRefreshTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.RefreshTokenGrantRefreshTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.RefreshTokenGrantRefreshTokenLifespan, true\n}\n\n// HasRefreshTokenGrantRefreshTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRefreshTokenGrantRefreshTokenLifespan() bool {\n\tif o != nil && !IsNil(o.RefreshTokenGrantRefreshTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRefreshTokenGrantRefreshTokenLifespan gets a reference to the given string and assigns it to the RefreshTokenGrantRefreshTokenLifespan field.\nfunc (o *OAuth2Client) SetRefreshTokenGrantRefreshTokenLifespan(v string) {\n\to.RefreshTokenGrantRefreshTokenLifespan = &v\n}\n\n// GetRegistrationAccessToken returns the RegistrationAccessToken field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRegistrationAccessToken() string {\n\tif o == nil || IsNil(o.RegistrationAccessToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RegistrationAccessToken\n}\n\n// GetRegistrationAccessTokenOk returns a tuple with the RegistrationAccessToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRegistrationAccessTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.RegistrationAccessToken) {\n\t\treturn nil, false\n\t}\n\treturn o.RegistrationAccessToken, true\n}\n\n// HasRegistrationAccessToken returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRegistrationAccessToken() bool {\n\tif o != nil && !IsNil(o.RegistrationAccessToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRegistrationAccessToken gets a reference to the given string and assigns it to the RegistrationAccessToken field.\nfunc (o *OAuth2Client) SetRegistrationAccessToken(v string) {\n\to.RegistrationAccessToken = &v\n}\n\n// GetRegistrationClientUri returns the RegistrationClientUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRegistrationClientUri() string {\n\tif o == nil || IsNil(o.RegistrationClientUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RegistrationClientUri\n}\n\n// GetRegistrationClientUriOk returns a tuple with the RegistrationClientUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRegistrationClientUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.RegistrationClientUri) {\n\t\treturn nil, false\n\t}\n\treturn o.RegistrationClientUri, true\n}\n\n// HasRegistrationClientUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRegistrationClientUri() bool {\n\tif o != nil && !IsNil(o.RegistrationClientUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRegistrationClientUri gets a reference to the given string and assigns it to the RegistrationClientUri field.\nfunc (o *OAuth2Client) SetRegistrationClientUri(v string) {\n\to.RegistrationClientUri = &v\n}\n\n// GetRequestObjectSigningAlg returns the RequestObjectSigningAlg field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRequestObjectSigningAlg() string {\n\tif o == nil || IsNil(o.RequestObjectSigningAlg) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RequestObjectSigningAlg\n}\n\n// GetRequestObjectSigningAlgOk returns a tuple with the RequestObjectSigningAlg field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRequestObjectSigningAlgOk() (*string, bool) {\n\tif o == nil || IsNil(o.RequestObjectSigningAlg) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestObjectSigningAlg, true\n}\n\n// HasRequestObjectSigningAlg returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRequestObjectSigningAlg() bool {\n\tif o != nil && !IsNil(o.RequestObjectSigningAlg) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestObjectSigningAlg gets a reference to the given string and assigns it to the RequestObjectSigningAlg field.\nfunc (o *OAuth2Client) SetRequestObjectSigningAlg(v string) {\n\to.RequestObjectSigningAlg = &v\n}\n\n// GetRequestUris returns the RequestUris field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRequestUris() []string {\n\tif o == nil || IsNil(o.RequestUris) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.RequestUris\n}\n\n// GetRequestUrisOk returns a tuple with the RequestUris field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRequestUrisOk() ([]string, bool) {\n\tif o == nil || IsNil(o.RequestUris) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestUris, true\n}\n\n// HasRequestUris returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRequestUris() bool {\n\tif o != nil && !IsNil(o.RequestUris) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestUris gets a reference to the given []string and assigns it to the RequestUris field.\nfunc (o *OAuth2Client) SetRequestUris(v []string) {\n\to.RequestUris = v\n}\n\n// GetResponseTypes returns the ResponseTypes field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetResponseTypes() []string {\n\tif o == nil || IsNil(o.ResponseTypes) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.ResponseTypes\n}\n\n// GetResponseTypesOk returns a tuple with the ResponseTypes field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetResponseTypesOk() ([]string, bool) {\n\tif o == nil || IsNil(o.ResponseTypes) {\n\t\treturn nil, false\n\t}\n\treturn o.ResponseTypes, true\n}\n\n// HasResponseTypes returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasResponseTypes() bool {\n\tif o != nil && !IsNil(o.ResponseTypes) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetResponseTypes gets a reference to the given []string and assigns it to the ResponseTypes field.\nfunc (o *OAuth2Client) SetResponseTypes(v []string) {\n\to.ResponseTypes = v\n}\n\n// GetScope returns the Scope field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetScope() string {\n\tif o == nil || IsNil(o.Scope) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Scope\n}\n\n// GetScopeOk returns a tuple with the Scope field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetScopeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Scope) {\n\t\treturn nil, false\n\t}\n\treturn o.Scope, true\n}\n\n// HasScope returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasScope() bool {\n\tif o != nil && !IsNil(o.Scope) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetScope gets a reference to the given string and assigns it to the Scope field.\nfunc (o *OAuth2Client) SetScope(v string) {\n\to.Scope = &v\n}\n\n// GetSectorIdentifierUri returns the SectorIdentifierUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetSectorIdentifierUri() string {\n\tif o == nil || IsNil(o.SectorIdentifierUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SectorIdentifierUri\n}\n\n// GetSectorIdentifierUriOk returns a tuple with the SectorIdentifierUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetSectorIdentifierUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.SectorIdentifierUri) {\n\t\treturn nil, false\n\t}\n\treturn o.SectorIdentifierUri, true\n}\n\n// HasSectorIdentifierUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasSectorIdentifierUri() bool {\n\tif o != nil && !IsNil(o.SectorIdentifierUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSectorIdentifierUri gets a reference to the given string and assigns it to the SectorIdentifierUri field.\nfunc (o *OAuth2Client) SetSectorIdentifierUri(v string) {\n\to.SectorIdentifierUri = &v\n}\n\n// GetSkipConsent returns the SkipConsent field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetSkipConsent() bool {\n\tif o == nil || IsNil(o.SkipConsent) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.SkipConsent\n}\n\n// GetSkipConsentOk returns a tuple with the SkipConsent field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetSkipConsentOk() (*bool, bool) {\n\tif o == nil || IsNil(o.SkipConsent) {\n\t\treturn nil, false\n\t}\n\treturn o.SkipConsent, true\n}\n\n// HasSkipConsent returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasSkipConsent() bool {\n\tif o != nil && !IsNil(o.SkipConsent) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSkipConsent gets a reference to the given bool and assigns it to the SkipConsent field.\nfunc (o *OAuth2Client) SetSkipConsent(v bool) {\n\to.SkipConsent = &v\n}\n\n// GetSkipLogoutConsent returns the SkipLogoutConsent field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetSkipLogoutConsent() bool {\n\tif o == nil || IsNil(o.SkipLogoutConsent) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.SkipLogoutConsent\n}\n\n// GetSkipLogoutConsentOk returns a tuple with the SkipLogoutConsent field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetSkipLogoutConsentOk() (*bool, bool) {\n\tif o == nil || IsNil(o.SkipLogoutConsent) {\n\t\treturn nil, false\n\t}\n\treturn o.SkipLogoutConsent, true\n}\n\n// HasSkipLogoutConsent returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasSkipLogoutConsent() bool {\n\tif o != nil && !IsNil(o.SkipLogoutConsent) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSkipLogoutConsent gets a reference to the given bool and assigns it to the SkipLogoutConsent field.\nfunc (o *OAuth2Client) SetSkipLogoutConsent(v bool) {\n\to.SkipLogoutConsent = &v\n}\n\n// GetSubjectType returns the SubjectType field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetSubjectType() string {\n\tif o == nil || IsNil(o.SubjectType) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SubjectType\n}\n\n// GetSubjectTypeOk returns a tuple with the SubjectType field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetSubjectTypeOk() (*string, bool) {\n\tif o == nil || IsNil(o.SubjectType) {\n\t\treturn nil, false\n\t}\n\treturn o.SubjectType, true\n}\n\n// HasSubjectType returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasSubjectType() bool {\n\tif o != nil && !IsNil(o.SubjectType) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSubjectType gets a reference to the given string and assigns it to the SubjectType field.\nfunc (o *OAuth2Client) SetSubjectType(v string) {\n\to.SubjectType = &v\n}\n\n// GetTokenEndpointAuthMethod returns the TokenEndpointAuthMethod field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetTokenEndpointAuthMethod() string {\n\tif o == nil || IsNil(o.TokenEndpointAuthMethod) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.TokenEndpointAuthMethod\n}\n\n// GetTokenEndpointAuthMethodOk returns a tuple with the TokenEndpointAuthMethod field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetTokenEndpointAuthMethodOk() (*string, bool) {\n\tif o == nil || IsNil(o.TokenEndpointAuthMethod) {\n\t\treturn nil, false\n\t}\n\treturn o.TokenEndpointAuthMethod, true\n}\n\n// HasTokenEndpointAuthMethod returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasTokenEndpointAuthMethod() bool {\n\tif o != nil && !IsNil(o.TokenEndpointAuthMethod) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTokenEndpointAuthMethod gets a reference to the given string and assigns it to the TokenEndpointAuthMethod field.\nfunc (o *OAuth2Client) SetTokenEndpointAuthMethod(v string) {\n\to.TokenEndpointAuthMethod = &v\n}\n\n// GetTokenEndpointAuthSigningAlg returns the TokenEndpointAuthSigningAlg field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetTokenEndpointAuthSigningAlg() string {\n\tif o == nil || IsNil(o.TokenEndpointAuthSigningAlg) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.TokenEndpointAuthSigningAlg\n}\n\n// GetTokenEndpointAuthSigningAlgOk returns a tuple with the TokenEndpointAuthSigningAlg field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetTokenEndpointAuthSigningAlgOk() (*string, bool) {\n\tif o == nil || IsNil(o.TokenEndpointAuthSigningAlg) {\n\t\treturn nil, false\n\t}\n\treturn o.TokenEndpointAuthSigningAlg, true\n}\n\n// HasTokenEndpointAuthSigningAlg returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasTokenEndpointAuthSigningAlg() bool {\n\tif o != nil && !IsNil(o.TokenEndpointAuthSigningAlg) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTokenEndpointAuthSigningAlg gets a reference to the given string and assigns it to the TokenEndpointAuthSigningAlg field.\nfunc (o *OAuth2Client) SetTokenEndpointAuthSigningAlg(v string) {\n\to.TokenEndpointAuthSigningAlg = &v\n}\n\n// GetTosUri returns the TosUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetTosUri() string {\n\tif o == nil || IsNil(o.TosUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.TosUri\n}\n\n// GetTosUriOk returns a tuple with the TosUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetTosUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.TosUri) {\n\t\treturn nil, false\n\t}\n\treturn o.TosUri, true\n}\n\n// HasTosUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasTosUri() bool {\n\tif o != nil && !IsNil(o.TosUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTosUri gets a reference to the given string and assigns it to the TosUri field.\nfunc (o *OAuth2Client) SetTosUri(v string) {\n\to.TosUri = &v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *OAuth2Client) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetUserinfoSignedResponseAlg returns the UserinfoSignedResponseAlg field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetUserinfoSignedResponseAlg() string {\n\tif o == nil || IsNil(o.UserinfoSignedResponseAlg) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.UserinfoSignedResponseAlg\n}\n\n// GetUserinfoSignedResponseAlgOk returns a tuple with the UserinfoSignedResponseAlg field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetUserinfoSignedResponseAlgOk() (*string, bool) {\n\tif o == nil || IsNil(o.UserinfoSignedResponseAlg) {\n\t\treturn nil, false\n\t}\n\treturn o.UserinfoSignedResponseAlg, true\n}\n\n// HasUserinfoSignedResponseAlg returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasUserinfoSignedResponseAlg() bool {\n\tif o != nil && !IsNil(o.UserinfoSignedResponseAlg) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUserinfoSignedResponseAlg gets a reference to the given string and assigns it to the UserinfoSignedResponseAlg field.\nfunc (o *OAuth2Client) SetUserinfoSignedResponseAlg(v string) {\n\to.UserinfoSignedResponseAlg = &v\n}\n\nfunc (o OAuth2Client) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o OAuth2Client) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.AccessTokenStrategy) {\n\t\ttoSerialize[\"access_token_strategy\"] = o.AccessTokenStrategy\n\t}\n\tif !IsNil(o.AllowedCorsOrigins) {\n\t\ttoSerialize[\"allowed_cors_origins\"] = o.AllowedCorsOrigins\n\t}\n\tif !IsNil(o.Audience) {\n\t\ttoSerialize[\"audience\"] = o.Audience\n\t}\n\tif !IsNil(o.AuthorizationCodeGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"authorization_code_grant_access_token_lifespan\"] = o.AuthorizationCodeGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.AuthorizationCodeGrantIdTokenLifespan) {\n\t\ttoSerialize[\"authorization_code_grant_id_token_lifespan\"] = o.AuthorizationCodeGrantIdTokenLifespan\n\t}\n\tif !IsNil(o.AuthorizationCodeGrantRefreshTokenLifespan) {\n\t\ttoSerialize[\"authorization_code_grant_refresh_token_lifespan\"] = o.AuthorizationCodeGrantRefreshTokenLifespan\n\t}\n\tif !IsNil(o.BackchannelLogoutSessionRequired) {\n\t\ttoSerialize[\"backchannel_logout_session_required\"] = o.BackchannelLogoutSessionRequired\n\t}\n\tif !IsNil(o.BackchannelLogoutUri) {\n\t\ttoSerialize[\"backchannel_logout_uri\"] = o.BackchannelLogoutUri\n\t}\n\tif !IsNil(o.ClientCredentialsGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"client_credentials_grant_access_token_lifespan\"] = o.ClientCredentialsGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.ClientId) {\n\t\ttoSerialize[\"client_id\"] = o.ClientId\n\t}\n\tif !IsNil(o.ClientName) {\n\t\ttoSerialize[\"client_name\"] = o.ClientName\n\t}\n\tif !IsNil(o.ClientSecret) {\n\t\ttoSerialize[\"client_secret\"] = o.ClientSecret\n\t}\n\tif !IsNil(o.ClientSecretExpiresAt) {\n\t\ttoSerialize[\"client_secret_expires_at\"] = o.ClientSecretExpiresAt\n\t}\n\tif !IsNil(o.ClientUri) {\n\t\ttoSerialize[\"client_uri\"] = o.ClientUri\n\t}\n\tif !IsNil(o.Contacts) {\n\t\ttoSerialize[\"contacts\"] = o.Contacts\n\t}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.FrontchannelLogoutSessionRequired) {\n\t\ttoSerialize[\"frontchannel_logout_session_required\"] = o.FrontchannelLogoutSessionRequired\n\t}\n\tif !IsNil(o.FrontchannelLogoutUri) {\n\t\ttoSerialize[\"frontchannel_logout_uri\"] = o.FrontchannelLogoutUri\n\t}\n\tif !IsNil(o.GrantTypes) {\n\t\ttoSerialize[\"grant_types\"] = o.GrantTypes\n\t}\n\tif !IsNil(o.ImplicitGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"implicit_grant_access_token_lifespan\"] = o.ImplicitGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.ImplicitGrantIdTokenLifespan) {\n\t\ttoSerialize[\"implicit_grant_id_token_lifespan\"] = o.ImplicitGrantIdTokenLifespan\n\t}\n\tif o.Jwks != nil {\n\t\ttoSerialize[\"jwks\"] = o.Jwks\n\t}\n\tif !IsNil(o.JwksUri) {\n\t\ttoSerialize[\"jwks_uri\"] = o.JwksUri\n\t}\n\tif !IsNil(o.JwtBearerGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"jwt_bearer_grant_access_token_lifespan\"] = o.JwtBearerGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.LogoUri) {\n\t\ttoSerialize[\"logo_uri\"] = o.LogoUri\n\t}\n\tif o.Metadata != nil {\n\t\ttoSerialize[\"metadata\"] = o.Metadata\n\t}\n\tif !IsNil(o.Owner) {\n\t\ttoSerialize[\"owner\"] = o.Owner\n\t}\n\tif !IsNil(o.PolicyUri) {\n\t\ttoSerialize[\"policy_uri\"] = o.PolicyUri\n\t}\n\tif !IsNil(o.PostLogoutRedirectUris) {\n\t\ttoSerialize[\"post_logout_redirect_uris\"] = o.PostLogoutRedirectUris\n\t}\n\tif !IsNil(o.RedirectUris) {\n\t\ttoSerialize[\"redirect_uris\"] = o.RedirectUris\n\t}\n\tif !IsNil(o.RefreshTokenGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"refresh_token_grant_access_token_lifespan\"] = o.RefreshTokenGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.RefreshTokenGrantIdTokenLifespan) {\n\t\ttoSerialize[\"refresh_token_grant_id_token_lifespan\"] = o.RefreshTokenGrantIdTokenLifespan\n\t}\n\tif !IsNil(o.RefreshTokenGrantRefreshTokenLifespan) {\n\t\ttoSerialize[\"refresh_token_grant_refresh_token_lifespan\"] = o.RefreshTokenGrantRefreshTokenLifespan\n\t}\n\tif !IsNil(o.RegistrationAccessToken) {\n\t\ttoSerialize[\"registration_access_token\"] = o.RegistrationAccessToken\n\t}\n\tif !IsNil(o.RegistrationClientUri) {\n\t\ttoSerialize[\"registration_client_uri\"] = o.RegistrationClientUri\n\t}\n\tif !IsNil(o.RequestObjectSigningAlg) {\n\t\ttoSerialize[\"request_object_signing_alg\"] = o.RequestObjectSigningAlg\n\t}\n\tif !IsNil(o.RequestUris) {\n\t\ttoSerialize[\"request_uris\"] = o.RequestUris\n\t}\n\tif !IsNil(o.ResponseTypes) {\n\t\ttoSerialize[\"response_types\"] = o.ResponseTypes\n\t}\n\tif !IsNil(o.Scope) {\n\t\ttoSerialize[\"scope\"] = o.Scope\n\t}\n\tif !IsNil(o.SectorIdentifierUri) {\n\t\ttoSerialize[\"sector_identifier_uri\"] = o.SectorIdentifierUri\n\t}\n\tif !IsNil(o.SkipConsent) {\n\t\ttoSerialize[\"skip_consent\"] = o.SkipConsent\n\t}\n\tif !IsNil(o.SkipLogoutConsent) {\n\t\ttoSerialize[\"skip_logout_consent\"] = o.SkipLogoutConsent\n\t}\n\tif !IsNil(o.SubjectType) {\n\t\ttoSerialize[\"subject_type\"] = o.SubjectType\n\t}\n\tif !IsNil(o.TokenEndpointAuthMethod) {\n\t\ttoSerialize[\"token_endpoint_auth_method\"] = o.TokenEndpointAuthMethod\n\t}\n\tif !IsNil(o.TokenEndpointAuthSigningAlg) {\n\t\ttoSerialize[\"token_endpoint_auth_signing_alg\"] = o.TokenEndpointAuthSigningAlg\n\t}\n\tif !IsNil(o.TosUri) {\n\t\ttoSerialize[\"tos_uri\"] = o.TosUri\n\t}\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\tif !IsNil(o.UserinfoSignedResponseAlg) {\n\t\ttoSerialize[\"userinfo_signed_response_alg\"] = o.UserinfoSignedResponseAlg\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *OAuth2Client) UnmarshalJSON(data []byte) (err error) {\n\tvarOAuth2Client := _OAuth2Client{}\n\n\terr = json.Unmarshal(data, &varOAuth2Client)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = OAuth2Client(varOAuth2Client)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"access_token_strategy\")\n\t\tdelete(additionalProperties, \"allowed_cors_origins\")\n\t\tdelete(additionalProperties, \"audience\")\n\t\tdelete(additionalProperties, \"authorization_code_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"authorization_code_grant_id_token_lifespan\")\n\t\tdelete(additionalProperties, \"authorization_code_grant_refresh_token_lifespan\")\n\t\tdelete(additionalProperties, \"backchannel_logout_session_required\")\n\t\tdelete(additionalProperties, \"backchannel_logout_uri\")\n\t\tdelete(additionalProperties, \"client_credentials_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"client_id\")\n\t\tdelete(additionalProperties, \"client_name\")\n\t\tdelete(additionalProperties, \"client_secret\")\n\t\tdelete(additionalProperties, \"client_secret_expires_at\")\n\t\tdelete(additionalProperties, \"client_uri\")\n\t\tdelete(additionalProperties, \"contacts\")\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"frontchannel_logout_session_required\")\n\t\tdelete(additionalProperties, \"frontchannel_logout_uri\")\n\t\tdelete(additionalProperties, \"grant_types\")\n\t\tdelete(additionalProperties, \"implicit_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"implicit_grant_id_token_lifespan\")\n\t\tdelete(additionalProperties, \"jwks\")\n\t\tdelete(additionalProperties, \"jwks_uri\")\n\t\tdelete(additionalProperties, \"jwt_bearer_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"logo_uri\")\n\t\tdelete(additionalProperties, \"metadata\")\n\t\tdelete(additionalProperties, \"owner\")\n\t\tdelete(additionalProperties, \"policy_uri\")\n\t\tdelete(additionalProperties, \"post_logout_redirect_uris\")\n\t\tdelete(additionalProperties, \"redirect_uris\")\n\t\tdelete(additionalProperties, \"refresh_token_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"refresh_token_grant_id_token_lifespan\")\n\t\tdelete(additionalProperties, \"refresh_token_grant_refresh_token_lifespan\")\n\t\tdelete(additionalProperties, \"registration_access_token\")\n\t\tdelete(additionalProperties, \"registration_client_uri\")\n\t\tdelete(additionalProperties, \"request_object_signing_alg\")\n\t\tdelete(additionalProperties, \"request_uris\")\n\t\tdelete(additionalProperties, \"response_types\")\n\t\tdelete(additionalProperties, \"scope\")\n\t\tdelete(additionalProperties, \"sector_identifier_uri\")\n\t\tdelete(additionalProperties, \"skip_consent\")\n\t\tdelete(additionalProperties, \"skip_logout_consent\")\n\t\tdelete(additionalProperties, \"subject_type\")\n\t\tdelete(additionalProperties, \"token_endpoint_auth_method\")\n\t\tdelete(additionalProperties, \"token_endpoint_auth_signing_alg\")\n\t\tdelete(additionalProperties, \"tos_uri\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"userinfo_signed_response_alg\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableOAuth2Client struct {\n\tvalue *OAuth2Client\n\tisSet bool\n}\n\nfunc (v NullableOAuth2Client) Get() *OAuth2Client {\n\treturn v.value\n}\n\nfunc (v *NullableOAuth2Client) Set(val *OAuth2Client) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableOAuth2Client) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableOAuth2Client) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableOAuth2Client(val *OAuth2Client) *NullableOAuth2Client {\n\treturn &NullableOAuth2Client{value: val, isSet: true}\n}\n\nfunc (v NullableOAuth2Client) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableOAuth2Client) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_o_auth2_consent_request_open_id_connect_context.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the OAuth2ConsentRequestOpenIDConnectContext type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &OAuth2ConsentRequestOpenIDConnectContext{}\n\n// OAuth2ConsentRequestOpenIDConnectContext OAuth2ConsentRequestOpenIDConnectContext struct for OAuth2ConsentRequestOpenIDConnectContext\ntype OAuth2ConsentRequestOpenIDConnectContext struct {\n\t// ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request. It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required.  OpenID Connect defines it as follows: > Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter.\n\tAcrValues []string `json:\"acr_values,omitempty\"`\n\t// Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are: page: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode. popup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over. touch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. wap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \\\\\\\"feature phone\\\\\\\" type display.  The Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.\n\tDisplay *string `json:\"display,omitempty\"`\n\t// IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client.\n\tIdTokenHintClaims map[string]interface{} `json:\"id_token_hint_claims,omitempty\"`\n\t// LoginHint hints about the login identifier the End-User might use to log in (if necessary). This hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier) and then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a phone number in the format specified for the phone_number Claim. The use of this parameter is optional.\n\tLoginHint *string `json:\"login_hint,omitempty\"`\n\t// UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a space-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value \\\\\\\"fr-CA fr en\\\\\\\" represents a preference for French as spoken in Canada, then French (without a region designation), followed by English (without a region designation). An error SHOULD NOT result if some or all of the requested locales are not supported by the OpenID Provider.\n\tUiLocales            []string `json:\"ui_locales,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _OAuth2ConsentRequestOpenIDConnectContext OAuth2ConsentRequestOpenIDConnectContext\n\n// NewOAuth2ConsentRequestOpenIDConnectContext instantiates a new OAuth2ConsentRequestOpenIDConnectContext object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewOAuth2ConsentRequestOpenIDConnectContext() *OAuth2ConsentRequestOpenIDConnectContext {\n\tthis := OAuth2ConsentRequestOpenIDConnectContext{}\n\treturn &this\n}\n\n// NewOAuth2ConsentRequestOpenIDConnectContextWithDefaults instantiates a new OAuth2ConsentRequestOpenIDConnectContext object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewOAuth2ConsentRequestOpenIDConnectContextWithDefaults() *OAuth2ConsentRequestOpenIDConnectContext {\n\tthis := OAuth2ConsentRequestOpenIDConnectContext{}\n\treturn &this\n}\n\n// GetAcrValues returns the AcrValues field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetAcrValues() []string {\n\tif o == nil || IsNil(o.AcrValues) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.AcrValues\n}\n\n// GetAcrValuesOk returns a tuple with the AcrValues field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetAcrValuesOk() ([]string, bool) {\n\tif o == nil || IsNil(o.AcrValues) {\n\t\treturn nil, false\n\t}\n\treturn o.AcrValues, true\n}\n\n// HasAcrValues returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasAcrValues() bool {\n\tif o != nil && !IsNil(o.AcrValues) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAcrValues gets a reference to the given []string and assigns it to the AcrValues field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetAcrValues(v []string) {\n\to.AcrValues = v\n}\n\n// GetDisplay returns the Display field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetDisplay() string {\n\tif o == nil || IsNil(o.Display) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Display\n}\n\n// GetDisplayOk returns a tuple with the Display field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetDisplayOk() (*string, bool) {\n\tif o == nil || IsNil(o.Display) {\n\t\treturn nil, false\n\t}\n\treturn o.Display, true\n}\n\n// HasDisplay returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasDisplay() bool {\n\tif o != nil && !IsNil(o.Display) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDisplay gets a reference to the given string and assigns it to the Display field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetDisplay(v string) {\n\to.Display = &v\n}\n\n// GetIdTokenHintClaims returns the IdTokenHintClaims field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetIdTokenHintClaims() map[string]interface{} {\n\tif o == nil || IsNil(o.IdTokenHintClaims) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.IdTokenHintClaims\n}\n\n// GetIdTokenHintClaimsOk returns a tuple with the IdTokenHintClaims field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetIdTokenHintClaimsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.IdTokenHintClaims) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.IdTokenHintClaims, true\n}\n\n// HasIdTokenHintClaims returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasIdTokenHintClaims() bool {\n\tif o != nil && !IsNil(o.IdTokenHintClaims) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdTokenHintClaims gets a reference to the given map[string]interface{} and assigns it to the IdTokenHintClaims field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetIdTokenHintClaims(v map[string]interface{}) {\n\to.IdTokenHintClaims = v\n}\n\n// GetLoginHint returns the LoginHint field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetLoginHint() string {\n\tif o == nil || IsNil(o.LoginHint) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.LoginHint\n}\n\n// GetLoginHintOk returns a tuple with the LoginHint field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetLoginHintOk() (*string, bool) {\n\tif o == nil || IsNil(o.LoginHint) {\n\t\treturn nil, false\n\t}\n\treturn o.LoginHint, true\n}\n\n// HasLoginHint returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasLoginHint() bool {\n\tif o != nil && !IsNil(o.LoginHint) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLoginHint gets a reference to the given string and assigns it to the LoginHint field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetLoginHint(v string) {\n\to.LoginHint = &v\n}\n\n// GetUiLocales returns the UiLocales field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetUiLocales() []string {\n\tif o == nil || IsNil(o.UiLocales) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.UiLocales\n}\n\n// GetUiLocalesOk returns a tuple with the UiLocales field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetUiLocalesOk() ([]string, bool) {\n\tif o == nil || IsNil(o.UiLocales) {\n\t\treturn nil, false\n\t}\n\treturn o.UiLocales, true\n}\n\n// HasUiLocales returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasUiLocales() bool {\n\tif o != nil && !IsNil(o.UiLocales) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUiLocales gets a reference to the given []string and assigns it to the UiLocales field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetUiLocales(v []string) {\n\to.UiLocales = v\n}\n\nfunc (o OAuth2ConsentRequestOpenIDConnectContext) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o OAuth2ConsentRequestOpenIDConnectContext) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.AcrValues) {\n\t\ttoSerialize[\"acr_values\"] = o.AcrValues\n\t}\n\tif !IsNil(o.Display) {\n\t\ttoSerialize[\"display\"] = o.Display\n\t}\n\tif !IsNil(o.IdTokenHintClaims) {\n\t\ttoSerialize[\"id_token_hint_claims\"] = o.IdTokenHintClaims\n\t}\n\tif !IsNil(o.LoginHint) {\n\t\ttoSerialize[\"login_hint\"] = o.LoginHint\n\t}\n\tif !IsNil(o.UiLocales) {\n\t\ttoSerialize[\"ui_locales\"] = o.UiLocales\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) UnmarshalJSON(data []byte) (err error) {\n\tvarOAuth2ConsentRequestOpenIDConnectContext := _OAuth2ConsentRequestOpenIDConnectContext{}\n\n\terr = json.Unmarshal(data, &varOAuth2ConsentRequestOpenIDConnectContext)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = OAuth2ConsentRequestOpenIDConnectContext(varOAuth2ConsentRequestOpenIDConnectContext)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"acr_values\")\n\t\tdelete(additionalProperties, \"display\")\n\t\tdelete(additionalProperties, \"id_token_hint_claims\")\n\t\tdelete(additionalProperties, \"login_hint\")\n\t\tdelete(additionalProperties, \"ui_locales\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableOAuth2ConsentRequestOpenIDConnectContext struct {\n\tvalue *OAuth2ConsentRequestOpenIDConnectContext\n\tisSet bool\n}\n\nfunc (v NullableOAuth2ConsentRequestOpenIDConnectContext) Get() *OAuth2ConsentRequestOpenIDConnectContext {\n\treturn v.value\n}\n\nfunc (v *NullableOAuth2ConsentRequestOpenIDConnectContext) Set(val *OAuth2ConsentRequestOpenIDConnectContext) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableOAuth2ConsentRequestOpenIDConnectContext) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableOAuth2ConsentRequestOpenIDConnectContext) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableOAuth2ConsentRequestOpenIDConnectContext(val *OAuth2ConsentRequestOpenIDConnectContext) *NullableOAuth2ConsentRequestOpenIDConnectContext {\n\treturn &NullableOAuth2ConsentRequestOpenIDConnectContext{value: val, isSet: true}\n}\n\nfunc (v NullableOAuth2ConsentRequestOpenIDConnectContext) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableOAuth2ConsentRequestOpenIDConnectContext) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_o_auth2_login_request.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the OAuth2LoginRequest type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &OAuth2LoginRequest{}\n\n// OAuth2LoginRequest OAuth2LoginRequest struct for OAuth2LoginRequest\ntype OAuth2LoginRequest struct {\n\t// ID is the identifier (\\\\\\\"login challenge\\\\\\\") of the login request. It is used to identify the session.\n\tChallenge   *string                                   `json:\"challenge,omitempty\"`\n\tClient      *OAuth2Client                             `json:\"client,omitempty\"`\n\tOidcContext *OAuth2ConsentRequestOpenIDConnectContext `json:\"oidc_context,omitempty\"`\n\t// RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters.\n\tRequestUrl                   *string  `json:\"request_url,omitempty\"`\n\tRequestedAccessTokenAudience []string `json:\"requested_access_token_audience,omitempty\"`\n\tRequestedScope               []string `json:\"requested_scope,omitempty\"`\n\t// SessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag) this ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false) this will be a new random value. This value is used as the \\\\\\\"sid\\\\\\\" parameter in the ID Token and in OIDC Front-/Back- channel logout. It's value can generally be used to associate consecutive login requests by a certain user.\n\tSessionId *string `json:\"session_id,omitempty\"`\n\t// Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL.  This feature allows you to update / set session information.\n\tSkip *bool `json:\"skip,omitempty\"`\n\t// Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail.\n\tSubject              *string `json:\"subject,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _OAuth2LoginRequest OAuth2LoginRequest\n\n// NewOAuth2LoginRequest instantiates a new OAuth2LoginRequest object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewOAuth2LoginRequest() *OAuth2LoginRequest {\n\tthis := OAuth2LoginRequest{}\n\treturn &this\n}\n\n// NewOAuth2LoginRequestWithDefaults instantiates a new OAuth2LoginRequest object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewOAuth2LoginRequestWithDefaults() *OAuth2LoginRequest {\n\tthis := OAuth2LoginRequest{}\n\treturn &this\n}\n\n// GetChallenge returns the Challenge field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetChallenge() string {\n\tif o == nil || IsNil(o.Challenge) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Challenge\n}\n\n// GetChallengeOk returns a tuple with the Challenge field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetChallengeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Challenge) {\n\t\treturn nil, false\n\t}\n\treturn o.Challenge, true\n}\n\n// HasChallenge returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasChallenge() bool {\n\tif o != nil && !IsNil(o.Challenge) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetChallenge gets a reference to the given string and assigns it to the Challenge field.\nfunc (o *OAuth2LoginRequest) SetChallenge(v string) {\n\to.Challenge = &v\n}\n\n// GetClient returns the Client field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetClient() OAuth2Client {\n\tif o == nil || IsNil(o.Client) {\n\t\tvar ret OAuth2Client\n\t\treturn ret\n\t}\n\treturn *o.Client\n}\n\n// GetClientOk returns a tuple with the Client field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetClientOk() (*OAuth2Client, bool) {\n\tif o == nil || IsNil(o.Client) {\n\t\treturn nil, false\n\t}\n\treturn o.Client, true\n}\n\n// HasClient returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasClient() bool {\n\tif o != nil && !IsNil(o.Client) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClient gets a reference to the given OAuth2Client and assigns it to the Client field.\nfunc (o *OAuth2LoginRequest) SetClient(v OAuth2Client) {\n\to.Client = &v\n}\n\n// GetOidcContext returns the OidcContext field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetOidcContext() OAuth2ConsentRequestOpenIDConnectContext {\n\tif o == nil || IsNil(o.OidcContext) {\n\t\tvar ret OAuth2ConsentRequestOpenIDConnectContext\n\t\treturn ret\n\t}\n\treturn *o.OidcContext\n}\n\n// GetOidcContextOk returns a tuple with the OidcContext field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetOidcContextOk() (*OAuth2ConsentRequestOpenIDConnectContext, bool) {\n\tif o == nil || IsNil(o.OidcContext) {\n\t\treturn nil, false\n\t}\n\treturn o.OidcContext, true\n}\n\n// HasOidcContext returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasOidcContext() bool {\n\tif o != nil && !IsNil(o.OidcContext) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOidcContext gets a reference to the given OAuth2ConsentRequestOpenIDConnectContext and assigns it to the OidcContext field.\nfunc (o *OAuth2LoginRequest) SetOidcContext(v OAuth2ConsentRequestOpenIDConnectContext) {\n\to.OidcContext = &v\n}\n\n// GetRequestUrl returns the RequestUrl field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetRequestUrl() string {\n\tif o == nil || IsNil(o.RequestUrl) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetRequestUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.RequestUrl) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestUrl, true\n}\n\n// HasRequestUrl returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasRequestUrl() bool {\n\tif o != nil && !IsNil(o.RequestUrl) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestUrl gets a reference to the given string and assigns it to the RequestUrl field.\nfunc (o *OAuth2LoginRequest) SetRequestUrl(v string) {\n\to.RequestUrl = &v\n}\n\n// GetRequestedAccessTokenAudience returns the RequestedAccessTokenAudience field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetRequestedAccessTokenAudience() []string {\n\tif o == nil || IsNil(o.RequestedAccessTokenAudience) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.RequestedAccessTokenAudience\n}\n\n// GetRequestedAccessTokenAudienceOk returns a tuple with the RequestedAccessTokenAudience field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetRequestedAccessTokenAudienceOk() ([]string, bool) {\n\tif o == nil || IsNil(o.RequestedAccessTokenAudience) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestedAccessTokenAudience, true\n}\n\n// HasRequestedAccessTokenAudience returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasRequestedAccessTokenAudience() bool {\n\tif o != nil && !IsNil(o.RequestedAccessTokenAudience) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestedAccessTokenAudience gets a reference to the given []string and assigns it to the RequestedAccessTokenAudience field.\nfunc (o *OAuth2LoginRequest) SetRequestedAccessTokenAudience(v []string) {\n\to.RequestedAccessTokenAudience = v\n}\n\n// GetRequestedScope returns the RequestedScope field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetRequestedScope() []string {\n\tif o == nil || IsNil(o.RequestedScope) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.RequestedScope\n}\n\n// GetRequestedScopeOk returns a tuple with the RequestedScope field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetRequestedScopeOk() ([]string, bool) {\n\tif o == nil || IsNil(o.RequestedScope) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestedScope, true\n}\n\n// HasRequestedScope returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasRequestedScope() bool {\n\tif o != nil && !IsNil(o.RequestedScope) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestedScope gets a reference to the given []string and assigns it to the RequestedScope field.\nfunc (o *OAuth2LoginRequest) SetRequestedScope(v []string) {\n\to.RequestedScope = v\n}\n\n// GetSessionId returns the SessionId field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetSessionId() string {\n\tif o == nil || IsNil(o.SessionId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionId\n}\n\n// GetSessionIdOk returns a tuple with the SessionId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetSessionIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionId) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionId, true\n}\n\n// HasSessionId returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasSessionId() bool {\n\tif o != nil && !IsNil(o.SessionId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionId gets a reference to the given string and assigns it to the SessionId field.\nfunc (o *OAuth2LoginRequest) SetSessionId(v string) {\n\to.SessionId = &v\n}\n\n// GetSkip returns the Skip field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetSkip() bool {\n\tif o == nil || IsNil(o.Skip) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.Skip\n}\n\n// GetSkipOk returns a tuple with the Skip field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetSkipOk() (*bool, bool) {\n\tif o == nil || IsNil(o.Skip) {\n\t\treturn nil, false\n\t}\n\treturn o.Skip, true\n}\n\n// HasSkip returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasSkip() bool {\n\tif o != nil && !IsNil(o.Skip) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSkip gets a reference to the given bool and assigns it to the Skip field.\nfunc (o *OAuth2LoginRequest) SetSkip(v bool) {\n\to.Skip = &v\n}\n\n// GetSubject returns the Subject field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetSubject() string {\n\tif o == nil || IsNil(o.Subject) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetSubjectOk() (*string, bool) {\n\tif o == nil || IsNil(o.Subject) {\n\t\treturn nil, false\n\t}\n\treturn o.Subject, true\n}\n\n// HasSubject returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasSubject() bool {\n\tif o != nil && !IsNil(o.Subject) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSubject gets a reference to the given string and assigns it to the Subject field.\nfunc (o *OAuth2LoginRequest) SetSubject(v string) {\n\to.Subject = &v\n}\n\nfunc (o OAuth2LoginRequest) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o OAuth2LoginRequest) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Challenge) {\n\t\ttoSerialize[\"challenge\"] = o.Challenge\n\t}\n\tif !IsNil(o.Client) {\n\t\ttoSerialize[\"client\"] = o.Client\n\t}\n\tif !IsNil(o.OidcContext) {\n\t\ttoSerialize[\"oidc_context\"] = o.OidcContext\n\t}\n\tif !IsNil(o.RequestUrl) {\n\t\ttoSerialize[\"request_url\"] = o.RequestUrl\n\t}\n\tif !IsNil(o.RequestedAccessTokenAudience) {\n\t\ttoSerialize[\"requested_access_token_audience\"] = o.RequestedAccessTokenAudience\n\t}\n\tif !IsNil(o.RequestedScope) {\n\t\ttoSerialize[\"requested_scope\"] = o.RequestedScope\n\t}\n\tif !IsNil(o.SessionId) {\n\t\ttoSerialize[\"session_id\"] = o.SessionId\n\t}\n\tif !IsNil(o.Skip) {\n\t\ttoSerialize[\"skip\"] = o.Skip\n\t}\n\tif !IsNil(o.Subject) {\n\t\ttoSerialize[\"subject\"] = o.Subject\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *OAuth2LoginRequest) UnmarshalJSON(data []byte) (err error) {\n\tvarOAuth2LoginRequest := _OAuth2LoginRequest{}\n\n\terr = json.Unmarshal(data, &varOAuth2LoginRequest)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = OAuth2LoginRequest(varOAuth2LoginRequest)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"challenge\")\n\t\tdelete(additionalProperties, \"client\")\n\t\tdelete(additionalProperties, \"oidc_context\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"requested_access_token_audience\")\n\t\tdelete(additionalProperties, \"requested_scope\")\n\t\tdelete(additionalProperties, \"session_id\")\n\t\tdelete(additionalProperties, \"skip\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableOAuth2LoginRequest struct {\n\tvalue *OAuth2LoginRequest\n\tisSet bool\n}\n\nfunc (v NullableOAuth2LoginRequest) Get() *OAuth2LoginRequest {\n\treturn v.value\n}\n\nfunc (v *NullableOAuth2LoginRequest) Set(val *OAuth2LoginRequest) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableOAuth2LoginRequest) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableOAuth2LoginRequest) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableOAuth2LoginRequest(val *OAuth2LoginRequest) *NullableOAuth2LoginRequest {\n\treturn &NullableOAuth2LoginRequest{value: val, isSet: true}\n}\n\nfunc (v NullableOAuth2LoginRequest) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableOAuth2LoginRequest) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_patch_identities_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the PatchIdentitiesBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &PatchIdentitiesBody{}\n\n// PatchIdentitiesBody Patch Identities Body\ntype PatchIdentitiesBody struct {\n\t// Identities holds the list of patches to apply  required\n\tIdentities           []IdentityPatch `json:\"identities,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _PatchIdentitiesBody PatchIdentitiesBody\n\n// NewPatchIdentitiesBody instantiates a new PatchIdentitiesBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewPatchIdentitiesBody() *PatchIdentitiesBody {\n\tthis := PatchIdentitiesBody{}\n\treturn &this\n}\n\n// NewPatchIdentitiesBodyWithDefaults instantiates a new PatchIdentitiesBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewPatchIdentitiesBodyWithDefaults() *PatchIdentitiesBody {\n\tthis := PatchIdentitiesBody{}\n\treturn &this\n}\n\n// GetIdentities returns the Identities field value if set, zero value otherwise.\nfunc (o *PatchIdentitiesBody) GetIdentities() []IdentityPatch {\n\tif o == nil || IsNil(o.Identities) {\n\t\tvar ret []IdentityPatch\n\t\treturn ret\n\t}\n\treturn o.Identities\n}\n\n// GetIdentitiesOk returns a tuple with the Identities field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *PatchIdentitiesBody) GetIdentitiesOk() ([]IdentityPatch, bool) {\n\tif o == nil || IsNil(o.Identities) {\n\t\treturn nil, false\n\t}\n\treturn o.Identities, true\n}\n\n// HasIdentities returns a boolean if a field has been set.\nfunc (o *PatchIdentitiesBody) HasIdentities() bool {\n\tif o != nil && !IsNil(o.Identities) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentities gets a reference to the given []IdentityPatch and assigns it to the Identities field.\nfunc (o *PatchIdentitiesBody) SetIdentities(v []IdentityPatch) {\n\to.Identities = v\n}\n\nfunc (o PatchIdentitiesBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o PatchIdentitiesBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Identities) {\n\t\ttoSerialize[\"identities\"] = o.Identities\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *PatchIdentitiesBody) UnmarshalJSON(data []byte) (err error) {\n\tvarPatchIdentitiesBody := _PatchIdentitiesBody{}\n\n\terr = json.Unmarshal(data, &varPatchIdentitiesBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = PatchIdentitiesBody(varPatchIdentitiesBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"identities\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullablePatchIdentitiesBody struct {\n\tvalue *PatchIdentitiesBody\n\tisSet bool\n}\n\nfunc (v NullablePatchIdentitiesBody) Get() *PatchIdentitiesBody {\n\treturn v.value\n}\n\nfunc (v *NullablePatchIdentitiesBody) Set(val *PatchIdentitiesBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullablePatchIdentitiesBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullablePatchIdentitiesBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullablePatchIdentitiesBody(val *PatchIdentitiesBody) *NullablePatchIdentitiesBody {\n\treturn &NullablePatchIdentitiesBody{value: val, isSet: true}\n}\n\nfunc (v NullablePatchIdentitiesBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullablePatchIdentitiesBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_perform_native_logout_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the PerformNativeLogoutBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &PerformNativeLogoutBody{}\n\n// PerformNativeLogoutBody Perform Native Logout Request Body\ntype PerformNativeLogoutBody struct {\n\t// The Session Token  Invalidate this session token.\n\tSessionToken         string `json:\"session_token\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _PerformNativeLogoutBody PerformNativeLogoutBody\n\n// NewPerformNativeLogoutBody instantiates a new PerformNativeLogoutBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewPerformNativeLogoutBody(sessionToken string) *PerformNativeLogoutBody {\n\tthis := PerformNativeLogoutBody{}\n\tthis.SessionToken = sessionToken\n\treturn &this\n}\n\n// NewPerformNativeLogoutBodyWithDefaults instantiates a new PerformNativeLogoutBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewPerformNativeLogoutBodyWithDefaults() *PerformNativeLogoutBody {\n\tthis := PerformNativeLogoutBody{}\n\treturn &this\n}\n\n// GetSessionToken returns the SessionToken field value\nfunc (o *PerformNativeLogoutBody) GetSessionToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SessionToken\n}\n\n// GetSessionTokenOk returns a tuple with the SessionToken field value\n// and a boolean to check if the value has been set.\nfunc (o *PerformNativeLogoutBody) GetSessionTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SessionToken, true\n}\n\n// SetSessionToken sets field value\nfunc (o *PerformNativeLogoutBody) SetSessionToken(v string) {\n\to.SessionToken = v\n}\n\nfunc (o PerformNativeLogoutBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o PerformNativeLogoutBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"session_token\"] = o.SessionToken\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *PerformNativeLogoutBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"session_token\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarPerformNativeLogoutBody := _PerformNativeLogoutBody{}\n\n\terr = json.Unmarshal(data, &varPerformNativeLogoutBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = PerformNativeLogoutBody(varPerformNativeLogoutBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullablePerformNativeLogoutBody struct {\n\tvalue *PerformNativeLogoutBody\n\tisSet bool\n}\n\nfunc (v NullablePerformNativeLogoutBody) Get() *PerformNativeLogoutBody {\n\treturn v.value\n}\n\nfunc (v *NullablePerformNativeLogoutBody) Set(val *PerformNativeLogoutBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullablePerformNativeLogoutBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullablePerformNativeLogoutBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullablePerformNativeLogoutBody(val *PerformNativeLogoutBody) *NullablePerformNativeLogoutBody {\n\treturn &NullablePerformNativeLogoutBody{value: val, isSet: true}\n}\n\nfunc (v NullablePerformNativeLogoutBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullablePerformNativeLogoutBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_provider.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the Provider type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Provider{}\n\n// Provider struct for Provider\ntype Provider struct {\n\t// The RP's client identifier, issued by the IdP.\n\tClientId *string `json:\"client_id,omitempty\"`\n\t// A full path of the IdP config file.\n\tConfigUrl *string `json:\"config_url,omitempty\"`\n\t// By specifying one of domain_hints values provided by the accounts endpoints, the FedCM dialog selectively shows the specified account.\n\tDomainHint *string `json:\"domain_hint,omitempty\"`\n\t// Array of strings that specifies the user information (\\\"name\\\", \\\" email\\\", \\\"picture\\\") that RP needs IdP to share with them.  Note: Field API is supported by Chrome 132 and later.\n\tFields []string `json:\"fields,omitempty\"`\n\t// By specifying one of login_hints values provided by the accounts endpoints, the FedCM dialog selectively shows the specified account.\n\tLoginHint *string `json:\"login_hint,omitempty\"`\n\t// A random string to ensure the response is issued for this specific request. Prevents replay attacks.\n\tNonce *string `json:\"nonce,omitempty\"`\n\t// Custom object that allows to specify additional key-value parameters: scope: A string value containing additional permissions that RP needs to request, for example \\\" drive.readonly calendar.readonly\\\" nonce: A random string to ensure the response is issued for this specific request. Prevents replay attacks.  Other custom key-value parameters.  Note: parameters is supported from Chrome 132.\n\tParameters           *map[string]string `json:\"parameters,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Provider Provider\n\n// NewProvider instantiates a new Provider object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewProvider() *Provider {\n\tthis := Provider{}\n\treturn &this\n}\n\n// NewProviderWithDefaults instantiates a new Provider object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewProviderWithDefaults() *Provider {\n\tthis := Provider{}\n\treturn &this\n}\n\n// GetClientId returns the ClientId field value if set, zero value otherwise.\nfunc (o *Provider) GetClientId() string {\n\tif o == nil || IsNil(o.ClientId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientId\n}\n\n// GetClientIdOk returns a tuple with the ClientId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetClientIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientId) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientId, true\n}\n\n// HasClientId returns a boolean if a field has been set.\nfunc (o *Provider) HasClientId() bool {\n\tif o != nil && !IsNil(o.ClientId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientId gets a reference to the given string and assigns it to the ClientId field.\nfunc (o *Provider) SetClientId(v string) {\n\to.ClientId = &v\n}\n\n// GetConfigUrl returns the ConfigUrl field value if set, zero value otherwise.\nfunc (o *Provider) GetConfigUrl() string {\n\tif o == nil || IsNil(o.ConfigUrl) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ConfigUrl\n}\n\n// GetConfigUrlOk returns a tuple with the ConfigUrl field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetConfigUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.ConfigUrl) {\n\t\treturn nil, false\n\t}\n\treturn o.ConfigUrl, true\n}\n\n// HasConfigUrl returns a boolean if a field has been set.\nfunc (o *Provider) HasConfigUrl() bool {\n\tif o != nil && !IsNil(o.ConfigUrl) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfigUrl gets a reference to the given string and assigns it to the ConfigUrl field.\nfunc (o *Provider) SetConfigUrl(v string) {\n\to.ConfigUrl = &v\n}\n\n// GetDomainHint returns the DomainHint field value if set, zero value otherwise.\nfunc (o *Provider) GetDomainHint() string {\n\tif o == nil || IsNil(o.DomainHint) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.DomainHint\n}\n\n// GetDomainHintOk returns a tuple with the DomainHint field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetDomainHintOk() (*string, bool) {\n\tif o == nil || IsNil(o.DomainHint) {\n\t\treturn nil, false\n\t}\n\treturn o.DomainHint, true\n}\n\n// HasDomainHint returns a boolean if a field has been set.\nfunc (o *Provider) HasDomainHint() bool {\n\tif o != nil && !IsNil(o.DomainHint) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDomainHint gets a reference to the given string and assigns it to the DomainHint field.\nfunc (o *Provider) SetDomainHint(v string) {\n\to.DomainHint = &v\n}\n\n// GetFields returns the Fields field value if set, zero value otherwise.\nfunc (o *Provider) GetFields() []string {\n\tif o == nil || IsNil(o.Fields) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.Fields\n}\n\n// GetFieldsOk returns a tuple with the Fields field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetFieldsOk() ([]string, bool) {\n\tif o == nil || IsNil(o.Fields) {\n\t\treturn nil, false\n\t}\n\treturn o.Fields, true\n}\n\n// HasFields returns a boolean if a field has been set.\nfunc (o *Provider) HasFields() bool {\n\tif o != nil && !IsNil(o.Fields) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFields gets a reference to the given []string and assigns it to the Fields field.\nfunc (o *Provider) SetFields(v []string) {\n\to.Fields = v\n}\n\n// GetLoginHint returns the LoginHint field value if set, zero value otherwise.\nfunc (o *Provider) GetLoginHint() string {\n\tif o == nil || IsNil(o.LoginHint) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.LoginHint\n}\n\n// GetLoginHintOk returns a tuple with the LoginHint field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetLoginHintOk() (*string, bool) {\n\tif o == nil || IsNil(o.LoginHint) {\n\t\treturn nil, false\n\t}\n\treturn o.LoginHint, true\n}\n\n// HasLoginHint returns a boolean if a field has been set.\nfunc (o *Provider) HasLoginHint() bool {\n\tif o != nil && !IsNil(o.LoginHint) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLoginHint gets a reference to the given string and assigns it to the LoginHint field.\nfunc (o *Provider) SetLoginHint(v string) {\n\to.LoginHint = &v\n}\n\n// GetNonce returns the Nonce field value if set, zero value otherwise.\nfunc (o *Provider) GetNonce() string {\n\tif o == nil || IsNil(o.Nonce) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Nonce\n}\n\n// GetNonceOk returns a tuple with the Nonce field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetNonceOk() (*string, bool) {\n\tif o == nil || IsNil(o.Nonce) {\n\t\treturn nil, false\n\t}\n\treturn o.Nonce, true\n}\n\n// HasNonce returns a boolean if a field has been set.\nfunc (o *Provider) HasNonce() bool {\n\tif o != nil && !IsNil(o.Nonce) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetNonce gets a reference to the given string and assigns it to the Nonce field.\nfunc (o *Provider) SetNonce(v string) {\n\to.Nonce = &v\n}\n\n// GetParameters returns the Parameters field value if set, zero value otherwise.\nfunc (o *Provider) GetParameters() map[string]string {\n\tif o == nil || IsNil(o.Parameters) {\n\t\tvar ret map[string]string\n\t\treturn ret\n\t}\n\treturn *o.Parameters\n}\n\n// GetParametersOk returns a tuple with the Parameters field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetParametersOk() (*map[string]string, bool) {\n\tif o == nil || IsNil(o.Parameters) {\n\t\treturn nil, false\n\t}\n\treturn o.Parameters, true\n}\n\n// HasParameters returns a boolean if a field has been set.\nfunc (o *Provider) HasParameters() bool {\n\tif o != nil && !IsNil(o.Parameters) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetParameters gets a reference to the given map[string]string and assigns it to the Parameters field.\nfunc (o *Provider) SetParameters(v map[string]string) {\n\to.Parameters = &v\n}\n\nfunc (o Provider) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Provider) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ClientId) {\n\t\ttoSerialize[\"client_id\"] = o.ClientId\n\t}\n\tif !IsNil(o.ConfigUrl) {\n\t\ttoSerialize[\"config_url\"] = o.ConfigUrl\n\t}\n\tif !IsNil(o.DomainHint) {\n\t\ttoSerialize[\"domain_hint\"] = o.DomainHint\n\t}\n\tif !IsNil(o.Fields) {\n\t\ttoSerialize[\"fields\"] = o.Fields\n\t}\n\tif !IsNil(o.LoginHint) {\n\t\ttoSerialize[\"login_hint\"] = o.LoginHint\n\t}\n\tif !IsNil(o.Nonce) {\n\t\ttoSerialize[\"nonce\"] = o.Nonce\n\t}\n\tif !IsNil(o.Parameters) {\n\t\ttoSerialize[\"parameters\"] = o.Parameters\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Provider) UnmarshalJSON(data []byte) (err error) {\n\tvarProvider := _Provider{}\n\n\terr = json.Unmarshal(data, &varProvider)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Provider(varProvider)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"client_id\")\n\t\tdelete(additionalProperties, \"config_url\")\n\t\tdelete(additionalProperties, \"domain_hint\")\n\t\tdelete(additionalProperties, \"fields\")\n\t\tdelete(additionalProperties, \"login_hint\")\n\t\tdelete(additionalProperties, \"nonce\")\n\t\tdelete(additionalProperties, \"parameters\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableProvider struct {\n\tvalue *Provider\n\tisSet bool\n}\n\nfunc (v NullableProvider) Get() *Provider {\n\treturn v.value\n}\n\nfunc (v *NullableProvider) Set(val *Provider) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableProvider) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableProvider) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableProvider(val *Provider) *NullableProvider {\n\treturn &NullableProvider{value: val, isSet: true}\n}\n\nfunc (v NullableProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableProvider) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_recovery_code_for_identity.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RecoveryCodeForIdentity type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RecoveryCodeForIdentity{}\n\n// RecoveryCodeForIdentity Used when an administrator creates a recovery code for an identity.\ntype RecoveryCodeForIdentity struct {\n\t// Expires At is the timestamp of when the recovery flow expires  The timestamp when the recovery code expires.\n\tExpiresAt *time.Time `json:\"expires_at,omitempty\"`\n\t// RecoveryCode is the code that can be used to recover the account\n\tRecoveryCode string `json:\"recovery_code\"`\n\t// RecoveryLink with flow  This link opens the recovery UI with an empty `code` field.\n\tRecoveryLink         string `json:\"recovery_link\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RecoveryCodeForIdentity RecoveryCodeForIdentity\n\n// NewRecoveryCodeForIdentity instantiates a new RecoveryCodeForIdentity object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRecoveryCodeForIdentity(recoveryCode string, recoveryLink string) *RecoveryCodeForIdentity {\n\tthis := RecoveryCodeForIdentity{}\n\tthis.RecoveryCode = recoveryCode\n\tthis.RecoveryLink = recoveryLink\n\treturn &this\n}\n\n// NewRecoveryCodeForIdentityWithDefaults instantiates a new RecoveryCodeForIdentity object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRecoveryCodeForIdentityWithDefaults() *RecoveryCodeForIdentity {\n\tthis := RecoveryCodeForIdentity{}\n\treturn &this\n}\n\n// GetExpiresAt returns the ExpiresAt field value if set, zero value otherwise.\nfunc (o *RecoveryCodeForIdentity) GetExpiresAt() time.Time {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryCodeForIdentity) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresAt, true\n}\n\n// HasExpiresAt returns a boolean if a field has been set.\nfunc (o *RecoveryCodeForIdentity) HasExpiresAt() bool {\n\tif o != nil && !IsNil(o.ExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresAt gets a reference to the given time.Time and assigns it to the ExpiresAt field.\nfunc (o *RecoveryCodeForIdentity) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = &v\n}\n\n// GetRecoveryCode returns the RecoveryCode field value\nfunc (o *RecoveryCodeForIdentity) GetRecoveryCode() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RecoveryCode\n}\n\n// GetRecoveryCodeOk returns a tuple with the RecoveryCode field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryCodeForIdentity) GetRecoveryCodeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RecoveryCode, true\n}\n\n// SetRecoveryCode sets field value\nfunc (o *RecoveryCodeForIdentity) SetRecoveryCode(v string) {\n\to.RecoveryCode = v\n}\n\n// GetRecoveryLink returns the RecoveryLink field value\nfunc (o *RecoveryCodeForIdentity) GetRecoveryLink() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RecoveryLink\n}\n\n// GetRecoveryLinkOk returns a tuple with the RecoveryLink field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryCodeForIdentity) GetRecoveryLinkOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RecoveryLink, true\n}\n\n// SetRecoveryLink sets field value\nfunc (o *RecoveryCodeForIdentity) SetRecoveryLink(v string) {\n\to.RecoveryLink = v\n}\n\nfunc (o RecoveryCodeForIdentity) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RecoveryCodeForIdentity) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ExpiresAt) {\n\t\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\t}\n\ttoSerialize[\"recovery_code\"] = o.RecoveryCode\n\ttoSerialize[\"recovery_link\"] = o.RecoveryLink\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RecoveryCodeForIdentity) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"recovery_code\",\n\t\t\"recovery_link\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRecoveryCodeForIdentity := _RecoveryCodeForIdentity{}\n\n\terr = json.Unmarshal(data, &varRecoveryCodeForIdentity)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RecoveryCodeForIdentity(varRecoveryCodeForIdentity)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"recovery_code\")\n\t\tdelete(additionalProperties, \"recovery_link\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRecoveryCodeForIdentity struct {\n\tvalue *RecoveryCodeForIdentity\n\tisSet bool\n}\n\nfunc (v NullableRecoveryCodeForIdentity) Get() *RecoveryCodeForIdentity {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryCodeForIdentity) Set(val *RecoveryCodeForIdentity) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryCodeForIdentity) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryCodeForIdentity) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryCodeForIdentity(val *RecoveryCodeForIdentity) *NullableRecoveryCodeForIdentity {\n\treturn &NullableRecoveryCodeForIdentity{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryCodeForIdentity) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryCodeForIdentity) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_recovery_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RecoveryFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RecoveryFlow{}\n\n// RecoveryFlow This request is used when an identity wants to recover their account.  We recommend reading the [Account Recovery Documentation](../self-service/flows/password-reset-account-recovery)\ntype RecoveryFlow struct {\n\t// Active, if set, contains the recovery method that is being used. It is initially not set.\n\tActive *string `json:\"active,omitempty\"`\n\t// Contains possible actions that could follow this flow\n\tContinueWith []ContinueWith `json:\"continue_with,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the request expires. If the user still wishes to update the setting, a new request has to be initiated.\n\tExpiresAt time.Time `json:\"expires_at\"`\n\t// ID represents the request's unique ID. When performing the recovery flow, this represents the id in the recovery ui's query parameter: http://<selfservice.flows.recovery.ui_url>?request=<id>\n\tId string `json:\"id\"`\n\t// IssuedAt is the time (UTC) when the request occurred.\n\tIssuedAt time.Time `json:\"issued_at\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl string `json:\"request_url\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// State represents the state of this request:  choose_method: ask the user to choose a method (e.g. recover account via email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the recovery challenge was passed.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the recovery flow to hooks and email templates\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType                 string      `json:\"type\"`\n\tUi                   UiContainer `json:\"ui\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RecoveryFlow RecoveryFlow\n\n// NewRecoveryFlow instantiates a new RecoveryFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRecoveryFlow(expiresAt time.Time, id string, issuedAt time.Time, requestUrl string, state interface{}, type_ string, ui UiContainer) *RecoveryFlow {\n\tthis := RecoveryFlow{}\n\tthis.ExpiresAt = expiresAt\n\tthis.Id = id\n\tthis.IssuedAt = issuedAt\n\tthis.RequestUrl = requestUrl\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewRecoveryFlowWithDefaults instantiates a new RecoveryFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRecoveryFlowWithDefaults() *RecoveryFlow {\n\tthis := RecoveryFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *RecoveryFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *RecoveryFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *RecoveryFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetContinueWith returns the ContinueWith field value if set, zero value otherwise.\nfunc (o *RecoveryFlow) GetContinueWith() []ContinueWith {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\tvar ret []ContinueWith\n\t\treturn ret\n\t}\n\treturn o.ContinueWith\n}\n\n// GetContinueWithOk returns a tuple with the ContinueWith field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetContinueWithOk() ([]ContinueWith, bool) {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\treturn nil, false\n\t}\n\treturn o.ContinueWith, true\n}\n\n// HasContinueWith returns a boolean if a field has been set.\nfunc (o *RecoveryFlow) HasContinueWith() bool {\n\tif o != nil && !IsNil(o.ContinueWith) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContinueWith gets a reference to the given []ContinueWith and assigns it to the ContinueWith field.\nfunc (o *RecoveryFlow) SetContinueWith(v []ContinueWith) {\n\to.ContinueWith = v\n}\n\n// GetExpiresAt returns the ExpiresAt field value\nfunc (o *RecoveryFlow) GetExpiresAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.ExpiresAt, true\n}\n\n// SetExpiresAt sets field value\nfunc (o *RecoveryFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = v\n}\n\n// GetId returns the Id field value\nfunc (o *RecoveryFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *RecoveryFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIssuedAt returns the IssuedAt field value\nfunc (o *RecoveryFlow) GetIssuedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IssuedAt, true\n}\n\n// SetIssuedAt sets field value\nfunc (o *RecoveryFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = v\n}\n\n// GetRequestUrl returns the RequestUrl field value\nfunc (o *RecoveryFlow) GetRequestUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RequestUrl, true\n}\n\n// SetRequestUrl sets field value\nfunc (o *RecoveryFlow) SetRequestUrl(v string) {\n\to.RequestUrl = v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *RecoveryFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *RecoveryFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *RecoveryFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *RecoveryFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *RecoveryFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *RecoveryFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *RecoveryFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *RecoveryFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *RecoveryFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *RecoveryFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *RecoveryFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *RecoveryFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *RecoveryFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\nfunc (o RecoveryFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RecoveryFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.ContinueWith) {\n\t\ttoSerialize[\"continue_with\"] = o.ContinueWith\n\t}\n\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\ttoSerialize[\"request_url\"] = o.RequestUrl\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RecoveryFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"expires_at\",\n\t\t\"id\",\n\t\t\"issued_at\",\n\t\t\"request_url\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRecoveryFlow := _RecoveryFlow{}\n\n\terr = json.Unmarshal(data, &varRecoveryFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RecoveryFlow(varRecoveryFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"continue_with\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRecoveryFlow struct {\n\tvalue *RecoveryFlow\n\tisSet bool\n}\n\nfunc (v NullableRecoveryFlow) Get() *RecoveryFlow {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryFlow) Set(val *RecoveryFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryFlow(val *RecoveryFlow) *NullableRecoveryFlow {\n\treturn &NullableRecoveryFlow{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_recovery_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// RecoveryFlowState The experimental state represents the state of a recovery flow. This field is EXPERIMENTAL and subject to change!\ntype RecoveryFlowState string\n\n// List of recoveryFlowState\nconst (\n\tRECOVERYFLOWSTATE_CHOOSE_METHOD    RecoveryFlowState = \"choose_method\"\n\tRECOVERYFLOWSTATE_SENT_EMAIL       RecoveryFlowState = \"sent_email\"\n\tRECOVERYFLOWSTATE_PASSED_CHALLENGE RecoveryFlowState = \"passed_challenge\"\n)\n\n// All allowed values of RecoveryFlowState enum\nvar AllowedRecoveryFlowStateEnumValues = []RecoveryFlowState{\n\t\"choose_method\",\n\t\"sent_email\",\n\t\"passed_challenge\",\n}\n\nfunc (v *RecoveryFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := RecoveryFlowState(value)\n\tfor _, existing := range AllowedRecoveryFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid RecoveryFlowState\", value)\n}\n\n// NewRecoveryFlowStateFromValue returns a pointer to a valid RecoveryFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewRecoveryFlowStateFromValue(v string) (*RecoveryFlowState, error) {\n\tev := RecoveryFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for RecoveryFlowState: valid values are %v\", v, AllowedRecoveryFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v RecoveryFlowState) IsValid() bool {\n\tfor _, existing := range AllowedRecoveryFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to recoveryFlowState value\nfunc (v RecoveryFlowState) Ptr() *RecoveryFlowState {\n\treturn &v\n}\n\ntype NullableRecoveryFlowState struct {\n\tvalue *RecoveryFlowState\n\tisSet bool\n}\n\nfunc (v NullableRecoveryFlowState) Get() *RecoveryFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryFlowState) Set(val *RecoveryFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryFlowState(val *RecoveryFlowState) *NullableRecoveryFlowState {\n\treturn &NullableRecoveryFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_recovery_identity_address.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RecoveryIdentityAddress type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RecoveryIdentityAddress{}\n\n// RecoveryIdentityAddress struct for RecoveryIdentityAddress\ntype RecoveryIdentityAddress struct {\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\tId        *string    `json:\"id,omitempty\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            *time.Time `json:\"updated_at,omitempty\"`\n\tValue                string     `json:\"value\"`\n\tVia                  string     `json:\"via\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RecoveryIdentityAddress RecoveryIdentityAddress\n\n// NewRecoveryIdentityAddress instantiates a new RecoveryIdentityAddress object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRecoveryIdentityAddress(value string, via string) *RecoveryIdentityAddress {\n\tthis := RecoveryIdentityAddress{}\n\tthis.Value = value\n\tthis.Via = via\n\treturn &this\n}\n\n// NewRecoveryIdentityAddressWithDefaults instantiates a new RecoveryIdentityAddress object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRecoveryIdentityAddressWithDefaults() *RecoveryIdentityAddress {\n\tthis := RecoveryIdentityAddress{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *RecoveryIdentityAddress) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *RecoveryIdentityAddress) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *RecoveryIdentityAddress) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetId returns the Id field value if set, zero value otherwise.\nfunc (o *RecoveryIdentityAddress) GetId() string {\n\tif o == nil || IsNil(o.Id) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.Id) {\n\t\treturn nil, false\n\t}\n\treturn o.Id, true\n}\n\n// HasId returns a boolean if a field has been set.\nfunc (o *RecoveryIdentityAddress) HasId() bool {\n\tif o != nil && !IsNil(o.Id) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetId gets a reference to the given string and assigns it to the Id field.\nfunc (o *RecoveryIdentityAddress) SetId(v string) {\n\to.Id = &v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *RecoveryIdentityAddress) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *RecoveryIdentityAddress) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *RecoveryIdentityAddress) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetValue returns the Value field value\nfunc (o *RecoveryIdentityAddress) GetValue() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Value\n}\n\n// GetValueOk returns a tuple with the Value field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetValueOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Value, true\n}\n\n// SetValue sets field value\nfunc (o *RecoveryIdentityAddress) SetValue(v string) {\n\to.Value = v\n}\n\n// GetVia returns the Via field value\nfunc (o *RecoveryIdentityAddress) GetVia() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Via\n}\n\n// GetViaOk returns a tuple with the Via field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetViaOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Via, true\n}\n\n// SetVia sets field value\nfunc (o *RecoveryIdentityAddress) SetVia(v string) {\n\to.Via = v\n}\n\nfunc (o RecoveryIdentityAddress) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RecoveryIdentityAddress) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Id) {\n\t\ttoSerialize[\"id\"] = o.Id\n\t}\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\ttoSerialize[\"value\"] = o.Value\n\ttoSerialize[\"via\"] = o.Via\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RecoveryIdentityAddress) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"value\",\n\t\t\"via\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRecoveryIdentityAddress := _RecoveryIdentityAddress{}\n\n\terr = json.Unmarshal(data, &varRecoveryIdentityAddress)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RecoveryIdentityAddress(varRecoveryIdentityAddress)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"value\")\n\t\tdelete(additionalProperties, \"via\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRecoveryIdentityAddress struct {\n\tvalue *RecoveryIdentityAddress\n\tisSet bool\n}\n\nfunc (v NullableRecoveryIdentityAddress) Get() *RecoveryIdentityAddress {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryIdentityAddress) Set(val *RecoveryIdentityAddress) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryIdentityAddress) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryIdentityAddress) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryIdentityAddress(val *RecoveryIdentityAddress) *NullableRecoveryIdentityAddress {\n\treturn &NullableRecoveryIdentityAddress{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryIdentityAddress) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryIdentityAddress) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_recovery_link_for_identity.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RecoveryLinkForIdentity type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RecoveryLinkForIdentity{}\n\n// RecoveryLinkForIdentity Used when an administrator creates a recovery link for an identity.\ntype RecoveryLinkForIdentity struct {\n\t// Recovery Link Expires At  The timestamp when the recovery link expires.\n\tExpiresAt *time.Time `json:\"expires_at,omitempty\"`\n\t// Recovery Link  This link can be used to recover the account.\n\tRecoveryLink         string `json:\"recovery_link\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RecoveryLinkForIdentity RecoveryLinkForIdentity\n\n// NewRecoveryLinkForIdentity instantiates a new RecoveryLinkForIdentity object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRecoveryLinkForIdentity(recoveryLink string) *RecoveryLinkForIdentity {\n\tthis := RecoveryLinkForIdentity{}\n\tthis.RecoveryLink = recoveryLink\n\treturn &this\n}\n\n// NewRecoveryLinkForIdentityWithDefaults instantiates a new RecoveryLinkForIdentity object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRecoveryLinkForIdentityWithDefaults() *RecoveryLinkForIdentity {\n\tthis := RecoveryLinkForIdentity{}\n\treturn &this\n}\n\n// GetExpiresAt returns the ExpiresAt field value if set, zero value otherwise.\nfunc (o *RecoveryLinkForIdentity) GetExpiresAt() time.Time {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryLinkForIdentity) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresAt, true\n}\n\n// HasExpiresAt returns a boolean if a field has been set.\nfunc (o *RecoveryLinkForIdentity) HasExpiresAt() bool {\n\tif o != nil && !IsNil(o.ExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresAt gets a reference to the given time.Time and assigns it to the ExpiresAt field.\nfunc (o *RecoveryLinkForIdentity) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = &v\n}\n\n// GetRecoveryLink returns the RecoveryLink field value\nfunc (o *RecoveryLinkForIdentity) GetRecoveryLink() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RecoveryLink\n}\n\n// GetRecoveryLinkOk returns a tuple with the RecoveryLink field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryLinkForIdentity) GetRecoveryLinkOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RecoveryLink, true\n}\n\n// SetRecoveryLink sets field value\nfunc (o *RecoveryLinkForIdentity) SetRecoveryLink(v string) {\n\to.RecoveryLink = v\n}\n\nfunc (o RecoveryLinkForIdentity) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RecoveryLinkForIdentity) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ExpiresAt) {\n\t\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\t}\n\ttoSerialize[\"recovery_link\"] = o.RecoveryLink\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RecoveryLinkForIdentity) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"recovery_link\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRecoveryLinkForIdentity := _RecoveryLinkForIdentity{}\n\n\terr = json.Unmarshal(data, &varRecoveryLinkForIdentity)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RecoveryLinkForIdentity(varRecoveryLinkForIdentity)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"recovery_link\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRecoveryLinkForIdentity struct {\n\tvalue *RecoveryLinkForIdentity\n\tisSet bool\n}\n\nfunc (v NullableRecoveryLinkForIdentity) Get() *RecoveryLinkForIdentity {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryLinkForIdentity) Set(val *RecoveryLinkForIdentity) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryLinkForIdentity) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryLinkForIdentity) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryLinkForIdentity(val *RecoveryLinkForIdentity) *NullableRecoveryLinkForIdentity {\n\treturn &NullableRecoveryLinkForIdentity{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryLinkForIdentity) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryLinkForIdentity) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_registration_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RegistrationFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RegistrationFlow{}\n\n// RegistrationFlow struct for RegistrationFlow\ntype RegistrationFlow struct {\n\t// Active, if set, contains the registration method that is being used. It is initially not set. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\tActive *string `json:\"active,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in, a new flow has to be initiated.\n\tExpiresAt time.Time `json:\"expires_at\"`\n\t// ID represents the flow's unique ID. When performing the registration flow, this represents the id in the registration ui's query parameter: http://<selfservice.flows.registration.ui_url>/?flow=<id>\n\tId string `json:\"id\"`\n\t// IdentitySchema optionally holds the ID of the identity schema that is used for this flow. This value can be set by the user when creating the flow and should be retained when the flow is saved or converted to another flow.\n\tIdentitySchema *string `json:\"identity_schema,omitempty\"`\n\t// IssuedAt is the time (UTC) when the flow occurred.\n\tIssuedAt time.Time `json:\"issued_at\"`\n\t// Ory OAuth 2.0 Login Challenge.  This value is set using the `login_challenge` query parameter of the registration and login endpoints. If set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\n\tOauth2LoginChallenge *string             `json:\"oauth2_login_challenge,omitempty\"`\n\tOauth2LoginRequest   *OAuth2LoginRequest `json:\"oauth2_login_request,omitempty\"`\n\tOrganizationId       NullableString      `json:\"organization_id,omitempty\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl string `json:\"request_url\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the flow has been completed. This is only set if the client has requested a session token exchange code, and if the flow is of type \\\"api\\\", and only on creating the flow.\n\tSessionTokenExchangeCode *string `json:\"session_token_exchange_code,omitempty\"`\n\t// State represents the state of this request:  choose_method: ask the user to choose a method (e.g. registration with email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the registration challenge was passed.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the registration to a webhook\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType                 string      `json:\"type\"`\n\tUi                   UiContainer `json:\"ui\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RegistrationFlow RegistrationFlow\n\n// NewRegistrationFlow instantiates a new RegistrationFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRegistrationFlow(expiresAt time.Time, id string, issuedAt time.Time, requestUrl string, state interface{}, type_ string, ui UiContainer) *RegistrationFlow {\n\tthis := RegistrationFlow{}\n\tthis.ExpiresAt = expiresAt\n\tthis.Id = id\n\tthis.IssuedAt = issuedAt\n\tthis.RequestUrl = requestUrl\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewRegistrationFlowWithDefaults instantiates a new RegistrationFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRegistrationFlowWithDefaults() *RegistrationFlow {\n\tthis := RegistrationFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *RegistrationFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetExpiresAt returns the ExpiresAt field value\nfunc (o *RegistrationFlow) GetExpiresAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.ExpiresAt, true\n}\n\n// SetExpiresAt sets field value\nfunc (o *RegistrationFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = v\n}\n\n// GetId returns the Id field value\nfunc (o *RegistrationFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *RegistrationFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIdentitySchema returns the IdentitySchema field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetIdentitySchema() string {\n\tif o == nil || IsNil(o.IdentitySchema) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdentitySchema\n}\n\n// GetIdentitySchemaOk returns a tuple with the IdentitySchema field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetIdentitySchemaOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdentitySchema) {\n\t\treturn nil, false\n\t}\n\treturn o.IdentitySchema, true\n}\n\n// HasIdentitySchema returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasIdentitySchema() bool {\n\tif o != nil && !IsNil(o.IdentitySchema) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentitySchema gets a reference to the given string and assigns it to the IdentitySchema field.\nfunc (o *RegistrationFlow) SetIdentitySchema(v string) {\n\to.IdentitySchema = &v\n}\n\n// GetIssuedAt returns the IssuedAt field value\nfunc (o *RegistrationFlow) GetIssuedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IssuedAt, true\n}\n\n// SetIssuedAt sets field value\nfunc (o *RegistrationFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = v\n}\n\n// GetOauth2LoginChallenge returns the Oauth2LoginChallenge field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetOauth2LoginChallenge() string {\n\tif o == nil || IsNil(o.Oauth2LoginChallenge) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Oauth2LoginChallenge\n}\n\n// GetOauth2LoginChallengeOk returns a tuple with the Oauth2LoginChallenge field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetOauth2LoginChallengeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Oauth2LoginChallenge) {\n\t\treturn nil, false\n\t}\n\treturn o.Oauth2LoginChallenge, true\n}\n\n// HasOauth2LoginChallenge returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasOauth2LoginChallenge() bool {\n\tif o != nil && !IsNil(o.Oauth2LoginChallenge) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOauth2LoginChallenge gets a reference to the given string and assigns it to the Oauth2LoginChallenge field.\nfunc (o *RegistrationFlow) SetOauth2LoginChallenge(v string) {\n\to.Oauth2LoginChallenge = &v\n}\n\n// GetOauth2LoginRequest returns the Oauth2LoginRequest field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetOauth2LoginRequest() OAuth2LoginRequest {\n\tif o == nil || IsNil(o.Oauth2LoginRequest) {\n\t\tvar ret OAuth2LoginRequest\n\t\treturn ret\n\t}\n\treturn *o.Oauth2LoginRequest\n}\n\n// GetOauth2LoginRequestOk returns a tuple with the Oauth2LoginRequest field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetOauth2LoginRequestOk() (*OAuth2LoginRequest, bool) {\n\tif o == nil || IsNil(o.Oauth2LoginRequest) {\n\t\treturn nil, false\n\t}\n\treturn o.Oauth2LoginRequest, true\n}\n\n// HasOauth2LoginRequest returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasOauth2LoginRequest() bool {\n\tif o != nil && !IsNil(o.Oauth2LoginRequest) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOauth2LoginRequest gets a reference to the given OAuth2LoginRequest and assigns it to the Oauth2LoginRequest field.\nfunc (o *RegistrationFlow) SetOauth2LoginRequest(v OAuth2LoginRequest) {\n\to.Oauth2LoginRequest = &v\n}\n\n// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *RegistrationFlow) GetOrganizationId() string {\n\tif o == nil || IsNil(o.OrganizationId.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OrganizationId.Get()\n}\n\n// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *RegistrationFlow) GetOrganizationIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.OrganizationId.Get(), o.OrganizationId.IsSet()\n}\n\n// HasOrganizationId returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasOrganizationId() bool {\n\tif o != nil && o.OrganizationId.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganizationId gets a reference to the given NullableString and assigns it to the OrganizationId field.\nfunc (o *RegistrationFlow) SetOrganizationId(v string) {\n\to.OrganizationId.Set(&v)\n}\n\n// SetOrganizationIdNil sets the value for OrganizationId to be an explicit nil\nfunc (o *RegistrationFlow) SetOrganizationIdNil() {\n\to.OrganizationId.Set(nil)\n}\n\n// UnsetOrganizationId ensures that no value is present for OrganizationId, not even an explicit nil\nfunc (o *RegistrationFlow) UnsetOrganizationId() {\n\to.OrganizationId.Unset()\n}\n\n// GetRequestUrl returns the RequestUrl field value\nfunc (o *RegistrationFlow) GetRequestUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RequestUrl, true\n}\n\n// SetRequestUrl sets field value\nfunc (o *RegistrationFlow) SetRequestUrl(v string) {\n\to.RequestUrl = v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *RegistrationFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetSessionTokenExchangeCode returns the SessionTokenExchangeCode field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetSessionTokenExchangeCode() string {\n\tif o == nil || IsNil(o.SessionTokenExchangeCode) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionTokenExchangeCode\n}\n\n// GetSessionTokenExchangeCodeOk returns a tuple with the SessionTokenExchangeCode field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetSessionTokenExchangeCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionTokenExchangeCode) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionTokenExchangeCode, true\n}\n\n// HasSessionTokenExchangeCode returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasSessionTokenExchangeCode() bool {\n\tif o != nil && !IsNil(o.SessionTokenExchangeCode) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionTokenExchangeCode gets a reference to the given string and assigns it to the SessionTokenExchangeCode field.\nfunc (o *RegistrationFlow) SetSessionTokenExchangeCode(v string) {\n\to.SessionTokenExchangeCode = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *RegistrationFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *RegistrationFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *RegistrationFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *RegistrationFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *RegistrationFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *RegistrationFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *RegistrationFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *RegistrationFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\nfunc (o RegistrationFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RegistrationFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.IdentitySchema) {\n\t\ttoSerialize[\"identity_schema\"] = o.IdentitySchema\n\t}\n\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\tif !IsNil(o.Oauth2LoginChallenge) {\n\t\ttoSerialize[\"oauth2_login_challenge\"] = o.Oauth2LoginChallenge\n\t}\n\tif !IsNil(o.Oauth2LoginRequest) {\n\t\ttoSerialize[\"oauth2_login_request\"] = o.Oauth2LoginRequest\n\t}\n\tif o.OrganizationId.IsSet() {\n\t\ttoSerialize[\"organization_id\"] = o.OrganizationId.Get()\n\t}\n\ttoSerialize[\"request_url\"] = o.RequestUrl\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif !IsNil(o.SessionTokenExchangeCode) {\n\t\ttoSerialize[\"session_token_exchange_code\"] = o.SessionTokenExchangeCode\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RegistrationFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"expires_at\",\n\t\t\"id\",\n\t\t\"issued_at\",\n\t\t\"request_url\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRegistrationFlow := _RegistrationFlow{}\n\n\terr = json.Unmarshal(data, &varRegistrationFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RegistrationFlow(varRegistrationFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"identity_schema\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"oauth2_login_challenge\")\n\t\tdelete(additionalProperties, \"oauth2_login_request\")\n\t\tdelete(additionalProperties, \"organization_id\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"session_token_exchange_code\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRegistrationFlow struct {\n\tvalue *RegistrationFlow\n\tisSet bool\n}\n\nfunc (v NullableRegistrationFlow) Get() *RegistrationFlow {\n\treturn v.value\n}\n\nfunc (v *NullableRegistrationFlow) Set(val *RegistrationFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRegistrationFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRegistrationFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRegistrationFlow(val *RegistrationFlow) *NullableRegistrationFlow {\n\treturn &NullableRegistrationFlow{value: val, isSet: true}\n}\n\nfunc (v NullableRegistrationFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRegistrationFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_registration_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// RegistrationFlowState The experimental state represents the state of a registration flow. This field is EXPERIMENTAL and subject to change!\ntype RegistrationFlowState string\n\n// List of registrationFlowState\nconst (\n\tREGISTRATIONFLOWSTATE_CHOOSE_METHOD    RegistrationFlowState = \"choose_method\"\n\tREGISTRATIONFLOWSTATE_SENT_EMAIL       RegistrationFlowState = \"sent_email\"\n\tREGISTRATIONFLOWSTATE_PASSED_CHALLENGE RegistrationFlowState = \"passed_challenge\"\n)\n\n// All allowed values of RegistrationFlowState enum\nvar AllowedRegistrationFlowStateEnumValues = []RegistrationFlowState{\n\t\"choose_method\",\n\t\"sent_email\",\n\t\"passed_challenge\",\n}\n\nfunc (v *RegistrationFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := RegistrationFlowState(value)\n\tfor _, existing := range AllowedRegistrationFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid RegistrationFlowState\", value)\n}\n\n// NewRegistrationFlowStateFromValue returns a pointer to a valid RegistrationFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewRegistrationFlowStateFromValue(v string) (*RegistrationFlowState, error) {\n\tev := RegistrationFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for RegistrationFlowState: valid values are %v\", v, AllowedRegistrationFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v RegistrationFlowState) IsValid() bool {\n\tfor _, existing := range AllowedRegistrationFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to registrationFlowState value\nfunc (v RegistrationFlowState) Ptr() *RegistrationFlowState {\n\treturn &v\n}\n\ntype NullableRegistrationFlowState struct {\n\tvalue *RegistrationFlowState\n\tisSet bool\n}\n\nfunc (v NullableRegistrationFlowState) Get() *RegistrationFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableRegistrationFlowState) Set(val *RegistrationFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRegistrationFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRegistrationFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRegistrationFlowState(val *RegistrationFlowState) *NullableRegistrationFlowState {\n\treturn &NullableRegistrationFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableRegistrationFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRegistrationFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_self_service_flow_expired_error.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n)\n\n// checks if the SelfServiceFlowExpiredError type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SelfServiceFlowExpiredError{}\n\n// SelfServiceFlowExpiredError Is sent when a flow is expired\ntype SelfServiceFlowExpiredError struct {\n\tError *GenericError `json:\"error,omitempty\"`\n\t// When the flow has expired\n\tExpiredAt *time.Time `json:\"expired_at,omitempty\"`\n\t// A Duration represents the elapsed time between two instants as an int64 nanosecond count. The representation limits the largest representable duration to approximately 290 years.\n\tSince *int64 `json:\"since,omitempty\"`\n\t// The flow ID that should be used for the new flow as it contains the correct messages.\n\tUseFlowId            *string `json:\"use_flow_id,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SelfServiceFlowExpiredError SelfServiceFlowExpiredError\n\n// NewSelfServiceFlowExpiredError instantiates a new SelfServiceFlowExpiredError object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSelfServiceFlowExpiredError() *SelfServiceFlowExpiredError {\n\tthis := SelfServiceFlowExpiredError{}\n\treturn &this\n}\n\n// NewSelfServiceFlowExpiredErrorWithDefaults instantiates a new SelfServiceFlowExpiredError object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSelfServiceFlowExpiredErrorWithDefaults() *SelfServiceFlowExpiredError {\n\tthis := SelfServiceFlowExpiredError{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *SelfServiceFlowExpiredError) GetError() GenericError {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SelfServiceFlowExpiredError) GetErrorOk() (*GenericError, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *SelfServiceFlowExpiredError) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given GenericError and assigns it to the Error field.\nfunc (o *SelfServiceFlowExpiredError) SetError(v GenericError) {\n\to.Error = &v\n}\n\n// GetExpiredAt returns the ExpiredAt field value if set, zero value otherwise.\nfunc (o *SelfServiceFlowExpiredError) GetExpiredAt() time.Time {\n\tif o == nil || IsNil(o.ExpiredAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiredAt\n}\n\n// GetExpiredAtOk returns a tuple with the ExpiredAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SelfServiceFlowExpiredError) GetExpiredAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiredAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiredAt, true\n}\n\n// HasExpiredAt returns a boolean if a field has been set.\nfunc (o *SelfServiceFlowExpiredError) HasExpiredAt() bool {\n\tif o != nil && !IsNil(o.ExpiredAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiredAt gets a reference to the given time.Time and assigns it to the ExpiredAt field.\nfunc (o *SelfServiceFlowExpiredError) SetExpiredAt(v time.Time) {\n\to.ExpiredAt = &v\n}\n\n// GetSince returns the Since field value if set, zero value otherwise.\nfunc (o *SelfServiceFlowExpiredError) GetSince() int64 {\n\tif o == nil || IsNil(o.Since) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Since\n}\n\n// GetSinceOk returns a tuple with the Since field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SelfServiceFlowExpiredError) GetSinceOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Since) {\n\t\treturn nil, false\n\t}\n\treturn o.Since, true\n}\n\n// HasSince returns a boolean if a field has been set.\nfunc (o *SelfServiceFlowExpiredError) HasSince() bool {\n\tif o != nil && !IsNil(o.Since) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSince gets a reference to the given int64 and assigns it to the Since field.\nfunc (o *SelfServiceFlowExpiredError) SetSince(v int64) {\n\to.Since = &v\n}\n\n// GetUseFlowId returns the UseFlowId field value if set, zero value otherwise.\nfunc (o *SelfServiceFlowExpiredError) GetUseFlowId() string {\n\tif o == nil || IsNil(o.UseFlowId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.UseFlowId\n}\n\n// GetUseFlowIdOk returns a tuple with the UseFlowId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SelfServiceFlowExpiredError) GetUseFlowIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.UseFlowId) {\n\t\treturn nil, false\n\t}\n\treturn o.UseFlowId, true\n}\n\n// HasUseFlowId returns a boolean if a field has been set.\nfunc (o *SelfServiceFlowExpiredError) HasUseFlowId() bool {\n\tif o != nil && !IsNil(o.UseFlowId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUseFlowId gets a reference to the given string and assigns it to the UseFlowId field.\nfunc (o *SelfServiceFlowExpiredError) SetUseFlowId(v string) {\n\to.UseFlowId = &v\n}\n\nfunc (o SelfServiceFlowExpiredError) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SelfServiceFlowExpiredError) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.ExpiredAt) {\n\t\ttoSerialize[\"expired_at\"] = o.ExpiredAt\n\t}\n\tif !IsNil(o.Since) {\n\t\ttoSerialize[\"since\"] = o.Since\n\t}\n\tif !IsNil(o.UseFlowId) {\n\t\ttoSerialize[\"use_flow_id\"] = o.UseFlowId\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SelfServiceFlowExpiredError) UnmarshalJSON(data []byte) (err error) {\n\tvarSelfServiceFlowExpiredError := _SelfServiceFlowExpiredError{}\n\n\terr = json.Unmarshal(data, &varSelfServiceFlowExpiredError)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SelfServiceFlowExpiredError(varSelfServiceFlowExpiredError)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"expired_at\")\n\t\tdelete(additionalProperties, \"since\")\n\t\tdelete(additionalProperties, \"use_flow_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSelfServiceFlowExpiredError struct {\n\tvalue *SelfServiceFlowExpiredError\n\tisSet bool\n}\n\nfunc (v NullableSelfServiceFlowExpiredError) Get() *SelfServiceFlowExpiredError {\n\treturn v.value\n}\n\nfunc (v *NullableSelfServiceFlowExpiredError) Set(val *SelfServiceFlowExpiredError) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSelfServiceFlowExpiredError) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSelfServiceFlowExpiredError) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSelfServiceFlowExpiredError(val *SelfServiceFlowExpiredError) *NullableSelfServiceFlowExpiredError {\n\treturn &NullableSelfServiceFlowExpiredError{value: val, isSet: true}\n}\n\nfunc (v NullableSelfServiceFlowExpiredError) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSelfServiceFlowExpiredError) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_session.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the Session type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Session{}\n\n// Session A Session\ntype Session struct {\n\t// Active state. If false the session is no longer active.\n\tActive *bool `json:\"active,omitempty\"`\n\t// The Session Authentication Timestamp  When this session was authenticated at. If multi-factor authentication was used this is the time when the last factor was authenticated (e.g. the TOTP code challenge was completed).\n\tAuthenticatedAt *time.Time `json:\"authenticated_at,omitempty\"`\n\t// A list of authenticators which were used to authenticate the session.\n\tAuthenticationMethods       []SessionAuthenticationMethod `json:\"authentication_methods,omitempty\"`\n\tAuthenticatorAssuranceLevel *AuthenticatorAssuranceLevel  `json:\"authenticator_assurance_level,omitempty\"`\n\t// Devices has history of all endpoints where the session was used\n\tDevices []SessionDevice `json:\"devices,omitempty\"`\n\t// The Session Expiry  When this session expires at.\n\tExpiresAt *time.Time `json:\"expires_at,omitempty\"`\n\t// Session ID\n\tId       string    `json:\"id\"`\n\tIdentity *Identity `json:\"identity,omitempty\"`\n\t// The Session Issuance Timestamp  When this session was issued at. Usually equal or close to `authenticated_at`.\n\tIssuedAt *time.Time `json:\"issued_at,omitempty\"`\n\t// Tokenized is the tokenized (e.g. JWT) version of the session.  It is only set when the `tokenize_as` query parameter was set to a valid tokenize template during calls to `/session/whoami`.\n\tTokenized            *string `json:\"tokenized,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Session Session\n\n// NewSession instantiates a new Session object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSession(id string) *Session {\n\tthis := Session{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewSessionWithDefaults instantiates a new Session object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSessionWithDefaults() *Session {\n\tthis := Session{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *Session) GetActive() bool {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetActiveOk() (*bool, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *Session) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given bool and assigns it to the Active field.\nfunc (o *Session) SetActive(v bool) {\n\to.Active = &v\n}\n\n// GetAuthenticatedAt returns the AuthenticatedAt field value if set, zero value otherwise.\nfunc (o *Session) GetAuthenticatedAt() time.Time {\n\tif o == nil || IsNil(o.AuthenticatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.AuthenticatedAt\n}\n\n// GetAuthenticatedAtOk returns a tuple with the AuthenticatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetAuthenticatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.AuthenticatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthenticatedAt, true\n}\n\n// HasAuthenticatedAt returns a boolean if a field has been set.\nfunc (o *Session) HasAuthenticatedAt() bool {\n\tif o != nil && !IsNil(o.AuthenticatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthenticatedAt gets a reference to the given time.Time and assigns it to the AuthenticatedAt field.\nfunc (o *Session) SetAuthenticatedAt(v time.Time) {\n\to.AuthenticatedAt = &v\n}\n\n// GetAuthenticationMethods returns the AuthenticationMethods field value if set, zero value otherwise.\nfunc (o *Session) GetAuthenticationMethods() []SessionAuthenticationMethod {\n\tif o == nil || IsNil(o.AuthenticationMethods) {\n\t\tvar ret []SessionAuthenticationMethod\n\t\treturn ret\n\t}\n\treturn o.AuthenticationMethods\n}\n\n// GetAuthenticationMethodsOk returns a tuple with the AuthenticationMethods field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetAuthenticationMethodsOk() ([]SessionAuthenticationMethod, bool) {\n\tif o == nil || IsNil(o.AuthenticationMethods) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthenticationMethods, true\n}\n\n// HasAuthenticationMethods returns a boolean if a field has been set.\nfunc (o *Session) HasAuthenticationMethods() bool {\n\tif o != nil && !IsNil(o.AuthenticationMethods) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthenticationMethods gets a reference to the given []SessionAuthenticationMethod and assigns it to the AuthenticationMethods field.\nfunc (o *Session) SetAuthenticationMethods(v []SessionAuthenticationMethod) {\n\to.AuthenticationMethods = v\n}\n\n// GetAuthenticatorAssuranceLevel returns the AuthenticatorAssuranceLevel field value if set, zero value otherwise.\nfunc (o *Session) GetAuthenticatorAssuranceLevel() AuthenticatorAssuranceLevel {\n\tif o == nil || IsNil(o.AuthenticatorAssuranceLevel) {\n\t\tvar ret AuthenticatorAssuranceLevel\n\t\treturn ret\n\t}\n\treturn *o.AuthenticatorAssuranceLevel\n}\n\n// GetAuthenticatorAssuranceLevelOk returns a tuple with the AuthenticatorAssuranceLevel field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetAuthenticatorAssuranceLevelOk() (*AuthenticatorAssuranceLevel, bool) {\n\tif o == nil || IsNil(o.AuthenticatorAssuranceLevel) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthenticatorAssuranceLevel, true\n}\n\n// HasAuthenticatorAssuranceLevel returns a boolean if a field has been set.\nfunc (o *Session) HasAuthenticatorAssuranceLevel() bool {\n\tif o != nil && !IsNil(o.AuthenticatorAssuranceLevel) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthenticatorAssuranceLevel gets a reference to the given AuthenticatorAssuranceLevel and assigns it to the AuthenticatorAssuranceLevel field.\nfunc (o *Session) SetAuthenticatorAssuranceLevel(v AuthenticatorAssuranceLevel) {\n\to.AuthenticatorAssuranceLevel = &v\n}\n\n// GetDevices returns the Devices field value if set, zero value otherwise.\nfunc (o *Session) GetDevices() []SessionDevice {\n\tif o == nil || IsNil(o.Devices) {\n\t\tvar ret []SessionDevice\n\t\treturn ret\n\t}\n\treturn o.Devices\n}\n\n// GetDevicesOk returns a tuple with the Devices field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetDevicesOk() ([]SessionDevice, bool) {\n\tif o == nil || IsNil(o.Devices) {\n\t\treturn nil, false\n\t}\n\treturn o.Devices, true\n}\n\n// HasDevices returns a boolean if a field has been set.\nfunc (o *Session) HasDevices() bool {\n\tif o != nil && !IsNil(o.Devices) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDevices gets a reference to the given []SessionDevice and assigns it to the Devices field.\nfunc (o *Session) SetDevices(v []SessionDevice) {\n\to.Devices = v\n}\n\n// GetExpiresAt returns the ExpiresAt field value if set, zero value otherwise.\nfunc (o *Session) GetExpiresAt() time.Time {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresAt, true\n}\n\n// HasExpiresAt returns a boolean if a field has been set.\nfunc (o *Session) HasExpiresAt() bool {\n\tif o != nil && !IsNil(o.ExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresAt gets a reference to the given time.Time and assigns it to the ExpiresAt field.\nfunc (o *Session) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = &v\n}\n\n// GetId returns the Id field value\nfunc (o *Session) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *Session) SetId(v string) {\n\to.Id = v\n}\n\n// GetIdentity returns the Identity field value if set, zero value otherwise.\nfunc (o *Session) GetIdentity() Identity {\n\tif o == nil || IsNil(o.Identity) {\n\t\tvar ret Identity\n\t\treturn ret\n\t}\n\treturn *o.Identity\n}\n\n// GetIdentityOk returns a tuple with the Identity field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetIdentityOk() (*Identity, bool) {\n\tif o == nil || IsNil(o.Identity) {\n\t\treturn nil, false\n\t}\n\treturn o.Identity, true\n}\n\n// HasIdentity returns a boolean if a field has been set.\nfunc (o *Session) HasIdentity() bool {\n\tif o != nil && !IsNil(o.Identity) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentity gets a reference to the given Identity and assigns it to the Identity field.\nfunc (o *Session) SetIdentity(v Identity) {\n\to.Identity = &v\n}\n\n// GetIssuedAt returns the IssuedAt field value if set, zero value otherwise.\nfunc (o *Session) GetIssuedAt() time.Time {\n\tif o == nil || IsNil(o.IssuedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.IssuedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.IssuedAt, true\n}\n\n// HasIssuedAt returns a boolean if a field has been set.\nfunc (o *Session) HasIssuedAt() bool {\n\tif o != nil && !IsNil(o.IssuedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIssuedAt gets a reference to the given time.Time and assigns it to the IssuedAt field.\nfunc (o *Session) SetIssuedAt(v time.Time) {\n\to.IssuedAt = &v\n}\n\n// GetTokenized returns the Tokenized field value if set, zero value otherwise.\nfunc (o *Session) GetTokenized() string {\n\tif o == nil || IsNil(o.Tokenized) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Tokenized\n}\n\n// GetTokenizedOk returns a tuple with the Tokenized field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetTokenizedOk() (*string, bool) {\n\tif o == nil || IsNil(o.Tokenized) {\n\t\treturn nil, false\n\t}\n\treturn o.Tokenized, true\n}\n\n// HasTokenized returns a boolean if a field has been set.\nfunc (o *Session) HasTokenized() bool {\n\tif o != nil && !IsNil(o.Tokenized) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTokenized gets a reference to the given string and assigns it to the Tokenized field.\nfunc (o *Session) SetTokenized(v string) {\n\to.Tokenized = &v\n}\n\nfunc (o Session) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Session) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.AuthenticatedAt) {\n\t\ttoSerialize[\"authenticated_at\"] = o.AuthenticatedAt\n\t}\n\tif !IsNil(o.AuthenticationMethods) {\n\t\ttoSerialize[\"authentication_methods\"] = o.AuthenticationMethods\n\t}\n\tif !IsNil(o.AuthenticatorAssuranceLevel) {\n\t\ttoSerialize[\"authenticator_assurance_level\"] = o.AuthenticatorAssuranceLevel\n\t}\n\tif !IsNil(o.Devices) {\n\t\ttoSerialize[\"devices\"] = o.Devices\n\t}\n\tif !IsNil(o.ExpiresAt) {\n\t\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.Identity) {\n\t\ttoSerialize[\"identity\"] = o.Identity\n\t}\n\tif !IsNil(o.IssuedAt) {\n\t\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\t}\n\tif !IsNil(o.Tokenized) {\n\t\ttoSerialize[\"tokenized\"] = o.Tokenized\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Session) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSession := _Session{}\n\n\terr = json.Unmarshal(data, &varSession)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Session(varSession)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"authenticated_at\")\n\t\tdelete(additionalProperties, \"authentication_methods\")\n\t\tdelete(additionalProperties, \"authenticator_assurance_level\")\n\t\tdelete(additionalProperties, \"devices\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"identity\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"tokenized\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSession struct {\n\tvalue *Session\n\tisSet bool\n}\n\nfunc (v NullableSession) Get() *Session {\n\treturn v.value\n}\n\nfunc (v *NullableSession) Set(val *Session) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSession) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSession) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSession(val *Session) *NullableSession {\n\treturn &NullableSession{value: val, isSet: true}\n}\n\nfunc (v NullableSession) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSession) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_session_authentication_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n)\n\n// checks if the SessionAuthenticationMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SessionAuthenticationMethod{}\n\n// SessionAuthenticationMethod A singular authenticator used during authentication / login.\ntype SessionAuthenticationMethod struct {\n\tAal *AuthenticatorAssuranceLevel `json:\"aal,omitempty\"`\n\t// When the authentication challenge was completed.\n\tCompletedAt *time.Time `json:\"completed_at,omitempty\"`\n\t// The method used in this authenticator. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\tMethod *string `json:\"method,omitempty\"`\n\t// The Organization id used for authentication\n\tOrganization *string `json:\"organization,omitempty\"`\n\t// OIDC or SAML provider id used for authentication\n\tProvider             *string `json:\"provider,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SessionAuthenticationMethod SessionAuthenticationMethod\n\n// NewSessionAuthenticationMethod instantiates a new SessionAuthenticationMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSessionAuthenticationMethod() *SessionAuthenticationMethod {\n\tthis := SessionAuthenticationMethod{}\n\treturn &this\n}\n\n// NewSessionAuthenticationMethodWithDefaults instantiates a new SessionAuthenticationMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSessionAuthenticationMethodWithDefaults() *SessionAuthenticationMethod {\n\tthis := SessionAuthenticationMethod{}\n\treturn &this\n}\n\n// GetAal returns the Aal field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetAal() AuthenticatorAssuranceLevel {\n\tif o == nil || IsNil(o.Aal) {\n\t\tvar ret AuthenticatorAssuranceLevel\n\t\treturn ret\n\t}\n\treturn *o.Aal\n}\n\n// GetAalOk returns a tuple with the Aal field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetAalOk() (*AuthenticatorAssuranceLevel, bool) {\n\tif o == nil || IsNil(o.Aal) {\n\t\treturn nil, false\n\t}\n\treturn o.Aal, true\n}\n\n// HasAal returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasAal() bool {\n\tif o != nil && !IsNil(o.Aal) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAal gets a reference to the given AuthenticatorAssuranceLevel and assigns it to the Aal field.\nfunc (o *SessionAuthenticationMethod) SetAal(v AuthenticatorAssuranceLevel) {\n\to.Aal = &v\n}\n\n// GetCompletedAt returns the CompletedAt field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetCompletedAt() time.Time {\n\tif o == nil || IsNil(o.CompletedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CompletedAt\n}\n\n// GetCompletedAtOk returns a tuple with the CompletedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetCompletedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CompletedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CompletedAt, true\n}\n\n// HasCompletedAt returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasCompletedAt() bool {\n\tif o != nil && !IsNil(o.CompletedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCompletedAt gets a reference to the given time.Time and assigns it to the CompletedAt field.\nfunc (o *SessionAuthenticationMethod) SetCompletedAt(v time.Time) {\n\to.CompletedAt = &v\n}\n\n// GetMethod returns the Method field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetMethod() string {\n\tif o == nil || IsNil(o.Method) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetMethodOk() (*string, bool) {\n\tif o == nil || IsNil(o.Method) {\n\t\treturn nil, false\n\t}\n\treturn o.Method, true\n}\n\n// HasMethod returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasMethod() bool {\n\tif o != nil && !IsNil(o.Method) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMethod gets a reference to the given string and assigns it to the Method field.\nfunc (o *SessionAuthenticationMethod) SetMethod(v string) {\n\to.Method = &v\n}\n\n// GetOrganization returns the Organization field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetOrganization() string {\n\tif o == nil || IsNil(o.Organization) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Organization\n}\n\n// GetOrganizationOk returns a tuple with the Organization field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetOrganizationOk() (*string, bool) {\n\tif o == nil || IsNil(o.Organization) {\n\t\treturn nil, false\n\t}\n\treturn o.Organization, true\n}\n\n// HasOrganization returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasOrganization() bool {\n\tif o != nil && !IsNil(o.Organization) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganization gets a reference to the given string and assigns it to the Organization field.\nfunc (o *SessionAuthenticationMethod) SetOrganization(v string) {\n\to.Organization = &v\n}\n\n// GetProvider returns the Provider field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetProvider() string {\n\tif o == nil || IsNil(o.Provider) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetProviderOk() (*string, bool) {\n\tif o == nil || IsNil(o.Provider) {\n\t\treturn nil, false\n\t}\n\treturn o.Provider, true\n}\n\n// HasProvider returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasProvider() bool {\n\tif o != nil && !IsNil(o.Provider) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProvider gets a reference to the given string and assigns it to the Provider field.\nfunc (o *SessionAuthenticationMethod) SetProvider(v string) {\n\to.Provider = &v\n}\n\nfunc (o SessionAuthenticationMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SessionAuthenticationMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Aal) {\n\t\ttoSerialize[\"aal\"] = o.Aal\n\t}\n\tif !IsNil(o.CompletedAt) {\n\t\ttoSerialize[\"completed_at\"] = o.CompletedAt\n\t}\n\tif !IsNil(o.Method) {\n\t\ttoSerialize[\"method\"] = o.Method\n\t}\n\tif !IsNil(o.Organization) {\n\t\ttoSerialize[\"organization\"] = o.Organization\n\t}\n\tif !IsNil(o.Provider) {\n\t\ttoSerialize[\"provider\"] = o.Provider\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SessionAuthenticationMethod) UnmarshalJSON(data []byte) (err error) {\n\tvarSessionAuthenticationMethod := _SessionAuthenticationMethod{}\n\n\terr = json.Unmarshal(data, &varSessionAuthenticationMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SessionAuthenticationMethod(varSessionAuthenticationMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"aal\")\n\t\tdelete(additionalProperties, \"completed_at\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"organization\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSessionAuthenticationMethod struct {\n\tvalue *SessionAuthenticationMethod\n\tisSet bool\n}\n\nfunc (v NullableSessionAuthenticationMethod) Get() *SessionAuthenticationMethod {\n\treturn v.value\n}\n\nfunc (v *NullableSessionAuthenticationMethod) Set(val *SessionAuthenticationMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSessionAuthenticationMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSessionAuthenticationMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSessionAuthenticationMethod(val *SessionAuthenticationMethod) *NullableSessionAuthenticationMethod {\n\treturn &NullableSessionAuthenticationMethod{value: val, isSet: true}\n}\n\nfunc (v NullableSessionAuthenticationMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSessionAuthenticationMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_session_device.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the SessionDevice type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SessionDevice{}\n\n// SessionDevice Device corresponding to a Session\ntype SessionDevice struct {\n\t// Device record ID\n\tId string `json:\"id\"`\n\t// IPAddress of the client\n\tIpAddress *string `json:\"ip_address,omitempty\"`\n\t// Geo Location corresponding to the IP Address\n\tLocation *string `json:\"location,omitempty\"`\n\t// UserAgent of the client\n\tUserAgent            *string `json:\"user_agent,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SessionDevice SessionDevice\n\n// NewSessionDevice instantiates a new SessionDevice object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSessionDevice(id string) *SessionDevice {\n\tthis := SessionDevice{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewSessionDeviceWithDefaults instantiates a new SessionDevice object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSessionDeviceWithDefaults() *SessionDevice {\n\tthis := SessionDevice{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *SessionDevice) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *SessionDevice) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *SessionDevice) SetId(v string) {\n\to.Id = v\n}\n\n// GetIpAddress returns the IpAddress field value if set, zero value otherwise.\nfunc (o *SessionDevice) GetIpAddress() string {\n\tif o == nil || IsNil(o.IpAddress) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IpAddress\n}\n\n// GetIpAddressOk returns a tuple with the IpAddress field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionDevice) GetIpAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.IpAddress) {\n\t\treturn nil, false\n\t}\n\treturn o.IpAddress, true\n}\n\n// HasIpAddress returns a boolean if a field has been set.\nfunc (o *SessionDevice) HasIpAddress() bool {\n\tif o != nil && !IsNil(o.IpAddress) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIpAddress gets a reference to the given string and assigns it to the IpAddress field.\nfunc (o *SessionDevice) SetIpAddress(v string) {\n\to.IpAddress = &v\n}\n\n// GetLocation returns the Location field value if set, zero value otherwise.\nfunc (o *SessionDevice) GetLocation() string {\n\tif o == nil || IsNil(o.Location) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Location\n}\n\n// GetLocationOk returns a tuple with the Location field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionDevice) GetLocationOk() (*string, bool) {\n\tif o == nil || IsNil(o.Location) {\n\t\treturn nil, false\n\t}\n\treturn o.Location, true\n}\n\n// HasLocation returns a boolean if a field has been set.\nfunc (o *SessionDevice) HasLocation() bool {\n\tif o != nil && !IsNil(o.Location) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLocation gets a reference to the given string and assigns it to the Location field.\nfunc (o *SessionDevice) SetLocation(v string) {\n\to.Location = &v\n}\n\n// GetUserAgent returns the UserAgent field value if set, zero value otherwise.\nfunc (o *SessionDevice) GetUserAgent() string {\n\tif o == nil || IsNil(o.UserAgent) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.UserAgent\n}\n\n// GetUserAgentOk returns a tuple with the UserAgent field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionDevice) GetUserAgentOk() (*string, bool) {\n\tif o == nil || IsNil(o.UserAgent) {\n\t\treturn nil, false\n\t}\n\treturn o.UserAgent, true\n}\n\n// HasUserAgent returns a boolean if a field has been set.\nfunc (o *SessionDevice) HasUserAgent() bool {\n\tif o != nil && !IsNil(o.UserAgent) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUserAgent gets a reference to the given string and assigns it to the UserAgent field.\nfunc (o *SessionDevice) SetUserAgent(v string) {\n\to.UserAgent = &v\n}\n\nfunc (o SessionDevice) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SessionDevice) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.IpAddress) {\n\t\ttoSerialize[\"ip_address\"] = o.IpAddress\n\t}\n\tif !IsNil(o.Location) {\n\t\ttoSerialize[\"location\"] = o.Location\n\t}\n\tif !IsNil(o.UserAgent) {\n\t\ttoSerialize[\"user_agent\"] = o.UserAgent\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SessionDevice) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSessionDevice := _SessionDevice{}\n\n\terr = json.Unmarshal(data, &varSessionDevice)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SessionDevice(varSessionDevice)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"ip_address\")\n\t\tdelete(additionalProperties, \"location\")\n\t\tdelete(additionalProperties, \"user_agent\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSessionDevice struct {\n\tvalue *SessionDevice\n\tisSet bool\n}\n\nfunc (v NullableSessionDevice) Get() *SessionDevice {\n\treturn v.value\n}\n\nfunc (v *NullableSessionDevice) Set(val *SessionDevice) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSessionDevice) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSessionDevice) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSessionDevice(val *SessionDevice) *NullableSessionDevice {\n\treturn &NullableSessionDevice{value: val, isSet: true}\n}\n\nfunc (v NullableSessionDevice) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSessionDevice) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_settings_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the SettingsFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SettingsFlow{}\n\n// SettingsFlow This flow is used when an identity wants to update settings (e.g. profile data, passwords, ...) in a selfservice manner.  We recommend reading the [User Settings Documentation](../self-service/flows/user-settings)\ntype SettingsFlow struct {\n\t// Active, if set, contains the registration method that is being used. It is initially not set.\n\tActive *string `json:\"active,omitempty\"`\n\t// Contains a list of actions, that could follow this flow  It can, for example, contain a reference to the verification flow, created as part of the user's registration.\n\tContinueWith []ContinueWith `json:\"continue_with,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to update the setting, a new flow has to be initiated.\n\tExpiresAt time.Time `json:\"expires_at\"`\n\t// ID represents the flow's unique ID. When performing the settings flow, this represents the id in the settings ui's query parameter: http://<selfservice.flows.settings.ui_url>?flow=<id>\n\tId       string   `json:\"id\"`\n\tIdentity Identity `json:\"identity\"`\n\t// IssuedAt is the time (UTC) when the flow occurred.\n\tIssuedAt time.Time `json:\"issued_at\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl string `json:\"request_url\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// State represents the state of this flow. It knows two states:  show_form: No user data has been collected, or it is invalid, and thus the form should be shown. success: Indicates that the settings flow has been updated successfully with the provided data. Done will stay true when repeatedly checking. If set to true, done will revert back to false only when a flow with invalid (e.g. \\\"please use a valid phone number\\\") data was sent.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the settings flow to hooks and email templates\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType                 string      `json:\"type\"`\n\tUi                   UiContainer `json:\"ui\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SettingsFlow SettingsFlow\n\n// NewSettingsFlow instantiates a new SettingsFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSettingsFlow(expiresAt time.Time, id string, identity Identity, issuedAt time.Time, requestUrl string, state interface{}, type_ string, ui UiContainer) *SettingsFlow {\n\tthis := SettingsFlow{}\n\tthis.ExpiresAt = expiresAt\n\tthis.Id = id\n\tthis.Identity = identity\n\tthis.IssuedAt = issuedAt\n\tthis.RequestUrl = requestUrl\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewSettingsFlowWithDefaults instantiates a new SettingsFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSettingsFlowWithDefaults() *SettingsFlow {\n\tthis := SettingsFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *SettingsFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *SettingsFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *SettingsFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetContinueWith returns the ContinueWith field value if set, zero value otherwise.\nfunc (o *SettingsFlow) GetContinueWith() []ContinueWith {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\tvar ret []ContinueWith\n\t\treturn ret\n\t}\n\treturn o.ContinueWith\n}\n\n// GetContinueWithOk returns a tuple with the ContinueWith field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetContinueWithOk() ([]ContinueWith, bool) {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\treturn nil, false\n\t}\n\treturn o.ContinueWith, true\n}\n\n// HasContinueWith returns a boolean if a field has been set.\nfunc (o *SettingsFlow) HasContinueWith() bool {\n\tif o != nil && !IsNil(o.ContinueWith) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContinueWith gets a reference to the given []ContinueWith and assigns it to the ContinueWith field.\nfunc (o *SettingsFlow) SetContinueWith(v []ContinueWith) {\n\to.ContinueWith = v\n}\n\n// GetExpiresAt returns the ExpiresAt field value\nfunc (o *SettingsFlow) GetExpiresAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.ExpiresAt, true\n}\n\n// SetExpiresAt sets field value\nfunc (o *SettingsFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = v\n}\n\n// GetId returns the Id field value\nfunc (o *SettingsFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *SettingsFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIdentity returns the Identity field value\nfunc (o *SettingsFlow) GetIdentity() Identity {\n\tif o == nil {\n\t\tvar ret Identity\n\t\treturn ret\n\t}\n\n\treturn o.Identity\n}\n\n// GetIdentityOk returns a tuple with the Identity field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetIdentityOk() (*Identity, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identity, true\n}\n\n// SetIdentity sets field value\nfunc (o *SettingsFlow) SetIdentity(v Identity) {\n\to.Identity = v\n}\n\n// GetIssuedAt returns the IssuedAt field value\nfunc (o *SettingsFlow) GetIssuedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IssuedAt, true\n}\n\n// SetIssuedAt sets field value\nfunc (o *SettingsFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = v\n}\n\n// GetRequestUrl returns the RequestUrl field value\nfunc (o *SettingsFlow) GetRequestUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RequestUrl, true\n}\n\n// SetRequestUrl sets field value\nfunc (o *SettingsFlow) SetRequestUrl(v string) {\n\to.RequestUrl = v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *SettingsFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *SettingsFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *SettingsFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *SettingsFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *SettingsFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *SettingsFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *SettingsFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *SettingsFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *SettingsFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *SettingsFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *SettingsFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *SettingsFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *SettingsFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\nfunc (o SettingsFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SettingsFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.ContinueWith) {\n\t\ttoSerialize[\"continue_with\"] = o.ContinueWith\n\t}\n\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"identity\"] = o.Identity\n\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\ttoSerialize[\"request_url\"] = o.RequestUrl\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SettingsFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"expires_at\",\n\t\t\"id\",\n\t\t\"identity\",\n\t\t\"issued_at\",\n\t\t\"request_url\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSettingsFlow := _SettingsFlow{}\n\n\terr = json.Unmarshal(data, &varSettingsFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SettingsFlow(varSettingsFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"continue_with\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"identity\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSettingsFlow struct {\n\tvalue *SettingsFlow\n\tisSet bool\n}\n\nfunc (v NullableSettingsFlow) Get() *SettingsFlow {\n\treturn v.value\n}\n\nfunc (v *NullableSettingsFlow) Set(val *SettingsFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSettingsFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSettingsFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSettingsFlow(val *SettingsFlow) *NullableSettingsFlow {\n\treturn &NullableSettingsFlow{value: val, isSet: true}\n}\n\nfunc (v NullableSettingsFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSettingsFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_settings_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// SettingsFlowState The experimental state represents the state of a settings flow. This field is EXPERIMENTAL and subject to change!\ntype SettingsFlowState string\n\n// List of settingsFlowState\nconst (\n\tSETTINGSFLOWSTATE_SHOW_FORM SettingsFlowState = \"show_form\"\n\tSETTINGSFLOWSTATE_SUCCESS   SettingsFlowState = \"success\"\n)\n\n// All allowed values of SettingsFlowState enum\nvar AllowedSettingsFlowStateEnumValues = []SettingsFlowState{\n\t\"show_form\",\n\t\"success\",\n}\n\nfunc (v *SettingsFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := SettingsFlowState(value)\n\tfor _, existing := range AllowedSettingsFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid SettingsFlowState\", value)\n}\n\n// NewSettingsFlowStateFromValue returns a pointer to a valid SettingsFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewSettingsFlowStateFromValue(v string) (*SettingsFlowState, error) {\n\tev := SettingsFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for SettingsFlowState: valid values are %v\", v, AllowedSettingsFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v SettingsFlowState) IsValid() bool {\n\tfor _, existing := range AllowedSettingsFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to settingsFlowState value\nfunc (v SettingsFlowState) Ptr() *SettingsFlowState {\n\treturn &v\n}\n\ntype NullableSettingsFlowState struct {\n\tvalue *SettingsFlowState\n\tisSet bool\n}\n\nfunc (v NullableSettingsFlowState) Get() *SettingsFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableSettingsFlowState) Set(val *SettingsFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSettingsFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSettingsFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSettingsFlowState(val *SettingsFlowState) *NullableSettingsFlowState {\n\treturn &NullableSettingsFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableSettingsFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSettingsFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_successful_code_exchange_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the SuccessfulCodeExchangeResponse type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SuccessfulCodeExchangeResponse{}\n\n// SuccessfulCodeExchangeResponse The Response for Registration Flows via API\ntype SuccessfulCodeExchangeResponse struct {\n\tSession Session `json:\"session\"`\n\t// The Session Token  A session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization Header:  Authorization: bearer ${session-token}  The session token is only issued for API flows, not for Browser flows!\n\tSessionToken         *string `json:\"session_token,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SuccessfulCodeExchangeResponse SuccessfulCodeExchangeResponse\n\n// NewSuccessfulCodeExchangeResponse instantiates a new SuccessfulCodeExchangeResponse object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSuccessfulCodeExchangeResponse(session Session) *SuccessfulCodeExchangeResponse {\n\tthis := SuccessfulCodeExchangeResponse{}\n\tthis.Session = session\n\treturn &this\n}\n\n// NewSuccessfulCodeExchangeResponseWithDefaults instantiates a new SuccessfulCodeExchangeResponse object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSuccessfulCodeExchangeResponseWithDefaults() *SuccessfulCodeExchangeResponse {\n\tthis := SuccessfulCodeExchangeResponse{}\n\treturn &this\n}\n\n// GetSession returns the Session field value\nfunc (o *SuccessfulCodeExchangeResponse) GetSession() Session {\n\tif o == nil {\n\t\tvar ret Session\n\t\treturn ret\n\t}\n\n\treturn o.Session\n}\n\n// GetSessionOk returns a tuple with the Session field value\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulCodeExchangeResponse) GetSessionOk() (*Session, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Session, true\n}\n\n// SetSession sets field value\nfunc (o *SuccessfulCodeExchangeResponse) SetSession(v Session) {\n\to.Session = v\n}\n\n// GetSessionToken returns the SessionToken field value if set, zero value otherwise.\nfunc (o *SuccessfulCodeExchangeResponse) GetSessionToken() string {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionToken\n}\n\n// GetSessionTokenOk returns a tuple with the SessionToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulCodeExchangeResponse) GetSessionTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionToken, true\n}\n\n// HasSessionToken returns a boolean if a field has been set.\nfunc (o *SuccessfulCodeExchangeResponse) HasSessionToken() bool {\n\tif o != nil && !IsNil(o.SessionToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionToken gets a reference to the given string and assigns it to the SessionToken field.\nfunc (o *SuccessfulCodeExchangeResponse) SetSessionToken(v string) {\n\to.SessionToken = &v\n}\n\nfunc (o SuccessfulCodeExchangeResponse) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SuccessfulCodeExchangeResponse) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"session\"] = o.Session\n\tif !IsNil(o.SessionToken) {\n\t\ttoSerialize[\"session_token\"] = o.SessionToken\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SuccessfulCodeExchangeResponse) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"session\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSuccessfulCodeExchangeResponse := _SuccessfulCodeExchangeResponse{}\n\n\terr = json.Unmarshal(data, &varSuccessfulCodeExchangeResponse)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SuccessfulCodeExchangeResponse(varSuccessfulCodeExchangeResponse)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"session\")\n\t\tdelete(additionalProperties, \"session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSuccessfulCodeExchangeResponse struct {\n\tvalue *SuccessfulCodeExchangeResponse\n\tisSet bool\n}\n\nfunc (v NullableSuccessfulCodeExchangeResponse) Get() *SuccessfulCodeExchangeResponse {\n\treturn v.value\n}\n\nfunc (v *NullableSuccessfulCodeExchangeResponse) Set(val *SuccessfulCodeExchangeResponse) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSuccessfulCodeExchangeResponse) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSuccessfulCodeExchangeResponse) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSuccessfulCodeExchangeResponse(val *SuccessfulCodeExchangeResponse) *NullableSuccessfulCodeExchangeResponse {\n\treturn &NullableSuccessfulCodeExchangeResponse{value: val, isSet: true}\n}\n\nfunc (v NullableSuccessfulCodeExchangeResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSuccessfulCodeExchangeResponse) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_successful_native_login.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the SuccessfulNativeLogin type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SuccessfulNativeLogin{}\n\n// SuccessfulNativeLogin The Response for Login Flows via API\ntype SuccessfulNativeLogin struct {\n\t// Contains a list of actions, that could follow this flow  It can, for example, this will contain a reference to the verification flow, created as part of the user's registration or the token of the session.\n\tContinueWith []ContinueWith `json:\"continue_with,omitempty\"`\n\tSession      Session        `json:\"session\"`\n\t// The Session Token  A session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization Header:  Authorization: bearer ${session-token}  The session token is only issued for API flows, not for Browser flows!\n\tSessionToken         *string `json:\"session_token,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SuccessfulNativeLogin SuccessfulNativeLogin\n\n// NewSuccessfulNativeLogin instantiates a new SuccessfulNativeLogin object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSuccessfulNativeLogin(session Session) *SuccessfulNativeLogin {\n\tthis := SuccessfulNativeLogin{}\n\tthis.Session = session\n\treturn &this\n}\n\n// NewSuccessfulNativeLoginWithDefaults instantiates a new SuccessfulNativeLogin object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSuccessfulNativeLoginWithDefaults() *SuccessfulNativeLogin {\n\tthis := SuccessfulNativeLogin{}\n\treturn &this\n}\n\n// GetContinueWith returns the ContinueWith field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeLogin) GetContinueWith() []ContinueWith {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\tvar ret []ContinueWith\n\t\treturn ret\n\t}\n\treturn o.ContinueWith\n}\n\n// GetContinueWithOk returns a tuple with the ContinueWith field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeLogin) GetContinueWithOk() ([]ContinueWith, bool) {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\treturn nil, false\n\t}\n\treturn o.ContinueWith, true\n}\n\n// HasContinueWith returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeLogin) HasContinueWith() bool {\n\tif o != nil && !IsNil(o.ContinueWith) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContinueWith gets a reference to the given []ContinueWith and assigns it to the ContinueWith field.\nfunc (o *SuccessfulNativeLogin) SetContinueWith(v []ContinueWith) {\n\to.ContinueWith = v\n}\n\n// GetSession returns the Session field value\nfunc (o *SuccessfulNativeLogin) GetSession() Session {\n\tif o == nil {\n\t\tvar ret Session\n\t\treturn ret\n\t}\n\n\treturn o.Session\n}\n\n// GetSessionOk returns a tuple with the Session field value\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeLogin) GetSessionOk() (*Session, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Session, true\n}\n\n// SetSession sets field value\nfunc (o *SuccessfulNativeLogin) SetSession(v Session) {\n\to.Session = v\n}\n\n// GetSessionToken returns the SessionToken field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeLogin) GetSessionToken() string {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionToken\n}\n\n// GetSessionTokenOk returns a tuple with the SessionToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeLogin) GetSessionTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionToken, true\n}\n\n// HasSessionToken returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeLogin) HasSessionToken() bool {\n\tif o != nil && !IsNil(o.SessionToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionToken gets a reference to the given string and assigns it to the SessionToken field.\nfunc (o *SuccessfulNativeLogin) SetSessionToken(v string) {\n\to.SessionToken = &v\n}\n\nfunc (o SuccessfulNativeLogin) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SuccessfulNativeLogin) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ContinueWith) {\n\t\ttoSerialize[\"continue_with\"] = o.ContinueWith\n\t}\n\ttoSerialize[\"session\"] = o.Session\n\tif !IsNil(o.SessionToken) {\n\t\ttoSerialize[\"session_token\"] = o.SessionToken\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SuccessfulNativeLogin) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"session\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSuccessfulNativeLogin := _SuccessfulNativeLogin{}\n\n\terr = json.Unmarshal(data, &varSuccessfulNativeLogin)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SuccessfulNativeLogin(varSuccessfulNativeLogin)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"continue_with\")\n\t\tdelete(additionalProperties, \"session\")\n\t\tdelete(additionalProperties, \"session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSuccessfulNativeLogin struct {\n\tvalue *SuccessfulNativeLogin\n\tisSet bool\n}\n\nfunc (v NullableSuccessfulNativeLogin) Get() *SuccessfulNativeLogin {\n\treturn v.value\n}\n\nfunc (v *NullableSuccessfulNativeLogin) Set(val *SuccessfulNativeLogin) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSuccessfulNativeLogin) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSuccessfulNativeLogin) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSuccessfulNativeLogin(val *SuccessfulNativeLogin) *NullableSuccessfulNativeLogin {\n\treturn &NullableSuccessfulNativeLogin{value: val, isSet: true}\n}\n\nfunc (v NullableSuccessfulNativeLogin) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSuccessfulNativeLogin) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_successful_native_registration.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the SuccessfulNativeRegistration type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SuccessfulNativeRegistration{}\n\n// SuccessfulNativeRegistration The Response for Registration Flows via API\ntype SuccessfulNativeRegistration struct {\n\t// Contains a list of actions, that could follow this flow  It can, for example, this will contain a reference to the verification flow, created as part of the user's registration or the token of the session.\n\tContinueWith []ContinueWith `json:\"continue_with,omitempty\"`\n\tIdentity     Identity       `json:\"identity\"`\n\tSession      *Session       `json:\"session,omitempty\"`\n\t// The Session Token  This field is only set when the session hook is configured as a post-registration hook.  A session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization Header:  Authorization: bearer ${session-token}  The session token is only issued for API flows, not for Browser flows!\n\tSessionToken         *string `json:\"session_token,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SuccessfulNativeRegistration SuccessfulNativeRegistration\n\n// NewSuccessfulNativeRegistration instantiates a new SuccessfulNativeRegistration object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSuccessfulNativeRegistration(identity Identity) *SuccessfulNativeRegistration {\n\tthis := SuccessfulNativeRegistration{}\n\tthis.Identity = identity\n\treturn &this\n}\n\n// NewSuccessfulNativeRegistrationWithDefaults instantiates a new SuccessfulNativeRegistration object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSuccessfulNativeRegistrationWithDefaults() *SuccessfulNativeRegistration {\n\tthis := SuccessfulNativeRegistration{}\n\treturn &this\n}\n\n// GetContinueWith returns the ContinueWith field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeRegistration) GetContinueWith() []ContinueWith {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\tvar ret []ContinueWith\n\t\treturn ret\n\t}\n\treturn o.ContinueWith\n}\n\n// GetContinueWithOk returns a tuple with the ContinueWith field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeRegistration) GetContinueWithOk() ([]ContinueWith, bool) {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\treturn nil, false\n\t}\n\treturn o.ContinueWith, true\n}\n\n// HasContinueWith returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeRegistration) HasContinueWith() bool {\n\tif o != nil && !IsNil(o.ContinueWith) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContinueWith gets a reference to the given []ContinueWith and assigns it to the ContinueWith field.\nfunc (o *SuccessfulNativeRegistration) SetContinueWith(v []ContinueWith) {\n\to.ContinueWith = v\n}\n\n// GetIdentity returns the Identity field value\nfunc (o *SuccessfulNativeRegistration) GetIdentity() Identity {\n\tif o == nil {\n\t\tvar ret Identity\n\t\treturn ret\n\t}\n\n\treturn o.Identity\n}\n\n// GetIdentityOk returns a tuple with the Identity field value\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeRegistration) GetIdentityOk() (*Identity, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identity, true\n}\n\n// SetIdentity sets field value\nfunc (o *SuccessfulNativeRegistration) SetIdentity(v Identity) {\n\to.Identity = v\n}\n\n// GetSession returns the Session field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeRegistration) GetSession() Session {\n\tif o == nil || IsNil(o.Session) {\n\t\tvar ret Session\n\t\treturn ret\n\t}\n\treturn *o.Session\n}\n\n// GetSessionOk returns a tuple with the Session field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeRegistration) GetSessionOk() (*Session, bool) {\n\tif o == nil || IsNil(o.Session) {\n\t\treturn nil, false\n\t}\n\treturn o.Session, true\n}\n\n// HasSession returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeRegistration) HasSession() bool {\n\tif o != nil && !IsNil(o.Session) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSession gets a reference to the given Session and assigns it to the Session field.\nfunc (o *SuccessfulNativeRegistration) SetSession(v Session) {\n\to.Session = &v\n}\n\n// GetSessionToken returns the SessionToken field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeRegistration) GetSessionToken() string {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionToken\n}\n\n// GetSessionTokenOk returns a tuple with the SessionToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeRegistration) GetSessionTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionToken, true\n}\n\n// HasSessionToken returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeRegistration) HasSessionToken() bool {\n\tif o != nil && !IsNil(o.SessionToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionToken gets a reference to the given string and assigns it to the SessionToken field.\nfunc (o *SuccessfulNativeRegistration) SetSessionToken(v string) {\n\to.SessionToken = &v\n}\n\nfunc (o SuccessfulNativeRegistration) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SuccessfulNativeRegistration) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ContinueWith) {\n\t\ttoSerialize[\"continue_with\"] = o.ContinueWith\n\t}\n\ttoSerialize[\"identity\"] = o.Identity\n\tif !IsNil(o.Session) {\n\t\ttoSerialize[\"session\"] = o.Session\n\t}\n\tif !IsNil(o.SessionToken) {\n\t\ttoSerialize[\"session_token\"] = o.SessionToken\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SuccessfulNativeRegistration) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identity\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSuccessfulNativeRegistration := _SuccessfulNativeRegistration{}\n\n\terr = json.Unmarshal(data, &varSuccessfulNativeRegistration)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SuccessfulNativeRegistration(varSuccessfulNativeRegistration)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"continue_with\")\n\t\tdelete(additionalProperties, \"identity\")\n\t\tdelete(additionalProperties, \"session\")\n\t\tdelete(additionalProperties, \"session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSuccessfulNativeRegistration struct {\n\tvalue *SuccessfulNativeRegistration\n\tisSet bool\n}\n\nfunc (v NullableSuccessfulNativeRegistration) Get() *SuccessfulNativeRegistration {\n\treturn v.value\n}\n\nfunc (v *NullableSuccessfulNativeRegistration) Set(val *SuccessfulNativeRegistration) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSuccessfulNativeRegistration) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSuccessfulNativeRegistration) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSuccessfulNativeRegistration(val *SuccessfulNativeRegistration) *NullableSuccessfulNativeRegistration {\n\treturn &NullableSuccessfulNativeRegistration{value: val, isSet: true}\n}\n\nfunc (v NullableSuccessfulNativeRegistration) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSuccessfulNativeRegistration) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_token_pagination.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the TokenPagination type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &TokenPagination{}\n\n// TokenPagination struct for TokenPagination\ntype TokenPagination struct {\n\t// Items per page  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\tPageSize *int64 `json:\"page_size,omitempty\"`\n\t// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\tPageToken            *string `json:\"page_token,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _TokenPagination TokenPagination\n\n// NewTokenPagination instantiates a new TokenPagination object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewTokenPagination() *TokenPagination {\n\tthis := TokenPagination{}\n\tvar pageSize int64 = 250\n\tthis.PageSize = &pageSize\n\treturn &this\n}\n\n// NewTokenPaginationWithDefaults instantiates a new TokenPagination object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewTokenPaginationWithDefaults() *TokenPagination {\n\tthis := TokenPagination{}\n\tvar pageSize int64 = 250\n\tthis.PageSize = &pageSize\n\treturn &this\n}\n\n// GetPageSize returns the PageSize field value if set, zero value otherwise.\nfunc (o *TokenPagination) GetPageSize() int64 {\n\tif o == nil || IsNil(o.PageSize) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.PageSize\n}\n\n// GetPageSizeOk returns a tuple with the PageSize field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *TokenPagination) GetPageSizeOk() (*int64, bool) {\n\tif o == nil || IsNil(o.PageSize) {\n\t\treturn nil, false\n\t}\n\treturn o.PageSize, true\n}\n\n// HasPageSize returns a boolean if a field has been set.\nfunc (o *TokenPagination) HasPageSize() bool {\n\tif o != nil && !IsNil(o.PageSize) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPageSize gets a reference to the given int64 and assigns it to the PageSize field.\nfunc (o *TokenPagination) SetPageSize(v int64) {\n\to.PageSize = &v\n}\n\n// GetPageToken returns the PageToken field value if set, zero value otherwise.\nfunc (o *TokenPagination) GetPageToken() string {\n\tif o == nil || IsNil(o.PageToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PageToken\n}\n\n// GetPageTokenOk returns a tuple with the PageToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *TokenPagination) GetPageTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.PageToken) {\n\t\treturn nil, false\n\t}\n\treturn o.PageToken, true\n}\n\n// HasPageToken returns a boolean if a field has been set.\nfunc (o *TokenPagination) HasPageToken() bool {\n\tif o != nil && !IsNil(o.PageToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPageToken gets a reference to the given string and assigns it to the PageToken field.\nfunc (o *TokenPagination) SetPageToken(v string) {\n\to.PageToken = &v\n}\n\nfunc (o TokenPagination) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o TokenPagination) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.PageSize) {\n\t\ttoSerialize[\"page_size\"] = o.PageSize\n\t}\n\tif !IsNil(o.PageToken) {\n\t\ttoSerialize[\"page_token\"] = o.PageToken\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *TokenPagination) UnmarshalJSON(data []byte) (err error) {\n\tvarTokenPagination := _TokenPagination{}\n\n\terr = json.Unmarshal(data, &varTokenPagination)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = TokenPagination(varTokenPagination)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"page_size\")\n\t\tdelete(additionalProperties, \"page_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableTokenPagination struct {\n\tvalue *TokenPagination\n\tisSet bool\n}\n\nfunc (v NullableTokenPagination) Get() *TokenPagination {\n\treturn v.value\n}\n\nfunc (v *NullableTokenPagination) Set(val *TokenPagination) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableTokenPagination) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableTokenPagination) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableTokenPagination(val *TokenPagination) *NullableTokenPagination {\n\treturn &NullableTokenPagination{value: val, isSet: true}\n}\n\nfunc (v NullableTokenPagination) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableTokenPagination) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_token_pagination_headers.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the TokenPaginationHeaders type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &TokenPaginationHeaders{}\n\n// TokenPaginationHeaders struct for TokenPaginationHeaders\ntype TokenPaginationHeaders struct {\n\t// The link header contains pagination links.  For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).  in: header\n\tLink *string `json:\"link,omitempty\"`\n\t// The total number of clients.  in: header\n\tXTotalCount          *string `json:\"x-total-count,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _TokenPaginationHeaders TokenPaginationHeaders\n\n// NewTokenPaginationHeaders instantiates a new TokenPaginationHeaders object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewTokenPaginationHeaders() *TokenPaginationHeaders {\n\tthis := TokenPaginationHeaders{}\n\treturn &this\n}\n\n// NewTokenPaginationHeadersWithDefaults instantiates a new TokenPaginationHeaders object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewTokenPaginationHeadersWithDefaults() *TokenPaginationHeaders {\n\tthis := TokenPaginationHeaders{}\n\treturn &this\n}\n\n// GetLink returns the Link field value if set, zero value otherwise.\nfunc (o *TokenPaginationHeaders) GetLink() string {\n\tif o == nil || IsNil(o.Link) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Link\n}\n\n// GetLinkOk returns a tuple with the Link field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *TokenPaginationHeaders) GetLinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Link) {\n\t\treturn nil, false\n\t}\n\treturn o.Link, true\n}\n\n// HasLink returns a boolean if a field has been set.\nfunc (o *TokenPaginationHeaders) HasLink() bool {\n\tif o != nil && !IsNil(o.Link) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLink gets a reference to the given string and assigns it to the Link field.\nfunc (o *TokenPaginationHeaders) SetLink(v string) {\n\to.Link = &v\n}\n\n// GetXTotalCount returns the XTotalCount field value if set, zero value otherwise.\nfunc (o *TokenPaginationHeaders) GetXTotalCount() string {\n\tif o == nil || IsNil(o.XTotalCount) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.XTotalCount\n}\n\n// GetXTotalCountOk returns a tuple with the XTotalCount field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *TokenPaginationHeaders) GetXTotalCountOk() (*string, bool) {\n\tif o == nil || IsNil(o.XTotalCount) {\n\t\treturn nil, false\n\t}\n\treturn o.XTotalCount, true\n}\n\n// HasXTotalCount returns a boolean if a field has been set.\nfunc (o *TokenPaginationHeaders) HasXTotalCount() bool {\n\tif o != nil && !IsNil(o.XTotalCount) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetXTotalCount gets a reference to the given string and assigns it to the XTotalCount field.\nfunc (o *TokenPaginationHeaders) SetXTotalCount(v string) {\n\to.XTotalCount = &v\n}\n\nfunc (o TokenPaginationHeaders) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o TokenPaginationHeaders) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Link) {\n\t\ttoSerialize[\"link\"] = o.Link\n\t}\n\tif !IsNil(o.XTotalCount) {\n\t\ttoSerialize[\"x-total-count\"] = o.XTotalCount\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *TokenPaginationHeaders) UnmarshalJSON(data []byte) (err error) {\n\tvarTokenPaginationHeaders := _TokenPaginationHeaders{}\n\n\terr = json.Unmarshal(data, &varTokenPaginationHeaders)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = TokenPaginationHeaders(varTokenPaginationHeaders)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"link\")\n\t\tdelete(additionalProperties, \"x-total-count\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableTokenPaginationHeaders struct {\n\tvalue *TokenPaginationHeaders\n\tisSet bool\n}\n\nfunc (v NullableTokenPaginationHeaders) Get() *TokenPaginationHeaders {\n\treturn v.value\n}\n\nfunc (v *NullableTokenPaginationHeaders) Set(val *TokenPaginationHeaders) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableTokenPaginationHeaders) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableTokenPaginationHeaders) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableTokenPaginationHeaders(val *TokenPaginationHeaders) *NullableTokenPaginationHeaders {\n\treturn &NullableTokenPaginationHeaders{value: val, isSet: true}\n}\n\nfunc (v NullableTokenPaginationHeaders) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableTokenPaginationHeaders) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_container.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiContainer type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiContainer{}\n\n// UiContainer Container represents a HTML Form. The container can work with both HTTP Form and JSON requests\ntype UiContainer struct {\n\t// Action should be used as the form action URL `<form action=\\\"{{ .Action }}\\\" method=\\\"post\\\">`.\n\tAction   string   `json:\"action\"`\n\tMessages []UiText `json:\"messages,omitempty\"`\n\t// Method is the form method (e.g. POST)\n\tMethod               string   `json:\"method\"`\n\tNodes                []UiNode `json:\"nodes\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiContainer UiContainer\n\n// NewUiContainer instantiates a new UiContainer object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiContainer(action string, method string, nodes []UiNode) *UiContainer {\n\tthis := UiContainer{}\n\tthis.Action = action\n\tthis.Method = method\n\tthis.Nodes = nodes\n\treturn &this\n}\n\n// NewUiContainerWithDefaults instantiates a new UiContainer object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiContainerWithDefaults() *UiContainer {\n\tthis := UiContainer{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *UiContainer) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *UiContainer) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *UiContainer) SetAction(v string) {\n\to.Action = v\n}\n\n// GetMessages returns the Messages field value if set, zero value otherwise.\nfunc (o *UiContainer) GetMessages() []UiText {\n\tif o == nil || IsNil(o.Messages) {\n\t\tvar ret []UiText\n\t\treturn ret\n\t}\n\treturn o.Messages\n}\n\n// GetMessagesOk returns a tuple with the Messages field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiContainer) GetMessagesOk() ([]UiText, bool) {\n\tif o == nil || IsNil(o.Messages) {\n\t\treturn nil, false\n\t}\n\treturn o.Messages, true\n}\n\n// HasMessages returns a boolean if a field has been set.\nfunc (o *UiContainer) HasMessages() bool {\n\tif o != nil && !IsNil(o.Messages) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMessages gets a reference to the given []UiText and assigns it to the Messages field.\nfunc (o *UiContainer) SetMessages(v []UiText) {\n\to.Messages = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UiContainer) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UiContainer) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UiContainer) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetNodes returns the Nodes field value\nfunc (o *UiContainer) GetNodes() []UiNode {\n\tif o == nil {\n\t\tvar ret []UiNode\n\t\treturn ret\n\t}\n\n\treturn o.Nodes\n}\n\n// GetNodesOk returns a tuple with the Nodes field value\n// and a boolean to check if the value has been set.\nfunc (o *UiContainer) GetNodesOk() ([]UiNode, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.Nodes, true\n}\n\n// SetNodes sets field value\nfunc (o *UiContainer) SetNodes(v []UiNode) {\n\to.Nodes = v\n}\n\nfunc (o UiContainer) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiContainer) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\tif !IsNil(o.Messages) {\n\t\ttoSerialize[\"messages\"] = o.Messages\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"nodes\"] = o.Nodes\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiContainer) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"method\",\n\t\t\"nodes\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiContainer := _UiContainer{}\n\n\terr = json.Unmarshal(data, &varUiContainer)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiContainer(varUiContainer)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"messages\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"nodes\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiContainer struct {\n\tvalue *UiContainer\n\tisSet bool\n}\n\nfunc (v NullableUiContainer) Get() *UiContainer {\n\treturn v.value\n}\n\nfunc (v *NullableUiContainer) Set(val *UiContainer) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiContainer) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiContainer) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiContainer(val *UiContainer) *NullableUiContainer {\n\treturn &NullableUiContainer{value: val, isSet: true}\n}\n\nfunc (v NullableUiContainer) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiContainer) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_node.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNode type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNode{}\n\n// UiNode Nodes are represented as HTML elements or their native UI equivalents. For example, a node can be an `<img>` tag, or an `<input element>` but also `some plain text`.\ntype UiNode struct {\n\tAttributes UiNodeAttributes `json:\"attributes\"`\n\t// Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup captcha CaptchaGroup saml SAMLGroup\n\tGroup    string     `json:\"group\"`\n\tMessages []UiText   `json:\"messages\"`\n\tMeta     UiNodeMeta `json:\"meta\"`\n\t// The node's type text Text input Input img Image a Anchor script Script div Division\n\tType                 string `json:\"type\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNode UiNode\n\n// NewUiNode instantiates a new UiNode object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNode(attributes UiNodeAttributes, group string, messages []UiText, meta UiNodeMeta, type_ string) *UiNode {\n\tthis := UiNode{}\n\tthis.Attributes = attributes\n\tthis.Group = group\n\tthis.Messages = messages\n\tthis.Meta = meta\n\tthis.Type = type_\n\treturn &this\n}\n\n// NewUiNodeWithDefaults instantiates a new UiNode object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeWithDefaults() *UiNode {\n\tthis := UiNode{}\n\treturn &this\n}\n\n// GetAttributes returns the Attributes field value\nfunc (o *UiNode) GetAttributes() UiNodeAttributes {\n\tif o == nil {\n\t\tvar ret UiNodeAttributes\n\t\treturn ret\n\t}\n\n\treturn o.Attributes\n}\n\n// GetAttributesOk returns a tuple with the Attributes field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetAttributesOk() (*UiNodeAttributes, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Attributes, true\n}\n\n// SetAttributes sets field value\nfunc (o *UiNode) SetAttributes(v UiNodeAttributes) {\n\to.Attributes = v\n}\n\n// GetGroup returns the Group field value\nfunc (o *UiNode) GetGroup() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Group\n}\n\n// GetGroupOk returns a tuple with the Group field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetGroupOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Group, true\n}\n\n// SetGroup sets field value\nfunc (o *UiNode) SetGroup(v string) {\n\to.Group = v\n}\n\n// GetMessages returns the Messages field value\nfunc (o *UiNode) GetMessages() []UiText {\n\tif o == nil {\n\t\tvar ret []UiText\n\t\treturn ret\n\t}\n\n\treturn o.Messages\n}\n\n// GetMessagesOk returns a tuple with the Messages field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetMessagesOk() ([]UiText, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.Messages, true\n}\n\n// SetMessages sets field value\nfunc (o *UiNode) SetMessages(v []UiText) {\n\to.Messages = v\n}\n\n// GetMeta returns the Meta field value\nfunc (o *UiNode) GetMeta() UiNodeMeta {\n\tif o == nil {\n\t\tvar ret UiNodeMeta\n\t\treturn ret\n\t}\n\n\treturn o.Meta\n}\n\n// GetMetaOk returns a tuple with the Meta field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetMetaOk() (*UiNodeMeta, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Meta, true\n}\n\n// SetMeta sets field value\nfunc (o *UiNode) SetMeta(v UiNodeMeta) {\n\to.Meta = v\n}\n\n// GetType returns the Type field value\nfunc (o *UiNode) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *UiNode) SetType(v string) {\n\to.Type = v\n}\n\nfunc (o UiNode) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNode) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"attributes\"] = o.Attributes\n\ttoSerialize[\"group\"] = o.Group\n\ttoSerialize[\"messages\"] = o.Messages\n\ttoSerialize[\"meta\"] = o.Meta\n\ttoSerialize[\"type\"] = o.Type\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNode) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"attributes\",\n\t\t\"group\",\n\t\t\"messages\",\n\t\t\"meta\",\n\t\t\"type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNode := _UiNode{}\n\n\terr = json.Unmarshal(data, &varUiNode)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNode(varUiNode)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"attributes\")\n\t\tdelete(additionalProperties, \"group\")\n\t\tdelete(additionalProperties, \"messages\")\n\t\tdelete(additionalProperties, \"meta\")\n\t\tdelete(additionalProperties, \"type\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNode struct {\n\tvalue *UiNode\n\tisSet bool\n}\n\nfunc (v NullableUiNode) Get() *UiNode {\n\treturn v.value\n}\n\nfunc (v *NullableUiNode) Set(val *UiNode) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNode) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNode) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNode(val *UiNode) *NullableUiNode {\n\treturn &NullableUiNode{value: val, isSet: true}\n}\n\nfunc (v NullableUiNode) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNode) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_node_anchor_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeAnchorAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeAnchorAttributes{}\n\n// UiNodeAnchorAttributes struct for UiNodeAnchorAttributes\ntype UiNodeAnchorAttributes struct {\n\t// The link's href (destination) URL.  format: uri\n\tHref string `json:\"href\"`\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"a\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType             string `json:\"node_type\"`\n\tTitle                UiText `json:\"title\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeAnchorAttributes UiNodeAnchorAttributes\n\n// NewUiNodeAnchorAttributes instantiates a new UiNodeAnchorAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeAnchorAttributes(href string, id string, nodeType string, title UiText) *UiNodeAnchorAttributes {\n\tthis := UiNodeAnchorAttributes{}\n\tthis.Href = href\n\tthis.Id = id\n\tthis.NodeType = nodeType\n\tthis.Title = title\n\treturn &this\n}\n\n// NewUiNodeAnchorAttributesWithDefaults instantiates a new UiNodeAnchorAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeAnchorAttributesWithDefaults() *UiNodeAnchorAttributes {\n\tthis := UiNodeAnchorAttributes{}\n\treturn &this\n}\n\n// GetHref returns the Href field value\nfunc (o *UiNodeAnchorAttributes) GetHref() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Href\n}\n\n// GetHrefOk returns a tuple with the Href field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeAnchorAttributes) GetHrefOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Href, true\n}\n\n// SetHref sets field value\nfunc (o *UiNodeAnchorAttributes) SetHref(v string) {\n\to.Href = v\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeAnchorAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeAnchorAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeAnchorAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeAnchorAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeAnchorAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeAnchorAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetTitle returns the Title field value\nfunc (o *UiNodeAnchorAttributes) GetTitle() UiText {\n\tif o == nil {\n\t\tvar ret UiText\n\t\treturn ret\n\t}\n\n\treturn o.Title\n}\n\n// GetTitleOk returns a tuple with the Title field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeAnchorAttributes) GetTitleOk() (*UiText, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Title, true\n}\n\n// SetTitle sets field value\nfunc (o *UiNodeAnchorAttributes) SetTitle(v UiText) {\n\to.Title = v\n}\n\nfunc (o UiNodeAnchorAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeAnchorAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"href\"] = o.Href\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"node_type\"] = o.NodeType\n\ttoSerialize[\"title\"] = o.Title\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeAnchorAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"href\",\n\t\t\"id\",\n\t\t\"node_type\",\n\t\t\"title\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeAnchorAttributes := _UiNodeAnchorAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeAnchorAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeAnchorAttributes(varUiNodeAnchorAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"href\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"title\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeAnchorAttributes struct {\n\tvalue *UiNodeAnchorAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeAnchorAttributes) Get() *UiNodeAnchorAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeAnchorAttributes) Set(val *UiNodeAnchorAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeAnchorAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeAnchorAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeAnchorAttributes(val *UiNodeAnchorAttributes) *NullableUiNodeAnchorAttributes {\n\treturn &NullableUiNodeAnchorAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeAnchorAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeAnchorAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_node_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UiNodeAttributes - struct for UiNodeAttributes\ntype UiNodeAttributes struct {\n\tUiNodeAnchorAttributes   *UiNodeAnchorAttributes\n\tUiNodeDivisionAttributes *UiNodeDivisionAttributes\n\tUiNodeImageAttributes    *UiNodeImageAttributes\n\tUiNodeInputAttributes    *UiNodeInputAttributes\n\tUiNodeScriptAttributes   *UiNodeScriptAttributes\n\tUiNodeTextAttributes     *UiNodeTextAttributes\n}\n\n// UiNodeAnchorAttributesAsUiNodeAttributes is a convenience function that returns UiNodeAnchorAttributes wrapped in UiNodeAttributes\nfunc UiNodeAnchorAttributesAsUiNodeAttributes(v *UiNodeAnchorAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeAnchorAttributes: v,\n\t}\n}\n\n// UiNodeDivisionAttributesAsUiNodeAttributes is a convenience function that returns UiNodeDivisionAttributes wrapped in UiNodeAttributes\nfunc UiNodeDivisionAttributesAsUiNodeAttributes(v *UiNodeDivisionAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeDivisionAttributes: v,\n\t}\n}\n\n// UiNodeImageAttributesAsUiNodeAttributes is a convenience function that returns UiNodeImageAttributes wrapped in UiNodeAttributes\nfunc UiNodeImageAttributesAsUiNodeAttributes(v *UiNodeImageAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeImageAttributes: v,\n\t}\n}\n\n// UiNodeInputAttributesAsUiNodeAttributes is a convenience function that returns UiNodeInputAttributes wrapped in UiNodeAttributes\nfunc UiNodeInputAttributesAsUiNodeAttributes(v *UiNodeInputAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeInputAttributes: v,\n\t}\n}\n\n// UiNodeScriptAttributesAsUiNodeAttributes is a convenience function that returns UiNodeScriptAttributes wrapped in UiNodeAttributes\nfunc UiNodeScriptAttributesAsUiNodeAttributes(v *UiNodeScriptAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeScriptAttributes: v,\n\t}\n}\n\n// UiNodeTextAttributesAsUiNodeAttributes is a convenience function that returns UiNodeTextAttributes wrapped in UiNodeAttributes\nfunc UiNodeTextAttributesAsUiNodeAttributes(v *UiNodeTextAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeTextAttributes: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UiNodeAttributes) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'a'\n\tif jsonDict[\"node_type\"] == \"a\" {\n\t\t// try to unmarshal JSON data into UiNodeAnchorAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeAnchorAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeAnchorAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeAnchorAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeAnchorAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'div'\n\tif jsonDict[\"node_type\"] == \"div\" {\n\t\t// try to unmarshal JSON data into UiNodeDivisionAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeDivisionAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeDivisionAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeDivisionAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeDivisionAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'img'\n\tif jsonDict[\"node_type\"] == \"img\" {\n\t\t// try to unmarshal JSON data into UiNodeImageAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeImageAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeImageAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeImageAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeImageAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'input'\n\tif jsonDict[\"node_type\"] == \"input\" {\n\t\t// try to unmarshal JSON data into UiNodeInputAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeInputAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeInputAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeInputAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeInputAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'script'\n\tif jsonDict[\"node_type\"] == \"script\" {\n\t\t// try to unmarshal JSON data into UiNodeScriptAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeScriptAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeScriptAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeScriptAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeScriptAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'text'\n\tif jsonDict[\"node_type\"] == \"text\" {\n\t\t// try to unmarshal JSON data into UiNodeTextAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeTextAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeTextAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeTextAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeTextAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeAnchorAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeAnchorAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeAnchorAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeAnchorAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeAnchorAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeAnchorAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeAnchorAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeDivisionAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeDivisionAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeDivisionAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeDivisionAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeDivisionAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeDivisionAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeDivisionAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeImageAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeImageAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeImageAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeImageAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeImageAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeImageAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeImageAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeInputAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeInputAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeInputAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeInputAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeInputAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeInputAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeInputAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeScriptAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeScriptAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeScriptAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeScriptAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeScriptAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeScriptAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeScriptAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeTextAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeTextAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeTextAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeTextAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeTextAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeTextAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeTextAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UiNodeAttributes) MarshalJSON() ([]byte, error) {\n\tif src.UiNodeAnchorAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeAnchorAttributes)\n\t}\n\n\tif src.UiNodeDivisionAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeDivisionAttributes)\n\t}\n\n\tif src.UiNodeImageAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeImageAttributes)\n\t}\n\n\tif src.UiNodeInputAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeInputAttributes)\n\t}\n\n\tif src.UiNodeScriptAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeScriptAttributes)\n\t}\n\n\tif src.UiNodeTextAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeTextAttributes)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UiNodeAttributes) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UiNodeAnchorAttributes != nil {\n\t\treturn obj.UiNodeAnchorAttributes\n\t}\n\n\tif obj.UiNodeDivisionAttributes != nil {\n\t\treturn obj.UiNodeDivisionAttributes\n\t}\n\n\tif obj.UiNodeImageAttributes != nil {\n\t\treturn obj.UiNodeImageAttributes\n\t}\n\n\tif obj.UiNodeInputAttributes != nil {\n\t\treturn obj.UiNodeInputAttributes\n\t}\n\n\tif obj.UiNodeScriptAttributes != nil {\n\t\treturn obj.UiNodeScriptAttributes\n\t}\n\n\tif obj.UiNodeTextAttributes != nil {\n\t\treturn obj.UiNodeTextAttributes\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UiNodeAttributes) GetActualInstanceValue() interface{} {\n\tif obj.UiNodeAnchorAttributes != nil {\n\t\treturn *obj.UiNodeAnchorAttributes\n\t}\n\n\tif obj.UiNodeDivisionAttributes != nil {\n\t\treturn *obj.UiNodeDivisionAttributes\n\t}\n\n\tif obj.UiNodeImageAttributes != nil {\n\t\treturn *obj.UiNodeImageAttributes\n\t}\n\n\tif obj.UiNodeInputAttributes != nil {\n\t\treturn *obj.UiNodeInputAttributes\n\t}\n\n\tif obj.UiNodeScriptAttributes != nil {\n\t\treturn *obj.UiNodeScriptAttributes\n\t}\n\n\tif obj.UiNodeTextAttributes != nil {\n\t\treturn *obj.UiNodeTextAttributes\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUiNodeAttributes struct {\n\tvalue *UiNodeAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeAttributes) Get() *UiNodeAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeAttributes) Set(val *UiNodeAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeAttributes(val *UiNodeAttributes) *NullableUiNodeAttributes {\n\treturn &NullableUiNodeAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_node_division_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeDivisionAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeDivisionAttributes{}\n\n// UiNodeDivisionAttributes Division sections are used for interactive widgets that require a hook in the DOM / view.\ntype UiNodeDivisionAttributes struct {\n\t// A classname that should be rendered into the DOM.\n\tClass *string `json:\"class,omitempty\"`\n\t// Data is a map of key-value pairs that are passed to the division.  They may be used for `data-...` attributes.\n\tData *map[string]string `json:\"data,omitempty\"`\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// NodeType represents this node's type. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \\\"script\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType             string `json:\"node_type\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeDivisionAttributes UiNodeDivisionAttributes\n\n// NewUiNodeDivisionAttributes instantiates a new UiNodeDivisionAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeDivisionAttributes(id string, nodeType string) *UiNodeDivisionAttributes {\n\tthis := UiNodeDivisionAttributes{}\n\tthis.Id = id\n\tthis.NodeType = nodeType\n\treturn &this\n}\n\n// NewUiNodeDivisionAttributesWithDefaults instantiates a new UiNodeDivisionAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeDivisionAttributesWithDefaults() *UiNodeDivisionAttributes {\n\tthis := UiNodeDivisionAttributes{}\n\treturn &this\n}\n\n// GetClass returns the Class field value if set, zero value otherwise.\nfunc (o *UiNodeDivisionAttributes) GetClass() string {\n\tif o == nil || IsNil(o.Class) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Class\n}\n\n// GetClassOk returns a tuple with the Class field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeDivisionAttributes) GetClassOk() (*string, bool) {\n\tif o == nil || IsNil(o.Class) {\n\t\treturn nil, false\n\t}\n\treturn o.Class, true\n}\n\n// HasClass returns a boolean if a field has been set.\nfunc (o *UiNodeDivisionAttributes) HasClass() bool {\n\tif o != nil && !IsNil(o.Class) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClass gets a reference to the given string and assigns it to the Class field.\nfunc (o *UiNodeDivisionAttributes) SetClass(v string) {\n\to.Class = &v\n}\n\n// GetData returns the Data field value if set, zero value otherwise.\nfunc (o *UiNodeDivisionAttributes) GetData() map[string]string {\n\tif o == nil || IsNil(o.Data) {\n\t\tvar ret map[string]string\n\t\treturn ret\n\t}\n\treturn *o.Data\n}\n\n// GetDataOk returns a tuple with the Data field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeDivisionAttributes) GetDataOk() (*map[string]string, bool) {\n\tif o == nil || IsNil(o.Data) {\n\t\treturn nil, false\n\t}\n\treturn o.Data, true\n}\n\n// HasData returns a boolean if a field has been set.\nfunc (o *UiNodeDivisionAttributes) HasData() bool {\n\tif o != nil && !IsNil(o.Data) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetData gets a reference to the given map[string]string and assigns it to the Data field.\nfunc (o *UiNodeDivisionAttributes) SetData(v map[string]string) {\n\to.Data = &v\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeDivisionAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeDivisionAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeDivisionAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeDivisionAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeDivisionAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeDivisionAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\nfunc (o UiNodeDivisionAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeDivisionAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Class) {\n\t\ttoSerialize[\"class\"] = o.Class\n\t}\n\tif !IsNil(o.Data) {\n\t\ttoSerialize[\"data\"] = o.Data\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"node_type\"] = o.NodeType\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeDivisionAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"node_type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeDivisionAttributes := _UiNodeDivisionAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeDivisionAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeDivisionAttributes(varUiNodeDivisionAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"class\")\n\t\tdelete(additionalProperties, \"data\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeDivisionAttributes struct {\n\tvalue *UiNodeDivisionAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeDivisionAttributes) Get() *UiNodeDivisionAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeDivisionAttributes) Set(val *UiNodeDivisionAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeDivisionAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeDivisionAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeDivisionAttributes(val *UiNodeDivisionAttributes) *NullableUiNodeDivisionAttributes {\n\treturn &NullableUiNodeDivisionAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeDivisionAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeDivisionAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_node_image_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeImageAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeImageAttributes{}\n\n// UiNodeImageAttributes struct for UiNodeImageAttributes\ntype UiNodeImageAttributes struct {\n\t// Height of the image\n\tHeight int64 `json:\"height\"`\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"img\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType string `json:\"node_type\"`\n\t// The image's source URL.  format: uri\n\tSrc string `json:\"src\"`\n\t// Width of the image\n\tWidth                int64 `json:\"width\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeImageAttributes UiNodeImageAttributes\n\n// NewUiNodeImageAttributes instantiates a new UiNodeImageAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeImageAttributes(height int64, id string, nodeType string, src string, width int64) *UiNodeImageAttributes {\n\tthis := UiNodeImageAttributes{}\n\tthis.Height = height\n\tthis.Id = id\n\tthis.NodeType = nodeType\n\tthis.Src = src\n\tthis.Width = width\n\treturn &this\n}\n\n// NewUiNodeImageAttributesWithDefaults instantiates a new UiNodeImageAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeImageAttributesWithDefaults() *UiNodeImageAttributes {\n\tthis := UiNodeImageAttributes{}\n\treturn &this\n}\n\n// GetHeight returns the Height field value\nfunc (o *UiNodeImageAttributes) GetHeight() int64 {\n\tif o == nil {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\n\treturn o.Height\n}\n\n// GetHeightOk returns a tuple with the Height field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetHeightOk() (*int64, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Height, true\n}\n\n// SetHeight sets field value\nfunc (o *UiNodeImageAttributes) SetHeight(v int64) {\n\to.Height = v\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeImageAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeImageAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeImageAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeImageAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetSrc returns the Src field value\nfunc (o *UiNodeImageAttributes) GetSrc() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Src\n}\n\n// GetSrcOk returns a tuple with the Src field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetSrcOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Src, true\n}\n\n// SetSrc sets field value\nfunc (o *UiNodeImageAttributes) SetSrc(v string) {\n\to.Src = v\n}\n\n// GetWidth returns the Width field value\nfunc (o *UiNodeImageAttributes) GetWidth() int64 {\n\tif o == nil {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\n\treturn o.Width\n}\n\n// GetWidthOk returns a tuple with the Width field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetWidthOk() (*int64, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Width, true\n}\n\n// SetWidth sets field value\nfunc (o *UiNodeImageAttributes) SetWidth(v int64) {\n\to.Width = v\n}\n\nfunc (o UiNodeImageAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeImageAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"height\"] = o.Height\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"node_type\"] = o.NodeType\n\ttoSerialize[\"src\"] = o.Src\n\ttoSerialize[\"width\"] = o.Width\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeImageAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"height\",\n\t\t\"id\",\n\t\t\"node_type\",\n\t\t\"src\",\n\t\t\"width\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeImageAttributes := _UiNodeImageAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeImageAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeImageAttributes(varUiNodeImageAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"height\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"src\")\n\t\tdelete(additionalProperties, \"width\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeImageAttributes struct {\n\tvalue *UiNodeImageAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeImageAttributes) Get() *UiNodeImageAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeImageAttributes) Set(val *UiNodeImageAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeImageAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeImageAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeImageAttributes(val *UiNodeImageAttributes) *NullableUiNodeImageAttributes {\n\treturn &NullableUiNodeImageAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeImageAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeImageAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_node_input_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeInputAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeInputAttributes{}\n\n// UiNodeInputAttributes InputAttributes represents the attributes of an input node\ntype UiNodeInputAttributes struct {\n\t// The autocomplete attribute for the input. email InputAttributeAutocompleteEmail tel InputAttributeAutocompleteTel url InputAttributeAutocompleteUrl current-password InputAttributeAutocompleteCurrentPassword new-password InputAttributeAutocompleteNewPassword one-time-code InputAttributeAutocompleteOneTimeCode username webauthn InputAttributeAutocompleteUsernameWebauthn\n\tAutocomplete *string `json:\"autocomplete,omitempty\"`\n\t// Sets the input's disabled field to true or false.\n\tDisabled bool    `json:\"disabled\"`\n\tLabel    *UiText `json:\"label,omitempty\"`\n\t// MaxLength may contain the input's maximum length.\n\tMaxlength *int64 `json:\"maxlength,omitempty\"`\n\t// The input's element name.\n\tName string `json:\"name\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"input\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType string `json:\"node_type\"`\n\t// OnClick may contain javascript which should be executed on click. This is primarily used for WebAuthn.  Deprecated: Using OnClick requires the use of eval() which is a security risk. Use OnClickTrigger instead.\n\tOnclick *string `json:\"onclick,omitempty\"`\n\t// OnClickTrigger may contain a WebAuthn trigger which should be executed on click.  The trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login. oryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration oryWebAuthnLogin WebAuthnTriggersWebAuthnLogin oryPasskeyLogin WebAuthnTriggersPasskeyLogin oryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit oryPasskeyRegistration WebAuthnTriggersPasskeyRegistration oryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\n\tOnclickTrigger *string `json:\"onclickTrigger,omitempty\"`\n\t// OnLoad may contain javascript which should be executed on load. This is primarily used for WebAuthn.  Deprecated: Using OnLoad requires the use of eval() which is a security risk. Use OnLoadTrigger instead.\n\tOnload *string `json:\"onload,omitempty\"`\n\t// OnLoadTrigger may contain a WebAuthn trigger which should be executed on load.  The trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login. oryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration oryWebAuthnLogin WebAuthnTriggersWebAuthnLogin oryPasskeyLogin WebAuthnTriggersPasskeyLogin oryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit oryPasskeyRegistration WebAuthnTriggersPasskeyRegistration oryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\n\tOnloadTrigger *string `json:\"onloadTrigger,omitempty\"`\n\t// The input's pattern.\n\tPattern *string `json:\"pattern,omitempty\"`\n\t// Mark this input field as required.\n\tRequired *bool `json:\"required,omitempty\"`\n\t// The input's element type. text InputAttributeTypeText password InputAttributeTypePassword number InputAttributeTypeNumber checkbox InputAttributeTypeCheckbox hidden InputAttributeTypeHidden email InputAttributeTypeEmail tel InputAttributeTypeTel submit InputAttributeTypeSubmit button InputAttributeTypeButton datetime-local InputAttributeTypeDateTimeLocal date InputAttributeTypeDate url InputAttributeTypeURI\n\tType string `json:\"type\"`\n\t// The input's value.\n\tValue                interface{} `json:\"value,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeInputAttributes UiNodeInputAttributes\n\n// NewUiNodeInputAttributes instantiates a new UiNodeInputAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeInputAttributes(disabled bool, name string, nodeType string, type_ string) *UiNodeInputAttributes {\n\tthis := UiNodeInputAttributes{}\n\tthis.Disabled = disabled\n\tthis.Name = name\n\tthis.NodeType = nodeType\n\tthis.Type = type_\n\treturn &this\n}\n\n// NewUiNodeInputAttributesWithDefaults instantiates a new UiNodeInputAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeInputAttributesWithDefaults() *UiNodeInputAttributes {\n\tthis := UiNodeInputAttributes{}\n\treturn &this\n}\n\n// GetAutocomplete returns the Autocomplete field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetAutocomplete() string {\n\tif o == nil || IsNil(o.Autocomplete) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Autocomplete\n}\n\n// GetAutocompleteOk returns a tuple with the Autocomplete field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetAutocompleteOk() (*string, bool) {\n\tif o == nil || IsNil(o.Autocomplete) {\n\t\treturn nil, false\n\t}\n\treturn o.Autocomplete, true\n}\n\n// HasAutocomplete returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasAutocomplete() bool {\n\tif o != nil && !IsNil(o.Autocomplete) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAutocomplete gets a reference to the given string and assigns it to the Autocomplete field.\nfunc (o *UiNodeInputAttributes) SetAutocomplete(v string) {\n\to.Autocomplete = &v\n}\n\n// GetDisabled returns the Disabled field value\nfunc (o *UiNodeInputAttributes) GetDisabled() bool {\n\tif o == nil {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\n\treturn o.Disabled\n}\n\n// GetDisabledOk returns a tuple with the Disabled field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetDisabledOk() (*bool, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Disabled, true\n}\n\n// SetDisabled sets field value\nfunc (o *UiNodeInputAttributes) SetDisabled(v bool) {\n\to.Disabled = v\n}\n\n// GetLabel returns the Label field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetLabel() UiText {\n\tif o == nil || IsNil(o.Label) {\n\t\tvar ret UiText\n\t\treturn ret\n\t}\n\treturn *o.Label\n}\n\n// GetLabelOk returns a tuple with the Label field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetLabelOk() (*UiText, bool) {\n\tif o == nil || IsNil(o.Label) {\n\t\treturn nil, false\n\t}\n\treturn o.Label, true\n}\n\n// HasLabel returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasLabel() bool {\n\tif o != nil && !IsNil(o.Label) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLabel gets a reference to the given UiText and assigns it to the Label field.\nfunc (o *UiNodeInputAttributes) SetLabel(v UiText) {\n\to.Label = &v\n}\n\n// GetMaxlength returns the Maxlength field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetMaxlength() int64 {\n\tif o == nil || IsNil(o.Maxlength) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Maxlength\n}\n\n// GetMaxlengthOk returns a tuple with the Maxlength field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetMaxlengthOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Maxlength) {\n\t\treturn nil, false\n\t}\n\treturn o.Maxlength, true\n}\n\n// HasMaxlength returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasMaxlength() bool {\n\tif o != nil && !IsNil(o.Maxlength) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMaxlength gets a reference to the given int64 and assigns it to the Maxlength field.\nfunc (o *UiNodeInputAttributes) SetMaxlength(v int64) {\n\to.Maxlength = &v\n}\n\n// GetName returns the Name field value\nfunc (o *UiNodeInputAttributes) GetName() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Name\n}\n\n// GetNameOk returns a tuple with the Name field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetNameOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Name, true\n}\n\n// SetName sets field value\nfunc (o *UiNodeInputAttributes) SetName(v string) {\n\to.Name = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeInputAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeInputAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetOnclick returns the Onclick field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetOnclick() string {\n\tif o == nil || IsNil(o.Onclick) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Onclick\n}\n\n// GetOnclickOk returns a tuple with the Onclick field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetOnclickOk() (*string, bool) {\n\tif o == nil || IsNil(o.Onclick) {\n\t\treturn nil, false\n\t}\n\treturn o.Onclick, true\n}\n\n// HasOnclick returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasOnclick() bool {\n\tif o != nil && !IsNil(o.Onclick) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOnclick gets a reference to the given string and assigns it to the Onclick field.\nfunc (o *UiNodeInputAttributes) SetOnclick(v string) {\n\to.Onclick = &v\n}\n\n// GetOnclickTrigger returns the OnclickTrigger field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetOnclickTrigger() string {\n\tif o == nil || IsNil(o.OnclickTrigger) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OnclickTrigger\n}\n\n// GetOnclickTriggerOk returns a tuple with the OnclickTrigger field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetOnclickTriggerOk() (*string, bool) {\n\tif o == nil || IsNil(o.OnclickTrigger) {\n\t\treturn nil, false\n\t}\n\treturn o.OnclickTrigger, true\n}\n\n// HasOnclickTrigger returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasOnclickTrigger() bool {\n\tif o != nil && !IsNil(o.OnclickTrigger) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOnclickTrigger gets a reference to the given string and assigns it to the OnclickTrigger field.\nfunc (o *UiNodeInputAttributes) SetOnclickTrigger(v string) {\n\to.OnclickTrigger = &v\n}\n\n// GetOnload returns the Onload field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetOnload() string {\n\tif o == nil || IsNil(o.Onload) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Onload\n}\n\n// GetOnloadOk returns a tuple with the Onload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetOnloadOk() (*string, bool) {\n\tif o == nil || IsNil(o.Onload) {\n\t\treturn nil, false\n\t}\n\treturn o.Onload, true\n}\n\n// HasOnload returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasOnload() bool {\n\tif o != nil && !IsNil(o.Onload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOnload gets a reference to the given string and assigns it to the Onload field.\nfunc (o *UiNodeInputAttributes) SetOnload(v string) {\n\to.Onload = &v\n}\n\n// GetOnloadTrigger returns the OnloadTrigger field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetOnloadTrigger() string {\n\tif o == nil || IsNil(o.OnloadTrigger) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OnloadTrigger\n}\n\n// GetOnloadTriggerOk returns a tuple with the OnloadTrigger field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetOnloadTriggerOk() (*string, bool) {\n\tif o == nil || IsNil(o.OnloadTrigger) {\n\t\treturn nil, false\n\t}\n\treturn o.OnloadTrigger, true\n}\n\n// HasOnloadTrigger returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasOnloadTrigger() bool {\n\tif o != nil && !IsNil(o.OnloadTrigger) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOnloadTrigger gets a reference to the given string and assigns it to the OnloadTrigger field.\nfunc (o *UiNodeInputAttributes) SetOnloadTrigger(v string) {\n\to.OnloadTrigger = &v\n}\n\n// GetPattern returns the Pattern field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetPattern() string {\n\tif o == nil || IsNil(o.Pattern) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Pattern\n}\n\n// GetPatternOk returns a tuple with the Pattern field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetPatternOk() (*string, bool) {\n\tif o == nil || IsNil(o.Pattern) {\n\t\treturn nil, false\n\t}\n\treturn o.Pattern, true\n}\n\n// HasPattern returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasPattern() bool {\n\tif o != nil && !IsNil(o.Pattern) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPattern gets a reference to the given string and assigns it to the Pattern field.\nfunc (o *UiNodeInputAttributes) SetPattern(v string) {\n\to.Pattern = &v\n}\n\n// GetRequired returns the Required field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetRequired() bool {\n\tif o == nil || IsNil(o.Required) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.Required\n}\n\n// GetRequiredOk returns a tuple with the Required field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetRequiredOk() (*bool, bool) {\n\tif o == nil || IsNil(o.Required) {\n\t\treturn nil, false\n\t}\n\treturn o.Required, true\n}\n\n// HasRequired returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasRequired() bool {\n\tif o != nil && !IsNil(o.Required) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequired gets a reference to the given bool and assigns it to the Required field.\nfunc (o *UiNodeInputAttributes) SetRequired(v bool) {\n\to.Required = &v\n}\n\n// GetType returns the Type field value\nfunc (o *UiNodeInputAttributes) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *UiNodeInputAttributes) SetType(v string) {\n\to.Type = v\n}\n\n// GetValue returns the Value field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *UiNodeInputAttributes) GetValue() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Value\n}\n\n// GetValueOk returns a tuple with the Value field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *UiNodeInputAttributes) GetValueOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Value) {\n\t\treturn nil, false\n\t}\n\treturn &o.Value, true\n}\n\n// HasValue returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasValue() bool {\n\tif o != nil && !IsNil(o.Value) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetValue gets a reference to the given interface{} and assigns it to the Value field.\nfunc (o *UiNodeInputAttributes) SetValue(v interface{}) {\n\to.Value = v\n}\n\nfunc (o UiNodeInputAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeInputAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Autocomplete) {\n\t\ttoSerialize[\"autocomplete\"] = o.Autocomplete\n\t}\n\ttoSerialize[\"disabled\"] = o.Disabled\n\tif !IsNil(o.Label) {\n\t\ttoSerialize[\"label\"] = o.Label\n\t}\n\tif !IsNil(o.Maxlength) {\n\t\ttoSerialize[\"maxlength\"] = o.Maxlength\n\t}\n\ttoSerialize[\"name\"] = o.Name\n\ttoSerialize[\"node_type\"] = o.NodeType\n\tif !IsNil(o.Onclick) {\n\t\ttoSerialize[\"onclick\"] = o.Onclick\n\t}\n\tif !IsNil(o.OnclickTrigger) {\n\t\ttoSerialize[\"onclickTrigger\"] = o.OnclickTrigger\n\t}\n\tif !IsNil(o.Onload) {\n\t\ttoSerialize[\"onload\"] = o.Onload\n\t}\n\tif !IsNil(o.OnloadTrigger) {\n\t\ttoSerialize[\"onloadTrigger\"] = o.OnloadTrigger\n\t}\n\tif !IsNil(o.Pattern) {\n\t\ttoSerialize[\"pattern\"] = o.Pattern\n\t}\n\tif !IsNil(o.Required) {\n\t\ttoSerialize[\"required\"] = o.Required\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\tif o.Value != nil {\n\t\ttoSerialize[\"value\"] = o.Value\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeInputAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"disabled\",\n\t\t\"name\",\n\t\t\"node_type\",\n\t\t\"type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeInputAttributes := _UiNodeInputAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeInputAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeInputAttributes(varUiNodeInputAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"autocomplete\")\n\t\tdelete(additionalProperties, \"disabled\")\n\t\tdelete(additionalProperties, \"label\")\n\t\tdelete(additionalProperties, \"maxlength\")\n\t\tdelete(additionalProperties, \"name\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"onclick\")\n\t\tdelete(additionalProperties, \"onclickTrigger\")\n\t\tdelete(additionalProperties, \"onload\")\n\t\tdelete(additionalProperties, \"onloadTrigger\")\n\t\tdelete(additionalProperties, \"pattern\")\n\t\tdelete(additionalProperties, \"required\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"value\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeInputAttributes struct {\n\tvalue *UiNodeInputAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeInputAttributes) Get() *UiNodeInputAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeInputAttributes) Set(val *UiNodeInputAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeInputAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeInputAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeInputAttributes(val *UiNodeInputAttributes) *NullableUiNodeInputAttributes {\n\treturn &NullableUiNodeInputAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeInputAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeInputAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_node_meta.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the UiNodeMeta type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeMeta{}\n\n// UiNodeMeta This might include a label and other information that can optionally be used to render UIs.\ntype UiNodeMeta struct {\n\tLabel                *UiText `json:\"label,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeMeta UiNodeMeta\n\n// NewUiNodeMeta instantiates a new UiNodeMeta object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeMeta() *UiNodeMeta {\n\tthis := UiNodeMeta{}\n\treturn &this\n}\n\n// NewUiNodeMetaWithDefaults instantiates a new UiNodeMeta object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeMetaWithDefaults() *UiNodeMeta {\n\tthis := UiNodeMeta{}\n\treturn &this\n}\n\n// GetLabel returns the Label field value if set, zero value otherwise.\nfunc (o *UiNodeMeta) GetLabel() UiText {\n\tif o == nil || IsNil(o.Label) {\n\t\tvar ret UiText\n\t\treturn ret\n\t}\n\treturn *o.Label\n}\n\n// GetLabelOk returns a tuple with the Label field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeMeta) GetLabelOk() (*UiText, bool) {\n\tif o == nil || IsNil(o.Label) {\n\t\treturn nil, false\n\t}\n\treturn o.Label, true\n}\n\n// HasLabel returns a boolean if a field has been set.\nfunc (o *UiNodeMeta) HasLabel() bool {\n\tif o != nil && !IsNil(o.Label) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLabel gets a reference to the given UiText and assigns it to the Label field.\nfunc (o *UiNodeMeta) SetLabel(v UiText) {\n\to.Label = &v\n}\n\nfunc (o UiNodeMeta) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeMeta) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Label) {\n\t\ttoSerialize[\"label\"] = o.Label\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeMeta) UnmarshalJSON(data []byte) (err error) {\n\tvarUiNodeMeta := _UiNodeMeta{}\n\n\terr = json.Unmarshal(data, &varUiNodeMeta)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeMeta(varUiNodeMeta)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"label\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeMeta struct {\n\tvalue *UiNodeMeta\n\tisSet bool\n}\n\nfunc (v NullableUiNodeMeta) Get() *UiNodeMeta {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeMeta) Set(val *UiNodeMeta) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeMeta) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeMeta) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeMeta(val *UiNodeMeta) *NullableUiNodeMeta {\n\treturn &NullableUiNodeMeta{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeMeta) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeMeta) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_node_script_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeScriptAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeScriptAttributes{}\n\n// UiNodeScriptAttributes struct for UiNodeScriptAttributes\ntype UiNodeScriptAttributes struct {\n\t// The script async type\n\tAsync bool `json:\"async\"`\n\t// The script cross origin policy\n\tCrossorigin string `json:\"crossorigin\"`\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// The script's integrity hash\n\tIntegrity string `json:\"integrity\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \\\"script\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType string `json:\"node_type\"`\n\t// Nonce for CSP  A nonce you may want to use to improve your Content Security Policy. You do not have to use this value but if you want to improve your CSP policies you may use it. You can also choose to use your own nonce value!\n\tNonce string `json:\"nonce\"`\n\t// The script referrer policy\n\tReferrerpolicy string `json:\"referrerpolicy\"`\n\t// The script source\n\tSrc string `json:\"src\"`\n\t// The script MIME type\n\tType                 string `json:\"type\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeScriptAttributes UiNodeScriptAttributes\n\n// NewUiNodeScriptAttributes instantiates a new UiNodeScriptAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeScriptAttributes(async bool, crossorigin string, id string, integrity string, nodeType string, nonce string, referrerpolicy string, src string, type_ string) *UiNodeScriptAttributes {\n\tthis := UiNodeScriptAttributes{}\n\tthis.Async = async\n\tthis.Crossorigin = crossorigin\n\tthis.Id = id\n\tthis.Integrity = integrity\n\tthis.NodeType = nodeType\n\tthis.Nonce = nonce\n\tthis.Referrerpolicy = referrerpolicy\n\tthis.Src = src\n\tthis.Type = type_\n\treturn &this\n}\n\n// NewUiNodeScriptAttributesWithDefaults instantiates a new UiNodeScriptAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeScriptAttributesWithDefaults() *UiNodeScriptAttributes {\n\tthis := UiNodeScriptAttributes{}\n\treturn &this\n}\n\n// GetAsync returns the Async field value\nfunc (o *UiNodeScriptAttributes) GetAsync() bool {\n\tif o == nil {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\n\treturn o.Async\n}\n\n// GetAsyncOk returns a tuple with the Async field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetAsyncOk() (*bool, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Async, true\n}\n\n// SetAsync sets field value\nfunc (o *UiNodeScriptAttributes) SetAsync(v bool) {\n\to.Async = v\n}\n\n// GetCrossorigin returns the Crossorigin field value\nfunc (o *UiNodeScriptAttributes) GetCrossorigin() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Crossorigin\n}\n\n// GetCrossoriginOk returns a tuple with the Crossorigin field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetCrossoriginOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Crossorigin, true\n}\n\n// SetCrossorigin sets field value\nfunc (o *UiNodeScriptAttributes) SetCrossorigin(v string) {\n\to.Crossorigin = v\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeScriptAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeScriptAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetIntegrity returns the Integrity field value\nfunc (o *UiNodeScriptAttributes) GetIntegrity() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Integrity\n}\n\n// GetIntegrityOk returns a tuple with the Integrity field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetIntegrityOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Integrity, true\n}\n\n// SetIntegrity sets field value\nfunc (o *UiNodeScriptAttributes) SetIntegrity(v string) {\n\to.Integrity = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeScriptAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeScriptAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetNonce returns the Nonce field value\nfunc (o *UiNodeScriptAttributes) GetNonce() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Nonce\n}\n\n// GetNonceOk returns a tuple with the Nonce field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetNonceOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Nonce, true\n}\n\n// SetNonce sets field value\nfunc (o *UiNodeScriptAttributes) SetNonce(v string) {\n\to.Nonce = v\n}\n\n// GetReferrerpolicy returns the Referrerpolicy field value\nfunc (o *UiNodeScriptAttributes) GetReferrerpolicy() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Referrerpolicy\n}\n\n// GetReferrerpolicyOk returns a tuple with the Referrerpolicy field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetReferrerpolicyOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Referrerpolicy, true\n}\n\n// SetReferrerpolicy sets field value\nfunc (o *UiNodeScriptAttributes) SetReferrerpolicy(v string) {\n\to.Referrerpolicy = v\n}\n\n// GetSrc returns the Src field value\nfunc (o *UiNodeScriptAttributes) GetSrc() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Src\n}\n\n// GetSrcOk returns a tuple with the Src field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetSrcOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Src, true\n}\n\n// SetSrc sets field value\nfunc (o *UiNodeScriptAttributes) SetSrc(v string) {\n\to.Src = v\n}\n\n// GetType returns the Type field value\nfunc (o *UiNodeScriptAttributes) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *UiNodeScriptAttributes) SetType(v string) {\n\to.Type = v\n}\n\nfunc (o UiNodeScriptAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeScriptAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"async\"] = o.Async\n\ttoSerialize[\"crossorigin\"] = o.Crossorigin\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"integrity\"] = o.Integrity\n\ttoSerialize[\"node_type\"] = o.NodeType\n\ttoSerialize[\"nonce\"] = o.Nonce\n\ttoSerialize[\"referrerpolicy\"] = o.Referrerpolicy\n\ttoSerialize[\"src\"] = o.Src\n\ttoSerialize[\"type\"] = o.Type\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeScriptAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"async\",\n\t\t\"crossorigin\",\n\t\t\"id\",\n\t\t\"integrity\",\n\t\t\"node_type\",\n\t\t\"nonce\",\n\t\t\"referrerpolicy\",\n\t\t\"src\",\n\t\t\"type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeScriptAttributes := _UiNodeScriptAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeScriptAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeScriptAttributes(varUiNodeScriptAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"async\")\n\t\tdelete(additionalProperties, \"crossorigin\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"integrity\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"nonce\")\n\t\tdelete(additionalProperties, \"referrerpolicy\")\n\t\tdelete(additionalProperties, \"src\")\n\t\tdelete(additionalProperties, \"type\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeScriptAttributes struct {\n\tvalue *UiNodeScriptAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeScriptAttributes) Get() *UiNodeScriptAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeScriptAttributes) Set(val *UiNodeScriptAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeScriptAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeScriptAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeScriptAttributes(val *UiNodeScriptAttributes) *NullableUiNodeScriptAttributes {\n\treturn &NullableUiNodeScriptAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeScriptAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeScriptAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_node_text_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeTextAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeTextAttributes{}\n\n// UiNodeTextAttributes struct for UiNodeTextAttributes\ntype UiNodeTextAttributes struct {\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"text\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType             string `json:\"node_type\"`\n\tText                 UiText `json:\"text\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeTextAttributes UiNodeTextAttributes\n\n// NewUiNodeTextAttributes instantiates a new UiNodeTextAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeTextAttributes(id string, nodeType string, text UiText) *UiNodeTextAttributes {\n\tthis := UiNodeTextAttributes{}\n\tthis.Id = id\n\tthis.NodeType = nodeType\n\tthis.Text = text\n\treturn &this\n}\n\n// NewUiNodeTextAttributesWithDefaults instantiates a new UiNodeTextAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeTextAttributesWithDefaults() *UiNodeTextAttributes {\n\tthis := UiNodeTextAttributes{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeTextAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeTextAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeTextAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeTextAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeTextAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeTextAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetText returns the Text field value\nfunc (o *UiNodeTextAttributes) GetText() UiText {\n\tif o == nil {\n\t\tvar ret UiText\n\t\treturn ret\n\t}\n\n\treturn o.Text\n}\n\n// GetTextOk returns a tuple with the Text field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeTextAttributes) GetTextOk() (*UiText, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Text, true\n}\n\n// SetText sets field value\nfunc (o *UiNodeTextAttributes) SetText(v UiText) {\n\to.Text = v\n}\n\nfunc (o UiNodeTextAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeTextAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"node_type\"] = o.NodeType\n\ttoSerialize[\"text\"] = o.Text\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeTextAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"node_type\",\n\t\t\"text\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeTextAttributes := _UiNodeTextAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeTextAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeTextAttributes(varUiNodeTextAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"text\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeTextAttributes struct {\n\tvalue *UiNodeTextAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeTextAttributes) Get() *UiNodeTextAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeTextAttributes) Set(val *UiNodeTextAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeTextAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeTextAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeTextAttributes(val *UiNodeTextAttributes) *NullableUiNodeTextAttributes {\n\treturn &NullableUiNodeTextAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeTextAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeTextAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_ui_text.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiText type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiText{}\n\n// UiText struct for UiText\ntype UiText struct {\n\t// The message's context. Useful when customizing messages.\n\tContext map[string]interface{} `json:\"context,omitempty\"`\n\tId      int64                  `json:\"id\"`\n\t// The message text. Written in american english.\n\tText string `json:\"text\"`\n\t// The message type. info Info error Error success Success\n\tType                 string `json:\"type\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiText UiText\n\n// NewUiText instantiates a new UiText object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiText(id int64, text string, type_ string) *UiText {\n\tthis := UiText{}\n\tthis.Id = id\n\tthis.Text = text\n\tthis.Type = type_\n\treturn &this\n}\n\n// NewUiTextWithDefaults instantiates a new UiText object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiTextWithDefaults() *UiText {\n\tthis := UiText{}\n\treturn &this\n}\n\n// GetContext returns the Context field value if set, zero value otherwise.\nfunc (o *UiText) GetContext() map[string]interface{} {\n\tif o == nil || IsNil(o.Context) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Context\n}\n\n// GetContextOk returns a tuple with the Context field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiText) GetContextOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Context) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Context, true\n}\n\n// HasContext returns a boolean if a field has been set.\nfunc (o *UiText) HasContext() bool {\n\tif o != nil && !IsNil(o.Context) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContext gets a reference to the given map[string]interface{} and assigns it to the Context field.\nfunc (o *UiText) SetContext(v map[string]interface{}) {\n\to.Context = v\n}\n\n// GetId returns the Id field value\nfunc (o *UiText) GetId() int64 {\n\tif o == nil {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiText) GetIdOk() (*int64, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiText) SetId(v int64) {\n\to.Id = v\n}\n\n// GetText returns the Text field value\nfunc (o *UiText) GetText() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Text\n}\n\n// GetTextOk returns a tuple with the Text field value\n// and a boolean to check if the value has been set.\nfunc (o *UiText) GetTextOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Text, true\n}\n\n// SetText sets field value\nfunc (o *UiText) SetText(v string) {\n\to.Text = v\n}\n\n// GetType returns the Type field value\nfunc (o *UiText) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *UiText) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *UiText) SetType(v string) {\n\to.Type = v\n}\n\nfunc (o UiText) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiText) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Context) {\n\t\ttoSerialize[\"context\"] = o.Context\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"text\"] = o.Text\n\ttoSerialize[\"type\"] = o.Type\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiText) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"text\",\n\t\t\"type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiText := _UiText{}\n\n\terr = json.Unmarshal(data, &varUiText)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiText(varUiText)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"context\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"text\")\n\t\tdelete(additionalProperties, \"type\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiText struct {\n\tvalue *UiText\n\tisSet bool\n}\n\nfunc (v NullableUiText) Get() *UiText {\n\treturn v.value\n}\n\nfunc (v *NullableUiText) Set(val *UiText) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiText) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiText) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiText(val *UiText) *NullableUiText {\n\treturn &NullableUiText{value: val, isSet: true}\n}\n\nfunc (v NullableUiText) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiText) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_fedcm_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateFedcmFlowBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateFedcmFlowBody{}\n\n// UpdateFedcmFlowBody struct for UpdateFedcmFlowBody\ntype UpdateFedcmFlowBody struct {\n\t// CSRFToken is the anti-CSRF token.\n\tCsrfToken string `json:\"csrf_token\"`\n\t// Nonce is the nonce that was used in the `navigator.credentials.get` call. If specified, it must match the `nonce` claim in the token.\n\tNonce *string `json:\"nonce,omitempty\"`\n\t// Token contains the result of `navigator.credentials.get`.\n\tToken string `json:\"token\"`\n\t// Transient data to pass along to any webhooks.\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateFedcmFlowBody UpdateFedcmFlowBody\n\n// NewUpdateFedcmFlowBody instantiates a new UpdateFedcmFlowBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateFedcmFlowBody(csrfToken string, token string) *UpdateFedcmFlowBody {\n\tthis := UpdateFedcmFlowBody{}\n\tthis.CsrfToken = csrfToken\n\tthis.Token = token\n\treturn &this\n}\n\n// NewUpdateFedcmFlowBodyWithDefaults instantiates a new UpdateFedcmFlowBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateFedcmFlowBodyWithDefaults() *UpdateFedcmFlowBody {\n\tthis := UpdateFedcmFlowBody{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value\nfunc (o *UpdateFedcmFlowBody) GetCsrfToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateFedcmFlowBody) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.CsrfToken, true\n}\n\n// SetCsrfToken sets field value\nfunc (o *UpdateFedcmFlowBody) SetCsrfToken(v string) {\n\to.CsrfToken = v\n}\n\n// GetNonce returns the Nonce field value if set, zero value otherwise.\nfunc (o *UpdateFedcmFlowBody) GetNonce() string {\n\tif o == nil || IsNil(o.Nonce) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Nonce\n}\n\n// GetNonceOk returns a tuple with the Nonce field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateFedcmFlowBody) GetNonceOk() (*string, bool) {\n\tif o == nil || IsNil(o.Nonce) {\n\t\treturn nil, false\n\t}\n\treturn o.Nonce, true\n}\n\n// HasNonce returns a boolean if a field has been set.\nfunc (o *UpdateFedcmFlowBody) HasNonce() bool {\n\tif o != nil && !IsNil(o.Nonce) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetNonce gets a reference to the given string and assigns it to the Nonce field.\nfunc (o *UpdateFedcmFlowBody) SetNonce(v string) {\n\to.Nonce = &v\n}\n\n// GetToken returns the Token field value\nfunc (o *UpdateFedcmFlowBody) GetToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Token\n}\n\n// GetTokenOk returns a tuple with the Token field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateFedcmFlowBody) GetTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Token, true\n}\n\n// SetToken sets field value\nfunc (o *UpdateFedcmFlowBody) SetToken(v string) {\n\to.Token = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateFedcmFlowBody) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateFedcmFlowBody) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateFedcmFlowBody) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateFedcmFlowBody) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateFedcmFlowBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateFedcmFlowBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\tif !IsNil(o.Nonce) {\n\t\ttoSerialize[\"nonce\"] = o.Nonce\n\t}\n\ttoSerialize[\"token\"] = o.Token\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateFedcmFlowBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"csrf_token\",\n\t\t\"token\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateFedcmFlowBody := _UpdateFedcmFlowBody{}\n\n\terr = json.Unmarshal(data, &varUpdateFedcmFlowBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateFedcmFlowBody(varUpdateFedcmFlowBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"nonce\")\n\t\tdelete(additionalProperties, \"token\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateFedcmFlowBody struct {\n\tvalue *UpdateFedcmFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateFedcmFlowBody) Get() *UpdateFedcmFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateFedcmFlowBody) Set(val *UpdateFedcmFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateFedcmFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateFedcmFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateFedcmFlowBody(val *UpdateFedcmFlowBody) *NullableUpdateFedcmFlowBody {\n\treturn &NullableUpdateFedcmFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateFedcmFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateFedcmFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_identity_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateIdentityBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateIdentityBody{}\n\n// UpdateIdentityBody Update Identity Body\ntype UpdateIdentityBody struct {\n\tCredentials *IdentityWithCredentials `json:\"credentials,omitempty\"`\n\t// ExternalID is an optional external ID of the identity. This is used to link the identity to an external system. If set, the external ID must be unique across all identities.\n\tExternalId *string `json:\"external_id,omitempty\"`\n\t// Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/<id>`.\n\tMetadataAdmin interface{} `json:\"metadata_admin,omitempty\"`\n\t// Store metadata about the identity which the identity itself can see when calling for example the session endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\n\tMetadataPublic interface{} `json:\"metadata_public,omitempty\"`\n\t// SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. If set will update the Identity's SchemaID.\n\tSchemaId string `json:\"schema_id\"`\n\t// State is the identity's state. active StateActive inactive StateInactive\n\tState string `json:\"state\"`\n\t// Traits represent an identity's traits. The identity is able to create, modify, and delete traits in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_id`.\n\tTraits               map[string]interface{} `json:\"traits\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateIdentityBody UpdateIdentityBody\n\n// NewUpdateIdentityBody instantiates a new UpdateIdentityBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateIdentityBody(schemaId string, state string, traits map[string]interface{}) *UpdateIdentityBody {\n\tthis := UpdateIdentityBody{}\n\tthis.SchemaId = schemaId\n\tthis.State = state\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateIdentityBodyWithDefaults instantiates a new UpdateIdentityBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateIdentityBodyWithDefaults() *UpdateIdentityBody {\n\tthis := UpdateIdentityBody{}\n\treturn &this\n}\n\n// GetCredentials returns the Credentials field value if set, zero value otherwise.\nfunc (o *UpdateIdentityBody) GetCredentials() IdentityWithCredentials {\n\tif o == nil || IsNil(o.Credentials) {\n\t\tvar ret IdentityWithCredentials\n\t\treturn ret\n\t}\n\treturn *o.Credentials\n}\n\n// GetCredentialsOk returns a tuple with the Credentials field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetCredentialsOk() (*IdentityWithCredentials, bool) {\n\tif o == nil || IsNil(o.Credentials) {\n\t\treturn nil, false\n\t}\n\treturn o.Credentials, true\n}\n\n// HasCredentials returns a boolean if a field has been set.\nfunc (o *UpdateIdentityBody) HasCredentials() bool {\n\tif o != nil && !IsNil(o.Credentials) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCredentials gets a reference to the given IdentityWithCredentials and assigns it to the Credentials field.\nfunc (o *UpdateIdentityBody) SetCredentials(v IdentityWithCredentials) {\n\to.Credentials = &v\n}\n\n// GetExternalId returns the ExternalId field value if set, zero value otherwise.\nfunc (o *UpdateIdentityBody) GetExternalId() string {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExternalId\n}\n\n// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetExternalIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\treturn nil, false\n\t}\n\treturn o.ExternalId, true\n}\n\n// HasExternalId returns a boolean if a field has been set.\nfunc (o *UpdateIdentityBody) HasExternalId() bool {\n\tif o != nil && !IsNil(o.ExternalId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.\nfunc (o *UpdateIdentityBody) SetExternalId(v string) {\n\to.ExternalId = &v\n}\n\n// GetMetadataAdmin returns the MetadataAdmin field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *UpdateIdentityBody) GetMetadataAdmin() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataAdmin\n}\n\n// GetMetadataAdminOk returns a tuple with the MetadataAdmin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *UpdateIdentityBody) GetMetadataAdminOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataAdmin) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataAdmin, true\n}\n\n// HasMetadataAdmin returns a boolean if a field has been set.\nfunc (o *UpdateIdentityBody) HasMetadataAdmin() bool {\n\tif o != nil && !IsNil(o.MetadataAdmin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataAdmin gets a reference to the given interface{} and assigns it to the MetadataAdmin field.\nfunc (o *UpdateIdentityBody) SetMetadataAdmin(v interface{}) {\n\to.MetadataAdmin = v\n}\n\n// GetMetadataPublic returns the MetadataPublic field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *UpdateIdentityBody) GetMetadataPublic() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataPublic\n}\n\n// GetMetadataPublicOk returns a tuple with the MetadataPublic field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *UpdateIdentityBody) GetMetadataPublicOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataPublic) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataPublic, true\n}\n\n// HasMetadataPublic returns a boolean if a field has been set.\nfunc (o *UpdateIdentityBody) HasMetadataPublic() bool {\n\tif o != nil && !IsNil(o.MetadataPublic) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataPublic gets a reference to the given interface{} and assigns it to the MetadataPublic field.\nfunc (o *UpdateIdentityBody) SetMetadataPublic(v interface{}) {\n\to.MetadataPublic = v\n}\n\n// GetSchemaId returns the SchemaId field value\nfunc (o *UpdateIdentityBody) GetSchemaId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SchemaId\n}\n\n// GetSchemaIdOk returns a tuple with the SchemaId field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetSchemaIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SchemaId, true\n}\n\n// SetSchemaId sets field value\nfunc (o *UpdateIdentityBody) SetSchemaId(v string) {\n\to.SchemaId = v\n}\n\n// GetState returns the State field value\nfunc (o *UpdateIdentityBody) GetState() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetStateOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *UpdateIdentityBody) SetState(v string) {\n\to.State = v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateIdentityBody) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateIdentityBody) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\nfunc (o UpdateIdentityBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateIdentityBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Credentials) {\n\t\ttoSerialize[\"credentials\"] = o.Credentials\n\t}\n\tif !IsNil(o.ExternalId) {\n\t\ttoSerialize[\"external_id\"] = o.ExternalId\n\t}\n\tif o.MetadataAdmin != nil {\n\t\ttoSerialize[\"metadata_admin\"] = o.MetadataAdmin\n\t}\n\tif o.MetadataPublic != nil {\n\t\ttoSerialize[\"metadata_public\"] = o.MetadataPublic\n\t}\n\ttoSerialize[\"schema_id\"] = o.SchemaId\n\ttoSerialize[\"state\"] = o.State\n\ttoSerialize[\"traits\"] = o.Traits\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateIdentityBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"schema_id\",\n\t\t\"state\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateIdentityBody := _UpdateIdentityBody{}\n\n\terr = json.Unmarshal(data, &varUpdateIdentityBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateIdentityBody(varUpdateIdentityBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"credentials\")\n\t\tdelete(additionalProperties, \"external_id\")\n\t\tdelete(additionalProperties, \"metadata_admin\")\n\t\tdelete(additionalProperties, \"metadata_public\")\n\t\tdelete(additionalProperties, \"schema_id\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateIdentityBody struct {\n\tvalue *UpdateIdentityBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateIdentityBody) Get() *UpdateIdentityBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateIdentityBody) Set(val *UpdateIdentityBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateIdentityBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateIdentityBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateIdentityBody(val *UpdateIdentityBody) *NullableUpdateIdentityBody {\n\treturn &NullableUpdateIdentityBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateIdentityBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateIdentityBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateLoginFlowBody - struct for UpdateLoginFlowBody\ntype UpdateLoginFlowBody struct {\n\tUpdateLoginFlowWithCodeMethod            *UpdateLoginFlowWithCodeMethod\n\tUpdateLoginFlowWithIdentifierFirstMethod *UpdateLoginFlowWithIdentifierFirstMethod\n\tUpdateLoginFlowWithLookupSecretMethod    *UpdateLoginFlowWithLookupSecretMethod\n\tUpdateLoginFlowWithOidcMethod            *UpdateLoginFlowWithOidcMethod\n\tUpdateLoginFlowWithPasskeyMethod         *UpdateLoginFlowWithPasskeyMethod\n\tUpdateLoginFlowWithPasswordMethod        *UpdateLoginFlowWithPasswordMethod\n\tUpdateLoginFlowWithSamlMethod            *UpdateLoginFlowWithSamlMethod\n\tUpdateLoginFlowWithTotpMethod            *UpdateLoginFlowWithTotpMethod\n\tUpdateLoginFlowWithWebAuthnMethod        *UpdateLoginFlowWithWebAuthnMethod\n}\n\n// UpdateLoginFlowWithCodeMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithCodeMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithCodeMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithCodeMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithCodeMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithIdentifierFirstMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithIdentifierFirstMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithIdentifierFirstMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithIdentifierFirstMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithIdentifierFirstMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithLookupSecretMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithLookupSecretMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithLookupSecretMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithLookupSecretMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithLookupSecretMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithOidcMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithOidcMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithOidcMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithOidcMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithOidcMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithPasskeyMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithPasskeyMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithPasskeyMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithPasskeyMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithPasskeyMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithPasswordMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithPasswordMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithPasswordMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithPasswordMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithPasswordMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithSamlMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithSamlMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithSamlMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithSamlMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithSamlMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithTotpMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithTotpMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithTotpMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithTotpMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithTotpMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithWebAuthnMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithWebAuthnMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithWebAuthnMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithWebAuthnMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithWebAuthnMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateLoginFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'code'\n\tif jsonDict[\"method\"] == \"code\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'identifier_first'\n\tif jsonDict[\"method\"] == \"identifier_first\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithIdentifierFirstMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithIdentifierFirstMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithIdentifierFirstMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithIdentifierFirstMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithIdentifierFirstMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'lookup_secret'\n\tif jsonDict[\"method\"] == \"lookup_secret\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithLookupSecretMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithLookupSecretMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithLookupSecretMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithLookupSecretMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithLookupSecretMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'oidc'\n\tif jsonDict[\"method\"] == \"oidc\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'passkey'\n\tif jsonDict[\"method\"] == \"passkey\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'password'\n\tif jsonDict[\"method\"] == \"password\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'saml'\n\tif jsonDict[\"method\"] == \"saml\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'totp'\n\tif jsonDict[\"method\"] == \"totp\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithTotpMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithTotpMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithTotpMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithTotpMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithTotpMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'webauthn'\n\tif jsonDict[\"method\"] == \"webauthn\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithCodeMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithCodeMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithIdentifierFirstMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithIdentifierFirstMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithIdentifierFirstMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithIdentifierFirstMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithIdentifierFirstMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithIdentifierFirstMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithIdentifierFirstMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithLookupSecretMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithLookupSecretMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithLookupSecretMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithLookupSecretMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithLookupSecretMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithLookupSecretMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithLookupSecretMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithOidcMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithOidcMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithPasskeyMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithPasskeyMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithPasswordMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithPasswordMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithSamlMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithSamlMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithTotpMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithTotpMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithTotpMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithTotpMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithTotpMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithTotpMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithTotpMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithWebAuthnMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithWebAuthnMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateLoginFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateLoginFlowWithCodeMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithCodeMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithIdentifierFirstMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithIdentifierFirstMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithLookupSecretMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithLookupSecretMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithOidcMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithOidcMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithPasskeyMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithPasskeyMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithPasswordMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithPasswordMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithSamlMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithSamlMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithTotpMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithTotpMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithWebAuthnMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithWebAuthnMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateLoginFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateLoginFlowWithCodeMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithIdentifierFirstMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithIdentifierFirstMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithLookupSecretMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithLookupSecretMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithOidcMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithPasskeyMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithPasswordMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithSamlMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithTotpMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithTotpMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithWebAuthnMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateLoginFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateLoginFlowWithCodeMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithIdentifierFirstMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithIdentifierFirstMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithLookupSecretMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithLookupSecretMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithOidcMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithPasskeyMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithPasswordMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithSamlMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithTotpMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithTotpMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithWebAuthnMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateLoginFlowBody struct {\n\tvalue *UpdateLoginFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowBody) Get() *UpdateLoginFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowBody) Set(val *UpdateLoginFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowBody(val *UpdateLoginFlowBody) *NullableUpdateLoginFlowBody {\n\treturn &NullableUpdateLoginFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_with_code_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithCodeMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithCodeMethod{}\n\n// UpdateLoginFlowWithCodeMethod Update Login flow using the code method\ntype UpdateLoginFlowWithCodeMethod struct {\n\t// Address is the address to send the code to, in case that there are multiple addresses. This field is only used in two-factor flows and is ineffective for passwordless flows.\n\tAddress *string `json:\"address,omitempty\"`\n\t// Code is the 6 digits code sent to the user\n\tCode *string `json:\"code,omitempty\"`\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken string `json:\"csrf_token\"`\n\t// Identifier is the code identifier The identifier requires that the user has already completed the registration or settings with code flow.\n\tIdentifier *string `json:\"identifier,omitempty\"`\n\t// Method should be set to \\\"code\\\" when logging in using the code strategy.\n\tMethod string `json:\"method\"`\n\t// Resend is set when the user wants to resend the code\n\tResend *string `json:\"resend,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithCodeMethod UpdateLoginFlowWithCodeMethod\n\n// NewUpdateLoginFlowWithCodeMethod instantiates a new UpdateLoginFlowWithCodeMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithCodeMethod(csrfToken string, method string) *UpdateLoginFlowWithCodeMethod {\n\tthis := UpdateLoginFlowWithCodeMethod{}\n\tthis.CsrfToken = csrfToken\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithCodeMethodWithDefaults instantiates a new UpdateLoginFlowWithCodeMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithCodeMethodWithDefaults() *UpdateLoginFlowWithCodeMethod {\n\tthis := UpdateLoginFlowWithCodeMethod{}\n\treturn &this\n}\n\n// GetAddress returns the Address field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetAddress() string {\n\tif o == nil || IsNil(o.Address) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Address\n}\n\n// GetAddressOk returns a tuple with the Address field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.Address) {\n\t\treturn nil, false\n\t}\n\treturn o.Address, true\n}\n\n// HasAddress returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasAddress() bool {\n\tif o != nil && !IsNil(o.Address) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAddress gets a reference to the given string and assigns it to the Address field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetAddress(v string) {\n\to.Address = &v\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetCode() string {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given string and assigns it to the Code field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetCode(v string) {\n\to.Code = &v\n}\n\n// GetCsrfToken returns the CsrfToken field value\nfunc (o *UpdateLoginFlowWithCodeMethod) GetCsrfToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.CsrfToken, true\n}\n\n// SetCsrfToken sets field value\nfunc (o *UpdateLoginFlowWithCodeMethod) SetCsrfToken(v string) {\n\to.CsrfToken = v\n}\n\n// GetIdentifier returns the Identifier field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetIdentifier() string {\n\tif o == nil || IsNil(o.Identifier) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Identifier\n}\n\n// GetIdentifierOk returns a tuple with the Identifier field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetIdentifierOk() (*string, bool) {\n\tif o == nil || IsNil(o.Identifier) {\n\t\treturn nil, false\n\t}\n\treturn o.Identifier, true\n}\n\n// HasIdentifier returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasIdentifier() bool {\n\tif o != nil && !IsNil(o.Identifier) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentifier gets a reference to the given string and assigns it to the Identifier field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetIdentifier(v string) {\n\to.Identifier = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithCodeMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithCodeMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetResend returns the Resend field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetResend() string {\n\tif o == nil || IsNil(o.Resend) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Resend\n}\n\n// GetResendOk returns a tuple with the Resend field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetResendOk() (*string, bool) {\n\tif o == nil || IsNil(o.Resend) {\n\t\treturn nil, false\n\t}\n\treturn o.Resend, true\n}\n\n// HasResend returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasResend() bool {\n\tif o != nil && !IsNil(o.Resend) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetResend gets a reference to the given string and assigns it to the Resend field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetResend(v string) {\n\to.Resend = &v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithCodeMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Address) {\n\t\ttoSerialize[\"address\"] = o.Address\n\t}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\tif !IsNil(o.Identifier) {\n\t\ttoSerialize[\"identifier\"] = o.Identifier\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Resend) {\n\t\ttoSerialize[\"resend\"] = o.Resend\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithCodeMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"csrf_token\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithCodeMethod := _UpdateLoginFlowWithCodeMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithCodeMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithCodeMethod(varUpdateLoginFlowWithCodeMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"address\")\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"identifier\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"resend\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithCodeMethod struct {\n\tvalue *UpdateLoginFlowWithCodeMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithCodeMethod) Get() *UpdateLoginFlowWithCodeMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithCodeMethod) Set(val *UpdateLoginFlowWithCodeMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithCodeMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithCodeMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithCodeMethod(val *UpdateLoginFlowWithCodeMethod) *NullableUpdateLoginFlowWithCodeMethod {\n\treturn &NullableUpdateLoginFlowWithCodeMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithCodeMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_with_identifier_first_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithIdentifierFirstMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithIdentifierFirstMethod{}\n\n// UpdateLoginFlowWithIdentifierFirstMethod Update Login Flow with Multi-Step Method\ntype UpdateLoginFlowWithIdentifierFirstMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Identifier is the email or username of the user trying to log in.\n\tIdentifier string `json:\"identifier\"`\n\t// Method should be set to \\\"password\\\" when logging in using the identifier and password strategy.\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithIdentifierFirstMethod UpdateLoginFlowWithIdentifierFirstMethod\n\n// NewUpdateLoginFlowWithIdentifierFirstMethod instantiates a new UpdateLoginFlowWithIdentifierFirstMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithIdentifierFirstMethod(identifier string, method string) *UpdateLoginFlowWithIdentifierFirstMethod {\n\tthis := UpdateLoginFlowWithIdentifierFirstMethod{}\n\tthis.Identifier = identifier\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithIdentifierFirstMethodWithDefaults instantiates a new UpdateLoginFlowWithIdentifierFirstMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithIdentifierFirstMethodWithDefaults() *UpdateLoginFlowWithIdentifierFirstMethod {\n\tthis := UpdateLoginFlowWithIdentifierFirstMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdentifier returns the Identifier field value\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetIdentifier() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Identifier\n}\n\n// GetIdentifierOk returns a tuple with the Identifier field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetIdentifierOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identifier, true\n}\n\n// SetIdentifier sets field value\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) SetIdentifier(v string) {\n\to.Identifier = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithIdentifierFirstMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithIdentifierFirstMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"identifier\"] = o.Identifier\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identifier\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithIdentifierFirstMethod := _UpdateLoginFlowWithIdentifierFirstMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithIdentifierFirstMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithIdentifierFirstMethod(varUpdateLoginFlowWithIdentifierFirstMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"identifier\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithIdentifierFirstMethod struct {\n\tvalue *UpdateLoginFlowWithIdentifierFirstMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithIdentifierFirstMethod) Get() *UpdateLoginFlowWithIdentifierFirstMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithIdentifierFirstMethod) Set(val *UpdateLoginFlowWithIdentifierFirstMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithIdentifierFirstMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithIdentifierFirstMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithIdentifierFirstMethod(val *UpdateLoginFlowWithIdentifierFirstMethod) *NullableUpdateLoginFlowWithIdentifierFirstMethod {\n\treturn &NullableUpdateLoginFlowWithIdentifierFirstMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithIdentifierFirstMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithIdentifierFirstMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_with_lookup_secret_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithLookupSecretMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithLookupSecretMethod{}\n\n// UpdateLoginFlowWithLookupSecretMethod Update Login Flow with Lookup Secret Method\ntype UpdateLoginFlowWithLookupSecretMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// The lookup secret.\n\tLookupSecret string `json:\"lookup_secret\"`\n\t// Method should be set to \\\"lookup_secret\\\" when logging in using the lookup_secret strategy.\n\tMethod               string `json:\"method\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithLookupSecretMethod UpdateLoginFlowWithLookupSecretMethod\n\n// NewUpdateLoginFlowWithLookupSecretMethod instantiates a new UpdateLoginFlowWithLookupSecretMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithLookupSecretMethod(lookupSecret string, method string) *UpdateLoginFlowWithLookupSecretMethod {\n\tthis := UpdateLoginFlowWithLookupSecretMethod{}\n\tthis.LookupSecret = lookupSecret\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithLookupSecretMethodWithDefaults instantiates a new UpdateLoginFlowWithLookupSecretMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithLookupSecretMethodWithDefaults() *UpdateLoginFlowWithLookupSecretMethod {\n\tthis := UpdateLoginFlowWithLookupSecretMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetLookupSecret returns the LookupSecret field value\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetLookupSecret() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.LookupSecret\n}\n\n// GetLookupSecretOk returns a tuple with the LookupSecret field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetLookupSecretOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.LookupSecret, true\n}\n\n// SetLookupSecret sets field value\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) SetLookupSecret(v string) {\n\to.LookupSecret = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) SetMethod(v string) {\n\to.Method = v\n}\n\nfunc (o UpdateLoginFlowWithLookupSecretMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithLookupSecretMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"lookup_secret\"] = o.LookupSecret\n\ttoSerialize[\"method\"] = o.Method\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"lookup_secret\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithLookupSecretMethod := _UpdateLoginFlowWithLookupSecretMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithLookupSecretMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithLookupSecretMethod(varUpdateLoginFlowWithLookupSecretMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"lookup_secret\")\n\t\tdelete(additionalProperties, \"method\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithLookupSecretMethod struct {\n\tvalue *UpdateLoginFlowWithLookupSecretMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithLookupSecretMethod) Get() *UpdateLoginFlowWithLookupSecretMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithLookupSecretMethod) Set(val *UpdateLoginFlowWithLookupSecretMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithLookupSecretMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithLookupSecretMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithLookupSecretMethod(val *UpdateLoginFlowWithLookupSecretMethod) *NullableUpdateLoginFlowWithLookupSecretMethod {\n\treturn &NullableUpdateLoginFlowWithLookupSecretMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithLookupSecretMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithLookupSecretMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_with_oidc_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithOidcMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithOidcMethod{}\n\n// UpdateLoginFlowWithOidcMethod Update Login Flow with OpenID Connect Method\ntype UpdateLoginFlowWithOidcMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// IDToken is an optional id token provided by an OIDC provider  If submitted, it is verified using the OIDC provider's public key set and the claims are used to populate the OIDC credentials of the identity. If the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use the `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.  Supported providers are Apple Google\n\tIdToken *string `json:\"id_token,omitempty\"`\n\t// IDTokenNonce is the nonce, used when generating the IDToken. If the provider supports nonce validation, the nonce will be validated against this value and required.\n\tIdTokenNonce *string `json:\"id_token_nonce,omitempty\"`\n\t// Method to use  This field must be set to `oidc` when using the oidc method.\n\tMethod string `json:\"method\"`\n\t// The provider to register with\n\tProvider string `json:\"provider\"`\n\t// The identity traits. This is a placeholder for the registration flow.\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// UpstreamParameters are the parameters that are passed to the upstream identity provider.  These parameters are optional and depend on what the upstream identity provider supports. Supported parameters are: `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session. `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`. `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`. `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\n\tUpstreamParameters   map[string]interface{} `json:\"upstream_parameters,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithOidcMethod UpdateLoginFlowWithOidcMethod\n\n// NewUpdateLoginFlowWithOidcMethod instantiates a new UpdateLoginFlowWithOidcMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithOidcMethod(method string, provider string) *UpdateLoginFlowWithOidcMethod {\n\tthis := UpdateLoginFlowWithOidcMethod{}\n\tthis.Method = method\n\tthis.Provider = provider\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithOidcMethodWithDefaults instantiates a new UpdateLoginFlowWithOidcMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithOidcMethodWithDefaults() *UpdateLoginFlowWithOidcMethod {\n\tthis := UpdateLoginFlowWithOidcMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdToken returns the IdToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetIdToken() string {\n\tif o == nil || IsNil(o.IdToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdToken\n}\n\n// GetIdTokenOk returns a tuple with the IdToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetIdTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdToken) {\n\t\treturn nil, false\n\t}\n\treturn o.IdToken, true\n}\n\n// HasIdToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasIdToken() bool {\n\tif o != nil && !IsNil(o.IdToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdToken gets a reference to the given string and assigns it to the IdToken field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetIdToken(v string) {\n\to.IdToken = &v\n}\n\n// GetIdTokenNonce returns the IdTokenNonce field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetIdTokenNonce() string {\n\tif o == nil || IsNil(o.IdTokenNonce) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdTokenNonce\n}\n\n// GetIdTokenNonceOk returns a tuple with the IdTokenNonce field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetIdTokenNonceOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdTokenNonce) {\n\t\treturn nil, false\n\t}\n\treturn o.IdTokenNonce, true\n}\n\n// HasIdTokenNonce returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasIdTokenNonce() bool {\n\tif o != nil && !IsNil(o.IdTokenNonce) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdTokenNonce gets a reference to the given string and assigns it to the IdTokenNonce field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetIdTokenNonce(v string) {\n\to.IdTokenNonce = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithOidcMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithOidcMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetProvider returns the Provider field value\nfunc (o *UpdateLoginFlowWithOidcMethod) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *UpdateLoginFlowWithOidcMethod) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetUpstreamParameters returns the UpstreamParameters field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetUpstreamParameters() map[string]interface{} {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.UpstreamParameters\n}\n\n// GetUpstreamParametersOk returns a tuple with the UpstreamParameters field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetUpstreamParametersOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.UpstreamParameters, true\n}\n\n// HasUpstreamParameters returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasUpstreamParameters() bool {\n\tif o != nil && !IsNil(o.UpstreamParameters) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpstreamParameters gets a reference to the given map[string]interface{} and assigns it to the UpstreamParameters field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetUpstreamParameters(v map[string]interface{}) {\n\to.UpstreamParameters = v\n}\n\nfunc (o UpdateLoginFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithOidcMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.IdToken) {\n\t\ttoSerialize[\"id_token\"] = o.IdToken\n\t}\n\tif !IsNil(o.IdTokenNonce) {\n\t\ttoSerialize[\"id_token_nonce\"] = o.IdTokenNonce\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"provider\"] = o.Provider\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.UpstreamParameters) {\n\t\ttoSerialize[\"upstream_parameters\"] = o.UpstreamParameters\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithOidcMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"provider\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithOidcMethod := _UpdateLoginFlowWithOidcMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithOidcMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithOidcMethod(varUpdateLoginFlowWithOidcMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"id_token\")\n\t\tdelete(additionalProperties, \"id_token_nonce\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"upstream_parameters\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithOidcMethod struct {\n\tvalue *UpdateLoginFlowWithOidcMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithOidcMethod) Get() *UpdateLoginFlowWithOidcMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithOidcMethod) Set(val *UpdateLoginFlowWithOidcMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithOidcMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithOidcMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithOidcMethod(val *UpdateLoginFlowWithOidcMethod) *NullableUpdateLoginFlowWithOidcMethod {\n\treturn &NullableUpdateLoginFlowWithOidcMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithOidcMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_with_passkey_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithPasskeyMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithPasskeyMethod{}\n\n// UpdateLoginFlowWithPasskeyMethod Update Login Flow with Passkey Method\ntype UpdateLoginFlowWithPasskeyMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method should be set to \\\"passkey\\\" when logging in using the Passkey strategy.\n\tMethod string `json:\"method\"`\n\t// Login a WebAuthn Security Key  This must contain the ID of the WebAuthN connection.\n\tPasskeyLogin         *string `json:\"passkey_login,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithPasskeyMethod UpdateLoginFlowWithPasskeyMethod\n\n// NewUpdateLoginFlowWithPasskeyMethod instantiates a new UpdateLoginFlowWithPasskeyMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithPasskeyMethod(method string) *UpdateLoginFlowWithPasskeyMethod {\n\tthis := UpdateLoginFlowWithPasskeyMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithPasskeyMethodWithDefaults instantiates a new UpdateLoginFlowWithPasskeyMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithPasskeyMethodWithDefaults() *UpdateLoginFlowWithPasskeyMethod {\n\tthis := UpdateLoginFlowWithPasskeyMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithPasskeyMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPasskeyLogin returns the PasskeyLogin field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetPasskeyLogin() string {\n\tif o == nil || IsNil(o.PasskeyLogin) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasskeyLogin\n}\n\n// GetPasskeyLoginOk returns a tuple with the PasskeyLogin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetPasskeyLoginOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasskeyLogin) {\n\t\treturn nil, false\n\t}\n\treturn o.PasskeyLogin, true\n}\n\n// HasPasskeyLogin returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) HasPasskeyLogin() bool {\n\tif o != nil && !IsNil(o.PasskeyLogin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasskeyLogin gets a reference to the given string and assigns it to the PasskeyLogin field.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) SetPasskeyLogin(v string) {\n\to.PasskeyLogin = &v\n}\n\nfunc (o UpdateLoginFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithPasskeyMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.PasskeyLogin) {\n\t\ttoSerialize[\"passkey_login\"] = o.PasskeyLogin\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithPasskeyMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithPasskeyMethod := _UpdateLoginFlowWithPasskeyMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithPasskeyMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithPasskeyMethod(varUpdateLoginFlowWithPasskeyMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"passkey_login\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithPasskeyMethod struct {\n\tvalue *UpdateLoginFlowWithPasskeyMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithPasskeyMethod) Get() *UpdateLoginFlowWithPasskeyMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasskeyMethod) Set(val *UpdateLoginFlowWithPasskeyMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithPasskeyMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasskeyMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithPasskeyMethod(val *UpdateLoginFlowWithPasskeyMethod) *NullableUpdateLoginFlowWithPasskeyMethod {\n\treturn &NullableUpdateLoginFlowWithPasskeyMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasskeyMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_with_password_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithPasswordMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithPasswordMethod{}\n\n// UpdateLoginFlowWithPasswordMethod Update Login Flow with Password Method\ntype UpdateLoginFlowWithPasswordMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Identifier is the email or username of the user trying to log in.\n\tIdentifier string `json:\"identifier\"`\n\t// Method should be set to \\\"password\\\" when logging in using the identifier and password strategy.\n\tMethod string `json:\"method\"`\n\t// The user's password.\n\tPassword string `json:\"password\"`\n\t// Identifier is the email or username of the user trying to log in. This field is deprecated!\n\tPasswordIdentifier *string `json:\"password_identifier,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithPasswordMethod UpdateLoginFlowWithPasswordMethod\n\n// NewUpdateLoginFlowWithPasswordMethod instantiates a new UpdateLoginFlowWithPasswordMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithPasswordMethod(identifier string, method string, password string) *UpdateLoginFlowWithPasswordMethod {\n\tthis := UpdateLoginFlowWithPasswordMethod{}\n\tthis.Identifier = identifier\n\tthis.Method = method\n\tthis.Password = password\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithPasswordMethodWithDefaults instantiates a new UpdateLoginFlowWithPasswordMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithPasswordMethodWithDefaults() *UpdateLoginFlowWithPasswordMethod {\n\tthis := UpdateLoginFlowWithPasswordMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdentifier returns the Identifier field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetIdentifier() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Identifier\n}\n\n// GetIdentifierOk returns a tuple with the Identifier field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetIdentifierOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identifier, true\n}\n\n// SetIdentifier sets field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetIdentifier(v string) {\n\to.Identifier = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPassword returns the Password field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetPassword() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetPasswordOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Password, true\n}\n\n// SetPassword sets field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetPassword(v string) {\n\to.Password = v\n}\n\n// GetPasswordIdentifier returns the PasswordIdentifier field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetPasswordIdentifier() string {\n\tif o == nil || IsNil(o.PasswordIdentifier) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasswordIdentifier\n}\n\n// GetPasswordIdentifierOk returns a tuple with the PasswordIdentifier field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetPasswordIdentifierOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasswordIdentifier) {\n\t\treturn nil, false\n\t}\n\treturn o.PasswordIdentifier, true\n}\n\n// HasPasswordIdentifier returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) HasPasswordIdentifier() bool {\n\tif o != nil && !IsNil(o.PasswordIdentifier) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasswordIdentifier gets a reference to the given string and assigns it to the PasswordIdentifier field.\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetPasswordIdentifier(v string) {\n\to.PasswordIdentifier = &v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithPasswordMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"identifier\"] = o.Identifier\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"password\"] = o.Password\n\tif !IsNil(o.PasswordIdentifier) {\n\t\ttoSerialize[\"password_identifier\"] = o.PasswordIdentifier\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithPasswordMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identifier\",\n\t\t\"method\",\n\t\t\"password\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithPasswordMethod := _UpdateLoginFlowWithPasswordMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithPasswordMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithPasswordMethod(varUpdateLoginFlowWithPasswordMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"identifier\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"password_identifier\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithPasswordMethod struct {\n\tvalue *UpdateLoginFlowWithPasswordMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithPasswordMethod) Get() *UpdateLoginFlowWithPasswordMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasswordMethod) Set(val *UpdateLoginFlowWithPasswordMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithPasswordMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasswordMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithPasswordMethod(val *UpdateLoginFlowWithPasswordMethod) *NullableUpdateLoginFlowWithPasswordMethod {\n\treturn &NullableUpdateLoginFlowWithPasswordMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasswordMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_with_saml_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithSamlMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithSamlMethod{}\n\n// UpdateLoginFlowWithSamlMethod Update login flow using SAML\ntype UpdateLoginFlowWithSamlMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method to use  This field must be set to `saml` when using the saml method.\n\tMethod string `json:\"method\"`\n\t// The provider to register with\n\tProvider string `json:\"provider\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithSamlMethod UpdateLoginFlowWithSamlMethod\n\n// NewUpdateLoginFlowWithSamlMethod instantiates a new UpdateLoginFlowWithSamlMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithSamlMethod(method string, provider string) *UpdateLoginFlowWithSamlMethod {\n\tthis := UpdateLoginFlowWithSamlMethod{}\n\tthis.Method = method\n\tthis.Provider = provider\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithSamlMethodWithDefaults instantiates a new UpdateLoginFlowWithSamlMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithSamlMethodWithDefaults() *UpdateLoginFlowWithSamlMethod {\n\tthis := UpdateLoginFlowWithSamlMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithSamlMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithSamlMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithSamlMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetProvider returns the Provider field value\nfunc (o *UpdateLoginFlowWithSamlMethod) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *UpdateLoginFlowWithSamlMethod) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithSamlMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithSamlMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"provider\"] = o.Provider\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithSamlMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"provider\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithSamlMethod := _UpdateLoginFlowWithSamlMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithSamlMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithSamlMethod(varUpdateLoginFlowWithSamlMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithSamlMethod struct {\n\tvalue *UpdateLoginFlowWithSamlMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithSamlMethod) Get() *UpdateLoginFlowWithSamlMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithSamlMethod) Set(val *UpdateLoginFlowWithSamlMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithSamlMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithSamlMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithSamlMethod(val *UpdateLoginFlowWithSamlMethod) *NullableUpdateLoginFlowWithSamlMethod {\n\treturn &NullableUpdateLoginFlowWithSamlMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithSamlMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_with_totp_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithTotpMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithTotpMethod{}\n\n// UpdateLoginFlowWithTotpMethod Update Login Flow with TOTP Method\ntype UpdateLoginFlowWithTotpMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method should be set to \\\"totp\\\" when logging in using the TOTP strategy.\n\tMethod string `json:\"method\"`\n\t// The TOTP code.\n\tTotpCode string `json:\"totp_code\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithTotpMethod UpdateLoginFlowWithTotpMethod\n\n// NewUpdateLoginFlowWithTotpMethod instantiates a new UpdateLoginFlowWithTotpMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithTotpMethod(method string, totpCode string) *UpdateLoginFlowWithTotpMethod {\n\tthis := UpdateLoginFlowWithTotpMethod{}\n\tthis.Method = method\n\tthis.TotpCode = totpCode\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithTotpMethodWithDefaults instantiates a new UpdateLoginFlowWithTotpMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithTotpMethodWithDefaults() *UpdateLoginFlowWithTotpMethod {\n\tthis := UpdateLoginFlowWithTotpMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithTotpMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithTotpMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithTotpMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTotpCode returns the TotpCode field value\nfunc (o *UpdateLoginFlowWithTotpMethod) GetTotpCode() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.TotpCode\n}\n\n// GetTotpCodeOk returns a tuple with the TotpCode field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetTotpCodeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.TotpCode, true\n}\n\n// SetTotpCode sets field value\nfunc (o *UpdateLoginFlowWithTotpMethod) SetTotpCode(v string) {\n\to.TotpCode = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithTotpMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithTotpMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithTotpMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"totp_code\"] = o.TotpCode\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithTotpMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"totp_code\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithTotpMethod := _UpdateLoginFlowWithTotpMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithTotpMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithTotpMethod(varUpdateLoginFlowWithTotpMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"totp_code\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithTotpMethod struct {\n\tvalue *UpdateLoginFlowWithTotpMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithTotpMethod) Get() *UpdateLoginFlowWithTotpMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithTotpMethod) Set(val *UpdateLoginFlowWithTotpMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithTotpMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithTotpMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithTotpMethod(val *UpdateLoginFlowWithTotpMethod) *NullableUpdateLoginFlowWithTotpMethod {\n\treturn &NullableUpdateLoginFlowWithTotpMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithTotpMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithTotpMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_login_flow_with_web_authn_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithWebAuthnMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithWebAuthnMethod{}\n\n// UpdateLoginFlowWithWebAuthnMethod Update Login Flow with WebAuthn Method\ntype UpdateLoginFlowWithWebAuthnMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Identifier is the email or username of the user trying to log in.\n\tIdentifier string `json:\"identifier\"`\n\t// Method should be set to \\\"webAuthn\\\" when logging in using the WebAuthn strategy.\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Login a WebAuthn Security Key  This must contain the ID of the WebAuthN connection.\n\tWebauthnLogin        *string `json:\"webauthn_login,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithWebAuthnMethod UpdateLoginFlowWithWebAuthnMethod\n\n// NewUpdateLoginFlowWithWebAuthnMethod instantiates a new UpdateLoginFlowWithWebAuthnMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithWebAuthnMethod(identifier string, method string) *UpdateLoginFlowWithWebAuthnMethod {\n\tthis := UpdateLoginFlowWithWebAuthnMethod{}\n\tthis.Identifier = identifier\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithWebAuthnMethodWithDefaults instantiates a new UpdateLoginFlowWithWebAuthnMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithWebAuthnMethodWithDefaults() *UpdateLoginFlowWithWebAuthnMethod {\n\tthis := UpdateLoginFlowWithWebAuthnMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdentifier returns the Identifier field value\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetIdentifier() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Identifier\n}\n\n// GetIdentifierOk returns a tuple with the Identifier field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetIdentifierOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identifier, true\n}\n\n// SetIdentifier sets field value\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetIdentifier(v string) {\n\to.Identifier = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetWebauthnLogin returns the WebauthnLogin field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetWebauthnLogin() string {\n\tif o == nil || IsNil(o.WebauthnLogin) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnLogin\n}\n\n// GetWebauthnLoginOk returns a tuple with the WebauthnLogin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetWebauthnLoginOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnLogin) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnLogin, true\n}\n\n// HasWebauthnLogin returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) HasWebauthnLogin() bool {\n\tif o != nil && !IsNil(o.WebauthnLogin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnLogin gets a reference to the given string and assigns it to the WebauthnLogin field.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetWebauthnLogin(v string) {\n\to.WebauthnLogin = &v\n}\n\nfunc (o UpdateLoginFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithWebAuthnMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"identifier\"] = o.Identifier\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.WebauthnLogin) {\n\t\ttoSerialize[\"webauthn_login\"] = o.WebauthnLogin\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identifier\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithWebAuthnMethod := _UpdateLoginFlowWithWebAuthnMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithWebAuthnMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithWebAuthnMethod(varUpdateLoginFlowWithWebAuthnMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"identifier\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"webauthn_login\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithWebAuthnMethod struct {\n\tvalue *UpdateLoginFlowWithWebAuthnMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithWebAuthnMethod) Get() *UpdateLoginFlowWithWebAuthnMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithWebAuthnMethod) Set(val *UpdateLoginFlowWithWebAuthnMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithWebAuthnMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithWebAuthnMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithWebAuthnMethod(val *UpdateLoginFlowWithWebAuthnMethod) *NullableUpdateLoginFlowWithWebAuthnMethod {\n\treturn &NullableUpdateLoginFlowWithWebAuthnMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithWebAuthnMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_recovery_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateRecoveryFlowBody - Update Recovery Flow Request Body\ntype UpdateRecoveryFlowBody struct {\n\tUpdateRecoveryFlowWithCodeMethod *UpdateRecoveryFlowWithCodeMethod\n\tUpdateRecoveryFlowWithLinkMethod *UpdateRecoveryFlowWithLinkMethod\n}\n\n// UpdateRecoveryFlowWithCodeMethodAsUpdateRecoveryFlowBody is a convenience function that returns UpdateRecoveryFlowWithCodeMethod wrapped in UpdateRecoveryFlowBody\nfunc UpdateRecoveryFlowWithCodeMethodAsUpdateRecoveryFlowBody(v *UpdateRecoveryFlowWithCodeMethod) UpdateRecoveryFlowBody {\n\treturn UpdateRecoveryFlowBody{\n\t\tUpdateRecoveryFlowWithCodeMethod: v,\n\t}\n}\n\n// UpdateRecoveryFlowWithLinkMethodAsUpdateRecoveryFlowBody is a convenience function that returns UpdateRecoveryFlowWithLinkMethod wrapped in UpdateRecoveryFlowBody\nfunc UpdateRecoveryFlowWithLinkMethodAsUpdateRecoveryFlowBody(v *UpdateRecoveryFlowWithLinkMethod) UpdateRecoveryFlowBody {\n\treturn UpdateRecoveryFlowBody{\n\t\tUpdateRecoveryFlowWithLinkMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateRecoveryFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'code'\n\tif jsonDict[\"method\"] == \"code\" {\n\t\t// try to unmarshal JSON data into UpdateRecoveryFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRecoveryFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRecoveryFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRecoveryFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRecoveryFlowBody as UpdateRecoveryFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'link'\n\tif jsonDict[\"method\"] == \"link\" {\n\t\t// try to unmarshal JSON data into UpdateRecoveryFlowWithLinkMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRecoveryFlowWithLinkMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRecoveryFlowWithLinkMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRecoveryFlowWithLinkMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRecoveryFlowBody as UpdateRecoveryFlowWithLinkMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRecoveryFlowWithCodeMethod'\n\tif jsonDict[\"method\"] == \"updateRecoveryFlowWithCodeMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRecoveryFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRecoveryFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRecoveryFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRecoveryFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRecoveryFlowBody as UpdateRecoveryFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRecoveryFlowWithLinkMethod'\n\tif jsonDict[\"method\"] == \"updateRecoveryFlowWithLinkMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRecoveryFlowWithLinkMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRecoveryFlowWithLinkMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRecoveryFlowWithLinkMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRecoveryFlowWithLinkMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRecoveryFlowBody as UpdateRecoveryFlowWithLinkMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateRecoveryFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateRecoveryFlowWithCodeMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRecoveryFlowWithCodeMethod)\n\t}\n\n\tif src.UpdateRecoveryFlowWithLinkMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRecoveryFlowWithLinkMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateRecoveryFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateRecoveryFlowWithCodeMethod != nil {\n\t\treturn obj.UpdateRecoveryFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateRecoveryFlowWithLinkMethod != nil {\n\t\treturn obj.UpdateRecoveryFlowWithLinkMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateRecoveryFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateRecoveryFlowWithCodeMethod != nil {\n\t\treturn *obj.UpdateRecoveryFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateRecoveryFlowWithLinkMethod != nil {\n\t\treturn *obj.UpdateRecoveryFlowWithLinkMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateRecoveryFlowBody struct {\n\tvalue *UpdateRecoveryFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateRecoveryFlowBody) Get() *UpdateRecoveryFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRecoveryFlowBody) Set(val *UpdateRecoveryFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRecoveryFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRecoveryFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRecoveryFlowBody(val *UpdateRecoveryFlowBody) *NullableUpdateRecoveryFlowBody {\n\treturn &NullableUpdateRecoveryFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRecoveryFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRecoveryFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_recovery_flow_with_code_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRecoveryFlowWithCodeMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRecoveryFlowWithCodeMethod{}\n\n// UpdateRecoveryFlowWithCodeMethod Update Recovery Flow with Code Method\ntype UpdateRecoveryFlowWithCodeMethod struct {\n\t// Code from the recovery email  If you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\n\tCode *string `json:\"code,omitempty\"`\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// The email address of the account to recover  If the email belongs to a valid account, a recovery email will be sent.  If you want to notify the email address if the account does not exist, see the [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/account-recovery-password-reset#attempted-recovery-notifications)  If a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.  format: email\n\tEmail *string `json:\"email,omitempty\"`\n\t// Method is the method that should be used for this recovery flow  Allowed values are `link` and `code`. link RecoveryStrategyLink code RecoveryStrategyCode\n\tMethod string `json:\"method\"`\n\t// A recovery address that is registered for the user. It can be an email, a phone number (to receive the code via SMS), etc. Used in RecoveryV2.\n\tRecoveryAddress *string `json:\"recovery_address,omitempty\"`\n\t// If there are multiple recovery addresses registered for the user, and the initially provided address is different from the address chosen when the choice (of masked addresses) is presented, then we need to make sure that the user actually knows the full address to avoid information exfiltration, so we ask for the full address. Used in RecoveryV2.\n\tRecoveryConfirmAddress *string `json:\"recovery_confirm_address,omitempty\"`\n\t// If there are multiple addresses registered for the user, a choice is presented and this field stores the result of this choice. Addresses are 'masked' (never sent in full to the client and shown partially in the UI) since at this point in the recovery flow, the user has not yet proven that it knows the full address and we want to avoid information exfiltration. So for all intents and purposes, the value of this field should be treated as an opaque identifier. Used in RecoveryV2.\n\tRecoverySelectAddress *string `json:\"recovery_select_address,omitempty\"`\n\t// Set to \\\"previous\\\" to go back in the flow, meaningfully. Used in RecoveryV2.\n\tScreen *string `json:\"screen,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRecoveryFlowWithCodeMethod UpdateRecoveryFlowWithCodeMethod\n\n// NewUpdateRecoveryFlowWithCodeMethod instantiates a new UpdateRecoveryFlowWithCodeMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRecoveryFlowWithCodeMethod(method string) *UpdateRecoveryFlowWithCodeMethod {\n\tthis := UpdateRecoveryFlowWithCodeMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateRecoveryFlowWithCodeMethodWithDefaults instantiates a new UpdateRecoveryFlowWithCodeMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRecoveryFlowWithCodeMethodWithDefaults() *UpdateRecoveryFlowWithCodeMethod {\n\tthis := UpdateRecoveryFlowWithCodeMethod{}\n\treturn &this\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetCode() string {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given string and assigns it to the Code field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetCode(v string) {\n\to.Code = &v\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetEmail returns the Email field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetEmail() string {\n\tif o == nil || IsNil(o.Email) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Email\n}\n\n// GetEmailOk returns a tuple with the Email field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetEmailOk() (*string, bool) {\n\tif o == nil || IsNil(o.Email) {\n\t\treturn nil, false\n\t}\n\treturn o.Email, true\n}\n\n// HasEmail returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasEmail() bool {\n\tif o != nil && !IsNil(o.Email) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetEmail gets a reference to the given string and assigns it to the Email field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetEmail(v string) {\n\to.Email = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetRecoveryAddress returns the RecoveryAddress field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoveryAddress() string {\n\tif o == nil || IsNil(o.RecoveryAddress) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RecoveryAddress\n}\n\n// GetRecoveryAddressOk returns a tuple with the RecoveryAddress field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoveryAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.RecoveryAddress) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoveryAddress, true\n}\n\n// HasRecoveryAddress returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasRecoveryAddress() bool {\n\tif o != nil && !IsNil(o.RecoveryAddress) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoveryAddress gets a reference to the given string and assigns it to the RecoveryAddress field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetRecoveryAddress(v string) {\n\to.RecoveryAddress = &v\n}\n\n// GetRecoveryConfirmAddress returns the RecoveryConfirmAddress field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoveryConfirmAddress() string {\n\tif o == nil || IsNil(o.RecoveryConfirmAddress) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RecoveryConfirmAddress\n}\n\n// GetRecoveryConfirmAddressOk returns a tuple with the RecoveryConfirmAddress field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoveryConfirmAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.RecoveryConfirmAddress) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoveryConfirmAddress, true\n}\n\n// HasRecoveryConfirmAddress returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasRecoveryConfirmAddress() bool {\n\tif o != nil && !IsNil(o.RecoveryConfirmAddress) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoveryConfirmAddress gets a reference to the given string and assigns it to the RecoveryConfirmAddress field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetRecoveryConfirmAddress(v string) {\n\to.RecoveryConfirmAddress = &v\n}\n\n// GetRecoverySelectAddress returns the RecoverySelectAddress field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoverySelectAddress() string {\n\tif o == nil || IsNil(o.RecoverySelectAddress) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RecoverySelectAddress\n}\n\n// GetRecoverySelectAddressOk returns a tuple with the RecoverySelectAddress field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoverySelectAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.RecoverySelectAddress) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoverySelectAddress, true\n}\n\n// HasRecoverySelectAddress returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasRecoverySelectAddress() bool {\n\tif o != nil && !IsNil(o.RecoverySelectAddress) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoverySelectAddress gets a reference to the given string and assigns it to the RecoverySelectAddress field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetRecoverySelectAddress(v string) {\n\to.RecoverySelectAddress = &v\n}\n\n// GetScreen returns the Screen field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetScreen() string {\n\tif o == nil || IsNil(o.Screen) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Screen\n}\n\n// GetScreenOk returns a tuple with the Screen field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetScreenOk() (*string, bool) {\n\tif o == nil || IsNil(o.Screen) {\n\t\treturn nil, false\n\t}\n\treturn o.Screen, true\n}\n\n// HasScreen returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasScreen() bool {\n\tif o != nil && !IsNil(o.Screen) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetScreen gets a reference to the given string and assigns it to the Screen field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetScreen(v string) {\n\to.Screen = &v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRecoveryFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRecoveryFlowWithCodeMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.Email) {\n\t\ttoSerialize[\"email\"] = o.Email\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.RecoveryAddress) {\n\t\ttoSerialize[\"recovery_address\"] = o.RecoveryAddress\n\t}\n\tif !IsNil(o.RecoveryConfirmAddress) {\n\t\ttoSerialize[\"recovery_confirm_address\"] = o.RecoveryConfirmAddress\n\t}\n\tif !IsNil(o.RecoverySelectAddress) {\n\t\ttoSerialize[\"recovery_select_address\"] = o.RecoverySelectAddress\n\t}\n\tif !IsNil(o.Screen) {\n\t\ttoSerialize[\"screen\"] = o.Screen\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRecoveryFlowWithCodeMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRecoveryFlowWithCodeMethod := _UpdateRecoveryFlowWithCodeMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRecoveryFlowWithCodeMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRecoveryFlowWithCodeMethod(varUpdateRecoveryFlowWithCodeMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"email\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"recovery_address\")\n\t\tdelete(additionalProperties, \"recovery_confirm_address\")\n\t\tdelete(additionalProperties, \"recovery_select_address\")\n\t\tdelete(additionalProperties, \"screen\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRecoveryFlowWithCodeMethod struct {\n\tvalue *UpdateRecoveryFlowWithCodeMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRecoveryFlowWithCodeMethod) Get() *UpdateRecoveryFlowWithCodeMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithCodeMethod) Set(val *UpdateRecoveryFlowWithCodeMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRecoveryFlowWithCodeMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithCodeMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRecoveryFlowWithCodeMethod(val *UpdateRecoveryFlowWithCodeMethod) *NullableUpdateRecoveryFlowWithCodeMethod {\n\treturn &NullableUpdateRecoveryFlowWithCodeMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRecoveryFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithCodeMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_recovery_flow_with_link_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRecoveryFlowWithLinkMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRecoveryFlowWithLinkMethod{}\n\n// UpdateRecoveryFlowWithLinkMethod Update Recovery Flow with Link Method\ntype UpdateRecoveryFlowWithLinkMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Email to Recover  Needs to be set when initiating the flow. If the email is a registered recovery email, a recovery link will be sent. If the email is not known, an email with details on what happened will be sent instead.  format: email\n\tEmail string `json:\"email\"`\n\t// Method is the method that should be used for this recovery flow  Allowed values are `link` and `code` link RecoveryStrategyLink code RecoveryStrategyCode\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRecoveryFlowWithLinkMethod UpdateRecoveryFlowWithLinkMethod\n\n// NewUpdateRecoveryFlowWithLinkMethod instantiates a new UpdateRecoveryFlowWithLinkMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRecoveryFlowWithLinkMethod(email string, method string) *UpdateRecoveryFlowWithLinkMethod {\n\tthis := UpdateRecoveryFlowWithLinkMethod{}\n\tthis.Email = email\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateRecoveryFlowWithLinkMethodWithDefaults instantiates a new UpdateRecoveryFlowWithLinkMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRecoveryFlowWithLinkMethodWithDefaults() *UpdateRecoveryFlowWithLinkMethod {\n\tthis := UpdateRecoveryFlowWithLinkMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetEmail returns the Email field value\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetEmail() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Email\n}\n\n// GetEmailOk returns a tuple with the Email field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetEmailOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Email, true\n}\n\n// SetEmail sets field value\nfunc (o *UpdateRecoveryFlowWithLinkMethod) SetEmail(v string) {\n\to.Email = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRecoveryFlowWithLinkMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRecoveryFlowWithLinkMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRecoveryFlowWithLinkMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"email\"] = o.Email\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRecoveryFlowWithLinkMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"email\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRecoveryFlowWithLinkMethod := _UpdateRecoveryFlowWithLinkMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRecoveryFlowWithLinkMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRecoveryFlowWithLinkMethod(varUpdateRecoveryFlowWithLinkMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"email\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRecoveryFlowWithLinkMethod struct {\n\tvalue *UpdateRecoveryFlowWithLinkMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRecoveryFlowWithLinkMethod) Get() *UpdateRecoveryFlowWithLinkMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithLinkMethod) Set(val *UpdateRecoveryFlowWithLinkMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRecoveryFlowWithLinkMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithLinkMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRecoveryFlowWithLinkMethod(val *UpdateRecoveryFlowWithLinkMethod) *NullableUpdateRecoveryFlowWithLinkMethod {\n\treturn &NullableUpdateRecoveryFlowWithLinkMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRecoveryFlowWithLinkMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithLinkMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_registration_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateRegistrationFlowBody - Update Registration Request Body\ntype UpdateRegistrationFlowBody struct {\n\tUpdateRegistrationFlowWithCodeMethod     *UpdateRegistrationFlowWithCodeMethod\n\tUpdateRegistrationFlowWithOidcMethod     *UpdateRegistrationFlowWithOidcMethod\n\tUpdateRegistrationFlowWithPasskeyMethod  *UpdateRegistrationFlowWithPasskeyMethod\n\tUpdateRegistrationFlowWithPasswordMethod *UpdateRegistrationFlowWithPasswordMethod\n\tUpdateRegistrationFlowWithProfileMethod  *UpdateRegistrationFlowWithProfileMethod\n\tUpdateRegistrationFlowWithSamlMethod     *UpdateRegistrationFlowWithSamlMethod\n\tUpdateRegistrationFlowWithWebAuthnMethod *UpdateRegistrationFlowWithWebAuthnMethod\n}\n\n// UpdateRegistrationFlowWithCodeMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithCodeMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithCodeMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithCodeMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithCodeMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithOidcMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithOidcMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithOidcMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithOidcMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithOidcMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithPasskeyMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithPasskeyMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithPasskeyMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithPasskeyMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithPasskeyMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithPasswordMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithPasswordMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithPasswordMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithPasswordMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithPasswordMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithProfileMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithProfileMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithProfileMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithProfileMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithProfileMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithSamlMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithSamlMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithSamlMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithSamlMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithSamlMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithWebAuthnMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithWebAuthnMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithWebAuthnMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithWebAuthnMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithWebAuthnMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateRegistrationFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'code'\n\tif jsonDict[\"method\"] == \"code\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'oidc'\n\tif jsonDict[\"method\"] == \"oidc\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'passkey'\n\tif jsonDict[\"method\"] == \"passkey\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'password'\n\tif jsonDict[\"method\"] == \"password\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'profile'\n\tif jsonDict[\"method\"] == \"profile\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithProfileMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithProfileMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithProfileMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithProfileMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithProfileMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'saml'\n\tif jsonDict[\"method\"] == \"saml\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'webauthn'\n\tif jsonDict[\"method\"] == \"webauthn\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithCodeMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithCodeMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithOidcMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithOidcMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithPasskeyMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithPasskeyMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithPasswordMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithPasswordMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithProfileMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithProfileMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithProfileMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithProfileMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithProfileMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithProfileMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithProfileMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithSamlMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithSamlMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithWebAuthnMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithWebAuthnMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateRegistrationFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateRegistrationFlowWithCodeMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithCodeMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithOidcMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithOidcMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithPasskeyMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithPasskeyMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithPasswordMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithPasswordMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithProfileMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithProfileMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithSamlMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithSamlMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithWebAuthnMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithWebAuthnMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateRegistrationFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateRegistrationFlowWithCodeMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithOidcMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithPasskeyMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithPasswordMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithProfileMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithProfileMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithSamlMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithWebAuthnMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateRegistrationFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateRegistrationFlowWithCodeMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithOidcMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithPasskeyMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithPasswordMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithProfileMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithProfileMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithSamlMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithWebAuthnMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateRegistrationFlowBody struct {\n\tvalue *UpdateRegistrationFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowBody) Get() *UpdateRegistrationFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowBody) Set(val *UpdateRegistrationFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowBody(val *UpdateRegistrationFlowBody) *NullableUpdateRegistrationFlowBody {\n\treturn &NullableUpdateRegistrationFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_registration_flow_with_code_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithCodeMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithCodeMethod{}\n\n// UpdateRegistrationFlowWithCodeMethod Update Registration Flow with Code Method\ntype UpdateRegistrationFlowWithCodeMethod struct {\n\t// The OTP Code sent to the user\n\tCode *string `json:\"code,omitempty\"`\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method to use  This field must be set to `code` when using the code method.\n\tMethod string `json:\"method\"`\n\t// Resend restarts the flow with a new code\n\tResend *string `json:\"resend,omitempty\"`\n\t// The identity's traits\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithCodeMethod UpdateRegistrationFlowWithCodeMethod\n\n// NewUpdateRegistrationFlowWithCodeMethod instantiates a new UpdateRegistrationFlowWithCodeMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithCodeMethod(method string, traits map[string]interface{}) *UpdateRegistrationFlowWithCodeMethod {\n\tthis := UpdateRegistrationFlowWithCodeMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithCodeMethodWithDefaults instantiates a new UpdateRegistrationFlowWithCodeMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithCodeMethodWithDefaults() *UpdateRegistrationFlowWithCodeMethod {\n\tthis := UpdateRegistrationFlowWithCodeMethod{}\n\treturn &this\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetCode() string {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given string and assigns it to the Code field.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetCode(v string) {\n\to.Code = &v\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetResend returns the Resend field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetResend() string {\n\tif o == nil || IsNil(o.Resend) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Resend\n}\n\n// GetResendOk returns a tuple with the Resend field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetResendOk() (*string, bool) {\n\tif o == nil || IsNil(o.Resend) {\n\t\treturn nil, false\n\t}\n\treturn o.Resend, true\n}\n\n// HasResend returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) HasResend() bool {\n\tif o != nil && !IsNil(o.Resend) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetResend gets a reference to the given string and assigns it to the Resend field.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetResend(v string) {\n\to.Resend = &v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithCodeMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Resend) {\n\t\ttoSerialize[\"resend\"] = o.Resend\n\t}\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithCodeMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithCodeMethod := _UpdateRegistrationFlowWithCodeMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithCodeMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithCodeMethod(varUpdateRegistrationFlowWithCodeMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"resend\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithCodeMethod struct {\n\tvalue *UpdateRegistrationFlowWithCodeMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithCodeMethod) Get() *UpdateRegistrationFlowWithCodeMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithCodeMethod) Set(val *UpdateRegistrationFlowWithCodeMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithCodeMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithCodeMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithCodeMethod(val *UpdateRegistrationFlowWithCodeMethod) *NullableUpdateRegistrationFlowWithCodeMethod {\n\treturn &NullableUpdateRegistrationFlowWithCodeMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithCodeMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_registration_flow_with_oidc_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithOidcMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithOidcMethod{}\n\n// UpdateRegistrationFlowWithOidcMethod Update Registration Flow with OpenID Connect Method\ntype UpdateRegistrationFlowWithOidcMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// IDToken is an optional id token provided by an OIDC provider  If submitted, it is verified using the OIDC provider's public key set and the claims are used to populate the OIDC credentials of the identity. If the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use the `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.  Supported providers are Apple Google\n\tIdToken *string `json:\"id_token,omitempty\"`\n\t// IDTokenNonce is the nonce, used when generating the IDToken. If the provider supports nonce validation, the nonce will be validated against this value and is required.\n\tIdTokenNonce *string `json:\"id_token_nonce,omitempty\"`\n\t// Method to use  This field must be set to `oidc` when using the oidc method.\n\tMethod string `json:\"method\"`\n\t// The provider to register with\n\tProvider string `json:\"provider\"`\n\t// The identity traits\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// UpstreamParameters are the parameters that are passed to the upstream identity provider.  These parameters are optional and depend on what the upstream identity provider supports. Supported parameters are: `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session. `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`. `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`. `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\n\tUpstreamParameters   map[string]interface{} `json:\"upstream_parameters,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithOidcMethod UpdateRegistrationFlowWithOidcMethod\n\n// NewUpdateRegistrationFlowWithOidcMethod instantiates a new UpdateRegistrationFlowWithOidcMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithOidcMethod(method string, provider string) *UpdateRegistrationFlowWithOidcMethod {\n\tthis := UpdateRegistrationFlowWithOidcMethod{}\n\tthis.Method = method\n\tthis.Provider = provider\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithOidcMethodWithDefaults instantiates a new UpdateRegistrationFlowWithOidcMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithOidcMethodWithDefaults() *UpdateRegistrationFlowWithOidcMethod {\n\tthis := UpdateRegistrationFlowWithOidcMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdToken returns the IdToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetIdToken() string {\n\tif o == nil || IsNil(o.IdToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdToken\n}\n\n// GetIdTokenOk returns a tuple with the IdToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetIdTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdToken) {\n\t\treturn nil, false\n\t}\n\treturn o.IdToken, true\n}\n\n// HasIdToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasIdToken() bool {\n\tif o != nil && !IsNil(o.IdToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdToken gets a reference to the given string and assigns it to the IdToken field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetIdToken(v string) {\n\to.IdToken = &v\n}\n\n// GetIdTokenNonce returns the IdTokenNonce field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetIdTokenNonce() string {\n\tif o == nil || IsNil(o.IdTokenNonce) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdTokenNonce\n}\n\n// GetIdTokenNonceOk returns a tuple with the IdTokenNonce field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetIdTokenNonceOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdTokenNonce) {\n\t\treturn nil, false\n\t}\n\treturn o.IdTokenNonce, true\n}\n\n// HasIdTokenNonce returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasIdTokenNonce() bool {\n\tif o != nil && !IsNil(o.IdTokenNonce) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdTokenNonce gets a reference to the given string and assigns it to the IdTokenNonce field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetIdTokenNonce(v string) {\n\to.IdTokenNonce = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetProvider returns the Provider field value\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetUpstreamParameters returns the UpstreamParameters field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetUpstreamParameters() map[string]interface{} {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.UpstreamParameters\n}\n\n// GetUpstreamParametersOk returns a tuple with the UpstreamParameters field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetUpstreamParametersOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.UpstreamParameters, true\n}\n\n// HasUpstreamParameters returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasUpstreamParameters() bool {\n\tif o != nil && !IsNil(o.UpstreamParameters) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpstreamParameters gets a reference to the given map[string]interface{} and assigns it to the UpstreamParameters field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetUpstreamParameters(v map[string]interface{}) {\n\to.UpstreamParameters = v\n}\n\nfunc (o UpdateRegistrationFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithOidcMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.IdToken) {\n\t\ttoSerialize[\"id_token\"] = o.IdToken\n\t}\n\tif !IsNil(o.IdTokenNonce) {\n\t\ttoSerialize[\"id_token_nonce\"] = o.IdTokenNonce\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"provider\"] = o.Provider\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.UpstreamParameters) {\n\t\ttoSerialize[\"upstream_parameters\"] = o.UpstreamParameters\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithOidcMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"provider\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithOidcMethod := _UpdateRegistrationFlowWithOidcMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithOidcMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithOidcMethod(varUpdateRegistrationFlowWithOidcMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"id_token\")\n\t\tdelete(additionalProperties, \"id_token_nonce\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"upstream_parameters\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithOidcMethod struct {\n\tvalue *UpdateRegistrationFlowWithOidcMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithOidcMethod) Get() *UpdateRegistrationFlowWithOidcMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithOidcMethod) Set(val *UpdateRegistrationFlowWithOidcMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithOidcMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithOidcMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithOidcMethod(val *UpdateRegistrationFlowWithOidcMethod) *NullableUpdateRegistrationFlowWithOidcMethod {\n\treturn &NullableUpdateRegistrationFlowWithOidcMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithOidcMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_registration_flow_with_passkey_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithPasskeyMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithPasskeyMethod{}\n\n// UpdateRegistrationFlowWithPasskeyMethod Update Registration Flow with Passkey Method\ntype UpdateRegistrationFlowWithPasskeyMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"passkey\\\" when trying to add, update, or remove a Passkey.\n\tMethod string `json:\"method\"`\n\t// Register a WebAuthn Security Key  It is expected that the JSON returned by the WebAuthn registration process is included here.\n\tPasskeyRegister *string `json:\"passkey_register,omitempty\"`\n\t// The identity's traits\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithPasskeyMethod UpdateRegistrationFlowWithPasskeyMethod\n\n// NewUpdateRegistrationFlowWithPasskeyMethod instantiates a new UpdateRegistrationFlowWithPasskeyMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithPasskeyMethod(method string, traits map[string]interface{}) *UpdateRegistrationFlowWithPasskeyMethod {\n\tthis := UpdateRegistrationFlowWithPasskeyMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithPasskeyMethodWithDefaults instantiates a new UpdateRegistrationFlowWithPasskeyMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithPasskeyMethodWithDefaults() *UpdateRegistrationFlowWithPasskeyMethod {\n\tthis := UpdateRegistrationFlowWithPasskeyMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPasskeyRegister returns the PasskeyRegister field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetPasskeyRegister() string {\n\tif o == nil || IsNil(o.PasskeyRegister) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasskeyRegister\n}\n\n// GetPasskeyRegisterOk returns a tuple with the PasskeyRegister field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetPasskeyRegisterOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasskeyRegister) {\n\t\treturn nil, false\n\t}\n\treturn o.PasskeyRegister, true\n}\n\n// HasPasskeyRegister returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) HasPasskeyRegister() bool {\n\tif o != nil && !IsNil(o.PasskeyRegister) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasskeyRegister gets a reference to the given string and assigns it to the PasskeyRegister field.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetPasskeyRegister(v string) {\n\to.PasskeyRegister = &v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithPasskeyMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.PasskeyRegister) {\n\t\ttoSerialize[\"passkey_register\"] = o.PasskeyRegister\n\t}\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithPasskeyMethod := _UpdateRegistrationFlowWithPasskeyMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithPasskeyMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithPasskeyMethod(varUpdateRegistrationFlowWithPasskeyMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"passkey_register\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithPasskeyMethod struct {\n\tvalue *UpdateRegistrationFlowWithPasskeyMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasskeyMethod) Get() *UpdateRegistrationFlowWithPasskeyMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasskeyMethod) Set(val *UpdateRegistrationFlowWithPasskeyMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasskeyMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasskeyMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithPasskeyMethod(val *UpdateRegistrationFlowWithPasskeyMethod) *NullableUpdateRegistrationFlowWithPasskeyMethod {\n\treturn &NullableUpdateRegistrationFlowWithPasskeyMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasskeyMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_registration_flow_with_password_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithPasswordMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithPasswordMethod{}\n\n// UpdateRegistrationFlowWithPasswordMethod Update Registration Flow with Password Method\ntype UpdateRegistrationFlowWithPasswordMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method to use  This field must be set to `password` when using the password method.\n\tMethod string `json:\"method\"`\n\t// Password to sign the user up with\n\tPassword string `json:\"password\"`\n\t// The identity's traits\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithPasswordMethod UpdateRegistrationFlowWithPasswordMethod\n\n// NewUpdateRegistrationFlowWithPasswordMethod instantiates a new UpdateRegistrationFlowWithPasswordMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithPasswordMethod(method string, password string, traits map[string]interface{}) *UpdateRegistrationFlowWithPasswordMethod {\n\tthis := UpdateRegistrationFlowWithPasswordMethod{}\n\tthis.Method = method\n\tthis.Password = password\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithPasswordMethodWithDefaults instantiates a new UpdateRegistrationFlowWithPasswordMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithPasswordMethodWithDefaults() *UpdateRegistrationFlowWithPasswordMethod {\n\tthis := UpdateRegistrationFlowWithPasswordMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPassword returns the Password field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetPassword() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetPasswordOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Password, true\n}\n\n// SetPassword sets field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetPassword(v string) {\n\to.Password = v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithPasswordMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"password\"] = o.Password\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"password\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithPasswordMethod := _UpdateRegistrationFlowWithPasswordMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithPasswordMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithPasswordMethod(varUpdateRegistrationFlowWithPasswordMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithPasswordMethod struct {\n\tvalue *UpdateRegistrationFlowWithPasswordMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasswordMethod) Get() *UpdateRegistrationFlowWithPasswordMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasswordMethod) Set(val *UpdateRegistrationFlowWithPasswordMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasswordMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasswordMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithPasswordMethod(val *UpdateRegistrationFlowWithPasswordMethod) *NullableUpdateRegistrationFlowWithPasswordMethod {\n\treturn &NullableUpdateRegistrationFlowWithPasswordMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasswordMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_registration_flow_with_profile_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithProfileMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithProfileMethod{}\n\n// UpdateRegistrationFlowWithProfileMethod Update Registration Flow with Profile Method\ntype UpdateRegistrationFlowWithProfileMethod struct {\n\t// The Anti-CSRF Token  This token is only required when performing browser flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to profile when trying to update a profile.\n\tMethod string `json:\"method\"`\n\t// Screen requests navigation to a previous screen.  This must be set to credential-selection to go back to the credential selection screen. credential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential previous RegistrationScreenPrevious\n\tScreen *string `json:\"screen,omitempty\"`\n\t// Traits  The identity's traits.\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithProfileMethod UpdateRegistrationFlowWithProfileMethod\n\n// NewUpdateRegistrationFlowWithProfileMethod instantiates a new UpdateRegistrationFlowWithProfileMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithProfileMethod(method string, traits map[string]interface{}) *UpdateRegistrationFlowWithProfileMethod {\n\tthis := UpdateRegistrationFlowWithProfileMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithProfileMethodWithDefaults instantiates a new UpdateRegistrationFlowWithProfileMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithProfileMethodWithDefaults() *UpdateRegistrationFlowWithProfileMethod {\n\tthis := UpdateRegistrationFlowWithProfileMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetScreen returns the Screen field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetScreen() string {\n\tif o == nil || IsNil(o.Screen) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Screen\n}\n\n// GetScreenOk returns a tuple with the Screen field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetScreenOk() (*string, bool) {\n\tif o == nil || IsNil(o.Screen) {\n\t\treturn nil, false\n\t}\n\treturn o.Screen, true\n}\n\n// HasScreen returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) HasScreen() bool {\n\tif o != nil && !IsNil(o.Screen) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetScreen gets a reference to the given string and assigns it to the Screen field.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetScreen(v string) {\n\to.Screen = &v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithProfileMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithProfileMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Screen) {\n\t\ttoSerialize[\"screen\"] = o.Screen\n\t}\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithProfileMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithProfileMethod := _UpdateRegistrationFlowWithProfileMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithProfileMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithProfileMethod(varUpdateRegistrationFlowWithProfileMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"screen\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithProfileMethod struct {\n\tvalue *UpdateRegistrationFlowWithProfileMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithProfileMethod) Get() *UpdateRegistrationFlowWithProfileMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithProfileMethod) Set(val *UpdateRegistrationFlowWithProfileMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithProfileMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithProfileMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithProfileMethod(val *UpdateRegistrationFlowWithProfileMethod) *NullableUpdateRegistrationFlowWithProfileMethod {\n\treturn &NullableUpdateRegistrationFlowWithProfileMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithProfileMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithProfileMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_registration_flow_with_saml_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithSamlMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithSamlMethod{}\n\n// UpdateRegistrationFlowWithSamlMethod Update registration flow using SAML\ntype UpdateRegistrationFlowWithSamlMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method to use  This field must be set to `saml` when using the saml method.\n\tMethod string `json:\"method\"`\n\t// The provider to register with\n\tProvider string `json:\"provider\"`\n\t// The identity traits\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithSamlMethod UpdateRegistrationFlowWithSamlMethod\n\n// NewUpdateRegistrationFlowWithSamlMethod instantiates a new UpdateRegistrationFlowWithSamlMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithSamlMethod(method string, provider string) *UpdateRegistrationFlowWithSamlMethod {\n\tthis := UpdateRegistrationFlowWithSamlMethod{}\n\tthis.Method = method\n\tthis.Provider = provider\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithSamlMethodWithDefaults instantiates a new UpdateRegistrationFlowWithSamlMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithSamlMethodWithDefaults() *UpdateRegistrationFlowWithSamlMethod {\n\tthis := UpdateRegistrationFlowWithSamlMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetProvider returns the Provider field value\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithSamlMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"provider\"] = o.Provider\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithSamlMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"provider\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithSamlMethod := _UpdateRegistrationFlowWithSamlMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithSamlMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithSamlMethod(varUpdateRegistrationFlowWithSamlMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithSamlMethod struct {\n\tvalue *UpdateRegistrationFlowWithSamlMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithSamlMethod) Get() *UpdateRegistrationFlowWithSamlMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithSamlMethod) Set(val *UpdateRegistrationFlowWithSamlMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithSamlMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithSamlMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithSamlMethod(val *UpdateRegistrationFlowWithSamlMethod) *NullableUpdateRegistrationFlowWithSamlMethod {\n\treturn &NullableUpdateRegistrationFlowWithSamlMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithSamlMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_registration_flow_with_web_authn_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithWebAuthnMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithWebAuthnMethod{}\n\n// UpdateRegistrationFlowWithWebAuthnMethod Update Registration Flow with WebAuthn Method\ntype UpdateRegistrationFlowWithWebAuthnMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"webauthn\\\" when trying to add, update, or remove a webAuthn pairing.\n\tMethod string `json:\"method\"`\n\t// The identity's traits\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Register a WebAuthn Security Key  It is expected that the JSON returned by the WebAuthn registration process is included here.\n\tWebauthnRegister *string `json:\"webauthn_register,omitempty\"`\n\t// Name of the WebAuthn Security Key to be Added  A human-readable name for the security key which will be added.\n\tWebauthnRegisterDisplayname *string `json:\"webauthn_register_displayname,omitempty\"`\n\tAdditionalProperties        map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithWebAuthnMethod UpdateRegistrationFlowWithWebAuthnMethod\n\n// NewUpdateRegistrationFlowWithWebAuthnMethod instantiates a new UpdateRegistrationFlowWithWebAuthnMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithWebAuthnMethod(method string, traits map[string]interface{}) *UpdateRegistrationFlowWithWebAuthnMethod {\n\tthis := UpdateRegistrationFlowWithWebAuthnMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithWebAuthnMethodWithDefaults instantiates a new UpdateRegistrationFlowWithWebAuthnMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithWebAuthnMethodWithDefaults() *UpdateRegistrationFlowWithWebAuthnMethod {\n\tthis := UpdateRegistrationFlowWithWebAuthnMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetWebauthnRegister returns the WebauthnRegister field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetWebauthnRegister() string {\n\tif o == nil || IsNil(o.WebauthnRegister) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRegister\n}\n\n// GetWebauthnRegisterOk returns a tuple with the WebauthnRegister field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetWebauthnRegisterOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRegister) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRegister, true\n}\n\n// HasWebauthnRegister returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) HasWebauthnRegister() bool {\n\tif o != nil && !IsNil(o.WebauthnRegister) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRegister gets a reference to the given string and assigns it to the WebauthnRegister field.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetWebauthnRegister(v string) {\n\to.WebauthnRegister = &v\n}\n\n// GetWebauthnRegisterDisplayname returns the WebauthnRegisterDisplayname field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetWebauthnRegisterDisplayname() string {\n\tif o == nil || IsNil(o.WebauthnRegisterDisplayname) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRegisterDisplayname\n}\n\n// GetWebauthnRegisterDisplaynameOk returns a tuple with the WebauthnRegisterDisplayname field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetWebauthnRegisterDisplaynameOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRegisterDisplayname) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRegisterDisplayname, true\n}\n\n// HasWebauthnRegisterDisplayname returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) HasWebauthnRegisterDisplayname() bool {\n\tif o != nil && !IsNil(o.WebauthnRegisterDisplayname) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRegisterDisplayname gets a reference to the given string and assigns it to the WebauthnRegisterDisplayname field.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetWebauthnRegisterDisplayname(v string) {\n\to.WebauthnRegisterDisplayname = &v\n}\n\nfunc (o UpdateRegistrationFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithWebAuthnMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.WebauthnRegister) {\n\t\ttoSerialize[\"webauthn_register\"] = o.WebauthnRegister\n\t}\n\tif !IsNil(o.WebauthnRegisterDisplayname) {\n\t\ttoSerialize[\"webauthn_register_displayname\"] = o.WebauthnRegisterDisplayname\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithWebAuthnMethod := _UpdateRegistrationFlowWithWebAuthnMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithWebAuthnMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithWebAuthnMethod(varUpdateRegistrationFlowWithWebAuthnMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"webauthn_register\")\n\t\tdelete(additionalProperties, \"webauthn_register_displayname\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithWebAuthnMethod struct {\n\tvalue *UpdateRegistrationFlowWithWebAuthnMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithWebAuthnMethod) Get() *UpdateRegistrationFlowWithWebAuthnMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithWebAuthnMethod) Set(val *UpdateRegistrationFlowWithWebAuthnMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithWebAuthnMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithWebAuthnMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithWebAuthnMethod(val *UpdateRegistrationFlowWithWebAuthnMethod) *NullableUpdateRegistrationFlowWithWebAuthnMethod {\n\treturn &NullableUpdateRegistrationFlowWithWebAuthnMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithWebAuthnMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_settings_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateSettingsFlowBody - Update Settings Flow Request Body\ntype UpdateSettingsFlowBody struct {\n\tUpdateSettingsFlowWithLookupMethod   *UpdateSettingsFlowWithLookupMethod\n\tUpdateSettingsFlowWithOidcMethod     *UpdateSettingsFlowWithOidcMethod\n\tUpdateSettingsFlowWithPasskeyMethod  *UpdateSettingsFlowWithPasskeyMethod\n\tUpdateSettingsFlowWithPasswordMethod *UpdateSettingsFlowWithPasswordMethod\n\tUpdateSettingsFlowWithProfileMethod  *UpdateSettingsFlowWithProfileMethod\n\tUpdateSettingsFlowWithSamlMethod     *UpdateSettingsFlowWithSamlMethod\n\tUpdateSettingsFlowWithTotpMethod     *UpdateSettingsFlowWithTotpMethod\n\tUpdateSettingsFlowWithWebAuthnMethod *UpdateSettingsFlowWithWebAuthnMethod\n}\n\n// UpdateSettingsFlowWithLookupMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithLookupMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithLookupMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithLookupMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithLookupMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithOidcMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithOidcMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithOidcMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithOidcMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithOidcMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithPasskeyMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithPasskeyMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithPasskeyMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithPasskeyMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithPasskeyMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithPasswordMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithPasswordMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithPasswordMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithPasswordMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithPasswordMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithProfileMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithProfileMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithProfileMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithProfileMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithProfileMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithSamlMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithSamlMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithSamlMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithSamlMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithSamlMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithTotpMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithTotpMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithTotpMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithTotpMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithTotpMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithWebAuthnMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithWebAuthnMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithWebAuthnMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithWebAuthnMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithWebAuthnMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateSettingsFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'lookup_secret'\n\tif jsonDict[\"method\"] == \"lookup_secret\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithLookupMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithLookupMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithLookupMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithLookupMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithLookupMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'oidc'\n\tif jsonDict[\"method\"] == \"oidc\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'passkey'\n\tif jsonDict[\"method\"] == \"passkey\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'password'\n\tif jsonDict[\"method\"] == \"password\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'profile'\n\tif jsonDict[\"method\"] == \"profile\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithProfileMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithProfileMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithProfileMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithProfileMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithProfileMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'saml'\n\tif jsonDict[\"method\"] == \"saml\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'totp'\n\tif jsonDict[\"method\"] == \"totp\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithTotpMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithTotpMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithTotpMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithTotpMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithTotpMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'webauthn'\n\tif jsonDict[\"method\"] == \"webauthn\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithLookupMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithLookupMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithLookupMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithLookupMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithLookupMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithLookupMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithLookupMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithOidcMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithOidcMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithPasskeyMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithPasskeyMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithPasswordMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithPasswordMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithProfileMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithProfileMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithProfileMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithProfileMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithProfileMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithProfileMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithProfileMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithSamlMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithSamlMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithTotpMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithTotpMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithTotpMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithTotpMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithTotpMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithTotpMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithTotpMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithWebAuthnMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithWebAuthnMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateSettingsFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateSettingsFlowWithLookupMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithLookupMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithOidcMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithOidcMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithPasskeyMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithPasskeyMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithPasswordMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithPasswordMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithProfileMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithProfileMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithSamlMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithSamlMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithTotpMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithTotpMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithWebAuthnMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithWebAuthnMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateSettingsFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateSettingsFlowWithLookupMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithLookupMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithOidcMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithPasskeyMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithPasswordMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithProfileMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithProfileMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithSamlMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithTotpMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithTotpMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithWebAuthnMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateSettingsFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateSettingsFlowWithLookupMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithLookupMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithOidcMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithPasskeyMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithPasswordMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithProfileMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithProfileMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithSamlMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithTotpMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithTotpMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithWebAuthnMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateSettingsFlowBody struct {\n\tvalue *UpdateSettingsFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowBody) Get() *UpdateSettingsFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowBody) Set(val *UpdateSettingsFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowBody(val *UpdateSettingsFlowBody) *NullableUpdateSettingsFlowBody {\n\treturn &NullableUpdateSettingsFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_settings_flow_with_lookup_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithLookupMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithLookupMethod{}\n\n// UpdateSettingsFlowWithLookupMethod Update Settings Flow with Lookup Method\ntype UpdateSettingsFlowWithLookupMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// If set to true will save the regenerated lookup secrets\n\tLookupSecretConfirm *bool `json:\"lookup_secret_confirm,omitempty\"`\n\t// Disables this method if true.\n\tLookupSecretDisable *bool `json:\"lookup_secret_disable,omitempty\"`\n\t// If set to true will regenerate the lookup secrets\n\tLookupSecretRegenerate *bool `json:\"lookup_secret_regenerate,omitempty\"`\n\t// If set to true will reveal the lookup secrets\n\tLookupSecretReveal *bool `json:\"lookup_secret_reveal,omitempty\"`\n\t// Method  Should be set to \\\"lookup\\\" when trying to add, update, or remove a lookup pairing.\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithLookupMethod UpdateSettingsFlowWithLookupMethod\n\n// NewUpdateSettingsFlowWithLookupMethod instantiates a new UpdateSettingsFlowWithLookupMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithLookupMethod(method string) *UpdateSettingsFlowWithLookupMethod {\n\tthis := UpdateSettingsFlowWithLookupMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithLookupMethodWithDefaults instantiates a new UpdateSettingsFlowWithLookupMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithLookupMethodWithDefaults() *UpdateSettingsFlowWithLookupMethod {\n\tthis := UpdateSettingsFlowWithLookupMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetLookupSecretConfirm returns the LookupSecretConfirm field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretConfirm() bool {\n\tif o == nil || IsNil(o.LookupSecretConfirm) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.LookupSecretConfirm\n}\n\n// GetLookupSecretConfirmOk returns a tuple with the LookupSecretConfirm field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretConfirmOk() (*bool, bool) {\n\tif o == nil || IsNil(o.LookupSecretConfirm) {\n\t\treturn nil, false\n\t}\n\treturn o.LookupSecretConfirm, true\n}\n\n// HasLookupSecretConfirm returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasLookupSecretConfirm() bool {\n\tif o != nil && !IsNil(o.LookupSecretConfirm) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLookupSecretConfirm gets a reference to the given bool and assigns it to the LookupSecretConfirm field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetLookupSecretConfirm(v bool) {\n\to.LookupSecretConfirm = &v\n}\n\n// GetLookupSecretDisable returns the LookupSecretDisable field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretDisable() bool {\n\tif o == nil || IsNil(o.LookupSecretDisable) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.LookupSecretDisable\n}\n\n// GetLookupSecretDisableOk returns a tuple with the LookupSecretDisable field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretDisableOk() (*bool, bool) {\n\tif o == nil || IsNil(o.LookupSecretDisable) {\n\t\treturn nil, false\n\t}\n\treturn o.LookupSecretDisable, true\n}\n\n// HasLookupSecretDisable returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasLookupSecretDisable() bool {\n\tif o != nil && !IsNil(o.LookupSecretDisable) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLookupSecretDisable gets a reference to the given bool and assigns it to the LookupSecretDisable field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetLookupSecretDisable(v bool) {\n\to.LookupSecretDisable = &v\n}\n\n// GetLookupSecretRegenerate returns the LookupSecretRegenerate field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretRegenerate() bool {\n\tif o == nil || IsNil(o.LookupSecretRegenerate) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.LookupSecretRegenerate\n}\n\n// GetLookupSecretRegenerateOk returns a tuple with the LookupSecretRegenerate field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretRegenerateOk() (*bool, bool) {\n\tif o == nil || IsNil(o.LookupSecretRegenerate) {\n\t\treturn nil, false\n\t}\n\treturn o.LookupSecretRegenerate, true\n}\n\n// HasLookupSecretRegenerate returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasLookupSecretRegenerate() bool {\n\tif o != nil && !IsNil(o.LookupSecretRegenerate) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLookupSecretRegenerate gets a reference to the given bool and assigns it to the LookupSecretRegenerate field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetLookupSecretRegenerate(v bool) {\n\to.LookupSecretRegenerate = &v\n}\n\n// GetLookupSecretReveal returns the LookupSecretReveal field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretReveal() bool {\n\tif o == nil || IsNil(o.LookupSecretReveal) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.LookupSecretReveal\n}\n\n// GetLookupSecretRevealOk returns a tuple with the LookupSecretReveal field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretRevealOk() (*bool, bool) {\n\tif o == nil || IsNil(o.LookupSecretReveal) {\n\t\treturn nil, false\n\t}\n\treturn o.LookupSecretReveal, true\n}\n\n// HasLookupSecretReveal returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasLookupSecretReveal() bool {\n\tif o != nil && !IsNil(o.LookupSecretReveal) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLookupSecretReveal gets a reference to the given bool and assigns it to the LookupSecretReveal field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetLookupSecretReveal(v bool) {\n\to.LookupSecretReveal = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateSettingsFlowWithLookupMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithLookupMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.LookupSecretConfirm) {\n\t\ttoSerialize[\"lookup_secret_confirm\"] = o.LookupSecretConfirm\n\t}\n\tif !IsNil(o.LookupSecretDisable) {\n\t\ttoSerialize[\"lookup_secret_disable\"] = o.LookupSecretDisable\n\t}\n\tif !IsNil(o.LookupSecretRegenerate) {\n\t\ttoSerialize[\"lookup_secret_regenerate\"] = o.LookupSecretRegenerate\n\t}\n\tif !IsNil(o.LookupSecretReveal) {\n\t\ttoSerialize[\"lookup_secret_reveal\"] = o.LookupSecretReveal\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithLookupMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithLookupMethod := _UpdateSettingsFlowWithLookupMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithLookupMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithLookupMethod(varUpdateSettingsFlowWithLookupMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"lookup_secret_confirm\")\n\t\tdelete(additionalProperties, \"lookup_secret_disable\")\n\t\tdelete(additionalProperties, \"lookup_secret_regenerate\")\n\t\tdelete(additionalProperties, \"lookup_secret_reveal\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithLookupMethod struct {\n\tvalue *UpdateSettingsFlowWithLookupMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithLookupMethod) Get() *UpdateSettingsFlowWithLookupMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithLookupMethod) Set(val *UpdateSettingsFlowWithLookupMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithLookupMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithLookupMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithLookupMethod(val *UpdateSettingsFlowWithLookupMethod) *NullableUpdateSettingsFlowWithLookupMethod {\n\treturn &NullableUpdateSettingsFlowWithLookupMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithLookupMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithLookupMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_settings_flow_with_oidc_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithOidcMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithOidcMethod{}\n\n// UpdateSettingsFlowWithOidcMethod Update Settings Flow with OpenID Connect Method\ntype UpdateSettingsFlowWithOidcMethod struct {\n\t// Flow ID is the flow's ID.  in: query\n\tFlow *string `json:\"flow,omitempty\"`\n\t// Link this provider  Either this or `unlink` must be set.  type: string in: body\n\tLink *string `json:\"link,omitempty\"`\n\t// Method  Should be set to profile when trying to update a profile.\n\tMethod string `json:\"method\"`\n\t// The identity's traits  in: body\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Unlink this provider  Either this or `link` must be set.  type: string in: body\n\tUnlink *string `json:\"unlink,omitempty\"`\n\t// UpstreamParameters are the parameters that are passed to the upstream identity provider.  These parameters are optional and depend on what the upstream identity provider supports. Supported parameters are: `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session. `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`. `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`. `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\n\tUpstreamParameters   map[string]interface{} `json:\"upstream_parameters,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithOidcMethod UpdateSettingsFlowWithOidcMethod\n\n// NewUpdateSettingsFlowWithOidcMethod instantiates a new UpdateSettingsFlowWithOidcMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithOidcMethod(method string) *UpdateSettingsFlowWithOidcMethod {\n\tthis := UpdateSettingsFlowWithOidcMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithOidcMethodWithDefaults instantiates a new UpdateSettingsFlowWithOidcMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithOidcMethodWithDefaults() *UpdateSettingsFlowWithOidcMethod {\n\tthis := UpdateSettingsFlowWithOidcMethod{}\n\treturn &this\n}\n\n// GetFlow returns the Flow field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetFlow() string {\n\tif o == nil || IsNil(o.Flow) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetFlowOk() (*string, bool) {\n\tif o == nil || IsNil(o.Flow) {\n\t\treturn nil, false\n\t}\n\treturn o.Flow, true\n}\n\n// HasFlow returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasFlow() bool {\n\tif o != nil && !IsNil(o.Flow) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFlow gets a reference to the given string and assigns it to the Flow field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetFlow(v string) {\n\to.Flow = &v\n}\n\n// GetLink returns the Link field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetLink() string {\n\tif o == nil || IsNil(o.Link) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Link\n}\n\n// GetLinkOk returns a tuple with the Link field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetLinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Link) {\n\t\treturn nil, false\n\t}\n\treturn o.Link, true\n}\n\n// HasLink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasLink() bool {\n\tif o != nil && !IsNil(o.Link) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLink gets a reference to the given string and assigns it to the Link field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetLink(v string) {\n\to.Link = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetUnlink returns the Unlink field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetUnlink() string {\n\tif o == nil || IsNil(o.Unlink) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Unlink\n}\n\n// GetUnlinkOk returns a tuple with the Unlink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetUnlinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Unlink) {\n\t\treturn nil, false\n\t}\n\treturn o.Unlink, true\n}\n\n// HasUnlink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasUnlink() bool {\n\tif o != nil && !IsNil(o.Unlink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUnlink gets a reference to the given string and assigns it to the Unlink field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetUnlink(v string) {\n\to.Unlink = &v\n}\n\n// GetUpstreamParameters returns the UpstreamParameters field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetUpstreamParameters() map[string]interface{} {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.UpstreamParameters\n}\n\n// GetUpstreamParametersOk returns a tuple with the UpstreamParameters field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetUpstreamParametersOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.UpstreamParameters, true\n}\n\n// HasUpstreamParameters returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasUpstreamParameters() bool {\n\tif o != nil && !IsNil(o.UpstreamParameters) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpstreamParameters gets a reference to the given map[string]interface{} and assigns it to the UpstreamParameters field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetUpstreamParameters(v map[string]interface{}) {\n\to.UpstreamParameters = v\n}\n\nfunc (o UpdateSettingsFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithOidcMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Flow) {\n\t\ttoSerialize[\"flow\"] = o.Flow\n\t}\n\tif !IsNil(o.Link) {\n\t\ttoSerialize[\"link\"] = o.Link\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.Unlink) {\n\t\ttoSerialize[\"unlink\"] = o.Unlink\n\t}\n\tif !IsNil(o.UpstreamParameters) {\n\t\ttoSerialize[\"upstream_parameters\"] = o.UpstreamParameters\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithOidcMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithOidcMethod := _UpdateSettingsFlowWithOidcMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithOidcMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithOidcMethod(varUpdateSettingsFlowWithOidcMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"flow\")\n\t\tdelete(additionalProperties, \"link\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"unlink\")\n\t\tdelete(additionalProperties, \"upstream_parameters\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithOidcMethod struct {\n\tvalue *UpdateSettingsFlowWithOidcMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithOidcMethod) Get() *UpdateSettingsFlowWithOidcMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithOidcMethod) Set(val *UpdateSettingsFlowWithOidcMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithOidcMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithOidcMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithOidcMethod(val *UpdateSettingsFlowWithOidcMethod) *NullableUpdateSettingsFlowWithOidcMethod {\n\treturn &NullableUpdateSettingsFlowWithOidcMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithOidcMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_settings_flow_with_passkey_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithPasskeyMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithPasskeyMethod{}\n\n// UpdateSettingsFlowWithPasskeyMethod Update Settings Flow with Passkey Method\ntype UpdateSettingsFlowWithPasskeyMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"passkey\\\" when trying to add, update, or remove a webAuthn pairing.\n\tMethod string `json:\"method\"`\n\t// Remove a WebAuthn Security Key  This must contain the ID of the WebAuthN connection.\n\tPasskeyRemove *string `json:\"passkey_remove,omitempty\"`\n\t// Register a WebAuthn Security Key  It is expected that the JSON returned by the WebAuthn registration process is included here.\n\tPasskeySettingsRegister *string `json:\"passkey_settings_register,omitempty\"`\n\tAdditionalProperties    map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithPasskeyMethod UpdateSettingsFlowWithPasskeyMethod\n\n// NewUpdateSettingsFlowWithPasskeyMethod instantiates a new UpdateSettingsFlowWithPasskeyMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithPasskeyMethod(method string) *UpdateSettingsFlowWithPasskeyMethod {\n\tthis := UpdateSettingsFlowWithPasskeyMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithPasskeyMethodWithDefaults instantiates a new UpdateSettingsFlowWithPasskeyMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithPasskeyMethodWithDefaults() *UpdateSettingsFlowWithPasskeyMethod {\n\tthis := UpdateSettingsFlowWithPasskeyMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPasskeyRemove returns the PasskeyRemove field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetPasskeyRemove() string {\n\tif o == nil || IsNil(o.PasskeyRemove) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasskeyRemove\n}\n\n// GetPasskeyRemoveOk returns a tuple with the PasskeyRemove field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetPasskeyRemoveOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasskeyRemove) {\n\t\treturn nil, false\n\t}\n\treturn o.PasskeyRemove, true\n}\n\n// HasPasskeyRemove returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) HasPasskeyRemove() bool {\n\tif o != nil && !IsNil(o.PasskeyRemove) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasskeyRemove gets a reference to the given string and assigns it to the PasskeyRemove field.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) SetPasskeyRemove(v string) {\n\to.PasskeyRemove = &v\n}\n\n// GetPasskeySettingsRegister returns the PasskeySettingsRegister field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetPasskeySettingsRegister() string {\n\tif o == nil || IsNil(o.PasskeySettingsRegister) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasskeySettingsRegister\n}\n\n// GetPasskeySettingsRegisterOk returns a tuple with the PasskeySettingsRegister field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetPasskeySettingsRegisterOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasskeySettingsRegister) {\n\t\treturn nil, false\n\t}\n\treturn o.PasskeySettingsRegister, true\n}\n\n// HasPasskeySettingsRegister returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) HasPasskeySettingsRegister() bool {\n\tif o != nil && !IsNil(o.PasskeySettingsRegister) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasskeySettingsRegister gets a reference to the given string and assigns it to the PasskeySettingsRegister field.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) SetPasskeySettingsRegister(v string) {\n\to.PasskeySettingsRegister = &v\n}\n\nfunc (o UpdateSettingsFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithPasskeyMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.PasskeyRemove) {\n\t\ttoSerialize[\"passkey_remove\"] = o.PasskeyRemove\n\t}\n\tif !IsNil(o.PasskeySettingsRegister) {\n\t\ttoSerialize[\"passkey_settings_register\"] = o.PasskeySettingsRegister\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithPasskeyMethod := _UpdateSettingsFlowWithPasskeyMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithPasskeyMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithPasskeyMethod(varUpdateSettingsFlowWithPasskeyMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"passkey_remove\")\n\t\tdelete(additionalProperties, \"passkey_settings_register\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithPasskeyMethod struct {\n\tvalue *UpdateSettingsFlowWithPasskeyMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasskeyMethod) Get() *UpdateSettingsFlowWithPasskeyMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasskeyMethod) Set(val *UpdateSettingsFlowWithPasskeyMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasskeyMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasskeyMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithPasskeyMethod(val *UpdateSettingsFlowWithPasskeyMethod) *NullableUpdateSettingsFlowWithPasskeyMethod {\n\treturn &NullableUpdateSettingsFlowWithPasskeyMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasskeyMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_settings_flow_with_password_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithPasswordMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithPasswordMethod{}\n\n// UpdateSettingsFlowWithPasswordMethod Update Settings Flow with Password Method\ntype UpdateSettingsFlowWithPasswordMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to password when trying to update a password.\n\tMethod string `json:\"method\"`\n\t// Password is the updated password\n\tPassword string `json:\"password\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithPasswordMethod UpdateSettingsFlowWithPasswordMethod\n\n// NewUpdateSettingsFlowWithPasswordMethod instantiates a new UpdateSettingsFlowWithPasswordMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithPasswordMethod(method string, password string) *UpdateSettingsFlowWithPasswordMethod {\n\tthis := UpdateSettingsFlowWithPasswordMethod{}\n\tthis.Method = method\n\tthis.Password = password\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithPasswordMethodWithDefaults instantiates a new UpdateSettingsFlowWithPasswordMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithPasswordMethodWithDefaults() *UpdateSettingsFlowWithPasswordMethod {\n\tthis := UpdateSettingsFlowWithPasswordMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithPasswordMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPassword returns the Password field value\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetPassword() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetPasswordOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Password, true\n}\n\n// SetPassword sets field value\nfunc (o *UpdateSettingsFlowWithPasswordMethod) SetPassword(v string) {\n\to.Password = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateSettingsFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithPasswordMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"password\"] = o.Password\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithPasswordMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"password\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithPasswordMethod := _UpdateSettingsFlowWithPasswordMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithPasswordMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithPasswordMethod(varUpdateSettingsFlowWithPasswordMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithPasswordMethod struct {\n\tvalue *UpdateSettingsFlowWithPasswordMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasswordMethod) Get() *UpdateSettingsFlowWithPasswordMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasswordMethod) Set(val *UpdateSettingsFlowWithPasswordMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasswordMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasswordMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithPasswordMethod(val *UpdateSettingsFlowWithPasswordMethod) *NullableUpdateSettingsFlowWithPasswordMethod {\n\treturn &NullableUpdateSettingsFlowWithPasswordMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasswordMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_settings_flow_with_profile_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithProfileMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithProfileMethod{}\n\n// UpdateSettingsFlowWithProfileMethod Update Settings Flow with Profile Method\ntype UpdateSettingsFlowWithProfileMethod struct {\n\t// The Anti-CSRF Token  This token is only required when performing browser flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to profile when trying to update a profile.\n\tMethod string `json:\"method\"`\n\t// Traits  The identity's traits.\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithProfileMethod UpdateSettingsFlowWithProfileMethod\n\n// NewUpdateSettingsFlowWithProfileMethod instantiates a new UpdateSettingsFlowWithProfileMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithProfileMethod(method string, traits map[string]interface{}) *UpdateSettingsFlowWithProfileMethod {\n\tthis := UpdateSettingsFlowWithProfileMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithProfileMethodWithDefaults instantiates a new UpdateSettingsFlowWithProfileMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithProfileMethodWithDefaults() *UpdateSettingsFlowWithProfileMethod {\n\tthis := UpdateSettingsFlowWithProfileMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithProfileMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithProfileMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateSettingsFlowWithProfileMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithProfileMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateSettingsFlowWithProfileMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithProfileMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithProfileMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithProfileMethod := _UpdateSettingsFlowWithProfileMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithProfileMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithProfileMethod(varUpdateSettingsFlowWithProfileMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithProfileMethod struct {\n\tvalue *UpdateSettingsFlowWithProfileMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithProfileMethod) Get() *UpdateSettingsFlowWithProfileMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithProfileMethod) Set(val *UpdateSettingsFlowWithProfileMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithProfileMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithProfileMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithProfileMethod(val *UpdateSettingsFlowWithProfileMethod) *NullableUpdateSettingsFlowWithProfileMethod {\n\treturn &NullableUpdateSettingsFlowWithProfileMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithProfileMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithProfileMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_settings_flow_with_saml_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithSamlMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithSamlMethod{}\n\n// UpdateSettingsFlowWithSamlMethod Update settings flow using SAML\ntype UpdateSettingsFlowWithSamlMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Flow ID is the flow's ID.  in: query\n\tFlow *string `json:\"flow,omitempty\"`\n\t// Link this provider  Either this or `unlink` must be set.  type: string in: body\n\tLink *string `json:\"link,omitempty\"`\n\t// Method  Should be set to saml when trying to update a profile.\n\tMethod string `json:\"method\"`\n\t// The identity's traits  in: body\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Unlink this provider  Either this or `link` must be set.  type: string in: body\n\tUnlink               *string `json:\"unlink,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithSamlMethod UpdateSettingsFlowWithSamlMethod\n\n// NewUpdateSettingsFlowWithSamlMethod instantiates a new UpdateSettingsFlowWithSamlMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithSamlMethod(method string) *UpdateSettingsFlowWithSamlMethod {\n\tthis := UpdateSettingsFlowWithSamlMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithSamlMethodWithDefaults instantiates a new UpdateSettingsFlowWithSamlMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithSamlMethodWithDefaults() *UpdateSettingsFlowWithSamlMethod {\n\tthis := UpdateSettingsFlowWithSamlMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetFlow returns the Flow field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetFlow() string {\n\tif o == nil || IsNil(o.Flow) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetFlowOk() (*string, bool) {\n\tif o == nil || IsNil(o.Flow) {\n\t\treturn nil, false\n\t}\n\treturn o.Flow, true\n}\n\n// HasFlow returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasFlow() bool {\n\tif o != nil && !IsNil(o.Flow) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFlow gets a reference to the given string and assigns it to the Flow field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetFlow(v string) {\n\to.Flow = &v\n}\n\n// GetLink returns the Link field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetLink() string {\n\tif o == nil || IsNil(o.Link) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Link\n}\n\n// GetLinkOk returns a tuple with the Link field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetLinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Link) {\n\t\treturn nil, false\n\t}\n\treturn o.Link, true\n}\n\n// HasLink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasLink() bool {\n\tif o != nil && !IsNil(o.Link) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLink gets a reference to the given string and assigns it to the Link field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetLink(v string) {\n\to.Link = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetUnlink returns the Unlink field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetUnlink() string {\n\tif o == nil || IsNil(o.Unlink) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Unlink\n}\n\n// GetUnlinkOk returns a tuple with the Unlink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetUnlinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Unlink) {\n\t\treturn nil, false\n\t}\n\treturn o.Unlink, true\n}\n\n// HasUnlink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasUnlink() bool {\n\tif o != nil && !IsNil(o.Unlink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUnlink gets a reference to the given string and assigns it to the Unlink field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetUnlink(v string) {\n\to.Unlink = &v\n}\n\nfunc (o UpdateSettingsFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithSamlMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.Flow) {\n\t\ttoSerialize[\"flow\"] = o.Flow\n\t}\n\tif !IsNil(o.Link) {\n\t\ttoSerialize[\"link\"] = o.Link\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.Unlink) {\n\t\ttoSerialize[\"unlink\"] = o.Unlink\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithSamlMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithSamlMethod := _UpdateSettingsFlowWithSamlMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithSamlMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithSamlMethod(varUpdateSettingsFlowWithSamlMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"flow\")\n\t\tdelete(additionalProperties, \"link\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"unlink\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithSamlMethod struct {\n\tvalue *UpdateSettingsFlowWithSamlMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithSamlMethod) Get() *UpdateSettingsFlowWithSamlMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithSamlMethod) Set(val *UpdateSettingsFlowWithSamlMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithSamlMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithSamlMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithSamlMethod(val *UpdateSettingsFlowWithSamlMethod) *NullableUpdateSettingsFlowWithSamlMethod {\n\treturn &NullableUpdateSettingsFlowWithSamlMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithSamlMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_settings_flow_with_totp_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithTotpMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithTotpMethod{}\n\n// UpdateSettingsFlowWithTotpMethod Update Settings Flow with TOTP Method\ntype UpdateSettingsFlowWithTotpMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"totp\\\" when trying to add, update, or remove a totp pairing.\n\tMethod string `json:\"method\"`\n\t// ValidationTOTP must contain a valid TOTP based on the\n\tTotpCode *string `json:\"totp_code,omitempty\"`\n\t// UnlinkTOTP if true will remove the TOTP pairing, effectively removing the credential. This can be used to set up a new TOTP device.\n\tTotpUnlink *bool `json:\"totp_unlink,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithTotpMethod UpdateSettingsFlowWithTotpMethod\n\n// NewUpdateSettingsFlowWithTotpMethod instantiates a new UpdateSettingsFlowWithTotpMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithTotpMethod(method string) *UpdateSettingsFlowWithTotpMethod {\n\tthis := UpdateSettingsFlowWithTotpMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithTotpMethodWithDefaults instantiates a new UpdateSettingsFlowWithTotpMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithTotpMethodWithDefaults() *UpdateSettingsFlowWithTotpMethod {\n\tthis := UpdateSettingsFlowWithTotpMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTotpCode returns the TotpCode field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTotpCode() string {\n\tif o == nil || IsNil(o.TotpCode) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.TotpCode\n}\n\n// GetTotpCodeOk returns a tuple with the TotpCode field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTotpCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.TotpCode) {\n\t\treturn nil, false\n\t}\n\treturn o.TotpCode, true\n}\n\n// HasTotpCode returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) HasTotpCode() bool {\n\tif o != nil && !IsNil(o.TotpCode) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTotpCode gets a reference to the given string and assigns it to the TotpCode field.\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetTotpCode(v string) {\n\to.TotpCode = &v\n}\n\n// GetTotpUnlink returns the TotpUnlink field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTotpUnlink() bool {\n\tif o == nil || IsNil(o.TotpUnlink) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.TotpUnlink\n}\n\n// GetTotpUnlinkOk returns a tuple with the TotpUnlink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTotpUnlinkOk() (*bool, bool) {\n\tif o == nil || IsNil(o.TotpUnlink) {\n\t\treturn nil, false\n\t}\n\treturn o.TotpUnlink, true\n}\n\n// HasTotpUnlink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) HasTotpUnlink() bool {\n\tif o != nil && !IsNil(o.TotpUnlink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTotpUnlink gets a reference to the given bool and assigns it to the TotpUnlink field.\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetTotpUnlink(v bool) {\n\to.TotpUnlink = &v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateSettingsFlowWithTotpMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithTotpMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TotpCode) {\n\t\ttoSerialize[\"totp_code\"] = o.TotpCode\n\t}\n\tif !IsNil(o.TotpUnlink) {\n\t\ttoSerialize[\"totp_unlink\"] = o.TotpUnlink\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithTotpMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithTotpMethod := _UpdateSettingsFlowWithTotpMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithTotpMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithTotpMethod(varUpdateSettingsFlowWithTotpMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"totp_code\")\n\t\tdelete(additionalProperties, \"totp_unlink\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithTotpMethod struct {\n\tvalue *UpdateSettingsFlowWithTotpMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithTotpMethod) Get() *UpdateSettingsFlowWithTotpMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithTotpMethod) Set(val *UpdateSettingsFlowWithTotpMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithTotpMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithTotpMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithTotpMethod(val *UpdateSettingsFlowWithTotpMethod) *NullableUpdateSettingsFlowWithTotpMethod {\n\treturn &NullableUpdateSettingsFlowWithTotpMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithTotpMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithTotpMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_settings_flow_with_web_authn_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithWebAuthnMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithWebAuthnMethod{}\n\n// UpdateSettingsFlowWithWebAuthnMethod Update Settings Flow with WebAuthn Method\ntype UpdateSettingsFlowWithWebAuthnMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"webauthn\\\" when trying to add, update, or remove a webAuthn pairing.\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Register a WebAuthn Security Key  It is expected that the JSON returned by the WebAuthn registration process is included here.\n\tWebauthnRegister *string `json:\"webauthn_register,omitempty\"`\n\t// Name of the WebAuthn Security Key to be Added  A human-readable name for the security key which will be added.\n\tWebauthnRegisterDisplayname *string `json:\"webauthn_register_displayname,omitempty\"`\n\t// Remove a WebAuthn Security Key  This must contain the ID of the WebAuthN connection.\n\tWebauthnRemove       *string `json:\"webauthn_remove,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithWebAuthnMethod UpdateSettingsFlowWithWebAuthnMethod\n\n// NewUpdateSettingsFlowWithWebAuthnMethod instantiates a new UpdateSettingsFlowWithWebAuthnMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithWebAuthnMethod(method string) *UpdateSettingsFlowWithWebAuthnMethod {\n\tthis := UpdateSettingsFlowWithWebAuthnMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithWebAuthnMethodWithDefaults instantiates a new UpdateSettingsFlowWithWebAuthnMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithWebAuthnMethodWithDefaults() *UpdateSettingsFlowWithWebAuthnMethod {\n\tthis := UpdateSettingsFlowWithWebAuthnMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetWebauthnRegister returns the WebauthnRegister field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRegister() string {\n\tif o == nil || IsNil(o.WebauthnRegister) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRegister\n}\n\n// GetWebauthnRegisterOk returns a tuple with the WebauthnRegister field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRegisterOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRegister) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRegister, true\n}\n\n// HasWebauthnRegister returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasWebauthnRegister() bool {\n\tif o != nil && !IsNil(o.WebauthnRegister) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRegister gets a reference to the given string and assigns it to the WebauthnRegister field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetWebauthnRegister(v string) {\n\to.WebauthnRegister = &v\n}\n\n// GetWebauthnRegisterDisplayname returns the WebauthnRegisterDisplayname field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRegisterDisplayname() string {\n\tif o == nil || IsNil(o.WebauthnRegisterDisplayname) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRegisterDisplayname\n}\n\n// GetWebauthnRegisterDisplaynameOk returns a tuple with the WebauthnRegisterDisplayname field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRegisterDisplaynameOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRegisterDisplayname) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRegisterDisplayname, true\n}\n\n// HasWebauthnRegisterDisplayname returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasWebauthnRegisterDisplayname() bool {\n\tif o != nil && !IsNil(o.WebauthnRegisterDisplayname) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRegisterDisplayname gets a reference to the given string and assigns it to the WebauthnRegisterDisplayname field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetWebauthnRegisterDisplayname(v string) {\n\to.WebauthnRegisterDisplayname = &v\n}\n\n// GetWebauthnRemove returns the WebauthnRemove field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRemove() string {\n\tif o == nil || IsNil(o.WebauthnRemove) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRemove\n}\n\n// GetWebauthnRemoveOk returns a tuple with the WebauthnRemove field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRemoveOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRemove) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRemove, true\n}\n\n// HasWebauthnRemove returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasWebauthnRemove() bool {\n\tif o != nil && !IsNil(o.WebauthnRemove) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRemove gets a reference to the given string and assigns it to the WebauthnRemove field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetWebauthnRemove(v string) {\n\to.WebauthnRemove = &v\n}\n\nfunc (o UpdateSettingsFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithWebAuthnMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.WebauthnRegister) {\n\t\ttoSerialize[\"webauthn_register\"] = o.WebauthnRegister\n\t}\n\tif !IsNil(o.WebauthnRegisterDisplayname) {\n\t\ttoSerialize[\"webauthn_register_displayname\"] = o.WebauthnRegisterDisplayname\n\t}\n\tif !IsNil(o.WebauthnRemove) {\n\t\ttoSerialize[\"webauthn_remove\"] = o.WebauthnRemove\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithWebAuthnMethod := _UpdateSettingsFlowWithWebAuthnMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithWebAuthnMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithWebAuthnMethod(varUpdateSettingsFlowWithWebAuthnMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"webauthn_register\")\n\t\tdelete(additionalProperties, \"webauthn_register_displayname\")\n\t\tdelete(additionalProperties, \"webauthn_remove\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithWebAuthnMethod struct {\n\tvalue *UpdateSettingsFlowWithWebAuthnMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithWebAuthnMethod) Get() *UpdateSettingsFlowWithWebAuthnMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithWebAuthnMethod) Set(val *UpdateSettingsFlowWithWebAuthnMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithWebAuthnMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithWebAuthnMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithWebAuthnMethod(val *UpdateSettingsFlowWithWebAuthnMethod) *NullableUpdateSettingsFlowWithWebAuthnMethod {\n\treturn &NullableUpdateSettingsFlowWithWebAuthnMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithWebAuthnMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_verification_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateVerificationFlowBody - Update Verification Flow Request Body\ntype UpdateVerificationFlowBody struct {\n\tUpdateVerificationFlowWithCodeMethod *UpdateVerificationFlowWithCodeMethod\n\tUpdateVerificationFlowWithLinkMethod *UpdateVerificationFlowWithLinkMethod\n}\n\n// UpdateVerificationFlowWithCodeMethodAsUpdateVerificationFlowBody is a convenience function that returns UpdateVerificationFlowWithCodeMethod wrapped in UpdateVerificationFlowBody\nfunc UpdateVerificationFlowWithCodeMethodAsUpdateVerificationFlowBody(v *UpdateVerificationFlowWithCodeMethod) UpdateVerificationFlowBody {\n\treturn UpdateVerificationFlowBody{\n\t\tUpdateVerificationFlowWithCodeMethod: v,\n\t}\n}\n\n// UpdateVerificationFlowWithLinkMethodAsUpdateVerificationFlowBody is a convenience function that returns UpdateVerificationFlowWithLinkMethod wrapped in UpdateVerificationFlowBody\nfunc UpdateVerificationFlowWithLinkMethodAsUpdateVerificationFlowBody(v *UpdateVerificationFlowWithLinkMethod) UpdateVerificationFlowBody {\n\treturn UpdateVerificationFlowBody{\n\t\tUpdateVerificationFlowWithLinkMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateVerificationFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'code'\n\tif jsonDict[\"method\"] == \"code\" {\n\t\t// try to unmarshal JSON data into UpdateVerificationFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateVerificationFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateVerificationFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateVerificationFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateVerificationFlowBody as UpdateVerificationFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'link'\n\tif jsonDict[\"method\"] == \"link\" {\n\t\t// try to unmarshal JSON data into UpdateVerificationFlowWithLinkMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateVerificationFlowWithLinkMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateVerificationFlowWithLinkMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateVerificationFlowWithLinkMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateVerificationFlowBody as UpdateVerificationFlowWithLinkMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateVerificationFlowWithCodeMethod'\n\tif jsonDict[\"method\"] == \"updateVerificationFlowWithCodeMethod\" {\n\t\t// try to unmarshal JSON data into UpdateVerificationFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateVerificationFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateVerificationFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateVerificationFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateVerificationFlowBody as UpdateVerificationFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateVerificationFlowWithLinkMethod'\n\tif jsonDict[\"method\"] == \"updateVerificationFlowWithLinkMethod\" {\n\t\t// try to unmarshal JSON data into UpdateVerificationFlowWithLinkMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateVerificationFlowWithLinkMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateVerificationFlowWithLinkMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateVerificationFlowWithLinkMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateVerificationFlowBody as UpdateVerificationFlowWithLinkMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateVerificationFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateVerificationFlowWithCodeMethod != nil {\n\t\treturn json.Marshal(&src.UpdateVerificationFlowWithCodeMethod)\n\t}\n\n\tif src.UpdateVerificationFlowWithLinkMethod != nil {\n\t\treturn json.Marshal(&src.UpdateVerificationFlowWithLinkMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateVerificationFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateVerificationFlowWithCodeMethod != nil {\n\t\treturn obj.UpdateVerificationFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateVerificationFlowWithLinkMethod != nil {\n\t\treturn obj.UpdateVerificationFlowWithLinkMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateVerificationFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateVerificationFlowWithCodeMethod != nil {\n\t\treturn *obj.UpdateVerificationFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateVerificationFlowWithLinkMethod != nil {\n\t\treturn *obj.UpdateVerificationFlowWithLinkMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateVerificationFlowBody struct {\n\tvalue *UpdateVerificationFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateVerificationFlowBody) Get() *UpdateVerificationFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateVerificationFlowBody) Set(val *UpdateVerificationFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateVerificationFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateVerificationFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateVerificationFlowBody(val *UpdateVerificationFlowBody) *NullableUpdateVerificationFlowBody {\n\treturn &NullableUpdateVerificationFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateVerificationFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateVerificationFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_verification_flow_with_code_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateVerificationFlowWithCodeMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateVerificationFlowWithCodeMethod{}\n\n// UpdateVerificationFlowWithCodeMethod struct for UpdateVerificationFlowWithCodeMethod\ntype UpdateVerificationFlowWithCodeMethod struct {\n\t// Code from the recovery email  If you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\n\tCode *string `json:\"code,omitempty\"`\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// The email address to verify  If the email belongs to a valid account, a verifiation email will be sent.  If you want to notify the email address if the account does not exist, see the [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation#attempted-verification-notifications)  If a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.  format: email\n\tEmail *string `json:\"email,omitempty\"`\n\t// Method is the method that should be used for this verification flow  Allowed values are `link` and `code`. link VerificationStrategyLink code VerificationStrategyCode\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateVerificationFlowWithCodeMethod UpdateVerificationFlowWithCodeMethod\n\n// NewUpdateVerificationFlowWithCodeMethod instantiates a new UpdateVerificationFlowWithCodeMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateVerificationFlowWithCodeMethod(method string) *UpdateVerificationFlowWithCodeMethod {\n\tthis := UpdateVerificationFlowWithCodeMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateVerificationFlowWithCodeMethodWithDefaults instantiates a new UpdateVerificationFlowWithCodeMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateVerificationFlowWithCodeMethodWithDefaults() *UpdateVerificationFlowWithCodeMethod {\n\tthis := UpdateVerificationFlowWithCodeMethod{}\n\treturn &this\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetCode() string {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given string and assigns it to the Code field.\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetCode(v string) {\n\to.Code = &v\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetEmail returns the Email field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetEmail() string {\n\tif o == nil || IsNil(o.Email) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Email\n}\n\n// GetEmailOk returns a tuple with the Email field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetEmailOk() (*string, bool) {\n\tif o == nil || IsNil(o.Email) {\n\t\treturn nil, false\n\t}\n\treturn o.Email, true\n}\n\n// HasEmail returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) HasEmail() bool {\n\tif o != nil && !IsNil(o.Email) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetEmail gets a reference to the given string and assigns it to the Email field.\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetEmail(v string) {\n\to.Email = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateVerificationFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateVerificationFlowWithCodeMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.Email) {\n\t\ttoSerialize[\"email\"] = o.Email\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateVerificationFlowWithCodeMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateVerificationFlowWithCodeMethod := _UpdateVerificationFlowWithCodeMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateVerificationFlowWithCodeMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateVerificationFlowWithCodeMethod(varUpdateVerificationFlowWithCodeMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"email\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateVerificationFlowWithCodeMethod struct {\n\tvalue *UpdateVerificationFlowWithCodeMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateVerificationFlowWithCodeMethod) Get() *UpdateVerificationFlowWithCodeMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateVerificationFlowWithCodeMethod) Set(val *UpdateVerificationFlowWithCodeMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateVerificationFlowWithCodeMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateVerificationFlowWithCodeMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateVerificationFlowWithCodeMethod(val *UpdateVerificationFlowWithCodeMethod) *NullableUpdateVerificationFlowWithCodeMethod {\n\treturn &NullableUpdateVerificationFlowWithCodeMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateVerificationFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateVerificationFlowWithCodeMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_update_verification_flow_with_link_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateVerificationFlowWithLinkMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateVerificationFlowWithLinkMethod{}\n\n// UpdateVerificationFlowWithLinkMethod Update Verification Flow with Link Method\ntype UpdateVerificationFlowWithLinkMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Email to Verify  Needs to be set when initiating the flow. If the email is a registered verification email, a verification link will be sent. If the email is not known, a email with details on what happened will be sent instead.  format: email\n\tEmail string `json:\"email\"`\n\t// Method is the method that should be used for this verification flow  Allowed values are `link` and `code` link VerificationStrategyLink code VerificationStrategyCode\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateVerificationFlowWithLinkMethod UpdateVerificationFlowWithLinkMethod\n\n// NewUpdateVerificationFlowWithLinkMethod instantiates a new UpdateVerificationFlowWithLinkMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateVerificationFlowWithLinkMethod(email string, method string) *UpdateVerificationFlowWithLinkMethod {\n\tthis := UpdateVerificationFlowWithLinkMethod{}\n\tthis.Email = email\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateVerificationFlowWithLinkMethodWithDefaults instantiates a new UpdateVerificationFlowWithLinkMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateVerificationFlowWithLinkMethodWithDefaults() *UpdateVerificationFlowWithLinkMethod {\n\tthis := UpdateVerificationFlowWithLinkMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateVerificationFlowWithLinkMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetEmail returns the Email field value\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetEmail() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Email\n}\n\n// GetEmailOk returns a tuple with the Email field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetEmailOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Email, true\n}\n\n// SetEmail sets field value\nfunc (o *UpdateVerificationFlowWithLinkMethod) SetEmail(v string) {\n\to.Email = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateVerificationFlowWithLinkMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateVerificationFlowWithLinkMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateVerificationFlowWithLinkMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateVerificationFlowWithLinkMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"email\"] = o.Email\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateVerificationFlowWithLinkMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"email\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateVerificationFlowWithLinkMethod := _UpdateVerificationFlowWithLinkMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateVerificationFlowWithLinkMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateVerificationFlowWithLinkMethod(varUpdateVerificationFlowWithLinkMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"email\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateVerificationFlowWithLinkMethod struct {\n\tvalue *UpdateVerificationFlowWithLinkMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateVerificationFlowWithLinkMethod) Get() *UpdateVerificationFlowWithLinkMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateVerificationFlowWithLinkMethod) Set(val *UpdateVerificationFlowWithLinkMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateVerificationFlowWithLinkMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateVerificationFlowWithLinkMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateVerificationFlowWithLinkMethod(val *UpdateVerificationFlowWithLinkMethod) *NullableUpdateVerificationFlowWithLinkMethod {\n\treturn &NullableUpdateVerificationFlowWithLinkMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateVerificationFlowWithLinkMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateVerificationFlowWithLinkMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_verifiable_identity_address.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the VerifiableIdentityAddress type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &VerifiableIdentityAddress{}\n\n// VerifiableIdentityAddress VerifiableAddress is an identity's verifiable address\ntype VerifiableIdentityAddress struct {\n\t// When this entry was created\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// The ID\n\tId *string `json:\"id,omitempty\"`\n\t// VerifiableAddressStatus must not exceed 16 characters as that is the limitation in the SQL Schema\n\tStatus string `json:\"status\"`\n\t// When this entry was last updated\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\"`\n\t// The address value  example foo@user.com\n\tValue string `json:\"value\"`\n\t// Indicates if the address has already been verified\n\tVerified   bool       `json:\"verified\"`\n\tVerifiedAt *time.Time `json:\"verified_at,omitempty\"`\n\t// The delivery method\n\tVia                  string `json:\"via\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _VerifiableIdentityAddress VerifiableIdentityAddress\n\n// NewVerifiableIdentityAddress instantiates a new VerifiableIdentityAddress object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewVerifiableIdentityAddress(status string, value string, verified bool, via string) *VerifiableIdentityAddress {\n\tthis := VerifiableIdentityAddress{}\n\tthis.Status = status\n\tthis.Value = value\n\tthis.Verified = verified\n\tthis.Via = via\n\treturn &this\n}\n\n// NewVerifiableIdentityAddressWithDefaults instantiates a new VerifiableIdentityAddress object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewVerifiableIdentityAddressWithDefaults() *VerifiableIdentityAddress {\n\tthis := VerifiableIdentityAddress{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *VerifiableIdentityAddress) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *VerifiableIdentityAddress) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *VerifiableIdentityAddress) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetId returns the Id field value if set, zero value otherwise.\nfunc (o *VerifiableIdentityAddress) GetId() string {\n\tif o == nil || IsNil(o.Id) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.Id) {\n\t\treturn nil, false\n\t}\n\treturn o.Id, true\n}\n\n// HasId returns a boolean if a field has been set.\nfunc (o *VerifiableIdentityAddress) HasId() bool {\n\tif o != nil && !IsNil(o.Id) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetId gets a reference to the given string and assigns it to the Id field.\nfunc (o *VerifiableIdentityAddress) SetId(v string) {\n\to.Id = &v\n}\n\n// GetStatus returns the Status field value\nfunc (o *VerifiableIdentityAddress) GetStatus() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetStatusOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Status, true\n}\n\n// SetStatus sets field value\nfunc (o *VerifiableIdentityAddress) SetStatus(v string) {\n\to.Status = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *VerifiableIdentityAddress) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *VerifiableIdentityAddress) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *VerifiableIdentityAddress) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetValue returns the Value field value\nfunc (o *VerifiableIdentityAddress) GetValue() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Value\n}\n\n// GetValueOk returns a tuple with the Value field value\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetValueOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Value, true\n}\n\n// SetValue sets field value\nfunc (o *VerifiableIdentityAddress) SetValue(v string) {\n\to.Value = v\n}\n\n// GetVerified returns the Verified field value\nfunc (o *VerifiableIdentityAddress) GetVerified() bool {\n\tif o == nil {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\n\treturn o.Verified\n}\n\n// GetVerifiedOk returns a tuple with the Verified field value\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetVerifiedOk() (*bool, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Verified, true\n}\n\n// SetVerified sets field value\nfunc (o *VerifiableIdentityAddress) SetVerified(v bool) {\n\to.Verified = v\n}\n\n// GetVerifiedAt returns the VerifiedAt field value if set, zero value otherwise.\nfunc (o *VerifiableIdentityAddress) GetVerifiedAt() time.Time {\n\tif o == nil || IsNil(o.VerifiedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.VerifiedAt\n}\n\n// GetVerifiedAtOk returns a tuple with the VerifiedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetVerifiedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.VerifiedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.VerifiedAt, true\n}\n\n// HasVerifiedAt returns a boolean if a field has been set.\nfunc (o *VerifiableIdentityAddress) HasVerifiedAt() bool {\n\tif o != nil && !IsNil(o.VerifiedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVerifiedAt gets a reference to the given time.Time and assigns it to the VerifiedAt field.\nfunc (o *VerifiableIdentityAddress) SetVerifiedAt(v time.Time) {\n\to.VerifiedAt = &v\n}\n\n// GetVia returns the Via field value\nfunc (o *VerifiableIdentityAddress) GetVia() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Via\n}\n\n// GetViaOk returns a tuple with the Via field value\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetViaOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Via, true\n}\n\n// SetVia sets field value\nfunc (o *VerifiableIdentityAddress) SetVia(v string) {\n\to.Via = v\n}\n\nfunc (o VerifiableIdentityAddress) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o VerifiableIdentityAddress) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Id) {\n\t\ttoSerialize[\"id\"] = o.Id\n\t}\n\ttoSerialize[\"status\"] = o.Status\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\ttoSerialize[\"value\"] = o.Value\n\ttoSerialize[\"verified\"] = o.Verified\n\tif !IsNil(o.VerifiedAt) {\n\t\ttoSerialize[\"verified_at\"] = o.VerifiedAt\n\t}\n\ttoSerialize[\"via\"] = o.Via\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *VerifiableIdentityAddress) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"status\",\n\t\t\"value\",\n\t\t\"verified\",\n\t\t\"via\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarVerifiableIdentityAddress := _VerifiableIdentityAddress{}\n\n\terr = json.Unmarshal(data, &varVerifiableIdentityAddress)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = VerifiableIdentityAddress(varVerifiableIdentityAddress)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"status\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"value\")\n\t\tdelete(additionalProperties, \"verified\")\n\t\tdelete(additionalProperties, \"verified_at\")\n\t\tdelete(additionalProperties, \"via\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableVerifiableIdentityAddress struct {\n\tvalue *VerifiableIdentityAddress\n\tisSet bool\n}\n\nfunc (v NullableVerifiableIdentityAddress) Get() *VerifiableIdentityAddress {\n\treturn v.value\n}\n\nfunc (v *NullableVerifiableIdentityAddress) Set(val *VerifiableIdentityAddress) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableVerifiableIdentityAddress) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableVerifiableIdentityAddress) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableVerifiableIdentityAddress(val *VerifiableIdentityAddress) *NullableVerifiableIdentityAddress {\n\treturn &NullableVerifiableIdentityAddress{value: val, isSet: true}\n}\n\nfunc (v NullableVerifiableIdentityAddress) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableVerifiableIdentityAddress) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_verification_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the VerificationFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &VerificationFlow{}\n\n// VerificationFlow Used to verify an out-of-band communication channel such as an email address or a phone number.  For more information head over to: https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation\ntype VerificationFlow struct {\n\t// Active, if set, contains the registration method that is being used. It is initially not set.\n\tActive *string `json:\"active,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the request expires. If the user still wishes to verify the address, a new request has to be initiated.\n\tExpiresAt *time.Time `json:\"expires_at,omitempty\"`\n\t// ID represents the request's unique ID. When performing the verification flow, this represents the id in the verify ui's query parameter: http://<selfservice.flows.verification.ui_url>?request=<id>  type: string format: uuid\n\tId string `json:\"id\"`\n\t// IssuedAt is the time (UTC) when the request occurred.\n\tIssuedAt *time.Time `json:\"issued_at,omitempty\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl *string `json:\"request_url,omitempty\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// State represents the state of this request:  choose_method: ask the user to choose a method (e.g. verify your email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the verification challenge was passed.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the verification flow to hooks and email templates\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType                 string      `json:\"type\"`\n\tUi                   UiContainer `json:\"ui\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _VerificationFlow VerificationFlow\n\n// NewVerificationFlow instantiates a new VerificationFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewVerificationFlow(id string, state interface{}, type_ string, ui UiContainer) *VerificationFlow {\n\tthis := VerificationFlow{}\n\tthis.Id = id\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewVerificationFlowWithDefaults instantiates a new VerificationFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewVerificationFlowWithDefaults() *VerificationFlow {\n\tthis := VerificationFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *VerificationFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetExpiresAt returns the ExpiresAt field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetExpiresAt() time.Time {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresAt, true\n}\n\n// HasExpiresAt returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasExpiresAt() bool {\n\tif o != nil && !IsNil(o.ExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresAt gets a reference to the given time.Time and assigns it to the ExpiresAt field.\nfunc (o *VerificationFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = &v\n}\n\n// GetId returns the Id field value\nfunc (o *VerificationFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *VerificationFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIssuedAt returns the IssuedAt field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetIssuedAt() time.Time {\n\tif o == nil || IsNil(o.IssuedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.IssuedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.IssuedAt, true\n}\n\n// HasIssuedAt returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasIssuedAt() bool {\n\tif o != nil && !IsNil(o.IssuedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIssuedAt gets a reference to the given time.Time and assigns it to the IssuedAt field.\nfunc (o *VerificationFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = &v\n}\n\n// GetRequestUrl returns the RequestUrl field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetRequestUrl() string {\n\tif o == nil || IsNil(o.RequestUrl) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.RequestUrl) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestUrl, true\n}\n\n// HasRequestUrl returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasRequestUrl() bool {\n\tif o != nil && !IsNil(o.RequestUrl) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestUrl gets a reference to the given string and assigns it to the RequestUrl field.\nfunc (o *VerificationFlow) SetRequestUrl(v string) {\n\to.RequestUrl = &v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *VerificationFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *VerificationFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *VerificationFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *VerificationFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *VerificationFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *VerificationFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *VerificationFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *VerificationFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *VerificationFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\nfunc (o VerificationFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o VerificationFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.ExpiresAt) {\n\t\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.IssuedAt) {\n\t\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\t}\n\tif !IsNil(o.RequestUrl) {\n\t\ttoSerialize[\"request_url\"] = o.RequestUrl\n\t}\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *VerificationFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarVerificationFlow := _VerificationFlow{}\n\n\terr = json.Unmarshal(data, &varVerificationFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = VerificationFlow(varVerificationFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableVerificationFlow struct {\n\tvalue *VerificationFlow\n\tisSet bool\n}\n\nfunc (v NullableVerificationFlow) Get() *VerificationFlow {\n\treturn v.value\n}\n\nfunc (v *NullableVerificationFlow) Set(val *VerificationFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableVerificationFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableVerificationFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableVerificationFlow(val *VerificationFlow) *NullableVerificationFlow {\n\treturn &NullableVerificationFlow{value: val, isSet: true}\n}\n\nfunc (v NullableVerificationFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableVerificationFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_verification_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// VerificationFlowState The experimental state represents the state of a verification flow. This field is EXPERIMENTAL and subject to change!\ntype VerificationFlowState string\n\n// List of verificationFlowState\nconst (\n\tVERIFICATIONFLOWSTATE_CHOOSE_METHOD    VerificationFlowState = \"choose_method\"\n\tVERIFICATIONFLOWSTATE_SENT_EMAIL       VerificationFlowState = \"sent_email\"\n\tVERIFICATIONFLOWSTATE_PASSED_CHALLENGE VerificationFlowState = \"passed_challenge\"\n)\n\n// All allowed values of VerificationFlowState enum\nvar AllowedVerificationFlowStateEnumValues = []VerificationFlowState{\n\t\"choose_method\",\n\t\"sent_email\",\n\t\"passed_challenge\",\n}\n\nfunc (v *VerificationFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := VerificationFlowState(value)\n\tfor _, existing := range AllowedVerificationFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid VerificationFlowState\", value)\n}\n\n// NewVerificationFlowStateFromValue returns a pointer to a valid VerificationFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewVerificationFlowStateFromValue(v string) (*VerificationFlowState, error) {\n\tev := VerificationFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for VerificationFlowState: valid values are %v\", v, AllowedVerificationFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v VerificationFlowState) IsValid() bool {\n\tfor _, existing := range AllowedVerificationFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to verificationFlowState value\nfunc (v VerificationFlowState) Ptr() *VerificationFlowState {\n\treturn &v\n}\n\ntype NullableVerificationFlowState struct {\n\tvalue *VerificationFlowState\n\tisSet bool\n}\n\nfunc (v NullableVerificationFlowState) Get() *VerificationFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableVerificationFlowState) Set(val *VerificationFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableVerificationFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableVerificationFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableVerificationFlowState(val *VerificationFlowState) *NullableVerificationFlowState {\n\treturn &NullableVerificationFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableVerificationFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableVerificationFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/model_version.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the Version type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Version{}\n\n// Version struct for Version\ntype Version struct {\n\t// Version is the service's version.\n\tVersion              *string `json:\"version,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Version Version\n\n// NewVersion instantiates a new Version object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewVersion() *Version {\n\tthis := Version{}\n\treturn &this\n}\n\n// NewVersionWithDefaults instantiates a new Version object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewVersionWithDefaults() *Version {\n\tthis := Version{}\n\treturn &this\n}\n\n// GetVersion returns the Version field value if set, zero value otherwise.\nfunc (o *Version) GetVersion() string {\n\tif o == nil || IsNil(o.Version) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Version\n}\n\n// GetVersionOk returns a tuple with the Version field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Version) GetVersionOk() (*string, bool) {\n\tif o == nil || IsNil(o.Version) {\n\t\treturn nil, false\n\t}\n\treturn o.Version, true\n}\n\n// HasVersion returns a boolean if a field has been set.\nfunc (o *Version) HasVersion() bool {\n\tif o != nil && !IsNil(o.Version) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVersion gets a reference to the given string and assigns it to the Version field.\nfunc (o *Version) SetVersion(v string) {\n\to.Version = &v\n}\n\nfunc (o Version) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Version) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Version) {\n\t\ttoSerialize[\"version\"] = o.Version\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Version) UnmarshalJSON(data []byte) (err error) {\n\tvarVersion := _Version{}\n\n\terr = json.Unmarshal(data, &varVersion)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Version(varVersion)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"version\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableVersion struct {\n\tvalue *Version\n\tisSet bool\n}\n\nfunc (v NullableVersion) Get() *Version {\n\treturn v.value\n}\n\nfunc (v *NullableVersion) Set(val *Version) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableVersion) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableVersion) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableVersion(val *Version) *NullableVersion {\n\treturn &NullableVersion{value: val, isSet: true}\n}\n\nfunc (v NullableVersion) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableVersion) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/client-go/response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"net/http\"\n)\n\n// APIResponse stores the API response returned by the server.\ntype APIResponse struct {\n\t*http.Response `json:\"-\"`\n\tMessage        string `json:\"message,omitempty\"`\n\t// Operation is the name of the OpenAPI operation.\n\tOperation string `json:\"operation,omitempty\"`\n\t// RequestURL is the request URL. This value is always available, even if the\n\t// embedded *http.Response is nil.\n\tRequestURL string `json:\"url,omitempty\"`\n\t// Method is the HTTP method used for the request.  This value is always\n\t// available, even if the embedded *http.Response is nil.\n\tMethod string `json:\"method,omitempty\"`\n\t// Payload holds the contents of the response body (which may be nil or empty).\n\t// This is provided here as the raw response.Body() reader will have already\n\t// been drained.\n\tPayload []byte `json:\"-\"`\n}\n\n// NewAPIResponse returns a new APIResponse object.\nfunc NewAPIResponse(r *http.Response) *APIResponse {\n\n\tresponse := &APIResponse{Response: r}\n\treturn response\n}\n\n// NewAPIResponseWithError returns a new APIResponse object with the provided error message.\nfunc NewAPIResponseWithError(errorMessage string) *APIResponse {\n\n\tresponse := &APIResponse{Message: errorMessage}\n\treturn response\n}\n"
  },
  {
    "path": "pkg/client-go/utils.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"time\"\n)\n\n// PtrBool is a helper routine that returns a pointer to given boolean value.\nfunc PtrBool(v bool) *bool { return &v }\n\n// PtrInt is a helper routine that returns a pointer to given integer value.\nfunc PtrInt(v int) *int { return &v }\n\n// PtrInt32 is a helper routine that returns a pointer to given integer value.\nfunc PtrInt32(v int32) *int32 { return &v }\n\n// PtrInt64 is a helper routine that returns a pointer to given integer value.\nfunc PtrInt64(v int64) *int64 { return &v }\n\n// PtrFloat32 is a helper routine that returns a pointer to given float value.\nfunc PtrFloat32(v float32) *float32 { return &v }\n\n// PtrFloat64 is a helper routine that returns a pointer to given float value.\nfunc PtrFloat64(v float64) *float64 { return &v }\n\n// PtrString is a helper routine that returns a pointer to given string value.\nfunc PtrString(v string) *string { return &v }\n\n// PtrTime is helper routine that returns a pointer to given Time value.\nfunc PtrTime(v time.Time) *time.Time { return &v }\n\ntype NullableBool struct {\n\tvalue *bool\n\tisSet bool\n}\n\nfunc (v NullableBool) Get() *bool {\n\treturn v.value\n}\n\nfunc (v *NullableBool) Set(val *bool) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableBool) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableBool) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableBool(val *bool) *NullableBool {\n\treturn &NullableBool{value: val, isSet: true}\n}\n\nfunc (v NullableBool) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableBool) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableInt struct {\n\tvalue *int\n\tisSet bool\n}\n\nfunc (v NullableInt) Get() *int {\n\treturn v.value\n}\n\nfunc (v *NullableInt) Set(val *int) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableInt) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableInt) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableInt(val *int) *NullableInt {\n\treturn &NullableInt{value: val, isSet: true}\n}\n\nfunc (v NullableInt) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableInt) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableInt32 struct {\n\tvalue *int32\n\tisSet bool\n}\n\nfunc (v NullableInt32) Get() *int32 {\n\treturn v.value\n}\n\nfunc (v *NullableInt32) Set(val *int32) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableInt32) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableInt32) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableInt32(val *int32) *NullableInt32 {\n\treturn &NullableInt32{value: val, isSet: true}\n}\n\nfunc (v NullableInt32) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableInt32) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableInt64 struct {\n\tvalue *int64\n\tisSet bool\n}\n\nfunc (v NullableInt64) Get() *int64 {\n\treturn v.value\n}\n\nfunc (v *NullableInt64) Set(val *int64) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableInt64) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableInt64) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableInt64(val *int64) *NullableInt64 {\n\treturn &NullableInt64{value: val, isSet: true}\n}\n\nfunc (v NullableInt64) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableInt64) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableFloat32 struct {\n\tvalue *float32\n\tisSet bool\n}\n\nfunc (v NullableFloat32) Get() *float32 {\n\treturn v.value\n}\n\nfunc (v *NullableFloat32) Set(val *float32) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableFloat32) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableFloat32) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableFloat32(val *float32) *NullableFloat32 {\n\treturn &NullableFloat32{value: val, isSet: true}\n}\n\nfunc (v NullableFloat32) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableFloat32) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableFloat64 struct {\n\tvalue *float64\n\tisSet bool\n}\n\nfunc (v NullableFloat64) Get() *float64 {\n\treturn v.value\n}\n\nfunc (v *NullableFloat64) Set(val *float64) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableFloat64) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableFloat64) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableFloat64(val *float64) *NullableFloat64 {\n\treturn &NullableFloat64{value: val, isSet: true}\n}\n\nfunc (v NullableFloat64) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableFloat64) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableString struct {\n\tvalue *string\n\tisSet bool\n}\n\nfunc (v NullableString) Get() *string {\n\treturn v.value\n}\n\nfunc (v *NullableString) Set(val *string) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableString) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableString) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableString(val *string) *NullableString {\n\treturn &NullableString{value: val, isSet: true}\n}\n\nfunc (v NullableString) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableString) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableTime struct {\n\tvalue *time.Time\n\tisSet bool\n}\n\nfunc (v NullableTime) Get() *time.Time {\n\treturn v.value\n}\n\nfunc (v *NullableTime) Set(val *time.Time) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableTime) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableTime) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableTime(val *time.Time) *NullableTime {\n\treturn &NullableTime{value: val, isSet: true}\n}\n\nfunc (v NullableTime) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableTime) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\n// IsNil checks if an input is nil\nfunc IsNil(i interface{}) bool {\n\tif i == nil {\n\t\treturn true\n\t}\n\tswitch reflect.TypeOf(i).Kind() {\n\tcase reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:\n\t\treturn reflect.ValueOf(i).IsNil()\n\tcase reflect.Array:\n\t\treturn reflect.ValueOf(i).IsZero()\n\t}\n\treturn false\n}\n\ntype MappedNullable interface {\n\tToMap() (map[string]interface{}, error)\n}\n\n// A wrapper for strict JSON decoding\nfunc newStrictDecoder(data []byte) *json.Decoder {\n\tdec := json.NewDecoder(bytes.NewBuffer(data))\n\tdec.DisallowUnknownFields()\n\treturn dec\n}\n\n// Prevent trying to import \"fmt\"\nfunc reportError(format string, a ...interface{}) error {\n\treturn fmt.Errorf(format, a...)\n}\n"
  },
  {
    "path": "pkg/clihelpers/helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage clihelpers\n\nconst (\n\tWarningJQIsComplicated = \"We have to admit, this is not easy if you don't speak jq fluently. What about opening an issue and telling us what predefined selectors you want to have? https://github.com/ory/kratos/issues/new/choose\"\n)\n"
  },
  {
    "path": "pkg/driver.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage pkg\n\nimport (\n\t\"cmp\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/embedx\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/randx\"\n)\n\nfunc NewConfigurationWithDefaults(t testing.TB, opts ...configx.OptionModifier) *config.Config {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {\n\t\tw.WriteHeader(http.StatusOK)\n\t\t_, _ = w.Write([]byte(`OK`))\n\t}))\n\tt.Cleanup(ts.Close)\n\n\tconfigOpts := append([]configx.OptionModifier{\n\t\tconfigx.WithValues(map[string]interface{}{\n\t\t\t\"log.level\":                                      \"error\",\n\t\t\tconfig.ViperKeyDSN:                               dbal.NewSQLiteTestDatabase(t),\n\t\t\tconfig.ViperKeyHasherArgon2ConfigMemory:          16384,\n\t\t\tconfig.ViperKeyHasherArgon2ConfigIterations:      1,\n\t\t\tconfig.ViperKeyHasherArgon2ConfigParallelism:     1,\n\t\t\tconfig.ViperKeyHasherArgon2ConfigSaltLength:      16,\n\t\t\tconfig.ViperKeyHasherBcryptCost:                  4,\n\t\t\tconfig.ViperKeyHasherArgon2ConfigKeyLength:       16,\n\t\t\tconfig.ViperKeyCourierSMTPURL:                    \"smtp://foo:bar@baz.com/\",\n\t\t\tconfig.ViperKeySelfServiceBrowserDefaultReturnTo: ts.URL,\n\t\t\tconfig.ViperKeySecretsCipher:                     []string{\"secret-thirty-two-character-long\"},\n\t\t\tconfig.ViperKeySecretsPagination:                 []string{uuid.Must(uuid.NewV4()).String()},\n\t\t\tconfig.ViperKeySelfServiceLoginFlowStyle:         \"unified\",\n\t\t}),\n\t\tconfigx.SkipValidation(),\n\t\tconfigx.DisableEnvLoading(),\n\t}, opts...)\n\treturn config.MustNew(t, logrusx.New(\"\", \"\"),\n\t\tcontextx.NewTestConfigProvider(embedx.ConfigSchema, configOpts...),\n\t\tconfigOpts...,\n\t)\n}\n\n// NewFastRegistryWithMocks returns a registry with several mocks and an SQLite in memory database that make testing\n// easier and way faster. This suite does not work for e2e or advanced integration tests.\nfunc NewFastRegistryWithMocks(t *testing.T, opts ...configx.OptionModifier) (*config.Config, *driver.RegistryDefault) {\n\tconf, reg := NewRegistryDefaultWithDSN(t, \"\", opts...)\n\treg.WithCSRFTokenGenerator(nosurfx.FakeCSRFTokenGenerator)\n\treg.WithCSRFHandler(nosurfx.NewFakeCSRFHandler(reg))\n\treg.WithHooks(map[string]func(config.SelfServiceHook) interface{}{\n\t\t\"err\": func(c config.SelfServiceHook) interface{} {\n\t\t\treturn &hook.Error{Config: c.Config}\n\t\t},\n\t})\n\n\treturn conf, reg\n}\n\n// NewRegistryDefaultWithDSN returns a more standard registry without mocks. Good for e2e and advanced integration testing!\nfunc NewRegistryDefaultWithDSN(t testing.TB, dsn string, opts ...configx.OptionModifier) (*config.Config, *driver.RegistryDefault) {\n\tc := NewConfigurationWithDefaults(t, append([]configx.OptionModifier{configx.WithValues(map[string]interface{}{\n\t\tconfig.ViperKeyDSN:             cmp.Or(dsn, dbal.NewSQLiteTestDatabase(t)),\n\t\t\"dev\":                          true,\n\t\tconfig.ViperKeySecretsCipher:   []string{randx.MustString(32, randx.AlphaNum)},\n\t\tconfig.ViperKeySecretsCookie:   []string{randx.MustString(32, randx.AlphaNum)},\n\t\tconfig.ViperKeySecretsDefault:  []string{randx.MustString(32, randx.AlphaNum)},\n\t\tconfig.ViperKeyCipherAlgorithm: \"xchacha20-poly1305\",\n\t})}, opts...)...)\n\treg, err := driver.NewRegistryFromDSN(t.Context(), c, logrusx.New(\"\", \"\", logrusx.ForceLevel(logrus.ErrorLevel)))\n\trequire.NoError(t, err)\n\n\treg.SetJSONNetVMProvider(jsonnetsecure.NewTestProvider(t))\n\n\trequire.NoError(t, reg.Init(t.Context(), contextx.NewTestConfigProvider(embedx.ConfigSchema), driver.SkipNetworkInit, driver.WithDisabledMigrationLogging()))\n\trequire.NoError(t, reg.Persister().MigrateUp(t.Context())) // always migrate up\n\n\tactual, err := reg.Persister().DetermineNetwork(t.Context())\n\trequire.NoError(t, err)\n\treg.SetPersister(reg.Persister().WithNetworkID(actual.ID))\n\n\trequire.EqualValues(t, reg.Persister().NetworkID(t.Context()), actual.ID)\n\trequire.NotEqual(t, uuid.Nil, reg.Persister().NetworkID(t.Context()))\n\treg.Persister()\n\n\treturn c, reg\n}\n\nfunc NewVeryFastRegistryWithoutDB(t *testing.T) (*config.Config, *driver.RegistryDefault) {\n\tc := NewConfigurationWithDefaults(t)\n\treg, err := driver.NewRegistryFromDSN(t.Context(), c, logrusx.New(\"\", \"\"))\n\trequire.NoError(t, err)\n\treturn c, reg\n}\n"
  },
  {
    "path": "pkg/httpclient/.gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n*.test\n*.prof\n"
  },
  {
    "path": "pkg/httpclient/.openapi-generator/FILES",
    "content": ".gitignore\n.openapi-generator-ignore\n.travis.yml\nREADME.md\napi/openapi.yaml\napi_courier.go\napi_frontend.go\napi_identity.go\napi_metadata.go\nclient.go\nconfiguration.go\ndocs/AuthenticatorAssuranceLevel.md\ndocs/BatchPatchIdentitiesResponse.md\ndocs/ConsistencyRequestParameters.md\ndocs/ContinueWith.md\ndocs/ContinueWithRecoveryUi.md\ndocs/ContinueWithRecoveryUiFlow.md\ndocs/ContinueWithRedirectBrowserTo.md\ndocs/ContinueWithSetOrySessionToken.md\ndocs/ContinueWithSettingsUi.md\ndocs/ContinueWithSettingsUiFlow.md\ndocs/ContinueWithVerificationUi.md\ndocs/ContinueWithVerificationUiFlow.md\ndocs/CourierAPI.md\ndocs/CourierMessageStatus.md\ndocs/CourierMessageType.md\ndocs/CreateFedcmFlowResponse.md\ndocs/CreateIdentityBody.md\ndocs/CreateRecoveryCodeForIdentityBody.md\ndocs/CreateRecoveryLinkForIdentityBody.md\ndocs/DeleteMySessionsCount.md\ndocs/ErrorAuthenticatorAssuranceLevelNotSatisfied.md\ndocs/ErrorBrowserLocationChangeRequired.md\ndocs/ErrorFlowReplaced.md\ndocs/ErrorGeneric.md\ndocs/FlowError.md\ndocs/FrontendAPI.md\ndocs/GenericError.md\ndocs/GetVersion200Response.md\ndocs/HealthNotReadyStatus.md\ndocs/HealthStatus.md\ndocs/Identity.md\ndocs/IdentityAPI.md\ndocs/IdentityCredentials.md\ndocs/IdentityCredentialsCode.md\ndocs/IdentityCredentialsCodeAddress.md\ndocs/IdentityCredentialsOidc.md\ndocs/IdentityCredentialsOidcProvider.md\ndocs/IdentityCredentialsPassword.md\ndocs/IdentityPatch.md\ndocs/IdentityPatchResponse.md\ndocs/IdentitySchemaContainer.md\ndocs/IdentityWithCredentials.md\ndocs/IdentityWithCredentialsOidc.md\ndocs/IdentityWithCredentialsOidcConfig.md\ndocs/IdentityWithCredentialsOidcConfigProvider.md\ndocs/IdentityWithCredentialsPassword.md\ndocs/IdentityWithCredentialsPasswordConfig.md\ndocs/IdentityWithCredentialsSaml.md\ndocs/IdentityWithCredentialsSamlConfig.md\ndocs/IdentityWithCredentialsSamlConfigProvider.md\ndocs/IsAlive200Response.md\ndocs/IsReady503Response.md\ndocs/JsonPatch.md\ndocs/LoginFlow.md\ndocs/LoginFlowState.md\ndocs/LogoutFlow.md\ndocs/Message.md\ndocs/MessageDispatch.md\ndocs/MetadataAPI.md\ndocs/NeedsPrivilegedSessionError.md\ndocs/OAuth2Client.md\ndocs/OAuth2ConsentRequestOpenIDConnectContext.md\ndocs/OAuth2LoginRequest.md\ndocs/PatchIdentitiesBody.md\ndocs/PerformNativeLogoutBody.md\ndocs/Provider.md\ndocs/RecoveryCodeForIdentity.md\ndocs/RecoveryFlow.md\ndocs/RecoveryFlowState.md\ndocs/RecoveryIdentityAddress.md\ndocs/RecoveryLinkForIdentity.md\ndocs/RegistrationFlow.md\ndocs/RegistrationFlowState.md\ndocs/SelfServiceFlowExpiredError.md\ndocs/Session.md\ndocs/SessionAuthenticationMethod.md\ndocs/SessionDevice.md\ndocs/SettingsFlow.md\ndocs/SettingsFlowState.md\ndocs/SuccessfulCodeExchangeResponse.md\ndocs/SuccessfulNativeLogin.md\ndocs/SuccessfulNativeRegistration.md\ndocs/TokenPagination.md\ndocs/TokenPaginationHeaders.md\ndocs/UiContainer.md\ndocs/UiNode.md\ndocs/UiNodeAnchorAttributes.md\ndocs/UiNodeAttributes.md\ndocs/UiNodeDivisionAttributes.md\ndocs/UiNodeImageAttributes.md\ndocs/UiNodeInputAttributes.md\ndocs/UiNodeMeta.md\ndocs/UiNodeScriptAttributes.md\ndocs/UiNodeTextAttributes.md\ndocs/UiText.md\ndocs/UpdateFedcmFlowBody.md\ndocs/UpdateIdentityBody.md\ndocs/UpdateLoginFlowBody.md\ndocs/UpdateLoginFlowWithCodeMethod.md\ndocs/UpdateLoginFlowWithIdentifierFirstMethod.md\ndocs/UpdateLoginFlowWithLookupSecretMethod.md\ndocs/UpdateLoginFlowWithOidcMethod.md\ndocs/UpdateLoginFlowWithPasskeyMethod.md\ndocs/UpdateLoginFlowWithPasswordMethod.md\ndocs/UpdateLoginFlowWithSamlMethod.md\ndocs/UpdateLoginFlowWithTotpMethod.md\ndocs/UpdateLoginFlowWithWebAuthnMethod.md\ndocs/UpdateRecoveryFlowBody.md\ndocs/UpdateRecoveryFlowWithCodeMethod.md\ndocs/UpdateRecoveryFlowWithLinkMethod.md\ndocs/UpdateRegistrationFlowBody.md\ndocs/UpdateRegistrationFlowWithCodeMethod.md\ndocs/UpdateRegistrationFlowWithOidcMethod.md\ndocs/UpdateRegistrationFlowWithPasskeyMethod.md\ndocs/UpdateRegistrationFlowWithPasswordMethod.md\ndocs/UpdateRegistrationFlowWithProfileMethod.md\ndocs/UpdateRegistrationFlowWithSamlMethod.md\ndocs/UpdateRegistrationFlowWithWebAuthnMethod.md\ndocs/UpdateSettingsFlowBody.md\ndocs/UpdateSettingsFlowWithLookupMethod.md\ndocs/UpdateSettingsFlowWithOidcMethod.md\ndocs/UpdateSettingsFlowWithPasskeyMethod.md\ndocs/UpdateSettingsFlowWithPasswordMethod.md\ndocs/UpdateSettingsFlowWithProfileMethod.md\ndocs/UpdateSettingsFlowWithSamlMethod.md\ndocs/UpdateSettingsFlowWithTotpMethod.md\ndocs/UpdateSettingsFlowWithWebAuthnMethod.md\ndocs/UpdateVerificationFlowBody.md\ndocs/UpdateVerificationFlowWithCodeMethod.md\ndocs/UpdateVerificationFlowWithLinkMethod.md\ndocs/VerifiableIdentityAddress.md\ndocs/VerificationFlow.md\ndocs/VerificationFlowState.md\ndocs/Version.md\ngit_push.sh\ngo.mod\ngo.sum\nmodel_authenticator_assurance_level.go\nmodel_batch_patch_identities_response.go\nmodel_consistency_request_parameters.go\nmodel_continue_with.go\nmodel_continue_with_recovery_ui.go\nmodel_continue_with_recovery_ui_flow.go\nmodel_continue_with_redirect_browser_to.go\nmodel_continue_with_set_ory_session_token.go\nmodel_continue_with_settings_ui.go\nmodel_continue_with_settings_ui_flow.go\nmodel_continue_with_verification_ui.go\nmodel_continue_with_verification_ui_flow.go\nmodel_courier_message_status.go\nmodel_courier_message_type.go\nmodel_create_fedcm_flow_response.go\nmodel_create_identity_body.go\nmodel_create_recovery_code_for_identity_body.go\nmodel_create_recovery_link_for_identity_body.go\nmodel_delete_my_sessions_count.go\nmodel_error_authenticator_assurance_level_not_satisfied.go\nmodel_error_browser_location_change_required.go\nmodel_error_flow_replaced.go\nmodel_error_generic.go\nmodel_flow_error.go\nmodel_generic_error.go\nmodel_get_version_200_response.go\nmodel_health_not_ready_status.go\nmodel_health_status.go\nmodel_identity.go\nmodel_identity_credentials.go\nmodel_identity_credentials_code.go\nmodel_identity_credentials_code_address.go\nmodel_identity_credentials_oidc.go\nmodel_identity_credentials_oidc_provider.go\nmodel_identity_credentials_password.go\nmodel_identity_patch.go\nmodel_identity_patch_response.go\nmodel_identity_schema_container.go\nmodel_identity_with_credentials.go\nmodel_identity_with_credentials_oidc.go\nmodel_identity_with_credentials_oidc_config.go\nmodel_identity_with_credentials_oidc_config_provider.go\nmodel_identity_with_credentials_password.go\nmodel_identity_with_credentials_password_config.go\nmodel_identity_with_credentials_saml.go\nmodel_identity_with_credentials_saml_config.go\nmodel_identity_with_credentials_saml_config_provider.go\nmodel_is_alive_200_response.go\nmodel_is_ready_503_response.go\nmodel_json_patch.go\nmodel_login_flow.go\nmodel_login_flow_state.go\nmodel_logout_flow.go\nmodel_message.go\nmodel_message_dispatch.go\nmodel_needs_privileged_session_error.go\nmodel_o_auth2_client.go\nmodel_o_auth2_consent_request_open_id_connect_context.go\nmodel_o_auth2_login_request.go\nmodel_patch_identities_body.go\nmodel_perform_native_logout_body.go\nmodel_provider.go\nmodel_recovery_code_for_identity.go\nmodel_recovery_flow.go\nmodel_recovery_flow_state.go\nmodel_recovery_identity_address.go\nmodel_recovery_link_for_identity.go\nmodel_registration_flow.go\nmodel_registration_flow_state.go\nmodel_self_service_flow_expired_error.go\nmodel_session.go\nmodel_session_authentication_method.go\nmodel_session_device.go\nmodel_settings_flow.go\nmodel_settings_flow_state.go\nmodel_successful_code_exchange_response.go\nmodel_successful_native_login.go\nmodel_successful_native_registration.go\nmodel_token_pagination.go\nmodel_token_pagination_headers.go\nmodel_ui_container.go\nmodel_ui_node.go\nmodel_ui_node_anchor_attributes.go\nmodel_ui_node_attributes.go\nmodel_ui_node_division_attributes.go\nmodel_ui_node_image_attributes.go\nmodel_ui_node_input_attributes.go\nmodel_ui_node_meta.go\nmodel_ui_node_script_attributes.go\nmodel_ui_node_text_attributes.go\nmodel_ui_text.go\nmodel_update_fedcm_flow_body.go\nmodel_update_identity_body.go\nmodel_update_login_flow_body.go\nmodel_update_login_flow_with_code_method.go\nmodel_update_login_flow_with_identifier_first_method.go\nmodel_update_login_flow_with_lookup_secret_method.go\nmodel_update_login_flow_with_oidc_method.go\nmodel_update_login_flow_with_passkey_method.go\nmodel_update_login_flow_with_password_method.go\nmodel_update_login_flow_with_saml_method.go\nmodel_update_login_flow_with_totp_method.go\nmodel_update_login_flow_with_web_authn_method.go\nmodel_update_recovery_flow_body.go\nmodel_update_recovery_flow_with_code_method.go\nmodel_update_recovery_flow_with_link_method.go\nmodel_update_registration_flow_body.go\nmodel_update_registration_flow_with_code_method.go\nmodel_update_registration_flow_with_oidc_method.go\nmodel_update_registration_flow_with_passkey_method.go\nmodel_update_registration_flow_with_password_method.go\nmodel_update_registration_flow_with_profile_method.go\nmodel_update_registration_flow_with_saml_method.go\nmodel_update_registration_flow_with_web_authn_method.go\nmodel_update_settings_flow_body.go\nmodel_update_settings_flow_with_lookup_method.go\nmodel_update_settings_flow_with_oidc_method.go\nmodel_update_settings_flow_with_passkey_method.go\nmodel_update_settings_flow_with_password_method.go\nmodel_update_settings_flow_with_profile_method.go\nmodel_update_settings_flow_with_saml_method.go\nmodel_update_settings_flow_with_totp_method.go\nmodel_update_settings_flow_with_web_authn_method.go\nmodel_update_verification_flow_body.go\nmodel_update_verification_flow_with_code_method.go\nmodel_update_verification_flow_with_link_method.go\nmodel_verifiable_identity_address.go\nmodel_verification_flow.go\nmodel_verification_flow_state.go\nmodel_version.go\nresponse.go\ntest/api_courier_test.go\ntest/api_frontend_test.go\ntest/api_identity_test.go\ntest/api_metadata_test.go\nutils.go\n"
  },
  {
    "path": "pkg/httpclient/.openapi-generator/VERSION",
    "content": "7.12.0\n"
  },
  {
    "path": "pkg/httpclient/.openapi-generator-ignore",
    "content": "# OpenAPI Generator Ignore\n# Generated by openapi-generator https://github.com/openapitools/openapi-generator\n\n# Use this file to prevent files from being overwritten by the generator.\n# The patterns follow closely to .gitignore or .dockerignore.\n\n# As an example, the C# client generator defines ApiClient.cs.\n# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:\n#ApiClient.cs\n\n# You can match any string of characters against a directory, file or extension with a single asterisk (*):\n#foo/*/qux\n# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux\n\n# You can recursively match patterns against a directory, file or extension with a double asterisk (**):\n#foo/**/qux\n# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux\n\n# You can also negate patterns with an exclamation (!).\n# For example, you can ignore all files in a docs folder with the file extension .md:\n#docs/*.md\n# Then explicitly reverse the ignore rule for a single file:\n#!docs/README.md\n"
  },
  {
    "path": "pkg/httpclient/.travis.yml",
    "content": "language: go\n\ninstall:\n  - go get -d -v .\n\nscript:\n  - go build -v ./\n\n"
  },
  {
    "path": "pkg/httpclient/README.md",
    "content": "# Go API client for client\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\n\n## Overview\nThis API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project.  By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client.\n\n- API version: \n- Package version: 1.0.0\n- Generator version: 7.12.0\n- Build package: org.openapitools.codegen.languages.GoClientCodegen\n\n## Installation\n\nInstall the following dependencies:\n\n```sh\ngo get github.com/stretchr/testify/assert\ngo get golang.org/x/net/context\n```\n\nPut the package under your project folder and add the following in import:\n\n```go\nimport client \"github.com/ory/client-go\"\n```\n\nTo use a proxy, set the environment variable `HTTP_PROXY`:\n\n```go\nos.Setenv(\"HTTP_PROXY\", \"http://proxy_name:proxy_port\")\n```\n\n## Configuration of Server URL\n\nDefault configuration comes with `Servers` field that contains server objects as defined in the OpenAPI specification.\n\n### Select Server Configuration\n\nFor using other server than the one defined on index 0 set context value `client.ContextServerIndex` of type `int`.\n\n```go\nctx := context.WithValue(context.Background(), client.ContextServerIndex, 1)\n```\n\n### Templated Server URL\n\nTemplated server URL is formatted using default variables from configuration or from context value `client.ContextServerVariables` of type `map[string]string`.\n\n```go\nctx := context.WithValue(context.Background(), client.ContextServerVariables, map[string]string{\n\t\"basePath\": \"v2\",\n})\n```\n\nNote, enum values are always validated and all unused variables are silently ignored.\n\n### URLs Configuration per Operation\n\nEach operation can use different server URL defined using `OperationServers` map in the `Configuration`.\nAn operation is uniquely identified by `\"{classname}Service.{nickname}\"` string.\nSimilar rules for overriding default operation server index and variables applies by using `client.ContextOperationServerIndices` and `client.ContextOperationServerVariables` context maps.\n\n```go\nctx := context.WithValue(context.Background(), client.ContextOperationServerIndices, map[string]int{\n\t\"{classname}Service.{nickname}\": 2,\n})\nctx = context.WithValue(context.Background(), client.ContextOperationServerVariables, map[string]map[string]string{\n\t\"{classname}Service.{nickname}\": {\n\t\t\"port\": \"8443\",\n\t},\n})\n```\n\n## Documentation for API Endpoints\n\nAll URIs are relative to *http://localhost*\n\nClass | Method | HTTP request | Description\n------------ | ------------- | ------------- | -------------\n*CourierAPI* | [**GetCourierMessage**](docs/CourierAPI.md#getcouriermessage) | **Get** /admin/courier/messages/{id} | Get a Message\n*CourierAPI* | [**ListCourierMessages**](docs/CourierAPI.md#listcouriermessages) | **Get** /admin/courier/messages | List Messages\n*FrontendAPI* | [**CreateBrowserLoginFlow**](docs/FrontendAPI.md#createbrowserloginflow) | **Get** /self-service/login/browser | Create Login Flow for Browsers\n*FrontendAPI* | [**CreateBrowserLogoutFlow**](docs/FrontendAPI.md#createbrowserlogoutflow) | **Get** /self-service/logout/browser | Create a Logout URL for Browsers\n*FrontendAPI* | [**CreateBrowserRecoveryFlow**](docs/FrontendAPI.md#createbrowserrecoveryflow) | **Get** /self-service/recovery/browser | Create Recovery Flow for Browsers\n*FrontendAPI* | [**CreateBrowserRegistrationFlow**](docs/FrontendAPI.md#createbrowserregistrationflow) | **Get** /self-service/registration/browser | Create Registration Flow for Browsers\n*FrontendAPI* | [**CreateBrowserSettingsFlow**](docs/FrontendAPI.md#createbrowsersettingsflow) | **Get** /self-service/settings/browser | Create Settings Flow for Browsers\n*FrontendAPI* | [**CreateBrowserVerificationFlow**](docs/FrontendAPI.md#createbrowserverificationflow) | **Get** /self-service/verification/browser | Create Verification Flow for Browser Clients\n*FrontendAPI* | [**CreateFedcmFlow**](docs/FrontendAPI.md#createfedcmflow) | **Get** /self-service/fed-cm/parameters | Get FedCM Parameters\n*FrontendAPI* | [**CreateNativeLoginFlow**](docs/FrontendAPI.md#createnativeloginflow) | **Get** /self-service/login/api | Create Login Flow for Native Apps\n*FrontendAPI* | [**CreateNativeRecoveryFlow**](docs/FrontendAPI.md#createnativerecoveryflow) | **Get** /self-service/recovery/api | Create Recovery Flow for Native Apps\n*FrontendAPI* | [**CreateNativeRegistrationFlow**](docs/FrontendAPI.md#createnativeregistrationflow) | **Get** /self-service/registration/api | Create Registration Flow for Native Apps\n*FrontendAPI* | [**CreateNativeSettingsFlow**](docs/FrontendAPI.md#createnativesettingsflow) | **Get** /self-service/settings/api | Create Settings Flow for Native Apps\n*FrontendAPI* | [**CreateNativeVerificationFlow**](docs/FrontendAPI.md#createnativeverificationflow) | **Get** /self-service/verification/api | Create Verification Flow for Native Apps\n*FrontendAPI* | [**DisableMyOtherSessions**](docs/FrontendAPI.md#disablemyothersessions) | **Delete** /sessions | Disable my other sessions\n*FrontendAPI* | [**DisableMySession**](docs/FrontendAPI.md#disablemysession) | **Delete** /sessions/{id} | Disable one of my sessions\n*FrontendAPI* | [**ExchangeSessionToken**](docs/FrontendAPI.md#exchangesessiontoken) | **Get** /sessions/token-exchange | Exchange Session Token\n*FrontendAPI* | [**GetFlowError**](docs/FrontendAPI.md#getflowerror) | **Get** /self-service/errors | Get User-Flow Errors\n*FrontendAPI* | [**GetLoginFlow**](docs/FrontendAPI.md#getloginflow) | **Get** /self-service/login/flows | Get Login Flow\n*FrontendAPI* | [**GetRecoveryFlow**](docs/FrontendAPI.md#getrecoveryflow) | **Get** /self-service/recovery/flows | Get Recovery Flow\n*FrontendAPI* | [**GetRegistrationFlow**](docs/FrontendAPI.md#getregistrationflow) | **Get** /self-service/registration/flows | Get Registration Flow\n*FrontendAPI* | [**GetSettingsFlow**](docs/FrontendAPI.md#getsettingsflow) | **Get** /self-service/settings/flows | Get Settings Flow\n*FrontendAPI* | [**GetVerificationFlow**](docs/FrontendAPI.md#getverificationflow) | **Get** /self-service/verification/flows | Get Verification Flow\n*FrontendAPI* | [**GetWebAuthnJavaScript**](docs/FrontendAPI.md#getwebauthnjavascript) | **Get** /.well-known/ory/webauthn.js | Get WebAuthn JavaScript\n*FrontendAPI* | [**ListMySessions**](docs/FrontendAPI.md#listmysessions) | **Get** /sessions | Get My Active Sessions\n*FrontendAPI* | [**PerformNativeLogout**](docs/FrontendAPI.md#performnativelogout) | **Delete** /self-service/logout/api | Perform Logout for Native Apps\n*FrontendAPI* | [**ToSession**](docs/FrontendAPI.md#tosession) | **Get** /sessions/whoami | Check Who the Current HTTP Session Belongs To\n*FrontendAPI* | [**UpdateFedcmFlow**](docs/FrontendAPI.md#updatefedcmflow) | **Post** /self-service/fed-cm/token | Submit a FedCM token\n*FrontendAPI* | [**UpdateLoginFlow**](docs/FrontendAPI.md#updateloginflow) | **Post** /self-service/login | Submit a Login Flow\n*FrontendAPI* | [**UpdateLogoutFlow**](docs/FrontendAPI.md#updatelogoutflow) | **Get** /self-service/logout | Update Logout Flow\n*FrontendAPI* | [**UpdateRecoveryFlow**](docs/FrontendAPI.md#updaterecoveryflow) | **Post** /self-service/recovery | Update Recovery Flow\n*FrontendAPI* | [**UpdateRegistrationFlow**](docs/FrontendAPI.md#updateregistrationflow) | **Post** /self-service/registration | Update Registration Flow\n*FrontendAPI* | [**UpdateSettingsFlow**](docs/FrontendAPI.md#updatesettingsflow) | **Post** /self-service/settings | Complete Settings Flow\n*FrontendAPI* | [**UpdateVerificationFlow**](docs/FrontendAPI.md#updateverificationflow) | **Post** /self-service/verification | Complete Verification Flow\n*IdentityAPI* | [**BatchPatchIdentities**](docs/IdentityAPI.md#batchpatchidentities) | **Patch** /admin/identities | Create multiple identities\n*IdentityAPI* | [**CreateIdentity**](docs/IdentityAPI.md#createidentity) | **Post** /admin/identities | Create an Identity\n*IdentityAPI* | [**CreateRecoveryCodeForIdentity**](docs/IdentityAPI.md#createrecoverycodeforidentity) | **Post** /admin/recovery/code | Create a Recovery Code\n*IdentityAPI* | [**CreateRecoveryLinkForIdentity**](docs/IdentityAPI.md#createrecoverylinkforidentity) | **Post** /admin/recovery/link | Create a Recovery Link\n*IdentityAPI* | [**DeleteIdentity**](docs/IdentityAPI.md#deleteidentity) | **Delete** /admin/identities/{id} | Delete an Identity\n*IdentityAPI* | [**DeleteIdentityCredentials**](docs/IdentityAPI.md#deleteidentitycredentials) | **Delete** /admin/identities/{id}/credentials/{type} | Delete a credential for a specific identity\n*IdentityAPI* | [**DeleteIdentitySessions**](docs/IdentityAPI.md#deleteidentitysessions) | **Delete** /admin/identities/{id}/sessions | Delete &amp; Invalidate an Identity&#39;s Sessions\n*IdentityAPI* | [**DisableSession**](docs/IdentityAPI.md#disablesession) | **Delete** /admin/sessions/{id} | Deactivate a Session\n*IdentityAPI* | [**ExtendSession**](docs/IdentityAPI.md#extendsession) | **Patch** /admin/sessions/{id}/extend | Extend a Session\n*IdentityAPI* | [**GetIdentity**](docs/IdentityAPI.md#getidentity) | **Get** /admin/identities/{id} | Get an Identity\n*IdentityAPI* | [**GetIdentityByExternalID**](docs/IdentityAPI.md#getidentitybyexternalid) | **Get** /admin/identities/by/external/{externalID} | Get an Identity by its External ID\n*IdentityAPI* | [**GetIdentitySchema**](docs/IdentityAPI.md#getidentityschema) | **Get** /schemas/{id} | Get Identity JSON Schema\n*IdentityAPI* | [**GetSession**](docs/IdentityAPI.md#getsession) | **Get** /admin/sessions/{id} | Get Session\n*IdentityAPI* | [**ListIdentities**](docs/IdentityAPI.md#listidentities) | **Get** /admin/identities | List Identities\n*IdentityAPI* | [**ListIdentitySchemas**](docs/IdentityAPI.md#listidentityschemas) | **Get** /schemas | Get all Identity Schemas\n*IdentityAPI* | [**ListIdentitySessions**](docs/IdentityAPI.md#listidentitysessions) | **Get** /admin/identities/{id}/sessions | List an Identity&#39;s Sessions\n*IdentityAPI* | [**ListSessions**](docs/IdentityAPI.md#listsessions) | **Get** /admin/sessions | List All Sessions\n*IdentityAPI* | [**PatchIdentity**](docs/IdentityAPI.md#patchidentity) | **Patch** /admin/identities/{id} | Patch an Identity\n*IdentityAPI* | [**UpdateIdentity**](docs/IdentityAPI.md#updateidentity) | **Put** /admin/identities/{id} | Update an Identity\n*MetadataAPI* | [**GetVersion**](docs/MetadataAPI.md#getversion) | **Get** /version | Return Running Software Version.\n*MetadataAPI* | [**IsAlive**](docs/MetadataAPI.md#isalive) | **Get** /health/alive | Check HTTP Server Status\n*MetadataAPI* | [**IsReady**](docs/MetadataAPI.md#isready) | **Get** /health/ready | Check HTTP Server and Database Status\n\n\n## Documentation For Models\n\n - [AuthenticatorAssuranceLevel](docs/AuthenticatorAssuranceLevel.md)\n - [BatchPatchIdentitiesResponse](docs/BatchPatchIdentitiesResponse.md)\n - [ConsistencyRequestParameters](docs/ConsistencyRequestParameters.md)\n - [ContinueWith](docs/ContinueWith.md)\n - [ContinueWithRecoveryUi](docs/ContinueWithRecoveryUi.md)\n - [ContinueWithRecoveryUiFlow](docs/ContinueWithRecoveryUiFlow.md)\n - [ContinueWithRedirectBrowserTo](docs/ContinueWithRedirectBrowserTo.md)\n - [ContinueWithSetOrySessionToken](docs/ContinueWithSetOrySessionToken.md)\n - [ContinueWithSettingsUi](docs/ContinueWithSettingsUi.md)\n - [ContinueWithSettingsUiFlow](docs/ContinueWithSettingsUiFlow.md)\n - [ContinueWithVerificationUi](docs/ContinueWithVerificationUi.md)\n - [ContinueWithVerificationUiFlow](docs/ContinueWithVerificationUiFlow.md)\n - [CourierMessageStatus](docs/CourierMessageStatus.md)\n - [CourierMessageType](docs/CourierMessageType.md)\n - [CreateFedcmFlowResponse](docs/CreateFedcmFlowResponse.md)\n - [CreateIdentityBody](docs/CreateIdentityBody.md)\n - [CreateRecoveryCodeForIdentityBody](docs/CreateRecoveryCodeForIdentityBody.md)\n - [CreateRecoveryLinkForIdentityBody](docs/CreateRecoveryLinkForIdentityBody.md)\n - [DeleteMySessionsCount](docs/DeleteMySessionsCount.md)\n - [ErrorAuthenticatorAssuranceLevelNotSatisfied](docs/ErrorAuthenticatorAssuranceLevelNotSatisfied.md)\n - [ErrorBrowserLocationChangeRequired](docs/ErrorBrowserLocationChangeRequired.md)\n - [ErrorFlowReplaced](docs/ErrorFlowReplaced.md)\n - [ErrorGeneric](docs/ErrorGeneric.md)\n - [FlowError](docs/FlowError.md)\n - [GenericError](docs/GenericError.md)\n - [GetVersion200Response](docs/GetVersion200Response.md)\n - [HealthNotReadyStatus](docs/HealthNotReadyStatus.md)\n - [HealthStatus](docs/HealthStatus.md)\n - [Identity](docs/Identity.md)\n - [IdentityCredentials](docs/IdentityCredentials.md)\n - [IdentityCredentialsCode](docs/IdentityCredentialsCode.md)\n - [IdentityCredentialsCodeAddress](docs/IdentityCredentialsCodeAddress.md)\n - [IdentityCredentialsOidc](docs/IdentityCredentialsOidc.md)\n - [IdentityCredentialsOidcProvider](docs/IdentityCredentialsOidcProvider.md)\n - [IdentityCredentialsPassword](docs/IdentityCredentialsPassword.md)\n - [IdentityPatch](docs/IdentityPatch.md)\n - [IdentityPatchResponse](docs/IdentityPatchResponse.md)\n - [IdentitySchemaContainer](docs/IdentitySchemaContainer.md)\n - [IdentityWithCredentials](docs/IdentityWithCredentials.md)\n - [IdentityWithCredentialsOidc](docs/IdentityWithCredentialsOidc.md)\n - [IdentityWithCredentialsOidcConfig](docs/IdentityWithCredentialsOidcConfig.md)\n - [IdentityWithCredentialsOidcConfigProvider](docs/IdentityWithCredentialsOidcConfigProvider.md)\n - [IdentityWithCredentialsPassword](docs/IdentityWithCredentialsPassword.md)\n - [IdentityWithCredentialsPasswordConfig](docs/IdentityWithCredentialsPasswordConfig.md)\n - [IdentityWithCredentialsSaml](docs/IdentityWithCredentialsSaml.md)\n - [IdentityWithCredentialsSamlConfig](docs/IdentityWithCredentialsSamlConfig.md)\n - [IdentityWithCredentialsSamlConfigProvider](docs/IdentityWithCredentialsSamlConfigProvider.md)\n - [IsAlive200Response](docs/IsAlive200Response.md)\n - [IsReady503Response](docs/IsReady503Response.md)\n - [JsonPatch](docs/JsonPatch.md)\n - [LoginFlow](docs/LoginFlow.md)\n - [LoginFlowState](docs/LoginFlowState.md)\n - [LogoutFlow](docs/LogoutFlow.md)\n - [Message](docs/Message.md)\n - [MessageDispatch](docs/MessageDispatch.md)\n - [NeedsPrivilegedSessionError](docs/NeedsPrivilegedSessionError.md)\n - [OAuth2Client](docs/OAuth2Client.md)\n - [OAuth2ConsentRequestOpenIDConnectContext](docs/OAuth2ConsentRequestOpenIDConnectContext.md)\n - [OAuth2LoginRequest](docs/OAuth2LoginRequest.md)\n - [PatchIdentitiesBody](docs/PatchIdentitiesBody.md)\n - [PerformNativeLogoutBody](docs/PerformNativeLogoutBody.md)\n - [Provider](docs/Provider.md)\n - [RecoveryCodeForIdentity](docs/RecoveryCodeForIdentity.md)\n - [RecoveryFlow](docs/RecoveryFlow.md)\n - [RecoveryFlowState](docs/RecoveryFlowState.md)\n - [RecoveryIdentityAddress](docs/RecoveryIdentityAddress.md)\n - [RecoveryLinkForIdentity](docs/RecoveryLinkForIdentity.md)\n - [RegistrationFlow](docs/RegistrationFlow.md)\n - [RegistrationFlowState](docs/RegistrationFlowState.md)\n - [SelfServiceFlowExpiredError](docs/SelfServiceFlowExpiredError.md)\n - [Session](docs/Session.md)\n - [SessionAuthenticationMethod](docs/SessionAuthenticationMethod.md)\n - [SessionDevice](docs/SessionDevice.md)\n - [SettingsFlow](docs/SettingsFlow.md)\n - [SettingsFlowState](docs/SettingsFlowState.md)\n - [SuccessfulCodeExchangeResponse](docs/SuccessfulCodeExchangeResponse.md)\n - [SuccessfulNativeLogin](docs/SuccessfulNativeLogin.md)\n - [SuccessfulNativeRegistration](docs/SuccessfulNativeRegistration.md)\n - [TokenPagination](docs/TokenPagination.md)\n - [TokenPaginationHeaders](docs/TokenPaginationHeaders.md)\n - [UiContainer](docs/UiContainer.md)\n - [UiNode](docs/UiNode.md)\n - [UiNodeAnchorAttributes](docs/UiNodeAnchorAttributes.md)\n - [UiNodeAttributes](docs/UiNodeAttributes.md)\n - [UiNodeDivisionAttributes](docs/UiNodeDivisionAttributes.md)\n - [UiNodeImageAttributes](docs/UiNodeImageAttributes.md)\n - [UiNodeInputAttributes](docs/UiNodeInputAttributes.md)\n - [UiNodeMeta](docs/UiNodeMeta.md)\n - [UiNodeScriptAttributes](docs/UiNodeScriptAttributes.md)\n - [UiNodeTextAttributes](docs/UiNodeTextAttributes.md)\n - [UiText](docs/UiText.md)\n - [UpdateFedcmFlowBody](docs/UpdateFedcmFlowBody.md)\n - [UpdateIdentityBody](docs/UpdateIdentityBody.md)\n - [UpdateLoginFlowBody](docs/UpdateLoginFlowBody.md)\n - [UpdateLoginFlowWithCodeMethod](docs/UpdateLoginFlowWithCodeMethod.md)\n - [UpdateLoginFlowWithIdentifierFirstMethod](docs/UpdateLoginFlowWithIdentifierFirstMethod.md)\n - [UpdateLoginFlowWithLookupSecretMethod](docs/UpdateLoginFlowWithLookupSecretMethod.md)\n - [UpdateLoginFlowWithOidcMethod](docs/UpdateLoginFlowWithOidcMethod.md)\n - [UpdateLoginFlowWithPasskeyMethod](docs/UpdateLoginFlowWithPasskeyMethod.md)\n - [UpdateLoginFlowWithPasswordMethod](docs/UpdateLoginFlowWithPasswordMethod.md)\n - [UpdateLoginFlowWithSamlMethod](docs/UpdateLoginFlowWithSamlMethod.md)\n - [UpdateLoginFlowWithTotpMethod](docs/UpdateLoginFlowWithTotpMethod.md)\n - [UpdateLoginFlowWithWebAuthnMethod](docs/UpdateLoginFlowWithWebAuthnMethod.md)\n - [UpdateRecoveryFlowBody](docs/UpdateRecoveryFlowBody.md)\n - [UpdateRecoveryFlowWithCodeMethod](docs/UpdateRecoveryFlowWithCodeMethod.md)\n - [UpdateRecoveryFlowWithLinkMethod](docs/UpdateRecoveryFlowWithLinkMethod.md)\n - [UpdateRegistrationFlowBody](docs/UpdateRegistrationFlowBody.md)\n - [UpdateRegistrationFlowWithCodeMethod](docs/UpdateRegistrationFlowWithCodeMethod.md)\n - [UpdateRegistrationFlowWithOidcMethod](docs/UpdateRegistrationFlowWithOidcMethod.md)\n - [UpdateRegistrationFlowWithPasskeyMethod](docs/UpdateRegistrationFlowWithPasskeyMethod.md)\n - [UpdateRegistrationFlowWithPasswordMethod](docs/UpdateRegistrationFlowWithPasswordMethod.md)\n - [UpdateRegistrationFlowWithProfileMethod](docs/UpdateRegistrationFlowWithProfileMethod.md)\n - [UpdateRegistrationFlowWithSamlMethod](docs/UpdateRegistrationFlowWithSamlMethod.md)\n - [UpdateRegistrationFlowWithWebAuthnMethod](docs/UpdateRegistrationFlowWithWebAuthnMethod.md)\n - [UpdateSettingsFlowBody](docs/UpdateSettingsFlowBody.md)\n - [UpdateSettingsFlowWithLookupMethod](docs/UpdateSettingsFlowWithLookupMethod.md)\n - [UpdateSettingsFlowWithOidcMethod](docs/UpdateSettingsFlowWithOidcMethod.md)\n - [UpdateSettingsFlowWithPasskeyMethod](docs/UpdateSettingsFlowWithPasskeyMethod.md)\n - [UpdateSettingsFlowWithPasswordMethod](docs/UpdateSettingsFlowWithPasswordMethod.md)\n - [UpdateSettingsFlowWithProfileMethod](docs/UpdateSettingsFlowWithProfileMethod.md)\n - [UpdateSettingsFlowWithSamlMethod](docs/UpdateSettingsFlowWithSamlMethod.md)\n - [UpdateSettingsFlowWithTotpMethod](docs/UpdateSettingsFlowWithTotpMethod.md)\n - [UpdateSettingsFlowWithWebAuthnMethod](docs/UpdateSettingsFlowWithWebAuthnMethod.md)\n - [UpdateVerificationFlowBody](docs/UpdateVerificationFlowBody.md)\n - [UpdateVerificationFlowWithCodeMethod](docs/UpdateVerificationFlowWithCodeMethod.md)\n - [UpdateVerificationFlowWithLinkMethod](docs/UpdateVerificationFlowWithLinkMethod.md)\n - [VerifiableIdentityAddress](docs/VerifiableIdentityAddress.md)\n - [VerificationFlow](docs/VerificationFlow.md)\n - [VerificationFlowState](docs/VerificationFlowState.md)\n - [Version](docs/Version.md)\n\n\n## Documentation For Authorization\n\n\nAuthentication schemes defined for the API:\n### oryAccessToken\n\n- **Type**: API key\n- **API key parameter name**: Authorization\n- **Location**: HTTP header\n\nNote, each API key must be added to a map of `map[string]APIKey` where the key is: oryAccessToken and passed in as the auth context for each request.\n\nExample\n\n```go\nauth := context.WithValue(\n\t\tcontext.Background(),\n\t\tclient.ContextAPIKeys,\n\t\tmap[string]client.APIKey{\n\t\t\t\"oryAccessToken\": {Key: \"API_KEY_STRING\"},\n\t\t},\n\t)\nr, err := client.Service.Operation(auth, args)\n```\n\n\n## Documentation for Utility Methods\n\nDue to the fact that model structure members are all pointers, this package contains\na number of utility functions to easily obtain pointers to values of basic types.\nEach of these functions takes a value of the given basic type and returns a pointer to it:\n\n* `PtrBool`\n* `PtrInt`\n* `PtrInt32`\n* `PtrInt64`\n* `PtrFloat`\n* `PtrFloat32`\n* `PtrFloat64`\n* `PtrString`\n* `PtrTime`\n\n## Author\n\noffice@ory.sh\n\n"
  },
  {
    "path": "pkg/httpclient/api_courier.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype CourierAPI interface {\n\n\t/*\n\t\tGetCourierMessage Get a Message\n\n\t\tGets a specific messages by the given ID.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id MessageID is the ID of the message.\n\t\t@return CourierAPIGetCourierMessageRequest\n\t*/\n\tGetCourierMessage(ctx context.Context, id string) CourierAPIGetCourierMessageRequest\n\n\t// GetCourierMessageExecute executes the request\n\t//  @return Message\n\tGetCourierMessageExecute(r CourierAPIGetCourierMessageRequest) (*Message, *http.Response, error)\n\n\t/*\n\t\tListCourierMessages List Messages\n\n\t\tLists all messages by given status and recipient.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return CourierAPIListCourierMessagesRequest\n\t*/\n\tListCourierMessages(ctx context.Context) CourierAPIListCourierMessagesRequest\n\n\t// ListCourierMessagesExecute executes the request\n\t//  @return []Message\n\tListCourierMessagesExecute(r CourierAPIListCourierMessagesRequest) ([]Message, *http.Response, error)\n}\n\n// CourierAPIService CourierAPI service\ntype CourierAPIService service\n\ntype CourierAPIGetCourierMessageRequest struct {\n\tctx        context.Context\n\tApiService CourierAPI\n\tid         string\n}\n\nfunc (r CourierAPIGetCourierMessageRequest) Execute() (*Message, *http.Response, error) {\n\treturn r.ApiService.GetCourierMessageExecute(r)\n}\n\n/*\nGetCourierMessage Get a Message\n\nGets a specific messages by the given ID.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id MessageID is the ID of the message.\n\t@return CourierAPIGetCourierMessageRequest\n*/\nfunc (a *CourierAPIService) GetCourierMessage(ctx context.Context, id string) CourierAPIGetCourierMessageRequest {\n\treturn CourierAPIGetCourierMessageRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Message\nfunc (a *CourierAPIService) GetCourierMessageExecute(r CourierAPIGetCourierMessageRequest) (*Message, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Message\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"CourierAPIService.GetCourierMessage\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/courier/messages/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype CourierAPIListCourierMessagesRequest struct {\n\tctx        context.Context\n\tApiService CourierAPI\n\tpageSize   *int64\n\tpageToken  *string\n\tstatus     *CourierMessageStatus\n\trecipient  *string\n}\n\n// Items per Page  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r CourierAPIListCourierMessagesRequest) PageSize(pageSize int64) CourierAPIListCourierMessagesRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r CourierAPIListCourierMessagesRequest) PageToken(pageToken string) CourierAPIListCourierMessagesRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Status filters out messages based on status. If no value is provided, it doesn&#39;t take effect on filter.\nfunc (r CourierAPIListCourierMessagesRequest) Status(status CourierMessageStatus) CourierAPIListCourierMessagesRequest {\n\tr.status = &status\n\treturn r\n}\n\n// Recipient filters out messages based on recipient. If no value is provided, it doesn&#39;t take effect on filter.\nfunc (r CourierAPIListCourierMessagesRequest) Recipient(recipient string) CourierAPIListCourierMessagesRequest {\n\tr.recipient = &recipient\n\treturn r\n}\n\nfunc (r CourierAPIListCourierMessagesRequest) Execute() ([]Message, *http.Response, error) {\n\treturn r.ApiService.ListCourierMessagesExecute(r)\n}\n\n/*\nListCourierMessages List Messages\n\nLists all messages by given status and recipient.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return CourierAPIListCourierMessagesRequest\n*/\nfunc (a *CourierAPIService) ListCourierMessages(ctx context.Context) CourierAPIListCourierMessagesRequest {\n\treturn CourierAPIListCourierMessagesRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Message\nfunc (a *CourierAPIService) ListCourierMessagesExecute(r CourierAPIListCourierMessagesRequest) ([]Message, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Message\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"CourierAPIService.ListCourierMessages\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/courier/messages\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\tif r.status != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"status\", r.status, \"form\", \"\")\n\t}\n\tif r.recipient != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"recipient\", r.recipient, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n"
  },
  {
    "path": "pkg/httpclient/api_frontend.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype FrontendAPI interface {\n\n\t/*\n\t\t\tCreateBrowserLoginFlow Create Login Flow for Browsers\n\n\t\t\tThis endpoint initializes a browser-based user login flow. This endpoint will set the appropriate\n\t\tcookies and anti-CSRF measures required for browser-based flows.\n\n\t\tIf this endpoint is opened as a link in the browser, it will be redirected to\n\t\t`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\n\t\texists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\n\t\t`?refresh=true` was set.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\n\t\tThe optional query parameter login_challenge is set when using Kratos with\n\t\tHydra in an OAuth2 flow. See the oauth2_provider.url configuration\n\t\toption.\n\n\t\tThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserLoginFlowRequest\n\t*/\n\tCreateBrowserLoginFlow(ctx context.Context) FrontendAPICreateBrowserLoginFlowRequest\n\n\t// CreateBrowserLoginFlowExecute executes the request\n\t//  @return LoginFlow\n\tCreateBrowserLoginFlowExecute(r FrontendAPICreateBrowserLoginFlowRequest) (*LoginFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserLogoutFlow Create a Logout URL for Browsers\n\n\t\t\tThis endpoint initializes a browser-based user logout flow and a URL which can be used to log out the user.\n\n\t\tThis endpoint is NOT INTENDED for API clients and only works\n\t\twith browsers (Chrome, Firefox, ...). For API clients you can\n\t\tcall the `/self-service/logout/api` URL directly with the Ory Session Token.\n\n\t\tThe URL is only valid for the currently signed in user. If no user is signed in, this endpoint returns\n\t\ta 401 error.\n\n\t\tWhen calling this endpoint from a backend, please ensure to properly forward the HTTP cookies.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserLogoutFlowRequest\n\t*/\n\tCreateBrowserLogoutFlow(ctx context.Context) FrontendAPICreateBrowserLogoutFlowRequest\n\n\t// CreateBrowserLogoutFlowExecute executes the request\n\t//  @return LogoutFlow\n\tCreateBrowserLogoutFlowExecute(r FrontendAPICreateBrowserLogoutFlowRequest) (*LogoutFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserRecoveryFlow Create Recovery Flow for Browsers\n\n\t\t\tThis endpoint initializes a browser-based account recovery flow. Once initialized, the browser will be redirected to\n\t\t`selfservice.flows.recovery.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\n\t\texists, the browser is returned to the configured return URL.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects\n\t\tor a 400 bad request error if the user is already authenticated.\n\n\t\tThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\n\t\tMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserRecoveryFlowRequest\n\t*/\n\tCreateBrowserRecoveryFlow(ctx context.Context) FrontendAPICreateBrowserRecoveryFlowRequest\n\n\t// CreateBrowserRecoveryFlowExecute executes the request\n\t//  @return RecoveryFlow\n\tCreateBrowserRecoveryFlowExecute(r FrontendAPICreateBrowserRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserRegistrationFlow Create Registration Flow for Browsers\n\n\t\t\tThis endpoint initializes a browser-based user registration flow. This endpoint will set the appropriate\n\t\tcookies and anti-CSRF measures required for browser-based flows.\n\n\t\tIf this endpoint is opened as a link in the browser, it will be redirected to\n\t\t`selfservice.flows.registration.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\n\t\texists already, the browser will be redirected to `urls.default_redirect_url`.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the registration flow without a redirect.\n\n\t\tThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserRegistrationFlowRequest\n\t*/\n\tCreateBrowserRegistrationFlow(ctx context.Context) FrontendAPICreateBrowserRegistrationFlowRequest\n\n\t// CreateBrowserRegistrationFlowExecute executes the request\n\t//  @return RegistrationFlow\n\tCreateBrowserRegistrationFlowExecute(r FrontendAPICreateBrowserRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserSettingsFlow Create Settings Flow for Browsers\n\n\t\t\tThis endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\n\t\t`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\n\t\tOry Kratos Session Cookie is included in the request, a login flow will be initialized.\n\n\t\tIf this endpoint is opened as a link in the browser, it will be redirected to\n\t\t`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid user session\n\t\twas set, the browser will be redirected to the login endpoint.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the settings flow without any redirects\n\t\tor a 401 forbidden error if no valid session was set.\n\n\t\tDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`session_inactive`: No Ory Session was found - sign in a user first.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\n\t\tThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\n\t\tMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserSettingsFlowRequest\n\t*/\n\tCreateBrowserSettingsFlow(ctx context.Context) FrontendAPICreateBrowserSettingsFlowRequest\n\n\t// CreateBrowserSettingsFlowExecute executes the request\n\t//  @return SettingsFlow\n\tCreateBrowserSettingsFlowExecute(r FrontendAPICreateBrowserSettingsFlowRequest) (*SettingsFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateBrowserVerificationFlow Create Verification Flow for Browser Clients\n\n\t\t\tThis endpoint initializes a browser-based account verification flow. Once initialized, the browser will be redirected to\n\t\t`selfservice.flows.verification.ui_url` with the flow ID set as the query parameter `?flow=`.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects.\n\n\t\tThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\n\n\t\tMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateBrowserVerificationFlowRequest\n\t*/\n\tCreateBrowserVerificationFlow(ctx context.Context) FrontendAPICreateBrowserVerificationFlowRequest\n\n\t// CreateBrowserVerificationFlowExecute executes the request\n\t//  @return VerificationFlow\n\tCreateBrowserVerificationFlowExecute(r FrontendAPICreateBrowserVerificationFlowRequest) (*VerificationFlow, *http.Response, error)\n\n\t/*\n\t\tCreateFedcmFlow Get FedCM Parameters\n\n\t\tThis endpoint returns a list of all available FedCM providers. It is only supported on the Ory Network.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return FrontendAPICreateFedcmFlowRequest\n\t*/\n\tCreateFedcmFlow(ctx context.Context) FrontendAPICreateFedcmFlowRequest\n\n\t// CreateFedcmFlowExecute executes the request\n\t//  @return CreateFedcmFlowResponse\n\tCreateFedcmFlowExecute(r FrontendAPICreateFedcmFlowRequest) (*CreateFedcmFlowResponse, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeLoginFlow Create Login Flow for Native Apps\n\n\t\t\tThis endpoint initiates a login flow for native apps that do not use a browser, such as mobile devices, smart TVs, and so on.\n\n\t\tIf a valid provided session cookie or session token is provided, a 400 Bad Request error\n\t\twill be returned unless the URL query parameter `?refresh=true` is set.\n\n\t\tTo fetch an existing login flow call `/self-service/login/flows?flow=<flow_id>`.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks, including CSRF login attacks.\n\n\t\tIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeLoginFlowRequest\n\t*/\n\tCreateNativeLoginFlow(ctx context.Context) FrontendAPICreateNativeLoginFlowRequest\n\n\t// CreateNativeLoginFlowExecute executes the request\n\t//  @return LoginFlow\n\tCreateNativeLoginFlowExecute(r FrontendAPICreateNativeLoginFlowRequest) (*LoginFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeRecoveryFlow Create Recovery Flow for Native Apps\n\n\t\t\tThis endpoint initiates a recovery flow for API clients such as mobile devices, smart TVs, and so on.\n\n\t\tIf a valid provided session cookie or session token is provided, a 400 Bad Request error.\n\n\t\tOn an existing recovery flow, use the `getRecoveryFlow` API endpoint.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeRecoveryFlowRequest\n\t*/\n\tCreateNativeRecoveryFlow(ctx context.Context) FrontendAPICreateNativeRecoveryFlowRequest\n\n\t// CreateNativeRecoveryFlowExecute executes the request\n\t//  @return RecoveryFlow\n\tCreateNativeRecoveryFlowExecute(r FrontendAPICreateNativeRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeRegistrationFlow Create Registration Flow for Native Apps\n\n\t\t\tThis endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\n\n\t\tIf a valid provided session cookie or session token is provided, a 400 Bad Request error\n\t\twill be returned unless the URL query parameter `?refresh=true` is set.\n\n\t\tTo fetch an existing registration flow call `/self-service/registration/flows?flow=<flow_id>`.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks.\n\n\t\tIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeRegistrationFlowRequest\n\t*/\n\tCreateNativeRegistrationFlow(ctx context.Context) FrontendAPICreateNativeRegistrationFlowRequest\n\n\t// CreateNativeRegistrationFlowExecute executes the request\n\t//  @return RegistrationFlow\n\tCreateNativeRegistrationFlowExecute(r FrontendAPICreateNativeRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeSettingsFlow Create Settings Flow for Native Apps\n\n\t\t\tThis endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\n\t\tYou must provide a valid Ory Kratos Session Token for this endpoint to respond with HTTP 200 OK.\n\n\t\tTo fetch an existing settings flow call `/self-service/settings/flows?flow=<flow_id>`.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks.\n\n\t\tDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor or change the configuration.\n\n\t\tIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`session_inactive`: No Ory Session was found - sign in a user first.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeSettingsFlowRequest\n\t*/\n\tCreateNativeSettingsFlow(ctx context.Context) FrontendAPICreateNativeSettingsFlowRequest\n\n\t// CreateNativeSettingsFlowExecute executes the request\n\t//  @return SettingsFlow\n\tCreateNativeSettingsFlowExecute(r FrontendAPICreateNativeSettingsFlowRequest) (*SettingsFlow, *http.Response, error)\n\n\t/*\n\t\t\tCreateNativeVerificationFlow Create Verification Flow for Native Apps\n\n\t\t\tThis endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\n\n\t\tTo fetch an existing verification flow call `/self-service/verification/flows?flow=<flow_id>`.\n\n\t\tYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n\t\tPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n\t\tyou vulnerable to a variety of CSRF attacks.\n\n\t\tThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\n\t\tMore information can be found at [Ory Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPICreateNativeVerificationFlowRequest\n\t*/\n\tCreateNativeVerificationFlow(ctx context.Context) FrontendAPICreateNativeVerificationFlowRequest\n\n\t// CreateNativeVerificationFlowExecute executes the request\n\t//  @return VerificationFlow\n\tCreateNativeVerificationFlowExecute(r FrontendAPICreateNativeVerificationFlowRequest) (*VerificationFlow, *http.Response, error)\n\n\t/*\n\t\t\tDisableMyOtherSessions Disable my other sessions\n\n\t\t\tCalling this endpoint invalidates all except the current session that belong to the logged-in user.\n\t\tSession data are not deleted.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIDisableMyOtherSessionsRequest\n\t*/\n\tDisableMyOtherSessions(ctx context.Context) FrontendAPIDisableMyOtherSessionsRequest\n\n\t// DisableMyOtherSessionsExecute executes the request\n\t//  @return DeleteMySessionsCount\n\tDisableMyOtherSessionsExecute(r FrontendAPIDisableMyOtherSessionsRequest) (*DeleteMySessionsCount, *http.Response, error)\n\n\t/*\n\t\t\tDisableMySession Disable one of my sessions\n\n\t\t\tCalling this endpoint invalidates the specified session. The current session cannot be revoked.\n\t\tSession data are not deleted.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the session's ID.\n\t\t\t@return FrontendAPIDisableMySessionRequest\n\t*/\n\tDisableMySession(ctx context.Context, id string) FrontendAPIDisableMySessionRequest\n\n\t// DisableMySessionExecute executes the request\n\tDisableMySessionExecute(r FrontendAPIDisableMySessionRequest) (*http.Response, error)\n\n\t/*\n\t\tExchangeSessionToken Exchange Session Token\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return FrontendAPIExchangeSessionTokenRequest\n\t*/\n\tExchangeSessionToken(ctx context.Context) FrontendAPIExchangeSessionTokenRequest\n\n\t// ExchangeSessionTokenExecute executes the request\n\t//  @return SuccessfulNativeLogin\n\tExchangeSessionTokenExecute(r FrontendAPIExchangeSessionTokenRequest) (*SuccessfulNativeLogin, *http.Response, error)\n\n\t/*\n\t\t\tGetFlowError Get User-Flow Errors\n\n\t\t\tThis endpoint returns the error associated with a user-facing self service errors.\n\n\t\tThis endpoint supports stub values to help you implement the error UI:\n\n\t\t`?id=stub:500` - returns a stub 500 (Internal Server Error) error.\n\n\t\tMore information can be found at [Ory Kratos User User Facing Error Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetFlowErrorRequest\n\t*/\n\tGetFlowError(ctx context.Context) FrontendAPIGetFlowErrorRequest\n\n\t// GetFlowErrorExecute executes the request\n\t//  @return FlowError\n\tGetFlowErrorExecute(r FrontendAPIGetFlowErrorRequest) (*FlowError, *http.Response, error)\n\n\t/*\n\t\t\tGetLoginFlow Get Login Flow\n\n\t\t\tThis endpoint returns a login flow's context with, for example, error details and other information.\n\n\t\tBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n\t\tFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\n\t\tIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n\t\tand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/login', async function (req, res) {\n\t\tconst flow = await client.getLoginFlow(req.header('cookie'), req.query['flow'])\n\n\t\tres.render('login', flow)\n\t\t})\n\t\t```\n\n\t\tThis request may fail due to several reasons. The `error.id` can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`self_service_flow_expired`: The flow is expired and you should request a new one.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetLoginFlowRequest\n\t*/\n\tGetLoginFlow(ctx context.Context) FrontendAPIGetLoginFlowRequest\n\n\t// GetLoginFlowExecute executes the request\n\t//  @return LoginFlow\n\tGetLoginFlowExecute(r FrontendAPIGetLoginFlowRequest) (*LoginFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetRecoveryFlow Get Recovery Flow\n\n\t\t\tThis endpoint returns a recovery flow's context with, for example, error details and other information.\n\n\t\tBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n\t\tFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\n\t\tIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n\t\tand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/recovery', async function (req, res) {\n\t\tconst flow = await client.getRecoveryFlow(req.header('Cookie'), req.query['flow'])\n\n\t\tres.render('recovery', flow)\n\t\t})\n\t\t```\n\n\t\tMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetRecoveryFlowRequest\n\t*/\n\tGetRecoveryFlow(ctx context.Context) FrontendAPIGetRecoveryFlowRequest\n\n\t// GetRecoveryFlowExecute executes the request\n\t//  @return RecoveryFlow\n\tGetRecoveryFlowExecute(r FrontendAPIGetRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetRegistrationFlow Get Registration Flow\n\n\t\t\tThis endpoint returns a registration flow's context with, for example, error details and other information.\n\n\t\tBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n\t\tFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\n\t\tIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n\t\tand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/registration', async function (req, res) {\n\t\tconst flow = await client.getRegistrationFlow(req.header('cookie'), req.query['flow'])\n\n\t\tres.render('registration', flow)\n\t\t})\n\t\t```\n\n\t\tThis request may fail due to several reasons. The `error.id` can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`self_service_flow_expired`: The flow is expired and you should request a new one.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetRegistrationFlowRequest\n\t*/\n\tGetRegistrationFlow(ctx context.Context) FrontendAPIGetRegistrationFlowRequest\n\n\t// GetRegistrationFlowExecute executes the request\n\t//  @return RegistrationFlow\n\tGetRegistrationFlowExecute(r FrontendAPIGetRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetSettingsFlow Get Settings Flow\n\n\t\t\tWhen accessing this endpoint through Ory Kratos' Public API you must ensure that either the Ory Kratos Session Cookie\n\t\tor the Ory Kratos Session Token are set.\n\n\t\tDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor or change the configuration.\n\n\t\tYou can access this endpoint without credentials when using Ory Kratos' Admin API.\n\n\t\tIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`session_inactive`: No Ory Session was found - sign in a user first.\n\t\t`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\n\t\tidentity logged in instead.\n\n\t\tMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetSettingsFlowRequest\n\t*/\n\tGetSettingsFlow(ctx context.Context) FrontendAPIGetSettingsFlowRequest\n\n\t// GetSettingsFlowExecute executes the request\n\t//  @return SettingsFlow\n\tGetSettingsFlowExecute(r FrontendAPIGetSettingsFlowRequest) (*SettingsFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetVerificationFlow Get Verification Flow\n\n\t\t\tThis endpoint returns a verification flow's context with, for example, error details and other information.\n\n\t\tBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n\t\tFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\n\t\tIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n\t\tand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/recovery', async function (req, res) {\n\t\tconst flow = await client.getVerificationFlow(req.header('cookie'), req.query['flow'])\n\n\t\tres.render('verification', flow)\n\t\t})\n\t\t```\n\n\t\tMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetVerificationFlowRequest\n\t*/\n\tGetVerificationFlow(ctx context.Context) FrontendAPIGetVerificationFlowRequest\n\n\t// GetVerificationFlowExecute executes the request\n\t//  @return VerificationFlow\n\tGetVerificationFlowExecute(r FrontendAPIGetVerificationFlowRequest) (*VerificationFlow, *http.Response, error)\n\n\t/*\n\t\t\tGetWebAuthnJavaScript Get WebAuthn JavaScript\n\n\t\t\tThis endpoint provides JavaScript which is needed in order to perform WebAuthn login and registration.\n\n\t\tIf you are building a JavaScript Browser App (e.g. in ReactJS or AngularJS) you will need to load this file:\n\n\t\t```html\n\t\t<script src=\"https://public-kratos.example.org/.well-known/ory/webauthn.js\" type=\"script\" async />\n\t\t```\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIGetWebAuthnJavaScriptRequest\n\t*/\n\tGetWebAuthnJavaScript(ctx context.Context) FrontendAPIGetWebAuthnJavaScriptRequest\n\n\t// GetWebAuthnJavaScriptExecute executes the request\n\t//  @return string\n\tGetWebAuthnJavaScriptExecute(r FrontendAPIGetWebAuthnJavaScriptRequest) (string, *http.Response, error)\n\n\t/*\n\t\t\tListMySessions Get My Active Sessions\n\n\t\t\tThis endpoints returns all other active sessions that belong to the logged-in user.\n\t\tThe current session can be retrieved by calling the `/sessions/whoami` endpoint.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIListMySessionsRequest\n\t*/\n\tListMySessions(ctx context.Context) FrontendAPIListMySessionsRequest\n\n\t// ListMySessionsExecute executes the request\n\t//  @return []Session\n\tListMySessionsExecute(r FrontendAPIListMySessionsRequest) ([]Session, *http.Response, error)\n\n\t/*\n\t\t\tPerformNativeLogout Perform Logout for Native Apps\n\n\t\t\tUse this endpoint to log out an identity using an Ory Session Token. If the Ory Session Token was successfully\n\t\trevoked, the server returns a 204 No Content response. A 204 No Content response is also sent when\n\t\tthe Ory Session Token has been revoked already before.\n\n\t\tIf the Ory Session Token is malformed or does not exist a 403 Forbidden response will be returned.\n\n\t\tThis endpoint does not remove any HTTP\n\t\tCookies - use the Browser-Based Self-Service Logout Flow instead.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIPerformNativeLogoutRequest\n\t*/\n\tPerformNativeLogout(ctx context.Context) FrontendAPIPerformNativeLogoutRequest\n\n\t// PerformNativeLogoutExecute executes the request\n\tPerformNativeLogoutExecute(r FrontendAPIPerformNativeLogoutRequest) (*http.Response, error)\n\n\t/*\n\t\t\tToSession Check Who the Current HTTP Session Belongs To\n\n\t\t\tUses the HTTP Headers in the GET request to determine (e.g. by using checking the cookies) who is authenticated.\n\t\tReturns a session object in the body or 401 if the credentials are invalid or no credentials were sent.\n\t\tWhen the request it successful it adds the user ID to the 'X-Kratos-Authenticated-Identity-Id' header\n\t\tin the response.\n\n\t\tIf you call this endpoint from a server-side application, you must forward the HTTP Cookie Header to this endpoint:\n\n\t\t```js\n\t\tpseudo-code example\n\t\trouter.get('/protected-endpoint', async function (req, res) {\n\t\tconst session = await client.toSession(undefined, req.header('cookie'))\n\n\t\tconsole.log(session)\n\t\t})\n\t\t```\n\n\t\tWhen calling this endpoint from a non-browser application (e.g. mobile app) you must include the session token:\n\n\t\t```js\n\t\tpseudo-code example\n\t\t...\n\t\tconst session = await client.toSession(\"the-session-token\")\n\n\t\tconsole.log(session)\n\t\t```\n\n\t\tWhen using a token template, the token is included in the `tokenized` field of the session.\n\n\t\t```js\n\t\tpseudo-code example\n\t\t...\n\t\tconst session = await client.toSession(\"the-session-token\", { tokenize_as: \"example-jwt-template\" })\n\n\t\tconsole.log(session.tokenized) // The JWT\n\t\t```\n\n\t\tDepending on your configuration this endpoint might return a 403 status code if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor or change the configuration.\n\n\t\tThis endpoint is useful for:\n\n\t\tAJAX calls. Remember to send credentials and set up CORS correctly!\n\t\tReverse proxies and API Gateways\n\t\tServer-side calls - use the `X-Session-Token` header!\n\n\t\tThis endpoint authenticates users by checking:\n\n\t\tif the `Cookie` HTTP header was set containing an Ory Kratos Session Cookie;\n\t\tif the `Authorization: bearer <ory-session-token>` HTTP header was set with a valid Ory Kratos Session Token;\n\t\tif the `X-Session-Token` HTTP header was set with a valid Ory Kratos Session Token.\n\n\t\tIf none of these headers are set or the cookie or token are invalid, the endpoint returns a HTTP 401 status code.\n\n\t\tAs explained above, this request may fail due to several reasons. The `error.id` can be one of:\n\n\t\t`session_inactive`: No active session was found in the request (e.g. no Ory Session Cookie / Ory Session Token).\n\t\t`session_aal2_required`: An active session was found but it does not fulfil the Authenticator Assurance Level, implying that the session must (e.g.) authenticate the second factor.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIToSessionRequest\n\t*/\n\tToSession(ctx context.Context) FrontendAPIToSessionRequest\n\n\t// ToSessionExecute executes the request\n\t//  @return Session\n\tToSessionExecute(r FrontendAPIToSessionRequest) (*Session, *http.Response, error)\n\n\t/*\n\t\t\tUpdateFedcmFlow Submit a FedCM token\n\n\t\t\tUse this endpoint to submit a token from a FedCM provider through\n\t\t`navigator.credentials.get` and log the user in. The parameters from\n\t\t`navigator.credentials.get` must have come from `GET\n\t\tself-service/fed-cm/parameters`.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateFedcmFlowRequest\n\t*/\n\tUpdateFedcmFlow(ctx context.Context) FrontendAPIUpdateFedcmFlowRequest\n\n\t// UpdateFedcmFlowExecute executes the request\n\t//  @return SuccessfulNativeLogin\n\tUpdateFedcmFlowExecute(r FrontendAPIUpdateFedcmFlowRequest) (*SuccessfulNativeLogin, *http.Response, error)\n\n\t/*\n\t\t\tUpdateLoginFlow Submit a Login Flow\n\n\t\t\tUse this endpoint to complete a login flow. This endpoint\n\t\tbehaves differently for API and browser flows.\n\n\t\tAPI flows expect `application/json` to be sent in the body and responds with\n\t\tHTTP 200 and a application/json body with the session token on success;\n\t\tHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\n\t\tHTTP 400 on form validation errors.\n\n\t\tBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\n\t\ta HTTP 303 redirect to the post/after login URL or the `return_to` value if it was set and if the login succeeded;\n\t\ta HTTP 303 redirect to the login UI URL with the flow ID containing the validation errors otherwise.\n\n\t\tBrowser flows with an accept header of `application/json` will not redirect but instead respond with\n\t\tHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\n\t\tHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\n\t\tHTTP 400 on form validation errors.\n\n\t\tIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\t\t`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\n\t\tMost likely used in Social Sign In flows.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateLoginFlowRequest\n\t*/\n\tUpdateLoginFlow(ctx context.Context) FrontendAPIUpdateLoginFlowRequest\n\n\t// UpdateLoginFlowExecute executes the request\n\t//  @return SuccessfulNativeLogin\n\tUpdateLoginFlowExecute(r FrontendAPIUpdateLoginFlowRequest) (*SuccessfulNativeLogin, *http.Response, error)\n\n\t/*\n\t\t\tUpdateLogoutFlow Update Logout Flow\n\n\t\t\tThis endpoint logs out an identity in a self-service manner.\n\n\t\tIf the `Accept` HTTP header is not set to `application/json`, the browser will be redirected (HTTP 303 See Other)\n\t\tto the `return_to` parameter of the initial request or fall back to `urls.default_return_to`.\n\n\t\tIf the `Accept` HTTP header is set to `application/json`, a 204 No Content response\n\t\twill be sent on successful logout instead.\n\n\t\tThis endpoint is NOT INTENDED for API clients and only works\n\t\twith browsers (Chrome, Firefox, ...). For API clients you can\n\t\tcall the `/self-service/logout/api` URL directly with the Ory Session Token.\n\n\t\tMore information can be found at [Ory Kratos User Logout Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-logout).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateLogoutFlowRequest\n\t*/\n\tUpdateLogoutFlow(ctx context.Context) FrontendAPIUpdateLogoutFlowRequest\n\n\t// UpdateLogoutFlowExecute executes the request\n\tUpdateLogoutFlowExecute(r FrontendAPIUpdateLogoutFlowRequest) (*http.Response, error)\n\n\t/*\n\t\t\tUpdateRecoveryFlow Update Recovery Flow\n\n\t\t\tUse this endpoint to update a recovery flow. This endpoint\n\t\tbehaves differently for API and browser flows and has several states:\n\n\t\t`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\n\t\tand works with API- and Browser-initiated flows.\n\t\tFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid.\n\t\tand a HTTP 303 See Other redirect with a fresh recovery flow if the flow was otherwise invalid (e.g. expired).\n\t\tFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Recovery UI URL with the Recovery Flow ID appended.\n\t\t`sent_email` is the success state after `choose_method` for the `link` method and allows the user to request another recovery email. It\n\t\tworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n\t\t`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a recovery link\")\n\t\tdoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n\t\t(if the link was valid) and instructs the user to update their password, or a redirect to the Recover UI URL with\n\t\ta new Recovery Flow ID which contains an error message that the recovery link was invalid.\n\n\t\tMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateRecoveryFlowRequest\n\t*/\n\tUpdateRecoveryFlow(ctx context.Context) FrontendAPIUpdateRecoveryFlowRequest\n\n\t// UpdateRecoveryFlowExecute executes the request\n\t//  @return RecoveryFlow\n\tUpdateRecoveryFlowExecute(r FrontendAPIUpdateRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error)\n\n\t/*\n\t\t\tUpdateRegistrationFlow Update Registration Flow\n\n\t\t\tUse this endpoint to complete a registration flow by sending an identity's traits and password. This endpoint\n\t\tbehaves differently for API and browser flows.\n\n\t\tAPI flows expect `application/json` to be sent in the body and respond with\n\t\tHTTP 200 and a application/json body with the created identity success - if the session hook is configured the\n\t\t`session` and `session_token` will also be included;\n\t\tHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\n\t\tHTTP 400 on form validation errors.\n\n\t\tBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\n\t\ta HTTP 303 redirect to the post/after registration URL or the `return_to` value if it was set and if the registration succeeded;\n\t\ta HTTP 303 redirect to the registration UI URL with the flow ID containing the validation errors otherwise.\n\n\t\tBrowser flows with an accept header of `application/json` will not redirect but instead respond with\n\t\tHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\n\t\tHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\n\t\tHTTP 400 on form validation errors.\n\n\t\tIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_already_available`: The user is already signed in.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\t\t`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\n\t\tMost likely used in Social Sign In flows.\n\n\t\tMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateRegistrationFlowRequest\n\t*/\n\tUpdateRegistrationFlow(ctx context.Context) FrontendAPIUpdateRegistrationFlowRequest\n\n\t// UpdateRegistrationFlowExecute executes the request\n\t//  @return SuccessfulNativeRegistration\n\tUpdateRegistrationFlowExecute(r FrontendAPIUpdateRegistrationFlowRequest) (*SuccessfulNativeRegistration, *http.Response, error)\n\n\t/*\n\t\t\tUpdateSettingsFlow Complete Settings Flow\n\n\t\t\tUse this endpoint to complete a settings flow by sending an identity's updated password. This endpoint\n\t\tbehaves differently for API and browser flows.\n\n\t\tAPI-initiated flows expect `application/json` to be sent in the body and respond with\n\t\tHTTP 200 and an application/json body with the session token on success;\n\t\tHTTP 303 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\n\t\tHTTP 400 on form validation errors.\n\t\tHTTP 401 when the endpoint is called without a valid session token.\n\t\tHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\n\t\tImplies that the user needs to re-authenticate.\n\n\t\tBrowser flows without HTTP Header `Accept` or with `Accept: text/*` respond with\n\t\ta HTTP 303 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\n\t\ta HTTP 303 redirect to the Settings UI URL with the flow ID containing the validation errors otherwise.\n\t\ta HTTP 303 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\n\n\t\tBrowser flows with HTTP Header `Accept: application/json` respond with\n\t\tHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\n\t\tHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\n\t\tHTTP 401 when the endpoint is called without a valid session cookie.\n\t\tHTTP 403 when the page is accessed without a session cookie or the session's AAL is too low.\n\t\tHTTP 400 on form validation errors.\n\n\t\tDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n\t\tAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n\t\tcredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n\t\tto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n\n\t\tIf this endpoint is called with a `Accept: application/json` HTTP header, the response contains the flow without a redirect. In the\n\t\tcase of an error, the `error.id` of the JSON response body can be one of:\n\n\t\t`session_refresh_required`: The identity requested to change something that needs a privileged session. Redirect\n\t\tthe identity to the login init endpoint with query parameters `?refresh=true&return_to=<the-current-browser-url>`,\n\t\tor initiate a refresh login flow otherwise.\n\t\t`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\t\t`session_inactive`: No Ory Session was found - sign in a user first.\n\t\t`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\n\t\tidentity logged in instead.\n\t\t`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\t\t`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\n\t\tMost likely used in Social Sign In flows.\n\n\t\tMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateSettingsFlowRequest\n\t*/\n\tUpdateSettingsFlow(ctx context.Context) FrontendAPIUpdateSettingsFlowRequest\n\n\t// UpdateSettingsFlowExecute executes the request\n\t//  @return SettingsFlow\n\tUpdateSettingsFlowExecute(r FrontendAPIUpdateSettingsFlowRequest) (*SettingsFlow, *http.Response, error)\n\n\t/*\n\t\t\tUpdateVerificationFlow Complete Verification Flow\n\n\t\t\tUse this endpoint to complete a verification flow. This endpoint\n\t\tbehaves differently for API and browser flows and has several states:\n\n\t\t`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\n\t\tand works with API- and Browser-initiated flows.\n\t\tFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\n\t\tand a HTTP 303 See Other redirect with a fresh verification flow if the flow was otherwise invalid (e.g. expired).\n\t\tFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Verification UI URL with the Verification Flow ID appended.\n\t\t`sent_email` is the success state after `choose_method` when using the `link` method and allows the user to request another verification email. It\n\t\tworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n\t\t`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a verification link\")\n\t\tdoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n\t\t(if the link was valid) and instructs the user to update their password, or a redirect to the Verification UI URL with\n\t\ta new Verification Flow ID which contains an error message that the verification link was invalid.\n\n\t\tMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return FrontendAPIUpdateVerificationFlowRequest\n\t*/\n\tUpdateVerificationFlow(ctx context.Context) FrontendAPIUpdateVerificationFlowRequest\n\n\t// UpdateVerificationFlowExecute executes the request\n\t//  @return VerificationFlow\n\tUpdateVerificationFlowExecute(r FrontendAPIUpdateVerificationFlowRequest) (*VerificationFlow, *http.Response, error)\n}\n\n// FrontendAPIService FrontendAPI service\ntype FrontendAPIService service\n\ntype FrontendAPICreateBrowserLoginFlowRequest struct {\n\tctx            context.Context\n\tApiService     FrontendAPI\n\trefresh        *bool\n\taal            *string\n\treturnTo       *string\n\tcookie         *string\n\tloginChallenge *string\n\torganization   *string\n\tvia            *string\n\tidentitySchema *string\n}\n\n// Refresh a login session  If set to true, this will refresh an existing login session by asking the user to sign in again. This will reset the authenticated_at time of the session.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Refresh(refresh bool) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.refresh = &refresh\n\treturn r\n}\n\n// Request a Specific AuthenticationMethod Assurance Level  Use this parameter to upgrade an existing session&#39;s authenticator assurance level (AAL). This allows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password, the AAL is 1. If you wish to \\&quot;upgrade\\&quot; the session&#39;s security by asking the user to perform TOTP / WebAuth/ ... you would set this to \\&quot;aal2\\&quot;.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Aal(aal string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.aal = &aal\n\treturn r\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Cookie(cookie string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\n// An optional Hydra login challenge. If present, Kratos will cooperate with Ory Hydra to act as an OAuth2 identity provider.  The value for this parameter comes from &#x60;login_challenge&#x60; URL Query parameter sent to your application (e.g. &#x60;/login?login_challenge&#x3D;abcde&#x60;).\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) LoginChallenge(loginChallenge string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.loginChallenge = &loginChallenge\n\treturn r\n}\n\n// An optional organization ID that should be used for logging this user in. This parameter is only effective in the Ory Network.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Organization(organization string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.organization = &organization\n\treturn r\n}\n\n// Via should contain the identity&#39;s credential the code should be sent to. Only relevant in aal2 flows.  DEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice of MFA credentials to choose from to perform the second factor instead.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Via(via string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.via = &via\n\treturn r\n}\n\n// An optional identity schema to use for the login flow.\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) IdentitySchema(identitySchema string) FrontendAPICreateBrowserLoginFlowRequest {\n\tr.identitySchema = &identitySchema\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserLoginFlowRequest) Execute() (*LoginFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserLoginFlowExecute(r)\n}\n\n/*\nCreateBrowserLoginFlow Create Login Flow for Browsers\n\nThis endpoint initializes a browser-based user login flow. This endpoint will set the appropriate\ncookies and anti-CSRF measures required for browser-based flows.\n\nIf this endpoint is opened as a link in the browser, it will be redirected to\n`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\n`?refresh=true` was set.\n\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\nThe optional query parameter login_challenge is set when using Kratos with\nHydra in an OAuth2 flow. See the oauth2_provider.url configuration\noption.\n\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserLoginFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserLoginFlow(ctx context.Context) FrontendAPICreateBrowserLoginFlowRequest {\n\treturn FrontendAPICreateBrowserLoginFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return LoginFlow\nfunc (a *FrontendAPIService) CreateBrowserLoginFlowExecute(r FrontendAPICreateBrowserLoginFlowRequest) (*LoginFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *LoginFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserLoginFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/login/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.refresh != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"refresh\", r.refresh, \"form\", \"\")\n\t}\n\tif r.aal != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"aal\", r.aal, \"form\", \"\")\n\t}\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\tif r.loginChallenge != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"login_challenge\", r.loginChallenge, \"form\", \"\")\n\t}\n\tif r.organization != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization\", r.organization, \"form\", \"\")\n\t}\n\tif r.via != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"via\", r.via, \"form\", \"\")\n\t}\n\tif r.identitySchema != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identity_schema\", r.identitySchema, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserLogoutFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tcookie     *string\n\treturnTo   *string\n}\n\n// HTTP Cookies  If you call this endpoint from a backend, please include the original Cookie header in the request.\nfunc (r FrontendAPICreateBrowserLogoutFlowRequest) Cookie(cookie string) FrontendAPICreateBrowserLogoutFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\n// Return to URL  The URL to which the browser should be redirected to after the logout has been performed.\nfunc (r FrontendAPICreateBrowserLogoutFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserLogoutFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserLogoutFlowRequest) Execute() (*LogoutFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserLogoutFlowExecute(r)\n}\n\n/*\nCreateBrowserLogoutFlow Create a Logout URL for Browsers\n\nThis endpoint initializes a browser-based user logout flow and a URL which can be used to log out the user.\n\nThis endpoint is NOT INTENDED for API clients and only works\nwith browsers (Chrome, Firefox, ...). For API clients you can\ncall the `/self-service/logout/api` URL directly with the Ory Session Token.\n\nThe URL is only valid for the currently signed in user. If no user is signed in, this endpoint returns\na 401 error.\n\nWhen calling this endpoint from a backend, please ensure to properly forward the HTTP cookies.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserLogoutFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserLogoutFlow(ctx context.Context) FrontendAPICreateBrowserLogoutFlowRequest {\n\treturn FrontendAPICreateBrowserLogoutFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return LogoutFlow\nfunc (a *FrontendAPIService) CreateBrowserLogoutFlowExecute(r FrontendAPICreateBrowserLogoutFlowRequest) (*LogoutFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *LogoutFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserLogoutFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/logout/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 500 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserRecoveryFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\treturnTo   *string\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserRecoveryFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserRecoveryFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserRecoveryFlowRequest) Execute() (*RecoveryFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserRecoveryFlowExecute(r)\n}\n\n/*\nCreateBrowserRecoveryFlow Create Recovery Flow for Browsers\n\nThis endpoint initializes a browser-based account recovery flow. Once initialized, the browser will be redirected to\n`selfservice.flows.recovery.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\nexists, the browser is returned to the configured return URL.\n\nIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects\nor a 400 bad request error if the user is already authenticated.\n\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserRecoveryFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserRecoveryFlow(ctx context.Context) FrontendAPICreateBrowserRecoveryFlowRequest {\n\treturn FrontendAPICreateBrowserRecoveryFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryFlow\nfunc (a *FrontendAPIService) CreateBrowserRecoveryFlowExecute(r FrontendAPICreateBrowserRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserRecoveryFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/recovery/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserRegistrationFlowRequest struct {\n\tctx                       context.Context\n\tApiService                FrontendAPI\n\treturnTo                  *string\n\tloginChallenge            *string\n\tafterVerificationReturnTo *string\n\torganization              *string\n\tidentitySchema            *string\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// Ory OAuth 2.0 Login Challenge.  If set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.  The value for this parameter comes from &#x60;login_challenge&#x60; URL Query parameter sent to your application (e.g. &#x60;/registration?login_challenge&#x3D;abcde&#x60;).  This feature is compatible with Ory Hydra when not running on the Ory Network.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) LoginChallenge(loginChallenge string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.loginChallenge = &loginChallenge\n\treturn r\n}\n\n// The URL to return the browser to after the verification flow was completed.  After the registration flow is completed, the user will be sent a verification email. Upon completing the verification flow, this URL will be used to override the default &#x60;selfservice.flows.verification.after.default_redirect_to&#x60; value.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) AfterVerificationReturnTo(afterVerificationReturnTo string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.afterVerificationReturnTo = &afterVerificationReturnTo\n\treturn r\n}\n\n// An optional organization ID that should be used to register this user. This parameter is only effective in the Ory Network.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) Organization(organization string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.organization = &organization\n\treturn r\n}\n\n// An optional identity schema to use for the registration flow.\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) IdentitySchema(identitySchema string) FrontendAPICreateBrowserRegistrationFlowRequest {\n\tr.identitySchema = &identitySchema\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserRegistrationFlowRequest) Execute() (*RegistrationFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserRegistrationFlowExecute(r)\n}\n\n/*\nCreateBrowserRegistrationFlow Create Registration Flow for Browsers\n\nThis endpoint initializes a browser-based user registration flow. This endpoint will set the appropriate\ncookies and anti-CSRF measures required for browser-based flows.\n\nIf this endpoint is opened as a link in the browser, it will be redirected to\n`selfservice.flows.registration.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\nexists already, the browser will be redirected to `urls.default_redirect_url`.\n\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\nIf this endpoint is called via an AJAX request, the response contains the registration flow without a redirect.\n\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserRegistrationFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserRegistrationFlow(ctx context.Context) FrontendAPICreateBrowserRegistrationFlowRequest {\n\treturn FrontendAPICreateBrowserRegistrationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RegistrationFlow\nfunc (a *FrontendAPIService) CreateBrowserRegistrationFlowExecute(r FrontendAPICreateBrowserRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RegistrationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserRegistrationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/registration/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\tif r.loginChallenge != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"login_challenge\", r.loginChallenge, \"form\", \"\")\n\t}\n\tif r.afterVerificationReturnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"after_verification_return_to\", r.afterVerificationReturnTo, \"form\", \"\")\n\t}\n\tif r.organization != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization\", r.organization, \"form\", \"\")\n\t}\n\tif r.identitySchema != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identity_schema\", r.identitySchema, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserSettingsFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\treturnTo   *string\n\tcookie     *string\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserSettingsFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserSettingsFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPICreateBrowserSettingsFlowRequest) Cookie(cookie string) FrontendAPICreateBrowserSettingsFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserSettingsFlowRequest) Execute() (*SettingsFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserSettingsFlowExecute(r)\n}\n\n/*\nCreateBrowserSettingsFlow Create Settings Flow for Browsers\n\nThis endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\nOry Kratos Session Cookie is included in the request, a login flow will be initialized.\n\nIf this endpoint is opened as a link in the browser, it will be redirected to\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid user session\nwas set, the browser will be redirected to the login endpoint.\n\nIf this endpoint is called via an AJAX request, the response contains the settings flow without any redirects\nor a 401 forbidden error if no valid session was set.\n\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`session_inactive`: No Ory Session was found - sign in a user first.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n\nMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserSettingsFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserSettingsFlow(ctx context.Context) FrontendAPICreateBrowserSettingsFlowRequest {\n\treturn FrontendAPICreateBrowserSettingsFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SettingsFlow\nfunc (a *FrontendAPIService) CreateBrowserSettingsFlowExecute(r FrontendAPICreateBrowserSettingsFlowRequest) (*SettingsFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SettingsFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserSettingsFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/settings/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateBrowserVerificationFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\treturnTo   *string\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateBrowserVerificationFlowRequest) ReturnTo(returnTo string) FrontendAPICreateBrowserVerificationFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r FrontendAPICreateBrowserVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) {\n\treturn r.ApiService.CreateBrowserVerificationFlowExecute(r)\n}\n\n/*\nCreateBrowserVerificationFlow Create Verification Flow for Browser Clients\n\nThis endpoint initializes a browser-based account verification flow. Once initialized, the browser will be redirected to\n`selfservice.flows.verification.ui_url` with the flow ID set as the query parameter `?flow=`.\n\nIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects.\n\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\n\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateBrowserVerificationFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateBrowserVerificationFlow(ctx context.Context) FrontendAPICreateBrowserVerificationFlowRequest {\n\treturn FrontendAPICreateBrowserVerificationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return VerificationFlow\nfunc (a *FrontendAPIService) CreateBrowserVerificationFlowExecute(r FrontendAPICreateBrowserVerificationFlowRequest) (*VerificationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *VerificationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateBrowserVerificationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/verification/browser\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateFedcmFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n}\n\nfunc (r FrontendAPICreateFedcmFlowRequest) Execute() (*CreateFedcmFlowResponse, *http.Response, error) {\n\treturn r.ApiService.CreateFedcmFlowExecute(r)\n}\n\n/*\nCreateFedcmFlow Get FedCM Parameters\n\nThis endpoint returns a list of all available FedCM providers. It is only supported on the Ory Network.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateFedcmFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateFedcmFlow(ctx context.Context) FrontendAPICreateFedcmFlowRequest {\n\treturn FrontendAPICreateFedcmFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return CreateFedcmFlowResponse\nfunc (a *FrontendAPIService) CreateFedcmFlowExecute(r FrontendAPICreateFedcmFlowRequest) (*CreateFedcmFlowResponse, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *CreateFedcmFlowResponse\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateFedcmFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/fed-cm/parameters\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeLoginFlowRequest struct {\n\tctx                            context.Context\n\tApiService                     FrontendAPI\n\trefresh                        *bool\n\taal                            *string\n\txSessionToken                  *string\n\treturnSessionTokenExchangeCode *bool\n\treturnTo                       *string\n\torganization                   *string\n\tvia                            *string\n\tidentitySchema                 *string\n}\n\n// Refresh a login session  If set to true, this will refresh an existing login session by asking the user to sign in again. This will reset the authenticated_at time of the session.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Refresh(refresh bool) FrontendAPICreateNativeLoginFlowRequest {\n\tr.refresh = &refresh\n\treturn r\n}\n\n// Request a Specific AuthenticationMethod Assurance Level  Use this parameter to upgrade an existing session&#39;s authenticator assurance level (AAL). This allows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password, the AAL is 1. If you wish to \\&quot;upgrade\\&quot; the session&#39;s security by asking the user to perform TOTP / WebAuth/ ... you would set this to \\&quot;aal2\\&quot;.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Aal(aal string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.aal = &aal\n\treturn r\n}\n\n// The Session Token of the Identity performing the settings flow.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) XSessionToken(xSessionToken string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token after the login flow has been completed.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) ReturnSessionTokenExchangeCode(returnSessionTokenExchangeCode bool) FrontendAPICreateNativeLoginFlowRequest {\n\tr.returnSessionTokenExchangeCode = &returnSessionTokenExchangeCode\n\treturn r\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) ReturnTo(returnTo string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// An optional organization ID that should be used for logging this user in. This parameter is only effective in the Ory Network.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Organization(organization string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.organization = &organization\n\treturn r\n}\n\n// Via should contain the identity&#39;s credential the code should be sent to. Only relevant in aal2 flows.  DEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice of MFA credentials to choose from to perform the second factor instead.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Via(via string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.via = &via\n\treturn r\n}\n\n// An optional identity schema to use for the login flow.\nfunc (r FrontendAPICreateNativeLoginFlowRequest) IdentitySchema(identitySchema string) FrontendAPICreateNativeLoginFlowRequest {\n\tr.identitySchema = &identitySchema\n\treturn r\n}\n\nfunc (r FrontendAPICreateNativeLoginFlowRequest) Execute() (*LoginFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeLoginFlowExecute(r)\n}\n\n/*\nCreateNativeLoginFlow Create Login Flow for Native Apps\n\nThis endpoint initiates a login flow for native apps that do not use a browser, such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\nwill be returned unless the URL query parameter `?refresh=true` is set.\n\nTo fetch an existing login flow call `/self-service/login/flows?flow=<flow_id>`.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks, including CSRF login attacks.\n\nIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeLoginFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeLoginFlow(ctx context.Context) FrontendAPICreateNativeLoginFlowRequest {\n\treturn FrontendAPICreateNativeLoginFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return LoginFlow\nfunc (a *FrontendAPIService) CreateNativeLoginFlowExecute(r FrontendAPICreateNativeLoginFlowRequest) (*LoginFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *LoginFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeLoginFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/login/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.refresh != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"refresh\", r.refresh, \"form\", \"\")\n\t}\n\tif r.aal != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"aal\", r.aal, \"form\", \"\")\n\t}\n\tif r.returnSessionTokenExchangeCode != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_session_token_exchange_code\", r.returnSessionTokenExchangeCode, \"form\", \"\")\n\t}\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\tif r.organization != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization\", r.organization, \"form\", \"\")\n\t}\n\tif r.via != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"via\", r.via, \"form\", \"\")\n\t}\n\tif r.identitySchema != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identity_schema\", r.identitySchema, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeRecoveryFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n}\n\nfunc (r FrontendAPICreateNativeRecoveryFlowRequest) Execute() (*RecoveryFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeRecoveryFlowExecute(r)\n}\n\n/*\nCreateNativeRecoveryFlow Create Recovery Flow for Native Apps\n\nThis endpoint initiates a recovery flow for API clients such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error.\n\nOn an existing recovery flow, use the `getRecoveryFlow` API endpoint.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeRecoveryFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeRecoveryFlow(ctx context.Context) FrontendAPICreateNativeRecoveryFlowRequest {\n\treturn FrontendAPICreateNativeRecoveryFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryFlow\nfunc (a *FrontendAPIService) CreateNativeRecoveryFlowExecute(r FrontendAPICreateNativeRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeRecoveryFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/recovery/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeRegistrationFlowRequest struct {\n\tctx                            context.Context\n\tApiService                     FrontendAPI\n\treturnSessionTokenExchangeCode *bool\n\treturnTo                       *string\n\torganization                   *string\n\tidentitySchema                 *string\n}\n\n// EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token after the login flow has been completed.\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) ReturnSessionTokenExchangeCode(returnSessionTokenExchangeCode bool) FrontendAPICreateNativeRegistrationFlowRequest {\n\tr.returnSessionTokenExchangeCode = &returnSessionTokenExchangeCode\n\treturn r\n}\n\n// The URL to return the browser to after the flow was completed.\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) ReturnTo(returnTo string) FrontendAPICreateNativeRegistrationFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// An optional organization ID that should be used to register this user. This parameter is only effective in the Ory Network.\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) Organization(organization string) FrontendAPICreateNativeRegistrationFlowRequest {\n\tr.organization = &organization\n\treturn r\n}\n\n// An optional identity schema to use for the registration flow.\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) IdentitySchema(identitySchema string) FrontendAPICreateNativeRegistrationFlowRequest {\n\tr.identitySchema = &identitySchema\n\treturn r\n}\n\nfunc (r FrontendAPICreateNativeRegistrationFlowRequest) Execute() (*RegistrationFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeRegistrationFlowExecute(r)\n}\n\n/*\nCreateNativeRegistrationFlow Create Registration Flow for Native Apps\n\nThis endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\n\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\nwill be returned unless the URL query parameter `?refresh=true` is set.\n\nTo fetch an existing registration flow call `/self-service/registration/flows?flow=<flow_id>`.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeRegistrationFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeRegistrationFlow(ctx context.Context) FrontendAPICreateNativeRegistrationFlowRequest {\n\treturn FrontendAPICreateNativeRegistrationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RegistrationFlow\nfunc (a *FrontendAPIService) CreateNativeRegistrationFlowExecute(r FrontendAPICreateNativeRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RegistrationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeRegistrationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/registration/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnSessionTokenExchangeCode != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_session_token_exchange_code\", r.returnSessionTokenExchangeCode, \"form\", \"\")\n\t}\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\tif r.organization != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization\", r.organization, \"form\", \"\")\n\t}\n\tif r.identitySchema != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identity_schema\", r.identitySchema, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeSettingsFlowRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\txSessionToken *string\n}\n\n// The Session Token of the Identity performing the settings flow.\nfunc (r FrontendAPICreateNativeSettingsFlowRequest) XSessionToken(xSessionToken string) FrontendAPICreateNativeSettingsFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\nfunc (r FrontendAPICreateNativeSettingsFlowRequest) Execute() (*SettingsFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeSettingsFlowExecute(r)\n}\n\n/*\nCreateNativeSettingsFlow Create Settings Flow for Native Apps\n\nThis endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\nYou must provide a valid Ory Kratos Session Token for this endpoint to respond with HTTP 200 OK.\n\nTo fetch an existing settings flow call `/self-service/settings/flows?flow=<flow_id>`.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor or change the configuration.\n\nIn the case of an error, the `error.id` of the JSON response body can be one of:\n\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`session_inactive`: No Ory Session was found - sign in a user first.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeSettingsFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeSettingsFlow(ctx context.Context) FrontendAPICreateNativeSettingsFlowRequest {\n\treturn FrontendAPICreateNativeSettingsFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SettingsFlow\nfunc (a *FrontendAPIService) CreateNativeSettingsFlowExecute(r FrontendAPICreateNativeSettingsFlowRequest) (*SettingsFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SettingsFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeSettingsFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/settings/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPICreateNativeVerificationFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\treturnTo   *string\n}\n\n// A URL contained in the return_to key of the verification flow. This piece of data has no effect on the actual logic of the flow and is purely informational.\nfunc (r FrontendAPICreateNativeVerificationFlowRequest) ReturnTo(returnTo string) FrontendAPICreateNativeVerificationFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r FrontendAPICreateNativeVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) {\n\treturn r.ApiService.CreateNativeVerificationFlowExecute(r)\n}\n\n/*\nCreateNativeVerificationFlow Create Verification Flow for Native Apps\n\nThis endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\n\nTo fetch an existing verification flow call `/self-service/verification/flows?flow=<flow_id>`.\n\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\nyou vulnerable to a variety of CSRF attacks.\n\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n\nMore information can be found at [Ory Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPICreateNativeVerificationFlowRequest\n*/\nfunc (a *FrontendAPIService) CreateNativeVerificationFlow(ctx context.Context) FrontendAPICreateNativeVerificationFlowRequest {\n\treturn FrontendAPICreateNativeVerificationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return VerificationFlow\nfunc (a *FrontendAPIService) CreateNativeVerificationFlowExecute(r FrontendAPICreateNativeVerificationFlowRequest) (*VerificationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *VerificationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.CreateNativeVerificationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/verification/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIDisableMyOtherSessionsRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\txSessionToken *string\n\tcookie        *string\n}\n\n// Set the Session Token when calling from non-browser clients. A session token has a format of &#x60;MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj&#x60;.\nfunc (r FrontendAPIDisableMyOtherSessionsRequest) XSessionToken(xSessionToken string) FrontendAPIDisableMyOtherSessionsRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that scenario you must include the HTTP Cookie Header which originally was included in the request to your server. An example of a session in the HTTP Cookie Header is: &#x60;ory_kratos_session&#x3D;a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f&#x3D;&#x3D;&#x60;.  It is ok if more than one cookie are included here as all other cookies will be ignored.\nfunc (r FrontendAPIDisableMyOtherSessionsRequest) Cookie(cookie string) FrontendAPIDisableMyOtherSessionsRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIDisableMyOtherSessionsRequest) Execute() (*DeleteMySessionsCount, *http.Response, error) {\n\treturn r.ApiService.DisableMyOtherSessionsExecute(r)\n}\n\n/*\nDisableMyOtherSessions Disable my other sessions\n\nCalling this endpoint invalidates all except the current session that belong to the logged-in user.\nSession data are not deleted.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIDisableMyOtherSessionsRequest\n*/\nfunc (a *FrontendAPIService) DisableMyOtherSessions(ctx context.Context) FrontendAPIDisableMyOtherSessionsRequest {\n\treturn FrontendAPIDisableMyOtherSessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return DeleteMySessionsCount\nfunc (a *FrontendAPIService) DisableMyOtherSessionsExecute(r FrontendAPIDisableMyOtherSessionsRequest) (*DeleteMySessionsCount, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodDelete\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *DeleteMySessionsCount\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.DisableMyOtherSessions\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIDisableMySessionRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\tid            string\n\txSessionToken *string\n\tcookie        *string\n}\n\n// Set the Session Token when calling from non-browser clients. A session token has a format of &#x60;MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj&#x60;.\nfunc (r FrontendAPIDisableMySessionRequest) XSessionToken(xSessionToken string) FrontendAPIDisableMySessionRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that scenario you must include the HTTP Cookie Header which originally was included in the request to your server. An example of a session in the HTTP Cookie Header is: &#x60;ory_kratos_session&#x3D;a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f&#x3D;&#x3D;&#x60;.  It is ok if more than one cookie are included here as all other cookies will be ignored.\nfunc (r FrontendAPIDisableMySessionRequest) Cookie(cookie string) FrontendAPIDisableMySessionRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIDisableMySessionRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DisableMySessionExecute(r)\n}\n\n/*\nDisableMySession Disable one of my sessions\n\nCalling this endpoint invalidates the specified session. The current session cannot be revoked.\nSession data are not deleted.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the session's ID.\n\t@return FrontendAPIDisableMySessionRequest\n*/\nfunc (a *FrontendAPIService) DisableMySession(ctx context.Context, id string) FrontendAPIDisableMySessionRequest {\n\treturn FrontendAPIDisableMySessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\nfunc (a *FrontendAPIService) DisableMySessionExecute(r FrontendAPIDisableMySessionRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.DisableMySession\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype FrontendAPIExchangeSessionTokenRequest struct {\n\tctx          context.Context\n\tApiService   FrontendAPI\n\tinitCode     *string\n\treturnToCode *string\n}\n\n// The part of the code return when initializing the flow.\nfunc (r FrontendAPIExchangeSessionTokenRequest) InitCode(initCode string) FrontendAPIExchangeSessionTokenRequest {\n\tr.initCode = &initCode\n\treturn r\n}\n\n// The part of the code returned by the return_to URL.\nfunc (r FrontendAPIExchangeSessionTokenRequest) ReturnToCode(returnToCode string) FrontendAPIExchangeSessionTokenRequest {\n\tr.returnToCode = &returnToCode\n\treturn r\n}\n\nfunc (r FrontendAPIExchangeSessionTokenRequest) Execute() (*SuccessfulNativeLogin, *http.Response, error) {\n\treturn r.ApiService.ExchangeSessionTokenExecute(r)\n}\n\n/*\nExchangeSessionToken Exchange Session Token\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIExchangeSessionTokenRequest\n*/\nfunc (a *FrontendAPIService) ExchangeSessionToken(ctx context.Context) FrontendAPIExchangeSessionTokenRequest {\n\treturn FrontendAPIExchangeSessionTokenRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SuccessfulNativeLogin\nfunc (a *FrontendAPIService) ExchangeSessionTokenExecute(r FrontendAPIExchangeSessionTokenRequest) (*SuccessfulNativeLogin, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SuccessfulNativeLogin\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.ExchangeSessionToken\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions/token-exchange\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.initCode == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"initCode is required and must be specified\")\n\t}\n\tif r.returnToCode == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"returnToCode is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"init_code\", r.initCode, \"form\", \"\")\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to_code\", r.returnToCode, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetFlowErrorRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n}\n\n// Error is the error&#39;s ID\nfunc (r FrontendAPIGetFlowErrorRequest) Id(id string) FrontendAPIGetFlowErrorRequest {\n\tr.id = &id\n\treturn r\n}\n\nfunc (r FrontendAPIGetFlowErrorRequest) Execute() (*FlowError, *http.Response, error) {\n\treturn r.ApiService.GetFlowErrorExecute(r)\n}\n\n/*\nGetFlowError Get User-Flow Errors\n\nThis endpoint returns the error associated with a user-facing self service errors.\n\nThis endpoint supports stub values to help you implement the error UI:\n\n`?id=stub:500` - returns a stub 500 (Internal Server Error) error.\n\nMore information can be found at [Ory Kratos User User Facing Error Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetFlowErrorRequest\n*/\nfunc (a *FrontendAPIService) GetFlowError(ctx context.Context) FrontendAPIGetFlowErrorRequest {\n\treturn FrontendAPIGetFlowErrorRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return FlowError\nfunc (a *FrontendAPIService) GetFlowErrorExecute(r FrontendAPIGetFlowErrorRequest) (*FlowError, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *FlowError\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetFlowError\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/errors\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 500 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetLoginFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n\tcookie     *string\n}\n\n// The Login Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/login?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetLoginFlowRequest) Id(id string) FrontendAPIGetLoginFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIGetLoginFlowRequest) Cookie(cookie string) FrontendAPIGetLoginFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetLoginFlowRequest) Execute() (*LoginFlow, *http.Response, error) {\n\treturn r.ApiService.GetLoginFlowExecute(r)\n}\n\n/*\nGetLoginFlow Get Login Flow\n\nThis endpoint returns a login flow's context with, for example, error details and other information.\n\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\nand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/login', async function (req, res) {\nconst flow = await client.getLoginFlow(req.header('cookie'), req.query['flow'])\n\nres.render('login', flow)\n})\n```\n\nThis request may fail due to several reasons. The `error.id` can be one of:\n\n`session_already_available`: The user is already signed in.\n`self_service_flow_expired`: The flow is expired and you should request a new one.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetLoginFlowRequest\n*/\nfunc (a *FrontendAPIService) GetLoginFlow(ctx context.Context) FrontendAPIGetLoginFlowRequest {\n\treturn FrontendAPIGetLoginFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return LoginFlow\nfunc (a *FrontendAPIService) GetLoginFlowExecute(r FrontendAPIGetLoginFlowRequest) (*LoginFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *LoginFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetLoginFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/login/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetRecoveryFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n\tcookie     *string\n}\n\n// The Flow ID  The value for this parameter comes from &#x60;request&#x60; URL Query parameter sent to your application (e.g. &#x60;/recovery?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetRecoveryFlowRequest) Id(id string) FrontendAPIGetRecoveryFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIGetRecoveryFlowRequest) Cookie(cookie string) FrontendAPIGetRecoveryFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetRecoveryFlowRequest) Execute() (*RecoveryFlow, *http.Response, error) {\n\treturn r.ApiService.GetRecoveryFlowExecute(r)\n}\n\n/*\nGetRecoveryFlow Get Recovery Flow\n\nThis endpoint returns a recovery flow's context with, for example, error details and other information.\n\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\nand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/recovery', async function (req, res) {\nconst flow = await client.getRecoveryFlow(req.header('Cookie'), req.query['flow'])\n\nres.render('recovery', flow)\n})\n```\n\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetRecoveryFlowRequest\n*/\nfunc (a *FrontendAPIService) GetRecoveryFlow(ctx context.Context) FrontendAPIGetRecoveryFlowRequest {\n\treturn FrontendAPIGetRecoveryFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryFlow\nfunc (a *FrontendAPIService) GetRecoveryFlowExecute(r FrontendAPIGetRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetRecoveryFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/recovery/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetRegistrationFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n\tcookie     *string\n}\n\n// The Registration Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/registration?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetRegistrationFlowRequest) Id(id string) FrontendAPIGetRegistrationFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIGetRegistrationFlowRequest) Cookie(cookie string) FrontendAPIGetRegistrationFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetRegistrationFlowRequest) Execute() (*RegistrationFlow, *http.Response, error) {\n\treturn r.ApiService.GetRegistrationFlowExecute(r)\n}\n\n/*\nGetRegistrationFlow Get Registration Flow\n\nThis endpoint returns a registration flow's context with, for example, error details and other information.\n\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\nand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/registration', async function (req, res) {\nconst flow = await client.getRegistrationFlow(req.header('cookie'), req.query['flow'])\n\nres.render('registration', flow)\n})\n```\n\nThis request may fail due to several reasons. The `error.id` can be one of:\n\n`session_already_available`: The user is already signed in.\n`self_service_flow_expired`: The flow is expired and you should request a new one.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetRegistrationFlowRequest\n*/\nfunc (a *FrontendAPIService) GetRegistrationFlow(ctx context.Context) FrontendAPIGetRegistrationFlowRequest {\n\treturn FrontendAPIGetRegistrationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RegistrationFlow\nfunc (a *FrontendAPIService) GetRegistrationFlowExecute(r FrontendAPIGetRegistrationFlowRequest) (*RegistrationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RegistrationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetRegistrationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/registration/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetSettingsFlowRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\tid            *string\n\txSessionToken *string\n\tcookie        *string\n}\n\n// ID is the Settings Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/settings?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetSettingsFlowRequest) Id(id string) FrontendAPIGetSettingsFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// The Session Token  When using the SDK in an app without a browser, please include the session token here.\nfunc (r FrontendAPIGetSettingsFlowRequest) XSessionToken(xSessionToken string) FrontendAPIGetSettingsFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIGetSettingsFlowRequest) Cookie(cookie string) FrontendAPIGetSettingsFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetSettingsFlowRequest) Execute() (*SettingsFlow, *http.Response, error) {\n\treturn r.ApiService.GetSettingsFlowExecute(r)\n}\n\n/*\nGetSettingsFlow Get Settings Flow\n\nWhen accessing this endpoint through Ory Kratos' Public API you must ensure that either the Ory Kratos Session Cookie\nor the Ory Kratos Session Token are set.\n\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor or change the configuration.\n\nYou can access this endpoint without credentials when using Ory Kratos' Admin API.\n\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`session_inactive`: No Ory Session was found - sign in a user first.\n`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\nidentity logged in instead.\n\nMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetSettingsFlowRequest\n*/\nfunc (a *FrontendAPIService) GetSettingsFlow(ctx context.Context) FrontendAPIGetSettingsFlowRequest {\n\treturn FrontendAPIGetSettingsFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SettingsFlow\nfunc (a *FrontendAPIService) GetSettingsFlowExecute(r FrontendAPIGetSettingsFlowRequest) (*SettingsFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SettingsFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetSettingsFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/settings/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetVerificationFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\tid         *string\n\tcookie     *string\n}\n\n// The Flow ID  The value for this parameter comes from &#x60;request&#x60; URL Query parameter sent to your application (e.g. &#x60;/verification?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIGetVerificationFlowRequest) Id(id string) FrontendAPIGetVerificationFlowRequest {\n\tr.id = &id\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK on the server side you must include the HTTP Cookie Header originally sent to your HTTP handler here.\nfunc (r FrontendAPIGetVerificationFlowRequest) Cookie(cookie string) FrontendAPIGetVerificationFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIGetVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) {\n\treturn r.ApiService.GetVerificationFlowExecute(r)\n}\n\n/*\nGetVerificationFlow Get Verification Flow\n\nThis endpoint returns a verification flow's context with, for example, error details and other information.\n\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\n\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\nand you need to forward the incoming HTTP Cookie header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/recovery', async function (req, res) {\nconst flow = await client.getVerificationFlow(req.header('cookie'), req.query['flow'])\n\nres.render('verification', flow)\n})\n```\n\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetVerificationFlowRequest\n*/\nfunc (a *FrontendAPIService) GetVerificationFlow(ctx context.Context) FrontendAPIGetVerificationFlowRequest {\n\treturn FrontendAPIGetVerificationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return VerificationFlow\nfunc (a *FrontendAPIService) GetVerificationFlowExecute(r FrontendAPIGetVerificationFlowRequest) (*VerificationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *VerificationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetVerificationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/verification/flows\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.id == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"id is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"id\", r.id, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIGetWebAuthnJavaScriptRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n}\n\nfunc (r FrontendAPIGetWebAuthnJavaScriptRequest) Execute() (string, *http.Response, error) {\n\treturn r.ApiService.GetWebAuthnJavaScriptExecute(r)\n}\n\n/*\nGetWebAuthnJavaScript Get WebAuthn JavaScript\n\nThis endpoint provides JavaScript which is needed in order to perform WebAuthn login and registration.\n\nIf you are building a JavaScript Browser App (e.g. in ReactJS or AngularJS) you will need to load this file:\n\n```html\n<script src=\"https://public-kratos.example.org/.well-known/ory/webauthn.js\" type=\"script\" async />\n```\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIGetWebAuthnJavaScriptRequest\n*/\nfunc (a *FrontendAPIService) GetWebAuthnJavaScript(ctx context.Context) FrontendAPIGetWebAuthnJavaScriptRequest {\n\treturn FrontendAPIGetWebAuthnJavaScriptRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return string\nfunc (a *FrontendAPIService) GetWebAuthnJavaScriptExecute(r FrontendAPIGetWebAuthnJavaScriptRequest) (string, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue string\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.GetWebAuthnJavaScript\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/.well-known/ory/webauthn.js\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIListMySessionsRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\tperPage       *int64\n\tpage          *int64\n\tpageSize      *int64\n\tpageToken     *string\n\txSessionToken *string\n\tcookie        *string\n}\n\n// Deprecated Items per Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This is the number of items per page.\nfunc (r FrontendAPIListMySessionsRequest) PerPage(perPage int64) FrontendAPIListMySessionsRequest {\n\tr.perPage = &perPage\n\treturn r\n}\n\n// Deprecated Pagination Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This value is currently an integer, but it is not sequential. The value is not the page number, but a reference. The next page can be any number and some numbers might return an empty list.  For example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist. The first page can be retrieved by omitting this parameter. Following page pointers will be returned in the &#x60;Link&#x60; header.\nfunc (r FrontendAPIListMySessionsRequest) Page(page int64) FrontendAPIListMySessionsRequest {\n\tr.page = &page\n\treturn r\n}\n\n// Page Size  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r FrontendAPIListMySessionsRequest) PageSize(pageSize int64) FrontendAPIListMySessionsRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r FrontendAPIListMySessionsRequest) PageToken(pageToken string) FrontendAPIListMySessionsRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Set the Session Token when calling from non-browser clients. A session token has a format of &#x60;MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj&#x60;.\nfunc (r FrontendAPIListMySessionsRequest) XSessionToken(xSessionToken string) FrontendAPIListMySessionsRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that scenario you must include the HTTP Cookie Header which originally was included in the request to your server. An example of a session in the HTTP Cookie Header is: &#x60;ory_kratos_session&#x3D;a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f&#x3D;&#x3D;&#x60;.  It is ok if more than one cookie are included here as all other cookies will be ignored.\nfunc (r FrontendAPIListMySessionsRequest) Cookie(cookie string) FrontendAPIListMySessionsRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIListMySessionsRequest) Execute() ([]Session, *http.Response, error) {\n\treturn r.ApiService.ListMySessionsExecute(r)\n}\n\n/*\nListMySessions Get My Active Sessions\n\nThis endpoints returns all other active sessions that belong to the logged-in user.\nThe current session can be retrieved by calling the `/sessions/whoami` endpoint.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIListMySessionsRequest\n*/\nfunc (a *FrontendAPIService) ListMySessions(ctx context.Context) FrontendAPIListMySessionsRequest {\n\treturn FrontendAPIListMySessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Session\nfunc (a *FrontendAPIService) ListMySessionsExecute(r FrontendAPIListMySessionsRequest) ([]Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.ListMySessions\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.perPage != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"per_page\", r.perPage, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.perPage = &defaultValue\n\t}\n\tif r.page != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page\", r.page, \"form\", \"\")\n\t}\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIPerformNativeLogoutRequest struct {\n\tctx                     context.Context\n\tApiService              FrontendAPI\n\tperformNativeLogoutBody *PerformNativeLogoutBody\n}\n\nfunc (r FrontendAPIPerformNativeLogoutRequest) PerformNativeLogoutBody(performNativeLogoutBody PerformNativeLogoutBody) FrontendAPIPerformNativeLogoutRequest {\n\tr.performNativeLogoutBody = &performNativeLogoutBody\n\treturn r\n}\n\nfunc (r FrontendAPIPerformNativeLogoutRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.PerformNativeLogoutExecute(r)\n}\n\n/*\nPerformNativeLogout Perform Logout for Native Apps\n\nUse this endpoint to log out an identity using an Ory Session Token. If the Ory Session Token was successfully\nrevoked, the server returns a 204 No Content response. A 204 No Content response is also sent when\nthe Ory Session Token has been revoked already before.\n\nIf the Ory Session Token is malformed or does not exist a 403 Forbidden response will be returned.\n\nThis endpoint does not remove any HTTP\nCookies - use the Browser-Based Self-Service Logout Flow instead.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIPerformNativeLogoutRequest\n*/\nfunc (a *FrontendAPIService) PerformNativeLogout(ctx context.Context) FrontendAPIPerformNativeLogoutRequest {\n\treturn FrontendAPIPerformNativeLogoutRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\nfunc (a *FrontendAPIService) PerformNativeLogoutExecute(r FrontendAPIPerformNativeLogoutRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.PerformNativeLogout\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/logout/api\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.performNativeLogoutBody == nil {\n\t\treturn nil, reportError(\"performNativeLogoutBody is required and must be specified\")\n\t}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.performNativeLogoutBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype FrontendAPIToSessionRequest struct {\n\tctx           context.Context\n\tApiService    FrontendAPI\n\txSessionToken *string\n\tcookie        *string\n\ttokenizeAs    *string\n}\n\n// Set the Session Token when calling from non-browser clients. A session token has a format of &#x60;MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj&#x60;.\nfunc (r FrontendAPIToSessionRequest) XSessionToken(xSessionToken string) FrontendAPIToSessionRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that scenario you must include the HTTP Cookie Header which originally was included in the request to your server. An example of a session in the HTTP Cookie Header is: &#x60;ory_kratos_session&#x3D;a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f&#x3D;&#x3D;&#x60;.  It is ok if more than one cookie are included here as all other cookies will be ignored.\nfunc (r FrontendAPIToSessionRequest) Cookie(cookie string) FrontendAPIToSessionRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\n// Returns the session additionally as a token (such as a JWT)  The value of this parameter has to be a valid, configured Ory Session token template. For more information head over to [the documentation](http://ory.sh/docs/identities/session-to-jwt-cors).\nfunc (r FrontendAPIToSessionRequest) TokenizeAs(tokenizeAs string) FrontendAPIToSessionRequest {\n\tr.tokenizeAs = &tokenizeAs\n\treturn r\n}\n\nfunc (r FrontendAPIToSessionRequest) Execute() (*Session, *http.Response, error) {\n\treturn r.ApiService.ToSessionExecute(r)\n}\n\n/*\nToSession Check Who the Current HTTP Session Belongs To\n\nUses the HTTP Headers in the GET request to determine (e.g. by using checking the cookies) who is authenticated.\nReturns a session object in the body or 401 if the credentials are invalid or no credentials were sent.\nWhen the request it successful it adds the user ID to the 'X-Kratos-Authenticated-Identity-Id' header\nin the response.\n\nIf you call this endpoint from a server-side application, you must forward the HTTP Cookie Header to this endpoint:\n\n```js\npseudo-code example\nrouter.get('/protected-endpoint', async function (req, res) {\nconst session = await client.toSession(undefined, req.header('cookie'))\n\nconsole.log(session)\n})\n```\n\nWhen calling this endpoint from a non-browser application (e.g. mobile app) you must include the session token:\n\n```js\npseudo-code example\n...\nconst session = await client.toSession(\"the-session-token\")\n\nconsole.log(session)\n```\n\nWhen using a token template, the token is included in the `tokenized` field of the session.\n\n```js\npseudo-code example\n...\nconst session = await client.toSession(\"the-session-token\", { tokenize_as: \"example-jwt-template\" })\n\nconsole.log(session.tokenized) // The JWT\n```\n\nDepending on your configuration this endpoint might return a 403 status code if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor or change the configuration.\n\nThis endpoint is useful for:\n\nAJAX calls. Remember to send credentials and set up CORS correctly!\nReverse proxies and API Gateways\nServer-side calls - use the `X-Session-Token` header!\n\nThis endpoint authenticates users by checking:\n\nif the `Cookie` HTTP header was set containing an Ory Kratos Session Cookie;\nif the `Authorization: bearer <ory-session-token>` HTTP header was set with a valid Ory Kratos Session Token;\nif the `X-Session-Token` HTTP header was set with a valid Ory Kratos Session Token.\n\nIf none of these headers are set or the cookie or token are invalid, the endpoint returns a HTTP 401 status code.\n\nAs explained above, this request may fail due to several reasons. The `error.id` can be one of:\n\n`session_inactive`: No active session was found in the request (e.g. no Ory Session Cookie / Ory Session Token).\n`session_aal2_required`: An active session was found but it does not fulfil the Authenticator Assurance Level, implying that the session must (e.g.) authenticate the second factor.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIToSessionRequest\n*/\nfunc (a *FrontendAPIService) ToSession(ctx context.Context) FrontendAPIToSessionRequest {\n\treturn FrontendAPIToSessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Session\nfunc (a *FrontendAPIService) ToSessionExecute(r FrontendAPIToSessionRequest) (*Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.ToSession\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/sessions/whoami\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.tokenizeAs != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"tokenize_as\", r.tokenizeAs, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateFedcmFlowRequest struct {\n\tctx                 context.Context\n\tApiService          FrontendAPI\n\tupdateFedcmFlowBody *UpdateFedcmFlowBody\n}\n\nfunc (r FrontendAPIUpdateFedcmFlowRequest) UpdateFedcmFlowBody(updateFedcmFlowBody UpdateFedcmFlowBody) FrontendAPIUpdateFedcmFlowRequest {\n\tr.updateFedcmFlowBody = &updateFedcmFlowBody\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateFedcmFlowRequest) Execute() (*SuccessfulNativeLogin, *http.Response, error) {\n\treturn r.ApiService.UpdateFedcmFlowExecute(r)\n}\n\n/*\nUpdateFedcmFlow Submit a FedCM token\n\nUse this endpoint to submit a token from a FedCM provider through\n`navigator.credentials.get` and log the user in. The parameters from\n`navigator.credentials.get` must have come from `GET\nself-service/fed-cm/parameters`.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateFedcmFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateFedcmFlow(ctx context.Context) FrontendAPIUpdateFedcmFlowRequest {\n\treturn FrontendAPIUpdateFedcmFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SuccessfulNativeLogin\nfunc (a *FrontendAPIService) UpdateFedcmFlowExecute(r FrontendAPIUpdateFedcmFlowRequest) (*SuccessfulNativeLogin, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SuccessfulNativeLogin\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateFedcmFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/fed-cm/token\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.updateFedcmFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateFedcmFlowBody is required and must be specified\")\n\t}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateFedcmFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v LoginFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateLoginFlowRequest struct {\n\tctx                 context.Context\n\tApiService          FrontendAPI\n\tflow                *string\n\tupdateLoginFlowBody *UpdateLoginFlowBody\n\txSessionToken       *string\n\tcookie              *string\n}\n\n// The Login Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/login?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateLoginFlowRequest) Flow(flow string) FrontendAPIUpdateLoginFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateLoginFlowRequest) UpdateLoginFlowBody(updateLoginFlowBody UpdateLoginFlowBody) FrontendAPIUpdateLoginFlowRequest {\n\tr.updateLoginFlowBody = &updateLoginFlowBody\n\treturn r\n}\n\n// The Session Token of the Identity performing the settings flow.\nfunc (r FrontendAPIUpdateLoginFlowRequest) XSessionToken(xSessionToken string) FrontendAPIUpdateLoginFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateLoginFlowRequest) Cookie(cookie string) FrontendAPIUpdateLoginFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateLoginFlowRequest) Execute() (*SuccessfulNativeLogin, *http.Response, error) {\n\treturn r.ApiService.UpdateLoginFlowExecute(r)\n}\n\n/*\nUpdateLoginFlow Submit a Login Flow\n\nUse this endpoint to complete a login flow. This endpoint\nbehaves differently for API and browser flows.\n\nAPI flows expect `application/json` to be sent in the body and responds with\nHTTP 200 and a application/json body with the session token on success;\nHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\nHTTP 400 on form validation errors.\n\nBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\na HTTP 303 redirect to the post/after login URL or the `return_to` value if it was set and if the login succeeded;\na HTTP 303 redirect to the login UI URL with the flow ID containing the validation errors otherwise.\n\nBrowser flows with an accept header of `application/json` will not redirect but instead respond with\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\nHTTP 400 on form validation errors.\n\nIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\nMost likely used in Social Sign In flows.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateLoginFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateLoginFlow(ctx context.Context) FrontendAPIUpdateLoginFlowRequest {\n\treturn FrontendAPIUpdateLoginFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SuccessfulNativeLogin\nfunc (a *FrontendAPIService) UpdateLoginFlowExecute(r FrontendAPIUpdateLoginFlowRequest) (*SuccessfulNativeLogin, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SuccessfulNativeLogin\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateLoginFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/login\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateLoginFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateLoginFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateLoginFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v LoginFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateLogoutFlowRequest struct {\n\tctx        context.Context\n\tApiService FrontendAPI\n\ttoken      *string\n\treturnTo   *string\n\tcookie     *string\n}\n\n// A Valid Logout Token  If you do not have a logout token because you only have a session cookie, call &#x60;/self-service/logout/browser&#x60; to generate a URL for this endpoint.\nfunc (r FrontendAPIUpdateLogoutFlowRequest) Token(token string) FrontendAPIUpdateLogoutFlowRequest {\n\tr.token = &token\n\treturn r\n}\n\n// The URL to return to after the logout was completed.\nfunc (r FrontendAPIUpdateLogoutFlowRequest) ReturnTo(returnTo string) FrontendAPIUpdateLogoutFlowRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateLogoutFlowRequest) Cookie(cookie string) FrontendAPIUpdateLogoutFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateLogoutFlowRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.UpdateLogoutFlowExecute(r)\n}\n\n/*\nUpdateLogoutFlow Update Logout Flow\n\nThis endpoint logs out an identity in a self-service manner.\n\nIf the `Accept` HTTP header is not set to `application/json`, the browser will be redirected (HTTP 303 See Other)\nto the `return_to` parameter of the initial request or fall back to `urls.default_return_to`.\n\nIf the `Accept` HTTP header is set to `application/json`, a 204 No Content response\nwill be sent on successful logout instead.\n\nThis endpoint is NOT INTENDED for API clients and only works\nwith browsers (Chrome, Firefox, ...). For API clients you can\ncall the `/self-service/logout/api` URL directly with the Ory Session Token.\n\nMore information can be found at [Ory Kratos User Logout Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-logout).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateLogoutFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateLogoutFlow(ctx context.Context) FrontendAPIUpdateLogoutFlowRequest {\n\treturn FrontendAPIUpdateLogoutFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\nfunc (a *FrontendAPIService) UpdateLogoutFlowExecute(r FrontendAPIUpdateLogoutFlowRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodGet\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateLogoutFlow\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/logout\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.token != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"token\", r.token, \"form\", \"\")\n\t}\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateRecoveryFlowRequest struct {\n\tctx                    context.Context\n\tApiService             FrontendAPI\n\tflow                   *string\n\tupdateRecoveryFlowBody *UpdateRecoveryFlowBody\n\ttoken                  *string\n\tcookie                 *string\n}\n\n// The Recovery Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/recovery?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) Flow(flow string) FrontendAPIUpdateRecoveryFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) UpdateRecoveryFlowBody(updateRecoveryFlowBody UpdateRecoveryFlowBody) FrontendAPIUpdateRecoveryFlowRequest {\n\tr.updateRecoveryFlowBody = &updateRecoveryFlowBody\n\treturn r\n}\n\n// Recovery Token  The recovery token which completes the recovery request. If the token is invalid (e.g. expired) an error will be shown to the end-user.  This parameter is usually set in a link and not used by any direct API call.\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) Token(token string) FrontendAPIUpdateRecoveryFlowRequest {\n\tr.token = &token\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) Cookie(cookie string) FrontendAPIUpdateRecoveryFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateRecoveryFlowRequest) Execute() (*RecoveryFlow, *http.Response, error) {\n\treturn r.ApiService.UpdateRecoveryFlowExecute(r)\n}\n\n/*\nUpdateRecoveryFlow Update Recovery Flow\n\nUse this endpoint to update a recovery flow. This endpoint\nbehaves differently for API and browser flows and has several states:\n\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\nand works with API- and Browser-initiated flows.\nFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid.\nand a HTTP 303 See Other redirect with a fresh recovery flow if the flow was otherwise invalid (e.g. expired).\nFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Recovery UI URL with the Recovery Flow ID appended.\n`sent_email` is the success state after `choose_method` for the `link` method and allows the user to request another recovery email. It\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a recovery link\")\ndoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n(if the link was valid) and instructs the user to update their password, or a redirect to the Recover UI URL with\na new Recovery Flow ID which contains an error message that the recovery link was invalid.\n\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateRecoveryFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateRecoveryFlow(ctx context.Context) FrontendAPIUpdateRecoveryFlowRequest {\n\treturn FrontendAPIUpdateRecoveryFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryFlow\nfunc (a *FrontendAPIService) UpdateRecoveryFlowExecute(r FrontendAPIUpdateRecoveryFlowRequest) (*RecoveryFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateRecoveryFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/recovery\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateRecoveryFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateRecoveryFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\tif r.token != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"token\", r.token, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateRecoveryFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v RecoveryFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateRegistrationFlowRequest struct {\n\tctx                        context.Context\n\tApiService                 FrontendAPI\n\tflow                       *string\n\tupdateRegistrationFlowBody *UpdateRegistrationFlowBody\n\tcookie                     *string\n}\n\n// The Registration Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/registration?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateRegistrationFlowRequest) Flow(flow string) FrontendAPIUpdateRegistrationFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateRegistrationFlowRequest) UpdateRegistrationFlowBody(updateRegistrationFlowBody UpdateRegistrationFlowBody) FrontendAPIUpdateRegistrationFlowRequest {\n\tr.updateRegistrationFlowBody = &updateRegistrationFlowBody\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateRegistrationFlowRequest) Cookie(cookie string) FrontendAPIUpdateRegistrationFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateRegistrationFlowRequest) Execute() (*SuccessfulNativeRegistration, *http.Response, error) {\n\treturn r.ApiService.UpdateRegistrationFlowExecute(r)\n}\n\n/*\nUpdateRegistrationFlow Update Registration Flow\n\nUse this endpoint to complete a registration flow by sending an identity's traits and password. This endpoint\nbehaves differently for API and browser flows.\n\nAPI flows expect `application/json` to be sent in the body and respond with\nHTTP 200 and a application/json body with the created identity success - if the session hook is configured the\n`session` and `session_token` will also be included;\nHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\nHTTP 400 on form validation errors.\n\nBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\na HTTP 303 redirect to the post/after registration URL or the `return_to` value if it was set and if the registration succeeded;\na HTTP 303 redirect to the registration UI URL with the flow ID containing the validation errors otherwise.\n\nBrowser flows with an accept header of `application/json` will not redirect but instead respond with\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\nHTTP 400 on form validation errors.\n\nIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_already_available`: The user is already signed in.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\nMost likely used in Social Sign In flows.\n\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateRegistrationFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateRegistrationFlow(ctx context.Context) FrontendAPIUpdateRegistrationFlowRequest {\n\treturn FrontendAPIUpdateRegistrationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SuccessfulNativeRegistration\nfunc (a *FrontendAPIService) UpdateRegistrationFlowExecute(r FrontendAPIUpdateRegistrationFlowRequest) (*SuccessfulNativeRegistration, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SuccessfulNativeRegistration\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateRegistrationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/registration\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateRegistrationFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateRegistrationFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateRegistrationFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v RegistrationFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateSettingsFlowRequest struct {\n\tctx                    context.Context\n\tApiService             FrontendAPI\n\tflow                   *string\n\tupdateSettingsFlowBody *UpdateSettingsFlowBody\n\txSessionToken          *string\n\tcookie                 *string\n}\n\n// The Settings Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/settings?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateSettingsFlowRequest) Flow(flow string) FrontendAPIUpdateSettingsFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateSettingsFlowRequest) UpdateSettingsFlowBody(updateSettingsFlowBody UpdateSettingsFlowBody) FrontendAPIUpdateSettingsFlowRequest {\n\tr.updateSettingsFlowBody = &updateSettingsFlowBody\n\treturn r\n}\n\n// The Session Token of the Identity performing the settings flow.\nfunc (r FrontendAPIUpdateSettingsFlowRequest) XSessionToken(xSessionToken string) FrontendAPIUpdateSettingsFlowRequest {\n\tr.xSessionToken = &xSessionToken\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateSettingsFlowRequest) Cookie(cookie string) FrontendAPIUpdateSettingsFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateSettingsFlowRequest) Execute() (*SettingsFlow, *http.Response, error) {\n\treturn r.ApiService.UpdateSettingsFlowExecute(r)\n}\n\n/*\nUpdateSettingsFlow Complete Settings Flow\n\nUse this endpoint to complete a settings flow by sending an identity's updated password. This endpoint\nbehaves differently for API and browser flows.\n\nAPI-initiated flows expect `application/json` to be sent in the body and respond with\nHTTP 200 and an application/json body with the session token on success;\nHTTP 303 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\nHTTP 400 on form validation errors.\nHTTP 401 when the endpoint is called without a valid session token.\nHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\nImplies that the user needs to re-authenticate.\n\nBrowser flows without HTTP Header `Accept` or with `Accept: text/*` respond with\na HTTP 303 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\na HTTP 303 redirect to the Settings UI URL with the flow ID containing the validation errors otherwise.\na HTTP 303 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\n\nBrowser flows with HTTP Header `Accept: application/json` respond with\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\nHTTP 401 when the endpoint is called without a valid session cookie.\nHTTP 403 when the page is accessed without a session cookie or the session's AAL is too low.\nHTTP 400 on form validation errors.\n\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\nto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n\nIf this endpoint is called with a `Accept: application/json` HTTP header, the response contains the flow without a redirect. In the\ncase of an error, the `error.id` of the JSON response body can be one of:\n\n`session_refresh_required`: The identity requested to change something that needs a privileged session. Redirect\nthe identity to the login init endpoint with query parameters `?refresh=true&return_to=<the-current-browser-url>`,\nor initiate a refresh login flow otherwise.\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n`session_inactive`: No Ory Session was found - sign in a user first.\n`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\nidentity logged in instead.\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\nMost likely used in Social Sign In flows.\n\nMore information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateSettingsFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateSettingsFlow(ctx context.Context) FrontendAPIUpdateSettingsFlowRequest {\n\treturn FrontendAPIUpdateSettingsFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return SettingsFlow\nfunc (a *FrontendAPIService) UpdateSettingsFlowExecute(r FrontendAPIUpdateSettingsFlowRequest) (*SettingsFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *SettingsFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateSettingsFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/settings\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateSettingsFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateSettingsFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.xSessionToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"X-Session-Token\", r.xSessionToken, \"simple\", \"\")\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateSettingsFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v SettingsFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 403 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 422 {\n\t\t\tvar v ErrorBrowserLocationChangeRequired\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype FrontendAPIUpdateVerificationFlowRequest struct {\n\tctx                        context.Context\n\tApiService                 FrontendAPI\n\tflow                       *string\n\tupdateVerificationFlowBody *UpdateVerificationFlowBody\n\ttoken                      *string\n\tcookie                     *string\n}\n\n// The Verification Flow ID  The value for this parameter comes from &#x60;flow&#x60; URL Query parameter sent to your application (e.g. &#x60;/verification?flow&#x3D;abcde&#x60;).\nfunc (r FrontendAPIUpdateVerificationFlowRequest) Flow(flow string) FrontendAPIUpdateVerificationFlowRequest {\n\tr.flow = &flow\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateVerificationFlowRequest) UpdateVerificationFlowBody(updateVerificationFlowBody UpdateVerificationFlowBody) FrontendAPIUpdateVerificationFlowRequest {\n\tr.updateVerificationFlowBody = &updateVerificationFlowBody\n\treturn r\n}\n\n// Verification Token  The verification token which completes the verification request. If the token is invalid (e.g. expired) an error will be shown to the end-user.  This parameter is usually set in a link and not used by any direct API call.\nfunc (r FrontendAPIUpdateVerificationFlowRequest) Token(token string) FrontendAPIUpdateVerificationFlowRequest {\n\tr.token = &token\n\treturn r\n}\n\n// HTTP Cookies  When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header sent by the client to your server here. This ensures that CSRF and session cookies are respected.\nfunc (r FrontendAPIUpdateVerificationFlowRequest) Cookie(cookie string) FrontendAPIUpdateVerificationFlowRequest {\n\tr.cookie = &cookie\n\treturn r\n}\n\nfunc (r FrontendAPIUpdateVerificationFlowRequest) Execute() (*VerificationFlow, *http.Response, error) {\n\treturn r.ApiService.UpdateVerificationFlowExecute(r)\n}\n\n/*\nUpdateVerificationFlow Complete Verification Flow\n\nUse this endpoint to complete a verification flow. This endpoint\nbehaves differently for API and browser flows and has several states:\n\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\nand works with API- and Browser-initiated flows.\nFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\nand a HTTP 303 See Other redirect with a fresh verification flow if the flow was otherwise invalid (e.g. expired).\nFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Verification UI URL with the Verification Flow ID appended.\n`sent_email` is the success state after `choose_method` when using the `link` method and allows the user to request another verification email. It\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a verification link\")\ndoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n(if the link was valid) and instructs the user to update their password, or a redirect to the Verification UI URL with\na new Verification Flow ID which contains an error message that the verification link was invalid.\n\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return FrontendAPIUpdateVerificationFlowRequest\n*/\nfunc (a *FrontendAPIService) UpdateVerificationFlow(ctx context.Context) FrontendAPIUpdateVerificationFlowRequest {\n\treturn FrontendAPIUpdateVerificationFlowRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return VerificationFlow\nfunc (a *FrontendAPIService) UpdateVerificationFlowExecute(r FrontendAPIUpdateVerificationFlowRequest) (*VerificationFlow, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *VerificationFlow\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"FrontendAPIService.UpdateVerificationFlow\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/self-service/verification\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\tif r.flow == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"flow is required and must be specified\")\n\t}\n\tif r.updateVerificationFlowBody == nil {\n\t\treturn localVarReturnValue, nil, reportError(\"updateVerificationFlowBody is required and must be specified\")\n\t}\n\n\tparameterAddToHeaderOrQuery(localVarQueryParams, \"flow\", r.flow, \"form\", \"\")\n\tif r.token != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"token\", r.token, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\", \"application/x-www-form-urlencoded\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.cookie != nil {\n\t\tparameterAddToHeaderOrQuery(localVarHeaderParams, \"Cookie\", r.cookie, \"simple\", \"\")\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateVerificationFlowBody\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v VerificationFlow\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 410 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n"
  },
  {
    "path": "pkg/httpclient/api_identity.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n)\n\ntype IdentityAPI interface {\n\n\t/*\n\t\t\tBatchPatchIdentities Create multiple identities\n\n\t\t\tCreates multiple [identities](https://www.ory.com/docs/kratos/concepts/identity-user-model).\n\n\t\tYou can also use this endpoint to [import credentials](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities),\n\t\tincluding passwords, social sign-in settings, and multi-factor authentication methods.\n\n\t\tIf the patch includes hashed passwords you can import up to 1,000 identities per request.\n\n\t\tIf the patch includes at least one plaintext password you can import up to 200 identities per request.\n\n\t\tAvoid importing large batches with plaintext passwords. They can cause timeouts as the passwords need to be hashed before they are stored.\n\n\t\tIf at least one identity is imported successfully, the response status is 200 OK.\n\t\tIf all imports fail, the response is one of the following 4xx errors:\n\t\t400 Bad Request: The request payload is invalid or improperly formatted.\n\t\t409 Conflict: Duplicate identities or conflicting data were detected.\n\n\t\tIf you get a 504 Gateway Timeout:\n\t\tReduce the batch size\n\t\tAvoid duplicate identities\n\t\tPre-hash passwords with BCrypt\n\n\t\tIf the issue persists, contact support.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return IdentityAPIBatchPatchIdentitiesRequest\n\t*/\n\tBatchPatchIdentities(ctx context.Context) IdentityAPIBatchPatchIdentitiesRequest\n\n\t// BatchPatchIdentitiesExecute executes the request\n\t//  @return BatchPatchIdentitiesResponse\n\tBatchPatchIdentitiesExecute(r IdentityAPIBatchPatchIdentitiesRequest) (*BatchPatchIdentitiesResponse, *http.Response, error)\n\n\t/*\n\t\t\tCreateIdentity Create an Identity\n\n\t\t\tCreate an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model).  This endpoint can also be used to\n\t\t[import credentials](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities)\n\t\tfor instance passwords, social sign in configurations or multifactor methods.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return IdentityAPICreateIdentityRequest\n\t*/\n\tCreateIdentity(ctx context.Context) IdentityAPICreateIdentityRequest\n\n\t// CreateIdentityExecute executes the request\n\t//  @return Identity\n\tCreateIdentityExecute(r IdentityAPICreateIdentityRequest) (*Identity, *http.Response, error)\n\n\t/*\n\t\t\tCreateRecoveryCodeForIdentity Create a Recovery Code\n\n\t\t\tThis endpoint creates a recovery code which should be given to the user in order for them to recover\n\t\t(or activate) their account.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return IdentityAPICreateRecoveryCodeForIdentityRequest\n\t*/\n\tCreateRecoveryCodeForIdentity(ctx context.Context) IdentityAPICreateRecoveryCodeForIdentityRequest\n\n\t// CreateRecoveryCodeForIdentityExecute executes the request\n\t//  @return RecoveryCodeForIdentity\n\tCreateRecoveryCodeForIdentityExecute(r IdentityAPICreateRecoveryCodeForIdentityRequest) (*RecoveryCodeForIdentity, *http.Response, error)\n\n\t/*\n\t\t\tCreateRecoveryLinkForIdentity Create a Recovery Link\n\n\t\t\tThis endpoint creates a recovery link which should be given to the user in order for them to recover\n\t\t(or activate) their account.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return IdentityAPICreateRecoveryLinkForIdentityRequest\n\t*/\n\tCreateRecoveryLinkForIdentity(ctx context.Context) IdentityAPICreateRecoveryLinkForIdentityRequest\n\n\t// CreateRecoveryLinkForIdentityExecute executes the request\n\t//  @return RecoveryLinkForIdentity\n\tCreateRecoveryLinkForIdentityExecute(r IdentityAPICreateRecoveryLinkForIdentityRequest) (*RecoveryLinkForIdentity, *http.Response, error)\n\n\t/*\n\t\t\tDeleteIdentity Delete an Identity\n\n\t\t\tCalling this endpoint irrecoverably and permanently deletes the [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) given its ID. This action can not be undone.\n\t\tThis endpoint returns 204 when the identity was deleted or 404 if the identity was not found.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the identity's ID.\n\t\t\t@return IdentityAPIDeleteIdentityRequest\n\t*/\n\tDeleteIdentity(ctx context.Context, id string) IdentityAPIDeleteIdentityRequest\n\n\t// DeleteIdentityExecute executes the request\n\tDeleteIdentityExecute(r IdentityAPIDeleteIdentityRequest) (*http.Response, error)\n\n\t/*\n\t\t\tDeleteIdentityCredentials Delete a credential for a specific identity\n\n\t\t\tDelete an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) credential by its type.\n\t\tYou cannot delete passkeys or code auth credentials through this API.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the identity's ID.\n\t\t\t@param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\t\t\t@return IdentityAPIDeleteIdentityCredentialsRequest\n\t*/\n\tDeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIDeleteIdentityCredentialsRequest\n\n\t// DeleteIdentityCredentialsExecute executes the request\n\tDeleteIdentityCredentialsExecute(r IdentityAPIDeleteIdentityCredentialsRequest) (*http.Response, error)\n\n\t/*\n\t\tDeleteIdentitySessions Delete & Invalidate an Identity's Sessions\n\n\t\tCalling this endpoint irrecoverably and permanently deletes and invalidates all sessions that belong to the given Identity.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id ID is the identity's ID.\n\t\t@return IdentityAPIDeleteIdentitySessionsRequest\n\t*/\n\tDeleteIdentitySessions(ctx context.Context, id string) IdentityAPIDeleteIdentitySessionsRequest\n\n\t// DeleteIdentitySessionsExecute executes the request\n\tDeleteIdentitySessionsExecute(r IdentityAPIDeleteIdentitySessionsRequest) (*http.Response, error)\n\n\t/*\n\t\tDisableSession Deactivate a Session\n\n\t\tCalling this endpoint deactivates the specified session. Session data is not deleted.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id ID is the session's ID.\n\t\t@return IdentityAPIDisableSessionRequest\n\t*/\n\tDisableSession(ctx context.Context, id string) IdentityAPIDisableSessionRequest\n\n\t// DisableSessionExecute executes the request\n\tDisableSessionExecute(r IdentityAPIDisableSessionRequest) (*http.Response, error)\n\n\t/*\n\t\t\tExtendSession Extend a Session\n\n\t\t\tCalling this endpoint extends the given session ID. If `session.earliest_possible_extend` is set it\n\t\twill only extend the session after the specified time has passed.\n\n\t\tThis endpoint returns per default a 204 No Content response on success. Older Ory Network projects may\n\t\treturn a 200 OK response with the session in the body. Returning the session as part of the response\n\t\twill be deprecated in the future and should not be relied upon.\n\n\t\tThis endpoint ignores consecutive requests to extend the same session and returns a 404 error in those\n\t\tscenarios. This endpoint also returns 404 errors if the session does not exist.\n\n\t\tRetrieve the session ID from the `/sessions/whoami` endpoint / `toSession` SDK method.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the session's ID.\n\t\t\t@return IdentityAPIExtendSessionRequest\n\t*/\n\tExtendSession(ctx context.Context, id string) IdentityAPIExtendSessionRequest\n\n\t// ExtendSessionExecute executes the request\n\t//  @return Session\n\tExtendSessionExecute(r IdentityAPIExtendSessionRequest) (*Session, *http.Response, error)\n\n\t/*\n\t\t\tGetIdentity Get an Identity\n\n\t\t\tReturn an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its ID. You can optionally\n\t\tinclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID must be set to the ID of identity you want to get\n\t\t\t@return IdentityAPIGetIdentityRequest\n\t*/\n\tGetIdentity(ctx context.Context, id string) IdentityAPIGetIdentityRequest\n\n\t// GetIdentityExecute executes the request\n\t//  @return Identity\n\tGetIdentityExecute(r IdentityAPIGetIdentityRequest) (*Identity, *http.Response, error)\n\n\t/*\n\t\t\tGetIdentityByExternalID Get an Identity by its External ID\n\n\t\t\tReturn an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its external ID. You can optionally\n\t\tinclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param externalID ExternalID must be set to the ID of identity you want to get\n\t\t\t@return IdentityAPIGetIdentityByExternalIDRequest\n\t*/\n\tGetIdentityByExternalID(ctx context.Context, externalID string) IdentityAPIGetIdentityByExternalIDRequest\n\n\t// GetIdentityByExternalIDExecute executes the request\n\t//  @return Identity\n\tGetIdentityByExternalIDExecute(r IdentityAPIGetIdentityByExternalIDRequest) (*Identity, *http.Response, error)\n\n\t/*\n\t\tGetIdentitySchema Get Identity JSON Schema\n\n\t\tReturn a specific identity schema.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id ID must be set to the ID of schema you want to get\n\t\t@return IdentityAPIGetIdentitySchemaRequest\n\t*/\n\tGetIdentitySchema(ctx context.Context, id string) IdentityAPIGetIdentitySchemaRequest\n\n\t// GetIdentitySchemaExecute executes the request\n\t//  @return map[string]interface{}\n\tGetIdentitySchemaExecute(r IdentityAPIGetIdentitySchemaRequest) (map[string]interface{}, *http.Response, error)\n\n\t/*\n\t\t\tGetSession Get Session\n\n\t\t\tThis endpoint is useful for:\n\n\t\tGetting a session object with all specified expandables that exist in an administrative context.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID is the session's ID.\n\t\t\t@return IdentityAPIGetSessionRequest\n\t*/\n\tGetSession(ctx context.Context, id string) IdentityAPIGetSessionRequest\n\n\t// GetSessionExecute executes the request\n\t//  @return Session\n\tGetSessionExecute(r IdentityAPIGetSessionRequest) (*Session, *http.Response, error)\n\n\t/*\n\t\tListIdentities List Identities\n\n\t\tLists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return IdentityAPIListIdentitiesRequest\n\t*/\n\tListIdentities(ctx context.Context) IdentityAPIListIdentitiesRequest\n\n\t// ListIdentitiesExecute executes the request\n\t//  @return []Identity\n\tListIdentitiesExecute(r IdentityAPIListIdentitiesRequest) ([]Identity, *http.Response, error)\n\n\t/*\n\t\tListIdentitySchemas Get all Identity Schemas\n\n\t\tReturns a list of all identity schemas currently in use.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return IdentityAPIListIdentitySchemasRequest\n\t*/\n\tListIdentitySchemas(ctx context.Context) IdentityAPIListIdentitySchemasRequest\n\n\t// ListIdentitySchemasExecute executes the request\n\t//  @return []IdentitySchemaContainer\n\tListIdentitySchemasExecute(r IdentityAPIListIdentitySchemasRequest) ([]IdentitySchemaContainer, *http.Response, error)\n\n\t/*\n\t\tListIdentitySessions List an Identity's Sessions\n\n\t\tThis endpoint returns all sessions that belong to the given Identity.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@param id ID is the identity's ID.\n\t\t@return IdentityAPIListIdentitySessionsRequest\n\t*/\n\tListIdentitySessions(ctx context.Context, id string) IdentityAPIListIdentitySessionsRequest\n\n\t// ListIdentitySessionsExecute executes the request\n\t//  @return []Session\n\tListIdentitySessionsExecute(r IdentityAPIListIdentitySessionsRequest) ([]Session, *http.Response, error)\n\n\t/*\n\t\tListSessions List All Sessions\n\n\t\tListing all sessions that exist.\n\n\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t@return IdentityAPIListSessionsRequest\n\t*/\n\tListSessions(ctx context.Context) IdentityAPIListSessionsRequest\n\n\t// ListSessionsExecute executes the request\n\t//  @return []Session\n\tListSessionsExecute(r IdentityAPIListSessionsRequest) ([]Session, *http.Response, error)\n\n\t/*\n\t\t\tPatchIdentity Patch an Identity\n\n\t\t\tPartially updates an [identity's](https://www.ory.sh/docs/kratos/concepts/identity-user-model) field using [JSON Patch](https://jsonpatch.com/).\n\t\tThe fields `id`, `stateChangedAt` and `credentials` can not be updated using this method.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID must be set to the ID of identity you want to update\n\t\t\t@return IdentityAPIPatchIdentityRequest\n\t*/\n\tPatchIdentity(ctx context.Context, id string) IdentityAPIPatchIdentityRequest\n\n\t// PatchIdentityExecute executes the request\n\t//  @return Identity\n\tPatchIdentityExecute(r IdentityAPIPatchIdentityRequest) (*Identity, *http.Response, error)\n\n\t/*\n\t\t\tUpdateIdentity Update an Identity\n\n\t\t\tThis endpoint updates an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model). The full identity\n\t\tpayload, except credentials, is expected. For partial updates, use the [patchIdentity](https://www.ory.sh/docs/reference/api#tag/identity/operation/patchIdentity) operation.\n\n\t\tA credential can be provided via the `credentials` field in the request body.\n\t\tIf provided, the credentials will be imported and added to the existing credentials of the identity.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@param id ID must be set to the ID of identity you want to update\n\t\t\t@return IdentityAPIUpdateIdentityRequest\n\t*/\n\tUpdateIdentity(ctx context.Context, id string) IdentityAPIUpdateIdentityRequest\n\n\t// UpdateIdentityExecute executes the request\n\t//  @return Identity\n\tUpdateIdentityExecute(r IdentityAPIUpdateIdentityRequest) (*Identity, *http.Response, error)\n}\n\n// IdentityAPIService IdentityAPI service\ntype IdentityAPIService service\n\ntype IdentityAPIBatchPatchIdentitiesRequest struct {\n\tctx                 context.Context\n\tApiService          IdentityAPI\n\tpatchIdentitiesBody *PatchIdentitiesBody\n}\n\nfunc (r IdentityAPIBatchPatchIdentitiesRequest) PatchIdentitiesBody(patchIdentitiesBody PatchIdentitiesBody) IdentityAPIBatchPatchIdentitiesRequest {\n\tr.patchIdentitiesBody = &patchIdentitiesBody\n\treturn r\n}\n\nfunc (r IdentityAPIBatchPatchIdentitiesRequest) Execute() (*BatchPatchIdentitiesResponse, *http.Response, error) {\n\treturn r.ApiService.BatchPatchIdentitiesExecute(r)\n}\n\n/*\nBatchPatchIdentities Create multiple identities\n\nCreates multiple [identities](https://www.ory.com/docs/kratos/concepts/identity-user-model).\n\nYou can also use this endpoint to [import credentials](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities),\nincluding passwords, social sign-in settings, and multi-factor authentication methods.\n\nIf the patch includes hashed passwords you can import up to 1,000 identities per request.\n\nIf the patch includes at least one plaintext password you can import up to 200 identities per request.\n\nAvoid importing large batches with plaintext passwords. They can cause timeouts as the passwords need to be hashed before they are stored.\n\nIf at least one identity is imported successfully, the response status is 200 OK.\nIf all imports fail, the response is one of the following 4xx errors:\n400 Bad Request: The request payload is invalid or improperly formatted.\n409 Conflict: Duplicate identities or conflicting data were detected.\n\nIf you get a 504 Gateway Timeout:\nReduce the batch size\nAvoid duplicate identities\nPre-hash passwords with BCrypt\n\nIf the issue persists, contact support.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPIBatchPatchIdentitiesRequest\n*/\nfunc (a *IdentityAPIService) BatchPatchIdentities(ctx context.Context) IdentityAPIBatchPatchIdentitiesRequest {\n\treturn IdentityAPIBatchPatchIdentitiesRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return BatchPatchIdentitiesResponse\nfunc (a *IdentityAPIService) BatchPatchIdentitiesExecute(r IdentityAPIBatchPatchIdentitiesRequest) (*BatchPatchIdentitiesResponse, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPatch\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *BatchPatchIdentitiesResponse\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.BatchPatchIdentities\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.patchIdentitiesBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 409 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPICreateIdentityRequest struct {\n\tctx                context.Context\n\tApiService         IdentityAPI\n\tcreateIdentityBody *CreateIdentityBody\n}\n\nfunc (r IdentityAPICreateIdentityRequest) CreateIdentityBody(createIdentityBody CreateIdentityBody) IdentityAPICreateIdentityRequest {\n\tr.createIdentityBody = &createIdentityBody\n\treturn r\n}\n\nfunc (r IdentityAPICreateIdentityRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.CreateIdentityExecute(r)\n}\n\n/*\nCreateIdentity Create an Identity\n\nCreate an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model).  This endpoint can also be used to\n[import credentials](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities)\nfor instance passwords, social sign in configurations or multifactor methods.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPICreateIdentityRequest\n*/\nfunc (a *IdentityAPIService) CreateIdentity(ctx context.Context) IdentityAPICreateIdentityRequest {\n\treturn IdentityAPICreateIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) CreateIdentityExecute(r IdentityAPICreateIdentityRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.CreateIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.createIdentityBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 409 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPICreateRecoveryCodeForIdentityRequest struct {\n\tctx                               context.Context\n\tApiService                        IdentityAPI\n\tcreateRecoveryCodeForIdentityBody *CreateRecoveryCodeForIdentityBody\n}\n\nfunc (r IdentityAPICreateRecoveryCodeForIdentityRequest) CreateRecoveryCodeForIdentityBody(createRecoveryCodeForIdentityBody CreateRecoveryCodeForIdentityBody) IdentityAPICreateRecoveryCodeForIdentityRequest {\n\tr.createRecoveryCodeForIdentityBody = &createRecoveryCodeForIdentityBody\n\treturn r\n}\n\nfunc (r IdentityAPICreateRecoveryCodeForIdentityRequest) Execute() (*RecoveryCodeForIdentity, *http.Response, error) {\n\treturn r.ApiService.CreateRecoveryCodeForIdentityExecute(r)\n}\n\n/*\nCreateRecoveryCodeForIdentity Create a Recovery Code\n\nThis endpoint creates a recovery code which should be given to the user in order for them to recover\n(or activate) their account.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPICreateRecoveryCodeForIdentityRequest\n*/\nfunc (a *IdentityAPIService) CreateRecoveryCodeForIdentity(ctx context.Context) IdentityAPICreateRecoveryCodeForIdentityRequest {\n\treturn IdentityAPICreateRecoveryCodeForIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryCodeForIdentity\nfunc (a *IdentityAPIService) CreateRecoveryCodeForIdentityExecute(r IdentityAPICreateRecoveryCodeForIdentityRequest) (*RecoveryCodeForIdentity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryCodeForIdentity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.CreateRecoveryCodeForIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/recovery/code\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.createRecoveryCodeForIdentityBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPICreateRecoveryLinkForIdentityRequest struct {\n\tctx                               context.Context\n\tApiService                        IdentityAPI\n\treturnTo                          *string\n\tcreateRecoveryLinkForIdentityBody *CreateRecoveryLinkForIdentityBody\n}\n\nfunc (r IdentityAPICreateRecoveryLinkForIdentityRequest) ReturnTo(returnTo string) IdentityAPICreateRecoveryLinkForIdentityRequest {\n\tr.returnTo = &returnTo\n\treturn r\n}\n\nfunc (r IdentityAPICreateRecoveryLinkForIdentityRequest) CreateRecoveryLinkForIdentityBody(createRecoveryLinkForIdentityBody CreateRecoveryLinkForIdentityBody) IdentityAPICreateRecoveryLinkForIdentityRequest {\n\tr.createRecoveryLinkForIdentityBody = &createRecoveryLinkForIdentityBody\n\treturn r\n}\n\nfunc (r IdentityAPICreateRecoveryLinkForIdentityRequest) Execute() (*RecoveryLinkForIdentity, *http.Response, error) {\n\treturn r.ApiService.CreateRecoveryLinkForIdentityExecute(r)\n}\n\n/*\nCreateRecoveryLinkForIdentity Create a Recovery Link\n\nThis endpoint creates a recovery link which should be given to the user in order for them to recover\n(or activate) their account.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPICreateRecoveryLinkForIdentityRequest\n*/\nfunc (a *IdentityAPIService) CreateRecoveryLinkForIdentity(ctx context.Context) IdentityAPICreateRecoveryLinkForIdentityRequest {\n\treturn IdentityAPICreateRecoveryLinkForIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return RecoveryLinkForIdentity\nfunc (a *IdentityAPIService) CreateRecoveryLinkForIdentityExecute(r IdentityAPICreateRecoveryLinkForIdentityRequest) (*RecoveryLinkForIdentity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPost\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *RecoveryLinkForIdentity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.CreateRecoveryLinkForIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/recovery/link\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.returnTo != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"return_to\", r.returnTo, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.createRecoveryLinkForIdentityBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIDeleteIdentityRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIDeleteIdentityRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DeleteIdentityExecute(r)\n}\n\n/*\nDeleteIdentity Delete an Identity\n\nCalling this endpoint irrecoverably and permanently deletes the [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) given its ID. This action can not be undone.\nThis endpoint returns 204 when the identity was deleted or 404 if the identity was not found.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the identity's ID.\n\t@return IdentityAPIDeleteIdentityRequest\n*/\nfunc (a *IdentityAPIService) DeleteIdentity(ctx context.Context, id string) IdentityAPIDeleteIdentityRequest {\n\treturn IdentityAPIDeleteIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\nfunc (a *IdentityAPIService) DeleteIdentityExecute(r IdentityAPIDeleteIdentityRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.DeleteIdentity\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype IdentityAPIDeleteIdentityCredentialsRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n\ttype_      string\n\tidentifier *string\n}\n\n// Identifier is the identifier of the OIDC/SAML credential to delete. Find the identifier by calling the &#x60;GET /admin/identities/{id}?include_credential&#x3D;{oidc,saml}&#x60; endpoint.\nfunc (r IdentityAPIDeleteIdentityCredentialsRequest) Identifier(identifier string) IdentityAPIDeleteIdentityCredentialsRequest {\n\tr.identifier = &identifier\n\treturn r\n}\n\nfunc (r IdentityAPIDeleteIdentityCredentialsRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DeleteIdentityCredentialsExecute(r)\n}\n\n/*\nDeleteIdentityCredentials Delete a credential for a specific identity\n\nDelete an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) credential by its type.\nYou cannot delete passkeys or code auth credentials through this API.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the identity's ID.\n\t@param type_ Type is the type of credentials to delete. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\t@return IdentityAPIDeleteIdentityCredentialsRequest\n*/\nfunc (a *IdentityAPIService) DeleteIdentityCredentials(ctx context.Context, id string, type_ string) IdentityAPIDeleteIdentityCredentialsRequest {\n\treturn IdentityAPIDeleteIdentityCredentialsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t\ttype_:      type_,\n\t}\n}\n\n// Execute executes the request\nfunc (a *IdentityAPIService) DeleteIdentityCredentialsExecute(r IdentityAPIDeleteIdentityCredentialsRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.DeleteIdentityCredentials\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}/credentials/{type}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"type\"+\"}\", url.PathEscape(parameterValueToString(r.type_, \"type_\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.identifier != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"identifier\", r.identifier, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype IdentityAPIDeleteIdentitySessionsRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIDeleteIdentitySessionsRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DeleteIdentitySessionsExecute(r)\n}\n\n/*\nDeleteIdentitySessions Delete & Invalidate an Identity's Sessions\n\nCalling this endpoint irrecoverably and permanently deletes and invalidates all sessions that belong to the given Identity.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the identity's ID.\n\t@return IdentityAPIDeleteIdentitySessionsRequest\n*/\nfunc (a *IdentityAPIService) DeleteIdentitySessions(ctx context.Context, id string) IdentityAPIDeleteIdentitySessionsRequest {\n\treturn IdentityAPIDeleteIdentitySessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\nfunc (a *IdentityAPIService) DeleteIdentitySessionsExecute(r IdentityAPIDeleteIdentitySessionsRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.DeleteIdentitySessions\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}/sessions\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype IdentityAPIDisableSessionRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIDisableSessionRequest) Execute() (*http.Response, error) {\n\treturn r.ApiService.DisableSessionExecute(r)\n}\n\n/*\nDisableSession Deactivate a Session\n\nCalling this endpoint deactivates the specified session. Session data is not deleted.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the session's ID.\n\t@return IdentityAPIDisableSessionRequest\n*/\nfunc (a *IdentityAPIService) DisableSession(ctx context.Context, id string) IdentityAPIDisableSessionRequest {\n\treturn IdentityAPIDisableSessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\nfunc (a *IdentityAPIService) DisableSessionExecute(r IdentityAPIDisableSessionRequest) (*http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod = http.MethodDelete\n\t\tlocalVarPostBody   interface{}\n\t\tformFiles          []formFile\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.DisableSession\")\n\tif err != nil {\n\t\treturn nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/sessions/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 401 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarHTTPResponse, nil\n}\n\ntype IdentityAPIExtendSessionRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIExtendSessionRequest) Execute() (*Session, *http.Response, error) {\n\treturn r.ApiService.ExtendSessionExecute(r)\n}\n\n/*\nExtendSession Extend a Session\n\nCalling this endpoint extends the given session ID. If `session.earliest_possible_extend` is set it\nwill only extend the session after the specified time has passed.\n\nThis endpoint returns per default a 204 No Content response on success. Older Ory Network projects may\nreturn a 200 OK response with the session in the body. Returning the session as part of the response\nwill be deprecated in the future and should not be relied upon.\n\nThis endpoint ignores consecutive requests to extend the same session and returns a 404 error in those\nscenarios. This endpoint also returns 404 errors if the session does not exist.\n\nRetrieve the session ID from the `/sessions/whoami` endpoint / `toSession` SDK method.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the session's ID.\n\t@return IdentityAPIExtendSessionRequest\n*/\nfunc (a *IdentityAPIService) ExtendSession(ctx context.Context, id string) IdentityAPIExtendSessionRequest {\n\treturn IdentityAPIExtendSessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Session\nfunc (a *IdentityAPIService) ExtendSessionExecute(r IdentityAPIExtendSessionRequest) (*Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPatch\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ExtendSession\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/sessions/{id}/extend\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIGetIdentityRequest struct {\n\tctx               context.Context\n\tApiService        IdentityAPI\n\tid                string\n\tincludeCredential *[]string\n}\n\n// Include Credentials in Response  Include any credential, for example &#x60;password&#x60; or &#x60;oidc&#x60;, in the response. When set to &#x60;oidc&#x60;, This will return the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\nfunc (r IdentityAPIGetIdentityRequest) IncludeCredential(includeCredential []string) IdentityAPIGetIdentityRequest {\n\tr.includeCredential = &includeCredential\n\treturn r\n}\n\nfunc (r IdentityAPIGetIdentityRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.GetIdentityExecute(r)\n}\n\n/*\nGetIdentity Get an Identity\n\nReturn an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its ID. You can optionally\ninclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID must be set to the ID of identity you want to get\n\t@return IdentityAPIGetIdentityRequest\n*/\nfunc (a *IdentityAPIService) GetIdentity(ctx context.Context, id string) IdentityAPIGetIdentityRequest {\n\treturn IdentityAPIGetIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) GetIdentityExecute(r IdentityAPIGetIdentityRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.GetIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.includeCredential != nil {\n\t\tt := *r.includeCredential\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIGetIdentityByExternalIDRequest struct {\n\tctx               context.Context\n\tApiService        IdentityAPI\n\texternalID        string\n\tincludeCredential *[]string\n}\n\n// Include Credentials in Response  Include any credential, for example &#x60;password&#x60; or &#x60;oidc&#x60;, in the response. When set to &#x60;oidc&#x60;, This will return the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\nfunc (r IdentityAPIGetIdentityByExternalIDRequest) IncludeCredential(includeCredential []string) IdentityAPIGetIdentityByExternalIDRequest {\n\tr.includeCredential = &includeCredential\n\treturn r\n}\n\nfunc (r IdentityAPIGetIdentityByExternalIDRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.GetIdentityByExternalIDExecute(r)\n}\n\n/*\nGetIdentityByExternalID Get an Identity by its External ID\n\nReturn an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its external ID. You can optionally\ninclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param externalID ExternalID must be set to the ID of identity you want to get\n\t@return IdentityAPIGetIdentityByExternalIDRequest\n*/\nfunc (a *IdentityAPIService) GetIdentityByExternalID(ctx context.Context, externalID string) IdentityAPIGetIdentityByExternalIDRequest {\n\treturn IdentityAPIGetIdentityByExternalIDRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\texternalID: externalID,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) GetIdentityByExternalIDExecute(r IdentityAPIGetIdentityByExternalIDRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.GetIdentityByExternalID\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/by/external/{externalID}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"externalID\"+\"}\", url.PathEscape(parameterValueToString(r.externalID, \"externalID\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.includeCredential != nil {\n\t\tt := *r.includeCredential\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIGetIdentitySchemaRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n}\n\nfunc (r IdentityAPIGetIdentitySchemaRequest) Execute() (map[string]interface{}, *http.Response, error) {\n\treturn r.ApiService.GetIdentitySchemaExecute(r)\n}\n\n/*\nGetIdentitySchema Get Identity JSON Schema\n\nReturn a specific identity schema.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID must be set to the ID of schema you want to get\n\t@return IdentityAPIGetIdentitySchemaRequest\n*/\nfunc (a *IdentityAPIService) GetIdentitySchema(ctx context.Context, id string) IdentityAPIGetIdentitySchemaRequest {\n\treturn IdentityAPIGetIdentitySchemaRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return map[string]interface{}\nfunc (a *IdentityAPIService) GetIdentitySchemaExecute(r IdentityAPIGetIdentitySchemaRequest) (map[string]interface{}, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue map[string]interface{}\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.GetIdentitySchema\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/schemas/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIGetSessionRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n\texpand     *[]string\n}\n\n// ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session. Example - ?expand&#x3D;Identity&amp;expand&#x3D;Devices If no value is provided, the expandable properties are skipped.\nfunc (r IdentityAPIGetSessionRequest) Expand(expand []string) IdentityAPIGetSessionRequest {\n\tr.expand = &expand\n\treturn r\n}\n\nfunc (r IdentityAPIGetSessionRequest) Execute() (*Session, *http.Response, error) {\n\treturn r.ApiService.GetSessionExecute(r)\n}\n\n/*\nGetSession Get Session\n\nThis endpoint is useful for:\n\nGetting a session object with all specified expandables that exist in an administrative context.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the session's ID.\n\t@return IdentityAPIGetSessionRequest\n*/\nfunc (a *IdentityAPIService) GetSession(ctx context.Context, id string) IdentityAPIGetSessionRequest {\n\treturn IdentityAPIGetSessionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Session\nfunc (a *IdentityAPIService) GetSessionExecute(r IdentityAPIGetSessionRequest) (*Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.GetSession\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/sessions/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.expand != nil {\n\t\tt := *r.expand\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"expand\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"expand\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIListIdentitiesRequest struct {\n\tctx                                 context.Context\n\tApiService                          IdentityAPI\n\tperPage                             *int64\n\tpage                                *int64\n\tpageSize                            *int64\n\tpageToken                           *string\n\tconsistency                         *string\n\tids                                 *[]string\n\tcredentialsIdentifier               *string\n\tpreviewCredentialsIdentifierSimilar *string\n\tincludeCredential                   *[]string\n\torganizationId                      *string\n}\n\n// Deprecated Items per Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This is the number of items per page.\nfunc (r IdentityAPIListIdentitiesRequest) PerPage(perPage int64) IdentityAPIListIdentitiesRequest {\n\tr.perPage = &perPage\n\treturn r\n}\n\n// Deprecated Pagination Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This value is currently an integer, but it is not sequential. The value is not the page number, but a reference. The next page can be any number and some numbers might return an empty list.  For example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist. The first page can be retrieved by omitting this parameter. Following page pointers will be returned in the &#x60;Link&#x60; header.\nfunc (r IdentityAPIListIdentitiesRequest) Page(page int64) IdentityAPIListIdentitiesRequest {\n\tr.page = &page\n\treturn r\n}\n\n// Page Size  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitiesRequest) PageSize(pageSize int64) IdentityAPIListIdentitiesRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitiesRequest) PageToken(pageToken string) IdentityAPIListIdentitiesRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Read Consistency Level (preview)  The read consistency level determines the consistency guarantee for reads:  strong (slow): The read is guaranteed to return the most recent data committed at the start of the read. eventual (very fast): The result will return data that is about 4.8 seconds old.  The default consistency guarantee can be changed in the Ory Network Console or using the Ory CLI with &#x60;ory patch project --replace &#39;/previews/default_read_consistency_level&#x3D;\\&quot;strong\\&quot;&#39;&#x60;.  Setting the default consistency level to &#x60;eventual&#x60; may cause regressions in the future as we add consistency controls to more APIs. Currently, the following APIs will be affected by this setting:  &#x60;GET /admin/identities&#x60;  This feature is in preview and only available in Ory Network.  ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level. strong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level. eventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\nfunc (r IdentityAPIListIdentitiesRequest) Consistency(consistency string) IdentityAPIListIdentitiesRequest {\n\tr.consistency = &consistency\n\treturn r\n}\n\n// Retrieve multiple identities by their IDs.  This parameter has the following limitations:  Duplicate or non-existent IDs are ignored. The order of returned IDs may be different from the request. This filter does not support pagination. You must implement your own pagination as the maximum number of items returned by this endpoint may not exceed a certain threshold (currently 500).\nfunc (r IdentityAPIListIdentitiesRequest) Ids(ids []string) IdentityAPIListIdentitiesRequest {\n\tr.ids = &ids\n\treturn r\n}\n\n// CredentialsIdentifier is the identifier (username, email) of the credentials to look up using exact match. Only one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\nfunc (r IdentityAPIListIdentitiesRequest) CredentialsIdentifier(credentialsIdentifier string) IdentityAPIListIdentitiesRequest {\n\tr.credentialsIdentifier = &credentialsIdentifier\n\treturn r\n}\n\n// This is an EXPERIMENTAL parameter that WILL CHANGE. Do NOT rely on consistent, deterministic behavior. THIS PARAMETER WILL BE REMOVED IN AN UPCOMING RELEASE WITHOUT ANY MIGRATION PATH.  CredentialsIdentifierSimilar is the (partial) identifier (username, email) of the credentials to look up using similarity search. Only one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\nfunc (r IdentityAPIListIdentitiesRequest) PreviewCredentialsIdentifierSimilar(previewCredentialsIdentifierSimilar string) IdentityAPIListIdentitiesRequest {\n\tr.previewCredentialsIdentifierSimilar = &previewCredentialsIdentifierSimilar\n\treturn r\n}\n\n// Include Credentials in Response  Include any credential, for example &#x60;password&#x60; or &#x60;oidc&#x60;, in the response. When set to &#x60;oidc&#x60;, This will return the initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\nfunc (r IdentityAPIListIdentitiesRequest) IncludeCredential(includeCredential []string) IdentityAPIListIdentitiesRequest {\n\tr.includeCredential = &includeCredential\n\treturn r\n}\n\n// List identities that belong to a specific organization.\nfunc (r IdentityAPIListIdentitiesRequest) OrganizationId(organizationId string) IdentityAPIListIdentitiesRequest {\n\tr.organizationId = &organizationId\n\treturn r\n}\n\nfunc (r IdentityAPIListIdentitiesRequest) Execute() ([]Identity, *http.Response, error) {\n\treturn r.ApiService.ListIdentitiesExecute(r)\n}\n\n/*\nListIdentities List Identities\n\nLists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPIListIdentitiesRequest\n*/\nfunc (a *IdentityAPIService) ListIdentities(ctx context.Context) IdentityAPIListIdentitiesRequest {\n\treturn IdentityAPIListIdentitiesRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Identity\nfunc (a *IdentityAPIService) ListIdentitiesExecute(r IdentityAPIListIdentitiesRequest) ([]Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ListIdentities\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.perPage != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"per_page\", r.perPage, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.perPage = &defaultValue\n\t}\n\tif r.page != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page\", r.page, \"form\", \"\")\n\t}\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\tif r.consistency != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"consistency\", r.consistency, \"form\", \"\")\n\t}\n\tif r.ids != nil {\n\t\tt := *r.ids\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"ids\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"ids\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\tif r.credentialsIdentifier != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"credentials_identifier\", r.credentialsIdentifier, \"form\", \"\")\n\t}\n\tif r.previewCredentialsIdentifierSimilar != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"preview_credentials_identifier_similar\", r.previewCredentialsIdentifierSimilar, \"form\", \"\")\n\t}\n\tif r.includeCredential != nil {\n\t\tt := *r.includeCredential\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"include_credential\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\tif r.organizationId != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"organization_id\", r.organizationId, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIListIdentitySchemasRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tperPage    *int64\n\tpage       *int64\n\tpageSize   *int64\n\tpageToken  *string\n}\n\n// Deprecated Items per Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This is the number of items per page.\nfunc (r IdentityAPIListIdentitySchemasRequest) PerPage(perPage int64) IdentityAPIListIdentitySchemasRequest {\n\tr.perPage = &perPage\n\treturn r\n}\n\n// Deprecated Pagination Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This value is currently an integer, but it is not sequential. The value is not the page number, but a reference. The next page can be any number and some numbers might return an empty list.  For example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist. The first page can be retrieved by omitting this parameter. Following page pointers will be returned in the &#x60;Link&#x60; header.\nfunc (r IdentityAPIListIdentitySchemasRequest) Page(page int64) IdentityAPIListIdentitySchemasRequest {\n\tr.page = &page\n\treturn r\n}\n\n// Page Size  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitySchemasRequest) PageSize(pageSize int64) IdentityAPIListIdentitySchemasRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitySchemasRequest) PageToken(pageToken string) IdentityAPIListIdentitySchemasRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\nfunc (r IdentityAPIListIdentitySchemasRequest) Execute() ([]IdentitySchemaContainer, *http.Response, error) {\n\treturn r.ApiService.ListIdentitySchemasExecute(r)\n}\n\n/*\nListIdentitySchemas Get all Identity Schemas\n\nReturns a list of all identity schemas currently in use.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPIListIdentitySchemasRequest\n*/\nfunc (a *IdentityAPIService) ListIdentitySchemas(ctx context.Context) IdentityAPIListIdentitySchemasRequest {\n\treturn IdentityAPIListIdentitySchemasRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []IdentitySchemaContainer\nfunc (a *IdentityAPIService) ListIdentitySchemasExecute(r IdentityAPIListIdentitySchemasRequest) ([]IdentitySchemaContainer, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []IdentitySchemaContainer\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ListIdentitySchemas\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/schemas\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.perPage != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"per_page\", r.perPage, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.perPage = &defaultValue\n\t}\n\tif r.page != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page\", r.page, \"form\", \"\")\n\t}\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIListIdentitySessionsRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n\tperPage    *int64\n\tpage       *int64\n\tpageSize   *int64\n\tpageToken  *string\n\tactive     *bool\n}\n\n// Deprecated Items per Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This is the number of items per page.\nfunc (r IdentityAPIListIdentitySessionsRequest) PerPage(perPage int64) IdentityAPIListIdentitySessionsRequest {\n\tr.perPage = &perPage\n\treturn r\n}\n\n// Deprecated Pagination Page  DEPRECATED: Please use &#x60;page_token&#x60; instead. This parameter will be removed in the future.  This value is currently an integer, but it is not sequential. The value is not the page number, but a reference. The next page can be any number and some numbers might return an empty list.  For example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist. The first page can be retrieved by omitting this parameter. Following page pointers will be returned in the &#x60;Link&#x60; header.\nfunc (r IdentityAPIListIdentitySessionsRequest) Page(page int64) IdentityAPIListIdentitySessionsRequest {\n\tr.page = &page\n\treturn r\n}\n\n// Page Size  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitySessionsRequest) PageSize(pageSize int64) IdentityAPIListIdentitySessionsRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListIdentitySessionsRequest) PageToken(pageToken string) IdentityAPIListIdentitySessionsRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\nfunc (r IdentityAPIListIdentitySessionsRequest) Active(active bool) IdentityAPIListIdentitySessionsRequest {\n\tr.active = &active\n\treturn r\n}\n\nfunc (r IdentityAPIListIdentitySessionsRequest) Execute() ([]Session, *http.Response, error) {\n\treturn r.ApiService.ListIdentitySessionsExecute(r)\n}\n\n/*\nListIdentitySessions List an Identity's Sessions\n\nThis endpoint returns all sessions that belong to the given Identity.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID is the identity's ID.\n\t@return IdentityAPIListIdentitySessionsRequest\n*/\nfunc (a *IdentityAPIService) ListIdentitySessions(ctx context.Context, id string) IdentityAPIListIdentitySessionsRequest {\n\treturn IdentityAPIListIdentitySessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Session\nfunc (a *IdentityAPIService) ListIdentitySessionsExecute(r IdentityAPIListIdentitySessionsRequest) ([]Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ListIdentitySessions\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}/sessions\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.perPage != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"per_page\", r.perPage, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.perPage = &defaultValue\n\t}\n\tif r.page != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page\", r.page, \"form\", \"\")\n\t}\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\tif r.active != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"active\", r.active, \"form\", \"\")\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIListSessionsRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tpageSize   *int64\n\tpageToken  *string\n\tactive     *bool\n\texpand     *[]string\n}\n\n// Items per Page  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListSessionsRequest) PageSize(pageSize int64) IdentityAPIListSessionsRequest {\n\tr.pageSize = &pageSize\n\treturn r\n}\n\n// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\nfunc (r IdentityAPIListSessionsRequest) PageToken(pageToken string) IdentityAPIListSessionsRequest {\n\tr.pageToken = &pageToken\n\treturn r\n}\n\n// Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\nfunc (r IdentityAPIListSessionsRequest) Active(active bool) IdentityAPIListSessionsRequest {\n\tr.active = &active\n\treturn r\n}\n\n// ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session. If no value is provided, the expandable properties are skipped.\nfunc (r IdentityAPIListSessionsRequest) Expand(expand []string) IdentityAPIListSessionsRequest {\n\tr.expand = &expand\n\treturn r\n}\n\nfunc (r IdentityAPIListSessionsRequest) Execute() ([]Session, *http.Response, error) {\n\treturn r.ApiService.ListSessionsExecute(r)\n}\n\n/*\nListSessions List All Sessions\n\nListing all sessions that exist.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return IdentityAPIListSessionsRequest\n*/\nfunc (a *IdentityAPIService) ListSessions(ctx context.Context) IdentityAPIListSessionsRequest {\n\treturn IdentityAPIListSessionsRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return []Session\nfunc (a *IdentityAPIService) ListSessionsExecute(r IdentityAPIListSessionsRequest) ([]Session, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue []Session\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.ListSessions\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/sessions\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\tif r.pageSize != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_size\", r.pageSize, \"form\", \"\")\n\t} else {\n\t\tvar defaultValue int64 = 250\n\t\tr.pageSize = &defaultValue\n\t}\n\tif r.pageToken != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"page_token\", r.pageToken, \"form\", \"\")\n\t}\n\tif r.active != nil {\n\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"active\", r.active, \"form\", \"\")\n\t}\n\tif r.expand != nil {\n\t\tt := *r.expand\n\t\tif reflect.TypeOf(t).Kind() == reflect.Slice {\n\t\t\ts := reflect.ValueOf(t)\n\t\t\tfor i := 0; i < s.Len(); i++ {\n\t\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"expand\", s.Index(i).Interface(), \"form\", \"multi\")\n\t\t\t}\n\t\t} else {\n\t\t\tparameterAddToHeaderOrQuery(localVarQueryParams, \"expand\", t, \"form\", \"multi\")\n\t\t}\n\t}\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIPatchIdentityRequest struct {\n\tctx        context.Context\n\tApiService IdentityAPI\n\tid         string\n\tjsonPatch  *[]JsonPatch\n}\n\nfunc (r IdentityAPIPatchIdentityRequest) JsonPatch(jsonPatch []JsonPatch) IdentityAPIPatchIdentityRequest {\n\tr.jsonPatch = &jsonPatch\n\treturn r\n}\n\nfunc (r IdentityAPIPatchIdentityRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.PatchIdentityExecute(r)\n}\n\n/*\nPatchIdentity Patch an Identity\n\nPartially updates an [identity's](https://www.ory.sh/docs/kratos/concepts/identity-user-model) field using [JSON Patch](https://jsonpatch.com/).\nThe fields `id`, `stateChangedAt` and `credentials` can not be updated using this method.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID must be set to the ID of identity you want to update\n\t@return IdentityAPIPatchIdentityRequest\n*/\nfunc (a *IdentityAPIService) PatchIdentity(ctx context.Context, id string) IdentityAPIPatchIdentityRequest {\n\treturn IdentityAPIPatchIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) PatchIdentityExecute(r IdentityAPIPatchIdentityRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPatch\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.PatchIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.jsonPatch\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 409 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype IdentityAPIUpdateIdentityRequest struct {\n\tctx                context.Context\n\tApiService         IdentityAPI\n\tid                 string\n\tupdateIdentityBody *UpdateIdentityBody\n}\n\nfunc (r IdentityAPIUpdateIdentityRequest) UpdateIdentityBody(updateIdentityBody UpdateIdentityBody) IdentityAPIUpdateIdentityRequest {\n\tr.updateIdentityBody = &updateIdentityBody\n\treturn r\n}\n\nfunc (r IdentityAPIUpdateIdentityRequest) Execute() (*Identity, *http.Response, error) {\n\treturn r.ApiService.UpdateIdentityExecute(r)\n}\n\n/*\nUpdateIdentity Update an Identity\n\nThis endpoint updates an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model). The full identity\npayload, except credentials, is expected. For partial updates, use the [patchIdentity](https://www.ory.sh/docs/reference/api#tag/identity/operation/patchIdentity) operation.\n\nA credential can be provided via the `credentials` field in the request body.\nIf provided, the credentials will be imported and added to the existing credentials of the identity.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@param id ID must be set to the ID of identity you want to update\n\t@return IdentityAPIUpdateIdentityRequest\n*/\nfunc (a *IdentityAPIService) UpdateIdentity(ctx context.Context, id string) IdentityAPIUpdateIdentityRequest {\n\treturn IdentityAPIUpdateIdentityRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t\tid:         id,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return Identity\nfunc (a *IdentityAPIService) UpdateIdentityExecute(r IdentityAPIUpdateIdentityRequest) (*Identity, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodPut\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *Identity\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"IdentityAPIService.UpdateIdentity\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/admin/identities/{id}\"\n\tlocalVarPath = strings.Replace(localVarPath, \"{\"+\"id\"+\"}\", url.PathEscape(parameterValueToString(r.id, \"id\")), -1)\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{\"application/json\"}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\t// body params\n\tlocalVarPostBody = r.updateIdentityBody\n\tif r.ctx != nil {\n\t\t// API Key Authentication\n\t\tif auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {\n\t\t\tif apiKey, ok := auth[\"oryAccessToken\"]; ok {\n\t\t\t\tvar key string\n\t\t\t\tif apiKey.Prefix != \"\" {\n\t\t\t\t\tkey = apiKey.Prefix + \" \" + apiKey.Key\n\t\t\t\t} else {\n\t\t\t\t\tkey = apiKey.Key\n\t\t\t\t}\n\t\t\t\tlocalVarHeaderParams[\"Authorization\"] = key\n\t\t\t}\n\t\t}\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 400 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 404 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 409 {\n\t\t\tvar v ErrorGeneric\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v ErrorGeneric\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n"
  },
  {
    "path": "pkg/httpclient/api_metadata.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\ntype MetadataAPI interface {\n\n\t/*\n\t\t\tGetVersion Return Running Software Version.\n\n\t\t\tThis endpoint returns the version of Ory Kratos.\n\n\t\tIf the service supports TLS Edge Termination, this endpoint does not require the\n\t\t`X-Forwarded-Proto` header to be set.\n\n\t\tBe aware that if you are running multiple nodes of this service, the version will never\n\t\trefer to the cluster state, only to a single instance.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return MetadataAPIGetVersionRequest\n\t*/\n\tGetVersion(ctx context.Context) MetadataAPIGetVersionRequest\n\n\t// GetVersionExecute executes the request\n\t//  @return GetVersion200Response\n\tGetVersionExecute(r MetadataAPIGetVersionRequest) (*GetVersion200Response, *http.Response, error)\n\n\t/*\n\t\t\tIsAlive Check HTTP Server Status\n\n\t\t\tThis endpoint returns a HTTP 200 status code when Ory Kratos is accepting incoming\n\t\tHTTP requests. This status does currently not include checks whether the database connection is working.\n\n\t\tIf the service supports TLS Edge Termination, this endpoint does not require the\n\t\t`X-Forwarded-Proto` header to be set.\n\n\t\tBe aware that if you are running multiple nodes of this service, the health status will never\n\t\trefer to the cluster state, only to a single instance.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return MetadataAPIIsAliveRequest\n\t*/\n\tIsAlive(ctx context.Context) MetadataAPIIsAliveRequest\n\n\t// IsAliveExecute executes the request\n\t//  @return IsAlive200Response\n\tIsAliveExecute(r MetadataAPIIsAliveRequest) (*IsAlive200Response, *http.Response, error)\n\n\t/*\n\t\t\tIsReady Check HTTP Server and Database Status\n\n\t\t\tThis endpoint returns a HTTP 200 status code when Ory Kratos is up running and the environment dependencies (e.g.\n\t\tthe database) are responsive as well.\n\n\t\tIf the service supports TLS Edge Termination, this endpoint does not require the\n\t\t`X-Forwarded-Proto` header to be set.\n\n\t\tBe aware that if you are running multiple nodes of Ory Kratos, the health status will never\n\t\trefer to the cluster state, only to a single instance.\n\n\t\t\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t\t\t@return MetadataAPIIsReadyRequest\n\t*/\n\tIsReady(ctx context.Context) MetadataAPIIsReadyRequest\n\n\t// IsReadyExecute executes the request\n\t//  @return IsAlive200Response\n\tIsReadyExecute(r MetadataAPIIsReadyRequest) (*IsAlive200Response, *http.Response, error)\n}\n\n// MetadataAPIService MetadataAPI service\ntype MetadataAPIService service\n\ntype MetadataAPIGetVersionRequest struct {\n\tctx        context.Context\n\tApiService MetadataAPI\n}\n\nfunc (r MetadataAPIGetVersionRequest) Execute() (*GetVersion200Response, *http.Response, error) {\n\treturn r.ApiService.GetVersionExecute(r)\n}\n\n/*\nGetVersion Return Running Software Version.\n\nThis endpoint returns the version of Ory Kratos.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the version will never\nrefer to the cluster state, only to a single instance.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return MetadataAPIGetVersionRequest\n*/\nfunc (a *MetadataAPIService) GetVersion(ctx context.Context) MetadataAPIGetVersionRequest {\n\treturn MetadataAPIGetVersionRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return GetVersion200Response\nfunc (a *MetadataAPIService) GetVersionExecute(r MetadataAPIGetVersionRequest) (*GetVersion200Response, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *GetVersion200Response\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"MetadataAPIService.GetVersion\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/version\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype MetadataAPIIsAliveRequest struct {\n\tctx        context.Context\n\tApiService MetadataAPI\n}\n\nfunc (r MetadataAPIIsAliveRequest) Execute() (*IsAlive200Response, *http.Response, error) {\n\treturn r.ApiService.IsAliveExecute(r)\n}\n\n/*\nIsAlive Check HTTP Server Status\n\nThis endpoint returns a HTTP 200 status code when Ory Kratos is accepting incoming\nHTTP requests. This status does currently not include checks whether the database connection is working.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return MetadataAPIIsAliveRequest\n*/\nfunc (a *MetadataAPIService) IsAlive(ctx context.Context) MetadataAPIIsAliveRequest {\n\treturn MetadataAPIIsAliveRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return IsAlive200Response\nfunc (a *MetadataAPIService) IsAliveExecute(r MetadataAPIIsAliveRequest) (*IsAlive200Response, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *IsAlive200Response\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"MetadataAPIService.IsAlive\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/health/alive\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\", \"text/plain\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tvar v string\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n\ntype MetadataAPIIsReadyRequest struct {\n\tctx        context.Context\n\tApiService MetadataAPI\n}\n\nfunc (r MetadataAPIIsReadyRequest) Execute() (*IsAlive200Response, *http.Response, error) {\n\treturn r.ApiService.IsReadyExecute(r)\n}\n\n/*\nIsReady Check HTTP Server and Database Status\n\nThis endpoint returns a HTTP 200 status code when Ory Kratos is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of Ory Kratos, the health status will never\nrefer to the cluster state, only to a single instance.\n\n\t@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().\n\t@return MetadataAPIIsReadyRequest\n*/\nfunc (a *MetadataAPIService) IsReady(ctx context.Context) MetadataAPIIsReadyRequest {\n\treturn MetadataAPIIsReadyRequest{\n\t\tApiService: a,\n\t\tctx:        ctx,\n\t}\n}\n\n// Execute executes the request\n//\n//\t@return IsAlive200Response\nfunc (a *MetadataAPIService) IsReadyExecute(r MetadataAPIIsReadyRequest) (*IsAlive200Response, *http.Response, error) {\n\tvar (\n\t\tlocalVarHTTPMethod  = http.MethodGet\n\t\tlocalVarPostBody    interface{}\n\t\tformFiles           []formFile\n\t\tlocalVarReturnValue *IsAlive200Response\n\t)\n\n\tlocalBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, \"MetadataAPIService.IsReady\")\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}\n\t}\n\n\tlocalVarPath := localBasePath + \"/health/ready\"\n\n\tlocalVarHeaderParams := make(map[string]string)\n\tlocalVarQueryParams := url.Values{}\n\tlocalVarFormParams := url.Values{}\n\n\t// to determine the Content-Type header\n\tlocalVarHTTPContentTypes := []string{}\n\n\t// set Content-Type header\n\tlocalVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)\n\tif localVarHTTPContentType != \"\" {\n\t\tlocalVarHeaderParams[\"Content-Type\"] = localVarHTTPContentType\n\t}\n\n\t// to determine the Accept header\n\tlocalVarHTTPHeaderAccepts := []string{\"application/json\", \"text/plain\"}\n\n\t// set Accept header\n\tlocalVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)\n\tif localVarHTTPHeaderAccept != \"\" {\n\t\tlocalVarHeaderParams[\"Accept\"] = localVarHTTPHeaderAccept\n\t}\n\treq, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)\n\tif err != nil {\n\t\treturn localVarReturnValue, nil, err\n\t}\n\n\tlocalVarHTTPResponse, err := a.client.callAPI(req)\n\tif err != nil || localVarHTTPResponse == nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tlocalVarBody, err := io.ReadAll(localVarHTTPResponse.Body)\n\tlocalVarHTTPResponse.Body.Close()\n\tlocalVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))\n\tif err != nil {\n\t\treturn localVarReturnValue, localVarHTTPResponse, err\n\t}\n\n\tif localVarHTTPResponse.StatusCode >= 300 {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: localVarHTTPResponse.Status,\n\t\t}\n\t\tif localVarHTTPResponse.StatusCode == 503 {\n\t\t\tvar v IsReady503Response\n\t\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\t\tif err != nil {\n\t\t\t\tnewErr.error = err.Error()\n\t\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t\t}\n\t\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\t\tnewErr.model = v\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tvar v string\n\t\terr = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\t\tif err != nil {\n\t\t\tnewErr.error = err.Error()\n\t\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t\t}\n\t\tnewErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)\n\t\tnewErr.model = v\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\terr = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get(\"Content-Type\"))\n\tif err != nil {\n\t\tnewErr := &GenericOpenAPIError{\n\t\t\tbody:  localVarBody,\n\t\t\terror: err.Error(),\n\t\t}\n\t\treturn localVarReturnValue, localVarHTTPResponse, newErr\n\t}\n\n\treturn localVarReturnValue, localVarHTTPResponse, nil\n}\n"
  },
  {
    "path": "pkg/httpclient/client.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"encoding/xml\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n)\n\nvar (\n\tJsonCheck       = regexp.MustCompile(`(?i:(?:application|text)/(?:[^;]+\\+)?json)`)\n\tXmlCheck        = regexp.MustCompile(`(?i:(?:application|text)/(?:[^;]+\\+)?xml)`)\n\tqueryParamSplit = regexp.MustCompile(`(^|&)([^&]+)`)\n\tqueryDescape    = strings.NewReplacer(\"%5B\", \"[\", \"%5D\", \"]\")\n)\n\n// APIClient manages communication with the Ory Identities API API v\n// In most cases there should be only one, shared, APIClient.\ntype APIClient struct {\n\tcfg    *Configuration\n\tcommon service // Reuse a single struct instead of allocating one for each service on the heap.\n\n\t// API Services\n\n\tCourierAPI CourierAPI\n\n\tFrontendAPI FrontendAPI\n\n\tIdentityAPI IdentityAPI\n\n\tMetadataAPI MetadataAPI\n}\n\ntype service struct {\n\tclient *APIClient\n}\n\n// NewAPIClient creates a new API client. Requires a userAgent string describing your application.\n// optionally a custom http.Client to allow for advanced features such as caching.\nfunc NewAPIClient(cfg *Configuration) *APIClient {\n\tif cfg.HTTPClient == nil {\n\t\tcfg.HTTPClient = http.DefaultClient\n\t}\n\n\tc := &APIClient{}\n\tc.cfg = cfg\n\tc.common.client = c\n\n\t// API Services\n\tc.CourierAPI = (*CourierAPIService)(&c.common)\n\tc.FrontendAPI = (*FrontendAPIService)(&c.common)\n\tc.IdentityAPI = (*IdentityAPIService)(&c.common)\n\tc.MetadataAPI = (*MetadataAPIService)(&c.common)\n\n\treturn c\n}\n\nfunc atoi(in string) (int, error) {\n\treturn strconv.Atoi(in)\n}\n\n// selectHeaderContentType select a content type from the available list.\nfunc selectHeaderContentType(contentTypes []string) string {\n\tif len(contentTypes) == 0 {\n\t\treturn \"\"\n\t}\n\tif contains(contentTypes, \"application/json\") {\n\t\treturn \"application/json\"\n\t}\n\treturn contentTypes[0] // use the first content type specified in 'consumes'\n}\n\n// selectHeaderAccept join all accept types and return\nfunc selectHeaderAccept(accepts []string) string {\n\tif len(accepts) == 0 {\n\t\treturn \"\"\n\t}\n\n\tif contains(accepts, \"application/json\") {\n\t\treturn \"application/json\"\n\t}\n\n\treturn strings.Join(accepts, \",\")\n}\n\n// contains is a case insensitive match, finding needle in a haystack\nfunc contains(haystack []string, needle string) bool {\n\tfor _, a := range haystack {\n\t\tif strings.EqualFold(a, needle) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Verify optional parameters are of the correct type.\nfunc typeCheckParameter(obj interface{}, expected string, name string) error {\n\t// Make sure there is an object.\n\tif obj == nil {\n\t\treturn nil\n\t}\n\n\t// Check the type is as expected.\n\tif reflect.TypeOf(obj).String() != expected {\n\t\treturn fmt.Errorf(\"expected %s to be of type %s but received %s\", name, expected, reflect.TypeOf(obj).String())\n\t}\n\treturn nil\n}\n\nfunc parameterValueToString(obj interface{}, key string) string {\n\tif reflect.TypeOf(obj).Kind() != reflect.Ptr {\n\t\tif actualObj, ok := obj.(interface{ GetActualInstanceValue() interface{} }); ok {\n\t\t\treturn fmt.Sprintf(\"%v\", actualObj.GetActualInstanceValue())\n\t\t}\n\n\t\treturn fmt.Sprintf(\"%v\", obj)\n\t}\n\tvar param, ok = obj.(MappedNullable)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\tdataMap, err := param.ToMap()\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn fmt.Sprintf(\"%v\", dataMap[key])\n}\n\n// parameterAddToHeaderOrQuery adds the provided object to the request header or url query\n// supporting deep object syntax\nfunc parameterAddToHeaderOrQuery(headerOrQueryParams interface{}, keyPrefix string, obj interface{}, style string, collectionType string) {\n\tvar v = reflect.ValueOf(obj)\n\tvar value = \"\"\n\tif v == reflect.ValueOf(nil) {\n\t\tvalue = \"null\"\n\t} else {\n\t\tswitch v.Kind() {\n\t\tcase reflect.Invalid:\n\t\t\tvalue = \"invalid\"\n\n\t\tcase reflect.Struct:\n\t\t\tif t, ok := obj.(MappedNullable); ok {\n\t\t\t\tdataMap, err := t.ToMap()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, dataMap, style, collectionType)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif t, ok := obj.(time.Time); ok {\n\t\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, t.Format(time.RFC3339Nano), style, collectionType)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvalue = v.Type().String() + \" value\"\n\t\tcase reflect.Slice:\n\t\t\tvar indValue = reflect.ValueOf(obj)\n\t\t\tif indValue == reflect.ValueOf(nil) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvar lenIndValue = indValue.Len()\n\t\t\tfor i := 0; i < lenIndValue; i++ {\n\t\t\t\tvar arrayValue = indValue.Index(i)\n\t\t\t\tvar keyPrefixForCollectionType = keyPrefix\n\t\t\t\tif style == \"deepObject\" {\n\t\t\t\t\tkeyPrefixForCollectionType = keyPrefix + \"[\" + strconv.Itoa(i) + \"]\"\n\t\t\t\t}\n\t\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefixForCollectionType, arrayValue.Interface(), style, collectionType)\n\t\t\t}\n\t\t\treturn\n\n\t\tcase reflect.Map:\n\t\t\tvar indValue = reflect.ValueOf(obj)\n\t\t\tif indValue == reflect.ValueOf(nil) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\titer := indValue.MapRange()\n\t\t\tfor iter.Next() {\n\t\t\t\tk, v := iter.Key(), iter.Value()\n\t\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, fmt.Sprintf(\"%s[%s]\", keyPrefix, k.String()), v.Interface(), style, collectionType)\n\t\t\t}\n\t\t\treturn\n\n\t\tcase reflect.Interface:\n\t\t\tfallthrough\n\t\tcase reflect.Ptr:\n\t\t\tparameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, v.Elem().Interface(), style, collectionType)\n\t\t\treturn\n\n\t\tcase reflect.Int, reflect.Int8, reflect.Int16,\n\t\t\treflect.Int32, reflect.Int64:\n\t\t\tvalue = strconv.FormatInt(v.Int(), 10)\n\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16,\n\t\t\treflect.Uint32, reflect.Uint64, reflect.Uintptr:\n\t\t\tvalue = strconv.FormatUint(v.Uint(), 10)\n\t\tcase reflect.Float32, reflect.Float64:\n\t\t\tvalue = strconv.FormatFloat(v.Float(), 'g', -1, 32)\n\t\tcase reflect.Bool:\n\t\t\tvalue = strconv.FormatBool(v.Bool())\n\t\tcase reflect.String:\n\t\t\tvalue = v.String()\n\t\tdefault:\n\t\t\tvalue = v.Type().String() + \" value\"\n\t\t}\n\t}\n\n\tswitch valuesMap := headerOrQueryParams.(type) {\n\tcase url.Values:\n\t\tif collectionType == \"csv\" && valuesMap.Get(keyPrefix) != \"\" {\n\t\t\tvaluesMap.Set(keyPrefix, valuesMap.Get(keyPrefix)+\",\"+value)\n\t\t} else {\n\t\t\tvaluesMap.Add(keyPrefix, value)\n\t\t}\n\t\tbreak\n\tcase map[string]string:\n\t\tvaluesMap[keyPrefix] = value\n\t\tbreak\n\t}\n}\n\n// helper for converting interface{} parameters to json strings\nfunc parameterToJson(obj interface{}) (string, error) {\n\tjsonBuf, err := json.Marshal(obj)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(jsonBuf), err\n}\n\n// callAPI do the request.\nfunc (c *APIClient) callAPI(request *http.Request) (*http.Response, error) {\n\tif c.cfg.Debug {\n\t\tdump, err := httputil.DumpRequestOut(request, true)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlog.Printf(\"\\n%s\\n\", string(dump))\n\t}\n\n\tresp, err := c.cfg.HTTPClient.Do(request)\n\tif err != nil {\n\t\treturn resp, err\n\t}\n\n\tif c.cfg.Debug {\n\t\tdump, err := httputil.DumpResponse(resp, true)\n\t\tif err != nil {\n\t\t\treturn resp, err\n\t\t}\n\t\tlog.Printf(\"\\n%s\\n\", string(dump))\n\t}\n\treturn resp, err\n}\n\n// Allow modification of underlying config for alternate implementations and testing\n// Caution: modifying the configuration while live can cause data races and potentially unwanted behavior\nfunc (c *APIClient) GetConfig() *Configuration {\n\treturn c.cfg\n}\n\ntype formFile struct {\n\tfileBytes    []byte\n\tfileName     string\n\tformFileName string\n}\n\n// prepareRequest build the request\nfunc (c *APIClient) prepareRequest(\n\tctx context.Context,\n\tpath string, method string,\n\tpostBody interface{},\n\theaderParams map[string]string,\n\tqueryParams url.Values,\n\tformParams url.Values,\n\tformFiles []formFile) (localVarRequest *http.Request, err error) {\n\n\tvar body *bytes.Buffer\n\n\t// Detect postBody type and post.\n\tif postBody != nil {\n\t\tcontentType := headerParams[\"Content-Type\"]\n\t\tif contentType == \"\" {\n\t\t\tcontentType = detectContentType(postBody)\n\t\t\theaderParams[\"Content-Type\"] = contentType\n\t\t}\n\n\t\tbody, err = setBody(postBody, contentType)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// add form parameters and file if available.\n\tif strings.HasPrefix(headerParams[\"Content-Type\"], \"multipart/form-data\") && len(formParams) > 0 || (len(formFiles) > 0) {\n\t\tif body != nil {\n\t\t\treturn nil, errors.New(\"Cannot specify postBody and multipart form at the same time.\")\n\t\t}\n\t\tbody = &bytes.Buffer{}\n\t\tw := multipart.NewWriter(body)\n\n\t\tfor k, v := range formParams {\n\t\t\tfor _, iv := range v {\n\t\t\t\tif strings.HasPrefix(k, \"@\") { // file\n\t\t\t\t\terr = addFile(w, k[1:], iv)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t} else { // form value\n\t\t\t\t\tw.WriteField(k, iv)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor _, formFile := range formFiles {\n\t\t\tif len(formFile.fileBytes) > 0 && formFile.fileName != \"\" {\n\t\t\t\tw.Boundary()\n\t\t\t\tpart, err := w.CreateFormFile(formFile.formFileName, filepath.Base(formFile.fileName))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\t_, err = part.Write(formFile.fileBytes)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Set the Boundary in the Content-Type\n\t\theaderParams[\"Content-Type\"] = w.FormDataContentType()\n\n\t\t// Set Content-Length\n\t\theaderParams[\"Content-Length\"] = fmt.Sprintf(\"%d\", body.Len())\n\t\tw.Close()\n\t}\n\n\tif strings.HasPrefix(headerParams[\"Content-Type\"], \"application/x-www-form-urlencoded\") && len(formParams) > 0 {\n\t\tif body != nil {\n\t\t\treturn nil, errors.New(\"Cannot specify postBody and x-www-form-urlencoded form at the same time.\")\n\t\t}\n\t\tbody = &bytes.Buffer{}\n\t\tbody.WriteString(formParams.Encode())\n\t\t// Set Content-Length\n\t\theaderParams[\"Content-Length\"] = fmt.Sprintf(\"%d\", body.Len())\n\t}\n\n\t// Setup path and query parameters\n\turl, err := url.Parse(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Override request host, if applicable\n\tif c.cfg.Host != \"\" {\n\t\turl.Host = c.cfg.Host\n\t}\n\n\t// Override request scheme, if applicable\n\tif c.cfg.Scheme != \"\" {\n\t\turl.Scheme = c.cfg.Scheme\n\t}\n\n\t// Adding Query Param\n\tquery := url.Query()\n\tfor k, v := range queryParams {\n\t\tfor _, iv := range v {\n\t\t\tquery.Add(k, iv)\n\t\t}\n\t}\n\n\t// Encode the parameters.\n\turl.RawQuery = queryParamSplit.ReplaceAllStringFunc(query.Encode(), func(s string) string {\n\t\tpieces := strings.Split(s, \"=\")\n\t\tpieces[0] = queryDescape.Replace(pieces[0])\n\t\treturn strings.Join(pieces, \"=\")\n\t})\n\n\t// Generate a new request\n\tif body != nil {\n\t\tlocalVarRequest, err = http.NewRequest(method, url.String(), body)\n\t} else {\n\t\tlocalVarRequest, err = http.NewRequest(method, url.String(), nil)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// add header parameters, if any\n\tif len(headerParams) > 0 {\n\t\theaders := http.Header{}\n\t\tfor h, v := range headerParams {\n\t\t\theaders[h] = []string{v}\n\t\t}\n\t\tlocalVarRequest.Header = headers\n\t}\n\n\t// Add the user agent to the request.\n\tlocalVarRequest.Header.Add(\"User-Agent\", c.cfg.UserAgent)\n\n\tif ctx != nil {\n\t\t// add context to the request\n\t\tlocalVarRequest = localVarRequest.WithContext(ctx)\n\n\t\t// Walk through any authentication.\n\n\t}\n\n\tfor header, value := range c.cfg.DefaultHeader {\n\t\tlocalVarRequest.Header.Add(header, value)\n\t}\n\treturn localVarRequest, nil\n}\n\nfunc (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) {\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\tif s, ok := v.(*string); ok {\n\t\t*s = string(b)\n\t\treturn nil\n\t}\n\tif f, ok := v.(*os.File); ok {\n\t\tf, err = os.CreateTemp(\"\", \"HttpClientFile\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = f.Write(b)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = f.Seek(0, io.SeekStart)\n\t\treturn\n\t}\n\tif f, ok := v.(**os.File); ok {\n\t\t*f, err = os.CreateTemp(\"\", \"HttpClientFile\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = (*f).Write(b)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = (*f).Seek(0, io.SeekStart)\n\t\treturn\n\t}\n\tif XmlCheck.MatchString(contentType) {\n\t\tif err = xml.Unmarshal(b, v); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\tif JsonCheck.MatchString(contentType) {\n\t\tif actualObj, ok := v.(interface{ GetActualInstance() interface{} }); ok { // oneOf, anyOf schemas\n\t\t\tif unmarshalObj, ok := actualObj.(interface{ UnmarshalJSON([]byte) error }); ok { // make sure it has UnmarshalJSON defined\n\t\t\t\tif err = unmarshalObj.UnmarshalJSON(b); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn errors.New(\"Unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined\")\n\t\t\t}\n\t\t} else if err = json.Unmarshal(b, v); err != nil { // simple model\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\treturn errors.New(\"undefined response type\")\n}\n\n// Add a file to the multipart request\nfunc addFile(w *multipart.Writer, fieldName, path string) error {\n\tfile, err := os.Open(filepath.Clean(path))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = file.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpart, err := w.CreateFormFile(fieldName, filepath.Base(path))\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = io.Copy(part, file)\n\n\treturn err\n}\n\n// Set request body from an interface{}\nfunc setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) {\n\tif bodyBuf == nil {\n\t\tbodyBuf = &bytes.Buffer{}\n\t}\n\n\tif reader, ok := body.(io.Reader); ok {\n\t\t_, err = bodyBuf.ReadFrom(reader)\n\t} else if fp, ok := body.(*os.File); ok {\n\t\t_, err = bodyBuf.ReadFrom(fp)\n\t} else if b, ok := body.([]byte); ok {\n\t\t_, err = bodyBuf.Write(b)\n\t} else if s, ok := body.(string); ok {\n\t\t_, err = bodyBuf.WriteString(s)\n\t} else if s, ok := body.(*string); ok {\n\t\t_, err = bodyBuf.WriteString(*s)\n\t} else if JsonCheck.MatchString(contentType) {\n\t\terr = json.NewEncoder(bodyBuf).Encode(body)\n\t} else if XmlCheck.MatchString(contentType) {\n\t\tvar bs []byte\n\t\tbs, err = xml.Marshal(body)\n\t\tif err == nil {\n\t\t\tbodyBuf.Write(bs)\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif bodyBuf.Len() == 0 {\n\t\terr = fmt.Errorf(\"invalid body type %s\\n\", contentType)\n\t\treturn nil, err\n\t}\n\treturn bodyBuf, nil\n}\n\n// detectContentType method is used to figure out `Request.Body` content type for request header\nfunc detectContentType(body interface{}) string {\n\tcontentType := \"text/plain; charset=utf-8\"\n\tkind := reflect.TypeOf(body).Kind()\n\n\tswitch kind {\n\tcase reflect.Struct, reflect.Map, reflect.Ptr:\n\t\tcontentType = \"application/json; charset=utf-8\"\n\tcase reflect.String:\n\t\tcontentType = \"text/plain; charset=utf-8\"\n\tdefault:\n\t\tif b, ok := body.([]byte); ok {\n\t\t\tcontentType = http.DetectContentType(b)\n\t\t} else if kind == reflect.Slice {\n\t\t\tcontentType = \"application/json; charset=utf-8\"\n\t\t}\n\t}\n\n\treturn contentType\n}\n\n// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go\ntype cacheControl map[string]string\n\nfunc parseCacheControl(headers http.Header) cacheControl {\n\tcc := cacheControl{}\n\tccHeader := headers.Get(\"Cache-Control\")\n\tfor _, part := range strings.Split(ccHeader, \",\") {\n\t\tpart = strings.Trim(part, \" \")\n\t\tif part == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.ContainsRune(part, '=') {\n\t\t\tkeyval := strings.Split(part, \"=\")\n\t\t\tcc[strings.Trim(keyval[0], \" \")] = strings.Trim(keyval[1], \",\")\n\t\t} else {\n\t\t\tcc[part] = \"\"\n\t\t}\n\t}\n\treturn cc\n}\n\n// CacheExpires helper function to determine remaining time before repeating a request.\nfunc CacheExpires(r *http.Response) time.Time {\n\t// Figure out when the cache expires.\n\tvar expires time.Time\n\tnow, err := time.Parse(time.RFC1123, r.Header.Get(\"date\"))\n\tif err != nil {\n\t\treturn time.Now()\n\t}\n\trespCacheControl := parseCacheControl(r.Header)\n\n\tif maxAge, ok := respCacheControl[\"max-age\"]; ok {\n\t\tlifetime, err := time.ParseDuration(maxAge + \"s\")\n\t\tif err != nil {\n\t\t\texpires = now\n\t\t} else {\n\t\t\texpires = now.Add(lifetime)\n\t\t}\n\t} else {\n\t\texpiresHeader := r.Header.Get(\"Expires\")\n\t\tif expiresHeader != \"\" {\n\t\t\texpires, err = time.Parse(time.RFC1123, expiresHeader)\n\t\t\tif err != nil {\n\t\t\t\texpires = now\n\t\t\t}\n\t\t}\n\t}\n\treturn expires\n}\n\nfunc strlen(s string) int {\n\treturn utf8.RuneCountInString(s)\n}\n\n// GenericOpenAPIError Provides access to the body, error and model on returned errors.\ntype GenericOpenAPIError struct {\n\tbody  []byte\n\terror string\n\tmodel interface{}\n}\n\n// Error returns non-empty string if there was an error.\nfunc (e GenericOpenAPIError) Error() string {\n\treturn e.error\n}\n\n// Body returns the raw bytes of the response\nfunc (e GenericOpenAPIError) Body() []byte {\n\treturn e.body\n}\n\n// Model returns the unpacked model of the error\nfunc (e GenericOpenAPIError) Model() interface{} {\n\treturn e.model\n}\n\n// format error message using title and detail when model implements rfc7807\nfunc formatErrorMessage(status string, v interface{}) string {\n\tstr := \"\"\n\tmetaValue := reflect.ValueOf(v).Elem()\n\n\tif metaValue.Kind() == reflect.Struct {\n\t\tfield := metaValue.FieldByName(\"Title\")\n\t\tif field != (reflect.Value{}) {\n\t\t\tstr = fmt.Sprintf(\"%s\", field.Interface())\n\t\t}\n\n\t\tfield = metaValue.FieldByName(\"Detail\")\n\t\tif field != (reflect.Value{}) {\n\t\t\tstr = fmt.Sprintf(\"%s (%s)\", str, field.Interface())\n\t\t}\n\t}\n\n\treturn strings.TrimSpace(fmt.Sprintf(\"%s %s\", status, str))\n}\n"
  },
  {
    "path": "pkg/httpclient/configuration.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// contextKeys are used to identify the type of value in the context.\n// Since these are string, it is possible to get a short description of the\n// context key for logging and debugging using key.String().\n\ntype contextKey string\n\nfunc (c contextKey) String() string {\n\treturn \"auth \" + string(c)\n}\n\nvar (\n\t// ContextAPIKeys takes a string apikey as authentication for the request\n\tContextAPIKeys = contextKey(\"apiKeys\")\n\n\t// ContextServerIndex uses a server configuration from the index.\n\tContextServerIndex = contextKey(\"serverIndex\")\n\n\t// ContextOperationServerIndices uses a server configuration from the index mapping.\n\tContextOperationServerIndices = contextKey(\"serverOperationIndices\")\n\n\t// ContextServerVariables overrides a server configuration variables.\n\tContextServerVariables = contextKey(\"serverVariables\")\n\n\t// ContextOperationServerVariables overrides a server configuration variables using operation specific values.\n\tContextOperationServerVariables = contextKey(\"serverOperationVariables\")\n)\n\n// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth\ntype BasicAuth struct {\n\tUserName string `json:\"userName,omitempty\"`\n\tPassword string `json:\"password,omitempty\"`\n}\n\n// APIKey provides API key based authentication to a request passed via context using ContextAPIKey\ntype APIKey struct {\n\tKey    string\n\tPrefix string\n}\n\n// ServerVariable stores the information about a server variable\ntype ServerVariable struct {\n\tDescription  string\n\tDefaultValue string\n\tEnumValues   []string\n}\n\n// ServerConfiguration stores the information about a server\ntype ServerConfiguration struct {\n\tURL         string\n\tDescription string\n\tVariables   map[string]ServerVariable\n}\n\n// ServerConfigurations stores multiple ServerConfiguration items\ntype ServerConfigurations []ServerConfiguration\n\n// Configuration stores the configuration of the API client\ntype Configuration struct {\n\tHost             string            `json:\"host,omitempty\"`\n\tScheme           string            `json:\"scheme,omitempty\"`\n\tDefaultHeader    map[string]string `json:\"defaultHeader,omitempty\"`\n\tUserAgent        string            `json:\"userAgent,omitempty\"`\n\tDebug            bool              `json:\"debug,omitempty\"`\n\tServers          ServerConfigurations\n\tOperationServers map[string]ServerConfigurations\n\tHTTPClient       *http.Client\n}\n\n// NewConfiguration returns a new Configuration object\nfunc NewConfiguration() *Configuration {\n\tcfg := &Configuration{\n\t\tDefaultHeader: make(map[string]string),\n\t\tUserAgent:     \"OpenAPI-Generator/1.0.0/go\",\n\t\tDebug:         false,\n\t\tServers: ServerConfigurations{\n\t\t\t{\n\t\t\t\tURL:         \"\",\n\t\t\t\tDescription: \"No description provided\",\n\t\t\t},\n\t\t},\n\t\tOperationServers: map[string]ServerConfigurations{},\n\t}\n\treturn cfg\n}\n\n// AddDefaultHeader adds a new HTTP header to the default header in the request\nfunc (c *Configuration) AddDefaultHeader(key string, value string) {\n\tc.DefaultHeader[key] = value\n}\n\n// URL formats template on a index using given variables\nfunc (sc ServerConfigurations) URL(index int, variables map[string]string) (string, error) {\n\tif index < 0 || len(sc) <= index {\n\t\treturn \"\", fmt.Errorf(\"index %v out of range %v\", index, len(sc)-1)\n\t}\n\tserver := sc[index]\n\turl := server.URL\n\n\t// go through variables and replace placeholders\n\tfor name, variable := range server.Variables {\n\t\tif value, ok := variables[name]; ok {\n\t\t\tfound := bool(len(variable.EnumValues) == 0)\n\t\t\tfor _, enumValue := range variable.EnumValues {\n\t\t\t\tif value == enumValue {\n\t\t\t\t\tfound = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !found {\n\t\t\t\treturn \"\", fmt.Errorf(\"the variable %s in the server URL has invalid value %v. Must be %v\", name, value, variable.EnumValues)\n\t\t\t}\n\t\t\turl = strings.Replace(url, \"{\"+name+\"}\", value, -1)\n\t\t} else {\n\t\t\turl = strings.Replace(url, \"{\"+name+\"}\", variable.DefaultValue, -1)\n\t\t}\n\t}\n\treturn url, nil\n}\n\n// ServerURL returns URL based on server settings\nfunc (c *Configuration) ServerURL(index int, variables map[string]string) (string, error) {\n\treturn c.Servers.URL(index, variables)\n}\n\nfunc getServerIndex(ctx context.Context) (int, error) {\n\tsi := ctx.Value(ContextServerIndex)\n\tif si != nil {\n\t\tif index, ok := si.(int); ok {\n\t\t\treturn index, nil\n\t\t}\n\t\treturn 0, reportError(\"Invalid type %T should be int\", si)\n\t}\n\treturn 0, nil\n}\n\nfunc getServerOperationIndex(ctx context.Context, endpoint string) (int, error) {\n\tosi := ctx.Value(ContextOperationServerIndices)\n\tif osi != nil {\n\t\tif operationIndices, ok := osi.(map[string]int); !ok {\n\t\t\treturn 0, reportError(\"Invalid type %T should be map[string]int\", osi)\n\t\t} else {\n\t\t\tindex, ok := operationIndices[endpoint]\n\t\t\tif ok {\n\t\t\t\treturn index, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn getServerIndex(ctx)\n}\n\nfunc getServerVariables(ctx context.Context) (map[string]string, error) {\n\tsv := ctx.Value(ContextServerVariables)\n\tif sv != nil {\n\t\tif variables, ok := sv.(map[string]string); ok {\n\t\t\treturn variables, nil\n\t\t}\n\t\treturn nil, reportError(\"ctx value of ContextServerVariables has invalid type %T should be map[string]string\", sv)\n\t}\n\treturn nil, nil\n}\n\nfunc getServerOperationVariables(ctx context.Context, endpoint string) (map[string]string, error) {\n\tosv := ctx.Value(ContextOperationServerVariables)\n\tif osv != nil {\n\t\tif operationVariables, ok := osv.(map[string]map[string]string); !ok {\n\t\t\treturn nil, reportError(\"ctx value of ContextOperationServerVariables has invalid type %T should be map[string]map[string]string\", osv)\n\t\t} else {\n\t\t\tvariables, ok := operationVariables[endpoint]\n\t\t\tif ok {\n\t\t\t\treturn variables, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn getServerVariables(ctx)\n}\n\n// ServerURLWithContext returns a new server URL given an endpoint\nfunc (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint string) (string, error) {\n\tsc, ok := c.OperationServers[endpoint]\n\tif !ok {\n\t\tsc = c.Servers\n\t}\n\n\tif ctx == nil {\n\t\treturn sc.URL(0, nil)\n\t}\n\n\tindex, err := getServerOperationIndex(ctx, endpoint)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvariables, err := getServerOperationVariables(ctx, endpoint)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn sc.URL(index, variables)\n}\n"
  },
  {
    "path": "pkg/httpclient/git_push.sh",
    "content": "#!/bin/sh\n# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/\n#\n# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl \"minor update\" \"gitlab.com\"\n\ngit_user_id=$1\ngit_repo_id=$2\nrelease_note=$3\ngit_host=$4\n\nif [ \"$git_host\" = \"\" ]; then\n    git_host=\"github.com\"\n    echo \"[INFO] No command line input provided. Set \\$git_host to $git_host\"\nfi\n\nif [ \"$git_user_id\" = \"\" ]; then\n    git_user_id=\"ory\"\n    echo \"[INFO] No command line input provided. Set \\$git_user_id to $git_user_id\"\nfi\n\nif [ \"$git_repo_id\" = \"\" ]; then\n    git_repo_id=\"client-go\"\n    echo \"[INFO] No command line input provided. Set \\$git_repo_id to $git_repo_id\"\nfi\n\nif [ \"$release_note\" = \"\" ]; then\n    release_note=\"Minor update\"\n    echo \"[INFO] No command line input provided. Set \\$release_note to $release_note\"\nfi\n\n# Initialize the local directory as a Git repository\ngit init\n\n# Adds the files in the local repository and stages them for commit.\ngit add .\n\n# Commits the tracked changes and prepares them to be pushed to a remote repository.\ngit commit -m \"$release_note\"\n\n# Sets the new remote\ngit_remote=$(git remote)\nif [ \"$git_remote\" = \"\" ]; then # git remote not defined\n\n    if [ \"$GIT_TOKEN\" = \"\" ]; then\n        echo \"[INFO] \\$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment.\"\n        git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git\n    else\n        git remote add origin https://${git_user_id}:\"${GIT_TOKEN}\"@${git_host}/${git_user_id}/${git_repo_id}.git\n    fi\n\nfi\n\ngit pull origin master\n\n# Pushes (Forces) the changes in the local repository up to the remote repository\necho \"Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git\"\ngit push origin master 2>&1 | grep -v 'To https'\n"
  },
  {
    "path": "pkg/httpclient/model_authenticator_assurance_level.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// AuthenticatorAssuranceLevel The authenticator assurance level can be one of \\\"aal1\\\", \\\"aal2\\\", or \\\"aal3\\\". A higher number means that it is harder for an attacker to compromise the account.  Generally, \\\"aal1\\\" implies that one authentication factor was used while AAL2 implies that two factors (e.g. password + TOTP) have been used.  To learn more about these levels please head over to: https://www.ory.sh/kratos/docs/concepts/credentials\ntype AuthenticatorAssuranceLevel string\n\n// List of authenticatorAssuranceLevel\nconst (\n\tAUTHENTICATORASSURANCELEVEL_AAL0 AuthenticatorAssuranceLevel = \"aal0\"\n\tAUTHENTICATORASSURANCELEVEL_AAL1 AuthenticatorAssuranceLevel = \"aal1\"\n\tAUTHENTICATORASSURANCELEVEL_AAL2 AuthenticatorAssuranceLevel = \"aal2\"\n\tAUTHENTICATORASSURANCELEVEL_AAL3 AuthenticatorAssuranceLevel = \"aal3\"\n)\n\n// All allowed values of AuthenticatorAssuranceLevel enum\nvar AllowedAuthenticatorAssuranceLevelEnumValues = []AuthenticatorAssuranceLevel{\n\t\"aal0\",\n\t\"aal1\",\n\t\"aal2\",\n\t\"aal3\",\n}\n\nfunc (v *AuthenticatorAssuranceLevel) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := AuthenticatorAssuranceLevel(value)\n\tfor _, existing := range AllowedAuthenticatorAssuranceLevelEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid AuthenticatorAssuranceLevel\", value)\n}\n\n// NewAuthenticatorAssuranceLevelFromValue returns a pointer to a valid AuthenticatorAssuranceLevel\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewAuthenticatorAssuranceLevelFromValue(v string) (*AuthenticatorAssuranceLevel, error) {\n\tev := AuthenticatorAssuranceLevel(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for AuthenticatorAssuranceLevel: valid values are %v\", v, AllowedAuthenticatorAssuranceLevelEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v AuthenticatorAssuranceLevel) IsValid() bool {\n\tfor _, existing := range AllowedAuthenticatorAssuranceLevelEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to authenticatorAssuranceLevel value\nfunc (v AuthenticatorAssuranceLevel) Ptr() *AuthenticatorAssuranceLevel {\n\treturn &v\n}\n\ntype NullableAuthenticatorAssuranceLevel struct {\n\tvalue *AuthenticatorAssuranceLevel\n\tisSet bool\n}\n\nfunc (v NullableAuthenticatorAssuranceLevel) Get() *AuthenticatorAssuranceLevel {\n\treturn v.value\n}\n\nfunc (v *NullableAuthenticatorAssuranceLevel) Set(val *AuthenticatorAssuranceLevel) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableAuthenticatorAssuranceLevel) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableAuthenticatorAssuranceLevel) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableAuthenticatorAssuranceLevel(val *AuthenticatorAssuranceLevel) *NullableAuthenticatorAssuranceLevel {\n\treturn &NullableAuthenticatorAssuranceLevel{value: val, isSet: true}\n}\n\nfunc (v NullableAuthenticatorAssuranceLevel) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableAuthenticatorAssuranceLevel) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_batch_patch_identities_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the BatchPatchIdentitiesResponse type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &BatchPatchIdentitiesResponse{}\n\n// BatchPatchIdentitiesResponse Patch identities response\ntype BatchPatchIdentitiesResponse struct {\n\t// The patch responses for the individual identities.\n\tIdentities           []IdentityPatchResponse `json:\"identities,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _BatchPatchIdentitiesResponse BatchPatchIdentitiesResponse\n\n// NewBatchPatchIdentitiesResponse instantiates a new BatchPatchIdentitiesResponse object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewBatchPatchIdentitiesResponse() *BatchPatchIdentitiesResponse {\n\tthis := BatchPatchIdentitiesResponse{}\n\treturn &this\n}\n\n// NewBatchPatchIdentitiesResponseWithDefaults instantiates a new BatchPatchIdentitiesResponse object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewBatchPatchIdentitiesResponseWithDefaults() *BatchPatchIdentitiesResponse {\n\tthis := BatchPatchIdentitiesResponse{}\n\treturn &this\n}\n\n// GetIdentities returns the Identities field value if set, zero value otherwise.\nfunc (o *BatchPatchIdentitiesResponse) GetIdentities() []IdentityPatchResponse {\n\tif o == nil || IsNil(o.Identities) {\n\t\tvar ret []IdentityPatchResponse\n\t\treturn ret\n\t}\n\treturn o.Identities\n}\n\n// GetIdentitiesOk returns a tuple with the Identities field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *BatchPatchIdentitiesResponse) GetIdentitiesOk() ([]IdentityPatchResponse, bool) {\n\tif o == nil || IsNil(o.Identities) {\n\t\treturn nil, false\n\t}\n\treturn o.Identities, true\n}\n\n// HasIdentities returns a boolean if a field has been set.\nfunc (o *BatchPatchIdentitiesResponse) HasIdentities() bool {\n\tif o != nil && !IsNil(o.Identities) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentities gets a reference to the given []IdentityPatchResponse and assigns it to the Identities field.\nfunc (o *BatchPatchIdentitiesResponse) SetIdentities(v []IdentityPatchResponse) {\n\to.Identities = v\n}\n\nfunc (o BatchPatchIdentitiesResponse) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o BatchPatchIdentitiesResponse) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Identities) {\n\t\ttoSerialize[\"identities\"] = o.Identities\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *BatchPatchIdentitiesResponse) UnmarshalJSON(data []byte) (err error) {\n\tvarBatchPatchIdentitiesResponse := _BatchPatchIdentitiesResponse{}\n\n\terr = json.Unmarshal(data, &varBatchPatchIdentitiesResponse)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = BatchPatchIdentitiesResponse(varBatchPatchIdentitiesResponse)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"identities\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableBatchPatchIdentitiesResponse struct {\n\tvalue *BatchPatchIdentitiesResponse\n\tisSet bool\n}\n\nfunc (v NullableBatchPatchIdentitiesResponse) Get() *BatchPatchIdentitiesResponse {\n\treturn v.value\n}\n\nfunc (v *NullableBatchPatchIdentitiesResponse) Set(val *BatchPatchIdentitiesResponse) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableBatchPatchIdentitiesResponse) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableBatchPatchIdentitiesResponse) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableBatchPatchIdentitiesResponse(val *BatchPatchIdentitiesResponse) *NullableBatchPatchIdentitiesResponse {\n\treturn &NullableBatchPatchIdentitiesResponse{value: val, isSet: true}\n}\n\nfunc (v NullableBatchPatchIdentitiesResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableBatchPatchIdentitiesResponse) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_consistency_request_parameters.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the ConsistencyRequestParameters type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ConsistencyRequestParameters{}\n\n// ConsistencyRequestParameters Control API consistency guarantees\ntype ConsistencyRequestParameters struct {\n\t// Read Consistency Level (preview)  The read consistency level determines the consistency guarantee for reads:  strong (slow): The read is guaranteed to return the most recent data committed at the start of the read. eventual (very fast): The result will return data that is about 4.8 seconds old.  The default consistency guarantee can be changed in the Ory Network Console or using the Ory CLI with `ory patch project --replace '/previews/default_read_consistency_level=\\\"strong\\\"'`.  Setting the default consistency level to `eventual` may cause regressions in the future as we add consistency controls to more APIs. Currently, the following APIs will be affected by this setting:  `GET /admin/identities`  This feature is in preview and only available in Ory Network.  ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level. strong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level. eventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\n\tConsistency          *string `json:\"consistency,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ConsistencyRequestParameters ConsistencyRequestParameters\n\n// NewConsistencyRequestParameters instantiates a new ConsistencyRequestParameters object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewConsistencyRequestParameters() *ConsistencyRequestParameters {\n\tthis := ConsistencyRequestParameters{}\n\treturn &this\n}\n\n// NewConsistencyRequestParametersWithDefaults instantiates a new ConsistencyRequestParameters object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewConsistencyRequestParametersWithDefaults() *ConsistencyRequestParameters {\n\tthis := ConsistencyRequestParameters{}\n\treturn &this\n}\n\n// GetConsistency returns the Consistency field value if set, zero value otherwise.\nfunc (o *ConsistencyRequestParameters) GetConsistency() string {\n\tif o == nil || IsNil(o.Consistency) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Consistency\n}\n\n// GetConsistencyOk returns a tuple with the Consistency field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ConsistencyRequestParameters) GetConsistencyOk() (*string, bool) {\n\tif o == nil || IsNil(o.Consistency) {\n\t\treturn nil, false\n\t}\n\treturn o.Consistency, true\n}\n\n// HasConsistency returns a boolean if a field has been set.\nfunc (o *ConsistencyRequestParameters) HasConsistency() bool {\n\tif o != nil && !IsNil(o.Consistency) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConsistency gets a reference to the given string and assigns it to the Consistency field.\nfunc (o *ConsistencyRequestParameters) SetConsistency(v string) {\n\to.Consistency = &v\n}\n\nfunc (o ConsistencyRequestParameters) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ConsistencyRequestParameters) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Consistency) {\n\t\ttoSerialize[\"consistency\"] = o.Consistency\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ConsistencyRequestParameters) UnmarshalJSON(data []byte) (err error) {\n\tvarConsistencyRequestParameters := _ConsistencyRequestParameters{}\n\n\terr = json.Unmarshal(data, &varConsistencyRequestParameters)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ConsistencyRequestParameters(varConsistencyRequestParameters)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"consistency\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableConsistencyRequestParameters struct {\n\tvalue *ConsistencyRequestParameters\n\tisSet bool\n}\n\nfunc (v NullableConsistencyRequestParameters) Get() *ConsistencyRequestParameters {\n\treturn v.value\n}\n\nfunc (v *NullableConsistencyRequestParameters) Set(val *ConsistencyRequestParameters) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableConsistencyRequestParameters) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableConsistencyRequestParameters) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableConsistencyRequestParameters(val *ConsistencyRequestParameters) *NullableConsistencyRequestParameters {\n\treturn &NullableConsistencyRequestParameters{value: val, isSet: true}\n}\n\nfunc (v NullableConsistencyRequestParameters) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableConsistencyRequestParameters) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_continue_with.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// ContinueWith - struct for ContinueWith\ntype ContinueWith struct {\n\tContinueWithRecoveryUi         *ContinueWithRecoveryUi\n\tContinueWithRedirectBrowserTo  *ContinueWithRedirectBrowserTo\n\tContinueWithSetOrySessionToken *ContinueWithSetOrySessionToken\n\tContinueWithSettingsUi         *ContinueWithSettingsUi\n\tContinueWithVerificationUi     *ContinueWithVerificationUi\n}\n\n// ContinueWithRecoveryUiAsContinueWith is a convenience function that returns ContinueWithRecoveryUi wrapped in ContinueWith\nfunc ContinueWithRecoveryUiAsContinueWith(v *ContinueWithRecoveryUi) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithRecoveryUi: v,\n\t}\n}\n\n// ContinueWithRedirectBrowserToAsContinueWith is a convenience function that returns ContinueWithRedirectBrowserTo wrapped in ContinueWith\nfunc ContinueWithRedirectBrowserToAsContinueWith(v *ContinueWithRedirectBrowserTo) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithRedirectBrowserTo: v,\n\t}\n}\n\n// ContinueWithSetOrySessionTokenAsContinueWith is a convenience function that returns ContinueWithSetOrySessionToken wrapped in ContinueWith\nfunc ContinueWithSetOrySessionTokenAsContinueWith(v *ContinueWithSetOrySessionToken) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithSetOrySessionToken: v,\n\t}\n}\n\n// ContinueWithSettingsUiAsContinueWith is a convenience function that returns ContinueWithSettingsUi wrapped in ContinueWith\nfunc ContinueWithSettingsUiAsContinueWith(v *ContinueWithSettingsUi) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithSettingsUi: v,\n\t}\n}\n\n// ContinueWithVerificationUiAsContinueWith is a convenience function that returns ContinueWithVerificationUi wrapped in ContinueWith\nfunc ContinueWithVerificationUiAsContinueWith(v *ContinueWithVerificationUi) ContinueWith {\n\treturn ContinueWith{\n\t\tContinueWithVerificationUi: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *ContinueWith) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'redirect_browser_to'\n\tif jsonDict[\"action\"] == \"redirect_browser_to\" {\n\t\t// try to unmarshal JSON data into ContinueWithRedirectBrowserTo\n\t\terr = json.Unmarshal(data, &dst.ContinueWithRedirectBrowserTo)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithRedirectBrowserTo, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithRedirectBrowserTo = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithRedirectBrowserTo: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'set_ory_session_token'\n\tif jsonDict[\"action\"] == \"set_ory_session_token\" {\n\t\t// try to unmarshal JSON data into ContinueWithSetOrySessionToken\n\t\terr = json.Unmarshal(data, &dst.ContinueWithSetOrySessionToken)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithSetOrySessionToken, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithSetOrySessionToken = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithSetOrySessionToken: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'show_recovery_ui'\n\tif jsonDict[\"action\"] == \"show_recovery_ui\" {\n\t\t// try to unmarshal JSON data into ContinueWithRecoveryUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithRecoveryUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithRecoveryUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithRecoveryUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithRecoveryUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'show_settings_ui'\n\tif jsonDict[\"action\"] == \"show_settings_ui\" {\n\t\t// try to unmarshal JSON data into ContinueWithSettingsUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithSettingsUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithSettingsUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithSettingsUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithSettingsUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'show_verification_ui'\n\tif jsonDict[\"action\"] == \"show_verification_ui\" {\n\t\t// try to unmarshal JSON data into ContinueWithVerificationUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithVerificationUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithVerificationUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithVerificationUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithVerificationUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithRecoveryUi'\n\tif jsonDict[\"action\"] == \"continueWithRecoveryUi\" {\n\t\t// try to unmarshal JSON data into ContinueWithRecoveryUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithRecoveryUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithRecoveryUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithRecoveryUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithRecoveryUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithRedirectBrowserTo'\n\tif jsonDict[\"action\"] == \"continueWithRedirectBrowserTo\" {\n\t\t// try to unmarshal JSON data into ContinueWithRedirectBrowserTo\n\t\terr = json.Unmarshal(data, &dst.ContinueWithRedirectBrowserTo)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithRedirectBrowserTo, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithRedirectBrowserTo = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithRedirectBrowserTo: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithSetOrySessionToken'\n\tif jsonDict[\"action\"] == \"continueWithSetOrySessionToken\" {\n\t\t// try to unmarshal JSON data into ContinueWithSetOrySessionToken\n\t\terr = json.Unmarshal(data, &dst.ContinueWithSetOrySessionToken)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithSetOrySessionToken, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithSetOrySessionToken = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithSetOrySessionToken: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithSettingsUi'\n\tif jsonDict[\"action\"] == \"continueWithSettingsUi\" {\n\t\t// try to unmarshal JSON data into ContinueWithSettingsUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithSettingsUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithSettingsUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithSettingsUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithSettingsUi: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'continueWithVerificationUi'\n\tif jsonDict[\"action\"] == \"continueWithVerificationUi\" {\n\t\t// try to unmarshal JSON data into ContinueWithVerificationUi\n\t\terr = json.Unmarshal(data, &dst.ContinueWithVerificationUi)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.ContinueWithVerificationUi, return on the first match\n\t\t} else {\n\t\t\tdst.ContinueWithVerificationUi = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal ContinueWith as ContinueWithVerificationUi: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src ContinueWith) MarshalJSON() ([]byte, error) {\n\tif src.ContinueWithRecoveryUi != nil {\n\t\treturn json.Marshal(&src.ContinueWithRecoveryUi)\n\t}\n\n\tif src.ContinueWithRedirectBrowserTo != nil {\n\t\treturn json.Marshal(&src.ContinueWithRedirectBrowserTo)\n\t}\n\n\tif src.ContinueWithSetOrySessionToken != nil {\n\t\treturn json.Marshal(&src.ContinueWithSetOrySessionToken)\n\t}\n\n\tif src.ContinueWithSettingsUi != nil {\n\t\treturn json.Marshal(&src.ContinueWithSettingsUi)\n\t}\n\n\tif src.ContinueWithVerificationUi != nil {\n\t\treturn json.Marshal(&src.ContinueWithVerificationUi)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *ContinueWith) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.ContinueWithRecoveryUi != nil {\n\t\treturn obj.ContinueWithRecoveryUi\n\t}\n\n\tif obj.ContinueWithRedirectBrowserTo != nil {\n\t\treturn obj.ContinueWithRedirectBrowserTo\n\t}\n\n\tif obj.ContinueWithSetOrySessionToken != nil {\n\t\treturn obj.ContinueWithSetOrySessionToken\n\t}\n\n\tif obj.ContinueWithSettingsUi != nil {\n\t\treturn obj.ContinueWithSettingsUi\n\t}\n\n\tif obj.ContinueWithVerificationUi != nil {\n\t\treturn obj.ContinueWithVerificationUi\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj ContinueWith) GetActualInstanceValue() interface{} {\n\tif obj.ContinueWithRecoveryUi != nil {\n\t\treturn *obj.ContinueWithRecoveryUi\n\t}\n\n\tif obj.ContinueWithRedirectBrowserTo != nil {\n\t\treturn *obj.ContinueWithRedirectBrowserTo\n\t}\n\n\tif obj.ContinueWithSetOrySessionToken != nil {\n\t\treturn *obj.ContinueWithSetOrySessionToken\n\t}\n\n\tif obj.ContinueWithSettingsUi != nil {\n\t\treturn *obj.ContinueWithSettingsUi\n\t}\n\n\tif obj.ContinueWithVerificationUi != nil {\n\t\treturn *obj.ContinueWithVerificationUi\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableContinueWith struct {\n\tvalue *ContinueWith\n\tisSet bool\n}\n\nfunc (v NullableContinueWith) Get() *ContinueWith {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWith) Set(val *ContinueWith) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWith) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWith) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWith(val *ContinueWith) *NullableContinueWith {\n\treturn &NullableContinueWith{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWith) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWith) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_continue_with_recovery_ui.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithRecoveryUi type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithRecoveryUi{}\n\n// ContinueWithRecoveryUi Indicates, that the UI flow could be continued by showing a recovery ui\ntype ContinueWithRecoveryUi struct {\n\t// Action will always be `show_recovery_ui` show_recovery_ui ContinueWithActionShowRecoveryUIString\n\tAction               string                     `json:\"action\"`\n\tFlow                 ContinueWithRecoveryUiFlow `json:\"flow\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithRecoveryUi ContinueWithRecoveryUi\n\n// NewContinueWithRecoveryUi instantiates a new ContinueWithRecoveryUi object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithRecoveryUi(action string, flow ContinueWithRecoveryUiFlow) *ContinueWithRecoveryUi {\n\tthis := ContinueWithRecoveryUi{}\n\tthis.Action = action\n\tthis.Flow = flow\n\treturn &this\n}\n\n// NewContinueWithRecoveryUiWithDefaults instantiates a new ContinueWithRecoveryUi object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithRecoveryUiWithDefaults() *ContinueWithRecoveryUi {\n\tthis := ContinueWithRecoveryUi{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithRecoveryUi) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRecoveryUi) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithRecoveryUi) SetAction(v string) {\n\to.Action = v\n}\n\n// GetFlow returns the Flow field value\nfunc (o *ContinueWithRecoveryUi) GetFlow() ContinueWithRecoveryUiFlow {\n\tif o == nil {\n\t\tvar ret ContinueWithRecoveryUiFlow\n\t\treturn ret\n\t}\n\n\treturn o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRecoveryUi) GetFlowOk() (*ContinueWithRecoveryUiFlow, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Flow, true\n}\n\n// SetFlow sets field value\nfunc (o *ContinueWithRecoveryUi) SetFlow(v ContinueWithRecoveryUiFlow) {\n\to.Flow = v\n}\n\nfunc (o ContinueWithRecoveryUi) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithRecoveryUi) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"flow\"] = o.Flow\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithRecoveryUi) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"flow\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithRecoveryUi := _ContinueWithRecoveryUi{}\n\n\terr = json.Unmarshal(data, &varContinueWithRecoveryUi)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithRecoveryUi(varContinueWithRecoveryUi)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"flow\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithRecoveryUi struct {\n\tvalue *ContinueWithRecoveryUi\n\tisSet bool\n}\n\nfunc (v NullableContinueWithRecoveryUi) Get() *ContinueWithRecoveryUi {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithRecoveryUi) Set(val *ContinueWithRecoveryUi) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithRecoveryUi) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithRecoveryUi) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithRecoveryUi(val *ContinueWithRecoveryUi) *NullableContinueWithRecoveryUi {\n\treturn &NullableContinueWithRecoveryUi{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithRecoveryUi) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithRecoveryUi) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_continue_with_recovery_ui_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithRecoveryUiFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithRecoveryUiFlow{}\n\n// ContinueWithRecoveryUiFlow struct for ContinueWithRecoveryUiFlow\ntype ContinueWithRecoveryUiFlow struct {\n\t// The ID of the recovery flow\n\tId string `json:\"id\"`\n\t// The URL of the recovery flow  If this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\n\tUrl                  *string `json:\"url,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithRecoveryUiFlow ContinueWithRecoveryUiFlow\n\n// NewContinueWithRecoveryUiFlow instantiates a new ContinueWithRecoveryUiFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithRecoveryUiFlow(id string) *ContinueWithRecoveryUiFlow {\n\tthis := ContinueWithRecoveryUiFlow{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewContinueWithRecoveryUiFlowWithDefaults instantiates a new ContinueWithRecoveryUiFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithRecoveryUiFlowWithDefaults() *ContinueWithRecoveryUiFlow {\n\tthis := ContinueWithRecoveryUiFlow{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *ContinueWithRecoveryUiFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRecoveryUiFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *ContinueWithRecoveryUiFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetUrl returns the Url field value if set, zero value otherwise.\nfunc (o *ContinueWithRecoveryUiFlow) GetUrl() string {\n\tif o == nil || IsNil(o.Url) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Url\n}\n\n// GetUrlOk returns a tuple with the Url field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRecoveryUiFlow) GetUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.Url) {\n\t\treturn nil, false\n\t}\n\treturn o.Url, true\n}\n\n// HasUrl returns a boolean if a field has been set.\nfunc (o *ContinueWithRecoveryUiFlow) HasUrl() bool {\n\tif o != nil && !IsNil(o.Url) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUrl gets a reference to the given string and assigns it to the Url field.\nfunc (o *ContinueWithRecoveryUiFlow) SetUrl(v string) {\n\to.Url = &v\n}\n\nfunc (o ContinueWithRecoveryUiFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithRecoveryUiFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.Url) {\n\t\ttoSerialize[\"url\"] = o.Url\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithRecoveryUiFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithRecoveryUiFlow := _ContinueWithRecoveryUiFlow{}\n\n\terr = json.Unmarshal(data, &varContinueWithRecoveryUiFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithRecoveryUiFlow(varContinueWithRecoveryUiFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"url\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithRecoveryUiFlow struct {\n\tvalue *ContinueWithRecoveryUiFlow\n\tisSet bool\n}\n\nfunc (v NullableContinueWithRecoveryUiFlow) Get() *ContinueWithRecoveryUiFlow {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithRecoveryUiFlow) Set(val *ContinueWithRecoveryUiFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithRecoveryUiFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithRecoveryUiFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithRecoveryUiFlow(val *ContinueWithRecoveryUiFlow) *NullableContinueWithRecoveryUiFlow {\n\treturn &NullableContinueWithRecoveryUiFlow{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithRecoveryUiFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithRecoveryUiFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_continue_with_redirect_browser_to.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithRedirectBrowserTo type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithRedirectBrowserTo{}\n\n// ContinueWithRedirectBrowserTo Indicates, that the UI flow could be continued by showing a recovery ui\ntype ContinueWithRedirectBrowserTo struct {\n\t// Action will always be `redirect_browser_to` redirect_browser_to ContinueWithActionRedirectBrowserToString\n\tAction string `json:\"action\"`\n\t// The URL to redirect the browser to\n\tRedirectBrowserTo    string `json:\"redirect_browser_to\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithRedirectBrowserTo ContinueWithRedirectBrowserTo\n\n// NewContinueWithRedirectBrowserTo instantiates a new ContinueWithRedirectBrowserTo object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithRedirectBrowserTo(action string, redirectBrowserTo string) *ContinueWithRedirectBrowserTo {\n\tthis := ContinueWithRedirectBrowserTo{}\n\tthis.Action = action\n\tthis.RedirectBrowserTo = redirectBrowserTo\n\treturn &this\n}\n\n// NewContinueWithRedirectBrowserToWithDefaults instantiates a new ContinueWithRedirectBrowserTo object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithRedirectBrowserToWithDefaults() *ContinueWithRedirectBrowserTo {\n\tthis := ContinueWithRedirectBrowserTo{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithRedirectBrowserTo) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRedirectBrowserTo) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithRedirectBrowserTo) SetAction(v string) {\n\to.Action = v\n}\n\n// GetRedirectBrowserTo returns the RedirectBrowserTo field value\nfunc (o *ContinueWithRedirectBrowserTo) GetRedirectBrowserTo() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RedirectBrowserTo\n}\n\n// GetRedirectBrowserToOk returns a tuple with the RedirectBrowserTo field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithRedirectBrowserTo) GetRedirectBrowserToOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RedirectBrowserTo, true\n}\n\n// SetRedirectBrowserTo sets field value\nfunc (o *ContinueWithRedirectBrowserTo) SetRedirectBrowserTo(v string) {\n\to.RedirectBrowserTo = v\n}\n\nfunc (o ContinueWithRedirectBrowserTo) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithRedirectBrowserTo) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"redirect_browser_to\"] = o.RedirectBrowserTo\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithRedirectBrowserTo) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"redirect_browser_to\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithRedirectBrowserTo := _ContinueWithRedirectBrowserTo{}\n\n\terr = json.Unmarshal(data, &varContinueWithRedirectBrowserTo)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithRedirectBrowserTo(varContinueWithRedirectBrowserTo)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"redirect_browser_to\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithRedirectBrowserTo struct {\n\tvalue *ContinueWithRedirectBrowserTo\n\tisSet bool\n}\n\nfunc (v NullableContinueWithRedirectBrowserTo) Get() *ContinueWithRedirectBrowserTo {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithRedirectBrowserTo) Set(val *ContinueWithRedirectBrowserTo) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithRedirectBrowserTo) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithRedirectBrowserTo) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithRedirectBrowserTo(val *ContinueWithRedirectBrowserTo) *NullableContinueWithRedirectBrowserTo {\n\treturn &NullableContinueWithRedirectBrowserTo{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithRedirectBrowserTo) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithRedirectBrowserTo) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_continue_with_set_ory_session_token.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithSetOrySessionToken type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithSetOrySessionToken{}\n\n// ContinueWithSetOrySessionToken Indicates that a session was issued, and the application should use this token for authenticated requests\ntype ContinueWithSetOrySessionToken struct {\n\t// Action will always be `set_ory_session_token` set_ory_session_token ContinueWithActionSetOrySessionTokenString\n\tAction string `json:\"action\"`\n\t// Token is the token of the session\n\tOrySessionToken      string `json:\"ory_session_token\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithSetOrySessionToken ContinueWithSetOrySessionToken\n\n// NewContinueWithSetOrySessionToken instantiates a new ContinueWithSetOrySessionToken object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithSetOrySessionToken(action string, orySessionToken string) *ContinueWithSetOrySessionToken {\n\tthis := ContinueWithSetOrySessionToken{}\n\tthis.Action = action\n\tthis.OrySessionToken = orySessionToken\n\treturn &this\n}\n\n// NewContinueWithSetOrySessionTokenWithDefaults instantiates a new ContinueWithSetOrySessionToken object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithSetOrySessionTokenWithDefaults() *ContinueWithSetOrySessionToken {\n\tthis := ContinueWithSetOrySessionToken{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithSetOrySessionToken) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSetOrySessionToken) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithSetOrySessionToken) SetAction(v string) {\n\to.Action = v\n}\n\n// GetOrySessionToken returns the OrySessionToken field value\nfunc (o *ContinueWithSetOrySessionToken) GetOrySessionToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.OrySessionToken\n}\n\n// GetOrySessionTokenOk returns a tuple with the OrySessionToken field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSetOrySessionToken) GetOrySessionTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.OrySessionToken, true\n}\n\n// SetOrySessionToken sets field value\nfunc (o *ContinueWithSetOrySessionToken) SetOrySessionToken(v string) {\n\to.OrySessionToken = v\n}\n\nfunc (o ContinueWithSetOrySessionToken) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithSetOrySessionToken) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"ory_session_token\"] = o.OrySessionToken\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithSetOrySessionToken) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"ory_session_token\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithSetOrySessionToken := _ContinueWithSetOrySessionToken{}\n\n\terr = json.Unmarshal(data, &varContinueWithSetOrySessionToken)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithSetOrySessionToken(varContinueWithSetOrySessionToken)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"ory_session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithSetOrySessionToken struct {\n\tvalue *ContinueWithSetOrySessionToken\n\tisSet bool\n}\n\nfunc (v NullableContinueWithSetOrySessionToken) Get() *ContinueWithSetOrySessionToken {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithSetOrySessionToken) Set(val *ContinueWithSetOrySessionToken) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithSetOrySessionToken) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithSetOrySessionToken) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithSetOrySessionToken(val *ContinueWithSetOrySessionToken) *NullableContinueWithSetOrySessionToken {\n\treturn &NullableContinueWithSetOrySessionToken{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithSetOrySessionToken) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithSetOrySessionToken) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_continue_with_settings_ui.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithSettingsUi type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithSettingsUi{}\n\n// ContinueWithSettingsUi Indicates, that the UI flow could be continued by showing a settings ui\ntype ContinueWithSettingsUi struct {\n\t// Action will always be `show_settings_ui` show_settings_ui ContinueWithActionShowSettingsUIString\n\tAction               string                     `json:\"action\"`\n\tFlow                 ContinueWithSettingsUiFlow `json:\"flow\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithSettingsUi ContinueWithSettingsUi\n\n// NewContinueWithSettingsUi instantiates a new ContinueWithSettingsUi object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithSettingsUi(action string, flow ContinueWithSettingsUiFlow) *ContinueWithSettingsUi {\n\tthis := ContinueWithSettingsUi{}\n\tthis.Action = action\n\tthis.Flow = flow\n\treturn &this\n}\n\n// NewContinueWithSettingsUiWithDefaults instantiates a new ContinueWithSettingsUi object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithSettingsUiWithDefaults() *ContinueWithSettingsUi {\n\tthis := ContinueWithSettingsUi{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithSettingsUi) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSettingsUi) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithSettingsUi) SetAction(v string) {\n\to.Action = v\n}\n\n// GetFlow returns the Flow field value\nfunc (o *ContinueWithSettingsUi) GetFlow() ContinueWithSettingsUiFlow {\n\tif o == nil {\n\t\tvar ret ContinueWithSettingsUiFlow\n\t\treturn ret\n\t}\n\n\treturn o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSettingsUi) GetFlowOk() (*ContinueWithSettingsUiFlow, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Flow, true\n}\n\n// SetFlow sets field value\nfunc (o *ContinueWithSettingsUi) SetFlow(v ContinueWithSettingsUiFlow) {\n\to.Flow = v\n}\n\nfunc (o ContinueWithSettingsUi) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithSettingsUi) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"flow\"] = o.Flow\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithSettingsUi) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"flow\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithSettingsUi := _ContinueWithSettingsUi{}\n\n\terr = json.Unmarshal(data, &varContinueWithSettingsUi)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithSettingsUi(varContinueWithSettingsUi)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"flow\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithSettingsUi struct {\n\tvalue *ContinueWithSettingsUi\n\tisSet bool\n}\n\nfunc (v NullableContinueWithSettingsUi) Get() *ContinueWithSettingsUi {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithSettingsUi) Set(val *ContinueWithSettingsUi) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithSettingsUi) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithSettingsUi) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithSettingsUi(val *ContinueWithSettingsUi) *NullableContinueWithSettingsUi {\n\treturn &NullableContinueWithSettingsUi{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithSettingsUi) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithSettingsUi) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_continue_with_settings_ui_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithSettingsUiFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithSettingsUiFlow{}\n\n// ContinueWithSettingsUiFlow struct for ContinueWithSettingsUiFlow\ntype ContinueWithSettingsUiFlow struct {\n\t// The ID of the settings flow\n\tId string `json:\"id\"`\n\t// The URL of the settings flow  If this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\n\tUrl                  *string `json:\"url,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithSettingsUiFlow ContinueWithSettingsUiFlow\n\n// NewContinueWithSettingsUiFlow instantiates a new ContinueWithSettingsUiFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithSettingsUiFlow(id string) *ContinueWithSettingsUiFlow {\n\tthis := ContinueWithSettingsUiFlow{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewContinueWithSettingsUiFlowWithDefaults instantiates a new ContinueWithSettingsUiFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithSettingsUiFlowWithDefaults() *ContinueWithSettingsUiFlow {\n\tthis := ContinueWithSettingsUiFlow{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *ContinueWithSettingsUiFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSettingsUiFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *ContinueWithSettingsUiFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetUrl returns the Url field value if set, zero value otherwise.\nfunc (o *ContinueWithSettingsUiFlow) GetUrl() string {\n\tif o == nil || IsNil(o.Url) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Url\n}\n\n// GetUrlOk returns a tuple with the Url field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithSettingsUiFlow) GetUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.Url) {\n\t\treturn nil, false\n\t}\n\treturn o.Url, true\n}\n\n// HasUrl returns a boolean if a field has been set.\nfunc (o *ContinueWithSettingsUiFlow) HasUrl() bool {\n\tif o != nil && !IsNil(o.Url) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUrl gets a reference to the given string and assigns it to the Url field.\nfunc (o *ContinueWithSettingsUiFlow) SetUrl(v string) {\n\to.Url = &v\n}\n\nfunc (o ContinueWithSettingsUiFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithSettingsUiFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.Url) {\n\t\ttoSerialize[\"url\"] = o.Url\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithSettingsUiFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithSettingsUiFlow := _ContinueWithSettingsUiFlow{}\n\n\terr = json.Unmarshal(data, &varContinueWithSettingsUiFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithSettingsUiFlow(varContinueWithSettingsUiFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"url\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithSettingsUiFlow struct {\n\tvalue *ContinueWithSettingsUiFlow\n\tisSet bool\n}\n\nfunc (v NullableContinueWithSettingsUiFlow) Get() *ContinueWithSettingsUiFlow {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithSettingsUiFlow) Set(val *ContinueWithSettingsUiFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithSettingsUiFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithSettingsUiFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithSettingsUiFlow(val *ContinueWithSettingsUiFlow) *NullableContinueWithSettingsUiFlow {\n\treturn &NullableContinueWithSettingsUiFlow{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithSettingsUiFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithSettingsUiFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_continue_with_verification_ui.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithVerificationUi type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithVerificationUi{}\n\n// ContinueWithVerificationUi Indicates, that the UI flow could be continued by showing a verification ui\ntype ContinueWithVerificationUi struct {\n\t// Action will always be `show_verification_ui` show_verification_ui ContinueWithActionShowVerificationUIString\n\tAction               string                         `json:\"action\"`\n\tFlow                 ContinueWithVerificationUiFlow `json:\"flow\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithVerificationUi ContinueWithVerificationUi\n\n// NewContinueWithVerificationUi instantiates a new ContinueWithVerificationUi object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithVerificationUi(action string, flow ContinueWithVerificationUiFlow) *ContinueWithVerificationUi {\n\tthis := ContinueWithVerificationUi{}\n\tthis.Action = action\n\tthis.Flow = flow\n\treturn &this\n}\n\n// NewContinueWithVerificationUiWithDefaults instantiates a new ContinueWithVerificationUi object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithVerificationUiWithDefaults() *ContinueWithVerificationUi {\n\tthis := ContinueWithVerificationUi{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *ContinueWithVerificationUi) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUi) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *ContinueWithVerificationUi) SetAction(v string) {\n\to.Action = v\n}\n\n// GetFlow returns the Flow field value\nfunc (o *ContinueWithVerificationUi) GetFlow() ContinueWithVerificationUiFlow {\n\tif o == nil {\n\t\tvar ret ContinueWithVerificationUiFlow\n\t\treturn ret\n\t}\n\n\treturn o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUi) GetFlowOk() (*ContinueWithVerificationUiFlow, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Flow, true\n}\n\n// SetFlow sets field value\nfunc (o *ContinueWithVerificationUi) SetFlow(v ContinueWithVerificationUiFlow) {\n\to.Flow = v\n}\n\nfunc (o ContinueWithVerificationUi) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithVerificationUi) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\ttoSerialize[\"flow\"] = o.Flow\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithVerificationUi) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"flow\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithVerificationUi := _ContinueWithVerificationUi{}\n\n\terr = json.Unmarshal(data, &varContinueWithVerificationUi)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithVerificationUi(varContinueWithVerificationUi)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"flow\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithVerificationUi struct {\n\tvalue *ContinueWithVerificationUi\n\tisSet bool\n}\n\nfunc (v NullableContinueWithVerificationUi) Get() *ContinueWithVerificationUi {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithVerificationUi) Set(val *ContinueWithVerificationUi) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithVerificationUi) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithVerificationUi) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithVerificationUi(val *ContinueWithVerificationUi) *NullableContinueWithVerificationUi {\n\treturn &NullableContinueWithVerificationUi{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithVerificationUi) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithVerificationUi) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_continue_with_verification_ui_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ContinueWithVerificationUiFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ContinueWithVerificationUiFlow{}\n\n// ContinueWithVerificationUiFlow struct for ContinueWithVerificationUiFlow\ntype ContinueWithVerificationUiFlow struct {\n\t// The ID of the verification flow\n\tId string `json:\"id\"`\n\t// The URL of the verification flow  If this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\n\tUrl *string `json:\"url,omitempty\"`\n\t// The address that should be verified in this flow\n\tVerifiableAddress    string `json:\"verifiable_address\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ContinueWithVerificationUiFlow ContinueWithVerificationUiFlow\n\n// NewContinueWithVerificationUiFlow instantiates a new ContinueWithVerificationUiFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewContinueWithVerificationUiFlow(id string, verifiableAddress string) *ContinueWithVerificationUiFlow {\n\tthis := ContinueWithVerificationUiFlow{}\n\tthis.Id = id\n\tthis.VerifiableAddress = verifiableAddress\n\treturn &this\n}\n\n// NewContinueWithVerificationUiFlowWithDefaults instantiates a new ContinueWithVerificationUiFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewContinueWithVerificationUiFlowWithDefaults() *ContinueWithVerificationUiFlow {\n\tthis := ContinueWithVerificationUiFlow{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *ContinueWithVerificationUiFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUiFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *ContinueWithVerificationUiFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetUrl returns the Url field value if set, zero value otherwise.\nfunc (o *ContinueWithVerificationUiFlow) GetUrl() string {\n\tif o == nil || IsNil(o.Url) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Url\n}\n\n// GetUrlOk returns a tuple with the Url field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUiFlow) GetUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.Url) {\n\t\treturn nil, false\n\t}\n\treturn o.Url, true\n}\n\n// HasUrl returns a boolean if a field has been set.\nfunc (o *ContinueWithVerificationUiFlow) HasUrl() bool {\n\tif o != nil && !IsNil(o.Url) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUrl gets a reference to the given string and assigns it to the Url field.\nfunc (o *ContinueWithVerificationUiFlow) SetUrl(v string) {\n\to.Url = &v\n}\n\n// GetVerifiableAddress returns the VerifiableAddress field value\nfunc (o *ContinueWithVerificationUiFlow) GetVerifiableAddress() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.VerifiableAddress\n}\n\n// GetVerifiableAddressOk returns a tuple with the VerifiableAddress field value\n// and a boolean to check if the value has been set.\nfunc (o *ContinueWithVerificationUiFlow) GetVerifiableAddressOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.VerifiableAddress, true\n}\n\n// SetVerifiableAddress sets field value\nfunc (o *ContinueWithVerificationUiFlow) SetVerifiableAddress(v string) {\n\to.VerifiableAddress = v\n}\n\nfunc (o ContinueWithVerificationUiFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ContinueWithVerificationUiFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.Url) {\n\t\ttoSerialize[\"url\"] = o.Url\n\t}\n\ttoSerialize[\"verifiable_address\"] = o.VerifiableAddress\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ContinueWithVerificationUiFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"verifiable_address\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarContinueWithVerificationUiFlow := _ContinueWithVerificationUiFlow{}\n\n\terr = json.Unmarshal(data, &varContinueWithVerificationUiFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ContinueWithVerificationUiFlow(varContinueWithVerificationUiFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"url\")\n\t\tdelete(additionalProperties, \"verifiable_address\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableContinueWithVerificationUiFlow struct {\n\tvalue *ContinueWithVerificationUiFlow\n\tisSet bool\n}\n\nfunc (v NullableContinueWithVerificationUiFlow) Get() *ContinueWithVerificationUiFlow {\n\treturn v.value\n}\n\nfunc (v *NullableContinueWithVerificationUiFlow) Set(val *ContinueWithVerificationUiFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableContinueWithVerificationUiFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableContinueWithVerificationUiFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableContinueWithVerificationUiFlow(val *ContinueWithVerificationUiFlow) *NullableContinueWithVerificationUiFlow {\n\treturn &NullableContinueWithVerificationUiFlow{value: val, isSet: true}\n}\n\nfunc (v NullableContinueWithVerificationUiFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableContinueWithVerificationUiFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_courier_message_status.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// CourierMessageStatus A Message's Status\ntype CourierMessageStatus string\n\n// List of courierMessageStatus\nconst (\n\tCOURIERMESSAGESTATUS_QUEUED     CourierMessageStatus = \"queued\"\n\tCOURIERMESSAGESTATUS_SENT       CourierMessageStatus = \"sent\"\n\tCOURIERMESSAGESTATUS_PROCESSING CourierMessageStatus = \"processing\"\n\tCOURIERMESSAGESTATUS_ABANDONED  CourierMessageStatus = \"abandoned\"\n)\n\n// All allowed values of CourierMessageStatus enum\nvar AllowedCourierMessageStatusEnumValues = []CourierMessageStatus{\n\t\"queued\",\n\t\"sent\",\n\t\"processing\",\n\t\"abandoned\",\n}\n\nfunc (v *CourierMessageStatus) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := CourierMessageStatus(value)\n\tfor _, existing := range AllowedCourierMessageStatusEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid CourierMessageStatus\", value)\n}\n\n// NewCourierMessageStatusFromValue returns a pointer to a valid CourierMessageStatus\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewCourierMessageStatusFromValue(v string) (*CourierMessageStatus, error) {\n\tev := CourierMessageStatus(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for CourierMessageStatus: valid values are %v\", v, AllowedCourierMessageStatusEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v CourierMessageStatus) IsValid() bool {\n\tfor _, existing := range AllowedCourierMessageStatusEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to courierMessageStatus value\nfunc (v CourierMessageStatus) Ptr() *CourierMessageStatus {\n\treturn &v\n}\n\ntype NullableCourierMessageStatus struct {\n\tvalue *CourierMessageStatus\n\tisSet bool\n}\n\nfunc (v NullableCourierMessageStatus) Get() *CourierMessageStatus {\n\treturn v.value\n}\n\nfunc (v *NullableCourierMessageStatus) Set(val *CourierMessageStatus) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCourierMessageStatus) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCourierMessageStatus) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCourierMessageStatus(val *CourierMessageStatus) *NullableCourierMessageStatus {\n\treturn &NullableCourierMessageStatus{value: val, isSet: true}\n}\n\nfunc (v NullableCourierMessageStatus) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCourierMessageStatus) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_courier_message_type.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// CourierMessageType It can either be `email` or `phone`\ntype CourierMessageType string\n\n// List of courierMessageType\nconst (\n\tCOURIERMESSAGETYPE_EMAIL CourierMessageType = \"email\"\n\tCOURIERMESSAGETYPE_PHONE CourierMessageType = \"phone\"\n)\n\n// All allowed values of CourierMessageType enum\nvar AllowedCourierMessageTypeEnumValues = []CourierMessageType{\n\t\"email\",\n\t\"phone\",\n}\n\nfunc (v *CourierMessageType) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := CourierMessageType(value)\n\tfor _, existing := range AllowedCourierMessageTypeEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid CourierMessageType\", value)\n}\n\n// NewCourierMessageTypeFromValue returns a pointer to a valid CourierMessageType\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewCourierMessageTypeFromValue(v string) (*CourierMessageType, error) {\n\tev := CourierMessageType(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for CourierMessageType: valid values are %v\", v, AllowedCourierMessageTypeEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v CourierMessageType) IsValid() bool {\n\tfor _, existing := range AllowedCourierMessageTypeEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to courierMessageType value\nfunc (v CourierMessageType) Ptr() *CourierMessageType {\n\treturn &v\n}\n\ntype NullableCourierMessageType struct {\n\tvalue *CourierMessageType\n\tisSet bool\n}\n\nfunc (v NullableCourierMessageType) Get() *CourierMessageType {\n\treturn v.value\n}\n\nfunc (v *NullableCourierMessageType) Set(val *CourierMessageType) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCourierMessageType) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCourierMessageType) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCourierMessageType(val *CourierMessageType) *NullableCourierMessageType {\n\treturn &NullableCourierMessageType{value: val, isSet: true}\n}\n\nfunc (v NullableCourierMessageType) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCourierMessageType) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_create_fedcm_flow_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the CreateFedcmFlowResponse type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &CreateFedcmFlowResponse{}\n\n// CreateFedcmFlowResponse Contains a list of all available FedCM providers.\ntype CreateFedcmFlowResponse struct {\n\tCsrfToken            *string    `json:\"csrf_token,omitempty\"`\n\tProviders            []Provider `json:\"providers,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _CreateFedcmFlowResponse CreateFedcmFlowResponse\n\n// NewCreateFedcmFlowResponse instantiates a new CreateFedcmFlowResponse object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewCreateFedcmFlowResponse() *CreateFedcmFlowResponse {\n\tthis := CreateFedcmFlowResponse{}\n\treturn &this\n}\n\n// NewCreateFedcmFlowResponseWithDefaults instantiates a new CreateFedcmFlowResponse object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewCreateFedcmFlowResponseWithDefaults() *CreateFedcmFlowResponse {\n\tthis := CreateFedcmFlowResponse{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *CreateFedcmFlowResponse) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateFedcmFlowResponse) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *CreateFedcmFlowResponse) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *CreateFedcmFlowResponse) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetProviders returns the Providers field value if set, zero value otherwise.\nfunc (o *CreateFedcmFlowResponse) GetProviders() []Provider {\n\tif o == nil || IsNil(o.Providers) {\n\t\tvar ret []Provider\n\t\treturn ret\n\t}\n\treturn o.Providers\n}\n\n// GetProvidersOk returns a tuple with the Providers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateFedcmFlowResponse) GetProvidersOk() ([]Provider, bool) {\n\tif o == nil || IsNil(o.Providers) {\n\t\treturn nil, false\n\t}\n\treturn o.Providers, true\n}\n\n// HasProviders returns a boolean if a field has been set.\nfunc (o *CreateFedcmFlowResponse) HasProviders() bool {\n\tif o != nil && !IsNil(o.Providers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProviders gets a reference to the given []Provider and assigns it to the Providers field.\nfunc (o *CreateFedcmFlowResponse) SetProviders(v []Provider) {\n\to.Providers = v\n}\n\nfunc (o CreateFedcmFlowResponse) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o CreateFedcmFlowResponse) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.Providers) {\n\t\ttoSerialize[\"providers\"] = o.Providers\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *CreateFedcmFlowResponse) UnmarshalJSON(data []byte) (err error) {\n\tvarCreateFedcmFlowResponse := _CreateFedcmFlowResponse{}\n\n\terr = json.Unmarshal(data, &varCreateFedcmFlowResponse)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = CreateFedcmFlowResponse(varCreateFedcmFlowResponse)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"providers\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableCreateFedcmFlowResponse struct {\n\tvalue *CreateFedcmFlowResponse\n\tisSet bool\n}\n\nfunc (v NullableCreateFedcmFlowResponse) Get() *CreateFedcmFlowResponse {\n\treturn v.value\n}\n\nfunc (v *NullableCreateFedcmFlowResponse) Set(val *CreateFedcmFlowResponse) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCreateFedcmFlowResponse) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCreateFedcmFlowResponse) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCreateFedcmFlowResponse(val *CreateFedcmFlowResponse) *NullableCreateFedcmFlowResponse {\n\treturn &NullableCreateFedcmFlowResponse{value: val, isSet: true}\n}\n\nfunc (v NullableCreateFedcmFlowResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCreateFedcmFlowResponse) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_create_identity_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the CreateIdentityBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &CreateIdentityBody{}\n\n// CreateIdentityBody Create Identity Body\ntype CreateIdentityBody struct {\n\tCredentials *IdentityWithCredentials `json:\"credentials,omitempty\"`\n\t// ExternalID is an optional external ID of the identity. This is used to link the identity to an external system. If set, the external ID must be unique across all identities.\n\tExternalId *string `json:\"external_id,omitempty\"`\n\t// Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/<id>`.\n\tMetadataAdmin interface{} `json:\"metadata_admin,omitempty\"`\n\t// Store metadata about the identity which the identity itself can see when calling for example the session endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\n\tMetadataPublic interface{}    `json:\"metadata_public,omitempty\"`\n\tOrganizationId NullableString `json:\"organization_id,omitempty\"`\n\t// RecoveryAddresses contains all the addresses that can be used to recover an identity.  Use this structure to import recovery addresses for an identity. Please keep in mind that the address needs to be represented in the Identity Schema or this field will be overwritten on the next identity update.\n\tRecoveryAddresses []RecoveryIdentityAddress `json:\"recovery_addresses,omitempty\"`\n\t// SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\n\tSchemaId string `json:\"schema_id\"`\n\t// State is the identity's state. active StateActive inactive StateInactive\n\tState *string `json:\"state,omitempty\"`\n\t// Traits represent an identity's traits. The identity is able to create, modify, and delete traits in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_url`.\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// VerifiableAddresses contains all the addresses that can be verified by the user.  Use this structure to import verified addresses for an identity. Please keep in mind that the address needs to be represented in the Identity Schema or this field will be overwritten on the next identity update.\n\tVerifiableAddresses  []VerifiableIdentityAddress `json:\"verifiable_addresses,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _CreateIdentityBody CreateIdentityBody\n\n// NewCreateIdentityBody instantiates a new CreateIdentityBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewCreateIdentityBody(schemaId string, traits map[string]interface{}) *CreateIdentityBody {\n\tthis := CreateIdentityBody{}\n\tthis.SchemaId = schemaId\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewCreateIdentityBodyWithDefaults instantiates a new CreateIdentityBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewCreateIdentityBodyWithDefaults() *CreateIdentityBody {\n\tthis := CreateIdentityBody{}\n\treturn &this\n}\n\n// GetCredentials returns the Credentials field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetCredentials() IdentityWithCredentials {\n\tif o == nil || IsNil(o.Credentials) {\n\t\tvar ret IdentityWithCredentials\n\t\treturn ret\n\t}\n\treturn *o.Credentials\n}\n\n// GetCredentialsOk returns a tuple with the Credentials field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetCredentialsOk() (*IdentityWithCredentials, bool) {\n\tif o == nil || IsNil(o.Credentials) {\n\t\treturn nil, false\n\t}\n\treturn o.Credentials, true\n}\n\n// HasCredentials returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasCredentials() bool {\n\tif o != nil && !IsNil(o.Credentials) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCredentials gets a reference to the given IdentityWithCredentials and assigns it to the Credentials field.\nfunc (o *CreateIdentityBody) SetCredentials(v IdentityWithCredentials) {\n\to.Credentials = &v\n}\n\n// GetExternalId returns the ExternalId field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetExternalId() string {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExternalId\n}\n\n// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetExternalIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\treturn nil, false\n\t}\n\treturn o.ExternalId, true\n}\n\n// HasExternalId returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasExternalId() bool {\n\tif o != nil && !IsNil(o.ExternalId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.\nfunc (o *CreateIdentityBody) SetExternalId(v string) {\n\to.ExternalId = &v\n}\n\n// GetMetadataAdmin returns the MetadataAdmin field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *CreateIdentityBody) GetMetadataAdmin() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataAdmin\n}\n\n// GetMetadataAdminOk returns a tuple with the MetadataAdmin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *CreateIdentityBody) GetMetadataAdminOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataAdmin) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataAdmin, true\n}\n\n// HasMetadataAdmin returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasMetadataAdmin() bool {\n\tif o != nil && !IsNil(o.MetadataAdmin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataAdmin gets a reference to the given interface{} and assigns it to the MetadataAdmin field.\nfunc (o *CreateIdentityBody) SetMetadataAdmin(v interface{}) {\n\to.MetadataAdmin = v\n}\n\n// GetMetadataPublic returns the MetadataPublic field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *CreateIdentityBody) GetMetadataPublic() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataPublic\n}\n\n// GetMetadataPublicOk returns a tuple with the MetadataPublic field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *CreateIdentityBody) GetMetadataPublicOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataPublic) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataPublic, true\n}\n\n// HasMetadataPublic returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasMetadataPublic() bool {\n\tif o != nil && !IsNil(o.MetadataPublic) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataPublic gets a reference to the given interface{} and assigns it to the MetadataPublic field.\nfunc (o *CreateIdentityBody) SetMetadataPublic(v interface{}) {\n\to.MetadataPublic = v\n}\n\n// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *CreateIdentityBody) GetOrganizationId() string {\n\tif o == nil || IsNil(o.OrganizationId.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OrganizationId.Get()\n}\n\n// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *CreateIdentityBody) GetOrganizationIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.OrganizationId.Get(), o.OrganizationId.IsSet()\n}\n\n// HasOrganizationId returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasOrganizationId() bool {\n\tif o != nil && o.OrganizationId.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganizationId gets a reference to the given NullableString and assigns it to the OrganizationId field.\nfunc (o *CreateIdentityBody) SetOrganizationId(v string) {\n\to.OrganizationId.Set(&v)\n}\n\n// SetOrganizationIdNil sets the value for OrganizationId to be an explicit nil\nfunc (o *CreateIdentityBody) SetOrganizationIdNil() {\n\to.OrganizationId.Set(nil)\n}\n\n// UnsetOrganizationId ensures that no value is present for OrganizationId, not even an explicit nil\nfunc (o *CreateIdentityBody) UnsetOrganizationId() {\n\to.OrganizationId.Unset()\n}\n\n// GetRecoveryAddresses returns the RecoveryAddresses field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetRecoveryAddresses() []RecoveryIdentityAddress {\n\tif o == nil || IsNil(o.RecoveryAddresses) {\n\t\tvar ret []RecoveryIdentityAddress\n\t\treturn ret\n\t}\n\treturn o.RecoveryAddresses\n}\n\n// GetRecoveryAddressesOk returns a tuple with the RecoveryAddresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetRecoveryAddressesOk() ([]RecoveryIdentityAddress, bool) {\n\tif o == nil || IsNil(o.RecoveryAddresses) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoveryAddresses, true\n}\n\n// HasRecoveryAddresses returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasRecoveryAddresses() bool {\n\tif o != nil && !IsNil(o.RecoveryAddresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoveryAddresses gets a reference to the given []RecoveryIdentityAddress and assigns it to the RecoveryAddresses field.\nfunc (o *CreateIdentityBody) SetRecoveryAddresses(v []RecoveryIdentityAddress) {\n\to.RecoveryAddresses = v\n}\n\n// GetSchemaId returns the SchemaId field value\nfunc (o *CreateIdentityBody) GetSchemaId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SchemaId\n}\n\n// GetSchemaIdOk returns a tuple with the SchemaId field value\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetSchemaIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SchemaId, true\n}\n\n// SetSchemaId sets field value\nfunc (o *CreateIdentityBody) SetSchemaId(v string) {\n\to.SchemaId = v\n}\n\n// GetState returns the State field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetState() string {\n\tif o == nil || IsNil(o.State) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.State\n}\n\n// GetStateOk returns a tuple with the State field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetStateOk() (*string, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn o.State, true\n}\n\n// HasState returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasState() bool {\n\tif o != nil && !IsNil(o.State) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetState gets a reference to the given string and assigns it to the State field.\nfunc (o *CreateIdentityBody) SetState(v string) {\n\to.State = &v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *CreateIdentityBody) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *CreateIdentityBody) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetVerifiableAddresses returns the VerifiableAddresses field value if set, zero value otherwise.\nfunc (o *CreateIdentityBody) GetVerifiableAddresses() []VerifiableIdentityAddress {\n\tif o == nil || IsNil(o.VerifiableAddresses) {\n\t\tvar ret []VerifiableIdentityAddress\n\t\treturn ret\n\t}\n\treturn o.VerifiableAddresses\n}\n\n// GetVerifiableAddressesOk returns a tuple with the VerifiableAddresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateIdentityBody) GetVerifiableAddressesOk() ([]VerifiableIdentityAddress, bool) {\n\tif o == nil || IsNil(o.VerifiableAddresses) {\n\t\treturn nil, false\n\t}\n\treturn o.VerifiableAddresses, true\n}\n\n// HasVerifiableAddresses returns a boolean if a field has been set.\nfunc (o *CreateIdentityBody) HasVerifiableAddresses() bool {\n\tif o != nil && !IsNil(o.VerifiableAddresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVerifiableAddresses gets a reference to the given []VerifiableIdentityAddress and assigns it to the VerifiableAddresses field.\nfunc (o *CreateIdentityBody) SetVerifiableAddresses(v []VerifiableIdentityAddress) {\n\to.VerifiableAddresses = v\n}\n\nfunc (o CreateIdentityBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o CreateIdentityBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Credentials) {\n\t\ttoSerialize[\"credentials\"] = o.Credentials\n\t}\n\tif !IsNil(o.ExternalId) {\n\t\ttoSerialize[\"external_id\"] = o.ExternalId\n\t}\n\tif o.MetadataAdmin != nil {\n\t\ttoSerialize[\"metadata_admin\"] = o.MetadataAdmin\n\t}\n\tif o.MetadataPublic != nil {\n\t\ttoSerialize[\"metadata_public\"] = o.MetadataPublic\n\t}\n\tif o.OrganizationId.IsSet() {\n\t\ttoSerialize[\"organization_id\"] = o.OrganizationId.Get()\n\t}\n\tif !IsNil(o.RecoveryAddresses) {\n\t\ttoSerialize[\"recovery_addresses\"] = o.RecoveryAddresses\n\t}\n\ttoSerialize[\"schema_id\"] = o.SchemaId\n\tif !IsNil(o.State) {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.VerifiableAddresses) {\n\t\ttoSerialize[\"verifiable_addresses\"] = o.VerifiableAddresses\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *CreateIdentityBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"schema_id\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarCreateIdentityBody := _CreateIdentityBody{}\n\n\terr = json.Unmarshal(data, &varCreateIdentityBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = CreateIdentityBody(varCreateIdentityBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"credentials\")\n\t\tdelete(additionalProperties, \"external_id\")\n\t\tdelete(additionalProperties, \"metadata_admin\")\n\t\tdelete(additionalProperties, \"metadata_public\")\n\t\tdelete(additionalProperties, \"organization_id\")\n\t\tdelete(additionalProperties, \"recovery_addresses\")\n\t\tdelete(additionalProperties, \"schema_id\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"verifiable_addresses\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableCreateIdentityBody struct {\n\tvalue *CreateIdentityBody\n\tisSet bool\n}\n\nfunc (v NullableCreateIdentityBody) Get() *CreateIdentityBody {\n\treturn v.value\n}\n\nfunc (v *NullableCreateIdentityBody) Set(val *CreateIdentityBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCreateIdentityBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCreateIdentityBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCreateIdentityBody(val *CreateIdentityBody) *NullableCreateIdentityBody {\n\treturn &NullableCreateIdentityBody{value: val, isSet: true}\n}\n\nfunc (v NullableCreateIdentityBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCreateIdentityBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_create_recovery_code_for_identity_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the CreateRecoveryCodeForIdentityBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &CreateRecoveryCodeForIdentityBody{}\n\n// CreateRecoveryCodeForIdentityBody Create Recovery Code for Identity Request Body\ntype CreateRecoveryCodeForIdentityBody struct {\n\t// Code Expires In  The recovery code will expire after that amount of time has passed. Defaults to the configuration value of `selfservice.methods.code.config.lifespan`.\n\tExpiresIn *string `json:\"expires_in,omitempty\" validate:\"regexp=^([0-9]+(ns|us|ms|s|m|h))*$\"`\n\t// The flow type can either be `api` or `browser`.\n\tFlowType *string `json:\"flow_type,omitempty\"`\n\t// Identity to Recover  The identity's ID you wish to recover.\n\tIdentityId           string `json:\"identity_id\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _CreateRecoveryCodeForIdentityBody CreateRecoveryCodeForIdentityBody\n\n// NewCreateRecoveryCodeForIdentityBody instantiates a new CreateRecoveryCodeForIdentityBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewCreateRecoveryCodeForIdentityBody(identityId string) *CreateRecoveryCodeForIdentityBody {\n\tthis := CreateRecoveryCodeForIdentityBody{}\n\tthis.IdentityId = identityId\n\treturn &this\n}\n\n// NewCreateRecoveryCodeForIdentityBodyWithDefaults instantiates a new CreateRecoveryCodeForIdentityBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewCreateRecoveryCodeForIdentityBodyWithDefaults() *CreateRecoveryCodeForIdentityBody {\n\tthis := CreateRecoveryCodeForIdentityBody{}\n\treturn &this\n}\n\n// GetExpiresIn returns the ExpiresIn field value if set, zero value otherwise.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetExpiresIn() string {\n\tif o == nil || IsNil(o.ExpiresIn) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExpiresIn\n}\n\n// GetExpiresInOk returns a tuple with the ExpiresIn field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetExpiresInOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExpiresIn) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresIn, true\n}\n\n// HasExpiresIn returns a boolean if a field has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) HasExpiresIn() bool {\n\tif o != nil && !IsNil(o.ExpiresIn) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresIn gets a reference to the given string and assigns it to the ExpiresIn field.\nfunc (o *CreateRecoveryCodeForIdentityBody) SetExpiresIn(v string) {\n\to.ExpiresIn = &v\n}\n\n// GetFlowType returns the FlowType field value if set, zero value otherwise.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetFlowType() string {\n\tif o == nil || IsNil(o.FlowType) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.FlowType\n}\n\n// GetFlowTypeOk returns a tuple with the FlowType field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetFlowTypeOk() (*string, bool) {\n\tif o == nil || IsNil(o.FlowType) {\n\t\treturn nil, false\n\t}\n\treturn o.FlowType, true\n}\n\n// HasFlowType returns a boolean if a field has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) HasFlowType() bool {\n\tif o != nil && !IsNil(o.FlowType) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFlowType gets a reference to the given string and assigns it to the FlowType field.\nfunc (o *CreateRecoveryCodeForIdentityBody) SetFlowType(v string) {\n\to.FlowType = &v\n}\n\n// GetIdentityId returns the IdentityId field value\nfunc (o *CreateRecoveryCodeForIdentityBody) GetIdentityId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.IdentityId\n}\n\n// GetIdentityIdOk returns a tuple with the IdentityId field value\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryCodeForIdentityBody) GetIdentityIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IdentityId, true\n}\n\n// SetIdentityId sets field value\nfunc (o *CreateRecoveryCodeForIdentityBody) SetIdentityId(v string) {\n\to.IdentityId = v\n}\n\nfunc (o CreateRecoveryCodeForIdentityBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o CreateRecoveryCodeForIdentityBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ExpiresIn) {\n\t\ttoSerialize[\"expires_in\"] = o.ExpiresIn\n\t}\n\tif !IsNil(o.FlowType) {\n\t\ttoSerialize[\"flow_type\"] = o.FlowType\n\t}\n\ttoSerialize[\"identity_id\"] = o.IdentityId\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *CreateRecoveryCodeForIdentityBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identity_id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarCreateRecoveryCodeForIdentityBody := _CreateRecoveryCodeForIdentityBody{}\n\n\terr = json.Unmarshal(data, &varCreateRecoveryCodeForIdentityBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = CreateRecoveryCodeForIdentityBody(varCreateRecoveryCodeForIdentityBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"expires_in\")\n\t\tdelete(additionalProperties, \"flow_type\")\n\t\tdelete(additionalProperties, \"identity_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableCreateRecoveryCodeForIdentityBody struct {\n\tvalue *CreateRecoveryCodeForIdentityBody\n\tisSet bool\n}\n\nfunc (v NullableCreateRecoveryCodeForIdentityBody) Get() *CreateRecoveryCodeForIdentityBody {\n\treturn v.value\n}\n\nfunc (v *NullableCreateRecoveryCodeForIdentityBody) Set(val *CreateRecoveryCodeForIdentityBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCreateRecoveryCodeForIdentityBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCreateRecoveryCodeForIdentityBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCreateRecoveryCodeForIdentityBody(val *CreateRecoveryCodeForIdentityBody) *NullableCreateRecoveryCodeForIdentityBody {\n\treturn &NullableCreateRecoveryCodeForIdentityBody{value: val, isSet: true}\n}\n\nfunc (v NullableCreateRecoveryCodeForIdentityBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCreateRecoveryCodeForIdentityBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_create_recovery_link_for_identity_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the CreateRecoveryLinkForIdentityBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &CreateRecoveryLinkForIdentityBody{}\n\n// CreateRecoveryLinkForIdentityBody Create Recovery Link for Identity Request Body\ntype CreateRecoveryLinkForIdentityBody struct {\n\t// Link Expires In  The recovery link will expire after that amount of time has passed. Defaults to the configuration value of `selfservice.methods.code.config.lifespan`.\n\tExpiresIn *string `json:\"expires_in,omitempty\" validate:\"regexp=^[0-9]+(ns|us|ms|s|m|h)$\"`\n\t// Identity to Recover  The identity's ID you wish to recover.\n\tIdentityId           string `json:\"identity_id\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _CreateRecoveryLinkForIdentityBody CreateRecoveryLinkForIdentityBody\n\n// NewCreateRecoveryLinkForIdentityBody instantiates a new CreateRecoveryLinkForIdentityBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewCreateRecoveryLinkForIdentityBody(identityId string) *CreateRecoveryLinkForIdentityBody {\n\tthis := CreateRecoveryLinkForIdentityBody{}\n\tthis.IdentityId = identityId\n\treturn &this\n}\n\n// NewCreateRecoveryLinkForIdentityBodyWithDefaults instantiates a new CreateRecoveryLinkForIdentityBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewCreateRecoveryLinkForIdentityBodyWithDefaults() *CreateRecoveryLinkForIdentityBody {\n\tthis := CreateRecoveryLinkForIdentityBody{}\n\treturn &this\n}\n\n// GetExpiresIn returns the ExpiresIn field value if set, zero value otherwise.\nfunc (o *CreateRecoveryLinkForIdentityBody) GetExpiresIn() string {\n\tif o == nil || IsNil(o.ExpiresIn) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExpiresIn\n}\n\n// GetExpiresInOk returns a tuple with the ExpiresIn field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryLinkForIdentityBody) GetExpiresInOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExpiresIn) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresIn, true\n}\n\n// HasExpiresIn returns a boolean if a field has been set.\nfunc (o *CreateRecoveryLinkForIdentityBody) HasExpiresIn() bool {\n\tif o != nil && !IsNil(o.ExpiresIn) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresIn gets a reference to the given string and assigns it to the ExpiresIn field.\nfunc (o *CreateRecoveryLinkForIdentityBody) SetExpiresIn(v string) {\n\to.ExpiresIn = &v\n}\n\n// GetIdentityId returns the IdentityId field value\nfunc (o *CreateRecoveryLinkForIdentityBody) GetIdentityId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.IdentityId\n}\n\n// GetIdentityIdOk returns a tuple with the IdentityId field value\n// and a boolean to check if the value has been set.\nfunc (o *CreateRecoveryLinkForIdentityBody) GetIdentityIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IdentityId, true\n}\n\n// SetIdentityId sets field value\nfunc (o *CreateRecoveryLinkForIdentityBody) SetIdentityId(v string) {\n\to.IdentityId = v\n}\n\nfunc (o CreateRecoveryLinkForIdentityBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o CreateRecoveryLinkForIdentityBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ExpiresIn) {\n\t\ttoSerialize[\"expires_in\"] = o.ExpiresIn\n\t}\n\ttoSerialize[\"identity_id\"] = o.IdentityId\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *CreateRecoveryLinkForIdentityBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identity_id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarCreateRecoveryLinkForIdentityBody := _CreateRecoveryLinkForIdentityBody{}\n\n\terr = json.Unmarshal(data, &varCreateRecoveryLinkForIdentityBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = CreateRecoveryLinkForIdentityBody(varCreateRecoveryLinkForIdentityBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"expires_in\")\n\t\tdelete(additionalProperties, \"identity_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableCreateRecoveryLinkForIdentityBody struct {\n\tvalue *CreateRecoveryLinkForIdentityBody\n\tisSet bool\n}\n\nfunc (v NullableCreateRecoveryLinkForIdentityBody) Get() *CreateRecoveryLinkForIdentityBody {\n\treturn v.value\n}\n\nfunc (v *NullableCreateRecoveryLinkForIdentityBody) Set(val *CreateRecoveryLinkForIdentityBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableCreateRecoveryLinkForIdentityBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableCreateRecoveryLinkForIdentityBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableCreateRecoveryLinkForIdentityBody(val *CreateRecoveryLinkForIdentityBody) *NullableCreateRecoveryLinkForIdentityBody {\n\treturn &NullableCreateRecoveryLinkForIdentityBody{value: val, isSet: true}\n}\n\nfunc (v NullableCreateRecoveryLinkForIdentityBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableCreateRecoveryLinkForIdentityBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_delete_my_sessions_count.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the DeleteMySessionsCount type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &DeleteMySessionsCount{}\n\n// DeleteMySessionsCount Deleted Session Count\ntype DeleteMySessionsCount struct {\n\t// The number of sessions that were revoked.\n\tCount                *int64 `json:\"count,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _DeleteMySessionsCount DeleteMySessionsCount\n\n// NewDeleteMySessionsCount instantiates a new DeleteMySessionsCount object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewDeleteMySessionsCount() *DeleteMySessionsCount {\n\tthis := DeleteMySessionsCount{}\n\treturn &this\n}\n\n// NewDeleteMySessionsCountWithDefaults instantiates a new DeleteMySessionsCount object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewDeleteMySessionsCountWithDefaults() *DeleteMySessionsCount {\n\tthis := DeleteMySessionsCount{}\n\treturn &this\n}\n\n// GetCount returns the Count field value if set, zero value otherwise.\nfunc (o *DeleteMySessionsCount) GetCount() int64 {\n\tif o == nil || IsNil(o.Count) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Count\n}\n\n// GetCountOk returns a tuple with the Count field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *DeleteMySessionsCount) GetCountOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Count) {\n\t\treturn nil, false\n\t}\n\treturn o.Count, true\n}\n\n// HasCount returns a boolean if a field has been set.\nfunc (o *DeleteMySessionsCount) HasCount() bool {\n\tif o != nil && !IsNil(o.Count) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCount gets a reference to the given int64 and assigns it to the Count field.\nfunc (o *DeleteMySessionsCount) SetCount(v int64) {\n\to.Count = &v\n}\n\nfunc (o DeleteMySessionsCount) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o DeleteMySessionsCount) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Count) {\n\t\ttoSerialize[\"count\"] = o.Count\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *DeleteMySessionsCount) UnmarshalJSON(data []byte) (err error) {\n\tvarDeleteMySessionsCount := _DeleteMySessionsCount{}\n\n\terr = json.Unmarshal(data, &varDeleteMySessionsCount)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = DeleteMySessionsCount(varDeleteMySessionsCount)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"count\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableDeleteMySessionsCount struct {\n\tvalue *DeleteMySessionsCount\n\tisSet bool\n}\n\nfunc (v NullableDeleteMySessionsCount) Get() *DeleteMySessionsCount {\n\treturn v.value\n}\n\nfunc (v *NullableDeleteMySessionsCount) Set(val *DeleteMySessionsCount) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableDeleteMySessionsCount) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableDeleteMySessionsCount) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableDeleteMySessionsCount(val *DeleteMySessionsCount) *NullableDeleteMySessionsCount {\n\treturn &NullableDeleteMySessionsCount{value: val, isSet: true}\n}\n\nfunc (v NullableDeleteMySessionsCount) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableDeleteMySessionsCount) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_error_authenticator_assurance_level_not_satisfied.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the ErrorAuthenticatorAssuranceLevelNotSatisfied type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ErrorAuthenticatorAssuranceLevelNotSatisfied{}\n\n// ErrorAuthenticatorAssuranceLevelNotSatisfied struct for ErrorAuthenticatorAssuranceLevelNotSatisfied\ntype ErrorAuthenticatorAssuranceLevelNotSatisfied struct {\n\tError *GenericError `json:\"error,omitempty\"`\n\t// Points to where to redirect the user to next.\n\tRedirectBrowserTo    *string `json:\"redirect_browser_to,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ErrorAuthenticatorAssuranceLevelNotSatisfied ErrorAuthenticatorAssuranceLevelNotSatisfied\n\n// NewErrorAuthenticatorAssuranceLevelNotSatisfied instantiates a new ErrorAuthenticatorAssuranceLevelNotSatisfied object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewErrorAuthenticatorAssuranceLevelNotSatisfied() *ErrorAuthenticatorAssuranceLevelNotSatisfied {\n\tthis := ErrorAuthenticatorAssuranceLevelNotSatisfied{}\n\treturn &this\n}\n\n// NewErrorAuthenticatorAssuranceLevelNotSatisfiedWithDefaults instantiates a new ErrorAuthenticatorAssuranceLevelNotSatisfied object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewErrorAuthenticatorAssuranceLevelNotSatisfiedWithDefaults() *ErrorAuthenticatorAssuranceLevelNotSatisfied {\n\tthis := ErrorAuthenticatorAssuranceLevelNotSatisfied{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) GetError() GenericError {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) GetErrorOk() (*GenericError, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given GenericError and assigns it to the Error field.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) SetError(v GenericError) {\n\to.Error = &v\n}\n\n// GetRedirectBrowserTo returns the RedirectBrowserTo field value if set, zero value otherwise.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) GetRedirectBrowserTo() string {\n\tif o == nil || IsNil(o.RedirectBrowserTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RedirectBrowserTo\n}\n\n// GetRedirectBrowserToOk returns a tuple with the RedirectBrowserTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) GetRedirectBrowserToOk() (*string, bool) {\n\tif o == nil || IsNil(o.RedirectBrowserTo) {\n\t\treturn nil, false\n\t}\n\treturn o.RedirectBrowserTo, true\n}\n\n// HasRedirectBrowserTo returns a boolean if a field has been set.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) HasRedirectBrowserTo() bool {\n\tif o != nil && !IsNil(o.RedirectBrowserTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRedirectBrowserTo gets a reference to the given string and assigns it to the RedirectBrowserTo field.\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) SetRedirectBrowserTo(v string) {\n\to.RedirectBrowserTo = &v\n}\n\nfunc (o ErrorAuthenticatorAssuranceLevelNotSatisfied) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ErrorAuthenticatorAssuranceLevelNotSatisfied) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.RedirectBrowserTo) {\n\t\ttoSerialize[\"redirect_browser_to\"] = o.RedirectBrowserTo\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ErrorAuthenticatorAssuranceLevelNotSatisfied) UnmarshalJSON(data []byte) (err error) {\n\tvarErrorAuthenticatorAssuranceLevelNotSatisfied := _ErrorAuthenticatorAssuranceLevelNotSatisfied{}\n\n\terr = json.Unmarshal(data, &varErrorAuthenticatorAssuranceLevelNotSatisfied)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ErrorAuthenticatorAssuranceLevelNotSatisfied(varErrorAuthenticatorAssuranceLevelNotSatisfied)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"redirect_browser_to\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableErrorAuthenticatorAssuranceLevelNotSatisfied struct {\n\tvalue *ErrorAuthenticatorAssuranceLevelNotSatisfied\n\tisSet bool\n}\n\nfunc (v NullableErrorAuthenticatorAssuranceLevelNotSatisfied) Get() *ErrorAuthenticatorAssuranceLevelNotSatisfied {\n\treturn v.value\n}\n\nfunc (v *NullableErrorAuthenticatorAssuranceLevelNotSatisfied) Set(val *ErrorAuthenticatorAssuranceLevelNotSatisfied) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableErrorAuthenticatorAssuranceLevelNotSatisfied) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableErrorAuthenticatorAssuranceLevelNotSatisfied) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableErrorAuthenticatorAssuranceLevelNotSatisfied(val *ErrorAuthenticatorAssuranceLevelNotSatisfied) *NullableErrorAuthenticatorAssuranceLevelNotSatisfied {\n\treturn &NullableErrorAuthenticatorAssuranceLevelNotSatisfied{value: val, isSet: true}\n}\n\nfunc (v NullableErrorAuthenticatorAssuranceLevelNotSatisfied) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableErrorAuthenticatorAssuranceLevelNotSatisfied) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_error_browser_location_change_required.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the ErrorBrowserLocationChangeRequired type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ErrorBrowserLocationChangeRequired{}\n\n// ErrorBrowserLocationChangeRequired struct for ErrorBrowserLocationChangeRequired\ntype ErrorBrowserLocationChangeRequired struct {\n\tError *ErrorGeneric `json:\"error,omitempty\"`\n\t// Points to where to redirect the user to next.\n\tRedirectBrowserTo    *string `json:\"redirect_browser_to,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ErrorBrowserLocationChangeRequired ErrorBrowserLocationChangeRequired\n\n// NewErrorBrowserLocationChangeRequired instantiates a new ErrorBrowserLocationChangeRequired object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewErrorBrowserLocationChangeRequired() *ErrorBrowserLocationChangeRequired {\n\tthis := ErrorBrowserLocationChangeRequired{}\n\treturn &this\n}\n\n// NewErrorBrowserLocationChangeRequiredWithDefaults instantiates a new ErrorBrowserLocationChangeRequired object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewErrorBrowserLocationChangeRequiredWithDefaults() *ErrorBrowserLocationChangeRequired {\n\tthis := ErrorBrowserLocationChangeRequired{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *ErrorBrowserLocationChangeRequired) GetError() ErrorGeneric {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret ErrorGeneric\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorBrowserLocationChangeRequired) GetErrorOk() (*ErrorGeneric, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *ErrorBrowserLocationChangeRequired) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given ErrorGeneric and assigns it to the Error field.\nfunc (o *ErrorBrowserLocationChangeRequired) SetError(v ErrorGeneric) {\n\to.Error = &v\n}\n\n// GetRedirectBrowserTo returns the RedirectBrowserTo field value if set, zero value otherwise.\nfunc (o *ErrorBrowserLocationChangeRequired) GetRedirectBrowserTo() string {\n\tif o == nil || IsNil(o.RedirectBrowserTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RedirectBrowserTo\n}\n\n// GetRedirectBrowserToOk returns a tuple with the RedirectBrowserTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorBrowserLocationChangeRequired) GetRedirectBrowserToOk() (*string, bool) {\n\tif o == nil || IsNil(o.RedirectBrowserTo) {\n\t\treturn nil, false\n\t}\n\treturn o.RedirectBrowserTo, true\n}\n\n// HasRedirectBrowserTo returns a boolean if a field has been set.\nfunc (o *ErrorBrowserLocationChangeRequired) HasRedirectBrowserTo() bool {\n\tif o != nil && !IsNil(o.RedirectBrowserTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRedirectBrowserTo gets a reference to the given string and assigns it to the RedirectBrowserTo field.\nfunc (o *ErrorBrowserLocationChangeRequired) SetRedirectBrowserTo(v string) {\n\to.RedirectBrowserTo = &v\n}\n\nfunc (o ErrorBrowserLocationChangeRequired) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ErrorBrowserLocationChangeRequired) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.RedirectBrowserTo) {\n\t\ttoSerialize[\"redirect_browser_to\"] = o.RedirectBrowserTo\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ErrorBrowserLocationChangeRequired) UnmarshalJSON(data []byte) (err error) {\n\tvarErrorBrowserLocationChangeRequired := _ErrorBrowserLocationChangeRequired{}\n\n\terr = json.Unmarshal(data, &varErrorBrowserLocationChangeRequired)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ErrorBrowserLocationChangeRequired(varErrorBrowserLocationChangeRequired)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"redirect_browser_to\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableErrorBrowserLocationChangeRequired struct {\n\tvalue *ErrorBrowserLocationChangeRequired\n\tisSet bool\n}\n\nfunc (v NullableErrorBrowserLocationChangeRequired) Get() *ErrorBrowserLocationChangeRequired {\n\treturn v.value\n}\n\nfunc (v *NullableErrorBrowserLocationChangeRequired) Set(val *ErrorBrowserLocationChangeRequired) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableErrorBrowserLocationChangeRequired) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableErrorBrowserLocationChangeRequired) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableErrorBrowserLocationChangeRequired(val *ErrorBrowserLocationChangeRequired) *NullableErrorBrowserLocationChangeRequired {\n\treturn &NullableErrorBrowserLocationChangeRequired{value: val, isSet: true}\n}\n\nfunc (v NullableErrorBrowserLocationChangeRequired) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableErrorBrowserLocationChangeRequired) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_error_flow_replaced.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the ErrorFlowReplaced type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ErrorFlowReplaced{}\n\n// ErrorFlowReplaced Is sent when a flow is replaced by a different flow of the same class\ntype ErrorFlowReplaced struct {\n\tError *GenericError `json:\"error,omitempty\"`\n\t// The flow ID that should be used for the new flow as it contains the correct messages.\n\tUseFlowId            *string `json:\"use_flow_id,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ErrorFlowReplaced ErrorFlowReplaced\n\n// NewErrorFlowReplaced instantiates a new ErrorFlowReplaced object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewErrorFlowReplaced() *ErrorFlowReplaced {\n\tthis := ErrorFlowReplaced{}\n\treturn &this\n}\n\n// NewErrorFlowReplacedWithDefaults instantiates a new ErrorFlowReplaced object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewErrorFlowReplacedWithDefaults() *ErrorFlowReplaced {\n\tthis := ErrorFlowReplaced{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *ErrorFlowReplaced) GetError() GenericError {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorFlowReplaced) GetErrorOk() (*GenericError, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *ErrorFlowReplaced) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given GenericError and assigns it to the Error field.\nfunc (o *ErrorFlowReplaced) SetError(v GenericError) {\n\to.Error = &v\n}\n\n// GetUseFlowId returns the UseFlowId field value if set, zero value otherwise.\nfunc (o *ErrorFlowReplaced) GetUseFlowId() string {\n\tif o == nil || IsNil(o.UseFlowId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.UseFlowId\n}\n\n// GetUseFlowIdOk returns a tuple with the UseFlowId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *ErrorFlowReplaced) GetUseFlowIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.UseFlowId) {\n\t\treturn nil, false\n\t}\n\treturn o.UseFlowId, true\n}\n\n// HasUseFlowId returns a boolean if a field has been set.\nfunc (o *ErrorFlowReplaced) HasUseFlowId() bool {\n\tif o != nil && !IsNil(o.UseFlowId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUseFlowId gets a reference to the given string and assigns it to the UseFlowId field.\nfunc (o *ErrorFlowReplaced) SetUseFlowId(v string) {\n\to.UseFlowId = &v\n}\n\nfunc (o ErrorFlowReplaced) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ErrorFlowReplaced) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.UseFlowId) {\n\t\ttoSerialize[\"use_flow_id\"] = o.UseFlowId\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ErrorFlowReplaced) UnmarshalJSON(data []byte) (err error) {\n\tvarErrorFlowReplaced := _ErrorFlowReplaced{}\n\n\terr = json.Unmarshal(data, &varErrorFlowReplaced)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ErrorFlowReplaced(varErrorFlowReplaced)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"use_flow_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableErrorFlowReplaced struct {\n\tvalue *ErrorFlowReplaced\n\tisSet bool\n}\n\nfunc (v NullableErrorFlowReplaced) Get() *ErrorFlowReplaced {\n\treturn v.value\n}\n\nfunc (v *NullableErrorFlowReplaced) Set(val *ErrorFlowReplaced) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableErrorFlowReplaced) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableErrorFlowReplaced) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableErrorFlowReplaced(val *ErrorFlowReplaced) *NullableErrorFlowReplaced {\n\treturn &NullableErrorFlowReplaced{value: val, isSet: true}\n}\n\nfunc (v NullableErrorFlowReplaced) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableErrorFlowReplaced) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_error_generic.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the ErrorGeneric type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &ErrorGeneric{}\n\n// ErrorGeneric The standard Ory JSON API error format.\ntype ErrorGeneric struct {\n\tError                GenericError `json:\"error\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _ErrorGeneric ErrorGeneric\n\n// NewErrorGeneric instantiates a new ErrorGeneric object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewErrorGeneric(error_ GenericError) *ErrorGeneric {\n\tthis := ErrorGeneric{}\n\tthis.Error = error_\n\treturn &this\n}\n\n// NewErrorGenericWithDefaults instantiates a new ErrorGeneric object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewErrorGenericWithDefaults() *ErrorGeneric {\n\tthis := ErrorGeneric{}\n\treturn &this\n}\n\n// GetError returns the Error field value\nfunc (o *ErrorGeneric) GetError() GenericError {\n\tif o == nil {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\n\treturn o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value\n// and a boolean to check if the value has been set.\nfunc (o *ErrorGeneric) GetErrorOk() (*GenericError, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Error, true\n}\n\n// SetError sets field value\nfunc (o *ErrorGeneric) SetError(v GenericError) {\n\to.Error = v\n}\n\nfunc (o ErrorGeneric) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o ErrorGeneric) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"error\"] = o.Error\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *ErrorGeneric) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"error\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarErrorGeneric := _ErrorGeneric{}\n\n\terr = json.Unmarshal(data, &varErrorGeneric)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = ErrorGeneric(varErrorGeneric)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableErrorGeneric struct {\n\tvalue *ErrorGeneric\n\tisSet bool\n}\n\nfunc (v NullableErrorGeneric) Get() *ErrorGeneric {\n\treturn v.value\n}\n\nfunc (v *NullableErrorGeneric) Set(val *ErrorGeneric) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableErrorGeneric) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableErrorGeneric) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableErrorGeneric(val *ErrorGeneric) *NullableErrorGeneric {\n\treturn &NullableErrorGeneric{value: val, isSet: true}\n}\n\nfunc (v NullableErrorGeneric) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableErrorGeneric) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_flow_error.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the FlowError type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &FlowError{}\n\n// FlowError struct for FlowError\ntype FlowError struct {\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time             `json:\"created_at,omitempty\"`\n\tError     map[string]interface{} `json:\"error,omitempty\"`\n\t// ID of the error container.\n\tId string `json:\"id\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            *time.Time `json:\"updated_at,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _FlowError FlowError\n\n// NewFlowError instantiates a new FlowError object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewFlowError(id string) *FlowError {\n\tthis := FlowError{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewFlowErrorWithDefaults instantiates a new FlowError object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewFlowErrorWithDefaults() *FlowError {\n\tthis := FlowError{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *FlowError) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *FlowError) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *FlowError) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *FlowError) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *FlowError) GetError() map[string]interface{} {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *FlowError) GetErrorOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *FlowError) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given map[string]interface{} and assigns it to the Error field.\nfunc (o *FlowError) SetError(v map[string]interface{}) {\n\to.Error = v\n}\n\n// GetId returns the Id field value\nfunc (o *FlowError) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *FlowError) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *FlowError) SetId(v string) {\n\to.Id = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *FlowError) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *FlowError) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *FlowError) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *FlowError) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\nfunc (o FlowError) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o FlowError) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *FlowError) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarFlowError := _FlowError{}\n\n\terr = json.Unmarshal(data, &varFlowError)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = FlowError(varFlowError)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableFlowError struct {\n\tvalue *FlowError\n\tisSet bool\n}\n\nfunc (v NullableFlowError) Get() *FlowError {\n\treturn v.value\n}\n\nfunc (v *NullableFlowError) Set(val *FlowError) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableFlowError) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableFlowError) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableFlowError(val *FlowError) *NullableFlowError {\n\treturn &NullableFlowError{value: val, isSet: true}\n}\n\nfunc (v NullableFlowError) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableFlowError) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_generic_error.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the GenericError type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &GenericError{}\n\n// GenericError struct for GenericError\ntype GenericError struct {\n\t// The status code\n\tCode *int64 `json:\"code,omitempty\"`\n\t// Debug information  This field is often not exposed to protect against leaking sensitive information.\n\tDebug *string `json:\"debug,omitempty\"`\n\t// Further error details\n\tDetails map[string]interface{} `json:\"details,omitempty\"`\n\t// The error ID  Useful when trying to identify various errors in application logic.\n\tId *string `json:\"id,omitempty\"`\n\t// Error message  The error's message.\n\tMessage string `json:\"message\"`\n\t// A human-readable reason for the error\n\tReason *string `json:\"reason,omitempty\"`\n\t// The request ID  The request ID is often exposed internally in order to trace errors across service architectures. This is often a UUID.\n\tRequest *string `json:\"request,omitempty\"`\n\t// The status description\n\tStatus               *string `json:\"status,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _GenericError GenericError\n\n// NewGenericError instantiates a new GenericError object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewGenericError(message string) *GenericError {\n\tthis := GenericError{}\n\tthis.Message = message\n\treturn &this\n}\n\n// NewGenericErrorWithDefaults instantiates a new GenericError object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewGenericErrorWithDefaults() *GenericError {\n\tthis := GenericError{}\n\treturn &this\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *GenericError) GetCode() int64 {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetCodeOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *GenericError) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given int64 and assigns it to the Code field.\nfunc (o *GenericError) SetCode(v int64) {\n\to.Code = &v\n}\n\n// GetDebug returns the Debug field value if set, zero value otherwise.\nfunc (o *GenericError) GetDebug() string {\n\tif o == nil || IsNil(o.Debug) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Debug\n}\n\n// GetDebugOk returns a tuple with the Debug field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetDebugOk() (*string, bool) {\n\tif o == nil || IsNil(o.Debug) {\n\t\treturn nil, false\n\t}\n\treturn o.Debug, true\n}\n\n// HasDebug returns a boolean if a field has been set.\nfunc (o *GenericError) HasDebug() bool {\n\tif o != nil && !IsNil(o.Debug) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDebug gets a reference to the given string and assigns it to the Debug field.\nfunc (o *GenericError) SetDebug(v string) {\n\to.Debug = &v\n}\n\n// GetDetails returns the Details field value if set, zero value otherwise.\nfunc (o *GenericError) GetDetails() map[string]interface{} {\n\tif o == nil || IsNil(o.Details) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Details\n}\n\n// GetDetailsOk returns a tuple with the Details field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetDetailsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Details) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Details, true\n}\n\n// HasDetails returns a boolean if a field has been set.\nfunc (o *GenericError) HasDetails() bool {\n\tif o != nil && !IsNil(o.Details) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDetails gets a reference to the given map[string]interface{} and assigns it to the Details field.\nfunc (o *GenericError) SetDetails(v map[string]interface{}) {\n\to.Details = v\n}\n\n// GetId returns the Id field value if set, zero value otherwise.\nfunc (o *GenericError) GetId() string {\n\tif o == nil || IsNil(o.Id) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.Id) {\n\t\treturn nil, false\n\t}\n\treturn o.Id, true\n}\n\n// HasId returns a boolean if a field has been set.\nfunc (o *GenericError) HasId() bool {\n\tif o != nil && !IsNil(o.Id) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetId gets a reference to the given string and assigns it to the Id field.\nfunc (o *GenericError) SetId(v string) {\n\to.Id = &v\n}\n\n// GetMessage returns the Message field value\nfunc (o *GenericError) GetMessage() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Message\n}\n\n// GetMessageOk returns a tuple with the Message field value\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetMessageOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Message, true\n}\n\n// SetMessage sets field value\nfunc (o *GenericError) SetMessage(v string) {\n\to.Message = v\n}\n\n// GetReason returns the Reason field value if set, zero value otherwise.\nfunc (o *GenericError) GetReason() string {\n\tif o == nil || IsNil(o.Reason) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Reason\n}\n\n// GetReasonOk returns a tuple with the Reason field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetReasonOk() (*string, bool) {\n\tif o == nil || IsNil(o.Reason) {\n\t\treturn nil, false\n\t}\n\treturn o.Reason, true\n}\n\n// HasReason returns a boolean if a field has been set.\nfunc (o *GenericError) HasReason() bool {\n\tif o != nil && !IsNil(o.Reason) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReason gets a reference to the given string and assigns it to the Reason field.\nfunc (o *GenericError) SetReason(v string) {\n\to.Reason = &v\n}\n\n// GetRequest returns the Request field value if set, zero value otherwise.\nfunc (o *GenericError) GetRequest() string {\n\tif o == nil || IsNil(o.Request) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Request\n}\n\n// GetRequestOk returns a tuple with the Request field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetRequestOk() (*string, bool) {\n\tif o == nil || IsNil(o.Request) {\n\t\treturn nil, false\n\t}\n\treturn o.Request, true\n}\n\n// HasRequest returns a boolean if a field has been set.\nfunc (o *GenericError) HasRequest() bool {\n\tif o != nil && !IsNil(o.Request) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequest gets a reference to the given string and assigns it to the Request field.\nfunc (o *GenericError) SetRequest(v string) {\n\to.Request = &v\n}\n\n// GetStatus returns the Status field value if set, zero value otherwise.\nfunc (o *GenericError) GetStatus() string {\n\tif o == nil || IsNil(o.Status) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *GenericError) GetStatusOk() (*string, bool) {\n\tif o == nil || IsNil(o.Status) {\n\t\treturn nil, false\n\t}\n\treturn o.Status, true\n}\n\n// HasStatus returns a boolean if a field has been set.\nfunc (o *GenericError) HasStatus() bool {\n\tif o != nil && !IsNil(o.Status) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetStatus gets a reference to the given string and assigns it to the Status field.\nfunc (o *GenericError) SetStatus(v string) {\n\to.Status = &v\n}\n\nfunc (o GenericError) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o GenericError) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\tif !IsNil(o.Debug) {\n\t\ttoSerialize[\"debug\"] = o.Debug\n\t}\n\tif !IsNil(o.Details) {\n\t\ttoSerialize[\"details\"] = o.Details\n\t}\n\tif !IsNil(o.Id) {\n\t\ttoSerialize[\"id\"] = o.Id\n\t}\n\ttoSerialize[\"message\"] = o.Message\n\tif !IsNil(o.Reason) {\n\t\ttoSerialize[\"reason\"] = o.Reason\n\t}\n\tif !IsNil(o.Request) {\n\t\ttoSerialize[\"request\"] = o.Request\n\t}\n\tif !IsNil(o.Status) {\n\t\ttoSerialize[\"status\"] = o.Status\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *GenericError) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"message\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarGenericError := _GenericError{}\n\n\terr = json.Unmarshal(data, &varGenericError)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = GenericError(varGenericError)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"debug\")\n\t\tdelete(additionalProperties, \"details\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"message\")\n\t\tdelete(additionalProperties, \"reason\")\n\t\tdelete(additionalProperties, \"request\")\n\t\tdelete(additionalProperties, \"status\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableGenericError struct {\n\tvalue *GenericError\n\tisSet bool\n}\n\nfunc (v NullableGenericError) Get() *GenericError {\n\treturn v.value\n}\n\nfunc (v *NullableGenericError) Set(val *GenericError) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableGenericError) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableGenericError) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableGenericError(val *GenericError) *NullableGenericError {\n\treturn &NullableGenericError{value: val, isSet: true}\n}\n\nfunc (v NullableGenericError) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableGenericError) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_get_version_200_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the GetVersion200Response type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &GetVersion200Response{}\n\n// GetVersion200Response struct for GetVersion200Response\ntype GetVersion200Response struct {\n\t// The version of Ory Kratos.\n\tVersion              string `json:\"version\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _GetVersion200Response GetVersion200Response\n\n// NewGetVersion200Response instantiates a new GetVersion200Response object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewGetVersion200Response(version string) *GetVersion200Response {\n\tthis := GetVersion200Response{}\n\tthis.Version = version\n\treturn &this\n}\n\n// NewGetVersion200ResponseWithDefaults instantiates a new GetVersion200Response object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewGetVersion200ResponseWithDefaults() *GetVersion200Response {\n\tthis := GetVersion200Response{}\n\treturn &this\n}\n\n// GetVersion returns the Version field value\nfunc (o *GetVersion200Response) GetVersion() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Version\n}\n\n// GetVersionOk returns a tuple with the Version field value\n// and a boolean to check if the value has been set.\nfunc (o *GetVersion200Response) GetVersionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Version, true\n}\n\n// SetVersion sets field value\nfunc (o *GetVersion200Response) SetVersion(v string) {\n\to.Version = v\n}\n\nfunc (o GetVersion200Response) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o GetVersion200Response) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"version\"] = o.Version\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *GetVersion200Response) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"version\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarGetVersion200Response := _GetVersion200Response{}\n\n\terr = json.Unmarshal(data, &varGetVersion200Response)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = GetVersion200Response(varGetVersion200Response)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"version\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableGetVersion200Response struct {\n\tvalue *GetVersion200Response\n\tisSet bool\n}\n\nfunc (v NullableGetVersion200Response) Get() *GetVersion200Response {\n\treturn v.value\n}\n\nfunc (v *NullableGetVersion200Response) Set(val *GetVersion200Response) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableGetVersion200Response) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableGetVersion200Response) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableGetVersion200Response(val *GetVersion200Response) *NullableGetVersion200Response {\n\treturn &NullableGetVersion200Response{value: val, isSet: true}\n}\n\nfunc (v NullableGetVersion200Response) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableGetVersion200Response) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_health_not_ready_status.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the HealthNotReadyStatus type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &HealthNotReadyStatus{}\n\n// HealthNotReadyStatus struct for HealthNotReadyStatus\ntype HealthNotReadyStatus struct {\n\t// Errors contains a list of errors that caused the not ready status.\n\tErrors               *map[string]string `json:\"errors,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _HealthNotReadyStatus HealthNotReadyStatus\n\n// NewHealthNotReadyStatus instantiates a new HealthNotReadyStatus object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewHealthNotReadyStatus() *HealthNotReadyStatus {\n\tthis := HealthNotReadyStatus{}\n\treturn &this\n}\n\n// NewHealthNotReadyStatusWithDefaults instantiates a new HealthNotReadyStatus object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewHealthNotReadyStatusWithDefaults() *HealthNotReadyStatus {\n\tthis := HealthNotReadyStatus{}\n\treturn &this\n}\n\n// GetErrors returns the Errors field value if set, zero value otherwise.\nfunc (o *HealthNotReadyStatus) GetErrors() map[string]string {\n\tif o == nil || IsNil(o.Errors) {\n\t\tvar ret map[string]string\n\t\treturn ret\n\t}\n\treturn *o.Errors\n}\n\n// GetErrorsOk returns a tuple with the Errors field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *HealthNotReadyStatus) GetErrorsOk() (*map[string]string, bool) {\n\tif o == nil || IsNil(o.Errors) {\n\t\treturn nil, false\n\t}\n\treturn o.Errors, true\n}\n\n// HasErrors returns a boolean if a field has been set.\nfunc (o *HealthNotReadyStatus) HasErrors() bool {\n\tif o != nil && !IsNil(o.Errors) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetErrors gets a reference to the given map[string]string and assigns it to the Errors field.\nfunc (o *HealthNotReadyStatus) SetErrors(v map[string]string) {\n\to.Errors = &v\n}\n\nfunc (o HealthNotReadyStatus) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o HealthNotReadyStatus) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Errors) {\n\t\ttoSerialize[\"errors\"] = o.Errors\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *HealthNotReadyStatus) UnmarshalJSON(data []byte) (err error) {\n\tvarHealthNotReadyStatus := _HealthNotReadyStatus{}\n\n\terr = json.Unmarshal(data, &varHealthNotReadyStatus)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = HealthNotReadyStatus(varHealthNotReadyStatus)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"errors\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableHealthNotReadyStatus struct {\n\tvalue *HealthNotReadyStatus\n\tisSet bool\n}\n\nfunc (v NullableHealthNotReadyStatus) Get() *HealthNotReadyStatus {\n\treturn v.value\n}\n\nfunc (v *NullableHealthNotReadyStatus) Set(val *HealthNotReadyStatus) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableHealthNotReadyStatus) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableHealthNotReadyStatus) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableHealthNotReadyStatus(val *HealthNotReadyStatus) *NullableHealthNotReadyStatus {\n\treturn &NullableHealthNotReadyStatus{value: val, isSet: true}\n}\n\nfunc (v NullableHealthNotReadyStatus) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableHealthNotReadyStatus) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_health_status.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the HealthStatus type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &HealthStatus{}\n\n// HealthStatus struct for HealthStatus\ntype HealthStatus struct {\n\t// Status always contains \\\"ok\\\".\n\tStatus               *string `json:\"status,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _HealthStatus HealthStatus\n\n// NewHealthStatus instantiates a new HealthStatus object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewHealthStatus() *HealthStatus {\n\tthis := HealthStatus{}\n\treturn &this\n}\n\n// NewHealthStatusWithDefaults instantiates a new HealthStatus object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewHealthStatusWithDefaults() *HealthStatus {\n\tthis := HealthStatus{}\n\treturn &this\n}\n\n// GetStatus returns the Status field value if set, zero value otherwise.\nfunc (o *HealthStatus) GetStatus() string {\n\tif o == nil || IsNil(o.Status) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *HealthStatus) GetStatusOk() (*string, bool) {\n\tif o == nil || IsNil(o.Status) {\n\t\treturn nil, false\n\t}\n\treturn o.Status, true\n}\n\n// HasStatus returns a boolean if a field has been set.\nfunc (o *HealthStatus) HasStatus() bool {\n\tif o != nil && !IsNil(o.Status) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetStatus gets a reference to the given string and assigns it to the Status field.\nfunc (o *HealthStatus) SetStatus(v string) {\n\to.Status = &v\n}\n\nfunc (o HealthStatus) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o HealthStatus) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Status) {\n\t\ttoSerialize[\"status\"] = o.Status\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *HealthStatus) UnmarshalJSON(data []byte) (err error) {\n\tvarHealthStatus := _HealthStatus{}\n\n\terr = json.Unmarshal(data, &varHealthStatus)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = HealthStatus(varHealthStatus)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"status\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableHealthStatus struct {\n\tvalue *HealthStatus\n\tisSet bool\n}\n\nfunc (v NullableHealthStatus) Get() *HealthStatus {\n\treturn v.value\n}\n\nfunc (v *NullableHealthStatus) Set(val *HealthStatus) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableHealthStatus) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableHealthStatus) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableHealthStatus(val *HealthStatus) *NullableHealthStatus {\n\treturn &NullableHealthStatus{value: val, isSet: true}\n}\n\nfunc (v NullableHealthStatus) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableHealthStatus) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the Identity type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Identity{}\n\n// Identity An [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) represents a (human) user in Ory.\ntype Identity struct {\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// Credentials represents all credentials that can be used for authenticating this identity.\n\tCredentials *map[string]IdentityCredentials `json:\"credentials,omitempty\"`\n\t// ExternalID is an optional external ID of the identity. This is used to link the identity to an external system. If set, the external ID must be unique across all identities.\n\tExternalId *string `json:\"external_id,omitempty\"`\n\t// ID is the identity's unique identifier.  The Identity ID can not be changed and can not be chosen. This ensures future compatibility and optimization for distributed stores such as CockroachDB.\n\tId string `json:\"id\"`\n\t// NullJSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger and is NULLable-\n\tMetadataAdmin interface{} `json:\"metadata_admin,omitempty\"`\n\t// NullJSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger and is NULLable-\n\tMetadataPublic interface{}    `json:\"metadata_public,omitempty\"`\n\tOrganizationId NullableString `json:\"organization_id,omitempty\"`\n\t// RecoveryAddresses contains all the addresses that can be used to recover an identity.\n\tRecoveryAddresses []RecoveryIdentityAddress `json:\"recovery_addresses,omitempty\"`\n\t// SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\n\tSchemaId string `json:\"schema_id\"`\n\t// SchemaURL is the URL of the endpoint where the identity's traits schema can be fetched from.  format: url\n\tSchemaUrl string `json:\"schema_url\"`\n\t// State is the identity's state.  This value has currently no effect. active StateActive inactive StateInactive\n\tState          *string    `json:\"state,omitempty\"`\n\tStateChangedAt *time.Time `json:\"state_changed_at,omitempty\"`\n\t// Traits represent an identity's traits. The identity is able to create, modify, and delete traits in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_url`.\n\tTraits interface{} `json:\"traits\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\"`\n\t// VerifiableAddresses contains all the addresses that can be verified by the user.\n\tVerifiableAddresses  []VerifiableIdentityAddress `json:\"verifiable_addresses,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Identity Identity\n\n// NewIdentity instantiates a new Identity object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentity(id string, schemaId string, schemaUrl string, traits interface{}) *Identity {\n\tthis := Identity{}\n\tthis.Id = id\n\tthis.SchemaId = schemaId\n\tthis.SchemaUrl = schemaUrl\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewIdentityWithDefaults instantiates a new Identity object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithDefaults() *Identity {\n\tthis := Identity{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *Identity) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *Identity) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *Identity) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetCredentials returns the Credentials field value if set, zero value otherwise.\nfunc (o *Identity) GetCredentials() map[string]IdentityCredentials {\n\tif o == nil || IsNil(o.Credentials) {\n\t\tvar ret map[string]IdentityCredentials\n\t\treturn ret\n\t}\n\treturn *o.Credentials\n}\n\n// GetCredentialsOk returns a tuple with the Credentials field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetCredentialsOk() (*map[string]IdentityCredentials, bool) {\n\tif o == nil || IsNil(o.Credentials) {\n\t\treturn nil, false\n\t}\n\treturn o.Credentials, true\n}\n\n// HasCredentials returns a boolean if a field has been set.\nfunc (o *Identity) HasCredentials() bool {\n\tif o != nil && !IsNil(o.Credentials) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCredentials gets a reference to the given map[string]IdentityCredentials and assigns it to the Credentials field.\nfunc (o *Identity) SetCredentials(v map[string]IdentityCredentials) {\n\to.Credentials = &v\n}\n\n// GetExternalId returns the ExternalId field value if set, zero value otherwise.\nfunc (o *Identity) GetExternalId() string {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExternalId\n}\n\n// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetExternalIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\treturn nil, false\n\t}\n\treturn o.ExternalId, true\n}\n\n// HasExternalId returns a boolean if a field has been set.\nfunc (o *Identity) HasExternalId() bool {\n\tif o != nil && !IsNil(o.ExternalId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.\nfunc (o *Identity) SetExternalId(v string) {\n\to.ExternalId = &v\n}\n\n// GetId returns the Id field value\nfunc (o *Identity) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *Identity) SetId(v string) {\n\to.Id = v\n}\n\n// GetMetadataAdmin returns the MetadataAdmin field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *Identity) GetMetadataAdmin() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataAdmin\n}\n\n// GetMetadataAdminOk returns a tuple with the MetadataAdmin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *Identity) GetMetadataAdminOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataAdmin) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataAdmin, true\n}\n\n// HasMetadataAdmin returns a boolean if a field has been set.\nfunc (o *Identity) HasMetadataAdmin() bool {\n\tif o != nil && !IsNil(o.MetadataAdmin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataAdmin gets a reference to the given interface{} and assigns it to the MetadataAdmin field.\nfunc (o *Identity) SetMetadataAdmin(v interface{}) {\n\to.MetadataAdmin = v\n}\n\n// GetMetadataPublic returns the MetadataPublic field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *Identity) GetMetadataPublic() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataPublic\n}\n\n// GetMetadataPublicOk returns a tuple with the MetadataPublic field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *Identity) GetMetadataPublicOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataPublic) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataPublic, true\n}\n\n// HasMetadataPublic returns a boolean if a field has been set.\nfunc (o *Identity) HasMetadataPublic() bool {\n\tif o != nil && !IsNil(o.MetadataPublic) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataPublic gets a reference to the given interface{} and assigns it to the MetadataPublic field.\nfunc (o *Identity) SetMetadataPublic(v interface{}) {\n\to.MetadataPublic = v\n}\n\n// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *Identity) GetOrganizationId() string {\n\tif o == nil || IsNil(o.OrganizationId.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OrganizationId.Get()\n}\n\n// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *Identity) GetOrganizationIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.OrganizationId.Get(), o.OrganizationId.IsSet()\n}\n\n// HasOrganizationId returns a boolean if a field has been set.\nfunc (o *Identity) HasOrganizationId() bool {\n\tif o != nil && o.OrganizationId.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganizationId gets a reference to the given NullableString and assigns it to the OrganizationId field.\nfunc (o *Identity) SetOrganizationId(v string) {\n\to.OrganizationId.Set(&v)\n}\n\n// SetOrganizationIdNil sets the value for OrganizationId to be an explicit nil\nfunc (o *Identity) SetOrganizationIdNil() {\n\to.OrganizationId.Set(nil)\n}\n\n// UnsetOrganizationId ensures that no value is present for OrganizationId, not even an explicit nil\nfunc (o *Identity) UnsetOrganizationId() {\n\to.OrganizationId.Unset()\n}\n\n// GetRecoveryAddresses returns the RecoveryAddresses field value if set, zero value otherwise.\nfunc (o *Identity) GetRecoveryAddresses() []RecoveryIdentityAddress {\n\tif o == nil || IsNil(o.RecoveryAddresses) {\n\t\tvar ret []RecoveryIdentityAddress\n\t\treturn ret\n\t}\n\treturn o.RecoveryAddresses\n}\n\n// GetRecoveryAddressesOk returns a tuple with the RecoveryAddresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetRecoveryAddressesOk() ([]RecoveryIdentityAddress, bool) {\n\tif o == nil || IsNil(o.RecoveryAddresses) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoveryAddresses, true\n}\n\n// HasRecoveryAddresses returns a boolean if a field has been set.\nfunc (o *Identity) HasRecoveryAddresses() bool {\n\tif o != nil && !IsNil(o.RecoveryAddresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoveryAddresses gets a reference to the given []RecoveryIdentityAddress and assigns it to the RecoveryAddresses field.\nfunc (o *Identity) SetRecoveryAddresses(v []RecoveryIdentityAddress) {\n\to.RecoveryAddresses = v\n}\n\n// GetSchemaId returns the SchemaId field value\nfunc (o *Identity) GetSchemaId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SchemaId\n}\n\n// GetSchemaIdOk returns a tuple with the SchemaId field value\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetSchemaIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SchemaId, true\n}\n\n// SetSchemaId sets field value\nfunc (o *Identity) SetSchemaId(v string) {\n\to.SchemaId = v\n}\n\n// GetSchemaUrl returns the SchemaUrl field value\nfunc (o *Identity) GetSchemaUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SchemaUrl\n}\n\n// GetSchemaUrlOk returns a tuple with the SchemaUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetSchemaUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SchemaUrl, true\n}\n\n// SetSchemaUrl sets field value\nfunc (o *Identity) SetSchemaUrl(v string) {\n\to.SchemaUrl = v\n}\n\n// GetState returns the State field value if set, zero value otherwise.\nfunc (o *Identity) GetState() string {\n\tif o == nil || IsNil(o.State) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.State\n}\n\n// GetStateOk returns a tuple with the State field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetStateOk() (*string, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn o.State, true\n}\n\n// HasState returns a boolean if a field has been set.\nfunc (o *Identity) HasState() bool {\n\tif o != nil && !IsNil(o.State) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetState gets a reference to the given string and assigns it to the State field.\nfunc (o *Identity) SetState(v string) {\n\to.State = &v\n}\n\n// GetStateChangedAt returns the StateChangedAt field value if set, zero value otherwise.\nfunc (o *Identity) GetStateChangedAt() time.Time {\n\tif o == nil || IsNil(o.StateChangedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.StateChangedAt\n}\n\n// GetStateChangedAtOk returns a tuple with the StateChangedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetStateChangedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.StateChangedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.StateChangedAt, true\n}\n\n// HasStateChangedAt returns a boolean if a field has been set.\nfunc (o *Identity) HasStateChangedAt() bool {\n\tif o != nil && !IsNil(o.StateChangedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetStateChangedAt gets a reference to the given time.Time and assigns it to the StateChangedAt field.\nfunc (o *Identity) SetStateChangedAt(v time.Time) {\n\to.StateChangedAt = &v\n}\n\n// GetTraits returns the Traits field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *Identity) GetTraits() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *Identity) GetTraitsOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn nil, false\n\t}\n\treturn &o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *Identity) SetTraits(v interface{}) {\n\to.Traits = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *Identity) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *Identity) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *Identity) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetVerifiableAddresses returns the VerifiableAddresses field value if set, zero value otherwise.\nfunc (o *Identity) GetVerifiableAddresses() []VerifiableIdentityAddress {\n\tif o == nil || IsNil(o.VerifiableAddresses) {\n\t\tvar ret []VerifiableIdentityAddress\n\t\treturn ret\n\t}\n\treturn o.VerifiableAddresses\n}\n\n// GetVerifiableAddressesOk returns a tuple with the VerifiableAddresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Identity) GetVerifiableAddressesOk() ([]VerifiableIdentityAddress, bool) {\n\tif o == nil || IsNil(o.VerifiableAddresses) {\n\t\treturn nil, false\n\t}\n\treturn o.VerifiableAddresses, true\n}\n\n// HasVerifiableAddresses returns a boolean if a field has been set.\nfunc (o *Identity) HasVerifiableAddresses() bool {\n\tif o != nil && !IsNil(o.VerifiableAddresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVerifiableAddresses gets a reference to the given []VerifiableIdentityAddress and assigns it to the VerifiableAddresses field.\nfunc (o *Identity) SetVerifiableAddresses(v []VerifiableIdentityAddress) {\n\to.VerifiableAddresses = v\n}\n\nfunc (o Identity) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Identity) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Credentials) {\n\t\ttoSerialize[\"credentials\"] = o.Credentials\n\t}\n\tif !IsNil(o.ExternalId) {\n\t\ttoSerialize[\"external_id\"] = o.ExternalId\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\tif o.MetadataAdmin != nil {\n\t\ttoSerialize[\"metadata_admin\"] = o.MetadataAdmin\n\t}\n\tif o.MetadataPublic != nil {\n\t\ttoSerialize[\"metadata_public\"] = o.MetadataPublic\n\t}\n\tif o.OrganizationId.IsSet() {\n\t\ttoSerialize[\"organization_id\"] = o.OrganizationId.Get()\n\t}\n\tif !IsNil(o.RecoveryAddresses) {\n\t\ttoSerialize[\"recovery_addresses\"] = o.RecoveryAddresses\n\t}\n\ttoSerialize[\"schema_id\"] = o.SchemaId\n\ttoSerialize[\"schema_url\"] = o.SchemaUrl\n\tif !IsNil(o.State) {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.StateChangedAt) {\n\t\ttoSerialize[\"state_changed_at\"] = o.StateChangedAt\n\t}\n\tif o.Traits != nil {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\tif !IsNil(o.VerifiableAddresses) {\n\t\ttoSerialize[\"verifiable_addresses\"] = o.VerifiableAddresses\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Identity) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"schema_id\",\n\t\t\"schema_url\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIdentity := _Identity{}\n\n\terr = json.Unmarshal(data, &varIdentity)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Identity(varIdentity)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"credentials\")\n\t\tdelete(additionalProperties, \"external_id\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"metadata_admin\")\n\t\tdelete(additionalProperties, \"metadata_public\")\n\t\tdelete(additionalProperties, \"organization_id\")\n\t\tdelete(additionalProperties, \"recovery_addresses\")\n\t\tdelete(additionalProperties, \"schema_id\")\n\t\tdelete(additionalProperties, \"schema_url\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"state_changed_at\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"verifiable_addresses\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentity struct {\n\tvalue *Identity\n\tisSet bool\n}\n\nfunc (v NullableIdentity) Get() *Identity {\n\treturn v.value\n}\n\nfunc (v *NullableIdentity) Set(val *Identity) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentity) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentity) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentity(val *Identity) *NullableIdentity {\n\treturn &NullableIdentity{value: val, isSet: true}\n}\n\nfunc (v NullableIdentity) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentity) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_credentials.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n)\n\n// checks if the IdentityCredentials type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentials{}\n\n// IdentityCredentials Credentials represents a specific credential type\ntype IdentityCredentials struct {\n\tConfig map[string]interface{} `json:\"config,omitempty\"`\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// Identifiers represent a list of unique identifiers this credential type matches.\n\tIdentifiers []string `json:\"identifiers,omitempty\"`\n\t// Type discriminates between different types of credentials. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\tType *string `json:\"type,omitempty\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\"`\n\t// Version refers to the version of the credential. Useful when changing the config schema.\n\tVersion              *int64 `json:\"version,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentials IdentityCredentials\n\n// NewIdentityCredentials instantiates a new IdentityCredentials object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentials() *IdentityCredentials {\n\tthis := IdentityCredentials{}\n\treturn &this\n}\n\n// NewIdentityCredentialsWithDefaults instantiates a new IdentityCredentials object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsWithDefaults() *IdentityCredentials {\n\tthis := IdentityCredentials{}\n\treturn &this\n}\n\n// GetConfig returns the Config field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetConfig() map[string]interface{} {\n\tif o == nil || IsNil(o.Config) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Config\n}\n\n// GetConfigOk returns a tuple with the Config field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetConfigOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Config) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Config, true\n}\n\n// HasConfig returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasConfig() bool {\n\tif o != nil && !IsNil(o.Config) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfig gets a reference to the given map[string]interface{} and assigns it to the Config field.\nfunc (o *IdentityCredentials) SetConfig(v map[string]interface{}) {\n\to.Config = v\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *IdentityCredentials) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetIdentifiers returns the Identifiers field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetIdentifiers() []string {\n\tif o == nil || IsNil(o.Identifiers) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.Identifiers\n}\n\n// GetIdentifiersOk returns a tuple with the Identifiers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetIdentifiersOk() ([]string, bool) {\n\tif o == nil || IsNil(o.Identifiers) {\n\t\treturn nil, false\n\t}\n\treturn o.Identifiers, true\n}\n\n// HasIdentifiers returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasIdentifiers() bool {\n\tif o != nil && !IsNil(o.Identifiers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentifiers gets a reference to the given []string and assigns it to the Identifiers field.\nfunc (o *IdentityCredentials) SetIdentifiers(v []string) {\n\to.Identifiers = v\n}\n\n// GetType returns the Type field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetType() string {\n\tif o == nil || IsNil(o.Type) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetTypeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Type) {\n\t\treturn nil, false\n\t}\n\treturn o.Type, true\n}\n\n// HasType returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasType() bool {\n\tif o != nil && !IsNil(o.Type) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetType gets a reference to the given string and assigns it to the Type field.\nfunc (o *IdentityCredentials) SetType(v string) {\n\to.Type = &v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *IdentityCredentials) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetVersion returns the Version field value if set, zero value otherwise.\nfunc (o *IdentityCredentials) GetVersion() int64 {\n\tif o == nil || IsNil(o.Version) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Version\n}\n\n// GetVersionOk returns a tuple with the Version field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentials) GetVersionOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Version) {\n\t\treturn nil, false\n\t}\n\treturn o.Version, true\n}\n\n// HasVersion returns a boolean if a field has been set.\nfunc (o *IdentityCredentials) HasVersion() bool {\n\tif o != nil && !IsNil(o.Version) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVersion gets a reference to the given int64 and assigns it to the Version field.\nfunc (o *IdentityCredentials) SetVersion(v int64) {\n\to.Version = &v\n}\n\nfunc (o IdentityCredentials) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentials) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Config) {\n\t\ttoSerialize[\"config\"] = o.Config\n\t}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Identifiers) {\n\t\ttoSerialize[\"identifiers\"] = o.Identifiers\n\t}\n\tif !IsNil(o.Type) {\n\t\ttoSerialize[\"type\"] = o.Type\n\t}\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\tif !IsNil(o.Version) {\n\t\ttoSerialize[\"version\"] = o.Version\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentials) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentials := _IdentityCredentials{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentials)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentials(varIdentityCredentials)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"config\")\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"identifiers\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"version\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentials struct {\n\tvalue *IdentityCredentials\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentials) Get() *IdentityCredentials {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentials) Set(val *IdentityCredentials) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentials) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentials) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentials(val *IdentityCredentials) *NullableIdentityCredentials {\n\treturn &NullableIdentityCredentials{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentials) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentials) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_credentials_code.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsCode type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsCode{}\n\n// IdentityCredentialsCode CredentialsCode represents a one time login/registration code\ntype IdentityCredentialsCode struct {\n\tAddresses            []IdentityCredentialsCodeAddress `json:\"addresses,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentialsCode IdentityCredentialsCode\n\n// NewIdentityCredentialsCode instantiates a new IdentityCredentialsCode object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsCode() *IdentityCredentialsCode {\n\tthis := IdentityCredentialsCode{}\n\treturn &this\n}\n\n// NewIdentityCredentialsCodeWithDefaults instantiates a new IdentityCredentialsCode object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsCodeWithDefaults() *IdentityCredentialsCode {\n\tthis := IdentityCredentialsCode{}\n\treturn &this\n}\n\n// GetAddresses returns the Addresses field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsCode) GetAddresses() []IdentityCredentialsCodeAddress {\n\tif o == nil || IsNil(o.Addresses) {\n\t\tvar ret []IdentityCredentialsCodeAddress\n\t\treturn ret\n\t}\n\treturn o.Addresses\n}\n\n// GetAddressesOk returns a tuple with the Addresses field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsCode) GetAddressesOk() ([]IdentityCredentialsCodeAddress, bool) {\n\tif o == nil || IsNil(o.Addresses) {\n\t\treturn nil, false\n\t}\n\treturn o.Addresses, true\n}\n\n// HasAddresses returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsCode) HasAddresses() bool {\n\tif o != nil && !IsNil(o.Addresses) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAddresses gets a reference to the given []IdentityCredentialsCodeAddress and assigns it to the Addresses field.\nfunc (o *IdentityCredentialsCode) SetAddresses(v []IdentityCredentialsCodeAddress) {\n\to.Addresses = v\n}\n\nfunc (o IdentityCredentialsCode) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsCode) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Addresses) {\n\t\ttoSerialize[\"addresses\"] = o.Addresses\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsCode) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsCode := _IdentityCredentialsCode{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsCode)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsCode(varIdentityCredentialsCode)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"addresses\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsCode struct {\n\tvalue *IdentityCredentialsCode\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsCode) Get() *IdentityCredentialsCode {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsCode) Set(val *IdentityCredentialsCode) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsCode) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsCode) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsCode(val *IdentityCredentialsCode) *NullableIdentityCredentialsCode {\n\treturn &NullableIdentityCredentialsCode{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsCode) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsCode) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_credentials_code_address.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsCodeAddress type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsCodeAddress{}\n\n// IdentityCredentialsCodeAddress struct for IdentityCredentialsCodeAddress\ntype IdentityCredentialsCodeAddress struct {\n\t// The address for this code\n\tAddress              *string `json:\"address,omitempty\"`\n\tChannel              *string `json:\"channel,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentialsCodeAddress IdentityCredentialsCodeAddress\n\n// NewIdentityCredentialsCodeAddress instantiates a new IdentityCredentialsCodeAddress object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsCodeAddress() *IdentityCredentialsCodeAddress {\n\tthis := IdentityCredentialsCodeAddress{}\n\treturn &this\n}\n\n// NewIdentityCredentialsCodeAddressWithDefaults instantiates a new IdentityCredentialsCodeAddress object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsCodeAddressWithDefaults() *IdentityCredentialsCodeAddress {\n\tthis := IdentityCredentialsCodeAddress{}\n\treturn &this\n}\n\n// GetAddress returns the Address field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsCodeAddress) GetAddress() string {\n\tif o == nil || IsNil(o.Address) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Address\n}\n\n// GetAddressOk returns a tuple with the Address field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsCodeAddress) GetAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.Address) {\n\t\treturn nil, false\n\t}\n\treturn o.Address, true\n}\n\n// HasAddress returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsCodeAddress) HasAddress() bool {\n\tif o != nil && !IsNil(o.Address) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAddress gets a reference to the given string and assigns it to the Address field.\nfunc (o *IdentityCredentialsCodeAddress) SetAddress(v string) {\n\to.Address = &v\n}\n\n// GetChannel returns the Channel field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsCodeAddress) GetChannel() string {\n\tif o == nil || IsNil(o.Channel) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Channel\n}\n\n// GetChannelOk returns a tuple with the Channel field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsCodeAddress) GetChannelOk() (*string, bool) {\n\tif o == nil || IsNil(o.Channel) {\n\t\treturn nil, false\n\t}\n\treturn o.Channel, true\n}\n\n// HasChannel returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsCodeAddress) HasChannel() bool {\n\tif o != nil && !IsNil(o.Channel) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetChannel gets a reference to the given string and assigns it to the Channel field.\nfunc (o *IdentityCredentialsCodeAddress) SetChannel(v string) {\n\to.Channel = &v\n}\n\nfunc (o IdentityCredentialsCodeAddress) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsCodeAddress) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Address) {\n\t\ttoSerialize[\"address\"] = o.Address\n\t}\n\tif !IsNil(o.Channel) {\n\t\ttoSerialize[\"channel\"] = o.Channel\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsCodeAddress) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsCodeAddress := _IdentityCredentialsCodeAddress{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsCodeAddress)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsCodeAddress(varIdentityCredentialsCodeAddress)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"address\")\n\t\tdelete(additionalProperties, \"channel\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsCodeAddress struct {\n\tvalue *IdentityCredentialsCodeAddress\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsCodeAddress) Get() *IdentityCredentialsCodeAddress {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsCodeAddress) Set(val *IdentityCredentialsCodeAddress) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsCodeAddress) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsCodeAddress) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsCodeAddress(val *IdentityCredentialsCodeAddress) *NullableIdentityCredentialsCodeAddress {\n\treturn &NullableIdentityCredentialsCodeAddress{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsCodeAddress) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsCodeAddress) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_credentials_oidc.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsOidc type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsOidc{}\n\n// IdentityCredentialsOidc struct for IdentityCredentialsOidc\ntype IdentityCredentialsOidc struct {\n\tProviders            []IdentityCredentialsOidcProvider `json:\"providers,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentialsOidc IdentityCredentialsOidc\n\n// NewIdentityCredentialsOidc instantiates a new IdentityCredentialsOidc object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsOidc() *IdentityCredentialsOidc {\n\tthis := IdentityCredentialsOidc{}\n\treturn &this\n}\n\n// NewIdentityCredentialsOidcWithDefaults instantiates a new IdentityCredentialsOidc object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsOidcWithDefaults() *IdentityCredentialsOidc {\n\tthis := IdentityCredentialsOidc{}\n\treturn &this\n}\n\n// GetProviders returns the Providers field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidc) GetProviders() []IdentityCredentialsOidcProvider {\n\tif o == nil || IsNil(o.Providers) {\n\t\tvar ret []IdentityCredentialsOidcProvider\n\t\treturn ret\n\t}\n\treturn o.Providers\n}\n\n// GetProvidersOk returns a tuple with the Providers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidc) GetProvidersOk() ([]IdentityCredentialsOidcProvider, bool) {\n\tif o == nil || IsNil(o.Providers) {\n\t\treturn nil, false\n\t}\n\treturn o.Providers, true\n}\n\n// HasProviders returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidc) HasProviders() bool {\n\tif o != nil && !IsNil(o.Providers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProviders gets a reference to the given []IdentityCredentialsOidcProvider and assigns it to the Providers field.\nfunc (o *IdentityCredentialsOidc) SetProviders(v []IdentityCredentialsOidcProvider) {\n\to.Providers = v\n}\n\nfunc (o IdentityCredentialsOidc) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsOidc) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Providers) {\n\t\ttoSerialize[\"providers\"] = o.Providers\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsOidc) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsOidc := _IdentityCredentialsOidc{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsOidc)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsOidc(varIdentityCredentialsOidc)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"providers\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsOidc struct {\n\tvalue *IdentityCredentialsOidc\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsOidc) Get() *IdentityCredentialsOidc {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsOidc) Set(val *IdentityCredentialsOidc) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsOidc) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsOidc) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsOidc(val *IdentityCredentialsOidc) *NullableIdentityCredentialsOidc {\n\treturn &NullableIdentityCredentialsOidc{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsOidc) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsOidc) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_credentials_oidc_provider.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsOidcProvider type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsOidcProvider{}\n\n// IdentityCredentialsOidcProvider struct for IdentityCredentialsOidcProvider\ntype IdentityCredentialsOidcProvider struct {\n\tInitialAccessToken   *string `json:\"initial_access_token,omitempty\"`\n\tInitialIdToken       *string `json:\"initial_id_token,omitempty\"`\n\tInitialRefreshToken  *string `json:\"initial_refresh_token,omitempty\"`\n\tOrganization         *string `json:\"organization,omitempty\"`\n\tProvider             *string `json:\"provider,omitempty\"`\n\tSubject              *string `json:\"subject,omitempty\"`\n\tUseAutoLink          *bool   `json:\"use_auto_link,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityCredentialsOidcProvider IdentityCredentialsOidcProvider\n\n// NewIdentityCredentialsOidcProvider instantiates a new IdentityCredentialsOidcProvider object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsOidcProvider() *IdentityCredentialsOidcProvider {\n\tthis := IdentityCredentialsOidcProvider{}\n\treturn &this\n}\n\n// NewIdentityCredentialsOidcProviderWithDefaults instantiates a new IdentityCredentialsOidcProvider object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsOidcProviderWithDefaults() *IdentityCredentialsOidcProvider {\n\tthis := IdentityCredentialsOidcProvider{}\n\treturn &this\n}\n\n// GetInitialAccessToken returns the InitialAccessToken field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialAccessToken() string {\n\tif o == nil || IsNil(o.InitialAccessToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.InitialAccessToken\n}\n\n// GetInitialAccessTokenOk returns a tuple with the InitialAccessToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialAccessTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.InitialAccessToken) {\n\t\treturn nil, false\n\t}\n\treturn o.InitialAccessToken, true\n}\n\n// HasInitialAccessToken returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasInitialAccessToken() bool {\n\tif o != nil && !IsNil(o.InitialAccessToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetInitialAccessToken gets a reference to the given string and assigns it to the InitialAccessToken field.\nfunc (o *IdentityCredentialsOidcProvider) SetInitialAccessToken(v string) {\n\to.InitialAccessToken = &v\n}\n\n// GetInitialIdToken returns the InitialIdToken field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialIdToken() string {\n\tif o == nil || IsNil(o.InitialIdToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.InitialIdToken\n}\n\n// GetInitialIdTokenOk returns a tuple with the InitialIdToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialIdTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.InitialIdToken) {\n\t\treturn nil, false\n\t}\n\treturn o.InitialIdToken, true\n}\n\n// HasInitialIdToken returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasInitialIdToken() bool {\n\tif o != nil && !IsNil(o.InitialIdToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetInitialIdToken gets a reference to the given string and assigns it to the InitialIdToken field.\nfunc (o *IdentityCredentialsOidcProvider) SetInitialIdToken(v string) {\n\to.InitialIdToken = &v\n}\n\n// GetInitialRefreshToken returns the InitialRefreshToken field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialRefreshToken() string {\n\tif o == nil || IsNil(o.InitialRefreshToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.InitialRefreshToken\n}\n\n// GetInitialRefreshTokenOk returns a tuple with the InitialRefreshToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetInitialRefreshTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.InitialRefreshToken) {\n\t\treturn nil, false\n\t}\n\treturn o.InitialRefreshToken, true\n}\n\n// HasInitialRefreshToken returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasInitialRefreshToken() bool {\n\tif o != nil && !IsNil(o.InitialRefreshToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetInitialRefreshToken gets a reference to the given string and assigns it to the InitialRefreshToken field.\nfunc (o *IdentityCredentialsOidcProvider) SetInitialRefreshToken(v string) {\n\to.InitialRefreshToken = &v\n}\n\n// GetOrganization returns the Organization field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetOrganization() string {\n\tif o == nil || IsNil(o.Organization) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Organization\n}\n\n// GetOrganizationOk returns a tuple with the Organization field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetOrganizationOk() (*string, bool) {\n\tif o == nil || IsNil(o.Organization) {\n\t\treturn nil, false\n\t}\n\treturn o.Organization, true\n}\n\n// HasOrganization returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasOrganization() bool {\n\tif o != nil && !IsNil(o.Organization) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganization gets a reference to the given string and assigns it to the Organization field.\nfunc (o *IdentityCredentialsOidcProvider) SetOrganization(v string) {\n\to.Organization = &v\n}\n\n// GetProvider returns the Provider field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetProvider() string {\n\tif o == nil || IsNil(o.Provider) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetProviderOk() (*string, bool) {\n\tif o == nil || IsNil(o.Provider) {\n\t\treturn nil, false\n\t}\n\treturn o.Provider, true\n}\n\n// HasProvider returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasProvider() bool {\n\tif o != nil && !IsNil(o.Provider) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProvider gets a reference to the given string and assigns it to the Provider field.\nfunc (o *IdentityCredentialsOidcProvider) SetProvider(v string) {\n\to.Provider = &v\n}\n\n// GetSubject returns the Subject field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetSubject() string {\n\tif o == nil || IsNil(o.Subject) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetSubjectOk() (*string, bool) {\n\tif o == nil || IsNil(o.Subject) {\n\t\treturn nil, false\n\t}\n\treturn o.Subject, true\n}\n\n// HasSubject returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasSubject() bool {\n\tif o != nil && !IsNil(o.Subject) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSubject gets a reference to the given string and assigns it to the Subject field.\nfunc (o *IdentityCredentialsOidcProvider) SetSubject(v string) {\n\to.Subject = &v\n}\n\n// GetUseAutoLink returns the UseAutoLink field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsOidcProvider) GetUseAutoLink() bool {\n\tif o == nil || IsNil(o.UseAutoLink) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.UseAutoLink\n}\n\n// GetUseAutoLinkOk returns a tuple with the UseAutoLink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsOidcProvider) GetUseAutoLinkOk() (*bool, bool) {\n\tif o == nil || IsNil(o.UseAutoLink) {\n\t\treturn nil, false\n\t}\n\treturn o.UseAutoLink, true\n}\n\n// HasUseAutoLink returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsOidcProvider) HasUseAutoLink() bool {\n\tif o != nil && !IsNil(o.UseAutoLink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUseAutoLink gets a reference to the given bool and assigns it to the UseAutoLink field.\nfunc (o *IdentityCredentialsOidcProvider) SetUseAutoLink(v bool) {\n\to.UseAutoLink = &v\n}\n\nfunc (o IdentityCredentialsOidcProvider) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsOidcProvider) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.InitialAccessToken) {\n\t\ttoSerialize[\"initial_access_token\"] = o.InitialAccessToken\n\t}\n\tif !IsNil(o.InitialIdToken) {\n\t\ttoSerialize[\"initial_id_token\"] = o.InitialIdToken\n\t}\n\tif !IsNil(o.InitialRefreshToken) {\n\t\ttoSerialize[\"initial_refresh_token\"] = o.InitialRefreshToken\n\t}\n\tif !IsNil(o.Organization) {\n\t\ttoSerialize[\"organization\"] = o.Organization\n\t}\n\tif !IsNil(o.Provider) {\n\t\ttoSerialize[\"provider\"] = o.Provider\n\t}\n\tif !IsNil(o.Subject) {\n\t\ttoSerialize[\"subject\"] = o.Subject\n\t}\n\tif !IsNil(o.UseAutoLink) {\n\t\ttoSerialize[\"use_auto_link\"] = o.UseAutoLink\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsOidcProvider) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsOidcProvider := _IdentityCredentialsOidcProvider{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsOidcProvider)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsOidcProvider(varIdentityCredentialsOidcProvider)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"initial_access_token\")\n\t\tdelete(additionalProperties, \"initial_id_token\")\n\t\tdelete(additionalProperties, \"initial_refresh_token\")\n\t\tdelete(additionalProperties, \"organization\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\tdelete(additionalProperties, \"use_auto_link\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsOidcProvider struct {\n\tvalue *IdentityCredentialsOidcProvider\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsOidcProvider) Get() *IdentityCredentialsOidcProvider {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsOidcProvider) Set(val *IdentityCredentialsOidcProvider) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsOidcProvider) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsOidcProvider) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsOidcProvider(val *IdentityCredentialsOidcProvider) *NullableIdentityCredentialsOidcProvider {\n\treturn &NullableIdentityCredentialsOidcProvider{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsOidcProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsOidcProvider) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_credentials_password.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityCredentialsPassword type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityCredentialsPassword{}\n\n// IdentityCredentialsPassword struct for IdentityCredentialsPassword\ntype IdentityCredentialsPassword struct {\n\t// HashedPassword is a hash-representation of the password.\n\tHashedPassword *string `json:\"hashed_password,omitempty\"`\n\t// UsePasswordMigrationHook is set to true if the password should be migrated using the password migration hook. If set, and the HashedPassword is empty, a webhook will be called during login to migrate the password.\n\tUsePasswordMigrationHook *bool `json:\"use_password_migration_hook,omitempty\"`\n\tAdditionalProperties     map[string]interface{}\n}\n\ntype _IdentityCredentialsPassword IdentityCredentialsPassword\n\n// NewIdentityCredentialsPassword instantiates a new IdentityCredentialsPassword object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityCredentialsPassword() *IdentityCredentialsPassword {\n\tthis := IdentityCredentialsPassword{}\n\treturn &this\n}\n\n// NewIdentityCredentialsPasswordWithDefaults instantiates a new IdentityCredentialsPassword object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityCredentialsPasswordWithDefaults() *IdentityCredentialsPassword {\n\tthis := IdentityCredentialsPassword{}\n\treturn &this\n}\n\n// GetHashedPassword returns the HashedPassword field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsPassword) GetHashedPassword() string {\n\tif o == nil || IsNil(o.HashedPassword) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.HashedPassword\n}\n\n// GetHashedPasswordOk returns a tuple with the HashedPassword field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsPassword) GetHashedPasswordOk() (*string, bool) {\n\tif o == nil || IsNil(o.HashedPassword) {\n\t\treturn nil, false\n\t}\n\treturn o.HashedPassword, true\n}\n\n// HasHashedPassword returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsPassword) HasHashedPassword() bool {\n\tif o != nil && !IsNil(o.HashedPassword) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetHashedPassword gets a reference to the given string and assigns it to the HashedPassword field.\nfunc (o *IdentityCredentialsPassword) SetHashedPassword(v string) {\n\to.HashedPassword = &v\n}\n\n// GetUsePasswordMigrationHook returns the UsePasswordMigrationHook field value if set, zero value otherwise.\nfunc (o *IdentityCredentialsPassword) GetUsePasswordMigrationHook() bool {\n\tif o == nil || IsNil(o.UsePasswordMigrationHook) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.UsePasswordMigrationHook\n}\n\n// GetUsePasswordMigrationHookOk returns a tuple with the UsePasswordMigrationHook field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityCredentialsPassword) GetUsePasswordMigrationHookOk() (*bool, bool) {\n\tif o == nil || IsNil(o.UsePasswordMigrationHook) {\n\t\treturn nil, false\n\t}\n\treturn o.UsePasswordMigrationHook, true\n}\n\n// HasUsePasswordMigrationHook returns a boolean if a field has been set.\nfunc (o *IdentityCredentialsPassword) HasUsePasswordMigrationHook() bool {\n\tif o != nil && !IsNil(o.UsePasswordMigrationHook) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUsePasswordMigrationHook gets a reference to the given bool and assigns it to the UsePasswordMigrationHook field.\nfunc (o *IdentityCredentialsPassword) SetUsePasswordMigrationHook(v bool) {\n\to.UsePasswordMigrationHook = &v\n}\n\nfunc (o IdentityCredentialsPassword) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityCredentialsPassword) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.HashedPassword) {\n\t\ttoSerialize[\"hashed_password\"] = o.HashedPassword\n\t}\n\tif !IsNil(o.UsePasswordMigrationHook) {\n\t\ttoSerialize[\"use_password_migration_hook\"] = o.UsePasswordMigrationHook\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityCredentialsPassword) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityCredentialsPassword := _IdentityCredentialsPassword{}\n\n\terr = json.Unmarshal(data, &varIdentityCredentialsPassword)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityCredentialsPassword(varIdentityCredentialsPassword)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"hashed_password\")\n\t\tdelete(additionalProperties, \"use_password_migration_hook\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityCredentialsPassword struct {\n\tvalue *IdentityCredentialsPassword\n\tisSet bool\n}\n\nfunc (v NullableIdentityCredentialsPassword) Get() *IdentityCredentialsPassword {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityCredentialsPassword) Set(val *IdentityCredentialsPassword) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityCredentialsPassword) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityCredentialsPassword) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityCredentialsPassword(val *IdentityCredentialsPassword) *NullableIdentityCredentialsPassword {\n\treturn &NullableIdentityCredentialsPassword{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityCredentialsPassword) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityCredentialsPassword) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_patch.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityPatch type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityPatch{}\n\n// IdentityPatch Payload for patching an identity\ntype IdentityPatch struct {\n\tCreate *CreateIdentityBody `json:\"create,omitempty\"`\n\t// The ID of this patch.  The patch ID is optional. If specified, the ID will be returned in the response, so consumers of this API can correlate the response with the patch.\n\tPatchId              *string `json:\"patch_id,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityPatch IdentityPatch\n\n// NewIdentityPatch instantiates a new IdentityPatch object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityPatch() *IdentityPatch {\n\tthis := IdentityPatch{}\n\treturn &this\n}\n\n// NewIdentityPatchWithDefaults instantiates a new IdentityPatch object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityPatchWithDefaults() *IdentityPatch {\n\tthis := IdentityPatch{}\n\treturn &this\n}\n\n// GetCreate returns the Create field value if set, zero value otherwise.\nfunc (o *IdentityPatch) GetCreate() CreateIdentityBody {\n\tif o == nil || IsNil(o.Create) {\n\t\tvar ret CreateIdentityBody\n\t\treturn ret\n\t}\n\treturn *o.Create\n}\n\n// GetCreateOk returns a tuple with the Create field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatch) GetCreateOk() (*CreateIdentityBody, bool) {\n\tif o == nil || IsNil(o.Create) {\n\t\treturn nil, false\n\t}\n\treturn o.Create, true\n}\n\n// HasCreate returns a boolean if a field has been set.\nfunc (o *IdentityPatch) HasCreate() bool {\n\tif o != nil && !IsNil(o.Create) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreate gets a reference to the given CreateIdentityBody and assigns it to the Create field.\nfunc (o *IdentityPatch) SetCreate(v CreateIdentityBody) {\n\to.Create = &v\n}\n\n// GetPatchId returns the PatchId field value if set, zero value otherwise.\nfunc (o *IdentityPatch) GetPatchId() string {\n\tif o == nil || IsNil(o.PatchId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PatchId\n}\n\n// GetPatchIdOk returns a tuple with the PatchId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatch) GetPatchIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.PatchId) {\n\t\treturn nil, false\n\t}\n\treturn o.PatchId, true\n}\n\n// HasPatchId returns a boolean if a field has been set.\nfunc (o *IdentityPatch) HasPatchId() bool {\n\tif o != nil && !IsNil(o.PatchId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPatchId gets a reference to the given string and assigns it to the PatchId field.\nfunc (o *IdentityPatch) SetPatchId(v string) {\n\to.PatchId = &v\n}\n\nfunc (o IdentityPatch) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityPatch) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Create) {\n\t\ttoSerialize[\"create\"] = o.Create\n\t}\n\tif !IsNil(o.PatchId) {\n\t\ttoSerialize[\"patch_id\"] = o.PatchId\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityPatch) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityPatch := _IdentityPatch{}\n\n\terr = json.Unmarshal(data, &varIdentityPatch)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityPatch(varIdentityPatch)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"create\")\n\t\tdelete(additionalProperties, \"patch_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityPatch struct {\n\tvalue *IdentityPatch\n\tisSet bool\n}\n\nfunc (v NullableIdentityPatch) Get() *IdentityPatch {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityPatch) Set(val *IdentityPatch) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityPatch) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityPatch) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityPatch(val *IdentityPatch) *NullableIdentityPatch {\n\treturn &NullableIdentityPatch{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityPatch) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityPatch) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_patch_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityPatchResponse type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityPatchResponse{}\n\n// IdentityPatchResponse Response for a single identity patch\ntype IdentityPatchResponse struct {\n\t// The action for this specific patch create ActionCreate  Create this identity. error ActionError  Error indicates that the patch failed.\n\tAction *string     `json:\"action,omitempty\"`\n\tError  interface{} `json:\"error,omitempty\"`\n\t// The identity ID payload of this patch\n\tIdentity *string `json:\"identity,omitempty\"`\n\t// The ID of this patch response, if an ID was specified in the patch.\n\tPatchId              *string `json:\"patch_id,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityPatchResponse IdentityPatchResponse\n\n// NewIdentityPatchResponse instantiates a new IdentityPatchResponse object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityPatchResponse() *IdentityPatchResponse {\n\tthis := IdentityPatchResponse{}\n\treturn &this\n}\n\n// NewIdentityPatchResponseWithDefaults instantiates a new IdentityPatchResponse object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityPatchResponseWithDefaults() *IdentityPatchResponse {\n\tthis := IdentityPatchResponse{}\n\treturn &this\n}\n\n// GetAction returns the Action field value if set, zero value otherwise.\nfunc (o *IdentityPatchResponse) GetAction() string {\n\tif o == nil || IsNil(o.Action) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatchResponse) GetActionOk() (*string, bool) {\n\tif o == nil || IsNil(o.Action) {\n\t\treturn nil, false\n\t}\n\treturn o.Action, true\n}\n\n// HasAction returns a boolean if a field has been set.\nfunc (o *IdentityPatchResponse) HasAction() bool {\n\tif o != nil && !IsNil(o.Action) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAction gets a reference to the given string and assigns it to the Action field.\nfunc (o *IdentityPatchResponse) SetAction(v string) {\n\to.Action = &v\n}\n\n// GetError returns the Error field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *IdentityPatchResponse) GetError() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *IdentityPatchResponse) GetErrorOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn &o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *IdentityPatchResponse) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given interface{} and assigns it to the Error field.\nfunc (o *IdentityPatchResponse) SetError(v interface{}) {\n\to.Error = v\n}\n\n// GetIdentity returns the Identity field value if set, zero value otherwise.\nfunc (o *IdentityPatchResponse) GetIdentity() string {\n\tif o == nil || IsNil(o.Identity) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Identity\n}\n\n// GetIdentityOk returns a tuple with the Identity field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatchResponse) GetIdentityOk() (*string, bool) {\n\tif o == nil || IsNil(o.Identity) {\n\t\treturn nil, false\n\t}\n\treturn o.Identity, true\n}\n\n// HasIdentity returns a boolean if a field has been set.\nfunc (o *IdentityPatchResponse) HasIdentity() bool {\n\tif o != nil && !IsNil(o.Identity) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentity gets a reference to the given string and assigns it to the Identity field.\nfunc (o *IdentityPatchResponse) SetIdentity(v string) {\n\to.Identity = &v\n}\n\n// GetPatchId returns the PatchId field value if set, zero value otherwise.\nfunc (o *IdentityPatchResponse) GetPatchId() string {\n\tif o == nil || IsNil(o.PatchId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PatchId\n}\n\n// GetPatchIdOk returns a tuple with the PatchId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityPatchResponse) GetPatchIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.PatchId) {\n\t\treturn nil, false\n\t}\n\treturn o.PatchId, true\n}\n\n// HasPatchId returns a boolean if a field has been set.\nfunc (o *IdentityPatchResponse) HasPatchId() bool {\n\tif o != nil && !IsNil(o.PatchId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPatchId gets a reference to the given string and assigns it to the PatchId field.\nfunc (o *IdentityPatchResponse) SetPatchId(v string) {\n\to.PatchId = &v\n}\n\nfunc (o IdentityPatchResponse) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityPatchResponse) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Action) {\n\t\ttoSerialize[\"action\"] = o.Action\n\t}\n\tif o.Error != nil {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.Identity) {\n\t\ttoSerialize[\"identity\"] = o.Identity\n\t}\n\tif !IsNil(o.PatchId) {\n\t\ttoSerialize[\"patch_id\"] = o.PatchId\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityPatchResponse) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityPatchResponse := _IdentityPatchResponse{}\n\n\terr = json.Unmarshal(data, &varIdentityPatchResponse)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityPatchResponse(varIdentityPatchResponse)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"identity\")\n\t\tdelete(additionalProperties, \"patch_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityPatchResponse struct {\n\tvalue *IdentityPatchResponse\n\tisSet bool\n}\n\nfunc (v NullableIdentityPatchResponse) Get() *IdentityPatchResponse {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityPatchResponse) Set(val *IdentityPatchResponse) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityPatchResponse) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityPatchResponse) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityPatchResponse(val *IdentityPatchResponse) *NullableIdentityPatchResponse {\n\treturn &NullableIdentityPatchResponse{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityPatchResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityPatchResponse) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_schema_container.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IdentitySchemaContainer type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentitySchemaContainer{}\n\n// IdentitySchemaContainer An Identity JSON Schema Container\ntype IdentitySchemaContainer struct {\n\t// The ID of the Identity JSON Schema\n\tId string `json:\"id\"`\n\t// The actual Identity JSON Schema\n\tSchema               map[string]interface{} `json:\"schema\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentitySchemaContainer IdentitySchemaContainer\n\n// NewIdentitySchemaContainer instantiates a new IdentitySchemaContainer object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentitySchemaContainer(id string, schema map[string]interface{}) *IdentitySchemaContainer {\n\tthis := IdentitySchemaContainer{}\n\tthis.Id = id\n\tthis.Schema = schema\n\treturn &this\n}\n\n// NewIdentitySchemaContainerWithDefaults instantiates a new IdentitySchemaContainer object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentitySchemaContainerWithDefaults() *IdentitySchemaContainer {\n\tthis := IdentitySchemaContainer{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *IdentitySchemaContainer) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentitySchemaContainer) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *IdentitySchemaContainer) SetId(v string) {\n\to.Id = v\n}\n\n// GetSchema returns the Schema field value\nfunc (o *IdentitySchemaContainer) GetSchema() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Schema\n}\n\n// GetSchemaOk returns a tuple with the Schema field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentitySchemaContainer) GetSchemaOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Schema, true\n}\n\n// SetSchema sets field value\nfunc (o *IdentitySchemaContainer) SetSchema(v map[string]interface{}) {\n\to.Schema = v\n}\n\nfunc (o IdentitySchemaContainer) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentitySchemaContainer) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"schema\"] = o.Schema\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentitySchemaContainer) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"schema\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIdentitySchemaContainer := _IdentitySchemaContainer{}\n\n\terr = json.Unmarshal(data, &varIdentitySchemaContainer)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentitySchemaContainer(varIdentitySchemaContainer)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"schema\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentitySchemaContainer struct {\n\tvalue *IdentitySchemaContainer\n\tisSet bool\n}\n\nfunc (v NullableIdentitySchemaContainer) Get() *IdentitySchemaContainer {\n\treturn v.value\n}\n\nfunc (v *NullableIdentitySchemaContainer) Set(val *IdentitySchemaContainer) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentitySchemaContainer) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentitySchemaContainer) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentitySchemaContainer(val *IdentitySchemaContainer) *NullableIdentitySchemaContainer {\n\treturn &NullableIdentitySchemaContainer{value: val, isSet: true}\n}\n\nfunc (v NullableIdentitySchemaContainer) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentitySchemaContainer) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_with_credentials.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentials type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentials{}\n\n// IdentityWithCredentials Create Identity and Import Credentials\ntype IdentityWithCredentials struct {\n\tOidc                 *IdentityWithCredentialsOidc     `json:\"oidc,omitempty\"`\n\tPassword             *IdentityWithCredentialsPassword `json:\"password,omitempty\"`\n\tSaml                 *IdentityWithCredentialsSaml     `json:\"saml,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentials IdentityWithCredentials\n\n// NewIdentityWithCredentials instantiates a new IdentityWithCredentials object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentials() *IdentityWithCredentials {\n\tthis := IdentityWithCredentials{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsWithDefaults instantiates a new IdentityWithCredentials object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsWithDefaults() *IdentityWithCredentials {\n\tthis := IdentityWithCredentials{}\n\treturn &this\n}\n\n// GetOidc returns the Oidc field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentials) GetOidc() IdentityWithCredentialsOidc {\n\tif o == nil || IsNil(o.Oidc) {\n\t\tvar ret IdentityWithCredentialsOidc\n\t\treturn ret\n\t}\n\treturn *o.Oidc\n}\n\n// GetOidcOk returns a tuple with the Oidc field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentials) GetOidcOk() (*IdentityWithCredentialsOidc, bool) {\n\tif o == nil || IsNil(o.Oidc) {\n\t\treturn nil, false\n\t}\n\treturn o.Oidc, true\n}\n\n// HasOidc returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentials) HasOidc() bool {\n\tif o != nil && !IsNil(o.Oidc) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOidc gets a reference to the given IdentityWithCredentialsOidc and assigns it to the Oidc field.\nfunc (o *IdentityWithCredentials) SetOidc(v IdentityWithCredentialsOidc) {\n\to.Oidc = &v\n}\n\n// GetPassword returns the Password field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentials) GetPassword() IdentityWithCredentialsPassword {\n\tif o == nil || IsNil(o.Password) {\n\t\tvar ret IdentityWithCredentialsPassword\n\t\treturn ret\n\t}\n\treturn *o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentials) GetPasswordOk() (*IdentityWithCredentialsPassword, bool) {\n\tif o == nil || IsNil(o.Password) {\n\t\treturn nil, false\n\t}\n\treturn o.Password, true\n}\n\n// HasPassword returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentials) HasPassword() bool {\n\tif o != nil && !IsNil(o.Password) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPassword gets a reference to the given IdentityWithCredentialsPassword and assigns it to the Password field.\nfunc (o *IdentityWithCredentials) SetPassword(v IdentityWithCredentialsPassword) {\n\to.Password = &v\n}\n\n// GetSaml returns the Saml field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentials) GetSaml() IdentityWithCredentialsSaml {\n\tif o == nil || IsNil(o.Saml) {\n\t\tvar ret IdentityWithCredentialsSaml\n\t\treturn ret\n\t}\n\treturn *o.Saml\n}\n\n// GetSamlOk returns a tuple with the Saml field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentials) GetSamlOk() (*IdentityWithCredentialsSaml, bool) {\n\tif o == nil || IsNil(o.Saml) {\n\t\treturn nil, false\n\t}\n\treturn o.Saml, true\n}\n\n// HasSaml returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentials) HasSaml() bool {\n\tif o != nil && !IsNil(o.Saml) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSaml gets a reference to the given IdentityWithCredentialsSaml and assigns it to the Saml field.\nfunc (o *IdentityWithCredentials) SetSaml(v IdentityWithCredentialsSaml) {\n\to.Saml = &v\n}\n\nfunc (o IdentityWithCredentials) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentials) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Oidc) {\n\t\ttoSerialize[\"oidc\"] = o.Oidc\n\t}\n\tif !IsNil(o.Password) {\n\t\ttoSerialize[\"password\"] = o.Password\n\t}\n\tif !IsNil(o.Saml) {\n\t\ttoSerialize[\"saml\"] = o.Saml\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentials) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentials := _IdentityWithCredentials{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentials)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentials(varIdentityWithCredentials)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"oidc\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"saml\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentials struct {\n\tvalue *IdentityWithCredentials\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentials) Get() *IdentityWithCredentials {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentials) Set(val *IdentityWithCredentials) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentials) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentials) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentials(val *IdentityWithCredentials) *NullableIdentityWithCredentials {\n\treturn &NullableIdentityWithCredentials{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentials) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentials) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_with_credentials_oidc.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsOidc type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsOidc{}\n\n// IdentityWithCredentialsOidc Create Identity and Import Social Sign In Credentials\ntype IdentityWithCredentialsOidc struct {\n\tConfig               *IdentityWithCredentialsOidcConfig `json:\"config,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsOidc IdentityWithCredentialsOidc\n\n// NewIdentityWithCredentialsOidc instantiates a new IdentityWithCredentialsOidc object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsOidc() *IdentityWithCredentialsOidc {\n\tthis := IdentityWithCredentialsOidc{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsOidcWithDefaults instantiates a new IdentityWithCredentialsOidc object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsOidcWithDefaults() *IdentityWithCredentialsOidc {\n\tthis := IdentityWithCredentialsOidc{}\n\treturn &this\n}\n\n// GetConfig returns the Config field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsOidc) GetConfig() IdentityWithCredentialsOidcConfig {\n\tif o == nil || IsNil(o.Config) {\n\t\tvar ret IdentityWithCredentialsOidcConfig\n\t\treturn ret\n\t}\n\treturn *o.Config\n}\n\n// GetConfigOk returns a tuple with the Config field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidc) GetConfigOk() (*IdentityWithCredentialsOidcConfig, bool) {\n\tif o == nil || IsNil(o.Config) {\n\t\treturn nil, false\n\t}\n\treturn o.Config, true\n}\n\n// HasConfig returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsOidc) HasConfig() bool {\n\tif o != nil && !IsNil(o.Config) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfig gets a reference to the given IdentityWithCredentialsOidcConfig and assigns it to the Config field.\nfunc (o *IdentityWithCredentialsOidc) SetConfig(v IdentityWithCredentialsOidcConfig) {\n\to.Config = &v\n}\n\nfunc (o IdentityWithCredentialsOidc) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsOidc) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Config) {\n\t\ttoSerialize[\"config\"] = o.Config\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsOidc) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsOidc := _IdentityWithCredentialsOidc{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsOidc)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsOidc(varIdentityWithCredentialsOidc)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"config\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsOidc struct {\n\tvalue *IdentityWithCredentialsOidc\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsOidc) Get() *IdentityWithCredentialsOidc {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsOidc) Set(val *IdentityWithCredentialsOidc) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsOidc) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsOidc) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsOidc(val *IdentityWithCredentialsOidc) *NullableIdentityWithCredentialsOidc {\n\treturn &NullableIdentityWithCredentialsOidc{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsOidc) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsOidc) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_with_credentials_oidc_config.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsOidcConfig type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsOidcConfig{}\n\n// IdentityWithCredentialsOidcConfig struct for IdentityWithCredentialsOidcConfig\ntype IdentityWithCredentialsOidcConfig struct {\n\t// A list of OpenID Connect Providers\n\tProviders            []IdentityWithCredentialsOidcConfigProvider `json:\"providers,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsOidcConfig IdentityWithCredentialsOidcConfig\n\n// NewIdentityWithCredentialsOidcConfig instantiates a new IdentityWithCredentialsOidcConfig object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsOidcConfig() *IdentityWithCredentialsOidcConfig {\n\tthis := IdentityWithCredentialsOidcConfig{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsOidcConfigWithDefaults instantiates a new IdentityWithCredentialsOidcConfig object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsOidcConfigWithDefaults() *IdentityWithCredentialsOidcConfig {\n\tthis := IdentityWithCredentialsOidcConfig{}\n\treturn &this\n}\n\n// GetProviders returns the Providers field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsOidcConfig) GetProviders() []IdentityWithCredentialsOidcConfigProvider {\n\tif o == nil || IsNil(o.Providers) {\n\t\tvar ret []IdentityWithCredentialsOidcConfigProvider\n\t\treturn ret\n\t}\n\treturn o.Providers\n}\n\n// GetProvidersOk returns a tuple with the Providers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidcConfig) GetProvidersOk() ([]IdentityWithCredentialsOidcConfigProvider, bool) {\n\tif o == nil || IsNil(o.Providers) {\n\t\treturn nil, false\n\t}\n\treturn o.Providers, true\n}\n\n// HasProviders returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsOidcConfig) HasProviders() bool {\n\tif o != nil && !IsNil(o.Providers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProviders gets a reference to the given []IdentityWithCredentialsOidcConfigProvider and assigns it to the Providers field.\nfunc (o *IdentityWithCredentialsOidcConfig) SetProviders(v []IdentityWithCredentialsOidcConfigProvider) {\n\to.Providers = v\n}\n\nfunc (o IdentityWithCredentialsOidcConfig) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsOidcConfig) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Providers) {\n\t\ttoSerialize[\"providers\"] = o.Providers\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsOidcConfig) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsOidcConfig := _IdentityWithCredentialsOidcConfig{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsOidcConfig)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsOidcConfig(varIdentityWithCredentialsOidcConfig)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"providers\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsOidcConfig struct {\n\tvalue *IdentityWithCredentialsOidcConfig\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfig) Get() *IdentityWithCredentialsOidcConfig {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfig) Set(val *IdentityWithCredentialsOidcConfig) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfig) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfig) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsOidcConfig(val *IdentityWithCredentialsOidcConfig) *NullableIdentityWithCredentialsOidcConfig {\n\treturn &NullableIdentityWithCredentialsOidcConfig{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfig) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfig) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_with_credentials_oidc_config_provider.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IdentityWithCredentialsOidcConfigProvider type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsOidcConfigProvider{}\n\n// IdentityWithCredentialsOidcConfigProvider Create Identity and Import Social Sign In Credentials Configuration\ntype IdentityWithCredentialsOidcConfigProvider struct {\n\tOrganization NullableString `json:\"organization,omitempty\"`\n\t// The OpenID Connect provider to link the subject to. Usually something like `google` or `github`.\n\tProvider string `json:\"provider\"`\n\t// The subject (`sub`) of the OpenID Connect connection. Usually the `sub` field of the ID Token.\n\tSubject string `json:\"subject\"`\n\t// If set, this credential allows the user to sign in using the OpenID Connect provider without setting the subject first.\n\tUseAutoLink          *bool `json:\"use_auto_link,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsOidcConfigProvider IdentityWithCredentialsOidcConfigProvider\n\n// NewIdentityWithCredentialsOidcConfigProvider instantiates a new IdentityWithCredentialsOidcConfigProvider object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsOidcConfigProvider(provider string, subject string) *IdentityWithCredentialsOidcConfigProvider {\n\tthis := IdentityWithCredentialsOidcConfigProvider{}\n\tthis.Provider = provider\n\tthis.Subject = subject\n\treturn &this\n}\n\n// NewIdentityWithCredentialsOidcConfigProviderWithDefaults instantiates a new IdentityWithCredentialsOidcConfigProvider object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsOidcConfigProviderWithDefaults() *IdentityWithCredentialsOidcConfigProvider {\n\tthis := IdentityWithCredentialsOidcConfigProvider{}\n\treturn &this\n}\n\n// GetOrganization returns the Organization field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetOrganization() string {\n\tif o == nil || IsNil(o.Organization.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Organization.Get()\n}\n\n// GetOrganizationOk returns a tuple with the Organization field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetOrganizationOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.Organization.Get(), o.Organization.IsSet()\n}\n\n// HasOrganization returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) HasOrganization() bool {\n\tif o != nil && o.Organization.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganization gets a reference to the given NullableString and assigns it to the Organization field.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetOrganization(v string) {\n\to.Organization.Set(&v)\n}\n\n// SetOrganizationNil sets the value for Organization to be an explicit nil\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetOrganizationNil() {\n\to.Organization.Set(nil)\n}\n\n// UnsetOrganization ensures that no value is present for Organization, not even an explicit nil\nfunc (o *IdentityWithCredentialsOidcConfigProvider) UnsetOrganization() {\n\to.Organization.Unset()\n}\n\n// GetProvider returns the Provider field value\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetSubject returns the Subject field value\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetSubject() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetSubjectOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Subject, true\n}\n\n// SetSubject sets field value\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetSubject(v string) {\n\to.Subject = v\n}\n\n// GetUseAutoLink returns the UseAutoLink field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetUseAutoLink() bool {\n\tif o == nil || IsNil(o.UseAutoLink) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.UseAutoLink\n}\n\n// GetUseAutoLinkOk returns a tuple with the UseAutoLink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) GetUseAutoLinkOk() (*bool, bool) {\n\tif o == nil || IsNil(o.UseAutoLink) {\n\t\treturn nil, false\n\t}\n\treturn o.UseAutoLink, true\n}\n\n// HasUseAutoLink returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) HasUseAutoLink() bool {\n\tif o != nil && !IsNil(o.UseAutoLink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUseAutoLink gets a reference to the given bool and assigns it to the UseAutoLink field.\nfunc (o *IdentityWithCredentialsOidcConfigProvider) SetUseAutoLink(v bool) {\n\to.UseAutoLink = &v\n}\n\nfunc (o IdentityWithCredentialsOidcConfigProvider) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsOidcConfigProvider) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif o.Organization.IsSet() {\n\t\ttoSerialize[\"organization\"] = o.Organization.Get()\n\t}\n\ttoSerialize[\"provider\"] = o.Provider\n\ttoSerialize[\"subject\"] = o.Subject\n\tif !IsNil(o.UseAutoLink) {\n\t\ttoSerialize[\"use_auto_link\"] = o.UseAutoLink\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsOidcConfigProvider) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"provider\",\n\t\t\"subject\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIdentityWithCredentialsOidcConfigProvider := _IdentityWithCredentialsOidcConfigProvider{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsOidcConfigProvider)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsOidcConfigProvider(varIdentityWithCredentialsOidcConfigProvider)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"organization\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\tdelete(additionalProperties, \"use_auto_link\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsOidcConfigProvider struct {\n\tvalue *IdentityWithCredentialsOidcConfigProvider\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfigProvider) Get() *IdentityWithCredentialsOidcConfigProvider {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfigProvider) Set(val *IdentityWithCredentialsOidcConfigProvider) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfigProvider) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfigProvider) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsOidcConfigProvider(val *IdentityWithCredentialsOidcConfigProvider) *NullableIdentityWithCredentialsOidcConfigProvider {\n\treturn &NullableIdentityWithCredentialsOidcConfigProvider{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsOidcConfigProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsOidcConfigProvider) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_with_credentials_password.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsPassword type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsPassword{}\n\n// IdentityWithCredentialsPassword Create Identity and Import Password Credentials\ntype IdentityWithCredentialsPassword struct {\n\tConfig               *IdentityWithCredentialsPasswordConfig `json:\"config,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsPassword IdentityWithCredentialsPassword\n\n// NewIdentityWithCredentialsPassword instantiates a new IdentityWithCredentialsPassword object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsPassword() *IdentityWithCredentialsPassword {\n\tthis := IdentityWithCredentialsPassword{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsPasswordWithDefaults instantiates a new IdentityWithCredentialsPassword object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsPasswordWithDefaults() *IdentityWithCredentialsPassword {\n\tthis := IdentityWithCredentialsPassword{}\n\treturn &this\n}\n\n// GetConfig returns the Config field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsPassword) GetConfig() IdentityWithCredentialsPasswordConfig {\n\tif o == nil || IsNil(o.Config) {\n\t\tvar ret IdentityWithCredentialsPasswordConfig\n\t\treturn ret\n\t}\n\treturn *o.Config\n}\n\n// GetConfigOk returns a tuple with the Config field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsPassword) GetConfigOk() (*IdentityWithCredentialsPasswordConfig, bool) {\n\tif o == nil || IsNil(o.Config) {\n\t\treturn nil, false\n\t}\n\treturn o.Config, true\n}\n\n// HasConfig returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsPassword) HasConfig() bool {\n\tif o != nil && !IsNil(o.Config) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfig gets a reference to the given IdentityWithCredentialsPasswordConfig and assigns it to the Config field.\nfunc (o *IdentityWithCredentialsPassword) SetConfig(v IdentityWithCredentialsPasswordConfig) {\n\to.Config = &v\n}\n\nfunc (o IdentityWithCredentialsPassword) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsPassword) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Config) {\n\t\ttoSerialize[\"config\"] = o.Config\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsPassword) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsPassword := _IdentityWithCredentialsPassword{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsPassword)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsPassword(varIdentityWithCredentialsPassword)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"config\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsPassword struct {\n\tvalue *IdentityWithCredentialsPassword\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsPassword) Get() *IdentityWithCredentialsPassword {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsPassword) Set(val *IdentityWithCredentialsPassword) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsPassword) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsPassword) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsPassword(val *IdentityWithCredentialsPassword) *NullableIdentityWithCredentialsPassword {\n\treturn &NullableIdentityWithCredentialsPassword{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsPassword) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsPassword) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_with_credentials_password_config.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsPasswordConfig type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsPasswordConfig{}\n\n// IdentityWithCredentialsPasswordConfig Create Identity and Import Password Credentials Configuration\ntype IdentityWithCredentialsPasswordConfig struct {\n\t// The hashed password in [PHC format](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities#hashed-passwords)\n\tHashedPassword *string `json:\"hashed_password,omitempty\"`\n\t// The password in plain text if no hash is available.\n\tPassword *string `json:\"password,omitempty\"`\n\t// If set to true, the password will be migrated using the password migration hook.\n\tUsePasswordMigrationHook *bool `json:\"use_password_migration_hook,omitempty\"`\n\tAdditionalProperties     map[string]interface{}\n}\n\ntype _IdentityWithCredentialsPasswordConfig IdentityWithCredentialsPasswordConfig\n\n// NewIdentityWithCredentialsPasswordConfig instantiates a new IdentityWithCredentialsPasswordConfig object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsPasswordConfig() *IdentityWithCredentialsPasswordConfig {\n\tthis := IdentityWithCredentialsPasswordConfig{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsPasswordConfigWithDefaults instantiates a new IdentityWithCredentialsPasswordConfig object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsPasswordConfigWithDefaults() *IdentityWithCredentialsPasswordConfig {\n\tthis := IdentityWithCredentialsPasswordConfig{}\n\treturn &this\n}\n\n// GetHashedPassword returns the HashedPassword field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetHashedPassword() string {\n\tif o == nil || IsNil(o.HashedPassword) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.HashedPassword\n}\n\n// GetHashedPasswordOk returns a tuple with the HashedPassword field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetHashedPasswordOk() (*string, bool) {\n\tif o == nil || IsNil(o.HashedPassword) {\n\t\treturn nil, false\n\t}\n\treturn o.HashedPassword, true\n}\n\n// HasHashedPassword returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) HasHashedPassword() bool {\n\tif o != nil && !IsNil(o.HashedPassword) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetHashedPassword gets a reference to the given string and assigns it to the HashedPassword field.\nfunc (o *IdentityWithCredentialsPasswordConfig) SetHashedPassword(v string) {\n\to.HashedPassword = &v\n}\n\n// GetPassword returns the Password field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetPassword() string {\n\tif o == nil || IsNil(o.Password) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetPasswordOk() (*string, bool) {\n\tif o == nil || IsNil(o.Password) {\n\t\treturn nil, false\n\t}\n\treturn o.Password, true\n}\n\n// HasPassword returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) HasPassword() bool {\n\tif o != nil && !IsNil(o.Password) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPassword gets a reference to the given string and assigns it to the Password field.\nfunc (o *IdentityWithCredentialsPasswordConfig) SetPassword(v string) {\n\to.Password = &v\n}\n\n// GetUsePasswordMigrationHook returns the UsePasswordMigrationHook field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetUsePasswordMigrationHook() bool {\n\tif o == nil || IsNil(o.UsePasswordMigrationHook) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.UsePasswordMigrationHook\n}\n\n// GetUsePasswordMigrationHookOk returns a tuple with the UsePasswordMigrationHook field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) GetUsePasswordMigrationHookOk() (*bool, bool) {\n\tif o == nil || IsNil(o.UsePasswordMigrationHook) {\n\t\treturn nil, false\n\t}\n\treturn o.UsePasswordMigrationHook, true\n}\n\n// HasUsePasswordMigrationHook returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsPasswordConfig) HasUsePasswordMigrationHook() bool {\n\tif o != nil && !IsNil(o.UsePasswordMigrationHook) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUsePasswordMigrationHook gets a reference to the given bool and assigns it to the UsePasswordMigrationHook field.\nfunc (o *IdentityWithCredentialsPasswordConfig) SetUsePasswordMigrationHook(v bool) {\n\to.UsePasswordMigrationHook = &v\n}\n\nfunc (o IdentityWithCredentialsPasswordConfig) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsPasswordConfig) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.HashedPassword) {\n\t\ttoSerialize[\"hashed_password\"] = o.HashedPassword\n\t}\n\tif !IsNil(o.Password) {\n\t\ttoSerialize[\"password\"] = o.Password\n\t}\n\tif !IsNil(o.UsePasswordMigrationHook) {\n\t\ttoSerialize[\"use_password_migration_hook\"] = o.UsePasswordMigrationHook\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsPasswordConfig) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsPasswordConfig := _IdentityWithCredentialsPasswordConfig{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsPasswordConfig)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsPasswordConfig(varIdentityWithCredentialsPasswordConfig)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"hashed_password\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"use_password_migration_hook\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsPasswordConfig struct {\n\tvalue *IdentityWithCredentialsPasswordConfig\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsPasswordConfig) Get() *IdentityWithCredentialsPasswordConfig {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsPasswordConfig) Set(val *IdentityWithCredentialsPasswordConfig) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsPasswordConfig) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsPasswordConfig) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsPasswordConfig(val *IdentityWithCredentialsPasswordConfig) *NullableIdentityWithCredentialsPasswordConfig {\n\treturn &NullableIdentityWithCredentialsPasswordConfig{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsPasswordConfig) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsPasswordConfig) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_with_credentials_saml.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsSaml type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsSaml{}\n\n// IdentityWithCredentialsSaml Payload to import SAML credentials\ntype IdentityWithCredentialsSaml struct {\n\tConfig               *IdentityWithCredentialsSamlConfig `json:\"config,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsSaml IdentityWithCredentialsSaml\n\n// NewIdentityWithCredentialsSaml instantiates a new IdentityWithCredentialsSaml object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsSaml() *IdentityWithCredentialsSaml {\n\tthis := IdentityWithCredentialsSaml{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsSamlWithDefaults instantiates a new IdentityWithCredentialsSaml object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsSamlWithDefaults() *IdentityWithCredentialsSaml {\n\tthis := IdentityWithCredentialsSaml{}\n\treturn &this\n}\n\n// GetConfig returns the Config field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsSaml) GetConfig() IdentityWithCredentialsSamlConfig {\n\tif o == nil || IsNil(o.Config) {\n\t\tvar ret IdentityWithCredentialsSamlConfig\n\t\treturn ret\n\t}\n\treturn *o.Config\n}\n\n// GetConfigOk returns a tuple with the Config field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsSaml) GetConfigOk() (*IdentityWithCredentialsSamlConfig, bool) {\n\tif o == nil || IsNil(o.Config) {\n\t\treturn nil, false\n\t}\n\treturn o.Config, true\n}\n\n// HasConfig returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsSaml) HasConfig() bool {\n\tif o != nil && !IsNil(o.Config) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfig gets a reference to the given IdentityWithCredentialsSamlConfig and assigns it to the Config field.\nfunc (o *IdentityWithCredentialsSaml) SetConfig(v IdentityWithCredentialsSamlConfig) {\n\to.Config = &v\n}\n\nfunc (o IdentityWithCredentialsSaml) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsSaml) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Config) {\n\t\ttoSerialize[\"config\"] = o.Config\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsSaml) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsSaml := _IdentityWithCredentialsSaml{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsSaml)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsSaml(varIdentityWithCredentialsSaml)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"config\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsSaml struct {\n\tvalue *IdentityWithCredentialsSaml\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsSaml) Get() *IdentityWithCredentialsSaml {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsSaml) Set(val *IdentityWithCredentialsSaml) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsSaml) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsSaml) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsSaml(val *IdentityWithCredentialsSaml) *NullableIdentityWithCredentialsSaml {\n\treturn &NullableIdentityWithCredentialsSaml{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsSaml) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsSaml) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_with_credentials_saml_config.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the IdentityWithCredentialsSamlConfig type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsSamlConfig{}\n\n// IdentityWithCredentialsSamlConfig Payload of SAML providers\ntype IdentityWithCredentialsSamlConfig struct {\n\t// A list of SAML Providers\n\tProviders            []IdentityWithCredentialsSamlConfigProvider `json:\"providers,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsSamlConfig IdentityWithCredentialsSamlConfig\n\n// NewIdentityWithCredentialsSamlConfig instantiates a new IdentityWithCredentialsSamlConfig object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsSamlConfig() *IdentityWithCredentialsSamlConfig {\n\tthis := IdentityWithCredentialsSamlConfig{}\n\treturn &this\n}\n\n// NewIdentityWithCredentialsSamlConfigWithDefaults instantiates a new IdentityWithCredentialsSamlConfig object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsSamlConfigWithDefaults() *IdentityWithCredentialsSamlConfig {\n\tthis := IdentityWithCredentialsSamlConfig{}\n\treturn &this\n}\n\n// GetProviders returns the Providers field value if set, zero value otherwise.\nfunc (o *IdentityWithCredentialsSamlConfig) GetProviders() []IdentityWithCredentialsSamlConfigProvider {\n\tif o == nil || IsNil(o.Providers) {\n\t\tvar ret []IdentityWithCredentialsSamlConfigProvider\n\t\treturn ret\n\t}\n\treturn o.Providers\n}\n\n// GetProvidersOk returns a tuple with the Providers field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsSamlConfig) GetProvidersOk() ([]IdentityWithCredentialsSamlConfigProvider, bool) {\n\tif o == nil || IsNil(o.Providers) {\n\t\treturn nil, false\n\t}\n\treturn o.Providers, true\n}\n\n// HasProviders returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsSamlConfig) HasProviders() bool {\n\tif o != nil && !IsNil(o.Providers) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProviders gets a reference to the given []IdentityWithCredentialsSamlConfigProvider and assigns it to the Providers field.\nfunc (o *IdentityWithCredentialsSamlConfig) SetProviders(v []IdentityWithCredentialsSamlConfigProvider) {\n\to.Providers = v\n}\n\nfunc (o IdentityWithCredentialsSamlConfig) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsSamlConfig) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Providers) {\n\t\ttoSerialize[\"providers\"] = o.Providers\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsSamlConfig) UnmarshalJSON(data []byte) (err error) {\n\tvarIdentityWithCredentialsSamlConfig := _IdentityWithCredentialsSamlConfig{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsSamlConfig)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsSamlConfig(varIdentityWithCredentialsSamlConfig)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"providers\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsSamlConfig struct {\n\tvalue *IdentityWithCredentialsSamlConfig\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfig) Get() *IdentityWithCredentialsSamlConfig {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfig) Set(val *IdentityWithCredentialsSamlConfig) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfig) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfig) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsSamlConfig(val *IdentityWithCredentialsSamlConfig) *NullableIdentityWithCredentialsSamlConfig {\n\treturn &NullableIdentityWithCredentialsSamlConfig{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfig) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfig) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_identity_with_credentials_saml_config_provider.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IdentityWithCredentialsSamlConfigProvider type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IdentityWithCredentialsSamlConfigProvider{}\n\n// IdentityWithCredentialsSamlConfigProvider Payload of specific SAML provider\ntype IdentityWithCredentialsSamlConfigProvider struct {\n\tOrganization NullableString `json:\"organization,omitempty\"`\n\t// The SAML provider to link the subject to.\n\tProvider string `json:\"provider\"`\n\t// The unique subject of the SAML connection. This value must be immutable at the source.\n\tSubject              string `json:\"subject\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IdentityWithCredentialsSamlConfigProvider IdentityWithCredentialsSamlConfigProvider\n\n// NewIdentityWithCredentialsSamlConfigProvider instantiates a new IdentityWithCredentialsSamlConfigProvider object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIdentityWithCredentialsSamlConfigProvider(provider string, subject string) *IdentityWithCredentialsSamlConfigProvider {\n\tthis := IdentityWithCredentialsSamlConfigProvider{}\n\tthis.Provider = provider\n\tthis.Subject = subject\n\treturn &this\n}\n\n// NewIdentityWithCredentialsSamlConfigProviderWithDefaults instantiates a new IdentityWithCredentialsSamlConfigProvider object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIdentityWithCredentialsSamlConfigProviderWithDefaults() *IdentityWithCredentialsSamlConfigProvider {\n\tthis := IdentityWithCredentialsSamlConfigProvider{}\n\treturn &this\n}\n\n// GetOrganization returns the Organization field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetOrganization() string {\n\tif o == nil || IsNil(o.Organization.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Organization.Get()\n}\n\n// GetOrganizationOk returns a tuple with the Organization field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetOrganizationOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.Organization.Get(), o.Organization.IsSet()\n}\n\n// HasOrganization returns a boolean if a field has been set.\nfunc (o *IdentityWithCredentialsSamlConfigProvider) HasOrganization() bool {\n\tif o != nil && o.Organization.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganization gets a reference to the given NullableString and assigns it to the Organization field.\nfunc (o *IdentityWithCredentialsSamlConfigProvider) SetOrganization(v string) {\n\to.Organization.Set(&v)\n}\n\n// SetOrganizationNil sets the value for Organization to be an explicit nil\nfunc (o *IdentityWithCredentialsSamlConfigProvider) SetOrganizationNil() {\n\to.Organization.Set(nil)\n}\n\n// UnsetOrganization ensures that no value is present for Organization, not even an explicit nil\nfunc (o *IdentityWithCredentialsSamlConfigProvider) UnsetOrganization() {\n\to.Organization.Unset()\n}\n\n// GetProvider returns the Provider field value\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *IdentityWithCredentialsSamlConfigProvider) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetSubject returns the Subject field value\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetSubject() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value\n// and a boolean to check if the value has been set.\nfunc (o *IdentityWithCredentialsSamlConfigProvider) GetSubjectOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Subject, true\n}\n\n// SetSubject sets field value\nfunc (o *IdentityWithCredentialsSamlConfigProvider) SetSubject(v string) {\n\to.Subject = v\n}\n\nfunc (o IdentityWithCredentialsSamlConfigProvider) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IdentityWithCredentialsSamlConfigProvider) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif o.Organization.IsSet() {\n\t\ttoSerialize[\"organization\"] = o.Organization.Get()\n\t}\n\ttoSerialize[\"provider\"] = o.Provider\n\ttoSerialize[\"subject\"] = o.Subject\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IdentityWithCredentialsSamlConfigProvider) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"provider\",\n\t\t\"subject\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIdentityWithCredentialsSamlConfigProvider := _IdentityWithCredentialsSamlConfigProvider{}\n\n\terr = json.Unmarshal(data, &varIdentityWithCredentialsSamlConfigProvider)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IdentityWithCredentialsSamlConfigProvider(varIdentityWithCredentialsSamlConfigProvider)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"organization\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIdentityWithCredentialsSamlConfigProvider struct {\n\tvalue *IdentityWithCredentialsSamlConfigProvider\n\tisSet bool\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfigProvider) Get() *IdentityWithCredentialsSamlConfigProvider {\n\treturn v.value\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfigProvider) Set(val *IdentityWithCredentialsSamlConfigProvider) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfigProvider) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfigProvider) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIdentityWithCredentialsSamlConfigProvider(val *IdentityWithCredentialsSamlConfigProvider) *NullableIdentityWithCredentialsSamlConfigProvider {\n\treturn &NullableIdentityWithCredentialsSamlConfigProvider{value: val, isSet: true}\n}\n\nfunc (v NullableIdentityWithCredentialsSamlConfigProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIdentityWithCredentialsSamlConfigProvider) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_is_alive_200_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IsAlive200Response type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IsAlive200Response{}\n\n// IsAlive200Response struct for IsAlive200Response\ntype IsAlive200Response struct {\n\t// Always \\\"ok\\\".\n\tStatus               string `json:\"status\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IsAlive200Response IsAlive200Response\n\n// NewIsAlive200Response instantiates a new IsAlive200Response object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIsAlive200Response(status string) *IsAlive200Response {\n\tthis := IsAlive200Response{}\n\tthis.Status = status\n\treturn &this\n}\n\n// NewIsAlive200ResponseWithDefaults instantiates a new IsAlive200Response object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIsAlive200ResponseWithDefaults() *IsAlive200Response {\n\tthis := IsAlive200Response{}\n\treturn &this\n}\n\n// GetStatus returns the Status field value\nfunc (o *IsAlive200Response) GetStatus() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value\n// and a boolean to check if the value has been set.\nfunc (o *IsAlive200Response) GetStatusOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Status, true\n}\n\n// SetStatus sets field value\nfunc (o *IsAlive200Response) SetStatus(v string) {\n\to.Status = v\n}\n\nfunc (o IsAlive200Response) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IsAlive200Response) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"status\"] = o.Status\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IsAlive200Response) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"status\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIsAlive200Response := _IsAlive200Response{}\n\n\terr = json.Unmarshal(data, &varIsAlive200Response)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IsAlive200Response(varIsAlive200Response)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"status\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIsAlive200Response struct {\n\tvalue *IsAlive200Response\n\tisSet bool\n}\n\nfunc (v NullableIsAlive200Response) Get() *IsAlive200Response {\n\treturn v.value\n}\n\nfunc (v *NullableIsAlive200Response) Set(val *IsAlive200Response) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIsAlive200Response) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIsAlive200Response) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIsAlive200Response(val *IsAlive200Response) *NullableIsAlive200Response {\n\treturn &NullableIsAlive200Response{value: val, isSet: true}\n}\n\nfunc (v NullableIsAlive200Response) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIsAlive200Response) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_is_ready_503_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the IsReady503Response type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &IsReady503Response{}\n\n// IsReady503Response struct for IsReady503Response\ntype IsReady503Response struct {\n\t// Errors contains a list of errors that caused the not ready status.\n\tErrors               map[string]string `json:\"errors\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _IsReady503Response IsReady503Response\n\n// NewIsReady503Response instantiates a new IsReady503Response object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewIsReady503Response(errors map[string]string) *IsReady503Response {\n\tthis := IsReady503Response{}\n\tthis.Errors = errors\n\treturn &this\n}\n\n// NewIsReady503ResponseWithDefaults instantiates a new IsReady503Response object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewIsReady503ResponseWithDefaults() *IsReady503Response {\n\tthis := IsReady503Response{}\n\treturn &this\n}\n\n// GetErrors returns the Errors field value\nfunc (o *IsReady503Response) GetErrors() map[string]string {\n\tif o == nil {\n\t\tvar ret map[string]string\n\t\treturn ret\n\t}\n\n\treturn o.Errors\n}\n\n// GetErrorsOk returns a tuple with the Errors field value\n// and a boolean to check if the value has been set.\nfunc (o *IsReady503Response) GetErrorsOk() (*map[string]string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Errors, true\n}\n\n// SetErrors sets field value\nfunc (o *IsReady503Response) SetErrors(v map[string]string) {\n\to.Errors = v\n}\n\nfunc (o IsReady503Response) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o IsReady503Response) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"errors\"] = o.Errors\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *IsReady503Response) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"errors\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarIsReady503Response := _IsReady503Response{}\n\n\terr = json.Unmarshal(data, &varIsReady503Response)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = IsReady503Response(varIsReady503Response)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"errors\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableIsReady503Response struct {\n\tvalue *IsReady503Response\n\tisSet bool\n}\n\nfunc (v NullableIsReady503Response) Get() *IsReady503Response {\n\treturn v.value\n}\n\nfunc (v *NullableIsReady503Response) Set(val *IsReady503Response) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableIsReady503Response) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableIsReady503Response) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableIsReady503Response(val *IsReady503Response) *NullableIsReady503Response {\n\treturn &NullableIsReady503Response{value: val, isSet: true}\n}\n\nfunc (v NullableIsReady503Response) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableIsReady503Response) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_json_patch.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the JsonPatch type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &JsonPatch{}\n\n// JsonPatch A JSONPatch document as defined by RFC 6902\ntype JsonPatch struct {\n\t// This field is used together with operation \\\"move\\\" and uses JSON Pointer notation.  Learn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\n\tFrom *string `json:\"from,omitempty\"`\n\t// The operation to be performed. One of \\\"add\\\", \\\"remove\\\", \\\"replace\\\", \\\"move\\\", \\\"copy\\\", or \\\"test\\\".\n\tOp string `json:\"op\"`\n\t// The path to the target path. Uses JSON pointer notation.  Learn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\n\tPath string `json:\"path\"`\n\t// The value to be used within the operations.  Learn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\n\tValue                interface{} `json:\"value,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _JsonPatch JsonPatch\n\n// NewJsonPatch instantiates a new JsonPatch object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewJsonPatch(op string, path string) *JsonPatch {\n\tthis := JsonPatch{}\n\tthis.Op = op\n\tthis.Path = path\n\treturn &this\n}\n\n// NewJsonPatchWithDefaults instantiates a new JsonPatch object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewJsonPatchWithDefaults() *JsonPatch {\n\tthis := JsonPatch{}\n\treturn &this\n}\n\n// GetFrom returns the From field value if set, zero value otherwise.\nfunc (o *JsonPatch) GetFrom() string {\n\tif o == nil || IsNil(o.From) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.From\n}\n\n// GetFromOk returns a tuple with the From field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *JsonPatch) GetFromOk() (*string, bool) {\n\tif o == nil || IsNil(o.From) {\n\t\treturn nil, false\n\t}\n\treturn o.From, true\n}\n\n// HasFrom returns a boolean if a field has been set.\nfunc (o *JsonPatch) HasFrom() bool {\n\tif o != nil && !IsNil(o.From) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFrom gets a reference to the given string and assigns it to the From field.\nfunc (o *JsonPatch) SetFrom(v string) {\n\to.From = &v\n}\n\n// GetOp returns the Op field value\nfunc (o *JsonPatch) GetOp() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Op\n}\n\n// GetOpOk returns a tuple with the Op field value\n// and a boolean to check if the value has been set.\nfunc (o *JsonPatch) GetOpOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Op, true\n}\n\n// SetOp sets field value\nfunc (o *JsonPatch) SetOp(v string) {\n\to.Op = v\n}\n\n// GetPath returns the Path field value\nfunc (o *JsonPatch) GetPath() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Path\n}\n\n// GetPathOk returns a tuple with the Path field value\n// and a boolean to check if the value has been set.\nfunc (o *JsonPatch) GetPathOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Path, true\n}\n\n// SetPath sets field value\nfunc (o *JsonPatch) SetPath(v string) {\n\to.Path = v\n}\n\n// GetValue returns the Value field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *JsonPatch) GetValue() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Value\n}\n\n// GetValueOk returns a tuple with the Value field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *JsonPatch) GetValueOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Value) {\n\t\treturn nil, false\n\t}\n\treturn &o.Value, true\n}\n\n// HasValue returns a boolean if a field has been set.\nfunc (o *JsonPatch) HasValue() bool {\n\tif o != nil && !IsNil(o.Value) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetValue gets a reference to the given interface{} and assigns it to the Value field.\nfunc (o *JsonPatch) SetValue(v interface{}) {\n\to.Value = v\n}\n\nfunc (o JsonPatch) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o JsonPatch) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.From) {\n\t\ttoSerialize[\"from\"] = o.From\n\t}\n\ttoSerialize[\"op\"] = o.Op\n\ttoSerialize[\"path\"] = o.Path\n\tif o.Value != nil {\n\t\ttoSerialize[\"value\"] = o.Value\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *JsonPatch) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"op\",\n\t\t\"path\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarJsonPatch := _JsonPatch{}\n\n\terr = json.Unmarshal(data, &varJsonPatch)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = JsonPatch(varJsonPatch)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"from\")\n\t\tdelete(additionalProperties, \"op\")\n\t\tdelete(additionalProperties, \"path\")\n\t\tdelete(additionalProperties, \"value\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableJsonPatch struct {\n\tvalue *JsonPatch\n\tisSet bool\n}\n\nfunc (v NullableJsonPatch) Get() *JsonPatch {\n\treturn v.value\n}\n\nfunc (v *NullableJsonPatch) Set(val *JsonPatch) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableJsonPatch) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableJsonPatch) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableJsonPatch(val *JsonPatch) *NullableJsonPatch {\n\treturn &NullableJsonPatch{value: val, isSet: true}\n}\n\nfunc (v NullableJsonPatch) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableJsonPatch) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_login_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the LoginFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &LoginFlow{}\n\n// LoginFlow This object represents a login flow. A login flow is initiated at the \\\"Initiate Login API / Browser Flow\\\" endpoint by a client.  Once a login flow is completed successfully, a session cookie or session token will be issued.\ntype LoginFlow struct {\n\t// The active login method  If set contains the login method used. If the flow is new, it is unset. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\tActive *string `json:\"active,omitempty\"`\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in, a new flow has to be initiated.\n\tExpiresAt time.Time `json:\"expires_at\"`\n\t// ID represents the flow's unique ID. When performing the login flow, this represents the id in the login UI's query parameter: http://<selfservice.flows.login.ui_url>/?flow=<flow_id>\n\tId string `json:\"id\"`\n\t// IdentitySchema optionally holds the ID of the identity schema that is used for this flow. This value can be set by the user when creating the flow and should be retained when the flow is saved or converted to another flow.\n\tIdentitySchema *string `json:\"identity_schema,omitempty\"`\n\t// IssuedAt is the time (UTC) when the flow started.\n\tIssuedAt time.Time `json:\"issued_at\"`\n\t// Ory OAuth 2.0 Login Challenge.  This value is set using the `login_challenge` query parameter of the registration and login endpoints. If set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\n\tOauth2LoginChallenge *string             `json:\"oauth2_login_challenge,omitempty\"`\n\tOauth2LoginRequest   *OAuth2LoginRequest `json:\"oauth2_login_request,omitempty\"`\n\tOrganizationId       NullableString      `json:\"organization_id,omitempty\"`\n\t// Refresh stores whether this login flow should enforce re-authentication.\n\tRefresh *bool `json:\"refresh,omitempty\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl   string                       `json:\"request_url\"`\n\tRequestedAal *AuthenticatorAssuranceLevel `json:\"requested_aal,omitempty\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the login flow has been completed. This is only set if the client has requested a session token exchange code, and if the flow is of type \\\"api\\\", and only on creating the login flow.\n\tSessionTokenExchangeCode *string `json:\"session_token_exchange_code,omitempty\"`\n\t// State represents the state of this request:  choose_method: ask the user to choose a method to sign in with sent_email: the email has been sent to the user passed_challenge: the request was successful and the login challenge was passed.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the login to hooks and email templates\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType string      `json:\"type\"`\n\tUi   UiContainer `json:\"ui\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            *time.Time `json:\"updated_at,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _LoginFlow LoginFlow\n\n// NewLoginFlow instantiates a new LoginFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewLoginFlow(expiresAt time.Time, id string, issuedAt time.Time, requestUrl string, state interface{}, type_ string, ui UiContainer) *LoginFlow {\n\tthis := LoginFlow{}\n\tthis.ExpiresAt = expiresAt\n\tthis.Id = id\n\tthis.IssuedAt = issuedAt\n\tthis.RequestUrl = requestUrl\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewLoginFlowWithDefaults instantiates a new LoginFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewLoginFlowWithDefaults() *LoginFlow {\n\tthis := LoginFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *LoginFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *LoginFlow) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetExpiresAt returns the ExpiresAt field value\nfunc (o *LoginFlow) GetExpiresAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.ExpiresAt, true\n}\n\n// SetExpiresAt sets field value\nfunc (o *LoginFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = v\n}\n\n// GetId returns the Id field value\nfunc (o *LoginFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *LoginFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIdentitySchema returns the IdentitySchema field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetIdentitySchema() string {\n\tif o == nil || IsNil(o.IdentitySchema) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdentitySchema\n}\n\n// GetIdentitySchemaOk returns a tuple with the IdentitySchema field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetIdentitySchemaOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdentitySchema) {\n\t\treturn nil, false\n\t}\n\treturn o.IdentitySchema, true\n}\n\n// HasIdentitySchema returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasIdentitySchema() bool {\n\tif o != nil && !IsNil(o.IdentitySchema) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentitySchema gets a reference to the given string and assigns it to the IdentitySchema field.\nfunc (o *LoginFlow) SetIdentitySchema(v string) {\n\to.IdentitySchema = &v\n}\n\n// GetIssuedAt returns the IssuedAt field value\nfunc (o *LoginFlow) GetIssuedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IssuedAt, true\n}\n\n// SetIssuedAt sets field value\nfunc (o *LoginFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = v\n}\n\n// GetOauth2LoginChallenge returns the Oauth2LoginChallenge field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetOauth2LoginChallenge() string {\n\tif o == nil || IsNil(o.Oauth2LoginChallenge) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Oauth2LoginChallenge\n}\n\n// GetOauth2LoginChallengeOk returns a tuple with the Oauth2LoginChallenge field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetOauth2LoginChallengeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Oauth2LoginChallenge) {\n\t\treturn nil, false\n\t}\n\treturn o.Oauth2LoginChallenge, true\n}\n\n// HasOauth2LoginChallenge returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasOauth2LoginChallenge() bool {\n\tif o != nil && !IsNil(o.Oauth2LoginChallenge) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOauth2LoginChallenge gets a reference to the given string and assigns it to the Oauth2LoginChallenge field.\nfunc (o *LoginFlow) SetOauth2LoginChallenge(v string) {\n\to.Oauth2LoginChallenge = &v\n}\n\n// GetOauth2LoginRequest returns the Oauth2LoginRequest field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetOauth2LoginRequest() OAuth2LoginRequest {\n\tif o == nil || IsNil(o.Oauth2LoginRequest) {\n\t\tvar ret OAuth2LoginRequest\n\t\treturn ret\n\t}\n\treturn *o.Oauth2LoginRequest\n}\n\n// GetOauth2LoginRequestOk returns a tuple with the Oauth2LoginRequest field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetOauth2LoginRequestOk() (*OAuth2LoginRequest, bool) {\n\tif o == nil || IsNil(o.Oauth2LoginRequest) {\n\t\treturn nil, false\n\t}\n\treturn o.Oauth2LoginRequest, true\n}\n\n// HasOauth2LoginRequest returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasOauth2LoginRequest() bool {\n\tif o != nil && !IsNil(o.Oauth2LoginRequest) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOauth2LoginRequest gets a reference to the given OAuth2LoginRequest and assigns it to the Oauth2LoginRequest field.\nfunc (o *LoginFlow) SetOauth2LoginRequest(v OAuth2LoginRequest) {\n\to.Oauth2LoginRequest = &v\n}\n\n// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *LoginFlow) GetOrganizationId() string {\n\tif o == nil || IsNil(o.OrganizationId.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OrganizationId.Get()\n}\n\n// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *LoginFlow) GetOrganizationIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.OrganizationId.Get(), o.OrganizationId.IsSet()\n}\n\n// HasOrganizationId returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasOrganizationId() bool {\n\tif o != nil && o.OrganizationId.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganizationId gets a reference to the given NullableString and assigns it to the OrganizationId field.\nfunc (o *LoginFlow) SetOrganizationId(v string) {\n\to.OrganizationId.Set(&v)\n}\n\n// SetOrganizationIdNil sets the value for OrganizationId to be an explicit nil\nfunc (o *LoginFlow) SetOrganizationIdNil() {\n\to.OrganizationId.Set(nil)\n}\n\n// UnsetOrganizationId ensures that no value is present for OrganizationId, not even an explicit nil\nfunc (o *LoginFlow) UnsetOrganizationId() {\n\to.OrganizationId.Unset()\n}\n\n// GetRefresh returns the Refresh field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetRefresh() bool {\n\tif o == nil || IsNil(o.Refresh) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.Refresh\n}\n\n// GetRefreshOk returns a tuple with the Refresh field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetRefreshOk() (*bool, bool) {\n\tif o == nil || IsNil(o.Refresh) {\n\t\treturn nil, false\n\t}\n\treturn o.Refresh, true\n}\n\n// HasRefresh returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasRefresh() bool {\n\tif o != nil && !IsNil(o.Refresh) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRefresh gets a reference to the given bool and assigns it to the Refresh field.\nfunc (o *LoginFlow) SetRefresh(v bool) {\n\to.Refresh = &v\n}\n\n// GetRequestUrl returns the RequestUrl field value\nfunc (o *LoginFlow) GetRequestUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RequestUrl, true\n}\n\n// SetRequestUrl sets field value\nfunc (o *LoginFlow) SetRequestUrl(v string) {\n\to.RequestUrl = v\n}\n\n// GetRequestedAal returns the RequestedAal field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetRequestedAal() AuthenticatorAssuranceLevel {\n\tif o == nil || IsNil(o.RequestedAal) {\n\t\tvar ret AuthenticatorAssuranceLevel\n\t\treturn ret\n\t}\n\treturn *o.RequestedAal\n}\n\n// GetRequestedAalOk returns a tuple with the RequestedAal field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetRequestedAalOk() (*AuthenticatorAssuranceLevel, bool) {\n\tif o == nil || IsNil(o.RequestedAal) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestedAal, true\n}\n\n// HasRequestedAal returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasRequestedAal() bool {\n\tif o != nil && !IsNil(o.RequestedAal) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestedAal gets a reference to the given AuthenticatorAssuranceLevel and assigns it to the RequestedAal field.\nfunc (o *LoginFlow) SetRequestedAal(v AuthenticatorAssuranceLevel) {\n\to.RequestedAal = &v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *LoginFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetSessionTokenExchangeCode returns the SessionTokenExchangeCode field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetSessionTokenExchangeCode() string {\n\tif o == nil || IsNil(o.SessionTokenExchangeCode) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionTokenExchangeCode\n}\n\n// GetSessionTokenExchangeCodeOk returns a tuple with the SessionTokenExchangeCode field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetSessionTokenExchangeCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionTokenExchangeCode) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionTokenExchangeCode, true\n}\n\n// HasSessionTokenExchangeCode returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasSessionTokenExchangeCode() bool {\n\tif o != nil && !IsNil(o.SessionTokenExchangeCode) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionTokenExchangeCode gets a reference to the given string and assigns it to the SessionTokenExchangeCode field.\nfunc (o *LoginFlow) SetSessionTokenExchangeCode(v string) {\n\to.SessionTokenExchangeCode = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *LoginFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *LoginFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *LoginFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *LoginFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *LoginFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *LoginFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *LoginFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *LoginFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *LoginFlow) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *LoginFlow) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *LoginFlow) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *LoginFlow) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\nfunc (o LoginFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o LoginFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.IdentitySchema) {\n\t\ttoSerialize[\"identity_schema\"] = o.IdentitySchema\n\t}\n\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\tif !IsNil(o.Oauth2LoginChallenge) {\n\t\ttoSerialize[\"oauth2_login_challenge\"] = o.Oauth2LoginChallenge\n\t}\n\tif !IsNil(o.Oauth2LoginRequest) {\n\t\ttoSerialize[\"oauth2_login_request\"] = o.Oauth2LoginRequest\n\t}\n\tif o.OrganizationId.IsSet() {\n\t\ttoSerialize[\"organization_id\"] = o.OrganizationId.Get()\n\t}\n\tif !IsNil(o.Refresh) {\n\t\ttoSerialize[\"refresh\"] = o.Refresh\n\t}\n\ttoSerialize[\"request_url\"] = o.RequestUrl\n\tif !IsNil(o.RequestedAal) {\n\t\ttoSerialize[\"requested_aal\"] = o.RequestedAal\n\t}\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif !IsNil(o.SessionTokenExchangeCode) {\n\t\ttoSerialize[\"session_token_exchange_code\"] = o.SessionTokenExchangeCode\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *LoginFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"expires_at\",\n\t\t\"id\",\n\t\t\"issued_at\",\n\t\t\"request_url\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarLoginFlow := _LoginFlow{}\n\n\terr = json.Unmarshal(data, &varLoginFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = LoginFlow(varLoginFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"identity_schema\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"oauth2_login_challenge\")\n\t\tdelete(additionalProperties, \"oauth2_login_request\")\n\t\tdelete(additionalProperties, \"organization_id\")\n\t\tdelete(additionalProperties, \"refresh\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"requested_aal\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"session_token_exchange_code\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableLoginFlow struct {\n\tvalue *LoginFlow\n\tisSet bool\n}\n\nfunc (v NullableLoginFlow) Get() *LoginFlow {\n\treturn v.value\n}\n\nfunc (v *NullableLoginFlow) Set(val *LoginFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableLoginFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableLoginFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableLoginFlow(val *LoginFlow) *NullableLoginFlow {\n\treturn &NullableLoginFlow{value: val, isSet: true}\n}\n\nfunc (v NullableLoginFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableLoginFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_login_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// LoginFlowState The experimental state represents the state of a login flow. This field is EXPERIMENTAL and subject to change!\ntype LoginFlowState string\n\n// List of loginFlowState\nconst (\n\tLOGINFLOWSTATE_CHOOSE_METHOD    LoginFlowState = \"choose_method\"\n\tLOGINFLOWSTATE_SENT_EMAIL       LoginFlowState = \"sent_email\"\n\tLOGINFLOWSTATE_PASSED_CHALLENGE LoginFlowState = \"passed_challenge\"\n)\n\n// All allowed values of LoginFlowState enum\nvar AllowedLoginFlowStateEnumValues = []LoginFlowState{\n\t\"choose_method\",\n\t\"sent_email\",\n\t\"passed_challenge\",\n}\n\nfunc (v *LoginFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := LoginFlowState(value)\n\tfor _, existing := range AllowedLoginFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid LoginFlowState\", value)\n}\n\n// NewLoginFlowStateFromValue returns a pointer to a valid LoginFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewLoginFlowStateFromValue(v string) (*LoginFlowState, error) {\n\tev := LoginFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for LoginFlowState: valid values are %v\", v, AllowedLoginFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v LoginFlowState) IsValid() bool {\n\tfor _, existing := range AllowedLoginFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to loginFlowState value\nfunc (v LoginFlowState) Ptr() *LoginFlowState {\n\treturn &v\n}\n\ntype NullableLoginFlowState struct {\n\tvalue *LoginFlowState\n\tisSet bool\n}\n\nfunc (v NullableLoginFlowState) Get() *LoginFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableLoginFlowState) Set(val *LoginFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableLoginFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableLoginFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableLoginFlowState(val *LoginFlowState) *NullableLoginFlowState {\n\treturn &NullableLoginFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableLoginFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableLoginFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_logout_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the LogoutFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &LogoutFlow{}\n\n// LogoutFlow Logout Flow\ntype LogoutFlow struct {\n\t// LogoutToken can be used to perform logout using AJAX.\n\tLogoutToken string `json:\"logout_token\"`\n\t// LogoutURL can be opened in a browser to sign the user out.  format: uri\n\tLogoutUrl            string `json:\"logout_url\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _LogoutFlow LogoutFlow\n\n// NewLogoutFlow instantiates a new LogoutFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewLogoutFlow(logoutToken string, logoutUrl string) *LogoutFlow {\n\tthis := LogoutFlow{}\n\tthis.LogoutToken = logoutToken\n\tthis.LogoutUrl = logoutUrl\n\treturn &this\n}\n\n// NewLogoutFlowWithDefaults instantiates a new LogoutFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewLogoutFlowWithDefaults() *LogoutFlow {\n\tthis := LogoutFlow{}\n\treturn &this\n}\n\n// GetLogoutToken returns the LogoutToken field value\nfunc (o *LogoutFlow) GetLogoutToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.LogoutToken\n}\n\n// GetLogoutTokenOk returns a tuple with the LogoutToken field value\n// and a boolean to check if the value has been set.\nfunc (o *LogoutFlow) GetLogoutTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.LogoutToken, true\n}\n\n// SetLogoutToken sets field value\nfunc (o *LogoutFlow) SetLogoutToken(v string) {\n\to.LogoutToken = v\n}\n\n// GetLogoutUrl returns the LogoutUrl field value\nfunc (o *LogoutFlow) GetLogoutUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.LogoutUrl\n}\n\n// GetLogoutUrlOk returns a tuple with the LogoutUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *LogoutFlow) GetLogoutUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.LogoutUrl, true\n}\n\n// SetLogoutUrl sets field value\nfunc (o *LogoutFlow) SetLogoutUrl(v string) {\n\to.LogoutUrl = v\n}\n\nfunc (o LogoutFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o LogoutFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"logout_token\"] = o.LogoutToken\n\ttoSerialize[\"logout_url\"] = o.LogoutUrl\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *LogoutFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"logout_token\",\n\t\t\"logout_url\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarLogoutFlow := _LogoutFlow{}\n\n\terr = json.Unmarshal(data, &varLogoutFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = LogoutFlow(varLogoutFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"logout_token\")\n\t\tdelete(additionalProperties, \"logout_url\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableLogoutFlow struct {\n\tvalue *LogoutFlow\n\tisSet bool\n}\n\nfunc (v NullableLogoutFlow) Get() *LogoutFlow {\n\treturn v.value\n}\n\nfunc (v *NullableLogoutFlow) Set(val *LogoutFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableLogoutFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableLogoutFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableLogoutFlow(val *LogoutFlow) *NullableLogoutFlow {\n\treturn &NullableLogoutFlow{value: val, isSet: true}\n}\n\nfunc (v NullableLogoutFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableLogoutFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_message.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the Message type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Message{}\n\n// Message struct for Message\ntype Message struct {\n\tBody    string  `json:\"body\"`\n\tChannel *string `json:\"channel,omitempty\"`\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"created_at\"`\n\t// Dispatches store information about the attempts of delivering a message May contain an error if any happened, or just the `success` state.\n\tDispatches []MessageDispatch    `json:\"dispatches,omitempty\"`\n\tId         string               `json:\"id\"`\n\tRecipient  string               `json:\"recipient\"`\n\tSendCount  int64                `json:\"send_count\"`\n\tStatus     CourierMessageStatus `json:\"status\"`\n\tSubject    string               `json:\"subject\"`\n\t//  recovery_invalid TypeRecoveryInvalid recovery_valid TypeRecoveryValid recovery_code_invalid TypeRecoveryCodeInvalid recovery_code_valid TypeRecoveryCodeValid verification_invalid TypeVerificationInvalid verification_valid TypeVerificationValid verification_code_invalid TypeVerificationCodeInvalid verification_code_valid TypeVerificationCodeValid stub TypeTestStub login_code_valid TypeLoginCodeValid registration_code_valid TypeRegistrationCodeValid\n\tTemplateType string             `json:\"template_type\"`\n\tType         CourierMessageType `json:\"type\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            time.Time `json:\"updated_at\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Message Message\n\n// NewMessage instantiates a new Message object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewMessage(body string, createdAt time.Time, id string, recipient string, sendCount int64, status CourierMessageStatus, subject string, templateType string, type_ CourierMessageType, updatedAt time.Time) *Message {\n\tthis := Message{}\n\tthis.Body = body\n\tthis.CreatedAt = createdAt\n\tthis.Id = id\n\tthis.Recipient = recipient\n\tthis.SendCount = sendCount\n\tthis.Status = status\n\tthis.Subject = subject\n\tthis.TemplateType = templateType\n\tthis.Type = type_\n\tthis.UpdatedAt = updatedAt\n\treturn &this\n}\n\n// NewMessageWithDefaults instantiates a new Message object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewMessageWithDefaults() *Message {\n\tthis := Message{}\n\treturn &this\n}\n\n// GetBody returns the Body field value\nfunc (o *Message) GetBody() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Body\n}\n\n// GetBodyOk returns a tuple with the Body field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetBodyOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Body, true\n}\n\n// SetBody sets field value\nfunc (o *Message) SetBody(v string) {\n\to.Body = v\n}\n\n// GetChannel returns the Channel field value if set, zero value otherwise.\nfunc (o *Message) GetChannel() string {\n\tif o == nil || IsNil(o.Channel) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Channel\n}\n\n// GetChannelOk returns a tuple with the Channel field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetChannelOk() (*string, bool) {\n\tif o == nil || IsNil(o.Channel) {\n\t\treturn nil, false\n\t}\n\treturn o.Channel, true\n}\n\n// HasChannel returns a boolean if a field has been set.\nfunc (o *Message) HasChannel() bool {\n\tif o != nil && !IsNil(o.Channel) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetChannel gets a reference to the given string and assigns it to the Channel field.\nfunc (o *Message) SetChannel(v string) {\n\to.Channel = &v\n}\n\n// GetCreatedAt returns the CreatedAt field value\nfunc (o *Message) GetCreatedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.CreatedAt, true\n}\n\n// SetCreatedAt sets field value\nfunc (o *Message) SetCreatedAt(v time.Time) {\n\to.CreatedAt = v\n}\n\n// GetDispatches returns the Dispatches field value if set, zero value otherwise.\nfunc (o *Message) GetDispatches() []MessageDispatch {\n\tif o == nil || IsNil(o.Dispatches) {\n\t\tvar ret []MessageDispatch\n\t\treturn ret\n\t}\n\treturn o.Dispatches\n}\n\n// GetDispatchesOk returns a tuple with the Dispatches field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetDispatchesOk() ([]MessageDispatch, bool) {\n\tif o == nil || IsNil(o.Dispatches) {\n\t\treturn nil, false\n\t}\n\treturn o.Dispatches, true\n}\n\n// HasDispatches returns a boolean if a field has been set.\nfunc (o *Message) HasDispatches() bool {\n\tif o != nil && !IsNil(o.Dispatches) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDispatches gets a reference to the given []MessageDispatch and assigns it to the Dispatches field.\nfunc (o *Message) SetDispatches(v []MessageDispatch) {\n\to.Dispatches = v\n}\n\n// GetId returns the Id field value\nfunc (o *Message) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *Message) SetId(v string) {\n\to.Id = v\n}\n\n// GetRecipient returns the Recipient field value\nfunc (o *Message) GetRecipient() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Recipient\n}\n\n// GetRecipientOk returns a tuple with the Recipient field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetRecipientOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Recipient, true\n}\n\n// SetRecipient sets field value\nfunc (o *Message) SetRecipient(v string) {\n\to.Recipient = v\n}\n\n// GetSendCount returns the SendCount field value\nfunc (o *Message) GetSendCount() int64 {\n\tif o == nil {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\n\treturn o.SendCount\n}\n\n// GetSendCountOk returns a tuple with the SendCount field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetSendCountOk() (*int64, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SendCount, true\n}\n\n// SetSendCount sets field value\nfunc (o *Message) SetSendCount(v int64) {\n\to.SendCount = v\n}\n\n// GetStatus returns the Status field value\nfunc (o *Message) GetStatus() CourierMessageStatus {\n\tif o == nil {\n\t\tvar ret CourierMessageStatus\n\t\treturn ret\n\t}\n\n\treturn o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetStatusOk() (*CourierMessageStatus, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Status, true\n}\n\n// SetStatus sets field value\nfunc (o *Message) SetStatus(v CourierMessageStatus) {\n\to.Status = v\n}\n\n// GetSubject returns the Subject field value\nfunc (o *Message) GetSubject() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetSubjectOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Subject, true\n}\n\n// SetSubject sets field value\nfunc (o *Message) SetSubject(v string) {\n\to.Subject = v\n}\n\n// GetTemplateType returns the TemplateType field value\nfunc (o *Message) GetTemplateType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.TemplateType\n}\n\n// GetTemplateTypeOk returns a tuple with the TemplateType field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetTemplateTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.TemplateType, true\n}\n\n// SetTemplateType sets field value\nfunc (o *Message) SetTemplateType(v string) {\n\to.TemplateType = v\n}\n\n// GetType returns the Type field value\nfunc (o *Message) GetType() CourierMessageType {\n\tif o == nil {\n\t\tvar ret CourierMessageType\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetTypeOk() (*CourierMessageType, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *Message) SetType(v CourierMessageType) {\n\to.Type = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value\nfunc (o *Message) GetUpdatedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *Message) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.UpdatedAt, true\n}\n\n// SetUpdatedAt sets field value\nfunc (o *Message) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = v\n}\n\nfunc (o Message) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Message) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"body\"] = o.Body\n\tif !IsNil(o.Channel) {\n\t\ttoSerialize[\"channel\"] = o.Channel\n\t}\n\ttoSerialize[\"created_at\"] = o.CreatedAt\n\tif !IsNil(o.Dispatches) {\n\t\ttoSerialize[\"dispatches\"] = o.Dispatches\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"recipient\"] = o.Recipient\n\ttoSerialize[\"send_count\"] = o.SendCount\n\ttoSerialize[\"status\"] = o.Status\n\ttoSerialize[\"subject\"] = o.Subject\n\ttoSerialize[\"template_type\"] = o.TemplateType\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Message) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"body\",\n\t\t\"created_at\",\n\t\t\"id\",\n\t\t\"recipient\",\n\t\t\"send_count\",\n\t\t\"status\",\n\t\t\"subject\",\n\t\t\"template_type\",\n\t\t\"type\",\n\t\t\"updated_at\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarMessage := _Message{}\n\n\terr = json.Unmarshal(data, &varMessage)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Message(varMessage)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"body\")\n\t\tdelete(additionalProperties, \"channel\")\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"dispatches\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"recipient\")\n\t\tdelete(additionalProperties, \"send_count\")\n\t\tdelete(additionalProperties, \"status\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\tdelete(additionalProperties, \"template_type\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableMessage struct {\n\tvalue *Message\n\tisSet bool\n}\n\nfunc (v NullableMessage) Get() *Message {\n\treturn v.value\n}\n\nfunc (v *NullableMessage) Set(val *Message) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableMessage) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableMessage) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableMessage(val *Message) *NullableMessage {\n\treturn &NullableMessage{value: val, isSet: true}\n}\n\nfunc (v NullableMessage) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableMessage) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_message_dispatch.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the MessageDispatch type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &MessageDispatch{}\n\n// MessageDispatch MessageDispatch represents an attempt of sending a courier message It contains the status of the attempt (failed or successful) and the error if any occured\ntype MessageDispatch struct {\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time              `json:\"created_at\"`\n\tError     map[string]interface{} `json:\"error,omitempty\"`\n\t// The ID of this message dispatch\n\tId string `json:\"id\"`\n\t// The ID of the message being dispatched\n\tMessageId string `json:\"message_id\"`\n\t// The status of this dispatch Either \\\"failed\\\" or \\\"success\\\" failed CourierMessageDispatchStatusFailed success CourierMessageDispatchStatusSuccess\n\tStatus string `json:\"status\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            time.Time `json:\"updated_at\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _MessageDispatch MessageDispatch\n\n// NewMessageDispatch instantiates a new MessageDispatch object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewMessageDispatch(createdAt time.Time, id string, messageId string, status string, updatedAt time.Time) *MessageDispatch {\n\tthis := MessageDispatch{}\n\tthis.CreatedAt = createdAt\n\tthis.Id = id\n\tthis.MessageId = messageId\n\tthis.Status = status\n\tthis.UpdatedAt = updatedAt\n\treturn &this\n}\n\n// NewMessageDispatchWithDefaults instantiates a new MessageDispatch object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewMessageDispatchWithDefaults() *MessageDispatch {\n\tthis := MessageDispatch{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value\nfunc (o *MessageDispatch) GetCreatedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.CreatedAt, true\n}\n\n// SetCreatedAt sets field value\nfunc (o *MessageDispatch) SetCreatedAt(v time.Time) {\n\to.CreatedAt = v\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *MessageDispatch) GetError() map[string]interface{} {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetErrorOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *MessageDispatch) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given map[string]interface{} and assigns it to the Error field.\nfunc (o *MessageDispatch) SetError(v map[string]interface{}) {\n\to.Error = v\n}\n\n// GetId returns the Id field value\nfunc (o *MessageDispatch) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *MessageDispatch) SetId(v string) {\n\to.Id = v\n}\n\n// GetMessageId returns the MessageId field value\nfunc (o *MessageDispatch) GetMessageId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.MessageId\n}\n\n// GetMessageIdOk returns a tuple with the MessageId field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetMessageIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.MessageId, true\n}\n\n// SetMessageId sets field value\nfunc (o *MessageDispatch) SetMessageId(v string) {\n\to.MessageId = v\n}\n\n// GetStatus returns the Status field value\nfunc (o *MessageDispatch) GetStatus() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetStatusOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Status, true\n}\n\n// SetStatus sets field value\nfunc (o *MessageDispatch) SetStatus(v string) {\n\to.Status = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value\nfunc (o *MessageDispatch) GetUpdatedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *MessageDispatch) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.UpdatedAt, true\n}\n\n// SetUpdatedAt sets field value\nfunc (o *MessageDispatch) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = v\n}\n\nfunc (o MessageDispatch) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o MessageDispatch) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"created_at\"] = o.CreatedAt\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"message_id\"] = o.MessageId\n\ttoSerialize[\"status\"] = o.Status\n\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *MessageDispatch) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"created_at\",\n\t\t\"id\",\n\t\t\"message_id\",\n\t\t\"status\",\n\t\t\"updated_at\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarMessageDispatch := _MessageDispatch{}\n\n\terr = json.Unmarshal(data, &varMessageDispatch)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = MessageDispatch(varMessageDispatch)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"message_id\")\n\t\tdelete(additionalProperties, \"status\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableMessageDispatch struct {\n\tvalue *MessageDispatch\n\tisSet bool\n}\n\nfunc (v NullableMessageDispatch) Get() *MessageDispatch {\n\treturn v.value\n}\n\nfunc (v *NullableMessageDispatch) Set(val *MessageDispatch) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableMessageDispatch) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableMessageDispatch) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableMessageDispatch(val *MessageDispatch) *NullableMessageDispatch {\n\treturn &NullableMessageDispatch{value: val, isSet: true}\n}\n\nfunc (v NullableMessageDispatch) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableMessageDispatch) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_needs_privileged_session_error.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the NeedsPrivilegedSessionError type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &NeedsPrivilegedSessionError{}\n\n// NeedsPrivilegedSessionError struct for NeedsPrivilegedSessionError\ntype NeedsPrivilegedSessionError struct {\n\tError *GenericError `json:\"error,omitempty\"`\n\t// Points to where to redirect the user to next.\n\tRedirectBrowserTo    string `json:\"redirect_browser_to\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _NeedsPrivilegedSessionError NeedsPrivilegedSessionError\n\n// NewNeedsPrivilegedSessionError instantiates a new NeedsPrivilegedSessionError object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewNeedsPrivilegedSessionError(redirectBrowserTo string) *NeedsPrivilegedSessionError {\n\tthis := NeedsPrivilegedSessionError{}\n\tthis.RedirectBrowserTo = redirectBrowserTo\n\treturn &this\n}\n\n// NewNeedsPrivilegedSessionErrorWithDefaults instantiates a new NeedsPrivilegedSessionError object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewNeedsPrivilegedSessionErrorWithDefaults() *NeedsPrivilegedSessionError {\n\tthis := NeedsPrivilegedSessionError{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *NeedsPrivilegedSessionError) GetError() GenericError {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *NeedsPrivilegedSessionError) GetErrorOk() (*GenericError, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *NeedsPrivilegedSessionError) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given GenericError and assigns it to the Error field.\nfunc (o *NeedsPrivilegedSessionError) SetError(v GenericError) {\n\to.Error = &v\n}\n\n// GetRedirectBrowserTo returns the RedirectBrowserTo field value\nfunc (o *NeedsPrivilegedSessionError) GetRedirectBrowserTo() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RedirectBrowserTo\n}\n\n// GetRedirectBrowserToOk returns a tuple with the RedirectBrowserTo field value\n// and a boolean to check if the value has been set.\nfunc (o *NeedsPrivilegedSessionError) GetRedirectBrowserToOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RedirectBrowserTo, true\n}\n\n// SetRedirectBrowserTo sets field value\nfunc (o *NeedsPrivilegedSessionError) SetRedirectBrowserTo(v string) {\n\to.RedirectBrowserTo = v\n}\n\nfunc (o NeedsPrivilegedSessionError) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o NeedsPrivilegedSessionError) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\ttoSerialize[\"redirect_browser_to\"] = o.RedirectBrowserTo\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *NeedsPrivilegedSessionError) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"redirect_browser_to\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarNeedsPrivilegedSessionError := _NeedsPrivilegedSessionError{}\n\n\terr = json.Unmarshal(data, &varNeedsPrivilegedSessionError)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = NeedsPrivilegedSessionError(varNeedsPrivilegedSessionError)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"redirect_browser_to\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableNeedsPrivilegedSessionError struct {\n\tvalue *NeedsPrivilegedSessionError\n\tisSet bool\n}\n\nfunc (v NullableNeedsPrivilegedSessionError) Get() *NeedsPrivilegedSessionError {\n\treturn v.value\n}\n\nfunc (v *NullableNeedsPrivilegedSessionError) Set(val *NeedsPrivilegedSessionError) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableNeedsPrivilegedSessionError) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableNeedsPrivilegedSessionError) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableNeedsPrivilegedSessionError(val *NeedsPrivilegedSessionError) *NullableNeedsPrivilegedSessionError {\n\treturn &NullableNeedsPrivilegedSessionError{value: val, isSet: true}\n}\n\nfunc (v NullableNeedsPrivilegedSessionError) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableNeedsPrivilegedSessionError) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_o_auth2_client.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n)\n\n// checks if the OAuth2Client type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &OAuth2Client{}\n\n// OAuth2Client struct for OAuth2Client\ntype OAuth2Client struct {\n\t// OAuth 2.0 Access Token Strategy  AccessTokenStrategy is the strategy used to generate access tokens. Valid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens Setting the stragegy here overrides the global setting in `strategies.access_token`.\n\tAccessTokenStrategy *string  `json:\"access_token_strategy,omitempty\"`\n\tAllowedCorsOrigins  []string `json:\"allowed_cors_origins,omitempty\"`\n\tAudience            []string `json:\"audience,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tAuthorizationCodeGrantAccessTokenLifespan *string `json:\"authorization_code_grant_access_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tAuthorizationCodeGrantIdTokenLifespan *string `json:\"authorization_code_grant_id_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tAuthorizationCodeGrantRefreshTokenLifespan *string `json:\"authorization_code_grant_refresh_token_lifespan,omitempty\"`\n\t// OpenID Connect Back-Channel Logout Session Required  Boolean value specifying whether the RP requires that a sid (session ID) Claim be included in the Logout Token to identify the RP session with the OP when the backchannel_logout_uri is used. If omitted, the default value is false.\n\tBackchannelLogoutSessionRequired *bool `json:\"backchannel_logout_session_required,omitempty\"`\n\t// OpenID Connect Back-Channel Logout URI  RP URL that will cause the RP to log itself out when sent a Logout Token by the OP.\n\tBackchannelLogoutUri *string `json:\"backchannel_logout_uri,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tClientCredentialsGrantAccessTokenLifespan *string `json:\"client_credentials_grant_access_token_lifespan,omitempty\"`\n\t// OAuth 2.0 Client ID  The ID is immutable. If no ID is provided, a UUID4 will be generated.\n\tClientId *string `json:\"client_id,omitempty\"`\n\t// OAuth 2.0 Client Name  The human-readable name of the client to be presented to the end-user during authorization.\n\tClientName *string `json:\"client_name,omitempty\"`\n\t// OAuth 2.0 Client Secret  The secret will be included in the create request as cleartext, and then never again. The secret is kept in hashed format and is not recoverable once lost.\n\tClientSecret *string `json:\"client_secret,omitempty\"`\n\t// OAuth 2.0 Client Secret Expires At  The field is currently not supported and its value is always 0.\n\tClientSecretExpiresAt *int64 `json:\"client_secret_expires_at,omitempty\"`\n\t// OAuth 2.0 Client URI  ClientURI is a URL string of a web page providing information about the client. If present, the server SHOULD display this URL to the end-user in a clickable fashion.\n\tClientUri *string  `json:\"client_uri,omitempty\"`\n\tContacts  []string `json:\"contacts,omitempty\"`\n\t// OAuth 2.0 Client Creation Date  CreatedAt returns the timestamp of the client's creation.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// OpenID Connect Front-Channel Logout Session Required  Boolean value specifying whether the RP requires that iss (issuer) and sid (session ID) query parameters be included to identify the RP session with the OP when the frontchannel_logout_uri is used. If omitted, the default value is false.\n\tFrontchannelLogoutSessionRequired *bool `json:\"frontchannel_logout_session_required,omitempty\"`\n\t// OpenID Connect Front-Channel Logout URI  RP URL that will cause the RP to log itself out when rendered in an iframe by the OP. An iss (issuer) query parameter and a sid (session ID) query parameter MAY be included by the OP to enable the RP to validate the request and to determine which of the potentially multiple sessions is to be logged out; if either is included, both MUST be.\n\tFrontchannelLogoutUri *string  `json:\"frontchannel_logout_uri,omitempty\"`\n\tGrantTypes            []string `json:\"grant_types,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tImplicitGrantAccessTokenLifespan *string `json:\"implicit_grant_access_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tImplicitGrantIdTokenLifespan *string `json:\"implicit_grant_id_token_lifespan,omitempty\"`\n\t// OAuth 2.0 Client JSON Web Key Set  Client's JSON Web Key Set [JWK] document, passed by value. The semantics of the jwks parameter are the same as the jwks_uri parameter, other than that the JWK Set is passed by value, rather than by reference. This parameter is intended only to be used by Clients that, for some reason, are unable to use the jwks_uri parameter, for instance, by native applications that might not have a location to host the contents of the JWK Set. If a Client can use jwks_uri, it MUST NOT use jwks. One significant downside of jwks is that it does not enable key rotation (which jwks_uri does, as described in Section 10 of OpenID Connect Core 1.0 [OpenID.Core]). The jwks_uri and jwks parameters MUST NOT be used together.\n\tJwks interface{} `json:\"jwks,omitempty\"`\n\t// OAuth 2.0 Client JSON Web Key Set URL  URL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains the signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the Client's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing and encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both signatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.\n\tJwksUri *string `json:\"jwks_uri,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tJwtBearerGrantAccessTokenLifespan *string `json:\"jwt_bearer_grant_access_token_lifespan,omitempty\"`\n\t// OAuth 2.0 Client Logo URI  A URL string referencing the client's logo.\n\tLogoUri  *string     `json:\"logo_uri,omitempty\"`\n\tMetadata interface{} `json:\"metadata,omitempty\"`\n\t// OAuth 2.0 Client Owner  Owner is a string identifying the owner of the OAuth 2.0 Client.\n\tOwner *string `json:\"owner,omitempty\"`\n\t// OAuth 2.0 Client Policy URI  PolicyURI is a URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data.\n\tPolicyUri              *string  `json:\"policy_uri,omitempty\"`\n\tPostLogoutRedirectUris []string `json:\"post_logout_redirect_uris,omitempty\"`\n\tRedirectUris           []string `json:\"redirect_uris,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tRefreshTokenGrantAccessTokenLifespan *string `json:\"refresh_token_grant_access_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tRefreshTokenGrantIdTokenLifespan *string `json:\"refresh_token_grant_id_token_lifespan,omitempty\"`\n\t// Specify a time duration in milliseconds, seconds, minutes, hours.\n\tRefreshTokenGrantRefreshTokenLifespan *string `json:\"refresh_token_grant_refresh_token_lifespan,omitempty\"`\n\t// OpenID Connect Dynamic Client Registration Access Token  RegistrationAccessToken can be used to update, get, or delete the OAuth2 Client. It is sent when creating a client using Dynamic Client Registration.\n\tRegistrationAccessToken *string `json:\"registration_access_token,omitempty\"`\n\t// OpenID Connect Dynamic Client Registration URL  RegistrationClientURI is the URL used to update, get, or delete the OAuth2 Client.\n\tRegistrationClientUri *string `json:\"registration_client_uri,omitempty\"`\n\t// OpenID Connect Request Object Signing Algorithm  JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.\n\tRequestObjectSigningAlg *string  `json:\"request_object_signing_alg,omitempty\"`\n\tRequestUris             []string `json:\"request_uris,omitempty\"`\n\tResponseTypes           []string `json:\"response_types,omitempty\"`\n\t// OAuth 2.0 Client Scope  Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens.\n\tScope *string `json:\"scope,omitempty\"`\n\t// OpenID Connect Sector Identifier URI  URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values.\n\tSectorIdentifierUri *string `json:\"sector_identifier_uri,omitempty\"`\n\t// SkipConsent skips the consent screen for this client. This field can only be set from the admin API.\n\tSkipConsent *bool `json:\"skip_consent,omitempty\"`\n\t// SkipLogoutConsent skips the logout consent screen for this client. This field can only be set from the admin API.\n\tSkipLogoutConsent *bool `json:\"skip_logout_consent,omitempty\"`\n\t// OpenID Connect Subject Type  The `subject_types_supported` Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`.\n\tSubjectType *string `json:\"subject_type,omitempty\"`\n\t// OAuth 2.0 Token Endpoint Authentication Method  Requested Client Authentication method for the Token Endpoint. The options are:  `client_secret_basic`: (default) Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` encoded in the HTTP Authorization header. `client_secret_post`: Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` in the HTTP body. `private_key_jwt`: Use JSON Web Tokens to authenticate the client. `none`: Used for public clients (native apps, mobile apps) which can not have secrets.\n\tTokenEndpointAuthMethod *string `json:\"token_endpoint_auth_method,omitempty\"`\n\t// OAuth 2.0 Token Endpoint Signing Algorithm  Requested Client Authentication signing algorithm for the Token Endpoint.\n\tTokenEndpointAuthSigningAlg *string `json:\"token_endpoint_auth_signing_alg,omitempty\"`\n\t// OAuth 2.0 Client Terms of Service URI  A URL string pointing to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client.\n\tTosUri *string `json:\"tos_uri,omitempty\"`\n\t// OAuth 2.0 Client Last Update Date  UpdatedAt returns the timestamp of the last update.\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\"`\n\t// OpenID Connect Request Userinfo Signed Response Algorithm  JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type.\n\tUserinfoSignedResponseAlg *string `json:\"userinfo_signed_response_alg,omitempty\"`\n\tAdditionalProperties      map[string]interface{}\n}\n\ntype _OAuth2Client OAuth2Client\n\n// NewOAuth2Client instantiates a new OAuth2Client object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewOAuth2Client() *OAuth2Client {\n\tthis := OAuth2Client{}\n\treturn &this\n}\n\n// NewOAuth2ClientWithDefaults instantiates a new OAuth2Client object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewOAuth2ClientWithDefaults() *OAuth2Client {\n\tthis := OAuth2Client{}\n\treturn &this\n}\n\n// GetAccessTokenStrategy returns the AccessTokenStrategy field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAccessTokenStrategy() string {\n\tif o == nil || IsNil(o.AccessTokenStrategy) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.AccessTokenStrategy\n}\n\n// GetAccessTokenStrategyOk returns a tuple with the AccessTokenStrategy field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAccessTokenStrategyOk() (*string, bool) {\n\tif o == nil || IsNil(o.AccessTokenStrategy) {\n\t\treturn nil, false\n\t}\n\treturn o.AccessTokenStrategy, true\n}\n\n// HasAccessTokenStrategy returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAccessTokenStrategy() bool {\n\tif o != nil && !IsNil(o.AccessTokenStrategy) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAccessTokenStrategy gets a reference to the given string and assigns it to the AccessTokenStrategy field.\nfunc (o *OAuth2Client) SetAccessTokenStrategy(v string) {\n\to.AccessTokenStrategy = &v\n}\n\n// GetAllowedCorsOrigins returns the AllowedCorsOrigins field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAllowedCorsOrigins() []string {\n\tif o == nil || IsNil(o.AllowedCorsOrigins) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.AllowedCorsOrigins\n}\n\n// GetAllowedCorsOriginsOk returns a tuple with the AllowedCorsOrigins field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAllowedCorsOriginsOk() ([]string, bool) {\n\tif o == nil || IsNil(o.AllowedCorsOrigins) {\n\t\treturn nil, false\n\t}\n\treturn o.AllowedCorsOrigins, true\n}\n\n// HasAllowedCorsOrigins returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAllowedCorsOrigins() bool {\n\tif o != nil && !IsNil(o.AllowedCorsOrigins) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAllowedCorsOrigins gets a reference to the given []string and assigns it to the AllowedCorsOrigins field.\nfunc (o *OAuth2Client) SetAllowedCorsOrigins(v []string) {\n\to.AllowedCorsOrigins = v\n}\n\n// GetAudience returns the Audience field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAudience() []string {\n\tif o == nil || IsNil(o.Audience) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.Audience\n}\n\n// GetAudienceOk returns a tuple with the Audience field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAudienceOk() ([]string, bool) {\n\tif o == nil || IsNil(o.Audience) {\n\t\treturn nil, false\n\t}\n\treturn o.Audience, true\n}\n\n// HasAudience returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAudience() bool {\n\tif o != nil && !IsNil(o.Audience) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAudience gets a reference to the given []string and assigns it to the Audience field.\nfunc (o *OAuth2Client) SetAudience(v []string) {\n\to.Audience = v\n}\n\n// GetAuthorizationCodeGrantAccessTokenLifespan returns the AuthorizationCodeGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.AuthorizationCodeGrantAccessTokenLifespan\n}\n\n// GetAuthorizationCodeGrantAccessTokenLifespanOk returns a tuple with the AuthorizationCodeGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthorizationCodeGrantAccessTokenLifespan, true\n}\n\n// HasAuthorizationCodeGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAuthorizationCodeGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.AuthorizationCodeGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthorizationCodeGrantAccessTokenLifespan gets a reference to the given string and assigns it to the AuthorizationCodeGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetAuthorizationCodeGrantAccessTokenLifespan(v string) {\n\to.AuthorizationCodeGrantAccessTokenLifespan = &v\n}\n\n// GetAuthorizationCodeGrantIdTokenLifespan returns the AuthorizationCodeGrantIdTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantIdTokenLifespan() string {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantIdTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.AuthorizationCodeGrantIdTokenLifespan\n}\n\n// GetAuthorizationCodeGrantIdTokenLifespanOk returns a tuple with the AuthorizationCodeGrantIdTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantIdTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantIdTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthorizationCodeGrantIdTokenLifespan, true\n}\n\n// HasAuthorizationCodeGrantIdTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAuthorizationCodeGrantIdTokenLifespan() bool {\n\tif o != nil && !IsNil(o.AuthorizationCodeGrantIdTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthorizationCodeGrantIdTokenLifespan gets a reference to the given string and assigns it to the AuthorizationCodeGrantIdTokenLifespan field.\nfunc (o *OAuth2Client) SetAuthorizationCodeGrantIdTokenLifespan(v string) {\n\to.AuthorizationCodeGrantIdTokenLifespan = &v\n}\n\n// GetAuthorizationCodeGrantRefreshTokenLifespan returns the AuthorizationCodeGrantRefreshTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantRefreshTokenLifespan() string {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantRefreshTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.AuthorizationCodeGrantRefreshTokenLifespan\n}\n\n// GetAuthorizationCodeGrantRefreshTokenLifespanOk returns a tuple with the AuthorizationCodeGrantRefreshTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetAuthorizationCodeGrantRefreshTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.AuthorizationCodeGrantRefreshTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthorizationCodeGrantRefreshTokenLifespan, true\n}\n\n// HasAuthorizationCodeGrantRefreshTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasAuthorizationCodeGrantRefreshTokenLifespan() bool {\n\tif o != nil && !IsNil(o.AuthorizationCodeGrantRefreshTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthorizationCodeGrantRefreshTokenLifespan gets a reference to the given string and assigns it to the AuthorizationCodeGrantRefreshTokenLifespan field.\nfunc (o *OAuth2Client) SetAuthorizationCodeGrantRefreshTokenLifespan(v string) {\n\to.AuthorizationCodeGrantRefreshTokenLifespan = &v\n}\n\n// GetBackchannelLogoutSessionRequired returns the BackchannelLogoutSessionRequired field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetBackchannelLogoutSessionRequired() bool {\n\tif o == nil || IsNil(o.BackchannelLogoutSessionRequired) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.BackchannelLogoutSessionRequired\n}\n\n// GetBackchannelLogoutSessionRequiredOk returns a tuple with the BackchannelLogoutSessionRequired field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetBackchannelLogoutSessionRequiredOk() (*bool, bool) {\n\tif o == nil || IsNil(o.BackchannelLogoutSessionRequired) {\n\t\treturn nil, false\n\t}\n\treturn o.BackchannelLogoutSessionRequired, true\n}\n\n// HasBackchannelLogoutSessionRequired returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasBackchannelLogoutSessionRequired() bool {\n\tif o != nil && !IsNil(o.BackchannelLogoutSessionRequired) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetBackchannelLogoutSessionRequired gets a reference to the given bool and assigns it to the BackchannelLogoutSessionRequired field.\nfunc (o *OAuth2Client) SetBackchannelLogoutSessionRequired(v bool) {\n\to.BackchannelLogoutSessionRequired = &v\n}\n\n// GetBackchannelLogoutUri returns the BackchannelLogoutUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetBackchannelLogoutUri() string {\n\tif o == nil || IsNil(o.BackchannelLogoutUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.BackchannelLogoutUri\n}\n\n// GetBackchannelLogoutUriOk returns a tuple with the BackchannelLogoutUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetBackchannelLogoutUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.BackchannelLogoutUri) {\n\t\treturn nil, false\n\t}\n\treturn o.BackchannelLogoutUri, true\n}\n\n// HasBackchannelLogoutUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasBackchannelLogoutUri() bool {\n\tif o != nil && !IsNil(o.BackchannelLogoutUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetBackchannelLogoutUri gets a reference to the given string and assigns it to the BackchannelLogoutUri field.\nfunc (o *OAuth2Client) SetBackchannelLogoutUri(v string) {\n\to.BackchannelLogoutUri = &v\n}\n\n// GetClientCredentialsGrantAccessTokenLifespan returns the ClientCredentialsGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientCredentialsGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.ClientCredentialsGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientCredentialsGrantAccessTokenLifespan\n}\n\n// GetClientCredentialsGrantAccessTokenLifespanOk returns a tuple with the ClientCredentialsGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientCredentialsGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientCredentialsGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientCredentialsGrantAccessTokenLifespan, true\n}\n\n// HasClientCredentialsGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientCredentialsGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.ClientCredentialsGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientCredentialsGrantAccessTokenLifespan gets a reference to the given string and assigns it to the ClientCredentialsGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetClientCredentialsGrantAccessTokenLifespan(v string) {\n\to.ClientCredentialsGrantAccessTokenLifespan = &v\n}\n\n// GetClientId returns the ClientId field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientId() string {\n\tif o == nil || IsNil(o.ClientId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientId\n}\n\n// GetClientIdOk returns a tuple with the ClientId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientId) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientId, true\n}\n\n// HasClientId returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientId() bool {\n\tif o != nil && !IsNil(o.ClientId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientId gets a reference to the given string and assigns it to the ClientId field.\nfunc (o *OAuth2Client) SetClientId(v string) {\n\to.ClientId = &v\n}\n\n// GetClientName returns the ClientName field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientName() string {\n\tif o == nil || IsNil(o.ClientName) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientName\n}\n\n// GetClientNameOk returns a tuple with the ClientName field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientNameOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientName) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientName, true\n}\n\n// HasClientName returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientName() bool {\n\tif o != nil && !IsNil(o.ClientName) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientName gets a reference to the given string and assigns it to the ClientName field.\nfunc (o *OAuth2Client) SetClientName(v string) {\n\to.ClientName = &v\n}\n\n// GetClientSecret returns the ClientSecret field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientSecret() string {\n\tif o == nil || IsNil(o.ClientSecret) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientSecret\n}\n\n// GetClientSecretOk returns a tuple with the ClientSecret field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientSecretOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientSecret) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientSecret, true\n}\n\n// HasClientSecret returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientSecret() bool {\n\tif o != nil && !IsNil(o.ClientSecret) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientSecret gets a reference to the given string and assigns it to the ClientSecret field.\nfunc (o *OAuth2Client) SetClientSecret(v string) {\n\to.ClientSecret = &v\n}\n\n// GetClientSecretExpiresAt returns the ClientSecretExpiresAt field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientSecretExpiresAt() int64 {\n\tif o == nil || IsNil(o.ClientSecretExpiresAt) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.ClientSecretExpiresAt\n}\n\n// GetClientSecretExpiresAtOk returns a tuple with the ClientSecretExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientSecretExpiresAtOk() (*int64, bool) {\n\tif o == nil || IsNil(o.ClientSecretExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientSecretExpiresAt, true\n}\n\n// HasClientSecretExpiresAt returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientSecretExpiresAt() bool {\n\tif o != nil && !IsNil(o.ClientSecretExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientSecretExpiresAt gets a reference to the given int64 and assigns it to the ClientSecretExpiresAt field.\nfunc (o *OAuth2Client) SetClientSecretExpiresAt(v int64) {\n\to.ClientSecretExpiresAt = &v\n}\n\n// GetClientUri returns the ClientUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetClientUri() string {\n\tif o == nil || IsNil(o.ClientUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientUri\n}\n\n// GetClientUriOk returns a tuple with the ClientUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetClientUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientUri) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientUri, true\n}\n\n// HasClientUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasClientUri() bool {\n\tif o != nil && !IsNil(o.ClientUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientUri gets a reference to the given string and assigns it to the ClientUri field.\nfunc (o *OAuth2Client) SetClientUri(v string) {\n\to.ClientUri = &v\n}\n\n// GetContacts returns the Contacts field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetContacts() []string {\n\tif o == nil || IsNil(o.Contacts) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.Contacts\n}\n\n// GetContactsOk returns a tuple with the Contacts field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetContactsOk() ([]string, bool) {\n\tif o == nil || IsNil(o.Contacts) {\n\t\treturn nil, false\n\t}\n\treturn o.Contacts, true\n}\n\n// HasContacts returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasContacts() bool {\n\tif o != nil && !IsNil(o.Contacts) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContacts gets a reference to the given []string and assigns it to the Contacts field.\nfunc (o *OAuth2Client) SetContacts(v []string) {\n\to.Contacts = v\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *OAuth2Client) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetFrontchannelLogoutSessionRequired returns the FrontchannelLogoutSessionRequired field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetFrontchannelLogoutSessionRequired() bool {\n\tif o == nil || IsNil(o.FrontchannelLogoutSessionRequired) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.FrontchannelLogoutSessionRequired\n}\n\n// GetFrontchannelLogoutSessionRequiredOk returns a tuple with the FrontchannelLogoutSessionRequired field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetFrontchannelLogoutSessionRequiredOk() (*bool, bool) {\n\tif o == nil || IsNil(o.FrontchannelLogoutSessionRequired) {\n\t\treturn nil, false\n\t}\n\treturn o.FrontchannelLogoutSessionRequired, true\n}\n\n// HasFrontchannelLogoutSessionRequired returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasFrontchannelLogoutSessionRequired() bool {\n\tif o != nil && !IsNil(o.FrontchannelLogoutSessionRequired) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFrontchannelLogoutSessionRequired gets a reference to the given bool and assigns it to the FrontchannelLogoutSessionRequired field.\nfunc (o *OAuth2Client) SetFrontchannelLogoutSessionRequired(v bool) {\n\to.FrontchannelLogoutSessionRequired = &v\n}\n\n// GetFrontchannelLogoutUri returns the FrontchannelLogoutUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetFrontchannelLogoutUri() string {\n\tif o == nil || IsNil(o.FrontchannelLogoutUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.FrontchannelLogoutUri\n}\n\n// GetFrontchannelLogoutUriOk returns a tuple with the FrontchannelLogoutUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetFrontchannelLogoutUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.FrontchannelLogoutUri) {\n\t\treturn nil, false\n\t}\n\treturn o.FrontchannelLogoutUri, true\n}\n\n// HasFrontchannelLogoutUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasFrontchannelLogoutUri() bool {\n\tif o != nil && !IsNil(o.FrontchannelLogoutUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFrontchannelLogoutUri gets a reference to the given string and assigns it to the FrontchannelLogoutUri field.\nfunc (o *OAuth2Client) SetFrontchannelLogoutUri(v string) {\n\to.FrontchannelLogoutUri = &v\n}\n\n// GetGrantTypes returns the GrantTypes field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetGrantTypes() []string {\n\tif o == nil || IsNil(o.GrantTypes) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.GrantTypes\n}\n\n// GetGrantTypesOk returns a tuple with the GrantTypes field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetGrantTypesOk() ([]string, bool) {\n\tif o == nil || IsNil(o.GrantTypes) {\n\t\treturn nil, false\n\t}\n\treturn o.GrantTypes, true\n}\n\n// HasGrantTypes returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasGrantTypes() bool {\n\tif o != nil && !IsNil(o.GrantTypes) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetGrantTypes gets a reference to the given []string and assigns it to the GrantTypes field.\nfunc (o *OAuth2Client) SetGrantTypes(v []string) {\n\to.GrantTypes = v\n}\n\n// GetImplicitGrantAccessTokenLifespan returns the ImplicitGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetImplicitGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.ImplicitGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ImplicitGrantAccessTokenLifespan\n}\n\n// GetImplicitGrantAccessTokenLifespanOk returns a tuple with the ImplicitGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetImplicitGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.ImplicitGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.ImplicitGrantAccessTokenLifespan, true\n}\n\n// HasImplicitGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasImplicitGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.ImplicitGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetImplicitGrantAccessTokenLifespan gets a reference to the given string and assigns it to the ImplicitGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetImplicitGrantAccessTokenLifespan(v string) {\n\to.ImplicitGrantAccessTokenLifespan = &v\n}\n\n// GetImplicitGrantIdTokenLifespan returns the ImplicitGrantIdTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetImplicitGrantIdTokenLifespan() string {\n\tif o == nil || IsNil(o.ImplicitGrantIdTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ImplicitGrantIdTokenLifespan\n}\n\n// GetImplicitGrantIdTokenLifespanOk returns a tuple with the ImplicitGrantIdTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetImplicitGrantIdTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.ImplicitGrantIdTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.ImplicitGrantIdTokenLifespan, true\n}\n\n// HasImplicitGrantIdTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasImplicitGrantIdTokenLifespan() bool {\n\tif o != nil && !IsNil(o.ImplicitGrantIdTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetImplicitGrantIdTokenLifespan gets a reference to the given string and assigns it to the ImplicitGrantIdTokenLifespan field.\nfunc (o *OAuth2Client) SetImplicitGrantIdTokenLifespan(v string) {\n\to.ImplicitGrantIdTokenLifespan = &v\n}\n\n// GetJwks returns the Jwks field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *OAuth2Client) GetJwks() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Jwks\n}\n\n// GetJwksOk returns a tuple with the Jwks field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *OAuth2Client) GetJwksOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Jwks) {\n\t\treturn nil, false\n\t}\n\treturn &o.Jwks, true\n}\n\n// HasJwks returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasJwks() bool {\n\tif o != nil && !IsNil(o.Jwks) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetJwks gets a reference to the given interface{} and assigns it to the Jwks field.\nfunc (o *OAuth2Client) SetJwks(v interface{}) {\n\to.Jwks = v\n}\n\n// GetJwksUri returns the JwksUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetJwksUri() string {\n\tif o == nil || IsNil(o.JwksUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.JwksUri\n}\n\n// GetJwksUriOk returns a tuple with the JwksUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetJwksUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.JwksUri) {\n\t\treturn nil, false\n\t}\n\treturn o.JwksUri, true\n}\n\n// HasJwksUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasJwksUri() bool {\n\tif o != nil && !IsNil(o.JwksUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetJwksUri gets a reference to the given string and assigns it to the JwksUri field.\nfunc (o *OAuth2Client) SetJwksUri(v string) {\n\to.JwksUri = &v\n}\n\n// GetJwtBearerGrantAccessTokenLifespan returns the JwtBearerGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetJwtBearerGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.JwtBearerGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.JwtBearerGrantAccessTokenLifespan\n}\n\n// GetJwtBearerGrantAccessTokenLifespanOk returns a tuple with the JwtBearerGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetJwtBearerGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.JwtBearerGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.JwtBearerGrantAccessTokenLifespan, true\n}\n\n// HasJwtBearerGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasJwtBearerGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.JwtBearerGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetJwtBearerGrantAccessTokenLifespan gets a reference to the given string and assigns it to the JwtBearerGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetJwtBearerGrantAccessTokenLifespan(v string) {\n\to.JwtBearerGrantAccessTokenLifespan = &v\n}\n\n// GetLogoUri returns the LogoUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetLogoUri() string {\n\tif o == nil || IsNil(o.LogoUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.LogoUri\n}\n\n// GetLogoUriOk returns a tuple with the LogoUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetLogoUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.LogoUri) {\n\t\treturn nil, false\n\t}\n\treturn o.LogoUri, true\n}\n\n// HasLogoUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasLogoUri() bool {\n\tif o != nil && !IsNil(o.LogoUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLogoUri gets a reference to the given string and assigns it to the LogoUri field.\nfunc (o *OAuth2Client) SetLogoUri(v string) {\n\to.LogoUri = &v\n}\n\n// GetMetadata returns the Metadata field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *OAuth2Client) GetMetadata() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Metadata\n}\n\n// GetMetadataOk returns a tuple with the Metadata field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *OAuth2Client) GetMetadataOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Metadata) {\n\t\treturn nil, false\n\t}\n\treturn &o.Metadata, true\n}\n\n// HasMetadata returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasMetadata() bool {\n\tif o != nil && !IsNil(o.Metadata) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadata gets a reference to the given interface{} and assigns it to the Metadata field.\nfunc (o *OAuth2Client) SetMetadata(v interface{}) {\n\to.Metadata = v\n}\n\n// GetOwner returns the Owner field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetOwner() string {\n\tif o == nil || IsNil(o.Owner) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Owner\n}\n\n// GetOwnerOk returns a tuple with the Owner field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetOwnerOk() (*string, bool) {\n\tif o == nil || IsNil(o.Owner) {\n\t\treturn nil, false\n\t}\n\treturn o.Owner, true\n}\n\n// HasOwner returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasOwner() bool {\n\tif o != nil && !IsNil(o.Owner) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOwner gets a reference to the given string and assigns it to the Owner field.\nfunc (o *OAuth2Client) SetOwner(v string) {\n\to.Owner = &v\n}\n\n// GetPolicyUri returns the PolicyUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetPolicyUri() string {\n\tif o == nil || IsNil(o.PolicyUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PolicyUri\n}\n\n// GetPolicyUriOk returns a tuple with the PolicyUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetPolicyUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.PolicyUri) {\n\t\treturn nil, false\n\t}\n\treturn o.PolicyUri, true\n}\n\n// HasPolicyUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasPolicyUri() bool {\n\tif o != nil && !IsNil(o.PolicyUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPolicyUri gets a reference to the given string and assigns it to the PolicyUri field.\nfunc (o *OAuth2Client) SetPolicyUri(v string) {\n\to.PolicyUri = &v\n}\n\n// GetPostLogoutRedirectUris returns the PostLogoutRedirectUris field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetPostLogoutRedirectUris() []string {\n\tif o == nil || IsNil(o.PostLogoutRedirectUris) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.PostLogoutRedirectUris\n}\n\n// GetPostLogoutRedirectUrisOk returns a tuple with the PostLogoutRedirectUris field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetPostLogoutRedirectUrisOk() ([]string, bool) {\n\tif o == nil || IsNil(o.PostLogoutRedirectUris) {\n\t\treturn nil, false\n\t}\n\treturn o.PostLogoutRedirectUris, true\n}\n\n// HasPostLogoutRedirectUris returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasPostLogoutRedirectUris() bool {\n\tif o != nil && !IsNil(o.PostLogoutRedirectUris) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPostLogoutRedirectUris gets a reference to the given []string and assigns it to the PostLogoutRedirectUris field.\nfunc (o *OAuth2Client) SetPostLogoutRedirectUris(v []string) {\n\to.PostLogoutRedirectUris = v\n}\n\n// GetRedirectUris returns the RedirectUris field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRedirectUris() []string {\n\tif o == nil || IsNil(o.RedirectUris) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.RedirectUris\n}\n\n// GetRedirectUrisOk returns a tuple with the RedirectUris field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRedirectUrisOk() ([]string, bool) {\n\tif o == nil || IsNil(o.RedirectUris) {\n\t\treturn nil, false\n\t}\n\treturn o.RedirectUris, true\n}\n\n// HasRedirectUris returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRedirectUris() bool {\n\tif o != nil && !IsNil(o.RedirectUris) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRedirectUris gets a reference to the given []string and assigns it to the RedirectUris field.\nfunc (o *OAuth2Client) SetRedirectUris(v []string) {\n\to.RedirectUris = v\n}\n\n// GetRefreshTokenGrantAccessTokenLifespan returns the RefreshTokenGrantAccessTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRefreshTokenGrantAccessTokenLifespan() string {\n\tif o == nil || IsNil(o.RefreshTokenGrantAccessTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RefreshTokenGrantAccessTokenLifespan\n}\n\n// GetRefreshTokenGrantAccessTokenLifespanOk returns a tuple with the RefreshTokenGrantAccessTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRefreshTokenGrantAccessTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.RefreshTokenGrantAccessTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.RefreshTokenGrantAccessTokenLifespan, true\n}\n\n// HasRefreshTokenGrantAccessTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRefreshTokenGrantAccessTokenLifespan() bool {\n\tif o != nil && !IsNil(o.RefreshTokenGrantAccessTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRefreshTokenGrantAccessTokenLifespan gets a reference to the given string and assigns it to the RefreshTokenGrantAccessTokenLifespan field.\nfunc (o *OAuth2Client) SetRefreshTokenGrantAccessTokenLifespan(v string) {\n\to.RefreshTokenGrantAccessTokenLifespan = &v\n}\n\n// GetRefreshTokenGrantIdTokenLifespan returns the RefreshTokenGrantIdTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRefreshTokenGrantIdTokenLifespan() string {\n\tif o == nil || IsNil(o.RefreshTokenGrantIdTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RefreshTokenGrantIdTokenLifespan\n}\n\n// GetRefreshTokenGrantIdTokenLifespanOk returns a tuple with the RefreshTokenGrantIdTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRefreshTokenGrantIdTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.RefreshTokenGrantIdTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.RefreshTokenGrantIdTokenLifespan, true\n}\n\n// HasRefreshTokenGrantIdTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRefreshTokenGrantIdTokenLifespan() bool {\n\tif o != nil && !IsNil(o.RefreshTokenGrantIdTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRefreshTokenGrantIdTokenLifespan gets a reference to the given string and assigns it to the RefreshTokenGrantIdTokenLifespan field.\nfunc (o *OAuth2Client) SetRefreshTokenGrantIdTokenLifespan(v string) {\n\to.RefreshTokenGrantIdTokenLifespan = &v\n}\n\n// GetRefreshTokenGrantRefreshTokenLifespan returns the RefreshTokenGrantRefreshTokenLifespan field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRefreshTokenGrantRefreshTokenLifespan() string {\n\tif o == nil || IsNil(o.RefreshTokenGrantRefreshTokenLifespan) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RefreshTokenGrantRefreshTokenLifespan\n}\n\n// GetRefreshTokenGrantRefreshTokenLifespanOk returns a tuple with the RefreshTokenGrantRefreshTokenLifespan field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRefreshTokenGrantRefreshTokenLifespanOk() (*string, bool) {\n\tif o == nil || IsNil(o.RefreshTokenGrantRefreshTokenLifespan) {\n\t\treturn nil, false\n\t}\n\treturn o.RefreshTokenGrantRefreshTokenLifespan, true\n}\n\n// HasRefreshTokenGrantRefreshTokenLifespan returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRefreshTokenGrantRefreshTokenLifespan() bool {\n\tif o != nil && !IsNil(o.RefreshTokenGrantRefreshTokenLifespan) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRefreshTokenGrantRefreshTokenLifespan gets a reference to the given string and assigns it to the RefreshTokenGrantRefreshTokenLifespan field.\nfunc (o *OAuth2Client) SetRefreshTokenGrantRefreshTokenLifespan(v string) {\n\to.RefreshTokenGrantRefreshTokenLifespan = &v\n}\n\n// GetRegistrationAccessToken returns the RegistrationAccessToken field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRegistrationAccessToken() string {\n\tif o == nil || IsNil(o.RegistrationAccessToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RegistrationAccessToken\n}\n\n// GetRegistrationAccessTokenOk returns a tuple with the RegistrationAccessToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRegistrationAccessTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.RegistrationAccessToken) {\n\t\treturn nil, false\n\t}\n\treturn o.RegistrationAccessToken, true\n}\n\n// HasRegistrationAccessToken returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRegistrationAccessToken() bool {\n\tif o != nil && !IsNil(o.RegistrationAccessToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRegistrationAccessToken gets a reference to the given string and assigns it to the RegistrationAccessToken field.\nfunc (o *OAuth2Client) SetRegistrationAccessToken(v string) {\n\to.RegistrationAccessToken = &v\n}\n\n// GetRegistrationClientUri returns the RegistrationClientUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRegistrationClientUri() string {\n\tif o == nil || IsNil(o.RegistrationClientUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RegistrationClientUri\n}\n\n// GetRegistrationClientUriOk returns a tuple with the RegistrationClientUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRegistrationClientUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.RegistrationClientUri) {\n\t\treturn nil, false\n\t}\n\treturn o.RegistrationClientUri, true\n}\n\n// HasRegistrationClientUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRegistrationClientUri() bool {\n\tif o != nil && !IsNil(o.RegistrationClientUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRegistrationClientUri gets a reference to the given string and assigns it to the RegistrationClientUri field.\nfunc (o *OAuth2Client) SetRegistrationClientUri(v string) {\n\to.RegistrationClientUri = &v\n}\n\n// GetRequestObjectSigningAlg returns the RequestObjectSigningAlg field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRequestObjectSigningAlg() string {\n\tif o == nil || IsNil(o.RequestObjectSigningAlg) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RequestObjectSigningAlg\n}\n\n// GetRequestObjectSigningAlgOk returns a tuple with the RequestObjectSigningAlg field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRequestObjectSigningAlgOk() (*string, bool) {\n\tif o == nil || IsNil(o.RequestObjectSigningAlg) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestObjectSigningAlg, true\n}\n\n// HasRequestObjectSigningAlg returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRequestObjectSigningAlg() bool {\n\tif o != nil && !IsNil(o.RequestObjectSigningAlg) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestObjectSigningAlg gets a reference to the given string and assigns it to the RequestObjectSigningAlg field.\nfunc (o *OAuth2Client) SetRequestObjectSigningAlg(v string) {\n\to.RequestObjectSigningAlg = &v\n}\n\n// GetRequestUris returns the RequestUris field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetRequestUris() []string {\n\tif o == nil || IsNil(o.RequestUris) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.RequestUris\n}\n\n// GetRequestUrisOk returns a tuple with the RequestUris field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetRequestUrisOk() ([]string, bool) {\n\tif o == nil || IsNil(o.RequestUris) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestUris, true\n}\n\n// HasRequestUris returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasRequestUris() bool {\n\tif o != nil && !IsNil(o.RequestUris) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestUris gets a reference to the given []string and assigns it to the RequestUris field.\nfunc (o *OAuth2Client) SetRequestUris(v []string) {\n\to.RequestUris = v\n}\n\n// GetResponseTypes returns the ResponseTypes field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetResponseTypes() []string {\n\tif o == nil || IsNil(o.ResponseTypes) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.ResponseTypes\n}\n\n// GetResponseTypesOk returns a tuple with the ResponseTypes field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetResponseTypesOk() ([]string, bool) {\n\tif o == nil || IsNil(o.ResponseTypes) {\n\t\treturn nil, false\n\t}\n\treturn o.ResponseTypes, true\n}\n\n// HasResponseTypes returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasResponseTypes() bool {\n\tif o != nil && !IsNil(o.ResponseTypes) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetResponseTypes gets a reference to the given []string and assigns it to the ResponseTypes field.\nfunc (o *OAuth2Client) SetResponseTypes(v []string) {\n\to.ResponseTypes = v\n}\n\n// GetScope returns the Scope field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetScope() string {\n\tif o == nil || IsNil(o.Scope) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Scope\n}\n\n// GetScopeOk returns a tuple with the Scope field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetScopeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Scope) {\n\t\treturn nil, false\n\t}\n\treturn o.Scope, true\n}\n\n// HasScope returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasScope() bool {\n\tif o != nil && !IsNil(o.Scope) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetScope gets a reference to the given string and assigns it to the Scope field.\nfunc (o *OAuth2Client) SetScope(v string) {\n\to.Scope = &v\n}\n\n// GetSectorIdentifierUri returns the SectorIdentifierUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetSectorIdentifierUri() string {\n\tif o == nil || IsNil(o.SectorIdentifierUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SectorIdentifierUri\n}\n\n// GetSectorIdentifierUriOk returns a tuple with the SectorIdentifierUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetSectorIdentifierUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.SectorIdentifierUri) {\n\t\treturn nil, false\n\t}\n\treturn o.SectorIdentifierUri, true\n}\n\n// HasSectorIdentifierUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasSectorIdentifierUri() bool {\n\tif o != nil && !IsNil(o.SectorIdentifierUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSectorIdentifierUri gets a reference to the given string and assigns it to the SectorIdentifierUri field.\nfunc (o *OAuth2Client) SetSectorIdentifierUri(v string) {\n\to.SectorIdentifierUri = &v\n}\n\n// GetSkipConsent returns the SkipConsent field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetSkipConsent() bool {\n\tif o == nil || IsNil(o.SkipConsent) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.SkipConsent\n}\n\n// GetSkipConsentOk returns a tuple with the SkipConsent field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetSkipConsentOk() (*bool, bool) {\n\tif o == nil || IsNil(o.SkipConsent) {\n\t\treturn nil, false\n\t}\n\treturn o.SkipConsent, true\n}\n\n// HasSkipConsent returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasSkipConsent() bool {\n\tif o != nil && !IsNil(o.SkipConsent) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSkipConsent gets a reference to the given bool and assigns it to the SkipConsent field.\nfunc (o *OAuth2Client) SetSkipConsent(v bool) {\n\to.SkipConsent = &v\n}\n\n// GetSkipLogoutConsent returns the SkipLogoutConsent field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetSkipLogoutConsent() bool {\n\tif o == nil || IsNil(o.SkipLogoutConsent) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.SkipLogoutConsent\n}\n\n// GetSkipLogoutConsentOk returns a tuple with the SkipLogoutConsent field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetSkipLogoutConsentOk() (*bool, bool) {\n\tif o == nil || IsNil(o.SkipLogoutConsent) {\n\t\treturn nil, false\n\t}\n\treturn o.SkipLogoutConsent, true\n}\n\n// HasSkipLogoutConsent returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasSkipLogoutConsent() bool {\n\tif o != nil && !IsNil(o.SkipLogoutConsent) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSkipLogoutConsent gets a reference to the given bool and assigns it to the SkipLogoutConsent field.\nfunc (o *OAuth2Client) SetSkipLogoutConsent(v bool) {\n\to.SkipLogoutConsent = &v\n}\n\n// GetSubjectType returns the SubjectType field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetSubjectType() string {\n\tif o == nil || IsNil(o.SubjectType) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SubjectType\n}\n\n// GetSubjectTypeOk returns a tuple with the SubjectType field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetSubjectTypeOk() (*string, bool) {\n\tif o == nil || IsNil(o.SubjectType) {\n\t\treturn nil, false\n\t}\n\treturn o.SubjectType, true\n}\n\n// HasSubjectType returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasSubjectType() bool {\n\tif o != nil && !IsNil(o.SubjectType) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSubjectType gets a reference to the given string and assigns it to the SubjectType field.\nfunc (o *OAuth2Client) SetSubjectType(v string) {\n\to.SubjectType = &v\n}\n\n// GetTokenEndpointAuthMethod returns the TokenEndpointAuthMethod field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetTokenEndpointAuthMethod() string {\n\tif o == nil || IsNil(o.TokenEndpointAuthMethod) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.TokenEndpointAuthMethod\n}\n\n// GetTokenEndpointAuthMethodOk returns a tuple with the TokenEndpointAuthMethod field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetTokenEndpointAuthMethodOk() (*string, bool) {\n\tif o == nil || IsNil(o.TokenEndpointAuthMethod) {\n\t\treturn nil, false\n\t}\n\treturn o.TokenEndpointAuthMethod, true\n}\n\n// HasTokenEndpointAuthMethod returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasTokenEndpointAuthMethod() bool {\n\tif o != nil && !IsNil(o.TokenEndpointAuthMethod) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTokenEndpointAuthMethod gets a reference to the given string and assigns it to the TokenEndpointAuthMethod field.\nfunc (o *OAuth2Client) SetTokenEndpointAuthMethod(v string) {\n\to.TokenEndpointAuthMethod = &v\n}\n\n// GetTokenEndpointAuthSigningAlg returns the TokenEndpointAuthSigningAlg field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetTokenEndpointAuthSigningAlg() string {\n\tif o == nil || IsNil(o.TokenEndpointAuthSigningAlg) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.TokenEndpointAuthSigningAlg\n}\n\n// GetTokenEndpointAuthSigningAlgOk returns a tuple with the TokenEndpointAuthSigningAlg field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetTokenEndpointAuthSigningAlgOk() (*string, bool) {\n\tif o == nil || IsNil(o.TokenEndpointAuthSigningAlg) {\n\t\treturn nil, false\n\t}\n\treturn o.TokenEndpointAuthSigningAlg, true\n}\n\n// HasTokenEndpointAuthSigningAlg returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasTokenEndpointAuthSigningAlg() bool {\n\tif o != nil && !IsNil(o.TokenEndpointAuthSigningAlg) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTokenEndpointAuthSigningAlg gets a reference to the given string and assigns it to the TokenEndpointAuthSigningAlg field.\nfunc (o *OAuth2Client) SetTokenEndpointAuthSigningAlg(v string) {\n\to.TokenEndpointAuthSigningAlg = &v\n}\n\n// GetTosUri returns the TosUri field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetTosUri() string {\n\tif o == nil || IsNil(o.TosUri) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.TosUri\n}\n\n// GetTosUriOk returns a tuple with the TosUri field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetTosUriOk() (*string, bool) {\n\tif o == nil || IsNil(o.TosUri) {\n\t\treturn nil, false\n\t}\n\treturn o.TosUri, true\n}\n\n// HasTosUri returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasTosUri() bool {\n\tif o != nil && !IsNil(o.TosUri) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTosUri gets a reference to the given string and assigns it to the TosUri field.\nfunc (o *OAuth2Client) SetTosUri(v string) {\n\to.TosUri = &v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *OAuth2Client) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetUserinfoSignedResponseAlg returns the UserinfoSignedResponseAlg field value if set, zero value otherwise.\nfunc (o *OAuth2Client) GetUserinfoSignedResponseAlg() string {\n\tif o == nil || IsNil(o.UserinfoSignedResponseAlg) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.UserinfoSignedResponseAlg\n}\n\n// GetUserinfoSignedResponseAlgOk returns a tuple with the UserinfoSignedResponseAlg field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2Client) GetUserinfoSignedResponseAlgOk() (*string, bool) {\n\tif o == nil || IsNil(o.UserinfoSignedResponseAlg) {\n\t\treturn nil, false\n\t}\n\treturn o.UserinfoSignedResponseAlg, true\n}\n\n// HasUserinfoSignedResponseAlg returns a boolean if a field has been set.\nfunc (o *OAuth2Client) HasUserinfoSignedResponseAlg() bool {\n\tif o != nil && !IsNil(o.UserinfoSignedResponseAlg) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUserinfoSignedResponseAlg gets a reference to the given string and assigns it to the UserinfoSignedResponseAlg field.\nfunc (o *OAuth2Client) SetUserinfoSignedResponseAlg(v string) {\n\to.UserinfoSignedResponseAlg = &v\n}\n\nfunc (o OAuth2Client) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o OAuth2Client) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.AccessTokenStrategy) {\n\t\ttoSerialize[\"access_token_strategy\"] = o.AccessTokenStrategy\n\t}\n\tif !IsNil(o.AllowedCorsOrigins) {\n\t\ttoSerialize[\"allowed_cors_origins\"] = o.AllowedCorsOrigins\n\t}\n\tif !IsNil(o.Audience) {\n\t\ttoSerialize[\"audience\"] = o.Audience\n\t}\n\tif !IsNil(o.AuthorizationCodeGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"authorization_code_grant_access_token_lifespan\"] = o.AuthorizationCodeGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.AuthorizationCodeGrantIdTokenLifespan) {\n\t\ttoSerialize[\"authorization_code_grant_id_token_lifespan\"] = o.AuthorizationCodeGrantIdTokenLifespan\n\t}\n\tif !IsNil(o.AuthorizationCodeGrantRefreshTokenLifespan) {\n\t\ttoSerialize[\"authorization_code_grant_refresh_token_lifespan\"] = o.AuthorizationCodeGrantRefreshTokenLifespan\n\t}\n\tif !IsNil(o.BackchannelLogoutSessionRequired) {\n\t\ttoSerialize[\"backchannel_logout_session_required\"] = o.BackchannelLogoutSessionRequired\n\t}\n\tif !IsNil(o.BackchannelLogoutUri) {\n\t\ttoSerialize[\"backchannel_logout_uri\"] = o.BackchannelLogoutUri\n\t}\n\tif !IsNil(o.ClientCredentialsGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"client_credentials_grant_access_token_lifespan\"] = o.ClientCredentialsGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.ClientId) {\n\t\ttoSerialize[\"client_id\"] = o.ClientId\n\t}\n\tif !IsNil(o.ClientName) {\n\t\ttoSerialize[\"client_name\"] = o.ClientName\n\t}\n\tif !IsNil(o.ClientSecret) {\n\t\ttoSerialize[\"client_secret\"] = o.ClientSecret\n\t}\n\tif !IsNil(o.ClientSecretExpiresAt) {\n\t\ttoSerialize[\"client_secret_expires_at\"] = o.ClientSecretExpiresAt\n\t}\n\tif !IsNil(o.ClientUri) {\n\t\ttoSerialize[\"client_uri\"] = o.ClientUri\n\t}\n\tif !IsNil(o.Contacts) {\n\t\ttoSerialize[\"contacts\"] = o.Contacts\n\t}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.FrontchannelLogoutSessionRequired) {\n\t\ttoSerialize[\"frontchannel_logout_session_required\"] = o.FrontchannelLogoutSessionRequired\n\t}\n\tif !IsNil(o.FrontchannelLogoutUri) {\n\t\ttoSerialize[\"frontchannel_logout_uri\"] = o.FrontchannelLogoutUri\n\t}\n\tif !IsNil(o.GrantTypes) {\n\t\ttoSerialize[\"grant_types\"] = o.GrantTypes\n\t}\n\tif !IsNil(o.ImplicitGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"implicit_grant_access_token_lifespan\"] = o.ImplicitGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.ImplicitGrantIdTokenLifespan) {\n\t\ttoSerialize[\"implicit_grant_id_token_lifespan\"] = o.ImplicitGrantIdTokenLifespan\n\t}\n\tif o.Jwks != nil {\n\t\ttoSerialize[\"jwks\"] = o.Jwks\n\t}\n\tif !IsNil(o.JwksUri) {\n\t\ttoSerialize[\"jwks_uri\"] = o.JwksUri\n\t}\n\tif !IsNil(o.JwtBearerGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"jwt_bearer_grant_access_token_lifespan\"] = o.JwtBearerGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.LogoUri) {\n\t\ttoSerialize[\"logo_uri\"] = o.LogoUri\n\t}\n\tif o.Metadata != nil {\n\t\ttoSerialize[\"metadata\"] = o.Metadata\n\t}\n\tif !IsNil(o.Owner) {\n\t\ttoSerialize[\"owner\"] = o.Owner\n\t}\n\tif !IsNil(o.PolicyUri) {\n\t\ttoSerialize[\"policy_uri\"] = o.PolicyUri\n\t}\n\tif !IsNil(o.PostLogoutRedirectUris) {\n\t\ttoSerialize[\"post_logout_redirect_uris\"] = o.PostLogoutRedirectUris\n\t}\n\tif !IsNil(o.RedirectUris) {\n\t\ttoSerialize[\"redirect_uris\"] = o.RedirectUris\n\t}\n\tif !IsNil(o.RefreshTokenGrantAccessTokenLifespan) {\n\t\ttoSerialize[\"refresh_token_grant_access_token_lifespan\"] = o.RefreshTokenGrantAccessTokenLifespan\n\t}\n\tif !IsNil(o.RefreshTokenGrantIdTokenLifespan) {\n\t\ttoSerialize[\"refresh_token_grant_id_token_lifespan\"] = o.RefreshTokenGrantIdTokenLifespan\n\t}\n\tif !IsNil(o.RefreshTokenGrantRefreshTokenLifespan) {\n\t\ttoSerialize[\"refresh_token_grant_refresh_token_lifespan\"] = o.RefreshTokenGrantRefreshTokenLifespan\n\t}\n\tif !IsNil(o.RegistrationAccessToken) {\n\t\ttoSerialize[\"registration_access_token\"] = o.RegistrationAccessToken\n\t}\n\tif !IsNil(o.RegistrationClientUri) {\n\t\ttoSerialize[\"registration_client_uri\"] = o.RegistrationClientUri\n\t}\n\tif !IsNil(o.RequestObjectSigningAlg) {\n\t\ttoSerialize[\"request_object_signing_alg\"] = o.RequestObjectSigningAlg\n\t}\n\tif !IsNil(o.RequestUris) {\n\t\ttoSerialize[\"request_uris\"] = o.RequestUris\n\t}\n\tif !IsNil(o.ResponseTypes) {\n\t\ttoSerialize[\"response_types\"] = o.ResponseTypes\n\t}\n\tif !IsNil(o.Scope) {\n\t\ttoSerialize[\"scope\"] = o.Scope\n\t}\n\tif !IsNil(o.SectorIdentifierUri) {\n\t\ttoSerialize[\"sector_identifier_uri\"] = o.SectorIdentifierUri\n\t}\n\tif !IsNil(o.SkipConsent) {\n\t\ttoSerialize[\"skip_consent\"] = o.SkipConsent\n\t}\n\tif !IsNil(o.SkipLogoutConsent) {\n\t\ttoSerialize[\"skip_logout_consent\"] = o.SkipLogoutConsent\n\t}\n\tif !IsNil(o.SubjectType) {\n\t\ttoSerialize[\"subject_type\"] = o.SubjectType\n\t}\n\tif !IsNil(o.TokenEndpointAuthMethod) {\n\t\ttoSerialize[\"token_endpoint_auth_method\"] = o.TokenEndpointAuthMethod\n\t}\n\tif !IsNil(o.TokenEndpointAuthSigningAlg) {\n\t\ttoSerialize[\"token_endpoint_auth_signing_alg\"] = o.TokenEndpointAuthSigningAlg\n\t}\n\tif !IsNil(o.TosUri) {\n\t\ttoSerialize[\"tos_uri\"] = o.TosUri\n\t}\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\tif !IsNil(o.UserinfoSignedResponseAlg) {\n\t\ttoSerialize[\"userinfo_signed_response_alg\"] = o.UserinfoSignedResponseAlg\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *OAuth2Client) UnmarshalJSON(data []byte) (err error) {\n\tvarOAuth2Client := _OAuth2Client{}\n\n\terr = json.Unmarshal(data, &varOAuth2Client)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = OAuth2Client(varOAuth2Client)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"access_token_strategy\")\n\t\tdelete(additionalProperties, \"allowed_cors_origins\")\n\t\tdelete(additionalProperties, \"audience\")\n\t\tdelete(additionalProperties, \"authorization_code_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"authorization_code_grant_id_token_lifespan\")\n\t\tdelete(additionalProperties, \"authorization_code_grant_refresh_token_lifespan\")\n\t\tdelete(additionalProperties, \"backchannel_logout_session_required\")\n\t\tdelete(additionalProperties, \"backchannel_logout_uri\")\n\t\tdelete(additionalProperties, \"client_credentials_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"client_id\")\n\t\tdelete(additionalProperties, \"client_name\")\n\t\tdelete(additionalProperties, \"client_secret\")\n\t\tdelete(additionalProperties, \"client_secret_expires_at\")\n\t\tdelete(additionalProperties, \"client_uri\")\n\t\tdelete(additionalProperties, \"contacts\")\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"frontchannel_logout_session_required\")\n\t\tdelete(additionalProperties, \"frontchannel_logout_uri\")\n\t\tdelete(additionalProperties, \"grant_types\")\n\t\tdelete(additionalProperties, \"implicit_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"implicit_grant_id_token_lifespan\")\n\t\tdelete(additionalProperties, \"jwks\")\n\t\tdelete(additionalProperties, \"jwks_uri\")\n\t\tdelete(additionalProperties, \"jwt_bearer_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"logo_uri\")\n\t\tdelete(additionalProperties, \"metadata\")\n\t\tdelete(additionalProperties, \"owner\")\n\t\tdelete(additionalProperties, \"policy_uri\")\n\t\tdelete(additionalProperties, \"post_logout_redirect_uris\")\n\t\tdelete(additionalProperties, \"redirect_uris\")\n\t\tdelete(additionalProperties, \"refresh_token_grant_access_token_lifespan\")\n\t\tdelete(additionalProperties, \"refresh_token_grant_id_token_lifespan\")\n\t\tdelete(additionalProperties, \"refresh_token_grant_refresh_token_lifespan\")\n\t\tdelete(additionalProperties, \"registration_access_token\")\n\t\tdelete(additionalProperties, \"registration_client_uri\")\n\t\tdelete(additionalProperties, \"request_object_signing_alg\")\n\t\tdelete(additionalProperties, \"request_uris\")\n\t\tdelete(additionalProperties, \"response_types\")\n\t\tdelete(additionalProperties, \"scope\")\n\t\tdelete(additionalProperties, \"sector_identifier_uri\")\n\t\tdelete(additionalProperties, \"skip_consent\")\n\t\tdelete(additionalProperties, \"skip_logout_consent\")\n\t\tdelete(additionalProperties, \"subject_type\")\n\t\tdelete(additionalProperties, \"token_endpoint_auth_method\")\n\t\tdelete(additionalProperties, \"token_endpoint_auth_signing_alg\")\n\t\tdelete(additionalProperties, \"tos_uri\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"userinfo_signed_response_alg\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableOAuth2Client struct {\n\tvalue *OAuth2Client\n\tisSet bool\n}\n\nfunc (v NullableOAuth2Client) Get() *OAuth2Client {\n\treturn v.value\n}\n\nfunc (v *NullableOAuth2Client) Set(val *OAuth2Client) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableOAuth2Client) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableOAuth2Client) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableOAuth2Client(val *OAuth2Client) *NullableOAuth2Client {\n\treturn &NullableOAuth2Client{value: val, isSet: true}\n}\n\nfunc (v NullableOAuth2Client) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableOAuth2Client) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_o_auth2_consent_request_open_id_connect_context.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the OAuth2ConsentRequestOpenIDConnectContext type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &OAuth2ConsentRequestOpenIDConnectContext{}\n\n// OAuth2ConsentRequestOpenIDConnectContext OAuth2ConsentRequestOpenIDConnectContext struct for OAuth2ConsentRequestOpenIDConnectContext\ntype OAuth2ConsentRequestOpenIDConnectContext struct {\n\t// ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request. It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required.  OpenID Connect defines it as follows: > Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter.\n\tAcrValues []string `json:\"acr_values,omitempty\"`\n\t// Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are: page: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode. popup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over. touch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. wap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \\\\\\\"feature phone\\\\\\\" type display.  The Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.\n\tDisplay *string `json:\"display,omitempty\"`\n\t// IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client.\n\tIdTokenHintClaims map[string]interface{} `json:\"id_token_hint_claims,omitempty\"`\n\t// LoginHint hints about the login identifier the End-User might use to log in (if necessary). This hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier) and then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a phone number in the format specified for the phone_number Claim. The use of this parameter is optional.\n\tLoginHint *string `json:\"login_hint,omitempty\"`\n\t// UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a space-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value \\\\\\\"fr-CA fr en\\\\\\\" represents a preference for French as spoken in Canada, then French (without a region designation), followed by English (without a region designation). An error SHOULD NOT result if some or all of the requested locales are not supported by the OpenID Provider.\n\tUiLocales            []string `json:\"ui_locales,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _OAuth2ConsentRequestOpenIDConnectContext OAuth2ConsentRequestOpenIDConnectContext\n\n// NewOAuth2ConsentRequestOpenIDConnectContext instantiates a new OAuth2ConsentRequestOpenIDConnectContext object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewOAuth2ConsentRequestOpenIDConnectContext() *OAuth2ConsentRequestOpenIDConnectContext {\n\tthis := OAuth2ConsentRequestOpenIDConnectContext{}\n\treturn &this\n}\n\n// NewOAuth2ConsentRequestOpenIDConnectContextWithDefaults instantiates a new OAuth2ConsentRequestOpenIDConnectContext object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewOAuth2ConsentRequestOpenIDConnectContextWithDefaults() *OAuth2ConsentRequestOpenIDConnectContext {\n\tthis := OAuth2ConsentRequestOpenIDConnectContext{}\n\treturn &this\n}\n\n// GetAcrValues returns the AcrValues field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetAcrValues() []string {\n\tif o == nil || IsNil(o.AcrValues) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.AcrValues\n}\n\n// GetAcrValuesOk returns a tuple with the AcrValues field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetAcrValuesOk() ([]string, bool) {\n\tif o == nil || IsNil(o.AcrValues) {\n\t\treturn nil, false\n\t}\n\treturn o.AcrValues, true\n}\n\n// HasAcrValues returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasAcrValues() bool {\n\tif o != nil && !IsNil(o.AcrValues) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAcrValues gets a reference to the given []string and assigns it to the AcrValues field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetAcrValues(v []string) {\n\to.AcrValues = v\n}\n\n// GetDisplay returns the Display field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetDisplay() string {\n\tif o == nil || IsNil(o.Display) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Display\n}\n\n// GetDisplayOk returns a tuple with the Display field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetDisplayOk() (*string, bool) {\n\tif o == nil || IsNil(o.Display) {\n\t\treturn nil, false\n\t}\n\treturn o.Display, true\n}\n\n// HasDisplay returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasDisplay() bool {\n\tif o != nil && !IsNil(o.Display) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDisplay gets a reference to the given string and assigns it to the Display field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetDisplay(v string) {\n\to.Display = &v\n}\n\n// GetIdTokenHintClaims returns the IdTokenHintClaims field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetIdTokenHintClaims() map[string]interface{} {\n\tif o == nil || IsNil(o.IdTokenHintClaims) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.IdTokenHintClaims\n}\n\n// GetIdTokenHintClaimsOk returns a tuple with the IdTokenHintClaims field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetIdTokenHintClaimsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.IdTokenHintClaims) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.IdTokenHintClaims, true\n}\n\n// HasIdTokenHintClaims returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasIdTokenHintClaims() bool {\n\tif o != nil && !IsNil(o.IdTokenHintClaims) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdTokenHintClaims gets a reference to the given map[string]interface{} and assigns it to the IdTokenHintClaims field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetIdTokenHintClaims(v map[string]interface{}) {\n\to.IdTokenHintClaims = v\n}\n\n// GetLoginHint returns the LoginHint field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetLoginHint() string {\n\tif o == nil || IsNil(o.LoginHint) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.LoginHint\n}\n\n// GetLoginHintOk returns a tuple with the LoginHint field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetLoginHintOk() (*string, bool) {\n\tif o == nil || IsNil(o.LoginHint) {\n\t\treturn nil, false\n\t}\n\treturn o.LoginHint, true\n}\n\n// HasLoginHint returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasLoginHint() bool {\n\tif o != nil && !IsNil(o.LoginHint) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLoginHint gets a reference to the given string and assigns it to the LoginHint field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetLoginHint(v string) {\n\to.LoginHint = &v\n}\n\n// GetUiLocales returns the UiLocales field value if set, zero value otherwise.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetUiLocales() []string {\n\tif o == nil || IsNil(o.UiLocales) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.UiLocales\n}\n\n// GetUiLocalesOk returns a tuple with the UiLocales field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) GetUiLocalesOk() ([]string, bool) {\n\tif o == nil || IsNil(o.UiLocales) {\n\t\treturn nil, false\n\t}\n\treturn o.UiLocales, true\n}\n\n// HasUiLocales returns a boolean if a field has been set.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) HasUiLocales() bool {\n\tif o != nil && !IsNil(o.UiLocales) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUiLocales gets a reference to the given []string and assigns it to the UiLocales field.\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) SetUiLocales(v []string) {\n\to.UiLocales = v\n}\n\nfunc (o OAuth2ConsentRequestOpenIDConnectContext) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o OAuth2ConsentRequestOpenIDConnectContext) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.AcrValues) {\n\t\ttoSerialize[\"acr_values\"] = o.AcrValues\n\t}\n\tif !IsNil(o.Display) {\n\t\ttoSerialize[\"display\"] = o.Display\n\t}\n\tif !IsNil(o.IdTokenHintClaims) {\n\t\ttoSerialize[\"id_token_hint_claims\"] = o.IdTokenHintClaims\n\t}\n\tif !IsNil(o.LoginHint) {\n\t\ttoSerialize[\"login_hint\"] = o.LoginHint\n\t}\n\tif !IsNil(o.UiLocales) {\n\t\ttoSerialize[\"ui_locales\"] = o.UiLocales\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *OAuth2ConsentRequestOpenIDConnectContext) UnmarshalJSON(data []byte) (err error) {\n\tvarOAuth2ConsentRequestOpenIDConnectContext := _OAuth2ConsentRequestOpenIDConnectContext{}\n\n\terr = json.Unmarshal(data, &varOAuth2ConsentRequestOpenIDConnectContext)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = OAuth2ConsentRequestOpenIDConnectContext(varOAuth2ConsentRequestOpenIDConnectContext)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"acr_values\")\n\t\tdelete(additionalProperties, \"display\")\n\t\tdelete(additionalProperties, \"id_token_hint_claims\")\n\t\tdelete(additionalProperties, \"login_hint\")\n\t\tdelete(additionalProperties, \"ui_locales\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableOAuth2ConsentRequestOpenIDConnectContext struct {\n\tvalue *OAuth2ConsentRequestOpenIDConnectContext\n\tisSet bool\n}\n\nfunc (v NullableOAuth2ConsentRequestOpenIDConnectContext) Get() *OAuth2ConsentRequestOpenIDConnectContext {\n\treturn v.value\n}\n\nfunc (v *NullableOAuth2ConsentRequestOpenIDConnectContext) Set(val *OAuth2ConsentRequestOpenIDConnectContext) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableOAuth2ConsentRequestOpenIDConnectContext) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableOAuth2ConsentRequestOpenIDConnectContext) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableOAuth2ConsentRequestOpenIDConnectContext(val *OAuth2ConsentRequestOpenIDConnectContext) *NullableOAuth2ConsentRequestOpenIDConnectContext {\n\treturn &NullableOAuth2ConsentRequestOpenIDConnectContext{value: val, isSet: true}\n}\n\nfunc (v NullableOAuth2ConsentRequestOpenIDConnectContext) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableOAuth2ConsentRequestOpenIDConnectContext) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_o_auth2_login_request.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the OAuth2LoginRequest type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &OAuth2LoginRequest{}\n\n// OAuth2LoginRequest OAuth2LoginRequest struct for OAuth2LoginRequest\ntype OAuth2LoginRequest struct {\n\t// ID is the identifier (\\\\\\\"login challenge\\\\\\\") of the login request. It is used to identify the session.\n\tChallenge   *string                                   `json:\"challenge,omitempty\"`\n\tClient      *OAuth2Client                             `json:\"client,omitempty\"`\n\tOidcContext *OAuth2ConsentRequestOpenIDConnectContext `json:\"oidc_context,omitempty\"`\n\t// RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters.\n\tRequestUrl                   *string  `json:\"request_url,omitempty\"`\n\tRequestedAccessTokenAudience []string `json:\"requested_access_token_audience,omitempty\"`\n\tRequestedScope               []string `json:\"requested_scope,omitempty\"`\n\t// SessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag) this ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false) this will be a new random value. This value is used as the \\\\\\\"sid\\\\\\\" parameter in the ID Token and in OIDC Front-/Back- channel logout. It's value can generally be used to associate consecutive login requests by a certain user.\n\tSessionId *string `json:\"session_id,omitempty\"`\n\t// Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL.  This feature allows you to update / set session information.\n\tSkip *bool `json:\"skip,omitempty\"`\n\t// Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail.\n\tSubject              *string `json:\"subject,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _OAuth2LoginRequest OAuth2LoginRequest\n\n// NewOAuth2LoginRequest instantiates a new OAuth2LoginRequest object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewOAuth2LoginRequest() *OAuth2LoginRequest {\n\tthis := OAuth2LoginRequest{}\n\treturn &this\n}\n\n// NewOAuth2LoginRequestWithDefaults instantiates a new OAuth2LoginRequest object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewOAuth2LoginRequestWithDefaults() *OAuth2LoginRequest {\n\tthis := OAuth2LoginRequest{}\n\treturn &this\n}\n\n// GetChallenge returns the Challenge field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetChallenge() string {\n\tif o == nil || IsNil(o.Challenge) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Challenge\n}\n\n// GetChallengeOk returns a tuple with the Challenge field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetChallengeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Challenge) {\n\t\treturn nil, false\n\t}\n\treturn o.Challenge, true\n}\n\n// HasChallenge returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasChallenge() bool {\n\tif o != nil && !IsNil(o.Challenge) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetChallenge gets a reference to the given string and assigns it to the Challenge field.\nfunc (o *OAuth2LoginRequest) SetChallenge(v string) {\n\to.Challenge = &v\n}\n\n// GetClient returns the Client field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetClient() OAuth2Client {\n\tif o == nil || IsNil(o.Client) {\n\t\tvar ret OAuth2Client\n\t\treturn ret\n\t}\n\treturn *o.Client\n}\n\n// GetClientOk returns a tuple with the Client field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetClientOk() (*OAuth2Client, bool) {\n\tif o == nil || IsNil(o.Client) {\n\t\treturn nil, false\n\t}\n\treturn o.Client, true\n}\n\n// HasClient returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasClient() bool {\n\tif o != nil && !IsNil(o.Client) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClient gets a reference to the given OAuth2Client and assigns it to the Client field.\nfunc (o *OAuth2LoginRequest) SetClient(v OAuth2Client) {\n\to.Client = &v\n}\n\n// GetOidcContext returns the OidcContext field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetOidcContext() OAuth2ConsentRequestOpenIDConnectContext {\n\tif o == nil || IsNil(o.OidcContext) {\n\t\tvar ret OAuth2ConsentRequestOpenIDConnectContext\n\t\treturn ret\n\t}\n\treturn *o.OidcContext\n}\n\n// GetOidcContextOk returns a tuple with the OidcContext field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetOidcContextOk() (*OAuth2ConsentRequestOpenIDConnectContext, bool) {\n\tif o == nil || IsNil(o.OidcContext) {\n\t\treturn nil, false\n\t}\n\treturn o.OidcContext, true\n}\n\n// HasOidcContext returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasOidcContext() bool {\n\tif o != nil && !IsNil(o.OidcContext) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOidcContext gets a reference to the given OAuth2ConsentRequestOpenIDConnectContext and assigns it to the OidcContext field.\nfunc (o *OAuth2LoginRequest) SetOidcContext(v OAuth2ConsentRequestOpenIDConnectContext) {\n\to.OidcContext = &v\n}\n\n// GetRequestUrl returns the RequestUrl field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetRequestUrl() string {\n\tif o == nil || IsNil(o.RequestUrl) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetRequestUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.RequestUrl) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestUrl, true\n}\n\n// HasRequestUrl returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasRequestUrl() bool {\n\tif o != nil && !IsNil(o.RequestUrl) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestUrl gets a reference to the given string and assigns it to the RequestUrl field.\nfunc (o *OAuth2LoginRequest) SetRequestUrl(v string) {\n\to.RequestUrl = &v\n}\n\n// GetRequestedAccessTokenAudience returns the RequestedAccessTokenAudience field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetRequestedAccessTokenAudience() []string {\n\tif o == nil || IsNil(o.RequestedAccessTokenAudience) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.RequestedAccessTokenAudience\n}\n\n// GetRequestedAccessTokenAudienceOk returns a tuple with the RequestedAccessTokenAudience field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetRequestedAccessTokenAudienceOk() ([]string, bool) {\n\tif o == nil || IsNil(o.RequestedAccessTokenAudience) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestedAccessTokenAudience, true\n}\n\n// HasRequestedAccessTokenAudience returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasRequestedAccessTokenAudience() bool {\n\tif o != nil && !IsNil(o.RequestedAccessTokenAudience) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestedAccessTokenAudience gets a reference to the given []string and assigns it to the RequestedAccessTokenAudience field.\nfunc (o *OAuth2LoginRequest) SetRequestedAccessTokenAudience(v []string) {\n\to.RequestedAccessTokenAudience = v\n}\n\n// GetRequestedScope returns the RequestedScope field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetRequestedScope() []string {\n\tif o == nil || IsNil(o.RequestedScope) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.RequestedScope\n}\n\n// GetRequestedScopeOk returns a tuple with the RequestedScope field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetRequestedScopeOk() ([]string, bool) {\n\tif o == nil || IsNil(o.RequestedScope) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestedScope, true\n}\n\n// HasRequestedScope returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasRequestedScope() bool {\n\tif o != nil && !IsNil(o.RequestedScope) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestedScope gets a reference to the given []string and assigns it to the RequestedScope field.\nfunc (o *OAuth2LoginRequest) SetRequestedScope(v []string) {\n\to.RequestedScope = v\n}\n\n// GetSessionId returns the SessionId field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetSessionId() string {\n\tif o == nil || IsNil(o.SessionId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionId\n}\n\n// GetSessionIdOk returns a tuple with the SessionId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetSessionIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionId) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionId, true\n}\n\n// HasSessionId returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasSessionId() bool {\n\tif o != nil && !IsNil(o.SessionId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionId gets a reference to the given string and assigns it to the SessionId field.\nfunc (o *OAuth2LoginRequest) SetSessionId(v string) {\n\to.SessionId = &v\n}\n\n// GetSkip returns the Skip field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetSkip() bool {\n\tif o == nil || IsNil(o.Skip) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.Skip\n}\n\n// GetSkipOk returns a tuple with the Skip field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetSkipOk() (*bool, bool) {\n\tif o == nil || IsNil(o.Skip) {\n\t\treturn nil, false\n\t}\n\treturn o.Skip, true\n}\n\n// HasSkip returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasSkip() bool {\n\tif o != nil && !IsNil(o.Skip) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSkip gets a reference to the given bool and assigns it to the Skip field.\nfunc (o *OAuth2LoginRequest) SetSkip(v bool) {\n\to.Skip = &v\n}\n\n// GetSubject returns the Subject field value if set, zero value otherwise.\nfunc (o *OAuth2LoginRequest) GetSubject() string {\n\tif o == nil || IsNil(o.Subject) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Subject\n}\n\n// GetSubjectOk returns a tuple with the Subject field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *OAuth2LoginRequest) GetSubjectOk() (*string, bool) {\n\tif o == nil || IsNil(o.Subject) {\n\t\treturn nil, false\n\t}\n\treturn o.Subject, true\n}\n\n// HasSubject returns a boolean if a field has been set.\nfunc (o *OAuth2LoginRequest) HasSubject() bool {\n\tif o != nil && !IsNil(o.Subject) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSubject gets a reference to the given string and assigns it to the Subject field.\nfunc (o *OAuth2LoginRequest) SetSubject(v string) {\n\to.Subject = &v\n}\n\nfunc (o OAuth2LoginRequest) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o OAuth2LoginRequest) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Challenge) {\n\t\ttoSerialize[\"challenge\"] = o.Challenge\n\t}\n\tif !IsNil(o.Client) {\n\t\ttoSerialize[\"client\"] = o.Client\n\t}\n\tif !IsNil(o.OidcContext) {\n\t\ttoSerialize[\"oidc_context\"] = o.OidcContext\n\t}\n\tif !IsNil(o.RequestUrl) {\n\t\ttoSerialize[\"request_url\"] = o.RequestUrl\n\t}\n\tif !IsNil(o.RequestedAccessTokenAudience) {\n\t\ttoSerialize[\"requested_access_token_audience\"] = o.RequestedAccessTokenAudience\n\t}\n\tif !IsNil(o.RequestedScope) {\n\t\ttoSerialize[\"requested_scope\"] = o.RequestedScope\n\t}\n\tif !IsNil(o.SessionId) {\n\t\ttoSerialize[\"session_id\"] = o.SessionId\n\t}\n\tif !IsNil(o.Skip) {\n\t\ttoSerialize[\"skip\"] = o.Skip\n\t}\n\tif !IsNil(o.Subject) {\n\t\ttoSerialize[\"subject\"] = o.Subject\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *OAuth2LoginRequest) UnmarshalJSON(data []byte) (err error) {\n\tvarOAuth2LoginRequest := _OAuth2LoginRequest{}\n\n\terr = json.Unmarshal(data, &varOAuth2LoginRequest)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = OAuth2LoginRequest(varOAuth2LoginRequest)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"challenge\")\n\t\tdelete(additionalProperties, \"client\")\n\t\tdelete(additionalProperties, \"oidc_context\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"requested_access_token_audience\")\n\t\tdelete(additionalProperties, \"requested_scope\")\n\t\tdelete(additionalProperties, \"session_id\")\n\t\tdelete(additionalProperties, \"skip\")\n\t\tdelete(additionalProperties, \"subject\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableOAuth2LoginRequest struct {\n\tvalue *OAuth2LoginRequest\n\tisSet bool\n}\n\nfunc (v NullableOAuth2LoginRequest) Get() *OAuth2LoginRequest {\n\treturn v.value\n}\n\nfunc (v *NullableOAuth2LoginRequest) Set(val *OAuth2LoginRequest) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableOAuth2LoginRequest) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableOAuth2LoginRequest) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableOAuth2LoginRequest(val *OAuth2LoginRequest) *NullableOAuth2LoginRequest {\n\treturn &NullableOAuth2LoginRequest{value: val, isSet: true}\n}\n\nfunc (v NullableOAuth2LoginRequest) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableOAuth2LoginRequest) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_patch_identities_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the PatchIdentitiesBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &PatchIdentitiesBody{}\n\n// PatchIdentitiesBody Patch Identities Body\ntype PatchIdentitiesBody struct {\n\t// Identities holds the list of patches to apply  required\n\tIdentities           []IdentityPatch `json:\"identities,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _PatchIdentitiesBody PatchIdentitiesBody\n\n// NewPatchIdentitiesBody instantiates a new PatchIdentitiesBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewPatchIdentitiesBody() *PatchIdentitiesBody {\n\tthis := PatchIdentitiesBody{}\n\treturn &this\n}\n\n// NewPatchIdentitiesBodyWithDefaults instantiates a new PatchIdentitiesBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewPatchIdentitiesBodyWithDefaults() *PatchIdentitiesBody {\n\tthis := PatchIdentitiesBody{}\n\treturn &this\n}\n\n// GetIdentities returns the Identities field value if set, zero value otherwise.\nfunc (o *PatchIdentitiesBody) GetIdentities() []IdentityPatch {\n\tif o == nil || IsNil(o.Identities) {\n\t\tvar ret []IdentityPatch\n\t\treturn ret\n\t}\n\treturn o.Identities\n}\n\n// GetIdentitiesOk returns a tuple with the Identities field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *PatchIdentitiesBody) GetIdentitiesOk() ([]IdentityPatch, bool) {\n\tif o == nil || IsNil(o.Identities) {\n\t\treturn nil, false\n\t}\n\treturn o.Identities, true\n}\n\n// HasIdentities returns a boolean if a field has been set.\nfunc (o *PatchIdentitiesBody) HasIdentities() bool {\n\tif o != nil && !IsNil(o.Identities) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentities gets a reference to the given []IdentityPatch and assigns it to the Identities field.\nfunc (o *PatchIdentitiesBody) SetIdentities(v []IdentityPatch) {\n\to.Identities = v\n}\n\nfunc (o PatchIdentitiesBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o PatchIdentitiesBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Identities) {\n\t\ttoSerialize[\"identities\"] = o.Identities\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *PatchIdentitiesBody) UnmarshalJSON(data []byte) (err error) {\n\tvarPatchIdentitiesBody := _PatchIdentitiesBody{}\n\n\terr = json.Unmarshal(data, &varPatchIdentitiesBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = PatchIdentitiesBody(varPatchIdentitiesBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"identities\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullablePatchIdentitiesBody struct {\n\tvalue *PatchIdentitiesBody\n\tisSet bool\n}\n\nfunc (v NullablePatchIdentitiesBody) Get() *PatchIdentitiesBody {\n\treturn v.value\n}\n\nfunc (v *NullablePatchIdentitiesBody) Set(val *PatchIdentitiesBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullablePatchIdentitiesBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullablePatchIdentitiesBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullablePatchIdentitiesBody(val *PatchIdentitiesBody) *NullablePatchIdentitiesBody {\n\treturn &NullablePatchIdentitiesBody{value: val, isSet: true}\n}\n\nfunc (v NullablePatchIdentitiesBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullablePatchIdentitiesBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_perform_native_logout_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the PerformNativeLogoutBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &PerformNativeLogoutBody{}\n\n// PerformNativeLogoutBody Perform Native Logout Request Body\ntype PerformNativeLogoutBody struct {\n\t// The Session Token  Invalidate this session token.\n\tSessionToken         string `json:\"session_token\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _PerformNativeLogoutBody PerformNativeLogoutBody\n\n// NewPerformNativeLogoutBody instantiates a new PerformNativeLogoutBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewPerformNativeLogoutBody(sessionToken string) *PerformNativeLogoutBody {\n\tthis := PerformNativeLogoutBody{}\n\tthis.SessionToken = sessionToken\n\treturn &this\n}\n\n// NewPerformNativeLogoutBodyWithDefaults instantiates a new PerformNativeLogoutBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewPerformNativeLogoutBodyWithDefaults() *PerformNativeLogoutBody {\n\tthis := PerformNativeLogoutBody{}\n\treturn &this\n}\n\n// GetSessionToken returns the SessionToken field value\nfunc (o *PerformNativeLogoutBody) GetSessionToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SessionToken\n}\n\n// GetSessionTokenOk returns a tuple with the SessionToken field value\n// and a boolean to check if the value has been set.\nfunc (o *PerformNativeLogoutBody) GetSessionTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SessionToken, true\n}\n\n// SetSessionToken sets field value\nfunc (o *PerformNativeLogoutBody) SetSessionToken(v string) {\n\to.SessionToken = v\n}\n\nfunc (o PerformNativeLogoutBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o PerformNativeLogoutBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"session_token\"] = o.SessionToken\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *PerformNativeLogoutBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"session_token\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarPerformNativeLogoutBody := _PerformNativeLogoutBody{}\n\n\terr = json.Unmarshal(data, &varPerformNativeLogoutBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = PerformNativeLogoutBody(varPerformNativeLogoutBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullablePerformNativeLogoutBody struct {\n\tvalue *PerformNativeLogoutBody\n\tisSet bool\n}\n\nfunc (v NullablePerformNativeLogoutBody) Get() *PerformNativeLogoutBody {\n\treturn v.value\n}\n\nfunc (v *NullablePerformNativeLogoutBody) Set(val *PerformNativeLogoutBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullablePerformNativeLogoutBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullablePerformNativeLogoutBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullablePerformNativeLogoutBody(val *PerformNativeLogoutBody) *NullablePerformNativeLogoutBody {\n\treturn &NullablePerformNativeLogoutBody{value: val, isSet: true}\n}\n\nfunc (v NullablePerformNativeLogoutBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullablePerformNativeLogoutBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_provider.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the Provider type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Provider{}\n\n// Provider struct for Provider\ntype Provider struct {\n\t// The RP's client identifier, issued by the IdP.\n\tClientId *string `json:\"client_id,omitempty\"`\n\t// A full path of the IdP config file.\n\tConfigUrl *string `json:\"config_url,omitempty\"`\n\t// By specifying one of domain_hints values provided by the accounts endpoints, the FedCM dialog selectively shows the specified account.\n\tDomainHint *string `json:\"domain_hint,omitempty\"`\n\t// Array of strings that specifies the user information (\\\"name\\\", \\\" email\\\", \\\"picture\\\") that RP needs IdP to share with them.  Note: Field API is supported by Chrome 132 and later.\n\tFields []string `json:\"fields,omitempty\"`\n\t// By specifying one of login_hints values provided by the accounts endpoints, the FedCM dialog selectively shows the specified account.\n\tLoginHint *string `json:\"login_hint,omitempty\"`\n\t// A random string to ensure the response is issued for this specific request. Prevents replay attacks.\n\tNonce *string `json:\"nonce,omitempty\"`\n\t// Custom object that allows to specify additional key-value parameters: scope: A string value containing additional permissions that RP needs to request, for example \\\" drive.readonly calendar.readonly\\\" nonce: A random string to ensure the response is issued for this specific request. Prevents replay attacks.  Other custom key-value parameters.  Note: parameters is supported from Chrome 132.\n\tParameters           *map[string]string `json:\"parameters,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Provider Provider\n\n// NewProvider instantiates a new Provider object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewProvider() *Provider {\n\tthis := Provider{}\n\treturn &this\n}\n\n// NewProviderWithDefaults instantiates a new Provider object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewProviderWithDefaults() *Provider {\n\tthis := Provider{}\n\treturn &this\n}\n\n// GetClientId returns the ClientId field value if set, zero value otherwise.\nfunc (o *Provider) GetClientId() string {\n\tif o == nil || IsNil(o.ClientId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ClientId\n}\n\n// GetClientIdOk returns a tuple with the ClientId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetClientIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ClientId) {\n\t\treturn nil, false\n\t}\n\treturn o.ClientId, true\n}\n\n// HasClientId returns a boolean if a field has been set.\nfunc (o *Provider) HasClientId() bool {\n\tif o != nil && !IsNil(o.ClientId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClientId gets a reference to the given string and assigns it to the ClientId field.\nfunc (o *Provider) SetClientId(v string) {\n\to.ClientId = &v\n}\n\n// GetConfigUrl returns the ConfigUrl field value if set, zero value otherwise.\nfunc (o *Provider) GetConfigUrl() string {\n\tif o == nil || IsNil(o.ConfigUrl) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ConfigUrl\n}\n\n// GetConfigUrlOk returns a tuple with the ConfigUrl field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetConfigUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.ConfigUrl) {\n\t\treturn nil, false\n\t}\n\treturn o.ConfigUrl, true\n}\n\n// HasConfigUrl returns a boolean if a field has been set.\nfunc (o *Provider) HasConfigUrl() bool {\n\tif o != nil && !IsNil(o.ConfigUrl) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetConfigUrl gets a reference to the given string and assigns it to the ConfigUrl field.\nfunc (o *Provider) SetConfigUrl(v string) {\n\to.ConfigUrl = &v\n}\n\n// GetDomainHint returns the DomainHint field value if set, zero value otherwise.\nfunc (o *Provider) GetDomainHint() string {\n\tif o == nil || IsNil(o.DomainHint) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.DomainHint\n}\n\n// GetDomainHintOk returns a tuple with the DomainHint field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetDomainHintOk() (*string, bool) {\n\tif o == nil || IsNil(o.DomainHint) {\n\t\treturn nil, false\n\t}\n\treturn o.DomainHint, true\n}\n\n// HasDomainHint returns a boolean if a field has been set.\nfunc (o *Provider) HasDomainHint() bool {\n\tif o != nil && !IsNil(o.DomainHint) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDomainHint gets a reference to the given string and assigns it to the DomainHint field.\nfunc (o *Provider) SetDomainHint(v string) {\n\to.DomainHint = &v\n}\n\n// GetFields returns the Fields field value if set, zero value otherwise.\nfunc (o *Provider) GetFields() []string {\n\tif o == nil || IsNil(o.Fields) {\n\t\tvar ret []string\n\t\treturn ret\n\t}\n\treturn o.Fields\n}\n\n// GetFieldsOk returns a tuple with the Fields field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetFieldsOk() ([]string, bool) {\n\tif o == nil || IsNil(o.Fields) {\n\t\treturn nil, false\n\t}\n\treturn o.Fields, true\n}\n\n// HasFields returns a boolean if a field has been set.\nfunc (o *Provider) HasFields() bool {\n\tif o != nil && !IsNil(o.Fields) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFields gets a reference to the given []string and assigns it to the Fields field.\nfunc (o *Provider) SetFields(v []string) {\n\to.Fields = v\n}\n\n// GetLoginHint returns the LoginHint field value if set, zero value otherwise.\nfunc (o *Provider) GetLoginHint() string {\n\tif o == nil || IsNil(o.LoginHint) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.LoginHint\n}\n\n// GetLoginHintOk returns a tuple with the LoginHint field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetLoginHintOk() (*string, bool) {\n\tif o == nil || IsNil(o.LoginHint) {\n\t\treturn nil, false\n\t}\n\treturn o.LoginHint, true\n}\n\n// HasLoginHint returns a boolean if a field has been set.\nfunc (o *Provider) HasLoginHint() bool {\n\tif o != nil && !IsNil(o.LoginHint) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLoginHint gets a reference to the given string and assigns it to the LoginHint field.\nfunc (o *Provider) SetLoginHint(v string) {\n\to.LoginHint = &v\n}\n\n// GetNonce returns the Nonce field value if set, zero value otherwise.\nfunc (o *Provider) GetNonce() string {\n\tif o == nil || IsNil(o.Nonce) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Nonce\n}\n\n// GetNonceOk returns a tuple with the Nonce field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetNonceOk() (*string, bool) {\n\tif o == nil || IsNil(o.Nonce) {\n\t\treturn nil, false\n\t}\n\treturn o.Nonce, true\n}\n\n// HasNonce returns a boolean if a field has been set.\nfunc (o *Provider) HasNonce() bool {\n\tif o != nil && !IsNil(o.Nonce) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetNonce gets a reference to the given string and assigns it to the Nonce field.\nfunc (o *Provider) SetNonce(v string) {\n\to.Nonce = &v\n}\n\n// GetParameters returns the Parameters field value if set, zero value otherwise.\nfunc (o *Provider) GetParameters() map[string]string {\n\tif o == nil || IsNil(o.Parameters) {\n\t\tvar ret map[string]string\n\t\treturn ret\n\t}\n\treturn *o.Parameters\n}\n\n// GetParametersOk returns a tuple with the Parameters field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Provider) GetParametersOk() (*map[string]string, bool) {\n\tif o == nil || IsNil(o.Parameters) {\n\t\treturn nil, false\n\t}\n\treturn o.Parameters, true\n}\n\n// HasParameters returns a boolean if a field has been set.\nfunc (o *Provider) HasParameters() bool {\n\tif o != nil && !IsNil(o.Parameters) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetParameters gets a reference to the given map[string]string and assigns it to the Parameters field.\nfunc (o *Provider) SetParameters(v map[string]string) {\n\to.Parameters = &v\n}\n\nfunc (o Provider) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Provider) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ClientId) {\n\t\ttoSerialize[\"client_id\"] = o.ClientId\n\t}\n\tif !IsNil(o.ConfigUrl) {\n\t\ttoSerialize[\"config_url\"] = o.ConfigUrl\n\t}\n\tif !IsNil(o.DomainHint) {\n\t\ttoSerialize[\"domain_hint\"] = o.DomainHint\n\t}\n\tif !IsNil(o.Fields) {\n\t\ttoSerialize[\"fields\"] = o.Fields\n\t}\n\tif !IsNil(o.LoginHint) {\n\t\ttoSerialize[\"login_hint\"] = o.LoginHint\n\t}\n\tif !IsNil(o.Nonce) {\n\t\ttoSerialize[\"nonce\"] = o.Nonce\n\t}\n\tif !IsNil(o.Parameters) {\n\t\ttoSerialize[\"parameters\"] = o.Parameters\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Provider) UnmarshalJSON(data []byte) (err error) {\n\tvarProvider := _Provider{}\n\n\terr = json.Unmarshal(data, &varProvider)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Provider(varProvider)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"client_id\")\n\t\tdelete(additionalProperties, \"config_url\")\n\t\tdelete(additionalProperties, \"domain_hint\")\n\t\tdelete(additionalProperties, \"fields\")\n\t\tdelete(additionalProperties, \"login_hint\")\n\t\tdelete(additionalProperties, \"nonce\")\n\t\tdelete(additionalProperties, \"parameters\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableProvider struct {\n\tvalue *Provider\n\tisSet bool\n}\n\nfunc (v NullableProvider) Get() *Provider {\n\treturn v.value\n}\n\nfunc (v *NullableProvider) Set(val *Provider) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableProvider) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableProvider) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableProvider(val *Provider) *NullableProvider {\n\treturn &NullableProvider{value: val, isSet: true}\n}\n\nfunc (v NullableProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableProvider) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_recovery_code_for_identity.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RecoveryCodeForIdentity type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RecoveryCodeForIdentity{}\n\n// RecoveryCodeForIdentity Used when an administrator creates a recovery code for an identity.\ntype RecoveryCodeForIdentity struct {\n\t// Expires At is the timestamp of when the recovery flow expires  The timestamp when the recovery code expires.\n\tExpiresAt *time.Time `json:\"expires_at,omitempty\"`\n\t// RecoveryCode is the code that can be used to recover the account\n\tRecoveryCode string `json:\"recovery_code\"`\n\t// RecoveryLink with flow  This link opens the recovery UI with an empty `code` field.\n\tRecoveryLink         string `json:\"recovery_link\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RecoveryCodeForIdentity RecoveryCodeForIdentity\n\n// NewRecoveryCodeForIdentity instantiates a new RecoveryCodeForIdentity object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRecoveryCodeForIdentity(recoveryCode string, recoveryLink string) *RecoveryCodeForIdentity {\n\tthis := RecoveryCodeForIdentity{}\n\tthis.RecoveryCode = recoveryCode\n\tthis.RecoveryLink = recoveryLink\n\treturn &this\n}\n\n// NewRecoveryCodeForIdentityWithDefaults instantiates a new RecoveryCodeForIdentity object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRecoveryCodeForIdentityWithDefaults() *RecoveryCodeForIdentity {\n\tthis := RecoveryCodeForIdentity{}\n\treturn &this\n}\n\n// GetExpiresAt returns the ExpiresAt field value if set, zero value otherwise.\nfunc (o *RecoveryCodeForIdentity) GetExpiresAt() time.Time {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryCodeForIdentity) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresAt, true\n}\n\n// HasExpiresAt returns a boolean if a field has been set.\nfunc (o *RecoveryCodeForIdentity) HasExpiresAt() bool {\n\tif o != nil && !IsNil(o.ExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresAt gets a reference to the given time.Time and assigns it to the ExpiresAt field.\nfunc (o *RecoveryCodeForIdentity) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = &v\n}\n\n// GetRecoveryCode returns the RecoveryCode field value\nfunc (o *RecoveryCodeForIdentity) GetRecoveryCode() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RecoveryCode\n}\n\n// GetRecoveryCodeOk returns a tuple with the RecoveryCode field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryCodeForIdentity) GetRecoveryCodeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RecoveryCode, true\n}\n\n// SetRecoveryCode sets field value\nfunc (o *RecoveryCodeForIdentity) SetRecoveryCode(v string) {\n\to.RecoveryCode = v\n}\n\n// GetRecoveryLink returns the RecoveryLink field value\nfunc (o *RecoveryCodeForIdentity) GetRecoveryLink() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RecoveryLink\n}\n\n// GetRecoveryLinkOk returns a tuple with the RecoveryLink field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryCodeForIdentity) GetRecoveryLinkOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RecoveryLink, true\n}\n\n// SetRecoveryLink sets field value\nfunc (o *RecoveryCodeForIdentity) SetRecoveryLink(v string) {\n\to.RecoveryLink = v\n}\n\nfunc (o RecoveryCodeForIdentity) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RecoveryCodeForIdentity) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ExpiresAt) {\n\t\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\t}\n\ttoSerialize[\"recovery_code\"] = o.RecoveryCode\n\ttoSerialize[\"recovery_link\"] = o.RecoveryLink\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RecoveryCodeForIdentity) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"recovery_code\",\n\t\t\"recovery_link\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRecoveryCodeForIdentity := _RecoveryCodeForIdentity{}\n\n\terr = json.Unmarshal(data, &varRecoveryCodeForIdentity)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RecoveryCodeForIdentity(varRecoveryCodeForIdentity)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"recovery_code\")\n\t\tdelete(additionalProperties, \"recovery_link\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRecoveryCodeForIdentity struct {\n\tvalue *RecoveryCodeForIdentity\n\tisSet bool\n}\n\nfunc (v NullableRecoveryCodeForIdentity) Get() *RecoveryCodeForIdentity {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryCodeForIdentity) Set(val *RecoveryCodeForIdentity) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryCodeForIdentity) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryCodeForIdentity) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryCodeForIdentity(val *RecoveryCodeForIdentity) *NullableRecoveryCodeForIdentity {\n\treturn &NullableRecoveryCodeForIdentity{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryCodeForIdentity) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryCodeForIdentity) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_recovery_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RecoveryFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RecoveryFlow{}\n\n// RecoveryFlow This request is used when an identity wants to recover their account.  We recommend reading the [Account Recovery Documentation](../self-service/flows/password-reset-account-recovery)\ntype RecoveryFlow struct {\n\t// Active, if set, contains the recovery method that is being used. It is initially not set.\n\tActive *string `json:\"active,omitempty\"`\n\t// Contains possible actions that could follow this flow\n\tContinueWith []ContinueWith `json:\"continue_with,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the request expires. If the user still wishes to update the setting, a new request has to be initiated.\n\tExpiresAt time.Time `json:\"expires_at\"`\n\t// ID represents the request's unique ID. When performing the recovery flow, this represents the id in the recovery ui's query parameter: http://<selfservice.flows.recovery.ui_url>?request=<id>\n\tId string `json:\"id\"`\n\t// IssuedAt is the time (UTC) when the request occurred.\n\tIssuedAt time.Time `json:\"issued_at\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl string `json:\"request_url\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// State represents the state of this request:  choose_method: ask the user to choose a method (e.g. recover account via email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the recovery challenge was passed.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the recovery flow to hooks and email templates\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType                 string      `json:\"type\"`\n\tUi                   UiContainer `json:\"ui\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RecoveryFlow RecoveryFlow\n\n// NewRecoveryFlow instantiates a new RecoveryFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRecoveryFlow(expiresAt time.Time, id string, issuedAt time.Time, requestUrl string, state interface{}, type_ string, ui UiContainer) *RecoveryFlow {\n\tthis := RecoveryFlow{}\n\tthis.ExpiresAt = expiresAt\n\tthis.Id = id\n\tthis.IssuedAt = issuedAt\n\tthis.RequestUrl = requestUrl\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewRecoveryFlowWithDefaults instantiates a new RecoveryFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRecoveryFlowWithDefaults() *RecoveryFlow {\n\tthis := RecoveryFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *RecoveryFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *RecoveryFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *RecoveryFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetContinueWith returns the ContinueWith field value if set, zero value otherwise.\nfunc (o *RecoveryFlow) GetContinueWith() []ContinueWith {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\tvar ret []ContinueWith\n\t\treturn ret\n\t}\n\treturn o.ContinueWith\n}\n\n// GetContinueWithOk returns a tuple with the ContinueWith field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetContinueWithOk() ([]ContinueWith, bool) {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\treturn nil, false\n\t}\n\treturn o.ContinueWith, true\n}\n\n// HasContinueWith returns a boolean if a field has been set.\nfunc (o *RecoveryFlow) HasContinueWith() bool {\n\tif o != nil && !IsNil(o.ContinueWith) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContinueWith gets a reference to the given []ContinueWith and assigns it to the ContinueWith field.\nfunc (o *RecoveryFlow) SetContinueWith(v []ContinueWith) {\n\to.ContinueWith = v\n}\n\n// GetExpiresAt returns the ExpiresAt field value\nfunc (o *RecoveryFlow) GetExpiresAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.ExpiresAt, true\n}\n\n// SetExpiresAt sets field value\nfunc (o *RecoveryFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = v\n}\n\n// GetId returns the Id field value\nfunc (o *RecoveryFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *RecoveryFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIssuedAt returns the IssuedAt field value\nfunc (o *RecoveryFlow) GetIssuedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IssuedAt, true\n}\n\n// SetIssuedAt sets field value\nfunc (o *RecoveryFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = v\n}\n\n// GetRequestUrl returns the RequestUrl field value\nfunc (o *RecoveryFlow) GetRequestUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RequestUrl, true\n}\n\n// SetRequestUrl sets field value\nfunc (o *RecoveryFlow) SetRequestUrl(v string) {\n\to.RequestUrl = v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *RecoveryFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *RecoveryFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *RecoveryFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *RecoveryFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *RecoveryFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *RecoveryFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *RecoveryFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *RecoveryFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *RecoveryFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *RecoveryFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *RecoveryFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *RecoveryFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *RecoveryFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\nfunc (o RecoveryFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RecoveryFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.ContinueWith) {\n\t\ttoSerialize[\"continue_with\"] = o.ContinueWith\n\t}\n\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\ttoSerialize[\"request_url\"] = o.RequestUrl\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RecoveryFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"expires_at\",\n\t\t\"id\",\n\t\t\"issued_at\",\n\t\t\"request_url\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRecoveryFlow := _RecoveryFlow{}\n\n\terr = json.Unmarshal(data, &varRecoveryFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RecoveryFlow(varRecoveryFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"continue_with\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRecoveryFlow struct {\n\tvalue *RecoveryFlow\n\tisSet bool\n}\n\nfunc (v NullableRecoveryFlow) Get() *RecoveryFlow {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryFlow) Set(val *RecoveryFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryFlow(val *RecoveryFlow) *NullableRecoveryFlow {\n\treturn &NullableRecoveryFlow{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_recovery_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// RecoveryFlowState The experimental state represents the state of a recovery flow. This field is EXPERIMENTAL and subject to change!\ntype RecoveryFlowState string\n\n// List of recoveryFlowState\nconst (\n\tRECOVERYFLOWSTATE_CHOOSE_METHOD    RecoveryFlowState = \"choose_method\"\n\tRECOVERYFLOWSTATE_SENT_EMAIL       RecoveryFlowState = \"sent_email\"\n\tRECOVERYFLOWSTATE_PASSED_CHALLENGE RecoveryFlowState = \"passed_challenge\"\n)\n\n// All allowed values of RecoveryFlowState enum\nvar AllowedRecoveryFlowStateEnumValues = []RecoveryFlowState{\n\t\"choose_method\",\n\t\"sent_email\",\n\t\"passed_challenge\",\n}\n\nfunc (v *RecoveryFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := RecoveryFlowState(value)\n\tfor _, existing := range AllowedRecoveryFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid RecoveryFlowState\", value)\n}\n\n// NewRecoveryFlowStateFromValue returns a pointer to a valid RecoveryFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewRecoveryFlowStateFromValue(v string) (*RecoveryFlowState, error) {\n\tev := RecoveryFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for RecoveryFlowState: valid values are %v\", v, AllowedRecoveryFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v RecoveryFlowState) IsValid() bool {\n\tfor _, existing := range AllowedRecoveryFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to recoveryFlowState value\nfunc (v RecoveryFlowState) Ptr() *RecoveryFlowState {\n\treturn &v\n}\n\ntype NullableRecoveryFlowState struct {\n\tvalue *RecoveryFlowState\n\tisSet bool\n}\n\nfunc (v NullableRecoveryFlowState) Get() *RecoveryFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryFlowState) Set(val *RecoveryFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryFlowState(val *RecoveryFlowState) *NullableRecoveryFlowState {\n\treturn &NullableRecoveryFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_recovery_identity_address.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RecoveryIdentityAddress type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RecoveryIdentityAddress{}\n\n// RecoveryIdentityAddress struct for RecoveryIdentityAddress\ntype RecoveryIdentityAddress struct {\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\tId        *string    `json:\"id,omitempty\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt            *time.Time `json:\"updated_at,omitempty\"`\n\tValue                string     `json:\"value\"`\n\tVia                  string     `json:\"via\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RecoveryIdentityAddress RecoveryIdentityAddress\n\n// NewRecoveryIdentityAddress instantiates a new RecoveryIdentityAddress object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRecoveryIdentityAddress(value string, via string) *RecoveryIdentityAddress {\n\tthis := RecoveryIdentityAddress{}\n\tthis.Value = value\n\tthis.Via = via\n\treturn &this\n}\n\n// NewRecoveryIdentityAddressWithDefaults instantiates a new RecoveryIdentityAddress object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRecoveryIdentityAddressWithDefaults() *RecoveryIdentityAddress {\n\tthis := RecoveryIdentityAddress{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *RecoveryIdentityAddress) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *RecoveryIdentityAddress) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *RecoveryIdentityAddress) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetId returns the Id field value if set, zero value otherwise.\nfunc (o *RecoveryIdentityAddress) GetId() string {\n\tif o == nil || IsNil(o.Id) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.Id) {\n\t\treturn nil, false\n\t}\n\treturn o.Id, true\n}\n\n// HasId returns a boolean if a field has been set.\nfunc (o *RecoveryIdentityAddress) HasId() bool {\n\tif o != nil && !IsNil(o.Id) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetId gets a reference to the given string and assigns it to the Id field.\nfunc (o *RecoveryIdentityAddress) SetId(v string) {\n\to.Id = &v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *RecoveryIdentityAddress) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *RecoveryIdentityAddress) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *RecoveryIdentityAddress) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetValue returns the Value field value\nfunc (o *RecoveryIdentityAddress) GetValue() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Value\n}\n\n// GetValueOk returns a tuple with the Value field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetValueOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Value, true\n}\n\n// SetValue sets field value\nfunc (o *RecoveryIdentityAddress) SetValue(v string) {\n\to.Value = v\n}\n\n// GetVia returns the Via field value\nfunc (o *RecoveryIdentityAddress) GetVia() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Via\n}\n\n// GetViaOk returns a tuple with the Via field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryIdentityAddress) GetViaOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Via, true\n}\n\n// SetVia sets field value\nfunc (o *RecoveryIdentityAddress) SetVia(v string) {\n\to.Via = v\n}\n\nfunc (o RecoveryIdentityAddress) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RecoveryIdentityAddress) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Id) {\n\t\ttoSerialize[\"id\"] = o.Id\n\t}\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\ttoSerialize[\"value\"] = o.Value\n\ttoSerialize[\"via\"] = o.Via\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RecoveryIdentityAddress) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"value\",\n\t\t\"via\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRecoveryIdentityAddress := _RecoveryIdentityAddress{}\n\n\terr = json.Unmarshal(data, &varRecoveryIdentityAddress)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RecoveryIdentityAddress(varRecoveryIdentityAddress)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"value\")\n\t\tdelete(additionalProperties, \"via\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRecoveryIdentityAddress struct {\n\tvalue *RecoveryIdentityAddress\n\tisSet bool\n}\n\nfunc (v NullableRecoveryIdentityAddress) Get() *RecoveryIdentityAddress {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryIdentityAddress) Set(val *RecoveryIdentityAddress) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryIdentityAddress) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryIdentityAddress) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryIdentityAddress(val *RecoveryIdentityAddress) *NullableRecoveryIdentityAddress {\n\treturn &NullableRecoveryIdentityAddress{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryIdentityAddress) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryIdentityAddress) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_recovery_link_for_identity.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RecoveryLinkForIdentity type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RecoveryLinkForIdentity{}\n\n// RecoveryLinkForIdentity Used when an administrator creates a recovery link for an identity.\ntype RecoveryLinkForIdentity struct {\n\t// Recovery Link Expires At  The timestamp when the recovery link expires.\n\tExpiresAt *time.Time `json:\"expires_at,omitempty\"`\n\t// Recovery Link  This link can be used to recover the account.\n\tRecoveryLink         string `json:\"recovery_link\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RecoveryLinkForIdentity RecoveryLinkForIdentity\n\n// NewRecoveryLinkForIdentity instantiates a new RecoveryLinkForIdentity object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRecoveryLinkForIdentity(recoveryLink string) *RecoveryLinkForIdentity {\n\tthis := RecoveryLinkForIdentity{}\n\tthis.RecoveryLink = recoveryLink\n\treturn &this\n}\n\n// NewRecoveryLinkForIdentityWithDefaults instantiates a new RecoveryLinkForIdentity object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRecoveryLinkForIdentityWithDefaults() *RecoveryLinkForIdentity {\n\tthis := RecoveryLinkForIdentity{}\n\treturn &this\n}\n\n// GetExpiresAt returns the ExpiresAt field value if set, zero value otherwise.\nfunc (o *RecoveryLinkForIdentity) GetExpiresAt() time.Time {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryLinkForIdentity) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresAt, true\n}\n\n// HasExpiresAt returns a boolean if a field has been set.\nfunc (o *RecoveryLinkForIdentity) HasExpiresAt() bool {\n\tif o != nil && !IsNil(o.ExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresAt gets a reference to the given time.Time and assigns it to the ExpiresAt field.\nfunc (o *RecoveryLinkForIdentity) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = &v\n}\n\n// GetRecoveryLink returns the RecoveryLink field value\nfunc (o *RecoveryLinkForIdentity) GetRecoveryLink() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RecoveryLink\n}\n\n// GetRecoveryLinkOk returns a tuple with the RecoveryLink field value\n// and a boolean to check if the value has been set.\nfunc (o *RecoveryLinkForIdentity) GetRecoveryLinkOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RecoveryLink, true\n}\n\n// SetRecoveryLink sets field value\nfunc (o *RecoveryLinkForIdentity) SetRecoveryLink(v string) {\n\to.RecoveryLink = v\n}\n\nfunc (o RecoveryLinkForIdentity) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RecoveryLinkForIdentity) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ExpiresAt) {\n\t\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\t}\n\ttoSerialize[\"recovery_link\"] = o.RecoveryLink\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RecoveryLinkForIdentity) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"recovery_link\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRecoveryLinkForIdentity := _RecoveryLinkForIdentity{}\n\n\terr = json.Unmarshal(data, &varRecoveryLinkForIdentity)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RecoveryLinkForIdentity(varRecoveryLinkForIdentity)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"recovery_link\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRecoveryLinkForIdentity struct {\n\tvalue *RecoveryLinkForIdentity\n\tisSet bool\n}\n\nfunc (v NullableRecoveryLinkForIdentity) Get() *RecoveryLinkForIdentity {\n\treturn v.value\n}\n\nfunc (v *NullableRecoveryLinkForIdentity) Set(val *RecoveryLinkForIdentity) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRecoveryLinkForIdentity) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRecoveryLinkForIdentity) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRecoveryLinkForIdentity(val *RecoveryLinkForIdentity) *NullableRecoveryLinkForIdentity {\n\treturn &NullableRecoveryLinkForIdentity{value: val, isSet: true}\n}\n\nfunc (v NullableRecoveryLinkForIdentity) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRecoveryLinkForIdentity) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_registration_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the RegistrationFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &RegistrationFlow{}\n\n// RegistrationFlow struct for RegistrationFlow\ntype RegistrationFlow struct {\n\t// Active, if set, contains the registration method that is being used. It is initially not set. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\tActive *string `json:\"active,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in, a new flow has to be initiated.\n\tExpiresAt time.Time `json:\"expires_at\"`\n\t// ID represents the flow's unique ID. When performing the registration flow, this represents the id in the registration ui's query parameter: http://<selfservice.flows.registration.ui_url>/?flow=<id>\n\tId string `json:\"id\"`\n\t// IdentitySchema optionally holds the ID of the identity schema that is used for this flow. This value can be set by the user when creating the flow and should be retained when the flow is saved or converted to another flow.\n\tIdentitySchema *string `json:\"identity_schema,omitempty\"`\n\t// IssuedAt is the time (UTC) when the flow occurred.\n\tIssuedAt time.Time `json:\"issued_at\"`\n\t// Ory OAuth 2.0 Login Challenge.  This value is set using the `login_challenge` query parameter of the registration and login endpoints. If set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\n\tOauth2LoginChallenge *string             `json:\"oauth2_login_challenge,omitempty\"`\n\tOauth2LoginRequest   *OAuth2LoginRequest `json:\"oauth2_login_request,omitempty\"`\n\tOrganizationId       NullableString      `json:\"organization_id,omitempty\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl string `json:\"request_url\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the flow has been completed. This is only set if the client has requested a session token exchange code, and if the flow is of type \\\"api\\\", and only on creating the flow.\n\tSessionTokenExchangeCode *string `json:\"session_token_exchange_code,omitempty\"`\n\t// State represents the state of this request:  choose_method: ask the user to choose a method (e.g. registration with email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the registration challenge was passed.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the registration to a webhook\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType                 string      `json:\"type\"`\n\tUi                   UiContainer `json:\"ui\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _RegistrationFlow RegistrationFlow\n\n// NewRegistrationFlow instantiates a new RegistrationFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewRegistrationFlow(expiresAt time.Time, id string, issuedAt time.Time, requestUrl string, state interface{}, type_ string, ui UiContainer) *RegistrationFlow {\n\tthis := RegistrationFlow{}\n\tthis.ExpiresAt = expiresAt\n\tthis.Id = id\n\tthis.IssuedAt = issuedAt\n\tthis.RequestUrl = requestUrl\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewRegistrationFlowWithDefaults instantiates a new RegistrationFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewRegistrationFlowWithDefaults() *RegistrationFlow {\n\tthis := RegistrationFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *RegistrationFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetExpiresAt returns the ExpiresAt field value\nfunc (o *RegistrationFlow) GetExpiresAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.ExpiresAt, true\n}\n\n// SetExpiresAt sets field value\nfunc (o *RegistrationFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = v\n}\n\n// GetId returns the Id field value\nfunc (o *RegistrationFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *RegistrationFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIdentitySchema returns the IdentitySchema field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetIdentitySchema() string {\n\tif o == nil || IsNil(o.IdentitySchema) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdentitySchema\n}\n\n// GetIdentitySchemaOk returns a tuple with the IdentitySchema field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetIdentitySchemaOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdentitySchema) {\n\t\treturn nil, false\n\t}\n\treturn o.IdentitySchema, true\n}\n\n// HasIdentitySchema returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasIdentitySchema() bool {\n\tif o != nil && !IsNil(o.IdentitySchema) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentitySchema gets a reference to the given string and assigns it to the IdentitySchema field.\nfunc (o *RegistrationFlow) SetIdentitySchema(v string) {\n\to.IdentitySchema = &v\n}\n\n// GetIssuedAt returns the IssuedAt field value\nfunc (o *RegistrationFlow) GetIssuedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IssuedAt, true\n}\n\n// SetIssuedAt sets field value\nfunc (o *RegistrationFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = v\n}\n\n// GetOauth2LoginChallenge returns the Oauth2LoginChallenge field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetOauth2LoginChallenge() string {\n\tif o == nil || IsNil(o.Oauth2LoginChallenge) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Oauth2LoginChallenge\n}\n\n// GetOauth2LoginChallengeOk returns a tuple with the Oauth2LoginChallenge field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetOauth2LoginChallengeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Oauth2LoginChallenge) {\n\t\treturn nil, false\n\t}\n\treturn o.Oauth2LoginChallenge, true\n}\n\n// HasOauth2LoginChallenge returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasOauth2LoginChallenge() bool {\n\tif o != nil && !IsNil(o.Oauth2LoginChallenge) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOauth2LoginChallenge gets a reference to the given string and assigns it to the Oauth2LoginChallenge field.\nfunc (o *RegistrationFlow) SetOauth2LoginChallenge(v string) {\n\to.Oauth2LoginChallenge = &v\n}\n\n// GetOauth2LoginRequest returns the Oauth2LoginRequest field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetOauth2LoginRequest() OAuth2LoginRequest {\n\tif o == nil || IsNil(o.Oauth2LoginRequest) {\n\t\tvar ret OAuth2LoginRequest\n\t\treturn ret\n\t}\n\treturn *o.Oauth2LoginRequest\n}\n\n// GetOauth2LoginRequestOk returns a tuple with the Oauth2LoginRequest field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetOauth2LoginRequestOk() (*OAuth2LoginRequest, bool) {\n\tif o == nil || IsNil(o.Oauth2LoginRequest) {\n\t\treturn nil, false\n\t}\n\treturn o.Oauth2LoginRequest, true\n}\n\n// HasOauth2LoginRequest returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasOauth2LoginRequest() bool {\n\tif o != nil && !IsNil(o.Oauth2LoginRequest) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOauth2LoginRequest gets a reference to the given OAuth2LoginRequest and assigns it to the Oauth2LoginRequest field.\nfunc (o *RegistrationFlow) SetOauth2LoginRequest(v OAuth2LoginRequest) {\n\to.Oauth2LoginRequest = &v\n}\n\n// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *RegistrationFlow) GetOrganizationId() string {\n\tif o == nil || IsNil(o.OrganizationId.Get()) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OrganizationId.Get()\n}\n\n// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *RegistrationFlow) GetOrganizationIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.OrganizationId.Get(), o.OrganizationId.IsSet()\n}\n\n// HasOrganizationId returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasOrganizationId() bool {\n\tif o != nil && o.OrganizationId.IsSet() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganizationId gets a reference to the given NullableString and assigns it to the OrganizationId field.\nfunc (o *RegistrationFlow) SetOrganizationId(v string) {\n\to.OrganizationId.Set(&v)\n}\n\n// SetOrganizationIdNil sets the value for OrganizationId to be an explicit nil\nfunc (o *RegistrationFlow) SetOrganizationIdNil() {\n\to.OrganizationId.Set(nil)\n}\n\n// UnsetOrganizationId ensures that no value is present for OrganizationId, not even an explicit nil\nfunc (o *RegistrationFlow) UnsetOrganizationId() {\n\to.OrganizationId.Unset()\n}\n\n// GetRequestUrl returns the RequestUrl field value\nfunc (o *RegistrationFlow) GetRequestUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RequestUrl, true\n}\n\n// SetRequestUrl sets field value\nfunc (o *RegistrationFlow) SetRequestUrl(v string) {\n\to.RequestUrl = v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *RegistrationFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetSessionTokenExchangeCode returns the SessionTokenExchangeCode field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetSessionTokenExchangeCode() string {\n\tif o == nil || IsNil(o.SessionTokenExchangeCode) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionTokenExchangeCode\n}\n\n// GetSessionTokenExchangeCodeOk returns a tuple with the SessionTokenExchangeCode field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetSessionTokenExchangeCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionTokenExchangeCode) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionTokenExchangeCode, true\n}\n\n// HasSessionTokenExchangeCode returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasSessionTokenExchangeCode() bool {\n\tif o != nil && !IsNil(o.SessionTokenExchangeCode) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionTokenExchangeCode gets a reference to the given string and assigns it to the SessionTokenExchangeCode field.\nfunc (o *RegistrationFlow) SetSessionTokenExchangeCode(v string) {\n\to.SessionTokenExchangeCode = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *RegistrationFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *RegistrationFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *RegistrationFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *RegistrationFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *RegistrationFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *RegistrationFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *RegistrationFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *RegistrationFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *RegistrationFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *RegistrationFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *RegistrationFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\nfunc (o RegistrationFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o RegistrationFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.IdentitySchema) {\n\t\ttoSerialize[\"identity_schema\"] = o.IdentitySchema\n\t}\n\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\tif !IsNil(o.Oauth2LoginChallenge) {\n\t\ttoSerialize[\"oauth2_login_challenge\"] = o.Oauth2LoginChallenge\n\t}\n\tif !IsNil(o.Oauth2LoginRequest) {\n\t\ttoSerialize[\"oauth2_login_request\"] = o.Oauth2LoginRequest\n\t}\n\tif o.OrganizationId.IsSet() {\n\t\ttoSerialize[\"organization_id\"] = o.OrganizationId.Get()\n\t}\n\ttoSerialize[\"request_url\"] = o.RequestUrl\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif !IsNil(o.SessionTokenExchangeCode) {\n\t\ttoSerialize[\"session_token_exchange_code\"] = o.SessionTokenExchangeCode\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *RegistrationFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"expires_at\",\n\t\t\"id\",\n\t\t\"issued_at\",\n\t\t\"request_url\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarRegistrationFlow := _RegistrationFlow{}\n\n\terr = json.Unmarshal(data, &varRegistrationFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = RegistrationFlow(varRegistrationFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"identity_schema\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"oauth2_login_challenge\")\n\t\tdelete(additionalProperties, \"oauth2_login_request\")\n\t\tdelete(additionalProperties, \"organization_id\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"session_token_exchange_code\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableRegistrationFlow struct {\n\tvalue *RegistrationFlow\n\tisSet bool\n}\n\nfunc (v NullableRegistrationFlow) Get() *RegistrationFlow {\n\treturn v.value\n}\n\nfunc (v *NullableRegistrationFlow) Set(val *RegistrationFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRegistrationFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRegistrationFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRegistrationFlow(val *RegistrationFlow) *NullableRegistrationFlow {\n\treturn &NullableRegistrationFlow{value: val, isSet: true}\n}\n\nfunc (v NullableRegistrationFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRegistrationFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_registration_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// RegistrationFlowState The experimental state represents the state of a registration flow. This field is EXPERIMENTAL and subject to change!\ntype RegistrationFlowState string\n\n// List of registrationFlowState\nconst (\n\tREGISTRATIONFLOWSTATE_CHOOSE_METHOD    RegistrationFlowState = \"choose_method\"\n\tREGISTRATIONFLOWSTATE_SENT_EMAIL       RegistrationFlowState = \"sent_email\"\n\tREGISTRATIONFLOWSTATE_PASSED_CHALLENGE RegistrationFlowState = \"passed_challenge\"\n)\n\n// All allowed values of RegistrationFlowState enum\nvar AllowedRegistrationFlowStateEnumValues = []RegistrationFlowState{\n\t\"choose_method\",\n\t\"sent_email\",\n\t\"passed_challenge\",\n}\n\nfunc (v *RegistrationFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := RegistrationFlowState(value)\n\tfor _, existing := range AllowedRegistrationFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid RegistrationFlowState\", value)\n}\n\n// NewRegistrationFlowStateFromValue returns a pointer to a valid RegistrationFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewRegistrationFlowStateFromValue(v string) (*RegistrationFlowState, error) {\n\tev := RegistrationFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for RegistrationFlowState: valid values are %v\", v, AllowedRegistrationFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v RegistrationFlowState) IsValid() bool {\n\tfor _, existing := range AllowedRegistrationFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to registrationFlowState value\nfunc (v RegistrationFlowState) Ptr() *RegistrationFlowState {\n\treturn &v\n}\n\ntype NullableRegistrationFlowState struct {\n\tvalue *RegistrationFlowState\n\tisSet bool\n}\n\nfunc (v NullableRegistrationFlowState) Get() *RegistrationFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableRegistrationFlowState) Set(val *RegistrationFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableRegistrationFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableRegistrationFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableRegistrationFlowState(val *RegistrationFlowState) *NullableRegistrationFlowState {\n\treturn &NullableRegistrationFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableRegistrationFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableRegistrationFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_self_service_flow_expired_error.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n)\n\n// checks if the SelfServiceFlowExpiredError type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SelfServiceFlowExpiredError{}\n\n// SelfServiceFlowExpiredError Is sent when a flow is expired\ntype SelfServiceFlowExpiredError struct {\n\tError *GenericError `json:\"error,omitempty\"`\n\t// When the flow has expired\n\tExpiredAt *time.Time `json:\"expired_at,omitempty\"`\n\t// A Duration represents the elapsed time between two instants as an int64 nanosecond count. The representation limits the largest representable duration to approximately 290 years.\n\tSince *int64 `json:\"since,omitempty\"`\n\t// The flow ID that should be used for the new flow as it contains the correct messages.\n\tUseFlowId            *string `json:\"use_flow_id,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SelfServiceFlowExpiredError SelfServiceFlowExpiredError\n\n// NewSelfServiceFlowExpiredError instantiates a new SelfServiceFlowExpiredError object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSelfServiceFlowExpiredError() *SelfServiceFlowExpiredError {\n\tthis := SelfServiceFlowExpiredError{}\n\treturn &this\n}\n\n// NewSelfServiceFlowExpiredErrorWithDefaults instantiates a new SelfServiceFlowExpiredError object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSelfServiceFlowExpiredErrorWithDefaults() *SelfServiceFlowExpiredError {\n\tthis := SelfServiceFlowExpiredError{}\n\treturn &this\n}\n\n// GetError returns the Error field value if set, zero value otherwise.\nfunc (o *SelfServiceFlowExpiredError) GetError() GenericError {\n\tif o == nil || IsNil(o.Error) {\n\t\tvar ret GenericError\n\t\treturn ret\n\t}\n\treturn *o.Error\n}\n\n// GetErrorOk returns a tuple with the Error field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SelfServiceFlowExpiredError) GetErrorOk() (*GenericError, bool) {\n\tif o == nil || IsNil(o.Error) {\n\t\treturn nil, false\n\t}\n\treturn o.Error, true\n}\n\n// HasError returns a boolean if a field has been set.\nfunc (o *SelfServiceFlowExpiredError) HasError() bool {\n\tif o != nil && !IsNil(o.Error) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetError gets a reference to the given GenericError and assigns it to the Error field.\nfunc (o *SelfServiceFlowExpiredError) SetError(v GenericError) {\n\to.Error = &v\n}\n\n// GetExpiredAt returns the ExpiredAt field value if set, zero value otherwise.\nfunc (o *SelfServiceFlowExpiredError) GetExpiredAt() time.Time {\n\tif o == nil || IsNil(o.ExpiredAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiredAt\n}\n\n// GetExpiredAtOk returns a tuple with the ExpiredAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SelfServiceFlowExpiredError) GetExpiredAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiredAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiredAt, true\n}\n\n// HasExpiredAt returns a boolean if a field has been set.\nfunc (o *SelfServiceFlowExpiredError) HasExpiredAt() bool {\n\tif o != nil && !IsNil(o.ExpiredAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiredAt gets a reference to the given time.Time and assigns it to the ExpiredAt field.\nfunc (o *SelfServiceFlowExpiredError) SetExpiredAt(v time.Time) {\n\to.ExpiredAt = &v\n}\n\n// GetSince returns the Since field value if set, zero value otherwise.\nfunc (o *SelfServiceFlowExpiredError) GetSince() int64 {\n\tif o == nil || IsNil(o.Since) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Since\n}\n\n// GetSinceOk returns a tuple with the Since field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SelfServiceFlowExpiredError) GetSinceOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Since) {\n\t\treturn nil, false\n\t}\n\treturn o.Since, true\n}\n\n// HasSince returns a boolean if a field has been set.\nfunc (o *SelfServiceFlowExpiredError) HasSince() bool {\n\tif o != nil && !IsNil(o.Since) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSince gets a reference to the given int64 and assigns it to the Since field.\nfunc (o *SelfServiceFlowExpiredError) SetSince(v int64) {\n\to.Since = &v\n}\n\n// GetUseFlowId returns the UseFlowId field value if set, zero value otherwise.\nfunc (o *SelfServiceFlowExpiredError) GetUseFlowId() string {\n\tif o == nil || IsNil(o.UseFlowId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.UseFlowId\n}\n\n// GetUseFlowIdOk returns a tuple with the UseFlowId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SelfServiceFlowExpiredError) GetUseFlowIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.UseFlowId) {\n\t\treturn nil, false\n\t}\n\treturn o.UseFlowId, true\n}\n\n// HasUseFlowId returns a boolean if a field has been set.\nfunc (o *SelfServiceFlowExpiredError) HasUseFlowId() bool {\n\tif o != nil && !IsNil(o.UseFlowId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUseFlowId gets a reference to the given string and assigns it to the UseFlowId field.\nfunc (o *SelfServiceFlowExpiredError) SetUseFlowId(v string) {\n\to.UseFlowId = &v\n}\n\nfunc (o SelfServiceFlowExpiredError) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SelfServiceFlowExpiredError) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Error) {\n\t\ttoSerialize[\"error\"] = o.Error\n\t}\n\tif !IsNil(o.ExpiredAt) {\n\t\ttoSerialize[\"expired_at\"] = o.ExpiredAt\n\t}\n\tif !IsNil(o.Since) {\n\t\ttoSerialize[\"since\"] = o.Since\n\t}\n\tif !IsNil(o.UseFlowId) {\n\t\ttoSerialize[\"use_flow_id\"] = o.UseFlowId\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SelfServiceFlowExpiredError) UnmarshalJSON(data []byte) (err error) {\n\tvarSelfServiceFlowExpiredError := _SelfServiceFlowExpiredError{}\n\n\terr = json.Unmarshal(data, &varSelfServiceFlowExpiredError)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SelfServiceFlowExpiredError(varSelfServiceFlowExpiredError)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"error\")\n\t\tdelete(additionalProperties, \"expired_at\")\n\t\tdelete(additionalProperties, \"since\")\n\t\tdelete(additionalProperties, \"use_flow_id\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSelfServiceFlowExpiredError struct {\n\tvalue *SelfServiceFlowExpiredError\n\tisSet bool\n}\n\nfunc (v NullableSelfServiceFlowExpiredError) Get() *SelfServiceFlowExpiredError {\n\treturn v.value\n}\n\nfunc (v *NullableSelfServiceFlowExpiredError) Set(val *SelfServiceFlowExpiredError) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSelfServiceFlowExpiredError) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSelfServiceFlowExpiredError) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSelfServiceFlowExpiredError(val *SelfServiceFlowExpiredError) *NullableSelfServiceFlowExpiredError {\n\treturn &NullableSelfServiceFlowExpiredError{value: val, isSet: true}\n}\n\nfunc (v NullableSelfServiceFlowExpiredError) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSelfServiceFlowExpiredError) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_session.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the Session type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Session{}\n\n// Session A Session\ntype Session struct {\n\t// Active state. If false the session is no longer active.\n\tActive *bool `json:\"active,omitempty\"`\n\t// The Session Authentication Timestamp  When this session was authenticated at. If multi-factor authentication was used this is the time when the last factor was authenticated (e.g. the TOTP code challenge was completed).\n\tAuthenticatedAt *time.Time `json:\"authenticated_at,omitempty\"`\n\t// A list of authenticators which were used to authenticate the session.\n\tAuthenticationMethods       []SessionAuthenticationMethod `json:\"authentication_methods,omitempty\"`\n\tAuthenticatorAssuranceLevel *AuthenticatorAssuranceLevel  `json:\"authenticator_assurance_level,omitempty\"`\n\t// Devices has history of all endpoints where the session was used\n\tDevices []SessionDevice `json:\"devices,omitempty\"`\n\t// The Session Expiry  When this session expires at.\n\tExpiresAt *time.Time `json:\"expires_at,omitempty\"`\n\t// Session ID\n\tId       string    `json:\"id\"`\n\tIdentity *Identity `json:\"identity,omitempty\"`\n\t// The Session Issuance Timestamp  When this session was issued at. Usually equal or close to `authenticated_at`.\n\tIssuedAt *time.Time `json:\"issued_at,omitempty\"`\n\t// Tokenized is the tokenized (e.g. JWT) version of the session.  It is only set when the `tokenize_as` query parameter was set to a valid tokenize template during calls to `/session/whoami`.\n\tTokenized            *string `json:\"tokenized,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Session Session\n\n// NewSession instantiates a new Session object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSession(id string) *Session {\n\tthis := Session{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewSessionWithDefaults instantiates a new Session object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSessionWithDefaults() *Session {\n\tthis := Session{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *Session) GetActive() bool {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetActiveOk() (*bool, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *Session) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given bool and assigns it to the Active field.\nfunc (o *Session) SetActive(v bool) {\n\to.Active = &v\n}\n\n// GetAuthenticatedAt returns the AuthenticatedAt field value if set, zero value otherwise.\nfunc (o *Session) GetAuthenticatedAt() time.Time {\n\tif o == nil || IsNil(o.AuthenticatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.AuthenticatedAt\n}\n\n// GetAuthenticatedAtOk returns a tuple with the AuthenticatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetAuthenticatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.AuthenticatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthenticatedAt, true\n}\n\n// HasAuthenticatedAt returns a boolean if a field has been set.\nfunc (o *Session) HasAuthenticatedAt() bool {\n\tif o != nil && !IsNil(o.AuthenticatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthenticatedAt gets a reference to the given time.Time and assigns it to the AuthenticatedAt field.\nfunc (o *Session) SetAuthenticatedAt(v time.Time) {\n\to.AuthenticatedAt = &v\n}\n\n// GetAuthenticationMethods returns the AuthenticationMethods field value if set, zero value otherwise.\nfunc (o *Session) GetAuthenticationMethods() []SessionAuthenticationMethod {\n\tif o == nil || IsNil(o.AuthenticationMethods) {\n\t\tvar ret []SessionAuthenticationMethod\n\t\treturn ret\n\t}\n\treturn o.AuthenticationMethods\n}\n\n// GetAuthenticationMethodsOk returns a tuple with the AuthenticationMethods field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetAuthenticationMethodsOk() ([]SessionAuthenticationMethod, bool) {\n\tif o == nil || IsNil(o.AuthenticationMethods) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthenticationMethods, true\n}\n\n// HasAuthenticationMethods returns a boolean if a field has been set.\nfunc (o *Session) HasAuthenticationMethods() bool {\n\tif o != nil && !IsNil(o.AuthenticationMethods) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthenticationMethods gets a reference to the given []SessionAuthenticationMethod and assigns it to the AuthenticationMethods field.\nfunc (o *Session) SetAuthenticationMethods(v []SessionAuthenticationMethod) {\n\to.AuthenticationMethods = v\n}\n\n// GetAuthenticatorAssuranceLevel returns the AuthenticatorAssuranceLevel field value if set, zero value otherwise.\nfunc (o *Session) GetAuthenticatorAssuranceLevel() AuthenticatorAssuranceLevel {\n\tif o == nil || IsNil(o.AuthenticatorAssuranceLevel) {\n\t\tvar ret AuthenticatorAssuranceLevel\n\t\treturn ret\n\t}\n\treturn *o.AuthenticatorAssuranceLevel\n}\n\n// GetAuthenticatorAssuranceLevelOk returns a tuple with the AuthenticatorAssuranceLevel field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetAuthenticatorAssuranceLevelOk() (*AuthenticatorAssuranceLevel, bool) {\n\tif o == nil || IsNil(o.AuthenticatorAssuranceLevel) {\n\t\treturn nil, false\n\t}\n\treturn o.AuthenticatorAssuranceLevel, true\n}\n\n// HasAuthenticatorAssuranceLevel returns a boolean if a field has been set.\nfunc (o *Session) HasAuthenticatorAssuranceLevel() bool {\n\tif o != nil && !IsNil(o.AuthenticatorAssuranceLevel) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAuthenticatorAssuranceLevel gets a reference to the given AuthenticatorAssuranceLevel and assigns it to the AuthenticatorAssuranceLevel field.\nfunc (o *Session) SetAuthenticatorAssuranceLevel(v AuthenticatorAssuranceLevel) {\n\to.AuthenticatorAssuranceLevel = &v\n}\n\n// GetDevices returns the Devices field value if set, zero value otherwise.\nfunc (o *Session) GetDevices() []SessionDevice {\n\tif o == nil || IsNil(o.Devices) {\n\t\tvar ret []SessionDevice\n\t\treturn ret\n\t}\n\treturn o.Devices\n}\n\n// GetDevicesOk returns a tuple with the Devices field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetDevicesOk() ([]SessionDevice, bool) {\n\tif o == nil || IsNil(o.Devices) {\n\t\treturn nil, false\n\t}\n\treturn o.Devices, true\n}\n\n// HasDevices returns a boolean if a field has been set.\nfunc (o *Session) HasDevices() bool {\n\tif o != nil && !IsNil(o.Devices) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetDevices gets a reference to the given []SessionDevice and assigns it to the Devices field.\nfunc (o *Session) SetDevices(v []SessionDevice) {\n\to.Devices = v\n}\n\n// GetExpiresAt returns the ExpiresAt field value if set, zero value otherwise.\nfunc (o *Session) GetExpiresAt() time.Time {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresAt, true\n}\n\n// HasExpiresAt returns a boolean if a field has been set.\nfunc (o *Session) HasExpiresAt() bool {\n\tif o != nil && !IsNil(o.ExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresAt gets a reference to the given time.Time and assigns it to the ExpiresAt field.\nfunc (o *Session) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = &v\n}\n\n// GetId returns the Id field value\nfunc (o *Session) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *Session) SetId(v string) {\n\to.Id = v\n}\n\n// GetIdentity returns the Identity field value if set, zero value otherwise.\nfunc (o *Session) GetIdentity() Identity {\n\tif o == nil || IsNil(o.Identity) {\n\t\tvar ret Identity\n\t\treturn ret\n\t}\n\treturn *o.Identity\n}\n\n// GetIdentityOk returns a tuple with the Identity field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetIdentityOk() (*Identity, bool) {\n\tif o == nil || IsNil(o.Identity) {\n\t\treturn nil, false\n\t}\n\treturn o.Identity, true\n}\n\n// HasIdentity returns a boolean if a field has been set.\nfunc (o *Session) HasIdentity() bool {\n\tif o != nil && !IsNil(o.Identity) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentity gets a reference to the given Identity and assigns it to the Identity field.\nfunc (o *Session) SetIdentity(v Identity) {\n\to.Identity = &v\n}\n\n// GetIssuedAt returns the IssuedAt field value if set, zero value otherwise.\nfunc (o *Session) GetIssuedAt() time.Time {\n\tif o == nil || IsNil(o.IssuedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.IssuedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.IssuedAt, true\n}\n\n// HasIssuedAt returns a boolean if a field has been set.\nfunc (o *Session) HasIssuedAt() bool {\n\tif o != nil && !IsNil(o.IssuedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIssuedAt gets a reference to the given time.Time and assigns it to the IssuedAt field.\nfunc (o *Session) SetIssuedAt(v time.Time) {\n\to.IssuedAt = &v\n}\n\n// GetTokenized returns the Tokenized field value if set, zero value otherwise.\nfunc (o *Session) GetTokenized() string {\n\tif o == nil || IsNil(o.Tokenized) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Tokenized\n}\n\n// GetTokenizedOk returns a tuple with the Tokenized field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Session) GetTokenizedOk() (*string, bool) {\n\tif o == nil || IsNil(o.Tokenized) {\n\t\treturn nil, false\n\t}\n\treturn o.Tokenized, true\n}\n\n// HasTokenized returns a boolean if a field has been set.\nfunc (o *Session) HasTokenized() bool {\n\tif o != nil && !IsNil(o.Tokenized) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTokenized gets a reference to the given string and assigns it to the Tokenized field.\nfunc (o *Session) SetTokenized(v string) {\n\to.Tokenized = &v\n}\n\nfunc (o Session) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Session) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.AuthenticatedAt) {\n\t\ttoSerialize[\"authenticated_at\"] = o.AuthenticatedAt\n\t}\n\tif !IsNil(o.AuthenticationMethods) {\n\t\ttoSerialize[\"authentication_methods\"] = o.AuthenticationMethods\n\t}\n\tif !IsNil(o.AuthenticatorAssuranceLevel) {\n\t\ttoSerialize[\"authenticator_assurance_level\"] = o.AuthenticatorAssuranceLevel\n\t}\n\tif !IsNil(o.Devices) {\n\t\ttoSerialize[\"devices\"] = o.Devices\n\t}\n\tif !IsNil(o.ExpiresAt) {\n\t\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.Identity) {\n\t\ttoSerialize[\"identity\"] = o.Identity\n\t}\n\tif !IsNil(o.IssuedAt) {\n\t\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\t}\n\tif !IsNil(o.Tokenized) {\n\t\ttoSerialize[\"tokenized\"] = o.Tokenized\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Session) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSession := _Session{}\n\n\terr = json.Unmarshal(data, &varSession)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Session(varSession)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"authenticated_at\")\n\t\tdelete(additionalProperties, \"authentication_methods\")\n\t\tdelete(additionalProperties, \"authenticator_assurance_level\")\n\t\tdelete(additionalProperties, \"devices\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"identity\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"tokenized\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSession struct {\n\tvalue *Session\n\tisSet bool\n}\n\nfunc (v NullableSession) Get() *Session {\n\treturn v.value\n}\n\nfunc (v *NullableSession) Set(val *Session) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSession) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSession) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSession(val *Session) *NullableSession {\n\treturn &NullableSession{value: val, isSet: true}\n}\n\nfunc (v NullableSession) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSession) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_session_authentication_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n)\n\n// checks if the SessionAuthenticationMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SessionAuthenticationMethod{}\n\n// SessionAuthenticationMethod A singular authenticator used during authentication / login.\ntype SessionAuthenticationMethod struct {\n\tAal *AuthenticatorAssuranceLevel `json:\"aal,omitempty\"`\n\t// When the authentication challenge was completed.\n\tCompletedAt *time.Time `json:\"completed_at,omitempty\"`\n\t// The method used in this authenticator. password CredentialsTypePassword oidc CredentialsTypeOIDC totp CredentialsTypeTOTP lookup_secret CredentialsTypeLookup webauthn CredentialsTypeWebAuthn code CredentialsTypeCodeAuth passkey CredentialsTypePasskey profile CredentialsTypeProfile saml CredentialsTypeSAML link_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself. code_recovery CredentialsTypeRecoveryCode\n\tMethod *string `json:\"method,omitempty\"`\n\t// The Organization id used for authentication\n\tOrganization *string `json:\"organization,omitempty\"`\n\t// OIDC or SAML provider id used for authentication\n\tProvider             *string `json:\"provider,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SessionAuthenticationMethod SessionAuthenticationMethod\n\n// NewSessionAuthenticationMethod instantiates a new SessionAuthenticationMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSessionAuthenticationMethod() *SessionAuthenticationMethod {\n\tthis := SessionAuthenticationMethod{}\n\treturn &this\n}\n\n// NewSessionAuthenticationMethodWithDefaults instantiates a new SessionAuthenticationMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSessionAuthenticationMethodWithDefaults() *SessionAuthenticationMethod {\n\tthis := SessionAuthenticationMethod{}\n\treturn &this\n}\n\n// GetAal returns the Aal field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetAal() AuthenticatorAssuranceLevel {\n\tif o == nil || IsNil(o.Aal) {\n\t\tvar ret AuthenticatorAssuranceLevel\n\t\treturn ret\n\t}\n\treturn *o.Aal\n}\n\n// GetAalOk returns a tuple with the Aal field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetAalOk() (*AuthenticatorAssuranceLevel, bool) {\n\tif o == nil || IsNil(o.Aal) {\n\t\treturn nil, false\n\t}\n\treturn o.Aal, true\n}\n\n// HasAal returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasAal() bool {\n\tif o != nil && !IsNil(o.Aal) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAal gets a reference to the given AuthenticatorAssuranceLevel and assigns it to the Aal field.\nfunc (o *SessionAuthenticationMethod) SetAal(v AuthenticatorAssuranceLevel) {\n\to.Aal = &v\n}\n\n// GetCompletedAt returns the CompletedAt field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetCompletedAt() time.Time {\n\tif o == nil || IsNil(o.CompletedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CompletedAt\n}\n\n// GetCompletedAtOk returns a tuple with the CompletedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetCompletedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CompletedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CompletedAt, true\n}\n\n// HasCompletedAt returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasCompletedAt() bool {\n\tif o != nil && !IsNil(o.CompletedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCompletedAt gets a reference to the given time.Time and assigns it to the CompletedAt field.\nfunc (o *SessionAuthenticationMethod) SetCompletedAt(v time.Time) {\n\to.CompletedAt = &v\n}\n\n// GetMethod returns the Method field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetMethod() string {\n\tif o == nil || IsNil(o.Method) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetMethodOk() (*string, bool) {\n\tif o == nil || IsNil(o.Method) {\n\t\treturn nil, false\n\t}\n\treturn o.Method, true\n}\n\n// HasMethod returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasMethod() bool {\n\tif o != nil && !IsNil(o.Method) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMethod gets a reference to the given string and assigns it to the Method field.\nfunc (o *SessionAuthenticationMethod) SetMethod(v string) {\n\to.Method = &v\n}\n\n// GetOrganization returns the Organization field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetOrganization() string {\n\tif o == nil || IsNil(o.Organization) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Organization\n}\n\n// GetOrganizationOk returns a tuple with the Organization field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetOrganizationOk() (*string, bool) {\n\tif o == nil || IsNil(o.Organization) {\n\t\treturn nil, false\n\t}\n\treturn o.Organization, true\n}\n\n// HasOrganization returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasOrganization() bool {\n\tif o != nil && !IsNil(o.Organization) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOrganization gets a reference to the given string and assigns it to the Organization field.\nfunc (o *SessionAuthenticationMethod) SetOrganization(v string) {\n\to.Organization = &v\n}\n\n// GetProvider returns the Provider field value if set, zero value otherwise.\nfunc (o *SessionAuthenticationMethod) GetProvider() string {\n\tif o == nil || IsNil(o.Provider) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionAuthenticationMethod) GetProviderOk() (*string, bool) {\n\tif o == nil || IsNil(o.Provider) {\n\t\treturn nil, false\n\t}\n\treturn o.Provider, true\n}\n\n// HasProvider returns a boolean if a field has been set.\nfunc (o *SessionAuthenticationMethod) HasProvider() bool {\n\tif o != nil && !IsNil(o.Provider) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetProvider gets a reference to the given string and assigns it to the Provider field.\nfunc (o *SessionAuthenticationMethod) SetProvider(v string) {\n\to.Provider = &v\n}\n\nfunc (o SessionAuthenticationMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SessionAuthenticationMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Aal) {\n\t\ttoSerialize[\"aal\"] = o.Aal\n\t}\n\tif !IsNil(o.CompletedAt) {\n\t\ttoSerialize[\"completed_at\"] = o.CompletedAt\n\t}\n\tif !IsNil(o.Method) {\n\t\ttoSerialize[\"method\"] = o.Method\n\t}\n\tif !IsNil(o.Organization) {\n\t\ttoSerialize[\"organization\"] = o.Organization\n\t}\n\tif !IsNil(o.Provider) {\n\t\ttoSerialize[\"provider\"] = o.Provider\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SessionAuthenticationMethod) UnmarshalJSON(data []byte) (err error) {\n\tvarSessionAuthenticationMethod := _SessionAuthenticationMethod{}\n\n\terr = json.Unmarshal(data, &varSessionAuthenticationMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SessionAuthenticationMethod(varSessionAuthenticationMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"aal\")\n\t\tdelete(additionalProperties, \"completed_at\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"organization\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSessionAuthenticationMethod struct {\n\tvalue *SessionAuthenticationMethod\n\tisSet bool\n}\n\nfunc (v NullableSessionAuthenticationMethod) Get() *SessionAuthenticationMethod {\n\treturn v.value\n}\n\nfunc (v *NullableSessionAuthenticationMethod) Set(val *SessionAuthenticationMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSessionAuthenticationMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSessionAuthenticationMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSessionAuthenticationMethod(val *SessionAuthenticationMethod) *NullableSessionAuthenticationMethod {\n\treturn &NullableSessionAuthenticationMethod{value: val, isSet: true}\n}\n\nfunc (v NullableSessionAuthenticationMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSessionAuthenticationMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_session_device.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the SessionDevice type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SessionDevice{}\n\n// SessionDevice Device corresponding to a Session\ntype SessionDevice struct {\n\t// Device record ID\n\tId string `json:\"id\"`\n\t// IPAddress of the client\n\tIpAddress *string `json:\"ip_address,omitempty\"`\n\t// Geo Location corresponding to the IP Address\n\tLocation *string `json:\"location,omitempty\"`\n\t// UserAgent of the client\n\tUserAgent            *string `json:\"user_agent,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SessionDevice SessionDevice\n\n// NewSessionDevice instantiates a new SessionDevice object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSessionDevice(id string) *SessionDevice {\n\tthis := SessionDevice{}\n\tthis.Id = id\n\treturn &this\n}\n\n// NewSessionDeviceWithDefaults instantiates a new SessionDevice object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSessionDeviceWithDefaults() *SessionDevice {\n\tthis := SessionDevice{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *SessionDevice) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *SessionDevice) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *SessionDevice) SetId(v string) {\n\to.Id = v\n}\n\n// GetIpAddress returns the IpAddress field value if set, zero value otherwise.\nfunc (o *SessionDevice) GetIpAddress() string {\n\tif o == nil || IsNil(o.IpAddress) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IpAddress\n}\n\n// GetIpAddressOk returns a tuple with the IpAddress field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionDevice) GetIpAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.IpAddress) {\n\t\treturn nil, false\n\t}\n\treturn o.IpAddress, true\n}\n\n// HasIpAddress returns a boolean if a field has been set.\nfunc (o *SessionDevice) HasIpAddress() bool {\n\tif o != nil && !IsNil(o.IpAddress) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIpAddress gets a reference to the given string and assigns it to the IpAddress field.\nfunc (o *SessionDevice) SetIpAddress(v string) {\n\to.IpAddress = &v\n}\n\n// GetLocation returns the Location field value if set, zero value otherwise.\nfunc (o *SessionDevice) GetLocation() string {\n\tif o == nil || IsNil(o.Location) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Location\n}\n\n// GetLocationOk returns a tuple with the Location field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionDevice) GetLocationOk() (*string, bool) {\n\tif o == nil || IsNil(o.Location) {\n\t\treturn nil, false\n\t}\n\treturn o.Location, true\n}\n\n// HasLocation returns a boolean if a field has been set.\nfunc (o *SessionDevice) HasLocation() bool {\n\tif o != nil && !IsNil(o.Location) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLocation gets a reference to the given string and assigns it to the Location field.\nfunc (o *SessionDevice) SetLocation(v string) {\n\to.Location = &v\n}\n\n// GetUserAgent returns the UserAgent field value if set, zero value otherwise.\nfunc (o *SessionDevice) GetUserAgent() string {\n\tif o == nil || IsNil(o.UserAgent) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.UserAgent\n}\n\n// GetUserAgentOk returns a tuple with the UserAgent field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SessionDevice) GetUserAgentOk() (*string, bool) {\n\tif o == nil || IsNil(o.UserAgent) {\n\t\treturn nil, false\n\t}\n\treturn o.UserAgent, true\n}\n\n// HasUserAgent returns a boolean if a field has been set.\nfunc (o *SessionDevice) HasUserAgent() bool {\n\tif o != nil && !IsNil(o.UserAgent) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUserAgent gets a reference to the given string and assigns it to the UserAgent field.\nfunc (o *SessionDevice) SetUserAgent(v string) {\n\to.UserAgent = &v\n}\n\nfunc (o SessionDevice) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SessionDevice) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.IpAddress) {\n\t\ttoSerialize[\"ip_address\"] = o.IpAddress\n\t}\n\tif !IsNil(o.Location) {\n\t\ttoSerialize[\"location\"] = o.Location\n\t}\n\tif !IsNil(o.UserAgent) {\n\t\ttoSerialize[\"user_agent\"] = o.UserAgent\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SessionDevice) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSessionDevice := _SessionDevice{}\n\n\terr = json.Unmarshal(data, &varSessionDevice)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SessionDevice(varSessionDevice)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"ip_address\")\n\t\tdelete(additionalProperties, \"location\")\n\t\tdelete(additionalProperties, \"user_agent\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSessionDevice struct {\n\tvalue *SessionDevice\n\tisSet bool\n}\n\nfunc (v NullableSessionDevice) Get() *SessionDevice {\n\treturn v.value\n}\n\nfunc (v *NullableSessionDevice) Set(val *SessionDevice) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSessionDevice) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSessionDevice) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSessionDevice(val *SessionDevice) *NullableSessionDevice {\n\treturn &NullableSessionDevice{value: val, isSet: true}\n}\n\nfunc (v NullableSessionDevice) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSessionDevice) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_settings_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the SettingsFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SettingsFlow{}\n\n// SettingsFlow This flow is used when an identity wants to update settings (e.g. profile data, passwords, ...) in a selfservice manner.  We recommend reading the [User Settings Documentation](../self-service/flows/user-settings)\ntype SettingsFlow struct {\n\t// Active, if set, contains the registration method that is being used. It is initially not set.\n\tActive *string `json:\"active,omitempty\"`\n\t// Contains a list of actions, that could follow this flow  It can, for example, contain a reference to the verification flow, created as part of the user's registration.\n\tContinueWith []ContinueWith `json:\"continue_with,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to update the setting, a new flow has to be initiated.\n\tExpiresAt time.Time `json:\"expires_at\"`\n\t// ID represents the flow's unique ID. When performing the settings flow, this represents the id in the settings ui's query parameter: http://<selfservice.flows.settings.ui_url>?flow=<id>\n\tId       string   `json:\"id\"`\n\tIdentity Identity `json:\"identity\"`\n\t// IssuedAt is the time (UTC) when the flow occurred.\n\tIssuedAt time.Time `json:\"issued_at\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl string `json:\"request_url\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// State represents the state of this flow. It knows two states:  show_form: No user data has been collected, or it is invalid, and thus the form should be shown. success: Indicates that the settings flow has been updated successfully with the provided data. Done will stay true when repeatedly checking. If set to true, done will revert back to false only when a flow with invalid (e.g. \\\"please use a valid phone number\\\") data was sent.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the settings flow to hooks and email templates\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType                 string      `json:\"type\"`\n\tUi                   UiContainer `json:\"ui\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SettingsFlow SettingsFlow\n\n// NewSettingsFlow instantiates a new SettingsFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSettingsFlow(expiresAt time.Time, id string, identity Identity, issuedAt time.Time, requestUrl string, state interface{}, type_ string, ui UiContainer) *SettingsFlow {\n\tthis := SettingsFlow{}\n\tthis.ExpiresAt = expiresAt\n\tthis.Id = id\n\tthis.Identity = identity\n\tthis.IssuedAt = issuedAt\n\tthis.RequestUrl = requestUrl\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewSettingsFlowWithDefaults instantiates a new SettingsFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSettingsFlowWithDefaults() *SettingsFlow {\n\tthis := SettingsFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *SettingsFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *SettingsFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *SettingsFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetContinueWith returns the ContinueWith field value if set, zero value otherwise.\nfunc (o *SettingsFlow) GetContinueWith() []ContinueWith {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\tvar ret []ContinueWith\n\t\treturn ret\n\t}\n\treturn o.ContinueWith\n}\n\n// GetContinueWithOk returns a tuple with the ContinueWith field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetContinueWithOk() ([]ContinueWith, bool) {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\treturn nil, false\n\t}\n\treturn o.ContinueWith, true\n}\n\n// HasContinueWith returns a boolean if a field has been set.\nfunc (o *SettingsFlow) HasContinueWith() bool {\n\tif o != nil && !IsNil(o.ContinueWith) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContinueWith gets a reference to the given []ContinueWith and assigns it to the ContinueWith field.\nfunc (o *SettingsFlow) SetContinueWith(v []ContinueWith) {\n\to.ContinueWith = v\n}\n\n// GetExpiresAt returns the ExpiresAt field value\nfunc (o *SettingsFlow) GetExpiresAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.ExpiresAt, true\n}\n\n// SetExpiresAt sets field value\nfunc (o *SettingsFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = v\n}\n\n// GetId returns the Id field value\nfunc (o *SettingsFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *SettingsFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIdentity returns the Identity field value\nfunc (o *SettingsFlow) GetIdentity() Identity {\n\tif o == nil {\n\t\tvar ret Identity\n\t\treturn ret\n\t}\n\n\treturn o.Identity\n}\n\n// GetIdentityOk returns a tuple with the Identity field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetIdentityOk() (*Identity, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identity, true\n}\n\n// SetIdentity sets field value\nfunc (o *SettingsFlow) SetIdentity(v Identity) {\n\to.Identity = v\n}\n\n// GetIssuedAt returns the IssuedAt field value\nfunc (o *SettingsFlow) GetIssuedAt() time.Time {\n\tif o == nil {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\n\treturn o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.IssuedAt, true\n}\n\n// SetIssuedAt sets field value\nfunc (o *SettingsFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = v\n}\n\n// GetRequestUrl returns the RequestUrl field value\nfunc (o *SettingsFlow) GetRequestUrl() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.RequestUrl, true\n}\n\n// SetRequestUrl sets field value\nfunc (o *SettingsFlow) SetRequestUrl(v string) {\n\to.RequestUrl = v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *SettingsFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *SettingsFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *SettingsFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *SettingsFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *SettingsFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *SettingsFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *SettingsFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *SettingsFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *SettingsFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *SettingsFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *SettingsFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *SettingsFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *SettingsFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *SettingsFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\nfunc (o SettingsFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SettingsFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.ContinueWith) {\n\t\ttoSerialize[\"continue_with\"] = o.ContinueWith\n\t}\n\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"identity\"] = o.Identity\n\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\ttoSerialize[\"request_url\"] = o.RequestUrl\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SettingsFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"expires_at\",\n\t\t\"id\",\n\t\t\"identity\",\n\t\t\"issued_at\",\n\t\t\"request_url\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSettingsFlow := _SettingsFlow{}\n\n\terr = json.Unmarshal(data, &varSettingsFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SettingsFlow(varSettingsFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"continue_with\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"identity\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSettingsFlow struct {\n\tvalue *SettingsFlow\n\tisSet bool\n}\n\nfunc (v NullableSettingsFlow) Get() *SettingsFlow {\n\treturn v.value\n}\n\nfunc (v *NullableSettingsFlow) Set(val *SettingsFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSettingsFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSettingsFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSettingsFlow(val *SettingsFlow) *NullableSettingsFlow {\n\treturn &NullableSettingsFlow{value: val, isSet: true}\n}\n\nfunc (v NullableSettingsFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSettingsFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_settings_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// SettingsFlowState The experimental state represents the state of a settings flow. This field is EXPERIMENTAL and subject to change!\ntype SettingsFlowState string\n\n// List of settingsFlowState\nconst (\n\tSETTINGSFLOWSTATE_SHOW_FORM SettingsFlowState = \"show_form\"\n\tSETTINGSFLOWSTATE_SUCCESS   SettingsFlowState = \"success\"\n)\n\n// All allowed values of SettingsFlowState enum\nvar AllowedSettingsFlowStateEnumValues = []SettingsFlowState{\n\t\"show_form\",\n\t\"success\",\n}\n\nfunc (v *SettingsFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := SettingsFlowState(value)\n\tfor _, existing := range AllowedSettingsFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid SettingsFlowState\", value)\n}\n\n// NewSettingsFlowStateFromValue returns a pointer to a valid SettingsFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewSettingsFlowStateFromValue(v string) (*SettingsFlowState, error) {\n\tev := SettingsFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for SettingsFlowState: valid values are %v\", v, AllowedSettingsFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v SettingsFlowState) IsValid() bool {\n\tfor _, existing := range AllowedSettingsFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to settingsFlowState value\nfunc (v SettingsFlowState) Ptr() *SettingsFlowState {\n\treturn &v\n}\n\ntype NullableSettingsFlowState struct {\n\tvalue *SettingsFlowState\n\tisSet bool\n}\n\nfunc (v NullableSettingsFlowState) Get() *SettingsFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableSettingsFlowState) Set(val *SettingsFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSettingsFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSettingsFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSettingsFlowState(val *SettingsFlowState) *NullableSettingsFlowState {\n\treturn &NullableSettingsFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableSettingsFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSettingsFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_successful_code_exchange_response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the SuccessfulCodeExchangeResponse type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SuccessfulCodeExchangeResponse{}\n\n// SuccessfulCodeExchangeResponse The Response for Registration Flows via API\ntype SuccessfulCodeExchangeResponse struct {\n\tSession Session `json:\"session\"`\n\t// The Session Token  A session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization Header:  Authorization: bearer ${session-token}  The session token is only issued for API flows, not for Browser flows!\n\tSessionToken         *string `json:\"session_token,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SuccessfulCodeExchangeResponse SuccessfulCodeExchangeResponse\n\n// NewSuccessfulCodeExchangeResponse instantiates a new SuccessfulCodeExchangeResponse object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSuccessfulCodeExchangeResponse(session Session) *SuccessfulCodeExchangeResponse {\n\tthis := SuccessfulCodeExchangeResponse{}\n\tthis.Session = session\n\treturn &this\n}\n\n// NewSuccessfulCodeExchangeResponseWithDefaults instantiates a new SuccessfulCodeExchangeResponse object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSuccessfulCodeExchangeResponseWithDefaults() *SuccessfulCodeExchangeResponse {\n\tthis := SuccessfulCodeExchangeResponse{}\n\treturn &this\n}\n\n// GetSession returns the Session field value\nfunc (o *SuccessfulCodeExchangeResponse) GetSession() Session {\n\tif o == nil {\n\t\tvar ret Session\n\t\treturn ret\n\t}\n\n\treturn o.Session\n}\n\n// GetSessionOk returns a tuple with the Session field value\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulCodeExchangeResponse) GetSessionOk() (*Session, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Session, true\n}\n\n// SetSession sets field value\nfunc (o *SuccessfulCodeExchangeResponse) SetSession(v Session) {\n\to.Session = v\n}\n\n// GetSessionToken returns the SessionToken field value if set, zero value otherwise.\nfunc (o *SuccessfulCodeExchangeResponse) GetSessionToken() string {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionToken\n}\n\n// GetSessionTokenOk returns a tuple with the SessionToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulCodeExchangeResponse) GetSessionTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionToken, true\n}\n\n// HasSessionToken returns a boolean if a field has been set.\nfunc (o *SuccessfulCodeExchangeResponse) HasSessionToken() bool {\n\tif o != nil && !IsNil(o.SessionToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionToken gets a reference to the given string and assigns it to the SessionToken field.\nfunc (o *SuccessfulCodeExchangeResponse) SetSessionToken(v string) {\n\to.SessionToken = &v\n}\n\nfunc (o SuccessfulCodeExchangeResponse) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SuccessfulCodeExchangeResponse) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"session\"] = o.Session\n\tif !IsNil(o.SessionToken) {\n\t\ttoSerialize[\"session_token\"] = o.SessionToken\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SuccessfulCodeExchangeResponse) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"session\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSuccessfulCodeExchangeResponse := _SuccessfulCodeExchangeResponse{}\n\n\terr = json.Unmarshal(data, &varSuccessfulCodeExchangeResponse)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SuccessfulCodeExchangeResponse(varSuccessfulCodeExchangeResponse)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"session\")\n\t\tdelete(additionalProperties, \"session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSuccessfulCodeExchangeResponse struct {\n\tvalue *SuccessfulCodeExchangeResponse\n\tisSet bool\n}\n\nfunc (v NullableSuccessfulCodeExchangeResponse) Get() *SuccessfulCodeExchangeResponse {\n\treturn v.value\n}\n\nfunc (v *NullableSuccessfulCodeExchangeResponse) Set(val *SuccessfulCodeExchangeResponse) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSuccessfulCodeExchangeResponse) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSuccessfulCodeExchangeResponse) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSuccessfulCodeExchangeResponse(val *SuccessfulCodeExchangeResponse) *NullableSuccessfulCodeExchangeResponse {\n\treturn &NullableSuccessfulCodeExchangeResponse{value: val, isSet: true}\n}\n\nfunc (v NullableSuccessfulCodeExchangeResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSuccessfulCodeExchangeResponse) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_successful_native_login.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the SuccessfulNativeLogin type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SuccessfulNativeLogin{}\n\n// SuccessfulNativeLogin The Response for Login Flows via API\ntype SuccessfulNativeLogin struct {\n\t// Contains a list of actions, that could follow this flow  It can, for example, this will contain a reference to the verification flow, created as part of the user's registration or the token of the session.\n\tContinueWith []ContinueWith `json:\"continue_with,omitempty\"`\n\tSession      Session        `json:\"session\"`\n\t// The Session Token  A session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization Header:  Authorization: bearer ${session-token}  The session token is only issued for API flows, not for Browser flows!\n\tSessionToken         *string `json:\"session_token,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SuccessfulNativeLogin SuccessfulNativeLogin\n\n// NewSuccessfulNativeLogin instantiates a new SuccessfulNativeLogin object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSuccessfulNativeLogin(session Session) *SuccessfulNativeLogin {\n\tthis := SuccessfulNativeLogin{}\n\tthis.Session = session\n\treturn &this\n}\n\n// NewSuccessfulNativeLoginWithDefaults instantiates a new SuccessfulNativeLogin object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSuccessfulNativeLoginWithDefaults() *SuccessfulNativeLogin {\n\tthis := SuccessfulNativeLogin{}\n\treturn &this\n}\n\n// GetContinueWith returns the ContinueWith field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeLogin) GetContinueWith() []ContinueWith {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\tvar ret []ContinueWith\n\t\treturn ret\n\t}\n\treturn o.ContinueWith\n}\n\n// GetContinueWithOk returns a tuple with the ContinueWith field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeLogin) GetContinueWithOk() ([]ContinueWith, bool) {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\treturn nil, false\n\t}\n\treturn o.ContinueWith, true\n}\n\n// HasContinueWith returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeLogin) HasContinueWith() bool {\n\tif o != nil && !IsNil(o.ContinueWith) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContinueWith gets a reference to the given []ContinueWith and assigns it to the ContinueWith field.\nfunc (o *SuccessfulNativeLogin) SetContinueWith(v []ContinueWith) {\n\to.ContinueWith = v\n}\n\n// GetSession returns the Session field value\nfunc (o *SuccessfulNativeLogin) GetSession() Session {\n\tif o == nil {\n\t\tvar ret Session\n\t\treturn ret\n\t}\n\n\treturn o.Session\n}\n\n// GetSessionOk returns a tuple with the Session field value\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeLogin) GetSessionOk() (*Session, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Session, true\n}\n\n// SetSession sets field value\nfunc (o *SuccessfulNativeLogin) SetSession(v Session) {\n\to.Session = v\n}\n\n// GetSessionToken returns the SessionToken field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeLogin) GetSessionToken() string {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionToken\n}\n\n// GetSessionTokenOk returns a tuple with the SessionToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeLogin) GetSessionTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionToken, true\n}\n\n// HasSessionToken returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeLogin) HasSessionToken() bool {\n\tif o != nil && !IsNil(o.SessionToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionToken gets a reference to the given string and assigns it to the SessionToken field.\nfunc (o *SuccessfulNativeLogin) SetSessionToken(v string) {\n\to.SessionToken = &v\n}\n\nfunc (o SuccessfulNativeLogin) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SuccessfulNativeLogin) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ContinueWith) {\n\t\ttoSerialize[\"continue_with\"] = o.ContinueWith\n\t}\n\ttoSerialize[\"session\"] = o.Session\n\tif !IsNil(o.SessionToken) {\n\t\ttoSerialize[\"session_token\"] = o.SessionToken\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SuccessfulNativeLogin) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"session\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSuccessfulNativeLogin := _SuccessfulNativeLogin{}\n\n\terr = json.Unmarshal(data, &varSuccessfulNativeLogin)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SuccessfulNativeLogin(varSuccessfulNativeLogin)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"continue_with\")\n\t\tdelete(additionalProperties, \"session\")\n\t\tdelete(additionalProperties, \"session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSuccessfulNativeLogin struct {\n\tvalue *SuccessfulNativeLogin\n\tisSet bool\n}\n\nfunc (v NullableSuccessfulNativeLogin) Get() *SuccessfulNativeLogin {\n\treturn v.value\n}\n\nfunc (v *NullableSuccessfulNativeLogin) Set(val *SuccessfulNativeLogin) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSuccessfulNativeLogin) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSuccessfulNativeLogin) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSuccessfulNativeLogin(val *SuccessfulNativeLogin) *NullableSuccessfulNativeLogin {\n\treturn &NullableSuccessfulNativeLogin{value: val, isSet: true}\n}\n\nfunc (v NullableSuccessfulNativeLogin) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSuccessfulNativeLogin) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_successful_native_registration.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the SuccessfulNativeRegistration type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &SuccessfulNativeRegistration{}\n\n// SuccessfulNativeRegistration The Response for Registration Flows via API\ntype SuccessfulNativeRegistration struct {\n\t// Contains a list of actions, that could follow this flow  It can, for example, this will contain a reference to the verification flow, created as part of the user's registration or the token of the session.\n\tContinueWith []ContinueWith `json:\"continue_with,omitempty\"`\n\tIdentity     Identity       `json:\"identity\"`\n\tSession      *Session       `json:\"session,omitempty\"`\n\t// The Session Token  This field is only set when the session hook is configured as a post-registration hook.  A session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization Header:  Authorization: bearer ${session-token}  The session token is only issued for API flows, not for Browser flows!\n\tSessionToken         *string `json:\"session_token,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _SuccessfulNativeRegistration SuccessfulNativeRegistration\n\n// NewSuccessfulNativeRegistration instantiates a new SuccessfulNativeRegistration object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewSuccessfulNativeRegistration(identity Identity) *SuccessfulNativeRegistration {\n\tthis := SuccessfulNativeRegistration{}\n\tthis.Identity = identity\n\treturn &this\n}\n\n// NewSuccessfulNativeRegistrationWithDefaults instantiates a new SuccessfulNativeRegistration object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewSuccessfulNativeRegistrationWithDefaults() *SuccessfulNativeRegistration {\n\tthis := SuccessfulNativeRegistration{}\n\treturn &this\n}\n\n// GetContinueWith returns the ContinueWith field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeRegistration) GetContinueWith() []ContinueWith {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\tvar ret []ContinueWith\n\t\treturn ret\n\t}\n\treturn o.ContinueWith\n}\n\n// GetContinueWithOk returns a tuple with the ContinueWith field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeRegistration) GetContinueWithOk() ([]ContinueWith, bool) {\n\tif o == nil || IsNil(o.ContinueWith) {\n\t\treturn nil, false\n\t}\n\treturn o.ContinueWith, true\n}\n\n// HasContinueWith returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeRegistration) HasContinueWith() bool {\n\tif o != nil && !IsNil(o.ContinueWith) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContinueWith gets a reference to the given []ContinueWith and assigns it to the ContinueWith field.\nfunc (o *SuccessfulNativeRegistration) SetContinueWith(v []ContinueWith) {\n\to.ContinueWith = v\n}\n\n// GetIdentity returns the Identity field value\nfunc (o *SuccessfulNativeRegistration) GetIdentity() Identity {\n\tif o == nil {\n\t\tvar ret Identity\n\t\treturn ret\n\t}\n\n\treturn o.Identity\n}\n\n// GetIdentityOk returns a tuple with the Identity field value\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeRegistration) GetIdentityOk() (*Identity, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identity, true\n}\n\n// SetIdentity sets field value\nfunc (o *SuccessfulNativeRegistration) SetIdentity(v Identity) {\n\to.Identity = v\n}\n\n// GetSession returns the Session field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeRegistration) GetSession() Session {\n\tif o == nil || IsNil(o.Session) {\n\t\tvar ret Session\n\t\treturn ret\n\t}\n\treturn *o.Session\n}\n\n// GetSessionOk returns a tuple with the Session field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeRegistration) GetSessionOk() (*Session, bool) {\n\tif o == nil || IsNil(o.Session) {\n\t\treturn nil, false\n\t}\n\treturn o.Session, true\n}\n\n// HasSession returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeRegistration) HasSession() bool {\n\tif o != nil && !IsNil(o.Session) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSession gets a reference to the given Session and assigns it to the Session field.\nfunc (o *SuccessfulNativeRegistration) SetSession(v Session) {\n\to.Session = &v\n}\n\n// GetSessionToken returns the SessionToken field value if set, zero value otherwise.\nfunc (o *SuccessfulNativeRegistration) GetSessionToken() string {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.SessionToken\n}\n\n// GetSessionTokenOk returns a tuple with the SessionToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *SuccessfulNativeRegistration) GetSessionTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.SessionToken) {\n\t\treturn nil, false\n\t}\n\treturn o.SessionToken, true\n}\n\n// HasSessionToken returns a boolean if a field has been set.\nfunc (o *SuccessfulNativeRegistration) HasSessionToken() bool {\n\tif o != nil && !IsNil(o.SessionToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetSessionToken gets a reference to the given string and assigns it to the SessionToken field.\nfunc (o *SuccessfulNativeRegistration) SetSessionToken(v string) {\n\to.SessionToken = &v\n}\n\nfunc (o SuccessfulNativeRegistration) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o SuccessfulNativeRegistration) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.ContinueWith) {\n\t\ttoSerialize[\"continue_with\"] = o.ContinueWith\n\t}\n\ttoSerialize[\"identity\"] = o.Identity\n\tif !IsNil(o.Session) {\n\t\ttoSerialize[\"session\"] = o.Session\n\t}\n\tif !IsNil(o.SessionToken) {\n\t\ttoSerialize[\"session_token\"] = o.SessionToken\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *SuccessfulNativeRegistration) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identity\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarSuccessfulNativeRegistration := _SuccessfulNativeRegistration{}\n\n\terr = json.Unmarshal(data, &varSuccessfulNativeRegistration)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = SuccessfulNativeRegistration(varSuccessfulNativeRegistration)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"continue_with\")\n\t\tdelete(additionalProperties, \"identity\")\n\t\tdelete(additionalProperties, \"session\")\n\t\tdelete(additionalProperties, \"session_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableSuccessfulNativeRegistration struct {\n\tvalue *SuccessfulNativeRegistration\n\tisSet bool\n}\n\nfunc (v NullableSuccessfulNativeRegistration) Get() *SuccessfulNativeRegistration {\n\treturn v.value\n}\n\nfunc (v *NullableSuccessfulNativeRegistration) Set(val *SuccessfulNativeRegistration) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableSuccessfulNativeRegistration) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableSuccessfulNativeRegistration) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableSuccessfulNativeRegistration(val *SuccessfulNativeRegistration) *NullableSuccessfulNativeRegistration {\n\treturn &NullableSuccessfulNativeRegistration{value: val, isSet: true}\n}\n\nfunc (v NullableSuccessfulNativeRegistration) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableSuccessfulNativeRegistration) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_token_pagination.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the TokenPagination type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &TokenPagination{}\n\n// TokenPagination struct for TokenPagination\ntype TokenPagination struct {\n\t// Items per page  This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\tPageSize *int64 `json:\"page_size,omitempty\"`\n\t// Next Page Token  The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\n\tPageToken            *string `json:\"page_token,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _TokenPagination TokenPagination\n\n// NewTokenPagination instantiates a new TokenPagination object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewTokenPagination() *TokenPagination {\n\tthis := TokenPagination{}\n\tvar pageSize int64 = 250\n\tthis.PageSize = &pageSize\n\treturn &this\n}\n\n// NewTokenPaginationWithDefaults instantiates a new TokenPagination object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewTokenPaginationWithDefaults() *TokenPagination {\n\tthis := TokenPagination{}\n\tvar pageSize int64 = 250\n\tthis.PageSize = &pageSize\n\treturn &this\n}\n\n// GetPageSize returns the PageSize field value if set, zero value otherwise.\nfunc (o *TokenPagination) GetPageSize() int64 {\n\tif o == nil || IsNil(o.PageSize) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.PageSize\n}\n\n// GetPageSizeOk returns a tuple with the PageSize field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *TokenPagination) GetPageSizeOk() (*int64, bool) {\n\tif o == nil || IsNil(o.PageSize) {\n\t\treturn nil, false\n\t}\n\treturn o.PageSize, true\n}\n\n// HasPageSize returns a boolean if a field has been set.\nfunc (o *TokenPagination) HasPageSize() bool {\n\tif o != nil && !IsNil(o.PageSize) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPageSize gets a reference to the given int64 and assigns it to the PageSize field.\nfunc (o *TokenPagination) SetPageSize(v int64) {\n\to.PageSize = &v\n}\n\n// GetPageToken returns the PageToken field value if set, zero value otherwise.\nfunc (o *TokenPagination) GetPageToken() string {\n\tif o == nil || IsNil(o.PageToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PageToken\n}\n\n// GetPageTokenOk returns a tuple with the PageToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *TokenPagination) GetPageTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.PageToken) {\n\t\treturn nil, false\n\t}\n\treturn o.PageToken, true\n}\n\n// HasPageToken returns a boolean if a field has been set.\nfunc (o *TokenPagination) HasPageToken() bool {\n\tif o != nil && !IsNil(o.PageToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPageToken gets a reference to the given string and assigns it to the PageToken field.\nfunc (o *TokenPagination) SetPageToken(v string) {\n\to.PageToken = &v\n}\n\nfunc (o TokenPagination) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o TokenPagination) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.PageSize) {\n\t\ttoSerialize[\"page_size\"] = o.PageSize\n\t}\n\tif !IsNil(o.PageToken) {\n\t\ttoSerialize[\"page_token\"] = o.PageToken\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *TokenPagination) UnmarshalJSON(data []byte) (err error) {\n\tvarTokenPagination := _TokenPagination{}\n\n\terr = json.Unmarshal(data, &varTokenPagination)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = TokenPagination(varTokenPagination)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"page_size\")\n\t\tdelete(additionalProperties, \"page_token\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableTokenPagination struct {\n\tvalue *TokenPagination\n\tisSet bool\n}\n\nfunc (v NullableTokenPagination) Get() *TokenPagination {\n\treturn v.value\n}\n\nfunc (v *NullableTokenPagination) Set(val *TokenPagination) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableTokenPagination) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableTokenPagination) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableTokenPagination(val *TokenPagination) *NullableTokenPagination {\n\treturn &NullableTokenPagination{value: val, isSet: true}\n}\n\nfunc (v NullableTokenPagination) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableTokenPagination) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_token_pagination_headers.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the TokenPaginationHeaders type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &TokenPaginationHeaders{}\n\n// TokenPaginationHeaders struct for TokenPaginationHeaders\ntype TokenPaginationHeaders struct {\n\t// The link header contains pagination links.  For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).  in: header\n\tLink *string `json:\"link,omitempty\"`\n\t// The total number of clients.  in: header\n\tXTotalCount          *string `json:\"x-total-count,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _TokenPaginationHeaders TokenPaginationHeaders\n\n// NewTokenPaginationHeaders instantiates a new TokenPaginationHeaders object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewTokenPaginationHeaders() *TokenPaginationHeaders {\n\tthis := TokenPaginationHeaders{}\n\treturn &this\n}\n\n// NewTokenPaginationHeadersWithDefaults instantiates a new TokenPaginationHeaders object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewTokenPaginationHeadersWithDefaults() *TokenPaginationHeaders {\n\tthis := TokenPaginationHeaders{}\n\treturn &this\n}\n\n// GetLink returns the Link field value if set, zero value otherwise.\nfunc (o *TokenPaginationHeaders) GetLink() string {\n\tif o == nil || IsNil(o.Link) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Link\n}\n\n// GetLinkOk returns a tuple with the Link field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *TokenPaginationHeaders) GetLinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Link) {\n\t\treturn nil, false\n\t}\n\treturn o.Link, true\n}\n\n// HasLink returns a boolean if a field has been set.\nfunc (o *TokenPaginationHeaders) HasLink() bool {\n\tif o != nil && !IsNil(o.Link) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLink gets a reference to the given string and assigns it to the Link field.\nfunc (o *TokenPaginationHeaders) SetLink(v string) {\n\to.Link = &v\n}\n\n// GetXTotalCount returns the XTotalCount field value if set, zero value otherwise.\nfunc (o *TokenPaginationHeaders) GetXTotalCount() string {\n\tif o == nil || IsNil(o.XTotalCount) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.XTotalCount\n}\n\n// GetXTotalCountOk returns a tuple with the XTotalCount field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *TokenPaginationHeaders) GetXTotalCountOk() (*string, bool) {\n\tif o == nil || IsNil(o.XTotalCount) {\n\t\treturn nil, false\n\t}\n\treturn o.XTotalCount, true\n}\n\n// HasXTotalCount returns a boolean if a field has been set.\nfunc (o *TokenPaginationHeaders) HasXTotalCount() bool {\n\tif o != nil && !IsNil(o.XTotalCount) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetXTotalCount gets a reference to the given string and assigns it to the XTotalCount field.\nfunc (o *TokenPaginationHeaders) SetXTotalCount(v string) {\n\to.XTotalCount = &v\n}\n\nfunc (o TokenPaginationHeaders) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o TokenPaginationHeaders) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Link) {\n\t\ttoSerialize[\"link\"] = o.Link\n\t}\n\tif !IsNil(o.XTotalCount) {\n\t\ttoSerialize[\"x-total-count\"] = o.XTotalCount\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *TokenPaginationHeaders) UnmarshalJSON(data []byte) (err error) {\n\tvarTokenPaginationHeaders := _TokenPaginationHeaders{}\n\n\terr = json.Unmarshal(data, &varTokenPaginationHeaders)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = TokenPaginationHeaders(varTokenPaginationHeaders)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"link\")\n\t\tdelete(additionalProperties, \"x-total-count\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableTokenPaginationHeaders struct {\n\tvalue *TokenPaginationHeaders\n\tisSet bool\n}\n\nfunc (v NullableTokenPaginationHeaders) Get() *TokenPaginationHeaders {\n\treturn v.value\n}\n\nfunc (v *NullableTokenPaginationHeaders) Set(val *TokenPaginationHeaders) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableTokenPaginationHeaders) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableTokenPaginationHeaders) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableTokenPaginationHeaders(val *TokenPaginationHeaders) *NullableTokenPaginationHeaders {\n\treturn &NullableTokenPaginationHeaders{value: val, isSet: true}\n}\n\nfunc (v NullableTokenPaginationHeaders) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableTokenPaginationHeaders) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_container.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiContainer type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiContainer{}\n\n// UiContainer Container represents a HTML Form. The container can work with both HTTP Form and JSON requests\ntype UiContainer struct {\n\t// Action should be used as the form action URL `<form action=\\\"{{ .Action }}\\\" method=\\\"post\\\">`.\n\tAction   string   `json:\"action\"`\n\tMessages []UiText `json:\"messages,omitempty\"`\n\t// Method is the form method (e.g. POST)\n\tMethod               string   `json:\"method\"`\n\tNodes                []UiNode `json:\"nodes\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiContainer UiContainer\n\n// NewUiContainer instantiates a new UiContainer object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiContainer(action string, method string, nodes []UiNode) *UiContainer {\n\tthis := UiContainer{}\n\tthis.Action = action\n\tthis.Method = method\n\tthis.Nodes = nodes\n\treturn &this\n}\n\n// NewUiContainerWithDefaults instantiates a new UiContainer object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiContainerWithDefaults() *UiContainer {\n\tthis := UiContainer{}\n\treturn &this\n}\n\n// GetAction returns the Action field value\nfunc (o *UiContainer) GetAction() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Action\n}\n\n// GetActionOk returns a tuple with the Action field value\n// and a boolean to check if the value has been set.\nfunc (o *UiContainer) GetActionOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Action, true\n}\n\n// SetAction sets field value\nfunc (o *UiContainer) SetAction(v string) {\n\to.Action = v\n}\n\n// GetMessages returns the Messages field value if set, zero value otherwise.\nfunc (o *UiContainer) GetMessages() []UiText {\n\tif o == nil || IsNil(o.Messages) {\n\t\tvar ret []UiText\n\t\treturn ret\n\t}\n\treturn o.Messages\n}\n\n// GetMessagesOk returns a tuple with the Messages field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiContainer) GetMessagesOk() ([]UiText, bool) {\n\tif o == nil || IsNil(o.Messages) {\n\t\treturn nil, false\n\t}\n\treturn o.Messages, true\n}\n\n// HasMessages returns a boolean if a field has been set.\nfunc (o *UiContainer) HasMessages() bool {\n\tif o != nil && !IsNil(o.Messages) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMessages gets a reference to the given []UiText and assigns it to the Messages field.\nfunc (o *UiContainer) SetMessages(v []UiText) {\n\to.Messages = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UiContainer) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UiContainer) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UiContainer) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetNodes returns the Nodes field value\nfunc (o *UiContainer) GetNodes() []UiNode {\n\tif o == nil {\n\t\tvar ret []UiNode\n\t\treturn ret\n\t}\n\n\treturn o.Nodes\n}\n\n// GetNodesOk returns a tuple with the Nodes field value\n// and a boolean to check if the value has been set.\nfunc (o *UiContainer) GetNodesOk() ([]UiNode, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.Nodes, true\n}\n\n// SetNodes sets field value\nfunc (o *UiContainer) SetNodes(v []UiNode) {\n\to.Nodes = v\n}\n\nfunc (o UiContainer) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiContainer) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"action\"] = o.Action\n\tif !IsNil(o.Messages) {\n\t\ttoSerialize[\"messages\"] = o.Messages\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"nodes\"] = o.Nodes\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiContainer) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"action\",\n\t\t\"method\",\n\t\t\"nodes\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiContainer := _UiContainer{}\n\n\terr = json.Unmarshal(data, &varUiContainer)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiContainer(varUiContainer)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"action\")\n\t\tdelete(additionalProperties, \"messages\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"nodes\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiContainer struct {\n\tvalue *UiContainer\n\tisSet bool\n}\n\nfunc (v NullableUiContainer) Get() *UiContainer {\n\treturn v.value\n}\n\nfunc (v *NullableUiContainer) Set(val *UiContainer) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiContainer) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiContainer) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiContainer(val *UiContainer) *NullableUiContainer {\n\treturn &NullableUiContainer{value: val, isSet: true}\n}\n\nfunc (v NullableUiContainer) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiContainer) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_node.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNode type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNode{}\n\n// UiNode Nodes are represented as HTML elements or their native UI equivalents. For example, a node can be an `<img>` tag, or an `<input element>` but also `some plain text`.\ntype UiNode struct {\n\tAttributes UiNodeAttributes `json:\"attributes\"`\n\t// Group specifies which group (e.g. password authenticator) this node belongs to. default DefaultGroup password PasswordGroup oidc OpenIDConnectGroup profile ProfileGroup link LinkGroup code CodeGroup totp TOTPGroup lookup_secret LookupGroup webauthn WebAuthnGroup passkey PasskeyGroup identifier_first IdentifierFirstGroup captcha CaptchaGroup saml SAMLGroup\n\tGroup    string     `json:\"group\"`\n\tMessages []UiText   `json:\"messages\"`\n\tMeta     UiNodeMeta `json:\"meta\"`\n\t// The node's type text Text input Input img Image a Anchor script Script div Division\n\tType                 string `json:\"type\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNode UiNode\n\n// NewUiNode instantiates a new UiNode object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNode(attributes UiNodeAttributes, group string, messages []UiText, meta UiNodeMeta, type_ string) *UiNode {\n\tthis := UiNode{}\n\tthis.Attributes = attributes\n\tthis.Group = group\n\tthis.Messages = messages\n\tthis.Meta = meta\n\tthis.Type = type_\n\treturn &this\n}\n\n// NewUiNodeWithDefaults instantiates a new UiNode object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeWithDefaults() *UiNode {\n\tthis := UiNode{}\n\treturn &this\n}\n\n// GetAttributes returns the Attributes field value\nfunc (o *UiNode) GetAttributes() UiNodeAttributes {\n\tif o == nil {\n\t\tvar ret UiNodeAttributes\n\t\treturn ret\n\t}\n\n\treturn o.Attributes\n}\n\n// GetAttributesOk returns a tuple with the Attributes field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetAttributesOk() (*UiNodeAttributes, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Attributes, true\n}\n\n// SetAttributes sets field value\nfunc (o *UiNode) SetAttributes(v UiNodeAttributes) {\n\to.Attributes = v\n}\n\n// GetGroup returns the Group field value\nfunc (o *UiNode) GetGroup() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Group\n}\n\n// GetGroupOk returns a tuple with the Group field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetGroupOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Group, true\n}\n\n// SetGroup sets field value\nfunc (o *UiNode) SetGroup(v string) {\n\to.Group = v\n}\n\n// GetMessages returns the Messages field value\nfunc (o *UiNode) GetMessages() []UiText {\n\tif o == nil {\n\t\tvar ret []UiText\n\t\treturn ret\n\t}\n\n\treturn o.Messages\n}\n\n// GetMessagesOk returns a tuple with the Messages field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetMessagesOk() ([]UiText, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn o.Messages, true\n}\n\n// SetMessages sets field value\nfunc (o *UiNode) SetMessages(v []UiText) {\n\to.Messages = v\n}\n\n// GetMeta returns the Meta field value\nfunc (o *UiNode) GetMeta() UiNodeMeta {\n\tif o == nil {\n\t\tvar ret UiNodeMeta\n\t\treturn ret\n\t}\n\n\treturn o.Meta\n}\n\n// GetMetaOk returns a tuple with the Meta field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetMetaOk() (*UiNodeMeta, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Meta, true\n}\n\n// SetMeta sets field value\nfunc (o *UiNode) SetMeta(v UiNodeMeta) {\n\to.Meta = v\n}\n\n// GetType returns the Type field value\nfunc (o *UiNode) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNode) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *UiNode) SetType(v string) {\n\to.Type = v\n}\n\nfunc (o UiNode) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNode) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"attributes\"] = o.Attributes\n\ttoSerialize[\"group\"] = o.Group\n\ttoSerialize[\"messages\"] = o.Messages\n\ttoSerialize[\"meta\"] = o.Meta\n\ttoSerialize[\"type\"] = o.Type\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNode) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"attributes\",\n\t\t\"group\",\n\t\t\"messages\",\n\t\t\"meta\",\n\t\t\"type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNode := _UiNode{}\n\n\terr = json.Unmarshal(data, &varUiNode)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNode(varUiNode)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"attributes\")\n\t\tdelete(additionalProperties, \"group\")\n\t\tdelete(additionalProperties, \"messages\")\n\t\tdelete(additionalProperties, \"meta\")\n\t\tdelete(additionalProperties, \"type\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNode struct {\n\tvalue *UiNode\n\tisSet bool\n}\n\nfunc (v NullableUiNode) Get() *UiNode {\n\treturn v.value\n}\n\nfunc (v *NullableUiNode) Set(val *UiNode) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNode) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNode) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNode(val *UiNode) *NullableUiNode {\n\treturn &NullableUiNode{value: val, isSet: true}\n}\n\nfunc (v NullableUiNode) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNode) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_node_anchor_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeAnchorAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeAnchorAttributes{}\n\n// UiNodeAnchorAttributes struct for UiNodeAnchorAttributes\ntype UiNodeAnchorAttributes struct {\n\t// The link's href (destination) URL.  format: uri\n\tHref string `json:\"href\"`\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"a\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType             string `json:\"node_type\"`\n\tTitle                UiText `json:\"title\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeAnchorAttributes UiNodeAnchorAttributes\n\n// NewUiNodeAnchorAttributes instantiates a new UiNodeAnchorAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeAnchorAttributes(href string, id string, nodeType string, title UiText) *UiNodeAnchorAttributes {\n\tthis := UiNodeAnchorAttributes{}\n\tthis.Href = href\n\tthis.Id = id\n\tthis.NodeType = nodeType\n\tthis.Title = title\n\treturn &this\n}\n\n// NewUiNodeAnchorAttributesWithDefaults instantiates a new UiNodeAnchorAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeAnchorAttributesWithDefaults() *UiNodeAnchorAttributes {\n\tthis := UiNodeAnchorAttributes{}\n\treturn &this\n}\n\n// GetHref returns the Href field value\nfunc (o *UiNodeAnchorAttributes) GetHref() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Href\n}\n\n// GetHrefOk returns a tuple with the Href field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeAnchorAttributes) GetHrefOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Href, true\n}\n\n// SetHref sets field value\nfunc (o *UiNodeAnchorAttributes) SetHref(v string) {\n\to.Href = v\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeAnchorAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeAnchorAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeAnchorAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeAnchorAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeAnchorAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeAnchorAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetTitle returns the Title field value\nfunc (o *UiNodeAnchorAttributes) GetTitle() UiText {\n\tif o == nil {\n\t\tvar ret UiText\n\t\treturn ret\n\t}\n\n\treturn o.Title\n}\n\n// GetTitleOk returns a tuple with the Title field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeAnchorAttributes) GetTitleOk() (*UiText, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Title, true\n}\n\n// SetTitle sets field value\nfunc (o *UiNodeAnchorAttributes) SetTitle(v UiText) {\n\to.Title = v\n}\n\nfunc (o UiNodeAnchorAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeAnchorAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"href\"] = o.Href\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"node_type\"] = o.NodeType\n\ttoSerialize[\"title\"] = o.Title\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeAnchorAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"href\",\n\t\t\"id\",\n\t\t\"node_type\",\n\t\t\"title\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeAnchorAttributes := _UiNodeAnchorAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeAnchorAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeAnchorAttributes(varUiNodeAnchorAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"href\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"title\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeAnchorAttributes struct {\n\tvalue *UiNodeAnchorAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeAnchorAttributes) Get() *UiNodeAnchorAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeAnchorAttributes) Set(val *UiNodeAnchorAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeAnchorAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeAnchorAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeAnchorAttributes(val *UiNodeAnchorAttributes) *NullableUiNodeAnchorAttributes {\n\treturn &NullableUiNodeAnchorAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeAnchorAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeAnchorAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_node_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UiNodeAttributes - struct for UiNodeAttributes\ntype UiNodeAttributes struct {\n\tUiNodeAnchorAttributes   *UiNodeAnchorAttributes\n\tUiNodeDivisionAttributes *UiNodeDivisionAttributes\n\tUiNodeImageAttributes    *UiNodeImageAttributes\n\tUiNodeInputAttributes    *UiNodeInputAttributes\n\tUiNodeScriptAttributes   *UiNodeScriptAttributes\n\tUiNodeTextAttributes     *UiNodeTextAttributes\n}\n\n// UiNodeAnchorAttributesAsUiNodeAttributes is a convenience function that returns UiNodeAnchorAttributes wrapped in UiNodeAttributes\nfunc UiNodeAnchorAttributesAsUiNodeAttributes(v *UiNodeAnchorAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeAnchorAttributes: v,\n\t}\n}\n\n// UiNodeDivisionAttributesAsUiNodeAttributes is a convenience function that returns UiNodeDivisionAttributes wrapped in UiNodeAttributes\nfunc UiNodeDivisionAttributesAsUiNodeAttributes(v *UiNodeDivisionAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeDivisionAttributes: v,\n\t}\n}\n\n// UiNodeImageAttributesAsUiNodeAttributes is a convenience function that returns UiNodeImageAttributes wrapped in UiNodeAttributes\nfunc UiNodeImageAttributesAsUiNodeAttributes(v *UiNodeImageAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeImageAttributes: v,\n\t}\n}\n\n// UiNodeInputAttributesAsUiNodeAttributes is a convenience function that returns UiNodeInputAttributes wrapped in UiNodeAttributes\nfunc UiNodeInputAttributesAsUiNodeAttributes(v *UiNodeInputAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeInputAttributes: v,\n\t}\n}\n\n// UiNodeScriptAttributesAsUiNodeAttributes is a convenience function that returns UiNodeScriptAttributes wrapped in UiNodeAttributes\nfunc UiNodeScriptAttributesAsUiNodeAttributes(v *UiNodeScriptAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeScriptAttributes: v,\n\t}\n}\n\n// UiNodeTextAttributesAsUiNodeAttributes is a convenience function that returns UiNodeTextAttributes wrapped in UiNodeAttributes\nfunc UiNodeTextAttributesAsUiNodeAttributes(v *UiNodeTextAttributes) UiNodeAttributes {\n\treturn UiNodeAttributes{\n\t\tUiNodeTextAttributes: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UiNodeAttributes) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'a'\n\tif jsonDict[\"node_type\"] == \"a\" {\n\t\t// try to unmarshal JSON data into UiNodeAnchorAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeAnchorAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeAnchorAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeAnchorAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeAnchorAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'div'\n\tif jsonDict[\"node_type\"] == \"div\" {\n\t\t// try to unmarshal JSON data into UiNodeDivisionAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeDivisionAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeDivisionAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeDivisionAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeDivisionAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'img'\n\tif jsonDict[\"node_type\"] == \"img\" {\n\t\t// try to unmarshal JSON data into UiNodeImageAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeImageAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeImageAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeImageAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeImageAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'input'\n\tif jsonDict[\"node_type\"] == \"input\" {\n\t\t// try to unmarshal JSON data into UiNodeInputAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeInputAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeInputAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeInputAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeInputAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'script'\n\tif jsonDict[\"node_type\"] == \"script\" {\n\t\t// try to unmarshal JSON data into UiNodeScriptAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeScriptAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeScriptAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeScriptAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeScriptAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'text'\n\tif jsonDict[\"node_type\"] == \"text\" {\n\t\t// try to unmarshal JSON data into UiNodeTextAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeTextAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeTextAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeTextAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeTextAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeAnchorAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeAnchorAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeAnchorAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeAnchorAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeAnchorAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeAnchorAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeAnchorAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeDivisionAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeDivisionAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeDivisionAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeDivisionAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeDivisionAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeDivisionAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeDivisionAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeImageAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeImageAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeImageAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeImageAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeImageAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeImageAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeImageAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeInputAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeInputAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeInputAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeInputAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeInputAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeInputAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeInputAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeScriptAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeScriptAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeScriptAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeScriptAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeScriptAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeScriptAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeScriptAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'uiNodeTextAttributes'\n\tif jsonDict[\"node_type\"] == \"uiNodeTextAttributes\" {\n\t\t// try to unmarshal JSON data into UiNodeTextAttributes\n\t\terr = json.Unmarshal(data, &dst.UiNodeTextAttributes)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UiNodeTextAttributes, return on the first match\n\t\t} else {\n\t\t\tdst.UiNodeTextAttributes = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UiNodeAttributes as UiNodeTextAttributes: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UiNodeAttributes) MarshalJSON() ([]byte, error) {\n\tif src.UiNodeAnchorAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeAnchorAttributes)\n\t}\n\n\tif src.UiNodeDivisionAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeDivisionAttributes)\n\t}\n\n\tif src.UiNodeImageAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeImageAttributes)\n\t}\n\n\tif src.UiNodeInputAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeInputAttributes)\n\t}\n\n\tif src.UiNodeScriptAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeScriptAttributes)\n\t}\n\n\tif src.UiNodeTextAttributes != nil {\n\t\treturn json.Marshal(&src.UiNodeTextAttributes)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UiNodeAttributes) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UiNodeAnchorAttributes != nil {\n\t\treturn obj.UiNodeAnchorAttributes\n\t}\n\n\tif obj.UiNodeDivisionAttributes != nil {\n\t\treturn obj.UiNodeDivisionAttributes\n\t}\n\n\tif obj.UiNodeImageAttributes != nil {\n\t\treturn obj.UiNodeImageAttributes\n\t}\n\n\tif obj.UiNodeInputAttributes != nil {\n\t\treturn obj.UiNodeInputAttributes\n\t}\n\n\tif obj.UiNodeScriptAttributes != nil {\n\t\treturn obj.UiNodeScriptAttributes\n\t}\n\n\tif obj.UiNodeTextAttributes != nil {\n\t\treturn obj.UiNodeTextAttributes\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UiNodeAttributes) GetActualInstanceValue() interface{} {\n\tif obj.UiNodeAnchorAttributes != nil {\n\t\treturn *obj.UiNodeAnchorAttributes\n\t}\n\n\tif obj.UiNodeDivisionAttributes != nil {\n\t\treturn *obj.UiNodeDivisionAttributes\n\t}\n\n\tif obj.UiNodeImageAttributes != nil {\n\t\treturn *obj.UiNodeImageAttributes\n\t}\n\n\tif obj.UiNodeInputAttributes != nil {\n\t\treturn *obj.UiNodeInputAttributes\n\t}\n\n\tif obj.UiNodeScriptAttributes != nil {\n\t\treturn *obj.UiNodeScriptAttributes\n\t}\n\n\tif obj.UiNodeTextAttributes != nil {\n\t\treturn *obj.UiNodeTextAttributes\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUiNodeAttributes struct {\n\tvalue *UiNodeAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeAttributes) Get() *UiNodeAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeAttributes) Set(val *UiNodeAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeAttributes(val *UiNodeAttributes) *NullableUiNodeAttributes {\n\treturn &NullableUiNodeAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_node_division_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeDivisionAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeDivisionAttributes{}\n\n// UiNodeDivisionAttributes Division sections are used for interactive widgets that require a hook in the DOM / view.\ntype UiNodeDivisionAttributes struct {\n\t// A classname that should be rendered into the DOM.\n\tClass *string `json:\"class,omitempty\"`\n\t// Data is a map of key-value pairs that are passed to the division.  They may be used for `data-...` attributes.\n\tData *map[string]string `json:\"data,omitempty\"`\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// NodeType represents this node's type. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \\\"script\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType             string `json:\"node_type\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeDivisionAttributes UiNodeDivisionAttributes\n\n// NewUiNodeDivisionAttributes instantiates a new UiNodeDivisionAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeDivisionAttributes(id string, nodeType string) *UiNodeDivisionAttributes {\n\tthis := UiNodeDivisionAttributes{}\n\tthis.Id = id\n\tthis.NodeType = nodeType\n\treturn &this\n}\n\n// NewUiNodeDivisionAttributesWithDefaults instantiates a new UiNodeDivisionAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeDivisionAttributesWithDefaults() *UiNodeDivisionAttributes {\n\tthis := UiNodeDivisionAttributes{}\n\treturn &this\n}\n\n// GetClass returns the Class field value if set, zero value otherwise.\nfunc (o *UiNodeDivisionAttributes) GetClass() string {\n\tif o == nil || IsNil(o.Class) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Class\n}\n\n// GetClassOk returns a tuple with the Class field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeDivisionAttributes) GetClassOk() (*string, bool) {\n\tif o == nil || IsNil(o.Class) {\n\t\treturn nil, false\n\t}\n\treturn o.Class, true\n}\n\n// HasClass returns a boolean if a field has been set.\nfunc (o *UiNodeDivisionAttributes) HasClass() bool {\n\tif o != nil && !IsNil(o.Class) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetClass gets a reference to the given string and assigns it to the Class field.\nfunc (o *UiNodeDivisionAttributes) SetClass(v string) {\n\to.Class = &v\n}\n\n// GetData returns the Data field value if set, zero value otherwise.\nfunc (o *UiNodeDivisionAttributes) GetData() map[string]string {\n\tif o == nil || IsNil(o.Data) {\n\t\tvar ret map[string]string\n\t\treturn ret\n\t}\n\treturn *o.Data\n}\n\n// GetDataOk returns a tuple with the Data field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeDivisionAttributes) GetDataOk() (*map[string]string, bool) {\n\tif o == nil || IsNil(o.Data) {\n\t\treturn nil, false\n\t}\n\treturn o.Data, true\n}\n\n// HasData returns a boolean if a field has been set.\nfunc (o *UiNodeDivisionAttributes) HasData() bool {\n\tif o != nil && !IsNil(o.Data) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetData gets a reference to the given map[string]string and assigns it to the Data field.\nfunc (o *UiNodeDivisionAttributes) SetData(v map[string]string) {\n\to.Data = &v\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeDivisionAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeDivisionAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeDivisionAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeDivisionAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeDivisionAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeDivisionAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\nfunc (o UiNodeDivisionAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeDivisionAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Class) {\n\t\ttoSerialize[\"class\"] = o.Class\n\t}\n\tif !IsNil(o.Data) {\n\t\ttoSerialize[\"data\"] = o.Data\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"node_type\"] = o.NodeType\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeDivisionAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"node_type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeDivisionAttributes := _UiNodeDivisionAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeDivisionAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeDivisionAttributes(varUiNodeDivisionAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"class\")\n\t\tdelete(additionalProperties, \"data\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeDivisionAttributes struct {\n\tvalue *UiNodeDivisionAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeDivisionAttributes) Get() *UiNodeDivisionAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeDivisionAttributes) Set(val *UiNodeDivisionAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeDivisionAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeDivisionAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeDivisionAttributes(val *UiNodeDivisionAttributes) *NullableUiNodeDivisionAttributes {\n\treturn &NullableUiNodeDivisionAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeDivisionAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeDivisionAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_node_image_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeImageAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeImageAttributes{}\n\n// UiNodeImageAttributes struct for UiNodeImageAttributes\ntype UiNodeImageAttributes struct {\n\t// Height of the image\n\tHeight int64 `json:\"height\"`\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"img\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType string `json:\"node_type\"`\n\t// The image's source URL.  format: uri\n\tSrc string `json:\"src\"`\n\t// Width of the image\n\tWidth                int64 `json:\"width\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeImageAttributes UiNodeImageAttributes\n\n// NewUiNodeImageAttributes instantiates a new UiNodeImageAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeImageAttributes(height int64, id string, nodeType string, src string, width int64) *UiNodeImageAttributes {\n\tthis := UiNodeImageAttributes{}\n\tthis.Height = height\n\tthis.Id = id\n\tthis.NodeType = nodeType\n\tthis.Src = src\n\tthis.Width = width\n\treturn &this\n}\n\n// NewUiNodeImageAttributesWithDefaults instantiates a new UiNodeImageAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeImageAttributesWithDefaults() *UiNodeImageAttributes {\n\tthis := UiNodeImageAttributes{}\n\treturn &this\n}\n\n// GetHeight returns the Height field value\nfunc (o *UiNodeImageAttributes) GetHeight() int64 {\n\tif o == nil {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\n\treturn o.Height\n}\n\n// GetHeightOk returns a tuple with the Height field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetHeightOk() (*int64, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Height, true\n}\n\n// SetHeight sets field value\nfunc (o *UiNodeImageAttributes) SetHeight(v int64) {\n\to.Height = v\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeImageAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeImageAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeImageAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeImageAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetSrc returns the Src field value\nfunc (o *UiNodeImageAttributes) GetSrc() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Src\n}\n\n// GetSrcOk returns a tuple with the Src field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetSrcOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Src, true\n}\n\n// SetSrc sets field value\nfunc (o *UiNodeImageAttributes) SetSrc(v string) {\n\to.Src = v\n}\n\n// GetWidth returns the Width field value\nfunc (o *UiNodeImageAttributes) GetWidth() int64 {\n\tif o == nil {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\n\treturn o.Width\n}\n\n// GetWidthOk returns a tuple with the Width field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeImageAttributes) GetWidthOk() (*int64, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Width, true\n}\n\n// SetWidth sets field value\nfunc (o *UiNodeImageAttributes) SetWidth(v int64) {\n\to.Width = v\n}\n\nfunc (o UiNodeImageAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeImageAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"height\"] = o.Height\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"node_type\"] = o.NodeType\n\ttoSerialize[\"src\"] = o.Src\n\ttoSerialize[\"width\"] = o.Width\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeImageAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"height\",\n\t\t\"id\",\n\t\t\"node_type\",\n\t\t\"src\",\n\t\t\"width\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeImageAttributes := _UiNodeImageAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeImageAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeImageAttributes(varUiNodeImageAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"height\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"src\")\n\t\tdelete(additionalProperties, \"width\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeImageAttributes struct {\n\tvalue *UiNodeImageAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeImageAttributes) Get() *UiNodeImageAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeImageAttributes) Set(val *UiNodeImageAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeImageAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeImageAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeImageAttributes(val *UiNodeImageAttributes) *NullableUiNodeImageAttributes {\n\treturn &NullableUiNodeImageAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeImageAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeImageAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_node_input_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeInputAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeInputAttributes{}\n\n// UiNodeInputAttributes InputAttributes represents the attributes of an input node\ntype UiNodeInputAttributes struct {\n\t// The autocomplete attribute for the input. email InputAttributeAutocompleteEmail tel InputAttributeAutocompleteTel url InputAttributeAutocompleteUrl current-password InputAttributeAutocompleteCurrentPassword new-password InputAttributeAutocompleteNewPassword one-time-code InputAttributeAutocompleteOneTimeCode username webauthn InputAttributeAutocompleteUsernameWebauthn\n\tAutocomplete *string `json:\"autocomplete,omitempty\"`\n\t// Sets the input's disabled field to true or false.\n\tDisabled bool    `json:\"disabled\"`\n\tLabel    *UiText `json:\"label,omitempty\"`\n\t// MaxLength may contain the input's maximum length.\n\tMaxlength *int64 `json:\"maxlength,omitempty\"`\n\t// The input's element name.\n\tName string `json:\"name\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"input\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType string `json:\"node_type\"`\n\t// OnClick may contain javascript which should be executed on click. This is primarily used for WebAuthn.  Deprecated: Using OnClick requires the use of eval() which is a security risk. Use OnClickTrigger instead.\n\tOnclick *string `json:\"onclick,omitempty\"`\n\t// OnClickTrigger may contain a WebAuthn trigger which should be executed on click.  The trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login. oryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration oryWebAuthnLogin WebAuthnTriggersWebAuthnLogin oryPasskeyLogin WebAuthnTriggersPasskeyLogin oryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit oryPasskeyRegistration WebAuthnTriggersPasskeyRegistration oryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\n\tOnclickTrigger *string `json:\"onclickTrigger,omitempty\"`\n\t// OnLoad may contain javascript which should be executed on load. This is primarily used for WebAuthn.  Deprecated: Using OnLoad requires the use of eval() which is a security risk. Use OnLoadTrigger instead.\n\tOnload *string `json:\"onload,omitempty\"`\n\t// OnLoadTrigger may contain a WebAuthn trigger which should be executed on load.  The trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login. oryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration oryWebAuthnLogin WebAuthnTriggersWebAuthnLogin oryPasskeyLogin WebAuthnTriggersPasskeyLogin oryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit oryPasskeyRegistration WebAuthnTriggersPasskeyRegistration oryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\n\tOnloadTrigger *string `json:\"onloadTrigger,omitempty\"`\n\t// The input's pattern.\n\tPattern *string `json:\"pattern,omitempty\"`\n\t// Mark this input field as required.\n\tRequired *bool `json:\"required,omitempty\"`\n\t// The input's element type. text InputAttributeTypeText password InputAttributeTypePassword number InputAttributeTypeNumber checkbox InputAttributeTypeCheckbox hidden InputAttributeTypeHidden email InputAttributeTypeEmail tel InputAttributeTypeTel submit InputAttributeTypeSubmit button InputAttributeTypeButton datetime-local InputAttributeTypeDateTimeLocal date InputAttributeTypeDate url InputAttributeTypeURI\n\tType string `json:\"type\"`\n\t// The input's value.\n\tValue                interface{} `json:\"value,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeInputAttributes UiNodeInputAttributes\n\n// NewUiNodeInputAttributes instantiates a new UiNodeInputAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeInputAttributes(disabled bool, name string, nodeType string, type_ string) *UiNodeInputAttributes {\n\tthis := UiNodeInputAttributes{}\n\tthis.Disabled = disabled\n\tthis.Name = name\n\tthis.NodeType = nodeType\n\tthis.Type = type_\n\treturn &this\n}\n\n// NewUiNodeInputAttributesWithDefaults instantiates a new UiNodeInputAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeInputAttributesWithDefaults() *UiNodeInputAttributes {\n\tthis := UiNodeInputAttributes{}\n\treturn &this\n}\n\n// GetAutocomplete returns the Autocomplete field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetAutocomplete() string {\n\tif o == nil || IsNil(o.Autocomplete) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Autocomplete\n}\n\n// GetAutocompleteOk returns a tuple with the Autocomplete field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetAutocompleteOk() (*string, bool) {\n\tif o == nil || IsNil(o.Autocomplete) {\n\t\treturn nil, false\n\t}\n\treturn o.Autocomplete, true\n}\n\n// HasAutocomplete returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasAutocomplete() bool {\n\tif o != nil && !IsNil(o.Autocomplete) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAutocomplete gets a reference to the given string and assigns it to the Autocomplete field.\nfunc (o *UiNodeInputAttributes) SetAutocomplete(v string) {\n\to.Autocomplete = &v\n}\n\n// GetDisabled returns the Disabled field value\nfunc (o *UiNodeInputAttributes) GetDisabled() bool {\n\tif o == nil {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\n\treturn o.Disabled\n}\n\n// GetDisabledOk returns a tuple with the Disabled field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetDisabledOk() (*bool, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Disabled, true\n}\n\n// SetDisabled sets field value\nfunc (o *UiNodeInputAttributes) SetDisabled(v bool) {\n\to.Disabled = v\n}\n\n// GetLabel returns the Label field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetLabel() UiText {\n\tif o == nil || IsNil(o.Label) {\n\t\tvar ret UiText\n\t\treturn ret\n\t}\n\treturn *o.Label\n}\n\n// GetLabelOk returns a tuple with the Label field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetLabelOk() (*UiText, bool) {\n\tif o == nil || IsNil(o.Label) {\n\t\treturn nil, false\n\t}\n\treturn o.Label, true\n}\n\n// HasLabel returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasLabel() bool {\n\tif o != nil && !IsNil(o.Label) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLabel gets a reference to the given UiText and assigns it to the Label field.\nfunc (o *UiNodeInputAttributes) SetLabel(v UiText) {\n\to.Label = &v\n}\n\n// GetMaxlength returns the Maxlength field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetMaxlength() int64 {\n\tif o == nil || IsNil(o.Maxlength) {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\treturn *o.Maxlength\n}\n\n// GetMaxlengthOk returns a tuple with the Maxlength field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetMaxlengthOk() (*int64, bool) {\n\tif o == nil || IsNil(o.Maxlength) {\n\t\treturn nil, false\n\t}\n\treturn o.Maxlength, true\n}\n\n// HasMaxlength returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasMaxlength() bool {\n\tif o != nil && !IsNil(o.Maxlength) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMaxlength gets a reference to the given int64 and assigns it to the Maxlength field.\nfunc (o *UiNodeInputAttributes) SetMaxlength(v int64) {\n\to.Maxlength = &v\n}\n\n// GetName returns the Name field value\nfunc (o *UiNodeInputAttributes) GetName() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Name\n}\n\n// GetNameOk returns a tuple with the Name field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetNameOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Name, true\n}\n\n// SetName sets field value\nfunc (o *UiNodeInputAttributes) SetName(v string) {\n\to.Name = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeInputAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeInputAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetOnclick returns the Onclick field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetOnclick() string {\n\tif o == nil || IsNil(o.Onclick) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Onclick\n}\n\n// GetOnclickOk returns a tuple with the Onclick field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetOnclickOk() (*string, bool) {\n\tif o == nil || IsNil(o.Onclick) {\n\t\treturn nil, false\n\t}\n\treturn o.Onclick, true\n}\n\n// HasOnclick returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasOnclick() bool {\n\tif o != nil && !IsNil(o.Onclick) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOnclick gets a reference to the given string and assigns it to the Onclick field.\nfunc (o *UiNodeInputAttributes) SetOnclick(v string) {\n\to.Onclick = &v\n}\n\n// GetOnclickTrigger returns the OnclickTrigger field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetOnclickTrigger() string {\n\tif o == nil || IsNil(o.OnclickTrigger) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OnclickTrigger\n}\n\n// GetOnclickTriggerOk returns a tuple with the OnclickTrigger field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetOnclickTriggerOk() (*string, bool) {\n\tif o == nil || IsNil(o.OnclickTrigger) {\n\t\treturn nil, false\n\t}\n\treturn o.OnclickTrigger, true\n}\n\n// HasOnclickTrigger returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasOnclickTrigger() bool {\n\tif o != nil && !IsNil(o.OnclickTrigger) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOnclickTrigger gets a reference to the given string and assigns it to the OnclickTrigger field.\nfunc (o *UiNodeInputAttributes) SetOnclickTrigger(v string) {\n\to.OnclickTrigger = &v\n}\n\n// GetOnload returns the Onload field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetOnload() string {\n\tif o == nil || IsNil(o.Onload) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Onload\n}\n\n// GetOnloadOk returns a tuple with the Onload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetOnloadOk() (*string, bool) {\n\tif o == nil || IsNil(o.Onload) {\n\t\treturn nil, false\n\t}\n\treturn o.Onload, true\n}\n\n// HasOnload returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasOnload() bool {\n\tif o != nil && !IsNil(o.Onload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOnload gets a reference to the given string and assigns it to the Onload field.\nfunc (o *UiNodeInputAttributes) SetOnload(v string) {\n\to.Onload = &v\n}\n\n// GetOnloadTrigger returns the OnloadTrigger field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetOnloadTrigger() string {\n\tif o == nil || IsNil(o.OnloadTrigger) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.OnloadTrigger\n}\n\n// GetOnloadTriggerOk returns a tuple with the OnloadTrigger field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetOnloadTriggerOk() (*string, bool) {\n\tif o == nil || IsNil(o.OnloadTrigger) {\n\t\treturn nil, false\n\t}\n\treturn o.OnloadTrigger, true\n}\n\n// HasOnloadTrigger returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasOnloadTrigger() bool {\n\tif o != nil && !IsNil(o.OnloadTrigger) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetOnloadTrigger gets a reference to the given string and assigns it to the OnloadTrigger field.\nfunc (o *UiNodeInputAttributes) SetOnloadTrigger(v string) {\n\to.OnloadTrigger = &v\n}\n\n// GetPattern returns the Pattern field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetPattern() string {\n\tif o == nil || IsNil(o.Pattern) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Pattern\n}\n\n// GetPatternOk returns a tuple with the Pattern field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetPatternOk() (*string, bool) {\n\tif o == nil || IsNil(o.Pattern) {\n\t\treturn nil, false\n\t}\n\treturn o.Pattern, true\n}\n\n// HasPattern returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasPattern() bool {\n\tif o != nil && !IsNil(o.Pattern) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPattern gets a reference to the given string and assigns it to the Pattern field.\nfunc (o *UiNodeInputAttributes) SetPattern(v string) {\n\to.Pattern = &v\n}\n\n// GetRequired returns the Required field value if set, zero value otherwise.\nfunc (o *UiNodeInputAttributes) GetRequired() bool {\n\tif o == nil || IsNil(o.Required) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.Required\n}\n\n// GetRequiredOk returns a tuple with the Required field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetRequiredOk() (*bool, bool) {\n\tif o == nil || IsNil(o.Required) {\n\t\treturn nil, false\n\t}\n\treturn o.Required, true\n}\n\n// HasRequired returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasRequired() bool {\n\tif o != nil && !IsNil(o.Required) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequired gets a reference to the given bool and assigns it to the Required field.\nfunc (o *UiNodeInputAttributes) SetRequired(v bool) {\n\to.Required = &v\n}\n\n// GetType returns the Type field value\nfunc (o *UiNodeInputAttributes) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeInputAttributes) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *UiNodeInputAttributes) SetType(v string) {\n\to.Type = v\n}\n\n// GetValue returns the Value field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *UiNodeInputAttributes) GetValue() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.Value\n}\n\n// GetValueOk returns a tuple with the Value field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *UiNodeInputAttributes) GetValueOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.Value) {\n\t\treturn nil, false\n\t}\n\treturn &o.Value, true\n}\n\n// HasValue returns a boolean if a field has been set.\nfunc (o *UiNodeInputAttributes) HasValue() bool {\n\tif o != nil && !IsNil(o.Value) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetValue gets a reference to the given interface{} and assigns it to the Value field.\nfunc (o *UiNodeInputAttributes) SetValue(v interface{}) {\n\to.Value = v\n}\n\nfunc (o UiNodeInputAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeInputAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Autocomplete) {\n\t\ttoSerialize[\"autocomplete\"] = o.Autocomplete\n\t}\n\ttoSerialize[\"disabled\"] = o.Disabled\n\tif !IsNil(o.Label) {\n\t\ttoSerialize[\"label\"] = o.Label\n\t}\n\tif !IsNil(o.Maxlength) {\n\t\ttoSerialize[\"maxlength\"] = o.Maxlength\n\t}\n\ttoSerialize[\"name\"] = o.Name\n\ttoSerialize[\"node_type\"] = o.NodeType\n\tif !IsNil(o.Onclick) {\n\t\ttoSerialize[\"onclick\"] = o.Onclick\n\t}\n\tif !IsNil(o.OnclickTrigger) {\n\t\ttoSerialize[\"onclickTrigger\"] = o.OnclickTrigger\n\t}\n\tif !IsNil(o.Onload) {\n\t\ttoSerialize[\"onload\"] = o.Onload\n\t}\n\tif !IsNil(o.OnloadTrigger) {\n\t\ttoSerialize[\"onloadTrigger\"] = o.OnloadTrigger\n\t}\n\tif !IsNil(o.Pattern) {\n\t\ttoSerialize[\"pattern\"] = o.Pattern\n\t}\n\tif !IsNil(o.Required) {\n\t\ttoSerialize[\"required\"] = o.Required\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\tif o.Value != nil {\n\t\ttoSerialize[\"value\"] = o.Value\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeInputAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"disabled\",\n\t\t\"name\",\n\t\t\"node_type\",\n\t\t\"type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeInputAttributes := _UiNodeInputAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeInputAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeInputAttributes(varUiNodeInputAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"autocomplete\")\n\t\tdelete(additionalProperties, \"disabled\")\n\t\tdelete(additionalProperties, \"label\")\n\t\tdelete(additionalProperties, \"maxlength\")\n\t\tdelete(additionalProperties, \"name\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"onclick\")\n\t\tdelete(additionalProperties, \"onclickTrigger\")\n\t\tdelete(additionalProperties, \"onload\")\n\t\tdelete(additionalProperties, \"onloadTrigger\")\n\t\tdelete(additionalProperties, \"pattern\")\n\t\tdelete(additionalProperties, \"required\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"value\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeInputAttributes struct {\n\tvalue *UiNodeInputAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeInputAttributes) Get() *UiNodeInputAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeInputAttributes) Set(val *UiNodeInputAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeInputAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeInputAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeInputAttributes(val *UiNodeInputAttributes) *NullableUiNodeInputAttributes {\n\treturn &NullableUiNodeInputAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeInputAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeInputAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_node_meta.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the UiNodeMeta type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeMeta{}\n\n// UiNodeMeta This might include a label and other information that can optionally be used to render UIs.\ntype UiNodeMeta struct {\n\tLabel                *UiText `json:\"label,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeMeta UiNodeMeta\n\n// NewUiNodeMeta instantiates a new UiNodeMeta object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeMeta() *UiNodeMeta {\n\tthis := UiNodeMeta{}\n\treturn &this\n}\n\n// NewUiNodeMetaWithDefaults instantiates a new UiNodeMeta object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeMetaWithDefaults() *UiNodeMeta {\n\tthis := UiNodeMeta{}\n\treturn &this\n}\n\n// GetLabel returns the Label field value if set, zero value otherwise.\nfunc (o *UiNodeMeta) GetLabel() UiText {\n\tif o == nil || IsNil(o.Label) {\n\t\tvar ret UiText\n\t\treturn ret\n\t}\n\treturn *o.Label\n}\n\n// GetLabelOk returns a tuple with the Label field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeMeta) GetLabelOk() (*UiText, bool) {\n\tif o == nil || IsNil(o.Label) {\n\t\treturn nil, false\n\t}\n\treturn o.Label, true\n}\n\n// HasLabel returns a boolean if a field has been set.\nfunc (o *UiNodeMeta) HasLabel() bool {\n\tif o != nil && !IsNil(o.Label) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLabel gets a reference to the given UiText and assigns it to the Label field.\nfunc (o *UiNodeMeta) SetLabel(v UiText) {\n\to.Label = &v\n}\n\nfunc (o UiNodeMeta) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeMeta) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Label) {\n\t\ttoSerialize[\"label\"] = o.Label\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeMeta) UnmarshalJSON(data []byte) (err error) {\n\tvarUiNodeMeta := _UiNodeMeta{}\n\n\terr = json.Unmarshal(data, &varUiNodeMeta)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeMeta(varUiNodeMeta)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"label\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeMeta struct {\n\tvalue *UiNodeMeta\n\tisSet bool\n}\n\nfunc (v NullableUiNodeMeta) Get() *UiNodeMeta {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeMeta) Set(val *UiNodeMeta) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeMeta) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeMeta) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeMeta(val *UiNodeMeta) *NullableUiNodeMeta {\n\treturn &NullableUiNodeMeta{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeMeta) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeMeta) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_node_script_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeScriptAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeScriptAttributes{}\n\n// UiNodeScriptAttributes struct for UiNodeScriptAttributes\ntype UiNodeScriptAttributes struct {\n\t// The script async type\n\tAsync bool `json:\"async\"`\n\t// The script cross origin policy\n\tCrossorigin string `json:\"crossorigin\"`\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// The script's integrity hash\n\tIntegrity string `json:\"integrity\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \\\"script\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType string `json:\"node_type\"`\n\t// Nonce for CSP  A nonce you may want to use to improve your Content Security Policy. You do not have to use this value but if you want to improve your CSP policies you may use it. You can also choose to use your own nonce value!\n\tNonce string `json:\"nonce\"`\n\t// The script referrer policy\n\tReferrerpolicy string `json:\"referrerpolicy\"`\n\t// The script source\n\tSrc string `json:\"src\"`\n\t// The script MIME type\n\tType                 string `json:\"type\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeScriptAttributes UiNodeScriptAttributes\n\n// NewUiNodeScriptAttributes instantiates a new UiNodeScriptAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeScriptAttributes(async bool, crossorigin string, id string, integrity string, nodeType string, nonce string, referrerpolicy string, src string, type_ string) *UiNodeScriptAttributes {\n\tthis := UiNodeScriptAttributes{}\n\tthis.Async = async\n\tthis.Crossorigin = crossorigin\n\tthis.Id = id\n\tthis.Integrity = integrity\n\tthis.NodeType = nodeType\n\tthis.Nonce = nonce\n\tthis.Referrerpolicy = referrerpolicy\n\tthis.Src = src\n\tthis.Type = type_\n\treturn &this\n}\n\n// NewUiNodeScriptAttributesWithDefaults instantiates a new UiNodeScriptAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeScriptAttributesWithDefaults() *UiNodeScriptAttributes {\n\tthis := UiNodeScriptAttributes{}\n\treturn &this\n}\n\n// GetAsync returns the Async field value\nfunc (o *UiNodeScriptAttributes) GetAsync() bool {\n\tif o == nil {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\n\treturn o.Async\n}\n\n// GetAsyncOk returns a tuple with the Async field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetAsyncOk() (*bool, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Async, true\n}\n\n// SetAsync sets field value\nfunc (o *UiNodeScriptAttributes) SetAsync(v bool) {\n\to.Async = v\n}\n\n// GetCrossorigin returns the Crossorigin field value\nfunc (o *UiNodeScriptAttributes) GetCrossorigin() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Crossorigin\n}\n\n// GetCrossoriginOk returns a tuple with the Crossorigin field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetCrossoriginOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Crossorigin, true\n}\n\n// SetCrossorigin sets field value\nfunc (o *UiNodeScriptAttributes) SetCrossorigin(v string) {\n\to.Crossorigin = v\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeScriptAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeScriptAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetIntegrity returns the Integrity field value\nfunc (o *UiNodeScriptAttributes) GetIntegrity() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Integrity\n}\n\n// GetIntegrityOk returns a tuple with the Integrity field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetIntegrityOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Integrity, true\n}\n\n// SetIntegrity sets field value\nfunc (o *UiNodeScriptAttributes) SetIntegrity(v string) {\n\to.Integrity = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeScriptAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeScriptAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetNonce returns the Nonce field value\nfunc (o *UiNodeScriptAttributes) GetNonce() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Nonce\n}\n\n// GetNonceOk returns a tuple with the Nonce field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetNonceOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Nonce, true\n}\n\n// SetNonce sets field value\nfunc (o *UiNodeScriptAttributes) SetNonce(v string) {\n\to.Nonce = v\n}\n\n// GetReferrerpolicy returns the Referrerpolicy field value\nfunc (o *UiNodeScriptAttributes) GetReferrerpolicy() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Referrerpolicy\n}\n\n// GetReferrerpolicyOk returns a tuple with the Referrerpolicy field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetReferrerpolicyOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Referrerpolicy, true\n}\n\n// SetReferrerpolicy sets field value\nfunc (o *UiNodeScriptAttributes) SetReferrerpolicy(v string) {\n\to.Referrerpolicy = v\n}\n\n// GetSrc returns the Src field value\nfunc (o *UiNodeScriptAttributes) GetSrc() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Src\n}\n\n// GetSrcOk returns a tuple with the Src field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetSrcOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Src, true\n}\n\n// SetSrc sets field value\nfunc (o *UiNodeScriptAttributes) SetSrc(v string) {\n\to.Src = v\n}\n\n// GetType returns the Type field value\nfunc (o *UiNodeScriptAttributes) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeScriptAttributes) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *UiNodeScriptAttributes) SetType(v string) {\n\to.Type = v\n}\n\nfunc (o UiNodeScriptAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeScriptAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"async\"] = o.Async\n\ttoSerialize[\"crossorigin\"] = o.Crossorigin\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"integrity\"] = o.Integrity\n\ttoSerialize[\"node_type\"] = o.NodeType\n\ttoSerialize[\"nonce\"] = o.Nonce\n\ttoSerialize[\"referrerpolicy\"] = o.Referrerpolicy\n\ttoSerialize[\"src\"] = o.Src\n\ttoSerialize[\"type\"] = o.Type\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeScriptAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"async\",\n\t\t\"crossorigin\",\n\t\t\"id\",\n\t\t\"integrity\",\n\t\t\"node_type\",\n\t\t\"nonce\",\n\t\t\"referrerpolicy\",\n\t\t\"src\",\n\t\t\"type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeScriptAttributes := _UiNodeScriptAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeScriptAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeScriptAttributes(varUiNodeScriptAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"async\")\n\t\tdelete(additionalProperties, \"crossorigin\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"integrity\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"nonce\")\n\t\tdelete(additionalProperties, \"referrerpolicy\")\n\t\tdelete(additionalProperties, \"src\")\n\t\tdelete(additionalProperties, \"type\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeScriptAttributes struct {\n\tvalue *UiNodeScriptAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeScriptAttributes) Get() *UiNodeScriptAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeScriptAttributes) Set(val *UiNodeScriptAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeScriptAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeScriptAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeScriptAttributes(val *UiNodeScriptAttributes) *NullableUiNodeScriptAttributes {\n\treturn &NullableUiNodeScriptAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeScriptAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeScriptAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_node_text_attributes.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiNodeTextAttributes type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiNodeTextAttributes{}\n\n// UiNodeTextAttributes struct for UiNodeTextAttributes\ntype UiNodeTextAttributes struct {\n\t// A unique identifier\n\tId string `json:\"id\"`\n\t// NodeType represents this node's types. It is a mirror of `node.type` and is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"text\\\". text Text input Input img Image a Anchor script Script div Division\n\tNodeType             string `json:\"node_type\"`\n\tText                 UiText `json:\"text\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiNodeTextAttributes UiNodeTextAttributes\n\n// NewUiNodeTextAttributes instantiates a new UiNodeTextAttributes object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiNodeTextAttributes(id string, nodeType string, text UiText) *UiNodeTextAttributes {\n\tthis := UiNodeTextAttributes{}\n\tthis.Id = id\n\tthis.NodeType = nodeType\n\tthis.Text = text\n\treturn &this\n}\n\n// NewUiNodeTextAttributesWithDefaults instantiates a new UiNodeTextAttributes object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiNodeTextAttributesWithDefaults() *UiNodeTextAttributes {\n\tthis := UiNodeTextAttributes{}\n\treturn &this\n}\n\n// GetId returns the Id field value\nfunc (o *UiNodeTextAttributes) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeTextAttributes) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiNodeTextAttributes) SetId(v string) {\n\to.Id = v\n}\n\n// GetNodeType returns the NodeType field value\nfunc (o *UiNodeTextAttributes) GetNodeType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.NodeType\n}\n\n// GetNodeTypeOk returns a tuple with the NodeType field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeTextAttributes) GetNodeTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.NodeType, true\n}\n\n// SetNodeType sets field value\nfunc (o *UiNodeTextAttributes) SetNodeType(v string) {\n\to.NodeType = v\n}\n\n// GetText returns the Text field value\nfunc (o *UiNodeTextAttributes) GetText() UiText {\n\tif o == nil {\n\t\tvar ret UiText\n\t\treturn ret\n\t}\n\n\treturn o.Text\n}\n\n// GetTextOk returns a tuple with the Text field value\n// and a boolean to check if the value has been set.\nfunc (o *UiNodeTextAttributes) GetTextOk() (*UiText, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Text, true\n}\n\n// SetText sets field value\nfunc (o *UiNodeTextAttributes) SetText(v UiText) {\n\to.Text = v\n}\n\nfunc (o UiNodeTextAttributes) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiNodeTextAttributes) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"node_type\"] = o.NodeType\n\ttoSerialize[\"text\"] = o.Text\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiNodeTextAttributes) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"node_type\",\n\t\t\"text\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiNodeTextAttributes := _UiNodeTextAttributes{}\n\n\terr = json.Unmarshal(data, &varUiNodeTextAttributes)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiNodeTextAttributes(varUiNodeTextAttributes)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"node_type\")\n\t\tdelete(additionalProperties, \"text\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiNodeTextAttributes struct {\n\tvalue *UiNodeTextAttributes\n\tisSet bool\n}\n\nfunc (v NullableUiNodeTextAttributes) Get() *UiNodeTextAttributes {\n\treturn v.value\n}\n\nfunc (v *NullableUiNodeTextAttributes) Set(val *UiNodeTextAttributes) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiNodeTextAttributes) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiNodeTextAttributes) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiNodeTextAttributes(val *UiNodeTextAttributes) *NullableUiNodeTextAttributes {\n\treturn &NullableUiNodeTextAttributes{value: val, isSet: true}\n}\n\nfunc (v NullableUiNodeTextAttributes) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiNodeTextAttributes) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_ui_text.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UiText type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UiText{}\n\n// UiText struct for UiText\ntype UiText struct {\n\t// The message's context. Useful when customizing messages.\n\tContext map[string]interface{} `json:\"context,omitempty\"`\n\tId      int64                  `json:\"id\"`\n\t// The message text. Written in american english.\n\tText string `json:\"text\"`\n\t// The message type. info Info error Error success Success\n\tType                 string `json:\"type\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UiText UiText\n\n// NewUiText instantiates a new UiText object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUiText(id int64, text string, type_ string) *UiText {\n\tthis := UiText{}\n\tthis.Id = id\n\tthis.Text = text\n\tthis.Type = type_\n\treturn &this\n}\n\n// NewUiTextWithDefaults instantiates a new UiText object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUiTextWithDefaults() *UiText {\n\tthis := UiText{}\n\treturn &this\n}\n\n// GetContext returns the Context field value if set, zero value otherwise.\nfunc (o *UiText) GetContext() map[string]interface{} {\n\tif o == nil || IsNil(o.Context) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Context\n}\n\n// GetContextOk returns a tuple with the Context field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UiText) GetContextOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Context) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Context, true\n}\n\n// HasContext returns a boolean if a field has been set.\nfunc (o *UiText) HasContext() bool {\n\tif o != nil && !IsNil(o.Context) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetContext gets a reference to the given map[string]interface{} and assigns it to the Context field.\nfunc (o *UiText) SetContext(v map[string]interface{}) {\n\to.Context = v\n}\n\n// GetId returns the Id field value\nfunc (o *UiText) GetId() int64 {\n\tif o == nil {\n\t\tvar ret int64\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *UiText) GetIdOk() (*int64, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *UiText) SetId(v int64) {\n\to.Id = v\n}\n\n// GetText returns the Text field value\nfunc (o *UiText) GetText() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Text\n}\n\n// GetTextOk returns a tuple with the Text field value\n// and a boolean to check if the value has been set.\nfunc (o *UiText) GetTextOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Text, true\n}\n\n// SetText sets field value\nfunc (o *UiText) SetText(v string) {\n\to.Text = v\n}\n\n// GetType returns the Type field value\nfunc (o *UiText) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *UiText) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *UiText) SetType(v string) {\n\to.Type = v\n}\n\nfunc (o UiText) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UiText) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Context) {\n\t\ttoSerialize[\"context\"] = o.Context\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\ttoSerialize[\"text\"] = o.Text\n\ttoSerialize[\"type\"] = o.Type\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UiText) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"text\",\n\t\t\"type\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUiText := _UiText{}\n\n\terr = json.Unmarshal(data, &varUiText)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UiText(varUiText)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"context\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"text\")\n\t\tdelete(additionalProperties, \"type\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUiText struct {\n\tvalue *UiText\n\tisSet bool\n}\n\nfunc (v NullableUiText) Get() *UiText {\n\treturn v.value\n}\n\nfunc (v *NullableUiText) Set(val *UiText) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUiText) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUiText) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUiText(val *UiText) *NullableUiText {\n\treturn &NullableUiText{value: val, isSet: true}\n}\n\nfunc (v NullableUiText) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUiText) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_fedcm_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateFedcmFlowBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateFedcmFlowBody{}\n\n// UpdateFedcmFlowBody struct for UpdateFedcmFlowBody\ntype UpdateFedcmFlowBody struct {\n\t// CSRFToken is the anti-CSRF token.\n\tCsrfToken string `json:\"csrf_token\"`\n\t// Nonce is the nonce that was used in the `navigator.credentials.get` call. If specified, it must match the `nonce` claim in the token.\n\tNonce *string `json:\"nonce,omitempty\"`\n\t// Token contains the result of `navigator.credentials.get`.\n\tToken string `json:\"token\"`\n\t// Transient data to pass along to any webhooks.\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateFedcmFlowBody UpdateFedcmFlowBody\n\n// NewUpdateFedcmFlowBody instantiates a new UpdateFedcmFlowBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateFedcmFlowBody(csrfToken string, token string) *UpdateFedcmFlowBody {\n\tthis := UpdateFedcmFlowBody{}\n\tthis.CsrfToken = csrfToken\n\tthis.Token = token\n\treturn &this\n}\n\n// NewUpdateFedcmFlowBodyWithDefaults instantiates a new UpdateFedcmFlowBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateFedcmFlowBodyWithDefaults() *UpdateFedcmFlowBody {\n\tthis := UpdateFedcmFlowBody{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value\nfunc (o *UpdateFedcmFlowBody) GetCsrfToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateFedcmFlowBody) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.CsrfToken, true\n}\n\n// SetCsrfToken sets field value\nfunc (o *UpdateFedcmFlowBody) SetCsrfToken(v string) {\n\to.CsrfToken = v\n}\n\n// GetNonce returns the Nonce field value if set, zero value otherwise.\nfunc (o *UpdateFedcmFlowBody) GetNonce() string {\n\tif o == nil || IsNil(o.Nonce) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Nonce\n}\n\n// GetNonceOk returns a tuple with the Nonce field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateFedcmFlowBody) GetNonceOk() (*string, bool) {\n\tif o == nil || IsNil(o.Nonce) {\n\t\treturn nil, false\n\t}\n\treturn o.Nonce, true\n}\n\n// HasNonce returns a boolean if a field has been set.\nfunc (o *UpdateFedcmFlowBody) HasNonce() bool {\n\tif o != nil && !IsNil(o.Nonce) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetNonce gets a reference to the given string and assigns it to the Nonce field.\nfunc (o *UpdateFedcmFlowBody) SetNonce(v string) {\n\to.Nonce = &v\n}\n\n// GetToken returns the Token field value\nfunc (o *UpdateFedcmFlowBody) GetToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Token\n}\n\n// GetTokenOk returns a tuple with the Token field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateFedcmFlowBody) GetTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Token, true\n}\n\n// SetToken sets field value\nfunc (o *UpdateFedcmFlowBody) SetToken(v string) {\n\to.Token = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateFedcmFlowBody) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateFedcmFlowBody) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateFedcmFlowBody) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateFedcmFlowBody) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateFedcmFlowBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateFedcmFlowBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\tif !IsNil(o.Nonce) {\n\t\ttoSerialize[\"nonce\"] = o.Nonce\n\t}\n\ttoSerialize[\"token\"] = o.Token\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateFedcmFlowBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"csrf_token\",\n\t\t\"token\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateFedcmFlowBody := _UpdateFedcmFlowBody{}\n\n\terr = json.Unmarshal(data, &varUpdateFedcmFlowBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateFedcmFlowBody(varUpdateFedcmFlowBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"nonce\")\n\t\tdelete(additionalProperties, \"token\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateFedcmFlowBody struct {\n\tvalue *UpdateFedcmFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateFedcmFlowBody) Get() *UpdateFedcmFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateFedcmFlowBody) Set(val *UpdateFedcmFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateFedcmFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateFedcmFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateFedcmFlowBody(val *UpdateFedcmFlowBody) *NullableUpdateFedcmFlowBody {\n\treturn &NullableUpdateFedcmFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateFedcmFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateFedcmFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_identity_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateIdentityBody type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateIdentityBody{}\n\n// UpdateIdentityBody Update Identity Body\ntype UpdateIdentityBody struct {\n\tCredentials *IdentityWithCredentials `json:\"credentials,omitempty\"`\n\t// ExternalID is an optional external ID of the identity. This is used to link the identity to an external system. If set, the external ID must be unique across all identities.\n\tExternalId *string `json:\"external_id,omitempty\"`\n\t// Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/<id>`.\n\tMetadataAdmin interface{} `json:\"metadata_admin,omitempty\"`\n\t// Store metadata about the identity which the identity itself can see when calling for example the session endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\n\tMetadataPublic interface{} `json:\"metadata_public,omitempty\"`\n\t// SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. If set will update the Identity's SchemaID.\n\tSchemaId string `json:\"schema_id\"`\n\t// State is the identity's state. active StateActive inactive StateInactive\n\tState string `json:\"state\"`\n\t// Traits represent an identity's traits. The identity is able to create, modify, and delete traits in a self-service manner. The input will always be validated against the JSON Schema defined in `schema_id`.\n\tTraits               map[string]interface{} `json:\"traits\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateIdentityBody UpdateIdentityBody\n\n// NewUpdateIdentityBody instantiates a new UpdateIdentityBody object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateIdentityBody(schemaId string, state string, traits map[string]interface{}) *UpdateIdentityBody {\n\tthis := UpdateIdentityBody{}\n\tthis.SchemaId = schemaId\n\tthis.State = state\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateIdentityBodyWithDefaults instantiates a new UpdateIdentityBody object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateIdentityBodyWithDefaults() *UpdateIdentityBody {\n\tthis := UpdateIdentityBody{}\n\treturn &this\n}\n\n// GetCredentials returns the Credentials field value if set, zero value otherwise.\nfunc (o *UpdateIdentityBody) GetCredentials() IdentityWithCredentials {\n\tif o == nil || IsNil(o.Credentials) {\n\t\tvar ret IdentityWithCredentials\n\t\treturn ret\n\t}\n\treturn *o.Credentials\n}\n\n// GetCredentialsOk returns a tuple with the Credentials field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetCredentialsOk() (*IdentityWithCredentials, bool) {\n\tif o == nil || IsNil(o.Credentials) {\n\t\treturn nil, false\n\t}\n\treturn o.Credentials, true\n}\n\n// HasCredentials returns a boolean if a field has been set.\nfunc (o *UpdateIdentityBody) HasCredentials() bool {\n\tif o != nil && !IsNil(o.Credentials) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCredentials gets a reference to the given IdentityWithCredentials and assigns it to the Credentials field.\nfunc (o *UpdateIdentityBody) SetCredentials(v IdentityWithCredentials) {\n\to.Credentials = &v\n}\n\n// GetExternalId returns the ExternalId field value if set, zero value otherwise.\nfunc (o *UpdateIdentityBody) GetExternalId() string {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ExternalId\n}\n\n// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetExternalIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.ExternalId) {\n\t\treturn nil, false\n\t}\n\treturn o.ExternalId, true\n}\n\n// HasExternalId returns a boolean if a field has been set.\nfunc (o *UpdateIdentityBody) HasExternalId() bool {\n\tif o != nil && !IsNil(o.ExternalId) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.\nfunc (o *UpdateIdentityBody) SetExternalId(v string) {\n\to.ExternalId = &v\n}\n\n// GetMetadataAdmin returns the MetadataAdmin field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *UpdateIdentityBody) GetMetadataAdmin() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataAdmin\n}\n\n// GetMetadataAdminOk returns a tuple with the MetadataAdmin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *UpdateIdentityBody) GetMetadataAdminOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataAdmin) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataAdmin, true\n}\n\n// HasMetadataAdmin returns a boolean if a field has been set.\nfunc (o *UpdateIdentityBody) HasMetadataAdmin() bool {\n\tif o != nil && !IsNil(o.MetadataAdmin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataAdmin gets a reference to the given interface{} and assigns it to the MetadataAdmin field.\nfunc (o *UpdateIdentityBody) SetMetadataAdmin(v interface{}) {\n\to.MetadataAdmin = v\n}\n\n// GetMetadataPublic returns the MetadataPublic field value if set, zero value otherwise (both if not set or set to explicit null).\nfunc (o *UpdateIdentityBody) GetMetadataPublic() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\treturn o.MetadataPublic\n}\n\n// GetMetadataPublicOk returns a tuple with the MetadataPublic field value if set, nil otherwise\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *UpdateIdentityBody) GetMetadataPublicOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.MetadataPublic) {\n\t\treturn nil, false\n\t}\n\treturn &o.MetadataPublic, true\n}\n\n// HasMetadataPublic returns a boolean if a field has been set.\nfunc (o *UpdateIdentityBody) HasMetadataPublic() bool {\n\tif o != nil && !IsNil(o.MetadataPublic) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetMetadataPublic gets a reference to the given interface{} and assigns it to the MetadataPublic field.\nfunc (o *UpdateIdentityBody) SetMetadataPublic(v interface{}) {\n\to.MetadataPublic = v\n}\n\n// GetSchemaId returns the SchemaId field value\nfunc (o *UpdateIdentityBody) GetSchemaId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.SchemaId\n}\n\n// GetSchemaIdOk returns a tuple with the SchemaId field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetSchemaIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.SchemaId, true\n}\n\n// SetSchemaId sets field value\nfunc (o *UpdateIdentityBody) SetSchemaId(v string) {\n\to.SchemaId = v\n}\n\n// GetState returns the State field value\nfunc (o *UpdateIdentityBody) GetState() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetStateOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *UpdateIdentityBody) SetState(v string) {\n\to.State = v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateIdentityBody) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateIdentityBody) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateIdentityBody) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\nfunc (o UpdateIdentityBody) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateIdentityBody) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Credentials) {\n\t\ttoSerialize[\"credentials\"] = o.Credentials\n\t}\n\tif !IsNil(o.ExternalId) {\n\t\ttoSerialize[\"external_id\"] = o.ExternalId\n\t}\n\tif o.MetadataAdmin != nil {\n\t\ttoSerialize[\"metadata_admin\"] = o.MetadataAdmin\n\t}\n\tif o.MetadataPublic != nil {\n\t\ttoSerialize[\"metadata_public\"] = o.MetadataPublic\n\t}\n\ttoSerialize[\"schema_id\"] = o.SchemaId\n\ttoSerialize[\"state\"] = o.State\n\ttoSerialize[\"traits\"] = o.Traits\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateIdentityBody) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"schema_id\",\n\t\t\"state\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateIdentityBody := _UpdateIdentityBody{}\n\n\terr = json.Unmarshal(data, &varUpdateIdentityBody)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateIdentityBody(varUpdateIdentityBody)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"credentials\")\n\t\tdelete(additionalProperties, \"external_id\")\n\t\tdelete(additionalProperties, \"metadata_admin\")\n\t\tdelete(additionalProperties, \"metadata_public\")\n\t\tdelete(additionalProperties, \"schema_id\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateIdentityBody struct {\n\tvalue *UpdateIdentityBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateIdentityBody) Get() *UpdateIdentityBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateIdentityBody) Set(val *UpdateIdentityBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateIdentityBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateIdentityBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateIdentityBody(val *UpdateIdentityBody) *NullableUpdateIdentityBody {\n\treturn &NullableUpdateIdentityBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateIdentityBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateIdentityBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateLoginFlowBody - struct for UpdateLoginFlowBody\ntype UpdateLoginFlowBody struct {\n\tUpdateLoginFlowWithCodeMethod            *UpdateLoginFlowWithCodeMethod\n\tUpdateLoginFlowWithIdentifierFirstMethod *UpdateLoginFlowWithIdentifierFirstMethod\n\tUpdateLoginFlowWithLookupSecretMethod    *UpdateLoginFlowWithLookupSecretMethod\n\tUpdateLoginFlowWithOidcMethod            *UpdateLoginFlowWithOidcMethod\n\tUpdateLoginFlowWithPasskeyMethod         *UpdateLoginFlowWithPasskeyMethod\n\tUpdateLoginFlowWithPasswordMethod        *UpdateLoginFlowWithPasswordMethod\n\tUpdateLoginFlowWithSamlMethod            *UpdateLoginFlowWithSamlMethod\n\tUpdateLoginFlowWithTotpMethod            *UpdateLoginFlowWithTotpMethod\n\tUpdateLoginFlowWithWebAuthnMethod        *UpdateLoginFlowWithWebAuthnMethod\n}\n\n// UpdateLoginFlowWithCodeMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithCodeMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithCodeMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithCodeMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithCodeMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithIdentifierFirstMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithIdentifierFirstMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithIdentifierFirstMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithIdentifierFirstMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithIdentifierFirstMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithLookupSecretMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithLookupSecretMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithLookupSecretMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithLookupSecretMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithLookupSecretMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithOidcMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithOidcMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithOidcMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithOidcMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithOidcMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithPasskeyMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithPasskeyMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithPasskeyMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithPasskeyMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithPasskeyMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithPasswordMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithPasswordMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithPasswordMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithPasswordMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithPasswordMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithSamlMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithSamlMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithSamlMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithSamlMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithSamlMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithTotpMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithTotpMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithTotpMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithTotpMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithTotpMethod: v,\n\t}\n}\n\n// UpdateLoginFlowWithWebAuthnMethodAsUpdateLoginFlowBody is a convenience function that returns UpdateLoginFlowWithWebAuthnMethod wrapped in UpdateLoginFlowBody\nfunc UpdateLoginFlowWithWebAuthnMethodAsUpdateLoginFlowBody(v *UpdateLoginFlowWithWebAuthnMethod) UpdateLoginFlowBody {\n\treturn UpdateLoginFlowBody{\n\t\tUpdateLoginFlowWithWebAuthnMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateLoginFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'code'\n\tif jsonDict[\"method\"] == \"code\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'identifier_first'\n\tif jsonDict[\"method\"] == \"identifier_first\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithIdentifierFirstMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithIdentifierFirstMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithIdentifierFirstMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithIdentifierFirstMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithIdentifierFirstMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'lookup_secret'\n\tif jsonDict[\"method\"] == \"lookup_secret\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithLookupSecretMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithLookupSecretMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithLookupSecretMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithLookupSecretMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithLookupSecretMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'oidc'\n\tif jsonDict[\"method\"] == \"oidc\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'passkey'\n\tif jsonDict[\"method\"] == \"passkey\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'password'\n\tif jsonDict[\"method\"] == \"password\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'saml'\n\tif jsonDict[\"method\"] == \"saml\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'totp'\n\tif jsonDict[\"method\"] == \"totp\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithTotpMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithTotpMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithTotpMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithTotpMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithTotpMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'webauthn'\n\tif jsonDict[\"method\"] == \"webauthn\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithCodeMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithCodeMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithIdentifierFirstMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithIdentifierFirstMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithIdentifierFirstMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithIdentifierFirstMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithIdentifierFirstMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithIdentifierFirstMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithIdentifierFirstMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithLookupSecretMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithLookupSecretMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithLookupSecretMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithLookupSecretMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithLookupSecretMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithLookupSecretMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithLookupSecretMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithOidcMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithOidcMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithPasskeyMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithPasskeyMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithPasswordMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithPasswordMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithSamlMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithSamlMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithTotpMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithTotpMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithTotpMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithTotpMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithTotpMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithTotpMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithTotpMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateLoginFlowWithWebAuthnMethod'\n\tif jsonDict[\"method\"] == \"updateLoginFlowWithWebAuthnMethod\" {\n\t\t// try to unmarshal JSON data into UpdateLoginFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateLoginFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateLoginFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateLoginFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateLoginFlowBody as UpdateLoginFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateLoginFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateLoginFlowWithCodeMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithCodeMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithIdentifierFirstMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithIdentifierFirstMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithLookupSecretMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithLookupSecretMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithOidcMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithOidcMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithPasskeyMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithPasskeyMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithPasswordMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithPasswordMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithSamlMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithSamlMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithTotpMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithTotpMethod)\n\t}\n\n\tif src.UpdateLoginFlowWithWebAuthnMethod != nil {\n\t\treturn json.Marshal(&src.UpdateLoginFlowWithWebAuthnMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateLoginFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateLoginFlowWithCodeMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithIdentifierFirstMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithIdentifierFirstMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithLookupSecretMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithLookupSecretMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithOidcMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithPasskeyMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithPasswordMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithSamlMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithTotpMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithTotpMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithWebAuthnMethod != nil {\n\t\treturn obj.UpdateLoginFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateLoginFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateLoginFlowWithCodeMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithIdentifierFirstMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithIdentifierFirstMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithLookupSecretMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithLookupSecretMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithOidcMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithPasskeyMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithPasswordMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithSamlMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithTotpMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithTotpMethod\n\t}\n\n\tif obj.UpdateLoginFlowWithWebAuthnMethod != nil {\n\t\treturn *obj.UpdateLoginFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateLoginFlowBody struct {\n\tvalue *UpdateLoginFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowBody) Get() *UpdateLoginFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowBody) Set(val *UpdateLoginFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowBody(val *UpdateLoginFlowBody) *NullableUpdateLoginFlowBody {\n\treturn &NullableUpdateLoginFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_with_code_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithCodeMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithCodeMethod{}\n\n// UpdateLoginFlowWithCodeMethod Update Login flow using the code method\ntype UpdateLoginFlowWithCodeMethod struct {\n\t// Address is the address to send the code to, in case that there are multiple addresses. This field is only used in two-factor flows and is ineffective for passwordless flows.\n\tAddress *string `json:\"address,omitempty\"`\n\t// Code is the 6 digits code sent to the user\n\tCode *string `json:\"code,omitempty\"`\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken string `json:\"csrf_token\"`\n\t// Identifier is the code identifier The identifier requires that the user has already completed the registration or settings with code flow.\n\tIdentifier *string `json:\"identifier,omitempty\"`\n\t// Method should be set to \\\"code\\\" when logging in using the code strategy.\n\tMethod string `json:\"method\"`\n\t// Resend is set when the user wants to resend the code\n\tResend *string `json:\"resend,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithCodeMethod UpdateLoginFlowWithCodeMethod\n\n// NewUpdateLoginFlowWithCodeMethod instantiates a new UpdateLoginFlowWithCodeMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithCodeMethod(csrfToken string, method string) *UpdateLoginFlowWithCodeMethod {\n\tthis := UpdateLoginFlowWithCodeMethod{}\n\tthis.CsrfToken = csrfToken\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithCodeMethodWithDefaults instantiates a new UpdateLoginFlowWithCodeMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithCodeMethodWithDefaults() *UpdateLoginFlowWithCodeMethod {\n\tthis := UpdateLoginFlowWithCodeMethod{}\n\treturn &this\n}\n\n// GetAddress returns the Address field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetAddress() string {\n\tif o == nil || IsNil(o.Address) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Address\n}\n\n// GetAddressOk returns a tuple with the Address field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.Address) {\n\t\treturn nil, false\n\t}\n\treturn o.Address, true\n}\n\n// HasAddress returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasAddress() bool {\n\tif o != nil && !IsNil(o.Address) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetAddress gets a reference to the given string and assigns it to the Address field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetAddress(v string) {\n\to.Address = &v\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetCode() string {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given string and assigns it to the Code field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetCode(v string) {\n\to.Code = &v\n}\n\n// GetCsrfToken returns the CsrfToken field value\nfunc (o *UpdateLoginFlowWithCodeMethod) GetCsrfToken() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.CsrfToken, true\n}\n\n// SetCsrfToken sets field value\nfunc (o *UpdateLoginFlowWithCodeMethod) SetCsrfToken(v string) {\n\to.CsrfToken = v\n}\n\n// GetIdentifier returns the Identifier field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetIdentifier() string {\n\tif o == nil || IsNil(o.Identifier) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Identifier\n}\n\n// GetIdentifierOk returns a tuple with the Identifier field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetIdentifierOk() (*string, bool) {\n\tif o == nil || IsNil(o.Identifier) {\n\t\treturn nil, false\n\t}\n\treturn o.Identifier, true\n}\n\n// HasIdentifier returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasIdentifier() bool {\n\tif o != nil && !IsNil(o.Identifier) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdentifier gets a reference to the given string and assigns it to the Identifier field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetIdentifier(v string) {\n\to.Identifier = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithCodeMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithCodeMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetResend returns the Resend field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetResend() string {\n\tif o == nil || IsNil(o.Resend) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Resend\n}\n\n// GetResendOk returns a tuple with the Resend field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetResendOk() (*string, bool) {\n\tif o == nil || IsNil(o.Resend) {\n\t\treturn nil, false\n\t}\n\treturn o.Resend, true\n}\n\n// HasResend returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasResend() bool {\n\tif o != nil && !IsNil(o.Resend) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetResend gets a reference to the given string and assigns it to the Resend field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetResend(v string) {\n\to.Resend = &v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithCodeMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithCodeMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithCodeMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Address) {\n\t\ttoSerialize[\"address\"] = o.Address\n\t}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\tif !IsNil(o.Identifier) {\n\t\ttoSerialize[\"identifier\"] = o.Identifier\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Resend) {\n\t\ttoSerialize[\"resend\"] = o.Resend\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithCodeMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"csrf_token\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithCodeMethod := _UpdateLoginFlowWithCodeMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithCodeMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithCodeMethod(varUpdateLoginFlowWithCodeMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"address\")\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"identifier\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"resend\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithCodeMethod struct {\n\tvalue *UpdateLoginFlowWithCodeMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithCodeMethod) Get() *UpdateLoginFlowWithCodeMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithCodeMethod) Set(val *UpdateLoginFlowWithCodeMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithCodeMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithCodeMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithCodeMethod(val *UpdateLoginFlowWithCodeMethod) *NullableUpdateLoginFlowWithCodeMethod {\n\treturn &NullableUpdateLoginFlowWithCodeMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithCodeMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_with_identifier_first_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithIdentifierFirstMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithIdentifierFirstMethod{}\n\n// UpdateLoginFlowWithIdentifierFirstMethod Update Login Flow with Multi-Step Method\ntype UpdateLoginFlowWithIdentifierFirstMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Identifier is the email or username of the user trying to log in.\n\tIdentifier string `json:\"identifier\"`\n\t// Method should be set to \\\"password\\\" when logging in using the identifier and password strategy.\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithIdentifierFirstMethod UpdateLoginFlowWithIdentifierFirstMethod\n\n// NewUpdateLoginFlowWithIdentifierFirstMethod instantiates a new UpdateLoginFlowWithIdentifierFirstMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithIdentifierFirstMethod(identifier string, method string) *UpdateLoginFlowWithIdentifierFirstMethod {\n\tthis := UpdateLoginFlowWithIdentifierFirstMethod{}\n\tthis.Identifier = identifier\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithIdentifierFirstMethodWithDefaults instantiates a new UpdateLoginFlowWithIdentifierFirstMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithIdentifierFirstMethodWithDefaults() *UpdateLoginFlowWithIdentifierFirstMethod {\n\tthis := UpdateLoginFlowWithIdentifierFirstMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdentifier returns the Identifier field value\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetIdentifier() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Identifier\n}\n\n// GetIdentifierOk returns a tuple with the Identifier field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetIdentifierOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identifier, true\n}\n\n// SetIdentifier sets field value\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) SetIdentifier(v string) {\n\to.Identifier = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithIdentifierFirstMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithIdentifierFirstMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"identifier\"] = o.Identifier\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithIdentifierFirstMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identifier\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithIdentifierFirstMethod := _UpdateLoginFlowWithIdentifierFirstMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithIdentifierFirstMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithIdentifierFirstMethod(varUpdateLoginFlowWithIdentifierFirstMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"identifier\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithIdentifierFirstMethod struct {\n\tvalue *UpdateLoginFlowWithIdentifierFirstMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithIdentifierFirstMethod) Get() *UpdateLoginFlowWithIdentifierFirstMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithIdentifierFirstMethod) Set(val *UpdateLoginFlowWithIdentifierFirstMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithIdentifierFirstMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithIdentifierFirstMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithIdentifierFirstMethod(val *UpdateLoginFlowWithIdentifierFirstMethod) *NullableUpdateLoginFlowWithIdentifierFirstMethod {\n\treturn &NullableUpdateLoginFlowWithIdentifierFirstMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithIdentifierFirstMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithIdentifierFirstMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_with_lookup_secret_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithLookupSecretMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithLookupSecretMethod{}\n\n// UpdateLoginFlowWithLookupSecretMethod Update Login Flow with Lookup Secret Method\ntype UpdateLoginFlowWithLookupSecretMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// The lookup secret.\n\tLookupSecret string `json:\"lookup_secret\"`\n\t// Method should be set to \\\"lookup_secret\\\" when logging in using the lookup_secret strategy.\n\tMethod               string `json:\"method\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithLookupSecretMethod UpdateLoginFlowWithLookupSecretMethod\n\n// NewUpdateLoginFlowWithLookupSecretMethod instantiates a new UpdateLoginFlowWithLookupSecretMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithLookupSecretMethod(lookupSecret string, method string) *UpdateLoginFlowWithLookupSecretMethod {\n\tthis := UpdateLoginFlowWithLookupSecretMethod{}\n\tthis.LookupSecret = lookupSecret\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithLookupSecretMethodWithDefaults instantiates a new UpdateLoginFlowWithLookupSecretMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithLookupSecretMethodWithDefaults() *UpdateLoginFlowWithLookupSecretMethod {\n\tthis := UpdateLoginFlowWithLookupSecretMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetLookupSecret returns the LookupSecret field value\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetLookupSecret() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.LookupSecret\n}\n\n// GetLookupSecretOk returns a tuple with the LookupSecret field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetLookupSecretOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.LookupSecret, true\n}\n\n// SetLookupSecret sets field value\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) SetLookupSecret(v string) {\n\to.LookupSecret = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) SetMethod(v string) {\n\to.Method = v\n}\n\nfunc (o UpdateLoginFlowWithLookupSecretMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithLookupSecretMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"lookup_secret\"] = o.LookupSecret\n\ttoSerialize[\"method\"] = o.Method\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithLookupSecretMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"lookup_secret\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithLookupSecretMethod := _UpdateLoginFlowWithLookupSecretMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithLookupSecretMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithLookupSecretMethod(varUpdateLoginFlowWithLookupSecretMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"lookup_secret\")\n\t\tdelete(additionalProperties, \"method\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithLookupSecretMethod struct {\n\tvalue *UpdateLoginFlowWithLookupSecretMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithLookupSecretMethod) Get() *UpdateLoginFlowWithLookupSecretMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithLookupSecretMethod) Set(val *UpdateLoginFlowWithLookupSecretMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithLookupSecretMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithLookupSecretMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithLookupSecretMethod(val *UpdateLoginFlowWithLookupSecretMethod) *NullableUpdateLoginFlowWithLookupSecretMethod {\n\treturn &NullableUpdateLoginFlowWithLookupSecretMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithLookupSecretMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithLookupSecretMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_with_oidc_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithOidcMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithOidcMethod{}\n\n// UpdateLoginFlowWithOidcMethod Update Login Flow with OpenID Connect Method\ntype UpdateLoginFlowWithOidcMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// IDToken is an optional id token provided by an OIDC provider  If submitted, it is verified using the OIDC provider's public key set and the claims are used to populate the OIDC credentials of the identity. If the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use the `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.  Supported providers are Apple Google\n\tIdToken *string `json:\"id_token,omitempty\"`\n\t// IDTokenNonce is the nonce, used when generating the IDToken. If the provider supports nonce validation, the nonce will be validated against this value and required.\n\tIdTokenNonce *string `json:\"id_token_nonce,omitempty\"`\n\t// Method to use  This field must be set to `oidc` when using the oidc method.\n\tMethod string `json:\"method\"`\n\t// The provider to register with\n\tProvider string `json:\"provider\"`\n\t// The identity traits. This is a placeholder for the registration flow.\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// UpstreamParameters are the parameters that are passed to the upstream identity provider.  These parameters are optional and depend on what the upstream identity provider supports. Supported parameters are: `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session. `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`. `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`. `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\n\tUpstreamParameters   map[string]interface{} `json:\"upstream_parameters,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithOidcMethod UpdateLoginFlowWithOidcMethod\n\n// NewUpdateLoginFlowWithOidcMethod instantiates a new UpdateLoginFlowWithOidcMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithOidcMethod(method string, provider string) *UpdateLoginFlowWithOidcMethod {\n\tthis := UpdateLoginFlowWithOidcMethod{}\n\tthis.Method = method\n\tthis.Provider = provider\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithOidcMethodWithDefaults instantiates a new UpdateLoginFlowWithOidcMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithOidcMethodWithDefaults() *UpdateLoginFlowWithOidcMethod {\n\tthis := UpdateLoginFlowWithOidcMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdToken returns the IdToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetIdToken() string {\n\tif o == nil || IsNil(o.IdToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdToken\n}\n\n// GetIdTokenOk returns a tuple with the IdToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetIdTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdToken) {\n\t\treturn nil, false\n\t}\n\treturn o.IdToken, true\n}\n\n// HasIdToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasIdToken() bool {\n\tif o != nil && !IsNil(o.IdToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdToken gets a reference to the given string and assigns it to the IdToken field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetIdToken(v string) {\n\to.IdToken = &v\n}\n\n// GetIdTokenNonce returns the IdTokenNonce field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetIdTokenNonce() string {\n\tif o == nil || IsNil(o.IdTokenNonce) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdTokenNonce\n}\n\n// GetIdTokenNonceOk returns a tuple with the IdTokenNonce field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetIdTokenNonceOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdTokenNonce) {\n\t\treturn nil, false\n\t}\n\treturn o.IdTokenNonce, true\n}\n\n// HasIdTokenNonce returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasIdTokenNonce() bool {\n\tif o != nil && !IsNil(o.IdTokenNonce) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdTokenNonce gets a reference to the given string and assigns it to the IdTokenNonce field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetIdTokenNonce(v string) {\n\to.IdTokenNonce = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithOidcMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithOidcMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetProvider returns the Provider field value\nfunc (o *UpdateLoginFlowWithOidcMethod) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *UpdateLoginFlowWithOidcMethod) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetUpstreamParameters returns the UpstreamParameters field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetUpstreamParameters() map[string]interface{} {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.UpstreamParameters\n}\n\n// GetUpstreamParametersOk returns a tuple with the UpstreamParameters field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) GetUpstreamParametersOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.UpstreamParameters, true\n}\n\n// HasUpstreamParameters returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithOidcMethod) HasUpstreamParameters() bool {\n\tif o != nil && !IsNil(o.UpstreamParameters) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpstreamParameters gets a reference to the given map[string]interface{} and assigns it to the UpstreamParameters field.\nfunc (o *UpdateLoginFlowWithOidcMethod) SetUpstreamParameters(v map[string]interface{}) {\n\to.UpstreamParameters = v\n}\n\nfunc (o UpdateLoginFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithOidcMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.IdToken) {\n\t\ttoSerialize[\"id_token\"] = o.IdToken\n\t}\n\tif !IsNil(o.IdTokenNonce) {\n\t\ttoSerialize[\"id_token_nonce\"] = o.IdTokenNonce\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"provider\"] = o.Provider\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.UpstreamParameters) {\n\t\ttoSerialize[\"upstream_parameters\"] = o.UpstreamParameters\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithOidcMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"provider\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithOidcMethod := _UpdateLoginFlowWithOidcMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithOidcMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithOidcMethod(varUpdateLoginFlowWithOidcMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"id_token\")\n\t\tdelete(additionalProperties, \"id_token_nonce\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"upstream_parameters\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithOidcMethod struct {\n\tvalue *UpdateLoginFlowWithOidcMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithOidcMethod) Get() *UpdateLoginFlowWithOidcMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithOidcMethod) Set(val *UpdateLoginFlowWithOidcMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithOidcMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithOidcMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithOidcMethod(val *UpdateLoginFlowWithOidcMethod) *NullableUpdateLoginFlowWithOidcMethod {\n\treturn &NullableUpdateLoginFlowWithOidcMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithOidcMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_with_passkey_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithPasskeyMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithPasskeyMethod{}\n\n// UpdateLoginFlowWithPasskeyMethod Update Login Flow with Passkey Method\ntype UpdateLoginFlowWithPasskeyMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method should be set to \\\"passkey\\\" when logging in using the Passkey strategy.\n\tMethod string `json:\"method\"`\n\t// Login a WebAuthn Security Key  This must contain the ID of the WebAuthN connection.\n\tPasskeyLogin         *string `json:\"passkey_login,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithPasskeyMethod UpdateLoginFlowWithPasskeyMethod\n\n// NewUpdateLoginFlowWithPasskeyMethod instantiates a new UpdateLoginFlowWithPasskeyMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithPasskeyMethod(method string) *UpdateLoginFlowWithPasskeyMethod {\n\tthis := UpdateLoginFlowWithPasskeyMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithPasskeyMethodWithDefaults instantiates a new UpdateLoginFlowWithPasskeyMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithPasskeyMethodWithDefaults() *UpdateLoginFlowWithPasskeyMethod {\n\tthis := UpdateLoginFlowWithPasskeyMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithPasskeyMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPasskeyLogin returns the PasskeyLogin field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetPasskeyLogin() string {\n\tif o == nil || IsNil(o.PasskeyLogin) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasskeyLogin\n}\n\n// GetPasskeyLoginOk returns a tuple with the PasskeyLogin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) GetPasskeyLoginOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasskeyLogin) {\n\t\treturn nil, false\n\t}\n\treturn o.PasskeyLogin, true\n}\n\n// HasPasskeyLogin returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) HasPasskeyLogin() bool {\n\tif o != nil && !IsNil(o.PasskeyLogin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasskeyLogin gets a reference to the given string and assigns it to the PasskeyLogin field.\nfunc (o *UpdateLoginFlowWithPasskeyMethod) SetPasskeyLogin(v string) {\n\to.PasskeyLogin = &v\n}\n\nfunc (o UpdateLoginFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithPasskeyMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.PasskeyLogin) {\n\t\ttoSerialize[\"passkey_login\"] = o.PasskeyLogin\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithPasskeyMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithPasskeyMethod := _UpdateLoginFlowWithPasskeyMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithPasskeyMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithPasskeyMethod(varUpdateLoginFlowWithPasskeyMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"passkey_login\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithPasskeyMethod struct {\n\tvalue *UpdateLoginFlowWithPasskeyMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithPasskeyMethod) Get() *UpdateLoginFlowWithPasskeyMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasskeyMethod) Set(val *UpdateLoginFlowWithPasskeyMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithPasskeyMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasskeyMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithPasskeyMethod(val *UpdateLoginFlowWithPasskeyMethod) *NullableUpdateLoginFlowWithPasskeyMethod {\n\treturn &NullableUpdateLoginFlowWithPasskeyMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasskeyMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_with_password_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithPasswordMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithPasswordMethod{}\n\n// UpdateLoginFlowWithPasswordMethod Update Login Flow with Password Method\ntype UpdateLoginFlowWithPasswordMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Identifier is the email or username of the user trying to log in.\n\tIdentifier string `json:\"identifier\"`\n\t// Method should be set to \\\"password\\\" when logging in using the identifier and password strategy.\n\tMethod string `json:\"method\"`\n\t// The user's password.\n\tPassword string `json:\"password\"`\n\t// Identifier is the email or username of the user trying to log in. This field is deprecated!\n\tPasswordIdentifier *string `json:\"password_identifier,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithPasswordMethod UpdateLoginFlowWithPasswordMethod\n\n// NewUpdateLoginFlowWithPasswordMethod instantiates a new UpdateLoginFlowWithPasswordMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithPasswordMethod(identifier string, method string, password string) *UpdateLoginFlowWithPasswordMethod {\n\tthis := UpdateLoginFlowWithPasswordMethod{}\n\tthis.Identifier = identifier\n\tthis.Method = method\n\tthis.Password = password\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithPasswordMethodWithDefaults instantiates a new UpdateLoginFlowWithPasswordMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithPasswordMethodWithDefaults() *UpdateLoginFlowWithPasswordMethod {\n\tthis := UpdateLoginFlowWithPasswordMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdentifier returns the Identifier field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetIdentifier() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Identifier\n}\n\n// GetIdentifierOk returns a tuple with the Identifier field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetIdentifierOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identifier, true\n}\n\n// SetIdentifier sets field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetIdentifier(v string) {\n\to.Identifier = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPassword returns the Password field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetPassword() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetPasswordOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Password, true\n}\n\n// SetPassword sets field value\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetPassword(v string) {\n\to.Password = v\n}\n\n// GetPasswordIdentifier returns the PasswordIdentifier field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetPasswordIdentifier() string {\n\tif o == nil || IsNil(o.PasswordIdentifier) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasswordIdentifier\n}\n\n// GetPasswordIdentifierOk returns a tuple with the PasswordIdentifier field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetPasswordIdentifierOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasswordIdentifier) {\n\t\treturn nil, false\n\t}\n\treturn o.PasswordIdentifier, true\n}\n\n// HasPasswordIdentifier returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) HasPasswordIdentifier() bool {\n\tif o != nil && !IsNil(o.PasswordIdentifier) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasswordIdentifier gets a reference to the given string and assigns it to the PasswordIdentifier field.\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetPasswordIdentifier(v string) {\n\to.PasswordIdentifier = &v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithPasswordMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithPasswordMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithPasswordMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"identifier\"] = o.Identifier\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"password\"] = o.Password\n\tif !IsNil(o.PasswordIdentifier) {\n\t\ttoSerialize[\"password_identifier\"] = o.PasswordIdentifier\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithPasswordMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identifier\",\n\t\t\"method\",\n\t\t\"password\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithPasswordMethod := _UpdateLoginFlowWithPasswordMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithPasswordMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithPasswordMethod(varUpdateLoginFlowWithPasswordMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"identifier\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"password_identifier\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithPasswordMethod struct {\n\tvalue *UpdateLoginFlowWithPasswordMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithPasswordMethod) Get() *UpdateLoginFlowWithPasswordMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasswordMethod) Set(val *UpdateLoginFlowWithPasswordMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithPasswordMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasswordMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithPasswordMethod(val *UpdateLoginFlowWithPasswordMethod) *NullableUpdateLoginFlowWithPasswordMethod {\n\treturn &NullableUpdateLoginFlowWithPasswordMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithPasswordMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_with_saml_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithSamlMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithSamlMethod{}\n\n// UpdateLoginFlowWithSamlMethod Update login flow using SAML\ntype UpdateLoginFlowWithSamlMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method to use  This field must be set to `saml` when using the saml method.\n\tMethod string `json:\"method\"`\n\t// The provider to register with\n\tProvider string `json:\"provider\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithSamlMethod UpdateLoginFlowWithSamlMethod\n\n// NewUpdateLoginFlowWithSamlMethod instantiates a new UpdateLoginFlowWithSamlMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithSamlMethod(method string, provider string) *UpdateLoginFlowWithSamlMethod {\n\tthis := UpdateLoginFlowWithSamlMethod{}\n\tthis.Method = method\n\tthis.Provider = provider\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithSamlMethodWithDefaults instantiates a new UpdateLoginFlowWithSamlMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithSamlMethodWithDefaults() *UpdateLoginFlowWithSamlMethod {\n\tthis := UpdateLoginFlowWithSamlMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithSamlMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithSamlMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithSamlMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetProvider returns the Provider field value\nfunc (o *UpdateLoginFlowWithSamlMethod) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *UpdateLoginFlowWithSamlMethod) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithSamlMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithSamlMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithSamlMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"provider\"] = o.Provider\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithSamlMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"provider\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithSamlMethod := _UpdateLoginFlowWithSamlMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithSamlMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithSamlMethod(varUpdateLoginFlowWithSamlMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithSamlMethod struct {\n\tvalue *UpdateLoginFlowWithSamlMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithSamlMethod) Get() *UpdateLoginFlowWithSamlMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithSamlMethod) Set(val *UpdateLoginFlowWithSamlMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithSamlMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithSamlMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithSamlMethod(val *UpdateLoginFlowWithSamlMethod) *NullableUpdateLoginFlowWithSamlMethod {\n\treturn &NullableUpdateLoginFlowWithSamlMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithSamlMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_with_totp_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithTotpMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithTotpMethod{}\n\n// UpdateLoginFlowWithTotpMethod Update Login Flow with TOTP Method\ntype UpdateLoginFlowWithTotpMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method should be set to \\\"totp\\\" when logging in using the TOTP strategy.\n\tMethod string `json:\"method\"`\n\t// The TOTP code.\n\tTotpCode string `json:\"totp_code\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithTotpMethod UpdateLoginFlowWithTotpMethod\n\n// NewUpdateLoginFlowWithTotpMethod instantiates a new UpdateLoginFlowWithTotpMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithTotpMethod(method string, totpCode string) *UpdateLoginFlowWithTotpMethod {\n\tthis := UpdateLoginFlowWithTotpMethod{}\n\tthis.Method = method\n\tthis.TotpCode = totpCode\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithTotpMethodWithDefaults instantiates a new UpdateLoginFlowWithTotpMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithTotpMethodWithDefaults() *UpdateLoginFlowWithTotpMethod {\n\tthis := UpdateLoginFlowWithTotpMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithTotpMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithTotpMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithTotpMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTotpCode returns the TotpCode field value\nfunc (o *UpdateLoginFlowWithTotpMethod) GetTotpCode() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.TotpCode\n}\n\n// GetTotpCodeOk returns a tuple with the TotpCode field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetTotpCodeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.TotpCode, true\n}\n\n// SetTotpCode sets field value\nfunc (o *UpdateLoginFlowWithTotpMethod) SetTotpCode(v string) {\n\to.TotpCode = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithTotpMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithTotpMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateLoginFlowWithTotpMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithTotpMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"totp_code\"] = o.TotpCode\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithTotpMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"totp_code\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithTotpMethod := _UpdateLoginFlowWithTotpMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithTotpMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithTotpMethod(varUpdateLoginFlowWithTotpMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"totp_code\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithTotpMethod struct {\n\tvalue *UpdateLoginFlowWithTotpMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithTotpMethod) Get() *UpdateLoginFlowWithTotpMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithTotpMethod) Set(val *UpdateLoginFlowWithTotpMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithTotpMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithTotpMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithTotpMethod(val *UpdateLoginFlowWithTotpMethod) *NullableUpdateLoginFlowWithTotpMethod {\n\treturn &NullableUpdateLoginFlowWithTotpMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithTotpMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithTotpMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_login_flow_with_web_authn_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateLoginFlowWithWebAuthnMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateLoginFlowWithWebAuthnMethod{}\n\n// UpdateLoginFlowWithWebAuthnMethod Update Login Flow with WebAuthn Method\ntype UpdateLoginFlowWithWebAuthnMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Identifier is the email or username of the user trying to log in.\n\tIdentifier string `json:\"identifier\"`\n\t// Method should be set to \\\"webAuthn\\\" when logging in using the WebAuthn strategy.\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Login a WebAuthn Security Key  This must contain the ID of the WebAuthN connection.\n\tWebauthnLogin        *string `json:\"webauthn_login,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateLoginFlowWithWebAuthnMethod UpdateLoginFlowWithWebAuthnMethod\n\n// NewUpdateLoginFlowWithWebAuthnMethod instantiates a new UpdateLoginFlowWithWebAuthnMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateLoginFlowWithWebAuthnMethod(identifier string, method string) *UpdateLoginFlowWithWebAuthnMethod {\n\tthis := UpdateLoginFlowWithWebAuthnMethod{}\n\tthis.Identifier = identifier\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateLoginFlowWithWebAuthnMethodWithDefaults instantiates a new UpdateLoginFlowWithWebAuthnMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateLoginFlowWithWebAuthnMethodWithDefaults() *UpdateLoginFlowWithWebAuthnMethod {\n\tthis := UpdateLoginFlowWithWebAuthnMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdentifier returns the Identifier field value\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetIdentifier() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Identifier\n}\n\n// GetIdentifierOk returns a tuple with the Identifier field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetIdentifierOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Identifier, true\n}\n\n// SetIdentifier sets field value\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetIdentifier(v string) {\n\to.Identifier = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetWebauthnLogin returns the WebauthnLogin field value if set, zero value otherwise.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetWebauthnLogin() string {\n\tif o == nil || IsNil(o.WebauthnLogin) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnLogin\n}\n\n// GetWebauthnLoginOk returns a tuple with the WebauthnLogin field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) GetWebauthnLoginOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnLogin) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnLogin, true\n}\n\n// HasWebauthnLogin returns a boolean if a field has been set.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) HasWebauthnLogin() bool {\n\tif o != nil && !IsNil(o.WebauthnLogin) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnLogin gets a reference to the given string and assigns it to the WebauthnLogin field.\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) SetWebauthnLogin(v string) {\n\to.WebauthnLogin = &v\n}\n\nfunc (o UpdateLoginFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateLoginFlowWithWebAuthnMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"identifier\"] = o.Identifier\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.WebauthnLogin) {\n\t\ttoSerialize[\"webauthn_login\"] = o.WebauthnLogin\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateLoginFlowWithWebAuthnMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"identifier\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateLoginFlowWithWebAuthnMethod := _UpdateLoginFlowWithWebAuthnMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateLoginFlowWithWebAuthnMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateLoginFlowWithWebAuthnMethod(varUpdateLoginFlowWithWebAuthnMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"identifier\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"webauthn_login\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateLoginFlowWithWebAuthnMethod struct {\n\tvalue *UpdateLoginFlowWithWebAuthnMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateLoginFlowWithWebAuthnMethod) Get() *UpdateLoginFlowWithWebAuthnMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateLoginFlowWithWebAuthnMethod) Set(val *UpdateLoginFlowWithWebAuthnMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateLoginFlowWithWebAuthnMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateLoginFlowWithWebAuthnMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateLoginFlowWithWebAuthnMethod(val *UpdateLoginFlowWithWebAuthnMethod) *NullableUpdateLoginFlowWithWebAuthnMethod {\n\treturn &NullableUpdateLoginFlowWithWebAuthnMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateLoginFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateLoginFlowWithWebAuthnMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_recovery_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateRecoveryFlowBody - Update Recovery Flow Request Body\ntype UpdateRecoveryFlowBody struct {\n\tUpdateRecoveryFlowWithCodeMethod *UpdateRecoveryFlowWithCodeMethod\n\tUpdateRecoveryFlowWithLinkMethod *UpdateRecoveryFlowWithLinkMethod\n}\n\n// UpdateRecoveryFlowWithCodeMethodAsUpdateRecoveryFlowBody is a convenience function that returns UpdateRecoveryFlowWithCodeMethod wrapped in UpdateRecoveryFlowBody\nfunc UpdateRecoveryFlowWithCodeMethodAsUpdateRecoveryFlowBody(v *UpdateRecoveryFlowWithCodeMethod) UpdateRecoveryFlowBody {\n\treturn UpdateRecoveryFlowBody{\n\t\tUpdateRecoveryFlowWithCodeMethod: v,\n\t}\n}\n\n// UpdateRecoveryFlowWithLinkMethodAsUpdateRecoveryFlowBody is a convenience function that returns UpdateRecoveryFlowWithLinkMethod wrapped in UpdateRecoveryFlowBody\nfunc UpdateRecoveryFlowWithLinkMethodAsUpdateRecoveryFlowBody(v *UpdateRecoveryFlowWithLinkMethod) UpdateRecoveryFlowBody {\n\treturn UpdateRecoveryFlowBody{\n\t\tUpdateRecoveryFlowWithLinkMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateRecoveryFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'code'\n\tif jsonDict[\"method\"] == \"code\" {\n\t\t// try to unmarshal JSON data into UpdateRecoveryFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRecoveryFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRecoveryFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRecoveryFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRecoveryFlowBody as UpdateRecoveryFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'link'\n\tif jsonDict[\"method\"] == \"link\" {\n\t\t// try to unmarshal JSON data into UpdateRecoveryFlowWithLinkMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRecoveryFlowWithLinkMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRecoveryFlowWithLinkMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRecoveryFlowWithLinkMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRecoveryFlowBody as UpdateRecoveryFlowWithLinkMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRecoveryFlowWithCodeMethod'\n\tif jsonDict[\"method\"] == \"updateRecoveryFlowWithCodeMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRecoveryFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRecoveryFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRecoveryFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRecoveryFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRecoveryFlowBody as UpdateRecoveryFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRecoveryFlowWithLinkMethod'\n\tif jsonDict[\"method\"] == \"updateRecoveryFlowWithLinkMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRecoveryFlowWithLinkMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRecoveryFlowWithLinkMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRecoveryFlowWithLinkMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRecoveryFlowWithLinkMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRecoveryFlowBody as UpdateRecoveryFlowWithLinkMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateRecoveryFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateRecoveryFlowWithCodeMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRecoveryFlowWithCodeMethod)\n\t}\n\n\tif src.UpdateRecoveryFlowWithLinkMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRecoveryFlowWithLinkMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateRecoveryFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateRecoveryFlowWithCodeMethod != nil {\n\t\treturn obj.UpdateRecoveryFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateRecoveryFlowWithLinkMethod != nil {\n\t\treturn obj.UpdateRecoveryFlowWithLinkMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateRecoveryFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateRecoveryFlowWithCodeMethod != nil {\n\t\treturn *obj.UpdateRecoveryFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateRecoveryFlowWithLinkMethod != nil {\n\t\treturn *obj.UpdateRecoveryFlowWithLinkMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateRecoveryFlowBody struct {\n\tvalue *UpdateRecoveryFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateRecoveryFlowBody) Get() *UpdateRecoveryFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRecoveryFlowBody) Set(val *UpdateRecoveryFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRecoveryFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRecoveryFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRecoveryFlowBody(val *UpdateRecoveryFlowBody) *NullableUpdateRecoveryFlowBody {\n\treturn &NullableUpdateRecoveryFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRecoveryFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRecoveryFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_recovery_flow_with_code_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRecoveryFlowWithCodeMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRecoveryFlowWithCodeMethod{}\n\n// UpdateRecoveryFlowWithCodeMethod Update Recovery Flow with Code Method\ntype UpdateRecoveryFlowWithCodeMethod struct {\n\t// Code from the recovery email  If you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\n\tCode *string `json:\"code,omitempty\"`\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// The email address of the account to recover  If the email belongs to a valid account, a recovery email will be sent.  If you want to notify the email address if the account does not exist, see the [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/account-recovery-password-reset#attempted-recovery-notifications)  If a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.  format: email\n\tEmail *string `json:\"email,omitempty\"`\n\t// Method is the method that should be used for this recovery flow  Allowed values are `link` and `code`. link RecoveryStrategyLink code RecoveryStrategyCode\n\tMethod string `json:\"method\"`\n\t// A recovery address that is registered for the user. It can be an email, a phone number (to receive the code via SMS), etc. Used in RecoveryV2.\n\tRecoveryAddress *string `json:\"recovery_address,omitempty\"`\n\t// If there are multiple recovery addresses registered for the user, and the initially provided address is different from the address chosen when the choice (of masked addresses) is presented, then we need to make sure that the user actually knows the full address to avoid information exfiltration, so we ask for the full address. Used in RecoveryV2.\n\tRecoveryConfirmAddress *string `json:\"recovery_confirm_address,omitempty\"`\n\t// If there are multiple addresses registered for the user, a choice is presented and this field stores the result of this choice. Addresses are 'masked' (never sent in full to the client and shown partially in the UI) since at this point in the recovery flow, the user has not yet proven that it knows the full address and we want to avoid information exfiltration. So for all intents and purposes, the value of this field should be treated as an opaque identifier. Used in RecoveryV2.\n\tRecoverySelectAddress *string `json:\"recovery_select_address,omitempty\"`\n\t// Set to \\\"previous\\\" to go back in the flow, meaningfully. Used in RecoveryV2.\n\tScreen *string `json:\"screen,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRecoveryFlowWithCodeMethod UpdateRecoveryFlowWithCodeMethod\n\n// NewUpdateRecoveryFlowWithCodeMethod instantiates a new UpdateRecoveryFlowWithCodeMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRecoveryFlowWithCodeMethod(method string) *UpdateRecoveryFlowWithCodeMethod {\n\tthis := UpdateRecoveryFlowWithCodeMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateRecoveryFlowWithCodeMethodWithDefaults instantiates a new UpdateRecoveryFlowWithCodeMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRecoveryFlowWithCodeMethodWithDefaults() *UpdateRecoveryFlowWithCodeMethod {\n\tthis := UpdateRecoveryFlowWithCodeMethod{}\n\treturn &this\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetCode() string {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given string and assigns it to the Code field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetCode(v string) {\n\to.Code = &v\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetEmail returns the Email field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetEmail() string {\n\tif o == nil || IsNil(o.Email) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Email\n}\n\n// GetEmailOk returns a tuple with the Email field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetEmailOk() (*string, bool) {\n\tif o == nil || IsNil(o.Email) {\n\t\treturn nil, false\n\t}\n\treturn o.Email, true\n}\n\n// HasEmail returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasEmail() bool {\n\tif o != nil && !IsNil(o.Email) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetEmail gets a reference to the given string and assigns it to the Email field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetEmail(v string) {\n\to.Email = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetRecoveryAddress returns the RecoveryAddress field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoveryAddress() string {\n\tif o == nil || IsNil(o.RecoveryAddress) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RecoveryAddress\n}\n\n// GetRecoveryAddressOk returns a tuple with the RecoveryAddress field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoveryAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.RecoveryAddress) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoveryAddress, true\n}\n\n// HasRecoveryAddress returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasRecoveryAddress() bool {\n\tif o != nil && !IsNil(o.RecoveryAddress) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoveryAddress gets a reference to the given string and assigns it to the RecoveryAddress field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetRecoveryAddress(v string) {\n\to.RecoveryAddress = &v\n}\n\n// GetRecoveryConfirmAddress returns the RecoveryConfirmAddress field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoveryConfirmAddress() string {\n\tif o == nil || IsNil(o.RecoveryConfirmAddress) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RecoveryConfirmAddress\n}\n\n// GetRecoveryConfirmAddressOk returns a tuple with the RecoveryConfirmAddress field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoveryConfirmAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.RecoveryConfirmAddress) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoveryConfirmAddress, true\n}\n\n// HasRecoveryConfirmAddress returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasRecoveryConfirmAddress() bool {\n\tif o != nil && !IsNil(o.RecoveryConfirmAddress) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoveryConfirmAddress gets a reference to the given string and assigns it to the RecoveryConfirmAddress field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetRecoveryConfirmAddress(v string) {\n\to.RecoveryConfirmAddress = &v\n}\n\n// GetRecoverySelectAddress returns the RecoverySelectAddress field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoverySelectAddress() string {\n\tif o == nil || IsNil(o.RecoverySelectAddress) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RecoverySelectAddress\n}\n\n// GetRecoverySelectAddressOk returns a tuple with the RecoverySelectAddress field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetRecoverySelectAddressOk() (*string, bool) {\n\tif o == nil || IsNil(o.RecoverySelectAddress) {\n\t\treturn nil, false\n\t}\n\treturn o.RecoverySelectAddress, true\n}\n\n// HasRecoverySelectAddress returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasRecoverySelectAddress() bool {\n\tif o != nil && !IsNil(o.RecoverySelectAddress) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRecoverySelectAddress gets a reference to the given string and assigns it to the RecoverySelectAddress field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetRecoverySelectAddress(v string) {\n\to.RecoverySelectAddress = &v\n}\n\n// GetScreen returns the Screen field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetScreen() string {\n\tif o == nil || IsNil(o.Screen) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Screen\n}\n\n// GetScreenOk returns a tuple with the Screen field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetScreenOk() (*string, bool) {\n\tif o == nil || IsNil(o.Screen) {\n\t\treturn nil, false\n\t}\n\treturn o.Screen, true\n}\n\n// HasScreen returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasScreen() bool {\n\tif o != nil && !IsNil(o.Screen) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetScreen gets a reference to the given string and assigns it to the Screen field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetScreen(v string) {\n\to.Screen = &v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRecoveryFlowWithCodeMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRecoveryFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRecoveryFlowWithCodeMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.Email) {\n\t\ttoSerialize[\"email\"] = o.Email\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.RecoveryAddress) {\n\t\ttoSerialize[\"recovery_address\"] = o.RecoveryAddress\n\t}\n\tif !IsNil(o.RecoveryConfirmAddress) {\n\t\ttoSerialize[\"recovery_confirm_address\"] = o.RecoveryConfirmAddress\n\t}\n\tif !IsNil(o.RecoverySelectAddress) {\n\t\ttoSerialize[\"recovery_select_address\"] = o.RecoverySelectAddress\n\t}\n\tif !IsNil(o.Screen) {\n\t\ttoSerialize[\"screen\"] = o.Screen\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRecoveryFlowWithCodeMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRecoveryFlowWithCodeMethod := _UpdateRecoveryFlowWithCodeMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRecoveryFlowWithCodeMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRecoveryFlowWithCodeMethod(varUpdateRecoveryFlowWithCodeMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"email\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"recovery_address\")\n\t\tdelete(additionalProperties, \"recovery_confirm_address\")\n\t\tdelete(additionalProperties, \"recovery_select_address\")\n\t\tdelete(additionalProperties, \"screen\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRecoveryFlowWithCodeMethod struct {\n\tvalue *UpdateRecoveryFlowWithCodeMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRecoveryFlowWithCodeMethod) Get() *UpdateRecoveryFlowWithCodeMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithCodeMethod) Set(val *UpdateRecoveryFlowWithCodeMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRecoveryFlowWithCodeMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithCodeMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRecoveryFlowWithCodeMethod(val *UpdateRecoveryFlowWithCodeMethod) *NullableUpdateRecoveryFlowWithCodeMethod {\n\treturn &NullableUpdateRecoveryFlowWithCodeMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRecoveryFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithCodeMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_recovery_flow_with_link_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRecoveryFlowWithLinkMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRecoveryFlowWithLinkMethod{}\n\n// UpdateRecoveryFlowWithLinkMethod Update Recovery Flow with Link Method\ntype UpdateRecoveryFlowWithLinkMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Email to Recover  Needs to be set when initiating the flow. If the email is a registered recovery email, a recovery link will be sent. If the email is not known, an email with details on what happened will be sent instead.  format: email\n\tEmail string `json:\"email\"`\n\t// Method is the method that should be used for this recovery flow  Allowed values are `link` and `code` link RecoveryStrategyLink code RecoveryStrategyCode\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRecoveryFlowWithLinkMethod UpdateRecoveryFlowWithLinkMethod\n\n// NewUpdateRecoveryFlowWithLinkMethod instantiates a new UpdateRecoveryFlowWithLinkMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRecoveryFlowWithLinkMethod(email string, method string) *UpdateRecoveryFlowWithLinkMethod {\n\tthis := UpdateRecoveryFlowWithLinkMethod{}\n\tthis.Email = email\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateRecoveryFlowWithLinkMethodWithDefaults instantiates a new UpdateRecoveryFlowWithLinkMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRecoveryFlowWithLinkMethodWithDefaults() *UpdateRecoveryFlowWithLinkMethod {\n\tthis := UpdateRecoveryFlowWithLinkMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetEmail returns the Email field value\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetEmail() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Email\n}\n\n// GetEmailOk returns a tuple with the Email field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetEmailOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Email, true\n}\n\n// SetEmail sets field value\nfunc (o *UpdateRecoveryFlowWithLinkMethod) SetEmail(v string) {\n\to.Email = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRecoveryFlowWithLinkMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRecoveryFlowWithLinkMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRecoveryFlowWithLinkMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRecoveryFlowWithLinkMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"email\"] = o.Email\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRecoveryFlowWithLinkMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"email\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRecoveryFlowWithLinkMethod := _UpdateRecoveryFlowWithLinkMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRecoveryFlowWithLinkMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRecoveryFlowWithLinkMethod(varUpdateRecoveryFlowWithLinkMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"email\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRecoveryFlowWithLinkMethod struct {\n\tvalue *UpdateRecoveryFlowWithLinkMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRecoveryFlowWithLinkMethod) Get() *UpdateRecoveryFlowWithLinkMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithLinkMethod) Set(val *UpdateRecoveryFlowWithLinkMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRecoveryFlowWithLinkMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithLinkMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRecoveryFlowWithLinkMethod(val *UpdateRecoveryFlowWithLinkMethod) *NullableUpdateRecoveryFlowWithLinkMethod {\n\treturn &NullableUpdateRecoveryFlowWithLinkMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRecoveryFlowWithLinkMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRecoveryFlowWithLinkMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_registration_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateRegistrationFlowBody - Update Registration Request Body\ntype UpdateRegistrationFlowBody struct {\n\tUpdateRegistrationFlowWithCodeMethod     *UpdateRegistrationFlowWithCodeMethod\n\tUpdateRegistrationFlowWithOidcMethod     *UpdateRegistrationFlowWithOidcMethod\n\tUpdateRegistrationFlowWithPasskeyMethod  *UpdateRegistrationFlowWithPasskeyMethod\n\tUpdateRegistrationFlowWithPasswordMethod *UpdateRegistrationFlowWithPasswordMethod\n\tUpdateRegistrationFlowWithProfileMethod  *UpdateRegistrationFlowWithProfileMethod\n\tUpdateRegistrationFlowWithSamlMethod     *UpdateRegistrationFlowWithSamlMethod\n\tUpdateRegistrationFlowWithWebAuthnMethod *UpdateRegistrationFlowWithWebAuthnMethod\n}\n\n// UpdateRegistrationFlowWithCodeMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithCodeMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithCodeMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithCodeMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithCodeMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithOidcMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithOidcMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithOidcMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithOidcMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithOidcMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithPasskeyMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithPasskeyMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithPasskeyMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithPasskeyMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithPasskeyMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithPasswordMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithPasswordMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithPasswordMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithPasswordMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithPasswordMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithProfileMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithProfileMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithProfileMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithProfileMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithProfileMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithSamlMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithSamlMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithSamlMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithSamlMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithSamlMethod: v,\n\t}\n}\n\n// UpdateRegistrationFlowWithWebAuthnMethodAsUpdateRegistrationFlowBody is a convenience function that returns UpdateRegistrationFlowWithWebAuthnMethod wrapped in UpdateRegistrationFlowBody\nfunc UpdateRegistrationFlowWithWebAuthnMethodAsUpdateRegistrationFlowBody(v *UpdateRegistrationFlowWithWebAuthnMethod) UpdateRegistrationFlowBody {\n\treturn UpdateRegistrationFlowBody{\n\t\tUpdateRegistrationFlowWithWebAuthnMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateRegistrationFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'code'\n\tif jsonDict[\"method\"] == \"code\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'oidc'\n\tif jsonDict[\"method\"] == \"oidc\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'passkey'\n\tif jsonDict[\"method\"] == \"passkey\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'password'\n\tif jsonDict[\"method\"] == \"password\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'profile'\n\tif jsonDict[\"method\"] == \"profile\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithProfileMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithProfileMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithProfileMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithProfileMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithProfileMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'saml'\n\tif jsonDict[\"method\"] == \"saml\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'webauthn'\n\tif jsonDict[\"method\"] == \"webauthn\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithCodeMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithCodeMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithOidcMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithOidcMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithPasskeyMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithPasskeyMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithPasswordMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithPasswordMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithProfileMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithProfileMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithProfileMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithProfileMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithProfileMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithProfileMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithProfileMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithSamlMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithSamlMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateRegistrationFlowWithWebAuthnMethod'\n\tif jsonDict[\"method\"] == \"updateRegistrationFlowWithWebAuthnMethod\" {\n\t\t// try to unmarshal JSON data into UpdateRegistrationFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateRegistrationFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateRegistrationFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateRegistrationFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateRegistrationFlowBody as UpdateRegistrationFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateRegistrationFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateRegistrationFlowWithCodeMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithCodeMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithOidcMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithOidcMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithPasskeyMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithPasskeyMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithPasswordMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithPasswordMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithProfileMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithProfileMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithSamlMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithSamlMethod)\n\t}\n\n\tif src.UpdateRegistrationFlowWithWebAuthnMethod != nil {\n\t\treturn json.Marshal(&src.UpdateRegistrationFlowWithWebAuthnMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateRegistrationFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateRegistrationFlowWithCodeMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithOidcMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithPasskeyMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithPasswordMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithProfileMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithProfileMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithSamlMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithWebAuthnMethod != nil {\n\t\treturn obj.UpdateRegistrationFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateRegistrationFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateRegistrationFlowWithCodeMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithOidcMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithPasskeyMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithPasswordMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithProfileMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithProfileMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithSamlMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateRegistrationFlowWithWebAuthnMethod != nil {\n\t\treturn *obj.UpdateRegistrationFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateRegistrationFlowBody struct {\n\tvalue *UpdateRegistrationFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowBody) Get() *UpdateRegistrationFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowBody) Set(val *UpdateRegistrationFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowBody(val *UpdateRegistrationFlowBody) *NullableUpdateRegistrationFlowBody {\n\treturn &NullableUpdateRegistrationFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_registration_flow_with_code_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithCodeMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithCodeMethod{}\n\n// UpdateRegistrationFlowWithCodeMethod Update Registration Flow with Code Method\ntype UpdateRegistrationFlowWithCodeMethod struct {\n\t// The OTP Code sent to the user\n\tCode *string `json:\"code,omitempty\"`\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method to use  This field must be set to `code` when using the code method.\n\tMethod string `json:\"method\"`\n\t// Resend restarts the flow with a new code\n\tResend *string `json:\"resend,omitempty\"`\n\t// The identity's traits\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithCodeMethod UpdateRegistrationFlowWithCodeMethod\n\n// NewUpdateRegistrationFlowWithCodeMethod instantiates a new UpdateRegistrationFlowWithCodeMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithCodeMethod(method string, traits map[string]interface{}) *UpdateRegistrationFlowWithCodeMethod {\n\tthis := UpdateRegistrationFlowWithCodeMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithCodeMethodWithDefaults instantiates a new UpdateRegistrationFlowWithCodeMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithCodeMethodWithDefaults() *UpdateRegistrationFlowWithCodeMethod {\n\tthis := UpdateRegistrationFlowWithCodeMethod{}\n\treturn &this\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetCode() string {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given string and assigns it to the Code field.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetCode(v string) {\n\to.Code = &v\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetResend returns the Resend field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetResend() string {\n\tif o == nil || IsNil(o.Resend) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Resend\n}\n\n// GetResendOk returns a tuple with the Resend field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetResendOk() (*string, bool) {\n\tif o == nil || IsNil(o.Resend) {\n\t\treturn nil, false\n\t}\n\treturn o.Resend, true\n}\n\n// HasResend returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) HasResend() bool {\n\tif o != nil && !IsNil(o.Resend) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetResend gets a reference to the given string and assigns it to the Resend field.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetResend(v string) {\n\to.Resend = &v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithCodeMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithCodeMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Resend) {\n\t\ttoSerialize[\"resend\"] = o.Resend\n\t}\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithCodeMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithCodeMethod := _UpdateRegistrationFlowWithCodeMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithCodeMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithCodeMethod(varUpdateRegistrationFlowWithCodeMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"resend\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithCodeMethod struct {\n\tvalue *UpdateRegistrationFlowWithCodeMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithCodeMethod) Get() *UpdateRegistrationFlowWithCodeMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithCodeMethod) Set(val *UpdateRegistrationFlowWithCodeMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithCodeMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithCodeMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithCodeMethod(val *UpdateRegistrationFlowWithCodeMethod) *NullableUpdateRegistrationFlowWithCodeMethod {\n\treturn &NullableUpdateRegistrationFlowWithCodeMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithCodeMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_registration_flow_with_oidc_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithOidcMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithOidcMethod{}\n\n// UpdateRegistrationFlowWithOidcMethod Update Registration Flow with OpenID Connect Method\ntype UpdateRegistrationFlowWithOidcMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// IDToken is an optional id token provided by an OIDC provider  If submitted, it is verified using the OIDC provider's public key set and the claims are used to populate the OIDC credentials of the identity. If the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use the `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.  Supported providers are Apple Google\n\tIdToken *string `json:\"id_token,omitempty\"`\n\t// IDTokenNonce is the nonce, used when generating the IDToken. If the provider supports nonce validation, the nonce will be validated against this value and is required.\n\tIdTokenNonce *string `json:\"id_token_nonce,omitempty\"`\n\t// Method to use  This field must be set to `oidc` when using the oidc method.\n\tMethod string `json:\"method\"`\n\t// The provider to register with\n\tProvider string `json:\"provider\"`\n\t// The identity traits\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// UpstreamParameters are the parameters that are passed to the upstream identity provider.  These parameters are optional and depend on what the upstream identity provider supports. Supported parameters are: `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session. `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`. `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`. `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\n\tUpstreamParameters   map[string]interface{} `json:\"upstream_parameters,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithOidcMethod UpdateRegistrationFlowWithOidcMethod\n\n// NewUpdateRegistrationFlowWithOidcMethod instantiates a new UpdateRegistrationFlowWithOidcMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithOidcMethod(method string, provider string) *UpdateRegistrationFlowWithOidcMethod {\n\tthis := UpdateRegistrationFlowWithOidcMethod{}\n\tthis.Method = method\n\tthis.Provider = provider\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithOidcMethodWithDefaults instantiates a new UpdateRegistrationFlowWithOidcMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithOidcMethodWithDefaults() *UpdateRegistrationFlowWithOidcMethod {\n\tthis := UpdateRegistrationFlowWithOidcMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetIdToken returns the IdToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetIdToken() string {\n\tif o == nil || IsNil(o.IdToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdToken\n}\n\n// GetIdTokenOk returns a tuple with the IdToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetIdTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdToken) {\n\t\treturn nil, false\n\t}\n\treturn o.IdToken, true\n}\n\n// HasIdToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasIdToken() bool {\n\tif o != nil && !IsNil(o.IdToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdToken gets a reference to the given string and assigns it to the IdToken field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetIdToken(v string) {\n\to.IdToken = &v\n}\n\n// GetIdTokenNonce returns the IdTokenNonce field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetIdTokenNonce() string {\n\tif o == nil || IsNil(o.IdTokenNonce) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.IdTokenNonce\n}\n\n// GetIdTokenNonceOk returns a tuple with the IdTokenNonce field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetIdTokenNonceOk() (*string, bool) {\n\tif o == nil || IsNil(o.IdTokenNonce) {\n\t\treturn nil, false\n\t}\n\treturn o.IdTokenNonce, true\n}\n\n// HasIdTokenNonce returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasIdTokenNonce() bool {\n\tif o != nil && !IsNil(o.IdTokenNonce) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIdTokenNonce gets a reference to the given string and assigns it to the IdTokenNonce field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetIdTokenNonce(v string) {\n\to.IdTokenNonce = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetProvider returns the Provider field value\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetUpstreamParameters returns the UpstreamParameters field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetUpstreamParameters() map[string]interface{} {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.UpstreamParameters\n}\n\n// GetUpstreamParametersOk returns a tuple with the UpstreamParameters field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) GetUpstreamParametersOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.UpstreamParameters, true\n}\n\n// HasUpstreamParameters returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) HasUpstreamParameters() bool {\n\tif o != nil && !IsNil(o.UpstreamParameters) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpstreamParameters gets a reference to the given map[string]interface{} and assigns it to the UpstreamParameters field.\nfunc (o *UpdateRegistrationFlowWithOidcMethod) SetUpstreamParameters(v map[string]interface{}) {\n\to.UpstreamParameters = v\n}\n\nfunc (o UpdateRegistrationFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithOidcMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.IdToken) {\n\t\ttoSerialize[\"id_token\"] = o.IdToken\n\t}\n\tif !IsNil(o.IdTokenNonce) {\n\t\ttoSerialize[\"id_token_nonce\"] = o.IdTokenNonce\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"provider\"] = o.Provider\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.UpstreamParameters) {\n\t\ttoSerialize[\"upstream_parameters\"] = o.UpstreamParameters\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithOidcMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"provider\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithOidcMethod := _UpdateRegistrationFlowWithOidcMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithOidcMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithOidcMethod(varUpdateRegistrationFlowWithOidcMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"id_token\")\n\t\tdelete(additionalProperties, \"id_token_nonce\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"upstream_parameters\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithOidcMethod struct {\n\tvalue *UpdateRegistrationFlowWithOidcMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithOidcMethod) Get() *UpdateRegistrationFlowWithOidcMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithOidcMethod) Set(val *UpdateRegistrationFlowWithOidcMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithOidcMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithOidcMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithOidcMethod(val *UpdateRegistrationFlowWithOidcMethod) *NullableUpdateRegistrationFlowWithOidcMethod {\n\treturn &NullableUpdateRegistrationFlowWithOidcMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithOidcMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_registration_flow_with_passkey_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithPasskeyMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithPasskeyMethod{}\n\n// UpdateRegistrationFlowWithPasskeyMethod Update Registration Flow with Passkey Method\ntype UpdateRegistrationFlowWithPasskeyMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"passkey\\\" when trying to add, update, or remove a Passkey.\n\tMethod string `json:\"method\"`\n\t// Register a WebAuthn Security Key  It is expected that the JSON returned by the WebAuthn registration process is included here.\n\tPasskeyRegister *string `json:\"passkey_register,omitempty\"`\n\t// The identity's traits\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithPasskeyMethod UpdateRegistrationFlowWithPasskeyMethod\n\n// NewUpdateRegistrationFlowWithPasskeyMethod instantiates a new UpdateRegistrationFlowWithPasskeyMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithPasskeyMethod(method string, traits map[string]interface{}) *UpdateRegistrationFlowWithPasskeyMethod {\n\tthis := UpdateRegistrationFlowWithPasskeyMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithPasskeyMethodWithDefaults instantiates a new UpdateRegistrationFlowWithPasskeyMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithPasskeyMethodWithDefaults() *UpdateRegistrationFlowWithPasskeyMethod {\n\tthis := UpdateRegistrationFlowWithPasskeyMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPasskeyRegister returns the PasskeyRegister field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetPasskeyRegister() string {\n\tif o == nil || IsNil(o.PasskeyRegister) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasskeyRegister\n}\n\n// GetPasskeyRegisterOk returns a tuple with the PasskeyRegister field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetPasskeyRegisterOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasskeyRegister) {\n\t\treturn nil, false\n\t}\n\treturn o.PasskeyRegister, true\n}\n\n// HasPasskeyRegister returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) HasPasskeyRegister() bool {\n\tif o != nil && !IsNil(o.PasskeyRegister) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasskeyRegister gets a reference to the given string and assigns it to the PasskeyRegister field.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetPasskeyRegister(v string) {\n\to.PasskeyRegister = &v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithPasskeyMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.PasskeyRegister) {\n\t\ttoSerialize[\"passkey_register\"] = o.PasskeyRegister\n\t}\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithPasskeyMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithPasskeyMethod := _UpdateRegistrationFlowWithPasskeyMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithPasskeyMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithPasskeyMethod(varUpdateRegistrationFlowWithPasskeyMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"passkey_register\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithPasskeyMethod struct {\n\tvalue *UpdateRegistrationFlowWithPasskeyMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasskeyMethod) Get() *UpdateRegistrationFlowWithPasskeyMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasskeyMethod) Set(val *UpdateRegistrationFlowWithPasskeyMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasskeyMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasskeyMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithPasskeyMethod(val *UpdateRegistrationFlowWithPasskeyMethod) *NullableUpdateRegistrationFlowWithPasskeyMethod {\n\treturn &NullableUpdateRegistrationFlowWithPasskeyMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasskeyMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_registration_flow_with_password_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithPasswordMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithPasswordMethod{}\n\n// UpdateRegistrationFlowWithPasswordMethod Update Registration Flow with Password Method\ntype UpdateRegistrationFlowWithPasswordMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method to use  This field must be set to `password` when using the password method.\n\tMethod string `json:\"method\"`\n\t// Password to sign the user up with\n\tPassword string `json:\"password\"`\n\t// The identity's traits\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithPasswordMethod UpdateRegistrationFlowWithPasswordMethod\n\n// NewUpdateRegistrationFlowWithPasswordMethod instantiates a new UpdateRegistrationFlowWithPasswordMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithPasswordMethod(method string, password string, traits map[string]interface{}) *UpdateRegistrationFlowWithPasswordMethod {\n\tthis := UpdateRegistrationFlowWithPasswordMethod{}\n\tthis.Method = method\n\tthis.Password = password\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithPasswordMethodWithDefaults instantiates a new UpdateRegistrationFlowWithPasswordMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithPasswordMethodWithDefaults() *UpdateRegistrationFlowWithPasswordMethod {\n\tthis := UpdateRegistrationFlowWithPasswordMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPassword returns the Password field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetPassword() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetPasswordOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Password, true\n}\n\n// SetPassword sets field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetPassword(v string) {\n\to.Password = v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithPasswordMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"password\"] = o.Password\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithPasswordMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"password\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithPasswordMethod := _UpdateRegistrationFlowWithPasswordMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithPasswordMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithPasswordMethod(varUpdateRegistrationFlowWithPasswordMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithPasswordMethod struct {\n\tvalue *UpdateRegistrationFlowWithPasswordMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasswordMethod) Get() *UpdateRegistrationFlowWithPasswordMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasswordMethod) Set(val *UpdateRegistrationFlowWithPasswordMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasswordMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasswordMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithPasswordMethod(val *UpdateRegistrationFlowWithPasswordMethod) *NullableUpdateRegistrationFlowWithPasswordMethod {\n\treturn &NullableUpdateRegistrationFlowWithPasswordMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithPasswordMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_registration_flow_with_profile_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithProfileMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithProfileMethod{}\n\n// UpdateRegistrationFlowWithProfileMethod Update Registration Flow with Profile Method\ntype UpdateRegistrationFlowWithProfileMethod struct {\n\t// The Anti-CSRF Token  This token is only required when performing browser flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to profile when trying to update a profile.\n\tMethod string `json:\"method\"`\n\t// Screen requests navigation to a previous screen.  This must be set to credential-selection to go back to the credential selection screen. credential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential previous RegistrationScreenPrevious\n\tScreen *string `json:\"screen,omitempty\"`\n\t// Traits  The identity's traits.\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithProfileMethod UpdateRegistrationFlowWithProfileMethod\n\n// NewUpdateRegistrationFlowWithProfileMethod instantiates a new UpdateRegistrationFlowWithProfileMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithProfileMethod(method string, traits map[string]interface{}) *UpdateRegistrationFlowWithProfileMethod {\n\tthis := UpdateRegistrationFlowWithProfileMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithProfileMethodWithDefaults instantiates a new UpdateRegistrationFlowWithProfileMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithProfileMethodWithDefaults() *UpdateRegistrationFlowWithProfileMethod {\n\tthis := UpdateRegistrationFlowWithProfileMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetScreen returns the Screen field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetScreen() string {\n\tif o == nil || IsNil(o.Screen) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Screen\n}\n\n// GetScreenOk returns a tuple with the Screen field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetScreenOk() (*string, bool) {\n\tif o == nil || IsNil(o.Screen) {\n\t\treturn nil, false\n\t}\n\treturn o.Screen, true\n}\n\n// HasScreen returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) HasScreen() bool {\n\tif o != nil && !IsNil(o.Screen) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetScreen gets a reference to the given string and assigns it to the Screen field.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetScreen(v string) {\n\to.Screen = &v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithProfileMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithProfileMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithProfileMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Screen) {\n\t\ttoSerialize[\"screen\"] = o.Screen\n\t}\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithProfileMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithProfileMethod := _UpdateRegistrationFlowWithProfileMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithProfileMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithProfileMethod(varUpdateRegistrationFlowWithProfileMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"screen\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithProfileMethod struct {\n\tvalue *UpdateRegistrationFlowWithProfileMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithProfileMethod) Get() *UpdateRegistrationFlowWithProfileMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithProfileMethod) Set(val *UpdateRegistrationFlowWithProfileMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithProfileMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithProfileMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithProfileMethod(val *UpdateRegistrationFlowWithProfileMethod) *NullableUpdateRegistrationFlowWithProfileMethod {\n\treturn &NullableUpdateRegistrationFlowWithProfileMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithProfileMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithProfileMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_registration_flow_with_saml_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithSamlMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithSamlMethod{}\n\n// UpdateRegistrationFlowWithSamlMethod Update registration flow using SAML\ntype UpdateRegistrationFlowWithSamlMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method to use  This field must be set to `saml` when using the saml method.\n\tMethod string `json:\"method\"`\n\t// The provider to register with\n\tProvider string `json:\"provider\"`\n\t// The identity traits\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithSamlMethod UpdateRegistrationFlowWithSamlMethod\n\n// NewUpdateRegistrationFlowWithSamlMethod instantiates a new UpdateRegistrationFlowWithSamlMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithSamlMethod(method string, provider string) *UpdateRegistrationFlowWithSamlMethod {\n\tthis := UpdateRegistrationFlowWithSamlMethod{}\n\tthis.Method = method\n\tthis.Provider = provider\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithSamlMethodWithDefaults instantiates a new UpdateRegistrationFlowWithSamlMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithSamlMethodWithDefaults() *UpdateRegistrationFlowWithSamlMethod {\n\tthis := UpdateRegistrationFlowWithSamlMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetProvider returns the Provider field value\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetProvider() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Provider\n}\n\n// GetProviderOk returns a tuple with the Provider field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetProviderOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Provider, true\n}\n\n// SetProvider sets field value\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetProvider(v string) {\n\to.Provider = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithSamlMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateRegistrationFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithSamlMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"provider\"] = o.Provider\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithSamlMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"provider\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithSamlMethod := _UpdateRegistrationFlowWithSamlMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithSamlMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithSamlMethod(varUpdateRegistrationFlowWithSamlMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"provider\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithSamlMethod struct {\n\tvalue *UpdateRegistrationFlowWithSamlMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithSamlMethod) Get() *UpdateRegistrationFlowWithSamlMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithSamlMethod) Set(val *UpdateRegistrationFlowWithSamlMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithSamlMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithSamlMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithSamlMethod(val *UpdateRegistrationFlowWithSamlMethod) *NullableUpdateRegistrationFlowWithSamlMethod {\n\treturn &NullableUpdateRegistrationFlowWithSamlMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithSamlMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_registration_flow_with_web_authn_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateRegistrationFlowWithWebAuthnMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateRegistrationFlowWithWebAuthnMethod{}\n\n// UpdateRegistrationFlowWithWebAuthnMethod Update Registration Flow with WebAuthn Method\ntype UpdateRegistrationFlowWithWebAuthnMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"webauthn\\\" when trying to add, update, or remove a webAuthn pairing.\n\tMethod string `json:\"method\"`\n\t// The identity's traits\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Register a WebAuthn Security Key  It is expected that the JSON returned by the WebAuthn registration process is included here.\n\tWebauthnRegister *string `json:\"webauthn_register,omitempty\"`\n\t// Name of the WebAuthn Security Key to be Added  A human-readable name for the security key which will be added.\n\tWebauthnRegisterDisplayname *string `json:\"webauthn_register_displayname,omitempty\"`\n\tAdditionalProperties        map[string]interface{}\n}\n\ntype _UpdateRegistrationFlowWithWebAuthnMethod UpdateRegistrationFlowWithWebAuthnMethod\n\n// NewUpdateRegistrationFlowWithWebAuthnMethod instantiates a new UpdateRegistrationFlowWithWebAuthnMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateRegistrationFlowWithWebAuthnMethod(method string, traits map[string]interface{}) *UpdateRegistrationFlowWithWebAuthnMethod {\n\tthis := UpdateRegistrationFlowWithWebAuthnMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateRegistrationFlowWithWebAuthnMethodWithDefaults instantiates a new UpdateRegistrationFlowWithWebAuthnMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateRegistrationFlowWithWebAuthnMethodWithDefaults() *UpdateRegistrationFlowWithWebAuthnMethod {\n\tthis := UpdateRegistrationFlowWithWebAuthnMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetWebauthnRegister returns the WebauthnRegister field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetWebauthnRegister() string {\n\tif o == nil || IsNil(o.WebauthnRegister) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRegister\n}\n\n// GetWebauthnRegisterOk returns a tuple with the WebauthnRegister field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetWebauthnRegisterOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRegister) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRegister, true\n}\n\n// HasWebauthnRegister returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) HasWebauthnRegister() bool {\n\tif o != nil && !IsNil(o.WebauthnRegister) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRegister gets a reference to the given string and assigns it to the WebauthnRegister field.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetWebauthnRegister(v string) {\n\to.WebauthnRegister = &v\n}\n\n// GetWebauthnRegisterDisplayname returns the WebauthnRegisterDisplayname field value if set, zero value otherwise.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetWebauthnRegisterDisplayname() string {\n\tif o == nil || IsNil(o.WebauthnRegisterDisplayname) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRegisterDisplayname\n}\n\n// GetWebauthnRegisterDisplaynameOk returns a tuple with the WebauthnRegisterDisplayname field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) GetWebauthnRegisterDisplaynameOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRegisterDisplayname) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRegisterDisplayname, true\n}\n\n// HasWebauthnRegisterDisplayname returns a boolean if a field has been set.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) HasWebauthnRegisterDisplayname() bool {\n\tif o != nil && !IsNil(o.WebauthnRegisterDisplayname) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRegisterDisplayname gets a reference to the given string and assigns it to the WebauthnRegisterDisplayname field.\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) SetWebauthnRegisterDisplayname(v string) {\n\to.WebauthnRegisterDisplayname = &v\n}\n\nfunc (o UpdateRegistrationFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateRegistrationFlowWithWebAuthnMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.WebauthnRegister) {\n\t\ttoSerialize[\"webauthn_register\"] = o.WebauthnRegister\n\t}\n\tif !IsNil(o.WebauthnRegisterDisplayname) {\n\t\ttoSerialize[\"webauthn_register_displayname\"] = o.WebauthnRegisterDisplayname\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateRegistrationFlowWithWebAuthnMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateRegistrationFlowWithWebAuthnMethod := _UpdateRegistrationFlowWithWebAuthnMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateRegistrationFlowWithWebAuthnMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateRegistrationFlowWithWebAuthnMethod(varUpdateRegistrationFlowWithWebAuthnMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"webauthn_register\")\n\t\tdelete(additionalProperties, \"webauthn_register_displayname\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateRegistrationFlowWithWebAuthnMethod struct {\n\tvalue *UpdateRegistrationFlowWithWebAuthnMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateRegistrationFlowWithWebAuthnMethod) Get() *UpdateRegistrationFlowWithWebAuthnMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithWebAuthnMethod) Set(val *UpdateRegistrationFlowWithWebAuthnMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateRegistrationFlowWithWebAuthnMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithWebAuthnMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateRegistrationFlowWithWebAuthnMethod(val *UpdateRegistrationFlowWithWebAuthnMethod) *NullableUpdateRegistrationFlowWithWebAuthnMethod {\n\treturn &NullableUpdateRegistrationFlowWithWebAuthnMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateRegistrationFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateRegistrationFlowWithWebAuthnMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_settings_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateSettingsFlowBody - Update Settings Flow Request Body\ntype UpdateSettingsFlowBody struct {\n\tUpdateSettingsFlowWithLookupMethod   *UpdateSettingsFlowWithLookupMethod\n\tUpdateSettingsFlowWithOidcMethod     *UpdateSettingsFlowWithOidcMethod\n\tUpdateSettingsFlowWithPasskeyMethod  *UpdateSettingsFlowWithPasskeyMethod\n\tUpdateSettingsFlowWithPasswordMethod *UpdateSettingsFlowWithPasswordMethod\n\tUpdateSettingsFlowWithProfileMethod  *UpdateSettingsFlowWithProfileMethod\n\tUpdateSettingsFlowWithSamlMethod     *UpdateSettingsFlowWithSamlMethod\n\tUpdateSettingsFlowWithTotpMethod     *UpdateSettingsFlowWithTotpMethod\n\tUpdateSettingsFlowWithWebAuthnMethod *UpdateSettingsFlowWithWebAuthnMethod\n}\n\n// UpdateSettingsFlowWithLookupMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithLookupMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithLookupMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithLookupMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithLookupMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithOidcMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithOidcMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithOidcMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithOidcMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithOidcMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithPasskeyMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithPasskeyMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithPasskeyMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithPasskeyMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithPasskeyMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithPasswordMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithPasswordMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithPasswordMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithPasswordMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithPasswordMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithProfileMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithProfileMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithProfileMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithProfileMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithProfileMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithSamlMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithSamlMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithSamlMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithSamlMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithSamlMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithTotpMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithTotpMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithTotpMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithTotpMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithTotpMethod: v,\n\t}\n}\n\n// UpdateSettingsFlowWithWebAuthnMethodAsUpdateSettingsFlowBody is a convenience function that returns UpdateSettingsFlowWithWebAuthnMethod wrapped in UpdateSettingsFlowBody\nfunc UpdateSettingsFlowWithWebAuthnMethodAsUpdateSettingsFlowBody(v *UpdateSettingsFlowWithWebAuthnMethod) UpdateSettingsFlowBody {\n\treturn UpdateSettingsFlowBody{\n\t\tUpdateSettingsFlowWithWebAuthnMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateSettingsFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'lookup_secret'\n\tif jsonDict[\"method\"] == \"lookup_secret\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithLookupMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithLookupMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithLookupMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithLookupMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithLookupMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'oidc'\n\tif jsonDict[\"method\"] == \"oidc\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'passkey'\n\tif jsonDict[\"method\"] == \"passkey\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'password'\n\tif jsonDict[\"method\"] == \"password\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'profile'\n\tif jsonDict[\"method\"] == \"profile\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithProfileMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithProfileMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithProfileMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithProfileMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithProfileMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'saml'\n\tif jsonDict[\"method\"] == \"saml\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'totp'\n\tif jsonDict[\"method\"] == \"totp\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithTotpMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithTotpMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithTotpMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithTotpMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithTotpMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'webauthn'\n\tif jsonDict[\"method\"] == \"webauthn\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithLookupMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithLookupMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithLookupMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithLookupMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithLookupMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithLookupMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithLookupMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithOidcMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithOidcMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithOidcMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithOidcMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithOidcMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithOidcMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithOidcMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithPasskeyMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithPasskeyMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithPasskeyMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithPasskeyMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithPasskeyMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithPasskeyMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithPasskeyMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithPasswordMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithPasswordMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithPasswordMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithPasswordMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithPasswordMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithPasswordMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithPasswordMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithProfileMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithProfileMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithProfileMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithProfileMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithProfileMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithProfileMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithProfileMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithSamlMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithSamlMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithSamlMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithSamlMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithSamlMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithSamlMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithSamlMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithTotpMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithTotpMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithTotpMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithTotpMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithTotpMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithTotpMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithTotpMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateSettingsFlowWithWebAuthnMethod'\n\tif jsonDict[\"method\"] == \"updateSettingsFlowWithWebAuthnMethod\" {\n\t\t// try to unmarshal JSON data into UpdateSettingsFlowWithWebAuthnMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateSettingsFlowWithWebAuthnMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateSettingsFlowWithWebAuthnMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateSettingsFlowWithWebAuthnMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateSettingsFlowBody as UpdateSettingsFlowWithWebAuthnMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateSettingsFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateSettingsFlowWithLookupMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithLookupMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithOidcMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithOidcMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithPasskeyMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithPasskeyMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithPasswordMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithPasswordMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithProfileMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithProfileMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithSamlMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithSamlMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithTotpMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithTotpMethod)\n\t}\n\n\tif src.UpdateSettingsFlowWithWebAuthnMethod != nil {\n\t\treturn json.Marshal(&src.UpdateSettingsFlowWithWebAuthnMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateSettingsFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateSettingsFlowWithLookupMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithLookupMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithOidcMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithPasskeyMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithPasswordMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithProfileMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithProfileMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithSamlMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithTotpMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithTotpMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithWebAuthnMethod != nil {\n\t\treturn obj.UpdateSettingsFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateSettingsFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateSettingsFlowWithLookupMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithLookupMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithOidcMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithOidcMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithPasskeyMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithPasskeyMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithPasswordMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithPasswordMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithProfileMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithProfileMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithSamlMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithSamlMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithTotpMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithTotpMethod\n\t}\n\n\tif obj.UpdateSettingsFlowWithWebAuthnMethod != nil {\n\t\treturn *obj.UpdateSettingsFlowWithWebAuthnMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateSettingsFlowBody struct {\n\tvalue *UpdateSettingsFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowBody) Get() *UpdateSettingsFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowBody) Set(val *UpdateSettingsFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowBody(val *UpdateSettingsFlowBody) *NullableUpdateSettingsFlowBody {\n\treturn &NullableUpdateSettingsFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_settings_flow_with_lookup_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithLookupMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithLookupMethod{}\n\n// UpdateSettingsFlowWithLookupMethod Update Settings Flow with Lookup Method\ntype UpdateSettingsFlowWithLookupMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// If set to true will save the regenerated lookup secrets\n\tLookupSecretConfirm *bool `json:\"lookup_secret_confirm,omitempty\"`\n\t// Disables this method if true.\n\tLookupSecretDisable *bool `json:\"lookup_secret_disable,omitempty\"`\n\t// If set to true will regenerate the lookup secrets\n\tLookupSecretRegenerate *bool `json:\"lookup_secret_regenerate,omitempty\"`\n\t// If set to true will reveal the lookup secrets\n\tLookupSecretReveal *bool `json:\"lookup_secret_reveal,omitempty\"`\n\t// Method  Should be set to \\\"lookup\\\" when trying to add, update, or remove a lookup pairing.\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithLookupMethod UpdateSettingsFlowWithLookupMethod\n\n// NewUpdateSettingsFlowWithLookupMethod instantiates a new UpdateSettingsFlowWithLookupMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithLookupMethod(method string) *UpdateSettingsFlowWithLookupMethod {\n\tthis := UpdateSettingsFlowWithLookupMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithLookupMethodWithDefaults instantiates a new UpdateSettingsFlowWithLookupMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithLookupMethodWithDefaults() *UpdateSettingsFlowWithLookupMethod {\n\tthis := UpdateSettingsFlowWithLookupMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetLookupSecretConfirm returns the LookupSecretConfirm field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretConfirm() bool {\n\tif o == nil || IsNil(o.LookupSecretConfirm) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.LookupSecretConfirm\n}\n\n// GetLookupSecretConfirmOk returns a tuple with the LookupSecretConfirm field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretConfirmOk() (*bool, bool) {\n\tif o == nil || IsNil(o.LookupSecretConfirm) {\n\t\treturn nil, false\n\t}\n\treturn o.LookupSecretConfirm, true\n}\n\n// HasLookupSecretConfirm returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasLookupSecretConfirm() bool {\n\tif o != nil && !IsNil(o.LookupSecretConfirm) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLookupSecretConfirm gets a reference to the given bool and assigns it to the LookupSecretConfirm field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetLookupSecretConfirm(v bool) {\n\to.LookupSecretConfirm = &v\n}\n\n// GetLookupSecretDisable returns the LookupSecretDisable field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretDisable() bool {\n\tif o == nil || IsNil(o.LookupSecretDisable) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.LookupSecretDisable\n}\n\n// GetLookupSecretDisableOk returns a tuple with the LookupSecretDisable field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretDisableOk() (*bool, bool) {\n\tif o == nil || IsNil(o.LookupSecretDisable) {\n\t\treturn nil, false\n\t}\n\treturn o.LookupSecretDisable, true\n}\n\n// HasLookupSecretDisable returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasLookupSecretDisable() bool {\n\tif o != nil && !IsNil(o.LookupSecretDisable) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLookupSecretDisable gets a reference to the given bool and assigns it to the LookupSecretDisable field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetLookupSecretDisable(v bool) {\n\to.LookupSecretDisable = &v\n}\n\n// GetLookupSecretRegenerate returns the LookupSecretRegenerate field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretRegenerate() bool {\n\tif o == nil || IsNil(o.LookupSecretRegenerate) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.LookupSecretRegenerate\n}\n\n// GetLookupSecretRegenerateOk returns a tuple with the LookupSecretRegenerate field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretRegenerateOk() (*bool, bool) {\n\tif o == nil || IsNil(o.LookupSecretRegenerate) {\n\t\treturn nil, false\n\t}\n\treturn o.LookupSecretRegenerate, true\n}\n\n// HasLookupSecretRegenerate returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasLookupSecretRegenerate() bool {\n\tif o != nil && !IsNil(o.LookupSecretRegenerate) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLookupSecretRegenerate gets a reference to the given bool and assigns it to the LookupSecretRegenerate field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetLookupSecretRegenerate(v bool) {\n\to.LookupSecretRegenerate = &v\n}\n\n// GetLookupSecretReveal returns the LookupSecretReveal field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretReveal() bool {\n\tif o == nil || IsNil(o.LookupSecretReveal) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.LookupSecretReveal\n}\n\n// GetLookupSecretRevealOk returns a tuple with the LookupSecretReveal field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetLookupSecretRevealOk() (*bool, bool) {\n\tif o == nil || IsNil(o.LookupSecretReveal) {\n\t\treturn nil, false\n\t}\n\treturn o.LookupSecretReveal, true\n}\n\n// HasLookupSecretReveal returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasLookupSecretReveal() bool {\n\tif o != nil && !IsNil(o.LookupSecretReveal) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLookupSecretReveal gets a reference to the given bool and assigns it to the LookupSecretReveal field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetLookupSecretReveal(v bool) {\n\to.LookupSecretReveal = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithLookupMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithLookupMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateSettingsFlowWithLookupMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithLookupMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.LookupSecretConfirm) {\n\t\ttoSerialize[\"lookup_secret_confirm\"] = o.LookupSecretConfirm\n\t}\n\tif !IsNil(o.LookupSecretDisable) {\n\t\ttoSerialize[\"lookup_secret_disable\"] = o.LookupSecretDisable\n\t}\n\tif !IsNil(o.LookupSecretRegenerate) {\n\t\ttoSerialize[\"lookup_secret_regenerate\"] = o.LookupSecretRegenerate\n\t}\n\tif !IsNil(o.LookupSecretReveal) {\n\t\ttoSerialize[\"lookup_secret_reveal\"] = o.LookupSecretReveal\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithLookupMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithLookupMethod := _UpdateSettingsFlowWithLookupMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithLookupMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithLookupMethod(varUpdateSettingsFlowWithLookupMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"lookup_secret_confirm\")\n\t\tdelete(additionalProperties, \"lookup_secret_disable\")\n\t\tdelete(additionalProperties, \"lookup_secret_regenerate\")\n\t\tdelete(additionalProperties, \"lookup_secret_reveal\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithLookupMethod struct {\n\tvalue *UpdateSettingsFlowWithLookupMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithLookupMethod) Get() *UpdateSettingsFlowWithLookupMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithLookupMethod) Set(val *UpdateSettingsFlowWithLookupMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithLookupMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithLookupMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithLookupMethod(val *UpdateSettingsFlowWithLookupMethod) *NullableUpdateSettingsFlowWithLookupMethod {\n\treturn &NullableUpdateSettingsFlowWithLookupMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithLookupMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithLookupMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_settings_flow_with_oidc_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithOidcMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithOidcMethod{}\n\n// UpdateSettingsFlowWithOidcMethod Update Settings Flow with OpenID Connect Method\ntype UpdateSettingsFlowWithOidcMethod struct {\n\t// Flow ID is the flow's ID.  in: query\n\tFlow *string `json:\"flow,omitempty\"`\n\t// Link this provider  Either this or `unlink` must be set.  type: string in: body\n\tLink *string `json:\"link,omitempty\"`\n\t// Method  Should be set to profile when trying to update a profile.\n\tMethod string `json:\"method\"`\n\t// The identity's traits  in: body\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Unlink this provider  Either this or `link` must be set.  type: string in: body\n\tUnlink *string `json:\"unlink,omitempty\"`\n\t// UpstreamParameters are the parameters that are passed to the upstream identity provider.  These parameters are optional and depend on what the upstream identity provider supports. Supported parameters are: `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session. `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`. `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`. `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\n\tUpstreamParameters   map[string]interface{} `json:\"upstream_parameters,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithOidcMethod UpdateSettingsFlowWithOidcMethod\n\n// NewUpdateSettingsFlowWithOidcMethod instantiates a new UpdateSettingsFlowWithOidcMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithOidcMethod(method string) *UpdateSettingsFlowWithOidcMethod {\n\tthis := UpdateSettingsFlowWithOidcMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithOidcMethodWithDefaults instantiates a new UpdateSettingsFlowWithOidcMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithOidcMethodWithDefaults() *UpdateSettingsFlowWithOidcMethod {\n\tthis := UpdateSettingsFlowWithOidcMethod{}\n\treturn &this\n}\n\n// GetFlow returns the Flow field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetFlow() string {\n\tif o == nil || IsNil(o.Flow) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetFlowOk() (*string, bool) {\n\tif o == nil || IsNil(o.Flow) {\n\t\treturn nil, false\n\t}\n\treturn o.Flow, true\n}\n\n// HasFlow returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasFlow() bool {\n\tif o != nil && !IsNil(o.Flow) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFlow gets a reference to the given string and assigns it to the Flow field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetFlow(v string) {\n\to.Flow = &v\n}\n\n// GetLink returns the Link field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetLink() string {\n\tif o == nil || IsNil(o.Link) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Link\n}\n\n// GetLinkOk returns a tuple with the Link field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetLinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Link) {\n\t\treturn nil, false\n\t}\n\treturn o.Link, true\n}\n\n// HasLink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasLink() bool {\n\tif o != nil && !IsNil(o.Link) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLink gets a reference to the given string and assigns it to the Link field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetLink(v string) {\n\to.Link = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetUnlink returns the Unlink field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetUnlink() string {\n\tif o == nil || IsNil(o.Unlink) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Unlink\n}\n\n// GetUnlinkOk returns a tuple with the Unlink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetUnlinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Unlink) {\n\t\treturn nil, false\n\t}\n\treturn o.Unlink, true\n}\n\n// HasUnlink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasUnlink() bool {\n\tif o != nil && !IsNil(o.Unlink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUnlink gets a reference to the given string and assigns it to the Unlink field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetUnlink(v string) {\n\to.Unlink = &v\n}\n\n// GetUpstreamParameters returns the UpstreamParameters field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetUpstreamParameters() map[string]interface{} {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.UpstreamParameters\n}\n\n// GetUpstreamParametersOk returns a tuple with the UpstreamParameters field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) GetUpstreamParametersOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.UpstreamParameters) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.UpstreamParameters, true\n}\n\n// HasUpstreamParameters returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithOidcMethod) HasUpstreamParameters() bool {\n\tif o != nil && !IsNil(o.UpstreamParameters) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpstreamParameters gets a reference to the given map[string]interface{} and assigns it to the UpstreamParameters field.\nfunc (o *UpdateSettingsFlowWithOidcMethod) SetUpstreamParameters(v map[string]interface{}) {\n\to.UpstreamParameters = v\n}\n\nfunc (o UpdateSettingsFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithOidcMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Flow) {\n\t\ttoSerialize[\"flow\"] = o.Flow\n\t}\n\tif !IsNil(o.Link) {\n\t\ttoSerialize[\"link\"] = o.Link\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.Unlink) {\n\t\ttoSerialize[\"unlink\"] = o.Unlink\n\t}\n\tif !IsNil(o.UpstreamParameters) {\n\t\ttoSerialize[\"upstream_parameters\"] = o.UpstreamParameters\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithOidcMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithOidcMethod := _UpdateSettingsFlowWithOidcMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithOidcMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithOidcMethod(varUpdateSettingsFlowWithOidcMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"flow\")\n\t\tdelete(additionalProperties, \"link\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"unlink\")\n\t\tdelete(additionalProperties, \"upstream_parameters\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithOidcMethod struct {\n\tvalue *UpdateSettingsFlowWithOidcMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithOidcMethod) Get() *UpdateSettingsFlowWithOidcMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithOidcMethod) Set(val *UpdateSettingsFlowWithOidcMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithOidcMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithOidcMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithOidcMethod(val *UpdateSettingsFlowWithOidcMethod) *NullableUpdateSettingsFlowWithOidcMethod {\n\treturn &NullableUpdateSettingsFlowWithOidcMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithOidcMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithOidcMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_settings_flow_with_passkey_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithPasskeyMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithPasskeyMethod{}\n\n// UpdateSettingsFlowWithPasskeyMethod Update Settings Flow with Passkey Method\ntype UpdateSettingsFlowWithPasskeyMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"passkey\\\" when trying to add, update, or remove a webAuthn pairing.\n\tMethod string `json:\"method\"`\n\t// Remove a WebAuthn Security Key  This must contain the ID of the WebAuthN connection.\n\tPasskeyRemove *string `json:\"passkey_remove,omitempty\"`\n\t// Register a WebAuthn Security Key  It is expected that the JSON returned by the WebAuthn registration process is included here.\n\tPasskeySettingsRegister *string `json:\"passkey_settings_register,omitempty\"`\n\tAdditionalProperties    map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithPasskeyMethod UpdateSettingsFlowWithPasskeyMethod\n\n// NewUpdateSettingsFlowWithPasskeyMethod instantiates a new UpdateSettingsFlowWithPasskeyMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithPasskeyMethod(method string) *UpdateSettingsFlowWithPasskeyMethod {\n\tthis := UpdateSettingsFlowWithPasskeyMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithPasskeyMethodWithDefaults instantiates a new UpdateSettingsFlowWithPasskeyMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithPasskeyMethodWithDefaults() *UpdateSettingsFlowWithPasskeyMethod {\n\tthis := UpdateSettingsFlowWithPasskeyMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPasskeyRemove returns the PasskeyRemove field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetPasskeyRemove() string {\n\tif o == nil || IsNil(o.PasskeyRemove) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasskeyRemove\n}\n\n// GetPasskeyRemoveOk returns a tuple with the PasskeyRemove field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetPasskeyRemoveOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasskeyRemove) {\n\t\treturn nil, false\n\t}\n\treturn o.PasskeyRemove, true\n}\n\n// HasPasskeyRemove returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) HasPasskeyRemove() bool {\n\tif o != nil && !IsNil(o.PasskeyRemove) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasskeyRemove gets a reference to the given string and assigns it to the PasskeyRemove field.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) SetPasskeyRemove(v string) {\n\to.PasskeyRemove = &v\n}\n\n// GetPasskeySettingsRegister returns the PasskeySettingsRegister field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetPasskeySettingsRegister() string {\n\tif o == nil || IsNil(o.PasskeySettingsRegister) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.PasskeySettingsRegister\n}\n\n// GetPasskeySettingsRegisterOk returns a tuple with the PasskeySettingsRegister field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) GetPasskeySettingsRegisterOk() (*string, bool) {\n\tif o == nil || IsNil(o.PasskeySettingsRegister) {\n\t\treturn nil, false\n\t}\n\treturn o.PasskeySettingsRegister, true\n}\n\n// HasPasskeySettingsRegister returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) HasPasskeySettingsRegister() bool {\n\tif o != nil && !IsNil(o.PasskeySettingsRegister) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetPasskeySettingsRegister gets a reference to the given string and assigns it to the PasskeySettingsRegister field.\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) SetPasskeySettingsRegister(v string) {\n\to.PasskeySettingsRegister = &v\n}\n\nfunc (o UpdateSettingsFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithPasskeyMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.PasskeyRemove) {\n\t\ttoSerialize[\"passkey_remove\"] = o.PasskeyRemove\n\t}\n\tif !IsNil(o.PasskeySettingsRegister) {\n\t\ttoSerialize[\"passkey_settings_register\"] = o.PasskeySettingsRegister\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithPasskeyMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithPasskeyMethod := _UpdateSettingsFlowWithPasskeyMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithPasskeyMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithPasskeyMethod(varUpdateSettingsFlowWithPasskeyMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"passkey_remove\")\n\t\tdelete(additionalProperties, \"passkey_settings_register\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithPasskeyMethod struct {\n\tvalue *UpdateSettingsFlowWithPasskeyMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasskeyMethod) Get() *UpdateSettingsFlowWithPasskeyMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasskeyMethod) Set(val *UpdateSettingsFlowWithPasskeyMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasskeyMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasskeyMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithPasskeyMethod(val *UpdateSettingsFlowWithPasskeyMethod) *NullableUpdateSettingsFlowWithPasskeyMethod {\n\treturn &NullableUpdateSettingsFlowWithPasskeyMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasskeyMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasskeyMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_settings_flow_with_password_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithPasswordMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithPasswordMethod{}\n\n// UpdateSettingsFlowWithPasswordMethod Update Settings Flow with Password Method\ntype UpdateSettingsFlowWithPasswordMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to password when trying to update a password.\n\tMethod string `json:\"method\"`\n\t// Password is the updated password\n\tPassword string `json:\"password\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithPasswordMethod UpdateSettingsFlowWithPasswordMethod\n\n// NewUpdateSettingsFlowWithPasswordMethod instantiates a new UpdateSettingsFlowWithPasswordMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithPasswordMethod(method string, password string) *UpdateSettingsFlowWithPasswordMethod {\n\tthis := UpdateSettingsFlowWithPasswordMethod{}\n\tthis.Method = method\n\tthis.Password = password\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithPasswordMethodWithDefaults instantiates a new UpdateSettingsFlowWithPasswordMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithPasswordMethodWithDefaults() *UpdateSettingsFlowWithPasswordMethod {\n\tthis := UpdateSettingsFlowWithPasswordMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithPasswordMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetPassword returns the Password field value\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetPassword() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Password\n}\n\n// GetPasswordOk returns a tuple with the Password field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetPasswordOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Password, true\n}\n\n// SetPassword sets field value\nfunc (o *UpdateSettingsFlowWithPasswordMethod) SetPassword(v string) {\n\to.Password = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithPasswordMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateSettingsFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithPasswordMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"password\"] = o.Password\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithPasswordMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"password\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithPasswordMethod := _UpdateSettingsFlowWithPasswordMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithPasswordMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithPasswordMethod(varUpdateSettingsFlowWithPasswordMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"password\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithPasswordMethod struct {\n\tvalue *UpdateSettingsFlowWithPasswordMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasswordMethod) Get() *UpdateSettingsFlowWithPasswordMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasswordMethod) Set(val *UpdateSettingsFlowWithPasswordMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasswordMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasswordMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithPasswordMethod(val *UpdateSettingsFlowWithPasswordMethod) *NullableUpdateSettingsFlowWithPasswordMethod {\n\treturn &NullableUpdateSettingsFlowWithPasswordMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithPasswordMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithPasswordMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_settings_flow_with_profile_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithProfileMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithProfileMethod{}\n\n// UpdateSettingsFlowWithProfileMethod Update Settings Flow with Profile Method\ntype UpdateSettingsFlowWithProfileMethod struct {\n\t// The Anti-CSRF Token  This token is only required when performing browser flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to profile when trying to update a profile.\n\tMethod string `json:\"method\"`\n\t// Traits  The identity's traits.\n\tTraits map[string]interface{} `json:\"traits\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithProfileMethod UpdateSettingsFlowWithProfileMethod\n\n// NewUpdateSettingsFlowWithProfileMethod instantiates a new UpdateSettingsFlowWithProfileMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithProfileMethod(method string, traits map[string]interface{}) *UpdateSettingsFlowWithProfileMethod {\n\tthis := UpdateSettingsFlowWithProfileMethod{}\n\tthis.Method = method\n\tthis.Traits = traits\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithProfileMethodWithDefaults instantiates a new UpdateSettingsFlowWithProfileMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithProfileMethodWithDefaults() *UpdateSettingsFlowWithProfileMethod {\n\tthis := UpdateSettingsFlowWithProfileMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithProfileMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithProfileMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTraits returns the Traits field value\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetTraits() map[string]interface{} {\n\tif o == nil {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// SetTraits sets field value\nfunc (o *UpdateSettingsFlowWithProfileMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithProfileMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithProfileMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateSettingsFlowWithProfileMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithProfileMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\ttoSerialize[\"traits\"] = o.Traits\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithProfileMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t\t\"traits\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithProfileMethod := _UpdateSettingsFlowWithProfileMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithProfileMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithProfileMethod(varUpdateSettingsFlowWithProfileMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithProfileMethod struct {\n\tvalue *UpdateSettingsFlowWithProfileMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithProfileMethod) Get() *UpdateSettingsFlowWithProfileMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithProfileMethod) Set(val *UpdateSettingsFlowWithProfileMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithProfileMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithProfileMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithProfileMethod(val *UpdateSettingsFlowWithProfileMethod) *NullableUpdateSettingsFlowWithProfileMethod {\n\treturn &NullableUpdateSettingsFlowWithProfileMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithProfileMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithProfileMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_settings_flow_with_saml_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithSamlMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithSamlMethod{}\n\n// UpdateSettingsFlowWithSamlMethod Update settings flow using SAML\ntype UpdateSettingsFlowWithSamlMethod struct {\n\t// The CSRF Token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Flow ID is the flow's ID.  in: query\n\tFlow *string `json:\"flow,omitempty\"`\n\t// Link this provider  Either this or `unlink` must be set.  type: string in: body\n\tLink *string `json:\"link,omitempty\"`\n\t// Method  Should be set to saml when trying to update a profile.\n\tMethod string `json:\"method\"`\n\t// The identity's traits  in: body\n\tTraits map[string]interface{} `json:\"traits,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Unlink this provider  Either this or `link` must be set.  type: string in: body\n\tUnlink               *string `json:\"unlink,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithSamlMethod UpdateSettingsFlowWithSamlMethod\n\n// NewUpdateSettingsFlowWithSamlMethod instantiates a new UpdateSettingsFlowWithSamlMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithSamlMethod(method string) *UpdateSettingsFlowWithSamlMethod {\n\tthis := UpdateSettingsFlowWithSamlMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithSamlMethodWithDefaults instantiates a new UpdateSettingsFlowWithSamlMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithSamlMethodWithDefaults() *UpdateSettingsFlowWithSamlMethod {\n\tthis := UpdateSettingsFlowWithSamlMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetFlow returns the Flow field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetFlow() string {\n\tif o == nil || IsNil(o.Flow) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Flow\n}\n\n// GetFlowOk returns a tuple with the Flow field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetFlowOk() (*string, bool) {\n\tif o == nil || IsNil(o.Flow) {\n\t\treturn nil, false\n\t}\n\treturn o.Flow, true\n}\n\n// HasFlow returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasFlow() bool {\n\tif o != nil && !IsNil(o.Flow) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetFlow gets a reference to the given string and assigns it to the Flow field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetFlow(v string) {\n\to.Flow = &v\n}\n\n// GetLink returns the Link field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetLink() string {\n\tif o == nil || IsNil(o.Link) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Link\n}\n\n// GetLinkOk returns a tuple with the Link field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetLinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Link) {\n\t\treturn nil, false\n\t}\n\treturn o.Link, true\n}\n\n// HasLink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasLink() bool {\n\tif o != nil && !IsNil(o.Link) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetLink gets a reference to the given string and assigns it to the Link field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetLink(v string) {\n\to.Link = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTraits returns the Traits field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetTraits() map[string]interface{} {\n\tif o == nil || IsNil(o.Traits) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.Traits\n}\n\n// GetTraitsOk returns a tuple with the Traits field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetTraitsOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.Traits) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.Traits, true\n}\n\n// HasTraits returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasTraits() bool {\n\tif o != nil && !IsNil(o.Traits) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTraits gets a reference to the given map[string]interface{} and assigns it to the Traits field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetTraits(v map[string]interface{}) {\n\to.Traits = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetUnlink returns the Unlink field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetUnlink() string {\n\tif o == nil || IsNil(o.Unlink) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Unlink\n}\n\n// GetUnlinkOk returns a tuple with the Unlink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) GetUnlinkOk() (*string, bool) {\n\tif o == nil || IsNil(o.Unlink) {\n\t\treturn nil, false\n\t}\n\treturn o.Unlink, true\n}\n\n// HasUnlink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithSamlMethod) HasUnlink() bool {\n\tif o != nil && !IsNil(o.Unlink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUnlink gets a reference to the given string and assigns it to the Unlink field.\nfunc (o *UpdateSettingsFlowWithSamlMethod) SetUnlink(v string) {\n\to.Unlink = &v\n}\n\nfunc (o UpdateSettingsFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithSamlMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.Flow) {\n\t\ttoSerialize[\"flow\"] = o.Flow\n\t}\n\tif !IsNil(o.Link) {\n\t\ttoSerialize[\"link\"] = o.Link\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.Traits) {\n\t\ttoSerialize[\"traits\"] = o.Traits\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.Unlink) {\n\t\ttoSerialize[\"unlink\"] = o.Unlink\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithSamlMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithSamlMethod := _UpdateSettingsFlowWithSamlMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithSamlMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithSamlMethod(varUpdateSettingsFlowWithSamlMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"flow\")\n\t\tdelete(additionalProperties, \"link\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"traits\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"unlink\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithSamlMethod struct {\n\tvalue *UpdateSettingsFlowWithSamlMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithSamlMethod) Get() *UpdateSettingsFlowWithSamlMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithSamlMethod) Set(val *UpdateSettingsFlowWithSamlMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithSamlMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithSamlMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithSamlMethod(val *UpdateSettingsFlowWithSamlMethod) *NullableUpdateSettingsFlowWithSamlMethod {\n\treturn &NullableUpdateSettingsFlowWithSamlMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithSamlMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithSamlMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_settings_flow_with_totp_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithTotpMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithTotpMethod{}\n\n// UpdateSettingsFlowWithTotpMethod Update Settings Flow with TOTP Method\ntype UpdateSettingsFlowWithTotpMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"totp\\\" when trying to add, update, or remove a totp pairing.\n\tMethod string `json:\"method\"`\n\t// ValidationTOTP must contain a valid TOTP based on the\n\tTotpCode *string `json:\"totp_code,omitempty\"`\n\t// UnlinkTOTP if true will remove the TOTP pairing, effectively removing the credential. This can be used to set up a new TOTP device.\n\tTotpUnlink *bool `json:\"totp_unlink,omitempty\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithTotpMethod UpdateSettingsFlowWithTotpMethod\n\n// NewUpdateSettingsFlowWithTotpMethod instantiates a new UpdateSettingsFlowWithTotpMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithTotpMethod(method string) *UpdateSettingsFlowWithTotpMethod {\n\tthis := UpdateSettingsFlowWithTotpMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithTotpMethodWithDefaults instantiates a new UpdateSettingsFlowWithTotpMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithTotpMethodWithDefaults() *UpdateSettingsFlowWithTotpMethod {\n\tthis := UpdateSettingsFlowWithTotpMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTotpCode returns the TotpCode field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTotpCode() string {\n\tif o == nil || IsNil(o.TotpCode) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.TotpCode\n}\n\n// GetTotpCodeOk returns a tuple with the TotpCode field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTotpCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.TotpCode) {\n\t\treturn nil, false\n\t}\n\treturn o.TotpCode, true\n}\n\n// HasTotpCode returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) HasTotpCode() bool {\n\tif o != nil && !IsNil(o.TotpCode) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTotpCode gets a reference to the given string and assigns it to the TotpCode field.\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetTotpCode(v string) {\n\to.TotpCode = &v\n}\n\n// GetTotpUnlink returns the TotpUnlink field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTotpUnlink() bool {\n\tif o == nil || IsNil(o.TotpUnlink) {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\treturn *o.TotpUnlink\n}\n\n// GetTotpUnlinkOk returns a tuple with the TotpUnlink field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTotpUnlinkOk() (*bool, bool) {\n\tif o == nil || IsNil(o.TotpUnlink) {\n\t\treturn nil, false\n\t}\n\treturn o.TotpUnlink, true\n}\n\n// HasTotpUnlink returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) HasTotpUnlink() bool {\n\tif o != nil && !IsNil(o.TotpUnlink) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTotpUnlink gets a reference to the given bool and assigns it to the TotpUnlink field.\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetTotpUnlink(v bool) {\n\to.TotpUnlink = &v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithTotpMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithTotpMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateSettingsFlowWithTotpMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithTotpMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TotpCode) {\n\t\ttoSerialize[\"totp_code\"] = o.TotpCode\n\t}\n\tif !IsNil(o.TotpUnlink) {\n\t\ttoSerialize[\"totp_unlink\"] = o.TotpUnlink\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithTotpMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithTotpMethod := _UpdateSettingsFlowWithTotpMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithTotpMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithTotpMethod(varUpdateSettingsFlowWithTotpMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"totp_code\")\n\t\tdelete(additionalProperties, \"totp_unlink\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithTotpMethod struct {\n\tvalue *UpdateSettingsFlowWithTotpMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithTotpMethod) Get() *UpdateSettingsFlowWithTotpMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithTotpMethod) Set(val *UpdateSettingsFlowWithTotpMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithTotpMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithTotpMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithTotpMethod(val *UpdateSettingsFlowWithTotpMethod) *NullableUpdateSettingsFlowWithTotpMethod {\n\treturn &NullableUpdateSettingsFlowWithTotpMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithTotpMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithTotpMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_settings_flow_with_web_authn_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateSettingsFlowWithWebAuthnMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateSettingsFlowWithWebAuthnMethod{}\n\n// UpdateSettingsFlowWithWebAuthnMethod Update Settings Flow with WebAuthn Method\ntype UpdateSettingsFlowWithWebAuthnMethod struct {\n\t// CSRFToken is the anti-CSRF token\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Method  Should be set to \\\"webauthn\\\" when trying to add, update, or remove a webAuthn pairing.\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// Register a WebAuthn Security Key  It is expected that the JSON returned by the WebAuthn registration process is included here.\n\tWebauthnRegister *string `json:\"webauthn_register,omitempty\"`\n\t// Name of the WebAuthn Security Key to be Added  A human-readable name for the security key which will be added.\n\tWebauthnRegisterDisplayname *string `json:\"webauthn_register_displayname,omitempty\"`\n\t// Remove a WebAuthn Security Key  This must contain the ID of the WebAuthN connection.\n\tWebauthnRemove       *string `json:\"webauthn_remove,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateSettingsFlowWithWebAuthnMethod UpdateSettingsFlowWithWebAuthnMethod\n\n// NewUpdateSettingsFlowWithWebAuthnMethod instantiates a new UpdateSettingsFlowWithWebAuthnMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateSettingsFlowWithWebAuthnMethod(method string) *UpdateSettingsFlowWithWebAuthnMethod {\n\tthis := UpdateSettingsFlowWithWebAuthnMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateSettingsFlowWithWebAuthnMethodWithDefaults instantiates a new UpdateSettingsFlowWithWebAuthnMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateSettingsFlowWithWebAuthnMethodWithDefaults() *UpdateSettingsFlowWithWebAuthnMethod {\n\tthis := UpdateSettingsFlowWithWebAuthnMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetWebauthnRegister returns the WebauthnRegister field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRegister() string {\n\tif o == nil || IsNil(o.WebauthnRegister) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRegister\n}\n\n// GetWebauthnRegisterOk returns a tuple with the WebauthnRegister field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRegisterOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRegister) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRegister, true\n}\n\n// HasWebauthnRegister returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasWebauthnRegister() bool {\n\tif o != nil && !IsNil(o.WebauthnRegister) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRegister gets a reference to the given string and assigns it to the WebauthnRegister field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetWebauthnRegister(v string) {\n\to.WebauthnRegister = &v\n}\n\n// GetWebauthnRegisterDisplayname returns the WebauthnRegisterDisplayname field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRegisterDisplayname() string {\n\tif o == nil || IsNil(o.WebauthnRegisterDisplayname) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRegisterDisplayname\n}\n\n// GetWebauthnRegisterDisplaynameOk returns a tuple with the WebauthnRegisterDisplayname field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRegisterDisplaynameOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRegisterDisplayname) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRegisterDisplayname, true\n}\n\n// HasWebauthnRegisterDisplayname returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasWebauthnRegisterDisplayname() bool {\n\tif o != nil && !IsNil(o.WebauthnRegisterDisplayname) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRegisterDisplayname gets a reference to the given string and assigns it to the WebauthnRegisterDisplayname field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetWebauthnRegisterDisplayname(v string) {\n\to.WebauthnRegisterDisplayname = &v\n}\n\n// GetWebauthnRemove returns the WebauthnRemove field value if set, zero value otherwise.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRemove() string {\n\tif o == nil || IsNil(o.WebauthnRemove) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.WebauthnRemove\n}\n\n// GetWebauthnRemoveOk returns a tuple with the WebauthnRemove field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) GetWebauthnRemoveOk() (*string, bool) {\n\tif o == nil || IsNil(o.WebauthnRemove) {\n\t\treturn nil, false\n\t}\n\treturn o.WebauthnRemove, true\n}\n\n// HasWebauthnRemove returns a boolean if a field has been set.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) HasWebauthnRemove() bool {\n\tif o != nil && !IsNil(o.WebauthnRemove) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetWebauthnRemove gets a reference to the given string and assigns it to the WebauthnRemove field.\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) SetWebauthnRemove(v string) {\n\to.WebauthnRemove = &v\n}\n\nfunc (o UpdateSettingsFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateSettingsFlowWithWebAuthnMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\tif !IsNil(o.WebauthnRegister) {\n\t\ttoSerialize[\"webauthn_register\"] = o.WebauthnRegister\n\t}\n\tif !IsNil(o.WebauthnRegisterDisplayname) {\n\t\ttoSerialize[\"webauthn_register_displayname\"] = o.WebauthnRegisterDisplayname\n\t}\n\tif !IsNil(o.WebauthnRemove) {\n\t\ttoSerialize[\"webauthn_remove\"] = o.WebauthnRemove\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateSettingsFlowWithWebAuthnMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateSettingsFlowWithWebAuthnMethod := _UpdateSettingsFlowWithWebAuthnMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateSettingsFlowWithWebAuthnMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateSettingsFlowWithWebAuthnMethod(varUpdateSettingsFlowWithWebAuthnMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"webauthn_register\")\n\t\tdelete(additionalProperties, \"webauthn_register_displayname\")\n\t\tdelete(additionalProperties, \"webauthn_remove\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateSettingsFlowWithWebAuthnMethod struct {\n\tvalue *UpdateSettingsFlowWithWebAuthnMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateSettingsFlowWithWebAuthnMethod) Get() *UpdateSettingsFlowWithWebAuthnMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateSettingsFlowWithWebAuthnMethod) Set(val *UpdateSettingsFlowWithWebAuthnMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateSettingsFlowWithWebAuthnMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateSettingsFlowWithWebAuthnMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateSettingsFlowWithWebAuthnMethod(val *UpdateSettingsFlowWithWebAuthnMethod) *NullableUpdateSettingsFlowWithWebAuthnMethod {\n\treturn &NullableUpdateSettingsFlowWithWebAuthnMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateSettingsFlowWithWebAuthnMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateSettingsFlowWithWebAuthnMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_verification_flow_body.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// UpdateVerificationFlowBody - Update Verification Flow Request Body\ntype UpdateVerificationFlowBody struct {\n\tUpdateVerificationFlowWithCodeMethod *UpdateVerificationFlowWithCodeMethod\n\tUpdateVerificationFlowWithLinkMethod *UpdateVerificationFlowWithLinkMethod\n}\n\n// UpdateVerificationFlowWithCodeMethodAsUpdateVerificationFlowBody is a convenience function that returns UpdateVerificationFlowWithCodeMethod wrapped in UpdateVerificationFlowBody\nfunc UpdateVerificationFlowWithCodeMethodAsUpdateVerificationFlowBody(v *UpdateVerificationFlowWithCodeMethod) UpdateVerificationFlowBody {\n\treturn UpdateVerificationFlowBody{\n\t\tUpdateVerificationFlowWithCodeMethod: v,\n\t}\n}\n\n// UpdateVerificationFlowWithLinkMethodAsUpdateVerificationFlowBody is a convenience function that returns UpdateVerificationFlowWithLinkMethod wrapped in UpdateVerificationFlowBody\nfunc UpdateVerificationFlowWithLinkMethodAsUpdateVerificationFlowBody(v *UpdateVerificationFlowWithLinkMethod) UpdateVerificationFlowBody {\n\treturn UpdateVerificationFlowBody{\n\t\tUpdateVerificationFlowWithLinkMethod: v,\n\t}\n}\n\n// Unmarshal JSON data into one of the pointers in the struct\nfunc (dst *UpdateVerificationFlowBody) UnmarshalJSON(data []byte) error {\n\tvar err error\n\t// use discriminator value to speed up the lookup\n\tvar jsonDict map[string]interface{}\n\terr = newStrictDecoder(data).Decode(&jsonDict)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON into map for the discriminator lookup\")\n\t}\n\n\t// check if the discriminator value is 'code'\n\tif jsonDict[\"method\"] == \"code\" {\n\t\t// try to unmarshal JSON data into UpdateVerificationFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateVerificationFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateVerificationFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateVerificationFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateVerificationFlowBody as UpdateVerificationFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'link'\n\tif jsonDict[\"method\"] == \"link\" {\n\t\t// try to unmarshal JSON data into UpdateVerificationFlowWithLinkMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateVerificationFlowWithLinkMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateVerificationFlowWithLinkMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateVerificationFlowWithLinkMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateVerificationFlowBody as UpdateVerificationFlowWithLinkMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateVerificationFlowWithCodeMethod'\n\tif jsonDict[\"method\"] == \"updateVerificationFlowWithCodeMethod\" {\n\t\t// try to unmarshal JSON data into UpdateVerificationFlowWithCodeMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateVerificationFlowWithCodeMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateVerificationFlowWithCodeMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateVerificationFlowWithCodeMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateVerificationFlowBody as UpdateVerificationFlowWithCodeMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\t// check if the discriminator value is 'updateVerificationFlowWithLinkMethod'\n\tif jsonDict[\"method\"] == \"updateVerificationFlowWithLinkMethod\" {\n\t\t// try to unmarshal JSON data into UpdateVerificationFlowWithLinkMethod\n\t\terr = json.Unmarshal(data, &dst.UpdateVerificationFlowWithLinkMethod)\n\t\tif err == nil {\n\t\t\treturn nil // data stored in dst.UpdateVerificationFlowWithLinkMethod, return on the first match\n\t\t} else {\n\t\t\tdst.UpdateVerificationFlowWithLinkMethod = nil\n\t\t\treturn fmt.Errorf(\"failed to unmarshal UpdateVerificationFlowBody as UpdateVerificationFlowWithLinkMethod: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Marshal data from the first non-nil pointers in the struct to JSON\nfunc (src UpdateVerificationFlowBody) MarshalJSON() ([]byte, error) {\n\tif src.UpdateVerificationFlowWithCodeMethod != nil {\n\t\treturn json.Marshal(&src.UpdateVerificationFlowWithCodeMethod)\n\t}\n\n\tif src.UpdateVerificationFlowWithLinkMethod != nil {\n\t\treturn json.Marshal(&src.UpdateVerificationFlowWithLinkMethod)\n\t}\n\n\treturn nil, nil // no data in oneOf schemas\n}\n\n// Get the actual instance\nfunc (obj *UpdateVerificationFlowBody) GetActualInstance() interface{} {\n\tif obj == nil {\n\t\treturn nil\n\t}\n\tif obj.UpdateVerificationFlowWithCodeMethod != nil {\n\t\treturn obj.UpdateVerificationFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateVerificationFlowWithLinkMethod != nil {\n\t\treturn obj.UpdateVerificationFlowWithLinkMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\n// Get the actual instance value\nfunc (obj UpdateVerificationFlowBody) GetActualInstanceValue() interface{} {\n\tif obj.UpdateVerificationFlowWithCodeMethod != nil {\n\t\treturn *obj.UpdateVerificationFlowWithCodeMethod\n\t}\n\n\tif obj.UpdateVerificationFlowWithLinkMethod != nil {\n\t\treturn *obj.UpdateVerificationFlowWithLinkMethod\n\t}\n\n\t// all schemas are nil\n\treturn nil\n}\n\ntype NullableUpdateVerificationFlowBody struct {\n\tvalue *UpdateVerificationFlowBody\n\tisSet bool\n}\n\nfunc (v NullableUpdateVerificationFlowBody) Get() *UpdateVerificationFlowBody {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateVerificationFlowBody) Set(val *UpdateVerificationFlowBody) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateVerificationFlowBody) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateVerificationFlowBody) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateVerificationFlowBody(val *UpdateVerificationFlowBody) *NullableUpdateVerificationFlowBody {\n\treturn &NullableUpdateVerificationFlowBody{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateVerificationFlowBody) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateVerificationFlowBody) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_verification_flow_with_code_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateVerificationFlowWithCodeMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateVerificationFlowWithCodeMethod{}\n\n// UpdateVerificationFlowWithCodeMethod struct for UpdateVerificationFlowWithCodeMethod\ntype UpdateVerificationFlowWithCodeMethod struct {\n\t// Code from the recovery email  If you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\n\tCode *string `json:\"code,omitempty\"`\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// The email address to verify  If the email belongs to a valid account, a verifiation email will be sent.  If you want to notify the email address if the account does not exist, see the [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation#attempted-verification-notifications)  If a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.  format: email\n\tEmail *string `json:\"email,omitempty\"`\n\t// Method is the method that should be used for this verification flow  Allowed values are `link` and `code`. link VerificationStrategyLink code VerificationStrategyCode\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateVerificationFlowWithCodeMethod UpdateVerificationFlowWithCodeMethod\n\n// NewUpdateVerificationFlowWithCodeMethod instantiates a new UpdateVerificationFlowWithCodeMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateVerificationFlowWithCodeMethod(method string) *UpdateVerificationFlowWithCodeMethod {\n\tthis := UpdateVerificationFlowWithCodeMethod{}\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateVerificationFlowWithCodeMethodWithDefaults instantiates a new UpdateVerificationFlowWithCodeMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateVerificationFlowWithCodeMethodWithDefaults() *UpdateVerificationFlowWithCodeMethod {\n\tthis := UpdateVerificationFlowWithCodeMethod{}\n\treturn &this\n}\n\n// GetCode returns the Code field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetCode() string {\n\tif o == nil || IsNil(o.Code) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Code\n}\n\n// GetCodeOk returns a tuple with the Code field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetCodeOk() (*string, bool) {\n\tif o == nil || IsNil(o.Code) {\n\t\treturn nil, false\n\t}\n\treturn o.Code, true\n}\n\n// HasCode returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) HasCode() bool {\n\tif o != nil && !IsNil(o.Code) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCode gets a reference to the given string and assigns it to the Code field.\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetCode(v string) {\n\to.Code = &v\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetEmail returns the Email field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetEmail() string {\n\tif o == nil || IsNil(o.Email) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Email\n}\n\n// GetEmailOk returns a tuple with the Email field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetEmailOk() (*string, bool) {\n\tif o == nil || IsNil(o.Email) {\n\t\treturn nil, false\n\t}\n\treturn o.Email, true\n}\n\n// HasEmail returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) HasEmail() bool {\n\tif o != nil && !IsNil(o.Email) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetEmail gets a reference to the given string and assigns it to the Email field.\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetEmail(v string) {\n\to.Email = &v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithCodeMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateVerificationFlowWithCodeMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateVerificationFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateVerificationFlowWithCodeMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Code) {\n\t\ttoSerialize[\"code\"] = o.Code\n\t}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\tif !IsNil(o.Email) {\n\t\ttoSerialize[\"email\"] = o.Email\n\t}\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateVerificationFlowWithCodeMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateVerificationFlowWithCodeMethod := _UpdateVerificationFlowWithCodeMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateVerificationFlowWithCodeMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateVerificationFlowWithCodeMethod(varUpdateVerificationFlowWithCodeMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"code\")\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"email\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateVerificationFlowWithCodeMethod struct {\n\tvalue *UpdateVerificationFlowWithCodeMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateVerificationFlowWithCodeMethod) Get() *UpdateVerificationFlowWithCodeMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateVerificationFlowWithCodeMethod) Set(val *UpdateVerificationFlowWithCodeMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateVerificationFlowWithCodeMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateVerificationFlowWithCodeMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateVerificationFlowWithCodeMethod(val *UpdateVerificationFlowWithCodeMethod) *NullableUpdateVerificationFlowWithCodeMethod {\n\treturn &NullableUpdateVerificationFlowWithCodeMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateVerificationFlowWithCodeMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateVerificationFlowWithCodeMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_update_verification_flow_with_link_method.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// checks if the UpdateVerificationFlowWithLinkMethod type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &UpdateVerificationFlowWithLinkMethod{}\n\n// UpdateVerificationFlowWithLinkMethod Update Verification Flow with Link Method\ntype UpdateVerificationFlowWithLinkMethod struct {\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCsrfToken *string `json:\"csrf_token,omitempty\"`\n\t// Email to Verify  Needs to be set when initiating the flow. If the email is a registered verification email, a verification link will be sent. If the email is not known, a email with details on what happened will be sent instead.  format: email\n\tEmail string `json:\"email\"`\n\t// Method is the method that should be used for this verification flow  Allowed values are `link` and `code` link VerificationStrategyLink code VerificationStrategyCode\n\tMethod string `json:\"method\"`\n\t// Transient data to pass along to any webhooks\n\tTransientPayload     map[string]interface{} `json:\"transient_payload,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _UpdateVerificationFlowWithLinkMethod UpdateVerificationFlowWithLinkMethod\n\n// NewUpdateVerificationFlowWithLinkMethod instantiates a new UpdateVerificationFlowWithLinkMethod object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewUpdateVerificationFlowWithLinkMethod(email string, method string) *UpdateVerificationFlowWithLinkMethod {\n\tthis := UpdateVerificationFlowWithLinkMethod{}\n\tthis.Email = email\n\tthis.Method = method\n\treturn &this\n}\n\n// NewUpdateVerificationFlowWithLinkMethodWithDefaults instantiates a new UpdateVerificationFlowWithLinkMethod object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewUpdateVerificationFlowWithLinkMethodWithDefaults() *UpdateVerificationFlowWithLinkMethod {\n\tthis := UpdateVerificationFlowWithLinkMethod{}\n\treturn &this\n}\n\n// GetCsrfToken returns the CsrfToken field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetCsrfToken() string {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.CsrfToken\n}\n\n// GetCsrfTokenOk returns a tuple with the CsrfToken field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetCsrfTokenOk() (*string, bool) {\n\tif o == nil || IsNil(o.CsrfToken) {\n\t\treturn nil, false\n\t}\n\treturn o.CsrfToken, true\n}\n\n// HasCsrfToken returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) HasCsrfToken() bool {\n\tif o != nil && !IsNil(o.CsrfToken) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCsrfToken gets a reference to the given string and assigns it to the CsrfToken field.\nfunc (o *UpdateVerificationFlowWithLinkMethod) SetCsrfToken(v string) {\n\to.CsrfToken = &v\n}\n\n// GetEmail returns the Email field value\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetEmail() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Email\n}\n\n// GetEmailOk returns a tuple with the Email field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetEmailOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Email, true\n}\n\n// SetEmail sets field value\nfunc (o *UpdateVerificationFlowWithLinkMethod) SetEmail(v string) {\n\to.Email = v\n}\n\n// GetMethod returns the Method field value\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetMethod() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Method\n}\n\n// GetMethodOk returns a tuple with the Method field value\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetMethodOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Method, true\n}\n\n// SetMethod sets field value\nfunc (o *UpdateVerificationFlowWithLinkMethod) SetMethod(v string) {\n\to.Method = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *UpdateVerificationFlowWithLinkMethod) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *UpdateVerificationFlowWithLinkMethod) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\nfunc (o UpdateVerificationFlowWithLinkMethod) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o UpdateVerificationFlowWithLinkMethod) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CsrfToken) {\n\t\ttoSerialize[\"csrf_token\"] = o.CsrfToken\n\t}\n\ttoSerialize[\"email\"] = o.Email\n\ttoSerialize[\"method\"] = o.Method\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *UpdateVerificationFlowWithLinkMethod) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"email\",\n\t\t\"method\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarUpdateVerificationFlowWithLinkMethod := _UpdateVerificationFlowWithLinkMethod{}\n\n\terr = json.Unmarshal(data, &varUpdateVerificationFlowWithLinkMethod)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = UpdateVerificationFlowWithLinkMethod(varUpdateVerificationFlowWithLinkMethod)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"csrf_token\")\n\t\tdelete(additionalProperties, \"email\")\n\t\tdelete(additionalProperties, \"method\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableUpdateVerificationFlowWithLinkMethod struct {\n\tvalue *UpdateVerificationFlowWithLinkMethod\n\tisSet bool\n}\n\nfunc (v NullableUpdateVerificationFlowWithLinkMethod) Get() *UpdateVerificationFlowWithLinkMethod {\n\treturn v.value\n}\n\nfunc (v *NullableUpdateVerificationFlowWithLinkMethod) Set(val *UpdateVerificationFlowWithLinkMethod) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableUpdateVerificationFlowWithLinkMethod) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableUpdateVerificationFlowWithLinkMethod) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableUpdateVerificationFlowWithLinkMethod(val *UpdateVerificationFlowWithLinkMethod) *NullableUpdateVerificationFlowWithLinkMethod {\n\treturn &NullableUpdateVerificationFlowWithLinkMethod{value: val, isSet: true}\n}\n\nfunc (v NullableUpdateVerificationFlowWithLinkMethod) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableUpdateVerificationFlowWithLinkMethod) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_verifiable_identity_address.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the VerifiableIdentityAddress type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &VerifiableIdentityAddress{}\n\n// VerifiableIdentityAddress VerifiableAddress is an identity's verifiable address\ntype VerifiableIdentityAddress struct {\n\t// When this entry was created\n\tCreatedAt *time.Time `json:\"created_at,omitempty\"`\n\t// The ID\n\tId *string `json:\"id,omitempty\"`\n\t// VerifiableAddressStatus must not exceed 16 characters as that is the limitation in the SQL Schema\n\tStatus string `json:\"status\"`\n\t// When this entry was last updated\n\tUpdatedAt *time.Time `json:\"updated_at,omitempty\"`\n\t// The address value  example foo@user.com\n\tValue string `json:\"value\"`\n\t// Indicates if the address has already been verified\n\tVerified   bool       `json:\"verified\"`\n\tVerifiedAt *time.Time `json:\"verified_at,omitempty\"`\n\t// The delivery method\n\tVia                  string `json:\"via\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _VerifiableIdentityAddress VerifiableIdentityAddress\n\n// NewVerifiableIdentityAddress instantiates a new VerifiableIdentityAddress object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewVerifiableIdentityAddress(status string, value string, verified bool, via string) *VerifiableIdentityAddress {\n\tthis := VerifiableIdentityAddress{}\n\tthis.Status = status\n\tthis.Value = value\n\tthis.Verified = verified\n\tthis.Via = via\n\treturn &this\n}\n\n// NewVerifiableIdentityAddressWithDefaults instantiates a new VerifiableIdentityAddress object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewVerifiableIdentityAddressWithDefaults() *VerifiableIdentityAddress {\n\tthis := VerifiableIdentityAddress{}\n\treturn &this\n}\n\n// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise.\nfunc (o *VerifiableIdentityAddress) GetCreatedAt() time.Time {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.CreatedAt\n}\n\n// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetCreatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.CreatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.CreatedAt, true\n}\n\n// HasCreatedAt returns a boolean if a field has been set.\nfunc (o *VerifiableIdentityAddress) HasCreatedAt() bool {\n\tif o != nil && !IsNil(o.CreatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field.\nfunc (o *VerifiableIdentityAddress) SetCreatedAt(v time.Time) {\n\to.CreatedAt = &v\n}\n\n// GetId returns the Id field value if set, zero value otherwise.\nfunc (o *VerifiableIdentityAddress) GetId() string {\n\tif o == nil || IsNil(o.Id) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetIdOk() (*string, bool) {\n\tif o == nil || IsNil(o.Id) {\n\t\treturn nil, false\n\t}\n\treturn o.Id, true\n}\n\n// HasId returns a boolean if a field has been set.\nfunc (o *VerifiableIdentityAddress) HasId() bool {\n\tif o != nil && !IsNil(o.Id) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetId gets a reference to the given string and assigns it to the Id field.\nfunc (o *VerifiableIdentityAddress) SetId(v string) {\n\to.Id = &v\n}\n\n// GetStatus returns the Status field value\nfunc (o *VerifiableIdentityAddress) GetStatus() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Status\n}\n\n// GetStatusOk returns a tuple with the Status field value\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetStatusOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Status, true\n}\n\n// SetStatus sets field value\nfunc (o *VerifiableIdentityAddress) SetStatus(v string) {\n\to.Status = v\n}\n\n// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise.\nfunc (o *VerifiableIdentityAddress) GetUpdatedAt() time.Time {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.UpdatedAt\n}\n\n// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetUpdatedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.UpdatedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.UpdatedAt, true\n}\n\n// HasUpdatedAt returns a boolean if a field has been set.\nfunc (o *VerifiableIdentityAddress) HasUpdatedAt() bool {\n\tif o != nil && !IsNil(o.UpdatedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field.\nfunc (o *VerifiableIdentityAddress) SetUpdatedAt(v time.Time) {\n\to.UpdatedAt = &v\n}\n\n// GetValue returns the Value field value\nfunc (o *VerifiableIdentityAddress) GetValue() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Value\n}\n\n// GetValueOk returns a tuple with the Value field value\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetValueOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Value, true\n}\n\n// SetValue sets field value\nfunc (o *VerifiableIdentityAddress) SetValue(v string) {\n\to.Value = v\n}\n\n// GetVerified returns the Verified field value\nfunc (o *VerifiableIdentityAddress) GetVerified() bool {\n\tif o == nil {\n\t\tvar ret bool\n\t\treturn ret\n\t}\n\n\treturn o.Verified\n}\n\n// GetVerifiedOk returns a tuple with the Verified field value\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetVerifiedOk() (*bool, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Verified, true\n}\n\n// SetVerified sets field value\nfunc (o *VerifiableIdentityAddress) SetVerified(v bool) {\n\to.Verified = v\n}\n\n// GetVerifiedAt returns the VerifiedAt field value if set, zero value otherwise.\nfunc (o *VerifiableIdentityAddress) GetVerifiedAt() time.Time {\n\tif o == nil || IsNil(o.VerifiedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.VerifiedAt\n}\n\n// GetVerifiedAtOk returns a tuple with the VerifiedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetVerifiedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.VerifiedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.VerifiedAt, true\n}\n\n// HasVerifiedAt returns a boolean if a field has been set.\nfunc (o *VerifiableIdentityAddress) HasVerifiedAt() bool {\n\tif o != nil && !IsNil(o.VerifiedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVerifiedAt gets a reference to the given time.Time and assigns it to the VerifiedAt field.\nfunc (o *VerifiableIdentityAddress) SetVerifiedAt(v time.Time) {\n\to.VerifiedAt = &v\n}\n\n// GetVia returns the Via field value\nfunc (o *VerifiableIdentityAddress) GetVia() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Via\n}\n\n// GetViaOk returns a tuple with the Via field value\n// and a boolean to check if the value has been set.\nfunc (o *VerifiableIdentityAddress) GetViaOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Via, true\n}\n\n// SetVia sets field value\nfunc (o *VerifiableIdentityAddress) SetVia(v string) {\n\to.Via = v\n}\n\nfunc (o VerifiableIdentityAddress) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o VerifiableIdentityAddress) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.CreatedAt) {\n\t\ttoSerialize[\"created_at\"] = o.CreatedAt\n\t}\n\tif !IsNil(o.Id) {\n\t\ttoSerialize[\"id\"] = o.Id\n\t}\n\ttoSerialize[\"status\"] = o.Status\n\tif !IsNil(o.UpdatedAt) {\n\t\ttoSerialize[\"updated_at\"] = o.UpdatedAt\n\t}\n\ttoSerialize[\"value\"] = o.Value\n\ttoSerialize[\"verified\"] = o.Verified\n\tif !IsNil(o.VerifiedAt) {\n\t\ttoSerialize[\"verified_at\"] = o.VerifiedAt\n\t}\n\ttoSerialize[\"via\"] = o.Via\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *VerifiableIdentityAddress) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"status\",\n\t\t\"value\",\n\t\t\"verified\",\n\t\t\"via\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarVerifiableIdentityAddress := _VerifiableIdentityAddress{}\n\n\terr = json.Unmarshal(data, &varVerifiableIdentityAddress)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = VerifiableIdentityAddress(varVerifiableIdentityAddress)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"created_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"status\")\n\t\tdelete(additionalProperties, \"updated_at\")\n\t\tdelete(additionalProperties, \"value\")\n\t\tdelete(additionalProperties, \"verified\")\n\t\tdelete(additionalProperties, \"verified_at\")\n\t\tdelete(additionalProperties, \"via\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableVerifiableIdentityAddress struct {\n\tvalue *VerifiableIdentityAddress\n\tisSet bool\n}\n\nfunc (v NullableVerifiableIdentityAddress) Get() *VerifiableIdentityAddress {\n\treturn v.value\n}\n\nfunc (v *NullableVerifiableIdentityAddress) Set(val *VerifiableIdentityAddress) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableVerifiableIdentityAddress) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableVerifiableIdentityAddress) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableVerifiableIdentityAddress(val *VerifiableIdentityAddress) *NullableVerifiableIdentityAddress {\n\treturn &NullableVerifiableIdentityAddress{value: val, isSet: true}\n}\n\nfunc (v NullableVerifiableIdentityAddress) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableVerifiableIdentityAddress) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_verification_flow.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n)\n\n// checks if the VerificationFlow type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &VerificationFlow{}\n\n// VerificationFlow Used to verify an out-of-band communication channel such as an email address or a phone number.  For more information head over to: https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation\ntype VerificationFlow struct {\n\t// Active, if set, contains the registration method that is being used. It is initially not set.\n\tActive *string `json:\"active,omitempty\"`\n\t// ExpiresAt is the time (UTC) when the request expires. If the user still wishes to verify the address, a new request has to be initiated.\n\tExpiresAt *time.Time `json:\"expires_at,omitempty\"`\n\t// ID represents the request's unique ID. When performing the verification flow, this represents the id in the verify ui's query parameter: http://<selfservice.flows.verification.ui_url>?request=<id>  type: string format: uuid\n\tId string `json:\"id\"`\n\t// IssuedAt is the time (UTC) when the request occurred.\n\tIssuedAt *time.Time `json:\"issued_at,omitempty\"`\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used to forward information contained in the URL's path or query for example.\n\tRequestUrl *string `json:\"request_url,omitempty\"`\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo *string `json:\"return_to,omitempty\"`\n\t// State represents the state of this request:  choose_method: ask the user to choose a method (e.g. verify your email) sent_email: the email has been sent to the user passed_challenge: the request was successful and the verification challenge was passed.\n\tState interface{} `json:\"state\"`\n\t// TransientPayload is used to pass data from the verification flow to hooks and email templates\n\tTransientPayload map[string]interface{} `json:\"transient_payload,omitempty\"`\n\t// The flow type can either be `api` or `browser`.\n\tType                 string      `json:\"type\"`\n\tUi                   UiContainer `json:\"ui\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _VerificationFlow VerificationFlow\n\n// NewVerificationFlow instantiates a new VerificationFlow object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewVerificationFlow(id string, state interface{}, type_ string, ui UiContainer) *VerificationFlow {\n\tthis := VerificationFlow{}\n\tthis.Id = id\n\tthis.State = state\n\tthis.Type = type_\n\tthis.Ui = ui\n\treturn &this\n}\n\n// NewVerificationFlowWithDefaults instantiates a new VerificationFlow object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewVerificationFlowWithDefaults() *VerificationFlow {\n\tthis := VerificationFlow{}\n\treturn &this\n}\n\n// GetActive returns the Active field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetActive() string {\n\tif o == nil || IsNil(o.Active) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Active\n}\n\n// GetActiveOk returns a tuple with the Active field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetActiveOk() (*string, bool) {\n\tif o == nil || IsNil(o.Active) {\n\t\treturn nil, false\n\t}\n\treturn o.Active, true\n}\n\n// HasActive returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasActive() bool {\n\tif o != nil && !IsNil(o.Active) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetActive gets a reference to the given string and assigns it to the Active field.\nfunc (o *VerificationFlow) SetActive(v string) {\n\to.Active = &v\n}\n\n// GetExpiresAt returns the ExpiresAt field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetExpiresAt() time.Time {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.ExpiresAt\n}\n\n// GetExpiresAtOk returns a tuple with the ExpiresAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetExpiresAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.ExpiresAt) {\n\t\treturn nil, false\n\t}\n\treturn o.ExpiresAt, true\n}\n\n// HasExpiresAt returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasExpiresAt() bool {\n\tif o != nil && !IsNil(o.ExpiresAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetExpiresAt gets a reference to the given time.Time and assigns it to the ExpiresAt field.\nfunc (o *VerificationFlow) SetExpiresAt(v time.Time) {\n\to.ExpiresAt = &v\n}\n\n// GetId returns the Id field value\nfunc (o *VerificationFlow) GetId() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Id\n}\n\n// GetIdOk returns a tuple with the Id field value\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetIdOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Id, true\n}\n\n// SetId sets field value\nfunc (o *VerificationFlow) SetId(v string) {\n\to.Id = v\n}\n\n// GetIssuedAt returns the IssuedAt field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetIssuedAt() time.Time {\n\tif o == nil || IsNil(o.IssuedAt) {\n\t\tvar ret time.Time\n\t\treturn ret\n\t}\n\treturn *o.IssuedAt\n}\n\n// GetIssuedAtOk returns a tuple with the IssuedAt field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetIssuedAtOk() (*time.Time, bool) {\n\tif o == nil || IsNil(o.IssuedAt) {\n\t\treturn nil, false\n\t}\n\treturn o.IssuedAt, true\n}\n\n// HasIssuedAt returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasIssuedAt() bool {\n\tif o != nil && !IsNil(o.IssuedAt) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetIssuedAt gets a reference to the given time.Time and assigns it to the IssuedAt field.\nfunc (o *VerificationFlow) SetIssuedAt(v time.Time) {\n\to.IssuedAt = &v\n}\n\n// GetRequestUrl returns the RequestUrl field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetRequestUrl() string {\n\tif o == nil || IsNil(o.RequestUrl) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.RequestUrl\n}\n\n// GetRequestUrlOk returns a tuple with the RequestUrl field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetRequestUrlOk() (*string, bool) {\n\tif o == nil || IsNil(o.RequestUrl) {\n\t\treturn nil, false\n\t}\n\treturn o.RequestUrl, true\n}\n\n// HasRequestUrl returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasRequestUrl() bool {\n\tif o != nil && !IsNil(o.RequestUrl) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetRequestUrl gets a reference to the given string and assigns it to the RequestUrl field.\nfunc (o *VerificationFlow) SetRequestUrl(v string) {\n\to.RequestUrl = &v\n}\n\n// GetReturnTo returns the ReturnTo field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetReturnTo() string {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.ReturnTo\n}\n\n// GetReturnToOk returns a tuple with the ReturnTo field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetReturnToOk() (*string, bool) {\n\tif o == nil || IsNil(o.ReturnTo) {\n\t\treturn nil, false\n\t}\n\treturn o.ReturnTo, true\n}\n\n// HasReturnTo returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasReturnTo() bool {\n\tif o != nil && !IsNil(o.ReturnTo) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetReturnTo gets a reference to the given string and assigns it to the ReturnTo field.\nfunc (o *VerificationFlow) SetReturnTo(v string) {\n\to.ReturnTo = &v\n}\n\n// GetState returns the State field value\n// If the value is explicit nil, the zero value for interface{} will be returned\nfunc (o *VerificationFlow) GetState() interface{} {\n\tif o == nil {\n\t\tvar ret interface{}\n\t\treturn ret\n\t}\n\n\treturn o.State\n}\n\n// GetStateOk returns a tuple with the State field value\n// and a boolean to check if the value has been set.\n// NOTE: If the value is an explicit nil, `nil, true` will be returned\nfunc (o *VerificationFlow) GetStateOk() (*interface{}, bool) {\n\tif o == nil || IsNil(o.State) {\n\t\treturn nil, false\n\t}\n\treturn &o.State, true\n}\n\n// SetState sets field value\nfunc (o *VerificationFlow) SetState(v interface{}) {\n\to.State = v\n}\n\n// GetTransientPayload returns the TransientPayload field value if set, zero value otherwise.\nfunc (o *VerificationFlow) GetTransientPayload() map[string]interface{} {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\tvar ret map[string]interface{}\n\t\treturn ret\n\t}\n\treturn o.TransientPayload\n}\n\n// GetTransientPayloadOk returns a tuple with the TransientPayload field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetTransientPayloadOk() (map[string]interface{}, bool) {\n\tif o == nil || IsNil(o.TransientPayload) {\n\t\treturn map[string]interface{}{}, false\n\t}\n\treturn o.TransientPayload, true\n}\n\n// HasTransientPayload returns a boolean if a field has been set.\nfunc (o *VerificationFlow) HasTransientPayload() bool {\n\tif o != nil && !IsNil(o.TransientPayload) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetTransientPayload gets a reference to the given map[string]interface{} and assigns it to the TransientPayload field.\nfunc (o *VerificationFlow) SetTransientPayload(v map[string]interface{}) {\n\to.TransientPayload = v\n}\n\n// GetType returns the Type field value\nfunc (o *VerificationFlow) GetType() string {\n\tif o == nil {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\n\treturn o.Type\n}\n\n// GetTypeOk returns a tuple with the Type field value\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetTypeOk() (*string, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Type, true\n}\n\n// SetType sets field value\nfunc (o *VerificationFlow) SetType(v string) {\n\to.Type = v\n}\n\n// GetUi returns the Ui field value\nfunc (o *VerificationFlow) GetUi() UiContainer {\n\tif o == nil {\n\t\tvar ret UiContainer\n\t\treturn ret\n\t}\n\n\treturn o.Ui\n}\n\n// GetUiOk returns a tuple with the Ui field value\n// and a boolean to check if the value has been set.\nfunc (o *VerificationFlow) GetUiOk() (*UiContainer, bool) {\n\tif o == nil {\n\t\treturn nil, false\n\t}\n\treturn &o.Ui, true\n}\n\n// SetUi sets field value\nfunc (o *VerificationFlow) SetUi(v UiContainer) {\n\to.Ui = v\n}\n\nfunc (o VerificationFlow) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o VerificationFlow) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Active) {\n\t\ttoSerialize[\"active\"] = o.Active\n\t}\n\tif !IsNil(o.ExpiresAt) {\n\t\ttoSerialize[\"expires_at\"] = o.ExpiresAt\n\t}\n\ttoSerialize[\"id\"] = o.Id\n\tif !IsNil(o.IssuedAt) {\n\t\ttoSerialize[\"issued_at\"] = o.IssuedAt\n\t}\n\tif !IsNil(o.RequestUrl) {\n\t\ttoSerialize[\"request_url\"] = o.RequestUrl\n\t}\n\tif !IsNil(o.ReturnTo) {\n\t\ttoSerialize[\"return_to\"] = o.ReturnTo\n\t}\n\tif o.State != nil {\n\t\ttoSerialize[\"state\"] = o.State\n\t}\n\tif !IsNil(o.TransientPayload) {\n\t\ttoSerialize[\"transient_payload\"] = o.TransientPayload\n\t}\n\ttoSerialize[\"type\"] = o.Type\n\ttoSerialize[\"ui\"] = o.Ui\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *VerificationFlow) UnmarshalJSON(data []byte) (err error) {\n\t// This validates that all required properties are included in the JSON object\n\t// by unmarshalling the object into a generic map with string keys and checking\n\t// that every required field exists as a key in the generic map.\n\trequiredProperties := []string{\n\t\t\"id\",\n\t\t\"state\",\n\t\t\"type\",\n\t\t\"ui\",\n\t}\n\n\tallProperties := make(map[string]interface{})\n\n\terr = json.Unmarshal(data, &allProperties)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, requiredProperty := range requiredProperties {\n\t\tif _, exists := allProperties[requiredProperty]; !exists {\n\t\t\treturn fmt.Errorf(\"no value given for required property %v\", requiredProperty)\n\t\t}\n\t}\n\n\tvarVerificationFlow := _VerificationFlow{}\n\n\terr = json.Unmarshal(data, &varVerificationFlow)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = VerificationFlow(varVerificationFlow)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"active\")\n\t\tdelete(additionalProperties, \"expires_at\")\n\t\tdelete(additionalProperties, \"id\")\n\t\tdelete(additionalProperties, \"issued_at\")\n\t\tdelete(additionalProperties, \"request_url\")\n\t\tdelete(additionalProperties, \"return_to\")\n\t\tdelete(additionalProperties, \"state\")\n\t\tdelete(additionalProperties, \"transient_payload\")\n\t\tdelete(additionalProperties, \"type\")\n\t\tdelete(additionalProperties, \"ui\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableVerificationFlow struct {\n\tvalue *VerificationFlow\n\tisSet bool\n}\n\nfunc (v NullableVerificationFlow) Get() *VerificationFlow {\n\treturn v.value\n}\n\nfunc (v *NullableVerificationFlow) Set(val *VerificationFlow) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableVerificationFlow) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableVerificationFlow) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableVerificationFlow(val *VerificationFlow) *NullableVerificationFlow {\n\treturn &NullableVerificationFlow{value: val, isSet: true}\n}\n\nfunc (v NullableVerificationFlow) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableVerificationFlow) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_verification_flow_state.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// VerificationFlowState The experimental state represents the state of a verification flow. This field is EXPERIMENTAL and subject to change!\ntype VerificationFlowState string\n\n// List of verificationFlowState\nconst (\n\tVERIFICATIONFLOWSTATE_CHOOSE_METHOD    VerificationFlowState = \"choose_method\"\n\tVERIFICATIONFLOWSTATE_SENT_EMAIL       VerificationFlowState = \"sent_email\"\n\tVERIFICATIONFLOWSTATE_PASSED_CHALLENGE VerificationFlowState = \"passed_challenge\"\n)\n\n// All allowed values of VerificationFlowState enum\nvar AllowedVerificationFlowStateEnumValues = []VerificationFlowState{\n\t\"choose_method\",\n\t\"sent_email\",\n\t\"passed_challenge\",\n}\n\nfunc (v *VerificationFlowState) UnmarshalJSON(src []byte) error {\n\tvar value string\n\terr := json.Unmarshal(src, &value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenumTypeValue := VerificationFlowState(value)\n\tfor _, existing := range AllowedVerificationFlowStateEnumValues {\n\t\tif existing == enumTypeValue {\n\t\t\t*v = enumTypeValue\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"%+v is not a valid VerificationFlowState\", value)\n}\n\n// NewVerificationFlowStateFromValue returns a pointer to a valid VerificationFlowState\n// for the value passed as argument, or an error if the value passed is not allowed by the enum\nfunc NewVerificationFlowStateFromValue(v string) (*VerificationFlowState, error) {\n\tev := VerificationFlowState(v)\n\tif ev.IsValid() {\n\t\treturn &ev, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid value '%v' for VerificationFlowState: valid values are %v\", v, AllowedVerificationFlowStateEnumValues)\n\t}\n}\n\n// IsValid return true if the value is valid for the enum, false otherwise\nfunc (v VerificationFlowState) IsValid() bool {\n\tfor _, existing := range AllowedVerificationFlowStateEnumValues {\n\t\tif existing == v {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Ptr returns reference to verificationFlowState value\nfunc (v VerificationFlowState) Ptr() *VerificationFlowState {\n\treturn &v\n}\n\ntype NullableVerificationFlowState struct {\n\tvalue *VerificationFlowState\n\tisSet bool\n}\n\nfunc (v NullableVerificationFlowState) Get() *VerificationFlowState {\n\treturn v.value\n}\n\nfunc (v *NullableVerificationFlowState) Set(val *VerificationFlowState) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableVerificationFlowState) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableVerificationFlowState) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableVerificationFlowState(val *VerificationFlowState) *NullableVerificationFlowState {\n\treturn &NullableVerificationFlowState{value: val, isSet: true}\n}\n\nfunc (v NullableVerificationFlowState) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableVerificationFlowState) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/model_version.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"encoding/json\"\n)\n\n// checks if the Version type satisfies the MappedNullable interface at compile time\nvar _ MappedNullable = &Version{}\n\n// Version struct for Version\ntype Version struct {\n\t// Version is the service's version.\n\tVersion              *string `json:\"version,omitempty\"`\n\tAdditionalProperties map[string]interface{}\n}\n\ntype _Version Version\n\n// NewVersion instantiates a new Version object\n// This constructor will assign default values to properties that have it defined,\n// and makes sure properties required by API are set, but the set of arguments\n// will change when the set of required properties is changed\nfunc NewVersion() *Version {\n\tthis := Version{}\n\treturn &this\n}\n\n// NewVersionWithDefaults instantiates a new Version object\n// This constructor will only assign default values to properties that have it defined,\n// but it doesn't guarantee that properties required by API are set\nfunc NewVersionWithDefaults() *Version {\n\tthis := Version{}\n\treturn &this\n}\n\n// GetVersion returns the Version field value if set, zero value otherwise.\nfunc (o *Version) GetVersion() string {\n\tif o == nil || IsNil(o.Version) {\n\t\tvar ret string\n\t\treturn ret\n\t}\n\treturn *o.Version\n}\n\n// GetVersionOk returns a tuple with the Version field value if set, nil otherwise\n// and a boolean to check if the value has been set.\nfunc (o *Version) GetVersionOk() (*string, bool) {\n\tif o == nil || IsNil(o.Version) {\n\t\treturn nil, false\n\t}\n\treturn o.Version, true\n}\n\n// HasVersion returns a boolean if a field has been set.\nfunc (o *Version) HasVersion() bool {\n\tif o != nil && !IsNil(o.Version) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetVersion gets a reference to the given string and assigns it to the Version field.\nfunc (o *Version) SetVersion(v string) {\n\to.Version = &v\n}\n\nfunc (o Version) MarshalJSON() ([]byte, error) {\n\ttoSerialize, err := o.ToMap()\n\tif err != nil {\n\t\treturn []byte{}, err\n\t}\n\treturn json.Marshal(toSerialize)\n}\n\nfunc (o Version) ToMap() (map[string]interface{}, error) {\n\ttoSerialize := map[string]interface{}{}\n\tif !IsNil(o.Version) {\n\t\ttoSerialize[\"version\"] = o.Version\n\t}\n\n\tfor key, value := range o.AdditionalProperties {\n\t\ttoSerialize[key] = value\n\t}\n\n\treturn toSerialize, nil\n}\n\nfunc (o *Version) UnmarshalJSON(data []byte) (err error) {\n\tvarVersion := _Version{}\n\n\terr = json.Unmarshal(data, &varVersion)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*o = Version(varVersion)\n\n\tadditionalProperties := make(map[string]interface{})\n\n\tif err = json.Unmarshal(data, &additionalProperties); err == nil {\n\t\tdelete(additionalProperties, \"version\")\n\t\to.AdditionalProperties = additionalProperties\n\t}\n\n\treturn err\n}\n\ntype NullableVersion struct {\n\tvalue *Version\n\tisSet bool\n}\n\nfunc (v NullableVersion) Get() *Version {\n\treturn v.value\n}\n\nfunc (v *NullableVersion) Set(val *Version) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableVersion) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableVersion) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableVersion(val *Version) *NullableVersion {\n\treturn &NullableVersion{value: val, isSet: true}\n}\n\nfunc (v NullableVersion) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableVersion) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n"
  },
  {
    "path": "pkg/httpclient/response.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"net/http\"\n)\n\n// APIResponse stores the API response returned by the server.\ntype APIResponse struct {\n\t*http.Response `json:\"-\"`\n\tMessage        string `json:\"message,omitempty\"`\n\t// Operation is the name of the OpenAPI operation.\n\tOperation string `json:\"operation,omitempty\"`\n\t// RequestURL is the request URL. This value is always available, even if the\n\t// embedded *http.Response is nil.\n\tRequestURL string `json:\"url,omitempty\"`\n\t// Method is the HTTP method used for the request.  This value is always\n\t// available, even if the embedded *http.Response is nil.\n\tMethod string `json:\"method,omitempty\"`\n\t// Payload holds the contents of the response body (which may be nil or empty).\n\t// This is provided here as the raw response.Body() reader will have already\n\t// been drained.\n\tPayload []byte `json:\"-\"`\n}\n\n// NewAPIResponse returns a new APIResponse object.\nfunc NewAPIResponse(r *http.Response) *APIResponse {\n\n\tresponse := &APIResponse{Response: r}\n\treturn response\n}\n\n// NewAPIResponseWithError returns a new APIResponse object with the provided error message.\nfunc NewAPIResponseWithError(errorMessage string) *APIResponse {\n\n\tresponse := &APIResponse{Message: errorMessage}\n\treturn response\n}\n"
  },
  {
    "path": "pkg/httpclient/utils.go",
    "content": "/*\nOry Identities API\n\nThis is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\n\nAPI version:\nContact: office@ory.sh\n*/\n\n// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"time\"\n)\n\n// PtrBool is a helper routine that returns a pointer to given boolean value.\nfunc PtrBool(v bool) *bool { return &v }\n\n// PtrInt is a helper routine that returns a pointer to given integer value.\nfunc PtrInt(v int) *int { return &v }\n\n// PtrInt32 is a helper routine that returns a pointer to given integer value.\nfunc PtrInt32(v int32) *int32 { return &v }\n\n// PtrInt64 is a helper routine that returns a pointer to given integer value.\nfunc PtrInt64(v int64) *int64 { return &v }\n\n// PtrFloat32 is a helper routine that returns a pointer to given float value.\nfunc PtrFloat32(v float32) *float32 { return &v }\n\n// PtrFloat64 is a helper routine that returns a pointer to given float value.\nfunc PtrFloat64(v float64) *float64 { return &v }\n\n// PtrString is a helper routine that returns a pointer to given string value.\nfunc PtrString(v string) *string { return &v }\n\n// PtrTime is helper routine that returns a pointer to given Time value.\nfunc PtrTime(v time.Time) *time.Time { return &v }\n\ntype NullableBool struct {\n\tvalue *bool\n\tisSet bool\n}\n\nfunc (v NullableBool) Get() *bool {\n\treturn v.value\n}\n\nfunc (v *NullableBool) Set(val *bool) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableBool) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableBool) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableBool(val *bool) *NullableBool {\n\treturn &NullableBool{value: val, isSet: true}\n}\n\nfunc (v NullableBool) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableBool) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableInt struct {\n\tvalue *int\n\tisSet bool\n}\n\nfunc (v NullableInt) Get() *int {\n\treturn v.value\n}\n\nfunc (v *NullableInt) Set(val *int) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableInt) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableInt) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableInt(val *int) *NullableInt {\n\treturn &NullableInt{value: val, isSet: true}\n}\n\nfunc (v NullableInt) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableInt) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableInt32 struct {\n\tvalue *int32\n\tisSet bool\n}\n\nfunc (v NullableInt32) Get() *int32 {\n\treturn v.value\n}\n\nfunc (v *NullableInt32) Set(val *int32) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableInt32) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableInt32) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableInt32(val *int32) *NullableInt32 {\n\treturn &NullableInt32{value: val, isSet: true}\n}\n\nfunc (v NullableInt32) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableInt32) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableInt64 struct {\n\tvalue *int64\n\tisSet bool\n}\n\nfunc (v NullableInt64) Get() *int64 {\n\treturn v.value\n}\n\nfunc (v *NullableInt64) Set(val *int64) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableInt64) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableInt64) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableInt64(val *int64) *NullableInt64 {\n\treturn &NullableInt64{value: val, isSet: true}\n}\n\nfunc (v NullableInt64) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableInt64) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableFloat32 struct {\n\tvalue *float32\n\tisSet bool\n}\n\nfunc (v NullableFloat32) Get() *float32 {\n\treturn v.value\n}\n\nfunc (v *NullableFloat32) Set(val *float32) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableFloat32) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableFloat32) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableFloat32(val *float32) *NullableFloat32 {\n\treturn &NullableFloat32{value: val, isSet: true}\n}\n\nfunc (v NullableFloat32) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableFloat32) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableFloat64 struct {\n\tvalue *float64\n\tisSet bool\n}\n\nfunc (v NullableFloat64) Get() *float64 {\n\treturn v.value\n}\n\nfunc (v *NullableFloat64) Set(val *float64) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableFloat64) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableFloat64) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableFloat64(val *float64) *NullableFloat64 {\n\treturn &NullableFloat64{value: val, isSet: true}\n}\n\nfunc (v NullableFloat64) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableFloat64) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableString struct {\n\tvalue *string\n\tisSet bool\n}\n\nfunc (v NullableString) Get() *string {\n\treturn v.value\n}\n\nfunc (v *NullableString) Set(val *string) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableString) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableString) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableString(val *string) *NullableString {\n\treturn &NullableString{value: val, isSet: true}\n}\n\nfunc (v NullableString) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableString) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\ntype NullableTime struct {\n\tvalue *time.Time\n\tisSet bool\n}\n\nfunc (v NullableTime) Get() *time.Time {\n\treturn v.value\n}\n\nfunc (v *NullableTime) Set(val *time.Time) {\n\tv.value = val\n\tv.isSet = true\n}\n\nfunc (v NullableTime) IsSet() bool {\n\treturn v.isSet\n}\n\nfunc (v *NullableTime) Unset() {\n\tv.value = nil\n\tv.isSet = false\n}\n\nfunc NewNullableTime(val *time.Time) *NullableTime {\n\treturn &NullableTime{value: val, isSet: true}\n}\n\nfunc (v NullableTime) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(v.value)\n}\n\nfunc (v *NullableTime) UnmarshalJSON(src []byte) error {\n\tv.isSet = true\n\treturn json.Unmarshal(src, &v.value)\n}\n\n// IsNil checks if an input is nil\nfunc IsNil(i interface{}) bool {\n\tif i == nil {\n\t\treturn true\n\t}\n\tswitch reflect.TypeOf(i).Kind() {\n\tcase reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:\n\t\treturn reflect.ValueOf(i).IsNil()\n\tcase reflect.Array:\n\t\treturn reflect.ValueOf(i).IsZero()\n\t}\n\treturn false\n}\n\ntype MappedNullable interface {\n\tToMap() (map[string]interface{}, error)\n}\n\n// A wrapper for strict JSON decoding\nfunc newStrictDecoder(data []byte) *json.Decoder {\n\tdec := json.NewDecoder(bytes.NewBuffer(data))\n\tdec.DisallowUnknownFields()\n\treturn dec\n}\n\n// Prevent trying to import \"fmt\"\nfunc reportError(format string, a ...interface{}) error {\n\treturn fmt.Errorf(format, a...)\n}\n"
  },
  {
    "path": "pkg/registrationhelpers/helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registrationhelpers\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"slices\"\n\t\"strings\"\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/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/ioutilx\"\n)\n\nfunc setupServer(t *testing.T, reg *driver.RegistryDefault) *httptest.Server {\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\n\tconf := reg.Config()\n\tconf.MustSet(t.Context(), config.ViperKeySelfServiceBrowserDefaultReturnTo, redirTS.URL+\"/default-return-to\")                                    //nolint:staticcheck\n\tconf.MustSet(t.Context(), config.ViperKeySelfServiceRegistrationAfter+\".\"+config.DefaultBrowserReturnURL, redirTS.URL+\"/registration-return-ts\") //nolint:staticcheck\n\treturn publicTS\n}\n\nfunc ExpectValidationError(ctx context.Context, t *testing.T, ts *httptest.Server, conf *config.Config, flow string, values func(url.Values)) string {\n\tisSPA := flow == \"spa\"\n\tisAPI := flow == \"api\"\n\treturn testhelpers.SubmitRegistrationFormCtx(ctx, t, isAPI, nil, ts, values,\n\t\tisSPA,\n\t\ttesthelpers.ExpectStatusCode(isAPI || isSPA, http.StatusBadRequest, http.StatusOK),\n\t\ttesthelpers.ExpectURL(isAPI || isSPA, ts.URL+registration.RouteSubmitFlow, conf.SelfServiceFlowRegistrationUI(ctx).String()))\n}\n\nfunc CheckFormContent(t *testing.T, body []byte, requiredFields ...string) {\n\tFieldNameSet(t, body, requiredFields)\n\tOutdatedFieldsDoNotExist(t, body)\n\tFormMethodIsPOST(t, body)\n}\n\n// FieldNameSet checks if the fields have the right \"name\" set.\nfunc FieldNameSet(t *testing.T, body []byte, fields []string) {\n\tfor _, f := range fields {\n\t\tassert.Equal(t, f, gjson.GetBytes(body, fmt.Sprintf(\"ui.nodes.#(attributes.name==%s).attributes.name\", f)).String(), \"%s\", body)\n\t}\n}\n\n// checks if some keys are not set, this should be used to catch regression issues\nfunc OutdatedFieldsDoNotExist(t *testing.T, body []byte) {\n\tfor _, k := range []string{\"request\"} {\n\t\tassert.Equal(t, false, gjson.GetBytes(body, fmt.Sprintf(\"ui.nodes.fields.#(name==%s)\", k)).Exists())\n\t}\n}\n\nfunc FormMethodIsPOST(t *testing.T, body []byte) {\n\tassert.Equal(t, \"POST\", gjson.GetBytes(body, \"ui.method\").String())\n}\n\n//go:embed stub/basic.schema.json\nvar basicSchema []byte\n\n//go:embed stub/multifield.schema.json\nvar multifieldSchema []byte\n\nvar skipIfNotEnabled = func(t *testing.T, flows []string, flow string) {\n\tif !slices.Contains(flows, flow) {\n\t\tt.Skipf(\"Skipping for %s flow because it was not included in the list of flows to be executed.\", flow)\n\t}\n}\n\nfunc AssertSchemaDoesNotExist(t *testing.T, reg *driver.RegistryDefault, flows []string, payload func(v url.Values)) {\n\tconf := reg.Config()\n\t_ = testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\tpublicTS := setupServer(t, reg)\n\tapiClient := testhelpers.NewDebugClient(t)\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\n\treset := func() {\n\t\ttesthelpers.SetDefaultIdentitySchemaFromRaw(conf, basicSchema)\n\t}\n\treset()\n\n\tt.Run(\"case=should fail because schema does not exist\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.Equal(t, int64(http.StatusInternalServerError), gjson.Get(actual, \"code\").Int(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"Internal Server Error\", gjson.Get(actual, \"status\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"reason\").String(), \"no such file or directory\", \"%s\", actual)\n\t\t}\n\n\t\tvalues := url.Values{\n\t\t\t\"traits.username\": {testhelpers.RandomEmail()},\n\t\t\t\"traits.foobar\":   {\"bar\"},\n\t\t\t\"csrf_token\":      {nosurfx.FakeCSRFToken},\n\t\t}\n\t\tpayload(values)\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tskipIfNotEnabled(t, flows, \"api\")\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaAPI(t, apiClient, publicTS)\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/i-do-not-exist.schema.json\")\n\t\t\tt.Cleanup(reset)\n\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, false, false, f, apiClient, values.Encode())\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL)\n\t\t\tcheck(t, gjson.Get(body, \"error\").Raw)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tskipIfNotEnabled(t, flows, \"spa\")\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, browserClient, publicTS, true, false, false)\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/i-do-not-exist.schema.json\")\n\t\t\tt.Cleanup(reset)\n\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, false, true, f, apiClient, values.Encode())\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL)\n\t\t\tcheck(t, gjson.Get(body, \"error\").Raw)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tskipIfNotEnabled(t, flows, \"browser\")\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, browserClient, publicTS, false, false, false)\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/i-do-not-exist.schema.json\")\n\t\t\tt.Cleanup(reset)\n\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, false, false, f, apiClient, values.Encode())\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tcheck(t, body)\n\t\t})\n\t})\n}\n\nfunc AssertCSRFFailures(t *testing.T, reg *driver.RegistryDefault, flows []string, payload func(v url.Values)) {\n\tconf := reg.Config()\n\ttesthelpers.SetDefaultIdentitySchemaFromRaw(conf, multifieldSchema)\n\t_ = testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\tpublicTS := setupServer(t, reg)\n\tapiClient := testhelpers.NewDebugClient(t)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tvalues := url.Values{\n\t\t\"csrf_token\":      {\"invalid_token\"},\n\t\t\"traits.username\": {testhelpers.RandomEmail()},\n\t\t\"traits.foobar\":   {\"bar\"},\n\t}\n\n\tpayload(values)\n\n\tt.Run(\"case=should fail because of missing CSRF token/type=browser\", func(t *testing.T) {\n\t\tskipIfNotEnabled(t, flows, \"browser\")\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\tbrowserClient.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, browserClient, publicTS, false, false, false)\n\n\t\tactual, res := testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, values.Encode())\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenServerTokenMismatch,\n\t\t\tjson.RawMessage(actual), \"%s\", actual)\n\t})\n\n\tt.Run(\"case=should fail because of missing CSRF token/type=spa\", func(t *testing.T) {\n\t\tskipIfNotEnabled(t, flows, \"spa\")\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\tbrowserClient.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, browserClient, publicTS, true, false, false)\n\n\t\tactual, res := testhelpers.RegistrationMakeRequest(t, false, true, f, browserClient, values.Encode())\n\t\tassert.EqualValues(t, http.StatusForbidden, res.StatusCode)\n\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch,\n\t\t\tjson.RawMessage(gjson.Get(actual, \"error\").Raw), \"%s\", actual)\n\t})\n\n\tt.Run(\"case=should pass even without CSRF token/type=api\", func(t *testing.T) {\n\t\tskipIfNotEnabled(t, flows, \"api\")\n\n\t\tf := testhelpers.InitializeRegistrationFlowViaAPI(t, apiClient, publicTS)\n\n\t\tactual, res := testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.NotEmpty(t, gjson.Get(actual, \"identity.id\").Raw, \"%s\", actual) // registration successful\n\t})\n\n\tt.Run(\"case=should fail with correct CSRF error cause/type=api\", func(t *testing.T) {\n\t\tskipIfNotEnabled(t, flows, \"api\")\n\n\t\tfor k, tc := range []struct {\n\t\t\tmod func(http.Header)\n\t\t\texp string\n\t\t}{\n\t\t\t{\n\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\th.Add(\"Cookie\", \"name=bar\")\n\t\t\t\t},\n\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Cookie\\\\\\\" key\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\th.Add(\"Origin\", \"www.bar.com\")\n\t\t\t\t},\n\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Origin\\\\\\\" key\",\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaAPI(t, apiClient, publicTS)\n\t\t\t\tc := f.Ui\n\n\t\t\t\treq := testhelpers.NewPostRequest(t, true, c.Action, bytes.NewBufferString(testhelpers.EncodeFormAsJSON(t, true, values)))\n\t\t\t\ttc.mod(req.Header)\n\n\t\t\t\tres, err := apiClient.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tactual := string(ioutilx.MustReadAll(res.Body))\n\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\tassert.Contains(t, actual, tc.exp)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc AssertRegistrationRespectsValidation(t *testing.T, reg *driver.RegistryDefault, flows []string, payload func(url.Values)) {\n\tconf := reg.Config()\n\ttesthelpers.SetDefaultIdentitySchemaFromRaw(conf, multifieldSchema)\n\t_ = testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\tpublicTS := setupServer(t, reg)\n\n\tt.Run(\"case=should return an error because not passing validation\", func(t *testing.T) {\n\t\temail := testhelpers.RandomEmail()\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\tCheckFormContent(t, []byte(actual), \"password\", \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0\").String(), `Property foobar is missing`, \"%s\", actual)\n\t\t\tassert.Equal(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", actual)\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"traits.username\", email)\n\t\t\tv.Del(\"traits.foobar\")\n\t\t\tpayload(v)\n\t\t}\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tcheck(t, ExpectValidationError(t.Context(), t, publicTS, conf, f, values))\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc AssertCommonErrorCases(t *testing.T, flows []string) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeySelfServiceRegistrationEnableLegacyOneStep, true),\n\t\tconfigx.WithValues(testhelpers.DefaultRawIdentitySchemaConfig(basicSchema)),\n\t)\n\tuiTS := testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\tpublicTS := setupServer(t, reg)\n\tapiClient := testhelpers.NewDebugClient(t)\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\n\tt.Run(\"description=can call endpoints only without session\", func(t *testing.T) {\n\t\tvalues := url.Values{}\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tres, err := testhelpers.NewHTTPClientWithArbitrarySessionCookie(t.Context(), t, reg).\n\t\t\t\tDo(httpx.MustNewRequest(\"POST\", publicTS.URL+registration.RouteSubmitFlow, strings.NewReader(values.Encode()), \"application/x-www-form-urlencoded\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%+v\", res.Request)\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.GetProvider(t.Context()).String(config.ViperKeySelfServiceBrowserDefaultReturnTo))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tres, err := testhelpers.NewHTTPClientWithArbitrarySessionToken(t.Context(), t, reg).\n\t\t\t\tDo(httpx.MustNewRequest(\"POST\", publicTS.URL+registration.RouteSubmitFlow, strings.NewReader(testhelpers.EncodeFormAsJSON(t, true, values)), \"application/json\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Len(t, res.Cookies(), 0)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassertx.EqualAsJSON(t, registration.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(ioutilx.MustReadAll(res.Body), \"error\").Raw))\n\t\t})\n\t})\n\n\tt.Run(\"case=should show the error ui because the request payload is malformed\", func(t *testing.T) {\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaAPICtx(t.Context(), t, apiClient, publicTS)\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, body, `Expected JSON sent in request body to be an object but got: Number`)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, apiClient, publicTS, true, false, false)\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, body, `Expected JSON sent in request body to be an object but got: Number`)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, false)\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/registration-ts\")\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"invalid URL escape\", \"%s\", body)\n\t\t\tassert.Equal(t, \"email\", gjson.Get(body, \"ui.nodes.#(attributes.name==\\\"traits.email\\\").attributes.type\").String(), \"%s\", body)\n\t\t})\n\t})\n\tt.Run(\"description=can call endpoints only without session\", func(t *testing.T) {\n\t\tvalues := url.Values{}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tres, err := testhelpers.NewHTTPClientWithArbitrarySessionCookie(t.Context(), t, reg).\n\t\t\t\tDo(httpx.MustNewRequest(\"POST\", publicTS.URL+registration.RouteSubmitFlow, strings.NewReader(values.Encode()), \"application/x-www-form-urlencoded\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%+v\", res.Request)\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.GetProvider(t.Context()).String(config.ViperKeySelfServiceBrowserDefaultReturnTo))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tres, err := testhelpers.NewHTTPClientWithArbitrarySessionToken(t.Context(), t, reg).\n\t\t\t\tDo(httpx.MustNewRequest(\"POST\", publicTS.URL+registration.RouteSubmitFlow, strings.NewReader(testhelpers.EncodeFormAsJSON(t, true, values)), \"application/json\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Len(t, res.Cookies(), 0)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassertx.EqualAsJSON(t, registration.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(ioutilx.MustReadAll(res.Body), \"error\").Raw))\n\t\t})\n\t})\n\n\tt.Run(\"case=should show the error ui because the request payload is malformed\", func(t *testing.T) {\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaAPICtx(t.Context(), t, apiClient, publicTS)\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, body, `Expected JSON sent in request body to be an object but got: Number`)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, apiClient, publicTS, true, false, false)\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, body, `Expected JSON sent in request body to be an object but got: Number`)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, false)\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/registration-ts\")\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"invalid URL escape\", \"%s\", body)\n\t\t\tassert.Equal(t, \"email\", gjson.Get(body, \"ui.nodes.#(attributes.name==\\\"traits.email\\\").attributes.type\").String(), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=should show the error ui because the method is missing in payload\", func(t *testing.T) {\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaAPICtx(t.Context(), t, apiClient, publicTS)\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, \"{}}\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"Could not find a strategy to sign you up with. Did you fill out the form correctly?\", \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, true, false, false)\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, false, true, f, browserClient, \"{}}\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"Could not find a strategy to sign you up with. Did you fill out the form correctly?\", \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, false)\n\t\t\tbody, res := testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, \"foo=bar\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/registration-ts\")\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"Could not find a strategy to sign you up with. Did you fill out the form correctly?\", \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=should show the error ui because the request id is missing\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.Equal(t, int64(http.StatusNotFound), gjson.Get(actual, \"code\").Int(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"Not Found\", gjson.Get(actual, \"status\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"message\").String(), \"Unable to locate the resource\", \"%s\", actual)\n\t\t}\n\n\t\tfakeFlow := &kratos.RegistrationFlow{Ui: kratos.UiContainer{\n\t\t\tAction: publicTS.URL + registration.RouteSubmitFlow + \"?flow=\" + x.NewUUID().String(),\n\t\t}}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual, res := testhelpers.RegistrationMakeRequest(t, true, false, fakeFlow, apiClient, \"{}\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tcheck(t, gjson.Get(actual, \"error\").Raw)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tactual, res := testhelpers.RegistrationMakeRequest(t, false, true, fakeFlow, browserClient, \"{}\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tcheck(t, gjson.Get(actual, \"error\").Raw)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tactual, res := testhelpers.RegistrationMakeRequest(t, false, false, fakeFlow, browserClient, \"\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tcheck(t, actual)\n\t\t})\n\t})\n\n\tt.Run(\"case=should return an error because the request is expired\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceRegistrationRequestLifespan, \"500ms\")\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceRegistrationRequestLifespan, \"10m\")\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaAPICtx(t.Context(), t, apiClient, publicTS)\n\n\t\t\ttime.Sleep(600 * time.Millisecond)\n\t\t\tactual, res := testhelpers.RegistrationMakeRequestCtx(t.Context(), t, true, false, f, apiClient, \"{}\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.Get(actual, \"use_flow_id\").String())\n\t\t\tassertx.EqualAsJSONExcept(t, flow.NewFlowExpiredError(time.Now()), json.RawMessage(actual), []string{\"use_flow_id\", \"expired_at\", \"since\"}, \"\")\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, true, false, false)\n\n\t\t\ttime.Sleep(600 * time.Millisecond)\n\t\t\tactual, res := testhelpers.RegistrationMakeRequest(t, false, true, f, browserClient, \"{}\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.Get(actual, \"use_flow_id\").String())\n\t\t\tassertx.EqualAsJSONExcept(t, flow.NewFlowExpiredError(time.Now()), json.RawMessage(actual), []string{\"use_flow_id\", \"expired_at\", \"since\"}, \"expired\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, false)\n\n\t\t\ttime.Sleep(600 * time.Millisecond)\n\t\t\tactual, res := testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, \"\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/registration-ts\")\n\t\t\tassert.NotEqual(t, f.Id, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.messages.0.text\").String(), \"expired\", \"%s\", actual)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail because the password was used in databreaches\", func(t *testing.T) {\n\t\ttesthelpers.SetDefaultIdentitySchemaFromRaw(conf, multifieldSchema)\n\t\tt.Cleanup(func() {\n\t\t\ttesthelpers.SetDefaultIdentitySchemaFromRaw(conf, basicSchema)\n\t\t})\n\n\t\temail := testhelpers.RandomEmail()\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\tCheckFormContent(t, []byte(actual), \"password\", \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==password).messages.0\").String(), \"data breaches and must no longer be used.\", \"%s\", actual)\n\n\t\t\t// but the method is still set\n\t\t\tassert.Equal(t, \"password\", gjson.Get(actual, \"ui.nodes.#(attributes.name==method).attributes.value\").String(), \"%s\", actual)\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"traits.username\", email)\n\t\t\tv.Set(\"password\", \"password\")\n\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t}\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tcheck(t, ExpectValidationError(t.Context(), t, publicTS, conf, f, values))\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pkg/registrationhelpers/stub/basic.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "pkg/registrationhelpers/stub/multifield.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"foobar\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        },\n        \"username\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"foobar\",\n        \"username\"\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "pkg/settingshelpers/helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settingshelpers\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n)\n\nfunc ExpectValidationError(t *testing.T, ts *httptest.Server, hc *http.Client, conf *config.Config, flow string, values func(url.Values)) string {\n\tisSPA := flow == \"spa\"\n\tisAPI := flow == \"api\"\n\tctx := context.Background()\n\treturn testhelpers.SubmitSettingsForm(t, isAPI, isSPA, hc, ts, values,\n\t\ttesthelpers.ExpectStatusCode(isAPI || isSPA, http.StatusBadRequest, http.StatusOK),\n\t\ttesthelpers.ExpectURL(isAPI || isSPA, ts.URL+settings.RouteSubmitFlow, conf.SelfServiceFlowSettingsUI(ctx).String()))\n}\n"
  },
  {
    "path": "pkg/testhelpers/config.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/randx\"\n)\n\nfunc DefaultIdentitySchemaConfig(url string) map[string]any {\n\treturn map[string]any{\n\t\tconfig.ViperKeyDefaultIdentitySchemaID: \"default\",\n\t\tconfig.ViperKeyIdentitySchemas: config.Schemas{\n\t\t\t{ID: \"default\", URL: url},\n\t\t},\n\t}\n}\n\nfunc DefaultRawIdentitySchemaConfig(schema []byte) map[string]any {\n\treturn DefaultIdentitySchemaConfig(\"base64://\" + base64.URLEncoding.EncodeToString(schema))\n}\n\nfunc WithDefaultIdentitySchema(ctx context.Context, url string) context.Context {\n\treturn contextx.WithConfigValues(ctx, DefaultIdentitySchemaConfig(url))\n}\n\n// Deprecated: Use context-based WithDefaultIdentitySchema instead\nfunc SetDefaultIdentitySchema(conf *config.Config, url string) func() {\n\tschemaUrl, _ := conf.DefaultIdentityTraitsSchemaURL(context.Background())\n\tconf.MustSet(context.Background(), config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\tconf.MustSet(context.Background(), config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t{ID: \"default\", URL: url},\n\t})\n\treturn func() {\n\t\tconf.MustSet(context.Background(), config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t{ID: \"default\", URL: schemaUrl.String()},\n\t\t})\n\t}\n}\n\n// WithAddIdentitySchema registers an identity schema in the config with a random ID and returns the ID\nfunc WithAddIdentitySchema(ctx context.Context, t *testing.T, conf *config.Config, url string) (context.Context, string) {\n\tid := randx.MustString(16, randx.Alpha)\n\tschemas, err := conf.IdentityTraitsSchemas(ctx)\n\trequire.NoError(t, err)\n\n\treturn contextx.WithConfigValue(ctx, config.ViperKeyIdentitySchemas, append(schemas, config.Schema{\n\t\tID:  id,\n\t\tURL: url,\n\t})), id\n}\n\n// UseIdentitySchema registers an identity schema in the config with a random ID and returns the ID\n//\n// It also registers a test cleanup function, to reset the schemas to the original values, after the test finishes\n// Deprecated: Use context-based WithAddIdentitySchema instead\nfunc UseIdentitySchema(t *testing.T, conf *config.Config, url string) (id string) {\n\tid = randx.MustString(16, randx.Alpha)\n\tschemas, err := conf.IdentityTraitsSchemas(context.Background())\n\trequire.NoError(t, err)\n\n\tconf.MustSet(context.Background(), config.ViperKeyIdentitySchemas, append(schemas, config.Schema{\n\t\tID:  id,\n\t\tURL: url,\n\t}))\n\tt.Cleanup(func() {\n\t\tconf.MustSet(context.Background(), config.ViperKeyIdentitySchemas, schemas)\n\t})\n\treturn id\n}\n\n// WithDefaultIdentitySchemaFromRaw allows setting the default identity schema from a raw JSON string.\nfunc WithDefaultIdentitySchemaFromRaw(ctx context.Context, schema []byte) context.Context {\n\treturn WithDefaultIdentitySchema(ctx, \"base64://\"+base64.URLEncoding.EncodeToString(schema))\n}\n\n// Deprecated: Use context-based WithDefaultIdentitySchemaFromRaw instead\nfunc SetDefaultIdentitySchemaFromRaw(conf *config.Config, schema []byte) {\n\tconf.MustSet(context.Background(), config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\tconf.MustSet(context.Background(), config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t{ID: \"default\", URL: \"base64://\" + base64.URLEncoding.EncodeToString(schema)},\n\t})\n}\n"
  },
  {
    "path": "pkg/testhelpers/courier.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"context\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier\"\n\tkeysetpagination \"github.com/ory/x/pagination/keysetpagination_v2\"\n)\n\nfunc CourierExpectMessage(ctx context.Context, t *testing.T, reg interface {\n\tcourier.PersistenceProvider\n}, recipient, subject string,\n) *courier.Message {\n\tt.Helper()\n\tmessages, _, err := reg.CourierPersister().ListMessages(ctx, courier.ListCourierMessagesParameters{\n\t\tRecipient: recipient,\n\t}, []keysetpagination.Option{})\n\trequire.NoError(t, err)\n\n\tsort.Slice(messages, func(i, j int) bool {\n\t\treturn messages[i].CreatedAt.After(messages[j].CreatedAt)\n\t})\n\n\tfor _, m := range messages {\n\t\tif strings.EqualFold(m.Recipient, recipient) && (strings.Contains(m.Subject, subject) || strings.Contains(m.Body, subject)) {\n\t\t\treturn &m\n\t\t}\n\t}\n\n\trequire.Failf(t, \"could not find courier messages\", \"could not find courier messages with recipient %s and subject %s\", recipient, subject)\n\treturn nil\n}\n\nfunc CourierExpectLinkInMessage(t *testing.T, message *courier.Message, offset int) string {\n\tif offset == 0 {\n\t\toffset = 1\n\t}\n\tmatch := regexp.MustCompile(`(http[^\\s]+)`).FindStringSubmatch(message.Body)\n\trequire.Len(t, match, offset*2)\n\n\treturn match[offset]\n}\n\nfunc CourierExpectCodeInMessage(t *testing.T, message *courier.Message, offset int) string {\n\tif offset == 0 {\n\t\toffset = 1\n\t}\n\tmatch := regexp.MustCompile(CodeRegex).FindStringSubmatch(message.Body)\n\trequire.Len(t, match, offset*2)\n\n\treturn match[offset]\n}\n"
  },
  {
    "path": "pkg/testhelpers/e2e_server.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/ory/x/tlsx\"\n\n\t\"github.com/phayes/freeport\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/cmd\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/cmdx\"\n\t\"github.com/ory/x/configx\"\n)\n\ntype ConfigOptions = map[string]interface{}\n\nfunc StartE2EServerOnly(t *testing.T, configFile string, isTLS bool, configOptions ConfigOptions) (publicPort, adminPort int) {\n\treturn startE2EServerOnly(t, configFile, isTLS, configOptions, 0)\n}\n\nfunc startE2EServerOnly(t *testing.T, configFile string, isTLS bool, configOptions ConfigOptions, tries int) (publicPort, adminPort int) {\n\tadminPort, err := freeport.GetFreePort()\n\trequire.NoError(t, err)\n\n\tpublicPort, err = freeport.GetFreePort()\n\trequire.NoError(t, err)\n\n\tpublicUrl := fmt.Sprintf(\"http://127.0.0.1:%d\", publicPort)\n\tadminUrl := fmt.Sprintf(\"http://127.0.0.1:%d\", adminPort)\n\n\tif isTLS {\n\t\tpublicUrl = fmt.Sprintf(\"https://127.0.0.1:%d\", publicPort)\n\t\tadminUrl = fmt.Sprintf(\"https://127.0.0.1:%d\", adminPort)\n\t}\n\n\tdsn := \"sqlite://\" + filepath.Join(t.TempDir(), \"db.sqlite\") + \"?_fk=true&mode=rwc\"\n\n\tctx := t.Context()\n\tdefaultConfig := map[string]any{\n\t\t\"dsn\":                       dsn,\n\t\t\"dev\":                       true,\n\t\t\"log.level\":                 \"error\",\n\t\t\"log.leak_sensitive_values\": true,\n\t\t\"serve.public.port\":         publicPort,\n\t\t\"serve.admin.port\":          adminPort,\n\t\t\"serve.public.base_url\":     publicUrl,\n\t\t\"serve.admin.base_url\":      adminUrl,\n\t}\n\n\tjsonnetPool := jsonnetsecure.NewProcessPool(runtime.GOMAXPROCS(0))\n\tt.Cleanup(jsonnetPool.Close)\n\n\t//nolint:staticcheck\n\t//lint:ignore SA1029 we really want this\n\tctx, cancel := context.WithCancel(ctx)\n\texecutor := &cmdx.CommandExecuter{\n\t\tNew: func() *cobra.Command {\n\t\t\treturn cmd.NewRootCmd(driver.WithJsonnetPool(jsonnetPool), driver.WithConfigOptions(configx.WithValues(defaultConfig), configx.WithValues(configOptions)))\n\t\t},\n\t\tCtx: ctx,\n\t}\n\n\tt.Log(\"Starting migrations...\")\n\t_ = executor.ExecNoErr(t, \"migrate\", \"sql\", dsn, \"--yes\")\n\tt.Logf(\"Migration done\")\n\n\tt.Log(\"Starting server...\")\n\tstdOut, stdErr := &bytes.Buffer{}, &bytes.Buffer{}\n\teg := executor.ExecBackground(nil, io.MultiWriter(os.Stdout, stdOut), io.MultiWriter(os.Stdout, stdErr), \"serve\", \"--config\", configFile, \"--watch-courier\")\n\n\terr = waitTimeout(t, eg, time.Second)\n\tif err != nil && tries < 5 {\n\t\tif !errors.Is(err, context.Canceled) || strings.Contains(err.Error(), \"address already in use\") || strings.Contains(stdErr.String(), \"address already in use\") {\n\t\t\tt.Logf(\"Detected an instance with port reuse, retrying #%d...\", tries)\n\t\t\ttime.Sleep(time.Millisecond * 500)\n\t\t\tcancel()\n\t\t\treturn startE2EServerOnly(t, configFile, isTLS, configOptions, tries+1)\n\t\t}\n\t}\n\trequire.NoError(t, err)\n\n\tt.Cleanup(cancel)\n\treturn publicPort, adminPort\n}\n\n// waitTimeout waits for the waitgroup for the specified max timeout.\n// Returns true if waiting timed out.\nfunc waitTimeout(t *testing.T, wg *errgroup.Group, timeout time.Duration) (err error) {\n\tc := make(chan struct{})\n\tgo func() {\n\t\tdefer close(c)\n\t\terr = wg.Wait()\n\t}()\n\tselect {\n\tcase <-c:\n\t\treturn\n\tcase <-time.After(timeout):\n\t\treturn\n\t}\n}\n\nfunc StartE2EServer(t *testing.T, configFile string, configOptions ConfigOptions) (publicUrl, adminUrl string) {\n\tpublicPort, adminPort := StartE2EServerOnly(t, configFile, false, configOptions)\n\treturn CheckE2EServerOnHTTP(t, publicPort, adminPort)\n}\n\nfunc CheckE2EServerOnHTTP(t *testing.T, publicPort, adminPort int) (publicUrl, adminUrl string) {\n\tpublicUrl = fmt.Sprintf(\"http://127.0.0.1:%d\", publicPort)\n\tadminUrl = fmt.Sprintf(\"http://127.0.0.1:%d\", adminPort)\n\twaitToComeAlive(t, publicUrl, adminUrl)\n\treturn\n}\n\nfunc waitToComeAlive(t *testing.T, publicUrl, adminUrl string) {\n\trequire.EventuallyWithT(t, func(t *assert.CollectT) {\n\t\t//#nosec G402 -- TLS InsecureSkipVerify set true\n\t\ttr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}\n\t\tclient := &http.Client{Transport: tr}\n\n\t\tfor _, url := range []string{\n\t\t\tpublicUrl + \"/health/ready\",\n\t\t\tadminUrl + \"/health/ready\",\n\t\t\tpublicUrl + \"/health/alive\",\n\t\t\tadminUrl + \"/health/alive\",\n\t\t} {\n\t\t\tres, err := client.Get(url)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tbody := x.MustReadAll(res.Body)\n\t\t\t_ = res.Body.Close()\n\n\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t}\n\t}, 10*time.Second, time.Second)\n}\n\nfunc CheckE2EServerOnHTTPS(t *testing.T, publicPort, adminPort int) (publicUrl, adminUrl string) {\n\tpublicUrl = fmt.Sprintf(\"https://127.0.0.1:%d\", publicPort)\n\tadminUrl = fmt.Sprintf(\"https://127.0.0.1:%d\", adminPort)\n\twaitToComeAlive(t, publicUrl, adminUrl)\n\treturn\n}\n\n// GenerateTLSCertificateFilesForTests writes a new, self-signed TLS\n// certificate+key (in PEM format) to a temporary location on disk and returns\n// the paths to both, as well as the respective contents in base64 encoding. The\n// files are automatically cleaned up when the given *testing.T concludes its\n// tests.\nfunc GenerateTLSCertificateFilesForTests(t *testing.T) (certPath, keyPath, certBase64, keyBase64 string) {\n\ttmpDir := t.TempDir()\n\n\tprivateKey, err := rsa.GenerateKey(rand.Reader, 2048)\n\trequire.NoError(t, err)\n\n\tcert, err := tlsx.CreateSelfSignedCertificate(privateKey)\n\trequire.NoError(t, err)\n\n\t// write cert\n\tcertFile, err := os.CreateTemp(tmpDir, \"test-*-cert.pem\")\n\trequire.NoError(t, err, \"Failed to create temp file for certificate: %v\", err)\n\tcertPath = certFile.Name()\n\n\tvar buf bytes.Buffer\n\tenc := base64.NewEncoder(base64.StdEncoding, &buf)\n\tcertOut := io.MultiWriter(enc, certFile)\n\terr = pem.Encode(certOut, &pem.Block{Type: \"CERTIFICATE\", Bytes: cert.Raw})\n\trequire.NoError(t, err, \"Failed to write data to %q: %v\", certPath, err)\n\terr = enc.Close()\n\trequire.NoError(t, err, \"Error closing base64 encoder\")\n\terr = certFile.Close()\n\trequire.NoError(t, err, \"Error closing %q: %v\", certPath, err)\n\tcertBase64 = buf.String()\n\tt.Log(\"wrote\", certPath)\n\n\t// write key\n\tkeyFile, err := os.CreateTemp(tmpDir, \"test-*-key.pem\")\n\trequire.NoError(t, err, \"Failed to create temp file for key: %v\", err)\n\tkeyPath = keyFile.Name()\n\tbuf.Reset()\n\tenc = base64.NewEncoder(base64.StdEncoding, &buf)\n\tkeyOut := io.MultiWriter(enc, keyFile)\n\n\tprivBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)\n\trequire.NoError(t, err, \"Failed to marshal private key: %v\", err)\n\n\terr = pem.Encode(keyOut, &pem.Block{Type: \"PRIVATE KEY\", Bytes: privBytes})\n\trequire.NoError(t, err, \"Failed to write data to %q: %v\", keyPath, err)\n\terr = enc.Close()\n\trequire.NoError(t, err, \"Error closing base64 encoder\")\n\terr = keyFile.Close()\n\trequire.NoError(t, err, \"Error closing %q: %v\", keyPath, err)\n\tkeyBase64 = buf.String()\n\tt.Log(\"wrote\", keyPath)\n\treturn\n}\n"
  },
  {
    "path": "pkg/testhelpers/errorx.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc NewErrorTestServer(t *testing.T, reg interface {\n\terrorx.PersistenceProvider\n\tconfig.Provider\n},\n) *httptest.Server {\n\tlogger := logrusx.New(\"\", \"\", logrusx.ForceLevel(logrus.TraceLevel))\n\twriter := herodot.NewJSONWriter(logger)\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\te, err := reg.SelfServiceErrorPersister().ReadErrorContainer(r.Context(), x.ParseUUID(r.URL.Query().Get(\"id\")))\n\t\trequire.NoError(t, err)\n\t\tt.Logf(\"Found error in NewErrorTestServer: %s\", e.Errors)\n\t\twriter.Write(w, r, e.Errors)\n\t}))\n\tt.Cleanup(ts.Close)\n\tts.URL = strings.ReplaceAll(ts.URL, \"127.0.0.1\", \"localhost\")\n\treg.Config().MustSet(t.Context(), config.ViperKeySelfServiceErrorUI, ts.URL)\n\treturn ts\n}\n\nfunc NewRedirTS(t *testing.T, body string, conf *config.Config) *httptest.Server {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif len(body) == 0 {\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t\treturn\n\t\t}\n\t\t_, _ = w.Write([]byte(body))\n\t}))\n\tt.Cleanup(ts.Close)\n\tconf.MustSet(context.Background(), config.ViperKeySelfServiceBrowserDefaultReturnTo, ts.URL)\n\treturn ts\n}\n\nfunc NewRedirSessionEchoTS(t *testing.T, reg interface {\n\thttpx.WriterProvider\n\tsession.ManagementProvider\n\tconfig.Provider\n},\n) *httptest.Server {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t// verify that the client has a session, and echo it back\n\t\tsess, err := reg.SessionManager().FetchFromRequest(r.Context(), r)\n\t\trequire.NoError(t, err, \"Headers: %+v\", r.Header)\n\t\treg.Writer().Write(w, r, sess)\n\t}))\n\tt.Cleanup(ts.Close)\n\treg.Config().MustSet(context.Background(), config.ViperKeySelfServiceBrowserDefaultReturnTo, ts.URL+\"/return-ts\")\n\treturn ts\n}\n\nfunc NewRedirNoSessionTS(t *testing.T, reg interface {\n\thttpx.WriterProvider\n\tsession.ManagementProvider\n\tconfig.Provider\n},\n) *httptest.Server {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t// verify that the client DOES NOT have a session\n\t\t_, err := reg.SessionManager().FetchFromRequest(r.Context(), r)\n\t\trequire.Error(t, err, \"Headers: %+v\", r.Header)\n\t\treg.Writer().Write(w, r, nil)\n\t}))\n\tt.Cleanup(ts.Close)\n\treg.Config().MustSet(context.Background(), config.ViperKeySelfServiceBrowserDefaultReturnTo, ts.URL+\"/return-ts\")\n\treturn ts\n}\n"
  },
  {
    "path": "pkg/testhelpers/fake.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"strings\"\n\n\t\"github.com/ory/x/randx\"\n)\n\nfunc RandomEmail() string {\n\treturn strings.ToLower(randx.MustString(16, randx.Alpha) + \"@ory.sh\")\n}\n\nfunc RandomPhone() string {\n\treturn \"+49151\" + randx.MustString(8, randx.Numeric)\n}\n"
  },
  {
    "path": "pkg/testhelpers/handler_mock.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/cookiejar\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httprouterx\"\n)\n\ntype mockDeps interface {\n\tidentity.PrivilegedPoolProvider\n\tidentity.ManagementProvider\n\tsession.ManagementProvider\n\tsession.PersistenceProvider\n\tconfig.Provider\n}\n\nfunc MockSetSession(t *testing.T, reg mockDeps, conf *config.Config) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\ti.NID = uuid.Must(uuid.NewV4())\n\t\trequire.NoError(t, i.SetCredentialsWithConfig(\n\t\t\tidentity.CredentialsTypePassword,\n\t\t\tidentity.Credentials{\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{faker.Email()},\n\t\t\t},\n\t\t\tjson.RawMessage(`{\"hashed_password\":\"$\"}`)))\n\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), i))\n\n\t\tMockSetSessionWithIdentity(t, reg, conf, i)(w, r)\n\t}\n}\n\nfunc MockSetSessionWithIdentity(t *testing.T, reg mockDeps, _ *config.Config, i *identity.Identity) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tactiveSession, err := NewActiveSession(r, reg, i, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\trequire.NoError(t, err)\n\t\tif aal := r.URL.Query().Get(\"set_aal\"); len(aal) > 0 {\n\t\t\tactiveSession.AuthenticatorAssuranceLevel = identity.AuthenticatorAssuranceLevel(aal)\n\t\t}\n\t\trequire.NoError(t, reg.SessionManager().UpsertAndIssueCookie(t.Context(), w, r, activeSession))\n\n\t\tw.WriteHeader(http.StatusOK)\n\t}\n}\n\nfunc MockMakeAuthenticatedRequest(t *testing.T, reg mockDeps, conf *config.Config, router *httprouterx.RouterPublic, req *http.Request) ([]byte, *http.Response) {\n\treturn MockMakeAuthenticatedRequestWithClient(t, reg, conf, router, req, NewClientWithCookies(t))\n}\n\nfunc MockMakeAuthenticatedRequestWithClient(t *testing.T, reg mockDeps, conf *config.Config, router *httprouterx.RouterPublic, req *http.Request, client *http.Client) ([]byte, *http.Response) {\n\treturn MockMakeAuthenticatedRequestWithClientAndID(t, reg, conf, router, req, client, nil)\n}\n\nfunc MockMakeAuthenticatedRequestWithClientAndID(t *testing.T, reg mockDeps, conf *config.Config, router *httprouterx.RouterPublic, req *http.Request, client *http.Client, id *identity.Identity) ([]byte, *http.Response) {\n\tset := \"/\" + uuid.Must(uuid.NewV4()).String() + \"/set\"\n\tif id == nil {\n\t\trouter.Handler(\"GET\", set, MockSetSession(t, reg, conf))\n\t} else {\n\t\trouter.Handler(\"GET\", set, MockSetSessionWithIdentity(t, reg, conf, id))\n\t}\n\n\tMockHydrateCookieClient(t, client, \"http://\"+req.URL.Host+set+\"?\"+req.URL.Query().Encode())\n\n\tres, err := client.Do(req)\n\trequire.NoError(t, errors.WithStack(err))\n\n\tbody, err := io.ReadAll(res.Body)\n\trequire.NoError(t, errors.WithStack(err))\n\n\trequire.NoError(t, res.Body.Close())\n\n\treturn body, res\n}\n\nfunc NewClientWithCookies(t *testing.T) *http.Client {\n\tcj, err := cookiejar.New(&cookiejar.Options{})\n\trequire.NoError(t, err)\n\treturn &http.Client{Jar: cj}\n}\n\nfunc NewNoRedirectClientWithCookies(t *testing.T) *http.Client {\n\tcj, err := cookiejar.New(&cookiejar.Options{})\n\trequire.NoError(t, err)\n\treturn &http.Client{\n\t\tJar: cj,\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t}\n}\n\nfunc MockHydrateCookieClient(t *testing.T, c *http.Client, u string) *http.Cookie {\n\tvar sessionCookie *http.Cookie\n\tres, err := c.Get(u)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\tbody := x.MustReadAll(res.Body)\n\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\tvar found bool\n\tfor _, rc := range res.Cookies() {\n\t\tif rc.Name == config.DefaultSessionCookieName {\n\t\t\tfound = true\n\t\t\tsessionCookie = rc\n\t\t}\n\t}\n\trequire.True(t, found, \"got body: %s\\ngot url: %s\", body, res.Request.URL.String())\n\treturn sessionCookie\n}\n\nfunc MockSessionCreateHandlerWithIdentity(t *testing.T, reg mockDeps, i *identity.Identity) (http.HandlerFunc, *session.Session) {\n\tvar ct []identity.CredentialsType\n\n\t// if identity was not created with any credentials,\n\t// then assume a 'password' credential type\n\tif len(i.Credentials) == 0 {\n\t\treturn MockSessionCreateHandlerWithIdentityAndAMR(t, reg, i, []identity.CredentialsType{\"password\"})\n\t}\n\n\t// otherwise, mock session with appropriate credential types\n\tfor _, c := range i.Credentials {\n\t\tct = append(ct, c.Type)\n\t}\n\n\treturn MockSessionCreateHandlerWithIdentityAndAMR(t, reg, i, ct)\n}\n\nfunc MockSessionCreateHandlerWithIdentityAndAMR(t *testing.T, reg mockDeps, i *identity.Identity, methods []identity.CredentialsType) (http.HandlerFunc, *session.Session) {\n\tvar sess session.Session\n\trequire.NoError(t, faker.FakeData(&sess))\n\t// require AuthenticatedAt to be time.Now() as we always compare it to the current time\n\tsess.AuthenticatedAt = time.Now().UTC()\n\tsess.IssuedAt = time.Now().UTC()\n\tsess.ExpiresAt = time.Now().UTC().Add(time.Hour * 24)\n\tsess.Active = true\n\n\tfor _, m := range methods {\n\t\tif m == identity.CredentialsTypeOIDC {\n\t\t\tif c, ok := i.Credentials[m]; ok {\n\t\t\t\tvar target identity.CredentialsOIDC\n\t\t\t\tif err := json.Unmarshal(c.Config, &target); err == nil {\n\t\t\t\t\tfor _, t := range target.Providers {\n\t\t\t\t\t\tsess.CompletedLoginForWithProvider(c.Type, identity.AuthenticatorAssuranceLevel1, t.Provider, \"\")\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsess.CompletedLoginFor(m, \"\")\n\t}\n\n\tsess.SetAuthenticatorAssuranceLevel()\n\n\tif _, err := reg.Config().DefaultIdentityTraitsSchemaURL(t.Context()); err != nil {\n\t\tSetDefaultIdentitySchema(reg.Config(), \"file://./stub/fake-session.schema.json\")\n\t}\n\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(t.Context(), i))\n\n\tinserted, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), i.ID)\n\trequire.NoError(t, err)\n\tsess.Identity = inserted\n\n\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), &sess))\n\trequire.Len(t, inserted.Credentials, len(i.Credentials))\n\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\trequire.NoError(t, reg.SessionManager().IssueCookie(t.Context(), w, r, &sess))\n\t}, &sess\n}\n\nfunc MockSessionCreateHandler(t *testing.T, reg mockDeps) (http.HandlerFunc, *session.Session) {\n\treturn MockSessionCreateHandlerWithIdentity(t, reg, &identity.Identity{\n\t\tID: x.NewUUID(), State: identity.StateActive, Traits: identity.Traits(`{\"baz\":\"bar\",\"foo\":true,\"bar\":2.5}`),\n\t})\n}\n"
  },
  {
    "path": "pkg/testhelpers/http.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/cookiejar\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc NewDebugClient(t *testing.T) *http.Client {\n\treturn &http.Client{Transport: NewTransportWithLogger(http.DefaultTransport, t)}\n}\n\nfunc NewClientWithCookieJar(t *testing.T, jar *cookiejar.Jar, checkRedirect CheckRedirectFunc) *http.Client {\n\tif jar == nil {\n\t\tj, err := cookiejar.New(nil)\n\t\tjar = j\n\t\trequire.NoError(t, err)\n\t}\n\tif checkRedirect == nil {\n\t\tcheckRedirect = DebugRedirects(t)\n\t}\n\treturn &http.Client{\n\t\tJar:           jar,\n\t\tCheckRedirect: checkRedirect,\n\t}\n}\n\ntype CheckRedirectFunc func(req *http.Request, via []*http.Request) error\n\nfunc DebugRedirects(t *testing.T) CheckRedirectFunc {\n\treturn func(req *http.Request, via []*http.Request) error {\n\t\tt.Logf(\"Redirect: %s\", req.URL.String())\n\n\t\tif len(via) >= 20 {\n\t\t\tfor k, v := range via {\n\t\t\t\tt.Logf(\"Failed with redirect (%d): %s\", k, v.URL.String())\n\t\t\t}\n\t\t\treturn errors.New(\"stopped after 20 redirects\")\n\t\t}\n\t\treturn nil\n\t}\n}\n\nfunc NewPostRequest(t *testing.T, isAPI bool, url string, payload io.Reader) *http.Request {\n\treq, err := http.NewRequest(\"POST\", url, payload)\n\trequire.NoError(t, err)\n\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"Accept\", \"text/html\")\n\tif isAPI {\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t}\n\n\treturn req\n}\n\nfunc NewHTTPGetJSONRequest(t *testing.T, url string) *http.Request {\n\treq, err := http.NewRequest(\"GET\", url, nil)\n\trequire.NoError(t, err)\n\n\t// req.Header.Set(\"Content-Type\", \"application/json;charset=utf-8\")\n\treq.Header.Set(\"Accept\", \"application/json\")\n\treturn req\n}\n\nfunc NewHTTPGetAJAXRequest(t *testing.T, url string) *http.Request {\n\treq, err := http.NewRequest(\"GET\", url, nil)\n\trequire.NoError(t, err)\n\n\treq.Header.Set(\"Accept\", \"application/json\")\n\treturn req\n}\n\nfunc NewHTTPDeleteJSONRequest(t *testing.T, url string, in interface{}) *http.Request {\n\tvar body bytes.Buffer\n\trequire.NoError(t, json.NewEncoder(&body).Encode(in))\n\treq, err := http.NewRequest(\"DELETE\", url, &body)\n\trequire.NoError(t, err)\n\treq.Header.Set(\"Accept\", \"application/json\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treturn req\n}\n\nfunc HTTPRequestJSON(t *testing.T, client *http.Client, method string, url string, in interface{}) ([]byte, *http.Response) {\n\tvar body bytes.Buffer\n\trequire.NoError(t, json.NewEncoder(&body).Encode(in))\n\n\treq, err := http.NewRequest(method, url, &body)\n\trequire.NoError(t, err)\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tres, err := client.Do(req)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\n\tpayload, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\n\treturn payload, res\n}\n\nfunc HTTPPostForm(t *testing.T, client *http.Client, remote string, in *url.Values) ([]byte, *http.Response) {\n\tif in == nil {\n\t\tin = new(url.Values)\n\t}\n\n\tres, err := client.PostForm(remote, *in)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\n\tpayload, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\n\treturn payload, res\n}\n\nfunc NewTestHTTPRequest(t *testing.T, method, url string, body io.Reader) *http.Request {\n\treq, err := http.NewRequest(method, url, body)\n\trequire.NoError(t, err)\n\treturn req\n}\n\nfunc EasyGet(t *testing.T, c *http.Client, url string) (*http.Response, []byte) {\n\tres, err := c.Get(url)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\tbody, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\treturn res, body\n}\n\nfunc EasyGetJSON(t *testing.T, c *http.Client, url string) (*http.Response, []byte) {\n\treq, err := http.NewRequest(\"GET\", url, nil)\n\trequire.NoError(t, err)\n\treq.Header.Set(\"Accept\", \"application/json\")\n\tres, err := c.Do(req)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\tbody, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\treturn res, body\n}\n\nfunc EasyGetBody(t *testing.T, c *http.Client, url string) []byte {\n\t_, body := EasyGet(t, c, url) // nolint: bodyclose\n\treturn body\n}\n\nfunc EasyCookieJar(t *testing.T, o *cookiejar.Options) *cookiejar.Jar {\n\tcj, err := cookiejar.New(o)\n\trequire.NoError(t, err)\n\treturn cj\n}\n"
  },
  {
    "path": "pkg/testhelpers/httptest.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc NewHTTPTestServer(t *testing.T, h http.Handler) *httptest.Server {\n\tts := httptest.NewServer(h)\n\tt.Cleanup(ts.Close)\n\treturn ts\n}\n"
  },
  {
    "path": "pkg/testhelpers/identity.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n)\n\nfunc CreateSession(t *testing.T, reg driver.Registry) *session.Session {\n\treq := NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(req.Context(), i))\n\tsess, err := NewActiveSession(req, reg, i, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\trequire.NoError(t, err)\n\trequire.NoError(t, reg.SessionPersister().UpsertSession(req.Context(), sess))\n\treturn sess\n}\n"
  },
  {
    "path": "pkg/testhelpers/identity_schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"github.com/ory/kratos/driver/config\"\n)\n\nfunc IdentitySchemasConfig(schemas map[string]string) map[string]any {\n\tvar s []config.Schema\n\tfor id, location := range schemas {\n\t\ts = append(s, config.Schema{ID: id, URL: location})\n\t}\n\treturn map[string]any{config.ViperKeyIdentitySchemas: s}\n}\n"
  },
  {
    "path": "pkg/testhelpers/json.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc JSONEq(t *testing.T, expected, actual interface{}, messageAndArgs ...interface{}) {\n\tvar eb, ab bytes.Buffer\n\trequire.NoError(t, json.NewEncoder(&eb).Encode(expected))\n\trequire.NoError(t, json.NewEncoder(&ab).Encode(actual))\n\tassert.JSONEq(t, eb.String(), ab.String(), messageAndArgs...)\n}\n\nfunc LogJSON(t *testing.T, v interface{}) {\n\tout, err := json.MarshalIndent(v, \"\", \"  \")\n\trequire.NoError(t, err)\n\tt.Logf(\"\\n%s\\n---\", out)\n}\n"
  },
  {
    "path": "pkg/testhelpers/network.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/x/networkx\"\n)\n\nfunc NewNetworkUnlessExisting(t *testing.T, ctx context.Context, p persistence.Persister) (uuid.UUID, persistence.Persister) {\n\tif n, ok := ctx.Value(\"network\").(*networkx.Network); ok && n != nil {\n\t\treturn n.ID, p.WithNetworkID(n.ID)\n\t}\n\n\tn := networkx.NewNetwork()\n\trequire.NoError(t, p.GetConnection(ctx).Create(n))\n\treturn n.ID, p.WithNetworkID(n.ID)\n}\n\nfunc NewNetwork(t testing.TB, ctx context.Context, p persistence.Persister) (uuid.UUID, persistence.Persister) {\n\tSkipIfNetworkContext(t, ctx)\n\n\tn := networkx.NewNetwork()\n\trequire.NoError(t, p.GetConnection(context.Background()).Create(n))\n\treturn n.ID, p.WithNetworkID(n.ID)\n}\n\nfunc ExistingNetwork(t *testing.T, p persistence.Persister, id uuid.UUID) persistence.Persister {\n\trequire.NoError(t, p.GetConnection(context.Background()).Save(&networkx.Network{ID: id}))\n\treturn p.WithNetworkID(id)\n}\n\nfunc SkipIfNetworkContext(t testing.TB, ctx context.Context) {\n\ta, b := ctx.Value(\"network\").(*networkx.Network)\n\tif a != nil && b {\n\t\tt.Skip(\"Network was set in context\")\n\t}\n}\n"
  },
  {
    "path": "pkg/testhelpers/sdk.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n)\n\nfunc NewSDKClient(ts *httptest.Server) *kratos.APIClient {\n\treturn NewSDKClientFromURL(ts.URL)\n}\n\nfunc NewSDKCustomClient(ts *httptest.Server, client *http.Client) *kratos.APIClient {\n\tconf := kratos.NewConfiguration()\n\tconf.Servers = kratos.ServerConfigurations{{URL: ts.URL}}\n\tconf.HTTPClient = client\n\treturn kratos.NewAPIClient(conf)\n}\n\nfunc NewSDKClientFromURL(u string) *kratos.APIClient {\n\tconf := kratos.NewConfiguration()\n\tconf.Servers = kratos.ServerConfigurations{{URL: u}}\n\treturn kratos.NewAPIClient(conf)\n}\n\nfunc SDKFormFieldsToURLValues(ff []kratos.UiNode) url.Values {\n\tvalues := url.Values{}\n\tfor _, f := range ff {\n\t\tattr := f.Attributes.UiNodeInputAttributes\n\t\tif attr == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tval := attr.Value\n\t\tif val == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch v := val.(type) {\n\t\tcase bool:\n\t\t\tvalues.Set(attr.Name, fmt.Sprintf(\"%v\", v))\n\t\tcase string:\n\t\t\tvalues.Set(attr.Name, fmt.Sprintf(\"%v\", v))\n\t\tcase float32:\n\t\t\tvalues.Set(attr.Name, fmt.Sprintf(\"%v\", v))\n\t\tcase float64:\n\t\t\tvalues.Set(attr.Name, fmt.Sprintf(\"%v\", v))\n\t\t}\n\t}\n\treturn values\n}\n\nfunc NewFakeCSRFNode() *kratos.UiNode {\n\treturn &kratos.UiNode{\n\t\tGroup: node.DefaultGroup.String(),\n\t\tType:  \"input\",\n\t\tAttributes: kratos.UiNodeInputAttributesAsUiNodeAttributes(&kratos.UiNodeInputAttributes{\n\t\t\tName:     \"csrf_token\",\n\t\t\tRequired: new(true),\n\t\t\tType:     \"hidden\",\n\t\t\tValue:    nosurfx.FakeCSRFToken,\n\t\t}),\n\t}\n}\n\nfunc NewSDKEmailNode(group string) *kratos.UiNode {\n\treturn &kratos.UiNode{\n\t\tType:  \"input\",\n\t\tGroup: group,\n\t\tAttributes: kratos.UiNodeInputAttributesAsUiNodeAttributes(&kratos.UiNodeInputAttributes{\n\t\t\tName:     \"email\",\n\t\t\tType:     \"email\",\n\t\t\tRequired: new(true),\n\t\t\tValue:    \"email\",\n\t\t}),\n\t}\n}\n\nfunc NewMethodSubmit(group, value string) *kratos.UiNode {\n\treturn &kratos.UiNode{\n\t\tType:  \"input\",\n\t\tGroup: group,\n\t\tAttributes: kratos.UiNodeInputAttributesAsUiNodeAttributes(&kratos.UiNodeInputAttributes{\n\t\t\tName:  \"method\",\n\t\t\tType:  \"submit\",\n\t\t\tValue: value,\n\t\t}),\n\t}\n}\n\nfunc NewPasswordNode() *kratos.UiNode {\n\treturn &kratos.UiNode{\n\t\tType:  \"input\",\n\t\tGroup: node.PasswordGroup.String(),\n\t\tAttributes: kratos.UiNodeInputAttributesAsUiNodeAttributes(&kratos.UiNodeInputAttributes{\n\t\t\tName:     \"password\",\n\t\t\tType:     \"password\",\n\t\t\tRequired: new(true),\n\t\t}),\n\t}\n}\n"
  },
  {
    "path": "pkg/testhelpers/selfservice.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gobuffalo/httptest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n)\n\nfunc TestSelfServicePreHook(\n\tconfigKey string,\n\tmakeRequestPre func(t *testing.T, ts *httptest.Server) (*http.Response, string),\n\tnewServer func(t *testing.T) *httptest.Server,\n\tconf *config.Config,\n) func(t *testing.T) {\n\tctx := context.Background()\n\treturn func(t *testing.T) {\n\t\tt.Run(\"case=pass without hooks\", func(t *testing.T) {\n\t\t\tt.Cleanup(SelfServiceHookConfigReset(t, conf))\n\n\t\t\tres, _ := makeRequestPre(t, newServer(t))\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=pass if hooks pass\", func(t *testing.T) {\n\t\t\tt.Cleanup(SelfServiceHookConfigReset(t, conf))\n\t\t\tconf.MustSet(ctx, configKey, []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\n\t\t\tres, _ := makeRequestPre(t, newServer(t))\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=err if hooks err\", func(t *testing.T) {\n\t\t\tt.Cleanup(SelfServiceHookConfigReset(t, conf))\n\t\t\tconf.MustSet(ctx, configKey, []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecuteLoginPreHook\": \"err\",\"ExecuteRegistrationPreHook\": \"err\",\"ExecuteSettingsPreHook\": \"err\",\"ExecuteVerificationPreHook\": \"err\",\"ExecuteRecoveryPreHook\": \"err\"}`)}})\n\n\t\t\tres, body := makeRequestPre(t, newServer(t))\n\t\t\tassert.EqualValues(t, http.StatusInternalServerError, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"err\", body)\n\t\t})\n\n\t\tt.Run(\"case=abort if hooks aborts\", func(t *testing.T) {\n\t\t\tt.Cleanup(SelfServiceHookConfigReset(t, conf))\n\t\t\tconf.MustSet(ctx, configKey, []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecuteLoginPreHook\": \"abort\",\"ExecuteRegistrationPreHook\": \"abort\",\"ExecuteSettingsPreHook\": \"abort\",\"ExecuteVerificationPreHook\": \"abort\",\"ExecuteRecoveryPreHook\": \"abort\"}`)}})\n\n\t\t\tres, body := makeRequestPre(t, newServer(t))\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Empty(t, body)\n\t\t})\n\n\t\tt.Run(\"case=redirect\", func(t *testing.T) {\n\t\t\tt.Skipf(\"Skipped because pre-redirect is no longer supported\")\n\n\t\t\tt.Cleanup(SelfServiceHookConfigReset(t, conf))\n\t\t\tconf.MustSet(ctx, configKey, []config.SelfServiceHook{{Name: \"redirect\", Config: []byte(`{\"to\": \"https://www.ory.sh/\"}`)}})\n\n\t\t\tres, _ := makeRequestPre(t, newServer(t))\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.EqualValues(t, \"https://www.ory.sh/\", res.Request.URL.String())\n\t\t})\n\t}\n}\n\nfunc SelfServiceHookCreateFakeIdentity(t *testing.T, reg driver.Registry) *identity.Identity {\n\ti := SelfServiceHookFakeIdentity(t)\n\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), i))\n\treturn i\n}\n\nfunc SelfServiceHookFakeIdentity(t *testing.T) *identity.Identity {\n\tvar i identity.Identity\n\trequire.NoError(t, faker.FakeData(&i))\n\ti.Traits = identity.Traits(`{}`)\n\ti.State = identity.StateActive\n\ti.NID = uuid.Must(uuid.NewV4())\n\treturn &i\n}\n\nfunc SelfServiceHookConfigReset(t *testing.T, conf *config.Config) func() {\n\tctx := context.Background()\n\treturn func() {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginAfter, nil)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginAfter+\".hooks\", nil)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginBeforeHooks, nil)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryAfter, nil)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryAfter+\".hooks\", nil)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter, nil)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".hooks\", nil)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationBeforeHooks, nil)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsAfter, nil)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsAfter+\".hooks\", nil)\n\t}\n}\n\nfunc SelfServiceHookSettingsSetDefaultRedirectTo(t *testing.T, conf *config.Config, value string) {\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsAfter+\".\"+config.DefaultBrowserReturnURL, value)\n}\n\nfunc SelfServiceHookSettingsSetDefaultRedirectToStrategy(t *testing.T, conf *config.Config, strategy, value string) {\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsAfter+\".\"+strategy+\".\"+config.DefaultBrowserReturnURL, value)\n}\n\nfunc SelfServiceHookLoginSetDefaultRedirectTo(t *testing.T, conf *config.Config, value string) {\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginAfter+\".\"+config.DefaultBrowserReturnURL, value)\n}\n\nfunc SelfServiceHookLoginSetDefaultRedirectToStrategy(t *testing.T, conf *config.Config, strategy, value string) {\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginAfter+\".\"+strategy+\".\"+config.DefaultBrowserReturnURL, value)\n}\n\nfunc SelfServiceHookRegistrationSetDefaultRedirectTo(t *testing.T, conf *config.Config, value string) {\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".\"+config.DefaultBrowserReturnURL, value)\n}\n\nfunc SelfServiceHookRegistrationSetDefaultRedirectToStrategy(t *testing.T, conf *config.Config, strategy, value string) {\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".\"+strategy+\".\"+config.DefaultBrowserReturnURL, value)\n}\n\nfunc SelfServiceHookLoginViperSetPost(t *testing.T, conf *config.Config, strategy string, c []config.SelfServiceHook) {\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceLoginAfter, strategy), c)\n}\n\nfunc SelfServiceHookRegistrationViperSetPost(t *testing.T, conf *config.Config, strategy string, c []config.SelfServiceHook) {\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, strategy), c)\n}\n\nfunc SelfServiceHookLoginErrorHandler(t *testing.T, w http.ResponseWriter, r *http.Request, err error) bool {\n\treturn SelfServiceHookErrorHandler(t, w, r, login.ErrHookAbortFlow, err)\n}\n\nfunc SelfServiceHookRegistrationErrorHandler(t *testing.T, w http.ResponseWriter, r *http.Request, err error) bool {\n\treturn SelfServiceHookErrorHandler(t, w, r, registration.ErrHookAbortFlow, err)\n}\n\nfunc SelfServiceHookSettingsErrorHandler(t *testing.T, w http.ResponseWriter, r *http.Request, err error) bool {\n\treturn SelfServiceHookErrorHandler(t, w, r, settings.ErrHookAbortFlow, err)\n}\n\nfunc SelfServiceHookErrorHandler(t *testing.T, w http.ResponseWriter, _ *http.Request, abortErr error, actualErr error) bool {\n\tif actualErr != nil {\n\t\tt.Logf(\"received error: %+v\", actualErr)\n\t\tif errors.Is(actualErr, abortErr) {\n\t\t\treturn false\n\t\t}\n\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\t_, _ = w.Write([]byte(actualErr.Error()))\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc SelfServiceMakeLoginPreHookRequest(t *testing.T, ts *httptest.Server) (*http.Response, string) {\n\treturn SelfServiceMakeHookRequest(t, ts, \"/login/pre\", false, url.Values{})\n}\n\nfunc SelfServiceMakeLoginPostHookRequest(t *testing.T, ts *httptest.Server, asAPI bool, query url.Values) (*http.Response, string) {\n\treturn SelfServiceMakeHookRequest(t, ts, \"/login/post\", asAPI, query)\n}\n\nfunc SelfServiceMakeRegistrationPreHookRequest(t *testing.T, ts *httptest.Server) (*http.Response, string) {\n\treturn SelfServiceMakeHookRequest(t, ts, \"/registration/pre\", false, url.Values{})\n}\n\nfunc SelfServiceMakeSettingsPreHookRequest(t *testing.T, ts *httptest.Server) (*http.Response, string) {\n\treturn SelfServiceMakeHookRequest(t, ts, \"/settings/pre\", false, url.Values{})\n}\n\nfunc SelfServiceMakeRecoveryPreHookRequest(t *testing.T, ts *httptest.Server) (*http.Response, string) {\n\treturn SelfServiceMakeHookRequest(t, ts, \"/recovery/pre\", false, url.Values{})\n}\n\nfunc SelfServiceMakeVerificationPreHookRequest(t *testing.T, ts *httptest.Server) (*http.Response, string) {\n\treturn SelfServiceMakeHookRequest(t, ts, \"/verification/pre\", false, url.Values{})\n}\n\nfunc SelfServiceMakeRegistrationPostHookRequest(t *testing.T, ts *httptest.Server, asAPI bool, query url.Values) (*http.Response, string) {\n\treturn SelfServiceMakeHookRequest(t, ts, \"/registration/post\", asAPI, query)\n}\n\nfunc SelfServiceMakeSettingsPostHookRequest(t *testing.T, ts *httptest.Server, asAPI bool, query url.Values) (*http.Response, string) {\n\treturn SelfServiceMakeHookRequest(t, ts, \"/settings/post\", asAPI, query)\n}\n\nfunc SelfServiceMakeHookRequest(t *testing.T, ts *httptest.Server, suffix string, asAPI bool, query url.Values) (*http.Response, string) {\n\tif len(query) > 0 {\n\t\tsuffix += \"?\" + query.Encode()\n\t}\n\treq, err := http.NewRequest(\"GET\", ts.URL+suffix, nil)\n\trequire.NoError(t, err)\n\treq.Header.Set(\"Accept\", \"text/html\")\n\tif asAPI {\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t}\n\tres, err := ts.Client().Do(req)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\tbody, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\treturn res, string(body)\n}\n\nfunc GetSelfServiceRedirectLocation(t *testing.T, url string) string {\n\tc := &http.Client{\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t}\n\treq, err := http.NewRequest(\"GET\", url, nil)\n\trequire.NoError(t, err)\n\tres, err := c.Do(req)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\treturn res.Header.Get(\"Location\")\n}\n\nfunc AssertMessage[B string | []byte](t *testing.T, body B, message string) {\n\tt.Helper()\n\trequire.Len(t, gjson.Get(string(body), \"ui.messages\").Array(), 1)\n\tassert.Equalf(t, message, gjson.Get(string(body), \"ui.messages.0.text\").String(), \"%s\", body)\n}\n\nfunc AssertFieldMessage[B string | []byte](t *testing.T, body B, fieldName, message string) {\n\tt.Helper()\n\tmessages := gjson.Get(string(body), \"ui.nodes.#(attributes.name==\"+fieldName+\").messages\")\n\trequire.Len(t, messages.Array(), 1, \"expected field %s to have one message, got %s\", fieldName, messages)\n\tassert.Equalf(t, message, messages.Get(\"0.text\").String(), \"%s\", body)\n}\n"
  },
  {
    "path": "pkg/testhelpers/selfservice_login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/ioutilx\"\n)\n\nfunc NewLoginUIFlowEchoServer(t *testing.T, reg driver.Registry) *httptest.Server {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\te, err := reg.LoginFlowPersister().GetLoginFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"flow\")))\n\t\trequire.NoError(t, err)\n\t\treg.Writer().Write(w, r, e)\n\t}))\n\tts.URL = strings.ReplaceAll(ts.URL, \"127.0.0.1\", \"localhost\")\n\treg.Config().MustSet(t.Context(), config.ViperKeySelfServiceLoginUI, ts.URL+\"/login-ts\")\n\tt.Cleanup(ts.Close)\n\treturn ts\n}\n\nfunc NewLoginUIWith401Response(t *testing.T, c *config.Config) *httptest.Server {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusUnauthorized)\n\t}))\n\tts.URL = strings.ReplaceAll(ts.URL, \"127.0.0.1\", \"localhost\")\n\tc.MustSet(t.Context(), config.ViperKeySelfServiceLoginUI, ts.URL+\"/login-ts\")\n\tt.Cleanup(ts.Close)\n\treturn ts\n}\n\ntype initFlowOptions struct {\n\taal                  identity.AuthenticatorAssuranceLevel\n\treturnTo             string\n\trefresh              bool\n\toauth2LoginChallenge string\n\tvia                  string\n\tidentitySchema       string\n\tctx                  context.Context\n\texpectActive         *string\n}\n\nfunc newInitFlowOptions(opts []InitFlowWithOption) *initFlowOptions {\n\treturn new(initFlowOptions).apply(opts)\n}\n\nfunc (o *initFlowOptions) apply(opts []InitFlowWithOption) *initFlowOptions {\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\treturn o\n}\n\nfunc getURLFromInitOptions(ts *httptest.Server, path string, forced bool, opts ...InitFlowWithOption) string {\n\to := new(initFlowOptions).apply(opts)\n\tq := url.Values{}\n\n\tif forced || o.refresh {\n\t\tq.Set(\"refresh\", \"true\")\n\t}\n\n\tif o.aal != \"\" {\n\t\tq.Set(\"aal\", string(o.aal))\n\t}\n\n\tif o.returnTo != \"\" {\n\t\tq.Set(\"return_to\", string(o.returnTo))\n\t}\n\n\tif o.oauth2LoginChallenge != \"\" {\n\t\tq.Set(\"login_challenge\", o.oauth2LoginChallenge)\n\t}\n\tif o.via != \"\" {\n\t\tq.Set(\"via\", o.via)\n\t}\n\tif o.identitySchema != \"\" {\n\t\tq.Set(\"identity_schema\", o.identitySchema)\n\t}\n\n\tu := urlx.ParseOrPanic(ts.URL + path)\n\tu.RawQuery = q.Encode()\n\treturn u.String()\n}\n\ntype InitFlowWithOption func(*initFlowOptions)\n\nfunc InitFlowWithAAL(aal identity.AuthenticatorAssuranceLevel) InitFlowWithOption {\n\treturn func(o *initFlowOptions) {\n\t\to.aal = aal\n\t}\n}\n\nfunc InitFlowWithReturnTo(returnTo string) InitFlowWithOption {\n\treturn func(o *initFlowOptions) {\n\t\to.returnTo = returnTo\n\t}\n}\n\nfunc InitFlowWithRefresh() InitFlowWithOption {\n\treturn func(o *initFlowOptions) {\n\t\to.refresh = true\n\t}\n}\n\nfunc InitFlowWithContext(ctx context.Context) InitFlowWithOption {\n\treturn func(o *initFlowOptions) {\n\t\to.ctx = ctx\n\t}\n}\n\nfunc InitFlowWithOAuth2LoginChallenge(hlc string) InitFlowWithOption {\n\treturn func(o *initFlowOptions) {\n\t\to.oauth2LoginChallenge = hlc\n\t}\n}\n\nfunc InitFlowWithIdentitySchema(schema string) InitFlowWithOption {\n\treturn func(o *initFlowOptions) {\n\t\to.identitySchema = schema\n\t}\n}\n\nfunc ExpectActive(active string) InitFlowWithOption {\n\treturn func(o *initFlowOptions) {\n\t\to.expectActive = &active\n\t}\n}\n\n// InitFlowWithVia sets the `via` query parameter which is used by the code MFA flows to determine the trait to use to send the code to the user\nfunc InitFlowWithVia(via string) InitFlowWithOption {\n\treturn func(o *initFlowOptions) {\n\t\to.via = via\n\t}\n}\n\n// Deprecated: use InitializeLoginFlowViaBrowserCtx instead.\nfunc InitializeLoginFlowViaBrowser(t *testing.T, client *http.Client, ts *httptest.Server, forced, isSPA, expectInitError, expectGetError bool, opts ...InitFlowWithOption) *kratos.LoginFlow {\n\treturn InitializeLoginFlowViaBrowserCtx(t.Context(), t, client, ts, forced, isSPA, expectInitError, expectGetError, opts...)\n}\n\nfunc InitializeLoginFlowViaBrowserCtx(ctx context.Context, t *testing.T, client *http.Client, ts *httptest.Server, forced, isSPA, expectInitError, expectGetError bool, opts ...InitFlowWithOption) *kratos.LoginFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\n\treq, err := http.NewRequest(\"GET\", getURLFromInitOptions(ts, login.RouteInitBrowserFlow, forced, opts...), nil)\n\trequire.NoError(t, err)\n\to := newInitFlowOptions(opts)\n\n\tif isSPA {\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t}\n\n\tres, err := client.Do(req.WithContext(ctx))\n\trequire.NoError(t, err)\n\tbody := x.MustReadAll(res.Body)\n\tif isSPA {\n\t\trequire.Truef(t, gjson.ValidBytes(body), \"body is not valid JSON: %s\", body)\n\t}\n\trequire.NoError(t, res.Body.Close())\n\trequire.Equal(t, 200, res.StatusCode, \"%s\", body)\n\tif expectInitError {\n\t\trequire.NotNil(t, res.Request.URL)\n\t\trequire.Contains(t, res.Request.URL.String(), \"error-ts\")\n\t}\n\n\tflowID := res.Request.URL.Query().Get(\"flow\")\n\tif isSPA {\n\t\tflowID = gjson.GetBytes(body, \"id\").String()\n\t}\n\tif !expectGetError {\n\t\trequire.NotEmpty(t, flowID)\n\t}\n\n\trs, r, err := publicClient.FrontendAPI.GetLoginFlow(ctx).Id(flowID).Execute()\n\tif expectGetError {\n\t\trequire.Error(t, err)\n\t\trequire.Nil(t, rs)\n\t} else {\n\t\trequire.NoError(t, err)\n\t\trequire.NoErrorf(t, err, \"%s\", ioutilx.MustReadAll(r.Body))\n\t\tassert.Equal(t, o.expectActive, rs.Active)\n\t}\n\n\treturn rs\n}\n\nfunc InitializeLoginFlowViaAPICtx(ctx context.Context, t *testing.T, client *http.Client, ts *httptest.Server, forced bool, opts ...InitFlowWithOption) *kratos.LoginFlow {\n\treturn initializeLoginFlowViaAPI(ctx, t, client, ts, forced, false, opts...)\n}\n\nfunc initializeLoginFlowViaAPI(ctx context.Context, t *testing.T, client *http.Client, ts *httptest.Server, forced bool, expectError bool, opts ...InitFlowWithOption) *kratos.LoginFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\n\to := new(initFlowOptions).apply(opts)\n\treq := publicClient.FrontendAPI.CreateNativeLoginFlow(ctx).Refresh(forced)\n\tif o.aal != \"\" {\n\t\treq = req.Aal(string(o.aal))\n\t}\n\tif o.via != \"\" {\n\t\treq = req.Via(o.via)\n\t}\n\tif o.identitySchema != \"\" {\n\t\treq = req.IdentitySchema(o.identitySchema)\n\t}\n\n\trs, res, err := req.Execute()\n\tif expectError {\n\t\trequire.Error(t, err)\n\t\trequire.Nil(t, rs)\n\t} else {\n\t\trequire.NoError(t, err, \"%s\", ioutilx.MustReadAll(res.Body))\n\t\tassert.Equal(t, o.expectActive, rs.Active)\n\t}\n\n\treturn rs\n}\n\n// Deprecated: use InitializeLoginFlowViaAPICtx instead.\nfunc InitializeLoginFlowViaAPI(t *testing.T, client *http.Client, ts *httptest.Server, forced bool, opts ...InitFlowWithOption) *kratos.LoginFlow {\n\treturn InitializeLoginFlowViaAPICtx(t.Context(), t, client, ts, forced, opts...)\n}\n\nfunc InitializeLoginFlowViaAPIExpectError(ctx context.Context, t *testing.T, client *http.Client, ts *httptest.Server, forced bool, opts ...InitFlowWithOption) *kratos.LoginFlow {\n\treturn initializeLoginFlowViaAPI(ctx, t, client, ts, forced, true, opts...)\n}\n\n// Deprecated: use LoginMakeRequestCtx instead.\nfunc LoginMakeRequest(\n\tt *testing.T,\n\tisAPI bool,\n\tisSPA bool,\n\tf *kratos.LoginFlow,\n\thc *http.Client,\n\tvalues string,\n\topt ...RequestOption,\n) (string, *http.Response) {\n\treturn LoginMakeRequestCtx(t.Context(), t, isAPI, isSPA, f, hc, values, opt...)\n}\n\ntype RequestOption func(*http.Request)\n\nfunc WithHeader(key, value string) RequestOption {\n\treturn func(r *http.Request) {\n\t\tr.Header.Add(key, value)\n\t}\n}\n\nfunc LoginMakeRequestCtx(\n\tctx context.Context,\n\tt *testing.T,\n\tisAPI bool,\n\tisSPA bool,\n\tf *kratos.LoginFlow,\n\thc *http.Client,\n\tvalues string,\n\topt ...RequestOption,\n) (string, *http.Response) {\n\trequire.NotEmpty(t, f.Ui.Action)\n\n\treq := NewPostRequest(t, isAPI, f.Ui.Action, bytes.NewBufferString(values))\n\tif isSPA && !isAPI {\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\treq.Header.Set(\"Sec-Fetch-Mode\", \"cors\")\n\t}\n\tfor _, o := range opt {\n\t\to(req)\n\t}\n\n\tres, err := hc.Do(req.WithContext(ctx))\n\trequire.NoError(t, err, \"action: %s\", f.Ui.Action)\n\tdefer func() { _ = res.Body.Close() }()\n\n\treturn string(ioutilx.MustReadAll(res.Body)), res\n}\n\nfunc GetLoginFlow(t *testing.T, client *http.Client, ts *httptest.Server, flowID string) *kratos.LoginFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\trs, _, err := publicClient.FrontendAPI.GetLoginFlow(context.Background()).Id(flowID).Execute()\n\trequire.NoError(t, err)\n\treturn rs\n}\n\n// Deprecated: use SubmitLoginFormCtx instead.\nfunc SubmitLoginForm(\n\tt *testing.T,\n\tisAPI bool,\n\thc *http.Client,\n\tpublicTS *httptest.Server,\n\twithValues func(v url.Values),\n\tisSPA bool,\n\tforced bool,\n\texpectedStatusCode int,\n\texpectedURL string,\n\topts ...InitFlowWithOption,\n) string {\n\treturn SubmitLoginFormCtx(context.Background(), t, isAPI, hc, publicTS, withValues, isSPA, forced, expectedStatusCode, expectedURL, opts...)\n}\n\n// SubmitLoginFormCtx initiates a login flow (for Browser and API!), fills out the form and modifies\n// the form values with `withValues`, and submits the form. Returns the body and checks for expectedStatusCode and\n// expectedURL on completion\nfunc SubmitLoginFormCtx(\n\tctx context.Context,\n\tt *testing.T,\n\tisAPI bool,\n\thc *http.Client,\n\tpublicTS *httptest.Server,\n\twithValues func(v url.Values),\n\tisSPA bool,\n\tforced bool,\n\texpectedStatusCode int,\n\texpectedURL string,\n\topts ...InitFlowWithOption,\n) string {\n\tif hc == nil {\n\t\thc = new(http.Client)\n\t\tif !isAPI {\n\t\t\thc = NewClientWithCookies(t)\n\t\t}\n\t}\n\n\thc.Transport = NewTransportWithLogger(hc.Transport, t)\n\tvar f *kratos.LoginFlow\n\tif isAPI {\n\t\tf = InitializeLoginFlowViaAPICtx(ctx, t, hc, publicTS, forced, opts...)\n\t} else {\n\t\tf = InitializeLoginFlowViaBrowserCtx(ctx, t, hc, publicTS, forced, isSPA, false, false, opts...)\n\t}\n\n\ttime.Sleep(time.Millisecond) // add a bit of delay to allow `1ns` to time out.\n\n\tpayload := SDKFormFieldsToURLValues(f.Ui.Nodes)\n\twithValues(payload)\n\tb, res := LoginMakeRequestCtx(ctx, t, isAPI, isSPA, f, hc, EncodeFormAsJSON(t, isAPI, payload))\n\tassert.EqualValuesf(t, expectedStatusCode, res.StatusCode, \"%s\", b)\n\tassert.Containsf(t, res.Request.URL.String(), expectedURL, \"%+v\\n\\t%s\", res.Request, b)\n\n\tt.Logf(\"%+v\", res.Header)\n\n\treturn b\n}\n"
  },
  {
    "path": "pkg/testhelpers/selfservice_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/ioutilx\"\n)\n\nfunc NewVerificationUIFlowEchoServer(t *testing.T, reg driver.Registry) *httptest.Server {\n\tctx := context.Background()\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\te, err := reg.VerificationFlowPersister().GetVerificationFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"flow\")))\n\t\trequire.NoError(t, err)\n\t\treg.Writer().Write(w, r, e)\n\t}))\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceVerificationUI, ts.URL+\"/verification-ts\")\n\tt.Cleanup(ts.Close)\n\treturn ts\n}\n\nfunc GetVerificationFlow(t *testing.T, client *http.Client, ts *httptest.Server) *kratos.VerificationFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\n\tres, err := client.Get(ts.URL + verification.RouteInitBrowserFlow)\n\trequire.NoError(t, err)\n\trequire.NoError(t, res.Body.Close())\n\n\trs, _, err := publicClient.FrontendAPI.GetVerificationFlow(context.Background()).Id(res.Request.URL.Query().Get(\"flow\")).Execute()\n\trequire.NoErrorf(t, err, \"%s\", res.Request.URL.String())\n\tassert.NotEmpty(t, rs.Active)\n\n\treturn rs\n}\n\nfunc InitializeVerificationFlowViaBrowser(t *testing.T, client *http.Client, isSPA bool, ts *httptest.Server) *kratos.VerificationFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\treq, err := http.NewRequest(\"GET\", ts.URL+verification.RouteInitBrowserFlow, nil)\n\trequire.NoError(t, err)\n\n\tif isSPA {\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t}\n\trequire.NoError(t, err)\n\n\tres, err := client.Do(req)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\n\tif isSPA {\n\t\tvar f kratos.VerificationFlow\n\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&f))\n\t\treturn &f\n\t}\n\n\trs, _, err := publicClient.FrontendAPI.GetVerificationFlow(context.Background()).Id(res.Request.URL.Query().Get(\"flow\")).Execute()\n\trequire.NoError(t, err)\n\tassert.NotEmpty(t, rs.Active)\n\n\treturn rs\n}\n\nfunc InitializeVerificationFlowViaAPI(t *testing.T, client *http.Client, ts *httptest.Server) *kratos.VerificationFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\n\trs, _, err := publicClient.FrontendAPI.CreateNativeVerificationFlow(context.Background()).Execute()\n\trequire.NoError(t, err)\n\tassert.NotEmpty(t, rs.Active)\n\n\treturn rs\n}\n\nfunc VerificationMakeRequest(\n\tt *testing.T,\n\tisAPI bool,\n\tf *kratos.VerificationFlow,\n\thc *http.Client,\n\tvalues string,\n) (string, *http.Response) {\n\trequire.NotEmpty(t, f.Ui.Action)\n\n\tres, err := hc.Do(NewPostRequest(t, isAPI, f.Ui.Action, bytes.NewBufferString(values)))\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\n\treturn string(ioutilx.MustReadAll(res.Body)), res\n}\n\n// SubmitVerificationForm initiates a registration flow (for Browser and API!), fills out the form and modifies\n// the form values with `withValues`, and submits the form. If completed, it will return the flow as JSON.\nfunc SubmitVerificationForm(\n\tt *testing.T,\n\tisAPI bool,\n\tisSPA bool,\n\thc *http.Client,\n\tpublicTS *httptest.Server,\n\twithValues func(v url.Values),\n\texpectedStatusCode int,\n\texpectedURL string,\n) string {\n\tvar f *kratos.VerificationFlow\n\tif isAPI {\n\t\tf = InitializeVerificationFlowViaAPI(t, hc, publicTS)\n\t} else {\n\t\tf = InitializeVerificationFlowViaBrowser(t, hc, isSPA, publicTS)\n\t}\n\n\ttime.Sleep(time.Millisecond) // add a bit of delay to allow `1ns` to time out.\n\n\tformPayload := SDKFormFieldsToURLValues(f.Ui.Nodes)\n\twithValues(formPayload)\n\n\tb, res := VerificationMakeRequest(t, isAPI || isSPA, f, hc, EncodeFormAsJSON(t, isAPI || isSPA, formPayload))\n\tassert.EqualValues(t, expectedStatusCode, res.StatusCode, \"%s\", b)\n\tassert.Contains(t, res.Request.URL.String(), expectedURL, \"%+v\\n\\t%s\", res.Request, b)\n\n\treturn b\n}\n"
  },
  {
    "path": "pkg/testhelpers/selfservice_registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/assertx\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\n\t\"github.com/ory/x/ioutilx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc NewRegistrationUIFlowEchoServer(t *testing.T, reg driver.Registry) *httptest.Server {\n\tctx := context.Background()\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\te, err := reg.RegistrationFlowPersister().GetRegistrationFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"flow\")))\n\t\trequire.NoError(t, err)\n\t\treg.Writer().Write(w, r, e)\n\t}))\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceRegistrationUI, ts.URL+\"/registration-ts\")\n\tt.Cleanup(ts.Close)\n\treturn ts\n}\n\n// Deprecated: Use context-based InitializeRegistrationFlowViaBrowserCtx instead\nfunc InitializeRegistrationFlowViaBrowser(t *testing.T, client *http.Client, ts *httptest.Server, isSPA bool, expectInitError bool, expectGetError bool, opts ...InitFlowWithOption) *kratos.RegistrationFlow {\n\treturn InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, client, ts, isSPA, expectInitError, expectGetError, opts...)\n}\n\nfunc InitializeRegistrationFlowViaBrowserCtx(ctx context.Context, t *testing.T, client *http.Client, ts *httptest.Server, isSPA bool, expectInitError bool, expectGetError bool, opts ...InitFlowWithOption) *kratos.RegistrationFlow {\n\treq, err := http.NewRequest(\"GET\", getURLFromInitOptions(ts, registration.RouteInitBrowserFlow, false, opts...), nil)\n\trequire.NoError(t, err)\n\treq = req.WithContext(ctx)\n\n\tif isSPA {\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t}\n\n\tres, err := client.Do(req)\n\trequire.NoError(t, err)\n\tbody := x.MustReadAll(res.Body)\n\tif isSPA {\n\t\trequire.True(t, gjson.ValidBytes(body), \"body is not valid JSON: %s\", string(body))\n\t}\n\trequire.NoError(t, res.Body.Close())\n\tif expectInitError {\n\t\trequire.Equal(t, 200, res.StatusCode)\n\t\trequire.NotNil(t, res.Request.URL)\n\t\trequire.Contains(t, res.Request.URL.String(), \"error-ts\")\n\t}\n\n\tflowID := res.Request.URL.Query().Get(\"flow\")\n\tif isSPA {\n\t\tflowID = gjson.GetBytes(body, \"id\").String()\n\t}\n\n\trs, _, err := NewSDKCustomClient(ts, client).FrontendAPI.GetRegistrationFlow(ctx).Id(flowID).Execute()\n\tif expectGetError {\n\t\trequire.Error(t, err)\n\t\trequire.Nil(t, rs)\n\t} else {\n\t\trequire.NoError(t, err)\n\t\tassert.Empty(t, rs.Active)\n\t}\n\treturn rs\n}\n\nfunc InitializeRegistrationFlowViaAPIExpectError(t *testing.T, client *http.Client, ts *httptest.Server, opts ...InitFlowWithOption) {\n\to := new(initFlowOptions).apply(opts)\n\n\t_, _, err := NewSDKCustomClient(ts, client).FrontendAPI.CreateNativeRegistrationFlow(context.Background()).IdentitySchema(o.identitySchema).Execute()\n\trequire.Error(t, err)\n}\n\n// Deprecated: Use context-based InitializeRegistrationFlowViaAPICtx instead\nfunc InitializeRegistrationFlowViaAPI(t *testing.T, client *http.Client, ts *httptest.Server, opts ...InitFlowWithOption) *kratos.RegistrationFlow {\n\treturn InitializeRegistrationFlowViaAPICtx(context.Background(), t, client, ts, opts...)\n}\n\nfunc InitializeRegistrationFlowViaAPICtx(ctx context.Context, t *testing.T, client *http.Client, ts *httptest.Server, opts ...InitFlowWithOption) *kratos.RegistrationFlow {\n\to := new(initFlowOptions).apply(opts)\n\n\trs, _, err := NewSDKCustomClient(ts, client).FrontendAPI.CreateNativeRegistrationFlow(ctx).IdentitySchema(o.identitySchema).Execute()\n\trequire.NoError(t, err)\n\tassert.Empty(t, rs.Active)\n\treturn rs\n}\n\nfunc GetRegistrationFlow(t *testing.T, client *http.Client, ts *httptest.Server, flowID string) *kratos.RegistrationFlow {\n\trs, _, err := NewSDKCustomClient(ts, client).FrontendAPI.GetRegistrationFlow(context.Background()).Id(flowID).Execute()\n\trequire.NoError(t, err)\n\tassert.Empty(t, rs.Active)\n\treturn rs\n}\n\nfunc RegistrationMakeRequest(\n\tt *testing.T,\n\tisAPI bool,\n\tisSPA bool,\n\tf *kratos.RegistrationFlow,\n\thc *http.Client,\n\tvalues string,\n) (string, *http.Response) {\n\treturn RegistrationMakeRequestCtx(t.Context(), t, isAPI, isSPA, f, hc, values)\n}\n\nfunc RegistrationMakeRequestCtx(\n\tctx context.Context,\n\tt *testing.T,\n\tisAPI bool,\n\tisSPA bool,\n\tf *kratos.RegistrationFlow,\n\thc *http.Client,\n\tvalues string,\n) (string, *http.Response) {\n\trequire.NotEmpty(t, f.Ui.Action)\n\n\treq := NewPostRequest(t, isAPI, f.Ui.Action, bytes.NewBufferString(values))\n\tif isSPA {\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\treq.Header.Set(\"Sec-Fetch-Mode\", \"cors\")\n\t}\n\treq = req.WithContext(ctx)\n\n\tres, err := hc.Do(req)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\n\treturn string(ioutilx.MustReadAll(res.Body)), res\n}\n\n// Deprecated: Use context-based SubmitRegistrationFormCtx instead\nfunc SubmitRegistrationForm(\n\tt *testing.T,\n\tisAPI bool,\n\thc *http.Client,\n\tpublicTS *httptest.Server,\n\twithValues func(v url.Values),\n\tisSPA bool,\n\texpectedStatusCode int,\n\texpectedURL string,\n\topts ...InitFlowWithOption,\n) string {\n\treturn SubmitRegistrationFormCtx(context.Background(), t, isAPI, hc, publicTS, withValues, isSPA, expectedStatusCode, expectedURL, opts...)\n}\n\n// SubmitRegistrationFormCtx (for Browser and API!), fills out the form and modifies\n// the form values with `withValues`, and submits the form. Returns the body and checks for expectedStatusCode and\n// expectedURL on completion\nfunc SubmitRegistrationFormCtx(\n\tctx context.Context,\n\tt *testing.T,\n\tisAPI bool,\n\thc *http.Client,\n\tpublicTS *httptest.Server,\n\twithValues func(v url.Values),\n\tisSPA bool,\n\texpectedStatusCode int,\n\texpectedURL string,\n\topts ...InitFlowWithOption,\n) string {\n\tif hc == nil {\n\t\thc = new(http.Client)\n\t}\n\n\thc.Transport = NewTransportWithLogger(hc.Transport, t)\n\tvar payload *kratos.RegistrationFlow\n\tif isAPI {\n\t\tpayload = InitializeRegistrationFlowViaAPICtx(ctx, t, hc, publicTS, opts...)\n\t} else {\n\t\tpayload = InitializeRegistrationFlowViaBrowserCtx(ctx, t, hc, publicTS, isSPA, false, false, opts...)\n\t}\n\n\ttime.Sleep(time.Millisecond) // add a bit of delay to allow `1ns` to time out.\n\n\tvalues := SDKFormFieldsToURLValues(payload.Ui.Nodes)\n\twithValues(values)\n\tb, res := RegistrationMakeRequest(t, isAPI, isSPA, payload, hc, EncodeFormAsJSON(t, isAPI, values))\n\tassert.EqualValues(t, expectedStatusCode, res.StatusCode, assertx.PrettifyJSONPayload(t, b))\n\tassert.Containsf(t, res.Request.URL.String(), expectedURL, \"%+v\\n\\t%s\", res.Request, assertx.PrettifyJSONPayload(t, b))\n\treturn b\n}\n"
  },
  {
    "path": "pkg/testhelpers/selfservice_settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\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/require\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc NewSettingsUIFlowEchoServer(t *testing.T, reg driver.Registry) *httptest.Server {\n\tctx := context.Background()\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\te, err := reg.SettingsFlowPersister().GetSettingsFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"flow\")))\n\t\trequire.NoError(t, err)\n\t\treg.Writer().Write(w, r, e)\n\t}))\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceSettingsURL, ts.URL+\"/settings-ts\")\n\tt.Cleanup(ts.Close)\n\treturn ts\n}\n\nfunc InitializeSettingsFlowViaBrowser(t *testing.T, client *http.Client, isSPA bool, ts *httptest.Server) *kratos.SettingsFlow {\n\tt.Helper()\n\tpublicClient := NewSDKCustomClient(ts, client)\n\n\treq, err := http.NewRequest(\"GET\", ts.URL+settings.RouteInitBrowserFlow, nil)\n\trequire.NoError(t, err)\n\n\tif isSPA {\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t}\n\n\tres, err := client.Do(req)\n\trequire.NoError(t, err)\n\n\tvar flowID string\n\tif isSPA {\n\t\tflowID = gjson.GetBytes(x.MustReadAll(res.Body), \"id\").String()\n\t} else {\n\t\tflowID = res.Request.URL.Query().Get(\"flow\")\n\t}\n\n\trequire.NoError(t, res.Body.Close())\n\n\trs, res, err := publicClient.FrontendAPI.GetSettingsFlow(context.Background()).Id(flowID).Execute()\n\trequire.NoError(t, err, \"%s\", ioutilx.MustReadAll(res.Body))\n\tassert.Empty(t, rs.Active)\n\n\treturn rs\n}\n\nfunc InitializeSettingsFlowViaAPI(t *testing.T, client *http.Client, ts *httptest.Server) *kratos.SettingsFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\n\trs, _, err := publicClient.FrontendAPI.CreateNativeSettingsFlow(context.Background()).Execute()\n\trequire.NoError(t, err)\n\tassert.Empty(t, rs.Active)\n\n\treturn rs\n}\n\nfunc EncodeFormAsJSON(t *testing.T, isApi bool, values url.Values) (payload string) {\n\tif !isApi {\n\t\treturn values.Encode()\n\t}\n\tpayload = \"{}\"\n\tfor k := range values {\n\t\tvar err error\n\t\tpayload, err = sjson.Set(payload, strings.ReplaceAll(k, \".\", \"\\\\.\"), values.Get(k))\n\t\trequire.NoError(t, err)\n\t}\n\treturn payload\n}\n\nfunc ExpectStatusCode(isAPI bool, api, browser int) int {\n\tif isAPI {\n\t\treturn api\n\t}\n\treturn browser\n}\n\nfunc ExpectURL(isAPI bool, api, browser string) string {\n\tif isAPI {\n\t\treturn api\n\t}\n\treturn browser\n}\n\nfunc NewSettingsUITestServer(t *testing.T, conf *config.Config) *httptest.Server {\n\trouter := http.NewServeMux()\n\trouter.HandleFunc(\"GET /settings\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusNoContent)\n\t})\n\trouter.HandleFunc(\"GET /login\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusUnauthorized)\n\t})\n\tts := httptest.NewServer(router)\n\tt.Cleanup(ts.Close)\n\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsURL, ts.URL+\"/settings\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginUI, ts.URL+\"/login\")\n\n\treturn ts\n}\n\nfunc NewSettingsUIEchoServer(t *testing.T, reg *driver.RegistryDefault) *httptest.Server {\n\trouter := http.NewServeMux()\n\trouter.HandleFunc(\"GET /settings\", func(w http.ResponseWriter, r *http.Request) {\n\t\tres, err := reg.SettingsFlowPersister().GetSettingsFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"flow\")))\n\t\trequire.NoError(t, err)\n\t\treg.Writer().Write(w, r, res)\n\t})\n\n\trouter.HandleFunc(\"GET /login\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusUnauthorized)\n\t})\n\tts := httptest.NewServer(router)\n\tt.Cleanup(ts.Close)\n\n\tctx := context.Background()\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceSettingsURL, ts.URL+\"/settings\")\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceLoginUI, ts.URL+\"/login\")\n\n\treturn ts\n}\n\nfunc NewSettingsLoginAcceptAPIServer(t *testing.T, publicClient *kratos.APIClient, conf *config.Config) *httptest.Server {\n\tvar called int\n\tloginTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\trequire.Equal(t, 0, called)\n\t\tcalled++\n\n\t\tconf.MustSet(r.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"5m\")\n\n\t\tres, _, err := publicClient.FrontendAPI.GetLoginFlow(context.Background()).Id(r.URL.Query().Get(\"flow\")).Execute()\n\n\t\trequire.NoError(t, err)\n\t\trequire.NotEmpty(t, res.RequestUrl)\n\n\t\tredir := urlx.ParseOrPanic(res.RequestUrl).Query().Get(\"return_to\")\n\t\tt.Logf(\"Redirecting to: %s\", redir)\n\t\thttp.Redirect(w, r, redir, http.StatusFound)\n\t}))\n\tt.Cleanup(func() {\n\t\tloginTS.Close()\n\t})\n\tctx := context.Background()\n\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginUI, loginTS.URL+\"/login\")\n\treturn loginTS\n}\n\n// AddAndLoginIdentity adds the given identity to the store (like a registration flow) and returns an http.Client\n// which contain their session.\nfunc AddAndLoginIdentity(t *testing.T, reg *driver.RegistryDefault, i *identity.Identity) (uuid.UUID, *http.Client) {\n\tloggedIn := AddAndLoginIdentities(t, reg, map[string]*identity.Identity{\"\": i})[\"\"]\n\treturn loggedIn.ID, loggedIn.Client\n}\n\n// AddAndLoginIdentities adds the given identities to the store (like a registration flow) and returns http.Clients\n// which contain their sessions.\nfunc AddAndLoginIdentities(t *testing.T, reg *driver.RegistryDefault, ids map[string]*identity.Identity) map[string]struct {\n\tClient *http.Client\n\tID     uuid.UUID\n} {\n\tresult := map[string]struct {\n\t\tClient *http.Client\n\t\tID     uuid.UUID\n\t}{}\n\tr := http.NewServeMux()\n\ts := httptest.NewServer(r)\n\tdefer s.Close()\n\n\tfor k, i := range ids {\n\t\ti.ID = uuid.Must(uuid.NewV4())\n\t\ttid := x.NewUUID().String()\n\t\troute, _ := MockSessionCreateHandlerWithIdentity(t, reg, i)\n\t\tlocation := \"/sessions/set/\" + tid\n\n\t\tr.Handle(\"GET \"+location, route)\n\t\tresult[k] = struct {\n\t\t\tClient *http.Client\n\t\t\tID     uuid.UUID\n\t\t}{\n\t\t\tClient: NewSessionClient(t, s.URL+location),\n\t\t\tID:     i.ID,\n\t\t}\n\t}\n\treturn result\n}\n\nfunc SettingsMakeRequest(\n\tt *testing.T,\n\tisAPI bool,\n\tisSPA bool,\n\tf *kratos.SettingsFlow,\n\thc *http.Client,\n\tvalues string,\n) (string, *http.Response) {\n\trequire.NotEmpty(t, f.Ui.Action)\n\n\treq := NewPostRequest(t, isSPA || isAPI, f.Ui.Action, bytes.NewBufferString(values))\n\tif isSPA || isAPI {\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t}\n\tif isSPA {\n\t\treq.Header.Set(\"Sec-Fetch-Mode\", \"cors\")\n\t}\n\n\tres, err := hc.Do(req)\n\trequire.NoError(t, err, \"action: %s\", f.Ui.Action)\n\tdefer func() { _ = res.Body.Close() }()\n\n\treturn string(ioutilx.MustReadAll(res.Body)), res\n}\n\n// SubmitSettingsForm initiates a settings flow (for Browser and API!), fills out the form and modifies\n// the form values with `withValues`, and submits the form. If completed, it will return the flow as JSON.\nfunc SubmitSettingsForm(\n\tt *testing.T,\n\tisAPI bool,\n\tisSPA bool,\n\thc *http.Client,\n\tpublicTS *httptest.Server,\n\twithValues func(v url.Values),\n\texpectedStatusCode int,\n\texpectedURL string,\n) string {\n\tif hc == nil {\n\t\thc = new(http.Client)\n\t\tif !isAPI {\n\t\t\thc = NewClientWithCookies(t)\n\t\t}\n\t}\n\n\thc.Transport = NewTransportWithLogger(hc.Transport, t)\n\tvar payload *kratos.SettingsFlow\n\tif isAPI {\n\t\tpayload = InitializeSettingsFlowViaAPI(t, hc, publicTS)\n\t} else {\n\t\tpayload = InitializeSettingsFlowViaBrowser(t, hc, isSPA, publicTS)\n\t}\n\n\ttime.Sleep(time.Millisecond * 10) // add a bit of delay to allow `1ns` to time out.\n\n\tvalues := SDKFormFieldsToURLValues(payload.Ui.Nodes)\n\twithValues(values)\n\n\tb, res := SettingsMakeRequest(t, isAPI, isSPA, payload, hc, EncodeFormAsJSON(t, isAPI || isSPA, values))\n\tassert.EqualValues(t, expectedStatusCode, res.StatusCode, \"%s\", b)\n\tassert.Contains(t, res.Request.URL.String(), expectedURL, \"%+v\\n\\t%s\", res.Request, b)\n\n\treturn b\n}\n"
  },
  {
    "path": "pkg/testhelpers/selfservice_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/tidwall/gjson\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\n\t\"github.com/ory/x/ioutilx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc NewVerifyAfterHookWebHookTarget(ctx context.Context, t *testing.T, conf *config.Config, assert func(t *testing.T, body []byte)) {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tmsg, err := io.ReadAll(r.Body)\n\t\trequire.NoError(t, err)\n\n\t\tassert(t, msg)\n\t}))\n\tbefore := conf.GetProvider(ctx).Get(config.ViperKeySelfServiceVerificationAfter + \".hooks\")\n\t// A hook to ensure that the verification hook is called with the correct data\n\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationAfter+\".hooks\", []map[string]interface{}{\n\t\t{\n\t\t\t\"hook\": \"web_hook\",\n\t\t\t\"config\": map[string]interface{}{\n\t\t\t\t\"url\":    ts.URL,\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"body\":   \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgICBpZGVudGl0eTogY3R4LmlkZW50aXR5Cn0=\",\n\t\t\t},\n\t\t},\n\t})\n\n\tt.Cleanup(ts.Close)\n\tt.Cleanup(func() {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationAfter+\".hooks\", before)\n\t})\n}\n\nfunc NewRecoveryAfterHookWebHookTarget(ctx context.Context, t *testing.T, conf *config.Config, assert func(t *testing.T, body []byte)) {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tmsg, err := io.ReadAll(r.Body)\n\t\trequire.NoError(t, err)\n\n\t\tassert(t, msg)\n\t}))\n\n\t// A hook to ensure that the recovery hook is called with the correct data\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryAfter+\".hooks\", []map[string]interface{}{\n\t\t{\n\t\t\t\"hook\": \"web_hook\",\n\t\t\t\"config\": map[string]interface{}{\n\t\t\t\t\"url\":    ts.URL,\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"body\":   \"base64://ZnVuY3Rpb24oY3R4KSB7CiAgICBpZGVudGl0eTogY3R4LmlkZW50aXR5Cn0=\",\n\t\t\t},\n\t\t},\n\t})\n\n\tt.Cleanup(ts.Close)\n\tt.Cleanup(func() {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryAfter+\".hooks\", []map[string]interface{}{})\n\t})\n}\n\nfunc NewRecoveryUIFlowEchoServer(t *testing.T, reg driver.Registry) *httptest.Server {\n\tctx := context.Background()\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\te, err := reg.RecoveryFlowPersister().GetRecoveryFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"flow\")))\n\t\trequire.NoError(t, err)\n\t\treg.Writer().Write(w, r, e)\n\t}))\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceRecoveryUI, ts.URL+\"/recovery-ts\")\n\tt.Cleanup(ts.Close)\n\treturn ts\n}\n\nfunc GetRecoveryFlowForType(t *testing.T, client *http.Client, ts *httptest.Server, ft flow.Type) *kratos.RecoveryFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\n\tvar url string\n\tswitch ft {\n\tcase flow.TypeBrowser:\n\t\turl = ts.URL + recovery.RouteInitBrowserFlow\n\tcase flow.TypeAPI:\n\t\turl = ts.URL + recovery.RouteInitAPIFlow\n\tdefault:\n\t\tt.Errorf(\"unknown type: %s\", ft)\n\t\tt.FailNow()\n\t}\n\n\tres, err := client.Get(url)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\n\tvar flowID string\n\tswitch ft {\n\tcase flow.TypeBrowser:\n\t\tflowID = res.Request.URL.Query().Get(\"flow\")\n\tcase flow.TypeAPI:\n\t\tflowID = gjson.GetBytes(ioutilx.MustReadAll(res.Body), \"id\").String()\n\tdefault:\n\t\tt.Errorf(\"unknown type: %s\", ft)\n\t\tt.FailNow()\n\t}\n\trequire.NotEmpty(t, flowID, \"expected to receive a flow id, got none. %s\", ioutilx.MustReadAll(res.Body))\n\n\trs, _, err := publicClient.FrontendAPI.GetRecoveryFlow(context.Background()).\n\t\tId(flowID).\n\t\tExecute()\n\trequire.NoError(t, err, \"expected no error when fetching recovery flow: %s\", err)\n\tassert.NotEmpty(t, rs.Active)\n\n\treturn rs\n}\n\nfunc GetRecoveryFlow(t *testing.T, client *http.Client, ts *httptest.Server) *kratos.RecoveryFlow {\n\treturn GetRecoveryFlowForType(t, client, ts, flow.TypeBrowser)\n}\n\nfunc InitializeRecoveryFlowViaBrowser(t *testing.T, client *http.Client, isSPA bool, ts *httptest.Server, values url.Values) *kratos.RecoveryFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\n\tu := ts.URL + recovery.RouteInitBrowserFlow\n\tif values != nil {\n\t\tu += \"?\" + values.Encode()\n\t}\n\treq, err := http.NewRequest(\"GET\", u, nil)\n\trequire.NoError(t, err)\n\n\tif isSPA {\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t}\n\n\tres, err := client.Do(req)\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\n\tif isSPA {\n\t\tvar f kratos.RecoveryFlow\n\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&f))\n\t\treturn &f\n\t}\n\n\trequire.NoError(t, res.Body.Close())\n\trs, _, err := publicClient.FrontendAPI.GetRecoveryFlow(context.Background()).Id(res.Request.URL.Query().Get(\"flow\")).Execute()\n\trequire.NoError(t, err)\n\tassert.NotEmpty(t, rs.Active)\n\n\treturn rs\n}\n\nfunc InitializeRecoveryFlowViaAPI(t *testing.T, client *http.Client, ts *httptest.Server) *kratos.RecoveryFlow {\n\tpublicClient := NewSDKCustomClient(ts, client)\n\n\trs, _, err := publicClient.FrontendAPI.CreateNativeRecoveryFlow(context.Background()).Execute()\n\trequire.NoError(t, err)\n\tassert.NotEmpty(t, rs.Active)\n\n\treturn rs\n}\n\nfunc RecoveryMakeRequest(\n\tt *testing.T,\n\tisAPI bool,\n\tf *kratos.RecoveryFlow,\n\thc *http.Client,\n\tvalues string,\n) (string, *http.Response) {\n\trequire.NotEmpty(t, f.Ui.Action)\n\n\tres, err := hc.Do(NewPostRequest(t, isAPI, f.Ui.Action, bytes.NewBufferString(values)))\n\trequire.NoError(t, err)\n\tdefer func() { _ = res.Body.Close() }()\n\n\treturn string(ioutilx.MustReadAll(res.Body)), res\n}\n\n// SubmitRecoveryForm initiates a registration flow (for Browser and API!), fills out the form and modifies\n// the form values with `withValues`, and submits the form. If completed, it will return the flow as JSON.\nfunc SubmitRecoveryForm(\n\tt *testing.T,\n\tisAPI bool,\n\tisSPA bool,\n\thc *http.Client,\n\tpublicTS *httptest.Server,\n\twithValues func(v url.Values),\n\texpectedStatusCode int,\n\texpectedURL string,\n) string {\n\thc.Transport = NewTransportWithLogger(hc.Transport, t)\n\tvar f *kratos.RecoveryFlow\n\tif isAPI {\n\t\tf = InitializeRecoveryFlowViaAPI(t, hc, publicTS)\n\t} else {\n\t\tf = InitializeRecoveryFlowViaBrowser(t, hc, isSPA, publicTS, nil)\n\t}\n\n\ttime.Sleep(time.Millisecond) // add a bit of delay to allow `1ns` to time out.\n\n\tformPayload := SDKFormFieldsToURLValues(f.Ui.Nodes)\n\twithValues(formPayload)\n\n\tb, res := RecoveryMakeRequest(t, isAPI || isSPA, f, hc, EncodeFormAsJSON(t, isAPI || isSPA, formPayload))\n\tassert.EqualValues(t, expectedStatusCode, res.StatusCode, \"%s\", b)\n\tassert.Contains(t, res.Request.URL.String(), expectedURL, \"%+v\\n\\t%s\", res.Request, b)\n\n\treturn b\n}\n\nfunc PersistNewRecoveryFlow(t *testing.T, strategies recovery.Strategies, conf *config.Config, reg *driver.RegistryDefault) *recovery.Flow {\n\tt.Helper()\n\treq := NewTestHTTPRequest(t, \"GET\", conf.SelfPublicURL(context.Background()).String()+\"/test\", nil)\n\tf, err := recovery.NewFlow(conf, conf.SelfServiceFlowRecoveryRequestLifespan(context.Background()), reg.GenerateCSRFToken(req), req, strategies, flow.TypeBrowser)\n\trequire.NoError(t, err, \"Expected no error when creating a new recovery flow: %s\", err)\n\n\terr = reg.RecoveryFlowPersister().CreateRecoveryFlow(context.Background(), f)\n\trequire.NoError(t, err, \"Expected no error when persisting a new recover flow: %s\", err)\n\treturn f\n}\n"
  },
  {
    "path": "pkg/testhelpers/server.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/urfave/negroni\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/httprouterx\"\n)\n\nfunc NewKratosServer(t *testing.T, reg driver.Registry) (public, admin *httptest.Server) {\n\treturn NewKratosServerWithRouters(t, reg, httprouterx.NewTestRouterPublic(t), httprouterx.NewTestRouterAdminWithPrefix(t))\n}\n\nfunc NewKratosServerWithCSRF(t *testing.T, reg driver.Registry) (public, admin *httptest.Server) {\n\tpublic, admin, _, _ = NewKratosServerWithCSRFAndRouters(t, reg)\n\treturn\n}\n\nfunc NewKratosServerWithCSRFAndRouters(t *testing.T, reg driver.Registry) (public, admin *httptest.Server, rp *httprouterx.RouterPublic, ra *httprouterx.RouterAdmin) {\n\trp, ra = httprouterx.NewTestRouterPublic(t), httprouterx.NewTestRouterAdminWithPrefix(t)\n\tcsrfHandler := nosurfx.NewTestCSRFHandler(rp, reg)\n\treg.WithCSRFHandler(csrfHandler)\n\n\tran := negroni.New()\n\tran.UseFunc(httprouterx.AddAdminPrefixIfNotPresentNegroni)\n\tran.UseHandler(ra)\n\n\trpn := negroni.New()\n\trpn.UseFunc(x.HTTPLoaderContextMiddleware(reg))\n\trpn.UseHandler(rp)\n\n\tpublic = httptest.NewServer(nosurfx.NewTestCSRFHandler(rpn, reg))\n\tadmin = httptest.NewServer(ran)\n\n\tpublic.URL = strings.ReplaceAll(public.URL, \"127.0.0.1\", \"localhost\")\n\n\tinitKratosServers(t, reg, public, admin, rp, ra)\n\n\tt.Cleanup(public.Close)\n\tt.Cleanup(admin.Close)\n\treturn\n}\n\nfunc NewKratosServerWithRouters(t *testing.T, reg driver.Registry, rp *httprouterx.RouterPublic, ra *httprouterx.RouterAdmin) (public, admin *httptest.Server) {\n\tnp := negroni.New()\n\tnp.UseFunc(httprouterx.TrimTrailingSlashNegroni)\n\tnp.UseHandler(rp)\n\n\tna := negroni.New()\n\tna.UseFunc(httprouterx.AddAdminPrefixIfNotPresentNegroni)\n\tna.UseFunc(httprouterx.TrimTrailingSlashNegroni)\n\tna.UseHandler(ra)\n\n\tpublic = httptest.NewServer(np)\n\tadmin = httptest.NewServer(na)\n\n\tinitKratosServers(t, reg, public, admin, rp, ra)\n\n\tt.Cleanup(public.Close)\n\tt.Cleanup(admin.Close)\n\treturn\n}\n\nfunc initKratosServers(t *testing.T, reg driver.Registry, public, admin *httptest.Server, rp *httprouterx.RouterPublic, ra *httprouterx.RouterAdmin) {\n\tctx := t.Context()\n\tif len(reg.Config().GetProvider(ctx).String(config.ViperKeySelfServiceLoginUI)) == 0 {\n\t\treg.Config().MustSet(ctx, config.ViperKeySelfServiceLoginUI, \"http://NewKratosServerWithRouters/you-forgot-to-set-me/login\")\n\t}\n\treg.Config().MustSet(ctx, config.ViperKeyPublicBaseURL, public.URL)\n\treg.Config().MustSet(ctx, config.ViperKeyAdminBaseURL, admin.URL)\n\n\treg.RegisterPublicRoutes(ctx, rp)\n\treg.RegisterAdminRoutes(ctx, ra)\n}\n"
  },
  {
    "path": "pkg/testhelpers/session.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\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/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/contextx\"\n)\n\ntype SessionLifespanProvider struct {\n\te time.Duration\n}\n\nfunc (p *SessionLifespanProvider) SessionLifespan(context.Context) time.Duration {\n\treturn p.e\n}\n\nfunc NewSessionClient(t *testing.T, u string) *http.Client {\n\tc := NewClientWithCookies(t)\n\tMockHydrateCookieClient(t, c, u)\n\treturn c\n}\n\nfunc maybePersistSession(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) {\n\tid, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(ctx, sess.Identity.ID)\n\tif err != nil {\n\t\trequire.NoError(t, sess.Identity.SetAvailableAAL(ctx, reg.IdentityManager()))\n\t\trequire.NoError(t, reg.IdentityManager().Create(ctx, sess.Identity))\n\t\tid, err = reg.PrivilegedIdentityPool().GetIdentityConfidential(ctx, sess.Identity.ID)\n\t\trequire.NoError(t, err)\n\t}\n\tsess.Identity = id\n\tsess.IdentityID = id.ID\n\n\trequire.NoError(t, err, reg.SessionPersister().UpsertSession(ctx, sess))\n}\n\nfunc NewHTTPClientWithSessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client {\n\tmaybePersistSession(ctx, t, reg, sess)\n\n\tvar handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\trequire.NoError(t, reg.SessionManager().IssueCookie(ctx, w, r, sess))\n\t})\n\n\tif _, ok := reg.CSRFHandler().(*nosurf.CSRFHandler); ok {\n\t\thandler = nosurf.New(handler)\n\t}\n\n\tts := httptest.NewServer(handler)\n\tdefer ts.Close()\n\n\tc := NewClientWithCookies(t)\n\tMockHydrateCookieClient(t, c, ts.URL)\n\treturn c\n}\n\nfunc NewHTTPClientWithSessionCookieLocalhost(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client {\n\tmaybePersistSession(ctx, t, reg, sess)\n\n\tvar handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\trequire.NoError(t, reg.SessionManager().IssueCookie(ctx, w, r, sess))\n\t})\n\n\tif _, ok := reg.CSRFHandler().(*nosurf.CSRFHandler); ok {\n\t\thandler = nosurf.New(handler)\n\t}\n\n\tts := httptest.NewServer(handler)\n\tdefer ts.Close()\n\n\tc := NewClientWithCookies(t)\n\n\tts.URL = strings.ReplaceAll(ts.URL, \"127.0.0.1\", \"localhost\")\n\tMockHydrateCookieClient(t, c, ts.URL)\n\treturn c\n}\n\nfunc NewNoRedirectHTTPClientWithSessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client {\n\tmaybePersistSession(ctx, t, reg, sess)\n\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\trequire.NoError(t, reg.SessionManager().IssueCookie(ctx, w, r, sess))\n\t}))\n\tdefer ts.Close()\n\n\tc := NewNoRedirectClientWithCookies(t)\n\n\tMockHydrateCookieClient(t, c, ts.URL)\n\treturn c\n}\n\nfunc NewTransportWithLogger(parent http.RoundTripper, t *testing.T) *TransportWithLogger {\n\treturn &TransportWithLogger{\n\t\tRoundTripper: parent,\n\t\tt:            t,\n\t}\n}\n\ntype TransportWithLogger struct {\n\thttp.RoundTripper\n\tt *testing.T\n}\n\nfunc (ct *TransportWithLogger) RoundTrip(req *http.Request) (*http.Response, error) {\n\tct.t.Logf(\"Made request to: %s\", req.URL.String())\n\tif ct.RoundTripper == nil {\n\t\treturn http.DefaultTransport.RoundTrip(req)\n\t}\n\treturn ct.RoundTripper.RoundTrip(req)\n}\n\nfunc NewHTTPClientWithSessionToken(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *http.Client {\n\tmaybePersistSession(ctx, t, reg, sess)\n\n\treturn &http.Client{\n\t\tTransport: NewTransportWithHeader(t, http.Header{\n\t\t\t\"Authorization\": {\"Bearer \" + sess.Token},\n\t\t}),\n\t}\n}\n\nfunc NewHTTPClientWithArbitrarySessionToken(ctx context.Context, t *testing.T, reg *driver.RegistryDefault) *http.Client {\n\treturn NewHTTPClientWithArbitrarySessionTokenAndTraits(ctx, t, reg, nil)\n}\n\nfunc NewHTTPClientWithArbitrarySessionTokenAndTraits(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, traits identity.Traits) *http.Client {\n\treq := NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil).WithContext(contextx.WithConfigValue(ctx, \"session.lifespan\", time.Hour))\n\ts, err := NewActiveSession(req, reg,\n\t\t&identity.Identity{ID: x.NewUUID(), State: identity.StateActive, Traits: traits, NID: x.NewUUID(), SchemaID: \"default\"},\n\t\ttime.Now(),\n\t\tidentity.CredentialsTypePassword,\n\t\tidentity.AuthenticatorAssuranceLevel1,\n\t)\n\trequire.NoError(t, err, \"Could not initialize session from identity.\")\n\n\treturn NewHTTPClientWithSessionToken(ctx, t, reg, s)\n}\n\nfunc NewHTTPClientWithArbitrarySessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault) *http.Client {\n\treq := NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\treq = req.WithContext(contextx.WithConfigValue(ctx, \"session.lifespan\", time.Hour))\n\tid := x.NewUUID()\n\ts, err := NewActiveSession(req, reg,\n\t\t&identity.Identity{ID: id, State: identity.StateActive, Traits: []byte(\"{}\"), Credentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\tidentity.CredentialsTypePassword: {Type: \"password\", Identifiers: []string{id.String()}, Config: []byte(`{\"hashed_password\":\"$2a$04$zvZz1zV\"}`)},\n\t\t}},\n\t\ttime.Now(),\n\t\tidentity.CredentialsTypePassword,\n\t\tidentity.AuthenticatorAssuranceLevel1,\n\t)\n\trequire.NoError(t, err, \"Could not initialize session from identity.\")\n\n\treturn NewHTTPClientWithSessionCookie(ctx, t, reg, s)\n}\n\nfunc NewNoRedirectHTTPClientWithArbitrarySessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault) *http.Client {\n\treq := NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\treq = req.WithContext(contextx.WithConfigValue(ctx, \"session.lifespan\", time.Hour))\n\tid := x.NewUUID()\n\ts, err := NewActiveSession(req, reg,\n\t\t&identity.Identity{ID: id, State: identity.StateActive,\n\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypePassword: {Type: \"password\", Identifiers: []string{id.String()}, Config: []byte(`{\"hashed_password\":\"$2a$04$zvZz1zV\"}`)},\n\t\t\t}},\n\t\ttime.Now(),\n\t\tidentity.CredentialsTypePassword,\n\t\tidentity.AuthenticatorAssuranceLevel1,\n\t)\n\trequire.NoError(t, err, \"Could not initialize session from identity.\")\n\n\treturn NewNoRedirectHTTPClientWithSessionCookie(ctx, t, reg, s)\n}\n\nfunc NewHTTPClientWithIdentitySessionCookie(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, id *identity.Identity) *http.Client {\n\treq := NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\treq = req.WithContext(contextx.WithConfigValue(ctx, \"session.lifespan\", time.Hour))\n\ts, err := NewActiveSession(req, reg,\n\t\tid,\n\t\ttime.Now(),\n\t\tidentity.CredentialsTypePassword,\n\t\tidentity.AuthenticatorAssuranceLevel1,\n\t)\n\trequire.NoError(t, err, \"Could not initialize session from identity.\")\n\n\treturn NewHTTPClientWithSessionCookie(ctx, t, reg, s)\n}\n\nfunc NewHTTPClientWithIdentitySessionCookieLocalhost(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, id *identity.Identity) *http.Client {\n\treq := NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\ts, err := NewActiveSession(req, reg,\n\t\tid,\n\t\ttime.Now(),\n\t\tidentity.CredentialsTypePassword,\n\t\tidentity.AuthenticatorAssuranceLevel1,\n\t)\n\trequire.NoError(t, err, \"Could not initialize session from identity.\")\n\n\treturn NewHTTPClientWithSessionCookieLocalhost(ctx, t, reg, s)\n}\n\nfunc NewHTTPClientWithIdentitySessionToken(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, id *identity.Identity) *http.Client {\n\treq := NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\treq = req.WithContext(contextx.WithConfigValue(ctx, \"session.lifespan\", time.Hour))\n\ts, err := NewActiveSession(req, reg,\n\t\tid,\n\t\ttime.Now(),\n\t\tidentity.CredentialsTypePassword,\n\t\tidentity.AuthenticatorAssuranceLevel1,\n\t)\n\trequire.NoError(t, err, \"Could not initialize session from identity.\")\n\n\treturn NewHTTPClientWithSessionToken(ctx, t, reg, s)\n}\n\nfunc EnsureAAL(t *testing.T, c *http.Client, ts *httptest.Server, aal string, methods ...string) {\n\tres, err := c.Get(ts.URL + session.RouteWhoami)\n\trequire.NoError(t, err)\n\tsess := x.MustReadAll(res.Body)\n\trequire.NoError(t, res.Body.Close())\n\tassert.EqualValues(t, aal, gjson.GetBytes(sess, \"authenticator_assurance_level\").String())\n\tfor _, method := range methods {\n\t\tassert.EqualValues(t, method, gjson.GetBytes(sess, \"authentication_methods.#(method==\"+method+\").method\").String())\n\t}\n\tassert.Len(t, gjson.GetBytes(sess, \"authentication_methods\").Array(), 1+len(methods))\n}\n\nfunc NewAuthorizedTransport(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, sess *session.Session) *TransportWithHeader {\n\tmaybePersistSession(ctx, t, reg, sess)\n\n\treturn NewTransportWithHeader(t, http.Header{\n\t\t\"Authorization\": {\"Bearer \" + sess.Token},\n\t})\n}\n\nfunc NewTransportWithHeader(t *testing.T, h http.Header) *TransportWithHeader {\n\tif t == nil {\n\t\tpanic(\"This function is for testing use only.\")\n\t}\n\treturn &TransportWithHeader{\n\t\tRoundTripper: http.DefaultTransport,\n\t\th:            h,\n\t}\n}\n\ntype TransportWithHeader struct {\n\thttp.RoundTripper\n\th http.Header\n}\n\nfunc (ct *TransportWithHeader) GetHeader() http.Header {\n\treturn ct.h\n}\n\nfunc (ct *TransportWithHeader) RoundTrip(req *http.Request) (*http.Response, error) {\n\tfor k := range ct.h {\n\t\treq.Header.Set(k, ct.h.Get(k))\n\t}\n\treturn ct.RoundTripper.RoundTrip(req)\n}\n\nfunc AssertNoCSRFCookieInResponse(t *testing.T, r *http.Response) {\n\tfound := false\n\tfor _, c := range r.Cookies() {\n\t\tif strings.HasPrefix(c.Name, \"csrf_token\") {\n\t\t\tfound = true\n\t\t}\n\t}\n\trequire.False(t, found)\n}\n"
  },
  {
    "path": "pkg/testhelpers/session_active.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n)\n\nfunc NewActiveSession(r *http.Request, reg interface {\n\tsession.ManagementProvider\n}, i *identity.Identity, authenticatedAt time.Time, completedLoginFor identity.CredentialsType, completedLoginAAL identity.AuthenticatorAssuranceLevel) (*session.Session, error) {\n\ts := session.NewInactiveSession()\n\ts.CompletedLoginFor(completedLoginFor, completedLoginAAL)\n\tif err := reg.SessionManager().ActivateSession(r, s, i, authenticatedAt); err != nil {\n\t\treturn nil, err\n\t}\n\treturn s, nil\n}\n"
  },
  {
    "path": "pkg/testhelpers/snapshot.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/bradleyjkemp/cupaloy/v2\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/sjson\"\n)\n\nfunc SnapshotTExcept(t *testing.T, actual interface{}, except []string) {\n\tt.Helper()\n\tcompare, err := json.MarshalIndent(actual, \"\", \"  \")\n\trequire.NoError(t, err, \"%+v\", actual)\n\tfor _, e := range except {\n\t\tcompare, err = sjson.DeleteBytes(compare, e)\n\t\trequire.NoError(t, err, \"%s\", e)\n\t}\n\n\tcupaloy.New(\n\t\tcupaloy.CreateNewAutomatically(true),\n\t\tcupaloy.FailOnUpdate(true),\n\t\tcupaloy.SnapshotFileExtension(\".json\"),\n\t).SnapshotT(t, compare)\n}\n"
  },
  {
    "path": "pkg/testhelpers/strategies.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/driver/config\"\n)\n\n// Deprecated: use MethodEnableConfig instead.\nfunc StrategyEnable(t *testing.T, c *config.Config, strategy string, enable bool) {\n\tctx := context.Background()\n\tc.MustSet(ctx, fmt.Sprintf(\"%s.%s.enabled\", config.ViperKeySelfServiceStrategyConfig, strategy), enable)\n}\n\nfunc MethodEnableConfig[S ~string](method S, enable bool) map[string]any {\n\treturn map[string]any{\n\t\tfmt.Sprintf(\"%s.%s.enabled\", config.ViperKeySelfServiceStrategyConfig, method): enable,\n\t}\n}\n"
  },
  {
    "path": "pkg/testhelpers/strategy_code.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage testhelpers\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n)\n\nvar CodeRegex = fmt.Sprintf(`(\\d{%d})`, code.CodeLength)\n"
  },
  {
    "path": "pkg/testhelpers/stub/fake-session.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {}\n}\n"
  },
  {
    "path": "proto/oidc/v1/state.proto",
    "content": "syntax = \"proto3\";\n\npackage oidc.v1;\n\nenum FlowKind {\n  FLOW_KIND_UNSPECIFIED = 0;\n  FLOW_KIND_LOGIN = 1;\n  FLOW_KIND_REGISTRATION = 2;\n  FLOW_KIND_SETTINGS = 3;\n}\n\nmessage State {\n  bytes flow_id = 1;\n  bytes session_token_exchange_code_sha512 = 2;\n  string provider_id = 3;\n  string pkce_verifier = 4;\n  FlowKind flow_kind = 5;\n}\n"
  },
  {
    "path": "quickstart-crdb.yml",
    "content": "version: \"3.7\"\n\nservices:\n  kratos-migrate:\n    environment:\n      - DSN=cockroach://root@cockroachd:26257/defaultdb?sslmode=disable&max_conns=20&max_idle_conns=4\n\n  kratos:\n    environment:\n      - DSN=cockroach://root@cockroachd:26257/defaultdb?sslmode=disable&max_conns=20&max_idle_conns=4\n\n  cockroachd:\n    image: cockroachdb/cockroach:latest-v25.4\n    ports:\n      - \"26257:26257\"\n    command: start-single-node --insecure\n    networks:\n      - intranet\n"
  },
  {
    "path": "quickstart-debug.yml",
    "content": "version: '3.7'\n\nservices:\n  kratos-migrate:\n    environment:\n      - LOG_LEVEL=trace\n\n  kratos:\n    environment:\n      - LOG_LEVEL=trace\n"
  },
  {
    "path": "quickstart-latest.yml",
    "content": "version: '3.7'\n\nservices:\n  kratos-migrate:\n    image: oryd/kratos:latest\n\n  kratos:\n    image: oryd/kratos:latest\n"
  },
  {
    "path": "quickstart-mysql.yml",
    "content": "version: \"3.7\"\n\nservices:\n  kratos-migrate:\n    environment:\n      - DSN=mysql://root:secret@tcp(mysqld:3306)/mysql?max_conns=20&max_idle_conns=4\n\n  kratos:\n    environment:\n      - DSN=mysql://root:secret@tcp(mysqld:3306)/mysql?max_conns=20&max_idle_conns=4\n\n  mysqld:\n    image: mysql:8.0\n    ports:\n      - \"3306:3306\"\n    environment:\n      - MYSQL_ROOT_PASSWORD=secret\n    networks:\n      - intranet\n"
  },
  {
    "path": "quickstart-oathkeeper.yml",
    "content": "version: '3.7'\n\nservices:\n  kratos:\n    environment:\n      - SERVE_PUBLIC_BASE_URL=http://127.0.0.1:4455/.ory/kratos/public/\n\n  kratos-selfservice-ui-node:\n    environment:\n      - PORT=4435\n      - KRATOS_BROWSER_URL=http://127.0.0.1:4455/.ory/kratos/public\n      - JWKS_URL=http://oathkeeper:4456/.well-known/jwks.json\n      - SECURITY_MODE=jwks\n\n  oathkeeper:\n    image: oryd/oathkeeper:v0.40\n    depends_on:\n      - kratos\n    ports:\n      - 4455:4455\n      - 4456:4456\n    command:\n      serve proxy -c \"/etc/config/oathkeeper/oathkeeper.yml\"\n    environment:\n      - LOG_LEVEL=debug\n    restart: on-failure\n    networks:\n      - intranet\n    volumes:\n      - ./contrib/quickstart/oathkeeper:/etc/config/oathkeeper\n"
  },
  {
    "path": "quickstart-postgres.yml",
    "content": "version: \"3.7\"\n\nservices:\n  kratos-migrate:\n    environment:\n      - DSN=postgres://kratos:secret@postgresd:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4\n\n  kratos:\n    environment:\n      - DSN=postgres://kratos:secret@postgresd:5432/kratos?sslmode=disable&max_conns=20&max_idle_conns=4\n\n  postgresd:\n    image: postgres:14\n    ports:\n      - \"5432:5432\"\n    environment:\n      - POSTGRES_USER=kratos\n      - POSTGRES_PASSWORD=secret\n      - POSTGRES_DB=kratos\n    networks:\n      - intranet\n"
  },
  {
    "path": "quickstart-selinux.yml",
    "content": "version: '3.7'\n\nservices:\n  kratos-migrate:\n    volumes:\n      -\n        type: volume\n        source: kratos-sqlite\n        target: /var/lib/sqlite\n        read_only: false\n      -\n        type: bind\n        source: ./contrib/quickstart/kratos/email-password\n        target: /etc/config/kratos:z\n\n  kratos:\n    volumes:\n      -\n        type: volume\n        source: kratos-sqlite\n        target: /var/lib/sqlite\n        read_only: false\n      -\n        type: bind\n        source: ./contrib/quickstart/kratos/email-password\n        target: /etc/config/kratos:z\n"
  },
  {
    "path": "quickstart-standalone.yml",
    "content": "version: '3.7'\n\nservices:\n  kratos-selfservice-ui-node:\n    ports:\n      - \"4455:4455\"\n    environment:\n      - PORT=4455\n      - SECURITY_MODE=\n      - KRATOS_BROWSER_URL=http://127.0.0.1:4433/\n"
  },
  {
    "path": "quickstart-tracing.yml",
    "content": "###########################################################################\n#######             FOR DEMONSTRATION PURPOSES ONLY                 #######\n###########################################################################\n#                                                                         #\n# If you have not yet read the tutorial, do so now:                       #\n#  https://www.ory.com/docs/hydra/5min-tutorial                            #\n#                                                                         #\n# This set up is only for demonstration purposes. The login               #\n# endpoint can only be used if you follow the steps in the tutorial.      #\n#                                                                         #\n###########################################################################\n\nversion: \"3.7\"\n\nservices:\n  kratos:\n    depends_on:\n      - jaeger\n    environment:\n      - TRACING_PROVIDER=jaeger\n      ### Jaeger ###\n      - TRACING_PROVIDERS_JAEGER_SAMPLING_SERVER_URL=http://jaeger:5778/sampling\n      - TRACING_PROVIDERS_JAEGER_LOCAL_AGENT_ADDRESS=jaeger:6831\n  jaeger:\n    image: jaegertracing/all-in-one:1.19.2\n    ports:\n      - \"16686:16686\" # The UI port\n    networks:\n      - intranet\n"
  },
  {
    "path": "quickstart-webauthn.yml",
    "content": "version: \"3.7\"\n\nservices:\n  kratos-migrate:\n    volumes:\n      - type: bind\n        source: ./contrib/quickstart/kratos/webauthn\n        target: /etc/config/kratos\n\n  kratos:\n    volumes:\n      - type: bind\n        source: ./contrib/quickstart/kratos/webauthn\n        target: /etc/config/kratos\n\n  kratos-selfservice-ui-node:\n    environment:\n      - KRATOS_BROWSER_URL=http://localhost:4433/\n"
  },
  {
    "path": "quickstart.yml",
    "content": "version: '3.7'\nservices:\n  kratos-migrate:\n    image: oryd/kratos:v26.2.0\n    environment:\n      - DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true&mode=rwc\n    volumes:\n      - type: volume\n        source: kratos-sqlite\n        target: /var/lib/sqlite\n        read_only: false\n      - type: bind\n        source: ./contrib/quickstart/kratos/email-password\n        target: /etc/config/kratos\n    command: -c /etc/config/kratos/kratos.yml migrate sql -e --yes\n    restart: on-failure\n    networks:\n      - intranet\n  kratos-selfservice-ui-node:\n    image: oryd/kratos-selfservice-ui-node:v26.2.0\n    environment:\n      - KRATOS_PUBLIC_URL=http://kratos:4433/\n      - KRATOS_BROWSER_URL=http://127.0.0.1:4433/\n      - COOKIE_SECRET=changeme\n      - CSRF_COOKIE_NAME=ory_csrf_ui\n      - CSRF_COOKIE_SECRET=changeme\n    networks:\n      - intranet\n    restart: on-failure\n  kratos:\n    depends_on:\n      - kratos-migrate\n    image: oryd/kratos:v26.2.0\n    ports:\n      - '4433:4433' # public\n      - '4434:4434' # admin\n    restart: unless-stopped\n    environment:\n      - DSN=sqlite:///var/lib/sqlite/db.sqlite?_fk=true\n      - LOG_LEVEL=trace\n    command: serve -c /etc/config/kratos/kratos.yml --dev --watch-courier\n    volumes:\n      - type: volume\n        source: kratos-sqlite\n        target: /var/lib/sqlite\n        read_only: false\n      - type: bind\n        source: ./contrib/quickstart/kratos/email-password\n        target: /etc/config/kratos\n    networks:\n      - intranet\n  mailslurper:\n    image: oryd/mailslurper:latest-smtps\n    ports:\n      - '4436:4436'\n      - '4437:4437'\n    networks:\n      - intranet\nnetworks:\n  intranet:\nvolumes:\n  kratos-sqlite:\n"
  },
  {
    "path": "request/auth.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage request\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n)\n\ntype (\n\tnoopAuthStrategy  struct{}\n\tbasicAuthStrategy struct {\n\t\tuser     string\n\t\tpassword string\n\t}\n\tapiKeyStrategy struct {\n\t\tname  string\n\t\tvalue string\n\t\tin    string\n\t}\n\tAuthStrategy interface {\n\t\tapply(req *retryablehttp.Request)\n\t}\n)\n\nfunc authStrategy(typ string, config map[string]any) (AuthStrategy, error) {\n\tswitch typ {\n\tcase \"\":\n\t\treturn NewNoopAuthStrategy(), nil\n\tcase \"api_key\":\n\t\tname, ok := config[\"name\"].(string)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"api_key auth strategy requires a string name\")\n\t\t}\n\t\tvalue, ok := config[\"value\"].(string)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"api_key auth strategy requires a string value\")\n\t\t}\n\t\tin, _ := config[\"in\"].(string) // in is optional\n\t\treturn NewAPIKeyStrategy(in, name, value), nil\n\tcase \"basic_auth\":\n\t\tuser, ok := config[\"user\"].(string)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"basic_auth auth strategy requires a string user\")\n\t\t}\n\t\tpassword, ok := config[\"password\"].(string)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"basic_auth auth strategy requires a string password\")\n\t\t}\n\t\treturn NewBasicAuthStrategy(user, password), nil\n\t}\n\n\treturn nil, fmt.Errorf(\"unsupported auth type: %s\", typ)\n}\n\nfunc NewNoopAuthStrategy() AuthStrategy {\n\treturn &noopAuthStrategy{}\n}\n\nfunc (c *noopAuthStrategy) apply(_ *retryablehttp.Request) {}\n\nfunc NewBasicAuthStrategy(user, password string) AuthStrategy {\n\treturn &basicAuthStrategy{\n\t\tuser:     user,\n\t\tpassword: password,\n\t}\n}\n\nfunc (c *basicAuthStrategy) apply(req *retryablehttp.Request) {\n\treq.SetBasicAuth(c.user, c.password)\n}\n\nfunc NewAPIKeyStrategy(in, name, value string) AuthStrategy {\n\treturn &apiKeyStrategy{\n\t\tin:    in,\n\t\tname:  name,\n\t\tvalue: value,\n\t}\n}\n\nfunc (c *apiKeyStrategy) apply(req *retryablehttp.Request) {\n\tswitch c.in {\n\tcase \"cookie\":\n\t\treq.AddCookie(&http.Cookie{Name: c.name, Value: c.value})\n\tdefault:\n\t\t// TODO add deprecation warning\n\t\tfallthrough\n\tcase \"header\", \"\":\n\t\treq.Header.Set(c.name, c.value)\n\t}\n}\n"
  },
  {
    "path": "request/auth_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage request\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestNoopAuthStrategy(t *testing.T) {\n\treq := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}\n\tauth := noopAuthStrategy{}\n\n\tauth.apply(&req)\n\n\tassert.Empty(t, req.Header, \"Empty auth strategy shall not modify any request headers\")\n}\n\nfunc TestBasicAuthStrategy(t *testing.T) {\n\treq := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}\n\tauth := basicAuthStrategy{\n\t\tuser:     \"test-user\",\n\t\tpassword: \"test-pass\",\n\t}\n\n\tauth.apply(&req)\n\n\tassert.Len(t, req.Header, 1)\n\n\tuser, pass, _ := req.BasicAuth()\n\tassert.Equal(t, \"test-user\", user)\n\tassert.Equal(t, \"test-pass\", pass)\n}\n\nfunc TestApiKeyInHeaderStrategy(t *testing.T) {\n\treq := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}\n\tauth := apiKeyStrategy{\n\t\tin:    \"header\",\n\t\tname:  \"my-api-key-name\",\n\t\tvalue: \"my-api-key-value\",\n\t}\n\n\tauth.apply(&req)\n\n\trequire.Len(t, req.Header, 1)\n\n\tactualValue := req.Header.Get(\"my-api-key-name\")\n\tassert.Equal(t, \"my-api-key-value\", actualValue)\n}\n\nfunc TestApiKeyInCookieStrategy(t *testing.T) {\n\treq := retryablehttp.Request{Request: &http.Request{Header: map[string][]string{}}}\n\tauth := apiKeyStrategy{\n\t\tin:    \"cookie\",\n\t\tname:  \"my-api-key-name\",\n\t\tvalue: \"my-api-key-value\",\n\t}\n\n\tauth.apply(&req)\n\n\tcookies := req.Cookies()\n\tassert.Len(t, cookies, 1)\n\n\tassert.Equal(t, \"my-api-key-name\", cookies[0].Name)\n\tassert.Equal(t, \"my-api-key-value\", cookies[0].Value)\n}\n\nfunc TestAuthStrategy(t *testing.T) {\n\tt.Parallel()\n\n\tfor _, tc := range map[string]struct {\n\t\tname     string\n\t\tconfig   map[string]any\n\t\texpected AuthStrategy\n\t}{\n\t\t\"noop\": {\n\t\t\tname:     \"\",\n\t\t\tconfig:   map[string]any{},\n\t\t\texpected: &noopAuthStrategy{},\n\t\t},\n\t\t\"basic_auth\": {\n\t\t\tname: \"basic_auth\",\n\t\t\tconfig: map[string]any{\n\t\t\t\t\"user\":     \"test-api-user\",\n\t\t\t\t\"password\": \"secret\",\n\t\t\t},\n\t\t\texpected: &basicAuthStrategy{},\n\t\t},\n\t\t\"api-key/header\": {\n\t\t\tname: \"api_key\",\n\t\t\tconfig: map[string]any{\n\t\t\t\t\"in\":    \"header\",\n\t\t\t\t\"name\":  \"my-api-key\",\n\t\t\t\t\"value\": \"secret\",\n\t\t\t},\n\t\t\texpected: &apiKeyStrategy{},\n\t\t},\n\t\t\"api-key/cookie\": {\n\t\t\tname: \"api_key\",\n\t\t\tconfig: map[string]any{\n\t\t\t\t\"in\":    \"cookie\",\n\t\t\t\t\"name\":  \"my-api-key\",\n\t\t\t\t\"value\": \"secret\",\n\t\t\t},\n\t\t\texpected: &apiKeyStrategy{},\n\t\t},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tstrategy, err := authStrategy(tc.name, tc.config)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.IsTypef(t, tc.expected, strategy, \"auth strategy should be of the expected type\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "request/builder.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage request\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/google/go-jsonnet\"\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/fetcher\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n)\n\nvar ErrCancel = errors.New(\"request cancel by JsonNet\")\n\nconst (\n\tContentTypeForm = \"application/x-www-form-urlencoded\"\n\tContentTypeJSON = \"application/json\"\n)\n\ntype (\n\tDependencies interface {\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\t\thttpx.ClientProvider\n\t\tjsonnetsecure.VMProvider\n\t}\n\tBuilder struct {\n\t\tr            *retryablehttp.Request\n\t\tConfig       *Config\n\t\tdeps         Dependencies\n\t\tcache        *ristretto.Cache[[]byte, []byte]\n\t\tbodySizeHint uint\n\t}\n\toptions struct {\n\t\tcache        *ristretto.Cache[[]byte, []byte]\n\t\tbodySizeHint uint\n\t}\n\tBuilderOption = func(*options)\n)\n\nfunc WithCache(cache *ristretto.Cache[[]byte, []byte]) BuilderOption {\n\treturn func(o *options) {\n\t\to.cache = cache\n\t}\n}\n\nfunc WithBodySizeHint(hint uint) BuilderOption {\n\treturn func(o *options) {\n\t\to.bodySizeHint = hint\n\t}\n}\n\nfunc NewBuilder(ctx context.Context, c *Config, deps Dependencies, o ...BuilderOption) (_ *Builder, err error) {\n\t_, span := deps.Tracer(ctx).Tracer().Start(ctx, \"request.NewBuilder\")\n\tdefer otelx.End(span, &err)\n\n\tvar opts options\n\tfor _, f := range o {\n\t\tf(&opts)\n\t}\n\n\tspan.SetAttributes(\n\t\tattribute.String(\"url\", c.URL),\n\t\tattribute.String(\"method\", c.Method),\n\t)\n\n\tr, err := retryablehttp.NewRequest(c.Method, c.URL, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tc.header = make(http.Header, len(c.Headers))\n\tfor k, v := range c.Headers {\n\t\tc.header.Add(k, v)\n\t}\n\tif c.header.Get(\"Content-Type\") == \"\" {\n\t\tc.header.Set(\"Content-Type\", ContentTypeJSON)\n\t}\n\n\tc.auth, err = authStrategy(c.Auth.Type, c.Auth.Config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Builder{\n\t\tr:            r,\n\t\tConfig:       c,\n\t\tdeps:         deps,\n\t\tcache:        opts.cache,\n\t\tbodySizeHint: opts.bodySizeHint,\n\t}, nil\n}\n\nfunc (b *Builder) addBody(ctx context.Context, body interface{}) (err error) {\n\tctx, span := b.deps.Tracer(ctx).Tracer().Start(ctx, \"request.Builder.addBody\")\n\tdefer otelx.End(span, &err)\n\n\tif isNilInterface(body) {\n\t\treturn nil\n\t}\n\n\tif b.Config.TemplateURI == \"\" {\n\t\treturn errors.New(\"got empty template path for request with body\")\n\t}\n\n\ttpl, err := b.readTemplate(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch b.r.Header.Get(\"Content-Type\") {\n\tcase ContentTypeForm:\n\t\tif err := b.addURLEncodedBody(ctx, tpl, body); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"\":\n\t\tb.r.Header.Set(\"Content-Type\", ContentTypeJSON)\n\t\tfallthrough\n\tcase ContentTypeJSON:\n\t\tif err := b.addJSONBody(ctx, tpl, body); err != nil {\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn errors.New(\"invalid config - incorrect Content-Type for request with body\")\n\t}\n\n\treturn nil\n}\n\nfunc (b *Builder) addJSONBody(ctx context.Context, jsonnetSnippet []byte, body interface{}) error {\n\tbuf := bytes.NewBuffer(make([]byte, 0, b.bodySizeHint))\n\tenc := json.NewEncoder(buf)\n\tenc.SetEscapeHTML(false)\n\tenc.SetIndent(\"\", \"\")\n\n\tif err := enc.Encode(body); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tvm, err := b.deps.JsonnetVM(ctx)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\tvm.TLACode(\"ctx\", buf.String())\n\n\tres, err := vm.EvaluateAnonymousSnippet(\n\t\tb.Config.TemplateURI,\n\t\tstring(jsonnetSnippet),\n\t)\n\tif err != nil {\n\t\t// Unfortunately we can not use errors.As / errors.Is, see:\n\t\t// https://github.com/google/go-jsonnet/issues/592\n\t\tif strings.Contains(err.Error(), (&jsonnet.RuntimeError{Msg: \"cancel\"}).Error()) {\n\t\t\treturn errors.WithStack(ErrCancel)\n\t\t}\n\n\t\treturn errors.WithStack(err)\n\t}\n\n\trb := strings.NewReader(res)\n\tif err := b.r.SetBody(io.NopCloser(rb)); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn nil\n}\n\nfunc (b *Builder) addURLEncodedBody(ctx context.Context, jsonnetSnippet []byte, body interface{}) error {\n\tbuf := bytes.NewBuffer(make([]byte, 0, b.bodySizeHint))\n\tenc := json.NewEncoder(buf)\n\tenc.SetEscapeHTML(false)\n\tenc.SetIndent(\"\", \"\")\n\n\tif err := enc.Encode(body); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tvm, err := b.deps.JsonnetVM(ctx)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\tvm.TLACode(\"ctx\", buf.String())\n\n\tres, err := vm.EvaluateAnonymousSnippet(b.Config.TemplateURI, string(jsonnetSnippet))\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tvalues := map[string]string{}\n\tif err := json.Unmarshal([]byte(res), &values); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tu := url.Values{}\n\n\tfor key, value := range values {\n\t\tu.Add(key, value)\n\t}\n\n\trb := strings.NewReader(u.Encode())\n\tif err := b.r.SetBody(io.NopCloser(rb)); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn nil\n}\n\nfunc (b *Builder) BuildRequest(ctx context.Context, body interface{}) (*retryablehttp.Request, error) {\n\tb.r.Header = b.Config.header\n\tb.Config.auth.apply(b.r)\n\n\t// According to the HTTP spec any request method, but TRACE is allowed to\n\t// have a body. Even this is a bad practice for some of them, like for GET\n\tif b.Config.Method != http.MethodTrace {\n\t\tif err := b.addBody(ctx, body); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn b.r, nil\n}\n\nfunc (b *Builder) addRawBody(body any) (err error) {\n\tif isNilInterface(body) {\n\t\treturn nil\n\t}\n\tbuf := bytes.NewBuffer(make([]byte, 0, b.bodySizeHint))\n\tenc := json.NewEncoder(buf)\n\tenc.SetEscapeHTML(false)\n\n\tif err := enc.Encode(body); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\tswitch contentType := b.r.Header.Get(\"Content-Type\"); contentType {\n\tcase \"\":\n\t\tb.r.Header.Set(\"Content-Type\", ContentTypeJSON)\n\t\tfallthrough\n\tcase ContentTypeJSON:\n\t\tif err := b.r.SetBody(buf); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\tdefault:\n\t\treturn herodot.ErrMisconfiguration.WithDetail(\"invalid_content_type\", contentType)\n\t}\n\n\treturn nil\n}\n\nfunc (b *Builder) BuildRawRequest(body any) (*retryablehttp.Request, error) {\n\tb.r.Header = b.Config.header\n\tb.Config.auth.apply(b.r)\n\n\t// According to the HTTP spec any request method, but TRACE is allowed to\n\t// have a body. Even this is a bad practice for some of them, like for GET\n\tif b.Config.Method != http.MethodTrace {\n\t\tif err := b.addRawBody(body); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn b.r, nil\n}\n\nfunc (b *Builder) readTemplate(ctx context.Context) ([]byte, error) {\n\ttemplateURI := b.Config.TemplateURI\n\n\tif templateURI == \"\" {\n\t\treturn nil, nil\n\t}\n\n\tf := fetcher.NewFetcher(fetcher.WithClient(b.deps.HTTPClient(ctx)), fetcher.WithCache(b.cache, 60*time.Minute))\n\ttpl, err := f.FetchContext(ctx, templateURI)\n\tif errors.Is(err, fetcher.ErrUnknownScheme) {\n\t\t// legacy filepath\n\t\ttemplateURI = \"file://\" + templateURI\n\t\tb.deps.Logger().WithError(err).Warnf(\n\t\t\t\"support for filepaths without a 'file://' scheme will be dropped in the next release, please use %s instead in your config\",\n\t\t\ttemplateURI)\n\n\t\ttpl, err = f.FetchContext(ctx, templateURI)\n\t}\n\t// this handles the first error if it is a known scheme error, or the second fetch error\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn tpl.Bytes(), nil\n}\n\nfunc isNilInterface(i interface{}) bool {\n\treturn i == nil || (reflect.ValueOf(i).Kind() == reflect.Pointer && reflect.ValueOf(i).IsNil())\n}\n"
  },
  {
    "path": "request/builder_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage request\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype testRequestBody struct {\n\tTo   string `json:\"to\"`\n\tFrom string `json:\"from\"`\n\tBody string `json:\"body\"`\n}\n\n//go:embed stub/test_body.jsonnet\nvar testJSONNetTemplate []byte\n\nfunc TestBuildRequest(t *testing.T) {\n\tt.Parallel()\n\n\tfor _, tc := range []struct {\n\t\tname             string\n\t\tmethod           string\n\t\turl              string\n\t\tauthStrategy     AuthStrategy\n\t\texpectedHeader   http.Header\n\t\tbodyTemplateURI  string\n\t\tbody             *testRequestBody\n\t\texpectedJSONBody string\n\t\texpectedRawBody  string\n\t\tconfig           Config\n\t}{\n\t\t{\n\t\t\tname:            \"POST request without auth\",\n\t\t\tmethod:          \"POST\",\n\t\t\turl:             \"https://test.kratos.ory.sh/my_endpoint1\",\n\t\t\tauthStrategy:    NewNoopAuthStrategy(),\n\t\t\tbodyTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\tbody: &testRequestBody{\n\t\t\t\tTo:   \"+15056445993\",\n\t\t\t\tFrom: \"+12288534869\",\n\t\t\t\tBody: \"test-sms-body\",\n\t\t\t},\n\t\t\texpectedJSONBody: `{\n\t\t\t\t\"body\": \"test-sms-body\",\n\t\t\t\t\"from\": \"+12288534869\",\n\t\t\t\t\"to\": \"+15056445993\"\n\t\t\t}`,\n\t\t\tconfig: Config{\n\t\t\t\tURL:         \"https://test.kratos.ory.sh/my_endpoint1\",\n\t\t\t\tMethod:      \"POST\",\n\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:            \"POST request with legacy template path\",\n\t\t\tmethod:          \"POST\",\n\t\t\turl:             \"https://test.kratos.ory.sh/my_endpoint1\",\n\t\t\tauthStrategy:    NewNoopAuthStrategy(),\n\t\t\tbodyTemplateURI: \"./stub/test_body.jsonnet\",\n\t\t\tbody: &testRequestBody{\n\t\t\t\tTo:   \"+15056445993\",\n\t\t\t\tFrom: \"+12288534869\",\n\t\t\t\tBody: \"test-sms-body\",\n\t\t\t},\n\t\t\texpectedJSONBody: `{\n\t\t\t\t\"body\": \"test-sms-body\",\n\t\t\t\t\"from\": \"+12288534869\",\n\t\t\t\t\"to\": \"+15056445993\"\n\t\t\t}`,\n\t\t\tconfig: Config{\n\t\t\t\tURL:         \"https://test.kratos.ory.sh/my_endpoint1\",\n\t\t\t\tMethod:      \"POST\",\n\t\t\t\tTemplateURI: \"./stub/test_body.jsonnet\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:            \"POST request with base64 encoded template path\",\n\t\t\tmethod:          \"POST\",\n\t\t\turl:             \"https://test.kratos.ory.sh/my_endpoint1\",\n\t\t\tauthStrategy:    NewNoopAuthStrategy(),\n\t\t\tbodyTemplateURI: \"base64://\" + base64.StdEncoding.EncodeToString(testJSONNetTemplate),\n\t\t\tbody: &testRequestBody{\n\t\t\t\tTo:   \"+15056445993\",\n\t\t\t\tFrom: \"+12288534869\",\n\t\t\t\tBody: \"test-sms-body\",\n\t\t\t},\n\t\t\texpectedJSONBody: `{\n\t\t\t\t\"body\": \"test-sms-body\",\n\t\t\t\t\"from\": \"+12288534869\",\n\t\t\t\t\"to\": \"+15056445993\"\n\t\t\t}`,\n\t\t\tconfig: Config{\n\t\t\t\tURL:         \"https://test.kratos.ory.sh/my_endpoint1\",\n\t\t\t\tMethod:      \"POST\",\n\t\t\t\tTemplateURI: \"base64://\" + base64.StdEncoding.EncodeToString(testJSONNetTemplate),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:            \"POST request with custom header\",\n\t\t\tmethod:          \"POST\",\n\t\t\turl:             \"https://test.kratos.ory.sh/my_endpoint2\",\n\t\t\tauthStrategy:    NewNoopAuthStrategy(),\n\t\t\texpectedHeader:  map[string][]string{\"Custom-Header\": {\"test\"}},\n\t\t\tbodyTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\tbody: &testRequestBody{\n\t\t\t\tTo:   \"+12127110378\",\n\t\t\t\tFrom: \"+15822228108\",\n\t\t\t\tBody: \"test-sms-body\",\n\t\t\t},\n\t\t\texpectedJSONBody: `{\n\t\t\t\t\"body\": \"test-sms-body\",\n\t\t\t\t\"from\": \"+15822228108\",\n\t\t\t\t\"to\": \"+12127110378\"\n\t\t\t}`,\n\t\t\tconfig: Config{\n\t\t\t\tURL:    \"https://test.kratos.ory.sh/my_endpoint2\",\n\t\t\t\tMethod: \"POST\",\n\t\t\t\tHeaders: map[string]string{\n\t\t\t\t\t\"Custom-Header\": \"test\",\n\t\t\t\t},\n\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:            \"GET request with body\",\n\t\t\tmethod:          \"GET\",\n\t\t\turl:             \"https://test.kratos.ory.sh/my_endpoint3\",\n\t\t\tauthStrategy:    NewBasicAuthStrategy(\"test-api-user\", \"secret\"),\n\t\t\tbodyTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\tbody: &testRequestBody{\n\t\t\t\tTo:   \"+14134242223\",\n\t\t\t\tFrom: \"+13104661805\",\n\t\t\t\tBody: \"test-sms-body\",\n\t\t\t},\n\t\t\texpectedJSONBody: `{\n\t\t\t\t\"body\": \"test-sms-body\",\n\t\t\t\t\"from\": \"+13104661805\",\n\t\t\t\t\"to\": \"+14134242223\"\n\t\t\t}`,\n\t\t\tconfig: Config{\n\t\t\t\tURL:    \"https://test.kratos.ory.sh/my_endpoint3\",\n\t\t\t\tMethod: \"GET\",\n\t\t\t\tAuth: AuthConfig{\n\t\t\t\t\tType: \"basic_auth\",\n\t\t\t\t\tConfig: map[string]any{\n\t\t\t\t\t\t\"user\":     \"test-api-user\",\n\t\t\t\t\t\t\"password\": \"secret\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:         \"GET request without body\",\n\t\t\tmethod:       \"GET\",\n\t\t\turl:          \"https://test.kratos.ory.sh/my_endpoint4\",\n\t\t\tauthStrategy: NewBasicAuthStrategy(\"test-api-user\", \"secret\"),\n\t\t\tconfig: Config{\n\t\t\t\tURL:    \"https://test.kratos.ory.sh/my_endpoint4\",\n\t\t\t\tMethod: \"GET\",\n\t\t\t\tAuth: AuthConfig{\n\t\t\t\t\tType: \"basic_auth\",\n\t\t\t\t\tConfig: map[string]any{\n\t\t\t\t\t\t\"user\":     \"test-api-user\",\n\t\t\t\t\t\t\"password\": \"secret\",\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:            \"DELETE request with body\",\n\t\t\tmethod:          \"DELETE\",\n\t\t\turl:             \"https://test.kratos.ory.sh/my_endpoint5\",\n\t\t\tauthStrategy:    NewAPIKeyStrategy(\"header\", \"my-api-key\", \"secret\"),\n\t\t\tbodyTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\tbody: &testRequestBody{\n\t\t\t\tTo:   \"+12235499085\",\n\t\t\t\tFrom: \"+14253787846\",\n\t\t\t\tBody: \"test-sms-body\",\n\t\t\t},\n\t\t\texpectedJSONBody: `{\n\t\t\t\t\"body\": \"test-sms-body\",\n\t\t\t\t\"from\": \"+14253787846\",\n\t\t\t\t\"to\": \"+12235499085\"\n\t\t\t}`,\n\t\t\tconfig: Config{\n\t\t\t\tURL:         \"https://test.kratos.ory.sh/my_endpoint5\",\n\t\t\t\tMethod:      \"DELETE\",\n\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t\tAuth: AuthConfig{\n\t\t\t\t\tType: \"api_key\",\n\t\t\t\t\tConfig: map[string]any{\n\t\t\t\t\t\t\"in\":    \"header\",\n\t\t\t\t\t\t\"name\":  \"my-api-key\",\n\t\t\t\t\t\t\"value\": \"secret\",\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:            \"POST request with urlencoded body\",\n\t\t\tmethod:          \"POST\",\n\t\t\turl:             \"https://test.kratos.ory.sh/my_endpoint6\",\n\t\t\tbodyTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\tauthStrategy:    NewAPIKeyStrategy(\"cookie\", \"my-api-key\", \"secret\"),\n\t\t\texpectedHeader:  map[string][]string{\"Content-Type\": {ContentTypeForm}},\n\t\t\tbody: &testRequestBody{\n\t\t\t\tTo:   \"+14134242223\",\n\t\t\t\tFrom: \"+13104661805\",\n\t\t\t\tBody: \"test-sms-body\",\n\t\t\t},\n\t\t\texpectedRawBody: \"body=test-sms-body&from=%2B13104661805&to=%2B14134242223\",\n\t\t\tconfig: Config{\n\t\t\t\tURL:         \"https://test.kratos.ory.sh/my_endpoint6\",\n\t\t\t\tMethod:      \"POST\",\n\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t\tHeaders: map[string]string{\n\t\t\t\t\t\"Content-Type\": ContentTypeForm,\n\t\t\t\t},\n\t\t\t\tAuth: AuthConfig{\n\t\t\t\t\tType: \"api_key\",\n\t\t\t\t\tConfig: map[string]any{\n\t\t\t\t\t\t\"in\":    \"cookie\",\n\t\t\t\t\t\t\"name\":  \"my-api-key\",\n\t\t\t\t\t\t\"value\": \"secret\",\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:            \"POST request with default body type\",\n\t\t\tmethod:          \"POST\",\n\t\t\turl:             \"https://test.kratos.ory.sh/my_endpoint7\",\n\t\t\tbodyTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\tauthStrategy:    NewBasicAuthStrategy(\"test-api-user\", \"secret\"),\n\t\t\texpectedHeader:  map[string][]string{\"Content-Type\": {ContentTypeJSON}},\n\t\t\tbody: &testRequestBody{\n\t\t\t\tTo:   \"+14134242223\",\n\t\t\t\tFrom: \"+13104661805\",\n\t\t\t\tBody: \"test-sms-body\",\n\t\t\t},\n\t\t\texpectedJSONBody: `{\n\t\t\t\t\"body\": \"test-sms-body\",\n\t\t\t\t\"from\": \"+13104661805\",\n\t\t\t\t\"to\": \"+14134242223\"\n\t\t\t}`,\n\t\t\tconfig: Config{\n\t\t\t\tURL:         \"https://test.kratos.ory.sh/my_endpoint7\",\n\t\t\t\tMethod:      \"POST\",\n\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t\tAuth: AuthConfig{\n\t\t\t\t\tType: \"basic_auth\",\n\t\t\t\t\tConfig: map[string]any{\n\t\t\t\t\t\t\"user\":     \"test-api-user\",\n\t\t\t\t\t\t\"password\": \"secret\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(\"request-type=\"+tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\trb, err := NewBuilder(context.Background(), &tc.config, newTestDependencyProvider(t))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, tc.bodyTemplateURI, rb.Config.TemplateURI)\n\t\t\tassert.Equal(t, tc.authStrategy, rb.Config.auth)\n\n\t\t\treq, err := rb.BuildRequest(context.Background(), tc.body)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, tc.url, req.URL.String())\n\t\t\tassert.Equal(t, tc.method, req.Method)\n\n\t\t\tif tc.expectedJSONBody != \"\" {\n\t\t\t\trequestBody, err := req.BodyBytes()\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.JSONEq(t, tc.expectedJSONBody, string(requestBody))\n\t\t\t} else if tc.expectedRawBody != \"\" {\n\t\t\t\trequestBody, err := req.BodyBytes()\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, tc.expectedRawBody, string(requestBody))\n\t\t\t}\n\n\t\t\tif tc.expectedHeader != nil {\n\t\t\t\tmustContainHeader(t, tc.expectedHeader, req.Header)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"cancel request\", func(t *testing.T) {\n\t\trb, err := NewBuilder(context.Background(), &Config{\n\t\t\tURL:         \"https://test.kratos.ory.sh/my_endpoint6\",\n\t\t\tMethod:      \"POST\",\n\t\t\tTemplateURI: \"file://./stub/cancel_body.jsonnet\",\n\t\t}, newTestDependencyProvider(t))\n\t\trequire.NoError(t, err)\n\n\t\t_, err = rb.BuildRequest(context.Background(), json.RawMessage(`{}`))\n\t\trequire.ErrorIs(t, err, ErrCancel)\n\t})\n}\n\ntype testDependencyProvider struct {\n\tx.BasicRegistry\n\t*jsonnetsecure.TestProvider\n}\n\nfunc newTestDependencyProvider(t *testing.T) *testDependencyProvider {\n\treturn &testDependencyProvider{\n\t\tBasicRegistry: x.BasicRegistry{\n\t\t\tL: logrusx.New(\"kratos\", \"test\"),\n\t\t\tT: otelx.NewNoop(),\n\t\t},\n\t\tTestProvider: jsonnetsecure.NewTestProvider(t),\n\t}\n}\n\nfunc mustContainHeader(t *testing.T, expected http.Header, actual http.Header) {\n\tt.Helper()\n\tfor k := range expected {\n\t\trequire.Contains(t, actual, k)\n\t\tassert.Equal(t, expected[k], actual[k])\n\t}\n}\n"
  },
  {
    "path": "request/config.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage request\n\nimport (\n\t\"net/http\"\n)\n\ntype (\n\tAuthConfig = struct {\n\t\tType   string         `json:\"type\" koanf:\"type\"`\n\t\tConfig map[string]any `json:\"config\" koanf:\"config\"`\n\t}\n\tResponseConfig = struct {\n\t\tParse  bool `json:\"parse\" koanf:\"parse\"`\n\t\tIgnore bool `json:\"ignore\" koanf:\"ignore\"`\n\t}\n\tConfig struct {\n\t\tID                 string            `json:\"id\" koanf:\"id\"`\n\t\tMethod             string            `json:\"method\" koanf:\"method\"`\n\t\tURL                string            `json:\"url\" koanf:\"url\"`\n\t\tTemplateURI        string            `json:\"body\" koanf:\"body\"`\n\t\tHeaders            map[string]string `json:\"headers\" koanf:\"headers\"`\n\t\tAuth               AuthConfig        `json:\"auth\" koanf:\"auth\"`\n\t\tEmitAnalyticsEvent *bool             `json:\"emit_analytics_event\" koanf:\"emit_analytics_event\"`\n\t\tCanInterrupt       bool              `json:\"can_interrupt\" koanf:\"can_interrupt\"`\n\t\tResponse           ResponseConfig    `json:\"response\" koanf:\"response\"`\n\n\t\tauth   AuthStrategy\n\t\theader http.Header\n\t}\n)\n"
  },
  {
    "path": "request/stub/cancel_body.jsonnet",
    "content": "error \"cancel\"\n"
  },
  {
    "path": "request/stub/test_body.jsonnet",
    "content": "function(ctx) {\n  from: ctx.from,\n  to: ctx.to,\n  body: ctx.body,\n}\n"
  },
  {
    "path": "schema/context.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"strings\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\nfunc ContextSetRoot(err *jsonschema.ValidationError, head string) *jsonschema.ValidationError {\n\terr.InstancePtr = prefixPointer(err.InstancePtr, head)\n\n\tif err.Context != nil {\n\t\tswitch ctx := err.Context.(type) {\n\t\tcase *jsonschema.ValidationErrorContextRequired:\n\t\t\tfor k, pointer := range ctx.Missing {\n\t\t\t\tctx.Missing[k] = prefixPointer(pointer, head)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor k := range err.Causes {\n\t\terr.Causes[k] = ContextSetRoot(err.Causes[k], head)\n\t}\n\n\treturn err\n}\n\nfunc prefixPointer(pointer, head string) string {\n\tif pointer == \"#\" || pointer == \"#/\" {\n\t\tpointer = \"#/\" + head\n\t} else {\n\t\tpointer = \"#/\" + head + \"/\" + strings.TrimPrefix(pointer, \"#/\")\n\t}\n\treturn pointer\n}\n"
  },
  {
    "path": "schema/context_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\nfunc TestContextSetRoot(t *testing.T) {\n\tfor k, tc := range []struct {\n\t\tin     jsonschema.ValidationError\n\t\thead   string\n\t\texpect jsonschema.ValidationError\n\t}{\n\t\t{\n\t\t\thead: \"traits\",\n\t\t\tin: jsonschema.ValidationError{\n\t\t\t\tInstancePtr: \"#/foo/bar\",\n\t\t\t\tContext:     &jsonschema.ValidationErrorContextRequired{Missing: []string{\"#/foo/bar/baz\"}},\n\t\t\t\tCauses: []*jsonschema.ValidationError{{\n\t\t\t\t\tInstancePtr: \"#/foo/bar\",\n\t\t\t\t\tContext:     &jsonschema.ValidationErrorContextRequired{Missing: []string{\"#/foo/bar/baz\"}},\n\t\t\t\t}},\n\t\t\t},\n\t\t\texpect: jsonschema.ValidationError{\n\t\t\t\tInstancePtr: \"#/traits/foo/bar\",\n\t\t\t\tContext:     &jsonschema.ValidationErrorContextRequired{Missing: []string{\"#/traits/foo/bar/baz\"}},\n\t\t\t\tCauses: []*jsonschema.ValidationError{{\n\t\t\t\t\tInstancePtr: \"#/traits/foo/bar\",\n\t\t\t\t\tContext:     &jsonschema.ValidationErrorContextRequired{Missing: []string{\"#/traits/foo/bar/baz\"}},\n\t\t\t\t}},\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\tassert.EqualValues(t, tc.expect, *ContextSetRoot(&tc.in, tc.head))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "schema/errors.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\n\t\"github.com/ory/kratos/text\"\n)\n\ntype ValidationError struct {\n\t*jsonschema.ValidationError\n\tMessages text.Messages\n}\n\nfunc NewRequiredError(missingPtr, missingFieldName string) error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     fmt.Sprintf(\"missing properties: %s\", missingFieldName),\n\t\t\tInstancePtr: missingPtr,\n\t\t\tContext: &jsonschema.ValidationErrorContextRequired{\n\t\t\t\tMissing: []string{missingFieldName},\n\t\t\t},\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewValidationErrorRequired(missingFieldName)),\n\t})\n}\n\nfunc NewTOTPVerifierWrongError(instancePtr string) error {\n\tt := text.NewErrorValidationTOTPVerifierWrong()\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     t.Text,\n\t\t\tInstancePtr: instancePtr,\n\t\t},\n\t\tMessages: new(text.Messages).Add(t),\n\t})\n}\n\nfunc NewWebAuthnVerifierWrongError(instancePtr string) error {\n\tt := text.NewErrorValidationTOTPVerifierWrong()\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     t.Text,\n\t\t\tInstancePtr: instancePtr,\n\t\t},\n\t\tMessages: new(text.Messages).Add(t),\n\t})\n}\n\nfunc NewLookupAlreadyUsed() error {\n\tt := text.NewErrorValidationLookupAlreadyUsed()\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     t.Text,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(t),\n\t})\n}\n\nfunc NewErrorValidationLookupInvalid() error {\n\tt := text.NewErrorValidationLookupInvalid()\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     t.Text,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(t),\n\t})\n}\n\ntype ValidationErrorContextPasswordPolicyViolation struct {\n\tReason string\n}\n\nfunc (r *ValidationErrorContextPasswordPolicyViolation) AddContext(_, _ string) {}\n\nfunc (r *ValidationErrorContextPasswordPolicyViolation) FinishInstanceContext() {}\n\nfunc NewPasswordPolicyViolationError(instancePtr string, message *text.Message) error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     fmt.Sprintf(\"the password does not fulfill the password policy because: %s\", message.Text),\n\t\t\tInstancePtr: instancePtr,\n\t\t\tContext: &ValidationErrorContextPasswordPolicyViolation{\n\t\t\t\tReason: message.Text,\n\t\t\t},\n\t\t},\n\t\tMessages: new(text.Messages).Add(message),\n\t})\n}\n\nfunc NewMissingIdentifierError() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     \"could not find any identifiers\",\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationIdentifierMissing()),\n\t})\n}\n\ntype ValidationErrorContextInvalidCredentialsError struct{}\n\nfunc (r *ValidationErrorContextInvalidCredentialsError) AddContext(_, _ string) {}\n\nfunc (r *ValidationErrorContextInvalidCredentialsError) FinishInstanceContext() {}\n\nfunc NewInvalidCredentialsError() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `the provided credentials are invalid, check for spelling mistakes in your password or username, email address, or phone number`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationInvalidCredentials()),\n\t})\n}\n\nfunc NewAccountNotFoundError() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     \"this account does not exist or has no login method configured\",\n\t\t\tInstancePtr: \"#/identifier\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationAccountNotFound()),\n\t})\n}\n\ntype ValidationErrorContextDuplicateCredentialsError struct {\n\tAvailableCredentials   []string `json:\"available_credential_types\"`\n\tAvailableOIDCProviders []string `json:\"available_oidc_providers\"`\n\tIdentifierHint         string   `json:\"credential_identifier_hint\"`\n}\n\nfunc (r *ValidationErrorContextDuplicateCredentialsError) AddContext(_, _ string) {}\n\nfunc (r *ValidationErrorContextDuplicateCredentialsError) FinishInstanceContext() {}\n\ntype DuplicateCredentialsHinter interface {\n\tAvailableCredentials() []string\n\tAvailableOIDCProviders() []string\n\tIdentifierHint() string\n\tHasHints() bool\n}\n\nfunc NewDuplicateCredentialsError(err error) error {\n\tif hinter := DuplicateCredentialsHinter(nil); errors.As(err, &hinter) && hinter.HasHints() {\n\t\treturn errors.WithStack(&ValidationError{\n\t\t\tValidationError: &jsonschema.ValidationError{\n\t\t\t\tMessage:     `an account with the same identifier (email, phone, username, ...) exists already`,\n\t\t\t\tInstancePtr: \"#/\",\n\t\t\t\tContext: &ValidationErrorContextDuplicateCredentialsError{\n\t\t\t\t\tAvailableCredentials:   hinter.AvailableCredentials(),\n\t\t\t\t\tAvailableOIDCProviders: hinter.AvailableOIDCProviders(),\n\t\t\t\t\tIdentifierHint:         hinter.IdentifierHint(),\n\t\t\t\t},\n\t\t\t},\n\t\t\tMessages: new(text.Messages).Add(text.NewErrorValidationDuplicateCredentialsWithHints(hinter.AvailableCredentials(), hinter.AvailableOIDCProviders(), hinter.IdentifierHint())),\n\t\t})\n\t}\n\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `an account with the same identifier (email, phone, username, ...) exists already`,\n\t\t\tInstancePtr: \"#/\",\n\t\t\tContext:     &ValidationErrorContextDuplicateCredentialsError{},\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationDuplicateCredentials()),\n\t})\n}\n\nfunc NewNoLoginStrategyResponsible() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `could not find a strategy to login with`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationLoginNoStrategyFound()),\n\t})\n}\n\nfunc NewNoRegistrationStrategyResponsible() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `could not find a strategy to sign up with`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationRegistrationNoStrategyFound()),\n\t})\n}\n\nfunc NewNoSettingsStrategyResponsible() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `could not find a strategy to update settings with`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationSettingsNoStrategyFound()),\n\t})\n}\n\nfunc NewNoRecoveryStrategyResponsible() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `could not find a strategy to recover your account with`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationRecoveryNoStrategyFound()),\n\t})\n}\n\nfunc NewNoVerificationStrategyResponsible() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `could not find a strategy to verify your account with`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationVerificationNoStrategyFound()),\n\t})\n}\n\nfunc NewAddressNotVerifiedError() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `account address not yet verified`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationAddressNotVerified()),\n\t})\n}\n\nfunc NewNoTOTPDeviceRegistered() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `you have no TOTP device set up`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationNoTOTPDevice()),\n\t})\n}\n\nfunc NewNoLookupDefined() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `you have no backup recovery codes set up`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationNoLookup()),\n\t})\n}\n\nfunc NewNoWebAuthnRegistered() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `you have no WebAuthn device set up`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationNoWebAuthnDevice()),\n\t})\n}\n\nfunc NewHookValidationError(instancePtr, message string, messages text.Messages) *ValidationError {\n\treturn &ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     message,\n\t\t\tInstancePtr: instancePtr,\n\t\t},\n\t\tMessages: messages,\n\t}\n}\n\ntype ValidationListError struct {\n\tValidations []*ValidationError\n}\n\nfunc (e ValidationListError) Error() string {\n\tvar detailError string\n\tfor pos, validationErr := range e.Validations {\n\t\tdetailError = detailError + fmt.Sprintf(\"\\n(%d) %s\", pos, validationErr.Error())\n\t}\n\treturn fmt.Sprintf(\"%d validation errors occurred:%s\", len(e.Validations), detailError)\n}\n\nfunc (e *ValidationListError) Add(v *ValidationError) {\n\te.Validations = append(e.Validations, v)\n}\n\nfunc (e ValidationListError) HasErrors() bool {\n\treturn len(e.Validations) > 0\n}\n\nfunc (e *ValidationListError) WithError(instancePtr, message string, details text.Messages) {\n\te.Validations = append(e.Validations, &ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     message,\n\t\t\tInstancePtr: instancePtr,\n\t\t},\n\t\tMessages: details,\n\t})\n}\n\nfunc NewValidationListError(errs []*ValidationError) error {\n\treturn errors.WithStack(&ValidationListError{Validations: errs})\n}\n\nfunc NewNoWebAuthnCredentials() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `account does not exist or has no security key set up`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationSuchNoWebAuthnUser()),\n\t})\n}\n\nfunc NewNoCodeAuthnCredentials() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `account does not exist or has not setup up sign in with code`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationNoCodeUser()),\n\t})\n}\n\nfunc NewTraitsMismatch() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `the submitted form data has changed from the previous submission`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationTraitsMismatch()),\n\t})\n}\n\nfunc NewRegistrationCodeInvalid() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `the provided code is invalid or has already been used`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed()),\n\t})\n}\n\nfunc NewLoginCodeInvalid() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `the provided code is invalid or has already been used`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationLoginCodeInvalidOrAlreadyUsed()),\n\t})\n}\n\nfunc NewLinkedCredentialsDoNotMatch() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `linked credentials do not match; please start a new flow`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationLoginLinkedCredentialsDoNotMatch()),\n\t})\n}\n\nfunc NewUnknownAddressError() error {\n\treturn errors.WithStack(&ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `the supplied address does not match any known addresses.`,\n\t\t\tInstancePtr: \"#/\",\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationAddressUnknown()),\n\t},\n\t)\n}\n"
  },
  {
    "path": "schema/errors_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\n\t\"github.com/ory/kratos/text\"\n)\n\nfunc TestListValidationErrors(t *testing.T) {\n\ttestErr := ValidationListError{}\n\n\tassert.False(t, testErr.HasErrors())\n\n\ttestErr.WithError(\"#/traits/password\", \"error message\", new(text.Messages).Add(text.NewErrorValidationDuplicateCredentials()))\n\tassert.True(t, testErr.HasErrors())\n\tassert.Len(t, testErr.Validations, 1)\n\n\tvalidationError := &ValidationError{\n\t\tValidationError: &jsonschema.ValidationError{\n\t\t\tMessage:     `the provided credentials are invalid, check for spelling mistakes in your password or username, email address, or phone number`,\n\t\t\tInstancePtr: \"#/\",\n\t\t\tContext:     &ValidationErrorContextPasswordPolicyViolation{},\n\t\t},\n\t\tMessages: new(text.Messages).Add(text.NewErrorValidationInvalidCredentials()),\n\t}\n\ttestErr.Add(validationError)\n\tassert.Len(t, testErr.Validations, 2)\n\tassert.Equal(t, \"2 validation errors occurred:\"+\n\t\t\"\\n(0) I[#/traits/password] S[] error message\"+\n\t\t\"\\n(1) I[#/] S[] the provided credentials are invalid, check for spelling mistakes in your password or username, email address, or phone number\",\n\t\ttestErr.Error())\n}\n"
  },
  {
    "path": "schema/extension.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/embedx\"\n\t\"github.com/ory/x/jsonschemax\"\n)\n\nconst (\n\tExtensionName string = \"ory.sh/kratos\"\n)\n\ntype (\n\tExtensionConfig struct {\n\t\tCredentials struct {\n\t\t\tPassword struct {\n\t\t\t\tIdentifier bool `json:\"identifier\"`\n\t\t\t} `json:\"password\"`\n\t\t\tWebAuthn struct {\n\t\t\t\tIdentifier bool `json:\"identifier\"`\n\t\t\t} `json:\"webauthn\"`\n\t\t\tPasskey struct {\n\t\t\t\tDisplayName bool `json:\"display_name\"`\n\t\t\t} `json:\"passkey\"`\n\t\t\tTOTP struct {\n\t\t\t\tAccountName bool `json:\"account_name\"`\n\t\t\t} `json:\"totp\"`\n\t\t\tCode struct {\n\t\t\t\tIdentifier bool   `json:\"identifier\"`\n\t\t\t\tVia        string `json:\"via\"`\n\t\t\t} `json:\"code\"`\n\t\t} `json:\"credentials\"`\n\t\tVerification struct {\n\t\t\tVia string `json:\"via\"`\n\t\t} `json:\"verification\"`\n\t\tRecovery struct {\n\t\t\tVia string `json:\"via\"`\n\t\t} `json:\"recovery\"`\n\t\tOrganization struct {\n\t\t\tMatcher string `json:\"matcher\"`\n\t\t} `json:\"organizations\"`\n\t\tRawSchema map[string]interface{} `json:\"-\"`\n\t}\n\n\tValidateExtension interface {\n\t\tRun(ctx jsonschema.ValidationContext, config ExtensionConfig, value interface{}) error\n\t\tFinish() error\n\t}\n\tCompileExtension interface {\n\t\tRun(ctx jsonschema.CompilerContext, config ExtensionConfig, rawSchema map[string]interface{}) error\n\t}\n\n\tExtensionRunner struct {\n\t\tmeta     *jsonschema.Schema\n\t\tcompile  func(ctx jsonschema.CompilerContext, rawSchema map[string]interface{}) (interface{}, error)\n\t\tvalidate func(ctx jsonschema.ValidationContext, s interface{}, v interface{}) error\n\n\t\tvalidateRunners []ValidateExtension\n\t\tcompileRunners  []CompileExtension\n\t}\n\n\tExtensionRunnerOption func(*ExtensionRunner)\n)\n\nfunc (e *ExtensionConfig) EnhancePath(path jsonschemax.Path) map[string]any {\n\tprops := path.CustomProperties\n\tif props == nil {\n\t\tprops = make(map[string]any)\n\t}\n\tprops[ExtensionName] = e\n\n\treturn props\n}\n\nfunc WithValidateRunners(runners ...ValidateExtension) ExtensionRunnerOption {\n\treturn func(r *ExtensionRunner) {\n\t\tr.validateRunners = append(r.validateRunners, runners...)\n\t}\n}\n\nfunc WithCompileRunners(runners ...CompileExtension) ExtensionRunnerOption {\n\treturn func(r *ExtensionRunner) {\n\t\tr.compileRunners = append(r.compileRunners, runners...)\n\t}\n}\n\nfunc NewExtensionRunner(ctx context.Context, opts ...ExtensionRunnerOption) (*ExtensionRunner, error) {\n\tvar err error\n\tr := new(ExtensionRunner)\n\tc := jsonschema.NewCompiler()\n\n\tif err = embedx.AddSchemaResources(c, embedx.IdentityExtension); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tr.meta, err = c.Compile(ctx, embedx.IdentityExtension.GetSchemaID())\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tr.compile = func(ctx jsonschema.CompilerContext, m map[string]interface{}) (interface{}, error) {\n\t\tif raw, ok := m[ExtensionName]; ok {\n\t\t\tvar b bytes.Buffer\n\t\t\tif err := json.NewEncoder(&b).Encode(raw); err != nil {\n\t\t\t\treturn nil, errors.WithStack(err)\n\t\t\t}\n\n\t\t\tvar e ExtensionConfig\n\t\t\tif err := json.NewDecoder(&b).Decode(&e); err != nil {\n\t\t\t\treturn nil, errors.WithStack(err)\n\t\t\t}\n\n\t\t\tfor _, runner := range r.compileRunners {\n\t\t\t\tif err := runner.Run(ctx, e, m); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\te.RawSchema = m\n\n\t\t\treturn &e, nil\n\t\t}\n\t\treturn nil, nil\n\t}\n\n\tr.validate = func(ctx jsonschema.ValidationContext, s interface{}, v interface{}) error {\n\t\tc, ok := s.(*ExtensionConfig)\n\t\tif !ok {\n\t\t\treturn nil\n\t\t}\n\n\t\tfor _, runner := range r.validateRunners {\n\t\t\tif err := runner.Run(ctx, *c, v); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(r)\n\t}\n\n\treturn r, nil\n}\n\nfunc (r *ExtensionRunner) Register(compiler *jsonschema.Compiler) *ExtensionRunner {\n\tcompiler.Extensions[ExtensionName] = r.Extension()\n\treturn r\n}\n\nfunc (r *ExtensionRunner) Extension() jsonschema.Extension {\n\treturn jsonschema.Extension{\n\t\tMeta:     r.meta,\n\t\tCompile:  r.compile,\n\t\tValidate: r.validate,\n\t}\n}\n\nfunc (r *ExtensionRunner) AddRunner(run ValidateExtension) *ExtensionRunner {\n\tr.validateRunners = append(r.validateRunners, run)\n\treturn r\n}\n\nfunc (r *ExtensionRunner) Finish() error {\n\tfor _, runner := range r.validateRunners {\n\t\tif err := runner.Finish(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "schema/extension_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t_ \"github.com/ory/jsonschema/v3/fileloader\"\n)\n\ntype extensionStub struct {\n\tidentifiers  []string\n\taccountNames []string\n}\n\nfunc (r *extensionStub) Run(ctx jsonschema.ValidationContext, config ExtensionConfig, value interface{}) error {\n\tif config.Credentials.Password.Identifier {\n\t\tr.identifiers = append(r.identifiers, fmt.Sprintf(\"%s\", value))\n\t}\n\tif config.Credentials.TOTP.AccountName {\n\t\tr.accountNames = append(r.accountNames, fmt.Sprintf(\"%s\", value))\n\t}\n\treturn nil\n}\n\nfunc (r *extensionStub) Finish() error {\n\treturn nil\n}\n\nvar ctx = context.Background()\n\nfunc TestExtensionRunner(t *testing.T) {\n\tt.Run(\"method=get identifier\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\texpectErr error\n\t\t\tschema    string\n\t\t\tdoc       string\n\t\t\texpect    []string\n\t\t}{\n\t\t\t{\n\t\t\t\tdoc:    `{\"email\":\"foo@ory.sh\"}`,\n\t\t\t\tschema: \"file://./stub/extension/schema.json\",\n\t\t\t\texpect: []string{\"foo@ory.sh\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdoc:    `{\"emails\":[\"foo@ory.sh\",\"bar@ory.sh\"]}`,\n\t\t\t\tschema: \"file://./stub/extension/schema.nested.json\",\n\t\t\t\texpect: []string{\"foo@ory.sh\", \"bar@ory.sh\"},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tc := jsonschema.NewCompiler()\n\t\t\t\trunner, err := NewExtensionRunner(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tr := new(extensionStub)\n\t\t\t\trunner.AddRunner(r).Register(c)\n\n\t\t\t\terr = c.MustCompile(ctx, tc.schema).Validate(bytes.NewBufferString(tc.doc))\n\t\t\t\tif tc.expectErr != nil {\n\t\t\t\t\trequire.EqualError(t, err, tc.expectErr.Error())\n\t\t\t\t}\n\n\t\t\t\tassert.EqualValues(t, tc.expect, r.identifiers)\n\t\t\t\tassert.EqualValues(t, tc.expect, r.accountNames)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"method=applies meta schema\", func(t *testing.T) {\n\t\tc := jsonschema.NewCompiler()\n\t\trunner, err := NewExtensionRunner(ctx)\n\t\trequire.NoError(t, err)\n\n\t\trunner.Register(c)\n\n\t\t_, err = c.Compile(ctx, \"file://./stub/extension/invalid.schema.json\")\n\t\tassert.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"expected boolean, but got number\")\n\t})\n}\n"
  },
  {
    "path": "schema/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/x/errorsx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/pagination/migrationpagination\"\n)\n\ntype (\n\thandlerDependencies interface {\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\tIdentitySchemaProvider\n\t\tnosurfx.CSRFProvider\n\t\tconfig.Provider\n\t\totelx.Provider\n\t\thttpx.ClientProvider\n\t}\n\tHandler struct {\n\t\tr handlerDependencies\n\t}\n\tHandlerProvider interface {\n\t\tSchemaHandler() *Handler\n\t}\n)\n\nfunc NewHandler(r handlerDependencies) *Handler {\n\treturn &Handler{r: r}\n}\n\nconst (\n\tSchemasPath   string = \"schemas\"\n\tmaxSchemaSize        = 1024 * 1024 // 1 MB\n)\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\th.r.CSRFHandler().IgnoreGlobs(\n\t\t\"/\"+SchemasPath+\"/*\",\n\t\thttprouterx.AdminPrefix+\"/\"+SchemasPath+\"/*\",\n\t)\n\tpublic.GET(fmt.Sprintf(\"/%s/{id}\", SchemasPath), h.getIdentitySchema)\n\tpublic.GET(fmt.Sprintf(\"/%s\", SchemasPath), h.getAll)\n\tpublic.GET(fmt.Sprintf(\"%s/%s/{id}\", httprouterx.AdminPrefix, SchemasPath), h.getIdentitySchema)\n\tpublic.GET(fmt.Sprintf(\"%s/%s\", httprouterx.AdminPrefix, SchemasPath), h.getAll)\n}\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(fmt.Sprintf(\"/%s/{id}\", SchemasPath), redir.RedirectToPublicRoute(h.r))\n\tadmin.GET(fmt.Sprintf(\"/%s\", SchemasPath), redir.RedirectToPublicRoute(h.r))\n}\n\n// Raw JSON Schema\n//\n// swagger:model identitySchema\ntype _ json.RawMessage\n\n// Get Identity JSON Schema Response\n//\n// swagger:parameters getIdentitySchema\ntype _ struct {\n\t// ID must be set to the ID of schema you want to get\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n}\n\n// swagger:route GET /schemas/{id} identity getIdentitySchema\n//\n// # Get Identity JSON Schema\n//\n// Return a specific identity schema.\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: identitySchema\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-medium\nfunc (h *Handler) getIdentitySchema(w http.ResponseWriter, r *http.Request) {\n\tctx, span := h.r.Tracer(r.Context()).Tracer().Start(r.Context(), \"schema.Handler.getIdentitySchema\")\n\tdefer span.End()\n\n\tss, err := h.r.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tid := r.PathValue(\"id\")\n\ts, err := ss.GetByID(id)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrNotFound.WithReasonf(\"Identity schema `%s` could not be found.\", id)))\n\t\treturn\n\t}\n\n\traw, err := h.ReadSchema(ctx, s.URL)\n\tif err != nil {\n\t\tcode, ok := errorsx.GetCodeFromHerodotError(err)\n\n\t\tif errors.Is(err, fs.ErrNotExist) || (ok && code == http.StatusNotFound) {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrMisconfiguration.WithReason(\"The file for this JSON Schema ID could not be found/fetched. This is a configuration issue.\").WithDebugf(\"%+v\", err)))\n\t\t} else if ok && code == http.StatusBadGateway {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrUpstreamError.WithReason(\"The file for this JSON Schema ID could not be fetched. This is an upstream issue.\").WithDebugf(\"%+v\", err)))\n\t\t} else {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrInternalServerError.WithReason(\"The file for this JSON Schema ID could not be read. This is an I/O issue.\").WithDebugf(\"%+v\", err)))\n\t\t}\n\t\treturn\n\t}\n\n\tw.Header().Add(\"Content-Type\", \"application/json\")\n\th.r.Writer().Write(w, r, json.RawMessage(raw))\n}\n\n// List of Identity JSON Schemas\n//\n// swagger:model identitySchemas\ntype IdentitySchemas []identitySchemaContainer\n\n// An Identity JSON Schema Container\n//\n// swagger:model identitySchemaContainer\ntype identitySchemaContainer struct {\n\t// The ID of the Identity JSON Schema\n\t// required: true\n\tID string `json:\"id\"`\n\t// The actual Identity JSON Schema\n\t// required: true\n\tSchema json.RawMessage `json:\"schema\"`\n}\n\n// List Identity JSON Schemas Response\n//\n// swagger:parameters listIdentitySchemas\ntype _ struct {\n\tmigrationpagination.RequestParameters\n}\n\n// List Identity JSON Schemas Response\n//\n// swagger:response identitySchemas\ntype _ struct {\n\tmigrationpagination.ResponseHeaderAnnotation\n\n\t// in: body\n\tBody IdentitySchemas\n}\n\n// swagger:route GET /schemas identity listIdentitySchemas\n//\n// # Get all Identity Schemas\n//\n// Returns a list of all identity schemas currently in use.\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: identitySchemas\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-medium\nfunc (h *Handler) getAll(w http.ResponseWriter, r *http.Request) {\n\tctx, span := h.r.Tracer(r.Context()).Tracer().Start(r.Context(), \"schema.Handler.getAll\")\n\tdefer span.End()\n\n\tpage, itemsPerPage := x.ParsePagination(r)\n\n\tallSchemas, err := h.r.IdentityTraitsSchemas(r.Context())\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\ttotal := allSchemas.Total()\n\tschemas := allSchemas.List(page, itemsPerPage)\n\n\tss := make(IdentitySchemas, len(schemas))\n\tfor i, schema := range schemas {\n\t\traw, err := h.ReadSchema(ctx, schema.URL)\n\t\tif err != nil {\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"The file for a JSON Schema ID could not be found or opened. This is a configuration issue.\").WithWrap(err)))\n\t\t\treturn\n\t\t}\n\t\tss[i] = identitySchemaContainer{\n\t\t\tID:     schema.ID,\n\t\t\tSchema: raw,\n\t\t}\n\t}\n\n\tx.PaginationHeader(w, *r.URL, int64(total), page, itemsPerPage)\n\th.r.Writer().Write(w, r, ss)\n}\n\nfunc (h *Handler) ReadSchema(ctx context.Context, uri *url.URL) (data []byte, err error) {\n\tctx, span := h.r.Tracer(ctx).Tracer().Start(ctx, \"schema.Handler.ReadSchema\")\n\tdefer otelx.End(span, &err)\n\n\tswitch uri.Scheme {\n\tcase \"file\":\n\t\tdata, err = os.ReadFile(uri.Host + uri.Path) //nolint:gosec\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(fmt.Errorf(\"could not read schema file: %w\", err))\n\t\t}\n\tcase \"base64\":\n\t\tdata, err = base64.StdEncoding.DecodeString(strings.TrimPrefix(uri.String(), \"base64://\"))\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(fmt.Errorf(\"could not decode schema file: %w\", err))\n\t\t}\n\tdefault:\n\t\treq, err := retryablehttp.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(fmt.Errorf(\"could not create request: %w\", err))\n\t\t}\n\t\tresp, err := h.r.HTTPClient(ctx).Do(req)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReason(\"could not fetch schema\").WithError(err.Error()).WithDetail(\"uri\", uri))\n\t\t}\n\t\tdefer func() { _ = resp.Body.Close() }()\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tif resp.StatusCode == http.StatusNotFound {\n\t\t\t\treturn nil, herodot.ErrNotFound.WithDetail(\"url\", uri)\n\t\t\t}\n\t\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithError(\"upstream error\").WithDetail(\"status_code\", resp.StatusCode).WithDetail(\"uri\", uri))\n\t\t}\n\t\tdata, err = io.ReadAll(io.LimitReader(resp.Body, maxSchemaSize))\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReason(\"could not read schema response\").WithError(err.Error()).WithDetail(\"uri\", uri))\n\t\t}\n\t}\n\treturn data, nil\n}\n"
  },
  {
    "path": "schema/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema_test\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/client-go\"\n\t_ \"github.com/ory/jsonschema/v3/fileloader\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestHandler(t *testing.T) {\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tts := contextx.NewConfigurableTestServer(router)\n\tt.Cleanup(ts.Close)\n\n\tmux := http.NewServeMux()\n\tmux.HandleFunc(\"GET /identity.schema.json\", func(w http.ResponseWriter, r *http.Request) {\n\t\tfile, err := os.Open(\"./stub/identity.schema.json\")\n\t\trequire.NoError(t, err)\n\t\t_, err = io.Copy(w, file)\n\t\trequire.NoError(t, err)\n\t})\n\tmux.HandleFunc(\"GET /500\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(500)\n\t})\n\tfileServer := httptest.NewServer(mux)\n\tt.Cleanup(fileServer.Close)\n\n\tschemas := map[string]struct {\n\t\turi                      string\n\t\tgetRaw                   func() ([]byte, error)\n\t\texpectedHttpResponseCode int\n\t}{\n\t\t\"default\": {\n\t\t\turi:                      \"file://./stub/identity.schema.json\",\n\t\t\tgetRaw:                   func() ([]byte, error) { return os.ReadFile(\"./stub/identity.schema.json\") },\n\t\t\texpectedHttpResponseCode: http.StatusOK,\n\t\t},\n\t\t\"identity2\": {\n\t\t\turi:                      \"file://./stub/identity-2.schema.json\",\n\t\t\tgetRaw:                   func() ([]byte, error) { return os.ReadFile(\"./stub/identity-2.schema.json\") },\n\t\t\texpectedHttpResponseCode: http.StatusOK,\n\t\t},\n\t\t\"base64\": {\n\t\t\turi: \"base64://ewogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInR5cGUiOiAib2JqZWN0IiwKICAicHJvcGVydGllcyI6IHsKICAgICJiYXIiOiB7CiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0KICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJiYXIiCiAgXQp9\",\n\t\t\tgetRaw: func() ([]byte, error) {\n\t\t\t\treturn base64.StdEncoding.DecodeString(\"ewogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInR5cGUiOiAib2JqZWN0IiwKICAicHJvcGVydGllcyI6IHsKICAgICJiYXIiOiB7CiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0KICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJiYXIiCiAgXQp9\")\n\t\t\t},\n\t\t\texpectedHttpResponseCode: http.StatusOK,\n\t\t},\n\t\t\"unreachable\": {\n\t\t\turi: \"http://127.0.0.1:12345/unreachable-schema\",\n\t\t\tgetRaw: func() ([]byte, error) {\n\t\t\t\treturn nil, fmt.Errorf(\"connection refused\")\n\t\t\t},\n\t\t\texpectedHttpResponseCode: http.StatusBadGateway,\n\t\t},\n\t\t\"no-file\": {\n\t\t\turi:                      \"file://./stub/does-not-exist.schema.json\",\n\t\t\tgetRaw:                   func() ([]byte, error) { return nil, fmt.Errorf(\"no such file or directory\") },\n\t\t\texpectedHttpResponseCode: http.StatusInternalServerError,\n\t\t},\n\t\t\"directory\": {\n\t\t\turi:    \"file://./stub\",\n\t\t\tgetRaw: func() ([]byte, error) { return nil, fmt.Errorf(\"is a directory\") },\n\t\t\t// On an existing directory, `open(2)` succeeds but `read(2)` fails so it looks like an I/O error.\n\t\t\texpectedHttpResponseCode: http.StatusInternalServerError,\n\t\t},\n\t\t\"file-network\": {\n\t\t\turi:                      fileServer.URL + \"/identity.schema.json\",\n\t\t\tgetRaw:                   func() ([]byte, error) { return os.ReadFile(\"./stub/identity.schema.json\") },\n\t\t\texpectedHttpResponseCode: http.StatusOK,\n\t\t},\n\t\t\"file-network-not-found\": {\n\t\t\turi:                      fileServer.URL + \"/not-found\",\n\t\t\tgetRaw:                   func() ([]byte, error) { return nil, fmt.Errorf(\"could not be found\") },\n\t\t\texpectedHttpResponseCode: http.StatusInternalServerError,\n\t\t},\n\t\t\"file-network-500\": {\n\t\t\turi:                      fileServer.URL + \"/500\",\n\t\t\tgetRaw:                   func() ([]byte, error) { return nil, fmt.Errorf(\"giving up\") },\n\t\t\texpectedHttpResponseCode: http.StatusBadGateway,\n\t\t},\n\t\t\"preset://email\": {\n\t\t\turi:                      \"file://./stub/identity-2.schema.json\",\n\t\t\tgetRaw:                   func() ([]byte, error) { return os.ReadFile(\"./stub/identity-2.schema.json\") },\n\t\t\texpectedHttpResponseCode: http.StatusOK,\n\t\t},\n\t}\n\tconfigSchemas := make(config.Schemas, 0, len(schemas))\n\tfor id, s := range schemas {\n\t\tconfigSchemas = append(configSchemas, config.Schema{\n\t\t\tID:  id,\n\t\t\tURL: s.uri,\n\t\t})\n\t}\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]any{\n\t\tconfig.ViperKeyPublicBaseURL:           ts.URL,\n\t\tconfig.ViperKeyDefaultIdentitySchemaID: \"default\",\n\t\tconfig.ViperKeyIdentitySchemas:         configSchemas,\n\t}))\n\treg.SchemaHandler().RegisterPublicRoutes(router)\n\n\tgetReq := func(ctx context.Context, t *testing.T, path string, expectCode int) []byte {\n\t\tres, err := ts.Client(ctx).Get(ts.URL + path)\n\t\trequire.NoError(t, err)\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\trequire.EqualValuesf(t, expectCode, res.StatusCode, \"%s\", body)\n\t\treturn body\n\t}\n\n\tfor id, s := range schemas {\n\t\tt.Run(fmt.Sprintf(\"case=get %s schema\", id), func(t *testing.T) {\n\n\t\t\t_, err := s.getRaw()\n\t\t\tactual := getReq(t.Context(), t, fmt.Sprintf(\"/schemas/%s\", url.PathEscape(id)), s.expectedHttpResponseCode)\n\t\t\trequire.True(t, json.Valid(actual), string(actual))\n\n\t\t\tswitch s.expectedHttpResponseCode {\n\t\t\tcase http.StatusOK:\n\t\t\t\trequire.NoError(t, err)\n\t\t\tcase http.StatusInternalServerError, http.StatusBadGateway:\n\n\t\t\tdefault:\n\t\t\t\tpanic(\"unreachable\")\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"case=get schema with base64 encoded ID\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\texpected, err := schemas[\"preset://email\"].getRaw()\n\t\trequire.NoError(t, err)\n\n\t\tactual := getReq(t.Context(), t, \"/schemas/\"+base64.RawURLEncoding.EncodeToString([]byte(\"preset://email\")), http.StatusOK)\n\t\trequire.JSONEq(t, string(expected), string(actual))\n\t})\n\n\tt.Run(\"case=get all schemas\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tdefaultSchema, err := configSchemas.FindSchemaByID(\"default\")\n\t\trequire.NoError(t, err)\n\t\tidentity2Schema, err := configSchemas.FindSchemaByID(\"identity2\")\n\t\trequire.NoError(t, err)\n\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyIdentitySchemas, config.Schemas{*defaultSchema, *identity2Schema})\n\n\t\tgetSchemasPaginated := func(t *testing.T, page, perPage, expectCode int) []byte {\n\t\t\treturn getReq(ctx, t, fmt.Sprintf(\"/schemas?page=%d&per_page=%d\", page, perPage), expectCode)\n\t\t}\n\n\t\tbody := getSchemasPaginated(t, 0, 10, http.StatusOK)\n\n\t\tvar result []client.IdentitySchemaContainer\n\t\trequire.NoErrorf(t, json.Unmarshal(body, &result), \"%s\", body)\n\n\t\tvar actualIDs []string\n\t\tfor _, s := range result {\n\t\t\tactualIDs = append(actualIDs, s.Id)\n\t\t}\n\t\tassert.Equal(t, []string{defaultSchema.ID, identity2Schema.ID}, actualIDs)\n\n\t\tassertCorrectSchema := func(t *testing.T, r client.IdentitySchemaContainer) {\n\t\t\texpected, err := schemas[r.Id].getRaw()\n\t\t\trequire.NoError(t, err)\n\t\t\tactual, err := json.Marshal(r.Schema)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.JSONEq(t, string(expected), string(actual))\n\t\t}\n\n\t\tfor _, r := range result {\n\t\t\tassertCorrectSchema(t, r)\n\t\t}\n\n\t\tfor page := range 2 {\n\t\t\tt.Run(fmt.Sprintf(\"page=%d\", page), func(t *testing.T) {\n\t\t\t\tbody := getSchemasPaginated(t, page, 1, http.StatusOK)\n\n\t\t\t\tvar result []client.IdentitySchemaContainer\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &result))\n\n\t\t\t\trequire.Len(t, result, 1)\n\t\t\t\tassert.Equal(t, actualIDs[page], result[0].Id)\n\t\t\t\tassertCorrectSchema(t, result[0])\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=read schema\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfor _, s := range schemas {\n\t\t\texpected, expectedErr := s.getRaw()\n\n\t\t\tactual, err := reg.SchemaHandler().ReadSchema(t.Context(), urlx.ParseOrPanic(s.uri))\n\t\t\tif expectedErr == nil {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t} else {\n\t\t\t\trequire.ErrorContains(t, err, expectedErr.Error()) // not using error.is because some of the errors are not accessible\n\t\t\t}\n\n\t\t\tif expectedErr == nil {\n\t\t\t\trequire.JSONEq(t, string(expected), string(actual))\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "schema/loader.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t_ \"github.com/ory/jsonschema/v3/base64loader\"\n\t_ \"github.com/ory/jsonschema/v3/fileloader\"\n\t_ \"github.com/ory/jsonschema/v3/httploader\"\n)\n"
  },
  {
    "path": "schema/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"io\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/pagination\"\n\t\"github.com/ory/x/urlx\"\n)\n\nvar _ IdentitySchemaList = (*Schemas)(nil)\n\ntype Schemas []Schema\n\ntype IdentitySchemaProvider interface {\n\tIdentityTraitsSchemas(ctx context.Context) (IdentitySchemaList, error)\n}\n\ntype deps interface {\n\tconfig.Provider\n}\n\ntype DefaultIdentitySchemaProvider struct {\n\td deps\n}\n\nfunc NewDefaultIdentityTraitsProvider(d deps) *DefaultIdentitySchemaProvider {\n\treturn &DefaultIdentitySchemaProvider{d: d}\n}\n\nfunc (d *DefaultIdentitySchemaProvider) IdentityTraitsSchemas(ctx context.Context) (IdentitySchemaList, error) {\n\tms, err := d.d.Config().IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ss Schemas\n\tfor _, s := range ms {\n\t\tsurl, err := url.Parse(s.URL)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\n\t\tss = append(ss, Schema{\n\t\t\tID:     s.ID,\n\t\t\tURL:    surl,\n\t\t\tRawURL: s.URL,\n\t\t})\n\t}\n\n\treturn ss, nil\n}\n\ntype IdentitySchemaList interface {\n\tGetByID(id string) (*Schema, error)\n\tTotal() int\n\tList(page, perPage int) Schemas\n}\n\nfunc (s Schemas) GetByID(id string) (*Schema, error) {\n\tid = cmp.Or(id, config.DefaultIdentityTraitsSchemaID)\n\n\tif ss, ok := s.findSchemaByID(id); ok {\n\t\treturn ss, nil\n\t}\n\n\tif decodedID, ok := TryDecodeID(id); ok {\n\t\tif ss, ok := s.findSchemaByID(decodedID); ok {\n\t\t\treturn ss, nil\n\t\t}\n\t}\n\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to find JSON Schema ID: %s\", id))\n}\n\nfunc (s Schemas) Total() int {\n\treturn len(s)\n}\n\nfunc (s Schemas) List(page, perPage int) Schemas {\n\tif page < 0 {\n\t\tpage = 0\n\t}\n\tif perPage < 1 {\n\t\tperPage = 1\n\t}\n\tstart, end := pagination.Index((page+1)*perPage, page*perPage, len(s))\n\treturn s[start:end]\n}\n\nfunc (s Schemas) findSchemaByID(id string) (*Schema, bool) {\n\tfor _, ss := range s {\n\t\tif ss.ID == id {\n\t\t\treturn &ss, true\n\t\t}\n\t}\n\treturn nil, false\n}\n\nvar (\n\torderedKeyCacheMutex sync.RWMutex\n\torderedKeyCache      map[string][]string\n)\n\nfunc init() {\n\torderedKeyCache = make(map[string][]string)\n}\n\nfunc computeKeyPositions(schema []byte, dest *[]string, parents []string) {\n\tswitch gjson.GetBytes(schema, \"type\").String() {\n\tcase \"object\":\n\t\tgjson.GetBytes(schema, \"properties\").ForEach(func(key, value gjson.Result) bool {\n\t\t\tcomputeKeyPositions([]byte(value.Raw), dest, append(parents, strings.ReplaceAll(key.String(), \".\", \"\\\\.\")))\n\t\t\treturn true\n\t\t})\n\tdefault:\n\t\t*dest = append(*dest, strings.Join(parents, \".\"))\n\t}\n}\n\nfunc GetKeysInOrder(ctx context.Context, schemaRef string) ([]string, error) {\n\torderedKeyCacheMutex.RLock()\n\tkeysInOrder, ok := orderedKeyCache[schemaRef]\n\torderedKeyCacheMutex.RUnlock()\n\tif !ok {\n\t\tsio, err := jsonschema.LoadURL(ctx, schemaRef)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t\tschema, err := io.ReadAll(io.LimitReader(sio, 1024*1024))\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\n\t\tcomputeKeyPositions(schema, &keysInOrder, []string{})\n\t\torderedKeyCacheMutex.Lock()\n\t\torderedKeyCache[schemaRef] = keysInOrder\n\t\torderedKeyCacheMutex.Unlock()\n\t}\n\n\treturn keysInOrder, nil\n}\n\ntype Schema struct {\n\tID  string   `json:\"id\"`\n\tURL *url.URL `json:\"-\"`\n\t// RawURL contains the raw URL value as it was passed in the configuration. URL parsing can break base64 encoded URLs.\n\tRawURL string `json:\"url\"`\n}\n\nfunc (s *Schema) SchemaURL(host *url.URL) *url.URL {\n\treturn IDToURL(host, s.ID)\n}\n\nfunc IDToURL(host *url.URL, id string) *url.URL {\n\treturn urlx.AppendPaths(host, SchemasPath, base64.RawURLEncoding.EncodeToString([]byte(id)))\n}\n\nfunc TryDecodeID(encoded string) (string, bool) {\n\tdecoded, err := base64.RawURLEncoding.DecodeString(encoded)\n\tif err != nil {\n\t\treturn \"\", false\n\t}\n\treturn string(decoded), true\n}\n"
  },
  {
    "path": "schema/schema_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/schema\"\n\tschematest \"github.com/ory/kratos/schema/test\"\n)\n\nfunc TestDefaultIdentityTraitsProvider(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tschematest.TestIdentitySchemaProvider(t, schema.NewDefaultIdentityTraitsProvider(reg))\n}\n\nfunc TestGetKeysInOrder(t *testing.T) {\n\tfor i, tc := range []struct {\n\t\tschemaRef string\n\t\tkeys      []string\n\t\tpath      string\n\t}{\n\t\t{schemaRef: \"file://./stub/identity.schema.json\", keys: []string{\"bar\", \"email\"}},\n\t\t{schemaRef: \"file://./stub/complex.schema.json\", keys: []string{\n\t\t\t\"meal.name\", \"meal.chef\", \"traits.email\",\n\t\t\t\"traits.stringy\", \"traits.numby\", \"traits.booly\", \"traits.should_big_number\", \"traits.should_long_string\",\n\t\t\t\"fruits\", \"vegetables\",\n\t\t}},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d schemaRef=%s\", i, tc.schemaRef), func(t *testing.T) {\n\t\t\tactual, err := schema.GetKeysInOrder(context.Background(), tc.schemaRef)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tc.keys, actual)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "schema/stub/complex.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/complex.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\"meal\"],\n  \"properties\": {\n    \"meal\": {\n      \"type\": \"object\",\n      \"required\": [\"name\"],\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\"\n        },\n        \"chef\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"booly\": {\n          \"type\": \"boolean\"\n        },\n        \"should_big_number\": {\n          \"type\": \"number\",\n          \"minimum\": 1200\n        },\n        \"should_long_string\": {\n          \"type\": \"string\",\n          \"minLength\": 25\n        }\n      }\n    },\n    \"fruits\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"vegetables\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"$ref\": \"#/definitions/veggie\"\n      }\n    }\n  },\n  \"definitions\": {\n    \"veggie\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"veggieName\",\n        \"veggieLike\"\n      ],\n      \"properties\": {\n        \"veggieName\": {\n          \"type\": \"string\"\n        },\n        \"veggieLike\": {\n          \"type\": \"boolean\"\n        },\n        \"veggieAmount\": {\n          \"type\": \"number\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "schema/stub/extension/invalid.schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": 0\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "schema/stub/extension/schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          },\n          \"totp\": {\n            \"account_name\": true\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "schema/stub/extension/schema.nested.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"emails\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\",\n        \"format\": \"email\",\n        \"ory.sh/kratos\": {\n          \"credentials\": {\n            \"password\": {\n              \"identifier\": true\n            },\n            \"totp\": {\n              \"account_name\": true\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "schema/stub/identity-2.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"bar\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "schema/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"bar\": {\n      \"type\": \"string\"\n    },\n    \"email\": {\n      \"type\": \"string\",\n      \"ory.sh/kratos\": {\n        \"credentials\": {\n          \"password\": {\n            \"identifier\": true\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "schema/stub/validator/firstName.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/firstName.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"firstName\": {\n      \"type\": \"string\",\n      \"description\": \"The person's first name.\"\n    },\n    \"lastName\": {\n      \"type\": \"string\",\n      \"description\": \"The person's last name.\"\n    },\n    \"age\": {\n      \"description\": \"Age in years which must be equal to or greater than zero.\",\n      \"type\": \"integer\",\n      \"minimum\": 1\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "schema/stub/validator/whatever.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/whatever.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"whatever\": {\n      \"type\": \"string\",\n      \"description\": \"The person's first name.\"\n    },\n    \"lastName\": {\n      \"type\": \"string\",\n      \"description\": \"The person's last name.\"\n    },\n    \"age\": {\n      \"description\": \"Age in years which must be equal to or greater than zero.\",\n      \"type\": \"integer\",\n      \"minimum\": 1\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "schema/test/schema_provider.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestIdentitySchemaProvider(t *testing.T, provider schema.IdentitySchemaProvider) {\n\turlFromID := func(id string) string {\n\t\treturn fmt.Sprintf(\"http://%s.com\", id)\n\t}\n\n\tschemas := schema.Schemas{\n\t\t{ID: \"foo\"},\n\t\t{ID: \"preset://email\"},\n\t\t{ID: config.DefaultIdentityTraitsSchemaID},\n\t}\n\n\tfor i := range schemas {\n\t\traw := urlFromID(schemas[i].ID)\n\t\tschemas[i].RawURL = raw\n\t\tschemas[i].URL = urlx.ParseOrPanic(raw)\n\t}\n\n\tctx := contextx.WithConfigValues(t.Context(), map[string]any{\n\t\tconfig.ViperKeyIdentitySchemas: func() (cs config.Schemas) {\n\t\t\tfor _, s := range schemas {\n\t\t\t\tcs = append(cs, config.Schema{\n\t\t\t\t\tID:  s.ID,\n\t\t\t\t\tURL: s.RawURL,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn cs\n\t\t}(),\n\t})\n\n\tlist, err := provider.IdentityTraitsSchemas(ctx)\n\trequire.NoError(t, err)\n\n\tt.Run(\"GetByID\", func(t *testing.T) {\n\t\tt.Run(\"case=get with raw schemaID\", func(t *testing.T) {\n\t\t\tfor _, schema := range schemas {\n\t\t\t\tactual, err := list.GetByID(schema.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, schema, *actual)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=get with encoded schemaID\", func(t *testing.T) {\n\t\t\tfor _, schema := range schemas {\n\t\t\t\tencodedID := base64.RawURLEncoding.EncodeToString([]byte(schema.ID))\n\n\t\t\t\tactual, err := list.GetByID(encodedID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, schema, *actual)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=get default schema\", func(t *testing.T) {\n\t\t\ts1, err := list.GetByID(\"\")\n\t\t\trequire.NoError(t, err)\n\t\t\ts2, err := list.GetByID(config.DefaultIdentityTraitsSchemaID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, &schemas[2], s1)\n\t\t\tassert.Equal(t, &schemas[2], s2)\n\t\t})\n\n\t\tt.Run(\"case=should return error on not existing id\", func(t *testing.T) {\n\t\t\ts, err := list.GetByID(\"not existing id\")\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Equal(t, (*schema.Schema)(nil), s)\n\t\t})\n\t})\n\n\tt.Run(\"List\", func(t *testing.T) {\n\t\tt.Run(\"case=get all schemas\", func(t *testing.T) {\n\t\t\tp0 := list.List(0, 4)\n\t\t\tassert.Equal(t, schemas, p0)\n\t\t})\n\n\t\tt.Run(\"case=smaller pages\", func(t *testing.T) {\n\t\t\tp0, p1 := list.List(0, 2), list.List(1, 2)\n\t\t\tassert.Equal(t, schemas, append(p0, p1...))\n\t\t})\n\n\t\tt.Run(\"case=indexes out of range\", func(t *testing.T) {\n\t\t\tp0 := list.List(-1, 10)\n\t\t\tassert.Equal(t, schemas, p0)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "schema/validator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"sync\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\ntype Validator struct {\n\tsync.RWMutex\n}\n\ntype ValidationProvider interface {\n\tSchemaValidator() *Validator\n}\n\nfunc NewValidator() *Validator {\n\treturn &Validator{}\n}\n\ntype validatorOptions struct {\n\te *ExtensionRunner\n}\n\nfunc WithExtensionRunner(e *ExtensionRunner) func(*validatorOptions) {\n\treturn func(o *validatorOptions) {\n\t\to.e = e\n\t}\n}\n\nfunc (v *Validator) Validate(\n\tctx context.Context,\n\thref string,\n\tdocument json.RawMessage,\n\topts ...func(*validatorOptions),\n) error {\n\tvar o validatorOptions\n\tfor _, opt := range opts {\n\t\topt(&o)\n\t}\n\n\tcompiler := jsonschema.NewCompiler()\n\tresource, err := jsonschema.LoadURL(ctx, href)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"Unable to load or parse the JSON schema.\").WithWrap(err).WithDebugf(\"%s\", err))\n\t}\n\n\tif o.e != nil {\n\t\to.e.Register(compiler)\n\t}\n\n\tif err := compiler.AddResource(href, resource); err != nil {\n\t\treturn errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"Unable to parse validate JSON object against JSON schema.\").WithWrap(err).WithDebugf(\"%s\", err))\n\t}\n\n\tschema, err := compiler.Compile(ctx, href)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"Unable to parse validate JSON object against JSON schema.\").WithWrap(err).WithDebugf(\"%s\", err))\n\t}\n\n\t// we decode explicitly here, so we can handle the error, and it is not lost in the schema validation\n\tdec, err := jsonschema.DecodeJSON(bytes.NewBuffer(document))\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to parse validate JSON object against JSON schema.\").WithWrap(err).WithDebugf(\"%s\", err))\n\t}\n\tif err := schema.ValidateInterface(dec); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif o.e != nil {\n\t\treturn o.e.Finish()\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "schema/validator_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/ory/jsonschema/v3/httploader\"\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSchemaValidator(t *testing.T) {\n\trouter := http.NewServeMux()\n\tfs := http.StripPrefix(\"/schema\", http.FileServer(http.Dir(\"stub/validator\")))\n\trouter.HandleFunc(\"/schema/{name}\", func(w http.ResponseWriter, r *http.Request) {\n\t\tfs.ServeHTTP(w, r)\n\t})\n\tts := httptest.NewServer(router)\n\tdefer ts.Close()\n\n\tctx := context.WithValue(ctx, httploader.ContextKey, httpx.NewResilientClient())\n\tfor k, tc := range []struct {\n\t\ti   json.RawMessage\n\t\terr string\n\t\tu   string\n\t}{\n\t\t{\n\t\t\ti: json.RawMessage(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t},\n\t\t{\n\t\t\ti:   json.RawMessage(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": -1 }`),\n\t\t\terr: \"I[#/age] S[#/properties/age/minimum] must be >= 1 but found -1\",\n\t\t},\n\t\t{\n\t\t\ti:   json.RawMessage(`{ \"whatever\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\terr: `I[#] S[#/additionalProperties] additionalProperties \"whatever\" not allowed`,\n\t\t},\n\t\t{\n\t\t\tu: ts.URL + \"/schema/whatever.schema.json\",\n\t\t\ti: json.RawMessage(`{ \"whatever\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t},\n\t\t{\n\t\t\tu:   ts.URL + \"/schema/whatever.schema.json\",\n\t\t\ti:   json.RawMessage(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\terr: `I[#] S[#/additionalProperties] additionalProperties \"firstName\" not allowed`,\n\t\t},\n\t\t{\n\t\t\tu:   ts.URL,\n\t\t\ti:   json.RawMessage(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\terr: \"Invalid configuration\",\n\t\t},\n\t\t{\n\t\t\tu:   \"not-a-url\",\n\t\t\ti:   json.RawMessage(`{ \"firstName\": \"first-name\", \"lastName\": \"last-name\", \"age\": 1 }`),\n\t\t\terr: \"Invalid configuration\",\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\terr := NewValidator().Validate(ctx, cmp.Or(tc.u, ts.URL+\"/schema/firstName.schema.json\"), tc.i)\n\t\t\tif tc.err == \"\" {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t} else {\n\t\t\t\trequire.EqualError(t, err, tc.err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "script/add-down-migrations.sh",
    "content": "#!/usr/bin/env bash\n\n# This script adds empty down migrations for any migration that misses them.\n# Adding them is necessary because if the down migration is missing, the\n# migration will only be applied once, even if the database is completely\n# rolled back.\n# In newer versions of ory/x/popx, the migration box enforces that all up\n# migrations have a down migration. Use this script to add them.\n\nset -Eeuo pipefail\n\nfor f in $(find . -name \"*.up.sql\"); do\n\tbase=$(basename $f)\n\tdir=$(dirname $f)\n\tmigra_name=$(echo $base | sed -e \"s/\\..*\\.up\\.sql//\" | sed -e \"s/\\.up\\.sql//\")\n\tif ! compgen -G \"$dir/$migra_name*.down.sql\" > /dev/null; then\n\t\techo \"Adding empty down migration for $f\"\n\t\ttouch $dir/$migra_name.down.sql\n\tfi\ndone\n"
  },
  {
    "path": "script/debug-entrypoint.sh",
    "content": "#!/usr/bin/env bash\n\nFILE_CHANGE_LOG_FILE=/tmp/changes.log\nSERVICE_ARGS=\"$@\"\n\nlog() {\n  echo \"***** $1 *****\"\n}\n\ninit() {\n  log \"Initializing\"\n  truncate -s 0 ${FILE_CHANGE_LOG_FILE}\n  tail -f ${FILE_CHANGE_LOG_FILE} &\n}\n\nbuild() {\n  log \"Building ${SERVICE_NAME} binary\"\n  go env -w GOPROXY=\"proxy.golang.org,direct\"\n  go mod download\n  go build -buildvcs=false -gcflags \"all=-N -l\" -o /${SERVICE_NAME}\n}\n\nstart() {\n  log \"Starting Delve\"\n  dlv --listen=:${DELVE_PORT} --headless=true --api-version=2 --accept-multiclient exec /${SERVICE_NAME} -- ${SERVICE_ARGS} &\n}\n\nrestart() {\n  build\n\n  log \"Killing old processes\"\n  killall dlv\n  killall ${SERVICE_NAME}\n\n  start\n}\n\nwatch() {\n  log \"Watching for changes\"\n  inotifywait -e \"MODIFY,DELETE,MOVED_TO,MOVED_FROM\" -m -r ${PWD} | (\n    while true; do\n      read path action file\n      ext=${file: -3}\n      if [[ \"$ext\" == \".go\" ]]; then\n        echo \"$file\"\n      fi\n    done\n  ) | (\n    WAITING=\"\"\n    while true; do\n      file=\"\"\n      read -t 1 file\n      if test -z \"$file\"; then\n        if test ! -z \"$WAITING\"; then\n          echo \"CHANGED\"\n          WAITING=\"\"\n        fi\n      else\n        log \"File ${file} changed\" >> ${FILE_CHANGE_LOG_FILE}\n        WAITING=1\n      fi\n    done\n  ) | (\n    while true; do\n      read TMP\n      restart\n    done\n  )\n}\n\n# main part\ninit\nbuild\nstart\nwatch\n"
  },
  {
    "path": "script/render-schemas.sh",
    "content": "#!/usr/bin/env bash\n\nset -euxo pipefail\n\nschema_version=\"${1:-$(git rev-parse --short HEAD)}\"\n\nsed \"s!ory://tracing-config!https://raw.githubusercontent.com/ory/kratos/$schema_version/oryx/otelx/config.schema.json!g;\" embedx/config.schema.json > .schemastore/config.schema.json\n\ngit config user.email \"60093411+ory-bot@users.noreply.github.com\"\ngit config user.name \"ory-bot\"\n\ngit add .schemastore/config.schema.json\ngit commit -m \"autogen: render config schema\" || true\n"
  },
  {
    "path": "script/test-envs.sh",
    "content": "#!/usr/bin/env bash\n\nexport TEST_DATABASE_MYSQL=\"mysql://root:secret@(127.0.0.1:3444)/mysql?parseTime=true&multiStatements=true\"\nexport TEST_DATABASE_POSTGRESQL=\"postgres://postgres:secret@127.0.0.1:3445/postgres?sslmode=disable\"\nexport TEST_DATABASE_COCKROACHDB=\"cockroach://root@127.0.0.1:3446/defaultdb?sslmode=disable\"\n"
  },
  {
    "path": "script/testenv.sh",
    "content": "#!/usr/bin/env bash\n\ndocker rm -f kratos_test_database_mysql kratos_test_database_postgres kratos_test_database_cockroach kratos_test_hydra || true\ndocker run --name kratos_test_database_mysql -p 3444:3306 -e MYSQL_ROOT_PASSWORD=secret -d mysql:8.0\ndocker run --name kratos_test_database_postgres -p 3445:5432 -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=postgres -d postgres:14 postgres -c log_statement=all\ndocker run --name kratos_test_database_cockroach -p 3446:26257 -p 3447:8080 -d cockroachdb/cockroach:latest-v25.4 start-single-node --insecure\ndocker run --name kratos_test_hydra -p 4444:4444 -p 4445:4445 -d -e DSN=memory -e URLS_SELF_ISSUER=http://localhost:4444/ -e URLS_LOGIN=http://localhost:4446/login -e URLS_CONSENT=http://localhost:4446/consent oryd/hydra:v2.0.2 serve all --dev\ndocker pull oryd/hydra:v2.2.0\n\nsource script/test-envs.sh\n"
  },
  {
    "path": "script/unset-e2e-uis.sh",
    "content": "#!/usr/bin/env bash\n\nunset NODE_UI_PATH\nunset RN_UI_PATH\nunset REACT_UI_PATH\n"
  },
  {
    "path": "selfservice/errorx/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage errorx\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\n// swagger:model flowError\ntype ErrorContainer struct {\n\t// ID of the error container.\n\t//\n\t// required: true\n\tID  uuid.UUID `db:\"id\" json:\"id\"`\n\tNID uuid.UUID `json:\"-\" db:\"nid\"`\n\n\t// The error\n\tErrors json.RawMessage `json:\"error\" db:\"errors\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"created_at\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"updated_at\" db:\"updated_at\"`\n\n\tSeenAt    sql.NullTime `json:\"-\" db:\"seen_at\"`\n\tWasSeen   bool         `json:\"-\" db:\"was_seen\"`\n\tCSRFToken string       `db:\"csrf_token\" json:\"-\"`\n}\n\nfunc (e ErrorContainer) TableName(ctx context.Context) string {\n\treturn \"selfservice_errors\"\n}\n"
  },
  {
    "path": "selfservice/errorx/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage errorx\n\nimport (\n\t\"cmp\"\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n)\n\nconst RouteGet = \"/self-service/errors\"\n\nvar stub500, _ = json.Marshal(herodot.ErrInternalServerError.WithReasonf(\"This is a stub error.\"))\n\ntype (\n\thandlerDependencies interface {\n\t\thttpx.WriterProvider\n\t\tPersistenceProvider\n\t\tconfig.Provider\n\t}\n\tHandlerProvider interface {\n\t\tSelfServiceErrorHandler() *Handler\n\t}\n\tHandler struct {\n\t\tr    handlerDependencies\n\t\tcsrf nosurfx.CSRFToken\n\t}\n)\n\nfunc NewHandler(\n\tr handlerDependencies,\n) *Handler {\n\treturn &Handler{r: r, csrf: nosurf.Token}\n}\n\nfunc (h *Handler) WithTokenGenerator(f func(r *http.Request) string) {\n\th.csrf = f\n}\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\tpublic.GET(RouteGet, h.publicFetchError)\n}\n\nfunc (h *Handler) RegisterAdminRoutes(public *httprouterx.RouterAdmin) {\n\tpublic.GET(RouteGet, redir.RedirectToPublicRoute(h.r))\n}\n\n// swagger:parameters getFlowError\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getFlowError struct {\n\t// Error is the error's ID\n\t//\n\t// in: query\n\t// required: true\n\tID string `json:\"id\"`\n}\n\n// swagger:route GET /self-service/errors frontend getFlowError\n//\n// # Get User-Flow Errors\n//\n// This endpoint returns the error associated with a user-facing self service errors.\n//\n// This endpoint supports stub values to help you implement the error UI:\n//\n// - `?id=stub:500` - returns a stub 500 (Internal Server Error) error.\n//\n// More information can be found at [Ory Kratos User User Facing Error Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors).\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: flowError\n//\t  403: errorGeneric\n//\t  404: errorGeneric\n//\t  500: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-low\nfunc (h *Handler) publicFetchError(w http.ResponseWriter, r *http.Request) {\n\tif err := h.fetchError(w, r); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n}\n\nfunc (h *Handler) fetchError(w http.ResponseWriter, r *http.Request) error {\n\tid := cmp.Or(r.URL.Query().Get(\"error\"), r.URL.Query().Get(\"id\"))\n\tswitch id {\n\tcase \"stub:500\":\n\t\th.r.Writer().Write(w, r, &ErrorContainer{ID: x.NewUUID(), Errors: stub500})\n\t\treturn nil\n\t}\n\n\tes, err := h.r.SelfServiceErrorPersister().ReadErrorContainer(r.Context(), x.ParseUUID(id))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\th.r.Writer().Write(w, r, es)\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/errorx/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage errorx_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/errorsx\"\n\t\"github.com/ory/x/httprouterx\"\n)\n\nfunc TestHandler(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\th := errorx.NewHandler(reg)\n\n\tt.Run(\"case=public authorization\", func(t *testing.T) {\n\t\trouter := httprouterx.NewTestRouterPublic(t)\n\t\tns := nosurfx.NewTestCSRFHandler(router, reg)\n\n\t\th.RegisterPublicRoutes(router)\n\t\trouter.Handler(\"GET\", \"/regen\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tns.RegenerateToken(w, r)\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t}))\n\t\trouter.Handler(\"GET\", \"/set-error\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tid, err := reg.SelfServiceErrorPersister().CreateErrorContainer(context.Background(), nosurf.Token(r), herodot.ErrNotFound.WithReason(\"foobar\"))\n\t\t\trequire.NoError(t, err)\n\t\t\t_, _ = w.Write([]byte(id.String()))\n\t\t}))\n\n\t\tts := httptest.NewServer(ns)\n\t\tdefer ts.Close()\n\n\t\tgetBody := func(t *testing.T, hc *http.Client, path string, expectedCode int) []byte {\n\t\t\tres, err := hc.Get(ts.URL + path)\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\trequire.EqualValues(t, expectedCode, res.StatusCode)\n\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\trequire.NoError(t, err)\n\t\t\treturn body\n\t\t}\n\t\texpectedError := x.MustEncodeJSON(t, herodot.ErrNotFound.WithReason(\"foobar\"))\n\n\t\tt.Run(\"call with valid csrf cookie\", func(t *testing.T) {\n\t\t\thc := &http.Client{}\n\t\t\tid := getBody(t, hc, \"/set-error\", http.StatusOK)\n\t\t\tactual := getBody(t, hc, errorx.RouteGet+\"?id=\"+string(id), http.StatusOK)\n\t\t\tassert.JSONEq(t, expectedError, gjson.GetBytes(actual, \"error\").Raw, \"%s\", actual)\n\n\t\t\t// We expect a forbid error if the error is not found, regardless of CSRF\n\t\t\t_ = getBody(t, hc, errorx.RouteGet+\"?id=does-not-exist\", http.StatusNotFound)\n\t\t})\n\t})\n\n\tt.Run(\"case=stubs\", func(t *testing.T) {\n\t\trouter := httprouterx.NewTestRouterPublic(t)\n\t\th.RegisterPublicRoutes(router)\n\t\tts := httptest.NewServer(router)\n\t\tdefer ts.Close()\n\n\t\tres, err := ts.Client().Get(ts.URL + errorx.RouteGet + \"?id=stub:500\")\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\tactual, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\n\t\tassert.EqualValues(t, \"This is a stub error.\", gjson.GetBytes(actual, \"error.reason\").String())\n\t})\n\n\tt.Run(\"case=errors types\", func(t *testing.T) {\n\t\trouter := httprouterx.NewTestRouterPublic(t)\n\t\th.RegisterPublicRoutes(router)\n\t\tts := httptest.NewServer(router)\n\t\tdefer ts.Close()\n\n\t\tfor k, tc := range []struct {\n\t\t\tgave error\n\t\t}{\n\t\t\t{gave: herodot.ErrNotFound.WithReason(\"foobar\")},\n\t\t\t{gave: herodot.ErrNotFound.WithReason(\"foobar\")},\n\t\t\t{gave: errors.WithStack(herodot.ErrNotFound.WithReason(\"foobar\"))},\n\t\t\t{gave: errors.WithStack(herodot.ErrNotFound.WithReason(\"foobar\").WithTrace(errors.New(\"asdf\")))},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tcsrf := x.NewUUID()\n\t\t\t\tid, err := reg.SelfServiceErrorPersister().CreateErrorContainer(context.Background(), csrf.String(), tc.gave)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tres, err := ts.Client().Get(ts.URL + errorx.RouteGet + \"?id=\" + id.String())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\tactual, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tgg := errorsx.Cause(tc.gave)\n\t\t\t\texpected, err := json.Marshal(errorx.ErrorContainer{\n\t\t\t\t\tID:     id,\n\t\t\t\t\tErrors: x.RequireJSONMarshal(t, gg),\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassertx.EqualAsJSONExcept(t, json.RawMessage(expected), json.RawMessage(actual), []string{\"created_at\", \"updated_at\"})\n\t\t\t\tassert.Empty(t, gjson.GetBytes(actual, \"csrf_token\").String())\n\t\t\t\tassert.JSONEq(t, string(x.RequireJSONMarshal(t, gg)), gjson.GetBytes(actual, \"error\").Raw)\n\t\t\t\tt.Logf(\"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/errorx/manager.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage errorx\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\ntype (\n\tmanagerDependencies interface {\n\t\tPersistenceProvider\n\t\tlogrusx.Provider\n\t\thttpx.WriterProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tconfig.Provider\n\t}\n\n\tManager struct {\n\t\td managerDependencies\n\t}\n\n\tManagementProvider interface {\n\t\t// SelfServiceErrorManager returns the errorx.Manager.\n\t\tSelfServiceErrorManager() *Manager\n\t}\n)\n\nfunc NewManager(d managerDependencies) *Manager {\n\treturn &Manager{d: d}\n}\n\n// Create is a simple helper that saves all errors in the store and returns the\n// error url, appending the error ID.\nfunc (m *Manager) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) (string, error) {\n\tm.d.Logger().WithError(err).WithRequest(r).Errorf(\"An error occurred and is being forwarded to the error user interface.\")\n\n\tid, addErr := m.d.SelfServiceErrorPersister().CreateErrorContainer(ctx, m.d.GenerateCSRFToken(r), err)\n\tif addErr != nil {\n\t\treturn \"\", addErr\n\t}\n\tq := url.Values{}\n\tq.Set(\"id\", id.String())\n\n\treturn urlx.CopyWithQuery(m.d.Config().SelfServiceFlowErrorURL(ctx), q).String(), nil\n}\n\n// Forward is a simple helper that saves all errors in the store and forwards the HTTP Request\n// to the error url, appending the error ID.\nfunc (m *Manager) Forward(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {\n\tif x.IsJSONRequest(r) {\n\t\tm.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tto, errCreate := m.Create(ctx, w, r, err)\n\tif errCreate != nil {\n\t\t// Everything failed. Resort to standard error output.\n\t\tm.d.Logger().WithError(errCreate).WithRequest(r).Error(\"Failed to create error container.\")\n\t\tm.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif x.AcceptsJSON(r) {\n\t\tm.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\thttp.Redirect(w, r, to, http.StatusSeeOther)\n}\n"
  },
  {
    "path": "selfservice/errorx/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage errorx\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype (\n\tPersister interface {\n\t\t// CreateErrorContainer adds an error to the manager and returns a\n\t\t// unique identifier or an error if insertion fails.\n\t\tCreateErrorContainer(ctx context.Context, csrfToken string, err error) (uuid.UUID, error)\n\n\t\t// ReadErrorContainer returns an error by its unique identifier and\n\t\t// marks the error as read. If an error occurs during retrieval the\n\t\t// second return parameter is an error.\n\t\tReadErrorContainer(ctx context.Context, id uuid.UUID) (*ErrorContainer, error)\n\n\t\t// ClearErrorContainers clears read containers that are older than a\n\t\t// certain amount of time. If force is set to true, unread errors will\n\t\t// be cleared as well.\n\t\tClearErrorContainers(ctx context.Context, olderThan time.Duration, force bool) error\n\t}\n\n\tPersistenceProvider interface {\n\t\tSelfServiceErrorPersister() Persister\n\t}\n)\n"
  },
  {
    "path": "selfservice/errorx/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage errorx\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestPersister(ctx context.Context, p persistence.Persister) func(t *testing.T) {\n\ttoJSON := func(t *testing.T, in interface{}) string {\n\t\tout, err := json.Marshal(in)\n\t\trequire.NoError(t, err)\n\t\treturn string(out)\n\t}\n\n\treturn func(t *testing.T) {\n\t\t_, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tt.Run(\"case=not found\", func(t *testing.T) {\n\t\t\t_, err := p.ReadErrorContainer(ctx, x.NewUUID())\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tt.Run(\"case=en- and decode properly\", func(t *testing.T) {\n\t\t\tactualID, err := p.CreateErrorContainer(ctx, \"nosurf\", herodot.ErrNotFound.WithReason(\"foobar\"))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactual, err := p.ReadErrorContainer(ctx, actualID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.JSONEq(t, `{\"code\":404,\"status\":\"Not Found\",\"reason\":\"foobar\",\"message\":\"The requested resource could not be found\"}`, gjson.Get(toJSON(t, actual), \"error\").String(), toJSON(t, actual))\n\t\t})\n\n\t\tt.Run(\"case=clear\", func(t *testing.T) {\n\t\t\tactualID, err := p.CreateErrorContainer(ctx, \"nosurf\", herodot.ErrNotFound.WithReason(\"foobar\"))\n\t\t\trequire.NoError(t, err)\n\n\t\t\t_, err = p.ReadErrorContainer(ctx, actualID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// We need to wait for at least one second or MySQL will randomly fail as it does not support\n\t\t\t// millisecond resolution on timestamp columns.\n\t\t\ttime.Sleep(time.Second + time.Millisecond*500)\n\t\t\trequire.NoError(t, p.ClearErrorContainers(ctx, time.Second, false))\n\t\t\tgot, err := p.ReadErrorContainer(ctx, actualID)\n\t\t\trequire.Error(t, err, \"%+v\", got)\n\t\t})\n\n\t\tt.Run(\"case=network\", func(t *testing.T) {\n\t\t\tt.Run(\"can not read error from another network\", func(t *testing.T) {\n\t\t\t\tcreated, err := p.CreateErrorContainer(ctx, \"nosurf\", herodot.ErrNotFound.WithReason(\"foobar\"))\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\ttime.Sleep(time.Second + time.Millisecond*500)\n\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err = other.ReadErrorContainer(ctx, created)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\tt.Run(\"can not clear another network\", func(t *testing.T) {\n\t\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\trequire.NoError(t, other.ClearErrorContainers(ctx, time.Second, true))\n\n\t\t\t\t\tc, err := p.ReadErrorContainer(ctx, created)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Contains(t, string(c.Errors), \"foobar\")\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/.schema/method.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/flow/method.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"method\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/config.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"github.com/ory/kratos/ui/container\"\n)\n\n// swagger:ignore\ntype MethodConfigurator interface {\n\tcontainer.NodeGetter\n\n\tcontainer.ErrorParser\n\n\t// form.NodeSetter\n\t// form.NodeUnsetter\n\tcontainer.ValueSetter\n\n\tcontainer.Resetter\n\tcontainer.MessageResetter\n\tcontainer.CSRFSetter\n\tcontainer.FieldSorter\n}\n"
  },
  {
    "path": "selfservice/flow/continue_with.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/urlx\"\n)\n\n// swagger:model continueWith\ntype ContinueWith interface {\n\t//swagger:ignore\n\tGetAction() string\n}\n\n// swagger:enum ContinueWithActionSetOrySessionToken\ntype ContinueWithActionSetOrySessionToken string\n\nconst (\n\tContinueWithActionSetOrySessionTokenString ContinueWithActionSetOrySessionToken = \"set_ory_session_token\" // #nosec G101 -- only a key constant\n)\n\nvar _ ContinueWith = new(ContinueWithSetOrySessionToken)\n\n// Indicates that a session was issued, and the application should use this token for authenticated requests\n//\n// swagger:model continueWithSetOrySessionToken\ntype ContinueWithSetOrySessionToken struct {\n\t// Action will always be `set_ory_session_token`\n\t//\n\t// required: true\n\tAction ContinueWithActionSetOrySessionToken `json:\"action\"`\n\n\t// Token is the token of the session\n\t//\n\t// required: true\n\tOrySessionToken string `json:\"ory_session_token\"`\n}\n\nfunc (ContinueWithSetOrySessionToken) AppendTo(url.Values) url.Values {\n\treturn nil\n}\n\nfunc NewContinueWithSetToken(t string) *ContinueWithSetOrySessionToken {\n\treturn &ContinueWithSetOrySessionToken{\n\t\tAction:          ContinueWithActionSetOrySessionTokenString,\n\t\tOrySessionToken: t,\n\t}\n}\n\nfunc (c ContinueWithSetOrySessionToken) GetAction() string {\n\treturn string(c.Action)\n}\n\n// swagger:enum ContinueWithActionShowVerificationUI\ntype ContinueWithActionShowVerificationUI string\n\n// #nosec G101 -- only a key constant\nconst (\n\tContinueWithActionShowVerificationUIString ContinueWithActionShowVerificationUI = \"show_verification_ui\"\n)\n\nvar _ ContinueWith = new(ContinueWithVerificationUI)\n\n// Indicates, that the UI flow could be continued by showing a verification ui\n//\n// swagger:model continueWithVerificationUi\ntype ContinueWithVerificationUI struct {\n\t// Action will always be `show_verification_ui`\n\t//\n\t// required: true\n\tAction ContinueWithActionShowVerificationUI `json:\"action\"`\n\t// Flow contains the ID of the verification flow\n\t//\n\t// required: true\n\tFlow ContinueWithVerificationUIFlow `json:\"flow\"`\n}\n\nfunc (c ContinueWithVerificationUI) GetAction() string {\n\treturn string(c.Action)\n}\n\n// swagger:model continueWithVerificationUiFlow\ntype ContinueWithVerificationUIFlow struct {\n\t// The ID of the verification flow\n\t//\n\t// required: true\n\tID uuid.UUID `json:\"id\"`\n\n\t// The address that should be verified in this flow\n\t//\n\t// required: true\n\tVerifiableAddress string `json:\"verifiable_address\"`\n\n\t// The URL of the verification flow\n\t//\n\t// If this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\n\t//\n\t// required: false\n\tURL string `json:\"url,omitempty\"`\n}\n\nfunc NewContinueWithVerificationUI(id uuid.UUID, address, url string) *ContinueWithVerificationUI {\n\treturn &ContinueWithVerificationUI{\n\t\tAction: ContinueWithActionShowVerificationUIString,\n\t\tFlow: ContinueWithVerificationUIFlow{\n\t\t\tID:                id,\n\t\t\tVerifiableAddress: address,\n\t\t\tURL:               url,\n\t\t},\n\t}\n}\n\nfunc (c ContinueWithVerificationUI) AppendTo(src *url.URL) *url.URL {\n\tvalues := src.Query()\n\tvalues.Set(\"flow\", c.Flow.ID.String())\n\treturn urlx.CopyWithQuery(src, values)\n}\n\ntype FlowWithContinueWith interface {\n\tFlow\n\tAddContinueWith(ContinueWith)\n\tContinueWith() []ContinueWith\n}\n\n// swagger:enum ContinueWithActionShowSettingsUI\ntype ContinueWithActionShowSettingsUI string\n\n// #nosec G101 -- only a key constant\nconst (\n\tContinueWithActionShowSettingsUIString ContinueWithActionShowSettingsUI = \"show_settings_ui\"\n)\n\nvar _ ContinueWith = new(ContinueWithSettingsUI)\n\n// Indicates, that the UI flow could be continued by showing a settings ui\n//\n// swagger:model continueWithSettingsUi\ntype ContinueWithSettingsUI struct {\n\t// Action will always be `show_settings_ui`\n\t//\n\t// required: true\n\tAction ContinueWithActionShowSettingsUI `json:\"action\"`\n\n\t// Flow contains the ID of the settings flow\n\t//\n\t// required: true\n\tFlow ContinueWithSettingsUIFlow `json:\"flow\"`\n}\n\nfunc (c ContinueWithSettingsUI) GetAction() string {\n\treturn string(c.Action)\n}\n\n// swagger:model continueWithSettingsUiFlow\ntype ContinueWithSettingsUIFlow struct {\n\t// The ID of the settings flow\n\t//\n\t// required: true\n\tID uuid.UUID `json:\"id\"`\n\n\t// The URL of the settings flow\n\t//\n\t// If this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\n\t//\n\t// required: false\n\tURL string `json:\"url,omitempty\"`\n}\n\nfunc NewContinueWithSettingsUI(f Flow, redirectTo string) *ContinueWithSettingsUI {\n\treturn &ContinueWithSettingsUI{\n\t\tAction: ContinueWithActionShowSettingsUIString,\n\t\tFlow: ContinueWithSettingsUIFlow{\n\t\t\tID:  f.GetID(),\n\t\t\tURL: redirectTo,\n\t\t},\n\t}\n}\n\n// swagger:enum ContinueWithActionShowRecoveryUI\ntype ContinueWithActionShowRecoveryUI string\n\n// #nosec G101 -- only a key constant\nconst (\n\tContinueWithActionShowRecoveryUIString ContinueWithActionShowRecoveryUI = \"show_recovery_ui\"\n)\n\n// Indicates, that the UI flow could be continued by showing a recovery ui\n//\n// swagger:model continueWithRecoveryUi\ntype ContinueWithRecoveryUI struct {\n\t// Action will always be `show_recovery_ui`\n\t//\n\t// required: true\n\tAction ContinueWithActionShowRecoveryUI `json:\"action\"`\n\t// Flow contains the ID of the recovery flow\n\t//\n\t// required: true\n\tFlow ContinueWithRecoveryUIFlow `json:\"flow\"`\n}\n\n// swagger:model continueWithRecoveryUiFlow\ntype ContinueWithRecoveryUIFlow struct {\n\t// The ID of the recovery flow\n\t//\n\t// required: true\n\tID uuid.UUID `json:\"id\"`\n\n\t// The URL of the recovery flow\n\t//\n\t// If this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\n\t//\n\t// required: false\n\tURL string `json:\"url,omitempty\"`\n}\n\nfunc NewContinueWithRecoveryUI(f Flow) *ContinueWithRecoveryUI {\n\treturn &ContinueWithRecoveryUI{\n\t\tAction: ContinueWithActionShowRecoveryUIString,\n\t\tFlow: ContinueWithRecoveryUIFlow{\n\t\t\tID: f.GetID(),\n\t\t},\n\t}\n}\n\nfunc (c ContinueWithRecoveryUI) GetAction() string {\n\treturn string(c.Action)\n}\n\n// swagger:enum ContinueWithActionRedirectBrowserTo\ntype ContinueWithActionRedirectBrowserTo string\n\n// #nosec G101 -- only a key constant\nconst (\n\tContinueWithActionRedirectBrowserToString ContinueWithActionRedirectBrowserTo = \"redirect_browser_to\"\n)\n\n// Indicates, that the UI flow could be continued by showing a recovery ui\n//\n// swagger:model continueWithRedirectBrowserTo\ntype ContinueWithRedirectBrowserTo struct {\n\t// Action will always be `redirect_browser_to`\n\t//\n\t// required: true\n\tAction ContinueWithActionRedirectBrowserTo `json:\"action\"`\n\n\t// The URL to redirect the browser to\n\t//\n\t// required: true\n\tRedirectTo string `json:\"redirect_browser_to\"`\n}\n\nfunc NewContinueWithRedirectBrowserTo(redirectTo string) *ContinueWithRedirectBrowserTo {\n\treturn &ContinueWithRedirectBrowserTo{\n\t\tAction:     ContinueWithActionRedirectBrowserToString,\n\t\tRedirectTo: redirectTo,\n\t}\n}\n\nfunc (c ContinueWithRedirectBrowserTo) GetAction() string {\n\treturn string(c.Action)\n}\n\nfunc ErrorWithContinueWith(err *herodot.DefaultError, continueWith ...ContinueWith) *herodot.DefaultError {\n\tif err.DetailsField == nil {\n\t\terr.DetailsField = map[string]interface{}{}\n\t}\n\terr.DetailsField[\"continue_with\"] = continueWith\n\n\treturn err\n}\n"
  },
  {
    "path": "selfservice/flow/duplicate_credentials.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nconst internalContextDuplicateCredentialsPath = \"registration_duplicate_credentials\"\n\ntype DuplicateCredentialsData struct {\n\tCredentialsType     identity.CredentialsType\n\tCredentialsConfig   sqlxx.JSONRawMessage\n\tDuplicateIdentifier string\n}\n\n// SetDuplicateCredentials sets the duplicate credentials data in the flow's internal context.\nfunc SetDuplicateCredentials(flow InternalContexter, creds DuplicateCredentialsData) error {\n\tif flow.GetInternalContext() == nil {\n\t\tflow.EnsureInternalContext()\n\t}\n\tbytes, err := sjson.SetBytes(\n\t\tflow.GetInternalContext(),\n\t\tinternalContextDuplicateCredentialsPath,\n\t\tcreds,\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tflow.SetInternalContext(bytes)\n\n\treturn nil\n}\n\n// DuplicateCredentials returns the duplicate credentials data from the flow's internal context.\nfunc DuplicateCredentials(flow InternalContexter) (*DuplicateCredentialsData, error) {\n\tif flow.GetInternalContext() == nil {\n\t\tflow.EnsureInternalContext()\n\t}\n\traw := gjson.GetBytes(flow.GetInternalContext(), internalContextDuplicateCredentialsPath)\n\tif !raw.IsObject() {\n\t\treturn nil, nil\n\t}\n\tvar creds DuplicateCredentialsData\n\terr := json.Unmarshal([]byte(raw.Raw), &creds)\n\n\treturn &creds, err\n}\n"
  },
  {
    "path": "selfservice/flow/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/swagger\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/text\"\n)\n\nvar (\n\tErrStrategyNotResponsible   = errors.New(\"strategy is not responsible for this request\")\n\tErrCompletedByStrategy      = errors.New(\"flow response completed by strategy\")\n\tErrStrategyAsksToReturnToUI = errors.New(\"flow strategy is redirecting to the ui\")\n)\n\n// Is sent when a flow is replaced by a different flow of the same class\n//\n// swagger:model errorFlowReplaced\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype errorFlowReplaced struct {\n\tError swagger.GenericError `json:\"error\"`\n\t// The flow ID that should be used for the new flow as it contains the correct messages.\n\tFlowID uuid.UUID `json:\"use_flow_id\"`\n}\n\n// ReplacedError is sent when a flow is replaced by a different flow of the same class\ntype ReplacedError struct {\n\t*herodot.DefaultError `json:\"error\"`\n\n\t// The flow ID that should be used for the new flow as it contains the correct messages.\n\tFlowID uuid.UUID `json:\"use_flow_id\"`\n\n\tflow Flow\n\n\t// TODO: This error could be enhanced by providing a \"flow class\" (e.g. \"Recovery\", \"Settings\", \"Verification\", \"Login\", etc.)\n}\n\nfunc (e *ReplacedError) WithFlow(flow Flow) *ReplacedError {\n\te.FlowID = flow.GetID()\n\te.flow = flow\n\treturn e\n}\n\nfunc (e *ReplacedError) GetFlow() Flow {\n\treturn e.flow\n}\n\nfunc (e *ReplacedError) EnhanceJSONError() interface{} {\n\treturn e\n}\n\nfunc NewFlowReplacedError(message *text.Message) *ReplacedError {\n\treturn &ReplacedError{\n\t\tDefaultError: nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowReplaced).\n\t\t\tWithError(\"self-service flow replaced\").\n\t\t\tWithReason(message.Text),\n\t}\n}\n\n// Is sent when a flow is expired\n//\n// swagger:model selfServiceFlowExpiredError\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype selfServiceFlowExpiredError struct {\n\tError swagger.GenericError `json:\"error\"`\n\n\t// When the flow has expired\n\tExpiredAt time.Time `json:\"expired_at\"`\n\n\t// Please use the \"expired_at\" field instead to have a more accurate result.\n\t//\n\t// Deprecated: true\n\tSince time.Duration `json:\"since\"`\n\n\t// The flow ID that should be used for the new flow as it contains the correct messages.\n\tFlowID uuid.UUID `json:\"use_flow_id\"`\n}\n\n// ExpiredError is sent when a flow is expired\ntype ExpiredError struct {\n\t*herodot.DefaultError `json:\"error\"`\n\n\t// When the flow has expired\n\tExpiredAt time.Time `json:\"expired_at\"`\n\n\t// DEPRECATED: Please use the \"expired_at\" field instead to have a more accurate result.\n\tSince time.Duration `json:\"since\"`\n\n\t// The flow ID that should be used for the new flow as it contains the correct messages.\n\tFlowID uuid.UUID `json:\"use_flow_id\"`\n\n\tflow Flow\n}\n\nfunc (e *ExpiredError) Unwrap() error {\n\treturn e.DefaultError\n}\n\nfunc (e *ExpiredError) WithContinueWith(continueWith ...ContinueWith) *ExpiredError {\n\te.DefaultError = ErrorWithContinueWith(e.DefaultError, continueWith...)\n\treturn e\n}\n\nfunc (e *ExpiredError) WithFlow(flow Flow) *ExpiredError {\n\te.FlowID = flow.GetID()\n\te.flow = flow\n\treturn e\n}\n\nfunc (e *ExpiredError) GetFlow() Flow {\n\treturn e.flow\n}\n\nfunc (e *ExpiredError) EnhanceJSONError() interface{} {\n\treturn e\n}\n\nfunc NewFlowExpiredError(at time.Time) *ExpiredError {\n\tago := time.Since(at)\n\treturn &ExpiredError{\n\t\tExpiredAt: at.UTC(),\n\t\tSince:     ago,\n\t\tDefaultError: nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).\n\t\t\tWithError(\"self-service flow expired\").\n\t\t\tWithReasonf(\"The self-service flow expired %.2f minutes ago, initialize a new one.\", ago.Minutes()),\n\t}\n}\n\n// Is sent when a flow requires a browser to change its location.\n//\n// swagger:model errorBrowserLocationChangeRequired\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype errorBrowserLocationChangeRequired struct {\n\tError swagger.ErrorGeneric `json:\"error\"`\n\n\t// Points to where to redirect the user to next.\n\tRedirectBrowserTo string `json:\"redirect_browser_to\"`\n}\n\n// BrowserLocationChangeRequiredError is sent when a flow requires a browser to change its location.\ntype BrowserLocationChangeRequiredError struct {\n\t*herodot.DefaultError `json:\"error\"`\n\n\t// Points to where to redirect the user to next.\n\tRedirectBrowserTo string `json:\"redirect_browser_to\"`\n}\n\nfunc (e *BrowserLocationChangeRequiredError) EnhanceJSONError() interface{} {\n\treturn e\n}\n\nfunc NewBrowserLocationChangeRequiredError(redirectTo string) *BrowserLocationChangeRequiredError {\n\treturn &BrowserLocationChangeRequiredError{\n\t\tRedirectBrowserTo: redirectTo,\n\t\tDefaultError: &herodot.DefaultError{\n\t\t\tIDField:     text.ErrIDSelfServiceBrowserLocationChangeRequiredError,\n\t\t\tCodeField:   http.StatusUnprocessableEntity,\n\t\t\tStatusField: http.StatusText(http.StatusUnprocessableEntity),\n\t\t\tReasonField: fmt.Sprintf(\"In order to complete this flow please redirect the browser to: %s\", redirectTo),\n\t\t\tDebugField:  \"\",\n\t\t\tErrorField:  \"browser location change required\",\n\t\t},\n\t}\n}\n\nfunc HandleHookError(_ http.ResponseWriter, r *http.Request, f Flow, traits identity.Traits, group node.UiNodeGroup, flowError error, logger logrusx.Provider, csrf nosurfx.CSRFTokenGeneratorProvider) error {\n\tif f != nil {\n\t\tif traits != nil {\n\t\t\tcont, err := container.NewFromStruct(\"\", group, traits, \"traits\")\n\t\t\tif err != nil {\n\t\t\t\tlogger.Logger().WithError(err).Error(\"could not update flow UI\")\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tfor _, n := range cont.Nodes {\n\t\t\t\t// we only set the value and not the whole field because we want to keep types from the initial form generation\n\t\t\t\tf.GetUI().Nodes.SetValueAttribute(n.ID(), n.Attributes.GetValue())\n\t\t\t}\n\t\t}\n\n\t\tif f.GetType() == TypeBrowser {\n\t\t\tf.GetUI().SetCSRF(csrf.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn flowError\n}\n\nfunc GetFlowExpiredRedirectURL(ctx context.Context, config *config.Config, route, returnTo string) *url.URL {\n\tredirectURL := urlx.AppendPaths(config.SelfPublicURL(ctx), route)\n\tif returnTo != \"\" {\n\t\tredirectURL = urlx.CopyWithQuery(redirectURL, url.Values{\"return_to\": {returnTo}})\n\t}\n\n\treturn redirectURL\n}\n"
  },
  {
    "path": "selfservice/flow/error_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype testCSRFTokenGenerator struct{}\n\nfunc (t *testCSRFTokenGenerator) GenerateCSRFToken(_ *http.Request) string {\n\treturn \"csrf_token_value\"\n}\n\n// testFlow is a minimalistic flow implementation to satisfy interface and is used only in tests.\ntype testFlow struct {\n\t// ID represents the flow's unique ID.\n\t//\n\t// required: true\n\tID uuid.UUID `json:\"id\" faker:\"-\" db:\"id\" rw:\"r\"`\n\n\t// Type represents the flow's type which can be either \"api\" or \"browser\", depending on the flow interaction.\n\t//\n\t// required: true\n\tType Type `json:\"type\" db:\"type\" faker:\"flow_type\"`\n\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used\n\t// to forward information contained in the URL's path or query for example.\n\t//\n\t// required: true\n\tRequestURL string `json:\"request_url\" db:\"request_url\"`\n\n\t// UI contains data which must be shown in the user interface.\n\t//\n\t// required: true\n\tUI *container.Container `json:\"ui\" db:\"ui\"`\n\n\t// Flow State\n\t//\n\t// The state represents the state of the verification flow.\n\t//\n\t// - choose_method: ask the user to choose a method (e.g. recover account via email)\n\t// - sent_email: the email has been sent to the user\n\t// - passed_challenge: the request was successful and the recovery challenge was passed.\n\t//\n\t// required: true\n\tState State `json:\"state\" db:\"state\"`\n}\n\nfunc (t *testFlow) GetID() uuid.UUID {\n\treturn t.ID\n}\n\nfunc (t *testFlow) GetType() Type {\n\treturn t.Type\n}\n\nfunc (t *testFlow) GetRequestURL() string {\n\treturn t.RequestURL\n}\n\nfunc (t *testFlow) AppendTo(url *url.URL) *url.URL {\n\treturn AppendFlowTo(url, t.ID)\n}\n\nfunc (t *testFlow) GetUI() *container.Container {\n\treturn t.UI\n}\n\nfunc (t *testFlow) GetState() State {\n\treturn t.State\n}\n\nfunc (t *testFlow) GetFlowName() FlowName {\n\treturn FlowName(\"test\")\n}\n\nfunc (t *testFlow) SetState(state State) {\n\tt.State = state\n}\n\nfunc (t *testFlow) GetTransientPayload() json.RawMessage {\n\treturn nil\n}\n\nfunc newTestFlow(r *http.Request, flowType Type) Flow {\n\tid := x.NewUUID()\n\trequestURL := x.RequestURL(r).String()\n\tui := &container.Container{\n\t\tMethod: \"POST\",\n\t\tAction: \"/test\",\n\t}\n\n\tui.Nodes.Append(node.NewInputField(\"traits.username\", nil, node.PasswordGroup, node.InputAttributeTypeText, node.WithRequiredInputAttribute))\n\tui.Nodes.Append(node.NewInputField(\"traits.password\", nil, node.PasswordGroup, node.InputAttributeTypePassword, node.WithRequiredInputAttribute))\n\n\treturn &testFlow{\n\t\tID:         id,\n\t\tUI:         ui,\n\t\tRequestURL: requestURL,\n\t\tType:       flowType,\n\t}\n}\n\nfunc prepareTraits(username, password string) identity.Traits {\n\tpayload := struct {\n\t\tUsername string `json:\"username\"`\n\t\tPassword string `json:\"password\"`\n\t}{username, password}\n\n\tdata, _ := json.Marshal(payload)\n\treturn data\n}\n\nfunc TestHandleHookError(t *testing.T) {\n\tr := &http.Request{URL: &url.URL{RawQuery: \"\"}}\n\tlogger := logrusx.New(\"kratos\", \"test\", logrusx.ForceLevel(logrus.FatalLevel))\n\tl := &x.BasicRegistry{L: logger, C: httpx.NewResilientClient(), T: otelx.NewNoop()}\n\tcsrf := testCSRFTokenGenerator{}\n\tf := newTestFlow(r, TypeBrowser)\n\ttr := prepareTraits(\"foo\", \"bar\")\n\n\tt.Run(\"case=fill_in_traits\", func(t *testing.T) {\n\t\tve := schema.NewValidationListError([]*schema.ValidationError{schema.NewHookValidationError(\"traits.username\", \"invalid username\", text.Messages{})})\n\n\t\terr := HandleHookError(nil, r, f, tr, node.PasswordGroup, ve, l, &csrf)\n\t\tassert.ErrorIs(t, err, ve)\n\t\tif assert.NotEmpty(t, f.GetUI()) {\n\t\t\tui := f.GetUI()\n\t\t\tassert.Len(t, ui.Nodes, 3)\n\t\t\tassert.ElementsMatch(t, ui.Nodes,\n\t\t\t\tnode.Nodes{\n\t\t\t\t\t&node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"traits.username\", Type: node.InputAttributeTypeText, FieldValue: \"foo\", Required: true}, Meta: &node.Meta{}},\n\t\t\t\t\t&node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"traits.password\", Type: node.InputAttributeTypePassword, FieldValue: \"bar\", Required: true}, Meta: &node.Meta{}},\n\t\t\t\t\t&node.Node{Type: node.Input, Group: node.DefaultGroup, Attributes: &node.InputAttributes{Name: \"csrf_token\", Type: node.InputAttributeTypeHidden, FieldValue: \"csrf_token_value\", Required: true}},\n\t\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=unmarshal_fail\", func(t *testing.T) {\n\t\tve := schema.NewValidationListError([]*schema.ValidationError{schema.NewHookValidationError(\"traits.username\", \"invalid username\", text.Messages{})})\n\n\t\terr := HandleHookError(nil, r, f, []byte(\"garbage\"), node.PasswordGroup, ve, l, &csrf)\n\t\tvar jsonErr *json.SyntaxError\n\t\tassert.ErrorAs(t, err, &jsonErr)\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/flow.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/x/redir\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc AppendFlowTo(src *url.URL, id uuid.UUID) *url.URL {\n\treturn urlx.CopyWithQuery(src, url.Values{\"flow\": {id.String()}})\n}\n\nfunc GetFlowID(r *http.Request) (uuid.UUID, error) {\n\trid := x.ParseUUID(r.URL.Query().Get(\"flow\"))\n\tif rid == uuid.Nil {\n\t\treturn rid, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"The flow query parameter is missing or malformed.\"))\n\t}\n\treturn rid, nil\n}\n\ntype Flow interface {\n\tGetID() uuid.UUID\n\tGetType() Type\n\tGetRequestURL() string\n\tAppendTo(*url.URL) *url.URL\n\tGetUI() *container.Container\n\tGetState() State\n\tSetState(State)\n\tGetFlowName() FlowName\n\tGetTransientPayload() json.RawMessage\n}\n\ntype FlowWithRedirect interface {\n\tSecureRedirectToOpts(ctx context.Context, cfg config.Provider) (opts []redir.SecureRedirectOption)\n}\n\ntype InternalContexter interface {\n\tEnsureInternalContext()\n\tGetInternalContext() sqlxx.JSONRawMessage\n\tSetInternalContext(sqlxx.JSONRawMessage)\n}\n\ntype OAuth2ChallengeProvider interface {\n\tGetOAuth2LoginChallenge() sqlxx.NullString\n}\n"
  },
  {
    "path": "selfservice/flow/flow_identity_schema.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"net/url\"\n\n\t\"github.com/ory/kratos/driver/config\"\n)\n\n// swagger:type string\ntype IdentitySchema string\n\n// Scan implements the Scanner interface.\nfunc (is *IdentitySchema) Scan(value any) error {\n\tvar v sql.NullString\n\tif err := (&v).Scan(value); err != nil {\n\t\treturn err\n\t}\n\t*is = IdentitySchema(v.String)\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (is *IdentitySchema) Value() (driver.Value, error) {\n\tif is == nil || len(*is) == 0 {\n\t\treturn sql.NullString{}.Value()\n\t}\n\treturn sql.NullString{Valid: true, String: string(*is)}.Value()\n}\n\n// URL returns the URL of the identity schema, or the default identity traits\n// schema URL if the schema is empty.\nfunc (is *IdentitySchema) URL(ctx context.Context, config *config.Config) (*url.URL, error) {\n\tif is == nil || len(*is) == 0 {\n\t\treturn config.DefaultIdentityTraitsSchemaURL(ctx)\n\t}\n\tschemas, err := config.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tschema, err := schemas.FindSchemaByID(string(*is))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn config.ParseURI(schema.URL)\n}\n\n// ID returns the ID of the identity schema, or the default identity schema ID.\nfunc (is *IdentitySchema) ID(ctx context.Context, config *config.Config) string {\n\tif is == nil || len(*is) == 0 {\n\t\treturn config.DefaultIdentityTraitsSchemaID(ctx)\n\t}\n\treturn string(*is)\n}\n"
  },
  {
    "path": "selfservice/flow/flow_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestGetFlowID(t *testing.T) {\n\t_, err := GetFlowID(&http.Request{URL: &url.URL{RawQuery: \"\"}})\n\trequire.Error(t, err)\n\tassert.Contains(t,\n\t\terrors.Cause(err).(*herodot.DefaultError).ReasonField,\n\t\t\"flow query parameter is missing or malformed\")\n\n\t_, err = GetFlowID(&http.Request{URL: &url.URL{RawQuery: \"flow=not-uuid\"}})\n\trequire.Error(t, err)\n\tassert.Contains(t,\n\t\terrors.Cause(err).(*herodot.DefaultError).ReasonField,\n\t\t\"flow query parameter is missing or malformed\")\n\n\texpected := x.NewUUID()\n\tactual, err := GetFlowID(&http.Request{URL: &url.URL{RawQuery: \"flow=\" + expected.String()}})\n\trequire.NoError(t, err)\n\tassert.Equal(t, expected.String(), actual.String())\n}\n"
  },
  {
    "path": "selfservice/flow/internal_context.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport \"github.com/ory/kratos/identity\"\n\nfunc PrefixInternalContextKey(t identity.CredentialsType, suffix string) string {\n\treturn string(t) + \"_\" + suffix\n}\n"
  },
  {
    "path": "selfservice/flow/login/aal.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n)\n\nfunc CheckAAL(f *Flow, expected identity.AuthenticatorAssuranceLevel) error {\n\tif f.RequestedAAL != expected {\n\t\treturn errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/flow/login/aal_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n)\n\nfunc TestCheckAAL(t *testing.T) {\n\tf := &login.Flow{RequestedAAL: identity.AuthenticatorAssuranceLevel1}\n\tassert.NoError(t, login.CheckAAL(f, identity.AuthenticatorAssuranceLevel1))\n\tassert.ErrorIs(t, login.CheckAAL(f, identity.AuthenticatorAssuranceLevel2), flow.ErrStrategyNotResponsible)\n}\n"
  },
  {
    "path": "selfservice/flow/login/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/x\"\n)\n\nvar (\n\tErrHookAbortFlow      = errors.New(\"aborted login hook execution\")\n\tErrAlreadyLoggedIn    = herodot.ErrBadRequest.WithID(text.ErrIDAlreadyLoggedIn).WithError(\"you are already logged in\").WithReason(\"A valid session was detected and thus login is not possible. Did you forget to set `?refresh=true`?\")\n\tErrAddressNotVerified = herodot.ErrBadRequest.WithID(text.ErrIDAddressNotVerified).WithError(\"your email or phone address is not yet verified\").WithReason(\"Your account's email or phone address are not verified yet. Please check your email or phone inbox or re-request verification.\")\n\n\t// ErrSessionHasAALAlready is returned when one attempts to upgrade the AAL of an active session which already has that AAL.\n\tErrSessionHasAALAlready = herodot.ErrUnauthorized.WithID(text.ErrIDSessionHasAALAlready).WithError(\"session has the requested authenticator assurance level already\").WithReason(\"The session has the requested AAL already.\")\n\n\t// ErrSessionRequiredForHigherAAL is returned when someone requests AAL2 or AAL3 even though no active session exists yet.\n\tErrSessionRequiredForHigherAAL = herodot.ErrUnauthorized.WithID(text.ErrIDSessionRequiredForHigherAAL).WithError(\"aal2 and aal3 can only be requested if a session exists already\").WithReason(\"You can not requested a higher AAL (AAL2/AAL3) without an active session.\")\n)\n\ntype (\n\terrorHandlerDependencies interface {\n\t\terrorx.ManagementProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\t\tconfig.Provider\n\t\tsessiontokenexchange.PersistenceProvider\n\t\thydra.Provider\n\n\t\tFlowPersistenceProvider\n\t\tHandlerProvider\n\t}\n\n\tErrorHandlerProvider interface{ LoginFlowErrorHandler() *ErrorHandler }\n\n\tErrorHandler struct {\n\t\td errorHandlerDependencies\n\t}\n)\n\nfunc NewFlowErrorHandler(d errorHandlerDependencies) *ErrorHandler {\n\treturn &ErrorHandler{d: d}\n}\n\nfunc (s *ErrorHandler) PrepareReplacementForExpiredFlow(w http.ResponseWriter, r *http.Request, f *Flow, err error) (*flow.ExpiredError, error) {\n\terrExpired := new(flow.ExpiredError)\n\tif !errors.As(err, &errExpired) {\n\t\treturn nil, nil\n\t}\n\t// create new flow because the old one is not valid\n\tnewFlow, err := s.d.LoginHandler().FromOldFlow(w, r, *f)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnewFlow.UI.Messages.Add(text.NewErrorValidationLoginFlowExpired(errExpired.ExpiredAt))\n\tif err := s.d.LoginFlowPersister().UpdateLoginFlow(r.Context(), newFlow); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn errExpired.WithFlow(newFlow), nil\n}\n\nfunc (s *ErrorHandler) WriteFlowError(w http.ResponseWriter, r *http.Request, f *Flow, ct identity.CredentialsType, group node.UiNodeGroup, err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.flow.login.ErrorHandler.WriteFlowError\",\n\t\ttrace.WithAttributes(attribute.String(\"error\", err.Error())))\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\tlogger := s.d.Logger().\n\t\tWithError(err).\n\t\tWithRequest(r).\n\t\tWithField(\"login_flow\", f.ToLoggerField())\n\n\tlogger.Info(\"Encountered self-service login error.\")\n\n\tif f == nil {\n\t\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), uuid.Nil, \"\", \"\", \"\", false, err))\n\t\ts.forward(w, r, nil, err)\n\t\treturn\n\t}\n\n\tspan.SetAttributes(attribute.String(\"flow_id\", f.ID.String()))\n\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), f.ID, string(f.Type), ct.String(), string(f.RequestedAAL), f.Refresh, err))\n\n\tif expired, inner := s.PrepareReplacementForExpiredFlow(w, r, f, err); inner != nil {\n\t\ts.WriteFlowError(w, r, f, ct, group, inner)\n\t\treturn\n\t} else if expired != nil {\n\t\tif f.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, expired)\n\t\t} else {\n\t\t\thttp.Redirect(w, r, expired.GetFlow().AppendTo(s.d.Config().SelfServiceFlowLoginUI(r.Context())).String(), http.StatusSeeOther)\n\t\t}\n\t\treturn\n\t}\n\n\tf.UI.ResetMessages()\n\tif err := f.UI.ParseError(group, err); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tif err := sortNodes(r.Context(), f.UI.Nodes); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tif err := s.d.LoginFlowPersister().UpdateLoginFlow(r.Context(), f); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tif f.Type == flow.TypeBrowser && !x.IsJSONRequest(r) {\n\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowLoginUI(r.Context())).String(), http.StatusSeeOther)\n\t\treturn\n\t}\n\n\t_, hasCode, _ := s.d.SessionTokenExchangePersister().CodeForFlow(r.Context(), f.ID)\n\tif f.Type == flow.TypeAPI && hasCode && group == node.OpenIDConnectGroup {\n\t\thttp.Redirect(w, r, f.ReturnTo, http.StatusSeeOther)\n\t\treturn\n\t}\n\n\tupdatedFlow, innerErr := s.d.LoginFlowPersister().GetLoginFlow(r.Context(), f.ID)\n\tif innerErr != nil {\n\t\ts.forward(w, r, updatedFlow, innerErr)\n\t\treturn\n\t}\n\n\t// If the flow has an OAuth2LoginChallenge, we try to fetch the login request from Hydra because we return the flow object as JSON.\n\tif updatedFlow.OAuth2LoginChallenge != \"\" {\n\t\thlr, err := s.d.Hydra().GetLoginRequest(ctx, string(updatedFlow.OAuth2LoginChallenge))\n\t\tif err != nil {\n\t\t\t// We don't redirect back to the third party on errors because Hydra doesn't\n\t\t\t// give us the 3rd party return_uri when it redirects to the login UI.\n\t\t\ts.forward(w, r, updatedFlow, err)\n\t\t\treturn\n\t\t}\n\t\tupdatedFlow.HydraLoginRequest = hlr\n\t}\n\n\ts.d.Writer().WriteCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), updatedFlow)\n}\n\nfunc (s *ErrorHandler) forward(w http.ResponseWriter, r *http.Request, rr *Flow, err error) {\n\tif rr == nil {\n\t\tif x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tif rr.Type == flow.TypeAPI {\n\t\ts.d.Writer().WriteErrorCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), err)\n\t} else {\n\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/login/error_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/gobuffalo/httptest\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n)\n\ntype opts struct {\n\tloginChallenge string\n}\n\nfunc withLoginChallenge(challenge string) func(*opts) {\n\treturn func(o *opts) {\n\t\to.loginChallenge = challenge\n\t}\n}\n\nfunc TestHandleError(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tpublic, _ := testhelpers.NewKratosServer(t, reg)\n\treg.SetHydra(hydra.NewFake())\n\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/password.schema.json\")\n\n\trouter := http.NewServeMux()\n\tts := httptest.NewServer(router)\n\tt.Cleanup(ts.Close)\n\n\ttesthelpers.NewLoginUIFlowEchoServer(t, reg)\n\ttesthelpers.NewErrorTestServer(t, reg)\n\n\th := reg.LoginFlowErrorHandler()\n\tsdk := testhelpers.NewSDKClient(public)\n\n\tvar loginFlow *login.Flow\n\tvar flowError error\n\tvar group node.UiNodeGroup\n\trouter.HandleFunc(\"GET /error\", func(w http.ResponseWriter, r *http.Request) {\n\t\th.WriteFlowError(w, r, loginFlow, \"\", group, flowError)\n\t})\n\n\treset := func() {\n\t\tloginFlow = nil\n\t\tflowError = nil\n\t\tgroup = \"\"\n\t}\n\n\tnewFlow := func(t *testing.T, ttl time.Duration, ft flow.Type, options ...func(*opts)) *login.Flow {\n\t\treq := &http.Request{URL: urlx.ParseOrPanic(\"/\")}\n\t\tf, err := login.NewFlow(conf, ttl, \"csrf_token\", req, ft)\n\t\trequire.NoError(t, err)\n\n\t\toptsD := &opts{}\n\t\tfor _, o := range options {\n\t\t\to(optsD)\n\t\t}\n\t\tif optsD.loginChallenge != \"\" {\n\t\t\tf.OAuth2LoginChallenge = sqlxx.NullString(optsD.loginChallenge)\n\t\t}\n\n\t\tfor _, s := range reg.LoginStrategies(context.Background()) {\n\t\t\tswitch s := s.(type) {\n\t\t\tcase login.UnifiedFormHydrator:\n\t\t\t\trequire.NoError(t, s.PopulateLoginMethod(req, identity.AuthenticatorAssuranceLevel1, f))\n\t\t\tcase login.AAL1FormHydrator:\n\t\t\t\trequire.NoError(t, s.PopulateLoginMethodFirstFactor(req, f))\n\t\t\t}\n\t\t}\n\n\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), f))\n\t\treturn f\n\t}\n\n\texpectErrorUI := func(t *testing.T) (map[string]interface{}, *http.Response) {\n\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\trequire.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tsse, _, err := sdk.FrontendAPI.GetFlowError(context.Background()).Id(res.Request.URL.Query().Get(\"id\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\treturn sse.Error, nil\n\t}\n\n\tanHourAgo := time.Now().Add(-time.Hour)\n\n\tt.Run(\"case=error with nil flow defaults to error ui redirect\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tgroup = node.PasswordGroup\n\n\t\tsse, _ := expectErrorUI(t)\n\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t})\n\n\tt.Run(\"case=relative error\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\t\treg.Config().MustSet(ctx, config.ViperKeySelfServiceErrorUI, \"/login-ts\")\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tgroup = node.PasswordGroup\n\t\tassert.Regexp(\n\t\t\tt,\n\t\t\t\"^/login-ts.*$\",\n\t\t\ttesthelpers.GetSelfServiceRedirectLocation(t, ts.URL+\"/error\"),\n\t\t)\n\t})\n\n\tt.Run(\"case=error with nil flow detects application/json\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tgroup = node.PasswordGroup\n\n\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tassert.Contains(t, res.Header.Get(\"Content-Type\"), \"application/json\")\n\t\tassert.NotContains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, string(body), \"system error\")\n\t})\n\n\tfor _, tc := range []struct {\n\t\tn string\n\t\tt flow.Type\n\t}{\n\t\t{\"api\", flow.TypeAPI},\n\t\t{\"spa\", flow.TypeBrowser},\n\t} {\n\t\tt.Run(\"flow=\"+tc.n, func(t *testing.T) {\n\t\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tloginFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\t\tgroup = node.PasswordGroup\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusGone, res.StatusCode, \"%+v\\n\\t%s\", res.Request, body)\n\n\t\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.GetBytes(body, \"use_flow_id\").String())\n\t\t\t\tassertx.EqualAsJSONExcept(t, flow.NewFlowExpiredError(anHourAgo), json.RawMessage(body), []string{\"since\", \"redirect_browser_to\", \"use_flow_id\"})\n\t\t\t})\n\n\t\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tloginFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\t\tgroup = node.PasswordGroup\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(gjson.GetBytes(body, \"ui.messages.0.id\").Int()), \"%s\", body)\n\t\t\t\tassert.Equal(t, loginFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tloginFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\t\tgroup = node.PasswordGroup\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.JSONEq(t, x.MustEncodeJSON(t, flowError), gjson.GetBytes(body, \"error\").Raw)\n\t\t\t})\n\n\t\t\tt.Run(\"case=refuses to parse oauth2 login challenge when Hydra is not configured\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tloginFlow = newFlow(t, time.Minute, flow.TypeBrowser, withLoginChallenge(hydra.FakeInvalidLoginChallenge))\n\t\t\t\tgroup = node.PasswordGroup\n\t\t\t\tflowError = herodot.ErrBadRequest.WithReason(\"missing field 'password' fake error\")\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\trequire.Equal(t, \"Unable to get OAuth 2.0 Login Challenge.\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=fetches hydra login request\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, \"https://fake-hydra\")\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, nil)\n\t\t\t\t})\n\n\t\t\t\tloginFlow = newFlow(t, time.Minute, flow.TypeBrowser, withLoginChallenge(hydra.FakeValidLoginChallenge))\n\t\t\t\tgroup = node.PasswordGroup\n\t\t\t\tflowError = herodot.ErrBadRequest.WithReason(\"missing field 'password' fake error\")\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\trequire.NotEmpty(t, gjson.GetBytes(body, \"oauth2_login_request\").Value(), \"%s\", body)\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\texpectLoginUI := func(t *testing.T) (*login.Flow, *http.Response) {\n\t\t\tres, err := http.DefaultClient.Get(ts.URL + \"/error\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowLoginUI(ctx).String()+\"?flow=\")\n\n\t\t\tlf, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(res.Request.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t\treturn lf, res\n\t\t}\n\n\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tloginFlow = &login.Flow{Type: flow.TypeBrowser}\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\tgroup = node.PasswordGroup\n\n\t\t\tlf, _ := expectLoginUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1)\n\t\t\tassert.Equal(t, int(text.ErrorValidationLoginFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t})\n\n\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tloginFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\tgroup = node.PasswordGroup\n\n\t\t\tlf, _ := expectLoginUI(t)\n\t\t\trequire.NotEmpty(t, lf.UI.Nodes, x.MustEncodeJSON(t, lf))\n\t\t\trequire.Len(t, lf.UI.Messages, 1, x.MustEncodeJSON(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(lf.UI.Messages[0].ID), x.MustEncodeJSON(t, lf))\n\t\t})\n\n\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tloginFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\tgroup = node.PasswordGroup\n\n\t\t\tsse, _ := expectErrorUI(t)\n\t\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/login/export_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/session\"\n)\n\nfunc CheckAALForTest(ctx context.Context, e *HookExecutor, s *session.Session, flow *Flow) error {\n\treturn e.checkAAL(ctx, s, flow)\n}\n"
  },
  {
    "path": "selfservice/flow/login/extension_identifier_label.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/x/jsonschemax\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/schema\"\n)\n\nfunc GetIdentifierLabelFromSchema(ctx context.Context, schemaURL string) (*text.Message, error) {\n\trunner, err := schema.NewExtensionRunner(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc := jsonschema.NewCompiler()\n\tc.ExtractAnnotations = true\n\trunner.Register(c)\n\n\tpaths, err := jsonschemax.ListPaths(ctx, schemaURL, c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlabels := []jsonschemax.Path{}\n\tfor _, path := range paths {\n\t\tif ext := path.CustomProperties[schema.ExtensionName]; ext != nil {\n\t\t\tconfig, ok := ext.(*schema.ExtensionConfig)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif config.Credentials.Password.Identifier ||\n\t\t\t\tconfig.Credentials.WebAuthn.Identifier ||\n\t\t\t\tconfig.Credentials.Passkey.DisplayName ||\n\t\t\t\tconfig.Credentials.TOTP.AccountName ||\n\t\t\t\tconfig.Credentials.Code.Identifier {\n\t\t\t\tlabels = append(labels, path)\n\t\t\t}\n\t\t}\n\t}\n\n\tmetaLabel := text.NewInfoNodeLabelID()\n\tif len(labels) == 1 && labels[0].Title != \"\" {\n\t\tmetaLabel = text.NewInfoNodeLabelGenerated(labels[0].Title, labels[0].Name)\n\t}\n\treturn metaLabel, nil\n}\n"
  },
  {
    "path": "selfservice/flow/login/extension_identifier_label_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/kratos/schema\"\n)\n\nfunc constructSchema(t *testing.T, ecModifier, ucModifier func(*schema.ExtensionConfig)) string {\n\tvar emailConfig, usernameConfig schema.ExtensionConfig\n\n\tif ecModifier != nil {\n\t\tecModifier(&emailConfig)\n\t}\n\tif ucModifier != nil {\n\t\tucModifier(&usernameConfig)\n\t}\n\n\tec, err := json.Marshal(&emailConfig)\n\trequire.NoError(t, err)\n\tuc, err := json.Marshal(&usernameConfig)\n\trequire.NoError(t, err)\n\n\tec, err = sjson.DeleteBytes(ec, \"verification\")\n\trequire.NoError(t, err)\n\tec, err = sjson.DeleteBytes(ec, \"recovery\")\n\trequire.NoError(t, err)\n\tec, err = sjson.DeleteBytes(ec, \"credentials.code.via\")\n\trequire.NoError(t, err)\n\tec, err = sjson.DeleteBytes(ec, \"organizations.matcher\")\n\trequire.NoError(t, err)\n\n\tuc, err = sjson.DeleteBytes(uc, \"verification\")\n\trequire.NoError(t, err)\n\tuc, err = sjson.DeleteBytes(uc, \"recovery\")\n\trequire.NoError(t, err)\n\tuc, err = sjson.DeleteBytes(uc, \"credentials.code.via\")\n\trequire.NoError(t, err)\n\tuc, err = sjson.DeleteBytes(uc, \"organizations.matcher\")\n\trequire.NoError(t, err)\n\n\treturn \"base64://\" + base64.StdEncoding.EncodeToString(fmt.Appendf(nil, `\n{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/identity.email.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n\t  \"type\": \"object\",\n      \"properties\": {\n\t\t\"email\": {\n\t\t  \"title\": \"Email\",\n\t\t  \"type\": \"string\",\n\t\t  \"ory.sh/kratos\": %s\n\t\t},\n\t\t\"username\": {\n\t\t  \"title\": \"Username\",\n\t\t  \"type\": \"string\",\n\t\t  \"ory.sh/kratos\": %s\n\t\t}\n\t  }\n\t}\n  }\n}`, ec, uc))\n}\n\nfunc TestGetIdentifierLabelFromSchema(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\tname                        string\n\t\temailConfig, usernameConfig func(*schema.ExtensionConfig)\n\t\texpected                    *text.Message\n\t}{\n\t\t{\n\t\t\tname: \"email for password\",\n\t\t\temailConfig: func(c *schema.ExtensionConfig) {\n\t\t\t\tc.Credentials.Password.Identifier = true\n\t\t\t},\n\t\t\texpected: text.NewInfoNodeLabelGenerated(\"Email\", \"traits.email\"),\n\t\t},\n\t\t{\n\t\t\tname: \"email for webauthn\",\n\t\t\temailConfig: func(c *schema.ExtensionConfig) {\n\t\t\t\tc.Credentials.WebAuthn.Identifier = true\n\t\t\t},\n\t\t\texpected: text.NewInfoNodeLabelGenerated(\"Email\", \"traits.email\"),\n\t\t},\n\t\t{\n\t\t\tname: \"email for totp\",\n\t\t\temailConfig: func(c *schema.ExtensionConfig) {\n\t\t\t\tc.Credentials.TOTP.AccountName = true\n\t\t\t},\n\t\t\texpected: text.NewInfoNodeLabelGenerated(\"Email\", \"traits.email\"),\n\t\t},\n\t\t{\n\t\t\tname: \"email for code\",\n\t\t\temailConfig: func(c *schema.ExtensionConfig) {\n\t\t\t\tc.Credentials.Code.Identifier = true\n\t\t\t},\n\t\t\texpected: text.NewInfoNodeLabelGenerated(\"Email\", \"traits.email\"),\n\t\t},\n\t\t{\n\t\t\tname: \"email for passkey\",\n\t\t\temailConfig: func(c *schema.ExtensionConfig) {\n\t\t\t\tc.Credentials.Passkey.DisplayName = true\n\t\t\t},\n\t\t\texpected: text.NewInfoNodeLabelGenerated(\"Email\", \"traits.email\"),\n\t\t},\n\t\t{\n\t\t\tname: \"email for all\",\n\t\t\temailConfig: func(c *schema.ExtensionConfig) {\n\t\t\t\tc.Credentials.Password.Identifier = true\n\t\t\t\tc.Credentials.WebAuthn.Identifier = true\n\t\t\t\tc.Credentials.TOTP.AccountName = true\n\t\t\t\tc.Credentials.Code.Identifier = true\n\t\t\t},\n\t\t\texpected: text.NewInfoNodeLabelGenerated(\"Email\", \"traits.email\"),\n\t\t},\n\t\t{\n\t\t\tname: \"username works as well\",\n\t\t\tusernameConfig: func(c *schema.ExtensionConfig) {\n\t\t\t\tc.Credentials.Password.Identifier = true\n\t\t\t},\n\t\t\texpected: text.NewInfoNodeLabelGenerated(\"Username\", \"traits.username\"),\n\t\t},\n\t\t{\n\t\t\tname: \"multiple identifiers\",\n\t\t\temailConfig: func(c *schema.ExtensionConfig) {\n\t\t\t\tc.Credentials.Password.Identifier = true\n\t\t\t},\n\t\t\tusernameConfig: func(c *schema.ExtensionConfig) {\n\t\t\t\tc.Credentials.Password.Identifier = true\n\t\t\t},\n\t\t\texpected: text.NewInfoNodeLabelID(),\n\t\t},\n\t\t{\n\t\t\tname:     \"no identifiers\",\n\t\t\texpected: text.NewInfoNodeLabelID(),\n\t\t},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tlabel, err := GetIdentifierLabelFromSchema(t.Context(), constructSchema(t, tc.emailConfig, tc.usernameConfig))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tc.expected, label)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/login/flow.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\n\thydraclientgo \"github.com/ory/hydra-client-go/v2\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\n// Login Flow\n//\n// This object represents a login flow. A login flow is initiated at the \"Initiate Login API / Browser Flow\"\n// endpoint by a client.\n//\n// Once a login flow is completed successfully, a session cookie or session token will be issued.\n//\n// swagger:model loginFlow\ntype Flow struct {\n\t// ID represents the flow's unique ID. When performing the login flow, this\n\t// represents the id in the login UI's query parameter: http://<selfservice.flows.login.ui_url>/?flow=<flow_id>\n\t//\n\t// required: true\n\tID             uuid.UUID     `json:\"id\" faker:\"-\" db:\"id\" rw:\"r\"`\n\tNID            uuid.UUID     `json:\"-\"  faker:\"-\" db:\"nid\"`\n\tOrganizationID uuid.NullUUID `json:\"organization_id,omitempty\"  faker:\"-\" db:\"organization_id\"`\n\n\t// Ory OAuth 2.0 Login Challenge.\n\t//\n\t// This value is set using the `login_challenge` query parameter of the registration and login endpoints.\n\t// If set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\n\tOAuth2LoginChallenge sqlxx.NullString `json:\"oauth2_login_challenge,omitempty\" faker:\"-\" db:\"oauth2_login_challenge_data\"`\n\n\t// HydraLoginRequest is an optional field whose presence indicates that Kratos\n\t// is being used as an identity provider in a Hydra OAuth2 flow. Kratos\n\t// populates this field by retrieving its value from Hydra and it is used by\n\t// the login and consent UIs.\n\tHydraLoginRequest *hydraclientgo.OAuth2LoginRequest `json:\"oauth2_login_request,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// Type represents the flow's type which can be either \"api\" or \"browser\", depending on the flow interaction.\n\t//\n\t// required: true\n\tType flow.Type `json:\"type\" db:\"type\" faker:\"flow_type\"`\n\n\t// ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\n\t// a new flow has to be initiated.\n\t//\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the flow started.\n\t//\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// InternalContext stores internal context used by internals - for example MFA keys.\n\tInternalContext sqlxx.JSONRawMessage `db:\"internal_context\" json:\"-\" faker:\"-\"`\n\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used\n\t// to forward information contained in the URL's path or query for example.\n\t//\n\t// required: true\n\tRequestURL string `json:\"request_url\" db:\"request_url\"`\n\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo string `json:\"return_to,omitempty\" db:\"-\"`\n\n\t// The active login method\n\t//\n\t// If set contains the login method used. If the flow is new, it is unset.\n\tActive identity.CredentialsType `json:\"active,omitempty\" db:\"active_method\"`\n\n\t// UI contains data which must be shown in the user interface.\n\t//\n\t// required: true\n\tUI *container.Container `json:\"ui\" db:\"ui\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"created_at\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"updated_at\" db:\"updated_at\"`\n\n\t// CSRFToken contains the anti-csrf token associated with this flow. Only set for browser flows.\n\tCSRFToken string `json:\"-\" db:\"csrf_token\"`\n\n\t// Refresh stores whether this login flow should enforce re-authentication.\n\tRefresh bool `json:\"refresh\" db:\"forced\"`\n\n\t// RequestedAAL stores if the flow was requested to update the authenticator assurance level.\n\t//\n\t// This value can be one of \"aal1\", \"aal2\", \"aal3\".\n\tRequestedAAL identity.AuthenticatorAssuranceLevel `json:\"requested_aal\" faker:\"len=4\" db:\"requested_aal\"`\n\n\t// SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the login flow has been completed.\n\t// This is only set if the client has requested a session token exchange code, and if the flow is of type \"api\",\n\t// and only on creating the login flow.\n\tSessionTokenExchangeCode string `json:\"session_token_exchange_code,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// State represents the state of this request:\n\t//\n\t// - choose_method: ask the user to choose a method to sign in with\n\t// - sent_email: the email has been sent to the user\n\t// - passed_challenge: the request was successful and the login challenge was passed.\n\t//\n\t// required: true\n\tState State `json:\"state\" faker:\"-\" db:\"state\"`\n\n\t// Only used internally\n\tIDToken string `json:\"-\" db:\"-\"`\n\n\t// Only used internally\n\tRawIDTokenNonce string `json:\"-\" db:\"-\"`\n\n\t// TransientPayload is used to pass data from the login to hooks and email templates\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// Contains a list of actions, that could follow this flow\n\t//\n\t// It can, for example, contain a reference to the verification flow, created as part of the user's\n\t// registration.\n\tContinueWithItems []flow.ContinueWith `json:\"-\" db:\"-\" faker:\"-\" `\n\n\t// ReturnToVerification contains the redirect URL for the verification flow.\n\tReturnToVerification string `json:\"-\" db:\"-\"`\n\n\tisAccountLinkingFlow bool `db:\"-\"`\n\n\t// IdentitySchema optionally holds the ID of the identity schema that is used\n\t// for this flow. This value can be set by the user when creating the flow and\n\t// should be retained when the flow is saved or converted to another flow.\n\tIdentitySchema flow.IdentitySchema `json:\"identity_schema,omitempty\" faker:\"-\" db:\"identity_schema_id\"`\n}\n\nvar (\n\t_ flow.Flow                 = (*Flow)(nil)\n\t_ flow.FlowWithContinueWith = (*Flow)(nil)\n)\n\nfunc NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, flowType flow.Type) (*Flow, error) {\n\tnow := time.Now().UTC()\n\tid := x.NewUUID()\n\trequestURL := x.RequestURL(r).String()\n\n\t// Pre-validate the return to URL which is contained in the HTTP request.\n\t_, err := redir.SecureRedirectTo(r,\n\t\tconf.SelfServiceBrowserDefaultReturnTo(r.Context()),\n\t\tredir.SecureRedirectUseSourceURL(requestURL),\n\t\tredir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\thydraLoginChallenge, err := hydra.GetLoginChallengeID(conf, r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trefresh, _ := strconv.ParseBool(r.URL.Query().Get(\"refresh\"))\n\n\treturn &Flow{\n\t\tID:                   id,\n\t\tOAuth2LoginChallenge: hydraLoginChallenge,\n\t\tExpiresAt:            now.Add(exp),\n\t\tIssuedAt:             now,\n\t\tUI: &container.Container{\n\t\t\tMethod: \"POST\",\n\t\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(conf.SelfPublicURL(r.Context()), RouteSubmitFlow), id).String(),\n\t\t},\n\t\tRequestURL: requestURL,\n\t\tCSRFToken:  csrf,\n\t\tType:       flowType,\n\t\tRefresh:    refresh,\n\t\tRequestedAAL: identity.AuthenticatorAssuranceLevel(strings.ToLower(cmp.Or(\n\t\t\tr.URL.Query().Get(\"aal\"),\n\t\t\tstring(identity.AuthenticatorAssuranceLevel1)))),\n\t\tInternalContext: []byte(\"{}\"),\n\t\tState:           flow.StateChooseMethod,\n\t}, nil\n}\n\nfunc (f *Flow) GetType() flow.Type                            { return f.Type }\nfunc (f *Flow) GetRequestURL() string                         { return f.RequestURL }\nfunc (f *Flow) GetID() uuid.UUID                              { return f.ID }\nfunc (f *Flow) GetInternalContext() sqlxx.JSONRawMessage      { return f.InternalContext }\nfunc (f *Flow) SetInternalContext(bytes sqlxx.JSONRawMessage) { f.InternalContext = bytes }\nfunc (f *Flow) GetUI() *container.Container                   { return f.UI }\nfunc (f *Flow) GetState() flow.State                          { return f.State }\nfunc (Flow) GetFlowName() flow.FlowName                       { return flow.LoginFlow }\nfunc (Flow) TableName() string                                { return \"selfservice_login_flows\" }\nfunc (f *Flow) ContinueWith() []flow.ContinueWith             { return f.ContinueWithItems }\nfunc (f *Flow) SetReturnToVerification(to string)             { f.ReturnToVerification = to }\nfunc (f *Flow) GetOAuth2LoginChallenge() sqlxx.NullString     { return f.OAuth2LoginChallenge }\nfunc (f *Flow) AppendTo(src *url.URL) *url.URL                { return flow.AppendFlowTo(src, f.ID) }\nfunc (f *Flow) SetState(state flow.State)                     { f.State = state }\nfunc (f *Flow) GetTransientPayload() json.RawMessage          { return f.TransientPayload }\n\n// IsRefresh returns true if the login flow was triggered to re-authenticate the user.\n// This is the case if the refresh query parameter is set to true.\nfunc (f *Flow) IsRefresh() bool { return f.Refresh }\n\nfunc (f *Flow) Valid() error {\n\tif f.ExpiresAt.Before(time.Now()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\treturn nil\n}\n\nfunc (f *Flow) EnsureInternalContext() {\n\tif !gjson.ParseBytes(f.InternalContext).IsObject() {\n\t\tf.InternalContext = []byte(\"{}\")\n\t}\n}\n\nfunc (f Flow) MarshalJSON() ([]byte, error) {\n\ttype local Flow\n\tf.SetReturnTo()\n\treturn json.Marshal(local(f))\n}\n\nfunc (f *Flow) SetReturnTo() {\n\t// Return to is already set, do not overwrite it.\n\tif len(f.ReturnTo) > 0 {\n\t\treturn\n\t}\n\tif u, err := url.Parse(f.RequestURL); err == nil {\n\t\tf.ReturnTo = u.Query().Get(\"return_to\")\n\t}\n}\n\nfunc (f *Flow) AfterFind(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\nfunc (f *Flow) AfterSave(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\nfunc (f *Flow) SecureRedirectToOpts(ctx context.Context, cfg config.Provider) (opts []redir.SecureRedirectOption) {\n\treturn []redir.SecureRedirectOption{\n\t\tredir.SecureRedirectReturnTo(f.ReturnTo),\n\t\tredir.SecureRedirectUseSourceURL(f.RequestURL),\n\t\tredir.SecureRedirectAllowURLs(cfg.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(cfg.Config().SelfPublicURL(ctx)),\n\t\tredir.SecureRedirectOverrideDefaultReturnTo(cfg.Config().SelfServiceFlowLoginReturnTo(ctx, f.Active.String())),\n\t}\n}\n\nfunc (f *Flow) AddContinueWith(c flow.ContinueWith) {\n\tf.ContinueWithItems = append(f.ContinueWithItems, c)\n}\n\nfunc (f *Flow) ToLoggerField() map[string]any {\n\tif f == nil {\n\t\treturn map[string]any{}\n\t}\n\treturn map[string]any{\n\t\t\"id\":            f.ID.String(),\n\t\t\"return_to\":     f.ReturnTo,\n\t\t\"request_url\":   f.RequestURL,\n\t\t\"active\":        f.Active,\n\t\t\"type\":          f.Type,\n\t\t\"nid\":           f.NID,\n\t\t\"state\":         f.State,\n\t\t\"refresh\":       f.Refresh,\n\t\t\"requested_aal\": f.RequestedAAL,\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/login/flow_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login_test\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/x/jsonx\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/pkg\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestFakeFlow(t *testing.T) {\n\tt.Parallel()\n\tvar r login.Flow\n\trequire.NoError(t, faker.FakeData(&r))\n\n\tassert.Equal(t, uuid.Nil, r.ID)\n\tassert.NotEmpty(t, r.IssuedAt)\n\tassert.NotEmpty(t, r.ExpiresAt)\n\tassert.NotEmpty(t, r.RequestURL)\n\tassert.NotEmpty(t, r.Active)\n\tassert.NotNil(t, r.UI)\n}\n\nfunc TestNewFlow(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tconf, _ := pkg.NewFastRegistryWithMocks(t)\n\n\tt.Run(\"type=aal\", func(t *testing.T) {\n\t\tr, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"aal=aal2&refresh=true\"}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, r.Refresh)\n\t\tassert.Equal(t, identity.AuthenticatorAssuranceLevel2, r.RequestedAAL)\n\n\t\tr, err = login.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"refresh=true\"}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, r.Refresh)\n\t\tassert.Equal(t, identity.AuthenticatorAssuranceLevel1, r.RequestedAAL)\n\t})\n\n\tt.Run(\"type=refresh\", func(t *testing.T) {\n\t\tt.Run(\"case=refresh accepts any truthy value\", func(t *testing.T) {\n\t\t\tparameters := []string{\"true\", \"True\", \"1\"}\n\n\t\t\tfor _, refresh := range parameters {\n\t\t\t\tr, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: fmt.Sprintf(\"refresh=%v\", refresh)}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.True(t, r.Refresh)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=refresh silently ignores invalid values\", func(t *testing.T) {\n\t\t\tr, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"refresh=foo\"}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.False(t, r.Refresh)\n\t\t})\n\t})\n\n\tt.Run(\"type=return_to\", func(t *testing.T) {\n\t\t_, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=https://not-allowed/foobar\"}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.Error(t, err)\n\n\t\t_, err = login.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=\" + urlx.AppendPaths(conf.SelfPublicURL(ctx), \"/self-service/login/browser\").String()}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t})\n\n\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\tt.Run(\"case=regular flow creation without a session\", func(t *testing.T) {\n\t\t\tr, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{\n\t\t\t\tURL:  urlx.ParseOrPanic(\"/\"),\n\t\t\t\tHost: \"ory.sh\", TLS: &tls.ConnectionState{},\n\t\t\t}, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, r.IssuedAt, r.ExpiresAt)\n\t\t\tassert.Equal(t, flow.TypeBrowser, r.Type)\n\t\t\tassert.False(t, r.Refresh)\n\t\t\tassert.Equal(t, \"https://ory.sh/\", r.RequestURL)\n\t\t})\n\n\t\tt.Run(\"case=regular flow creation\", func(t *testing.T) {\n\t\t\tr, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{\n\t\t\t\tURL:  urlx.ParseOrPanic(\"https://ory.sh/\"),\n\t\t\t\tHost: \"ory.sh\",\n\t\t\t}, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"https://ory.sh/\", r.RequestURL)\n\t\t})\n\t})\n\n\tt.Run(\"type=api\", func(t *testing.T) {\n\t\tt.Run(\"case=flow with refresh\", func(t *testing.T) {\n\t\t\tr, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{\n\t\t\t\tURL:  urlx.ParseOrPanic(\"/?refresh=true\"),\n\t\t\t\tHost: \"ory.sh\",\n\t\t\t}, flow.TypeAPI)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, r.IssuedAt, r.ExpiresAt)\n\t\t\tassert.Equal(t, flow.TypeAPI, r.Type)\n\t\t\tassert.True(t, r.Refresh)\n\t\t\tassert.Equal(t, \"http://ory.sh/?refresh=true\", r.RequestURL)\n\t\t})\n\n\t\tt.Run(\"case=flow without refresh\", func(t *testing.T) {\n\t\t\tr, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{\n\t\t\t\tURL:  urlx.ParseOrPanic(\"/\"),\n\t\t\t\tHost: \"ory.sh\",\n\t\t\t}, flow.TypeAPI)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, r.IssuedAt, r.ExpiresAt)\n\t\t\tassert.Equal(t, flow.TypeAPI, r.Type)\n\t\t\tassert.False(t, r.Refresh)\n\t\t\tassert.Equal(t, \"http://ory.sh/\", r.RequestURL)\n\t\t})\n\t})\n\n\tt.Run(\"should parse login_challenge when Hydra is configured\", func(t *testing.T) {\n\t\t_, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{URL: urlx.ParseOrPanic(\"https://ory.sh/?login_challenge=badee1\"), Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.Error(t, err)\n\n\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, \"https://hydra\")\n\n\t\tr, err := login.NewFlow(conf, 0, \"csrf\", &http.Request{URL: urlx.ParseOrPanic(\"https://ory.sh/?login_challenge=8aadcb8fc1334186a84c4da9813356d9\"), Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, \"8aadcb8fc1334186a84c4da9813356d9\", string(r.OAuth2LoginChallenge))\n\t})\n}\n\nfunc TestFlow(t *testing.T) {\n\tt.Parallel()\n\tr := &login.Flow{ID: x.NewUUID()}\n\tassert.Equal(t, r.ID, r.GetID())\n\n\tt.Run(\"case=expired\", func(t *testing.T) {\n\t\tfor _, tc := range []struct {\n\t\t\tr     *login.Flow\n\t\t\tvalid bool\n\t\t}{\n\t\t\t{\n\t\t\t\tr:     &login.Flow{ExpiresAt: time.Now().Add(time.Hour), IssuedAt: time.Now().Add(-time.Minute)},\n\t\t\t\tvalid: true,\n\t\t\t},\n\t\t\t{r: &login.Flow{ExpiresAt: time.Now().Add(-time.Hour), IssuedAt: time.Now().Add(-time.Minute)}},\n\t\t} {\n\t\t\tif tc.valid {\n\t\t\t\trequire.NoError(t, tc.r.Valid())\n\t\t\t} else {\n\t\t\t\trequire.Error(t, tc.r.Valid())\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestGetType(t *testing.T) {\n\tt.Parallel()\n\tfor _, ft := range []flow.Type{\n\t\tflow.TypeAPI,\n\t\tflow.TypeBrowser,\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", ft), func(t *testing.T) {\n\t\t\tr := &login.Flow{Type: ft}\n\t\t\tassert.Equal(t, ft, r.GetType())\n\t\t})\n\t}\n}\n\nfunc TestGetRequestURL(t *testing.T) {\n\tt.Parallel()\n\texpectedURL := \"http://foo/bar/baz\"\n\tf := &login.Flow{RequestURL: expectedURL}\n\tassert.Equal(t, expectedURL, f.GetRequestURL())\n}\n\nfunc TestFlowEncodeJSON(t *testing.T) {\n\tt.Parallel()\n\tassert.EqualValues(t, \"\", gjson.Get(jsonx.TestMarshalJSONString(t, &login.Flow{RequestURL: \"https://foo.bar?foo=bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, &login.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, login.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n}\n\nfunc TestFlowDontOverrideReturnTo(t *testing.T) {\n\tt.Parallel()\n\tf := &login.Flow{ReturnTo: \"/foo\"}\n\tf.SetReturnTo()\n\tassert.Equal(t, \"/foo\", f.ReturnTo)\n\n\tf = &login.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}\n\tf.SetReturnTo()\n\tassert.Equal(t, \"/bar\", f.ReturnTo)\n}\n\nfunc TestDuplicateCredentials(t *testing.T) {\n\tt.Parallel()\n\tt.Run(\"case=returns previous data\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tf := new(login.Flow)\n\t\tdc := flow.DuplicateCredentialsData{\n\t\t\tCredentialsType:     \"foo\",\n\t\t\tCredentialsConfig:   sqlxx.JSONRawMessage(`{\"bar\":\"baz\"}`),\n\t\t\tDuplicateIdentifier: \"bar\",\n\t\t}\n\n\t\trequire.NoError(t, flow.SetDuplicateCredentials(f, dc))\n\t\tactual, err := flow.DuplicateCredentials(f)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, dc, *actual)\n\t})\n\n\tt.Run(\"case=returns nil data\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tf := new(login.Flow)\n\t\tactual, err := flow.DuplicateCredentials(f)\n\t\trequire.NoError(t, err)\n\t\tassert.Nil(t, actual)\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/login/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"net/http\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/herodot\"\n\thydraclientgo \"github.com/ory/hydra-client-go/v2\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/stringsx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst (\n\tRouteInitBrowserFlow = \"/self-service/login/browser\"\n\tRouteInitAPIFlow     = \"/self-service/login/api\"\n\n\tRouteGetFlow = \"/self-service/login/flows\"\n\n\tRouteSubmitFlow = \"/self-service/login\"\n)\n\ntype (\n\tdependencies interface {\n\t\tHookExecutorProvider\n\t\tFlowPersistenceProvider\n\t\terrorx.ManagementProvider\n\t\thydra.Provider\n\t\tStrategyProvider\n\t\tsession.HandlerProvider\n\t\tsession.ManagementProvider\n\t\thttpx.WriterProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tnosurfx.CSRFProvider\n\t\totelx.Provider\n\t\tconfig.Provider\n\t\tErrorHandlerProvider\n\t\tsessiontokenexchange.PersistenceProvider\n\t\tlogrusx.Provider\n\t}\n\tHandlerProvider interface {\n\t\tLoginHandler() *Handler\n\t}\n\tHandler struct{ d dependencies }\n)\n\nfunc NewHandler(d dependencies) *Handler { return &Handler{d: d} }\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\th.d.CSRFHandler().IgnorePath(RouteInitAPIFlow)\n\th.d.CSRFHandler().IgnorePath(RouteSubmitFlow)\n\n\tpublic.GET(RouteInitBrowserFlow, h.createBrowserLoginFlow)\n\tpublic.GET(RouteInitAPIFlow, h.createNativeLoginFlow)\n\tpublic.GET(RouteGetFlow, h.getLoginFlow)\n\n\tpublic.POST(RouteSubmitFlow, h.updateLoginFlow)\n\tpublic.GET(RouteSubmitFlow, h.updateLoginFlow)\n}\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))\n\n\tadmin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n}\n\ntype FlowOption func(f *Flow)\n\nfunc WithFlowReturnTo(returnTo string) FlowOption {\n\treturn func(f *Flow) {\n\t\tf.ReturnTo = returnTo\n\t}\n}\n\nfunc WithOrganizationID(organizationID uuid.NullUUID) FlowOption {\n\treturn func(f *Flow) {\n\t\tf.OrganizationID = organizationID\n\t}\n}\n\nfunc WithInternalContext(internalContext []byte) FlowOption {\n\treturn func(f *Flow) {\n\t\tf.InternalContext = internalContext\n\t}\n}\n\nfunc WithFormErrorMessage(messages []text.Message) FlowOption {\n\treturn func(f *Flow) {\n\t\tfor i := range messages {\n\t\t\tf.UI.Messages.Add(&messages[i])\n\t\t}\n\t}\n}\n\nfunc WithIsAccountLinking() FlowOption {\n\treturn func(f *Flow) {\n\t\tf.isAccountLinkingFlow = true\n\t}\n}\n\nfunc WithLoginChallenge(loginChallenge string) FlowOption {\n\treturn func(f *Flow) {\n\t\tf.OAuth2LoginChallenge = sqlxx.NullString(loginChallenge)\n\t}\n}\n\nfunc (h *Handler) NewLoginFlow(w http.ResponseWriter, r *http.Request, ft flow.Type, opts ...FlowOption) (*Flow, *session.Session, error) {\n\tconf := h.d.Config()\n\tf, err := NewFlow(conf, conf.SelfServiceFlowLoginRequestLifespan(r.Context()), h.d.GenerateCSRFToken(r), r, ft)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tfor _, o := range opts {\n\t\to(f)\n\t}\n\n\tif f.RequestedAAL == \"\" {\n\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel1\n\t}\n\n\tswitch cs := stringsx.SwitchExact(string(f.RequestedAAL)); {\n\tcase cs.AddCase(string(identity.AuthenticatorAssuranceLevel1)):\n\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel1\n\t\tidentitySchema := \"\"\n\t\tif requestedSchema := r.URL.Query().Get(\"identity_schema\"); requestedSchema != \"\" {\n\t\t\tidentitySchema, err = conf.SelfServiceFlowIdentitySchema(r.Context(), requestedSchema)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t}\n\t\tf.IdentitySchema = flow.IdentitySchema(identitySchema)\n\tcase cs.AddCase(string(identity.AuthenticatorAssuranceLevel2)):\n\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\tdefault:\n\t\treturn nil, nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to parse AuthenticationMethod Assurance Level (AAL): %s\", cs.ToUnknownCaseErr()))\n\t}\n\n\t// We assume an error means the user has no session\n\tsess, err := h.d.SessionManager().FetchFromRequest(r.Context(), r)\n\tif e := new(session.ErrNoActiveSessionFound); errors.As(err, &e) {\n\t\t// No session exists yet\n\t\treturnSessionTokenExchangeCode, _ := strconv.ParseBool(r.URL.Query().Get(\"return_session_token_exchange_code\"))\n\n\t\tif ft == flow.TypeAPI && returnSessionTokenExchangeCode {\n\t\t\te, err := h.d.SessionTokenExchangePersister().CreateSessionTokenExchanger(r.Context(), f.ID)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, errors.WithStack(err)\n\t\t\t}\n\t\t\tf.SessionTokenExchangeCode = e.InitCode\n\t\t}\n\n\t\t// We can not request an AAL > 1 because we must first verify the first factor.\n\t\tif f.RequestedAAL > identity.AuthenticatorAssuranceLevel1 {\n\t\t\treturn nil, nil, errors.WithStack(ErrSessionRequiredForHigherAAL)\n\t\t}\n\n\t\t// We are setting refresh to false if no session exists.\n\t\tf.Refresh = false\n\n\t\tgoto preLoginHook\n\t} else if err != nil {\n\t\t// Some other error happened - return that one.\n\t\treturn nil, nil, err\n\t} else {\n\t\t// A session exists already\n\t\tif f.Refresh {\n\t\t\t// We are refreshing so let's continue\n\t\t\tgoto preLoginHook\n\t\t}\n\n\t\t// We are not refreshing - so are we requesting MFA?\n\n\t\t// If level is 1 we are not requesting AAL -> we are logged in already.\n\t\tif f.RequestedAAL == identity.AuthenticatorAssuranceLevel1 {\n\t\t\treturn nil, sess, errors.WithStack(ErrAlreadyLoggedIn)\n\t\t}\n\n\t\t// We are requesting an assurance level which the session already has. So we are not upgrading the session\n\t\t// in which case we want to return an error.\n\t\tif f.RequestedAAL <= sess.AuthenticatorAssuranceLevel {\n\t\t\treturn nil, sess, errors.WithStack(ErrAlreadyLoggedIn)\n\t\t}\n\n\t\t// Looks like we are requesting an AAL which is higher than what the session has.\n\t\tgoto preLoginHook\n\t}\n\npreLoginHook:\n\tloginStrategies := h.d.LoginStrategies(r.Context(), PrepareOrganizations(r, f, sess)...)\n\tfor _, s := range loginStrategies {\n\t\tvar populateErr error\n\n\t\tswitch f.RequestedAAL {\n\t\tcase identity.AuthenticatorAssuranceLevel1:\n\t\t\tswitch s := s.(type) {\n\t\t\tcase AAL1FormHydrator:\n\t\t\t\tswitch {\n\t\t\t\tcase f.IsRefresh() && sess != nil:\n\t\t\t\t\t// Refreshing takes precedence over identifier_first auth which can not be a refresh flow.\n\t\t\t\t\t// Therefor this comes first.\n\t\t\t\t\tpopulateErr = s.PopulateLoginMethodFirstFactorRefresh(r, f, sess)\n\t\t\t\tcase h.d.Config().SelfServiceLoginFlowIdentifierFirstEnabled(r.Context()) && !f.isAccountLinkingFlow:\n\t\t\t\t\tpopulateErr = s.PopulateLoginMethodIdentifierFirstIdentification(r, f)\n\t\t\t\tdefault:\n\t\t\t\t\tpopulateErr = s.PopulateLoginMethodFirstFactor(r, f)\n\t\t\t\t}\n\t\t\tcase AAL2FormHydrator:\n\t\t\t\tcontinue\n\t\t\tdefault:\n\t\t\t\tpopulateErr = errors.WithStack(\n\t\t\t\t\tx.PseudoPanic.WithReasonf(\"A login strategy was expected to implement interface AAL1FormHydrator but did not.\"),\n\t\t\t\t)\n\t\t\t}\n\t\tcase identity.AuthenticatorAssuranceLevel2:\n\t\t\tswitch s := s.(type) {\n\t\t\tcase AAL2FormHydrator:\n\t\t\t\tswitch {\n\t\t\t\tcase f.IsRefresh():\n\t\t\t\t\t// Refresh takes precedence.\n\t\t\t\t\tpopulateErr = s.PopulateLoginMethodSecondFactorRefresh(r, f)\n\t\t\t\tdefault:\n\t\t\t\t\tpopulateErr = s.PopulateLoginMethodSecondFactor(r, f)\n\t\t\t\t}\n\t\t\tcase AAL1FormHydrator:\n\t\t\t\tcontinue\n\t\t\tdefault:\n\t\t\t\tpopulateErr = errors.WithStack(\n\t\t\t\t\tx.PseudoPanic.WithReasonf(\"A login strategy was expected to implement interface AAL2FormHydrator but did not.\"),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif populateErr != nil {\n\t\t\treturn nil, nil, populateErr\n\t\t}\n\t}\n\n\tif f.Refresh {\n\t\tf.UI.Messages.Set(text.NewInfoLoginReAuth())\n\t}\n\n\tif sess != nil && f.RequestedAAL > sess.AuthenticatorAssuranceLevel && f.RequestedAAL > identity.AuthenticatorAssuranceLevel1 {\n\t\tf.UI.Messages.Add(text.NewInfoLoginMFA())\n\t}\n\n\tif err := sortNodes(r.Context(), f.UI.Nodes); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif f.Type == flow.TypeBrowser {\n\t\tf.UI.SetCSRF(h.d.GenerateCSRFToken(r))\n\t}\n\n\tif err := h.d.LoginHookExecutor().PreLoginHook(w, r, f); err != nil {\n\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, \"\", node.DefaultGroup, err)\n\t\treturn f, sess, nil\n\t}\n\n\tif err := h.d.LoginFlowPersister().CreateLoginFlow(r.Context(), f); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\t// If the strategy supports fast login, we can attempt a fast path.\n\t// We execute the fast login here because it requires the flow to be created first.\n\tfor _, s := range loginStrategies {\n\t\tif strategy, ok := s.(FastLoginStrategy); ok {\n\t\t\tif err := strategy.FastLogin2FA(w, r, f, sess); errors.Is(err, flow.ErrStrategyNotResponsible) {\n\t\t\t\tcontinue\n\t\t\t} else if errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\t\tspan := trace.SpanFromContext(r.Context())\n\t\t\t\tspan.AddEvent(events.NewLoginInitiated(r.Context(), f.ID, ft.String(), f.Refresh, f.OrganizationID, string(f.RequestedAAL)))\n\t\t\t\treturn nil, nil, err\n\t\t\t} else if err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\tspan := trace.SpanFromContext(r.Context())\n\tspan.AddEvent(events.NewLoginInitiated(r.Context(), f.ID, ft.String(), f.Refresh, f.OrganizationID, string(f.RequestedAAL)))\n\treturn f, nil, nil\n}\n\nfunc (h *Handler) FromOldFlow(w http.ResponseWriter, r *http.Request, of Flow) (*Flow, error) {\n\tnf, _, err := h.NewLoginFlow(w, r, of.Type)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnf.RequestURL = of.RequestURL\n\tnf.IdentitySchema = of.IdentitySchema\n\treturn nf, nil\n}\n\n// Create Native Login Flow Parameters\n//\n// swagger:parameters createNativeLoginFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createNativeLoginFlow struct {\n\t// Refresh a login session\n\t//\n\t// If set to true, this will refresh an existing login session by\n\t// asking the user to sign in again. This will reset the\n\t// authenticated_at time of the session.\n\t//\n\t// in: query\n\tRefresh bool `json:\"refresh\"`\n\n\t// Request a Specific AuthenticationMethod Assurance Level\n\t//\n\t// Use this parameter to upgrade an existing session's authenticator assurance level (AAL). This\n\t// allows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password,\n\t// the AAL is 1. If you wish to \"upgrade\" the session's security by asking the user to perform TOTP / WebAuth/ ...\n\t// you would set this to \"aal2\".\n\t//\n\t// in: query\n\tRequestAAL identity.AuthenticatorAssuranceLevel `json:\"aal\"`\n\n\t// The Session Token of the Identity performing the settings flow.\n\t//\n\t// in: header\n\tSessionToken string `json:\"X-Session-Token\"`\n\n\t// EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token\n\t// after the login flow has been completed.\n\t//\n\t// in: query\n\tEnableSessionTokenExchangeCode bool `json:\"return_session_token_exchange_code\"`\n\n\t// The URL to return the browser to after the flow was completed.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n\n\t// An optional organization ID that should be used for logging this user in.\n\t// This parameter is only effective in the Ory Network.\n\t//\n\t// required: false\n\t// in: query\n\tOrganization string `json:\"organization\"`\n\n\t// Via should contain the identity's credential the code should be sent to. Only relevant in aal2 flows.\n\t//\n\t// DEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice\n\t// of MFA credentials to choose from to perform the second factor instead.\n\t//\n\t// in: query\n\tVia string `json:\"via\"`\n\n\t// An optional identity schema to use for the login flow.\n\t//\n\t// required: false\n\t// in: query\n\tIdentitySchema string `json:\"identity_schema\"`\n}\n\n// swagger:route GET /self-service/login/api frontend createNativeLoginFlow\n//\n// # Create Login Flow for Native Apps\n//\n// This endpoint initiates a login flow for native apps that do not use a browser, such as mobile devices, smart TVs, and so on.\n//\n// If a valid provided session cookie or session token is provided, a 400 Bad Request error\n// will be returned unless the URL query parameter `?refresh=true` is set.\n//\n// To fetch an existing login flow call `/self-service/login/flows?flow=<flow_id>`.\n//\n// You MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n// Pages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n// you vulnerable to a variety of CSRF attacks, including CSRF login attacks.\n//\n// In the case of an error, the `error.id` of the JSON response body can be one of:\n//\n// - `session_already_available`: The user is already signed in.\n// - `session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n// - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n//\n// This endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n//\n// More information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: loginFlow\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createNativeLoginFlow(w http.ResponseWriter, r *http.Request) {\n\tvar err error\n\tctx, span := h.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.flow.login.createNativeLoginFlow\")\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\tf, _, err := h.NewLoginFlow(w, r, flow.TypeAPI)\n\tif err != nil {\n\t\tif !errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\th.d.Writer().WriteError(w, r, err)\n\t\t}\n\t\treturn\n\t}\n\n\th.d.Writer().Write(w, r, f)\n}\n\n// Initialize Browser Login Flow Parameters\n//\n// swagger:parameters createBrowserLoginFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createBrowserLoginFlow struct {\n\t// Refresh a login session\n\t//\n\t// If set to true, this will refresh an existing login session by\n\t// asking the user to sign in again. This will reset the\n\t// authenticated_at time of the session.\n\t//\n\t// in: query\n\tRefresh bool `json:\"refresh\"`\n\n\t// Request a Specific AuthenticationMethod Assurance Level\n\t//\n\t// Use this parameter to upgrade an existing session's authenticator assurance level (AAL). This\n\t// allows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password,\n\t// the AAL is 1. If you wish to \"upgrade\" the session's security by asking the user to perform TOTP / WebAuth/ ...\n\t// you would set this to \"aal2\".\n\t//\n\t// in: query\n\tRequestAAL identity.AuthenticatorAssuranceLevel `json:\"aal\"`\n\n\t// The URL to return the browser to after the flow was completed.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n\n\t// An optional Hydra login challenge. If present, Kratos will cooperate with\n\t// Ory Hydra to act as an OAuth2 identity provider.\n\t//\n\t// The value for this parameter comes from `login_challenge` URL Query parameter sent to your\n\t// application (e.g. `/login?login_challenge=abcde`).\n\t//\n\t// required: false\n\t// in: query\n\tHydraLoginChallenge string `json:\"login_challenge\"`\n\n\t// An optional organization ID that should be used for logging this user in.\n\t// This parameter is only effective in the Ory Network.\n\t//\n\t// required: false\n\t// in: query\n\tOrganization string `json:\"organization\"`\n\n\t// Via should contain the identity's credential the code should be sent to. Only relevant in aal2 flows.\n\t//\n\t// DEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice\n\t// of MFA credentials to choose from to perform the second factor instead.\n\t//\n\t// in: query\n\tVia string `json:\"via\"`\n\n\t// An optional identity schema to use for the login flow.\n\t//\n\t// required: false\n\t// in: query\n\tIdentitySchema string `json:\"identity_schema\"`\n}\n\n// swagger:route GET /self-service/login/browser frontend createBrowserLoginFlow\n//\n// # Create Login Flow for Browsers\n//\n// This endpoint initializes a browser-based user login flow. This endpoint will set the appropriate\n// cookies and anti-CSRF measures required for browser-based flows.\n//\n// If this endpoint is opened as a link in the browser, it will be redirected to\n// `selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\n// exists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\n// `?refresh=true` was set.\n//\n// If this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n// case of an error, the `error.id` of the JSON response body can be one of:\n//\n// - `session_already_available`: The user is already signed in.\n// - `session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\n// - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n// - `security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n//\n// The optional query parameter login_challenge is set when using Kratos with\n// Hydra in an OAuth2 flow. See the oauth2_provider.url configuration\n// option.\n//\n// This endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n//\n// More information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: loginFlow\n//\t  303: emptyResponse\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createBrowserLoginFlow(w http.ResponseWriter, r *http.Request) {\n\tvar err error\n\tctx, span := h.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.flow.login.createBrowserLoginFlow\")\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\tvar (\n\t\thydraLoginRequest   *hydraclientgo.OAuth2LoginRequest\n\t\thydraLoginChallenge sqlxx.NullString\n\t)\n\tif r.URL.Query().Has(\"login_challenge\") {\n\t\tvar err error\n\t\thydraLoginChallenge, err = hydra.GetLoginChallengeID(h.d.Config(), r)\n\t\tif err != nil {\n\t\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\t\treturn\n\t\t}\n\n\t\thydraLoginRequest, err = h.d.Hydra().GetLoginRequest(ctx, string(hydraLoginChallenge))\n\t\tif err != nil {\n\t\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\t\treturn\n\t\t}\n\n\t\tif !hydraLoginRequest.GetSkip() {\n\t\t\tq := r.URL.Query()\n\t\t\tq.Set(\"refresh\", \"true\")\n\t\t\tr.URL.RawQuery = q.Encode()\n\t\t}\n\n\t\t// on OAuth2 flows, we need to use the RequestURL\n\t\t// as the ReturnTo URL.\n\t\t// This is because a user might want to switch between\n\t\t// different flows, such as login to registration and login to recovery.\n\t\t// After completing a complex flow, such as recovery, we want the user\n\t\t// to be redirected back to the original OAuth2 login flow.\n\t\tif hydraLoginRequest.RequestUrl != \"\" && h.d.Config().OAuth2ProviderOverrideReturnTo(ctx) {\n\t\t\t// replace the return_to query parameter\n\t\t\tq := r.URL.Query()\n\t\t\tq.Set(\"return_to\", hydraLoginRequest.RequestUrl)\n\t\t\tr.URL.RawQuery = q.Encode()\n\t\t}\n\t}\n\n\ta, sess, err := h.NewLoginFlow(w, r, flow.TypeBrowser)\n\tif errors.Is(err, ErrAlreadyLoggedIn) {\n\t\tif hydraLoginRequest != nil {\n\t\t\tif !hydraLoginRequest.GetSkip() {\n\t\t\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, errors.WithStack(herodot.ErrForbidden.WithReason(\"ErrAlreadyLoggedIn indicated we can skip login, but Hydra asked us to refresh\")))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trt, hydraErr := h.d.Hydra().AcceptLoginRequest(ctx,\n\t\t\t\thydra.AcceptLoginRequestParams{\n\t\t\t\t\tLoginChallenge:        string(hydraLoginChallenge),\n\t\t\t\t\tIdentityID:            sess.IdentityID.String(),\n\t\t\t\t\tSessionID:             sess.ID.String(),\n\t\t\t\t\tAuthenticationMethods: sess.AMR,\n\t\t\t\t})\n\t\t\tif hydraErr != nil {\n\t\t\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, hydraErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tx.SendFlowCompletedAsRedirectOrJSON(w, r, h.d.Writer(), err, rt)\n\t\t\treturn\n\t\t}\n\n\t\treturnTo, redirErr := redir.SecureRedirectTo(r, h.d.Config().SelfServiceBrowserDefaultReturnTo(ctx),\n\t\t\tredir.SecureRedirectAllowSelfServiceURLs(h.d.Config().SelfPublicURL(ctx)),\n\t\t\tredir.SecureRedirectAllowURLs(h.d.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),\n\t\t)\n\t\tif redirErr != nil {\n\t\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, redirErr)\n\t\t\treturn\n\t\t}\n\n\t\tx.SendFlowCompletedAsRedirectOrJSON(w, r, h.d.Writer(), err, returnTo.String())\n\t\treturn\n\t} else if errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\treturn\n\t} else if err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\treturn\n\t}\n\n\ta.HydraLoginRequest = hydraLoginRequest\n\n\tx.SendFlowCompletedAsRedirectOrJSON(w, r, h.d.Writer(), a, a.AppendTo(h.d.Config().SelfServiceFlowLoginUI(ctx)).String())\n}\n\n// Get Login Flow Parameters\n//\n// swagger:parameters getLoginFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getLoginFlow struct {\n\t// The Login Flow ID\n\t//\n\t// The value for this parameter comes from `flow` URL Query parameter sent to your\n\t// application (e.g. `/login?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tID string `json:\"id\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// swagger:route GET /self-service/login/flows frontend getLoginFlow\n//\n// # Get Login Flow\n//\n// This endpoint returns a login flow's context with, for example, error details and other information.\n//\n// Browser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n// For AJAX requests you must ensure that cookies are included in the request or requests will fail.\n//\n// If you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n// and you need to forward the incoming HTTP Cookie header to this endpoint:\n//\n//\t```js\n//\t// pseudo-code example\n//\trouter.get('/login', async function (req, res) {\n//\t  const flow = await client.getLoginFlow(req.header('cookie'), req.query['flow'])\n//\n//\t  res.render('login', flow)\n//\t})\n//\t```\n//\n// This request may fail due to several reasons. The `error.id` can be one of:\n//\n// - `session_already_available`: The user is already signed in.\n// - `self_service_flow_expired`: The flow is expired and you should request a new one.\n//\n// More information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: loginFlow\n//\t  403: errorGeneric\n//\t  404: errorGeneric\n//\t  410: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-low\nfunc (h *Handler) getLoginFlow(w http.ResponseWriter, r *http.Request) {\n\tvar err error\n\tctx, span := h.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.flow.login.getLoginFlow\")\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\tar, err := h.d.LoginFlowPersister().GetLoginFlow(ctx, x.ParseUUID(r.URL.Query().Get(\"id\")))\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\t// Browser flows must include the CSRF token\n\t//\n\t// Resolves: https://github.com/ory/kratos/issues/1282\n\tif ar.Type == flow.TypeBrowser && !nosurf.VerifyToken(h.d.GenerateCSRFToken(r), ar.CSRFToken) {\n\t\th.d.Writer().WriteError(w, r, nosurfx.CSRFErrorReason(r, h.d))\n\t\treturn\n\t}\n\n\tif ar.ExpiresAt.Before(time.Now()) {\n\t\tif ar.Type == flow.TypeBrowser {\n\t\t\tredirectURL := flow.GetFlowExpiredRedirectURL(ctx, h.d.Config(), RouteInitBrowserFlow, ar.ReturnTo)\n\n\t\t\th.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).\n\t\t\t\tWithReason(\"The login flow has expired. Redirect the user to the login flow init endpoint to initialize a new login flow.\").\n\t\t\t\tWithDetail(\"redirect_to\", redirectURL.String()).\n\t\t\t\tWithDetail(\"return_to\", ar.ReturnTo)))\n\t\t\treturn\n\t\t}\n\t\th.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).\n\t\t\tWithReason(\"The login flow has expired. Call the login flow init API endpoint to initialize a new login flow.\").\n\t\t\tWithDetail(\"api\", urlx.AppendPaths(h.d.Config().SelfPublicURL(ctx), RouteInitAPIFlow).String())))\n\t\treturn\n\t}\n\n\tif ar.OAuth2LoginChallenge != \"\" {\n\t\thlr, err := h.d.Hydra().GetLoginRequest(ctx, string(ar.OAuth2LoginChallenge))\n\t\tif err != nil {\n\t\t\t// We don't redirect back to the third party on errors because Hydra doesn't\n\t\t\t// give us the 3rd party return_uri when it redirects to the login UI.\n\t\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\t\treturn\n\t\t}\n\t\tar.HydraLoginRequest = hlr\n\t}\n\n\th.d.Writer().Write(w, r, ar)\n}\n\n// Update Login Flow Parameters\n//\n// swagger:parameters updateLoginFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateLoginFlow struct {\n\t// The Login Flow ID\n\t//\n\t// The value for this parameter comes from `flow` URL Query parameter sent to your\n\t// application (e.g. `/login?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tFlow string `json:\"flow\"`\n\n\t// in: body\n\t// required: true\n\tBody updateLoginFlowBody\n\n\t// The Session Token of the Identity performing the settings flow.\n\t//\n\t// in: header\n\tSessionToken string `json:\"X-Session-Token\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// swagger:model updateLoginFlowBody\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateLoginFlowBody struct{}\n\n// swagger:route POST /self-service/login frontend updateLoginFlow\n//\n// # Submit a Login Flow\n//\n// Use this endpoint to complete a login flow. This endpoint\n// behaves differently for API and browser flows.\n//\n// API flows expect `application/json` to be sent in the body and responds with\n//   - HTTP 200 and a application/json body with the session token on success;\n//   - HTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\n//   - HTTP 400 on form validation errors.\n//\n// Browser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\n//   - a HTTP 303 redirect to the post/after login URL or the `return_to` value if it was set and if the login succeeded;\n//   - a HTTP 303 redirect to the login UI URL with the flow ID containing the validation errors otherwise.\n//\n// Browser flows with an accept header of `application/json` will not redirect but instead respond with\n//   - HTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\n//   - HTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\n//   - HTTP 400 on form validation errors.\n//\n// If this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\n// case of an error, the `error.id` of the JSON response body can be one of:\n//\n//   - `session_already_available`: The user is already signed in.\n//   - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n//   - `security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n//   - `browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\n//     Most likely used in Social Sign In flows.\n//\n// More information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n//\n//\tSchemes: http, https\n//\n//\tConsumes:\n//\t- application/json\n//\t- application/x-www-form-urlencoded\n//\n//\tProduces:\n//\t- application/json\n//\n//\tHeader:\n//\t- Set-Cookie\n//\n//\tResponses:\n//\t  200: successfulNativeLogin\n//\t  303: emptyResponse\n//\t  400: loginFlow\n//\t  410: errorGeneric\n//\t  422: errorBrowserLocationChangeRequired\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-high\nfunc (h *Handler) updateLoginFlow(w http.ResponseWriter, r *http.Request) {\n\tvar err error\n\tctx, span := h.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.flow.login.updateLoginFlow\")\n\tctx = semconv.ContextWithAttributes(ctx,\n\t\tattribute.String(events.AttributeKeySelfServiceStrategyUsed.String(), \"login\"),\n\t\tattribute.String(events.AttributeKeySelfServiceFlowName.String(), \"login\"),\n\t)\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\trid, err := flow.GetFlowID(r)\n\tif err != nil {\n\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, nil, \"\", node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tf, err := h.d.LoginFlowPersister().GetLoginFlow(ctx, rid)\n\tif err != nil {\n\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, \"\", node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tsess, err := h.d.SessionManager().FetchFromRequest(ctx, r)\n\tif err == nil {\n\t\tif f.Refresh {\n\t\t\t// If we want to refresh, continue the login\n\t\t\tgoto continueLogin\n\t\t}\n\n\t\tif f.RequestedAAL > sess.AuthenticatorAssuranceLevel {\n\t\t\t// If we want to upgrade AAL, continue the login\n\t\t\tgoto continueLogin\n\t\t}\n\n\t\tif x.IsJSONRequest(r) || f.Type == flow.TypeAPI {\n\t\t\t// We are not upgrading AAL, nor are we refreshing. Error!\n\t\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, \"\", node.DefaultGroup, errors.WithStack(ErrAlreadyLoggedIn))\n\t\t\treturn\n\t\t}\n\n\t\thttp.Redirect(w, r, h.d.Config().SelfServiceBrowserDefaultReturnTo(ctx).String(), http.StatusSeeOther)\n\t\treturn\n\t} else if e := new(session.ErrNoActiveSessionFound); errors.As(err, &e) {\n\t\t// Only failure scenario here is if we try to upgrade the session to a higher AAL without actually\n\t\t// having a session.\n\t\tif f.RequestedAAL > identity.AuthenticatorAssuranceLevel1 {\n\t\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, \"\", node.DefaultGroup, errors.WithStack(ErrSessionRequiredForHigherAAL))\n\t\t\treturn\n\t\t}\n\n\t\tsess = session.NewInactiveSession()\n\t} else {\n\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, \"\", node.DefaultGroup, err)\n\t\treturn\n\t}\n\ncontinueLogin:\n\tif err := f.Valid(); err != nil {\n\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, \"\", node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tvar ct identity.CredentialsType\n\tvar i *identity.Identity\n\tvar group node.UiNodeGroup\n\tfor _, ss := range h.d.AllLoginStrategies() {\n\t\tinterim, err := ss.Login(w, r, f, sess)\n\t\tgroup = ss.NodeGroup()\n\t\tif errors.Is(err, flow.ErrStrategyNotResponsible) {\n\t\t\tcontinue\n\t\t} else if errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\treturn\n\t\t} else if err != nil {\n\t\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, ss.ID(), group, err)\n\t\t\treturn\n\t\t}\n\n\t\t// What can happen is that we re-authenticate as another user. In this case, we need to use a completely fresh\n\t\t// session!\n\t\tif sess.IdentityID != uuid.Nil && sess.IdentityID != interim.ID {\n\t\t\tsess = session.NewInactiveSession()\n\t\t}\n\n\t\tmethod := ss.CompletedAuthenticationMethod(ctx)\n\t\tsess.CompletedLoginForMethod(method)\n\t\ti = interim\n\t\tct = ss.ID()\n\t\tbreak\n\t}\n\n\tif i == nil {\n\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, ct, node.DefaultGroup, errors.WithStack(schema.NewNoLoginStrategyResponsible()))\n\t\treturn\n\t}\n\n\tif err := h.d.LoginHookExecutor().PostLoginHook(w, r, group, f, i, sess, \"\"); err != nil {\n\t\tif errors.Is(err, ErrAddressNotVerified) {\n\t\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, ct, node.DefaultGroup, errors.WithStack(schema.NewAddressNotVerifiedError()))\n\t\t\treturn\n\t\t}\n\n\t\th.d.LoginFlowErrorHandler().WriteFlowError(w, r, f, ct, node.DefaultGroup, err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/login/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/httptest\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\tstdtotp \"github.com/pquerna/otp/totp\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/httprouterx\"\n\n\t\"github.com/ory/kratos/corpx\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/strategy/totp\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc TestFlowLifecycle(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tfakeHydra := hydra.NewFake()\n\treg.SetHydra(fakeHydra)\n\n\trouterPublic := httprouterx.NewTestRouterPublic(t)\n\tts, _ := testhelpers.NewKratosServerWithRouters(t, reg, routerPublic, httprouterx.NewTestRouterAdminWithPrefix(t))\n\tloginTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\n\treturnToTS := testhelpers.NewRedirTS(t, \"return_to\", conf)\n\terrorTS := testhelpers.NewErrorTestServer(t, reg)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, returnToTS.URL)\n\n\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t{ID: \"default\", URL: \"file://./stub/password.schema.json\"},\n\t\t{ID: \"email\", URL: \"file://./stub/email.schema.json\", SelfserviceSelectable: true},\n\t\t{ID: \"phone\", URL: \"file://./stub/phone.schema.json\", SelfserviceSelectable: true},\n\t\t{ID: \"not-allowed\", URL: \"file://./stub/password.schema.json\"},\n\t})\n\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\n\tassertion := func(body []byte, isForced, isApi bool) {\n\t\tr := gjson.GetBytes(body, \"refresh\")\n\t\tassert.True(t, r.Exists(), \"%s\", body)\n\t\tassert.Equal(t, isForced, r.Bool(), \"%s\", body)\n\t\tif isApi {\n\t\t\tassert.Equal(t, \"api\", gjson.GetBytes(body, \"type\").String())\n\t\t} else {\n\t\t\tassert.Equal(t, \"browser\", gjson.GetBytes(body, \"type\").String())\n\t\t}\n\t}\n\n\tinitAuthenticatedFlow := func(t *testing.T, extQuery url.Values, isAPI bool) (*http.Response, []byte) {\n\t\troute := login.RouteInitBrowserFlow\n\t\tif isAPI {\n\t\t\troute = login.RouteInitAPIFlow\n\t\t}\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+route, nil)\n\t\treq.URL.RawQuery = extQuery.Encode()\n\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, routerPublic, req)\n\t\tif isAPI {\n\t\t\tassert.Len(t, res.Header.Get(\"Set-Cookie\"), 0)\n\t\t}\n\t\treturn res, body\n\t}\n\n\tinitUnauthenticatedFlow := func(t *testing.T, extQuery url.Values, isAPI bool) (*http.Response, []byte) {\n\t\troute := login.RouteInitBrowserFlow\n\t\tif isAPI {\n\t\t\troute = login.RouteInitAPIFlow\n\t\t}\n\t\tclient := ts.Client()\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+route, nil)\n\n\t\treq.URL.RawQuery = extQuery.Encode()\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, errors.WithStack(err))\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, errors.WithStack(err))\n\t\trequire.NoError(t, res.Body.Close())\n\t\treturn res, body\n\t}\n\n\tinitFlowWithAccept := func(t *testing.T, query url.Values, isAPI bool, accept string) (*http.Response, []byte) {\n\t\troute := login.RouteInitBrowserFlow\n\t\tif isAPI {\n\t\t\troute = login.RouteInitAPIFlow\n\t\t}\n\t\tc := ts.Client()\n\t\treq, err := http.NewRequest(\"GET\", ts.URL+route+\"?\"+query.Encode(), nil)\n\t\trequire.NoError(t, err)\n\t\tif accept != \"\" {\n\t\t\treq.Header.Set(\"Accept\", accept)\n\t\t}\n\n\t\tres, err := c.Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\treturn res, body\n\t}\n\n\tinitFlow := func(t *testing.T, query url.Values, isAPI bool) (*http.Response, []byte) {\n\t\treturn initFlowWithAccept(t, query, isAPI, \"\")\n\t}\n\n\tinitSPAFlow := func(t *testing.T, query url.Values) (*http.Response, []byte) {\n\t\treturn initFlowWithAccept(t, query, false, \"application/json\")\n\t}\n\n\tid1mail, id2mail := x.NewUUID().String(), x.NewUUID().String()\n\tidentity1 := &identity.Identity{\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{id1mail},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`), // foobar\n\t\t\t},\n\t\t},\n\t\tState:  identity.StateActive,\n\t\tTraits: identity.Traits(`{\"username\":\"` + id1mail + `\"}`),\n\t}\n\tidentity2 := &identity.Identity{\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{id2mail},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`), // foobar\n\t\t\t},\n\t\t},\n\t\tState:  identity.StateActive,\n\t\tTraits: identity.Traits(`{\"username\":\"` + id2mail + `\"}`),\n\t}\n\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), identity1))\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), identity2))\n\n\tt.Run(\"lifecycle=submit\", func(t *testing.T) {\n\t\tt.Run(\"interaction=unauthenticated\", func(t *testing.T) {\n\t\t\trun := func(t *testing.T, tt flow.Type, aal string, values url.Values) (string, *http.Response) {\n\t\t\t\tf := login.Flow{\n\t\t\t\t\tType: tt, ExpiresAt: time.Now().Add(time.Minute), IssuedAt: time.Now(),\n\t\t\t\t\tUI: container.New(\"\"), Refresh: false, RequestedAAL: identity.AuthenticatorAssuranceLevel(aal),\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))\n\n\t\t\t\tres, err := http.PostForm(ts.URL+login.RouteSubmitFlow+\"?flow=\"+f.ID.String(), values)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := x.MustReadAll(res.Body)\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\treturn string(body), res\n\t\t\t}\n\n\t\t\tt.Run(\"case=ensure aal can only be upgraded with a session on submit\", func(t *testing.T) {\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\t\tbody, res := run(t, flow.TypeAPI, \"aal2\", url.Values{\"method\": {\"password\"}})\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteSubmitFlow)\n\t\t\t\t\tassertx.EqualAsJSON(t, \"You can not requested a higher AAL (AAL2/AAL3) without an active session.\", gjson.Get(body, \"error.reason\").String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\tbody, res := run(t, flow.TypeBrowser, \"aal2\", url.Values{\"method\": {\"password\"}})\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), errorTS.URL)\n\t\t\t\t\tassertx.EqualAsJSON(t, \"You can not requested a higher AAL (AAL2/AAL3) without an active session.\", gjson.Get(body, \"reason\").String())\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=end up with method missing when aal is ok\", func(t *testing.T) {\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\t\tbody, res := run(t, flow.TypeAPI, \"aal1\", url.Values{\"method\": {\"not-exist\"}})\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteSubmitFlow)\n\t\t\t\t\tassertx.EqualAsJSON(t, text.NewErrorValidationLoginNoStrategyFound().Text, gjson.Get(body, \"ui.messages.0.text\").String(), body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\tbody, res := run(t, flow.TypeBrowser, \"aal1\", url.Values{\"method\": {\"not-exist\"}})\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL)\n\t\t\t\t\tassertx.EqualAsJSON(t, text.NewErrorValidationLoginNoStrategyFound().Text, gjson.Get(body, \"ui.messages.0.text\").String(), body)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=reset the session when refresh is true but identity is different\", func(t *testing.T) {\n\t\t\t\ttesthelpers.NewRedirSessionEchoTS(t, reg)\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, returnToTS.URL)\n\t\t\t\t})\n\n\t\t\t\trun := func(t *testing.T, tt flow.Type) (string, string) {\n\t\t\t\t\tf := login.Flow{Type: tt, ExpiresAt: time.Now().Add(time.Minute), IssuedAt: time.Now(), UI: container.New(\"\"), Refresh: false, RequestedAAL: \"aal1\"}\n\t\t\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))\n\n\t\t\t\t\thc := testhelpers.NewClientWithCookies(t)\n\t\t\t\t\tres, err := hc.PostForm(ts.URL+login.RouteSubmitFlow+\"?flow=\"+f.ID.String(), url.Values{\"method\": {\"password\"}, \"password_identifier\": {id1mail}, \"password\": {\"foobar\"}, \"csrf_token\": {nosurfx.FakeCSRFToken}})\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tfirstSession := x.MustReadAll(res.Body)\n\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\t\tf = login.Flow{Type: tt, ExpiresAt: time.Now().Add(time.Minute), IssuedAt: time.Now(), UI: container.New(\"\"), Refresh: true, RequestedAAL: \"aal1\"}\n\t\t\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))\n\n\t\t\t\t\tvv := testhelpers.EncodeFormAsJSON(t, tt == flow.TypeAPI, url.Values{\"method\": {\"password\"}, \"password_identifier\": {id2mail}, \"password\": {\"foobar\"}, \"csrf_token\": {nosurfx.FakeCSRFToken}})\n\n\t\t\t\t\treq, err := http.NewRequest(\"POST\", ts.URL+login.RouteSubmitFlow+\"?flow=\"+f.ID.String(), strings.NewReader(vv))\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tif tt == flow.TypeAPI {\n\t\t\t\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\t\t\t\treq.Header.Set(\"Authorization\", \"Bearer \"+gjson.GetBytes(firstSession, \"session_token\").String())\n\t\t\t\t\t} else {\n\t\t\t\t\t\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\t\t\t\t\t}\n\n\t\t\t\t\tres, err = hc.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tsecondSession := x.MustReadAll(res.Body)\n\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\t\treturn string(firstSession), string(secondSession)\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\ta, b := run(t, flow.TypeBrowser)\n\n\t\t\t\t\tassert.Equal(t, id1mail, gjson.Get(a, \"identity.traits.username\").String())\n\t\t\t\t\tassert.Equal(t, id2mail, gjson.Get(b, \"identity.traits.username\").String())\n\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(b, \"id\").String())\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(b, \"id\").String(), gjson.Get(a, \"id\").String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\t\ta, b := run(t, flow.TypeAPI)\n\n\t\t\t\t\tassert.Equal(t, id1mail, gjson.Get(a, \"session.identity.traits.username\").String())\n\t\t\t\t\tassert.Equal(t, id2mail, gjson.Get(b, \"session.identity.traits.username\").String())\n\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(a, \"session_token\").String())\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(a, \"session_token\").String(), gjson.Get(b, \"session_token\").String())\n\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(b, \"session.id\").String())\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(b, \"session.id\").String(), gjson.Get(a, \"id\").String())\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=changed kratos session identifiers when refresh is true\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, returnToTS.URL)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\t// Setup flow\n\t\t\t\t\tf := login.Flow{Type: flow.TypeBrowser, ExpiresAt: time.Now().Add(time.Minute), IssuedAt: time.Now(), UI: container.New(\"\"), Refresh: false, RequestedAAL: \"aal1\"}\n\t\t\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))\n\n\t\t\t\t\t// Submit Login\n\t\t\t\t\thc := testhelpers.NewClientWithCookies(t)\n\t\t\t\t\tres, err := hc.PostForm(ts.URL+login.RouteSubmitFlow+\"?flow=\"+f.ID.String(), url.Values{\"method\": {\"password\"}, \"password_identifier\": {id1mail}, \"password\": {\"foobar\"}, \"csrf_token\": {nosurfx.FakeCSRFToken}})\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t// Check response and session cookie presence\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\trequire.Len(t, hc.Jar.Cookies(urlx.ParseOrPanic(ts.URL+login.RouteGetFlow)), 1)\n\t\t\t\t\trequire.Contains(t, fmt.Sprintf(\"%v\", hc.Jar.Cookies(urlx.ParseOrPanic(ts.URL))), \"ory_kratos_session\")\n\t\t\t\t\tcookies1 := hc.Jar.Cookies(urlx.ParseOrPanic(ts.URL + login.RouteGetFlow))\n\n\t\t\t\t\treq, err := http.NewRequest(\"GET\", ts.URL+\"/sessions/whoami\", nil)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tres, err = hc.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\tfirstSession := x.MustReadAll(res.Body)\n\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\t\t// Refresh\n\t\t\t\t\tf = login.Flow{Type: flow.TypeBrowser, ExpiresAt: time.Now().Add(time.Minute), IssuedAt: time.Now(), UI: container.New(\"\"), Refresh: true, RequestedAAL: \"aal1\"}\n\t\t\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))\n\n\t\t\t\t\tvv := testhelpers.EncodeFormAsJSON(t, false, url.Values{\"method\": {\"password\"}, \"password_identifier\": {id1mail}, \"password\": {\"foobar\"}, \"csrf_token\": {nosurfx.FakeCSRFToken}})\n\n\t\t\t\t\treq, err = http.NewRequest(\"POST\", ts.URL+login.RouteSubmitFlow+\"?flow=\"+f.ID.String(), strings.NewReader(vv))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n\t\t\t\t\t// Submit Login\n\t\t\t\t\tres, err = hc.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t// Check response and session cookie presence\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\trequire.Len(t, hc.Jar.Cookies(urlx.ParseOrPanic(ts.URL+login.RouteGetFlow)), 1)\n\t\t\t\t\trequire.Contains(t, fmt.Sprintf(\"%v\", hc.Jar.Cookies(urlx.ParseOrPanic(ts.URL))), \"ory_kratos_session\")\n\t\t\t\t\tcookies2 := hc.Jar.Cookies(urlx.ParseOrPanic(ts.URL + login.RouteGetFlow))\n\n\t\t\t\t\treq, err = http.NewRequest(\"GET\", ts.URL+\"/sessions/whoami\", nil)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tres, err = hc.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\tsecondSession := x.MustReadAll(res.Body)\n\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\t\t// Sessions should still be resolvable despite different kratos session identifier due to nonce\n\t\t\t\t\tassert.NotEqual(t, cookies1[0].String(), cookies2[0].String())\n\t\t\t\t\tassert.Equal(t, id1mail, gjson.Get(string(firstSession), \"identity.traits.username\").String())\n\t\t\t\t\tassert.Equal(t, id1mail, gjson.Get(string(secondSession), \"identity.traits.username\").String())\n\t\t\t\t\tassert.Equal(t, gjson.Get(string(secondSession), \"id\").String(), gjson.Get(string(firstSession), \"id\").String())\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=ensure aal is checked for upgradeability on session\", func(t *testing.T) {\n\t\t\trun := func(t *testing.T, tt flow.Type, values url.Values) (string, *http.Response) {\n\t\t\t\tf := login.Flow{\n\t\t\t\t\tType: tt, ExpiresAt: time.Now().Add(time.Minute), IssuedAt: time.Now(),\n\t\t\t\t\tUI: container.New(\"\"), Refresh: false, RequestedAAL: \"aal1\",\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))\n\n\t\t\t\treq, err := http.NewRequest(\"GET\", ts.URL+login.RouteSubmitFlow+\"?flow=\"+f.ID.String(), strings.NewReader(values.Encode()))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n\t\t\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, routerPublic, req)\n\t\t\t\treturn string(body), res\n\t\t\t}\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tbody, res := run(t, flow.TypeAPI, url.Values{\"method\": {\"password\"}})\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteSubmitFlow)\n\t\t\t\tassertx.EqualAsJSON(t, login.ErrAlreadyLoggedIn.Reason(), gjson.Get(body, \"ui.messages.0.text\").String(), body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t_, res := run(t, flow.TypeBrowser, url.Values{\"method\": {\"password\"}})\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), returnToTS.URL)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should return an error because the request is expired\", func(t *testing.T) {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginRequestLifespan, \"50ms\")\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginRequestLifespan, \"10m\")\n\t\t\t})\n\n\t\t\texpired := time.Now().Add(-time.Minute)\n\t\t\trun := func(t *testing.T, tt flow.Type, aal string, values string, isSPA bool) (string, *http.Response) {\n\t\t\t\tf := login.Flow{\n\t\t\t\t\tType: tt, ExpiresAt: expired, IssuedAt: time.Now(),\n\t\t\t\t\tUI: container.New(\"\"), Refresh: false, RequestedAAL: identity.AuthenticatorAssuranceLevel(aal),\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), &f))\n\n\t\t\t\treq, err := http.NewRequest(\"POST\", ts.URL+login.RouteSubmitFlow+\"?flow=\"+f.ID.String(), strings.NewReader(values))\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tif isSPA {\n\t\t\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\t\t} else {\n\t\t\t\t\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\t\t\t\t}\n\n\t\t\t\tres, err := http.DefaultClient.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := x.MustReadAll(res.Body)\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\treturn string(body), res\n\t\t\t}\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tactual, res := run(t, flow.TypeAPI, \"aal1\", `{\"method\":\"password\"}`, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteSubmitFlow)\n\t\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.Get(actual, \"use_flow_id\").String())\n\t\t\t\tassertx.EqualAsJSONExcept(t, flow.NewFlowExpiredError(expired), json.RawMessage(actual), []string{\"use_flow_id\", \"since\"}, \"expired\", \"%s\", actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tbody, res := run(t, flow.TypeBrowser, \"aal1\", url.Values{\"method\": {\"password\"}}.Encode(), false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL)\n\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"expired\", \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=SPA\", func(t *testing.T) {\n\t\t\t\tactual, res := run(t, flow.TypeBrowser, \"aal1\", `{\"method\":\"password\"}`, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteSubmitFlow)\n\t\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.Get(actual, \"use_flow_id\").String())\n\t\t\t\tassertx.EqualAsJSONExcept(t, flow.NewFlowExpiredError(expired), json.RawMessage(actual), []string{\"use_flow_id\", \"since\"}, \"expired\", \"%s\", actual)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should return to settings flow after successful mfa login after recovery\", func(t *testing.T) {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeTOTP.String(), true)\n\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToTS.URL})\n\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, string(identity.AuthenticatorAssuranceLevel1))\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, string(identity.AuthenticatorAssuranceLevel1))\n\t\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeTOTP.String(), false)\n\t\t\t})\n\n\t\t\tkey, err := totp.NewKey(context.Background(), \"foo\", reg)\n\t\t\trequire.NoError(t, err)\n\t\t\temail := testhelpers.RandomEmail()\n\t\t\tid := &identity.Identity{\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\"password\": {\n\t\t\t\t\t\tType:        \"password\",\n\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\": \"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, email)),\n\t\t\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\t\t}\n\n\t\t\trequire.NoError(t, reg.IdentityManager().CreateIdentities(context.Background(), []*identity.Identity{id}, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\tid.SetCredentials(identity.CredentialsTypeTOTP, identity.Credentials{\n\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\tIdentifiers: []string{id.ID.String()},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"` + string(key.URL()) + `\"}`),\n\t\t\t})\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(context.Background(), id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\th := func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\tsess, err := testhelpers.NewActiveSession(r, reg, id, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tsess.AuthenticatorAssuranceLevel = identity.AuthenticatorAssuranceLevel1\n\t\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), sess))\n\t\t\t\trequire.NoError(t, reg.SessionManager().IssueCookie(context.Background(), w, r, sess))\n\t\t\t\trequire.Equal(t, identity.AuthenticatorAssuranceLevel1, sess.AuthenticatorAssuranceLevel)\n\t\t\t}\n\n\t\t\trouterPublic.GET(\"/mock-session\", h)\n\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\t\ttesthelpers.MockHydrateCookieClient(t, client, ts.URL+\"/mock-session\")\n\n\t\t\tsettingsURL := ts.URL + settings.RouteInitBrowserFlow + \"?return_to=\" + url.QueryEscape(returnToTS.URL)\n\t\t\treq, err := http.NewRequest(\"GET\", settingsURL, nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// we initialize the settings flow with a session that has AAL1 set\n\t\t\tresp, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\t// we expect the request to redirect to the login flow because the AAL1 session is not sufficient\n\t\t\trequestURL, err := url.Parse(resp.Request.Referer())\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, login.RouteInitBrowserFlow, requestURL.Path)\n\t\t\trequire.Equal(t, \"aal2\", requestURL.Query().Get(\"aal\"))\n\t\t\trequire.Equal(t, settingsURL, requestURL.Query().Get(\"return_to\"))\n\n\t\t\t// we expect to be on the login page now\n\t\t\trespURL := resp.Request.URL\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, \"/login-ts\", respURL.Path)\n\t\t\tflowID := respURL.Query().Get(\"flow\")\n\t\t\trequire.NotEmpty(t, flowID)\n\n\t\t\tcode, err := stdtotp.GenerateCode(key.Secret(), time.Now())\n\t\t\trequire.NoError(t, err)\n\n\t\t\treq, err = http.NewRequest(\"GET\", ts.URL+login.RouteGetFlow+\"?id=\"+flowID, nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\t\t\tresp, err = client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\tbody := string(x.MustReadAll(resp.Body))\n\t\t\tdefer func() { _ = resp.Body.Close() }()\n\n\t\t\ttotpNode := gjson.Get(body, \"ui.nodes.#(attributes.name==totp_code)\").String()\n\t\t\trequire.NotEmpty(t, totpNode)\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"ui.action\").String())\n\n\t\t\tcsrfToken := gjson.Get(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\n\t\t\treq, err = http.NewRequest(\"POST\", ts.URL+login.RouteSubmitFlow+\"?flow=\"+flowID, strings.NewReader(url.Values{\n\t\t\t\t\"method\":     {\"totp\"},\n\t\t\t\t\"totp_code\":  {code},\n\t\t\t\t\"csrf_token\": {csrfToken},\n\t\t\t}.Encode()))\n\n\t\t\trequire.NoError(t, err)\n\t\t\treq.Header.Add(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n\t\t\tclient.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\n\t\t\tresp, err = client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusSeeOther, resp.StatusCode)\n\n\t\t\tlocation, err := resp.Location()\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, settings.RouteInitBrowserFlow, location.Path)\n\t\t})\n\t})\n\n\tt.Run(\"lifecycle=init\", func(t *testing.T) {\n\t\tt.Run(\"suite=identity schema in query\", func(t *testing.T) {\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tname           string\n\t\t\t\tquery          url.Values\n\t\t\t\twantErr        bool\n\t\t\t\twantIdentifier string\n\t\t\t}{{\n\t\t\t\tname:    \"not-allowed\",\n\t\t\t\tquery:   url.Values{\"identity_schema\": {\"not-allowed\"}},\n\t\t\t\twantErr: true,\n\t\t\t}, {\n\t\t\t\tname:    \"not-found\",\n\t\t\t\tquery:   url.Values{\"identity_schema\": {\"not-found\"}},\n\t\t\t\twantErr: true,\n\t\t\t}, {\n\t\t\t\tname:           \"phone\",\n\t\t\t\tquery:          url.Values{\"identity_schema\": {\"phone\"}},\n\t\t\t\twantIdentifier: \"Phone Number\",\n\t\t\t}, {\n\t\t\t\tname:           \"email\",\n\t\t\t\tquery:          url.Values{\"identity_schema\": {\"email\"}},\n\t\t\t\twantIdentifier: \"E-Mail Address\",\n\t\t\t}, {\n\t\t\t\tname:           \"default\",\n\t\t\t\tquery:          url.Values{\"identity_schema\": {\"default\"}},\n\t\t\t\twantIdentifier: \"Username\",\n\t\t\t}} {\n\t\t\t\tt.Run(\"case=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\tt.Run(\"flow=api\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := initFlow(t, tc.query, true)\n\t\t\t\t\t\tif tc.wantErr {\n\t\t\t\t\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassert.Equalf(t, tc.wantIdentifier, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier).meta.label.text\").String(), \"%s\", body)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := initFlow(t, tc.query, false)\n\t\t\t\t\t\tif tc.wantErr {\n\t\t\t\t\t\t\trequire.Contains(t, res.Request.URL.String(), errorTS.URL, \"%s\", body)\n\t\t\t\t\t\t\tassert.EqualValues(t, \"Bad Request\", gjson.GetBytes(body, \"status\").String(), \"%s\", body)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassert.Equalf(t, tc.wantIdentifier, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier).meta.label.context.title\").String(), \"%s\", body)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"flow=api\", func(t *testing.T) {\n\t\t\tt.Run(\"case=does not set forced flag on unauthenticated request\", func(t *testing.T) {\n\t\t\t\tres, body := initFlow(t, url.Values{}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassertion(body, false, true)\n\t\t\t\tassert.Empty(t, gjson.GetBytes(body, \"session_token_exchange_code\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=returns session exchange code with any truthy value\", func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToTS.URL, \"https://example.com\"})\n\t\t\t\tparameters := []string{\"true\", \"True\", \"1\"}\n\n\t\t\t\tfor _, param := range parameters {\n\t\t\t\t\tt.Run(\"return_session_token_exchange_code=\"+param, func(t *testing.T) {\n\t\t\t\t\t\tres, body := initFlow(t, url.Values{\n\t\t\t\t\t\t\t\"return_session_token_exchange_code\": {param},\n\t\t\t\t\t\t\t\"return_to\":                          {\"https://example.com/redirect\"},\n\t\t\t\t\t\t}, true)\n\t\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\t\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"session_token_exchange_code\").String())\n\t\t\t\t\t\tassert.Equal(t, \"https://example.com/redirect\", gjson.GetBytes(body, \"return_to\").String())\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"case=can not request refresh and aal at the same time on unauthenticated request\", func(t *testing.T) {\n\t\t\t\tres, body := initFlow(t, url.Values{\"refresh\": {\"true\"}, \"aal\": {\"aal2\"}}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassertx.EqualAsJSON(t, \"You can not requested a higher AAL (AAL2/AAL3) without an active session.\", gjson.GetBytes(body, \"error.reason\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=can request refresh and aal at the same time on authenticated request\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"refresh\": {\"true\"}, \"aal\": {\"aal2\"}}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassertx.EqualAsJSON(t, \"Please confirm this action by verifying that it is you.\", gjson.GetBytes(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t\tassertx.EqualAsJSON(t, \"Please complete the second authentication challenge.\", gjson.GetBytes(body, \"ui.messages.1.text\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=can not request aal2 on unauthenticated request\", func(t *testing.T) {\n\t\t\t\tres, body := initFlow(t, url.Values{\"aal\": {\"aal2\"}}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassertx.EqualAsJSON(t, \"You can not requested a higher AAL (AAL2/AAL3) without an active session.\", gjson.GetBytes(body, \"error.reason\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=ignores aal1 if session has aal1 already\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"aal\": {\"aal1\"}}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassertx.EqualAsJSON(t, \"A valid session was detected and thus login is not possible. Did you forget to set `?refresh=true`?\", gjson.GetBytes(body, \"error.reason\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=aal0 is not a valid value\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"aal\": {\"aal0\"}}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassertx.EqualAsJSON(t, \"Unable to parse AuthenticationMethod Assurance Level (AAL): expected one of [aal1, aal2] but got aal0\", gjson.GetBytes(body, \"error.reason\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=indicates two factor auth\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"aal\": {\"aal2\"}}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassert.Equal(t, gjson.GetBytes(body, \"ui.messages.0.text\").String(), text.NewInfoLoginMFA().Text)\n\t\t\t})\n\n\t\t\tt.Run(\"case=does not set forced flag on unauthenticated request with refresh=true\", func(t *testing.T) {\n\t\t\t\tres, body := initFlow(t, url.Values{\"refresh\": {\"true\"}}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassertion(body, false, true)\n\t\t\t})\n\n\t\t\tt.Run(\"case=does not set forced flag on authenticated request without refresh=true\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{}, true)\n\t\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\tassertx.EqualAsJSON(t, login.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=does not set forced flag on authenticated request with refresh=false\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"refresh\": {\"false\"}}, true)\n\t\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\tassertx.EqualAsJSON(t, login.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=does set forced flag on authenticated request with refresh=true\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"refresh\": {\"true\"}}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassertion(body, true, true)\n\t\t\t})\n\n\t\t\tt.Run(\"case=check info message on authenticated request with refresh=true\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"refresh\": {\"true\"}}, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), login.RouteInitAPIFlow)\n\t\t\t\tassertion(body, true, true)\n\t\t\t\tassert.Equal(t, gjson.GetBytes(body, \"ui.messages.0.text\").String(), text.NewInfoLoginReAuth().Text)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\t\tt.Run(\"case=does not set forced flag on unauthenticated request\", func(t *testing.T) {\n\t\t\t\tres, body := initFlow(t, url.Values{}, false)\n\t\t\t\tassertion(body, false, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL)\n\t\t\t})\n\n\t\t\tt.Run(\"case=never returns a session token exchange code\", func(t *testing.T) {\n\t\t\t\t_, body := initFlow(t, urlx.ParseOrPanic(\"/?return_session_token_exchange_code=true\").Query(), false)\n\t\t\t\tassertion(body, false, false)\n\t\t\t\tassert.Empty(t, gjson.GetBytes(body, \"session_token_exchange_code\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=can not request refresh and aal at the same time on unauthenticated request\", func(t *testing.T) {\n\t\t\t\tres, body := initFlow(t, url.Values{\"refresh\": {\"true\"}, \"aal\": {\"aal2\"}}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), errorTS.URL)\n\t\t\t\tassertx.EqualAsJSON(t, \"You can not requested a higher AAL (AAL2/AAL3) without an active session.\", gjson.GetBytes(body, \"reason\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=can request refresh and aal at the same time on authenticated request\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"refresh\": {\"true\"}, \"aal\": {\"aal2\"}}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL)\n\t\t\t\tassertx.EqualAsJSON(t, \"Please confirm this action by verifying that it is you.\", gjson.GetBytes(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t\tassertx.EqualAsJSON(t, \"Please complete the second authentication challenge.\", gjson.GetBytes(body, \"ui.messages.1.text\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=redirects if aal2 is requested and set up already without refresh\", func(t *testing.T) {\n\t\t\t\tres, _ := initAuthenticatedFlow(t, url.Values{\"aal\": {\"aal2\"}, \"set_aal\": {\"aal2\"}}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), returnToTS.URL)\n\t\t\t})\n\n\t\t\tt.Run(\"case=can not request aal2 on unauthenticated request\", func(t *testing.T) {\n\t\t\t\tres, body := initFlow(t, url.Values{\"aal\": {\"aal2\"}}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), errorTS.URL)\n\t\t\t\tassertx.EqualAsJSON(t, \"You can not requested a higher AAL (AAL2/AAL3) without an active session.\", gjson.GetBytes(body, \"reason\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=ignores aal1 if session has aal1 already\", func(t *testing.T) {\n\t\t\t\tres, _ := initAuthenticatedFlow(t, url.Values{\"aal\": {\"aal1\"}}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), returnToTS.URL)\n\t\t\t})\n\n\t\t\tt.Run(\"case=aal0 is not a valid value\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"aal\": {\"aal0\"}}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), errorTS.URL)\n\t\t\t\tassertx.EqualAsJSON(t, \"Unable to parse AuthenticationMethod Assurance Level (AAL): expected one of [aal1, aal2] but got aal0\", gjson.GetBytes(body, \"reason\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=indicates two factor auth\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"aal\": {\"aal2\"}}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL)\n\t\t\t\tassert.Equal(t, gjson.GetBytes(body, \"ui.messages.0.text\").String(), text.NewInfoLoginMFA().Text)\n\t\t\t})\n\n\t\t\tt.Run(\"case=makes request with JSON\", func(t *testing.T) {\n\t\t\t\tres, body := initSPAFlow(t, url.Values{})\n\t\t\t\tassertion(body, false, false)\n\t\t\t\tassert.NotContains(t, res.Request.URL.String(), loginTS.URL)\n\t\t\t})\n\n\t\t\tt.Run(\"case=does not set forced flag on unauthenticated request with refresh=true\", func(t *testing.T) {\n\t\t\t\tres, body := initFlow(t, url.Values{\"refresh\": {\"true\"}}, false)\n\t\t\t\tassertion(body, false, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL)\n\t\t\t})\n\n\t\t\tt.Run(\"case=does not set forced flag on authenticated request without refresh=true\", func(t *testing.T) {\n\t\t\t\tres, _ := initAuthenticatedFlow(t, url.Values{}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), returnToTS.URL)\n\t\t\t})\n\n\t\t\tt.Run(\"case=does not set forced flag on authenticated request with refresh=false\", func(t *testing.T) {\n\t\t\t\tres, _ := initAuthenticatedFlow(t, url.Values{\"refresh\": {\"false\"}}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), returnToTS.URL)\n\t\t\t})\n\n\t\t\tt.Run(\"case=does set forced flag on authenticated request with refresh=true\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"refresh\": {\"true\"}}, false)\n\t\t\t\tassertion(body, true, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL)\n\t\t\t})\n\n\t\t\tt.Run(\"case=redirects with 303\", func(t *testing.T) {\n\t\t\t\tc := &http.Client{}\n\t\t\t\t// don't get the reference, instead copy the values, so we don't alter the client directly.\n\t\t\t\t*c = *ts.Client()\n\t\t\t\t// prevent the redirect\n\t\t\t\tc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\t\t\t\treq, err := http.NewRequest(\"GET\", ts.URL+login.RouteInitBrowserFlow, nil)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tres, err := c.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t// here we check that the redirect status is 303\n\t\t\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t\t})\n\n\t\t\tt.Run(\"case=refuses to parse oauth2 login challenge when Hydra is not configured\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"login_challenge\": {hydra.FakeValidLoginChallenge}}, false)\n\t\t\t\trequire.Contains(t, res.Request.URL.String(), errorTS.URL)\n\t\t\t\trequire.Contains(t, string(body), \"refusing to parse\")\n\t\t\t})\n\n\t\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, \"https://fake-hydra\")\n\n\t\t\tt.Run(\"case=oauth2 flow init should override return_to to the oauth2 request_url\", func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{\"https://www.ory.sh\", \"https://example.com\"})\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderOverrideReturnTo, true)\n\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderOverrideReturnTo, false)\n\t\t\t\t})\n\n\t\t\t\tres, _ := initUnauthenticatedFlow(t, url.Values{\n\t\t\t\t\t\"return_to\":       {\"https://example.com\"},\n\t\t\t\t\t\"login_challenge\": {hydra.FakeValidLoginChallenge},\n\t\t\t\t}, false)\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\trequire.Contains(t, res.Request.URL.String(), loginTS.URL)\n\n\t\t\t\tc := ts.Client()\n\t\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+login.RouteGetFlow, nil)\n\t\t\t\treq.URL.RawQuery = url.Values{\"id\": {res.Request.URL.Query().Get(\"flow\")}}.Encode()\n\n\t\t\t\tres, err := c.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, errors.WithStack(err))\n\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\tassert.Equal(t, \"https://www.ory.sh\", gjson.GetBytes(body, \"return_to\").Value())\n\t\t\t})\n\n\t\t\tt.Run(\"case=invalid oauth2 login challenge returns 400 Bad Request\", func(t *testing.T) {\n\t\t\t\tres, body := initAuthenticatedFlow(t, url.Values{\"login_challenge\": {hydra.FakeInvalidLoginChallenge}}, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), errorTS.URL)\n\t\t\t\tassert.Equal(t, int64(http.StatusBadRequest), gjson.GetBytes(body, \"code\").Int())\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"reason\").String(), \"Unable to get OAuth 2.0 Login Challenge\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=oauth2 flow init succeeds\", func(t *testing.T) {\n\t\t\t\tres, _ := initAuthenticatedFlow(t, url.Values{\"login_challenge\": {hydra.FakeValidLoginChallenge}}, false)\n\t\t\t\trequire.Contains(t, res.Request.URL.String(), loginTS.URL)\n\t\t\t})\n\n\t\t\tt.Run(\"case=oauth2 flow init adds oauth2_login_request field\", func(t *testing.T) {\n\t\t\t\tres, body := initSPAFlow(t, url.Values{\"login_challenge\": {hydra.FakeValidLoginChallenge}})\n\t\t\t\tassert.NotContains(t, res.Request.URL.String(), loginTS.URL)\n\n\t\t\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"oauth2_login_request\").Value(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=oauth2 flow with existing session and JSON request should not return null\", func(t *testing.T) {\n\t\t\t\t// This test reproduces issue #10255 where the /self-service/login/browser endpoint\n\t\t\t\t// returns null when called with an existing session, a Hydra login challenge, and\n\t\t\t\t// an Accept: application/json header.\n\n\t\t\t\t// Has a side effect on this test suite but since its running serial and there is no significantly easier way it's acceptable.\n\t\t\t\tfakeHydra.Skip = true\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tfakeHydra.Skip = false\n\t\t\t\t})\n\n\t\t\t\t// Make a login request with an authenticated session, Hydra login challenge, and JSON accept\n\t\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+login.RouteInitBrowserFlow+\"?login_challenge=\"+hydra.FakeValidLoginChallenge, nil)\n\t\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, routerPublic, req)\n\n\t\t\t\t// Before the fix, this would return 200 OK with \"null\" as body because of variable shadowing\n\t\t\t\t// After the fix, it should return a proper error response with ErrAlreadyLoggedIn\n\t\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode, \"Response should be 400 Bad Request, got body: %s\", body)\n\t\t\t\tassert.NotEqual(t, \"null\", string(body), \"Response should not be null\")\n\t\t\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"error.id\").String(), \"Should have error.id field, got body: %s\", body)\n\t\t\t\tassert.Equal(t, \"session_already_available\", gjson.GetBytes(body, \"error.id\").String(), \"Should return session_already_available error, got body: %s\", body)\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"error.reason\").String(), \"A valid session was detected\", \"Should have proper error reason, got body: %s\", body)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=relative redirect when self-service login ui is a relative URL\", func(t *testing.T) {\n\t\t\treg.Config().MustSet(ctx, config.ViperKeySelfServiceLoginUI, \"/login-ts\")\n\t\t\tassert.Regexp(\n\t\t\t\tt,\n\t\t\t\t\"^/login-ts.*$\",\n\t\t\t\ttesthelpers.GetSelfServiceRedirectLocation(t, ts.URL+login.RouteInitBrowserFlow),\n\t\t\t)\n\t\t})\n\t})\n}\n\nfunc TestGetFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tpublic, _ := testhelpers.NewKratosServerWithCSRF(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\treturnToTS := testhelpers.NewRedirTS(t, \"\", conf)\n\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/password.schema.json\")\n\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t{ID: \"default\", URL: \"file://./stub/password.schema.json\"},\n\t\t{ID: \"email\", URL: \"file://./stub/email.schema.json\", SelfserviceSelectable: true},\n\t\t{ID: \"phone\", URL: \"file://./stub/phone.schema.json\", SelfserviceSelectable: true},\n\t\t{ID: \"not-allowed\", URL: \"file://./stub/password.schema.json\"},\n\t})\n\n\tsetupLoginUI := func(t *testing.T, c *http.Client) *httptest.Server {\n\t\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t// It is important that we use a HTTP request to fetch the flow because that will show us if CSRF works or not\n\t\t\t_, err := w.Write(testhelpers.EasyGetBody(t, c, public.URL+login.RouteGetFlow+\"?id=\"+r.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t}))\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginUI, ts.URL)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, returnToTS.URL)\n\t\tt.Cleanup(ts.Close)\n\t\treturn ts\n\t}\n\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword), map[string]interface{}{\n\t\t\"enabled\": true,\n\t})\n\n\tt.Run(\"case=fetching successful\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupLoginUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+login.RouteInitBrowserFlow)\n\n\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String(), \"%s\", body)\n\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"id\").String(), \"%s\", body)\n\t\tassert.Empty(t, gjson.GetBytes(body, \"headers\").Value(), \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), gjson.GetBytes(body, \"id\").String(), \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), public.URL, \"%s\", body)\n\t})\n\n\tt.Run(\"case=csrf cookie missing\", func(t *testing.T) {\n\t\tclient := http.DefaultClient\n\t\tsetupLoginUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+login.RouteInitBrowserFlow)\n\n\t\tassert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=expired\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupLoginUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+login.RouteInitBrowserFlow)\n\n\t\t// Expire the flow\n\t\tf, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tf.ExpiresAt = time.Now().Add(-time.Second)\n\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), f))\n\n\t\t// Try the flow but it is expired\n\t\tres, body := testhelpers.EasyGet(t, client, public.URL+login.RouteGetFlow+\"?id=\"+f.ID.String())\n\t\tassert.EqualValues(t, http.StatusGone, res.StatusCode)\n\t\tassert.Equal(t, public.URL+login.RouteInitBrowserFlow, gjson.GetBytes(body, \"error.details.redirect_to\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=expired with return_to and schema_id\", func(t *testing.T) {\n\t\treturnTo := returnToTS.URL\n\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupLoginUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+login.RouteInitBrowserFlow+\"?return_to=\"+returnTo+\"&identity_schema=email\")\n\n\t\t// Expire the flow\n\t\tf, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tf.ExpiresAt = time.Now().Add(-time.Second)\n\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), f))\n\n\t\t// Retrieve the flow and verify that return_to is in the response\n\t\tgetURL := fmt.Sprintf(\"%s%s?id=%s&return_to=%s\", public.URL, login.RouteGetFlow, f.ID, returnTo)\n\t\tgetBody := testhelpers.EasyGetBody(t, client, getURL)\n\t\tassert.Equal(t, gjson.GetBytes(getBody, \"error.details.return_to\").String(), returnTo)\n\n\t\t// submit the flow but it is expired\n\t\tu := public.URL + login.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\tres, err := client.PostForm(u, url.Values{\"password_identifier\": {\"email@ory.sh\"}, \"csrf_token\": {f.CSRFToken}, \"password\": {\"password\"}, \"method\": {\"password\"}})\n\t\trequire.NoError(t, err)\n\t\tresBody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\tf, err = reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, public.URL+login.RouteInitBrowserFlow+\"?return_to=\"+returnTo+\"&identity_schema=email\", f.RequestURL)\n\t})\n\n\tt.Run(\"case=not found\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupLoginUI(t, client)\n\n\t\tres, _ := testhelpers.EasyGet(t, client, public.URL+login.RouteGetFlow+\"?id=\"+x.NewUUID().String())\n\t\tassert.EqualValues(t, http.StatusNotFound, res.StatusCode)\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/login/hook.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype (\n\tPreHookExecutor interface {\n\t\tExecuteLoginPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error\n\t}\n\n\tPostHookExecutor interface {\n\t\tExecuteLoginPostHook(w http.ResponseWriter, r *http.Request, g node.UiNodeGroup, a *Flow, s *session.Session) error\n\t}\n\n\tHooksProvider interface {\n\t\tPreLoginHooks(ctx context.Context) ([]PreHookExecutor, error)\n\t\tPostLoginHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]PostHookExecutor, error)\n\t}\n)\n\ntype (\n\texecutorDependencies interface {\n\t\tconfig.Provider\n\t\thydra.Provider\n\t\tidentity.PrivilegedPoolProvider\n\t\tidentity.ManagementProvider\n\t\tsession.ManagementProvider\n\t\tsession.PersistenceProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\t\tsessiontokenexchange.PersistenceProvider\n\t\tHandlerProvider\n\n\t\tFlowPersistenceProvider\n\t\tHooksProvider\n\t\tStrategyProvider\n\t}\n\tHookExecutor struct {\n\t\td executorDependencies\n\t}\n\tHookExecutorProvider interface {\n\t\tLoginHookExecutor() *HookExecutor\n\t}\n)\n\nfunc PostHookExecutorNames(e []PostHookExecutor) []string {\n\tnames := make([]string, len(e))\n\tfor k, ee := range e {\n\t\tnames[k] = fmt.Sprintf(\"%T\", ee)\n\t}\n\treturn names\n}\n\nfunc NewHookExecutor(d executorDependencies) *HookExecutor {\n\treturn &HookExecutor{d: d}\n}\n\nfunc (e *HookExecutor) checkAAL(ctx context.Context, s *session.Session, a *Flow) error {\n\terr := e.d.SessionManager().DoesSessionSatisfy(ctx, s, e.d.Config().SessionWhoAmIAAL(ctx))\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif aalErr := new(session.ErrAALNotSatisfied); errors.As(err, &aalErr) {\n\t\tif a != nil && aalErr.PassReturnToAndLoginChallengeParameters(a.RequestURL) != nil {\n\t\t\t_ = aalErr.WithDetail(\"pass_request_params_error\", \"failed to pass request parameters to aalErr.RedirectTo\")\n\t\t}\n\t\treturn aalErr\n\t}\n\n\treturn err\n}\n\nfunc (e *HookExecutor) handleLoginError(_ http.ResponseWriter, r *http.Request, g node.UiNodeGroup, f *Flow, i *identity.Identity, flowError error) error {\n\tif f != nil {\n\t\tif i != nil {\n\t\t\tcont, err := container.NewFromStruct(\"\", g, i.Traits, \"traits\")\n\t\t\tif err != nil {\n\t\t\t\te.d.Logger().WithError(err).Warn(\"could not update flow UI\")\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tfor _, n := range cont.Nodes {\n\t\t\t\t// we only set the value and not the whole field because we want to keep types from the initial form generation\n\t\t\t\tf.UI.Nodes.SetValueAttribute(n.ID(), n.Attributes.GetValue())\n\t\t\t}\n\t\t}\n\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(e.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn flowError\n}\n\nfunc (e *HookExecutor) PostLoginHook(\n\tw http.ResponseWriter,\n\tr *http.Request,\n\tg node.UiNodeGroup,\n\tf *Flow,\n\ti *identity.Identity,\n\ts *session.Session,\n\tprovider string,\n) (err error) {\n\tctx := r.Context()\n\tctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, \"HookExecutor.PostLoginHook\")\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\t// We need to set the identity here because we check the available AAL in maybeLinkCredentials.\n\ts.IdentityID = i.ID\n\ts.Identity = i\n\n\tif err := e.maybeLinkCredentials(ctx, s, i, f); err != nil {\n\t\treturn err\n\t}\n\n\tif err := e.d.SessionManager().ActivateSession(r, s, i, time.Now().UTC()); err != nil {\n\t\treturn err\n\t}\n\n\tc := e.d.Config()\n\t// Verify the redirect URL before we do any other processing.\n\treturnTo, err := redir.SecureRedirectTo(r,\n\t\tc.SelfServiceBrowserDefaultReturnTo(ctx),\n\t\tredir.SecureRedirectReturnTo(f.ReturnTo),\n\t\tredir.SecureRedirectUseSourceURL(f.RequestURL),\n\t\tredir.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)),\n\t\tredir.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowLoginReturnTo(ctx, f.Active.String())),\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tspan.SetAttributes(otelx.StringAttrs(map[string]string{\n\t\t\"return_to\":       returnTo.String(),\n\t\t\"flow_type\":       string(flow.TypeBrowser),\n\t\t\"redirect_reason\": \"login successful\",\n\t})...)\n\n\tif f.Type == flow.TypeBrowser && x.IsJSONRequest(r) {\n\t\tf.AddContinueWith(flow.NewContinueWithRedirectBrowserTo(returnTo.String()))\n\t}\n\n\tclassified := s\n\ts = s.Declassified()\n\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tWithField(\"flow_method\", f.Active).\n\t\tDebug(\"Running ExecuteLoginPostHook.\")\n\thooks, err := e.d.PostLoginHooks(ctx, f.Active)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k, executor := range hooks {\n\t\tif err := executor.ExecuteLoginPostHook(w, r, g, f, s); err != nil {\n\t\t\tif errors.Is(err, ErrHookAbortFlow) {\n\t\t\t\te.d.Logger().\n\t\t\t\t\tWithRequest(r).\n\t\t\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\t\t\tWithField(\"executor_position\", k).\n\t\t\t\t\tWithField(\"executors\", PostHookExecutorNames(hooks)).\n\t\t\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\t\t\tWithField(\"flow_method\", f.Active).\n\t\t\t\t\tDebug(\"A ExecuteLoginPostHook hook aborted early.\")\n\n\t\t\t\tspan.SetAttributes(attribute.String(\"redirect_reason\", \"aborted by hook\"), attribute.String(\"executor\", fmt.Sprintf(\"%T\", executor)))\n\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn e.handleLoginError(w, r, g, f, i, err)\n\t\t}\n\n\t\te.d.Logger().\n\t\t\tWithRequest(r).\n\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\tWithField(\"executor_position\", k).\n\t\t\tWithField(\"executors\", PostHookExecutorNames(hooks)).\n\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\tWithField(\"flow_method\", f.Active).\n\t\t\tDebug(\"ExecuteLoginPostHook completed successfully.\")\n\t}\n\n\tif f.Type == flow.TypeAPI {\n\t\tspan.SetAttributes(attribute.String(\"flow_type\", string(flow.TypeAPI)))\n\t\tif err := e.d.SessionPersister().UpsertSession(ctx, s); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\te.d.Logger().\n\t\t\tWithRequest(r).\n\t\t\tWithField(\"session_id\", s.ID).\n\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\tInfo(\"Identity authenticated successfully and was issued an Ory Kratos Session Token.\")\n\n\t\tspan.AddEvent(events.NewLoginSucceeded(ctx, &events.LoginSucceededOpts{\n\t\t\tSessionID:    s.ID,\n\t\t\tIdentityID:   i.ID,\n\t\t\tFlowID:       f.ID,\n\t\t\tFlowType:     string(f.Type),\n\t\t\tRequestedAAL: string(f.RequestedAAL),\n\t\t\tIsRefresh:    f.Refresh,\n\t\t\tMethod:       f.Active.String(),\n\t\t\tSSOProvider:  provider,\n\t\t}))\n\t\tif f.IDToken != \"\" {\n\t\t\t// We don't want to redirect with the code, if the flow was submitted with an ID token.\n\t\t\t// This is the case for Sign in with native Apple SDK or Google SDK.\n\t\t} else if handled, err := e.d.SessionManager().MaybeRedirectAPICodeFlow(w, r, f, s.ID, g); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t} else if handled {\n\t\t\treturn nil\n\t\t}\n\n\t\tresponse := &APIFlowResponse{\n\t\t\tSession:      s,\n\t\t\tToken:        s.Token,\n\t\t\tContinueWith: f.ContinueWith(),\n\t\t}\n\t\tif e.checkAAL(ctx, classified, f) != nil {\n\t\t\t// If AAL is not satisfied, we omit the identity to preserve the user's privacy in case of a phishing attack.\n\t\t\tresponse.Session.Identity = nil\n\t\t}\n\n\t\te.d.Writer().Write(w, r, response)\n\t\treturn nil\n\t}\n\n\tif err := e.d.SessionManager().UpsertAndIssueCookie(ctx, w, r, s); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tWithField(\"session_id\", s.ID).\n\t\tInfo(\"Identity authenticated successfully and was issued an Ory Kratos Session Cookie.\")\n\n\tspan.AddEvent(events.NewLoginSucceeded(ctx, &events.LoginSucceededOpts{\n\t\tSessionID:  s.ID,\n\t\tFlowID:     f.ID,\n\t\tIdentityID: i.ID, FlowType: string(f.Type), RequestedAAL: string(f.RequestedAAL), IsRefresh: f.Refresh, Method: f.Active.String(),\n\t\tSSOProvider: provider,\n\t}))\n\n\tif x.IsJSONRequest(r) {\n\t\tspan.SetAttributes(attribute.String(\"flow_type\", \"spa\"))\n\n\t\t// Browser flows rely on cookies. Adding tokens in the mix will confuse consumers.\n\t\ts.Token = \"\"\n\n\t\t// If we detect that whoami would require a higher AAL, we redirect!\n\t\tif err := e.checkAAL(ctx, classified, f); err != nil {\n\t\t\tif aalErr := new(session.ErrAALNotSatisfied); errors.As(err, &aalErr) {\n\t\t\t\tif data, _ := flow.DuplicateCredentials(f); data == nil {\n\t\t\t\t\tspan.SetAttributes(attribute.String(\"return_to\", aalErr.RedirectTo), attribute.String(\"redirect_reason\", \"requires aal2\"))\n\t\t\t\t\te.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(aalErr.RedirectTo))\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\t// Special case: If we are in a flow that wants to link credentials, we create a\n\t\t\t\t// new login flow here that asks for the require AAL, but also copies over the\n\t\t\t\t// internal context and the organization ID.\n\t\t\t\tr.URL, err = url.Parse(aalErr.RedirectTo)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\t\t\t\tnewFlow, _, err := e.d.LoginHandler().NewLoginFlow(w, r, flow.TypeBrowser,\n\t\t\t\t\tWithInternalContext(f.InternalContext),\n\t\t\t\t\tWithOrganizationID(f.OrganizationID),\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.WithStack(err)\n\t\t\t\t}\n\n\t\t\t\tx.SendFlowCompletedAsRedirectOrJSON(w, r, e.d.Writer(), newFlow, newFlow.AppendTo(e.d.Config().SelfServiceFlowLoginUI(ctx)).String())\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\t\t// If Kratos is used as a Hydra login provider, we need to redirect back to Hydra by returning a 422 status\n\t\t// with the post login challenge URL as the body.\n\t\tif f.OAuth2LoginChallenge != \"\" {\n\t\t\tpostChallengeURL, err := e.d.Hydra().AcceptLoginRequest(ctx,\n\t\t\t\thydra.AcceptLoginRequestParams{\n\t\t\t\t\tLoginChallenge:        string(f.OAuth2LoginChallenge),\n\t\t\t\t\tIdentityID:            i.ID.String(),\n\t\t\t\t\tSessionID:             s.ID.String(),\n\t\t\t\t\tAuthenticationMethods: s.AMR,\n\t\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tspan.SetAttributes(attribute.String(\"return_to\", postChallengeURL), attribute.String(\"redirect_reason\", \"oauth2 login challenge\"))\n\t\t\te.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(postChallengeURL))\n\t\t\treturn nil\n\t\t}\n\n\t\tresponse := &APIFlowResponse{\n\t\t\tSession:      s,\n\t\t\tContinueWith: f.ContinueWith(),\n\t\t}\n\t\te.d.Writer().Write(w, r, response)\n\t\treturn nil\n\t}\n\n\t// If we detect that whoami would require a higher AAL, we redirect!\n\tif err := e.checkAAL(ctx, classified, f); err != nil {\n\t\tif aalErr := new(session.ErrAALNotSatisfied); errors.As(err, &aalErr) {\n\t\t\tif data, _ := flow.DuplicateCredentials(f); data == nil {\n\t\t\t\thttp.Redirect(w, r, aalErr.RedirectTo, http.StatusSeeOther)\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\t// Special case: If we are in a flow that wants to link credentials, we create a\n\t\t\t// new login flow here that asks for the require AAL, but also copies over the\n\t\t\t// internal context and the organization ID.\n\t\t\tr.URL, err = url.Parse(aalErr.RedirectTo)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\t\t\tnewFlow, _, err := e.d.LoginHandler().NewLoginFlow(w, r, flow.TypeBrowser,\n\t\t\t\tWithInternalContext(f.InternalContext),\n\t\t\t\tWithOrganizationID(f.OrganizationID),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\n\t\t\tx.SendFlowCompletedAsRedirectOrJSON(w, r, e.d.Writer(), newFlow, newFlow.AppendTo(e.d.Config().SelfServiceFlowLoginUI(ctx)).String())\n\t\t\treturn nil\n\t\t}\n\t\treturn errors.WithStack(err)\n\t}\n\n\tfinalReturnTo := returnTo.String()\n\tif f.OAuth2LoginChallenge != \"\" {\n\t\trt, err := e.d.Hydra().AcceptLoginRequest(ctx,\n\t\t\thydra.AcceptLoginRequestParams{\n\t\t\t\tLoginChallenge:        string(f.OAuth2LoginChallenge),\n\t\t\t\tIdentityID:            i.ID.String(),\n\t\t\t\tSessionID:             s.ID.String(),\n\t\t\t\tAuthenticationMethods: s.AMR,\n\t\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfinalReturnTo = rt\n\t\tspan.SetAttributes(attribute.String(\"return_to\", rt), attribute.String(\"redirect_reason\", \"oauth2 login challenge\"))\n\t} else if f.ReturnToVerification != \"\" {\n\t\tfinalReturnTo = f.ReturnToVerification\n\t\tspan.SetAttributes(attribute.String(\"redirect_reason\", \"verification requested\"))\n\t}\n\n\tredir.ContentNegotiationRedirection(w, r, s, e.d.Writer(), finalReturnTo)\n\treturn nil\n}\n\nfunc (e *HookExecutor) PreLoginHook(w http.ResponseWriter, r *http.Request, a *Flow) error {\n\thooks, err := e.d.PreLoginHooks(r.Context())\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, h := range hooks {\n\t\tif err := h.ExecuteLoginPreHook(w, r, a); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// maybeLinkCredentials links the identity with the credentials of the inner context of the login flow.\nfunc (e *HookExecutor) maybeLinkCredentials(ctx context.Context, sess *session.Session, ident *identity.Identity, loginFlow *Flow) (err error) {\n\tctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, \"HookExecutor.PostLoginHook.maybeLinkCredentials\")\n\tdefer otelx.End(span, &err)\n\n\tif e.checkAAL(ctx, sess, loginFlow) != nil {\n\t\t// we don't yet want to link credentials because the required AAL is not satisfied\n\t\treturn nil\n\t}\n\n\tlc, err := flow.DuplicateCredentials(loginFlow)\n\tif err != nil {\n\t\treturn err\n\t} else if lc == nil {\n\t\treturn nil\n\t}\n\n\tif err = e.checkDuplicateCredentialsIdentifierMatch(ctx, ident, lc.DuplicateIdentifier); err != nil {\n\t\treturn err\n\t}\n\tstrategy, err := e.d.AllLoginStrategies().Strategy(lc.CredentialsType)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tlinkableStrategy, ok := strategy.(LinkableStrategy)\n\tif !ok {\n\t\t// This should never happen because we check for this in the registration flow.\n\t\treturn errors.Errorf(\"strategy is not linkable: %T\", linkableStrategy)\n\t}\n\n\tif err := linkableStrategy.Link(ctx, ident, lc.CredentialsConfig); err != nil {\n\t\treturn err\n\t}\n\n\tif err = linkableStrategy.CompletedLogin(sess, lc); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (e *HookExecutor) checkDuplicateCredentialsIdentifierMatch(ctx context.Context, i *identity.Identity, match string) error {\n\tif len(i.Credentials) == 0 {\n\t\tif err := e.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, i, identity.ExpandCredentials); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor _, credentials := range i.Credentials {\n\t\tfor _, identifier := range credentials.Identifiers {\n\t\t\tif identifier == match {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\treturn schema.NewLinkedCredentialsDoNotMatch()\n}\n"
  },
  {
    "path": "selfservice/flow/login/hook_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/contextx\"\n)\n\nfunc TestLoginExecutor(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tfor _, strategy := range identity.AllCredentialTypes {\n\t\tt.Run(\"strategy=\"+strategy.String(), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t\t\treg.SetHydra(hydra.NewFake())\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/login.schema.json\")\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, returnToServer.URL)\n\t\t\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\n\t\t\tnewServer := func(t *testing.T, ft flow.Type, useIdentity *identity.Identity, flowCallback ...func(*login.Flow)) *httptest.Server {\n\t\t\t\trouter := http.NewServeMux()\n\n\t\t\t\trouter.HandleFunc(\"GET /login/pre\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t\tloginFlow, err := login.NewFlow(conf, time.Minute, \"\", r, ft)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tif testhelpers.SelfServiceHookLoginErrorHandler(t, w, r, reg.LoginHookExecutor().PreLoginHook(w, r, loginFlow)) {\n\t\t\t\t\t\t_, _ = w.Write([]byte(\"ok\"))\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\trouter.HandleFunc(\"GET /login/post\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t\tloginFlow, err := login.NewFlow(conf, time.Minute, \"\", r, ft)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tloginFlow.Active = strategy\n\t\t\t\t\tloginFlow.RequestURL = x.RequestURL(r).String()\n\t\t\t\t\tfor _, cb := range flowCallback {\n\t\t\t\t\t\tcb(loginFlow)\n\t\t\t\t\t}\n\n\t\t\t\t\tsess := session.NewInactiveSession()\n\t\t\t\t\tsess.CompletedLoginFor(identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\tif useIdentity == nil {\n\t\t\t\t\t\tuseIdentity = testhelpers.SelfServiceHookCreateFakeIdentity(t, reg)\n\t\t\t\t\t}\n\n\t\t\t\t\ttesthelpers.SelfServiceHookLoginErrorHandler(t, w, r,\n\t\t\t\t\t\treg.LoginHookExecutor().PostLoginHook(w, r, strategy.ToUiNodeGroup(), loginFlow, useIdentity, sess, \"\"))\n\t\t\t\t})\n\n\t\t\t\trouter.HandleFunc(\"GET /login/post2fa\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t\tloginFlow, err := login.NewFlow(conf, time.Minute, \"\", r, ft)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tloginFlow.Active = strategy\n\t\t\t\t\tloginFlow.RequestURL = x.RequestURL(r).String()\n\t\t\t\t\tfor _, cb := range flowCallback {\n\t\t\t\t\t\tcb(loginFlow)\n\t\t\t\t\t}\n\n\t\t\t\t\tsess := session.NewInactiveSession()\n\t\t\t\t\tsess.CompletedLoginFor(identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\tsess.CompletedLoginFor(identity.CredentialsTypeTOTP, identity.AuthenticatorAssuranceLevel2)\n\t\t\t\t\tif useIdentity == nil {\n\t\t\t\t\t\tuseIdentity = testhelpers.SelfServiceHookCreateFakeIdentity(t, reg)\n\t\t\t\t\t}\n\n\t\t\t\t\ttesthelpers.SelfServiceHookLoginErrorHandler(t, w, r,\n\t\t\t\t\t\treg.LoginHookExecutor().PostLoginHook(w, r, strategy.ToUiNodeGroup(), loginFlow, useIdentity, sess, \"\"))\n\t\t\t\t})\n\n\t\t\t\tts := httptest.NewServer(router)\n\t\t\t\tt.Cleanup(ts.Close)\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)\n\t\t\t\treturn ts\n\t\t\t}\n\n\t\t\tmakeRequestPost := testhelpers.SelfServiceMakeLoginPostHookRequest\n\t\t\tviperSetPost := testhelpers.SelfServiceHookLoginViperSetPost\n\n\t\t\tt.Run(\"method=PostLoginHook\", func(t *testing.T) {\n\t\t\t\tt.Run(\"case=pass without hooks\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL, res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass without hooks if client is ajax\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tts := newServer(t, flow.TypeBrowser, nil)\n\t\t\t\t\tres, body := makeRequestPost(t, ts, true, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), ts.URL)\n\t\t\t\t\tassert.JSONEq(t, fmt.Sprintf(`[{\"action\":\"redirect_browser_to\",\"redirect_browser_to\":\"%s\"}]`, returnToServer.URL), gjson.Get(body, \"continue_with\").Raw)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass if hooks pass\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tviperSetPost(t, conf, strategy.String(), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, returnToServer.URL, res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=fail if hooks fail\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tviperSetPost(t, conf, strategy.String(), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecuteLoginPostHook\": \"abort\"}`)}})\n\n\t\t\t\t\tts := newServer(t, flow.TypeBrowser, nil)\n\t\t\t\t\tres, body := makeRequestPost(t, ts, false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), ts.URL)\n\t\t\t\t\tassert.Empty(t, body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=use return_to value\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToServer.URL})\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), false, url.Values{\"return_to\": {returnToServer.URL + \"/kratos\"}})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, returnToServer.URL+\"/kratos\", res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=use nested config value\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginAfter+\".\"+config.DefaultBrowserReturnURL, returnToServer.URL+\"/kratos\")\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL+\"/kratos\", res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=use nested config value\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\ttesthelpers.SelfServiceHookLoginSetDefaultRedirectTo(t, conf, returnToServer.URL+\"/not-kratos\")\n\t\t\t\t\ttesthelpers.SelfServiceHookLoginSetDefaultRedirectToStrategy(t, conf, strategy.String(), returnToServer.URL+\"/kratos\")\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL+\"/kratos\", res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass if hooks pass\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tviperSetPost(t, conf, strategy.String(), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL, res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=send a json response for API clients\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeAPI, nil), true, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session.identity.id\").String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"suite=handle login challenge with browser and application/json\", func(t *testing.T) {\n\t\t\t\t\tt.Run(\"case=includes the return_to address for a valid challenge\", func(t *testing.T) {\n\t\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\t\twithOAuthChallenge := func(f *login.Flow) {\n\t\t\t\t\t\t\tf.OAuth2LoginChallenge = hydra.FakeValidLoginChallenge\n\t\t\t\t\t\t}\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil, withOAuthChallenge), true, url.Values{})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusUnprocessableEntity, res.StatusCode, \"%s\", body)\n\t\t\t\t\t\tassert.Equal(t, hydra.FakePostLoginURL, gjson.Get(body, \"redirect_browser_to\").String(), \"%s\", body)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=returns an error for an invalid challenge\", func(t *testing.T) {\n\t\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\t\twithOAuthChallenge := func(f *login.Flow) {\n\t\t\t\t\t\t\tf.OAuth2LoginChallenge = hydra.FakeInvalidLoginChallenge\n\t\t\t\t\t\t}\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil, withOAuthChallenge), true, url.Values{})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusInternalServerError, res.StatusCode, \"%s\", body)\n\t\t\t\t\t\tassert.Equal(t, hydra.ErrFakeAcceptLoginRequestFailed.Error(), body, \"%s\", body)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass without hooks for browser flow with application/json\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, nil), true, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session.identity.id\").String())\n\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"session.token\").String())\n\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"session_token\").String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=work normally if AAL is satisfied\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tuseIdentity := &identity.Identity{Credentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypePassword: {Type: identity.CredentialsTypePassword},\n\t\t\t\t\t\tidentity.CredentialsTypeTOTP:     {Type: identity.CredentialsTypeTOTP},\n\t\t\t\t\t}}\n\t\t\t\t\trequire.NoError(t, reg.Persister().CreateIdentity(context.Background(), useIdentity))\n\n\t\t\t\t\tt.Run(\"browser client\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, useIdentity), false, url.Values{})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\t\tassert.EqualValues(t, returnToServer.URL, res.Request.URL.String())\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"api client returns the session with identity and the token\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeAPI, useIdentity), true, url.Values{})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session.identity\").String())\n\t\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session_token\").String())\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"browser JSON client returns the session with identity but not the token\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, useIdentity), true, url.Values{})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session.id\").String())\n\t\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session.identity\").String())\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"session_token\").String())\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=redirect to login if AAL is too low\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, \"highest_available\")\n\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\t\t})\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, \"https://hydra\")\n\n\t\t\t\t\tuseIdentity := &identity.Identity{Credentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypePassword: {Type: identity.CredentialsTypePassword, Config: []byte(`{\"hashed_password\": \"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`), Identifiers: []string{testhelpers.RandomEmail()}},\n\t\t\t\t\t\tidentity.CredentialsTypeWebAuthn: {Type: identity.CredentialsTypeWebAuthn, Config: []byte(`{\"credentials\":[{\"is_passwordless\":false}]}`), Identifiers: []string{testhelpers.RandomEmail()}},\n\t\t\t\t\t}}\n\t\t\t\t\trequire.NoError(t, reg.Persister().CreateIdentity(context.Background(), useIdentity))\n\n\t\t\t\t\tt.Run(\"browser client\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, useIdentity), false, url.Values{})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusNotFound, res.StatusCode, \"%s\", body)\n\t\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), \"/self-service/login/browser?aal=aal2\")\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"browser client with login challenge\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, useIdentity), false, url.Values{\n\t\t\t\t\t\t\t\"login_challenge\": []string{hydra.FakeValidLoginChallenge},\n\t\t\t\t\t\t})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusNotFound, res.StatusCode, \"%s\", body)\n\n\t\t\t\t\t\tassert.Equal(t, res.Request.URL.Path, \"/self-service/login/browser\")\n\t\t\t\t\t\tassert.Equal(t, res.Request.URL.Query().Get(\"aal\"), \"aal2\")\n\t\t\t\t\t\tassert.Equal(t, res.Request.URL.Query().Get(\"login_challenge\"), hydra.FakeValidLoginChallenge)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"api client returns the token and the session without the identity\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeAPI, useIdentity), true, url.Values{})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"session.identity\").String())\n\t\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session_token\").String())\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"browser JSON client\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, useIdentity), true, url.Values{})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusUnprocessableEntity, res.StatusCode, \"%s\", body)\n\t\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"redirect_browser_to\").String())\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"redirect_browser_to\").String(), \"/self-service/login/browser?aal=aal2\", \"%s\", body)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"browser JSON client with login challenge\", func(t *testing.T) {\n\t\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, useIdentity), true, url.Values{\n\t\t\t\t\t\t\t\"login_challenge\": []string{hydra.FakeValidLoginChallenge},\n\t\t\t\t\t\t})\n\t\t\t\t\t\trequire.EqualValuesf(t, http.StatusUnprocessableEntity, res.StatusCode, \"%s\", body)\n\t\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"redirect_browser_to\").String())\n\n\t\t\t\t\t\tredirectBrowserTo, err := url.Parse(gjson.Get(body, \"redirect_browser_to\").String())\n\t\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t\tassert.Equal(t, redirectBrowserTo.Path, \"/self-service/login/browser\")\n\t\t\t\t\t\tassert.Equal(t, redirectBrowserTo.Query().Get(\"aal\"), \"aal2\")\n\t\t\t\t\t\tassert.Equal(t, redirectBrowserTo.Query().Get(\"login_challenge\"), hydra.FakeValidLoginChallenge)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=maybe links credential\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\tconf.MustSet(ctx, \"selfservice.methods.totp.enabled\", true)\n\n\t\t\t\temail1, email2 := testhelpers.RandomEmail(), testhelpers.RandomEmail()\n\t\t\t\tpasswordOnlyIdentity := &identity.Identity{Credentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\tConfig:      []byte(`{\"hashed_password\": \"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`),\n\t\t\t\t\t\tIdentifiers: []string{email1},\n\t\t\t\t\t},\n\t\t\t\t}}\n\t\t\t\ttwoFAIdentitiy := &identity.Identity{Credentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\tConfig:      []byte(`{\"hashed_password\": \"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`),\n\t\t\t\t\t\tIdentifiers: []string{email2},\n\t\t\t\t\t},\n\t\t\t\t\tidentity.CredentialsTypeTOTP: {\n\t\t\t\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\t\t\t\tConfig:      []byte(`{\"totp_url\":\"otpauth://totp/test\"}`),\n\t\t\t\t\t\tIdentifiers: []string{email2},\n\t\t\t\t\t},\n\t\t\t\t}}\n\t\t\t\trequire.NoError(t, reg.Persister().CreateIdentity(ctx, passwordOnlyIdentity))\n\t\t\t\trequire.NoError(t, reg.Persister().CreateIdentity(ctx, twoFAIdentitiy))\n\n\t\t\t\tcredsOIDCPWOnly, err := identity.NewCredentialsOIDC(\n\t\t\t\t\t&identity.CredentialsOIDCEncryptedTokens{IDToken: \"id-token\", AccessToken: \"access-token\", RefreshToken: \"refresh-token\"},\n\t\t\t\t\t\"my-provider\",\n\t\t\t\t\temail1,\n\t\t\t\t\t\"\",\n\t\t\t\t)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tcredsOIDC2FA, err := identity.NewCredentialsOIDC(\n\t\t\t\t\t&identity.CredentialsOIDCEncryptedTokens{IDToken: \"id-token\", AccessToken: \"access-token\", RefreshToken: \"refresh-token\"},\n\t\t\t\t\t\"my-provider\",\n\t\t\t\t\temail2,\n\t\t\t\t\t\"\",\n\t\t\t\t)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tt.Run(\"sub-case=does not link after first factor when second factor is available\", func(t *testing.T) {\n\t\t\t\t\tduplicateCredentialsData := flow.DuplicateCredentialsData{\n\t\t\t\t\t\tCredentialsType:     identity.CredentialsTypeOIDC,\n\t\t\t\t\t\tCredentialsConfig:   credsOIDC2FA.Config,\n\t\t\t\t\t\tDuplicateIdentifier: email2,\n\t\t\t\t\t}\n\t\t\t\t\tts := newServer(t, flow.TypeBrowser, twoFAIdentitiy, func(l *login.Flow) {\n\t\t\t\t\t\trequire.NoError(t, flow.SetDuplicateCredentials(l, duplicateCredentialsData))\n\t\t\t\t\t})\n\t\t\t\t\tres, _ := makeRequestPost(t, ts, false, url.Values{})\n\n\t\t\t\t\tassert.Equal(t, reg.Config().SelfServiceFlowLoginUI(ctx).Host, res.Request.URL.Host)\n\t\t\t\t\tassert.Equal(t, reg.Config().SelfServiceFlowLoginUI(ctx).Path, res.Request.URL.Path)\n\t\t\t\t\tnewFlowID := res.Request.URL.Query().Get(\"flow\")\n\t\t\t\t\tassert.NotEmpty(t, newFlowID)\n\n\t\t\t\t\tnewFlow, err := reg.LoginFlowPersister().GetLoginFlow(ctx, uuid.Must(uuid.FromString(newFlowID)))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tnewFlowDuplicateCredentialsData, err := flow.DuplicateCredentials(newFlow)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t// Duplicate credentials data should have been copied over\n\t\t\t\t\tassert.Equal(t, duplicateCredentialsData.CredentialsType, newFlowDuplicateCredentialsData.CredentialsType)\n\t\t\t\t\tassert.Equal(t, duplicateCredentialsData.DuplicateIdentifier, newFlowDuplicateCredentialsData.DuplicateIdentifier)\n\t\t\t\t\tassert.JSONEq(t, string(duplicateCredentialsData.CredentialsConfig), string(newFlowDuplicateCredentialsData.CredentialsConfig))\n\n\t\t\t\t\t// AAL should be AAL2\n\t\t\t\t\tassert.Equal(t, identity.AuthenticatorAssuranceLevel2, newFlow.RequestedAAL)\n\n\t\t\t\t\t// TOTP nodes should be present\n\t\t\t\t\tfound := false\n\t\t\t\t\tfor _, n := range newFlow.UI.Nodes {\n\t\t\t\t\t\tif n.Group == node.TOTPGroup {\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tassert.True(t, found, \"could not find TOTP nodes in %+v\", newFlow.UI.Nodes)\n\n\t\t\t\t\tident, err := reg.Persister().GetIdentity(ctx, twoFAIdentitiy.ID, identity.ExpandCredentials)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Len(t, ident.Credentials, 2)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"sub-case=links after second factor when second factor is available\", func(t *testing.T) {\n\t\t\t\t\tts := newServer(t, flow.TypeBrowser, twoFAIdentitiy, func(l *login.Flow) {\n\t\t\t\t\t\trequire.NoError(t, flow.SetDuplicateCredentials(l, flow.DuplicateCredentialsData{\n\t\t\t\t\t\t\tCredentialsType:     identity.CredentialsTypeOIDC,\n\t\t\t\t\t\t\tCredentialsConfig:   credsOIDC2FA.Config,\n\t\t\t\t\t\t\tDuplicateIdentifier: email2,\n\t\t\t\t\t\t}))\n\t\t\t\t\t})\n\t\t\t\t\tres, body := testhelpers.SelfServiceMakeHookRequest(t, ts, \"/login/post2fa\", false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Equalf(t, returnToServer.URL, res.Request.URL.String(), \"%s\", body)\n\n\t\t\t\t\tident, err := reg.Persister().GetIdentity(ctx, twoFAIdentitiy.ID, identity.ExpandCredentials)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Len(t, ident.Credentials, 3)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"sub-case=links matching identity\", func(t *testing.T) {\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, passwordOnlyIdentity, func(l *login.Flow) {\n\t\t\t\t\t\trequire.NoError(t, flow.SetDuplicateCredentials(l, flow.DuplicateCredentialsData{\n\t\t\t\t\t\t\tCredentialsType:     identity.CredentialsTypeOIDC,\n\t\t\t\t\t\t\tCredentialsConfig:   credsOIDCPWOnly.Config,\n\t\t\t\t\t\t\tDuplicateIdentifier: email1,\n\t\t\t\t\t\t}))\n\t\t\t\t\t}), false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Equalf(t, returnToServer.URL, res.Request.URL.String(), \"%s\", body)\n\n\t\t\t\t\tident, err := reg.Persister().GetIdentity(ctx, passwordOnlyIdentity.ID, identity.ExpandCredentials)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Len(t, ident.Credentials, 2)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"sub-case=errors on non-matching identity\", func(t *testing.T) {\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, flow.TypeBrowser, passwordOnlyIdentity, func(l *login.Flow) {\n\t\t\t\t\t\trequire.NoError(t, flow.SetDuplicateCredentials(l, flow.DuplicateCredentialsData{\n\t\t\t\t\t\t\tCredentialsType:     identity.CredentialsTypeOIDC,\n\t\t\t\t\t\t\tCredentialsConfig:   credsOIDCPWOnly.Config,\n\t\t\t\t\t\t\tDuplicateIdentifier: \"wrong@example.com\",\n\t\t\t\t\t\t}))\n\t\t\t\t\t}), false, url.Values{})\n\t\t\t\t\trequire.EqualValues(t, http.StatusInternalServerError, res.StatusCode)\n\t\t\t\t\tassert.Equal(t, schema.NewLinkedCredentialsDoNotMatch().Error(), body, \"%s\", body)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tt.Run(\"method=PreLoginHook\", testhelpers.TestSelfServicePreHook(\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginBeforeHooks,\n\t\t\t\t\ttesthelpers.SelfServiceMakeLoginPreHookRequest,\n\t\t\t\t\tfunc(t *testing.T) *httptest.Server {\n\t\t\t\t\t\treturn newServer(t, flow.TypeAPI, nil)\n\t\t\t\t\t},\n\t\t\t\t\tconf,\n\t\t\t\t))\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tt.Run(\"method=PreLoginHook\", testhelpers.TestSelfServicePreHook(\n\t\t\t\t\tconfig.ViperKeySelfServiceLoginBeforeHooks,\n\t\t\t\t\ttesthelpers.SelfServiceMakeLoginPreHookRequest,\n\t\t\t\t\tfunc(t *testing.T) *httptest.Server {\n\t\t\t\t\t\treturn newServer(t, flow.TypeBrowser, nil)\n\t\t\t\t\t},\n\t\t\t\t\tconf,\n\t\t\t\t))\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"method=checkAAL\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]interface{}{\n\t\t\tconfig.ViperKeyPublicBaseURL:                     returnToServer.URL,\n\t\t\tconfig.ViperKeySelfServiceBrowserDefaultReturnTo: returnToServer.URL,\n\t\t}), configx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/login.schema.json\")))\n\n\t\tt.Run(\"returns no error when sufficient\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySessionWhoAmIAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\tassert.NoError(t,\n\t\t\t\tlogin.CheckAALForTest(ctx, reg.LoginHookExecutor(), &session.Session{\n\t\t\t\t\tAMR: session.AuthenticationMethods{{\n\t\t\t\t\t\tMethod: identity.CredentialsTypePassword,\n\t\t\t\t\t\tAAL:    identity.AuthenticatorAssuranceLevel1,\n\t\t\t\t\t}},\n\t\t\t\t\tAuthenticatorAssuranceLevel: identity.AuthenticatorAssuranceLevel1,\n\t\t\t\t}, nil),\n\t\t\t)\n\n\t\t\tctx = contextx.WithConfigValue(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\tassert.NoError(t,\n\t\t\t\tlogin.CheckAALForTest(ctx, reg.LoginHookExecutor(), &session.Session{\n\t\t\t\t\tAMR: session.AuthenticationMethods{{\n\t\t\t\t\t\tMethod: identity.CredentialsTypePassword,\n\t\t\t\t\t\tAAL:    identity.AuthenticatorAssuranceLevel1,\n\t\t\t\t\t}, {\n\t\t\t\t\t\tMethod: identity.CredentialsTypeLookup,\n\t\t\t\t\t\tAAL:    identity.AuthenticatorAssuranceLevel2,\n\t\t\t\t\t}},\n\t\t\t\t\tAuthenticatorAssuranceLevel: identity.AuthenticatorAssuranceLevel2,\n\t\t\t\t}, nil),\n\t\t\t)\n\t\t})\n\n\t\tt.Run(\"copies parameters to redirect URL when AAL is not sufficient\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\taalErr := new(session.ErrAALNotSatisfied)\n\t\t\trequire.ErrorAs(t,\n\t\t\t\tlogin.CheckAALForTest(ctx, reg.LoginHookExecutor(), &session.Session{\n\t\t\t\t\tAMR: session.AuthenticationMethods{{\n\t\t\t\t\t\tMethod: identity.CredentialsTypePassword,\n\t\t\t\t\t\tAAL:    identity.AuthenticatorAssuranceLevel1,\n\t\t\t\t\t}},\n\t\t\t\t\tAuthenticatorAssuranceLevel: identity.AuthenticatorAssuranceLevel1,\n\t\t\t\t\tIdentity: &identity.Identity{\n\t\t\t\t\t\tInternalAvailableAAL: identity.NullableAuthenticatorAssuranceLevel{\n\t\t\t\t\t\t\tNullString: sql.NullString{String: string(identity.AuthenticatorAssuranceLevel2), Valid: true},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}, &login.Flow{\n\t\t\t\t\tRequestURL: \"https://www.ory.sh/?return_to=https://www.ory.sh/kratos&login_challenge=challenge\",\n\t\t\t\t}),\n\t\t\t\t&aalErr,\n\t\t\t)\n\t\t\tassert.Equal(t, returnToServer.URL+\"/self-service/login/browser?aal=aal2&login_challenge=challenge&return_to=https%3A%2F%2Fwww.ory.sh%2Fkratos\", aalErr.RedirectTo)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/login/organizations.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n)\n\nvar organizationFilter = []StrategyFilter{func(s Strategy) bool {\n\ta, b := s.(flow.OrganizationImplementor)\n\treturn b && a.SupportsOrganizations()\n}}\n\nfunc PrepareOrganizations(r *http.Request, f *Flow, sess *session.Session) []StrategyFilter {\n\tif f.RequestedAAL != identity.AuthenticatorAssuranceLevel1 {\n\t\treturn []StrategyFilter{}\n\t}\n\n\tif f.OrganizationID.Valid {\n\t\treturn organizationFilter\n\t}\n\n\torgID := flow.ParseOrganizationFromURLQuery(r.Context(), r.URL.Query())\n\tif sess != nil && sess.Identity != nil && sess.Identity.OrganizationID.Valid {\n\t\torgID = sess.Identity.OrganizationID\n\t}\n\n\tif !orgID.Valid {\n\t\treturn []StrategyFilter{}\n\t}\n\n\tf.OrganizationID = orgID\n\t// We only apply the filter on AAL1, because the OIDC strategy can only satsify\n\t// AAL1.\n\treturn organizationFilter\n}\n"
  },
  {
    "path": "selfservice/flow/login/organizations_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n)\n\nfunc TestPrepareOrganizations(t *testing.T) {\n\tt.Run(\"should return empty filter if AAL is not AAL1\", func(t *testing.T) {\n\t\tf := &Flow{RequestedAAL: identity.AuthenticatorAssuranceLevel2}\n\t\tr := &http.Request{URL: new(url.URL)}\n\t\tsess := &session.Session{}\n\n\t\tfilters := PrepareOrganizations(r, f, sess)\n\t\tassert.Empty(t, filters)\n\t})\n\n\tt.Run(\"should return empty filter if OrganizationID is not set\", func(t *testing.T) {\n\t\tf := &Flow{RequestedAAL: identity.AuthenticatorAssuranceLevel1}\n\t\tr := &http.Request{URL: new(url.URL)}\n\t\tsess := &session.Session{}\n\n\t\tfilters := PrepareOrganizations(r, f, sess)\n\t\tassert.Empty(t, filters)\n\t})\n\n\tt.Run(\"should return organization filter if OrganizationID is valid\", func(t *testing.T) {\n\t\tf := &Flow{RequestedAAL: identity.AuthenticatorAssuranceLevel1}\n\t\tr := &http.Request{URL: new(url.URL)}\n\t\tsess := &session.Session{\n\t\t\tIdentity: &identity.Identity{OrganizationID: uuid.NullUUID{Valid: true}},\n\t\t}\n\n\t\tfilters := PrepareOrganizations(r, f, sess)\n\t\trequire.NotEmpty(t, filters)\n\t\tassert.Equal(t, organizationFilter, filters)\n\t})\n\n\tt.Run(\"should parse OrganizationID from URL query if not in session\", func(t *testing.T) {\n\t\tf := &Flow{RequestedAAL: identity.AuthenticatorAssuranceLevel1}\n\t\tr := &http.Request{\n\t\t\tURL: &url.URL{\n\t\t\t\tRawQuery: \"organization=123e4567-e89b-12d3-a456-426614174000\",\n\t\t\t},\n\t\t}\n\t\tsess := &session.Session{}\n\t\tfilters := PrepareOrganizations(r, f, sess)\n\t\trequire.NotEmpty(t, filters)\n\t\tassert.Equal(t, organizationFilter, filters)\n\t})\n\n\tt.Run(\"should use organization ID already in flow\", func(t *testing.T) {\n\t\torgID := uuid.NullUUID{UUID: uuid.Must(uuid.NewV4()), Valid: true}\n\t\tf := &Flow{\n\t\t\tRequestedAAL:   identity.AuthenticatorAssuranceLevel1,\n\t\t\tOrganizationID: orgID,\n\t\t}\n\t\tr := &http.Request{URL: new(url.URL)}\n\t\tsess := &session.Session{}\n\n\t\tfilters := PrepareOrganizations(r, f, sess)\n\t\trequire.NotEmpty(t, filters)\n\t\tassert.Equal(t, organizationFilter, filters)\n\t\tassert.Equal(t, orgID, f.OrganizationID)\n\t})\n\n\tt.Run(\"should prioritize session org ID over query param when both present\", func(t *testing.T) {\n\t\tsessionOrgID := uuid.Must(uuid.NewV4())\n\t\tqueryOrgID := uuid.Must(uuid.NewV4())\n\n\t\tf := &Flow{RequestedAAL: identity.AuthenticatorAssuranceLevel1}\n\t\tr := &http.Request{\n\t\t\tURL: &url.URL{\n\t\t\t\tRawQuery: \"organization=\" + queryOrgID.String(),\n\t\t\t},\n\t\t}\n\t\tsess := &session.Session{\n\t\t\tIdentity: &identity.Identity{\n\t\t\t\tOrganizationID: uuid.NullUUID{UUID: sessionOrgID, Valid: true},\n\t\t\t},\n\t\t}\n\n\t\tfilters := PrepareOrganizations(r, f, sess)\n\t\trequire.NotEmpty(t, filters)\n\t\tassert.Equal(t, organizationFilter, filters)\n\t\tassert.Equal(t, sessionOrgID, f.OrganizationID.UUID)\n\t})\n\n\tt.Run(\"should not return filter when organization is set but AAL2 requested\", func(t *testing.T) {\n\t\torgID := uuid.NullUUID{UUID: uuid.Must(uuid.NewV4()), Valid: true}\n\t\tf := &Flow{\n\t\t\tRequestedAAL:   identity.AuthenticatorAssuranceLevel2,\n\t\t\tOrganizationID: orgID,\n\t\t}\n\t\tr := &http.Request{URL: new(url.URL)}\n\t\tsess := &session.Session{}\n\n\t\tfilters := PrepareOrganizations(r, f, sess)\n\t\tassert.Empty(t, filters)\n\t\t// Organization ID should remain set in the flow\n\t\tassert.Equal(t, orgID, f.OrganizationID)\n\t})\n\n\tt.Run(\"should use organization ID from query when session has no org ID\", func(t *testing.T) {\n\t\tqueryOrgID := uuid.Must(uuid.NewV4())\n\n\t\tf := &Flow{RequestedAAL: identity.AuthenticatorAssuranceLevel1}\n\t\tr := &http.Request{\n\t\t\tURL: &url.URL{\n\t\t\t\tRawQuery: \"organization=\" + queryOrgID.String(),\n\t\t\t},\n\t\t}\n\t\tsess := &session.Session{\n\t\t\tIdentity: &identity.Identity{\n\t\t\t\t// No organization ID set\n\t\t\t\tOrganizationID: uuid.NullUUID{Valid: false},\n\t\t\t},\n\t\t}\n\n\t\tfilters := PrepareOrganizations(r, f, sess)\n\t\trequire.NotEmpty(t, filters)\n\t\tassert.Equal(t, organizationFilter, filters)\n\t\tassert.Equal(t, queryOrgID, f.OrganizationID.UUID)\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/login/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype (\n\tFlowPersister interface {\n\t\tUpdateLoginFlow(context.Context, *Flow) error\n\t\tCreateLoginFlow(context.Context, *Flow) error\n\t\tGetLoginFlow(context.Context, uuid.UUID) (*Flow, error)\n\t\tForceLoginFlow(ctx context.Context, id uuid.UUID) error\n\t\tDeleteExpiredLoginFlows(context.Context, time.Time, int) error\n\t}\n\tFlowPersistenceProvider interface {\n\t\tLoginFlowPersister() FlowPersister\n\t}\n)\n"
  },
  {
    "path": "selfservice/flow/login/session.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n)\n\n// The Response for Login Flows via API\n//\n// swagger:model successfulNativeLogin\ntype APIFlowResponse struct {\n\t// The Session Token\n\t//\n\t// A session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\n\t// Header:\n\t//\n\t// \t\tAuthorization: bearer ${session-token}\n\t//\n\t// The session token is only issued for API flows, not for Browser flows!\n\tToken string `json:\"session_token,omitempty\"`\n\n\t// The Session\n\t//\n\t// The session contains information about the user, the session device, and so on.\n\t// This is only available for API flows, not for Browser flows!\n\t//\n\t// required: true\n\tSession *session.Session `json:\"session\"`\n\n\t// Contains a list of actions, that could follow this flow\n\t//\n\t// It can, for example, this will contain a reference to the verification flow, created as part of the user's\n\t// registration or the token of the session.\n\t//\n\t// required: false\n\tContinueWith []flow.ContinueWith `json:\"continue_with\"`\n}\n"
  },
  {
    "path": "selfservice/flow/login/sort.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc sortNodes(ctx context.Context, n node.Nodes) error {\n\treturn n.SortBySchema(ctx,\n\t\tnode.SortByGroups([]node.UiNodeGroup{\n\t\t\tnode.OpenIDConnectGroup,\n\t\t\tnode.DefaultGroup,\n\t\t\tnode.WebAuthnGroup,\n\t\t\tnode.PasskeyGroup,\n\t\t\tnode.CodeGroup,\n\t\t\tnode.PasswordGroup,\n\t\t\tnode.TOTPGroup,\n\t\t\tnode.LookupGroup,\n\t\t}),\n\t\tnode.SortUseOrder([]string{\n\t\t\t\"csrf_token\",\n\t\t\t\"identifier\",\n\t\t\t\"password\",\n\t\t}),\n\t)\n}\n"
  },
  {
    "path": "selfservice/flow/login/state.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport \"github.com/ory/kratos/selfservice/flow\"\n\n// Login Flow State\n//\n// The state represents the state of the login flow.\n//\n// - choose_method: ask the user to choose a method (e.g. login account via email)\n// - sent_email: the email has been sent to the user\n// - passed_challenge: the request was successful and the login challenge was passed.\n//\n// swagger:model loginFlowState\ntype State = flow.State\n"
  },
  {
    "path": "selfservice/flow/login/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\ntype Strategy interface {\n\tID() identity.CredentialsType\n\tNodeGroup() node.UiNodeGroup\n\tLogin(w http.ResponseWriter, r *http.Request, f *Flow, sess *session.Session) (i *identity.Identity, err error)\n\tCompletedAuthenticationMethod(ctx context.Context) session.AuthenticationMethod\n}\n\ntype Strategies []Strategy\n\ntype LinkableStrategy interface {\n\tLink(ctx context.Context, i *identity.Identity, credentials sqlxx.JSONRawMessage) error\n\tCompletedLogin(sess *session.Session, data *flow.DuplicateCredentialsData) error\n\tSetDuplicateCredentials(f flow.InternalContexter, duplicateIdentifier string, credentials identity.Credentials, provider string) error\n}\n\ntype FastLoginStrategy interface {\n\tFastLogin1FA(w http.ResponseWriter, r *http.Request, f *Flow, sess *session.Session) (err error)\n\tFastLogin2FA(w http.ResponseWriter, r *http.Request, f *Flow, sess *session.Session) (err error)\n}\n\nfunc (s Strategies) Strategy(id identity.CredentialsType) (Strategy, error) {\n\tids := make([]identity.CredentialsType, len(s))\n\tfor k, ss := range s {\n\t\tids[k] = ss.ID()\n\t\tif ss.ID() == id {\n\t\t\treturn ss, nil\n\t\t}\n\t}\n\n\treturn nil, errors.Errorf(`unable to find strategy for %s have %v`, id, ids)\n}\n\nfunc (s Strategies) MustStrategy(id identity.CredentialsType) Strategy {\n\tstrategy, err := s.Strategy(id)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn strategy\n}\n\ntype StrategyFilter func(strategy Strategy) bool\n\ntype StrategyProvider interface {\n\tAllLoginStrategies() Strategies\n\tLoginStrategies(ctx context.Context, filters ...StrategyFilter) Strategies\n}\n"
  },
  {
    "path": "selfservice/flow/login/strategy_form_hydrator.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\tstderr \"errors\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n)\n\ntype UnifiedFormHydrator interface {\n\tID() identity.CredentialsType\n\tPopulateLoginMethod(r *http.Request, requestedAAL identity.AuthenticatorAssuranceLevel, sr *Flow) error\n}\n\ntype AAL1FormHydrator interface {\n\tID() identity.CredentialsType\n\tPopulateLoginMethodFirstFactorRefresh(r *http.Request, sr *Flow, sess *session.Session) error\n\tPopulateLoginMethodFirstFactor(r *http.Request, sr *Flow) error\n\n\t// PopulateLoginMethodIdentifierFirstCredentials populates the login form with the first factor credentials.\n\t// This method is called when the login flow is set to identifier first. The method will receive information\n\t// about the identity that is being used to log in and the identifier that was used to find the identity.\n\t//\n\t// The method should populate the login form with the credentials of the identity.\n\t//\n\t// If the method can not find any credentials (because the identity does not exist) idfirst.ErrNoCredentialsFound\n\t// must be returned. When returning  idfirst.ErrNoCredentialsFound the strategy will appropriately deal with\n\t// account enumeration mitigation.\n\t//\n\t// This method does however need to take appropriate steps to show/hide certain fields depending on the account\n\t// enumeration configuration.\n\tPopulateLoginMethodIdentifierFirstCredentials(r *http.Request, sr *Flow, options ...FormHydratorModifier) error\n\tPopulateLoginMethodIdentifierFirstIdentification(r *http.Request, sr *Flow) error\n}\n\ntype AAL2FormHydrator interface {\n\tID() identity.CredentialsType\n\tPopulateLoginMethodSecondFactor(r *http.Request, sr *Flow) error\n\tPopulateLoginMethodSecondFactorRefresh(r *http.Request, sr *Flow) error\n}\n\nvar ErrBreakLoginPopulate = stderr.New(\"skip rest of login form population\")\n\ntype FormHydratorOptions struct {\n\tIdentityHint *identity.Identity\n\tIdentifier   string\n}\n\ntype FormHydratorModifier func(o *FormHydratorOptions)\n\nfunc WithIdentityHint(i *identity.Identity) FormHydratorModifier {\n\treturn func(o *FormHydratorOptions) {\n\t\to.IdentityHint = i\n\t}\n}\n\nfunc WithIdentifier(i string) FormHydratorModifier {\n\treturn func(o *FormHydratorOptions) {\n\t\to.Identifier = i\n\t}\n}\n\nfunc NewFormHydratorOptions(modifiers []FormHydratorModifier) *FormHydratorOptions {\n\to := new(FormHydratorOptions)\n\tfor _, m := range modifiers {\n\t\tm(o)\n\t}\n\treturn o\n}\n"
  },
  {
    "path": "selfservice/flow/login/strategy_form_hydrator_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\nfunc TestWithIdentityHint(t *testing.T) {\n\texpected := new(identity.Identity)\n\topts := NewFormHydratorOptions([]FormHydratorModifier{WithIdentityHint(expected)})\n\tassert.Equal(t, expected, opts.IdentityHint)\n}\n\nfunc TestWithIdentifier(t *testing.T) {\n\texpected := \"identifier\"\n\topts := NewFormHydratorOptions([]FormHydratorModifier{WithIdentifier(expected)})\n\tassert.Equal(t, expected, opts.Identifier)\n}\n"
  },
  {
    "path": "selfservice/flow/login/stub/email.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/email.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"title\": \"E-Mail Address\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/login/stub/fake-session.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/login/stub/login.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/login/stub/password.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"username\": {\n          \"type\": \"string\",\n          \"title\": \"Username\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/login/stub/phone.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/phone.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"phone\": {\n          \"type\": \"string\",\n          \"title\": \"Phone Number\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/login/stub/registration.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/login/stub/updated.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/login/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc TestFlowPersister(ctx context.Context, p persistence.Persister) func(t *testing.T) {\n\tvar clearids = func(r *login.Flow) {\n\t\tr.ID = uuid.UUID{}\n\t}\n\n\treturn func(t *testing.T) {\n\t\t_, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tt.Run(\"case=should error when the login flow does not exist\", func(t *testing.T) {\n\t\t\t_, err := p.GetLoginFlow(ctx, x.NewUUID())\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tvar newFlow = func(t *testing.T) *login.Flow {\n\t\t\tvar r login.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\tclearids(&r)\n\t\t\treturn &r\n\t\t}\n\n\t\tt.Run(\"case=should create with set ids\", func(t *testing.T) {\n\t\t\tvar r login.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\trequire.NoError(t, p.CreateLoginFlow(ctx, &r))\n\t\t})\n\n\t\tt.Run(\"case=should create a new login flow and properly set IDs\", func(t *testing.T) {\n\t\t\tr := newFlow(t)\n\t\t\terr := p.CreateLoginFlow(ctx, r)\n\t\t\trequire.NoError(t, err, \"%#v\", err)\n\t\t\tassert.NotEqual(t, uuid.Nil, r.ID)\n\t\t})\n\n\t\tt.Run(\"case=should create and fetch a login flow\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\terr := p.CreateLoginFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactual, err := p.GetLoginFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.EqualValues(t, expected.ID, actual.ID)\n\t\t\tx.AssertEqualTime(t, expected.IssuedAt, actual.IssuedAt)\n\t\t\tx.AssertEqualTime(t, expected.ExpiresAt, actual.ExpiresAt)\n\t\t\tassert.EqualValues(t, expected.RequestURL, actual.RequestURL)\n\t\t\tassert.EqualValues(t, expected.Active, actual.Active)\n\t\t\tassertx.EqualAsJSON(t, expected.UI, actual.UI, \"expected:\\t%s\\nactual:\\t%s\", expected.UI, actual.UI)\n\t\t})\n\n\t\tt.Run(\"case=should properly set the flow type\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\texpected.Refresh = true\n\t\t\texpected.Type = flow.TypeAPI\n\t\t\texpected.UI = container.New(\"ory-sh\")\n\n\t\t\terr := p.CreateLoginFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactual, err := p.GetLoginFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, flow.TypeAPI, actual.Type)\n\n\t\t\tactual.UI = container.New(\"not-ory-sh\")\n\t\t\tactual.Type = flow.TypeBrowser\n\t\t\tactual.Refresh = true\n\n\t\t\trequire.NoError(t, p.UpdateLoginFlow(ctx, actual))\n\n\t\t\tactual, err = p.GetLoginFlow(ctx, actual.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, flow.TypeBrowser, actual.Type)\n\t\t\tassert.True(t, actual.Refresh)\n\t\t\tassert.Equal(t, \"not-ory-sh\", actual.UI.Action)\n\t\t})\n\n\t\tt.Run(\"case=should not cause data loss when updating a request without changes\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\terr := p.CreateLoginFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactual, err := p.GetLoginFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.NoError(t, p.UpdateLoginFlow(ctx, actual))\n\n\t\t\tactual, err = p.GetLoginFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassertx.EqualAsJSON(t, expected.UI, actual.UI)\n\t\t})\n\n\t\tt.Run(\"case=should create and update and properly deal with internal context\", func(t *testing.T) {\n\t\t\tfor k, tc := range []struct {\n\t\t\t\tin     []byte\n\t\t\t\texpect string\n\t\t\t}{\n\t\t\t\t{in: []byte(\"[]\"), expect: \"{}\"},\n\t\t\t\t{expect: \"{}\"},\n\t\t\t\t{in: []byte(\"null\"), expect: \"{}\"},\n\t\t\t\t{in: []byte(`{\"foo\":\"bar\"}`), expect: `{\"foo\":\"bar\"}`},\n\t\t\t} {\n\t\t\t\tt.Run(fmt.Sprintf(\"run=%d\", k), func(t *testing.T) {\n\t\t\t\t\tr := newFlow(t)\n\t\t\t\t\tr.InternalContext = tc.in\n\t\t\t\t\trequire.NoError(t, p.CreateLoginFlow(ctx, r))\n\t\t\t\t\tassert.Equal(t, tc.expect, string(r.InternalContext))\n\n\t\t\t\t\tr.InternalContext = tc.in\n\t\t\t\t\trequire.NoError(t, p.UpdateLoginFlow(ctx, r))\n\t\t\t\t\tassert.Equal(t, tc.expect, string(r.InternalContext))\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=network\", func(t *testing.T) {\n\t\t\tid := x.NewUUID()\n\t\t\tnid, p := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\tt.Run(\"sets id on creation\", func(t *testing.T) {\n\t\t\t\texpected := &login.Flow{ID: id, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour)}\n\t\t\t\trequire.NoError(t, p.CreateLoginFlow(ctx, expected))\n\t\t\t\tassert.EqualValues(t, id, expected.ID)\n\t\t\t\tassert.EqualValues(t, nid, expected.NID)\n\n\t\t\t\tactual, err := p.GetLoginFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, id, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\t\t\t})\n\n\t\t\tt.Run(\"can not update id\", func(t *testing.T) {\n\t\t\t\texpected, err := p.GetLoginFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, p.UpdateLoginFlow(ctx, expected))\n\n\t\t\t\tactual, err := p.GetLoginFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, id, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\t\t\t})\n\n\t\t\tt.Run(\"can not force on another network\", func(t *testing.T) {\n\t\t\t\texpected, err := p.GetLoginFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\t\texpected.RequestURL = \"updated\"\n\t\t\t\trequire.Error(t, other.ForceLoginFlow(ctx, expected.ID))\n\n\t\t\t\t_, err = other.GetLoginFlow(ctx, id)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\tactual, err := p.GetLoginFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.False(t, actual.Refresh)\n\t\t\t})\n\n\t\t\tt.Run(\"can not update on another network\", func(t *testing.T) {\n\t\t\t\texpected, err := p.GetLoginFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\t\texpected.RequestURL = \"updated\"\n\t\t\t\trequire.Error(t, other.UpdateLoginFlow(ctx, expected))\n\n\t\t\t\t_, err = other.GetLoginFlow(ctx, id)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\tactual, err := p.GetLoginFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotEqual(t, \"updated\", actual.RequestURL)\n\t\t\t})\n\n\t\t\tt.Run(\"can not get on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetLoginFlow(ctx, id)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/login/testsetup_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage login_test\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"testing\"\n)\n\nvar returnToServer *httptest.Server\n\nfunc TestMain(m *testing.M) {\n\treturnToServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = w.Write([]byte(\"OK\"))\n\t}))\n\tos.Exit(m.Run())\n}\n"
  },
  {
    "path": "selfservice/flow/logout/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage logout\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n)\n\nconst (\n\tRouteInitBrowserFlow = \"/self-service/logout/browser\"\n\tRouteAPIFlow         = \"/self-service/logout/api\"\n\tRouteSubmitFlow      = \"/self-service/logout\"\n)\n\ntype (\n\tdependencies interface {\n\t\thttpx.WriterProvider\n\t\tnosurfx.CSRFProvider\n\t\tsession.ManagementProvider\n\t\tsession.PersistenceProvider\n\t\terrorx.ManagementProvider\n\t\tconfig.Provider\n\t}\n\tHandlerProvider interface {\n\t\tLogoutHandler() *Handler\n\t}\n\tHandler struct{ d dependencies }\n)\n\nfunc NewHandler(d dependencies) *Handler { return &Handler{d: d} }\n\nfunc (h *Handler) RegisterPublicRoutes(router *httprouterx.RouterPublic) {\n\th.d.CSRFHandler().IgnorePath(RouteAPIFlow)\n\n\trouter.GET(RouteInitBrowserFlow, h.createBrowserLogoutFlow)\n\trouter.DELETE(RouteAPIFlow, h.performNativeLogout)\n\trouter.GET(RouteSubmitFlow, h.updateLogoutFlow)\n}\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.DELETE(RouteAPIFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n}\n\n// Logout Flow\n//\n// swagger:model logoutFlow\ntype logoutFlow struct {\n\t// LogoutURL can be opened in a browser to sign the user out.\n\t//\n\t// format: uri\n\t// required: true\n\tLogoutURL string `json:\"logout_url\"`\n\n\t// LogoutToken can be used to perform logout using AJAX.\n\t//\n\t// required: true\n\tLogoutToken string `json:\"logout_token\"`\n}\n\n// Create Browser Logout Flow Parameters\n//\n// swagger:parameters createBrowserLogoutFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createBrowserLogoutFlow struct {\n\t// HTTP Cookies\n\t//\n\t// If you call this endpoint from a backend, please include the\n\t// original Cookie header in the request.\n\t//\n\t// in: header\n\t// name: cookie\n\tCookie string `json:\"cookie\"`\n\n\t// Return to URL\n\t//\n\t// The URL to which the browser should be redirected to after the logout\n\t// has been performed.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n}\n\n// swagger:route GET /self-service/logout/browser frontend createBrowserLogoutFlow\n//\n// # Create a Logout URL for Browsers\n//\n// This endpoint initializes a browser-based user logout flow and a URL which can be used to log out the user.\n//\n// This endpoint is NOT INTENDED for API clients and only works\n// with browsers (Chrome, Firefox, ...). For API clients you can\n// call the `/self-service/logout/api` URL directly with the Ory Session Token.\n//\n// The URL is only valid for the currently signed in user. If no user is signed in, this endpoint returns\n// a 401 error.\n//\n// When calling this endpoint from a backend, please ensure to properly forward the HTTP cookies.\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: logoutFlow\n//\t  400: errorGeneric\n//\t  401: errorGeneric\n//\t  500: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createBrowserLogoutFlow(w http.ResponseWriter, r *http.Request) {\n\tsess, err := h.d.SessionManager().FetchFromRequest(r.Context(), r)\n\tif err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tconf := h.d.Config()\n\n\trequestURL := x.RequestURL(r)\n\n\tvar returnTo *url.URL\n\n\tif requestURL.Query().Get(\"return_to\") != \"\" {\n\t\t// Pre-validate the return to URL which is contained in the HTTP request.\n\t\treturnTo, err = redir.SecureRedirectTo(r,\n\t\t\th.d.Config().SelfServiceFlowLogoutRedirectURL(r.Context()),\n\t\t\tredir.SecureRedirectUseSourceURL(requestURL.String()),\n\t\t\tredir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),\n\t\t\tredir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),\n\t\t)\n\t\tif err != nil {\n\t\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tparams := url.Values{\"token\": {sess.LogoutToken}}\n\n\tif returnTo != nil {\n\t\tparams.Set(\"return_to\", returnTo.String())\n\t}\n\n\th.d.Writer().Write(w, r, &logoutFlow{\n\t\tLogoutToken: sess.LogoutToken,\n\t\tLogoutURL:   urlx.CopyWithQuery(urlx.AppendPaths(h.d.Config().SelfPublicURL(r.Context()), RouteSubmitFlow), params).String(),\n\t})\n}\n\n// Perform Native Logout Parameters\n//\n// swagger:parameters performNativeLogout\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype performNativeLogout struct {\n\t// in: body\n\t// required: true\n\tBody performNativeLogoutBody\n}\n\n// Perform Native Logout Request Body\n//\n// swagger:model performNativeLogoutBody\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype performNativeLogoutBody struct {\n\t// The Session Token\n\t//\n\t// Invalidate this session token.\n\t//\n\t// required: true\n\tSessionToken string `json:\"session_token\"`\n}\n\n// swagger:route DELETE /self-service/logout/api frontend performNativeLogout\n//\n// # Perform Logout for Native Apps\n//\n// Use this endpoint to log out an identity using an Ory Session Token. If the Ory Session Token was successfully\n// revoked, the server returns a 204 No Content response. A 204 No Content response is also sent when\n// the Ory Session Token has been revoked already before.\n//\n// If the Ory Session Token is malformed or does not exist a 403 Forbidden response will be returned.\n//\n// This endpoint does not remove any HTTP\n// Cookies - use the Browser-Based Self-Service Logout Flow instead.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  204: emptyResponse\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) performNativeLogout(w http.ResponseWriter, r *http.Request) {\n\tvar p performNativeLogoutBody\n\tif err := decoderx.Decode(r, &p,\n\t\tdecoderx.HTTPJSONDecoder(),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"DELETE\")); err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\tsess, err := h.d.SessionPersister().GetSessionByToken(r.Context(), p.SessionToken, session.ExpandNothing, identity.ExpandNothing)\n\tif err != nil {\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\th.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrForbidden.WithReason(\"The provided Ory Session Token could not be found, is invalid, or otherwise malformed.\")))\n\t\t\treturn\n\t\t}\n\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif err := h.d.SessionPersister().RevokeSessionByToken(r.Context(), p.SessionToken); err != nil {\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\th.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrForbidden.WithReason(\"The provided Ory Session Token could not be found, is invalid, or otherwise malformed.\")))\n\t\t\treturn\n\t\t}\n\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewSessionRevoked(r.Context(), sess.ID, sess.IdentityID))\n\n\tw.WriteHeader(http.StatusNoContent)\n}\n\n// Update Logout Flow Parameters\n//\n// swagger:parameters updateLogoutFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateLogoutFlow struct {\n\t// A Valid Logout Token\n\t//\n\t// If you do not have a logout token because you only have a session cookie,\n\t// call `/self-service/logout/browser` to generate a URL for this endpoint.\n\t//\n\t// in: query\n\tToken string `json:\"token\"`\n\n\t// The URL to return to after the logout was completed.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// swagger:route GET /self-service/logout frontend updateLogoutFlow\n//\n// # Update Logout Flow\n//\n// This endpoint logs out an identity in a self-service manner.\n//\n// If the `Accept` HTTP header is not set to `application/json`, the browser will be redirected (HTTP 303 See Other)\n// to the `return_to` parameter of the initial request or fall back to `urls.default_return_to`.\n//\n// If the `Accept` HTTP header is set to `application/json`, a 204 No Content response\n// will be sent on successful logout instead.\n//\n// This endpoint is NOT INTENDED for API clients and only works\n// with browsers (Chrome, Firefox, ...). For API clients you can\n// call the `/self-service/logout/api` URL directly with the Ory Session Token.\n//\n// More information can be found at [Ory Kratos User Logout Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-logout).\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  303: emptyResponse\n//\t  204: emptyResponse\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-low\nfunc (h *Handler) updateLogoutFlow(w http.ResponseWriter, r *http.Request) {\n\texpected := r.URL.Query().Get(\"token\")\n\tif len(expected) == 0 {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrBadRequest.WithReason(\"Please include a token in the URL query.\")))\n\t\treturn\n\t}\n\n\tsess, err := h.d.SessionManager().FetchFromRequest(r.Context(), r)\n\tif err != nil {\n\t\t// We could handle `session.ErrNoActiveSessionFound` gracefully with `h.completeLogout()` here but that would\n\t\t// actually be an issue as it incorrectly indicates to clients that the session has been removed even if\n\t\t// `RevokeSessionByToken` has not actually been called.\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tif sess.LogoutToken != expected {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrForbidden.WithReason(\"Unable to log out because the logout token in the URL query does not match the session cookie.\")))\n\t\treturn\n\t}\n\n\tif err := h.d.SessionManager().PurgeFromRequest(r.Context(), w, r); err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewSessionRevoked(r.Context(), sess.ID, sess.IdentityID))\n\n\th.completeLogout(w, r)\n}\n\nfunc (h *Handler) completeLogout(w http.ResponseWriter, r *http.Request) {\n\t_ = h.d.CSRFHandler().RegenerateToken(w, r)\n\n\tret, err := redir.SecureRedirectTo(r, h.d.Config().SelfServiceFlowLogoutRedirectURL(r.Context()),\n\t\tredir.SecureRedirectUseSourceURL(r.RequestURI),\n\t\tredir.SecureRedirectAllowURLs(h.d.Config().SelfServiceBrowserAllowedReturnToDomains(r.Context())),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(h.d.Config().SelfPublicURL(r.Context())),\n\t)\n\tif err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tif x.IsJSONRequest(r) {\n\t\tw.WriteHeader(http.StatusNoContent)\n\t\treturn\n\t}\n\n\thttp.Redirect(w, r, ret.String(), http.StatusSeeOther)\n}\n"
  },
  {
    "path": "selfservice/flow/logout/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage logout_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/cookiejar\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/ory/kratos/session\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestLogout(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\tpublic, _, publicRouter, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\tpublicRouter.GET(\"/session/browser/set\", func(writer http.ResponseWriter, request *http.Request) {\n\t\ttesthelpers.MockSetSession(t, reg, conf)(writer, request)\n\t})\n\tpublicRouter.Handler(\"GET\", \"/session/browser/get\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tsess, err := reg.SessionManager().FetchFromRequest(r.Context(), r)\n\t\tif err != nil {\n\t\t\treg.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\t\treg.Writer().Write(w, r, sess)\n\t}))\n\tpublicRouter.Handler(\"POST\", \"/csrf/check\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusNoContent)\n\t}))\n\tconf.MustSet(ctx, config.ViperKeySelfServiceLogoutBrowserDefaultReturnTo, public.URL+\"/session/browser/get\")\n\n\tt.Run(\"case=successful logout for API clients\", func(t *testing.T) {\n\t\thc := testhelpers.NewDebugClient(t)\n\n\t\tsess := testhelpers.CreateSession(t, reg)\n\t\t_, res := testhelpers.HTTPRequestJSON(t, hc, \"DELETE\", public.URL+\"/self-service/logout/api\", json.RawMessage(`{\"session_token\": \"`+sess.Token+`\"}`))\n\t\tassert.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\tactual, err := reg.SessionPersister().GetSession(ctx, sess.ID, session.ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.False(t, actual.IsActive())\n\n\t\t// Retrying logout should not fail:\n\t\t_, res = testhelpers.HTTPRequestJSON(t, hc, \"DELETE\", public.URL+\"/self-service/logout/api\", json.RawMessage(`{\"session_token\": \"`+sess.Token+`\"}`))\n\t\tassert.Equal(t, http.StatusNoContent, res.StatusCode)\n\t})\n\n\tt.Run(\"case=unsuccessful logout for API clients because session token is invalid\", func(t *testing.T) {\n\t\thc := testhelpers.NewDebugClient(t)\n\n\t\t_, res := testhelpers.HTTPRequestJSON(t, hc, \"DELETE\", public.URL+\"/self-service/logout/api\", json.RawMessage(`{\"session_token\": \"not a valid token\"}`))\n\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t})\n\n\tt.Run(\"case=unsuccessful logout for API clients because session token is missing\", func(t *testing.T) {\n\t\thc := testhelpers.NewDebugClient(t)\n\n\t\t_, res := testhelpers.HTTPRequestJSON(t, hc, \"DELETE\", public.URL+\"/self-service/logout/api\", json.RawMessage(`{}`))\n\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t})\n\n\tmakeBrowserLogout := func(t *testing.T, hc *http.Client, u string) ([]byte, *http.Response) {\n\t\tres, err := hc.Get(u)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\treturn x.MustReadAll(res.Body), res\n\t}\n\n\tgetLogoutUrl := func(t *testing.T, params url.Values) (hc *http.Client, logoutUrl string) {\n\t\thc = testhelpers.NewSessionClient(t, public.URL+\"/session/browser/set\")\n\n\t\tu, err := url.Parse(public.URL)\n\t\trequire.NoError(t, err)\n\t\tu.Path = \"/self-service/logout/browser\"\n\t\tif params != nil {\n\t\t\tu.RawQuery = params.Encode()\n\t\t}\n\n\t\tbody, res := testhelpers.HTTPRequestJSON(t, hc, \"GET\", u.String(), nil)\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\tlogoutUrl = gjson.GetBytes(body, \"logout_url\").String()\n\t\tlogoutToken := gjson.GetBytes(body, \"logout_token\").String()\n\n\t\tlogoutUrlParsed, err := url.Parse(logoutUrl)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equalf(t, logoutUrlParsed.Query().Get(\"token\"), logoutToken, \"%s\", body)\n\t\tassert.Equalf(t, logoutUrlParsed.Path, \"/self-service/logout\", \"%s\", body)\n\t\treturn\n\t}\n\n\tt.Run(\"case=successful logout for browser clients\", func(t *testing.T) {\n\t\tensurePrincipalChange := func(t *testing.T, originalCookies []*http.Cookie) {\n\t\t\tcj, err := cookiejar.New(nil)\n\t\t\trequire.NoError(t, err)\n\t\t\tcj.SetCookies(urlx.ParseOrPanic(public.URL), originalCookies)\n\t\t\tres, err := (&http.Client{Jar: cj}).PostForm(public.URL+\"/csrf/check\", url.Values{})\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.EqualValues(t, http.StatusForbidden, res.StatusCode)\n\t\t\tbody := x.MustReadAll(res.Body)\n\t\t\tassert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\thc, logoutUrl := getLogoutUrl(t, nil)\n\t\t\toriginalCookies := hc.Jar.Cookies(urlx.ParseOrPanic(public.URL))\n\t\t\trequire.Contains(t, fmt.Sprintf(\"%v\", hc.Jar.Cookies(urlx.ParseOrPanic(public.URL))), \"ory_kratos_session\")\n\n\t\t\tbody, res := makeBrowserLogout(t, hc, logoutUrl)\n\t\t\tassert.EqualValues(t, public.URL+\"/session/browser/get\", res.Request.URL.String())\n\t\t\trequire.NotContains(t, fmt.Sprintf(\"%v\", hc.Jar.Cookies(urlx.ParseOrPanic(public.URL))), \"ory_kratos_session\")\n\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"No active session was found in this request.\", gjson.GetBytes(body, \"error.reason\").String())\n\n\t\t\t// Logout means CSRF has also been changed.\n\t\t\tensurePrincipalChange(t, originalCookies)\n\t\t})\n\n\t\tt.Run(\"type=ajax\", func(t *testing.T) {\n\t\t\thc, logoutUrl := getLogoutUrl(t, nil)\n\t\t\toriginalCookies := hc.Jar.Cookies(urlx.ParseOrPanic(public.URL))\n\t\t\trequire.Contains(t, fmt.Sprintf(\"%v\", hc.Jar.Cookies(urlx.ParseOrPanic(public.URL))), \"ory_kratos_session\")\n\n\t\t\tbody, res := testhelpers.HTTPRequestJSON(t, hc, \"GET\", logoutUrl, nil)\n\t\t\tassert.EqualValues(t, logoutUrl, res.Request.URL.String())\n\n\t\t\tassert.EqualValues(t, http.StatusNoContent, res.StatusCode, \"%s\", body)\n\t\t\trequire.NotContains(t, fmt.Sprintf(\"%v\", hc.Jar.Cookies(urlx.ParseOrPanic(public.URL))), \"ory_kratos_session\")\n\n\t\t\t// Logout means CSRF has also been changed.\n\t\t\tensurePrincipalChange(t, originalCookies)\n\t\t})\n\t})\n\n\tt.Run(\"case=calling submission without token but with session\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\thc := testhelpers.NewSessionClient(t, public.URL+\"/session/browser/set\")\n\t\t\tbody, res := makeBrowserLogout(t, hc, public.URL+\"/self-service/logout\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"Please include a token in the URL query.\", gjson.GetBytes(body, \"reason\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=ajax\", func(t *testing.T) {\n\t\t\thc := testhelpers.NewSessionClient(t, public.URL+\"/session/browser/set\")\n\t\t\tbody, res := testhelpers.HTTPRequestJSON(t, hc, \"GET\", public.URL+\"/self-service/logout\", nil)\n\t\t\tassert.EqualValues(t, public.URL+\"/self-service/logout\", res.Request.URL.String())\n\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"Please include a token in the URL query.\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=calling submission with token but without session\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t_, logoutUrl := getLogoutUrl(t, nil)\n\n\t\t\tbody, res := makeBrowserLogout(t, http.DefaultClient, logoutUrl)\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"No active session was found in this request.\", gjson.GetBytes(body, \"reason\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=ajax\", func(t *testing.T) {\n\t\t\t_, logoutUrl := getLogoutUrl(t, nil)\n\n\t\t\tbody, res := testhelpers.HTTPRequestJSON(t, http.DefaultClient, \"GET\", logoutUrl, nil)\n\t\t\tassert.EqualValues(t, logoutUrl, res.Request.URL.String())\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"No active session was found in this request.\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=calling submission with token but with session from another user\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\totherUser := testhelpers.NewSessionClient(t, public.URL+\"/session/browser/set\")\n\t\t\t_, logoutUrl := getLogoutUrl(t, nil)\n\n\t\t\tbody, res := makeBrowserLogout(t, otherUser, logoutUrl)\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"Unable to log out because the logout token in the URL query does not match the session cookie.\", gjson.GetBytes(body, \"reason\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=ajax\", func(t *testing.T) {\n\t\t\totherUser := testhelpers.NewSessionClient(t, public.URL+\"/session/browser/set\")\n\t\t\t_, logoutUrl := getLogoutUrl(t, nil)\n\n\t\t\tbody, res := testhelpers.HTTPRequestJSON(t, otherUser, \"GET\", logoutUrl, nil)\n\t\t\tassert.EqualValues(t, logoutUrl, res.Request.URL.String())\n\t\t\tassert.EqualValues(t, http.StatusForbidden, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"Unable to log out because the logout token in the URL query does not match the session cookie.\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=calling submission with invalid token\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\thc, logoutUrl := getLogoutUrl(t, nil)\n\n\t\t\tbody, res := makeBrowserLogout(t, hc, logoutUrl+\"invalid\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"Unable to log out because the logout token in the URL query does not match the session cookie.\", gjson.GetBytes(body, \"reason\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=ajax\", func(t *testing.T) {\n\t\t\thc, logoutUrl := getLogoutUrl(t, nil)\n\n\t\t\tbody, res := testhelpers.HTTPRequestJSON(t, hc, \"GET\", logoutUrl+\"invalid\", nil)\n\t\t\tassert.EqualValues(t, logoutUrl+\"invalid\", res.Request.URL.String())\n\t\t\tassert.EqualValues(t, http.StatusForbidden, res.StatusCode, \"%s\", body)\n\t\t\tassert.EqualValues(t, \"Unable to log out because the logout token in the URL query does not match the session cookie.\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=calling browser init without session\", func(t *testing.T) {\n\t\tbody, res := testhelpers.HTTPRequestJSON(t, http.DefaultClient, \"GET\", public.URL+\"/self-service/logout/browser\", nil)\n\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode)\n\t\tassert.EqualValues(t, \"No active session was found in this request.\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=init logout through browser does 303 redirect\", func(t *testing.T) {\n\t\t// init the logout\n\t\thc, logoutUrl := getLogoutUrl(t, nil)\n\t\t// prevent the redirect, so we can get check the status code\n\t\thc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t}\n\t\t// submit the login\n\t\treq, err := http.NewRequest(\"GET\", logoutUrl, nil)\n\t\trequire.NoError(t, err)\n\n\t\tres, err := hc.Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\t// here we check that the redirect status is 303\n\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t})\n\n\tt.Run(\"case=init logout with return_to should carry over return_to\", func(t *testing.T) {\n\t\treg.Config().MustSet(context.Background(), config.ViperKeyURLsAllowedReturnToDomains, []string{\"https://www.ory.sh\"})\n\n\t\thc, logoutUrl := getLogoutUrl(t, url.Values{\"return_to\": {\"https://www.ory.sh\"}})\n\n\t\tlogoutUrlParsed, err := url.Parse(logoutUrl)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, \"https://www.ory.sh\", logoutUrlParsed.Query().Get(\"return_to\"))\n\n\t\thc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t}\n\n\t\tbody, res := makeBrowserLogout(t, hc, logoutUrl)\n\t\tassert.EqualValues(t, http.StatusSeeOther, res.StatusCode, \"%s\", body)\n\t\tassert.EqualValues(t, \"https://www.ory.sh\", res.Header.Get(\"Location\"))\n\t})\n\n\tt.Run(\"case=init logout with return_to should not carry over return_to if not allowed\", func(t *testing.T) {\n\t\thc := testhelpers.NewSessionClient(t, public.URL+\"/session/browser/set\")\n\n\t\tlogoutUrl := public.URL + \"/self-service/logout/browser?return_to=https://www.ory.com\"\n\n\t\tr, err := http.NewRequest(\"GET\", logoutUrl, nil)\n\t\trequire.NoError(t, err)\n\t\tr.Header.Set(\"Accept\", \"application/json\")\n\n\t\tresp, err := hc.Do(r)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = resp.Body.Close() }()\n\n\t\tbody, err := io.ReadAll(resp.Body)\n\t\trequire.NoError(t, err)\n\n\t\tassert.EqualValues(t, \"Requested return_to URL \\\"https://www.ory.com\\\" is not allowed.\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\tassert.EqualValues(t, http.StatusBadRequest, resp.StatusCode, \"%s\", body)\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/logout/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/name.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\n// FlowName is the flow name.\n//\n// The flow name can be one of:\n// - 'login'\n// - 'registration'\n// - 'settings'\n// - 'recovery'\n// - 'verification'\n//\n// swagger:ignore\ntype FlowName string\n\nconst (\n\tLoginFlow        FlowName = \"login\"\n\tRegistrationFlow FlowName = \"registration\"\n\tSettingsFlow     FlowName = \"settings\"\n\tRecoveryFlow     FlowName = \"recovery\"\n\tVerificationFlow FlowName = \"verification\"\n)\n\nfunc (t Type) String() string {\n\treturn string(t)\n}\n"
  },
  {
    "path": "selfservice/flow/nosurf.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n)\n\nfunc GetCSRFToken(reg interface {\n\tnosurfx.CSRFProvider\n\tnosurfx.CSRFTokenGeneratorProvider\n}, w http.ResponseWriter, r *http.Request, p Type) string {\n\ttoken := reg.GenerateCSRFToken(r)\n\tif p != TypeBrowser {\n\t\treturn token\n\t}\n\n\tif token == \"\" {\n\t\treturn reg.CSRFHandler().RegenerateToken(w, r)\n\t}\n\n\treturn token\n}\n"
  },
  {
    "path": "selfservice/flow/nosurf_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/nosurf\"\n)\n\nfunc TestGetCSRFToken(t *testing.T) {\n\tnoToken := &mockReg{\n\t\tpresentToken:     \"\",\n\t\tregeneratedToken: \"regenerated\",\n\t}\n\n\ttokenPresent := &mockReg{\n\t\tpresentToken:     \"existing\",\n\t\tregeneratedToken: \"regenerated\",\n\t}\n\n\tt.Run(\"case=no token, browser flow\", func(t *testing.T) {\n\t\tassert.Equal(t, \"regenerated\", GetCSRFToken(noToken, nil, nil, TypeBrowser))\n\t})\n\n\tt.Run(\"case=token present, browser flow\", func(t *testing.T) {\n\t\tassert.Equal(t, \"existing\", GetCSRFToken(tokenPresent, nil, nil, TypeBrowser))\n\t})\n\n\tt.Run(\"case=no token, api flow\", func(t *testing.T) {\n\t\tassert.Equal(t, \"\", GetCSRFToken(noToken, nil, nil, TypeAPI))\n\t})\n\n\tt.Run(\"case=token present, api flow\", func(t *testing.T) {\n\t\tassert.Equal(t, \"existing\", GetCSRFToken(tokenPresent, nil, nil, TypeAPI))\n\t})\n}\n\ntype mockReg struct {\n\tpresentToken, regeneratedToken string\n\n\tnosurf.Handler\n}\n\nfunc (m *mockReg) GenerateCSRFToken(*http.Request) string {\n\treturn m.presentToken\n}\n\nfunc (m *mockReg) CSRFHandler() nosurf.Handler {\n\treturn m\n}\n\nfunc (m *mockReg) RegenerateToken(http.ResponseWriter, *http.Request) string {\n\treturn m.regeneratedToken\n}\n"
  },
  {
    "path": "selfservice/flow/organizations.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n)\n\n// ParseOrganizationFromURLQuery is a helper function that parses the organization ID for a self-service flow from\n// the URL query parameters. If the organization ID is not found in the URL query parameters, the function will return\n// an NULL UUID.\nfunc ParseOrganizationFromURLQuery(ctx context.Context, q url.Values) (orgID uuid.NullUUID) {\n\tif rawOrg := q.Get(\"organization\"); rawOrg != \"\" {\n\t\torgIDFromURL, err := uuid.FromString(rawOrg)\n\t\tif err != nil {\n\t\t\ttrace.SpanFromContext(ctx).RecordError(err, trace.WithAttributes(attribute.String(\"organization\", rawOrg)))\n\t\t} else {\n\t\t\torgID = uuid.NullUUID{UUID: orgIDFromURL, Valid: true}\n\t\t}\n\t}\n\treturn\n}\n\ntype OrganizationImplementor interface {\n\tSupportsOrganizations() bool\n}\n"
  },
  {
    "path": "selfservice/flow/organizations_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestParseOrganizationFromURLQuery(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tquery    url.Values\n\t\texpected uuid.NullUUID\n\t}{\n\t\t{\n\t\t\tname:     \"valid organization ID\",\n\t\t\tquery:    url.Values{\"organization\": {\"123e4567-e89b-12d3-a456-426614174000\"}},\n\t\t\texpected: uuid.NullUUID{UUID: uuid.FromStringOrNil(\"123e4567-e89b-12d3-a456-426614174000\"), Valid: true},\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid organization ID\",\n\t\t\tquery:    url.Values{\"organization\": {\"invalid-uuid\"}},\n\t\t\texpected: uuid.NullUUID{Valid: false},\n\t\t},\n\t\t{\n\t\t\tname:     \"missing organization ID\",\n\t\t\tquery:    url.Values{},\n\t\t\texpected: uuid.NullUUID{Valid: 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\tctx := context.Background()\n\t\t\tresult := ParseOrganizationFromURLQuery(ctx, tt.query)\n\t\t\tassert.Equal(t, tt.expected, result)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/.snapshots/TestHandleError-flow=api-case=fails_if_active_strategy_is_disabled.json",
    "content": "{\n  \"type\": \"api\",\n  \"request_url\": \"http:///\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"code\",\n        \"attributes\": {\n          \"name\": \"email\",\n          \"type\": \"email\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070007,\n            \"text\": \"Email\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"code\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"code\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 4000001,\n        \"text\": \"You attempted recovery using code, which is not enabled or does not exist. An administrator needs to enable this recovery method.\",\n        \"type\": \"error\",\n        \"context\": {\n          \"reason\": \"You attempted recovery using code, which is not enabled or does not exist. An administrator needs to enable this recovery method.\"\n        }\n      }\n    ]\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/.snapshots/TestHandleError-flow=browser-case=fails_to_retry_flow_if_recovery_strategy_id_is_not_valid.json",
    "content": "{\n  \"code\": 400,\n  \"message\": \"The request was malformed or contained invalid parameters\",\n  \"reason\": \"You attempted recovery using not-valid, which is not enabled or does not exist. An administrator needs to enable this recovery method.\",\n  \"status\": \"Bad Request\"\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/.snapshots/TestHandleError-flow=spa-case=fails_if_active_strategy_is_disabled.json",
    "content": "{\n  \"type\": \"browser\",\n  \"request_url\": \"http:///\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"code\",\n        \"attributes\": {\n          \"name\": \"email\",\n          \"type\": \"email\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070007,\n            \"text\": \"Email\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"code\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"code\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 4000001,\n        \"text\": \"You attempted recovery using code, which is not enabled or does not exist. An administrator needs to enable this recovery method.\",\n        \"type\": \"error\",\n        \"context\": {\n          \"reason\": \"You attempted recovery using code, which is not enabled or does not exist. An administrator needs to enable this recovery method.\"\n        }\n      }\n    ]\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/.snapshots/TestHandleError_WithContinueWith-flow=api-case=fails_if_active_strategy_is_disabled.json",
    "content": "{\n  \"type\": \"api\",\n  \"request_url\": \"http:///\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"code\",\n        \"attributes\": {\n          \"name\": \"email\",\n          \"type\": \"email\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070007,\n            \"text\": \"Email\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"code\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"code\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 4000001,\n        \"text\": \"You attempted recovery using code, which is not enabled or does not exist. An administrator needs to enable this recovery method.\",\n        \"type\": \"error\",\n        \"context\": {\n          \"reason\": \"You attempted recovery using code, which is not enabled or does not exist. An administrator needs to enable this recovery method.\"\n        }\n      }\n    ]\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/.snapshots/TestHandleError_WithContinueWith-flow=browser-case=fails_to_retry_flow_if_recovery_strategy_id_is_not_valid.json",
    "content": "{\n  \"code\": 400,\n  \"message\": \"The request was malformed or contained invalid parameters\",\n  \"reason\": \"You attempted recovery using not-valid, which is not enabled or does not exist. An administrator needs to enable this recovery method.\",\n  \"status\": \"Bad Request\"\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/.snapshots/TestHandleError_WithContinueWith-flow=spa-case=fails_if_active_strategy_is_disabled.json",
    "content": "{\n  \"type\": \"browser\",\n  \"request_url\": \"http:///\",\n  \"active\": \"link\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"code\",\n        \"attributes\": {\n          \"name\": \"email\",\n          \"type\": \"email\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070007,\n            \"text\": \"Email\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"code\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"code\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 4000001,\n        \"text\": \"You attempted recovery using code, which is not enabled or does not exist. An administrator needs to enable this recovery method.\",\n        \"type\": \"error\",\n        \"context\": {\n          \"reason\": \"You attempted recovery using code, which is not enabled or does not exist. An administrator needs to enable this recovery method.\"\n        }\n      }\n    ]\n  },\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n)\n\nvar (\n\tErrHookAbortFlow   = errors.New(\"aborted recovery hook execution\")\n\tErrAlreadyLoggedIn = herodot.ErrBadRequest.WithID(text.ErrIDAlreadyLoggedIn).WithReason(\"A valid session was detected and thus recovery is not possible.\")\n)\n\ntype (\n\terrorHandlerDependencies interface {\n\t\terrorx.ManagementProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tconfig.Provider\n\t\tStrategyProvider\n\n\t\tFlowPersistenceProvider\n\t}\n\n\tErrorHandlerProvider interface {\n\t\tRecoveryFlowErrorHandler() *ErrorHandler\n\t}\n\n\tErrorHandler struct {\n\t\td errorHandlerDependencies\n\t}\n)\n\nfunc NewErrorHandler(d errorHandlerDependencies) *ErrorHandler {\n\treturn &ErrorHandler{d: d}\n}\n\nfunc (s *ErrorHandler) WriteFlowError(\n\tw http.ResponseWriter,\n\tr *http.Request,\n\tf *Flow,\n\tgroup node.UiNodeGroup,\n\trecoveryErr error,\n) {\n\tlogger := s.d.Logger().\n\t\tWithError(recoveryErr).\n\t\tWithRequest(r).\n\t\tWithField(\"recovery_flow\", f.ToLoggerField())\n\n\tlogger.\n\t\tInfo(\"Encountered self-service recovery error.\")\n\n\tif f == nil {\n\t\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), uuid.Nil, \"\", \"\", recoveryErr))\n\t\ts.forward(w, r, nil, recoveryErr)\n\t\treturn\n\t}\n\n\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), f.ID, string(f.Type), f.Active.String(), recoveryErr))\n\n\tif expiredError := new(flow.ExpiredError); errors.As(recoveryErr, &expiredError) {\n\t\tstrategies, _, err := s.d.RecoveryStrategies(r.Context()).ActiveStrategies(f.Active.String())\n\t\tif err != nil {\n\t\t\tstrategies, _, err = s.d.GetActiveRecoveryStrategies(r.Context())\n\t\t\t// Can't retry the recovery if no primary strategy has been set\n\t\t\tif err != nil {\n\t\t\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// create new flow because the old one is not valid\n\t\tnewFlow, err := FromOldFlow(s.d.Config(), s.d.Config().SelfServiceFlowRecoveryRequestLifespan(r.Context()), s.d.GenerateCSRFToken(r), r, strategies, *f)\n\t\tif err != nil {\n\t\t\t// failed to create a new session and redirect to it, handle that error as a new one\n\t\t\ts.WriteFlowError(w, r, f, group, err)\n\t\t\treturn\n\t\t}\n\n\t\tnewFlow.UI.Messages.Add(text.NewErrorValidationRecoveryFlowExpired(expiredError.ExpiredAt))\n\t\tif err := s.d.RecoveryFlowPersister().CreateRecoveryFlow(r.Context(), newFlow); err != nil {\n\t\t\ts.forward(w, r, newFlow, err)\n\t\t\treturn\n\t\t}\n\n\t\tif s.d.Config().UseContinueWithTransitions(r.Context()) {\n\t\t\tswitch {\n\t\t\tcase newFlow.Type.IsAPI():\n\t\t\t\texpiredError.FlowID = newFlow.ID\n\t\t\t\ts.d.Writer().WriteError(w, r, expiredError.WithContinueWith(flow.NewContinueWithRecoveryUI(newFlow)))\n\t\t\tcase x.IsJSONRequest(r):\n\t\t\t\thttp.Redirect(w, r, urlx.CopyWithQuery(\n\t\t\t\t\turlx.AppendPaths(s.d.Config().SelfPublicURL(r.Context()), RouteGetFlow),\n\t\t\t\t\turl.Values{\"id\": {newFlow.ID.String()}},\n\t\t\t\t).String(), http.StatusSeeOther)\n\t\t\tdefault:\n\t\t\t\thttp.Redirect(w, r, newFlow.AppendTo(s.d.Config().SelfServiceFlowRecoveryUI(r.Context())).String(), http.StatusSeeOther)\n\t\t\t}\n\t\t} else {\n\t\t\ttrace.SpanFromContext(r.Context()).AddEvent(semconv.NewDeprecatedFeatureUsedEvent(r.Context(), \"no_continue_with_transition_recovery_error_handler\"))\n\n\t\t\t// We need to use the new flow, as that flow will be a browser flow. Bug fix for:\n\t\t\t//\n\t\t\t// https://github.com/ory/kratos/issues/2049!!\n\t\t\tif newFlow.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\t\t\thttp.Redirect(w, r, urlx.CopyWithQuery(urlx.AppendPaths(s.d.Config().SelfPublicURL(r.Context()),\n\t\t\t\t\tRouteGetFlow), url.Values{\"id\": {newFlow.ID.String()}}).String(), http.StatusSeeOther)\n\t\t\t} else {\n\t\t\t\thttp.Redirect(w, r, newFlow.AppendTo(s.d.Config().SelfServiceFlowRecoveryUI(r.Context())).String(), http.StatusSeeOther)\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tf.UI.ResetMessages()\n\tif err := f.UI.ParseError(group, recoveryErr); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tf.Active = sqlxx.NullString(group)\n\tif err := s.d.RecoveryFlowPersister().UpdateRecoveryFlow(r.Context(), f); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tif f.Type == flow.TypeBrowser && !x.IsJSONRequest(r) {\n\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowRecoveryUI(r.Context())).String(), http.StatusSeeOther)\n\t\treturn\n\t}\n\n\tupdatedFlow, innerErr := s.d.RecoveryFlowPersister().GetRecoveryFlow(r.Context(), f.ID)\n\tif innerErr != nil {\n\t\ts.forward(w, r, updatedFlow, innerErr)\n\t\treturn\n\t}\n\n\ts.d.Writer().WriteCode(w, r, x.RecoverStatusCode(recoveryErr, http.StatusBadRequest), updatedFlow)\n}\n\nfunc (s *ErrorHandler) forward(w http.ResponseWriter, r *http.Request, rr *Flow, err error) {\n\tif rr == nil {\n\t\tif x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tif rr.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteErrorCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), err)\n\t} else {\n\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/error_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery_test\n\nimport (\n\t\"context\"\n\t\"io\"\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/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/jsonx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestHandleError(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceRecoveryEnabled: true,\n\t\t\tconfig.ViperKeySelfServiceRecoveryUse:     \"code\",\n\t\t}),\n\t)\n\n\tpublic, _ := testhelpers.NewKratosServer(t, reg)\n\n\trouter := http.NewServeMux()\n\tts := httptest.NewServer(router)\n\tt.Cleanup(ts.Close)\n\n\ttesthelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\ttesthelpers.NewErrorTestServer(t, reg)\n\n\th := reg.RecoveryFlowErrorHandler()\n\tsdk := testhelpers.NewSDKClient(public)\n\n\tvar recoveryFlow *recovery.Flow\n\tvar flowError error\n\tvar methodName node.UiNodeGroup\n\trouter.HandleFunc(\"GET /error\", func(w http.ResponseWriter, r *http.Request) {\n\t\th.WriteFlowError(w, r, recoveryFlow, methodName, flowError)\n\t})\n\n\treset := func() {\n\t\trecoveryFlow = nil\n\t\tflowError = nil\n\t\tmethodName = \"\"\n\t}\n\n\tnewFlow := func(t *testing.T, ttl time.Duration, ft flow.Type) *recovery.Flow {\n\t\treq := &http.Request{URL: urlx.ParseOrPanic(\"/\")}\n\t\ts, _, err := reg.GetActiveRecoveryStrategies(context.Background())\n\t\trequire.NoError(t, err)\n\t\tf, err := recovery.NewFlow(conf, ttl, nosurfx.FakeCSRFToken, req, s, ft)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(context.Background(), f))\n\t\tf, err = reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), f.ID)\n\t\trequire.NoError(t, err)\n\t\treturn f\n\t}\n\n\texpectErrorUI := func(t *testing.T) (map[string]interface{}, *http.Response) {\n\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\trequire.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tsse, _, err := sdk.FrontendAPI.GetFlowError(context.Background()).Id(res.Request.URL.Query().Get(\"id\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\treturn sse.Error, nil\n\t}\n\n\tanHourAgo := time.Now().Add(-time.Hour)\n\n\tt.Run(\"case=error with nil flow defaults to error ui redirect\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\tsse, _ := expectErrorUI(t)\n\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t})\n\n\tt.Run(\"case=error with nil flow detects application/json\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tassert.Contains(t, res.Header.Get(\"Content-Type\"), \"application/json\")\n\t\tassert.NotContains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, string(body), \"system error\")\n\t})\n\n\tfor _, tc := range []struct {\n\t\tn string\n\t\tt flow.Type\n\t}{\n\t\t{\"api\", flow.TypeAPI},\n\t\t{\"spa\", flow.TypeBrowser},\n\t} {\n\t\tt.Run(\"flow=\"+tc.n, func(t *testing.T) {\n\t\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\trecoveryFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Contains(t, res.Request.URL.String(), public.URL+recovery.RouteGetFlow)\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode, \"%+v\", res.Request)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int(text.ErrorValidationRecoveryFlowExpired), int(gjson.GetBytes(body, \"ui.messages.0.id\").Int()), string(body))\n\t\t\t\tassert.NotEqual(t, recoveryFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\trecoveryFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(gjson.GetBytes(body, \"ui.messages.0.id\").Int()), \"%s\", body)\n\t\t\t\tassert.Equal(t, recoveryFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\trecoveryFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.JSONEq(t, x.MustEncodeJSON(t, flowError), gjson.GetBytes(body, \"error\").Raw)\n\t\t\t})\n\n\t\t\tt.Run(\"case=fails if active strategy is disabled\", func(t *testing.T) {\n\t\t\t\tc, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\trequire.NoError(t, c.Set(context.Background(), \"selfservice.methods.code.enabled\", false))\n\t\t\t\trequire.NoError(t, c.Set(context.Background(), config.ViperKeySelfServiceRecoveryUse, \"code\"))\n\t\t\t\t_, _, err := reg.GetActiveRecoveryStrategies(context.Background())\n\t\t\t\trecoveryFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = err\n\t\t\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"id\", \"expires_at\", \"issued_at\", \"ui.action\", \"ui.nodes.0.attributes.value\"))\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\texpectRecoveryUI := func(t *testing.T) (*recovery.Flow, *http.Response) {\n\t\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowRecoveryUI(ctx).String()+\"?flow=\")\n\n\t\t\trf, err := reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), uuid.FromStringOrNil(res.Request.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t\treturn rf, res\n\t\t}\n\n\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = &recovery.Flow{Type: flow.TypeBrowser}\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\tmethodName = node.LinkGroup\n\n\t\t\tlf, _ := expectRecoveryUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1, \"%s\", jsonx.TestMarshalJSONString(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationRecoveryFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t})\n\n\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\tmethodName = node.LinkGroup\n\n\t\t\tlf, _ := expectRecoveryUI(t)\n\t\t\trequire.NotEmpty(t, lf.UI, x.MustEncodeJSON(t, lf))\n\t\t\trequire.Len(t, lf.UI.Messages, 1, x.MustEncodeJSON(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(lf.UI.Messages[0].ID), x.MustEncodeJSON(t, lf))\n\t\t})\n\n\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\tmethodName = node.LinkGroup\n\n\t\t\tsse, _ := expectErrorUI(t)\n\t\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t\t})\n\n\t\tt.Run(\"case=new flow uses strategy of old flow\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = &recovery.Flow{Type: flow.TypeBrowser, Active: \"code\"}\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\n\t\t\tlf, _ := expectRecoveryUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1, \"%s\", jsonx.TestMarshalJSONString(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationRecoveryFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t\tassert.Equal(t, recoveryFlow.Active.String(), lf.Active.String())\n\t\t})\n\n\t\tt.Run(\"case=new flow uses current strategy if strategy of old flow does not exist\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = &recovery.Flow{Type: flow.TypeBrowser, Active: \"not-valid\"}\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\n\t\t\tlf, _ := expectRecoveryUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1, \"%s\", jsonx.TestMarshalJSONString(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationRecoveryFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t\tassert.Equal(t, \"code\", lf.Active.String())\n\t\t})\n\n\t\tt.Run(\"case=fails to retry flow if recovery strategy id is not valid\", func(t *testing.T) {\n\t\t\tt.Cleanup(func() {\n\t\t\t\treset()\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryUse, \"code\")\n\t\t\t})\n\n\t\t\trecoveryFlow = newFlow(t, 0, flow.TypeBrowser)\n\t\t\trecoveryFlow.Active = \"not-valid\"\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryUse, \"not-valid\")\n\t\t\tsse, _ := expectErrorUI(t)\n\t\t\tsnapshotx.SnapshotT(t, sse)\n\t\t})\n\t})\n}\n\nfunc TestHandleError_WithContinueWith(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeyUseContinueWithTransitions: true,\n\t\t\tconfig.ViperKeySelfServiceRecoveryEnabled: true,\n\t\t\tconfig.ViperKeySelfServiceRecoveryUse:     \"code\",\n\t\t}),\n\t)\n\n\tpublic, _ := testhelpers.NewKratosServer(t, reg)\n\n\trouter := http.NewServeMux()\n\tts := httptest.NewServer(router)\n\tt.Cleanup(ts.Close)\n\n\ttesthelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\ttesthelpers.NewErrorTestServer(t, reg)\n\n\th := reg.RecoveryFlowErrorHandler()\n\tsdk := testhelpers.NewSDKClient(public)\n\n\tvar recoveryFlow *recovery.Flow\n\tvar flowError error\n\tvar methodName node.UiNodeGroup\n\trouter.HandleFunc(\"GET /error\", func(w http.ResponseWriter, r *http.Request) {\n\t\th.WriteFlowError(w, r, recoveryFlow, methodName, flowError)\n\t})\n\n\treset := func() {\n\t\trecoveryFlow = nil\n\t\tflowError = nil\n\t\tmethodName = \"\"\n\t}\n\n\tnewFlow := func(t *testing.T, ttl time.Duration, ft flow.Type) *recovery.Flow {\n\t\treq := &http.Request{URL: urlx.ParseOrPanic(\"/\")}\n\t\ts, _, err := reg.GetActiveRecoveryStrategies(context.Background())\n\t\trequire.NoError(t, err)\n\t\tf, err := recovery.NewFlow(conf, ttl, nosurfx.FakeCSRFToken, req, s, ft)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(context.Background(), f))\n\t\tf, err = reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), f.ID)\n\t\trequire.NoError(t, err)\n\t\treturn f\n\t}\n\n\texpectErrorUI := func(t *testing.T) (map[string]interface{}, *http.Response) {\n\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\trequire.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tsse, _, err := sdk.FrontendAPI.GetFlowError(context.Background()).Id(res.Request.URL.Query().Get(\"id\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\treturn sse.Error, nil\n\t}\n\n\tanHourAgo := time.Now().Add(-time.Hour)\n\n\tt.Run(\"case=error with nil flow defaults to error ui redirect\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\tsse, _ := expectErrorUI(t)\n\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t})\n\n\tt.Run(\"case=error with nil flow detects application/json\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tassert.Contains(t, res.Header.Get(\"Content-Type\"), \"application/json\")\n\t\tassert.NotContains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, string(body), \"system error\")\n\t})\n\n\tfor _, tc := range []struct {\n\t\tn string\n\t\tt flow.Type\n\t}{\n\t\t{\"api\", flow.TypeAPI},\n\t\t{\"spa\", flow.TypeBrowser},\n\t} {\n\t\tt.Run(\"flow=\"+tc.n, func(t *testing.T) {\n\t\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\trecoveryFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\tswitch tc.t {\n\t\t\t\tcase flow.TypeAPI:\n\t\t\t\t\trequire.Equal(t, http.StatusGone, res.StatusCode, \"%s\", body)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"error.details.continue_with\").Array(), 1, \"%s\", body)\n\t\t\t\t\trequire.Equal(t, \"show_recovery_ui\", gjson.GetBytes(body, \"error.details.continue_with.0.action\").String(), \"%s\", body)\n\t\t\t\t\tid := gjson.GetBytes(body, \"error.details.continue_with.0.flow.id\").String()\n\t\t\t\t\tres, err = ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, public.URL+recovery.RouteGetFlow+\"?id=\"+id))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tbody = ioutilx.MustReadAll(res.Body)\n\t\t\t\tcase flow.TypeBrowser:\n\t\t\t\t\trequire.Contains(t, res.Request.URL.String(), public.URL+recovery.RouteGetFlow, \"%s\", body)\n\t\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode, \"%+v\", res.Request)\n\t\t\t\t}\n\t\t\t\tassert.Equal(t, int(text.ErrorValidationRecoveryFlowExpired), int(gjson.GetBytes(body, \"ui.messages.0.id\").Int()), string(body))\n\t\t\t\tassert.NotEqual(t, recoveryFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\trecoveryFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(gjson.GetBytes(body, \"ui.messages.0.id\").Int()), \"%s\", body)\n\t\t\t\tassert.Equal(t, recoveryFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\trecoveryFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.JSONEq(t, x.MustEncodeJSON(t, flowError), gjson.GetBytes(body, \"error\").Raw)\n\t\t\t})\n\n\t\t\tt.Run(\"case=fails if active strategy is disabled\", func(t *testing.T) {\n\t\t\t\tc, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\t\trequire.NoError(t, c.Set(context.Background(), \"selfservice.methods.code.enabled\", false))\n\t\t\t\trequire.NoError(t, c.Set(context.Background(), config.ViperKeySelfServiceRecoveryUse, \"code\"))\n\t\t\t\t_, _, err := reg.GetActiveRecoveryStrategies(context.Background())\n\t\t\t\trecoveryFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = err\n\t\t\t\tmethodName = node.UiNodeGroup(recovery.RecoveryStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"id\", \"expires_at\", \"issued_at\", \"ui.action\", \"ui.nodes.0.attributes.value\"))\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\texpectRecoveryUI := func(t *testing.T) (*recovery.Flow, *http.Response) {\n\t\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowRecoveryUI(ctx).String()+\"?flow=\")\n\n\t\t\trf, err := reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), uuid.FromStringOrNil(res.Request.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t\treturn rf, res\n\t\t}\n\n\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = &recovery.Flow{Type: flow.TypeBrowser}\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\tmethodName = node.LinkGroup\n\n\t\t\tlf, _ := expectRecoveryUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1, \"%s\", jsonx.TestMarshalJSONString(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationRecoveryFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t})\n\n\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\tmethodName = node.LinkGroup\n\n\t\t\tlf, _ := expectRecoveryUI(t)\n\t\t\trequire.NotEmpty(t, lf.UI, x.MustEncodeJSON(t, lf))\n\t\t\trequire.Len(t, lf.UI.Messages, 1, x.MustEncodeJSON(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(lf.UI.Messages[0].ID), x.MustEncodeJSON(t, lf))\n\t\t})\n\n\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\tmethodName = node.LinkGroup\n\n\t\t\tsse, _ := expectErrorUI(t)\n\t\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t\t})\n\n\t\tt.Run(\"case=new flow uses strategy of old flow\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = &recovery.Flow{Type: flow.TypeBrowser, Active: \"code\"}\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\n\t\t\tlf, _ := expectRecoveryUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1, \"%s\", jsonx.TestMarshalJSONString(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationRecoveryFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t\tassert.Equal(t, recoveryFlow.Active.String(), lf.Active.String())\n\t\t})\n\n\t\tt.Run(\"case=new flow uses current strategy if strategy of old flow does not exist\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\trecoveryFlow = &recovery.Flow{Type: flow.TypeBrowser, Active: \"not-valid\"}\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\n\t\t\tlf, _ := expectRecoveryUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1, \"%s\", jsonx.TestMarshalJSONString(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationRecoveryFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t\tassert.Equal(t, \"code\", lf.Active.String())\n\t\t})\n\n\t\tt.Run(\"case=fails to retry flow if recovery strategy id is not valid\", func(t *testing.T) {\n\t\t\tt.Cleanup(func() {\n\t\t\t\treset()\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryUse, \"code\")\n\t\t\t})\n\n\t\t\trecoveryFlow = newFlow(t, 0, flow.TypeBrowser)\n\t\t\trecoveryFlow.Active = \"not-valid\"\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryUse, \"not-valid\")\n\t\t\tsse, _ := expectErrorUI(t)\n\t\t\tsnapshotx.SnapshotT(t, sse)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/flow.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/x/redir\"\n\n\t\"github.com/ory/pop/v6\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\n// A Recovery Flow\n//\n// This request is used when an identity wants to recover their account.\n//\n// We recommend reading the [Account Recovery Documentation](../self-service/flows/password-reset-account-recovery)\n//\n// swagger:model recoveryFlow\ntype Flow struct {\n\t// ID represents the request's unique ID. When performing the recovery flow, this\n\t// represents the id in the recovery ui's query parameter: http://<selfservice.flows.recovery.ui_url>?request=<id>\n\t//\n\t// required: true\n\t// type: string\n\t// format: uuid\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// Type represents the flow's type which can be either \"api\" or \"browser\", depending on the flow interaction.\n\t//\n\t// required: true\n\tType flow.Type `json:\"type\" db:\"type\" faker:\"flow_type\"`\n\n\t// ExpiresAt is the time (UTC) when the request expires. If the user still wishes to update the setting,\n\t// a new request has to be initiated.\n\t//\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the request occurred.\n\t//\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used\n\t// to forward information contained in the URL's path or query for example.\n\t//\n\t// required: true\n\tRequestURL string `json:\"request_url\" db:\"request_url\"`\n\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo string `json:\"return_to,omitempty\" db:\"-\"`\n\n\t// Active, if set, contains the recovery method that is being used. It is initially\n\t// not set.\n\tActive sqlxx.NullString `json:\"active,omitempty\" faker:\"-\" db:\"active_method\"`\n\n\t// UI contains data which must be shown in the user interface.\n\t//\n\t// required: true\n\tUI *container.Container `json:\"ui\" db:\"ui\"`\n\n\t// State represents the state of this request:\n\t//\n\t// - choose_method: ask the user to choose a method (e.g. recover account via email)\n\t// - sent_email: the email has been sent to the user\n\t// - passed_challenge: the request was successful and the recovery challenge was passed.\n\t//\n\t// required: true\n\tState State `json:\"state\" faker:\"-\" db:\"state\"`\n\n\t// CSRFToken contains the anti-csrf token associated with this request.\n\tCSRFToken string `json:\"-\" db:\"csrf_token\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\n\t// RecoveredIdentityID is a helper struct field for gobuffalo.pop.\n\tRecoveredIdentityID uuid.NullUUID `json:\"-\" faker:\"-\" db:\"recovered_identity_id\"`\n\tNID                 uuid.UUID     `json:\"-\"  faker:\"-\" db:\"nid\"`\n\n\t// DangerousSkipCSRFCheck indicates whether anti CSRF measures should be enforced in this flow\n\t//\n\t// This is needed, because we can not enforce these measures, if the flow has been initialized by someone else than\n\t// the user.\n\tDangerousSkipCSRFCheck bool `json:\"-\" faker:\"-\" db:\"skip_csrf_check\"`\n\n\t// Contains possible actions that could follow this flow\n\tContinueWith []flow.ContinueWith `json:\"continue_with,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// TransientPayload is used to pass data from the recovery flow to hooks and email templates\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" faker:\"-\" db:\"-\"`\n}\n\nvar _ flow.Flow = (*Flow)(nil)\n\nfunc NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, strategies Strategies, ft flow.Type) (*Flow, error) {\n\tnow := time.Now().UTC()\n\tid := x.NewUUID()\n\n\t// Pre-validate the return to URL which is contained in the HTTP request.\n\trequestURL := x.RequestURL(r).String()\n\t_, err := redir.SecureRedirectTo(r,\n\t\tconf.SelfServiceBrowserDefaultReturnTo(r.Context()),\n\t\tredir.SecureRedirectUseSourceURL(requestURL),\n\t\tredir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstate := flow.StateChooseMethod\n\tif conf.ChooseRecoveryAddress(r.Context()) {\n\t\tstate = flow.StateRecoveryAwaitingAddress\n\t} else {\n\t\ttrace.SpanFromContext(r.Context()).AddEvent(semconv.NewDeprecatedFeatureUsedEvent(r.Context(), \"legacy_recovery_flow\"))\n\t}\n\n\tf := &Flow{\n\t\tID:         id,\n\t\tExpiresAt:  now.Add(exp),\n\t\tIssuedAt:   now,\n\t\tRequestURL: requestURL,\n\t\tUI: &container.Container{\n\t\t\tMethod: \"POST\",\n\t\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(conf.SelfPublicURL(r.Context()), RouteSubmitFlow), id).String(),\n\t\t},\n\t\tState:     state,\n\t\tCSRFToken: csrf,\n\t\tType:      ft,\n\t}\n\n\tfor _, strategy := range strategies {\n\t\tif strategy.IsPrimary() {\n\t\t\tf.Active = sqlxx.NullString(strategy.NodeGroup())\n\t\t}\n\t\tif err := strategy.PopulateRecoveryMethod(r, f); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn f, nil\n}\n\nfunc FromOldFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, strategies Strategies, of Flow) (*Flow, error) {\n\tf := of.Type\n\t// Using the same flow in the recovery/verification context can lead to using API flow in a verification/recovery email\n\tif of.Type == flow.TypeAPI && of.Active.String() == string(RecoveryStrategyLink) {\n\t\tf = flow.TypeBrowser\n\t}\n\tnf, err := NewFlow(conf, exp, csrf, r, strategies, f)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnf.RequestURL = of.RequestURL\n\treturn nf, nil\n}\n\nfunc (f *Flow) GetType() flow.Type                   { return f.Type }\nfunc (f *Flow) GetRequestURL() string                { return f.RequestURL }\nfunc (Flow) TableName() string                       { return \"selfservice_recovery_flows\" }\nfunc (f Flow) GetID() uuid.UUID                      { return f.ID }\nfunc (f *Flow) GetUI() *container.Container          { return f.UI }\nfunc (f *Flow) GetState() State                      { return f.State }\nfunc (Flow) GetFlowName() flow.FlowName              { return flow.RecoveryFlow }\nfunc (f *Flow) SetState(state State)                 { f.State = state }\nfunc (f *Flow) GetTransientPayload() json.RawMessage { return f.TransientPayload }\n\nfunc (f *Flow) Valid() error {\n\tif f.ExpiresAt.Before(time.Now().UTC()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\treturn nil\n}\n\nfunc (f *Flow) AppendTo(src *url.URL) *url.URL {\n\treturn urlx.CopyWithQuery(src, url.Values{\"flow\": {f.ID.String()}})\n}\n\nfunc (f *Flow) SetCSRFToken(token string) {\n\tf.CSRFToken = token\n\tf.UI.SetCSRF(token)\n}\n\nfunc (f Flow) MarshalJSON() ([]byte, error) {\n\ttype local Flow\n\tf.SetReturnTo()\n\treturn json.Marshal(local(f))\n}\n\nfunc (f *Flow) SetReturnTo() {\n\t// Return to is already set, do not overwrite it.\n\tif len(f.ReturnTo) > 0 {\n\t\treturn\n\t}\n\tif u, err := url.Parse(f.RequestURL); err == nil {\n\t\tf.ReturnTo = u.Query().Get(\"return_to\")\n\t}\n}\n\nfunc (f *Flow) AfterFind(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\nfunc (f *Flow) AfterSave(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\nfunc (f *Flow) ToLoggerField() map[string]any {\n\tif f == nil {\n\t\treturn map[string]any{}\n\t}\n\treturn map[string]any{\n\t\t\"id\":          f.ID.String(),\n\t\t\"return_to\":   f.ReturnTo,\n\t\t\"request_url\": f.RequestURL,\n\t\t\"active\":      f.Active,\n\t\t\"type\":        f.Type,\n\t\t\"nid\":         f.NID,\n\t\t\"state\":       f.State,\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/flow_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\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/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/x/jsonx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf := pkg.NewConfigurationWithDefaults(t)\n\n\tmust := func(r *recovery.Flow, err error) *recovery.Flow {\n\t\trequire.NoError(t, err)\n\t\treturn r\n\t}\n\n\tu := &http.Request{URL: urlx.ParseOrPanic(\"http://foo/bar/baz\"), Host: \"foo\"}\n\tfor k, tc := range []struct {\n\t\tr         *recovery.Flow\n\t\texpectErr bool\n\t}{\n\t\t{r: must(recovery.NewFlow(conf, time.Hour, \"\", u, nil, flow.TypeBrowser))},\n\t\t{r: must(recovery.NewFlow(conf, -time.Hour, \"\", u, nil, flow.TypeBrowser)), expectErr: true},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\terr := tc.r.Valid()\n\t\t\tif tc.expectErr {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\t\t})\n\t}\n\n\tassert.EqualValues(t, flow.StateChooseMethod,\n\t\tmust(recovery.NewFlow(conf, time.Hour, \"\", u, nil, flow.TypeBrowser)).State)\n\n\tt.Run(\"type=return_to\", func(t *testing.T) {\n\t\t_, err := recovery.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=https://not-allowed/foobar\"}, Host: \"ory.sh\"}, nil, flow.TypeBrowser)\n\t\trequire.Error(t, err)\n\n\t\t_, err = recovery.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=\" + urlx.AppendPaths(conf.SelfPublicURL(ctx), \"/self-service/login/browser\").String()}, Host: \"ory.sh\"}, nil, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestGetType(t *testing.T) {\n\tfor _, ft := range []flow.Type{\n\t\tflow.TypeAPI,\n\t\tflow.TypeBrowser,\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", ft), func(t *testing.T) {\n\t\t\tr := &recovery.Flow{Type: ft}\n\t\t\tassert.Equal(t, ft, r.GetType())\n\t\t})\n\t}\n}\n\nfunc TestGetRequestURL(t *testing.T) {\n\texpectedURL := \"http://foo/bar/baz\"\n\tf := &recovery.Flow{RequestURL: expectedURL}\n\tassert.Equal(t, expectedURL, f.GetRequestURL())\n}\n\nfunc TestFlowEncodeJSON(t *testing.T) {\n\tassert.EqualValues(t, \"\", gjson.Get(jsonx.TestMarshalJSONString(t, &recovery.Flow{RequestURL: \"https://foo.bar?foo=bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, &recovery.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, recovery.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n}\n\nfunc TestFromOldFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\tr := http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=\" + urlx.AppendPaths(conf.SelfPublicURL(ctx), \"/self-service/login/browser\").String()}, Host: \"ory.sh\"}\n\tt.Run(\"strategy=code\", func(t *testing.T) {\n\t\tfor _, ft := range []flow.Type{\n\t\t\tflow.TypeAPI,\n\t\t\tflow.TypeBrowser,\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=original flow is %s\", ft), func(t *testing.T) {\n\t\t\t\tf, err := recovery.NewFlow(conf, 0, \"csrf\", &r, recovery.Strategies{code.NewStrategy(reg)}, ft)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tnF, err := recovery.FromOldFlow(conf, time.Duration(time.Hour), f.CSRFToken, &r, nil, *f)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, ft, nF.Type)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"strategy=link\", func(t *testing.T) {\n\t\tfor _, ft := range []flow.Type{\n\t\t\tflow.TypeAPI,\n\t\t\tflow.TypeBrowser,\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=original flow is %s\", ft), func(t *testing.T) {\n\t\t\t\tf, err := recovery.NewFlow(conf, 0, \"csrf\", &r, recovery.Strategies{link.NewStrategy(reg)}, ft)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tnF, err := recovery.FromOldFlow(conf, time.Duration(time.Hour), f.CSRFToken, &r, nil, *f)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, flow.TypeBrowser, nF.Type)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestFlowDontOverrideReturnTo(t *testing.T) {\n\tf := &recovery.Flow{ReturnTo: \"/foo\"}\n\tf.SetReturnTo()\n\tassert.Equal(t, \"/foo\", f.ReturnTo)\n\n\tf = &recovery.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}\n\tf.SetReturnTo()\n\tassert.Equal(t, \"/bar\", f.ReturnTo)\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst (\n\tRouteInitBrowserFlow = \"/self-service/recovery/browser\"\n\tRouteInitAPIFlow     = \"/self-service/recovery/api\"\n\tRouteGetFlow         = \"/self-service/recovery/flows\"\n\n\tRouteSubmitFlow = \"/self-service/recovery\"\n)\n\ntype (\n\tHandlerProvider interface {\n\t\tRecoveryHandler() *Handler\n\t}\n\thandlerDependencies interface {\n\t\terrorx.ManagementProvider\n\t\tidentity.ManagementProvider\n\t\tidentity.PrivilegedPoolProvider\n\t\tsession.HandlerProvider\n\t\tStrategyProvider\n\t\tFlowPersistenceProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\thttpx.WriterProvider\n\t\tnosurfx.CSRFProvider\n\t\tconfig.Provider\n\t\tErrorHandlerProvider\n\t\tHookExecutorProvider\n\t}\n\tHandler struct {\n\t\td handlerDependencies\n\t}\n)\n\nfunc NewHandler(d handlerDependencies) *Handler { return &Handler{d: d} }\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\th.d.CSRFHandler().IgnorePath(RouteInitAPIFlow)\n\th.d.CSRFHandler().IgnorePath(RouteSubmitFlow)\n\n\tredirect := session.RedirectOnAuthenticated(h.d)\n\tpublic.GET(RouteInitBrowserFlow, h.d.SessionHandler().IsNotAuthenticated(h.createBrowserRecoveryFlow, func(w http.ResponseWriter, r *http.Request) {\n\t\tif x.IsJSONRequest(r) {\n\t\t\th.d.Writer().WriteError(w, r, errors.WithStack(ErrAlreadyLoggedIn))\n\t\t} else {\n\t\t\tredirect(w, r)\n\t\t}\n\t}))\n\n\tpublic.GET(RouteInitAPIFlow, h.d.SessionHandler().IsNotAuthenticated(h.createNativeRecoveryFlow,\n\t\tsession.RespondWithJSONErrorOnAuthenticated(h.d.Writer(), ErrAlreadyLoggedIn)))\n\n\tpublic.GET(RouteGetFlow, h.getRecoveryFlow)\n\n\tpublic.GET(RouteSubmitFlow, h.updateRecoveryFlow)\n\tpublic.POST(RouteSubmitFlow, h.updateRecoveryFlow)\n}\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n}\n\n// swagger:route GET /self-service/recovery/api frontend createNativeRecoveryFlow\n//\n// # Create Recovery Flow for Native Apps\n//\n// This endpoint initiates a recovery flow for API clients such as mobile devices, smart TVs, and so on.\n//\n// If a valid provided session cookie or session token is provided, a 400 Bad Request error.\n//\n// On an existing recovery flow, use the `getRecoveryFlow` API endpoint.\n//\n// You MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n// Pages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n// you vulnerable to a variety of CSRF attacks.\n//\n// This endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n//\n// More information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: recoveryFlow\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createNativeRecoveryFlow(w http.ResponseWriter, r *http.Request) {\n\tif !h.d.Config().SelfServiceFlowRecoveryEnabled(r.Context()) {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Recovery is not allowed because it was disabled.\")))\n\t\treturn\n\t}\n\tactiveRecoveryStrategies, _, err := h.d.GetActiveRecoveryStrategies(r.Context())\n\tif err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tf, err := NewFlow(h.d.Config(), h.d.Config().SelfServiceFlowRecoveryRequestLifespan(r.Context()), h.d.GenerateCSRFToken(r), r, activeRecoveryStrategies, flow.TypeAPI)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif err := h.d.RecoveryExecutor().PreRecoveryHook(w, r, f); err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif err := h.d.RecoveryFlowPersister().CreateRecoveryFlow(r.Context(), f); err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.d.Writer().Write(w, r, f)\n}\n\n// Create Browser Recovery Flow Parameters\n//\n// swagger:parameters createBrowserRecoveryFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createBrowserRecoveryFlow struct {\n\t// The URL to return the browser to after the flow was completed.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n}\n\n// swagger:route GET /self-service/recovery/browser frontend createBrowserRecoveryFlow\n//\n// # Create Recovery Flow for Browsers\n//\n// This endpoint initializes a browser-based account recovery flow. Once initialized, the browser will be redirected to\n// `selfservice.flows.recovery.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\n// exists, the browser is returned to the configured return URL.\n//\n// If this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects\n// or a 400 bad request error if the user is already authenticated.\n//\n// This endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n//\n// More information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: recoveryFlow\n//\t  303: emptyResponse\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createBrowserRecoveryFlow(w http.ResponseWriter, r *http.Request) {\n\tif !h.d.Config().SelfServiceFlowRecoveryEnabled(r.Context()) {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Recovery is not allowed because it was disabled.\")))\n\t\treturn\n\t}\n\tactiveRecoveryStrategies, _, err := h.d.GetActiveRecoveryStrategies(r.Context())\n\tif err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tf, err := NewFlow(h.d.Config(), h.d.Config().SelfServiceFlowRecoveryRequestLifespan(r.Context()), h.d.GenerateCSRFToken(r), r, activeRecoveryStrategies, flow.TypeBrowser)\n\tif err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tif err := h.d.RecoveryExecutor().PreRecoveryHook(w, r, f); err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif err := h.d.RecoveryFlowPersister().CreateRecoveryFlow(r.Context(), f); err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tredirTo := f.AppendTo(h.d.Config().SelfServiceFlowRecoveryUI(r.Context())).String()\n\tx.SendFlowCompletedAsRedirectOrJSON(w, r, h.d.Writer(), f, redirTo)\n}\n\n// Get Recovery Flow Parameters\n//\n// swagger:parameters getRecoveryFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getRecoveryFlow struct {\n\t// The Flow ID\n\t//\n\t// The value for this parameter comes from `request` URL Query parameter sent to your\n\t// application (e.g. `/recovery?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tFlowID string `json:\"id\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// swagger:route GET /self-service/recovery/flows frontend getRecoveryFlow\n//\n// # Get Recovery Flow\n//\n// This endpoint returns a recovery flow's context with, for example, error details and other information.\n//\n// Browser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n// For AJAX requests you must ensure that cookies are included in the request or requests will fail.\n//\n// If you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n// and you need to forward the incoming HTTP Cookie header to this endpoint:\n//\n//\t```js\n//\t// pseudo-code example\n//\trouter.get('/recovery', async function (req, res) {\n//\t  const flow = await client.getRecoveryFlow(req.header('Cookie'), req.query['flow'])\n//\n//\t  res.render('recovery', flow)\n//\t})\n//\t```\n//\n// More information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: recoveryFlow\n//\t  404: errorGeneric\n//\t  410: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-low\nfunc (h *Handler) getRecoveryFlow(w http.ResponseWriter, r *http.Request) {\n\tif !h.d.Config().SelfServiceFlowRecoveryEnabled(r.Context()) {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Recovery is not allowed because it was disabled.\")))\n\t\treturn\n\t}\n\n\trid := x.ParseUUID(r.URL.Query().Get(\"id\"))\n\tf, err := h.d.RecoveryFlowPersister().GetRecoveryFlow(r.Context(), rid)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\t// Browser flows must include the CSRF token\n\t//\n\t// Resolves: https://github.com/ory/kratos/issues/1282\n\tif f.Type.IsBrowser() && !f.DangerousSkipCSRFCheck && !nosurf.VerifyToken(h.d.GenerateCSRFToken(r), f.CSRFToken) {\n\t\th.d.Writer().WriteError(w, r, nosurfx.CSRFErrorReason(r, h.d))\n\t\treturn\n\t}\n\n\tif f.ExpiresAt.Before(time.Now().UTC()) {\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tredirectURL := flow.GetFlowExpiredRedirectURL(r.Context(), h.d.Config(), RouteInitBrowserFlow, f.ReturnTo)\n\n\t\t\th.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.\n\t\t\t\tWithReason(\"The recovery flow has expired. Redirect the user to the recovery flow init endpoint to initialize a new recovery flow.\").\n\t\t\t\tWithDetail(\"redirect_to\", redirectURL.String()).\n\t\t\t\tWithDetail(\"return_to\", f.ReturnTo)))\n\t\t\treturn\n\t\t}\n\n\t\th.d.Writer().WriteError(w, r, flow.NewFlowExpiredError(f.ExpiresAt).\n\t\t\tWithDetail(\"api\", urlx.AppendPaths(h.d.Config().SelfPublicURL(r.Context()), RouteInitAPIFlow).String()))\n\t\treturn\n\t}\n\n\th.d.Writer().Write(w, r, f)\n}\n\n// Update Recovery Flow Parameters\n//\n// swagger:parameters updateRecoveryFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateRecoveryFlow struct {\n\t// The Recovery Flow ID\n\t//\n\t// The value for this parameter comes from `flow` URL Query parameter sent to your\n\t// application (e.g. `/recovery?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tFlow string `json:\"flow\"`\n\n\t// Recovery Token\n\t//\n\t// The recovery token which completes the recovery request. If the token\n\t// is invalid (e.g. expired) an error will be shown to the end-user.\n\t//\n\t// This parameter is usually set in a link and not used by any direct API call.\n\t//\n\t// in: query\n\tToken string `json:\"token\" form:\"token\"`\n\n\t// in: body\n\t// required: true\n\tBody updateRecoveryFlowBody\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// Update Recovery Flow Request Body\n//\n// swagger:model updateRecoveryFlowBody\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateRecoveryFlowBody struct{}\n\n// swagger:route POST /self-service/recovery frontend updateRecoveryFlow\n//\n// # Update Recovery Flow\n//\n// Use this endpoint to update a recovery flow. This endpoint\n// behaves differently for API and browser flows and has several states:\n//\n//   - `choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\n//     and works with API- and Browser-initiated flows.\n//   - For API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid.\n//     and a HTTP 303 See Other redirect with a fresh recovery flow if the flow was otherwise invalid (e.g. expired).\n//   - For Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Recovery UI URL with the Recovery Flow ID appended.\n//   - `sent_email` is the success state after `choose_method` for the `link` method and allows the user to request another recovery email. It\n//     works for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n//   - `passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a recovery link\")\n//     does not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n//     (if the link was valid) and instructs the user to update their password, or a redirect to the Recover UI URL with\n//     a new Recovery Flow ID which contains an error message that the recovery link was invalid.\n//\n// More information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\n//\n//\t\tConsumes:\n//\t\t- application/json\n//\t\t- application/x-www-form-urlencoded\n//\n//\t\tProduces:\n//\t\t- application/json\n//\n//\t\tSchemes: http, https\n//\n//\t    Responses:\n//\t      200: recoveryFlow\n//\t      303: emptyResponse\n//\t      400: recoveryFlow\n//\t      410: errorGeneric\n//\t      422: errorBrowserLocationChangeRequired\n//\t      default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-high\nfunc (h *Handler) updateRecoveryFlow(w http.ResponseWriter, r *http.Request) {\n\trid, err := flow.GetFlowID(r)\n\tif err != nil {\n\t\th.d.RecoveryFlowErrorHandler().WriteFlowError(w, r, nil, node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tf, err := h.d.RecoveryFlowPersister().GetRecoveryFlow(r.Context(), rid)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\th.d.RecoveryFlowErrorHandler().WriteFlowError(w, r, nil, node.DefaultGroup, errors.WithStack(herodot.ErrNotFound.WithReasonf(\"The recovery request could not be found. Please restart the flow.\")))\n\t\treturn\n\t} else if err != nil {\n\t\th.d.RecoveryFlowErrorHandler().WriteFlowError(w, r, nil, node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tif err := f.Valid(); err != nil {\n\t\th.d.RecoveryFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tvar g node.UiNodeGroup\n\tvar found bool\n\tfor _, ss := range h.d.AllRecoveryStrategies() {\n\t\terr := ss.Recover(w, r, f)\n\t\tif errors.Is(err, flow.ErrStrategyNotResponsible) {\n\t\t\tcontinue\n\t\t} else if errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\treturn\n\t\t} else if err != nil {\n\t\t\th.d.RecoveryFlowErrorHandler().WriteFlowError(w, r, f, ss.NodeGroup(), err)\n\t\t\treturn\n\t\t}\n\n\t\tfound = true\n\t\tg = ss.NodeGroup()\n\t\tbreak\n\t}\n\n\tif !found {\n\t\th.d.RecoveryFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(schema.NewNoRecoveryStrategyResponsible()))\n\t\treturn\n\t}\n\n\t// WARNING - just because no error was returned does not mean that the challenge was accepted. Instead, the\n\t// success state is available as:\n\t//\n\t//\tif f.State == flow.StatePassedChallenge\n\n\tif f.Type == flow.TypeBrowser && !x.IsJSONRequest(r) {\n\t\thttp.Redirect(w, r, f.AppendTo(h.d.Config().SelfServiceFlowRecoveryUI(r.Context())).String(), http.StatusSeeOther)\n\t\treturn\n\t}\n\n\tupdatedFlow, err := h.d.RecoveryFlowPersister().GetRecoveryFlow(r.Context(), f.ID)\n\tif err != nil {\n\t\th.d.RecoveryFlowErrorHandler().WriteFlowError(w, r, f, g, err)\n\t\treturn\n\t}\n\tupdatedFlow.TransientPayload = f.TransientPayload\n\n\th.d.Writer().Write(w, r, updatedFlow)\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\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\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/corpx\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httprouterx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc TestHandlerRedirectOnAuthenticated(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeySelfServiceRecoveryEnabled, true),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t)\n\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tts, _ := testhelpers.NewKratosServerWithRouters(t, reg, router, httprouterx.NewTestRouterAdminWithPrefix(t))\n\n\tredirTS := testhelpers.NewRedirTS(t, \"already authenticated\", conf)\n\n\tt.Run(\"does redirect to default on authenticated request\", func(t *testing.T) {\n\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, router, testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+recovery.RouteInitBrowserFlow, nil))\n\t\tassert.Contains(t, res.Request.URL.String(), redirTS.URL, \"%+v\", res)\n\t\tassert.EqualValues(t, \"already authenticated\", string(body))\n\t})\n\n\tt.Run(\"does redirect to default on authenticated request\", func(t *testing.T) {\n\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, router, testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+recovery.RouteInitAPIFlow, nil))\n\t\tassert.Contains(t, res.Request.URL.String(), recovery.RouteInitAPIFlow)\n\t\tassert.EqualValues(t, text.ErrIDAlreadyLoggedIn, gjson.GetBytes(body, \"error.id\").Str)\n\t\tassertx.EqualAsJSON(t, recovery.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw))\n\t})\n}\n\nfunc TestInitFlow(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(recovery.RecoveryStrategyLink, true)),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(recovery.RecoveryStrategyCode, true)),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t\tconfigx.WithValue(config.ViperKeySelfServiceRecoveryEnabled, true),\n\t)\n\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tpublicTS, _ := testhelpers.NewKratosServerWithRouters(t, reg, router, httprouterx.NewTestRouterAdminWithPrefix(t))\n\trecoveryTS := testhelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\n\tassertion := func(body []byte, isForced, isApi bool) {\n\t\tif isApi {\n\t\t\tassert.Equal(t, \"api\", gjson.GetBytes(body, \"type\").String())\n\t\t} else {\n\t\t\tassert.Equal(t, \"browser\", gjson.GetBytes(body, \"type\").String())\n\t\t}\n\t}\n\n\tinitAuthenticatedFlow := func(t *testing.T, isAPI, isSPA bool) (*http.Response, []byte) {\n\t\troute := recovery.RouteInitBrowserFlow\n\t\tif isAPI {\n\t\t\troute = recovery.RouteInitAPIFlow\n\t\t}\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", publicTS.URL+route, nil)\n\t\tif isSPA {\n\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t}\n\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, router, req)\n\t\tif isAPI {\n\t\t\tassert.Len(t, res.Header.Get(\"Set-Cookie\"), 0)\n\t\t}\n\t\treturn res, body\n\t}\n\n\tinitFlow := func(t *testing.T, isAPI bool) (*http.Response, []byte) {\n\t\troute := recovery.RouteInitBrowserFlow\n\t\tif isAPI {\n\t\t\troute = recovery.RouteInitAPIFlow\n\t\t}\n\t\tc := publicTS.Client()\n\t\tres, err := c.Get(publicTS.URL + route)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\treturn res, body\n\t}\n\n\tinitSPAFlow := func(t *testing.T, hc *http.Client, isSPA bool) (*http.Response, []byte) {\n\t\troute := recovery.RouteInitBrowserFlow\n\t\tc := publicTS.Client()\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", publicTS.URL+route, nil)\n\t\tif isSPA {\n\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t}\n\t\tres, err := c.Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\treturn res, body\n\t}\n\n\tt.Run(\"flow=api\", func(t *testing.T) {\n\t\tt.Run(\"case=creates a new flow on unauthenticated request\", func(t *testing.T) {\n\t\t\tres, body := initFlow(t, true)\n\t\t\tassert.Contains(t, res.Request.URL.String(), recovery.RouteInitAPIFlow)\n\t\t\tassertion(body, false, true)\n\t\t})\n\n\t\tt.Run(\"case=fails on authenticated request\", func(t *testing.T) {\n\t\t\tres, body := initAuthenticatedFlow(t, true, false)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, recovery.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"flow=spa\", func(t *testing.T) {\n\t\tt.Run(\"case=creates a new flow on unauthenticated request\", func(t *testing.T) {\n\t\t\tres, body := initSPAFlow(t, new(http.Client), true)\n\t\t\tassert.Contains(t, res.Request.URL.String(), recovery.RouteInitBrowserFlow)\n\t\t\tassertion(body, false, false)\n\t\t})\n\n\t\tt.Run(\"case=fails on authenticated request\", func(t *testing.T) {\n\t\t\tres, body := initAuthenticatedFlow(t, false, true)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, recovery.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\tt.Run(\"case=does not set forced flag on unauthenticated request\", func(t *testing.T) {\n\t\t\tres, body := initFlow(t, false)\n\t\t\tassertion(body, false, false)\n\t\t\tassert.Contains(t, res.Request.URL.String(), recoveryTS.URL)\n\t\t})\n\n\t\tt.Run(\"case=fails on authenticated request\", func(t *testing.T) {\n\t\t\tres, _ := initAuthenticatedFlow(t, false, false)\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceBrowserDefaultReturnTo(t.Context()).Host)\n\t\t})\n\n\t\tt.Run(\"case=relative redirect when self-service recovery ui is a relative URL\", func(t *testing.T) {\n\t\t\treg.Config().MustSet(ctx, config.ViperKeySelfServiceRecoveryUI, \"/recovery-ts\")\n\t\t\tassert.Regexp(\n\t\t\t\tt,\n\t\t\t\t\"^/recovery-ts.*$\",\n\t\t\t\ttesthelpers.GetSelfServiceRedirectLocation(t, publicTS.URL+recovery.RouteInitBrowserFlow),\n\t\t\t)\n\t\t})\n\n\t\tt.Run(\"case=redirects with 303\", func(t *testing.T) {\n\t\t\tc := &http.Client{}\n\t\t\t// don't get the reference, instead copy the values, so we don't alter the client directly.\n\t\t\t*c = *publicTS.Client()\n\t\t\t// prevent the redirect\n\t\t\tc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\t\t\treq, err := http.NewRequest(\"GET\", publicTS.URL+recovery.RouteInitBrowserFlow, nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tres, err := c.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t// here we check that the redirect status is 303\n\t\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t})\n\t})\n}\n\nfunc TestGetFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryEnabled, true)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(recovery.RecoveryStrategyLink),\n\t\tmap[string]interface{}{\"enabled\": true})\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(recovery.RecoveryStrategyCode),\n\t\tmap[string]interface{}{\"enabled\": true})\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\n\treturnToTS := testhelpers.NewRedirTS(t, \"\", conf)\n\tpublic, _ := testhelpers.NewKratosServerWithCSRF(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewRedirTS(t, \"\", conf)\n\n\tsetupRecoveryTS := func(t *testing.T, c *http.Client) *httptest.Server {\n\t\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t_, err := w.Write(testhelpers.EasyGetBody(t, c, public.URL+recovery.RouteGetFlow+\"?id=\"+r.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t}))\n\t\tt.Cleanup(ts.Close)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryUI, ts.URL)\n\t\treturn ts\n\t}\n\n\tt.Run(\"case=csrf cookie missing\", func(t *testing.T) {\n\t\tclient := http.DefaultClient\n\t\tsetupRecoveryTS(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+recovery.RouteInitBrowserFlow)\n\n\t\tassert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=valid\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupRecoveryTS(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+recovery.RouteInitBrowserFlow)\n\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String(), \"%s\", body)\n\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"id\").String(), \"%s\", body)\n\t\tassert.Empty(t, gjson.GetBytes(body, \"headers\").Value(), \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), gjson.GetBytes(body, \"id\").String(), \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), public.URL, \"%s\", body)\n\t})\n\n\tt.Run(\"case=expired\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupRecoveryTS(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+recovery.RouteInitBrowserFlow)\n\n\t\t// Expire the flow\n\t\tf, err := reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tf.ExpiresAt = time.Now().Add(-time.Second)\n\t\trequire.NoError(t, reg.RecoveryFlowPersister().UpdateRecoveryFlow(context.Background(), f))\n\n\t\tres, body := testhelpers.EasyGet(t, client, public.URL+recovery.RouteGetFlow+\"?id=\"+f.ID.String())\n\t\tassert.EqualValues(t, http.StatusGone, res.StatusCode)\n\t\tassert.Equal(t, public.URL+recovery.RouteInitBrowserFlow, gjson.GetBytes(body, \"error.details.redirect_to\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=expired with return_to\", func(t *testing.T) {\n\t\treturnTo := returnToTS.URL\n\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupRecoveryTS(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+recovery.RouteInitBrowserFlow+\"?return_to=\"+returnTo)\n\n\t\t// Expire the flow\n\t\tf, err := reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tf.ExpiresAt = time.Now().Add(-time.Second)\n\t\trequire.NoError(t, reg.RecoveryFlowPersister().UpdateRecoveryFlow(context.Background(), f))\n\n\t\t// Retrieve the flow and verify that return_to is in the response\n\t\tgetURL := fmt.Sprintf(\"%s%s?id=%s&return_to=%s\", public.URL, recovery.RouteGetFlow, f.ID, returnTo)\n\t\tgetBody := testhelpers.EasyGetBody(t, client, getURL)\n\t\tassert.Equal(t, gjson.GetBytes(getBody, \"error.details.return_to\").String(), returnTo)\n\n\t\t// submit the flow but it is expired\n\t\tu := public.URL + recovery.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\tres, err := client.PostForm(u, url.Values{\"email\": {\"email@ory.sh\"}, \"csrf_token\": {f.CSRFToken}, \"method\": {\"link\"}})\n\t\trequire.NoError(t, err)\n\t\tresBody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\tf, err = reg.RecoveryFlowPersister().GetRecoveryFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, public.URL+recovery.RouteInitBrowserFlow+\"?return_to=\"+returnTo, f.RequestURL)\n\t})\n\n\tt.Run(\"case=not found\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupRecoveryTS(t, client)\n\n\t\tres, _ := testhelpers.EasyGet(t, client, public.URL+recovery.RouteGetFlow+\"?id=\"+x.NewUUID().String())\n\t\tassert.EqualValues(t, http.StatusNotFound, res.StatusCode)\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/hook.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\ntype (\n\tPreHookExecutor interface {\n\t\tExecuteRecoveryPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error\n\t}\n\tPreHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow) error\n\n\tPostHookExecutor interface {\n\t\tExecutePostRecoveryHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error\n\t}\n\tPostHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error\n\n\tHooksProvider interface {\n\t\tPreRecoveryHooks(ctx context.Context) ([]PreHookExecutor, error)\n\t\tPostRecoveryHooks(ctx context.Context) ([]PostHookExecutor, error)\n\t}\n)\n\nfunc PostHookRecoveryExecutorNames(e []PostHookExecutor) []string {\n\tnames := make([]string, len(e))\n\tfor k, ee := range e {\n\t\tnames[k] = fmt.Sprintf(\"%T\", ee)\n\t}\n\treturn names\n}\n\nfunc (f PreHookExecutorFunc) ExecuteRecoveryPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error {\n\treturn f(w, r, a)\n}\n\nfunc (f PostHookExecutorFunc) ExecutePostRecoveryHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error {\n\treturn f(w, r, a, s)\n}\n\ntype (\n\texecutorDependencies interface {\n\t\tconfig.Provider\n\t\tidentity.ManagementProvider\n\t\tidentity.ValidationProvider\n\t\tsession.PersistenceProvider\n\t\tHooksProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tlogrusx.Provider\n\t\thttpx.WriterProvider\n\t}\n\n\tHookExecutor struct {\n\t\td executorDependencies\n\t}\n\n\tHookExecutorProvider interface {\n\t\tRecoveryExecutor() *HookExecutor\n\t}\n)\n\nfunc NewHookExecutor(d executorDependencies) *HookExecutor {\n\treturn &HookExecutor{\n\t\td: d,\n\t}\n}\n\nfunc (e *HookExecutor) PostRecoveryHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error {\n\tlogger := e.d.Logger().\n\t\tWithRequest(r)\n\n\tif s.Identity != nil {\n\t\tlogger = logger.WithField(\"identity_id\", s.Identity.ID)\n\t}\n\n\tlogger.Debug(\"Running ExecutePostRecoveryHooks.\")\n\thooks, err := e.d.PostRecoveryHooks(r.Context())\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k, executor := range hooks {\n\t\tif err := executor.ExecutePostRecoveryHook(w, r, a, s); err != nil {\n\t\t\tvar traits identity.Traits\n\t\t\tif s.Identity != nil {\n\t\t\t\ttraits = s.Identity.Traits\n\t\t\t}\n\t\t\treturn flow.HandleHookError(w, r, a, traits, node.LinkGroup, err, e.d, e.d)\n\t\t}\n\n\t\tlogger.\n\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\tWithField(\"executor_position\", k).\n\t\t\tWithField(\"executors\", PostHookRecoveryExecutorNames(hooks)).\n\t\t\tDebug(\"ExecutePostRecoveryHook completed successfully.\")\n\t}\n\n\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewRecoverySucceeded(r.Context(), a.ID, s.Identity.ID, string(a.Type), a.Active.String()))\n\n\tlogger.Debug(\"Post recovery execution hooks completed successfully.\")\n\n\treturn nil\n}\n\nfunc (e *HookExecutor) PreRecoveryHook(w http.ResponseWriter, r *http.Request, a *Flow) error {\n\thooks, err := e.d.PreRecoveryHooks(r.Context())\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, executor := range hooks {\n\t\tif err := executor.ExecuteRecoveryPreHook(w, r, a); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/hook_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/httptest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n)\n\nfunc TestRecoveryExecutor(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t)\n\ts := recovery.Strategies{code.NewStrategy(reg)}\n\n\tnewServer := func(t *testing.T, i *identity.Identity, ft flow.Type) *httptest.Server {\n\t\trouter := http.NewServeMux()\n\t\trouter.HandleFunc(\"GET /recovery/pre\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\ta, err := recovery.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, s, ft)\n\t\t\trequire.NoError(t, err)\n\t\t\tif testhelpers.SelfServiceHookErrorHandler(t, w, r, recovery.ErrHookAbortFlow, reg.RecoveryExecutor().PreRecoveryHook(w, r, a)) {\n\t\t\t\t_, _ = w.Write([]byte(\"ok\"))\n\t\t\t}\n\t\t})\n\n\t\trouter.HandleFunc(\"GET /recovery/post\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\ta, err := recovery.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, s, ft)\n\t\t\trequire.NoError(t, err)\n\t\t\ts, err := testhelpers.NewActiveSession(r,\n\t\t\t\treg,\n\t\t\t\ti,\n\t\t\t\ttime.Now().UTC(),\n\t\t\t\tidentity.CredentialsTypeRecoveryLink,\n\t\t\t\tidentity.AuthenticatorAssuranceLevel1,\n\t\t\t)\n\t\t\trequire.NoError(t, err)\n\t\t\ta.RequestURL = x.RequestURL(r).String()\n\t\t\tif testhelpers.SelfServiceHookErrorHandler(t, w, r, recovery.ErrHookAbortFlow, reg.RecoveryExecutor().PostRecoveryHook(w, r, a, s)) {\n\t\t\t\t_, _ = w.Write([]byte(\"ok\"))\n\t\t\t}\n\t\t})\n\n\t\tts := httptest.NewServer(router)\n\t\tt.Cleanup(ts.Close)\n\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)\n\t\treturn ts\n\t}\n\n\tt.Run(\"method=PostRecoveryHook\", func(t *testing.T) {\n\t\tt.Run(\"case=pass without hooks\", func(t *testing.T) {\n\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\tts := newServer(t, i, flow.TypeBrowser)\n\n\t\t\tres, _ := testhelpers.SelfServiceMakeHookRequest(t, ts, \"/recovery/post\", false, url.Values{})\n\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=pass if hooks pass\", func(t *testing.T) {\n\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal),\n\t\t\t\t[]config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\tts := newServer(t, i, flow.TypeBrowser)\n\n\t\t\tres, _ := testhelpers.SelfServiceMakeHookRequest(t, ts, \"/recovery/post\", false, url.Values{})\n\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=fail if hooks fail\", func(t *testing.T) {\n\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal),\n\t\t\t\t[]config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecutePostRecoveryHook\": \"abort\"}`)}})\n\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\tts := newServer(t, i, flow.TypeBrowser)\n\n\t\t\tres, body := testhelpers.SelfServiceMakeHookRequest(t, ts, \"/recovery/post\", false, url.Values{})\n\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Equal(t, \"\", body)\n\t\t})\n\t})\n\n\tfor _, kind := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {\n\t\tt.Run(\"type=\"+string(kind)+\"/method=PreRecoveryHook\", testhelpers.TestSelfServicePreHook(\n\t\t\tconfig.ViperKeySelfServiceRecoveryBeforeHooks,\n\t\t\ttesthelpers.SelfServiceMakeRecoveryPreHookRequest,\n\t\t\tfunc(t *testing.T) *httptest.Server {\n\t\t\t\treturn newServer(t, nil, kind)\n\t\t\t},\n\t\t\tconf,\n\t\t))\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype (\n\tFlowPersister interface {\n\t\tCreateRecoveryFlow(context.Context, *Flow) error\n\t\tGetRecoveryFlow(ctx context.Context, id uuid.UUID) (*Flow, error)\n\t\tUpdateRecoveryFlow(context.Context, *Flow) error\n\t\tDeleteExpiredRecoveryFlows(context.Context, time.Time, int) error\n\t}\n\tFlowPersistenceProvider interface {\n\t\tRecoveryFlowPersister() FlowPersister\n\t}\n)\n"
  },
  {
    "path": "selfservice/flow/recovery/state.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery\n\nimport \"github.com/ory/kratos/selfservice/flow\"\n\n// Recovery Flow State\n//\n// The state represents the state of the recovery flow.\n//\n// - choose_method: ask the user to choose a method (e.g. recover account via email)\n// - sent_email: the email has been sent to the user\n// - passed_challenge: the request was successful and the recovery challenge was passed.\n//\n// swagger:model recoveryFlowState\ntype State = flow.State\n"
  },
  {
    "path": "selfservice/flow/recovery/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage recovery\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\n//swagger:enum RecoveryMethod\ntype RecoveryMethod string\n\nconst (\n\tRecoveryStrategyLink RecoveryMethod = \"link\"\n\tRecoveryStrategyCode RecoveryMethod = \"code\"\n)\n\ntype (\n\tStrategy interface {\n\t\tRecoveryStrategyID() string\n\t\tIsPrimary() bool\n\t\tNodeGroup() node.UiNodeGroup\n\t\tPopulateRecoveryMethod(*http.Request, *Flow) error\n\t\tRecover(w http.ResponseWriter, r *http.Request, f *Flow) (err error)\n\t}\n\tStrategies       []Strategy\n\tStrategyProvider interface {\n\t\tAllRecoveryStrategies() Strategies\n\t\tRecoveryStrategies(ctx context.Context) Strategies\n\t\tGetActiveRecoveryStrategies(ctx context.Context) (active Strategies, primary Strategy, err error)\n\t}\n)\n\nfunc (s Strategies) ActiveStrategies(id string) (active Strategies, primary Strategy, err error) {\n\tids := make([]string, len(s))\n\tfor k, ss := range s {\n\t\tids[k] = ss.RecoveryStrategyID()\n\t\tif ss.RecoveryStrategyID() == id || !ss.IsPrimary() {\n\t\t\tactive = append(active, ss)\n\t\t\tif ss.IsPrimary() {\n\t\t\t\tprimary = ss\n\t\t\t}\n\t\t}\n\t}\n\n\tif primary == nil {\n\t\treturn nil, nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"unable to find strategy for %s have %v\", id, ids))\n\t}\n\n\treturn active, primary, nil\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"booly\": {\n          \"type\": \"boolean\"\n        },\n        \"should_big_number\": {\n          \"type\": \"number\",\n          \"minimum\": 1200\n        },\n        \"should_long_string\": {\n          \"type\": \"string\",\n          \"minLength\": 25\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/recovery/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc TestFlowPersister(ctx context.Context, p interface {\n\tpersistence.Persister\n},\n) func(t *testing.T) {\n\tclearids := func(r *recovery.Flow) {\n\t\tr.ID = uuid.UUID{}\n\t}\n\n\treturn func(t *testing.T) {\n\t\tnid, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tt.Run(\"case=should error when the recovery request does not exist\", func(t *testing.T) {\n\t\t\t_, err := p.GetRecoveryFlow(ctx, x.NewUUID())\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tnewFlow := func(t *testing.T) *recovery.Flow {\n\t\t\tvar r recovery.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\tclearids(&r)\n\t\t\tr.State = flow.StateShowForm\n\t\t\treturn &r\n\t\t}\n\n\t\tt.Run(\"case=should create a new recovery request\", func(t *testing.T) {\n\t\t\tr := newFlow(t)\n\t\t\terr := p.CreateRecoveryFlow(ctx, r)\n\t\t\trequire.NoError(t, err, \"%#v\", err)\n\t\t\trequire.Equal(t, nid, r.NID)\n\n\t\t\tt.Run(\"fail to find on other network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetRecoveryFlow(ctx, r.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should create with set ids\", func(t *testing.T) {\n\t\t\tvar r recovery.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\tr.State = flow.StateShowForm\n\t\t\trequire.NoError(t, p.CreateRecoveryFlow(ctx, &r))\n\t\t\trequire.Equal(t, nid, r.NID)\n\n\t\t\tt.Run(\"fail to find on other network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetRecoveryFlow(ctx, r.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should create and fetch a recovery request\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\terr := p.CreateRecoveryFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, nid, expected.NID)\n\n\t\t\tt.Run(\"fail to find on other network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetRecoveryFlow(ctx, expected.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\n\t\t\tactual, err := p.GetRecoveryFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.EqualValues(t, expected.ID, actual.ID)\n\t\t\tx.AssertEqualTime(t, expected.IssuedAt, actual.IssuedAt)\n\t\t\tx.AssertEqualTime(t, expected.ExpiresAt, actual.ExpiresAt)\n\t\t\tassert.EqualValues(t, expected.RequestURL, actual.RequestURL)\n\t\t\tassertx.EqualAsJSON(t, expected.UI, actual.UI, \"expected:\\t%s\\nactual:\\t%s\", expected.UI, actual.UI)\n\t\t})\n\n\t\tt.Run(\"case=should create and update a recovery request\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\texpected.UI.Nodes = node.Nodes{}\n\t\t\texpected.UI.Nodes.Append(node.NewInputField(\"zab\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\ta.Pattern = \"baz\"\n\t\t\t})))\n\n\t\t\texpected.UI.Nodes.Append(node.NewInputField(\"foo\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\ta.Pattern = \"baz\"\n\t\t\t})))\n\n\t\t\terr := p.CreateRecoveryFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\texpected.UI.Action = \"/new-action\"\n\t\t\texpected.UI.Nodes.Append(\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"zab\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"zab\"\n\t\t\t\t})))\n\n\t\t\texpected.RequestURL = \"/new-request-url\"\n\n\t\t\tt.Run(\"fail to find on other network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\trequire.ErrorIs(t, other.UpdateRecoveryFlow(ctx, expected), sqlcon.ErrNoRows)\n\n\t\t\t\tactual, err := p.GetRecoveryFlow(ctx, expected.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.NotEqual(t, \"/new-request-url\", actual.RequestURL)\n\t\t\t})\n\n\t\t\trequire.NoError(t, p.UpdateRecoveryFlow(ctx, expected))\n\n\t\t\tactual, err := p.GetRecoveryFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, \"/new-action\", actual.UI.Action)\n\t\t\tassert.Equal(t, \"/new-request-url\", actual.RequestURL)\n\t\t\tassertx.EqualAsJSON(t, node.Nodes{\n\t\t\t\t// v0.5: {Name: \"zab\", Type: \"zab\", Pattern: \"zab\"},\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"baz\"\n\t\t\t\t})),\n\t\t\t\tnode.NewInputField(\"foo\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"baz\"\n\t\t\t\t})),\n\t\t\t\t// v0.5: {Name: \"zab\", Type: \"bar\", Pattern: \"baz\"},\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"zab\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"zab\"\n\t\t\t\t})),\n\t\t\t}, actual.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=should not cause data loss when updating a request without changes\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\terr := p.CreateRecoveryFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactual, err := p.GetRecoveryFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.NoError(t, p.UpdateRecoveryFlow(ctx, actual))\n\n\t\t\tactual, err = p.GetRecoveryFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassertx.EqualAsJSON(t, expected.UI, actual.UI)\n\t\t})\n\n\t\tt.Run(\"case=handle network reference issues\", func(t *testing.T) {\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/registration/.snapshots/TestSortNodes-case=1.json.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"value\": \"YTc3djZwaWpsZTFha3UyNHRlMDMyaTRxaHMxMWVmcmk=\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.username\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.foobar\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/flow/registration/decoder.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/x/decoderx\"\n)\n\nfunc DecodeBody(p interface{}, r *http.Request, schema []byte, ds *url.URL) error {\n\traw, err := sjson.SetBytes(schema,\n\t\t\"properties.traits.$ref\", ds.String()+\"#/properties/traits\")\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(raw)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn decoderx.Decode(r, p, compiler, decoderx.HTTPDecoderSetValidatePayloads(true), decoderx.HTTPDecoderJSONFollowsFormFormat())\n}\n"
  },
  {
    "path": "selfservice/flow/registration/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/x\"\n)\n\nvar (\n\tErrHookAbortFlow        = errors.New(\"aborted registration hook execution\")\n\tErrAlreadyLoggedIn      = herodot.ErrBadRequest.WithID(text.ErrIDAlreadyLoggedIn).WithError(\"you are already logged in\").WithReason(\"A valid session was detected and thus registration is not possible.\")\n\tErrRegistrationDisabled = herodot.ErrBadRequest.WithID(text.ErrIDSelfServiceFlowDisabled).WithError(\"registration flow disabled\").WithReason(\"Registration is not allowed because it was disabled.\")\n)\n\ntype (\n\terrorHandlerDependencies interface {\n\t\terrorx.ManagementProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\t\tconfig.Provider\n\t\thydra.Provider\n\n\t\tsessiontokenexchange.PersistenceProvider\n\t\tFlowPersistenceProvider\n\t\tHandlerProvider\n\t}\n\n\tErrorHandlerProvider interface{ RegistrationFlowErrorHandler() *ErrorHandler }\n\n\tErrorHandler struct {\n\t\td errorHandlerDependencies\n\t}\n)\n\nfunc NewErrorHandler(d errorHandlerDependencies) *ErrorHandler {\n\treturn &ErrorHandler{d: d}\n}\n\nfunc (s *ErrorHandler) PrepareReplacementForExpiredFlow(w http.ResponseWriter, r *http.Request, f *Flow, err error) (*flow.ExpiredError, error) {\n\terrExpired := new(flow.ExpiredError)\n\tif !errors.As(err, &errExpired) {\n\t\treturn nil, nil\n\t}\n\t// create new flow because the old one is not valid\n\tnewFlow, err := s.d.RegistrationHandler().FromOldFlow(w, r, *f)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnewFlow.UI.Messages.Add(text.NewErrorValidationRegistrationFlowExpired(errExpired.ExpiredAt))\n\tif err := s.d.RegistrationFlowPersister().UpdateRegistrationFlow(r.Context(), newFlow); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn errExpired.WithFlow(newFlow), nil\n}\n\nfunc (s *ErrorHandler) WriteFlowError(\n\tw http.ResponseWriter,\n\tr *http.Request,\n\tf *Flow,\n\tct identity.CredentialsType,\n\tgroup node.UiNodeGroup,\n\terr error,\n) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.flow.registration.ErrorHandler.WriteFlowError\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.String(\"error\", err.Error()),\n\t\t))\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\tif dup := new(identity.ErrDuplicateCredentials); errors.As(err, &dup) {\n\t\terr = schema.NewDuplicateCredentialsError(dup)\n\t}\n\n\tlogger := s.d.Logger().\n\t\tWithError(err).\n\t\tWithRequest(r).\n\t\tWithField(\"registration_flow\", f.ToLoggerField())\n\n\tlogger.Info(\"Encountered self-service flow error.\")\n\n\tif f == nil {\n\t\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), uuid.Nil, \"\", \"\", err))\n\t\ts.forward(w, r, nil, err)\n\t\treturn\n\t}\n\tspan.SetAttributes(attribute.String(\"flow_id\", f.ID.String()))\n\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), f.ID, string(f.Type), ct.String(), err))\n\n\tif expired, inner := s.PrepareReplacementForExpiredFlow(w, r, f, err); inner != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t} else if expired != nil {\n\t\tif f.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, expired)\n\t\t} else {\n\t\t\thttp.Redirect(w, r, expired.GetFlow().AppendTo(s.d.Config().SelfServiceFlowRegistrationUI(r.Context())).String(), http.StatusSeeOther)\n\t\t}\n\t\treturn\n\t}\n\n\tf.UI.ResetMessages()\n\tif err := f.UI.ParseError(group, err); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tds, err := f.IdentitySchema.URL(r.Context(), s.d.Config())\n\tif err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tif err := SortNodes(r.Context(), f.UI.Nodes, ds.String()); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tif err := s.d.RegistrationFlowPersister().UpdateRegistrationFlow(r.Context(), f); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tif f.Type == flow.TypeBrowser && !x.IsJSONRequest(r) {\n\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowRegistrationUI(r.Context())).String(), http.StatusFound)\n\t\treturn\n\t}\n\tif _, hasCode, _ := s.d.SessionTokenExchangePersister().CodeForFlow(r.Context(), f.ID); group == node.OpenIDConnectGroup && f.Type == flow.TypeAPI && hasCode {\n\t\thttp.Redirect(w, r, f.ReturnTo, http.StatusSeeOther)\n\t\treturn\n\t}\n\n\tupdatedFlow, innerErr := s.d.RegistrationFlowPersister().GetRegistrationFlow(r.Context(), f.ID)\n\tif innerErr != nil {\n\t\ts.forward(w, r, updatedFlow, innerErr)\n\t\treturn\n\t}\n\n\t// If the flow has an OAuth2LoginChallenge, we try to fetch the login request from Hydra because we return the flow object as JSON.\n\tif updatedFlow.OAuth2LoginChallenge != \"\" {\n\t\thlr, err := s.d.Hydra().GetLoginRequest(ctx, string(updatedFlow.OAuth2LoginChallenge))\n\t\tif err != nil {\n\t\t\t// We don't redirect back to the third party on errors because Hydra doesn't\n\t\t\t// give us the 3rd party return_uri when it redirects to the login UI.\n\t\t\ts.forward(w, r, updatedFlow, err)\n\t\t\treturn\n\t\t}\n\t\tupdatedFlow.HydraLoginRequest = hlr\n\t}\n\n\ts.d.Writer().WriteCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), updatedFlow)\n}\n\nfunc (s *ErrorHandler) forward(w http.ResponseWriter, r *http.Request, rr *Flow, err error) {\n\tif rr == nil {\n\t\tif x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tif rr.Type == flow.TypeAPI {\n\t\ts.d.Writer().WriteErrorCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), err)\n\t} else {\n\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/registration/error_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration_test\n\nimport (\n\t\"context\"\n\t\"io\"\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/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\ntype opts struct {\n\toauth2LoginChallenge string\n}\n\nfunc withLoginChallenge(challenge string) func(*opts) {\n\treturn func(o *opts) {\n\t\to.oauth2LoginChallenge = challenge\n\t}\n}\n\nfunc TestHandleError(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeySelfServiceRegistrationEnabled, true),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/login.schema.json\")),\n\t)\n\treg.SetHydra(hydra.NewFake())\n\n\tpublic, _ := testhelpers.NewKratosServer(t, reg)\n\n\trouter := http.NewServeMux()\n\tts := httptest.NewServer(router)\n\tt.Cleanup(ts.Close)\n\n\ttesthelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\ttesthelpers.NewErrorTestServer(t, reg)\n\n\th := reg.RegistrationFlowErrorHandler()\n\tsdk := testhelpers.NewSDKClient(public)\n\n\tvar registrationFlow *registration.Flow\n\tvar flowError error\n\tvar group node.UiNodeGroup\n\trouter.HandleFunc(\"GET /error\", func(w http.ResponseWriter, r *http.Request) {\n\t\th.WriteFlowError(w, r, registrationFlow, \"\", group, flowError)\n\t})\n\n\treset := func() {\n\t\tregistrationFlow = nil\n\t\tflowError = nil\n\t\tgroup = \"\"\n\t}\n\n\tnewFlow := func(t *testing.T, ttl time.Duration, ft flow.Type, options ...func(*opts)) *registration.Flow {\n\t\treq := &http.Request{URL: urlx.ParseOrPanic(\"/\")}\n\t\tf, err := registration.NewFlow(conf, ttl, \"csrf_token\", req, ft)\n\t\trequire.NoError(t, err)\n\t\topts := &opts{}\n\t\tfor _, o := range options {\n\t\t\to(opts)\n\t\t}\n\t\tif opts.oauth2LoginChallenge != \"\" {\n\t\t\tf.OAuth2LoginChallenge = sqlxx.NullString(opts.oauth2LoginChallenge)\n\t\t}\n\t\tfor _, s := range reg.RegistrationStrategies(context.Background()) {\n\t\t\tvar populateErr error\n\t\t\tswitch strategy := s.(type) {\n\t\t\tcase registration.FormHydrator:\n\t\t\t\tswitch {\n\t\t\t\tcase conf.SelfServiceFlowRegistrationTwoSteps(ctx):\n\t\t\t\t\tpopulateErr = strategy.PopulateRegistrationMethodProfile(req, f)\n\t\t\t\tdefault:\n\t\t\t\t\tpopulateErr = strategy.PopulateRegistrationMethod(req, f)\n\t\t\t\t}\n\t\t\tcase registration.UnifiedFormHydrator:\n\t\t\t\tpopulateErr = strategy.PopulateRegistrationMethod(req, f)\n\t\t\tdefault:\n\t\t\t\tpopulateErr = errors.WithStack(x.PseudoPanic.WithReasonf(\"A registration strategy was expected to implement one of the interfaces UnifiedFormHydrator or FormHydrator but did not.\"))\n\t\t\t}\n\t\t\trequire.NoError(t, populateErr)\n\t\t}\n\n\t\trequire.NoError(t, reg.RegistrationFlowPersister().CreateRegistrationFlow(context.Background(), f))\n\t\treturn f\n\t}\n\n\texpectErrorUI := func(t *testing.T) (interface{}, *http.Response) {\n\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\trequire.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tsse, _, err := sdk.FrontendAPI.GetFlowError(context.Background()).Id(res.Request.URL.Query().Get(\"id\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\treturn sse.Error, nil\n\t}\n\n\tanHourAgo := time.Now().Add(-time.Hour)\n\n\tt.Run(\"case=error with nil flow defaults to error ui redirect\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tgroup = node.PasswordGroup\n\n\t\tsse, _ := expectErrorUI(t)\n\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t})\n\n\tt.Run(\"case=error with nil flow detects application/json\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tgroup = node.PasswordGroup\n\n\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tassert.Contains(t, res.Header.Get(\"Content-Type\"), \"application/json\")\n\t\tassert.NotContains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, string(body), \"system error\")\n\t})\n\n\tfor _, tc := range []struct {\n\t\tn string\n\t\tt flow.Type\n\t}{\n\t\t{\"api\", flow.TypeAPI},\n\t\t{\"spa\", flow.TypeBrowser},\n\t} {\n\t\tt.Run(\"flow=\"+tc.n, func(t *testing.T) {\n\t\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tregistrationFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\t\tgroup = node.PasswordGroup\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusGone, res.StatusCode, \"%+v\\n\\t%s\", res.Request, body)\n\n\t\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.GetBytes(body, \"use_flow_id\").String())\n\t\t\t\tassert.Equal(t, text.ErrIDSelfServiceFlowExpired, gjson.GetBytes(body, \"error.id\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tregistrationFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\t\tgroup = node.PasswordGroup\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(gjson.GetBytes(body, \"ui.messages.0.id\").Int()), \"%s\", body)\n\t\t\t\tassert.Equal(t, registrationFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tregistrationFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\t\tgroup = node.PasswordGroup\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.JSONEq(t, x.MustEncodeJSON(t, flowError), gjson.GetBytes(body, \"error\").Raw)\n\t\t\t})\n\n\t\t\tt.Run(\"case=refuses to parse oauth2 login challenge when Hydra is not configured\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tregistrationFlow = newFlow(t, time.Minute, flow.TypeBrowser, withLoginChallenge(hydra.FakeInvalidLoginChallenge))\n\t\t\t\tgroup = node.PasswordGroup\n\t\t\t\tflowError = herodot.ErrBadRequest.WithReason(\"missing field 'password' fake error\")\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\trequire.Equal(t, \"Unable to get OAuth 2.0 Login Challenge.\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=fetches hydra login request\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, \"https://fake-hydra\")\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, nil)\n\t\t\t\t})\n\n\t\t\t\tregistrationFlow = newFlow(t, time.Minute, flow.TypeBrowser, withLoginChallenge(hydra.FakeValidLoginChallenge))\n\t\t\t\tgroup = node.PasswordGroup\n\t\t\t\tflowError = herodot.ErrBadRequest.WithReason(\"missing field 'password' fake error\")\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\trequire.NotEmpty(t, gjson.GetBytes(body, \"oauth2_login_request\").Value(), \"%s\", body)\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\texpectRegistrationUI := func(t *testing.T) (*registration.Flow, *http.Response) {\n\t\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowRegistrationUI(ctx).String()+\"?flow=\")\n\n\t\t\trf, err := reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), uuid.FromStringOrNil(res.Request.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t\treturn rf, res\n\t\t}\n\n\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tregistrationFlow = &registration.Flow{Type: flow.TypeBrowser}\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\tgroup = node.PasswordGroup\n\n\t\t\tlf, _ := expectRegistrationUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1)\n\t\t\tassert.Equal(t, int(text.ErrorValidationRegistrationFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t})\n\n\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tregistrationFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\tgroup = node.PasswordGroup\n\n\t\t\tlf, _ := expectRegistrationUI(t)\n\t\t\trequire.NotEmpty(t, lf.UI, x.MustEncodeJSON(t, lf))\n\t\t\trequire.Len(t, lf.UI.Messages, 1, x.MustEncodeJSON(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(lf.UI.Messages[0].ID), x.MustEncodeJSON(t, lf))\n\t\t})\n\n\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tregistrationFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\tgroup = node.PasswordGroup\n\n\t\t\tsse, _ := expectErrorUI(t)\n\t\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/registration/fixtures/sort/1.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.foobar\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.username\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\",\n      \"value\": \"YTc3djZwaWpsZTFha3UyNHRlMDMyaTRxaHMxMWVmcmk=\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/flow/registration/fixtures/sort.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"username\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"foobar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/registration/flow.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\n\thydraclientgo \"github.com/ory/hydra-client-go/v2\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\n// swagger:model registrationFlow\ntype Flow struct {\n\t// ID represents the flow's unique ID. When performing the registration flow, this\n\t// represents the id in the registration ui's query parameter: http://<selfservice.flows.registration.ui_url>/?flow=<id>\n\t//\n\t// required: true\n\tID uuid.UUID `json:\"id\" faker:\"-\" db:\"id\"`\n\n\t// Ory OAuth 2.0 Login Challenge.\n\t//\n\t// This value is set using the `login_challenge` query parameter of the registration and login endpoints.\n\t// If set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\n\tOAuth2LoginChallenge sqlxx.NullString `json:\"oauth2_login_challenge,omitempty\" faker:\"-\" db:\"oauth2_login_challenge_data\"`\n\n\t// HydraLoginRequest is an optional field whose presence indicates that Kratos\n\t// is being used as an identity provider in a Hydra OAuth2 flow. Kratos\n\t// populates this field by retrieving its value from Hydra and it is used by\n\t// the login and consent UIs.\n\tHydraLoginRequest *hydraclientgo.OAuth2LoginRequest `json:\"oauth2_login_request,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// Type represents the flow's type which can be either \"api\" or \"browser\", depending on the flow interaction.\n\t//\n\t// required: true\n\tType flow.Type `json:\"type\" db:\"type\" faker:\"flow_type\"`\n\n\t// ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\n\t// a new flow has to be initiated.\n\t//\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the flow occurred.\n\t//\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// InternalContext stores internal context used by internals - for example MFA keys.\n\tInternalContext sqlxx.JSONRawMessage `db:\"internal_context\" json:\"-\" faker:\"-\"`\n\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used\n\t// to forward information contained in the URL's path or query for example.\n\t//\n\t// required: true\n\tRequestURL string `json:\"request_url\" faker:\"url\" db:\"request_url\"`\n\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo string `json:\"return_to,omitempty\" db:\"-\"`\n\n\t// ReturnToVerification contains the redirect URL for the verification flow.\n\tReturnToVerification string `json:\"-\" db:\"-\"`\n\n\t// Active, if set, contains the registration method that is being used. It is initially\n\t// not set.\n\tActive identity.CredentialsType `json:\"active,omitempty\" faker:\"identity_credentials_type\" db:\"active_method\"`\n\n\t// UI contains data which must be shown in the user interface.\n\t//\n\t// required: true\n\tUI *container.Container `json:\"ui\" db:\"ui\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\n\t// CSRFToken contains the anti-csrf token associated with this flow. Only set for browser flows.\n\tCSRFToken      string        `json:\"-\" db:\"csrf_token\"`\n\tNID            uuid.UUID     `json:\"-\" faker:\"-\" db:\"nid\"`\n\tOrganizationID uuid.NullUUID `json:\"organization_id,omitempty\"  faker:\"-\" db:\"organization_id\"`\n\n\t// TransientPayload is used to pass data from the registration to a webhook\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// Contains a list of actions, that could follow this flow\n\t//\n\t// It can, for example, contain a reference to the verification flow, created as part of the user's\n\t// registration.\n\tContinueWithItems []flow.ContinueWith `json:\"-\" db:\"-\" faker:\"-\" `\n\n\t// SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the flow has been completed.\n\t// This is only set if the client has requested a session token exchange code, and if the flow is of type \"api\",\n\t// and only on creating the flow.\n\tSessionTokenExchangeCode string `json:\"session_token_exchange_code,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// State represents the state of this request:\n\t//\n\t// - choose_method: ask the user to choose a method (e.g. registration with email)\n\t// - sent_email: the email has been sent to the user\n\t// - passed_challenge: the request was successful and the registration challenge was passed.\n\t// required: true\n\tState State `json:\"state\" faker:\"-\" db:\"state\"`\n\n\t// only used internally\n\tIDToken string `json:\"-\" faker:\"-\" db:\"-\"`\n\t// Only used internally\n\tRawIDTokenNonce string `json:\"-\" db:\"-\"`\n\n\t// IdentitySchema optionally holds the ID of the identity schema that is used\n\t// for this flow. This value can be set by the user when creating the flow and\n\t// should be retained when the flow is saved or converted to another flow.\n\tIdentitySchema flow.IdentitySchema `json:\"identity_schema,omitempty\" faker:\"-\" db:\"identity_schema_id\"`\n}\n\nvar _ flow.Flow = new(Flow)\n\nfunc NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, ft flow.Type) (*Flow, error) {\n\tnow := time.Now().UTC()\n\tid := x.NewUUID()\n\n\t// Pre-validate the return to URL which is contained in the HTTP request.\n\trequestURL := x.RequestURL(r).String()\n\t_, err := redir.SecureRedirectTo(r,\n\t\tconf.SelfServiceBrowserDefaultReturnTo(r.Context()),\n\t\tredir.SecureRedirectUseSourceURL(requestURL),\n\t\tredir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\thlc, err := hydra.GetLoginChallengeID(conf, r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tidentitySchema := \"\"\n\tif requestedSchema := r.URL.Query().Get(\"identity_schema\"); requestedSchema != \"\" {\n\t\tidentitySchema, err = conf.SelfServiceFlowIdentitySchema(r.Context(), requestedSchema)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn &Flow{\n\t\tID:                   id,\n\t\tOAuth2LoginChallenge: hlc,\n\t\tExpiresAt:            now.Add(exp),\n\t\tIssuedAt:             now,\n\t\tRequestURL:           requestURL,\n\t\tUI: &container.Container{\n\t\t\tMethod: \"POST\",\n\t\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(conf.SelfPublicURL(r.Context()), RouteSubmitFlow), id).String(),\n\t\t},\n\t\tCSRFToken:       csrf,\n\t\tType:            ft,\n\t\tInternalContext: []byte(\"{}\"),\n\t\tState:           flow.StateChooseMethod,\n\t\tIdentitySchema:  flow.IdentitySchema(identitySchema),\n\t}, nil\n}\n\nfunc (Flow) TableName() string                                { return \"selfservice_registration_flows\" }\nfunc (f Flow) GetID() uuid.UUID                               { return f.ID }\nfunc (f *Flow) AppendTo(src *url.URL) *url.URL                { return flow.AppendFlowTo(src, f.ID) }\nfunc (f *Flow) GetType() flow.Type                            { return f.Type }\nfunc (f *Flow) GetRequestURL() string                         { return f.RequestURL }\nfunc (f *Flow) GetInternalContext() sqlxx.JSONRawMessage      { return f.InternalContext }\nfunc (f *Flow) SetInternalContext(bytes sqlxx.JSONRawMessage) { f.InternalContext = bytes }\nfunc (f *Flow) GetUI() *container.Container                   { return f.UI }\nfunc (f *Flow) GetState() State                               { return f.State }\nfunc (Flow) GetFlowName() flow.FlowName                       { return flow.RegistrationFlow }\nfunc (f *Flow) SetState(state State)                          { f.State = state }\nfunc (f *Flow) GetTransientPayload() json.RawMessage          { return f.TransientPayload }\nfunc (f *Flow) SetReturnToVerification(to string)             { f.ReturnToVerification = to }\nfunc (f *Flow) GetOAuth2LoginChallenge() sqlxx.NullString     { return f.OAuth2LoginChallenge }\n\nfunc (f *Flow) Valid() error {\n\tif f.ExpiresAt.Before(time.Now()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\treturn nil\n}\n\nfunc (f *Flow) EnsureInternalContext() {\n\tif !gjson.ParseBytes(f.InternalContext).IsObject() {\n\t\tf.InternalContext = []byte(\"{}\")\n\t}\n}\n\nfunc (f Flow) MarshalJSON() ([]byte, error) {\n\ttype local Flow\n\tf.SetReturnTo()\n\treturn json.Marshal(local(f))\n}\n\nfunc (f *Flow) SetReturnTo() {\n\t// Return to is already set, do not overwrite it.\n\tif len(f.ReturnTo) > 0 {\n\t\treturn\n\t}\n\tif u, err := url.Parse(f.RequestURL); err == nil {\n\t\tf.ReturnTo = u.Query().Get(\"return_to\")\n\t}\n}\n\nfunc (f *Flow) AfterFind(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\nfunc (f *Flow) AfterSave(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\nfunc (f *Flow) AddContinueWith(c flow.ContinueWith) {\n\tf.ContinueWithItems = append(f.ContinueWithItems, c)\n}\n\nfunc (f *Flow) ContinueWith() []flow.ContinueWith { return f.ContinueWithItems }\n\nfunc (f *Flow) SecureRedirectToOpts(ctx context.Context, cfg config.Provider) (opts []redir.SecureRedirectOption) {\n\treturn []redir.SecureRedirectOption{\n\t\tredir.SecureRedirectReturnTo(f.ReturnTo),\n\t\tredir.SecureRedirectUseSourceURL(f.RequestURL),\n\t\tredir.SecureRedirectAllowURLs(cfg.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(cfg.Config().SelfPublicURL(ctx)),\n\t\tredir.SecureRedirectOverrideDefaultReturnTo(cfg.Config().SelfServiceFlowRegistrationReturnTo(ctx, f.Active.String())),\n\t}\n}\n\nfunc (f *Flow) ToLoggerField() map[string]any {\n\tif f == nil {\n\t\treturn map[string]any{}\n\t}\n\treturn map[string]any{\n\t\t\"id\":          f.ID.String(),\n\t\t\"return_to\":   f.ReturnTo,\n\t\t\"request_url\": f.RequestURL,\n\t\t\"active\":      f.Active,\n\t\t\"Type\":        f.Type,\n\t\t\"nid\":         f.NID,\n\t\t\"state\":       f.State,\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/registration/flow_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration_test\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/jsonx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestFakeFlow(t *testing.T) {\n\tvar r registration.Flow\n\trequire.NoError(t, faker.FakeData(&r))\n\n\tassert.Equal(t, uuid.Nil, r.ID)\n\tassert.NotEmpty(t, r.IssuedAt)\n\tassert.NotEmpty(t, r.ExpiresAt)\n\tassert.NotEmpty(t, r.RequestURL)\n\tassert.NotEmpty(t, r.Active)\n}\n\nfunc TestNewFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, _ := pkg.NewFastRegistryWithMocks(t)\n\tt.Run(\"case=0\", func(t *testing.T) {\n\t\tr, err := registration.NewFlow(conf, 0, \"csrf\", &http.Request{\n\t\t\tURL:  urlx.ParseOrPanic(\"/\"),\n\t\t\tHost: \"ory.sh\", TLS: &tls.ConnectionState{},\n\t\t}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, r.IssuedAt, r.ExpiresAt)\n\t\tassert.Equal(t, flow.TypeBrowser, r.Type)\n\t\tassert.Equal(t, \"https://ory.sh/\", r.RequestURL)\n\t})\n\n\tt.Run(\"type=return_to\", func(t *testing.T) {\n\t\t_, err := registration.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=https://not-allowed/foobar\"}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.Error(t, err)\n\n\t\t_, err = registration.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=\" + urlx.AppendPaths(conf.SelfPublicURL(ctx), \"/self-service/login/browser\").String()}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t})\n\n\tt.Run(\"case=1\", func(t *testing.T) {\n\t\tr, err := registration.NewFlow(conf, 0, \"csrf\", &http.Request{\n\t\t\tURL:  urlx.ParseOrPanic(\"/?refresh=true\"),\n\t\t\tHost: \"ory.sh\",\n\t\t}, flow.TypeAPI)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, r.IssuedAt, r.ExpiresAt)\n\t\tassert.Equal(t, flow.TypeAPI, r.Type)\n\t\tassert.Equal(t, \"http://ory.sh/?refresh=true\", r.RequestURL)\n\t})\n\n\tt.Run(\"case=2\", func(t *testing.T) {\n\t\tr, err := registration.NewFlow(conf, 0, \"csrf\", &http.Request{\n\t\t\tURL:  urlx.ParseOrPanic(\"https://ory.sh/\"),\n\t\t\tHost: \"ory.sh\",\n\t\t}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, \"https://ory.sh/\", r.RequestURL)\n\t})\n\n\tt.Run(\"should parse login_challenge when Hydra is configured\", func(t *testing.T) {\n\t\t_, err := registration.NewFlow(conf, 0, \"csrf\", &http.Request{URL: urlx.ParseOrPanic(\"https://ory.sh/?login_challenge=badee1\"), Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.Error(t, err)\n\n\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, \"https://hydra\")\n\n\t\tr, err := registration.NewFlow(conf, 0, \"csrf\", &http.Request{URL: urlx.ParseOrPanic(\"https://ory.sh/?login_challenge=8aadcb8fc1334186a84c4da9813356d9\"), Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, \"8aadcb8fc1334186a84c4da9813356d9\", string(r.OAuth2LoginChallenge))\n\t})\n}\n\nfunc TestFlow(t *testing.T) {\n\tr := &registration.Flow{ID: x.NewUUID()}\n\tassert.Equal(t, r.ID, r.GetID())\n\n\tt.Run(\"case=expired\", func(t *testing.T) {\n\t\tfor _, tc := range []struct {\n\t\t\tr     *registration.Flow\n\t\t\tvalid bool\n\t\t}{\n\t\t\t{\n\t\t\t\tr:     &registration.Flow{ExpiresAt: time.Now().Add(time.Hour), IssuedAt: time.Now().Add(-time.Minute)},\n\t\t\t\tvalid: true,\n\t\t\t},\n\t\t\t{r: &registration.Flow{ExpiresAt: time.Now().Add(-time.Hour), IssuedAt: time.Now().Add(-time.Minute)}},\n\t\t} {\n\t\t\tif tc.valid {\n\t\t\t\trequire.NoError(t, tc.r.Valid())\n\t\t\t} else {\n\t\t\t\trequire.Error(t, tc.r.Valid())\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestGetType(t *testing.T) {\n\tfor _, ft := range []flow.Type{\n\t\tflow.TypeAPI,\n\t\tflow.TypeBrowser,\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", ft), func(t *testing.T) {\n\t\t\tr := &registration.Flow{Type: ft}\n\t\t\tassert.Equal(t, ft, r.GetType())\n\t\t})\n\t}\n}\n\nfunc TestGetRequestURL(t *testing.T) {\n\texpectedURL := \"http://foo/bar/baz\"\n\tf := &registration.Flow{RequestURL: expectedURL}\n\tassert.Equal(t, expectedURL, f.GetRequestURL())\n}\n\nfunc TestFlowEncodeJSON(t *testing.T) {\n\tassert.EqualValues(t, \"\", gjson.Get(jsonx.TestMarshalJSONString(t, &registration.Flow{RequestURL: \"https://foo.bar?foo=bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, &registration.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, registration.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n}\n\nfunc TestFlowDontOverrideReturnTo(t *testing.T) {\n\tf := &registration.Flow{ReturnTo: \"/foo\"}\n\tf.SetReturnTo()\n\tassert.Equal(t, \"/foo\", f.ReturnTo)\n\n\tf = &registration.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}\n\tf.SetReturnTo()\n\tassert.Equal(t, \"/bar\", f.ReturnTo)\n}\n"
  },
  {
    "path": "selfservice/flow/registration/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/herodot\"\n\thydraclientgo \"github.com/ory/hydra-client-go/v2\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst (\n\tRouteInitBrowserFlow = \"/self-service/registration/browser\"\n\tRouteInitAPIFlow     = \"/self-service/registration/api\"\n\n\tRouteGetFlow = \"/self-service/registration/flows\"\n\n\tRouteSubmitFlow = \"/self-service/registration\"\n)\n\ntype (\n\thandlerDependencies interface {\n\t\tconfig.Provider\n\t\terrorx.ManagementProvider\n\t\thydra.Provider\n\t\tsession.HandlerProvider\n\t\tsession.ManagementProvider\n\t\thttpx.WriterProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tnosurfx.CSRFProvider\n\t\tStrategyProvider\n\t\tHookExecutorProvider\n\t\tFlowPersistenceProvider\n\t\tErrorHandlerProvider\n\t\tsessiontokenexchange.PersistenceProvider\n\t\tlogrusx.Provider\n\t}\n\tHandlerProvider interface {\n\t\tRegistrationHandler() *Handler\n\t}\n\tHandler struct {\n\t\td handlerDependencies\n\t}\n)\n\nfunc NewHandler(d handlerDependencies) *Handler { return &Handler{d: d} }\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\th.d.CSRFHandler().IgnorePath(RouteInitAPIFlow)\n\th.d.CSRFHandler().IgnorePath(RouteSubmitFlow)\n\n\tpublic.GET(RouteInitBrowserFlow, h.createBrowserRegistrationFlow)\n\tpublic.GET(RouteInitAPIFlow, h.d.SessionHandler().IsNotAuthenticated(h.createNativeRegistrationFlow,\n\t\tsession.RespondWithJSONErrorOnAuthenticated(h.d.Writer(), errors.WithStack(ErrAlreadyLoggedIn))))\n\n\tpublic.GET(RouteGetFlow, h.getRegistrationFlow)\n\n\tpublic.POST(RouteSubmitFlow, h.d.SessionHandler().IsNotAuthenticated(h.updateRegistrationFlow, h.onAuthenticated))\n\tpublic.GET(RouteSubmitFlow, h.d.SessionHandler().IsNotAuthenticated(h.updateRegistrationFlow, h.onAuthenticated))\n}\n\nfunc (h *Handler) onAuthenticated(w http.ResponseWriter, r *http.Request) {\n\thandler := session.RedirectOnAuthenticated(h.d)\n\tif x.IsJSONRequest(r) {\n\t\thandler = session.RespondWithJSONErrorOnAuthenticated(h.d.Writer(), ErrAlreadyLoggedIn)\n\t}\n\n\thandler(w, r)\n}\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n}\n\ntype FlowOption func(f *Flow)\n\nfunc WithFlowReturnTo(returnTo string) FlowOption {\n\treturn func(f *Flow) {\n\t\tf.ReturnTo = returnTo\n\t}\n}\n\nfunc WithFlowOAuth2LoginChallenge(loginChallenge string) FlowOption {\n\treturn func(f *Flow) {\n\t\tf.OAuth2LoginChallenge = sqlxx.NullString(loginChallenge)\n\t}\n}\n\nfunc (h *Handler) NewRegistrationFlow(w http.ResponseWriter, r *http.Request, ft flow.Type, opts ...FlowOption) (*Flow, error) {\n\tif !h.d.Config().SelfServiceFlowRegistrationEnabled(r.Context()) {\n\t\treturn nil, errors.WithStack(ErrRegistrationDisabled)\n\t}\n\n\tf, err := NewFlow(h.d.Config(), h.d.Config().SelfServiceFlowRegistrationRequestLifespan(r.Context()), h.d.GenerateCSRFToken(r), r, ft)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, o := range opts {\n\t\to(f)\n\t}\n\n\tif ft == flow.TypeAPI && r.URL.Query().Get(\"return_session_token_exchange_code\") == \"true\" {\n\t\te, err := h.d.SessionTokenExchangePersister().CreateSessionTokenExchanger(r.Context(), f.ID)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err))\n\t\t}\n\t\tf.SessionTokenExchangeCode = e.InitCode\n\t}\n\n\tfor _, s := range h.d.RegistrationStrategies(r.Context(), PrepareOrganizations(r, f)...) {\n\t\tvar populateErr error\n\n\t\tswitch strategy := s.(type) {\n\t\tcase FormHydrator:\n\t\t\tif h.d.Config().SelfServiceFlowRegistrationTwoSteps(r.Context()) {\n\t\t\t\tpopulateErr = strategy.PopulateRegistrationMethodProfile(r, f)\n\t\t\t} else {\n\t\t\t\tpopulateErr = strategy.PopulateRegistrationMethod(r, f)\n\t\t\t}\n\t\tcase UnifiedFormHydrator:\n\t\t\tpopulateErr = strategy.PopulateRegistrationMethod(r, f)\n\t\tdefault:\n\t\t\tpopulateErr = errors.WithStack(x.PseudoPanic.WithReasonf(\"A registration strategy was expected to implement one of the interfaces UnifiedFormHydrator or FormHydrator but did not.\"))\n\t\t}\n\n\t\tif populateErr != nil {\n\t\t\treturn nil, populateErr\n\t\t}\n\t}\n\n\tds, err := f.IdentitySchema.URL(r.Context(), h.d.Config())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := SortNodes(r.Context(), f.UI.Nodes, ds.String()); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := h.d.RegistrationExecutor().PreRegistrationHook(w, r, f); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := h.d.RegistrationFlowPersister().CreateRegistrationFlow(r.Context(), f); err != nil {\n\t\treturn nil, err\n\t}\n\n\tspan := trace.SpanFromContext(r.Context())\n\tspan.AddEvent(events.NewRegistrationInitiated(r.Context(), f.ID, string(ft), f.OrganizationID))\n\n\treturn f, nil\n}\n\nfunc (h *Handler) FromOldFlow(w http.ResponseWriter, r *http.Request, of Flow) (*Flow, error) {\n\tnf, err := h.NewRegistrationFlow(w, r, of.Type)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnf.RequestURL = of.RequestURL\n\tnf.IdentitySchema = of.IdentitySchema\n\treturn nf, nil\n}\n\n// swagger:route GET /self-service/registration/api frontend createNativeRegistrationFlow\n//\n// # Create Registration Flow for Native Apps\n//\n// This endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\n//\n// If a valid provided session cookie or session token is provided, a 400 Bad Request error\n// will be returned unless the URL query parameter `?refresh=true` is set.\n//\n// To fetch an existing registration flow call `/self-service/registration/flows?flow=<flow_id>`.\n//\n// You MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n// Pages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n// you vulnerable to a variety of CSRF attacks.\n//\n// In the case of an error, the `error.id` of the JSON response body can be one of:\n//\n// - `session_already_available`: The user is already signed in.\n// - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n//\n// This endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n//\n// More information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: registrationFlow\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createNativeRegistrationFlow(w http.ResponseWriter, r *http.Request) {\n\ta, err := h.NewRegistrationFlow(w, r, flow.TypeAPI)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.d.Writer().Write(w, r, a)\n}\n\n// Create Native Registration Flow Parameters\n//\n// swagger:parameters createNativeRegistrationFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createNativeRegistrationFlow struct {\n\t// EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token\n\t// after the login flow has been completed.\n\t//\n\t// in: query\n\tEnableSessionTokenExchangeCode bool `json:\"return_session_token_exchange_code\"`\n\n\t// The URL to return the browser to after the flow was completed.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n\n\t// An optional organization ID that should be used to register this user.\n\t// This parameter is only effective in the Ory Network.\n\t//\n\t// required: false\n\t// in: query\n\tOrganization string `json:\"organization\"`\n\n\t// An optional identity schema to use for the registration flow.\n\t//\n\t// required: false\n\t// in: query\n\tIdentitySchema string `json:\"identity_schema\"`\n}\n\n// Create Browser Registration Flow Parameters\n//\n// swagger:parameters createBrowserRegistrationFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createBrowserRegistrationFlow struct {\n\t// The URL to return the browser to after the flow was completed.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n\n\t// Ory OAuth 2.0 Login Challenge.\n\t//\n\t// If set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\n\t//\n\t// The value for this parameter comes from `login_challenge` URL Query parameter sent to your\n\t// application (e.g. `/registration?login_challenge=abcde`).\n\t//\n\t// This feature is compatible with Ory Hydra when not running on the Ory Network.\n\t//\n\t// required: false\n\t// in: query\n\tLoginChallenge string `json:\"login_challenge\"`\n\n\t// The URL to return the browser to after the verification flow was completed.\n\t//\n\t// After the registration flow is completed, the user will be sent a verification email.\n\t// Upon completing the verification flow, this URL will be used to override the default\n\t// `selfservice.flows.verification.after.default_redirect_to` value.\n\t//\n\t// required: false\n\t// in: query\n\tAfterVerificationReturnTo string `json:\"after_verification_return_to\"`\n\n\t// An optional organization ID that should be used to register this user.\n\t// This parameter is only effective in the Ory Network.\n\t//\n\t// required: false\n\t// in: query\n\tOrganization string `json:\"organization\"`\n\n\t// An optional identity schema to use for the registration flow.\n\t//\n\t// required: false\n\t// in: query\n\tIdentitySchema string `json:\"identity_schema\"`\n}\n\n// swagger:route GET /self-service/registration/browser frontend createBrowserRegistrationFlow\n//\n// # Create Registration Flow for Browsers\n//\n// This endpoint initializes a browser-based user registration flow. This endpoint will set the appropriate\n// cookies and anti-CSRF measures required for browser-based flows.\n//\n// If this endpoint is opened as a link in the browser, it will be redirected to\n// `selfservice.flows.registration.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\n// exists already, the browser will be redirected to `urls.default_redirect_url`.\n//\n// If this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n// case of an error, the `error.id` of the JSON response body can be one of:\n//\n// - `session_already_available`: The user is already signed in.\n// - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n// - `security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n//\n// If this endpoint is called via an AJAX request, the response contains the registration flow without a redirect.\n//\n// This endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n//\n// More information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n//\n//\tSchemes: http, https\n//\n//\tProduces:\n//\t- application/json\n//\n//\tResponses:\n//\t  200: registrationFlow\n//\t  303: emptyResponse\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createBrowserRegistrationFlow(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\n\ta, err := h.NewRegistrationFlow(w, r, flow.TypeBrowser)\n\tif err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\treturn\n\t}\n\n\tvar (\n\t\thydraLoginRequest   *hydraclientgo.OAuth2LoginRequest\n\t\thydraLoginChallenge sqlxx.NullString\n\t)\n\tif r.URL.Query().Has(\"login_challenge\") {\n\t\tvar err error\n\t\thydraLoginChallenge, err = hydra.GetLoginChallengeID(h.d.Config(), r)\n\t\tif err != nil {\n\t\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\t\treturn\n\t\t}\n\n\t\thydraLoginRequest, err = h.d.Hydra().GetLoginRequest(ctx, hydraLoginChallenge.String())\n\t\tif err != nil {\n\t\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\t\treturn\n\t\t}\n\n\t\t// on OAuth2 flows, we need to use the RequestURL\n\t\t// as the ReturnTo URL.\n\t\t// This is because a user might want to switch between\n\t\t// different flows, such as login to registration and login to recovery.\n\t\t// After completing a complex flow, such as recovery, we want the user\n\t\t// to be redirected back to the original OAuth2 login flow.\n\t\tif hydraLoginRequest.RequestUrl != \"\" && h.d.Config().OAuth2ProviderOverrideReturnTo(r.Context()) {\n\t\t\tq := r.URL.Query()\n\t\t\t// replace the return_to query parameter\n\t\t\tq.Set(\"return_to\", hydraLoginRequest.RequestUrl)\n\t\t\tr.URL.RawQuery = q.Encode()\n\t\t}\n\t}\n\n\tif sess, err := h.d.SessionManager().FetchFromRequest(ctx, r); err == nil {\n\t\tif hydraLoginRequest != nil {\n\t\t\tif hydraLoginRequest.GetSkip() {\n\t\t\t\trt, err := h.d.Hydra().AcceptLoginRequest(r.Context(),\n\t\t\t\t\thydra.AcceptLoginRequestParams{\n\t\t\t\t\t\tLoginChallenge:        string(hydraLoginChallenge),\n\t\t\t\t\t\tIdentityID:            sess.IdentityID.String(),\n\t\t\t\t\t\tSessionID:             sess.ID.String(),\n\t\t\t\t\t\tAuthenticationMethods: sess.AMR,\n\t\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\treturnTo, err := url.Parse(rt)\n\t\t\t\tif err != nil {\n\t\t\t\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to parse URL: %s\", rt)))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tx.SendFlowCompletedAsRedirectOrJSON(w, r, h.d.Writer(), err, returnTo.String())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// hydra indicates that we cannot skip the login request\n\t\t\t// so we must perform the login flow.\n\t\t\t// we directly go to the login handler from here\n\t\t\t// copy over any query parameters, such as `return_to` and `login_challenge`\n\t\t\tloginURL := urlx.CopyWithQuery(urlx.AppendPaths(h.d.Config().SelfPublicURL(ctx), \"/self-service/login/browser\"), x.RequestURL(r).Query())\n\t\t\thttp.Redirect(\n\t\t\t\tw,\n\t\t\t\tr,\n\t\t\t\tloginURL.String(),\n\t\t\t\thttp.StatusFound,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\tif x.IsJSONRequest(r) {\n\t\t\th.d.Writer().WriteError(w, r, errors.WithStack(ErrAlreadyLoggedIn))\n\t\t\treturn\n\t\t}\n\n\t\treturnTo, redirErr := redir.SecureRedirectTo(r, h.d.Config().SelfServiceBrowserDefaultReturnTo(ctx),\n\t\t\tredir.SecureRedirectAllowSelfServiceURLs(h.d.Config().SelfPublicURL(ctx)),\n\t\t\tredir.SecureRedirectAllowURLs(h.d.Config().SelfServiceBrowserAllowedReturnToDomains(ctx)),\n\t\t)\n\t\tif redirErr != nil {\n\t\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, redirErr)\n\t\t\treturn\n\t\t}\n\n\t\thttp.Redirect(w, r, returnTo.String(), http.StatusSeeOther)\n\t\treturn\n\t}\n\n\tredirTo := a.AppendTo(h.d.Config().SelfServiceFlowRegistrationUI(ctx)).String()\n\tx.SendFlowCompletedAsRedirectOrJSON(w, r, h.d.Writer(), a, redirTo)\n}\n\n// Get Registration Flow Parameters\n//\n// swagger:parameters getRegistrationFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getRegistrationFlow struct {\n\t// The Registration Flow ID\n\t//\n\t// The value for this parameter comes from `flow` URL Query parameter sent to your\n\t// application (e.g. `/registration?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tID string `json:\"id\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// swagger:route GET /self-service/registration/flows frontend getRegistrationFlow\n//\n// # Get Registration Flow\n//\n// This endpoint returns a registration flow's context with, for example, error details and other information.\n//\n// Browser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n// For AJAX requests you must ensure that cookies are included in the request or requests will fail.\n//\n// If you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n// and you need to forward the incoming HTTP Cookie header to this endpoint:\n//\n//\t```js\n//\t// pseudo-code example\n//\trouter.get('/registration', async function (req, res) {\n//\t  const flow = await client.getRegistrationFlow(req.header('cookie'), req.query['flow'])\n//\n//\t  res.render('registration', flow)\n//\t})\n//\t```\n//\n// This request may fail due to several reasons. The `error.id` can be one of:\n//\n// - `session_already_available`: The user is already signed in.\n// - `self_service_flow_expired`: The flow is expired and you should request a new one.\n//\n// More information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: registrationFlow\n//\t  403: errorGeneric\n//\t  404: errorGeneric\n//\t  410: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-low\nfunc (h *Handler) getRegistrationFlow(w http.ResponseWriter, r *http.Request) {\n\tif !h.d.Config().SelfServiceFlowRegistrationEnabled(r.Context()) {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(ErrRegistrationDisabled))\n\t\treturn\n\t}\n\n\tar, err := h.d.RegistrationFlowPersister().GetRegistrationFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"id\")))\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\t// Browser flows must include the CSRF token\n\t//\n\t// Resolves: https://github.com/ory/kratos/issues/1282\n\tif ar.Type == flow.TypeBrowser && !nosurf.VerifyToken(h.d.GenerateCSRFToken(r), ar.CSRFToken) {\n\t\th.d.Writer().WriteError(w, r, nosurfx.CSRFErrorReason(r, h.d))\n\t\treturn\n\t}\n\n\tif ar.ExpiresAt.Before(time.Now()) {\n\t\tif ar.Type == flow.TypeBrowser {\n\t\t\tredirectURL := flow.GetFlowExpiredRedirectURL(r.Context(), h.d.Config(), RouteInitBrowserFlow, ar.ReturnTo)\n\n\t\t\th.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).\n\t\t\t\tWithReason(\"The registration flow has expired. Redirect the user to the registration flow init endpoint to initialize a new registration flow.\").\n\t\t\t\tWithDetail(\"redirect_to\", redirectURL.String()).\n\t\t\t\tWithDetail(\"return_to\", ar.ReturnTo)))\n\t\t\treturn\n\t\t}\n\t\th.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.WithID(text.ErrIDSelfServiceFlowExpired).\n\t\t\tWithReason(\"The registration flow has expired. Call the registration flow init API endpoint to initialize a new registration flow.\").\n\t\t\tWithDetail(\"api\", urlx.AppendPaths(h.d.Config().SelfPublicURL(r.Context()), RouteInitAPIFlow).String())))\n\t\treturn\n\t}\n\n\tif ar.OAuth2LoginChallenge != \"\" {\n\t\thlr, err := h.d.Hydra().GetLoginRequest(r.Context(), string(ar.OAuth2LoginChallenge))\n\t\tif err != nil {\n\t\t\t// We don't redirect back to the third party on errors because Hydra doesn't\n\t\t\t// give us the 3rd party return_uri when it redirects to the login UI.\n\t\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\t\treturn\n\t\t}\n\t\tar.HydraLoginRequest = hlr\n\t}\n\n\th.d.Writer().Write(w, r, ar)\n}\n\n// Update Registration Flow Parameters\n//\n// swagger:parameters updateRegistrationFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateRegistrationFlow struct {\n\t// The Registration Flow ID\n\t//\n\t// The value for this parameter comes from `flow` URL Query parameter sent to your\n\t// application (e.g. `/registration?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tFlow string `json:\"flow\"`\n\n\t// in: body\n\t// required: true\n\tBody updateRegistrationFlowBody\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// Update Registration Request Body\n//\n// swagger:model updateRegistrationFlowBody\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateRegistrationFlowBody struct{}\n\n// swagger:route POST /self-service/registration frontend updateRegistrationFlow\n//\n// # Update Registration Flow\n//\n// Use this endpoint to complete a registration flow by sending an identity's traits and password. This endpoint\n// behaves differently for API and browser flows.\n//\n// API flows expect `application/json` to be sent in the body and respond with\n//   - HTTP 200 and a application/json body with the created identity success - if the session hook is configured the\n//     `session` and `session_token` will also be included;\n//   - HTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\n//   - HTTP 400 on form validation errors.\n//\n// Browser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\n//   - a HTTP 303 redirect to the post/after registration URL or the `return_to` value if it was set and if the registration succeeded;\n//   - a HTTP 303 redirect to the registration UI URL with the flow ID containing the validation errors otherwise.\n//\n// Browser flows with an accept header of `application/json` will not redirect but instead respond with\n//   - HTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\n//   - HTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\n//   - HTTP 400 on form validation errors.\n//\n// If this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\n// case of an error, the `error.id` of the JSON response body can be one of:\n//\n//   - `session_already_available`: The user is already signed in.\n//   - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n//   - `security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n//   - `browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\n//     Most likely used in Social Sign In flows.\n//\n// More information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n//\n//\tSchemes: http, https\n//\n//\tConsumes:\n//\t- application/json\n//\t- application/x-www-form-urlencoded\n//\n//\tProduces:\n//\t- application/json\n//\n//\tResponses:\n//\t  200: successfulNativeRegistration\n//\t  303: emptyResponse\n//\t  400: registrationFlow\n//\t  410: errorGeneric\n//\t  422: errorBrowserLocationChangeRequired\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-high\nfunc (h *Handler) updateRegistrationFlow(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\tctx = semconv.ContextWithAttributes(ctx,\n\t\tattribute.String(events.AttributeKeySelfServiceStrategyUsed.String(), \"registration\"),\n\t\tattribute.String(events.AttributeKeySelfServiceFlowName.String(), \"registration\"),\n\t)\n\tr = r.WithContext(ctx)\n\n\trid, err := flow.GetFlowID(r)\n\tif err != nil {\n\t\th.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, nil, \"\", node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tf, err := h.d.RegistrationFlowPersister().GetRegistrationFlow(r.Context(), rid)\n\tif err != nil {\n\t\th.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, nil, \"\", node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tif _, err := h.d.SessionManager().FetchFromRequest(r.Context(), r); err == nil {\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\thttp.Redirect(w, r, h.d.Config().SelfServiceBrowserDefaultReturnTo(r.Context()).String(), http.StatusSeeOther)\n\t\t\treturn\n\t\t}\n\n\t\th.d.Writer().WriteError(w, r, errors.WithStack(ErrAlreadyLoggedIn))\n\t\treturn\n\t}\n\n\tif err := f.Valid(); err != nil {\n\t\th.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, f, \"\", node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\ti := identity.NewIdentity(f.IdentitySchema.ID(ctx, h.d.Config()))\n\tvar s Strategy\n\tfor _, ss := range h.d.AllRegistrationStrategies() {\n\t\tif err := ss.Register(w, r, f, i); errors.Is(err, flow.ErrStrategyNotResponsible) {\n\t\t\tcontinue\n\t\t} else if errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\treturn\n\t\t} else if err != nil {\n\t\t\th.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, f, ss.ID(), ss.NodeGroup(), err)\n\t\t\treturn\n\t\t}\n\n\t\ts = ss\n\t\tbreak\n\t}\n\n\tif s == nil {\n\t\th.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, f, \"\", node.DefaultGroup, errors.WithStack(schema.NewNoRegistrationStrategyResponsible()))\n\t\treturn\n\t}\n\n\tif err := h.d.RegistrationExecutor().PostRegistrationHook(w, r, s.ID(), \"\", \"\", f, i); err != nil {\n\t\th.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, f, s.ID(), s.NodeGroup(), err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/registration/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/corpx\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/selfservice/strategy/password\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc TestHandlerRedirectOnAuthenticated(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tfakeHydra := hydra.NewFake()\n\treg.SetHydra(fakeHydra)\n\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tts, _ := testhelpers.NewKratosServerWithRouters(t, reg, router, httprouterx.NewTestRouterAdminWithPrefix(t))\n\n\t// Set it first as otherwise it will overwrite the ViperKeySelfServiceBrowserDefaultReturnTo key;\n\treturnToTS := testhelpers.NewRedirTS(t, \"return_to\", conf)\n\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToTS.URL})\n\n\tredirTS := testhelpers.NewRedirTS(t, \"already authenticated\", conf)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationEnabled, true)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\n\tt.Run(\"does redirect to default on authenticated request\", func(t *testing.T) {\n\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, router, testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+registration.RouteInitBrowserFlow, nil))\n\t\tassert.Contains(t, res.Request.URL.String(), redirTS.URL)\n\t\tassert.EqualValues(t, \"already authenticated\", string(body))\n\t})\n\n\tt.Run(\"does redirect to default on authenticated request\", func(t *testing.T) {\n\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, router, testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+registration.RouteInitAPIFlow, nil))\n\t\tassert.Contains(t, res.Request.URL.String(), registration.RouteInitAPIFlow)\n\t\tassertx.EqualAsJSON(t, registration.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw))\n\t})\n\n\tt.Run(\"does redirect to return_to url on authenticated request\", func(t *testing.T) {\n\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, router, testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+registration.RouteInitBrowserFlow+\"?return_to=\"+returnToTS.URL, nil))\n\t\tassert.Contains(t, res.Request.URL.String(), returnToTS.URL)\n\t\tassert.EqualValues(t, \"return_to\", string(body))\n\t})\n\n\tt.Run(\"oauth2 with session and skip=false is redirected to login\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, \"https://fake-hydra\")\n\n\t\tfakeHydra.RequestURL = \"https://www.ory.sh/oauth2/auth?audience=&client_id=foo&login_verifier=\"\n\t\tfakeHydra.Skip = false\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tclient.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t}\n\t\t_, res := testhelpers.MockMakeAuthenticatedRequestWithClient(t, reg, conf, router, testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+registration.RouteInitBrowserFlow+\"?login_challenge=\"+hydra.FakeValidLoginChallenge, nil), client)\n\t\tassert.Contains(t, res.Header.Get(\"location\"), login.RouteInitBrowserFlow)\n\t})\n\n\tt.Run(\"oauth2 with session and skip=true is accepted\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, \"https://fake-hydra\")\n\n\t\tfakeHydra.Skip = true\n\t\tfakeHydra.RequestURL = \"https://www.ory.sh/oauth2/auth?audience=&client_id=foo&login_verifier=\"\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tclient.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t}\n\t\t_, res := testhelpers.MockMakeAuthenticatedRequestWithClient(t, reg, conf, router, testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+registration.RouteInitBrowserFlow+\"?login_challenge=\"+hydra.FakeValidLoginChallenge, nil), client)\n\t\tassert.Contains(t, res.Header.Get(\"location\"), hydra.FakePostLoginURL)\n\t})\n}\n\nfunc TestInitFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword),\n\t\tmap[string]interface{}{\"enabled\": true})\n\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tpublicTS, _ := testhelpers.NewKratosServerWithRouters(t, reg, router, httprouterx.NewTestRouterAdminWithPrefix(t))\n\tregistrationTS := testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\treturnToTS := testhelpers.NewRedirTS(t, \"return_to\", conf)\n\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationEnabled, true)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, returnToTS.URL)\n\n\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t{ID: \"default\", URL: \"file://./stub/registration.schema.json\"},\n\t\t{ID: \"email\", URL: \"file://./stub/registration.schema.json\", SelfserviceSelectable: true},\n\t\t{ID: \"phone\", URL: \"file://./stub/registration.phone.schema.json\", SelfserviceSelectable: true},\n\t\t{ID: \"not-allowed\", URL: \"file://./stub/registration.schema.json\"},\n\t})\n\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"email\")\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\n\tassertion := func(body []byte, isForced, isApi bool) {\n\t\tif isApi {\n\t\t\tassert.Equal(t, \"api\", gjson.GetBytes(body, \"type\").String())\n\t\t} else {\n\t\t\tassert.Equal(t, \"browser\", gjson.GetBytes(body, \"type\").String())\n\t\t}\n\t}\n\n\tinitAuthenticatedFlow := func(t *testing.T, isAPI bool, isSPA bool) (*http.Response, []byte) {\n\t\troute := registration.RouteInitBrowserFlow\n\t\tif isAPI {\n\t\t\troute = registration.RouteInitAPIFlow\n\t\t}\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", publicTS.URL+route, nil)\n\t\tif isSPA {\n\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t}\n\t\tbody, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, router, req)\n\t\tif isAPI {\n\t\t\tassert.Len(t, res.Header.Get(\"Set-Cookie\"), 0)\n\t\t}\n\t\treturn res, body\n\t}\n\n\tinitFlowWithAccept := func(t *testing.T, query url.Values, isAPI bool, accept string) (*http.Response, []byte) {\n\t\troute := registration.RouteInitBrowserFlow\n\t\tif isAPI {\n\t\t\troute = registration.RouteInitAPIFlow\n\t\t}\n\t\tc := publicTS.Client()\n\t\treq, err := http.NewRequest(\"GET\", publicTS.URL+route+\"?\"+query.Encode(), nil)\n\t\trequire.NoError(t, err)\n\t\tif accept != \"\" {\n\t\t\treq.Header.Set(\"Accept\", accept)\n\t\t}\n\n\t\tres, err := c.Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\treturn res, body\n\t}\n\n\tinitFlow := func(t *testing.T, query url.Values, isAPI bool) (*http.Response, []byte) {\n\t\treturn initFlowWithAccept(t, query, isAPI, \"\")\n\t}\n\n\tinitSPAFlow := func(t *testing.T) (*http.Response, []byte) {\n\t\treturn initFlowWithAccept(t, url.Values{}, false, \"application/json\")\n\t}\n\n\tt.Run(\"suite=identity schema in query\", func(t *testing.T) {\n\t\tfor _, tc := range []struct {\n\t\t\tname    string\n\t\t\tquery   url.Values\n\t\t\twantErr bool\n\t\t\tassert  func(*testing.T, []byte)\n\t\t}{{\n\t\t\tname:    \"not-allowed\",\n\t\t\tquery:   url.Values{\"identity_schema\": {\"not-allowed\"}},\n\t\t\twantErr: true,\n\t\t}, {\n\t\t\tname:    \"not-found\",\n\t\t\tquery:   url.Values{\"identity_schema\": {\"not-found\"}},\n\t\t\twantErr: true,\n\t\t}, {\n\t\t\tname:  \"phone\",\n\t\t\tquery: url.Values{\"identity_schema\": {\"phone\"}},\n\t\t\tassert: func(t *testing.T, body []byte) {\n\t\t\t\tassert.True(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.phone)\").Exists(), \"%s\", body)\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.email)\").Exists(), \"%s\", body)\n\t\t\t},\n\t\t}, {\n\t\t\tname:  \"email\",\n\t\t\tquery: url.Values{\"identity_schema\": {\"email\"}},\n\t\t\tassert: func(t *testing.T, body []byte) {\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.phone)\").Exists(), \"%s\", body)\n\t\t\t\tassert.True(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.email)\").Exists(), \"%s\", body)\n\t\t\t},\n\t\t}} {\n\t\t\tt.Run(\"case=\"+tc.name, func(t *testing.T) {\n\t\t\t\tt.Run(\"flow=api\", func(t *testing.T) {\n\t\t\t\t\tres, body := initFlow(t, tc.query, true)\n\t\t\t\t\tif tc.wantErr {\n\t\t\t\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\ttc.assert(t, body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\t\t\t\tres, body := initFlow(t, tc.query, false)\n\t\t\t\t\tif tc.wantErr {\n\t\t\t\t\t\trequire.Contains(t, res.Request.URL.String(), errTS.URL, \"%s\", body)\n\t\t\t\t\t\tassert.EqualValues(t, \"Bad Request\", gjson.GetBytes(body, \"status\").String(), \"%s\", body)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\ttc.assert(t, body)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"flow=api\", func(t *testing.T) {\n\t\tt.Run(\"case=creates a new flow on unauthenticated request\", func(t *testing.T) {\n\t\t\tres, body := initFlow(t, url.Values{}, true)\n\t\t\tassert.Contains(t, res.Request.URL.String(), registration.RouteInitAPIFlow)\n\t\t\tassertion(body, false, true)\n\t\t\tassert.Empty(t, gjson.GetBytes(body, \"session_token_exchange_code\").String())\n\t\t})\n\n\t\tt.Run(\"case=returns a session token exchange code\", func(t *testing.T) {\n\t\t\tres, body := initFlow(t, urlx.ParseOrPanic(\"/?return_session_token_exchange_code=true\").Query(), true)\n\t\t\tassert.Contains(t, res.Request.URL.String(), registration.RouteInitAPIFlow)\n\t\t\tassertion(body, false, true)\n\t\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"session_token_exchange_code\").String())\n\t\t})\n\n\t\tt.Run(\"case=fails on authenticated request\", func(t *testing.T) {\n\t\t\tres, body := initAuthenticatedFlow(t, true, false)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, registration.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\tt.Run(\"case=does not set forced flag on unauthenticated request\", func(t *testing.T) {\n\t\t\tres, body := initFlow(t, url.Values{}, false)\n\t\t\tassertion(body, false, false)\n\t\t\tassert.Contains(t, res.Request.URL.String(), registrationTS.URL)\n\t\t\tassert.Empty(t, gjson.GetBytes(body, \"session_token_exchange_code\").String())\n\t\t})\n\n\t\tt.Run(\"case=never returns a session token exchange code\", func(t *testing.T) {\n\t\t\t_, body := initFlow(t, urlx.ParseOrPanic(\"/?return_session_token_exchange_code=true\").Query(), false)\n\t\t\tassertion(body, false, false)\n\t\t\tassert.Empty(t, gjson.GetBytes(body, \"session_token_exchange_code\").String())\n\t\t})\n\n\t\tt.Run(\"case=makes request with JSON\", func(t *testing.T) {\n\t\t\tres, body := initSPAFlow(t)\n\t\t\tassertion(body, false, false)\n\t\t\tassert.NotContains(t, res.Request.URL.String(), registrationTS.URL)\n\t\t})\n\n\t\tt.Run(\"case=redirects when already authenticated\", func(t *testing.T) {\n\t\t\tres, _ := initAuthenticatedFlow(t, false, false)\n\t\t\tassert.Contains(t, res.Request.URL.String(), returnToTS.URL)\n\t\t})\n\n\t\tt.Run(\"case=responds with error if already authenticated and SPA\", func(t *testing.T) {\n\t\t\tres, body := initAuthenticatedFlow(t, false, true)\n\t\t\tassert.NotContains(t, res.Request.URL.String(), returnToTS.URL)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, registration.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"case=relative redirect when self-service registration ui is a relative URL\", func(t *testing.T) {\n\t\t\treg.Config().MustSet(ctx, config.ViperKeySelfServiceRegistrationUI, \"/registration-ts\")\n\t\t\tassert.Regexp(\n\t\t\t\tt,\n\t\t\t\t\"^/registration-ts.*$\",\n\t\t\t\ttesthelpers.GetSelfServiceRedirectLocation(t, publicTS.URL+registration.RouteInitBrowserFlow),\n\t\t\t)\n\t\t})\n\n\t\tt.Run(\"case=redirects with 303\", func(t *testing.T) {\n\t\t\tc := &http.Client{}\n\t\t\t// don't get the reference, instead copy the values, so we don't alter the client directly.\n\t\t\t*c = *publicTS.Client()\n\t\t\t// prevent the redirect\n\t\t\tc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\t\t\treq, err := http.NewRequest(\"GET\", publicTS.URL+registration.RouteInitBrowserFlow, nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tres, err := c.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t// here we check that the redirect status is 303\n\t\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t})\n\t})\n}\n\nfunc TestDisabledFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationEnabled, false)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/login.schema.json\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword),\n\t\tmap[string]interface{}{\"enabled\": true})\n\n\tpublicTS, _ := testhelpers.NewKratosServerWithCSRF(t, reg)\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\n\tmakeRequest := func(t *testing.T, route string, isSPA bool) (*http.Response, []byte) {\n\t\tc := publicTS.Client()\n\t\treq, err := http.NewRequest(\"GET\", publicTS.URL+route, nil)\n\t\trequire.NoError(t, err)\n\n\t\tif isSPA {\n\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t}\n\n\t\tres, err := c.Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\treturn res, body\n\t}\n\n\tt.Run(\"flow=api\", func(t *testing.T) {\n\t\tt.Run(\"case=init fails when flow disabled\", func(t *testing.T) {\n\t\t\tres, body := makeRequest(t, registration.RouteInitAPIFlow, false)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, registration.ErrRegistrationDisabled, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"case=get flow fails when flow disabled\", func(t *testing.T) {\n\t\t\tres, body := makeRequest(t, registration.RouteGetFlow+\"?id=\"+x.NewUUID().String(), false)\n\t\t\trequire.Contains(t, res.Request.URL.String(), errTS.URL, \"%s\", body)\n\t\t\tassert.EqualValues(t, registration.ErrRegistrationDisabled.ReasonField, gjson.GetBytes(body, \"reason\").String(), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\tt.Run(\"case=init responds with error if flow disabled and SPA\", func(t *testing.T) {\n\t\t\tres, body := makeRequest(t, registration.RouteInitBrowserFlow, true)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, registration.ErrRegistrationDisabled, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"case=get flow responds with error if flow disabled and SPA\", func(t *testing.T) {\n\t\t\tres, body := makeRequest(t, registration.RouteGetFlow+\"?id=\"+x.NewUUID().String(), true)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, registration.ErrRegistrationDisabled, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t})\n\t})\n}\n\nfunc TestGetFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationEnabled, true)\n\treturnToTS := testhelpers.NewRedirTS(t, \"return_to\", conf)\n\n\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t{ID: \"email\", URL: \"file://./stub/registration.schema.json\", SelfserviceSelectable: true},\n\t\t{ID: \"phone\", URL: \"file://./stub/registration.phone.schema.json\", SelfserviceSelectable: true},\n\t\t{ID: \"not-allowed\", URL: \"file://./stub/registration.schema.json\"},\n\t})\n\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"email\")\n\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword),\n\t\tmap[string]interface{}{\"enabled\": true})\n\n\tpublic, _ := testhelpers.NewKratosServerWithCSRF(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewRedirTS(t, \"\", conf)\n\n\tsetupRegistrationUI := func(t *testing.T, c *http.Client) *httptest.Server {\n\t\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t_, err := w.Write(testhelpers.EasyGetBody(t, c, public.URL+registration.RouteGetFlow+\"?id=\"+r.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t}))\n\t\tt.Cleanup(ts.Close)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationUI, ts.URL)\n\t\treturn ts\n\t}\n\n\tt.Run(\"case=valid\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t_ = setupRegistrationUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow)\n\n\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String(), \"%s\", body)\n\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"id\").String(), \"%s\", body)\n\t\tassert.Empty(t, gjson.GetBytes(body, \"headers\").Value(), \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), gjson.GetBytes(body, \"id\").String(), \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), public.URL, \"%s\", body)\n\t})\n\n\tt.Run(\"case=csrf cookie missing\", func(t *testing.T) {\n\t\tclient := http.DefaultClient\n\t\t_ = setupRegistrationUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow)\n\n\t\tassert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=expired\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupRegistrationUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow)\n\n\t\t// Expire the flow\n\t\tf, err := reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tf.ExpiresAt = time.Now().Add(-time.Second)\n\t\trequire.NoError(t, reg.RegistrationFlowPersister().UpdateRegistrationFlow(context.Background(), f))\n\n\t\tres, body := testhelpers.EasyGet(t, client, public.URL+registration.RouteGetFlow+\"?id=\"+f.ID.String())\n\t\tassert.EqualValues(t, http.StatusGone, res.StatusCode)\n\t\tassert.Equal(t, public.URL+registration.RouteInitBrowserFlow, gjson.GetBytes(body, \"error.details.redirect_to\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=expired with return_to and identity_schema\", func(t *testing.T) {\n\t\treturnTo := returnToTS.URL\n\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupRegistrationUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow+\n\t\t\t\"?return_to=\"+returnTo+\n\t\t\t\"&identity_schema=email\")\n\n\t\t// Expire the flow\n\t\tf, err := reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tf.ExpiresAt = time.Now().Add(-time.Second)\n\t\trequire.NoError(t, reg.RegistrationFlowPersister().UpdateRegistrationFlow(context.Background(), f))\n\n\t\t// Retrieve the flow and verify that return_to is in the response\n\t\tgetURL := fmt.Sprintf(\"%s%s?id=%s&return_to=%s\", public.URL, registration.RouteGetFlow, f.ID, returnTo)\n\t\tgetBody := testhelpers.EasyGetBody(t, client, getURL)\n\t\tassert.Equal(t, gjson.GetBytes(getBody, \"error.details.return_to\").String(), returnTo)\n\n\t\t// submit the flow but it is expired\n\t\tu := public.URL + registration.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\tres, err := client.PostForm(u, url.Values{\"method\": {\"password\"}, \"csrf_token\": {f.CSRFToken}, \"password\": {\"password\"}, \"traits.email\": {\"email@ory.sh\"}})\n\t\trequire.NoError(t, err)\n\t\tresBody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\tf, err = reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, public.URL+registration.RouteInitBrowserFlow+\"?return_to=\"+returnTo+\"&identity_schema=email\", f.RequestURL)\n\t})\n\n\tt.Run(\"case=not found\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsetupRegistrationUI(t, client)\n\n\t\tres, _ := testhelpers.EasyGet(t, client, public.URL+registration.RouteGetFlow+\"?id=\"+x.NewUUID().String())\n\t\tassert.EqualValues(t, http.StatusNotFound, res.StatusCode)\n\t})\n}\n\n// This test verifies that the password method is still executed even if the\n// oidc strategy is ordered before the password strategy\n// when submitting the form with both `method=password` and `provider=google`.\nfunc TestOIDCStrategyOrder(t *testing.T) {\n\tt.Logf(\"This test has been set up to validate the current incorrect `oidc` behaviour. When submitting the form, the `oidc` strategy is executed first, even if the method is set to `password`.\")\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\t// reorder the strategies\n\treg.WithSelfserviceStrategies(t, []any{\n\t\toidc.NewStrategy(reg, oidc.ForCredentialType(identity.CredentialsTypeOIDC)),\n\t\tpassword.NewStrategy(reg),\n\t})\n\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationEnabled, true)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword),\n\t\tmap[string]interface{}{\"enabled\": true})\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeOIDC),\n\t\tmap[string]interface{}{\"enabled\": true})\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeCodeAuth),\n\t\tmap[string]interface{}{\"passwordless_enabled\": true})\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeOIDC)+\".config\", &oidc.ConfigurationCollection{Providers: []oidc.Configuration{\n\t\t{\n\t\t\tID:           \"google\",\n\t\t\tProvider:     \"google\",\n\t\t\tClientID:     \"1234\",\n\t\t\tClientSecret: \"1234\",\n\t\t},\n\t}})\n\n\tpublic, _ := testhelpers.NewKratosServerWithCSRF(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewRedirTS(t, \"\", conf)\n\n\tsetupRegistrationUI := func(t *testing.T, c *http.Client) *httptest.Server {\n\t\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t_, err := w.Write(testhelpers.EasyGetBody(t, c, public.URL+registration.RouteGetFlow+\"?id=\"+r.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t}))\n\t\tt.Cleanup(ts.Close)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationUI, ts.URL)\n\t\treturn ts\n\t}\n\n\tt.Run(\"case=accept `password` method while including `provider:google`\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t_ = setupRegistrationUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow)\n\n\t\tflow := gjson.GetBytes(body, \"id\").String()\n\n\t\tcsrfToken := gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\t\temail := faker.Email()\n\t\tpayload := json.RawMessage(`{\"traits\": {\"email\": \"` + email + `\"},\"method\": \"password\",\"password\": \"asdasdasdsa21312@#!@%\",\"provider\": \"google\",\"csrf_token\": \"` + csrfToken + `\"}`)\n\n\t\treq, err := http.NewRequestWithContext(context.Background(), http.MethodPost, public.URL+registration.RouteSubmitFlow+\"?flow=\"+flow, bytes.NewBuffer(payload))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\n\t\trequire.NoError(t, err)\n\t\tresp, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, http.StatusOK, resp.StatusCode, \"%s\", ioutilx.MustReadAll(resp.Body))\n\n\t\tverifiableAddress, err := reg.PrivilegedIdentityPool().FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, email)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, strings.ToLower(email), verifiableAddress.Value)\n\n\t\tid, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(ctx, verifiableAddress.IdentityID)\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, id.ID)\n\n\t\t_, ok := id.GetCredentials(identity.CredentialsTypePassword)\n\t\trequire.True(t, ok)\n\t})\n\n\tt.Run(\"case=accept oidc flow with just `provider:google`\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t_ = setupRegistrationUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+registration.RouteInitBrowserFlow)\n\n\t\tflow := gjson.GetBytes(body, \"id\").String()\n\n\t\tcsrfToken := gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\n\t\tpayload := json.RawMessage(`{\"provider\": \"google\",\"csrf_token\": \"` + csrfToken + `\"}`)\n\n\t\treq, err := http.NewRequestWithContext(context.Background(), http.MethodPost, public.URL+registration.RouteSubmitFlow+\"?flow=\"+flow, bytes.NewBuffer(payload))\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\n\t\tresp, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = resp.Body.Close() }()\n\t\trequire.Equal(t, http.StatusUnprocessableEntity, resp.StatusCode)\n\t\tb, err := io.ReadAll(resp.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.Containsf(t,\n\t\t\tgjson.GetBytes(b, \"error.reason\").String(),\n\t\t\t\"In order to complete this flow please redirect the browser to: https://accounts.google.com/o/oauth2/v2/auth\",\n\t\t\t\"%s\", b,\n\t\t)\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/registration/hook.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\ntype (\n\tPreHookExecutor interface {\n\t\tExecuteRegistrationPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error\n\t}\n\tPreHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow) error\n\n\tPostHookPostPersistExecutor interface {\n\t\tExecutePostRegistrationPostPersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error\n\t}\n\tPostHookPostPersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error\n\n\tPostHookPrePersistExecutor interface {\n\t\tExecutePostRegistrationPrePersistHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error\n\t}\n\tPostHookPrePersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error\n\n\tHooksProvider interface {\n\t\tPreRegistrationHooks(ctx context.Context) ([]PreHookExecutor, error)\n\t\tPostRegistrationPrePersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]PostHookPrePersistExecutor, error)\n\t\tPostRegistrationPostPersistHooks(ctx context.Context, credentialsType identity.CredentialsType) ([]PostHookPostPersistExecutor, error)\n\t}\n)\n\nfunc ExecutorNames[T any](e []T) []string {\n\tnames := make([]string, len(e))\n\tfor k, ee := range e {\n\t\tnames[k] = fmt.Sprintf(\"%T\", ee)\n\t}\n\treturn names\n}\n\nfunc (f PreHookExecutorFunc) ExecuteRegistrationPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error {\n\treturn f(w, r, a)\n}\n\nfunc (f PostHookPostPersistExecutorFunc) ExecutePostRegistrationPostPersistHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error {\n\treturn f(w, r, a, s)\n}\n\nfunc (f PostHookPrePersistExecutorFunc) ExecutePostRegistrationPrePersistHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error {\n\treturn f(w, r, a, i)\n}\n\ntype (\n\texecutorDependencies interface {\n\t\tconfig.Provider\n\t\tidentity.ManagementProvider\n\t\tidentity.PrivilegedPoolProvider\n\t\tidentity.ValidationProvider\n\t\tlogin.FlowPersistenceProvider\n\t\tlogin.StrategyProvider\n\t\tsession.PersistenceProvider\n\t\tsession.ManagementProvider\n\t\tHooksProvider\n\t\tFlowPersistenceProvider\n\t\thydra.Provider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\thttpx.ClientProvider\n\t\tlogrusx.Provider\n\t\thttpx.WriterProvider\n\t\totelx.Provider\n\t\tsessiontokenexchange.PersistenceProvider\n\t}\n\tHookExecutor struct {\n\t\td executorDependencies\n\t}\n\tHookExecutorProvider interface {\n\t\tRegistrationExecutor() *HookExecutor\n\t}\n)\n\nfunc NewHookExecutor(d executorDependencies) *HookExecutor {\n\treturn &HookExecutor{d: d}\n}\n\nfunc (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Request, ct identity.CredentialsType, provider, organizationID string, registrationFlow *Flow, i *identity.Identity) (err error) {\n\tctx := r.Context()\n\tctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, \"HookExecutor.PostRegistrationHook\")\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tWithField(\"flow_method\", ct).\n\t\tDebug(\"Running PostRegistrationPrePersistHooks.\")\n\tpreHooks, err := e.d.PostRegistrationPrePersistHooks(ctx, ct)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k, executor := range preHooks {\n\t\tif err := executor.ExecutePostRegistrationPrePersistHook(w, r, registrationFlow, i); err != nil {\n\t\t\tif errors.Is(err, ErrHookAbortFlow) {\n\t\t\t\te.d.Logger().\n\t\t\t\t\tWithRequest(r).\n\t\t\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\t\t\tWithField(\"executor_position\", k).\n\t\t\t\t\tWithField(\"executors\", ExecutorNames(preHooks)).\n\t\t\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\t\t\tWithField(\"flow_method\", ct).\n\t\t\t\t\tDebug(\"A ExecutePostRegistrationPrePersistHook hook aborted early.\")\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\te.d.Logger().\n\t\t\t\tWithRequest(r).\n\t\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\t\tWithField(\"executor_position\", k).\n\t\t\t\tWithField(\"executors\", ExecutorNames(preHooks)).\n\t\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\t\tWithField(\"flow_method\", ct).\n\t\t\t\tWithError(err).\n\t\t\t\tError(\"ExecutePostRegistrationPostPersistHook hook failed with an error.\")\n\n\t\t\ttraits := i.Traits\n\t\t\treturn flow.HandleHookError(w, r, registrationFlow, traits, ct.ToUiNodeGroup(), err, e.d, e.d)\n\t\t}\n\n\t\te.d.Logger().WithRequest(r).\n\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\tWithField(\"executor_position\", k).\n\t\t\tWithField(\"executors\", ExecutorNames(preHooks)).\n\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\tWithField(\"flow_method\", ct).\n\t\t\tDebug(\"ExecutePostRegistrationPrePersistHook completed successfully.\")\n\t}\n\n\t// We need to make sure that the identity has a valid schema before passing it down to the identity pool.\n\tif err := e.d.IdentityValidator().Validate(ctx, i); err != nil {\n\t\treturn err\n\t}\n\t// We're now creating the identity because any of the hooks could trigger a \"redirect\" or a \"session\" which\n\t// would imply that the identity has to exist already.\n\tif err := e.d.IdentityManager().Create(ctx, i); err != nil {\n\t\tif errors.Is(err, sqlcon.ErrUniqueViolation) {\n\t\t\tstrategy, err := e.d.AllLoginStrategies().Strategy(ct)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif strategy, ok := strategy.(login.LinkableStrategy); ok {\n\t\t\t\tduplicateIdentifier, err := e.getDuplicateIdentifier(ctx, i)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tif err := strategy.SetDuplicateCredentials(\n\t\t\t\t\tregistrationFlow,\n\t\t\t\t\tduplicateIdentifier,\n\t\t\t\t\ti.Credentials[ct],\n\t\t\t\t\tprovider,\n\t\t\t\t); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn err\n\t}\n\n\t// At this point the identity is already created and will not be rolled back, so\n\t// we want all PostPersist hooks to be able to continue even when the client cancels the request.\n\tctx = context.WithoutCancel(ctx)\n\tr = r.WithContext(ctx)\n\n\t// Verify the redirect URL before we do any other processing.\n\tc := e.d.Config()\n\treturnTo, err := redir.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(ctx),\n\t\tredir.SecureRedirectReturnTo(registrationFlow.ReturnTo),\n\t\tredir.SecureRedirectUseSourceURL(registrationFlow.RequestURL),\n\t\tredir.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)),\n\t\tredir.SecureRedirectOverrideDefaultReturnTo(c.SelfServiceFlowRegistrationReturnTo(ctx, ct.String())),\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tspan.SetAttributes(otelx.StringAttrs(map[string]string{\n\t\t\"return_to\":       returnTo.String(),\n\t\t\"flow_type\":       string(registrationFlow.Type),\n\t\t\"redirect_reason\": \"registration successful\",\n\t})...)\n\n\tif registrationFlow.Type == flow.TypeBrowser && x.IsJSONRequest(r) {\n\t\tregistrationFlow.AddContinueWith(flow.NewContinueWithRedirectBrowserTo(returnTo.String()))\n\t}\n\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tInfo(\"A new identity has registered using self-service registration.\")\n\n\tspan.AddEvent(events.NewRegistrationSucceeded(ctx, registrationFlow.ID, i.ID, string(registrationFlow.Type), ct.String(), provider))\n\n\ts := session.NewInactiveSession()\n\n\ts.CompletedLoginForWithProvider(ct, identity.AuthenticatorAssuranceLevel1, provider, organizationID)\n\tif err := e.d.SessionManager().ActivateSession(r, s, i, time.Now().UTC()); err != nil {\n\t\treturn err\n\t}\n\n\t// We persist the session here so that subsequent hooks (like verification) can use it.\n\tif err := e.d.SessionPersister().UpsertSession(ctx, s); err != nil {\n\t\treturn err\n\t}\n\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tWithField(\"flow_method\", ct).\n\t\tDebug(\"Running PostRegistrationPostPersistHooks.\")\n\tpostHooks, err := e.d.PostRegistrationPostPersistHooks(ctx, ct)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k, executor := range postHooks {\n\t\tif err := executor.ExecutePostRegistrationPostPersistHook(w, r, registrationFlow, s); err != nil {\n\t\t\tif errors.Is(err, ErrHookAbortFlow) {\n\t\t\t\te.d.Logger().\n\t\t\t\t\tWithRequest(r).\n\t\t\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\t\t\tWithField(\"executor_position\", k).\n\t\t\t\t\tWithField(\"executors\", ExecutorNames(postHooks)).\n\t\t\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\t\t\tWithField(\"flow_method\", ct).\n\t\t\t\t\tDebug(\"A ExecutePostRegistrationPostPersistHook hook aborted early.\")\n\n\t\t\t\tspan.SetAttributes(attribute.String(\"redirect_reason\", \"aborted by hook\"), attribute.String(\"executor\", fmt.Sprintf(\"%T\", executor)))\n\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\te.d.Logger().\n\t\t\t\tWithRequest(r).\n\t\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\t\tWithField(\"executor_position\", k).\n\t\t\t\tWithField(\"executors\", ExecutorNames(postHooks)).\n\t\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\t\tWithField(\"flow_method\", ct).\n\t\t\t\tWithError(err).\n\t\t\t\tError(\"ExecutePostRegistrationPostPersistHook hook failed with an error.\")\n\n\t\t\tspan.SetAttributes(attribute.String(\"redirect_reason\", \"hook error\"), attribute.String(\"executor\", fmt.Sprintf(\"%T\", executor)))\n\n\t\t\ttraits := i.Traits\n\t\t\treturn flow.HandleHookError(w, r, registrationFlow, traits, ct.ToUiNodeGroup(), err, e.d, e.d)\n\t\t}\n\n\t\te.d.Logger().WithRequest(r).\n\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\tWithField(\"executor_position\", k).\n\t\t\tWithField(\"executors\", ExecutorNames(postHooks)).\n\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\tWithField(\"flow_method\", ct).\n\t\t\tDebug(\"ExecutePostRegistrationPostPersistHook completed successfully.\")\n\t}\n\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"flow_method\", ct).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tDebug(\"Post registration execution hooks completed successfully.\")\n\n\tif registrationFlow.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\tspan.SetAttributes(attribute.String(\"flow_type\", string(flow.TypeAPI)))\n\n\t\tif registrationFlow.IDToken != \"\" {\n\t\t\t// We don't want to redirect with the code, if the flow was submitted with an ID token.\n\t\t\t// This is the case for Sign in with native Apple SDK or Google SDK.\n\t\t} else if handled, err := e.d.SessionManager().MaybeRedirectAPICodeFlow(w, r, registrationFlow, s.ID, ct.ToUiNodeGroup()); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t} else if handled {\n\t\t\treturn nil\n\t\t}\n\n\t\te.d.Writer().Write(w, r, &APIFlowResponse{\n\t\t\tIdentity:     i,\n\t\t\tContinueWith: registrationFlow.ContinueWith(),\n\t\t})\n\t\treturn nil\n\t}\n\n\tfinalReturnTo := returnTo.String()\n\tif registrationFlow.OAuth2LoginChallenge != \"\" {\n\t\tif registrationFlow.ReturnToVerification != \"\" {\n\t\t\t// Special case: If Kratos is used as a login UI *and* we want to show the verification UI,\n\t\t\t// redirect to the verification URL first and then return to Hydra.\n\t\t\tfinalReturnTo = registrationFlow.ReturnToVerification\n\t\t} else {\n\t\t\tcallbackURL, err := e.d.Hydra().AcceptLoginRequest(ctx,\n\t\t\t\thydra.AcceptLoginRequestParams{\n\t\t\t\t\tLoginChallenge:        string(registrationFlow.OAuth2LoginChallenge),\n\t\t\t\t\tIdentityID:            i.ID.String(),\n\t\t\t\t\tSessionID:             s.ID.String(),\n\t\t\t\t\tAuthenticationMethods: s.AMR,\n\t\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfinalReturnTo = callbackURL\n\t\t}\n\t\tspan.SetAttributes(attribute.String(\"redirect_reason\", \"oauth2 login challenge\"))\n\t} else if registrationFlow.ReturnToVerification != \"\" {\n\t\tfinalReturnTo = registrationFlow.ReturnToVerification\n\t\tspan.SetAttributes(attribute.String(\"redirect_reason\", \"verification requested\"))\n\t}\n\tspan.SetAttributes(attribute.String(\"return_to\", finalReturnTo))\n\n\tredir.ContentNegotiationRedirection(w, r, s.Declassified(), e.d.Writer(), finalReturnTo)\n\treturn nil\n}\n\nfunc (e *HookExecutor) getDuplicateIdentifier(ctx context.Context, i *identity.Identity) (string, error) {\n\t_, id, _, err := e.d.IdentityManager().ConflictingIdentity(ctx, i)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn id, nil\n}\n\nfunc (e *HookExecutor) PreRegistrationHook(w http.ResponseWriter, r *http.Request, a *Flow) error {\n\thooks, err := e.d.PreRegistrationHooks(r.Context())\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, executor := range hooks {\n\t\tif err := executor.ExecuteRegistrationPreHook(w, r, a); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/flow/registration/hook_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/gobuffalo/httptest\"\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestRegistrationExecutor(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\n\tfor _, strategy := range identity.AllCredentialTypes {\n\t\tstrategy := strategy.String()\n\n\t\tt.Run(\"strategy=\"+strategy, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t\t\treg.SetHydra(hydra.NewFake())\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, returnToServer.URL)\n\n\t\t\tnewServer := func(t *testing.T, i *identity.Identity, ft flow.Type, flowCallbacks ...func(*registration.Flow)) *httptest.Server {\n\t\t\t\trouter := http.NewServeMux()\n\n\t\t\t\thandleErr := testhelpers.SelfServiceHookRegistrationErrorHandler\n\t\t\t\trouter.HandleFunc(\"GET /registration/pre\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t\tf, err := registration.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, ft)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tif handleErr(t, w, r, reg.RegistrationHookExecutor().PreRegistrationHook(w, r, f)) {\n\t\t\t\t\t\t_, _ = w.Write([]byte(\"ok\"))\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\trouter.HandleFunc(\"GET /registration/post\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t\tif i == nil {\n\t\t\t\t\t\ti = testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\t\t\t}\n\t\t\t\t\tregFlow, err := registration.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, ft)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tregFlow.RequestURL = x.RequestURL(r).String()\n\t\t\t\t\tfor _, callback := range flowCallbacks {\n\t\t\t\t\t\tcallback(regFlow)\n\t\t\t\t\t}\n\t\t\t\t\t_ = handleErr(t, w, r, reg.RegistrationHookExecutor().PostRegistrationHook(w, r, identity.CredentialsType(strategy), \"\", \"\", regFlow, i))\n\t\t\t\t})\n\n\t\t\t\tts := httptest.NewServer(router)\n\t\t\t\tt.Cleanup(ts.Close)\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)\n\t\t\t\treturn ts\n\t\t\t}\n\n\t\t\tmakeRequestPost := testhelpers.SelfServiceMakeRegistrationPostHookRequest\n\t\t\tviperSetPost := testhelpers.SelfServiceHookRegistrationViperSetPost\n\t\t\tt.Run(\"method=PostRegistrationHook\", func(t *testing.T) {\n\t\t\t\tt.Run(\"case=pass without hooks\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\n\t\t\t\t\tts := newServer(t, i, flow.TypeBrowser)\n\t\t\t\t\tres, body := makeRequestPost(t, ts, false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL, res.Request.URL.String())\n\n\t\t\t\t\tactual, err := reg.IdentityPool().GetIdentity(context.Background(), i.ID, identity.ExpandNothing)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, actual.Traits, i.Traits)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass without hooks if ajax client\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\n\t\t\t\t\tts := newServer(t, i, flow.TypeBrowser)\n\t\t\t\t\tres, body := makeRequestPost(t, ts, true, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), ts.URL)\n\t\t\t\t\tassert.JSONEq(t, fmt.Sprintf(`[{\"action\":\"redirect_browser_to\",\"redirect_browser_to\":\"%s\"}]`, returnToServer.URL), gjson.Get(body, \"continue_with\").Raw)\n\n\t\t\t\t\tactual, err := reg.IdentityPool().GetIdentity(context.Background(), i.ID, identity.ExpandNothing)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, actual.Traits, i.Traits)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass if hooks pass\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tviperSetPost(t, conf, strategy, []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL, res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=fail if hooks fail\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tviperSetPost(t, conf, strategy, []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecutePostRegistrationPrePersistHook\": \"abort\"}`)}})\n\t\t\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, i, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"\", body)\n\n\t\t\t\t\t_, err := reg.IdentityPool().GetIdentity(context.Background(), i.ID, identity.ExpandNothing)\n\t\t\t\t\trequire.Error(t, err)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=use return_to value\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToServer.URL})\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{\"return_to\": {returnToServer.URL + \"/kratos\"}})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL+\"/kratos\", res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=use nested config value\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\ttesthelpers.SelfServiceHookRegistrationSetDefaultRedirectToStrategy(t, conf, strategy, returnToServer.URL+\"/kratos\")\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL+\"/kratos\", res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=use nested config value\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToServer.URL + \"/kratos\"})\n\t\t\t\t\ttesthelpers.SelfServiceHookRegistrationSetDefaultRedirectTo(t, conf, returnToServer.URL+\"/not-kratos\")\n\t\t\t\t\ttesthelpers.SelfServiceHookRegistrationSetDefaultRedirectToStrategy(t, conf, strategy, returnToServer.URL+\"/kratos\")\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL+\"/kratos\", res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass if hooks pass\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tviperSetPost(t, conf, strategy, []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL, res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=send a json response for API clients\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeAPI), true, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"identity.id\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass without hooks for browser flow with application/json\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), true, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"identity.id\"))\n\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"session.token\"))\n\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"session_token\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should redirect to verification UI if show_verification_ui hook is set\", func(t *testing.T) {\n\t\t\t\t\tverificationTS := testhelpers.NewVerificationUIFlowEchoServer(t, reg)\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".hooks\", []map[string]interface{}{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"hook\": hook.KeyVerificationUI,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\t\t\ti.Traits = identity.Traits(`{\"email\": \"verifiable@ory.sh\"}`)\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, i, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), verificationTS.URL)\n\t\t\t\t\tassert.NotEmpty(t, res.Request.URL.Query().Get(\"flow\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should redirect to verification UI if there is a login_challenge\", func(t *testing.T) {\n\t\t\t\t\tverificationTS := testhelpers.NewVerificationUIFlowEchoServer(t, reg)\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".hooks\", []map[string]interface{}{{\n\t\t\t\t\t\t\"hook\": hook.KeyVerificationUI,\n\t\t\t\t\t}})\n\t\t\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\t\t\ti.Traits = identity.Traits(`{\"email\": \"verifiable-valid-login_challenge@ory.sh\"}`)\n\n\t\t\t\t\twithOAuthChallenge := func(f *registration.Flow) {\n\t\t\t\t\t\tf.OAuth2LoginChallenge = hydra.FakeValidLoginChallenge\n\t\t\t\t\t}\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, i, flow.TypeBrowser, withOAuthChallenge), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), verificationTS.URL)\n\t\t\t\t\tflowID := res.Request.URL.Query().Get(\"flow\")\n\t\t\t\t\trequire.NotEmpty(t, flowID)\n\t\t\t\t\tflow, err := reg.VerificationFlowPersister().GetVerificationFlow(ctx, uuid.Must(uuid.FromString(flowID)))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, hydra.FakeValidLoginChallenge, flow.OAuth2LoginChallenge.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should redirect to first verification UI if show_verification_ui hook is set and multiple verifiable addresses\", func(t *testing.T) {\n\t\t\t\t\tverificationTS := testhelpers.NewVerificationUIFlowEchoServer(t, reg)\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".hooks\", []map[string]interface{}{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"hook\": hook.KeyVerificationUI,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\t\t\ti.SchemaID = testhelpers.UseIdentitySchema(t, conf, \"file://./stub/registration-multi-email.schema.json\")\n\t\t\t\t\ti.Traits = identity.Traits(`{\"emails\": [\"one@ory.sh\", \"two@ory.sh\"]}`)\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, i, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), verificationTS.URL)\n\t\t\t\t\tassert.NotEmpty(t, res.Request.URL.Query().Get(\"flow\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should still sent session if show_verification_ui is set after session hook\", func(t *testing.T) {\n\t\t\t\t\tverificationTS := testhelpers.NewVerificationUIFlowEchoServer(t, reg)\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".hooks\", []map[string]interface{}{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"hook\": hook.KeyVerificationUI,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"hook\": hook.KeySessionIssuer,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\t\t\ti.Traits = identity.Traits(`{\"email\": \"verifiable4@ory.sh\"}`)\n\n\t\t\t\t\tjar := testhelpers.EasyCookieJar(t, nil)\n\t\t\t\t\ts := newServer(t, i, flow.TypeBrowser)\n\t\t\t\t\ts.Client().Jar = jar\n\t\t\t\t\tres, _ := makeRequestPost(t, s, false, url.Values{})\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), verificationTS.URL)\n\t\t\t\t\tassert.NotEmpty(t, res.Request.URL.Query().Get(\"flow\"))\n\t\t\t\t\tu, err := url.Parse(s.URL)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tcookies := jar.Cookies(u)\n\t\t\t\t\trequire.Len(t, cookies, 1)\n\t\t\t\t\tassert.Equal(t, \"ory_kratos_session\", cookies[0].Name)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tfor _, kind := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {\n\t\t\t\tt.Run(\"type=\"+string(kind)+\"/method=PreRegistrationHook\", testhelpers.TestSelfServicePreHook(\n\t\t\t\t\tconfig.ViperKeySelfServiceRegistrationBeforeHooks,\n\t\t\t\t\ttesthelpers.SelfServiceMakeRegistrationPreHookRequest,\n\t\t\t\t\tfunc(t *testing.T) *httptest.Server {\n\t\t\t\t\t\treturn newServer(t, nil, kind)\n\t\t\t\t\t},\n\t\t\t\t\tconf,\n\t\t\t\t))\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/registration/oragnizations.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n)\n\nvar organizationFilter = []StrategyFilter{func(s Strategy) bool {\n\ta, b := s.(flow.OrganizationImplementor)\n\treturn b && a.SupportsOrganizations()\n}}\n\nfunc PrepareOrganizations(r *http.Request, f *Flow) []StrategyFilter {\n\tif f.OrganizationID.Valid {\n\t\treturn organizationFilter\n\t}\n\n\torgID := flow.ParseOrganizationFromURLQuery(r.Context(), r.URL.Query())\n\tif !orgID.Valid {\n\t\treturn []StrategyFilter{}\n\t}\n\n\tf.OrganizationID = orgID\n\treturn organizationFilter\n}\n"
  },
  {
    "path": "selfservice/flow/registration/organizations_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPrepareOrganizations(t *testing.T) {\n\ttests := []struct {\n\t\tname           string\n\t\tflow           *Flow\n\t\tquery          url.Values\n\t\texpectedFilter []StrategyFilter\n\t}{\n\t\t{\n\t\t\tname:           \"valid organization ID in flow\",\n\t\t\tflow:           &Flow{OrganizationID: uuid.NullUUID{UUID: uuid.FromStringOrNil(\"123e4567-e89b-12d3-a456-426614174000\"), Valid: true}},\n\t\t\tquery:          url.Values{},\n\t\t\texpectedFilter: organizationFilter,\n\t\t},\n\t\t{\n\t\t\tname:           \"valid organization ID in query\",\n\t\t\tflow:           &Flow{},\n\t\t\tquery:          url.Values{\"organization\": {\"123e4567-e89b-12d3-a456-426614174000\"}},\n\t\t\texpectedFilter: organizationFilter,\n\t\t},\n\t\t{\n\t\t\tname:           \"invalid organization ID in query\",\n\t\t\tflow:           &Flow{},\n\t\t\tquery:          url.Values{\"organization\": {\"invalid-uuid\"}},\n\t\t\texpectedFilter: []StrategyFilter{},\n\t\t},\n\t\t{\n\t\t\tname:           \"missing organization ID\",\n\t\t\tflow:           &Flow{},\n\t\t\tquery:          url.Values{},\n\t\t\texpectedFilter: []StrategyFilter{},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\treq := &http.Request{URL: &url.URL{RawQuery: tt.query.Encode()}}\n\t\t\tresult := PrepareOrganizations(req.WithContext(context.Background()), tt.flow)\n\t\t\tassert.Equal(t, tt.expectedFilter, result)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/registration/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype FlowPersister interface {\n\tUpdateRegistrationFlow(context.Context, *Flow) error\n\tCreateRegistrationFlow(context.Context, *Flow) error\n\tGetRegistrationFlow(context.Context, uuid.UUID) (*Flow, error)\n\tDeleteExpiredRegistrationFlows(context.Context, time.Time, int) error\n}\n\ntype FlowPersistenceProvider interface {\n\tRegistrationFlowPersister() FlowPersister\n}\n"
  },
  {
    "path": "selfservice/flow/registration/session.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n)\n\n// The Response for Registration Flows via API\n//\n// swagger:model successfulNativeRegistration\ntype APIFlowResponse struct {\n\t// The Session Token\n\t//\n\t// This field is only set when the session hook is configured as a post-registration hook.\n\t//\n\t// A session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\n\t// Header:\n\t//\n\t// \t\tAuthorization: bearer ${session-token}\n\t//\n\t// The session token is only issued for API flows, not for Browser flows!\n\tToken string `json:\"session_token,omitempty\"`\n\n\t// The Session\n\t//\n\t// This field is only set when the session hook is configured as a post-registration hook.\n\t//\n\t// The session contains information about the user, the session device, and so on.\n\t// This is only available for API flows, not for Browser flows!\n\tSession *session.Session `json:\"session,omitempty\"`\n\n\t// The Identity\n\t//\n\t// The identity that just signed up.\n\t//\n\t// required: true\n\tIdentity *identity.Identity `json:\"identity\"`\n\n\t// Contains a list of actions, that could follow this flow\n\t//\n\t// It can, for example, this will contain a reference to the verification flow, created as part of the user's\n\t// registration or the token of the session.\n\t//\n\t// required: false\n\tContinueWith []flow.ContinueWith `json:\"continue_with\"`\n}\n"
  },
  {
    "path": "selfservice/flow/registration/sort.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc SortNodes(ctx context.Context, n node.Nodes, schemaRef string) error {\n\treturn n.SortBySchema(ctx,\n\t\tnode.SortBySchema(schemaRef),\n\t\tnode.SortByGroups([]node.UiNodeGroup{\n\t\t\tnode.OpenIDConnectGroup,\n\t\t\tnode.SAMLGroup,\n\t\t\tnode.DefaultGroup,\n\t\t\tnode.WebAuthnGroup,\n\t\t\tnode.PasskeyGroup,\n\t\t\tnode.CodeGroup,\n\t\t\tnode.PasswordGroup,\n\t\t\tnode.CaptchaGroup,\n\t\t\tnode.ProfileGroup,\n\t\t}),\n\t\tnode.SortUpdateOrder(node.PasswordLoginOrder),\n\t\tnode.SortUseOrderAppend([]string{\n\t\t\t// WebAuthn\n\t\t\tnode.WebAuthnRemove,\n\t\t\tnode.WebAuthnRegisterDisplayName,\n\t\t\tnode.WebAuthnRegister,\n\t\t}),\n\t)\n}\n"
  },
  {
    "path": "selfservice/flow/registration/sort_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"context\"\n\t\"embed\"\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\n//go:embed fixtures/sort/*\nvar sortFixtures embed.FS\n\nfunc TestSortNodes(t *testing.T) {\n\tctx := context.Background()\n\n\t// TODO add more test cases.\n\tentries, err := sortFixtures.ReadDir(\"fixtures/sort\")\n\trequire.NoError(t, err)\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tt.Run(\"case=\"+entry.Name(), func(t *testing.T) {\n\t\t\ttoSort, err := sortFixtures.ReadFile(\"fixtures/sort/\" + entry.Name())\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar n node.Nodes\n\t\t\trequire.NoError(t, json.Unmarshal(toSort, &n))\n\t\t\trequire.NoError(t, SortNodes(ctx, n, \"file://fixtures/sort.schema.json\"))\n\n\t\t\tsnapshotx.SnapshotT(t, n)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/registration/state.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport \"github.com/ory/kratos/selfservice/flow\"\n\n// State represents the state of this request:\n//\n// - choose_method: ask the user to choose a method (e.g. registration with email)\n// - sent_email: the email has been sent to the user\n// - passed_challenge: the request was successful and the registration challenge was passed.\n//\n// swagger:model registrationFlowState\ntype State = flow.State\n"
  },
  {
    "path": "selfservice/flow/registration/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\ntype Strategy interface {\n\tID() identity.CredentialsType\n\tNodeGroup() node.UiNodeGroup\n\tRegister(w http.ResponseWriter, r *http.Request, f *Flow, i *identity.Identity) (err error)\n}\n\ntype Strategies []Strategy\n\nfunc (s Strategies) Strategy(id identity.CredentialsType) (Strategy, error) {\n\tids := make([]identity.CredentialsType, len(s))\n\tfor k, ss := range s {\n\t\tids[k] = ss.ID()\n\t\tif ss.ID() == id {\n\t\t\treturn ss, nil\n\t\t}\n\t}\n\n\treturn nil, errors.Errorf(`unable to find strategy for %s have %v`, id, ids)\n}\n\nfunc (s Strategies) MustStrategy(id identity.CredentialsType) Strategy {\n\tstrategy, err := s.Strategy(id)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn strategy\n}\n\ntype StrategyFilter func(strategy Strategy) bool\n\ntype StrategyProvider interface {\n\tRegistrationStrategies(ctx context.Context, filters ...StrategyFilter) Strategies\n\tAllRegistrationStrategies() Strategies\n}\n"
  },
  {
    "path": "selfservice/flow/registration/strategy_form_hydrator.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration\n\nimport (\n\t\"encoding/json\"\n\tstderr \"errors\"\n\t\"net/http\"\n)\n\nvar ErrBreakRegistrationPopulate = stderr.New(\"skip rest of registration form population\")\n\ntype UnifiedFormHydrator interface {\n\tPopulateRegistrationMethod(r *http.Request, sr *Flow) error\n}\n\ntype FormHydratorOptions struct {\n\tWithTraits json.RawMessage\n}\n\ntype FormHydratorModifier func(o *FormHydratorOptions)\n\nfunc WithTraits(traits json.RawMessage) FormHydratorModifier {\n\treturn func(o *FormHydratorOptions) {\n\t\to.WithTraits = traits\n\t}\n}\n\ntype FormHydrator interface {\n\tUnifiedFormHydrator\n\tPopulateRegistrationMethodCredentials(r *http.Request, sr *Flow, options ...FormHydratorModifier) error\n\tPopulateRegistrationMethodProfile(r *http.Request, sr *Flow, options ...FormHydratorModifier) error\n}\n"
  },
  {
    "path": "selfservice/flow/registration/stub/fake-session.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/registration/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"booly\": {\n          \"type\": \"boolean\"\n        },\n        \"should_big_number\": {\n          \"type\": \"number\",\n          \"minimum\": 1200\n        },\n        \"should_long_string\": {\n          \"type\": \"string\",\n          \"minLength\": 25\n        }\n\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/registration/stub/login.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/registration/stub/registration-multi-email.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"emails\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"ory.sh/kratos\": {\n              \"credentials\": {\n                \"password\": {\n                  \"identifier\": true\n                }\n              },\n              \"verification\": {\n                \"via\": \"email\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/registration/stub/registration.phone.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"phone-label\": {\n          \"type\": \"string\"\n        },\n        \"phone\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"sms\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"sms\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/registration/stub/registration.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/registration/stub/updated.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/registration/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/ory/x/assertx\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestFlowPersister(ctx context.Context, p persistence.Persister) func(t *testing.T) {\n\tvar clearids = func(r *registration.Flow) {\n\t\tr.ID = uuid.UUID{}\n\t}\n\n\treturn func(t *testing.T) {\n\t\t_, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tt.Run(\"case=should error when the registration flow does not exist\", func(t *testing.T) {\n\t\t\t_, err := p.GetRegistrationFlow(ctx, x.NewUUID())\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tvar newFlow = func(t *testing.T) *registration.Flow {\n\t\t\tvar r registration.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\tclearids(&r)\n\t\t\treturn &r\n\t\t}\n\n\t\tt.Run(\"case=should create a new registration flow and properly set IDs\", func(t *testing.T) {\n\t\t\tr := newFlow(t)\n\t\t\terr := p.CreateRegistrationFlow(ctx, r)\n\t\t\trequire.NoError(t, err, \"%#v\", err)\n\n\t\t\tassert.NotEqual(t, uuid.Nil, r.ID)\n\t\t})\n\n\t\tt.Run(\"case=should create with set ids\", func(t *testing.T) {\n\t\t\tvar r registration.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\trequire.NoError(t, p.CreateRegistrationFlow(ctx, &r))\n\t\t})\n\n\t\tt.Run(\"case=should create and fetch a registration flow\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\terr := p.CreateRegistrationFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactual, err := p.GetRegistrationFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.EqualValues(t, expected.ID, actual.ID)\n\t\t\tx.AssertEqualTime(t, expected.IssuedAt, actual.IssuedAt)\n\t\t\tx.AssertEqualTime(t, expected.ExpiresAt, actual.ExpiresAt)\n\t\t\tassert.EqualValues(t, expected.RequestURL, actual.RequestURL)\n\t\t\tassert.EqualValues(t, expected.Active, actual.Active)\n\t\t\tassertx.EqualAsJSON(t, expected.UI, actual.UI, \"expected:\\t%s\\nactual:\\t%s\", expected.UI, actual.UI)\n\t\t})\n\n\t\tt.Run(\"case=should not cause data loss when updating a request without changes\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\texpected.UI.Nodes = node.Nodes{}\n\t\t\texpected.UI.Nodes.Append(node.NewInputField(\"zab\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\ta.Pattern = \"baz\"\n\t\t\t})))\n\n\t\t\texpected.UI.Nodes.Append(node.NewInputField(\"foo\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\ta.Pattern = \"baz\"\n\t\t\t})))\n\n\t\t\terr := p.CreateRegistrationFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\texpected.UI.Action = \"/new-action\"\n\t\t\texpected.UI.Nodes.Append(\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"zab\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"zab\"\n\t\t\t\t})))\n\n\t\t\texpected.RequestURL = \"/new-request-url\"\n\t\t\trequire.NoError(t, p.UpdateRegistrationFlow(ctx, expected))\n\n\t\t\tactual, err := p.GetRegistrationFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, \"/new-action\", actual.UI.Action)\n\t\t\tassert.Equal(t, \"/new-request-url\", actual.RequestURL)\n\t\t\tassertx.EqualAsJSON(t, node.Nodes{\n\t\t\t\t// v0.5: {Name: \"zab\", Type: \"zab\", Pattern: \"zab\"},\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"baz\"\n\t\t\t\t})),\n\t\t\t\tnode.NewInputField(\"foo\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"baz\"\n\t\t\t\t})),\n\t\t\t\t// v0.5: {Name: \"zab\", Type: \"bar\", Pattern: \"baz\"},\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"zab\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"zab\"\n\t\t\t\t})),\n\t\t\t}, actual.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=should create and update and properly deal with internal context\", func(t *testing.T) {\n\t\t\tfor k, tc := range []struct {\n\t\t\t\tin     []byte\n\t\t\t\texpect string\n\t\t\t}{\n\t\t\t\t{in: []byte(\"[]\"), expect: \"{}\"},\n\t\t\t\t{expect: \"{}\"},\n\t\t\t\t{in: []byte(\"null\"), expect: \"{}\"},\n\t\t\t\t{in: []byte(`{\"foo\":\"bar\"}`), expect: `{\"foo\":\"bar\"}`},\n\t\t\t} {\n\t\t\t\tt.Run(fmt.Sprintf(\"run=%d\", k), func(t *testing.T) {\n\t\t\t\t\tr := newFlow(t)\n\t\t\t\t\tr.InternalContext = tc.in\n\t\t\t\t\trequire.NoError(t, p.CreateRegistrationFlow(ctx, r))\n\t\t\t\t\tassert.Equal(t, tc.expect, string(r.InternalContext))\n\n\t\t\t\t\tr.InternalContext = tc.in\n\t\t\t\t\trequire.NoError(t, p.UpdateRegistrationFlow(ctx, r))\n\t\t\t\t\tassert.Equal(t, tc.expect, string(r.InternalContext))\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=network\", func(t *testing.T) {\n\t\t\tid := x.NewUUID()\n\t\t\tnid, p := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\tt.Run(\"sets id on creation\", func(t *testing.T) {\n\t\t\t\tnow := time.Now()\n\t\t\t\texpected := &registration.Flow{ID: id, ExpiresAt: now.Add(time.Hour), IssuedAt: now}\n\t\t\t\trequire.NoError(t, p.CreateRegistrationFlow(ctx, expected))\n\t\t\t\tassert.EqualValues(t, id, expected.ID)\n\t\t\t\tassert.EqualValues(t, nid, expected.NID)\n\n\t\t\t\tactual, err := p.GetRegistrationFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, id, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\t\t\t})\n\n\t\t\tt.Run(\"can not update id\", func(t *testing.T) {\n\t\t\t\texpected, err := p.GetRegistrationFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, p.UpdateRegistrationFlow(ctx, expected))\n\n\t\t\t\tactual, err := p.GetRegistrationFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, id, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\t\t\t})\n\n\t\t\tt.Run(\"can not update on another network\", func(t *testing.T) {\n\t\t\t\texpected, err := p.GetRegistrationFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\t\texpected.RequestURL = \"updated\"\n\t\t\t\trequire.Error(t, other.UpdateRegistrationFlow(ctx, expected))\n\n\t\t\t\t_, err = other.GetRegistrationFlow(ctx, id)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\tactual, err := p.GetRegistrationFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotEqual(t, \"updated\", actual.RequestURL)\n\t\t\t})\n\n\t\t\tt.Run(\"can not get on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetRegistrationFlow(ctx, id)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/registration/testsetup_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage registration_test\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"testing\"\n)\n\nvar returnToServer *httptest.Server\n\nfunc TestMain(m *testing.M) {\n\treturnToServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = w.Write([]byte(\"OK\"))\n\t}))\n\tos.Exit(m.Run())\n}\n"
  },
  {
    "path": "selfservice/flow/request.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/strategy\"\n\t\"github.com/ory/x/decoderx\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/nosurf\"\n)\n\n//go:embed .schema/method.schema.json\nvar methodSchema []byte\n\nvar ErrOriginHeaderNeedsBrowserFlow = herodot.ErrBadRequest.\n\tWithReasonf(`The HTTP Request Header included the \"Origin\" key, indicating that this request was made as part of an AJAX request in a Browser. The flow however was initiated as an API request. To prevent potential misuse and mitigate several attack vectors including CSRF, the request has been blocked. Please consult the documentation.`)\n\nvar ErrCookieHeaderNeedsBrowserFlow = herodot.ErrBadRequest.\n\tWithReasonf(`The HTTP Request Header included the \"Cookie\" key, indicating that this request was made by a Browser. The flow however was initiated as an API request. To prevent potential misuse and mitigate several attack vectors including CSRF, the request has been blocked. Please consult the documentation.`)\n\nfunc EnsureCSRF(\n\treg config.Provider,\n\tr *http.Request,\n\tflowType Type,\n\tdisableAPIFlowEnforcement bool,\n\tgenerator func(r *http.Request) string,\n\tactual string,\n) error {\n\tswitch flowType {\n\tcase TypeAPI:\n\t\tif disableAPIFlowEnforcement {\n\t\t\treturn nil\n\t\t}\n\n\t\t// API Based flows to not require anti-CSRF tokens because we can not leverage a session, making this\n\t\t// endpoint pointless.\n\n\t\t// Let's ensure that no-one mistakenly makes an AJAX request using the API flow.\n\t\tif r.Header.Get(\"Origin\") != \"\" {\n\t\t\treturn errors.WithStack(ErrOriginHeaderNeedsBrowserFlow)\n\t\t}\n\n\t\t// Workaround for Cloudflare setting cookies that we can't control.\n\t\t// https://developers.cloudflare.com/fundamentals/reference/policies-compliances/cloudflare-cookies/\n\t\tvar cookies []string\n\t\tfor _, c := range r.Cookies() {\n\t\t\tif !strings.HasPrefix(c.Name, \"__cf\") && !strings.HasPrefix(c.Name, \"_cf\") && !strings.HasPrefix(c.Name, \"cf_\") {\n\t\t\t\tcookies = append(cookies, c.Name)\n\t\t\t}\n\t\t}\n\n\t\tif len(cookies) > 0 {\n\t\t\treturn errors.WithStack(ErrCookieHeaderNeedsBrowserFlow.WithDetail(\"found cookies\", cookies))\n\t\t}\n\n\t\treturn nil\n\tdefault:\n\t\tif !nosurf.VerifyToken(generator(r), actual) {\n\t\t\treturn errors.WithStack(nosurfx.CSRFErrorReason(r, reg))\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc MethodEnabledAndAllowedFromRequest(r *http.Request, flow FlowName, expected string, d interface {\n\tconfig.Provider\n},\n) error {\n\tvar method struct {\n\t\tMethod string `json:\"method\" form:\"method\"`\n\t}\n\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(methodSchema)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif err := decoderx.Decode(r, &method, compiler,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"PUT\", \"PATCH\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(false),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn MethodEnabledAndAllowed(r.Context(), flow, expected, method.Method, d)\n}\n\nfunc MethodEnabledAndAllowed(ctx context.Context, _ FlowName, expected, actual string, d config.Provider) error {\n\tif actual != expected {\n\t\ttrace.SpanFromContext(ctx).SetAttributes(attribute.String(\"not_responsible_reason\", \"method mismatch\"))\n\t\treturn errors.WithStack(ErrStrategyNotResponsible)\n\t}\n\n\tif !d.Config().SelfServiceStrategy(ctx, expected).Enabled {\n\t\treturn errors.WithStack(herodot.ErrNotFound.WithReason(strategy.EndpointDisabledMessage))\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/flow/request_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestVerifyRequest(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\trequire.EqualError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeBrowser, false, nosurfx.FakeCSRFTokenGenerator, \"not_csrf_token\"), nosurfx.ErrInvalidCSRFToken.Error())\n\trequire.NoError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeBrowser, false, nosurfx.FakeCSRFTokenGenerator, nosurfx.FakeCSRFToken), nil)\n\trequire.NoError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, \"\"))\n\trequire.EqualError(t, flow.EnsureCSRF(reg, &http.Request{\n\t\tHeader: http.Header{\"Origin\": {\"https://www.ory.sh\"}},\n\t}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, \"\"), flow.ErrOriginHeaderNeedsBrowserFlow.Error())\n\trequire.EqualError(t, flow.EnsureCSRF(reg, &http.Request{\n\t\tHeader: http.Header{\"Cookie\": {\"cookie=ory\"}},\n\t}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, \"\"), flow.ErrCookieHeaderNeedsBrowserFlow.Error(), \"should error because of cookie=ory\")\n\n\terr := flow.EnsureCSRF(reg, &http.Request{\n\t\tHeader: http.Header{\"Cookie\": {\"cookie1=cookievalue\", \"cookie2=cookievalue\"}},\n\t}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, \"\")\n\tvar he herodot.DetailsCarrier\n\trequire.ErrorAs(t, err, &he)\n\tcs, ok := he.Details()[\"found cookies\"].([]string)\n\trequire.True(t, ok)\n\trequire.ElementsMatch(t, cs, []string{\"cookie1\", \"cookie2\"})\n\n\t// Cloudflare\n\trequire.NoError(t, flow.EnsureCSRF(reg, &http.Request{\n\t\tHeader: http.Header{\"Cookie\": {\"__cflb=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7\", \"_cfuvid=blub\", \"cf_clearance=bla\"}},\n\t}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, \"\"), \"should ignore Cloudflare cookies\")\n\trequire.NoError(t, flow.EnsureCSRF(reg, &http.Request{\n\t\tHeader: http.Header{\"Cookie\": {\"__cflb=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7; __cfruid=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7\"}},\n\t}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, \"\"), \"should ignore Cloudflare cookies\")\n\trequire.EqualError(t, flow.EnsureCSRF(reg, &http.Request{\n\t\tHeader: http.Header{\"Cookie\": {\"__cflb=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7; __cfruid=0pg1RtZzPoPDprTf8gX3TJm8XF5hKZ4pZV74UCe7; some_cookie=some_value\"}},\n\t}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, \"\"), flow.ErrCookieHeaderNeedsBrowserFlow.Error(), \"should error because of some_cookie\")\n\trequire.EqualError(t, flow.EnsureCSRF(reg, &http.Request{\n\t\tHeader: http.Header{\"Cookie\": {\"some_cookie=some_value\"}},\n\t}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, \"\"), flow.ErrCookieHeaderNeedsBrowserFlow.Error(), \"should error because of some_cookie\")\n\trequire.NoError(t, flow.EnsureCSRF(reg, &http.Request{}, flow.TypeAPI, false, nosurfx.FakeCSRFTokenGenerator, \"\"), \"no cookie, no error\")\n}\n\nfunc TestMethodEnabledAndAllowed(t *testing.T) {\n\tctx := context.Background()\n\tconf, d := pkg.NewFastRegistryWithMocks(t)\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif err := flow.MethodEnabledAndAllowedFromRequest(r, flow.LoginFlow, \"password\", d); err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tw.WriteHeader(http.StatusNoContent)\n\t}))\n\tt.Cleanup(ts.Close)\n\n\tt.Run(\"allowed\", func(t *testing.T) {\n\t\tres, err := ts.Client().PostForm(ts.URL, url.Values{\"method\": {\"password\"}})\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\tassert.Equal(t, http.StatusNoContent, res.StatusCode)\n\t})\n\n\tt.Run(\"unknown\", func(t *testing.T) {\n\t\tres, err := ts.Client().PostForm(ts.URL, url.Values{\"method\": {\"other\"}})\n\t\trequire.NoError(t, err)\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\tassert.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\t\tassert.Contains(t, string(body), \"is not responsible for this request\")\n\t})\n\n\tt.Run(\"disabled\", func(t *testing.T) {\n\t\trequire.NoError(t, conf.Set(ctx, fmt.Sprintf(\"%s.%s.enabled\", config.ViperKeySelfServiceStrategyConfig, \"password\"), false))\n\t\tres, err := ts.Client().PostForm(ts.URL, url.Values{\"method\": {\"password\"}})\n\t\trequire.NoError(t, err)\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\tassert.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\t\tassert.Contains(t, string(body), \"The requested resource could not be found\")\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/settings/.snapshots/TestHandler-case=multi-schema_endpoint=init-description=init_a_flow_as_API-description=success.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"email\",\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/flow/settings/.snapshots/TestHandler-case=multi-schema_endpoint=init-description=init_a_flow_as_SPA-description=success.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"email\",\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/flow/settings/.snapshots/TestHandler-case=multi-schema_endpoint=init-description=init_a_flow_as_browser-description=success.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"email\",\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/flow/settings/.snapshots/TestHandler-endpoint=init-description=init_a_flow_as_API-description=success.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"email\",\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/flow/settings/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/swagger\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nvar ErrHookAbortFlow = errors.New(\"aborted settings hook execution\")\n\ntype (\n\terrorHandlerDependencies interface {\n\t\tconfig.Provider\n\t\terrorx.ManagementProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\n\t\tHandlerProvider\n\t\tFlowPersistenceProvider\n\t\tschema.IdentitySchemaProvider\n\t}\n\n\tErrorHandlerProvider interface{ SettingsFlowErrorHandler() *ErrorHandler }\n\n\tErrorHandler struct {\n\t\td errorHandlerDependencies\n\t}\n)\n\n// Is sent when a privileged session is required to perform the settings update.\n//\n// swagger:model needsPrivilegedSessionError\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype needsPrivilegedSessionError struct {\n\tError swagger.GenericError `json:\"error\"`\n\n\t// Points to where to redirect the user to next.\n\t//\n\t// required: true\n\tRedirectBrowserTo string `json:\"redirect_browser_to\"`\n}\n\n// FlowNeedsReAuth is sent when a privileged session is required to perform the settings update.\ntype FlowNeedsReAuth struct {\n\t*herodot.DefaultError `json:\"error\"`\n\n\t// Points to where to redirect the user to next.\n\tRedirectBrowserTo string `json:\"redirect_browser_to\"`\n}\n\nfunc (e *FlowNeedsReAuth) EnhanceJSONError() interface{} {\n\treturn e\n}\n\nfunc NewFlowNeedsReAuth() *FlowNeedsReAuth {\n\treturn &FlowNeedsReAuth{\n\t\tDefaultError: herodot.ErrForbidden.WithID(text.ErrIDNeedsPrivilegedSession).\n\t\t\tWithReasonf(\"The login session is too old and thus not allowed to update these fields. Please re-authenticate.\"),\n\t}\n}\n\nfunc NewErrorHandler(d errorHandlerDependencies) *ErrorHandler {\n\treturn &ErrorHandler{d: d}\n}\n\nfunc (s *ErrorHandler) reauthenticate(\n\tctx context.Context,\n\tw http.ResponseWriter,\n\tr *http.Request,\n\tf *Flow,\n\terr *FlowNeedsReAuth,\n) {\n\treturnTo := urlx.CopyWithQuery(urlx.AppendPaths(s.d.Config().SelfPublicURL(ctx), r.URL.Path), r.URL.Query())\n\n\tparams := url.Values{}\n\tparams.Set(\"refresh\", \"true\")\n\tparams.Set(\"return_to\", returnTo.String())\n\n\tredirectTo := urlx.AppendPaths(urlx.CopyWithQuery(s.d.Config().SelfPublicURL(ctx), params), login.RouteInitBrowserFlow).String()\n\terr.RedirectBrowserTo = redirectTo\n\tif f.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\thttp.Redirect(w, r, redirectTo, http.StatusSeeOther)\n}\n\nfunc (s *ErrorHandler) PrepareReplacementForExpiredFlow(ctx context.Context, w http.ResponseWriter, r *http.Request, f *Flow, id *identity.Identity, sess *session.Session, err error) (*flow.ExpiredError, error) {\n\te := new(flow.ExpiredError)\n\tif !errors.As(err, &e) {\n\t\treturn nil, nil\n\t}\n\n\t// create new flow because the old one is not valid\n\ta, err := s.d.SettingsHandler().FromOldFlow(ctx, w, r, id, sess, *f)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ta.UI.Messages.Add(text.NewErrorValidationSettingsFlowExpired(e.ExpiredAt))\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, a); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn e.WithFlow(a), nil\n}\n\nfunc (s *ErrorHandler) WriteFlowError(\n\tctx context.Context,\n\tw http.ResponseWriter,\n\tr *http.Request,\n\tgroup node.UiNodeGroup,\n\tf *Flow,\n\tid *identity.Identity,\n\tsess *session.Session,\n\terr error,\n) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.flow.settings.ErrorHandler.WriteFlowError\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.String(\"error\", err.Error()),\n\t\t))\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\tlogger := s.d.Logger().\n\t\tWithError(err).\n\t\tWithRequest(r).\n\t\tWithField(\"settings_flow\", f.ToLoggerField())\n\n\tlogger.Info(\"Encountered self-service settings error.\")\n\n\tshouldRespondWithJSON := x.IsJSONRequest(r)\n\tif f != nil {\n\t\tspan.SetAttributes(attribute.String(\"flow_id\", f.ID.String()))\n\t\tif f.Type == flow.TypeAPI {\n\t\t\tshouldRespondWithJSON = true\n\t\t}\n\t}\n\n\tif e := new(session.ErrNoActiveSessionFound); errors.As(err, &e) {\n\t\tif shouldRespondWithJSON {\n\t\t\ts.d.Writer().WriteError(w, r, err)\n\t\t} else {\n\t\t\tu := urlx.AppendPaths(s.d.Config().SelfPublicURL(ctx), login.RouteInitBrowserFlow)\n\t\t\thttp.Redirect(w, r, u.String(), http.StatusSeeOther)\n\t\t}\n\t\treturn\n\t}\n\n\tif aalErr := new(session.ErrAALNotSatisfied); errors.As(err, &aalErr) {\n\t\tif shouldRespondWithJSON {\n\t\t\ts.d.Writer().WriteError(w, r, aalErr)\n\t\t} else {\n\t\t\thttp.Redirect(w, r, aalErr.RedirectTo, http.StatusSeeOther)\n\t\t}\n\t\treturn\n\t}\n\n\tif f == nil {\n\t\ttrace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, uuid.Nil, \"\", \"\", err))\n\t\ts.forward(ctx, w, r, nil, err)\n\t\treturn\n\t}\n\ttrace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, f.ID, string(f.Type), f.Active.String(), err))\n\n\tif expired, inner := s.PrepareReplacementForExpiredFlow(ctx, w, r, f, id, sess, err); inner != nil {\n\t\ts.forward(ctx, w, r, f, err)\n\t\treturn\n\t} else if expired != nil {\n\t\tif id == nil {\n\t\t\ts.forward(ctx, w, r, f, err)\n\t\t\treturn\n\t\t}\n\n\t\tif f.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, expired)\n\t\t} else {\n\t\t\thttp.Redirect(w, r, expired.GetFlow().AppendTo(s.d.Config().SelfServiceFlowSettingsUI(ctx)).String(), http.StatusSeeOther)\n\t\t}\n\t\treturn\n\t}\n\n\tif errors.Is(err, flow.ErrStrategyAsksToReturnToUI) {\n\t\tif shouldRespondWithJSON {\n\t\t\ts.d.Writer().Write(w, r, f)\n\t\t} else {\n\t\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowSettingsUI(ctx)).String(), http.StatusSeeOther)\n\t\t}\n\t\treturn\n\t}\n\n\tif e := new(FlowNeedsReAuth); errors.As(err, &e) {\n\t\ts.reauthenticate(ctx, w, r, f, e)\n\t\treturn\n\t}\n\n\tif err := f.UI.ParseError(group, err); err != nil {\n\t\ts.forward(ctx, w, r, f, err)\n\t\treturn\n\t}\n\n\t// Lookup the schema from the loaded configuration. This local schema\n\t// URL is needed for sorting the UI nodes, instead of the public URL.\n\tschemas, err := s.d.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\ts.forward(ctx, w, r, f, err)\n\t\treturn\n\t}\n\n\tschema, err := schemas.GetByID(id.SchemaID)\n\tif err != nil {\n\t\ts.forward(ctx, w, r, f, err)\n\t\treturn\n\t}\n\n\tif err := sortNodes(ctx, f.UI.Nodes, schema.RawURL); err != nil {\n\t\ts.forward(ctx, w, r, f, err)\n\t\treturn\n\t}\n\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, f); err != nil {\n\t\ts.forward(ctx, w, r, f, err)\n\t\treturn\n\t}\n\n\tif f.Type == flow.TypeBrowser && !x.IsJSONRequest(r) {\n\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowSettingsUI(ctx)).String(), http.StatusSeeOther)\n\t\treturn\n\t}\n\n\tupdatedFlow, innerErr := s.d.SettingsFlowPersister().GetSettingsFlow(ctx, f.ID)\n\tif innerErr != nil {\n\t\ts.forward(ctx, w, r, updatedFlow, innerErr)\n\t}\n\n\ts.d.Writer().WriteCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), updatedFlow)\n}\n\nfunc (s *ErrorHandler) forward(ctx context.Context, w http.ResponseWriter, r *http.Request, rr *Flow, err error) {\n\tif rr == nil {\n\t\tif x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\t\ts.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\treturn\n\t}\n\n\tif rr.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteErrorCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), err)\n\t} else {\n\t\ts.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/settings/error_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gobuffalo/httptest\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestHandleError(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\n\tpublic, _ := testhelpers.NewKratosServer(t, reg)\n\n\trouter := http.NewServeMux()\n\tts := httptest.NewServer(router)\n\tt.Cleanup(ts.Close)\n\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\tloginTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\n\th := reg.SettingsFlowErrorHandler()\n\tsdk := testhelpers.NewSDKClient(public)\n\n\tvar settingsFlow *settings.Flow\n\tvar flowError error\n\tvar flowMethod node.UiNodeGroup\n\tvar id identity.Identity\n\trequire.NoError(t, faker.FakeData(&id))\n\tid.SchemaID = \"default\"\n\tid.State = identity.StateActive\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &id))\n\n\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil).WithContext(contextx.WithConfigValue(ctx, config.ViperKeySessionLifespan, time.Hour))\n\n\t// This needs an authenticated client in order to call the RouteGetFlow endpoint\n\ts, err := testhelpers.NewActiveSession(req, reg, &id, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\trequire.NoError(t, err)\n\tc := testhelpers.NewHTTPClientWithSessionToken(ctx, t, reg, s)\n\trouter.HandleFunc(\"GET /error\", func(w http.ResponseWriter, r *http.Request) {\n\t\th.WriteFlowError(ctx, w, r, flowMethod, settingsFlow, &id, s, flowError)\n\t})\n\n\trouter.HandleFunc(\"GET /fake-redirect\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _, err := reg.LoginHandler().NewLoginFlow(w, r, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t})\n\n\treset := func() {\n\t\tsettingsFlow = nil\n\t\tflowError = nil\n\t\tflowMethod = node.DefaultGroup\n\t}\n\n\tnewFlow := func(t *testing.T, ttl time.Duration, ft flow.Type) *settings.Flow {\n\t\treq := &http.Request{URL: urlx.ParseOrPanic(\"/\")}\n\t\tf, err := settings.NewFlow(conf, ttl, req, &id, ft)\n\t\trequire.NoError(t, err)\n\n\t\tfor _, s := range reg.SettingsStrategies(context.Background()) {\n\t\t\trequire.NoError(t, s.PopulateSettingsMethod(ctx, req, &id, f))\n\t\t}\n\n\t\trequire.NoError(t, reg.SettingsFlowPersister().CreateSettingsFlow(context.Background(), f))\n\t\treturn f\n\t}\n\n\texpectErrorUI := func(t *testing.T) (map[string]interface{}, *http.Response) {\n\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\trequire.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tsse, _, err := sdk.FrontendAPI.GetFlowError(context.Background()).\n\t\t\tId(res.Request.URL.Query().Get(\"id\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\treturn sse.Error, nil\n\t}\n\n\texpiredAnHourAgo := time.Now().Add(-time.Hour)\n\n\tt.Run(\"case=error with nil flow defaults to error ui redirect\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tflowMethod = settings.StrategyProfile\n\n\t\tsse, _ := expectErrorUI(t)\n\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t})\n\n\tt.Run(\"case=error with nil flow detects application/json\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tflowMethod = settings.StrategyProfile\n\n\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tassert.Contains(t, res.Header.Get(\"Content-Type\"), \"application/json\")\n\t\tassert.NotContains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, string(body), \"system error\")\n\t})\n\n\tfor _, tc := range []struct {\n\t\tn string\n\t\tt flow.Type\n\t}{\n\t\t{\"api\", flow.TypeAPI},\n\t\t{\"spa\", flow.TypeBrowser},\n\t} {\n\t\tt.Run(\"flow=\"+tc.n, func(t *testing.T) {\n\t\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tsettingsFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = flow.NewFlowExpiredError(expiredAnHourAgo)\n\t\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\t\tres, err := c.Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Contains(t, res.Request.URL.String(), ts.URL+\"/error\")\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusGone, res.StatusCode, \"%+v\\n\\t%s\", res.Request, body)\n\n\t\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.GetBytes(body, \"use_flow_id\").String())\n\t\t\t\tassertx.EqualAsJSONExcept(t, flow.NewFlowExpiredError(expiredAnHourAgo), json.RawMessage(body), []string{\"since\", \"redirect_browser_to\", \"use_flow_id\"})\n\t\t\t})\n\n\t\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tsettingsFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(gjson.GetBytes(body, \"ui.messages.0.id\").Int()), \"%s\", body)\n\t\t\t\tassert.Equal(t, settingsFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=return to UI error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tsettingsFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\t\tsettingsFlow.IdentityID = id.ID\n\t\t\t\tflowError = flow.ErrStrategyAsksToReturnToUI\n\t\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, settingsFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=no active session\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tsettingsFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\t\tsettingsFlow.IdentityID = id.ID\n\t\t\t\tflowError = errors.WithStack(session.NewErrNoActiveSessionFound())\n\t\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusUnauthorized, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, session.NewErrNoActiveSessionFound().Reason(), gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"case=aal too low\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tsettingsFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\t\tsettingsFlow.IdentityID = id.ID\n\t\t\t\tflowError = errors.WithStack(session.NewErrAALNotSatisfied(\"a\"))\n\t\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusForbidden, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassertx.EqualAsJSON(t, session.NewErrAALNotSatisfied(\"a\"), json.RawMessage(body))\n\t\t\t})\n\n\t\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tsettingsFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.JSONEq(t, x.MustEncodeJSON(t, flowError), gjson.GetBytes(body, \"error\").Raw)\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\texpectSettingsUI := func(t *testing.T) (*settings.Flow, *http.Response) {\n\t\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowSettingsUI(ctx).String()+\"?flow=\")\n\n\t\t\tsf, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(res.Request.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t\treturn sf, res\n\t\t}\n\n\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tsettingsFlow = &settings.Flow{Type: flow.TypeBrowser}\n\t\t\tflowError = flow.NewFlowExpiredError(expiredAnHourAgo)\n\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\tlf, _ := expectSettingsUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1)\n\t\t\tassert.Equal(t, int(text.ErrorValidationSettingsFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t})\n\n\t\tt.Run(\"case=return to ui error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tsettingsFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tsettingsFlow.IdentityID = id.ID\n\t\t\tflowError = flow.ErrStrategyAsksToReturnToUI\n\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\tlf, _ := expectSettingsUI(t)\n\t\t\tassert.EqualValues(t, settingsFlow.ID, lf.ID)\n\t\t})\n\n\t\tt.Run(\"case=no active session error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tsettingsFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tsettingsFlow.IdentityID = id.ID\n\t\t\tflowError = errors.WithStack(session.NewErrNoActiveSessionFound())\n\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL)\n\n\t\t\tlf, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(res.Request.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, identity.AuthenticatorAssuranceLevel1, lf.RequestedAAL)\n\t\t})\n\n\t\tt.Run(\"case=aal too low\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tsettingsFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tsettingsFlow.IdentityID = id.ID\n\t\t\tflowError = errors.WithStack(session.NewErrAALNotSatisfied(conf.SelfServiceFlowLoginUI(ctx).String()))\n\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\tclient := &http.Client{}\n\t\t\t*client = *ts.Client()\n\n\t\t\t// disable redirects\n\t\t\tclient.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\n\t\t\tres, err := client.Get(ts.URL + \"/error\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\tloc, err := res.Location()\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// we should end up at the login URL since the NewALLNotSatisfied takes in a redirect to URL.\n\t\t\tassert.Contains(t, loc.String(), loginTS.URL)\n\n\t\t\t// test the JSON resoponse as well\n\t\t\trequest, err := http.NewRequest(\"GET\", ts.URL+\"/error\", nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequest.Header.Add(\"Accept\", \"application/json\")\n\n\t\t\tres, err = client.Do(request)\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// the body should contain the reason for the error\n\t\t\tbody := x.MustReadAll(res.Body)\n\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\trequire.NotEmpty(t, gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t// We end up at the error endpoint with an aal2 error message because ts.client has no session.\n\t\t\tassert.Equal(t, session.NewErrAALNotSatisfied(\"\").Reason(), gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"case=session old error\", func(t *testing.T) {\n\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{urlx.AppendPaths(conf.SelfPublicURL(ctx), \"/error\").String()})\n\t\t\tt.Cleanup(reset)\n\n\t\t\tsettingsFlow = &settings.Flow{Type: flow.TypeBrowser}\n\t\t\tflowError = settings.NewFlowNeedsReAuth()\n\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\trequire.Contains(t, res.Request.URL.String(), conf.GetProvider(ctx).String(config.ViperKeySelfServiceLoginUI))\n\t\t})\n\n\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tsettingsFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\tlf, _ := expectSettingsUI(t)\n\t\t\trequire.NotEmpty(t, lf.UI, x.MustEncodeJSON(t, lf))\n\t\t\trequire.Len(t, lf.UI.Messages, 1, x.MustEncodeJSON(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(lf.UI.Messages[0].ID), x.MustEncodeJSON(t, lf))\n\t\t})\n\n\t\tt.Run(\"case=inaccessible public URL\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\t// Since WriteFlowError is invoked directly by the /error handler above,\n\t\t\t// manipulate the schema's URL directly to a bad URL.\n\t\t\tid.SchemaURL = \"http://some.random.url\"\n\n\t\t\tsettingsFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\tlf, _ := expectSettingsUI(t)\n\t\t\trequire.NotEmpty(t, lf.UI, x.MustEncodeJSON(t, lf))\n\t\t\trequire.Len(t, lf.UI.Messages, 1, x.MustEncodeJSON(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(lf.UI.Messages[0].ID), x.MustEncodeJSON(t, lf))\n\t\t})\n\n\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tsettingsFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\tflowMethod = settings.StrategyProfile\n\n\t\t\tsse, _ := expectErrorUI(t)\n\t\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/settings/flow.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\n// Flow represents a Settings Flow\n//\n// This flow is used when an identity wants to update settings\n// (e.g. profile data, passwords, ...) in a selfservice manner.\n//\n// We recommend reading the [User Settings Documentation](../self-service/flows/user-settings)\n//\n// swagger:model settingsFlow\ntype Flow struct {\n\t// ID represents the flow's unique ID. When performing the settings flow, this\n\t// represents the id in the settings ui's query parameter: http://<selfservice.flows.settings.ui_url>?flow=<id>\n\t//\n\t// required: true\n\t// type: string\n\t// format: uuid\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// Type represents the flow's type which can be either \"api\" or \"browser\", depending on the flow interaction.\n\t//\n\t// required: true\n\tType flow.Type `json:\"type\" db:\"type\" faker:\"flow_type\"`\n\n\t// ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to update the setting,\n\t// a new flow has to be initiated.\n\t//\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the flow occurred.\n\t//\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used\n\t// to forward information contained in the URL's path or query for example.\n\t//\n\t// required: true\n\tRequestURL string `json:\"request_url\" db:\"request_url\"`\n\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo string `json:\"return_to,omitempty\" db:\"-\"`\n\n\t// Active, if set, contains the registration method that is being used. It is initially\n\t// not set.\n\tActive sqlxx.NullString `json:\"active,omitempty\" db:\"active_method\"`\n\n\t// UI contains data which must be shown in the user interface.\n\t//\n\t// required: true\n\tUI *container.Container `json:\"ui\" db:\"ui\"`\n\n\t// Identity contains the identity's data in raw form.\n\t//\n\t// If `state` is `success` this will be the updated identity!\n\t//\n\t// required: true\n\tIdentity *identity.Identity `json:\"identity\" faker:\"identity\" db:\"-\" belongs_to:\"identities\" fk_id:\"IdentityID\"`\n\n\t// State represents the state of this flow. It knows two states:\n\t//\n\t// - show_form: No user data has been collected, or it is invalid, and thus the form should be shown.\n\t// - success: Indicates that the settings flow has been updated successfully with the provided data.\n\t//\t   Done will stay true when repeatedly checking. If set to true, done will revert back to false only\n\t//\t   when a flow with invalid (e.g. \"please use a valid phone number\") data was sent.\n\t//\n\t// required: true\n\tState State `json:\"state\" faker:\"-\" db:\"state\"`\n\n\t// InternalContext stores internal context used by internals - for example MFA keys.\n\tInternalContext sqlxx.JSONRawMessage `db:\"internal_context\" json:\"-\" faker:\"-\"`\n\n\t// IdentityID is a helper struct field for gobuffalo.pop.\n\tIdentityID uuid.UUID `json:\"-\" faker:\"-\" db:\"identity_id\"`\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\tNID       uuid.UUID `json:\"-\" faker:\"-\" db:\"nid\"`\n\n\t// Contains a list of actions, that could follow this flow\n\t//\n\t// It can, for example, contain a reference to the verification flow, created as part of the user's\n\t// registration.\n\t//\n\t// required: false\n\tContinueWithItems []flow.ContinueWith `json:\"continue_with,omitempty\" db:\"-\" faker:\"-\" `\n\n\t// TransientPayload is used to pass data from the settings flow to hooks and email templates\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" faker:\"-\" db:\"-\"`\n}\n\nvar (\n\t_ flow.Flow              = (*Flow)(nil)\n\t_ flow.InternalContexter = (*Flow)(nil)\n)\n\nfunc MustNewFlow(conf *config.Config, exp time.Duration, r *http.Request, i *identity.Identity, ft flow.Type) *Flow {\n\tf, err := NewFlow(conf, exp, r, i, ft)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn f\n}\n\nfunc NewFlow(conf *config.Config, exp time.Duration, r *http.Request, i *identity.Identity, ft flow.Type) (*Flow, error) {\n\tnow := time.Now().UTC()\n\tid := x.NewUUID()\n\n\t// Pre-validate the return to URL which is contained in the HTTP request.\n\trequestURL := x.RequestURL(r).String()\n\t_, err := redir.SecureRedirectTo(r,\n\t\tconf.SelfServiceBrowserDefaultReturnTo(r.Context()),\n\t\tredir.SecureRedirectUseSourceURL(requestURL),\n\t\tredir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Flow{\n\t\tID:         id,\n\t\tExpiresAt:  now.Add(exp),\n\t\tIssuedAt:   now,\n\t\tRequestURL: requestURL,\n\t\tIdentityID: i.ID,\n\t\tIdentity:   i,\n\t\tType:       ft,\n\t\tState:      flow.StateShowForm,\n\t\tUI: &container.Container{\n\t\t\tMethod: \"POST\",\n\t\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(conf.SelfPublicURL(r.Context()), RouteSubmitFlow), id).String(),\n\t\t},\n\t\tInternalContext: []byte(\"{}\"),\n\t}, nil\n}\n\nfunc (f *Flow) GetInternalContext() sqlxx.JSONRawMessage        { return f.InternalContext }\nfunc (f *Flow) SetInternalContext(message sqlxx.JSONRawMessage) { f.InternalContext = message }\nfunc (f *Flow) GetType() flow.Type                              { return f.Type }\nfunc (f *Flow) GetRequestURL() string                           { return f.RequestURL }\nfunc (Flow) TableName() string                                  { return \"selfservice_settings_flows\" }\nfunc (f Flow) GetID() uuid.UUID                                 { return f.ID }\nfunc (f *Flow) AppendTo(src *url.URL) *url.URL                  { return flow.AppendFlowTo(src, f.ID) }\nfunc (f *Flow) GetUI() *container.Container                     { return f.UI }\nfunc (f *Flow) ContinueWith() []flow.ContinueWith               { return f.ContinueWithItems }\nfunc (f *Flow) GetState() State                                 { return f.State }\nfunc (Flow) GetFlowName() flow.FlowName                         { return flow.SettingsFlow }\nfunc (f *Flow) SetState(state State)                            { f.State = state }\nfunc (f *Flow) GetTransientPayload() json.RawMessage            { return f.TransientPayload }\n\nfunc (f *Flow) Valid(s *session.Session) error {\n\tif f.ExpiresAt.Before(time.Now().UTC()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\n\tif f.IdentityID != s.Identity.ID {\n\t\treturn errors.WithStack(herodot.ErrForbidden.WithID(text.ErrIDInitiatedBySomeoneElse).WithReasonf(\n\t\t\t\"The request was initiated by someone else and has been blocked for security reasons. Please go back and try again.\"))\n\t}\n\n\treturn nil\n}\n\nfunc (f *Flow) EnsureInternalContext() {\n\tif !gjson.ParseBytes(f.InternalContext).IsObject() {\n\t\tf.InternalContext = []byte(\"{}\")\n\t}\n}\n\nfunc (f Flow) MarshalJSON() ([]byte, error) {\n\ttype local Flow\n\tf.SetReturnTo()\n\treturn json.Marshal(local(f))\n}\n\nfunc (f *Flow) SetReturnTo() {\n\t// Return to is already set, do not overwrite it.\n\tif len(f.ReturnTo) > 0 {\n\t\treturn\n\t}\n\tif u, err := url.Parse(f.RequestURL); err == nil {\n\t\tf.ReturnTo = u.Query().Get(\"return_to\")\n\t}\n}\n\nfunc (f *Flow) AfterFind(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\nfunc (f *Flow) AfterSave(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\nfunc (f *Flow) AddContinueWith(c flow.ContinueWith) {\n\tf.ContinueWithItems = append(f.ContinueWithItems, c)\n}\n\nfunc (f *Flow) ToLoggerField() map[string]any {\n\tif f == nil {\n\t\treturn map[string]any{}\n\t}\n\treturn map[string]any{\n\t\t\"id\":          f.ID.String(),\n\t\t\"return_to\":   f.ReturnTo,\n\t\t\"request_url\": f.RequestURL,\n\t\t\"active\":      f.Active,\n\t\t\"Type\":        f.Type,\n\t\t\"nid\":         f.NID,\n\t\t\"state\":       f.State,\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/settings/flow_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings_test\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/jsonx\"\n\n\t\"github.com/ory/kratos/pkg\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestFakeFlow(t *testing.T) {\n\tvar r settings.Flow\n\trequire.NoError(t, faker.FakeData(&r))\n\n\tassert.Equal(t, uuid.Nil, r.ID)\n\tassert.NotEmpty(t, r.IssuedAt)\n\tassert.NotEmpty(t, r.ExpiresAt)\n\tassert.NotEmpty(t, r.RequestURL)\n\tassert.NotEmpty(t, r.Active)\n}\n\nfunc TestNewFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf := pkg.NewConfigurationWithDefaults(t)\n\n\tid := &identity.Identity{ID: x.NewUUID()}\n\tt.Run(\"case=0\", func(t *testing.T) {\n\t\tr, err := settings.NewFlow(conf, 0, &http.Request{URL: urlx.ParseOrPanic(\"/\"),\n\t\t\tHost: \"ory.sh\", TLS: &tls.ConnectionState{}}, id, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, r.IssuedAt, r.ExpiresAt)\n\t\tassert.Equal(t, flow.TypeBrowser, r.Type)\n\t\tassert.Equal(t, \"https://ory.sh/\", r.RequestURL)\n\t})\n\n\tt.Run(\"type=return_to\", func(t *testing.T) {\n\t\t_, err := registration.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=https://not-allowed/foobar\"}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.Error(t, err)\n\n\t\t_, err = registration.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=\" + urlx.AppendPaths(conf.SelfPublicURL(ctx), \"/self-service/login/browser\").String()}, Host: \"ory.sh\"}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t})\n\n\tt.Run(\"case=1\", func(t *testing.T) {\n\t\tr, err := settings.NewFlow(conf, 0, &http.Request{\n\t\t\tURL:  urlx.ParseOrPanic(\"/?refresh=true\"),\n\t\t\tHost: \"ory.sh\"}, id, flow.TypeAPI)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, r.IssuedAt, r.ExpiresAt)\n\t\tassert.Equal(t, flow.TypeAPI, r.Type)\n\t\tassert.Equal(t, \"http://ory.sh/?refresh=true\", r.RequestURL)\n\t})\n\n\tt.Run(\"case=2\", func(t *testing.T) {\n\t\tr, err := settings.NewFlow(conf, 0, &http.Request{\n\t\t\tURL:  urlx.ParseOrPanic(\"https://ory.sh/\"),\n\t\t\tHost: \"ory.sh\"}, id, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, \"https://ory.sh/\", r.RequestURL)\n\t})\n}\n\nfunc TestFlow(t *testing.T) {\n\tconf := pkg.NewConfigurationWithDefaults(t)\n\n\talice := x.NewUUID()\n\tmalice := x.NewUUID()\n\tfor k, tc := range []struct {\n\t\tr         *settings.Flow\n\t\ts         *session.Session\n\t\texpectErr bool\n\t}{\n\t\t{\n\t\t\tr: settings.MustNewFlow(\n\t\t\t\tconf,\n\t\t\t\ttime.Hour,\n\t\t\t\t&http.Request{URL: urlx.ParseOrPanic(\"http://foo/bar/baz\"), Host: \"foo\"},\n\t\t\t\t&identity.Identity{ID: alice},\n\t\t\t\tflow.TypeBrowser,\n\t\t\t),\n\t\t\ts: &session.Session{Identity: &identity.Identity{ID: alice}},\n\t\t},\n\t\t{\n\t\t\tr: settings.MustNewFlow(\n\t\t\t\tconf,\n\t\t\t\ttime.Hour,\n\t\t\t\t&http.Request{URL: urlx.ParseOrPanic(\"http://foo/bar/baz\"), Host: \"foo\"},\n\t\t\t\t&identity.Identity{ID: alice},\n\t\t\t\tflow.TypeBrowser,\n\t\t\t),\n\t\t\ts:         &session.Session{Identity: &identity.Identity{ID: malice}},\n\t\t\texpectErr: true,\n\t\t},\n\t\t{\n\t\t\tr: settings.MustNewFlow(\n\t\t\t\tconf,\n\t\t\t\t-time.Hour,\n\t\t\t\t&http.Request{URL: urlx.ParseOrPanic(\"http://foo/bar/baz\"), Host: \"foo\"},\n\t\t\t\t&identity.Identity{ID: alice},\n\t\t\t\tflow.TypeBrowser,\n\t\t\t),\n\t\t\ts:         &session.Session{Identity: &identity.Identity{ID: alice}},\n\t\t\texpectErr: true,\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\terr := tc.r.Valid(tc.s)\n\t\t\tif tc.expectErr {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\t\t})\n\t}\n}\n\nfunc TestGetType(t *testing.T) {\n\tfor _, ft := range []flow.Type{\n\t\tflow.TypeAPI,\n\t\tflow.TypeBrowser,\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", ft), func(t *testing.T) {\n\t\t\tr := &settings.Flow{Type: ft}\n\t\t\tassert.Equal(t, ft, r.GetType())\n\t\t})\n\t}\n}\n\nfunc TestGetRequestURL(t *testing.T) {\n\texpectedURL := \"http://foo/bar/baz\"\n\tf := &settings.Flow{RequestURL: expectedURL}\n\tassert.Equal(t, expectedURL, f.GetRequestURL())\n}\n\nfunc TestEnsureInternalContext(t *testing.T) {\n\tf := new(settings.Flow)\n\tf.EnsureInternalContext()\n\tassert.Equal(t, \"{}\", string(f.InternalContext))\n}\n\nfunc TestFlowEncodeJSON(t *testing.T) {\n\tassert.EqualValues(t, \"\", gjson.Get(jsonx.TestMarshalJSONString(t, &settings.Flow{RequestURL: \"https://foo.bar?foo=bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, &settings.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, settings.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n}\n\nfunc TestFlowDontOverrideReturnTo(t *testing.T) {\n\tf := &settings.Flow{ReturnTo: \"/foo\"}\n\tf.SetReturnTo()\n\tassert.Equal(t, \"/foo\", f.ReturnTo)\n\n\tf = &settings.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}\n\tf.SetReturnTo()\n\tassert.Equal(t, \"/bar\", f.ReturnTo)\n}\n"
  },
  {
    "path": "selfservice/flow/settings/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst (\n\tRouteInitBrowserFlow = \"/self-service/settings/browser\"\n\tRouteInitAPIFlow     = \"/self-service/settings/api\"\n\tRouteGetFlow         = \"/self-service/settings/flows\"\n\n\tRouteSubmitFlow = \"/self-service/settings\"\n\n\tContinuityPrefix = \"ory_kratos_settings\"\n)\n\nfunc ContinuityKey(id string) string {\n\treturn ContinuityPrefix + \"_\" + id\n}\n\ntype (\n\thandlerDependencies interface {\n\t\tnosurfx.CSRFProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\n\t\tconfig.Provider\n\n\t\tsession.HandlerProvider\n\t\tsession.ManagementProvider\n\n\t\tidentity.ValidationProvider\n\t\tidentity.ManagementProvider\n\t\tidentity.PrivilegedPoolProvider\n\n\t\terrorx.ManagementProvider\n\n\t\tcontinuity.ManagementProvider\n\n\t\tErrorHandlerProvider\n\t\tFlowPersistenceProvider\n\t\tStrategyProvider\n\t\tHookExecutorProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\n\t\tschema.IdentitySchemaProvider\n\n\t\tlogin.HandlerProvider\n\t}\n\tHandlerProvider interface {\n\t\tSettingsHandler() *Handler\n\t}\n\tHandler struct {\n\t\td    handlerDependencies\n\t\tcsrf nosurfx.CSRFToken\n\t}\n)\n\nfunc NewHandler(d handlerDependencies) *Handler { return &Handler{d: d, csrf: nosurf.Token} }\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\th.d.CSRFHandler().IgnorePath(RouteInitAPIFlow)\n\th.d.CSRFHandler().IgnorePath(RouteSubmitFlow)\n\n\tpublic.GET(RouteInitBrowserFlow, h.d.SessionHandler().IsAuthenticated(h.createBrowserSettingsFlow, func(w http.ResponseWriter, r *http.Request) {\n\t\tif x.IsJSONRequest(r) {\n\t\t\th.d.Writer().WriteError(w, r, session.NewErrNoActiveSessionFound())\n\t\t} else {\n\t\t\tloginFlowUrl := h.d.Config().SelfPublicURL(r.Context()).JoinPath(login.RouteInitBrowserFlow).String()\n\t\t\tredirectUrl, err := redir.TakeOverReturnToParameter(r.URL.String(), loginFlowUrl)\n\t\t\tif err != nil {\n\t\t\t\thttp.Redirect(w, r, h.d.Config().SelfServiceFlowLoginUI(r.Context()).String(), http.StatusSeeOther)\n\t\t\t} else {\n\t\t\t\thttp.Redirect(w, r, redirectUrl, http.StatusSeeOther)\n\t\t\t}\n\t\t}\n\t}))\n\n\tpublic.GET(RouteInitAPIFlow, h.d.SessionHandler().IsAuthenticated(h.createNativeSettingsFlow, nil))\n\tpublic.GET(RouteGetFlow, h.d.SessionHandler().IsAuthenticated(h.getSettingsFlow, OnUnauthenticated(h.d)))\n\n\tpublic.POST(RouteSubmitFlow, h.d.SessionHandler().IsAuthenticated(h.updateSettingsFlow, OnUnauthenticated(h.d)))\n\tpublic.GET(RouteSubmitFlow, h.d.SessionHandler().IsAuthenticated(h.updateSettingsFlow, OnUnauthenticated(h.d)))\n}\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))\n\n\tadmin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))\n\n\tadmin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n}\n\nfunc (h *Handler) NewFlow(ctx context.Context, w http.ResponseWriter, r *http.Request, i *identity.Identity, s *session.Session, ft flow.Type) (_ *Flow, err error) {\n\tctx, span := h.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.flow.settings.Handler.NewFlow\")\n\tdefer otelx.End(span, &err)\n\n\tf, err := NewFlow(h.d.Config(), h.d.Config().SelfServiceFlowSettingsFlowLifespan(r.Context()), r, i, ft)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := h.d.SettingsHookExecutor().PreSettingsHook(ctx, w, r, f, s); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, strategy := range h.d.SettingsStrategies(ctx) {\n\t\tif err := h.d.ContinuityManager().Abort(ctx, w, r, ContinuityKey(strategy.SettingsStrategyID())); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err := strategy.PopulateSettingsMethod(ctx, r, i, f); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tds, err := h.d.Config().IdentityTraitsSchemaURL(ctx, i.SchemaID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := sortNodes(r.Context(), f.UI.Nodes, ds.String()); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := h.d.SettingsFlowPersister().CreateSettingsFlow(r.Context(), f); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn f, nil\n}\n\nfunc (h *Handler) FromOldFlow(ctx context.Context, w http.ResponseWriter, r *http.Request, i *identity.Identity, s *session.Session, of Flow) (*Flow, error) {\n\tnf, err := h.NewFlow(ctx, w, r, i, s, of.Type)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnf.RequestURL = of.RequestURL\n\treturn nf, nil\n}\n\n// Create Native Settings Flow Parameters\n//\n// swagger:parameters createNativeSettingsFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createNativeSettingsFlow struct {\n\t// The Session Token of the Identity performing the settings flow.\n\t//\n\t// in: header\n\tSessionToken string `json:\"X-Session-Token\"`\n}\n\n// swagger:route GET /self-service/settings/api frontend createNativeSettingsFlow\n//\n// # Create Settings Flow for Native Apps\n//\n// This endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\n// You must provide a valid Ory Kratos Session Token for this endpoint to respond with HTTP 200 OK.\n//\n// To fetch an existing settings flow call `/self-service/settings/flows?flow=<flow_id>`.\n//\n// You MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n// Pages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n// you vulnerable to a variety of CSRF attacks.\n//\n// Depending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n// Assurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n// credentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n// to sign in with the second factor or change the configuration.\n//\n// In the case of an error, the `error.id` of the JSON response body can be one of:\n//\n// - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n// - `session_inactive`: No Ory Session was found - sign in a user first.\n//\n// This endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n//\n// More information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n//\n//\t   Schemes: http, https\n//\n//\t   Responses:\n//\t\t  200: settingsFlow\n//\t\t  400: errorGeneric\n//\t\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createNativeSettingsFlow(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\ts, err := h.d.SessionManager().FetchFromRequestContext(ctx, r)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif err := h.d.SessionManager().DoesSessionSatisfy(ctx, s, h.d.Config().SelfServiceSettingsRequiredAAL(ctx)); err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tf, err := h.NewFlow(ctx, w, r, s.Identity, s, flow.TypeAPI)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.d.Writer().Write(w, r, f)\n}\n\n// Create Browser Settings Flow Parameters\n//\n// swagger:parameters createBrowserSettingsFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createBrowserSettingsFlow struct {\n\t// The URL to return the browser to after the flow was completed.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// swagger:route GET /self-service/settings/browser frontend createBrowserSettingsFlow\n//\n// # Create Settings Flow for Browsers\n//\n// This endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\n// `selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\n// Ory Kratos Session Cookie is included in the request, a login flow will be initialized.\n//\n// If this endpoint is opened as a link in the browser, it will be redirected to\n// `selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid user session\n// was set, the browser will be redirected to the login endpoint.\n//\n// If this endpoint is called via an AJAX request, the response contains the settings flow without any redirects\n// or a 401 forbidden error if no valid session was set.\n//\n// Depending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n// Assurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n// credentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n// to sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n//\n// If this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n// case of an error, the `error.id` of the JSON response body can be one of:\n//\n// - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n// - `session_inactive`: No Ory Session was found - sign in a user first.\n// - `security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n//\n// This endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\n//\n// More information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: settingsFlow\n//\t  303: emptyResponse\n//\t  400: errorGeneric\n//\t  401: errorGeneric\n//\t  403: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createBrowserSettingsFlow(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\ts, err := h.d.SessionManager().FetchFromRequestContext(ctx, r)\n\tif err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\treturn\n\t}\n\n\tvar managerOptions []session.ManagerOptions\n\trequestURL := x.RequestURL(r)\n\tif requestURL.Query().Get(\"return_to\") != \"\" {\n\t\tmanagerOptions = append(managerOptions, session.WithRequestURL(requestURL.String()))\n\t}\n\n\tif err := h.d.SessionManager().DoesSessionSatisfy(ctx, s, h.d.Config().SelfServiceSettingsRequiredAAL(ctx), managerOptions...); err != nil {\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, nil, nil, nil, err)\n\t\treturn\n\t}\n\n\tf, err := h.NewFlow(ctx, w, r, s.Identity, s, flow.TypeBrowser)\n\tif err != nil {\n\t\th.d.SelfServiceErrorManager().Forward(ctx, w, r, err)\n\t\treturn\n\t}\n\n\tredirTo := f.AppendTo(h.d.Config().SelfServiceFlowSettingsUI(ctx)).String()\n\tx.SendFlowCompletedAsRedirectOrJSON(w, r, h.d.Writer(), f, redirTo)\n}\n\n// Get Settings Flow\n//\n// swagger:parameters getSettingsFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getSettingsFlow struct {\n\t// ID is the Settings Flow ID\n\t//\n\t// The value for this parameter comes from `flow` URL Query parameter sent to your\n\t// application (e.g. `/settings?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tID string `json:\"id\"`\n\n\t// The Session Token\n\t//\n\t// When using the SDK in an app without a browser, please include the\n\t// session token here.\n\t//\n\t// in: header\n\tSessionToken string `json:\"X-Session-Token\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// swagger:route GET /self-service/settings/flows frontend getSettingsFlow\n//\n// # Get Settings Flow\n//\n// When accessing this endpoint through Ory Kratos' Public API you must ensure that either the Ory Kratos Session Cookie\n// or the Ory Kratos Session Token are set.\n//\n// Depending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n// Assurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n// credentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n// to sign in with the second factor or change the configuration.\n//\n// You can access this endpoint without credentials when using Ory Kratos' Admin API.\n//\n// If this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\n// case of an error, the `error.id` of the JSON response body can be one of:\n//\n//   - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n//   - `session_inactive`: No Ory Session was found - sign in a user first.\n//   - `security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\n//     identity logged in instead.\n//\n// More information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: settingsFlow\n//\t  401: errorGeneric\n//\t  403: errorGeneric\n//\t  404: errorGeneric\n//\t  410: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-low\nfunc (h *Handler) getSettingsFlow(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\trid := x.ParseUUID(r.URL.Query().Get(\"id\"))\n\tpr, err := h.d.SettingsFlowPersister().GetSettingsFlow(ctx, rid)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tsess, err := h.d.SessionManager().FetchFromRequestContext(ctx, r)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif pr.IdentityID != sess.Identity.ID {\n\t\th.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrForbidden.\n\t\t\tWithID(text.ErrIDInitiatedBySomeoneElse).\n\t\t\tWithReasonf(\"The request was made for another identity and has been blocked for security reasons.\")))\n\t\treturn\n\t}\n\n\t// we cannot redirect back to the request URL (/self-service/settings/flows?id=...) since it would just redirect\n\t// to a page displaying raw JSON to the client (browser), which is not what we want.\n\t// Let's rather carry over the flow ID as a query parameter and redirect to the settings UI URL.\n\trequestURL := urlx.CopyWithQuery(h.d.Config().SelfServiceFlowSettingsUI(ctx), url.Values{\"flow\": {rid.String()}})\n\tif err := h.d.SessionManager().DoesSessionSatisfy(ctx, sess, h.d.Config().SelfServiceSettingsRequiredAAL(ctx), session.WithRequestURL(requestURL.String())); err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif pr.ExpiresAt.Before(time.Now().UTC()) {\n\t\tif pr.Type == flow.TypeBrowser {\n\t\t\tredirectURL := flow.GetFlowExpiredRedirectURL(ctx, h.d.Config(), RouteInitBrowserFlow, pr.ReturnTo)\n\n\t\t\th.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.\n\t\t\t\tWithReason(\"The settings flow has expired. Redirect the user to the settings flow init endpoint to initialize a new settings flow.\").\n\t\t\t\tWithDetail(\"redirect_to\", redirectURL.String()).\n\t\t\t\tWithDetail(\"return_to\", pr.ReturnTo)))\n\t\t\treturn\n\t\t}\n\t\th.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.\n\t\t\tWithReason(\"The settings flow has expired. Call the settings flow init API endpoint to initialize a new settings flow.\").\n\t\t\tWithDetail(\"api\", urlx.AppendPaths(h.d.Config().SelfPublicURL(ctx), RouteInitAPIFlow).String())))\n\t\treturn\n\t}\n\n\th.d.Writer().Write(w, r, pr)\n}\n\n// Update Settings Flow Parameters\n//\n// swagger:parameters updateSettingsFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateSettingsFlow struct {\n\t// The Settings Flow ID\n\t//\n\t// The value for this parameter comes from `flow` URL Query parameter sent to your\n\t// application (e.g. `/settings?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tFlow string `json:\"flow\"`\n\n\t// in: body\n\t// required: true\n\tBody updateSettingsFlowBody\n\n\t// The Session Token of the Identity performing the settings flow.\n\t//\n\t// in: header\n\tSessionToken string `json:\"X-Session-Token\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// Update Settings Flow Request Body\n//\n// swagger:model updateSettingsFlowBody\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateSettingsFlowBody struct{}\n\n// swagger:route POST /self-service/settings frontend updateSettingsFlow\n//\n// # Complete Settings Flow\n//\n// Use this endpoint to complete a settings flow by sending an identity's updated password. This endpoint\n// behaves differently for API and browser flows.\n//\n// API-initiated flows expect `application/json` to be sent in the body and respond with\n//   - HTTP 200 and an application/json body with the session token on success;\n//   - HTTP 303 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\n//   - HTTP 400 on form validation errors.\n//   - HTTP 401 when the endpoint is called without a valid session token.\n//   - HTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\n//     Implies that the user needs to re-authenticate.\n//\n// Browser flows without HTTP Header `Accept` or with `Accept: text/*` respond with\n//   - a HTTP 303 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\n//   - a HTTP 303 redirect to the Settings UI URL with the flow ID containing the validation errors otherwise.\n//   - a HTTP 303 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\n//\n// Browser flows with HTTP Header `Accept: application/json` respond with\n//   - HTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\n//   - HTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\n//   - HTTP 401 when the endpoint is called without a valid session cookie.\n//   - HTTP 403 when the page is accessed without a session cookie or the session's AAL is too low.\n//   - HTTP 400 on form validation errors.\n//\n// Depending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\n// Assurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n// credentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n// to sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\n//\n// If this endpoint is called with a `Accept: application/json` HTTP header, the response contains the flow without a redirect. In the\n// case of an error, the `error.id` of the JSON response body can be one of:\n//\n//   - `session_refresh_required`: The identity requested to change something that needs a privileged session. Redirect\n//     the identity to the login init endpoint with query parameters `?refresh=true&return_to=<the-current-browser-url>`,\n//     or initiate a refresh login flow otherwise.\n//   - `security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\n//   - `session_inactive`: No Ory Session was found - sign in a user first.\n//   - `security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\n//     identity logged in instead.\n//   - `security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\n//   - `browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\n//     Most likely used in Social Sign In flows.\n//\n// More information can be found at [Ory Kratos User Settings & Profile Management Documentation](../self-service/flows/user-settings).\n//\n//\tConsumes:\n//\t- application/json\n//\t- application/x-www-form-urlencoded\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSecurity:\n//\t  sessionToken:\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: settingsFlow\n//\t  303: emptyResponse\n//\t  400: settingsFlow\n//\t  401: errorGeneric\n//\t  403: errorGeneric\n//\t  410: errorGeneric\n//\t  422: errorBrowserLocationChangeRequired\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-high\nfunc (h *Handler) updateSettingsFlow(w http.ResponseWriter, r *http.Request) {\n\tvar (\n\t\terr error\n\t\tctx = r.Context()\n\t)\n\n\tctx, span := h.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.flow.settings.Handler.updateSettingsFlow\")\n\tdefer otelx.End(span, &err)\n\n\trid, err := GetFlowID(r)\n\tif err != nil {\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, nil, nil, nil, err)\n\t\treturn\n\t}\n\n\tf, err := h.d.SettingsFlowPersister().GetSettingsFlow(ctx, rid)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, nil, nil, nil, errors.WithStack(herodot.ErrNotFound.WithReasonf(\"The settings request could not be found. Please restart the flow.\")))\n\t\treturn\n\t} else if err != nil {\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, nil, nil, nil, err)\n\t\treturn\n\t}\n\n\tss, err := h.d.SessionManager().FetchFromRequestContext(ctx, r)\n\tif err != nil {\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, nil, nil, err)\n\t\treturn\n\t}\n\n\trequestURL := x.RequestURL(r).String()\n\tif err := h.d.SessionManager().DoesSessionSatisfy(ctx, ss, h.d.Config().SelfServiceSettingsRequiredAAL(ctx), session.WithRequestURL(requestURL)); err != nil {\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, nil, nil, err)\n\t\treturn\n\t}\n\n\tif err := f.Valid(ss); err != nil {\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, ss.Identity, ss, err)\n\t\treturn\n\t}\n\n\tvar s string\n\tvar updateContext *UpdateContext\n\tfor _, strat := range h.d.AllSettingsStrategies() {\n\t\tuc, err := strat.Settings(ctx, w, r, f, ss)\n\t\tif errors.Is(err, flow.ErrStrategyNotResponsible) {\n\t\t\tcontinue\n\t\t} else if errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\treturn\n\t\t} else if err != nil {\n\t\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, strat.NodeGroup(), f, ss.Identity, ss, err)\n\t\t\treturn\n\t\t}\n\n\t\ts = strat.SettingsStrategyID()\n\t\tupdateContext = uc\n\t\tbreak\n\t}\n\n\tif updateContext == nil {\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, ss.Identity, ss, errors.WithStack(schema.NewNoSettingsStrategyResponsible()))\n\t\treturn\n\t}\n\n\ti, err := updateContext.GetIdentityToUpdate()\n\tif err != nil {\n\t\t// An identity to update must always be present.\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, ss.Identity, ss, err)\n\t\treturn\n\t}\n\n\tif err := h.d.SettingsHookExecutor().PostSettingsHook(ctx, w, r, s, updateContext, i); err != nil {\n\t\th.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, node.DefaultGroup, f, ss.Identity, ss, err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/settings/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/snapshotx\"\n\n\t\"github.com/ory/x/assertx\"\n\n\t\"github.com/ory/kratos/session\"\n\n\t\"github.com/gofrs/uuid\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\n\t\"github.com/ory/kratos/corpx\"\n\n\t\"github.com/ory/x/ioutilx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc TestHandler(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t{ID: \"default\", URL: \"file://./stub/identity.schema.json\", SelfserviceSelectable: true},\n\t\t{ID: \"not-default\", URL: \"file://./stub/multi-email.schema.json\"},\n\t})\n\n\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePassword.String(), true)\n\ttesthelpers.StrategyEnable(t, conf, settings.StrategyProfile, true)\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\t_ = testhelpers.NewSettingsUITestServer(t, conf)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\n\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"1ns\")\n\n\tprimaryIdentity := &identity.Identity{ID: x.NewUUID(), Traits: identity.Traits(`{}`)}\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), primaryIdentity))\n\tprimaryUser := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, primaryIdentity)\n\totherUser := testhelpers.NewHTTPClientWithArbitrarySessionCookie(ctx, t, reg)\n\n\tnewExpiredFlow := func() *settings.Flow {\n\t\tf, err := settings.NewFlow(conf, -time.Minute,\n\t\t\t&http.Request{URL: urlx.ParseOrPanic(publicTS.URL + login.RouteInitBrowserFlow)},\n\t\t\tprimaryIdentity, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\treturn f\n\t}\n\n\tassertion := func(t *testing.T, body []byte, isApi bool) {\n\t\tif isApi {\n\t\t\tassert.Equal(t, \"api\", gjson.GetBytes(body, \"type\").String())\n\t\t} else {\n\t\t\tassert.Equal(t, \"browser\", gjson.GetBytes(body, \"type\").String())\n\t\t}\n\t}\n\n\ttype options struct {\n\t\tquery url.Values\n\t}\n\ttype initAuthOptions func(*options)\n\n\tWithInitAuthQuery := func(q url.Values) initAuthOptions {\n\t\treturn func(o *options) {\n\t\t\to.query = q\n\t\t}\n\t}\n\n\tinitAuthenticatedFlow := func(t *testing.T, hc *http.Client, isAPI bool, isSPA bool, opts ...initAuthOptions) (*http.Response, []byte) {\n\t\top := new(options)\n\t\tfor _, o := range opts {\n\t\t\to(op)\n\t\t}\n\t\troute := settings.RouteInitBrowserFlow\n\t\tif isAPI {\n\t\t\troute = settings.RouteInitAPIFlow\n\t\t}\n\t\treqURL, err := url.Parse(publicTS.URL + route)\n\t\trequire.NoError(t, err)\n\t\treqURL.RawQuery = op.query.Encode()\n\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", reqURL.String(), nil)\n\t\tif isSPA || isAPI {\n\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t}\n\t\tres, err := hc.Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tif isAPI {\n\t\t\tassert.Len(t, res.Header.Get(\"Set-Cookie\"), 0)\n\t\t}\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\treturn res, body\n\t}\n\n\tinitFlow := func(t *testing.T, hc *http.Client, isAPI bool, opts ...initAuthOptions) (*http.Response, []byte) {\n\t\treturn initAuthenticatedFlow(t, hc, isAPI, false, opts...)\n\t}\n\n\tinitSPAFlow := func(t *testing.T, hc *http.Client, opts ...initAuthOptions) (*http.Response, []byte) {\n\t\treturn initAuthenticatedFlow(t, hc, false, true, opts...)\n\t}\n\n\taal2Identity := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, &identity.Identity{\n\t\tState:  identity.StateActive,\n\t\tTraits: []byte(`{\"email\":\"foo@bar\"}`),\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\tidentity.CredentialsTypePassword: {Type: identity.CredentialsTypePassword, Config: []byte(`{\"hashed_password\":\"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`), Identifiers: []string{\"foo@bar\"}},\n\t\t\tidentity.CredentialsTypeWebAuthn: {Type: identity.CredentialsTypeWebAuthn, Config: []byte(`{\"credentials\":[{\"is_passwordless\":false}]}`), Identifiers: []string{\"foo@bar\"}},\n\t\t},\n\t})\n\n\tt.Run(\"endpoint=init\", func(t *testing.T) {\n\t\tt.Run(\"description=init a flow as API\", func(t *testing.T) {\n\t\t\tt.Run(\"description=without privileges\", func(t *testing.T) {\n\t\t\t\tres, body := initFlow(t, new(http.Client), true)\n\t\t\t\tassert.Equalf(t, http.StatusUnauthorized, res.StatusCode, \"%s\", body)\n\t\t\t\tassert.Equalf(t, text.ErrNoActiveSession, gjson.GetBytes(body, \"error.id\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"description=success\", func(t *testing.T) {\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\t\t\t\tres, body := initFlow(t, user1, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), settings.RouteInitAPIFlow)\n\t\t\t\tassertion(t, body, true)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.GetBytes(body, \"ui.nodes\").Raw), snapshotx.ExceptPaths(\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\n\t\t\tt.Run(\"description=can not init if identity has aal2 but session has aal1\", func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\tres, body := initFlow(t, aal2Identity, true)\n\t\t\t\tassert.Equalf(t, http.StatusForbidden, res.StatusCode, \"%s\", body)\n\t\t\t\tassertx.EqualAsJSON(t,\n\t\t\t\t\tsession.NewErrAALNotSatisfied(publicTS.URL+\"/self-service/login/browser?aal=aal2\"),\n\t\t\t\t\tjson.RawMessage(body))\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"description=init a flow as browser\", func(t *testing.T) {\n\t\t\tt.Run(\"case=unauthorized users are redirected to login preserving redirect_to param\", func(t *testing.T) {\n\t\t\t\tc := testhelpers.NewClientWithCookies(t)\n\t\t\t\t// prevent the redirect\n\t\t\t\tc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\t\t\t\treturnTo := \"?return_to=validRedirect\"\n\t\t\t\treq, err := http.NewRequest(\"GET\", publicTS.URL+settings.RouteInitBrowserFlow+returnTo, nil)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tres, err := c.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t// here we check that the redirect status is 303\n\t\t\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t\t\tlocation, err := res.Location()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, publicTS.URL+login.RouteInitBrowserFlow+returnTo, location.String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=unauthorized users are redirected to login\", func(t *testing.T) {\n\t\t\t\tc := testhelpers.NewClientWithCookies(t)\n\t\t\t\t// prevent the redirect\n\t\t\t\tc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\t\t\t\treq, err := http.NewRequest(\"GET\", publicTS.URL+settings.RouteInitBrowserFlow, nil)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tres, err := c.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t// here we check that the redirect status is 303\n\t\t\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t\t\tlocation, err := res.Location()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, publicTS.URL+login.RouteInitBrowserFlow, location.String())\n\t\t\t})\n\n\t\t\tt.Run(\"description=success\", func(t *testing.T) {\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionCookie(ctx, t, reg)\n\t\t\t\tres, body := initFlow(t, user1, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), reg.Config().SelfServiceFlowSettingsUI(ctx).String())\n\t\t\t\tassertion(t, body, false)\n\t\t\t})\n\n\t\t\tt.Run(\"description=can not init if identity has aal2 but session has aal1\", func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\tres, body := initFlow(t, aal2Identity, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), reg.Config().SelfServiceFlowLoginUI(ctx).String())\n\t\t\t\tassert.EqualValues(t, \"Please complete the second authentication challenge.\", gjson.GetBytes(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"description=settings return_to is persisted through aal2 flow\", func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\tres, _ := initFlow(t, aal2Identity, false, WithInitAuthQuery(url.Values{\"return_to\": {\"https://www.ory.sh\"}}))\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), reg.Config().SelfServiceFlowLoginUI(ctx).String())\n\n\t\t\t\tlf, err := reg.LoginFlowPersister().GetLoginFlow(ctx, uuid.FromStringOrNil(res.Request.URL.Query().Get(\"flow\")))\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\treqURL, err := url.Parse(lf.RequestURL)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// the return_to of login will be set to the full URL of the settings URL with its query parameters\n\t\t\t\tsettingsURL, err := url.Parse(publicTS.URL + settings.RouteInitBrowserFlow)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tq := settingsURL.Query()\n\t\t\t\tq.Set(\"return_to\", \"https://www.ory.sh\")\n\t\t\t\tsettingsURL.RawQuery = q.Encode()\n\n\t\t\t\tassert.Equal(t, settingsURL.String(), reqURL.Query().Get(\"return_to\"))\n\t\t\t})\n\n\t\t\tt.Run(\"case=redirects with 303\", func(t *testing.T) {\n\t\t\t\tc := testhelpers.NewHTTPClientWithArbitrarySessionCookie(ctx, t, reg)\n\t\t\t\t// prevent the redirect\n\t\t\t\tc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\t\t\t\treq, err := http.NewRequest(\"GET\", publicTS.URL+settings.RouteInitBrowserFlow, nil)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tres, err := c.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t// here we check that the redirect status is 303\n\t\t\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t\t\tlocation, err := res.Location()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Contains(t, location.String(), conf.SelfServiceFlowSettingsUI(ctx).String())\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"description=init a flow as SPA\", func(t *testing.T) {\n\t\t\tt.Run(\"description=without privileges\", func(t *testing.T) {\n\t\t\t\tres, body := initSPAFlow(t, new(http.Client))\n\t\t\t\tassert.Equalf(t, http.StatusUnauthorized, res.StatusCode, \"%s\", body)\n\t\t\t\tassert.Equalf(t, text.ErrNoActiveSession, gjson.GetBytes(body, \"error.id\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"description=success\", func(t *testing.T) {\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\t\t\t\tres, body := initSPAFlow(t, user1)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), settings.RouteInitBrowserFlow)\n\t\t\t\tassertion(t, body, false)\n\t\t\t})\n\n\t\t\tt.Run(\"description=can not init if identity has aal2 but session has aal1\", func(t *testing.T) {\n\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, &identity.Identity{\n\t\t\t\t\tState:  identity.StateActive,\n\t\t\t\t\tTraits: []byte(`{\"email\":\"` + email + `\"}`),\n\t\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypePassword: {Type: identity.CredentialsTypePassword, Config: []byte(`{\"hashed_password\":\"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`), Identifiers: []string{email}},\n\t\t\t\t\t\tidentity.CredentialsTypeWebAuthn: {Type: identity.CredentialsTypeWebAuthn, Config: []byte(`{\"credentials\":[{\"is_passwordless\":false}]}`), Identifiers: []string{email}},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tres, body := initSPAFlow(t, user1)\n\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\n\t\t\t\turl := url.URL{\n\t\t\t\t\tScheme: conf.SelfPublicURL(ctx).Scheme,\n\t\t\t\t\tHost:   conf.SelfPublicURL(ctx).Host,\n\t\t\t\t\tPath:   login.RouteInitBrowserFlow,\n\t\t\t\t}\n\t\t\t\tq := url.Query()\n\t\t\t\tq.Add(\"aal\", \"aal2\")\n\t\t\t\turl.RawQuery = q.Encode()\n\n\t\t\t\tassertx.EqualAsJSON(t, session.NewErrAALNotSatisfied(url.String()), json.RawMessage(body))\n\t\t\t})\n\n\t\t\tt.Run(\"description=settings return_to should persist through mfa flows\", func(t *testing.T) {\n\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, &identity.Identity{\n\t\t\t\t\tState:  identity.StateActive,\n\t\t\t\t\tTraits: []byte(`{\"email\":\"` + email + `\"}`),\n\t\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypePassword: {Type: identity.CredentialsTypePassword, Config: []byte(`{\"hashed_password\":\"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`), Identifiers: []string{email}},\n\t\t\t\t\t\tidentity.CredentialsTypeWebAuthn: {Type: identity.CredentialsTypeWebAuthn, Config: []byte(`{\"credentials\":[{\"is_passwordless\":false}]}`), Identifiers: []string{email}},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tres, body := initSPAFlow(t, user1, WithInitAuthQuery(url.Values{\"return_to\": {\"https://www.ory.sh/\"}}))\n\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\n\t\t\t\treturnToURL := gjson.GetBytes(body, \"redirect_browser_to\").String()\n\t\t\t\trequire.NotEmpty(t, returnToURL)\n\n\t\t\t\treqURL, err := url.Parse(gjson.GetBytes(body, \"redirect_browser_to\").String())\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tsettingsURL, err := url.Parse(publicTS.URL + settings.RouteInitBrowserFlow)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tq := settingsURL.Query()\n\t\t\t\tq.Add(\"return_to\", \"https://www.ory.sh/\")\n\t\t\t\tsettingsURL.RawQuery = q.Encode()\n\n\t\t\t\tassert.Equal(t, settingsURL.String(), reqURL.Query().Get(\"return_to\"))\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"case=multi-schema_endpoint=init\", func(t *testing.T) {\n\t\tt.Run(\"description=init a flow as API\", func(t *testing.T) {\n\t\t\tt.Run(\"description=success\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\t\t\t})\n\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\n\t\t\t\t// set the default schema to something else than the default\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"not-default\")\n\t\t\t\tres, body := initFlow(t, user1, true)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), settings.RouteInitAPIFlow)\n\t\t\t\tassertion(t, body, true)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.GetBytes(body, \"ui.nodes\").Raw), snapshotx.ExceptPaths(\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"description=init a flow as browser\", func(t *testing.T) {\n\t\t\tt.Run(\"description=success\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\t\t\t})\n\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\n\t\t\t\t// set the default schema to something else than the default\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"not-default\")\n\n\t\t\t\tres, body := initFlow(t, user1, false)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), reg.Config().SelfServiceFlowSettingsUI(ctx).String())\n\t\t\t\tassertion(t, body, false)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.GetBytes(body, \"ui.nodes\").Raw), snapshotx.ExceptPaths(\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"description=init a flow as SPA\", func(t *testing.T) {\n\t\t\tt.Run(\"description=success\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\t\t\t})\n\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\n\t\t\t\t// set the default schema to something else than the default\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"not-default\")\n\n\t\t\t\tres, body := initSPAFlow(t, user1)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), settings.RouteInitBrowserFlow)\n\t\t\t\tassertion(t, body, false)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.GetBytes(body, \"ui.nodes\").Raw), snapshotx.ExceptPaths(\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"endpoint=fetch\", func(t *testing.T) {\n\t\tt.Run(\"description=fetching a non-existent flow should return a 404 error\", func(t *testing.T) {\n\t\t\t_, _, err := testhelpers.NewSDKCustomClient(publicTS, otherUser).FrontendAPI.GetSettingsFlow(context.Background()).Id(\"i-do-not-exist\").Execute()\n\t\t\trequire.Error(t, err)\n\n\t\t\trequire.IsTypef(t, new(kratos.GenericOpenAPIError), err, \"%T\", err)\n\t\t\tassert.Equal(t, int64(http.StatusNotFound), gjson.GetBytes(err.(*kratos.GenericOpenAPIError).Body(), \"error.code\").Int())\n\t\t})\n\n\t\tt.Run(\"description=fetching an expired flow returns 410\", func(t *testing.T) {\n\t\t\tpr := newExpiredFlow()\n\t\t\trequire.NoError(t, reg.SettingsFlowPersister().CreateSettingsFlow(context.Background(), pr))\n\n\t\t\t_, _, err := testhelpers.NewSDKCustomClient(publicTS, primaryUser).FrontendAPI.GetSettingsFlow(context.Background()).Id(pr.ID.String()).Execute()\n\t\t\trequire.Error(t, err)\n\n\t\t\trequire.IsTypef(t, new(kratos.GenericOpenAPIError), err, \"%T\", err)\n\t\t\tassert.Equal(t, int64(http.StatusGone), gjson.GetBytes(err.(*kratos.GenericOpenAPIError).Body(), \"error.code\").Int())\n\t\t})\n\n\t\tt.Run(\"case=expired with return_to\", func(t *testing.T) {\n\t\t\treturnTo := \"https://www.ory.sh\"\n\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\n\t\t\tclient := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\t\t\tbody := testhelpers.EasyGetBody(t, client, publicTS.URL+settings.RouteInitBrowserFlow+\"?return_to=\"+returnTo)\n\n\t\t\t// Expire the flow\n\t\t\tf, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"id\").String()))\n\t\t\trequire.NoError(t, err)\n\t\t\tf.ExpiresAt = time.Now().Add(-time.Second)\n\t\t\trequire.NoError(t, reg.SettingsFlowPersister().UpdateSettingsFlow(context.Background(), f))\n\n\t\t\t// Retrieve the flow and verify that return_to is in the response\n\t\t\tgetURL := fmt.Sprintf(\"%s%s?id=%s&return_to=%s\", publicTS.URL, settings.RouteGetFlow, f.ID, returnTo)\n\t\t\tgetBody := testhelpers.EasyGetBody(t, client, getURL)\n\t\t\tassert.Equal(t, gjson.GetBytes(getBody, \"error.details.return_to\").String(), returnTo)\n\n\t\t\t// submit the flow but it is expired\n\t\t\tu := publicTS.URL + settings.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\t\tres, err := client.PostForm(u, url.Values{\"method\": {\"password\"}, \"csrf_token\": {\"csrf\"}, \"password\": {\"password\"}})\n\t\t\trequire.NoError(t, err)\n\t\t\tresBody, err := io.ReadAll(res.Body)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\tf, err = reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, \"id\").String()))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, publicTS.URL+settings.RouteInitBrowserFlow+\"?return_to=\"+returnTo, f.RequestURL)\n\t\t})\n\n\t\tt.Run(\"description=should fail to fetch request if identity changed\", func(t *testing.T) {\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\t\t\t\tuser2 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\n\t\t\t\tres, err := user1.Get(publicTS.URL + settings.RouteInitAPIFlow)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tassert.Len(t, res.Header.Get(\"Set-Cookie\"), 0)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\tid := gjson.GetBytes(body, \"id\")\n\t\t\t\trequire.NotEmpty(t, id)\n\n\t\t\t\tres, err = user2.Get(publicTS.URL + settings.RouteGetFlow + \"?id=\" + id.String())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\trequire.EqualValues(t, res.StatusCode, http.StatusForbidden)\n\t\t\t\tbody = ioutilx.MustReadAll(res.Body)\n\t\t\t\tassert.Containsf(t, gjson.GetBytes(body, \"error.reason\").String(), \"The request was made for another identity and has been blocked for security reasons\", \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tres, err := primaryUser.Get(publicTS.URL + settings.RouteInitBrowserFlow)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\trid := res.Request.URL.Query().Get(\"flow\")\n\t\t\t\trequire.NotEmpty(t, rid)\n\n\t\t\t\t_, _, err = testhelpers.NewSDKCustomClient(publicTS, otherUser).FrontendAPI.GetSettingsFlow(context.Background()).Id(rid).Execute()\n\t\t\t\trequire.Error(t, err)\n\t\t\t\trequire.IsTypef(t, new(kratos.GenericOpenAPIError), err, \"%T\", err)\n\t\t\t\tassert.Equal(t, int64(http.StatusForbidden), gjson.GetBytes(err.(*kratos.GenericOpenAPIError).Body(), \"error.code\").Int(), \"should return a 403 error because the identities from the cookies do not match\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"description=can not fetch if identity has aal2 but session has aal1\", func(t *testing.T) {\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t})\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, \"aal1\")\n\n\t\t\tres, body := initFlow(t, aal2Identity, false)\n\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\tflowID := gjson.GetBytes(body, \"id\").String()\n\t\t\tsettingsURL := publicTS.URL + settings.RouteGetFlow + \"?id=\" + flowID\n\t\t\tres, err := aal2Identity.Get(settingsURL)\n\t\t\trequire.NoError(t, err)\n\t\t\tbody = ioutilx.MustReadAll(res.Body)\n\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\turl := url.URL{\n\t\t\t\tScheme: conf.SelfPublicURL(ctx).Scheme,\n\t\t\t\tHost:   conf.SelfPublicURL(ctx).Host,\n\t\t\t\tPath:   login.RouteInitBrowserFlow,\n\t\t\t}\n\n\t\t\treturnTo := conf.SelfServiceFlowSettingsUI(context.Background())\n\t\t\trq := returnTo.Query()\n\t\t\t// the flow ID should be perserved within the return_to query parameter of the login flow\n\t\t\trq.Set(\"flow\", flowID)\n\t\t\treturnTo.RawQuery = rq.Encode()\n\n\t\t\tq := url.Query()\n\t\t\tq.Set(\"aal\", \"aal2\")\n\t\t\tq.Set(\"return_to\", returnTo.String())\n\t\t\turl.RawQuery = q.Encode()\n\n\t\t\trequire.EqualValues(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, session.NewErrAALNotSatisfied(url.String()), json.RawMessage(body))\n\t\t})\n\t})\n\n\tt.Run(\"endpoint=submit\", func(t *testing.T) {\n\t\tt.Run(\"description=can not submit if identity has aal2 but session has aal1\", func(t *testing.T) {\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\t})\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, \"aal1\")\n\n\t\t\t\tres, body := initFlow(t, aal2Identity, false)\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, false, &f, aal2Identity, `{\"method\":\"not-exists\"}`)\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tassert.Equal(t, \"Please complete the second authentication challenge.\", gjson.Get(actual, \"ui.messages.0.text\").String(), actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\t})\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, \"aal1\")\n\n\t\t\t\tres, body := initFlow(t, aal2Identity, false)\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, true, &f, aal2Identity, `{\"method\":\"not-exists\"}`)\n\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\n\t\t\t\turl := url.URL{\n\t\t\t\t\tScheme: conf.SelfPublicURL(ctx).Scheme,\n\t\t\t\t\tHost:   conf.SelfPublicURL(ctx).Host,\n\t\t\t\t\tPath:   login.RouteInitBrowserFlow,\n\t\t\t\t}\n\t\t\t\tq := url.Query()\n\t\t\t\tq.Set(\"aal\", \"aal2\")\n\t\t\t\tq.Set(\"return_to\", publicTS.URL+\"/self-service/settings?flow=\"+f.GetId())\n\t\t\t\turl.RawQuery = q.Encode()\n\n\t\t\t\tassert.Equal(t, url.String(), gjson.Get(actual, \"redirect_browser_to\").String(), actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\t})\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, \"aal1\")\n\n\t\t\t\tres, body := initFlow(t, aal2Identity, true)\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, &f, aal2Identity, `{\"method\":\"not-exists\"}`)\n\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\n\t\t\t\turl := url.URL{\n\t\t\t\t\tScheme: conf.SelfPublicURL(ctx).Scheme,\n\t\t\t\t\tHost:   conf.SelfPublicURL(ctx).Host,\n\t\t\t\t\tPath:   login.RouteInitBrowserFlow,\n\t\t\t\t}\n\n\t\t\t\tq := url.Query()\n\t\t\t\tq.Set(\"aal\", \"aal2\")\n\t\t\t\tq.Set(\"return_to\", publicTS.URL+\"/self-service/settings?flow=\"+f.GetId())\n\t\t\t\turl.RawQuery = q.Encode()\n\t\t\t\tassert.Equal(t, url.String(), gjson.Get(actual, \"redirect_browser_to\").String(), actual)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"description=fail to submit form as another user\", func(t *testing.T) {\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\t\t\t\tuser2 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\t\t\t\t_, body := initFlow(t, user1, true)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, &f, user2, `{\"method\":\"not-exists\"}`)\n\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\tassert.Equal(t, \"The request was initiated by someone else and has been blocked for security reasons. Please go back and try again.\", gjson.Get(actual, \"error.reason\").String(), actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionCookie(ctx, t, reg)\n\t\t\t\tuser2 := testhelpers.NewHTTPClientWithArbitrarySessionCookie(ctx, t, reg)\n\t\t\t\t_, body := initFlow(t, user1, true)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, true, &f, user2, `{\"method\":\"not-exists\"}`)\n\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\tassert.Equal(t, \"The request was initiated by someone else and has been blocked for security reasons. Please go back and try again.\", gjson.Get(actual, \"error.reason\").String(), actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionCookie(ctx, t, reg)\n\t\t\t\tuser2 := testhelpers.NewHTTPClientWithArbitrarySessionCookie(ctx, t, reg)\n\t\t\t\t_, body := initFlow(t, user1, true)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, false, &f, user2, `{\"method\":\"not-exists\"}`)\n\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\tassert.Equal(t, \"The request was initiated by someone else and has been blocked for security reasons. Please go back and try again.\", gjson.Get(actual, \"error.reason\").String(), actual)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"description=submit - kratos session cookie issued\", func(t *testing.T) {\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\t_, body := initFlow(t, primaryUser, false)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, true, &f, primaryUser, fmt.Sprintf(`{\"method\":\"profile\", \"numby\": 15, \"csrf_token\": \"%s\"}`, nosurfx.FakeCSRFToken))\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\trequire.Len(t, primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL+login.RouteGetFlow)), 1)\n\t\t\t\trequire.Contains(t, fmt.Sprintf(\"%v\", primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL))), \"ory_kratos_session\")\n\t\t\t\tassert.Equal(t, \"Your changes have been saved!\", gjson.Get(actual, \"ui.messages.0.text\").String(), actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t_, body := initFlow(t, primaryUser, false)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, false, &f, primaryUser, `method=profile&traits.numby=15&csrf_token=`+nosurfx.FakeCSRFToken)\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\trequire.Len(t, primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL+login.RouteGetFlow)), 1)\n\t\t\t\trequire.Contains(t, fmt.Sprintf(\"%v\", primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL))), \"ory_kratos_session\")\n\t\t\t\tassert.Equal(t, \"Your changes have been saved!\", gjson.Get(actual, \"ui.messages.0.text\").String(), actual)\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"case=multi-schema_endpoint=submit\", func(t *testing.T) {\n\t\tt.Run(\"description=fail to submit form with invalid data\", func(t *testing.T) {\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tname  string\n\t\t\t\tisAPI bool\n\t\t\t\tisSPA bool\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tname:  \"api\",\n\t\t\t\t\tisAPI: true,\n\t\t\t\t}, {\n\t\t\t\t\tname:  \"spa\",\n\t\t\t\t\tisSPA: true,\n\t\t\t\t}, {\n\t\t\t\t\tname: \"browser\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"type=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\t\t\t\t})\n\n\t\t\t\t\tuser1 := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\n\t\t\t\t\t// set the default schema to something else than the default\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"not-default\")\n\n\t\t\t\t\t_, body := initFlow(t, user1, true)\n\t\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\t\t_, res := testhelpers.SettingsMakeRequest(t, tc.isAPI, tc.isSPA, &f, user1, `{\"should_long_string\":\"tooshort\"}`)\n\t\t\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"description=submit - kratos session cookie issued\", func(t *testing.T) {\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\t_, body := initFlow(t, primaryUser, false)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, true, &f, primaryUser, fmt.Sprintf(`{\"method\":\"profile\", \"numby\": 15, \"csrf_token\": \"%s\"}`, nosurfx.FakeCSRFToken))\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\trequire.Len(t, primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL+login.RouteGetFlow)), 1)\n\t\t\t\trequire.Contains(t, fmt.Sprintf(\"%v\", primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL))), \"ory_kratos_session\")\n\t\t\t\tassert.Equal(t, \"Your changes have been saved!\", gjson.Get(actual, \"ui.messages.0.text\").String(), actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t_, body := initFlow(t, primaryUser, false)\n\t\t\t\tvar f kratos.SettingsFlow\n\t\t\t\trequire.NoError(t, json.Unmarshal(body, &f))\n\n\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, false, &f, primaryUser, `method=profile&traits.numby=15&csrf_token=`+nosurfx.FakeCSRFToken)\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\trequire.Len(t, primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL+login.RouteGetFlow)), 1)\n\t\t\t\trequire.Contains(t, fmt.Sprintf(\"%v\", primaryUser.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL))), \"ory_kratos_session\")\n\t\t\t\tassert.Equal(t, \"Your changes have been saved!\", gjson.Get(actual, \"ui.messages.0.text\").String(), actual)\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"case=relative redirect when self-service settings ui is a relative url\", func(t *testing.T) {\n\t\treg.Config().MustSet(ctx, config.ViperKeySelfServiceSettingsURL, \"/settings-ts\")\n\t\tuser1 := testhelpers.NewNoRedirectHTTPClientWithArbitrarySessionCookie(ctx, t, reg)\n\t\tres, _ := initFlow(t, user1, false)\n\t\tassert.Regexp(\n\t\t\tt,\n\t\t\t\"^/settings-ts.*$\",\n\t\t\tres.Header.Get(\"Location\"),\n\t\t)\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/settings/hook.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/kratos/session\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/kratos/schema\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/sirupsen/logrus\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/x\"\n)\n\ntype (\n\tPreHookExecutor interface {\n\t\tExecuteSettingsPreHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error\n\t}\n\tPreHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error\n\n\tPostHookPrePersistExecutor interface {\n\t\tExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity, s *session.Session) error\n\t}\n\tPostHookPrePersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity, s *session.Session) error\n\n\tPostHookPostPersistExecutor interface {\n\t\tExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *Flow, id *identity.Identity, s *session.Session) error\n\t}\n\tPostHookPostPersistExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, id *identity.Identity, s *session.Session) error\n\n\tHooksProvider interface {\n\t\tPreSettingsHooks(ctx context.Context) ([]PreHookExecutor, error)\n\t\tPostSettingsPrePersistHooks(ctx context.Context, settingsType string) ([]PostHookPrePersistExecutor, error)\n\t\tPostSettingsPostPersistHooks(ctx context.Context, settingsType string) ([]PostHookPostPersistExecutor, error)\n\t}\n\n\texecutorDependencies interface {\n\t\tidentity.ManagementProvider\n\t\tidentity.ValidationProvider\n\t\tsession.ManagementProvider\n\t\tconfig.Provider\n\n\t\tHandlerProvider\n\t\tHooksProvider\n\t\tFlowPersistenceProvider\n\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tlogrusx.Provider\n\t\thttpx.WriterProvider\n\t\totelx.Provider\n\t}\n\tHookExecutor struct {\n\t\td executorDependencies\n\t}\n\tHookExecutorProvider interface {\n\t\tSettingsHookExecutor() *HookExecutor\n\t}\n)\n\nfunc (f PreHookExecutorFunc) ExecuteSettingsPreHook(w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) error {\n\treturn f(w, r, a, s)\n}\n\nfunc (f PostHookPrePersistExecutorFunc) ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity, s *session.Session) error {\n\treturn f(w, r, a, i, s)\n}\n\nfunc (f PostHookPostPersistExecutorFunc) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *Flow, id *identity.Identity, s *session.Session) error {\n\treturn f(w, r, a, id, s)\n}\n\nfunc PostHookPostPersistExecutorNames(e []PostHookPostPersistExecutor) []string {\n\tnames := make([]string, len(e))\n\tfor k, ee := range e {\n\t\tnames[k] = fmt.Sprintf(\"%T\", ee)\n\t}\n\treturn names\n}\n\nfunc PostHookPrePersistExecutorNames(e []PostHookPrePersistExecutor) []string {\n\tnames := make([]string, len(e))\n\tfor k, ee := range e {\n\t\tnames[k] = fmt.Sprintf(\"%T\", ee)\n\t}\n\treturn names\n}\n\nfunc NewHookExecutor(d executorDependencies) *HookExecutor {\n\treturn &HookExecutor{d: d}\n}\n\ntype PostSettingsHookOption func(o *postSettingsHookOptions)\n\ntype postSettingsHookOptions struct {\n\tcb func(ctxUpdate *UpdateContext) error\n}\n\nfunc WithCallback(cb func(ctxUpdate *UpdateContext) error) func(o *postSettingsHookOptions) {\n\treturn func(o *postSettingsHookOptions) {\n\t\to.cb = cb\n\t}\n}\n\nfunc (e *HookExecutor) handleSettingsError(_ context.Context, _ http.ResponseWriter, r *http.Request, settingsType string, f *Flow, i *identity.Identity, flowError error) error {\n\tif f != nil {\n\t\tif i != nil {\n\t\t\tvar group node.UiNodeGroup\n\t\t\tswitch settingsType {\n\t\t\tcase \"password\":\n\t\t\t\tgroup = node.PasswordGroup\n\t\t\tcase \"oidc\":\n\t\t\t\tgroup = node.OpenIDConnectGroup\n\t\t\t}\n\n\t\t\tcont, err := container.NewFromStruct(\"\", group, i.Traits, \"traits\")\n\t\t\tif err != nil {\n\t\t\t\te.d.Logger().WithError(err).Error(\"could not update flow UI\")\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tfor _, n := range cont.Nodes {\n\t\t\t\t// we only set the value and not the whole field because we want to keep types from the initial form generation\n\t\t\t\tf.UI.Nodes.SetValueAttribute(n.ID(), n.Attributes.GetValue())\n\t\t\t}\n\t\t}\n\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(e.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn flowError\n}\n\nfunc (e *HookExecutor) PostSettingsHook(ctx context.Context, w http.ResponseWriter, r *http.Request, settingsType string, ctxUpdate *UpdateContext, i *identity.Identity, opts ...PostSettingsHookOption) (err error) {\n\tctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.flow.settings.HookExecutor.PostSettingsHook\")\n\tdefer otelx.End(span, &err)\n\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tWithField(\"flow_method\", settingsType).\n\t\tDebug(\"Running PostSettingsPrePersistHooks.\")\n\n\t// Verify the redirect URL before we do any other processing.\n\tc := e.d.Config()\n\treturnTo, err := redir.SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(ctx),\n\t\tredir.SecureRedirectUseSourceURL(ctxUpdate.Flow.RequestURL),\n\t\tredir.SecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(ctx)),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(ctx)),\n\t\tredir.SecureRedirectOverrideDefaultReturnTo(\n\t\t\te.d.Config().SelfServiceFlowSettingsReturnTo(ctx, settingsType,\n\t\t\t\tctxUpdate.Flow.AppendTo(e.d.Config().SelfServiceFlowSettingsUI(ctx)))),\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\thookOptions := new(postSettingsHookOptions)\n\tfor _, f := range opts {\n\t\tf(hookOptions)\n\t}\n\n\tpreHooks, err := e.d.PostSettingsPrePersistHooks(ctx, settingsType)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k, executor := range preHooks {\n\t\tlogFields := logrus.Fields{\n\t\t\t\"executor\":          fmt.Sprintf(\"%T\", executor),\n\t\t\t\"executor_position\": k,\n\t\t\t\"executors\":         PostHookPrePersistExecutorNames(preHooks),\n\t\t\t\"identity_id\":       i.ID,\n\t\t\t\"flow_method\":       settingsType,\n\t\t}\n\n\t\tif err := executor.ExecuteSettingsPrePersistHook(w, r, ctxUpdate.Flow, i, ctxUpdate.Session); err != nil {\n\t\t\tif errors.Is(err, ErrHookAbortFlow) {\n\t\t\t\te.d.Logger().WithRequest(r).WithFields(logFields).\n\t\t\t\t\tDebug(\"A ExecuteSettingsPrePersistHook hook aborted early.\")\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tvar group node.UiNodeGroup\n\t\t\tswitch settingsType {\n\t\t\tcase \"password\":\n\t\t\t\tgroup = node.PasswordGroup\n\t\t\tcase \"oidc\":\n\t\t\t\tgroup = node.OpenIDConnectGroup\n\t\t\t}\n\t\t\treturn flow.HandleHookError(w, r, ctxUpdate.Flow, i.Traits, group, err, e.d, e.d)\n\t\t}\n\n\t\te.d.Logger().WithRequest(r).WithFields(logFields).Debug(\"ExecuteSettingsPrePersistHook completed successfully.\")\n\t}\n\n\toptions := []identity.ManagerOption{identity.ManagerExposeValidationErrorsForInternalTypeAssertion}\n\tttl := e.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)\n\tif ctxUpdate.Session.AuthenticatedAt.Add(ttl).After(time.Now()) {\n\t\toptions = append(options, identity.ManagerAllowWriteProtectedTraits)\n\t}\n\n\tif err := e.d.IdentityManager().Update(ctx, i, options...); err != nil {\n\t\tif errors.Is(err, identity.ErrProtectedFieldModified) {\n\t\t\te.d.Logger().WithError(err).Debug(\"Modifying protected field requires re-authentication.\")\n\t\t\treturn errors.WithStack(NewFlowNeedsReAuth())\n\t\t}\n\t\tif errors.Is(err, sqlcon.ErrUniqueViolation) {\n\t\t\treturn schema.NewDuplicateCredentialsError(err)\n\t\t}\n\t\treturn err\n\t}\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tDebug(\"An identity's settings have been updated.\")\n\n\tctxUpdate.UpdateIdentity(i)\n\tctxUpdate.Flow.State = flow.StateSuccess\n\tif hookOptions.cb != nil {\n\t\tif err := hookOptions.cb(ctxUpdate); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tnewFlow, err := e.d.SettingsHandler().NewFlow(ctx, w, r, i, ctxUpdate.Session, ctxUpdate.Flow.Type)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tctxUpdate.Flow.UI = newFlow.UI\n\tctxUpdate.Flow.UI.ResetMessages()\n\tctxUpdate.Flow.UI.AddMessage(node.DefaultGroup, text.NewInfoSelfServiceSettingsUpdateSuccess())\n\tctxUpdate.Flow.InternalContext = newFlow.InternalContext\n\tif err := e.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, ctxUpdate.Flow); err != nil {\n\t\treturn err\n\t}\n\n\tpostHooks, err := e.d.PostSettingsPostPersistHooks(ctx, settingsType)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k, executor := range postHooks {\n\t\tif err := executor.ExecuteSettingsPostPersistHook(w, r, ctxUpdate.Flow, i, ctxUpdate.Session); err != nil {\n\t\t\tif errors.Is(err, ErrHookAbortFlow) {\n\t\t\t\te.d.Logger().\n\t\t\t\t\tWithRequest(r).\n\t\t\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\t\t\tWithField(\"executor_position\", k).\n\t\t\t\t\tWithField(\"executors\", PostHookPostPersistExecutorNames(postHooks)).\n\t\t\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\t\t\tWithField(\"flow_method\", settingsType).\n\t\t\t\t\tDebug(\"A ExecuteSettingsPostPersistHook hook aborted early.\")\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn e.handleSettingsError(ctx, w, r, settingsType, ctxUpdate.Flow, i, err)\n\t\t}\n\n\t\te.d.Logger().WithRequest(r).\n\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\tWithField(\"executor_position\", k).\n\t\t\tWithField(\"executors\", PostHookPostPersistExecutorNames(postHooks)).\n\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\tWithField(\"flow_method\", settingsType).\n\t\t\tDebug(\"ExecuteSettingsPostPersistHook completed successfully.\")\n\t}\n\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tWithField(\"flow_method\", settingsType).\n\t\tDebug(\"Completed all PostSettingsPrePersistHooks and PostSettingsPostPersistHooks.\")\n\n\ttrace.SpanFromContext(ctx).AddEvent(events.NewSettingsSucceeded(\n\t\tctx, ctxUpdate.Flow.ID, i.ID, string(ctxUpdate.Flow.Type), settingsType))\n\n\tif ctxUpdate.Flow.Type == flow.TypeAPI {\n\t\tupdatedFlow, err := e.d.SettingsFlowPersister().GetSettingsFlow(ctx, ctxUpdate.Flow.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// ContinueWith items are transient items, not stored in the database, and need to be carried over here, so\n\t\t// they can be returned to the client.\n\t\tupdatedFlow.ContinueWithItems = ctxUpdate.Flow.ContinueWithItems\n\n\t\te.d.Writer().Write(w, r, updatedFlow)\n\t\treturn nil\n\t}\n\n\tif err := e.d.SessionManager().IssueCookie(ctx, w, r, ctxUpdate.Session); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif x.IsJSONRequest(r) {\n\t\tupdatedFlow, err := e.d.SettingsFlowPersister().GetSettingsFlow(ctx, ctxUpdate.Flow.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// ContinueWith items are transient items, not stored in the database, and need to be carried over here, so\n\t\t// they can be returned to the client.\n\t\tctxUpdate.Flow.AddContinueWith(flow.NewContinueWithRedirectBrowserTo(returnTo.String()))\n\t\tupdatedFlow.ContinueWithItems = ctxUpdate.Flow.ContinueWithItems\n\n\t\te.d.Writer().Write(w, r, updatedFlow)\n\t\treturn nil\n\t}\n\n\tredir.ContentNegotiationRedirection(w, r, i.CopyWithoutCredentials(), e.d.Writer(), returnTo.String())\n\treturn nil\n}\n\nfunc (e *HookExecutor) PreSettingsHook(ctx context.Context, w http.ResponseWriter, r *http.Request, a *Flow, s *session.Session) (err error) {\n\tctx, span := e.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.flow.settings.HookExecutor.PreSettingsHook\")\n\tdefer otelx.End(span, &err)\n\n\thooks, err := e.d.PreSettingsHooks(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, executor := range hooks {\n\t\tif err := executor.ExecuteSettingsPreHook(w, r, a, s); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/flow/settings/hook_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/gobuffalo/httptest\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestSettingsExecutor(t *testing.T) {\n\tctx := context.Background()\n\tfor _, strategy := range []string{\n\t\tidentity.CredentialsTypePassword.String(),\n\t\tsettings.StrategyProfile,\n\t} {\n\t\tt.Run(\"strategy=\"+strategy, func(t *testing.T) {\n\t\t\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, returnToServer.URL)\n\n\t\t\treg.WithHooks(map[string]func(config.SelfServiceHook) interface{}{\n\t\t\t\t\"err\": func(c config.SelfServiceHook) interface{} {\n\t\t\t\t\treturn &hook.Error{Config: c.Config}\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tnewServer := func(t *testing.T, i *identity.Identity, ft flow.Type) *httptest.Server {\n\t\t\t\tt.Helper()\n\t\t\t\trouter := http.NewServeMux()\n\t\t\t\thandleErr := testhelpers.SelfServiceHookSettingsErrorHandler\n\t\t\t\trouter.HandleFunc(\"GET /settings/pre\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t\tif i == nil {\n\t\t\t\t\t\ti = testhelpers.SelfServiceHookCreateFakeIdentity(t, reg)\n\t\t\t\t\t}\n\t\t\t\t\tsess, _ := testhelpers.NewActiveSession(r, reg, i, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\n\t\t\t\t\tf, err := settings.NewFlow(conf, time.Minute, r, sess.Identity, ft)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tif handleErr(t, w, r, reg.SettingsHookExecutor().PreSettingsHook(r.Context(), w, r, f, sess)) {\n\t\t\t\t\t\t_, _ = w.Write([]byte(\"ok\"))\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\trouter.HandleFunc(\"GET /settings/post\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t\tif i == nil {\n\t\t\t\t\t\ti = testhelpers.SelfServiceHookCreateFakeIdentity(t, reg)\n\t\t\t\t\t}\n\t\t\t\t\tsess, _ := testhelpers.NewActiveSession(r, reg, i, time.Now().UTC(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\n\t\t\t\t\ta, err := settings.NewFlow(conf, time.Minute, r, sess.Identity, ft)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\ta.RequestURL = x.RequestURL(r).String()\n\t\t\t\t\trequire.NoError(t, reg.SettingsFlowPersister().CreateSettingsFlow(r.Context(), a))\n\t\t\t\t\t_ = handleErr(t, w, r, reg.SettingsHookExecutor().\n\t\t\t\t\t\tPostSettingsHook(ctx, w, r, strategy, &settings.UpdateContext{Flow: a, Session: sess}, i))\n\t\t\t\t})\n\t\t\t\tts := httptest.NewServer(router)\n\t\t\t\tt.Cleanup(ts.Close)\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)\n\t\t\t\treturn ts\n\t\t\t}\n\n\t\t\tmakeRequestPost := testhelpers.SelfServiceMakeSettingsPostHookRequest\n\t\t\tviperSetPost := func(strategy string, c []config.SelfServiceHook) {\n\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceSettingsAfter, strategy), c)\n\t\t\t}\n\n\t\t\tuiTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))\n\t\t\tt.Cleanup(uiTS.Close)\n\t\t\tuiURL := uiTS.URL + \"/user/settings\"\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsURL, uiURL)\n\n\t\t\tt.Run(\"method=PostSettingsHook\", func(t *testing.T) {\n\t\t\t\tt.Run(\"case=pass without hooks\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiURL)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass without hooks if ajax client\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tts := newServer(t, nil, flow.TypeBrowser)\n\t\t\t\t\tres, body := makeRequestPost(t, ts, true, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), ts.URL)\n\t\t\t\t\tassert.EqualValues(t, gjson.Get(body, \"continue_with.0.action\").String(), \"redirect_browser_to\")\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass if hooks pass\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tviperSetPost(strategy, []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiURL)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=fail if hooks fail\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tviperSetPost(strategy, []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecuteSettingsPrePersistHook\": \"abort\"}`)}})\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"\", body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=use return_to value\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToServer.URL})\n\t\t\t\t\ttesthelpers.SelfServiceHookSettingsSetDefaultRedirectTo(t, conf, \"https://www.ory.sh\")\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{\"return_to\": {returnToServer.URL + \"/kratos\"}})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL+\"/kratos\", res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=use nested config value\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\ttesthelpers.SelfServiceHookSettingsSetDefaultRedirectTo(t, conf, returnToServer.URL+\"/kratos\")\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL+\"/kratos\", res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=use nested config value\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\ttesthelpers.SelfServiceHookSettingsSetDefaultRedirectTo(t, conf, \"https://www.ory.sh/not-kratos\")\n\t\t\t\t\ttesthelpers.SelfServiceHookSettingsSetDefaultRedirectToStrategy(t, conf, strategy, returnToServer.URL+\"/kratos\")\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.EqualValues(t, returnToServer.URL+\"/kratos\", res.Request.URL.String())\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass if hooks pass\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tviperSetPost(strategy, []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), false, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiURL)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=send a json response for API clients\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\t\t\tviperSetPost(strategy, nil)\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeAPI), true, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"identity.id\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=pass without hooks for browser flow with application/json\", func(t *testing.T) {\n\t\t\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\t\t\t\tres, body := makeRequestPost(t, newServer(t, nil, flow.TypeBrowser), true, url.Values{})\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"identity.id\"))\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tfor _, kind := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {\n\t\t\t\tt.Run(\"type=\"+string(kind)+\"/method=PreSettingsHook\", testhelpers.TestSelfServicePreHook(\n\t\t\t\t\tconfig.ViperKeySelfServiceSettingsBeforeHooks,\n\t\t\t\t\ttesthelpers.SelfServiceMakeSettingsPreHookRequest,\n\t\t\t\t\tfunc(t *testing.T) *httptest.Server {\n\t\t\t\t\t\treturn newServer(t, nil, kind)\n\t\t\t\t\t},\n\t\t\t\t\tconf,\n\t\t\t\t))\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/settings/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype (\n\tFlowPersister interface {\n\t\tCreateSettingsFlow(context.Context, *Flow) error\n\t\tGetSettingsFlow(ctx context.Context, id uuid.UUID) (*Flow, error)\n\t\tUpdateSettingsFlow(context.Context, *Flow) error\n\t\tDeleteExpiredSettingsFlows(context.Context, time.Time, int) error\n\t}\n\tFlowPersistenceProvider interface {\n\t\tSettingsFlowPersister() FlowPersister\n\t}\n)\n"
  },
  {
    "path": "selfservice/flow/settings/sort.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc sortNodes(ctx context.Context, n node.Nodes, schemaRef string) error {\n\treturn n.SortBySchema(ctx,\n\t\tnode.SortBySchema(schemaRef),\n\t\tnode.SortByGroups([]node.UiNodeGroup{\n\t\t\tnode.DefaultGroup,\n\t\t\tnode.ProfileGroup,\n\t\t\tnode.PasswordGroup,\n\t\t\tnode.OpenIDConnectGroup,\n\t\t\tnode.LookupGroup,\n\t\t\tnode.WebAuthnGroup,\n\t\t\tnode.PasskeyGroup,\n\t\t\tnode.TOTPGroup,\n\t\t}),\n\t\tnode.SortUseOrderAppend([]string{\n\t\t\t// Lookup\n\t\t\tnode.LookupReveal,\n\t\t\tnode.LookupRegenerate,\n\t\t\tnode.LookupCodes,\n\t\t\tnode.LookupConfirm,\n\n\t\t\t// WebAuthn\n\t\t\tnode.WebAuthnRemove,\n\t\t\tnode.WebAuthnRegisterDisplayName,\n\t\t\tnode.WebAuthnRegister,\n\n\t\t\t// Passkey\n\t\t\tnode.PasskeyRemove,\n\t\t\tnode.PasskeyRegister,\n\n\t\t\t// TOTP\n\t\t\tnode.TOTPQR,\n\t\t\tnode.TOTPSecretKey,\n\t\t\tnode.TOTPUnlink,\n\t\t\tnode.TOTPCode,\n\t\t}),\n\t)\n}\n"
  },
  {
    "path": "selfservice/flow/settings/state.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport \"github.com/ory/kratos/selfservice/flow\"\n\n// State represents the state of this flow. It knows two states:\n//\n//   - show_form: No user data has been collected, or it is invalid, and thus the form should be shown.\n//   - success: Indicates that the settings flow has been updated successfully with the provided data.\n//     Done will stay true when repeatedly checking. If set to true, done will revert back to false only\n//     when a flow with invalid (e.g. \"please use a valid phone number\") data was sent.\n//\n// swagger:model settingsFlowState\ntype State = flow.State\n"
  },
  {
    "path": "selfservice/flow/settings/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"reflect\"\n\n\t\"github.com/ory/kratos/session\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\nconst (\n\tStrategyProfile = \"profile\"\n)\n\nvar pkgName = reflect.TypeOf(Strategies{}).PkgPath()\n\ntype Strategy interface {\n\tSettingsStrategyID() string\n\tNodeGroup() node.UiNodeGroup\n\tPopulateSettingsMethod(context.Context, *http.Request, *identity.Identity, *Flow) error\n\tSettings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *Flow, s *session.Session) (*UpdateContext, error)\n}\n\ntype Strategies []Strategy\n\nfunc (s Strategies) Strategy(id string) (Strategy, error) {\n\tids := make([]string, len(s))\n\tfor k, ss := range s {\n\t\tids[k] = ss.SettingsStrategyID()\n\t\tif ss.SettingsStrategyID() == id {\n\t\t\treturn ss, nil\n\t\t}\n\t}\n\n\treturn nil, errors.Errorf(`unable to find strategy for %s have %v`, id, ids)\n}\n\nfunc (s Strategies) MustStrategy(id string) Strategy {\n\tstrategy, err := s.Strategy(id)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn strategy\n}\n\ntype StrategyProvider interface {\n\tSettingsStrategies(ctx context.Context) Strategies\n\tAllSettingsStrategies() Strategies\n}\n"
  },
  {
    "path": "selfservice/flow/settings/strategy_helper.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport (\n\t\"net/http\"\n\t\"runtime/debug\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n)\n\nvar (\n\tErrContinuePreviousAction = errors.New(\"found prior action\")\n)\n\ntype UpdatePayload interface {\n\tGetFlowID() uuid.UUID\n\tSetFlowID(id uuid.UUID)\n}\n\ntype UpdateContext struct {\n\tSession  *session.Session\n\tFlow     *Flow\n\ttoUpdate *identity.Identity\n}\n\nfunc (c *UpdateContext) UpdateIdentity(i *identity.Identity) {\n\tc.toUpdate = i\n}\n\nfunc (c *UpdateContext) GetIdentityToUpdate() (*identity.Identity, error) {\n\tif c.toUpdate == nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Could not find a identity to update.\"))\n\t}\n\treturn c.toUpdate, nil\n}\n\nfunc (c UpdateContext) GetSessionIdentity() *identity.Identity {\n\tif c.Session == nil {\n\t\treturn nil\n\t}\n\treturn c.Session.Identity\n}\n\nfunc PrepareUpdate(d interface {\n\tlogrusx.Provider\n\tcontinuity.ManagementProvider\n}, w http.ResponseWriter, r *http.Request, f *Flow, ss *session.Session, name string, payload UpdatePayload) (*UpdateContext, error) {\n\tpayload.SetFlowID(f.ID)\n\tc := &UpdateContext{Session: ss, Flow: f}\n\tif f.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\treturn c, nil\n\t}\n\n\tif _, err := d.ContinuityManager().Continue(r.Context(), w, r, name, ContinuityOptions(payload, ss.Identity)...); err == nil {\n\t\tif payload.GetFlowID() == f.ID {\n\t\t\treturn c, ErrContinuePreviousAction\n\t\t}\n\t\td.Logger().\n\t\t\tWithField(\"package\", pkgName).\n\t\t\tWithField(\"stack_trace\", string(debug.Stack())).\n\t\t\tWithField(\"expected_request_id\", payload.GetFlowID()).\n\t\t\tWithField(\"actual_request_id\", f.ID).\n\t\t\tDebug(\"Flow ID from continuity manager does not match Flow ID from request.\")\n\t\treturn c, nil\n\t} else if !errors.Is(err, &continuity.ErrNotResumable) {\n\t\treturn new(UpdateContext), err\n\t}\n\n\treturn c, nil\n}\n\nfunc ContinuityOptions(p interface{}, i *identity.Identity) []continuity.ManagerOption {\n\treturn []continuity.ManagerOption{\n\t\tcontinuity.WithPayload(p),\n\t\tcontinuity.WithIdentity(i),\n\t\tcontinuity.WithLifespan(time.Minute * 15),\n\t}\n}\n\nfunc GetFlowID(r *http.Request) (uuid.UUID, error) {\n\trid := x.ParseUUID(r.URL.Query().Get(\"flow\"))\n\tif rid == uuid.Nil {\n\t\treturn rid, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"The flow query parameter is missing or malformed.\"))\n\t}\n\treturn rid, nil\n}\n\nfunc OnUnauthenticated(reg interface {\n\tconfig.Provider\n\thttpx.WriterProvider\n}) func(http.ResponseWriter, *http.Request) {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\thandler := session.RedirectOnUnauthenticated(reg.Config().SelfServiceFlowLoginUI(r.Context()).String())\n\t\tif x.IsJSONRequest(r) {\n\t\t\thandler = session.RespondWithJSONErrorOnAuthenticated(reg.Writer(), herodot.ErrUnauthorized.WithReasonf(\"A valid Ory Session Cookie or Ory Session Token is missing.\"))\n\t\t}\n\n\t\thandler(w, r)\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/settings/strategy_helper_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestGetIdentityToUpdate(t *testing.T) {\n\tc := new(UpdateContext)\n\t_, err := c.GetIdentityToUpdate()\n\trequire.Error(t, err)\n\n\texpected := &identity.Identity{ID: x.NewUUID()}\n\tc.UpdateIdentity(expected)\n\n\tactual, err := c.GetIdentityToUpdate()\n\trequire.NoError(t, err)\n\trequire.Equal(t, expected, actual)\n}\n"
  },
  {
    "path": "selfservice/flow/settings/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\":{\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"minLength\": 10\n        },\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"booly\": {\n          \"type\": \"boolean\"\n        },\n        \"should_big_number\": {\n          \"type\": \"number\",\n          \"minimum\": 1200\n        },\n        \"should_long_string\": {\n          \"type\": \"string\",\n          \"minLength\": 25\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/settings/stub/multi-email.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"emails\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"ory.sh/kratos\": {\n              \"credentials\": {\n                \"password\": {\n                  \"identifier\": true\n                }\n              },\n              \"verification\": {\n                \"via\": \"email\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/settings/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/x/assertx\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc clearids(r *settings.Flow) {\n\tr.ID = uuid.Nil\n\tr.Identity.ID = uuid.Nil\n\tr.IdentityID = uuid.Nil\n}\n\nfunc TestFlowPersister(ctx context.Context, p persistence.Persister) func(t *testing.T) {\n\treturn func(t *testing.T) {\n\t\t_, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tt.Run(\"case=should error when the settings request does not exist\", func(t *testing.T) {\n\t\t\t_, err := p.GetSettingsFlow(ctx, x.NewUUID())\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tnewFlow := func(t *testing.T) *settings.Flow {\n\t\t\tvar r settings.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\tclearids(&r)\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, r.Identity))\n\t\t\trequire.NotZero(t, r.Identity.ID)\n\t\t\tr.IdentityID = r.Identity.ID\n\t\t\tr.State = flow.StateShowForm\n\t\t\treturn &r\n\t\t}\n\n\t\tt.Run(\"case=should create a new settings request\", func(t *testing.T) {\n\t\t\tr := newFlow(t)\n\t\t\terr := p.CreateSettingsFlow(ctx, r)\n\t\t\trequire.NoError(t, err, \"%#v\", err)\n\n\t\t\tt.Run(\"can not fetch on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetSettingsFlow(ctx, r.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should create and update and properly deal with internal context\", func(t *testing.T) {\n\t\t\tfor k, tc := range []struct {\n\t\t\t\tin     []byte\n\t\t\t\texpect string\n\t\t\t}{\n\t\t\t\t{in: []byte(\"[]\"), expect: \"{}\"},\n\t\t\t\t{expect: \"{}\"},\n\t\t\t\t{in: []byte(\"null\"), expect: \"{}\"},\n\t\t\t\t{in: []byte(`{\"foo\":\"bar\"}`), expect: `{\"foo\":\"bar\"}`},\n\t\t\t} {\n\t\t\t\tt.Run(fmt.Sprintf(\"run=%d\", k), func(t *testing.T) {\n\t\t\t\t\tr := newFlow(t)\n\t\t\t\t\tr.InternalContext = tc.in\n\t\t\t\t\trequire.NoError(t, p.CreateSettingsFlow(ctx, r))\n\t\t\t\t\tassert.Equal(t, tc.expect, string(r.InternalContext))\n\n\t\t\t\t\tr.InternalContext = tc.in\n\t\t\t\t\trequire.NoError(t, p.UpdateSettingsFlow(ctx, r))\n\t\t\t\t\tassert.Equal(t, tc.expect, string(r.InternalContext))\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should ensure that internal context is an object\", func(t *testing.T) {\n\t\t\tr := newFlow(t)\n\t\t\tr.InternalContext = []byte(\"null\")\n\t\t\trequire.NoError(t, p.CreateSettingsFlow(ctx, r))\n\t\t\tassert.Equal(t, \"{}\", string(r.InternalContext))\n\n\t\t\tr.InternalContext = nil\n\t\t\trequire.NoError(t, p.UpdateSettingsFlow(ctx, r))\n\t\t\tassert.Equal(t, \"{}\", string(r.InternalContext))\n\t\t})\n\n\t\tt.Run(\"case=should create with set ids\", func(t *testing.T) {\n\t\t\tvar r settings.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\tr.State = flow.StateShowForm\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, r.Identity))\n\t\t\trequire.NoError(t, p.CreateSettingsFlow(ctx, &r))\n\n\t\t\trequire.NotEmpty(t, r.Identity)\n\t\t\tt.Run(\"can not fetch on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetSettingsFlow(ctx, r.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should create and fetch a settings request\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\terr := p.CreateSettingsFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactual, err := p.GetSettingsFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tfactual, _ := json.Marshal(actual.UI)\n\t\t\tfexpected, _ := json.Marshal(expected.UI)\n\n\t\t\trequire.NotEmpty(t, actual.UI.Action)\n\t\t\tassert.EqualValues(t, expected.ID, actual.ID)\n\t\t\tassert.JSONEq(t, string(fexpected), string(factual))\n\t\t\tx.AssertEqualTime(t, expected.IssuedAt, actual.IssuedAt)\n\t\t\tx.AssertEqualTime(t, expected.ExpiresAt, actual.ExpiresAt)\n\t\t\tassert.EqualValues(t, expected.RequestURL, actual.RequestURL)\n\t\t\tassert.EqualValues(t, expected.Identity.ID, actual.Identity.ID)\n\t\t\tassert.EqualValues(t, expected.Identity.Traits, actual.Identity.Traits)\n\t\t\tassert.EqualValues(t, expected.Identity.SchemaID, actual.Identity.SchemaID)\n\t\t\tassert.Empty(t, actual.Identity.Credentials)\n\t\t})\n\n\t\tt.Run(\"case=should fail to create if identity does not exist\", func(t *testing.T) {\n\t\t\tvar expected settings.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&expected))\n\t\t\tclearids(&expected)\n\t\t\texpected.Identity = nil\n\t\t\texpected.IdentityID = uuid.Nil\n\t\t\texpected.State = flow.StateShowForm\n\t\t\terr := p.CreateSettingsFlow(ctx, &expected)\n\t\t\trequire.Errorf(t, err, \"%+v\", expected)\n\t\t})\n\n\t\tt.Run(\"case=should create and update a settings request\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\texpected.UI.Nodes = node.Nodes{}\n\t\t\texpected.UI.Nodes.Append(node.NewInputField(\"zab\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\ta.Pattern = \"baz\"\n\t\t\t})))\n\n\t\t\texpected.UI.Nodes.Append(node.NewInputField(\"foo\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\ta.Pattern = \"baz\"\n\t\t\t})))\n\n\t\t\terr := p.CreateSettingsFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\texpected.UI.Action = \"/new-action\"\n\t\t\texpected.UI.Nodes.Append(\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"zab\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"zab\"\n\t\t\t\t})))\n\n\t\t\texpected.RequestURL = \"/new-request-url\"\n\t\t\trequire.NoError(t, p.UpdateSettingsFlow(ctx, expected))\n\n\t\t\tactual, err := p.GetSettingsFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, \"/new-action\", actual.UI.Action)\n\t\t\tassert.Equal(t, \"/new-request-url\", actual.RequestURL)\n\t\t\tassertx.EqualAsJSON(t, node.Nodes{\n\t\t\t\t// v0.5: {Name: \"zab\", Type: \"zab\", Pattern: \"zab\"},\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"baz\"\n\t\t\t\t})),\n\t\t\t\tnode.NewInputField(\"foo\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"baz\"\n\t\t\t\t})),\n\t\t\t\t// v0.5: {Name: \"zab\", Type: \"bar\", Pattern: \"baz\"},\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"zab\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"zab\"\n\t\t\t\t})),\n\t\t\t}, actual.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=network\", func(t *testing.T) {\n\t\t\tid := x.NewUUID()\n\t\t\tiid := x.NewUUID()\n\t\t\tnid, p := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, &identity.Identity{ID: iid}))\n\n\t\t\tt.Run(\"sets id on creation\", func(t *testing.T) {\n\t\t\t\texpected := &settings.Flow{ID: id, IdentityID: iid, State: flow.StateShowForm, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour)}\n\t\t\t\trequire.NoError(t, p.CreateSettingsFlow(ctx, expected))\n\t\t\t\tassert.EqualValues(t, id, expected.ID)\n\t\t\t\tassert.EqualValues(t, nid, expected.NID)\n\n\t\t\t\tactual, err := p.GetSettingsFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, id, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\n\t\t\t\t_, err = p.GetSettingsFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t})\n\n\t\t\tt.Run(\"can not update id\", func(t *testing.T) {\n\t\t\t\texpected, err := p.GetSettingsFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\t\texpected.RequestURL = \"updated\"\n\t\t\t\trequire.ErrorIs(t, other.UpdateSettingsFlow(ctx, expected), sqlcon.ErrNoRows)\n\n\t\t\t\tactual, err := p.GetSettingsFlow(ctx, id)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, id, actual.ID)\n\t\t\t\tassert.EqualValues(t, nid, actual.NID)\n\t\t\t\trequire.NotEqual(t, \"updated\", actual.RequestURL)\n\t\t\t})\n\n\t\t\tt.Run(\"can not get on another network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetSettingsFlow(ctx, id)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\n\t\t\tt.Run(\"network isolation\", func(t *testing.T) {\n\t\t\t\tnid2, _ := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\tnid1, p := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\t\tiid1, iid2 := x.NewUUID(), x.NewUUID()\n\t\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at) VALUES (?, ?, 'default', '{}', ?, ?)\", iid1, nid1, time.Now(), time.Now()).Exec())\n\t\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at) VALUES (?, ?, 'default', '{}', ?, ?)\", iid2, nid2, time.Now(), time.Now()).Exec())\n\n\t\t\t\tsid1, sid2 := x.NewUUID(), x.NewUUID()\n\t\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO selfservice_settings_flows (id, nid, identity_id, ui, created_at, updated_at, expires_at, request_url, internal_context) VALUES (?, ?, ?, '{}', ?, ?, ?, '', '{}')\", sid1, nid1, iid1, time.Now(), time.Now(), time.Now().Add(time.Hour)).Exec())\n\t\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO selfservice_settings_flows (id, nid, identity_id, ui, created_at, updated_at, expires_at, request_url, internal_context) VALUES (?, ?, ?, '{}', ?, ?, ?, '', '{}')\", sid2, nid2, iid2, time.Now(), time.Now(), time.Now().Add(time.Hour)).Exec())\n\n\t\t\t\t_, err := p.GetSettingsFlow(ctx, sid1)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\t_, err = p.GetSettingsFlow(ctx, sid2)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/settings/testsetup_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage settings_test\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"testing\"\n)\n\nvar returnToServer *httptest.Server\n\nfunc TestMain(m *testing.M) {\n\treturnToServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = w.Write([]byte(\"OK\"))\n\t}))\n\tos.Exit(m.Run())\n}\n"
  },
  {
    "path": "selfservice/flow/state.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Flow State\n//\n// The state represents the state of the verification flow.\n//\n// - choose_method: ask the user to choose a method (e.g. recover account via email)\n// - sent_email: the email has been sent to the user\n// - passed_challenge: the request was successful and the recovery challenge was passed.\n// - show_form: a form is shown to the user to perform the flow\n// - success: the flow has been completed successfully\n//\n// swagger:enum selfServiceFlowState\ntype State string\n\n// #nosec G101 -- only a key constant\n// Define the various states for all flows.\n// Recovery flows for V2 have different states (see below).\nconst (\n\tStateChooseMethod State = \"choose_method\"\n\t// Note: this state should actually be called `StateMessageSent`,\n\t// where a 'Message' is a code or link sent to an address (e.g. `email`, `sms`, etc).\n\tStateEmailSent       State = \"sent_email\"\n\tStatePassedChallenge State = \"passed_challenge\"\n\tStateShowForm        State = \"show_form\"\n\tStateSuccess         State = \"success\"\n\n\t// Recovery V2.\n\t// The initial state is different from `StateChooseMethod` to distinguish recovery v1 vs v2.\n\t// This avoids the issue of the feature flag being toggled while some recovery flows are on-going.\n\t// This would lead to an inconsistent state machine/logic for these flows.\n\tStateRecoveryAwaitingAddress        State = \"recovery_awaiting_address\"\n\tStateRecoveryAwaitingAddressChoice  State = \"recovery_awaiting_address_choice\"\n\tStateRecoveryAwaitingAddressConfirm State = \"recovery_confirming_address\"\n\tStateRecoveryAwaitingCode           State = \"recovery_awaiting_code\"\n\t// The final success state is the same as in Recovery V1 (`passed_challenge`).\n\t// Since this is the terminal state, it is not affected by toggling the feature flag.\n\n\t// State machine diagrams:\n\t// - ./state_recovery_v1.mermaid\n\t// - ./state_recovery_v2.mermaid\n)\n\nvar states = []State{\n\tStateChooseMethod,\n\tStateEmailSent,\n\tStatePassedChallenge,\n}\n\nfunc indexOf(current State) int {\n\tfor k, s := range states {\n\t\tif s == current {\n\t\t\treturn k\n\t\t}\n\t}\n\treturn 0\n}\n\nfunc HasReachedState(expected, actual State) bool {\n\treturn indexOf(actual) >= indexOf(expected)\n}\n\nfunc IsStateRecoveryV2(state State) bool {\n\treturn strings.HasPrefix(state.String(), \"recovery_\")\n}\n\nfunc NextState(current State) State {\n\tif current == StatePassedChallenge {\n\t\treturn StatePassedChallenge\n\t}\n\n\treturn states[indexOf(current)+1]\n}\n\n// For some reason using sqlxx.NullString as the State type does not work here.\n// Reimplementing the Scanner interface on type State does work and allows\n// the state to be NULL in the database.\n\n// MarshalJSON returns m as the JSON encoding of m.\nfunc (ns State) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(string(ns))\n}\n\n// UnmarshalJSON sets *m to a copy of data.\nfunc (ns *State) UnmarshalJSON(data []byte) error {\n\tif ns == nil {\n\t\treturn errors.New(\"json.RawMessage: UnmarshalJSON on nil pointer\")\n\t}\n\tif len(data) == 0 {\n\t\treturn nil\n\t}\n\treturn errors.WithStack(json.Unmarshal(data, (*string)(ns)))\n}\n\n// Scan implements the Scanner interface.\nfunc (ns *State) Scan(value interface{}) error {\n\tvar v sql.NullString\n\tif err := (&v).Scan(value); err != nil {\n\t\treturn err\n\t}\n\t*ns = State(v.String)\n\treturn nil\n}\n\n// Value implements the driver Valuer interface.\nfunc (ns State) Value() (driver.Value, error) {\n\tif len(ns) == 0 {\n\t\treturn sql.NullString{}.Value()\n\t}\n\treturn sql.NullString{Valid: true, String: string(ns)}.Value()\n}\n\n// String implements the Stringer interface.\nfunc (ns State) String() string {\n\treturn string(ns)\n}\n"
  },
  {
    "path": "selfservice/flow/state_recovery_v1.mermaid",
    "content": "stateDiagram-v2\n  [*] --> choose_method\n  choose_method --> sent_email: provided an existing email address\n  sent_email --> sent_email: clicked 'resend code'\n  choose_method --> sent_email: provided a non existing email address - pretend we sent a code\n  sent_email --> passed_challenge: provided valid code\n  passed_challenge --> [*]\n\n  note right of sent_email\n    If the email exists, a recovery code is sent to it.\n    Otherwise, an email mentioning that this is an unknown address may be sent depending on the configuration.\n  end note\n"
  },
  {
    "path": "selfservice/flow/state_recovery_v2.mermaid",
    "content": "stateDiagram-v2\n  [*] --> recovery_awaiting_address\n\n  recovery_awaiting_address --> recovery_awaiting_address_choice: provided any address which exists\n  recovery_awaiting_address --> recovery_awaiting_code: provided any address which does not exist - pretend we sent a code\n  recovery_awaiting_address --> recovery_awaiting_code: provided any address & auto-picked the only existing address\n  recovery_awaiting_address_choice --> recovery_confirming_address: chose a masked address\n  recovery_confirming_address --> recovery_awaiting_address_choice : choose different address\nrecovery_awaiting_address_choice --> recovery_awaiting_code: chose a masked address & it is the one provided initially (do not ask again for the full address)\n  recovery_confirming_address --> recovery_awaiting_code: provided the full address corresponding to the masked address\n  recovery_awaiting_code --> recovery_awaiting_code: clicked 'resend code'\n  recovery_awaiting_code --> passed_challenge: provided valid code\n  recovery_awaiting_code --> recovery_awaiting_address_choice : choose different address\n\n  passed_challenge --> [*]\n\n\n  note right of recovery_awaiting_code\n    If the address exists, a recovery code is sent to it.\n    Otherwise, an email mentioning that this is an unknown address may be sent depending on the configuration\n  end note\n\n"
  },
  {
    "path": "selfservice/flow/state_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestState(t *testing.T) {\n\tassert.EqualValues(t, StateEmailSent, NextState(StateChooseMethod))\n\tassert.EqualValues(t, StatePassedChallenge, NextState(StateEmailSent))\n\tassert.EqualValues(t, StatePassedChallenge, NextState(StatePassedChallenge))\n\n\tassert.True(t, HasReachedState(StatePassedChallenge, StatePassedChallenge))\n\tassert.False(t, HasReachedState(StatePassedChallenge, StateEmailSent))\n\tassert.False(t, HasReachedState(StateEmailSent, StateChooseMethod))\n}\n"
  },
  {
    "path": "selfservice/flow/type.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flow\n\n// Type is the flow type.\n//\n// The flow type can either be `api` or `browser`.\n//\n// swagger:model selfServiceFlowType\ntype Type string\n\nconst (\n\tTypeAPI     Type = \"api\"\n\tTypeBrowser Type = \"browser\"\n)\n\nfunc (t Type) IsBrowser() bool {\n\treturn t == TypeBrowser\n}\n\nfunc (t Type) IsAPI() bool {\n\treturn t == TypeAPI\n}\n\nfunc (t Type) Valid() bool {\n\tswitch t {\n\tcase TypeAPI, TypeBrowser:\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "selfservice/flow/verification/.snapshots/TestHandleError-flow=browser-case=fails_to_retry_flow_if_recovery_strategy_id_is_not_valid.json",
    "content": "{\n  \"code\": 400,\n  \"message\": \"The request was malformed or contained invalid parameters\",\n  \"reason\": \"The active verification strategy not-valid is not enabled. Please enable it in the configuration.\",\n  \"status\": \"Bad Request\"\n}\n"
  },
  {
    "path": "selfservice/flow/verification/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n)\n\nvar ErrHookAbortFlow = errors.New(\"aborted verification hook execution\")\n\ntype (\n\terrorHandlerDependencies interface {\n\t\terrorx.ManagementProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\t\tnosurfx.CSRFProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tconfig.Provider\n\t\tFlowPersistenceProvider\n\t\tStrategyProvider\n\t}\n\n\tErrorHandlerProvider interface {\n\t\tVerificationFlowErrorHandler() *ErrorHandler\n\t}\n\n\tErrorHandler struct {\n\t\td errorHandlerDependencies\n\t}\n)\n\nfunc NewErrorHandler(d errorHandlerDependencies) *ErrorHandler {\n\treturn &ErrorHandler{d: d}\n}\n\nfunc (s *ErrorHandler) WriteFlowError(\n\tw http.ResponseWriter,\n\tr *http.Request,\n\tf *Flow,\n\tgroup node.UiNodeGroup,\n\terr error,\n) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.flow.verification.ErrorHandler.WriteFlowError\",\n\t\ttrace.WithAttributes(\n\t\t\tattribute.String(\"error\", err.Error()),\n\t\t))\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\tlogger := s.d.Logger().\n\t\tWithError(err).\n\t\tWithRequest(r).\n\t\tWithField(\"verification_flow\", f.ToLoggerField())\n\n\tlogger.\n\t\tInfo(\"Encountered self-service verification error.\")\n\n\tif f == nil {\n\t\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), uuid.Nil, \"\", \"\", err))\n\t\ts.forward(w, r, nil, err)\n\t\treturn\n\t}\n\tspan.SetAttributes(attribute.String(\"flow_id\", f.ID.String()))\n\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), f.ID, string(f.Type), f.Active.String(), err))\n\n\tif e := new(flow.ExpiredError); errors.As(err, &e) {\n\t\tstrategies, _, err := s.d.VerificationStrategies(r.Context()).ActiveStrategies(f.Active.String())\n\t\tif err != nil {\n\t\t\tstrategies, _, err = s.d.GetActiveVerificationStrategies(r.Context())\n\t\t\t// Can't retry the verification if no primary strategy has been set\n\t\t\tif err != nil {\n\t\t\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t// create new flow because the old one is not valid\n\t\ta, err := FromOldFlow(s.d.Config(), s.d.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()),\n\t\t\ts.d.CSRFHandler().RegenerateToken(w, r), r, strategies, f)\n\t\tif err != nil {\n\t\t\t// failed to create a new session and redirect to it, handle that error as a new one\n\t\t\ts.WriteFlowError(w, r, f, group, err)\n\t\t\treturn\n\t\t}\n\n\t\ta.UI.Messages.Add(text.NewErrorValidationVerificationFlowExpired(e.ExpiredAt))\n\t\tif err := s.d.VerificationFlowPersister().CreateVerificationFlow(r.Context(), a); err != nil {\n\t\t\ts.forward(w, r, a, err)\n\t\t\treturn\n\t\t}\n\n\t\t// We need to use the new flow, as that flow will be a browser flow. Bug fix for:\n\t\t//\n\t\t// https://github.com/ory/kratos/issues/2049!!\n\t\tif a.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\t\thttp.Redirect(w, r, urlx.CopyWithQuery(urlx.AppendPaths(s.d.Config().SelfPublicURL(r.Context()),\n\t\t\t\tRouteGetFlow), url.Values{\"id\": {a.ID.String()}}).String(), http.StatusSeeOther)\n\t\t} else {\n\t\t\thttp.Redirect(w, r, a.AppendTo(s.d.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther)\n\t\t}\n\t\treturn\n\t}\n\n\tif err := f.UI.ParseError(group, err); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tf.Active = sqlxx.NullString(group)\n\tif err := s.d.VerificationFlowPersister().UpdateVerificationFlow(r.Context(), f); err != nil {\n\t\ts.forward(w, r, f, err)\n\t\treturn\n\t}\n\n\tif f.Type == flow.TypeBrowser && !x.IsJSONRequest(r) {\n\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowVerificationUI(r.Context())).String(), http.StatusSeeOther)\n\t\treturn\n\t}\n\n\tupdatedFlow, innerErr := s.d.VerificationFlowPersister().GetVerificationFlow(r.Context(), f.ID)\n\tif innerErr != nil {\n\t\ts.forward(w, r, updatedFlow, innerErr)\n\t\treturn\n\t}\n\n\ts.d.Writer().WriteCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), updatedFlow)\n}\n\nfunc (s *ErrorHandler) forward(w http.ResponseWriter, r *http.Request, rr *Flow, err error) {\n\tif rr == nil {\n\t\tif x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t\treturn\n\t}\n\n\tif rr.Type == flow.TypeAPI || x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteErrorCode(w, r, x.RecoverStatusCode(err, http.StatusBadRequest), err)\n\t} else {\n\t\ts.d.SelfServiceErrorManager().Forward(r.Context(), w, r, err)\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/verification/error_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification_test\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/x/jsonx\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/gobuffalo/httptest\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestHandleError(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)\n\n\tpublic, _ := testhelpers.NewKratosServer(t, reg)\n\n\trouter := http.NewServeMux()\n\tts := httptest.NewServer(router)\n\tt.Cleanup(ts.Close)\n\n\ttesthelpers.NewVerificationUIFlowEchoServer(t, reg)\n\ttesthelpers.NewErrorTestServer(t, reg)\n\n\th := reg.VerificationFlowErrorHandler()\n\tsdk := testhelpers.NewSDKClient(public)\n\n\tvar verificationFlow *verification.Flow\n\tvar flowError error\n\tvar methodName node.UiNodeGroup\n\trouter.HandleFunc(\"GET /error\", func(w http.ResponseWriter, r *http.Request) {\n\t\th.WriteFlowError(w, r, verificationFlow, methodName, flowError)\n\t})\n\n\treset := func() {\n\t\tverificationFlow = nil\n\t\tflowError = nil\n\t\tmethodName = \"\"\n\t}\n\n\tnewFlow := func(t *testing.T, ttl time.Duration, ft flow.Type) *verification.Flow {\n\t\tt.Helper()\n\t\treq := &http.Request{URL: urlx.ParseOrPanic(\"/\")}\n\t\tstrategies, _, err := reg.GetActiveVerificationStrategies(context.Background())\n\t\trequire.NoError(t, err)\n\t\tf, err := verification.NewFlow(conf, ttl, nosurfx.FakeCSRFToken, req, strategies, ft)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(context.Background(), f))\n\t\tf, err = reg.VerificationFlowPersister().GetVerificationFlow(context.Background(), f.ID)\n\t\trequire.NoError(t, err)\n\t\treturn f\n\t}\n\n\texpectErrorUI := func(t *testing.T) (map[string]interface{}, *http.Response) {\n\t\tt.Helper()\n\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\trequire.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tsse, _, err := sdk.FrontendAPI.GetFlowError(context.Background()).Id(res.Request.URL.Query().Get(\"id\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\treturn sse.Error, nil\n\t}\n\n\tanHourAgo := time.Now().Add(-time.Hour)\n\n\tt.Run(\"case=error with nil flow defaults to error ui redirect\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tmethodName = node.UiNodeGroup(verification.VerificationStrategyLink)\n\n\t\tsse, _ := expectErrorUI(t)\n\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t})\n\n\tt.Run(\"case=error with nil flow detects application/json\", func(t *testing.T) {\n\t\tt.Cleanup(reset)\n\n\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\tmethodName = node.UiNodeGroup(verification.VerificationStrategyLink)\n\n\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tassert.Contains(t, res.Header.Get(\"Content-Type\"), \"application/json\")\n\t\tassert.NotContains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String()+\"?id=\")\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, string(body), \"system error\")\n\t})\n\n\tfor _, tc := range []struct {\n\t\tn string\n\t\tt flow.Type\n\t}{\n\t\t{\"api\", flow.TypeAPI},\n\t\t{\"spa\", flow.TypeBrowser},\n\t} {\n\t\tt.Run(\"flow=\"+tc.n, func(t *testing.T) {\n\t\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tverificationFlow = newFlow(t, time.Minute, flow.TypeAPI)\n\t\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\t\tmethodName = node.UiNodeGroup(verification.VerificationStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Contains(t, res.Request.URL.String(), public.URL+verification.RouteGetFlow)\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode, \"%+v\", res.Request)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int(text.ErrorValidationVerificationFlowExpired), int(gjson.GetBytes(body, \"ui.messages.0.id\").Int()), string(body))\n\t\t\t\tassert.NotEqual(t, verificationFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tverificationFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\t\tmethodName = node.UiNodeGroup(verification.VerificationStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(gjson.GetBytes(body, \"ui.messages.0.id\").Int()), \"%s\", body)\n\t\t\t\tassert.Equal(t, verificationFlow.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t})\n\n\t\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(reset)\n\n\t\t\t\tverificationFlow = newFlow(t, time.Minute, tc.t)\n\t\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\t\tmethodName = node.UiNodeGroup(verification.VerificationStrategyLink)\n\n\t\t\t\tres, err := ts.Client().Do(testhelpers.NewHTTPGetJSONRequest(t, ts.URL+\"/error\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\trequire.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.JSONEq(t, x.MustEncodeJSON(t, flowError), gjson.GetBytes(body, \"error\").Raw)\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\texpectVerificationUI := func(t *testing.T) (*verification.Flow, *http.Response) {\n\t\t\tres, err := ts.Client().Get(ts.URL + \"/error\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowVerificationUI(ctx).String()+\"?flow=\")\n\n\t\t\tvf, err := reg.VerificationFlowPersister().GetVerificationFlow(context.Background(), uuid.FromStringOrNil(res.Request.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t\treturn vf, res\n\t\t}\n\n\t\tt.Run(\"case=expired error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tverificationFlow = &verification.Flow{Type: flow.TypeBrowser}\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\t\t\tmethodName = node.LinkGroup\n\n\t\t\tlf, _ := expectVerificationUI(t)\n\t\t\trequire.Len(t, lf.UI.Messages, 1, \"%s\", jsonx.TestMarshalJSONString(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationVerificationFlowExpired), int(lf.UI.Messages[0].ID))\n\t\t})\n\n\t\tt.Run(\"case=validation error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tverificationFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = schema.NewInvalidCredentialsError()\n\t\t\tmethodName = node.LinkGroup\n\n\t\t\tlf, _ := expectVerificationUI(t)\n\t\t\trequire.NotEmpty(t, lf.UI, x.MustEncodeJSON(t, lf))\n\t\t\trequire.Len(t, lf.UI.Messages, 1, x.MustEncodeJSON(t, lf))\n\t\t\tassert.Equal(t, int(text.ErrorValidationInvalidCredentials), int(lf.UI.Messages[0].ID), x.MustEncodeJSON(t, lf))\n\t\t})\n\n\t\tt.Run(\"case=generic error\", func(t *testing.T) {\n\t\t\tt.Cleanup(reset)\n\n\t\t\tverificationFlow = newFlow(t, time.Minute, flow.TypeBrowser)\n\t\t\tflowError = herodot.ErrInternalServerError.WithReason(\"system error\")\n\t\t\tmethodName = node.LinkGroup\n\n\t\t\tsse, _ := expectErrorUI(t)\n\t\t\tassertx.EqualAsJSON(t, flowError, sse)\n\t\t})\n\n\t\tt.Run(\"case=fails to retry flow if recovery strategy id is not valid\", func(t *testing.T) {\n\t\t\tt.Cleanup(func() {\n\t\t\t\treset()\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationUse, \"code\")\n\t\t\t})\n\n\t\t\tverificationFlow = newFlow(t, 0, flow.TypeBrowser)\n\t\t\tverificationFlow.Active = \"not-valid\"\n\t\t\tflowError = flow.NewFlowExpiredError(anHourAgo)\n\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationUse, \"not-valid\")\n\t\t\tsse, _ := expectErrorUI(t)\n\t\t\ttesthelpers.SnapshotTExcept(t, sse, nil)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/verification/fake_strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\ntype FakeStrategy struct{}\n\nvar _ Strategy = new(FakeStrategy)\n\nfunc (f FakeStrategy) VerificationStrategyID() string {\n\treturn \"fake\"\n}\n\nfunc (f FakeStrategy) IsPrimary() bool {\n\treturn true\n}\n\nfunc (f FakeStrategy) VerificationNodeGroup() node.UiNodeGroup {\n\treturn \"fake\"\n}\n\nfunc (f FakeStrategy) PopulateVerificationMethod(*http.Request, *Flow) error {\n\treturn nil\n}\n\nfunc (f FakeStrategy) Verify(_ http.ResponseWriter, _ *http.Request, _ *Flow) (err error) {\n\treturn nil\n}\n\nfunc (f FakeStrategy) SendVerificationCode(context.Context, *Flow, *identity.Identity, *identity.VerifiableAddress) error {\n\treturn nil\n}\n\nfunc (f FakeStrategy) NodeGroup() node.UiNodeGroup {\n\treturn \"fake\"\n}\n"
  },
  {
    "path": "selfservice/flow/verification/flow.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/redir\"\n\n\t\"github.com/ory/pop/v6\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\n// A Verification Flow\n//\n// Used to verify an out-of-band communication\n// channel such as an email address or a phone number.\n//\n// For more information head over to: https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation\n//\n// swagger:model verificationFlow\ntype Flow struct {\n\t// ID represents the request's unique ID. When performing the verification flow, this\n\t// represents the id in the verify ui's query parameter: http://<selfservice.flows.verification.ui_url>?request=<id>\n\t//\n\t// type: string\n\t// format: uuid\n\t// required: true\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// Type represents the flow's type which can be either \"api\" or \"browser\", depending on the flow interaction.\n\t//\n\t// required: true\n\tType flow.Type `json:\"type\" db:\"type\" faker:\"flow_type\"`\n\n\t// ExpiresAt is the time (UTC) when the request expires. If the user still wishes to verify the address,\n\t// a new request has to be initiated.\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the request occurred.\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// RequestURL is the initial URL that was requested from Ory Kratos. It can be used\n\t// to forward information contained in the URL's path or query for example.\n\tRequestURL string `json:\"request_url\" db:\"request_url\"`\n\n\t// ReturnTo contains the requested return_to URL.\n\tReturnTo string `json:\"return_to,omitempty\" db:\"-\"`\n\n\t// Active, if set, contains the registration method that is being used. It is initially\n\t// not set.\n\tActive sqlxx.NullString `json:\"active,omitempty\" faker:\"-\" db:\"active_method\"`\n\n\t// UI contains data which must be shown in the user interface.\n\t//\n\t// required: true\n\tUI *container.Container `json:\"ui\" db:\"ui\"`\n\n\t// State represents the state of this request:\n\t//\n\t// - choose_method: ask the user to choose a method (e.g. verify your email)\n\t// - sent_email: the email has been sent to the user\n\t// - passed_challenge: the request was successful and the verification challenge was passed.\n\t//\n\t// required: true\n\tState State `json:\"state\" faker:\"-\" db:\"state\"`\n\n\t// OAuth2LoginChallenge holds the login challenge originally set during the registration flow.\n\tOAuth2LoginChallenge sqlxx.NullString `json:\"-\" db:\"oauth2_login_challenge\"`\n\tOAuth2LoginChallengeParams\n\n\t// CSRFToken contains the anti-csrf token associated with this request.\n\tCSRFToken string `json:\"-\" db:\"csrf_token\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\tNID       uuid.UUID `json:\"-\"  faker:\"-\" db:\"nid\"`\n\n\t// TransientPayload is used to pass data from the verification flow to hooks and email templates\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" faker:\"-\" db:\"-\"`\n}\n\ntype OAuth2LoginChallengeParams struct {\n\t// SessionID holds the session id if set from a registraton hook.\n\tSessionID uuid.NullUUID `json:\"-\" faker:\"-\" db:\"session_id\"`\n\n\t// IdentityID holds the identity id if set from a registraton hook.\n\tIdentityID uuid.NullUUID `json:\"-\" faker:\"-\" db:\"identity_id\"`\n\n\t// AMR contains a list of authentication methods that were used to verify the\n\t// session if set from a registration hook.\n\tAMR session.AuthenticationMethods `db:\"authentication_methods\" json:\"-\"`\n}\n\nvar _ flow.Flow = (*Flow)(nil)\n\nfunc NewFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, strategies Strategies, ft flow.Type) (*Flow, error) {\n\tnow := time.Now().UTC()\n\tid := x.NewUUID()\n\n\t// Pre-validate the return to URL which is contained in the HTTP request.\n\trequestURL := x.RequestURL(r).String()\n\t_, err := redir.SecureRedirectTo(r,\n\t\tconf.SelfServiceBrowserDefaultReturnTo(r.Context()),\n\t\tredir.SecureRedirectUseSourceURL(requestURL),\n\t\tredir.SecureRedirectAllowURLs(conf.SelfServiceBrowserAllowedReturnToDomains(r.Context())),\n\t\tredir.SecureRedirectAllowSelfServiceURLs(conf.SelfPublicURL(r.Context())),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tf := &Flow{\n\t\tID:         id,\n\t\tExpiresAt:  now.Add(exp),\n\t\tIssuedAt:   now,\n\t\tRequestURL: requestURL,\n\t\tUI: &container.Container{\n\t\t\tMethod: \"POST\",\n\t\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(conf.SelfPublicURL(r.Context()), RouteSubmitFlow), id).String(),\n\t\t},\n\t\tCSRFToken: csrf,\n\t\tState:     flow.StateChooseMethod,\n\t\tType:      ft,\n\t}\n\n\tfor _, strategy := range strategies {\n\t\tif ps, isPrimary := strategy.(PrimaryStrategy); isPrimary {\n\t\t\tf.Active = sqlxx.NullString(ps.NodeGroup())\n\t\t}\n\t\tif err := strategy.PopulateVerificationMethod(r, f); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn f, nil\n}\n\nfunc FromOldFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, strategies Strategies, of *Flow) (*Flow, error) {\n\tf := of.Type\n\t// Using the same flow in the recovery/verification context can lead to using API flow in a verification/recovery email\n\tif of.Type == flow.TypeAPI {\n\t\tf = flow.TypeBrowser\n\t}\n\tnf, err := NewFlow(conf, exp, csrf, r, strategies, f)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnf.RequestURL = of.RequestURL\n\treturn nf, nil\n}\n\nfunc NewPostHookFlow(conf *config.Config, exp time.Duration, csrf string, r *http.Request, strategies Strategies, original flow.Flow) (*Flow, error) {\n\tf, err := NewFlow(conf, exp, csrf, r, strategies, original.GetType())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf.TransientPayload = original.GetTransientPayload()\n\trequestURL, err := url.ParseRequestURI(original.GetRequestURL())\n\tif err != nil {\n\t\trequestURL = new(url.URL)\n\t}\n\tquery := requestURL.Query()\n\t// we need to keep the return_to in-tact if the `after_verification_return_to` is empty\n\t// otherwise we take the `after_verification_return_to` query parameter over the current `return_to`\n\tif afterVerificationReturn := query.Get(\"after_verification_return_to\"); afterVerificationReturn != \"\" {\n\t\tquery.Set(\"return_to\", afterVerificationReturn)\n\t}\n\tquery.Del(\"after_verification_return_to\")\n\trequestURL.RawQuery = query.Encode()\n\tf.RequestURL = requestURL.String()\n\tif t, ok := original.(flow.OAuth2ChallengeProvider); ok {\n\t\tf.OAuth2LoginChallenge = t.GetOAuth2LoginChallenge()\n\t}\n\treturn f, nil\n}\n\nfunc (f *Flow) GetType() flow.Type                        { return f.Type }\nfunc (f *Flow) GetRequestURL() string                     { return f.RequestURL }\nfunc (Flow) TableName() string                            { return \"selfservice_verification_flows\" }\nfunc (f Flow) GetID() uuid.UUID                           { return f.ID }\nfunc (f *Flow) GetState() State                           { return f.State }\nfunc (Flow) GetFlowName() flow.FlowName                   { return flow.VerificationFlow }\nfunc (f *Flow) SetState(state State)                      { f.State = state }\nfunc (f *Flow) GetTransientPayload() json.RawMessage      { return f.TransientPayload }\nfunc (f *Flow) GetOAuth2LoginChallenge() sqlxx.NullString { return f.OAuth2LoginChallenge }\nfunc (f *Flow) GetUI() *container.Container               { return f.UI }\n\nfunc (f *Flow) Valid() error {\n\tif f.ExpiresAt.Before(time.Now()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\treturn nil\n}\n\nfunc (f *Flow) AppendTo(src *url.URL) *url.URL {\n\tvalues := src.Query()\n\tvalues.Set(\"flow\", f.ID.String())\n\treturn urlx.CopyWithQuery(src, values)\n}\n\nfunc (f *Flow) SetCSRFToken(token string) {\n\tf.CSRFToken = token\n\tf.UI.SetCSRF(token)\n}\n\nfunc (f Flow) MarshalJSON() ([]byte, error) {\n\ttype local Flow\n\tf.SetReturnTo()\n\treturn json.Marshal(local(f))\n}\n\nfunc (f *Flow) SetReturnTo() {\n\tif u, err := url.Parse(f.RequestURL); err == nil {\n\t\tf.ReturnTo = u.Query().Get(\"return_to\")\n\t}\n}\n\nfunc (f *Flow) AfterFind(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\nfunc (f *Flow) AfterSave(*pop.Connection) error {\n\tf.SetReturnTo()\n\treturn nil\n}\n\n// ContinueURL generates the URL to show on the continue screen after succesful verification\n//\n// It follows the following precedence:\n//  1. If a `return_to` parameter has been passed to the flow's creation, is a valid URL and it's in the `selfservice.allowed_return_urls` that URL is returned\n//  2. If `selfservice.flows.verification.after` is set, that URL is returned\n//  3. As a fallback, the `selfservice.default_browser_return_url` URL is returned\nfunc (f *Flow) ContinueURL(ctx context.Context, config *config.Config) *url.URL {\n\tflowContinueURL := config.SelfServiceFlowVerificationReturnTo(ctx, config.SelfServiceBrowserDefaultReturnTo(ctx))\n\n\t// Parse the flows request URL\n\tverificationRequestURL, err := urlx.Parse(f.GetRequestURL())\n\tif err != nil {\n\t\t// Return flow default, or global default return URL\n\t\treturn flowContinueURL\n\t}\n\n\tverificationRequest := http.Request{URL: verificationRequestURL}\n\n\treturnTo, err := redir.SecureRedirectTo(&verificationRequest, flowContinueURL,\n\t\tredir.SecureRedirectAllowSelfServiceURLs(config.SelfPublicURL(ctx)),\n\t\tredir.SecureRedirectAllowURLs(config.SelfServiceBrowserAllowedReturnToDomains(ctx)),\n\t)\n\tif err != nil {\n\t\t// an error occured return flow default, or global default return URL\n\t\treturn flowContinueURL\n\t}\n\treturn returnTo\n}\n\nfunc (f *Flow) ToLoggerField() map[string]interface{} {\n\tif f == nil {\n\t\treturn map[string]interface{}{}\n\t}\n\treturn map[string]interface{}{\n\t\t\"id\":          f.ID.String(),\n\t\t\"return_to\":   f.ReturnTo,\n\t\t\"request_url\": f.RequestURL,\n\t\t\"active\":      f.Active,\n\t\t\"Type\":        f.Type,\n\t\t\"nid\":         f.NID,\n\t\t\"state\":       f.State,\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/verification/flow_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/jsonx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n)\n\nfunc TestFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, _ := pkg.NewFastRegistryWithMocks(t)\n\n\tmust := func(r *verification.Flow, err error) *verification.Flow {\n\t\trequire.NoError(t, err)\n\t\treturn r\n\t}\n\n\tu := &http.Request{URL: urlx.ParseOrPanic(\"http://foo/bar/baz\"), Host: \"foo\"}\n\tfor k, tc := range []struct {\n\t\tr         *verification.Flow\n\t\texpectErr bool\n\t}{\n\t\t{r: must(verification.NewFlow(conf, time.Hour, \"\", u, nil, flow.TypeBrowser))},\n\t\t{r: must(verification.NewFlow(conf, -time.Hour, \"\", u, nil, flow.TypeBrowser)), expectErr: true},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\terr := tc.r.Valid()\n\t\t\tif tc.expectErr {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\t\t})\n\t}\n\n\tt.Run(\"type=return_to\", func(t *testing.T) {\n\t\t_, err := verification.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=https://not-allowed/foobar\"}, Host: \"ory.sh\"}, nil, flow.TypeBrowser)\n\t\trequire.Error(t, err)\n\n\t\t_, err = verification.NewFlow(conf, 0, \"csrf\", &http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=\" + urlx.AppendPaths(conf.SelfPublicURL(ctx), \"/self-service/login/browser\").String()}, Host: \"ory.sh\"}, nil, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t})\n\n\tassert.EqualValues(t, flow.StateChooseMethod,\n\t\tmust(verification.NewFlow(conf, time.Hour, \"\", u, nil, flow.TypeBrowser)).State)\n}\n\nfunc TestGetType(t *testing.T) {\n\tfor _, ft := range []flow.Type{\n\t\tflow.TypeAPI,\n\t\tflow.TypeBrowser,\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", ft), func(t *testing.T) {\n\t\t\tr := &verification.Flow{Type: ft}\n\t\t\tassert.Equal(t, ft, r.GetType())\n\t\t})\n\t}\n}\n\nfunc TestGetRequestURL(t *testing.T) {\n\texpectedURL := \"http://foo/bar/baz\"\n\tf := &verification.Flow{RequestURL: expectedURL}\n\tassert.Equal(t, expectedURL, f.GetRequestURL())\n}\n\nfunc TestNewPostHookFlow(t *testing.T) {\n\tconf := pkg.NewConfigurationWithDefaults(t)\n\tu := &http.Request{URL: urlx.ParseOrPanic(\"http://foo/bar/baz\"), Host: \"foo\"}\n\texpectReturnTo := func(t *testing.T, originalFlowRequestQueryParams url.Values, expectedReturnTo string) {\n\t\toriginalFlow := registration.Flow{\n\t\t\tRequestURL: \"http://foo.com/bar?\" + originalFlowRequestQueryParams.Encode(),\n\t\t}\n\t\tt.Log(originalFlow.RequestURL)\n\t\tf, err := verification.NewPostHookFlow(conf, time.Second, \"\", u, nil, &originalFlow)\n\t\trequire.NoError(t, err)\n\t\tu, err := urlx.Parse(f.RequestURL)\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, strings.HasPrefix(f.RequestURL, \"http://foo.com/bar?\"))\n\t\tassert.Equal(t, \"\", u.Query().Get(\"after_verification_return_to\"))\n\t\tassert.Equal(t, expectedReturnTo, u.Query().Get(\"return_to\"))\n\t}\n\n\tt.Run(\"case=after_verification_return_to supplied\", func(t *testing.T) {\n\t\texpectedReturnTo := \"http://foo.com/verification_callback\"\n\t\texpectReturnTo(t, url.Values{\"after_verification_return_to\": {expectedReturnTo}}, expectedReturnTo)\n\t})\n\n\tt.Run(\"case=return_to supplied\", func(t *testing.T) {\n\t\texpectReturnTo(t, url.Values{\"return_to\": {\"http://foo.com/original_flow_callback\"}}, \"http://foo.com/original_flow_callback\")\n\t})\n\n\tt.Run(\"case=return_to and after_verification_return_to supplied\", func(t *testing.T) {\n\t\texpectedReturnTo := \"http://foo.com/verification_callback\"\n\t\texpectReturnTo(t, url.Values{\n\t\t\t\"return_to\":                    {\"http://foo.com/original_flow_callback\"},\n\t\t\t\"after_verification_return_to\": {expectedReturnTo},\n\t\t}, expectedReturnTo)\n\t})\n}\n\nfunc TestFlowEncodeJSON(t *testing.T) {\n\tassert.EqualValues(t, \"\", gjson.Get(jsonx.TestMarshalJSONString(t, &verification.Flow{RequestURL: \"https://foo.bar?foo=bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, &verification.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n\tassert.EqualValues(t, \"/bar\", gjson.Get(jsonx.TestMarshalJSONString(t, verification.Flow{RequestURL: \"https://foo.bar?return_to=/bar\"}), \"return_to\").String())\n}\n\nfunc TestFromOldFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf := pkg.NewConfigurationWithDefaults(t)\n\tr := http.Request{URL: &url.URL{Path: \"/\", RawQuery: \"return_to=\" + urlx.AppendPaths(conf.SelfPublicURL(ctx), \"/self-service/login/browser\").String()}, Host: \"ory.sh\"}\n\tfor _, ft := range []flow.Type{\n\t\tflow.TypeAPI,\n\t\tflow.TypeBrowser,\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=original flow is %s\", ft), func(t *testing.T) {\n\t\t\tf, err := verification.NewFlow(conf, 0, \"csrf\", &r, nil, ft)\n\t\t\trequire.NoError(t, err)\n\t\t\tnf, err := verification.FromOldFlow(conf, time.Duration(time.Hour), f.CSRFToken, &r, nil, f)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, flow.TypeBrowser, nf.Type)\n\t\t})\n\t}\n}\n\nfunc TestContinueURL(t *testing.T) {\n\tconst globalReturnTo = \"https://ory.sh/global-return-to\"\n\tconst localReturnTo = \"https://ory.sh/local-return-to\"\n\tconst flowReturnTo = \"https://ory.sh/flow-return-to\"\n\n\tfor _, tc := range []struct {\n\t\tdesc       string\n\t\tprep       func(conf *config.Config)\n\t\trequestURL string\n\t\texpect     string\n\t}{\n\t\t{\n\t\t\tdesc: \"return_to has precedence over global return to\",\n\t\t\tprep: func(conf *config.Config) {\n\t\t\t\tconf.MustSet(context.Background(), config.ViperKeyURLsAllowedReturnToDomains, []string{localReturnTo})\n\t\t\t},\n\t\t\trequestURL: fmt.Sprintf(\"http://kratos:4433/verification?return_to=%s\", localReturnTo),\n\t\t\texpect:     localReturnTo,\n\t\t},\n\t\t{\n\t\t\tdesc:       \"with return_to not allowed\",\n\t\t\trequestURL: fmt.Sprintf(\"http://kratos:4433/verification?return_to=%s\", localReturnTo),\n\t\t\texpect:     globalReturnTo,\n\t\t},\n\t\t{\n\t\t\tdesc:       \"with invalid request url\",\n\t\t\trequestURL: string([]byte{0x7f}), // 0x7f is an ASCII control char, and fails URL validation\n\t\t\texpect:     globalReturnTo,\n\t\t},\n\t\t{\n\t\t\tdesc: \"flow return to has precedence over global return to\",\n\t\t\tprep: func(conf *config.Config) {\n\t\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceVerificationBrowserDefaultReturnTo, flowReturnTo)\n\t\t\t},\n\t\t\trequestURL: \"http://kratos:4433/verification\",\n\t\t\texpect:     flowReturnTo,\n\t\t},\n\t\t{\n\t\t\tdesc: \"return_to has precedence over flow return to\",\n\t\t\tprep: func(conf *config.Config) {\n\t\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceVerificationBrowserDefaultReturnTo, flowReturnTo)\n\t\t\t\tconf.MustSet(context.Background(), config.ViperKeyURLsAllowedReturnToDomains, []string{localReturnTo})\n\t\t\t},\n\t\t\trequestURL: fmt.Sprintf(\"http://kratos:4433/verification?return_to=%s\", localReturnTo),\n\t\t\texpect:     localReturnTo,\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", tc.desc), func(t *testing.T) {\n\t\t\tconf := pkg.NewConfigurationWithDefaults(t)\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceBrowserDefaultReturnTo, globalReturnTo)\n\t\t\tif tc.prep != nil {\n\t\t\t\ttc.prep(conf)\n\t\t\t}\n\t\t\tflow := verification.Flow{\n\t\t\t\tRequestURL: tc.requestURL,\n\t\t\t}\n\n\t\t\turl := flow.ContinueURL(context.Background(), conf)\n\t\t\trequire.NotNil(t, url)\n\n\t\t\trequire.Equal(t, tc.expect, url.String())\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flow/verification/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst (\n\tRouteInitBrowserFlow = \"/self-service/verification/browser\"\n\tRouteInitAPIFlow     = \"/self-service/verification/api\"\n\tRouteGetFlow         = \"/self-service/verification/flows\"\n\n\tRouteSubmitFlow = \"/self-service/verification\"\n)\n\ntype (\n\tHandlerProvider interface {\n\t\tVerificationHandler() *Handler\n\t}\n\thandlerDependencies interface {\n\t\terrorx.ManagementProvider\n\t\tidentity.ManagementProvider\n\t\tidentity.PrivilegedPoolProvider\n\t\tconfig.Provider\n\t\thydra.Provider\n\t\tsession.PersistenceProvider\n\t\tsession.ManagementProvider\n\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\thttpx.WriterProvider\n\t\tnosurfx.CSRFProvider\n\t\tlogrusx.Provider\n\n\t\tFlowPersistenceProvider\n\t\tErrorHandlerProvider\n\t\tStrategyProvider\n\t\tHookExecutorProvider\n\t}\n\tHandler struct {\n\t\td handlerDependencies\n\t}\n)\n\nfunc NewHandler(d handlerDependencies) *Handler { return &Handler{d: d} }\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\th.d.CSRFHandler().IgnorePath(RouteInitAPIFlow)\n\th.d.CSRFHandler().IgnorePath(RouteSubmitFlow)\n\n\tpublic.GET(RouteInitBrowserFlow, h.createBrowserVerificationFlow)\n\tpublic.GET(RouteInitAPIFlow, h.createNativeVerificationFlow)\n\tpublic.GET(RouteGetFlow, h.getVerificationFlow)\n\n\tpublic.POST(RouteSubmitFlow, h.updateVerificationFlow)\n\tpublic.GET(RouteSubmitFlow, h.updateVerificationFlow)\n}\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(RouteInitBrowserFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteInitAPIFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteGetFlow, redir.RedirectToPublicRoute(h.d))\n\n\tadmin.POST(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n\tadmin.GET(RouteSubmitFlow, redir.RedirectToPublicRoute(h.d))\n}\n\ntype FlowOption func(f *Flow)\n\nfunc WithFlowReturnTo(returnTo string) FlowOption {\n\treturn func(f *Flow) {\n\t\tf.ReturnTo = returnTo\n\t}\n}\n\nfunc (h *Handler) NewVerificationFlow(w http.ResponseWriter, r *http.Request, ft flow.Type, opts ...FlowOption) (*Flow, error) {\n\tstrategies, _, err := h.d.GetActiveVerificationStrategies(r.Context())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tf, err := NewFlow(h.d.Config(), h.d.Config().SelfServiceFlowVerificationRequestLifespan(r.Context()), h.d.GenerateCSRFToken(r), r, strategies, ft)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, o := range opts {\n\t\to(f)\n\t}\n\n\tif err := h.d.VerificationExecutor().PreVerificationHook(w, r, f); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := h.d.VerificationFlowPersister().CreateVerificationFlow(r.Context(), f); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn f, nil\n}\n\n// Create Verification Flow Parameters for Native Apps\n//\n// swagger:parameters createNativeVerificationFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createNativeVerificationFlow struct {\n\t// A URL contained in the return_to key of the verification flow.\n\t// This piece of data has no effect on the actual logic of the flow and is purely informational.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n}\n\n// swagger:route GET /self-service/verification/api frontend createNativeVerificationFlow\n//\n// # Create Verification Flow for Native Apps\n//\n// This endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\n//\n// To fetch an existing verification flow call `/self-service/verification/flows?flow=<flow_id>`.\n//\n// You MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\n// Pages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\n// you vulnerable to a variety of CSRF attacks.\n//\n// This endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\n//\n// More information can be found at [Ory Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: verificationFlow\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createNativeVerificationFlow(w http.ResponseWriter, r *http.Request) {\n\tif !h.d.Config().SelfServiceFlowVerificationEnabled(r.Context()) {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Verification is not allowed because it was disabled.\")))\n\t\treturn\n\t}\n\n\treq, err := h.NewVerificationFlow(w, r, flow.TypeAPI)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.d.Writer().Write(w, r, req)\n}\n\n// Create Browser Verification Flow Parameters\n//\n// swagger:parameters createBrowserVerificationFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createBrowserVerificationFlow struct {\n\t// The URL to return the browser to after the flow was completed.\n\t//\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n}\n\n// swagger:route GET /self-service/verification/browser frontend createBrowserVerificationFlow\n//\n// # Create Verification Flow for Browser Clients\n//\n// This endpoint initializes a browser-based account verification flow. Once initialized, the browser will be redirected to\n// `selfservice.flows.verification.ui_url` with the flow ID set as the query parameter `?flow=`.\n//\n// If this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects.\n//\n// This endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\n//\n// More information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: verificationFlow\n//\t  303: emptyResponse\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) createBrowserVerificationFlow(w http.ResponseWriter, r *http.Request) {\n\tif !h.d.Config().SelfServiceFlowVerificationEnabled(r.Context()) {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Verification is not allowed because it was disabled.\")))\n\t\treturn\n\t}\n\n\treq, err := h.NewVerificationFlow(w, r, flow.TypeBrowser)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tredirTo := req.AppendTo(h.d.Config().SelfServiceFlowVerificationUI(r.Context())).String()\n\tx.SendFlowCompletedAsRedirectOrJSON(w, r, h.d.Writer(), req, redirTo)\n}\n\n// Get Verification Flow Parameters\n//\n// swagger:parameters getVerificationFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getVerificationFlow struct {\n\t// The Flow ID\n\t//\n\t// The value for this parameter comes from `request` URL Query parameter sent to your\n\t// application (e.g. `/verification?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tFlowID string `json:\"id\"`\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK on the server side you must include the HTTP Cookie Header\n\t// originally sent to your HTTP handler here.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookie string `json:\"cookie\"`\n}\n\n// swagger:route GET /self-service/verification/flows frontend getVerificationFlow\n//\n// # Get Verification Flow\n//\n// This endpoint returns a verification flow's context with, for example, error details and other information.\n//\n// Browser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\n// For AJAX requests you must ensure that cookies are included in the request or requests will fail.\n//\n// If you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\n// and you need to forward the incoming HTTP Cookie header to this endpoint:\n//\n//\t```js\n//\t// pseudo-code example\n//\trouter.get('/recovery', async function (req, res) {\n//\t  const flow = await client.getVerificationFlow(req.header('cookie'), req.query['flow'])\n//\n//\t  res.render('verification', flow)\n//\t})\n//\t```\n//\n// More information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: verificationFlow\n//\t  403: errorGeneric\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-low\nfunc (h *Handler) getVerificationFlow(w http.ResponseWriter, r *http.Request) {\n\tif !h.d.Config().SelfServiceFlowVerificationEnabled(r.Context()) {\n\t\th.d.SelfServiceErrorManager().Forward(r.Context(), w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Verification is not allowed because it was disabled.\")))\n\t\treturn\n\t}\n\n\trid := x.ParseUUID(r.URL.Query().Get(\"id\"))\n\treq, err := h.d.VerificationFlowPersister().GetVerificationFlow(r.Context(), rid)\n\tif err != nil {\n\t\th.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\t// Browser flows must include the CSRF token\n\t//\n\t// Resolves: https://github.com/ory/kratos/issues/1282\n\tif req.Type == flow.TypeBrowser && !nosurf.VerifyToken(h.d.GenerateCSRFToken(r), req.CSRFToken) {\n\t\th.d.Writer().WriteError(w, r, nosurfx.CSRFErrorReason(r, h.d))\n\t\treturn\n\t}\n\n\tif req.ExpiresAt.Before(time.Now().UTC()) {\n\t\tif req.Type == flow.TypeBrowser {\n\t\t\tredirectURL := flow.GetFlowExpiredRedirectURL(r.Context(), h.d.Config(), RouteInitBrowserFlow, req.ReturnTo)\n\n\t\t\th.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.\n\t\t\t\tWithReason(\"The verification flow has expired. Redirect the user to the verification flow init endpoint to initialize a new verification flow.\").\n\t\t\t\tWithDetail(\"redirect_to\", redirectURL.String()).\n\t\t\t\tWithDetail(\"return_to\", req.ReturnTo)))\n\t\t\treturn\n\t\t}\n\t\th.d.Writer().WriteError(w, r, errors.WithStack(nosurfx.ErrGone.\n\t\t\tWithReason(\"The verification flow has expired. Call the verification flow init API endpoint to initialize a new verification flow.\").\n\t\t\tWithDetail(\"api\", urlx.AppendPaths(h.d.Config().SelfPublicURL(r.Context()), RouteInitAPIFlow).String())))\n\t\treturn\n\t}\n\n\th.d.Writer().Write(w, r, req)\n}\n\n// Update Verification Flow Parameters\n//\n// swagger:parameters updateVerificationFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateVerificationFlow struct {\n\t// The Verification Flow ID\n\t//\n\t// The value for this parameter comes from `flow` URL Query parameter sent to your\n\t// application (e.g. `/verification?flow=abcde`).\n\t//\n\t// required: true\n\t// in: query\n\tFlow string `json:\"flow\"`\n\n\t// Verification Token\n\t//\n\t// The verification token which completes the verification request. If the token\n\t// is invalid (e.g. expired) an error will be shown to the end-user.\n\t//\n\t// This parameter is usually set in a link and not used by any direct API call.\n\t//\n\t// in: query\n\tToken string `json:\"token\" form:\"token\"`\n\n\t// in: body\n\t// required: true\n\tBody updateVerificationFlowBody\n\n\t// HTTP Cookies\n\t//\n\t// When using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\n\t// sent by the client to your server here. This ensures that CSRF and session cookies are respected.\n\t//\n\t// in: header\n\t// name: Cookie\n\tCookies string `json:\"Cookie\"`\n}\n\n// Update Verification Flow Request Body\n//\n// swagger:model updateVerificationFlowBody\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateVerificationFlowBody struct{}\n\n// swagger:route POST /self-service/verification frontend updateVerificationFlow\n//\n// # Complete Verification Flow\n//\n// Use this endpoint to complete a verification flow. This endpoint\n// behaves differently for API and browser flows and has several states:\n//\n//   - `choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\n//     and works with API- and Browser-initiated flows.\n//   - For API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\n//     and a HTTP 303 See Other redirect with a fresh verification flow if the flow was otherwise invalid (e.g. expired).\n//   - For Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Verification UI URL with the Verification Flow ID appended.\n//   - `sent_email` is the success state after `choose_method` when using the `link` method and allows the user to request another verification email. It\n//     works for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\n//   - `passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\"sending a verification link\")\n//     does not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\n//     (if the link was valid) and instructs the user to update their password, or a redirect to the Verification UI URL with\n//     a new Verification Flow ID which contains an error message that the verification link was invalid.\n//\n// More information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\n//\n//\tConsumes:\n//\t- application/json\n//\t- application/x-www-form-urlencoded\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: verificationFlow\n//\t  303: emptyResponse\n//\t  400: verificationFlow\n//\t  410: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-high\nfunc (h *Handler) updateVerificationFlow(w http.ResponseWriter, r *http.Request) {\n\trid, err := flow.GetFlowID(r)\n\tif err != nil {\n\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, nil, node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tctx := r.Context()\n\tf, err := h.d.VerificationFlowPersister().GetVerificationFlow(ctx, rid)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, nil, node.DefaultGroup, errors.WithStack(herodot.ErrNotFound.WithReasonf(\"The verification request could not be found. Please restart the flow.\")))\n\t\treturn\n\t} else if err != nil {\n\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, nil, node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tif err := f.Valid(); err != nil {\n\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, err)\n\t\treturn\n\t}\n\n\tvar g node.UiNodeGroup\n\tvar found bool\n\tfor _, ss := range h.d.AllVerificationStrategies() {\n\t\t// If a primary strategy is set, but it does not match the current strategy, that strategy is not responsible anyways.\n\t\tif ps, isPrimary := ss.(PrimaryStrategy); isPrimary && f.Active.String() != \"\" && f.Active.String() != ps.VerificationStrategyID() {\n\t\t\tcontinue\n\t\t}\n\n\t\terr := ss.Verify(w, r, f)\n\t\tif errors.Is(err, flow.ErrStrategyNotResponsible) {\n\t\t\tcontinue\n\t\t} else if errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\treturn\n\t\t} else if err != nil {\n\t\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, ss.NodeGroup(), err)\n\t\t\treturn\n\t\t}\n\n\t\tfound = true\n\t\tg = ss.NodeGroup()\n\t\tbreak\n\t}\n\n\tif !found {\n\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, errors.WithStack(schema.NewNoVerificationStrategyResponsible()))\n\t\treturn\n\t}\n\n\t// API flows can receive requests from the browser, if the link strategy is used.\n\t// However, x.IsBrowserRequest only checks for form submissions, not JSON requests made from a browser context\n\tif x.IsBrowserRequest(r) || (f.Type == flow.TypeBrowser && x.IsJSONRequest(r)) {\n\t\t// Special case: If we ended up here through a OAuth2 login challenge, we need to accept the login request\n\t\t// and redirect back to the OAuth2 provider.\n\t\tif flow.HasReachedState(flow.StatePassedChallenge, f.State) && f.OAuth2LoginChallenge.String() != \"\" {\n\t\t\tif !f.IdentityID.Valid || !f.SessionID.Valid {\n\t\t\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup,\n\t\t\t\t\therodot.ErrBadRequest.WithReasonf(\"No session was found for this flow. Please retry the authentication.\"))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tcallbackURL, err := h.d.Hydra().AcceptLoginRequest(ctx,\n\t\t\t\thydra.AcceptLoginRequestParams{\n\t\t\t\t\tLoginChallenge:        string(f.OAuth2LoginChallenge),\n\t\t\t\t\tIdentityID:            f.IdentityID.UUID.String(),\n\t\t\t\t\tSessionID:             f.SessionID.UUID.String(),\n\t\t\t\t\tAuthenticationMethods: f.AMR,\n\t\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tsess, err := h.d.SessionPersister().GetSession(ctx, f.SessionID.UUID, session.ExpandDefault)\n\t\t\tif err != nil {\n\t\t\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\terr = h.d.SessionManager().IssueCookie(ctx, w, r, sess)\n\t\t\tif err != nil {\n\t\t\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif x.IsJSONRequest(r) {\n\t\t\t\t// This intentionally works differently than the \"form browser\" flow,\n\t\t\t\t// as it _does_ show the `verification success` UI, but the \"Continue\"\n\t\t\t\t// button contains the link to the OAuth2 provider with the `login_verifier`.\n\t\t\t\tcontinueNode := f.UI.Nodes.Find(\"continue\")\n\t\t\t\tif continueNode != nil {\n\t\t\t\t\tif attr, ok := continueNode.Attributes.(*node.AnchorAttributes); ok {\n\t\t\t\t\t\tattr.HREF = callbackURL\n\t\t\t\t\t\tif err := h.d.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {\n\t\t\t\t\t\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, node.DefaultGroup, err)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\th.d.Writer().Write(w, r, f)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// The flow does not have the `continue` node, which is an unknown state.\n\t\t\t\t// This should never happen.\n\t\t\t} else {\n\t\t\t\thttp.Redirect(w, r, callbackURL, http.StatusSeeOther)\n\t\t\t\treturn\n\t\t\t}\n\t\t} else if x.IsBrowserRequest(r) {\n\t\t\thttp.Redirect(w, r, f.AppendTo(h.d.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)\n\t\t\treturn\n\t\t}\n\t}\n\n\tupdatedFlow, err := h.d.VerificationFlowPersister().GetVerificationFlow(ctx, f.ID)\n\tif err != nil {\n\t\th.d.VerificationFlowErrorHandler().WriteFlowError(w, r, f, g, err)\n\t\treturn\n\t}\n\n\th.d.Writer().Write(w, r, updatedFlow)\n}\n"
  },
  {
    "path": "selfservice/flow/verification/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/gobuffalo/httptest\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestGetFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(verification.VerificationStrategyLink),\n\t\tmap[string]interface{}{\"enabled\": true})\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\n\tpublic, _ := testhelpers.NewKratosServerWithCSRF(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewRedirTS(t, \"\", conf)\n\n\tsetupVerificationUI := func(t *testing.T, c *http.Client) *httptest.Server {\n\t\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t_, err := w.Write(testhelpers.EasyGetBody(t, c, public.URL+verification.RouteGetFlow+\"?id=\"+r.URL.Query().Get(\"flow\")))\n\t\t\trequire.NoError(t, err)\n\t\t}))\n\t\tt.Cleanup(ts.Close)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationUI, ts.URL)\n\t\treturn ts\n\t}\n\n\tassertFlowPayload := func(t *testing.T, body []byte, isApi bool) {\n\t\tif isApi {\n\t\t\tassert.Equal(t, \"api\", gjson.GetBytes(body, \"type\").String(), \"%s\", body)\n\t\t\tassert.Empty(t, gjson.GetBytes(body, \"ui.fields.#(attributes.name==csrf_token).attributes.value\").String(), \"%s\", body)\n\t\t} else {\n\t\t\tassert.Equal(t, \"browser\", gjson.GetBytes(body, \"type\").String(), \"%s\", body)\n\t\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String(), \"%s\", body)\n\t\t}\n\n\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"id\").String(), \"%s\", body)\n\t\tassert.Empty(t, gjson.GetBytes(body, \"headers\").Value(), \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), gjson.GetBytes(body, \"id\").String(), \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), public.URL, \"%s\", body)\n\t}\n\n\tt.Run(\"case=valid\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t_ = setupVerificationUI(t, client)\n\t\t\tres, body := testhelpers.EasyGet(t, client, public.URL+verification.RouteInitBrowserFlow)\n\t\t\trequire.NotEqualValues(t, res.Request.URL.String(), public.URL+verification.RouteInitBrowserFlow)\n\t\t\tassertFlowPayload(t, body, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t_ = setupVerificationUI(t, client)\n\t\t\tres, body := testhelpers.EasyGetJSON(t, client, public.URL+verification.RouteInitBrowserFlow)\n\t\t\trequire.EqualValues(t, res.Request.URL.String(), public.URL+verification.RouteInitBrowserFlow)\n\t\t\tassertFlowPayload(t, body, false)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t_ = setupVerificationUI(t, client)\n\t\t\tres, body := testhelpers.EasyGet(t, client, public.URL+verification.RouteInitAPIFlow)\n\t\t\tassert.Len(t, res.Header.Get(\"Set-Cookie\"), 0)\n\t\t\tassertFlowPayload(t, body, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=csrf cookie missing\", func(t *testing.T) {\n\t\tclient := http.DefaultClient\n\t\t_ = setupVerificationUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+verification.RouteInitBrowserFlow)\n\n\t\tassert.EqualValues(t, nosurfx.ErrInvalidCSRFToken.ReasonField, gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=expired\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t_ = setupVerificationUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+verification.RouteInitBrowserFlow)\n\n\t\t// Expire the flow\n\t\tf, err := reg.VerificationFlowPersister().GetVerificationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tf.ExpiresAt = time.Now().Add(-time.Second)\n\t\trequire.NoError(t, reg.VerificationFlowPersister().UpdateVerificationFlow(context.Background(), f))\n\n\t\tres, body := testhelpers.EasyGet(t, client, public.URL+verification.RouteGetFlow+\"?id=\"+f.ID.String())\n\t\tassert.EqualValues(t, http.StatusGone, res.StatusCode)\n\t\tassert.Equal(t, public.URL+verification.RouteInitBrowserFlow, gjson.GetBytes(body, \"error.details.redirect_to\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=expired with return_to\", func(t *testing.T) {\n\t\treturnTo := \"https://www.ory.sh\"\n\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t_ = setupVerificationUI(t, client)\n\t\tbody := testhelpers.EasyGetBody(t, client, public.URL+verification.RouteInitBrowserFlow+\"?return_to=\"+returnTo)\n\n\t\t// Expire the flow\n\t\tf, err := reg.VerificationFlowPersister().GetVerificationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tf.ExpiresAt = time.Now().Add(-time.Second)\n\t\trequire.NoError(t, reg.VerificationFlowPersister().UpdateVerificationFlow(context.Background(), f))\n\n\t\t// Retrieve the flow and verify that return_to is in the response\n\t\tgetURL := fmt.Sprintf(\"%s%s?id=%s&return_to=%s\", public.URL, verification.RouteGetFlow, f.ID, returnTo)\n\t\tgetBody := testhelpers.EasyGetBody(t, client, getURL)\n\t\tassert.Equal(t, gjson.GetBytes(getBody, \"error.details.return_to\").String(), returnTo)\n\n\t\t// submit the flow but it is expired\n\t\tu := public.URL + verification.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\tres, err := client.PostForm(u, url.Values{\"method\": {\"link\"}, \"csrf_token\": {f.CSRFToken}, \"email\": {\"email@ory.sh\"}})\n\t\trequire.NoError(t, err)\n\t\tresBody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\tf, err = reg.VerificationFlowPersister().GetVerificationFlow(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(resBody, \"id\").String()))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, public.URL+verification.RouteInitBrowserFlow+\"?return_to=\"+returnTo, f.RequestURL)\n\t})\n\n\tt.Run(\"case=relative redirect when self-service verification ui is a relative URL\", func(t *testing.T) {\n\t\tts, _ := testhelpers.NewKratosServer(t, reg)\n\t\treg.Config().MustSet(ctx, config.ViperKeySelfServiceVerificationUI, \"/verification-ts\")\n\t\tassert.Regexp(\n\t\t\tt,\n\t\t\t\"^/verification-ts.*$\",\n\t\t\ttesthelpers.GetSelfServiceRedirectLocation(t, ts.URL+verification.RouteInitBrowserFlow),\n\t\t)\n\t})\n\n\tt.Run(\"case=not found\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t_ = setupVerificationUI(t, client)\n\n\t\tres, _ := testhelpers.EasyGet(t, client, public.URL+verification.RouteGetFlow+\"?id=\"+x.NewUUID().String())\n\t\tassert.EqualValues(t, http.StatusNotFound, res.StatusCode)\n\t})\n\n\tt.Run(\"case=redirects with 303\", func(t *testing.T) {\n\t\tts, _ := testhelpers.NewKratosServer(t, reg)\n\n\t\t// prevent the redirect\n\t\tts.Client().CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t}\n\t\treq, err := http.NewRequest(\"GET\", ts.URL+verification.RouteInitBrowserFlow, nil)\n\t\trequire.NoError(t, err)\n\n\t\tres, err := ts.Client().Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\t// here we check that the redirect status is 303\n\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t})\n}\n\nfunc TestPostFlow(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\treg.WithSelfserviceStrategies(t, []any{&verification.FakeStrategy{}})\n\treg.SetHydra(hydra.NewFake())\n\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\n\tpublic, _ := testhelpers.NewKratosServerWithCSRF(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewRedirTS(t, \"\", conf)\n\n\tt.Run(\"client=browser/case=valid\", func(t *testing.T) {\n\t\tf := &verification.Flow{\n\t\t\tID:        uuid.Must(uuid.NewV4()),\n\t\t\tType:      \"browser\",\n\t\t\tExpiresAt: time.Now().Add(1 * time.Hour),\n\t\t\tIssuedAt:  time.Now(),\n\t\t\tState:     flow.StateChooseMethod,\n\t\t}\n\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(ctx, f))\n\n\t\tclient := testhelpers.NewNoRedirectClientWithCookies(t)\n\n\t\tu := public.URL + verification.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\tresp, err := client.PostForm(u, url.Values{\"method\": {\"fake\"}})\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, http.StatusSeeOther, resp.StatusCode)\n\t\tassert.Equal(t, conf.SelfServiceFlowVerificationUI(ctx).String()+\"?flow=\"+f.ID.String(), resp.Header.Get(\"Location\"))\n\t})\n\n\tt.Run(\"client=spa/case=valid\", func(t *testing.T) {\n\t\tf := &verification.Flow{\n\t\t\tID:        uuid.Must(uuid.NewV4()),\n\t\t\tType:      \"browser\",\n\t\t\tExpiresAt: time.Now().Add(1 * time.Hour),\n\t\t\tIssuedAt:  time.Now(),\n\t\t\tState:     flow.StateChooseMethod,\n\t\t}\n\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(ctx, f))\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\tu := public.URL + verification.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\treq, err := http.NewRequest(\"POST\", u, strings.NewReader(`{\"method\": \"fake\"}`))\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\tresp, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t})\n\n\tt.Run(\"suite=with OIDC login challenge\", func(t *testing.T) {\n\t\tcreateFlow := func(t *testing.T) *verification.Flow {\n\t\t\tt.Helper()\n\t\t\ts := testhelpers.CreateSession(t, reg)\n\n\t\t\tf := &verification.Flow{\n\t\t\t\tID:                   uuid.Must(uuid.NewV4()),\n\t\t\t\tType:                 \"browser\",\n\t\t\t\tExpiresAt:            time.Now().Add(1 * time.Hour),\n\t\t\t\tIssuedAt:             time.Now(),\n\t\t\t\tOAuth2LoginChallenge: hydra.FakeValidLoginChallenge,\n\t\t\t\tOAuth2LoginChallengeParams: verification.OAuth2LoginChallengeParams{\n\t\t\t\t\tSessionID:  uuid.NullUUID{UUID: s.ID, Valid: true},\n\t\t\t\t\tIdentityID: uuid.NullUUID{UUID: s.IdentityID, Valid: true},\n\t\t\t\t\tAMR:        s.AMR,\n\t\t\t\t},\n\t\t\t\tUI: &container.Container{\n\t\t\t\t\tAction: \"http://action\",\n\t\t\t\t\tNodes:  []*node.Node{node.NewAnchorField(\"continue\", \"https://ory.sh\", node.CodeGroup, text.NewInfoNodeLabelContinue())},\n\t\t\t\t},\n\t\t\t\tState: flow.StatePassedChallenge,\n\t\t\t}\n\t\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(ctx, f))\n\t\t\treturn f\n\t\t}\n\t\tt.Run(\"client=browser/case=succeeds with a session\", func(t *testing.T) {\n\t\t\tf := createFlow(t)\n\t\t\tclient := testhelpers.NewNoRedirectClientWithCookies(t)\n\n\t\t\tu := public.URL + verification.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\t\tresp, err := client.PostForm(u, url.Values{\"method\": {\"fake\"}})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusSeeOther, resp.StatusCode)\n\t\t\tassert.Equal(t, hydra.FakePostLoginURL, resp.Header.Get(\"Location\"))\n\t\t})\n\t\tt.Run(\"client=spa/case=succeeds with a session\", func(t *testing.T) {\n\t\t\tf := createFlow(t)\n\t\t\tclient := testhelpers.NewNoRedirectClientWithCookies(t)\n\n\t\t\tu := public.URL + verification.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\t\tresp, err := client.Post(u, \"application/json\", strings.NewReader(`{\"method\": \"fake\"}`))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\tbody := x.MustReadAll(resp.Body)\n\t\t\tassert.Equal(t, hydra.FakePostLoginURL, gjson.GetBytes(body, \"ui.nodes.#(attributes.id==continue).attributes.href\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"case=fails without a session\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t_, err := w.Write(testhelpers.EasyGetBody(t, client, public.URL+verification.RouteGetFlow+\"?id=\"+r.URL.Query().Get(\"flow\")))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}))\n\t\t\tt.Cleanup(ts.Close)\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationUI, ts.URL)\n\n\t\t\tf := &verification.Flow{\n\t\t\t\tID:                   uuid.Must(uuid.NewV4()),\n\t\t\t\tType:                 \"browser\",\n\t\t\t\tExpiresAt:            time.Now().Add(1 * time.Hour),\n\t\t\t\tIssuedAt:             time.Now(),\n\t\t\t\tOAuth2LoginChallenge: hydra.FakeValidLoginChallenge,\n\t\t\t\tState:                flow.StateChooseMethod,\n\t\t\t}\n\t\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(ctx, f))\n\n\t\t\tu := public.URL + verification.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\t\tresp, err := client.PostForm(u, url.Values{\"method\": {\"fake\"}})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\tassert.Equal(t, f.ID.String(), resp.Request.URL.Query().Get(\"flow\"))\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/verification/hook.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\ntype (\n\tPreHookExecutor interface {\n\t\tExecuteVerificationPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error\n\t}\n\tPreHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow) error\n\n\tPostHookExecutor interface {\n\t\tExecutePostVerificationHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error\n\t}\n\tPostHookExecutorFunc func(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error\n\n\tHooksProvider interface {\n\t\tPostVerificationHooks(ctx context.Context) ([]PostHookExecutor, error)\n\t\tPreVerificationHooks(ctx context.Context) ([]PreHookExecutor, error)\n\t}\n)\n\nfunc PostHookVerificationExecutorNames(e []PostHookExecutor) []string {\n\tnames := make([]string, len(e))\n\tfor k, ee := range e {\n\t\tnames[k] = fmt.Sprintf(\"%T\", ee)\n\t}\n\treturn names\n}\n\nfunc (f PreHookExecutorFunc) ExecuteVerificationPreHook(w http.ResponseWriter, r *http.Request, a *Flow) error {\n\treturn f(w, r, a)\n}\n\nfunc (f PostHookExecutorFunc) ExecutePostVerificationHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error {\n\treturn f(w, r, a, i)\n}\n\ntype (\n\texecutorDependencies interface {\n\t\tconfig.Provider\n\t\tidentity.ManagementProvider\n\t\tidentity.ValidationProvider\n\t\tsession.PersistenceProvider\n\t\tHooksProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tlogrusx.Provider\n\t\thttpx.WriterProvider\n\t}\n\n\tHookExecutor struct {\n\t\td executorDependencies\n\t}\n\n\tHookExecutorProvider interface {\n\t\tVerificationExecutor() *HookExecutor\n\t}\n)\n\nfunc NewHookExecutor(d executorDependencies) *HookExecutor {\n\treturn &HookExecutor{\n\t\td: d,\n\t}\n}\n\nfunc (e *HookExecutor) PreVerificationHook(w http.ResponseWriter, r *http.Request, a *Flow) error {\n\thooks, err := e.d.PreVerificationHooks(r.Context())\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, executor := range hooks {\n\t\tif err := executor.ExecuteVerificationPreHook(w, r, a); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (e *HookExecutor) PostVerificationHook(w http.ResponseWriter, r *http.Request, a *Flow, i *identity.Identity) error {\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tDebug(\"Running ExecutePostVerificationHooks.\")\n\thooks, err := e.d.PostVerificationHooks(r.Context())\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k, executor := range hooks {\n\t\tif err := executor.ExecutePostVerificationHook(w, r, a, i); err != nil {\n\t\t\treturn flow.HandleHookError(w, r, a, i.Traits, node.LinkGroup, err, e.d, e.d)\n\t\t}\n\n\t\te.d.Logger().WithRequest(r).\n\t\t\tWithField(\"executor\", fmt.Sprintf(\"%T\", executor)).\n\t\t\tWithField(\"executor_position\", k).\n\t\t\tWithField(\"executors\", PostHookVerificationExecutorNames(hooks)).\n\t\t\tWithField(\"identity_id\", i.ID).\n\t\t\tDebug(\"ExecutePostVerificationHook completed successfully.\")\n\t}\n\n\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationSucceeded(r.Context(), a.ID, i.ID, string(a.Type), a.Active.String()))\n\n\te.d.Logger().\n\t\tWithRequest(r).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tDebug(\"Post verification execution hooks completed successfully.\")\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/flow/verification/hook_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\n\t\"github.com/gobuffalo/httptest\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestVerificationExecutor(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tnewServer := func(t *testing.T, i *identity.Identity, ft flow.Type) *httptest.Server {\n\t\trouter := http.NewServeMux()\n\t\trouter.HandleFunc(\"GET /verification/pre\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\tstrategies, _, err := reg.GetActiveVerificationStrategies(r.Context())\n\t\t\trequire.NoError(t, err)\n\t\t\ta, err := verification.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, strategies, ft)\n\t\t\trequire.NoError(t, err)\n\t\t\tif testhelpers.SelfServiceHookErrorHandler(t, w, r, verification.ErrHookAbortFlow, reg.VerificationExecutor().PreVerificationHook(w, r, a)) {\n\t\t\t\t_, _ = w.Write([]byte(\"ok\"))\n\t\t\t}\n\t\t})\n\n\t\trouter.HandleFunc(\"GET /verification/post\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\tstrategies, _, err := reg.GetActiveVerificationStrategies(r.Context())\n\t\t\trequire.NoError(t, err)\n\t\t\ta, err := verification.NewFlow(conf, time.Minute, nosurfx.FakeCSRFToken, r, strategies, ft)\n\t\t\trequire.NoError(t, err)\n\t\t\ta.RequestURL = x.RequestURL(r).String()\n\t\t\tif testhelpers.SelfServiceHookErrorHandler(t, w, r, verification.ErrHookAbortFlow, reg.VerificationExecutor().PostVerificationHook(w, r, a, i)) {\n\t\t\t\t_, _ = w.Write([]byte(\"ok\"))\n\t\t\t}\n\t\t})\n\n\t\tts := httptest.NewServer(router)\n\t\tt.Cleanup(ts.Close)\n\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)\n\t\treturn ts\n\t}\n\n\tt.Run(\"method=PostVerificationHook\", func(t *testing.T) {\n\t\tt.Run(\"case=pass without hooks\", func(t *testing.T) {\n\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\tts := newServer(t, i, flow.TypeBrowser)\n\n\t\t\tres, _ := testhelpers.SelfServiceMakeHookRequest(t, ts, \"/verification/post\", false, url.Values{})\n\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=pass if hooks pass\", func(t *testing.T) {\n\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceVerificationAfter, config.HookGlobal),\n\t\t\t\t[]config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\tts := newServer(t, i, flow.TypeBrowser)\n\n\t\t\tres, _ := testhelpers.SelfServiceMakeHookRequest(t, ts, \"/verification/post\", false, url.Values{})\n\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=fail if hooks fail\", func(t *testing.T) {\n\t\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceVerificationAfter, config.HookGlobal),\n\t\t\t\t[]config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecutePostVerificationHook\": \"abort\"}`)}})\n\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\tts := newServer(t, i, flow.TypeBrowser)\n\n\t\t\tres, body := testhelpers.SelfServiceMakeHookRequest(t, ts, \"/verification/post\", false, url.Values{})\n\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Equal(t, \"\", body)\n\t\t})\n\n\t\tfor _, kind := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {\n\t\t\tt.Run(\"type=\"+string(kind)+\"/method=PreVerificationHook\", testhelpers.TestSelfServicePreHook(\n\t\t\t\tconfig.ViperKeySelfServiceVerificationBeforeHooks,\n\t\t\t\ttesthelpers.SelfServiceMakeVerificationPreHookRequest,\n\t\t\t\tfunc(t *testing.T) *httptest.Server {\n\t\t\t\t\ti := testhelpers.SelfServiceHookFakeIdentity(t)\n\t\t\t\t\treturn newServer(t, i, kind)\n\t\t\t\t},\n\t\t\t\tconf,\n\t\t\t))\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/flow/verification/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype (\n\tFlowPersistenceProvider interface {\n\t\tVerificationFlowPersister() FlowPersister\n\t}\n\tFlowPersister interface {\n\t\tCreateVerificationFlow(context.Context, *Flow) error\n\t\tGetVerificationFlow(ctx context.Context, id uuid.UUID) (*Flow, error)\n\t\tUpdateVerificationFlow(context.Context, *Flow) error\n\t\tDeleteExpiredVerificationFlows(context.Context, time.Time, int) error\n\t}\n)\n"
  },
  {
    "path": "selfservice/flow/verification/state.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification\n\nimport \"github.com/ory/kratos/selfservice/flow\"\n\n// Verification Flow State\n//\n// The state represents the state of the verification flow.\n//\n// - choose_method: ask the user to choose a method (e.g. recover account via email)\n// - sent_email: the email has been sent to the user\n// - passed_challenge: the request was successful and the recovery challenge was passed.\n//\n// swagger:model verificationFlowState\ntype State = flow.State\n"
  },
  {
    "path": "selfservice/flow/verification/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage verification\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/pkg/errors\"\n)\n\n//swagger:enum VerificationStrategy\ntype VerificationStrategy string\n\nconst (\n\tVerificationStrategyLink VerificationStrategy = \"link\"\n\tVerificationStrategyCode VerificationStrategy = \"code\"\n)\n\ntype (\n\tStrategy interface {\n\t\tVerificationStrategyID() string\n\t\tNodeGroup() node.UiNodeGroup\n\t\tPopulateVerificationMethod(*http.Request, *Flow) error\n\t\tVerify(w http.ResponseWriter, r *http.Request, f *Flow) (err error)\n\t}\n\tPrimaryStrategy interface {\n\t\tStrategy\n\t\tSendVerificationCode(context.Context, *Flow, *identity.Identity, *identity.VerifiableAddress) error\n\t}\n\tStrategies       []Strategy\n\tStrategyProvider interface {\n\t\tVerificationStrategies(ctx context.Context) Strategies\n\t\tAllVerificationStrategies() Strategies\n\t\tGetActiveVerificationStrategies(context.Context) (active Strategies, primary PrimaryStrategy, err error)\n\t}\n)\n\nfunc (s Strategies) ActiveStrategies(id string) (active Strategies, primary PrimaryStrategy, err error) {\n\tids := make([]string, len(s))\n\tactiveStrategies := Strategies{}\n\tfor k, ss := range s {\n\t\tids[k] = ss.VerificationStrategyID()\n\t\tif ps, isPrimary := ss.(PrimaryStrategy); ss.VerificationStrategyID() == id || !isPrimary {\n\t\t\tactiveStrategies = append(activeStrategies, ss)\n\t\t\tif isPrimary {\n\t\t\t\tprimary = ps\n\t\t\t}\n\t\t}\n\t}\n\n\tif primary == nil {\n\t\treturn nil, nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"unable to find strategy for %s have %v\", id, ids))\n\t}\n\n\treturn activeStrategies, primary, nil\n}\n\nfunc (s Strategies) MustStrategy(id string) Strategy {\n\t_, strategy, err := s.ActiveStrategies(id)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn strategy\n}\n"
  },
  {
    "path": "selfservice/flow/verification/stub/extension/schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"emails\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"ory.sh/kratos\": {\n              \"verification\": {\n                \"via\": \"email\"\n              }\n            }\n          }\n        },\n        \"username\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/verification/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/flow/verification/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc TestFlowPersister(ctx context.Context, p interface {\n\tpersistence.Persister\n},\n) func(t *testing.T) {\n\tclearids := func(r *verification.Flow) {\n\t\tr.ID = uuid.UUID{}\n\t}\n\n\treturn func(t *testing.T) {\n\t\tnid, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tt.Run(\"case=should error when the verification request does not exist\", func(t *testing.T) {\n\t\t\t_, err := p.GetVerificationFlow(ctx, x.NewUUID())\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tnewFlow := func(t *testing.T) *verification.Flow {\n\t\t\tvar r verification.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\tclearids(&r)\n\t\t\tr.State = flow.StateChooseMethod\n\t\t\treturn &r\n\t\t}\n\n\t\tt.Run(\"case=should create a new verification flow\", func(t *testing.T) {\n\t\t\tr := newFlow(t)\n\t\t\terr := p.CreateVerificationFlow(ctx, r)\n\t\t\trequire.NoError(t, err, \"%#v\", err)\n\t\t\trequire.Equal(t, nid, r.NID)\n\n\t\t\tt.Run(\"fail to find on other network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetVerificationFlow(ctx, r.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should create with set ids\", func(t *testing.T) {\n\t\t\tvar r verification.Flow\n\t\t\trequire.NoError(t, faker.FakeData(&r))\n\t\t\tr.State = flow.StateChooseMethod\n\t\t\trequire.NoError(t, p.CreateVerificationFlow(ctx, &r))\n\t\t\trequire.Equal(t, nid, r.NID)\n\n\t\t\tt.Run(\"fail to find on other network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetVerificationFlow(ctx, r.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should create and fetch a verification request\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\trequire.NoError(t, p.CreateVerificationFlow(ctx, expected))\n\t\t\trequire.Equal(t, nid, expected.NID)\n\n\t\t\tt.Run(\"fail to find on other network\", func(t *testing.T) {\n\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t_, err := p.GetVerificationFlow(ctx, expected.ID)\n\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t})\n\n\t\t\tactual, err := p.GetVerificationFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.EqualValues(t, expected.ID, actual.ID)\n\t\t\tx.AssertEqualTime(t, expected.IssuedAt, actual.IssuedAt)\n\t\t\tx.AssertEqualTime(t, expected.ExpiresAt, actual.ExpiresAt)\n\t\t\tassert.EqualValues(t, expected.RequestURL, actual.RequestURL)\n\t\t\tassertx.EqualAsJSON(t, expected.UI, actual.UI, \"expected:\\t%s\\nactual:\\t%s\", expected.UI, actual.UI)\n\t\t})\n\n\t\tt.Run(\"case=should create and update a verification request\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\texpected.UI.Nodes = node.Nodes{}\n\t\t\texpected.UI.Nodes.Append(node.NewInputField(\"zab\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\ta.Pattern = \"baz\"\n\t\t\t})))\n\n\t\t\texpected.UI.Nodes.Append(node.NewInputField(\"foo\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\ta.Pattern = \"baz\"\n\t\t\t})))\n\n\t\t\terr := p.CreateVerificationFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\texpected.UI.Action = \"/new-action\"\n\t\t\texpected.UI.Nodes.Append(\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"zab\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"zab\"\n\t\t\t\t})))\n\n\t\t\texpected.RequestURL = \"/new-request-url\"\n\n\t\t\tt.Run(\"fail to find on other network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\trequire.ErrorIs(t, other.UpdateVerificationFlow(ctx, expected), sqlcon.ErrNoRows)\n\n\t\t\t\tactual, err := p.GetVerificationFlow(ctx, expected.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.NotEqual(t, \"/new-request-url\", actual.RequestURL)\n\t\t\t})\n\n\t\t\trequire.NoError(t, p.UpdateVerificationFlow(ctx, expected))\n\n\t\t\tactual, err := p.GetVerificationFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, \"/new-action\", actual.UI.Action)\n\t\t\tassert.Equal(t, \"/new-request-url\", actual.RequestURL)\n\t\t\tassertx.EqualAsJSON(t, node.Nodes{\n\t\t\t\t// v0.5: {Name: \"zab\", Type: \"zab\", Pattern: \"zab\"},\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"baz\"\n\t\t\t\t})),\n\t\t\t\tnode.NewInputField(\"foo\", nil, node.DefaultGroup, \"bar\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"baz\"\n\t\t\t\t})),\n\t\t\t\t// v0.5: {Name: \"zab\", Type: \"bar\", Pattern: \"baz\"},\n\t\t\t\tnode.NewInputField(\"zab\", nil, node.DefaultGroup, \"zab\", node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Pattern = \"zab\"\n\t\t\t\t})),\n\t\t\t}, actual.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=should not cause data loss when updating a request without changes\", func(t *testing.T) {\n\t\t\texpected := newFlow(t)\n\t\t\terr := p.CreateVerificationFlow(ctx, expected)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tactual, err := p.GetVerificationFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.NoError(t, p.UpdateVerificationFlow(ctx, actual))\n\n\t\t\tactual, err = p.GetVerificationFlow(ctx, expected.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassertx.EqualAsJSON(t, expected.UI, actual.UI)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/flowhelpers/login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flowhelpers\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/session\"\n)\n\n// GuessForcedLoginIdentifier returns the identifier for login flows where the identity needs to refresh the session.\nfunc GuessForcedLoginIdentifier(r *http.Request, d interface {\n\tsession.ManagementProvider\n\tidentity.PrivilegedPoolProvider\n}, f interface {\n\tIsRefresh() bool\n}, ct identity.CredentialsType) (identifier string, id *identity.Identity, creds *identity.Credentials) {\n\tvar ok bool\n\t// This block adds the identifier to the method when the request is forced - as a hint for the user.\n\tif !f.IsRefresh() {\n\t\t// do nothing\n\t} else if sess, err := d.SessionManager().FetchFromRequest(r.Context(), r); err != nil {\n\t\t// do nothing\n\t} else if id, err = d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), sess.IdentityID); err != nil {\n\t\t// do nothing\n\t} else if creds, ok = id.GetCredentials(ct); !ok {\n\t\t// do nothing\n\t} else if len(creds.Identifiers) == 0 {\n\t\t// do nothing\n\t} else {\n\t\tidentifier = creds.Identifiers[0]\n\t}\n\treturn\n}\n"
  },
  {
    "path": "selfservice/flowhelpers/login_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage flowhelpers_test\n\nimport (\n\t\"context\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flowhelpers\"\n)\n\nfunc TestGuessForcedLoginIdentifier(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/login.schema.json\")\n\n\ti := identity.NewIdentity(\"\")\n\tic := identity.Credentials{\n\t\tType:        identity.CredentialsTypePassword,\n\t\tIdentifiers: []string{\"foobar\"},\n\t}\n\ti.Credentials[identity.CredentialsTypePassword] = ic\n\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), i))\n\n\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil)\n\n\tsess, err := testhelpers.NewActiveSession(req, reg, i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\trequire.NoError(t, err)\n\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), sess))\n\n\tr := httptest.NewRequest(\"GET\", \"/login\", nil)\n\tr.Header.Set(\"Authorization\", \"Bearer \"+sess.Token)\n\n\tvar f login.Flow\n\tf.Refresh = true\n\n\tidentifier, id, creds := flowhelpers.GuessForcedLoginIdentifier(r, reg, &f, identity.CredentialsTypePassword)\n\tassert.Equal(t, \"foobar\", identifier)\n\tassert.EqualValues(t, ic.Type, creds.Type)\n\tassert.EqualValues(t, ic.Identifiers, creds.Identifiers)\n\tassert.EqualValues(t, id.ID, id.ID)\n}\n"
  },
  {
    "path": "selfservice/flowhelpers/stub/login.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=body_is_empty.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"external_id\": \"original-external-id\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=identity_has_updated_admin_metadata.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"external_id\": \"original-external-id\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"useful\": \"admin metadata\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=identity_has_updated_external_id.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"external_id\": \"updated-external-id\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=identity_has_updated_public_metadata.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"external_id\": \"original-external-id\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"useful\": \"metadata\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=identity_has_updated_recovery_addresses.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"external_id\": \"original-external-id\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@other-example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@other-example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=identity_has_updated_state.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"external_id\": \"original-external-id\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"inactive\",\n  \"traits\": {\n    \"email\": \"some@example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=identity_has_updated_traits.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"external_id\": \"original-external-id\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@other-example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=identity_has_updated_verified_addresses.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"external_id\": \"original-external-id\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@other-example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@other-example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=identity_is_present_but_empty#01.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\"\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=identity_is_present_but_empty.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"external_id\": \"original-external-id\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/.snapshots/TestWebHooks-update_identity_fields-case=update_identity_fields-case=unset_external_id.json",
    "content": "{\n  \"id\": \"00000000-0000-0000-0000-000000000000\",\n  \"credentials\": {\n    \"password\": {\n      \"type\": \"password\",\n      \"identifiers\": [\n        \"test\"\n      ],\n      \"config\": {\n        \"hashed_password\": \"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"\n      },\n      \"version\": 0,\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  },\n  \"schema_id\": \"default\",\n  \"schema_url\": \"file://stub/default.schema.json\",\n  \"state\": \"active\",\n  \"traits\": {\n    \"email\": \"some@example.org\"\n  },\n  \"verifiable_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"verified\": false,\n      \"via\": \"email\",\n      \"status\": \"pending\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"recovery_addresses\": [\n    {\n      \"id\": \"00000000-0000-0000-0000-000000000000\",\n      \"value\": \"some@example.org\",\n      \"via\": \"email\",\n      \"created_at\": \"0001-01-01T00:00:00Z\",\n      \"updated_at\": \"0001-01-01T00:00:00Z\"\n    }\n  ],\n  \"metadata_public\": {\n    \"public\": \"data\"\n  },\n  \"metadata_admin\": {\n    \"admin\": \"data\"\n  },\n  \"created_at\": \"0001-01-01T00:00:00Z\",\n  \"updated_at\": \"0001-01-01T00:00:00Z\",\n  \"organization_id\": null\n}\n"
  },
  {
    "path": "selfservice/hook/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"net/http\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n)\n\nvar (\n\t_ registration.PostHookPrePersistExecutor  = new(Error)\n\t_ registration.PostHookPostPersistExecutor = new(Error)\n\t_ registration.PreHookExecutor             = new(Error)\n\n\t_ login.PreHookExecutor  = new(Error)\n\t_ login.PostHookExecutor = new(Error)\n\t_ login.PreHookExecutor  = new(Error)\n\n\t_ settings.PostHookPostPersistExecutor = new(Error)\n\t_ settings.PostHookPrePersistExecutor  = new(Error)\n\t_ settings.PreHookExecutor             = new(Error)\n\n\t_ verification.PreHookExecutor = new(Error)\n\t_ recovery.PreHookExecutor     = new(Error)\n)\n\ntype Error struct {\n\tConfig json.RawMessage\n}\n\nfunc (e Error) err(path string, abort error) error {\n\tswitch gjson.GetBytes(e.Config, path).String() {\n\tcase \"err\":\n\t\treturn errors.New(\"err\")\n\tcase \"abort\":\n\t\treturn abort\n\t}\n\treturn nil\n}\n\nfunc (e Error) ExecuteSettingsPreHook(w http.ResponseWriter, r *http.Request, a *settings.Flow, s *session.Session) error {\n\treturn e.err(\"ExecuteSettingsPreHook\", settings.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecuteSettingsPrePersistHook(w http.ResponseWriter, r *http.Request, a *settings.Flow, i *identity.Identity, s *session.Session) error {\n\treturn e.err(\"ExecuteSettingsPrePersistHook\", settings.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, a *settings.Flow, id *identity.Identity, s *session.Session) error {\n\treturn e.err(\"ExecuteSettingsPostPersistHook\", settings.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecuteLoginPostHook(w http.ResponseWriter, r *http.Request, g node.UiNodeGroup, a *login.Flow, s *session.Session) error {\n\treturn e.err(\"ExecuteLoginPostHook\", login.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecuteLoginPreHook(w http.ResponseWriter, r *http.Request, a *login.Flow) error {\n\treturn e.err(\"ExecuteLoginPreHook\", login.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecuteRegistrationPreHook(w http.ResponseWriter, r *http.Request, a *registration.Flow) error {\n\treturn e.err(\"ExecuteRegistrationPreHook\", registration.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecutePostRegistrationPostPersistHook(w http.ResponseWriter, r *http.Request, a *registration.Flow, s *session.Session) error {\n\treturn e.err(\"ExecutePostRegistrationPostPersistHook\", registration.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecutePostRegistrationPrePersistHook(w http.ResponseWriter, r *http.Request, a *registration.Flow, i *identity.Identity) error {\n\treturn e.err(\"ExecutePostRegistrationPrePersistHook\", registration.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecuteRecoveryPreHook(w http.ResponseWriter, r *http.Request, a *recovery.Flow) error {\n\treturn e.err(\"ExecuteRecoveryPreHook\", recovery.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecutePostRecoveryHook(w http.ResponseWriter, r *http.Request, a *recovery.Flow, s *session.Session) error {\n\treturn e.err(\"ExecutePostRecoveryHook\", recovery.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecuteVerificationPreHook(w http.ResponseWriter, r *http.Request, a *verification.Flow) error {\n\treturn e.err(\"ExecuteVerificationPreHook\", verification.ErrHookAbortFlow)\n}\n\nfunc (e Error) ExecutePostVerificationHook(w http.ResponseWriter, r *http.Request, a *verification.Flow, i *identity.Identity) error {\n\treturn e.err(\"ExecutePostVerificationHook\", verification.ErrHookAbortFlow)\n}\n"
  },
  {
    "path": "selfservice/hook/hooks.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook\n\nconst (\n\tKeySessionIssuer          = \"session\"\n\tKeySessionDestroyer       = \"revoke_active_sessions\"\n\tKeyWebHook                = \"web_hook\"\n\tKeyRequireVerifiedAddress = \"require_verified_address\"\n\tKeyVerificationUI         = \"show_verification_ui\"\n\tKeyVerifier               = \"verification\"\n)\n"
  },
  {
    "path": "selfservice/hook/hooktest/web_hook_test_server.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hooktest\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"slices\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/ioutilx\"\n)\n\nvar jsonnet = base64.StdEncoding.EncodeToString([]byte(\"function(ctx) ctx\"))\n\ntype Server struct {\n\t*httptest.Server\n\tLastBody []byte\n}\n\n// NewServer returns a new webhook server for testing.\nfunc NewServer() *Server {\n\ts := new(Server)\n\thttptestServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\ts.LastBody = ioutilx.MustReadAll(r.Body)\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\n\ts.Server = httptestServer\n\n\treturn s\n}\n\n// HookConfig returns the hook configuration for calling the webhook server.\nfunc (s *Server) HookConfig() config.SelfServiceHook {\n\treturn config.SelfServiceHook{\n\t\tName: \"web_hook\",\n\t\tConfig: []byte(fmt.Sprintf(`\n{\n\t\"method\": \"POST\",\n\t\"url\": \"%s\",\n\t\"body\": \"base64://%s\"\n}`, s.URL, jsonnet)),\n\t}\n}\n\nfunc (s *Server) AssertTransientPayload(t *testing.T, expected string) {\n\trequire.NotEmpty(t, s.LastBody)\n\tactual := gjson.GetBytes(s.LastBody, \"flow.transient_payload\").String()\n\tassert.JSONEq(t, expected, actual, \"%+v\", actual)\n}\n\n// SetConfig adds the webhook to the list of hooks for the given key and restores\n// the original configuration after the test.\nfunc (s *Server) SetConfig(t *testing.T, conf *configx.Provider, key string) {\n\tvar newValue []config.SelfServiceHook\n\toriginal := conf.Get(key)\n\tif originalHooks, ok := original.([]config.SelfServiceHook); ok {\n\t\tnewValue = slices.Clone(originalHooks)\n\t}\n\trequire.NoError(t, conf.Set(key, append(newValue, s.HookConfig())))\n\tt.Cleanup(func() {\n\t\t_ = conf.Set(key, original)\n\t})\n}\n"
  },
  {
    "path": "selfservice/hook/password_migration_hook.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/codes\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.11.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\tgrpccodes \"google.golang.org/grpc/codes\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/request\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype (\n\tPasswordMigration struct {\n\t\tdeps webHookDependencies\n\t\tconf *request.Config\n\t}\n\tPasswordMigrationRequest struct {\n\t\tIdentifier string             `json:\"identifier\"`\n\t\tPassword   string             `json:\"password\"`\n\t\tIdentity   *identity.Identity `json:\"-\"`\n\t}\n\tPasswordMigrationResponse struct {\n\t\tStatus string `json:\"status\"`\n\t}\n)\n\nfunc NewPasswordMigrationHook(deps webHookDependencies, conf *request.Config) *PasswordMigration {\n\treturn &PasswordMigration{deps: deps, conf: conf}\n}\n\nfunc (p *PasswordMigration) Execute(ctx context.Context, req *http.Request, flow flow.Flow, data *PasswordMigrationRequest) (err error) {\n\tvar (\n\t\thttpClient = p.deps.HTTPClient(ctx)\n\t\temitEvent  = p.conf.EmitAnalyticsEvent == nil || *p.conf.EmitAnalyticsEvent // default true\n\t\ttracer     = trace.SpanFromContext(ctx).TracerProvider().Tracer(\"kratos-webhooks\")\n\t)\n\n\tctx, span := tracer.Start(ctx, \"selfservice.login.password_migration\")\n\tdefer otelx.End(span, &err)\n\n\tif emitEvent {\n\t\tInstrumentHTTPClientForEvents(ctx, httpClient, x.NewUUID(), \"password_migration_hook\")\n\t}\n\tbuilder, err := request.NewBuilder(ctx, p.conf, p.deps)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\tvar whReq *retryablehttp.Request\n\tif p.conf.TemplateURI == \"\" {\n\t\twhReq, err = builder.BuildRequest(ctx, nil) // passing a nil body here skips Jsonnet\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\trawData, err := json.Marshal(data)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tif err = whReq.SetBody(rawData); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t} else {\n\t\ttype templateContextMerged struct {\n\t\t\ttemplateContext\n\t\t\tPassword   string `json:\"password\"`\n\t\t\tIdentifier string `json:\"identifier\"`\n\t\t}\n\t\twhReq, err = builder.BuildRequest(ctx, templateContextMerged{\n\t\t\ttemplateContext: templateContext{\n\t\t\t\tFlow:           flow,\n\t\t\t\tRequestHeaders: req.Header,\n\t\t\t\tRequestMethod:  req.Method,\n\t\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\t\tRequestCookies: cookies(req),\n\t\t\t\tIdentity:       data.Identity,\n\t\t\t},\n\t\t\tPassword:   data.Password,\n\t\t\tIdentifier: data.Identifier,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tp.deps.Logger().WithRequest(whReq.Request).Info(\"Dispatching password migration hook\")\n\twhReq = whReq.WithContext(ctx)\n\n\tresp, err := httpClient.Do(whReq)\n\tif err != nil {\n\t\treturn herodot.DefaultError{\n\t\t\tCodeField:     http.StatusBadGateway,\n\t\t\tStatusField:   http.StatusText(http.StatusBadGateway),\n\t\t\tGRPCCodeField: grpccodes.Aborted,\n\t\t\tReasonField:   \"A third-party upstream service could not be reached. Please try again later.\",\n\t\t\tErrorField:    \"calling the password migration hook failed\",\n\t\t}.WithWrap(errors.WithStack(err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\tspan.SetAttributes(semconv.HTTPAttributesFromHTTPStatusCode(resp.StatusCode)...)\n\n\tswitch resp.StatusCode {\n\tcase http.StatusOK:\n\t\t// We now check if the response matches `{\"status\": \"password_match\" }`.\n\t\tdec := json.NewDecoder(io.LimitReader(resp.Body, 1024)) // limit the response body to 1KB\n\t\tvar response PasswordMigrationResponse\n\t\tif err := dec.Decode(&response); err != nil || response.Status != \"password_match\" {\n\t\t\treturn errors.WithStack(schema.NewInvalidCredentialsError())\n\t\t}\n\t\treturn nil\n\n\tcase http.StatusForbidden:\n\t\treturn errors.WithStack(schema.NewInvalidCredentialsError())\n\tdefault:\n\t\tspan.SetStatus(codes.Error, \"Unexpected HTTP status code\")\n\t\treturn herodot.DefaultError{\n\t\t\tCodeField:     http.StatusBadGateway,\n\t\t\tStatusField:   http.StatusText(http.StatusBadGateway),\n\t\t\tGRPCCodeField: grpccodes.Aborted,\n\t\t\tReasonField:   \"A third-party upstream service responded improperly. Please try again later.\",\n\t\t\tErrorField:    fmt.Sprintf(\"password migration hook failed with status code %v\", resp.StatusCode),\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "selfservice/hook/require_verified_address.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/session\"\n)\n\nvar _ login.PostHookExecutor = new(AddressVerifier)\n\ntype (\n\taddressVerifierDependencies interface {\n\t\tconfig.Provider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tnosurfx.CSRFProvider\n\t\tverification.StrategyProvider\n\t\tverification.FlowPersistenceProvider\n\t\tidentity.PrivilegedPoolProvider\n\t\thttpx.WriterProvider\n\t\totelx.Provider\n\t}\n\tAddressVerifier struct {\n\t\tr addressVerifierDependencies\n\t}\n)\n\nfunc NewAddressVerifier(r addressVerifierDependencies) *AddressVerifier {\n\treturn &AddressVerifier{\n\t\tr: r,\n\t}\n}\n\nfunc (e *AddressVerifier) ExecuteLoginPostHook(w http.ResponseWriter, r *http.Request, _ node.UiNodeGroup, f *login.Flow, s *session.Session) (err error) {\n\tctx, span := e.r.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.hook.Verifier.do\")\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\t// TODO remove once flag is removed.\n\tif e.r.Config().UseLegacyRequireVerifiedLoginError(ctx) {\n\t\tif f.Active != identity.CredentialsTypePassword {\n\t\t\tspan.AddEvent(semconv.NewDeprecatedFeatureUsedEvent(ctx, \"legacy_require_verified_login_error\"))\n\t\t\treturn nil\n\t\t}\n\t}\n\t// END TODO\n\n\tif len(s.Identity.VerifiableAddresses) == 0 {\n\t\treturn errors.WithStack(herodot.ErrMisconfiguration.WithReason(\"A misconfiguration prevents login. Expected to find a verification address but this identity does not have one assigned.\"))\n\t}\n\n\tfor _, va := range s.Identity.VerifiableAddresses {\n\t\tif va.Verified {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tstrategies, primaryStrategy, err := e.r.GetActiveVerificationStrategies(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// TODO remove once flag is removed.\n\tif e.r.Config().UseLegacyRequireVerifiedLoginError(ctx) {\n\t\tspan.AddEvent(semconv.NewDeprecatedFeatureUsedEvent(ctx, \"legacy_require_verified_login_error\"))\n\t\treturn login.ErrAddressNotVerified\n\t}\n\t// END TODO\n\n\ti := s.Identity\n\tfor k := range i.VerifiableAddresses {\n\t\taddress := &i.VerifiableAddresses[k]\n\t\tif address.Value == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tverificationFlow, err := verification.NewPostHookFlow(e.r.Config(),\n\t\t\te.r.Config().SelfServiceFlowVerificationRequestLifespan(ctx),\n\t\t\te.r.GenerateCSRFToken(r), r, strategies, f)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tverificationFlow.State = flow.StateEmailSent\n\t\tfor _, strategy := range strategies {\n\t\t\tif ps, isPrimary := strategy.(verification.PrimaryStrategy); isPrimary {\n\t\t\t\tverificationFlow.Active = sqlxx.NullString(ps.NodeGroup())\n\t\t\t}\n\t\t\tif err := strategy.PopulateVerificationMethod(r, verificationFlow); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tverificationFlow.SessionID = uuid.NullUUID{UUID: s.ID, Valid: true}\n\t\tverificationFlow.IdentityID = uuid.NullUUID{UUID: s.Identity.ID, Valid: true}\n\n\t\tverificationFlow.UI.Nodes.Append(\n\t\t\tnode.NewInputField(address.Via, address.Value, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\t\tWithMetaLabel(text.NewInfoNodeResendOTP()),\n\t\t)\n\n\t\tif err := e.r.VerificationFlowPersister().CreateVerificationFlow(ctx, verificationFlow); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := primaryStrategy.SendVerificationCode(ctx, verificationFlow, i, address); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tflowURL := verificationFlow.AppendTo(e.r.Config().SelfServiceFlowVerificationUI(ctx)).String()\n\t\tcontinueWith := flow.NewContinueWithVerificationUI(verificationFlow.ID, address.Value, flowURL)\n\t\tf.AddContinueWith(continueWith)\n\n\t\tif x.IsJSONRequest(r) {\n\t\t\te.r.Writer().WriteErrorCode(w, r, http.StatusForbidden, flow.ErrorWithContinueWith(login.ErrAddressNotVerified, continueWith))\n\t\t\treturn errors.WithStack(login.ErrHookAbortFlow)\n\t\t}\n\n\t\tif x.IsBrowserRequest(r) {\n\t\t\thttp.Redirect(w, r, flowURL, http.StatusSeeOther)\n\t\t\treturn errors.WithStack(login.ErrHookAbortFlow)\n\t\t}\n\n\t\treturn errors.WithStack(login.ErrHookAbortFlow)\n\t}\n\n\treturn login.ErrAddressNotVerified\n}\n"
  },
  {
    "path": "selfservice/hook/require_verified_address_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestAddressVerifier(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.schema.json\")\n\tverifier := hook.NewAddressVerifier(reg)\n\n\tt.Run(\"legacy behavior\", func(t *testing.T) {\n\t\t// Mock legacy config\n\t\tconf.MustSet(context.Background(), config.ViperKeyUseLegacyRequireVerifiedLoginError, true)\n\n\t\tfor _, tc := range []struct {\n\t\t\tflow       *login.Flow\n\t\t\tneverError bool\n\t\t}{\n\t\t\t{&login.Flow{Active: identity.CredentialsTypePassword}, false},\n\t\t\t{&login.Flow{Active: identity.CredentialsTypeOIDC}, true},\n\t\t} {\n\t\t\tt.Run(tc.flow.Active.String()+\" flow\", func(t *testing.T) {\n\t\t\t\tfor _, uc := range []struct {\n\t\t\t\t\tname                string\n\t\t\t\t\tverifiableAddresses []identity.VerifiableAddress\n\t\t\t\t\texpectedError       error\n\t\t\t\t}{\n\t\t\t\t\t{\n\t\t\t\t\t\tname:                \"No Verification Address\",\n\t\t\t\t\t\tverifiableAddresses: []identity.VerifiableAddress{},\n\t\t\t\t\t\texpectedError:       herodot.ErrMisconfiguration.WithReason(\"A misconfiguration prevents login. Expected to find a verification address but this identity does not have one assigned.\"),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"Single Address Not Verified\",\n\t\t\t\t\t\tverifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t\t\t{ID: uuid.UUID{}, Verified: false},\n\t\t\t\t\t\t},\n\t\t\t\t\t\texpectedError: login.ErrAddressNotVerified,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"Single Address Verified\",\n\t\t\t\t\t\tverifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t\t\t{ID: uuid.UUID{}, Verified: true},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"Multiple Addresses Verified\",\n\t\t\t\t\t\tverifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t\t\t{ID: uuid.UUID{}, Verified: true},\n\t\t\t\t\t\t\t{ID: uuid.UUID{}, Verified: true},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"Multiple Addresses Not Verified\",\n\t\t\t\t\t\tverifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t\t\t{ID: uuid.UUID{}, Verified: false},\n\t\t\t\t\t\t\t{ID: uuid.UUID{}, Verified: false},\n\t\t\t\t\t\t},\n\t\t\t\t\t\texpectedError: login.ErrAddressNotVerified,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"One Address Verified And One Not\",\n\t\t\t\t\t\tverifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t\t\t{ID: uuid.UUID{}, Verified: true},\n\t\t\t\t\t\t\t{ID: uuid.UUID{}, Verified: false},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t} {\n\t\t\t\t\tt.Run(uc.name, func(t *testing.T) {\n\t\t\t\t\t\tsessions := &session.Session{\n\t\t\t\t\t\t\tID:       x.NewUUID(),\n\t\t\t\t\t\t\tIdentity: &identity.Identity{ID: x.NewUUID(), VerifiableAddresses: uc.verifiableAddresses},\n\t\t\t\t\t\t}\n\t\t\t\t\t\terr := verifier.ExecuteLoginPostHook(nil, httptest.NewRequest(\"GET\", \"http://example.com\", nil), node.DefaultGroup, tc.flow, sessions)\n\t\t\t\t\t\tif tc.neverError || uc.expectedError == nil {\n\t\t\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tassert.ErrorIs(t, err, uc.expectedError)\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\tt.Run(\"current behavior\", func(t *testing.T) {\n\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/require_verified.schema.json\")\n\t\t// Mock non-legacy config\n\t\tconf.MustSet(context.Background(), config.ViperKeyUseLegacyRequireVerifiedLoginError, false)\n\n\t\tmockRequest := httptest.NewRequest(\"GET\", \"http://example.com\", nil)\n\n\t\t// Setup flow with identity addresses needing verification\n\t\tloginFlow := &login.Flow{\n\t\t\tActive: identity.CredentialsTypePassword,\n\t\t\tID:     x.NewUUID(),\n\t\t}\n\n\t\t// Must persist the flow first for foreign key constraints\n\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), loginFlow))\n\t\tt.Run(\"json request for unverified address\", func(t *testing.T) {\n\t\t\t// Mock JSON request\n\t\t\tmockJSONReq := httptest.NewRequest(\"GET\", \"http://example.com\", nil)\n\t\t\tmockJSONReq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\tmockJSONReq.Header.Set(\"Accept\", \"application/json\")\n\t\t\tmockResponse := httptest.NewRecorder()\n\n\t\t\t// Create identity with valid values\n\t\t\tidentity := &identity.Identity{\n\t\t\t\tID:     x.NewUUID(),\n\t\t\t\tTraits: identity.Traits(`{\"email\":\"user@example.com\"}`),\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tID:       x.NewUUID(),\n\t\t\t\t\t\tValue:    \"user@example.com\",\n\t\t\t\t\t\tVerified: false,\n\t\t\t\t\t\tVia:      identity.AddressTypeEmail,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\t// Persist identity to satisfy foreign key constraints\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), identity))\n\n\t\t\tsessions := &session.Session{\n\t\t\t\tID:       x.NewUUID(),\n\t\t\t\tIdentity: identity,\n\t\t\t}\n\n\t\t\t// Expect verification flow creation and ErrHookAbortFlow\n\t\t\terr := verifier.ExecuteLoginPostHook(mockResponse, mockJSONReq, node.DefaultGroup, loginFlow, sessions)\n\t\t\tassert.ErrorIs(t, err, login.ErrHookAbortFlow)\n\n\t\t\t// Verify response contains continueWith and ErrAddressNotVerified\n\t\t\tresp := mockResponse.Result()\n\t\t\tbody, _ := io.ReadAll(resp.Body)\n\n\t\t\tvar responseErr *herodot.DefaultError\n\n\t\t\t// Check for required JSON fields\n\t\t\trequire.NoError(t, json.Unmarshal([]byte(gjson.GetBytes(body, \"error\").Raw), &responseErr))\n\t\t\tassert.Contains(t, responseErr.ReasonField, login.ErrAddressNotVerified.Reason(), \"%s\", string(body))\n\n\t\t\t// Verify flow has continueWith added\n\t\t\tvar continueWith flow.ContinueWithVerificationUI\n\t\t\trequire.NoError(t, json.Unmarshal([]byte(gjson.GetBytes(body, \"error.details.continue_with.0\").Raw), &continueWith))\n\n\t\t\t// Verify the continueWith fields are properly set\n\t\t\tassert.NotEmpty(t, continueWith.Flow.ID)\n\t\t\tassert.Contains(t, continueWith.Action, \"show_verification_ui\")\n\t\t\tassert.Contains(t, continueWith.Flow.URL, reg.Config().SelfServiceFlowVerificationUI(context.Background()).String())\n\t\t\tassert.Equal(t, continueWith.Flow.VerifiableAddress, \"user@example.com\")\n\t\t})\n\n\t\tt.Run(\"browser request for unverified address\", func(t *testing.T) {\n\t\t\t// Mock browser request\n\t\t\tmockBrowserReq := httptest.NewRequest(\"GET\", \"http://example.com\", nil)\n\t\t\tmockBrowserReq.Header.Set(\"Accept\", \"text/html\")\n\t\t\tmockResponse := httptest.NewRecorder()\n\n\t\t\t// Create new flow for this test case\n\t\t\tbrowserFlow := &login.Flow{\n\t\t\t\tActive: identity.CredentialsTypePassword,\n\t\t\t\tID:     x.NewUUID(),\n\t\t\t}\n\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), browserFlow))\n\n\t\t\t// Create identity with valid values\n\t\t\tidentity := &identity.Identity{\n\t\t\t\tID:     x.NewUUID(),\n\t\t\t\tTraits: identity.Traits(`{\"email\":\"user2@example.com\"}`),\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tID:       x.NewUUID(),\n\t\t\t\t\t\tValue:    \"user2@example.com\",\n\t\t\t\t\t\tVerified: false,\n\t\t\t\t\t\tVia:      identity.AddressTypeEmail,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), identity))\n\n\t\t\tsessions := &session.Session{\n\t\t\t\tID:       x.NewUUID(),\n\t\t\t\tIdentity: identity,\n\t\t\t}\n\n\t\t\t// Expect verification flow creation and redirect\n\t\t\terr := verifier.ExecuteLoginPostHook(mockResponse, mockBrowserReq, node.DefaultGroup, browserFlow, sessions)\n\t\t\tassert.ErrorIs(t, err, login.ErrHookAbortFlow)\n\n\t\t\t// Verify redirect occurred\n\t\t\tresp := mockResponse.Result()\n\t\t\tdefer func() { _ = resp.Body.Close() }()\n\t\t\tassert.Equal(t, http.StatusSeeOther, resp.StatusCode)\n\t\t\tassert.NotEmpty(t, resp.Header.Get(\"Location\"))\n\t\t})\n\n\t\tt.Run(\"verified address skips verification\", func(t *testing.T) {\n\t\t\t// Create new flow for this test case\n\t\t\tverifiedFlow := &login.Flow{\n\t\t\t\tActive: identity.CredentialsTypePassword,\n\t\t\t\tID:     x.NewUUID(),\n\t\t\t}\n\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), verifiedFlow))\n\n\t\t\t// Create identity with verified address\n\t\t\tidentity := &identity.Identity{\n\t\t\t\tID:     x.NewUUID(),\n\t\t\t\tTraits: identity.Traits(`{\"email\":\"verified@example.com\"}`),\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tID:       x.NewUUID(),\n\t\t\t\t\t\tValue:    \"verified@example.com\",\n\t\t\t\t\t\tVerified: true,\n\t\t\t\t\t\tVia:      identity.AddressTypeEmail,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), identity))\n\n\t\t\tsessions := &session.Session{\n\t\t\t\tID:       x.NewUUID(),\n\t\t\t\tIdentity: identity,\n\t\t\t}\n\n\t\t\terr := verifier.ExecuteLoginPostHook(nil, mockRequest, node.DefaultGroup, verifiedFlow, sessions)\n\t\t\tassert.NoError(t, err)\n\t\t})\n\n\t\tt.Run(\"no verifiable address\", func(t *testing.T) {\n\t\t\t// Create new flow for this test case\n\t\t\tnoAddressFlow := &login.Flow{\n\t\t\t\tActive: identity.CredentialsTypePassword,\n\t\t\t\tID:     x.NewUUID(),\n\t\t\t}\n\t\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(context.Background(), noAddressFlow))\n\n\t\t\t// Create identity with no verifiable addresses\n\t\t\tidentity := &identity.Identity{\n\t\t\t\tID:                  x.NewUUID(),\n\t\t\t\tTraits:              identity.Traits(`{}`),\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{},\n\t\t\t}\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), identity))\n\n\t\t\tsessions := &session.Session{\n\t\t\t\tID:       x.NewUUID(),\n\t\t\t\tIdentity: identity,\n\t\t\t}\n\n\t\t\terr := verifier.ExecuteLoginPostHook(nil, mockRequest, node.DefaultGroup, noAddressFlow, sessions)\n\t\t\tassert.ErrorIs(t, err, herodot.ErrMisconfiguration)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/hook/session_destroyer.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar _ login.PostHookExecutor = new(SessionDestroyer)\nvar _ recovery.PostHookExecutor = new(SessionDestroyer)\nvar _ settings.PostHookPostPersistExecutor = new(SessionDestroyer)\n\ntype (\n\tsessionDestroyerDependencies interface {\n\t\tsession.ManagementProvider\n\t\tsession.PersistenceProvider\n\t}\n\tSessionDestroyer struct {\n\t\tr sessionDestroyerDependencies\n\t}\n)\n\nfunc NewSessionDestroyer(r sessionDestroyerDependencies) *SessionDestroyer {\n\treturn &SessionDestroyer{r: r}\n}\n\nfunc (e *SessionDestroyer) ExecuteLoginPostHook(_ http.ResponseWriter, r *http.Request, _ node.UiNodeGroup, _ *login.Flow, s *session.Session) error {\n\treturn otelx.WithSpan(r.Context(), \"selfservice.hook.SessionDestroyer.ExecuteLoginPostHook\", func(ctx context.Context) error {\n\t\tif _, err := e.r.SessionPersister().RevokeSessionsIdentityExcept(ctx, s.Identity.ID, s.ID); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc (e *SessionDestroyer) ExecutePostRecoveryHook(_ http.ResponseWriter, r *http.Request, _ *recovery.Flow, s *session.Session) error {\n\treturn otelx.WithSpan(r.Context(), \"selfservice.hook.SessionDestroyer.ExecutePostRecoveryHook\", func(ctx context.Context) error {\n\t\tif _, err := e.r.SessionPersister().RevokeSessionsIdentityExcept(ctx, s.Identity.ID, s.ID); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc (e *SessionDestroyer) ExecuteSettingsPostPersistHook(_ http.ResponseWriter, r *http.Request, _ *settings.Flow, i *identity.Identity, s *session.Session) error {\n\treturn otelx.WithSpan(r.Context(), \"selfservice.hook.SessionDestroyer.ExecuteSettingsPostPersistHook\", func(ctx context.Context) error {\n\t\tif _, err := e.r.SessionPersister().RevokeSessionsIdentityExcept(ctx, i.ID, s.ID); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "selfservice/hook/session_destroyer_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/corpx\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gobuffalo/httptest\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/session\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc TestSessionDestroyer(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, \"http://localhost/\")\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/stub.schema.json\")\n\n\th := hook.NewSessionDestroyer(reg)\n\n\tfor _, tc := range []struct {\n\t\tname string\n\t\thook func(*identity.Identity) error\n\t}{\n\t\t{\n\t\t\tname: \"ExecuteLoginPostHook\",\n\t\t\thook: func(i *identity.Identity) error {\n\t\t\t\treturn h.ExecuteLoginPostHook(\n\t\t\t\t\thttptest.NewRecorder(),\n\t\t\t\t\tnew(http.Request),\n\t\t\t\t\tnode.DefaultGroup,\n\t\t\t\t\tnil,\n\t\t\t\t\t&session.Session{Identity: i},\n\t\t\t\t)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ExecutePostRecoveryHook\",\n\t\t\thook: func(i *identity.Identity) error {\n\t\t\t\treturn h.ExecutePostRecoveryHook(\n\t\t\t\t\thttptest.NewRecorder(),\n\t\t\t\t\tnew(http.Request),\n\t\t\t\t\tnil,\n\t\t\t\t\t&session.Session{Identity: i},\n\t\t\t\t)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ExecuteSettingsPostPersistHook\",\n\t\t\thook: func(i *identity.Identity) error {\n\t\t\t\treturn h.ExecuteSettingsPostPersistHook(\n\t\t\t\t\thttptest.NewRecorder(),\n\t\t\t\t\tnew(http.Request),\n\t\t\t\t\tnil,\n\t\t\t\t\ti,\n\t\t\t\t\t&session.Session{Identity: i},\n\t\t\t\t)\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tvar i identity.Identity\n\t\t\trequire.NoError(t, faker.FakeData(&i))\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &i))\n\n\t\t\tsessions := make([]session.Session, 5)\n\t\t\tfor k := range sessions {\n\t\t\t\ts := sessions[k] // keep this for pointers' sake ;)\n\t\t\t\trequire.NoError(t, faker.FakeData(&s))\n\t\t\t\ts.IdentityID = uuid.Nil\n\t\t\t\ts.Identity = &i\n\t\t\t\ts.Active = true\n\n\t\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), &s))\n\t\t\t\tsessions[k] = s\n\t\t\t}\n\n\t\t\tfor k := range sessions {\n\t\t\t\tsess, err := reg.SessionPersister().GetSession(context.Background(), sessions[k].ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.True(t, sess.IsActive())\n\t\t\t}\n\n\t\t\t// Should revoke all the sessions.\n\t\t\trequire.NoError(t, tc.hook(&i))\n\n\t\t\tfor k := range sessions {\n\t\t\t\tsess, err := reg.SessionPersister().GetSession(context.Background(), sessions[k].ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.False(t, sess.IsActive())\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/hook/session_issuer.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar _ registration.PostHookPostPersistExecutor = new(SessionIssuer)\n\ntype (\n\tsessionIssuerDependencies interface {\n\t\tsession.ManagementProvider\n\t\tsession.PersistenceProvider\n\t\tsessiontokenexchange.PersistenceProvider\n\t\tconfig.Provider\n\t\thttpx.WriterProvider\n\t\thydra.Provider\n\t}\n\tSessionIssuerProvider interface {\n\t\tHookSessionIssuer() *SessionIssuer\n\t}\n\tSessionIssuer struct {\n\t\tr sessionIssuerDependencies\n\t}\n)\n\nfunc NewSessionIssuer(r sessionIssuerDependencies) *SessionIssuer {\n\treturn &SessionIssuer{r: r}\n}\n\nfunc (e *SessionIssuer) ExecutePostRegistrationPostPersistHook(w http.ResponseWriter, r *http.Request, a *registration.Flow, s *session.Session) error {\n\treturn otelx.WithSpan(r.Context(), \"selfservice.hook.SessionIssuer.ExecutePostRegistrationPostPersistHook\", func(ctx context.Context) error {\n\t\treturn e.executePostRegistrationPostPersistHook(w, r.WithContext(ctx), a, s)\n\t})\n}\n\nfunc (e *SessionIssuer) executePostRegistrationPostPersistHook(w http.ResponseWriter, r *http.Request, a *registration.Flow, s *session.Session) error {\n\tif a.Type == flow.TypeAPI {\n\t\t// We don't want to redirect with the code, if the flow was submitted with an ID token.\n\t\t// This is the case for Sign in with native Apple SDK or Google SDK.\n\t\tif s.AuthenticatedVia(identity.CredentialsTypeOIDC) && a.IDToken == \"\" {\n\t\t\tif handled, err := e.r.SessionManager().MaybeRedirectAPICodeFlow(w, r, a, s.ID, node.OpenIDConnectGroup); err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t} else if handled {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\ta.AddContinueWith(flow.NewContinueWithSetToken(s.Token))\n\t\te.r.Writer().Write(w, r, &registration.APIFlowResponse{\n\t\t\tSession:      s,\n\t\t\tToken:        s.Token,\n\t\t\tIdentity:     s.Identity,\n\t\t\tContinueWith: a.ContinueWithItems,\n\t\t})\n\n\t\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewLoginSucceeded(r.Context(), &events.LoginSucceededOpts{\n\t\t\tSessionID:  s.ID,\n\t\t\tIdentityID: s.Identity.ID,\n\t\t\tFlowID:     a.ID,\n\t\t\tFlowType:   string(a.Type),\n\t\t\tMethod:     a.Active.String(),\n\t\t}))\n\n\t\treturn errors.WithStack(registration.ErrHookAbortFlow)\n\t}\n\n\t// cookie is issued both for browser and for SPA flows\n\tif err := e.r.SessionManager().IssueCookie(r.Context(), w, r, s); err != nil {\n\t\treturn err\n\t}\n\n\ttrace.SpanFromContext(r.Context()).AddEvent(events.NewLoginSucceeded(r.Context(), &events.LoginSucceededOpts{\n\t\tSessionID:  s.ID,\n\t\tIdentityID: s.Identity.ID,\n\t\tFlowID:     a.ID,\n\t\tFlowType:   string(a.Type),\n\t\tMethod:     a.Active.String(),\n\t}))\n\n\t// SPA flows additionally send the session\n\tif x.IsJSONRequest(r) {\n\t\tif err := e.acceptLoginChallenge(r.Context(), a, s, s.Identity); err != nil {\n\t\t\treturn err\n\t\t}\n\t\te.r.Writer().Write(w, r, &registration.APIFlowResponse{\n\t\t\tSession:      s,\n\t\t\tIdentity:     s.Identity,\n\t\t\tContinueWith: a.ContinueWithItems,\n\t\t})\n\t\treturn errors.WithStack(registration.ErrHookAbortFlow)\n\t}\n\n\treturn nil\n}\n\nfunc (e *SessionIssuer) acceptLoginChallenge(ctx context.Context, registrationFlow *registration.Flow, s *session.Session, i *identity.Identity) error {\n\t// If Kratos is used as a Hydra login provider, we need to redirect back to Hydra by using the continue_with items\n\t// with the post login challenge URL as the body.\n\t// We only do this if the flow did not create a verification flow (e.g. verification is disabled or not active due to it being a code flow).\n\t// Since the session issuer hook must be the last hook in the flow, we can safely assume that the verification flow was already added (if it was)\n\tif registrationFlow.OAuth2LoginChallenge != \"\" && !willVerificationFollow(registrationFlow) {\n\t\tpostChallengeURL, err := e.r.Hydra().AcceptLoginRequest(ctx,\n\t\t\thydra.AcceptLoginRequestParams{\n\t\t\t\tLoginChallenge:        string(registrationFlow.OAuth2LoginChallenge),\n\t\t\t\tIdentityID:            i.ID.String(),\n\t\t\t\tSessionID:             s.ID.String(),\n\t\t\t\tAuthenticationMethods: s.AMR,\n\t\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcw := []flow.ContinueWith{}\n\t\tfor _, i := range registrationFlow.ContinueWithItems {\n\t\t\t// Filter any continueWithRedirectBrowserTo items out of the list\n\t\t\t// We will add a new one at the end of the flow\n\t\t\t// as the OAuth2 login challenge should be the last step in the flow\n\t\t\tif i.GetAction() != string(flow.ContinueWithActionRedirectBrowserToString) {\n\t\t\t\tcw = append(cw, i)\n\t\t\t}\n\t\t}\n\t\tregistrationFlow.ContinueWithItems = append(cw, flow.NewContinueWithRedirectBrowserTo(postChallengeURL))\n\t}\n\treturn nil\n}\n\n// willVerificationFollow returns true if the flow's continue with items contain a verification UI.\nfunc willVerificationFollow(f *registration.Flow) bool {\n\tfor _, i := range f.ContinueWithItems {\n\t\tif i.GetAction() == string(flow.ContinueWithActionShowVerificationUIString) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "selfservice/hook/session_issuer_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/randx\"\n)\n\nfunc TestSessionIssuer(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\treg.SetHydra(hydra.NewFake())\n\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, \"http://localhost/\")\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/stub.schema.json\")\n\n\tvar r http.Request\n\th := hook.NewSessionIssuer(reg)\n\n\tt.Run(\"method=sign-up\", func(t *testing.T) {\n\t\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\t\t\ts := testhelpers.CreateSession(t, reg)\n\t\t\tf := &registration.Flow{Type: flow.TypeBrowser}\n\n\t\t\trequire.NoError(t, h.ExecutePostRegistrationPostPersistHook(w, &r,\n\t\t\t\tf, &session.Session{ID: s.ID, Identity: s.Identity, Token: randx.MustString(12, randx.AlphaLowerNum)}))\n\n\t\t\trequire.Empty(t, f.ContinueWithItems)\n\n\t\t\tgot, err := reg.SessionPersister().GetSession(context.Background(), s.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, s.ID, got.ID)\n\t\t\tassert.True(t, got.AuthenticatedAt.After(time.Now().Add(-time.Minute)))\n\n\t\t\tassert.Contains(t, w.Header().Get(\"Set-Cookie\"), config.DefaultSessionCookieName)\n\t\t})\n\n\t\tt.Run(\"flow=api\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\n\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\ts := &session.Session{\n\t\t\t\tID:              x.NewUUID(),\n\t\t\t\tIdentity:        i,\n\t\t\t\tToken:           randx.MustString(12, randx.AlphaLowerNum),\n\t\t\t\tLogoutToken:     randx.MustString(12, randx.AlphaLowerNum),\n\t\t\t\tAuthenticatedAt: time.Now().UTC(),\n\t\t\t}\n\t\t\tf := &registration.Flow{Type: flow.TypeAPI}\n\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, s))\n\n\t\t\terr := h.ExecutePostRegistrationPostPersistHook(w, &http.Request{Header: http.Header{\"Accept\": {\"application/json\"}}}, f, s)\n\t\t\trequire.ErrorIs(t, err, registration.ErrHookAbortFlow, \"%+v\", err)\n\t\t\trequire.Len(t, f.ContinueWithItems, 1)\n\n\t\t\tst := f.ContinueWithItems[0]\n\t\t\trequire.IsType(t, &flow.ContinueWithSetOrySessionToken{}, st)\n\t\t\tassert.NotEmpty(t, st.(*flow.ContinueWithSetOrySessionToken).OrySessionToken)\n\n\t\t\tgot, err := reg.SessionPersister().GetSession(context.Background(), s.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, s.ID.String(), got.ID.String())\n\t\t\tassert.True(t, got.AuthenticatedAt.After(time.Now().Add(-time.Minute)))\n\n\t\t\tassert.Empty(t, w.Header().Get(\"Set-Cookie\"))\n\t\t\tbody := w.Body.Bytes()\n\t\t\tassert.Equal(t, i.ID.String(), gjson.GetBytes(body, \"identity.id\").String())\n\t\t\tassert.Equal(t, s.ID.String(), gjson.GetBytes(body, \"session.id\").String())\n\t\t\tassert.Equal(t, got.Token, gjson.GetBytes(body, \"session_token\").String())\n\t\t})\n\n\t\tt.Run(\"flow=spa\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\n\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\ts := &session.Session{\n\t\t\t\tID:              x.NewUUID(),\n\t\t\t\tIdentity:        i,\n\t\t\t\tToken:           randx.MustString(12, randx.AlphaLowerNum),\n\t\t\t\tLogoutToken:     randx.MustString(12, randx.AlphaLowerNum),\n\t\t\t\tAuthenticatedAt: time.Now().UTC(),\n\t\t\t}\n\t\t\tf := &registration.Flow{Type: flow.TypeBrowser}\n\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, s))\n\n\t\t\terr := h.ExecutePostRegistrationPostPersistHook(w, &http.Request{Header: http.Header{\"Accept\": {\"application/json\"}}}, f, s)\n\t\t\trequire.ErrorIs(t, err, registration.ErrHookAbortFlow, \"%+v\", err)\n\t\t\trequire.Empty(t, f.ContinueWithItems)\n\n\t\t\tgot, err := reg.SessionPersister().GetSession(context.Background(), s.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, s.ID.String(), got.ID.String())\n\t\t\tassert.True(t, got.AuthenticatedAt.After(time.Now().Add(-time.Minute)))\n\n\t\t\tassert.NotEmpty(t, w.Header().Get(\"Set-Cookie\"))\n\t\t\tbody := w.Body.Bytes()\n\t\t\tassert.Equal(t, i.ID.String(), gjson.GetBytes(body, \"identity.id\").String())\n\t\t\tassert.Equal(t, s.ID.String(), gjson.GetBytes(body, \"session.id\").String())\n\t\t\tassert.Empty(t, gjson.GetBytes(body, \"session_token\").String())\n\t\t})\n\t})\n\n\tt.Run(\"method=sign-up with oauth2\", func(t *testing.T) {\n\t\tt.Run(\"flow=browser\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\t\t\ts := testhelpers.CreateSession(t, reg)\n\t\t\tf := &registration.Flow{\n\t\t\t\tType:                 flow.TypeBrowser,\n\t\t\t\tOAuth2LoginChallenge: hydra.FakeValidLoginChallenge,\n\t\t\t}\n\n\t\t\trequire.NoError(t, h.ExecutePostRegistrationPostPersistHook(w, &r,\n\t\t\t\tf, &session.Session{ID: s.ID, Identity: s.Identity, Token: randx.MustString(12, randx.AlphaLowerNum)}))\n\n\t\t\trequire.Empty(t, f.ContinueWithItems)\n\n\t\t\tgot, err := reg.SessionPersister().GetSession(context.Background(), s.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, s.ID, got.ID)\n\t\t\tassert.True(t, got.AuthenticatedAt.After(time.Now().Add(-time.Minute)))\n\n\t\t\tassert.Contains(t, w.Header().Get(\"Set-Cookie\"), config.DefaultSessionCookieName)\n\t\t})\n\n\t\tt.Run(\"flow=api\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\n\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\ts := &session.Session{\n\t\t\t\tID:              x.NewUUID(),\n\t\t\t\tIdentity:        i,\n\t\t\t\tToken:           randx.MustString(12, randx.AlphaLowerNum),\n\t\t\t\tLogoutToken:     randx.MustString(12, randx.AlphaLowerNum),\n\t\t\t\tAuthenticatedAt: time.Now().UTC(),\n\t\t\t}\n\t\t\tf := &registration.Flow{\n\t\t\t\tType:                 flow.TypeAPI,\n\t\t\t\tOAuth2LoginChallenge: hydra.FakeValidLoginChallenge,\n\t\t\t}\n\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, s))\n\n\t\t\terr := h.ExecutePostRegistrationPostPersistHook(w, &http.Request{Header: http.Header{\"Accept\": {\"application/json\"}}}, f, s)\n\t\t\trequire.ErrorIs(t, err, registration.ErrHookAbortFlow, \"%+v\", err)\n\t\t\trequire.Len(t, f.ContinueWithItems, 1)\n\n\t\t\tst := f.ContinueWithItems[0]\n\t\t\trequire.IsType(t, &flow.ContinueWithSetOrySessionToken{}, st)\n\t\t\tassert.NotEmpty(t, st.(*flow.ContinueWithSetOrySessionToken).OrySessionToken)\n\n\t\t\tgot, err := reg.SessionPersister().GetSession(context.Background(), s.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, s.ID.String(), got.ID.String())\n\t\t\tassert.True(t, got.AuthenticatedAt.After(time.Now().Add(-time.Minute)))\n\n\t\t\tassert.Empty(t, w.Header().Get(\"Set-Cookie\"))\n\t\t\tbody := w.Body.Bytes()\n\t\t\tassert.Equal(t, i.ID.String(), gjson.GetBytes(body, \"identity.id\").String())\n\t\t\tassert.Equal(t, s.ID.String(), gjson.GetBytes(body, \"session.id\").String())\n\t\t\tassert.Equal(t, got.Token, gjson.GetBytes(body, \"session_token\").String())\n\t\t})\n\n\t\tt.Run(\"flow=spa\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\n\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\ts := &session.Session{\n\t\t\t\tID:              x.NewUUID(),\n\t\t\t\tIdentity:        i,\n\t\t\t\tToken:           randx.MustString(12, randx.AlphaLowerNum),\n\t\t\t\tLogoutToken:     randx.MustString(12, randx.AlphaLowerNum),\n\t\t\t\tAuthenticatedAt: time.Now().UTC(),\n\t\t\t}\n\t\t\tf := &registration.Flow{\n\t\t\t\tType:                 flow.TypeBrowser,\n\t\t\t\tOAuth2LoginChallenge: hydra.FakeValidLoginChallenge,\n\t\t\t\tContinueWithItems:    []flow.ContinueWith{flow.NewContinueWithRedirectBrowserTo(\"https://ory.sh\")},\n\t\t\t}\n\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, s))\n\n\t\t\terr := h.ExecutePostRegistrationPostPersistHook(w, &http.Request{Header: http.Header{\"Accept\": {\"application/json\"}}}, f, s)\n\t\t\trequire.ErrorIs(t, err, registration.ErrHookAbortFlow, \"%+v\", err)\n\t\t\trequire.Len(t, f.ContinueWithItems, 1)\n\t\t\trequire.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, f.ContinueWithItems[0].GetAction())\n\t\t\trequire.EqualValues(t, hydra.FakePostLoginURL, f.ContinueWithItems[0].(*flow.ContinueWithRedirectBrowserTo).RedirectTo)\n\n\t\t\tgot, err := reg.SessionPersister().GetSession(context.Background(), s.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, s.ID.String(), got.ID.String())\n\t\t\tassert.True(t, got.AuthenticatedAt.After(time.Now().Add(-time.Minute)))\n\n\t\t\tassert.NotEmpty(t, w.Header().Get(\"Set-Cookie\"))\n\t\t\tbody := w.Body.Bytes()\n\t\t\tassert.Equal(t, i.ID.String(), gjson.GetBytes(body, \"identity.id\").String())\n\t\t\tassert.Equal(t, s.ID.String(), gjson.GetBytes(body, \"session.id\").String())\n\t\t\tassert.Empty(t, gjson.GetBytes(body, \"session_token\").String())\n\t\t})\n\t})\n\n\tt.Run(\"method=sign-up with oauth2 and verification enabled\", func(t *testing.T) {\n\t\tw := httptest.NewRecorder()\n\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\ts := &session.Session{\n\t\t\tID:              x.NewUUID(),\n\t\t\tIdentity:        i,\n\t\t\tToken:           randx.MustString(12, randx.AlphaLowerNum),\n\t\t\tLogoutToken:     randx.MustString(12, randx.AlphaLowerNum),\n\t\t\tAuthenticatedAt: time.Now().UTC(),\n\t\t}\n\t\tvf := &verification.Flow{\n\t\t\tID: x.NewUUID(),\n\t\t}\n\t\tf := &registration.Flow{\n\t\t\tType:                 flow.TypeBrowser,\n\t\t\tOAuth2LoginChallenge: hydra.FakeValidLoginChallenge,\n\t\t\tContinueWithItems:    []flow.ContinueWith{flow.NewContinueWithVerificationUI(vf.ID, \"some@ory.sh\", \"\")},\n\t\t}\n\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, s))\n\n\t\terr := h.ExecutePostRegistrationPostPersistHook(w, &http.Request{Header: http.Header{\"Accept\": {\"application/json\"}}}, f, s)\n\t\trequire.ErrorIs(t, err, registration.ErrHookAbortFlow, \"%+v\", err)\n\t\trequire.Len(t, f.ContinueWithItems, 1)\n\t\trequire.EqualValues(t, flow.ContinueWithActionShowVerificationUIString, f.ContinueWithItems[0].GetAction())\n\n\t\tgot, err := reg.SessionPersister().GetSession(context.Background(), s.ID, session.ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, s.ID.String(), got.ID.String())\n\t\tassert.True(t, got.AuthenticatedAt.After(time.Now().Add(-time.Minute)))\n\n\t\tassert.NotEmpty(t, w.Header().Get(\"Set-Cookie\"))\n\t\tbody := w.Body.Bytes()\n\t\tassert.Equal(t, i.ID.String(), gjson.GetBytes(body, \"identity.id\").String())\n\t\tassert.Equal(t, s.ID.String(), gjson.GetBytes(body, \"session.id\").String())\n\t\tassert.Empty(t, gjson.GetBytes(body, \"session_token\").String())\n\t})\n\n\tt.Run(\"method=sign-up with oauth2 and some other continue with item\", func(t *testing.T) {\n\t\tw := httptest.NewRecorder()\n\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\ts := &session.Session{\n\t\t\tID:              x.NewUUID(),\n\t\t\tIdentity:        i,\n\t\t\tToken:           randx.MustString(12, randx.AlphaLowerNum),\n\t\t\tLogoutToken:     randx.MustString(12, randx.AlphaLowerNum),\n\t\t\tAuthenticatedAt: time.Now().UTC(),\n\t\t}\n\t\tvf := &recovery.Flow{\n\t\t\tID: x.NewUUID(),\n\t\t}\n\t\tf := &registration.Flow{\n\t\t\tType:                 flow.TypeBrowser,\n\t\t\tOAuth2LoginChallenge: hydra.FakeValidLoginChallenge,\n\t\t\tContinueWithItems:    []flow.ContinueWith{flow.NewContinueWithRecoveryUI(vf)},\n\t\t}\n\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, s))\n\n\t\terr := h.ExecutePostRegistrationPostPersistHook(w, &http.Request{Header: http.Header{\"Accept\": {\"application/json\"}}}, f, s)\n\t\trequire.ErrorIs(t, err, registration.ErrHookAbortFlow, \"%+v\", err)\n\t\trequire.Len(t, f.ContinueWithItems, 2)\n\t\trequire.EqualValues(t, flow.ContinueWithActionShowRecoveryUIString, f.ContinueWithItems[0].GetAction())\n\t\trequire.EqualValues(t, hydra.FakePostLoginURL, f.ContinueWithItems[1].(*flow.ContinueWithRedirectBrowserTo).RedirectTo)\n\n\t\tgot, err := reg.SessionPersister().GetSession(context.Background(), s.ID, session.ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, s.ID.String(), got.ID.String())\n\t\tassert.True(t, got.AuthenticatedAt.After(time.Now().Add(-time.Minute)))\n\n\t\tassert.NotEmpty(t, w.Header().Get(\"Set-Cookie\"))\n\t\tbody := w.Body.Bytes()\n\t\tassert.Equal(t, i.ID.String(), gjson.GetBytes(body, \"identity.id\").String())\n\t\tassert.Equal(t, s.ID.String(), gjson.GetBytes(body, \"session.id\").String())\n\t\tassert.Empty(t, gjson.GetBytes(body, \"session_token\").String())\n\t})\n}\n"
  },
  {
    "path": "selfservice/hook/show_verification_ui.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar (\n\t_ registration.PostHookPostPersistExecutor = new(ShowVerificationUIHook)\n\t_ login.PostHookExecutor                   = new(ShowVerificationUIHook)\n\t_ settings.PostHookPostPersistExecutor     = new(ShowVerificationUIHook)\n)\n\ntype (\n\tshowVerificationUIDependencies interface {\n\t\thttpx.WriterProvider\n\t\tconfig.Provider\n\t\totelx.Provider\n\t\tlogrusx.Provider\n\t}\n\n\tShowVerfificationUIProvider interface {\n\t\tHookShowVerificationUI() *ShowVerificationUIHook\n\t}\n\n\t// ShowVerificationUIHook is a post registration hook that redirects browser clients to the verification UI.\n\tShowVerificationUIHook struct {\n\t\td showVerificationUIDependencies\n\t}\n)\n\nfunc NewShowVerificationUIHook(d showVerificationUIDependencies) *ShowVerificationUIHook {\n\treturn &ShowVerificationUIHook{d: d}\n}\n\n// ExecutePostRegistrationPostPersistHook adds redirect headers and status code if the request is a browser request.\n// If the request is not a browser request, this hook does nothing.\nfunc (e *ShowVerificationUIHook) ExecutePostRegistrationPostPersistHook(_ http.ResponseWriter, r *http.Request, f *registration.Flow, _ *session.Session) error {\n\treturn e.execute(r, f)\n}\n\n// ExecuteLoginPostHook adds redirect headers and status code if the request is a browser request.\n// If the request is not a browser request, this hook does nothing.\nfunc (e *ShowVerificationUIHook) ExecuteLoginPostHook(_ http.ResponseWriter, r *http.Request, _ node.UiNodeGroup, f *login.Flow, _ *session.Session) error {\n\treturn e.execute(r, f)\n}\n\nfunc (e *ShowVerificationUIHook) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, f *settings.Flow, id *identity.Identity, s *session.Session) error {\n\treturn e.execute(r, f)\n}\n\ntype verificationUIFlow interface {\n\tflow.InternalContexter\n\tflow.FlowWithContinueWith\n}\n\ntype loginOrRegistrationFlow interface {\n\tSetReturnToVerification(string)\n}\n\nfunc (e *ShowVerificationUIHook) execute(r *http.Request, f verificationUIFlow) (err error) {\n\tctx, span := e.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.hook.ShowVerificationUIHook.Do\")\n\tdefer otelx.End(span, &err)\n\n\tvar cw flow.ContinueWithVerificationUIFlow\n\n\tverificationFlow := gjson.GetBytes(f.GetInternalContext(), InternalContextRegistrationVerificationFlow).Raw\n\tif verificationFlow == \"\" {\n\t\t// TODO This is a fallback for flows that do not have the appropriate internalContext yet.\n\t\t// Remove this once we have released this change.\n\t\tfor _, c := range f.ContinueWith() {\n\t\t\tif item, ok := c.(*flow.ContinueWithVerificationUI); ok {\n\t\t\t\tcw = item.Flow\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif err := json.Unmarshal([]byte(verificationFlow), &cw); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif cw.ID == uuid.Nil {\n\t\te.d.Logger().WithRequest(r).Warn(\"Ignoring hook `show_verification_ui` because no verification flow ID was found in the registration flow. Cannot show verification UI. This is likely a configuration issue or a bug.\")\n\t\treturn nil\n\t}\n\n\tvf := flow.NewContinueWithVerificationUI(cw.ID, cw.VerifiableAddress, cw.URL)\n\tf.AddContinueWith(vf)\n\n\tif returnToVerification, ok := f.(loginOrRegistrationFlow); ok && x.IsBrowserRequest(r) {\n\t\tverificationUI := e.d.Config().SelfServiceFlowVerificationUI(ctx)\n\t\treturnToVerification.SetReturnToVerification(vf.AppendTo(verificationUI).String())\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/hook/show_verification_ui_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n)\n\nfunc TestExecutePostRegistrationPostPersistHook(t *testing.T) {\n\tt.Run(\"flow=registration\", func(t *testing.T) {\n\t\tt.Run(\"case=no continue with items returns 200 OK\", func(t *testing.T) {\n\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\tf := &registration.Flow{}\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecutePostRegistrationPostPersistHook(rec, browserRequest, f, nil))\n\t\t\trequire.Equal(t, 200, rec.Code)\n\t\t})\n\n\t\tt.Run(\"case=not a browser request returns 200 OK\", func(t *testing.T) {\n\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\tbrowserRequest.Header.Add(\"Accept\", \"application/json\")\n\t\t\tf := &registration.Flow{}\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecutePostRegistrationPostPersistHook(rec, browserRequest, f, nil))\n\t\t\trequire.Equal(t, 200, rec.Code)\n\t\t})\n\n\t\tt.Run(\"case=verification ui in continue with item returns redirect\", func(t *testing.T) {\n\t\t\tconf, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceVerificationUI, \"/verification\")\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\tvf := &verification.Flow{\n\t\t\t\tID: uuid.Must(uuid.NewV4()),\n\t\t\t}\n\t\t\trf := &registration.Flow{}\n\t\t\trf.ContinueWithItems = []flow.ContinueWith{\n\t\t\t\tflow.NewContinueWithVerificationUI(vf.ID, \"some@ory.sh\", \"\"),\n\t\t\t}\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecutePostRegistrationPostPersistHook(rec, browserRequest, rf, nil))\n\t\t\tassert.Equal(t, 200, rec.Code)\n\t\t\tassert.Equal(t, \"/verification?flow=\"+vf.ID.String(), rf.ReturnToVerification)\n\t\t})\n\n\t\tt.Run(\"case=no verification ui in continue with item returns 200 OK\", func(t *testing.T) {\n\t\t\tconf, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceVerificationUI, \"/verification\")\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\trf := &registration.Flow{}\n\t\t\trf.ContinueWithItems = []flow.ContinueWith{\n\t\t\t\tflow.NewContinueWithSetToken(\"token\"),\n\t\t\t}\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecutePostRegistrationPostPersistHook(rec, browserRequest, rf, nil))\n\t\t\tassert.Equal(t, 200, rec.Code)\n\t\t})\n\t})\n\n\tt.Run(\"flow=login\", func(t *testing.T) {\n\t\tt.Run(\"case=no continue with items returns 200 OK\", func(t *testing.T) {\n\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\tf := &login.Flow{}\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecuteLoginPostHook(rec, browserRequest, \"\", f, nil))\n\t\t\trequire.Equal(t, 200, rec.Code)\n\t\t})\n\n\t\tt.Run(\"case=not a browser request returns 200 OK\", func(t *testing.T) {\n\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\tbrowserRequest.Header.Add(\"Accept\", \"application/json\")\n\t\t\tf := &login.Flow{}\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecuteLoginPostHook(rec, browserRequest, \"\", f, nil))\n\t\t\trequire.Equal(t, 200, rec.Code)\n\t\t})\n\n\t\tt.Run(\"case=verification ui in continue with item returns redirect\", func(t *testing.T) {\n\t\t\tconf, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceVerificationUI, \"/verification\")\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\tvf := &verification.Flow{\n\t\t\t\tID: uuid.Must(uuid.NewV4()),\n\t\t\t}\n\t\t\trf := &login.Flow{}\n\t\t\trf.ContinueWithItems = []flow.ContinueWith{\n\t\t\t\tflow.NewContinueWithVerificationUI(vf.ID, \"some@ory.sh\", \"\"),\n\t\t\t}\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecuteLoginPostHook(rec, browserRequest, \"\", rf, nil))\n\t\t\tassert.Equal(t, 200, rec.Code)\n\t\t\tassert.Equal(t, \"/verification?flow=\"+vf.ID.String(), rf.ReturnToVerification)\n\t\t})\n\n\t\tt.Run(\"case=no verification ui in continue with item returns 200 OK\", func(t *testing.T) {\n\t\t\tconf, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceVerificationUI, \"/verification\")\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\trf := &login.Flow{}\n\t\t\trf.ContinueWithItems = []flow.ContinueWith{\n\t\t\t\tflow.NewContinueWithSetToken(\"token\"),\n\t\t\t}\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecuteLoginPostHook(rec, browserRequest, \"\", rf, nil))\n\t\t\tassert.Equal(t, 200, rec.Code)\n\t\t})\n\t})\n\n\tt.Run(\"internal_context=registration\", func(t *testing.T) {\n\t\tt.Run(\"case=verification flow from internal context returns redirect\", func(t *testing.T) {\n\t\t\tconf, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceVerificationUI, \"/verification\")\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\tvfID := uuid.Must(uuid.NewV4())\n\n\t\t\tcw := flow.ContinueWithVerificationUIFlow{\n\t\t\t\tID:                vfID,\n\t\t\t\tVerifiableAddress: \"test@ory.sh\",\n\t\t\t}\n\n\t\t\tinternalContext, err := json.Marshal(map[string]interface{}{\n\t\t\t\thook.InternalContextRegistrationVerificationFlow: cw,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\n\t\t\trf := &registration.Flow{}\n\t\t\trf.InternalContext = internalContext\n\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecutePostRegistrationPostPersistHook(rec, browserRequest, rf, nil))\n\t\t\tassert.Equal(t, 200, rec.Code)\n\t\t\tassert.Equal(t, \"/verification?flow=\"+vfID.String(), rf.ReturnToVerification)\n\t\t})\n\n\t\tt.Run(\"case=invalid json in internal context returns error\", func(t *testing.T) {\n\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\n\t\t\tinternalContext, err := json.Marshal(map[string]interface{}{\n\t\t\t\thook.InternalContextRegistrationVerificationFlow: \"invalid json\",\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\n\t\t\trf := &registration.Flow{}\n\t\t\trf.InternalContext = internalContext\n\n\t\t\trec := httptest.NewRecorder()\n\t\t\terr = h.ExecutePostRegistrationPostPersistHook(rec, browserRequest, rf, nil)\n\t\t\trequire.Error(t, err)\n\t\t})\n\t})\n\n\tt.Run(\"internal_context=login\", func(t *testing.T) {\n\t\tt.Run(\"case=verification flow from internal context returns redirect\", func(t *testing.T) {\n\t\t\tconf, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceVerificationUI, \"/verification\")\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\t\tvfID := uuid.Must(uuid.NewV4())\n\n\t\t\tcw := flow.ContinueWithVerificationUIFlow{\n\t\t\t\tID:                vfID,\n\t\t\t\tVerifiableAddress: \"test@ory.sh\",\n\t\t\t}\n\n\t\t\tinternalContext, err := json.Marshal(map[string]interface{}{\n\t\t\t\thook.InternalContextRegistrationVerificationFlow: cw,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\n\t\t\tlf := &login.Flow{}\n\t\t\tlf.InternalContext = internalContext\n\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, h.ExecuteLoginPostHook(rec, browserRequest, \"\", lf, nil))\n\t\t\tassert.Equal(t, 200, rec.Code)\n\t\t\tassert.Equal(t, \"/verification?flow=\"+vfID.String(), lf.ReturnToVerification)\n\t\t})\n\n\t\tt.Run(\"case=invalid json in internal context returns error\", func(t *testing.T) {\n\t\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\t\th := hook.NewShowVerificationUIHook(reg)\n\t\t\tbrowserRequest := httptest.NewRequest(\"GET\", \"/\", nil)\n\n\t\t\tinternalContext, err := json.Marshal(map[string]interface{}{\n\t\t\t\thook.InternalContextRegistrationVerificationFlow: \"invalid json\",\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\n\t\t\tlf := &login.Flow{}\n\t\t\tlf.InternalContext = internalContext\n\n\t\t\trec := httptest.NewRecorder()\n\t\t\terr = h.ExecuteLoginPostHook(rec, browserRequest, \"\", lf, nil)\n\t\t\trequire.Error(t, err)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/hook/stub/bad_template.jsonnet",
    "content": "function(ctx) {\n  flow_id: ctx.foo,\n}\n"
  },
  {
    "path": "selfservice/hook/stub/cancel_template.jsonnet",
    "content": "error \"cancel\"\n"
  },
  {
    "path": "selfservice/hook/stub/code.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/hook/stub/require_verified.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n              \"verification\": {\n                \"via\": \"email\"\n              }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/hook/stub/stub.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/hook/stub/test_body.jsonnet",
    "content": "function(ctx) std.prune({\n  flow_id: ctx.flow.id,\n  identity_id: if std.objectHas(ctx, \"identity\") then ctx.identity.id,\n  session_id: if std.objectHas(ctx, \"session\") then ctx.session.id,\n  headers: ctx.request_headers,\n  url: ctx.request_url,\n  method: ctx.request_method,\n  cookies: ctx.request_cookies,\n  transient_payload: if std.objectHas(ctx.flow, \"transient_payload\") then ctx.flow.transient_payload,\n})\n"
  },
  {
    "path": "selfservice/hook/stub/verify.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"emails\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"ory.sh/kratos\": {\n              \"verification\": {\n                \"via\": \"email\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/hook/verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/tidwall/sjson\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/otelx/semconv\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar (\n\t_ registration.PostHookPostPersistExecutor = new(Verifier)\n\t_ settings.PostHookPostPersistExecutor     = new(Verifier)\n\t_ login.PostHookExecutor                   = new(Verifier)\n)\n\ntype (\n\tverifierDependencies interface {\n\t\tconfig.Provider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\tnosurfx.CSRFProvider\n\t\tverification.StrategyProvider\n\t\tverification.FlowPersistenceProvider\n\t\tidentity.PrivilegedPoolProvider\n\t\thttpx.WriterProvider\n\t\totelx.Provider\n\t}\n\tVerifier struct {\n\t\tr verifierDependencies\n\t}\n)\n\nfunc NewVerifier(r verifierDependencies) *Verifier {\n\treturn &Verifier{r: r}\n}\n\nfunc (e *Verifier) ExecutePostRegistrationPostPersistHook(w http.ResponseWriter, r *http.Request, f *registration.Flow, s *session.Session) error {\n\treturn otelx.WithSpan(r.Context(), \"selfservice.hook.Verifier.ExecutePostRegistrationPostPersistHook\", func(ctx context.Context) error {\n\t\treturn e.do(w, r.WithContext(ctx), s.Identity, f, func(v *verification.Flow) {\n\t\t\tv.OAuth2LoginChallenge = f.OAuth2LoginChallenge\n\t\t\tv.SessionID = uuid.NullUUID{UUID: s.ID, Valid: true}\n\t\t\tv.IdentityID = uuid.NullUUID{UUID: s.Identity.ID, Valid: true}\n\t\t\tv.AMR = s.AMR\n\t\t})\n\t})\n}\n\nfunc (e *Verifier) ExecuteSettingsPostPersistHook(w http.ResponseWriter, r *http.Request, f *settings.Flow, i *identity.Identity, _ *session.Session) error {\n\treturn otelx.WithSpan(r.Context(), \"selfservice.hook.Verifier.ExecuteSettingsPostPersistHook\", func(ctx context.Context) error {\n\t\treturn e.do(w, r.WithContext(ctx), i, f, nil)\n\t})\n}\n\nfunc (e *Verifier) ExecuteLoginPostHook(w http.ResponseWriter, r *http.Request, g node.UiNodeGroup, f *login.Flow, s *session.Session) (err error) {\n\tctx, span := e.r.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.hook.Verifier.ExecuteLoginPostHook\")\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\tif f.RequestedAAL != identity.AuthenticatorAssuranceLevel1 {\n\t\tspan.SetAttributes(attribute.String(\"skip_reason\", \"skipping verification hook because AAL is not 1\"))\n\t\treturn nil\n\t}\n\n\treturn e.do(w, r.WithContext(ctx), s.Identity, f, func(vf *verification.Flow) {\n\t\tvf.OAuth2LoginChallenge = f.OAuth2LoginChallenge\n\t\tvar sID uuid.UUID\n\t\tif s != nil {\n\t\t\tsID = s.ID\n\t\t}\n\t\tvf.SessionID = uuid.NullUUID{UUID: sID, Valid: sID != uuid.Nil}\n\t\tvar iID uuid.UUID\n\t\tif s != nil && s.Identity != nil {\n\t\t\tiID = s.Identity.ID\n\t\t}\n\t\tvf.IdentityID = uuid.NullUUID{UUID: iID, Valid: iID != uuid.Nil}\n\t\tvf.AMR = s.AMR\n\t})\n}\n\nconst InternalContextRegistrationVerificationFlow = \"registration_verification_flow_continue_with\"\n\nfunc (e *Verifier) do(\n\tw http.ResponseWriter,\n\tr *http.Request,\n\ti *identity.Identity,\n\tf interface {\n\t\tflow.FlowWithContinueWith\n\t\tflow.InternalContexter\n\t},\n\tflowCallback func(*verification.Flow),\n) (err error) {\n\tctx, span := e.r.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.hook.Verifier.do\")\n\tr = r.WithContext(ctx)\n\tdefer otelx.End(span, &err)\n\n\t// This is called after the identity has been created so we can safely assume that all addresses are available\n\t// already.\n\n\tstrategies, primaryStrategy, err := e.r.GetActiveVerificationStrategies(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tisBrowserFlow := f.GetType() == flow.TypeBrowser\n\tisRegistrationOrLoginFlow := f.GetFlowName() == flow.RegistrationFlow || f.GetFlowName() == flow.LoginFlow\n\n\tfor k := range i.VerifiableAddresses {\n\t\taddress := &i.VerifiableAddresses[k]\n\t\tif isRegistrationOrLoginFlow && address.Verified {\n\t\t\tcontinue\n\t\t} else if !isRegistrationOrLoginFlow && address.Status != identity.VerifiableAddressStatusPending {\n\t\t\t// In case of the settings flow, we only want to create a new verification flow if there is no pending\n\t\t\t// verification flow for the address. Otherwise, we would create a new verification flow for each setting,\n\t\t\t// even if the address did not change.\n\t\t\tcontinue\n\t\t}\n\n\t\tif address.Value == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar csrf string\n\n\t\t// TODO: this is pretty ugly, we should probably have a better way to handle CSRF tokens here.\n\t\tif isBrowserFlow {\n\t\t\tif isRegistrationOrLoginFlow {\n\t\t\t\t// If this hook is executed from a registration flow, we need to regenerate the CSRF token.\n\t\t\t\tcsrf = e.r.CSRFHandler().RegenerateToken(w, r)\n\t\t\t} else {\n\t\t\t\t// If it came from a settings flow, there already is a CSRF token, so we can just use that.\n\t\t\t\tcsrf = e.r.GenerateCSRFToken(r)\n\t\t\t}\n\t\t}\n\n\t\tverificationFlow, err := verification.NewPostHookFlow(e.r.Config(),\n\t\t\te.r.Config().SelfServiceFlowVerificationRequestLifespan(ctx),\n\t\t\tcsrf, r, strategies, f)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif flowCallback != nil {\n\t\t\tflowCallback(verificationFlow)\n\t\t}\n\n\t\tverificationFlow.State = flow.StateEmailSent\n\t\tif err := primaryStrategy.PopulateVerificationMethod(r, verificationFlow); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tverificationFlow.UI.Nodes.Append(\n\t\t\tnode.NewInputField(address.Via, address.Value, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\t\tWithMetaLabel(text.NewInfoNodeResendOTP()),\n\t\t)\n\n\t\tif err := e.r.VerificationFlowPersister().CreateVerificationFlow(ctx, verificationFlow); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := primaryStrategy.SendVerificationCode(ctx, verificationFlow, i, address); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tflowURL := \"\"\n\t\tif verificationFlow.Type == flow.TypeBrowser {\n\t\t\tflowURL = verificationFlow.AppendTo(e.r.Config().SelfServiceFlowVerificationUI(ctx)).String()\n\t\t}\n\n\t\tcontinueWith := flow.NewContinueWithVerificationUI(verificationFlow.ID, address.Value, flowURL)\n\t\tinternalContext, err := sjson.SetBytes(f.GetInternalContext(), InternalContextRegistrationVerificationFlow, continueWith.Flow)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tf.SetInternalContext(internalContext)\n\n\t\tif e.r.Config().UseLegacyShowVerificationUI(ctx) {\n\t\t\tspan.AddEvent(semconv.NewDeprecatedFeatureUsedEvent(ctx, \"legacy_continue_with_verification_ui\"))\n\t\t\tf.AddContinueWith(continueWith)\n\t\t\tcontinue // Legacy behavior\n\t\t}\n\n\t\tbreak // We only do this for the first address we find as we can't redirect to multiple flows at once.\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/hook/verification_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook_test\n\nimport (\n\t\"context\"\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/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestVerifier(t *testing.T) {\n\tctx := context.Background()\n\tu := &http.Request{URL: urlx.ParseOrPanic(\"https://www.ory.sh/\")}\n\n\tfor _, tc := range []struct {\n\t\tname     string\n\t\texecHook func(h *hook.Verifier, i *identity.Identity, f flow.Flow) error\n\n\t\toriginalFlow func() interface {\n\t\t\tflow.InternalContexter\n\t\t\tflow.FlowWithContinueWith\n\t\t}\n\t}{\n\t\t{\n\t\t\tname: \"login\",\n\t\t\texecHook: func(h *hook.Verifier, i *identity.Identity, f flow.Flow) error {\n\t\t\t\treturn h.ExecuteLoginPostHook(\n\t\t\t\t\thttptest.NewRecorder(), u, node.CodeGroup, f.(*login.Flow), &session.Session{ID: x.NewUUID(), Identity: i})\n\t\t\t},\n\t\t\toriginalFlow: func() interface {\n\t\t\t\tflow.InternalContexter\n\t\t\t\tflow.FlowWithContinueWith\n\t\t\t} {\n\t\t\t\treturn &login.Flow{RequestURL: \"http://foo.com/login\", RequestedAAL: \"aal1\"}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"registration\",\n\t\t\texecHook: func(h *hook.Verifier, i *identity.Identity, f flow.Flow) error {\n\t\t\t\treturn h.ExecutePostRegistrationPostPersistHook(\n\t\t\t\t\thttptest.NewRecorder(), u, f.(*registration.Flow), &session.Session{ID: x.NewUUID(), Identity: i})\n\t\t\t},\n\t\t\toriginalFlow: func() interface {\n\t\t\t\tflow.InternalContexter\n\t\t\t\tflow.FlowWithContinueWith\n\t\t\t} {\n\t\t\t\treturn &registration.Flow{RequestURL: \"http://foo.com/registration?after_verification_return_to=verification_callback\"}\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(\"flow=\"+tc.name, func(t *testing.T) {\n\t\t\tfor _, enabled := range []bool{true, false} {\n\t\t\t\tt.Run(fmt.Sprintf(\"legacy flag=%v\", enabled), func(t *testing.T) {\n\t\t\t\t\tt.Run(\"case=should send out emails for unverified addresses\", func(t *testing.T) {\n\t\t\t\t\t\toriginalFlow := tc.originalFlow()\n\t\t\t\t\t\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/verify.schema.json\")\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, \"https://www.ory.sh/\")\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCourierSMTPURL, \"smtp://foo@bar@dev.null/\")\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyUseLegacyShowVerificationUI, enabled)\n\n\t\t\t\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\t\t\ti.Traits = identity.Traits(`{\"emails\":[\"foo@ory.sh\",\"bar@ory.sh\"]}`)\n\t\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), i))\n\n\t\t\t\t\t\th := hook.NewVerifier(reg)\n\t\t\t\t\t\trequire.NoError(t, tc.execHook(h, i, originalFlow))\n\t\t\t\t\t\tassert.Lenf(t, gjson.GetBytes(originalFlow.GetInternalContext(), hook.InternalContextRegistrationVerificationFlow).Array(), 1, \"%s\", originalFlow.GetInternalContext())\n\n\t\t\t\t\t\tvar verificationID uuid.UUID\n\t\t\t\t\t\tif enabled {\n\t\t\t\t\t\t\tassert.Lenf(t, originalFlow.ContinueWith(), 2, \"%#v\", originalFlow.ContinueWith())\n\t\t\t\t\t\t\tassertContinueWithAddresses(t, originalFlow.ContinueWith(), []string{\"foo@ory.sh\", \"bar@ory.sh\"})\n\t\t\t\t\t\t\tvf := originalFlow.ContinueWith()[0]\n\t\t\t\t\t\t\tassert.IsType(t, &flow.ContinueWithVerificationUI{}, vf)\n\t\t\t\t\t\t\tverificationID = vf.(*flow.ContinueWithVerificationUI).Flow.ID\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tassert.Lenf(t, originalFlow.ContinueWith(), 0, \"%#v\", originalFlow.ContinueWith())\n\t\t\t\t\t\t\tverificationID = uuid.FromStringOrNil(gjson.GetBytes(originalFlow.GetInternalContext(), hook.InternalContextRegistrationVerificationFlow+\".id\").String())\n\t\t\t\t\t\t\trequire.NotEqual(t, uuid.Nil, verificationID, \"%s\", originalFlow.GetInternalContext())\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\texpectedVerificationFlow, err := reg.VerificationFlowPersister().GetVerificationFlow(ctx, verificationID)\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\trequire.Equal(t, expectedVerificationFlow.State, flow.StateEmailSent)\n\t\t\t\t\t\trequire.NotEmpty(t, expectedVerificationFlow.SessionID.UUID)\n\t\t\t\t\t\trequire.NotEmpty(t, expectedVerificationFlow.IdentityID.UUID)\n\t\t\t\t\t\trequire.NotNil(t, expectedVerificationFlow.UI.Nodes.Find(\"email\"))\n\n\t\t\t\t\t\tmessages, err := reg.CourierPersister().NextMessages(context.Background(), 12)\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\tif enabled {\n\t\t\t\t\t\t\trequire.Len(t, messages, 2)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.Len(t, messages, 1)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tt.Run(\"case should skip already verified addresses\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\toriginalFlow := tc.originalFlow()\n\t\t\t\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/verify.schema.json\")\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, \"https://www.ory.sh/\")\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyCourierSMTPURL, \"smtp://foo@bar@dev.null/\")\n\t\t\t\th := hook.NewVerifier(reg)\n\n\t\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\ti.Traits = identity.Traits(`{\"emails\":[\"foo@ory.sh\",\"bar@ory.sh\"]}`)\n\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), i))\n\t\t\t\tfor _, address := range i.VerifiableAddresses {\n\t\t\t\t\taddress.Verified = true\n\t\t\t\t\taddress.VerifiedAt = new(sqlxx.NullTime(time.Now()))\n\t\t\t\t\taddress.Status = identity.VerifiableAddressStatusCompleted\n\t\t\t\t\trequire.NoError(t, reg.Persister().UpdateVerifiableAddress(context.Background(), &address))\n\t\t\t\t}\n\t\t\t\ti, err := reg.PrivilegedIdentityPool().GetIdentity(ctx, i.ID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\trequire.NoError(t, tc.execHook(h, i, originalFlow))\n\t\t\t\tassert.Empty(t, originalFlow.ContinueWith(), \"%#ßv\", originalFlow.ContinueWith())\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"flow=login/case=does not run if aal is not 1\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\t\th := hook.NewVerifier(reg)\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\tf := &login.Flow{RequestedAAL: \"aal2\"}\n\t\trequire.NoError(t, h.ExecuteLoginPostHook(httptest.NewRecorder(), u, node.CodeGroup, f, &session.Session{ID: x.NewUUID(), Identity: i}))\n\n\t\tmessages, err := reg.CourierPersister().NextMessages(context.Background(), 12)\n\t\trequire.EqualError(t, err, \"queue is empty\")\n\t\trequire.Len(t, messages, 0)\n\t})\n\n\tt.Run(\"name=settings\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/verify.schema.json\")\n\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, \"https://www.ory.sh/\")\n\t\tconf.MustSet(ctx, config.ViperKeyCourierSMTPURL, \"smtp://foo@bar@dev.null/\")\n\t\tconf.MustSet(ctx, config.ViperKeyUseLegacyShowVerificationUI, true)\n\n\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\ti.Traits = identity.Traits(`{\"emails\":[\"foo@ory.sh\",\"bar@ory.sh\",\"baz@ory.sh\"]}`)\n\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), i))\n\n\t\tactual, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, \"foo@ory.sh\")\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, \"foo@ory.sh\", actual.Value)\n\n\t\tactual, err = reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, \"bar@ory.sh\")\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, \"bar@ory.sh\", actual.Value)\n\n\t\tactual, err = reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, \"baz@ory.sh\")\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, \"baz@ory.sh\", actual.Value)\n\n\t\tverifiedAt := sqlxx.NullTime(time.Now())\n\t\tactual.Status = identity.VerifiableAddressStatusCompleted\n\t\tactual.Verified = true\n\t\tactual.VerifiedAt = &verifiedAt\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateVerifiableAddress(context.Background(), actual))\n\n\t\ti, err = reg.IdentityPool().GetIdentity(context.Background(), i.ID, identity.ExpandDefault)\n\t\trequire.NoError(t, err)\n\n\t\toriginalFlow := &settings.Flow{RequestURL: \"http://foo.com/settings?after_verification_return_to=verification_callback\"}\n\n\t\th := hook.NewVerifier(reg)\n\t\trequire.NoError(t, h.ExecuteSettingsPostPersistHook(\n\t\t\thttptest.NewRecorder(), u, originalFlow, i, &session.Session{ID: x.NewUUID(), Identity: i}))\n\t\tassert.Lenf(t, originalFlow.ContinueWith(), 2, \"%#ßv\", originalFlow.ContinueWith())\n\t\tassertContinueWithAddresses(t, originalFlow.ContinueWith(), []string{\"foo@ory.sh\", \"bar@ory.sh\"})\n\t\tvf := originalFlow.ContinueWith()[0]\n\t\tassert.IsType(t, &flow.ContinueWithVerificationUI{}, vf)\n\t\tfView := vf.(*flow.ContinueWithVerificationUI).Flow\n\n\t\texpectedVerificationFlow, err := reg.VerificationFlowPersister().GetVerificationFlow(ctx, fView.ID)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, expectedVerificationFlow.State, flow.StateEmailSent)\n\n\t\tmessages, err := reg.CourierPersister().NextMessages(context.Background(), 12)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, messages, 2)\n\n\t\trecipients := make([]string, len(messages))\n\t\tfor k, m := range messages {\n\t\t\trecipients[k] = m.Recipient\n\t\t}\n\n\t\tassert.Contains(t, recipients, \"foo@ory.sh\")\n\t\tassert.Contains(t, recipients, \"bar@ory.sh\")\n\t\t// Email to baz@ory.sh is skipped because it is verified already.\n\t\tassert.NotContains(t, recipients, \"baz@ory.sh\")\n\n\t\t// these addresses will be marked as sent and won't be sent again by the settings hook\n\t\taddress1, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, \"foo@ory.sh\")\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, identity.VerifiableAddressStatusSent, address1.Status)\n\t\taddress2, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, \"bar@ory.sh\")\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, identity.VerifiableAddressStatusSent, address2.Status)\n\n\t\toriginalFlow = &settings.Flow{RequestURL: \"http://foo.com/settings?after_verification_return_to=verification_callback\"}\n\n\t\trequire.NoError(t, h.ExecuteSettingsPostPersistHook(\n\t\t\thttptest.NewRecorder(), u, originalFlow, i, &session.Session{ID: x.NewUUID(), Identity: i}))\n\n\t\tassert.Emptyf(t, originalFlow.ContinueWith(), \"%+v\", originalFlow.ContinueWith())\n\n\t\trequire.NoError(t, err)\n\t\tmessages, err = reg.CourierPersister().NextMessages(context.Background(), 12)\n\t\trequire.EqualError(t, err, courier.ErrQueueEmpty.Error())\n\t\tassert.Len(t, messages, 0)\n\t})\n}\n\nfunc assertContinueWithAddresses(t *testing.T, cs []flow.ContinueWith, addresses []string) {\n\tt.Helper()\n\n\trequire.Len(t, cs, len(addresses))\n\n\te := make([]string, len(cs))\n\n\tfor i, c := range cs {\n\t\trequire.IsType(t, &flow.ContinueWithVerificationUI{}, c)\n\t\tfView := c.(*flow.ContinueWithVerificationUI).Flow\n\t\te[i] = fView.VerifiableAddress\n\t}\n\n\trequire.ElementsMatch(t, addresses, e)\n}\n"
  },
  {
    "path": "selfservice/hook/web_hook.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/textproto\"\n\t\"time\"\n\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/codes\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.11.0\"\n\t\"go.opentelemetry.io/otel/trace\"\n\tgrpccodes \"google.golang.org/grpc/codes\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/request\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/reqlog\"\n)\n\nvar _ interface {\n\tlogin.PreHookExecutor\n\tlogin.PostHookExecutor\n\n\tregistration.PostHookPostPersistExecutor\n\tregistration.PostHookPrePersistExecutor\n\tregistration.PreHookExecutor\n\n\tverification.PreHookExecutor\n\tverification.PostHookExecutor\n\n\trecovery.PreHookExecutor\n\trecovery.PostHookExecutor\n\n\tsettings.PreHookExecutor\n\tsettings.PostHookPrePersistExecutor\n\tsettings.PostHookPostPersistExecutor\n} = (*WebHook)(nil)\n\nvar jsonnetCache, _ = ristretto.NewCache(&ristretto.Config[[]byte, []byte]{\n\tMaxCost:     100 << 20, // 100MB,\n\tNumCounters: 1_000_000, // 1kB per snippet -> 100k snippets -> 1M counters\n\tBufferItems: 64,\n})\n\ntype (\n\twebHookDependencies interface {\n\t\tlogrusx.Provider\n\t\thttpx.ClientProvider\n\t\totelx.Provider\n\t\tjsonnetsecure.VMProvider\n\t\tconfig.Provider\n\t}\n\n\ttemplateContext struct {\n\t\tFlow           flow.Flow          `json:\"flow\"`\n\t\tRequestHeaders http.Header        `json:\"request_headers\"`\n\t\tRequestMethod  string             `json:\"request_method\"`\n\t\tRequestURL     string             `json:\"request_url\"`\n\t\tRequestCookies map[string]string  `json:\"request_cookies\"`\n\t\tIdentity       *identity.Identity `json:\"identity,omitempty\"`\n\t\tSession        *session.Session   `json:\"session,omitempty\"`\n\t}\n\n\tWebHook struct {\n\t\tdeps webHookDependencies\n\t\tconf *request.Config\n\t}\n\n\tdetailedMessage struct {\n\t\tID      int             `json:\"id\"`\n\t\tText    string          `json:\"text\"`\n\t\tType    string          `json:\"type\"`\n\t\tContext json.RawMessage `json:\"context,omitempty\"`\n\t}\n\n\terrorMessage struct {\n\t\tInstancePtr      string            `json:\"instance_ptr\"`\n\t\tDetailedMessages []detailedMessage `json:\"messages\"`\n\t}\n\n\trawHookResponse struct {\n\t\tMessages []errorMessage `json:\"messages\"`\n\t}\n)\n\nfunc cookies(req *http.Request) map[string]string {\n\tcookies := make(map[string]string)\n\tfor _, c := range req.Cookies() {\n\t\tif c.Name != \"\" {\n\t\t\tcookies[c.Name] = c.Value\n\t\t}\n\t}\n\treturn cookies\n}\n\nfunc NewWebHook(r webHookDependencies, c *request.Config) *WebHook {\n\treturn &WebHook{deps: r, conf: c}\n}\n\nfunc (e *WebHook) ExecuteLoginPreHook(_ http.ResponseWriter, req *http.Request, flow *login.Flow) error {\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecuteLoginPreHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecuteLoginPostHook(_ http.ResponseWriter, req *http.Request, _ node.UiNodeGroup, flow *login.Flow, session *session.Session) error {\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecuteLoginPostHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t\tIdentity:       session.Identity,\n\t\t\tSession:        session,\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecuteVerificationPreHook(_ http.ResponseWriter, req *http.Request, flow *verification.Flow) error {\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecuteVerificationPreHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecutePostVerificationHook(_ http.ResponseWriter, req *http.Request, flow *verification.Flow, id *identity.Identity) error {\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecutePostVerificationHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t\tIdentity:       id,\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecuteRecoveryPreHook(_ http.ResponseWriter, req *http.Request, flow *recovery.Flow) error {\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecuteRecoveryPreHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestCookies: cookies(req),\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecutePostRecoveryHook(_ http.ResponseWriter, req *http.Request, flow *recovery.Flow, session *session.Session) error {\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecutePostRecoveryHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t\tIdentity:       session.Identity,\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecuteRegistrationPreHook(_ http.ResponseWriter, req *http.Request, flow *registration.Flow) error {\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecuteRegistrationPreHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecutePostRegistrationPrePersistHook(_ http.ResponseWriter, req *http.Request, flow *registration.Flow, id *identity.Identity) error {\n\tif !e.conf.CanInterrupt && !e.conf.Response.Parse {\n\t\treturn nil\n\t}\n\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecutePostRegistrationPrePersistHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t\tIdentity:       id,\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecutePostRegistrationPostPersistHook(_ http.ResponseWriter, req *http.Request, flow *registration.Flow, session *session.Session) error {\n\tif e.conf.CanInterrupt || e.conf.Response.Parse {\n\t\treturn nil\n\t}\n\n\t// We want to decouple the request from the hook execution, so that the hooks still execute even\n\t// if the request is canceled.\n\tctx := context.WithoutCancel(req.Context())\n\n\treturn otelx.WithSpan(ctx, \"selfservice.hook.WebHook.ExecutePostRegistrationPostPersistHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t\tIdentity:       session.Identity,\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecuteSettingsPreHook(_ http.ResponseWriter, req *http.Request, flow *settings.Flow, s *session.Session) error {\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecuteSettingsPreHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t\tSession:        s,\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecuteSettingsPostPersistHook(_ http.ResponseWriter, req *http.Request, flow *settings.Flow, id *identity.Identity, s *session.Session) error {\n\tif e.conf.CanInterrupt || e.conf.Response.Parse {\n\t\treturn nil\n\t}\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecuteSettingsPostPersistHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t\tIdentity:       id,\n\t\t\tSession:        s,\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) ExecuteSettingsPrePersistHook(_ http.ResponseWriter, req *http.Request, flow *settings.Flow, id *identity.Identity, s *session.Session) error {\n\tif !e.conf.CanInterrupt && !e.conf.Response.Parse {\n\t\treturn nil\n\t}\n\treturn otelx.WithSpan(req.Context(), \"selfservice.hook.WebHook.ExecuteSettingsPrePersistHook\", func(ctx context.Context) error {\n\t\treturn e.execute(ctx, &templateContext{\n\t\t\tFlow:           flow,\n\t\t\tRequestHeaders: req.Header,\n\t\t\tRequestMethod:  req.Method,\n\t\t\tRequestURL:     x.RequestURL(req).String(),\n\t\t\tRequestCookies: cookies(req),\n\t\t\tIdentity:       id,\n\t\t\tSession:        s,\n\t\t})\n\t})\n}\n\nfunc (e *WebHook) execute(ctx context.Context, data *templateContext) error {\n\tvar (\n\t\thttpClient     = e.deps.HTTPClient(ctx)\n\t\tignoreResponse = e.conf.Response.Ignore\n\t\tcanInterrupt   = e.conf.CanInterrupt\n\t\tparseResponse  = e.conf.Response.Parse\n\t\temitEvent      = e.conf.EmitAnalyticsEvent == nil || *e.conf.EmitAnalyticsEvent // default true\n\t\twebhookID      = e.conf.ID\n\t\t// The trigger ID is a random ID. It can be used to correlate webhook requests across retries.\n\t\ttriggerID = x.NewUUID()\n\t\ttracer    = trace.SpanFromContext(ctx).TracerProvider().Tracer(\"kratos-webhooks\")\n\t)\n\tif ignoreResponse && (parseResponse || canInterrupt) {\n\t\treturn errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"A webhook is configured to ignore the response but also to parse the response. This is not possible.\"))\n\t}\n\n\tmakeRequest := func() (finalErr error) {\n\t\tif ignoreResponse {\n\t\t\t// This means we want to run this closure asynchronously and not be\n\t\t\t// canceled when the parent context is canceled.\n\t\t\t//\n\t\t\t// The webhook will still cancel after 30 seconds as that is the\n\t\t\t// configured timeout for the HTTP client.\n\t\t\tctx = context.WithoutCancel(ctx)\n\t\t}\n\t\tctx, span := tracer.Start(ctx, \"selfservice.webhook\")\n\t\tdefer otelx.End(span, &finalErr)\n\n\t\tif emitEvent {\n\t\t\tInstrumentHTTPClientForEvents(ctx, httpClient, triggerID, webhookID)\n\t\t}\n\n\t\tdefer func(startTime time.Time) {\n\t\t\ttraceID, spanID := span.SpanContext().TraceID(), span.SpanContext().SpanID()\n\t\t\tlogger := e.deps.Logger().WithField(\"otel\", map[string]string{\n\t\t\t\t\"trace_id\": traceID.String(),\n\t\t\t\t\"span_id\":  spanID.String(),\n\t\t\t}).WithField(\"duration\", time.Since(startTime))\n\t\t\tif finalErr != nil {\n\t\t\t\tif emitEvent && !errors.Is(finalErr, context.Canceled) {\n\t\t\t\t\tspan.AddEvent(events.NewWebhookFailed(ctx, finalErr, triggerID, webhookID))\n\t\t\t\t}\n\t\t\t\tif ignoreResponse {\n\t\t\t\t\tlogger.WithError(finalErr).Warning(\"Webhook request failed but the error was ignored because the configuration indicated that the upstream response should be ignored\")\n\t\t\t\t} else {\n\t\t\t\t\tlogger.WithError(finalErr).Error(\"Webhook request failed\")\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlogger.Info(\"Webhook request succeeded\")\n\t\t\t\tif emitEvent {\n\t\t\t\t\tspan.AddEvent(events.NewWebhookSucceeded(ctx, triggerID, webhookID))\n\t\t\t\t}\n\t\t\t}\n\t\t}(time.Now())\n\n\t\tbuilder, err := request.NewBuilder(ctx, e.conf, e.deps, request.WithCache(jsonnetCache))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tspan.SetAttributes(\n\t\t\tattribute.String(\"webhook.jsonnet.template-uri\", builder.Config.TemplateURI),\n\t\t\tattribute.Bool(\"webhook.can_interrupt\", canInterrupt),\n\t\t\tattribute.Bool(\"webhook.response.ignore\", ignoreResponse),\n\t\t\tattribute.Bool(\"webhook.response.parse\", parseResponse),\n\t\t)\n\n\t\tdata.RequestHeaders = RemoveDisallowedHeaders(data.RequestHeaders, e.deps.Config().WebhookHeaderAllowlist(ctx))\n\n\t\treq, err := builder.BuildRequest(ctx, data)\n\t\tif errors.Is(err, request.ErrCancel) {\n\t\t\tspan.SetAttributes(attribute.Bool(\"webhook.jsonnet.canceled\", true))\n\t\t\treturn nil\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif data.Identity != nil {\n\t\t\tspan.SetAttributes(\n\t\t\t\tattribute.String(\"webhook.identity.id\", data.Identity.ID.String()),\n\t\t\t\tattribute.String(\"webhook.identity.nid\", data.Identity.NID.String()),\n\t\t\t)\n\t\t}\n\n\t\te.deps.Logger().WithRequest(req.Request).Info(\"Dispatching webhook\")\n\n\t\treq = req.WithContext(ctx)\n\n\t\tresp, err := httpClient.Do(req)\n\t\tif err != nil {\n\t\t\tif IsTimeoutError(err) {\n\t\t\t\treturn herodot.DefaultError{\n\t\t\t\t\tCodeField:     http.StatusGatewayTimeout,\n\t\t\t\t\tStatusField:   http.StatusText(http.StatusGatewayTimeout),\n\t\t\t\t\tGRPCCodeField: grpccodes.DeadlineExceeded,\n\t\t\t\t\tErrorField:    err.Error(),\n\t\t\t\t\tReasonField:   \"A third-party upstream service could not be reached. Please try again later.\",\n\t\t\t\t}.WithWrap(errors.WithStack(err))\n\t\t\t}\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tdefer func() { _ = resp.Body.Close() }()\n\t\tresp.Body = io.NopCloser(io.LimitReader(resp.Body, 5<<20)) // read at most 5 MB from the response\n\t\tspan.SetAttributes(semconv.HTTPAttributesFromHTTPStatusCode(resp.StatusCode)...)\n\n\t\tif resp.StatusCode >= http.StatusBadRequest {\n\t\t\tspan.SetStatus(codes.Error, \"HTTP status code >= 400\")\n\t\t\tif canInterrupt || parseResponse {\n\t\t\t\tif err := parseWebhookResponse(resp, data.Identity); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn herodot.DefaultError{\n\t\t\t\tCodeField:     http.StatusBadGateway,\n\t\t\t\tStatusField:   http.StatusText(http.StatusBadGateway),\n\t\t\t\tGRPCCodeField: grpccodes.Aborted,\n\t\t\t\tReasonField:   \"A third-party upstream service responded improperly. Please try again later.\",\n\t\t\t\tErrorField:    fmt.Sprintf(\"webhook failed with status code %v\", resp.StatusCode),\n\t\t\t}\n\t\t}\n\n\t\tif parseResponse {\n\t\t\treturn parseWebhookResponse(resp, data.Identity)\n\t\t}\n\t\treturn nil\n\t}\n\n\tif !ignoreResponse {\n\t\tt0 := time.Now()\n\t\terr := makeRequest()\n\t\treqlog.AccumulateExternalLatency(ctx, time.Since(t0))\n\t\treturn err\n\t}\n\tgo func() {\n\t\t// we cannot handle the error as we are running async, and it is logged anyway\n\t\t_ = makeRequest()\n\t}()\n\treturn nil\n}\n\n// RemoveDisallowedHeaders removes all headers from httpHeaders that are not in\n// headerAllowlist.\nfunc RemoveDisallowedHeaders(httpHeaders http.Header, headerAllowlist []string) http.Header {\n\tres := make(http.Header, len(headerAllowlist))\n\tfor _, allowed := range headerAllowlist {\n\t\tallowed = textproto.CanonicalMIMEHeaderKey(allowed)\n\t\tif h, ok := httpHeaders[allowed]; ok {\n\t\t\tres[allowed] = h\n\t\t}\n\t}\n\treturn res\n}\n\nfunc parseWebhookResponse(resp *http.Response, id *identity.Identity) (err error) {\n\tif resp == nil {\n\t\treturn errors.Errorf(\"empty response provided from the webhook\")\n\t}\n\n\tif resp.StatusCode == http.StatusOK {\n\t\ttype localIdentity identity.Identity\n\t\tvar hookResponse struct {\n\t\t\tIdentity *localIdentity `json:\"identity\"`\n\t\t}\n\t\t// io.ReadAll is safe, because resp.Body is already a limited reader.\n\t\tbody, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, \"webhook response body could not be read\")\n\t\t}\n\t\tif err = json.Unmarshal(body, &hookResponse); err != nil {\n\t\t\treturn errors.Wrap(err, \"webhook response could not be unmarshalled properly from JSON\")\n\t\t}\n\n\t\tif hookResponse.Identity == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tif len(hookResponse.Identity.Traits) > 0 {\n\t\t\tid.Traits = hookResponse.Identity.Traits\n\t\t}\n\n\t\tif len(hookResponse.Identity.SchemaID) > 0 {\n\t\t\tid.SchemaID = hookResponse.Identity.SchemaID\n\t\t}\n\n\t\tif len(hookResponse.Identity.State) > 0 {\n\t\t\tid.State = hookResponse.Identity.State\n\t\t}\n\n\t\tif len(hookResponse.Identity.VerifiableAddresses) > 0 {\n\t\t\tid.VerifiableAddresses = hookResponse.Identity.VerifiableAddresses\n\t\t}\n\n\t\tif len(hookResponse.Identity.RecoveryAddresses) > 0 {\n\t\t\tid.RecoveryAddresses = hookResponse.Identity.RecoveryAddresses\n\t\t}\n\n\t\tif len(hookResponse.Identity.MetadataPublic) > 0 {\n\t\t\tid.MetadataPublic = hookResponse.Identity.MetadataPublic\n\t\t}\n\n\t\tif len(hookResponse.Identity.MetadataAdmin) > 0 {\n\t\t\tid.MetadataAdmin = hookResponse.Identity.MetadataAdmin\n\t\t}\n\n\t\tif gjson.GetBytes(body, \"identity.external_id\").Exists() {\n\t\t\tid.ExternalID = hookResponse.Identity.ExternalID\n\t\t}\n\n\t\treturn nil\n\t} else if resp.StatusCode == http.StatusNoContent {\n\t\treturn nil\n\t} else if resp.StatusCode >= http.StatusBadRequest {\n\t\tvar hookResponse rawHookResponse\n\t\tif err := json.NewDecoder(resp.Body).Decode(&hookResponse); err != nil {\n\t\t\treturn errors.Wrap(err, \"webhook response could not be unmarshalled properly from JSON\")\n\t\t}\n\n\t\tvar validationErrs []*schema.ValidationError\n\t\tfor _, msg := range hookResponse.Messages {\n\t\t\tmessages := text.Messages{}\n\t\t\tfor _, detail := range msg.DetailedMessages {\n\t\t\t\tvar msgType text.UITextType\n\t\t\t\tif detail.Type == \"error\" {\n\t\t\t\t\tmsgType = text.Error\n\t\t\t\t} else {\n\t\t\t\t\tmsgType = text.Info\n\t\t\t\t}\n\t\t\t\tmessages.Add(&text.Message{\n\t\t\t\t\tID:      text.ID(detail.ID),\n\t\t\t\t\tText:    detail.Text,\n\t\t\t\t\tType:    msgType,\n\t\t\t\t\tContext: detail.Context,\n\t\t\t\t})\n\t\t\t}\n\t\t\tvalidationErrs = append(validationErrs, schema.NewHookValidationError(msg.InstancePtr, \"a webhook target returned an error\", messages))\n\t\t}\n\n\t\tif len(validationErrs) == 0 {\n\t\t\treturn errors.New(\"error while parsing webhook response: got no validation errors\")\n\t\t}\n\n\t\treturn schema.NewValidationListError(validationErrs)\n\t}\n\n\treturn nil\n}\n\nfunc IsTimeoutError(err error) bool {\n\tvar te interface{ Timeout() bool }\n\treturn errors.As(err, &te) && te.Timeout() || errors.Is(err, context.DeadlineExceeded)\n}\n\nfunc InstrumentHTTPClientForEvents(ctx context.Context, httpClient *retryablehttp.Client, triggerID uuid.UUID, webhookID string) {\n\t// TODO(@alnr): improve this implementation to redact sensitive data\n\tvar (\n\t\tattempt   = 0\n\t\trequestID uuid.UUID\n\t\treqBody   []byte\n\t)\n\thttpClient.RequestLogHook = func(_ retryablehttp.Logger, req *http.Request, retryNumber int) {\n\t\tattempt = retryNumber + 1\n\t\trequestID = x.NewUUID()\n\t\treq.Header.Set(\"Ory-Webhook-Request-ID\", requestID.String())\n\t\treq.Header.Set(\"Ory-Webhook-Trigger-ID\", triggerID.String())\n\t\t// TODO(@alnr): redact sensitive data\n\t\t// reqBody, _ = httputil.DumpRequestOut(req, true)\n\t\treqBody = []byte(\"<redacted>\")\n\t}\n\thttpClient.ResponseLogHook = func(_ retryablehttp.Logger, res *http.Response) {\n\t\t// res.Body = io.NopCloser(io.LimitReader(res.Body, 5<<20)) // read at most 5 MB from the response\n\t\t// resBody, _ := httputil.DumpResponse(res, true)\n\t\t// resBody = resBody[:min(len(resBody), 2<<10)] // truncate response body to 2 kB for event\n\t\t// TODO(@alnr): redact sensitive data\n\t\tresBody := []byte(\"<redacted>\")\n\t\ttrace.SpanFromContext(ctx).AddEvent(events.NewWebhookDelivered(ctx, res.Request.URL, reqBody, res.StatusCode, resBody, attempt, requestID, triggerID, webhookID))\n\t}\n}\n"
  },
  {
    "path": "selfservice/hook/web_hook_integration_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage hook_test\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t_ \"embed\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"slices\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus/hooks/test\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\tsdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n\t\"go.opentelemetry.io/otel/sdk/trace/tracetest\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/request\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\nvar transientPayload = json.RawMessage(`{\n\t\"stuff\": {\n\t\t\"name\": \"fubar\",\n\t\t\"numbers\": [42, 12345, 3.1415]\n\t}\n}`)\n\nfunc TestWebHooks(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tlogger := logrusx.New(\"kratos\", \"test\")\n\n\tconf.MustSet(ctx, config.ViperKeyWebhookHeaderAllowlist, []string{\n\t\t\"Accept\",\n\t\t\"Accept-Encoding\",\n\t\t\"Accept-Language\",\n\t\t\"Content-Length\",\n\t\t\"Content-Type\",\n\t\t\"Origin\",\n\t\t\"Priority\",\n\t\t\"Referer\",\n\t\t\"Sec-Ch-Ua\",\n\t\t\"Sec-Ch-Ua-Mobile\",\n\t\t\"Sec-Ch-Ua-Platform\",\n\t\t\"Sec-Fetch-Dest\",\n\t\t\"Sec-Fetch-Mode\",\n\t\t\"Sec-Fetch-Site\",\n\t\t\"Sec-Fetch-User\",\n\t\t\"True-Client-Ip\",\n\t\t\"User-Agent\",\n\t\t\"Valid-Header\",\n\t})\n\n\twhDeps := struct {\n\t\tx.BasicRegistry\n\t\t*jsonnetsecure.TestProvider\n\t\tconfig.Provider\n\t}{\n\t\tx.BasicRegistry{L: logger, C: reg.HTTPClient(ctx), T: otelx.NewNoop()},\n\t\tjsonnetsecure.NewTestProvider(t),\n\t\treg,\n\t}\n\ttype WebHookRequest struct {\n\t\tBody    string\n\t\tHeaders http.Header\n\t\tMethod  string\n\t}\n\n\twebHookEndPoint := func(whr *WebHookRequest) http.HandlerFunc {\n\t\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\t\tbody, err := io.ReadAll(r.Body)\n\t\t\tif err != nil {\n\t\t\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\twhr.Body = string(body)\n\t\t\twhr.Headers = r.Header\n\t\t\twhr.Method = r.Method\n\t\t}\n\t}\n\n\twebHookHttpCodeEndPoint := func(code int) http.HandlerFunc {\n\t\treturn func(w http.ResponseWriter, _ *http.Request) {\n\t\t\tw.WriteHeader(code)\n\t\t}\n\t}\n\n\twebHookHttpCodeWithBodyEndPoint := func(t *testing.T, code int, body []byte) http.HandlerFunc {\n\t\treturn func(w http.ResponseWriter, _ *http.Request) {\n\t\t\tw.WriteHeader(code)\n\t\t\t_, err := w.Write(body)\n\t\t\tassert.NoError(t, err, \"error while returning response from webHookHttpCodeWithBodyEndPoint\")\n\t\t}\n\t}\n\n\tpath := \"/web_hook\"\n\tnewServer := func(f http.HandlerFunc) *httptest.Server {\n\t\tr := http.NewServeMux()\n\n\t\tr.HandleFunc(\"CONNECT \"+path, f)\n\t\tr.HandleFunc(\"DELETE \"+path, f)\n\t\tr.HandleFunc(\"GET \"+path, f)\n\t\tr.HandleFunc(\"OPTIONS \"+path, f)\n\t\tr.HandleFunc(\"PATCH \"+path, f)\n\t\tr.HandleFunc(\"POST \"+path, f)\n\t\tr.HandleFunc(\"PUT \"+path, f)\n\t\tr.HandleFunc(\"TRACE \"+path, f)\n\n\t\tts := httptest.NewServer(r)\n\t\tt.Cleanup(ts.Close)\n\t\treturn ts\n\t}\n\n\tbodyWithFlowOnly := func(req *http.Request, f flow.Flow) string {\n\t\tbody := fmt.Sprintf(`{\n\t\t\t\t\t\"flow_id\": \"%s\",\n\t\t\t\t\t\"method\": \"%s\",\n\t\t\t\t\t\"url\": \"%s\",\n\t\t\t\t\t\"cookies\": {\n\t\t\t\t\t\t\"Some-Cookie-1\": \"Some-Cookie-Value\",\n\t\t\t\t\t\t\"Some-Cookie-2\": \"Some-other-Cookie-Value\",\n\t\t\t\t\t\t\"Some-Cookie-3\": \"Third-Cookie-Value\"\n\t\t\t\t\t}\n\t\t\t\t}`, f.GetID(), req.Method, \"http://www.ory.sh/some_end_point\")\n\t\tif len(req.Header) != 0 {\n\t\t\tif ua := req.Header.Get(\"User-Agent\"); ua != \"\" {\n\t\t\t\tbody, _ = sjson.Set(body, \"headers.User-Agent\", []string{ua})\n\t\t\t}\n\t\t}\n\n\t\treturn body\n\t}\n\n\tbodyWithFlowAndIdentityAndTransientPayload := func(req *http.Request, f flow.Flow, s *session.Session, tp json.RawMessage) string {\n\t\tbody := fmt.Sprintf(`{\n\t\t\t\t\t\"flow_id\": \"%s\",\n\t\t\t\t\t\"identity_id\": \"%s\",\n\t\t\t\t\t\"method\": \"%s\",\n\t\t\t\t\t\"url\": \"%s\",\n\t\t\t\t\t\"cookies\": {\n\t\t\t\t\t\t\"Some-Cookie-1\": \"Some-Cookie-Value\",\n\t\t\t\t\t\t\"Some-Cookie-2\": \"Some-other-Cookie-Value\",\n\t\t\t\t\t\t\"Some-Cookie-3\": \"Third-Cookie-Value\"\n\t\t\t\t\t},\n\t\t\t\t\t\"transient_payload\": %s\n\t\t\t\t}`, f.GetID(), s.Identity.ID, req.Method, \"http://www.ory.sh/some_end_point\", string(tp))\n\t\tif len(req.Header) != 0 {\n\t\t\tif ua := req.Header.Get(\"User-Agent\"); ua != \"\" {\n\t\t\t\tbody, _ = sjson.Set(body, \"headers.User-Agent\", []string{ua})\n\t\t\t}\n\t\t}\n\n\t\treturn body\n\t}\n\n\tbodyWithFlowAndIdentityAndSessionAndTransientPayload := func(req *http.Request, f flow.Flow, s *session.Session, tp json.RawMessage) string {\n\t\tbody := fmt.Sprintf(`{\n\t\t\t\t\t\"flow_id\": \"%s\",\n\t\t\t\t\t\"identity_id\": \"%s\",\n\t\t\t\t\t\"session_id\": \"%s\",\n\t\t\t\t\t\"method\": \"%s\",\n\t\t\t\t\t\"url\": \"%s\",\n\t\t\t\t\t\"cookies\": {\n\t\t\t\t\t\t\"Some-Cookie-1\": \"Some-Cookie-Value\",\n\t\t\t\t\t\t\"Some-Cookie-2\": \"Some-other-Cookie-Value\",\n\t\t\t\t\t\t\"Some-Cookie-3\": \"Third-Cookie-Value\"\n\t\t\t\t\t},\n\t\t\t\t\t\"transient_payload\": %s\n\t\t\t\t}`, f.GetID(), s.Identity.ID, s.ID, req.Method, \"http://www.ory.sh/some_end_point\", string(tp))\n\t\tif len(req.Header) != 0 {\n\t\t\tif ua := req.Header.Get(\"User-Agent\"); ua != \"\" {\n\t\t\t\tbody, _ = sjson.Set(body, \"headers.User-Agent\", []string{ua})\n\t\t\t}\n\t\t}\n\n\t\treturn body\n\t}\n\n\tfor _, tc := range []struct {\n\t\tuc           string\n\t\tcallWebHook  func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error\n\t\texpectedBody func(req *http.Request, f flow.Flow, s *session.Session) string\n\t\tcreateFlow   func() flow.Flow\n\t}{\n\t\t{\n\t\t\tuc:         \"Pre Login Hook\",\n\t\t\tcreateFlow: func() flow.Flow { return &login.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, _ *session.Session) error {\n\t\t\t\treturn wh.ExecuteLoginPreHook(nil, req, f.(*login.Flow))\n\t\t\t},\n\t\t\texpectedBody: func(req *http.Request, f flow.Flow, _ *session.Session) string {\n\t\t\t\treturn bodyWithFlowOnly(req, f)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Login Hook\",\n\t\t\tcreateFlow: func() flow.Flow { return &login.Flow{ID: x.NewUUID(), TransientPayload: transientPayload} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecuteLoginPostHook(nil, req, node.PasswordGroup, f.(*login.Flow), s)\n\t\t\t},\n\t\t\texpectedBody: func(req *http.Request, f flow.Flow, s *session.Session) string {\n\t\t\t\treturn bodyWithFlowAndIdentityAndSessionAndTransientPayload(req, f, s, transientPayload)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tuc:         \"Pre Registration Hook\",\n\t\t\tcreateFlow: func() flow.Flow { return &registration.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, _ *session.Session) error {\n\t\t\t\treturn wh.ExecuteRegistrationPreHook(nil, req, f.(*registration.Flow))\n\t\t\t},\n\t\t\texpectedBody: func(req *http.Request, f flow.Flow, _ *session.Session) string {\n\t\t\t\treturn bodyWithFlowOnly(req, f)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tuc: \"Post Registration Hook\",\n\t\t\tcreateFlow: func() flow.Flow {\n\t\t\t\treturn &registration.Flow{\n\t\t\t\t\tID:               x.NewUUID(),\n\t\t\t\t\tTransientPayload: transientPayload,\n\t\t\t\t}\n\t\t\t},\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostRegistrationPostPersistHook(nil, req, f.(*registration.Flow), s)\n\t\t\t},\n\t\t\texpectedBody: func(req *http.Request, f flow.Flow, s *session.Session) string {\n\t\t\t\treturn bodyWithFlowAndIdentityAndTransientPayload(req, f, s, transientPayload)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Recovery Hook\",\n\t\t\tcreateFlow: func() flow.Flow { return &recovery.Flow{ID: x.NewUUID(), TransientPayload: transientPayload} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostRecoveryHook(nil, req, f.(*recovery.Flow), s)\n\t\t\t},\n\t\t\texpectedBody: func(req *http.Request, f flow.Flow, s *session.Session) string {\n\t\t\t\treturn bodyWithFlowAndIdentityAndTransientPayload(req, f, s, transientPayload)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Verification Hook\",\n\t\t\tcreateFlow: func() flow.Flow { return &verification.Flow{ID: x.NewUUID(), TransientPayload: transientPayload} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostVerificationHook(nil, req, f.(*verification.Flow), s.Identity)\n\t\t\t},\n\t\t\texpectedBody: func(req *http.Request, f flow.Flow, s *session.Session) string {\n\t\t\t\treturn bodyWithFlowAndIdentityAndTransientPayload(req, f, s, transientPayload)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Settings Hook\",\n\t\t\tcreateFlow: func() flow.Flow { return &settings.Flow{ID: x.NewUUID(), TransientPayload: transientPayload} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecuteSettingsPostPersistHook(nil, req, f.(*settings.Flow), s.Identity, s)\n\t\t\t},\n\t\t\texpectedBody: func(req *http.Request, f flow.Flow, s *session.Session) string {\n\t\t\t\treturn bodyWithFlowAndIdentityAndSessionAndTransientPayload(req, f, s, transientPayload)\n\t\t\t},\n\t\t},\n\t} {\n\t\ttc := tc\n\t\tt.Run(\"uc=\"+tc.uc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tfor _, auth := range []struct {\n\t\t\t\tuc             string\n\t\t\t\tauthConfig     request.AuthConfig\n\t\t\t\texpectedHeader func(header http.Header)\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tuc:             \"no auth\",\n\t\t\t\t\tauthConfig:     request.AuthConfig{},\n\t\t\t\t\texpectedHeader: func(header http.Header) {},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tuc: \"api key in header\",\n\t\t\t\t\tauthConfig: request.AuthConfig{Type: \"api_key\", Config: map[string]any{\n\t\t\t\t\t\t\"name\":  \"My-Key\",\n\t\t\t\t\t\t\"value\": \"My-Key-Value\",\n\t\t\t\t\t\t\"in\":    \"header\",\n\t\t\t\t\t}},\n\t\t\t\t\texpectedHeader: func(header http.Header) {\n\t\t\t\t\t\theader.Set(\"My-Key\", \"My-Key-Value\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tuc: \"api key in cookie\",\n\t\t\t\t\tauthConfig: request.AuthConfig{Type: \"api_key\", Config: map[string]any{\n\t\t\t\t\t\t\"name\":  \"My-Key\",\n\t\t\t\t\t\t\"value\": \"My-Key-Value\",\n\t\t\t\t\t\t\"in\":    \"cookie\",\n\t\t\t\t\t}},\n\t\t\t\t\texpectedHeader: func(header http.Header) {\n\t\t\t\t\t\theader.Set(\"Cookie\", \"My-Key=My-Key-Value\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tuc: \"basic auth\",\n\t\t\t\t\tauthConfig: request.AuthConfig{Type: \"basic_auth\", Config: map[string]any{\n\t\t\t\t\t\t\"user\":     \"My-User\",\n\t\t\t\t\t\t\"password\": \"Super-Secret\",\n\t\t\t\t\t}},\n\t\t\t\t\texpectedHeader: func(header http.Header) {\n\t\t\t\t\t\theader.Set(\"Authorization\", \"Basic \"+base64.StdEncoding.EncodeToString([]byte(\"My-User:Super-Secret\")))\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"auth=\"+auth.uc, func(t *testing.T) {\n\t\t\t\t\tfor _, method := range []string{\"CONNECT\", \"DELETE\", \"GET\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\", \"TRACE\", \"GARBAGE\"} {\n\t\t\t\t\t\tt.Run(\"method=\"+method, func(t *testing.T) {\n\t\t\t\t\t\t\tf := tc.createFlow()\n\t\t\t\t\t\t\treq := (&http.Request{\n\t\t\t\t\t\t\t\tHost: \"www.ory.sh\",\n\t\t\t\t\t\t\t\tHeader: map[string][]string{\n\t\t\t\t\t\t\t\t\t\"Some-Header\":    {\"Some-Value\"},\n\t\t\t\t\t\t\t\t\t\"User-Agent\":     {\"Foo-Bar-Browser\"},\n\t\t\t\t\t\t\t\t\t\"Invalid-Header\": {\"should be ignored\"},\n\t\t\t\t\t\t\t\t\t\"Valid-Header\":   {\"should not be ignored\"},\n\t\t\t\t\t\t\t\t\t\"Cookie\":         {\"Some-Cookie-1=Some-Cookie-Value; Some-Cookie-2=Some-other-Cookie-Value\", \"Some-Cookie-3=Third-Cookie-Value\"},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tRequestURI: \"/some_end_point\",\n\t\t\t\t\t\t\t\tMethod:     http.MethodPost,\n\t\t\t\t\t\t\t\tURL:        &url.URL{Path: \"/some_end_point\"},\n\t\t\t\t\t\t\t}).WithContext(ctx)\n\t\t\t\t\t\t\tcookie, err := req.Cookie(\"Some-Cookie-1\")\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\trequire.Equal(t, cookie.Name, \"Some-Cookie-1\")\n\t\t\t\t\t\t\trequire.Equal(t, cookie.Value, \"Some-Cookie-Value\")\n\t\t\t\t\t\t\tcookie, err = req.Cookie(\"Some-Cookie-2\")\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\trequire.Equal(t, cookie.Name, \"Some-Cookie-2\")\n\t\t\t\t\t\t\trequire.Equal(t, cookie.Value, \"Some-other-Cookie-Value\")\n\t\t\t\t\t\t\tcookie, err = req.Cookie(\"Some-Cookie-3\")\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\trequire.Equal(t, cookie.Name, \"Some-Cookie-3\")\n\t\t\t\t\t\t\trequire.Equal(t, cookie.Value, \"Third-Cookie-Value\")\n\n\t\t\t\t\t\t\ts := &session.Session{ID: x.NewUUID(), Identity: &identity.Identity{ID: x.NewUUID()}}\n\t\t\t\t\t\t\twhr := &WebHookRequest{}\n\t\t\t\t\t\t\tts := newServer(webHookEndPoint(whr))\n\n\t\t\t\t\t\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\t\t\t\t\t\tMethod:      method,\n\t\t\t\t\t\t\t\tURL:         ts.URL + path,\n\t\t\t\t\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t\t\t\t\t\tAuth:        auth.authConfig,\n\t\t\t\t\t\t\t})\n\n\t\t\t\t\t\t\terr = tc.callWebHook(wh, req, f, s)\n\t\t\t\t\t\t\tif method == \"GARBAGE\" {\n\t\t\t\t\t\t\t\tassert.Error(t, err)\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tassert.NoError(t, err)\n\n\t\t\t\t\t\t\tassert.Equal(t, method, whr.Method)\n\n\t\t\t\t\t\t\texpectedHeader := http.Header{}\n\t\t\t\t\t\t\texpectedHeader.Set(\"Content-Type\", \"application/json\")\n\t\t\t\t\t\t\tauth.expectedHeader(expectedHeader)\n\t\t\t\t\t\t\tfor k, v := range expectedHeader {\n\t\t\t\t\t\t\t\tvals := whr.Headers.Values(k)\n\t\t\t\t\t\t\t\tassert.Equal(t, v, vals)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tassert.NotZero(t, whr.Headers.Get(\"Ory-Webhook-Request-ID\"))\n\t\t\t\t\t\t\tassert.NotZero(t, whr.Headers.Get(\"Ory-Webhook-Trigger-ID\"))\n\n\t\t\t\t\t\t\tif method != \"TRACE\" {\n\t\t\t\t\t\t\t\t// According to the HTTP spec any request method, but TRACE is allowed to\n\t\t\t\t\t\t\t\t// have a body. Even this is a really bad practice for some of them, like for\n\t\t\t\t\t\t\t\t// GET\n\t\t\t\t\t\t\t\tassert.Zero(t, gjson.Get(whr.Body, \"headers.Invalid-Header\"))\n\t\t\t\t\t\t\t\tassert.NotZero(t, gjson.Get(whr.Body, \"headers.Valid-Header\"))\n\t\t\t\t\t\t\t\twhr.Body, err = sjson.Delete(whr.Body, \"headers.Valid-Header\")\n\t\t\t\t\t\t\t\tassert.NoError(t, err)\n\n\t\t\t\t\t\t\t\tassert.JSONEq(t, tc.expectedBody(req, f, s), whr.Body)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tassert.Emptyf(t, whr.Body, \"HTTP %s is not allowed to have a body\", method)\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})\n\t\t\t}\n\t\t})\n\t}\n\n\twebHookResponse := []byte(\n\t\t`{\n\t\t\t\"messages\": [{\n\t\t\t\t\"instance_ptr\": \"#/traits/username\",\n\t\t\t\t\"messages\": [{\n\t\t\t\t\t\"id\": 1234,\n\t\t\t\t\t\"text\": \"error message\",\n\t\t\t\t\t\"type\": \"info\"\n\t\t\t\t}]\n\t\t\t}]\n\t\t}`,\n\t)\n\n\twebhookError := schema.NewValidationListError([]*schema.ValidationError{schema.NewHookValidationError(\"#/traits/username\", \"a webhook target returned an error\", text.Messages{{ID: 1234, Type: \"info\", Text: \"error message\"}})})\n\tfor _, tc := range []struct {\n\t\tuc              string\n\t\tcallWebHook     func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error\n\t\twebHookResponse func() (int, []byte)\n\t\tcreateFlow      func() flow.Flow\n\t\texpectedError   error\n\t}{\n\t\t{\n\t\t\tuc:         \"Pre Login Hook - no block\",\n\t\t\tcreateFlow: func() flow.Flow { return &login.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, _ *session.Session) error {\n\t\t\t\treturn wh.ExecuteLoginPreHook(nil, req, f.(*login.Flow))\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusOK, []byte{}\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Pre Login Hook - block\",\n\t\t\tcreateFlow: func() flow.Flow { return &login.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, _ *session.Session) error {\n\t\t\t\treturn wh.ExecuteLoginPreHook(nil, req, f.(*login.Flow))\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusBadRequest, webHookResponse\n\t\t\t},\n\t\t\texpectedError: webhookError,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Login Hook - no block\",\n\t\t\tcreateFlow: func() flow.Flow { return &login.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecuteLoginPostHook(nil, req, node.PasswordGroup, f.(*login.Flow), s)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusOK, []byte{}\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Login Hook - block\",\n\t\t\tcreateFlow: func() flow.Flow { return &login.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecuteLoginPostHook(nil, req, node.PasswordGroup, f.(*login.Flow), s)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusBadRequest, webHookResponse\n\t\t\t},\n\t\t\texpectedError: webhookError,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Pre Registration Hook - no block\",\n\t\t\tcreateFlow: func() flow.Flow { return &registration.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, _ *session.Session) error {\n\t\t\t\treturn wh.ExecuteRegistrationPreHook(nil, req, f.(*registration.Flow))\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusOK, []byte{}\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Pre Registration Hook - block\",\n\t\t\tcreateFlow: func() flow.Flow { return &registration.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, _ *session.Session) error {\n\t\t\t\treturn wh.ExecuteRegistrationPreHook(nil, req, f.(*registration.Flow))\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusBadRequest, webHookResponse\n\t\t\t},\n\t\t\texpectedError: webhookError,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Registration Post Persist Hook - no block\",\n\t\t\tcreateFlow: func() flow.Flow { return &registration.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostRegistrationPostPersistHook(nil, req, f.(*registration.Flow), s)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusOK, []byte{}\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Registration Post Persist Hook - block has no effect\",\n\t\t\tcreateFlow: func() flow.Flow { return &registration.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostRegistrationPostPersistHook(nil, req, f.(*registration.Flow), s)\n\t\t\t},\n\t\t\t// This would usually error, but post persist does not execute blocking web hooks, so we expect no error.\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusBadRequest, webHookResponse\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Registration Pre Persist Hook - no block\",\n\t\t\tcreateFlow: func() flow.Flow { return &registration.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostRegistrationPrePersistHook(nil, req, f.(*registration.Flow), s.Identity)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusOK, []byte{}\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Registration Pre Persist Hook - block\",\n\t\t\tcreateFlow: func() flow.Flow { return &registration.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostRegistrationPrePersistHook(nil, req, f.(*registration.Flow), s.Identity)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusBadRequest, webHookResponse\n\t\t\t},\n\t\t\texpectedError: webhookError,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Recovery Hook - no block\",\n\t\t\tcreateFlow: func() flow.Flow { return &recovery.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostRecoveryHook(nil, req, f.(*recovery.Flow), s)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusOK, []byte{}\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Recovery Hook - block\",\n\t\t\tcreateFlow: func() flow.Flow { return &recovery.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostRecoveryHook(nil, req, f.(*recovery.Flow), s)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusBadRequest, webHookResponse\n\t\t\t},\n\t\t\texpectedError: webhookError,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Verification Hook - no block\",\n\t\t\tcreateFlow: func() flow.Flow { return &verification.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostVerificationHook(nil, req, f.(*verification.Flow), s.Identity)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusOK, []byte{}\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Verification Hook - block\",\n\t\t\tcreateFlow: func() flow.Flow { return &verification.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecutePostVerificationHook(nil, req, f.(*verification.Flow), s.Identity)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusBadRequest, webHookResponse\n\t\t\t},\n\t\t\texpectedError: webhookError,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Settings Hook - no block\",\n\t\t\tcreateFlow: func() flow.Flow { return &settings.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecuteSettingsPostPersistHook(nil, req, f.(*settings.Flow), s.Identity, s)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusOK, []byte{}\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Settings Hook Pre Persist - block\",\n\t\t\tcreateFlow: func() flow.Flow { return &settings.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecuteSettingsPrePersistHook(nil, req, f.(*settings.Flow), s.Identity, s)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusBadRequest, webHookResponse\n\t\t\t},\n\t\t\texpectedError: webhookError,\n\t\t},\n\t\t{\n\t\t\tuc:         \"Post Settings Hook Post Persist - block has no effect\",\n\t\t\tcreateFlow: func() flow.Flow { return &settings.Flow{ID: x.NewUUID()} },\n\t\t\tcallWebHook: func(wh *hook.WebHook, req *http.Request, f flow.Flow, s *session.Session) error {\n\t\t\t\treturn wh.ExecuteSettingsPostPersistHook(nil, req, f.(*settings.Flow), s.Identity, s)\n\t\t\t},\n\t\t\twebHookResponse: func() (int, []byte) {\n\t\t\t\treturn http.StatusBadRequest, webHookResponse\n\t\t\t},\n\t\t\texpectedError: nil,\n\t\t},\n\t} {\n\t\ttc := tc\n\t\tt.Run(\"uc=\"+tc.uc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tfor _, method := range []string{\"CONNECT\", \"DELETE\", \"GET\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\", \"TRACE\"} {\n\t\t\t\tt.Run(\"method=\"+method, func(t *testing.T) {\n\t\t\t\t\tf := tc.createFlow()\n\t\t\t\t\treq := &http.Request{\n\t\t\t\t\t\tHost: \"www.ory.sh\",\n\t\t\t\t\t\tHeader: map[string][]string{\n\t\t\t\t\t\t\t\"Some-Header\":       {\"Some-Value\"},\n\t\t\t\t\t\t\t\"X-Forwarded-Proto\": {\"https\"},\n\t\t\t\t\t\t\t\"Cookie\":            {\"Some-Cookie-1=Some-Cookie-Value; Some-Cookie-2=Some-other-Cookie-Value\", \"Some-Cookie-3=Third-Cookie-Value\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRequestURI: \"/some_end_point\",\n\t\t\t\t\t\tMethod:     http.MethodPost,\n\t\t\t\t\t\tURL: &url.URL{\n\t\t\t\t\t\t\tPath: \"some_end_point\",\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t\ts := &session.Session{ID: x.NewUUID(), Identity: &identity.Identity{ID: x.NewUUID()}}\n\t\t\t\t\tcode, res := tc.webHookResponse()\n\t\t\t\t\tts := newServer(webHookHttpCodeWithBodyEndPoint(t, code, res))\n\n\t\t\t\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\t\t\t\tMethod:       method,\n\t\t\t\t\t\tURL:          ts.URL + path,\n\t\t\t\t\t\tTemplateURI:  \"file://./stub/test_body.jsonnet\",\n\t\t\t\t\t\tCanInterrupt: true,\n\t\t\t\t\t})\n\n\t\t\t\t\terr := tc.callWebHook(wh, req, f, s)\n\t\t\t\t\tif tc.expectedError == nil {\n\t\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tvar validationError *schema.ValidationListError\n\t\t\t\t\tvar expectedError *schema.ValidationListError\n\t\t\t\t\tif assert.ErrorAs(t, err, &validationError) && assert.ErrorAs(t, tc.expectedError, &expectedError) {\n\t\t\t\t\t\tassert.Equal(t, expectedError, validationError)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"update identity fields\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\trun := func(t *testing.T, id identity.Identity, responseCode int, response []byte) *identity.WithCredentialsAndAdminMetadataInJSON {\n\t\t\tf := &registration.Flow{ID: x.NewUUID()}\n\t\t\treq := &http.Request{\n\t\t\t\tHost:       \"www.ory.sh\",\n\t\t\t\tHeader:     map[string][]string{},\n\t\t\t\tRequestURI: \"/some_end_point\",\n\t\t\t\tMethod:     http.MethodPost,\n\t\t\t\tURL:        &url.URL{Path: \"some_end_point\"},\n\t\t\t}\n\t\t\tts := newServer(webHookHttpCodeWithBodyEndPoint(t, responseCode, response))\n\t\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\t\tMethod:      \"POST\",\n\t\t\t\tURL:         ts.URL + path,\n\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t\tResponse: request.ResponseConfig{\n\t\t\t\t\tParse: true,\n\t\t\t\t},\n\t\t\t})\n\t\t\tin := &id\n\t\t\terr := wh.ExecutePostRegistrationPrePersistHook(nil, req, f, in)\n\t\t\trequire.NoError(t, err)\n\t\t\tresult := identity.WithCredentialsAndAdminMetadataInJSON(*in)\n\t\t\treturn &result\n\t\t}\n\n\t\tt.Run(\"case=update identity fields\", func(t *testing.T) {\n\t\t\texpected := identity.Identity{\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tType:        \"password\",\n\t\t\t\t\t\tIdentifiers: []string{\"test\"},\n\t\t\t\t\t\tConfig:      []byte(`{\"hashed_password\":\"$argon2id$v=19$m=65536,t=1,p=1$Z3JlZW5hbmRlcnNlY3JldA$Z3JlZW5hbmRlcnNlY3JldA\"}`),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tExternalID: \"original-external-id\",\n\t\t\t\tSchemaID:   \"default\",\n\t\t\t\tSchemaURL:  \"file://stub/default.schema.json\",\n\t\t\t\tState:      identity.StateActive,\n\t\t\t\tTraits:     []byte(`{\"email\":\"some@example.org\"}`),\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{{\n\t\t\t\t\tValue:    \"some@example.org\",\n\t\t\t\t\tVerified: false,\n\t\t\t\t\tVia:      \"email\",\n\t\t\t\t\tStatus:   identity.VerifiableAddressStatusPending,\n\t\t\t\t}},\n\t\t\t\tRecoveryAddresses: []identity.RecoveryAddress{{\n\t\t\t\t\tValue: \"some@example.org\",\n\t\t\t\t\tVia:   \"email\",\n\t\t\t\t}},\n\t\t\t\tMetadataPublic: []byte(`{\"public\":\"data\"}`),\n\t\t\t\tMetadataAdmin:  []byte(`{\"admin\":\"data\"}`),\n\t\t\t}\n\n\t\t\tt.Run(\"case=body is empty\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\n\t\t\tt.Run(\"case=identity is present but empty\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{\"identity\":{}}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\n\t\t\tt.Run(\"case=identity has updated traits\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{\"identity\":{\"traits\":{\"email\":\"some@other-example.org\"}}}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\n\t\t\tt.Run(\"case=identity has updated state\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{\"identity\":{\"state\":\"inactive\"}}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\n\t\t\tt.Run(\"case=identity has updated public metadata\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{\"identity\":{\"metadata_public\":{\"useful\":\"metadata\"}}}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\n\t\t\tt.Run(\"case=identity has updated admin metadata\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{\"identity\":{\"metadata_admin\":{\"useful\":\"admin metadata\"}}}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\n\t\t\tt.Run(\"case=identity has updated verified addresses\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{\"identity\":{\"traits\":{\"email\":\"some@other-example.org\"},\"verifiable_addresses\":[{\"value\":\"some@other-example.org\",\"via\":\"email\"}]}}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\n\t\t\tt.Run(\"case=identity has updated recovery addresses\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{\"identity\":{\"traits\":{\"email\":\"some@other-example.org\"},\"recovery_addresses\":[{\"value\":\"some@other-example.org\",\"via\":\"email\"}]}}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\n\t\t\tt.Run(\"case=identity has updated external_id\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{\"identity\":{\"external_id\":\"updated-external-id\"}}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\n\t\t\tt.Run(\"case=unset external_id\", func(t *testing.T) {\n\t\t\t\tactual := run(t, expected, http.StatusOK, []byte(`{\"identity\":{\"external_id\":\"\"}}`))\n\t\t\t\tsnapshotx.SnapshotT(t, &actual)\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"cannot have parse and ignore both set\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tts := newServer(webHookHttpCodeEndPoint(200))\n\t\treq := &http.Request{\n\t\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\t\tHost:   \"www.ory.sh\",\n\t\t\tTLS:    new(tls.ConnectionState),\n\t\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\n\t\t\tMethod: http.MethodPost,\n\t\t}\n\t\tf := &login.Flow{ID: x.NewUUID()}\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tMethod:      \"GET\",\n\t\t\tURL:         ts.URL + path,\n\t\t\tTemplateURI: \"./stub/test_body.jsonnet\",\n\t\t\tResponse: request.ResponseConfig{\n\t\t\t\tIgnore: true,\n\t\t\t\tParse:  true,\n\t\t\t},\n\t\t})\n\n\t\terr := wh.ExecuteLoginPreHook(nil, req, f)\n\t\tassert.Error(t, err)\n\t})\n\n\tfor _, tc := range []struct {\n\t\tuc    string\n\t\tparse bool\n\t}{\n\t\t{uc: \"Post Settings Hook - parse true\", parse: true},\n\t\t{uc: \"Post Settings Hook - parse false\", parse: false},\n\t} {\n\t\tt.Run(\"uc=\"+tc.uc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tts := newServer(webHookHttpCodeWithBodyEndPoint(t, 200, []byte(`{\"identity\":{\"traits\":{\"email\":\"some@other-example.org\"}}}`)))\n\t\t\treq := &http.Request{\n\t\t\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\t\t\tHost:   \"www.ory.sh\",\n\t\t\t\tTLS:    new(tls.ConnectionState),\n\t\t\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\n\t\t\t\tMethod: http.MethodPost,\n\t\t\t}\n\t\t\tf := &settings.Flow{ID: x.NewUUID()}\n\t\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\t\tMethod:      \"POST\",\n\t\t\t\tURL:         ts.URL + path,\n\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t\tResponse: request.ResponseConfig{\n\t\t\t\t\tParse: tc.parse,\n\t\t\t\t},\n\t\t\t})\n\t\t\tuuid := x.NewUUID()\n\t\t\tin := &identity.Identity{ID: uuid}\n\t\t\ts := &session.Session{ID: x.NewUUID(), Identity: in}\n\n\t\t\tpostPersistErr := wh.ExecuteSettingsPostPersistHook(nil, req, f, in, s)\n\t\t\tassert.NoError(t, postPersistErr)\n\t\t\tassert.Equal(t, in, &identity.Identity{ID: uuid})\n\n\t\t\tprePersistErr := wh.ExecuteSettingsPrePersistHook(nil, req, f, in, s)\n\t\t\tassert.NoError(t, prePersistErr)\n\t\t\tif tc.parse == true {\n\t\t\t\tassert.Equal(t, in, &identity.Identity{ID: uuid, Traits: identity.Traits(`{\"email\":\"some@other-example.org\"}`)})\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, in, &identity.Identity{ID: uuid})\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"must error when template is erroneous\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tts := newServer(webHookHttpCodeEndPoint(200))\n\t\treq := &http.Request{\n\t\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\t\tHost:   \"www.ory.sh\",\n\t\t\tTLS:    new(tls.ConnectionState),\n\t\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\t\t\tMethod: http.MethodPost,\n\t\t}\n\t\tf := &login.Flow{ID: x.NewUUID()}\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tMethod:      \"POST\",\n\t\t\tURL:         ts.URL + path,\n\t\t\tTemplateURI: \"file://./stub/bad_template.jsonnet\",\n\t\t})\n\n\t\terr := wh.ExecuteLoginPreHook(nil, req, f)\n\t\tassert.Error(t, err)\n\t})\n\n\tt.Run(\"must not error when template is erroneous and responses are ignored\", func(t *testing.T) {\n\t\tts := newServer(webHookHttpCodeEndPoint(200))\n\t\treq := &http.Request{\n\t\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\t\tHost:   \"www.ory.sh\",\n\t\t\tTLS:    new(tls.ConnectionState),\n\t\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\t\t\tMethod: http.MethodPost,\n\t\t}\n\t\tf := &login.Flow{ID: x.NewUUID()}\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tMethod:      \"GET\",\n\t\t\tURL:         ts.URL + path,\n\t\t\tTemplateURI: \"file://./stub/bad_template.jsonnet\",\n\t\t\tResponse: request.ResponseConfig{\n\t\t\t\tIgnore: true,\n\t\t\t},\n\t\t})\n\n\t\terr := wh.ExecuteLoginPreHook(nil, req, f)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"must not make request\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\treq := &http.Request{\n\t\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\t\tHost:   \"www.ory.sh\",\n\t\t\tTLS:    new(tls.ConnectionState),\n\t\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\n\t\t\tMethod: http.MethodPost,\n\t\t}\n\t\tf := &login.Flow{ID: x.NewUUID()}\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tMethod:      \"POST\",\n\t\t\tURL:         \"https://i-do-not-exist/\",\n\t\t\tTemplateURI: \"file://./stub/cancel_template.jsonnet\",\n\t\t})\n\n\t\terr := wh.ExecuteLoginPreHook(nil, req, f)\n\t\tassert.NoError(t, err)\n\t})\n\n\tboolToString := func(f bool) string {\n\t\tif f {\n\t\t\treturn \" not\"\n\t\t} else {\n\t\t\treturn \"\"\n\t\t}\n\t}\n\n\tt.Run(\"ignores the response and is async\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(1)\n\t\twaitTime := time.Millisecond * 100\n\t\tts := newServer(func(w http.ResponseWriter, _ *http.Request) {\n\t\t\tdefer wg.Done()\n\t\t\ttime.Sleep(waitTime)\n\t\t\tw.WriteHeader(http.StatusBadRequest)\n\t\t})\n\n\t\treq := &http.Request{\n\t\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\t\tHost:   \"www.ory.sh\",\n\t\t\tTLS:    new(tls.ConnectionState),\n\t\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\t\t\tMethod: http.MethodPost,\n\t\t}\n\t\tf := &login.Flow{ID: x.NewUUID()}\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tMethod:      \"GET\",\n\t\t\tURL:         ts.URL + path,\n\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\tResponse: request.ResponseConfig{\n\t\t\t\tIgnore: true,\n\t\t\t},\n\t\t})\n\n\t\tstart := time.Now()\n\t\terr := wh.ExecuteLoginPreHook(nil, req, f)\n\t\tassert.NoError(t, err)\n\t\tassert.Less(t, time.Since(start), waitTime)\n\n\t\twg.Wait()\n\t})\n\n\tt.Run(\"does not error on 500 request with retry\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// This test essentially ensures that we do not regress on the bug we had where 500 status code\n\t\t// would cause a retry, but because the body was incorrectly set we ended up with a ContentLength\n\t\t// error.\n\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(3) // HTTP client does 3 attempts\n\t\tts := newServer(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tdefer wg.Done()\n\t\t\tw.WriteHeader(500)\n\t\t\t_, _ = w.Write([]byte(`{\"error\":\"some error\"}`))\n\t\t})\n\n\t\treq := &http.Request{\n\t\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\t\tHost:   \"www.ory.sh\",\n\t\t\tTLS:    new(tls.ConnectionState),\n\t\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\t\t\tMethod: http.MethodPost,\n\t\t}\n\t\tf := &login.Flow{ID: x.NewUUID()}\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tMethod:      \"GET\",\n\t\t\tURL:         ts.URL + path,\n\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t})\n\n\t\terr := wh.ExecuteLoginPreHook(nil, req, f)\n\t\trequire.Error(t, err)\n\t\tassert.NotContains(t, err.Error(), \"ContentLength\")\n\n\t\twg.Wait()\n\t})\n\n\tfor _, tc := range []struct {\n\t\tcode        int\n\t\tmustSuccess bool\n\t}{\n\t\t{200, true},\n\t\t{299, true},\n\t\t{300, true},\n\t\t{399, true},\n\t\t{400, false},\n\t\t{499, false},\n\t\t{500, false},\n\t\t{599, false},\n\t} {\n\t\ttc := tc\n\t\tt.Run(\"Must\"+boolToString(tc.mustSuccess)+\" error when end point is returning \"+strconv.Itoa(tc.code), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tts := newServer(webHookHttpCodeEndPoint(tc.code))\n\t\t\treq := &http.Request{\n\t\t\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\t\t\tHost:   \"www.ory.sh\",\n\t\t\t\tTLS:    new(tls.ConnectionState),\n\t\t\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\t\t\t\tMethod: http.MethodPost,\n\t\t\t}\n\t\t\tf := &login.Flow{ID: x.NewUUID()}\n\t\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\t\tMethod:      \"POST\",\n\t\t\t\tURL:         ts.URL + path,\n\t\t\t\tTemplateURI: \"file://./stub/test_body.jsonnet\",\n\t\t\t})\n\n\t\t\terr := wh.ExecuteLoginPreHook(nil, req, f)\n\t\t\tif tc.mustSuccess {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t} else {\n\t\t\t\tassert.Error(t, err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDisallowPrivateIPRanges(t *testing.T) {\n\tt.Parallel()\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeyClientHTTPNoPrivateIPRanges, true)\n\tconf.MustSet(ctx, config.ViperKeyClientHTTPPrivateIPExceptionURLs, []string{\"http://localhost/exception\"})\n\tlogger := logrusx.New(\"kratos\", \"test\")\n\twhDeps := struct {\n\t\tx.BasicRegistry\n\t\t*jsonnetsecure.TestProvider\n\t\tconfig.Provider\n\t}{\n\t\tx.BasicRegistry{L: logger, C: reg.HTTPClient(context.Background()), T: otelx.NewNoop()},\n\t\tjsonnetsecure.NewTestProvider(t),\n\t\treg,\n\t}\n\n\treq := &http.Request{\n\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\tHost:   \"www.ory.sh\",\n\t\tTLS:    new(tls.ConnectionState),\n\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\t\tMethod: http.MethodPost,\n\t}\n\ts := &session.Session{ID: x.NewUUID(), Identity: &identity.Identity{ID: x.NewUUID()}}\n\tf := &login.Flow{ID: x.NewUUID()}\n\n\tt.Run(\"not allowed to call url\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tURL:         \"https://localhost:1234/\",\n\t\t\tMethod:      \"GET\",\n\t\t\tTemplateURI: \"file://stub/test_body.jsonnet\",\n\t\t})\n\t\terr := wh.ExecuteLoginPostHook(nil, req, node.DefaultGroup, f, s)\n\t\tassert.ErrorContains(t, err, \"is not a permitted destination\")\n\t})\n\n\tt.Run(\"allowed to call exempt url\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tURL:         \"http://localhost/exception\",\n\t\t\tMethod:      \"GET\",\n\t\t\tTemplateURI: \"file://stub/test_body.jsonnet\",\n\t\t})\n\t\terr := wh.ExecuteLoginPostHook(nil, req, node.DefaultGroup, f, s)\n\t\trequire.Error(t, err, \"the target does not exist and we still receive an error\")\n\t\trequire.NotContains(t, err.Error(), \"is not a permitted destination\", \"but the error is not related to the IP range.\")\n\t})\n\n\tt.Run(\"not allowed to load from source\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\treq := &http.Request{\n\t\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\t\tHost:   \"www.ory.sh\",\n\t\t\tTLS:    new(tls.ConnectionState),\n\t\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\t\t\tMethod: http.MethodPost,\n\t\t}\n\t\ts := &session.Session{ID: x.NewUUID(), Identity: &identity.Identity{ID: x.NewUUID()}}\n\t\tf := &login.Flow{ID: x.NewUUID()}\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tURL:         \"https://www.google.com/\",\n\t\t\tMethod:      \"GET\",\n\t\t\tTemplateURI: \"http://192.168.178.0/test_body.jsonnet\",\n\t\t})\n\t\terr := wh.ExecuteLoginPostHook(nil, req, node.DefaultGroup, f, s)\n\t\trequire.ErrorContains(t, err, \"is not a permitted destination\")\n\t})\n}\n\nfunc TestAsyncWebhook(t *testing.T) {\n\tt.Parallel()\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tlogger := logrusx.New(\"kratos\", \"test\")\n\tlogHook := new(test.Hook)\n\tlogger.Logger.Hooks.Add(logHook)\n\twhDeps := struct {\n\t\tx.BasicRegistry\n\t\t*jsonnetsecure.TestProvider\n\t\tconfig.Provider\n\t}{\n\t\tx.BasicRegistry{L: logger, C: reg.HTTPClient(context.Background()), T: otelx.NewNoop()},\n\t\tjsonnetsecure.NewTestProvider(t),\n\t\treg,\n\t}\n\n\treq := &http.Request{\n\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\tHost:   \"www.ory.sh\",\n\t\tTLS:    new(tls.ConnectionState),\n\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\t\tMethod: http.MethodPost,\n\t}\n\n\tincomingCtx, incomingCancel := context.WithCancel(context.Background())\n\tif deadline, ok := t.Deadline(); ok {\n\t\t// cancel this context one second before test timeout for clean shutdown\n\t\tvar cleanup context.CancelFunc\n\t\tincomingCtx, cleanup = context.WithDeadline(incomingCtx, deadline.Add(-time.Second))\n\t\tdefer cleanup()\n\t}\n\n\treq = req.WithContext(incomingCtx)\n\ts := &session.Session{ID: x.NewUUID(), Identity: &identity.Identity{ID: x.NewUUID()}}\n\tf := &login.Flow{ID: x.NewUUID()}\n\n\thandlerEntered, blockHandlerOnExit := make(chan struct{}), make(chan struct{})\n\twebhookReceiver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tclose(handlerEntered)\n\t\t<-blockHandlerOnExit\n\t\t_, _ = w.Write([]byte(\"ok\"))\n\t}))\n\tt.Cleanup(webhookReceiver.Close)\n\n\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\tURL:         webhookReceiver.URL,\n\t\tMethod:      \"GET\",\n\t\tTemplateURI: \"file://stub/test_body.jsonnet\",\n\t\tResponse: request.ResponseConfig{\n\t\t\tIgnore: true,\n\t\t},\n\t})\n\terr := wh.ExecuteLoginPostHook(nil, req, node.DefaultGroup, f, s)\n\trequire.NoError(t, err) // execution returns immediately for async webhook\n\tselect {\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatal(\"timed out waiting for webhook request to reach test handler\")\n\tcase <-handlerEntered:\n\t\t// ok\n\t}\n\t// at this point, a goroutine is in the middle of the call to our test handler and waiting for a response\n\tincomingCancel() // simulate the incoming Kratos request having finished\n\tclose(blockHandlerOnExit)\n\ttimeout := time.After(1 * time.Second)\n\tvar found bool\n\tfor !found {\n\t\tfor _, entry := range logHook.AllEntries() {\n\t\t\tif entry.Message == \"Webhook request succeeded\" {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tselect {\n\t\tcase <-timeout:\n\t\t\tt.Fatal(\"timed out waiting for successful webhook completion\")\n\t\tcase <-time.After(50 * time.Millisecond):\n\t\t\t// continue loop\n\t\t}\n\t}\n\trequire.True(t, found)\n}\n\nfunc TestWebhookEvents(t *testing.T) {\n\tt.Parallel()\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tlogger := logrusx.New(\"kratos\", \"test\")\n\twhDeps := struct {\n\t\tx.BasicRegistry\n\t\t*jsonnetsecure.TestProvider\n\t\tconfig.Provider\n\t}{\n\t\tx.BasicRegistry{L: logger, C: reg.HTTPClient(context.Background()), T: otelx.NewNoop()},\n\t\tjsonnetsecure.NewTestProvider(t),\n\t\treg,\n\t}\n\n\treq := &http.Request{\n\t\tHeader: map[string][]string{\"Some-Header\": {\"Some-Value\"}},\n\t\tHost:   \"www.ory.sh\",\n\t\tTLS:    new(tls.ConnectionState),\n\t\tURL:    &url.URL{Path: \"/some_end_point\"},\n\t\tMethod: http.MethodPost,\n\t}\n\tf := &login.Flow{ID: x.NewUUID()}\n\n\twebhookReceiver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.URL.Path == \"/ok\" {\n\t\t\tw.WriteHeader(200)\n\t\t\t_, _ = w.Write([]byte(\"ok\"))\n\t\t} else {\n\t\t\tw.WriteHeader(500)\n\t\t\t_, _ = w.Write([]byte(\"fail\"))\n\t\t}\n\t}))\n\tt.Cleanup(webhookReceiver.Close)\n\n\tgetAttributes := func(attrs []attribute.KeyValue) (webhookID, triggerID, requestID string) {\n\t\tfor _, kv := range attrs {\n\t\t\tswitch semconv.AttributeKey(kv.Key) {\n\t\t\tcase events.AttributeKeyWebhookID:\n\t\t\t\twebhookID = kv.Value.Emit()\n\t\t\tcase events.AttributeKeyWebhookTriggerID:\n\t\t\t\ttriggerID = kv.Value.Emit()\n\t\t\tcase events.AttributeKeyWebhookRequestID:\n\t\t\t\trequestID = kv.Value.Emit()\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tt.Run(\"success\", func(t *testing.T) {\n\t\twhID := x.NewUUID()\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tID:          whID.String(),\n\t\t\tURL:         webhookReceiver.URL + \"/ok\",\n\t\t\tMethod:      \"GET\",\n\t\t\tTemplateURI: \"file://stub/test_body.jsonnet\",\n\t\t})\n\n\t\trecorder := tracetest.NewSpanRecorder()\n\t\ttracer := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)).Tracer(\"test\")\n\t\tctx, span := tracer.Start(context.Background(), \"parent\")\n\t\tdefer span.End()\n\n\t\tr1 := req.Clone(ctx)\n\n\t\trequire.NoError(t, wh.ExecuteLoginPreHook(nil, r1, f))\n\n\t\tended := recorder.Ended()\n\t\trequire.NotEmpty(t, ended)\n\n\t\ti := slices.IndexFunc(ended, func(sp sdktrace.ReadOnlySpan) bool { return sp.Name() == \"selfservice.webhook\" })\n\t\trequire.GreaterOrEqual(t, i, 0)\n\n\t\tevs := ended[i].Events()\n\t\ti = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == events.WebhookDelivered.String() })\n\t\trequire.GreaterOrEqual(t, i, 0)\n\n\t\tactualWhID, deliveredTriggerID, deliveredRequestID := getAttributes(evs[i].Attributes)\n\t\trequire.Equal(t, whID.String(), actualWhID)\n\t\trequire.NotEmpty(t, deliveredTriggerID)\n\t\trequire.NotEmpty(t, deliveredRequestID)\n\t\tassert.NotEqual(t, deliveredTriggerID, deliveredRequestID)\n\n\t\ti = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == events.WebhookSucceeded.String() })\n\t\trequire.GreaterOrEqual(t, i, 0)\n\n\t\tactualWhID, succeededTriggerID, _ := getAttributes(evs[i].Attributes)\n\t\trequire.Equal(t, whID.String(), actualWhID)\n\t\trequire.NotEmpty(t, succeededTriggerID)\n\n\t\tassert.Equal(t, deliveredTriggerID, succeededTriggerID)\n\n\t\ti = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == events.WebhookFailed.String() })\n\t\trequire.Equal(t, -1, i)\n\t})\n\n\tt.Run(\"failed\", func(t *testing.T) {\n\t\twhID := x.NewUUID()\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tID:          whID.String(),\n\t\t\tURL:         webhookReceiver.URL + \"/fail\",\n\t\t\tMethod:      \"GET\",\n\t\t\tTemplateURI: \"file://stub/test_body.jsonnet\",\n\t\t})\n\n\t\trecorder := tracetest.NewSpanRecorder()\n\t\ttracer := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)).Tracer(\"test\")\n\t\tctx, span := tracer.Start(context.Background(), \"parent\")\n\t\tdefer span.End()\n\n\t\tr1 := req.Clone(ctx)\n\t\trequire.Error(t, wh.ExecuteLoginPreHook(nil, r1, f))\n\n\t\tended := recorder.Ended()\n\t\trequire.NotEmpty(t, ended)\n\n\t\ti := slices.IndexFunc(ended, func(sp sdktrace.ReadOnlySpan) bool { return sp.Name() == \"selfservice.webhook\" })\n\t\trequire.GreaterOrEqual(t, i, 0)\n\n\t\tevs := ended[i].Events()\n\n\t\tvar deliveredEvents []sdktrace.Event\n\t\tdeliveredTriggerIDs := map[string]struct{}{}\n\t\tdeliveredRequestIDs := map[string]struct{}{}\n\t\tfor _, ev := range evs {\n\t\t\tif ev.Name == events.WebhookDelivered.String() {\n\t\t\t\tdeliveredEvents = append(deliveredEvents, ev)\n\t\t\t\tactualWhID, triggerID, requestID := getAttributes(ev.Attributes)\n\t\t\t\trequire.Equal(t, whID.String(), actualWhID)\n\t\t\t\trequire.NotEmpty(t, triggerID)\n\t\t\t\trequire.NotEmpty(t, requestID)\n\t\t\t\tdeliveredTriggerIDs[triggerID] = struct{}{}\n\t\t\t\tdeliveredRequestIDs[requestID] = struct{}{}\n\t\t\t}\n\t\t}\n\n\t\tassert.Len(t, deliveredEvents, 3)\n\t\tassert.Len(t, deliveredTriggerIDs, 1)\n\t\tassert.Len(t, deliveredRequestIDs, 3)\n\n\t\ti = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == \"WebhookFailed\" })\n\t\trequire.GreaterOrEqual(t, i, 0)\n\n\t\tactualWhID, failedTriggerID, _ := getAttributes(evs[i].Attributes)\n\t\trequire.Equal(t, whID.String(), actualWhID)\n\t\trequire.NotEmpty(t, failedTriggerID)\n\n\t\tassert.Contains(t, deliveredTriggerIDs, failedTriggerID)\n\n\t\ti = slices.IndexFunc(evs, func(ev sdktrace.Event) bool { return ev.Name == \"WebhookSucceeded\" })\n\t\trequire.Equal(t, i, -1)\n\t})\n\n\tt.Run(\"event disabled\", func(t *testing.T) {\n\t\twh := hook.NewWebHook(&whDeps, &request.Config{\n\t\t\tURL:                webhookReceiver.URL + \"/fail\",\n\t\t\tMethod:             \"GET\",\n\t\t\tTemplateURI:        \"file://stub/test_body.jsonnet\",\n\t\t\tEmitAnalyticsEvent: new(false),\n\t\t})\n\n\t\trecorder := tracetest.NewSpanRecorder()\n\t\ttracer := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder)).Tracer(\"test\")\n\t\tctx, span := tracer.Start(context.Background(), \"parent\")\n\t\tdefer span.End()\n\n\t\tr1 := req.Clone(ctx)\n\t\trequire.Error(t, wh.ExecuteLoginPreHook(nil, r1, f))\n\n\t\tended := recorder.Ended()\n\t\trequire.NotEmpty(t, ended)\n\n\t\ti := slices.IndexFunc(ended, func(sp sdktrace.ReadOnlySpan) bool {\n\t\t\treturn sp.Name() == \"selfservice.webhook\"\n\t\t})\n\t\trequire.GreaterOrEqual(t, i, 0)\n\n\t\tevs := ended[i].Events()\n\t\ti = slices.IndexFunc(evs, func(ev sdktrace.Event) bool {\n\t\t\treturn ev.Name == \"WebhookDelivered\"\n\t\t})\n\t\trequire.Equal(t, -1, i)\n\n\t\ti = slices.IndexFunc(evs, func(ev sdktrace.Event) bool {\n\t\t\treturn ev.Name == \"WebhookFailed\"\n\t\t})\n\t\trequire.Equal(t, -1, i)\n\n\t\ti = slices.IndexFunc(evs, func(ev sdktrace.Event) bool {\n\t\t\treturn ev.Name == \"WebhookSucceeded\"\n\t\t})\n\t\trequire.Equal(t, i, -1)\n\t})\n}\n\nfunc TestRemoveDisallowedHeaders(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"empty http headers\", func(t *testing.T) {\n\t\theaders := http.Header{}\n\t\tallowList := []string{\"Content-Type\", \"Host\"}\n\t\tnewHeaders := hook.RemoveDisallowedHeaders(headers, allowList)\n\n\t\trequire.Len(t, newHeaders, 0)\n\t})\n\tt.Run(\"empty allow list\", func(t *testing.T) {\n\t\theaders := http.Header{\"Accept\": {\"application/json\"}, \"Content-Type\": {\"text/html\"}}\n\t\tallowList := []string{}\n\t\tnewHeaders := hook.RemoveDisallowedHeaders(headers, allowList)\n\n\t\trequire.Len(t, newHeaders, 0)\n\t})\n\tt.Run(\"all forbidden\", func(t *testing.T) {\n\t\theaders := http.Header{\"Accept\": {\"application/json\"}, \"Authorization\": {\"Bearer foo\"}}\n\t\tallowList := []string{\"Content-Type\", \"Host\"}\n\t\tnewHeaders := hook.RemoveDisallowedHeaders(headers, allowList)\n\n\t\trequire.Len(t, newHeaders, 0)\n\t})\n\tt.Run(\"general case\", func(t *testing.T) {\n\t\theaders := http.Header{\"Accept\": {\"application/json\"}, \"Content-Type\": {\"text/html\"}}\n\t\tallowList := []string{\"Content-Type\", \"Host\"}\n\t\tnewHeaders := hook.RemoveDisallowedHeaders(headers, allowList)\n\n\t\trequire.Len(t, newHeaders, 1)\n\t\th, present := newHeaders[\"Content-Type\"]\n\t\trequire.True(t, present)\n\t\trequire.Equal(t, []string{\"text/html\"}, h)\n\t})\n}\n"
  },
  {
    "path": "selfservice/sessiontokenexchange/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage sessiontokenexchange\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype Codes struct {\n\tInitCode     string\n\tReturnToCode string\n}\n\ntype Exchanger struct {\n\tID        uuid.UUID     `db:\"id\"`\n\tNID       uuid.UUID     `db:\"nid\"`\n\tFlowID    uuid.UUID     `db:\"flow_id\"`\n\tSessionID uuid.NullUUID `db:\"session_id\"`\n\n\tInitCode     string `db:\"init_code\"`\n\tReturnToCode string `db:\"return_to_code\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `db:\"updated_at\"`\n}\n\nfunc (Exchanger) TableName() string { return \"session_token_exchanges\" }\n\ntype (\n\tPersister interface {\n\t\tCreateSessionTokenExchanger(ctx context.Context, flowID uuid.UUID) (e *Exchanger, err error)\n\t\tGetExchangerFromCode(ctx context.Context, initCode string, returnToCode string) (*Exchanger, error)\n\t\tUpdateSessionOnExchanger(ctx context.Context, flowID uuid.UUID, sessionID uuid.UUID) error\n\t\tCodeForFlow(ctx context.Context, flowID uuid.UUID) (codes *Codes, found bool, err error)\n\t\tMoveToNewFlow(ctx context.Context, oldFlow, newFlow uuid.UUID) error\n\n\t\tDeleteExpiredExchangers(context.Context, time.Time, int) error\n\t}\n\n\tPersistenceProvider interface {\n\t\tSessionTokenExchangePersister() Persister\n\t}\n)\n"
  },
  {
    "path": "selfservice/sessiontokenexchange/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/x/randx\"\n)\n\ntype testParams struct {\n\tflowID, sessionID      uuid.UUID\n\tinitCode, returnToCode string\n}\n\nfunc newParams() testParams {\n\treturn testParams{\n\t\tflowID:       uuid.Must(uuid.NewV4()),\n\t\tsessionID:    uuid.Must(uuid.NewV4()),\n\t\tinitCode:     randx.MustString(64, randx.AlphaNum),\n\t\treturnToCode: randx.MustString(64, randx.AlphaNum),\n\t}\n}\nfunc (t *testParams) setCodes(e *sessiontokenexchange.Exchanger) {\n\tt.initCode = e.InitCode\n\tt.returnToCode = e.ReturnToCode\n}\n\nfunc TestPersister(ctx context.Context, p interface {\n\tpersistence.Persister\n}) func(t *testing.T) {\n\treturn func(t *testing.T) {\n\t\tnid, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tt.Run(\"suite=create-update-get\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tparams := newParams()\n\n\t\t\tt.Run(\"step=create\", func(t *testing.T) {\n\t\t\t\te, err := p.CreateSessionTokenExchanger(ctx, params.flowID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tparams.setCodes(e)\n\t\t\t\tcodes, ok, err := p.CodeForFlow(ctx, params.flowID)\n\t\t\t\tassert.True(t, ok)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.Equal(t, params.initCode, codes.InitCode)\n\t\t\t\tassert.Equal(t, params.returnToCode, codes.ReturnToCode)\n\t\t\t})\n\t\t\tt.Run(\"step=update\", func(t *testing.T) {\n\t\t\t\trequire.NoError(t, p.UpdateSessionOnExchanger(ctx, params.flowID, params.sessionID))\n\t\t\t})\n\t\t\tt.Run(\"step=get\", func(t *testing.T) {\n\t\t\t\te, err := p.GetExchangerFromCode(ctx, params.initCode, params.returnToCode)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, params.sessionID, e.SessionID.UUID)\n\t\t\t\tassert.Equal(t, nid, e.NID)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"suite=CodeForFlow\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tt.Run(\"case=returns false for non-existing flow\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\t_, ok, err := p.CodeForFlow(ctx, uuid.Must(uuid.NewV4()))\n\t\t\t\tassert.False(t, ok)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"suite=MoveToNewFlow\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tt.Run(\"case=move to new flow\", func(t *testing.T) {\n\t\t\t\tparams := newParams()\n\t\t\t\tother := newParams()\n\n\t\t\t\te, err := p.CreateSessionTokenExchanger(ctx, params.flowID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tparams.setCodes(e)\n\t\t\t\trequire.NoError(t, p.MoveToNewFlow(ctx, params.flowID, other.flowID))\n\t\t\t\trequire.NoError(t, p.UpdateSessionOnExchanger(ctx, other.flowID, params.sessionID))\n\n\t\t\t\te, err = p.GetExchangerFromCode(ctx, params.initCode, params.returnToCode)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, params.sessionID, e.SessionID.UUID)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"suite=GetExchangerFromCode\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tt.Run(\"case=errors if session not found\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tparams := newParams()\n\n\t\t\t\te, err := p.CreateSessionTokenExchanger(ctx, params.flowID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tparams.setCodes(e)\n\n\t\t\t\te, err = p.GetExchangerFromCode(ctx, params.initCode, params.returnToCode)\n\n\t\t\t\tassert.Error(t, err)\n\t\t\t\tassert.Nil(t, e)\n\t\t\t})\n\n\t\t\tt.Run(\"case=errors if code is invalid\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tparams := newParams()\n\t\t\t\tother := newParams()\n\n\t\t\t\te, err := p.CreateSessionTokenExchanger(ctx, params.flowID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tparams.setCodes(e)\n\n\t\t\t\trequire.NoError(t, p.UpdateSessionOnExchanger(ctx, params.flowID, params.sessionID))\n\t\t\t\te, err = p.GetExchangerFromCode(ctx, other.initCode, other.returnToCode)\n\n\t\t\t\tassert.Error(t, err)\n\t\t\t\tassert.Nil(t, e)\n\t\t\t})\n\n\t\t\tt.Run(\"case=errors if code is empty\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tparams := newParams()\n\n\t\t\t\te, err := p.CreateSessionTokenExchanger(ctx, params.flowID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tparams.setCodes(e)\n\n\t\t\t\trequire.NoError(t, p.UpdateSessionOnExchanger(ctx, params.flowID, params.sessionID))\n\t\t\t\te, err = p.GetExchangerFromCode(ctx, \"\", \"\")\n\n\t\t\t\tassert.Error(t, err)\n\t\t\t\tassert.Nil(t, e)\n\t\t\t})\n\n\t\t\tt.Run(\"case=errors if other network ID\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tparams := newParams()\n\t\t\t\totherNID := uuid.Must(uuid.NewV4())\n\n\t\t\t\te, err := p.CreateSessionTokenExchanger(ctx, params.flowID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tparams.setCodes(e)\n\n\t\t\t\trequire.NoError(t, p.UpdateSessionOnExchanger(ctx, params.flowID, params.sessionID))\n\t\t\t\te, err = p.WithNetworkID(otherNID).GetExchangerFromCode(ctx, params.initCode, params.returnToCode)\n\n\t\t\t\tassert.Error(t, err)\n\t\t\t\tassert.Nil(t, e)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.schema/login.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/code/login.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"code\": {\n      \"type\": \"string\"\n    },\n    \"identifier\": {\n      \"type\": \"string\"\n    },\n    \"address\": {\n      \"type\": \"string\"\n    },\n    \"resend\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"code\"\n      ]\n    },\n    \"flow\": {\n      \"type\": \"string\",\n      \"format\": \"uuid\"\n    },\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.schema/recovery.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/profile/recovery.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"code\": {\n      \"type\": \"string\"\n    },\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\"\n    },\n    \"recovery_address\": {\n      \"type\": \"string\"\n    },\n    \"recovery_select_address\": {\n      \"type\": \"string\"\n    },\n    \"recovery_confirm_address\": {\n      \"type\": \"string\"\n    },\n    \"screen\": {\n      \"type\": \"string\"\n    },\n    \"flow\": {\n      \"type\": \"string\",\n      \"format\": \"uuid\"\n    },\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.schema/registration.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/code/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"code\": {\n      \"type\": \"string\"\n    },\n    \"resend\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"code\"\n      ]\n    },\n    \"traits\": {\n      \"description\": \"This field will be overwritten in registration.go's decoder() method. Do not add anything to this field as it has no effect.\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.schema/verification.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/profile/verification.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"method\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"code\",\n        \"link\"\n      ]\n    },\n    \"code\": {\n      \"type\": \"string\"\n    },\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\"\n    },\n    \"flow\": {\n      \"type\": \"string\",\n      \"format\": \"uuid\"\n    },\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestAdminStrategy-case=form_should_not_contain_email_field_when_creating_recovery_code.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestAdminStrategy-description=should_fail_on_malformed_expiry_time.json",
    "content": "{\n  \"error\": {\n    \"code\": 400,\n    \"message\": \"The request was malformed or contained invalid parameters\",\n    \"reason\": \"Unable to parse \\\"expires_in\\\" whose format should match \\\"[0-9]+(ns|us|ms|s|m|h)\\\" but did not: not-a-valid-value\",\n    \"status\": \"Bad Request\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestAdminStrategy-description=should_fail_on_negative_expiry_time.json",
    "content": "{\n  \"error\": {\n    \"code\": 400,\n    \"message\": \"The request was malformed or contained invalid parameters\",\n    \"reason\": \"Value from \\\"expires_in\\\" must result to a future time: -1h0m0s\",\n    \"status\": \"Bad Request\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestAdminStrategy-description=should_not_be_able_to_recover_an_account_that_does_not_exist.json",
    "content": "{\n  \"error\": {\n    \"code\": 404,\n    \"message\": \"Unable to locate the resource\",\n    \"reason\": \"could not find identity\",\n    \"status\": \"Not Found\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor-case=code_is_used_for_2fa_but_request_is_1fa.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor-case=code_is_used_for_passwordless_login_and_request_is_1fa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010015,\n        \"text\": \"Send sign in code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh-case=code_is_used_for_2fa_and_request_is_1fa_with_refresh.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh-case=code_is_used_for_passwordless_login_and_request_is_1fa_with_refresh.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010015,\n        \"text\": \"Send sign in code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_does_not_have_a_code_method-case=code_is_used_for_2fa.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_does_not_have_a_code_method-case=code_is_used_for_passwordless_login.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010015,\n        \"text\": \"Send sign in code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_has_code_method-case=code_is_used_for_2fa.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_has_code_method-case=code_is_used_for_passwordless_login.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010015,\n        \"text\": \"Send sign in code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=with_no_identity-case=code_is_used_for_2fa.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=with_no_identity-case=code_is_used_for_passwordless_login.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_enabled-case=code_is_used_for_2fa.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_enabled-case=code_is_used_for_passwordless_login.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010015,\n        \"text\": \"Send sign in code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=code_is_used_for_2fa.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=code_is_used_for_passwordless_login.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010015,\n        \"text\": \"Send sign in code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=code_is_used_for_2fa_and_request_is_2fa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"address\",\n      \"type\": \"submit\",\n      \"value\": \"foo@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010023,\n        \"text\": \"Send code to foo@ory.sh\",\n        \"type\": \"info\",\n        \"context\": {\n          \"address\": \"foo@ory.sh\",\n          \"channel\": \"email\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=code_is_used_for_passwordless_login_and_request_is_2fa.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-using_via-case=code_is_used_for_2fa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"address\",\n      \"type\": \"submit\",\n      \"value\": \"populateloginmethodsecondfactor-code-mfa-via-2fa@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010023,\n        \"text\": \"Send code to populateloginmethodsecondfactor-code-mfa-via-2fa@ory.sh\",\n        \"type\": \"info\",\n        \"context\": {\n          \"address\": \"populateloginmethodsecondfactor-code-mfa-via-2fa@ory.sh\",\n          \"channel\": \"email\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-using_via-case=code_is_used_for_passwordless_login.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-without_via-case=code_is_used_for_2fa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"address\",\n      \"type\": \"submit\",\n      \"value\": \"+4917655138291\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917655138291\",\n        \"type\": \"info\",\n        \"context\": {\n          \"address\": \"+4917655138291\",\n          \"channel\": \"sms\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"address\",\n      \"type\": \"submit\",\n      \"value\": \"populateloginmethodsecondfactor-no-via-2fa-0@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010023,\n        \"text\": \"Send code to populateloginmethodsecondfactor-no-via-2fa-0@ory.sh\",\n        \"type\": \"info\",\n        \"context\": {\n          \"address\": \"populateloginmethodsecondfactor-no-via-2fa-0@ory.sh\",\n          \"channel\": \"email\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"address\",\n      \"type\": \"submit\",\n      \"value\": \"populateloginmethodsecondfactor-no-via-2fa-1@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010023,\n        \"text\": \"Send code to populateloginmethodsecondfactor-no-via-2fa-1@ory.sh\",\n        \"type\": \"info\",\n        \"context\": {\n          \"address\": \"populateloginmethodsecondfactor-no-via-2fa-1@ory.sh\",\n          \"channel\": \"email\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-without_via-case=code_is_used_for_passwordless_login.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactorRefresh-case=code_is_used_for_2fa_and_request_is_2fa_with_refresh.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"address\",\n      \"type\": \"submit\",\n      \"value\": \"fooaewdk@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010023,\n        \"text\": \"Send code to fooaewdk@ory.sh\",\n        \"type\": \"info\",\n        \"context\": {\n          \"address\": \"fooaewdk@ory.sh\",\n          \"channel\": \"email\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactorRefresh-case=code_is_used_for_passwordless_login_and_request_is_2fa_with_refresh.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Browser_client-suite=mfa-case=verify_initial_payload_with_fast_login.json",
    "content": "{\n  \"active\": \"code\",\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"sent_email\",\n  \"type\": \"browser\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010014,\n        \"text\": \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"identifier\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"fixed_mfa_test_fast_browser@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_test_fast_browser@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_test_fast_browser@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070013,\n            \"text\": \"Login code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"resend\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070008,\n            \"text\": \"Resend code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"csrf_token\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"hidden\"\n        },\n        \"group\": \"default\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Browser_client-suite=mfa-case=verify_initial_payload_with_fast_login_and_fallback_enabled-case=no_code_credential.json",
    "content": "{\n  \"active\": \"code\",\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"sent_email\",\n  \"type\": \"browser\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010014,\n        \"text\": \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"identifier\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"fixed_mfa_fallback_without_ccbrowser@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_fallback_without_ccbrowser@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_fallback_without_ccbrowser@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070013,\n            \"text\": \"Login code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"resend\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070008,\n            \"text\": \"Resend code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"csrf_token\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"hidden\"\n        },\n        \"group\": \"default\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Browser_client-suite=mfa-case=verify_initial_payload_with_fast_login_and_fallback_enabled-case=with_code_credential.json",
    "content": "{\n  \"active\": \"code\",\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"sent_email\",\n  \"type\": \"browser\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010014,\n        \"text\": \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"identifier\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"fixed_mfa_fallback_with_ccbrowser@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_fallback_with_ccbrowser@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_fallback_with_ccbrowser@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070013,\n            \"text\": \"Login code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"resend\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070008,\n            \"text\": \"Resend code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"csrf_token\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"hidden\"\n        },\n        \"group\": \"default\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Browser_client-suite=mfa-case=verify_initial_payload_without_fast_login.json",
    "content": "{\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"choose_method\",\n  \"type\": \"browser\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010004,\n        \"text\": \"Please complete the second authentication challenge.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"csrf_token\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"hidden\"\n        },\n        \"group\": \"default\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"address\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"fixed_mfa_test_browser@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_test_browser@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_test_browser@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"totp_code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\",\n          \"value\": \"\"\n        },\n        \"group\": \"totp\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010006,\n            \"text\": \"Authentication code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"totp\"\n        },\n        \"group\": \"totp\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010009,\n            \"text\": \"Use Authenticator\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Browser_client-suite=mfa-case=without_via_parameter_all_options_are_shown-field=address-email.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"+4917613213110\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"+4917613213110\",\n          \"channel\": \"sms\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917613213110\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-1browser@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-1browser@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-1browser@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-2browser@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-2browser@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-2browser@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Browser_client-suite=mfa-case=without_via_parameter_all_options_are_shown-field=address-phone.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"+4917613213110\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"+4917613213110\",\n          \"channel\": \"sms\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917613213110\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-1browser@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-1browser@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-1browser@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-2browser@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-2browser@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-2browser@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Browser_client-suite=mfa-case=without_via_parameter_all_options_are_shown-field=identifier-email.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"+4917613213110\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"+4917613213110\",\n          \"channel\": \"sms\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917613213110\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-1browser@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-1browser@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-1browser@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-2browser@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-2browser@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-2browser@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Native_client-suite=mfa-case=verify_initial_payload_with_fast_login.json",
    "content": "{\n  \"active\": \"code\",\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"sent_email\",\n  \"type\": \"api\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010014,\n        \"text\": \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"identifier\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"fixed_mfa_test_fast_api@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_test_fast_api@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_test_fast_api@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070013,\n            \"text\": \"Login code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"resend\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070008,\n            \"text\": \"Resend code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Native_client-suite=mfa-case=verify_initial_payload_with_fast_login_and_fallback_enabled-case=no_code_credential.json",
    "content": "{\n  \"active\": \"code\",\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"sent_email\",\n  \"type\": \"api\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010014,\n        \"text\": \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"identifier\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"fixed_mfa_fallback_without_ccapi@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_fallback_without_ccapi@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_fallback_without_ccapi@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070013,\n            \"text\": \"Login code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"resend\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070008,\n            \"text\": \"Resend code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Native_client-suite=mfa-case=verify_initial_payload_with_fast_login_and_fallback_enabled-case=with_code_credential.json",
    "content": "{\n  \"active\": \"code\",\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"sent_email\",\n  \"type\": \"api\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010014,\n        \"text\": \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"identifier\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"fixed_mfa_fallback_with_ccapi@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_fallback_with_ccapi@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_fallback_with_ccapi@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070013,\n            \"text\": \"Login code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"resend\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070008,\n            \"text\": \"Resend code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Native_client-suite=mfa-case=verify_initial_payload_without_fast_login.json",
    "content": "{\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"choose_method\",\n  \"type\": \"api\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010004,\n        \"text\": \"Please complete the second authentication challenge.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"csrf_token\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"hidden\"\n        },\n        \"group\": \"default\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"address\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"fixed_mfa_test_api@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_test_api@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_test_api@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"totp_code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\",\n          \"value\": \"\"\n        },\n        \"group\": \"totp\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010006,\n            \"text\": \"Authentication code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"totp\"\n        },\n        \"group\": \"totp\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010009,\n            \"text\": \"Use Authenticator\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Native_client-suite=mfa-case=without_via_parameter_all_options_are_shown-field=address-email.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"+4917613213111\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"+4917613213111\",\n          \"channel\": \"sms\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917613213111\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-1api@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-1api@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-1api@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-2api@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-2api@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-2api@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Native_client-suite=mfa-case=without_via_parameter_all_options_are_shown-field=address-phone.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"+4917613213111\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"+4917613213111\",\n          \"channel\": \"sms\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917613213111\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-1api@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-1api@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-1api@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-2api@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-2api@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-2api@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=Native_client-suite=mfa-case=without_via_parameter_all_options_are_shown-field=identifier-email.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"+4917613213111\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"+4917613213111\",\n          \"channel\": \"sms\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917613213111\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-1api@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-1api@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-1api@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-2api@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-2api@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-2api@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=SPA_client-suite=mfa-case=verify_initial_payload_with_fast_login.json",
    "content": "{\n  \"active\": \"code\",\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"sent_email\",\n  \"type\": \"browser\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010014,\n        \"text\": \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"identifier\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"fixed_mfa_test_fast_spa@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_test_fast_spa@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_test_fast_spa@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070013,\n            \"text\": \"Login code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"resend\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070008,\n            \"text\": \"Resend code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"csrf_token\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"hidden\"\n        },\n        \"group\": \"default\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=SPA_client-suite=mfa-case=verify_initial_payload_with_fast_login_and_fallback_enabled-case=no_code_credential.json",
    "content": "{\n  \"active\": \"code\",\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"sent_email\",\n  \"type\": \"browser\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010014,\n        \"text\": \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"identifier\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"fixed_mfa_fallback_without_ccspa@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_fallback_without_ccspa@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_fallback_without_ccspa@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070013,\n            \"text\": \"Login code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"resend\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070008,\n            \"text\": \"Resend code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"csrf_token\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"hidden\"\n        },\n        \"group\": \"default\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=SPA_client-suite=mfa-case=verify_initial_payload_with_fast_login_and_fallback_enabled-case=with_code_credential.json",
    "content": "{\n  \"active\": \"code\",\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"sent_email\",\n  \"type\": \"browser\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010014,\n        \"text\": \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"identifier\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"fixed_mfa_fallback_with_ccspa@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_fallback_with_ccspa@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_fallback_with_ccspa@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"hidden\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070013,\n            \"text\": \"Login code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070009,\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"resend\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"code\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070008,\n            \"text\": \"Resend code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"csrf_token\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"hidden\"\n        },\n        \"group\": \"default\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=SPA_client-suite=mfa-case=verify_initial_payload_without_fast_login.json",
    "content": "{\n  \"organization_id\": null,\n  \"refresh\": false,\n  \"requested_aal\": \"aal2\",\n  \"state\": \"choose_method\",\n  \"type\": \"browser\",\n  \"ui\": {\n    \"messages\": [\n      {\n        \"id\": 1010004,\n        \"text\": \"Please complete the second authentication challenge.\",\n        \"type\": \"info\"\n      }\n    ],\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"csrf_token\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"hidden\"\n        },\n        \"group\": \"default\",\n        \"messages\": [],\n        \"meta\": {},\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"address\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"fixed_mfa_test_spa@ory.sh\"\n        },\n        \"group\": \"code\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"context\": {\n              \"address\": \"fixed_mfa_test_spa@ory.sh\",\n              \"channel\": \"email\"\n            },\n            \"id\": 1010023,\n            \"text\": \"Send code to fixed_mfa_test_spa@ory.sh\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"totp_code\",\n          \"node_type\": \"input\",\n          \"required\": true,\n          \"type\": \"text\",\n          \"value\": \"\"\n        },\n        \"group\": \"totp\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010006,\n            \"text\": \"Authentication code\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      },\n      {\n        \"attributes\": {\n          \"disabled\": false,\n          \"name\": \"method\",\n          \"node_type\": \"input\",\n          \"type\": \"submit\",\n          \"value\": \"totp\"\n        },\n        \"group\": \"totp\",\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010009,\n            \"text\": \"Use Authenticator\",\n            \"type\": \"info\"\n          }\n        },\n        \"type\": \"input\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=SPA_client-suite=mfa-case=without_via_parameter_all_options_are_shown-field=address-email.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"+4917613213112\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"+4917613213112\",\n          \"channel\": \"sms\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917613213112\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-1spa@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-1spa@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-1spa@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-2spa@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-2spa@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-2spa@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=SPA_client-suite=mfa-case=without_via_parameter_all_options_are_shown-field=address-phone.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"+4917613213112\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"+4917613213112\",\n          \"channel\": \"sms\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917613213112\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-1spa@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-1spa@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-1spa@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-2spa@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-2spa@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-2spa@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestLoginCodeStrategy-test=SPA_client-suite=mfa-case=without_via_parameter_all_options_are_shown-field=identifier-email.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"+4917613213112\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"+4917613213112\",\n          \"channel\": \"sms\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to +4917613213112\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-1spa@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-1spa@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-1spa@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"address\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code-mfa-2spa@ory.sh\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"address\": \"code-mfa-2spa@ory.sh\",\n          \"channel\": \"email\"\n        },\n        \"id\": 1010023,\n        \"text\": \"Send code to code-mfa-2spa@ory.sh\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethod.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040006,\n        \"text\": \"Send sign up code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodCredentials.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040006,\n        \"text\": \"Send sign up code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodProfile.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=1.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=2.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040006,\n        \"text\": \"Send sign up code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=3.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=4.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040006,\n        \"text\": \"Send sign up code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery-description=should_set_all_the_correct_recovery_payloads.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery-description=should_set_all_the_correct_recovery_payloads_after_submission.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"email\",\n      \"type\": \"submit\",\n      \"value\": \"test@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith-description=should_set_all_the_correct_recovery_payloads-type=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address (Email, phone number, etc)\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith-description=should_set_all_the_correct_recovery_payloads-type=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address (Email, phone number, etc)\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith-description=should_set_all_the_correct_recovery_payloads-type=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address (Email, phone number, etc)\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith-description=should_set_all_the_correct_recovery_payloads_after_submission-type=api.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_confirm_address\",\n      \"type\": \"email\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith-description=should_set_all_the_correct_recovery_payloads_after_submission-type=browser.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_confirm_address\",\n      \"type\": \"email\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith-description=should_set_all_the_correct_recovery_payloads_after_submission-type=spa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_confirm_address\",\n      \"type\": \"email\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Email-description=should_set_all_the_correct_recovery_payloads-type=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Email-description=should_set_all_the_correct_recovery_payloads-type=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Email-description=should_set_all_the_correct_recovery_payloads-type=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Email-description=should_set_all_the_correct_recovery_payloads_after_submission-type=api.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_confirm_address\",\n      \"type\": \"submit\",\n      \"value\": \"test-api@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_address\",\n      \"type\": \"hidden\",\n      \"value\": \"test-api@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1060007,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Email-description=should_set_all_the_correct_recovery_payloads_after_submission-type=browser.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_confirm_address\",\n      \"type\": \"submit\",\n      \"value\": \"test-browser@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_address\",\n      \"type\": \"hidden\",\n      \"value\": \"test-browser@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1060007,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Email-description=should_set_all_the_correct_recovery_payloads_after_submission-type=spa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_confirm_address\",\n      \"type\": \"submit\",\n      \"value\": \"test-spa@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_address\",\n      \"type\": \"hidden\",\n      \"value\": \"test-spa@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1060007,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Phone-description=should_set_all_the_correct_recovery_payloads-type=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Phone-description=should_set_all_the_correct_recovery_payloads-type=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Phone-description=should_set_all_the_correct_recovery_payloads-type=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Phone-description=should_set_all_the_correct_recovery_payloads_after_submission-type=api.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_confirm_address\",\n      \"type\": \"submit\",\n      \"value\": \"+491705550177\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_address\",\n      \"type\": \"hidden\",\n      \"value\": \"+491705550177\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1060007,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Phone-description=should_set_all_the_correct_recovery_payloads_after_submission-type=browser.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_confirm_address\",\n      \"type\": \"submit\",\n      \"value\": \"+491705550176\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_address\",\n      \"type\": \"hidden\",\n      \"value\": \"+491705550176\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1060007,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_OneAddress_Phone-description=should_set_all_the_correct_recovery_payloads_after_submission-type=spa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_confirm_address\",\n      \"type\": \"submit\",\n      \"value\": \"+491705550178\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_address\",\n      \"type\": \"hidden\",\n      \"value\": \"+491705550178\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1060007,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_SeveralAddresses-description=should_set_all_the_correct_recovery_payloads-type=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_SeveralAddresses-description=should_set_all_the_correct_recovery_payloads-type=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_SeveralAddresses-description=should_set_all_the_correct_recovery_payloads-type=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"recovery_address\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070016,\n        \"text\": \"Recovery address\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_SeveralAddresses-description=should_set_all_the_correct_recovery_payloads_after_submission-type=api.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_select_address\",\n      \"type\": \"submit\",\n      \"value\": \"MEIbSbZvtBRV9TH7wdFTF5CGyhxaaM2zuLDSIELCIAI=\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070000,\n        \"text\": \"+49****67\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_select_address\",\n      \"type\": \"submit\",\n      \"value\": \"BgkSG7BOfSWi+9/IRUclTboO0eGGkgnBFQiBv/v2Jw4=\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070000,\n        \"text\": \"te****@ory.sh\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_address\",\n      \"type\": \"hidden\",\n      \"value\": \"+491705550167\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_SeveralAddresses-description=should_set_all_the_correct_recovery_payloads_after_submission-type=browser.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_select_address\",\n      \"type\": \"submit\",\n      \"value\": \"52McLavwKA6+jlSuOBVOPvkoLn8r1fUhZDMj2eytcac=\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070000,\n        \"text\": \"+49****66\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_select_address\",\n      \"type\": \"submit\",\n      \"value\": \"9lRwDY12jB69oE1+tGQXV2XFJbgBtjjRwBjH+iHmHjA=\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070000,\n        \"text\": \"te****@ory.sh\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_address\",\n      \"type\": \"hidden\",\n      \"value\": \"+491705550166\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_V2_WithContinueWith_SeveralAddresses-description=should_set_all_the_correct_recovery_payloads_after_submission-type=spa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_select_address\",\n      \"type\": \"submit\",\n      \"value\": \"zx+NolDxaddHeLJ05eafzEcdjhJ0F8h5MAPtmbBSZkE=\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070000,\n        \"text\": \"+49****68\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_select_address\",\n      \"type\": \"submit\",\n      \"value\": \"pti/PkfHLV3dkx+jIA+yDc8KCLmQ/K872SvKfFQ7C/c=\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070000,\n        \"text\": \"te****@ory.sh\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"recovery_address\",\n      \"type\": \"hidden\",\n      \"value\": \"+491705550168\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_WithContinueWith-description=should_set_all_the_correct_recovery_payloads-type=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_WithContinueWith-description=should_set_all_the_correct_recovery_payloads-type=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_WithContinueWith-description=should_set_all_the_correct_recovery_payloads-type=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_WithContinueWith-description=should_set_all_the_correct_recovery_payloads_after_submission-type=api.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"email\",\n      \"type\": \"submit\",\n      \"value\": \"test@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_WithContinueWith-description=should_set_all_the_correct_recovery_payloads_after_submission-type=browser.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"email\",\n      \"type\": \"submit\",\n      \"value\": \"test@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestRecovery_WithContinueWith-description=should_set_all_the_correct_recovery_payloads_after_submission-type=spa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"pattern\": \"[0-9]+\",\n      \"disabled\": false,\n      \"maxlength\": 6,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070010,\n        \"text\": \"Recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"email\",\n      \"type\": \"submit\",\n      \"value\": \"test@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestVerification-description=should_set_all_the_correct_verification_payloads.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"code\"\n    },\n    \"group\": \"code\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/.snapshots/TestVerification-description=should_set_all_the_correct_verification_payloads_after_submission.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"hidden\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"code\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070011,\n        \"text\": \"Verification code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"email\",\n      \"type\": \"submit\",\n      \"value\": \"test@ory.sh\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070008,\n        \"text\": \"Resend code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/code/code_login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\n// swagger:ignore\ntype LoginCode struct {\n\t// ID represents the tokens's unique ID.\n\t//\n\t// required: true\n\t// type: string\n\t// format: uuid\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// Address represents the address that the code was sent to.\n\t// this can be an email address or a phone number.\n\tAddress string `json:\"-\" db:\"address\"`\n\n\t// AddressType represents the type of the address\n\t// this can be an email address or a phone number.\n\tAddressType identity.CodeChannel `json:\"-\" db:\"address_type\"`\n\n\t// CodeHMAC represents the HMACed value of the verification code\n\tCodeHMAC string `json:\"-\" db:\"code\"`\n\n\t// UsedAt is the timestamp of when the code was used or null if it wasn't yet\n\tUsedAt sql.NullTime `json:\"-\" db:\"used_at\"`\n\n\t// ExpiresAt is the time (UTC) when the token expires.\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the token was issued.\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\n\t// FlowID is a helper struct field for gobuffalo.pop.\n\tFlowID uuid.UUID `json:\"-\" faker:\"-\" db:\"selfservice_login_flow_id\"`\n\n\tNID        uuid.UUID `json:\"-\"  faker:\"-\" db:\"nid\"`\n\tIdentityID uuid.UUID `json:\"identity_id\" faker:\"-\" db:\"identity_id\"`\n}\n\nfunc (LoginCode) TableName(ctx context.Context) string {\n\treturn \"identity_login_codes\"\n}\n\nfunc (f *LoginCode) Validate() error {\n\tif f == nil {\n\t\treturn errors.WithStack(ErrCodeNotFound)\n\t}\n\tif f.ExpiresAt.Before(time.Now().UTC()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\tif f.UsedAt.Valid {\n\t\treturn errors.WithStack(ErrCodeAlreadyUsed)\n\t}\n\treturn nil\n}\n\nfunc (f *LoginCode) GetHMACCode() string {\n\treturn f.CodeHMAC\n}\n\nfunc (f *LoginCode) GetID() uuid.UUID {\n\treturn f.ID\n}\n\n// swagger:ignore\ntype CreateLoginCodeParams struct {\n\t// Address is the email address or phone number the code should be sent to.\n\t// required: true\n\tAddress string\n\n\t// AddressType is the type of the address (email or phone number).\n\t// required: true\n\tAddressType identity.CodeChannel\n\n\t// Code represents the recovery code\n\t// required: true\n\tRawCode string\n\n\t// ExpiresAt is the time (UTC) when the code expires.\n\t// required: true\n\tExpiresIn time.Duration\n\n\t// FlowID is a helper struct field for gobuffalo.pop.\n\t// required: true\n\tFlowID uuid.UUID\n\n\t// IdentityID is the identity that this code is for\n\t// required: true\n\tIdentityID uuid.UUID\n}\n"
  },
  {
    "path": "selfservice/strategy/code/code_login_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"database/sql\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestLoginCode(t *testing.T) {\n\tt.Parallel()\n\n\tconf := pkg.NewConfigurationWithDefaults(t)\n\n\tnewCode := func(expiresIn time.Duration, f *login.Flow) *code.LoginCode {\n\t\treturn &code.LoginCode{\n\t\t\tID:        x.NewUUID(),\n\t\t\tFlowID:    f.ID,\n\t\t\tExpiresAt: time.Now().Add(expiresIn),\n\t\t}\n\t}\n\n\treq := &http.Request{URL: urlx.ParseOrPanic(\"https://www.ory.sh/\")}\n\tt.Run(\"method=Validate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"case=returns error if flow is expired\", func(t *testing.T) {\n\t\t\tf, err := login.NewFlow(conf, -time.Hour, \"\", req, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(-time.Hour, f)\n\t\t\texpected := new(flow.ExpiredError)\n\t\t\trequire.ErrorAs(t, c.Validate(), &expected)\n\t\t})\n\t\tt.Run(\"case=returns no error if flow is not expired\", func(t *testing.T) {\n\t\t\tf, err := login.NewFlow(conf, time.Hour, \"\", req, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\trequire.NoError(t, c.Validate())\n\t\t})\n\n\t\tt.Run(\"case=returns error if flow has been used\", func(t *testing.T) {\n\t\t\tf, err := login.NewFlow(conf, -time.Hour, \"\", req, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\tc.UsedAt = sql.NullTime{\n\t\t\t\tTime:  time.Now(),\n\t\t\t\tValid: true,\n\t\t\t}\n\t\t\trequire.ErrorIs(t, c.Validate(), code.ErrCodeAlreadyUsed)\n\t\t})\n\n\t\tt.Run(\"case=returns no error if flow has not been used\", func(t *testing.T) {\n\t\t\tf, err := login.NewFlow(conf, -time.Hour, \"\", req, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\tc.UsedAt = sql.NullTime{\n\t\t\t\tValid: false,\n\t\t\t}\n\t\t\trequire.NoError(t, c.Validate())\n\t\t})\n\n\t\tt.Run(\"case=returns error if flow is nil\", func(t *testing.T) {\n\t\t\tvar c *code.LoginCode\n\t\t\trequire.ErrorIs(t, c.Validate(), code.ErrCodeNotFound)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/code_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\ntype RecoveryCodeType int\n\nconst (\n\tRecoveryCodeTypeAdmin RecoveryCodeType = iota + 1\n\tRecoveryCodeTypeSelfService\n)\n\nvar (\n\tErrCodeNotFound          = herodot.ErrNotFound.WithReasonf(\"unknown code\")\n\tErrCodeAlreadyUsed       = herodot.ErrBadRequest.WithReasonf(\"The code was already used. Please request another code.\")\n\tErrCodeSubmittedTooOften = herodot.ErrBadRequest.WithReasonf(\"The request was submitted too often. Please request another code.\")\n)\n\ntype RecoveryCode struct {\n\t// ID represents the code's unique ID.\n\t//\n\t// required: true\n\t// type: string\n\t// format: uuid\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// CodeHMAC represents the HMACed value of the recovery code\n\tCodeHMAC string `json:\"-\" db:\"code\"`\n\n\t// UsedAt is the timestamp of when the code was used or null if it wasn't yet\n\tUsedAt sql.NullTime `json:\"-\" db:\"used_at\"`\n\n\t// RecoveryAddress links this code to a recovery address.\n\t// required: true\n\tRecoveryAddress *identity.RecoveryAddress `json:\"recovery_address\" belongs_to:\"identity_recovery_addresses\" fk_id:\"RecoveryAddressID\"`\n\n\t// CodeType is the type of the code - either \"admin\" or \"selfservice\"\n\tCodeType RecoveryCodeType `json:\"-\" faker:\"-\" db:\"code_type\"`\n\n\t// ExpiresAt is the time (UTC) when the code expires.\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the code was issued.\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\t// RecoveryAddressID is a helper struct field for gobuffalo.pop.\n\tRecoveryAddressID uuid.NullUUID `json:\"-\" faker:\"-\" db:\"identity_recovery_address_id\"`\n\t// FlowID is a helper struct field for gobuffalo.pop.\n\tFlowID     uuid.UUID `json:\"-\" faker:\"-\" db:\"selfservice_recovery_flow_id\"`\n\tNID        uuid.UUID `json:\"-\" faker:\"-\" db:\"nid\"`\n\tIdentityID uuid.UUID `json:\"identity_id\" faker:\"-\" db:\"identity_id\"`\n}\n\nfunc (RecoveryCode) TableName(ctx context.Context) string {\n\treturn \"identity_recovery_codes\"\n}\n\nfunc (f *RecoveryCode) Validate() error {\n\tif f == nil {\n\t\treturn errors.WithStack(ErrCodeNotFound)\n\t}\n\tif f.ExpiresAt.Before(time.Now().UTC()) {\n\t\treturn errors.WithStack(ErrCodeNotFound)\n\t}\n\tif f.UsedAt.Valid {\n\t\treturn errors.WithStack(ErrCodeAlreadyUsed)\n\t}\n\treturn nil\n}\n\nfunc (f *RecoveryCode) GetHMACCode() string {\n\treturn f.CodeHMAC\n}\n\nfunc (f *RecoveryCode) GetID() uuid.UUID {\n\treturn f.ID\n}\n\ntype CreateRecoveryCodeParams struct {\n\t// Code represents the recovery code\n\tRawCode string\n\n\t// CodeType is the type of the code - either \"admin\" or \"selfservice\"\n\tCodeType RecoveryCodeType\n\n\t// ExpiresAt is the time (UTC) when the code expires.\n\t// required: true\n\tExpiresIn time.Duration\n\n\t// RecoveryAddressID is a helper struct field for gobuffalo.pop.\n\tRecoveryAddress *identity.RecoveryAddress\n\n\t// FlowID is a helper struct field for gobuffalo.pop.\n\tFlowID uuid.UUID\n\n\tIdentityID uuid.UUID\n}\n"
  },
  {
    "path": "selfservice/strategy/code/code_recovery_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestRecoveryCode(t *testing.T) {\n\tconf := pkg.NewConfigurationWithDefaults(t)\n\n\tnewCode := func(expiresIn time.Duration, f *recovery.Flow) *code.RecoveryCode {\n\t\treturn &code.RecoveryCode{\n\t\t\tID:        x.NewUUID(),\n\t\t\tFlowID:    f.ID,\n\t\t\tExpiresAt: time.Now().Add(expiresIn),\n\t\t}\n\t}\n\n\treq := &http.Request{URL: urlx.ParseOrPanic(\"https://www.ory.sh/\")}\n\tt.Run(\"method=Validate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"case=returns ErrCodeNotFound if code is expired\", func(t *testing.T) {\n\t\t\tf, err := recovery.NewFlow(conf, -time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(-time.Hour, f)\n\t\t\trequire.ErrorIs(t, c.Validate(), code.ErrCodeNotFound)\n\t\t})\n\t\tt.Run(\"case=expired code does not return flow.ExpiredError\", func(t *testing.T) {\n\t\t\tf, err := recovery.NewFlow(conf, -time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(-time.Hour, f)\n\t\t\texpired := new(flow.ExpiredError)\n\t\t\trequire.False(t, errors.As(c.Validate(), &expired), \"expired code should not return flow.ExpiredError\")\n\t\t})\n\t\tt.Run(\"case=returns no error if flow is not expired\", func(t *testing.T) {\n\t\t\tf, err := recovery.NewFlow(conf, time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\trequire.NoError(t, c.Validate())\n\t\t})\n\n\t\tt.Run(\"case=returns error if flow has been used\", func(t *testing.T) {\n\t\t\tf, err := recovery.NewFlow(conf, -time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\tc.UsedAt = sql.NullTime{\n\t\t\t\tTime:  time.Now(),\n\t\t\t\tValid: true,\n\t\t\t}\n\t\t\trequire.ErrorIs(t, c.Validate(), code.ErrCodeAlreadyUsed)\n\t\t})\n\n\t\tt.Run(\"case=returns no error if flow has not been used\", func(t *testing.T) {\n\t\t\tf, err := recovery.NewFlow(conf, -time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\tc.UsedAt = sql.NullTime{\n\t\t\t\tValid: false,\n\t\t\t}\n\t\t\trequire.NoError(t, c.Validate())\n\t\t})\n\n\t\tt.Run(\"case=returns error if flow is nil\", func(t *testing.T) {\n\t\t\tvar c *code.RecoveryCode\n\t\t\trequire.ErrorIs(t, c.Validate(), code.ErrCodeNotFound)\n\t\t})\n\t})\n}\n\nfunc TestRecoveryCodeType(t *testing.T) {\n\tassert.Equal(t, 1, int(code.RecoveryCodeTypeAdmin))\n\tassert.Equal(t, 2, int(code.RecoveryCodeTypeSelfService))\n}\n"
  },
  {
    "path": "selfservice/strategy/code/code_registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\n// swagger:ignore\ntype RegistrationCode struct {\n\t// ID represents the tokens's unique ID.\n\t//\n\t// required: true\n\t// type: string\n\t// format: uuid\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// Address represents the address that the code was sent to.\n\t// this can be an email address or a phone number.\n\tAddress string `json:\"-\" db:\"address\"`\n\n\t// AddressType represents the type of the address\n\t// this can be an email address or a phone number.\n\tAddressType identity.CodeChannel `json:\"-\" db:\"address_type\"`\n\n\t// CodeHMAC represents the HMACed value of the verification code\n\tCodeHMAC string `json:\"-\" db:\"code\"`\n\n\t// UsedAt is the timestamp of when the code was used or null if it wasn't yet\n\tUsedAt sql.NullTime `json:\"-\" db:\"used_at\"`\n\n\t// ExpiresAt is the time (UTC) when the token expires.\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the token was issued.\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\n\t// FlowID is a helper struct field for gobuffalo.pop.\n\tFlowID uuid.UUID `json:\"-\" faker:\"-\" db:\"selfservice_registration_flow_id\"`\n\n\tNID uuid.UUID `json:\"-\"  faker:\"-\" db:\"nid\"`\n}\n\nfunc (RegistrationCode) TableName(context.Context) string {\n\treturn \"identity_registration_codes\"\n}\n\nfunc (f *RegistrationCode) Validate() error {\n\tif f == nil {\n\t\treturn errors.WithStack(ErrCodeNotFound)\n\t}\n\tif f.ExpiresAt.Before(time.Now().UTC()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\tif f.UsedAt.Valid {\n\t\treturn errors.WithStack(ErrCodeAlreadyUsed)\n\t}\n\treturn nil\n}\n\nfunc (f *RegistrationCode) GetHMACCode() string {\n\treturn f.CodeHMAC\n}\n\nfunc (f *RegistrationCode) GetID() uuid.UUID {\n\treturn f.ID\n}\n\n// swagger:ignore\ntype CreateRegistrationCodeParams struct {\n\t// Address is the email address or phone number the code should be sent to.\n\t// required: true\n\tAddress string\n\n\t// AddressType is the type of the address (email or phone number).\n\t// required: true\n\tAddressType identity.CodeChannel\n\n\t// Code represents the recovery code\n\t// required: true\n\tRawCode string\n\n\t// ExpiresAt is the time (UTC) when the code expires.\n\t// required: true\n\tExpiresIn time.Duration\n\n\t// FlowID is a helper struct field for gobuffalo.pop.\n\t// required: true\n\tFlowID uuid.UUID\n}\n"
  },
  {
    "path": "selfservice/strategy/code/code_registration_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"database/sql\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestRegistrationCode(t *testing.T) {\n\tconf := pkg.NewConfigurationWithDefaults(t)\n\tnewCode := func(expiresIn time.Duration, f *registration.Flow) *code.RegistrationCode {\n\t\treturn &code.RegistrationCode{\n\t\t\tID:        x.NewUUID(),\n\t\t\tFlowID:    f.ID,\n\t\t\tExpiresAt: time.Now().Add(expiresIn),\n\t\t}\n\t}\n\n\treq := &http.Request{URL: urlx.ParseOrPanic(\"https://www.ory.sh/\")}\n\tt.Run(\"method=Validate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"case=returns error if flow is expired\", func(t *testing.T) {\n\t\t\tf, err := registration.NewFlow(conf, -time.Hour, \"\", req, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(-time.Hour, f)\n\t\t\texpected := new(flow.ExpiredError)\n\t\t\trequire.ErrorAs(t, c.Validate(), &expected)\n\t\t})\n\t\tt.Run(\"case=returns no error if flow is not expired\", func(t *testing.T) {\n\t\t\tf, err := registration.NewFlow(conf, time.Hour, \"\", req, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\trequire.NoError(t, c.Validate())\n\t\t})\n\n\t\tt.Run(\"case=returns error if flow has been used\", func(t *testing.T) {\n\t\t\tf, err := registration.NewFlow(conf, -time.Hour, \"\", req, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\tc.UsedAt = sql.NullTime{\n\t\t\t\tTime:  time.Now(),\n\t\t\t\tValid: true,\n\t\t\t}\n\t\t\trequire.ErrorIs(t, c.Validate(), code.ErrCodeAlreadyUsed)\n\t\t})\n\n\t\tt.Run(\"case=returns no error if flow has not been used\", func(t *testing.T) {\n\t\t\tf, err := registration.NewFlow(conf, -time.Hour, \"\", req, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\tc.UsedAt = sql.NullTime{\n\t\t\t\tValid: false,\n\t\t\t}\n\t\t\trequire.NoError(t, c.Validate())\n\t\t})\n\n\t\tt.Run(\"case=returns error if flow is nil\", func(t *testing.T) {\n\t\t\tvar c *code.RegistrationCode\n\t\t\trequire.ErrorIs(t, c.Validate(), code.ErrCodeNotFound)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/code_sender.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/courier/template/sms\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/stringsx\"\n\t\"github.com/ory/x/urlx\"\n)\n\ntype (\n\tsenderDependencies interface {\n\t\tcourier.Provider\n\t\tcourier.ConfigProvider\n\n\t\tidentity.PoolProvider\n\t\tidentity.ManagementProvider\n\t\tidentity.PrivilegedPoolProvider\n\t\tlogrusx.Provider\n\t\tconfig.Provider\n\n\t\tRecoveryCodePersistenceProvider\n\t\tVerificationCodePersistenceProvider\n\t\tRegistrationCodePersistenceProvider\n\t\tLoginCodePersistenceProvider\n\n\t\thttpx.ClientProvider\n\t}\n\tSenderProvider interface {\n\t\tCodeSender() *Sender\n\t}\n\n\tSender struct {\n\t\tdeps senderDependencies\n\t}\n\tAddress struct {\n\t\tTo  string\n\t\tVia identity.CodeChannel\n\t}\n)\n\nvar ErrUnknownAddress = herodot.ErrNotFound.WithReason(\"recovery requested for unknown address\")\n\nfunc NewSender(deps senderDependencies) *Sender {\n\treturn &Sender{deps: deps}\n}\n\nfunc (s *Sender) SendCode(ctx context.Context, f flow.Flow, id *identity.Identity, header http.Header, addresses ...Address) error {\n\ts.deps.Logger().\n\t\tWithSensitiveField(\"address\", addresses).\n\t\tDebugf(\"Preparing %s code\", f.GetFlowName())\n\n\ttransientPayload, err := x.ParseRawMessageOrEmpty(f.GetTransientPayload())\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\t// send to all addresses\n\tfor _, address := range addresses {\n\t\t// We have to generate a unique code per address, or otherwise it is not possible to link which\n\t\t// address was used to verify the code.\n\t\t//\n\t\t// See also [this discussion](https://github.com/ory/kratos/pull/3456#discussion_r1307560988).\n\t\trawCode := GenerateCode()\n\n\t\tswitch f.GetFlowName() {\n\t\tcase flow.RegistrationFlow:\n\t\t\tcode, err := s.deps.\n\t\t\t\tRegistrationCodePersister().\n\t\t\t\tCreateRegistrationCode(ctx, &CreateRegistrationCodeParams{\n\t\t\t\t\tAddressType: address.Via,\n\t\t\t\t\tRawCode:     rawCode,\n\t\t\t\t\tExpiresIn:   s.deps.Config().SelfServiceCodeMethodLifespan(ctx),\n\t\t\t\t\tFlowID:      f.GetID(),\n\t\t\t\t\tAddress:     address.To,\n\t\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tmodel, err := x.StructToMap(id.Traits)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\ts.deps.Logger().\n\t\t\t\tWithField(\"registration_flow_id\", code.FlowID).\n\t\t\t\tWithField(\"registration_code_id\", code.ID).\n\t\t\t\tWithSensitiveField(\"registration_code\", rawCode).\n\t\t\t\tInfo(\"Sending out registration email with code.\")\n\n\t\t\tvar t courier.Template\n\t\t\tswitch address.Via {\n\t\t\tcase identity.ChannelTypeEmail:\n\t\t\t\tt = email.NewRegistrationCodeValid(s.deps, &email.RegistrationCodeValidModel{\n\t\t\t\t\tTo:                 address.To,\n\t\t\t\t\tRegistrationCode:   rawCode,\n\t\t\t\t\tTraits:             model,\n\t\t\t\t\tRequestURL:         f.GetRequestURL(),\n\t\t\t\t\tTransientPayload:   transientPayload,\n\t\t\t\t\tExpiresInMinutes:   int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()),\n\t\t\t\t\tUserRequestHeaders: hook.RemoveDisallowedHeaders(header, s.deps.Config().WebhookHeaderAllowlist(ctx)),\n\t\t\t\t})\n\t\t\tcase identity.ChannelTypeSMS:\n\t\t\t\tt = sms.NewRegistrationCodeValid(s.deps, &sms.RegistrationCodeValidModel{\n\t\t\t\t\tTo:                 address.To,\n\t\t\t\t\tRegistrationCode:   rawCode,\n\t\t\t\t\tIdentity:           model,\n\t\t\t\t\tRequestURL:         f.GetRequestURL(),\n\t\t\t\t\tTransientPayload:   transientPayload,\n\t\t\t\t\tExpiresInMinutes:   int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()),\n\t\t\t\t\tUserRequestHeaders: hook.RemoveDisallowedHeaders(header, s.deps.Config().WebhookHeaderAllowlist(ctx)),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tif err := s.send(ctx, string(address.Via), t); err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\n\t\tcase flow.LoginFlow:\n\t\t\tcode, err := s.deps.\n\t\t\t\tLoginCodePersister().\n\t\t\t\tCreateLoginCode(ctx, &CreateLoginCodeParams{\n\t\t\t\t\tAddressType: address.Via,\n\t\t\t\t\tAddress:     address.To,\n\t\t\t\t\tRawCode:     rawCode,\n\t\t\t\t\tExpiresIn:   s.deps.Config().SelfServiceCodeMethodLifespan(ctx),\n\t\t\t\t\tFlowID:      f.GetID(),\n\t\t\t\t\tIdentityID:  id.ID,\n\t\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tmodel, err := x.StructToMap(id)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ts.deps.Logger().\n\t\t\t\tWithField(\"login_flow_id\", code.FlowID).\n\t\t\t\tWithField(\"login_code_id\", code.ID).\n\t\t\t\tWithSensitiveField(\"login_code\", rawCode).\n\t\t\t\tInfo(\"Sending out login email with code.\")\n\n\t\t\tvar t courier.Template\n\t\t\tswitch address.Via {\n\t\t\tcase identity.ChannelTypeEmail:\n\t\t\t\tt = email.NewLoginCodeValid(s.deps, &email.LoginCodeValidModel{\n\t\t\t\t\tTo:                 address.To,\n\t\t\t\t\tLoginCode:          rawCode,\n\t\t\t\t\tIdentity:           model,\n\t\t\t\t\tRequestURL:         f.GetRequestURL(),\n\t\t\t\t\tTransientPayload:   transientPayload,\n\t\t\t\t\tExpiresInMinutes:   int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()),\n\t\t\t\t\tUserRequestHeaders: hook.RemoveDisallowedHeaders(header, s.deps.Config().WebhookHeaderAllowlist(ctx)),\n\t\t\t\t})\n\t\t\tcase identity.ChannelTypeSMS:\n\t\t\t\tt = sms.NewLoginCodeValid(s.deps, &sms.LoginCodeValidModel{\n\t\t\t\t\tTo:                 address.To,\n\t\t\t\t\tLoginCode:          rawCode,\n\t\t\t\t\tIdentity:           model,\n\t\t\t\t\tRequestURL:         f.GetRequestURL(),\n\t\t\t\t\tTransientPayload:   transientPayload,\n\t\t\t\t\tExpiresInMinutes:   int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()),\n\t\t\t\t\tUserRequestHeaders: hook.RemoveDisallowedHeaders(header, s.deps.Config().WebhookHeaderAllowlist(ctx)),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tif err := s.send(ctx, string(address.Via), t); err != nil {\n\t\t\t\treturn errors.WithStack(err)\n\t\t\t}\n\n\t\tdefault:\n\t\t\treturn errors.WithStack(errors.New(\"received unknown flow type\"))\n\n\t\t}\n\t}\n\treturn nil\n}\n\n// SendRecoveryCode sends a recovery code to the specified address\n//\n// If the address does not exist in the store and dispatching invalid emails is enabled (CourierEnableInvalidDispatch is\n// true), an email is still being sent to prevent account enumeration attacks. In that case, this function returns the\n// ErrUnknownAddress error.\nfunc (s *Sender) SendRecoveryCode(ctx context.Context, f *recovery.Flow, via, to string, requestHeader http.Header) error {\n\ts.deps.Logger().\n\t\tWithField(\"via\", via).\n\t\tWithSensitiveField(\"address\", to).\n\t\tDebug(\"Preparing recovery code.\")\n\n\taddress, err := s.deps.IdentityPool().FindRecoveryAddressByValue(ctx, via, to)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\tnotifyUnknownRecipients := s.deps.Config().SelfServiceFlowRecoveryNotifyUnknownRecipients(ctx)\n\t\ts.deps.Logger().\n\t\t\tWithField(\"via\", via).\n\t\t\tWithSensitiveField(\"address\", to).\n\t\t\tWithField(\"strategy\", \"code\").\n\t\t\tWithField(\"was_notified\", notifyUnknownRecipients).\n\t\t\tInfo(\"Account recovery was requested for an unknown address.\")\n\n\t\ttransientPayload, err := x.ParseRawMessageOrEmpty(f.GetTransientPayload())\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\t// We only send a notification if the configuration allows it *and* the channel is email.\n\t\t// That's because we pay per SMS sent (typically) so we want to avoid that, contrary to email.\n\t\tshouldNotifyOfUnkownRecipient := notifyUnknownRecipients && via == identity.AddressTypeEmail\n\n\t\tif !shouldNotifyOfUnkownRecipient {\n\t\t\t// do nothing\n\t\t} else if err := s.send(ctx, via, email.NewRecoveryCodeInvalid(s.deps, &email.RecoveryCodeInvalidModel{\n\t\t\tTo:               to,\n\t\t\tRequestURL:       f.RequestURL,\n\t\t\tTransientPayload: transientPayload,\n\t\t})); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn errors.WithStack(ErrUnknownAddress)\n\t} else if err != nil {\n\t\t// DB error\n\t\treturn err\n\t}\n\n\t// Get the identity associated with the recovery address\n\ti, err := s.deps.IdentityPool().GetIdentity(ctx, address.IdentityID, identity.ExpandDefault)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trawCode := GenerateCode()\n\n\tvar code *RecoveryCode\n\tif code, err = s.deps.\n\t\tRecoveryCodePersister().\n\t\tCreateRecoveryCode(ctx, &CreateRecoveryCodeParams{\n\t\t\tRawCode:         rawCode,\n\t\t\tCodeType:        RecoveryCodeTypeSelfService,\n\t\t\tExpiresIn:       s.deps.Config().SelfServiceCodeMethodLifespan(ctx),\n\t\t\tRecoveryAddress: address,\n\t\t\tFlowID:          f.ID,\n\t\t\tIdentityID:      i.ID,\n\t\t}); err != nil {\n\t\treturn err\n\t}\n\n\treturn s.SendRecoveryCodeTo(ctx, i, rawCode, code, f, requestHeader)\n}\n\nfunc (s *Sender) SendRecoveryCodeTo(ctx context.Context, i *identity.Identity, codeString string, code *RecoveryCode, f *recovery.Flow, requestHeader http.Header) error {\n\ts.deps.Logger().\n\t\tWithField(\"via\", code.RecoveryAddress.Via).\n\t\tWithField(\"identity_id\", code.RecoveryAddress.IdentityID).\n\t\tWithField(\"recovery_code_id\", code.ID).\n\t\tWithSensitiveField(\"address\", code.RecoveryAddress.Value).\n\t\tWithSensitiveField(\"recovery_code\", codeString).\n\t\tInfo(\"Sending out recovery code.\")\n\n\tmodel, err := x.StructToMap(i)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttransientPayload, err := x.ParseRawMessageOrEmpty(f.GetTransientPayload())\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tvar t courier.Template\n\n\tswitch code.RecoveryAddress.Via {\n\tcase identity.AddressTypeEmail:\n\t\tt = email.NewRecoveryCodeValid(s.deps, &email.RecoveryCodeValidModel{\n\t\t\tTo:                 code.RecoveryAddress.Value,\n\t\t\tRecoveryCode:       codeString,\n\t\t\tIdentity:           model,\n\t\t\tRequestURL:         f.GetRequestURL(),\n\t\t\tTransientPayload:   transientPayload,\n\t\t\tExpiresInMinutes:   int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()),\n\t\t\tUserRequestHeaders: hook.RemoveDisallowedHeaders(requestHeader, s.deps.Config().WebhookHeaderAllowlist(ctx)),\n\t\t})\n\tcase identity.AddressTypeSMS:\n\t\tu, err := url.Parse(f.GetRequestURL())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tt = sms.NewRecoveryCodeValid(s.deps, &sms.RecoveryCodeValidModel{\n\t\t\tTo:                 code.RecoveryAddress.Value,\n\t\t\tRecoveryCode:       codeString,\n\t\t\tIdentity:           model,\n\t\t\tRequestURL:         f.GetRequestURL(),\n\t\t\tRequestURLDomain:   u.Hostname(),\n\t\t\tTransientPayload:   transientPayload,\n\t\t\tExpiresInMinutes:   int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()),\n\t\t\tUserRequestHeaders: hook.RemoveDisallowedHeaders(requestHeader, s.deps.Config().WebhookHeaderAllowlist(ctx)),\n\t\t})\n\tdefault:\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected email or sms but got %s\", code.RecoveryAddress.Via))\n\t}\n\n\treturn s.send(ctx, code.RecoveryAddress.Via, t)\n}\n\n// SendVerificationCode sends a verification code & link to the specified address\n//\n// If the address does not exist in the store and dispatching invalid emails is enabled (CourierEnableInvalidDispatch is\n// true), an email is still being sent to prevent account enumeration attacks. In that case, this function returns the\n// ErrUnknownAddress error.\nfunc (s *Sender) SendVerificationCode(ctx context.Context, f *verification.Flow, via string, to string) error {\n\ts.deps.Logger().\n\t\tWithField(\"via\", via).\n\t\tWithSensitiveField(\"address\", to).\n\t\tDebug(\"Preparing verification code.\")\n\n\taddress, err := s.deps.IdentityPool().FindVerifiableAddressByValue(ctx, via, to)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\tnotifyUnknownRecipients := s.deps.Config().SelfServiceFlowVerificationNotifyUnknownRecipients(ctx)\n\t\ts.deps.Logger().\n\t\t\tWithField(\"via\", via).\n\t\t\tWithField(\"strategy\", \"code\").\n\t\t\tWithSensitiveField(\"email_address\", to).\n\t\t\tWithField(\"was_notified\", notifyUnknownRecipients).\n\t\t\tInfo(\"Address verification was requested for an unknown address.\")\n\n\t\ttransientPayload, err := x.ParseRawMessageOrEmpty(f.GetTransientPayload())\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tif !notifyUnknownRecipients {\n\t\t\t// do nothing\n\t\t} else if err := s.send(ctx, via, email.NewVerificationCodeInvalid(s.deps, &email.VerificationCodeInvalidModel{\n\t\t\tTo:               to,\n\t\t\tRequestURL:       f.GetRequestURL(),\n\t\t\tTransientPayload: transientPayload,\n\t\t})); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn errors.WithStack(ErrUnknownAddress)\n\n\t} else if err != nil {\n\t\treturn err\n\t}\n\n\trawCode := GenerateCode()\n\tvar code *VerificationCode\n\tif code, err = s.deps.VerificationCodePersister().CreateVerificationCode(ctx, &CreateVerificationCodeParams{\n\t\tRawCode:           rawCode,\n\t\tExpiresIn:         s.deps.Config().SelfServiceCodeMethodLifespan(ctx),\n\t\tVerifiableAddress: address,\n\t\tFlowID:            f.ID,\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\t// Get the identity associated with the recovery address\n\ti, err := s.deps.IdentityPool().GetIdentity(ctx, address.IdentityID, identity.ExpandDefault)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn s.SendVerificationCodeTo(ctx, f, i, rawCode, code)\n}\n\nfunc (s *Sender) constructVerificationLink(ctx context.Context, fID uuid.UUID, codeStr string) string {\n\treturn urlx.CopyWithQuery(\n\t\turlx.AppendPaths(s.deps.Config().SelfServiceLinkMethodBaseURL(ctx), verification.RouteSubmitFlow),\n\t\turl.Values{\n\t\t\t\"flow\": {fID.String()},\n\t\t\t\"code\": {codeStr},\n\t\t}).String()\n}\n\nfunc (s *Sender) SendVerificationCodeTo(ctx context.Context, f *verification.Flow, i *identity.Identity, codeString string, code *VerificationCode) error {\n\ts.deps.Logger().\n\t\tWithField(\"via\", code.VerifiableAddress.Via).\n\t\tWithField(\"identity_id\", i.ID).\n\t\tWithField(\"verification_code_id\", code.ID).\n\t\tWithSensitiveField(\"email_address\", code.VerifiableAddress.Value).\n\t\tWithSensitiveField(\"verification_link_token\", codeString).\n\t\tInfo(\"Sending out verification email with verification code.\")\n\n\tmodel, err := x.StructToMap(i)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttransientPayload, err := x.ParseRawMessageOrEmpty(f.GetTransientPayload())\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tvar t courier.Template\n\n\t// TODO: this can likely be abstracted by making templates not specific to the channel they're using\n\tswitch code.VerifiableAddress.Via {\n\tcase identity.ChannelTypeEmail:\n\t\tt = email.NewVerificationCodeValid(s.deps, &email.VerificationCodeValidModel{\n\t\t\tTo:               code.VerifiableAddress.Value,\n\t\t\tVerificationURL:  s.constructVerificationLink(ctx, f.ID, codeString),\n\t\t\tIdentity:         model,\n\t\t\tVerificationCode: codeString,\n\t\t\tRequestURL:       f.GetRequestURL(),\n\t\t\tTransientPayload: transientPayload,\n\t\t\tExpiresInMinutes: int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()),\n\t\t})\n\tcase identity.ChannelTypeSMS:\n\t\tt = sms.NewVerificationCodeValid(s.deps, &sms.VerificationCodeValidModel{\n\t\t\tTo:               code.VerifiableAddress.Value,\n\t\t\tVerificationCode: codeString,\n\t\t\tIdentity:         model,\n\t\t\tRequestURL:       f.GetRequestURL(),\n\t\t\tTransientPayload: transientPayload,\n\t\t\tExpiresInMinutes: int(s.deps.Config().SelfServiceCodeMethodLifespan(ctx).Minutes()),\n\t\t})\n\tdefault:\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected email or sms but got %s\", code.VerifiableAddress.Via))\n\t}\n\n\tif err := s.send(ctx, code.VerifiableAddress.Via, t); err != nil {\n\t\treturn err\n\t}\n\tcode.VerifiableAddress.Status = identity.VerifiableAddressStatusSent\n\treturn s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, code.VerifiableAddress, \"status\")\n}\n\nfunc (s *Sender) send(ctx context.Context, via string, t courier.Template) error {\n\tswitch f := stringsx.SwitchExact(via); {\n\tcase f.AddCase(identity.ChannelTypeEmail):\n\t\tc, err := s.deps.Courier(ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tt, ok := t.(courier.EmailTemplate)\n\t\tif !ok {\n\t\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected email template but got %T\", t))\n\t\t}\n\n\t\t_, err = c.QueueEmail(ctx, t)\n\t\treturn err\n\tcase f.AddCase(identity.ChannelTypeSMS):\n\t\tc, err := s.deps.Courier(ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tt, ok := t.(courier.SMSTemplate)\n\t\tif !ok {\n\t\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected sms template but got %T\", t))\n\t\t}\n\n\t\t_, err = c.QueueSMS(ctx, t)\n\t\treturn err\n\tdefault:\n\t\treturn f.ToUnknownCaseErr()\n\t}\n}\n\n// hackyInferChannel infers the channel (email or sms) based on the address format.\n// Once we support arbitrary custom channels, we need to refactor the recovery/verification\n// flow to first look up the address, and let the user select the channel if multiple are available.\nfunc hackyInferChannel(addr string) string {\n\t// Inferring the address type like this is a bit hacky, and actually not really necessary.\n\t// That's because `SendRecoveryCode` expects it, but not because it fundamentally is required.\n\tif strings.ContainsRune(addr, '@') {\n\t\treturn identity.AddressTypeEmail\n\t}\n\treturn identity.AddressTypeSMS\n}\n"
  },
  {
    "path": "selfservice/strategy/code/code_sender_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/http\"\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/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/x/urlx\"\n)\n\nvar b64 = func(str string) string {\n\treturn base64.StdEncoding.EncodeToString([]byte(str))\n}\n\nfunc TestSender(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/default.schema.json\")),\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeyPublicBaseURL:                                  \"https://www.ory.sh/\",\n\t\t\tconfig.ViperKeyCourierSMTPURL:                                 \"smtp://foo@bar@dev.null/\",\n\t\t\tconfig.ViperKeySelfServiceRecoveryNotifyUnknownRecipients:     true,\n\t\t\tconfig.ViperKeySelfServiceVerificationNotifyUnknownRecipients: true,\n\t\t}),\n\t)\n\n\tconf.MustSet(ctx, config.ViperKeyWebhookHeaderAllowlist, []string{\"user-agent\", \"X-CUSTOM-HEADER\"})\n\n\tu := &http.Request{URL: urlx.ParseOrPanic(\"https://www.ory.sh/\")}\n\n\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\n\t// Fake phone numbers which are unallocated numbers that telephone companies cannot assign\n\t// to customers in Germany under current legislation.\n\t// This is to protect residents against the potential influx of phone calls\n\t// that they may receive should their telephone numbers appear in a movie or film.\n\tphoneNumberKnown := \"+49-160-555-5762\"\n\tphoneNumberUnknown := \"+49-155-555-4570\"\n\ti.Traits = identity.Traits(fmt.Sprintf(`{\"email\": \"tracked@ory.sh\", \"phone\": \"%s\"}`, phoneNumberKnown))\n\trequire.NoError(t, reg.IdentityManager().Create(ctx, i))\n\n\tt.Run(\"method=SendRecoveryCode email\", func(t *testing.T) {\n\t\trecoveryCode := func(t *testing.T) {\n\t\t\tt.Helper()\n\t\t\tf, err := recovery.NewFlow(conf, time.Hour, \"\", u, recovery.Strategies{code.NewStrategy(reg)}, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(ctx, f))\n\n\t\t\theader := http.Header{}\n\t\t\theader.Add(\"user-agent\", \"user-agent header\")\n\t\t\theader.Add(\"X-CUSTOM-HEADER\", \"x-custom header 1\")\n\t\t\theader.Add(\"X-Custom-header\", \"x-custom header 2\")\n\t\t\theader.Add(\"some-other-header\", \"some-other-value\")\n\t\t\trequire.NoError(t, reg.CodeSender().SendRecoveryCode(ctx, f, \"email\", \"tracked@ory.sh\", header))\n\t\t\trequire.ErrorIs(t, reg.CodeSender().SendRecoveryCode(ctx, f, \"email\", \"not-tracked@ory.sh\", header), code.ErrUnknownAddress)\n\t\t}\n\n\t\tt.Run(\"case=with default templates\", func(t *testing.T) {\n\t\t\trecoveryCode(t)\n\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 12)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, messages, 2)\n\n\t\t\tassert.EqualValues(t, \"tracked@ory.sh\", messages[0].Recipient)\n\t\t\tassert.Contains(t, messages[0].Subject, \"Use code\")\n\n\t\t\tassert.Regexp(t, testhelpers.CodeRegex, messages[0].Body)\n\n\t\t\tassert.EqualValues(t, \"not-tracked@ory.sh\", messages[1].Recipient)\n\t\t\tassert.Contains(t, messages[1].Subject, \"Account access attempted\")\n\n\t\t\tassert.NotRegexp(t, testhelpers.CodeRegex, messages[1].Body, \"Expected message to not contain an 6 digit recovery code, but it did: \", messages[1].Body)\n\n\t\t\theaders := messages[0].RequestHeaders\n\t\t\tassert.Equal(t, []any{\"user-agent header\"}, gjson.GetBytes(headers, \"User-Agent\").Value())\n\t\t\tassert.Equal(t, []any{\"x-custom header 1\", \"x-custom header 2\"}, gjson.GetBytes(headers, \"X-Custom-Header\").Value())\n\t\t\tassert.Empty(t, gjson.GetBytes(headers, \"Some-Other-Header\").Value(), \"Expected Some-Other-Header to be empty\")\n\t\t})\n\n\t\tt.Run(\"case=with custom templates\", func(t *testing.T) {\n\t\t\tsubject := \"custom template recovery code\"\n\t\t\tbody := \"custom template recovery code body\"\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesRecoveryCodeInvalidEmail, nil)\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesRecoveryCodeValidEmail, nil)\n\t\t\t})\n\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesRecoveryCodeInvalidEmail, fmt.Sprintf(`{ \"subject\": \"base64://%s\", \"body\": { \"plaintext\": \"base64://%s\", \"html\": \"base64://%s\" }}`, b64(subject+\" invalid\"), b64(body), b64(body)))\n\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesRecoveryCodeValidEmail, fmt.Sprintf(`{ \"subject\": \"base64://%s\", \"body\": { \"plaintext\": \"base64://%s\", \"html\": \"base64://%s\" }}`, b64(subject+\" valid\"), b64(body+\" {{ .RecoveryCode }}\"), b64(body+\" {{ .RecoveryCode }}\")))\n\t\t\trecoveryCode(t)\n\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 12)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, messages, 2)\n\n\t\t\tassert.EqualValues(t, \"tracked@ory.sh\", messages[0].Recipient)\n\t\t\tassert.Equal(t, messages[0].Subject, subject+\" valid\")\n\t\t\tassert.Contains(t, messages[0].Body, body)\n\n\t\t\tassert.Regexp(t, testhelpers.CodeRegex, messages[0].Body)\n\n\t\t\tassert.EqualValues(t, \"not-tracked@ory.sh\", messages[1].Recipient)\n\t\t\tassert.Equal(t, messages[1].Subject, subject+\" invalid\")\n\t\t\tassert.Equal(t, messages[1].Body, body)\n\n\t\t\theaders := messages[0].RequestHeaders\n\t\t\tassert.Equal(t, []any{\"user-agent header\"}, gjson.GetBytes(headers, \"User-Agent\").Value())\n\t\t\tassert.Equal(t, []any{\"x-custom header 1\", \"x-custom header 2\"}, gjson.GetBytes(headers, \"X-Custom-Header\").Value())\n\t\t\tassert.Empty(t, gjson.GetBytes(headers, \"Some-Other-Header\").Value(), \"Expected Some-Other-Header to be empty\")\n\t\t})\n\t})\n\n\tt.Run(\"method=SendRecoveryCode sms\", func(t *testing.T) {\n\t\trecoveryCode := func(t *testing.T) {\n\t\t\tt.Helper()\n\t\t\tf, err := recovery.NewFlow(conf, time.Hour, \"\", u, recovery.Strategies{code.NewStrategy(reg)}, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(ctx, f))\n\n\t\t\theader := http.Header{}\n\t\t\theader.Add(\"user-agent\", \"user-agent header\")\n\t\t\theader.Add(\"X-CUSTOM-HEADER\", \"x-custom header 1\")\n\t\t\theader.Add(\"X-Custom-header\", \"x-custom header 2\")\n\t\t\theader.Add(\"some-other-header\", \"some-other-value\")\n\t\t\trequire.NoError(t, reg.CodeSender().SendRecoveryCode(ctx, f, \"sms\", phoneNumberKnown, header))\n\t\t\trequire.ErrorIs(t, reg.CodeSender().SendRecoveryCode(ctx, f, \"sms\", phoneNumberUnknown, header), code.ErrUnknownAddress)\n\t\t}\n\n\t\tt.Run(\"case=with default templates\", func(t *testing.T) {\n\t\t\trecoveryCode(t)\n\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 12)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, messages, 1)\n\n\t\t\tassert.EqualValues(t, phoneNumberKnown, messages[0].Recipient)\n\t\t\tassert.Contains(t, messages[0].Body, \"Your recovery code is:\")\n\t\t\tassert.Contains(t, messages[0].Body, \"@www.ory.sh #\")\n\n\t\t\tassert.Regexp(t, testhelpers.CodeRegex, messages[0].Body)\n\t\t})\n\n\t\tt.Run(\"case=with custom templates\", func(t *testing.T) {\n\t\t\tbody := \"custom template recovery code body\"\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesRecoveryCodeValidSMS, nil)\n\t\t\t})\n\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesRecoveryCodeValidSMS, fmt.Sprintf(`{ \"body\": { \"plaintext\": \"base64://%s\"}}`, b64(body+\" {{ .RecoveryCode }}\")))\n\t\t\trecoveryCode(t)\n\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 12)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, messages, 1)\n\n\t\t\tassert.EqualValues(t, phoneNumberKnown, messages[0].Recipient)\n\t\t\tassert.Contains(t, messages[0].Body, body)\n\t\t\theaders := messages[0].RequestHeaders\n\t\t\tassert.Equal(t, []any{\"user-agent header\"}, gjson.GetBytes(headers, \"User-Agent\").Value())\n\t\t\tassert.Equal(t, []any{\"x-custom header 1\", \"x-custom header 2\"}, gjson.GetBytes(headers, \"X-Custom-Header\").Value())\n\t\t\tassert.Empty(t, gjson.GetBytes(headers, \"Some-Other-Header\").Value(), \"Expected Some-Other-Header to be empty\")\n\n\t\t\tassert.Regexp(t, testhelpers.CodeRegex, messages[0].Body)\n\t\t})\n\t})\n\n\tt.Run(\"method=SendVerificationCode\", func(t *testing.T) {\n\t\tverificationFlow := func(t *testing.T) {\n\t\t\tt.Helper()\n\n\t\t\tf, err := verification.NewFlow(conf, time.Hour, \"\", u, verification.Strategies{code.NewStrategy(reg)}, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(ctx, f))\n\n\t\t\trequire.NoError(t, reg.CodeSender().SendVerificationCode(ctx, f, \"email\", \"tracked@ory.sh\"))\n\t\t\trequire.ErrorIs(t, reg.CodeSender().SendVerificationCode(ctx, f, \"email\", \"not-tracked@ory.sh\"), code.ErrUnknownAddress)\n\t\t}\n\n\t\tt.Run(\"case=with default templates\", func(t *testing.T) {\n\t\t\tverificationFlow(t)\n\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 12)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, messages, 2)\n\n\t\t\tassert.EqualValues(t, \"tracked@ory.sh\", messages[0].Recipient)\n\t\t\tassert.Contains(t, messages[0].Subject, \"Use code\")\n\n\t\t\tassert.Regexp(t, testhelpers.CodeRegex, messages[0].Body)\n\n\t\t\tassert.EqualValues(t, \"not-tracked@ory.sh\", messages[1].Recipient)\n\t\t\tassert.Contains(t, messages[1].Subject, \"Someone tried to verify this email address\")\n\n\t\t\tassert.NotRegexp(t, testhelpers.CodeRegex, messages[1].Body, \"Expected message to not contain an 6 digit recovery code, but it did: \", messages[1].Body)\n\t\t})\n\n\t\tt.Run(\"case=with custom templates\", func(t *testing.T) {\n\t\t\tsubject := \"custom template verification code\"\n\t\t\tbody := \"custom template verification code body\"\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesVerificationCodeInvalidEmail, nil)\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesVerificationCodeValidEmail, nil)\n\t\t\t})\n\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesVerificationCodeInvalidEmail, fmt.Sprintf(`{ \"subject\": \"base64://%s\", \"body\": { \"plaintext\": \"base64://%s\", \"html\": \"base64://%s\" }}`, b64(subject+\" invalid\"), b64(body), b64(body)))\n\t\t\tconf.MustSet(ctx, config.ViperKeyCourierTemplatesVerificationCodeValidEmail, fmt.Sprintf(`{ \"subject\": \"base64://%s\", \"body\": { \"plaintext\": \"base64://%s\", \"html\": \"base64://%s\" }}`, b64(subject+\" valid\"), b64(body+\" {{ .VerificationCode }}\"), b64(body+\" {{ .VerificationCode }}\")))\n\t\t\tverificationFlow(t)\n\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 12)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, messages, 2)\n\n\t\t\tassert.EqualValues(t, \"tracked@ory.sh\", messages[0].Recipient)\n\t\t\tassert.Equal(t, messages[0].Subject, subject+\" valid\")\n\t\t\tassert.Contains(t, messages[0].Body, body)\n\n\t\t\tassert.Regexp(t, testhelpers.CodeRegex, messages[0].Body)\n\n\t\t\tassert.EqualValues(t, \"not-tracked@ory.sh\", messages[1].Recipient)\n\t\t\tassert.Equal(t, messages[1].Subject, subject+\" invalid\")\n\t\t\tassert.Equal(t, messages[1].Body, body)\n\t\t})\n\t})\n\n\tt.Run(\"case=should be able to disable invalid email dispatch\", func(t *testing.T) {\n\t\tfor _, tc := range []struct {\n\t\t\tflow      string\n\t\t\tsend      func(t *testing.T)\n\t\t\tconfigKey string\n\t\t}{\n\t\t\t{\n\t\t\t\tflow:      \"recovery\",\n\t\t\t\tconfigKey: config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients,\n\t\t\t\tsend: func(t *testing.T) {\n\t\t\t\t\ts, _, err := reg.RecoveryStrategies(ctx).ActiveStrategies(\"code\")\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tf, err := recovery.NewFlow(conf, time.Hour, \"\", u, s, flow.TypeBrowser)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\trequire.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(ctx, f))\n\n\t\t\t\t\terr = reg.CodeSender().SendRecoveryCode(ctx, f, \"email\", \"not-tracked@ory.sh\", nil)\n\t\t\t\t\trequire.ErrorIs(t, err, code.ErrUnknownAddress)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tflow:      \"verification\",\n\t\t\t\tconfigKey: config.ViperKeySelfServiceVerificationNotifyUnknownRecipients,\n\t\t\t\tsend: func(t *testing.T) {\n\t\t\t\t\ts, _, err := reg.VerificationStrategies(ctx).ActiveStrategies(\"code\")\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tf, err := verification.NewFlow(conf, time.Hour, \"\", u, s, flow.TypeBrowser)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(ctx, f))\n\n\t\t\t\t\terr = reg.CodeSender().SendVerificationCode(ctx, f, \"email\", \"not-tracked@ory.sh\")\n\t\t\t\t\trequire.ErrorIs(t, err, code.ErrUnknownAddress)\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"strategy=\"+tc.flow, func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, tc.configKey, false)\n\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, tc.configKey, true)\n\t\t\t\t})\n\n\t\t\t\ttc.send(t)\n\n\t\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 0)\n\n\t\t\t\trequire.ErrorIs(t, err, courier.ErrQueueEmpty)\n\t\t\t\trequire.Len(t, messages, 0)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/code_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n)\n\ntype VerificationCode struct {\n\t// ID represents the code's unique ID.\n\t//\n\t// required: true\n\t// type: string\n\t// format: uuid\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// CodeHMAC represents the HMACed value of the verification code\n\tCodeHMAC string `json:\"-\" db:\"code_hmac\"`\n\n\t// UsedAt is the timestamp of when the code was used or null if it wasn't yet\n\tUsedAt sql.NullTime `json:\"-\" db:\"used_at\"`\n\n\t// VerifiableAddress links this code to a verification address.\n\t// required: true\n\tVerifiableAddress *identity.VerifiableAddress `json:\"verification_address\" belongs_to:\"identity_verifiable_addresses\"`\n\n\t// ExpiresAt is the time (UTC) when the code expires.\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the code was issued.\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\t// VerifiableAddressID is a helper struct field for gobuffalo.pop.\n\tVerifiableAddressID uuid.NullUUID `json:\"-\" faker:\"-\" db:\"identity_verifiable_address_id\"`\n\t// FlowID is a helper struct field for gobuffalo.pop.\n\tFlowID uuid.UUID `json:\"-\" faker:\"-\" db:\"selfservice_verification_flow_id\"`\n\tNID    uuid.UUID `json:\"-\" faker:\"-\" db:\"nid\"`\n}\n\nfunc (VerificationCode) TableName(context.Context) string {\n\treturn \"identity_verification_codes\"\n}\n\n// Validate validates the state of the verification code\n//\n// - If the code is expired, `flow.ExpiredError` is returned\n// - If the code was already used `ErrCodeAlreadyUsed` is returnd\n// - Otherwise, `nil` is returned\nfunc (f *VerificationCode) Validate() error {\n\tif f == nil {\n\t\treturn errors.WithStack(ErrCodeNotFound)\n\t}\n\tif f.ExpiresAt.Before(time.Now().UTC()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\tif f.UsedAt.Valid {\n\t\treturn errors.WithStack(ErrCodeAlreadyUsed)\n\t}\n\treturn nil\n}\n\nfunc (f *VerificationCode) GetHMACCode() string {\n\treturn f.CodeHMAC\n}\n\nfunc (f *VerificationCode) GetID() uuid.UUID {\n\treturn f.ID\n}\n\ntype CreateVerificationCodeParams struct {\n\t// Code represents the recovery code\n\tRawCode string\n\n\t// ExpiresIn is the lifetime of the code\n\tExpiresIn time.Duration\n\n\t// VerifiableAddress is the address to be verified\n\tVerifiableAddress *identity.VerifiableAddress\n\n\t// FlowID is the id of the current verification flow\n\tFlowID uuid.UUID\n}\n"
  },
  {
    "path": "selfservice/strategy/code/code_verification_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"database/sql\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestVerificationCode(t *testing.T) {\n\tt.Parallel()\n\n\tconf := pkg.NewConfigurationWithDefaults(t)\n\n\tnewCode := func(expiresIn time.Duration, f *verification.Flow) *code.VerificationCode {\n\t\treturn &code.VerificationCode{\n\t\t\tID:        x.NewUUID(),\n\t\t\tFlowID:    f.ID,\n\t\t\tExpiresAt: time.Now().Add(expiresIn),\n\t\t}\n\t}\n\n\treq := &http.Request{URL: urlx.ParseOrPanic(\"https://www.ory.sh/\")}\n\tt.Run(\"method=Validate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"case=returns error if flow is expired\", func(t *testing.T) {\n\t\t\tf, err := verification.NewFlow(conf, -time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(-time.Hour, f)\n\t\t\texpected := new(flow.ExpiredError)\n\t\t\trequire.ErrorAs(t, c.Validate(), &expected)\n\t\t})\n\t\tt.Run(\"case=returns no error if flow is not expired\", func(t *testing.T) {\n\t\t\tf, err := verification.NewFlow(conf, time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\trequire.NoError(t, c.Validate())\n\t\t})\n\n\t\tt.Run(\"case=returns error if flow has been used\", func(t *testing.T) {\n\t\t\tf, err := verification.NewFlow(conf, -time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\tc.UsedAt = sql.NullTime{\n\t\t\t\tTime:  time.Now(),\n\t\t\t\tValid: true,\n\t\t\t}\n\t\t\trequire.ErrorIs(t, c.Validate(), code.ErrCodeAlreadyUsed)\n\t\t})\n\n\t\tt.Run(\"case=returns no error if flow has not been used\", func(t *testing.T) {\n\t\t\tf, err := verification.NewFlow(conf, -time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tc := newCode(time.Hour, f)\n\t\t\tc.UsedAt = sql.NullTime{\n\t\t\t\tValid: false,\n\t\t\t}\n\t\t\trequire.NoError(t, c.Validate())\n\t\t})\n\n\t\tt.Run(\"case=returns error if flow is nil\", func(t *testing.T) {\n\t\t\tvar c *code.VerificationCode\n\t\t\trequire.ErrorIs(t, c.Validate(), code.ErrCodeNotFound)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/node.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc nodeSubmitRegistration() *node.Node {\n\treturn node.NewInputField(\"method\", identity.CredentialsTypeCodeAuth, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoSelfServiceRegistrationRegisterCode())\n}\n\nfunc nodeRegistrationResendNode() *node.Node {\n\treturn node.NewInputField(\"resend\", identity.CredentialsTypeCodeAuth, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoNodeResendOTP())\n}\n\nfunc nodeRegistrationSelectCredentialsNode() *node.Node {\n\treturn node.NewInputField(\n\t\t\"screen\",\n\t\t\"credential-selection\",\n\t\tnode.ProfileGroup,\n\t\tnode.InputAttributeTypeSubmit,\n\t).WithMetaLabel(text.NewInfoRegistrationBack())\n}\n\nfunc nodeContinueButton() *node.Node {\n\treturn node.NewInputField(\"method\", identity.CredentialsTypeCodeAuth, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoNodeLabelContinue())\n}\n\nfunc nodeCodeInputFieldHidden() *node.Node {\n\treturn node.NewInputField(\"method\", identity.CredentialsTypeCodeAuth, node.CodeGroup, node.InputAttributeTypeHidden)\n}\n\nfunc nodeCodeInputField() *node.Node {\n\treturn node.NewInputField(\"code\", nil, node.CodeGroup, node.InputAttributeTypeText, node.WithRequiredInputAttribute)\n}\n"
  },
  {
    "path": "selfservice/strategy/code/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype (\n\tRecoveryCodePersister interface {\n\t\tCreateRecoveryCode(ctx context.Context, dto *CreateRecoveryCodeParams) (*RecoveryCode, error)\n\t\tUseRecoveryCode(ctx context.Context, fID uuid.UUID, code string) (*RecoveryCode, error)\n\t\tDeleteRecoveryCodesOfFlow(ctx context.Context, fID uuid.UUID) error\n\t}\n\n\tRecoveryCodePersistenceProvider interface {\n\t\tRecoveryCodePersister() RecoveryCodePersister\n\t}\n\n\tVerificationCodePersister interface {\n\t\tCreateVerificationCode(context.Context, *CreateVerificationCodeParams) (*VerificationCode, error)\n\t\tUseVerificationCode(context.Context, uuid.UUID, string) (*VerificationCode, error)\n\t\tDeleteVerificationCodesOfFlow(context.Context, uuid.UUID) error\n\t}\n\n\tVerificationCodePersistenceProvider interface {\n\t\tVerificationCodePersister() VerificationCodePersister\n\t}\n\n\tRegistrationCodePersistenceProvider interface {\n\t\tRegistrationCodePersister() RegistrationCodePersister\n\t}\n\n\tRegistrationCodePersister interface {\n\t\tCreateRegistrationCode(context.Context, *CreateRegistrationCodeParams) (*RegistrationCode, error)\n\t\tUseRegistrationCode(ctx context.Context, flowID uuid.UUID, code string, addresses ...string) (*RegistrationCode, error)\n\t\tDeleteRegistrationCodesOfFlow(ctx context.Context, flowID uuid.UUID) error\n\t\tGetUsedRegistrationCode(ctx context.Context, flowID uuid.UUID) (*RegistrationCode, error)\n\t}\n\n\tLoginCodePersistenceProvider interface {\n\t\tLoginCodePersister() LoginCodePersister\n\t}\n\n\tLoginCodePersister interface {\n\t\tCreateLoginCode(context.Context, *CreateLoginCodeParams) (*LoginCode, error)\n\t\tUseLoginCode(ctx context.Context, flowID uuid.UUID, identityID uuid.UUID, code string) (*LoginCode, error)\n\t\tDeleteLoginCodesOfFlow(ctx context.Context, flowID uuid.UUID) error\n\t\tGetUsedLoginCode(ctx context.Context, flowID uuid.UUID) (*LoginCode, error)\n\t}\n)\n"
  },
  {
    "path": "selfservice/strategy/code/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed .schema/recovery.schema.json\nvar recoveryMethodSchema []byte\n\n//go:embed .schema/verification.schema.json\nvar verificationMethodSchema []byte\n\n//go:embed .schema/login.schema.json\nvar loginMethodSchema []byte\n\n//go:embed .schema/registration.schema.json\nvar registrationSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/code/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/samber/lo\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/randx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nvar (\n\t_ recovery.Strategy                 = (*Strategy)(nil)\n\t_ verification.Strategy             = (*Strategy)(nil)\n\t_ login.Strategy                    = (*Strategy)(nil)\n\t_ registration.Strategy             = (*Strategy)(nil)\n\t_ identity.ActiveCredentialsCounter = (*Strategy)(nil)\n\t_ x.Handler                         = (*Strategy)(nil)\n)\n\ntype (\n\t// FlowMethod contains the configuration for this selfservice strategy.\n\tFlowMethod struct {\n\t\t*container.Container\n\t}\n\n\tdependencies interface {\n\t\tnosurfx.CSRFProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\t\tx.TransactionPersistenceProvider\n\n\t\tconfig.Provider\n\n\t\tsession.HandlerProvider\n\t\tsession.ManagementProvider\n\t\tsettings.HandlerProvider\n\t\tsettings.FlowPersistenceProvider\n\n\t\tidentity.ValidationProvider\n\t\tidentity.ManagementProvider\n\t\tidentity.PoolProvider\n\t\tidentity.PrivilegedPoolProvider\n\n\t\tcourier.Provider\n\n\t\terrorx.ManagementProvider\n\n\t\trecovery.ErrorHandlerProvider\n\t\trecovery.FlowPersistenceProvider\n\t\trecovery.StrategyProvider\n\t\trecovery.HookExecutorProvider\n\n\t\tverification.FlowPersistenceProvider\n\t\tverification.StrategyProvider\n\t\tverification.HookExecutorProvider\n\n\t\tlogin.StrategyProvider\n\t\tlogin.FlowPersistenceProvider\n\n\t\tregistration.StrategyProvider\n\t\tregistration.FlowPersistenceProvider\n\n\t\tRecoveryCodePersistenceProvider\n\t\tVerificationCodePersistenceProvider\n\t\tSenderProvider\n\n\t\tRegistrationCodePersistenceProvider\n\t\tLoginCodePersistenceProvider\n\n\t\tschema.IdentitySchemaProvider\n\t\tsession.PersistenceProvider\n\n\t\tsessiontokenexchange.PersistenceProvider\n\n\t\tcontinuity.ManagementProvider\n\n\t\thydra.Provider\n\t}\n\n\tStrategy struct{ deps dependencies }\n\n\tcodeIdentifier struct {\n\t\tIdentifier string `json:\"identifier\"`\n\t}\n)\n\nfunc (s *Strategy) CountActiveFirstFactorCredentials(ctx context.Context, cc map[identity.CredentialsType]identity.Credentials) (int, error) {\n\tcodeConfig := s.deps.Config().SelfServiceCodeStrategy(ctx)\n\tif codeConfig.PasswordlessEnabled {\n\t\t// Login with code for passwordless is enabled\n\t\treturn 1, nil\n\t}\n\n\treturn 0, nil\n}\n\nfunc (s *Strategy) CountActiveMultiFactorCredentials(ctx context.Context, cc map[identity.CredentialsType]identity.Credentials) (int, error) {\n\tcodeConfig := s.deps.Config().SelfServiceCodeStrategy(ctx)\n\tif !codeConfig.MFAEnabled {\n\t\treturn 0, nil\n\t}\n\n\t// Get code credentials if they exist\n\tcreds, ok := cc[identity.CredentialsTypeCodeAuth]\n\tif !ok {\n\t\treturn 0, nil\n\t}\n\n\t// Check if the credentials config is valid JSON\n\tif !gjson.Valid(string(creds.Config)) {\n\t\treturn 0, nil\n\t}\n\n\t// Check for v0 format with address_type field\n\tif gjson.GetBytes(creds.Config, \"address_type\").Exists() {\n\t\taddressType := gjson.GetBytes(creds.Config, \"address_type\").String()\n\t\tif addressType != \"\" {\n\t\t\treturn 1, nil\n\t\t}\n\t\treturn 0, nil\n\t}\n\n\tvar conf identity.CredentialsCode\n\tif err := json.Unmarshal(creds.Config, &conf); err != nil {\n\t\treturn 0, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to unmarshal credentials config: %s\", err))\n\t}\n\n\t// If no addresses configured, return 0\n\tif len(conf.Addresses) == 0 {\n\t\treturn 0, nil\n\t}\n\n\t// Count valid addresses configured for MFA\n\tvalidAddresses := 0\n\tfor _, addr := range conf.Addresses {\n\t\tif addr.Address != \"\" {\n\t\t\tvalidAddresses++\n\t\t}\n\t}\n\n\treturn validAddresses, nil\n}\n\nfunc NewStrategy(deps dependencies) *Strategy { return &Strategy{deps: deps} }\n\nfunc (s *Strategy) ID() identity.CredentialsType {\n\treturn identity.CredentialsTypeCodeAuth\n}\n\nfunc (s *Strategy) NodeGroup() node.UiNodeGroup {\n\treturn node.CodeGroup\n}\n\nfunc (s *Strategy) PopulateMethod(r *http.Request, f flow.Flow) error {\n\tcodeConfig := s.deps.Config().SelfServiceCodeStrategy(r.Context())\n\tswitch f := f.(type) {\n\tcase *login.Flow:\n\t\tif f.RequestedAAL == identity.AuthenticatorAssuranceLevel2 {\n\t\t\tif !codeConfig.MFAEnabled {\n\t\t\t\t// if the flow is requesting AAL2 but MFA is not enabled for the code strategy, we return nil so that\n\t\t\t\t// other strategies can fulfil the request\n\t\t\t\treturn nil\n\t\t\t}\n\t\t} else if !codeConfig.PasswordlessEnabled {\n\t\t\t// if the flow is a normal login flow but passwordless is not enabled for the code strategy,\n\t\t\t// we return nil so that other strategies can fulfil the request\n\t\t\treturn nil\n\t\t}\n\tcase *registration.Flow:\n\t\t// for registration flows, we don't have AAL requirements, so we just check that passwordless is enabled.\n\t\tif !codeConfig.PasswordlessEnabled {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tif string(f.GetState()) == \"\" {\n\t\tf.SetState(flow.StateChooseMethod)\n\t}\n\n\tf.GetUI().ResetMessages()\n\n\tswitch f.GetState() {\n\tcase flow.StateChooseMethod:\n\t\tif err := s.populateChooseMethodFlow(r, f); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase flow.StateEmailSent:\n\t\tif err := s.populateEmailSentFlow(r.Context(), f); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase flow.StatePassedChallenge:\n\t\tfallthrough\n\tdefault:\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReason(\"received an unexpected flow state\"))\n\t}\n\n\t// no matter the flow type or state we need to set the CSRF token\n\tif f.GetType() == flow.TypeBrowser {\n\t\tf.GetUI().SetCSRF(s.deps.GenerateCSRFToken(r))\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) populateChooseMethodFlow(r *http.Request, f flow.Flow) error {\n\tctx := r.Context()\n\tswitch f := f.(type) {\n\tcase *recovery.Flow, *verification.Flow:\n\t\tf.GetUI().Nodes.Append(\n\t\t\tnode.NewInputField(\"email\", nil, node.CodeGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).\n\t\t\t\tWithMetaLabel(text.NewInfoNodeInputEmail()),\n\t\t)\n\t\tf.GetUI().Nodes.Append(\n\t\t\tnode.NewInputField(\"method\", s.ID(), node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\t\tWithMetaLabel(text.NewInfoNodeLabelContinue()),\n\t\t)\n\tcase *login.Flow:\n\t\tds, err := f.IdentitySchema.URL(ctx, s.deps.Config())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif f.RequestedAAL == identity.AuthenticatorAssuranceLevel2 {\n\t\t\tvia := r.URL.Query().Get(\"via\")\n\n\t\t\tsess, err := s.deps.SessionManager().FetchFromRequest(r.Context(), r)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// We need to load the identity's credentials.\n\t\t\tif len(sess.Identity.Credentials) == 0 {\n\t\t\t\tif err := s.deps.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, sess.Identity, identity.ExpandCredentials); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\taddresses, err := s.FindCodeAddresses(ctx, sess, via)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfor _, address := range addresses {\n\t\t\t\tf.GetUI().Nodes.Append(node.NewInputField(\"address\", address.To, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\t\t\tWithMetaLabel(text.NewInfoSelfServiceLoginAAL2CodeAddress(string(address.Via), address.To)))\n\t\t\t}\n\t\t} else {\n\t\t\tidentifierLabel, err := login.GetIdentifierLabelFromSchema(ctx, ds.String())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tf.GetUI().Nodes.Upsert(node.NewInputField(\"identifier\", \"\", node.DefaultGroup, node.InputAttributeTypeText, node.WithRequiredInputAttribute).WithMetaLabel(identifierLabel))\n\t\t\tf.GetUI().Nodes.Append(\n\t\t\t\tnode.NewInputField(\"method\", s.ID(), node.CodeGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoSelfServiceLoginCode()),\n\t\t\t)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) populateEmailSentFlow(ctx context.Context, f flow.Flow) error {\n\t// fresh ui node group\n\tfreshNodes := f.GetUI().Nodes\n\tfreshNodes.ClearTransientNodes()\n\tvar route string\n\tvar codeMetaLabel *text.Message\n\tvar message *text.Message\n\n\tvar resendNode *node.Node\n\tvar backNode *node.Node\n\n\tswitch f.GetFlowName() {\n\tcase flow.RecoveryFlow:\n\t\troute = recovery.RouteSubmitFlow\n\t\tcodeMetaLabel = text.NewInfoNodeLabelRecoveryCode()\n\t\tmessage = text.NewRecoveryEmailWithCodeSent()\n\n\t\tresendNode = node.NewInputField(\"email\", nil, node.CodeGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).\n\t\t\tWithMetaLabel(text.NewInfoNodeResendOTP())\n\n\tcase flow.VerificationFlow:\n\t\troute = verification.RouteSubmitFlow\n\t\tcodeMetaLabel = text.NewInfoNodeLabelVerificationCode()\n\t\tmessage = text.NewVerificationEmailWithCodeSent()\n\n\tcase flow.LoginFlow:\n\t\troute = login.RouteSubmitFlow\n\t\tcodeMetaLabel = text.NewInfoNodeLabelLoginCode()\n\t\tmessage = text.NewLoginCodeSent()\n\n\t\t// preserve the login identifier that was submitted\n\t\t// so we can retry the code flow with the same data\n\t\tfor _, n := range f.GetUI().Nodes {\n\t\t\tif n.ID() == \"identifier\" || n.ID() == \"address\" {\n\t\t\t\tif input, ok := n.Attributes.(*node.InputAttributes); ok {\n\t\t\t\t\tinput.Type = \"hidden\"\n\t\t\t\t\tn.Attributes = input\n\t\t\t\t\tinput.Name = \"identifier\"\n\t\t\t\t}\n\t\t\t\tfreshNodes = append(freshNodes, n)\n\t\t\t}\n\t\t}\n\n\t\tresendNode = node.NewInputField(\"resend\", \"code\", node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\tWithMetaLabel(text.NewInfoNodeResendOTP())\n\n\tcase flow.RegistrationFlow:\n\t\troute = registration.RouteSubmitFlow\n\t\tcodeMetaLabel = text.NewInfoNodeLabelRegistrationCode()\n\t\tmessage = text.NewRegistrationEmailWithCodeSent()\n\n\t\t// in the registration flow we need to preserve the trait fields that were submitted\n\t\t// so we can retry the code flow with the same data\n\t\tfor _, n := range f.GetUI().Nodes {\n\t\t\tif t, ok := n.Attributes.(*node.InputAttributes); ok && t.Type == node.InputAttributeTypeSubmit {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif n.Group == node.DefaultGroup {\n\t\t\t\t// we don't need the user to change the values here\n\t\t\t\t// for better UX let's make them disabled\n\t\t\t\t// when there are errors we won't hide the fields\n\t\t\t\tif len(n.Messages) == 0 {\n\t\t\t\t\tif input, ok := n.Attributes.(*node.InputAttributes); ok {\n\t\t\t\t\t\tinput.Type = \"hidden\"\n\t\t\t\t\t\tn.Attributes = input\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfreshNodes = append(freshNodes, n)\n\t\t\t}\n\t\t}\n\n\t\tresendNode = nodeRegistrationResendNode()\n\n\t\t// Insert a back button if we have a two-step registration screen, so that the\n\t\t// user can navigate back to the credential selection screen.\n\t\tif s.deps.Config().SelfServiceFlowRegistrationTwoSteps(ctx) {\n\t\t\tbackNode = nodeRegistrationSelectCredentialsNode()\n\t\t}\n\tdefault:\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReason(\"received an unexpected flow type\"))\n\t}\n\n\t// Hidden field Required for the re-send code button\n\t// !!important!!: this field must be appended before the code submit button since upsert will replace the first node with the same name\n\tfreshNodes.Upsert(nodeCodeInputFieldHidden())\n\n\t// code input field\n\tfreshNodes.Upsert(nodeCodeInputField().WithMetaLabel(codeMetaLabel))\n\n\t// code submit button\n\tfreshNodes.Append(nodeContinueButton())\n\n\tif resendNode != nil {\n\t\tfreshNodes.Append(resendNode)\n\t}\n\n\tif backNode != nil {\n\t\tfreshNodes.Append(backNode)\n\t}\n\n\tf.GetUI().Nodes = freshNodes\n\n\tf.GetUI().Method = \"POST\"\n\tf.GetUI().Action = flow.AppendFlowTo(urlx.AppendPaths(s.deps.Config().SelfPublicURL(ctx), route), f.GetID()).String()\n\n\tf.GetUI().Messages.Set(message)\n\treturn nil\n}\n\n// NewCodeUINodes creates a fresh UI for the code flow.\n// this is used with the `recovery`, `verification`, `registration` and `login` flows.\nfunc (s *Strategy) NewCodeUINodes(r *http.Request, f flow.Flow, data any) error {\n\tif err := s.PopulateMethod(r, f); err != nil {\n\t\treturn err\n\t}\n\n\tprefix := \"\" // The login flow does not process traits\n\tif f.GetFlowName() == flow.RegistrationFlow {\n\t\t// The registration form does however\n\t\tprefix = \"traits\"\n\t}\n\n\tcont, err := container.NewFromStruct(\"\", node.CodeGroup, data, prefix)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, n := range cont.Nodes {\n\t\t// we only set the value and not the whole field because we want to keep types from the initial form generation\n\t\tf.GetUI().GetNodes().SetValueAttribute(n.ID(), n.Attributes.GetValue())\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) FindCodeAddresses(ctx context.Context, sess *session.Session, via string) (result []Address, _ error) {\n\t// The via parameter lets us hint at the OTP address to use for 2fa.\n\tif via == \"\" {\n\t\taddresses, found, err := FindCodeAddressCandidates(sess.Identity, s.deps.Config().SelfServiceCodeMethodMissingCredentialFallbackEnabled(ctx))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t} else if !found {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\tsort.SliceStable(addresses, func(i, j int) bool {\n\t\t\treturn addresses[i].To < addresses[j].To && addresses[i].Via < addresses[j].Via\n\t\t})\n\n\t\treturn addresses, nil\n\t} else {\n\t\tvalue := gjson.GetBytes(sess.Identity.Traits, via).String()\n\t\tif value == \"\" {\n\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"No value found for trait %s in the current identity.\", via))\n\t\t}\n\n\t\t// TODO Remove this normalization once the via parameter is deprecated.\n\t\t//\n\t\t// Here we need to normalize the via parameter to the actual address. This is necessary because otherwise\n\t\t// we won't find the address in the list of addresses.\n\t\t//\n\t\t// Since we don't know if the via parameter is an email address or a phone number, we need to normalize for both.\n\t\tvalue = x.GracefulNormalization(value)\n\n\t\taddresses, found, err := FindCodeAddressCandidates(sess.Identity, s.deps.Config().SelfServiceCodeMethodMissingCredentialFallbackEnabled(ctx))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t} else if !found {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\taddress, found := lo.Find(addresses, func(item Address) bool {\n\t\t\treturn item.To == value\n\t\t})\n\t\tif !found {\n\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"You can only reference a trait that matches a verification email address in the via parameter, or a registered credential.\"))\n\t\t}\n\n\t\treturn []Address{address}, nil\n\t}\n}\n\nfunc SetDefaultFlowState(f flow.Flow, resend string) {\n\t// By Default the flow should be in the 'choose method' state.\n\tif f.GetState() == \"\" {\n\t\tf.SetState(flow.StateChooseMethod)\n\t}\n\n\tif strings.EqualFold(resend, \"code\") {\n\t\tf.SetState(flow.StateChooseMethod)\n\t}\n}\n\nconst CodeLength = 6\n\nfunc GenerateCode() string {\n\treturn randx.MustString(CodeLength, randx.Numeric)\n}\n\n// MaskAddress masks an address by replacing the middle part with asterisks.\n//\n// If the address contains an @, the part before the @ is masked by taking the first 2 characters and adding 4 *\n// (if the part before the @ is less than 2 characters the full value is used).\n// Otherwise, the first 3 characters and last two characters are taken and 4 * are added in between.\n//\n// Examples:\n// - foo@bar -> fo****@bar\n// - foobar -> fo****ar\n// - f@bar -> f@bar\n// - fo@bar -> fo****@bar\n// - +12345678910 -> +12****10\nfunc MaskAddress(input string) string {\n\tif strings.Contains(input, \"@\") {\n\t\tpre, post, found := strings.Cut(input, \"@\")\n\t\tif !found || len(pre) < 2 {\n\t\t\treturn input\n\t\t}\n\t\treturn pre[:2] + strings.Repeat(\"*\", 4) + \"@\" + post\n\t}\n\tif len(input) < 6 {\n\t\treturn input\n\t}\n\treturn input[:3] + strings.Repeat(\"*\", 4) + input[len(input)-2:]\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/samber/lo\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nvar (\n\t_ login.AAL1FormHydrator = new(Strategy)\n\t_ login.AAL2FormHydrator = (*Strategy)(nil)\n\t_ login.Strategy         = (*Strategy)(nil)\n)\n\n// Update Login flow using the code method\n//\n// swagger:model updateLoginFlowWithCodeMethod\ntype updateLoginFlowWithCodeMethod struct {\n\t// Method should be set to \"code\" when logging in using the code strategy.\n\t//\n\t// required: true\n\tMethod string `json:\"method\" form:\"method\"`\n\n\t// CSRFToken is the anti-CSRF token\n\t//\n\t// required: true\n\tCSRFToken string `json:\"csrf_token\" form:\"csrf_token\"`\n\n\t// Code is the 6 digits code sent to the user\n\t//\n\t// required: false\n\tCode string `json:\"code\" form:\"code\"`\n\n\t// Identifier is the code identifier\n\t// The identifier requires that the user has already completed the registration or settings with code flow.\n\t// required: false\n\tIdentifier string `json:\"identifier\" form:\"identifier\"`\n\n\t// Address is the address to send the code to, in case that there are multiple addresses. This field\n\t// is only used in two-factor flows and is ineffective for passwordless flows.\n\tAddress string `json:\"address\" form:\"address\"`\n\n\t// Resend is set when the user wants to resend the code\n\t// required: false\n\tResend string `json:\"resend\" form:\"resend\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) CompletedAuthenticationMethod(ctx context.Context) session.AuthenticationMethod {\n\taal := identity.AuthenticatorAssuranceLevel1\n\tif s.deps.Config().SelfServiceCodeStrategy(ctx).MFAEnabled {\n\t\taal = identity.AuthenticatorAssuranceLevel2\n\t}\n\n\treturn session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    aal,\n\t}\n}\n\nfunc (s *Strategy) HandleLoginError(r *http.Request, f *login.Flow, body *updateLoginFlowWithCodeMethod, err error, hideIdentifier bool) error {\n\tif errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\treturn err\n\t}\n\n\tif f != nil {\n\t\tidentifier := \"\"\n\t\tif body != nil {\n\t\t\tidentifier = cmp.Or(body.Address, body.Identifier)\n\t\t}\n\n\t\tf.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\t\tif hideIdentifier {\n\t\t\tidentifierNode := node.NewInputField(\"identifier\", identifier, node.DefaultGroup, node.InputAttributeTypeHidden)\n\t\t\tidentifierNode.Attributes.SetValue(identifier)\n\t\t\tf.UI.GetNodes().Upsert(identifierNode)\n\t\t}\n\n\t\tf.UI.Nodes.SetValueAttribute(\"identifier\", identifier)\n\t}\n\n\treturn err\n}\n\n// findIdentityByIdentifier returns the identity and the code credential for the given identifier.\n// If the identity does not have a code credential, it will attempt to find\n// the identity through other credentials matching the identifier.\n// the fallback mechanism is used for migration purposes of old accounts that do not have a code credential.\nfunc (s *Strategy) findIdentityByIdentifier(ctx context.Context, identifier string) (id *identity.Identity, cred *identity.Credentials, isFallback bool, err error) {\n\tctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.code.Strategy.findIdentityByIdentifier\")\n\tdefer otelx.End(span, &err)\n\n\tid, cred, err = s.deps.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), identifier)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t// this is a migration for old identities that do not have a code credential\n\t\t// we might be able to do a fallback login since we could not find a credential on this identifier\n\t\t// Case insensitive because we only care about emails.\n\t\tid, err := s.deps.PrivilegedIdentityPool().FindIdentityByCredentialIdentifier(ctx, identifier, false, identity.ExpandDefault)\n\t\tif err != nil {\n\t\t\treturn nil, nil, false, errors.WithStack(schema.NewNoCodeAuthnCredentials())\n\t\t}\n\n\t\t// we don't know if the user has verified the code yet, so we just return the identity\n\t\t// and let the caller decide what to do with it\n\t\treturn id, nil, true, nil\n\t} else if err != nil {\n\t\treturn nil, nil, false, errors.WithStack(schema.NewNoCodeAuthnCredentials())\n\t}\n\n\tif len(cred.Identifiers) == 0 {\n\t\treturn nil, nil, false, errors.WithStack(schema.NewNoCodeAuthnCredentials())\n\t}\n\n\t// we don't need the code credential, we just need to know that it exists\n\treturn id, cred, false, nil\n}\n\ntype decodedMethod struct {\n\tMethod  string `json:\"method\" form:\"method\"`\n\tAddress string `json:\"address\" form:\"address\"`\n}\n\nfunc (s *Strategy) methodEnabledAndAllowedFromRequest(r *http.Request, f *login.Flow) (*decodedMethod, error) {\n\tvar method decodedMethod\n\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(loginMethodSchema)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif err := decoderx.Decode(r, &method, compiler,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"PUT\", \"PATCH\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(false),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn &method, errors.WithStack(err)\n\t}\n\n\tif err := flow.MethodEnabledAndAllowed(r.Context(), f.GetFlowName(), s.ID().String(), method.Method, s.deps); err != nil {\n\t\treturn &method, err\n\t}\n\n\treturn &method, nil\n}\n\nfunc (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (_ *identity.Identity, err error) {\n\tctx, span := s.deps.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.code.Strategy.Login\")\n\tdefer otelx.End(span, &err)\n\n\tif s.deps.Config().SelfServiceCodeStrategy(ctx).PasswordlessEnabled {\n\t\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel1); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else if s.deps.Config().SelfServiceCodeStrategy(ctx).MFAEnabled {\n\t\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel2); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\treturn nil, errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\tif p, err := s.methodEnabledAndAllowedFromRequest(r, f); errors.Is(err, flow.ErrStrategyNotResponsible) {\n\t\tif !s.deps.Config().SelfServiceCodeStrategy(ctx).MFAEnabled {\n\t\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"MFA is not enabled\"))\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif p == nil || len(p.Address) == 0 {\n\t\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"method not set or address not set\"))\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// In this special case we only expect `address` to be set.\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar p updateLoginFlowWithCodeMethod\n\tif err := decoderx.Decode(r, &p,\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.MustHTTPRawJSONSchemaCompiler(loginMethodSchema),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\"),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn nil, s.HandleLoginError(r, f, &p, err, false)\n\t}\n\n\tf.TransientPayload = p.TransientPayload\n\n\tif err := flow.EnsureCSRF(s.deps, r, f.Type, s.deps.Config().DisableAPIFlowEnforcement(ctx), s.deps.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn nil, s.HandleLoginError(r, f, &p, err, false)\n\t}\n\n\t// By Default the flow should be in the 'choose method' state.\n\tSetDefaultFlowState(f, p.Resend)\n\n\tswitch f.GetState() {\n\tcase flow.StateChooseMethod:\n\t\tidentifier := maybeNormalizeEmail(\n\t\t\tcmp.Or(p.Identifier, p.Address),\n\t\t)\n\t\tid, addresses, err := s.findIdentityForIdentifier(ctx, identifier, f.RequestedAAL, sess)\n\t\tif err != nil {\n\t\t\treturn nil, s.HandleLoginError(r, f, &p, err, false)\n\t\t}\n\t\tif err := s.loginSendCode(ctx, w, r, f, id, identifier, addresses, false); err != nil {\n\t\t\treturn nil, s.HandleLoginError(r, f, &p, err, false)\n\t\t}\n\t\treturn nil, nil\n\tcase flow.StateEmailSent:\n\t\ti, err := s.loginVerifyCode(ctx, f, &p, sess)\n\t\tif err != nil {\n\t\t\treturn nil, s.HandleLoginError(r, f, &p, err, true)\n\t\t}\n\t\treturn i, nil\n\tcase flow.StatePassedChallenge:\n\t\treturn nil, s.HandleLoginError(r, f, &p, errors.WithStack(schema.NewNoLoginStrategyResponsible()), false)\n\t}\n\n\treturn nil, s.HandleLoginError(r, f, &p, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unexpected flow state: %s\", f.GetState())), false)\n}\n\nfunc (s *Strategy) FastLogin1FA(w http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (err error) {\n\tctx, span := s.deps.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.code.Strategy.FastLogin1FA\")\n\tdefer otelx.End(span, &err)\n\n\tif f.RequestedAAL != identity.AuthenticatorAssuranceLevel1 || !s.deps.Config().SelfServiceCodeStrategy(ctx).PasswordlessEnabled || f.GetState() != flow.StateChooseMethod {\n\t\treturn flow.ErrStrategyNotResponsible\n\t}\n\n\tvar p1 idfirst.UpdateLoginFlowWithIdentifierFirstMethod\n\tif err := decoderx.Decode(r, &p1,\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.MustHTTPRawJSONSchemaCompiler(idfirst.LoginSchema),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn err\n\t}\n\tidentifier := p1.Identifier\n\tid, addresses, err := s.findIdentityForIdentifier(ctx, identifier, f.RequestedAAL, sess)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\taccountEnumerationMitigate := s.deps.Config().SecurityAccountEnumerationMitigate(r.Context())\n\n\tif c, err := s.CountActiveFirstFactorCredentials(ctx, id.Credentials); err != nil {\n\t\treturn err\n\t} else if c == 0 {\n\t\treturn flow.ErrStrategyNotResponsible\n\t} else {\n\t\tfor _, strat := range s.deps.LoginStrategies(ctx, login.PrepareOrganizations(r, f, sess)...) {\n\t\t\tif strat, ok := strat.(identity.ActiveCredentialsCounter); !ok || strat.ID() == identity.CredentialsTypeCodeAuth {\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\tif cc, err := strat.CountActiveFirstFactorCredentials(ctx, id.Credentials); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t} else if cc > 0 || accountEnumerationMitigate {\n\t\t\t\t\treturn flow.ErrStrategyNotResponsible\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := s.loginSendCode(ctx, w, r, f, id, identifier, addresses, f.RequestedAAL == \"aal2\"); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) FastLogin2FA(w http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (err error) {\n\tctx, span := s.deps.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.code.Strategy.FastLogin2FA\")\n\tdefer otelx.End(span, &err)\n\n\tif f.RequestedAAL != identity.AuthenticatorAssuranceLevel2 || !s.deps.Config().SelfServiceCodeStrategy(ctx).MFAEnabled || f.GetState() != flow.StateChooseMethod {\n\t\treturn flow.ErrStrategyNotResponsible\n\t}\n\n\taddresses, err := s.FindCodeAddresses(ctx, sess, r.URL.Query().Get(\"via\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(addresses) != 1 {\n\t\treturn flow.ErrStrategyNotResponsible\n\t}\n\tidentifier := addresses[0].To\n\tid, addresses, err := s.findIdentityForIdentifier(ctx, identifier, f.RequestedAAL, sess)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfallbackEnabled := s.deps.Config().SelfServiceCodeMethodMissingCredentialFallbackEnabled(ctx)\n\n\tif c, err := s.CountActiveMultiFactorCredentials(ctx, sess.Identity.Credentials); err != nil {\n\t\treturn err\n\t} else if c == 0 && !fallbackEnabled {\n\t\treturn flow.ErrStrategyNotResponsible\n\t} else {\n\t\tfor _, strat := range s.deps.LoginStrategies(ctx, login.PrepareOrganizations(r, f, sess)...) {\n\t\t\tif strat, ok := strat.(identity.ActiveCredentialsCounter); !ok || strat.ID() == identity.CredentialsTypeCodeAuth {\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\tif cc, err := strat.CountActiveMultiFactorCredentials(ctx, sess.Identity.Credentials); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t} else if cc > 0 {\n\t\t\t\t\treturn flow.ErrStrategyNotResponsible\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := s.loginSendCode(ctx, w, r, f, id, identifier, addresses, true); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) findIdentifierInVerifiableAddress(i *identity.Identity, identifier string) (*Address, error) {\n\tverifiableAddress, found := lo.Find(i.VerifiableAddresses, func(va identity.VerifiableAddress) bool {\n\t\treturn va.Value == identifier\n\t})\n\tif !found {\n\t\treturn nil, errors.WithStack(schema.NewUnknownAddressError())\n\t}\n\n\t// This should be fine for legacy cases because we use `UpgradeCredentials` to normalize all address types prior\n\t// to calling this method.\n\tparsed, err := identity.NewCodeChannel(verifiableAddress.Via)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Address{\n\t\tTo:  verifiableAddress.Value,\n\t\tVia: parsed,\n\t}, nil\n}\n\nfunc (s *Strategy) findIdentityForIdentifier(ctx context.Context, identifier string, requestedAAL identity.AuthenticatorAssuranceLevel, session *session.Session) (_ *identity.Identity, _ []Address, err error) {\n\tctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.code.Strategy.findIdentityForIdentifier\")\n\tspan.SetAttributes(\n\t\tattribute.String(\"flow.requested_aal\", string(requestedAAL)),\n\t)\n\n\tdefer otelx.End(span, &err)\n\n\tif len(identifier) == 0 {\n\t\treturn nil, nil, errors.WithStack(schema.NewRequiredError(\"#/identifier\", \"identifier\"))\n\t}\n\n\tidentifier = x.GracefulNormalization(identifier)\n\n\tvar addresses []Address\n\n\t// Step 1: Get the identity\n\ti, cred, isFallback, err := s.findIdentityByIdentifier(ctx, identifier)\n\tif err != nil {\n\t\tif requestedAAL == identity.AuthenticatorAssuranceLevel2 {\n\t\t\t// When using two-factor auth, the identity used to not have any code credential associated. Therefore,\n\t\t\t// we need to gracefully handle this flow.\n\t\t\t//\n\t\t\t// TODO this section should be removed at some point when we are sure that all identities have a code credential.\n\t\t\tif codeCred := new(schema.ValidationError); errors.As(err, &codeCred) && codeCred.Message == \"account does not exist or has not setup up sign in with code\" {\n\t\t\t\tfallbackAllowed := s.deps.Config().SelfServiceCodeMethodMissingCredentialFallbackEnabled(ctx)\n\t\t\t\tspan.SetAttributes(\n\t\t\t\t\tattribute.Bool(config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, fallbackAllowed),\n\t\t\t\t)\n\n\t\t\t\tif !fallbackAllowed {\n\t\t\t\t\ts.deps.Logger().Warn(\"The identity does not have a code credential but the fallback mechanism is disabled. Login failed.\")\n\t\t\t\t\treturn nil, nil, errors.WithStack(schema.NewNoCodeAuthnCredentials())\n\t\t\t\t}\n\n\t\t\t\taddress, err := s.findIdentifierInVerifiableAddress(session.Identity, identifier)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, nil, err\n\t\t\t\t}\n\n\t\t\t\t// We only end up here if the identity's identity schema does not have the `code` identifier extension defined.\n\t\t\t\t// We know that this is the case for a couple of projects who use 2FA with the code credential.\n\t\t\t\t//\n\t\t\t\t// In those scenarios, the identity has no code credential, and the code credential will also not be created by\n\t\t\t\t// the identity schema.\n\t\t\t\t//\n\t\t\t\t// To avoid future regressions, we will not perform an update on the identity here. Effectively, whenever\n\t\t\t\t// the identity would be updated again (and the identity schema + extensions parsed), it would be likely\n\t\t\t\t// that the code credentials are overwritten.\n\t\t\t\t//\n\t\t\t\t// So we accept that the identity in this case will simply not have code credentials, and we will rely on the\n\t\t\t\t// fallback mechanism to authenticate the user.\n\t\t\t\treturn session.Identity, []Address{*address}, nil\n\t\t\t} else if err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t}\n\t\treturn nil, nil, err\n\t} else if isFallback {\n\t\tfallbackAllowed := s.deps.Config().SelfServiceCodeMethodMissingCredentialFallbackEnabled(ctx)\n\t\tspan.SetAttributes(\n\t\t\tattribute.String(\"identity.id\", i.ID.String()),\n\t\t\tattribute.String(\"network.id\", i.NID.String()),\n\t\t\tattribute.Bool(config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, fallbackAllowed),\n\t\t)\n\n\t\tif !fallbackAllowed {\n\t\t\ts.deps.Logger().Warn(\"The identity does not have a code credential but the fallback mechanism is disabled. Login failed.\")\n\t\t\treturn nil, nil, errors.WithStack(schema.NewNoCodeAuthnCredentials())\n\t\t}\n\n\t\t// We don't have a code credential, but we can still login the user if they have a verified address.\n\t\t// This is a migration path for old accounts that do not have a code credential.\n\t\taddresses = []Address{{\n\t\t\tTo:  identifier,\n\t\t\tVia: identity.CodeChannelEmail,\n\t\t}}\n\n\t\t// We only end up here if the identity's identity schema does not have the `code` identifier extension defined.\n\t\t// We know that this is the case for a couple of projects who use 2FA with the code credential.\n\t\t//\n\t\t// In those scenarios, the identity has no code credential, and the code credential will also not be created by\n\t\t// the identity schema.\n\t\t//\n\t\t// To avoid future regressions, we will not perform an update on the identity here. Effectively, whenever\n\t\t// the identity would be updated again (and the identity schema + extensions parsed), it would be likely\n\t\t// that the code credentials are overwritten.\n\t\t//\n\t\t// So we accept that the identity in this case will simply not have code credentials, and we will rely on the\n\t\t// fallback mechanism to authenticate the user.\n\t} else {\n\t\tspan.SetAttributes(\n\t\t\tattribute.String(\"identity.id\", i.ID.String()),\n\t\t\tattribute.String(\"network.id\", i.NID.String()),\n\t\t)\n\n\t\tvar conf identity.CredentialsCode\n\t\tif err := json.Unmarshal(cred.Config, &conf); err != nil {\n\t\t\treturn nil, nil, errors.WithStack(err)\n\t\t}\n\n\t\tfor _, address := range conf.Addresses {\n\t\t\taddresses = append(addresses, Address{\n\t\t\t\tTo:  address.Address,\n\t\t\t\tVia: address.Channel,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn i, addresses, nil\n}\n\nfunc (s *Strategy) loginSendCode(ctx context.Context, w http.ResponseWriter, r *http.Request, f *login.Flow, id *identity.Identity, identifier string, addresses []Address, successResponse bool) (err error) {\n\tctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.code.Strategy.loginSendCode\")\n\tdefer otelx.End(span, &err)\n\n\tif address, found := lo.Find(addresses, func(item Address) bool {\n\t\treturn item.To == x.GracefulNormalization(identifier)\n\t}); found {\n\t\taddresses = []Address{address}\n\t}\n\n\t// Step 2: Delete any previous login codes for this flow ID\n\tif err := s.deps.LoginCodePersister().DeleteLoginCodesOfFlow(ctx, f.GetID()); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\t// kratos only supports `email` identifiers at the moment with the code method\n\t// this is validated in the identity validation step above\n\tif err := s.deps.CodeSender().SendCode(ctx, f, id, r.Header, addresses...); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\t// sets the flow state to code sent\n\tf.SetState(flow.NextState(f.GetState()))\n\n\tif err := s.NewCodeUINodes(r, f, &codeIdentifier{Identifier: identifier}); err != nil {\n\t\treturn err\n\t}\n\n\tf.Active = identity.CredentialsTypeCodeAuth\n\tif err = s.deps.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\treturn err\n\t}\n\n\tif f.OAuth2LoginChallenge != \"\" {\n\t\thlr, err := s.deps.Hydra().GetLoginRequest(ctx, string(f.OAuth2LoginChallenge))\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tf.HydraLoginRequest = hlr\n\t}\n\n\tif x.IsJSONRequest(r) {\n\t\tif successResponse {\n\t\t\ts.deps.Writer().WriteCode(w, r, http.StatusOK, f)\n\t\t} else {\n\t\t\ts.deps.Writer().WriteCode(w, r, http.StatusBadRequest, f)\n\t\t}\n\t} else {\n\t\thttp.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowLoginUI(ctx)).String(), http.StatusSeeOther)\n\t}\n\n\t// we return an error to the flow handler so that it does not continue execution of the hooks.\n\t// we are not done with the login flow yet. The user needs to verify the code and then we need to persist the identity.\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\n// If identifier is an email, we lower case it because on mobile phones the first letter sometimes is capitalized.\nfunc maybeNormalizeEmail(input string) string {\n\tif strings.Contains(input, \"@\") {\n\t\treturn strings.ToLower(input)\n\t}\n\treturn input\n}\n\nfunc (s *Strategy) loginVerifyCode(ctx context.Context, f *login.Flow, p *updateLoginFlowWithCodeMethod, sess *session.Session) (_ *identity.Identity, err error) {\n\tctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.code.Strategy.loginVerifyCode\")\n\tdefer otelx.End(span, &err)\n\n\t// we are in the second submission state of the flow\n\t// we need to check the code and update the identity\n\tif p.Code == \"\" {\n\t\treturn nil, errors.WithStack(schema.NewRequiredError(\"#/code\", \"code\"))\n\t}\n\n\tp.Identifier = maybeNormalizeEmail(\n\t\tcmp.Or(\n\t\t\tp.Address,\n\t\t\tp.Identifier, // Older versions of Kratos required us to send the identifier here.\n\t\t),\n\t)\n\n\tvar i *identity.Identity\n\tif f.RequestedAAL == identity.AuthenticatorAssuranceLevel2 {\n\t\ti = sess.Identity\n\t} else {\n\t\ti, _, err = s.findIdentityForIdentifier(ctx, p.Identifier, f.RequestedAAL, sess)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tloginCode, err := s.deps.LoginCodePersister().UseLoginCode(ctx, f.ID, i.ID, p.Code)\n\tif err != nil {\n\t\tif errors.Is(err, ErrCodeNotFound) {\n\t\t\treturn nil, schema.NewLoginCodeInvalid()\n\t\t}\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\ti, err = s.deps.PrivilegedIdentityPool().GetIdentity(ctx, loginCode.IdentityID, identity.ExpandDefault)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\t// Step 2: The code was correct\n\tf.Active = identity.CredentialsTypeCodeAuth\n\n\t// since nothing has errored yet, we can assume that the code is correct\n\t// and we can update the login flow\n\tf.SetState(flow.NextState(f.GetState()))\n\n\tif err := s.deps.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\t// Step 3: Verify the address\n\tif err := s.verifyAddress(ctx, i, Address{\n\t\tTo:  loginCode.Address,\n\t\tVia: loginCode.AddressType,\n\t}, true); err != nil {\n\t\treturn nil, err\n\t}\n\n\tverifiedAt := sqlxx.NullTime(time.Now().UTC())\n\tfor idx := range i.VerifiableAddresses {\n\t\tva := i.VerifiableAddresses[idx]\n\t\tif !va.Verified && loginCode.Address == va.Value {\n\t\t\tva.Verified = true\n\t\t\tva.VerifiedAt = &verifiedAt\n\t\t\tva.Status = identity.VerifiableAddressStatusCompleted\n\t\t\tif err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, &va, \"verified\", \"verified_at\", \"status\"); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn i, nil\n}\n\nfunc (s *Strategy) verifyAddress(ctx context.Context, i *identity.Identity, verified Address, persistNow bool) error {\n\tfor idx := range i.VerifiableAddresses {\n\t\taddress := &i.VerifiableAddresses[idx]\n\t\tif address.Verified {\n\t\t\tcontinue\n\t\t}\n\n\t\tif verified.To != address.Value || string(verified.Via) != address.Via {\n\t\t\tcontinue\n\t\t}\n\n\t\taddress.Verified = true\n\t\taddress.VerifiedAt = new(sqlxx.NullTime(time.Now().UTC()))\n\t\taddress.Status = identity.VerifiableAddressStatusCompleted\n\t\tif persistNow {\n\t\t\tif err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, address, \"verified\", \"verified_at\", \"status\"); errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\t\t// This happens when the verified address does not yet exist, for example during registration. In this case we just skip.\n\t\t\t\ts.deps.Logger().WithError(err).Warnf(\"Could not update verifiable address for identity %s.\", i.ID)\n\t\t\t\tcontinue\n\t\t\t} else if err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ti.VerifiableAddresses[idx] = *address\n\t\tbreak\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactorRefresh(r *http.Request, f *login.Flow, _ *session.Session) error {\n\treturn s.PopulateMethod(r, f)\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactor(r *http.Request, f *login.Flow) error {\n\treturn s.PopulateMethod(r, f)\n}\n\nfunc (s *Strategy) PopulateLoginMethodSecondFactor(r *http.Request, f *login.Flow) error {\n\treturn s.PopulateMethod(r, f)\n}\n\nfunc (s *Strategy) PopulateLoginMethodSecondFactorRefresh(r *http.Request, f *login.Flow) error {\n\treturn s.PopulateMethod(r, f)\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstCredentials(r *http.Request, f *login.Flow, opts ...login.FormHydratorModifier) error {\n\tif !s.deps.Config().SelfServiceCodeStrategy(r.Context()).PasswordlessEnabled {\n\t\t// We only return this if passwordless is disabled, because if it is enabled we can always sign in using this method.\n\t\treturn errors.WithStack(idfirst.ErrNoCredentialsFound)\n\t}\n\to := login.NewFormHydratorOptions(opts)\n\n\t// If the identity hint is nil and account enumeration mitigation is disabled, we return an error.\n\tif o.IdentityHint == nil && !s.deps.Config().SecurityAccountEnumerationMitigate(r.Context()) {\n\t\treturn errors.WithStack(idfirst.ErrNoCredentialsFound)\n\t}\n\n\tf.GetUI().Nodes.Append(\n\t\tnode.NewInputField(\"method\", s.ID(), node.CodeGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoSelfServiceLoginCode()),\n\t)\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstIdentification(r *http.Request, f *login.Flow) error {\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_login_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strings\"\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/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\toryClient \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/selfservice/strategy/totp\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/stringsx\"\n)\n\nfunc createIdentity(ctx context.Context, t *testing.T, reg driver.Registry, withoutCodeCredential bool, withTotp bool, moreIdentifiers ...string) *identity.Identity {\n\tt.Helper()\n\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.NID = x.NewUUID()\n\temail := testhelpers.RandomEmail()\n\n\tids := fmt.Sprintf(`\"email\":\"%s\"`, email)\n\tfor i, identifier := range moreIdentifiers {\n\t\tids = fmt.Sprintf(`%s,\"email_%d\":\"%s\"`, ids, i+1, identifier)\n\t}\n\n\ti.Traits = identity.Traits(fmt.Sprintf(`{\"tos\": true, %s}`, ids))\n\n\tcredentials := map[identity.CredentialsType]identity.Credentials{\n\t\tidentity.CredentialsTypePassword: {Identifiers: append([]string{email}, moreIdentifiers...), Type: identity.CredentialsTypePassword, Config: sqlxx.JSONRawMessage(\"{\\\"some\\\" : \\\"secret\\\"}\")},\n\t\tidentity.CredentialsTypeOIDC:     {Type: identity.CredentialsTypeOIDC, Identifiers: append([]string{email}, moreIdentifiers...), Config: sqlxx.JSONRawMessage(\"{\\\"some\\\" : \\\"secret\\\"}\")},\n\t\tidentity.CredentialsTypeWebAuthn: {Type: identity.CredentialsTypeWebAuthn, Identifiers: append([]string{email}, moreIdentifiers...), Config: sqlxx.JSONRawMessage(\"{\\\"some\\\" : \\\"secret\\\", \\\"user_handle\\\": \\\"rVIFaWRcTTuQLkXFmQWpgA==\\\"}\")},\n\t}\n\tif !withoutCodeCredential {\n\t\tcredentials[identity.CredentialsTypeCodeAuth] = identity.Credentials{Type: identity.CredentialsTypeCodeAuth, Identifiers: append([]string{email}, moreIdentifiers...), Config: sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"` + email + `\"}]}`)}\n\t}\n\tif withTotp {\n\t\tkey, err := totp.NewKey(context.Background(), \"foo\", reg)\n\t\trequire.NoError(t, err)\n\t\tcredentials[identity.CredentialsTypeTOTP] = identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\tIdentifiers: append([]string{email}, moreIdentifiers...),\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"` + string(key.URL()) + `\"}`),\n\t\t}\n\t}\n\ti.Credentials = credentials\n\n\tvar va []identity.VerifiableAddress\n\tfor _, identifier := range moreIdentifiers {\n\t\tva = append(va, identity.VerifiableAddress{Value: identifier, Verified: false, Status: identity.VerifiableAddressStatusCompleted})\n\t}\n\n\tva = append(va, identity.VerifiableAddress{Value: email, Verified: true, Status: identity.VerifiableAddressStatusCompleted})\n\n\ti.VerifiableAddresses = va\n\n\trequire.NoError(t, reg.IdentityManager().Create(ctx, i))\n\treturn i\n}\n\nfunc TestLoginCodeStrategy(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/code.identity.schema.json\")),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeCodeAuth, true)),\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tfmt.Sprintf(\"%s.%s.passwordless_enabled\", config.ViperKeySelfServiceStrategyConfig, identity.CredentialsTypeCodeAuth): true,\n\t\t}),\n\t)\n\tconf.MustSet(ctx, config.ViperKeyWebhookHeaderAllowlist, []string{\"x-CUSTOM-header\", \"uSER-Agent\"})\n\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublic, _, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\ttype state struct {\n\t\tflowID        string\n\t\tidentity      *identity.Identity\n\t\tclient        *http.Client\n\t\tidentityEmail string\n\t\ttestServer    *httptest.Server\n\t\tbody          string\n\t}\n\n\ttype ApiType string\n\n\tconst (\n\t\tApiTypeBrowser ApiType = \"browser\"\n\t\tApiTypeSPA     ApiType = \"spa\"\n\t\tApiTypeNative  ApiType = \"api\"\n\t)\n\n\tcreateLoginFlowWithIdentity := func(ctx context.Context, t *testing.T, public *httptest.Server, apiType ApiType, user *identity.Identity) *state {\n\t\tt.Helper()\n\n\t\tvar client *http.Client\n\t\tif apiType == ApiTypeNative {\n\t\t\tclient = &http.Client{}\n\t\t} else {\n\t\t\tclient = testhelpers.NewClientWithCookies(t)\n\t\t}\n\n\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\n\t\tvar clientInit *oryClient.LoginFlow\n\t\tif apiType == ApiTypeNative {\n\t\t\tclientInit = testhelpers.InitializeLoginFlowViaAPICtx(ctx, t, client, public, false)\n\t\t} else {\n\t\t\tclientInit = testhelpers.InitializeLoginFlowViaBrowserCtx(ctx, t, client, public, false, apiType == ApiTypeSPA, false, false)\n\t\t}\n\n\t\tbody, err := json.Marshal(clientInit)\n\t\trequire.NoError(t, err)\n\n\t\tcsrfToken := gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\t\tif apiType == ApiTypeNative {\n\t\t\trequire.Emptyf(t, csrfToken, \"csrf_token should be empty in native flows, but was found in: %s\", body)\n\t\t} else {\n\t\t\trequire.NotEmptyf(t, csrfToken, \"could not find csrf_token in: %s\", body)\n\t\t}\n\n\t\treturn &state{\n\t\t\tflowID:     clientInit.GetId(),\n\t\t\tidentity:   user,\n\t\t\tclient:     client,\n\t\t\ttestServer: public,\n\t\t}\n\t}\n\n\tcreateLoginFlow := func(ctx context.Context, t *testing.T, public *httptest.Server, apiType ApiType, withoutCodeCredential bool, moreIdentifiers ...string) *state {\n\t\tt.Helper()\n\t\ts := createLoginFlowWithIdentity(ctx, t, public, apiType, createIdentity(ctx, t, reg, withoutCodeCredential, false, moreIdentifiers...))\n\t\tloginEmail := gjson.Get(s.identity.Traits.String(), \"email\").String()\n\t\trequire.NotEmptyf(t, loginEmail, \"could not find the email trait inside the identity: %s\", s.identity.Traits.String())\n\t\ts.identityEmail = loginEmail\n\t\treturn s\n\t}\n\n\ttype onSubmitAssertion func(t *testing.T, s *state, body string, res *http.Response)\n\n\tsubmitLogin := func(ctx context.Context, t *testing.T, s *state, apiType ApiType, vals func(v *url.Values), mustHaveSession bool, submitAssertion onSubmitAssertion) *state {\n\t\tt.Helper()\n\n\t\tlf, resp, err := testhelpers.NewSDKCustomClient(s.testServer, s.client).FrontendAPI.GetLoginFlow(ctx).Id(s.flowID).Execute()\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(lf.Ui.Nodes)\n\t\t// we need to remove resend here\n\t\t// since it is not required for the first request\n\t\t// subsequent requests might need it later\n\t\tvalues.Del(\"resend\")\n\t\tvalues.Set(\"method\", \"code\")\n\t\tvals(&values)\n\n\t\tbody, resp := testhelpers.LoginMakeRequestCtx(ctx,\n\t\t\tt,\n\t\t\tapiType == ApiTypeNative,\n\t\t\tapiType == ApiTypeSPA,\n\t\t\tlf,\n\t\t\ts.client,\n\t\t\ttesthelpers.EncodeFormAsJSON(t, apiType == ApiTypeNative, values),\n\t\t\ttesthelpers.WithHeader(\"uSER-Agent\", \"ory-kratos-test\"),\n\t\t\ttesthelpers.WithHeader(\"x-CUSTOM-header\", \"value 1\"),\n\t\t\ttesthelpers.WithHeader(\"x-custom-HEADER\", \"value 2\"),\n\t\t\ttesthelpers.WithHeader(\"x-unknown-header\", \"should not be forwarded\"),\n\t\t)\n\n\t\tif submitAssertion != nil {\n\t\t\tsubmitAssertion(t, s, body, resp)\n\t\t\treturn s\n\t\t}\n\t\ts.body = body\n\n\t\tif mustHaveSession {\n\t\t\treq, err := http.NewRequest(\"GET\", s.testServer.URL+session.RouteWhoami, nil)\n\t\t\trequire.NoError(t, err)\n\t\t\treq = req.WithContext(ctx)\n\n\t\t\tif apiType == ApiTypeNative {\n\t\t\t\treq.Header.Set(\"Authorization\", \"Bearer \"+gjson.Get(body, \"session_token\").String())\n\t\t\t}\n\n\t\t\tresp, err = s.client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tbody = string(ioutilx.MustReadAll(resp.Body))\n\t\t\trequire.EqualValuesf(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t\t} else {\n\t\t\t// SPAs need to be informed that the login has not yet completed using status 400.\n\t\t\t// Browser clients will redirect back to the login URL.\n\t\t\tif apiType == ApiTypeBrowser {\n\t\t\t\trequire.EqualValuesf(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t\t\t} else {\n\t\t\t\trequire.EqualValuesf(t, http.StatusBadRequest, resp.StatusCode, \"%s\", body)\n\t\t\t}\n\t\t}\n\n\t\treturn s\n\t}\n\n\tfor _, tc := range []struct {\n\t\td       string\n\t\tapiType ApiType\n\t}{\n\t\t{\n\t\t\td:       \"SPA client\",\n\t\t\tapiType: ApiTypeSPA,\n\t\t},\n\t\t{\n\t\t\td:       \"Browser client\",\n\t\t\tapiType: ApiTypeBrowser,\n\t\t},\n\t\t{\n\t\t\td:       \"Native client\",\n\t\t\tapiType: ApiTypeNative,\n\t\t},\n\t} {\n\t\tt.Run(\"test=\"+tc.d, func(t *testing.T) {\n\t\t\tt.Run(\"case=email identifier should be case insensitive\", func(t *testing.T) {\n\t\t\t\t// create login flow\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, false)\n\n\t\t\t\t// submit email\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", stringsx.ToUpperInitial(s.identityEmail))\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\t// 3. Submit OTP\n\t\t\t\tsubmitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t}, true, nil)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should be able to log in with code sent to email\", func(t *testing.T) {\n\t\t\t\t// create login flow\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, false)\n\n\t\t\t\t// submit email\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\t// 3. Submit OTP\n\t\t\t\tstate := submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t}, true, nil)\n\t\t\t\tif tc.apiType == ApiTypeSPA {\n\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(state.body, \"continue_with.0.action\").String(), \"%s\", state.body)\n\t\t\t\t\tassert.Contains(t, gjson.Get(state.body, \"continue_with.0.redirect_browser_to\").String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%s\", state.body)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Empty(t, gjson.Get(state.body, \"continue_with\").Array(), \"%s\", state.body)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"case=should be able to log in legacy cases\", func(t *testing.T) {\n\t\t\t\trun := func(t *testing.T, s *state) {\n\t\t\t\t\t// submit email\n\t\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t\t}, false, nil)\n\n\t\t\t\t\tt.Logf(\"s.body: %s\", s.body)\n\n\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\n\t\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\t\t// 3. Submit OTP\n\t\t\t\t\tstate := submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t\t}, true, nil)\n\t\t\t\t\tif tc.apiType == ApiTypeSPA {\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(state.body, \"continue_with.0.redirect_browser_to\").String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%s\", state.body)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(state.body, \"continue_with\").Array(), \"%s\", state.body)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tinitDefault := func(t *testing.T, cf string) *state {\n\t\t\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\t\ti.NID = x.NewUUID()\n\n\t\t\t\t\t// valid fake phone number for libphonenumber\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\ti.Traits = identity.Traits(fmt.Sprintf(`{\"tos\": true, \"email\": \"%s\"}`, email))\n\t\t\t\t\ti.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypeCodeAuth: {\n\t\t\t\t\t\t\tType:        identity.CredentialsTypeCodeAuth,\n\t\t\t\t\t\t\tIdentifiers: []string{email},\n\t\t\t\t\t\t\tVersion:     0,\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(cf),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentities(ctx, i)) // We explicitly bypass identity validation to test the legacy code path\n\t\t\t\t\ts := createLoginFlowWithIdentity(ctx, t, public, tc.apiType, i)\n\t\t\t\t\ts.identityEmail = email\n\t\t\t\t\treturn s\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"case=should be able to send address type with spaces\", func(t *testing.T) {\n\t\t\t\t\trun(t,\n\t\t\t\t\t\tinitDefault(t, `{\"address_type\": \"email                               \", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`),\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should be able to send to empty address type\", func(t *testing.T) {\n\t\t\t\t\trun(t,\n\t\t\t\t\t\tinitDefault(t, `{\"address_type\": \"\", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`),\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should be able to send to empty credentials config\", func(t *testing.T) {\n\t\t\t\t\trun(t,\n\t\t\t\t\t\tinitDefault(t, `{}`),\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should be able to send to identity with no credentials at all when fallback is enabled\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, true)\n\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, nil)\n\t\t\t\t\t})\n\n\t\t\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\t\ti.NID = x.NewUUID()\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\ti.Traits = identity.Traits(fmt.Sprintf(`{\"tos\": true, \"email\": \"%s\"}`, email))\n\t\t\t\t\ti.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\t// This makes it possible for our code to find the identity identifier here.\n\t\t\t\t\t\tidentity.CredentialsTypePassword: {Type: identity.CredentialsTypePassword, Identifiers: []string{email}, Config: sqlxx.JSONRawMessage(`{}`)},\n\t\t\t\t\t}\n\n\t\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentities(ctx, i)) // We explicitly bypass identity validation to test the legacy code path\n\t\t\t\t\ts := createLoginFlowWithIdentity(ctx, t, public, tc.apiType, i)\n\t\t\t\t\ts.identityEmail = email\n\t\t\t\t\trun(t, s)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should fail to send to identity with no credentials at all when fallback is disabled\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, false)\n\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, nil)\n\t\t\t\t\t})\n\n\t\t\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\t\ti.NID = x.NewUUID()\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\ti.Traits = identity.Traits(fmt.Sprintf(`{\"tos\": true, \"email\": \"%s\"}`, email))\n\t\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentities(ctx, i)) // We explicitly bypass identity validation to test the legacy code path\n\t\t\t\t\ts := createLoginFlowWithIdentity(ctx, t, public, tc.apiType, i)\n\t\t\t\t\ts.identityEmail = email\n\t\t\t\t\t// submit email\n\t\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t\t}, false, nil)\n\t\t\t\t\tassert.Contains(t, s.body, \"4000035\", \"Should not find the account\")\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=should be able to log in with code to sms and normalize the number\", func(t *testing.T) {\n\t\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\ti.NID = x.NewUUID()\n\n\t\t\t\t// valid fake phone number for libphonenumber\n\t\t\t\tphone := \"+1 (415) 55526-71\"\n\t\t\t\ti.Traits = identity.Traits(fmt.Sprintf(`{\"tos\": true, \"phone_1\": \"%s\"}`, phone))\n\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(ctx, i))\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().DeleteIdentity(ctx, i.ID))\n\t\t\t\t})\n\n\t\t\t\ts := createLoginFlowWithIdentity(ctx, t, public, tc.apiType, i)\n\n\t\t\t\t// submit phone\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", phone)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, x.GracefulNormalization(phone), \"Your login code is:\")\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode)\n\t\t\t\theaders := message.RequestHeaders\n\t\t\t\tassert.Equal(t, []any{\"ory-kratos-test\"}, gjson.GetBytes(headers, \"User-Agent\").Value())\n\t\t\t\tassert.Equal(t, []any{\"value 1\", \"value 2\"}, gjson.GetBytes(headers, \"X-Custom-Header\").Value())\n\t\t\t\tassert.Empty(t, gjson.GetBytes(headers, \"Some-Other-Header\").Value(), \"Expected Some-Other-Header to be empty\")\n\n\t\t\t\t// 3. Submit OTP\n\t\t\t\tstate := submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t}, true, nil)\n\t\t\t\tif tc.apiType == ApiTypeSPA {\n\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(state.body, \"continue_with.0.action\").String(), \"%s\", state.body)\n\t\t\t\t\tassert.Contains(t, gjson.Get(state.body, \"continue_with.0.redirect_browser_to\").String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%s\", state.body)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Empty(t, gjson.Get(state.body, \"continue_with\").Array(), \"%s\", state.body)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"case=new identities automatically have login with code\", func(t *testing.T) {\n\t\t\t\tctx := context.Background()\n\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationEnabled, true)\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".password.enabled\", true)\n\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\n\t\t\t\tregistrationFlow := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, public, tc.apiType == ApiTypeNative, false, false)\n\n\t\t\t\temail := testhelpers.RandomEmail()\n\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(registrationFlow.Ui.Nodes)\n\t\t\t\tvalues.Set(\"traits.email\", email)\n\t\t\t\tvalues.Set(\"method\", \"password\")\n\t\t\t\tvalues.Set(\"traits.tos\", \"1\")\n\t\t\t\tvalues.Set(\"password\", x.NewUUID().String())\n\n\t\t\t\t_, resp := testhelpers.RegistrationMakeRequest(t, tc.apiType == ApiTypeNative, tc.apiType == ApiTypeSPA, registrationFlow, client, testhelpers.EncodeFormAsJSON(t, tc.apiType == ApiTypeNative, values))\n\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\n\t\t\t\t_, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypeCodeAuth, email)\n\t\t\t\trequire.NoError(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, true)\n\n\t\t\t\ts.identityEmail = email\n\t\t\t\t// submit email\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\theaders := message.RequestHeaders\n\t\t\t\tassert.Equal(t, []any{\"ory-kratos-test\"}, gjson.GetBytes(headers, \"User-Agent\").Value())\n\t\t\t\tassert.Equal(t, []any{\"value 1\", \"value 2\"}, gjson.GetBytes(headers, \"X-Custom-Header\").Value())\n\t\t\t\tassert.Empty(t, gjson.GetBytes(headers, \"Some-Other-Header\").Value(), \"Expected Some-Other-Header to be empty\")\n\t\t\t\t// submit code\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t}, true, nil)\n\n\t\t\t\t// assert that the identity contains a code credential\n\t\t\t\tidentity, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypeCodeAuth, s.identityEmail)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, cred)\n\t\t\t\tassert.Equal(t, identity.ID, cred.IdentityID)\n\t\t\t})\n\n\t\t\tt.Run(\"case=old identities should be able to login with code\", func(t *testing.T) {\n\t\t\t\t// createLoginFlow uses the persister layer to create the identity\n\t\t\t\t// we pass in `true` to not do automatic code credential creation\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, true)\n\n\t\t\t\t// submit email\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\t// submit code\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t}, true, nil)\n\n\t\t\t\t// assert that the identity contains a code credential\n\t\t\t\tidentity, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypeCodeAuth, s.identityEmail)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, cred)\n\t\t\t\tassert.Equal(t, identity.ID, cred.IdentityID)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should not be able to change submitted id on code submit\", func(t *testing.T) {\n\t\t\t\t// create login flow\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, false)\n\n\t\t\t\t// submit email\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\t// 3. Submit OTP\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", \"not-\"+s.identityEmail)\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t}, false, func(t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\trequire.EqualValues(t, conf.SelfServiceFlowLoginUI(ctx).Path, resp.Request.URL.Path)\n\t\t\t\t\t\tlf, resp, err := testhelpers.NewSDKCustomClient(public, s.client).FrontendAPI.GetLoginFlow(ctx).Id(s.flowID).Execute()\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\tbody, err := json.Marshal(lf)\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.messages.0.text\").String(), \"account does not exist or has not setup sign in with code\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, resp.StatusCode)\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"account does not exist or has not setup sign in with code\")\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=should not be able to proceed to code entry when the account is unknown\", func(t *testing.T) {\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, false)\n\n\t\t\t\t// submit email\n\t\t\t\tsubmitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", testhelpers.RandomEmail())\n\t\t\t\t}, false, func(t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\trequire.EqualValues(t, conf.SelfServiceFlowLoginUI(ctx).Path, resp.Request.URL.Path)\n\n\t\t\t\t\t\tlf, resp, err := testhelpers.NewSDKCustomClient(public, s.client).FrontendAPI.GetLoginFlow(ctx).Id(s.flowID).Execute()\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\tbody, err := json.Marshal(lf)\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.messages.0.text\").String(), \"account does not exist or has not setup sign in with code\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, resp.StatusCode)\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"account does not exist or has not setup sign in with code\")\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=should not be able to use valid code after 5 attempts\", func(t *testing.T) {\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, false)\n\n\t\t\t\t// submit email\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\tfor i := 0; i < 5; i++ {\n\t\t\t\t\t// 3. Submit OTP\n\t\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", \"111111\")\n\t\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t\t}, false, func(t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\t// in browser flows we redirect back to the login ui\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, resp.StatusCode)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"The login code is invalid or has already been used\")\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\t// 3. Submit OTP\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, func(t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t// in browser flows we redirect back to the login ui\n\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t} else {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, resp.StatusCode)\n\t\t\t\t\t}\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"The request was submitted too often.\")\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=code should expire\", func(t *testing.T) {\n\t\t\t\tctx := context.Background()\n\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.config.lifespan\", \"1ns\")\n\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.config.lifespan\", \"1h\")\n\t\t\t\t})\n\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, false)\n\n\t\t\t\t// submit email\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\tsubmitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, func(t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t// with browser clients we redirect back to the UI with a new flow id as a query parameter\n\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\trequire.Equal(t, conf.SelfServiceFlowLoginUI(ctx).Path, resp.Request.URL.Path)\n\t\t\t\t\t\tlf, _, err := testhelpers.NewSDKCustomClient(public, s.client).FrontendAPI.GetLoginFlow(ctx).Id(resp.Request.URL.Query().Get(\"flow\")).Execute()\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\n\t\t\t\t\t\tbody, err := json.Marshal(lf)\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\tassert.Regexpf(t, regexp.MustCompile(`The login flow expired 0\\.0\\d minutes ago, please try again\\.`), gjson.GetBytes(body, \"ui.messages.0.text\").Str, \"%s\", body)\n\t\t\t\t\t} else {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusGone, resp.StatusCode)\n\t\t\t\t\t\tassert.Regexpf(t, regexp.MustCompile(`The self-service flow expired 0\\.0\\d minutes ago, initialize a new one\\.`), gjson.Get(body, \"error.reason\").Str, \"%s\", body)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=resend code should invalidate previous code\", func(t *testing.T) {\n\t\t\t\tctx := context.Background()\n\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, false)\n\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\t// resend code\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"resend\", \"code\")\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage = testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\t\t\t\tloginCode2 := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\tassert.NotEmpty(t, loginCode2)\n\n\t\t\t\tassert.NotEqual(t, loginCode, loginCode2)\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, func(t *testing.T, s *state, body string, res *http.Response) {\n\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\t} else {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\t\t}\n\t\t\t\t\trequire.Contains(t, gjson.Get(body, \"ui.messages\").String(), \"The login code is invalid or has already been used. Please try again\")\n\t\t\t\t})\n\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode2)\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, true, nil)\n\t\t\t})\n\n\t\t\tt.Run(\"case=on login with un-verified address, should verify it\", func(t *testing.T) {\n\t\t\t\ts := createLoginFlow(ctx, t, public, tc.apiType, false, testhelpers.RandomEmail())\n\n\t\t\t\t// we need to fetch only the first email\n\t\t\t\tloginEmail := gjson.Get(s.identity.Traits.String(), \"email_1\").String()\n\t\t\t\trequire.NotEmpty(t, loginEmail)\n\n\t\t\t\ts.identityEmail = loginEmail\n\n\t\t\t\tvar va *identity.VerifiableAddress\n\n\t\t\t\tfor _, v := range s.identity.VerifiableAddresses {\n\t\t\t\t\tif v.Value == loginEmail {\n\t\t\t\t\t\tva = &v\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trequire.NotNil(t, va)\n\t\t\t\trequire.False(t, va.Verified)\n\n\t\t\t\t// submit email\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, false, nil)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, loginEmail, \"Use code\")\n\t\t\t\trequire.Contains(t, message.Body, \"Login to your account with the following code\")\n\n\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\trequire.NotEmpty(t, loginCode)\n\n\t\t\t\t// Submit OTP\n\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t\tv.Set(\"identifier\", s.identityEmail)\n\t\t\t\t}, true, nil)\n\n\t\t\t\tid, err := reg.PrivilegedIdentityPool().GetIdentity(ctx, s.identity.ID, identity.ExpandEverything)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tva = nil\n\n\t\t\t\tfor _, v := range id.VerifiableAddresses {\n\t\t\t\t\tif v.Value == loginEmail {\n\t\t\t\t\t\tva = &v\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trequire.NotNil(t, va)\n\t\t\t\trequire.True(t, va.Verified)\n\t\t\t})\n\n\t\t\tt.Run(\"suite=mfa\", func(t *testing.T) {\n\t\t\t\tctx := context.Background()\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.passwordless_enabled\", false)\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.mfa_enabled\", true)\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".totp.enabled\", true)\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.passwordless_enabled\", true)\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.mfa_enabled\", false)\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".totp.enabled\", false)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should be able to get AAL2 session\", func(t *testing.T) {\n\t\t\t\t\trun := func(t *testing.T, withoutCodeCredential bool, overrideCodeCredential *identity.Credentials, overrideAllCredentials map[identity.CredentialsType]identity.Credentials) (*state, *http.Client) {\n\t\t\t\t\t\tuser := createIdentity(ctx, t, reg, withoutCodeCredential, false)\n\t\t\t\t\t\tif overrideCodeCredential != nil {\n\t\t\t\t\t\t\ttoUpdate := user.Credentials[identity.CredentialsTypeCodeAuth]\n\t\t\t\t\t\t\tif overrideCodeCredential.Config != nil {\n\t\t\t\t\t\t\t\ttoUpdate.Config = overrideCodeCredential.Config\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif overrideCodeCredential.Identifiers != nil {\n\t\t\t\t\t\t\t\ttoUpdate.Identifiers = overrideCodeCredential.Identifiers\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tuser.Credentials[identity.CredentialsTypeCodeAuth] = toUpdate\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif overrideAllCredentials != nil {\n\t\t\t\t\t\t\tuser.Credentials = overrideAllCredentials\n\t\t\t\t\t\t}\n\t\t\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(ctx, user))\n\n\t\t\t\t\t\tvar cl *http.Client\n\t\t\t\t\t\tvar f *oryClient.LoginFlow\n\t\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, user)\n\t\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, cl, public, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email\"), testhelpers.ExpectActive(\"code\"))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionCookieLocalhost(ctx, t, reg, user)\n\t\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, cl, public, false, tc.apiType == ApiTypeSPA, false, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email\"), testhelpers.ExpectActive(\"code\"))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbody, err := json.Marshal(f)\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", body)\n\t\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\t\trequire.EqualValues(t, text.InfoSelfServiceLoginCodeSent, gjson.GetBytes(body, \"ui.messages.0.id\").Int(), \"%s\", body)\n\n\t\t\t\t\t\ts := &state{\n\t\t\t\t\t\t\tflowID:        f.GetId(),\n\t\t\t\t\t\t\tidentity:      user,\n\t\t\t\t\t\t\tclient:        cl,\n\t\t\t\t\t\t\ttestServer:    public,\n\t\t\t\t\t\t\tidentityEmail: gjson.Get(user.Traits.String(), \"email\").String(),\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.identityEmail, \"Use code\")\n\t\t\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\t\t\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\t\t\treturn submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t\t\t}, true, nil), cl\n\t\t\t\t\t}\n\n\t\t\t\t\tt.Run(\"case=correct code credential without fallback works\", func(t *testing.T) {\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.identity.schema.json\") // has code identifier\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, false)   // fallback enabled\n\n\t\t\t\t\t\t_, cl := run(t, true, nil, nil)\n\t\t\t\t\t\ttesthelpers.EnsureAAL(t, cl, public, \"aal2\", \"code\")\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=disabling mfa does not lock out the users\", func(t *testing.T) {\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.identity.schema.json\") // has code identifier\n\n\t\t\t\t\t\ts, cl := run(t, true, nil, nil)\n\t\t\t\t\t\ttesthelpers.EnsureAAL(t, cl, public, \"aal2\", \"code\")\n\n\t\t\t\t\t\temail := gjson.GetBytes(s.identity.Traits, \"email\").String()\n\t\t\t\t\t\ts.identityEmail = email\n\n\t\t\t\t\t\t// We change now disable code mfa and enable passwordless instead.\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.mfa_enabled\", false)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.passwordless_enabled\", true)\n\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.passwordless_enabled\", false)\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.mfa_enabled\", true)\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\ts = createLoginFlowWithIdentity(ctx, t, public, tc.apiType, s.identity)\n\t\t\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\t\tv.Set(\"identifier\", email)\n\t\t\t\t\t\t\tv.Set(\"method\", \"code\")\n\t\t\t\t\t\t}, false, nil)\n\n\t\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Use code\")\n\t\t\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\t\t\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\t\t\tloginResult := submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t\t\t}, true, nil)\n\n\t\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\t\tassert.EqualValues(t, \"aal1\", gjson.Get(loginResult.body, \"session.authenticator_assurance_level\").String())\n\t\t\t\t\t\t\tassert.EqualValues(t, \"code\", gjson.Get(loginResult.body, \"session.authentication_methods.#(method==code).method\").String())\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// The user should be able to sign in correctly even though, probably, the internal state was aal2 for available AAL.\n\t\t\t\t\t\t\tres, err := s.client.Get(public.URL + session.RouteWhoami)\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, loginResult.body)\n\t\t\t\t\t\t\tsess := x.MustReadAll(res.Body)\n\t\t\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\t\t\t\tassert.EqualValues(t, \"aal1\", gjson.GetBytes(sess, \"authenticator_assurance_level\").String())\n\t\t\t\t\t\t\tassert.EqualValues(t, \"code\", gjson.GetBytes(sess, \"authentication_methods.#(method==code).method\").String())\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=missing code credential with fallback works when identity schema has the code identifier set\", func(t *testing.T) {\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.identity.schema.json\") // has code identifier\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, true)    // fallback enabled\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, false)\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\t_, cl := run(t, false, nil, nil)\n\t\t\t\t\t\ttesthelpers.EnsureAAL(t, cl, public, \"aal2\", \"code\")\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=missing code credential with fallback works even when identity schema has no code identifier set\", func(t *testing.T) {\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/no-code.schema.json\")    // missing the code identifier\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, true) // fallback enabled\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.identity.schema.json\")\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, false)\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\t_, cl := run(t, false, nil, nil)\n\t\t\t\t\t\ttesthelpers.EnsureAAL(t, cl, public, \"aal2\", \"code\")\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=missing code credential with fallback works even when identity schema has no code identifier set\", func(t *testing.T) {\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/no-code-id.schema.json\") // missing the code identifier\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, true) // fallback enabled\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.identity.schema.json\")\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, false)\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\t_, cl := run(t, true, &identity.Credentials{}, map[identity.CredentialsType]identity.Credentials{})\n\t\t\t\t\t\ttesthelpers.EnsureAAL(t, cl, public, \"aal2\", \"code\")\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=legacy code credential with fallback works when identity schema has the code identifier not set\", func(t *testing.T) {\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/no-code.schema.json\")    // has code identifier\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, true) // fallback enabled\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.identity.schema.json\") // has code identifier\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, false)\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\t_, cl := run(t, false, &identity.Credentials{Config: []byte(`{\"via\":\"\"}`)}, nil)\n\t\t\t\t\t\ttesthelpers.EnsureAAL(t, cl, public, \"aal2\", \"code\")\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=legacy code credential with fallback works when identity schema has the code identifier not set\", func(t *testing.T) {\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/no-code.schema.json\")    // has code identifier\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, true) // fallback enabled\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.identity.schema.json\") // has code identifier\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, false)\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tfor k, credentialsConfig := range []string{\n\t\t\t\t\t\t\t`{\"address_type\": \"email                               \", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`,\n\t\t\t\t\t\t\t`{\"address_type\": \"email\", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`,\n\t\t\t\t\t\t\t`{\"address_type\": \"\", \"used_at\": {\"Time\": \"0001-01-01T00:00:00Z\", \"Valid\": false}}`,\n\t\t\t\t\t\t\t`{\"address_type\": \"\"}`,\n\t\t\t\t\t\t\t`{\"address_type\": \"phone\"}`,\n\t\t\t\t\t\t\t`{}`,\n\t\t\t\t\t\t} {\n\t\t\t\t\t\t\tt.Run(fmt.Sprintf(\"config=%d\", k), func(t *testing.T) {\n\t\t\t\t\t\t\t\t_, cl := run(t, false, &identity.Credentials{Config: []byte(credentialsConfig)}, nil)\n\t\t\t\t\t\t\t\ttesthelpers.EnsureAAL(t, cl, public, \"aal2\", \"code\")\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})\n\n\t\t\t\tt.Run(\"case=without via parameter all options are shown\", func(t *testing.T) {\n\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code-mfa.identity.schema.json\")\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, false)\n\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.identity.schema.json\")\n\t\t\t\t\t})\n\n\t\t\t\t\tvar cl *http.Client\n\t\t\t\t\tvar f *oryClient.LoginFlow\n\n\t\t\t\t\tuser := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\t\tuser.NID = x.NewUUID()\n\t\t\t\t\temail1 := \"code-mfa-1\" + string(tc.apiType) + \"@ory.sh\"\n\t\t\t\t\temail2 := \"code-mfa-2\" + string(tc.apiType) + \"@ory.sh\"\n\t\t\t\t\tphone1 := 4917613213110\n\t\t\t\t\tswitch tc.apiType {\n\t\t\t\t\tcase ApiTypeNative:\n\t\t\t\t\t\tphone1 += 1\n\t\t\t\t\tcase ApiTypeSPA:\n\t\t\t\t\t\tphone1 += 2\n\t\t\t\t\t}\n\t\t\t\t\tuser.Traits = identity.Traits(fmt.Sprintf(`{\"email1\":\"%s\",\"email2\":\"%s\",\"phone1\":\"+%d\"}`, email1, email2, phone1))\n\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Create(ctx, user))\n\n\t\t\t\t\trun := func(t *testing.T, identifierField string, identifier string) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, user)\n\t\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, cl, public, false, testhelpers.InitFlowWithAAL(\"aal2\"))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionCookieLocalhost(ctx, t, reg, user)\n\t\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, cl, public, false, tc.apiType == ApiTypeSPA, false, false, testhelpers.InitFlowWithAAL(\"aal2\"))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbody, err := json.Marshal(f)\n\t\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.GetBytes(body, \"ui.nodes.#(group==code)#\").Raw))\n\t\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\t\trequire.EqualValues(t, text.InfoSelfServiceLoginMFA, gjson.GetBytes(body, \"ui.messages.0.id\").Int(), \"%s\", body)\n\n\t\t\t\t\t\ts := &state{\n\t\t\t\t\t\t\tflowID:        f.GetId(),\n\t\t\t\t\t\t\tidentity:      user,\n\t\t\t\t\t\t\tclient:        cl,\n\t\t\t\t\t\t\ttestServer:    public,\n\t\t\t\t\t\t\tidentityEmail: gjson.Get(user.Traits.String(), \"email\").String(),\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\t\tv.Del(\"method\")\n\t\t\t\t\t\t\tv.Set(identifierField, identifier)\n\t\t\t\t\t\t}, false, nil)\n\n\t\t\t\t\t\tvar message *courier.Message\n\t\t\t\t\t\tif !strings.HasPrefix(identifier, \"+\") {\n\t\t\t\t\t\t\t// email\n\t\t\t\t\t\t\tmessage = testhelpers.CourierExpectMessage(ctx, t, reg, x.GracefulNormalization(identifier), \"Use code\")\n\t\t\t\t\t\t\tassert.Contains(t, message.Body, \"Login to your account with the following code\")\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// SMS\n\t\t\t\t\t\t\tmessage = testhelpers.CourierExpectMessage(ctx, t, reg, x.GracefulNormalization(identifier), \"Your login code is:\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tloginCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\t\tassert.NotEmpty(t, loginCode)\n\n\t\t\t\t\t\tt.Logf(\"loginCode: %s\", loginCode)\n\n\t\t\t\t\t\tsubmitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\t\tv.Set(\"code\", loginCode)\n\t\t\t\t\t\t\tv.Set(identifierField, identifier)\n\t\t\t\t\t\t}, true, nil)\n\n\t\t\t\t\t\ttesthelpers.EnsureAAL(t, cl, public, \"aal2\", \"code\")\n\t\t\t\t\t}\n\n\t\t\t\t\tt.Run(\"field=identifier-email\", func(t *testing.T) {\n\t\t\t\t\t\trun(t, \"identifier\", email1)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"field=address-email\", func(t *testing.T) {\n\t\t\t\t\t\trun(t, \"address\", email2)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"field=address-phone\", func(t *testing.T) {\n\t\t\t\t\t\trun(t, \"address\", fmt.Sprintf(\"+%d\", phone1))\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=cannot use different identifier\", func(t *testing.T) {\n\t\t\t\t\tidentity := createIdentity(ctx, t, reg, false, true)\n\t\t\t\t\tvar cl *http.Client\n\t\t\t\t\tvar f *oryClient.LoginFlow\n\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, identity)\n\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, cl, public, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email\"))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionCookieLocalhost(ctx, t, reg, identity)\n\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, cl, public, false, tc.apiType == ApiTypeSPA, false, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email\"))\n\t\t\t\t\t}\n\n\t\t\t\t\tbody, err := json.Marshal(f)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.nodes.#(group==code)\").Array(), 1)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.nodes.#(group==totp)\").Array(), 1)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\trequire.EqualValues(t, gjson.GetBytes(body, \"ui.messages.0.id\").Int(), text.InfoSelfServiceLoginMFA, \"%s\", body)\n\n\t\t\t\t\ts := &state{\n\t\t\t\t\t\tflowID:        f.GetId(),\n\t\t\t\t\t\tidentity:      identity,\n\t\t\t\t\t\tclient:        cl,\n\t\t\t\t\t\ttestServer:    public,\n\t\t\t\t\t\tidentityEmail: gjson.Get(identity.Traits.String(), \"email\").String(),\n\t\t\t\t\t}\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\ts = submitLogin(ctx, t, s, tc.apiType, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"identifier\", email)\n\t\t\t\t\t}, false, nil)\n\n\t\t\t\t\trequire.Equal(t, \"This account does not exist or has not setup sign in with code.\", gjson.Get(s.body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=verify initial payload with fast login\", func(t *testing.T) {\n\t\t\t\t\tfixedEmail := fmt.Sprintf(\"fixed_mfa_test_fast_%s@ory.sh\", tc.apiType)\n\t\t\t\t\tidentity := createIdentity(ctx, t, reg, false, false, fixedEmail)\n\t\t\t\t\tvar cl *http.Client\n\t\t\t\t\tvar f *oryClient.LoginFlow\n\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, identity)\n\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, cl, public, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email_1\"), testhelpers.ExpectActive(\"code\"))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionCookieLocalhost(ctx, t, reg, identity)\n\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, cl, public, false, tc.apiType == ApiTypeSPA, false, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email_1\"), testhelpers.ExpectActive(\"code\"))\n\t\t\t\t\t}\n\n\t\t\t\t\tbody, err := json.Marshal(f)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\trequire.EqualValues(t, flow.StateEmailSent, gjson.GetBytes(body, \"state\").String(), \"%s\", body)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", body)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\trequire.EqualValues(t, gjson.GetBytes(body, \"ui.messages.0.id\").Int(), text.InfoSelfServiceLoginCodeSent, \"%s\", body)\n\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"ui.nodes.5.attributes.value\", \"id\", \"created_at\", \"expires_at\", \"updated_at\", \"issued_at\", \"request_url\", \"ui.action\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=verify initial payload without fast login\", func(t *testing.T) {\n\t\t\t\t\tfixedEmail := fmt.Sprintf(\"fixed_mfa_test_%s@ory.sh\", tc.apiType)\n\t\t\t\t\tidentity := createIdentity(ctx, t, reg, false, true, fixedEmail)\n\t\t\t\t\tvar cl *http.Client\n\t\t\t\t\tvar f *oryClient.LoginFlow\n\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, identity)\n\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, cl, public, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email_1\"))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionCookieLocalhost(ctx, t, reg, identity)\n\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, cl, public, false, tc.apiType == ApiTypeSPA, false, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email_1\"))\n\t\t\t\t\t}\n\n\t\t\t\t\tbody, err := json.Marshal(f)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\trequire.EqualValues(t, flow.StateChooseMethod, gjson.GetBytes(body, \"state\").String(), \"%s\", body)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", body)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.nodes.#(group==totp)\").Array(), 1, \"%s\", body)\n\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"ui.nodes.0.attributes.value\", \"id\", \"created_at\", \"expires_at\", \"updated_at\", \"issued_at\", \"request_url\", \"ui.action\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=verify initial payload with fast login and fallback enabled\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, true)\n\t\t\t\t\tt.Run(\"case=no code credential\", func(t *testing.T) {\n\t\t\t\t\t\tfixedEmail := fmt.Sprintf(\"fixed_mfa_fallback_without_cc%s@ory.sh\", tc.apiType)\n\t\t\t\t\t\tidentity := createIdentity(ctx, t, reg, true, false, fixedEmail)\n\n\t\t\t\t\t\tvar cl *http.Client\n\t\t\t\t\t\tvar f *oryClient.LoginFlow\n\t\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, identity)\n\t\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, cl, public, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email_1\"), testhelpers.ExpectActive(\"code\"))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionCookieLocalhost(ctx, t, reg, identity)\n\t\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, cl, public, false, tc.apiType == ApiTypeSPA, false, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email_1\"), testhelpers.ExpectActive(\"code\"))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbody, err := json.Marshal(f)\n\t\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t\trequire.EqualValues(t, flow.StateEmailSent, gjson.GetBytes(body, \"state\").String(), \"%s\", body)\n\t\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", body)\n\t\t\t\t\t\trequire.Len(t, gjson.GetBytes(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\t\trequire.EqualValues(t, text.InfoSelfServiceLoginCodeSent, gjson.GetBytes(body, \"ui.messages.0.id\").Int(), \"%s\", body)\n\n\t\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"ui.nodes.5.attributes.value\", \"id\", \"created_at\", \"expires_at\", \"updated_at\", \"issued_at\", \"request_url\", \"ui.action\"))\n\t\t\t\t\t})\n\t\t\t\t\tt.Run(\"case=with code credential\", func(t *testing.T) {\n\t\t\t\t\t\tfixedEmail := fmt.Sprintf(\"fixed_mfa_fallback_with_cc%s@ory.sh\", tc.apiType)\n\t\t\t\t\t\tidentity := createIdentity(ctx, t, reg, false, false, fixedEmail)\n\n\t\t\t\t\t\tvar cl *http.Client\n\t\t\t\t\t\tvar f *oryClient.LoginFlow\n\t\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, identity)\n\t\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, cl, public, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email_1\"), testhelpers.ExpectActive(\"code\"))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionCookieLocalhost(ctx, t, reg, identity)\n\t\t\t\t\t\t\tf = testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, cl, public, false, tc.apiType == ApiTypeSPA, false, false, testhelpers.InitFlowWithAAL(\"aal2\"), testhelpers.InitFlowWithVia(\"email_1\"), testhelpers.ExpectActive(\"code\"))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbody, err := json.Marshal(f)\n\t\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t\trequire.EqualValuesf(t, flow.StateEmailSent, gjson.GetBytes(body, \"state\").String(), \"%s\", body)\n\t\t\t\t\t\trequire.Lenf(t, gjson.GetBytes(body, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", body)\n\t\t\t\t\t\trequire.Lenf(t, gjson.GetBytes(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\t\trequire.EqualValuesf(t, text.InfoSelfServiceLoginCodeSent, gjson.GetBytes(body, \"ui.messages.0.id\").Int(), \"%s\", body)\n\n\t\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"ui.nodes.5.attributes.value\", \"id\", \"created_at\", \"expires_at\", \"updated_at\", \"issued_at\", \"request_url\", \"ui.action\"))\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=using a non existing identity trait results in an error\", func(t *testing.T) {\n\t\t\t\t\tidentity := createIdentity(ctx, t, reg, false, false)\n\t\t\t\t\tvar cl *http.Client\n\t\t\t\t\tvar res *http.Response\n\t\t\t\t\tvar err error\n\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, identity)\n\t\t\t\t\t\tres, err = cl.Get(public.URL + \"/self-service/login/api?aal=aal2&via=doesnt_exist\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionCookieLocalhost(ctx, t, reg, identity)\n\t\t\t\t\t\tres, err = cl.Get(public.URL + \"/self-service/login/browser?aal=aal2&via=doesnt_exist\")\n\t\t\t\t\t}\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\tbody = []byte(gjson.GetBytes(body, \"error\").Raw)\n\t\t\t\t\t}\n\t\t\t\t\trequire.Equal(t, \"No value found for trait doesnt_exist in the current identity.\", gjson.GetBytes(body, \"reason\").String(), \"%s\", body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=unset trait in identity should lead to an error\", func(t *testing.T) {\n\t\t\t\t\tidentity := createIdentity(ctx, t, reg, false, false)\n\t\t\t\t\tvar cl *http.Client\n\t\t\t\t\tvar res *http.Response\n\t\t\t\t\tvar err error\n\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, identity)\n\t\t\t\t\t\tres, err = cl.Get(public.URL + \"/self-service/login/api?aal=aal2&via=email_1\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcl = testhelpers.NewHTTPClientWithIdentitySessionCookieLocalhost(ctx, t, reg, identity)\n\t\t\t\t\t\tres, err = cl.Get(public.URL + \"/self-service/login/browser?aal=aal2&via=email_1\")\n\t\t\t\t\t}\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\tbody = []byte(gjson.GetBytes(body, \"error\").Raw)\n\t\t\t\t\t}\n\t\t\t\t\trequire.Equal(t, \"No value found for trait email_1 in the current identity.\", gjson.GetBytes(body, \"reason\").String(), \"%s\", body)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestFormHydration(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeCodeAuth), map[string]interface{}{\n\t\t\t\"enabled\":              true,\n\t\t\t\"passwordless_enabled\": true,\n\t\t}),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/code.identity.schema.json\")),\n\t)\n\n\ts, err := reg.AllLoginStrategies().Strategy(identity.CredentialsTypeCodeAuth)\n\trequire.NoError(t, err)\n\tfhAAL1, ok := s.(login.AAL1FormHydrator)\n\trequire.True(t, ok)\n\tfhAAL2, ok := s.(login.AAL2FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f *login.Flow) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.UI.Nodes.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f.UI.Nodes)\n\t}\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *login.Flow) {\n\t\tt.Helper()\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tf, err := login.NewFlow(reg.Config(), time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tpasswordlessEnabled := func(ctx context.Context) context.Context {\n\t\treturn contextx.WithConfigValue(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeCodeAuth), map[string]interface{}{\n\t\t\t\"enabled\":              true,\n\t\t\t\"passwordless_enabled\": true,\n\t\t\t\"mfa_enabled\":          false,\n\t\t})\n\t}\n\n\tmfaEnabled := func(ctx context.Context) context.Context {\n\t\treturn contextx.WithConfigValue(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeCodeAuth), map[string]interface{}{\n\t\t\t\"enabled\":              true,\n\t\t\t\"passwordless_enabled\": false,\n\t\t\t\"mfa_enabled\":          true,\n\t\t})\n\t}\n\n\ttoMFARequest := func(ctx context.Context, t *testing.T, r *http.Request, f *login.Flow, traits string) {\n\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\t\tr.URL = &url.URL{Path: \"/\", RawQuery: \"via=email\"}\n\t\t// I only fear god.\n\t\tr.Header = testhelpers.NewHTTPClientWithArbitrarySessionTokenAndTraits(ctx, t, reg, []byte(traits)).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t}\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactor\", func(t *testing.T) {\n\t\tt.Run(\"case=code is used for 2fa but request is 1fa\", func(t *testing.T) {\n\t\t\tr, f := newFlow(mfaEnabled(t.Context()), t)\n\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel1\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=code is used for passwordless login and request is 1fa\", func(t *testing.T) {\n\t\t\tr, f := newFlow(passwordlessEnabled(t.Context()), t)\n\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel1\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactorRefresh\", func(t *testing.T) {\n\t\tt.Run(\"case=code is used for passwordless login and request is 1fa with refresh\", func(t *testing.T) {\n\t\t\tr, f := newFlow(passwordlessEnabled(t.Context()), t)\n\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel1\n\t\t\tf.Refresh = true\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=code is used for 2fa and request is 1fa with refresh\", func(t *testing.T) {\n\t\t\tr, f := newFlow(mfaEnabled(t.Context()), t)\n\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel1\n\t\t\tf.Refresh = true\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodSecondFactor\", func(t *testing.T) {\n\t\tt.Run(\"using via\", func(t *testing.T) {\n\t\t\ttest := func(ctx context.Context, t *testing.T, email string) {\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\ttoMFARequest(ctx, t, r, f, `{\"email\":\"`+email+`\"}`)\n\n\t\t\t\t// We still use the legacy hydrator under the hood here and thus need to set this correctly.\n\t\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\t\t\t\tr.URL = &url.URL{Path: \"/\", RawQuery: \"via=email\"}\n\n\t\t\t\trequire.NoError(t, fhAAL2.PopulateLoginMethodSecondFactor(r, f))\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t}\n\n\t\t\tt.Run(\"case=code is used for 2fa\", func(t *testing.T) {\n\t\t\t\ttest(mfaEnabled(t.Context()), t, \"PopulateLoginMethodSecondFactor-code-mfa-via-2fa@ory.sh\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=code is used for passwordless login\", func(t *testing.T) {\n\t\t\t\ttest(passwordlessEnabled(t.Context()), t, \"PopulateLoginMethodSecondFactor-code-mfa-via-passwordless@ory.sh\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"without via\", func(t *testing.T) {\n\t\t\ttest := func(ctx context.Context, t *testing.T, traits string) {\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\ttoMFARequest(ctx, t, r, f, traits)\n\n\t\t\t\t// We still use the legacy hydrator under the hood here and thus need to set this correctly.\n\t\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\t\t\t\tr.URL = &url.URL{Path: \"/\"}\n\n\t\t\t\trequire.NoError(t, fhAAL2.PopulateLoginMethodSecondFactor(r, f))\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t}\n\n\t\t\tt.Run(\"case=code is used for 2fa\", func(t *testing.T) {\n\t\t\t\tctx := testhelpers.WithDefaultIdentitySchema(mfaEnabled(t.Context()), \"file://./stub/code-mfa.identity.schema.json\")\n\t\t\t\ttest(ctx, t, `{\"email1\":\"PopulateLoginMethodSecondFactor-no-via-2fa-0@ory.sh\",\"email2\":\"PopulateLoginMethodSecondFactor-no-via-2fa-1@ory.sh\",\"phone1\":\"+4917655138291\"}`)\n\t\t\t})\n\n\t\t\tt.Run(\"case=code is used for passwordless login\", func(t *testing.T) {\n\t\t\t\tctx := testhelpers.WithDefaultIdentitySchema(passwordlessEnabled(t.Context()), \"file://./stub/code-mfa.identity.schema.json\")\n\t\t\t\ttest(ctx, t, `{\"email1\":\"PopulateLoginMethodSecondFactor-no-via-passwordless-0@ory.sh\",\"email2\":\"PopulateLoginMethodSecondFactor-no-via-passwordless-1@ory.sh\",\"phone1\":\"+4917655138292\"}`)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=code is used for 2fa and request is 2fa\", func(t *testing.T) {\n\t\t\tctx := mfaEnabled(t.Context())\n\t\t\tr, f := newFlow(ctx, t)\n\t\t\ttoMFARequest(ctx, t, r, f, `{\"email\":\"foo@ory.sh\"}`)\n\t\t\trequire.NoError(t, fhAAL2.PopulateLoginMethodSecondFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=code is used for passwordless login and request is 2fa\", func(t *testing.T) {\n\t\t\tctx := passwordlessEnabled(t.Context())\n\t\t\tr, f := newFlow(ctx, t)\n\t\t\ttoMFARequest(ctx, t, r, f, `{\"email\":\"fooaewasd@ory.sh\"}`)\n\t\t\trequire.NoError(t, fhAAL2.PopulateLoginMethodSecondFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodSecondFactorRefresh\", func(t *testing.T) {\n\t\tt.Run(\"case=code is used for 2fa and request is 2fa with refresh\", func(t *testing.T) {\n\t\t\tctx := mfaEnabled(t.Context())\n\t\t\tr, f := newFlow(ctx, t)\n\t\t\ttoMFARequest(ctx, t, r, f, `{\"email\":\"fooaewdk@ory.sh\"}`)\n\t\t\tf.Refresh = true\n\t\t\trequire.NoError(t, fhAAL2.PopulateLoginMethodSecondFactorRefresh(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=code is used for passwordless login and request is 2fa with refresh\", func(t *testing.T) {\n\t\t\tctx := passwordlessEnabled(t.Context())\n\t\t\tr, f := newFlow(ctx, t)\n\t\t\ttoMFARequest(ctx, t, r, f, `{\"email\":\"foojoajweids@ory.sh\"}`)\n\t\t\tf.Refresh = true\n\t\t\trequire.NoError(t, fhAAL2.PopulateLoginMethodSecondFactorRefresh(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstCredentials\", func(t *testing.T) {\n\t\tt.Run(\"case=no options\", func(t *testing.T) {\n\t\t\tt.Run(\"case=code is used for 2fa\", func(t *testing.T) {\n\t\t\t\tr, f := newFlow(mfaEnabled(t.Context()), t)\n\t\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactor(r, f))\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\n\t\t\tt.Run(\"case=code is used for passwordless login\", func(t *testing.T) {\n\t\t\t\tr, f := newFlow(passwordlessEnabled(t.Context()), t)\n\t\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactor(r, f))\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=WithIdentityHint\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\tt.Run(\"case=code is used for 2fa\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(mfaEnabled(t.Context()), config.ViperKeySecurityAccountEnumerationMitigate, true),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=code is used for passwordless login\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(passwordlessEnabled(t.Context()), config.ViperKeySecurityAccountEnumerationMitigate, true),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")))\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tt.Run(\"case=with no identity\", func(t *testing.T) {\n\t\t\t\t\tt.Run(\"case=code is used for 2fa\", func(t *testing.T) {\n\t\t\t\t\t\tr, f := newFlow(mfaEnabled(t.Context()), t)\n\t\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=code is used for passwordless login\", func(t *testing.T) {\n\t\t\t\t\t\tr, f := newFlow(passwordlessEnabled(t.Context()), t)\n\t\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\tt.Run(\"case=identity has code method\", func(t *testing.T) {\n\t\t\t\t\tidentifier := x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\t\tid := createIdentity(t.Context(), t, reg, false, false, identifier)\n\n\t\t\t\t\tt.Run(\"case=code is used for 2fa\", func(t *testing.T) {\n\t\t\t\t\t\tr, f := newFlow(mfaEnabled(t.Context()), t)\n\t\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=code is used for passwordless login\", func(t *testing.T) {\n\t\t\t\t\t\tr, f := newFlow(passwordlessEnabled(t.Context()), t)\n\t\t\t\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)))\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=identity does not have a code method\", func(t *testing.T) {\n\t\t\t\t\tid := identity.NewIdentity(\"default\")\n\n\t\t\t\t\tt.Run(\"case=code is used for 2fa\", func(t *testing.T) {\n\t\t\t\t\t\tr, f := newFlow(mfaEnabled(t.Context()), t)\n\t\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=code is used for passwordless login\", func(t *testing.T) {\n\t\t\t\t\t\tr, f := newFlow(passwordlessEnabled(t.Context()), t)\n\t\t\t\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)))\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstIdentification\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodIdentifierFirstIdentification(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n}\n\nfunc TestCodeLoginWithLoginChallenge(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeCodeAuth), map[string]interface{}{\n\t\t\t\"enabled\":              true,\n\t\t\t\"passwordless_enabled\": true,\n\t\t}),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/code.identity.schema.json\")),\n\t)\n\treg.SetHydra(hydra.NewFake())\n\treg.WithCSRFTokenGenerator(nosurfx.FakeCSRFTokenGenerator)\n\ts := code.NewStrategy(reg)\n\n\tloginChallenge := hydra.FakeValidLoginChallenge\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *login.Flow) {\n\t\tt.Helper()\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tf, err := login.NewFlow(reg.Config(), time.Minute, nosurfx.FakeCSRFToken, r, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, reg.LoginFlowPersister().CreateLoginFlow(ctx, f))\n\t\treturn r, f\n\t}\n\n\tt.Run(\"case=fetches login challenge on code input state\", func(t *testing.T) {\n\t\t_, f := newFlow(t.Context(), t)\n\t\tf.OAuth2LoginChallenge = sqlxx.NullString(loginChallenge)\n\t\ti := createIdentity(t.Context(), t, reg, false, false)\n\t\temail := gjson.Get(i.Traits.String(), \"email\").String()\n\n\t\tbody := gjson.Parse(`{\n\t\t\t\"method\": \"code\",\n\t\t\t\"identifier\": \"` + email + `\",\n\t\t\t\"csrf_token\": \"` + f.CSRFToken + `\"\n\t\t}`)\n\t\tr := httptest.NewRequest(\"POST\", \"/self-service/login/browser\", strings.NewReader(body.Raw))\n\t\tr.Header.Add(\"Content-Type\", \"application/json\")\n\n\t\t_, err := s.Login(httptest.NewRecorder(), r, f, nil)\n\t\trequire.ErrorIs(t, err, flow.ErrCompletedByStrategy)\n\t\trequire.NotNil(t, f.HydraLoginRequest)\n\t})\n\n\tt.Run(\"case=returns error if login challenge is invalid\", func(t *testing.T) {\n\t\t_, f := newFlow(t.Context(), t)\n\t\tf.OAuth2LoginChallenge = sqlxx.NullString(hydra.FakeInvalidLoginChallenge)\n\t\ti := createIdentity(t.Context(), t, reg, false, false)\n\t\temail := gjson.Get(i.Traits.String(), \"email\").String()\n\n\t\tbody := gjson.Parse(`{\n\t\t\t\"method\": \"code\",\n\t\t\t\"identifier\": \"` + email + `\",\n\t\t\t\"csrf_token\": \"` + f.CSRFToken + `\"\n\t\t}`)\n\t\tr := httptest.NewRequest(\"POST\", \"/self-service/login/browser\", strings.NewReader(body.Raw))\n\t\tr.Header.Add(\"Content-Type\", \"application/json\")\n\n\t\t_, err := s.Login(httptest.NewRecorder(), r, f, nil)\n\t\trequire.ErrorIs(t, err, herodot.ErrBadRequest)\n\t\trequire.Nil(t, f.HydraLoginRequest)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_mfa.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/samber/lo\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n)\n\nfunc FindAllIdentifiers(i *identity.Identity) (result []Address) {\n\tfor _, a := range i.VerifiableAddresses {\n\t\tif len(a.Via) == 0 || len(a.Value) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tresult = append(result, Address{Via: identity.CodeChannel(a.Via), To: a.Value})\n\t}\n\treturn result\n}\n\nfunc FindCodeAddressCandidates(i *identity.Identity, fallbackEnabled bool) (result []Address, found bool, _ error) {\n\t// If no hint was given, we show all OTP addresses from the credentials.\n\tcreds, ok := i.GetCredentials(identity.CredentialsTypeCodeAuth)\n\tif !ok {\n\t\tif !fallbackEnabled {\n\t\t\t// Without a fallback and with no credentials found, we can't really do a lot and exit early.\n\t\t\treturn nil, false, nil\n\t\t}\n\n\t\treturn FindAllIdentifiers(i), true, nil\n\t} else {\n\t\tvar conf identity.CredentialsCode\n\t\tif len(creds.Config) > 0 {\n\t\t\tif err := json.Unmarshal(creds.Config, &conf); err != nil {\n\t\t\t\treturn nil, false, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to unmarshal credentials config: %s\", err))\n\t\t\t}\n\t\t}\n\n\t\tif len(conf.Addresses) == 0 {\n\t\t\tif !fallbackEnabled {\n\t\t\t\t// Without a fallback and with no credentials found, we can't really do a lot and exit early.\n\t\t\t\treturn nil, false, nil\n\t\t\t}\n\n\t\t\treturn FindAllIdentifiers(i), true, nil\n\t\t}\n\t\treturn lo.Map(conf.Addresses, func(item identity.CredentialsCodeAddress, _ int) Address {\n\t\t\treturn Address{Via: item.Channel, To: item.Address}\n\t\t}), true, nil\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_mfa_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\nfunc TestFindAllIdentifiers(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tinput    *identity.Identity\n\t\texpected []Address\n\t}{\n\t\t{\n\t\t\tname: \"valid verifiable addresses\",\n\t\t\tinput: &identity.Identity{\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{Via: \"email\", Value: \"user@example.com\"},\n\t\t\t\t\t{Via: \"sms\", Value: \"+1234567890\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: []Address{\n\t\t\t\t{Via: identity.CodeChannel(\"email\"), To: \"user@example.com\"},\n\t\t\t\t{Via: identity.CodeChannel(\"sms\"), To: \"+1234567890\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"empty verifiable addresses\",\n\t\t\tinput: &identity.Identity{\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"verifiable address with empty fields\",\n\t\t\tinput: &identity.Identity{\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{Via: \"\", 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\tresult := FindAllIdentifiers(tt.input)\n\t\t\tassert.Equal(t, tt.expected, result)\n\t\t})\n\t}\n}\n\nfunc TestFindCodeAddressCandidates(t *testing.T) {\n\ttests := []struct {\n\t\tname            string\n\t\tinput           *identity.Identity\n\t\tfallbackEnabled bool\n\t\texpected        []Address\n\t\tfound           bool\n\t\twantErr         bool\n\t}{\n\t\t{\n\t\t\tname: \"valid credentials with addresses\",\n\t\t\tinput: &identity.Identity{\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypeCodeAuth: {\n\t\t\t\t\t\tConfig: []byte(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"user@example.com\"},{\"channel\":\"sms\",\"address\":\"+1234567890\"}]}`),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tfallbackEnabled: false,\n\t\t\texpected: []Address{\n\t\t\t\t{Via: identity.CodeChannel(\"email\"), To: \"user@example.com\"},\n\t\t\t\t{Via: identity.CodeChannel(\"sms\"), To: \"+1234567890\"},\n\t\t\t},\n\t\t\tfound:   true,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"no credentials, fallback enabled\",\n\t\t\tinput: &identity.Identity{\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{Via: \"email\", Value: \"user@example.com\"},\n\t\t\t\t\t{Via: \"sms\", Value: \"+1234567890\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tfallbackEnabled: true,\n\t\t\texpected: []Address{\n\t\t\t\t{Via: identity.CodeChannel(\"email\"), To: \"user@example.com\"},\n\t\t\t\t{Via: identity.CodeChannel(\"sms\"), To: \"+1234567890\"},\n\t\t\t},\n\t\t\tfound:   true,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"no credentials, fallback disabled\",\n\t\t\tinput: &identity.Identity{\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{Via: \"email\", Value: \"user@example.com\"},\n\t\t\t\t\t{Via: \"sms\", Value: \"+1234567890\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tfallbackEnabled: false,\n\t\t\texpected:        nil,\n\t\t\tfound:           false,\n\t\t\twantErr:         false,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid credentials config\",\n\t\t\tinput: &identity.Identity{\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypeCodeAuth: {\n\t\t\t\t\t\tConfig: []byte(`invalid`),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tfallbackEnabled: false,\n\t\t\texpected:        nil,\n\t\t\tfound:           false,\n\t\t\twantErr:         true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid credentials config, fallback enabled, verifiable addresses exist\",\n\t\t\tinput: &identity.Identity{\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypeCodeAuth: {\n\t\t\t\t\t\tConfig: []byte(`invalid`),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{Via: \"email\", Value: \"user@example.com\"},\n\t\t\t\t\t{Via: \"sms\", Value: \"+1234567890\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tfallbackEnabled: true,\n\t\t\texpected: []Address{\n\t\t\t\t{Via: identity.CodeChannel(\"email\"), To: \"user@example.com\"},\n\t\t\t\t{Via: identity.CodeChannel(\"sms\"), To: \"+1234567890\"},\n\t\t\t},\n\t\t\tfound:   true,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, found, err := FindCodeAddressCandidates(tt.input, tt.fallbackEnabled)\n\t\t\tif tt.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, tt.expected, result)\n\t\t\t\tassert.Equal(t, tt.found, found)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"crypto/sha256\"\n\t\"crypto/subtle\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/redir\"\n\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc (s *Strategy) RecoveryStrategyID() string {\n\treturn string(recovery.RecoveryStrategyCode)\n}\n\nfunc (s *Strategy) IsPrimary() bool {\n\treturn true\n}\n\n// This builds the initial UI (first recovery screen).\nfunc (s *Strategy) PopulateRecoveryMethod(r *http.Request, f *recovery.Flow) error {\n\tswitch f.State {\n\tcase flow.StateChooseMethod:\n\t\tf.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\t\tf.UI.GetNodes().Upsert(\n\t\t\tnode.NewInputField(\"email\", nil, node.CodeGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).\n\t\t\t\tWithMetaLabel(text.NewInfoNodeInputEmail()),\n\t\t)\n\tcase flow.StateRecoveryAwaitingAddress:\n\t\t// re-initialize the UI with a \"clean\" new state\n\t\tf.UI = &container.Container{\n\t\t\tMethod: \"POST\",\n\t\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(s.deps.Config().SelfPublicURL(r.Context()), recovery.RouteSubmitFlow), f.ID).String(),\n\t\t\tNodes:  f.UI.Nodes,\n\t\t}\n\t\tf.UI.Nodes.ClearTransientNodes()\n\t\tf.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\t\tf.UI.GetNodes().Append(\n\t\t\tnode.NewInputField(\"recovery_address\", nil, node.CodeGroup, node.InputAttributeTypeText, node.WithRequiredInputAttribute).\n\t\t\t\tWithMetaLabel(text.NewRecoveryAskAnyRecoveryAddress()),\n\t\t)\n\tdefault:\n\t\t// Unreachable.\n\t\treturn errors.Errorf(\"unreachable state: %s\", f.State)\n\t}\n\n\tf.UI.\n\t\tGetNodes().\n\t\tAppend(node.NewInputField(\"method\", s.RecoveryStrategyID(), node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\tWithMetaLabel(text.NewInfoNodeLabelContinue()))\n\n\treturn nil\n}\n\n// Update Recovery Flow with Code Method\n//\n// swagger:model updateRecoveryFlowWithCodeMethod\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateRecoveryFlowWithCodeMethod struct {\n\t// The email address of the account to recover\n\t//\n\t// If the email belongs to a valid account, a recovery email will be sent.\n\t//\n\t// If you want to notify the email address if the account does not exist, see\n\t// the [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/account-recovery-password-reset#attempted-recovery-notifications)\n\t//\n\t// If a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.\n\t//\n\t// format: email\n\t// required: false\n\tEmail string `json:\"email\" form:\"email\"`\n\n\t// Code from the recovery email\n\t//\n\t// If you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\n\t//\n\t// required: false\n\tCode string `json:\"code\" form:\"code\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `form:\"csrf_token\" json:\"csrf_token\"`\n\n\t// Method is the method that should be used for this recovery flow\n\t//\n\t// Allowed values are `link` and `code`.\n\t//\n\t// required: true\n\tMethod recovery.RecoveryMethod `json:\"method\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n\n\t// A recovery address that is registered for the user.\n\t// It can be an email, a phone number (to receive the code via SMS), etc.\n\t// Used in RecoveryV2.\n\tRecoveryAddress string `json:\"recovery_address\" form:\"recovery_address\"`\n\n\t// If there are multiple addresses registered for the user, a choice is presented and this field\n\t// stores the result of this choice.\n\t// Addresses are 'masked' (never sent in full to the client and shown partially in the UI) since at this point in the recovery flow,\n\t// the user has not yet proven that it knows the full address and we want to avoid\n\t// information exfiltration.\n\t// So for all intents and purposes, the value of this field should be treated as an opaque identifier.\n\t// Used in RecoveryV2.\n\tRecoverySelectAddress string `json:\"recovery_select_address\" form:\"recovery_select_address\"`\n\n\t// If there are multiple recovery addresses registered for the user, and the initially provided address\n\t// is different from the address chosen when the choice (of masked addresses) is presented, then we need to make sure\n\t// that the user actually knows the full address to avoid information exfiltration, so we ask for the full address.\n\t// Used in RecoveryV2.\n\tRecoveryConfirmAddress string `json:\"recovery_confirm_address\" form:\"recovery_confirm_address\"`\n\n\t// Set to \"previous\" to go back in the flow, meaningfully.\n\t// Used in RecoveryV2.\n\tScreen string `json:\"screen\" form:\"screen\"`\n}\n\nfunc (s *Strategy) isCodeFlow(f *recovery.Flow) bool {\n\tvalue, err := f.Active.Value()\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn value == s.NodeGroup().String()\n}\n\nfunc (s *Strategy) Recover(w http.ResponseWriter, r *http.Request, f *recovery.Flow) (err error) {\n\tctx, span := s.deps.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.code.Strategy.Recover\")\n\tspan.SetAttributes(attribute.String(\"selfservice_flows_recovery_use\", s.deps.Config().SelfServiceFlowRecoveryUse(ctx)))\n\tdefer otelx.End(span, &err)\n\n\tif !s.isCodeFlow(f) {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"not code flow\"))\n\t\treturn errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\tbody, err := s.decodeRecovery(r)\n\tif err != nil {\n\t\treturn s.HandleRecoveryError(w, r, nil, body, err)\n\t}\n\n\tf.TransientPayload = body.TransientPayload\n\n\tif f.DangerousSkipCSRFCheck {\n\t\ts.deps.Logger().\n\t\t\tWithRequest(r).\n\t\t\tDebugf(\"A recovery flow with `DangerousSkipCSRFCheck` set has been submitted, skipping anti-CSRF measures.\")\n\t} else if err := flow.EnsureCSRF(s.deps, r, f.Type, s.deps.Config().DisableAPIFlowEnforcement(ctx), s.deps.GenerateCSRFToken, body.CSRFToken); err != nil {\n\t\t// If a CSRF violation occurs the flow is most likely FUBAR, as the user either lost the CSRF token, or an attack occured.\n\t\t// In this case, we just issue a new flow and \"abandon\" the old flow.\n\t\treturn s.retryRecoveryFlow(w, r, flow.TypeBrowser, RetryWithError(err))\n\t}\n\n\tsID := s.RecoveryStrategyID()\n\n\tf.UI.ResetMessages()\n\n\t// NOTE: This is implicitly looking at the state machine (for Recovery v1), by inspecting which fields are present,\n\t// instead of inspecting the state explicitly.\n\t// For Recovery v2 we inspect the state explicitly, a few lines below.\n\tif !flow.IsStateRecoveryV2(f.State) {\n\t\t// If the email is not present in the submission body, the user needs a new code via resend\n\t\tif f.State != flow.StateChooseMethod && len(body.Email) == 0 {\n\t\t\tif err := flow.MethodEnabledAndAllowed(ctx, flow.RecoveryFlow, sID, sID, s.deps); err != nil {\n\t\t\t\treturn s.HandleRecoveryError(w, r, nil, body, err)\n\t\t\t}\n\t\t\treturn s.recoveryUseCode(w, r, body, f)\n\t\t}\n\t}\n\n\tif _, err := s.deps.SessionManager().FetchFromRequest(ctx, r); err == nil {\n\t\t// User is already logged in\n\t\tif x.IsJSONRequest(r) {\n\t\t\tsession.RespondWithJSONErrorOnAuthenticated(s.deps.Writer(), recovery.ErrAlreadyLoggedIn)(w, r)\n\t\t} else {\n\t\t\tsession.RedirectOnAuthenticated(s.deps)(w, r)\n\t\t}\n\t\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n\t}\n\n\t// Recovery V1 sets some magic fields in the UI and inspects them in the body, e.g. `method`.\n\t// This is brittle and rendered unnecessary in Recovery V2 by properly inspecting the `state` (and the CSRF token).\n\tif !flow.IsStateRecoveryV2(f.State) {\n\t\tif err := flow.MethodEnabledAndAllowed(ctx, flow.RecoveryFlow, sID, body.Method, s.deps); err != nil {\n\t\t\treturn s.HandleRecoveryError(w, r, nil, body, err)\n\t\t}\n\t}\n\n\trecoveryFlow, err := s.deps.RecoveryFlowPersister().GetRecoveryFlow(ctx, x.ParseUUID(body.Flow))\n\tif err != nil {\n\t\treturn s.HandleRecoveryError(w, r, recoveryFlow, body, err)\n\t}\n\n\tif err := recoveryFlow.Valid(); err != nil {\n\t\treturn s.HandleRecoveryError(w, r, recoveryFlow, body, err)\n\t}\n\n\tif body.Screen == \"previous\" {\n\t\treturn s.recoveryV2HandleGoBack(r, f, body)\n\t}\n\n\tswitch recoveryFlow.State {\n\tcase flow.StateChooseMethod,\n\t\tflow.StateEmailSent:\n\t\treturn s.recoveryHandleFormSubmission(w, r, recoveryFlow, body)\n\tcase flow.StatePassedChallenge:\n\t\t// was already handled, do not allow retry\n\t\treturn s.retryRecoveryFlow(w, r, recoveryFlow.Type, RetryWithMessage(text.NewErrorValidationRecoveryRetrySuccess()))\n\n\t\t// Recovery V2.\n\tcase flow.StateRecoveryAwaitingAddress:\n\t\treturn s.recoveryV2HandleStateAwaitingAddress(r, recoveryFlow, body)\n\tcase flow.StateRecoveryAwaitingAddressChoice:\n\t\treturn s.recoveryV2HandleStateAwaitingAddressChoice(r, recoveryFlow, body)\n\tcase flow.StateRecoveryAwaitingAddressConfirm:\n\t\treturn s.recoveryV2HandleStateConfirmingAddress(r, recoveryFlow, body)\n\tcase flow.StateRecoveryAwaitingCode:\n\t\treturn s.recoveryV2HandleStateAwaitingCode(w, r, recoveryFlow, body)\n\n\tdefault:\n\t\treturn s.retryRecoveryFlow(w, r, recoveryFlow.Type, RetryWithMessage(text.NewErrorValidationRecoveryStateFailure()))\n\t}\n}\n\nfunc (s *Strategy) recoveryIssueSession(w http.ResponseWriter, r *http.Request, f *recovery.Flow, id *identity.Identity) error {\n\tctx := r.Context()\n\n\tf.UI.Messages.Clear()\n\tf.State = flow.StatePassedChallenge\n\tf.RecoveredIdentityID = uuid.NullUUID{\n\t\tUUID:  id.ID,\n\t\tValid: true,\n\t}\n\n\tif f.Type == flow.TypeBrowser {\n\t\tf.SetCSRFToken(s.deps.CSRFHandler().RegenerateToken(w, r))\n\t}\n\n\tif err := s.deps.RecoveryFlowPersister().UpdateRecoveryFlow(ctx, f); err != nil {\n\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t}\n\n\tsess := session.NewInactiveSession()\n\tsess.CompletedLoginFor(identity.CredentialsTypeRecoveryCode, identity.AuthenticatorAssuranceLevel1)\n\tif err := s.deps.SessionManager().ActivateSession(r, sess, id, time.Now().UTC()); err != nil {\n\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t}\n\n\tif err := s.deps.RecoveryExecutor().PostRecoveryHook(w, r, f, sess); err != nil {\n\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t}\n\n\tswitch f.Type {\n\tcase flow.TypeBrowser:\n\t\tif err := s.deps.SessionManager().UpsertAndIssueCookie(ctx, w, r, sess); err != nil {\n\t\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t\t}\n\tcase flow.TypeAPI:\n\t\tif err := s.deps.SessionPersister().UpsertSession(r.Context(), sess); err != nil {\n\t\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t\t}\n\t\tf.ContinueWith = append(f.ContinueWith, flow.NewContinueWithSetToken(sess.Token))\n\t}\n\n\tsf, err := s.deps.SettingsHandler().NewFlow(ctx, w, r, sess.Identity, sess, f.Type)\n\tif err != nil {\n\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t}\n\n\treturnToURL := s.deps.Config().SelfServiceFlowRecoveryReturnTo(r.Context(), nil)\n\treturnTo := \"\"\n\tif returnToURL != nil {\n\t\treturnTo = returnToURL.String()\n\t}\n\tsf.RequestURL, err = redir.TakeOverReturnToParameter(f.RequestURL, sf.RequestURL, returnTo)\n\tif err != nil {\n\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t}\n\n\tconfig := s.deps.Config()\n\n\tsf.UI.Messages.Set(text.NewRecoverySuccessful(time.Now().Add(config.SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx))))\n\tif err := s.deps.SettingsFlowPersister().UpdateSettingsFlow(r.Context(), sf); err != nil {\n\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t}\n\n\tif s.deps.Config().UseContinueWithTransitions(ctx) {\n\t\tredirectTo := sf.AppendTo(s.deps.Config().SelfServiceFlowSettingsUI(r.Context())).String()\n\t\tswitch {\n\t\tcase f.Type.IsAPI(), x.IsJSONRequest(r):\n\t\t\tf.ContinueWith = append(f.ContinueWith, flow.NewContinueWithSettingsUI(sf, redirectTo))\n\t\t\ts.deps.Writer().Write(w, r, f)\n\t\tdefault:\n\t\t\thttp.Redirect(w, r, redirectTo, http.StatusSeeOther)\n\t\t}\n\t} else {\n\t\ttrace.SpanFromContext(r.Context()).AddEvent(semconv.NewDeprecatedFeatureUsedEvent(r.Context(), \"no_continue_with_transition_recovery_issue_session\"))\n\t\tif x.IsJSONRequest(r) {\n\t\t\ts.deps.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(sf.AppendTo(s.deps.Config().SelfServiceFlowSettingsUI(r.Context())).String()))\n\t\t} else {\n\t\t\thttp.Redirect(w, r, sf.AppendTo(s.deps.Config().SelfServiceFlowSettingsUI(r.Context())).String(), http.StatusSeeOther)\n\t\t}\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\n// NOTE: This function handles two cases:\n//   - A code was submitted: try to use it\n//   - No code was submitted: delete all existing codes, re-generate a new one, send it.\n//     This corresponds to the user clicking on the 're-send code' button.\nfunc (s *Strategy) recoveryUseCode(w http.ResponseWriter, r *http.Request, body *recoverySubmitPayload, f *recovery.Flow) error {\n\tctx := r.Context()\n\tcode, err := s.deps.RecoveryCodePersister().UseRecoveryCode(ctx, f.ID, body.Code)\n\tif errors.Is(err, ErrCodeNotFound) {\n\t\tf.UI.Messages.Clear()\n\t\tf.UI.Messages.Add(text.NewErrorValidationRecoveryCodeInvalidOrAlreadyUsed())\n\t\tif err := s.deps.RecoveryFlowPersister().UpdateRecoveryFlow(ctx, f); err != nil {\n\t\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t\t}\n\n\t\tif f.Type == flow.TypeBrowser && !x.IsJSONRequest(r) {\n\t\t\thttp.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowRecoveryUI(r.Context())).String(), http.StatusSeeOther)\n\t\t} else {\n\t\t\ts.deps.Writer().Write(w, r, f)\n\t\t}\n\t\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n\t} else if err != nil {\n\t\treturn s.retryRecoveryFlow(w, r, f.Type, RetryWithError(err))\n\t}\n\n\t// Important to expand everything here, as we need the data for recovery.\n\trecovered, err := s.deps.IdentityPool().GetIdentity(ctx, code.IdentityID, identity.ExpandEverything)\n\tif err != nil {\n\t\treturn s.HandleRecoveryError(w, r, f, nil, err)\n\t}\n\n\t// mark address as verified only for a self-service flow\n\tif code.CodeType == RecoveryCodeTypeSelfService {\n\t\tif err := s.markRecoveryAddressVerified(w, r, f, recovered, code.RecoveryAddress); err != nil {\n\t\t\treturn s.HandleRecoveryError(w, r, f, body, err)\n\t\t}\n\t}\n\n\treturn s.recoveryIssueSession(w, r, f, recovered)\n}\n\ntype retry struct {\n\terr     error\n\tmessage *text.Message\n}\n\ntype RetryOption func(*retry)\n\nfunc RetryWithError(err error) RetryOption {\n\treturn func(r *retry) {\n\t\tr.err = err\n\t}\n}\n\nfunc RetryWithMessage(msg *text.Message) RetryOption {\n\treturn func(r *retry) {\n\t\tr.message = msg\n\t}\n}\n\nfunc (s *Strategy) retryRecoveryFlow(w http.ResponseWriter, r *http.Request, ft flow.Type, opts ...RetryOption) error {\n\tretryOptions := retry{}\n\n\tfor _, o := range opts {\n\t\to(&retryOptions)\n\t}\n\n\tlogger := s.deps.Logger().WithRequest(r).WithError(retryOptions.err)\n\tif retryOptions.message != nil {\n\t\tlogger = logger.WithField(\"message\", retryOptions.message)\n\t}\n\tlogger.Debug(\"A recovery flow is being retried because a validation error occurred.\")\n\n\tctx := r.Context()\n\tconfig := s.deps.Config()\n\n\tf, err := recovery.NewFlow(config, config.SelfServiceFlowRecoveryRequestLifespan(ctx), s.deps.CSRFHandler().RegenerateToken(w, r), r, recovery.Strategies{s}, ft)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif retryOptions.message != nil {\n\t\tf.UI.Messages.Add(retryOptions.message)\n\t}\n\n\tif retryOptions.err != nil {\n\t\tif expired := new(flow.ExpiredError); errors.As(retryOptions.err, &expired) {\n\t\t\tf.UI.Messages.Add(text.NewErrorValidationRecoveryFlowExpired(expired.ExpiredAt))\n\t\t} else if err := f.UI.ParseError(node.CodeGroup, retryOptions.err); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := s.deps.RecoveryFlowPersister().CreateRecoveryFlow(ctx, f); err != nil {\n\t\treturn err\n\t}\n\n\tif s.deps.Config().UseContinueWithTransitions(ctx) {\n\t\tswitch {\n\t\tcase x.IsJSONRequest(r):\n\t\t\trErr := new(herodot.DefaultError)\n\t\t\tif !errors.As(retryOptions.err, &rErr) {\n\t\t\t\trErr = rErr.WithError(retryOptions.err.Error())\n\t\t\t}\n\t\t\ts.deps.Writer().WriteError(w, r, flow.ErrorWithContinueWith(rErr, flow.NewContinueWithRecoveryUI(f)))\n\t\tdefault:\n\t\t\thttp.Redirect(w, r, f.AppendTo(config.SelfServiceFlowRecoveryUI(ctx)).String(), http.StatusSeeOther)\n\t\t}\n\t} else {\n\t\ttrace.SpanFromContext(r.Context()).AddEvent(semconv.NewDeprecatedFeatureUsedEvent(r.Context(), \"no_continue_with_transition_recovery_retry_flow_handler\"))\n\t\tif x.IsJSONRequest(r) {\n\t\t\thttp.Redirect(w, r, urlx.CopyWithQuery(urlx.AppendPaths(config.SelfPublicURL(ctx),\n\t\t\t\trecovery.RouteGetFlow), url.Values{\"id\": {f.ID.String()}}).String(), http.StatusSeeOther)\n\t\t} else {\n\t\t\thttp.Redirect(w, r, f.AppendTo(config.SelfServiceFlowRecoveryUI(ctx)).String(), http.StatusSeeOther)\n\t\t}\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc AddressToHashBase64(address string) string {\n\thash := sha256.Sum256([]byte(address))\n\treturn base64.StdEncoding.EncodeToString(hash[:])\n}\n\nfunc (s *Strategy) recoveryV2HandleStateAwaitingAddress(r *http.Request, f *recovery.Flow, body *recoverySubmitPayload) error {\n\tif f.State != flow.StateRecoveryAwaitingAddress {\n\t\treturn errors.Errorf(\"unreachable state: %s\", f.State)\n\t}\n\n\tif len(body.RecoveryAddress) == 0 {\n\t\treturn schema.NewRequiredError(\"#/recovery_address\", \"recovery_address\")\n\t}\n\n\t// Need to retrieve all possible recovery addresses and present a choice.\n\trecoveryAddresses, err := s.deps.IdentityPool().FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(r.Context(), body.RecoveryAddress)\n\t// Real error.\n\tif err != nil && !errors.Is(err, sqlcon.ErrNoRows) {\n\t\treturn err\n\t}\n\n\t// No rows returned.\n\tif len(recoveryAddresses) == 0 {\n\t\t// To avoid an attacker from using this case to probe for existing addresses, we pretend it exists.\n\t\t// This is the same behavior as in Recovery V1.\n\t\trecoveryAddresses = append(recoveryAddresses, identity.RecoveryAddress{Value: body.RecoveryAddress})\n\t}\n\n\tf.State = flow.StateRecoveryAwaitingAddressChoice\n\n\tif len(recoveryAddresses) == 1 && recoveryAddresses[0].Value == body.RecoveryAddress {\n\t\t// Skip two states for convenience:\n\t\t// - No need to present a choice with only one option\n\t\t// - No need to ask for the full address if there is only one and it was just provided in full\n\n\t\tbody.RecoveryConfirmAddress = body.RecoveryAddress\n\t\tf.State = flow.StateRecoveryAwaitingAddressConfirm\n\t\tif err := s.deps.RecoveryFlowPersister().UpdateRecoveryFlow(r.Context(), f); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn s.recoveryV2HandleStateConfirmingAddress(r, f, body)\n\t}\n\n\t// re-initialize the UI with a \"clean\" new state\n\tf.UI = &container.Container{\n\t\tMethod: \"POST\",\n\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(s.deps.Config().SelfPublicURL(r.Context()), recovery.RouteSubmitFlow), f.ID).String(),\n\t\tNodes:  f.UI.Nodes,\n\t}\n\tf.UI.Nodes.ClearTransientNodes()\n\n\tf.UI.SetCSRF(f.CSRFToken)\n\n\tf.State = flow.StateRecoveryAwaitingAddressChoice\n\tf.UI.Messages.Set(text.NewRecoveryAskToChooseAddress())\n\n\tslices.SortFunc(recoveryAddresses, func(a, b identity.RecoveryAddress) int {\n\t\treturn strings.Compare(a.Value, b.Value)\n\t})\n\n\tfor _, a := range recoveryAddresses {\n\t\t// NOTE: Only send the masked value and the hash, to avoid information exfiltration.\n\t\t// Why the hash? So that we can recognize later, when the user chooses the masked address in the list,\n\t\t// that the chosen masked address is the `recovery_address` provided in the beginning,\n\t\t// and then we do not ask again the user to provide it in full.\n\t\thashBase64 := AddressToHashBase64(a.Value)\n\t\tf.UI.GetNodes().Append(node.NewInputField(\"recovery_select_address\", hashBase64, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\tWithMetaLabel(&text.Message{\n\t\t\t\tID:   text.InfoNodeLabel,\n\t\t\t\tText: MaskAddress(a.Value),\n\t\t\t\tType: text.Info,\n\t\t\t}))\n\t}\n\n\tf.UI.Nodes.Append(node.NewInputField(\"method\", s.NodeGroup(), node.CodeGroup, node.InputAttributeTypeHidden))\n\tf.UI.Nodes.Append(node.NewInputField(\"recovery_address\", body.RecoveryAddress, node.CodeGroup, node.InputAttributeTypeHidden))\n\t// No back button here because there is no point for the user.\n\n\tif err := s.deps.RecoveryFlowPersister().UpdateRecoveryFlow(r.Context(), f); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) recoveryV2HandleStateAwaitingAddressChoice(r *http.Request, f *recovery.Flow, body *recoverySubmitPayload) error {\n\tif f.State != flow.StateRecoveryAwaitingAddressChoice {\n\t\treturn errors.Errorf(\"unreachable state: %s\", f.State)\n\t}\n\n\tif len(body.RecoverySelectAddress) == 0 {\n\t\treturn schema.NewRequiredError(\"#/recovery_select_address\", \"recovery_select_address\")\n\t}\n\n\tif len(body.RecoveryAddress) == 0 {\n\t\treturn schema.NewRequiredError(\"#/recovery_address\", \"recovery_address\")\n\t}\n\n\t// Is the chosen masked address the same as the address provided in full at the beginning?\n\t// If yes, then do not ask it again in full.\n\t// Technically we check `hash(recovery_address) == recovery_select_address` and\n\t// `recovery_select_address` is `hash(recovery_address)`.\n\thashBase64 := AddressToHashBase64(body.RecoveryAddress)\n\n\t// Better safe than sorry, use constant time comparison.\n\tif subtle.ConstantTimeCompare([]byte(hashBase64), []byte(body.RecoverySelectAddress)) == 1 {\n\t\t// Skip a state: do not ask the user again to provide the full address.\n\t\tbody.RecoveryConfirmAddress = body.RecoveryAddress\n\t\tf.State = flow.StateRecoveryAwaitingAddressConfirm\n\t\treturn s.recoveryV2HandleStateConfirmingAddress(r, f, body)\n\t}\n\n\t// re-initialize the UI with a \"clean\" new state\n\tf.UI = &container.Container{\n\t\tMethod: \"POST\",\n\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(s.deps.Config().SelfPublicURL(r.Context()), recovery.RouteSubmitFlow), f.ID).String(),\n\t\tNodes:  f.UI.Nodes,\n\t}\n\tf.UI.Nodes.ClearTransientNodes()\n\tf.UI.SetCSRF(f.CSRFToken)\n\n\tf.State = flow.StateRecoveryAwaitingAddressConfirm\n\tf.UI.Messages.Set(text.NewRecoveryAskForFullAddress())\n\n\t// Retrieve the selected recovery address in plaintext to determine the input label and type.\n\tvar plaintextRecoveryAddress string\n\trecoveryAddresses, err := s.deps.IdentityPool().FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(r.Context(), body.RecoveryAddress)\n\tif err == nil {\n\t\tfor _, a := range recoveryAddresses {\n\t\t\tif subtle.ConstantTimeCompare([]byte(AddressToHashBase64(a.Value)), []byte(body.RecoverySelectAddress)) == 1 {\n\t\t\t\tplaintextRecoveryAddress = a.Value\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif plaintextRecoveryAddress == \"\" {\n\t\treturn herodot.ErrBadRequest.\n\t\t\tWithReason(\"The selected recovery address is not valid.\").\n\t\t\tWithDebug(\"The selected recovery address does not match any of the known recovery addresses.\")\n\t}\n\n\tvar inputType node.UiNodeInputAttributeType\n\tvar label *text.Message\n\tif strings.ContainsRune(plaintextRecoveryAddress, '@') {\n\t\tinputType = node.InputAttributeTypeEmail\n\t\tlabel = text.NewInfoNodeInputEmail()\n\t} else {\n\t\tinputType = node.InputAttributeTypeTel\n\t\tlabel = text.NewInfoNodeInputPhoneNumber()\n\t}\n\n\tf.UI.Nodes.Append(node.NewInputField(\"recovery_confirm_address\", body.RecoveryConfirmAddress, node.CodeGroup, inputType, node.WithRequiredInputAttribute).\n\t\tWithMetaLabel(label),\n\t)\n\tf.UI.Nodes.Append(node.NewInputField(\"recovery_address\", body.RecoveryAddress, node.CodeGroup, node.InputAttributeTypeHidden))\n\n\tf.UI.\n\t\tGetNodes().\n\t\tAppend(node.NewInputField(\"method\", s.RecoveryStrategyID(), node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\tWithMetaLabel(text.NewInfoNodeLabelContinue()))\n\n\tf.UI.Nodes.Append(node.NewInputField(\"method\", s.NodeGroup(), node.CodeGroup, node.InputAttributeTypeHidden))\n\n\tbuttonScreen := node.NewInputField(\"screen\", \"previous\", node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewRecoveryBack())\n\tf.UI.GetNodes().Append(buttonScreen)\n\n\tif err := s.deps.RecoveryFlowPersister().UpdateRecoveryFlow(r.Context(), f); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) recoveryV2HandleStateConfirmingAddress(r *http.Request, f *recovery.Flow, body *recoverySubmitPayload) error {\n\tif f.State != flow.StateRecoveryAwaitingAddressConfirm {\n\t\treturn errors.Errorf(\"unreachable state: %s\", f.State)\n\t}\n\n\tif len(body.RecoveryConfirmAddress) == 0 {\n\t\treturn schema.NewRequiredError(\"#/recovery_confirm_address\", \"recovery_confirm_address\")\n\t}\n\n\tif err := s.deps.RecoveryCodePersister().DeleteRecoveryCodesOfFlow(r.Context(), f.ID); err != nil {\n\t\treturn err\n\t}\n\n\tf.TransientPayload = body.TransientPayload\n\n\t// NOTE: We do not fetch the db address here. We only (try to) send the code to the user provided address.\n\t// That way we avoid information exfiltration.\n\t// `SendRecoveryCode` will anyway check by itself if the provided address is a known address or not.\n\tif err := s.deps.CodeSender().SendRecoveryCode(r.Context(), f, hackyInferChannel(body.RecoveryConfirmAddress), body.RecoveryConfirmAddress, r.Header); err != nil {\n\t\tif !errors.Is(err, ErrUnknownAddress) {\n\t\t\treturn err\n\t\t}\n\n\t\t// Continue execution\n\t}\n\n\t// re-initialize the UI with a \"clean\" new state\n\tf.UI = &container.Container{\n\t\tMethod: \"POST\",\n\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(s.deps.Config().SelfPublicURL(r.Context()), recovery.RouteSubmitFlow), f.ID).String(),\n\t\tNodes:  f.UI.Nodes,\n\t}\n\tf.UI.Nodes.ClearTransientNodes()\n\tf.UI.SetCSRF(f.CSRFToken)\n\n\tf.State = flow.StateRecoveryAwaitingCode\n\n\tuiText := text.NewRecoveryCodeRecoverySelectAddressSent(MaskAddress(body.RecoveryConfirmAddress))\n\n\tf.UI.Messages.Set(uiText)\n\tf.UI.Nodes.Append(node.NewInputField(\"code\", nil, node.CodeGroup, node.InputAttributeTypeText, node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\ta.Required = true\n\t\ta.Pattern = \"[0-9]+\"\n\t\ta.MaxLength = CodeLength\n\t})).\n\t\tWithMetaLabel(text.NewInfoNodeLabelRecoveryCode()),\n\t)\n\n\tf.UI.Nodes.Append(node.NewInputField(\"method\", s.NodeGroup(), node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoNodeLabelContinue()),\n\t)\n\n\tf.UI.Nodes.Append(node.NewInputField(\"method\", s.NodeGroup(), node.CodeGroup, node.InputAttributeTypeHidden))\n\n\t// Required to make 'resend' work.\n\tf.UI.Nodes.Append(node.NewInputField(\"recovery_confirm_address\", body.RecoveryConfirmAddress, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoNodeResendOTP()),\n\t)\n\tf.UI.Nodes.Append(node.NewInputField(\"recovery_address\", body.RecoveryAddress, node.CodeGroup, node.InputAttributeTypeHidden))\n\n\tbuttonScreen := node.NewInputField(\"screen\", \"previous\", node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewRecoveryBack())\n\tf.UI.GetNodes().Append(buttonScreen)\n\n\tif err := s.deps.RecoveryFlowPersister().UpdateRecoveryFlow(r.Context(), f); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) recoveryV2HandleStateAwaitingCode(w http.ResponseWriter, r *http.Request, f *recovery.Flow, body *recoverySubmitPayload) error {\n\tif f.State != flow.StateRecoveryAwaitingCode {\n\t\treturn errors.Errorf(\"unreachable state: %s\", f.State)\n\t}\n\n\tif len(body.Code) == 0 {\n\t\t// The 're-send' button was clicked. We handle it as if the user first arrived at the state `RecoveryV2StateAwaitingAddressConfirm`.\n\t\t// That will invalidate all existing codes and send a new code.\n\t\tf.State = flow.StateRecoveryAwaitingAddressConfirm\n\t\treturn s.recoveryV2HandleStateConfirmingAddress(r, f, body)\n\t} else {\n\t\treturn s.recoveryUseCode(w, r, body, f)\n\t}\n}\n\nfunc (s *Strategy) recoveryV2HandleGoBack(r *http.Request, f *recovery.Flow, body *recoverySubmitPayload) error {\n\t// If no address choice needs to take place, just go to the first screen.\n\trecoveryAddresses, _ := s.deps.IdentityPool().FindAllRecoveryAddressesForIdentityByRecoveryAddressValue(r.Context(), body.RecoveryAddress)\n\tif len(recoveryAddresses) <= 1 {\n\t\tf.State = flow.StateRecoveryAwaitingAddress\n\t\terr := s.PopulateRecoveryMethod(r, f)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := s.deps.RecoveryFlowPersister().UpdateRecoveryFlow(r.Context(), f); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t}\n\n\tswitch f.State {\n\t// Go back to the second screen (choose an address) by essentially going to the first screen\n\t// and re-submitting the form (to arrive at the second screen).\n\t// This contraption is necessary since the UI nodes are stored in the database and not generated on the fly.\n\t// So simply redirecting to a previous screen (as in: 'web page') would do nothing, it would just show the same UI.\n\t// This way we force the UI generation code to re-run and the new UI nodes to be stored to the database.\n\tcase flow.StateRecoveryAwaitingCode:\n\t\tfallthrough\n\tcase flow.StateRecoveryAwaitingAddressConfirm:\n\t\t// Reset some body fields since we are going to (almost) the beginning of the flow.\n\t\tbody.RecoveryConfirmAddress = \"\"\n\t\tbody.RecoverySelectAddress = \"\"\n\t\tbody.Screen = \"\"\n\n\t\tf.State = flow.StateRecoveryAwaitingAddress\n\n\t\treturn s.recoveryV2HandleStateAwaitingAddress(r, f, body)\n\tdefault:\n\t\t// Should not trigger, but do something sensible: start from scratch.\n\t\treturn s.PopulateRecoveryMethod(r, f)\n\t}\n}\n\n// recoveryHandleFormSubmission handles the submission of an address for recovery\nfunc (s *Strategy) recoveryHandleFormSubmission(w http.ResponseWriter, r *http.Request, f *recovery.Flow, body *recoverySubmitPayload) error {\n\tif len(body.Email) == 0 {\n\t\treturn s.HandleRecoveryError(w, r, f, body, schema.NewRequiredError(\"#/email\", \"email\"))\n\t}\n\n\tctx := r.Context()\n\tconfig := s.deps.Config()\n\n\tif err := flow.EnsureCSRF(s.deps, r, f.Type, config.DisableAPIFlowEnforcement(ctx), s.deps.GenerateCSRFToken, body.CSRFToken); err != nil {\n\t\treturn s.HandleRecoveryError(w, r, f, body, err)\n\t}\n\n\tif err := s.deps.RecoveryCodePersister().DeleteRecoveryCodesOfFlow(ctx, f.ID); err != nil {\n\t\treturn s.HandleRecoveryError(w, r, f, body, err)\n\t}\n\n\tf.TransientPayload = body.TransientPayload\n\tif err := s.deps.CodeSender().SendRecoveryCode(ctx, f, identity.AddressTypeEmail, body.Email, r.Header); err != nil {\n\t\tif !errors.Is(err, ErrUnknownAddress) {\n\t\t\treturn s.HandleRecoveryError(w, r, f, body, err)\n\t\t}\n\t\t// Continue execution\n\t}\n\n\t// re-initialize the UI with a \"clean\" new state\n\tf.UI = &container.Container{\n\t\tMethod: \"POST\",\n\t\tAction: flow.AppendFlowTo(urlx.AppendPaths(s.deps.Config().SelfPublicURL(r.Context()), recovery.RouteSubmitFlow), f.ID).String(),\n\t\tNodes:  f.UI.Nodes,\n\t}\n\tf.UI.Nodes.ClearTransientNodes()\n\n\tf.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\n\tf.Active = sqlxx.NullString(s.NodeGroup())\n\tf.State = flow.StateEmailSent\n\tf.UI.Messages.Set(text.NewRecoveryEmailWithCodeSent())\n\tf.UI.Nodes.Append(node.NewInputField(\"code\", nil, node.CodeGroup, node.InputAttributeTypeText, node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\ta.Required = true\n\t\ta.Pattern = \"[0-9]+\"\n\t\ta.MaxLength = CodeLength\n\t})).\n\t\tWithMetaLabel(text.NewInfoNodeLabelRecoveryCode()),\n\t)\n\tf.UI.Nodes.Append(node.NewInputField(\"method\", s.NodeGroup(), node.CodeGroup, node.InputAttributeTypeHidden))\n\n\tf.UI.\n\t\tGetNodes().\n\t\tAppend(node.NewInputField(\"method\", s.RecoveryStrategyID(), node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\tWithMetaLabel(text.NewInfoNodeLabelContinue()))\n\n\tf.UI.Nodes.Append(node.NewInputField(\"email\", body.Email, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoNodeResendOTP()),\n\t)\n\tif err := s.deps.RecoveryFlowPersister().UpdateRecoveryFlow(r.Context(), f); err != nil {\n\t\treturn s.HandleRecoveryError(w, r, f, body, err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) markRecoveryAddressVerified(w http.ResponseWriter, r *http.Request, f *recovery.Flow, id *identity.Identity, recoveryAddress *identity.RecoveryAddress) error {\n\tfor k, v := range id.VerifiableAddresses {\n\t\tif v.Value == recoveryAddress.Value {\n\t\t\tid.VerifiableAddresses[k].Verified = true\n\t\t\tid.VerifiableAddresses[k].VerifiedAt = new(sqlxx.NullTime(time.Now().UTC()))\n\t\t\tid.VerifiableAddresses[k].Status = identity.VerifiableAddressStatusCompleted\n\t\t\tif err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), &id.VerifiableAddresses[k], \"verified\", \"verified_at\", \"status\"); err != nil {\n\t\t\t\treturn s.HandleRecoveryError(w, r, f, nil, err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) HandleRecoveryError(w http.ResponseWriter, r *http.Request, fl *recovery.Flow, body *recoverySubmitPayload, err error) error {\n\tif fl != nil {\n\t\tif flow.IsStateRecoveryV2(fl.State) {\n\t\t\t// Unreachable: RecoveryV2 never uses this function.\n\t\t\treturn err\n\t\t}\n\t\temail := \"\"\n\t\tif body != nil {\n\t\t\temail = body.Email\n\t\t}\n\n\t\tfl.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\t\tfl.UI.GetNodes().Upsert(\n\t\t\tnode.NewInputField(\"email\", email, node.CodeGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).\n\t\t\t\tWithMetaLabel(text.NewInfoNodeInputEmail()),\n\t\t)\n\t}\n\n\treturn err\n}\n\ntype recoverySubmitPayload struct {\n\tMethod           string          `json:\"method\" form:\"method\"`\n\tCode             string          `json:\"code\" form:\"code\"`\n\tCSRFToken        string          `json:\"csrf_token\" form:\"csrf_token\"`\n\tFlow             string          `json:\"flow\" form:\"flow\"`\n\tEmail            string          `json:\"email\" form:\"email\"`\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n\n\t// Used in RecoveryV2.\n\tRecoveryAddress        string `json:\"recovery_address\" form:\"recovery_address\"`\n\tRecoverySelectAddress  string `json:\"recovery_select_address\" form:\"recovery_select_address\"`\n\tRecoveryConfirmAddress string `json:\"recovery_confirm_address\" form:\"recovery_confirm_address\"`\n\tScreen                 string `json:\"screen\" form:\"screen\"`\n}\n\nfunc (s *Strategy) decodeRecovery(r *http.Request) (*recoverySubmitPayload, error) {\n\tvar body recoverySubmitPayload\n\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(recoveryMethodSchema)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif err := decoderx.Decode(r, &body, compiler,\n\t\tdecoderx.HTTPDecoderUseQueryAndBody(),\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn &body, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_recovery_admin.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst (\n\tRouteAdminCreateRecoveryCode = \"/recovery/code\"\n)\n\nfunc (s *Strategy) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\ts.deps.CSRFHandler().IgnorePath(RouteAdminCreateRecoveryCode)\n\tpublic.POST(RouteAdminCreateRecoveryCode, redir.RedirectToAdminRoute(s.deps))\n}\n\nfunc (s *Strategy) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\twrappedCreateRecoveryCode := strategy.IsDisabled(s.deps, s.RecoveryStrategyID(), s.createRecoveryCodeForIdentity)\n\tadmin.POST(RouteAdminCreateRecoveryCode, wrappedCreateRecoveryCode)\n}\n\n// Create Recovery Code for Identity Parameters\n//\n// swagger:parameters createRecoveryCodeForIdentity\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype createRecoveryCodeForIdentity struct {\n\t// in: body\n\tBody createRecoveryCodeForIdentityBody\n}\n\n// Create Recovery Code for Identity Request Body\n//\n// swagger:model createRecoveryCodeForIdentityBody\ntype createRecoveryCodeForIdentityBody struct {\n\t// Identity to Recover\n\t//\n\t// The identity's ID you wish to recover.\n\t//\n\t// required: true\n\tIdentityID uuid.UUID `json:\"identity_id\"`\n\n\t// Code Expires In\n\t//\n\t// The recovery code will expire after that amount of time has passed. Defaults to the configuration value of\n\t// `selfservice.methods.code.config.lifespan`.\n\t//\n\t//\n\t// pattern: ^([0-9]+(ns|us|ms|s|m|h))*$\n\t// example:\n\t//\t- 1h\n\t//\t- 1m\n\t//\t- 1s\n\tExpiresIn string `json:\"expires_in\"`\n\n\t// Flow Type\n\t//\n\t// The flow type for the recovery flow. Defaults to browser.\n\t//\n\t// required: false\n\tFlowType *flow.Type `json:\"flow_type\"`\n}\n\n// Recovery Code for Identity\n//\n// Used when an administrator creates a recovery code for an identity.\n//\n// swagger:model recoveryCodeForIdentity\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype recoveryCodeForIdentity struct {\n\t// RecoveryLink with flow\n\t//\n\t// This link opens the recovery UI with an empty `code` field.\n\t//\n\t// required: true\n\t// format: uri\n\tRecoveryLink string `json:\"recovery_link\"`\n\n\t// RecoveryCode is the code that can be used to recover the account\n\t//\n\t// required: true\n\tRecoveryCode string `json:\"recovery_code\"`\n\n\t// Expires At is the timestamp of when the recovery flow expires\n\t//\n\t// The timestamp when the recovery code expires.\n\tExpiresAt time.Time `json:\"expires_at\"`\n}\n\n// swagger:route POST /admin/recovery/code identity createRecoveryCodeForIdentity\n//\n// # Create a Recovery Code\n//\n// This endpoint creates a recovery code which should be given to the user in order for them to recover\n// (or activate) their account.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  201: recoveryCodeForIdentity\n//\t  400: errorGeneric\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (s *Strategy) createRecoveryCodeForIdentity(w http.ResponseWriter, r *http.Request) {\n\tvar p createRecoveryCodeForIdentityBody\n\tif err := decoderx.Decode(r, &p, decoderx.HTTPJSONDecoder()); err != nil {\n\t\ts.deps.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tctx := r.Context()\n\tconfig := s.deps.Config()\n\n\texpiresIn := config.SelfServiceCodeMethodLifespan(ctx)\n\tif len(p.ExpiresIn) > 0 {\n\t\t// If an expiration of the code was supplied use it instead of the default duration\n\t\tvar err error\n\t\texpiresIn, err = time.ParseDuration(p.ExpiresIn)\n\t\tif err != nil {\n\t\t\ts.deps.Writer().WriteError(w, r, errors.WithStack(herodot.\n\t\t\t\tErrBadRequest.\n\t\t\t\tWithReasonf(`Unable to parse \"expires_in\" whose format should match \"[0-9]+(ns|us|ms|s|m|h)\" but did not: %s`, p.ExpiresIn)))\n\t\t\treturn\n\t\t}\n\t}\n\n\tif expiresIn <= 0 {\n\t\ts.deps.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Value from \"expires_in\" must result to a future time: %s`, expiresIn)))\n\t\treturn\n\t}\n\n\tflowType := flow.TypeBrowser\n\tif p.FlowType != nil {\n\t\tflowType = *p.FlowType\n\t}\n\tif !flowType.Valid() {\n\t\ts.deps.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Value from \"flow_type\" is not valid: %q`, flowType)))\n\t\treturn\n\t}\n\n\trecoveryFlow, err := recovery.NewFlow(config, expiresIn, s.deps.GenerateCSRFToken(r), r, recovery.Strategies{s}, flowType)\n\tif err != nil {\n\t\ts.deps.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\trecoveryFlow.DangerousSkipCSRFCheck = true\n\trecoveryFlow.State = flow.StateEmailSent\n\trecoveryFlow.UI.Nodes = node.Nodes{}\n\trecoveryFlow.UI.Nodes.Append(node.NewInputField(\"code\", nil, node.CodeGroup, node.InputAttributeTypeText, node.WithRequiredInputAttribute, node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\ta.Pattern = \"[0-9]+\"\n\t\ta.MaxLength = CodeLength\n\t})).\n\t\tWithMetaLabel(text.NewInfoNodeLabelRecoveryCode()),\n\t)\n\trawCode := GenerateCode()\n\n\trecoveryFlow.UI.Nodes.\n\t\tAppend(node.NewInputField(\"method\", s.RecoveryStrategyID(), node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\tWithMetaLabel(text.NewInfoNodeLabelContinue()))\n\n\tid, err := s.deps.IdentityPool().GetIdentity(ctx, p.IdentityID, identity.ExpandDefault)\n\tif notFoundErr := sqlcon.ErrNoRows; errors.As(err, &notFoundErr) {\n\t\ts.deps.Writer().WriteError(w, r, notFoundErr.WithReasonf(\"could not find identity\"))\n\t\treturn\n\t} else if err != nil {\n\t\ts.deps.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif err := s.deps.TransactionalPersisterProvider().Transaction(ctx, func(ctx context.Context, c *pop.Connection) error {\n\t\tif err := s.deps.RecoveryFlowPersister().CreateRecoveryFlow(ctx, recoveryFlow); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif _, err := s.deps.RecoveryCodePersister().CreateRecoveryCode(ctx, &CreateRecoveryCodeParams{\n\t\t\tRawCode:    rawCode,\n\t\t\tCodeType:   RecoveryCodeTypeAdmin,\n\t\t\tExpiresIn:  expiresIn,\n\t\t\tFlowID:     recoveryFlow.ID,\n\t\t\tIdentityID: id.ID,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn nil\n\t}); err != nil {\n\t\ts.deps.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\ttrace.SpanFromContext(r.Context()).AddEvent(\n\t\tevents.NewRecoveryInitiatedByAdmin(ctx, recoveryFlow.ID, id.ID, flowType.String(), \"code\"),\n\t)\n\n\ts.deps.Logger().\n\t\tWithField(\"identity_id\", id.ID).\n\t\tWithSensitiveField(\"recovery_code\", rawCode).\n\t\tInfo(\"A recovery code has been created.\")\n\n\tbody := &recoveryCodeForIdentity{\n\t\tExpiresAt: recoveryFlow.ExpiresAt.UTC(),\n\t\tRecoveryLink: urlx.CopyWithQuery(\n\t\t\ts.deps.Config().SelfServiceFlowRecoveryUI(ctx),\n\t\t\turl.Values{\n\t\t\t\t\"flow\": {recoveryFlow.ID.String()},\n\t\t\t}).String(),\n\t\tRecoveryCode: rawCode,\n\t}\n\n\ts.deps.Writer().WriteCode(w, r, http.StatusCreated, body, herodot.UnescapedHTML)\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_recovery_admin_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/cookiejar\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t. \"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\nfunc TestAdminStrategy(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(defaultConfig),\n\t\tconfigx.WithValue(config.ViperKeyUseContinueWithTransitions, true),\n\t)\n\n\t_ = testhelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublicTS, adminTS := testhelpers.NewKratosServer(t, reg)\n\tadminSDK := testhelpers.NewSDKClient(adminTS)\n\n\ttype createCodeParams = kratos.CreateRecoveryCodeForIdentityBody\n\tcreateCode := func(params createCodeParams) (*kratos.RecoveryCodeForIdentity, *http.Response, error) {\n\t\treturn adminSDK.IdentityAPI.\n\t\t\tCreateRecoveryCodeForIdentity(context.Background()).\n\t\t\tCreateRecoveryCodeForIdentityBody(params).Execute()\n\t}\n\n\tt.Run(\"no panic on empty body #1384\", func(t *testing.T) {\n\t\tctx := context.Background()\n\t\ts, ps, err := reg.RecoveryStrategies(ctx).ActiveStrategies(\"code\")\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, s, 1)\n\t\tw := httptest.NewRecorder()\n\t\tr := &http.Request{URL: new(url.URL)}\n\t\tf, err := recovery.NewFlow(reg.Config(), time.Minute, \"\", r, s, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\trequire.NotPanics(t, func() {\n\t\t\trequire.Error(t, ps.(*Strategy).HandleRecoveryError(w, r, f, nil, errors.New(\"test\")))\n\t\t})\n\t})\n\n\tt.Run(\"description=should not be able to recover an account that does not exist\", func(t *testing.T) {\n\t\t_, _, err := createCode(createCodeParams{IdentityId: x.NewUUID().String()})\n\n\t\trequire.IsType(t, err, new(kratos.GenericOpenAPIError), \"%T\", err)\n\t\tsnapshotx.SnapshotT(t, err.(*kratos.GenericOpenAPIError).Model())\n\t})\n\n\tt.Run(\"description=should fail on malformed expiry time\", func(t *testing.T) {\n\t\t_, _, err := createCode(createCodeParams{IdentityId: x.NewUUID().String(), ExpiresIn: new(\"not-a-valid-value\")})\n\t\trequire.IsType(t, err, new(kratos.GenericOpenAPIError), \"%T\", err)\n\t\tsnapshotx.SnapshotT(t, err.(*kratos.GenericOpenAPIError).Model())\n\t})\n\n\tt.Run(\"description=should fail on negative expiry time\", func(t *testing.T) {\n\t\t_, _, err := createCode(createCodeParams{IdentityId: x.NewUUID().String(), ExpiresIn: new(\"-1h\")})\n\t\trequire.IsType(t, err, new(kratos.GenericOpenAPIError), \"%T\", err)\n\t\tsnapshotx.SnapshotT(t, err.(*kratos.GenericOpenAPIError).Model())\n\t})\n\n\tsubmitRecoveryCode := func(t *testing.T, client *http.Client, link string, code string) []byte {\n\t\tt.Helper()\n\t\tif client == nil {\n\t\t\tclient = publicTS.Client()\n\t\t}\n\t\tres, err := client.Get(link)\n\t\trequire.NoError(t, err)\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\taction := gjson.GetBytes(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\n\t\tres, err = client.PostForm(action, url.Values{\n\t\t\t\"code\": {code},\n\t\t})\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\treturn ioutilx.MustReadAll(res.Body)\n\t}\n\n\tassertEmailNotVerified := func(t *testing.T, email string) {\n\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, email)\n\t\tassert.NoError(t, err)\n\t\tassert.False(t, addr.Verified)\n\t\tassert.Nil(t, addr.VerifiedAt)\n\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\t}\n\n\tt.Run(\"description=should create code without email\", func(t *testing.T) {\n\t\tid := identity.Identity{Traits: identity.Traits(`{}`)}\n\n\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(),\n\t\t\t&id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\tcode, _, err := createCode(createCodeParams{IdentityId: id.ID.String()})\n\t\trequire.NoError(t, err)\n\n\t\trequire.NotEmpty(t, code.RecoveryLink)\n\t\trequire.Contains(t, code.RecoveryLink, \"flow=\")\n\t\trequire.NotContains(t, code.RecoveryLink, \"code=\")\n\t\trequire.NotEmpty(t, code.RecoveryCode)\n\t\trequire.True(t, code.ExpiresAt.Before(time.Now().Add(conf.SelfServiceFlowRecoveryRequestLifespan(t.Context()))))\n\n\t\tclient := new(*publicTS.Client())\n\t\tclient.Jar, _ = cookiejar.New(nil)\n\t\tbody := submitRecoveryCode(t, client, code.RecoveryLink, code.RecoveryCode)\n\t\ttesthelpers.AssertMessage(t, body, \"You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 60.00 minutes.\")\n\t\tu, err := url.Parse(publicTS.URL)\n\t\trequire.NoError(t, err)\n\t\tcs := client.Jar.Cookies(u)\n\t\trequire.Len(t, cs, 1, \"%s\", body)\n\t\tassert.Equal(t, \"ory_kratos_session\", cs[0].Name, \"%s\", body)\n\t})\n\n\tt.Run(\"description=should not be able to recover with expired code\", func(t *testing.T) {\n\t\trecoveryEmail := \"recover.expired@ory.sh\"\n\t\tid := identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, recoveryEmail))}\n\n\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(),\n\t\t\t&id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\tcode, _, err := createCode(createCodeParams{IdentityId: id.ID.String(), ExpiresIn: new(\"100ms\")})\n\t\trequire.NoError(t, err)\n\n\t\ttime.Sleep(time.Millisecond * 100)\n\t\trequire.NotEmpty(t, code.RecoveryLink)\n\t\trequire.True(t, code.ExpiresAt.Before(time.Now().Add(conf.SelfServiceFlowRecoveryRequestLifespan(t.Context()))))\n\n\t\tbody := submitRecoveryCode(t, nil, code.RecoveryLink, code.RecoveryCode)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.messages.0.text\").Str, \"The recovery flow expired\", \"%s\", body)\n\n\t\t// The recovery address should not be verified if the flow was initiated by the admins\n\t\tassertEmailNotVerified(t, recoveryEmail)\n\t})\n\n\tt.Run(\"description=should create a valid recovery link and set the expiry time as well and recover the account\", func(t *testing.T) {\n\t\trecoveryEmail := \"recoverme@ory.sh\"\n\t\tid := identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, recoveryEmail))}\n\n\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(),\n\t\t\t&id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\tcode, _, err := createCode(createCodeParams{IdentityId: id.ID.String()})\n\t\trequire.NoError(t, err)\n\n\t\trequire.NotEmpty(t, code.RecoveryLink)\n\t\trequire.True(t, code.ExpiresAt.Before(time.Now().Add(conf.SelfServiceFlowRecoveryRequestLifespan(t.Context())+time.Second)))\n\n\t\tbody := submitRecoveryCode(t, nil, code.RecoveryLink, code.RecoveryCode)\n\n\t\ttesthelpers.AssertMessage(t, body, \"You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 60.00 minutes.\")\n\n\t\t// The recovery address should be verified if the flow was initiated by the admins\n\t\tassertEmailNotVerified(t, recoveryEmail)\n\t})\n\n\tt.Run(\"description=should respect custom expires_in in response\", func(t *testing.T) {\n\t\tid := identity.Identity{Traits: identity.Traits(`{}`)}\n\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(),\n\t\t\t&id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\tcode, _, err := createCode(createCodeParams{IdentityId: id.ID.String(), ExpiresIn: new(\"24h\")})\n\t\trequire.NoError(t, err)\n\n\t\t// The response expires_at must reflect the requested 24h, not the default 1h lifespan.\n\t\trequire.WithinDuration(t, time.Now().Add(24*time.Hour), *code.ExpiresAt, time.Hour)\n\t})\n\n\tt.Run(\"case=should not be able to use code from different flow\", func(t *testing.T) {\n\t\temail := testhelpers.RandomEmail()\n\t\ti := createIdentityToRecover(t, reg, email)\n\n\t\tc1, _, err := createCode(createCodeParams{IdentityId: i.ID.String(), ExpiresIn: new(\"1h\")})\n\t\trequire.NoError(t, err)\n\t\tc2, _, err := createCode(createCodeParams{IdentityId: i.ID.String(), ExpiresIn: new(\"1h\")})\n\t\trequire.NoError(t, err)\n\t\tcode2 := c2.RecoveryCode\n\t\trequire.NotEmpty(t, code2)\n\n\t\tbody := submitRecoveryCode(t, nil, c1.RecoveryLink, c2.RecoveryCode)\n\n\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t})\n\n\tt.Run(\"case=form should not contain email field when creating recovery code\", func(t *testing.T) {\n\t\temail := testhelpers.RandomEmail()\n\t\ti := createIdentityToRecover(t, reg, email)\n\n\t\tc1, _, err := createCode(createCodeParams{IdentityId: i.ID.String(), ExpiresIn: new(\"1h\")})\n\t\trequire.NoError(t, err)\n\n\t\tres, err := http.Get(c1.RecoveryLink)\n\t\trequire.NoError(t, err)\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.GetBytes(body, \"ui.nodes\").String()))\n\t})\n\n\tt.Run(\"case=should be able to create and complete an API flow\", func(t *testing.T) {\n\t\temail := testhelpers.RandomEmail()\n\t\ti := createIdentityToRecover(t, reg, email)\n\n\t\tcode, _, err := createCode(createCodeParams{IdentityId: i.ID.String(), FlowType: new(string(flow.TypeAPI))})\n\t\trequire.NoError(t, err)\n\n\t\tres, err := publicTS.Client().Get(code.RecoveryLink)\n\t\trequire.NoError(t, err)\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\taction := gjson.GetBytes(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\n\t\tres, err = publicTS.Client().Post(action, \"application/json\", strings.NewReader(fmt.Sprintf(`{\"code\":\"%s\"}`, code.RecoveryCode)))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\tcontinueWith := gjson.GetBytes(ioutilx.MustReadAll(res.Body), \"continue_with\").Array()\n\t\trequire.Len(t, continueWith, 2)\n\t\tassert.EqualValues(t, flow.ContinueWithActionSetOrySessionTokenString, continueWith[0].Get(\"action\").String())\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_recovery_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/davecgh/go-spew/spew\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/corpx\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/hook/hooktest\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc extractCsrfToken(body []byte) string {\n\treturn gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n}\n\ntype ClientType string\n\nconst (\n\tRecoveryClientTypeBrowser ClientType = \"browser\"\n\tRecoveryClientTypeSPA     ClientType = \"spa\"\n\tRecoveryClientTypeAPI     ClientType = \"api\"\n)\n\nfunc (c ClientType) String() string {\n\treturn string(c)\n}\n\nfunc apiHttpClient(*testing.T) *http.Client {\n\treturn &http.Client{}\n}\n\nfunc spaHttpClient(t *testing.T) *http.Client {\n\treturn testhelpers.NewClientWithCookies(t)\n}\n\nfunc browserHttpClient(t *testing.T) *http.Client {\n\treturn testhelpers.NewClientWithCookies(t)\n}\n\nvar flowTypes = []ClientType{RecoveryClientTypeBrowser, RecoveryClientTypeAPI, RecoveryClientTypeSPA}\n\nvar flowTypeCases = []struct {\n\tFlowType        flow.Type\n\tClientType      ClientType\n\tGetClient       func(*testing.T) *http.Client\n\tFormContentType string\n}{\n\t{\n\t\tFlowType:        flow.TypeBrowser,\n\t\tClientType:      RecoveryClientTypeBrowser,\n\t\tGetClient:       testhelpers.NewClientWithCookies,\n\t\tFormContentType: \"application/x-www-form-urlencoded\",\n\t},\n\t{\n\t\tFlowType:   flow.TypeAPI,\n\t\tClientType: RecoveryClientTypeAPI,\n\t\tGetClient: func(_ *testing.T) *http.Client {\n\t\t\treturn &http.Client{}\n\t\t},\n\t\tFormContentType: \"application/json\",\n\t},\n\t{\n\t\tFlowType:        flow.TypeBrowser,\n\t\tClientType:      RecoveryClientTypeSPA,\n\t\tGetClient:       testhelpers.NewClientWithCookies,\n\t\tFormContentType: \"application/json\",\n\t},\n}\n\nfunc withCSRFToken(t *testing.T, clientType ClientType, body string, v url.Values) string {\n\tt.Helper()\n\tcsrfToken := gjson.Get(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\tif csrfToken != \"\" && clientType != RecoveryClientTypeAPI {\n\t\tv.Set(\"csrf_token\", csrfToken)\n\t}\n\tif clientType == RecoveryClientTypeBrowser {\n\t\treturn v.Encode()\n\t}\n\treturn testhelpers.EncodeFormAsJSON(t, true, v)\n}\n\nfunc createIdentityToRecover(t *testing.T, reg *driver.RegistryDefault, email string) *identity.Identity {\n\tt.Helper()\n\tid := &identity.Identity{\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\"password\": {\n\t\t\t\tType:        \"password\",\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t},\n\t\t},\n\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, email)),\n\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\tState:    identity.StateActive,\n\t}\n\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), id, identity.ManagerAllowWriteProtectedTraits))\n\n\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, email)\n\tassert.NoError(t, err)\n\tassert.False(t, addr.Verified)\n\tassert.Nil(t, addr.VerifiedAt)\n\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\treturn id\n}\n\nfunc TestRecovery(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(defaultConfig),\n\t)\n\n\t_ = testhelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublic, _, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\tsubmitRecovery := func(t *testing.T, client *http.Client, flowType ClientType, values func(url.Values), code int) string {\n\t\tisSPA := flowType == RecoveryClientTypeSPA\n\t\tisAPI := flowType == RecoveryClientTypeAPI\n\t\tif client == nil {\n\t\t\tclient = testhelpers.NewDebugClient(t)\n\t\t\tif !isAPI {\n\t\t\t\tclient = testhelpers.NewClientWithCookies(t)\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t}\n\t\t}\n\n\t\texpectedUrl := testhelpers.ExpectURL(isAPI || isSPA, public.URL+recovery.RouteSubmitFlow, conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\treturn testhelpers.SubmitRecoveryForm(t, isAPI, isSPA, client, public, values, code, expectedUrl)\n\t}\n\n\tsubmitRecoveryCode := func(t *testing.T, client *http.Client, flow string, flowType ClientType, recoveryCode string, statusCode int) string {\n\t\taction := gjson.Get(flow, \"ui.action\").String()\n\t\tassert.NotEmpty(t, action)\n\n\t\tvalues := withCSRFToken(t, flowType, flow, url.Values{\n\t\t\t\"code\":   {recoveryCode},\n\t\t\t\"method\": {\"code\"},\n\t\t})\n\n\t\tcontentType := \"application/json\"\n\t\tif flowType == RecoveryClientTypeBrowser {\n\t\t\tcontentType = \"application/x-www-form-urlencoded\"\n\t\t}\n\n\t\tres, err := client.Post(action, contentType, bytes.NewBufferString(values))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, statusCode, res.StatusCode)\n\n\t\treturn string(ioutilx.MustReadAll(res.Body))\n\t}\n\n\tresendRecoveryCode := func(t *testing.T, client *http.Client, flow string, flowType ClientType, statusCode int) string {\n\t\taction := gjson.Get(flow, \"ui.action\").String()\n\t\tassert.NotEmpty(t, action)\n\n\t\temail := gjson.Get(flow, \"ui.nodes.#(attributes.name==email).attributes.value\").String()\n\n\t\tvalues := withCSRFToken(t, flowType, flow, url.Values{\n\t\t\t\"method\": {\"code\"},\n\t\t\t\"email\":  {email},\n\t\t})\n\n\t\tcontentType := \"application/json\"\n\t\tif flowType == RecoveryClientTypeBrowser {\n\t\t\tcontentType = \"application/x-www-form-urlencoded\"\n\t\t}\n\n\t\tres, err := client.Post(action, contentType, bytes.NewBufferString(values))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, statusCode, res.StatusCode)\n\n\t\treturn string(ioutilx.MustReadAll(res.Body))\n\t}\n\n\texpectValidationError := func(t *testing.T, hc *http.Client, flowType ClientType, values func(url.Values)) string {\n\t\tcode := testhelpers.ExpectStatusCode(flowType == RecoveryClientTypeAPI || flowType == RecoveryClientTypeSPA, http.StatusBadRequest, http.StatusOK)\n\t\treturn submitRecovery(t, hc, flowType, values, code)\n\t}\n\n\texpectSuccessfulRecovery := func(t *testing.T, hc *http.Client, flowType ClientType, values func(url.Values)) string {\n\t\tcode := testhelpers.ExpectStatusCode(flowType == RecoveryClientTypeAPI || flowType == RecoveryClientTypeSPA, http.StatusUnprocessableEntity, http.StatusOK)\n\t\treturn submitRecovery(t, hc, flowType, values, code)\n\t}\n\n\tExpectVerfiableAddressStatus := func(t *testing.T, email string, status identity.VerifiableAddressStatus) {\n\t\taddr, err := reg.IdentityPool().\n\t\t\tFindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, email)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, status, addr.Status, \"verifiable address %s was not %s. instead %s\", email, status, addr.Status)\n\t}\n\n\tt.Run(\"description=should recover an account\", func(t *testing.T) {\n\t\tcheckRecovery := func(t *testing.T, client *http.Client, flowType ClientType, recoveryEmail, recoverySubmissionResponse string) string {\n\t\t\tExpectVerfiableAddressStatus(t, recoveryEmail, identity.VerifiableAddressStatusPending)\n\n\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(recoverySubmissionResponse, \"active\").String(), \"%s\", recoverySubmissionResponse)\n\t\t\tassert.True(t, gjson.Get(recoverySubmissionResponse, \"ui.nodes.#(attributes.name==code)\").Exists(), \"%s\", recoverySubmissionResponse)\n\t\t\tassert.Len(t, gjson.Get(recoverySubmissionResponse, \"ui.messages\").Array(), 1, \"%s\", recoverySubmissionResponse)\n\t\t\tassertx.EqualAsJSON(t, text.NewRecoveryEmailWithCodeSent(), json.RawMessage(gjson.Get(recoverySubmissionResponse, \"ui.messages.0\").Raw))\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\tassert.Contains(t, message.Body, \"Recover access to your account by entering\")\n\n\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\tassert.NotEmpty(t, recoveryCode)\n\n\t\t\tstatusCode := testhelpers.ExpectStatusCode(flowType == RecoveryClientTypeAPI || flowType == RecoveryClientTypeSPA, http.StatusUnprocessableEntity, http.StatusOK)\n\t\t\treturn submitRecoveryCode(t, client, recoverySubmissionResponse, flowType, recoveryCode, statusCode)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tvar wg sync.WaitGroup\n\t\t\twg.Add(1)\n\t\t\ttesthelpers.NewRecoveryAfterHookWebHookTarget(ctx, t, conf, func(t *testing.T, msg []byte) {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tassert.EqualValues(t, \"recoverme1@ory.sh\", gjson.GetBytes(msg, \"identity.verifiable_addresses.0.value\").String(), string(msg))\n\t\t\t\tassert.EqualValues(t, true, gjson.GetBytes(msg, \"identity.verifiable_addresses.0.verified\").Bool(), string(msg))\n\t\t\t\tassert.EqualValues(t, \"completed\", gjson.GetBytes(msg, \"identity.verifiable_addresses.0.status\").String(), string(msg))\n\t\t\t})\n\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\temail := \"recoverme1@ory.sh\"\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\trecoverySubmissionResponse := submitRecovery(t, client, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}, http.StatusOK)\n\t\t\tbody := checkRecovery(t, client, RecoveryClientTypeBrowser, email, recoverySubmissionResponse)\n\n\t\t\tassert.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\tres, err := client.Get(public.URL + session.RouteWhoami)\n\t\t\trequire.NoError(t, err)\n\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\n\t\t\twg.Wait()\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\temail := \"recoverme3@ory.sh\"\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\trecoverySubmissionResponse := submitRecovery(t, client, RecoveryClientTypeSPA, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}, http.StatusOK)\n\t\t\tbody := checkRecovery(t, client, RecoveryClientTypeSPA, email, recoverySubmissionResponse)\n\t\t\tassert.Equal(t, \"browser_location_change_required\", gjson.Get(body, \"error.id\").String())\n\t\t\tassert.Contains(t, gjson.Get(body, \"redirect_browser_to\").String(), \"settings-ts?\")\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tclient := &http.Client{}\n\t\t\temail := \"recoverme4@ory.sh\"\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\trecoverySubmissionResponse := submitRecovery(t, client, RecoveryClientTypeAPI, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}, http.StatusOK)\n\t\t\tbody := checkRecovery(t, client, RecoveryClientTypeAPI, email, recoverySubmissionResponse)\n\t\t\tassert.Equal(t, \"browser_location_change_required\", gjson.Get(body, \"error.id\").String())\n\t\t\tassert.Contains(t, gjson.Get(body, \"redirect_browser_to\").String(), \"settings-ts?\")\n\t\t})\n\n\t\tt.Run(\"description=should pass transient data to email template and webhooks\", func(t *testing.T) {\n\t\t\twebhookTS := hooktest.NewServer()\n\t\t\tt.Cleanup(webhookTS.Close)\n\n\t\t\tconf.MustSet(ctx, \"selfservice.flows.recovery.after.hooks\", []config.SelfServiceHook{webhookTS.HookConfig()})\n\t\t\tt.Cleanup(func() { conf.MustSet(ctx, \"selfservice.flows.recovery.after.hooks\", nil) })\n\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\temail := testhelpers.RandomEmail()\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\ttemplatePayload := `{\"payload\":\"template data\"}`\n\t\t\twebhookPayload := `{\"payload\":\"webhook data\"}`\n\n\t\t\tf := testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\n\t\t\tformPayload := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tformPayload.Set(\"email\", email)\n\t\t\tformPayload.Set(\"transient_payload\", templatePayload)\n\n\t\t\tbody, _ := testhelpers.RecoveryMakeRequest(t, false, f, client, formPayload.Encode())\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Use code\")\n\t\t\tassert.Equal(t, templatePayload, gjson.GetBytes(message.TemplateData, \"transient_payload\").String(),\n\t\t\t\t\"should pass transient payload to email template\")\n\n\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\tassert.NotEmpty(t, recoveryCode)\n\n\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\tassert.NotEmpty(t, action)\n\n\t\t\t_, err := client.Post(action, \"application/x-www-form-urlencoded\", bytes.NewBufferString(\n\t\t\t\twithCSRFToken(t, RecoveryClientTypeBrowser, body, url.Values{\n\t\t\t\t\t\"code\":              {recoveryCode},\n\t\t\t\t\t\"method\":            {\"code\"},\n\t\t\t\t\t\"transient_payload\": {webhookPayload},\n\t\t\t\t})))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.JSONEq(t, webhookPayload, gjson.GetBytes(webhookTS.LastBody, \"flow.transient_payload\").String(),\n\t\t\t\t\"should pass transient payload to webhook\")\n\t\t})\n\n\t\tt.Run(\"description=should return browser to return url\", func(t *testing.T) {\n\t\t\treturnTo := public.URL + \"/return-to\"\n\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdesc        string\n\t\t\t\treturnTo    string\n\t\t\t\tf           func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow\n\t\t\t\texpectedAAL string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from recovery flow\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from config\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, returnTo)\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, \"\")\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"no return to\",\n\t\t\t\t\treturnTo: \"\",\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to with an account that has 2fa enabled\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, id *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPDisplayName, \"Kratos\")\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPID, \"ory.sh\")\n\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t})\n\t\t\t\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeWebAuthn.String(), true)\n\n\t\t\t\t\t\tid.SetCredentials(identity.CredentialsTypeWebAuthn, identity.Credentials{\n\t\t\t\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\t\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":false, \"display_name\":\"test\"}]}`),\n\t\t\t\t\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Update(ctx, id, identity.ManagerAllowWriteProtectedTraits))\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t\texpectedAAL: \"aal2\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\ti := createIdentityToRecover(t, reg, email)\n\n\t\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t\t\tf := tc.f(t, client, i)\n\n\t\t\t\t\tformPayload := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\t\tformPayload.Set(\"email\", email)\n\n\t\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, false, f, client, formPayload.Encode())\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\texpectedURL := testhelpers.ExpectURL(false, public.URL+recovery.RouteSubmitFlow, conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), expectedURL, \"%+v\\n\\t%s\", res.Request, body)\n\n\t\t\t\t\tbody = checkRecovery(t, client, RecoveryClientTypeBrowser, email, body)\n\n\t\t\t\t\trequire.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\t\tsettingsId := gjson.Get(body, \"id\").String()\n\n\t\t\t\t\tsf, err := reg.SettingsFlowPersister().GetSettingsFlow(ctx, uuid.Must(uuid.FromString(settingsId)))\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tu, err := url.Parse(public.URL)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, client.Jar.Cookies(u), 2)\n\t\t\t\t\tfound := false\n\t\t\t\t\tfor _, cookie := range client.Jar.Cookies(u) {\n\t\t\t\t\t\tif cookie.Name == \"ory_kratos_session\" {\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\trequire.True(t, found)\n\n\t\t\t\t\trequire.Equal(t, tc.returnTo, sf.ReturnTo)\n\t\t\t\t\tres, err = client.Get(public.URL + session.RouteWhoami)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\t\tif tc.expectedAAL == \"aal2\" {\n\t\t\t\t\t\trequire.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\t\t\trequire.Equalf(t, session.NewErrAALNotSatisfied(\"\").Reason(), gjson.Get(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\t\trequire.Equalf(t, \"session_aal2_required\", gjson.Get(body, \"error.id\").String(), \"%s\", body)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\t\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads after submission\", func(t *testing.T) {\n\t\tbody := expectSuccessfulRecovery(t, nil, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", \"test@ory.sh\")\n\t\t})\n\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes\").String()), []string{\"0.attributes.value\"})\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetRecoveryFlow(t, c, public)\n\n\t\ttesthelpers.SnapshotTExcept(t, rs.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\tassert.EqualValues(t, public.URL+recovery.RouteSubmitFlow+\"?flow=\"+rs.Id, rs.Ui.Action)\n\t\tassert.Empty(t, rs.Ui.Messages)\n\t})\n\n\tt.Run(\"description=should require an email to be sent\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+string(flowType), func(t *testing.T) {\n\t\t\t\tbody := expectValidationError(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\tv.Del(\"email\")\n\t\t\t\t})\n\t\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, \"Property email is missing.\",\n\t\t\t\t\tgjson.Get(body, \"ui.nodes.#(attributes.name==email).messages.0.text\").String(),\n\t\t\t\t\t\"%s\", body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should require a valid email to be sent\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tfor _, email := range []string{\"\\\\\", \"asdf\", \"...\", \"aiacobelli.sec@gmail.com,alejandro.iacobelli@mercadolibre.com\"} {\n\t\t\t\tt.Run(\"type=\"+string(flowType), func(t *testing.T) {\n\t\t\t\t\tresponseJSON := expectValidationError(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"email\", email)\n\t\t\t\t\t})\n\t\t\t\t\tactiveMethod := gjson.Get(responseJSON, \"active\").String()\n\t\t\t\t\tassert.EqualValues(t, node.CodeGroup, activeMethod, \"expected method to be %s got %s\", node.CodeGroup, activeMethod)\n\t\t\t\t\tactualMessage := gjson.Get(responseJSON, \"ui.nodes.#(attributes.name==email).messages.0.text\").String()\n\t\t\t\t\tassert.EqualValues(t, \"Enter a valid email address\", actualMessage, \"%s\", responseJSON)\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"description=should try to submit the form while authenticated\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+string(flowType), func(t *testing.T) {\n\t\t\t\tisSPA := flowType == \"spa\"\n\t\t\t\tisAPI := flowType == \"api\"\n\t\t\t\tclient := testhelpers.NewDebugClient(t)\n\t\t\t\tif !isAPI {\n\t\t\t\t\tclient = testhelpers.NewClientWithCookies(t)\n\t\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t\t}\n\n\t\t\t\tvar f *kratos.RecoveryFlow\n\t\t\t\tif isAPI {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaAPI(t, client, public)\n\t\t\t\t} else {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaBrowser(t, client, isSPA, public, nil)\n\t\t\t\t}\n\t\t\t\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil).WithContext(contextx.WithConfigValue(ctx, config.ViperKeySessionLifespan, time.Hour))\n\t\t\t\tsession, err := testhelpers.NewActiveSession(req,\n\t\t\t\t\treg,\n\t\t\t\t\t&identity.Identity{ID: x.NewUUID(), State: identity.StateActive, NID: x.NewUUID()},\n\t\t\t\t\ttime.Now(),\n\t\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\t\tidentity.AuthenticatorAssuranceLevel1,\n\t\t\t\t)\n\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// Add the authentication to the request\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(testhelpers.NewAuthorizedTransport(ctx, t, reg, session), t).RoundTripper\n\n\t\t\t\tv := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tv.Set(\"email\", \"some-email@example.org\")\n\t\t\t\tv.Set(\"method\", \"code\")\n\n\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, isAPI || isSPA, f, client, testhelpers.EncodeFormAsJSON(t, isAPI || isSPA, v))\n\n\t\t\t\tif isAPI || isSPA {\n\t\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), recovery.RouteSubmitFlow, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, recovery.ErrAlreadyLoggedIn, json.RawMessage(gjson.Get(body, \"error\").Raw), nil)\n\t\t\t\t} else {\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover account that does not exist\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, true)\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, false)\n\t\t})\n\n\t\tcheck := func(t *testing.T, c *http.Client, flowType ClientType, email string) {\n\t\t\twithValues := func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}\n\t\t\tbody := submitRecovery(t, c, flowType, withValues, http.StatusOK)\n\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code).attributes.value\").String(), \"%s\", body)\n\t\t\tassertx.EqualAsJSON(t, text.NewRecoveryEmailWithCodeSent(), json.RawMessage(gjson.Get(body, \"ui.messages.0\").Raw))\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Account access attempted\")\n\t\t\tassert.Contains(t, message.Body, \"If this was you, check if you signed up using a different address.\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\temail := \"recover_browser@ory.sh\"\n\t\t\tc := browserHttpClient(t)\n\t\t\tcheck(t, c, RecoveryClientTypeBrowser, email)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\temail := \"recover_spa@ory.sh\"\n\t\t\tc := spaHttpClient(t)\n\t\t\tcheck(t, c, RecoveryClientTypeSPA, email)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\temail := \"recover_api@ory.sh\"\n\t\t\tc := apiHttpClient(t)\n\t\t\tcheck(t, c, RecoveryClientTypeAPI, email)\n\t\t})\n\t})\n\n\tt.Run(\"description=should not be able to recover an inactive account\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+string(flowType.ClientType), func(t *testing.T) {\n\t\t\t\temail := \"recoverinactive_\" + string(flowType.ClientType) + \"@ory.sh\"\n\t\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", email)\n\t\t\t\t}\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecovery(t, cl, flowType.ClientType, values, http.StatusOK)\n\t\t\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, email)\n\t\t\t\tassert.NoError(t, err)\n\n\t\t\t\temailText := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Use code\")\n\t\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, emailText, 1)\n\n\t\t\t\t// Deactivate the identity\n\t\t\t\trequire.NoError(t, reg.Persister().GetConnection(context.Background()).RawQuery(\"UPDATE identities SET state=? WHERE id = ?\", identity.StateInactive, addr.IdentityID).Exec())\n\n\t\t\t\tif flowType.ClientType == RecoveryClientTypeAPI || flowType.ClientType == RecoveryClientTypeSPA {\n\t\t\t\t\tbody = submitRecoveryCode(t, cl, body, flowType.ClientType, recoveryCode, http.StatusUnauthorized)\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", addr.IdentityID), json.RawMessage(gjson.Get(body, \"error\").Raw), \"%s\", body)\n\t\t\t\t} else {\n\t\t\t\t\tbody = submitRecoveryCode(t, cl, body, flowType.ClientType, recoveryCode, http.StatusOK)\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", addr.IdentityID), json.RawMessage(body), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should recover and invalidate all other sessions if hook is set\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"revoke_active_sessions\"}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t})\n\n\t\temail := testhelpers.RandomEmail()\n\t\tid := createIdentityToRecover(t, reg, email)\n\n\t\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil)\n\t\tsess, err := testhelpers.NewActiveSession(req, reg, id, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), sess))\n\n\t\tactualSession, err := reg.SessionPersister().GetSession(context.Background(), sess.ID, session.ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, actualSession.IsActive())\n\n\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\tactual := expectSuccessfulRecovery(t, cl, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", email)\n\t\t})\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Use code\")\n\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t}\n\n\t\taction := gjson.Get(actual, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\t\tcsrf_token := gjson.Get(actual, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\t\trequire.NotEmpty(t, csrf_token)\n\n\t\tsubmitRecoveryCode(t, cl, actual, RecoveryClientTypeBrowser, recoveryCode, http.StatusSeeOther)\n\n\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\tactualSession, err = reg.SessionPersister().GetSession(context.Background(), sess.ID, session.ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.False(t, actualSession.IsActive())\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid code more than 5 times\", func(t *testing.T) {\n\t\temail := testhelpers.RandomEmail()\n\t\tcreateIdentityToRecover(t, reg, email)\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody := submitRecovery(t, c, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", email)\n\t\t}, http.StatusOK)\n\t\tinitialFlowId := gjson.Get(body, \"id\")\n\n\t\tfor submitTry := 0; submitTry < 5; submitTry++ {\n\t\t\tbody := submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, \"12312312\", http.StatusOK)\n\n\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t\t}\n\n\t\t// submit an invalid code for the 6th time\n\t\tbody = submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, \"12312312\", http.StatusOK)\n\n\t\trequire.Len(t, gjson.Get(body, \"ui.messages\").Array(), 1)\n\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t// check that a new flow has been created\n\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==email)\").Exists())\n\t})\n\n\tt.Run(\"description=should be able to recover after using invalid code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\t_ = createIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tactual := submitRecovery(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\t\t\tform := withCSRFToken(t, testCase.ClientType, actual, url.Values{\n\t\t\t\t\t\"code\": {\"12312312\"},\n\t\t\t\t})\n\n\t\t\t\taction := gjson.Get(actual, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\n\t\t\t\tres, err := c.Post(action, testCase.FormContentType, bytes.NewBufferString(form))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\tflowId := gjson.Get(actual, \"id\").String()\n\t\t\t\trequire.NotEmpty(t, flowId)\n\n\t\t\t\trs, res, err := testhelpers.\n\t\t\t\t\tNewSDKCustomClient(public, c).\n\t\t\t\t\tFrontendAPI.GetRecoveryFlow(context.Background()).\n\t\t\t\t\tId(flowId).\n\t\t\t\t\tExecute()\n\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\trequire.NotEmpty(t, body)\n\n\t\t\t\trequire.Len(t, rs.Ui.Messages, 1)\n\t\t\t\tassert.Equal(t, \"The recovery code is invalid or has already been used. Please try again.\", rs.Ui.Messages[0].Text)\n\n\t\t\t\tform = withCSRFToken(t, testCase.ClientType, actual, url.Values{\n\t\t\t\t\t\"code\": {recoveryCode},\n\t\t\t\t})\n\t\t\t\t// Now submit the correct code\n\t\t\t\tres, err = c.Post(action, testCase.FormContentType, bytes.NewBufferString(form))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\tjson := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\tassert.Len(t, gjson.GetBytes(json, \"ui.messages\").Array(), 1)\n\t\t\t\t\tassert.Contains(t, gjson.GetBytes(json, \"ui.messages.0.text\").String(), \"You successfully recovered your account.\")\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tassert.Equal(t, http.StatusUnprocessableEntity, res.StatusCode)\n\n\t\t\t\t\tjson := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\tassert.Equal(t, gjson.GetBytes(json, \"error.id\").String(), \"browser_location_change_required\")\n\t\t\t\t\tassert.Contains(t, gjson.GetBytes(json, \"redirect_browser_to\").String(), \"settings-ts?\")\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid code\", func(t *testing.T) {\n\t\temail := \"recoverme+invalid_code@ory.sh\"\n\t\tcreateIdentityToRecover(t, reg, email)\n\t\tc := testhelpers.NewClientWithCookies(t)\n\n\t\tbody := submitRecovery(t, c, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", email)\n\t\t}, http.StatusOK)\n\n\t\tbody = submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, \"12312312\", http.StatusOK)\n\n\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t})\n\n\tt.Run(\"description=should not be able to submit recover address after flow expired\", func(t *testing.T) {\n\t\trecoveryEmail := \"recoverme5@ory.sh\"\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Millisecond*200)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Minute)\n\t\t})\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetRecoveryFlow(t, c, public)\n\n\t\ttime.Sleep(time.Millisecond * 201)\n\n\t\tres, err := c.PostForm(rs.Ui.Action, url.Values{\"email\": {recoveryEmail}})\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.NotContains(t, res.Request.URL.String(), \"flow=\"+rs.Id)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowRecoveryUI(ctx).String())\n\n\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, recoveryEmail)\n\t\tassert.NoError(t, err)\n\t\tassert.False(t, addr.Verified)\n\t\tassert.Nil(t, addr.VerifiedAt)\n\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\t})\n\n\tt.Run(\"description=should not be able to submit code after flow expired\", func(t *testing.T) {\n\t\trecoveryEmail := \"recoverme6@ory.sh\"\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Millisecond*200)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Minute)\n\t\t})\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\n\t\tbody := expectSuccessfulRecovery(t, c, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t})\n\n\t\tinitialFlowId := gjson.Get(body, \"id\")\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\tassert.Contains(t, message.Body, \"Recover access to your account by entering\")\n\n\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\ttime.Sleep(time.Millisecond * 201)\n\n\t\tbody = submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, recoveryCode, http.StatusOK)\n\n\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\tassert.Regexpf(t, regexp.MustCompile(`The recovery flow expired 0\\.0\\d minutes ago, please try again\\.`), gjson.Get(body, \"ui.messages.0.text\").Str, \"%s\", body)\n\n\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, recoveryEmail)\n\t\trequire.NoError(t, err)\n\t\tassert.False(t, addr.Verified)\n\t\tassert.Nil(t, addr.VerifiedAt)\n\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\t})\n\n\tt.Run(\"description=should not break ui if empty code is submitted\", func(t *testing.T) {\n\t\trecoveryEmail := \"recoverme7@ory.sh\"\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody := expectSuccessfulRecovery(t, c, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t})\n\n\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\n\t\tbody = submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, \"\", http.StatusOK)\n\n\t\tassert.NotContains(t, gjson.Get(body, \"ui.nodes\").String(), \"Property email is missing.\")\n\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t})\n\n\tt.Run(\"description=should be able to re-send the recovery code\", func(t *testing.T) {\n\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody := expectSuccessfulRecovery(t, c, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t})\n\n\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\tbody = resendRecoveryCode(t, c, body, RecoveryClientTypeBrowser, http.StatusOK)\n\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\tsubmitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, recoveryCode, http.StatusOK)\n\t})\n\n\tt.Run(\"description=should not be able to use first code after re-sending email\", func(t *testing.T) {\n\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody := expectSuccessfulRecovery(t, c, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t})\n\n\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\tmessage1 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\trecoveryCode1 := testhelpers.CourierExpectCodeInMessage(t, message1, 1)\n\n\t\tbody = resendRecoveryCode(t, c, body, RecoveryClientTypeBrowser, http.StatusOK)\n\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\tmessage2 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\trecoveryCode2 := testhelpers.CourierExpectCodeInMessage(t, message2, 1)\n\n\t\tbody = submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, recoveryCode1, http.StatusOK)\n\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\n\t\t// For good measure, check that the second code works!\n\t\tbody = submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, recoveryCode2, http.StatusOK)\n\t\ttesthelpers.AssertMessage(t, body, \"You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 60.00 minutes.\")\n\t})\n\n\tt.Run(\"description=should not show outdated validation message if newer message appears #2799\", func(t *testing.T) {\n\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody := expectSuccessfulRecovery(t, c, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t})\n\n\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\tbody = submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, \"12312312\", http.StatusOK) // Now send a wrong code that triggers \"global\" validation error\n\n\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code).messages\").Array())\n\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t})\n\n\tt.Run(\"description=should recover if post recovery hook is successful\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t})\n\n\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\tbody := expectSuccessfulRecovery(t, cl, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t})\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t}\n\n\t\tsubmitRecoveryCode(t, cl, body, RecoveryClientTypeBrowser, recoveryCode, http.StatusSeeOther)\n\n\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\t})\n\n\tt.Run(\"description=should not be able to recover if post recovery hook fails\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecutePostRecoveryHook\": \"err\"}`)}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t})\n\n\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\tbody := expectSuccessfulRecovery(t, cl, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t})\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t}\n\n\t\tinitialFlowId := gjson.Get(body, \"id\")\n\t\tbody = submitRecoveryCode(t, cl, body, RecoveryClientTypeBrowser, recoveryCode, http.StatusSeeOther)\n\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1) // No session\n\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t})\n}\n\nfunc TestRecovery_WithContinueWith(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(defaultConfig),\n\t\tconfigx.WithValue(config.ViperKeyUseContinueWithTransitions, true),\n\t)\n\n\t_ = testhelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublic, _, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\tsubmitRecoveryForm := func(t *testing.T, client *http.Client, clientType ClientType, values func(url.Values), code int) string {\n\t\tisSPA := clientType == RecoveryClientTypeSPA\n\t\tisAPI := clientType == RecoveryClientTypeAPI\n\t\tif client == nil {\n\t\t\tclient = testhelpers.NewDebugClient(t)\n\t\t\tif !isAPI {\n\t\t\t\tclient = testhelpers.NewClientWithCookies(t)\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t}\n\t\t}\n\n\t\texpectedUrl := testhelpers.ExpectURL(isAPI || isSPA, public.URL+recovery.RouteSubmitFlow, conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\treturn testhelpers.SubmitRecoveryForm(t, isAPI, isSPA, client, public, values, code, expectedUrl)\n\t}\n\n\tsubmitRecoveryCode := func(t *testing.T, client *http.Client, flow string, flowType ClientType, recoveryCode string, statusCode int) string {\n\t\tt.Helper()\n\t\taction := gjson.Get(flow, \"ui.action\").String()\n\t\tassert.NotEmpty(t, action)\n\n\t\tvalues := withCSRFToken(t, flowType, flow, url.Values{\n\t\t\t\"code\":   {recoveryCode},\n\t\t\t\"method\": {\"code\"},\n\t\t})\n\n\t\tcontentType := \"application/json\"\n\t\tif flowType == RecoveryClientTypeBrowser {\n\t\t\tcontentType = \"application/x-www-form-urlencoded\"\n\t\t}\n\n\t\tres, err := client.Post(action, contentType, bytes.NewBufferString(values))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, statusCode, res.StatusCode)\n\n\t\treturn string(ioutilx.MustReadAll(res.Body))\n\t}\n\n\tresendRecoveryCode := func(t *testing.T, client *http.Client, flow string, flowType ClientType, statusCode int) string {\n\t\taction := gjson.Get(flow, \"ui.action\").String()\n\t\tassert.NotEmpty(t, action)\n\n\t\temail := gjson.Get(flow, \"ui.nodes.#(attributes.name==email).attributes.value\").String()\n\n\t\tvalues := withCSRFToken(t, flowType, flow, url.Values{\n\t\t\t\"method\": {\"code\"},\n\t\t\t\"email\":  {email},\n\t\t})\n\n\t\tcontentType := \"application/json\"\n\t\tif flowType == RecoveryClientTypeBrowser {\n\t\t\tcontentType = \"application/x-www-form-urlencoded\"\n\t\t}\n\n\t\tres, err := client.Post(action, contentType, bytes.NewBufferString(values))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, statusCode, res.StatusCode)\n\n\t\treturn string(ioutilx.MustReadAll(res.Body))\n\t}\n\n\texpectValidationError := func(t *testing.T, hc *http.Client, flowType ClientType, values func(url.Values)) string {\n\t\tcode := testhelpers.ExpectStatusCode(flowType == RecoveryClientTypeAPI || flowType == RecoveryClientTypeSPA, http.StatusBadRequest, http.StatusOK)\n\t\treturn submitRecoveryForm(t, hc, flowType, values, code)\n\t}\n\n\texpectVerfiableAddressStatus := func(t *testing.T, email string, status identity.VerifiableAddressStatus) {\n\t\taddr, err := reg.IdentityPool().\n\t\t\tFindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, email)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, status, addr.Status, \"verifiable address %s was not %s. instead %s\", email, status, addr.Status)\n\t}\n\n\tsubmitCodeAndExpectRedirectToSettings := func(t *testing.T, c *http.Client, clientType ClientType, recoveryCode, body string) {\n\t\tt.Helper()\n\t\tswitch clientType {\n\t\tcase RecoveryClientTypeBrowser:\n\t\t\tbody = submitRecoveryCode(t, c, body, clientType, recoveryCode, http.StatusOK)\n\t\t\trequire.Len(t, c.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(c.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\t\t\trequire.Contains(t, body, \"You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 60.00 minutes.\")\n\t\tcase RecoveryClientTypeSPA:\n\t\t\tbody = submitRecoveryCode(t, c, body, clientType, recoveryCode, http.StatusOK)\n\t\t\t// assert.Equal(t, \"browser_location_change_required\", gjson.Get(body, \"error.id\").String())\n\t\t\trequire.Len(t, c.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(c.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\tcase RecoveryClientTypeAPI:\n\t\t\tbody = submitRecoveryCode(t, c, body, clientType, recoveryCode, http.StatusOK)\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String(), \"%s\", body)\n\t\t}\n\t}\n\n\tt.Run(\"description=should recover an account\", func(t *testing.T) {\n\t\tcheckRecovery := func(t *testing.T, client *http.Client, flowType ClientType, recoveryEmail, recoverySubmissionResponse string) string {\n\t\t\texpectVerfiableAddressStatus(t, recoveryEmail, identity.VerifiableAddressStatusPending)\n\n\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(recoverySubmissionResponse, \"active\").String(), \"%s\", recoverySubmissionResponse)\n\t\t\tassert.True(t, gjson.Get(recoverySubmissionResponse, \"ui.nodes.#(attributes.name==code)\").Exists(), \"%s\", recoverySubmissionResponse)\n\t\t\tassert.Len(t, gjson.Get(recoverySubmissionResponse, \"ui.messages\").Array(), 1, \"%s\", recoverySubmissionResponse)\n\t\t\tassertx.EqualAsJSON(t, text.NewRecoveryEmailWithCodeSent(), json.RawMessage(gjson.Get(recoverySubmissionResponse, \"ui.messages.0\").Raw))\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\tassert.Contains(t, message.Body, \"Recover access to your account by entering\")\n\n\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\tassert.NotEmpty(t, recoveryCode)\n\n\t\t\t// statusCode := testhelpers.ExpectStatusCode(flowType == RecoveryClientTypeSPA, http.StatusUnprocessableEntity, http.StatusOK)\n\t\t\treturn submitRecoveryCode(t, client, recoverySubmissionResponse, flowType, recoveryCode, http.StatusOK)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\temail := testhelpers.RandomEmail()\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\trecoverySubmissionResponse := submitRecoveryForm(t, client, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}, http.StatusOK)\n\t\t\tbody := checkRecovery(t, client, RecoveryClientTypeBrowser, email, recoverySubmissionResponse)\n\n\t\t\tassert.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\tres, err := client.Get(public.URL + session.RouteWhoami)\n\t\t\trequire.NoError(t, err)\n\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\temail := testhelpers.RandomEmail()\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\trecoverySubmissionResponse := submitRecoveryForm(t, client, RecoveryClientTypeSPA, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}, http.StatusOK)\n\t\t\tbody := checkRecovery(t, client, RecoveryClientTypeSPA, email, recoverySubmissionResponse)\n\t\t\tassert.Equal(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\tassert.Len(t, gjson.Get(body, \"continue_with\").Array(), 1)\n\t\t\tsfId := gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow.id\").String()\n\t\t\tassert.NotEmpty(t, uuid.Must(uuid.FromString(sfId)))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tclient := &http.Client{}\n\t\t\temail := testhelpers.RandomEmail()\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\trecoverySubmissionResponse := submitRecoveryForm(t, client, RecoveryClientTypeAPI, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}, http.StatusOK)\n\t\t\tbody := checkRecovery(t, client, RecoveryClientTypeAPI, email, recoverySubmissionResponse)\n\t\t\tassert.Equal(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\tassert.Len(t, gjson.Get(body, \"continue_with\").Array(), 2)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String())\n\t\t\tsfId := gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow.id\").String()\n\t\t\tassert.NotEmpty(t, uuid.Must(uuid.FromString(sfId)))\n\t\t})\n\n\t\tt.Run(\"description=should return browser to return url\", func(t *testing.T) {\n\t\t\treturnTo := public.URL + \"/return-to\"\n\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdesc        string\n\t\t\t\treturnTo    string\n\t\t\t\tf           func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow\n\t\t\t\texpectedAAL string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from recovery flow\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from config\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, returnTo)\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, \"\")\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"no return to\",\n\t\t\t\t\treturnTo: \"\",\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to with an account that has 2fa enabled\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, id *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPDisplayName, \"Kratos\")\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPID, \"ory.sh\")\n\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t})\n\t\t\t\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeWebAuthn.String(), true)\n\n\t\t\t\t\t\tid.SetCredentials(identity.CredentialsTypeWebAuthn, identity.Credentials{\n\t\t\t\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\t\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":false, \"display_name\":\"test\"}]}`),\n\t\t\t\t\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Update(ctx, id, identity.ManagerAllowWriteProtectedTraits))\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t\texpectedAAL: \"aal2\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\ti := createIdentityToRecover(t, reg, email)\n\n\t\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t\t\tf := tc.f(t, client, i)\n\n\t\t\t\t\tformPayload := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\t\tformPayload.Set(\"email\", email)\n\n\t\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, false, f, client, formPayload.Encode())\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\texpectedURL := testhelpers.ExpectURL(false, public.URL+recovery.RouteSubmitFlow, conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), expectedURL, \"%+v\\n\\t%s\", res.Request, body)\n\n\t\t\t\t\tbody = checkRecovery(t, client, RecoveryClientTypeBrowser, email, body)\n\n\t\t\t\t\trequire.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\t\tsettingsId := gjson.Get(body, \"id\").String()\n\n\t\t\t\t\tsf, err := reg.SettingsFlowPersister().GetSettingsFlow(ctx, uuid.Must(uuid.FromString(settingsId)))\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tu, err := url.Parse(public.URL)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, client.Jar.Cookies(u), 2)\n\t\t\t\t\tfound := false\n\t\t\t\t\tfor _, cookie := range client.Jar.Cookies(u) {\n\t\t\t\t\t\tif cookie.Name == \"ory_kratos_session\" {\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\trequire.True(t, found)\n\n\t\t\t\t\trequire.Equal(t, tc.returnTo, sf.ReturnTo)\n\t\t\t\t\tres, err = client.Get(public.URL + session.RouteWhoami)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\t\tif tc.expectedAAL == \"aal2\" {\n\t\t\t\t\t\trequire.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\t\t\trequire.Equalf(t, session.NewErrAALNotSatisfied(\"\").Reason(), gjson.Get(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\t\trequire.Equalf(t, \"session_aal2_required\", gjson.Get(body, \"error.id\").String(), \"%s\", body)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\t\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads after submission\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tbody := submitRecoveryForm(t, testCase.GetClient(t), testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", \"test@ory.sh\")\n\t\t\t\t}, http.StatusOK)\n\t\t\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes\").String()), []string{\"0.attributes.value\"})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\trs := testhelpers.GetRecoveryFlowForType(t, c, public, testCase.FlowType)\n\n\t\t\t\ttesthelpers.SnapshotTExcept(t, rs.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\t\t\tassert.EqualValues(t, public.URL+recovery.RouteSubmitFlow+\"?flow=\"+rs.Id, rs.Ui.Action)\n\t\t\t\tassert.Empty(t, rs.Ui.Messages)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should require an email to be sent\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+flowType.String(), func(t *testing.T) {\n\t\t\t\tbody := expectValidationError(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\tv.Del(\"email\")\n\t\t\t\t})\n\t\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, \"Property email is missing.\",\n\t\t\t\t\tgjson.Get(body, \"ui.nodes.#(attributes.name==email).messages.0.text\").String(),\n\t\t\t\t\t\"%s\", body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should require a valid email to be sent\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+flowType.String(), func(t *testing.T) {\n\t\t\t\tfor _, email := range []string{\"\\\\\", \"asdf\", \"...\", \"aiacobelli.sec@gmail.com,alejandro.iacobelli@mercadolibre.com\"} {\n\t\t\t\t\tresponseJSON := expectValidationError(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"email\", email)\n\t\t\t\t\t})\n\t\t\t\t\tactiveMethod := gjson.Get(responseJSON, \"active\").String()\n\t\t\t\t\tassert.EqualValues(t, node.CodeGroup, activeMethod, \"expected method to be %s got %s\", node.CodeGroup, activeMethod)\n\t\t\t\t\tactualMessage := gjson.Get(responseJSON, \"ui.nodes.#(attributes.name==email).messages.0.text\").String()\n\t\t\t\t\tassert.EqualValues(t, \"Enter a valid email address\", actualMessage, \"%s\", responseJSON)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should try to submit the form while authenticated\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tisSPA := testCase.ClientType == \"spa\"\n\t\t\t\tisAPI := testCase.ClientType == \"api\"\n\t\t\t\tclient := testCase.GetClient(t)\n\n\t\t\t\tvar f *kratos.RecoveryFlow\n\t\t\t\tif isAPI {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaAPI(t, client, public)\n\t\t\t\t} else {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaBrowser(t, client, isSPA, public, nil)\n\t\t\t\t}\n\t\t\t\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil)\n\t\t\t\treq = req.WithContext(contextx.WithConfigValue(ctx, config.ViperKeySessionLifespan, time.Hour))\n\n\t\t\t\tsession, err := testhelpers.NewActiveSession(\n\t\t\t\t\treq,\n\t\t\t\t\treg,\n\t\t\t\t\t&identity.Identity{ID: x.NewUUID(), State: identity.StateActive, NID: x.NewUUID()},\n\t\t\t\t\ttime.Now(),\n\t\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\t\tidentity.AuthenticatorAssuranceLevel1,\n\t\t\t\t)\n\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// Add the authentication to the request\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(testhelpers.NewAuthorizedTransport(ctx, t, reg, session), t).RoundTripper\n\n\t\t\t\tv := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tv.Set(\"email\", \"some-email@example.org\")\n\t\t\t\tv.Set(\"method\", \"code\")\n\n\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, isAPI || isSPA, f, client, testhelpers.EncodeFormAsJSON(t, isAPI || isSPA, v))\n\n\t\t\t\tif isAPI || isSPA {\n\t\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), recovery.RouteSubmitFlow, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, recovery.ErrAlreadyLoggedIn, json.RawMessage(gjson.Get(body, \"error\").Raw), nil)\n\t\t\t\t} else {\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover account that does not exist\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, true)\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, false)\n\t\t})\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\temail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\twithValues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", email)\n\t\t\t\t}\n\t\t\t\tbody := submitRecoveryForm(t, c, testCase.ClientType, withValues, http.StatusOK)\n\t\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code).attributes.value\").String(), \"%s\", body)\n\t\t\t\tassertx.EqualAsJSON(t, text.NewRecoveryEmailWithCodeSent(), json.RawMessage(gjson.Get(body, \"ui.messages.0\").Raw))\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Account access attempted\")\n\t\t\t\tassert.Contains(t, message.Body, \"If this was you, check if you signed up using a different address.\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover an inactive account\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\temail := \"recoverinactive_\" + testCase.ClientType.String() + \"@ory.sh\"\n\t\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", email)\n\t\t\t\t}\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecoveryForm(t, cl, testCase.ClientType, values, http.StatusOK)\n\t\t\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, email)\n\t\t\t\tassert.NoError(t, err)\n\n\t\t\t\temailText := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Use code\")\n\t\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, emailText, 1)\n\n\t\t\t\t// Deactivate the identity\n\t\t\t\trequire.NoError(t, reg.Persister().GetConnection(context.Background()).RawQuery(\"UPDATE identities SET state=? WHERE id = ?\", identity.StateInactive, addr.IdentityID).Exec())\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\tfallthrough\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tbody = submitRecoveryCode(t, cl, body, testCase.ClientType, recoveryCode, http.StatusUnauthorized)\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", addr.IdentityID), json.RawMessage(gjson.Get(body, \"error\").Raw), \"%s\", body)\n\t\t\t\tdefault:\n\t\t\t\t\tbody = submitRecoveryCode(t, cl, body, testCase.ClientType, recoveryCode, http.StatusOK)\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", addr.IdentityID), json.RawMessage(body), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should recover and invalidate all other sessions if hook is set\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"revoke_active_sessions\"}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t})\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\tid := createIdentityToRecover(t, reg, email)\n\n\t\t\t\totherSession, err := testhelpers.NewActiveSession(httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil), reg, id, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, otherSession))\n\n\t\t\t\trefetchedOtherSession, err := reg.SessionPersister().GetSession(ctx, otherSession.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.True(t, refetchedOtherSession.IsActive())\n\n\t\t\t\tcl := testCase.GetClient(t)\n\t\t\t\tactual := submitRecoveryForm(t, cl, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", email)\n\t\t\t\t}, http.StatusOK)\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Use code\")\n\t\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\t\t\tsubmitCodeAndExpectRedirectToSettings(t, cl, testCase.ClientType, recoveryCode, actual)\n\n\t\t\t\trefetchedOtherSession, err = reg.SessionPersister().GetSession(ctx, otherSession.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.False(t, refetchedOtherSession.IsActive())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid code more than 5 times\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\tbody := submitRecoveryForm(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", email)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tinitialFlowId := gjson.Get(body, \"id\")\n\n\t\t\t\tfor submitTry := 0; submitTry < 5; submitTry++ {\n\t\t\t\t\tbody := submitRecoveryCode(t, c, body, testCase.ClientType, \"12312312\", http.StatusOK)\n\n\t\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t\t\t\t}\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\t// submit an invalid code for the 6th time\n\t\t\t\t\tbody = submitRecoveryCode(t, c, body, testCase.ClientType, \"12312312\", http.StatusOK)\n\n\t\t\t\t\trequire.Len(t, gjson.Get(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\t\t// check that a new flow has been created\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==email)\").Exists())\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tfallthrough\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\t// submit an invalid code for the 6th time\n\t\t\t\t\tbody = submitRecoveryCode(t, c, body, testCase.ClientType, \"12312312\", http.StatusBadRequest)\n\n\t\t\t\t\tassert.Equal(t, \"Bad Request\", gjson.Get(body, \"error.status\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\tcontinueWith := gjson.Get(body, \"error.details.continue_with\").Array()\n\t\t\t\t\tassert.Len(t, continueWith, 1, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"show_recovery_ui\", continueWith[0].Get(\"action\").String(), \"%s\", body)\n\t\t\t\t\tflowId := continueWith[0].Get(\"flow.id\").String()\n\t\t\t\t\tassert.NotEmpty(t, flowId, \"%s\", body)\n\t\t\t\t\trequire.NotEqual(t, flowId, initialFlowId, \"%s\", body)\n\n\t\t\t\t\tflow, err := reg.Persister().GetRecoveryFlow(ctx, uuid.Must(uuid.FromString(flowId)))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Len(t, flow.UI.Messages, 1, \"%+v\", flow)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", flow.UI.Messages[0].Text)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should be able to recover after using invalid code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\t_ = createIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tactual := submitRecoveryForm(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\t\t\tform := withCSRFToken(t, testCase.ClientType, actual, url.Values{\n\t\t\t\t\t\"code\": {\"12312312\"},\n\t\t\t\t})\n\n\t\t\t\taction := gjson.Get(actual, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\n\t\t\t\tres, err := c.Post(action, testCase.FormContentType, bytes.NewBufferString(form))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\tflowId := gjson.Get(actual, \"id\").String()\n\t\t\t\trequire.NotEmpty(t, flowId)\n\n\t\t\t\trs, res, err := testhelpers.\n\t\t\t\t\tNewSDKCustomClient(public, c).\n\t\t\t\t\tFrontendAPI.GetRecoveryFlow(context.Background()).\n\t\t\t\t\tId(flowId).\n\t\t\t\t\tExecute()\n\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\trequire.NotEmpty(t, body)\n\n\t\t\t\trequire.Len(t, rs.Ui.Messages, 1)\n\t\t\t\tassert.Equal(t, \"The recovery code is invalid or has already been used. Please try again.\", rs.Ui.Messages[0].Text)\n\n\t\t\t\tform = withCSRFToken(t, testCase.ClientType, actual, url.Values{\n\t\t\t\t\t\"code\": {recoveryCode},\n\t\t\t\t})\n\t\t\t\t// Now submit the correct code\n\t\t\t\tres, err = c.Post(action, testCase.FormContentType, bytes.NewBufferString(form))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\tjson := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\tassert.Len(t, gjson.GetBytes(json, \"ui.messages\").Array(), 1)\n\t\t\t\t\tassert.Contains(t, gjson.GetBytes(json, \"ui.messages.0.text\").String(), \"You successfully recovered your account.\")\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\tjson := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\trequire.Len(t, c.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\t\t\tcookies := spew.Sdump(c.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\t\t\t\trequire.NotEmpty(t, gjson.GetBytes(json, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", json)\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\tjson := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\trequire.NotEmpty(t, gjson.GetBytes(json, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", json)\n\t\t\t\t\trequire.NotEmpty(t, gjson.GetBytes(json, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String(), \"%s\", json)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=does not issue csrf cookie when submitting API flow\", func(t *testing.T) {\n\t\tt.Run(\"type=\"+RecoveryClientTypeAPI.String(), func(t *testing.T) {\n\t\t\tc := new(http.Client)\n\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t_ = createIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\tactual := submitRecoveryForm(t, c, RecoveryClientTypeAPI, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t}, http.StatusOK)\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\t\taction := gjson.Get(actual, \"ui.action\").String()\n\t\t\trequire.NotEmpty(t, action)\n\n\t\t\tflowId := gjson.Get(actual, \"id\").String()\n\t\t\trequire.NotEmpty(t, flowId)\n\n\t\t\tform := withCSRFToken(t, RecoveryClientTypeAPI, actual, url.Values{\n\t\t\t\t\"code\": {recoveryCode},\n\t\t\t})\n\n\t\t\t// Now submit the correct code\n\t\t\tres, err := c.Post(action, \"application/json\", bytes.NewBufferString(form))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tassert.Empty(t, res.Header.Get(\"Set-Cookie\"))\n\n\t\t\tjson := ioutilx.MustReadAll(res.Body)\n\t\t\trequire.NotEmpty(t, gjson.GetBytes(json, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", json)\n\t\t\trequire.NotEmpty(t, gjson.GetBytes(json, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String(), \"%s\", json)\n\t\t})\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryForm(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", email)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tbody = submitRecoveryCode(t, c, body, RecoveryClientTypeBrowser, \"12312312\", http.StatusOK)\n\n\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to submit recover address after flow expired\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Millisecond*100)\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Minute)\n\t\t\t\t})\n\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\tvar rs *kratos.RecoveryFlow\n\t\t\t\tvar res *http.Response\n\t\t\t\tvar err error\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tfallthrough\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\trs = testhelpers.GetRecoveryFlow(t, c, public)\n\t\t\t\t\ttime.Sleep(time.Millisecond * 110)\n\t\t\t\t\tres, err = c.PostForm(rs.Ui.Action, url.Values{\"email\": {recoveryEmail}, \"method\": {\"code\"}})\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\tassert.NotContains(t, res.Request.URL.String(), \"flow=\"+rs.Id)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\trs = testhelpers.InitializeRecoveryFlowViaAPI(t, c, public)\n\t\t\t\t\ttime.Sleep(time.Millisecond * 110)\n\t\t\t\t\tform := testhelpers.EncodeFormAsJSON(t, true, url.Values{\"email\": {recoveryEmail}, \"method\": {\"code\"}})\n\t\t\t\t\tres, err = c.Post(rs.Ui.Action, \"application/json\", bytes.NewBufferString(form))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\t\tassert.Equal(t, http.StatusGone, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"self_service_flow_expired\", gjson.GetBytes(body, \"error.id\").String(), \"%s\", body)\n\t\t\t\t\tcontinueWith := gjson.GetBytes(body, \"error.details.continue_with\").Array()\n\t\t\t\t\tassert.Len(t, continueWith, 1, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"show_recovery_ui\", continueWith[0].Get(\"action\").String(), \"%s\", continueWith)\n\t\t\t\t\tflowId := continueWith[0].Get(\"flow.id\").String()\n\t\t\t\t\trequire.NotEmpty(t, flowId, \"%s\", body)\n\t\t\t\t}\n\n\t\t\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, recoveryEmail)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.False(t, addr.Verified)\n\t\t\t\tassert.Nil(t, addr.VerifiedAt)\n\t\t\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to submit code after flow expired\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Millisecond*200)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Minute)\n\t\t})\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tc := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecoveryForm(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tinitialFlowId := gjson.Get(body, \"id\")\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message.Body, \"Recover access to your account by entering\")\n\n\t\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\t\t\ttime.Sleep(time.Millisecond * 201)\n\n\t\t\t\tif testCase.FlowType == \"browser\" {\n\t\t\t\t\tbody = submitRecoveryCode(t, c, body, testCase.ClientType, recoveryCode, http.StatusOK)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\tassert.Regexpf(t, regexp.MustCompile(`The recovery flow expired 0\\.0\\d minutes ago, please try again\\.`), gjson.Get(body, \"ui.messages.0.text\").Str, \"%s\", body)\n\t\t\t\t} else {\n\t\t\t\t\tbody = submitRecoveryCode(t, c, body, testCase.ClientType, recoveryCode, http.StatusGone)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\t\t\t\t\tassert.Equal(t, \"self_service_flow_expired\", gjson.Get(body, \"error.id\").String())\n\t\t\t\t}\n\n\t\t\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, recoveryEmail)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.False(t, addr.Verified)\n\t\t\t\tassert.Nil(t, addr.VerifiedAt)\n\t\t\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not break ui if empty code is submitted\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\tbody := submitRecoveryForm(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\n\t\t\t\tbody = submitRecoveryCode(t, c, body, testCase.ClientType, \"\", http.StatusOK)\n\n\t\t\t\tassert.NotContains(t, gjson.Get(body, \"ui.nodes\").String(), \"Property email is missing.\")\n\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should be able to resend the recovery code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\tbody := submitRecoveryForm(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\t\t\tbody = resendRecoveryCode(t, c, body, testCase.ClientType, http.StatusOK)\n\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\t\t\tsubmitCodeAndExpectRedirectToSettings(t, c, testCase.ClientType, recoveryCode, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to use first code after re-sending email\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\tbody := submitRecoveryForm(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\t\t\tmessage1 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\t\trecoveryCode1 := testhelpers.CourierExpectCodeInMessage(t, message1, 1)\n\n\t\t\t\tbody = resendRecoveryCode(t, c, body, testCase.ClientType, http.StatusOK)\n\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\t\t\tmessage2 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\t\trecoveryCode2 := testhelpers.CourierExpectCodeInMessage(t, message2, 1)\n\n\t\t\t\tbody = submitRecoveryCode(t, c, body, testCase.ClientType, recoveryCode1, http.StatusOK)\n\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\n\t\t\t\tsubmitCodeAndExpectRedirectToSettings(t, c, testCase.ClientType, recoveryCode2, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not show outdated validation message if newer message appears #2799\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\tbody := submitRecoveryForm(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\t\t\tbody = submitRecoveryCode(t, c, body, testCase.ClientType, \"12312312\", http.StatusOK) // Now send a wrong code that triggers \"global\" validation error\n\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code).messages\").Array())\n\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should recover if post recovery hook is successful\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t\t\t})\n\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tcl := testCase.GetClient(t)\n\t\t\t\tbody := submitRecoveryForm(t, cl, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\t\t\tsubmitCodeAndExpectRedirectToSettings(t, cl, testCase.ClientType, recoveryCode, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover if post recovery hook fails\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecutePostRecoveryHook\": \"err\"}`)}})\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t\t\t})\n\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\t\t\tbody := submitRecoveryForm(t, cl, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\t\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\n\t\t\t\tinitialFlowId := gjson.Get(body, \"id\")\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tbody = submitRecoveryCode(t, cl, body, testCase.ClientType, recoveryCode, http.StatusSeeOther)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tbody = submitRecoveryCode(t, cl, body, testCase.ClientType, recoveryCode, http.StatusBadRequest)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\tbody = submitRecoveryCode(t, cl, body, testCase.ClientType, recoveryCode, http.StatusBadRequest)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\t\t\t\t\trequire.Equal(t, \"err\", gjson.Get(body, \"error.message\").String(), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\n// Recovery V2 is only tested with `ContinueWith`.\nfunc TestRecovery_V2_WithContinueWith_OneAddress_Email(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(defaultConfig),\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeyUseContinueWithTransitions: true,\n\t\t\tconfig.ViperKeyChooseRecoveryAddress:      true,\n\t\t}),\n\t)\n\n\t_ = testhelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublic, _, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\tsubmitRecoveryFormInitial := func(t *testing.T, client *http.Client, flowType ClientType, values func(url.Values), code int) string {\n\t\tisSPA := flowType == RecoveryClientTypeSPA\n\t\tisAPI := flowType == RecoveryClientTypeAPI\n\t\tif client == nil {\n\t\t\tclient = testhelpers.NewDebugClient(t)\n\t\t\tif !isAPI {\n\t\t\t\tclient = testhelpers.NewClientWithCookies(t)\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t}\n\t\t}\n\n\t\texpectedUrl := testhelpers.ExpectURL(isAPI || isSPA, public.URL+recovery.RouteSubmitFlow, conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\treturn testhelpers.SubmitRecoveryForm(t, isAPI, isSPA, client, public, values, code, expectedUrl)\n\t}\n\n\tsubmitRecoveryFormSubsequent := func(t *testing.T, client *http.Client, flow string, flowType ClientType, urlValuesFn func(url.Values), statusCode int) string {\n\t\tt.Helper()\n\t\taction := gjson.Get(flow, \"ui.action\").String()\n\t\tassert.NotEmpty(t, action)\n\n\t\turlValues := url.Values{}\n\t\turlValuesFn(urlValues)\n\t\tvalues := withCSRFToken(t, flowType, flow, urlValues)\n\n\t\tcontentType := \"application/json\"\n\t\tif flowType == RecoveryClientTypeBrowser {\n\t\t\tcontentType = \"application/x-www-form-urlencoded\"\n\t\t}\n\n\t\tres, err := client.Post(action, contentType, bytes.NewBufferString(values))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, statusCode, res.StatusCode)\n\n\t\treturn string(ioutilx.MustReadAll(res.Body))\n\t}\n\n\texpectVerifiableAddressStatus := func(t *testing.T, email string, status identity.VerifiableAddressStatus) {\n\t\taddr, err := reg.IdentityPool().\n\t\t\tFindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, email)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, status, addr.Status, \"verifiable address %s was not %s. instead %s\", email, status, addr.Status)\n\t}\n\n\tcheckRecoveryScreenAskForCode := func(t *testing.T, chosenRecoveryConfirmAddress, recoverySubmissionResponse string) {\n\t\texpectVerifiableAddressStatus(t, chosenRecoveryConfirmAddress, identity.VerifiableAddressStatusPending)\n\n\t\tassert.True(t, gjson.Get(recoverySubmissionResponse, \"ui.nodes.#(attributes.name==code)\").Exists(), \"%s\", recoverySubmissionResponse)\n\t\tassert.Len(t, gjson.Get(recoverySubmissionResponse, \"ui.messages\").Array(), 1, \"%s\", recoverySubmissionResponse)\n\t\tassertx.EqualAsJSON(t, text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(chosenRecoveryConfirmAddress)), json.RawMessage(gjson.Get(recoverySubmissionResponse, \"ui.messages.0\").Raw))\n\t}\n\n\textractCodeFromCourierAndSubmit := func(t *testing.T, client *http.Client, flowType ClientType, chosenRecoveryConfirmAddress string, recoverySubmissionResponse string, expectedCode int) string {\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, chosenRecoveryConfirmAddress, \"Use code\")\n\t\tassert.Contains(t, message.Body, \"Recover access to your account by entering\")\n\n\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\tassert.NotEmpty(t, recoveryCode)\n\n\t\treturn submitRecoveryFormSubsequent(t, client, recoverySubmissionResponse, flowType, func(v url.Values) { v.Set(\"code\", recoveryCode) }, expectedCode)\n\t}\n\n\trecoverHappyPath := func(t *testing.T, client *http.Client, clientType ClientType, anyAddress string) string {\n\t\trecoverySubmissionResponse := submitRecoveryFormInitial(t, client, clientType, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", anyAddress)\n\t\t}, http.StatusOK)\n\n\t\tcheckRecoveryScreenAskForCode(t, anyAddress, recoverySubmissionResponse)\n\n\t\tbody := extractCodeFromCourierAndSubmit(t, client, clientType, anyAddress, recoverySubmissionResponse, http.StatusOK)\n\t\treturn body\n\t}\n\n\texpectRedirectToSettings := func(t *testing.T, client *http.Client, clientType ClientType, body string) {\n\t\tswitch clientType {\n\t\tcase RecoveryClientTypeBrowser:\n\t\t\trequire.Len(t, client.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(client.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\t\t\trequire.Contains(t, body, \"You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 60.00 minutes.\")\n\t\tcase RecoveryClientTypeSPA:\n\t\t\trequire.Len(t, client.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(client.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\tcase RecoveryClientTypeAPI:\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String(), \"%s\", body)\n\t\t}\n\t}\n\n\tt.Run(\"description=should recover an account\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\temail := testhelpers.RandomEmail()\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\n\t\t\tbody := recoverHappyPath(t, client, RecoveryClientTypeBrowser, email)\n\n\t\t\tassert.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\tres, err := client.Get(public.URL + session.RouteWhoami)\n\t\t\trequire.NoError(t, err)\n\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\temail := testhelpers.RandomEmail()\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\n\t\t\tbody := recoverHappyPath(t, client, RecoveryClientTypeSPA, email)\n\n\t\t\tassert.Equal(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\tassert.Len(t, gjson.Get(body, \"continue_with\").Array(), 1)\n\t\t\tsfId := gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow.id\").String()\n\t\t\tassert.NotEmpty(t, uuid.Must(uuid.FromString(sfId)))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tclient := &http.Client{}\n\t\t\temail := testhelpers.RandomEmail()\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\n\t\t\tbody := recoverHappyPath(t, client, RecoveryClientTypeAPI, email)\n\n\t\t\tassert.Equal(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\tassert.Len(t, gjson.Get(body, \"continue_with\").Array(), 2)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String())\n\t\t\tsfId := gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow.id\").String()\n\t\t\tassert.NotEmpty(t, uuid.Must(uuid.FromString(sfId)))\n\t\t})\n\n\t\tt.Run(\"description=should return browser to return url\", func(t *testing.T) {\n\t\t\treturnTo := public.URL + \"/return-to\"\n\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdesc        string\n\t\t\t\treturnTo    string\n\t\t\t\tf           func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow\n\t\t\t\texpectedAAL string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from recovery flow\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from config\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, returnTo)\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, \"\")\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"no return to\",\n\t\t\t\t\treturnTo: \"\",\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to with an account that has 2fa enabled\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, id *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPDisplayName, \"Kratos\")\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPID, \"ory.sh\")\n\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t})\n\t\t\t\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeWebAuthn.String(), true)\n\n\t\t\t\t\t\tid.SetCredentials(identity.CredentialsTypeWebAuthn, identity.Credentials{\n\t\t\t\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\t\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":false, \"display_name\":\"test\"}]}`),\n\t\t\t\t\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Update(ctx, id, identity.ManagerAllowWriteProtectedTraits))\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t\texpectedAAL: \"aal2\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\ti := createIdentityToRecover(t, reg, email)\n\n\t\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t\t\tf := tc.f(t, client, i)\n\n\t\t\t\t\tformPayload := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\t\tformPayload.Set(\"recovery_address\", email)\n\n\t\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, false, f, client, formPayload.Encode())\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, client, RecoveryClientTypeBrowser, email, body, http.StatusOK)\n\n\t\t\t\t\trequire.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\t\tsettingsId := gjson.Get(body, \"id\").String()\n\n\t\t\t\t\tsf, err := reg.SettingsFlowPersister().GetSettingsFlow(ctx, uuid.Must(uuid.FromString(settingsId)))\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tu, err := url.Parse(public.URL)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, client.Jar.Cookies(u), 2)\n\t\t\t\t\tfound := false\n\t\t\t\t\tfor _, cookie := range client.Jar.Cookies(u) {\n\t\t\t\t\t\tif cookie.Name == \"ory_kratos_session\" {\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\trequire.True(t, found)\n\n\t\t\t\t\trequire.Equal(t, tc.returnTo, sf.ReturnTo)\n\t\t\t\t\tres, err = client.Get(public.URL + session.RouteWhoami)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\t\tif tc.expectedAAL == \"aal2\" {\n\t\t\t\t\t\trequire.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\t\t\trequire.Equalf(t, session.NewErrAALNotSatisfied(\"\").Reason(), gjson.Get(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\t\trequire.Equalf(t, \"session_aal2_required\", gjson.Get(body, \"error.id\").String(), \"%s\", body)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\t\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads after submission\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress := fmt.Sprintf(\"test-%s@ory.sh\", testCase.ClientType)\n\t\t\t\tcreateIdentityToRecover(t, reg, address)\n\t\t\t\tbody := submitRecoveryFormInitial(t, testCase.GetClient(t), testCase.ClientType, func(u url.Values) { u.Set(\"recovery_address\", address) }, http.StatusOK)\n\t\t\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes\").String()), []string{\"0.attributes.value\"})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\trs := testhelpers.GetRecoveryFlowForType(t, c, public, testCase.FlowType)\n\n\t\t\t\ttesthelpers.SnapshotTExcept(t, rs.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\t\t\tassert.EqualValues(t, public.URL+recovery.RouteSubmitFlow+\"?flow=\"+rs.Id, rs.Ui.Action)\n\t\t\t\tassert.Empty(t, rs.Ui.Messages)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should require an address to be sent\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+flowType.String(), func(t *testing.T) {\n\t\t\t\tcode := testhelpers.ExpectStatusCode(flowType == RecoveryClientTypeAPI || flowType == RecoveryClientTypeSPA, http.StatusBadRequest, http.StatusOK)\n\t\t\t\tbody := submitRecoveryFormInitial(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\tv.Del(\"recovery_address\")\n\t\t\t\t}, code)\n\t\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, \"Property recovery_address is missing.\",\n\t\t\t\t\tgjson.Get(body, \"ui.nodes.#(attributes.name==recovery_address).messages.0.text\").String(),\n\t\t\t\t\t\"%s\", body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should pretend the address exists when it does not\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+flowType.String(), func(t *testing.T) {\n\t\t\t\tfor _, address := range []string{\"\\\\@\", \"asdf@\", \"...@\", \"aiacobelli.sec@gmail.com,alejandro.iacobelli@mercadolibre.com\"} {\n\t\t\t\t\tbody := submitRecoveryFormInitial(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_address\", address)\n\t\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t\tactiveMethod := gjson.Get(body, \"active\").String()\n\t\t\t\t\tassert.EqualValues(t, node.CodeGroup, activeMethod, \"expected method to be %s got %s\", node.CodeGroup, activeMethod)\n\t\t\t\t\texpectedMessage := text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(address)).Text\n\t\t\t\t\tactualMessage := gjson.Get(body, \"ui.messages.0.text\").String()\n\t\t\t\t\tassert.EqualValues(t, expectedMessage, actualMessage, \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should try to submit the form while authenticated\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tisSPA := testCase.ClientType == \"spa\"\n\t\t\t\tisAPI := testCase.ClientType == \"api\"\n\t\t\t\tclient := testCase.GetClient(t)\n\n\t\t\t\tvar f *kratos.RecoveryFlow\n\t\t\t\tif isAPI {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaAPI(t, client, public)\n\t\t\t\t} else {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaBrowser(t, client, isSPA, public, nil)\n\t\t\t\t}\n\t\t\t\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil).WithContext(contextx.WithConfigValue(ctx, config.ViperKeySessionLifespan, time.Hour))\n\n\t\t\t\tsession, err := testhelpers.NewActiveSession(\n\t\t\t\t\treq,\n\t\t\t\t\treg,\n\t\t\t\t\t&identity.Identity{ID: x.NewUUID(), State: identity.StateActive, NID: x.NewUUID()},\n\t\t\t\t\ttime.Now(),\n\t\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\t\tidentity.AuthenticatorAssuranceLevel1,\n\t\t\t\t)\n\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// Add the authentication to the request\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(testhelpers.NewAuthorizedTransport(ctx, t, reg, session), t).RoundTripper\n\n\t\t\t\tv := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tv.Set(\"recovery_address\", \"some-email@example.org\")\n\t\t\t\tv.Set(\"method\", \"code\")\n\n\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, isAPI || isSPA, f, client, testhelpers.EncodeFormAsJSON(t, isAPI || isSPA, v))\n\n\t\t\t\tif isAPI || isSPA {\n\t\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), recovery.RouteSubmitFlow, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, recovery.ErrAlreadyLoggedIn, json.RawMessage(gjson.Get(body, \"error\").Raw), nil)\n\t\t\t\t} else {\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover account that does not exist\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, true)\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, false)\n\t\t})\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\temail := x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\twithValues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", email)\n\t\t\t\t}\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, withValues, http.StatusOK)\n\t\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code).attributes.value\").String(), \"%s\", body)\n\t\t\t\tassertx.EqualAsJSON(t, text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(email)), json.RawMessage(gjson.Get(body, \"ui.messages.0\").Raw))\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Account access attempted\")\n\t\t\t\tassert.Contains(t, message.Body, \"If this was you, check if you signed up using a different address.\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover an inactive account\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress := \"recoverinactive_\" + testCase.ClientType.String() + \"@ory.sh\"\n\t\t\t\tcreateIdentityToRecover(t, reg, address)\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address)\n\t\t\t\t}\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, cl, testCase.ClientType, values, http.StatusOK)\n\t\t\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(context.Background(), identity.AddressTypeEmail, address)\n\t\t\t\tassert.NoError(t, err)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, address, body)\n\n\t\t\t\t// Deactivate the identity\n\t\t\t\trequire.NoError(t, reg.Persister().GetConnection(context.Background()).RawQuery(\"UPDATE identities SET state=? WHERE id = ?\", identity.StateInactive, addr.IdentityID).Exec())\n\n\t\t\t\tcode := testhelpers.ExpectStatusCode(testCase.ClientType == RecoveryClientTypeAPI || testCase.ClientType == RecoveryClientTypeSPA, http.StatusUnauthorized, http.StatusOK)\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, address, body, code)\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\tfallthrough\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", addr.IdentityID), json.RawMessage(gjson.Get(body, \"error\").Raw), \"%s\", body)\n\t\t\t\tdefault:\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", addr.IdentityID), json.RawMessage(body), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should recover and invalidate all other sessions if hook is set\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"revoke_active_sessions\"}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t})\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\tid := createIdentityToRecover(t, reg, email)\n\n\t\t\t\totherSession, err := testhelpers.NewActiveSession(httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil), reg, id, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, otherSession))\n\n\t\t\t\trefetchedOtherSession, err := reg.SessionPersister().GetSession(ctx, otherSession.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.True(t, refetchedOtherSession.IsActive())\n\n\t\t\t\tcl := testCase.GetClient(t)\n\t\t\t\tbody := recoverHappyPath(t, cl, testCase.ClientType, email)\n\n\t\t\t\texpectRedirectToSettings(t, cl, testCase.ClientType, body)\n\n\t\t\t\trefetchedOtherSession, err = reg.SessionPersister().GetSession(ctx, otherSession.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.False(t, refetchedOtherSession.IsActive())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid code more than 5 times\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", email)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, email, body)\n\n\t\t\t\tinitialFlowId := gjson.Get(body, \"id\")\n\n\t\t\t\tfor range 5 {\n\t\t\t\t\tbody := submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusOK)\n\n\t\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t\t\t\t}\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\t// submit an invalid code for the 6th time\n\t\t\t\t\tbody := submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusOK)\n\n\t\t\t\t\trequire.Len(t, gjson.Get(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\t\t// check that a new flow has been created\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_address)\").Exists())\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tfallthrough\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\t// submit an invalid code for the 6th time\n\t\t\t\t\tbody := submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusBadRequest)\n\n\t\t\t\t\tassert.Equal(t, \"Bad Request\", gjson.Get(body, \"error.status\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\tcontinueWith := gjson.Get(body, \"error.details.continue_with\").Array()\n\t\t\t\t\tassert.Len(t, continueWith, 1, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"show_recovery_ui\", continueWith[0].Get(\"action\").String(), \"%s\", body)\n\t\t\t\t\tflowId := continueWith[0].Get(\"flow.id\").String()\n\t\t\t\t\tassert.NotEmpty(t, flowId, \"%s\", body)\n\t\t\t\t\trequire.NotEqual(t, flowId, initialFlowId, \"%s\", body)\n\n\t\t\t\t\tflow, err := reg.Persister().GetRecoveryFlow(ctx, uuid.Must(uuid.FromString(flowId)))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Len(t, flow.UI.Messages, 1, \"%+v\", flow)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", flow.UI.Messages[0].Text)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should be able to recover after using invalid code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\t_ = createIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, recoveryEmail, body)\n\n\t\t\t\t// Submit invalid code.\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusOK)\n\t\t\t\tflowId := gjson.Get(body, \"id\").String()\n\t\t\t\trequire.NotEmpty(t, flowId)\n\n\t\t\t\trs, res, err := testhelpers.\n\t\t\t\t\tNewSDKCustomClient(public, c).\n\t\t\t\t\tFrontendAPI.GetRecoveryFlow(context.Background()).\n\t\t\t\t\tId(flowId).\n\t\t\t\t\tExecute()\n\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tgetBody := ioutilx.MustReadAll(res.Body)\n\t\t\t\trequire.NotEmpty(t, getBody)\n\n\t\t\t\trequire.Len(t, rs.Ui.Messages, 1)\n\t\t\t\tassert.Equal(t, \"The recovery code is invalid or has already been used. Please try again.\", rs.Ui.Messages[0].Text)\n\n\t\t\t\t// Now submit the correct code\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, c, testCase.ClientType, recoveryEmail, body, http.StatusOK)\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tassert.Len(t, gjson.Get(body, \"ui.messages\").Array(), 1)\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"You successfully recovered your account.\")\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\trequire.Len(t, c.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\t\t\tcookies := spew.Sdump(c.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\t\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String(), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not break ui if empty code is submitted\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, recoveryEmail, body)\n\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", \"\")\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// Not an error, just handle it as a code resend.\n\t\t\t\ttesthelpers.AssertMessage(t, body, text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(recoveryEmail)).Text)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should be able to resend the recovery code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, recoveryEmail, body)\n\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", \"\")\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", recoveryEmail) // Trigger resend.\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_confirm_address).attributes.value\").String())\n\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, c, testCase.ClientType, recoveryEmail, body, http.StatusOK)\n\t\t\t\texpectRedirectToSettings(t, c, testCase.ClientType, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to use first code after re-sending email\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, recoveryEmail, body)\n\n\t\t\t\tmessage1 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Use code\")\n\t\t\t\tassert.Contains(t, message1.Body, \"Recover access to your account by entering\")\n\t\t\t\trecoveryCode1 := testhelpers.CourierExpectCodeInMessage(t, message1, 1)\n\t\t\t\tassert.NotEmpty(t, recoveryCode1)\n\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", \"\")\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", recoveryEmail) // Trigger resend.\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_confirm_address).attributes.value\").String())\n\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\n\t\t\t\t// Try to submit the old (expired) code.\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", recoveryCode1)\n\t\t\t\t}, http.StatusOK)\n\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\n\t\t\t\t// Send the right code.\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, c, testCase.ClientType, recoveryEmail, body, http.StatusOK)\n\t\t\t\texpectRedirectToSettings(t, c, testCase.ClientType, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should recover if post recovery hook is successful\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t\t\t})\n\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tcl := testCase.GetClient(t)\n\n\t\t\t\tbody := recoverHappyPath(t, cl, testCase.ClientType, recoveryEmail)\n\t\t\t\texpectRedirectToSettings(t, cl, testCase.ClientType, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover if post recovery hook fails\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecutePostRecoveryHook\": \"err\"}`)}})\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t\t\t})\n\n\t\t\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, cl, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryEmail)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, recoveryEmail, body)\n\n\t\t\t\tassert.Equal(t, recoveryEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_confirm_address).attributes.value\").String())\n\n\t\t\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\n\t\t\t\tinitialFlowId := gjson.Get(body, \"id\")\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, recoveryEmail, body, http.StatusSeeOther)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, recoveryEmail, body, http.StatusBadRequest)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, recoveryEmail, body, http.StatusBadRequest)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\t\t\t\t\trequire.Equal(t, \"err\", gjson.Get(body, \"error.message\").String(), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"choose different address\", func(t *testing.T) {\n\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\twrongEmail := testhelpers.RandomEmail()\n\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\trecoverySubmissionResponse := submitRecoveryFormInitial(t, client, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", wrongEmail)\n\t\t}, http.StatusOK)\n\t\tassert.True(t, gjson.Get(recoverySubmissionResponse, \"ui.nodes.#(attributes.name==code)\").Exists(), \"%s\", recoverySubmissionResponse)\n\n\t\tsubmitRecoveryFormSubsequent(t, client, recoverySubmissionResponse, RecoveryClientTypeBrowser, func(v url.Values) { v.Set(\"screen\", \"previous\") }, http.StatusOK)\n\t\trecoverHappyPath(t, client, RecoveryClientTypeBrowser, recoveryEmail)\n\t})\n}\n\nfunc createIdentityToRecoverPhone(t *testing.T, reg *driver.RegistryDefault, address string) *identity.Identity {\n\tt.Helper()\n\tid := &identity.Identity{\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\"password\": {\n\t\t\t\tType:        \"password\",\n\t\t\t\tIdentifiers: []string{address},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t},\n\t\t},\n\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"phone\":\"%s\"}`, address)),\n\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\tState:    identity.StateActive,\n\t}\n\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), id, identity.ManagerAllowWriteProtectedTraits))\n\n\treturn id\n}\n\n// Recovery V2 is only tested with `ContinueWith`.\nfunc TestRecovery_V2_WithContinueWith_OneAddress_Phone(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(defaultConfig),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeCodeAuth, true)),\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeyUseContinueWithTransitions: true,\n\t\t\tconfig.ViperKeyChooseRecoveryAddress:      true,\n\t\t}),\n\t)\n\n\t_ = testhelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublic, _, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\tsubmitRecoveryFormInitial := func(t *testing.T, client *http.Client, flowType ClientType, values func(url.Values), code int) string {\n\t\tisSPA := flowType == RecoveryClientTypeSPA\n\t\tisAPI := flowType == RecoveryClientTypeAPI\n\t\tif client == nil {\n\t\t\tclient = testhelpers.NewDebugClient(t)\n\t\t\tif !isAPI {\n\t\t\t\tclient = testhelpers.NewClientWithCookies(t)\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t}\n\t\t}\n\n\t\texpectedUrl := testhelpers.ExpectURL(isAPI || isSPA, public.URL+recovery.RouteSubmitFlow, conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\treturn testhelpers.SubmitRecoveryForm(t, isAPI, isSPA, client, public, values, code, expectedUrl)\n\t}\n\n\tsubmitRecoveryFormSubsequent := func(t *testing.T, client *http.Client, flow string, flowType ClientType, urlValuesFn func(url.Values), statusCode int) string {\n\t\tt.Helper()\n\t\taction := gjson.Get(flow, \"ui.action\").String()\n\t\tassert.NotEmpty(t, action)\n\n\t\turlValues := url.Values{}\n\t\turlValuesFn(urlValues)\n\t\tvalues := withCSRFToken(t, flowType, flow, urlValues)\n\n\t\tcontentType := \"application/json\"\n\t\tif flowType == RecoveryClientTypeBrowser {\n\t\t\tcontentType = \"application/x-www-form-urlencoded\"\n\t\t}\n\n\t\tres, err := client.Post(action, contentType, bytes.NewBufferString(values))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, statusCode, res.StatusCode)\n\n\t\treturn string(ioutilx.MustReadAll(res.Body))\n\t}\n\n\textractCodeFromCourierAndSubmit := func(t *testing.T, client *http.Client, flowType ClientType, chosenRecoveryConfirmAddress string, recoverySubmissionResponse string, expectedCode int) string {\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, chosenRecoveryConfirmAddress, \"\")\n\t\tassert.Contains(t, message.Body, \"Your recovery code is\")\n\n\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\tassert.NotEmpty(t, recoveryCode)\n\n\t\treturn submitRecoveryFormSubsequent(t, client, recoverySubmissionResponse, flowType, func(v url.Values) { v.Set(\"code\", recoveryCode) }, expectedCode)\n\t}\n\n\trecoverHappyPath := func(t *testing.T, client *http.Client, clientType ClientType, anyAddress string) string {\n\t\trecoverySubmissionResponse := submitRecoveryFormInitial(t, client, clientType, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", anyAddress)\n\t\t}, http.StatusOK)\n\n\t\tbody := extractCodeFromCourierAndSubmit(t, client, clientType, anyAddress, recoverySubmissionResponse, http.StatusOK)\n\t\treturn body\n\t}\n\n\texpectRedirectToSettings := func(t *testing.T, client *http.Client, clientType ClientType, body string) {\n\t\tswitch clientType {\n\t\tcase RecoveryClientTypeBrowser:\n\t\t\trequire.Len(t, client.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(client.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\t\t\trequire.Contains(t, body, \"You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 60.00 minutes.\")\n\t\tcase RecoveryClientTypeSPA:\n\t\t\trequire.Len(t, client.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(client.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\tcase RecoveryClientTypeAPI:\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String(), \"%s\", body)\n\t\t}\n\t}\n\n\tt.Run(\"description=should recover an account\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\taddress := testhelpers.RandomPhone()\n\t\t\tcreateIdentityToRecoverPhone(t, reg, address)\n\n\t\t\tbody := recoverHappyPath(t, client, RecoveryClientTypeBrowser, address)\n\n\t\t\tassert.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\tres, err := client.Get(public.URL + session.RouteWhoami)\n\t\t\trequire.NoError(t, err)\n\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\taddress := testhelpers.RandomPhone()\n\t\t\tcreateIdentityToRecoverPhone(t, reg, address)\n\n\t\t\tbody := recoverHappyPath(t, client, RecoveryClientTypeSPA, address)\n\n\t\t\tassert.Equal(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\tassert.Len(t, gjson.Get(body, \"continue_with\").Array(), 1)\n\t\t\tsfId := gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow.id\").String()\n\t\t\tassert.NotEmpty(t, uuid.Must(uuid.FromString(sfId)))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tclient := &http.Client{}\n\t\t\taddress := testhelpers.RandomPhone()\n\t\t\tcreateIdentityToRecoverPhone(t, reg, address)\n\n\t\t\tbody := recoverHappyPath(t, client, RecoveryClientTypeAPI, address)\n\n\t\t\tassert.Equal(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\tassert.Len(t, gjson.Get(body, \"continue_with\").Array(), 2)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String())\n\t\t\tsfId := gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow.id\").String()\n\t\t\tassert.NotEmpty(t, uuid.Must(uuid.FromString(sfId)))\n\t\t})\n\n\t\tt.Run(\"description=should return browser to return url\", func(t *testing.T) {\n\t\t\treturnTo := public.URL + \"/return-to\"\n\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdesc        string\n\t\t\t\treturnTo    string\n\t\t\t\tf           func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow\n\t\t\t\texpectedAAL string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from recovery flow\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from config\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, returnTo)\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, \"\")\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"no return to\",\n\t\t\t\t\treturnTo: \"\",\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to with an account that has 2fa enabled\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, id *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPDisplayName, \"Kratos\")\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPID, \"ory.sh\")\n\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t})\n\t\t\t\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeWebAuthn.String(), true)\n\n\t\t\t\t\t\tid.SetCredentials(identity.CredentialsTypeWebAuthn, identity.Credentials{\n\t\t\t\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\t\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":false, \"display_name\":\"test\"}]}`),\n\t\t\t\t\t\t\tIdentifiers: []string{testhelpers.RandomPhone()},\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Update(ctx, id, identity.ManagerAllowWriteProtectedTraits))\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t\texpectedAAL: \"aal2\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\t\taddress := testhelpers.RandomPhone()\n\t\t\t\t\ti := createIdentityToRecoverPhone(t, reg, address)\n\n\t\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t\t\tf := tc.f(t, client, i)\n\n\t\t\t\t\tformPayload := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\t\tformPayload.Set(\"recovery_address\", address)\n\n\t\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, false, f, client, formPayload.Encode())\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, client, RecoveryClientTypeBrowser, address, body, http.StatusOK)\n\n\t\t\t\t\trequire.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\t\tsettingsId := gjson.Get(body, \"id\").String()\n\n\t\t\t\t\tsf, err := reg.SettingsFlowPersister().GetSettingsFlow(ctx, uuid.Must(uuid.FromString(settingsId)))\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tu, err := url.Parse(public.URL)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, client.Jar.Cookies(u), 2)\n\t\t\t\t\tfound := false\n\t\t\t\t\tfor _, cookie := range client.Jar.Cookies(u) {\n\t\t\t\t\t\tif cookie.Name == \"ory_kratos_session\" {\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\trequire.True(t, found)\n\n\t\t\t\t\trequire.Equal(t, tc.returnTo, sf.ReturnTo)\n\t\t\t\t\tres, err = client.Get(public.URL + session.RouteWhoami)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\t\tif tc.expectedAAL == \"aal2\" {\n\t\t\t\t\t\trequire.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\t\t\trequire.Equalf(t, session.NewErrAALNotSatisfied(\"\").Reason(), gjson.Get(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\t\trequire.Equalf(t, \"session_aal2_required\", gjson.Get(body, \"error.id\").String(), \"%s\", body)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\t\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads after submission\", func(t *testing.T) {\n\t\tfakes := []string{\"+491705550176\", \"+491705550177\", \"+491705550178\"}\n\t\tfakeIdx := 0\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress := fakes[fakeIdx]\n\t\t\t\tfakeIdx += 1\n\n\t\t\t\tcreateIdentityToRecoverPhone(t, reg, address)\n\t\t\t\tbody := submitRecoveryFormInitial(t, testCase.GetClient(t), testCase.ClientType, func(u url.Values) { u.Set(\"recovery_address\", address) }, http.StatusOK)\n\t\t\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes\").String()), []string{\"0.attributes.value\"})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\trs := testhelpers.GetRecoveryFlowForType(t, c, public, testCase.FlowType)\n\n\t\t\t\ttesthelpers.SnapshotTExcept(t, rs.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\t\t\tassert.EqualValues(t, public.URL+recovery.RouteSubmitFlow+\"?flow=\"+rs.Id, rs.Ui.Action)\n\t\t\t\tassert.Empty(t, rs.Ui.Messages)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should require an address to be sent\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+flowType.String(), func(t *testing.T) {\n\t\t\t\tcode := testhelpers.ExpectStatusCode(flowType == RecoveryClientTypeAPI || flowType == RecoveryClientTypeSPA, http.StatusBadRequest, http.StatusOK)\n\t\t\t\tbody := submitRecoveryFormInitial(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\tv.Del(\"recovery_address\")\n\t\t\t\t}, code)\n\t\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, \"Property recovery_address is missing.\",\n\t\t\t\t\tgjson.Get(body, \"ui.nodes.#(attributes.name==recovery_address).messages.0.text\").String(),\n\t\t\t\t\t\"%s\", body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should require an existing address to be sent\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+flowType.String(), func(t *testing.T) {\n\t\t\t\tfor _, address := range []string{\"\\\\\", \"asdf\", \"...\", testhelpers.RandomPhone() + \",\" + testhelpers.RandomPhone()} {\n\t\t\t\t\tbody := submitRecoveryFormInitial(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_address\", address)\n\t\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t\tactiveMethod := gjson.Get(body, \"active\").String()\n\t\t\t\t\tassert.EqualValues(t, node.CodeGroup, activeMethod, \"expected method to be %s got %s\", node.CodeGroup, activeMethod)\n\t\t\t\t\texpectedMessage := text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(address)).Text\n\t\t\t\t\tactualMessage := gjson.Get(body, \"ui.messages.0.text\").String()\n\t\t\t\t\tassert.EqualValues(t, expectedMessage, actualMessage, \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should try to submit the form while authenticated\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tisSPA := testCase.ClientType == \"spa\"\n\t\t\t\tisAPI := testCase.ClientType == \"api\"\n\t\t\t\tclient := testCase.GetClient(t)\n\n\t\t\t\tvar f *kratos.RecoveryFlow\n\t\t\t\tif isAPI {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaAPI(t, client, public)\n\t\t\t\t} else {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaBrowser(t, client, isSPA, public, nil)\n\t\t\t\t}\n\t\t\t\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil).WithContext(contextx.WithConfigValue(ctx, config.ViperKeySessionLifespan, time.Hour))\n\n\t\t\t\tsession, err := testhelpers.NewActiveSession(\n\t\t\t\t\treq,\n\t\t\t\t\treg,\n\t\t\t\t\t&identity.Identity{ID: x.NewUUID(), State: identity.StateActive, NID: x.NewUUID()},\n\t\t\t\t\ttime.Now(),\n\t\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\t\tidentity.AuthenticatorAssuranceLevel1,\n\t\t\t\t)\n\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// Add the authentication to the request\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(testhelpers.NewAuthorizedTransport(ctx, t, reg, session), t).RoundTripper\n\n\t\t\t\tv := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tv.Set(\"recovery_address\", testhelpers.RandomPhone())\n\t\t\t\tv.Set(\"method\", \"code\")\n\n\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, isAPI || isSPA, f, client, testhelpers.EncodeFormAsJSON(t, isAPI || isSPA, v))\n\n\t\t\t\tif isAPI || isSPA {\n\t\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), recovery.RouteSubmitFlow, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, recovery.ErrAlreadyLoggedIn, json.RawMessage(gjson.Get(body, \"error\").Raw), nil)\n\t\t\t\t} else {\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover account that does not exist\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, true)\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, false)\n\t\t})\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress := testhelpers.RandomPhone()\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\twithValues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address)\n\t\t\t\t}\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, withValues, http.StatusOK)\n\t\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code).attributes.value\").String(), \"%s\", body)\n\t\t\t\tassertx.EqualAsJSON(t, text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(address)), json.RawMessage(gjson.Get(body, \"ui.messages.0\").Raw))\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover an inactive account\", func(t *testing.T) {\n\t\tfakes := []string{\"+491705550173\", \"+491705550174\", \"+491705550175\"}\n\t\tfakeIdx := 0\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress := fakes[fakeIdx]\n\t\t\t\tfakeIdx += 1\n\n\t\t\t\ti := createIdentityToRecoverPhone(t, reg, address)\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address)\n\t\t\t\t}\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, cl, testCase.ClientType, values, http.StatusOK)\n\n\t\t\t\t// Deactivate the identity\n\t\t\t\trequire.NoError(t, reg.Persister().GetConnection(context.Background()).RawQuery(\"UPDATE identities SET state=? WHERE id = ?\", identity.StateInactive, i.ID).Exec())\n\n\t\t\t\tcode := testhelpers.ExpectStatusCode(testCase.ClientType == RecoveryClientTypeAPI || testCase.ClientType == RecoveryClientTypeSPA, http.StatusUnauthorized, http.StatusOK)\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, address, body, code)\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\tfallthrough\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", i.ID), json.RawMessage(gjson.Get(body, \"error\").Raw), \"%s\", body)\n\t\t\t\tdefault:\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", i.ID), json.RawMessage(body), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should recover and invalidate all other sessions if hook is set\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"revoke_active_sessions\"}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t})\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress := testhelpers.RandomPhone()\n\t\t\t\tid := createIdentityToRecoverPhone(t, reg, address)\n\n\t\t\t\totherSession, err := testhelpers.NewActiveSession(httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil), reg, id, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, otherSession))\n\n\t\t\t\trefetchedOtherSession, err := reg.SessionPersister().GetSession(ctx, otherSession.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.True(t, refetchedOtherSession.IsActive())\n\n\t\t\t\tcl := testCase.GetClient(t)\n\t\t\t\tbody := recoverHappyPath(t, cl, testCase.ClientType, address)\n\n\t\t\t\texpectRedirectToSettings(t, cl, testCase.ClientType, body)\n\n\t\t\t\trefetchedOtherSession, err = reg.SessionPersister().GetSession(ctx, otherSession.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.False(t, refetchedOtherSession.IsActive())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid code more than 5 times\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverPhone(t, reg, address)\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tinitialFlowId := gjson.Get(body, \"id\")\n\n\t\t\t\tfor range 5 {\n\t\t\t\t\tbody := submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusOK)\n\n\t\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t\t\t\t}\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\t// submit an invalid code for the 6th time\n\t\t\t\t\tbody := submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusOK)\n\n\t\t\t\t\trequire.Len(t, gjson.Get(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\t\t// check that a new flow has been created\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_address)\").Exists())\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tfallthrough\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\t// submit an invalid code for the 6th time\n\t\t\t\t\tbody := submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusBadRequest)\n\n\t\t\t\t\tassert.Equal(t, \"Bad Request\", gjson.Get(body, \"error.status\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\tcontinueWith := gjson.Get(body, \"error.details.continue_with\").Array()\n\t\t\t\t\tassert.Len(t, continueWith, 1, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"show_recovery_ui\", continueWith[0].Get(\"action\").String(), \"%s\", body)\n\t\t\t\t\tflowId := continueWith[0].Get(\"flow.id\").String()\n\t\t\t\t\tassert.NotEmpty(t, flowId, \"%s\", body)\n\t\t\t\t\trequire.NotEqual(t, flowId, initialFlowId, \"%s\", body)\n\n\t\t\t\t\tflow, err := reg.Persister().GetRecoveryFlow(ctx, uuid.Must(uuid.FromString(flowId)))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Len(t, flow.UI.Messages, 1, \"%+v\", flow)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", flow.UI.Messages[0].Text)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should be able to recover after using invalid code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\trecoveryAddress := testhelpers.RandomPhone()\n\t\t\t\t_ = createIdentityToRecoverPhone(t, reg, recoveryAddress)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryAddress)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// Submit invalid code.\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusOK)\n\t\t\t\tflowId := gjson.Get(body, \"id\").String()\n\t\t\t\trequire.NotEmpty(t, flowId)\n\n\t\t\t\trs, res, err := testhelpers.\n\t\t\t\t\tNewSDKCustomClient(public, c).\n\t\t\t\t\tFrontendAPI.GetRecoveryFlow(context.Background()).\n\t\t\t\t\tId(flowId).\n\t\t\t\t\tExecute()\n\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tgetBody := ioutilx.MustReadAll(res.Body)\n\t\t\t\trequire.NotEmpty(t, getBody)\n\n\t\t\t\trequire.Len(t, rs.Ui.Messages, 1)\n\t\t\t\tassert.Equal(t, \"The recovery code is invalid or has already been used. Please try again.\", rs.Ui.Messages[0].Text)\n\n\t\t\t\t// Now submit the correct code\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, c, testCase.ClientType, recoveryAddress, body, http.StatusOK)\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tassert.Len(t, gjson.Get(body, \"ui.messages\").Array(), 1)\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"You successfully recovered your account.\")\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\trequire.Len(t, c.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\t\t\tcookies := spew.Sdump(c.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\t\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String(), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not break ui if empty code is submitted\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryAddress := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverPhone(t, reg, recoveryAddress)\n\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryAddress)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", \"\")\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", recoveryAddress)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// Not an error, just handle it as a code resend.\n\t\t\t\ttesthelpers.AssertMessage(t, body, text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(recoveryAddress)).Text)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should be able to resend the recovery code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryAddress := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverPhone(t, reg, recoveryAddress)\n\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryAddress)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", \"\")\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", recoveryAddress) // Trigger resend.\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, recoveryAddress, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_confirm_address).attributes.value\").String())\n\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, c, testCase.ClientType, recoveryAddress, body, http.StatusOK)\n\t\t\t\texpectRedirectToSettings(t, c, testCase.ClientType, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to use first code after re-sending address\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\trecoveryAddress := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverPhone(t, reg, recoveryAddress)\n\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryAddress)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tmessage1 := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryAddress, \"\")\n\t\t\t\tassert.Contains(t, message1.Body, \"Your recovery code is:\")\n\t\t\t\trecoveryCode1 := testhelpers.CourierExpectCodeInMessage(t, message1, 1)\n\t\t\t\tassert.NotEmpty(t, recoveryCode1)\n\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", \"\")\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", recoveryAddress) // Trigger resend.\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, recoveryAddress, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_confirm_address).attributes.value\").String())\n\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\n\t\t\t\t// Try to submit the old (expired) code.\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", recoveryCode1)\n\t\t\t\t}, http.StatusOK)\n\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\n\t\t\t\t// Send the right code.\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, c, testCase.ClientType, recoveryAddress, body, http.StatusOK)\n\t\t\t\texpectRedirectToSettings(t, c, testCase.ClientType, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should recover if post recovery hook is successful\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t\t\t})\n\n\t\t\t\trecoveryAddress := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverPhone(t, reg, recoveryAddress)\n\n\t\t\t\tcl := testCase.GetClient(t)\n\n\t\t\t\tbody := recoverHappyPath(t, cl, testCase.ClientType, recoveryAddress)\n\t\t\t\texpectRedirectToSettings(t, cl, testCase.ClientType, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover if post recovery hook fails\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecutePostRecoveryHook\": \"err\"}`)}})\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t\t\t})\n\n\t\t\t\trecoveryAddress := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverPhone(t, reg, recoveryAddress)\n\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, cl, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", recoveryAddress)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tassert.Equal(t, recoveryAddress, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_confirm_address).attributes.value\").String())\n\n\t\t\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\n\t\t\t\tinitialFlowId := gjson.Get(body, \"id\")\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, recoveryAddress, body, http.StatusSeeOther)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, recoveryAddress, body, http.StatusBadRequest)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, recoveryAddress, body, http.StatusBadRequest)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\t\t\t\t\trequire.Equal(t, \"err\", gjson.Get(body, \"error.message\").String(), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"choose different address\", func(t *testing.T) {\n\t\trecoveryAddress := testhelpers.RandomPhone()\n\t\twrongAddress := testhelpers.RandomPhone()\n\t\tcreateIdentityToRecoverPhone(t, reg, recoveryAddress)\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\trecoverySubmissionResponse := submitRecoveryFormInitial(t, client, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", wrongAddress)\n\t\t}, http.StatusOK)\n\t\tassert.True(t, gjson.Get(recoverySubmissionResponse, \"ui.nodes.#(attributes.name==code)\").Exists(), \"%s\", recoverySubmissionResponse)\n\n\t\tsubmitRecoveryFormSubsequent(t, client, recoverySubmissionResponse, RecoveryClientTypeBrowser, func(v url.Values) { v.Set(\"screen\", \"previous\") }, http.StatusOK)\n\t\trecoverHappyPath(t, client, RecoveryClientTypeBrowser, recoveryAddress)\n\t})\n}\n\nfunc createIdentityToRecoverEmailAndPhone(t *testing.T, reg *driver.RegistryDefault, email string, phone string) *identity.Identity {\n\tt.Helper()\n\tid := &identity.Identity{\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\"password\": {\n\t\t\t\tType:        \"password\",\n\t\t\t\tIdentifiers: []string{phone},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$2a$08$.cOYmAd.vCpDOoiVJrO5B.hjTLKQQ6cAK40u8uB.FnZDyPvVvQ9Q.\"}`),\n\t\t\t},\n\t\t},\n\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":\"%s\", \"phone\":\"%s\"}`, email, phone)),\n\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\tState:    identity.StateActive,\n\t}\n\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), id, identity.ManagerAllowWriteProtectedTraits))\n\n\treturn id\n}\n\n// Recovery V2 is only tested with `ContinueWith`.\nfunc TestRecovery_V2_WithContinueWith_SeveralAddresses(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(defaultConfig),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeCodeAuth, true)),\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeyUseContinueWithTransitions: true,\n\t\t\tconfig.ViperKeyChooseRecoveryAddress:      true,\n\t\t}),\n\t)\n\n\t_ = testhelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublic, _, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\tsubmitRecoveryFormInitial := func(t *testing.T, client *http.Client, flowType ClientType, values func(url.Values), code int) string {\n\t\tisSPA := flowType == RecoveryClientTypeSPA\n\t\tisAPI := flowType == RecoveryClientTypeAPI\n\t\tif client == nil {\n\t\t\tclient = testhelpers.NewDebugClient(t)\n\t\t\tif !isAPI {\n\t\t\t\tclient = testhelpers.NewClientWithCookies(t)\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t}\n\t\t}\n\n\t\texpectedUrl := testhelpers.ExpectURL(isAPI || isSPA, public.URL+recovery.RouteSubmitFlow, conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\treturn testhelpers.SubmitRecoveryForm(t, isAPI, isSPA, client, public, values, code, expectedUrl)\n\t}\n\n\tsubmitRecoveryFormSubsequent := func(t *testing.T, client *http.Client, flow string, flowType ClientType, urlValuesFn func(url.Values), statusCode int) string {\n\t\tt.Helper()\n\t\taction := gjson.Get(flow, \"ui.action\").String()\n\t\tassert.NotEmpty(t, action)\n\n\t\turlValues := url.Values{}\n\t\turlValuesFn(urlValues)\n\t\tvalues := withCSRFToken(t, flowType, flow, urlValues)\n\n\t\tcontentType := \"application/json\"\n\t\tif flowType == RecoveryClientTypeBrowser {\n\t\t\tcontentType = \"application/x-www-form-urlencoded\"\n\t\t}\n\n\t\tres, err := client.Post(action, contentType, bytes.NewBufferString(values))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, statusCode, res.StatusCode)\n\n\t\treturn string(ioutilx.MustReadAll(res.Body))\n\t}\n\n\tcheckRecoveryScreenAskForRecoverySelectAddress := func(t *testing.T, recoverySubmissionResponse string) {\n\t\tassert.True(t, gjson.Get(recoverySubmissionResponse, \"ui.nodes.#(attributes.name==recovery_select_address)\").Exists(), \"%s\", recoverySubmissionResponse)\n\t\tassert.Len(t, gjson.Get(recoverySubmissionResponse, \"ui.messages\").Array(), 1, \"%s\", recoverySubmissionResponse)\n\t\tassertx.EqualAsJSON(t, text.NewRecoveryAskToChooseAddress(), json.RawMessage(gjson.Get(recoverySubmissionResponse, \"ui.messages.0\").Raw))\n\t}\n\n\tcheckRecoveryScreenAskForRecoveryConfirmAddress := func(t *testing.T, recoverySubmissionResponse string) {\n\t\tassert.True(t, gjson.Get(recoverySubmissionResponse, \"ui.nodes.#(attributes.name==recovery_confirm_address)\").Exists(), \"%s\", recoverySubmissionResponse)\n\t\tassert.Len(t, gjson.Get(recoverySubmissionResponse, \"ui.messages\").Array(), 1, \"%s\", recoverySubmissionResponse)\n\t\tassertx.EqualAsJSON(t, text.NewRecoveryAskForFullAddress(), json.RawMessage(gjson.Get(recoverySubmissionResponse, \"ui.messages.0\").Raw))\n\t}\n\n\tcheckRecoveryScreenAskForCode := func(t *testing.T, chosenRecoveryConfirmAddress, recoverySubmissionResponse string) {\n\t\tassert.True(t, gjson.Get(recoverySubmissionResponse, \"ui.nodes.#(attributes.name==code)\").Exists(), \"%s\", recoverySubmissionResponse)\n\t\tassert.Len(t, gjson.Get(recoverySubmissionResponse, \"ui.messages\").Array(), 1, \"%s\", recoverySubmissionResponse)\n\t\tassertx.EqualAsJSON(t, text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(chosenRecoveryConfirmAddress)), json.RawMessage(gjson.Get(recoverySubmissionResponse, \"ui.messages.0\").Raw))\n\t}\n\n\textractCodeFromCourierAndSubmit := func(t *testing.T, client *http.Client, flowType ClientType, chosenRecoveryConfirmAddress string, recoverySubmissionResponse string, expectedCode int) string {\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, chosenRecoveryConfirmAddress, \"\")\n\n\t\t// For some reason the wording is different between email and sms.\n\t\tif strings.ContainsRune(chosenRecoveryConfirmAddress, '@') {\n\t\t\tassert.Contains(t, message.Body, \"Recover access to your account by entering\")\n\t\t} else {\n\t\t\tassert.Contains(t, message.Body, \"Your recovery code is\")\n\t\t}\n\n\t\trecoveryCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\tassert.NotEmpty(t, recoveryCode)\n\n\t\treturn submitRecoveryFormSubsequent(t, client, recoverySubmissionResponse, flowType, func(v url.Values) { v.Set(\"code\", recoveryCode) }, expectedCode)\n\t}\n\n\trecoverHappyPath := func(t *testing.T, client *http.Client, clientType ClientType, anyAddress string, chosenAddress string) string {\n\t\trecoverySubmissionResponse := submitRecoveryFormInitial(t, client, clientType, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", anyAddress)\n\t\t}, http.StatusOK)\n\n\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, recoverySubmissionResponse)\n\t\trecoverySubmissionResponse = submitRecoveryFormSubsequent(t, client, recoverySubmissionResponse, clientType, func(v url.Values) {\n\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(chosenAddress))\n\t\t\tv.Set(\"recovery_address\", anyAddress)\n\t\t}, http.StatusOK)\n\n\t\t// If the first provided address is different from the chosen masked address,\n\t\t// the server asks the client to provide the chosen address in full.\n\t\tif anyAddress != chosenAddress {\n\t\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, recoverySubmissionResponse)\n\t\t\trecoverySubmissionResponse = submitRecoveryFormSubsequent(t, client, recoverySubmissionResponse, clientType, func(v url.Values) {\n\t\t\t\tv.Set(\"recovery_confirm_address\", chosenAddress)\n\t\t\t}, http.StatusOK)\n\t\t}\n\n\t\tcheckRecoveryScreenAskForCode(t, chosenAddress, recoverySubmissionResponse)\n\n\t\tbody := extractCodeFromCourierAndSubmit(t, client, clientType, chosenAddress, recoverySubmissionResponse, http.StatusOK)\n\t\treturn body\n\t}\n\n\texpectRedirectToSettings := func(t *testing.T, client *http.Client, clientType ClientType, body string) {\n\t\tswitch clientType {\n\t\tcase RecoveryClientTypeBrowser:\n\t\t\trequire.Len(t, client.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(client.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\t\t\trequire.Contains(t, body, \"You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 60.00 minutes.\")\n\t\tcase RecoveryClientTypeSPA:\n\t\t\trequire.Len(t, client.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(client.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\tcase RecoveryClientTypeAPI:\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String(), \"%s\", body)\n\t\t}\n\t}\n\n\tt.Run(\"description=should recover an account\", func(t *testing.T) {\n\t\tchosenAddressIdenticalToRecoveryAddressCases := []bool{true, false}\n\n\t\tfor _, identical := range chosenAddressIdenticalToRecoveryAddressCases {\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tvar body string\n\t\t\t\tif identical {\n\t\t\t\t\tbody = recoverHappyPath(t, client, RecoveryClientTypeBrowser, address2, address2)\n\t\t\t\t} else {\n\t\t\t\t\tbody = recoverHappyPath(t, client, RecoveryClientTypeBrowser, address2, address1)\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\tres, err := client.Get(public.URL + session.RouteWhoami)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tvar body string\n\t\t\t\tif identical {\n\t\t\t\t\tbody = recoverHappyPath(t, client, RecoveryClientTypeSPA, address2, address2)\n\t\t\t\t} else {\n\t\t\t\t\tbody = recoverHappyPath(t, client, RecoveryClientTypeSPA, address2, address1)\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\t\tassert.Len(t, gjson.Get(body, \"continue_with\").Array(), 1)\n\t\t\t\tsfId := gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow.id\").String()\n\t\t\t\tassert.NotEmpty(t, uuid.Must(uuid.FromString(sfId)))\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tclient := &http.Client{}\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tvar body string\n\t\t\t\tif identical {\n\t\t\t\t\tbody = recoverHappyPath(t, client, RecoveryClientTypeAPI, address2, address2)\n\t\t\t\t} else {\n\t\t\t\t\tbody = recoverHappyPath(t, client, RecoveryClientTypeAPI, address2, address1)\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\t\tassert.Len(t, gjson.Get(body, \"continue_with\").Array(), 2)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String())\n\t\t\t\tsfId := gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow.id\").String()\n\t\t\t\tassert.NotEmpty(t, uuid.Must(uuid.FromString(sfId)))\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"description=should return browser to return url\", func(t *testing.T) {\n\t\t\treturnTo := public.URL + \"/return-to\"\n\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdesc        string\n\t\t\t\treturnTo    string\n\t\t\t\tf           func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow\n\t\t\t\texpectedAAL string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from recovery flow\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from config\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, returnTo)\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, \"\")\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"no return to\",\n\t\t\t\t\treturnTo: \"\",\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, identity *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to with an account that has 2fa enabled\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client, id *identity.Identity) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPDisplayName, \"Kratos\")\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPID, \"ory.sh\")\n\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\t\t\t})\n\t\t\t\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeWebAuthn.String(), true)\n\n\t\t\t\t\t\tid.SetCredentials(identity.CredentialsTypeWebAuthn, identity.Credentials{\n\t\t\t\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\t\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":false, \"display_name\":\"test\"}]}`),\n\t\t\t\t\t\t\tIdentifiers: []string{testhelpers.RandomPhone()},\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\trequire.NoError(t, reg.IdentityManager().Update(ctx, id, identity.ManagerAllowWriteProtectedTraits))\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t\texpectedAAL: \"aal2\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\t\ti := createIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t\t\tf := tc.f(t, client, i)\n\n\t\t\t\t\tformPayload := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\t\tformPayload.Set(\"recovery_address\", address2)\n\n\t\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, false, f, client, formPayload.Encode())\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\n\t\t\t\t\t// This screen might get skipped in the backend if there is only one possible address to choose.\n\t\t\t\t\tif gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_select_address)\").Exists() {\n\t\t\t\t\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\t\t\t\t\t\tbody = submitRecoveryFormSubsequent(t, client, body, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\t\t\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t\t\t}, http.StatusOK)\n\t\t\t\t\t}\n\n\t\t\t\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\t\t\t\t\tbody = submitRecoveryFormSubsequent(t, client, body, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_confirm_address\", address2)\n\t\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t\tcheckRecoveryScreenAskForCode(t, address2, body)\n\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, client, RecoveryClientTypeBrowser, address2, body, http.StatusOK)\n\n\t\t\t\t\trequire.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\t\tsettingsId := gjson.Get(body, \"id\").String()\n\n\t\t\t\t\tsf, err := reg.SettingsFlowPersister().GetSettingsFlow(ctx, uuid.Must(uuid.FromString(settingsId)))\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tu, err := url.Parse(public.URL)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, client.Jar.Cookies(u), 2)\n\t\t\t\t\tfound := false\n\t\t\t\t\tfor _, cookie := range client.Jar.Cookies(u) {\n\t\t\t\t\t\tif cookie.Name == \"ory_kratos_session\" {\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\trequire.True(t, found)\n\n\t\t\t\t\trequire.Equal(t, tc.returnTo, sf.ReturnTo)\n\t\t\t\t\tres, err = client.Get(public.URL + session.RouteWhoami)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tbody = string(x.MustReadAll(res.Body))\n\t\t\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\t\t\tif tc.expectedAAL == \"aal2\" {\n\t\t\t\t\t\trequire.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\t\t\trequire.Equalf(t, session.NewErrAALNotSatisfied(\"\").Reason(), gjson.Get(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\t\trequire.Equalf(t, \"session_aal2_required\", gjson.Get(body, \"error.id\").String(), \"%s\", body)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, \"code_recovery\", gjson.Get(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\t\t\t\tassert.Equal(t, \"aal1\", gjson.Get(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads after submission\", func(t *testing.T) {\n\t\tfakes := []string{\"+491705550166\", \"+491705550167\", \"+491705550168\"}\n\t\tfakeIdx := 0\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress2 := fakes[fakeIdx]\n\t\t\t\tfakeIdx += 1\n\n\t\t\t\taddress1 := \"test_mrecovery_addresses-\" + testCase.ClientType.String() + \"@ory.sh\"\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, testCase.GetClient(t), testCase.ClientType, func(u url.Values) { u.Set(\"recovery_address\", address2) }, http.StatusOK)\n\t\t\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes\").String()), []string{\"0.attributes.value\"})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\trs := testhelpers.GetRecoveryFlowForType(t, c, public, testCase.FlowType)\n\n\t\t\t\ttesthelpers.SnapshotTExcept(t, rs.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\t\t\tassert.EqualValues(t, public.URL+recovery.RouteSubmitFlow+\"?flow=\"+rs.Id, rs.Ui.Action)\n\t\t\t\tassert.Empty(t, rs.Ui.Messages)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should require an address to be sent\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+flowType.String(), func(t *testing.T) {\n\t\t\t\tcode := testhelpers.ExpectStatusCode(flowType == RecoveryClientTypeAPI || flowType == RecoveryClientTypeSPA, http.StatusBadRequest, http.StatusOK)\n\t\t\t\tbody := submitRecoveryFormInitial(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\tv.Del(\"recovery_address\")\n\t\t\t\t}, code)\n\t\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, \"Property recovery_address is missing.\",\n\t\t\t\t\tgjson.Get(body, \"ui.nodes.#(attributes.name==recovery_address).messages.0.text\").String(),\n\t\t\t\t\t\"%s\", body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should pretend the address exists when it does not\", func(t *testing.T) {\n\t\tfor _, flowType := range flowTypes {\n\t\t\tt.Run(\"type=\"+flowType.String(), func(t *testing.T) {\n\t\t\t\tfor _, address := range []string{\"\\\\\", \"asdf\", \"...\", \"aiacobelli.sec@gmail.com,alejandro.iacobelli@mercadolibre.com\"} {\n\t\t\t\t\tbody := submitRecoveryFormInitial(t, nil, flowType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_address\", address)\n\t\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t\tactiveMethod := gjson.Get(body, \"active\").String()\n\t\t\t\t\tassert.EqualValues(t, node.CodeGroup, activeMethod, \"expected method to be %s got %s\", node.CodeGroup, activeMethod)\n\n\t\t\t\t\texpectedMessage := text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(address)).Text\n\t\t\t\t\tactualMessage := gjson.Get(body, \"ui.messages.0.text\").String()\n\t\t\t\t\tassert.EqualValues(t, expectedMessage, actualMessage, \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should try to submit the form while authenticated\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tisSPA := testCase.ClientType == \"spa\"\n\t\t\t\tisAPI := testCase.ClientType == \"api\"\n\t\t\t\tclient := testCase.GetClient(t)\n\n\t\t\t\tvar f *kratos.RecoveryFlow\n\t\t\t\tif isAPI {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaAPI(t, client, public)\n\t\t\t\t} else {\n\t\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaBrowser(t, client, isSPA, public, nil)\n\t\t\t\t}\n\t\t\t\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil).WithContext(contextx.WithConfigValue(ctx, config.ViperKeySessionLifespan, time.Hour))\n\n\t\t\t\tsession, err := testhelpers.NewActiveSession(\n\t\t\t\t\treq,\n\t\t\t\t\treg,\n\t\t\t\t\t&identity.Identity{ID: x.NewUUID(), State: identity.StateActive, NID: x.NewUUID()},\n\t\t\t\t\ttime.Now(),\n\t\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\t\tidentity.AuthenticatorAssuranceLevel1,\n\t\t\t\t)\n\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// Add the authentication to the request\n\t\t\t\tclient.Transport = testhelpers.NewTransportWithLogger(testhelpers.NewAuthorizedTransport(ctx, t, reg, session), t).RoundTripper\n\n\t\t\t\tv := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tv.Set(\"recovery_address\", \"some-address@example.org\")\n\t\t\t\tv.Set(\"method\", \"code\")\n\n\t\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, isAPI || isSPA, f, client, testhelpers.EncodeFormAsJSON(t, isAPI || isSPA, v))\n\n\t\t\t\tif isAPI || isSPA {\n\t\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), recovery.RouteSubmitFlow, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, recovery.ErrAlreadyLoggedIn, json.RawMessage(gjson.Get(body, \"error\").Raw), nil)\n\t\t\t\t} else {\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover account that does not exist\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, true)\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, false)\n\t\t})\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress := x.NewUUID().String() + \"@ory.sh\"\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\twithValues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address)\n\t\t\t\t}\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, withValues, http.StatusOK)\n\t\t\t\tassert.EqualValues(t, node.CodeGroup, gjson.Get(body, \"active\").String(), \"%s\", body)\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code).attributes.value\").String(), \"%s\", body)\n\t\t\t\tassertx.EqualAsJSON(t, text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(address)), json.RawMessage(gjson.Get(body, \"ui.messages.0\").Raw))\n\n\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, address, \"Account access attempted\")\n\t\t\t\tassert.Contains(t, message.Body, \"If this was you, check if you signed up using a different address.\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover an inactive account\", func(t *testing.T) {\n\t\tfakes := []string{\"+491705550163\", \"+491705550164\", \"+491705550165\"}\n\t\tfakeIdx := 0\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress2 := fakes[fakeIdx]\n\t\t\t\tfakeIdx += 1\n\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\ti := createIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t}\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, cl, testCase.ClientType, values, http.StatusOK)\n\n\t\t\t\t// This screen might get skipped in the backend if there is only one possible address to choose.\n\t\t\t\tif gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_select_address)\").Exists() {\n\t\t\t\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\t\t\t\t\tbody = submitRecoveryFormSubsequent(t, cl, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t\t}, http.StatusOK)\n\t\t\t\t}\n\n\t\t\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, cl, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address2)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, address2, body)\n\n\t\t\t\t// Deactivate the identity\n\t\t\t\trequire.NoError(t, reg.Persister().GetConnection(context.Background()).RawQuery(\"UPDATE identities SET state=? WHERE id = ?\", identity.StateInactive, i.ID).Exec())\n\n\t\t\t\tcode := testhelpers.ExpectStatusCode(testCase.ClientType == RecoveryClientTypeAPI || testCase.ClientType == RecoveryClientTypeSPA, http.StatusUnauthorized, http.StatusOK)\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, address2, body, code)\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\tfallthrough\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", i.ID), json.RawMessage(gjson.Get(body, \"error\").Raw), \"%s\", body)\n\t\t\t\tdefault:\n\t\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", i.ID), json.RawMessage(body), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should see error if invalid recovery address is submitted\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\t_ = createIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t}\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, cl, testCase.ClientType, values, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\t\t\t\tsc := http.StatusOK\n\t\t\t\tif testCase.ClientType != RecoveryClientTypeBrowser {\n\t\t\t\t\tsc = http.StatusBadRequest\n\t\t\t\t}\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, cl, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t\t\t\tv.Set(\"recovery_address\", \"not-the-correct@email.com\")\n\t\t\t\t}, sc)\n\n\t\t\t\trequire.Equal(t, 1, len(gjson.Get(body, \"ui.messages\").Array()), \"%s\", body)\n\t\t\t\tassert.Equal(t, \"4000001\", gjson.Get(body, \"ui.messages.0.id\").String(), \"%s\", body)\n\t\t\t\tassert.Equal(t, \"The selected recovery address is not valid.\", gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should recover and invalidate all other sessions if hook is set\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"revoke_active_sessions\"}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t})\n\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tid := createIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\totherSession, err := testhelpers.NewActiveSession(httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil), reg, id, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(ctx, otherSession))\n\n\t\t\t\trefetchedOtherSession, err := reg.SessionPersister().GetSession(ctx, otherSession.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.True(t, refetchedOtherSession.IsActive())\n\n\t\t\t\tcl := testCase.GetClient(t)\n\t\t\t\tbody := recoverHappyPath(t, cl, testCase.ClientType, address2, address1)\n\n\t\t\t\texpectRedirectToSettings(t, cl, testCase.ClientType, body)\n\n\t\t\t\trefetchedOtherSession, err = reg.SessionPersister().GetSession(ctx, otherSession.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.False(t, refetchedOtherSession.IsActive())\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid code more than 5 times\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// This screen might get skipped in the backend if there is only one possible address to choose.\n\t\t\t\tif gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_select_address)\").Exists() {\n\t\t\t\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\t\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t\t}, http.StatusOK)\n\t\t\t\t}\n\n\t\t\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address1)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, address1, body)\n\n\t\t\t\tinitialFlowId := gjson.Get(body, \"id\")\n\n\t\t\t\tfor range 5 {\n\t\t\t\t\tbody := submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusOK)\n\n\t\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\t\t\t\t}\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\t// submit an invalid code for the 6th time\n\t\t\t\t\tbody := submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusOK)\n\n\t\t\t\t\trequire.Len(t, gjson.Get(body, \"ui.messages\").Array(), 1, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\t\t\t// check that a new flow has been created\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_address)\").Exists())\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tfallthrough\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\t// submit an invalid code for the 6th time\n\t\t\t\t\tbody := submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusBadRequest)\n\n\t\t\t\t\tassert.Equal(t, \"Bad Request\", gjson.Get(body, \"error.status\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\tcontinueWith := gjson.Get(body, \"error.details.continue_with\").Array()\n\t\t\t\t\tassert.Len(t, continueWith, 1, \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"show_recovery_ui\", continueWith[0].Get(\"action\").String(), \"%s\", body)\n\t\t\t\t\tflowId := continueWith[0].Get(\"flow.id\").String()\n\t\t\t\t\tassert.NotEmpty(t, flowId, \"%s\", body)\n\t\t\t\t\trequire.NotEqual(t, flowId, initialFlowId, \"%s\", body)\n\n\t\t\t\t\tflow, err := reg.Persister().GetRecoveryFlow(ctx, uuid.Must(uuid.FromString(flowId)))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Len(t, flow.UI.Messages, 1, \"%+v\", flow)\n\t\t\t\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", flow.UI.Messages[0].Text)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should be able to recover after using invalid code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// This screen might get skipped in the backend if there is only one possible address to choose.\n\t\t\t\tif gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_select_address)\").Exists() {\n\t\t\t\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\t\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t\t}, http.StatusOK)\n\t\t\t\t}\n\n\t\t\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address1)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, address1, body)\n\n\t\t\t\t// Submit invalid code.\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) { v.Set(\"code\", \"12312312\") }, http.StatusOK)\n\t\t\t\tflowId := gjson.Get(body, \"id\").String()\n\t\t\t\trequire.NotEmpty(t, flowId)\n\n\t\t\t\trs, res, err := testhelpers.\n\t\t\t\t\tNewSDKCustomClient(public, c).\n\t\t\t\t\tFrontendAPI.GetRecoveryFlow(context.Background()).\n\t\t\t\t\tId(flowId).\n\t\t\t\t\tExecute()\n\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tgetBody := ioutilx.MustReadAll(res.Body)\n\t\t\t\trequire.NotEmpty(t, getBody)\n\n\t\t\t\trequire.Len(t, rs.Ui.Messages, 1)\n\t\t\t\tassert.Equal(t, \"The recovery code is invalid or has already been used. Please try again.\", rs.Ui.Messages[0].Text)\n\n\t\t\t\t// Now submit the correct code\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, c, testCase.ClientType, address1, body, http.StatusOK)\n\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tassert.Len(t, gjson.Get(body, \"ui.messages\").Array(), 1)\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"You successfully recovered your account.\")\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\trequire.Len(t, c.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\t\t\tcookies := spew.Sdump(c.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\t\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==show_settings_ui).flow\").String(), \"%s\", body)\n\t\t\t\t\trequire.NotEmpty(t, gjson.Get(body, \"continue_with.#(action==set_ory_session_token).ory_session_token\").String(), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not break ui if empty code is submitted\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tc := testCase.GetClient(t)\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// This screen might get skipped in the backend if there is only one possible address to choose.\n\t\t\t\tif gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_select_address)\").Exists() {\n\t\t\t\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\t\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t\t}, http.StatusOK)\n\t\t\t\t}\n\n\t\t\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address1)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, address1, body)\n\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", \"\")\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address1)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// Not an error, just handle it as a code resend.\n\t\t\t\ttesthelpers.AssertMessage(t, body, text.NewRecoveryCodeRecoverySelectAddressSent(code.MaskAddress(address1)).Text)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should be able to resend the recovery code\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// This screen might get skipped in the backend if there is only one possible address to choose.\n\t\t\t\tif gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_select_address)\").Exists() {\n\t\t\t\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\t\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t\t}, http.StatusOK)\n\t\t\t\t}\n\n\t\t\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address1)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, address1, body)\n\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", \"\")\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address1) // Trigger resend.\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, address1, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_confirm_address).attributes.value\").String())\n\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, c, testCase.ClientType, address1, body, http.StatusOK)\n\t\t\t\texpectRedirectToSettings(t, c, testCase.ClientType, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to use first code after re-sending address\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tc := testCase.GetClient(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, c, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// This screen might get skipped in the backend if there is only one possible address to choose.\n\t\t\t\tif gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_select_address)\").Exists() {\n\t\t\t\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\t\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t\t}, http.StatusOK)\n\t\t\t\t}\n\n\t\t\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address1)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, address1, body)\n\n\t\t\t\tmessage1 := testhelpers.CourierExpectMessage(ctx, t, reg, address1, \"\")\n\t\t\t\tassert.Contains(t, message1.Body, \"Recover access to your account\")\n\t\t\t\trecoveryCode1 := testhelpers.CourierExpectCodeInMessage(t, message1, 1)\n\t\t\t\tassert.NotEmpty(t, recoveryCode1)\n\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", \"\")\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address1) // Trigger resend.\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\t\t\trequire.NotEmpty(t, action)\n\t\t\t\tassert.Equal(t, address1, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_confirm_address).attributes.value\").String())\n\t\t\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\n\t\t\t\t// Try to submit the old (expired) code.\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, c, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"code\", recoveryCode1)\n\t\t\t\t}, http.StatusOK)\n\t\t\t\ttesthelpers.AssertMessage(t, body, \"The recovery code is invalid or has already been used. Please try again.\")\n\n\t\t\t\t// Send the right code.\n\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, c, testCase.ClientType, address1, body, http.StatusOK)\n\t\t\t\texpectRedirectToSettings(t, c, testCase.ClientType, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should recover if post recovery hook is successful\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t\t\t})\n\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tcl := testCase.GetClient(t)\n\n\t\t\t\tbody := recoverHappyPath(t, cl, testCase.ClientType, address2, address1)\n\t\t\t\texpectRedirectToSettings(t, cl, testCase.ClientType, body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should not be able to recover if post recovery hook fails\", func(t *testing.T) {\n\t\tfor _, testCase := range flowTypeCases {\n\t\t\tt.Run(\"type=\"+testCase.ClientType.String(), func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecutePostRecoveryHook\": \"err\"}`)}})\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t\t\t})\n\n\t\t\t\taddress1 := testhelpers.RandomEmail()\n\t\t\t\taddress2 := testhelpers.RandomPhone()\n\t\t\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tbody := submitRecoveryFormInitial(t, cl, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\t// This screen might get skipped in the backend if there is only one possible address to choose.\n\t\t\t\tif gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_select_address)\").Exists() {\n\t\t\t\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\t\t\t\t\tbody = submitRecoveryFormSubsequent(t, cl, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t\t\t\t\tv.Set(\"recovery_address\", address2)\n\t\t\t\t\t}, http.StatusOK)\n\t\t\t\t}\n\n\t\t\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\t\t\t\tbody = submitRecoveryFormSubsequent(t, cl, body, testCase.ClientType, func(v url.Values) {\n\t\t\t\t\tv.Set(\"recovery_confirm_address\", address1)\n\t\t\t\t}, http.StatusOK)\n\n\t\t\t\tcheckRecoveryScreenAskForCode(t, address1, body)\n\n\t\t\t\tassert.Equal(t, address1, gjson.Get(body, \"ui.nodes.#(attributes.name==recovery_confirm_address).attributes.value\").String())\n\n\t\t\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\n\t\t\t\tinitialFlowId := gjson.Get(body, \"id\")\n\t\t\t\tswitch testCase.ClientType {\n\t\t\t\tcase RecoveryClientTypeBrowser:\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, address1, body, http.StatusSeeOther)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t\t\t\tcase RecoveryClientTypeSPA:\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, address1, body, http.StatusBadRequest)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\t\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\t\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t\t\t\tcase RecoveryClientTypeAPI:\n\t\t\t\t\tbody = extractCodeFromCourierAndSubmit(t, cl, testCase.ClientType, address1, body, http.StatusBadRequest)\n\t\t\t\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\t\t\t\t\trequire.Equal(t, \"err\", gjson.Get(body, \"error.message\").String(), \"%s\", body)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"choose different address - screens 2->3->2->4\", func(t *testing.T) {\n\t\taddress1 := testhelpers.RandomEmail()\n\t\taddress2 := testhelpers.RandomPhone()\n\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\tbody := submitRecoveryFormInitial(t, client, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", address1)\n\t\t}, http.StatusOK)\n\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\n\t\tbody = submitRecoveryFormSubsequent(t, client, body, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", address1)\n\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address2))\n\t\t}, http.StatusOK)\n\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\n\t\tbody = submitRecoveryFormSubsequent(t, client, body, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", address1)\n\t\t\tv.Set(\"screen\", \"previous\")\n\t\t}, http.StatusOK)\n\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\n\t\t// Choose another address\n\t\tbody = submitRecoveryFormSubsequent(t, client, body, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", address1)\n\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t}, http.StatusOK)\n\t\tcheckRecoveryScreenAskForCode(t, address1, body)\n\n\t\tbody = extractCodeFromCourierAndSubmit(t, client, RecoveryClientTypeBrowser, address1, body, http.StatusOK)\n\t\tassert.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\t})\n\n\tt.Run(\"choose different address - screens 2->4->2->3->4\", func(t *testing.T) {\n\t\taddress1 := testhelpers.RandomEmail()\n\t\taddress2 := testhelpers.RandomPhone()\n\t\tcreateIdentityToRecoverEmailAndPhone(t, reg, address1, address2)\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\tbody := submitRecoveryFormInitial(t, client, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", address1)\n\t\t}, http.StatusOK)\n\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\n\t\tbody = submitRecoveryFormSubsequent(t, client, body, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", address1)\n\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address1))\n\t\t}, http.StatusOK)\n\t\tcheckRecoveryScreenAskForCode(t, address1, body)\n\n\t\t// Choose another address\n\t\tbody = submitRecoveryFormSubsequent(t, client, body, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", address1)\n\t\t\tv.Set(\"screen\", \"previous\")\n\t\t}, http.StatusOK)\n\t\tcheckRecoveryScreenAskForRecoverySelectAddress(t, body)\n\n\t\tbody = submitRecoveryFormSubsequent(t, client, body, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", address1)\n\t\t\tv.Set(\"recovery_select_address\", code.AddressToHashBase64(address2))\n\t\t}, http.StatusOK)\n\t\tcheckRecoveryScreenAskForRecoveryConfirmAddress(t, body)\n\n\t\tbody = submitRecoveryFormSubsequent(t, client, body, RecoveryClientTypeBrowser, func(v url.Values) {\n\t\t\tv.Set(\"recovery_address\", address1)\n\t\t\tv.Set(\"recovery_confirm_address\", address2)\n\t\t}, http.StatusOK)\n\t\tcheckRecoveryScreenAskForCode(t, address2, body)\n\n\t\tbody = extractCodeFromCourierAndSubmit(t, client, RecoveryClientTypeBrowser, address2, body, http.StatusOK)\n\t\tassert.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\tgjson.Get(body, \"ui.messages.0.text\").String())\n\t})\n}\n\nfunc TestDisabledStrategy(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(defaultConfig),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeCodeAuth, false)),\n\t)\n\n\tpublicTS, adminTS := testhelpers.NewKratosServer(t, reg)\n\tadminSDK := testhelpers.NewSDKClient(adminTS)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tt.Run(\"role=admin\", func(t *testing.T) {\n\t\tt.Run(\"description=can not create recovery link when link method is disabled\", func(t *testing.T) {\n\t\t\tid := identity.Identity{Traits: identity.Traits(`{\"email\":\"recovery-endpoint-disabled@ory.sh\"}`)}\n\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(context.Background(),\n\t\t\t\t&id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\trl, _, err := adminSDK.IdentityAPI.\n\t\t\t\tCreateRecoveryLinkForIdentity(context.Background()).\n\t\t\t\tCreateRecoveryLinkForIdentityBody(kratos.CreateRecoveryLinkForIdentityBody{IdentityId: id.ID.String()}).\n\t\t\t\tExecute()\n\t\t\tassert.Nil(t, rl)\n\t\t\trequire.IsType(t, new(kratos.GenericOpenAPIError), err, \"%s\", err)\n\n\t\t\tbr, _ := err.(*kratos.GenericOpenAPIError)\n\t\t\tassert.Contains(t, string(br.Body()), \"This endpoint was disabled by system administrator\", \"%s\", br.Body())\n\t\t})\n\t})\n\n\tt.Run(\"role=public\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\n\t\tt.Run(\"description=can not recover an account by post request when code method is disabled\", func(t *testing.T) {\n\t\t\tf := testhelpers.PersistNewRecoveryFlow(t, recovery.Strategies{code.NewStrategy(reg)}, conf, reg)\n\t\t\tu := publicTS.URL + recovery.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\n\t\t\tres, err := c.PostForm(u, url.Values{\n\t\t\t\t\"email\":      {\"email@ory.sh\"},\n\t\t\t\t\"method\":     {\"code\"},\n\t\t\t\t\"csrf_token\": {f.CSRFToken},\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tb := ioutilx.MustReadAll(res.Body)\n\t\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\")\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/urlx\"\n)\n\nvar (\n\t_ registration.Strategy     = new(Strategy)\n\t_ registration.FormHydrator = new(Strategy)\n)\n\n// Update Registration Flow with Code Method\n//\n// swagger:model updateRegistrationFlowWithCodeMethod\ntype updateRegistrationFlowWithCodeMethod struct {\n\t// The identity's traits\n\t//\n\t// required: true\n\tTraits json.RawMessage `json:\"traits\" form:\"traits\"`\n\n\t// The OTP Code sent to the user\n\t//\n\t// required: false\n\tCode string `json:\"code\" form:\"code\"`\n\n\t// The CSRF Token\n\tCSRFToken string `json:\"csrf_token\" form:\"csrf_token\"`\n\n\t// Method to use\n\t//\n\t// This field must be set to `code` when using the code method.\n\t//\n\t// required: true\n\tMethod string `json:\"method\" form:\"method\"`\n\n\t// Resend restarts the flow with a new code\n\t//\n\t// required: false\n\tResend string `json:\"resend\" form:\"resend\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (p *updateRegistrationFlowWithCodeMethod) GetResend() string {\n\treturn p.Resend\n}\n\nfunc (s *Strategy) HandleRegistrationError(ctx context.Context, r *http.Request, f *registration.Flow, body *updateRegistrationFlowWithCodeMethod, err error) error {\n\tif errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\treturn err\n\t}\n\n\tif f != nil {\n\t\tif body != nil {\n\t\t\taction := f.AppendTo(urlx.AppendPaths(s.deps.Config().SelfPublicURL(ctx), registration.RouteSubmitFlow)).String()\n\t\t\tfor _, n := range container.NewFromJSON(action, node.DefaultGroup, body.Traits, \"traits\").Nodes {\n\t\t\t\t// we only set the value and not the whole field because we want to keep types from the initial form generation\n\t\t\t\tf.UI.Nodes.SetValueAttribute(n.ID(), n.Attributes.GetValue())\n\t\t\t}\n\t\t}\n\n\t\tf.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\t}\n\n\treturn err\n}\n\nfunc (s *Strategy) PopulateRegistrationMethod(r *http.Request, f *registration.Flow) error {\n\tif !s.deps.Config().SelfServiceCodeStrategy(r.Context()).PasswordlessEnabled {\n\t\treturn nil\n\t}\n\n\tf.GetUI().Nodes.Append(nodeSubmitRegistration())\n\n\tf.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodCredentials(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\tif !s.deps.Config().SelfServiceCodeStrategy(r.Context()).PasswordlessEnabled {\n\t\treturn nil\n\t}\n\n\tf.GetUI().Nodes.RemoveMatching(nodeRegistrationResendNode())\n\tf.GetUI().Nodes.RemoveMatching(nodeRegistrationSelectCredentialsNode())\n\tf.GetUI().Nodes.RemoveMatching(nodeContinueButton())\n\tf.GetUI().Nodes.RemoveMatching(nodeCodeInputFieldHidden())\n\tf.GetUI().Nodes.RemoveMatching(nodeCodeInputField())\n\n\tf.GetUI().Nodes.Append(nodeSubmitRegistration())\n\n\tf.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodProfile(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\tif !s.deps.Config().SelfServiceCodeStrategy(r.Context()).PasswordlessEnabled {\n\t\treturn nil\n\t}\n\n\tf.GetUI().Nodes.RemoveMatching(nodeSubmitRegistration())\n\tf.GetUI().Nodes.RemoveMatching(nodeRegistrationResendNode())\n\tf.GetUI().Nodes.RemoveMatching(nodeRegistrationSelectCredentialsNode())\n\tf.GetUI().Nodes.RemoveMatching(nodeContinueButton())\n\tf.GetUI().Nodes.RemoveMatching(nodeCodeInputFieldHidden())\n\tf.GetUI().Nodes.RemoveMatching(nodeCodeInputField())\n\n\tf.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\treturn nil\n}\n\nfunc (s *Strategy) validateTraits(ctx context.Context, traits json.RawMessage, i *identity.Identity) error {\n\ti.Traits = []byte(\"{}\")\n\tif gjson.ValidBytes(traits) {\n\t\ti.Traits = identity.Traits(traits)\n\t}\n\n\t// Validate the identity\n\tif err := s.deps.IdentityValidator().Validate(ctx, i); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) validateAndGetCredentialsFromTraits(ctx context.Context, i *identity.Identity, traits json.RawMessage) (*identity.Credentials, *identity.CredentialsCode, error) {\n\tif err := s.validateTraits(ctx, traits, i); err != nil {\n\t\treturn nil, nil, errors.WithStack(err)\n\t}\n\n\tcred, ok := i.GetCredentials(identity.CredentialsTypeCodeAuth)\n\tif !ok {\n\t\treturn nil, nil, errors.WithStack(schema.NewMissingIdentifierError())\n\t} else if len(strings.Join(cred.Identifiers, \"\")) == 0 {\n\t\treturn nil, nil, errors.WithStack(schema.NewMissingIdentifierError())\n\t}\n\n\tvar conf identity.CredentialsCode\n\tif len(cred.Config) > 0 {\n\t\tif err := json.Unmarshal(cred.Config, &conf); err != nil {\n\t\t\treturn nil, nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to unmarshal credentials config: %s\", err))\n\t\t}\n\t}\n\n\treturn cred, &conf, nil\n}\n\nfunc (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registration.Flow, i *identity.Identity) (err error) {\n\tctx, span := s.deps.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.code.Strategy.Register\")\n\tdefer otelx.End(span, &err)\n\n\tif err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.ID().String(), s.deps); err != nil {\n\t\treturn err\n\t}\n\n\tds, err := f.IdentitySchema.URL(ctx, s.deps.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar p updateRegistrationFlowWithCodeMethod\n\tif err := registration.DecodeBody(&p, r, registrationSchema, ds); err != nil {\n\t\treturn s.HandleRegistrationError(ctx, r, f, &p, err)\n\t}\n\n\tf.TransientPayload = p.TransientPayload\n\n\tif err := flow.EnsureCSRF(s.deps, r, f.Type, s.deps.Config().DisableAPIFlowEnforcement(ctx), s.deps.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn s.HandleRegistrationError(ctx, r, f, &p, err)\n\t}\n\n\t// By Default the flow should be in the 'choose method' state.\n\tSetDefaultFlowState(f, p.Resend)\n\n\tswitch f.GetState() {\n\tcase flow.StateChooseMethod:\n\t\treturn s.HandleRegistrationError(ctx, r, f, &p, s.registrationSendEmail(ctx, w, r, f, &p, i))\n\tcase flow.StateEmailSent:\n\t\treturn s.HandleRegistrationError(ctx, r, f, &p, s.registrationVerifyCode(ctx, f, &p, i))\n\tcase flow.StatePassedChallenge:\n\t\treturn s.HandleRegistrationError(ctx, r, f, &p, errors.WithStack(schema.NewNoRegistrationStrategyResponsible()))\n\t}\n\n\treturn s.HandleRegistrationError(ctx, r, f, &p, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unexpected flow state: %s\", f.GetState())))\n}\n\nfunc (s *Strategy) registrationSendEmail(ctx context.Context, w http.ResponseWriter, r *http.Request, f *registration.Flow, p *updateRegistrationFlowWithCodeMethod, i *identity.Identity) (err error) {\n\tctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.code.Strategy.registrationSendEmail\")\n\tdefer otelx.End(span, &err)\n\n\tif len(p.Traits) == 0 {\n\t\treturn errors.WithStack(schema.NewRequiredError(\"#/traits\", \"traits\"))\n\t}\n\n\t// Create the Registration code\n\n\t// Step 1: validate the identity's traits\n\t_, conf, err := s.validateAndGetCredentialsFromTraits(ctx, i, p.Traits)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Step 2: Delete any previous registration codes for this flow ID\n\tif err := s.deps.RegistrationCodePersister().DeleteRegistrationCodesOfFlow(ctx, f.ID); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\t// Step 3: Get the identity email and send the code\n\tvar addresses []Address\n\tfor _, address := range conf.Addresses {\n\t\taddresses = append(addresses, Address{To: address.Address, Via: address.Channel})\n\t}\n\n\t// kratos only supports `email` identifiers at the moment with the code method\n\t// this is validated in the identity validation step above\n\tif err := s.deps.CodeSender().SendCode(ctx, f, i, r.Header, addresses...); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\t// sets the flow state to code sent\n\tf.SetState(flow.NextState(f.GetState()))\n\n\t// Step 4: Generate the UI for the `code` input form\n\t// re-initialize the UI with a \"clean\" new state\n\t// this should also provide a \"resend\" button and an option to change the email address\n\tif err := s.NewCodeUINodes(r, f, p.Traits); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tf.Active = identity.CredentialsTypeCodeAuth\n\tif err := s.deps.RegistrationFlowPersister().UpdateRegistrationFlow(ctx, f); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif f.OAuth2LoginChallenge != \"\" {\n\t\thlr, err := s.deps.Hydra().GetLoginRequest(ctx, string(f.OAuth2LoginChallenge))\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tf.HydraLoginRequest = hlr\n\t}\n\n\tif x.IsJSONRequest(r) {\n\t\ts.deps.Writer().WriteCode(w, r, http.StatusBadRequest, f)\n\t} else {\n\t\thttp.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowRegistrationUI(ctx)).String(), http.StatusSeeOther)\n\t}\n\n\t// we return an error to the flow handler so that it does not continue execution of the hooks.\n\t// we are not done with the registration flow yet. The user needs to verify the code and then we need to persist the identity.\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) registrationVerifyCode(ctx context.Context, f *registration.Flow, p *updateRegistrationFlowWithCodeMethod, i *identity.Identity) (err error) {\n\tctx, span := s.deps.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.code.Strategy.registrationVerifyCode\")\n\tdefer otelx.End(span, &err)\n\n\tif len(p.Code) == 0 {\n\t\treturn errors.WithStack(schema.NewRequiredError(\"#/code\", \"code\"))\n\t}\n\n\tif len(p.Traits) == 0 {\n\t\treturn errors.WithStack(schema.NewRequiredError(\"#/traits\", \"traits\"))\n\t}\n\n\t// Step 1: Re-validate the identity's traits\n\t// this is important since the client could have switched out the identity's traits\n\t// this method also returns the credentials for a temporary identity\n\tcred, _, err := s.validateAndGetCredentialsFromTraits(ctx, i, p.Traits)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Step 2: Check if the flow traits match the identity traits\n\tfor _, n := range container.NewFromJSON(\"\", node.DefaultGroup, p.Traits, \"traits\").Nodes {\n\t\tui := f.GetUI()\n\t\tif ui == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tnodes := ui.GetNodes()\n\t\tif nodes == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tnode := nodes.Find(n.ID())\n\t\tif node == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tnodeAttrs := node.Attributes\n\t\tif nodeAttrs == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tnAttrs := n.Attributes\n\t\tif nAttrs == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif nodeAttrs.GetValue() != nAttrs.GetValue() {\n\t\t\treturn errors.WithStack(schema.NewTraitsMismatch())\n\t\t}\n\t}\n\n\t// Step 3: Attempt to use the code\n\tregistrationCode, err := s.deps.RegistrationCodePersister().UseRegistrationCode(ctx, f.ID, p.Code, cred.Identifiers...)\n\tif err != nil {\n\t\tif errors.Is(err, ErrCodeNotFound) {\n\t\t\treturn errors.WithStack(schema.NewRegistrationCodeInvalid())\n\t\t}\n\t\treturn errors.WithStack(err)\n\t}\n\n\t// Step 4: Verify the address\n\tif err := s.verifyAddress(ctx, i, Address{\n\t\tTo:  registrationCode.Address,\n\t\tVia: registrationCode.AddressType,\n\t}, false); err != nil {\n\t\treturn err\n\t}\n\n\t// since nothing has errored yet, we can assume that the code is correct\n\t// and we can update the registration flow\n\tf.SetState(flow.NextState(f.GetState()))\n\n\tif err := s.deps.RegistrationFlowPersister().UpdateRegistrationFlow(ctx, f); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_registration_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"regexp\"\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/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\toryClient \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\ntype state struct {\n\tflowID         string\n\tclient         *http.Client\n\temail          string\n\ttestServer     *httptest.Server\n\tresultIdentity *identity.Identity\n\tbody           string\n}\n\nfunc TestRegistrationCodeStrategyDisabled(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/code.identity.schema.json\")),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, false)),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeCodeAuth, false)),\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tfmt.Sprintf(\"%s.%s.passwordless_enabled\", config.ViperKeySelfServiceStrategyConfig, identity.CredentialsTypeCodeAuth): false,\n\t\t\tconfig.ViperKeySelfServiceRegistrationEnableLegacyOneStep:                                                             true,\n\t\t}),\n\t)\n\n\t_ = testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublic, _, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\tclient := testhelpers.NewClientWithCookies(t)\n\tresp, err := client.Get(public.URL + registration.RouteInitBrowserFlow)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\n\tbody, err := io.ReadAll(resp.Body)\n\trequire.NoError(t, err)\n\trequire.Falsef(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.value==code)\").Exists(), \"%s\", body)\n\n\t// attempt to still submit the code form even though it doesn't exist\n\n\tpayload := strings.NewReader(url.Values{\n\t\t\"csrf_token\":   {gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()},\n\t\t\"method\":       {\"code\"},\n\t\t\"traits.email\": {testhelpers.RandomEmail()},\n\t}.Encode())\n\treq, err := http.NewRequestWithContext(t.Context(), \"POST\", public.URL+registration.RouteSubmitFlow+\"?flow=\"+gjson.GetBytes(body, \"id\").String(), payload)\n\trequire.NoError(t, err)\n\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"Accept\", \"application/json\")\n\n\tresp, err = client.Do(req)\n\trequire.NoError(t, err)\n\trequire.Equal(t, http.StatusNotFound, resp.StatusCode)\n\tbody, err = io.ReadAll(resp.Body)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"This endpoint was disabled by system administrator. Please check your url or contact the system administrator to enable it.\", gjson.GetBytes(body, \"error.reason\").String())\n}\n\nfunc TestRegistrationCodeStrategy(t *testing.T) {\n\tt.Parallel()\n\n\ttype ApiType string\n\n\tconst (\n\t\tApiTypeBrowser ApiType = \"browser\"\n\t\tApiTypeSPA     ApiType = \"spa\"\n\t\tApiTypeNative  ApiType = \"api\"\n\t)\n\n\tsetup := func(ctx context.Context, t *testing.T, cfgOpts ...configx.OptionModifier) (*config.Config, *driver.RegistryDefault, *httptest.Server) {\n\t\tconf, reg := pkg.NewFastRegistryWithMocks(t, append([]configx.OptionModifier{\n\t\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/code.identity.schema.json\")),\n\t\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, false)),\n\t\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeCodeAuth, true)),\n\t\t\tconfigx.WithValues(map[string]any{\n\t\t\t\tfmt.Sprintf(\"%s.%s.passwordless_enabled\", config.ViperKeySelfServiceStrategyConfig, identity.CredentialsTypeCodeAuth): true,\n\t\t\t\tconfig.ViperKeySelfServiceRegistrationAfter + \".code.hooks\": []map[string]any{\n\t\t\t\t\t{\"hook\": \"session\"},\n\t\t\t\t},\n\t\t\t\tconfig.ViperKeySelfServiceRegistrationEnableLegacyOneStep: true,\n\t\t\t}),\n\t\t}, cfgOpts...)...)\n\n\t\t_ = testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\t\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\t\tpublic, _, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\t\treturn conf, reg, public\n\t}\n\n\tcreateRegistrationFlowInternal := func(ctx context.Context, t *testing.T, public *httptest.Server, apiType ApiType, identitySchema string) *state {\n\t\tt.Helper()\n\n\t\tvar client *http.Client\n\n\t\tif apiType == ApiTypeNative {\n\t\t\tclient = &http.Client{}\n\t\t} else {\n\t\t\tclient = testhelpers.NewClientWithCookies(t)\n\t\t}\n\n\t\tclient.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\n\t\tvar clientInit *oryClient.RegistrationFlow\n\t\tif apiType == ApiTypeNative {\n\t\t\tclientInit = testhelpers.InitializeRegistrationFlowViaAPICtx(ctx, t, client, public, testhelpers.InitFlowWithIdentitySchema(identitySchema))\n\t\t} else {\n\t\t\tclientInit = testhelpers.InitializeRegistrationFlowViaBrowserCtx(ctx, t, client, public, apiType == ApiTypeSPA, false, false, testhelpers.InitFlowWithIdentitySchema(identitySchema))\n\t\t}\n\n\t\tbody, err := json.Marshal(clientInit)\n\t\trequire.NoError(t, err)\n\n\t\tcsrfToken := gjson.GetBytes(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\t\tif apiType == ApiTypeNative {\n\t\t\trequire.Emptyf(t, csrfToken, \"expected an empty value for csrf_token on native api flows but got %s\", body)\n\t\t} else {\n\t\t\trequire.NotEmpty(t, csrfToken)\n\t\t}\n\n\t\trequire.Truef(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.email)\").Exists(), \"%s\", body)\n\t\trequire.Truef(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.value==code)\").Exists(), \"%s\", body)\n\n\t\treturn &state{\n\t\t\tclient:     client,\n\t\t\tflowID:     clientInit.GetId(),\n\t\t\ttestServer: public,\n\t\t}\n\t}\n\n\tcreateRegistrationFlow := func(ctx context.Context, t *testing.T, public *httptest.Server, apiType ApiType) *state {\n\t\treturn createRegistrationFlowInternal(ctx, t, public, apiType, \"\")\n\t}\n\n\tcreateRegistrationFlowWithIdentity := func(ctx context.Context, t *testing.T, public *httptest.Server, apiType ApiType, identitySchema string) *state {\n\t\treturn createRegistrationFlowInternal(ctx, t, public, apiType, identitySchema)\n\t}\n\n\ttype onSubmitAssertion func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response)\n\n\tregisterNewUser := func(ctx context.Context, t *testing.T, s *state, apiType ApiType, submitAssertion onSubmitAssertion) *state {\n\t\tt.Helper()\n\n\t\tif s.email == \"\" {\n\t\t\ts.email = testhelpers.RandomEmail()\n\t\t}\n\n\t\trf, resp, err := testhelpers.NewSDKCustomClient(s.testServer, s.client).FrontendAPI.GetRegistrationFlow(context.Background()).Id(s.flowID).Execute()\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(rf.Ui.Nodes)\n\t\tvalues.Set(\"traits.email\", s.email)\n\t\tvalues.Set(\"traits.tos\", \"1\")\n\t\tvalues.Set(\"method\", \"code\")\n\n\t\tbody, resp := testhelpers.RegistrationMakeRequest(t, apiType == ApiTypeNative, apiType == ApiTypeSPA, rf, s.client, testhelpers.EncodeFormAsJSON(t, apiType == ApiTypeNative, values))\n\t\ts.body = body\n\n\t\tif submitAssertion != nil {\n\t\t\tsubmitAssertion(ctx, t, s, body, resp)\n\t\t\treturn s\n\t\t}\n\t\tt.Logf(\"%v\", body)\n\n\t\tif apiType == ApiTypeBrowser {\n\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t\t} else {\n\t\t\trequire.EqualValues(t, http.StatusBadRequest, resp.StatusCode)\n\t\t}\n\n\t\tcsrfToken := gjson.Get(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\t\tif apiType == ApiTypeNative {\n\t\t\tassert.Emptyf(t, csrfToken, \"expected an empty value for csrf_token on native api flows but got %s\", body)\n\t\t} else {\n\t\t\tassert.NotEmptyf(t, csrfToken, \"%s\", body)\n\t\t}\n\t\trequire.Equal(t, s.email, gjson.Get(body, \"ui.nodes.#(attributes.name==traits.email).attributes.value\").String())\n\n\t\treturn s\n\t}\n\n\tsubmitOTP := func(ctx context.Context, t *testing.T, reg *driver.RegistryDefault, s *state, vals func(v *url.Values), apiType ApiType, submitAssertion onSubmitAssertion) *state {\n\t\tt.Helper()\n\n\t\trf, resp, err := testhelpers.NewSDKCustomClient(s.testServer, s.client).FrontendAPI.GetRegistrationFlow(context.Background()).Id(s.flowID).Execute()\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(rf.Ui.Nodes)\n\t\t// the sdk to values always adds resend which isn't what we always need here.\n\t\t// so we delete it here.\n\t\t// the custom vals func can add it again if needed.\n\t\tvalues.Del(\"resend\")\n\t\tvalues.Set(\"traits.email\", s.email)\n\t\tvalues.Set(\"traits.tos\", \"1\")\n\t\tvals(&values)\n\n\t\tbody, resp := testhelpers.RegistrationMakeRequest(t, apiType == ApiTypeNative, apiType == ApiTypeSPA, rf, s.client, testhelpers.EncodeFormAsJSON(t, apiType == ApiTypeNative, values))\n\t\ts.body = body\n\n\t\tif submitAssertion != nil {\n\t\t\tsubmitAssertion(ctx, t, s, body, resp)\n\t\t\treturn s\n\t\t}\n\n\t\trequire.Equal(t, http.StatusOK, resp.StatusCode, body)\n\n\t\tverifiableAddress, err := reg.PrivilegedIdentityPool().FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, s.email)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, strings.ToLower(s.email), verifiableAddress.Value)\n\n\t\tid, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(ctx, verifiableAddress.IdentityID)\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, id.ID)\n\n\t\t_, ok := id.GetCredentials(identity.CredentialsTypeCodeAuth)\n\t\trequire.True(t, ok)\n\n\t\ts.resultIdentity = id\n\t\treturn s\n\t}\n\n\tt.Run(\"test=different flows on the same configurations\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tctx := context.Background()\n\t\tconf, reg, public := setup(ctx, t)\n\n\t\tfor _, tc := range []struct {\n\t\t\td       string\n\t\t\tapiType ApiType\n\t\t}{\n\t\t\t{\n\t\t\t\td:       \"SPA client\",\n\t\t\t\tapiType: ApiTypeSPA,\n\t\t\t},\n\t\t\t{\n\t\t\t\td:       \"Browser client\",\n\t\t\t\tapiType: ApiTypeBrowser,\n\t\t\t},\n\t\t\t{\n\t\t\t\td:       \"Native client\",\n\t\t\t\tapiType: ApiTypeNative,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"flow=\"+tc.d, func(t *testing.T) {\n\t\t\t\tt.Run(\"case=should be able to register with code identity credentials\", func(t *testing.T) {\n\t\t\t\t\tctx := context.Background()\n\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\tstate := createRegistrationFlow(ctx, t, public, tc.apiType)\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\tstate = registerNewUser(ctx, t, state, tc.apiType, nil)\n\n\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, state.email, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Complete your account registration with the following code\")\n\n\t\t\t\t\tregistrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, registrationCode)\n\n\t\t\t\t\t// 3. Submit OTP\n\t\t\t\t\tstate = submitOTP(ctx, t, reg, state, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", registrationCode)\n\t\t\t\t\t}, tc.apiType, nil)\n\n\t\t\t\t\tswitch tc.apiType {\n\t\t\t\t\tcase ApiTypeSPA:\n\t\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(state.body, \"continue_with.0.action\").String(), \"%s\", state.body)\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(state.body, \"continue_with.0.redirect_browser_to\").String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%s\", state.body)\n\t\t\t\t\tcase ApiTypeBrowser:\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(state.body, \"continue_with\").Array(), \"%s\", state.body)\n\t\t\t\t\tcase ApiTypeNative:\n\t\t\t\t\t\tassert.NotContains(t, gjson.Get(state.body, \"continue_with\").Raw, string(flow.ContinueWithActionRedirectBrowserToString), \"%s\", state.body)\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should normalize email address on sign up\", func(t *testing.T) {\n\t\t\t\t\tctx := context.Background()\n\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\tstate := createRegistrationFlow(ctx, t, public, tc.apiType)\n\t\t\t\t\tsourceMail := testhelpers.RandomEmail()\n\t\t\t\t\tstate.email = strings.ToUpper(sourceMail)\n\t\t\t\t\tassert.NotEqual(t, sourceMail, state.email)\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\tstate = registerNewUser(ctx, t, state, tc.apiType, nil)\n\n\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, sourceMail, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Complete your account registration with the following code\")\n\n\t\t\t\t\tregistrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, registrationCode)\n\n\t\t\t\t\t// 3. Submit OTP\n\t\t\t\t\tstate = submitOTP(ctx, t, reg, state, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", registrationCode)\n\t\t\t\t\t}, tc.apiType, nil)\n\n\t\t\t\t\tcreds, ok := state.resultIdentity.GetCredentials(identity.CredentialsTypeCodeAuth)\n\t\t\t\t\trequire.True(t, ok)\n\t\t\t\t\trequire.Len(t, creds.Identifiers, 1)\n\t\t\t\t\tassert.Equal(t, sourceMail, creds.Identifiers[0])\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should be able to resend the code\", func(t *testing.T) {\n\t\t\t\t\tctx := context.Background()\n\n\t\t\t\t\ts := createRegistrationFlow(ctx, t, public, tc.apiType)\n\n\t\t\t\t\ts = registerNewUser(ctx, t, s, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, resp.StatusCode)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcsrfToken := gjson.Get(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\t\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\t\trequire.Empty(t, csrfToken, \"expected the csrf_token to be empty but got %s\", body)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.NotEmptyf(t, csrfToken, \"expected the csrf_token to exist but got %s\", body)\n\t\t\t\t\t\t}\n\t\t\t\t\t\trequire.Equal(t, s.email, gjson.Get(body, \"ui.nodes.#(attributes.name==traits.email).attributes.value\").String())\n\n\t\t\t\t\t\tattr := gjson.Get(body, \"ui.nodes.#(attributes.name==method)#\").String()\n\t\t\t\t\t\trequire.NotEmpty(t, attr)\n\n\t\t\t\t\t\tval := gjson.Get(attr, \"#(attributes.type==hidden).attributes.value\").String()\n\t\t\t\t\t\trequire.Equal(t, \"code\", val, body)\n\t\t\t\t\t})\n\n\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Complete your account registration with the following code\")\n\n\t\t\t\t\tregistrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, registrationCode)\n\n\t\t\t\t\t// resend code\n\t\t\t\t\ts = submitOTP(ctx, t, reg, s, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"resend\", \"code\")\n\t\t\t\t\t}, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, resp.StatusCode)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcsrfToken := gjson.Get(body, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String()\n\t\t\t\t\t\tif tc.apiType == ApiTypeNative {\n\t\t\t\t\t\t\tassert.Emptyf(t, csrfToken, \"expected an empty value for csrf_token on native api flows but got %s\", body)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.NotEmptyf(t, csrfToken, \"expected to find the csrf_token but got %s\", body)\n\t\t\t\t\t\t}\n\t\t\t\t\t\trequire.Containsf(t, gjson.Get(body, \"ui.messages\").String(), \"A code has been sent to the address(es) you provided\", \"%s\", body)\n\t\t\t\t\t})\n\n\t\t\t\t\t// get the new code from email\n\t\t\t\t\tmessage = testhelpers.CourierExpectMessage(ctx, t, reg, s.email, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Complete your account registration with the following code\")\n\n\t\t\t\t\tregistrationCode2 := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, registrationCode2)\n\n\t\t\t\t\trequire.NotEqual(t, registrationCode, registrationCode2)\n\n\t\t\t\t\t// try submit old code\n\t\t\t\t\ts = submitOTP(ctx, t, reg, s, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", registrationCode)\n\t\t\t\t\t}, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t}\n\t\t\t\t\t\trequire.Contains(t, gjson.Get(body, \"ui.messages\").String(), \"The registration code is invalid or has already been used. Please try again\")\n\t\t\t\t\t})\n\n\t\t\t\t\tsubmitOTP(ctx, t, reg, s, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", registrationCode2)\n\t\t\t\t\t}, tc.apiType, nil)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=swapping out traits should not be possible on code submit\", func(t *testing.T) {\n\t\t\t\t\tctx := context.Background()\n\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\ts := createRegistrationFlow(ctx, t, public, tc.apiType)\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\ts = registerNewUser(ctx, t, s, tc.apiType, nil)\n\n\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Complete your account registration with the following code\")\n\n\t\t\t\t\tregistrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, registrationCode)\n\n\t\t\t\t\ts.email = \"not-\" + s.email // swap out email\n\t\t\t\t\t// 3. Submit OTP\n\t\t\t\t\tsubmitOTP(ctx, t, reg, s, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", registrationCode)\n\t\t\t\t\t}, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t}\n\t\t\t\t\t\trequire.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"The provided traits do not match the traits previously associated with this flow.\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=swapping out traits that aren't strings should not be possible on code submit\", func(t *testing.T) {\n\t\t\t\t\tctx := context.Background()\n\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\ts := createRegistrationFlow(ctx, t, public, tc.apiType)\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\ts = registerNewUser(ctx, t, s, tc.apiType, nil)\n\n\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Complete your account registration\")\n\n\t\t\t\t\tregistrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, registrationCode)\n\n\t\t\t\t\t// 3. Submit OTP\n\t\t\t\t\tsubmitOTP(ctx, t, reg, s, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", registrationCode)\n\t\t\t\t\t\tv.Set(\"traits.tos\", \"0\")\n\t\t\t\t\t}, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t}\n\t\t\t\t\t\trequire.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"The provided traits do not match the traits previously associated with this flow.\")\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=code should not be able to use more than 5 times\", func(t *testing.T) {\n\t\t\t\t\tctx := context.Background()\n\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\ts := createRegistrationFlow(ctx, t, public, tc.apiType)\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\ts = registerNewUser(ctx, t, s, tc.apiType, nil)\n\n\t\t\t\t\terr := reg.Persister().Transaction(ctx, func(ctx context.Context, connection *pop.Connection) error {\n\t\t\t\t\t\tcount, err := connection.RawQuery(fmt.Sprintf(\"SELECT * FROM %s WHERE selfservice_registration_flow_id = ?\", new(code.RegistrationCode).TableName(ctx)), uuid.FromStringOrNil(s.flowID)).Count(new(code.RegistrationCode))\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\trequire.Equal(t, 1, count)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t})\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tfor i := 0; i < 5; i++ {\n\t\t\t\t\t\ts = submitOTP(ctx, t, reg, s, func(v *url.Values) {\n\t\t\t\t\t\t\tv.Set(\"code\", \"111111\")\n\t\t\t\t\t\t}, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\trequire.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"The registration code is invalid or has already been used\")\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\ts = submitOTP(ctx, t, reg, s, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", \"111111\")\n\t\t\t\t\t}, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode, \"%s\", body)\n\t\t\t\t\t\t}\n\t\t\t\t\t\trequire.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"The request was submitted too often.\")\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"test=cases with different configs\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tctx := context.Background()\n\t\tconf, reg, public := setup(ctx, t)\n\n\t\tfor _, tc := range []struct {\n\t\t\td       string\n\t\t\tapiType ApiType\n\t\t}{\n\t\t\t{\n\t\t\t\td:       \"SPA client\",\n\t\t\t\tapiType: ApiTypeSPA,\n\t\t\t},\n\t\t\t{\n\t\t\t\td:       \"Browser client\",\n\t\t\t\tapiType: ApiTypeBrowser,\n\t\t\t},\n\t\t\t{\n\t\t\t\td:       \"Native client\",\n\t\t\t\tapiType: ApiTypeNative,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"test=\"+tc.d, func(t *testing.T) {\n\t\t\t\tt.Run(\"case=should fail when schema does not contain the `code` extension\", func(t *testing.T) {\n\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/no-code.schema.json\")\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, false)\n\n\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeyCodeConfigMissingCredentialFallbackEnabled, true)\n\t\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/code.identity.schema.json\")\n\t\t\t\t\t})\n\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\ts := createRegistrationFlow(ctx, t, public, tc.apiType)\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\tregisterNewUser(ctx, t, s, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\t// we expect a redirect to the registration page with the flow id\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\t\trequire.Equal(t, conf.SelfServiceFlowRegistrationUI(ctx).Path, resp.Request.URL.Path)\n\t\t\t\t\t\t\trf, resp, err := testhelpers.NewSDKCustomClient(public, s.client).FrontendAPI.GetRegistrationFlow(ctx).Id(resp.Request.URL.Query().Get(\"flow\")).Execute()\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\t\tbody, err := json.Marshal(rf)\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\trequire.Contains(t, gjson.GetBytes(body, \"ui.messages\").String(), \"Could not find any login identifiers\")\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusBadRequest, resp.StatusCode, \"%v\", body)\n\t\t\t\t\t\t\trequire.Contains(t, gjson.Get(body, \"ui.messages\").String(), \"Could not find any login identifiers\")\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should have verifiable address even if after session hook is disabled\", func(t *testing.T) {\n\t\t\t\t\t// disable the after session hook\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".code.hooks\", []map[string]interface{}{})\n\n\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".code.hooks\", []map[string]interface{}{\n\t\t\t\t\t\t\t{\"hook\": \"session\"},\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\tstate := createRegistrationFlow(ctx, t, public, tc.apiType)\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\tstate = registerNewUser(ctx, t, state, tc.apiType, nil)\n\n\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, state.email, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Complete your account registration with the following code\")\n\n\t\t\t\t\tregistrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, registrationCode)\n\n\t\t\t\t\t// 3. Submit OTP\n\t\t\t\t\tsubmitOTP(ctx, t, reg, state, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", registrationCode)\n\t\t\t\t\t}, tc.apiType, nil)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=code should expire\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.config.lifespan\", \"10ns\")\n\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.config.lifespan\", \"1h\")\n\t\t\t\t\t})\n\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\ts := createRegistrationFlow(ctx, t, public, tc.apiType)\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\ts = registerNewUser(ctx, t, s, tc.apiType, nil)\n\n\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, s.email, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Complete your account registration with the following code\")\n\n\t\t\t\t\tregistrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, registrationCode)\n\n\t\t\t\t\tsubmitOTP(ctx, t, reg, s, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", registrationCode)\n\t\t\t\t\t}, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\t// with browser clients we redirect back to the UI with a new flow id as a query parameter\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\t\trequire.Equal(t, conf.SelfServiceFlowRegistrationUI(ctx).Path, resp.Request.URL.Path)\n\t\t\t\t\t\t\trequire.NotEqual(t, s.flowID, resp.Request.URL.Query().Get(\"flow\"))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.Equal(t, http.StatusGone, resp.StatusCode)\n\t\t\t\t\t\t\tassert.Regexpf(t, regexp.MustCompile(`The self-service flow expired 0\\.0\\d minutes ago, initialize a new one\\.`), gjson.Get(body, \"error.reason\").Str, \"%s\", body)\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\tt.Run(\"test=multi-schema select\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tctx := context.Background()\n\t\tconf, reg, public := setup(ctx, t, configx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeyIdentitySchemas: config.Schemas{\n\t\t\t\t{ID: \"code\", URL: \"file://./stub/code.identity.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t{ID: \"no-code\", URL: \"file://stub/no-code.schema.json\", SelfserviceSelectable: true},\n\t\t\t},\n\t\t\tconfig.ViperKeyDefaultIdentitySchemaID: \"no-code\",\n\t\t}))\n\n\t\tfor _, tc := range []struct {\n\t\t\td       string\n\t\t\tapiType ApiType\n\t\t}{\n\t\t\t{\n\t\t\t\td:       \"SPA client\",\n\t\t\t\tapiType: ApiTypeSPA,\n\t\t\t},\n\t\t\t{\n\t\t\t\td:       \"Browser client\",\n\t\t\t\tapiType: ApiTypeBrowser,\n\t\t\t},\n\t\t\t{\n\t\t\t\td:       \"Native client\",\n\t\t\t\tapiType: ApiTypeNative,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"flow=\"+tc.d, func(t *testing.T) {\n\t\t\t\tt.Run(\"case=should be able to register with code identity credentials\", func(t *testing.T) {\n\t\t\t\t\tctx := context.Background()\n\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\tstate := createRegistrationFlowWithIdentity(ctx, t, public, tc.apiType, \"code\")\n\t\t\t\t\tstate.email = testhelpers.RandomEmail()\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\tstate = registerNewUser(ctx, t, state, tc.apiType, nil)\n\n\t\t\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, state.email, \"Use code\")\n\t\t\t\t\tassert.Contains(t, message.Body, \"Complete your account registration with the following code\")\n\n\t\t\t\t\tregistrationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\t\t\t\t\tassert.NotEmpty(t, registrationCode)\n\n\t\t\t\t\t// 3. Submit OTP\n\t\t\t\t\tstate = submitOTP(ctx, t, reg, state, func(v *url.Values) {\n\t\t\t\t\t\tv.Set(\"code\", registrationCode)\n\t\t\t\t\t}, tc.apiType, nil)\n\n\t\t\t\t\tswitch tc.apiType {\n\t\t\t\t\tcase ApiTypeSPA:\n\t\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(state.body, \"continue_with.0.action\").String(), \"%s\", state.body)\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(state.body, \"continue_with.0.redirect_browser_to\").String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%s\", state.body)\n\t\t\t\t\tcase ApiTypeBrowser:\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(state.body, \"continue_with\").Array(), \"%s\", state.body)\n\t\t\t\t\tcase ApiTypeNative:\n\t\t\t\t\t\tassert.NotContains(t, gjson.Get(state.body, \"continue_with\").Raw, string(flow.ContinueWithActionRedirectBrowserToString), \"%s\", state.body)\n\t\t\t\t\t}\n\n\t\t\t\t\tidentity, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypeCodeAuth, state.email)\n\t\t\t\t\trequire.NoError(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t\tassert.NotEmpty(t, identity.ID, \"%s\", identity.ID)\n\t\t\t\t\tassert.Equal(t, state.email, gjson.Get(identity.Traits.String(), \"email\").String(), \"%s\", identity.Traits.String())\n\t\t\t\t\tassert.Equal(t, \"code\", identity.SchemaID, \"%s\", identity.SchemaID)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=registration should fail with invalid form data\", func(t *testing.T) {\n\t\t\t\t\t// 1. Initiate flow\n\t\t\t\t\ts := createRegistrationFlowWithIdentity(t.Context(), t, public, tc.apiType, \"code\")\n\t\t\t\t\ts.email = \"invalidemail\"\n\n\t\t\t\t\t// 2. Submit Identifier (email)\n\t\t\t\t\tregisterNewUser(t.Context(), t, s, tc.apiType, func(ctx context.Context, t *testing.T, s *state, body string, resp *http.Response) {\n\t\t\t\t\t\tif tc.apiType == ApiTypeBrowser {\n\t\t\t\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, resp.StatusCode)\n\t\t\t\t\t\t}\n\t\t\t\t\t\trequire.Equal(t, int64(4000040), gjson.Get(body, \"ui.nodes.#(attributes.name==traits.email).messages.0.id\").Int(), \"%s\", body)\n\t\t\t\t\t\trequire.Equal(t, \"Enter a valid email address\", gjson.Get(body, \"ui.nodes.#(attributes.name==traits.email).messages.0.text\").String(), \"%s\", body)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestPopulateRegistrationMethod(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://stub/code.identity.schema.json\")),\n\t\tconfigx.WithValue(fmt.Sprintf(\"%s.%s.passwordless_enabled\", config.ViperKeySelfServiceStrategyConfig, identity.CredentialsTypeCodeAuth), true),\n\t)\n\n\ts, err := reg.AllRegistrationStrategies().Strategy(identity.CredentialsTypeCodeAuth)\n\trequire.NoError(t, err)\n\n\tfh, ok := s.(registration.FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f node.Nodes) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f, snapshotx.ExceptNestedKeys(\"nonce\", \"src\"))\n\t}\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *registration.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/registration/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := registration.NewFlow(reg.Config(), time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateRegistrationMethod\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethod(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodProfile\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodCredentials\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=idempotency\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\n\t\tvar snapshots []node.Nodes\n\n\t\tt.Run(\"case=1\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=2\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=3\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=4\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=evaluate\", func(t *testing.T) {\n\t\t\tassertx.EqualAsJSON(t, snapshots[0], snapshots[2])\n\t\t\tassertx.EqualAsJSON(t, snapshots[1], snapshots[3])\n\t\t})\n\t})\n}\n\nfunc TestCodeRegistrationWithLoginChallenge(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeCodeAuth), map[string]interface{}{\n\t\t\t\"enabled\":              true,\n\t\t\t\"passwordless_enabled\": true,\n\t\t}),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/code.identity.schema.json\")),\n\t)\n\treg.SetHydra(hydra.NewFake())\n\treg.WithCSRFTokenGenerator(nosurfx.FakeCSRFTokenGenerator)\n\ts := code.NewStrategy(reg)\n\n\tloginChallenge := hydra.FakeValidLoginChallenge\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *registration.Flow) {\n\t\tt.Helper()\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/registration/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tf, err := registration.NewFlow(reg.Config(), time.Minute, nosurfx.FakeCSRFToken, r, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, reg.RegistrationFlowPersister().CreateRegistrationFlow(ctx, f))\n\t\treturn r, f\n\t}\n\n\tt.Run(\"case=fetches login challenge on code input state\", func(t *testing.T) {\n\t\t_, f := newFlow(t.Context(), t)\n\t\tf.OAuth2LoginChallenge = sqlxx.NullString(loginChallenge)\n\t\ti := identity.NewIdentity(f.IdentitySchema.ID(t.Context(), reg.Config()))\n\t\temail := testhelpers.RandomEmail()\n\n\t\tbody := gjson.Parse(`{\n\t\t\t\"method\": \"code\",\n\t\t\t\"traits.email\": \"` + email + `\",\n\t\t\t\"csrf_token\": \"` + f.CSRFToken + `\"\n\t\t}`)\n\t\tr := httptest.NewRequest(\"POST\", \"/self-service/registration/browser\", strings.NewReader(body.Raw))\n\t\tr.Header.Add(\"Content-Type\", \"application/json\")\n\n\t\terr := s.Register(httptest.NewRecorder(), r, f, i)\n\t\trequire.ErrorIs(t, err, flow.ErrCompletedByStrategy)\n\t\trequire.NotNil(t, f.HydraLoginRequest)\n\t})\n\n\tt.Run(\"case=returns error if login challenge is invalid\", func(t *testing.T) {\n\t\t_, f := newFlow(t.Context(), t)\n\t\tf.OAuth2LoginChallenge = sqlxx.NullString(hydra.FakeInvalidLoginChallenge)\n\t\ti := identity.NewIdentity(f.IdentitySchema.ID(t.Context(), reg.Config()))\n\n\t\temail := testhelpers.RandomEmail()\n\n\t\tbody := gjson.Parse(`{\n\t\t\t\"method\": \"code\",\n\t\t\t\"traits.email\": \"` + email + `\",\n\t\t\t\"csrf_token\": \"` + f.CSRFToken + `\"\n\t\t}`)\n\t\tr := httptest.NewRequest(\"POST\", \"/self-service/registration/browser\", strings.NewReader(body.Raw))\n\t\tr.Header.Add(\"Content-Type\", \"application/json\")\n\n\t\terr := s.Register(httptest.NewRecorder(), r, f, i)\n\t\trequire.ErrorIs(t, err, herodot.ErrBadRequest)\n\t\trequire.Nil(t, f.HydraLoginRequest)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"maps\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/x/contextx\"\n)\n\nvar defaultConfig = func() map[string]any {\n\tcfg := map[string]any{\n\t\tconfig.ViperKeySelfServiceRecoveryEnabled:     true,\n\t\tconfig.ViperKeySelfServiceRecoveryUse:         \"code\",\n\t\tconfig.ViperKeySelfServiceVerificationEnabled: true,\n\t\tconfig.ViperKeySelfServiceVerificationUse:     \"code\",\n\t}\n\tmaps.Copy(cfg, testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/default.schema.json\"))\n\tmaps.Copy(cfg, testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, true))\n\tmaps.Copy(cfg, testhelpers.MethodEnableConfig(identity.CredentialsTypeCodeAuth, true))\n\treturn cfg\n}()\n\nfunc TestMaskAddress(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\taddress  string\n\t\texpected string\n\t}{\n\t\t{\n\t\t\taddress:  \"a\",\n\t\t\texpected: \"a\",\n\t\t},\n\t\t{\n\t\t\taddress:  \"ab@cd\",\n\t\t\texpected: \"ab****@cd\",\n\t\t},\n\t\t{\n\t\t\taddress:  \"fixed@ory.sh\",\n\t\t\texpected: \"fi****@ory.sh\",\n\t\t},\n\t\t{\n\t\t\taddress:  \"f@ory.sh\",\n\t\t\texpected: \"f@ory.sh\",\n\t\t},\n\t\t{\n\t\t\taddress:  \"+12345678910\",\n\t\t\texpected: \"+12****10\",\n\t\t},\n\t\t{\n\t\t\taddress:  \"+123456\",\n\t\t\texpected: \"+12****56\",\n\t\t},\n\t} {\n\t\tt.Run(\"case=\"+tc.address, func(t *testing.T) {\n\t\t\tassert.Equal(t, tc.expected, code.MaskAddress(tc.address))\n\t\t})\n\t}\n}\n\nfunc TestCountActiveCredentials(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tstrategy := code.NewStrategy(reg)\n\tctx := context.Background()\n\n\tt.Run(\"first factor\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\tin                  map[identity.CredentialsType]identity.Credentials\n\t\t\texpected            int\n\t\t\tpasswordlessEnabled bool\n\t\t\tenabled             bool\n\t\t}{\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte{},\n\t\t\t\t}},\n\t\t\t\tpasswordlessEnabled: false,\n\t\t\t\tenabled:             true,\n\t\t\t\texpected:            0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte{},\n\t\t\t\t}},\n\t\t\t\tpasswordlessEnabled: true,\n\t\t\t\tenabled:             false,\n\t\t\t\texpected:            1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin:                  map[identity.CredentialsType]identity.Credentials{},\n\t\t\t\tpasswordlessEnabled: true,\n\t\t\t\tenabled:             true,\n\t\t\t\texpected:            1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{}`),\n\t\t\t\t}},\n\t\t\t\tpasswordlessEnabled: true,\n\t\t\t\tenabled:             true,\n\t\t\t\texpected:            1,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, \"selfservice.methods.code.passwordless_enabled\", tc.passwordlessEnabled)\n\t\t\t\tctx = contextx.WithConfigValue(ctx, \"selfservice.methods.code.enabled\", tc.enabled)\n\n\t\t\t\tcc := map[identity.CredentialsType]identity.Credentials{}\n\t\t\t\tfor _, c := range tc.in {\n\t\t\t\t\tcc[c.Type] = c\n\t\t\t\t}\n\n\t\t\t\tactual, err := strategy.CountActiveFirstFactorCredentials(ctx, cc)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, tc.expected, actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"second factor\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\tin         map[identity.CredentialsType]identity.Credentials\n\t\t\texpected   int\n\t\t\tmfaEnabled bool\n\t\t\tenabled    bool\n\t\t}{\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte{},\n\t\t\t\t}},\n\t\t\t\tmfaEnabled: false,\n\t\t\t\tenabled:    true,\n\t\t\t\texpected:   0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte{},\n\t\t\t\t}},\n\t\t\t\tmfaEnabled: true,\n\t\t\t\tenabled:    false,\n\t\t\t\texpected:   0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin:         map[identity.CredentialsType]identity.Credentials{},\n\t\t\t\tmfaEnabled: true,\n\t\t\t\tenabled:    true,\n\t\t\t\texpected:   0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{}`),\n\t\t\t\t}},\n\t\t\t\tmfaEnabled: true,\n\t\t\t\tenabled:    true,\n\t\t\t\texpected:   0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{\"address_type\":\"email\",\"used_at\":{\"Time\":\"0001-01-01T00:00:00Z\",\"Valid\":false}}`),\n\t\t\t\t}},\n\t\t\t\tmfaEnabled: true,\n\t\t\t\tenabled:    true,\n\t\t\t\texpected:   1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"test@ory.sh\"}]}`),\n\t\t\t\t}},\n\t\t\t\tmfaEnabled: true,\n\t\t\t\tenabled:    true,\n\t\t\t\texpected:   1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{\"addresses\":[]}`),\n\t\t\t\t}},\n\t\t\t\tmfaEnabled: true,\n\t\t\t\tenabled:    true,\n\t\t\t\texpected:   0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{\"addresses\":[{\"channel\":\"sms\",\"address\":\"+1234567890\"}]}`),\n\t\t\t\t}},\n\t\t\t\tmfaEnabled: true,\n\t\t\t\tenabled:    true,\n\t\t\t\texpected:   1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{\"addresses\":[{\"channel\":\"sms\",\"address\":\"+1234567890\"},{\"channel\":\"email\",\"address\":\"test@ory.sh\"}]}`),\n\t\t\t\t}},\n\t\t\t\tmfaEnabled: true,\n\t\t\t\tenabled:    true,\n\t\t\t\texpected:   2,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, \"selfservice.methods.code.mfa_enabled\", tc.mfaEnabled)\n\t\t\t\tctx = contextx.WithConfigValue(ctx, \"selfservice.methods.code.enabled\", tc.enabled)\n\n\t\t\t\tcc := map[identity.CredentialsType]identity.Credentials{}\n\t\t\t\tfor _, c := range tc.in {\n\t\t\t\t\tcc[c.Type] = c\n\t\t\t\t}\n\n\t\t\t\tactual, err := strategy.CountActiveMultiFactorCredentials(ctx, cc)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, tc.expected, actual)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc (s *Strategy) VerificationStrategyID() string {\n\treturn string(verification.VerificationStrategyCode)\n}\n\n// PopulateVerificationMethod set's the appropriate UI nodes on this flow\n//\n// If the flow's state is `sent_email`, the `code` input and the success notification is set\n// Otherwise, the default email input is added.\n// If the flow is a browser flow, the CSRF token is added to the UI.\nfunc (s *Strategy) PopulateVerificationMethod(r *http.Request, f *verification.Flow) error {\n\treturn s.PopulateMethod(r, f)\n}\n\nfunc (s *Strategy) decodeVerification(r *http.Request) (*updateVerificationFlowWithCodeMethod, error) {\n\tvar body updateVerificationFlowWithCodeMethod\n\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(verificationMethodSchema)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif err := decoderx.Decode(r, &body, compiler,\n\t\tdecoderx.HTTPDecoderUseQueryAndBody(),\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn &body, nil\n}\n\n// handleVerificationError is a convenience function for handling all types of errors that may occur (e.g. validation error).\nfunc (s *Strategy) handleVerificationError(r *http.Request, f *verification.Flow, body *updateVerificationFlowWithCodeMethod, err error) error {\n\tif f != nil {\n\t\tf.UI.SetCSRF(s.deps.GenerateCSRFToken(r))\n\t\temail := \"\"\n\t\tif body != nil {\n\t\t\temail = body.Email\n\t\t}\n\t\tf.UI.GetNodes().Upsert(\n\t\t\tnode.NewInputField(\"email\", email, node.CodeGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).WithMetaLabel(text.NewInfoNodeInputEmail()),\n\t\t)\n\t}\n\n\treturn err\n}\n\n// swagger:model updateVerificationFlowWithCodeMethod\ntype updateVerificationFlowWithCodeMethod struct {\n\t// The email address to verify\n\t//\n\t// If the email belongs to a valid account, a verifiation email will be sent.\n\t//\n\t// If you want to notify the email address if the account does not exist, see\n\t// the [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation#attempted-verification-notifications)\n\t//\n\t// If a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.\n\t//\n\t// format: email\n\t// required: false\n\tEmail string `form:\"email\" json:\"email\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `form:\"csrf_token\" json:\"csrf_token\"`\n\n\t// Method is the method that should be used for this verification flow\n\t//\n\t// Allowed values are `link` and `code`.\n\t//\n\t// required: true\n\tMethod verification.VerificationStrategy `json:\"method\"`\n\n\t// Code from the recovery email\n\t//\n\t// If you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\n\t//\n\t// required: false\n\tCode string `json:\"code\" form:\"code\"`\n\n\t// The id of the flow\n\tFlow string `json:\"-\" form:\"-\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\n// getMethod returns the method of this submission or \"\" if no method could be found\nfunc (body *updateVerificationFlowWithCodeMethod) getMethod() verification.VerificationStrategy {\n\tif body.Method != \"\" {\n\t\treturn body.Method\n\t}\n\tif body.Code != \"\" {\n\t\treturn verification.VerificationStrategyCode\n\t}\n\n\treturn \"\"\n}\n\nfunc (s *Strategy) Verify(w http.ResponseWriter, r *http.Request, f *verification.Flow) (err error) {\n\tctx, span := s.deps.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.code.Strategy.Verify\")\n\tspan.SetAttributes(attribute.String(\"selfservice_flows_verification_use\", s.deps.Config().SelfServiceFlowVerificationUse(ctx)))\n\tdefer otelx.End(span, &err)\n\n\tbody, err := s.decodeVerification(r)\n\tif err != nil {\n\t\treturn s.handleVerificationError(r, nil, body, err)\n\t}\n\n\tf.TransientPayload = body.TransientPayload\n\n\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.VerificationStrategyID(), string(body.getMethod()), s.deps); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\tif err := f.Valid(); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\tswitch f.State {\n\tcase flow.StateChooseMethod:\n\t\tfallthrough\n\tcase flow.StateEmailSent:\n\t\treturn s.verificationHandleFormSubmission(ctx, w, r, f, body)\n\tcase flow.StatePassedChallenge:\n\t\treturn s.retryVerificationFlowWithMessage(ctx, w, r, f.Type, text.NewErrorValidationVerificationRetrySuccess())\n\tdefault:\n\t\treturn s.retryVerificationFlowWithMessage(ctx, w, r, f.Type, text.NewErrorValidationVerificationStateFailure())\n\t}\n}\n\nfunc (s *Strategy) handleLinkClick(ctx context.Context, w http.ResponseWriter, r *http.Request, f *verification.Flow, code string) error {\n\t// Pre-fill the code\n\tif codeField := f.UI.Nodes.Find(\"code\"); codeField != nil {\n\t\tcodeField.Attributes.SetValue(code)\n\t}\n\n\t// In the verification flow, we can't enforce CSRF if the flow is opened from an email, so we initialize the CSRF\n\t// token here, so all subsequent interactions are protected\n\tcsrfToken := s.deps.CSRFHandler().RegenerateToken(w, r)\n\tf.UI.SetCSRF(csrfToken)\n\tf.CSRFToken = csrfToken\n\n\tif err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {\n\t\treturn err\n\t}\n\n\t// we always redirect to the browser UI here to allow API flows to complete aswell\n\t// TODO: In the future, we might want to redirect to a custom URI scheme here, to allow to open an app on the device of\n\t// the user to handle the flow directly.\n\thttp.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) verificationHandleFormSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request, f *verification.Flow, body *updateVerificationFlowWithCodeMethod) error {\n\tif len(body.Code) > 0 {\n\t\tif r.Method == http.MethodGet {\n\t\t\t// Special case: in the code strategy we send out links as well, that contain the code\n\t\t\treturn s.handleLinkClick(ctx, w, r, f, body.Code)\n\t\t}\n\n\t\t// If not GET: try to use the submitted code\n\t\treturn s.verificationUseCode(ctx, w, r, body.Code, f)\n\t} else if len(body.Email) == 0 {\n\t\t// If no code and no email was provided, fail with a validation error\n\t\treturn s.handleVerificationError(r, f, body, schema.NewRequiredError(\"#/email\", \"email\"))\n\t}\n\n\tif err := flow.EnsureCSRF(s.deps, r, f.Type, s.deps.Config().DisableAPIFlowEnforcement(ctx), s.deps.GenerateCSRFToken, body.CSRFToken); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\tif err := s.deps.VerificationCodePersister().DeleteVerificationCodesOfFlow(ctx, f.ID); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\tif err := s.deps.CodeSender().SendVerificationCode(ctx, f, identity.AddressTypeEmail, body.Email); err != nil {\n\t\tif !errors.Is(err, ErrUnknownAddress) {\n\t\t\treturn s.handleVerificationError(r, f, body, err)\n\t\t}\n\t\t// Continue execution\n\t}\n\n\tf.State = flow.StateEmailSent\n\n\tif err := s.PopulateVerificationMethod(r, f); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\tif body.Email != \"\" {\n\t\tf.UI.Nodes.Append(\n\t\t\tnode.NewInputField(\"email\", body.Email, node.CodeGroup, node.InputAttributeTypeSubmit).\n\t\t\t\tWithMetaLabel(text.NewInfoNodeResendOTP()),\n\t\t)\n\t}\n\n\tif err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) verificationUseCode(ctx context.Context, w http.ResponseWriter, r *http.Request, codeString string, f *verification.Flow) error {\n\tcode, err := s.deps.VerificationCodePersister().UseVerificationCode(ctx, f.ID, codeString)\n\tif errors.Is(err, ErrCodeNotFound) {\n\t\tf.UI.Messages.Clear()\n\t\tf.UI.Messages.Add(text.NewErrorValidationVerificationCodeInvalidOrAlreadyUsed())\n\t\tif err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {\n\t\t\treturn s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)\n\t\t}\n\n\t\tif x.IsBrowserRequest(r) {\n\t\t\thttp.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)\n\t\t} else {\n\t\t\ts.deps.Writer().Write(w, r, f)\n\t\t}\n\t\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n\t} else if err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)\n\t}\n\n\taddress := code.VerifiableAddress\n\taddress.Verified = true\n\tverifiedAt := sqlxx.NullTime(time.Now().UTC())\n\taddress.VerifiedAt = &verifiedAt\n\taddress.Status = identity.VerifiableAddressStatusCompleted\n\tif err := s.deps.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, address, \"verified\", \"verified_at\", \"status\"); err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)\n\t}\n\n\ti, err := s.deps.IdentityPool().GetIdentity(ctx, code.VerifiableAddress.IdentityID, identity.ExpandDefault)\n\tif err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)\n\t}\n\n\treturnTo := f.ContinueURL(ctx, s.deps.Config())\n\n\tf.UI = &container.Container{\n\t\tMethod: \"GET\",\n\t\tAction: returnTo.String(),\n\t}\n\n\tf.State = flow.StatePassedChallenge\n\t// See https://github.com/ory/kratos/issues/1547\n\tf.SetCSRFToken(flow.GetCSRFToken(s.deps, w, r, f.Type))\n\tf.UI.Messages.Set(text.NewInfoSelfServiceVerificationSuccessful())\n\tf.UI.\n\t\tNodes.\n\t\tAppend(node.NewAnchorField(\"continue\", returnTo.String(), node.CodeGroup, text.NewInfoNodeLabelContinue()).\n\t\t\tWithMetaLabel(text.NewInfoNodeLabelContinue()))\n\n\tif err := s.deps.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)\n\t}\n\n\tif err := s.deps.VerificationExecutor().PostVerificationHook(w, r, f, i); err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, f.Type, err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) retryVerificationFlowWithMessage(ctx context.Context, w http.ResponseWriter, r *http.Request, ft flow.Type, message *text.Message) error {\n\ts.deps.\n\t\tLogger().\n\t\tWithRequest(r).\n\t\tWithField(\"message\", message).\n\t\tDebug(\"A verification flow is being retried because a validation error occurred.\")\n\n\tf, err := verification.NewFlow(s.deps.Config(),\n\t\ts.deps.Config().SelfServiceFlowVerificationRequestLifespan(ctx), s.deps.CSRFHandler().RegenerateToken(w, r), r, verification.Strategies{s}, ft)\n\tif err != nil {\n\t\treturn s.handleVerificationError(r, f, nil, err)\n\t}\n\n\tf.UI.Messages.Add(message)\n\n\tif err := s.deps.VerificationFlowPersister().CreateVerificationFlow(ctx, f); err != nil {\n\t\treturn s.handleVerificationError(r, f, nil, err)\n\t}\n\n\tif x.IsJSONRequest(r) {\n\t\ts.deps.Writer().WriteError(w, r, flow.NewFlowReplacedError(text.NewErrorSystemGeneric(\"An error occured, please use the new flow.\")).WithFlow(f))\n\t} else {\n\t\thttp.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) retryVerificationFlowWithError(ctx context.Context, w http.ResponseWriter, r *http.Request, ft flow.Type, verErr error) error {\n\ts.deps.\n\t\tLogger().\n\t\tWithRequest(r).\n\t\tWithError(verErr).\n\t\tDebug(\"A verification flow is being retried because an error occurred.\")\n\n\tf, err := verification.NewFlow(s.deps.Config(),\n\t\ts.deps.Config().SelfServiceFlowVerificationRequestLifespan(ctx), s.deps.CSRFHandler().RegenerateToken(w, r), r, verification.Strategies{s}, ft)\n\tif err != nil {\n\t\treturn s.handleVerificationError(r, f, nil, err)\n\t}\n\n\tvar toReturn error\n\n\tif expired := new(flow.ExpiredError); errors.As(verErr, &expired) {\n\t\tf.UI.Messages.Add(text.NewErrorValidationVerificationFlowExpired(expired.ExpiredAt))\n\t\ttoReturn = expired.WithFlow(f)\n\t} else if err := f.UI.ParseError(node.LinkGroup, verErr); err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.deps.VerificationFlowPersister().CreateVerificationFlow(ctx, f); err != nil {\n\t\treturn s.handleVerificationError(r, f, nil, err)\n\t}\n\n\tif x.IsJSONRequest(r) {\n\t\tif toReturn == nil {\n\t\t\ttoReturn = flow.NewFlowReplacedError(text.NewErrorSystemGeneric(\"An error occured, please retry the flow.\")).\n\t\t\t\tWithFlow(f)\n\t\t}\n\t\ts.deps.Writer().WriteError(w, r, toReturn)\n\t} else {\n\t\thttp.Redirect(w, r, f.AppendTo(s.deps.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) SendVerificationCode(ctx context.Context, f *verification.Flow, i *identity.Identity, a *identity.VerifiableAddress) (err error) {\n\trawCode := GenerateCode()\n\n\tcode, err := s.deps.VerificationCodePersister().CreateVerificationCode(ctx, &CreateVerificationCodeParams{\n\t\tRawCode:           rawCode,\n\t\tExpiresIn:         s.deps.Config().SelfServiceCodeMethodLifespan(ctx),\n\t\tVerifiableAddress: a,\n\t\tFlowID:            f.ID,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn s.deps.CodeSender().SendVerificationCodeTo(ctx, f, i, rawCode, code)\n}\n"
  },
  {
    "path": "selfservice/strategy/code/strategy_verification_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strings\"\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\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestVerification(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(defaultConfig))\n\n\tidentityToVerify := &identity.Identity{\n\t\tID:       x.NewUUID(),\n\t\tTraits:   identity.Traits(`{\"email\":\"verifyme@ory.sh\"}`),\n\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\"password\": {\n\t\t\t\tType:        \"password\",\n\t\t\t\tIdentifiers: []string{\"recoverme@ory.sh\"},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"foo\"}`),\n\t\t\t},\n\t\t},\n\t}\n\n\tverificationEmail := gjson.GetBytes(identityToVerify.Traits, \"email\").String()\n\n\t_ = testhelpers.NewVerificationUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewRedirTS(t, \"returned\", conf)\n\n\tpublic, _ := testhelpers.NewKratosServerWithCSRF(t, reg)\n\n\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), identityToVerify,\n\t\tidentity.ManagerAllowWriteProtectedTraits))\n\n\texpect := func(t *testing.T, hc *http.Client, isAPI, isSPA bool, values func(url.Values), c int) string {\n\t\tif hc == nil {\n\t\t\thc = testhelpers.NewDebugClient(t)\n\t\t\tif !isAPI {\n\t\t\t\thc = testhelpers.NewClientWithCookies(t)\n\t\t\t\thc.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t}\n\t\t}\n\n\t\treturn testhelpers.SubmitVerificationForm(t, isAPI, isSPA, hc, public, values, c,\n\t\t\ttesthelpers.ExpectURL(isAPI || isSPA, public.URL+verification.RouteSubmitFlow, conf.SelfServiceFlowVerificationUI(ctx).String()))\n\t}\n\n\texpectValidationError := func(t *testing.T, hc *http.Client, isAPI, isSPA bool, values func(url.Values)) string {\n\t\treturn expect(t, hc, isAPI, isSPA, values, testhelpers.ExpectStatusCode(isAPI || isSPA, http.StatusBadRequest, http.StatusOK))\n\t}\n\n\texpectSuccess := func(t *testing.T, hc *http.Client, isAPI, isSPA bool, values func(url.Values)) string {\n\t\treturn expect(t, hc, isAPI, isSPA, values, http.StatusOK)\n\t}\n\n\tsubmitVerificationCode := func(t *testing.T, body string, c *http.Client, code string) (string, *http.Response) {\n\t\taction := gjson.Get(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action, \"%v\", string(body))\n\t\tcsrfToken := extractCsrfToken([]byte(body))\n\n\t\tres, err := c.PostForm(action, url.Values{\n\t\t\t\"code\":       {code},\n\t\t\t\"csrf_token\": {csrfToken},\n\t\t})\n\t\trequire.NoError(t, err)\n\n\t\treturn string(ioutilx.MustReadAll(res.Body)), res\n\t}\n\n\tt.Run(\"description=should set all the correct verification payloads after submission\", func(t *testing.T) {\n\t\tbody := expectSuccess(t, nil, false, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", \"test@ory.sh\")\n\t\t})\n\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes\").String()), []string{\"3.attributes.value\"})\n\t})\n\n\tt.Run(\"description=should set all the correct verification payloads\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetVerificationFlow(t, c, public)\n\n\t\ttesthelpers.SnapshotTExcept(t, rs.Ui.Nodes, []string{\"2.attributes.value\"})\n\t\tassert.EqualValues(t, public.URL+verification.RouteSubmitFlow+\"?flow=\"+rs.Id, rs.Ui.Action)\n\t\tassert.Empty(t, rs.Ui.Messages)\n\t})\n\n\tt.Run(\"description=should not execute submit without correct method set\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetVerificationFlow(t, c, public)\n\n\t\tres, err := c.PostForm(rs.Ui.Action, url.Values{\"method\": {\"not-link\"}, \"email\": {verificationEmail}})\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowVerificationUI(ctx).String())\n\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\t\ttesthelpers.AssertFieldMessage(t, body, \"method\", `value must be one of \"code\", \"link\"`)\n\t})\n\n\tt.Run(\"description=should require an email to be sent\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, string(node.CodeGroup), gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, \"Property email is missing.\",\n\t\t\t\tgjson.Get(actual, \"ui.nodes.#(attributes.name==email).messages.0.text\").String(),\n\t\t\t\t\"%s\", actual)\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Del(\"email\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, nil, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, nil, false, true, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, nil, true, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"description=should require a valid email to be sent\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string, value string) {\n\t\t\tassert.EqualValues(t, string(node.CodeGroup), gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, \"Enter a valid email address\",\n\t\t\t\tgjson.Get(actual, \"ui.nodes.#(attributes.name==email).messages.0.text\").String(),\n\t\t\t\t\"%s\", actual)\n\t\t}\n\n\t\tfor _, email := range []string{\"\\\\\", \"asdf\", \"...\", \"aiacobelli.sec@gmail.com,alejandro.iacobelli@mercadolibre.com\"} {\n\t\t\tvalues := func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, nil, false, false, values), email)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, nil, false, true, values), email)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, nil, true, false, values), email)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should try to verify an email that does not exist\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationNotifyUnknownRecipients, true)\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationNotifyUnknownRecipients, false)\n\t\t})\n\n\t\tvar email string\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, string(node.CodeGroup), gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==email).attributes.value\").String(), \"%s\", actual)\n\t\t\tassertx.EqualAsJSON(t, text.NewVerificationEmailWithCodeSent(), json.RawMessage(gjson.Get(actual, \"ui.messages.0\").Raw))\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Someone tried to verify this email address\")\n\t\t\tassert.Contains(t, message.Body, \"If this was you, check if you signed up using a different address.\")\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", email)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\temail = x.NewUUID().String() + \"@ory.sh\"\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\temail = x.NewUUID().String() + \"@ory.sh\"\n\t\t\tcheck(t, expectSuccess(t, nil, false, true, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\temail = x.NewUUID().String() + \"@ory.sh\"\n\t\t\tcheck(t, expectSuccess(t, nil, true, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"description=clicking link should prefill code\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.SubmitVerificationForm(t, false, false, c, public, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t}, 200, \"\")\n\t\tfID := gjson.Get(f, \"id\").String()\n\t\tres, err := c.Get(public.URL + verification.RouteSubmitFlow + \"?flow=\" + fID + \"&code=12312312\")\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowVerificationUI(ctx).String()+\"?flow=\")\n\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\tassert.Equal(t, \"12312312\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==code).attributes.value\").String(), \"%v\", string(body))\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid code\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.SubmitVerificationForm(t, false, false, c, public, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t}, 200, \"\")\n\n\t\tbody, res := submitVerificationCode(t, f, c, \"12312312\")\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\ttesthelpers.AssertMessage(t, body, \"The verification code is invalid or has already been used. Please try again.\")\n\t})\n\n\tt.Run(\"description=should not be able to submit email in expired flow\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationRequestLifespan, 100*time.Millisecond)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationRequestLifespan, time.Minute)\n\t\t})\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetVerificationFlow(t, c, public)\n\n\t\ttime.Sleep(101 * time.Millisecond)\n\n\t\tres, err := c.PostForm(rs.Ui.Action, url.Values{\"method\": {\"code\"}, \"email\": {verificationEmail}})\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.NotContains(t, res.Request.URL.String(), \"flow=\"+rs.Id)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowVerificationUI(ctx).String())\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\tassert.Regexpf(t, regexp.MustCompile(`The verification flow expired 0\\.0\\d minutes ago, please try again\\.`), gjson.GetBytes(body, \"ui.messages.0.text\").Str, \"%s\", body)\n\t})\n\n\tt.Run(\"description=should not be able to submit code in expired flow\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationRequestLifespan, 100*time.Millisecond)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationRequestLifespan, time.Minute)\n\t\t})\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody := expectSuccess(t, c, false, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t})\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\tassert.Contains(t, message.Body, \"Verify your account with the following code\")\n\n\t\tcode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\ttime.Sleep(101 * time.Millisecond)\n\n\t\tf, _ := submitVerificationCode(t, body, c, code)\n\n\t\tassert.Regexpf(t, regexp.MustCompile(`The verification flow expired 0\\.0\\d minutes ago, please try again\\.`), gjson.Get(f, \"ui.messages.0.text\").Str, \"%s\", body)\n\t})\n\n\tt.Run(\"description=should verify an email address\", func(t *testing.T) {\n\t\tvar wg sync.WaitGroup\n\t\ttesthelpers.NewVerifyAfterHookWebHookTarget(ctx, t, conf, func(t *testing.T, msg []byte) {\n\t\t\tdefer wg.Done()\n\t\t\tassert.EqualValues(t, true, gjson.GetBytes(msg, \"identity.verifiable_addresses.0.verified\").Bool(), string(msg))\n\t\t\tassert.EqualValues(t, \"completed\", gjson.GetBytes(msg, \"identity.verifiable_addresses.0.status\").String(), string(msg))\n\t\t})\n\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, string(node.CodeGroup), gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, verificationEmail, gjson.Get(actual, \"ui.nodes.#(attributes.name==email).attributes.value\").String(), \"%s\", actual)\n\t\t\tassertx.EqualAsJSON(t, text.NewVerificationEmailWithCodeSent(), json.RawMessage(gjson.Get(actual, \"ui.messages.0\").Raw))\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\t\tassert.Contains(t, message.Body, \"Verify your account with the following code\")\n\n\t\t\tverificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\t\tassert.Contains(t, verificationLink, public.URL+verification.RouteSubmitFlow)\n\t\t\tassert.Contains(t, verificationLink, \"code=\")\n\n\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\t\tres, err := cl.Get(verificationLink)\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\tf := ioutilx.MustReadAll(res.Body)\n\n\t\t\tcode := gjson.GetBytes(f, \"ui.nodes.#(attributes.name==code).attributes.value\").String()\n\t\t\trequire.NotEmpty(t, code)\n\n\t\t\tbody, res := submitVerificationCode(t, string(f), cl, code)\n\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.EqualValues(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\tassert.EqualValues(t, text.NewInfoSelfServiceVerificationSuccessful().Text, gjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\tid, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), identityToVerify.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, id.VerifiableAddresses, 1)\n\n\t\t\taddress := id.VerifiableAddresses[0]\n\t\t\tassert.EqualValues(t, verificationEmail, address.Value)\n\t\t\tassert.True(t, address.Verified)\n\t\t\tassert.EqualValues(t, identity.VerifiableAddressStatusCompleted, address.Status)\n\t\t\tassert.True(t, time.Time(*address.VerifiedAt).Add(time.Second*5).After(time.Now()))\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\twg.Add(1)\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, values))\n\t\t\twg.Wait()\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\twg.Add(1)\n\t\t\tcheck(t, expectSuccess(t, nil, false, true, values))\n\t\t\twg.Wait()\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\twg.Add(1)\n\t\t\tcheck(t, expectSuccess(t, nil, true, false, values))\n\t\t\twg.Wait()\n\t\t})\n\t})\n\n\tt.Run(\"description=should verify an email address when the link is opened in another browser\", func(t *testing.T) {\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t}\n\n\t\texpectSuccess(t, nil, false, false, values)\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\tverificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\t\tcode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\tres, err := cl.Get(verificationLink)\n\t\trequire.NoError(t, err)\n\t\tbody := string(ioutilx.MustReadAll(res.Body))\n\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\tassert.Contains(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL))[0].Name, nosurfx.CSRFTokenName)\n\n\t\tactualBody, _ := submitVerificationCode(t, body, cl, code)\n\t\tassert.EqualValues(t, \"passed_challenge\", gjson.Get(actualBody, \"state\").String())\n\t})\n\n\tnewValidFlow := func(t *testing.T, fType flow.Type, requestURL string) (*verification.Flow, *code.VerificationCode, string) {\n\t\tf, err := verification.NewFlow(conf, time.Hour, nosurfx.FakeCSRFToken, httptest.NewRequest(\"GET\", requestURL, nil), verification.Strategies{code.NewStrategy(reg)}, fType)\n\t\trequire.NoError(t, err)\n\t\tf.State = flow.StateEmailSent\n\t\tu, err := url.Parse(f.RequestURL)\n\t\trequire.NoError(t, err)\n\t\tf.OAuth2LoginChallenge = sqlxx.NullString(u.Query().Get(\"login_challenge\"))\n\t\tf.IdentityID = uuid.NullUUID{UUID: x.NewUUID(), Valid: true}\n\t\tf.SessionID = uuid.NullUUID{UUID: x.NewUUID(), Valid: true}\n\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(context.Background(), f))\n\t\temail := identity.NewVerifiableEmailAddress(verificationEmail, identityToVerify.ID)\n\t\tidentityToVerify.VerifiableAddresses = append(identityToVerify.VerifiableAddresses, *email)\n\t\trequire.NoError(t, reg.IdentityManager().Update(context.Background(), identityToVerify, identity.ManagerAllowWriteProtectedTraits))\n\n\t\tparams := &code.CreateVerificationCodeParams{\n\t\t\tRawCode:           \"12312312\",\n\t\t\tExpiresIn:         time.Hour,\n\t\t\tVerifiableAddress: &identityToVerify.VerifiableAddresses[0],\n\t\t\tFlowID:            f.ID,\n\t\t}\n\t\tverificationCode, err := reg.VerificationCodePersister().CreateVerificationCode(context.Background(), params)\n\t\trequire.NoError(t, err)\n\t\treturn f, verificationCode, params.RawCode\n\t}\n\n\tnewValidBrowserFlow := func(t *testing.T, requestURL string) (*verification.Flow, *code.VerificationCode, string) {\n\t\treturn newValidFlow(t, flow.TypeBrowser, requestURL)\n\t}\n\n\tt.Run(\"case=contains link to return_to\", func(t *testing.T) {\n\t\treturnToURL := public.URL + \"/after-verification\"\n\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToURL})\n\t\tclient := &http.Client{}\n\n\t\tf, _, rawCode := newValidBrowserFlow(t, public.URL+verification.RouteInitBrowserFlow+\"?\"+url.Values{\"return_to\": {returnToURL}}.Encode())\n\n\t\taction := public.URL + verification.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\n\t\tres, err := client.PostForm(action, url.Values{\n\t\t\t\"code\":       {rawCode},\n\t\t\t\"csrf_token\": {nosurfx.FakeCSRFToken},\n\t\t})\n\t\trequire.NoError(t, err)\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\tassert.Equal(t, returnToURL, gjson.GetBytes(body, \"ui.nodes.#(attributes.id==continue).attributes.href\").String())\n\t})\n\n\tt.Run(\"case=should respond with replaced error if successful code is submitted again via api\", func(t *testing.T) {\n\t\t_ = expectSuccess(t, nil, true, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t})\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\tassert.Contains(t, message.Body, \"Verify your account with the following code\")\n\n\t\tverificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\tassert.Contains(t, verificationLink, public.URL+verification.RouteSubmitFlow)\n\t\tassert.Contains(t, verificationLink, \"code=\")\n\n\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\tres, err := cl.Get(verificationLink)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\toriginal := ioutilx.MustReadAll(res.Body)\n\n\t\tcode := gjson.GetBytes(original, \"ui.nodes.#(attributes.name==code).attributes.value\").String()\n\t\trequire.NotEmpty(t, code)\n\t\taction := gjson.GetBytes(original, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\n\t\tc := testhelpers.NewDebugClient(t)\n\t\tres, err = c.Post(action, \"application/json\", strings.NewReader(fmt.Sprintf(`{\"code\": \"%v\"}`, code)))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\tf1 := ioutilx.MustReadAll(res.Body)\n\n\t\tassert.EqualValues(t, \"passed_challenge\", gjson.GetBytes(f1, \"state\").String())\n\n\t\tres, err = c.Post(action, \"application/json\", strings.NewReader(fmt.Sprintf(`{\"code\": \"%v\"}`, code)))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusGone, res.StatusCode)\n\n\t\tf2 := ioutilx.MustReadAll(res.Body)\n\t\tassert.Equal(t, text.ErrIDSelfServiceFlowReplaced, gjson.GetBytes(f2, \"error.id\").String())\n\t})\n\n\tresendVerificationCode := func(t *testing.T, client *http.Client, flow string, flowType ClientType, statusCode int) string {\n\t\taction := gjson.Get(flow, \"ui.action\").String()\n\t\tassert.NotEmpty(t, action)\n\n\t\temail := gjson.Get(flow, \"ui.nodes.#(attributes.name==email).attributes.value\").String()\n\n\t\tvalues := withCSRFToken(t, flowType, flow, url.Values{\n\t\t\t\"method\": {\"code\"},\n\t\t\t\"email\":  {email},\n\t\t})\n\n\t\tcontentType := \"application/json\"\n\t\tif flowType == RecoveryClientTypeBrowser {\n\t\t\tcontentType = \"application/x-www-form-urlencoded\"\n\t\t}\n\n\t\tres, err := client.Post(action, contentType, bytes.NewBufferString(values))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, statusCode, res.StatusCode)\n\n\t\treturn string(ioutilx.MustReadAll(res.Body))\n\t}\n\n\tt.Run(\"case=should be able to resend code\", func(t *testing.T) {\n\t\tbody := expectSuccess(t, nil, true, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t})\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\t_ = testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody = resendVerificationCode(t, c, body, RecoveryClientTypeBrowser, http.StatusOK)\n\n\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\t\tassert.Equal(t, verificationEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\tmessage = testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\tverificationCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\tsubmitVerificationCode(t, body, c, verificationCode)\n\t})\n\n\tt.Run(\"case=should not be able to use first code after resending code\", func(t *testing.T) {\n\t\tbody := expectSuccess(t, nil, true, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t})\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\tfirstCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody = resendVerificationCode(t, c, body, RecoveryClientTypeBrowser, http.StatusOK)\n\n\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==code)\").Exists())\n\t\tassert.Equal(t, verificationEmail, gjson.Get(body, \"ui.nodes.#(attributes.name==email).attributes.value\").String())\n\n\t\tmessage = testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\tsecondCode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\tbody, res := submitVerificationCode(t, body, c, firstCode)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\ttesthelpers.AssertMessage(t, body, \"The verification code is invalid or has already been used. Please try again.\")\n\n\t\t// For good measure, check that the second code still works!\n\n\t\tbody, res = submitVerificationCode(t, body, c, secondCode)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\ttesthelpers.AssertMessage(t, body, \"You successfully verified your email address.\")\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid code more than 5 times\", func(t *testing.T) {\n\t\temail := strings.ToLower(testhelpers.RandomEmail())\n\t\tcreateIdentityToRecover(t, reg, email)\n\t\tc := testhelpers.NewClientWithCookies(t)\n\n\t\tbody := expectSuccess(t, nil, true, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t})\n\t\tinitialFlowId := gjson.Get(body, \"id\")\n\n\t\tfor submitTry := 0; submitTry < 5; submitTry++ {\n\t\t\txcbody, _ := submitVerificationCode(t, body, c, \"12312312\")\n\t\t\trequire.Equal(t, initialFlowId.String(), gjson.Get(xcbody, \"id\").String())\n\n\t\t\ttesthelpers.AssertMessage(t, []byte(xcbody), \"The verification code is invalid or has already been used. Please try again.\")\n\t\t}\n\n\t\t// submit an invalid code for the 6th time\n\t\tbody, _ = submitVerificationCode(t, body, c, \"12312312\")\n\n\t\trequire.Len(t, gjson.Get(body, \"ui.messages\").Array(), 1)\n\t\tassert.Equal(t, \"The request was submitted too often. Please request another code.\", gjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t// check that a new flow has been created\n\t\tassert.NotEqual(t, gjson.Get(body, \"id\"), initialFlowId)\n\n\t\tassert.True(t, gjson.Get(body, \"ui.nodes.#(attributes.name==email)\").Exists())\n\t})\n\n\tt.Run(\"description=should be able to verify already verified email address\", func(t *testing.T) {\n\t\temail := strings.ToLower(testhelpers.RandomEmail())\n\t\tcreateIdentityToRecover(t, reg, email)\n\t\tc := testhelpers.NewClientWithCookies(t)\n\n\t\tbody := expectSuccess(t, nil, true, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t})\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\tcode := testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\tbody, res := submitVerificationCode(t, body, c, code)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\ttesthelpers.AssertMessage(t, body, \"You successfully verified your email address.\")\n\n\t\tbody = expectSuccess(t, nil, true, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t})\n\t\tmessage = testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Use code\")\n\t\tcode = testhelpers.CourierExpectCodeInMessage(t, message, 1)\n\n\t\tbody, res = submitVerificationCode(t, body, c, code)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\ttesthelpers.AssertMessage(t, body, \"You successfully verified your email address.\")\n\t})\n\n\tt.Run(\"case=respects return_to URI parameter\", func(t *testing.T) {\n\t\treturnToURL := public.URL + \"/after-verification\"\n\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToURL})\n\n\t\tfor _, fType := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {\n\t\t\tt.Run(fmt.Sprintf(\"type=%s\", fType), func(t *testing.T) {\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tflow, _, rawCode := newValidFlow(t, fType, public.URL+verification.RouteInitBrowserFlow+\"?\"+url.Values{\"return_to\": {returnToURL}}.Encode())\n\n\t\t\t\tbody := fmt.Sprintf(\n\t\t\t\t\t`{\"csrf_token\":\"%s\",\"code\":\"%s\"}`, flow.CSRFToken, rawCode,\n\t\t\t\t)\n\n\t\t\t\tres, err := client.Post(public.URL+verification.RouteSubmitFlow+\"?\"+url.Values{\"flow\": {flow.ID.String()}}.Encode(), \"application/json\", bytes.NewBuffer([]byte(body)))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tresponseBody := gjson.ParseBytes(ioutilx.MustReadAll(res.Body))\n\n\t\t\t\tassert.Equal(t, responseBody.Get(\"state\").String(), \"passed_challenge\", \"%v\", responseBody)\n\t\t\t\tassert.True(t, responseBody.Get(\"ui.nodes.#(attributes.id==continue)\").Exists(), \"%v\", responseBody)\n\t\t\t\tassert.Equal(t, returnToURL, responseBody.Get(\"ui.nodes.#(attributes.id==continue).attributes.href\").String(), \"%v\", responseBody)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=contains default return to url\", func(t *testing.T) {\n\t\tglobalReturnTo := public.URL + \"/global\"\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, globalReturnTo)\n\n\t\tfor _, fType := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {\n\t\t\tt.Run(fmt.Sprintf(\"type=%s\", fType), func(t *testing.T) {\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tflow, _, rawCode := newValidFlow(t, fType, public.URL+verification.RouteInitBrowserFlow)\n\n\t\t\t\tbody := fmt.Sprintf(\n\t\t\t\t\t`{\"csrf_token\":\"%s\",\"code\":\"%s\"}`, flow.CSRFToken, rawCode,\n\t\t\t\t)\n\n\t\t\t\tres, err := client.Post(public.URL+verification.RouteSubmitFlow+\"?\"+url.Values{\"flow\": {flow.ID.String()}}.Encode(), \"application/json\", bytes.NewBuffer([]byte(body)))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tresponseBody := gjson.ParseBytes(ioutilx.MustReadAll(res.Body))\n\t\t\t\tt.Logf(\"%v\", responseBody)\n\n\t\t\t\tassert.Equal(t, responseBody.Get(\"state\").String(), \"passed_challenge\", \"%v\", responseBody)\n\t\t\t\tassert.True(t, responseBody.Get(\"ui.nodes.#(attributes.id==continue)\").Exists(), \"%v\", responseBody)\n\t\t\t\tassert.Equal(t, globalReturnTo, responseBody.Get(\"ui.nodes.#(attributes.id==continue).attributes.href\").String(), \"%v\", responseBody)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=doesn't continue with OAuth2 flow if code is invalid\", func(t *testing.T) {\n\t\treturnToURL := public.URL + \"/after-verification\"\n\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToURL})\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tflow, _, _ := newValidFlow(t, flow.TypeBrowser,\n\t\t\tpublic.URL+verification.RouteInitBrowserFlow+\"?\"+url.Values{\n\t\t\t\t\"return_to\":       {returnToURL},\n\t\t\t\t\"login_challenge\": {\"any_valid_challenge\"},\n\t\t\t}.Encode())\n\n\t\tbody := fmt.Sprintf(\n\t\t\t`{\"csrf_token\":\"%s\",\"code\":\"%s\"}`, flow.CSRFToken, \"2475\",\n\t\t)\n\n\t\tres, err := client.Post(\n\t\t\tpublic.URL+verification.RouteSubmitFlow+\"?\"+url.Values{\"flow\": {flow.ID.String()}}.Encode(),\n\t\t\t\"application/json\",\n\t\t\tbytes.NewBuffer([]byte(body)),\n\t\t)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\tresponseBody := gjson.ParseBytes(ioutilx.MustReadAll(res.Body))\n\n\t\tassert.Equal(t, responseBody.Get(\"state\").String(), \"sent_email\", \"%v\", responseBody)\n\t\tassert.Len(t, responseBody.Get(\"ui.messages\").Array(), 1, \"%v\", responseBody)\n\t\tassert.Equal(t, \"The verification code is invalid or has already been used. Please try again.\", responseBody.Get(\"ui.messages.0.text\").String(), \"%v\", responseBody)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/code/stub/code-mfa.identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email1\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            }\n          }\n        },\n        \"email2\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            }\n          }\n        },\n        \"phone1\": {\n          \"type\": \"string\",\n          \"format\": \"tel\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"sms\"\n              }\n            }\n          }\n        }\n      },\n      \"required\": []\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/stub/code.identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              },\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"email_0\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"email_1\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"phone_1\": {\n          \"type\": \"string\",\n          \"format\": \"tel\",\n          \"title\": \"Phone\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"sms\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"sms\"\n            }\n          }\n        },\n        \"tos\": {\n          \"type\": \"boolean\",\n          \"title\": \"Tos\",\n          \"description\": \"Please accept the terms and conditions\"\n        }\n      },\n      \"required\": []\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/stub/default.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"phone\": {\n          \"type\": \"string\",\n          \"format\": \"tel\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"sms\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"sms\"\n            },\n            \"recovery\": {\n              \"via\": \"sms\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/stub/no-code-id.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/stub/no-code.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/code/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage code\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/randx\"\n)\n\nfunc TestPersister(ctx context.Context, p interface {\n\tpersistence.Persister\n},\n) func(t *testing.T) {\n\treturn func(t *testing.T) {\n\t\tnid, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecretsDefault, []string{\"secret-a\", \"secret-b\"})\n\n\t\tt.Run(\"code=recovery\", func(t *testing.T) {\n\t\t\tnewRecoveryCodeDTO := func(t *testing.T, email string) (*code.CreateRecoveryCodeParams, *recovery.Flow, *identity.RecoveryAddress) {\n\t\t\t\tvar f recovery.Flow\n\t\t\t\trequire.NoError(t, faker.FakeData(&f))\n\t\t\t\tf.State = flow.StateChooseMethod\n\n\t\t\t\trequire.NoError(t, p.CreateRecoveryFlow(ctx, &f))\n\n\t\t\t\tvar i identity.Identity\n\t\t\t\trequire.NoError(t, faker.FakeData(&i))\n\n\t\t\t\taddress := &identity.RecoveryAddress{Value: email, Via: identity.AddressTypeEmail, IdentityID: i.ID}\n\t\t\t\ti.RecoveryAddresses = append(i.RecoveryAddresses, *address)\n\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, &i))\n\n\t\t\t\treturn &code.CreateRecoveryCodeParams{\n\t\t\t\t\tRawCode:         randx.MustString(8, randx.Numeric),\n\t\t\t\t\tFlowID:          f.ID,\n\t\t\t\t\tRecoveryAddress: &i.RecoveryAddresses[0],\n\t\t\t\t\tExpiresIn:       time.Minute,\n\t\t\t\t\tIdentityID:      i.ID,\n\t\t\t\t}, &f, &i.RecoveryAddresses[0]\n\t\t\t}\n\n\t\t\tt.Run(\"case=should error when the recovery token does not exist\", func(t *testing.T) {\n\t\t\t\t_, err := p.UseRecoveryCode(ctx, x.NewUUID(), \"i-do-not-exist\")\n\t\t\t\trequire.Error(t, err)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should create a new recovery code\", func(t *testing.T) {\n\t\t\t\tdto, f, a := newRecoveryCodeDTO(t, \"foo-user@ory.sh\")\n\t\t\t\trCode, err := p.CreateRecoveryCode(ctx, dto)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, f.ID, rCode.FlowID)\n\t\t\t\tassert.Equal(t, dto.IdentityID, rCode.IdentityID)\n\t\t\t\trequire.True(t, rCode.RecoveryAddressID.Valid)\n\t\t\t\tassert.Equal(t, a.ID, rCode.RecoveryAddressID.UUID)\n\t\t\t\tassert.Equal(t, a.ID, rCode.RecoveryAddress.ID)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should create a recovery code and use it\", func(t *testing.T) {\n\t\t\t\tdto, f, _ := newRecoveryCodeDTO(t, \"other-user@ory.sh\")\n\t\t\t\t_, err := p.CreateRecoveryCode(ctx, dto)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tt.Run(\"not work on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.UseRecoveryCode(ctx, f.ID, dto.RawCode)\n\t\t\t\t\trequire.ErrorIs(t, err, code.ErrCodeNotFound)\n\t\t\t\t})\n\n\t\t\t\tactual, err := p.UseRecoveryCode(ctx, f.ID, dto.RawCode)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, nid, actual.NID)\n\t\t\t\tassert.Equal(t, dto.IdentityID, actual.IdentityID)\n\t\t\t\tassert.NotEqual(t, dto.RawCode, actual.CodeHMAC)\n\t\t\t\tassert.EqualValues(t, f.ID, actual.FlowID)\n\n\t\t\t\t_, err = p.UseRecoveryCode(ctx, f.ID, dto.RawCode)\n\t\t\t\trequire.ErrorIs(t, err, code.ErrCodeAlreadyUsed)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should not be able to use expired codes\", func(t *testing.T) {\n\t\t\t\tdto, f, _ := newRecoveryCodeDTO(t, \"expired-code@ory.sh\")\n\t\t\t\tdto.ExpiresIn = -time.Hour\n\t\t\t\t_, err := p.CreateRecoveryCode(ctx, dto)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t_, err = p.UseRecoveryCode(ctx, f.ID, dto.RawCode)\n\t\t\t\trequire.ErrorIs(t, err, code.ErrCodeNotFound)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should increment flow submit count and fail after too many tries (default limit)\", func(t *testing.T) {\n\t\t\t\tdto, f, _ := newRecoveryCodeDTO(t, \"submit-count-default-limit@ory.sh\")\n\t\t\t\t_, err := p.CreateRecoveryCode(ctx, dto)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tvar tooOften, wrongCode int32\n\t\t\t\tvar wg sync.WaitGroup\n\t\t\t\tfor range 50 {\n\t\t\t\t\twg.Add(1)\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\t\t_, err := p.UseRecoveryCode(ctx, f.ID, \"i-do-not-exist\")\n\t\t\t\t\t\tif !assert.Error(t, err) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif errors.Is(err, code.ErrCodeSubmittedTooOften) {\n\t\t\t\t\t\t\tatomic.AddInt32(&tooOften, 1)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tatomic.AddInt32(&wrongCode, 1)\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t}\n\t\t\t\twg.Wait()\n\n\t\t\t\trequire.EqualValues(t, 50, wrongCode+tooOften, \"all 50 attempts made\")\n\t\t\t\trequire.LessOrEqual(t, wrongCode, int32(5), \"max. 5 attempts have gone past the duplication check\")\n\n\t\t\t\t// Submit again, just to be sure\n\t\t\t\t_, err = p.UseRecoveryCode(ctx, f.ID, \"i-do-not-exist\")\n\t\t\t\trequire.ErrorIs(t, err, code.ErrCodeSubmittedTooOften)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should increment flow submit count and fail after too many tries (custom limit)\", func(t *testing.T) {\n\t\t\t\tlimit := 2\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeyCodeMaxSubmissions, limit)\n\n\t\t\t\tdto, f, _ := newRecoveryCodeDTO(t, \"submit-count-custom-limit@ory.sh\")\n\t\t\t\t_, err := p.CreateRecoveryCode(ctx, dto)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tvar tooOften, wrongCode int32\n\t\t\t\tvar wg sync.WaitGroup\n\t\t\t\tfor range 50 {\n\t\t\t\t\twg.Add(1)\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\t\t_, err := p.UseRecoveryCode(ctx, f.ID, \"i-do-not-exist\")\n\t\t\t\t\t\tif !assert.Error(t, err) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif errors.Is(err, code.ErrCodeSubmittedTooOften) {\n\t\t\t\t\t\t\tatomic.AddInt32(&tooOften, 1)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tatomic.AddInt32(&wrongCode, 1)\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t}\n\t\t\t\twg.Wait()\n\n\t\t\t\trequire.EqualValues(t, 50, wrongCode+tooOften, \"all 50 attempts made\")\n\t\t\t\trequire.LessOrEqual(t, wrongCode, int32(limit), \"max. %d attempts have gone past the duplication check\", limit)\n\n\t\t\t\t// Submit again, just to be sure\n\t\t\t\t_, err = p.UseRecoveryCode(ctx, f.ID, \"i-do-not-exist\")\n\t\t\t\trequire.ErrorIs(t, err, code.ErrCodeSubmittedTooOften)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should delete codes of flow\", func(t *testing.T) {\n\t\t\t\tdto, f, _ := newRecoveryCodeDTO(t, testhelpers.RandomEmail())\n\t\t\t\tfor i := 0; i < 10; i++ {\n\t\t\t\t\tdto.RawCode = string(randx.MustString(8, randx.Numeric))\n\t\t\t\t\t_, err := p.CreateRecoveryCode(ctx, dto)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t}\n\n\t\t\t\tcount, err := p.GetConnection(ctx).Where(\"selfservice_recovery_flow_id = ?\", f.ID).Count(&code.RecoveryCode{})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, 10, count)\n\n\t\t\t\terr = p.DeleteRecoveryCodesOfFlow(ctx, f.ID)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t// Count again, should be 0\n\t\t\t\tcount, err = p.GetConnection(ctx).Where(\"selfservice_recovery_flow_id = ?\", f.ID).Count(&code.RecoveryCode{})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, 0, count)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage strategy\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/httpx\"\n)\n\nconst EndpointDisabledMessage = \"This endpoint was disabled by system administrator. Please check your url or contact the system administrator to enable it.\"\n\ntype disabledChecker interface {\n\tconfig.Provider\n\thttpx.WriterProvider\n}\n\nfunc disabledWriter(c disabledChecker, enabled bool, wrap http.HandlerFunc, w http.ResponseWriter, r *http.Request) {\n\tif !enabled {\n\t\tc.Writer().WriteError(w, r, herodot.ErrNotFound.WithReason(EndpointDisabledMessage))\n\t\treturn\n\t}\n\twrap(w, r)\n}\n\nfunc IsDisabled(c disabledChecker, strategy string, wrap http.HandlerFunc) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tdisabledWriter(c, c.Config().SelfServiceStrategy(r.Context(), strategy).Enabled, wrap, w, r)\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.schema/login.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/identity_disovery/login.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"identifier\": {\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-case=Multi-Schema-method=PopulateLoginMethodIdentifierFirstIdentification.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"identifier_first\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"identifier_first\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"identifier_first\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_does_not_have_a_password.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_has_password.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_enabled.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"identifier_first\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"identifier_first\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"identifier_first\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/idfirst/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/idfirst/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage idfirst\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed .schema/login.schema.json\nvar LoginSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/idfirst/strategy.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage idfirst\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype dependencies interface {\n\tlogrusx.Provider\n\thttpx.WriterProvider\n\tnosurfx.CSRFTokenGeneratorProvider\n\tnosurfx.CSRFProvider\n\totelx.Provider\n\n\tconfig.Provider\n\n\tidentity.PrivilegedPoolProvider\n\tlogin.StrategyProvider\n\tlogin.FlowPersistenceProvider\n}\n\ntype Strategy struct{ d dependencies }\n\nfunc NewStrategy(d dependencies) *Strategy { return &Strategy{d: d} }\n\nfunc (s *Strategy) ID() identity.CredentialsType {\n\treturn identity.CredentialsType(node.IdentifierFirstGroup)\n}\n\nfunc (s *Strategy) CompletedAuthenticationMethod(ctx context.Context) session.AuthenticationMethod {\n\treturn session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    identity.NoAuthenticatorAssuranceLevel,\n\t}\n}\n\nfunc (s *Strategy) NodeGroup() node.UiNodeGroup {\n\treturn node.IdentifierFirstGroup\n}\n"
  },
  {
    "path": "selfservice/strategy/idfirst/strategy_login.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage idfirst\n\nimport (\n\t\"net/http\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/schema\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nvar (\n\t_                     login.AAL1FormHydrator = new(Strategy)\n\t_                     login.Strategy         = new(Strategy)\n\tErrNoCredentialsFound                        = errors.New(\"no credentials found\")\n)\n\nfunc (s *Strategy) handleLoginError(r *http.Request, f *login.Flow, payload UpdateLoginFlowWithIdentifierFirstMethod, err error) error {\n\tif f != nil {\n\t\tf.UI.Nodes.SetValueAttribute(\"identifier\", payload.Identifier)\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (_ *identity.Identity, err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.idfirst.Strategy.Login\")\n\tdefer otelx.End(span, &err)\n\n\tif !s.d.Config().SelfServiceLoginFlowIdentifierFirstEnabled(ctx) {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"strategy is not enabled\"))\n\t\treturn nil, errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel1); err != nil {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"requested AAL is not sufficient\"))\n\t\treturn nil, err\n\t}\n\n\tvar p UpdateLoginFlowWithIdentifierFirstMethod\n\tif err := decoderx.Decode(r, &p,\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.MustHTTPRawJSONSchemaCompiler(LoginSchema),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, p, err)\n\t}\n\tf.TransientPayload = p.TransientPayload\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, p, err)\n\t}\n\n\texpand := identity.ExpandCredentials\n\tif s.d.Config().SecurityAccountEnumerationMitigate(ctx) {\n\t\texpand = identity.ExpandNothing\n\t}\n\t// Look up the user by the identifier.\n\tidentityHint, err := s.d.PrivilegedIdentityPool().FindIdentityByCredentialIdentifier(ctx, p.Identifier,\n\t\t/* We are dealing with user input -> lookup should be case-insensitive */ false,\n\t\texpand,\n\t)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t// If the user is not found, we still want to potentially show the UI for some method. That's why we don't exit here.\n\t\t// We have to mitigate account enumeration. So we continue without setting the identity hint.\n\t\t//\n\t\t// This will later be handled by `didPopulate`.\n\t} else if err != nil {\n\t\t// An error happened during lookup\n\t\treturn nil, s.handleLoginError(r, f, p, err)\n\t}\n\n\tf.UI.ResetMessages()\n\tf.UI.Nodes.SetValueAttribute(\"identifier\", p.Identifier)\n\n\t// Add identity hint\n\topts := []login.FormHydratorModifier{login.WithIdentityHint(identityHint), login.WithIdentifier(p.Identifier)}\n\n\tdidPopulate := false\n\tfor _, ls := range s.d.LoginStrategies(ctx, login.PrepareOrganizations(r, f, sess)...) {\n\t\tpopulator, ok := ls.(login.AAL1FormHydrator)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := populator.PopulateLoginMethodIdentifierFirstCredentials(r, f, opts...); errors.Is(err, login.ErrBreakLoginPopulate) {\n\t\t\tdidPopulate = true\n\t\t\tbreak\n\t\t} else if errors.Is(err, ErrNoCredentialsFound) {\n\t\t\t// This strategy is not responsible for this flow. We do not set didPopulate to true if that happens.\n\t\t} else if err != nil {\n\t\t\treturn nil, s.handleLoginError(r, f, p, err)\n\t\t} else {\n\t\t\tdidPopulate = true\n\n\t\t\t// If the method supports FastLogin, we can attempt a fast path.\n\t\t\tif strategy, ok := ls.(login.FastLoginStrategy); ok {\n\t\t\t\terr = strategy.FastLogin1FA(w, r, f, sess)\n\t\t\t\tif err == nil {\n\t\t\t\t\treturn nil, flow.ErrCompletedByStrategy\n\t\t\t\t} else if !errors.Is(err, flow.ErrStrategyNotResponsible) {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// If no strategy populated, it means that the account (very likely) does not exist. We show a user not found error,\n\t// but only if account enumeration mitigation is disabled. Otherwise, we proceed to render the rest of the form.\n\tif !didPopulate && !s.d.Config().SecurityAccountEnumerationMitigate(ctx) {\n\t\treturn nil, s.handleLoginError(r, f, p, errors.WithStack(schema.NewAccountNotFoundError()))\n\t}\n\n\t// We found credentials - hide the identifier.\n\tf.UI.GetNodes().RemoveMatching(node.NewInputField(\"method\", s.ID(), s.NodeGroup(), node.InputAttributeTypeSubmit))\n\n\t// We set the identifier to hidden, so it's still available in the form but not visible to the user.\n\tfor k, n := range f.UI.Nodes {\n\t\tif n.ID() != \"identifier\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tattrs, ok := f.UI.Nodes[k].Attributes.(*node.InputAttributes)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tattrs.Autocomplete = node.InputAttributeAutocompleteUsernameWebauthn\n\n\t\tattrs.Type = node.InputAttributeTypeHidden\n\t\tf.UI.Nodes[k].Attributes = attrs\n\t}\n\n\tf.Active = s.ID()\n\tif err = s.d.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, p, err)\n\t}\n\n\tif x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteCode(w, r, http.StatusBadRequest, f)\n\t} else {\n\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowLoginUI(ctx)).String(), http.StatusSeeOther)\n\t}\n\n\treturn nil, flow.ErrCompletedByStrategy\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactorRefresh(r *http.Request, sr *login.Flow, _ *session.Session) error {\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactor(r *http.Request, sr *login.Flow) error {\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstIdentification(r *http.Request, f *login.Flow) error {\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\n\tds, err := f.IdentitySchema.URL(r.Context(), s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tidentifierLabel, err := login.GetIdentifierLabelFromSchema(r.Context(), ds.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tf.UI.SetNode(node.NewInputField(\"identifier\", \"\", s.NodeGroup(), node.InputAttributeTypeText, node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\ta.Autocomplete = node.InputAttributeAutocompleteUsernameWebauthn\n\t\ta.Required = true\n\t})).WithMetaLabel(identifierLabel))\n\tf.UI.GetNodes().Append(node.NewInputField(\"method\", s.ID(), s.NodeGroup(), node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelContinue()))\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstCredentials(_ *http.Request, f *login.Flow, opts ...login.FormHydratorModifier) error {\n\treturn ErrNoCredentialsFound\n}\n"
  },
  {
    "path": "selfservice/strategy/idfirst/strategy_login_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage idfirst_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\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\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/httprouterx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestCompleteLogin(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(map[string]any{\n\t\t\t// We enable the password method to test the identifier first strategy\n\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypePassword) + \".enabled\": true,\n\t\t\tconfig.ViperKeySelfServiceLoginFlowStyle: \"identifier_first\",\n\t\t}),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/default.schema.json\")),\n\t)\n\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tpublicTS, _ := testhelpers.NewKratosServerWithRouters(t, reg, router, httprouterx.NewTestRouterAdminWithPrefix(t))\n\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tuiTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\n\t// ensureFieldsExist := func(t *testing.T, body []byte) {\n\t//\tregistrationhelpers.CheckFormContent(t, body, \"identifier\",\n\t//\t\t\"password\",\n\t//\t\t\"csrf_token\")\n\t// }\n\n\tapiClient := testhelpers.NewDebugClient(t)\n\n\tt.Run(\"case=should show the error ui because the request payload is malformed\", func(t *testing.T) {\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\t\tbody, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, true, false, f, apiClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, body, `Expected JSON sent in request body to be an object but got: Number`)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false, testhelpers.InitFlowWithContext(t.Context()))\n\n\t\t\tbody, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, false, false, f, browserClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login-ts\")\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"invalid URL escape\", \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, true, false, false, testhelpers.InitFlowWithContext(t.Context()))\n\n\t\t\tbody, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, false, true, f, browserClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"invalid URL escape\", \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail because identifier first can not handle AAL2\", func(t *testing.T) {\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\tupdate, err := reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\trequire.NoError(t, err)\n\t\tupdate.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), update))\n\n\t\treq, err := http.NewRequest(\"POST\", f.Ui.Action, bytes.NewBufferString(`{\"method\":\"identifier_first\"}`))\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tactual, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, router, req)\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\tassert.Equal(t, text.NewErrorValidationLoginNoStrategyFound().Text, gjson.GetBytes(actual, \"ui.messages.0.text\").String())\n\t})\n\n\tt.Run(\"should return an error because the request does not exist\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.Equal(t, int64(http.StatusNotFound), gjson.Get(actual, \"code\").Int(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"Not Found\", gjson.Get(actual, \"status\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"message\").String(), \"Unable to locate the resource\", \"%s\", actual)\n\t\t}\n\n\t\tfakeFlow := &kratos.LoginFlow{\n\t\t\tUi: kratos.UiContainer{\n\t\t\t\tAction: publicTS.URL + login.RouteSubmitFlow + \"?flow=\" + x.NewUUID().String(),\n\t\t\t},\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, true, false, fakeFlow, apiClient, \"{}\")\n\t\t\tassert.Len(t, res.Cookies(), 0)\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tcheck(t, gjson.Get(actual, \"error\").Raw)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tactual, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, false, false, fakeFlow, browserClient, \"\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tcheck(t, actual)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, false, true, fakeFlow, apiClient, \"{}\")\n\t\t\tassert.Len(t, res.Cookies(), 0)\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tcheck(t, gjson.Get(actual, \"error\").Raw)\n\t\t})\n\t})\n\n\tt.Run(\"case=should return an error because the request is expired\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceLoginRequestLifespan, time.Millisecond*50)\n\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, true)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceLoginRequestLifespan, time.Hour)\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, nil)\n\t\t})\n\n\t\tvalues := url.Values{\n\t\t\t\"csrf_token\": {nosurfx.FakeCSRFToken},\n\t\t\t\"identifier\": {\"identifier\"},\n\t\t\t\"method\":     {\"identifier_first\"},\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\t\t// TODO: Testing expiry with time.Sleep is flaky, we should better use a fake clock at the registry level.\n\t\t\ttime.Sleep(time.Millisecond * 60)\n\t\t\tactual, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.Get(actual, \"use_flow_id\").String())\n\t\t\tassertx.EqualAsJSONExcept(t, flow.NewFlowExpiredError(time.Now()), json.RawMessage(actual), []string{\"use_flow_id\", \"since\", \"expired_at\"}, \"expired\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, false, false)\n\n\t\t\t// TODO: Testing expiry with time.Sleep is flaky, we should better use a fake clock at the registry level.\n\t\t\ttime.Sleep(time.Millisecond * 60)\n\t\t\tactual, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, false, false, f, browserClient, values.Encode())\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login-ts\")\n\t\t\tassert.NotEqual(t, f.Id, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.messages.0.text\").String(), \"expired\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=SPA\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, true, false, false)\n\n\t\t\t// TODO: Testing expiry with time.Sleep is flaky, we should better use a fake clock at the registry level.\n\t\t\ttime.Sleep(time.Millisecond * 60)\n\t\t\tactual, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, false, true, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.Get(actual, \"use_flow_id\").String())\n\t\t\tassertx.EqualAsJSONExcept(t, flow.NewFlowExpiredError(time.Now()), json.RawMessage(actual), []string{\"use_flow_id\", \"since\", \"expired_at\"}, \"expired\", \"%s\", actual)\n\t\t})\n\t})\n\n\tt.Run(\"case=should have correct CSRF behavior\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, true)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, nil)\n\t\t})\n\n\t\tvalues := url.Values{\n\t\t\t\"method\":     {\"identifier_first\"},\n\t\t\t\"csrf_token\": {\"invalid_token\"},\n\t\t\t\"identifier\": {\"login-identifier-csrf-browser\"},\n\t\t}\n\n\t\tt.Run(\"case=should fail because of missing CSRF token/type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tbrowserClient.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)\n\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values.Encode())\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenServerTokenMismatch,\n\t\t\t\tjson.RawMessage(actual), \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=should fail because of missing CSRF token/type=spa\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tbrowserClient.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, true, false, false)\n\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, false, true, f, browserClient, values.Encode())\n\t\t\tassert.EqualValues(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch,\n\t\t\t\tjson.RawMessage(gjson.Get(actual, \"error\").Raw), \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=should pass even without CSRF token/type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassert.Contains(t, actual, \"1010022\")\n\t\t})\n\n\t\tt.Run(\"case=should fail with correct CSRF error cause/type=api\", func(t *testing.T) {\n\t\t\tfor k, tc := range []struct {\n\t\t\t\tmod func(http.Header)\n\t\t\t\texp string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\t\th.Add(\"Cookie\", \"name=bar\")\n\t\t\t\t\t},\n\t\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Cookie\\\\\\\" key\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\t\th.Add(\"Origin\", \"www.bar.com\")\n\t\t\t\t\t},\n\t\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Origin\\\\\\\" key\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\t\t\t\treq := testhelpers.NewPostRequest(t, true, f.Ui.Action, bytes.NewBufferString(testhelpers.EncodeFormAsJSON(t, true, values)))\n\t\t\t\t\ttc.mod(req.Header)\n\n\t\t\t\t\tres, err := apiClient.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\t\tactual := string(ioutilx.MustReadAll(res.Body))\n\t\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\t\tassert.Contains(t, actual, tc.exp)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\texpectValidationError := func(t *testing.T, isAPI, refresh, isSPA bool, values func(url.Values)) string {\n\t\treturn testhelpers.SubmitLoginForm(t, isAPI, nil, publicTS, values,\n\t\t\tisSPA, refresh,\n\t\t\ttesthelpers.ExpectStatusCode(isAPI || isSPA, http.StatusBadRequest, http.StatusOK),\n\t\t\ttesthelpers.ExpectURL(isAPI || isSPA, publicTS.URL+login.RouteSubmitFlow, conf.SelfServiceFlowLoginUI(t.Context()).String()))\n\t}\n\n\tt.Run(\"should return an error because the user does not exist\", func(t *testing.T) {\n\t\t// In this test we check if the account mitigation behaves correctly by enabling all login strategies EXCEPT\n\t\t// for the passwordless code strategy. That is because this strategy always shows the login button.\n\n\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePassword.String(), true)\n\n\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeOIDC.String(), true)\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeOIDC)+\".config\", &oidc.ConfigurationCollection{Providers: []oidc.Configuration{\n\t\t\t{\n\t\t\t\tID:           \"google\",\n\t\t\t\tProvider:     \"google\",\n\t\t\t\tLabel:        \"Google\",\n\t\t\t\tClientID:     \"a\",\n\t\t\t\tClientSecret: \"b\",\n\t\t\t\tMapper:       \"file://\",\n\t\t\t},\n\t\t}})\n\n\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeWebAuthn.String(), true)\n\t\tconf.MustSet(t.Context(), config.ViperKeyWebAuthnPasswordless, true)\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeWebAuthn)+\".config.rp.display_name\", \"Ory Corp\")\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeWebAuthn)+\".config.rp.id\", \"localhost\")\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeWebAuthn)+\".config.rp.origin\", \"http://localhost:4455\")\n\n\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePasskey.String(), true)\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePasskey)+\".enabled\", true)\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePasskey)+\".config.rp.display_name\", \"Ory Corp\")\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePasskey)+\".config.rp.id\", \"localhost\")\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePasskey)+\".config.rp.origins\", []string{\"http://localhost:4455\"})\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), \"selfservice.methods.password\", nil)\n\t\t\tconf.MustSet(t.Context(), \"selfservice.methods.oidc\", nil)\n\t\t\tconf.MustSet(t.Context(), \"selfservice.methods.passkey\", nil)\n\t\t\tconf.MustSet(t.Context(), \"selfservice.methods.webauthn\", nil)\n\t\t\tconf.MustSet(t.Context(), \"selfservice.methods.code\", nil)\n\t\t})\n\n\t\tt.Run(\"account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, true)\n\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, nil)\n\t\t\t})\n\n\t\t\tcheck := func(t *testing.T, body string, isAPI bool) {\n\t\t\t\tt.Logf(\"%s\", body)\n\t\t\t\tif !isAPI {\n\t\t\t\t\tassert.Contains(t, body, fmt.Sprintf(\"%d\", text.InfoSelfServiceLoginWebAuthn), \"we do expect to see a webauthn trigger:\\n%s\", body)\n\t\t\t\t\tassert.Contains(t, body, fmt.Sprintf(\"%d\", text.InfoSelfServiceLoginPasskey), \"we do expect to see a passkey trigger button:\\n%s\", body)\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(t, \"hidden\", gjson.Get(body, \"ui.nodes.#(attributes.name==identifier).attributes.type\").String(), \"identifier is hidden to appear that we found an identity even though we did not\")\n\n\t\t\t\tassert.NotContains(t, body, text.NewErrorValidationAccountNotFound().Text, \"we do not expect to see an account not found error:\\n%s\", body)\n\n\t\t\t\tassert.Contains(t, body, fmt.Sprintf(\"%d\", text.InfoSelfServiceLoginPassword), \"we do expect to see a password trigger:\\n%s\", body)\n\n\t\t\t\t// We do expect to see the same social sign in buttons that were on the first page:\n\t\t\t\tassert.Contains(t, body, fmt.Sprintf(\"%d\", text.InfoSelfServiceLoginWith), \"we do expect to see a oidc trigger:\\n%s\", body)\n\t\t\t\tassert.Contains(t, body, \"google\", \"we do expect to see a google trigger:\\n%s\", body)\n\t\t\t}\n\n\t\t\tvalues := func(v url.Values) {\n\t\t\t\tv.Set(\"identifier\", \"identifier\")\n\t\t\t\tv.Set(\"method\", \"identifier_first\")\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, false, false, false, values), false)\n\t\t\t})\n\n\t\t\tt.Run(\"type=SPA\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, false, false, true, values), false)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, true, false, false, values), true)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, false)\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, nil)\n\t\t\t})\n\n\t\t\tcheck := func(t *testing.T, body string) {\n\t\t\t\tt.Logf(\"aaxx %s\", body)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.action\").String(), publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\t\t\t\tassert.Contains(t, body, text.NewErrorValidationAccountNotFound().Text, \"we do expect to see an error that the account does not exist: %s\", body)\n\n\t\t\t\tassert.Equal(t, \"text\", gjson.Get(body, \"ui.nodes.#(attributes.name==identifier).attributes.type\").String(), \"identifier is not hidden and we can see the input field as well\")\n\t\t\t\tassert.Equal(t, \"google\", gjson.Get(body, \"ui.nodes.#(attributes.name==provider).attributes.value\").String(), \"google oidc button is not hidden\")\n\n\t\t\t\tassert.NotContains(t, body, fmt.Sprintf(\"%d\", text.InfoSelfServiceLoginPasskey), \"we do not expect to see a passkey trigger button: %s\", body)\n\t\t\t\tassert.NotContains(t, body, fmt.Sprintf(\"%d\", text.InfoSelfServiceLoginWebAuthn), \"we do not expect to see a webauthn trigger: %s\", body)\n\t\t\t\tassert.NotContains(t, body, fmt.Sprintf(\"%d\", text.InfoSelfServiceLoginPassword), \"we do not expect to see a password trigger: %s\", body)\n\t\t\t}\n\n\t\t\tvalues := func(v url.Values) {\n\t\t\t\tv.Set(\"identifier\", \"identifier\")\n\t\t\t\tv.Set(\"method\", \"identifier_first\")\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, false, false, false, values))\n\t\t\t})\n\n\t\t\tt.Run(\"type=SPA\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, false, false, true, values))\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, true, false, false, values))\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"should pass with real request\", func(t *testing.T) {\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, true, false)\n\n\t\tfirstValues := func(v url.Values) {\n\t\t\tv.Set(\"identifier\", identifier)\n\t\t\tv.Set(\"method\", \"identifier_first\")\n\t\t}\n\n\t\tsecondValues := func(v url.Values) {\n\t\t\tv.Set(\"identifier\", identifier)\n\t\t\tv.Set(\"password\", pwd)\n\t\t\tv.Set(\"method\", \"password\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\n\t\t\tsecondStep := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, firstValues,\n\t\t\t\ttrue, false, http.StatusBadRequest, publicTS.URL+login.RouteSubmitFlow)\n\t\t\tt.Logf(\"secondStep: %s\", secondStep)\n\t\t\tassert.Contains(t, secondStep, \"current-password\")\n\t\t\tassert.Contains(t, secondStep, `\"value\":\"password\"`)\n\n\t\t\tbody := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, secondValues,\n\t\t\t\tfalse, false, http.StatusOK, redirTS.URL)\n\n\t\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.email\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\thc := testhelpers.NewClientWithCookies(t)\n\n\t\t\tsecondStep := testhelpers.SubmitLoginForm(t, false, hc, publicTS, firstValues,\n\t\t\t\ttrue, false, http.StatusBadRequest, publicTS.URL+login.RouteSubmitFlow)\n\t\t\tt.Logf(\"secondStep: %s\", secondStep)\n\t\t\tassert.Contains(t, secondStep, \"current-password\")\n\t\t\tassert.Contains(t, secondStep, `\"value\":\"password\"`)\n\n\t\t\tbody := testhelpers.SubmitLoginForm(t, false, hc, publicTS, secondValues,\n\t\t\t\ttrue, false, http.StatusOK, publicTS.URL+login.RouteSubmitFlow)\n\n\t\t\tassert.Equal(t, identifier, gjson.Get(body, \"session.identity.traits.email\").String(), \"%s\", body)\n\t\t\tassert.Empty(t, gjson.Get(body, \"session_token\").String(), \"%s\", body)\n\t\t\tassert.Empty(t, gjson.Get(body, \"session.token\").String(), \"%s\", body)\n\n\t\t\t// Was the session cookie set?\n\t\t\trequire.NotEmpty(t, hc.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL)), \"%+v\", hc.Jar)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tsecondStep := testhelpers.SubmitLoginForm(t, true, nil, publicTS, firstValues,\n\t\t\t\tfalse, false, http.StatusBadRequest, publicTS.URL+login.RouteSubmitFlow)\n\t\t\tt.Logf(\"secondStep: %s\", secondStep)\n\t\t\tassert.Contains(t, secondStep, \"current-password\")\n\t\t\tassert.Contains(t, secondStep, `\"value\":\"password\"`)\n\n\t\t\tbody := testhelpers.SubmitLoginForm(t, true, nil, publicTS, secondValues,\n\t\t\t\tfalse, false, http.StatusOK, publicTS.URL+login.RouteSubmitFlow)\n\n\t\t\tassert.Equal(t, identifier, gjson.Get(body, \"session.identity.traits.email\").String(), \"%s\", body)\n\t\t\tst := gjson.Get(body, \"session_token\").String()\n\t\t\tassert.NotEmpty(t, st, \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"with code method\", func(t *testing.T) {\n\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeCodeAuth.String(), true)\n\t\tconf.MustSet(t.Context(), fmt.Sprintf(\"%s.%s.passwordless_enabled\", config.ViperKeySelfServiceStrategyConfig, identity.CredentialsTypeCodeAuth.String()), true)\n\t\tt.Cleanup(func() {\n\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeCodeAuth.String(), false)\n\t\t\tconf.MustSet(t.Context(), fmt.Sprintf(\"%s.%s.passwordless_enabled\", config.ViperKeySelfServiceStrategyConfig, identity.CredentialsTypeCodeAuth.String()), nil)\n\t\t})\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\n\t\tt.Run(\"only code method enabled\", func(t *testing.T) {\n\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePassword.String(), false)\n\t\t\tt.Cleanup(func() {\n\t\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePassword.String(), true)\n\t\t\t})\n\n\t\t\temail := testhelpers.RandomEmail()\n\t\t\tcreateIdentity(t.Context(), reg, t, email, false, true)\n\t\t\tfirstStepValues := func(v url.Values) {\n\t\t\t\tv.Set(\"identifier\", email)\n\t\t\t\tv.Set(\"method\", \"identifier_first\")\n\t\t\t}\n\n\t\t\tt.Run(\"account enumeration mitigation off\", func(t *testing.T) {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, false)\n\n\t\t\t\tfirstStep := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, firstStepValues,\n\t\t\t\t\ttrue, false, http.StatusBadRequest, publicTS.URL+login.RouteSubmitFlow)\n\t\t\t\tb := []byte(firstStep)\n\n\t\t\t\t// Expect email sent (fast login)\n\t\t\t\trequire.EqualValues(t, flow.StateEmailSent, gjson.GetBytes(b, \"state\").String(), \"%s\", b)\n\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", b)\n\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.messages\").Array(), 1, \"%s\", b)\n\t\t\t\trequire.EqualValues(t, text.InfoSelfServiceLoginCodeSent, gjson.GetBytes(b, \"ui.messages.0.id\").Int(), \"%s\", b)\n\t\t\t})\n\n\t\t\tt.Run(\"account enumeration mitigation on\", func(t *testing.T) {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, true)\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, false)\n\t\t\t\t})\n\n\t\t\t\tfirstStep := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, firstStepValues,\n\t\t\t\t\ttrue, false, http.StatusBadRequest, publicTS.URL+login.RouteSubmitFlow)\n\t\t\t\tb := []byte(firstStep)\n\n\t\t\t\t// Expect email sent (fast login)\n\t\t\t\trequire.EqualValues(t, flow.StateEmailSent, gjson.GetBytes(b, \"state\").String(), \"%s\", b)\n\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", b)\n\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.messages\").Array(), 1, \"%s\", b)\n\t\t\t\trequire.EqualValues(t, text.InfoSelfServiceLoginCodeSent, gjson.GetBytes(b, \"ui.messages.0.id\").Int(), \"%s\", b)\n\t\t\t})\n\t\t})\n\t\tt.Run(\"all methods enabled\", func(t *testing.T) {\n\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePassword.String(), true)\n\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeCodeAuth.String(), true)\n\n\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeWebAuthn.String(), true)\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeWebAuthn)+\".config.rp.display_name\", \"Ory Corp\")\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeWebAuthn)+\".config.rp.id\", \"localhost\")\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeWebAuthn)+\".config.rp.origin\", \"http://localhost:4455\")\n\n\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePasskey.String(), true)\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePasskey)+\".config.rp.display_name\", \"Ory Corp\")\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePasskey)+\".config.rp.id\", \"localhost\")\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePasskey)+\".config.rp.origins\", []string{\"http://localhost:4455\"})\n\n\t\t\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeOIDC.String(), true)\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeOIDC)+\".config\", &oidc.ConfigurationCollection{Providers: []oidc.Configuration{\n\t\t\t\t{\n\t\t\t\t\tID:           \"google\",\n\t\t\t\t\tProvider:     \"google\",\n\t\t\t\t\tLabel:        \"Google\",\n\t\t\t\t\tClientID:     \"a\",\n\t\t\t\t\tClientSecret: \"b\",\n\t\t\t\t\tMapper:       \"file://\",\n\t\t\t\t},\n\t\t\t}})\n\n\t\t\tt.Run(\"all methods configured\", func(t *testing.T) {\n\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentityWithAllMethods(t.Context(), reg, t, email)\n\t\t\t\tfirstStepValues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", email)\n\t\t\t\t\tv.Set(\"method\", \"identifier_first\")\n\t\t\t\t}\n\n\t\t\t\tfirstStep := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, firstStepValues,\n\t\t\t\t\ttrue, false, http.StatusBadRequest, publicTS.URL+login.RouteSubmitFlow)\n\t\t\t\tb := []byte(firstStep)\n\n\t\t\t\t// Expect method selection\n\t\t\t\trequire.EqualValues(t, flow.StateChooseMethod, gjson.GetBytes(b, \"state\").String(), \"%s\", b)\n\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.nodes.#(group==password)\").Array(), 1, \"%s\", b)\n\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", b)\n\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.nodes.#(group==webauthn)\").Array(), 1, \"%s\", b)\n\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.nodes.#(group==passkey)\").Array(), 1, \"%s\", b)\n\t\t\t\trequire.False(t, gjson.GetBytes(b, \"ui.messages\").Exists())\n\t\t\t})\n\t\t\tt.Run(\"only code method configured\", func(t *testing.T) {\n\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\tcreateIdentity(t.Context(), reg, t, email, false, true)\n\t\t\t\tfirstStepValues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", email)\n\t\t\t\t\tv.Set(\"method\", \"identifier_first\")\n\t\t\t\t}\n\t\t\t\tt.Run(\"account enumeration mitigation off\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, false)\n\n\t\t\t\t\tfirstStep := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, firstStepValues,\n\t\t\t\t\t\ttrue, false, http.StatusBadRequest, publicTS.URL+login.RouteSubmitFlow)\n\t\t\t\t\tb := []byte(firstStep)\n\n\t\t\t\t\t// Expect email sent (fast login)\n\t\t\t\t\trequire.EqualValues(t, flow.StateEmailSent, gjson.GetBytes(b, \"state\").String(), \"%s\", b)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", b)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.messages\").Array(), 1, \"%s\", b)\n\t\t\t\t\trequire.EqualValues(t, text.InfoSelfServiceLoginCodeSent, gjson.GetBytes(b, \"ui.messages.0.id\").Int(), \"%s\", b)\n\t\t\t\t})\n\t\t\t\tt.Run(\"account enumeration mitigation on\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, true)\n\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, false)\n\t\t\t\t\t})\n\n\t\t\t\t\tfirstStep := testhelpers.SubmitLoginFormCtx(t.Context(), t, false, browserClient, publicTS, firstStepValues,\n\t\t\t\t\t\ttrue, false, http.StatusBadRequest, publicTS.URL+login.RouteSubmitFlow)\n\t\t\t\t\tb := []byte(firstStep)\n\n\t\t\t\t\t// Expect method selection\n\t\t\t\t\trequire.EqualValues(t, flow.StateChooseMethod, gjson.GetBytes(b, \"state\").String(), \"%s\", b)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.nodes.#(group==password)\").Array(), 1, \"%s\", b)\n\t\t\t\t\trequire.Len(t, gjson.GetBytes(b, \"ui.nodes.#(group==code)\").Array(), 1, \"%s\", b)\n\t\t\t\t\trequire.False(t, gjson.GetBytes(b, \"ui.messages\").Exists())\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n}\n\nfunc TestFormHydration(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]any{\n\t\tconfig.ViperKeySelfServiceLoginFlowStyle: \"identifier_first\",\n\t\tconfig.ViperKeyDefaultIdentitySchemaID:   \"default\",\n\t\tconfig.ViperKeyIdentitySchemas: config.Schemas{\n\t\t\t{ID: \"default\", URL: \"file://stub/default.schema.json\", SelfserviceSelectable: true},\n\t\t\t{ID: \"not-default\", URL: \"file://stub/doesnotexist.schema.json\", SelfserviceSelectable: true},\n\t\t},\n\t}))\n\n\ts, err := reg.AllLoginStrategies().Strategy(identity.CredentialsType(node.IdentifierFirstGroup))\n\trequire.NoError(t, err)\n\tfh, ok := s.(login.AAL1FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f *login.Flow) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.UI.Nodes.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f.UI.Nodes)\n\t}\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *login.Flow) {\n\t\tt.Helper()\n\t\tquery := \"\"\n\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/browser\"+query, nil)\n\t\tr = r.WithContext(t.Context())\n\t\tf, err := login.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactor\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactor(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactorRefresh\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstCredentials\", func(t *testing.T) {\n\t\tt.Run(\"case=no options\", func(t *testing.T) {\n\t\t\tr, f := newFlow(t.Context(), t)\n\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=WithIdentifier\", func(t *testing.T) {\n\t\t\tr, f := newFlow(t.Context(), t)\n\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=WithIdentityHint\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, true)\n\n\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, false)\n\n\t\t\t\tt.Run(\"case=identity has password\", func(t *testing.T) {\n\t\t\t\t\tid := identity.NewIdentity(\"default\")\n\n\t\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=identity does not have a password\", func(t *testing.T) {\n\t\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\t\tr, f := newFlow(t.Context(), t)\n\t\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstIdentification\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodIdentifierFirstIdentification(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/idfirst/strategy_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage idfirst_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\nfunc TestCompletedAuthenticationMethod(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\ts := idfirst.NewStrategy(reg)\n\tctx := context.Background()\n\n\tmethod := s.CompletedAuthenticationMethod(ctx)\n\tassert.Equal(t, s.ID(), method.Method)\n\tassert.Equal(t, identity.NoAuthenticatorAssuranceLevel, method.AAL)\n}\n\nfunc TestNodeGroup(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\ts := idfirst.NewStrategy(reg)\n\n\tgroup := s.NodeGroup()\n\tassert.Equal(t, node.IdentifierFirstGroup, group)\n}\n\nfunc createIdentity(ctx context.Context, reg *driver.RegistryDefault, t *testing.T, email string, withPassword bool, withCode bool) *identity.Identity {\n\tiId := x.NewUUID()\n\tid := &identity.Identity{\n\t\tID:          iId,\n\t\tTraits:      identity.Traits(fmt.Sprintf(`{ \"email\": \"%s\" }`, email)),\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{},\n\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t{\n\t\t\t\tValue:    email,\n\t\t\t\tVerified: true,\n\t\t\t\tStatus:   identity.VerifiableAddressStatusCompleted,\n\t\t\t},\n\t\t},\n\t}\n\tif withPassword {\n\t\tp, _ := reg.Hasher(ctx).Generate(context.Background(), []byte(\"password\"))\n\t\tid.Credentials[identity.CredentialsTypePassword] = identity.Credentials{\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{email},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t}\n\t}\n\tif withCode {\n\t\tid.Credentials[identity.CredentialsTypeCodeAuth] = identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeCodeAuth,\n\t\t\tIdentifiers: []string{email}, Config: sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"` + email + `\"}]}`),\n\t\t}\n\t}\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), id))\n\treturn id\n}\n\nfunc createIdentityWithAllMethods(ctx context.Context, reg *driver.RegistryDefault, t *testing.T, email string) *identity.Identity {\n\tid := x.NewUUID()\n\tpwd, _ := reg.Hasher(ctx).Generate(context.Background(), []byte(\"password\"))\n\tnewIdentity := &identity.Identity{\n\t\tID:     id,\n\t\tTraits: identity.Traits(fmt.Sprintf(`{ \"email\": \"%s\" }`, email)),\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(pwd) + `\"}`),\n\t\t\t},\n\t\t\tidentity.CredentialsTypeCodeAuth: {\n\t\t\t\tType:        identity.CredentialsTypeCodeAuth,\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"` + email + `\"}]}`),\n\t\t\t},\n\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"some\" : \"secret\"}`),\n\t\t\t},\n\t\t\tidentity.CredentialsTypeSAML: {\n\t\t\t\tType:        identity.CredentialsTypeSAML,\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"saml\" : \"secret\"}`),\n\t\t\t},\n\t\t\tidentity.CredentialsTypeWebAuthn: {\n\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"some\" : \"secret\", \"user_handle\": \"rVIFaWRcTTuQLkXFmQWpgA==\"}`),\n\t\t\t},\n\t\t\tidentity.CredentialsTypePasskey: {\n\t\t\t\tType:        identity.CredentialsTypePasskey,\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      []byte(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\"},{\"id\":\"YmFyYmFy\",\"display_name\":\"bar\"}]}`),\n\t\t\t},\n\t\t},\n\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t{\n\t\t\t\tValue:    email,\n\t\t\t\tVerified: true,\n\t\t\t\tStatus:   identity.VerifiableAddressStatusCompleted,\n\t\t\t},\n\t\t},\n\t}\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), newIdentity))\n\treturn newIdentity\n}\n"
  },
  {
    "path": "selfservice/strategy/idfirst/stub/default.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/idfirst/types.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage idfirst\n\nimport \"encoding/json\"\n\n// Update Login Flow with Multi-Step Method\n//\n// swagger:model updateLoginFlowWithIdentifierFirstMethod\ntype UpdateLoginFlowWithIdentifierFirstMethod struct {\n\t// Method should be set to \"password\" when logging in using the identifier and password strategy.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Identifier is the email or username of the user trying to log in.\n\t//\n\t// required: true\n\tIdentifier string `json:\"identifier\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n"
  },
  {
    "path": "selfservice/strategy/link/.schema/email.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/password/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\n    \"email\"\n  ],\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"minLength\": 1\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/link/.schema/recovery.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/profile/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"token\": {\n      \"type\": \"string\"\n    },\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\"\n    },\n    \"flow\": {\n      \"type\": \"string\",\n      \"format\": \"uuid\"\n    },\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/link/.schema/verification.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/profile/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"token\": {\n      \"type\": \"string\"\n    },\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\"\n    },\n    \"flow\": {\n      \"type\": \"string\",\n      \"format\": \"uuid\"\n    },\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/link/.snapshots/TestRecovery-description=should_set_all_the_correct_recovery_payloads.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"link\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"link\"\n    },\n    \"group\": \"link\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/link/.snapshots/TestRecovery-description=should_set_all_the_correct_recovery_payloads_after_submission.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"link\",\n    \"attributes\": {\n      \"name\": \"email\",\n      \"type\": \"email\",\n      \"value\": \"test@ory.sh\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"link\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"link\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/link/.snapshots/TestVerification-description=should_set_all_the_correct_verification_payloads.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"link\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"link\"\n    },\n    \"group\": \"link\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/link/.snapshots/TestVerification-description=should_set_all_the_correct_verification_payloads_after_submission.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"link\",\n    \"attributes\": {\n      \"name\": \"email\",\n      \"type\": \"email\",\n      \"value\": \"test@ory.sh\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070007,\n        \"text\": \"Email\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"link\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"link\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070009,\n        \"text\": \"Continue\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/link/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link\n\nimport (\n\t\"context\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype (\n\tRecoveryTokenPersister interface {\n\t\tCreateRecoveryToken(ctx context.Context, token *RecoveryToken) error\n\t\tUseRecoveryToken(ctx context.Context, fID uuid.UUID, token string) (*RecoveryToken, error)\n\t\tDeleteRecoveryToken(ctx context.Context, token string) error\n\t}\n\n\tRecoveryTokenPersistenceProvider interface {\n\t\tRecoveryTokenPersister() RecoveryTokenPersister\n\t}\n\n\tVerificationTokenPersister interface {\n\t\tCreateVerificationToken(ctx context.Context, token *VerificationToken) error\n\t\tUseVerificationToken(ctx context.Context, fID uuid.UUID, token string) (*VerificationToken, error)\n\t\tDeleteVerificationToken(ctx context.Context, token string) error\n\t}\n\n\tVerificationTokenPersistenceProvider interface {\n\t\tVerificationTokenPersister() VerificationTokenPersister\n\t}\n)\n"
  },
  {
    "path": "selfservice/strategy/link/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed .schema/recovery.schema.json\nvar recoveryMethodSchema []byte\n\n//go:embed .schema/verification.schema.json\nvar verificationMethodSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/link/sender.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/courier/template/email\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/urlx\"\n)\n\ntype (\n\tsenderDependencies interface {\n\t\tcourier.Provider\n\t\tcourier.ConfigProvider\n\n\t\tidentity.PoolProvider\n\t\tidentity.ManagementProvider\n\t\tidentity.PrivilegedPoolProvider\n\t\tlogrusx.Provider\n\t\tconfig.Provider\n\n\t\tVerificationTokenPersistenceProvider\n\t\tRecoveryTokenPersistenceProvider\n\n\t\thttpx.ClientProvider\n\t}\n\tSenderProvider interface {\n\t\tLinkSender() *Sender\n\t}\n\n\tSender struct {\n\t\tr senderDependencies\n\t}\n)\n\nvar ErrUnknownAddress = errors.New(\"verification requested for unknown address\")\n\nfunc NewSender(r senderDependencies) *Sender {\n\treturn &Sender{r: r}\n}\n\n// SendRecoveryLink sends a recovery link to the specified address\n//\n// If the address does not exist in the store and dispatching invalid emails is enabled (CourierEnableInvalidDispatch is\n// true), an email is still being sent to prevent account enumeration attacks. In that case, this function returns the\n// ErrUnknownAddress error.\nfunc (s *Sender) SendRecoveryLink(ctx context.Context, f *recovery.Flow, via, to string) error {\n\ts.r.Logger().\n\t\tWithField(\"via\", via).\n\t\tWithSensitiveField(\"address\", to).\n\t\tDebug(\"Preparing recovery link.\")\n\n\taddress, err := s.r.IdentityPool().FindRecoveryAddressByValue(ctx, via, to)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\tnotifyUnknownRecipients := s.r.Config().SelfServiceFlowRecoveryNotifyUnknownRecipients(ctx)\n\t\ts.r.Logger().\n\t\t\tWithField(\"via\", via).\n\t\t\tWithField(\"strategy\", \"link\").\n\t\t\tWithSensitiveField(\"email_address\", address).\n\t\t\tWithField(\"was_notified\", notifyUnknownRecipients).\n\t\t\tInfo(\"Account recovery was requested for an unknown address.\")\n\n\t\ttransientPayload, err := x.ParseRawMessageOrEmpty(f.GetTransientPayload())\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tif !notifyUnknownRecipients {\n\t\t\t// do nothing\n\t\t} else if err := s.send(ctx, string(via), email.NewRecoveryInvalid(s.r, &email.RecoveryInvalidModel{\n\t\t\tTo:               to,\n\t\t\tRequestURL:       f.GetRequestURL(),\n\t\t\tTransientPayload: transientPayload,\n\t\t})); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn errors.WithStack(ErrUnknownAddress)\n\t} else if err != nil {\n\t\t// DB error\n\t\treturn err\n\t}\n\n\t// Get the identity associated with the recovery address\n\ti, err := s.r.IdentityPool().GetIdentity(ctx, address.IdentityID, identity.ExpandDefault)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttoken := NewSelfServiceRecoveryToken(address, f, s.r.Config().SelfServiceLinkMethodLifespan(ctx))\n\tif err := s.r.RecoveryTokenPersister().CreateRecoveryToken(ctx, token); err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.SendRecoveryTokenTo(ctx, f, i, address, token); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// SendVerificationLink sends a verification link to the specified address\n//\n// If the address does not exist in the store and dispatching invalid emails is enabled (CourierEnableInvalidDispatch is\n// true), an email is still being sent to prevent account enumeration attacks. In that case, this function returns the\n// ErrUnknownAddress error.\nfunc (s *Sender) SendVerificationLink(ctx context.Context, f *verification.Flow, via, to string) error {\n\ts.r.Logger().\n\t\tWithField(\"via\", via).\n\t\tWithSensitiveField(\"address\", to).\n\t\tDebug(\"Preparing verification link.\")\n\n\taddress, err := s.r.IdentityPool().FindVerifiableAddressByValue(ctx, via, to)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\tnotifyUnknownRecipients := s.r.Config().SelfServiceFlowVerificationNotifyUnknownRecipients(ctx)\n\t\ts.r.Logger().\n\t\t\tWithField(\"via\", via).\n\t\t\tWithField(\"strategy\", \"link\").\n\t\t\tWithSensitiveField(\"email_address\", to).\n\t\t\tWithField(\"was_notified\", notifyUnknownRecipients).\n\t\t\tInfo(\"Address verification was requested for an unknown address.\")\n\n\t\ttransientPayload, err := x.ParseRawMessageOrEmpty(f.GetTransientPayload())\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\t\tif !notifyUnknownRecipients {\n\t\t\t// do nothing\n\t\t} else if err := s.send(ctx, string(via), email.NewVerificationInvalid(s.r, &email.VerificationInvalidModel{\n\t\t\tTo:               to,\n\t\t\tRequestURL:       f.GetRequestURL(),\n\t\t\tTransientPayload: transientPayload,\n\t\t})); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn errors.WithStack(ErrUnknownAddress)\n\t} else if err != nil {\n\t\t// DB error\n\t\treturn err\n\t}\n\n\t// Get the identity associated with the verification address\n\ti, err := s.r.IdentityPool().GetIdentity(ctx, address.IdentityID, identity.ExpandDefault)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttoken := NewSelfServiceVerificationToken(address, f, s.r.Config().SelfServiceLinkMethodLifespan(ctx))\n\tif err := s.r.VerificationTokenPersister().CreateVerificationToken(ctx, token); err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.SendVerificationTokenTo(ctx, f, i, address, token); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (s *Sender) SendRecoveryTokenTo(ctx context.Context, f *recovery.Flow, i *identity.Identity, address *identity.RecoveryAddress, token *RecoveryToken) error {\n\ts.r.Logger().\n\t\tWithField(\"via\", address.Via).\n\t\tWithField(\"identity_id\", address.IdentityID).\n\t\tWithField(\"recovery_link_id\", token.ID).\n\t\tWithSensitiveField(\"email_address\", address.Value).\n\t\tWithSensitiveField(\"recovery_link_token\", token.Token).\n\t\tInfo(\"Sending out recovery email with recovery link.\")\n\n\tmodel, err := x.StructToMap(i)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttransientPayload, err := x.ParseRawMessageOrEmpty(f.GetTransientPayload())\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\trecoveryUrl := urlx.CopyWithQuery(\n\t\turlx.AppendPaths(s.r.Config().SelfServiceLinkMethodBaseURL(ctx), recovery.RouteSubmitFlow),\n\t\turl.Values{\n\t\t\t\"token\": {token.Token},\n\t\t\t\"flow\":  {f.ID.String()},\n\t\t}).\n\t\tString()\n\n\treturn s.send(ctx, string(address.Via), email.NewRecoveryValid(s.r,\n\t\t&email.RecoveryValidModel{\n\t\t\tTo:               address.Value,\n\t\t\tRecoveryURL:      recoveryUrl,\n\t\t\tIdentity:         model,\n\t\t\tRequestURL:       f.GetRequestURL(),\n\t\t\tTransientPayload: transientPayload,\n\t\t\tExpiresInMinutes: int(s.r.Config().SelfServiceLinkMethodLifespan(ctx).Minutes()),\n\t\t}))\n}\n\nfunc (s *Sender) SendVerificationTokenTo(ctx context.Context, f *verification.Flow, i *identity.Identity, address *identity.VerifiableAddress, token *VerificationToken) error {\n\ts.r.Logger().\n\t\tWithField(\"via\", address.Via).\n\t\tWithField(\"identity_id\", address.IdentityID).\n\t\tWithField(\"verification_link_id\", token.ID).\n\t\tWithSensitiveField(\"email_address\", address.Value).\n\t\tWithSensitiveField(\"verification_link_token\", token.Token).\n\t\tInfo(\"Sending out verification email with verification link.\")\n\n\tmodel, err := x.StructToMap(i)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttransientPayload, err := x.ParseRawMessageOrEmpty(f.GetTransientPayload())\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tverificationUrl := urlx.CopyWithQuery(\n\t\turlx.AppendPaths(s.r.Config().SelfServiceLinkMethodBaseURL(ctx), verification.RouteSubmitFlow),\n\t\turl.Values{\n\t\t\t\"flow\":  {f.ID.String()},\n\t\t\t\"token\": {token.Token},\n\t\t}).String()\n\n\tif err := s.send(ctx, address.Via, email.NewVerificationValid(s.r,\n\t\t&email.VerificationValidModel{\n\t\t\tTo:               address.Value,\n\t\t\tVerificationURL:  verificationUrl,\n\t\t\tIdentity:         model,\n\t\t\tRequestURL:       f.GetRequestURL(),\n\t\t\tTransientPayload: transientPayload,\n\t\t\tExpiresInMinutes: int(s.r.Config().SelfServiceLinkMethodLifespan(ctx).Minutes()),\n\t\t})); err != nil {\n\t\treturn err\n\t}\n\taddress.Status = identity.VerifiableAddressStatusSent\n\tif err := s.r.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, address, \"status\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (s *Sender) send(ctx context.Context, via string, t courier.EmailTemplate) error {\n\tswitch via {\n\tcase identity.AddressTypeEmail:\n\t\tc, err := s.r.Courier(ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = c.QueueEmail(ctx, t)\n\t\treturn err\n\tdefault:\n\t\treturn errors.Errorf(\"received unexpected via type: %s\", via)\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/link/sender_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestManager(t *testing.T) {\n\tt.Parallel()\n\n\tctx := t.Context()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tinitViper(t, conf)\n\tctx = testhelpers.WithDefaultIdentitySchema(ctx, \"file://./stub/default.schema.json\")\n\tctx = contextx.WithConfigValues(ctx, map[string]any{\n\t\tconfig.ViperKeyPublicBaseURL:                                  \"https://www.ory.sh/\",\n\t\tconfig.ViperKeyCourierSMTPURL:                                 \"smtp://foo@bar@dev.null/\",\n\t\tconfig.ViperKeySelfServiceRecoveryNotifyUnknownRecipients:     true,\n\t\tconfig.ViperKeySelfServiceVerificationNotifyUnknownRecipients: true,\n\t})\n\n\tu := &http.Request{URL: urlx.ParseOrPanic(\"https://www.ory.sh/\")}\n\n\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\ti.Traits = identity.Traits(`{\"email\": \"tracked@ory.sh\"}`)\n\trequire.NoError(t, reg.IdentityManager().Create(ctx, i))\n\n\tfor _, tc := range []struct {\n\t\td               string\n\t\tsetupContext    func(ctx context.Context) context.Context\n\t\trecoveryURL     string\n\t\tverificationURL string\n\t}{{\n\t\td:               \"without BaseURL\",\n\t\tsetupContext:    func(ctx context.Context) context.Context { return ctx },\n\t\trecoveryURL:     \"https://www.ory.sh/self-service/recovery?flow=\",\n\t\tverificationURL: \"https://www.ory.sh/self-service/verification?flow=\",\n\t}, {\n\t\td: \"with BaseURL\",\n\t\tsetupContext: func(ctx context.Context) context.Context {\n\t\t\treturn x.WithBaseURL(ctx, urlx.ParseOrPanic(\"https://proxy.example.com/some/subpath/\"))\n\t\t},\n\t\trecoveryURL:     \"https://proxy.example.com/some/subpath/self-service/recovery?flow=\",\n\t\tverificationURL: \"https://proxy.example.com/some/subpath/self-service/verification?flow=\",\n\t}} {\n\t\tctx := tc.setupContext(ctx)\n\n\t\tt.Run(\"case=\"+tc.d, func(t *testing.T) {\n\t\t\tt.Run(\"method=SendRecoveryLink\", func(t *testing.T) {\n\t\t\t\ts, _, err := reg.RecoveryStrategies(ctx).ActiveStrategies(\"link\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tf, err := recovery.NewFlow(conf, time.Hour, \"\", u, s, flow.TypeBrowser)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\trequire.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(ctx, f))\n\n\t\t\t\trequire.NoError(t, reg.LinkSender().SendRecoveryLink(ctx, f, \"email\", \"tracked@ory.sh\"))\n\t\t\t\trequire.EqualError(t, reg.LinkSender().SendRecoveryLink(ctx, f, \"email\", \"not-tracked@ory.sh\"), link.ErrUnknownAddress.Error())\n\n\t\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 12)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, messages, 2)\n\n\t\t\t\tassert.EqualValues(t, \"tracked@ory.sh\", messages[0].Recipient)\n\t\t\t\tassert.Contains(t, messages[0].Subject, \"Recover access to your account\")\n\t\t\t\tassert.Contains(t, messages[0].Body, tc.recoveryURL)\n\t\t\t\tassert.Contains(t, messages[0].Body, \"token=\")\n\t\t\t\tassert.Contains(t, messages[0].Body, \"flow=\")\n\n\t\t\t\tassert.EqualValues(t, \"not-tracked@ory.sh\", messages[1].Recipient)\n\t\t\t\tassert.Contains(t, messages[1].Subject, \"Account access attempted\")\n\t\t\t\tassert.NotContains(t, messages[1].Body, f.RequestURL+\"self-service/recovery?flow=\")\n\t\t\t\tassert.NotContains(t, messages[1].Body, \"token=\")\n\t\t\t\tassert.NotContains(t, messages[1].Body, \"flow=\")\n\t\t\t})\n\n\t\t\tt.Run(\"method=SendRecoveryLink via HTTP\", func(t *testing.T) {\n\t\t\t\ttype requestBody struct {\n\t\t\t\t\tRecipient    string\n\t\t\t\t\tRecoveryURL  string `json:\"recovery_url\"`\n\t\t\t\t\tTo           string\n\t\t\t\t\tTemplateType string\n\t\t\t\t\tSubject      string\n\t\t\t\t}\n\t\t\t\tmessages := make(chan *requestBody, 2)\n\t\t\t\tsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t\tvar message requestBody\n\t\t\t\t\trequire.NoError(t, json.NewDecoder(r.Body).Decode(&message))\n\t\t\t\t\tmessages <- &message\n\t\t\t\t}))\n\t\t\t\tt.Cleanup(srv.Close)\n\n\t\t\t\tctx = contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\tconfig.ViperKeyCourierDeliveryStrategy:           \"http\",\n\t\t\t\t\tconfig.ViperKeyCourierHTTPRequestConfig + \".url\": srv.URL,\n\t\t\t\t})\n\n\t\t\t\ts, _, err := reg.RecoveryStrategies(ctx).ActiveStrategies(\"link\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tf, err := recovery.NewFlow(conf, time.Hour, \"\", u, s, flow.TypeBrowser)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\trequire.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(ctx, f))\n\n\t\t\t\trequire.NoError(t, reg.LinkSender().SendRecoveryLink(ctx, f, \"email\", \"tracked@ory.sh\"))\n\t\t\t\trequire.EqualError(t, reg.LinkSender().SendRecoveryLink(ctx, f, \"email\", \"not-tracked@ory.sh\"), link.ErrUnknownAddress.Error())\n\n\t\t\t\tcour, err := reg.Courier(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\trequire.NoError(t, cour.DispatchQueue(ctx))\n\t\t\t\tclose(messages)\n\n\t\t\t\trequire.Len(t, messages, 2)\n\n\t\t\t\tmsg := <-messages\n\t\t\t\tassert.EqualValues(t, \"tracked@ory.sh\", msg.To)\n\t\t\t\tassert.Contains(t, msg.Subject, \"Recover access to your account\")\n\t\t\t\tassert.Contains(t, msg.RecoveryURL, tc.recoveryURL)\n\n\t\t\t\tmsg = <-messages\n\t\t\t\tassert.EqualValues(t, \"not-tracked@ory.sh\", msg.To)\n\t\t\t\tassert.Contains(t, msg.Subject, \"Account access attempted\")\n\t\t\t\tassert.NotContains(t, msg.RecoveryURL, tc.recoveryURL)\n\t\t\t})\n\n\t\t\tt.Run(\"method=SendVerificationLink\", func(t *testing.T) {\n\t\t\t\tstrategies, _, err := reg.GetActiveVerificationStrategies(ctx)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tf, err := verification.NewFlow(conf, time.Hour, \"\", u, strategies, flow.TypeBrowser)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(ctx, f))\n\n\t\t\t\trequire.NoError(t, reg.LinkSender().SendVerificationLink(ctx, f, \"email\", \"tracked@ory.sh\"))\n\t\t\t\trequire.EqualError(t, reg.LinkSender().SendVerificationLink(ctx, f, \"email\", \"not-tracked@ory.sh\"), link.ErrUnknownAddress.Error())\n\t\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 12)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, messages, 2)\n\n\t\t\t\tassert.EqualValues(t, \"tracked@ory.sh\", messages[0].Recipient)\n\t\t\t\tassert.Contains(t, messages[0].Subject, \"Please verify\")\n\t\t\t\tassert.Contains(t, messages[0].Body, tc.verificationURL)\n\t\t\t\tassert.Contains(t, messages[0].Body, \"token=\")\n\t\t\t\tassert.Contains(t, messages[0].Body, \"flow=\")\n\n\t\t\t\tassert.EqualValues(t, \"not-tracked@ory.sh\", messages[1].Recipient)\n\t\t\t\tassert.Contains(t, messages[1].Subject, \"tried to verify\")\n\t\t\t\tassert.NotContains(t, messages[1].Body, tc.verificationURL)\n\t\t\t\taddress, err := reg.IdentityPool().FindVerifiableAddressByValue(ctx, identity.AddressTypeEmail, \"tracked@ory.sh\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, identity.VerifiableAddressStatusSent, address.Status)\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"case=should be able to disable invalid email dispatch\", func(t *testing.T) {\n\t\tfor _, tc := range []struct {\n\t\t\tflow      string\n\t\t\tsend      func(t *testing.T)\n\t\t\tconfigKey string\n\t\t}{\n\t\t\t{\n\t\t\t\tflow:      \"recovery\",\n\t\t\t\tconfigKey: config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients,\n\t\t\t\tsend: func(t *testing.T) {\n\t\t\t\t\ts, _, err := reg.RecoveryStrategies(ctx).ActiveStrategies(\"link\")\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tf, err := recovery.NewFlow(conf, time.Hour, \"\", u, s, flow.TypeBrowser)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\trequire.NoError(t, reg.RecoveryFlowPersister().CreateRecoveryFlow(ctx, f))\n\n\t\t\t\t\terr = reg.LinkSender().SendRecoveryLink(ctx, f, \"email\", \"not-tracked@ory.sh\")\n\t\t\t\t\trequire.ErrorIs(t, err, link.ErrUnknownAddress)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tflow:      \"verification\",\n\t\t\t\tconfigKey: config.ViperKeySelfServiceVerificationNotifyUnknownRecipients,\n\t\t\t\tsend: func(t *testing.T) {\n\t\t\t\t\ts, _, err := reg.VerificationStrategies(ctx).ActiveStrategies(\"link\")\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tf, err := verification.NewFlow(conf, time.Hour, \"\", u, s, flow.TypeBrowser)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(ctx, f))\n\n\t\t\t\t\terr = reg.LinkSender().SendVerificationLink(ctx, f, \"email\", \"not-tracked@ory.sh\")\n\t\t\t\t\trequire.ErrorIs(t, err, link.ErrUnknownAddress)\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"strategy=\"+tc.flow, func(t *testing.T) {\n\t\t\t\tctx = contextx.WithConfigValue(ctx, tc.configKey, false)\n\n\t\t\t\ttc.send(t)\n\n\t\t\t\tmessages, err := reg.CourierPersister().NextMessages(ctx, 0)\n\n\t\t\t\trequire.ErrorIs(t, err, courier.ErrQueueEmpty)\n\t\t\t\trequire.Len(t, messages, 0)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/link/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link\n\nimport (\n\t\"github.com/ory/kratos/courier\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar (\n\t_ recovery.Strategy     = (*Strategy)(nil)\n\t_ verification.Strategy = (*Strategy)(nil)\n\t_ x.Handler             = (*Strategy)(nil)\n)\n\ntype (\n\t// FlowMethod contains the configuration for this selfservice strategy.\n\tFlowMethod struct {\n\t\t*container.Container\n\t}\n\n\tdependencies interface {\n\t\tnosurfx.CSRFProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\t\tx.TransactionPersistenceProvider\n\n\t\tconfig.Provider\n\n\t\tsession.HandlerProvider\n\t\tsession.ManagementProvider\n\t\tsettings.HandlerProvider\n\t\tsettings.FlowPersistenceProvider\n\n\t\tidentity.ValidationProvider\n\t\tidentity.ManagementProvider\n\t\tidentity.PoolProvider\n\t\tidentity.PrivilegedPoolProvider\n\n\t\tcourier.Provider\n\n\t\terrorx.ManagementProvider\n\n\t\trecovery.ErrorHandlerProvider\n\t\trecovery.FlowPersistenceProvider\n\t\trecovery.StrategyProvider\n\t\trecovery.HookExecutorProvider\n\n\t\tverification.ErrorHandlerProvider\n\t\tverification.FlowPersistenceProvider\n\t\tverification.StrategyProvider\n\t\tverification.HookExecutorProvider\n\t\tverification.HandlerProvider\n\n\t\tRecoveryTokenPersistenceProvider\n\t\tVerificationTokenPersistenceProvider\n\t\tSenderProvider\n\n\t\tschema.IdentitySchemaProvider\n\t}\n\n\tStrategy struct{ d dependencies }\n)\n\nfunc NewStrategy(d dependencies) *Strategy      { return &Strategy{d: d} }\nfunc (s *Strategy) NodeGroup() node.UiNodeGroup { return node.LinkGroup }\n"
  },
  {
    "path": "selfservice/strategy/link/strategy_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/pop/v6\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst RouteAdminCreateRecoveryLink = \"/recovery/link\"\n\nfunc (s *Strategy) RecoveryStrategyID() string {\n\treturn string(recovery.RecoveryStrategyLink)\n}\n\nfunc (s *Strategy) IsPrimary() bool {\n\treturn true\n}\n\nfunc (s *Strategy) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\ts.d.CSRFHandler().IgnorePath(RouteAdminCreateRecoveryLink)\n\tpublic.POST(RouteAdminCreateRecoveryLink, redir.RedirectToAdminRoute(s.d))\n}\n\nfunc (s *Strategy) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\twrappedCreateRecoveryLink := strategy.IsDisabled(s.d, s.RecoveryStrategyID(), s.createRecoveryLinkForIdentity)\n\tadmin.POST(RouteAdminCreateRecoveryLink, wrappedCreateRecoveryLink)\n}\n\nfunc (s *Strategy) PopulateRecoveryMethod(r *http.Request, f *recovery.Flow) error {\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.GetNodes().Upsert(\n\t\t// v0.5: form.Field{Name: \"email\", Type: \"email\", Required: true},\n\t\tnode.NewInputField(\"email\", nil, node.LinkGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).WithMetaLabel(text.NewInfoNodeInputEmail()),\n\t)\n\tf.UI.GetNodes().Append(node.NewInputField(\"method\", s.RecoveryStrategyID(), node.LinkGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelContinue()))\n\n\treturn nil\n}\n\n// Create Recovery Link for Identity Parameters\n//\n// swagger:parameters createRecoveryLinkForIdentity\ntype _ struct {\n\t// in: body\n\tBody createRecoveryLinkForIdentityBody\n\t// in: query\n\tReturnTo string `json:\"return_to\"`\n}\n\n// Create Recovery Link for Identity Request Body\n//\n// swagger:model createRecoveryLinkForIdentityBody\ntype createRecoveryLinkForIdentityBody struct {\n\t// Identity to Recover\n\t//\n\t// The identity's ID you wish to recover.\n\t//\n\t// required: true\n\tIdentityID uuid.UUID `json:\"identity_id\"`\n\n\t// Link Expires In\n\t//\n\t// The recovery link will expire after that amount of time has passed. Defaults to the configuration value of\n\t// `selfservice.methods.code.config.lifespan`.\n\t//\n\t//\n\t// pattern: ^[0-9]+(ns|us|ms|s|m|h)$\n\t// example:\n\t//\t- 1h\n\t//\t- 1m\n\t//\t- 1s\n\tExpiresIn string `json:\"expires_in\"`\n}\n\n// Identity Recovery Link\n//\n// Used when an administrator creates a recovery link for an identity.\n//\n// swagger:model recoveryLinkForIdentity\ntype recoveryLinkForIdentity struct {\n\t// Recovery Link\n\t//\n\t// This link can be used to recover the account.\n\t//\n\t// required: true\n\t// format: uri\n\tRecoveryLink string `json:\"recovery_link\"`\n\n\t// Recovery Link Expires At\n\t//\n\t// The timestamp when the recovery link expires.\n\tExpiresAt time.Time `json:\"expires_at\"`\n}\n\n// swagger:route POST /admin/recovery/link identity createRecoveryLinkForIdentity\n//\n// # Create a Recovery Link\n//\n// This endpoint creates a recovery link which should be given to the user in order for them to recover\n// (or activate) their account.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: recoveryLinkForIdentity\n//\t  400: errorGeneric\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (s *Strategy) createRecoveryLinkForIdentity(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\n\tvar p createRecoveryLinkForIdentityBody\n\tif err := decoderx.Decode(r, &p, decoderx.HTTPJSONDecoder()); err != nil {\n\t\ts.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\texpiresIn := s.d.Config().SelfServiceLinkMethodLifespan(ctx)\n\tif len(p.ExpiresIn) > 0 {\n\t\tvar err error\n\t\texpiresIn, err = time.ParseDuration(p.ExpiresIn)\n\t\tif err != nil {\n\t\t\ts.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to parse \"expires_in\" whose format should match \"[0-9]+(ns|us|ms|s|m|h)\" but did not: %s`, p.ExpiresIn)))\n\t\t\treturn\n\t\t}\n\t}\n\n\tif expiresIn <= 0 {\n\t\ts.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Value from \"expires_in\" must be result to a future time: %s`, p.ExpiresIn)))\n\t\treturn\n\t}\n\n\treq, err := recovery.NewFlow(s.d.Config(), expiresIn, s.d.GenerateCSRFToken(r), r, recovery.Strategies{s}, flow.TypeBrowser)\n\tif err != nil {\n\t\ts.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tid, err := s.d.IdentityPool().GetIdentity(ctx, p.IdentityID, identity.ExpandDefault)\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\ts.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"The requested identity id does not exist.\").WithWrap(err)))\n\t\treturn\n\t} else if err != nil {\n\t\ts.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\ttoken := NewAdminRecoveryToken(id.ID, req.ID, expiresIn)\n\tif err := s.d.TransactionalPersisterProvider().Transaction(ctx, func(ctx context.Context, c *pop.Connection) error {\n\t\tif err := s.d.RecoveryFlowPersister().CreateRecoveryFlow(ctx, req); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn s.d.RecoveryTokenPersister().CreateRecoveryToken(ctx, token)\n\t}); err != nil {\n\t\ts.d.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\ttrace.SpanFromContext(ctx).AddEvent(\n\t\tevents.NewRecoveryInitiatedByAdmin(ctx, req.ID, id.ID, req.Type.String(), \"link\"),\n\t)\n\n\ts.d.Logger().\n\t\tWithField(\"identity_id\", id.ID).\n\t\tWithSensitiveField(\"recovery_link_token\", token).\n\t\tInfo(\"A recovery link has been created.\")\n\n\ts.d.Writer().Write(w, r, &recoveryLinkForIdentity{\n\t\tExpiresAt: req.ExpiresAt.UTC(),\n\t\tRecoveryLink: urlx.CopyWithQuery(\n\t\t\turlx.AppendPaths(s.d.Config().SelfPublicURL(ctx), recovery.RouteSubmitFlow),\n\t\t\turl.Values{\n\t\t\t\t\"token\": {token.Token},\n\t\t\t\t\"flow\":  {req.ID.String()},\n\t\t\t}).String(),\n\t},\n\t\therodot.UnescapedHTML)\n}\n\n// Update Recovery Flow with Link Method\n//\n// swagger:model updateRecoveryFlowWithLinkMethod\ntype _ struct {\n\t// Email to Recover\n\t//\n\t// Needs to be set when initiating the flow. If the email is a registered\n\t// recovery email, a recovery link will be sent. If the email is not known,\n\t// an email with details on what happened will be sent instead.\n\t//\n\t// format: email\n\t// required: true\n\tEmail string `json:\"email\" form:\"email\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `form:\"csrf_token\" json:\"csrf_token\"`\n\n\t// Method is the method that should be used for this recovery flow\n\t//\n\t// Allowed values are `link` and `code`\n\t//\n\t// required: true\n\tMethod recovery.RecoveryMethod `json:\"method\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) Recover(w http.ResponseWriter, r *http.Request, f *recovery.Flow) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.link.Strategy.Recover\")\n\tspan.SetAttributes(attribute.String(\"selfservice_flows_recovery_use\", s.d.Config().SelfServiceFlowRecoveryUse(ctx)))\n\tdefer otelx.End(span, &err)\n\n\tbody, err := s.decodeRecovery(r)\n\tif err != nil {\n\t\treturn s.HandleRecoveryError(r, nil, body, err)\n\t}\n\n\tf.TransientPayload = body.TransientPayload\n\n\tif len(body.Token) > 0 {\n\t\tif err := flow.MethodEnabledAndAllowed(r.Context(), f.GetFlowName(), s.RecoveryStrategyID(), s.RecoveryStrategyID(), s.d); err != nil {\n\t\t\treturn s.HandleRecoveryError(r, nil, body, err)\n\t\t}\n\n\t\treturn s.recoveryUseToken(ctx, w, r, f.ID, body)\n\t}\n\n\tif _, err := s.d.SessionManager().FetchFromRequest(r.Context(), r); err == nil {\n\t\tif x.IsJSONRequest(r) {\n\t\t\tsession.RespondWithJSONErrorOnAuthenticated(s.d.Writer(), recovery.ErrAlreadyLoggedIn)(w, r)\n\t\t} else {\n\t\t\tsession.RedirectOnAuthenticated(s.d)(w, r)\n\t\t}\n\t\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n\t}\n\n\tif err := flow.MethodEnabledAndAllowed(r.Context(), f.GetFlowName(), s.RecoveryStrategyID(), body.Method, s.d); err != nil {\n\t\treturn s.HandleRecoveryError(r, nil, body, err)\n\t}\n\n\treq, err := s.d.RecoveryFlowPersister().GetRecoveryFlow(r.Context(), x.ParseUUID(body.Flow))\n\tif err != nil {\n\t\treturn s.HandleRecoveryError(r, req, body, err)\n\t}\n\n\tif err := req.Valid(); err != nil {\n\t\treturn s.HandleRecoveryError(r, req, body, err)\n\t}\n\n\tswitch req.State {\n\tcase flow.StateChooseMethod,\n\t\tflow.StateEmailSent:\n\t\treturn s.recoveryHandleFormSubmission(r, req)\n\tcase flow.StatePassedChallenge:\n\t\t// was already handled, do not allow retry\n\t\treturn s.retryRecoveryFlowWithMessage(w, r, req.Type, text.NewErrorValidationRecoveryRetrySuccess())\n\tdefault:\n\t\treturn s.retryRecoveryFlowWithMessage(w, r, req.Type, text.NewErrorValidationRecoveryStateFailure())\n\t}\n}\n\nfunc (s *Strategy) recoveryIssueSession(ctx context.Context, w http.ResponseWriter, r *http.Request, f *recovery.Flow, id *identity.Identity) error {\n\tf.UI.Messages.Clear()\n\tf.State = flow.StatePassedChallenge\n\tf.SetCSRFToken(s.d.CSRFHandler().RegenerateToken(w, r))\n\tf.RecoveredIdentityID = uuid.NullUUID{\n\t\tUUID:  id.ID,\n\t\tValid: true,\n\t}\n\tif err := s.d.RecoveryFlowPersister().UpdateRecoveryFlow(r.Context(), f); err != nil {\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\tsess := session.NewInactiveSession()\n\tsess.CompletedLoginFor(identity.CredentialsTypeRecoveryLink, identity.AuthenticatorAssuranceLevel1)\n\tif err := s.d.SessionManager().ActivateSession(r, sess, id, time.Now().UTC()); err != nil {\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\t// Force load.\n\tif err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, sess.Identity, identity.ExpandEverything); err != nil {\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\tif err := s.d.RecoveryExecutor().PostRecoveryHook(w, r, f, sess); err != nil {\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\tif err := s.d.SessionManager().UpsertAndIssueCookie(r.Context(), w, r, sess); err != nil {\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\tsf, err := s.d.SettingsHandler().NewFlow(ctx, w, r, sess.Identity, sess, flow.TypeBrowser)\n\tif err != nil {\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\treturnToURL := s.d.Config().SelfServiceFlowRecoveryReturnTo(r.Context(), nil)\n\treturnTo := \"\"\n\tif returnToURL != nil {\n\t\treturnTo = returnToURL.String()\n\t}\n\n\tsf.RequestURL, err = redir.TakeOverReturnToParameter(f.RequestURL, sf.RequestURL, returnTo)\n\tif err != nil {\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\tsf.UI.Messages.Set(text.NewRecoverySuccessful(time.Now().Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(r.Context()))))\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(r.Context(), sf); err != nil {\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\thttp.Redirect(w, r, sf.AppendTo(s.d.Config().SelfServiceFlowSettingsUI(r.Context())).String(), http.StatusSeeOther)\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) recoveryUseToken(ctx context.Context, w http.ResponseWriter, r *http.Request, fID uuid.UUID, body *recoverySubmitPayload) error {\n\ttoken, err := s.d.RecoveryTokenPersister().UseRecoveryToken(r.Context(), fID, body.Token)\n\tif err != nil {\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\treturn s.retryRecoveryFlowWithMessage(w, r, flow.TypeBrowser, text.NewErrorValidationRecoveryTokenInvalidOrAlreadyUsed())\n\t\t}\n\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\tvar f *recovery.Flow\n\tif !token.FlowID.Valid {\n\t\tf, err = recovery.NewFlow(s.d.Config(), time.Until(token.ExpiresAt), s.d.GenerateCSRFToken(r), r, recovery.Strategies{s}, flow.TypeBrowser)\n\t\tif err != nil {\n\t\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t\t}\n\n\t\tif err := s.d.RecoveryFlowPersister().CreateRecoveryFlow(r.Context(), f); err != nil {\n\t\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t\t}\n\t} else {\n\t\tf, err = s.d.RecoveryFlowPersister().GetRecoveryFlow(r.Context(), token.FlowID.UUID)\n\t\tif err != nil {\n\t\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t\t}\n\t}\n\n\tif err := token.Valid(); err != nil {\n\t\treturn s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err)\n\t}\n\n\t// Important to expand everything here, as we need the data for recovery.\n\trecovered, err := s.d.IdentityPool().GetIdentity(r.Context(), token.IdentityID, identity.ExpandEverything)\n\tif err != nil {\n\t\treturn s.HandleRecoveryError(r, f, nil, err)\n\t}\n\n\t// mark address as verified only for a self-service flow\n\tif token.TokenType == RecoveryTokenTypeSelfService {\n\t\tif err := s.markRecoveryAddressVerified(r, f, recovered, token.RecoveryAddress); err != nil {\n\t\t\treturn s.HandleRecoveryError(r, f, body, err)\n\t\t}\n\t}\n\n\treturn s.recoveryIssueSession(ctx, w, r, f, recovered)\n}\n\nfunc (s *Strategy) retryRecoveryFlowWithMessage(w http.ResponseWriter, r *http.Request, ft flow.Type, message *text.Message) error {\n\ts.d.Logger().WithRequest(r).WithField(\"message\", message).Debug(\"A recovery flow is being retried because a validation error occurred.\")\n\n\treq, err := recovery.NewFlow(s.d.Config(), s.d.Config().SelfServiceFlowRecoveryRequestLifespan(r.Context()), s.d.CSRFHandler().RegenerateToken(w, r), r, recovery.Strategies{s}, ft)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treq.UI.Messages.Add(message)\n\tif err := s.d.RecoveryFlowPersister().CreateRecoveryFlow(r.Context(), req); err != nil {\n\t\treturn err\n\t}\n\n\tif ft == flow.TypeBrowser {\n\t\thttp.Redirect(w, r, req.AppendTo(s.d.Config().SelfServiceFlowRecoveryUI(r.Context())).String(), http.StatusSeeOther)\n\t} else {\n\t\thttp.Redirect(w, r, urlx.CopyWithQuery(urlx.AppendPaths(s.d.Config().SelfPublicURL(r.Context()),\n\t\t\trecovery.RouteGetFlow), url.Values{\"id\": {req.ID.String()}}).String(), http.StatusSeeOther)\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) retryRecoveryFlowWithError(w http.ResponseWriter, r *http.Request, ft flow.Type, recErr error) error {\n\ts.d.Logger().WithRequest(r).WithError(recErr).Debug(\"A recovery flow is being retried because a validation error occurred.\")\n\n\treq, err := recovery.NewFlow(s.d.Config(), s.d.Config().SelfServiceFlowRecoveryRequestLifespan(r.Context()), s.d.CSRFHandler().RegenerateToken(w, r), r, recovery.Strategies{s}, ft)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif expired := new(flow.ExpiredError); errors.As(recErr, &expired) {\n\t\treturn s.retryRecoveryFlowWithMessage(w, r, ft, text.NewErrorValidationRecoveryFlowExpired(expired.ExpiredAt))\n\t} else {\n\t\tif err := req.UI.ParseError(node.LinkGroup, recErr); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif err := s.d.RecoveryFlowPersister().CreateRecoveryFlow(r.Context(), req); err != nil {\n\t\treturn err\n\t}\n\n\tif ft == flow.TypeBrowser {\n\t\thttp.Redirect(w, r, req.AppendTo(s.d.Config().SelfServiceFlowRecoveryUI(r.Context())).String(), http.StatusSeeOther)\n\t} else {\n\t\thttp.Redirect(w, r, urlx.CopyWithQuery(urlx.AppendPaths(s.d.Config().SelfPublicURL(r.Context()),\n\t\t\trecovery.RouteGetFlow), url.Values{\"id\": {req.ID.String()}}).String(), http.StatusSeeOther)\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) recoveryHandleFormSubmission(r *http.Request, f *recovery.Flow) error {\n\tbody, err := s.decodeRecovery(r)\n\tif err != nil {\n\t\treturn s.HandleRecoveryError(r, f, body, err)\n\t}\n\n\tif len(body.Email) == 0 {\n\t\treturn s.HandleRecoveryError(r, f, body, schema.NewRequiredError(\"#/email\", \"email\"))\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(r.Context()), s.d.GenerateCSRFToken, body.CSRFToken); err != nil {\n\t\treturn s.HandleRecoveryError(r, f, body, err)\n\t}\n\n\tif err := s.d.LinkSender().SendRecoveryLink(r.Context(), f, identity.AddressTypeEmail, body.Email); err != nil {\n\t\tif !errors.Is(err, ErrUnknownAddress) {\n\t\t\treturn s.HandleRecoveryError(r, f, body, err)\n\t\t}\n\t\t// Continue execution\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.GetNodes().Upsert(\n\t\t// v0.5: form.Field{Name: \"email\", Type: \"email\", Required: true, Value: body.Body.Email}\n\t\tnode.NewInputField(\"email\", body.Email, node.LinkGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).WithMetaLabel(text.NewInfoNodeInputEmail()),\n\t)\n\n\tf.Active = sqlxx.NullString(s.NodeGroup())\n\tf.State = flow.StateEmailSent\n\tf.UI.Messages.Set(text.NewRecoveryEmailSent())\n\tif err := s.d.RecoveryFlowPersister().UpdateRecoveryFlow(r.Context(), f); err != nil {\n\t\treturn s.HandleRecoveryError(r, f, body, err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) markRecoveryAddressVerified(r *http.Request, f *recovery.Flow, id *identity.Identity, recoveryAddress *identity.RecoveryAddress) error {\n\tfor k, v := range id.VerifiableAddresses {\n\t\tif v.Value == recoveryAddress.Value {\n\t\t\tid.VerifiableAddresses[k].Verified = true\n\t\t\tid.VerifiableAddresses[k].VerifiedAt = new(sqlxx.NullTime(time.Now().UTC()))\n\t\t\tid.VerifiableAddresses[k].Status = identity.VerifiableAddressStatusCompleted\n\t\t\tif err := s.d.PrivilegedIdentityPool().UpdateVerifiableAddress(r.Context(), &id.VerifiableAddresses[k], \"verified\", \"verified_at\", \"status\"); err != nil {\n\t\t\t\treturn s.HandleRecoveryError(r, f, nil, err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) HandleRecoveryError(r *http.Request, req *recovery.Flow, body *recoverySubmitPayload, err error) error {\n\tif req != nil {\n\t\temail := \"\"\n\t\tif body != nil {\n\t\t\temail = body.Email\n\t\t}\n\n\t\treq.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\treq.UI.GetNodes().Upsert(\n\t\t\t// v0.5: form.Field{Name: \"email\", Type: \"email\", Required: true, Value: body.Body.Email}\n\t\t\tnode.NewInputField(\"email\", email, node.LinkGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).WithMetaLabel(text.NewInfoNodeInputEmail()),\n\t\t)\n\t}\n\n\treturn err\n}\n\ntype recoverySubmitPayload struct {\n\tMethod           string          `json:\"method\" form:\"method\"`\n\tToken            string          `json:\"token\" form:\"token\"`\n\tCSRFToken        string          `json:\"csrf_token\" form:\"csrf_token\"`\n\tFlow             string          `json:\"flow\" form:\"flow\"`\n\tEmail            string          `json:\"email\" form:\"email\"`\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) decodeRecovery(r *http.Request) (*recoverySubmitPayload, error) {\n\tvar body recoverySubmitPayload\n\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(recoveryMethodSchema)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif err := decoderx.Decode(r, &body, compiler,\n\t\tdecoderx.HTTPDecoderUseQueryAndBody(),\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn &body, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/link/strategy_recovery_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link_test\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/davecgh/go-spew/spew\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/corpx\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc createIdentityToRecover(t *testing.T, reg *driver.RegistryDefault, email string) *identity.Identity {\n\tid := &identity.Identity{\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\"password\": {Type: \"password\", Identifiers: []string{email}, Config: sqlxx.JSONRawMessage(`{\"hashed_password\":\"foo\"}`)},\n\t\t},\n\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, email)),\n\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t}\n\trequire.NoError(t, reg.IdentityManager().Create(t.Context(), id, identity.ManagerAllowWriteProtectedTraits))\n\n\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(t.Context(), identity.AddressTypeEmail, email)\n\tassert.NoError(t, err)\n\tassert.False(t, addr.Verified)\n\tassert.Nil(t, addr.VerifiedAt)\n\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\treturn id\n}\n\nfunc TestAdminStrategy(t *testing.T) {\n\tctx := t.Context()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tinitViper(t, conf)\n\n\t_ = testhelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublicTS, adminTS := testhelpers.NewKratosServer(t, reg)\n\tadminSDK := testhelpers.NewSDKClient(adminTS)\n\n\tcheckLink := func(t *testing.T, l *kratos.RecoveryLinkForIdentity, isBefore time.Time) {\n\t\trequire.Contains(t, l.RecoveryLink, publicTS.URL+recovery.RouteSubmitFlow)\n\t\trl := urlx.ParseOrPanic(l.RecoveryLink)\n\t\tassert.NotEmpty(t, rl.Query().Get(\"token\"))\n\t\tassert.NotEmpty(t, rl.Query().Get(\"flow\"))\n\t\trequire.True(t, (*l.ExpiresAt).Before(isBefore))\n\t}\n\n\tt.Run(\"no panic on empty body #1384\", func(t *testing.T) {\n\t\ts, ps, err := reg.RecoveryStrategies(t.Context()).ActiveStrategies(\"link\")\n\t\trequire.NoError(t, err)\n\n\t\tr := &http.Request{URL: new(url.URL)}\n\t\tf, err := recovery.NewFlow(reg.Config(), time.Minute, \"\", r, s, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\n\t\trequire.IsType(t, &link.Strategy{}, ps)\n\t\texpectedErr := errors.New(\"test\")\n\t\terr = ps.(*link.Strategy).HandleRecoveryError(r, f, nil, expectedErr)\n\t\trequire.ErrorIs(t, err, expectedErr)\n\t})\n\n\tt.Run(\"description=should not be able to recover an account that does not exist\", func(t *testing.T) {\n\t\t_, _, err := adminSDK.IdentityAPI.CreateRecoveryLinkForIdentity(t.Context()).CreateRecoveryLinkForIdentityBody(kratos.CreateRecoveryLinkForIdentityBody{\n\t\t\tIdentityId: x.NewUUID().String(),\n\t\t}).Execute()\n\t\trequire.IsTypef(t, err, new(kratos.GenericOpenAPIError), \"%T\", err)\n\t\tassert.EqualError(t, err, \"400 Bad Request\")\n\t})\n\n\tt.Run(\"description=should create a valid recovery link without email\", func(t *testing.T) {\n\t\tid := identity.Identity{Traits: identity.Traits(`{}`)}\n\n\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(),\n\t\t\t&id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\trl, _, err := adminSDK.IdentityAPI.CreateRecoveryLinkForIdentity(t.Context()).CreateRecoveryLinkForIdentityBody(kratos.CreateRecoveryLinkForIdentityBody{\n\t\t\tIdentityId: id.ID.String(),\n\t\t\tExpiresIn:  new(\"100ms\"),\n\t\t}).Execute()\n\t\trequire.NoError(t, err)\n\n\t\ttime.Sleep(time.Millisecond * 100)\n\t\tcheckLink(t, rl, time.Now().Add(conf.SelfServiceFlowRecoveryRequestLifespan(ctx)))\n\t\tres, err := publicTS.Client().Get(rl.RecoveryLink)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t// We end up here because the link is expired.\n\t\tassert.Contains(t, res.Request.URL.Path, \"/recover\", rl.RecoveryLink)\n\t})\n\n\tt.Run(\"description=should create a valid recovery link and set the expiry time and not be able to recover the account\", func(t *testing.T) {\n\t\trecoveryEmail := \"recover.expired@ory.sh\"\n\t\tid := identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, recoveryEmail))}\n\n\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(),\n\t\t\t&id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\trl, _, err := adminSDK.IdentityAPI.CreateRecoveryLinkForIdentity(t.Context()).CreateRecoveryLinkForIdentityBody(kratos.CreateRecoveryLinkForIdentityBody{\n\t\t\tIdentityId: id.ID.String(),\n\t\t\tExpiresIn:  new(\"100ms\"),\n\t\t}).Execute()\n\t\trequire.NoError(t, err)\n\n\t\ttime.Sleep(time.Millisecond * 100)\n\t\tcheckLink(t, rl, time.Now().Add(conf.SelfServiceFlowRecoveryRequestLifespan(ctx)))\n\t\tres, err := publicTS.Client().Get(rl.RecoveryLink)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t// We end up here because the link is expired.\n\t\tassert.Contains(t, res.Request.URL.Path, \"/recover\", rl.RecoveryLink)\n\n\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(t.Context(), identity.AddressTypeEmail, recoveryEmail)\n\t\tassert.NoError(t, err)\n\t\tassert.False(t, addr.Verified)\n\t\tassert.Nil(t, addr.VerifiedAt)\n\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\t})\n\n\tt.Run(\"description=should create a valid recovery link and set the expiry time as well and recover the account\", func(t *testing.T) {\n\t\trecoveryEmail := \"recoverme@ory.sh\"\n\t\tid := identity.Identity{Traits: identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, recoveryEmail))}\n\n\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(),\n\t\t\t&id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\trl, _, err := adminSDK.IdentityAPI.CreateRecoveryLinkForIdentity(t.Context()).CreateRecoveryLinkForIdentityBody(kratos.CreateRecoveryLinkForIdentityBody{\n\t\t\tIdentityId: id.ID.String(),\n\t\t}).Execute()\n\t\trequire.NoError(t, err)\n\n\t\tcheckLink(t, rl, time.Now().Add(conf.SelfServiceFlowRecoveryRequestLifespan(ctx)+time.Second))\n\t\tres, err := publicTS.Client().Get(rl.RecoveryLink)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowSettingsUI(ctx).String())\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\ttesthelpers.LogJSON(t, rl)\n\n\t\tf, err := reg.SettingsFlowPersister().GetSettingsFlow(t.Context(), uuid.FromStringOrNil(res.Request.URL.Query().Get(\"flow\")))\n\t\trequire.NoError(t, err, \"%s\", res.Request.URL.String())\n\n\t\trequire.Len(t, f.UI.Messages, 1)\n\t\tassert.Equal(t, \"You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 60.00 minutes.\", f.UI.Messages[0].Text)\n\n\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(t.Context(), identity.AddressTypeEmail, recoveryEmail)\n\t\tassert.NoError(t, err)\n\t\tassert.False(t, addr.Verified)\n\t\tassert.Nil(t, addr.VerifiedAt)\n\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\t})\n\n\tt.Run(\"case=should not be able to use code from different flow\", func(t *testing.T) {\n\t\temail := strings.ToLower(testhelpers.RandomEmail())\n\t\tid := createIdentityToRecover(t, reg, email)\n\n\t\trl1, _, err := adminSDK.IdentityAPI.\n\t\t\tCreateRecoveryLinkForIdentity(t.Context()).\n\t\t\tCreateRecoveryLinkForIdentityBody(kratos.CreateRecoveryLinkForIdentityBody{\n\t\t\t\tIdentityId: id.ID.String(),\n\t\t\t}).\n\t\t\tExecute()\n\t\trequire.NoError(t, err)\n\n\t\tcheckLink(t, rl1, time.Now().Add(conf.SelfServiceFlowRecoveryRequestLifespan(ctx)+time.Second))\n\n\t\trl2, _, err := adminSDK.IdentityAPI.\n\t\t\tCreateRecoveryLinkForIdentity(t.Context()).\n\t\t\tCreateRecoveryLinkForIdentityBody(kratos.CreateRecoveryLinkForIdentityBody{\n\t\t\t\tIdentityId: id.ID.String(),\n\t\t\t}).\n\t\t\tExecute()\n\t\trequire.NoError(t, err)\n\n\t\tcheckLink(t, rl2, time.Now().Add(conf.SelfServiceFlowRecoveryRequestLifespan(ctx)+time.Second))\n\n\t\trecoveryUrl1, err := url.Parse(rl1.RecoveryLink)\n\t\trequire.NoError(t, err)\n\n\t\trecoveryUrl2, err := url.Parse(rl2.RecoveryLink)\n\t\trequire.NoError(t, err)\n\n\t\ttoken1 := recoveryUrl1.Query().Get(\"token\")\n\t\trequire.NotEmpty(t, token1)\n\t\ttoken2 := recoveryUrl2.Query().Get(\"token\")\n\t\trequire.NotEmpty(t, token2)\n\t\trequire.NotEqual(t, token1, token2)\n\n\t\tvalues := recoveryUrl1.Query()\n\n\t\tvalues.Set(\"token\", token2)\n\n\t\trecoveryUrl1.RawQuery = values.Encode()\n\n\t\taction := recoveryUrl1.String()\n\t\t// Submit the modified link with token from rl2 and flow from rl1\n\t\tres, err := publicTS.Client().Get(action)\n\t\trequire.NoError(t, err)\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\taction = gjson.GetBytes(body, \"ui.action\").String()\n\t\trequire.NotEmpty(t, action)\n\t\tassert.Equal(t, \"The recovery token is invalid or has already been used. Please retry the flow.\", gjson.GetBytes(body, \"ui.messages.0.text\").String())\n\t})\n}\n\nfunc TestRecovery(t *testing.T) {\n\tctx := t.Context()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".code.enabled\", false)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".link.enabled\", true)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/default.schema.json\")\n\tinitViper(t, conf)\n\n\t_ = testhelpers.NewRecoveryUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublic, _, publicRouter, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\texpect := func(t *testing.T, hc *http.Client, isAPI, isSPA bool, values func(url.Values), c int) string {\n\t\tif hc == nil {\n\t\t\thc = testhelpers.NewDebugClient(t)\n\t\t\tif !isAPI {\n\t\t\t\thc = testhelpers.NewClientWithCookies(t)\n\t\t\t\thc.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t}\n\t\t}\n\n\t\treturn testhelpers.SubmitRecoveryForm(t, isAPI, isSPA, hc, public, values, c,\n\t\t\ttesthelpers.ExpectURL(isAPI || isSPA, public.URL+recovery.RouteSubmitFlow, conf.SelfServiceFlowRecoveryUI(ctx).String()))\n\t}\n\n\texpectValidationError := func(t *testing.T, hc *http.Client, isAPI, isSPA bool, values func(url.Values)) string {\n\t\treturn expect(t, hc, isAPI, isSPA, values, testhelpers.ExpectStatusCode(isAPI || isSPA, http.StatusBadRequest, http.StatusOK))\n\t}\n\n\texpectSuccess := func(t *testing.T, hc *http.Client, isAPI, isSPA bool, values func(url.Values)) string {\n\t\treturn expect(t, hc, isAPI, isSPA, values, http.StatusOK)\n\t}\n\n\tt.Run(\"description=should set all the correct recovery payloads after submission\", func(t *testing.T) {\n\t\tbody := expectSuccess(t, nil, false, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", \"test@ory.sh\")\n\t\t})\n\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes\").String()), []string{\"0.attributes.value\"})\n\t})\n\n\tt.Run(\"description=should set all the correct recovery payloads\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetRecoveryFlow(t, c, public)\n\n\t\ttesthelpers.SnapshotTExcept(t, rs.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\tassert.EqualValues(t, public.URL+recovery.RouteSubmitFlow+\"?flow=\"+rs.Id, rs.Ui.Action)\n\t\tassert.Empty(t, rs.Ui.Messages)\n\t})\n\n\tt.Run(\"description=should require an email to be sent\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, node.LinkGroup, gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, \"Property email is missing.\",\n\t\t\t\tgjson.Get(actual, \"ui.nodes.#(attributes.name==email).messages.0.text\").String(),\n\t\t\t\t\"%s\", actual)\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Del(\"email\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, nil, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, nil, false, true, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, nil, true, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"description=should require a valid email to be sent\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string, value string) {\n\t\t\tassert.EqualValues(t, node.LinkGroup, gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, \"Enter a valid email address\",\n\t\t\t\tgjson.Get(actual, \"ui.nodes.#(attributes.name==email).messages.0.text\").String(),\n\t\t\t\t\"%s\", actual)\n\t\t}\n\t\tfor _, email := range []string{\"\\\\\", \"asdf\", \"...\", \"aiacobelli.sec@gmail.com,alejandro.iacobelli@mercadolibre.com\"} {\n\t\t\tvalues := func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, nil, false, false, values), email)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, nil, false, true, values), email)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, nil, true, false, values), email)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should try to submit the form while authenticated\", func(t *testing.T) {\n\t\trun := func(t *testing.T, flow string) {\n\t\t\tisAPI := flow == \"api\"\n\t\t\tisSPA := flow == \"spa\"\n\t\t\thc := testhelpers.NewDebugClient(t)\n\t\t\tif !isAPI {\n\t\t\t\thc = testhelpers.NewClientWithCookies(t)\n\t\t\t\thc.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t}\n\n\t\t\tvar f *kratos.RecoveryFlow\n\t\t\tif isAPI {\n\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaAPI(t, hc, public)\n\t\t\t} else {\n\t\t\t\tf = testhelpers.InitializeRecoveryFlowViaBrowser(t, hc, isSPA, public, nil)\n\t\t\t}\n\n\t\t\tv := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tv.Set(\"email\", \"some-email@example.org\")\n\t\t\tv.Set(\"method\", \"link\")\n\n\t\t\tauthClient := testhelpers.NewHTTPClientWithArbitrarySessionToken(ctx, t, reg)\n\t\t\tif isAPI {\n\t\t\t\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil).WithContext(contextx.WithConfigValue(ctx, config.ViperKeySessionLifespan, time.Hour))\n\t\t\t\ts, err := testhelpers.NewActiveSession(req, reg,\n\t\t\t\t\t&identity.Identity{ID: x.NewUUID(), State: identity.StateActive, NID: x.NewUUID()},\n\t\t\t\t\ttime.Now(),\n\t\t\t\t\tidentity.CredentialsTypePassword,\n\t\t\t\t\tidentity.AuthenticatorAssuranceLevel1,\n\t\t\t\t)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tauthClient = testhelpers.NewHTTPClientWithSessionCookieLocalhost(ctx, t, reg, s)\n\t\t\t}\n\n\t\t\tbody, res := testhelpers.RecoveryMakeRequest(t, isAPI || isSPA, f, authClient, testhelpers.EncodeFormAsJSON(t, isAPI || isSPA, v))\n\n\t\t\tif isAPI || isSPA {\n\t\t\t\trequire.EqualValuesf(t, http.StatusBadRequest, res.StatusCode, \"%s\", body)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), recovery.RouteSubmitFlow, \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t\tassertx.EqualAsJSONExcept(t, recovery.ErrAlreadyLoggedIn, json.RawMessage(gjson.Get(body, \"error\").Raw), nil)\n\t\t\t} else {\n\t\t\t\tassert.EqualValuesf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%+v\\n\\t%s\", res.Request, body)\n\t\t\t}\n\t\t}\n\n\t\tfor _, f := range []string{\"browser\", \"spa\", \"api\"} {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\trun(t, f)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should try to recover an email that does not exist\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, true)\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryNotifyUnknownRecipients, false)\n\t\t})\n\t\tvar email string\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, node.LinkGroup, gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==email).attributes.value\").String(), \"%s\", actual)\n\t\t\tassertx.EqualAsJSON(t, text.NewRecoveryEmailSent(), json.RawMessage(gjson.Get(actual, \"ui.messages.0\").Raw))\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Account access attempted\")\n\t\t\tassert.Contains(t, message.Body, \"If this was you, check if you signed up using a different address.\")\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", email)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\temail = x.NewUUID().String() + \"@ory.sh\"\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\temail = x.NewUUID().String() + \"@ory.sh\"\n\t\t\tcheck(t, expectSuccess(t, nil, false, true, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\temail = x.NewUUID().String() + \"@ory.sh\"\n\t\t\tcheck(t, expectSuccess(t, nil, true, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"description=should not be able to recover an inactive account\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, recoverySubmissionResponse, recoveryEmail string, isAPI bool) {\n\t\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(t.Context(), identity.AddressTypeEmail, recoveryEmail)\n\t\t\tassert.NoError(t, err)\n\n\t\t\trecoveryLink := testhelpers.CourierExpectLinkInMessage(t, testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Recover access to your account\"), 1)\n\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\t// Deactivate the identity\n\t\t\trequire.NoError(t, reg.Persister().GetConnection(t.Context()).RawQuery(\"UPDATE identities SET state=? WHERE id = ?\", identity.StateInactive, addr.IdentityID).Exec())\n\n\t\t\tres, err := cl.Get(recoveryLink)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\tif isAPI {\n\t\t\t\tassert.Equal(t, http.StatusUnauthorized, res.StatusCode)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), public.URL+recovery.RouteSubmitFlow)\n\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", addr.IdentityID), json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String())\n\t\t\t\tassertx.EqualAsJSON(t, session.ErrIdentityDisabled.WithDetail(\"identity_id\", addr.IdentityID), json.RawMessage(body), \"%s\", body)\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\temail := \"recoverinactive1@ory.sh\"\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}), email, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\temail := \"recoverinactive2@ory.sh\"\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\tcheck(t, expectSuccess(t, nil, true, true, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}), email, true)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\temail := \"recoverinactive3@ory.sh\"\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\tcheck(t, expectSuccess(t, nil, true, false, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}), email, true)\n\t\t})\n\t})\n\n\tt.Run(\"description=should recover an account\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, recoverySubmissionResponse, recoveryEmail, returnTo string) {\n\t\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(t.Context(), identity.AddressTypeEmail, recoveryEmail)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.False(t, addr.Verified)\n\t\t\tassert.Nil(t, addr.VerifiedAt)\n\t\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\n\t\t\tassert.EqualValues(t, node.LinkGroup, gjson.Get(recoverySubmissionResponse, \"active\").String(), \"%s\", recoverySubmissionResponse)\n\t\t\tassert.EqualValues(t, recoveryEmail, gjson.Get(recoverySubmissionResponse, \"ui.nodes.#(attributes.name==email).attributes.value\").String(), \"%s\", recoverySubmissionResponse)\n\t\t\trequire.Len(t, gjson.Get(recoverySubmissionResponse, \"ui.messages\").Array(), 1, \"%s\", recoverySubmissionResponse)\n\t\t\tassertx.EqualAsJSON(t, text.NewRecoveryEmailSent(), json.RawMessage(gjson.Get(recoverySubmissionResponse, \"ui.messages.0\").Raw))\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Recover access to your account\")\n\t\t\tassert.Contains(t, message.Body, \"Recover access to your account by clicking the following link\")\n\n\t\t\trecoveryLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\t\tassert.Contains(t, recoveryLink, public.URL+recovery.RouteSubmitFlow)\n\t\t\tassert.Contains(t, recoveryLink, \"token=\")\n\n\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\n\t\t\tres, err := cl.Get(recoveryLink)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowSettingsUI(ctx).String())\n\n\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\tassert.Equal(t, text.NewRecoverySuccessful(time.Now().Add(time.Hour)).Text,\n\t\t\t\tgjson.GetBytes(body, \"ui.messages.0.text\").String())\n\t\t\tassert.Equal(t, returnTo, gjson.GetBytes(body, \"return_to\").String())\n\n\t\t\taddr, err = reg.IdentityPool().FindVerifiableAddressByValue(t.Context(), identity.AddressTypeEmail, recoveryEmail)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.True(t, addr.Verified)\n\t\t\tassert.NotEqual(t, sqlxx.NullTime{}, addr.VerifiedAt)\n\t\t\tassert.Equal(t, identity.VerifiableAddressStatusCompleted, addr.Status)\n\n\t\t\tres, err = cl.Get(public.URL + session.RouteWhoami)\n\t\t\trequire.NoError(t, err)\n\t\t\tbody = x.MustReadAll(res.Body)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Equal(t, \"link_recovery\", gjson.GetBytes(body, \"authentication_methods.0.method\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"aal1\", gjson.GetBytes(body, \"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tvar wg sync.WaitGroup\n\t\t\twg.Add(1)\n\t\t\ttesthelpers.NewRecoveryAfterHookWebHookTarget(ctx, t, conf, func(t *testing.T, msg []byte) {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tassert.EqualValues(t, \"recoverme1@ory.sh\", gjson.GetBytes(msg, \"identity.verifiable_addresses.0.value\").String(), string(msg))\n\t\t\t\tassert.EqualValues(t, true, gjson.GetBytes(msg, \"identity.verifiable_addresses.0.verified\").Bool(), string(msg))\n\t\t\t\tassert.EqualValues(t, \"completed\", gjson.GetBytes(msg, \"identity.verifiable_addresses.0.status\").String(), string(msg))\n\t\t\t})\n\n\t\t\temail := \"recoverme1@ory.sh\"\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}), email, \"\")\n\n\t\t\twg.Wait()\n\t\t})\n\n\t\tt.Run(\"description=should return browser to return url\", func(t *testing.T) {\n\t\t\treturnTo := public.URL + \"/return-to\"\n\t\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnTo})\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdesc     string\n\t\t\t\treturnTo string\n\t\t\t\tf        func(t *testing.T, client *http.Client) *kratos.RecoveryFlow\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from recovery flow\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, url.Values{\"return_to\": []string{returnTo}})\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"should use return_to from config\",\n\t\t\t\t\treturnTo: returnTo,\n\t\t\t\t\tf: func(t *testing.T, client *http.Client) *kratos.RecoveryFlow {\n\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, returnTo)\n\t\t\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryBrowserDefaultReturnTo, \"\")\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"no return to\",\n\t\t\t\t\treturnTo: \"\",\n\t\t\t\t\tf: func(t *testing.T, client *http.Client) *kratos.RecoveryFlow {\n\t\t\t\t\t\treturn testhelpers.InitializeRecoveryFlowViaBrowser(t, client, false, public, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tcreateIdentityToRecover(t, reg, email)\n\n\t\t\t\t\thc := testhelpers.NewClientWithCookies(t)\n\t\t\t\t\thc.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\n\t\t\t\t\tf := tc.f(t, hc)\n\n\t\t\t\t\ttime.Sleep(time.Millisecond) // add a bit of delay to allow `1ns` to time out.\n\n\t\t\t\t\tformPayload := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\t\tformPayload.Set(\"email\", email)\n\n\t\t\t\t\tb, res := testhelpers.RecoveryMakeRequest(t, false, f, hc, testhelpers.EncodeFormAsJSON(t, false, formPayload))\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", b)\n\t\t\t\t\texpectedURL := testhelpers.ExpectURL(false, public.URL+recovery.RouteSubmitFlow, conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), expectedURL, \"%+v\\n\\t%s\", res.Request, b)\n\n\t\t\t\t\tcheck(t, b, email, tc.returnTo)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\temail := \"recoverme3@ory.sh\"\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\tcheck(t, expectSuccess(t, nil, true, true, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}), email, \"\")\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\temail := \"recoverme4@ory.sh\"\n\t\t\tcreateIdentityToRecover(t, reg, email)\n\t\t\tcheck(t, expectSuccess(t, nil, true, false, func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}), email, \"\")\n\t\t})\n\t})\n\n\tt.Run(\"description=should recover an account and set the csrf cookies\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual, recoveryEmail string, cl *http.Client, do func(*http.Client, *http.Request) (*http.Response, error)) {\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Recover access to your account\")\n\t\t\trecoveryLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\t\t\tres, err := do(cl, testhelpers.NewTestHTTPRequest(t, \"GET\", recoveryLink, nil))\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, nosurfx.CSRFTokenName)\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\t\t\treturnTo, err := res.Location()\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Contains(t, returnTo.String(), conf.SelfServiceFlowSettingsUI(ctx).String(), \"we end up at the settings screen\")\n\n\t\t\trl := urlx.ParseOrPanic(recoveryLink)\n\t\t\tactualRes, err := cl.Get(public.URL + recovery.RouteGetFlow + \"?id=\" + rl.Query().Get(\"flow\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tbody := x.MustReadAll(actualRes.Body)\n\t\t\trequire.NoError(t, actualRes.Body.Close())\n\t\t\tassert.Equal(t, http.StatusOK, actualRes.StatusCode, \"%s\", body)\n\t\t\tassert.Equal(t, string(flow.StatePassedChallenge), gjson.GetBytes(body, \"state\").String(), \"%s\", body)\n\t\t}\n\n\t\temail := x.NewUUID().String() + \"@ory.sh\"\n\t\tid := createIdentityToRecover(t, reg, email)\n\n\t\tt.Run(\"case=unauthenticated\", func(t *testing.T) {\n\t\t\tvalues := func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, values), email, testhelpers.NewClientWithCookies(t), (*http.Client).Do)\n\t\t})\n\n\t\tt.Run(\"case=already logged into another account\", func(t *testing.T) {\n\t\t\tvalues := func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}\n\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, values), email, testhelpers.NewClientWithCookies(t), func(cl *http.Client, req *http.Request) (*http.Response, error) {\n\t\t\t\t_, res := testhelpers.MockMakeAuthenticatedRequestWithClient(t, reg, conf, publicRouter, req, cl)\n\t\t\t\treturn res, nil\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=already logged into the account\", func(t *testing.T) {\n\t\t\tvalues := func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}\n\n\t\t\tcl := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, values), email, cl, func(_ *http.Client, req *http.Request) (*http.Response, error) {\n\t\t\t\t_, res := testhelpers.MockMakeAuthenticatedRequestWithClientAndID(t, reg, conf, publicRouter, req, cl, id)\n\t\t\t\treturn res, nil\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"description=should recover and invalidate all other sessions if hook is set\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"revoke_active_sessions\"}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t})\n\n\t\trecoveryEmail := strings.ToLower(testhelpers.RandomEmail())\n\t\temail := recoveryEmail\n\t\tid := createIdentityToRecover(t, reg, email)\n\n\t\treq := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil)\n\t\tsess, err := testhelpers.NewActiveSession(req, reg, id, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), sess))\n\n\t\tactualSession, err := reg.SessionPersister().GetSession(t.Context(), sess.ID, session.ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, actualSession.IsActive())\n\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Recover access to your account\")\n\t\t\trecoveryLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\t\t\tres, err := cl.Get(recoveryLink)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\n\t\t\tactualSession, err := reg.SessionPersister().GetSession(t.Context(), sess.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.False(t, actualSession.IsActive())\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t}\n\n\t\tcheck(t, expectSuccess(t, nil, false, false, values))\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid link\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.InitializeRecoveryFlowViaBrowser(t, c, false, public, nil)\n\t\tres, err := c.Get(f.Ui.Action + \"&token=i-do-not-exist\")\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowRecoveryUI(ctx).String()+\"?flow=\")\n\n\t\trs, _, err := testhelpers.NewSDKCustomClient(public, c).FrontendAPI.GetRecoveryFlow(t.Context()).Id(res.Request.URL.Query().Get(\"flow\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, rs.Ui.Messages, 1)\n\t\tassert.Equal(t, \"The recovery token is invalid or has already been used. Please retry the flow.\", rs.Ui.Messages[0].Text)\n\t})\n\n\tt.Run(\"description=should not be able to use an outdated link\", func(t *testing.T) {\n\t\trecoveryEmail := \"recoverme5@ory.sh\"\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Millisecond*200)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Minute)\n\t\t})\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetRecoveryFlow(t, c, public)\n\n\t\ttime.Sleep(time.Millisecond * 201)\n\n\t\tres, err := c.PostForm(rs.Ui.Action, url.Values{\"email\": {recoveryEmail}})\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.NotContains(t, res.Request.URL.String(), \"flow=\"+rs.Id)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowRecoveryUI(ctx).String())\n\n\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(t.Context(), identity.AddressTypeEmail, recoveryEmail)\n\t\tassert.NoError(t, err)\n\t\tassert.False(t, addr.Verified)\n\t\tassert.Nil(t, addr.VerifiedAt)\n\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\t})\n\n\tt.Run(\"description=should not be able to use an outdated flow\", func(t *testing.T) {\n\t\trecoveryEmail := \"recoverme6@ory.sh\"\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Millisecond*200)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRecoveryRequestLifespan, time.Minute)\n\t\t})\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody := expectSuccess(t, c, false, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t})\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Recover access to your account\")\n\t\tassert.Contains(t, message.Body, \"Recover access to your account by clicking the following link\")\n\n\t\trecoveryLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\ttime.Sleep(time.Millisecond * 201)\n\n\t\tres, err := c.Get(recoveryLink)\n\t\trequire.NoError(t, err)\n\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowRecoveryUI(ctx).String())\n\t\tassert.NotContains(t, res.Request.URL.String(), gjson.Get(body, \"id\").String())\n\n\t\trs, _, err := testhelpers.NewSDKCustomClient(public, c).FrontendAPI.GetRecoveryFlow(t.Context()).Id(res.Request.URL.Query().Get(\"flow\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, rs.Ui.Messages, 1)\n\t\tassert.Contains(t, rs.Ui.Messages[0].Text, \"The recovery flow expired\")\n\n\t\taddr, err := reg.IdentityPool().FindVerifiableAddressByValue(t.Context(), identity.AddressTypeEmail, recoveryEmail)\n\t\tassert.NoError(t, err)\n\t\tassert.False(t, addr.Verified)\n\t\tassert.Nil(t, addr.VerifiedAt)\n\t\tassert.Equal(t, identity.VerifiableAddressStatusPending, addr.Status)\n\t})\n\n\tt.Run(\"description=should recover if post recovery hook is successful\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{}`)}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t})\n\n\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Recover access to your account\")\n\t\t\trecoveryLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\t\t\tres, err := cl.Get(recoveryLink)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 2)\n\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.Contains(t, cookies, \"ory_kratos_session\")\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t}\n\n\t\tcheck(t, expectSuccess(t, nil, false, false, values))\n\t})\n\n\tt.Run(\"description=should not be able to recover if post recovery hook fails\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), []config.SelfServiceHook{{Name: \"err\", Config: []byte(`{\"ExecutePostRecoveryHook\": \"err\"}`)}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRecoveryAfter, config.HookGlobal), nil)\n\t\t})\n\n\t\trecoveryEmail := testhelpers.RandomEmail()\n\t\tcreateIdentityToRecover(t, reg, recoveryEmail)\n\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, recoveryEmail, \"Recover access to your account\")\n\t\t\trecoveryLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\t\tcl.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\t\t\tres, err := cl.Get(recoveryLink)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.Equal(t, http.StatusSeeOther, res.StatusCode)\n\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\tcookies := spew.Sdump(cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)))\n\t\t\tassert.NotContains(t, cookies, \"ory_kratos_session\")\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", recoveryEmail)\n\t\t}\n\n\t\tcheck(t, expectSuccess(t, nil, false, false, values))\n\t})\n}\n\nfunc TestDisabledEndpoint(t *testing.T) {\n\tctx := t.Context()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tinitViper(t, conf)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(recovery.RecoveryStrategyLink)+\".enabled\", false)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(recovery.RecoveryStrategyCode)+\".enabled\", false)\n\n\tpublicTS, adminTS := testhelpers.NewKratosServer(t, reg)\n\tadminSDK := testhelpers.NewSDKClient(adminTS)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tt.Run(\"role=admin\", func(t *testing.T) {\n\t\tt.Run(\"description=can not create recovery link when link method is disabled\", func(t *testing.T) {\n\t\t\tid := identity.Identity{Traits: identity.Traits(`{\"email\":\"recovery-endpoint-disabled@ory.sh\"}`)}\n\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(t.Context(),\n\t\t\t\t&id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\trl, _, err := adminSDK.IdentityAPI.CreateRecoveryLinkForIdentity(t.Context()).CreateRecoveryLinkForIdentityBody(kratos.CreateRecoveryLinkForIdentityBody{\n\t\t\t\tIdentityId: id.ID.String(),\n\t\t\t}).Execute()\n\t\t\tassert.Nil(t, rl)\n\t\t\trequire.IsType(t, new(kratos.GenericOpenAPIError), err, \"%s\", err)\n\n\t\t\tbr, _ := err.(*kratos.GenericOpenAPIError)\n\t\t\tassert.Contains(t, string(br.Body()), \"This endpoint was disabled by system administrator\", \"%s\", br.Body())\n\t\t})\n\t})\n\n\tt.Run(\"role=public\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\n\t\tt.Run(\"description=can not recover an account by get request when link method is disabled\", func(t *testing.T) {\n\t\t\tf := testhelpers.PersistNewRecoveryFlow(t, nil, conf, reg)\n\t\t\tu := publicTS.URL + recovery.RouteSubmitFlow + \"?flow=\" + f.ID.String() + \"&token=endpoint-disabled\"\n\t\t\tres, err := c.Get(u)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tb := ioutilx.MustReadAll(res.Body)\n\t\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\")\n\t\t})\n\n\t\tt.Run(\"description=can not recover an account by post request when link method is disabled\", func(t *testing.T) {\n\t\t\tf := testhelpers.PersistNewRecoveryFlow(t, nil, conf, reg)\n\t\t\tu := publicTS.URL + recovery.RouteSubmitFlow + \"?flow=\" + f.ID.String()\n\t\t\tres, err := c.PostForm(u, url.Values{\"email\": {\"email@ory.sh\"}, \"method\": {\"link\"}})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tb := ioutilx.MustReadAll(res.Body)\n\t\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\")\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/link/strategy_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n)\n\nfunc initViper(t *testing.T, c *config.Config) {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusOK)\n\t\t_, _ = w.Write([]byte(`OK`))\n\t}))\n\tt.Cleanup(ts.Close)\n\tctx := context.Background()\n\ttesthelpers.SetDefaultIdentitySchema(c, \"file://./stub/default.schema.json\")\n\tc.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, ts.URL)\n\tc.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{ts.URL})\n\tc.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+identity.CredentialsTypePassword.String()+\".enabled\", true)\n\tc.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(recovery.RecoveryStrategyLink)+\".enabled\", true)\n\tc.MustSet(ctx, config.ViperKeySelfServiceRecoveryUse, \"link\")\n\tc.MustSet(ctx, config.ViperKeySelfServiceRecoveryEnabled, true)\n\tc.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)\n\tc.MustSet(ctx, config.ViperKeySelfServiceVerificationUse, \"link\")\n}\n"
  },
  {
    "path": "selfservice/strategy/link/strategy_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc (s *Strategy) VerificationStrategyID() string {\n\treturn string(verification.VerificationStrategyLink)\n}\n\nfunc (s *Strategy) PopulateVerificationMethod(r *http.Request, f *verification.Flow) error {\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.GetNodes().Upsert(\n\t\t// v0.5: form.Field{Name: \"email\", Type: \"email\", Required: true}\n\t\tnode.NewInputField(\"email\", nil, node.LinkGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).WithMetaLabel(text.NewInfoNodeInputEmail()),\n\t)\n\tf.UI.GetNodes().Append(node.NewInputField(\"method\", s.VerificationStrategyID(), node.LinkGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelContinue()))\n\treturn nil\n}\n\ntype verificationSubmitPayload struct {\n\tMethod           string          `json:\"method\" form:\"method\"`\n\tToken            string          `json:\"token\" form:\"token\"`\n\tCSRFToken        string          `json:\"csrf_token\" form:\"csrf_token\"`\n\tFlow             string          `json:\"flow\" form:\"flow\"`\n\tEmail            string          `json:\"email\" form:\"email\"`\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) decodeVerification(r *http.Request) (*verificationSubmitPayload, error) {\n\tvar body verificationSubmitPayload\n\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(verificationMethodSchema)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tif err := decoderx.Decode(r, &body, compiler,\n\t\tdecoderx.HTTPDecoderUseQueryAndBody(),\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn &body, nil\n}\n\n// handleVerificationError is a convenience function for handling all types of errors that may occur (e.g. validation error).\nfunc (s *Strategy) handleVerificationError(r *http.Request, f *verification.Flow, body *verificationSubmitPayload, err error) error {\n\tif f != nil {\n\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\temail := \"\"\n\t\tif body != nil {\n\t\t\temail = body.Email\n\t\t}\n\t\tf.UI.GetNodes().Upsert(\n\t\t\t// v0.5: form.Field{Name: \"email\", Type: \"email\", Required: true, Value: body.Body.Email}\n\t\t\tnode.NewInputField(\"email\", email, node.LinkGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).WithMetaLabel(text.NewInfoNodeInputEmail()),\n\t\t)\n\t}\n\n\treturn err\n}\n\n// Update Verification Flow with Link Method\n//\n// swagger:model updateVerificationFlowWithLinkMethod\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateVerificationFlowWithLinkMethod struct {\n\t// Email to Verify\n\t//\n\t// Needs to be set when initiating the flow. If the email is a registered\n\t// verification email, a verification link will be sent. If the email is not known,\n\t// a email with details on what happened will be sent instead.\n\t//\n\t// format: email\n\t// required: true\n\tEmail string `json:\"email\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `form:\"csrf_token\" json:\"csrf_token\"`\n\n\t// Method is the method that should be used for this verification flow\n\t//\n\t// Allowed values are `link` and `code`\n\t//\n\t// required: true\n\tMethod verification.VerificationStrategy `json:\"method\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) Verify(w http.ResponseWriter, r *http.Request, f *verification.Flow) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.link.Strategy.Verify\")\n\tspan.SetAttributes(attribute.String(\"selfservice_flows_verification_use\", s.d.Config().SelfServiceFlowVerificationUse(ctx)))\n\tdefer otelx.End(span, &err)\n\n\tbody, err := s.decodeVerification(r)\n\tif err != nil {\n\t\treturn s.handleVerificationError(r, nil, body, err)\n\t}\n\tf.TransientPayload = body.TransientPayload\n\n\tif len(body.Token) > 0 {\n\t\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.VerificationStrategyID(), s.VerificationStrategyID(), s.d); err != nil {\n\t\t\treturn s.handleVerificationError(r, nil, body, err)\n\t\t}\n\n\t\treturn s.verificationUseToken(ctx, w, r, body, f)\n\t}\n\n\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.VerificationStrategyID(), body.Method, s.d); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\tif err := f.Valid(); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\tswitch f.State {\n\tcase flow.StateChooseMethod, flow.StateEmailSent:\n\t\treturn s.verificationHandleFormSubmission(ctx, r, f)\n\tcase flow.StatePassedChallenge:\n\t\treturn s.retryVerificationFlowWithMessage(ctx, w, r, f.Type, text.NewErrorValidationVerificationRetrySuccess())\n\tdefault:\n\t\treturn s.retryVerificationFlowWithMessage(ctx, w, r, f.Type, text.NewErrorValidationVerificationStateFailure())\n\t}\n}\n\nfunc (s *Strategy) verificationHandleFormSubmission(ctx context.Context, r *http.Request, f *verification.Flow) error {\n\tbody, err := s.decodeVerification(r)\n\tif err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\tif len(body.Email) == 0 {\n\t\treturn s.handleVerificationError(r, f, body, schema.NewRequiredError(\"#/email\", \"email\"))\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, body.CSRFToken); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\tif err := s.d.LinkSender().SendVerificationLink(ctx, f, identity.AddressTypeEmail, body.Email); err != nil {\n\t\tif !errors.Is(err, ErrUnknownAddress) {\n\t\t\treturn s.handleVerificationError(r, f, body, err)\n\t\t}\n\t\t// Continue execution\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.GetNodes().Upsert(\n\t\t// v0.5: form.Field{Name: \"email\", Type: \"email\", Required: true, Value: body.Body.Email}\n\t\tnode.NewInputField(\"email\", body.Email, node.LinkGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute).WithMetaLabel(text.NewInfoNodeInputEmail()),\n\t)\n\n\tf.Active = sqlxx.NullString(s.NodeGroup())\n\tf.State = flow.StateEmailSent\n\tf.UI.Messages.Set(text.NewVerificationEmailSent())\n\tif err := s.d.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {\n\t\treturn s.handleVerificationError(r, f, body, err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) verificationUseToken(ctx context.Context, w http.ResponseWriter, r *http.Request, body *verificationSubmitPayload, f *verification.Flow) error {\n\ttoken, err := s.d.VerificationTokenPersister().UseVerificationToken(ctx, f.ID, body.Token)\n\tif err != nil {\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\treturn s.retryVerificationFlowWithMessage(ctx, w, r, flow.TypeBrowser, text.NewErrorValidationVerificationTokenInvalidOrAlreadyUsed())\n\t\t}\n\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)\n\t}\n\n\tif err := token.Valid(); err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)\n\t}\n\n\taddress := token.VerifiableAddress\n\taddress.Verified = true\n\tverifiedAt := sqlxx.NullTime(time.Now().UTC())\n\taddress.VerifiedAt = &verifiedAt\n\taddress.Status = identity.VerifiableAddressStatusCompleted\n\tif err := s.d.PrivilegedIdentityPool().UpdateVerifiableAddress(ctx, address, \"verified\", \"verified_at\", \"status\"); err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)\n\t}\n\n\ti, err := s.d.IdentityPool().GetIdentity(ctx, token.VerifiableAddress.IdentityID, identity.ExpandDefault)\n\tif err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)\n\t}\n\n\treturnTo := f.ContinueURL(ctx, s.d.Config())\n\n\tf.UI.\n\t\tNodes.\n\t\tAppend(node.NewAnchorField(\"continue\", returnTo.String(), node.CodeGroup, text.NewInfoNodeLabelContinue()).\n\t\t\tWithMetaLabel(text.NewInfoNodeLabelContinue()))\n\n\tf.UI = &container.Container{\n\t\tMethod: \"GET\",\n\t\tAction: returnTo.String(),\n\t}\n\tf.UI.Messages.Clear()\n\tf.State = flow.StatePassedChallenge\n\t// See https://github.com/ory/kratos/issues/1547\n\tf.SetCSRFToken(flow.GetCSRFToken(s.d, w, r, f.Type))\n\tf.UI.Messages.Set(text.NewInfoSelfServiceVerificationSuccessful())\n\tf.UI.\n\t\tNodes.\n\t\tAppend(node.NewAnchorField(\"continue\", returnTo.String(), node.LinkGroup, text.NewInfoNodeLabelContinue()).\n\t\t\tWithMetaLabel(text.NewInfoNodeLabelContinue()))\n\n\tif err := s.d.VerificationFlowPersister().UpdateVerificationFlow(ctx, f); err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)\n\t}\n\n\tif err := s.d.VerificationExecutor().PostVerificationHook(w, r, f, i); err != nil {\n\t\treturn s.retryVerificationFlowWithError(ctx, w, r, flow.TypeBrowser, err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) retryVerificationFlowWithMessage(ctx context.Context, w http.ResponseWriter, r *http.Request, ft flow.Type, message *text.Message) error {\n\ts.d.Logger().WithRequest(r).WithField(\"message\", message).Debug(\"A verification flow is being retried because a validation error occurred.\")\n\n\tf, err := verification.NewFlow(s.d.Config(),\n\t\ts.d.Config().SelfServiceFlowVerificationRequestLifespan(ctx), s.d.CSRFHandler().RegenerateToken(w, r), r, verification.Strategies{s}, ft)\n\tif err != nil {\n\t\treturn s.handleVerificationError(r, f, nil, err)\n\t}\n\n\tf.UI.Messages.Add(message)\n\tif err := s.d.VerificationFlowPersister().CreateVerificationFlow(ctx, f); err != nil {\n\t\treturn s.handleVerificationError(r, f, nil, err)\n\t}\n\n\tif ft == flow.TypeBrowser {\n\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)\n\t} else {\n\t\thttp.Redirect(w, r, urlx.CopyWithQuery(urlx.AppendPaths(s.d.Config().SelfPublicURL(ctx),\n\t\t\tverification.RouteGetFlow), url.Values{\"id\": {f.ID.String()}}).String(), http.StatusSeeOther)\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) retryVerificationFlowWithError(ctx context.Context, w http.ResponseWriter, r *http.Request, ft flow.Type, verErr error) error {\n\ts.d.Logger().WithRequest(r).WithError(verErr).Debug(\"A verification flow is being retried because an error occurred.\")\n\n\tf, err := verification.NewFlow(s.d.Config(),\n\t\ts.d.Config().SelfServiceFlowVerificationRequestLifespan(ctx), s.d.CSRFHandler().RegenerateToken(w, r), r, verification.Strategies{s}, ft)\n\tif err != nil {\n\t\treturn s.handleVerificationError(r, f, nil, err)\n\t}\n\n\tif expired := new(flow.ExpiredError); errors.As(verErr, &expired) {\n\t\treturn s.retryVerificationFlowWithMessage(ctx, w, r, ft, text.NewErrorValidationVerificationFlowExpired(expired.ExpiredAt))\n\t} else {\n\t\tif err := f.UI.ParseError(node.LinkGroup, verErr); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif err := s.d.VerificationFlowPersister().CreateVerificationFlow(ctx, f); err != nil {\n\t\treturn s.handleVerificationError(r, f, nil, err)\n\t}\n\n\tif ft == flow.TypeBrowser {\n\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowVerificationUI(ctx)).String(), http.StatusSeeOther)\n\t} else {\n\t\thttp.Redirect(w, r, urlx.CopyWithQuery(urlx.AppendPaths(s.d.Config().SelfPublicURL(ctx),\n\t\t\tverification.RouteGetFlow), url.Values{\"id\": {f.ID.String()}}).String(), http.StatusSeeOther)\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) SendVerificationCode(ctx context.Context, f *verification.Flow, i *identity.Identity, a *identity.VerifiableAddress) error {\n\ttoken := NewSelfServiceVerificationToken(a, f, s.d.Config().SelfServiceLinkMethodLifespan(ctx))\n\tif err := s.d.VerificationTokenPersister().CreateVerificationToken(ctx, token); err != nil {\n\t\treturn err\n\t}\n\n\treturn s.d.LinkSender().SendVerificationTokenTo(ctx, f, i, a, token)\n}\n"
  },
  {
    "path": "selfservice/strategy/link/strategy_verification_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc TestVerification(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tinitViper(t, conf)\n\n\tidentityToVerify := &identity.Identity{\n\t\tID:       x.NewUUID(),\n\t\tTraits:   identity.Traits(`{\"email\":\"verifyme@ory.sh\"}`),\n\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\"password\": {Type: \"password\", Identifiers: []string{\"recoverme@ory.sh\"}, Config: sqlxx.JSONRawMessage(`{\"hashed_password\":\"foo\"}`)},\n\t\t},\n\t}\n\n\tverificationEmail := gjson.GetBytes(identityToVerify.Traits, \"email\").String()\n\n\t_ = testhelpers.NewVerificationUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewRedirTS(t, \"returned\", conf)\n\n\tpublic, _ := testhelpers.NewKratosServerWithCSRF(t, reg)\n\n\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), identityToVerify,\n\t\tidentity.ManagerAllowWriteProtectedTraits))\n\n\texpect := func(t *testing.T, hc *http.Client, isAPI, isSPA bool, values func(url.Values), c int) string {\n\t\tif hc == nil {\n\t\t\thc = testhelpers.NewDebugClient(t)\n\t\t\tif !isAPI {\n\t\t\t\thc = testhelpers.NewClientWithCookies(t)\n\t\t\t\thc.Transport = testhelpers.NewTransportWithLogger(http.DefaultTransport, t).RoundTripper\n\t\t\t}\n\t\t}\n\n\t\treturn testhelpers.SubmitVerificationForm(t, isAPI, isSPA, hc, public, values, c,\n\t\t\ttesthelpers.ExpectURL(isAPI || isSPA, public.URL+verification.RouteSubmitFlow, conf.SelfServiceFlowVerificationUI(ctx).String()))\n\t}\n\n\texpectValidationError := func(t *testing.T, hc *http.Client, isAPI, isSPA bool, values func(url.Values)) string {\n\t\treturn expect(t, hc, isAPI, isSPA, values, testhelpers.ExpectStatusCode(isAPI || isSPA, http.StatusBadRequest, http.StatusOK))\n\t}\n\n\texpectSuccess := func(t *testing.T, hc *http.Client, isAPI, isSPA bool, values func(url.Values)) string {\n\t\treturn expect(t, hc, isAPI, isSPA, values, http.StatusOK)\n\t}\n\n\tt.Run(\"description=should set all the correct verification payloads after submission\", func(t *testing.T) {\n\t\tbody := expectSuccess(t, nil, false, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", \"test@ory.sh\")\n\t\t})\n\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes\").String()), []string{\"0.attributes.value\"})\n\t})\n\n\tt.Run(\"description=should set all the correct verification payloads\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetVerificationFlow(t, c, public)\n\n\t\ttesthelpers.SnapshotTExcept(t, rs.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\tassert.EqualValues(t, public.URL+verification.RouteSubmitFlow+\"?flow=\"+rs.Id, rs.Ui.Action)\n\t\tassert.Empty(t, rs.Ui.Messages)\n\t})\n\n\tt.Run(\"description=should not execute submit without correct method set\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetVerificationFlow(t, c, public)\n\n\t\tres, err := c.PostForm(rs.Ui.Action, url.Values{\"method\": {\"not-link\"}, \"email\": {verificationEmail}})\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowVerificationUI(ctx).String())\n\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\t\tassert.Equal(t, \"Could not find a strategy to verify your account with. Did you fill out the form correctly?\", gjson.GetBytes(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"description=should require an email to be sent\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, string(node.LinkGroup), gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, \"Property email is missing.\",\n\t\t\t\tgjson.Get(actual, \"ui.nodes.#(attributes.name==email).messages.0.text\").String(),\n\t\t\t\t\"%s\", actual)\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Del(\"email\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, nil, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, nil, false, true, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, nil, true, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"description=should require a valid email to be sent\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string, value string) {\n\t\t\tassert.EqualValues(t, string(node.LinkGroup), gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, \"Enter a valid email address\",\n\t\t\t\tgjson.Get(actual, \"ui.nodes.#(attributes.name==email).messages.0.text\").String(),\n\t\t\t\t\"%s\", actual)\n\t\t}\n\n\t\tfor _, email := range []string{\"\\\\\", \"asdf\", \"...\", \"aiacobelli.sec@gmail.com,alejandro.iacobelli@mercadolibre.com\"} {\n\t\t\tvalues := func(v url.Values) {\n\t\t\t\tv.Set(\"email\", email)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, nil, false, false, values), email)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, nil, false, true, values), email)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tcheck(t, expectValidationError(t, nil, true, false, values), email)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=should try to verify an email that does not exist\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationNotifyUnknownRecipients, true)\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationNotifyUnknownRecipients, false)\n\t\t})\n\t\tvar email string\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, string(node.LinkGroup), gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==email).attributes.value\").String(), \"%s\", actual)\n\t\t\tassertx.EqualAsJSON(t, text.NewVerificationEmailSent(), json.RawMessage(gjson.Get(actual, \"ui.messages.0\").Raw))\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, email, \"Someone tried to verify this email address\")\n\t\t\tassert.Contains(t, message.Body, \"If this was you, check if you signed up using a different address.\")\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", email)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\temail = x.NewUUID().String() + \"@ory.sh\"\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\temail = x.NewUUID().String() + \"@ory.sh\"\n\t\t\tcheck(t, expectSuccess(t, nil, false, true, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\temail = x.NewUUID().String() + \"@ory.sh\"\n\t\t\tcheck(t, expectSuccess(t, nil, true, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"description=should not be able to use an invalid link\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.InitializeVerificationFlowViaBrowser(t, c, false, public)\n\t\tres, err := c.Get(public.URL + verification.RouteSubmitFlow + \"?flow=\" + f.Id + \"&token=i-do-not-exist\")\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowVerificationUI(ctx).String()+\"?flow=\")\n\n\t\tsr, _, err := testhelpers.NewSDKCustomClient(public, c).FrontendAPI.GetVerificationFlow(context.Background()).Id(res.Request.URL.Query().Get(\"flow\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, sr.Ui.Messages, 1)\n\t\tassert.Equal(t, \"The verification token is invalid or has already been used. Please retry the flow.\", sr.Ui.Messages[0].Text)\n\t})\n\n\tt.Run(\"description=should not be able to request link with an outdated flow\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationRequestLifespan, time.Millisecond*200)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationRequestLifespan, time.Minute)\n\t\t})\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\trs := testhelpers.GetVerificationFlow(t, c, public)\n\n\t\ttime.Sleep(time.Millisecond * 201)\n\n\t\tres, err := c.PostForm(rs.Ui.Action, url.Values{\"method\": {\"link\"}, \"email\": {verificationEmail}})\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.NotContains(t, res.Request.URL.String(), \"flow=\"+rs.Id)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowVerificationUI(ctx).String())\n\t})\n\n\tt.Run(\"description=should not be able to use link with an outdated flow\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationRequestLifespan, time.Millisecond*200)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationRequestLifespan, time.Minute)\n\t\t})\n\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tbody := expectSuccess(t, c, false, false, func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t})\n\n\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Please verify your email address\")\n\t\tassert.Contains(t, message.Body, \"Verify your account by opening the following link\")\n\n\t\tverificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\ttime.Sleep(time.Millisecond * 201)\n\n\t\t// Clear cookies as link might be opened in another browser\n\t\tc = testhelpers.NewClientWithCookies(t)\n\t\tres, err := c.Get(verificationLink)\n\t\trequire.NoError(t, err)\n\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowVerificationUI(ctx).String())\n\t\tassert.NotContains(t, res.Request.URL.String(), gjson.Get(body, \"id\").String())\n\n\t\tsr, _, err := testhelpers.NewSDKCustomClient(public, c).FrontendAPI.GetVerificationFlow(context.Background()).Id(res.Request.URL.Query().Get(\"flow\")).Execute()\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, sr.Ui.Messages, 1)\n\t\tassert.Contains(t, sr.Ui.Messages[0].Text, \"The verification flow expired\")\n\t})\n\n\tt.Run(\"description=should verify an email address\", func(t *testing.T) {\n\t\tvar wg sync.WaitGroup\n\t\ttesthelpers.NewVerifyAfterHookWebHookTarget(ctx, t, conf, func(t *testing.T, msg []byte) {\n\t\t\tdefer wg.Done()\n\t\t\tassert.EqualValues(t, true, gjson.GetBytes(msg, \"identity.verifiable_addresses.0.verified\").Bool(), string(msg))\n\t\t\tassert.EqualValues(t, \"completed\", gjson.GetBytes(msg, \"identity.verifiable_addresses.0.status\").String(), string(msg))\n\t\t})\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, string(node.LinkGroup), gjson.Get(actual, \"active\").String(), \"%s\", actual)\n\t\t\tassert.EqualValues(t, verificationEmail, gjson.Get(actual, \"ui.nodes.#(attributes.name==email).attributes.value\").String(), \"%s\", actual)\n\t\t\tassertx.EqualAsJSON(t, text.NewVerificationEmailSent(), json.RawMessage(gjson.Get(actual, \"ui.messages.0\").Raw))\n\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Please verify your email address\")\n\t\t\tassert.Contains(t, message.Body, \"Verify your account by opening the following link\")\n\n\t\t\tverificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\t\tassert.Contains(t, verificationLink, public.URL+verification.RouteSubmitFlow)\n\t\t\tassert.Contains(t, verificationLink, \"token=\")\n\n\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\t\tres, err := cl.Get(verificationLink)\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowVerificationUI(ctx).String())\n\t\t\tbody := string(ioutilx.MustReadAll(res.Body))\n\t\t\tassert.EqualValues(t, \"passed_challenge\", gjson.Get(body, \"state\").String())\n\t\t\tassert.EqualValues(t, text.NewInfoSelfServiceVerificationSuccessful().Text, gjson.Get(body, \"ui.messages.0.text\").String())\n\n\t\t\tid, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), identityToVerify.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, id.VerifiableAddresses, 1)\n\n\t\t\taddress := id.VerifiableAddresses[0]\n\t\t\tassert.EqualValues(t, verificationEmail, address.Value)\n\t\t\tassert.True(t, address.Verified)\n\t\t\tassert.EqualValues(t, identity.VerifiableAddressStatusCompleted, address.Status)\n\t\t\tassert.True(t, time.Time(*address.VerifiedAt).Add(time.Second*5).After(time.Now()))\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\twg.Add(1)\n\t\t\tcheck(t, expectSuccess(t, nil, false, false, values))\n\t\t\twg.Wait()\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\twg.Add(1)\n\t\t\tcheck(t, expectSuccess(t, nil, false, true, values))\n\t\t\twg.Wait()\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\twg.Add(1)\n\t\t\tcheck(t, expectSuccess(t, nil, true, false, values))\n\t\t\twg.Wait()\n\t\t})\n\t})\n\n\tt.Run(\"description=should verify an email address when the link is opened in another browser\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tmessage := testhelpers.CourierExpectMessage(ctx, t, reg, verificationEmail, \"Please verify your email address\")\n\t\t\tverificationLink := testhelpers.CourierExpectLinkInMessage(t, message, 1)\n\n\t\t\tcl := testhelpers.NewClientWithCookies(t)\n\t\t\tres, err := cl.Get(verificationLink)\n\t\t\trequire.NoError(t, err)\n\t\t\tbody := string(ioutilx.MustReadAll(res.Body))\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\trequire.Len(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL)), 1)\n\t\t\tassert.Contains(t, cl.Jar.Cookies(urlx.ParseOrPanic(public.URL))[0].Name, nosurfx.CSRFTokenName)\n\n\t\t\tactualRes, err := cl.Get(public.URL + verification.RouteGetFlow + \"?id=\" + gjson.Get(body, \"id\").String())\n\t\t\trequire.NoError(t, err)\n\t\t\tactualBody := string(ioutilx.MustReadAll(actualRes.Body))\n\t\t\trequire.NoError(t, actualRes.Body.Close())\n\t\t\tassert.Equal(t, http.StatusOK, actualRes.StatusCode)\n\n\t\t\tassertx.EqualAsJSON(t, body, actualBody)\n\t\t\tassert.EqualValues(t, \"passed_challenge\", gjson.Get(actualBody, \"state\").String())\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"email\", verificationEmail)\n\t\t}\n\n\t\tcheck(t, expectSuccess(t, nil, false, false, values))\n\t})\n\n\tnewValidFlow := func(t *testing.T, fType flow.Type, requestURL string) (*verification.Flow, *link.VerificationToken) {\n\t\tf, err := verification.NewFlow(conf, time.Hour, nosurfx.FakeCSRFToken, httptest.NewRequest(\"GET\", requestURL, nil), nil, fType)\n\t\trequire.NoError(t, err)\n\t\tf.State = flow.StateEmailSent\n\t\trequire.NoError(t, reg.VerificationFlowPersister().CreateVerificationFlow(context.Background(), f))\n\t\temail := identity.NewVerifiableEmailAddress(verificationEmail, identityToVerify.ID)\n\t\tidentityToVerify.VerifiableAddresses = append(identityToVerify.VerifiableAddresses, *email)\n\t\trequire.NoError(t, reg.IdentityManager().Update(context.Background(), identityToVerify, identity.ManagerAllowWriteProtectedTraits))\n\n\t\ttoken := link.NewSelfServiceVerificationToken(&identityToVerify.VerifiableAddresses[0], f, time.Hour)\n\t\trequire.NoError(t, reg.VerificationTokenPersister().CreateVerificationToken(context.Background(), token))\n\t\treturn f, token\n\t}\n\n\tnewValidBrowserFlow := func(t *testing.T, requestURL string) (*verification.Flow, *link.VerificationToken) {\n\t\treturn newValidFlow(t, flow.TypeBrowser, requestURL)\n\t}\n\n\tt.Run(\"case=respects return_to URI parameter\", func(t *testing.T) {\n\t\treturnToURL := public.URL + \"/after-verification\"\n\t\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{returnToURL})\n\n\t\tfor _, fType := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {\n\t\t\tt.Run(fmt.Sprintf(\"type=%s\", fType), func(t *testing.T) {\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tflow, token := newValidFlow(t, fType, public.URL+verification.RouteInitBrowserFlow+\"?\"+url.Values{\"return_to\": {returnToURL}}.Encode())\n\n\t\t\t\tres, err := client.Get(public.URL + verification.RouteSubmitFlow + \"?\" + url.Values{\"flow\": {flow.ID.String()}, \"token\": {token.Token}}.Encode())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tresponseBody := gjson.ParseBytes(ioutilx.MustReadAll(res.Body))\n\n\t\t\t\tassert.Equal(t, responseBody.Get(\"state\").String(), \"passed_challenge\", \"%v\", responseBody)\n\t\t\t\tassert.True(t, responseBody.Get(\"ui.nodes.#(attributes.id==continue)\").Exists(), \"%v\", responseBody)\n\t\t\t\tassert.Equal(t, returnToURL, responseBody.Get(\"ui.nodes.#(attributes.id==continue).attributes.href\").String(), \"%v\", responseBody)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=contains default return to url\", func(t *testing.T) {\n\t\tglobalReturnTo := public.URL + \"/global\"\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, globalReturnTo)\n\n\t\tfor _, fType := range []flow.Type{flow.TypeBrowser, flow.TypeAPI} {\n\t\t\tt.Run(fmt.Sprintf(\"type=%s\", fType), func(t *testing.T) {\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tflow, token := newValidFlow(t, fType, public.URL+verification.RouteInitBrowserFlow)\n\n\t\t\t\tres, err := client.Get(public.URL + verification.RouteSubmitFlow + \"?\" + url.Values{\"flow\": {flow.ID.String()}, \"token\": {token.Token}}.Encode())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tresponseBody := gjson.ParseBytes(ioutilx.MustReadAll(res.Body))\n\n\t\t\t\tassert.Equal(t, responseBody.Get(\"state\").String(), \"passed_challenge\", \"%v\", responseBody)\n\t\t\t\tassert.True(t, responseBody.Get(\"ui.nodes.#(attributes.id==continue)\").Exists(), \"%v\", responseBody)\n\t\t\t\tassert.Equal(t, globalReturnTo, responseBody.Get(\"ui.nodes.#(attributes.id==continue).attributes.href\").String(), \"%v\", responseBody)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should not be able to use code from different flow\", func(t *testing.T) {\n\t\tf1, _ := newValidBrowserFlow(t, public.URL+verification.RouteInitBrowserFlow)\n\n\t\t_, t2 := newValidBrowserFlow(t, public.URL+verification.RouteInitBrowserFlow)\n\n\t\tformValues := url.Values{\n\t\t\t\"flow\":  {f1.ID.String()},\n\t\t\t\"token\": {t2.Token},\n\t\t}\n\t\tsubmitUrl := public.URL + verification.RouteSubmitFlow + \"?\" + formValues.Encode()\n\n\t\tres, err := public.Client().Get(submitUrl)\n\t\trequire.NoError(t, err)\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\tassert.Equal(t, \"The verification token is invalid or has already been used. Please retry the flow.\", gjson.GetBytes(body, \"ui.messages.0.text\").String())\n\t})\n\n\tt.Run(\"case=doesn't continue with OAuth2 flow if code is invalid\", func(t *testing.T) {\n\t\tglobalReturnTo := public.URL + \"/global\"\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, globalReturnTo)\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tflow, _ := newValidFlow(t, flow.TypeBrowser, public.URL+verification.RouteInitBrowserFlow)\n\n\t\tres, err := client.Get(public.URL + verification.RouteSubmitFlow + \"?\" + url.Values{\n\t\t\t\"flow\":  {flow.ID.String()},\n\t\t\t\"token\": {\"invalid token\"},\n\t\t}.Encode())\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\tresponseBody := gjson.ParseBytes(ioutilx.MustReadAll(res.Body))\n\n\t\tassert.Equal(t, \"choose_method\", responseBody.Get(\"state\").String(), \"%v\", responseBody)\n\t\tassert.Len(t, responseBody.Get(\"ui.messages\").Array(), 1, \"%v\", responseBody)\n\t\tassert.Equal(t, \"The verification token is invalid or has already been used. Please retry the flow.\", responseBody.Get(\"ui.messages.0.text\").String(), \"%v\", responseBody)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/link/stub/default.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/link/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc TestPersister(ctx context.Context, p interface {\n\tpersistence.Persister\n},\n) func(t *testing.T) {\n\treturn func(t *testing.T) {\n\t\tnid, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecretsDefault, []string{\"secret-a\", \"secret-b\"})\n\n\t\tt.Run(\"token=recovery\", func(t *testing.T) {\n\t\t\tnewRecoveryToken := func(t *testing.T, email string) (*link.RecoveryToken, *recovery.Flow) {\n\t\t\t\tvar req recovery.Flow\n\t\t\t\trequire.NoError(t, faker.FakeData(&req))\n\t\t\t\treq.State = flow.StateChooseMethod\n\t\t\t\trequire.NoError(t, p.CreateRecoveryFlow(ctx, &req))\n\n\t\t\t\tvar i identity.Identity\n\t\t\t\trequire.NoError(t, faker.FakeData(&i))\n\n\t\t\t\taddress := &identity.RecoveryAddress{Value: email, Via: identity.AddressTypeEmail, IdentityID: i.ID}\n\t\t\t\ti.RecoveryAddresses = append(i.RecoveryAddresses, *address)\n\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, &i))\n\n\t\t\t\treturn &link.RecoveryToken{\n\t\t\t\t\tToken:           x.NewUUID().String(),\n\t\t\t\t\tFlowID:          uuid.NullUUID{UUID: req.ID, Valid: true},\n\t\t\t\t\tRecoveryAddress: &i.RecoveryAddresses[0],\n\t\t\t\t\tExpiresAt:       time.Now(),\n\t\t\t\t\tIssuedAt:        time.Now(),\n\t\t\t\t\tIdentityID:      i.ID,\n\t\t\t\t\tTokenType:       link.RecoveryTokenTypeAdmin,\n\t\t\t\t}, &req\n\t\t\t}\n\n\t\t\tt.Run(\"case=should error when the recovery token does not exist\", func(t *testing.T) {\n\t\t\t\t_, err := p.UseRecoveryToken(ctx, x.NewUUID(), \"i-do-not-exist\")\n\t\t\t\trequire.Error(t, err)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should create a new recovery token\", func(t *testing.T) {\n\t\t\t\ttoken, _ := newRecoveryToken(t, \"foo-user@ory.sh\")\n\t\t\t\trequire.NoError(t, p.CreateRecoveryToken(ctx, token))\n\t\t\t})\n\n\t\t\tt.Run(\"case=should error when token is used with different flow id\", func(t *testing.T) {\n\t\t\t\ttoken, _ := newRecoveryToken(t, \"foo-user1@ory.sh\")\n\t\t\t\trequire.NoError(t, p.CreateRecoveryToken(ctx, token))\n\t\t\t\t_, err := p.UseRecoveryToken(ctx, x.NewUUID(), token.Token)\n\t\t\t\trequire.Error(t, err)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should create a recovery token and use it\", func(t *testing.T) {\n\t\t\t\texpected, f := newRecoveryToken(t, \"other-user@ory.sh\")\n\t\t\t\trequire.NoError(t, p.CreateRecoveryToken(ctx, expected))\n\n\t\t\t\tt.Run(\"not work on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.UseRecoveryToken(ctx, f.ID, expected.Token)\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\n\t\t\t\tactual, err := p.UseRecoveryToken(ctx, f.ID, expected.Token)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, nid, actual.NID)\n\t\t\t\tassert.Equal(t, expected.IdentityID, actual.IdentityID)\n\t\t\t\tassert.NotEqual(t, expected.Token, actual.Token)\n\t\t\t\tassert.EqualValues(t, expected.FlowID, actual.FlowID)\n\n\t\t\t\tt.Run(\"double spend\", func(t *testing.T) {\n\t\t\t\t\t_, err = p.UseRecoveryToken(ctx, f.ID, expected.Token)\n\t\t\t\t\trequire.Error(t, err)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=update to identity should not invalidate token\", func(t *testing.T) {\n\t\t\t\texpected, f := newRecoveryToken(t, \"some-user@ory.sh\")\n\n\t\t\t\trequire.NoError(t, p.CreateRecoveryToken(ctx, expected))\n\t\t\t\tid, err := p.GetIdentity(ctx, expected.IdentityID, identity.ExpandDefault)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, p.UpdateIdentity(ctx, id))\n\n\t\t\t\tactual, err := p.UseRecoveryToken(ctx, f.ID, expected.Token)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, nid, actual.NID)\n\t\t\t\tassert.Equal(t, expected.IdentityID, actual.IdentityID)\n\t\t\t\tassert.NotEqual(t, expected.Token, actual.Token)\n\t\t\t\tassert.EqualValues(t, expected.FlowID, actual.FlowID)\n\n\t\t\t\tt.Run(\"double spend\", func(t *testing.T) {\n\t\t\t\t\t_, err = p.UseRecoveryToken(ctx, f.ID, expected.Token)\n\t\t\t\t\trequire.Error(t, err)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"token=verification\", func(t *testing.T) {\n\t\t\tnewVerificationToken := func(t *testing.T, email string) (*verification.Flow, *link.VerificationToken) {\n\t\t\t\tvar f verification.Flow\n\t\t\t\trequire.NoError(t, faker.FakeData(&f))\n\t\t\t\tf.State = flow.StateChooseMethod\n\t\t\t\trequire.NoError(t, p.CreateVerificationFlow(ctx, &f))\n\n\t\t\t\tvar i identity.Identity\n\t\t\t\trequire.NoError(t, faker.FakeData(&i))\n\n\t\t\t\taddress := &identity.VerifiableAddress{Value: email, Via: identity.AddressTypeEmail}\n\t\t\t\ti.VerifiableAddresses = append(i.VerifiableAddresses, *address)\n\n\t\t\t\trequire.NoError(t, p.CreateIdentity(ctx, &i))\n\t\t\t\treturn &f, &link.VerificationToken{\n\t\t\t\t\tToken:             x.NewUUID().String(),\n\t\t\t\t\tFlowID:            f.ID,\n\t\t\t\t\tVerifiableAddress: &i.VerifiableAddresses[0],\n\t\t\t\t\tExpiresAt:         time.Now(),\n\t\t\t\t\tIssuedAt:          time.Now(),\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tt.Run(\"case=should error when the verification token does not exist\", func(t *testing.T) {\n\t\t\t\t_, err := p.UseVerificationToken(ctx, x.NewUUID(), \"i-do-not-exist\")\n\t\t\t\trequire.Error(t, err)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should error when the verification token does exist but the flow does not\", func(t *testing.T) {\n\t\t\t\t_, token := newVerificationToken(t, x.NewUUID().String()+\"@ory.sh\")\n\t\t\t\trequire.NoError(t, p.CreateVerificationToken(ctx, token))\n\t\t\t\t_, err := p.UseVerificationToken(ctx, x.NewUUID(), token.Token)\n\t\t\t\trequire.Error(t, err)\n\t\t\t})\n\n\t\t\tt.Run(\"case=should create a new verification token\", func(t *testing.T) {\n\t\t\t\t_, token := newVerificationToken(t, \"foo-user@ory.sh\")\n\t\t\t\trequire.NoError(t, p.CreateVerificationToken(ctx, token))\n\t\t\t})\n\n\t\t\tt.Run(\"case=should create a verification token and use it\", func(t *testing.T) {\n\t\t\t\tf, expected := newVerificationToken(t, \"other-user@ory.sh\")\n\t\t\t\trequire.NoError(t, p.CreateVerificationToken(ctx, expected))\n\n\t\t\t\tt.Run(\"not work on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.UseVerificationToken(ctx, f.ID, expected.Token)\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\n\t\t\t\tactual, err := p.UseVerificationToken(ctx, f.ID, expected.Token)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassertx.EqualAsJSONExcept(t, expected.VerifiableAddress, actual.VerifiableAddress, []string{\"created_at\", \"updated_at\"})\n\t\t\t\tassert.Equal(t, nid, actual.NID)\n\t\t\t\tassert.Equal(t, expected.VerifiableAddress.IdentityID, actual.VerifiableAddress.IdentityID)\n\t\t\t\tassert.NotEqual(t, expected.Token, actual.Token)\n\t\t\t\tassert.EqualValues(t, expected.FlowID, actual.FlowID)\n\n\t\t\t\t_, err = p.UseVerificationToken(ctx, f.ID, expected.Token)\n\t\t\t\trequire.Error(t, err)\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/link/token_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\n\t\"github.com/gofrs/uuid\"\n\terrors \"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/randx\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n\t\"github.com/ory/kratos/x\"\n)\n\ntype RecoveryTokenType int\n\nconst (\n\tRecoveryTokenTypeAdmin RecoveryTokenType = iota + 1\n\tRecoveryTokenTypeSelfService\n)\n\ntype RecoveryToken struct {\n\t// ID represents the tokens's unique ID.\n\t//\n\t// required: true\n\t// type: string\n\t// format: uuid\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// Token represents the recovery token. It can not be longer than 64 chars!\n\tToken string `json:\"-\" db:\"token\"`\n\n\t// RecoveryAddress links this token to a recovery address.\n\t// required: true\n\tRecoveryAddress *identity.RecoveryAddress `json:\"recovery_address\" belongs_to:\"identity_recovery_addresses\" fk_id:\"RecoveryAddressID\"`\n\n\tTokenType RecoveryTokenType `json:\"-\" faker:\"-\" db:\"token_type\"`\n\n\t// ExpiresAt is the time (UTC) when the token expires.\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the token was issued.\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\t// RecoveryAddressID is a helper struct field for gobuffalo.pop.\n\tRecoveryAddressID *uuid.UUID `json:\"-\" faker:\"-\" db:\"identity_recovery_address_id\"`\n\t// FlowID is a helper struct field for gobuffalo.pop.\n\tFlowID     uuid.NullUUID `json:\"-\" faker:\"-\" db:\"selfservice_recovery_flow_id\"`\n\tNID        uuid.UUID     `json:\"-\"  faker:\"-\" db:\"nid\"`\n\tIdentityID uuid.UUID     `json:\"identity_id\"  faker:\"-\" db:\"identity_id\"`\n}\n\nfunc (RecoveryToken) TableName(ctx context.Context) string {\n\treturn \"identity_recovery_tokens\"\n}\n\nfunc NewSelfServiceRecoveryToken(address *identity.RecoveryAddress, f *recovery.Flow, expiresIn time.Duration) *RecoveryToken {\n\tnow := time.Now().UTC()\n\tvar identityID = uuid.UUID{}\n\tvar recoveryAddressID = uuid.UUID{}\n\tif address != nil {\n\t\tidentityID = address.IdentityID\n\t\trecoveryAddressID = address.ID\n\t}\n\treturn &RecoveryToken{\n\t\tID:                x.NewUUID(),\n\t\tToken:             randx.MustString(32, randx.AlphaNum),\n\t\tRecoveryAddress:   address,\n\t\tExpiresAt:         now.Add(expiresIn),\n\t\tIssuedAt:          now,\n\t\tIdentityID:        identityID,\n\t\tFlowID:            uuid.NullUUID{UUID: f.ID, Valid: true},\n\t\tRecoveryAddressID: &recoveryAddressID,\n\t\tTokenType:         RecoveryTokenTypeSelfService,\n\t}\n}\n\nfunc NewAdminRecoveryToken(identityID uuid.UUID, fID uuid.UUID, expiresIn time.Duration) *RecoveryToken {\n\tnow := time.Now().UTC()\n\treturn &RecoveryToken{\n\t\tID:         x.NewUUID(),\n\t\tToken:      randx.MustString(32, randx.AlphaNum),\n\t\tExpiresAt:  now.Add(expiresIn),\n\t\tIssuedAt:   now,\n\t\tIdentityID: identityID,\n\t\tFlowID:     uuid.NullUUID{UUID: fID, Valid: true},\n\t\tTokenType:  RecoveryTokenTypeAdmin,\n\t}\n}\n\nfunc (f *RecoveryToken) Valid() error {\n\tif f.ExpiresAt.Before(time.Now()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/link/token_recovery_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/stringslice\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/recovery\"\n)\n\nfunc TestRecoveryToken(t *testing.T) {\n\tconf, _ := pkg.NewFastRegistryWithMocks(t)\n\n\treq := &http.Request{URL: urlx.ParseOrPanic(\"https://www.ory.sh/\")}\n\tt.Run(\"func=NewSelfServiceRecoveryToken\", func(t *testing.T) {\n\t\tt.Run(\"case=creates unique tokens\", func(t *testing.T) {\n\t\t\tf, err := recovery.NewFlow(conf, time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\ttokens := make([]string, 10)\n\t\t\tfor k := range tokens {\n\t\t\t\ttokens[k] = link.NewSelfServiceRecoveryToken(nil, f, time.Hour).Token\n\t\t\t}\n\n\t\t\tassert.Len(t, stringslice.Unique(tokens), len(tokens))\n\t\t})\n\t})\n\tt.Run(\"method=Valid\", func(t *testing.T) {\n\t\tt.Run(\"case=is invalid when the flow is expired\", func(t *testing.T) {\n\t\t\tf, err := recovery.NewFlow(conf, -time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\ttoken := link.NewSelfServiceRecoveryToken(nil, f, -time.Hour)\n\t\t\trequire.Error(t, token.Valid())\n\t\t\tassert.EqualError(t, token.Valid(), f.Valid().Error())\n\t\t})\n\t})\n}\n\nfunc TestRecoveryTokenType(t *testing.T) {\n\tassert.Equal(t, 1, int(link.RecoveryTokenTypeAdmin))\n\tassert.Equal(t, 2, int(link.RecoveryTokenTypeSelfService))\n}\n"
  },
  {
    "path": "selfservice/strategy/link/token_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/randx\"\n)\n\ntype VerificationToken struct {\n\t// ID represents the tokens's unique ID.\n\t//\n\t// required: true\n\t// type: string\n\t// format: uuid\n\tID uuid.UUID `json:\"id\" db:\"id\" faker:\"-\"`\n\n\t// Token represents the verification token. It can not be longer than 64 chars!\n\tToken string `json:\"-\" db:\"token\"`\n\n\t// VerifiableAddress links this token to a verification address.\n\t// required: true\n\tVerifiableAddress *identity.VerifiableAddress `json:\"verification_address\" belongs_to:\"identity_verifiable_addresses\" fk_id:\"VerificationAddVerifiableAddressIDressID\"`\n\n\t// ExpiresAt is the time (UTC) when the token expires.\n\t// required: true\n\tExpiresAt time.Time `json:\"expires_at\" faker:\"time_type\" db:\"expires_at\"`\n\n\t// IssuedAt is the time (UTC) when the token was issued.\n\t// required: true\n\tIssuedAt time.Time `json:\"issued_at\" faker:\"time_type\" db:\"issued_at\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\t// VerifiableAddressID is a helper struct field for gobuffalo.pop.\n\tVerifiableAddressID uuid.UUID `json:\"-\" faker:\"-\" db:\"identity_verifiable_address_id\"`\n\t// FlowID is a helper struct field for gobuffalo.pop.\n\tFlowID uuid.UUID `json:\"-\" faker:\"-\" db:\"selfservice_verification_flow_id\"`\n\tNID    uuid.UUID `json:\"-\" faker:\"-\" db:\"nid\"`\n}\n\nfunc (VerificationToken) TableName(ctx context.Context) string {\n\treturn \"identity_verification_tokens\"\n}\n\nfunc NewSelfServiceVerificationToken(address *identity.VerifiableAddress, f *verification.Flow, expiresIn time.Duration) *VerificationToken {\n\tnow := time.Now().UTC()\n\treturn &VerificationToken{\n\t\tID:                x.NewUUID(),\n\t\tToken:             randx.MustString(32, randx.AlphaNum),\n\t\tVerifiableAddress: address,\n\t\tExpiresAt:         now.Add(expiresIn),\n\t\tIssuedAt:          now,\n\t\tFlowID:            f.ID,\n\t}\n}\n\nfunc (f *VerificationToken) Valid() error {\n\tif f.ExpiresAt.Before(time.Now().UTC()) {\n\t\treturn errors.WithStack(flow.NewFlowExpiredError(f.ExpiresAt))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/link/token_verification_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage link_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/link\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/stringslice\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/verification\"\n)\n\nfunc TestVerificationToken(t *testing.T) {\n\tconf, _ := pkg.NewFastRegistryWithMocks(t)\n\n\treq := &http.Request{URL: urlx.ParseOrPanic(\"https://www.ory.sh/\")}\n\tt.Run(\"func=NewSelfServiceVerificationToken\", func(t *testing.T) {\n\t\tt.Run(\"case=creates unique tokens\", func(t *testing.T) {\n\t\t\tf, err := verification.NewFlow(conf, time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\ttokens := make([]string, 10)\n\t\t\tfor k := range tokens {\n\t\t\t\ttokens[k] = link.NewSelfServiceVerificationToken(nil, f, time.Hour).Token\n\t\t\t}\n\n\t\t\tassert.Len(t, stringslice.Unique(tokens), len(tokens))\n\t\t})\n\t})\n\tt.Run(\"method=Valid\", func(t *testing.T) {\n\t\tt.Run(\"case=is invalid when the flow is expired\", func(t *testing.T) {\n\t\t\tf, err := verification.NewFlow(conf, -time.Hour, \"\", req, nil, flow.TypeBrowser)\n\t\t\trequire.NoError(t, err)\n\n\t\t\ttoken := link.NewSelfServiceVerificationToken(nil, f, -time.Hour)\n\t\t\trequire.Error(t, token.Valid())\n\t\t\tassert.EqualError(t, token.Valid(), f.Valid().Error())\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/.schema/login.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/lookup/login.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\n    \"lookup_secret\",\n    \"method\"\n  ],\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"lookup_secret\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/.schema/settings.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/lookup/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"lookup_secret_disable\": {\n      \"type\": \"boolean\"\n    },\n    \"lookup_secret_reveal\": {\n      \"type\": \"boolean\"\n    },\n    \"lookup_secret_regenerate\": {\n      \"type\": \"boolean\"\n    },\n    \"lookup_secret_confirm\": {\n      \"type\": \"boolean\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteLogin-case=lookup_payload_is_set_when_identity_has_lookup.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010007,\n        \"text\": \"Backup recovery code\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"lookup_secret\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010010,\n        \"text\": \"Use backup recovery code\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=button_for_regeneration_is_displayed_when_identity_has_no_recovery_codes_yet-case=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_regenerate\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050008,\n        \"text\": \"Generate new backup recovery codes\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=button_for_regeneration_is_displayed_when_identity_has_no_recovery_codes_yet-case=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_regenerate\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050008,\n        \"text\": \"Generate new backup recovery codes\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=button_for_regeneration_is_displayed_when_identity_has_no_recovery_codes_yet-case=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_regenerate\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050008,\n        \"text\": \"Generate new backup recovery codes\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=hide_recovery_codes_behind_reveal_button-case=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_reveal\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050007,\n        \"text\": \"Reveal backup recovery codes\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=hide_recovery_codes_behind_reveal_button-case=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_reveal\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050007,\n        \"text\": \"Reveal backup recovery codes\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=hide_recovery_codes_behind_reveal_button-case=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_reveal\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050007,\n        \"text\": \"Reveal backup recovery codes\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=hide_recovery_codes_behind_reveal_button_and_show_disable_button-case=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_reveal\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050007,\n        \"text\": \"Reveal backup recovery codes\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_disable\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050016,\n        \"text\": \"Disable this method\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=hide_recovery_codes_behind_reveal_button_and_show_disable_button-case=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_reveal\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050007,\n        \"text\": \"Reveal backup recovery codes\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_disable\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050016,\n        \"text\": \"Disable this method\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=hide_recovery_codes_behind_reveal_button_and_show_disable_button-case=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_reveal\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050007,\n        \"text\": \"Reveal backup recovery codes\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"lookup_secret_disable\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"lookup_secret\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050016,\n        \"text\": \"Disable this method\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-case=should_pass_without_csrf_if_API_flow.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"text\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"text\": {\n        \"id\": 1050015,\n        \"text\": \"key-0, used, key-2, key-3, used, key-5, key-6, used, key-8, key-9, used, key-11\",\n        \"type\": \"info\",\n        \"context\": {\n          \"secrets\": [\n            {\n              \"id\": 1050009,\n              \"text\": \"key-0\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-0\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:39 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:39Z\",\n                \"used_at_unix\": 1629199959\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-2\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-2\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-3\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-3\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:42 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:42Z\",\n                \"used_at_unix\": 1629199962\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-5\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-5\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-6\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-6\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:45 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:45Z\",\n                \"used_at_unix\": 1629199965\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-8\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-8\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-9\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-9\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:48 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:48Z\",\n                \"used_at_unix\": 1629199968\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-11\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-11\"\n              }\n            }\n          ]\n        }\n      },\n      \"id\": \"lookup_secret_codes\",\n      \"node_type\": \"text\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050010,\n        \"text\": \"These are your back up recovery codes. Please keep them in a safe place!\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret_disable\",\n      \"type\": \"submit\",\n      \"value\": \"true\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050016,\n        \"text\": \"Disable this method\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret_regenerate\",\n      \"type\": \"submit\",\n      \"value\": \"true\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050008,\n        \"text\": \"Generate new backup recovery codes\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-type=regenerate_but_no_confirmation-type=api.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"text\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"text\": {\n        \"id\": 1050015,\n        \"text\": \"key-0, used, key-2, key-3, used, key-5, key-6, used, key-8, key-9, used, key-11\",\n        \"type\": \"info\",\n        \"context\": {\n          \"secrets\": [\n            {\n              \"id\": 1050009,\n              \"text\": \"key-0\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-0\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:39 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:39Z\",\n                \"used_at_unix\": 1629199959\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-2\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-2\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-3\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-3\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:42 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:42Z\",\n                \"used_at_unix\": 1629199962\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-5\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-5\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-6\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-6\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:45 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:45Z\",\n                \"used_at_unix\": 1629199965\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-8\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-8\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-9\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-9\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:48 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:48Z\",\n                \"used_at_unix\": 1629199968\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-11\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-11\"\n              }\n            }\n          ]\n        }\n      },\n      \"id\": \"lookup_secret_codes\",\n      \"node_type\": \"text\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050010,\n        \"text\": \"These are your back up recovery codes. Please keep them in a safe place!\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret_disable\",\n      \"type\": \"submit\",\n      \"value\": \"true\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050016,\n        \"text\": \"Disable this method\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret_regenerate\",\n      \"type\": \"submit\",\n      \"value\": \"true\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050008,\n        \"text\": \"Generate new backup recovery codes\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-type=regenerate_but_no_confirmation-type=browser.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"text\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"text\": {\n        \"id\": 1050015,\n        \"text\": \"key-0, used, key-2, key-3, used, key-5, key-6, used, key-8, key-9, used, key-11\",\n        \"type\": \"info\",\n        \"context\": {\n          \"secrets\": [\n            {\n              \"id\": 1050009,\n              \"text\": \"key-0\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-0\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:39 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:39Z\",\n                \"used_at_unix\": 1629199959\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-2\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-2\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-3\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-3\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:42 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:42Z\",\n                \"used_at_unix\": 1629199962\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-5\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-5\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-6\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-6\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:45 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:45Z\",\n                \"used_at_unix\": 1629199965\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-8\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-8\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-9\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-9\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:48 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:48Z\",\n                \"used_at_unix\": 1629199968\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-11\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-11\"\n              }\n            }\n          ]\n        }\n      },\n      \"id\": \"lookup_secret_codes\",\n      \"node_type\": \"text\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050010,\n        \"text\": \"These are your back up recovery codes. Please keep them in a safe place!\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret_disable\",\n      \"type\": \"submit\",\n      \"value\": \"true\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050016,\n        \"text\": \"Disable this method\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret_regenerate\",\n      \"type\": \"submit\",\n      \"value\": \"true\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050008,\n        \"text\": \"Generate new backup recovery codes\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestCompleteSettings-type=regenerate_but_no_confirmation-type=spa.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"text\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"text\": {\n        \"id\": 1050015,\n        \"text\": \"key-0, used, key-2, key-3, used, key-5, key-6, used, key-8, key-9, used, key-11\",\n        \"type\": \"info\",\n        \"context\": {\n          \"secrets\": [\n            {\n              \"id\": 1050009,\n              \"text\": \"key-0\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-0\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:39 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:39Z\",\n                \"used_at_unix\": 1629199959\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-2\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-2\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-3\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-3\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:42 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:42Z\",\n                \"used_at_unix\": 1629199962\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-5\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-5\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-6\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-6\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:45 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:45Z\",\n                \"used_at_unix\": 1629199965\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-8\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-8\"\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-9\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-9\"\n              }\n            },\n            {\n              \"id\": 1050014,\n              \"text\": \"Secret was used at 2021-08-17 11:32:48 +0000 UTC\",\n              \"type\": \"info\",\n              \"context\": {\n                \"used_at\": \"2021-08-17T11:32:48Z\",\n                \"used_at_unix\": 1629199968\n              }\n            },\n            {\n              \"id\": 1050009,\n              \"text\": \"key-11\",\n              \"type\": \"info\",\n              \"context\": {\n                \"secret\": \"key-11\"\n              }\n            }\n          ]\n        }\n      },\n      \"id\": \"lookup_secret_codes\",\n      \"node_type\": \"text\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050010,\n        \"text\": \"These are your back up recovery codes. Please keep them in a safe place!\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret_disable\",\n      \"type\": \"submit\",\n      \"value\": \"true\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050016,\n        \"text\": \"Disable this method\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret_regenerate\",\n      \"type\": \"submit\",\n      \"value\": \"true\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050008,\n        \"text\": \"Generate new backup recovery codes\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010007,\n        \"text\": \"Backup recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"lookup_secret\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010010,\n        \"text\": \"Use backup recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactorRefresh.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"lookup_secret\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010007,\n        \"text\": \"Backup recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"lookup_secret\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"lookup_secret\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010010,\n        \"text\": \"Use backup recovery code\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/lookup/login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage lookup\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc (s *Strategy) PopulateLoginMethod(r *http.Request, requestedAAL identity.AuthenticatorAssuranceLevel, sr *login.Flow) error {\n\t// This strategy can only solve AAL2\n\tif requestedAAL != identity.AuthenticatorAssuranceLevel2 {\n\t\treturn nil\n\t}\n\n\t// We have done proper validation before so this should never error\n\tsess, err := s.d.SessionManager().FetchFromRequest(r.Context(), r)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tid, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), sess.IdentityID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, ok := id.GetCredentials(s.ID())\n\tif !ok {\n\t\t// Identity has no lookup codes\n\t\treturn nil\n\t}\n\n\tsr.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tsr.UI.SetNode(node.NewInputField(node.LookupCodeEnter, \"\", node.LookupGroup, node.InputAttributeTypeText, node.WithRequiredInputAttribute).WithMetaLabel(text.NewInfoLoginLookupLabel()))\n\tsr.UI.GetNodes().Append(node.NewInputField(\"method\", s.ID(), node.LookupGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoLoginLookup()))\n\n\treturn nil\n}\n\nfunc (s *Strategy) handleLoginError(r *http.Request, f *login.Flow, err error) error {\n\tif f != nil {\n\t\tf.UI.Nodes.ResetNodes(node.LookupCodeEnter)\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn err\n}\n\n// Update Login Flow with Lookup Secret Method\n//\n// swagger:model updateLoginFlowWithLookupSecretMethod\ntype updateLoginFlowWithLookupSecretMethod struct {\n\t// Method should be set to \"lookup_secret\" when logging in using the lookup_secret strategy.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// The lookup secret.\n\t//\n\t// required: true\n\tCode string `json:\"lookup_secret\"`\n}\n\nfunc (s *Strategy) Login(_ http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (i *identity.Identity, err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.lookup.Strategy.Login\")\n\tdefer otelx.End(span, &err)\n\n\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel2); err != nil {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"requested AAL is not AAL2\"))\n\t\treturn nil, err\n\t}\n\n\tif err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.ID().String(), s.d); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar p updateLoginFlowWithLookupSecretMethod\n\tif err := decoderx.Decode(r, &p,\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.MustHTTPRawJSONSchemaCompiler(loginSchema),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\ti, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), sess.IdentityID.String())\n\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoLookupDefined()))\n\t} else if err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tvar o identity.CredentialsLookupConfig\n\tif err := json.Unmarshal(c.Config, &o); err != nil {\n\t\treturn nil, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"The lookup secrets could not be decoded properly\").WithDebug(err.Error()).WithWrap(err)), i.ID)\n\t}\n\n\tvar found bool\n\tfor k, rc := range o.RecoveryCodes {\n\t\tif rc.Code == p.Code {\n\t\t\tif time.Time(rc.UsedAt).IsZero() {\n\t\t\t\to.RecoveryCodes[k].UsedAt = sqlxx.NullTime(time.Now().UTC().Round(time.Second))\n\t\t\t\tfound = true\n\t\t\t} else {\n\t\t\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(schema.NewLookupAlreadyUsed()), i.ID))\n\t\t\t}\n\t\t}\n\t}\n\n\tif !found {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(schema.NewErrorValidationLookupInvalid()), i.ID))\n\t}\n\n\t// We can't use a transaction here because HydrateIdentityAssociations (used by update) does not support transactions.\n\ttoUpdate, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, sess.IdentityID)\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(err, i.ID))\n\t}\n\n\tencoded, err := json.Marshal(&o)\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Unable to encode updated lookup secrets.\").WithDebug(err.Error())), i.ID))\n\t}\n\n\tc.Config = encoded\n\ttoUpdate.SetCredentials(s.ID(), *c)\n\n\t// We can't use a transaction here because HydrateIdentityAssociations (used by update) does not support transactions.\n\tif err := s.d.IdentityManager().Update(ctx, toUpdate,\n\t\t// We need to allow write protected traits because we are updating the lookup secrets.\n\t\tidentity.ManagerAllowWriteProtectedTraits,\n\t); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Unable to update identity.\").WithDebug(err.Error())), i.ID))\n\t}\n\n\tf.Active = s.ID()\n\tif err = s.d.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Could not update flow.\").WithDebug(err.Error())), i.ID))\n\t}\n\n\treturn i, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/login_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage lookup_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\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\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\nvar lookupCodeGJSONQuery = \"ui.nodes.#(attributes.name==\" + identity.CredentialsTypeLookup.String() + \")\"\n\nfunc TestCompleteLogin(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, false)),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeLookup, true)),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/login.schema.json\")),\n\t)\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tuiTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\n\tt.Run(\"case=lookup payload is set when identity has lookup\", func(t *testing.T) {\n\t\tid, _ := createIdentity(t, reg)\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\"0.attributes.value\"})\n\t})\n\n\tt.Run(\"case=lookup payload is not set when identity has no lookup\", func(t *testing.T) {\n\t\tid := createIdentityWithoutLookup(t, reg)\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\tassertx.EqualAsJSON(t, nil, f.Ui.Nodes)\n\t})\n\n\tt.Run(\"case=lookup payload is not set when identity has no lookup\", func(t *testing.T) {\n\t\tid := createIdentityWithoutLookup(t, reg)\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\tassertx.EqualAsJSON(t, nil, f.Ui.Nodes)\n\t})\n\n\tdoAPIFlowWithClient := func(t *testing.T, v func(url.Values), id *identity.Identity, apiClient *http.Client, forced bool) (string, *http.Response) {\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, forced, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", identity.CredentialsTypeLookup.String())\n\t\tv(values)\n\t\tpayload := testhelpers.EncodeFormAsJSON(t, true, values)\n\t\treturn testhelpers.LoginMakeRequest(t, true, false, f, apiClient, payload)\n\t}\n\n\tdoAPIFlow := func(t *testing.T, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\treturn doAPIFlowWithClient(t, v, id, apiClient, false)\n\t}\n\n\tdoBrowserFlowWithClient := func(t *testing.T, spa bool, v func(url.Values), id *identity.Identity, browserClient *http.Client, forced bool) (string, *http.Response) {\n\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, forced, spa, false, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", identity.CredentialsTypeLookup.String())\n\t\tv(values)\n\t\treturn testhelpers.LoginMakeRequest(t, false, spa, f, browserClient, values.Encode())\n\t}\n\n\tdoBrowserFlow := func(t *testing.T, spa bool, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\t\treturn doBrowserFlowWithClient(t, spa, v, id, browserClient, false)\n\t}\n\n\tcheckURL := func(t *testing.T, shouldRedirect bool, res *http.Response) {\n\t\tif shouldRedirect {\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login-ts\")\n\t\t} else {\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t}\n\t}\n\n\tt.Run(\"case=should fail if code is invalid\", func(t *testing.T) {\n\t\tfor _, tc := range []struct {\n\t\t\td       string\n\t\t\tcode    string\n\t\t\tmessage string\n\t\t\tkey     string\n\t\t}{\n\t\t\t{d: \"empty\", key: lookupCodeGJSONQuery + \".messages.0.text\", message: \"Property lookup_secret is missing.\"},\n\t\t\t{d: \"invalid\", key: \"ui.messages.0.text\", code: \"invalid\", message: text.NewErrorValidationLookupInvalid().Text},\n\t\t\t{d: \"already-used\", key: \"ui.messages.0.text\", code: \"key-1\", message: text.NewErrorValidationLookupAlreadyUsed().Text},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"code=%s\", tc.d), func(t *testing.T) {\n\t\t\t\tid, _ := createIdentity(t, reg)\n\t\t\t\tpayload := func(v url.Values) {\n\t\t\t\t\tv.Set(node.LookupCodeEnter, tc.code)\n\t\t\t\t}\n\n\t\t\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, tc.message, gjson.Get(body, tc.key).String(), \"%s\", body)\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\t\tbody, res := doAPIFlow(t, payload, id)\n\t\t\t\t\tcheck(t, false, body, res)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\tbody, res := doBrowserFlow(t, false, payload, id)\n\t\t\t\t\tcheck(t, true, body, res)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\t\tbody, res := doBrowserFlow(t, true, payload, id)\n\t\t\t\t\tcheck(t, false, body, res)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail if lookup was not set up for identity\", func(t *testing.T) {\n\t\tid := createIdentityWithoutLookup(t, reg)\n\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(node.LookupCodeEnter, \"1111111\")\n\t\t}\n\n\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, text.NewErrorValidationNoLookup().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tbody, res := doAPIFlow(t, payload, id)\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, payload, id)\n\t\t\tcheck(t, true, body, res)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, payload, id)\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\t})\n\n\tt.Run(\"case=should pass when code is supplied correctly\", func(t *testing.T) {\n\t\tid, _ := createIdentity(t, reg)\n\t\tpayload := func(code string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(node.LookupCodeEnter, code)\n\t\t\t}\n\t\t}\n\n\t\tstartAt := time.Now()\n\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response, usedKey string, expectedAuths int) {\n\t\t\tprefix := \"session.\"\n\t\t\tif shouldRedirect {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), redirTS.URL+\"/return-ts\")\n\t\t\t\tprefix = \"\"\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\t}\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"active\").Bool(), \"%s\", body)\n\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel2, gjson.Get(body, prefix+\"authenticator_assurance_level\").String())\n\t\t\trequire.Len(t, gjson.Get(body, prefix+\"authentication_methods\").Array(), expectedAuths)\n\t\t\tassert.EqualValues(t, identity.CredentialsTypePassword, gjson.Get(body, prefix+\"authentication_methods.0.method\").String())\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"authentication_methods.0.completed_at\").Time().After(startAt))\n\t\t\tassert.EqualValues(t, identity.CredentialsTypeLookup, gjson.Get(body, prefix+\"authentication_methods.1.method\").String())\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"authentication_methods.1.completed_at\").Time().After(startAt))\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"authentication_methods.1.completed_at\").Time().After(gjson.Get(body, prefix+\"authentication_methods.0.completed_at\").Time()))\n\t\t\tif expectedAuths == 3 {\n\t\t\t\tassert.EqualValues(t, identity.CredentialsTypeLookup, gjson.Get(body, prefix+\"authentication_methods.2.method\").String())\n\t\t\t\tassert.True(t, gjson.Get(body, prefix+\"authentication_methods.2.completed_at\").Time().After(startAt))\n\t\t\t\tassert.True(t, gjson.Get(body, prefix+\"authentication_methods.2.completed_at\").Time().After(gjson.Get(body, prefix+\"authentication_methods.1.completed_at\").Time()))\n\t\t\t\tassert.False(t, gjson.Get(body, prefix+\"authenticated_at\").Time().Before(gjson.Get(body, prefix+\"authentication_methods.2.completed_at\").Time()))\n\t\t\t} else {\n\t\t\t\tassert.False(t, gjson.Get(body, prefix+\"authenticated_at\").Time().Before(gjson.Get(body, prefix+\"authentication_methods.1.completed_at\").Time()))\n\t\t\t}\n\n\t\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), uuid.FromStringOrNil(gjson.Get(body, prefix+\"identity.id\").String()))\n\t\t\trequire.NoError(t, err)\n\t\t\tcreds, ok := actual.GetCredentials(identity.CredentialsTypeLookup)\n\t\t\trequire.True(t, ok)\n\n\t\t\tvar conf identity.CredentialsLookupConfig\n\t\t\trequire.NoError(t, json.Unmarshal(creds.Config, &conf))\n\n\t\t\tvar found bool\n\t\t\tfor _, rc := range conf.RecoveryCodes {\n\t\t\t\tif rc.Code == usedKey {\n\t\t\t\t\tfound = true\n\t\t\t\t\trequire.False(t, time.Time(rc.UsedAt).IsZero())\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trequire.True(t, found)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\t\tbody, res := doAPIFlowWithClient(t, payload(\"key-0\"), id, apiClient, false)\n\t\t\tcheck(t, false, body, res, \"key-0\", 2)\n\t\t\t// We can still use another key\n\t\t\tbody, res = doAPIFlowWithClient(t, payload(\"key-2\"), id, apiClient, true)\n\t\t\tcheck(t, false, body, res, \"key-2\", 3)\n\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\t\t\tbody, res := doBrowserFlowWithClient(t, false, payload(\"key-3\"), id, browserClient, false)\n\t\t\tcheck(t, true, body, res, \"key-3\", 2)\n\t\t\t// We can still use another key\n\t\t\tbody, res = doBrowserFlowWithClient(t, false, payload(\"key-5\"), id, browserClient, true)\n\t\t\tcheck(t, true, body, res, \"key-5\", 3)\n\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\t\t\tbody, res := doBrowserFlowWithClient(t, true, payload(\"key-6\"), id, browserClient, false)\n\t\t\tcheck(t, false, body, res, \"key-6\", 2)\n\t\t\t// We can still use another key\n\t\t\tbody, res = doBrowserFlowWithClient(t, true, payload(\"key-8\"), id, browserClient, true)\n\t\t\tcheck(t, false, body, res, \"key-8\", 3)\n\n\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(body, \"continue_with.0.action\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"continue_with.0.redirect_browser_to\").String(), conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail because lookup can not handle AAL1\", func(t *testing.T) {\n\t\tapiClient := testhelpers.NewDebugClient(t)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(ctx, t, apiClient, publicTS, false)\n\n\t\tupdate, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\trequire.NoError(t, err)\n\t\tupdate.RequestedAAL = identity.AuthenticatorAssuranceLevel1\n\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), update))\n\n\t\treq, err := http.NewRequest(\"POST\", f.Ui.Action, bytes.NewBufferString(`{\"method\":\"lookup\"}`))\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tres, err := http.DefaultClient.Do(req)\n\t\trequire.NoError(t, err)\n\t\tbody := x.MustReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\tassert.Equal(t, text.NewErrorValidationLoginNoStrategyFound().Text, gjson.GetBytes(body, \"ui.messages.0.text\").String())\n\t})\n\n\tt.Run(\"case=should pass without csrf if API flow\", func(t *testing.T) {\n\t\tid, _ := createIdentity(t, reg)\n\t\tbody, res := doAPIFlow(t, func(v url.Values) {\n\t\t\tv.Del(\"csrf_token\")\n\t\t\tv.Set(node.LookupCodeEnter, \"111111\")\n\t\t}, id)\n\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\tassert.Equal(t, text.NewErrorValidationLookupInvalid().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=should fail if CSRF token is invalid\", func(t *testing.T) {\n\t\tid, _ := createIdentity(t, reg)\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(node.LookupCodeEnter, \"111111\")\n\t\t\t}, id)\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"reason\").String(), body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(node.LookupCodeEnter, \"111111\")\n\t\t\t}, id)\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"error.reason\").String(), body)\n\t\t})\n\t})\n}\n\nfunc TestFormHydration(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeLookup)+\".enabled\", true)\n\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/login.schema.json\")\n\n\ts, err := reg.AllLoginStrategies().Strategy(identity.CredentialsTypeLookup)\n\trequire.NoError(t, err)\n\tfh, ok := s.(login.AAL2FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f *login.Flow) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.UI.Nodes.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f.UI.Nodes)\n\t}\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *login.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := login.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateLoginMethodSecondFactor\", func(t *testing.T) {\n\t\tid, _ := createIdentity(t, reg)\n\t\theaders := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\tr, f := newFlow(ctx, t)\n\n\t\tr.Header = headers\n\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\n\t\trequire.NoError(t, fh.PopulateLoginMethodSecondFactor(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodSecondFactorRefresh\", func(t *testing.T) {\n\t\tid, _ := createIdentity(t, reg)\n\t\theaders := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\tr, f := newFlow(ctx, t)\n\n\t\tr.Header = headers\n\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\n\t\trequire.NoError(t, fh.PopulateLoginMethodSecondFactorRefresh(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/nodes.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage lookup\n\nimport (\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc NewRevealLookupNode() *node.Node {\n\treturn node.NewInputField(node.LookupReveal, \"true\", node.LookupGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoSelfServiceSettingsRevealLookup())\n}\n\nfunc NewRegenerateLookupNode() *node.Node {\n\treturn node.NewInputField(\n\t\tnode.LookupRegenerate, \"true\", node.LookupGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoSelfServiceSettingsRegenerateLookup())\n}\n\nfunc NewDisableLookupNode() *node.Node {\n\treturn node.NewInputField(node.LookupDisable, \"true\", node.LookupGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoSelfServiceSettingsDisableLookup())\n}\n\nfunc NewConfirmLookupNode() *node.Node {\n\treturn node.NewInputField(node.LookupConfirm, \"true\", node.LookupGroup, node.InputAttributeTypeSubmit).\n\t\tWithMetaLabel(text.NewInfoSelfServiceSettingsLookupConfirm())\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage lookup\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed .schema/login.schema.json\nvar loginSchema []byte\n\n//go:embed .schema/settings.schema.json\nvar settingsSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/lookup/settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage lookup\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/x/randx\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/ory/kratos/session\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n)\n\nfunc (s *Strategy) SettingsStrategyID() string {\n\treturn identity.CredentialsTypeLookup.String()\n}\n\nconst (\n\tinternalContextKeyRevealed    = \"revealed\"\n\tInternalContextKeyRegenerated = \"regenerated\"\n)\n\nconst numCodes = 12\n\nvar allSettingsNodes = []string{\n\tnode.LookupRegenerate,\n\tnode.LookupReveal,\n\tnode.LookupRegenerate,\n\tnode.LookupDisable,\n\tnode.LookupCodes,\n\tnode.LookupConfirm,\n}\n\n// Update Settings Flow with Lookup Method\n//\n// swagger:model updateSettingsFlowWithLookupMethod\ntype updateSettingsFlowWithLookupMethod struct {\n\t// If set to true will reveal the lookup secrets\n\tRevealLookup bool `json:\"lookup_secret_reveal\"`\n\n\t// If set to true will regenerate the lookup secrets\n\tRegenerateLookup bool `json:\"lookup_secret_regenerate\"`\n\n\t// If set to true will save the regenerated lookup secrets\n\tConfirmLookup bool `json:\"lookup_secret_confirm\"`\n\n\t// Disables this method if true.\n\tDisableLookup bool `json:\"lookup_secret_disable\"`\n\n\t// CSRFToken is the anti-CSRF token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Method\n\t//\n\t// Should be set to \"lookup\" when trying to add, update, or remove a lookup pairing.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Flow is flow ID.\n\t//\n\t// swagger:ignore\n\tFlow string `json:\"flow\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (p *updateSettingsFlowWithLookupMethod) GetFlowID() uuid.UUID {\n\treturn x.ParseUUID(p.Flow)\n}\n\nfunc (p *updateSettingsFlowWithLookupMethod) SetFlowID(rid uuid.UUID) {\n\tp.Flow = rid.String()\n}\n\nfunc (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.lookup.Strategy.Settings\")\n\tdefer otelx.End(span, &err)\n\n\tvar p updateSettingsFlowWithLookupMethod\n\tctxUpdate, err := settings.PrepareUpdate(s.d, w, r, f, ss, settings.ContinuityKey(s.SettingsStrategyID()), &p)\n\tif errors.Is(err, settings.ErrContinuePreviousAction) {\n\t\treturn ctxUpdate, s.continueSettingsFlow(ctx, r, ctxUpdate, p)\n\t} else if err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err)\n\t}\n\n\tif err := s.decodeSettingsFlow(r, &p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err)\n\t}\n\n\tif p.RegenerateLookup || p.RevealLookup || p.ConfirmLookup || p.DisableLookup {\n\t\t// This method has only two submit buttons\n\t\tp.Method = s.SettingsStrategyID()\n\t\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\t\treturn nil, s.handleSettingsError(w, r, ctxUpdate, p, err)\n\t\t}\n\t} else {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"neither reveal, regenerate, confirm, nor disable was set\"))\n\t\treturn nil, errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\t// This does not come from the payload!\n\tp.Flow = ctxUpdate.Flow.ID.String()\n\tif err := s.continueSettingsFlow(ctx, r, ctxUpdate, p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(w, r, ctxUpdate, p, err)\n\t}\n\n\treturn ctxUpdate, nil\n}\n\nfunc (s *Strategy) decodeSettingsFlow(r *http.Request, dest interface{}) error {\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(settingsSchema)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn decoderx.Decode(r, dest, compiler,\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t)\n}\n\nfunc (s *Strategy) continueSettingsFlow(ctx context.Context, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithLookupMethod) error {\n\tif p.ConfirmLookup || p.RevealLookup || p.RegenerateLookup || p.DisableLookup {\n\t\tif err := flow.MethodEnabledAndAllowed(ctx, flow.SettingsFlow, s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := flow.EnsureCSRF(s.d, r, ctxUpdate.Flow.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) {\n\t\t\treturn errors.WithStack(settings.NewFlowNeedsReAuth())\n\t\t}\n\t} else {\n\t\treturn errors.New(\"ended up in unexpected state\")\n\t}\n\n\tif p.ConfirmLookup {\n\t\treturn s.continueSettingsFlowConfirm(ctx, ctxUpdate)\n\t} else if p.RevealLookup {\n\t\tif err := s.continueSettingsFlowReveal(ctx, ctxUpdate); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn flow.ErrStrategyAsksToReturnToUI\n\t} else if p.DisableLookup {\n\t\treturn s.continueSettingsFlowDisable(ctx, ctxUpdate)\n\t} else if p.RegenerateLookup {\n\t\tif err := s.continueSettingsFlowRegenerate(ctx, ctxUpdate); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// regen\n\t\treturn flow.ErrStrategyAsksToReturnToUI\n\t}\n\n\treturn errors.New(\"ended up in unexpected state\")\n}\n\nfunc (s *Strategy) continueSettingsFlowDisable(ctx context.Context, ctxUpdate *settings.UpdateContext) error {\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.Identity.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ti.DeleteCredentialsType(s.ID())\n\n\tfor _, n := range allSettingsNodes {\n\t\tctxUpdate.Flow.UI.Nodes.Remove(n)\n\t}\n\n\tctxUpdate.Flow.UI.Nodes.Upsert(NewRegenerateLookupNode())\n\tctxUpdate.Flow.InternalContext, err = sjson.SetBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), internalContextKeyRevealed), false)\n\tif err != nil {\n\t\treturn err\n\t}\n\tctxUpdate.Flow.InternalContext, err = sjson.SetRawBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeyRegenerated), []byte(\"{}\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, ctxUpdate.Flow); err != nil {\n\t\treturn err\n\t}\n\n\tctxUpdate.UpdateIdentity(i)\n\treturn nil\n}\n\nfunc (s *Strategy) continueSettingsFlowReveal(ctx context.Context, ctxUpdate *settings.UpdateContext) error {\n\thasLookup, err := s.identityHasLookup(ctx, ctxUpdate.Session.Identity)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !hasLookup {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Can not reveal lookup codes because you have none.\"))\n\t}\n\n\t_, cred, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), ctxUpdate.Session.IdentityID.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar creds identity.CredentialsLookupConfig\n\tif err := json.Unmarshal(cred.Config, &creds); err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode lookup codes from JSON.\").WithDebug(err.Error()))\n\t}\n\n\tfor _, n := range allSettingsNodes {\n\t\tctxUpdate.Flow.UI.Nodes.Remove(n)\n\t}\n\n\tctxUpdate.Flow.UI.Nodes.Upsert(creds.ToNode())\n\tctxUpdate.Flow.UI.Nodes.Upsert(NewDisableLookupNode())\n\tctxUpdate.Flow.UI.Nodes.Upsert(NewRegenerateLookupNode())\n\n\tctxUpdate.Flow.InternalContext, err = sjson.SetBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), internalContextKeyRevealed), true)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, ctxUpdate.Flow); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) continueSettingsFlowRegenerate(ctx context.Context, ctxUpdate *settings.UpdateContext) error {\n\tcodes := make([]identity.RecoveryCode, numCodes)\n\tfor k := range codes {\n\t\tcodes[k] = identity.RecoveryCode{Code: randx.MustString(8, randx.AlphaLowerNum)}\n\t}\n\n\tfor _, n := range allSettingsNodes {\n\t\tctxUpdate.Flow.UI.Nodes.Remove(n)\n\t}\n\n\tctxUpdate.Flow.UI.Nodes.Upsert((&identity.CredentialsLookupConfig{RecoveryCodes: codes}).ToNode())\n\tctxUpdate.Flow.UI.Nodes.Upsert(NewConfirmLookupNode())\n\n\tvar err error\n\tctxUpdate.Flow.InternalContext, err = sjson.SetBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeyRegenerated), codes)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, ctxUpdate.Flow); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) continueSettingsFlowConfirm(ctx context.Context, ctxUpdate *settings.UpdateContext) error {\n\tcodes := gjson.GetBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeyRegenerated)).Array()\n\tif len(codes) != numCodes {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"You must (re-)generate recovery backup codes before you can save them.\"))\n\t}\n\n\trc := make([]identity.RecoveryCode, len(codes))\n\tfor k := range rc {\n\t\trc[k] = identity.RecoveryCode{Code: codes[k].Get(\"code\").String()}\n\t}\n\n\tco, err := json.Marshal(&identity.CredentialsLookupConfig{RecoveryCodes: rc})\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode totp options to JSON: %s\", err))\n\t}\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.Identity.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// We do not really need the identifier, so we add the identity's ID\n\tc := &identity.Credentials{Type: s.ID(), Identifiers: []string{i.ID.String()}, Config: co}\n\tc.Config = co\n\ti.SetCredentials(s.ID(), *c)\n\n\t// Remove the TOTP URL from the internal context now that it is set!\n\tctxUpdate.Flow.InternalContext, err = sjson.DeleteBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeyRegenerated))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, ctxUpdate.Flow); err != nil {\n\t\treturn err\n\t}\n\n\t// Since we added the method, it also means that we have authenticated it\n\tif err := s.d.SessionManager().SessionAddAuthenticationMethods(ctx, ctxUpdate.Session.ID, session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    identity.AuthenticatorAssuranceLevel2,\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\tctxUpdate.UpdateIdentity(i)\n\treturn nil\n}\n\nfunc (s *Strategy) identityHasLookup(ctx context.Context, id *identity.Identity) (bool, error) {\n\tif len(id.Credentials) == 0 {\n\t\tif err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, id, identity.ExpandCredentials); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tcount, err := s.CountActiveMultiFactorCredentials(ctx, id.Credentials)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn count > 0, nil\n}\n\nfunc (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.lookup.Strategy.PopulateSettingsMethod\")\n\tdefer otelx.End(span, &err)\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\n\thasLookup, err := s.identityHasLookup(ctx, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif hasLookup {\n\t\tf.UI.Nodes.Upsert(NewRevealLookupNode())\n\t\tf.UI.Nodes.Upsert(NewDisableLookupNode())\n\t} else {\n\t\tf.UI.Nodes.Upsert(NewRegenerateLookupNode())\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithLookupMethod, err error) error {\n\t// Do not pause flow if the flow type is an API flow as we can't save cookies in those flows.\n\tif e := new(settings.FlowNeedsReAuth); errors.As(err, &e) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser {\n\t\tif err := s.d.ContinuityManager().Pause(r.Context(), w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif ctxUpdate.Flow != nil {\n\t\tctxUpdate.Flow.UI.ResetMessages()\n\t\tctxUpdate.Flow.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/settings_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage lookup_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\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\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/snapshotx\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/strategy/lookup\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc createIdentityWithoutLookup(t *testing.T, reg driver.Registry) *identity.Identity {\n\tid, _ := createIdentity(t, reg)\n\tdelete(id.Credentials, identity.CredentialsTypeLookup)\n\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(context.Background(), id))\n\treturn id\n}\n\nfunc createIdentity(t *testing.T, reg driver.Registry) (*identity.Identity, []identity.RecoveryCode) {\n\tcodes := make([]identity.RecoveryCode, 12)\n\tfor k := range codes {\n\t\tvar usedAt sqlxx.NullTime\n\t\tif k%3 == 1 {\n\t\t\tusedAt = sqlxx.NullTime(time.Unix(int64(1629199958+k), 0))\n\t\t}\n\t\tcodes[k] = identity.RecoveryCode{Code: fmt.Sprintf(\"key-%d\", k), UsedAt: usedAt}\n\t}\n\tidentifier := x.NewUUID().String() + \"@ory.sh\"\n\tpassword := x.NewUUID().String()\n\tp, err := reg.Hasher(context.Background()).Generate(context.Background(), []byte(password))\n\trequire.NoError(t, err)\n\ti := &identity.Identity{\n\t\tTraits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, identifier)),\n\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t{\n\t\t\t\tValue:     identifier,\n\t\t\t\tVerified:  false,\n\t\t\t\tCreatedAt: time.Now(),\n\t\t\t},\n\t\t},\n\t}\n\n\trc, err := json.Marshal(&identity.CredentialsLookupConfig{RecoveryCodes: codes})\n\trequire.NoError(t, err)\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\ti.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\tidentity.CredentialsTypePassword: {\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{identifier},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t},\n\t\tidentity.CredentialsTypeLookup: {\n\t\t\tType:        identity.CredentialsTypeLookup,\n\t\t\tIdentifiers: []string{i.ID.String()},\n\t\t\tConfig:      rc,\n\t\t},\n\t}\n\n\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(context.Background(), i))\n\treturn i, codes\n}\n\nfunc TestCompleteSettings(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, false)),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(\"profile\", false)),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeLookup, true)),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/login.schema.json\")),\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceSettingsRequiredAAL:                   \"aal1\",\n\t\t\tconfig.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter: \"1m\",\n\t\t}),\n\t)\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tuiTS := testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewRedirSessionEchoTS(t, reg)\n\tloginTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\n\tdoAPIFlow := func(t *testing.T, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tv(values)\n\t\tpayload := testhelpers.EncodeFormAsJSON(t, true, values)\n\t\treturn testhelpers.SettingsMakeRequest(t, true, false, f, apiClient, payload)\n\t}\n\n\tdoBrowserFlow := func(t *testing.T, spa bool, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, spa, publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tv(values)\n\t\treturn testhelpers.SettingsMakeRequest(t, false, spa, f, browserClient, testhelpers.EncodeFormAsJSON(t, spa, values))\n\t}\n\n\tt.Run(\"case=hide recovery codes behind reveal button and show disable button\", func(t *testing.T) {\n\t\tid, _ := createIdentity(t, reg)\n\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\n\t\tt.Run(\"case=spa\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, true, publicTS)\n\t\t\tsnapshotx.SnapshotT(t, f.Ui.Nodes, snapshotx.ExceptPaths(\"0.attributes.value\"))\n\t\t})\n\n\t\tt.Run(\"case=browser\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, false, publicTS)\n\t\t\tsnapshotx.SnapshotT(t, f.Ui.Nodes, snapshotx.ExceptPaths(\"0.attributes.value\"))\n\t\t})\n\n\t\tt.Run(\"case=api\", func(t *testing.T) {\n\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\t\tsnapshotx.SnapshotT(t, f.Ui.Nodes, snapshotx.ExceptPaths(\"0.attributes.value\"))\n\t\t})\n\t})\n\n\tt.Run(\"case=button for regeneration is displayed when identity has no recovery codes yet\", func(t *testing.T) {\n\t\tid := createIdentityWithoutLookup(t, reg)\n\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\n\t\tt.Run(\"case=spa\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, true, publicTS)\n\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\t})\n\n\t\tt.Run(\"case=browser\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, false, publicTS)\n\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\t})\n\n\t\tt.Run(\"case=api\", func(t *testing.T) {\n\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\t})\n\t})\n\n\tt.Run(\"case=should pass without csrf if API flow\", func(t *testing.T) {\n\t\tid, _ := createIdentity(t, reg)\n\n\t\tbody, res := doAPIFlow(t, func(v url.Values) {\n\t\t\tv.Del(\"csrf_token\")\n\t\t\tv.Set(node.LookupReveal, \"true\")\n\t\t}, id)\n\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes\").Raw), []string{\"0.attributes.value\"})\n\t})\n\n\tt.Run(\"case=should fail if CSRF token is invalid\", func(t *testing.T) {\n\t\tid := createIdentityWithoutLookup(t, reg)\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(node.LookupReveal, \"true\")\n\t\t\t}, id)\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"reason\").String(), body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(node.LookupReveal, \"true\")\n\t\t\t}, id)\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"error.reason\").String(), body)\n\t\t})\n\t})\n\n\tt.Run(\"type=can not reveal or regenerate or remove without privileged session\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"1ns\")\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"5m\")\n\t\t})\n\n\t\tid, codes := createIdentity(t, reg)\n\n\t\tcheckIdentity := func(t *testing.T) {\n\t\t\t_, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeLookup, id.ID.String())\n\t\t\trequire.NoError(t, err)\n\t\t\tassertx.EqualAsJSON(t, codes, json.RawMessage(gjson.GetBytes(cred.Config, \"recovery_codes\").Raw))\n\t\t}\n\n\t\tfor _, tc := range []struct {\n\t\t\tv func(v url.Values)\n\t\t\td string\n\t\t}{\n\t\t\t{\n\t\t\t\td: \"reveal\",\n\t\t\t\tv: func(v url.Values) {\n\t\t\t\t\tv.Set(node.LookupReveal, \"true\")\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\td: \"regenerate\",\n\t\t\t\tv: func(v url.Values) {\n\t\t\t\t\tv.Set(node.LookupRegenerate, \"true\")\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\td: \"disable\",\n\t\t\t\tv: func(v url.Values) {\n\t\t\t\t\tv.Set(node.LookupDisable, \"true\")\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"case=\"+tc.d, func(t *testing.T) {\n\t\t\t\tpayload := tc.v\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\t\tactual, res := doAPIFlow(t, payload, id)\n\t\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"redirect_browser_to\").String(), publicTS.URL+\"/self-service/login/browser?refresh=true&return_to=\")\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(actual), []string{\"redirect_browser_to\"})\n\t\t\t\t\tcheckIdentity(t)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\t\tactual, res := doBrowserFlow(t, true, payload, id)\n\t\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"redirect_browser_to\").String(), publicTS.URL+\"/self-service/login/browser?refresh=true&return_to=\")\n\t\t\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(actual), []string{\"redirect_browser_to\"})\n\t\t\t\t\tcheckIdentity(t)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\tactual, res := doBrowserFlow(t, false, payload, id)\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL+\"/login-ts\")\n\t\t\t\t\tassertx.EqualAsJSON(t, text.NewInfoLoginReAuth().Text, json.RawMessage(gjson.Get(actual, \"ui.messages.0.text\").Raw), actual)\n\t\t\t\t\tcheckIdentity(t)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"type=can not confirm without regenerate\", func(t *testing.T) {\n\t\tid, codes := createIdentity(t, reg)\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(node.LookupConfirm, \"true\")\n\t\t}\n\n\t\tconst reason = \"You must (re-)generate recovery backup codes before you can save them.\"\n\n\t\tcheckIdentity := func(t *testing.T) {\n\t\t\t_, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeLookup, id.ID.String())\n\t\t\trequire.NoError(t, err)\n\t\t\tassertx.EqualAsJSON(t, codes, json.RawMessage(gjson.GetBytes(cred.Config, \"recovery_codes\").Raw))\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual, res := doAPIFlow(t, payload, id)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassert.EqualValues(t, reason, gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\tcheckIdentity(t)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tactual, res := doBrowserFlow(t, true, payload, id)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassert.EqualValues(t, reason, gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\tcheckIdentity(t)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tactual, res := doBrowserFlow(t, false, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\tassert.EqualValues(t, reason, gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\tcheckIdentity(t)\n\t\t})\n\t})\n\n\tt.Run(\"type=regenerate but no confirmation\", func(t *testing.T) {\n\t\tid, codes := createIdentity(t, reg)\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(node.LookupRegenerate, \"true\")\n\t\t}\n\n\t\tcheckIdentity := func(t *testing.T) {\n\t\t\t_, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeLookup, id.ID.String())\n\t\t\trequire.NoError(t, err)\n\t\t\tassertx.EqualAsJSON(t, codes, json.RawMessage(gjson.GetBytes(cred.Config, \"recovery_codes\").Raw))\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual, res := doAPIFlow(t, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(actual, \"ui.nodes\").Raw), []string{\"0.attributes.value\"})\n\t\t\tcheckIdentity(t)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tactual, res := doBrowserFlow(t, true, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(actual, \"ui.nodes\").Raw), []string{\"0.attributes.value\"})\n\t\t\tcheckIdentity(t)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tactual, res := doBrowserFlow(t, false, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\ttesthelpers.SnapshotTExcept(t, json.RawMessage(gjson.Get(actual, \"ui.nodes\").Raw), []string{\"0.attributes.value\"})\n\t\t\tcheckIdentity(t)\n\t\t})\n\t})\n\n\tt.Run(\"type=regenerate with confirmation\", func(t *testing.T) {\n\t\tfor _, tc := range []struct {\n\t\t\td string\n\t\t\tc func(t *testing.T) *identity.Identity\n\t\t}{\n\t\t\t{\n\t\t\t\td: \"with\",\n\t\t\t\tc: func(t *testing.T) *identity.Identity {\n\t\t\t\t\ti, _ := createIdentity(t, reg)\n\t\t\t\t\treturn i\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\td: \"without\",\n\t\t\t\tc: func(t *testing.T) *identity.Identity {\n\t\t\t\t\treturn createIdentityWithoutLookup(t, reg)\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"credentials=\"+tc.d, func(t *testing.T) {\n\t\t\t\tpayload := func(v url.Values) {\n\t\t\t\t\tv.Del(node.LookupReveal)\n\t\t\t\t\tv.Del(node.LookupDisable)\n\t\t\t\t\tv.Set(node.LookupRegenerate, \"true\")\n\t\t\t\t}\n\n\t\t\t\tpayloadConfirm := func(v url.Values) {\n\t\t\t\t\tv.Del(node.LookupRegenerate)\n\t\t\t\t\tv.Del(node.LookupDisable)\n\t\t\t\t\tv.Del(node.LookupReveal)\n\t\t\t\t\tv.Set(node.LookupConfirm, \"true\")\n\t\t\t\t}\n\n\t\t\t\tcheckIdentity := func(t *testing.T, id *identity.Identity, f *kratos.SettingsFlow) {\n\t\t\t\t\t_, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeLookup, id.ID.String())\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.NotContains(t, gjson.GetBytes(cred.Config, \"recovery_codes\").Raw, \"key-1\")\n\t\t\t\t\tassert.NotContains(t, gjson.GetBytes(cred.Config, \"recovery_codes\").Raw, \"key-0\")\n\n\t\t\t\t\tactualFlow, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Empty(t, gjson.GetBytes(actualFlow.InternalContext, flow.PrefixInternalContextKey(identity.CredentialsTypeLookup, lookup.InternalContextKeyRegenerated)))\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\t\tid, _ := createIdentity(t, reg)\n\t\t\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\t\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\n\t\t\t\t\tpayload(values)\n\t\t\t\t\t_, _ = testhelpers.SettingsMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\n\t\t\t\t\tpayloadConfirm(values)\n\t\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\t\tassert.EqualValues(t, flow.StateSuccess, json.RawMessage(gjson.Get(actual, \"state\").String()))\n\n\t\t\t\t\tcheckIdentity(t, id, f)\n\t\t\t\t\ttesthelpers.EnsureAAL(t, apiClient, publicTS, \"aal2\", string(identity.CredentialsTypeLookup))\n\t\t\t\t})\n\n\t\t\t\trunBrowser := func(t *testing.T, spa bool) {\n\t\t\t\t\tid, _ := createIdentity(t, reg)\n\n\t\t\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\t\t\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, spa, publicTS)\n\t\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\n\t\t\t\t\tpayload(values)\n\t\t\t\t\t_, _ = testhelpers.SettingsMakeRequest(t, false, spa, f, browserClient, testhelpers.EncodeFormAsJSON(t, spa, values))\n\n\t\t\t\t\tpayloadConfirm(values)\n\t\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, spa, f, browserClient, testhelpers.EncodeFormAsJSON(t, spa, values))\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\tif spa {\n\t\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(actual, \"continue_with.0.action\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"continue_with.0.redirect_browser_to\").String(), uiTS.URL, \"%s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t\t\t\t}\n\n\t\t\t\t\tassert.EqualValues(t, flow.StateSuccess, json.RawMessage(gjson.Get(actual, \"state\").String()))\n\t\t\t\t\tcheckIdentity(t, id, f)\n\t\t\t\t\ttesthelpers.EnsureAAL(t, browserClient, publicTS, \"aal2\", string(identity.CredentialsTypeLookup))\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\trunBrowser(t, false)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\t\trunBrowser(t, true)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"type=remove lookup codes\", func(t *testing.T) {\n\t\tfor _, tc := range []struct {\n\t\t\td string\n\t\t\tc func(t *testing.T) *identity.Identity\n\t\t}{\n\t\t\t{\n\t\t\t\td: \"with\",\n\t\t\t\tc: func(t *testing.T) *identity.Identity {\n\t\t\t\t\ti, _ := createIdentity(t, reg)\n\t\t\t\t\treturn i\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\td: \"without\",\n\t\t\t\tc: func(t *testing.T) *identity.Identity {\n\t\t\t\t\treturn createIdentityWithoutLookup(t, reg)\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"credentials=\"+tc.d, func(t *testing.T) {\n\t\t\t\tpayloadConfirm := func(v url.Values) {\n\t\t\t\t\tv.Del(node.LookupRegenerate)\n\t\t\t\t\tv.Del(node.LookupReveal)\n\t\t\t\t\tv.Set(node.LookupDisable, \"true\")\n\t\t\t\t}\n\n\t\t\t\tcheckIdentity := func(t *testing.T, id *identity.Identity, f *kratos.SettingsFlow) {\n\t\t\t\t\t_, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeLookup, id.ID.String())\n\t\t\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t\tactualFlow, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Empty(t, gjson.GetBytes(actualFlow.InternalContext, flow.PrefixInternalContextKey(identity.CredentialsTypeLookup, lookup.InternalContextKeyRegenerated)).Raw)\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\t\tid, _ := createIdentity(t, reg)\n\t\t\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\t\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\n\t\t\t\t\tpayloadConfirm(values)\n\t\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\t\tassert.EqualValues(t, flow.StateSuccess, json.RawMessage(gjson.Get(actual, \"state\").String()))\n\n\t\t\t\t\tcheckIdentity(t, id, f)\n\t\t\t\t\ttesthelpers.EnsureAAL(t, apiClient, publicTS, \"aal1\")\n\t\t\t\t})\n\n\t\t\t\trunBrowser := func(t *testing.T, spa bool) {\n\t\t\t\t\tid, _ := createIdentity(t, reg)\n\n\t\t\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\t\t\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, spa, publicTS)\n\t\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\n\t\t\t\t\tpayloadConfirm(values)\n\t\t\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, spa, f, browserClient, testhelpers.EncodeFormAsJSON(t, spa, values))\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\tif spa {\n\t\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(actual, \"continue_with.0.action\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"continue_with.0.redirect_browser_to\").String(), uiTS.URL, \"%s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t\t\t\t}\n\n\t\t\t\t\tassert.EqualValues(t, flow.StateSuccess, json.RawMessage(gjson.Get(actual, \"state\").String()))\n\t\t\t\t\tcheckIdentity(t, id, f)\n\t\t\t\t\ttesthelpers.EnsureAAL(t, browserClient, publicTS, \"aal1\")\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\trunBrowser(t, false)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\t\trunBrowser(t, true)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage lookup\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\n// var _ login.Strategy = new(Strategy)\nvar (\n\t_ settings.Strategy                 = (*Strategy)(nil)\n\t_ login.AAL2FormHydrator            = (*Strategy)(nil)\n\t_ identity.ActiveCredentialsCounter = (*Strategy)(nil)\n)\n\ntype dependencies interface {\n\tlogrusx.Provider\n\thttpx.WriterProvider\n\tnosurfx.CSRFTokenGeneratorProvider\n\tnosurfx.CSRFProvider\n\tx.TransactionPersistenceProvider\n\totelx.Provider\n\n\tconfig.Provider\n\n\tcontinuity.ManagementProvider\n\n\terrorx.ManagementProvider\n\thash.HashProvider\n\n\tregistration.HandlerProvider\n\tregistration.HooksProvider\n\tregistration.ErrorHandlerProvider\n\tregistration.HookExecutorProvider\n\tregistration.FlowPersistenceProvider\n\n\tlogin.HooksProvider\n\tlogin.ErrorHandlerProvider\n\tlogin.HookExecutorProvider\n\tlogin.FlowPersistenceProvider\n\tlogin.HandlerProvider\n\n\tsettings.FlowPersistenceProvider\n\tsettings.HookExecutorProvider\n\tsettings.HooksProvider\n\tsettings.ErrorHandlerProvider\n\n\tidentity.PrivilegedPoolProvider\n\tidentity.ValidationProvider\n\tidentity.ManagementProvider\n\n\tsession.HandlerProvider\n\tsession.ManagementProvider\n}\n\ntype Strategy struct{ d dependencies }\n\nfunc NewStrategy(d dependencies) *Strategy { return &Strategy{d: d} }\n\nfunc (s *Strategy) CountActiveFirstFactorCredentials(_ context.Context, _ map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\treturn 0, nil\n}\n\nfunc (s *Strategy) CountActiveMultiFactorCredentials(_ context.Context, cc map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\tfor _, c := range cc {\n\t\tif c.Type == s.ID() && len(c.Config) > 0 {\n\t\t\tvar conf identity.CredentialsLookupConfig\n\t\t\tif err := json.Unmarshal(c.Config, &conf); err != nil {\n\t\t\t\treturn 0, errors.WithStack(err)\n\t\t\t}\n\n\t\t\tif len(conf.RecoveryCodes) > 0 {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (s *Strategy) ID() identity.CredentialsType {\n\treturn identity.CredentialsTypeLookup\n}\n\nfunc (s *Strategy) NodeGroup() node.UiNodeGroup {\n\treturn node.LookupGroup\n}\n\nfunc (s *Strategy) CompletedAuthenticationMethod(ctx context.Context) session.AuthenticationMethod {\n\treturn session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    identity.AuthenticatorAssuranceLevel2,\n\t}\n}\n\nfunc (s *Strategy) PopulateLoginMethodSecondFactor(r *http.Request, f *login.Flow) error {\n\treturn s.PopulateLoginMethod(r, identity.AuthenticatorAssuranceLevel2, f)\n}\n\nfunc (s *Strategy) PopulateLoginMethodSecondFactorRefresh(r *http.Request, f *login.Flow) error {\n\treturn s.PopulateLoginMethod(r, identity.AuthenticatorAssuranceLevel2, f)\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/strategy_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage lookup_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/selfservice/strategy/lookup\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n)\n\nfunc TestCountActiveFirstFactorCredentials(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tstrategy := lookup.NewStrategy(reg)\n\n\tt.Run(\"first factor\", func(t *testing.T) {\n\t\tactual, err := strategy.CountActiveFirstFactorCredentials(t.Context(), nil)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 0, actual)\n\t})\n\n\tt.Run(\"multi factor\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\tin       map[identity.CredentialsType]identity.Credentials\n\t\t\texpected int\n\t\t}{\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte{},\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{\"recovery_codes\": []}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:        strategy.ID(),\n\t\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\t\tConfig:      []byte(`{\"recovery_codes\": [{}]}`),\n\t\t\t\t}},\n\t\t\t\texpected: 1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin:       nil,\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tactual, err := strategy.CountActiveMultiFactorCredentials(t.Context(), tc.in)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, tc.expected, actual)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/lookup/stub/login.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.schema/link.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/password/login.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"provider\": {\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n    \"traits\": {\n      \"description\": \"DO NOT DELETE THIS FIELD. This field will be overwritten in login.go's and registration.go's decoder() method. Do not add anything to this field as it has no effect.\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"upstream_parameters\": {\n      \"type\": \"object\",\n      \"$comment\": \"Only the defined parameters are allowed. This is to prevent users from sending arbitrary parameters or craft URLs that cause unexpected behavior.\",\n      \"properties\": {\n        \"login_hint\": {\n          \"description\": \"The login hint could be an email address or identifier in the upstream provider to pre-fill the upstream provider login form\",\n          \"type\": \"string\"\n        },\n        \"hd\": {\n          \"description\": \"The hd (hosted domain) parameter streamlines the login process for G Suite hosted accounts. By including the domain of the G Suite user (for example, mycollege.edu), you can indicate that the account selection UI should be optimized for accounts at that domain.\",\n          \"type\": \"string\"\n        },\n        \"prompt\": {\n          \"description\": \"The prompt specifies whether the Authorization Server prompts the End-User for reauthentication and consent (for example, select_account).\",\n          \"type\": \"string\"\n        },\n        \"auth_type\": {\n          \"description\": \"The `auth_type` parameter specifies the requested authentication features (as a comma-separated list).\",\n          \"type\": \"string\"\n        },\n        \"acr_values\": {\n          \"description\": \"The `acr_values` parameter specifies the Authentication Context Class Reference values for the authorization request.\",\n          \"type\": \"string\"\n        },\n        \"additionalProperties\": false\n      }\n    },\n    \"id_token\": {\n      \"type\": \"string\",\n      \"description\": \"An optional id token provided by an OIDC provider\"\n    },\n    \"id_token_nonce\": {\n      \"type\": \"string\",\n      \"description\": \"The nonce used when requesting the id_token from the provider. Required, if an id_token is given and the provider supports it.\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.schema/settings.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/oidc/.schema/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"link\": {\n      \"type\": \"string\"\n    },\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"unlink\": {\n      \"type\": \"string\"\n    },\n    \"traits\": {\n      \"description\": \"This field will be overwritten in registration.go's decoder() method. Do not add anything to this field as it has no effect.\"\n    },\n    \"upstream_parameters\": {\n      \"type\": \"object\",\n      \"$comment\": \"Only the defined parameters are allowed. This is to prevent users from sending arbitrary parameters or craft URLs that cause unexpected behavior.\",\n      \"properties\": {\n        \"login_hint\": {\n          \"description\": \"The login hint could be an email address or identifier in the upstream provider to pre-fill the upstream provider login form\",\n          \"type\": \"string\"\n        },\n        \"hd\": {\n          \"description\": \"The hd (hosted domain) parameter streamlines the login process for G Suite hosted accounts. By including the domain of the G Suite user (for example, mycollege.edu), you can indicate that the account selection UI should be optimized for accounts at that domain.\",\n          \"type\": \"string\"\n        },\n        \"prompt\": {\n          \"description\": \"The prompt specifies whether the Authorization Server prompts the End-User for reauthentication and consent (for example, select_account).\",\n          \"type\": \"string\"\n        },\n        \"acr_values\": {\n          \"description\": \"The `acr_values` parameter specifies the Authentication Context Class Reference values for the authorization request.\",\n          \"type\": \"string\"\n        },\n        \"additionalProperties\": false\n      }\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"test-provider\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010002,\n        \"text\": \"Sign in with test-provider\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"test-provider\",\n          \"provider_id\": \"test-provider\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"test-provider\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010002,\n        \"text\": \"Sign in with test-provider\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"test-provider\",\n          \"provider_id\": \"test-provider\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"test-provider\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010002,\n        \"text\": \"Sign in with test-provider\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"test-provider\",\n          \"provider_id\": \"test-provider\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_does_not_have_a_oidc.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_has_oidc.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"test-provider\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010002,\n        \"text\": \"Sign in with test-provider\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"test-provider\",\n          \"provider_id\": \"test-provider\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"test-provider\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010002,\n        \"text\": \"Sign in with test-provider\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"test-provider\",\n          \"provider_id\": \"test-provider\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"test-provider\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010002,\n        \"text\": \"Sign in with test-provider\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"test-provider\",\n          \"provider_id\": \"test-provider\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"test-provider\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010002,\n        \"text\": \"Sign in with test-provider\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"test-provider\",\n          \"provider_id\": \"test-provider\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"test-provider\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010002,\n        \"text\": \"Sign in with test-provider\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"test-provider\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactorRefresh.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethod.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"providerID\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with providerID\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"providerID\",\n          \"provider_id\": \"providerID\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodCredentials.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"providerID\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with providerID\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"providerID\",\n          \"provider_id\": \"providerID\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodProfile.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"providerID\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with providerID\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"providerID\",\n          \"provider_id\": \"providerID\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=1.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"providerID\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with providerID\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"providerID\",\n          \"provider_id\": \"providerID\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=2.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"providerID\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with providerID\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"providerID\",\n          \"provider_id\": \"providerID\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=3.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"providerID\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with providerID\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"providerID\",\n          \"provider_id\": \"providerID\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=4.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"providerID\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with providerID\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"providerID\",\n          \"provider_id\": \"providerID\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-case=should_adjust_linkable_providers_based_on_linked_credentials-agent=githuber.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-case=should_adjust_linkable_providers_based_on_linked_credentials-agent=multiuser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-case=should_adjust_linkable_providers_based_on_linked_credentials-agent=oryer.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-case=should_adjust_linkable_providers_based_on_linked_credentials-agent=password.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=api-case=API_flow_populates_link_and_unlink_nodes-identity=multi_credentials.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=api-case=API_flow_populates_link_and_unlink_nodes-identity=multi_oidc.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=api-case=API_flow_populates_link_and_unlink_nodes-identity=password_only.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=api-case=API_flow_populates_link_and_unlink_nodes-identity=single_oidc.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=link-case=should_link_a_connection-flow=fetch.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=link-case=should_link_a_connection-flow=original.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=link-case=should_link_a_connection-flow=response.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"required\": true,\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"github\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"ory\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"Ory\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=link-case=should_link_a_connection_even_if_user_does_not_have_oidc_credentials_yet.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=link-case=should_not_be_able_to_link_a_connection_which_already_exists.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"required\": true,\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"github\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"ory\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"Ory\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=link-case=should_not_be_able_to_link_an_non-existing_connection.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"required\": true,\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"github\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=unlink-case=should_not_be_able_to_unlink_a_connection_not_yet_linked-flow=fetch.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=unlink-case=should_not_be_able_to_unlink_a_connection_not_yet_linked-flow=json.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"required\": true,\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"github\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"ory\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"Ory\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=unlink-case=should_not_be_able_to_unlink_an_non-existing_connection-flow=fetch.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"Ory\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=unlink-case=should_not_be_able_to_unlink_an_non-existing_connection-flow=json.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"required\": true,\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink github\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"github\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"ory\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink Ory\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"Ory\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=unlink-case=should_not_be_able_to_unlink_the_last_remaining_connection-flow=fetch.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"email\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestSettingsStrategy-suite=unlink-case=should_not_be_able_to_unlink_the_last_remaining_connection-flow=json.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"required\": true,\n      \"autocomplete\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"github\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050002,\n        \"text\": \"Link google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"autoPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with autoPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"autoPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"claimsViaUserInfo\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with claimsViaUserInfo\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"claimsViaUserInfo\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"forcePKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with forcePKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"forcePKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"invalid-issuer\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with invalid-issuer\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"invalid-issuer\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"neverPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with neverPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"neverPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"secondProvider\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with secondProvider\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"secondProvider\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"valid2\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with valid2\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"valid2\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-oidc-strategy-lh-false@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"autocomplete\": \"current-password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010022,\n            \"text\": \"Sign in with password\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-oidc-strategy-lh-false@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-oidc-strategy-lh-false@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [],\n          \"available_providers\": [],\n          \"duplicateIdentifier\": \"email-exist-with-oidc-strategy-lh-false@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-oidc-strategy-lh-false@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"autoPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with autoPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"autoPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"claimsViaUserInfo\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with claimsViaUserInfo\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"claimsViaUserInfo\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"forcePKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with forcePKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"forcePKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"invalid-issuer\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with invalid-issuer\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"invalid-issuer\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"neverPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with neverPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"neverPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"secondProvider\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with secondProvider\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"secondProvider\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"valid2\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with valid2\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"valid2\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-oidc-strategy-lh-false@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"autocomplete\": \"current-password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010022,\n            \"text\": \"Sign in with password\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-oidc-strategy-lh-false@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-oidc-strategy-lh-false@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [],\n          \"available_providers\": [],\n          \"duplicateIdentifier\": \"email-exist-with-oidc-strategy-lh-false@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-oidc-strategy-lh-false@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"autoPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with autoPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"autoPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"claimsViaUserInfo\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with claimsViaUserInfo\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"claimsViaUserInfo\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"forcePKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with forcePKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"forcePKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"invalid-issuer\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with invalid-issuer\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"invalid-issuer\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"neverPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with neverPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"neverPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"secondProvider\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with secondProvider\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"secondProvider\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"valid2\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with valid2\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"valid2\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-oidc-strategy-lh-false@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"autocomplete\": \"current-password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010022,\n            \"text\": \"Sign in with password\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-oidc-strategy-lh-false@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-oidc-strategy-lh-false@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [],\n          \"available_providers\": [],\n          \"duplicateIdentifier\": \"email-exist-with-oidc-strategy-lh-false@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-oidc-strategy-lh-false@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"autoPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with autoPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"autoPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"claimsViaUserInfo\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with claimsViaUserInfo\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"claimsViaUserInfo\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"forcePKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with forcePKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"forcePKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"invalid-issuer\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with invalid-issuer\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"invalid-issuer\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"neverPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with neverPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"neverPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"secondProvider\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with secondProvider\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"secondProvider\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"valid2\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with valid2\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"valid2\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-password-strategy-lh-false@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"autocomplete\": \"current-password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010022,\n            \"text\": \"Sign in with password\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-password-strategy-lh-false@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-password-strategy-lh-false@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [],\n          \"available_providers\": [],\n          \"duplicateIdentifier\": \"email-exist-with-password-strategy-lh-false@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-password-strategy-lh-false@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"autoPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with autoPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"autoPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"claimsViaUserInfo\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with claimsViaUserInfo\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"claimsViaUserInfo\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"forcePKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with forcePKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"forcePKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"invalid-issuer\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with invalid-issuer\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"invalid-issuer\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"neverPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with neverPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"neverPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"secondProvider\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with secondProvider\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"secondProvider\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"valid2\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with valid2\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"valid2\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-password-strategy-lh-false@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"autocomplete\": \"current-password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010022,\n            \"text\": \"Sign in with password\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-password-strategy-lh-false@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-password-strategy-lh-false@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [],\n          \"available_providers\": [],\n          \"duplicateIdentifier\": \"email-exist-with-password-strategy-lh-false@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-password-strategy-lh-false@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=false-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"autoPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with autoPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"autoPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"claimsViaUserInfo\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with claimsViaUserInfo\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"claimsViaUserInfo\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"forcePKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with forcePKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"forcePKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"invalid-issuer\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with invalid-issuer\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"invalid-issuer\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"neverPKCE\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with neverPKCE\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"neverPKCE\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"secondProvider\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with secondProvider\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"secondProvider\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"valid2\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with valid2\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"valid2\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-password-strategy-lh-false@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"autocomplete\": \"current-password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010022,\n            \"text\": \"Sign in with password\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-password-strategy-lh-false@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-password-strategy-lh-false@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [],\n          \"available_providers\": [],\n          \"duplicateIdentifier\": \"email-exist-with-password-strategy-lh-false@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-password-strategy-lh-false@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_login.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"secondProvider\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with secondProvider\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"secondProvider\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-oidc-strategy-lh-true@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-oidc-strategy-lh-true@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-oidc-strategy-lh-true@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [\n            \"oidc\"\n          ],\n          \"available_providers\": [\n            \"secondProvider\"\n          ],\n          \"duplicateIdentifier\": \"email-exist-with-oidc-strategy-lh-true@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-oidc-strategy-lh-true@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"secondProvider\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with secondProvider\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"secondProvider\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-oidc-strategy-lh-true@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-oidc-strategy-lh-true@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-oidc-strategy-lh-true@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [\n            \"oidc\"\n          ],\n          \"available_providers\": [\n            \"secondProvider\"\n          ],\n          \"duplicateIdentifier\": \"email-exist-with-oidc-strategy-lh-true@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-oidc-strategy-lh-true@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_oidc_credentials-case=should_fail_registration_id_first_strategy_enabled.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"oidc\",\n        \"attributes\": {\n          \"name\": \"provider\",\n          \"type\": \"submit\",\n          \"value\": \"secondProvider\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010018,\n            \"text\": \"Confirm with secondProvider\",\n            \"type\": \"info\",\n            \"context\": {\n              \"provider\": \"secondProvider\"\n            }\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-oidc-strategy-lh-true@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-oidc-strategy-lh-true@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-oidc-strategy-lh-true@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [\n            \"oidc\"\n          ],\n          \"available_providers\": [\n            \"secondProvider\"\n          ],\n          \"duplicateIdentifier\": \"email-exist-with-oidc-strategy-lh-true@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-oidc-strategy-lh-true@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_login.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-password-strategy-lh-true@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"autocomplete\": \"current-password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010022,\n            \"text\": \"Sign in with password\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-password-strategy-lh-true@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-password-strategy-lh-true@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [\n            \"password\"\n          ],\n          \"available_providers\": [],\n          \"duplicateIdentifier\": \"email-exist-with-password-strategy-lh-true@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-password-strategy-lh-true@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-password-strategy-lh-true@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"autocomplete\": \"current-password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010022,\n            \"text\": \"Sign in with password\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-password-strategy-lh-true@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-password-strategy-lh-true@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [\n            \"password\"\n          ],\n          \"available_providers\": [],\n          \"duplicateIdentifier\": \"email-exist-with-password-strategy-lh-true@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-password-strategy-lh-true@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-login-hints-enabled=true-case=should_fail_to_register_and_return_fresh_login_flow_if_email_is_already_being_used_by_password_credentials-case=should_fail_registration_id_first_strategy_enabled.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"active\": \"oidc\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"value\": \"email-exist-with-password-strategy-lh-true@ory.sh\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070004,\n            \"text\": \"ID\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"password\",\n          \"type\": \"password\",\n          \"required\": true,\n          \"autocomplete\": \"current-password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1070001,\n            \"text\": \"Password\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"password\",\n        \"attributes\": {\n          \"name\": \"method\",\n          \"type\": \"submit\",\n          \"value\": \"password\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"id\": 1010022,\n            \"text\": \"Sign in with password\",\n            \"type\": \"info\"\n          }\n        }\n      }\n    ],\n    \"messages\": [\n      {\n        \"id\": 1010016,\n        \"text\": \"You tried to sign in with \\\"email-exist-with-password-strategy-lh-true@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"email-exist-with-password-strategy-lh-true@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\",\n        \"type\": \"info\",\n        \"context\": {\n          \"available_credential_types\": [\n            \"password\"\n          ],\n          \"available_providers\": [],\n          \"duplicateIdentifier\": \"email-exist-with-password-strategy-lh-true@ory.sh\",\n          \"duplicate_identifier\": \"email-exist-with-password-strategy-lh-true@ory.sh\",\n          \"provider\": \"generic\"\n        }\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/.snapshots/TestStrategy-method=TestPopulateSignUpMethod.json",
    "content": "{\n  \"method\": \"POST\",\n  \"nodes\": [\n    {\n      \"type\": \"input\",\n      \"group\": \"default\",\n      \"attributes\": {\n        \"name\": \"csrf_token\",\n        \"type\": \"hidden\",\n        \"required\": true,\n        \"disabled\": false,\n        \"node_type\": \"input\"\n      },\n      \"messages\": [],\n      \"meta\": {}\n    },\n    {\n      \"type\": \"input\",\n      \"group\": \"oidc\",\n      \"attributes\": {\n        \"name\": \"provider\",\n        \"type\": \"submit\",\n        \"value\": \"valid\",\n        \"disabled\": false,\n        \"node_type\": \"input\"\n      },\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1040002,\n          \"text\": \"Sign up with valid\",\n          \"type\": \"info\",\n          \"context\": {\n            \"provider\": \"valid\",\n            \"provider_id\": \"valid\"\n          }\n        }\n      }\n    },\n    {\n      \"type\": \"input\",\n      \"group\": \"oidc\",\n      \"attributes\": {\n        \"name\": \"provider\",\n        \"type\": \"submit\",\n        \"value\": \"valid2\",\n        \"disabled\": false,\n        \"node_type\": \"input\"\n      },\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1040002,\n          \"text\": \"Sign up with valid2\",\n          \"type\": \"info\",\n          \"context\": {\n            \"provider\": \"valid2\",\n            \"provider_id\": \"valid2\"\n          }\n        }\n      }\n    },\n    {\n      \"type\": \"input\",\n      \"group\": \"oidc\",\n      \"attributes\": {\n        \"name\": \"provider\",\n        \"type\": \"submit\",\n        \"value\": \"secondProvider\",\n        \"disabled\": false,\n        \"node_type\": \"input\"\n      },\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1040002,\n          \"text\": \"Sign up with secondProvider\",\n          \"type\": \"info\",\n          \"context\": {\n            \"provider\": \"secondProvider\",\n            \"provider_id\": \"secondProvider\"\n          }\n        }\n      }\n    },\n    {\n      \"type\": \"input\",\n      \"group\": \"oidc\",\n      \"attributes\": {\n        \"name\": \"provider\",\n        \"type\": \"submit\",\n        \"value\": \"claimsViaUserInfo\",\n        \"disabled\": false,\n        \"node_type\": \"input\"\n      },\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1040002,\n          \"text\": \"Sign up with claimsViaUserInfo\",\n          \"type\": \"info\",\n          \"context\": {\n            \"provider\": \"claimsViaUserInfo\",\n            \"provider_id\": \"claimsViaUserInfo\"\n          }\n        }\n      }\n    },\n    {\n      \"type\": \"input\",\n      \"group\": \"oidc\",\n      \"attributes\": {\n        \"name\": \"provider\",\n        \"type\": \"submit\",\n        \"value\": \"neverPKCE\",\n        \"disabled\": false,\n        \"node_type\": \"input\"\n      },\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1040002,\n          \"text\": \"Sign up with neverPKCE\",\n          \"type\": \"info\",\n          \"context\": {\n            \"provider\": \"neverPKCE\",\n            \"provider_id\": \"neverPKCE\"\n          }\n        }\n      }\n    },\n    {\n      \"type\": \"input\",\n      \"group\": \"oidc\",\n      \"attributes\": {\n        \"name\": \"provider\",\n        \"type\": \"submit\",\n        \"value\": \"autoPKCE\",\n        \"disabled\": false,\n        \"node_type\": \"input\"\n      },\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1040002,\n          \"text\": \"Sign up with autoPKCE\",\n          \"type\": \"info\",\n          \"context\": {\n            \"provider\": \"autoPKCE\",\n            \"provider_id\": \"autoPKCE\"\n          }\n        }\n      }\n    },\n    {\n      \"type\": \"input\",\n      \"group\": \"oidc\",\n      \"attributes\": {\n        \"name\": \"provider\",\n        \"type\": \"submit\",\n        \"value\": \"forcePKCE\",\n        \"disabled\": false,\n        \"node_type\": \"input\"\n      },\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1040002,\n          \"text\": \"Sign up with forcePKCE\",\n          \"type\": \"info\",\n          \"context\": {\n            \"provider\": \"forcePKCE\",\n            \"provider_id\": \"forcePKCE\"\n          }\n        }\n      }\n    },\n    {\n      \"type\": \"input\",\n      \"group\": \"oidc\",\n      \"attributes\": {\n        \"name\": \"provider\",\n        \"type\": \"submit\",\n        \"value\": \"invalid-issuer\",\n        \"disabled\": false,\n        \"node_type\": \"input\"\n      },\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1040002,\n          \"text\": \"Sign up with invalid-issuer\",\n          \"type\": \"info\",\n          \"context\": {\n            \"provider\": \"invalid-issuer\",\n            \"provider_id\": \"invalid-issuer\"\n          }\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/const.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nconst (\n\tsessionName = \"ory_kratos_oidc_auth_code_session\"\n)\n"
  },
  {
    "path": "selfservice/strategy/oidc/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/logrusx\"\n)\n\nvar (\n\tErrScopeMissing = herodot.ErrBadRequest.\n\t\t\tWithError(\"authentication failed because a required scope was not granted\").\n\t\t\tWithReasonf(`Unable to finish because one or more permissions were not granted. Please retry and accept all permissions.`)\n\n\tErrIDTokenMissing = herodot.ErrBadRequest.\n\t\t\t\tWithError(\"authentication failed because id_token is missing\").\n\t\t\t\tWithReasonf(`Authentication failed because no id_token was returned. Please accept the \"openid\" permission and try again.`)\n)\n\nfunc logUpstreamError(l *logrusx.Logger, resp *http.Response) error {\n\tif resp.StatusCode == http.StatusOK {\n\t\treturn nil\n\t}\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, 1024))\n\tif err != nil {\n\t\tl = l.WithError(err)\n\t}\n\n\tl.WithField(\"response_code\", resp.StatusCode).WithField(\"response_body\", string(body)).Error(\"The upstream OIDC provider returned a non 200 status code.\")\n\treturn errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"OpenID Connect provider returned a %d status code but 200 is expected.\", resp.StatusCode))\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/fedcm/definitions.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage fedcm\n\nimport \"encoding/json\"\n\ntype Provider struct {\n\t// A full path of the IdP config file.\n\tConfigURL string `json:\"config_url\"`\n\n\t// The RP's client identifier, issued by the IdP.\n\tClientID string `json:\"client_id\"`\n\n\t// A random string to ensure the response is issued for this specific request.\n\t// Prevents replay attacks.\n\tNonce string `json:\"nonce\"`\n\n\t// By specifying one of login_hints values provided by the accounts endpoints,\n\t// the FedCM dialog selectively shows the specified account.\n\tLoginHint string `json:\"login_hint,omitempty\"`\n\n\t// By specifying one of domain_hints values provided by the accounts endpoints,\n\t// the FedCM dialog selectively shows the specified account.\n\tDomainHint string `json:\"domain_hint,omitempty\"`\n\n\t// Array of strings that specifies the user information (\"name\", \" email\",\n\t// \"picture\") that RP needs IdP to share with them.\n\t//\n\t// Note: Field API is supported by Chrome 132 and later.\n\tFields []string `json:\"fields,omitempty\"`\n\n\t// Custom object that allows to specify additional key-value parameters:\n\t//  - scope: A string value containing additional permissions that RP needs to\n\t//    request, for example \" drive.readonly calendar.readonly\"\n\t//  - nonce: A random string to ensure the response is issued for this specific\n\t//    request. Prevents replay attacks.\n\t//\n\t// Other custom key-value parameters.\n\t//\n\t// Note: parameters is supported from Chrome 132.\n\tParameters map[string]string `json:\"parameters,omitempty\"`\n}\n\n// CreateFedcmFlowResponse\n//\n// Contains a list of all available FedCM providers.\n//\n// swagger:model createFedcmFlowResponse\ntype CreateFedcmFlowResponse struct {\n\tProviders []Provider `json:\"providers\"`\n\tCSRFToken string     `json:\"csrf_token\"`\n}\n\n// swagger:route GET /self-service/fed-cm/parameters frontend createFedcmFlow\n//\n// # Get FedCM Parameters\n//\n// This endpoint returns a list of all available FedCM providers. It is only supported on the Ory Network.\n//\n//\tConsumes:\n//\t- application/json\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: createFedcmFlowResponse\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-low\n\ntype UpdateFedcmFlowBody struct {\n\t// Token contains the result of `navigator.credentials.get`.\n\t//\n\t// required: true\n\tToken string `json:\"token\"`\n\n\t// Nonce is the nonce that was used in the `navigator.credentials.get` call. If\n\t// specified, it must match the `nonce` claim in the token.\n\t//\n\t// required: false\n\tNonce string `json:\"nonce\"`\n\n\t// CSRFToken is the anti-CSRF token.\n\t//\n\t// required: true\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Transient data to pass along to any webhooks.\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\n// swagger:parameters updateFedcmFlow\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateFedcmFlow struct {\n\t// in: body\n\t// required: true\n\tBody UpdateFedcmFlowBody\n}\n\n// swagger:route POST /self-service/fed-cm/token frontend updateFedcmFlow\n//\n// # Submit a FedCM token\n//\n// Use this endpoint to submit a token from a FedCM provider through\n// `navigator.credentials.get` and log the user in. The parameters from\n// `navigator.credentials.get` must have come from `GET\n// /self-service/fed-cm/parameters`.\n//\n//\tConsumes:\n//\t- application/json\n//\t- application/x-www-form-urlencoded\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tHeader:\n//\t- Set-Cookie\n//\n//\tResponses:\n//\t  200: successfulNativeLogin\n//\t  303: emptyResponse\n//\t  400: loginFlow\n//\t  410: errorGeneric\n//\t  422: errorBrowserLocationChangeRequired\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-high\n"
  },
  {
    "path": "selfservice/strategy/oidc/form.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\n\t\"dario.cat/mergo\"\n\n\t\"github.com/ory/kratos/identity\"\n)\n\n// merge merges the userFormValues (extracted from the initial POST request) prefixed with `traits` (encoded) with the\n// values coming from the OpenID Provider (openIDProviderValues).\nfunc merge(containerTraits json.RawMessage, openIDProviderValues json.RawMessage) (identity.Traits, error) {\n\tif len(containerTraits) == 0 || string(containerTraits) == \"{}\" {\n\t\treturn identity.Traits(openIDProviderValues), nil\n\t}\n\n\tvar pt map[string]interface{}\n\tif err := json.NewDecoder(bytes.NewBuffer(openIDProviderValues)).Decode(&pt); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ct map[string]interface{}\n\tif err := json.NewDecoder(bytes.NewBuffer(containerTraits)).Decode(&ct); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// decoderForm (coming from POST request) overrides decodedTraits (coming from OP)\n\tif err := mergo.Merge(&pt, &ct, mergo.WithOverride); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar result bytes.Buffer\n\tif err := json.NewEncoder(&result).Encode(pt); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn result.Bytes(), nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/form_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMerge(t *testing.T) {\n\tfor k, tc := range []struct {\n\t\tschema string\n\t\tform   json.RawMessage\n\t\top     json.RawMessage\n\t\texpect json.RawMessage\n\t}{\n\t\t{\n\t\t\tform:   json.RawMessage(\"{}\"),\n\t\t\top:     json.RawMessage(\"{}\"),\n\t\t\texpect: json.RawMessage(\"{}\"),\n\t\t},\n\t\t{\n\t\t\tform:   json.RawMessage(`{\"foo\": \"bar\", \"bool\": true, \"opv\": \"blubb\"}`),\n\t\t\top:     json.RawMessage(`{\"baz\":\"bar\",\"opv\":\"bla\"}`),\n\t\t\texpect: json.RawMessage(`{\"foo\":\"bar\",\"baz\":\"bar\",\"bool\":true,\"opv\":\"blubb\"}`),\n\t\t},\n\t\t{\n\t\t\tform:   json.RawMessage(`{\"bool\": true, \"opv\": \"blubb\"}`),\n\t\t\top:     json.RawMessage(`{\"foo\":\"bar\",\"baz\":\"bar\",\"opv\":\"bla\"}`),\n\t\t\texpect: json.RawMessage(`{\"foo\":\"bar\",\"baz\":\"bar\",\"bool\":true,\"opv\":\"blubb\"}`),\n\t\t},\n\t\t{\n\t\t\tform:   json.RawMessage(`{\"bool\": true, \"opv\": \"blubb\"}`),\n\t\t\top:     json.RawMessage(`{\"foo\":\"bar\",\"baz\":\"bar\",\"opv\":\"bla\"}`),\n\t\t\texpect: json.RawMessage(`{\"foo\":\"bar\",\"baz\":\"bar\",\"bool\":true,\"opv\":\"blubb\"}`),\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tgot, err := merge(tc.form, tc.op)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.JSONEq(t, string(tc.expect), string(got))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/nodes.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc NewLinkNode(providerID, providerLabel string) *node.Node {\n\treturn node.NewInputField(\"link\", providerID, node.OpenIDConnectGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoSelfServiceSettingsUpdateLinkOIDC(providerLabel))\n}\n\nfunc NewUnlinkNode(providerID, providerLabel string) *node.Node {\n\treturn node.NewInputField(\"unlink\", providerID, node.OpenIDConnectGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoSelfServiceSettingsUpdateUnlinkOIDC(providerLabel))\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/pkce.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"slices\"\n\n\tgooidc \"github.com/coreos/go-oidc/v3/oidc\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\toidcv1 \"github.com/ory/kratos/gen/oidc/v1\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n)\n\ntype pkceDependencies interface {\n\tlogrusx.Provider\n\thttpx.ClientProvider\n}\n\nfunc PKCEChallenge(s *oidcv1.State) []oauth2.AuthCodeOption {\n\tif s.GetPkceVerifier() == \"\" {\n\t\treturn nil\n\t}\n\treturn []oauth2.AuthCodeOption{oauth2.S256ChallengeOption(s.GetPkceVerifier())}\n}\n\nfunc PKCEVerifier(s *oidcv1.State) []oauth2.AuthCodeOption {\n\tif s.GetPkceVerifier() == \"\" {\n\t\treturn nil\n\t}\n\treturn []oauth2.AuthCodeOption{oauth2.VerifierOption(s.GetPkceVerifier())}\n}\n\nfunc maybePKCE(ctx context.Context, d pkceDependencies, _p Provider) (verifier string) {\n\tif _p.Config().PKCE == \"never\" {\n\t\treturn \"\"\n\t}\n\n\tp, ok := _p.(OAuth2Provider)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\n\tif p.Config().PKCE != \"force\" {\n\t\t// autodiscover PKCE support\n\t\tpkceSupported, err := discoverPKCE(ctx, d, p)\n\t\tif err != nil {\n\t\t\td.Logger().WithError(err).Warnf(\"Failed to autodiscover PKCE support for provider %q. Continuing without PKCE.\", p.Config().ID)\n\t\t\treturn \"\"\n\t\t}\n\t\tif !pkceSupported {\n\t\t\td.Logger().Infof(\"Provider %q does not advertise support for PKCE. Continuing without PKCE.\", p.Config().ID)\n\t\t\treturn \"\"\n\t\t}\n\t}\n\treturn oauth2.GenerateVerifier()\n}\n\nfunc discoverPKCE(ctx context.Context, d pkceDependencies, p OAuth2Provider) (pkceSupported bool, err error) {\n\tif p.Config().IssuerURL == \"\" {\n\t\treturn false, errors.New(\"Issuer URL must be set to autodiscover PKCE support\")\n\t}\n\n\tctx = gooidc.ClientContext(ctx, d.HTTPClient(ctx).HTTPClient)\n\tgp, err := gooidc.NewProvider(ctx, p.Config().IssuerURL)\n\tif err != nil {\n\t\treturn false, errors.Wrap(err, \"failed to initialize provider\")\n\t}\n\tvar claims struct {\n\t\tCodeChallengeMethodsSupported []string `json:\"code_challenge_methods_supported\"`\n\t}\n\tif err := gp.Claims(&claims); err != nil {\n\t\treturn false, errors.Wrap(err, \"failed to deserialize provider claims\")\n\t}\n\treturn slices.Contains(claims.CodeChallengeMethodsSupported, \"S256\"), nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/pkce_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\toidcv1 \"github.com/ory/kratos/gen/oidc/v1\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestPKCESupport(t *testing.T) {\n\tsupported := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = fmt.Fprintf(w, `{\"issuer\": \"http://%s\", \"code_challenge_methods_supported\":[\"S256\"]}`, r.Host)\n\t}))\n\tt.Cleanup(supported.Close)\n\tnotSupported := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = fmt.Fprintf(w, `{\"issuer\": \"http://%s\", \"code_challenge_methods_supported\": [\"plain\"]}`, r.Host)\n\t}))\n\tt.Cleanup(notSupported.Close)\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t_ = conf\n\tstrat := oidc.NewStrategy(reg)\n\n\tfor _, tc := range []struct {\n\t\tc    *oidc.Configuration\n\t\tpkce bool\n\t}{\n\t\t{c: &oidc.Configuration{IssuerURL: supported.URL, PKCE: \"force\"}, pkce: true},\n\t\t{c: &oidc.Configuration{IssuerURL: supported.URL, PKCE: \"never\"}, pkce: false},\n\t\t{c: &oidc.Configuration{IssuerURL: supported.URL, PKCE: \"auto\"}, pkce: true},\n\t\t{c: &oidc.Configuration{IssuerURL: supported.URL, PKCE: \"\"}, pkce: true}, // same as auto\n\n\t\t{c: &oidc.Configuration{IssuerURL: notSupported.URL, PKCE: \"force\"}, pkce: true},\n\t\t{c: &oidc.Configuration{IssuerURL: notSupported.URL, PKCE: \"never\"}, pkce: false},\n\t\t{c: &oidc.Configuration{IssuerURL: notSupported.URL, PKCE: \"auto\"}, pkce: false},\n\t\t{c: &oidc.Configuration{IssuerURL: notSupported.URL, PKCE: \"\"}, pkce: false}, // same as auto\n\n\t\t{c: &oidc.Configuration{IssuerURL: \"\", PKCE: \"force\"}, pkce: true},\n\t\t{c: &oidc.Configuration{IssuerURL: \"\", PKCE: \"never\"}, pkce: false},\n\t\t{c: &oidc.Configuration{IssuerURL: \"\", PKCE: \"auto\"}, pkce: false},\n\t\t{c: &oidc.Configuration{IssuerURL: \"\", PKCE: \"\"}, pkce: false}, // same as auto\n\n\t} {\n\t\tprovider := oidc.NewProviderGenericOIDC(tc.c, reg)\n\n\t\tflow := &login.Flow{\n\t\t\tID: x.NewUUID(),\n\t\t}\n\n\t\tstateParam, pkce, err := strat.GenerateState(context.Background(), provider, flow)\n\t\trequire.NoError(t, err)\n\t\trequire.NotEmpty(t, stateParam)\n\n\t\tstate, err := oidc.DecryptState(context.Background(), reg.Cipher(context.Background()), stateParam)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, oidcv1.FlowKind_FLOW_KIND_LOGIN, state.FlowKind)\n\n\t\tif tc.pkce {\n\t\t\trequire.NotEmpty(t, pkce)\n\t\t\trequire.NotEmpty(t, oidc.PKCEVerifier(state))\n\t\t} else {\n\t\t\trequire.Empty(t, pkce)\n\t\t\trequire.Empty(t, oidc.PKCEVerifier(state))\n\t\t}\n\t}\n\n\tt.Run(\"OAuth1\", func(t *testing.T) {\n\t\tfor _, provider := range []oidc.Provider{\n\t\t\toidc.NewProviderX(&oidc.Configuration{IssuerURL: supported.URL, PKCE: \"force\"}, reg),\n\t\t\toidc.NewProviderX(&oidc.Configuration{IssuerURL: supported.URL, PKCE: \"never\"}, reg),\n\t\t\toidc.NewProviderX(&oidc.Configuration{IssuerURL: supported.URL, PKCE: \"auto\"}, reg),\n\t\t} {\n\t\t\tstateParam, pkce, err := strat.GenerateState(context.Background(), provider, &registration.Flow{ID: x.NewUUID()})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotEmpty(t, stateParam)\n\t\t\tassert.Empty(t, pkce)\n\n\t\t\tstate, err := oidc.DecryptState(context.Background(), reg.Cipher(context.Background()), stateParam)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Empty(t, oidc.PKCEVerifier(state))\n\t\t\tassert.Equal(t, oidcv1.FlowKind_FLOW_KIND_REGISTRATION, state.FlowKind)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/dghubble/oauth1\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\ntype (\n\tProvider interface {\n\t\tConfig() *Configuration\n\t}\n\tOAuth2Provider interface {\n\t\tProvider\n\t\tAuthCodeURLOptions(r ider) []oauth2.AuthCodeOption\n\t\tOAuth2(ctx context.Context) (*oauth2.Config, error)\n\t\tClaims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error)\n\t}\n\tOAuth1Provider interface {\n\t\tProvider\n\t\tOAuth1(ctx context.Context) *oauth1.Config\n\t\tAuthURL(ctx context.Context, state string) (string, error)\n\t\tClaims(ctx context.Context, token *oauth1.Token) (*Claims, error)\n\t\tExchangeToken(ctx context.Context, req *http.Request) (*oauth1.Token, error)\n\t}\n)\n\ntype OAuth2TokenExchanger interface {\n\tExchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error)\n}\n\ntype IDTokenVerifier interface {\n\tVerify(ctx context.Context, rawIDToken string) (*Claims, error)\n}\n\ntype NonceValidationSkipper interface {\n\tCanSkipNonce(*Claims) bool\n}\n\ntype Claims struct {\n\tIssuer            string `json:\"iss,omitempty\"`\n\tSubject           string `json:\"sub,omitempty\"`\n\tObject            string `json:\"oid,omitempty\"`\n\tName              string `json:\"name,omitempty\"`\n\tGivenName         string `json:\"given_name,omitempty\"`\n\tFamilyName        string `json:\"family_name,omitempty\"`\n\tLastName          string `json:\"last_name,omitempty\"`\n\tMiddleName        string `json:\"middle_name,omitempty\"`\n\tNickname          string `json:\"nickname,omitempty\"`\n\tPreferredUsername string `json:\"preferred_username,omitempty\"`\n\tProfile           string `json:\"profile,omitempty\"`\n\tPicture           string `json:\"picture,omitempty\"`\n\tWebsite           string `json:\"website,omitempty\"`\n\tEmail             string `json:\"email,omitempty\"`\n\t// ConvertibleBoolean is used as Apple casually sends the email_verified field as a string.\n\tEmailVerified       x.ConvertibleBoolean   `json:\"email_verified,omitempty\"`\n\tGender              string                 `json:\"gender,omitempty\"`\n\tBirthdate           string                 `json:\"birthdate,omitempty\"`\n\tZoneinfo            string                 `json:\"zoneinfo,omitempty\"`\n\tLocale              Locale                 `json:\"locale,omitempty\"`\n\tPhoneNumber         string                 `json:\"phone_number,omitempty\"`\n\tPhoneNumberVerified bool                   `json:\"phone_number_verified,omitempty\"`\n\tUpdatedAt           int64                  `json:\"updated_at,omitempty\"`\n\tHD                  string                 `json:\"hd,omitempty\"`\n\tTeam                string                 `json:\"team,omitempty\"`\n\tNonce               string                 `json:\"nonce,omitempty\"`\n\tNonceSupported      bool                   `json:\"nonce_supported,omitempty\"`\n\tRawClaims           map[string]interface{} `json:\"raw_claims,omitempty\"`\n}\n\ntype Locale string\n\nfunc (l *Locale) UnmarshalJSON(data []byte) error {\n\tvar linkedInLocale struct {\n\t\tLanguage string `json:\"language\"`\n\t\tCountry  string `json:\"country\"`\n\t}\n\tif err := json.Unmarshal(data, &linkedInLocale); err == nil {\n\t\tswitch {\n\t\tcase linkedInLocale.Language == \"\":\n\t\t\t*l = Locale(linkedInLocale.Country)\n\t\tcase linkedInLocale.Country == \"\":\n\t\t\t*l = Locale(linkedInLocale.Language)\n\t\tdefault:\n\t\t\t*l = Locale(strings.Join([]string{linkedInLocale.Language, linkedInLocale.Country}, \"-\"))\n\t\t}\n\n\t\treturn nil\n\t}\n\n\treturn json.Unmarshal(data, (*string)(l))\n}\n\n// Validate checks if the claims are valid.\nfunc (c *Claims) Validate() error {\n\tif c.Subject == \"\" {\n\t\treturn errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"provider did not return a subject\"))\n\t}\n\tif c.Issuer == \"\" {\n\t\treturn errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"issuer not set in claims\"))\n\t}\n\treturn nil\n}\n\n// UpstreamParameters returns a list of oauth2.AuthCodeOption based on the upstream parameters.\n//\n// Only allowed parameters are returned and the rest is ignored.\n// Allowed parameters are also defined in the `oidc/.schema/link.schema.json` file, however,\n// this function also validates the parameters to prevent any potential security issues.\n//\n// Allowed parameters are:\n// - `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\n// - `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\n// - `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\n// - `auth_type` (string): The `auth_type` parameter specifies the requested authentication features (as a comma-separated list), e.g. `reauthenticate`.\nfunc UpstreamParameters(upstreamParameters map[string]string) []oauth2.AuthCodeOption {\n\t// validation of upstream parameters are already handled in the `oidc/.schema/link.schema.json` and `oidc/.schema/settings.schema.json` file.\n\t// `upstreamParameters` will always only contain allowed parameters based on the configuration.\n\n\t// we double-check the parameters here to prevent any potential security issues.\n\tallowedParameters := map[string]struct{}{\n\t\t\"login_hint\": {},\n\t\t\"hd\":         {},\n\t\t\"prompt\":     {},\n\t\t\"auth_type\":  {},\n\t\t\"acr_values\": {},\n\t}\n\n\tvar params []oauth2.AuthCodeOption\n\tfor up, v := range upstreamParameters {\n\t\tif _, ok := allowedParameters[up]; ok {\n\t\t\tparams = append(params, oauth2.SetAuthURLParam(up, v))\n\t\t}\n\t}\n\n\treturn params\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_amazon.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"slices\"\n\t\"testing\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\t\"golang.org/x/oauth2/amazon\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar _ OAuth2Provider = (*ProviderAmazon)(nil)\n\nvar amazonSupportedScopes = []string{\"profile\", \"profile:user_id\", \"postal_code\"}\n\ntype ProviderAmazon struct {\n\t*ProviderGenericOIDC\n\tamazonProfileURL string // Only overriden in tests.\n}\n\ntype amazonProfileResponse struct {\n\tUserID     string `json:\"user_id\"`\n\tEmail      string `json:\"email\"`\n\tName       string `json:\"name\"`\n\tPostalCode string `json:\"postal_code\"`\n}\n\nfunc NewProviderAmazon(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\tconfig.IssuerURL = amazon.Endpoint.AuthURL\n\tconst amazonProfileURL string = \"https://api.amazon.com/user/profile\"\n\n\treturn &ProviderAmazon{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t\tamazonProfileURL: amazonProfileURL,\n\t}\n}\n\nfunc (p *ProviderAmazon) SetProfileURL(t *testing.T, url string) {\n\tp.amazonProfileURL = url\n}\n\nfunc (p *ProviderAmazon) Config() *Configuration {\n\treturn p.config\n}\n\nfunc (p *ProviderAmazon) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     p.config.ClientID,\n\t\tClientSecret: p.config.ClientSecret,\n\t\tEndpoint:     amazon.Endpoint,\n\t\tScopes:       p.config.Scope,\n\t\tRedirectURL:  p.config.Redir(p.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (p *ProviderAmazon) validateConfiguration() error {\n\tfor _, s := range p.config.Scope {\n\t\tif !slices.Contains(amazonSupportedScopes, s) {\n\t\t\treturn errors.WithStack(\n\t\t\t\therodot.ErrMisconfiguration.WithReasonf(\"scope %s not supported. Supported: %+v\", s, amazonSupportedScopes))\n\t\t}\n\t}\n\tif p.config.PKCE == \"auto\" {\n\t\treturn errors.WithStack(herodot.ErrMisconfiguration.WithReason(\"pkce:auto is not supported because Amazon does not support PKCE discovery\"))\n\t}\n\n\treturn nil\n}\n\nfunc (p *ProviderAmazon) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\t// This is as good a place as any to validate the configuration.\n\tif err := p.validateConfiguration(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn p.oauth2(ctx), nil\n}\n\nfunc (p *ProviderAmazon) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{}\n}\n\nfunc (p *ProviderAmazon) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (_ *Claims, err error) {\n\tctx, span := p.reg.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.ProviderAmazon.Claims\")\n\tdefer otelx.End(span, &err)\n\n\tctx, client := httpx.SetOAuth2(ctx, p.reg.HTTPClient(ctx), p.oauth2(ctx), exchange)\n\n\treq, err := retryablehttp.NewRequestWithContext(ctx, http.MethodGet, p.amazonProfileURL, nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReason(\"failed to create HTTP request\").WithDetail(\"url\", p.amazonProfileURL).WithError(err.Error()))\n\t}\n\treq.Header.Set(\"x-amz-access-token\", exchange.AccessToken)\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReason(\"failed to make HTTP request\").WithDetail(\"url\", p.amazonProfileURL).WithError(err.Error()))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\tbody := io.LimitReader(resp.Body, 64*1024) // 64 KiB\n\n\tif resp.StatusCode != http.StatusOK {\n\t\trawResponse, _ := io.ReadAll(body)\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReason(\"non 200 response\").WithDetail(\"url\", p.amazonProfileURL).WithDetail(\"external_error\", string(rawResponse)).\n\t\t\tWithDetail(\"external_status_code\", resp.StatusCode))\n\t}\n\n\tprofile := amazonProfileResponse{}\n\tif err := json.NewDecoder(body).Decode(&profile); err != nil {\n\t\trawResponse, _ := io.ReadAll(body)\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithDetail(\"url\", p.amazonProfileURL).WithDetail(\"raw_response\", rawResponse).WithError(err.Error()))\n\t}\n\n\tclaims := &Claims{\n\t\tSubject:  profile.UserID,\n\t\tIssuer:   amazon.Endpoint.TokenURL,\n\t\tName:     profile.Name,\n\t\tEmail:    profile.Email,\n\t\tZoneinfo: profile.PostalCode,\n\t}\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_amazon_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/oauth2\"\n\t\"golang.org/x/oauth2/amazon\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nfunc TestAmazonOidcClaims(t *testing.T) {\n\tt.Parallel()\n\n\thandler := http.NewServeMux()\n\texpectedAccessToken := \"my-access-token\"\n\thandler.HandleFunc(\"GET /user/profile\", func(w http.ResponseWriter, r *http.Request) {\n\t\ttoken := r.Header.Get(\"x-amz-access-token\")\n\t\tif token != expectedAccessToken {\n\t\t\tw.WriteHeader(http.StatusForbidden)\n\t\t\treturn\n\t\t}\n\t\t// From the official docs: https://developer.amazon.com/docs/login-with-amazon/customer-profile.html .\n\t\tuserProfile := `\n{\n    \"user_id\" : \"amzn1.account.K2LI23KL2LK2\",\n    \"email\" : \"johndoe@gmail.com\",\n    \"name\" : \"John Doe\",\n    \"postal_code\": \"98101\"\n}\n`\n\n\t\t_, err := w.Write([]byte(userProfile))\n\t\trequire.NoError(t, err)\n\t})\n\tamazonAPI := httptest.NewServer(handler)\n\tt.Cleanup(amazonAPI.Close)\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := oidc.NewProviderAmazon(&oidc.Configuration{}, reg).(*oidc.ProviderAmazon)\n\tp.SetProfileURL(t, amazonAPI.URL+\"/user/profile\")\n\n\tclaims, err := p.Claims(t.Context(), &oauth2.Token{AccessToken: expectedAccessToken}, nil)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, claims)\n\n\trequire.Equal(t, claims.Subject, \"amzn1.account.K2LI23KL2LK2\")\n\trequire.Equal(t, claims.Issuer, amazon.Endpoint.TokenURL)\n\trequire.Equal(t, claims.Name, \"John Doe\")\n\trequire.Equal(t, claims.Email, \"johndoe@gmail.com\")\n\trequire.Equal(t, claims.Zoneinfo, \"98101\")\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_apple.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"crypto/ecdsa\"\n\t\"crypto/x509\"\n\t\"encoding/json\"\n\t\"encoding/pem\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/coreos/go-oidc/v3/oidc\"\n\t\"github.com/golang-jwt/jwt/v4\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"golang.org/x/oauth2\"\n)\n\ntype ProviderApple struct {\n\t*ProviderGenericOIDC\n\tJWKSUrl string\n}\n\nvar _ OAuth2Provider = (*ProviderApple)(nil)\n\nfunc NewProviderApple(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\tconfig.IssuerURL = \"https://appleid.apple.com\"\n\treturn &ProviderApple{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t\tJWKSUrl: \"https://appleid.apple.com/auth/keys\",\n\t}\n}\n\nfunc (a *ProviderApple) newClientSecret() (string, error) {\n\t// decode the pem format\n\tblock, _ := pem.Decode([]byte(a.config.PrivateKey))\n\tif block == nil || block.Type != \"PRIVATE KEY\" {\n\t\treturn \"\", errors.New(\"failed to decode PEM block containing private key\")\n\t}\n\n\tparsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"Private key decoding failed\")\n\t}\n\tprivateKey, ok := parsedKey.(*ecdsa.PrivateKey)\n\tif !ok {\n\t\treturn \"\", errors.New(\"Private key is not ecdsa key\")\n\t}\n\n\tnow := time.Now()\n\texpirationTime := time.Now().Add(5 * time.Minute)\n\n\tappleToken := jwt.NewWithClaims(jwt.SigningMethodES256,\n\t\tjwt.RegisteredClaims{\n\t\t\tAudience:  []string{\"https://appleid.apple.com\"},\n\t\t\tExpiresAt: jwt.NewNumericDate(expirationTime),\n\t\t\tIssuedAt:  jwt.NewNumericDate(now),\n\t\t\tIssuer:    a.config.TeamId,\n\t\t\tSubject:   a.config.ClientID,\n\t\t})\n\tappleToken.Header[\"kid\"] = a.config.PrivateKeyId\n\n\treturn appleToken.SignedString(privateKey)\n}\n\nfunc (a *ProviderApple) oauth2(ctx context.Context) (*oauth2.Config, error) {\n\t// Apple requires a JWT token that acts as a client secret\n\tsecret, err := a.newClientSecret()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ta.config.ClientSecret = secret\n\n\tendpoint := oauth2.Endpoint{\n\t\tAuthURL:   \"https://appleid.apple.com/auth/authorize\",\n\t\tTokenURL:  \"https://appleid.apple.com/auth/token\",\n\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t}\n\treturn a.oauth2ConfigFromEndpoint(ctx, endpoint), nil\n}\n\nfunc (a *ProviderApple) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn a.oauth2(ctx)\n}\n\nfunc (a *ProviderApple) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\tvar options []oauth2.AuthCodeOption\n\n\tif isForced(r) {\n\t\toptions = append(options, oauth2.SetAuthURLParam(\"prompt\", \"login\"))\n\t}\n\tif len(a.config.RequestedClaims) != 0 {\n\t\toptions = append(options, oauth2.SetAuthURLParam(\"claims\", string(a.config.RequestedClaims)))\n\t}\n\n\t// When requesting email or name, Apple requires the form_post response mode.\n\t// This also means the return url will be called by Apple using POST method.\n\tfor _, scope := range a.config.Scope {\n\t\tif scope == \"email\" || scope == \"name\" {\n\t\t\toptions = append(options, oauth2.SetAuthURLParam(\"response_mode\", \"form_post\"))\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn options\n}\n\nfunc (a *ProviderApple) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\tclaims, err := a.ProviderGenericOIDC.Claims(ctx, exchange, query)\n\tif err != nil {\n\t\treturn claims, err\n\t}\n\ta.DecodeQuery(query, claims)\n\n\treturn claims, nil\n}\n\n// DecodeQuery decodes extra user info from Apple into the given `Claims`.\n// The info is sent as an extra query parameter to the redirect URL.\n// See https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/configuring_your_webpage_for_sign_in_with_apple#3331292\n// Note that there's no way to make sure the info hasn't been tampered with.\nfunc (a *ProviderApple) DecodeQuery(query url.Values, claims *Claims) {\n\tvar user struct {\n\t\tName *struct {\n\t\t\tFirstName *string `json:\"firstName\"`\n\t\t\tLastName  *string `json:\"lastName\"`\n\t\t} `json:\"name\"`\n\t}\n\tif err := json.Unmarshal([]byte(query.Get(\"user\")), &user); err == nil {\n\t\tif name := user.Name; name != nil {\n\t\t\tif firstName := name.FirstName; firstName != nil {\n\t\t\t\tif claims.GivenName == \"\" {\n\t\t\t\t\tclaims.GivenName = *firstName\n\t\t\t\t}\n\t\t\t}\n\t\t\tif lastName := name.LastName; lastName != nil {\n\t\t\t\tif claims.LastName == \"\" {\n\t\t\t\t\tclaims.LastName = *lastName\n\t\t\t\t}\n\t\t\t\tif claims.FamilyName == \"\" {\n\t\t\t\t\tclaims.FamilyName = *lastName\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvar _ IDTokenVerifier = new(ProviderApple)\n\nconst issuerURLApple = \"https://appleid.apple.com\"\n\nfunc (a *ProviderApple) Verify(ctx context.Context, rawIDToken string) (*Claims, error) {\n\tkeySet := oidc.NewRemoteKeySet(ctx, a.JWKSUrl)\n\tctx = oidc.ClientContext(ctx, a.reg.HTTPClient(ctx).HTTPClient)\n\n\treturn verifyToken(ctx, keySet, a.config, rawIDToken, issuerURLApple)\n}\n\nvar _ NonceValidationSkipper = new(ProviderApple)\n\nfunc (a *ProviderApple) CanSkipNonce(c *Claims) bool {\n\treturn c.NonceSupported\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_apple_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t_ \"embed\"\n\n\t\"github.com/golang-jwt/jwt/v4\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nfunc TestDecodeQuery(t *testing.T) {\n\tquery := url.Values{\n\t\t\"user\": []string{`{\"name\": {\"firstName\": \"first\", \"lastName\": \"last\"}, \"email\": \"email@email.com\"}`},\n\t}\n\n\tfor k, tc := range []struct {\n\t\tclaims     *oidc.Claims\n\t\tfamilyName string\n\t\tgivenName  string\n\t\tlastName   string\n\t}{\n\t\t{claims: &oidc.Claims{}, familyName: \"last\", givenName: \"first\", lastName: \"last\"},\n\t\t{claims: &oidc.Claims{FamilyName: \"fam\"}, familyName: \"fam\", givenName: \"first\", lastName: \"last\"},\n\t\t{claims: &oidc.Claims{FamilyName: \"fam\", GivenName: \"giv\"}, familyName: \"fam\", givenName: \"giv\", lastName: \"last\"},\n\t\t{claims: &oidc.Claims{FamilyName: \"fam\", GivenName: \"giv\", LastName: \"las\"}, familyName: \"fam\", givenName: \"giv\", lastName: \"las\"},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\ta := oidc.NewProviderApple(&oidc.Configuration{}, nil).(*oidc.ProviderApple)\n\t\t\ta.DecodeQuery(query, tc.claims)\n\t\t\tassert.Equal(t, tc.familyName, tc.claims.FamilyName)\n\t\t\tassert.Equal(t, tc.givenName, tc.claims.GivenName)\n\t\t\tassert.Equal(t, tc.lastName, tc.claims.LastName)\n\t\t\t// Never extract email from the query, as the same info can be extracted and verified from the ID token.\n\t\t\tassert.Empty(t, tc.claims.Email)\n\t\t})\n\t}\n}\n\nfunc TestAppleVerify(t *testing.T) {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(200)\n\t\t_, _ = w.Write(publicJWKS)\n\t}))\n\n\ttsOtherJWKS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(200)\n\t\t_, _ = w.Write(publicJWKS2)\n\t}))\n\tmakeClaims := func(aud string) jwt.RegisteredClaims {\n\t\treturn jwt.RegisteredClaims{\n\t\t\tIssuer:    \"https://appleid.apple.com\",\n\t\t\tSubject:   \"acme@ory.sh\",\n\t\t\tAudience:  jwt.ClaimStrings{aud},\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),\n\t\t}\n\t}\n\tt.Run(\"case=successful verification\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\tapple := oidc.NewProviderApple(&oidc.Configuration{\n\t\t\tClientID: \"com.example.app\",\n\t\t}, reg).(*oidc.ProviderApple)\n\t\tapple.JWKSUrl = ts.URL\n\t\ttoken := createIDToken(t, makeClaims(\"com.example.app\"))\n\n\t\tc, err := apple.Verify(context.Background(), token)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, \"acme@ory.sh\", c.Email)\n\t\tassert.Equal(t, \"acme@ory.sh\", c.Subject)\n\t\tassert.Equal(t, \"https://appleid.apple.com\", c.Issuer)\n\t})\n\n\tt.Run(\"case=fails due to client_id mismatch\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\tapple := oidc.NewProviderApple(&oidc.Configuration{\n\t\t\tClientID: \"com.example.app\",\n\t\t}, reg).(*oidc.ProviderApple)\n\t\tapple.JWKSUrl = ts.URL\n\t\ttoken := createIDToken(t, makeClaims(\"com.different-example.app\"))\n\n\t\t_, err := apple.Verify(context.Background(), token)\n\t\trequire.Error(t, err)\n\t\tassert.Equal(t, `token audience didn't match allowed audiences: [com.example.app] oidc: expected audience \"com.example.app\" got [\"com.different-example.app\"]`, err.Error())\n\t})\n\n\tt.Run(\"case=fails due to jwks mismatch\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\tapple := oidc.NewProviderApple(&oidc.Configuration{\n\t\t\tClientID: \"com.example.app\",\n\t\t}, reg).(*oidc.ProviderApple)\n\t\tapple.JWKSUrl = tsOtherJWKS.URL\n\t\ttoken := createIDToken(t, makeClaims(\"com.example.app\"))\n\n\t\t_, err := apple.Verify(context.Background(), token)\n\t\trequire.Error(t, err)\n\t\tassert.Equal(t, \"failed to verify signature: failed to verify id token signature\", err.Error())\n\t})\n\n\tt.Run(\"case=succeedes with additional id token audience\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\tapple := oidc.NewProviderApple(&oidc.Configuration{\n\t\t\tClientID:                   \"something.else.app\",\n\t\t\tAdditionalIDTokenAudiences: []string{\"com.example.app\"},\n\t\t}, reg).(*oidc.ProviderApple)\n\t\tapple.JWKSUrl = ts.URL\n\t\ttoken := createIDToken(t, makeClaims(\"com.example.app\"))\n\n\t\t_, err := apple.Verify(context.Background(), token)\n\t\trequire.NoError(t, err)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_auth0.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/url\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n)\n\ntype ProviderAuth0 struct {\n\t*ProviderGenericOIDC\n}\n\nvar _ OAuth2Provider = (*ProviderAuth0)(nil)\n\nfunc NewProviderAuth0(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderAuth0{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (g *ProviderAuth0) oauth2(ctx context.Context) (*oauth2.Config, error) {\n\tendpoint, err := url.Parse(g.config.IssuerURL)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tauthURL := *endpoint\n\ttokenURL := *endpoint\n\n\tauthURL.Path = path.Join(authURL.Path, \"/authorize\")\n\ttokenURL.Path = path.Join(tokenURL.Path, \"/oauth/token\")\n\n\tc := &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\tAuthURL:  authURL.String(),\n\t\t\tTokenURL: tokenURL.String(),\n\t\t},\n\t\tScopes:      g.config.Scope,\n\t\tRedirectURL: g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n\n\treturn c, nil\n}\n\nfunc (g *ProviderAuth0) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn g.oauth2(ctx)\n}\n\nfunc (g *ProviderAuth0) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\to, err := g.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tu, err := url.Parse(g.config.IssuerURL)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tu.Path = path.Join(u.Path, \"/userinfo\")\n\n\tctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), o, exchange)\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", u.String(), nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(g.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Once auth0 fixes this bug, all this workaround can be removed.\n\tb, err := io.ReadAll(io.LimitReader(resp.Body, 1024*1024))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tb, err = authZeroUpdatedAtWorkaround(b)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Once we get here, we know that if there is an updated_at field in the json, it is the correct type.\n\tvar claims Claims\n\tif err := json.Unmarshal(b, &claims); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tclaims.Issuer = cmp.Or(claims.Issuer, g.config.IssuerURL)\n\treturn &claims, nil\n}\n\n// There is a bug in the response from Auth0. The updated_at field may be a string and not an int64.\n// https://community.auth0.com/t/oidc-id-token-claim-updated-at-violates-oidc-specification-breaks-rp-implementations/24098\n// We work around this by reading the json generically (as map[string]inteface{} and looking at the updated_at field\n// if it exists. If it's the wrong type (string), we fill out the claims by hand.\nfunc authZeroUpdatedAtWorkaround(body []byte) ([]byte, error) {\n\t// Force updatedAt to be an int if given as a string in the response.\n\tif updatedAtField := gjson.GetBytes(body, \"updated_at\"); updatedAtField.Exists() && updatedAtField.Type == gjson.String {\n\t\tt, err := time.Parse(time.RFC3339, updatedAtField.String())\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"bad time format in updated_at\"))\n\t\t}\n\t\tbody, err = sjson.SetBytes(body, \"updated_at\", t.Unix())\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t\t}\n\t}\n\treturn body, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_auth0_unit_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestAuthZeroUpdatedAtWorkaround(t *testing.T) {\n\tactual, err := authZeroUpdatedAtWorkaround([]byte(\"{}\"))\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"{}\", string(actual))\n\n\tactual, err = authZeroUpdatedAtWorkaround([]byte(`{\"updated_at\":1234}`))\n\trequire.NoError(t, err)\n\tassert.Equal(t, `{\"updated_at\":1234}`, string(actual))\n\n\ttimestamp := time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)\n\tinput, err := json.Marshal(map[string]interface{}{\n\t\t\"updated_at\": timestamp,\n\t})\n\trequire.NoError(t, err)\n\tactual, err = authZeroUpdatedAtWorkaround(input)\n\trequire.NoError(t, err)\n\tassert.Equal(t, fmt.Sprintf(`{\"updated_at\":%d}`, timestamp.Unix()), string(actual))\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_config.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"encoding/json\"\n\t\"maps\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst (\n\t// AccountLinkingModeVerifyWithExistingCredential requires the user to verify their identity\n\t// with an existing credential before accounts are linked.\n\tAccountLinkingModeVerifyWithExistingCredential = \"confirm_with_existing_credential\"\n\t// AccountLinkingModeAutomatic silently links accounts if the provider verifies email ownership.\n\tAccountLinkingModeAutomatic = \"automatic\"\n\n\t// ProviderTypeApple is the provider type for Sign in with Apple.\n\tProviderTypeApple = \"apple\"\n\t// ProviderTypeGoogle is the provider type for Sign in with Google.\n\tProviderTypeGoogle = \"google\"\n)\n\ntype Configuration struct {\n\t// ID is the provider's ID\n\tID string `json:\"id\"`\n\n\t// Provider is either \"generic\" for a generic OAuth 2.0 / OpenID Connect Provider or one of:\n\t// - generic\n\t// - google\n\t// - github\n\t// - github-app\n\t// - gitlab\n\t// - microsoft\n\t// - discord\n\t// - salesforce\n\t// - slack\n\t// - facebook\n\t// - auth0\n\t// - vk\n\t// - yandex\n\t// - apple\n\t// - spotify\n\t// - netid\n\t// - dingtalk\n\t// - linkedin\n\t// - patreon\n\t// - amazon\n\t// - uaepass\n\tProvider string `json:\"provider\"`\n\n\t// Label represents an optional label which can be used in the UI generation.\n\tLabel string `json:\"label\"`\n\n\t// ClientID is the application's Client ID.\n\tClientID string `json:\"client_id\"`\n\n\t// ClientSecret is the application's secret.\n\tClientSecret string `json:\"client_secret\"`\n\n\t// IssuerURL is the OpenID Connect Server URL. You can leave this empty if `provider` is not set to `generic`.\n\t// If set, neither `auth_url` nor `token_url` are required.\n\tIssuerURL string `json:\"issuer_url\"`\n\n\t// AuthURL is the authorize url, typically something like: https://example.org/oauth2/auth\n\t// Should only be used when the OAuth2 / OpenID Connect server is not supporting OpenID Connect Discovery and when\n\t// `provider` is set to `generic`.\n\tAuthURL string `json:\"auth_url\"`\n\n\t// TokenURL is the token url, typically something like: https://example.org/oauth2/token\n\t// Should only be used when the OAuth2 / OpenID Connect server is not supporting OpenID Connect Discovery and when\n\t// `provider` is set to `generic`.\n\tTokenURL string `json:\"token_url\"`\n\n\t// Tenant is the Azure AD Tenant to use for authentication, and must be set when `provider` is set to `microsoft`.\n\t// Can be either `common`, `organizations`, `consumers` for a multitenant application or a specific tenant like\n\t// `8eaef023-2b34-4da1-9baa-8bc8c9d6a490` or `contoso.onmicrosoft.com`.\n\tTenant string `json:\"microsoft_tenant\"`\n\n\t// SubjectSource is a flag which controls from which endpoint the subject identifier is taken by microsoft provider.\n\t// Can be either `userinfo` or `me` or `oid`.\n\t// If the value is `userinfo` then the subject identifier is taken from sub field of userinfo standard endpoint response.\n\t// If the value is `me` then the `id` field of https://graph.microsoft.com/v1.0/me response is taken as subject.\n\t// If the value is `oid` then the the oid (Object ID) is taken to identify users across different services.\n\t// The default is `userinfo`.\n\tSubjectSource string `json:\"subject_source\"`\n\n\t// TeamId is the Apple Developer Team ID that's needed for the `apple` `provider` to work.\n\t// It can be found Apple Developer website and combined with `apple_private_key` and `apple_private_key_id`\n\t// is used to generate `client_secret`\n\tTeamId string `json:\"apple_team_id\"`\n\n\t// PrivateKeyId is the private Apple key identifier. Keys can be generated via developer.apple.com.\n\t// This key should be generated with the `Sign In with Apple` option checked.\n\t// This is needed when `provider` is set to `apple`\n\tPrivateKeyId string `json:\"apple_private_key_id\"`\n\n\t// PrivateKeyId is the Apple private key identifier that can be downloaded during key generation.\n\t// This is needed when `provider` is set to `apple`\n\tPrivateKey string `json:\"apple_private_key\"`\n\n\t// Scope specifies optional requested permissions.\n\tScope []string `json:\"scope\"`\n\n\t// Mapper specifies the JSONNet code snippet which uses the OpenID Connect Provider's data (e.g. GitHub or Google\n\t// profile information) to hydrate the identity's data.\n\t//\n\t// It can be either a URL (file://, http(s)://, base64://) or an inline JSONNet code snippet.\n\tMapper string `json:\"mapper_url\"`\n\n\t// RequestedClaims is a string encoded json object that specifies claims and optionally their properties that should be\n\t// included in the id_token or returned from the UserInfo Endpoint.\n\t//\n\t// More information: https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter\n\tRequestedClaims json.RawMessage `json:\"requested_claims\"`\n\n\t// An optional organization ID that this provider belongs to.\n\t// This parameter is only effective in the Ory Network.\n\tOrganizationID string `json:\"organization_id\"`\n\n\t// AdditionalIDTokenAudiences is a list of additional audiences allowed in the ID Token.\n\t// This is only relevant in OIDC flows that submit an IDToken instead of using the callback from the OIDC provider.\n\tAdditionalIDTokenAudiences []string `json:\"additional_id_token_audiences\"`\n\n\t// ClaimsSource is a flag which controls where the claims are taken from when\n\t// using the generic provider. Can be either `userinfo` (calls the userinfo\n\t// endpoint to get the claims) or `id_token` (takes the claims from the id\n\t// token). It defaults to `id_token`.\n\tClaimsSource string `json:\"claims_source\"`\n\n\t// PKCE controls if the OpenID Connect OAuth2 flow should use PKCE (Proof Key for Code Exchange).\n\t// Possible values are: `auto` (default), `never`, `force`.\n\t// - `auto`: PKCE is used if the provider supports it. Requires setting `issuer_url`.\n\t// - `never`: Disable PKCE entirely for this provider, even if the provider advertises support for it.\n\t// - `force`: Always use PKCE, even if the provider does not advertise support for it. OAuth2 flows will fail if the provider does not support PKCE.\n\t// IMPORTANT: If you set this to `force`, you must whitelist a different return URL for your OAuth2 client in the provider's configuration.\n\t// Instead of <base-url>/self-service/methods/oidc/callback/<provider>, you must use <base-url>/self-service/methods/oidc/callback\n\t// (Note the missing <provider> path segment and no trailing slash).\n\tPKCE string `json:\"pkce\"`\n\n\t// FedCMConfigURL is the URL to the FedCM IdP configuration file.\n\t// This is only effective in the Ory Network.\n\tFedCMConfigURL string `json:\"fedcm_config_url\"`\n\n\t// NetIDTokenOriginHeader contains the orgin header to be used when exchanging a\n\t// NetID FedCM token for an ID token.\n\tNetIDTokenOriginHeader string `json:\"net_id_token_origin_header\"`\n\n\t// AccountLinkingMode controls how account conflicts are resolved for this provider.\n\t// Possible values: \"confirm_with_existing_credential\" (default), \"automatic\".\n\t// This is only effective in the Ory Network.\n\tAccountLinkingMode string `json:\"account_linking_mode,omitempty\"`\n}\n\nfunc (p Configuration) Redir(public *url.URL) string {\n\tif p.PKCE == \"force\" {\n\t\treturn urlx.AppendPaths(public, RouteCallbackGeneric).String()\n\t}\n\n\tif p.OrganizationID != \"\" {\n\t\troute := RouteOrganizationCallback\n\t\troute = strings.Replace(route, \"{provider}\", p.ID, 1)\n\t\troute = strings.Replace(route, \"{organization}\", p.OrganizationID, 1)\n\t\treturn urlx.AppendPaths(public, route).String()\n\t}\n\n\treturn urlx.AppendPaths(public, strings.Replace(RouteCallback, \"{provider}\", p.ID, 1)).String()\n}\n\ntype ConfigurationCollection struct {\n\tBaseRedirectURI string          `json:\"base_redirect_uri\"`\n\tProviders       []Configuration `json:\"providers\"`\n}\n\n// !!! WARNING !!!\n//\n// If you add a provider here, please also add a test to\n// provider_private_net_test.go\nvar supportedProviders = map[string]func(config *Configuration, reg Dependencies) Provider{\n\t\"generic\":     NewProviderGenericOIDC,\n\t\"google\":      NewProviderGoogle,\n\t\"github\":      NewProviderGitHub,\n\t\"github-app\":  NewProviderGitHubApp,\n\t\"gitlab\":      NewProviderGitLab,\n\t\"microsoft\":   NewProviderMicrosoft,\n\t\"discord\":     NewProviderDiscord,\n\t\"salesforce\":  NewProviderSalesforce,\n\t\"slack\":       NewProviderSlack,\n\t\"facebook\":    NewProviderFacebook,\n\t\"auth0\":       NewProviderAuth0,\n\t\"vk\":          NewProviderVK,\n\t\"yandex\":      NewProviderYandex,\n\t\"apple\":       NewProviderApple,\n\t\"spotify\":     NewProviderSpotify,\n\t\"netid\":       NewProviderNetID,\n\t\"dingtalk\":    NewProviderDingTalk,\n\t\"linkedin\":    NewProviderLinkedIn,\n\t\"linkedin_v2\": NewProviderLinkedInV2,\n\t\"patreon\":     NewProviderPatreon,\n\t\"lark\":        NewProviderLark,\n\t\"x\":           NewProviderX,\n\t\"line\":        NewProviderLineV21,\n\t\"jackson\":     NewProviderJackson,\n\t\"fedcm-test\":  NewProviderTestFedcm,\n\t\"amazon\":      NewProviderAmazon,\n\t\"uaepass\":     NewProviderUAEPass,\n}\n\nfunc (c ConfigurationCollection) Provider(id string, reg Dependencies) (Provider, error) {\n\tfor _, p := range c.Providers {\n\t\tif p.ID == id {\n\t\t\tif f, ok := supportedProviders[p.Provider]; ok {\n\t\t\t\treturn f(&p, reg), nil\n\t\t\t}\n\n\t\t\treturn nil, errors.Errorf(\"provider type %s is not supported, supported are: %v\", p.Provider, maps.Keys(supportedProviders))\n\t\t}\n\t}\n\treturn nil, errors.WithStack(herodot.ErrNotFound.WithReasonf(`OpenID Connect Provider \"%s\" is unknown or has not been configured`, id))\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_config_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nfunc TestConfig(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tvar c map[string]interface{}\n\trequire.NoError(t, json.NewDecoder(\n\t\tbytes.NewBufferString(`{\"config\":{\"providers\": [{\"provider\": \"generic\"}]}}`)).Decode(&c))\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeOIDC), c)\n\n\ts := oidc.NewStrategy(reg)\n\tcollection, err := s.Config(ctx)\n\trequire.NoError(t, err)\n\n\trequire.Len(t, collection.Providers, 1)\n\tassert.Equal(t, \"generic\", collection.Providers[0].Provider)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_dingtalk.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/ory/herodot\"\n)\n\ntype ProviderDingTalk struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nvar _ OAuth2Provider = (*ProviderDingTalk)(nil)\n\nfunc NewProviderDingTalk(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderDingTalk{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (g *ProviderDingTalk) Config() *Configuration {\n\treturn g.config\n}\n\nfunc (g *ProviderDingTalk) oauth2(ctx context.Context) *oauth2.Config {\n\tendpoint := oauth2.Endpoint{\n\t\tAuthURL:  \"https://login.dingtalk.com/oauth2/auth\",\n\t\tTokenURL: \"https://api.dingtalk.com/v1.0/oauth2/userAccessToken\",\n\t}\n\n\treturn &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint:     endpoint,\n\t\t// DingTalk only allow to set scopes: openid or openid corpid\n\t\tScopes:      g.config.Scope,\n\t\tRedirectURL: g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (g *ProviderDingTalk) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{\n\t\toauth2.SetAuthURLParam(\"prompt\", \"consent\"),\n\t}\n}\n\nfunc (g *ProviderDingTalk) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn g.oauth2(ctx), nil\n}\n\nfunc (g *ProviderDingTalk) ExchangeOAuth2Token(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\tconf, err := g.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpTokenParams := &struct {\n\t\tClientID     string `json:\"clientId\"`\n\t\tClientSecret string `json:\"clientSecret\"`\n\t\tCode         string `json:\"code\"`\n\t\tGrantType    string `json:\"grantType\"`\n\t}{conf.ClientID, conf.ClientSecret, code, \"authorization_code\"}\n\tbs, err := json.Marshal(pTokenParams)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tr := strings.NewReader(string(bs))\n\tclient := g.reg.HTTPClient(ctx, httpx.ResilientClientDisallowInternalIPs())\n\treq, err := retryablehttp.NewRequest(\"POST\", conf.Endpoint.TokenURL, r)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\treq.Header.Add(\"Content-Type\", \"application/json;charset=UTF-8\")\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(g.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar dToken struct {\n\t\tErrCode     int    `json:\"code\"`\n\t\tErrMsg      string `json:\"message\"`\n\t\tAccessToken string `json:\"accessToken\"` // Interface call credentials\n\t\tExpiresIn   int64  `json:\"expireIn\"`    // access_token interface call credential timeout time, unit (seconds)\n\t}\n\n\tif err := json.NewDecoder(resp.Body).Decode(&dToken); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tif dToken.ErrCode != 0 {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"dToken.ErrCode = %d, dToken.ErrMsg = %s\", dToken.ErrCode, dToken.ErrMsg))\n\t}\n\n\ttoken := &oauth2.Token{\n\t\tAccessToken: dToken.AccessToken,\n\t\tExpiry:      time.Unix(time.Now().Unix()+int64(dToken.ExpiresIn), 0),\n\t}\n\treturn token, nil\n}\n\nfunc (g *ProviderDingTalk) Claims(ctx context.Context, exchange *oauth2.Token, _ url.Values) (*Claims, error) {\n\tuserInfoURL := \"https://api.dingtalk.com/v1.0/contact/users/me\"\n\taccessToken := exchange.AccessToken\n\n\tclient := g.reg.HTTPClient(ctx, httpx.ResilientClientDisallowInternalIPs())\n\treq, err := retryablehttp.NewRequest(\"GET\", userInfoURL, nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\treq.Header.Add(\"x-acs-dingtalk-access-token\", accessToken)\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(g.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar user struct {\n\t\tNick      string `json:\"nick\"`\n\t\tOpenID    string `json:\"openId\"`\n\t\tAvatarURL string `json:\"avatarUrl\"`\n\t\tEmail     string `json:\"email\"`\n\t\tErrMsg    string `json:\"message\"`\n\t\tErrCode   string `json:\"code\"`\n\t}\n\n\tif err := json.NewDecoder(resp.Body).Decode(&user); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tif user.ErrMsg != \"\" {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"userResp.ErrCode = %s, userResp.ErrMsg = %s\", user.ErrCode, user.ErrMsg))\n\t}\n\n\treturn &Claims{\n\t\tIssuer:   userInfoURL,\n\t\tSubject:  user.OpenID,\n\t\tNickname: user.Nick,\n\t\tName:     user.Nick,\n\t\tPicture:  user.AvatarURL,\n\t\tEmail:    user.Email,\n\t}, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_discord.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"slices\"\n\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/bwmarrin/discordgo\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/x/stringsx\"\n)\n\nvar _ OAuth2Provider = (*ProviderDiscord)(nil)\n\ntype ProviderDiscord struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc NewProviderDiscord(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderDiscord{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (d *ProviderDiscord) Config() *Configuration {\n\treturn d.config\n}\n\nfunc (d *ProviderDiscord) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     d.config.ClientID,\n\t\tClientSecret: d.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\tAuthURL:  discordgo.EndpointOauth2 + \"authorize\",\n\t\t\tTokenURL: discordgo.EndpointOauth2 + \"token\",\n\t\t},\n\t\tRedirectURL: d.config.Redir(d.reg.Config().OIDCRedirectURIBase(ctx)),\n\t\tScopes:      d.config.Scope,\n\t}\n}\n\nfunc (d *ProviderDiscord) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn d.oauth2(ctx), nil\n}\n\nfunc (d *ProviderDiscord) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\tif isForced(r) {\n\t\treturn []oauth2.AuthCodeOption{\n\t\t\toauth2.SetAuthURLParam(\"prompt\", \"consent\"),\n\t\t}\n\t}\n\treturn []oauth2.AuthCodeOption{\n\t\toauth2.SetAuthURLParam(\"prompt\", \"none\"),\n\t}\n}\n\nfunc (d *ProviderDiscord) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\tgrantedScopes := stringsx.Splitx(fmt.Sprintf(\"%s\", exchange.Extra(\"scope\")), \" \")\n\tfor _, check := range d.Config().Scope {\n\t\tif !slices.Contains(grantedScopes, check) {\n\t\t\treturn nil, errors.WithStack(ErrScopeMissing)\n\t\t}\n\t}\n\n\tdg, err := discordgo.New(fmt.Sprintf(\"Bearer %s\", exchange.AccessToken))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"%s\", err))\n\t}\n\n\tuser, err := dg.User(\"@me\")\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tclaims := &Claims{\n\t\tIssuer:            discordgo.EndpointOauth2,\n\t\tSubject:           user.ID,\n\t\tName:              fmt.Sprintf(\"%s#%s\", user.Username, user.Discriminator),\n\t\tNickname:          user.Username,\n\t\tPreferredUsername: user.Username,\n\t\tPicture:           user.AvatarURL(\"\"),\n\t\tEmail:             user.Email,\n\t\tEmailVerified:     x.ConvertibleBoolean(user.Verified),\n\t\tLocale:            Locale(user.Locale),\n\t}\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_facebook.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"crypto/hmac\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/url\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar _ OAuth2Provider = (*ProviderFacebook)(nil)\n\ntype ProviderFacebook struct {\n\t*ProviderGenericOIDC\n}\n\nfunc NewProviderFacebook(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\tconfig.IssuerURL = \"https://www.facebook.com\"\n\treturn &ProviderFacebook{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (g *ProviderFacebook) generateAppSecretProof(token *oauth2.Token) string {\n\tsecret := g.config.ClientSecret\n\tdata := token.AccessToken\n\n\th := hmac.New(sha256.New, []byte(secret))\n\th.Write([]byte(data))\n\treturn hex.EncodeToString(h.Sum(nil))\n}\n\nfunc (g *ProviderFacebook) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\tp, err := g.provider(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tendpoint := p.Endpoint()\n\tendpoint.AuthURL = \"https://facebook.com/dialog/oauth\"\n\tendpoint.TokenURL = \"https://graph.facebook.com/oauth/access_token\"\n\treturn g.oauth2ConfigFromEndpoint(ctx, endpoint), nil\n}\n\nfunc (g *ProviderFacebook) Claims(ctx context.Context, token *oauth2.Token, query url.Values) (*Claims, error) {\n\to, err := g.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tappSecretProof := g.generateAppSecretProof(token)\n\t// Do not use the versioned Graph API here. If you do, it will break once the version is deprecated. See also:\n\t//\n\t// When you use https://graph.facebook.com/me without specifying a version, Facebook defaults to the oldest\n\t// available version your app supports. This behavior ensures backward compatibility but can lead to unintended\n\t// issues if that version becomes deprecated.\n\tu, err := url.Parse(fmt.Sprintf(\"https://graph.facebook.com/me?fields=id,name,first_name,last_name,middle_name,email,picture,birthday,gender&appsecret_proof=%s\", appSecretProof))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), o, token)\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", u.String(), nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(g.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar user struct {\n\t\tID            string `json:\"id,omitempty\"`\n\t\tName          string `json:\"name,omitempty\"`\n\t\tFirstName     string `json:\"first_name,omitempty\"`\n\t\tLastName      string `json:\"last_name,omitempty\"`\n\t\tMiddleName    string `json:\"middle_name,omitempty\"`\n\t\tEmail         string `json:\"email,omitempty\"`\n\t\tEmailVerified bool\n\t\tPicture       struct {\n\t\t\tData struct {\n\t\t\t\tURL string `json:\"url,omitempty\"`\n\t\t\t} `json:\"data,omitempty\"`\n\t\t} `json:\"picture,omitempty\"`\n\t\tBirthDay string `json:\"birthday,omitempty\"`\n\t\tGender   string `json:\"gender,omitempty\"`\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(&user); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"%s\", err))\n\t}\n\n\tif user.Email != \"\" {\n\t\tuser.EmailVerified = true\n\t}\n\n\treturn &Claims{\n\t\tIssuer:            u.String(),\n\t\tSubject:           user.ID,\n\t\tName:              user.Name,\n\t\tGivenName:         user.FirstName,\n\t\tFamilyName:        user.LastName,\n\t\tMiddleName:        user.MiddleName,\n\t\tNickname:          user.Name,\n\t\tPreferredUsername: user.Name,\n\t\tPicture:           user.Picture.Data.URL,\n\t\tEmail:             user.Email,\n\t\tEmailVerified:     x.ConvertibleBoolean(user.EmailVerified),\n\t\tGender:            user.Gender,\n\t\tBirthdate:         user.BirthDay,\n\t}, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_generic_oidc.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\t\"slices\"\n\t\"time\"\n\n\tgooidc \"github.com/coreos/go-oidc/v3/oidc\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/reqlog\"\n)\n\nvar _ OAuth2Provider = (*ProviderGenericOIDC)(nil)\n\ntype ProviderGenericOIDC struct {\n\tp      *gooidc.Provider\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc NewProviderGenericOIDC(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderGenericOIDC{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nconst (\n\tClaimsSourceIDToken  = \"id_token\"\n\tClaimsSourceUserInfo = \"userinfo\"\n)\n\nfunc (g *ProviderGenericOIDC) Config() *Configuration {\n\treturn g.config\n}\n\nfunc (g *ProviderGenericOIDC) withHTTPClientContext(ctx context.Context) context.Context {\n\treturn gooidc.ClientContext(ctx, g.reg.HTTPClient(ctx).HTTPClient)\n}\n\nfunc (g *ProviderGenericOIDC) provider(ctx context.Context) (*gooidc.Provider, error) {\n\tif g.p == nil {\n\t\tp, err := gooidc.NewProvider(g.withHTTPClientContext(ctx), g.config.IssuerURL)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"Unable to initialize OpenID Connect Provider: %s\", err))\n\t\t}\n\t\tg.p = p\n\t}\n\treturn g.p, nil\n}\n\nfunc (g *ProviderGenericOIDC) oauth2ConfigFromEndpoint(ctx context.Context, endpoint oauth2.Endpoint) *oauth2.Config {\n\tscope := g.config.Scope\n\tif !slices.Contains(scope, gooidc.ScopeOpenID) {\n\t\tscope = append(scope, gooidc.ScopeOpenID)\n\t}\n\n\treturn &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint:     endpoint,\n\t\tScopes:       scope,\n\t\tRedirectURL:  g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (g *ProviderGenericOIDC) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\tp, err := g.provider(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tendpoint := p.Endpoint()\n\treturn g.oauth2ConfigFromEndpoint(ctx, endpoint), nil\n}\n\nfunc (g *ProviderGenericOIDC) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\tvar options []oauth2.AuthCodeOption\n\n\tif isForced(r) {\n\t\toptions = append(options, oauth2.SetAuthURLParam(\"prompt\", \"login\"))\n\t}\n\tif len(g.config.RequestedClaims) != 0 {\n\t\toptions = append(options, oauth2.SetAuthURLParam(\"claims\", string(g.config.RequestedClaims)))\n\t}\n\n\treturn options\n}\n\nfunc (g *ProviderGenericOIDC) verifyAndDecodeClaimsWithProvider(ctx context.Context, provider *gooidc.Provider, raw string) (*Claims, error) {\n\ttoken, err := provider.VerifierContext(g.withHTTPClientContext(ctx), &gooidc.Config{ClientID: g.config.ClientID}).Verify(ctx, raw)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"%s\", err))\n\t}\n\n\tvar claims Claims\n\tif err := token.Claims(&claims); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"%s\", err))\n\t}\n\n\tvar rawClaims map[string]interface{}\n\tif err := token.Claims(&rawClaims); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"%s\", err))\n\t}\n\tclaims.RawClaims = rawClaims\n\n\treturn &claims, nil\n}\n\nfunc (g *ProviderGenericOIDC) Claims(ctx context.Context, exchange *oauth2.Token, _ url.Values) (*Claims, error) {\n\tswitch g.config.ClaimsSource {\n\tcase ClaimsSourceIDToken, \"\":\n\t\treturn g.claimsFromIDToken(ctx, exchange)\n\tcase ClaimsSourceUserInfo:\n\t\treturn g.claimsFromUserInfo(ctx, exchange)\n\t}\n\n\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.\n\t\tWithReasonf(\"Unknown claims source: %q\", g.config.ClaimsSource))\n}\n\nfunc (g *ProviderGenericOIDC) claimsFromUserInfo(ctx context.Context, exchange *oauth2.Token) (*Claims, error) {\n\tp, err := g.provider(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tt0 := time.Now()\n\tuserInfo, err := p.UserInfo(g.withHTTPClientContext(ctx), oauth2.StaticTokenSource(exchange))\n\treqlog.AccumulateExternalLatency(ctx, time.Since(t0))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar claims Claims\n\tif err = userInfo.Claims(&claims); err != nil {\n\t\treturn nil, err\n\t}\n\tvar rawClaims map[string]interface{}\n\tif err := userInfo.Claims(&rawClaims); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"%s\", err))\n\t}\n\tclaims.RawClaims = rawClaims\n\n\t// NOTE: Due to the possibility of token substitution attacks (see Section\n\t// 16.11), the UserInfo Response is not guaranteed to be about the End-User\n\t// identified by the sub (subject) element of the ID Token. The sub Claim in the\n\t// UserInfo Response MUST be verified to exactly match the sub Claim in the ID\n\t// Token; if they do not match, the UserInfo Response values MUST NOT be used.\n\t// See https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse\n\tidToken, err := g.verifiedIDToken(ctx, exchange)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif idToken.Subject != claims.Subject {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReason(\"sub (Subject) claim mismatch between ID token and UserInfo endpoint\"))\n\t}\n\n\t// If signed, the UserInfo Response MUST contain the Claims iss (issuer) and aud\n\t// (audience) as members. The iss value MUST be the OP's Issuer Identifier URL.\n\t// The aud value MUST be or include the RP's Client ID value.\n\t// See https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse\n\t//\n\t// Consequently, the issuer might not be present in the UserInfo response and we\n\t// need to set it here.\n\tif claims.Issuer == \"\" {\n\t\tclaims.Issuer = idToken.Issuer\n\t}\n\n\treturn &claims, nil\n}\n\nfunc (g *ProviderGenericOIDC) claimsFromIDToken(ctx context.Context, exchange *oauth2.Token) (*Claims, error) {\n\tp, raw, err := g.idTokenAndProvider(ctx, exchange)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn g.verifyAndDecodeClaimsWithProvider(ctx, p, raw)\n}\n\nfunc (g *ProviderGenericOIDC) idTokenAndProvider(ctx context.Context, exchange *oauth2.Token) (*gooidc.Provider, string, error) {\n\traw, ok := exchange.Extra(\"id_token\").(string)\n\tif !ok || len(raw) == 0 {\n\t\treturn nil, \"\", errors.WithStack(ErrIDTokenMissing)\n\t}\n\n\tp, err := g.provider(ctx)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\treturn p, raw, nil\n}\n\nfunc (g *ProviderGenericOIDC) verifiedIDToken(ctx context.Context, exchange *oauth2.Token) (*gooidc.IDToken, error) {\n\tp, raw, err := g.idTokenAndProvider(ctx, exchange)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttoken, err := p.VerifierContext(g.withHTTPClientContext(ctx), &gooidc.Config{ClientID: g.config.ClientID}).Verify(ctx, raw)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"%s\", err))\n\t}\n\n\treturn token, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_generic_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc makeOIDCClaims() json.RawMessage {\n\tclaims, _ := json.Marshal(map[string]interface{}{\n\t\t\"id_token\": map[string]interface{}{\n\t\t\t\"email\": map[string]bool{\n\t\t\t\t\"essential\": true,\n\t\t\t},\n\t\t\t\"email_verified\": map[string]bool{\n\t\t\t\t\"essential\": true,\n\t\t\t},\n\t\t},\n\t})\n\treturn claims\n}\n\nfunc makeAuthCodeURL(t *testing.T, r *login.Flow, reg *driver.RegistryDefault) string {\n\tp := oidc.NewProviderGenericOIDC(&oidc.Configuration{\n\t\tProvider:        \"generic\",\n\t\tID:              \"valid\",\n\t\tClientID:        \"client\",\n\t\tClientSecret:    \"secret\",\n\t\tIssuerURL:       \"https://accounts.google.com\",\n\t\tMapper:          \"file://./stub/hydra.schema.json\",\n\t\tRequestedClaims: makeOIDCClaims(),\n\t}, reg)\n\tc, err := p.(oidc.OAuth2Provider).OAuth2(context.Background())\n\trequire.NoError(t, err)\n\treturn c.AuthCodeURL(\"state\", p.(oidc.OAuth2Provider).AuthCodeURLOptions(r)...)\n}\n\nfunc TestProviderGenericOIDC_AddAuthCodeURLOptions(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, \"https://ory.sh\")\n\tt.Run(\"case=redirectURI is public base url\", func(t *testing.T) {\n\t\tr := &login.Flow{ID: x.NewUUID(), Refresh: true}\n\t\tactual, err := url.ParseRequestURI(makeAuthCodeURL(t, r, reg))\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, actual.Query().Get(\"redirect_uri\"), \"https://ory.sh\")\n\t})\n\n\tt.Run(\"case=redirectURI is public base url\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeyOIDCBaseRedirectURL, \"https://example.org\")\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeyOIDCBaseRedirectURL, nil)\n\t\t})\n\t\tr := &login.Flow{ID: x.NewUUID(), Refresh: true}\n\t\tactual, err := url.ParseRequestURI(makeAuthCodeURL(t, r, reg))\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, actual.Query().Get(\"redirect_uri\"), \"https://example.org\")\n\t})\n\n\tt.Run(\"case=expect prompt to be login with forced flag\", func(t *testing.T) {\n\t\tr := &login.Flow{\n\t\t\tID:      x.NewUUID(),\n\t\t\tRefresh: true,\n\t\t}\n\t\tassert.Contains(t, makeAuthCodeURL(t, r, reg), \"prompt=login\")\n\t})\n\n\tt.Run(\"case=expect prompt to not be login without forced flag\", func(t *testing.T) {\n\t\tr := &login.Flow{\n\t\t\tID: x.NewUUID(),\n\t\t}\n\t\tassert.NotContains(t, makeAuthCodeURL(t, r, reg), \"prompt=login\")\n\t})\n\n\tt.Run(\"case=expect requested claims to be set\", func(t *testing.T) {\n\t\tr := &login.Flow{\n\t\t\tID: x.NewUUID(),\n\t\t}\n\t\tassert.Contains(t, makeAuthCodeURL(t, r, reg), \"claims=\"+url.QueryEscape(string(makeOIDCClaims())))\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_github.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"slices\"\n\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\t\"golang.org/x/oauth2/github\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/stringsx\"\n\n\tghapi \"github.com/google/go-github/v38/github\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar _ OAuth2Provider = (*ProviderGitHub)(nil)\n\ntype ProviderGitHub struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc NewProviderGitHub(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderGitHub{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (g *ProviderGitHub) Config() *Configuration {\n\treturn g.config\n}\n\nfunc (g *ProviderGitHub) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint:     github.Endpoint,\n\t\tScopes:       g.config.Scope,\n\t\tRedirectURL:  g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (g *ProviderGitHub) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn g.oauth2(ctx), nil\n}\n\nfunc (g *ProviderGitHub) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{}\n}\n\nfunc (g *ProviderGitHub) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\tgrantedScopes := stringsx.Splitx(fmt.Sprintf(\"%s\", exchange.Extra(\"scope\")), \",\")\n\tfor _, check := range g.Config().Scope {\n\t\tif !slices.Contains(grantedScopes, check) {\n\t\t\treturn nil, errors.WithStack(ErrScopeMissing)\n\t\t}\n\t}\n\n\tctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), g.oauth2(ctx), exchange)\n\tgh := ghapi.NewClient(client.HTTPClient)\n\n\tuser, _, err := gh.Users.Get(ctx, \"\")\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tclaims := &Claims{\n\t\tSubject:   fmt.Sprintf(\"%d\", user.GetID()),\n\t\tIssuer:    github.Endpoint.TokenURL,\n\t\tName:      user.GetName(),\n\t\tNickname:  user.GetLogin(),\n\t\tWebsite:   user.GetBlog(),\n\t\tPicture:   user.GetAvatarURL(),\n\t\tProfile:   user.GetHTMLURL(),\n\t\tUpdatedAt: user.GetUpdatedAt().Unix(),\n\t}\n\n\t// GitHub does not provide the user's private emails in the call to `/user`. Therefore, if scope \"user:email\" is set,\n\t// we want to make another request to `/user/emails` and merge that with our claims.\n\tif slices.Contains(grantedScopes, \"user:email\") {\n\t\temails, _, err := gh.Users.ListEmails(ctx, nil)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t\t}\n\n\t\tfor k, e := range emails {\n\t\t\t// If it is the primary email or it's the last email (no primary email set?), set the email.\n\t\t\tif e.GetPrimary() || k == len(emails) {\n\t\t\t\tclaims.Email = e.GetEmail()\n\t\t\t\tclaims.EmailVerified = x.ConvertibleBoolean(e.GetVerified())\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_github_app.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\t\"golang.org/x/oauth2/github\"\n\n\tghapi \"github.com/google/go-github/v38/github\"\n\n\t\"github.com/ory/herodot\"\n)\n\ntype ProviderGitHubApp struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc NewProviderGitHubApp(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderGitHubApp{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (g *ProviderGitHubApp) Config() *Configuration {\n\treturn g.config\n}\n\nfunc (g *ProviderGitHubApp) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint:     github.Endpoint,\n\t\tScopes:       g.config.Scope,\n\t\tRedirectURL:  g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (g *ProviderGitHubApp) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn g.oauth2(ctx), nil\n}\n\nfunc (g *ProviderGitHubApp) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{}\n}\n\nfunc (g *ProviderGitHubApp) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\tctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), g.oauth2(ctx), exchange)\n\tgh := ghapi.NewClient(client.HTTPClient)\n\n\tuser, _, err := gh.Users.Get(ctx, \"\")\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tclaims := &Claims{\n\t\tSubject:   fmt.Sprintf(\"%d\", user.GetID()),\n\t\tIssuer:    github.Endpoint.TokenURL,\n\t\tName:      user.GetName(),\n\t\tNickname:  user.GetLogin(),\n\t\tWebsite:   user.GetBlog(),\n\t\tPicture:   user.GetAvatarURL(),\n\t\tProfile:   user.GetHTMLURL(),\n\t\tUpdatedAt: user.GetUpdatedAt().Unix(),\n\t}\n\n\t// GitHub does not provide the user's private emails in the call to `/user`. Therefore, if scope \"user:email\" is set,\n\t// we want to make another request to `/user/emails` and merge that with our claims.\n\temails, _, err := gh.Users.ListEmails(ctx, nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tfor k, e := range emails {\n\t\t// If it is the primary email or it's the last email (no primary email set?), set the email.\n\t\tif e.GetPrimary() || k == len(emails)-1 {\n\t\t\tclaims.Email = e.GetEmail()\n\t\t\tclaims.EmailVerified = x.ConvertibleBoolean(e.GetVerified())\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_gitlab.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"path\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n)\n\nconst (\n\tdefaultEndpoint = \"https://gitlab.com\"\n)\n\nvar _ OAuth2Provider = (*ProviderGitLab)(nil)\n\ntype ProviderGitLab struct {\n\t*ProviderGenericOIDC\n}\n\nfunc NewProviderGitLab(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderGitLab{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (g *ProviderGitLab) oauth2(ctx context.Context) (*oauth2.Config, error) {\n\tendpoint, err := g.endpoint()\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tauthURL := *endpoint\n\ttokenURL := *endpoint\n\n\tauthURL.Path = path.Join(authURL.Path, \"/oauth/authorize\")\n\ttokenURL.Path = path.Join(tokenURL.Path, \"/oauth/token\")\n\n\treturn &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\tAuthURL:  authURL.String(),\n\t\t\tTokenURL: tokenURL.String(),\n\t\t},\n\t\tScopes:      g.config.Scope,\n\t\tRedirectURL: g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}, nil\n}\n\nfunc (g *ProviderGitLab) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn g.oauth2(ctx)\n}\n\nfunc (g *ProviderGitLab) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\to, err := g.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tu, err := g.endpoint()\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tu.Path = path.Join(u.Path, \"/oauth/userinfo\")\n\n\tctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), o, exchange)\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", u.String(), nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\treq.Header.Set(\"Accept\", \"application/json\")\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(g.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar claims Claims\n\tif err := json.NewDecoder(resp.Body).Decode(&claims); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tclaims.Issuer = cmp.Or(claims.Issuer, g.config.IssuerURL)\n\treturn &claims, nil\n}\n\nfunc (g *ProviderGitLab) endpoint() (*url.URL, error) {\n\te := defaultEndpoint\n\tif len(g.config.IssuerURL) > 0 {\n\t\te = g.config.IssuerURL\n\t}\n\treturn url.Parse(e)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_google.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"slices\"\n\n\tgooidc \"github.com/coreos/go-oidc/v3/oidc\"\n\t\"golang.org/x/oauth2\"\n)\n\nvar _ OAuth2Provider = (*ProviderGoogle)(nil)\n\ntype ProviderGoogle struct {\n\t*ProviderGenericOIDC\n\tJWKSUrl string\n}\n\nfunc NewProviderGoogle(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\tconfig.IssuerURL = \"https://accounts.google.com\"\n\treturn &ProviderGoogle{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t\tJWKSUrl: \"https://www.googleapis.com/oauth2/v3/certs\",\n\t}\n}\n\nfunc (g *ProviderGoogle) oauth2ConfigFromEndpoint(ctx context.Context, endpoint oauth2.Endpoint) *oauth2.Config {\n\tscope := g.config.Scope\n\tif !slices.Contains(scope, gooidc.ScopeOpenID) {\n\t\tscope = append(scope, gooidc.ScopeOpenID)\n\t}\n\n\tscope = slices.DeleteFunc(slices.Clone(scope), func(s string) bool { return s == gooidc.ScopeOfflineAccess })\n\n\treturn &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint:     endpoint,\n\t\tScopes:       scope,\n\t\tRedirectURL:  g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (g *ProviderGoogle) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\tp, err := g.provider(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tendpoint := p.Endpoint()\n\treturn g.oauth2ConfigFromEndpoint(ctx, endpoint), nil\n}\n\nfunc (g *ProviderGoogle) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\tscope := g.config.Scope\n\toptions := g.ProviderGenericOIDC.AuthCodeURLOptions(r)\n\n\tif slices.Contains(scope, gooidc.ScopeOfflineAccess) {\n\t\toptions = append(options, oauth2.AccessTypeOffline)\n\t}\n\n\treturn options\n}\n\nvar _ IDTokenVerifier = new(ProviderGoogle)\n\nconst issuerURLGoogle = \"https://accounts.google.com\"\n\nfunc (g *ProviderGoogle) Verify(ctx context.Context, rawIDToken string) (*Claims, error) {\n\tkeySet := gooidc.NewRemoteKeySet(ctx, g.JWKSUrl)\n\tctx = gooidc.ClientContext(ctx, g.reg.HTTPClient(ctx).HTTPClient)\n\n\treturn verifyToken(ctx, keySet, g.config, rawIDToken, issuerURLGoogle)\n}\n\nvar _ NonceValidationSkipper = new(ProviderGoogle)\n\nfunc (g *ProviderGoogle) CanSkipNonce(c *Claims) bool {\n\t// Not all SDKs support nonce validation, so we skip it if no nonce is present in the claims of the ID Token.\n\treturn c.Nonce == \"\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_google_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v4\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestProviderGoogle_Scope(t *testing.T) {\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\n\tp := oidc.NewProviderGoogle(&oidc.Configuration{\n\t\tProvider:        \"google\",\n\t\tID:              \"valid\",\n\t\tClientID:        \"client\",\n\t\tClientSecret:    \"secret\",\n\t\tMapper:          \"file://./stub/hydra.schema.json\",\n\t\tRequestedClaims: nil,\n\t\tScope:           []string{\"email\", \"profile\", \"offline_access\"},\n\t}, reg)\n\n\tc, _ := p.(oidc.OAuth2Provider).OAuth2(context.Background())\n\tassert.NotContains(t, c.Scopes, \"offline_access\")\n}\n\nfunc TestProviderGoogle_AccessType(t *testing.T) {\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\n\tp := oidc.NewProviderGoogle(&oidc.Configuration{\n\t\tProvider:        \"google\",\n\t\tID:              \"valid\",\n\t\tClientID:        \"client\",\n\t\tClientSecret:    \"secret\",\n\t\tMapper:          \"file://./stub/hydra.schema.json\",\n\t\tRequestedClaims: nil,\n\t\tScope:           []string{\"email\", \"profile\", \"offline_access\"},\n\t}, reg)\n\n\tr := &login.Flow{\n\t\tID: x.NewUUID(),\n\t}\n\n\toptions := p.(oidc.OAuth2Provider).AuthCodeURLOptions(r)\n\tassert.Contains(t, options, oauth2.AccessTypeOffline)\n}\n\nfunc TestGoogleVerify(t *testing.T) {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(200)\n\t\t_, _ = w.Write(publicJWKS)\n\t}))\n\n\ttsOtherJWKS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(200)\n\t\t_, _ = w.Write(publicJWKS2)\n\t}))\n\n\tmakeClaims := func(aud string) jwt.RegisteredClaims {\n\t\treturn jwt.RegisteredClaims{\n\t\t\tIssuer:    \"https://accounts.google.com\",\n\t\t\tSubject:   \"acme@ory.sh\",\n\t\t\tAudience:  jwt.ClaimStrings{aud},\n\t\t\tExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),\n\t\t}\n\t}\n\tcreateProvider := func(jwksUrl string) *oidc.ProviderGoogle {\n\t\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\t\tp := oidc.NewProviderGoogle(&oidc.Configuration{\n\t\t\tProvider:        \"google\",\n\t\t\tID:              \"valid\",\n\t\t\tClientID:        \"com.example.app\",\n\t\t\tClientSecret:    \"secret\",\n\t\t\tMapper:          \"file://./stub/hydra.schema.json\",\n\t\t\tRequestedClaims: nil,\n\t\t\tScope:           []string{\"email\", \"profile\", \"offline_access\"},\n\t\t}, reg).(*oidc.ProviderGoogle)\n\t\tp.JWKSUrl = jwksUrl\n\t\treturn p\n\t}\n\tt.Run(\"case=successful verification\", func(t *testing.T) {\n\t\tp := createProvider(ts.URL)\n\t\ttoken := createIDToken(t, makeClaims(\"com.example.app\"))\n\n\t\tc, err := p.Verify(context.Background(), token)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, \"acme@ory.sh\", c.Email)\n\t\tassert.Equal(t, \"acme@ory.sh\", c.Subject)\n\t\tassert.Equal(t, \"https://accounts.google.com\", c.Issuer)\n\t})\n\n\tt.Run(\"case=fails due to client_id mismatch\", func(t *testing.T) {\n\t\tp := createProvider(ts.URL)\n\t\ttoken := createIDToken(t, makeClaims(\"com.different-example.app\"))\n\n\t\t_, err := p.Verify(context.Background(), token)\n\t\trequire.Error(t, err)\n\t\tassert.Equal(t, `token audience didn't match allowed audiences: [com.example.app] oidc: expected audience \"com.example.app\" got [\"com.different-example.app\"]`, err.Error())\n\t})\n\n\tt.Run(\"case=fails due to jwks mismatch\", func(t *testing.T) {\n\t\tp := createProvider(tsOtherJWKS.URL)\n\t\ttoken := createIDToken(t, makeClaims(\"com.example.app\"))\n\n\t\t_, err := p.Verify(context.Background(), token)\n\t\trequire.Error(t, err)\n\t\tassert.Equal(t, \"failed to verify signature: failed to verify id token signature\", err.Error())\n\t})\n\n\tt.Run(\"case=succeedes with additional id token audience\", func(t *testing.T) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\tgoogle := oidc.NewProviderGoogle(&oidc.Configuration{\n\t\t\tClientID:                   \"something.else.app\",\n\t\t\tAdditionalIDTokenAudiences: []string{\"com.example.app\"},\n\t\t}, reg).(*oidc.ProviderGoogle)\n\t\tgoogle.JWKSUrl = ts.URL\n\t\ttoken := createIDToken(t, makeClaims(\"com.example.app\"))\n\n\t\t_, err := google.Verify(context.Background(), token)\n\t\trequire.NoError(t, err)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_jackson.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"github.com/coreos/go-oidc/v3/oidc\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/x/urlx\"\n)\n\ntype ProviderJackson struct {\n\t*ProviderGenericOIDC\n}\n\nfunc NewProviderJackson(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderJackson{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (j *ProviderJackson) setProvider(ctx context.Context) {\n\tif j.p == nil {\n\t\tinternalHost := strings.TrimSuffix(j.config.TokenURL, \"/api/oauth/token\")\n\t\tconfig := oidc.ProviderConfig{\n\t\t\tIssuerURL:     j.config.IssuerURL,\n\t\t\tAuthURL:       j.config.AuthURL,\n\t\t\tTokenURL:      j.config.TokenURL,\n\t\t\tDeviceAuthURL: \"\",\n\t\t\tUserInfoURL:   internalHost + \"/api/oauth/userinfo\",\n\t\t\tJWKSURL:       internalHost + \"/oauth/jwks\",\n\t\t\tAlgorithms:    []string{\"RS256\"},\n\t\t}\n\t\tj.p = config.NewProvider(j.withHTTPClientContext(ctx))\n\t}\n}\n\nfunc (j *ProviderJackson) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\tj.setProvider(ctx)\n\tendpoint := j.p.Endpoint()\n\tconfig := j.oauth2ConfigFromEndpoint(ctx, endpoint)\n\tconfig.RedirectURL = urlx.AppendPaths(\n\t\tj.reg.Config().SAMLRedirectURIBase(ctx),\n\t\t\"/self-service/methods/saml/callback/\"+j.config.ID).String()\n\n\treturn config, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_jackson_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nfunc TestProviderJackson(t *testing.T) {\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\n\tj := oidc.NewProviderJackson(&oidc.Configuration{\n\t\tProvider:  \"jackson\",\n\t\tIssuerURL: \"https://www.jackson.com/oauth\",\n\t\tAuthURL:   \"https://www.jackson.com/oauth/auth\",\n\t\tTokenURL:  \"https://www.jackson.com/api/oauth/token\",\n\t\tMapper:    \"file://./stub/hydra.schema.json\",\n\t\tScope:     []string{\"email\", \"profile\"},\n\t\tID:        \"some-id\",\n\t}, reg)\n\tassert.NotNil(t, j)\n\n\tc, err := j.(oidc.OAuth2Provider).OAuth2(context.Background())\n\trequire.NoError(t, err)\n\n\tassert.True(t, strings.HasSuffix(c.RedirectURL, \"/self-service/methods/saml/callback/some-id\"))\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_lark.go",
    "content": "// Copyright © 2022 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/httpx\"\n)\n\nvar _ OAuth2Provider = (*ProviderLark)(nil)\n\ntype ProviderLark struct {\n\t*ProviderGenericOIDC\n}\n\nvar (\n\tlarkAuthEndpoint = oauth2.Endpoint{\n\t\tAuthURL:   \"https://passport.feishu.cn/suite/passport/oauth/authorize\",\n\t\tTokenURL:  \"https://passport.feishu.cn/suite/passport/oauth/token\",\n\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t}\n\tlarkUserEndpoint = \"https://passport.feishu.cn/suite/passport/oauth/userinfo\"\n)\n\nfunc NewProviderLark(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderLark{\n\t\t&ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (pl *ProviderLark) Config() *Configuration {\n\treturn pl.config\n}\n\nfunc (pl *ProviderLark) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn &oauth2.Config{\n\t\tClientID:     pl.config.ClientID,\n\t\tClientSecret: pl.config.ClientSecret,\n\t\tEndpoint:     larkAuthEndpoint,\n\t\t// Lark uses fixed scope that can not be configured in runtime\n\t\tScopes:      pl.config.Scope,\n\t\tRedirectURL: pl.config.Redir(pl.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}, nil\n}\n\nfunc (pl *ProviderLark) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\t// larkClaim is defined in the https://open.feishu.cn/document/common-capabilities/sso/api/get-user-info\n\ttype larkClaim struct {\n\t\tSub          string `json:\"sub\"`\n\t\tName         string `json:\"name\"`\n\t\tPicture      string `json:\"picture\"`\n\t\tOpenID       string `json:\"open_id\"`\n\t\tUnionID      string `json:\"union_id\"`\n\t\tEnName       string `json:\"en_name\"`\n\t\tTenantKey    string `json:\"tenant_key\"`\n\t\tAvatarURL    string `json:\"avatar_url\"`\n\t\tAvatarThumb  string `json:\"avatar_thumb\"`\n\t\tAvatarMiddle string `json:\"avatar_middle\"`\n\t\tAvatarBig    string `json:\"avatar_big\"`\n\t\tEmail        string `json:\"email\"`\n\t\tUserID       string `json:\"user_id\"`\n\t\tMobile       string `json:\"mobile\"`\n\t}\n\tvar (\n\t\tclient = pl.reg.HTTPClient(ctx, httpx.ResilientClientDisallowInternalIPs())\n\t\tuser   larkClaim\n\t)\n\n\treq, err := retryablehttp.NewRequest(\"GET\", larkUserEndpoint, nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\texchange.SetAuthHeader(req.Request)\n\tres, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = res.Body.Close() }()\n\n\tif err := logUpstreamError(pl.reg.Logger(), res); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := json.NewDecoder(res.Body).Decode(&user); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\treturn &Claims{\n\t\tIssuer:      larkUserEndpoint,\n\t\tSubject:     user.OpenID,\n\t\tName:        user.Name,\n\t\tNickname:    user.Name,\n\t\tPicture:     user.AvatarURL,\n\t\tEmail:       user.Email,\n\t\tPhoneNumber: user.Mobile,\n\t}, nil\n}\n\nfunc (pl *ProviderLark) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{}\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_line_2_1.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\n\t\"golang.org/x/oauth2\"\n)\n\ntype ProviderLineV21 struct {\n\t*ProviderGenericOIDC\n}\n\nfunc NewProviderLineV21(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\tconfig.IssuerURL = \"https://access.line.me\"\n\treturn &ProviderLineV21{\n\t\t&ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (g *ProviderLineV21) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {\n\to, err := g.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Line login requires adding id_token_key_type=JWK when getting the token in order to issue an ES256 token.\n\t// https://blog.miniasp.com/post/2022/04/08/LINE-Login-with-OpenID-Connect-in-ASPNET-Core (Chinese)\n\topts = append(opts, oauth2.SetAuthURLParam(\"id_token_key_type\", \"JWK\"))\n\treturn o.Exchange(ctx, code, opts...)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_linkedin.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\t\"golang.org/x/oauth2/linkedin\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/httpx\"\n)\n\ntype LinkedInProfile struct {\n\tLocalizedLastName  string `json:\"localizedLastName\"`\n\tLocalizedFirstName string `json:\"localizedFirstName\"`\n\tProfilePicture     *struct {\n\t\tDisplayImage struct {\n\t\t\tElements []struct {\n\t\t\t\tIdentifiers []struct {\n\t\t\t\t\tIdentifier string `json:\"identifier\"`\n\t\t\t\t} `json:\"identifiers\"`\n\t\t\t} `json:\"elements\"`\n\t\t} `json:\"displayImage~\"`\n\t} `json:\"profilePicture,omitempty\"`\n\tID string `json:\"id\"`\n}\n\ntype LinkedInEmail struct {\n\tElements []struct {\n\t\tHandle struct {\n\t\t\tEmailAddress string `json:\"emailAddress\"`\n\t\t} `json:\"handle~\"`\n\t\tHandleUrn string `json:\"handle\"`\n\t} `json:\"elements\"`\n}\n\ntype LinkedInIntrospection struct {\n\tActive       bool   `json:\"active\"`\n\tClientID     string `json:\"client_id\"`\n\tAuthorizedAt uint32 `json:\"authorized_at\"`\n\tCreatedAt    uint32 `json:\"created_at\"`\n\tExpiresAt    uint32 `json:\"expires_at\"`\n\tStatus       string `json:\"status\"`\n\tScope        string `json:\"scope\"`\n\tAuthType     string `json:\"auth_type\"`\n}\n\n// type APIUrl string\n\nconst (\n\tProfileURL       string = \"https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,profilePicture(displayImage~digitalmediaAsset:playableStreams))\"\n\tEmailURL         string = \"https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))\"\n\tIntrospectionURL string = \"https://www.linkedin.com/oauth/v2/introspectToken\"\n)\n\nvar _ OAuth2Provider = (*ProviderLinkedIn)(nil)\n\ntype ProviderLinkedIn struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc NewProviderLinkedIn(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderLinkedIn{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (l *ProviderLinkedIn) Config() *Configuration {\n\treturn l.config\n}\n\nfunc (l *ProviderLinkedIn) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     l.config.ClientID,\n\t\tClientSecret: l.config.ClientSecret,\n\t\tEndpoint:     linkedin.Endpoint,\n\t\tScopes:       l.config.Scope,\n\t\tRedirectURL:  l.config.Redir(l.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (l *ProviderLinkedIn) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn l.oauth2(ctx), nil\n}\n\nfunc (l *ProviderLinkedIn) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{}\n}\n\nfunc (l *ProviderLinkedIn) fetch(ctx context.Context, client *retryablehttp.Client, url string, result interface{}) (err error) {\n\tctx, span := l.reg.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.ProviderLinkedIn.fetch\")\n\tdefer otelx.End(span, &err)\n\n\treq, err := retryablehttp.NewRequestWithContext(ctx, http.MethodGet, url, nil)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tres, err := client.Do(req)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tdefer func() { _ = res.Body.Close() }()\n\tif err := logUpstreamError(l.reg.Logger(), res); err != nil {\n\t\treturn err\n\t}\n\n\tif err := json.NewDecoder(res.Body).Decode(result); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn nil\n}\n\nfunc (l *ProviderLinkedIn) Profile(ctx context.Context, client *retryablehttp.Client) (*LinkedInProfile, error) {\n\tvar result LinkedInProfile\n\tif err := l.fetch(ctx, client, ProfileURL, &result); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn &result, nil\n}\n\nfunc (l *ProviderLinkedIn) Email(ctx context.Context, client *retryablehttp.Client) (*LinkedInEmail, error) {\n\tvar result LinkedInEmail\n\tif err := l.fetch(ctx, client, EmailURL, &result); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn &result, nil\n}\n\nfunc (l *ProviderLinkedIn) ProfilePicture(profile *LinkedInProfile) string {\n\tif profile.ProfilePicture == nil {\n\t\treturn \"\"\n\t}\n\n\telements := profile.ProfilePicture.DisplayImage.Elements\n\ti := len(elements)\n\tif i == 0 {\n\t\treturn \"\"\n\t} else if i > 3 {\n\t\ti = 3\n\t}\n\n\tidentifiers := elements[i-1].Identifiers\n\tif len(identifiers) == 0 {\n\t\treturn \"\"\n\t}\n\n\treturn identifiers[0].Identifier\n}\n\nfunc (l *ProviderLinkedIn) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (_ *Claims, err error) {\n\tctx, span := l.reg.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.ProviderLinkedIn.Claims\")\n\tdefer otelx.End(span, &err)\n\n\to, err := l.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tctx, client := httpx.SetOAuth2(ctx, l.reg.HTTPClient(ctx), o, exchange)\n\tprofile, err := l.Profile(ctx, client)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\temail, err := l.Email(ctx, client)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tclaims := &Claims{\n\t\tSubject:   profile.ID,\n\t\tIssuer:    \"https://login.linkedin.com/\",\n\t\tEmail:     email.Elements[0].Handle.EmailAddress,\n\t\tGivenName: profile.LocalizedFirstName,\n\t\tLastName:  profile.LocalizedLastName,\n\t\tPicture:   l.ProfilePicture(profile),\n\t}\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_linkedin_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/jarcoal/httpmock\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nfunc TestProviderLinkedin_Claims(t *testing.T) {\n\t_, base := pkg.NewFastRegistryWithMocks(t)\n\treg := &mockRegistry{base, retryablehttp.NewClient()}\n\thttpmock.ActivateNonDefault(reg.cl.HTTPClient)\n\tt.Cleanup(httpmock.DeactivateAndReset)\n\n\thttpmock.RegisterResponder(\"GET\", \"https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,profilePicture(displayImage~digitalmediaAsset:playableStreams))\",\n\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\"localizedLastName\": \"Doe\",\n\t\t\t\t\"profilePicture\": map[string]interface{}{\n\t\t\t\t\t\"displayImage\": \"urn:li:digitalmediaAsset:D5F04AQG1h-anJtsLeA\",\n\t\t\t\t\t\"displayImage~\": map[string]interface{}{\n\t\t\t\t\t\t\"elements\": []map[string]interface{}{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"identifiers\": []map[string]interface{}{\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"identifier\": \"https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_100_100/0/1661502148285?e=1674086400&v=beta&t=5zdsvAqGS1idrfNAai-zwp01K4hApGeJyRV8zBTjZck\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\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\"identifiers\": []map[string]interface{}{\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"identifier\": \"https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_200_200/0/1661502148285?e=1674086400&v=beta&t=fbHprGci4vEpeQdzRTyt9YJjNXTgv-14h9ROr_DE5R4\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\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\"identifiers\": []map[string]interface{}{\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"identifier\": \"https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_400_400/0/1661502148285?e=1674086400&v=beta&t=SMgXuiZ5DiiPf4iMrTPQWoCnFRh4Y-DfoWjopUZ0qCo\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\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\"identifiers\": []map[string]interface{}{\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"identifier\": \"https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_800_800/0/1661502148285?e=1674086400&v=beta&t=QzC9b32DmLRIwU7P27LfJUqglXsJm056WkA_TTOHxHU\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\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},\n\t\t\t\t\"localizedFirstName\": \"John\",\n\t\t\t\t\"id\":                 \"5foOWOiYXD\",\n\t\t\t})\n\t\t\treturn resp, err\n\t\t})\n\n\thttpmock.RegisterResponder(\"GET\", \"https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))\",\n\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\"elements\": []map[string]interface{}{\n\t\t\t\t\t{\n\t\t\t\t\t\t\"handle~\": map[string]interface{}{\n\t\t\t\t\t\t\t\"emailAddress\": \"john.doe@gmail.com\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"handle\": \"urn:li:emailAddress:-1810278278\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn resp, err\n\t\t},\n\t)\n\n\thttpmock.RegisterResponder(\"POST\", \"https://www.linkedin.com/oauth/v2/introspectToken\",\n\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\"active\":        true,\n\t\t\t\t\"client_id\":     \"78e533g1dq0rs5\",\n\t\t\t\t\"authorized_at\": 1668518671,\n\t\t\t\t\"created_at\":    1668518671,\n\t\t\t\t\"status\":        \"active\",\n\t\t\t\t\"expires_at\":    2673702676,\n\t\t\t\t\"scope\":         \"r_emailaddress,r_liteprofile\",\n\t\t\t\t\"auth_type\":     \"3L\",\n\t\t\t})\n\t\t\treturn resp, err\n\t\t},\n\t)\n\n\tc := &oidc.Configuration{\n\t\tID:           \"linkedin\",\n\t\tProvider:     \"linkedin\",\n\t\tClientID:     \"abcd\",\n\t\tClientSecret: \"secret\",\n\t\tMapper:       \"file://./stub/oidc.linkedin.jsonnet\",\n\t\tScope:        []string{\"r_emailaddress\", \"r_liteprofile\"},\n\t}\n\tlinkedin := oidc.NewProviderLinkedIn(c, reg)\n\n\tconst fakeLinkedinIDToken = \"id_token_mock_\" // #nosec G101 test code\n\tactual, err := linkedin.(oidc.OAuth2Provider).Claims(\n\t\tcontext.Background(),\n\t\t(&oauth2.Token{AccessToken: \"foo\", Expiry: time.Now().Add(time.Hour)}).WithExtra(map[string]interface{}{\"id_token\": fakeLinkedinIDToken}),\n\t\turl.Values{},\n\t)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, &oidc.Claims{\n\t\tIssuer:    \"https://login.linkedin.com/\",\n\t\tSubject:   \"5foOWOiYXD\",\n\t\tGivenName: \"John\",\n\t\tLastName:  \"Doe\",\n\t\tEmail:     \"john.doe@gmail.com\",\n\t\tPicture:   \"https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_400_400/0/1661502148285?e=1674086400&v=beta&t=SMgXuiZ5DiiPf4iMrTPQWoCnFRh4Y-DfoWjopUZ0qCo\",\n\t}, actual)\n}\n\nfunc TestProviderLinkedin_No_Picture(t *testing.T) {\n\t_, base := pkg.NewFastRegistryWithMocks(t)\n\treg := &mockRegistry{base, retryablehttp.NewClient()}\n\thttpmock.ActivateNonDefault(reg.cl.HTTPClient)\n\tt.Cleanup(httpmock.DeactivateAndReset)\n\n\thttpmock.RegisterResponder(\"GET\", \"https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,profilePicture(displayImage~digitalmediaAsset:playableStreams))\",\n\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\"localizedLastName\":  \"Doe\",\n\t\t\t\t\"localizedFirstName\": \"John\",\n\t\t\t\t\"id\":                 \"5foOWOiYXD\",\n\t\t\t})\n\t\t\treturn resp, err\n\t\t})\n\n\thttpmock.RegisterResponder(\"GET\", \"https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))\",\n\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\"elements\": []map[string]interface{}{\n\t\t\t\t\t{\n\t\t\t\t\t\t\"handle~\": map[string]interface{}{\n\t\t\t\t\t\t\t\"emailAddress\": \"john.doe@gmail.com\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"handle\": \"urn:li:emailAddress:-1810278278\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn resp, err\n\t\t},\n\t)\n\n\thttpmock.RegisterResponder(\"POST\", \"https://www.linkedin.com/oauth/v2/introspectToken\",\n\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\"active\":        true,\n\t\t\t\t\"client_id\":     \"78e533g1dq0rs5\",\n\t\t\t\t\"authorized_at\": 1668518671,\n\t\t\t\t\"created_at\":    1668518671,\n\t\t\t\t\"status\":        \"active\",\n\t\t\t\t\"expires_at\":    2673702676,\n\t\t\t\t\"scope\":         \"r_emailaddress,r_liteprofile\",\n\t\t\t\t\"auth_type\":     \"3L\",\n\t\t\t})\n\t\t\treturn resp, err\n\t\t},\n\t)\n\n\tc := &oidc.Configuration{\n\t\tID:           \"linkedin\",\n\t\tProvider:     \"linkedin\",\n\t\tClientID:     \"abcd\",\n\t\tClientSecret: \"secret\",\n\t\tMapper:       \"file://./stub/oidc.linkedin.jsonnet\",\n\t\tScope:        []string{\"r_emailaddress\", \"r_liteprofile\"},\n\t}\n\tlinkedin := oidc.NewProviderLinkedIn(c, reg)\n\n\tconst fakeLinkedinIDToken = \"id_token_mock_\" // #nosec G101 test code\n\tactual, err := linkedin.(oidc.OAuth2Provider).Claims(\n\t\tcontext.Background(),\n\t\t(&oauth2.Token{AccessToken: \"foo\", Expiry: time.Now().Add(time.Hour)}).WithExtra(map[string]interface{}{\"id_token\": fakeLinkedinIDToken}),\n\t\turl.Values{},\n\t)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, &oidc.Claims{\n\t\tIssuer:    \"https://login.linkedin.com/\",\n\t\tSubject:   \"5foOWOiYXD\",\n\t\tGivenName: \"John\",\n\t\tLastName:  \"Doe\",\n\t\tEmail:     \"john.doe@gmail.com\",\n\t\tPicture:   \"\",\n\t}, actual)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_linkedin_v2.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nfunc NewProviderLinkedInV2(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\tconfig.ClaimsSource = ClaimsSourceUserInfo\n\tconfig.IssuerURL = \"https://www.linkedin.com/oauth\"\n\n\treturn &ProviderGenericOIDC{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_linkedin_v2_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nfunc TestProviderLinkedInV2_Discovery(t *testing.T) {\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\n\tp := oidc.NewProviderLinkedInV2(&oidc.Configuration{\n\t\tProvider:        \"linkedin_v2\",\n\t\tID:              \"valid\",\n\t\tClientID:        \"client\",\n\t\tClientSecret:    \"secret\",\n\t\tMapper:          \"file://./stub/hydra.schema.json\",\n\t\tRequestedClaims: nil,\n\t\tScope:           []string{\"email\", \"profile\", \"offline_access\"},\n\t}, reg)\n\n\tc, err := p.(oidc.OAuth2Provider).OAuth2(context.Background())\n\trequire.NoError(t, err)\n\tassert.Contains(t, c.Scopes, \"openid\")\n\tassert.Equal(t, \"https://www.linkedin.com/oauth/v2/accessToken\", c.Endpoint.TokenURL)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_microsoft.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"strings\"\n\n\tgooidc \"github.com/coreos/go-oidc/v3/oidc\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/golang-jwt/jwt/v4\"\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/httpx\"\n)\n\nvar _ OAuth2Provider = (*ProviderMicrosoft)(nil)\n\ntype ProviderMicrosoft struct {\n\t*ProviderGenericOIDC\n}\n\nfunc NewProviderMicrosoft(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderMicrosoft{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (m *ProviderMicrosoft) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\tif strings.TrimSpace(m.config.Tenant) == \"\" {\n\t\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"No Tenant specified for the `microsoft` oidc provider %s\", m.config.ID))\n\t}\n\n\tendpointPrefix := \"https://login.microsoftonline.com/\" + m.config.Tenant\n\tendpoint := oauth2.Endpoint{\n\t\tAuthURL:  endpointPrefix + \"/oauth2/v2.0/authorize\",\n\t\tTokenURL: endpointPrefix + \"/oauth2/v2.0/token\",\n\t}\n\n\treturn m.oauth2ConfigFromEndpoint(ctx, endpoint), nil\n}\n\nfunc (m *ProviderMicrosoft) Claims(ctx context.Context, exchange *oauth2.Token, _ url.Values) (*Claims, error) {\n\traw, ok := exchange.Extra(\"id_token\").(string)\n\tif !ok || len(raw) == 0 {\n\t\treturn nil, errors.WithStack(ErrIDTokenMissing)\n\t}\n\n\tparser := new(jwt.Parser)\n\tunverifiedClaims := microsoftUnverifiedClaims{}\n\tif _, _, err := parser.ParseUnverified(raw, &unverifiedClaims); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif _, err := uuid.FromString(unverifiedClaims.TenantID); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"TenantID claim is not a valid UUID: %s\", err))\n\t}\n\n\tissuer := \"https://login.microsoftonline.com/\" + unverifiedClaims.TenantID + \"/v2.0\"\n\tctx = context.WithValue(ctx, oauth2.HTTPClient, m.reg.HTTPClient(ctx).HTTPClient)\n\tp, err := gooidc.NewProvider(ctx, issuer)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"Unable to initialize OpenID Connect Provider: %s\", err))\n\t}\n\n\tclaims, err := m.verifyAndDecodeClaimsWithProvider(ctx, p, raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn m.updateSubject(ctx, claims, exchange)\n}\n\nfunc (m *ProviderMicrosoft) updateSubject(ctx context.Context, claims *Claims, exchange *oauth2.Token) (*Claims, error) {\n\tif m.config.SubjectSource == \"me\" {\n\t\to, err := m.OAuth2(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tctx, client := httpx.SetOAuth2(ctx, m.reg.HTTPClient(ctx), o, exchange)\n\t\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", \"https://graph.microsoft.com/v1.0/me\", nil)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t\t}\n\n\t\tresp, err := client.Do(req)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"Unable to fetch from `https://graph.microsoft.com/v1.0/me`: %s\", err))\n\t\t}\n\t\tdefer func() { _ = resp.Body.Close() }()\n\n\t\tif err := logUpstreamError(m.reg.Logger(), resp); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar user struct {\n\t\t\tID string `json:\"id\"`\n\t\t}\n\t\tif err := json.NewDecoder(resp.Body).Decode(&user); err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"Unable to decode JSON from `https://graph.microsoft.com/v1.0/me`: %s\", err))\n\t\t}\n\n\t\tclaims.Subject = user.ID\n\t}\n\n\tif m.config.SubjectSource == \"oid\" {\n\t\tclaims.Subject = claims.Object\n\t}\n\n\treturn claims, nil\n}\n\ntype microsoftUnverifiedClaims struct {\n\tTenantID string `json:\"tid,omitempty\"`\n}\n\nfunc (c *microsoftUnverifiedClaims) Valid() error {\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_netid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/coreos/go-oidc/v3/oidc\"\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst (\n\tdefaultBrokerScheme = \"https\"\n\tdefaultBrokerHost   = \"broker.netid.de\"\n)\n\nvar _ OAuth2Provider = (*ProviderNetID)(nil)\n\ntype ProviderNetID struct {\n\t*ProviderGenericOIDC\n}\n\nfunc NewProviderNetID(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\tconfig.IssuerURL = fmt.Sprintf(\"%s://%s/\", defaultBrokerScheme, defaultBrokerHost)\n\tif !slices.Contains(config.Scope, oidc.ScopeOpenID) {\n\t\tconfig.Scope = append(config.Scope, oidc.ScopeOpenID)\n\t}\n\n\treturn &ProviderNetID{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (n *ProviderNetID) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn n.oAuth2(ctx)\n}\n\nfunc (n *ProviderNetID) oAuth2(ctx context.Context) (*oauth2.Config, error) {\n\tu := n.brokerURL()\n\n\tauthURL := urlx.AppendPaths(u, \"/authorize\")\n\ttokenURL := urlx.AppendPaths(u, \"/token\")\n\n\treturn &oauth2.Config{\n\t\tClientID:     n.config.ClientID,\n\t\tClientSecret: n.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\tAuthURL:  authURL.String(),\n\t\t\tTokenURL: tokenURL.String(),\n\t\t},\n\t\tScopes:      n.config.Scope,\n\t\tRedirectURL: n.config.Redir(n.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}, nil\n}\n\nfunc (n *ProviderNetID) Claims(ctx context.Context, exchange *oauth2.Token, _ url.Values) (*Claims, error) {\n\to, err := n.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tctx, client := httpx.SetOAuth2(ctx, n.reg.HTTPClient(ctx), o, exchange)\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", urlx.AppendPaths(n.brokerURL(), \"/userinfo\").String(), nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"%s\", err))\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(n.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\tp, err := n.provider(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\traw, ok := exchange.Extra(\"id_token\").(string)\n\tif !ok || len(raw) == 0 {\n\t\treturn nil, errors.WithStack(ErrIDTokenMissing)\n\t}\n\n\tclaims, err := n.verifyAndDecodeClaimsWithProvider(ctx, p, raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar userinfo Claims\n\tif err := json.NewDecoder(resp.Body).Decode(&userinfo); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tuserinfo.Issuer = claims.Issuer\n\tuserinfo.Subject = claims.Subject\n\treturn &userinfo, nil\n}\n\nfunc (n *ProviderNetID) Verify(ctx context.Context, rawIDToken string) (*Claims, error) {\n\tprovider, err := n.provider(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"POST\", urlx.AppendPaths(n.brokerURL(), \"/token\").String(), strings.NewReader(url.Values{\n\t\t\"grant_type\":  {\"netid_fedcm\"},\n\t\t\"fedcm_token\": {rawIDToken},\n\t}.Encode()))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"Origin\", n.config.NetIDTokenOriginHeader)\n\tres, err := n.reg.HTTPClient(ctx).Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttoken := struct {\n\t\tIDToken string `json:\"id_token\"`\n\t}{}\n\n\tif err := json.NewDecoder(res.Body).Decode(&token); err != nil {\n\t\treturn nil, err\n\t}\n\n\tidToken, err := provider.VerifierContext(\n\t\tn.withHTTPClientContext(ctx),\n\t\t&oidc.Config{ClientID: n.config.ClientID},\n\t).Verify(ctx, token.IDToken)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar (\n\t\tclaims    Claims\n\t\trawClaims map[string]any\n\t)\n\n\tif err = idToken.Claims(&claims); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = idToken.Claims(&rawClaims); err != nil {\n\t\treturn nil, err\n\t}\n\tclaims.RawClaims = rawClaims\n\n\treturn &claims, nil\n}\n\nfunc (n *ProviderNetID) brokerURL() *url.URL {\n\treturn &url.URL{Scheme: defaultBrokerScheme, Host: defaultBrokerHost}\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_netid_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nfunc TestNetidProvider(t *testing.T) {\n\tt.Skip(\"can't test this automatically, because the token is only valid for a short time\")\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\n\tp := oidc.NewProviderNetID(&oidc.Configuration{\n\t\tClientID: \"9b56b26a-e93d-4fce-8f16-951a9858f23e\",\n\t}, reg)\n\n\trawToken := `...`\n\n\tclaims, err := p.(oidc.IDTokenVerifier).Verify(context.Background(), rawToken)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, claims)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_patreon.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar _ OAuth2Provider = (*ProviderPatreon)(nil)\n\ntype ProviderPatreon struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\ntype PatreonIdentityResponse struct {\n\tData struct {\n\t\tAttributes struct {\n\t\t\tEmail     string `json:\"email\"`\n\t\t\tFirstName string `json:\"first_name\"`\n\t\t\tFullName  string `json:\"full_name\"`\n\t\t\tImageURL  string `json:\"image_url\"`\n\t\t\tLastName  string `json:\"last_name\"`\n\t\t} `json:\"attributes\"`\n\t\tID   string `json:\"id\"`\n\t\tType string `json:\"type\"`\n\t} `json:\"data\"`\n}\n\nfunc NewProviderPatreon(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderPatreon{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (d *ProviderPatreon) Config() *Configuration {\n\treturn d.config\n}\n\nfunc (d *ProviderPatreon) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     d.config.ClientID,\n\t\tClientSecret: d.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\tAuthURL:  \"https://www.patreon.com/oauth2/authorize\",\n\t\t\tTokenURL: \"https://www.patreon.com/api/oauth2/token\",\n\t\t},\n\t\tRedirectURL: d.config.Redir(d.reg.Config().OIDCRedirectURIBase(ctx)),\n\t\tScopes:      d.config.Scope,\n\t}\n}\n\nfunc (d *ProviderPatreon) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn d.oauth2(ctx), nil\n}\n\nfunc (d *ProviderPatreon) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\tif isForced(r) {\n\t\treturn []oauth2.AuthCodeOption{\n\t\t\toauth2.SetAuthURLParam(\"prompt\", \"consent\"),\n\t\t}\n\t}\n\treturn []oauth2.AuthCodeOption{\n\t\toauth2.SetAuthURLParam(\"prompt\", \"none\"),\n\t}\n}\n\nfunc (d *ProviderPatreon) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\tidentityURL := \"https://www.patreon.com/api/oauth2/v2/identity?fields%5Buser%5D=first_name,last_name,url,full_name,email,image_url\"\n\n\to := d.oauth2(ctx)\n\tctx, client := httpx.SetOAuth2(ctx, d.reg.HTTPClient(ctx), o, exchange)\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", identityURL, nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"%s\", err))\n\t}\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\treq.Header.Add(\"Authorization\", \"Bearer \"+exchange.AccessToken)\n\n\tres, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = res.Body.Close() }()\n\n\tif err := logUpstreamError(d.reg.Logger(), res); err != nil {\n\t\treturn nil, err\n\t}\n\n\tdata := PatreonIdentityResponse{}\n\tjsonErr := json.NewDecoder(res.Body).Decode(&data)\n\tif jsonErr != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(jsonErr).WithReasonf(\"%s\", jsonErr))\n\t}\n\n\tclaims := &Claims{\n\t\tIssuer:     \"https://www.patreon.com/\",\n\t\tSubject:    data.Data.ID,\n\t\tName:       data.Data.Attributes.FullName,\n\t\tEmail:      data.Data.Attributes.Email,\n\t\tGivenName:  data.Data.Attributes.FirstName,\n\t\tFamilyName: data.Data.Attributes.LastName,\n\t\tLastName:   data.Data.Attributes.LastName,\n\t\tPicture:    data.Data.Attributes.ImageURL,\n\t}\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_private_net_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nconst (\n\twellknownJWKs = \"https://raw.githubusercontent.com/aeneasr/private-oidc/master/jwks\"\n\tfakeJWTJWKS   = \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjk5OTk5OTk5OTksImF1ZCI6ImFiY2QiLCJpc3MiOiJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYWVuZWFzci9wcml2YXRlLW9pZGMvbWFzdGVyL2p3a3MifQ.RLR3dSRGGIjbRqyOMGMYFGTzcVHi7hPuFs_IKYywVWJ_XMyzWozTW4M8uuvBUPiVoNDNs7osm-AkRl7cBfw0by1XEcnEKZStCjdEh7Q0IGGb4hgq8rRqm1d3uJwNIGU5h7-s7tMnDED2ZTZhp304U99YWz7Ozl_TA9tqolBLLZEmIfXSY_RR3rMoDwtHZvWhI0OZtPdcBh86vWS9zG6QPHM5qGtRMMIs-ljXrrgS8LulUI5CAVEeHlQLXroBIe9v89IkKi07A7YRrk1SxFxlojcZ2v0z-0iTI3WL8mUoocF-RYy1RgJTK_dPYkSJebaN0R5MmBax5MXLKy4baNHKsg\"\n)\n\nfunc TestProviderPrivateIP(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(ctx, config.ViperKeyClientHTTPNoPrivateIPRanges, true)\n\n\tgeneric := func(c *oidc.Configuration) oidc.Provider {\n\t\treturn oidc.NewProviderGenericOIDC(c, reg)\n\t}\n\tauth0 := func(c *oidc.Configuration) oidc.Provider {\n\t\treturn oidc.NewProviderAuth0(c, reg)\n\t}\n\tgitlab := func(c *oidc.Configuration) oidc.Provider {\n\t\treturn oidc.NewProviderGitLab(c, reg)\n\t}\n\n\t// We do not test the Auth URL as the Auth URL is not vulnerable to SSRF attacks.\n\t// The AuthURL is only given to the user's browser, thus it is not possible to cause SSRF.\n\t// We only care about Token URLs and Issuer URLs.\n\tfor k, tc := range []struct {\n\t\tc  *oidc.Configuration\n\t\te  string\n\t\tp  func(c *oidc.Configuration) oidc.Provider\n\t\tid string\n\t}{\n\t\t// Apple uses a fixed token URL and does not use the issuer.\n\n\t\t{p: auth0, c: &oidc.Configuration{IssuerURL: \"http://127.0.0.2/\"}, e: \"is not a permitted destination\"},\n\t\t// The TokenURL is fixed in Auth0 to {issuer_url}/token. Since the issuer is called first, any local token fails also.\n\n\t\t// If the issuer URL is local, we fail\n\t\t{p: generic, c: &oidc.Configuration{IssuerURL: \"http://127.0.0.2/\"}, e: \"is not a permitted destination\", id: fakeJWTJWKS},\n\t\t{p: generic, c: &oidc.Configuration{IssuerURL: \"http://127.0.0.2/\", ClaimsSource: \"userinfo\"}, e: \"is not a permitted destination\", id: fakeJWTJWKS},\n\t\t{p: generic, c: &oidc.Configuration{IssuerURL: \"http://127.0.0.2/\", ClaimsSource: \"invalid\"}, e: \"Unknown claims source: \\\"invalid\\\"\", id: fakeJWTJWKS},\n\n\t\t// If the issuer URL has a local JWKs URL, we fail\n\t\t{p: generic, c: &oidc.Configuration{ClientID: \"abcd\", IssuerURL: wellknownJWKs}, e: \"is not a permitted destination\", id: fakeJWTJWKS},\n\t\t{p: generic, c: &oidc.Configuration{ClientID: \"abcd\", IssuerURL: wellknownJWKs, ClaimsSource: \"userinfo\"}, e: \"is not a permitted destination\", id: fakeJWTJWKS},\n\n\t\t// The next call does not fail because the provider uses only the ID JSON Web Token to verify this call and does\n\t\t// not use the TokenURL at all!\n\t\t// {p: generic, c: &oidc.Configuration{ClientID: \"abcd\", IssuerURL: wellknownToken, TokenURL: \"http://127.0.0.3/\"}, e: \"127.0.0.3 is not a public IP address\", id: fakeJWTToken},\n\n\t\t// Discord uses a fixed token URL and does not use the issuer.\n\t\t// Facebook uses a fixed token URL and does not use the issuer.\n\t\t// GitHub uses a fixed token URL and does not use the issuer.\n\t\t// GitHub App uses a fixed token URL and does not use the issuer.\n\t\t// GitHub App uses a fixed token URL and does not use the issuer.\n\t\t// LinkedInV2 uses a fixed token URL and does not use the issuer.\n\n\t\t{p: gitlab, c: &oidc.Configuration{IssuerURL: \"http://127.0.0.2/\"}, e: \"is not a permitted destination\"},\n\t\t// The TokenURL is fixed in GitLab to {issuer_url}/token. Since the issuer is called first, any local token fails also.\n\n\t\t// Google uses a fixed token URL and does not use the issuer.\n\t\t// Microsoft uses a fixed token URL and does not use the issuer.\n\t\t// Slack uses a fixed token URL and does not use the issuer.\n\t\t// Spotify uses a fixed token URL and does not use the issuer.\n\t\t// VK uses a fixed token URL and does not use the issuer.\n\t\t// Yandex uses a fixed token URL and does not use the issuer.\n\t\t// NetID uses a fixed token URL and does not use the issuer.\n\t\t// X uses a fixed token URL and userinfoRL and does not use the issuer value.\n\t\t// Line v2.1 uses a fixed token URL and does not use the issuer.\n\t\t// UAE PASS uses fixed token URL and userinfo URL and does not use the issuer value.\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tp := tc.p(tc.c)\n\t\t\t_, err := p.(oidc.OAuth2Provider).Claims(context.Background(), (&oauth2.Token{RefreshToken: \"foo\", Expiry: time.Now().Add(-time.Hour)}).WithExtra(map[string]interface{}{\n\t\t\t\t\"id_token\": tc.id,\n\t\t\t}), url.Values{})\n\t\t\trequire.Error(t, err)\n\t\t\tassert.Contains(t, fmt.Sprintf(\"%+v\", err), tc.e)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_salesforce.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/url\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar _ OAuth2Provider = (*ProviderSalesforce)(nil)\n\ntype ProviderSalesforce struct {\n\t*ProviderGenericOIDC\n}\n\nfunc NewProviderSalesforce(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderSalesforce{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (g *ProviderSalesforce) oauth2(ctx context.Context) (*oauth2.Config, error) {\n\tendpoint, err := url.Parse(g.config.IssuerURL)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tauthURL := *endpoint\n\ttokenURL := *endpoint\n\n\tauthURL.Path = path.Join(authURL.Path, \"/services/oauth2/authorize\")\n\ttokenURL.Path = path.Join(tokenURL.Path, \"/services/oauth2/token\")\n\n\tc := &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\tAuthURL:  authURL.String(),\n\t\t\tTokenURL: tokenURL.String(),\n\t\t},\n\t\tScopes:      g.config.Scope,\n\t\tRedirectURL: g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n\n\treturn c, nil\n}\n\nfunc (g *ProviderSalesforce) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn g.oauth2(ctx)\n}\n\nfunc (g *ProviderSalesforce) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\to, err := g.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tu, err := url.Parse(g.config.IssuerURL)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tu.Path = path.Join(u.Path, \"/services/oauth2/userinfo\")\n\n\tctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), o, exchange)\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", u.String(), nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\treq.Header.Add(\"Content-Type\", \"application/json\")\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(g.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Once Salesforce fixes this bug, all this workaround can be removed.\n\tb, err := io.ReadAll(io.LimitReader(resp.Body, 1024*1024))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tb, err = salesforceUpdatedAtWorkaround(b)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Once we get here, we know that if there is an updated_at field in the json, it is the correct type.\n\tvar claims Claims\n\tif err := json.Unmarshal(b, &claims); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tclaims.Issuer = cmp.Or(claims.Issuer, g.config.IssuerURL)\n\treturn &claims, nil\n}\n\n// There is a bug in the response from Salesforce. The updated_at field may be a string and not an int64.\n// https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5\n// We work around this by reading the json generically (as map[string]inteface{} and looking at the updated_at field\n// if it exists. If it's the wrong type (string), we fill out the claims by hand.\nfunc salesforceUpdatedAtWorkaround(body []byte) ([]byte, error) {\n\t// Force updatedAt to be an int if given as a string in the response.\n\tif updatedAtField := gjson.GetBytes(body, \"updated_at\"); updatedAtField.Exists() && updatedAtField.Type == gjson.String {\n\t\tt, err := time.Parse(time.RFC3339, updatedAtField.String())\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"bad time format in updated_at\"))\n\t\t}\n\t\tbody, err = sjson.SetBytes(body, \"updated_at\", t.Unix())\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t\t}\n\t}\n\treturn body, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_salesforce_unit_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSalesforceUpdatedAtWorkaround(t *testing.T) {\n\tactual, err := salesforceUpdatedAtWorkaround([]byte(\"{}\"))\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"{}\", string(actual))\n\n\tactual, err = salesforceUpdatedAtWorkaround([]byte(`{\"updated_at\":1234}`))\n\trequire.NoError(t, err)\n\tassert.Equal(t, `{\"updated_at\":1234}`, string(actual))\n\n\ttimestamp := time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)\n\tinput, err := json.Marshal(map[string]interface{}{\n\t\t\"updated_at\": timestamp,\n\t})\n\trequire.NoError(t, err)\n\tactual, err = salesforceUpdatedAtWorkaround(input)\n\trequire.NoError(t, err)\n\tassert.Equal(t, fmt.Sprintf(`{\"updated_at\":%d}`, timestamp.Unix()), string(actual))\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_slack.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"slices\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/x/stringsx\"\n\n\t\"github.com/slack-go/slack\"\n)\n\nvar _ OAuth2Provider = (*ProviderSlack)(nil)\n\ntype ProviderSlack struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc NewProviderSlack(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderSlack{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (d *ProviderSlack) Config() *Configuration {\n\treturn d.config\n}\n\nfunc (d *ProviderSlack) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     d.config.ClientID,\n\t\tClientSecret: d.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\t// slack's oauth v2 does not implement the oauth2 standard so we use the old version.\n\t\t\t// to use v2 we would need to rename the request 'scope' field to 'user_scope'.\n\t\t\tAuthURL:  \"https://slack.com/oauth/authorize\",\n\t\t\tTokenURL: slack.APIURL + \"oauth.access\",\n\t\t},\n\t\tRedirectURL: d.config.Redir(d.reg.Config().OIDCRedirectURIBase(ctx)),\n\t\tScopes:      d.config.Scope,\n\t}\n}\n\nfunc (d *ProviderSlack) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn d.oauth2(ctx), nil\n}\n\nfunc (d *ProviderSlack) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{}\n}\n\nfunc (d *ProviderSlack) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\tgrantedScopes := stringsx.Splitx(fmt.Sprintf(\"%s\", exchange.Extra(\"scope\")), \",\")\n\tfor _, check := range d.Config().Scope {\n\t\tif !slices.Contains(grantedScopes, check) {\n\t\t\treturn nil, errors.WithStack(ErrScopeMissing)\n\t\t}\n\t}\n\n\tapi := slack.New(exchange.AccessToken)\n\tidentity, err := api.GetUserIdentity()\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tclaims := &Claims{\n\t\tIssuer:            \"https://slack.com/oauth/\",\n\t\tSubject:           identity.User.ID,\n\t\tName:              identity.User.Name,\n\t\tPreferredUsername: identity.User.Name,\n\t\tNickname:          identity.User.Name,\n\t\tEmail:             identity.User.Email,\n\t\tEmailVerified:     true,\n\t\tPicture:           identity.User.Image512,\n\t\tTeam:              identity.Team.ID,\n\t}\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_spotify.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"slices\"\n\n\t\"golang.org/x/oauth2/spotify\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/ory/x/stringsx\"\n\n\tspotifyapi \"github.com/zmb3/spotify/v2\"\n\tspotifyauth \"github.com/zmb3/spotify/v2/auth\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar _ OAuth2Provider = (*ProviderSpotify)(nil)\n\ntype ProviderSpotify struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc NewProviderSpotify(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderSpotify{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (g *ProviderSpotify) Config() *Configuration {\n\treturn g.config\n}\n\nfunc (g *ProviderSpotify) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint:     spotify.Endpoint,\n\t\tScopes:       g.config.Scope,\n\t\tRedirectURL:  g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (g *ProviderSpotify) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn g.oauth2(ctx), nil\n}\n\nfunc (g *ProviderSpotify) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{}\n}\n\nfunc (g *ProviderSpotify) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\tgrantedScopes := stringsx.Splitx(fmt.Sprintf(\"%s\", exchange.Extra(\"scope\")), \" \")\n\tfor _, check := range g.Config().Scope {\n\t\tif !slices.Contains(grantedScopes, check) {\n\t\t\treturn nil, errors.WithStack(ErrScopeMissing)\n\t\t}\n\t}\n\n\tauth := spotifyauth.New(\n\t\tspotifyauth.WithRedirectURL(g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx))),\n\t\tspotifyauth.WithScopes(spotifyauth.ScopeUserReadPrivate))\n\n\tctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), auth, exchange)\n\tspotifyClient := spotifyapi.New(client.HTTPClient)\n\n\tuser, err := spotifyClient.CurrentUser(ctx)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tvar userPicture string\n\tif len(user.Images) > 0 {\n\t\tuserPicture = user.Images[0].URL\n\t}\n\n\tclaims := &Claims{\n\t\tSubject:   user.ID,\n\t\tIssuer:    spotify.Endpoint.TokenURL,\n\t\tName:      user.DisplayName,\n\t\tNickname:  user.DisplayName,\n\t\tEmail:     user.Email,\n\t\tPicture:   userPicture,\n\t\tProfile:   user.ExternalURLs[\"spotify\"],\n\t\tBirthdate: user.Birthdate,\n\t}\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestClaimsValidate(t *testing.T) {\n\trequire.Error(t, new(Claims).Validate())\n\trequire.Error(t, (&Claims{Issuer: \"not-empty\"}).Validate())\n\trequire.Error(t, (&Claims{Issuer: \"not-empty\"}).Validate())\n\trequire.Error(t, (&Claims{Subject: \"not-empty\"}).Validate())\n\trequire.Error(t, (&Claims{Subject: \"not-empty\"}).Validate())\n\trequire.NoError(t, (&Claims{Issuer: \"not-empty\", Subject: \"not-empty\"}).Validate())\n}\n\ntype TestProvider struct {\n\t*ProviderGenericOIDC\n}\n\nfunc NewTestProvider(c *Configuration, reg Dependencies) Provider {\n\treturn &TestProvider{\n\t\tProviderGenericOIDC: NewProviderGenericOIDC(c, reg).(*ProviderGenericOIDC),\n\t}\n}\n\nfunc RegisterTestProvider(t *testing.T, id string) {\n\tsupportedProviders[id] = func(c *Configuration, reg Dependencies) Provider {\n\t\treturn NewTestProvider(c, reg)\n\t}\n\tt.Cleanup(func() {\n\t\tdelete(supportedProviders, id)\n\t})\n}\n\nvar _ IDTokenVerifier = new(TestProvider)\n\nfunc (t *TestProvider) Verify(_ context.Context, token string) (*Claims, error) {\n\tif token == \"error\" {\n\t\treturn nil, fmt.Errorf(\"stub error\")\n\t}\n\tc := Claims{}\n\tif err := json.Unmarshal([]byte(token), &c); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &c, nil\n}\n\n// SetUserInfoURL allows overriding the userinfo URL for testing purposes.\nfunc (p *ProviderUAEPass) SetUserInfoURL(t *testing.T, url string) {\n\tt.Helper()\n\tp.userinfoURL = url\n}\n\nfunc TestLocale(t *testing.T) {\n\t// test json unmarshal\n\tfor _, tc := range []struct {\n\t\tname      string\n\t\tjson      string\n\t\texpected  string\n\t\tassertErr assert.ErrorAssertionFunc\n\t}{{\n\t\tname:     \"empty\",\n\t\tjson:     `{}`,\n\t\texpected: \"\",\n\t}, {\n\t\tname:     \"empty string locale\",\n\t\tjson:     `{\"locale\":\"\"}`,\n\t\texpected: \"\",\n\t}, {\n\t\tname:      \"invalid string locale\",\n\t\tjson:      `{\"locale\":\"\"\"}`,\n\t\tassertErr: assert.Error,\n\t}, {\n\t\tname:     \"string locale\",\n\t\tjson:     `{\"locale\":\"en-US\"}`,\n\t\texpected: \"en-US\",\n\t}, {\n\t\tname:     \"linkedin locale\",\n\t\tjson:     `{\"locale\":{\"country\":\"US\",\"language\":\"en\",\"ignore\":\"me\"}}`,\n\t\texpected: \"en-US\",\n\t}, {\n\t\tname:     \"missing country linkedin locale\",\n\t\tjson:     `{\"locale\":{\"language\":\"en\"}}`,\n\t\texpected: \"en\",\n\t}, {\n\t\tname:     \"missing language linkedin locale\",\n\t\tjson:     `{\"locale\":{\"country\":\"US\"}}`,\n\t\texpected: \"US\",\n\t}, {\n\t\tname:     \"invalid linkedin locale\",\n\t\tjson:     `{\"locale\":{\"invalid\":\"me\"}}`,\n\t\texpected: \"\",\n\t}} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tvar c Claims\n\t\t\terr := json.Unmarshal([]byte(tc.json), &c)\n\t\t\tif tc.assertErr != nil {\n\t\t\t\ttc.assertErr(t, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, tc.expected, c.Locale)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_test_fedcm.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\n// ProviderTestFedcm is a mock provider to test FedCM.\ntype ProviderTestFedcm struct {\n\t*ProviderGenericOIDC\n}\n\nvar _ OAuth2Provider = (*ProviderTestFedcm)(nil)\n\nfunc NewProviderTestFedcm(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderTestFedcm{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t}\n}\n\nfunc (g *ProviderTestFedcm) Verify(_ context.Context, rawIDToken string) (claims *Claims, err error) {\n\trawClaims := &struct {\n\t\tClaims\n\t\tjwt.MapClaims\n\t}{}\n\t_, err = jwt.ParseWithClaims(rawIDToken, rawClaims, func(token *jwt.Token) (interface{}, error) {\n\t\treturn []byte(`xxxxxxx`), nil\n\t}, jwt.WithoutClaimsValidation())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trawClaims.Issuer = \"https://example.com/fedcm\"\n\n\tif err = rawClaims.Validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &rawClaims.Claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_test_fedcm_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nfunc TestFedcmTestProvider(t *testing.T) {\n\t_, reg := pkg.NewVeryFastRegistryWithoutDB(t)\n\n\tp := oidc.NewProviderTestFedcm(&oidc.Configuration{}, reg)\n\n\trawToken := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1NWVlMjgxNC02ZTQ4LTRmZTktYWIzNS1mM2QxYzczM2I3ZTciLCJub25jZSI6ImVkOWM0ZDcyMDZkMDc1YTg4NjY0ZmE3YjMwY2Q5ZGE2NGU4ZTkwMjY5MGJhZmI2YjNmMmY2OWU5YzU1ZGUyNTcwOTFlYTk3ZTFiZTFiYjdiNDZmMjJjYzY0ZSIsImV4cCI6MTczNzU1ODM4MTk3MSwiaWF0IjoxNzM3NDcxOTgxOTcxLCJlbWFpbCI6InhweGN3dnU1YjRuemZvdGZAZXhhbXBsZS5jb20iLCJuYW1lIjoiVXNlciBOYW1lIiwicGljdHVyZSI6Imh0dHBzOi8vYXBpLmRpY2ViZWFyLmNvbS83LngvYm90dHRzL3BuZz9zZWVkPSUyNDJiJTI0MTAlMjR5WEs3eWozNEg4SkhCNm8zOG1sc2xlYzl1WkozZ2F2UGlDaFdaeFFIbnk3VkFKRlouS3RGZSJ9.GnSP_x8J_yS5wrTwtB6B-BydYYljrpVjQjS2vZ5D8Hg` // #nosec G101 -- test code\n\n\tclaims, err := p.(oidc.IDTokenVerifier).Verify(context.Background(), rawToken)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, claims)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_uaepass.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar _ OAuth2Provider = (*ProviderUAEPass)(nil)\n\n// UAE PASS scopes for different user types\n// See: https://docs.uaepass.ae/feature-guides/authentication/web-application/endpoints\n//\n// For UAE Citizens/Residents:\n//   - urn:uae:digitalid:profile:general - General profile information\n//   - urn:uae:digitalid:profile:general:profileType - Profile type (SOP1/SOP2/SOP3)\n//   - urn:uae:digitalid:profile:general:unifiedId - Unified ID\n//\n// For Visitors:\n//   - urn:uae:digitalid:profile - Basic profile\n//\n// Common scopes:\n//   - openid - Required for OIDC flows (returns id_token)\n\n// ProviderUAEPass implements the OAuth2Provider interface for UAE PASS.\n// UAE PASS is the UAE's official digital identity platform that allows\n// citizens, residents, and visitors to authenticate with government and\n// private sector services.\ntype ProviderUAEPass struct {\n\t*ProviderGenericOIDC\n\tbaseURL     string // Base URL for UAE PASS (e.g., https://id.uaepass.ae or https://stg-id.uaepass.ae)\n\tuserinfoURL string // Can be overridden in tests\n}\n\n// NewProviderUAEPass creates a new UAE PASS provider.\n// The IssuerURL configuration determines which UAE PASS environment to use:\n// - Production: https://id.uaepass.ae (default if not set)\n// - Staging: https://stg-id.uaepass.ae\nfunc NewProviderUAEPass(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\t// Use IssuerURL if provided, otherwise default to production\n\tbaseURL := config.IssuerURL\n\tif baseURL == \"\" {\n\t\tbaseURL = \"https://id.uaepass.ae\"\n\t}\n\n\t// Normalize: remove trailing slash if present\n\tbaseURL = strings.TrimSuffix(baseURL, \"/\")\n\n\t// UAE PASS doesn't support PKCE discovery, so treat \"auto\" (or unset) as \"force\"\n\t// since UAE PASS does support PKCE.\n\tif config.PKCE == \"\" || config.PKCE == \"auto\" {\n\t\tconfig.PKCE = \"force\"\n\t}\n\n\treturn &ProviderUAEPass{\n\t\tProviderGenericOIDC: &ProviderGenericOIDC{\n\t\t\tconfig: config,\n\t\t\treg:    reg,\n\t\t},\n\t\tbaseURL:     baseURL,\n\t\tuserinfoURL: baseURL + \"/idshub/userinfo\",\n\t}\n}\n\n// oauth2 returns the OAuth2 configuration for UAE PASS.\nfunc (p *ProviderUAEPass) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     p.config.ClientID,\n\t\tClientSecret: p.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\tAuthURL:   p.baseURL + \"/idshub/authorize\",\n\t\t\tTokenURL:  p.baseURL + \"/idshub/token\",\n\t\t\tAuthStyle: oauth2.AuthStyleInHeader,\n\t\t},\n\t\tScopes:      p.config.Scope,\n\t\tRedirectURL: p.config.Redir(p.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\n// OAuth2 returns the OAuth2 configuration for UAE PASS.\nfunc (p *ProviderUAEPass) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn p.oauth2(ctx), nil\n}\n\n// UAE PASS acr_values for different authentication levels\n// UAEPassACRWeb is the default authentication level for web-based authentication\nconst UAEPassACRWeb = \"urn:safelayer:tws:policies:authentication:level:low\" //nolint:gosec // not a credential, this is an ACR URN\n\n// AuthCodeURLOptions returns OAuth2 authorization URL options.\n// UAE PASS requires acr_values parameter for authentication.\n// The default is web-based authentication. To use mobile app-to-app\n// authentication, pass acr_values via upstream_parameters.\nfunc (p *ProviderUAEPass) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{\n\t\toauth2.SetAuthURLParam(\"acr_values\", UAEPassACRWeb),\n\t}\n}\n\n// Claims fetches user claims from the UAE PASS userinfo endpoint.\n// UAE PASS requires the access token to be passed as a Bearer token in the Authorization header.\nfunc (p *ProviderUAEPass) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (_ *Claims, err error) {\n\tctx, span := p.reg.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.ProviderUAEPass.Claims\")\n\tdefer otelx.End(span, &err)\n\n\tctx, client := httpx.SetOAuth2(ctx, p.reg.HTTPClient(ctx), p.oauth2(ctx), exchange)\n\n\treq, err := retryablehttp.NewRequestWithContext(ctx, http.MethodGet, p.userinfoURL, nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.\n\t\t\tWithReason(\"failed to create HTTP request\").\n\t\t\tWithDetail(\"url\", p.userinfoURL).\n\t\t\tWithError(err.Error()))\n\t}\n\n\t// UAE PASS expects the access token as a Bearer token in the Authorization header\n\treq.Header.Set(\"Authorization\", \"Bearer \"+exchange.AccessToken)\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.\n\t\t\tWithReason(\"failed to make HTTP request to UAE PASS userinfo endpoint\").\n\t\t\tWithDetail(\"url\", p.userinfoURL).\n\t\t\tWithError(err.Error()))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tbody := io.LimitReader(resp.Body, 64*1024) // 64 KiB limit\n\n\tif resp.StatusCode != http.StatusOK {\n\t\trawResponse, _ := io.ReadAll(body)\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.\n\t\t\tWithReason(\"UAE PASS userinfo endpoint returned non-200 status\").\n\t\t\tWithDetail(\"url\", p.userinfoURL).\n\t\t\tWithDetail(\"external_error\", string(rawResponse)).\n\t\t\tWithDetail(\"external_status_code\", resp.StatusCode))\n\t}\n\n\tvar rawClaims map[string]interface{}\n\tif err := json.NewDecoder(body).Decode(&rawClaims); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.\n\t\t\tWithReason(\"failed to decode UAE PASS userinfo response\").\n\t\t\tWithDetail(\"url\", p.userinfoURL).\n\t\t\tWithError(err.Error()))\n\t}\n\n\tstr := func(key string) string {\n\t\tif v, ok := rawClaims[key].(string); ok {\n\t\t\treturn v\n\t\t}\n\t\treturn \"\"\n\t}\n\n\t// Map UAE PASS profile to standard OIDC claims\n\tclaims := &Claims{\n\t\tSubject:   str(\"sub\"),\n\t\tIssuer:    p.baseURL,\n\t\tEmail:     str(\"email\"),\n\t\tName:      str(\"fullnameEN\"),\n\t\tGivenName: str(\"firstnameEN\"),\n\t\tLastName:  str(\"lastnameEN\"),\n\t\tRawClaims: rawClaims,\n\t}\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_uaepass_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n)\n\nfunc TestUAEPassOIDCClaims(t *testing.T) {\n\tt.Parallel()\n\n\thandler := http.NewServeMux()\n\texpectedAccessToken := \"my-access-token\"\n\n\thandler.HandleFunc(\"GET /userinfo\", func(w http.ResponseWriter, r *http.Request) {\n\t\t// Verify Bearer token authentication\n\t\tauthHeader := r.Header.Get(\"Authorization\")\n\t\tif authHeader != \"Bearer \"+expectedAccessToken {\n\t\t\tw.WriteHeader(http.StatusUnauthorized)\n\t\t\t_, _ = w.Write([]byte(`{\"error\": \"invalid_token\"}`))\n\t\t\treturn\n\t\t}\n\n\t\t// UAE PASS citizen/resident profile response\n\t\tuserProfile := `{\n\t\t\t\"sub\": \"uaepass-user-123456\",\n\t\t\t\"email\": \"user@example.ae\",\n\t\t\t\"fullnameEN\": \"Ahmed Mohammed Al Rashid\",\n\t\t\t\"fullnameAR\": \"أحمد محمد الراشد\",\n\t\t\t\"firstnameEN\": \"Ahmed\",\n\t\t\t\"firstnameAR\": \"أحمد\",\n\t\t\t\"lastnameEN\": \"Al Rashid\",\n\t\t\t\"lastnameAR\": \"الراشد\",\n\t\t\t\"uuid\": \"550e8400-e29b-41d4-a716-446655440000\",\n\t\t\t\"unifiedId\": \"784-1990-1234567-1\",\n\t\t\t\"idn\": \"784199012345671\",\n\t\t\t\"userType\": \"SOP1\",\n\t\t\t\"nationalityEN\": \"ARE\",\n\t\t\t\"gender\": \"M\",\n\t\t\t\"dob\": \"1990-05-15\",\n\t\t\t\"mobile\": \"+971501234567\"\n\t\t}`\n\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\t_, err := w.Write([]byte(userProfile))\n\t\trequire.NoError(t, err)\n\t})\n\n\tuaePassAPI := httptest.NewServer(handler)\n\tt.Cleanup(uaePassAPI.Close)\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tScope: []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\tp.SetUserInfoURL(t, uaePassAPI.URL+\"/userinfo\")\n\n\tclaims, err := p.Claims(t.Context(), &oauth2.Token{AccessToken: expectedAccessToken}, nil)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, claims)\n\n\t// Verify standard claims\n\tassert.Equal(t, \"uaepass-user-123456\", claims.Subject)\n\tassert.Equal(t, \"user@example.ae\", claims.Email)\n\tassert.Equal(t, \"Ahmed Mohammed Al Rashid\", claims.Name)\n\tassert.Equal(t, \"Ahmed\", claims.GivenName)\n\tassert.Equal(t, \"Al Rashid\", claims.LastName)\n\n\t// Verify raw claims contain UAE PASS specific data\n\trequire.NotNil(t, claims.RawClaims)\n\tassert.Equal(t, \"550e8400-e29b-41d4-a716-446655440000\", claims.RawClaims[\"uuid\"])\n\tassert.Equal(t, \"784-1990-1234567-1\", claims.RawClaims[\"unifiedId\"])\n\tassert.Equal(t, \"784199012345671\", claims.RawClaims[\"idn\"])\n\tassert.Equal(t, \"SOP1\", claims.RawClaims[\"userType\"])\n\tassert.Equal(t, \"ARE\", claims.RawClaims[\"nationalityEN\"])\n\tassert.Equal(t, \"M\", claims.RawClaims[\"gender\"])\n\tassert.Equal(t, \"1990-05-15\", claims.RawClaims[\"dob\"])\n\tassert.Equal(t, \"+971501234567\", claims.RawClaims[\"mobile\"])\n\tassert.Equal(t, \"أحمد محمد الراشد\", claims.RawClaims[\"fullnameAR\"])\n}\n\nfunc TestUAEPassOIDCClaimsVisitor(t *testing.T) {\n\tt.Parallel()\n\n\thandler := http.NewServeMux()\n\texpectedAccessToken := \"visitor-access-token\"\n\n\thandler.HandleFunc(\"GET /userinfo\", func(w http.ResponseWriter, r *http.Request) {\n\t\tauthHeader := r.Header.Get(\"Authorization\")\n\t\tif authHeader != \"Bearer \"+expectedAccessToken {\n\t\t\tw.WriteHeader(http.StatusUnauthorized)\n\t\t\treturn\n\t\t}\n\n\t\t// UAE PASS visitor profile response (limited data)\n\t\tuserProfile := `{\n\t\t\t\"sub\": \"uaepass-visitor-789\",\n\t\t\t\"email\": \"visitor@example.com\",\n\t\t\t\"fullnameEN\": \"John Smith\",\n\t\t\t\"firstnameEN\": \"John\",\n\t\t\t\"lastnameEN\": \"Smith\",\n\t\t\t\"uuid\": \"660e8400-e29b-41d4-a716-446655440001\",\n\t\t\t\"userType\": \"SOP3\"\n\t\t}`\n\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\t_, _ = w.Write([]byte(userProfile))\n\t})\n\n\tuaePassAPI := httptest.NewServer(handler)\n\tt.Cleanup(uaePassAPI.Close)\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tScope: []string{\"urn:uae:digitalid:profile\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\tp.SetUserInfoURL(t, uaePassAPI.URL+\"/userinfo\")\n\n\tclaims, err := p.Claims(t.Context(), &oauth2.Token{AccessToken: expectedAccessToken}, nil)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, claims)\n\n\tassert.Equal(t, \"uaepass-visitor-789\", claims.Subject)\n\tassert.Equal(t, \"visitor@example.com\", claims.Email)\n\tassert.Equal(t, \"John Smith\", claims.Name)\n\tassert.Equal(t, \"SOP3\", claims.RawClaims[\"userType\"])\n}\n\nfunc TestUAEPassOIDCClaimsError(t *testing.T) {\n\tt.Parallel()\n\n\thandler := http.NewServeMux()\n\n\thandler.HandleFunc(\"GET /userinfo\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusUnauthorized)\n\t\t_, _ = w.Write([]byte(`{\"error\": \"invalid_token\", \"error_description\": \"The access token is expired\"}`))\n\t})\n\n\tuaePassAPI := httptest.NewServer(handler)\n\tt.Cleanup(uaePassAPI.Close)\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tp := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tScope: []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\tp.SetUserInfoURL(t, uaePassAPI.URL+\"/userinfo\")\n\n\tclaims, err := p.Claims(t.Context(), &oauth2.Token{AccessToken: \"invalid-token\"}, nil)\n\trequire.Error(t, err)\n\trequire.Nil(t, claims)\n}\n\nfunc TestUAEPassIssuerURL(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\t// Test staging via IssuerURL\n\tpStaging := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tIssuerURL: \"https://stg-id.uaepass.ae\",\n\t\tScope:     []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\n\toauth2Config, err := pStaging.OAuth2(t.Context())\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"https://stg-id.uaepass.ae/idshub/authorize\", oauth2Config.Endpoint.AuthURL)\n\tassert.Equal(t, \"https://stg-id.uaepass.ae/idshub/token\", oauth2Config.Endpoint.TokenURL)\n\n\t// Test production via IssuerURL\n\tpProduction := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tIssuerURL: \"https://id.uaepass.ae\",\n\t\tScope:     []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\n\toauth2Config, err = pProduction.OAuth2(t.Context())\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"https://id.uaepass.ae/idshub/authorize\", oauth2Config.Endpoint.AuthURL)\n\tassert.Equal(t, \"https://id.uaepass.ae/idshub/token\", oauth2Config.Endpoint.TokenURL)\n\n\t// Test default (production) when IssuerURL is not set\n\tpDefault := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tScope: []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\n\toauth2Config, err = pDefault.OAuth2(t.Context())\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"https://id.uaepass.ae/idshub/authorize\", oauth2Config.Endpoint.AuthURL)\n\tassert.Equal(t, \"https://id.uaepass.ae/idshub/token\", oauth2Config.Endpoint.TokenURL)\n\n\t// Test trailing slash is handled correctly\n\tpTrailingSlash := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tIssuerURL: \"https://stg-id.uaepass.ae/\",\n\t\tScope:     []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\n\toauth2Config, err = pTrailingSlash.OAuth2(t.Context())\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"https://stg-id.uaepass.ae/idshub/authorize\", oauth2Config.Endpoint.AuthURL)\n\tassert.Equal(t, \"https://stg-id.uaepass.ae/idshub/token\", oauth2Config.Endpoint.TokenURL)\n}\n\nfunc TestUAEPassConfigurationValidation(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\t// Test PKCE auto falls back to force (no error)\n\tpPKCEAuto := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tPKCE:  \"auto\",\n\t\tScope: []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\n\t_, err := pPKCEAuto.OAuth2(t.Context())\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"force\", pPKCEAuto.Config().PKCE)\n\n\t// Test PKCE unset also falls back to force\n\tpPKCEUnset := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tScope: []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\n\t_, err = pPKCEUnset.OAuth2(t.Context())\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"force\", pPKCEUnset.Config().PKCE)\n\n\t// Test valid configuration\n\tpValid := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tPKCE:  \"never\",\n\t\tScope: []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\n\toauth2Config, err := pValid.OAuth2(t.Context())\n\trequire.NoError(t, err)\n\trequire.NotNil(t, oauth2Config)\n}\n\nfunc TestUAEPassAuthFlowDefaultsToWeb(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tp := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tScope: []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\n\toptions := p.AuthCodeURLOptions(nil)\n\trequire.Len(t, options, 1)\n\n\toauth2Config, err := p.OAuth2(t.Context())\n\trequire.NoError(t, err)\n\n\tauthURL := oauth2Config.AuthCodeURL(\"test-state\", options...)\n\tparsedURL, err := url.Parse(authURL)\n\trequire.NoError(t, err)\n\n\tacrValue := parsedURL.Query().Get(\"acr_values\")\n\tassert.Equal(t, oidc.UAEPassACRWeb, acrValue)\n}\n\nfunc TestUAEPassMobileACRViaUpstreamParams(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tp := oidc.NewProviderUAEPass(&oidc.Configuration{\n\t\tScope: []string{\"urn:uae:digitalid:profile:general\"},\n\t}, reg).(*oidc.ProviderUAEPass)\n\n\toauth2Config, err := p.OAuth2(t.Context())\n\trequire.NoError(t, err)\n\n\t// Simulate the ordering in getAuthRedirectURL: provider defaults first, then upstream params.\n\t// Upstream acr_values should override the provider's default web ACR value.\n\tvar opts []oauth2.AuthCodeOption\n\topts = append(opts, p.AuthCodeURLOptions(nil)...)\n\topts = append(opts, oidc.UpstreamParameters(map[string]string{\n\t\t\"acr_values\": \"urn:digitalid:authentication:flow:mobileondevice\",\n\t})...)\n\n\tauthURL := oauth2Config.AuthCodeURL(\"test-state\", opts...)\n\tparsedURL, err := url.Parse(authURL)\n\trequire.NoError(t, err)\n\n\tacrValue := parsedURL.Query().Get(\"acr_values\")\n\tassert.Equal(t, \"urn:digitalid:authentication:flow:mobileondevice\", acrValue)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_userinfo_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/jarcoal/httpmock\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype mockRegistry struct {\n\t*driver.RegistryDefault\n\tcl *retryablehttp.Client\n}\n\nfunc (s *mockRegistry) HTTPClient(ctx context.Context, opts ...httpx.ResilientOptions) *retryablehttp.Client {\n\treturn s.cl\n}\n\nfunc TestProviderClaimsRespectsErrorCodes(t *testing.T) {\n\tconf, base := pkg.NewFastRegistryWithMocks(t)\n\tconf.MustSet(context.Background(), config.ViperKeyClientHTTPNoPrivateIPRanges, true)\n\tbase.SetTracer(otelx.NewNoop())\n\treg := &mockRegistry{base, retryablehttp.NewClient()}\n\n\tctx := context.Background()\n\ttoken := &oauth2.Token{AccessToken: \"foo\", Expiry: time.Now().Add(time.Hour)}\n\n\texpectedClaims := &oidc.Claims{\n\t\tIssuer:            \"ignore-me\",\n\t\tSubject:           \"123456789012345\",\n\t\tName:              \"John Doe\",\n\t\tGivenName:         \"John\",\n\t\tFamilyName:        \"Doe\",\n\t\tNickname:          \"John Doe\",\n\t\tPreferredUsername: \"John Doe\",\n\t\tEmail:             \"john.doe@example.com\",\n\t\tEmailVerified:     true,\n\t\tBirthdate:         \"01/01/1990\",\n\t}\n\n\tdefaultUserinfoHandler := func(req *http.Request) (*http.Response, error) {\n\t\tif head := req.Header.Get(\"Authorization\"); len(head) == 0 {\n\t\t\tresp, err := httpmock.NewJsonResponse(401, map[string]interface{}{\"error\": \"\"})\n\t\t\treturn resp, err\n\t\t}\n\n\t\tresp, err := httpmock.NewJsonResponse(200, expectedClaims)\n\t\treturn resp, err\n\t}\n\n\tfor _, tc := range []struct {\n\t\tname             string\n\t\tissuer           string\n\t\tuserInfoEndpoint string\n\t\tconfig           *oidc.Configuration\n\t\tprovider         oidc.Provider\n\t\tuserInfoHandler  func(req *http.Request) (*http.Response, error)\n\t\texpectedClaims   *oidc.Claims\n\t\tuseToken         *oauth2.Token\n\t\thook             func(t *testing.T)\n\t}{\n\t\t{\n\t\t\tname:             \"auth0\",\n\t\t\tuserInfoHandler:  defaultUserinfoHandler,\n\t\t\tuserInfoEndpoint: \"https://www.auth0.com/userinfo\",\n\t\t\tprovider: oidc.NewProviderAuth0(&oidc.Configuration{\n\t\t\t\tIssuerURL: \"https://www.auth0.com\",\n\t\t\t\tID:        \"auth0\",\n\t\t\t\tProvider:  \"auth0\",\n\t\t\t}, reg),\n\t\t},\n\t\t{\n\t\t\tname:             \"salesforce\",\n\t\t\tuserInfoHandler:  defaultUserinfoHandler,\n\t\t\tuserInfoEndpoint: \"https://www.salesforce.com/services/oauth2/userinfo\",\n\t\t\tprovider: oidc.NewProviderSalesforce(&oidc.Configuration{\n\t\t\t\tIssuerURL: \"https://www.salesforce.com\",\n\t\t\t\tID:        \"salesforce\",\n\t\t\t\tProvider:  \"salesforce\",\n\t\t\t}, reg),\n\t\t},\n\t\t{\n\t\t\tname:             \"netid\",\n\t\t\tuserInfoHandler:  defaultUserinfoHandler,\n\t\t\tuserInfoEndpoint: \"https://broker.netid.de/userinfo\",\n\t\t\tprovider: oidc.NewProviderNetID(&oidc.Configuration{\n\t\t\t\tIssuerURL: \"https://broker.netid.de\",\n\t\t\t\tID:        \"netid\",\n\t\t\t\tProvider:  \"netid\",\n\t\t\t\tClientID:  \"foo\",\n\t\t\t}, reg),\n\t\t\tuseToken: token.WithExtra(map[string]interface{}{\"id_token\": \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsImlhdCI6MTUxNjIzOTAyMiwidGlkIjoiYTliODYzODUtZjMyYy00ODAzLWFmYzgtNGIyMzEyZmJkZjI0IiwiaXNzIjoiaHR0cHM6Ly9icm9rZXIubmV0aWQuZGUvIiwiYXVkIjpbImZvbyJdLCJleHAiOjQwNzE3Mjg1MDR9.Zt_-9jULoENQ7pq6rKrevhecBlWKR2rzNti42EJti_OelCrGbl5zyHYRfIg264VzEY0Zp9BAZTWmcF6Z-cpuD05RddTXZDrrC_47bJeYDL-bjDfKjoSZUt_1JnNFgqeyMA1Ji6HCIsEf_g8QnuSELAO0c-qb2ANmDLVL8_dY6oUmCN5oWLJS2q6xO-Iaz-AuKaGTbLZBcjCe2NB_--kIx4IrgLm78U90X3ePOV0CcYZLNvDgzEsVJ2M4ixKp87bYUaZZ3JJEjuxgnHqKRMExDKron3mvfQtC1L-dZQyeDo3-mCFJL_JhEw9zLmoWFBD7aARrVL_yAe31o26FB3S-Dg\"}),\n\t\t\thook: func(t *testing.T) {\n\t\t\t\thttpmock.RegisterResponder(\"GET\", \"https://broker.netid.de/.well-known/openid-configuration\",\n\t\t\t\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\t\t\t\treturn httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\t\t\t\"issuer\":   \"https://broker.netid.de/\",\n\t\t\t\t\t\t\t\"jwks_uri\": \"https://broker.netid.de/.well-known/jwks.json\",\n\t\t\t\t\t\t})\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\thttpmock.RegisterResponder(\"GET\", \"https://broker.netid.de/.well-known/jwks.json\",\n\t\t\t\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\t\t\t\treturn httpmock.NewJsonResponse(200, json.RawMessage(`{\n  \"keys\": [\n    {\n      \"kty\": \"RSA\",\n      \"e\": \"AQAB\",\n      \"alg\": \"RS256\",\n      \"use\": \"sig\",\n      \"n\": \"uw0hrmZpA5CH5lDnjbCK6ZQD-I0BGxde-ChDzUz5eK4r7VtyMGvAxwD-k-mWw4FJ2NgYmT_T89sAtE6NQqT5u9HAe-CI22lf5LMvmqvMzekcmBAvXNw8VeTV_N6CbS9INJrxf20cObf-kpTxVxlYtYxYwIhYdOw3DwX8y31vI38qHQ4_OzTRo4KFVLCr68MzIHHRI4d5EHrFv1VFjiaa_ATwuwCMUfg0RMnK09FpMCgvp0E6bQeptXhBBNQVQkoC5whT1GzikfSxyeugjQ_XuTt1MKoyYsN2pmfrBdcfWrPYvV_JPgO1MkEtqErvtCByairINfXrHTMOxNWe3sYlXQ\"\n    }\n  ]\n}`))\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t},\n\t\t\texpectedClaims: &oidc.Claims{Issuer: \"https://broker.netid.de/\", Subject: \"1234567890\", Name: \"John Doe\", GivenName: \"John\", FamilyName: \"Doe\", LastName: \"\", MiddleName: \"\", Nickname: \"John Doe\", PreferredUsername: \"John Doe\", Profile: \"\", Picture: \"\", Website: \"\", Email: \"john.doe@example.com\", EmailVerified: true, Gender: \"\", Birthdate: \"01/01/1990\", Zoneinfo: \"\", Locale: \"\", PhoneNumber: \"\", PhoneNumberVerified: false, UpdatedAt: 0, HD: \"\", Team: \"\"},\n\t\t},\n\t\t{\n\t\t\tname:             \"vk\",\n\t\t\tuserInfoEndpoint: \"https://api.vk.com/method/users.get\",\n\t\t\tprovider: oidc.NewProviderVK(&oidc.Configuration{\n\t\t\t\tIssuerURL: \"https://oauth.vk.com\",\n\t\t\t\tID:        \"vk\",\n\t\t\t\tProvider:  \"vk\",\n\t\t\t}, reg),\n\t\t\tuseToken: token.WithExtra(map[string]interface{}{\"email\": \"john.doe@example.com\"}),\n\t\t\tuserInfoHandler: func(req *http.Request) (*http.Response, error) {\n\t\t\t\tif head := req.URL.Query().Get(\"access_token\"); len(head) == 0 {\n\t\t\t\t\tresp, err := httpmock.NewJsonResponse(401, map[string]interface{}{\"error\": \"\"})\n\t\t\t\t\treturn resp, err\n\t\t\t\t}\n\n\t\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\t\"response\": []map[string]interface{}{{\"id\": 123456789012345}},\n\t\t\t\t})\n\t\t\t\treturn resp, err\n\t\t\t},\n\n\t\t\texpectedClaims: &oidc.Claims{\n\t\t\t\tIssuer:  \"https://api.vk.com/method/users.get\",\n\t\t\t\tSubject: \"123456789012345\",\n\t\t\t\tEmail:   \"john.doe@example.com\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:             \"yandex\",\n\t\t\tuserInfoEndpoint: \"https://login.yandex.ru/info\",\n\t\t\tprovider: oidc.NewProviderYandex(&oidc.Configuration{\n\t\t\t\tIssuerURL: \"https://oauth.yandex.com\",\n\t\t\t\tID:        \"vk\",\n\t\t\t\tProvider:  \"vk\",\n\t\t\t}, reg),\n\t\t\tuseToken: token.WithExtra(map[string]interface{}{\"email\": \"john.doe@example.com\"}),\n\t\t\tuserInfoHandler: func(req *http.Request) (*http.Response, error) {\n\t\t\t\tif head := req.URL.Query().Get(\"oauth_token\"); len(head) == 0 {\n\t\t\t\t\tresp, err := httpmock.NewJsonResponse(401, map[string]interface{}{\"error\": \"\"})\n\t\t\t\t\treturn resp, err\n\t\t\t\t}\n\n\t\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\t\"id\":            \"123456789012345\",\n\t\t\t\t\t\"default_email\": \"john.doe@example.com\",\n\t\t\t\t})\n\t\t\t\treturn resp, err\n\t\t\t},\n\n\t\t\texpectedClaims: &oidc.Claims{\n\t\t\t\tIssuer:  \"https://login.yandex.ru/info\",\n\t\t\t\tSubject: \"123456789012345\",\n\t\t\t\tEmail:   \"john.doe@example.com\",\n\t\t\t\tPicture: \"https://avatars.yandex.net/get-yapic//islands-200\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"facebook\",\n\t\t\thook: func(t *testing.T) {\n\t\t\t\thttpmock.RegisterResponder(\"GET\", \"https://www.facebook.com/.well-known/openid-configuration\",\n\t\t\t\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\t\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\t\t\t\"issuer\": \"https://www.facebook.com\",\n\t\t\t\t\t\t})\n\t\t\t\t\t\treturn resp, err\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t},\n\t\t\tuserInfoEndpoint: \"https://graph.facebook.com/me\",\n\t\t\tprovider: oidc.NewProviderFacebook(&oidc.Configuration{\n\t\t\t\tID:       \"facebook\",\n\t\t\t\tProvider: \"facebook\",\n\t\t\t}, reg),\n\t\t\tuseToken: token,\n\t\t\tuserInfoHandler: func(req *http.Request) (*http.Response, error) {\n\t\t\t\tif head := req.Header.Get(\"Authorization\"); len(head) == 0 {\n\t\t\t\t\tresp, err := httpmock.NewJsonResponse(401, map[string]interface{}{\"error\": \"\"})\n\t\t\t\t\treturn resp, err\n\t\t\t\t}\n\t\t\t\tif _, ok := req.URL.Query()[\"appsecret_proof\"]; !ok {\n\t\t\t\t\tresp, err := httpmock.NewJsonResponse(400, map[string]interface{}{\"error\": \"\"})\n\t\t\t\t\treturn resp, err\n\t\t\t\t}\n\n\t\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\t\"id\":         \"123456789012345\",\n\t\t\t\t\t\"name\":       \"John Doe\",\n\t\t\t\t\t\"first_name\": \"John\",\n\t\t\t\t\t\"last_name\":  \"Doe\",\n\t\t\t\t\t\"email\":      \"john.doe@example.com\",\n\t\t\t\t\t\"birthday\":   \"01/01/1990\",\n\t\t\t\t})\n\t\t\t\treturn resp, err\n\t\t\t},\n\t\t\texpectedClaims: &oidc.Claims{\n\t\t\t\tIssuer:            \"https://graph.facebook.com/me?fields=id,name,first_name,last_name,middle_name,email,picture,birthday,gender&appsecret_proof=0c0d98f7e3d9d45e72e8877bc1b104327efb9c07b18f2ffeced76d81307f1fff\",\n\t\t\t\tSubject:           \"123456789012345\",\n\t\t\t\tName:              \"John Doe\",\n\t\t\t\tGivenName:         \"John\",\n\t\t\t\tFamilyName:        \"Doe\",\n\t\t\t\tNickname:          \"John Doe\",\n\t\t\t\tPreferredUsername: \"John Doe\",\n\t\t\t\tEmail:             \"john.doe@example.com\",\n\t\t\t\tEmailVerified:     true,\n\t\t\t\tBirthdate:         \"01/01/1990\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:             \"gitlab\",\n\t\t\tuserInfoHandler:  defaultUserinfoHandler,\n\t\t\tuserInfoEndpoint: \"https://www.gitlab.com/oauth/userinfo\",\n\t\t\tprovider: oidc.NewProviderGitLab(&oidc.Configuration{\n\t\t\t\tIssuerURL: \"https://www.gitlab.com\",\n\t\t\t\tID:        \"gitlab\",\n\t\t\t\tProvider:  \"gitlab\",\n\t\t\t}, reg),\n\t\t},\n\t\t// Microsoft is a more complicated set up because it actually verifies the ID Token before using the userinfo endpoint.\n\t\t{\n\t\t\tname: \"microsoft\",\n\t\t\tuserInfoHandler: func(req *http.Request) (*http.Response, error) {\n\t\t\t\tif head := req.Header.Get(\"Authorization\"); len(head) == 0 {\n\t\t\t\t\tresp, err := httpmock.NewJsonResponse(401, map[string]interface{}{\"error\": \"\"})\n\t\t\t\t\treturn resp, err\n\t\t\t\t}\n\n\t\t\t\tresp, err := httpmock.NewJsonResponse(200, json.RawMessage(`{\"id\":\"new-id\"}`))\n\t\t\t\treturn resp, err\n\t\t\t},\n\t\t\tuserInfoEndpoint: \"https://graph.microsoft.com/v1.0/me\",\n\t\t\tprovider: oidc.NewProviderMicrosoft(&oidc.Configuration{\n\t\t\t\tIssuerURL:     \"https://login.microsoftonline.com/\",\n\t\t\t\tID:            \"microsoft\",\n\t\t\t\tProvider:      \"microsoft\",\n\t\t\t\tTenant:        \"a9b86385-f32c-4803-afc8-4b2312fbdf24\",\n\t\t\t\tClientID:      \"foo\",\n\t\t\t\tClientSecret:  \"bar\",\n\t\t\t\tSubjectSource: \"me\",\n\t\t\t}, reg),\n\t\t\tuseToken: token.WithExtra(map[string]interface{}{\"id_token\": \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsImlhdCI6MTUxNjIzOTAyMiwidGlkIjoiYTliODYzODUtZjMyYy00ODAzLWFmYzgtNGIyMzEyZmJkZjI0IiwiaXNzIjoiaHR0cHM6Ly9sb2dpbi5taWNyb3NvZnRvbmxpbmUuY29tL2E5Yjg2Mzg1LWYzMmMtNDgwMy1hZmM4LTRiMjMxMmZiZGYyNC92Mi4wIiwiYXVkIjpbImZvbyJdLCJleHAiOjQwNzE3Mjg1MDR9.LRgolO5_-26uMrBo89NCzUfi87a8jf7rXlWgZVYnpfowzqn-U0JhNGVzEOXOACoPdX9HsEtYj4hZxYgYcd6z7yqgmOZXE-y58L5BHYZU1kk37O1Dl_VnN-BmeCWs_JZiXF2KEnu7BW8btYnb26qCnc3_8RGbyJwI4UU6ynbJzAmLCPUPgoMZ2Jpahx_K8vqLe-4rveLyUvVVHMoUAV16I-Wg08GcuA5cY0_91-QpA5Kq0AA58wbrUbAOEAOYOpa_63QqYcCcZLbnX_w09Z1YCXGGVSbbpzjr3cJ8EFA6doloBKRLWtNtEnGk4hyjHyp_ls89ZYqJ1ngy95AEswzwJQ\"}),\n\t\t\thook: func(t *testing.T) {\n\t\t\t\thttpmock.RegisterResponder(\"GET\", \"https://login.microsoftonline.com/a9b86385-f32c-4803-afc8-4b2312fbdf24/v2.0/.well-known/openid-configuration\",\n\t\t\t\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\t\t\t\treturn httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\t\t\t\"issuer\":   \"https://login.microsoftonline.com/a9b86385-f32c-4803-afc8-4b2312fbdf24/v2.0\",\n\t\t\t\t\t\t\t\"jwks_uri\": \"https://login.microsoftonline.com/a9b86385-f32c-4803-afc8-4b2312fbdf24/v2.0/.well-known/jwks.json\",\n\t\t\t\t\t\t})\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\thttpmock.RegisterResponder(\"GET\", \"https://login.microsoftonline.com/a9b86385-f32c-4803-afc8-4b2312fbdf24/v2.0/.well-known/jwks.json\",\n\t\t\t\t\tfunc(req *http.Request) (*http.Response, error) {\n\t\t\t\t\t\treturn httpmock.NewJsonResponse(200, json.RawMessage(`{\n  \"keys\": [\n    {\n      \"kty\": \"RSA\",\n      \"e\": \"AQAB\",\n      \"use\": \"sig\",\n      \"alg\": \"RS256\",\n      \"n\": \"xGnYx5u9S7LCrtINTI1Kav6pqzFj72JhYunqrDhnG18Iu-MJdCSmd26xiiZFY6UsJoG-te_DcUE_YHvhx6vwC0tJO-Z2uxwKRxWsMiyW8v3V0Bfbtu7jlf0kpQYeAeAIAHih7GU_v5jtxaGLNR9JIbmENUGOoIfydfTWLKHtwI0MB8tNVwjqP8e6ZSr6DNIjsKKuz98BeRlZ576jyH2GcmM9DguLZv73qU1m1OLoHFW5rAPH70-nhr9V67TTR1A1is9v85uLlKBnZgKp-EdYtBEvKRWSpw5vxCnKggJjEYATOB2H5m_c8F1e1tsJLAehaXKMKZYI-VlWWxj7KixDKw\"\n    }\n  ]\n}`))\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t},\n\t\t\texpectedClaims: &oidc.Claims{\n\t\t\t\tIssuer: \"https://login.microsoftonline.com/a9b86385-f32c-4803-afc8-4b2312fbdf24/v2.0\", Subject: \"new-id\", Name: \"John Doe\", Email: \"john.doe@example.com\",\n\t\t\t\tRawClaims: map[string]interface{}{\"aud\": []interface{}{\"foo\"}, \"exp\": 4.071728504e+09, \"iat\": 1.516239022e+09, \"iss\": \"https://login.microsoftonline.com/a9b86385-f32c-4803-afc8-4b2312fbdf24/v2.0\", \"email\": \"john.doe@example.com\", \"name\": \"John Doe\", \"sub\": \"1234567890\", \"tid\": \"a9b86385-f32c-4803-afc8-4b2312fbdf24\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"dingtalk\",\n\t\t\tuserInfoHandler: func(req *http.Request) (*http.Response, error) {\n\t\t\t\tif head := req.Header.Get(\"x-acs-dingtalk-access-token\"); len(head) == 0 {\n\t\t\t\t\tresp, err := httpmock.NewJsonResponse(401, map[string]interface{}{\"error\": \"\"})\n\t\t\t\t\treturn resp, err\n\t\t\t\t}\n\n\t\t\t\tresp, err := httpmock.NewJsonResponse(200, map[string]interface{}{\n\t\t\t\t\t\"openId\": \"123456789012345\",\n\t\t\t\t\t\"email\":  \"john.doe@example.com\",\n\t\t\t\t})\n\t\t\t\treturn resp, err\n\t\t\t},\n\t\t\tuserInfoEndpoint: \"https://api.dingtalk.com/v1.0/contact/users/me\",\n\t\t\tprovider: oidc.NewProviderDingTalk(&oidc.Configuration{\n\t\t\t\tIssuerURL: \"https://www.dingtalk.com\",\n\t\t\t\tID:        \"dingtalk\",\n\t\t\t\tProvider:  \"dingtalk\",\n\t\t\t}, reg),\n\t\t\texpectedClaims: &oidc.Claims{\n\t\t\t\tIssuer:  \"https://api.dingtalk.com/v1.0/contact/users/me\",\n\t\t\t\tSubject: \"123456789012345\",\n\t\t\t\tEmail:   \"john.doe@example.com\",\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\ttoken := token\n\t\t\tif tc.useToken != nil {\n\t\t\t\ttoken = tc.useToken\n\t\t\t}\n\n\t\t\tt.Run(\"http error is respected\", func(t *testing.T) {\n\t\t\t\thttpmock.ActivateNonDefault(reg.cl.HTTPClient)\n\t\t\t\tt.Cleanup(httpmock.DeactivateAndReset)\n\n\t\t\t\tif tc.hook != nil {\n\t\t\t\t\ttc.hook(t)\n\t\t\t\t}\n\n\t\t\t\thttpmock.RegisterResponder(\"GET\", tc.userInfoEndpoint, func(req *http.Request) (*http.Response, error) {\n\t\t\t\t\treturn httpmock.NewJsonResponse(455, map[string]interface{}{})\n\t\t\t\t})\n\n\t\t\t\t_, err := tc.provider.(oidc.OAuth2Provider).Claims(ctx, token, url.Values{})\n\t\t\t\tvar he *herodot.DefaultError\n\t\t\t\trequire.ErrorAs(t, err, &he)\n\t\t\t\tassert.Equal(t, \"OpenID Connect provider returned a 455 status code but 200 is expected.\", he.Reason(), \"%+v\", err)\n\t\t\t})\n\n\t\t\tt.Run(\"call is successful\", func(t *testing.T) {\n\t\t\t\thttpmock.ActivateNonDefault(reg.cl.HTTPClient)\n\t\t\t\tt.Cleanup(httpmock.DeactivateAndReset)\n\n\t\t\t\tif tc.hook != nil {\n\t\t\t\t\ttc.hook(t)\n\t\t\t\t}\n\n\t\t\t\thttpmock.RegisterResponder(\"GET\", tc.userInfoEndpoint, tc.userInfoHandler)\n\n\t\t\t\tclaims, err := tc.provider.(oidc.OAuth2Provider).Claims(ctx, token, url.Values{})\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tif tc.expectedClaims == nil {\n\t\t\t\t\tassert.Equal(t, expectedClaims, claims)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Equal(t, tc.expectedClaims, claims)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_vk.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"strconv\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar _ OAuth2Provider = (*ProviderVK)(nil)\n\ntype ProviderVK struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc NewProviderVK(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderVK{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (g *ProviderVK) Config() *Configuration {\n\treturn g.config\n}\n\nfunc (g *ProviderVK) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\tAuthURL:  \"https://oauth.vk.com/authorize\",\n\t\t\tTokenURL: \"https://oauth.vk.com/access_token\",\n\t\t},\n\t\tScopes:      g.config.Scope,\n\t\tRedirectURL: g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (g *ProviderVK) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{}\n}\n\nfunc (g *ProviderVK) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn g.oauth2(ctx), nil\n}\n\nfunc (g *ProviderVK) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\to, err := g.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), o, exchange)\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", \"https://api.vk.com/method/users.get?fields=photo_200,nickname,bdate,sex&access_token=\"+exchange.AccessToken+\"&v=5.103\", nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(g.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\ttype User struct {\n\t\tID        int    `json:\"id,omitempty\"`\n\t\tFirstName string `json:\"first_name,omitempty\"`\n\t\tLastName  string `json:\"last_name,omitempty\"`\n\t\tNickname  string `json:\"nickname,omitempty\"`\n\t\tPicture   string `json:\"photo_200,omitempty\"`\n\t\tEmail     string `json:\"-\"`\n\t\tGender    int    `json:\"sex,omitempty\"`\n\t\tBirthDay  string `json:\"bdate,omitempty\"`\n\t}\n\n\tvar response struct {\n\t\tResult []User `json:\"response,omitempty\"`\n\t}\n\n\tif err := json.NewDecoder(resp.Body).Decode(&response); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tif len(response.Result) == 0 {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"VK did not return a user in the userinfo request.\"))\n\t}\n\n\tuser := response.Result[0]\n\n\tif email, ok := exchange.Extra(\"email\").(string); ok {\n\t\tuser.Email = email\n\t}\n\n\tgender := \"\"\n\tswitch user.Gender {\n\tcase 1:\n\t\tgender = \"female\"\n\tcase 2:\n\t\tgender = \"male\"\n\t}\n\n\treturn &Claims{\n\t\tIssuer:     \"https://api.vk.com/method/users.get\",\n\t\tSubject:    strconv.Itoa(user.ID),\n\t\tGivenName:  user.FirstName,\n\t\tFamilyName: user.LastName,\n\t\tNickname:   user.Nickname,\n\t\tPicture:    user.Picture,\n\t\tEmail:      user.Email,\n\t\tGender:     gender,\n\t\tBirthdate:  user.BirthDay,\n\t}, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_x.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/dghubble/oauth1\"\n\t\"github.com/dghubble/oauth1/twitter\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar _ OAuth1Provider = (*ProviderX)(nil)\n\nconst (\n\txUserInfoBase      = \"https://api.twitter.com/1.1/account/verify_credentials.json\"\n\txUserInfoWithEmail = xUserInfoBase + \"?include_email=true\"\n)\n\ntype ProviderX struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc (p *ProviderX) Config() *Configuration {\n\treturn p.config\n}\n\nfunc NewProviderX(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderX{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (p *ProviderX) ExchangeToken(ctx context.Context, req *http.Request) (*oauth1.Token, error) {\n\trequestToken, verifier, err := oauth1.ParseAuthorizationCallback(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\taccessToken, accessSecret, err := p.OAuth1(ctx).AccessToken(requestToken, \"\", verifier)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn oauth1.NewToken(accessToken, accessSecret), nil\n}\n\nfunc (p *ProviderX) AuthURL(ctx context.Context, state string) (_ string, err error) {\n\tctx, span := p.reg.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.ProviderLinkedIn.fetch\")\n\tdefer otelx.End(span, &err)\n\n\tc := p.OAuth1(ctx)\n\n\t// We need to cheat so that callback validates on return\n\tc.CallbackURL = c.CallbackURL + fmt.Sprintf(\"?state=%s&code=unused\", state)\n\n\trequestToken, _, err := c.RequestToken()\n\tif err != nil {\n\t\tspan.RecordError(err)\n\t\treturn \"\", errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(`Unable to sign in with X because the OAuth1 request token could not be initialized: %s`, err))\n\t}\n\n\tauthzURL, err := c.AuthorizationURL(requestToken)\n\tif err != nil {\n\t\tspan.RecordError(err)\n\t\treturn \"\", errors.WithStack(herodot.ErrMisconfiguration.WithWrap(err).WithReasonf(`Unable to sign in with X because the OAuth1 authorization URL could not be parsed: %s`, err))\n\t}\n\n\treturn authzURL.String(), nil\n}\n\nfunc (p *ProviderX) CheckError(ctx context.Context, r *http.Request) error {\n\tif r.URL.Query().Get(\"denied\") == \"\" {\n\t\treturn nil\n\t}\n\n\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to sign in with X because the user denied the request.`))\n}\n\nfunc (p *ProviderX) OAuth1(ctx context.Context) *oauth1.Config {\n\treturn &oauth1.Config{\n\t\tConsumerKey:    p.config.ClientID,\n\t\tConsumerSecret: p.config.ClientSecret,\n\t\tEndpoint:       twitter.AuthenticateEndpoint,\n\t\tCallbackURL:    p.config.Redir(p.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (p *ProviderX) userInfoEndpoint() string {\n\tfor _, scope := range p.config.Scope {\n\t\tif scope == \"email\" {\n\t\t\treturn xUserInfoWithEmail\n\t\t}\n\t}\n\n\treturn xUserInfoBase\n}\n\nfunc (p *ProviderX) Claims(ctx context.Context, token *oauth1.Token) (*Claims, error) {\n\tctx = context.WithValue(ctx, oauth1.HTTPClient, p.reg.HTTPClient(ctx).HTTPClient)\n\n\tc := p.OAuth1(ctx)\n\tclient := c.Client(ctx, token)\n\tendpoint := p.userInfoEndpoint()\n\n\tresp, err := client.Get(endpoint)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(p.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\tuser := &xUser{}\n\tif err := json.NewDecoder(resp.Body).Decode(user); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\twebsite := \"\"\n\tif user.URL != nil {\n\t\twebsite = *user.URL\n\t}\n\n\treturn &Claims{\n\t\tIssuer:            endpoint,\n\t\tSubject:           user.IDStr,\n\t\tName:              user.Name,\n\t\tPicture:           user.ProfileImageURLHTTPS,\n\t\tEmail:             user.Email,\n\t\tPreferredUsername: user.ScreenName,\n\t\tWebsite:           website,\n\t}, nil\n}\n\ntype xUser struct {\n\tID                     int      `json:\"id\"`\n\tIDStr                  string   `json:\"id_str\"`\n\tName                   string   `json:\"name\"`\n\tScreenName             string   `json:\"screen_name\"`\n\tLocation               string   `json:\"location\"`\n\tDescription            string   `json:\"description\"`\n\tURL                    *string  `json:\"url,omitempty\"`\n\tProtected              bool     `json:\"protected\"`\n\tFollowersCount         int      `json:\"followers_count\"`\n\tFriendsCount           int      `json:\"friends_count\"`\n\tListedCount            int      `json:\"listed_count\"`\n\tCreatedAt              string   `json:\"created_at\"`\n\tFavouritesCount        int      `json:\"favourites_count\"`\n\tVerified               bool     `json:\"verified\"`\n\tStatusesCount          int      `json:\"statuses_count\"`\n\tDefaultProfile         bool     `json:\"default_profile\"`\n\tDefaultProfileImage    bool     `json:\"default_profile_image\"`\n\tProfileImageURLHTTPS   string   `json:\"profile_image_url_https\"`\n\tWithheldInCountries    []string `json:\"withheld_in_countries\"`\n\tSuspended              bool     `json:\"suspended\"`\n\tNeedsPhoneVerification bool     `json:\"needs_phone_verification\"`\n\tEmail                  string   `json:\"email\"`\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/provider_yandex.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/x/httpx\"\n\n\t\"github.com/ory/herodot\"\n)\n\nvar _ OAuth2Provider = (*ProviderYandex)(nil)\n\ntype ProviderYandex struct {\n\tconfig *Configuration\n\treg    Dependencies\n}\n\nfunc NewProviderYandex(\n\tconfig *Configuration,\n\treg Dependencies,\n) Provider {\n\treturn &ProviderYandex{\n\t\tconfig: config,\n\t\treg:    reg,\n\t}\n}\n\nfunc (g *ProviderYandex) Config() *Configuration {\n\treturn g.config\n}\n\nfunc (g *ProviderYandex) oauth2(ctx context.Context) *oauth2.Config {\n\treturn &oauth2.Config{\n\t\tClientID:     g.config.ClientID,\n\t\tClientSecret: g.config.ClientSecret,\n\t\tEndpoint: oauth2.Endpoint{\n\t\t\tAuthURL:  \"https://oauth.yandex.com/authorize\",\n\t\t\tTokenURL: \"https://oauth.yandex.com/token\",\n\t\t},\n\t\tScopes:      g.config.Scope,\n\t\tRedirectURL: g.config.Redir(g.reg.Config().OIDCRedirectURIBase(ctx)),\n\t}\n}\n\nfunc (g *ProviderYandex) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {\n\treturn []oauth2.AuthCodeOption{}\n}\n\nfunc (g *ProviderYandex) OAuth2(ctx context.Context) (*oauth2.Config, error) {\n\treturn g.oauth2(ctx), nil\n}\n\nfunc (g *ProviderYandex) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {\n\to, err := g.OAuth2(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tctx, client := httpx.SetOAuth2(ctx, g.reg.HTTPClient(ctx), o, exchange)\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", \"https://login.yandex.ru/info?format=json&oauth_token=\"+exchange.AccessToken, nil)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif err := logUpstreamError(g.reg.Logger(), resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar user struct {\n\t\tID           string `json:\"id,omitempty\"`\n\t\tFirstName    string `json:\"first_name,omitempty\"`\n\t\tLastName     string `json:\"last_name,omitempty\"`\n\t\tEmail        string `json:\"default_email,omitempty\"`\n\t\tPicture      string `json:\"default_avatar_id,omitempty\"`\n\t\tPictureEmpty bool   `json:\"is_avatar_empty,omitempty\"`\n\t\tGender       string `json:\"sex,omitempty\"`\n\t\tBirthDay     string `json:\"birthday,omitempty\"`\n\t}\n\n\tif err := json.NewDecoder(resp.Body).Decode(&user); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithWrap(err).WithReasonf(\"%s\", err))\n\t}\n\n\tif !user.PictureEmpty {\n\t\tuser.Picture = \"https://avatars.yandex.net/get-yapic/\" + user.Picture + \"/islands-200\"\n\t} else {\n\t\tuser.Picture = \"\"\n\t}\n\n\treturn &Claims{\n\t\tIssuer:     \"https://login.yandex.ru/info\",\n\t\tSubject:    user.ID,\n\t\tGivenName:  user.FirstName,\n\t\tFamilyName: user.LastName,\n\t\tPicture:    user.Picture,\n\t\tEmail:      user.Email,\n\t\tGender:     user.Gender,\n\t\tBirthdate:  user.BirthDay,\n\t}, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed .schema/link.schema.json\nvar linkSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/oidc/state.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"crypto/sha512\"\n\t\"crypto/subtle\"\n\n\t\"golang.org/x/oauth2\"\n\t\"google.golang.org/protobuf/proto\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/cipher\"\n\toidcv1 \"github.com/ory/kratos/gen/oidc/v1\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc encryptState(ctx context.Context, c cipher.Cipher, state *oidcv1.State) (ciphertext string, err error) {\n\tm, err := proto.Marshal(state)\n\tif err != nil {\n\t\treturn \"\", herodot.ErrInternalServerError.WithReasonf(\"Unable to marshal state: %s\", err)\n\t}\n\treturn c.Encrypt(ctx, m)\n}\n\nfunc DecryptState(ctx context.Context, c cipher.Cipher, ciphertext string) (*oidcv1.State, error) {\n\tplaintext, err := c.Decrypt(ctx, ciphertext)\n\tif err != nil {\n\t\treturn nil, herodot.ErrBadRequest.WithReasonf(\"Unable to decrypt state: %s\", err)\n\t}\n\tvar state oidcv1.State\n\tif err := proto.Unmarshal(plaintext, &state); err != nil {\n\t\treturn nil, herodot.ErrBadRequest.WithReasonf(\"Unable to unmarshal state: %s\", err)\n\t}\n\treturn &state, nil\n}\n\nfunc (s *Strategy) GenerateState(ctx context.Context, p Provider, flow flow.Flow) (stateParam string, pkce []oauth2.AuthCodeOption, err error) {\n\tstate := oidcv1.State{\n\t\tFlowId:                         flow.GetID().Bytes(),\n\t\tSessionTokenExchangeCodeSha512: x.NewUUID().Bytes(),\n\t\tProviderId:                     p.Config().ID,\n\t\tPkceVerifier:                   maybePKCE(ctx, s.d, p),\n\t}\n\n\tswitch flow.(type) {\n\tcase *login.Flow:\n\t\tstate.FlowKind = oidcv1.FlowKind_FLOW_KIND_LOGIN\n\tcase *registration.Flow:\n\t\tstate.FlowKind = oidcv1.FlowKind_FLOW_KIND_REGISTRATION\n\tcase *settings.Flow:\n\t\tstate.FlowKind = oidcv1.FlowKind_FLOW_KIND_SETTINGS\n\tdefault:\n\t\tstate.FlowKind = oidcv1.FlowKind_FLOW_KIND_UNSPECIFIED\n\t}\n\n\tif code, hasCode, _ := s.d.SessionTokenExchangePersister().CodeForFlow(ctx, flow.GetID()); hasCode {\n\t\tsum := sha512.Sum512([]byte(code.InitCode))\n\t\tstate.SessionTokenExchangeCodeSha512 = sum[:]\n\t}\n\n\tparam, err := encryptState(ctx, s.d.Cipher(ctx), &state)\n\tif err != nil {\n\t\treturn \"\", nil, herodot.ErrInternalServerError.WithReason(\"Unable to encrypt state\").WithWrap(err)\n\t}\n\treturn param, PKCEChallenge(&state), nil\n}\n\nfunc codeMatches(s *oidcv1.State, code string) bool {\n\tsum := sha512.Sum512([]byte(code))\n\treturn subtle.ConstantTimeCompare(s.GetSessionTokenExchangeCodeSha512(), sum[:]) == 1\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/state_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/cipher\"\n\toidcv1 \"github.com/ory/kratos/gen/oidc/v1\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestGenerateState(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\t_ = conf\n\tstrat := oidc.NewStrategy(reg)\n\tctx := context.Background()\n\tciph := reg.Cipher(ctx)\n\t_, ok := ciph.(*cipher.Noop)\n\trequire.False(t, ok)\n\n\tflow := &registration.Flow{\n\t\tID: x.NewUUID(),\n\t}\n\n\tstateParam, pkce, err := strat.GenerateState(ctx, &testProvider{}, flow)\n\trequire.NoError(t, err)\n\trequire.NotEmpty(t, stateParam)\n\tassert.Empty(t, pkce)\n\n\tstate, err := oidc.DecryptState(ctx, ciph, stateParam)\n\trequire.NoError(t, err)\n\tassert.Equal(t, flow.GetID().Bytes(), state.FlowId)\n\tassert.Empty(t, oidc.PKCEVerifier(state))\n\tassert.Equal(t, \"test-provider\", state.ProviderId)\n\tassert.Equal(t, oidcv1.FlowKind_FLOW_KIND_REGISTRATION, state.FlowKind)\n}\n\ntype testProvider struct{}\n\nfunc (t *testProvider) Config() *oidc.Configuration {\n\treturn &oidc.Configuration{ID: \"test-provider\", PKCE: \"never\"}\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"bytes\"\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"maps\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/cipher\"\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/driver/config\"\n\toidcv1 \"github.com/ory/kratos/gen/oidc/v1\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/selfservice/strategy\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/reqlog\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nconst (\n\tRouteBase = \"/self-service/methods/oidc\"\n\n\tRouteAuth                 = RouteBase + \"/auth/{flow}\"\n\tRouteCallback             = RouteBase + \"/callback/{provider}\"\n\tRouteCallbackGeneric      = RouteBase + \"/callback\"\n\tRouteOrganizationCallback = RouteBase + \"/organization/{organization}/callback/{provider}\"\n)\n\nvar (\n\t_ identity.ActiveCredentialsCounter = (*Strategy)(nil)\n\t_ x.Handler                         = (*Strategy)(nil)\n)\n\ntype Dependencies interface {\n\terrorx.ManagementProvider\n\n\tconfig.Provider\n\n\tlogrusx.Provider\n\tx.CookieProvider\n\tnosurfx.CSRFProvider\n\tnosurfx.CSRFTokenGeneratorProvider\n\thttpx.WriterProvider\n\thttpx.ClientProvider\n\totelx.Provider\n\n\tidentity.ValidationProvider\n\tidentity.PrivilegedPoolProvider\n\tidentity.ActiveCredentialsCounterStrategyProvider\n\tidentity.ManagementProvider\n\n\tsession.ManagementProvider\n\tsession.HandlerProvider\n\tsessiontokenexchange.PersistenceProvider\n\n\tlogin.HookExecutorProvider\n\tlogin.FlowPersistenceProvider\n\tlogin.HooksProvider\n\tlogin.StrategyProvider\n\tlogin.HandlerProvider\n\tlogin.ErrorHandlerProvider\n\n\tregistration.HookExecutorProvider\n\tregistration.FlowPersistenceProvider\n\tregistration.HooksProvider\n\tregistration.StrategyProvider\n\tregistration.HandlerProvider\n\tregistration.ErrorHandlerProvider\n\n\tsettings.ErrorHandlerProvider\n\tsettings.FlowPersistenceProvider\n\tsettings.HookExecutorProvider\n\n\tcontinuity.ManagementProvider\n\n\tcipher.Provider\n\n\tjsonnetsecure.VMProvider\n}\n\nfunc isForced(req interface{}) bool {\n\tf, ok := req.(interface {\n\t\tIsRefresh() bool\n\t})\n\treturn ok && f.IsRefresh()\n}\n\n// ConflictingIdentityVerdict encodes the decision on what to do on a oconflict\n// between an existing and a new identity.\ntype ConflictingIdentityVerdict int\n\nconst (\n\t// ConflictingIdentityVerdictUnknown is the default value and should not be used.\n\tConflictingIdentityVerdictUnknown ConflictingIdentityVerdict = iota\n\n\t// ConflictingIdentityVerdictReject rejects the new identity. The flow will\n\t// continue with an explicit account linking step, where the user will need to\n\t// confirm an existing credential on the identity.\n\tConflictingIdentityVerdictReject\n\n\t// ConflictingIdentityVerdictMerge merges the new identity into the existing.\n\tConflictingIdentityVerdictMerge\n)\n\n// Strategy implements selfservice.LoginStrategy, selfservice.RegistrationStrategy and selfservice.SettingsStrategy.\n// It supports login, registration and settings via OpenID Providers.\ntype Strategy struct {\n\td                           Dependencies\n\tvalidator                   *schema.Validator\n\tcredType                    identity.CredentialsType\n\thandleUnknownProviderError  func(err error) error\n\thandleMethodNotAllowedError func(err error) error\n\n\tconflictingIdentityPolicy ConflictingIdentityPolicy\n}\ntype ConflictingIdentityPolicy func(ctx context.Context, existingIdentity, newIdentity *identity.Identity, provider Provider, claims *Claims) ConflictingIdentityVerdict\n\ntype AuthCodeContainer struct {\n\tFlowID           string              `json:\"flow_id\"`\n\tState            string              `json:\"state\"`\n\tIdentitySchema   flow.IdentitySchema `json:\"identity_schema_id,omitempty\"`\n\tTraits           json.RawMessage     `json:\"traits\"`\n\tTransientPayload json.RawMessage     `json:\"transient_payload\"`\n}\n\nfunc (s *Strategy) CountActiveFirstFactorCredentials(ctx context.Context, cc map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\treturn CountActiveFirstFactorCredentials(ctx, s.ID(), cc, false)\n}\n\nfunc CountActiveFirstFactorCredentials(_ context.Context, id identity.CredentialsType, cc map[identity.CredentialsType]identity.Credentials, withOrgs bool) (count int, err error) {\n\tfor _, c := range cc {\n\t\tif c.Type == id && gjson.ValidBytes(c.Config) {\n\t\t\tvar conf identity.CredentialsOIDC\n\t\t\tif err = json.Unmarshal(c.Config, &conf); err != nil {\n\t\t\t\treturn 0, errors.WithStack(err)\n\t\t\t}\n\n\t\t\tfor _, identifier := range c.Identifiers {\n\t\t\t\tprovider, sub, ok := strings.Cut(identifier, \":\")\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tfor _, prov := range conf.Providers {\n\t\t\t\t\tif withOrgs && len(prov.Organization) == 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t} else if !withOrgs && len(prov.Organization) > 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tif provider == prov.Provider && sub == prov.Subject && prov.Subject != \"\" && prov.Provider != \"\" {\n\t\t\t\t\t\tcount++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (s *Strategy) CountActiveMultiFactorCredentials(_ context.Context, _ map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\treturn 0, nil\n}\n\nfunc (s *Strategy) RegisterPublicRoutes(r *httprouterx.RouterPublic) {\n\twrappedHandleCallback := strategy.IsDisabled(s.d, s.ID().String(), s.HandleCallback)\n\tr.GET(RouteCallback, wrappedHandleCallback)\n\tr.GET(RouteCallbackGeneric, wrappedHandleCallback)\n\n\t// Apple can use the POST request method when calling the callback\n\t// Apple is the only (known) provider that sometimes does a form POST to the callback URL.\n\t// This is a workaround to handle this case.\n\t// But since the URL contains the `id` of the provider, we just allow all OIDC provider callbacks to bypass CSRF.\n\t// This is fine, because all other providers seem to use GET, which is CSRF safe.\n\ts.d.CSRFHandler().IgnoreGlob(RouteBase + \"/callback/*\")\n\n\t// When handler is called using POST method, the cookies are not attached to the request\n\t// by the browser. So here we just redirect the request to the same location rewriting the\n\t// form fields to query params. This second GET request should have the cookies attached.\n\tr.POST(RouteCallback, s.redirectToGET)\n}\n\nfunc (s *Strategy) RegisterAdminRoutes(*httprouterx.RouterAdmin) {}\n\n// Redirect POST request to GET rewriting form fields to query params.\nfunc (s *Strategy) redirectToGET(w http.ResponseWriter, r *http.Request) {\n\tpublicURL := s.d.Config().SelfPublicURL(r.Context())\n\tdest := *r.URL\n\tdest.Host = publicURL.Host\n\tdest.Scheme = publicURL.Scheme\n\tif err := r.ParseForm(); err == nil {\n\t\tq := dest.Query()\n\t\tfor key, values := range r.Form {\n\t\t\tfor _, value := range values {\n\t\t\t\tq.Set(key, value)\n\t\t\t}\n\t\t}\n\t\tdest.RawQuery = q.Encode()\n\t}\n\tdest.Path = filepath.Join(publicURL.Path, dest.Path)\n\n\thttp.Redirect(w, r, dest.String(), http.StatusFound)\n}\n\ntype NewStrategyOpt func(s *Strategy)\n\n// ForCredentialType overrides the credentials type for this strategy.\nfunc ForCredentialType(ct identity.CredentialsType) NewStrategyOpt {\n\treturn func(s *Strategy) { s.credType = ct }\n}\n\n// WithUnknownProviderHandler overrides the error returned when the provider\n// cannot be found.\nfunc WithUnknownProviderHandler(handler func(error) error) NewStrategyOpt {\n\treturn func(s *Strategy) { s.handleUnknownProviderError = handler }\n}\n\n// WithHandleMethodNotAllowedError overrides the error returned when method is\n// not allowed.\nfunc WithHandleMethodNotAllowedError(handler func(error) error) NewStrategyOpt {\n\treturn func(s *Strategy) { s.handleMethodNotAllowedError = handler }\n}\n\n// WithOnConflictingIdentity sets a policy handler for deciding what to do when a\n// new identity conflicts with an existing one during login.\nfunc WithOnConflictingIdentity(handler ConflictingIdentityPolicy) NewStrategyOpt {\n\treturn func(s *Strategy) { s.conflictingIdentityPolicy = handler }\n}\n\n// SetOnConflictingIdentity sets a policy handler for deciding what to do when a\n// new identity conflicts with an existing one during login. This should only be\n// called in tests.\nfunc (s *Strategy) SetOnConflictingIdentity(t testing.TB, handler ConflictingIdentityPolicy) {\n\tif t == nil {\n\t\tpanic(\"this should only be called in tests\")\n\t}\n\ts.conflictingIdentityPolicy = handler\n}\n\nfunc NewStrategy(d Dependencies, opts ...NewStrategyOpt) *Strategy {\n\ts := &Strategy{\n\t\td:                           d,\n\t\tvalidator:                   schema.NewValidator(),\n\t\tcredType:                    identity.CredentialsTypeOIDC,\n\t\thandleUnknownProviderError:  func(err error) error { return err },\n\t\thandleMethodNotAllowedError: func(err error) error { return err },\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(s)\n\t}\n\n\treturn s\n}\n\nfunc (s *Strategy) ID() identity.CredentialsType {\n\treturn s.credType\n}\n\nfunc (s *Strategy) validateFlow(ctx context.Context, r *http.Request, rid uuid.UUID, kind oidcv1.FlowKind) (f flow.Flow, err error) {\n\tif rid.IsNil() {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReason(\"The session cookie contains invalid values and the flow could not be executed. Please try again.\"))\n\t}\n\n\tswitch kind {\n\n\tcase oidcv1.FlowKind_FLOW_KIND_LOGIN:\n\t\tlf, err := s.d.LoginFlowPersister().GetLoginFlow(ctx, rid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn lf, lf.Valid()\n\n\tcase oidcv1.FlowKind_FLOW_KIND_REGISTRATION:\n\t\trf, err := s.d.RegistrationFlowPersister().GetRegistrationFlow(ctx, rid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn rf, rf.Valid()\n\n\tcase oidcv1.FlowKind_FLOW_KIND_SETTINGS:\n\t\tsf, err := s.d.SettingsFlowPersister().GetSettingsFlow(ctx, rid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsess, err := s.d.SessionManager().FetchFromRequest(ctx, r)\n\t\tif err != nil {\n\t\t\treturn sf, err\n\t\t}\n\t\treturn sf, sf.Valid(sess)\n\n\t}\n\n\t// fallback to the old behavior for backwards compatibility\n\tfor _, kind := range []oidcv1.FlowKind{oidcv1.FlowKind_FLOW_KIND_LOGIN, oidcv1.FlowKind_FLOW_KIND_REGISTRATION, oidcv1.FlowKind_FLOW_KIND_SETTINGS} {\n\t\tif f, err = s.validateFlow(ctx, r, rid, kind); f != nil {\n\t\t\treturn f, err\n\t\t}\n\t}\n\treturn f, err\n}\n\nfunc (s *Strategy) ValidateCallback(w http.ResponseWriter, r *http.Request) (flow.Flow, *oidcv1.State, *AuthCodeContainer, error) {\n\tvar (\n\t\tcodeParam  = cmp.Or(r.URL.Query().Get(\"code\"), r.URL.Query().Get(\"authCode\"))\n\t\tstateParam = r.URL.Query().Get(\"state\")\n\t\terrorParam = r.URL.Query().Get(\"error\")\n\t)\n\n\tif stateParam == \"\" {\n\t\treturn nil, nil, nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow because the OpenID Provider did not return the state query parameter.`))\n\t}\n\tstate, err := DecryptState(r.Context(), s.d.Cipher(r.Context()), stateParam)\n\tif err != nil {\n\t\treturn nil, nil, nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow because the state parameter is invalid.`))\n\t}\n\n\tif providerFromURL := r.PathValue(\"provider\"); providerFromURL != \"\" {\n\t\t// We're serving an OIDC callback URL with provider in the URL.\n\t\tif state.ProviderId == \"\" {\n\t\t\t// provider in URL, but not in state: compatiblity mode, remove this fallback later\n\t\t\tstate.ProviderId = providerFromURL\n\t\t} else if state.ProviderId != providerFromURL {\n\t\t\t// provider in state, but URL with different provider -> something's fishy\n\t\t\treturn nil, nil, nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow: provider mismatch between internal state and URL.`))\n\t\t}\n\t}\n\tif state.ProviderId == \"\" {\n\t\t// weird: provider neither in the state nor in the URL\n\t\treturn nil, nil, nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow: provider could not be retrieved from state nor URL.`))\n\t}\n\n\tf, err := s.validateFlow(r.Context(), r, uuid.FromBytesOrNil(state.FlowId), state.FlowKind)\n\tif err != nil {\n\t\treturn nil, state, nil, err\n\t}\n\n\ttokenCode, hasSessionTokenCode, err := s.d.SessionTokenExchangePersister().CodeForFlow(r.Context(), f.GetID())\n\tif err != nil {\n\t\treturn nil, state, nil, err\n\t}\n\n\tcntnr := AuthCodeContainer{}\n\tif f.GetType() == flow.TypeBrowser || !hasSessionTokenCode {\n\t\tif _, err := s.d.ContinuityManager().Continue(r.Context(), w, r, sessionName,\n\t\t\tcontinuity.WithPayload(&cntnr),\n\t\t\tcontinuity.WithExpireInsteadOfDelete(time.Minute),\n\t\t); err != nil {\n\t\t\treturn nil, state, nil, err\n\t\t}\n\t\tif stateParam != cntnr.State {\n\t\t\treturn nil, state, &cntnr, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow because the query state parameter does not match the state parameter from the session cookie.`))\n\t\t}\n\t} else {\n\t\t// We need to validate the tokenCode here\n\t\tif !codeMatches(state, tokenCode.InitCode) {\n\t\t\treturn nil, state, &cntnr, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow because the query state parameter does not match the state parameter from the code.`))\n\t\t}\n\t\tcntnr.State = stateParam\n\t\tcntnr.FlowID = uuid.FromBytesOrNil(state.FlowId).String()\n\t}\n\n\tif errorParam != \"\" {\n\t\treturn f, state, &cntnr, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow because the OpenID Provider returned error \"%s\": %s`, r.URL.Query().Get(\"error\"), r.URL.Query().Get(\"error_description\")))\n\t}\n\n\tif codeParam == \"\" {\n\t\treturn f, state, &cntnr, errors.WithStack(herodot.ErrBadRequest.WithReasonf(`Unable to complete OpenID Connect flow because the OpenID Provider did not return the code query parameter.`))\n\t}\n\n\treturn f, state, &cntnr, nil\n}\n\nfunc registrationOrLoginFlowID(flow any) (uuid.UUID, bool) {\n\tswitch f := flow.(type) {\n\tcase *registration.Flow:\n\t\treturn f.ID, true\n\tcase *login.Flow:\n\t\treturn f.ID, true\n\tdefault:\n\t\treturn uuid.Nil, false\n\t}\n}\n\nfunc (s *Strategy) alreadyAuthenticated(ctx context.Context, w http.ResponseWriter, r *http.Request, f interface{}) (bool, error) {\n\tif sess, _ := s.d.SessionManager().FetchFromRequest(ctx, r); sess != nil {\n\t\tif _, ok := f.(*settings.Flow); ok {\n\t\t\t// ignore this if it's a settings flow\n\t\t} else if !isForced(f) {\n\t\t\treturnTo := s.d.Config().SelfServiceBrowserDefaultReturnTo(ctx)\n\t\t\tif redirecter, ok := f.(flow.FlowWithRedirect); ok {\n\t\t\t\tr, err := redir.SecureRedirectTo(r, returnTo, redirecter.SecureRedirectToOpts(ctx, s.d)...)\n\t\t\t\tif err == nil {\n\t\t\t\t\treturnTo = r\n\t\t\t\t}\n\t\t\t}\n\t\t\tif flowID, ok := registrationOrLoginFlowID(f); ok {\n\t\t\t\tif codes, hasCode, _ := s.d.SessionTokenExchangePersister().CodeForFlow(ctx, flowID); hasCode {\n\t\t\t\t\tif err := s.d.SessionTokenExchangePersister().UpdateSessionOnExchanger(ctx, flowID, sess.ID); err != nil {\n\t\t\t\t\t\treturn false, err\n\t\t\t\t\t}\n\t\t\t\t\tq := returnTo.Query()\n\t\t\t\t\tq.Set(\"code\", codes.ReturnToCode)\n\t\t\t\t\treturnTo.RawQuery = q.Encode()\n\t\t\t\t}\n\t\t\t}\n\t\t\thttp.Redirect(w, r, returnTo.String(), http.StatusSeeOther)\n\t\t\treturn true, nil\n\t\t}\n\t}\n\n\treturn false, nil\n}\n\nfunc (s *Strategy) HandleCallback(w http.ResponseWriter, r *http.Request) {\n\tvar (\n\t\tcode = cmp.Or(r.URL.Query().Get(\"code\"), r.URL.Query().Get(\"authCode\"))\n\t\terr  error\n\t)\n\n\tctx := r.Context()\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"strategy.oidc.HandleCallback\")\n\tdefer otelx.End(span, &err)\n\tr = r.WithContext(ctx)\n\n\treq, state, cntnr, err := s.ValidateCallback(w, r)\n\tif err != nil {\n\t\tif req != nil {\n\t\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, err))\n\t\t} else {\n\t\t\ts.d.SelfServiceErrorManager().Forward(ctx, w, r, s.HandleError(ctx, w, r, nil, \"\", nil, err))\n\t\t}\n\t\treturn\n\t}\n\n\tif authenticated, err := s.alreadyAuthenticated(ctx, w, r, req); err != nil {\n\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, err))\n\t} else if authenticated {\n\t\treturn\n\t}\n\n\tprovider, err := s.Provider(ctx, state.ProviderId)\n\tif err != nil {\n\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, err))\n\t\treturn\n\t}\n\n\tvar claims *Claims\n\tvar et *identity.CredentialsOIDCEncryptedTokens\n\tswitch p := provider.(type) {\n\tcase OAuth2Provider:\n\t\tt0 := time.Now()\n\t\ttoken, err := s.exchangeCode(ctx, p, code, PKCEVerifier(state))\n\t\treqlog.AccumulateExternalLatency(ctx, time.Since(t0))\n\t\tif err != nil {\n\t\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, err))\n\t\t\treturn\n\t\t}\n\n\t\tet, err = s.encryptOAuth2Tokens(ctx, token)\n\t\tif err != nil {\n\t\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, err))\n\t\t\treturn\n\t\t}\n\n\t\tt0 = time.Now()\n\t\tclaims, err = p.Claims(ctx, token, r.URL.Query())\n\t\treqlog.AccumulateExternalLatency(ctx, time.Since(t0))\n\t\tif err != nil {\n\t\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, err))\n\t\t\treturn\n\t\t}\n\tcase OAuth1Provider:\n\t\tt0 := time.Now()\n\t\ttoken, err := p.ExchangeToken(ctx, r)\n\t\treqlog.AccumulateExternalLatency(ctx, time.Since(t0))\n\t\tif err != nil {\n\t\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, err))\n\t\t\treturn\n\t\t}\n\n\t\tt0 = time.Now()\n\t\tclaims, err = p.Claims(ctx, token)\n\t\treqlog.AccumulateExternalLatency(ctx, time.Since(t0))\n\t\tif err != nil {\n\t\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, err))\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err = claims.Validate(); err != nil {\n\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, err))\n\t\treturn\n\t}\n\n\tspan.SetAttributes(attribute.StringSlice(\"claims\", slices.Collect(maps.Keys(claims.RawClaims))))\n\n\tswitch a := req.(type) {\n\tcase *login.Flow:\n\t\ta.Active = s.ID()\n\t\ta.TransientPayload = cntnr.TransientPayload\n\t\t// For API/native flows, the continuity cookie is not available so\n\t\t// cntnr.TransientPayload is empty. Fall back to the value persisted\n\t\t// in InternalContext during Login().\n\t\tif len(a.TransientPayload) == 0 {\n\t\t\tif tp := gjson.GetBytes(a.GetInternalContext(), \"transient_payload\"); tp.Exists() {\n\t\t\t\ta.TransientPayload = json.RawMessage(tp.Raw)\n\t\t\t}\n\t\t}\n\t\ta.IdentitySchema = cntnr.IdentitySchema\n\t\tif ff, err := s.ProcessLogin(ctx, w, r, a, et, claims, provider, cntnr); err != nil {\n\t\t\tif errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif ff != nil {\n\t\t\t\ts.forwardError(ctx, w, r, ff, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\ts.forwardError(ctx, w, r, a, err)\n\t\t}\n\t\treturn\n\tcase *registration.Flow:\n\t\ta.Active = s.ID()\n\t\ta.TransientPayload = cntnr.TransientPayload\n\t\t// For API/native flows, the continuity cookie is not available so\n\t\t// cntnr.TransientPayload is empty. Fall back to the value persisted\n\t\t// in InternalContext during Register().\n\t\tif len(a.TransientPayload) == 0 {\n\t\t\tif tp := gjson.GetBytes(a.GetInternalContext(), \"transient_payload\"); tp.Exists() {\n\t\t\t\ta.TransientPayload = json.RawMessage(tp.Raw)\n\t\t\t}\n\t\t}\n\t\ta.IdentitySchema = cntnr.IdentitySchema\n\t\tif ff, err := s.processRegistration(ctx, w, r, a, et, claims, provider, cntnr); errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\treturn\n\t\t} else if err != nil {\n\t\t\tif ff != nil {\n\t\t\t\ts.forwardError(ctx, w, r, ff, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\ts.forwardError(ctx, w, r, a, err)\n\t\t}\n\t\treturn\n\tcase *settings.Flow:\n\t\ta.Active = sqlxx.NullString(s.ID())\n\t\ta.TransientPayload = cntnr.TransientPayload\n\t\tsess, err := s.d.SessionManager().FetchFromRequest(ctx, r)\n\t\tif err != nil {\n\t\t\ts.forwardError(ctx, w, r, a, s.HandleError(ctx, w, r, a, state.ProviderId, nil, err))\n\t\t\treturn\n\t\t}\n\t\tif err := s.linkProvider(ctx, w, r, &settings.UpdateContext{Session: sess, Flow: a}, et, claims, provider); err != nil {\n\t\t\ts.forwardError(ctx, w, r, a, s.HandleError(ctx, w, r, a, state.ProviderId, nil, err))\n\t\t\treturn\n\t\t}\n\t\treturn\n\tdefault:\n\t\ts.forwardError(ctx, w, r, req, s.HandleError(ctx, w, r, req, state.ProviderId, nil, errors.WithStack(x.PseudoPanic.\n\t\t\tWithDetailf(\"cause\", \"Unexpected type in OpenID Connect flow: %T\", a))))\n\t\treturn\n\t}\n}\n\nfunc (s *Strategy) exchangeCode(ctx context.Context, provider OAuth2Provider, code string, opts []oauth2.AuthCodeOption) (token *oauth2.Token, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"strategy.oidc.exchangeCode\", trace.WithAttributes(\n\t\tattribute.String(\"provider_id\", provider.Config().ID),\n\t\tattribute.String(\"provider_label\", provider.Config().Label)))\n\tdefer otelx.End(span, &err)\n\n\tte, ok := provider.(OAuth2TokenExchanger)\n\tif !ok {\n\t\tte, err = provider.OAuth2(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tclient := s.d.HTTPClient(ctx)\n\tctx = context.WithValue(ctx, oauth2.HTTPClient, client.HTTPClient)\n\treturn te.Exchange(ctx, code, opts...)\n}\n\nfunc (s *Strategy) populateMethod(r *http.Request, f flow.Flow, message func(provider string, providerId string) *text.Message) error {\n\tconf, err := s.Config(r.Context())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tf.GetUI().SetCSRF(s.d.GenerateCSRFToken(r))\n\tAddProviders(f.GetUI(), conf.Providers, message, s.ID())\n\n\treturn nil\n}\n\nfunc (s *Strategy) Config(ctx context.Context) (*ConfigurationCollection, error) {\n\tvar c ConfigurationCollection\n\n\tconf := s.d.Config().SelfServiceStrategy(ctx, string(s.ID())).Config\n\tif err := json.\n\t\tNewDecoder(bytes.NewBuffer(conf)).\n\t\tDecode(&c); err != nil {\n\t\ts.d.Logger().WithError(err).WithField(\"config\", conf)\n\t\treturn nil, errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"Unable to decode OpenID Connect Provider configuration: %s\", err))\n\t}\n\n\treturn &c, nil\n}\n\nfunc (s *Strategy) Provider(ctx context.Context, id string) (Provider, error) {\n\tif c, err := s.Config(ctx); err != nil {\n\t\treturn nil, err\n\t} else if provider, err := c.Provider(id, s.d); err != nil {\n\t\treturn nil, s.handleUnknownProviderError(err)\n\t} else {\n\t\treturn provider, nil\n\t}\n}\n\nfunc (s *Strategy) forwardError(ctx context.Context, w http.ResponseWriter, r *http.Request, f flow.Flow, err error) {\n\tswitch ff := f.(type) {\n\tcase *login.Flow:\n\t\ts.d.LoginFlowErrorHandler().WriteFlowError(w, r, ff, s.ID(), s.NodeGroup(), err)\n\tcase *registration.Flow:\n\t\ts.d.RegistrationFlowErrorHandler().WriteFlowError(w, r, ff, s.ID(), s.NodeGroup(), err)\n\tcase *settings.Flow:\n\t\tvar i *identity.Identity\n\t\tvar sess *session.Session\n\t\tif currentSession, err := s.d.SessionManager().FetchFromRequest(ctx, r); err == nil {\n\t\t\ti = currentSession.Identity\n\t\t\tsess = currentSession\n\t\t}\n\t\ts.d.SettingsFlowErrorHandler().WriteFlowError(ctx, w, r, s.NodeGroup(), ff, i, sess, err)\n\tdefault:\n\t\tpanic(errors.Errorf(\"unexpected type: %T\", ff))\n\t}\n}\n\nfunc (s *Strategy) HandleError(ctx context.Context, w http.ResponseWriter, r *http.Request, f flow.Flow, usedProviderID string, traits []byte, err error) error {\n\tswitch rf := f.(type) {\n\tcase *login.Flow:\n\t\treturn err\n\tcase *registration.Flow:\n\t\t// Reset all nodes to not confuse users.\n\t\t// This is kinda hacky and will probably need to be updated at some point.\n\n\t\tif dup := new(identity.ErrDuplicateCredentials); errors.As(err, &dup) {\n\t\t\terr = schema.NewDuplicateCredentialsError(dup)\n\n\t\t\tif validationErr := new(schema.ValidationError); errors.As(err, &validationErr) {\n\t\t\t\tfor _, m := range validationErr.Messages {\n\t\t\t\t\tm := m\n\t\t\t\t\trf.UI.Messages.Add(&m)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trf.UI.Messages.Add(text.NewErrorValidationDuplicateCredentialsOnOIDCLink())\n\t\t\t}\n\n\t\t\tlf, err := s.registrationToLogin(ctx, w, r, rf)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t// return a new login flow with the error message embedded in the login flow.\n\t\t\tvar redirectURL *url.URL\n\t\t\tif lf.Type == flow.TypeAPI {\n\t\t\t\treturnTo := s.d.Config().SelfServiceBrowserDefaultReturnTo(ctx)\n\t\t\t\tif redirecter, ok := f.(flow.FlowWithRedirect); ok {\n\t\t\t\t\tsecureReturnTo, err := redir.SecureRedirectTo(r, returnTo, redirecter.SecureRedirectToOpts(ctx, s.d)...)\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\treturnTo = secureReturnTo\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tredirectURL = lf.AppendTo(returnTo)\n\t\t\t} else {\n\t\t\t\tredirectURL = lf.AppendTo(s.d.Config().SelfServiceFlowLoginUI(ctx))\n\t\t\t}\n\t\t\tif dc, err := flow.DuplicateCredentials(lf); err == nil && dc != nil {\n\t\t\t\tredirectURL = urlx.CopyWithQuery(redirectURL, url.Values{\"no_org_ui\": {\"true\"}})\n\t\t\t\ts.populateAccountLinkingUI(ctx, lf, usedProviderID, dc.DuplicateIdentifier, dup.AvailableCredentials(), dup.AvailableOIDCProviders())\n\t\t\t\tif err := s.d.LoginFlowPersister().UpdateLoginFlow(ctx, lf); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tx.SendFlowErrorAsRedirectOrJSON(w, r, s.d.Writer(), lf, redirectURL.String())\n\t\t\t// ensure the function does not continue to execute\n\t\t\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n\t\t}\n\n\t\trf.UI.Nodes = node.Nodes{}\n\n\t\t// Adds the \"Continue\" button\n\t\trf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\tAddProvider(rf.UI, usedProviderID, text.NewInfoRegistrationContinue(), s.ID())\n\n\t\tgroup := node.DefaultGroup\n\t\tif s.d.Config().SelfServiceLegacyOIDCRegistrationGroup(ctx) {\n\t\t\tgroup = node.OpenIDConnectGroup\n\t\t\ttrace.SpanFromContext(r.Context()).AddEvent(semconv.NewDeprecatedFeatureUsedEvent(r.Context(), \"legacy_oidc_registration_group\"))\n\t\t}\n\n\t\tif traits != nil {\n\t\t\tds, err := rf.IdentitySchema.URL(ctx, s.d.Config())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\ttraitNodes, err := container.NodesFromJSONSchema(ctx, group, ds.String(), \"\", nil)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\trf.UI.Nodes = append(rf.UI.Nodes, traitNodes...)\n\t\t\trf.UI.UpdateNodeValuesFromJSON(traits, \"traits\", group)\n\t\t}\n\n\t\treturn err\n\tcase *settings.Flow:\n\t\treturn err\n\t}\n\n\treturn err\n}\n\nfunc (s *Strategy) populateAccountLinkingUI(ctx context.Context, lf *login.Flow, usedProviderID string, duplicateIdentifier string, availableCredentials []string, availableProviders []string) {\n\tnewLoginURL := s.d.Config().SelfServiceFlowLoginUI(ctx).String()\n\tusedProviderLabel := usedProviderID\n\tprovider, _ := s.Provider(ctx, usedProviderID)\n\tif provider != nil && provider.Config() != nil {\n\t\tusedProviderLabel = provider.Config().Label\n\t\tif usedProviderLabel == \"\" {\n\t\t\tusedProviderLabel = provider.Config().Provider\n\t\t}\n\t}\n\tloginHintsEnabled := s.d.Config().SelfServiceFlowRegistrationLoginHints(ctx)\n\tnodes := []*node.Node{}\n\tfor _, n := range lf.UI.Nodes {\n\t\t// We don't want to touch nodes unecessary nodes\n\t\tif n.Meta == nil || n.Meta.Label == nil || n.Group == node.DefaultGroup {\n\t\t\tnodes = append(nodes, n)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Skip the provider that was used to get here (in case they used an OIDC provider)\n\t\tpID := gjson.GetBytes(n.Meta.Label.Context, \"provider_id\").String()\n\t\tif n.Group == node.OpenIDConnectGroup {\n\t\t\tif pID == usedProviderID {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// Hide any provider that is not available for the user\n\t\t\tif loginHintsEnabled && !slices.Contains(availableProviders, pID) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// Replace some labels to make it easier for the user to understand what's going on.\n\t\tswitch n.Meta.Label.ID {\n\t\tcase text.InfoSelfServiceLogin:\n\t\t\tn.Meta.Label = text.NewInfoLoginAndLink()\n\t\tcase text.InfoSelfServiceLoginWith:\n\t\t\tp := gjson.GetBytes(n.Meta.Label.Context, \"provider\").String()\n\t\t\tn.Meta.Label = text.NewInfoLoginWithAndLink(p)\n\t\tdefault:\n\t\t\t// do nothing\n\t\t}\n\n\t\t// This can happen, if login hints are disabled. In that case, we need to make sure to show all credential options.\n\t\t// It could in theory also happen due to a mis-configuration, and in that case, we should make sure to not delete the entire flow.\n\t\tif !loginHintsEnabled {\n\t\t\tnodes = append(nodes, n)\n\t\t} else {\n\t\t\t// Hide nodes from credentials that are not relevant for the user\n\t\t\tfor _, ct := range availableCredentials {\n\t\t\t\tif ct == string(n.Group) {\n\t\t\t\t\tnodes = append(nodes, n)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Hide the \"primary\" identifier field present for Password, webauthn or passwordless, as we already know the identifier\n\tidentifierNode := lf.UI.Nodes.Find(\"identifier\")\n\tif identifierNode != nil {\n\t\tif attributes, ok := identifierNode.Attributes.(*node.InputAttributes); ok {\n\t\t\tattributes.Type = node.InputAttributeTypeHidden\n\t\t\tattributes.SetValue(duplicateIdentifier)\n\t\t\tidentifierNode.Attributes = attributes\n\t\t}\n\t}\n\n\tlf.UI.Nodes = nodes\n\tlf.UI.Messages.Clear()\n\tlf.UI.Messages.Add(text.NewInfoLoginLinkMessage(duplicateIdentifier, usedProviderLabel, newLoginURL, availableCredentials, availableProviders))\n}\n\nfunc (s *Strategy) NodeGroup() node.UiNodeGroup {\n\treturn node.OpenIDConnectGroup\n}\n\nfunc (s *Strategy) CompletedAuthenticationMethod(context.Context) session.AuthenticationMethod {\n\treturn session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    identity.AuthenticatorAssuranceLevel1,\n\t}\n}\n\nfunc (s *Strategy) ProcessIDToken(r *http.Request, provider Provider, idToken, idTokenNonce string) (*Claims, error) {\n\tverifier, ok := provider.(IDTokenVerifier)\n\tif !ok {\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"The provider %s does not support id_token verification\", provider.Config().Provider))\n\t}\n\tt0 := time.Now()\n\tclaims, err := verifier.Verify(r.Context(), idToken)\n\treqlog.AccumulateExternalLatency(r.Context(), time.Since(t0))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrForbidden.WithReasonf(\"Could not verify id_token\").WithWrap(err).WithError(err.Error()))\n\t}\n\n\tif err := claims.Validate(); err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrForbidden.WithReasonf(\"The id_token claims were invalid\").WithWrap(err))\n\t}\n\n\t// First check if the JWT contains the nonce claim.\n\tif claims.Nonce == \"\" {\n\t\t// If it doesn't, check if the provider supports nonces.\n\t\tif nonceSkipper, ok := verifier.(NonceValidationSkipper); !ok || !nonceSkipper.CanSkipNonce(claims) {\n\t\t\t// If the provider supports nonces, abort the flow!\n\t\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"No nonce was included in the id_token but is required by the provider\"))\n\t\t}\n\t\t// If the provider does not support nonces, we don't do validation and return the claim.\n\t\t// This case only applies to Apple, as some of their devices do not support nonces.\n\t\t// https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple\n\t} else if idTokenNonce == \"\" {\n\t\t// A nonce was present in the JWT token, but no nonce was submitted in the flow\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"No nonce was provided but is required by the provider\"))\n\t} else if idTokenNonce != claims.Nonce {\n\t\t// The nonce from the JWT token does not match the nonce from the flow.\n\t\treturn nil, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"The supplied nonce does not match the nonce from the id_token\"))\n\t}\n\t// Nonce checking was successful\n\n\treturn claims, nil\n}\n\nfunc (s *Strategy) linkCredentials(ctx context.Context, i *identity.Identity, tokens *identity.CredentialsOIDCEncryptedTokens, provider, subject, organization string) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"strategy.oidc.linkCredentials\", trace.WithAttributes(\n\t\tattribute.String(\"provider\", provider),\n\t\t// attribute.String(\"subject\", subject), // PII\n\t\tattribute.String(\"organization\", organization)))\n\tdefer otelx.End(span, &err)\n\n\tif len(i.Credentials) == 0 {\n\t\tif err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, i, identity.ExpandCredentials); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar conf identity.CredentialsOIDC\n\tcreds, err := i.ParseCredentials(s.ID(), &conf)\n\tif errors.Is(err, herodot.ErrNotFound) {\n\t\tvar err error\n\t\tif creds, err = identity.NewOIDCLikeCredentials(tokens, s.ID(), provider, subject, organization); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if err != nil {\n\t\treturn err\n\t} else {\n\t\tcreds.Identifiers = append(creds.Identifiers, identity.OIDCUniqueID(provider, subject))\n\t\tconf.Providers = append(conf.Providers, identity.CredentialsOIDCProvider{\n\t\t\tSubject:             subject,\n\t\t\tProvider:            provider,\n\t\t\tInitialAccessToken:  tokens.GetAccessToken(),\n\t\t\tInitialRefreshToken: tokens.GetRefreshToken(),\n\t\t\tInitialIDToken:      tokens.GetIDToken(),\n\t\t\tOrganization:        organization,\n\t\t})\n\n\t\tcreds.Config, err = json.Marshal(conf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\ti.Credentials[s.ID()] = *creds\n\tif orgID, err := uuid.FromString(organization); err == nil {\n\t\ti.OrganizationID = uuid.NullUUID{UUID: orgID, Valid: true}\n\t}\n\n\treturn nil\n}\n\nfunc getAuthRedirectURL(ctx context.Context, provider Provider, req ider, state string, upstreamParameters map[string]string, opts []oauth2.AuthCodeOption) (codeURL string, err error) {\n\tswitch p := provider.(type) {\n\tcase OAuth2Provider:\n\t\tc, err := p.OAuth2(ctx)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\topts = append(opts, p.AuthCodeURLOptions(req)...)\n\t\topts = append(opts, UpstreamParameters(upstreamParameters)...)\n\n\t\treturn c.AuthCodeURL(state, opts...), nil\n\tcase OAuth1Provider:\n\t\treturn p.AuthURL(ctx, state)\n\tdefault:\n\t\treturn \"\", errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"The provider %s does not support the OAuth 2.0 or OAuth 1.0 protocol\", provider.Config().Provider))\n\t}\n}\n\nfunc (s *Strategy) encryptOAuth2Tokens(ctx context.Context, token *oauth2.Token) (et *identity.CredentialsOIDCEncryptedTokens, err error) {\n\tet = new(identity.CredentialsOIDCEncryptedTokens)\n\tif token == nil {\n\t\treturn et, nil\n\t}\n\n\tif idToken, ok := token.Extra(\"id_token\").(string); ok {\n\t\tet.IDToken, err = s.d.Cipher(ctx).Encrypt(ctx, []byte(idToken))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tet.AccessToken, err = s.d.Cipher(ctx).Encrypt(ctx, []byte(token.AccessToken))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tet.RefreshToken, err = s.d.Cipher(ctx).Encrypt(ctx, []byte(token.RefreshToken))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn et, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/strategy_helper_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v4\"\n\n\t\"github.com/phayes/freeport\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/rakutentech/jwk-go/jwk\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/moby/moby/api/types/container\"\n\t\"github.com/moby/moby/api/types/network\"\n\n\tdockertest \"github.com/ory/dockertest/v4\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/resilience\"\n\t\"github.com/ory/x/urlx\"\n)\n\ntype idTokenClaims struct {\n\ttraits struct {\n\t\twebsite string\n\t\tgroups  []string\n\t}\n\tmetadataPublic struct {\n\t\tpicture string\n\t}\n\tmetadataAdmin struct {\n\t\tphoneNumber string\n\t}\n}\n\nfunc (token *idTokenClaims) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tIDToken struct {\n\t\t\tWebsite     string   `json:\"website,omitempty\"`\n\t\t\tGroups      []string `json:\"groups,omitempty\"`\n\t\t\tPicture     string   `json:\"picture,omitempty\"`\n\t\t\tPhoneNumber string   `json:\"phone_number,omitempty\"`\n\t\t} `json:\"id_token\"`\n\t}{\n\t\tIDToken: struct {\n\t\t\tWebsite     string   `json:\"website,omitempty\"`\n\t\t\tGroups      []string `json:\"groups,omitempty\"`\n\t\t\tPicture     string   `json:\"picture,omitempty\"`\n\t\t\tPhoneNumber string   `json:\"phone_number,omitempty\"`\n\t\t}{\n\t\t\tWebsite:     token.traits.website,\n\t\t\tGroups:      token.traits.groups,\n\t\t\tPicture:     token.metadataPublic.picture,\n\t\t\tPhoneNumber: token.metadataAdmin.phoneNumber,\n\t\t},\n\t})\n}\n\nfunc createClient(t *testing.T, remote string, redir []string) (id, secret string) {\n\trequire.EventuallyWithT(t, func(t *assert.CollectT) {\n\t\tvar b bytes.Buffer\n\t\trequire.NoError(t, json.NewEncoder(&b).Encode(&struct {\n\t\t\tScope                   string   `json:\"scope\"`\n\t\t\tGrantTypes              []string `json:\"grant_types\"`\n\t\t\tResponseTypes           []string `json:\"response_types\"`\n\t\t\tRedirectURIs            []string `json:\"redirect_uris\"`\n\t\t\tTokenEndpointAuthMethod string   `json:\"token_endpoint_auth_method\"`\n\t\t}{\n\t\t\tGrantTypes:    []string{\"authorization_code\", \"refresh_token\"},\n\t\t\tResponseTypes: []string{\"code\"},\n\t\t\tScope:         \"offline offline_access openid\",\n\t\t\tRedirectURIs:  redir,\n\n\t\t\t// This is a workaround to prevent golang.org/x/oauth2 from\n\t\t\t// swallowing the actual error messages from failed token exchanges.\n\t\t\t//\n\t\t\t// The library first attempts to use the Authorization header to\n\t\t\t// pass Client ID+secret during token exchange (client_secret_basic\n\t\t\t// in Hydra terminology). If that fails (with any error), it tries\n\t\t\t// again with the Client ID+secret passed in the HTTP POST body\n\t\t\t// (client_secret_post in Hydra). If that also fails, this second\n\t\t\t// error is returned.\n\t\t\t//\n\t\t\t// Now, if the the client was indeed configured to use\n\t\t\t// client_secret_basic, but the token exchange fails for another\n\t\t\t// reason, the error message will be swallowed and replaced with\n\t\t\t// \"invalid_client\".\n\t\t\t//\n\t\t\t// Manually setting this to client_secret_post means that during\n\t\t\t// tests, all token exchanges will first fail with `invalid_client`\n\t\t\t// and then be retried with the correct method. This is the only way\n\t\t\t// to get the actual error message from the server, however.\n\t\t\t//\n\t\t\t// https://github.com/golang/oauth2/blob/5fd42413edb3b1699004a31b72e485e0e4ba1b13/pkg/token.go#L227-L242\n\t\t\tTokenEndpointAuthMethod: \"client_secret_post\",\n\t\t}))\n\n\t\tres, err := http.Post(remote+\"/admin/clients\", \"application/json\", &b)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\trequire.Equal(t, http.StatusCreated, res.StatusCode)\n\n\t\tid = gjson.GetBytes(body, \"client_id\").String()\n\t\tsecret = gjson.GetBytes(body, \"client_secret\").String()\n\t}, time.Minute, time.Second)\n\treturn\n}\n\nfunc newHydraIntegration(t *testing.T, remote *string, subject *string, claims *idTokenClaims, scope *[]string) string {\n\trouter := http.NewServeMux()\n\n\ttype p struct {\n\t\tSubject    string          `json:\"subject,omitempty\"`\n\t\tSession    json.RawMessage `json:\"session,omitempty\"`\n\t\tGrantScope []string        `json:\"grant_scope,omitempty\"`\n\t}\n\n\tdo := func(w http.ResponseWriter, r *http.Request, href string, payload io.Reader) {\n\t\treq, err := http.NewRequest(\"PUT\", href, payload)\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tres, err := http.DefaultClient.Do(req)\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\n\t\tvar response struct {\n\t\t\tRedirectTo string `json:\"redirect_to\"`\n\t\t}\n\t\trequire.NoError(t, json.NewDecoder(bytes.NewBuffer(body)).Decode(&response))\n\t\trequire.NotNil(t, response.RedirectTo, \"%s\", body)\n\n\t\thttp.Redirect(w, r, response.RedirectTo, http.StatusSeeOther)\n\t}\n\n\trouter.HandleFunc(\"GET /login\", func(w http.ResponseWriter, r *http.Request) {\n\t\trequire.NotEmpty(t, *remote)\n\t\trequire.NotEmpty(t, *subject)\n\n\t\tchallenge := r.URL.Query().Get(\"login_challenge\")\n\t\trequire.NotEmpty(t, challenge)\n\n\t\tvar b bytes.Buffer\n\t\trequire.NoError(t, json.NewEncoder(&b).Encode(&p{\n\t\t\tSubject: *subject,\n\t\t}))\n\t\thref := urlx.MustJoin(*remote, \"/admin/oauth2/auth/requests/login/accept\") + \"?login_challenge=\" + challenge\n\t\tdo(w, r, href, &b)\n\t})\n\n\trouter.HandleFunc(\"GET /consent\", func(w http.ResponseWriter, r *http.Request) {\n\t\trequire.NotEmpty(t, *remote)\n\t\trequire.NotNil(t, *scope)\n\n\t\tchallenge := r.URL.Query().Get(\"consent_challenge\")\n\t\trequire.NotEmpty(t, challenge)\n\n\t\tvar b bytes.Buffer\n\t\tmsg, err := json.Marshal(claims)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, json.NewEncoder(&b).Encode(&p{GrantScope: *scope, Session: msg}))\n\t\thref := urlx.MustJoin(*remote, \"/admin/oauth2/auth/requests/consent/accept\") + \"?consent_challenge=\" + challenge\n\t\tdo(w, r, href, &b)\n\t})\n\n\tserver := httptest.NewServer(router)\n\tt.Cleanup(server.Close)\n\tserver.URL = strings.Replace(server.URL, \"127.0.0.1\", \"localhost\", 1)\n\treturn server.URL\n}\n\nfunc newReturnTS(t *testing.T, reg driver.Registry) *httptest.Server {\n\tctx := context.Background()\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.URL.Path == \"/app_code\" {\n\t\t\treg.Writer().Write(w, r, \"ok\")\n\t\t\treturn\n\t\t}\n\t\tsess, err := reg.SessionManager().FetchFromRequest(r.Context(), r)\n\t\trequire.NoError(t, err)\n\t\treg.Writer().Write(w, r, sess)\n\t}))\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, ts.URL)\n\treg.Config().MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{ts.URL})\n\tt.Cleanup(ts.Close)\n\treturn ts\n}\n\nfunc newUI(t *testing.T, reg driver.Registry) *httptest.Server {\n\tctx := context.Background()\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tvar e interface{}\n\t\tvar err error\n\t\tswitch r.URL.Path {\n\t\tcase \"/login\":\n\t\t\te, err = reg.LoginFlowPersister().GetLoginFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"flow\")))\n\t\tcase \"/registration\":\n\t\t\te, err = reg.RegistrationFlowPersister().GetRegistrationFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"flow\")))\n\t\tcase \"/settings\":\n\t\t\te, err = reg.SettingsFlowPersister().GetSettingsFlow(r.Context(), x.ParseUUID(r.URL.Query().Get(\"flow\")))\n\t\t}\n\n\t\trequire.NoError(t, err)\n\t\treg.Writer().Write(w, r, e)\n\t}))\n\tt.Cleanup(ts.Close)\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceLoginUI, ts.URL+\"/login\")\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceRegistrationUI, ts.URL+\"/registration\")\n\treg.Config().MustSet(ctx, config.ViperKeySelfServiceSettingsURL, ts.URL+\"/settings\")\n\treturn ts\n}\n\nfunc newHydra(t *testing.T, subject *string, claims *idTokenClaims, scope *[]string) (remoteAdmin, remotePublic, hydraIntegrationTSURL string) {\n\thydraIntegrationTSURL = newHydraIntegration(t, &remoteAdmin, subject, claims, scope)\n\n\tpublicPort, err := freeport.GetFreePort()\n\trequire.NoError(t, err)\n\n\tpool := dockertest.NewPoolT(t, \"\")\n\n\thydra := pool.RunT(t, \"oryd/hydra\",\n\t\t// Keep tag in sync with the version in ci.yaml\n\t\tdockertest.WithTag(\"v2.2.0\"),\n\t\tdockertest.WithoutReuse(),\n\t\tdockertest.WithEnv([]string{\n\t\t\t\"DSN=memory\",\n\t\t\tfmt.Sprintf(\"URLS_SELF_ISSUER=http://localhost:%d/\", publicPort),\n\t\t\t\"URLS_LOGIN=\" + hydraIntegrationTSURL + \"/login\",\n\t\t\t\"URLS_CONSENT=\" + hydraIntegrationTSURL + \"/consent\",\n\t\t\t\"LOG_LEAK_SENSITIVE_VALUES=true\",\n\t\t\t\"SECRETS_SYSTEM=someverylongsecretthatis32byteslong\",\n\t\t}),\n\t\tdockertest.WithCmd([]string{\"serve\", \"all\", \"--dev\"}),\n\t\tdockertest.WithContainerConfig(func(cc *container.Config) {\n\t\t\tcc.ExposedPorts = network.PortSet{\n\t\t\t\tnetwork.MustParsePort(\"4444/tcp\"): struct{}{},\n\t\t\t\tnetwork.MustParsePort(\"4445/tcp\"): struct{}{},\n\t\t\t}\n\t\t}),\n\t\tdockertest.WithHostConfig(func(hc *container.HostConfig) {\n\t\t\thc.PortBindings = network.PortMap{\n\t\t\t\tnetwork.MustParsePort(\"4444/tcp\"): {{HostPort: strconv.Itoa(publicPort)}},\n\t\t\t\tnetwork.MustParsePort(\"4445/tcp\"): {{HostPort: \"\"}},\n\t\t\t}\n\t\t}),\n\t)\n\trequire.NotEmpty(t, hydra.GetPort(\"4444/tcp\"), \"%+v\", hydra.Container().NetworkSettings.Ports)\n\trequire.NotEmpty(t, hydra.GetPort(\"4445/tcp\"), \"%+v\", hydra.Container)\n\n\tremotePublic = \"http://localhost:\" + hydra.GetPort(\"4444/tcp\")\n\tremoteAdmin = \"http://localhost:\" + hydra.GetPort(\"4445/tcp\")\n\n\terr = resilience.Retry(logrusx.New(\"\", \"\"), time.Second*1, time.Second*30, func() error {\n\t\tpr := remotePublic + \"/health/ready\"\n\t\tres, err := http.DefaultClient.Get(pr)\n\t\tif err != nil || res.StatusCode != 200 {\n\t\t\treturn errors.Errorf(\"Hydra public is not ready at %s\", pr)\n\t\t}\n\n\t\twellKnown := remotePublic + \"/.well-known/openid-configuration\"\n\t\tres, err = http.DefaultClient.Get(wellKnown)\n\t\tif err != nil || res.StatusCode != 200 {\n\t\t\treturn errors.Errorf(\"Hydra .well-known is not ready at %s\", wellKnown)\n\t\t}\n\n\t\tar := remoteAdmin + \"/health/ready\"\n\t\tres, err = http.DefaultClient.Get(ar)\n\t\tif err != nil {\n\t\t\treturn errors.Errorf(\"Hydra admin is not ready at %s\", ar)\n\t\t} else if res.StatusCode != 200 {\n\t\t\treturn errors.Errorf(\"Hydra admin is not ready at %s\", ar)\n\t\t}\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n\n\tt.Logf(\"Ory Hydra running at: %s %s\", remotePublic, remoteAdmin)\n\n\treturn remoteAdmin, remotePublic, hydraIntegrationTSURL\n}\n\nfunc newOIDCProvider(\n\tt *testing.T,\n\tkratos *httptest.Server,\n\thydraPublic string,\n\thydraAdmin string,\n\tid string,\n\topts ...func(*oidc.Configuration),\n) oidc.Configuration {\n\tclientID, secret := createClient(t, hydraAdmin, []string{kratos.URL + oidc.RouteBase + \"/callback/\" + id, kratos.URL + oidc.RouteCallbackGeneric})\n\n\tcfg := oidc.Configuration{\n\t\tProvider:     \"generic\",\n\t\tID:           id,\n\t\tClientID:     clientID,\n\t\tClientSecret: secret,\n\t\tIssuerURL:    hydraPublic + \"/\",\n\t\tMapper:       \"file://./stub/oidc.hydra.jsonnet\",\n\t}\n\tfor _, opt := range opts {\n\t\topt(&cfg)\n\t}\n\n\treturn cfg\n}\n\nfunc viperSetProviderConfig(t *testing.T, conf *config.Config, providers ...oidc.Configuration) {\n\tctx := context.Background()\n\tbaseKey := fmt.Sprintf(\"%s.%s\", config.ViperKeySelfServiceStrategyConfig, identity.CredentialsTypeOIDC)\n\tcurrentConfig := conf.GetProvider(ctx).Get(baseKey + \".config\")\n\tcurrentEnabled := conf.GetProvider(ctx).Get(baseKey + \".enabled\")\n\n\tconf.MustSet(ctx, baseKey+\".config\", &oidc.ConfigurationCollection{Providers: providers})\n\tconf.MustSet(ctx, baseKey+\".enabled\", true)\n\n\tt.Cleanup(func() {\n\t\tconf.MustSet(ctx, baseKey+\".config\", currentConfig)\n\t\tconf.MustSet(ctx, baseKey+\".enabled\", currentEnabled)\n\t})\n}\n\n// AssertSystemError asserts an error ui response\nfunc AssertSystemError(t *testing.T, errTS *httptest.Server, res *http.Response, body []byte, code int, reason string) {\n\trequire.Contains(t, res.Request.URL.String(), errTS.URL, \"%s\", body)\n\n\tassert.Equal(t, int64(code), gjson.GetBytes(body, \"code\").Int(), \"%s\", body)\n\tassert.Contains(t, gjson.GetBytes(body, \"reason\").String(), reason, \"%s\", body)\n}\n\n//go:embed stub/jwk.json\nvar rawKey []byte\n\n//go:embed stub/jwks_public.json\nvar publicJWKS []byte\n\n// Just a public key set, to be able to test what happens if an ID token was issued by a different private key.\n//\n//go:embed stub/jwks_public2.json\nvar publicJWKS2 []byte\n\ntype claims struct {\n\t*jwt.RegisteredClaims\n\tEmail string `json:\"email\"`\n}\n\nfunc createIDToken(t *testing.T, cl jwt.RegisteredClaims) string {\n\tkey := &jwk.KeySpec{}\n\trequire.NoError(t, json.Unmarshal(rawKey, key))\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodRS256, &claims{\n\t\tRegisteredClaims: &cl,\n\t\tEmail:            \"acme@ory.sh\",\n\t})\n\ttoken.Header[\"kid\"] = key.KeyID\n\ts, err := token.SignedString(key.Key)\n\trequire.NoError(t, err)\n\treturn s\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/strategy_login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"bytes\"\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/sjson\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/continuity\"\n\toidcv1 \"github.com/ory/kratos/gen/oidc/v1\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flowhelpers\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nvar (\n\t_ login.AAL1FormHydrator = (*Strategy)(nil)\n\t_ login.Strategy         = (*Strategy)(nil)\n)\n\n// Update Login Flow with OpenID Connect Method\n//\n// swagger:model updateLoginFlowWithOidcMethod\ntype UpdateLoginFlowWithOidcMethod struct {\n\t// The provider to register with\n\t//\n\t// required: true\n\tProvider string `json:\"provider\"`\n\n\t// The CSRF Token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Method to use\n\t//\n\t// This field must be set to `oidc` when using the oidc method.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// The identity traits. This is a placeholder for the registration flow.\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// UpstreamParameters are the parameters that are passed to the upstream identity provider.\n\t//\n\t// These parameters are optional and depend on what the upstream identity provider supports.\n\t// Supported parameters are:\n\t// - `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\n\t// - `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\n\t// - `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\n\t// - `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\n\t//\n\t// required: false\n\tUpstreamParameters json.RawMessage `json:\"upstream_parameters\"`\n\n\t// IDToken is an optional id token provided by an OIDC provider\n\t//\n\t// If submitted, it is verified using the OIDC provider's public key set and the claims are used to populate\n\t// the OIDC credentials of the identity.\n\t// If the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use\n\t// the `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.\n\t//\n\t// Supported providers are\n\t// - Apple\n\t// - Google\n\t// required: false\n\tIDToken string `json:\"id_token,omitempty\"`\n\n\t// IDTokenNonce is the nonce, used when generating the IDToken.\n\t// If the provider supports nonce validation, the nonce will be validated against this value and required.\n\t//\n\t// required: false\n\tIDTokenNonce string `json:\"id_token_nonce,omitempty\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) handleConflictingIdentity(ctx context.Context, loginFlow *login.Flow, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider, container *AuthCodeContainer) (verdict ConflictingIdentityVerdict, id *identity.Identity, credentials *identity.Credentials, err error) {\n\tif s.conflictingIdentityPolicy == nil {\n\t\treturn ConflictingIdentityVerdictReject, nil, nil, nil\n\t}\n\n\t// Find out if there is a conflicting identity\n\tnewIdentity, va, err := s.newIdentityFromClaims(ctx, claims, provider, container, loginFlow.IdentitySchema)\n\tif err != nil {\n\t\treturn ConflictingIdentityVerdictReject, nil, nil, nil\n\t}\n\n\t// Validate the identity itself\n\t// We ignore the error here because the claims may not fulfil the requirements\n\t// of the identity schema.\n\t//\n\t// However, this is not a problem because the identity will be merged with the existing\n\t// identity and the existing identity will be updated with the new credentials, but not any traits.\n\t//\n\t// We do need the validation step however, to \"hydrate\" the verifiable address of the user, which is then\n\t// used in subsequent calls to match the existing with the new identity.\n\t_ = s.d.IdentityValidator().Validate(ctx, newIdentity)\n\n\tfor n := range newIdentity.VerifiableAddresses {\n\t\tverifiable := &newIdentity.VerifiableAddresses[n]\n\t\tfor _, verified := range va {\n\t\t\tif verifiable.Via == verified.Via && verifiable.Value == verified.Value {\n\t\t\t\tverifiable.Status = identity.VerifiableAddressStatusCompleted\n\t\t\t\tverifiable.Verified = true\n\t\t\t\tt := sqlxx.NullTime(time.Now().UTC().Round(time.Second))\n\t\t\t\tverifiable.VerifiedAt = &t\n\t\t\t}\n\t\t}\n\t}\n\n\tcreds, err := identity.NewOIDCLikeCredentials(token, s.ID(), provider.Config().ID, claims.Subject, provider.Config().OrganizationID)\n\tif err != nil {\n\t\treturn ConflictingIdentityVerdictUnknown, nil, nil, err\n\t}\n\n\tnewIdentity.SetCredentials(s.ID(), *creds)\n\n\texistingIdentity, _, _, err := s.d.IdentityManager().ConflictingIdentity(ctx, newIdentity)\n\tif err != nil {\n\t\treturn ConflictingIdentityVerdictReject, nil, nil, nil\n\t}\n\n\tverdict = s.conflictingIdentityPolicy(ctx, existingIdentity, newIdentity, provider, claims)\n\tif verdict == ConflictingIdentityVerdictMerge {\n\t\tif err = existingIdentity.MergeOIDCCredentials(s.ID(), *creds); err != nil {\n\t\t\treturn ConflictingIdentityVerdictUnknown, nil, nil, err\n\t\t}\n\n\t\tif err = s.d.PrivilegedIdentityPool().UpdateIdentity(ctx, existingIdentity); err != nil {\n\t\t\treturn ConflictingIdentityVerdictUnknown, nil, nil, err\n\t\t}\n\t}\n\n\treturn verdict, existingIdentity, creds, nil\n}\n\nfunc (s *Strategy) ProcessLogin(ctx context.Context, w http.ResponseWriter, r *http.Request, loginFlow *login.Flow, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider, container *AuthCodeContainer) (_ *registration.Flow, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.Strategy.processLogin\")\n\tdefer otelx.End(span, &err)\n\n\ti, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), identity.OIDCUniqueID(provider.Config().ID, claims.Subject))\n\tif err != nil {\n\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\tvar verdict ConflictingIdentityVerdict\n\t\t\tverdict, i, c, err = s.handleConflictingIdentity(ctx, loginFlow, token, claims, provider, container)\n\t\t\tswitch verdict {\n\t\t\tcase ConflictingIdentityVerdictMerge:\n\t\t\t\t// Do nothing\n\t\t\tcase ConflictingIdentityVerdictReject:\n\t\t\t\t// If no account was found we're \"manually\" creating a new registration flow and redirecting the browser\n\t\t\t\t// to that endpoint.\n\n\t\t\t\t// That will execute the \"pre registration\" hook which allows to e.g. disallow this request. The registration\n\t\t\t\t// ui however will NOT be shown, instead the user is directly redirected to the auth path. That should then\n\t\t\t\t// do a silent re-request. While this might be a bit excessive from a network perspective it should usually\n\t\t\t\t// happen without any downsides to user experience as the flow has already been authorized and should\n\t\t\t\t// not need additional consent/login.\n\n\t\t\t\t// This is kinda hacky but the only way to ensure seamless login/registration flows when using OIDC.\n\t\t\t\ts.d.\n\t\t\t\t\tLogger().\n\t\t\t\t\tWithField(\"provider\", provider.Config().ID).\n\t\t\t\t\tWithField(\"subject\", claims.Subject).\n\t\t\t\t\tDebug(\"Received successful OpenID Connect callback but user is not registered. Re-initializing registration flow now.\")\n\n\t\t\t\t// If return_to was set before, we need to preserve it.\n\t\t\t\tvar opts []registration.FlowOption\n\t\t\t\tif len(loginFlow.ReturnTo) > 0 {\n\t\t\t\t\topts = append(opts, registration.WithFlowReturnTo(loginFlow.ReturnTo))\n\t\t\t\t}\n\n\t\t\t\tif loginFlow.OAuth2LoginChallenge.String() != \"\" {\n\t\t\t\t\topts = append(opts, registration.WithFlowOAuth2LoginChallenge(loginFlow.OAuth2LoginChallenge.String()))\n\t\t\t\t}\n\n\t\t\t\tregistrationFlow, err := s.d.RegistrationHandler().NewRegistrationFlow(w, r, loginFlow.Type, opts...)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, s.HandleError(ctx, w, r, loginFlow, provider.Config().ID, nil, err)\n\t\t\t\t}\n\n\t\t\t\terr = s.d.SessionTokenExchangePersister().MoveToNewFlow(ctx, loginFlow.ID, registrationFlow.ID)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, s.HandleError(ctx, w, r, loginFlow, provider.Config().ID, nil, err)\n\t\t\t\t}\n\n\t\t\t\tregistrationFlow.OrganizationID = loginFlow.OrganizationID\n\t\t\t\tregistrationFlow.IDToken = loginFlow.IDToken\n\t\t\t\tregistrationFlow.RawIDTokenNonce = loginFlow.RawIDTokenNonce\n\t\t\t\tregistrationFlow.TransientPayload = loginFlow.TransientPayload\n\t\t\t\tregistrationFlow.Active = s.ID()\n\t\t\t\tregistrationFlow.IdentitySchema = loginFlow.IdentitySchema\n\n\t\t\t\t// We are converting the flow here, but want to retain the original request URL.\n\t\t\t\tregistrationFlow.RequestURL = loginFlow.RequestURL\n\n\t\t\t\tif _, err := s.processRegistration(ctx, w, r, registrationFlow, token, claims, provider, container); err != nil {\n\t\t\t\t\treturn registrationFlow, err\n\t\t\t\t}\n\n\t\t\t\treturn nil, nil\n\t\t\tcase ConflictingIdentityVerdictUnknown:\n\t\t\t\tfallthrough\n\t\t\tdefault:\n\t\t\t\t// This should never happen if err == nil, but just for safety:\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReason(\"The OpenID Connect identity merge policy returned an unknown verdict without other error details, which prevents the sign up from completing. Please report this as a bug.\"))\n\t\t\t}\n\n\t\t} else {\n\t\t\treturn nil, s.HandleError(ctx, w, r, loginFlow, provider.Config().ID, nil, err)\n\t\t}\n\t}\n\n\tvar oidcCredentials identity.CredentialsOIDC\n\tif err := json.NewDecoder(bytes.NewBuffer(c.Config)).Decode(&oidcCredentials); err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, loginFlow, provider.Config().ID, nil, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"The OpenID Connect credentials could not be decoded properly\").WithDebug(err.Error())), i.ID))\n\t}\n\n\tsess := session.NewInactiveSession()\n\tsess.CompletedLoginForWithProvider(s.ID(), identity.AuthenticatorAssuranceLevel1, provider.Config().ID, provider.Config().OrganizationID)\n\n\tfor _, c := range oidcCredentials.Providers {\n\t\tif c.Subject == claims.Subject && c.Provider == provider.Config().ID {\n\t\t\tif err = s.d.LoginHookExecutor().PostLoginHook(w, r, node.OpenIDConnectGroup, loginFlow, i, sess, provider.Config().ID); err != nil {\n\t\t\t\treturn nil, x.WrapWithIdentityIDError(s.HandleError(ctx, w, r, loginFlow, provider.Config().ID, nil, err), i.ID)\n\t\t\t}\n\t\t\treturn nil, nil\n\t\t}\n\t}\n\n\treturn nil, s.HandleError(ctx, w, r, loginFlow, provider.Config().ID, nil, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Unable to find matching OpenID Connect credentials.\").WithDebugf(`Unable to find credentials that match the given provider \"%s\" and subject \"%s\".`, provider.Config().ID, claims.Subject)), i.ID))\n}\n\nfunc (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, _ *session.Session) (i *identity.Identity, err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.oidc.Strategy.Login\")\n\tdefer otelx.End(span, &err)\n\n\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel1); err != nil {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"requested AAL is not AAL1\"))\n\t\treturn nil, err\n\t}\n\n\tvar p UpdateLoginFlowWithOidcMethod\n\tif err := s.newLinkDecoder(ctx, &p, r, &f.IdentitySchema); err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, f, \"\", nil, err)\n\t}\n\n\tf.IDToken = p.IDToken\n\tf.RawIDTokenNonce = p.IDTokenNonce\n\tf.TransientPayload = p.TransientPayload\n\n\tpid := p.Provider // this can come from both url query and post body\n\tif pid == \"\" {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"provider ID missing\"))\n\t\treturn nil, errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\tif !strings.EqualFold(strings.ToLower(p.Method), s.SettingsStrategyID()) && p.Method != \"\" {\n\t\t// the user is sending a method that is not oidc, but the payload includes a provider\n\t\ts.d.Logger().\n\t\t\tWithRequest(r).\n\t\t\tWithField(\"provider\", p.Provider).\n\t\t\tWithField(\"method\", p.Method).\n\t\t\tWarn(\"The payload includes a `provider` field but is using a method other than `oidc`. Therefore, social sign in will not be executed.\")\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"method is not oidc\"))\n\t\treturn nil, errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, s.handleMethodNotAllowedError(err))\n\t}\n\n\tprovider, err := s.Provider(ctx, pid)\n\tif err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\n\treq, err := s.validateFlow(ctx, r, f.ID, oidcv1.FlowKind_FLOW_KIND_LOGIN)\n\tif err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\n\tif authenticated, err := s.alreadyAuthenticated(ctx, w, r, req); err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, err)\n\t} else if authenticated {\n\t\treturn i, nil\n\t}\n\n\tif p.IDToken != \"\" {\n\t\tclaims, err := s.ProcessIDToken(r, provider, p.IDToken, p.IDTokenNonce)\n\t\tif err != nil {\n\t\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, err)\n\t\t}\n\t\t_, err = s.ProcessLogin(ctx, w, r, f, nil, claims, provider, &AuthCodeContainer{\n\t\t\tFlowID: f.ID.String(),\n\t\t\tTraits: p.Traits,\n\t\t})\n\t\tif errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\treturn nil, err\n\t\t} else if err != nil {\n\t\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, err)\n\t\t}\n\t\treturn nil, errors.WithStack(flow.ErrCompletedByStrategy)\n\t}\n\n\tstate, pkce, err := s.GenerateState(ctx, provider, f)\n\tif err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\tif err := s.d.ContinuityManager().Pause(ctx, w, r, sessionName,\n\t\tcontinuity.WithPayload(&AuthCodeContainer{\n\t\t\tState:            state,\n\t\t\tFlowID:           f.ID.String(),\n\t\t\tTraits:           p.Traits,\n\t\t\tTransientPayload: f.TransientPayload,\n\t\t\tIdentitySchema:   f.IdentitySchema,\n\t\t}),\n\t\tcontinuity.WithLifespan(time.Minute*30)); err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\n\t// For API/native flows, persist TransientPayload in InternalContext so it\n\t// survives the OIDC redirect. Browser flows restore it from the continuity\n\t// cookie instead, which is not available in native flows because the\n\t// callback comes from a different user agent (system browser/webview).\n\tif f.Type == flow.TypeAPI && len(f.TransientPayload) > 0 {\n\t\tf.EnsureInternalContext()\n\t\tic, err := sjson.SetRawBytes(f.InternalContext, \"transient_payload\", f.TransientPayload)\n\t\tif err != nil {\n\t\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, err)\n\t\t}\n\t\tf.InternalContext = ic\n\t}\n\n\tf.Active = s.ID()\n\tif err = s.d.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Could not update flow\").WithWrap(err)))\n\t}\n\n\tvar up map[string]string\n\tif err := json.NewDecoder(bytes.NewBuffer(p.UpstreamParameters)).Decode(&up); err != nil {\n\t\treturn nil, err\n\t}\n\n\tcodeURL, err := getAuthRedirectURL(ctx, provider, f, state, up, pkce)\n\tif err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\n\tif x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(codeURL))\n\t} else {\n\t\thttp.Redirect(w, r, codeURL, http.StatusSeeOther)\n\t}\n\n\treturn nil, errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactorRefresh(r *http.Request, lf *login.Flow, _ *session.Session) error {\n\tconf, err := s.Config(r.Context())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar providers []Configuration\n\t_, id, c := flowhelpers.GuessForcedLoginIdentifier(r, s.d, lf, s.ID())\n\tif id == nil || c == nil {\n\t\tproviders = nil\n\t} else {\n\t\tvar credentials identity.CredentialsOIDC\n\t\tif err := json.Unmarshal(c.Config, &credentials); err != nil {\n\t\t\t// failed to read OIDC credentials, don't add any providers\n\t\t\tproviders = nil\n\t\t} else {\n\t\t\t// add only providers that can actually be used to log in as this identity\n\t\t\tproviders = make([]Configuration, 0, len(conf.Providers))\n\t\t\tfor i := range conf.Providers {\n\t\t\t\tfor j := range credentials.Providers {\n\t\t\t\t\tif conf.Providers[i].ID == credentials.Providers[j].Provider {\n\t\t\t\t\t\tproviders = append(providers, conf.Providers[i])\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tlf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tAddProviders(lf.UI, providers, text.NewInfoLoginWith, s.ID())\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactor(r *http.Request, f *login.Flow) error {\n\treturn s.populateMethod(r, f, text.NewInfoLoginWith)\n}\n\nfunc (s *Strategy) removeProviders(conf *ConfigurationCollection, f *login.Flow) {\n\tfor _, l := range conf.Providers {\n\t\tgroup := node.OpenIDConnectGroup\n\t\tif s.ID() == identity.CredentialsTypeSAML {\n\t\t\tgroup = node.SAMLGroup\n\t\t}\n\n\t\tif l.OrganizationID != \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tf.GetUI().Nodes.RemoveMatching(&node.Node{\n\t\t\tGroup: group,\n\t\t\tType:  node.Input,\n\t\t\tAttributes: &node.InputAttributes{\n\t\t\t\tName:       \"provider\",\n\t\t\t\tFieldValue: l.ID,\n\t\t\t},\n\t\t})\n\t}\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstCredentials(r *http.Request, f *login.Flow, mods ...login.FormHydratorModifier) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.oidc.Strategy.PopulateLoginMethodIdentifierFirstCredentials\")\n\tdefer otelx.End(span, &err)\n\n\tconf, err := s.Config(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\to := login.NewFormHydratorOptions(mods)\n\n\tvar linked []Provider\n\tif o.IdentityHint != nil {\n\t\tvar err error\n\t\t// If we have an identity hint we check if the identity has any providers configured.\n\t\tif linked, err = s.linkedProviders(conf, o.IdentityHint); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif len(linked) == 0 {\n\t\t// If we found no credentials:\n\t\tif s.d.Config().SecurityAccountEnumerationMitigate(ctx) {\n\t\t\t// We found no credentials but do not want to leak that we know that. So we return early and do not\n\t\t\t// modify the initial provider list.\n\t\t\treturn nil\n\t\t}\n\n\t\tif o.IdentityHint != nil {\n\t\t\t// We found no credentials. We remove all the providers and tell the strategy that we found nothing.\n\t\t\t// We only execute this, if the identity hint is set, otherwise we do not know if the user has any credentials and we likely stay on the `provide_credentials` screen.\n\t\t\t// The OIDC method is special in that regard, as it's the only method showing buttons on that screen.\n\t\t\ts.removeProviders(conf, f)\n\t\t}\n\t\treturn idfirst.ErrNoCredentialsFound\n\t}\n\n\tif !s.d.Config().SecurityAccountEnumerationMitigate(ctx) {\n\t\t// Account enumeration is disabled, so we show all providers that are linked to the identity.\n\t\t// User is found and enumeration mitigation is disabled. Filter the list!\n\t\ts.removeProviders(conf, f)\n\n\t\tfor _, l := range linked {\n\t\t\tlc := l.Config()\n\n\t\t\t// Organizations are handled differently.\n\t\t\tif lc.OrganizationID != \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tAddProvider(f.UI, lc.ID, text.NewInfoLoginWith(cmp.Or(lc.Label, lc.ID), lc.ID), s.ID())\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstIdentification(r *http.Request, f *login.Flow) error {\n\treturn s.populateMethod(r, f, text.NewInfoLoginWith)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/strategy_login_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\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/require\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\nfunc createIdentity(t *testing.T, ctx context.Context, reg driver.Registry, id uuid.UUID, provider string) *identity.Identity {\n\tcreds, err := identity.NewCredentialsOIDC(new(identity.CredentialsOIDCEncryptedTokens), provider, id.String(), \"\")\n\trequire.NoError(t, err)\n\n\ti := identity.NewIdentity(\"default\")\n\ti.SetCredentials(identity.CredentialsTypeOIDC, *creds)\n\n\trequire.NoError(t, reg.IdentityManager().Create(ctx, i))\n\treturn i\n}\n\nfunc TestFormHydration(t *testing.T) {\n\tctx := context.Background()\n\tproviderID := \"test-provider\"\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeOIDC, true)),\n\t\tconfigx.WithValue(config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeOIDC)+\".config.providers\",\n\t\t\t[]map[string]any{{\n\t\t\t\t\"provider\":      \"generic\",\n\t\t\t\t\"id\":            providerID,\n\t\t\t\t\"client_id\":     \"invalid\",\n\t\t\t\t\"client_secret\": \"invalid\",\n\t\t\t\t\"issuer_url\":    \"https://foobar/\",\n\t\t\t\t\"mapper_url\":    \"file://./stub/oidc.facebook.jsonnet\",\n\t\t\t}},\n\t\t),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://stub/stub.schema.json\")),\n\t)\n\n\ts, err := reg.AllLoginStrategies().Strategy(identity.CredentialsTypeOIDC)\n\trequire.NoError(t, err)\n\tfh, ok := s.(login.AAL1FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f *login.Flow) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.UI.Nodes.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f.UI.Nodes)\n\t}\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *login.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := login.NewFlow(reg.Config(), time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactor\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactor(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactorRefresh\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\n\t\tid := createIdentity(t, ctx, reg, x.NewUUID(), providerID)\n\t\tr.Header = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\tf.Refresh = true\n\n\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodSecondFactorRefresh\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstCredentials\", func(t *testing.T) {\n\t\tt.Run(\"case=no options\", func(t *testing.T) {\n\t\t\tr, f := newFlow(ctx, t)\n\t\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactor(r, f))\n\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=WithIdentifier\", func(t *testing.T) {\n\t\t\tr, f := newFlow(ctx, t)\n\t\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactor(r, f))\n\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=WithIdentityHint\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecurityAccountEnumerationMitigate, true)\n\n\t\t\t\tid := identity.NewIdentity(providerID)\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactor(r, f))\n\t\t\t\trequire.NoError(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)))\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecurityAccountEnumerationMitigate, false)\n\n\t\t\t\tt.Run(\"case=identity has oidc\", func(t *testing.T) {\n\t\t\t\t\tidentifier := x.NewUUID()\n\t\t\t\t\tid := createIdentity(t, ctx, reg, identifier, providerID)\n\n\t\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\t\trequire.NoError(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)))\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=identity does not have a oidc\", func(t *testing.T) {\n\t\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstIdentification\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodIdentifierFirstIdentification(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/strategy_registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/continuity\"\n\toidcv1 \"github.com/ory/kratos/gen/oidc/v1\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/fetcher\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nvar (\n\t_ registration.Strategy     = (*Strategy)(nil)\n\t_ registration.FormHydrator = (*Strategy)(nil)\n)\n\nvar jsonnetCache, _ = ristretto.NewCache(&ristretto.Config[[]byte, []byte]{\n\tMaxCost:     100 << 20, // 100MB,\n\tNumCounters: 1_000_000, // 1kB per snippet -> 100k snippets -> 1M counters\n\tBufferItems: 64,\n})\n\ntype MetadataType string\n\ntype VerifiedAddress struct {\n\tValue string `json:\"value\"`\n\tVia   string `json:\"via\"`\n}\n\nconst (\n\tVerifiedAddressesKey = \"identity.verified_addresses\"\n\n\tPublicMetadata MetadataType = \"identity.metadata_public\"\n\tAdminMetadata  MetadataType = \"identity.metadata_admin\"\n)\n\nfunc (s *Strategy) PopulateRegistrationMethod(r *http.Request, f *registration.Flow) error {\n\treturn s.populateMethod(r, f, text.NewInfoRegistrationWith)\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodProfile(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\treturn s.populateMethod(r, f, text.NewInfoRegistrationWith)\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodCredentials(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\treturn s.populateMethod(r, f, text.NewInfoRegistrationWith)\n}\n\n// Update Registration Flow with OpenID Connect Method\n//\n// swagger:model updateRegistrationFlowWithOidcMethod\ntype UpdateRegistrationFlowWithOidcMethod struct {\n\t// The provider to register with\n\t//\n\t// required: true\n\tProvider string `json:\"provider\"`\n\n\t// The CSRF Token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// The identity traits\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// Method to use\n\t//\n\t// This field must be set to `oidc` when using the oidc method.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// UpstreamParameters are the parameters that are passed to the upstream identity provider.\n\t//\n\t// These parameters are optional and depend on what the upstream identity provider supports.\n\t// Supported parameters are:\n\t// - `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\n\t// - `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\n\t// - `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\n\t// - `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\n\t//\n\t// required: false\n\tUpstreamParameters json.RawMessage `json:\"upstream_parameters\"`\n\n\t// IDToken is an optional id token provided by an OIDC provider\n\t//\n\t// If submitted, it is verified using the OIDC provider's public key set and the claims are used to populate\n\t// the OIDC credentials of the identity.\n\t// If the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use\n\t// the `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.\n\t//\n\t// Supported providers are\n\t// - Apple\n\t// - Google\n\t// required: false\n\tIDToken string `json:\"id_token,omitempty\"`\n\n\t// IDTokenNonce is the nonce, used when generating the IDToken.\n\t// If the provider supports nonce validation, the nonce will be validated against this value and is required.\n\t//\n\t// required: false\n\tIDTokenNonce string `json:\"id_token_nonce,omitempty\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) newLinkDecoder(ctx context.Context, p interface{}, r *http.Request, identitySchema *flow.IdentitySchema) error {\n\tds, err := identitySchema.URL(ctx, s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\traw, err := sjson.SetBytes(linkSchema, \"properties.traits.$ref\", ds.String()+\"#/properties/traits\")\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(raw)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif err := decoderx.Decode(r, &p, compiler,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(false),\n\t\tdecoderx.HTTPDecoderUseQueryAndBody(),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registration.Flow, _ *identity.Identity) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.oidc.Strategy.Register\")\n\tdefer otelx.End(span, &err)\n\n\tvar p UpdateRegistrationFlowWithOidcMethod\n\tif err := s.newLinkDecoder(ctx, &p, r, &f.IdentitySchema); err != nil {\n\t\treturn s.HandleError(ctx, w, r, f, \"\", nil, err)\n\t}\n\n\tpid := p.Provider // this can come from both url query and post body\n\tif pid == \"\" {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"provider ID missing\"))\n\t\treturn errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\tf.TransientPayload = p.TransientPayload\n\tf.IDToken = p.IDToken\n\tf.RawIDTokenNonce = p.IDTokenNonce\n\n\tif !strings.EqualFold(strings.ToLower(p.Method), s.SettingsStrategyID()) && p.Method != \"\" {\n\t\t// the user is sending a method that is not oidc, but the payload includes a provider\n\t\ts.d.Logger().\n\t\t\tWithRequest(r).\n\t\t\tWithField(\"provider\", p.Provider).\n\t\t\tWithField(\"method\", p.Method).\n\t\t\tWarn(\"The payload includes a `provider` field but is using a method other than `oidc`. Therefore, social sign in will not be executed.\")\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"method is not oidc\"))\n\t\treturn errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil {\n\t\treturn s.HandleError(ctx, w, r, f, pid, nil, s.handleMethodNotAllowedError(err))\n\t}\n\n\tprovider, err := s.Provider(ctx, pid)\n\tif err != nil {\n\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\n\treq, err := s.validateFlow(ctx, r, f.ID, oidcv1.FlowKind_FLOW_KIND_REGISTRATION)\n\tif err != nil {\n\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\n\tif authenticated, err := s.alreadyAuthenticated(ctx, w, r, req); err != nil {\n\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t} else if authenticated {\n\t\treturn errors.WithStack(registration.ErrAlreadyLoggedIn)\n\t}\n\n\tif p.IDToken != \"\" {\n\t\tclaims, err := s.ProcessIDToken(r, provider, p.IDToken, p.IDTokenNonce)\n\t\tif err != nil {\n\t\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t\t}\n\t\t_, err = s.processRegistration(ctx, w, r, f, nil, claims, provider, &AuthCodeContainer{\n\t\t\tFlowID:           f.ID.String(),\n\t\t\tTraits:           p.Traits,\n\t\t\tTransientPayload: f.TransientPayload,\n\t\t\tIdentitySchema:   f.IdentitySchema,\n\t\t})\n\t\tif errors.Is(err, flow.ErrCompletedByStrategy) {\n\t\t\treturn err\n\t\t} else if err != nil {\n\t\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t\t}\n\t\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n\t}\n\n\tstate, pkce, err := s.GenerateState(ctx, provider, f)\n\tif err != nil {\n\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\tif err := s.d.ContinuityManager().Pause(ctx, w, r, sessionName,\n\t\tcontinuity.WithPayload(&AuthCodeContainer{\n\t\t\tState:            state,\n\t\t\tFlowID:           f.ID.String(),\n\t\t\tTraits:           p.Traits,\n\t\t\tTransientPayload: f.TransientPayload,\n\t\t\tIdentitySchema:   f.IdentitySchema,\n\t\t}),\n\t\tcontinuity.WithLifespan(time.Minute*30)); err != nil {\n\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\n\t// For API/native flows, persist TransientPayload in InternalContext so it\n\t// survives the OIDC redirect. Browser flows restore it from the continuity\n\t// cookie instead, which is not available in native flows because the\n\t// callback comes from a different user agent (system browser/webview).\n\tif f.Type == flow.TypeAPI && len(f.TransientPayload) > 0 {\n\t\tf.EnsureInternalContext()\n\t\tic, err := sjson.SetRawBytes(f.InternalContext, \"transient_payload\", f.TransientPayload)\n\t\tif err != nil {\n\t\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t\t}\n\t\tf.InternalContext = ic\n\t\tif err := s.d.RegistrationFlowPersister().UpdateRegistrationFlow(ctx, f); err != nil {\n\t\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t\t}\n\t}\n\n\tvar up map[string]string\n\tif err := json.NewDecoder(bytes.NewBuffer(p.UpstreamParameters)).Decode(&up); err != nil {\n\t\treturn err\n\t}\n\n\tcodeURL, err := getAuthRedirectURL(ctx, provider, f, state, up, pkce)\n\tif err != nil {\n\t\treturn s.HandleError(ctx, w, r, f, pid, nil, err)\n\t}\n\tif x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(codeURL))\n\t} else {\n\t\thttp.Redirect(w, r, codeURL, http.StatusSeeOther)\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) registrationToLogin(ctx context.Context, w http.ResponseWriter, r *http.Request, rf *registration.Flow) (*login.Flow, error) {\n\t// If return_to was set before, we need to preserve it.\n\tvar opts []login.FlowOption\n\tif len(rf.ReturnTo) > 0 {\n\t\topts = append(opts, login.WithFlowReturnTo(rf.ReturnTo))\n\t}\n\n\tif len(rf.UI.Messages) > 0 {\n\t\topts = append(opts, login.WithFormErrorMessage(rf.UI.Messages))\n\t}\n\n\topts = append(opts, login.WithInternalContext(rf.InternalContext), login.WithIsAccountLinking())\n\tif rf.OAuth2LoginChallenge != \"\" {\n\t\topts = append(opts, login.WithLoginChallenge(rf.OAuth2LoginChallenge.String()))\n\t}\n\n\tlf, _, err := s.d.LoginHandler().NewLoginFlow(w, r, rf.Type, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = s.d.SessionTokenExchangePersister().MoveToNewFlow(ctx, rf.ID, lf.ID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// In this scenario, we are performing account linking. The request URL is set to the \"original\" registration URL\n\t// the user used to try and sign up (which triggered the account linking flow).\n\t//\n\t// In this scenario we want to keep the original request url instead of the current request url, as the current\n\t// request url is an \"in-between\" state where we are half-way through performing account linking.\n\tlf.RequestURL = rf.RequestURL\n\tlf.TransientPayload = rf.TransientPayload\n\tlf.Active = s.ID()\n\tlf.OrganizationID = rf.OrganizationID\n\tlf.IdentitySchema = rf.IdentitySchema\n\n\treturn lf, nil\n}\n\nfunc (s *Strategy) processRegistration(ctx context.Context, w http.ResponseWriter, r *http.Request, rf *registration.Flow, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider, container *AuthCodeContainer) (_ *login.Flow, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.Strategy.processRegistration\")\n\tdefer otelx.End(span, &err)\n\n\tif _, _, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), identity.OIDCUniqueID(provider.Config().ID, claims.Subject)); err == nil {\n\t\t// If the identity already exists, we should perform the login flow instead.\n\n\t\t// That will execute the \"pre registration\" hook which allows to e.g. disallow this flow. The registration\n\t\t// ui however will NOT be shown, instead the user is directly redirected to the auth path. That should then\n\t\t// do a silent re-request. While this might be a bit excessive from a network perspective it should usually\n\t\t// happen without any downsides to user experience as the request has already been authorized and should\n\t\t// not need additional consent/login.\n\n\t\t// This is kinda hacky but the only way to ensure seamless login/registration flows when using OIDC.\n\t\ts.d.Logger().WithRequest(r).WithField(\"Provider\", provider.Config().ID).\n\t\t\tWithField(\"subject\", claims.Subject).\n\t\t\tDebug(\"Received successful OpenID Connect callback but user is already registered. Re-initializing login flow now.\")\n\n\t\tlf, err := s.registrationToLogin(ctx, w, r, rf)\n\t\tif err != nil {\n\t\t\treturn nil, s.HandleError(ctx, w, r, rf, provider.Config().ID, nil, err)\n\t\t}\n\n\t\tif _, err := s.ProcessLogin(ctx, w, r, lf, token, claims, provider, container); err != nil {\n\t\t\treturn lf, s.HandleError(ctx, w, r, rf, provider.Config().ID, nil, err)\n\t\t}\n\n\t\treturn nil, nil\n\t}\n\n\ti, va, err := s.newIdentityFromClaims(ctx, claims, provider, container, rf.IdentitySchema)\n\tif err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, rf, provider.Config().ID, nil, err)\n\t}\n\n\t// Validate the identity itself\n\tif err := s.d.IdentityValidator().Validate(ctx, i); err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, rf, provider.Config().ID, i.Traits, err)\n\t}\n\n\tfor n := range i.VerifiableAddresses {\n\t\tverifiable := &i.VerifiableAddresses[n]\n\t\tfor _, verified := range va {\n\t\t\tif verifiable.Via == verified.Via && verifiable.Value == verified.Value {\n\t\t\t\tverifiable.Status = identity.VerifiableAddressStatusCompleted\n\t\t\t\tverifiable.Verified = true\n\t\t\t\tt := sqlxx.NullTime(time.Now().UTC().Round(time.Second))\n\t\t\t\tverifiable.VerifiedAt = &t\n\t\t\t}\n\t\t}\n\t}\n\n\tcreds, err := identity.NewOIDCLikeCredentials(token, s.ID(), provider.Config().ID, claims.Subject, provider.Config().OrganizationID)\n\tif err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, rf, provider.Config().ID, i.Traits, err)\n\t}\n\n\ti.SetCredentials(s.ID(), *creds)\n\tif err := s.d.RegistrationExecutor().PostRegistrationHook(w, r, s.ID(), provider.Config().ID, provider.Config().OrganizationID, rf, i); err != nil {\n\t\treturn nil, s.HandleError(ctx, w, r, rf, provider.Config().ID, i.Traits, err)\n\t}\n\n\treturn nil, nil\n}\n\nfunc (s *Strategy) newIdentityFromClaims(ctx context.Context, claims *Claims, provider Provider, container *AuthCodeContainer, schema flow.IdentitySchema) (_ *identity.Identity, _ []VerifiedAddress, err error) {\n\tfetch := fetcher.NewFetcher(fetcher.WithClient(s.d.HTTPClient(ctx)), fetcher.WithCache(jsonnetCache, 60*time.Minute))\n\tjsonnetSnippet, err := fetch.FetchContext(ctx, provider.Config().Mapper)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tvar jsonClaims bytes.Buffer\n\tvar evaluated string\n\tif err = json.NewEncoder(&jsonClaims).Encode(claims); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\ttrace.SpanFromContext(ctx).AddEvent(events.NewJsonnetMappingFailed(\n\t\t\t\tctx, err, jsonClaims.Bytes(), evaluated, provider.Config().Provider, s.ID().String(),\n\t\t\t))\n\t\t}\n\t}()\n\n\tvm, err := s.d.JsonnetVM(ctx)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tvm.ExtCode(\"claims\", jsonClaims.String())\n\tevaluated, err = vm.EvaluateAnonymousSnippet(provider.Config().Mapper, jsonnetSnippet.String())\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\ti := identity.NewIdentity(schema.ID(ctx, s.d.Config()))\n\tif err = s.setTraits(provider, container, evaluated, i); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif err = s.setMetadata(evaluated, i, PublicMetadata); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif err = s.setMetadata(evaluated, i, AdminMetadata); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tva, err := s.extractVerifiedAddresses(evaluated)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif orgID, parseErr := uuid.FromString(provider.Config().OrganizationID); parseErr == nil {\n\t\ti.OrganizationID = uuid.NullUUID{UUID: orgID, Valid: true}\n\t}\n\n\ts.d.Logger().\n\t\tWithField(\"oidc_provider\", provider.Config().ID).\n\t\tWithSensitiveField(\"oidc_claims\", claims).\n\t\tWithSensitiveField(\"mapper_jsonnet_output\", evaluated).\n\t\tWithField(\"mapper_jsonnet_url\", provider.Config().Mapper).\n\t\tDebug(\"OpenID Connect Jsonnet mapper completed.\")\n\treturn i, va, nil\n}\n\nfunc (s *Strategy) setTraits(provider Provider, container *AuthCodeContainer, evaluated string, i *identity.Identity) error {\n\tjsonTraits := gjson.Get(evaluated, \"identity.traits\")\n\tif !jsonTraits.IsObject() {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"OpenID Connect Jsonnet mapper did not return an object for key identity.traits. Please check your Jsonnet code!\"))\n\t}\n\n\tif container != nil {\n\t\ttraits, err := merge(container.Traits, json.RawMessage(jsonTraits.Raw))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\ti.Traits = traits\n\t} else {\n\t\ti.Traits = identity.Traits(jsonTraits.Raw)\n\t}\n\ts.d.Logger().\n\t\tWithField(\"oidc_provider\", provider.Config().ID).\n\t\tWithSensitiveField(\"identity_traits\", i.Traits).\n\t\tWithSensitiveField(\"mapper_jsonnet_output\", evaluated).\n\t\tWithField(\"mapper_jsonnet_url\", provider.Config().Mapper).\n\t\tDebug(\"Merged form values and OpenID Connect Jsonnet output.\")\n\treturn nil\n}\n\nfunc (s *Strategy) setMetadata(evaluated string, i *identity.Identity, m MetadataType) error {\n\tif m != PublicMetadata && m != AdminMetadata {\n\t\treturn errors.Errorf(\"undefined metadata type: %s\", m)\n\t}\n\n\tmetadata := gjson.Get(evaluated, string(m))\n\tif metadata.Exists() && !metadata.IsObject() {\n\t\treturn errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"OpenID Connect Jsonnet mapper did not return an object for key %s. Please check your Jsonnet code!\", m))\n\t}\n\n\tswitch m {\n\tcase PublicMetadata:\n\t\ti.MetadataPublic = []byte(metadata.Raw)\n\tcase AdminMetadata:\n\t\ti.MetadataAdmin = []byte(metadata.Raw)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) extractVerifiedAddresses(evaluated string) ([]VerifiedAddress, error) {\n\tif verifiedAddresses := gjson.Get(evaluated, VerifiedAddressesKey); verifiedAddresses.Exists() {\n\t\tif !verifiedAddresses.IsArray() {\n\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"OpenID Connect Jsonnet mapper did not return an array for key %s. Please check your Jsonnet code!\", VerifiedAddressesKey))\n\t\t}\n\n\t\tvar va []VerifiedAddress\n\t\tif err := json.Unmarshal([]byte(verifiedAddresses.Raw), &va); err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Failed to unmarshal value for key %s. Please check your Jsonnet code!\", VerifiedAddressesKey).WithDebugf(\"%s\", err))\n\t\t}\n\n\t\tfor i := range va {\n\t\t\tva := &va[i]\n\t\t\tif va.Via == identity.AddressTypeEmail {\n\t\t\t\tva.Value = strings.ToLower(strings.TrimSpace(va.Value))\n\t\t\t}\n\t\t}\n\n\t\treturn va, nil\n\t}\n\n\treturn nil, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/strategy_registration_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\nfunc TestPopulateRegistrationMethod(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://stub/registration.schema.json\")),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeOIDC, true)),\n\t\tconfigx.WithValue(config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeOIDC)+\".config.providers\",\n\t\t\t[]map[string]any{{\n\t\t\t\t\"provider\":      \"generic\",\n\t\t\t\t\"id\":            \"providerID\",\n\t\t\t\t\"client_id\":     \"invalid\",\n\t\t\t\t\"client_secret\": \"invalid\",\n\t\t\t\t\"issuer_url\":    \"https://foobar/\",\n\t\t\t\t\"mapper_url\":    \"file://./stub/oidc.facebook.jsonnet\",\n\t\t\t}}),\n\t)\n\n\ts, err := reg.AllRegistrationStrategies().Strategy(identity.CredentialsTypeOIDC)\n\trequire.NoError(t, err)\n\n\tfh, ok := s.(registration.FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f node.Nodes) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f, snapshotx.ExceptNestedKeys(\"nonce\", \"src\"))\n\t}\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *registration.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/registration/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := registration.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateRegistrationMethod\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethod(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodProfile\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodCredentials\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=idempotency\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\n\t\tvar snapshots []node.Nodes\n\n\t\tt.Run(\"case=1\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=2\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=3\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=4\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=evaluate\", func(t *testing.T) {\n\t\t\tassertx.EqualAsJSON(t, snapshots[0], snapshots[2])\n\t\t\tassertx.EqualAsJSON(t, snapshots[1], snapshots[3])\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/strategy_settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"bytes\"\n\t\"cmp\"\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/sjson\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/continuity\"\n\toidcv1 \"github.com/ory/kratos/gen/oidc/v1\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/strategy\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n)\n\n//go:embed .schema/settings.schema.json\nvar settingsSchema []byte\n\nvar (\n\t_                                settings.Strategy = new(Strategy)\n\tUnknownConnectionValidationError                   = &jsonschema.ValidationError{\n\t\tMessage: \"can not unlink non-existing OpenID Connect connection\", InstancePtr: \"#/\",\n\t}\n)\n\nvar ConnectionExistValidationError = &jsonschema.ValidationError{\n\tMessage: \"can not link unknown or already existing OpenID Connect connection\", InstancePtr: \"#/\",\n}\n\nvar UnlinkAllFirstFactorConnectionsError = &jsonschema.ValidationError{\n\tMessage: \"can not unlink OpenID Connect connection because it is the last remaining first factor credential\", InstancePtr: \"#/\",\n}\n\nfunc (s *Strategy) SettingsStrategyID() string {\n\treturn s.ID().String()\n}\n\nfunc (s *Strategy) decoderSettings(ctx context.Context, p *updateSettingsFlowWithOidcMethod, r *http.Request, settingsFlow *settings.Flow) error {\n\tschema := flow.IdentitySchema(settingsFlow.Identity.SchemaID)\n\tds, err := schema.URL(ctx, s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\traw, err := sjson.SetBytes(settingsSchema,\n\t\t\"properties.traits.$ref\", ds.String()+\"#/properties/traits\")\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(raw)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif err := decoderx.Decode(r, &p, compiler,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderUseQueryAndBody(),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(false),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) linkedProviders(conf *ConfigurationCollection, confidential *identity.Identity) ([]Provider, error) {\n\tcreds, ok := confidential.GetCredentials(s.ID())\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\n\tvar available identity.CredentialsOIDC\n\tif err := json.Unmarshal(creds.Config, &available); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tvar result []Provider\n\tfor _, p := range available.Providers {\n\t\tprov, err := conf.Provider(p.Provider, s.d)\n\t\tif errors.Is(err, herodot.ErrNotFound) {\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresult = append(result, prov)\n\t}\n\n\treturn result, nil\n}\n\nfunc (s *Strategy) linkableProviders(conf *ConfigurationCollection, confidential *identity.Identity) ([]Provider, error) {\n\tvar available identity.CredentialsOIDC\n\tcreds, ok := confidential.GetCredentials(s.ID())\n\tif ok {\n\t\tif err := json.Unmarshal(creds.Config, &available); err != nil {\n\t\t\treturn nil, errors.WithStack(err)\n\t\t}\n\t}\n\n\tvar result []Provider\n\tfor _, p := range conf.Providers {\n\t\tvar found bool\n\t\tfor _, pp := range available.Providers {\n\t\t\tif pp.Provider == p.ID {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif !found {\n\t\t\tprov, err := conf.Provider(p.ID, s.d)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tresult = append(result, prov)\n\t\t}\n\t}\n\n\treturn result, nil\n}\n\nfunc (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, sr *settings.Flow) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.Strategy.PopulateSettingsMethod\")\n\tdefer otelx.End(span, &err)\n\n\tconf, err := s.Config(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tlinkable, err := s.linkableProviders(conf, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tlinked, err := s.linkedProviders(conf, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsr.UI.GetNodes().Remove(\"unlink\", \"link\")\n\tsr.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tfor _, l := range linkable {\n\t\t// We do not want to offer to link SSO providers in the settings.\n\t\tif l.Config().OrganizationID != \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tsr.UI.GetNodes().Append(NewLinkNode(l.Config().ID, cmp.Or(l.Config().Label, l.Config().ID)))\n\t}\n\n\tcount, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif count > 1 {\n\t\t// This means that we're able to remove a connection because it is the last configured credential. If it is\n\t\t// removed, the identity is no longer able to sign in.\n\t\tfor _, l := range linked {\n\t\t\tsr.UI.GetNodes().Append(NewUnlinkNode(l.Config().ID, cmp.Or(l.Config().Label, l.Config().ID)))\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Update Settings Flow with OpenID Connect Method\n//\n// swagger:model updateSettingsFlowWithOidcMethod\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateSettingsFlowWithOidcMethod struct {\n\t// Method\n\t//\n\t// Should be set to profile when trying to update a profile.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Link this provider\n\t//\n\t// Either this or `unlink` must be set.\n\t//\n\t// type: string\n\t// in: body\n\tLink string `json:\"link\"`\n\n\t// Unlink this provider\n\t//\n\t// Either this or `link` must be set.\n\t//\n\t// type: string\n\t// in: body\n\tUnlink string `json:\"unlink\"`\n\n\t// Flow ID is the flow's ID.\n\t//\n\t// in: query\n\tFlowID string `json:\"flow\"`\n\n\t// The identity's traits\n\t//\n\t// in: body\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// UpstreamParameters are the parameters that are passed to the upstream identity provider.\n\t//\n\t// These parameters are optional and depend on what the upstream identity provider supports.\n\t// Supported parameters are:\n\t// - `login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\n\t// - `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\n\t// - `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\n\t// - `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\n\t//\n\t// required: false\n\tUpstreamParameters json.RawMessage `json:\"upstream_parameters\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (p *updateSettingsFlowWithOidcMethod) GetFlowID() uuid.UUID {\n\treturn x.ParseUUID(p.FlowID)\n}\n\nfunc (p *updateSettingsFlowWithOidcMethod) SetFlowID(rid uuid.UUID) {\n\tp.FlowID = rid.String()\n}\n\nfunc (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.Strategy.Settings\")\n\tdefer otelx.End(span, &err)\n\n\tvar p updateSettingsFlowWithOidcMethod\n\tif err := s.decoderSettings(ctx, &p, r, f); err != nil {\n\t\treturn nil, err\n\t}\n\tf.TransientPayload = p.TransientPayload\n\n\tctxUpdate, err := settings.PrepareUpdate(s.d, w, r, f, ss, settings.ContinuityKey(s.SettingsStrategyID()), &p)\n\tif errors.Is(err, settings.ErrContinuePreviousAction) {\n\t\tif !s.d.Config().SelfServiceStrategy(ctx, s.SettingsStrategyID()).Enabled {\n\t\t\treturn nil, s.handleMethodNotAllowedError(errors.WithStack(herodot.ErrNotFound.WithReason(strategy.EndpointDisabledMessage)))\n\t\t}\n\n\t\tif len(p.Link) > 0 {\n\t\t\tif err := s.initLinkProvider(ctx, w, r, ctxUpdate, &p); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\treturn ctxUpdate, nil\n\t\t} else if len(p.Unlink) > 0 {\n\t\t\tif err := s.unlinkProvider(ctx, w, r, ctxUpdate, &p); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\treturn ctxUpdate, nil\n\t\t}\n\n\t\treturn nil, s.handleSettingsError(ctx, w, r, ctxUpdate, &p, errors.WithStack(herodot.ErrBadRequest.WithReason(\"Expected either link or unlink to be set when continuing flow but both are unset.\")))\n\t} else if err != nil {\n\t\treturn nil, s.handleSettingsError(ctx, w, r, ctxUpdate, &p, err)\n\t}\n\n\tif len(p.Link)+len(p.Unlink) == 0 {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"neither link nor unlink set\"))\n\t\treturn nil, errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\tif !s.d.Config().SelfServiceStrategy(ctx, s.SettingsStrategyID()).Enabled {\n\t\treturn nil, s.handleMethodNotAllowedError(errors.WithStack(herodot.ErrNotFound.WithReason(strategy.EndpointDisabledMessage)))\n\t}\n\n\tswitch l, u := len(p.Link), len(p.Unlink); {\n\tcase l > 0 && u > 0:\n\t\treturn nil, s.handleSettingsError(ctx, w, r, ctxUpdate, &p, errors.WithStack(&jsonschema.ValidationError{\n\t\t\tMessage:     \"it is not possible to link and unlink providers in the same request\",\n\t\t\tInstancePtr: \"#/\",\n\t\t}))\n\tcase l > 0:\n\t\tif err := s.initLinkProvider(ctx, w, r, ctxUpdate, &p); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn ctxUpdate, nil\n\tcase u > 0:\n\t\tif err := s.unlinkProvider(ctx, w, r, ctxUpdate, &p); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn ctxUpdate, nil\n\t}\n\t// this case should never be reached as we previously checked whether link and unlink are both empty\n\treturn nil, errors.WithStack(flow.ErrStrategyNotResponsible)\n}\n\nfunc (s *Strategy) isLinkable(ctx context.Context, ctxUpdate *settings.UpdateContext, toLink string) (*identity.Identity, error) {\n\tproviders, err := s.Config(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.Identity.ID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlinkable, err := s.linkableProviders(providers, i)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar found bool\n\tfor _, available := range linkable {\n\t\tif toLink == available.Config().ID {\n\t\t\tfound = true\n\t\t}\n\t}\n\n\tif !found {\n\t\treturn nil, errors.WithStack(ConnectionExistValidationError)\n\t}\n\n\treturn i, nil\n}\n\nfunc (s *Strategy) initLinkProvider(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *updateSettingsFlowWithOidcMethod) error {\n\tif _, err := s.isLinkable(ctx, ctxUpdate, p.Link); err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(settings.NewFlowNeedsReAuth()))\n\t}\n\n\tprovider, err := s.Provider(ctx, p.Link)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\treq, err := s.validateFlow(ctx, r, ctxUpdate.Flow.ID, oidcv1.FlowKind_FLOW_KIND_SETTINGS)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tstate, pkce, err := s.GenerateState(ctx, provider, ctxUpdate.Flow)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\tif err := s.d.ContinuityManager().Pause(ctx, w, r, sessionName,\n\t\tcontinuity.WithPayload(&AuthCodeContainer{\n\t\t\tState:  state,\n\t\t\tFlowID: ctxUpdate.Flow.ID.String(),\n\t\t\tTraits: p.Traits,\n\t\t}),\n\t\tcontinuity.WithLifespan(time.Minute*30)); err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tvar up map[string]string\n\tif err := json.NewDecoder(bytes.NewBuffer(p.UpstreamParameters)).Decode(&up); err != nil {\n\t\treturn err\n\t}\n\n\tcodeURL, err := getAuthRedirectURL(ctx, provider, req, state, up, pkce)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(codeURL))\n\t} else {\n\t\thttp.Redirect(w, r, codeURL, http.StatusSeeOther)\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) linkProvider(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, token *identity.CredentialsOIDCEncryptedTokens, claims *Claims, provider Provider) error {\n\tp := &updateSettingsFlowWithOidcMethod{\n\t\tLink: provider.Config().ID, FlowID: ctxUpdate.Flow.ID.String(),\n\t}\n\n\tif ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(settings.NewFlowNeedsReAuth()))\n\t}\n\n\ti, err := s.isLinkable(ctx, ctxUpdate, p.Link)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif err := s.linkCredentials(ctx, i, token, provider.Config().ID, claims.Subject, provider.Config().OrganizationID); err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\t// Add authentication method to session after\n\t// linking with 3rd party OIDC provider\n\tif err := s.d.SessionManager().SessionAddAuthenticationMethods(\n\t\tctx,\n\t\tctxUpdate.Session.ID,\n\t\tsession.AuthenticationMethod{\n\t\t\tMethod:       s.ID(),\n\t\t\tAAL:          identity.AuthenticatorAssuranceLevel1,\n\t\t\tProvider:     provider.Config().ID,\n\t\t\tOrganization: provider.Config().OrganizationID,\n\t\t}); err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif err := s.d.SettingsHookExecutor().PostSettingsHook(ctx, w, r, s.SettingsStrategyID(), ctxUpdate, i, settings.WithCallback(func(ctxUpdate *settings.UpdateContext) error {\n\t\t// Credential population is done by PostSettingsHook on ctxUpdate.Session.Identity\n\t\treturn s.PopulateSettingsMethod(ctx, r, ctxUpdate.Session.Identity, ctxUpdate.Flow)\n\t})); err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) unlinkProvider(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *updateSettingsFlowWithOidcMethod) error {\n\tif ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(settings.NewFlowNeedsReAuth()))\n\t}\n\n\tproviders, err := s.Config(ctx)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.Identity.ID)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tavailableProviders, err := s.linkedProviders(providers, i)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tvar cc identity.CredentialsOIDC\n\tcreds, err := i.ParseCredentials(s.ID(), &cc)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tcount, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, i)\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif count < 2 {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(UnlinkAllFirstFactorConnectionsError))\n\t}\n\n\tvar found bool\n\tvar updatedProviders []identity.CredentialsOIDCProvider\n\tvar updatedIdentifiers []string\n\tfor _, available := range availableProviders {\n\t\tif p.Unlink == available.Config().ID {\n\t\t\tfor _, link := range cc.Providers {\n\t\t\t\tif link.Provider != p.Unlink {\n\t\t\t\t\tupdatedIdentifiers = append(updatedIdentifiers, identity.OIDCUniqueID(link.Provider, link.Subject))\n\t\t\t\t\tupdatedProviders = append(updatedProviders, link)\n\t\t\t\t} else {\n\t\t\t\t\tfound = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif !found {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(UnknownConnectionValidationError))\n\t}\n\n\tcreds.Identifiers = updatedIdentifiers\n\tcreds.Config, err = json.Marshal(&identity.CredentialsOIDC{Providers: updatedProviders})\n\tif err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(err))\n\t}\n\n\ti.Credentials[s.ID()] = *creds\n\tif err := s.d.SettingsHookExecutor().PostSettingsHook(ctx, w, r, s.SettingsStrategyID(), ctxUpdate, i, settings.WithCallback(func(ctxUpdate *settings.UpdateContext) error {\n\t\t// Credential population is done by PostSettingsHook on ctxUpdate.Session.Identity\n\t\treturn s.PopulateSettingsMethod(ctx, r, ctxUpdate.Session.Identity, ctxUpdate.Flow)\n\t})); err != nil {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\treturn errors.WithStack(flow.ErrCompletedByStrategy)\n}\n\nfunc (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *updateSettingsFlowWithOidcMethod, err error) error {\n\t// Do not pause flow if the flow type is an API flow as we can't save cookies in those flows.\n\tif e := new(settings.FlowNeedsReAuth); errors.As(err, &e) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser {\n\t\tif err := s.d.ContinuityManager().Pause(ctx, w, r,\n\t\t\tsettings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.Session.Identity)...); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif ctxUpdate.Flow != nil {\n\t\tctxUpdate.Flow.UI.ResetMessages()\n\t\tctxUpdate.Flow.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t}\n\n\treturn err\n}\n\nfunc (s *Strategy) Link(ctx context.Context, i *identity.Identity, credentialsConfig sqlxx.JSONRawMessage) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.oidc.Strategy.Link\")\n\tdefer otelx.End(span, &err)\n\n\tvar credentialsOIDCConfig identity.CredentialsOIDC\n\tif err := json.Unmarshal(credentialsConfig, &credentialsOIDCConfig); err != nil {\n\t\treturn err\n\t}\n\tif len(credentialsOIDCConfig.Providers) != 1 {\n\t\treturn errors.New(\"no oidc provider was set\")\n\t}\n\tcredentialsOIDCProvider := credentialsOIDCConfig.Providers[0]\n\n\tif err := s.linkCredentials(\n\t\tctx,\n\t\ti,\n\t\t// The tokens in this credential are coming from the existing identity. Hence, the values are already encrypted.\n\t\tcredentialsOIDCProvider.GetTokens(),\n\t\tcredentialsOIDCProvider.Provider,\n\t\tcredentialsOIDCProvider.Subject,\n\t\tcredentialsOIDCProvider.Organization,\n\t); err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.d.IdentityManager().Update(ctx, i, identity.ManagerAllowWriteProtectedTraits); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) CompletedLogin(sess *session.Session, data *flow.DuplicateCredentialsData) error {\n\tvar credentialsOIDCConfig identity.CredentialsOIDC\n\tif err := json.Unmarshal(data.CredentialsConfig, &credentialsOIDCConfig); err != nil {\n\t\treturn err\n\t}\n\tif len(credentialsOIDCConfig.Providers) != 1 {\n\t\treturn errors.New(\"no oidc provider was set\")\n\t}\n\tcredentialsOIDCProvider := credentialsOIDCConfig.Providers[0]\n\n\tsess.CompletedLoginForWithProvider(\n\t\ts.ID(),\n\t\tidentity.AuthenticatorAssuranceLevel1,\n\t\tcredentialsOIDCProvider.Provider,\n\t\tcredentialsOIDCProvider.Organization,\n\t)\n\n\treturn nil\n}\n\nfunc (s *Strategy) SetDuplicateCredentials(f flow.InternalContexter, duplicateIdentifier string, credentials identity.Credentials, provider string) error {\n\tvar credentialsOIDCConfig identity.CredentialsOIDC\n\tif err := json.Unmarshal(credentials.Config, &credentialsOIDCConfig); err != nil {\n\t\treturn err\n\t}\n\n\t// We want to only set the provider in the credentials config that was used to authenticate the user.\n\tfor _, p := range credentialsOIDCConfig.Providers {\n\t\tif p.Provider == provider {\n\t\t\tcredentialsOIDCConfig.Providers = []identity.CredentialsOIDCProvider{p}\n\t\t\tconfig, err := json.Marshal(credentialsOIDCConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn flow.SetDuplicateCredentials(f, flow.DuplicateCredentialsData{\n\t\t\t\tCredentialsType:     s.ID(),\n\t\t\t\tCredentialsConfig:   config,\n\t\t\t\tDuplicateIdentifier: duplicateIdentifier,\n\t\t\t})\n\t\t}\n\t}\n\treturn fmt.Errorf(\"provider %q not found in credentials\", provider)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/strategy_settings_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"slices\"\n\t\"strconv\"\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\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/corpx\"\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc TestSettingsStrategy(t *testing.T) {\n\tt.Parallel()\n\n\tnormalPrivilegedSessionFor := 5 * time.Minute\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/settings.schema.json\")),\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceBrowserDefaultReturnTo:                \"https://www.ory.sh/kratos\",\n\t\t\tconfig.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter: normalPrivilegedSessionFor,\n\t\t}),\n\t)\n\n\tvar (\n\t\tsubject string\n\t\tclaims  idTokenClaims\n\t\tscope   []string\n\t)\n\tremoteAdmin, remotePublic, _ := newHydra(t, &subject, &claims, &scope)\n\tuiTS := newUI(t, reg)\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\tviperSetProviderConfig(\n\t\tt,\n\t\tconf,\n\t\tnewOIDCProvider(t, publicTS, remotePublic, remoteAdmin, \"ory\", func(c *oidc.Configuration) {\n\t\t\tc.Label = \"Ory\"\n\t\t}),\n\t\tnewOIDCProvider(t, publicTS, remotePublic, remoteAdmin, \"ory-sso\", func(c *oidc.Configuration) {\n\t\t\tc.OrganizationID = \"org-1\"\n\t\t}),\n\t\tnewOIDCProvider(t, publicTS, remotePublic, remoteAdmin, \"google\"),\n\t\tnewOIDCProvider(t, publicTS, remotePublic, remoteAdmin, \"github\"),\n\t)\n\n\ttype userDataFunc = func() (string, *identity.Identity)\n\tpasswordIdentity := func() (string, *identity.Identity) {\n\t\te := testhelpers.RandomEmail()\n\t\treturn e, &identity.Identity{\n\t\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, e)),\n\t\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\"password\": {\n\t\t\t\t\tType:        \"password\",\n\t\t\t\t\tIdentifiers: []string{e},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$argon2id$iammocked....\"}`),\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t}\n\tsingleOIDCIdentity := func() (string, *identity.Identity) {\n\t\te := testhelpers.RandomEmail()\n\t\treturn e, &identity.Identity{\n\t\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, e)),\n\t\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{\"ory:\" + e},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(fmt.Sprintf(`{\"providers\":[{\"provider\":\"ory\",\"subject\":\"%s\"}]}`, e)),\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t}\n\tmultiOIDCIdentity := func() (string, *identity.Identity) {\n\t\te := testhelpers.RandomEmail()\n\t\treturn e, &identity.Identity{\n\t\t\tTraits: identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, e)),\n\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{\"ory:\" + e, \"github:\" + e},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(fmt.Sprintf(`{\"providers\":[{\"provider\":\"ory\",\"subject\":\"%s\"},{\"provider\":\"github\",\"subject\":\"%s\"}]}`, e, e)),\n\t\t\t\t},\n\t\t\t},\n\t\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\t}\n\t}\n\tmultiCredentialIdentity := func() (string, *identity.Identity) {\n\t\te := testhelpers.RandomEmail()\n\t\treturn e, &identity.Identity{\n\t\t\tTraits: identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, e)),\n\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\"password\": {\n\t\t\t\t\tType:        \"password\",\n\t\t\t\t\tIdentifiers: []string{e},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"$argon2id$iammocked....\"}`),\n\t\t\t\t},\n\t\t\t\tidentity.CredentialsTypeOIDC: {\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{\"ory:\" + e, \"google:\" + e},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(fmt.Sprintf(`{\"providers\":[{\"provider\":\"ory\",\"subject\":\"%s\"},{\"provider\":\"google\",\"subject\":\"%s\"}]}`, e, e)),\n\t\t\t\t},\n\t\t\t},\n\t\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t\t}\n\t}\n\n\tnewProfileFlow := func(t *testing.T, client *http.Client, redirectTo string, exp time.Duration) *settings.Flow {\n\t\treq, err := reg.SettingsFlowPersister().GetSettingsFlow(t.Context(),\n\t\t\tx.ParseUUID(testhelpers.InitializeSettingsFlowViaBrowser(t, client, false, publicTS).Id))\n\t\trequire.NoError(t, err)\n\t\tassert.Empty(t, req.Active)\n\n\t\tif redirectTo != \"\" {\n\t\t\treq.RequestURL = redirectTo\n\t\t}\n\t\treq.ExpiresAt = time.Now().Add(exp)\n\t\trequire.NoError(t, reg.SettingsFlowPersister().UpdateSettingsFlow(t.Context(), req))\n\n\t\t// sanity check\n\t\tgot, err := reg.SettingsFlowPersister().GetSettingsFlow(t.Context(), req.ID)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, got.UI.Nodes, len(req.UI.Nodes))\n\n\t\treturn req\n\t}\n\n\t// does the same as new profile request but uses the SDK\n\tnprSDK := func(t *testing.T, client *http.Client) *kratos.SettingsFlow {\n\t\treturn testhelpers.InitializeSettingsFlowViaBrowser(t, client, false, publicTS)\n\t}\n\n\t_, sharedPasswordUser := passwordIdentity()\n\tsharedUserID, sharedUserClient := testhelpers.AddAndLoginIdentity(t, reg, sharedPasswordUser)\n\n\tt.Run(\"case=should not be able to continue a flow with a malformed ID\", func(t *testing.T) {\n\t\tbody, res := testhelpers.HTTPPostForm(t, sharedUserClient, publicTS.URL+settings.RouteSubmitFlow+\"?flow=i-am-not-a-uuid\", nil)\n\t\tAssertSystemError(t, errTS, res, body, 400, \"malformed\")\n\t})\n\n\tt.Run(\"case=should not be able to continue a flow without the flow query parameter\", func(t *testing.T) {\n\t\tbody, res := testhelpers.HTTPPostForm(t, sharedUserClient, publicTS.URL+settings.RouteSubmitFlow, nil)\n\t\tAssertSystemError(t, errTS, res, body, 400, \"query parameter is missing\")\n\t})\n\n\tt.Run(\"case=should not be able to continue a flow with a non-existing ID\", func(t *testing.T) {\n\t\tbody, res := testhelpers.HTTPPostForm(t, sharedUserClient, publicTS.URL+settings.RouteSubmitFlow+\"?flow=\"+x.NewUUID().String(), nil)\n\t\tAssertSystemError(t, errTS, res, body, 404, \"not be found\")\n\t})\n\n\tt.Run(\"case=should not be able to continue a flow that is expired\", func(t *testing.T) {\n\t\treq := newProfileFlow(t, sharedUserClient, \"\", -time.Hour)\n\t\tbody, res := testhelpers.HTTPPostForm(t, sharedUserClient, publicTS.URL+settings.RouteSubmitFlow+\"?flow=\"+req.ID.String(), nil)\n\n\t\trequire.Contains(t, res.Request.URL.String(), uiTS.URL+\"/settings?flow=\")\n\t\tassert.NotContains(t, res.Request.URL.String(), req.ID.String(), \"should initialize a new flow\")\n\t\tassert.Contains(t, gjson.GetBytes(body, `ui.messages.0.text`).String(), \"expired\")\n\t})\n\n\tt.Run(\"case=should not be able to fetch another user's data\", func(t *testing.T) {\n\t\t_, otherUserData := passwordIdentity()\n\t\t_, otherUserClient := testhelpers.AddAndLoginIdentity(t, reg, otherUserData)\n\n\t\treq := newProfileFlow(t, sharedUserClient, \"\", time.Hour)\n\n\t\t_, _, err := testhelpers.NewSDKCustomClient(publicTS, otherUserClient).FrontendAPI.GetSettingsFlow(t.Context()).Id(req.ID.String()).Execute()\n\t\trequire.Error(t, err)\n\t\tassert.Contains(t, err.Error(), \"403\")\n\t})\n\n\tt.Run(\"case=should fetch the settings request and expect data to be set appropriately\", func(t *testing.T) {\n\t\treq := newProfileFlow(t, sharedUserClient, \"\", time.Hour)\n\n\t\trs, _, err := testhelpers.NewSDKCustomClient(publicTS, sharedUserClient).FrontendAPI.GetSettingsFlow(t.Context()).Id(req.ID.String()).Execute()\n\t\trequire.NoError(t, err)\n\n\t\t// Check our sanity. Does the SDK relay the same info that we expect and got from the store?\n\t\tassert.Equal(t, publicTS.URL+\"/self-service/settings/browser\", req.RequestURL)\n\t\tassert.Empty(t, req.Active)\n\t\tassert.NotEmpty(t, req.IssuedAt)\n\t\tassert.EqualValues(t, sharedUserID, req.Identity.ID)\n\t\tassert.EqualValues(t, sharedPasswordUser.Traits, req.Identity.Traits)\n\t\tassert.EqualValues(t, sharedPasswordUser.SchemaID, req.Identity.SchemaID)\n\n\t\tassert.EqualValues(t, req.ID.String(), rs.Id)\n\t\tassert.EqualValues(t, req.RequestURL, rs.RequestUrl)\n\t\tassert.EqualValues(t, req.Identity.ID.String(), rs.Identity.Id)\n\t\tassert.EqualValues(t, req.IssuedAt, rs.IssuedAt)\n\n\t\trequire.NotNil(t, identity.CredentialsTypeOIDC.String(), rs.Ui)\n\t\trequire.EqualValues(t, \"POST\", rs.Ui.Method)\n\t\trequire.EqualValues(t, publicTS.URL+settings.RouteSubmitFlow+\"?flow=\"+req.ID.String(),\n\t\t\trs.Ui.Action)\n\t})\n\n\tt.Run(\"case=should adjust linkable providers based on linked credentials\", func(t *testing.T) {\n\t\t_, pw := passwordIdentity()\n\t\t_, ory := singleOIDCIdentity()\n\t\t_, gh := multiOIDCIdentity()\n\t\t_, multi := multiCredentialIdentity()\n\t\tuserData := map[string]*identity.Identity{\n\t\t\t\"password\":  pw,\n\t\t\t\"oryer\":     ory,\n\t\t\t\"githuber\":  gh,\n\t\t\t\"multiuser\": multi,\n\t\t}\n\t\tusers := testhelpers.AddAndLoginIdentities(t, reg, userData)\n\t\tfor name, user := range users {\n\t\t\tt.Run(\"agent=\"+name, func(t *testing.T) {\n\t\t\t\trs := nprSDK(t, user.Client)\n\t\t\t\tsnapshotx.SnapshotT(t, rs.Ui.Nodes, snapshotx.ExceptPaths(\"0.attributes.value\", \"1.attributes.value\"))\n\t\t\t})\n\t\t}\n\t})\n\n\taction := func(req *kratos.SettingsFlow) string {\n\t\treturn req.Ui.Action\n\t}\n\n\tcheckCredentials := func(t *testing.T, shouldExist bool, iid uuid.UUID, provider, subject string, expectTokens bool) {\n\t\tactual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), iid)\n\t\trequire.NoError(t, err)\n\n\t\tvar cc identity.CredentialsOIDC\n\t\tcreds, err := actual.ParseCredentials(identity.CredentialsTypeOIDC, &cc)\n\t\trequire.NoError(t, err)\n\n\t\tif shouldExist {\n\t\t\tassert.Contains(t, creds.Identifiers, provider+\":\"+subject)\n\t\t} else {\n\t\t\tassert.NotContains(t, creds.Identifiers, provider+\":\"+subject)\n\t\t}\n\n\t\tvar found bool\n\t\tfor _, p := range cc.Providers {\n\t\t\tif p.Provider == provider && p.Subject == subject {\n\t\t\t\tfound = true\n\t\t\t\tif expectTokens {\n\t\t\t\t\tassert.NotEmpty(t, p.InitialIDToken)\n\t\t\t\t\tassert.NotEmpty(t, p.InitialAccessToken)\n\t\t\t\t\tassert.NotEmpty(t, p.InitialRefreshToken)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\trequire.EqualValues(t, shouldExist, found)\n\t}\n\n\tt.Run(\"suite=unlink\", func(t *testing.T) {\n\t\tunlink := func(t *testing.T, agent *http.Client, provider string) (body []byte, res *http.Response, req *kratos.SettingsFlow) {\n\t\t\treq = nprSDK(t, agent)\n\t\t\tbody, res = testhelpers.HTTPPostForm(t, agent, action(req),\n\t\t\t\t&url.Values{\"csrf_token\": {nosurfx.FakeCSRFToken}, \"unlink\": {provider}})\n\t\t\treturn\n\t\t}\n\n\t\tunlinkInvalid := func(userData userDataFunc, provider, errorMessage string) func(t *testing.T) {\n\t\t\treturn func(t *testing.T) {\n\t\t\t\t_, identityData := userData()\n\t\t\t\t_, client := testhelpers.AddAndLoginIdentity(t, reg, identityData)\n\n\t\t\t\tbody, res, req := unlink(t, client, provider)\n\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/settings?flow=\"+req.Id)\n\n\t\t\t\t// assert.EqualValues(t, identity.CredentialsTypeOIDC.String(), gjson.GetBytes(body, \"active\").String())\n\n\t\t\t\t// The original options to link google and github are still there\n\t\t\t\tt.Run(\"flow=fetch\", func(t *testing.T) {\n\t\t\t\t\tsnapshotx.SnapshotT(t, req.Ui.Nodes, snapshotx.ExceptPaths(\"0.attributes.value\", \"1.attributes.value\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"flow=json\", func(t *testing.T) {\n\t\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.GetBytes(body, `ui.nodes`).Raw), snapshotx.ExceptPaths(\"0.attributes.value\", \"1.attributes.value\"))\n\t\t\t\t})\n\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), publicTS.URL+settings.RouteSubmitFlow+\"?flow=\")\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, `ui.messages.0.text`).String(), errorMessage)\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"case=should not be able to unlink the last remaining connection\",\n\t\t\tunlinkInvalid(singleOIDCIdentity, \"ory\", \"can not unlink OpenID Connect connection because it is the last remaining first factor credential\"))\n\n\t\tt.Run(\"case=should not be able to unlink an non-existing connection\",\n\t\t\tunlinkInvalid(multiOIDCIdentity, \"i-do-not-exist\", \"can not unlink non-existing OpenID Connect connection\"))\n\n\t\tt.Run(\"case=should not be able to unlink a connection not yet linked\",\n\t\t\tunlinkInvalid(multiOIDCIdentity, \"google\", \"can not unlink non-existing OpenID Connect connection\"))\n\n\t\tt.Run(\"case=should unlink a connection\", func(t *testing.T) {\n\t\t\temail, userData := multiOIDCIdentity()\n\t\t\t// only keep the relevant identity\n\t\t\tuserID, client := testhelpers.AddAndLoginIdentity(t, reg, userData)\n\t\t\tprovider := \"github\"\n\n\t\t\tbody, res, req := unlink(t, client, provider)\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/settings?flow=\"+req.Id)\n\t\t\trequire.Equalf(t, \"success\", gjson.GetBytes(body, \"state\").String(), \"%s\", body)\n\n\t\t\tcheckCredentials(t, false, userID, provider, email, false)\n\t\t\tcheckCredentials(t, true, userID, \"ory\", email, false)\n\t\t})\n\n\t\tt.Run(\"case=should not be able to unlink a connection without a privileged session\", func(t *testing.T) {\n\t\t\tuserEmail, userData := multiOIDCIdentity()\n\t\t\tuserID, client := testhelpers.AddAndLoginIdentity(t, reg, userData)\n\t\t\tprovider := \"github\"\n\n\t\t\trunUnauthed := func(t *testing.T) *kratos.SettingsFlow {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, time.Millisecond)\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, normalPrivilegedSessionFor)\n\t\t\t\t})\n\t\t\t\t_, res, req := unlink(t, client, provider)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login\")\n\n\t\t\t\tfa := testhelpers.NewSDKCustomClient(publicTS, client).FrontendAPI\n\t\t\t\tlf, _, err := fa.GetLoginFlow(t.Context()).Id(res.Request.URL.Query()[\"flow\"][0]).Execute()\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tfor _, n := range lf.Ui.Nodes {\n\t\t\t\t\tif n.Group == \"oidc\" && n.Attributes.UiNodeInputAttributes.Name == \"provider\" {\n\t\t\t\t\t\tassert.Contains(t, []string{\"ory\", \"github\"}, n.Attributes.UiNodeInputAttributes.Value)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trs, _, err := fa.GetSettingsFlow(t.Context()).Id(req.Id).Execute()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.EqualValues(t, flow.StateShowForm, rs.State)\n\n\t\t\t\tcheckCredentials(t, true, userID, provider, userEmail, false)\n\n\t\t\t\treturn req\n\t\t\t}\n\n\t\t\tt.Run(\"subcase=should not update without re-auth\", func(t *testing.T) {\n\t\t\t\t_ = runUnauthed(t)\n\t\t\t})\n\n\t\t\tt.Run(\"subcase=should update after re-auth\", func(t *testing.T) {\n\t\t\t\treq := runUnauthed(t)\n\n\t\t\t\t// fake login by allowing longer sessions...\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, normalPrivilegedSessionFor)\n\n\t\t\t\tbody, res := testhelpers.HTTPPostForm(t, client, action(req),\n\t\t\t\t\t&url.Values{\"csrf_token\": {nosurfx.FakeCSRFToken}, \"unlink\": {provider}})\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/settings?flow=\"+req.Id)\n\n\t\t\t\tassert.Equal(t, \"success\", gjson.GetBytes(body, \"state\").String())\n\n\t\t\t\tcheckCredentials(t, false, userID, provider, userEmail, false)\n\t\t\t\tcheckCredentials(t, true, userID, \"ory\", userEmail, false)\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"suite=link\", func(t *testing.T) {\n\t\tuserEmail, userData := multiOIDCIdentity()\n\t\t_, client := testhelpers.AddAndLoginIdentity(t, reg, userData)\n\n\t\tlink := func(t *testing.T, client *http.Client, provider string) (body []byte, res *http.Response, req *kratos.SettingsFlow) {\n\t\t\treq = nprSDK(t, client)\n\t\t\tbody, res = testhelpers.HTTPPostForm(t, client, action(req),\n\t\t\t\t&url.Values{\"csrf_token\": {nosurfx.FakeCSRFToken}, \"link\": {provider}})\n\t\t\treturn\n\t\t}\n\n\t\tlinkInvalid := func(userData userDataFunc, provider string) func(t *testing.T) {\n\t\t\treturn func(t *testing.T) {\n\t\t\t\t_, identityData := userData()\n\t\t\t\t_, client := testhelpers.AddAndLoginIdentity(t, reg, identityData)\n\n\t\t\t\tbody, res, req := link(t, client, provider)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/settings?flow=\"+req.Id)\n\n\t\t\t\t// assert.EqualValues(t, identity.CredentialsTypeOIDC.String(), gjson.GetBytes(body, \"active\").String())\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.action\").String(), publicTS.URL+settings.RouteSubmitFlow+\"?flow=\")\n\n\t\t\t\t// The original options to link google and github are still there\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.GetBytes(body, \"ui.nodes\").Raw), snapshotx.ExceptPaths(\"0.attributes.value\", \"1.attributes.value\"))\n\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, `ui.messages.0.text`).String(),\n\t\t\t\t\t\"can not link unknown or already existing OpenID Connect connection\")\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"case=should not be able to link an non-existing connection\",\n\t\t\tlinkInvalid(singleOIDCIdentity, \"i-do-not-exist\"))\n\n\t\tt.Run(\"case=should not be able to link a connection which already exists\",\n\t\t\tlinkInvalid(multiOIDCIdentity, \"github\"))\n\n\t\tt.Run(\"case=should not be able to link a connection already linked by another identity\", func(t *testing.T) {\n\t\t\t// While this theoretically allows for account enumeration - because we see an error indicator if an\n\t\t\t// OIDC connection is being linked that exists already - it would require the attacker to already\n\t\t\t// have control over the social profile, in which case account enumeration is the least of our worries.\n\t\t\t// Instead of using the OIDC profile for enumeration, the attacker would use it for account takeover.\n\t\t\t_, otherUserData := singleOIDCIdentity()\n\t\t\t_, otherClient := testhelpers.AddAndLoginIdentity(t, reg, otherUserData)\n\n\t\t\tsubject = userEmail\n\t\t\tscope = []string{\"openid\"}\n\n\t\t\tbody, res, _ := link(t, otherClient, \"github\")\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\tassert.Containsf(t, gjson.GetBytes(body, \"ui.messages.0.text\").String(), \"An account with the same identifier (email, phone, username, ...) exists already.\", \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"case=should not be able to link a connection which is missing the ID token\", func(t *testing.T) {\n\t\t\tsubject = \"hackerman+scope-missing\"\n\t\t\tscope = []string{}\n\n\t\t\tbody, res, _ := link(t, client, \"google\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\n\t\t\tassert.Containsf(t, gjson.GetBytes(body, `ui.messages.0.text`).String(),\n\t\t\t\t\"no id_token was returned\",\n\t\t\t\t\"%s\", body)\n\t\t})\n\n\t\tt.Run(\"case=should not be able to link a connection which is missing the ID token\", func(t *testing.T) {\n\t\t\tsubject = \"hackerman+scope-missing\"\n\t\t\tscope = []string{}\n\n\t\t\tbody, res, _ := link(t, client, \"google\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\n\t\t\tassert.Contains(t, gjson.GetBytes(body, `ui.messages.0.text`).String(),\n\t\t\t\t\"no id_token was returned\")\n\t\t})\n\n\t\tt.Run(\"case=should link a connection\", func(t *testing.T) {\n\t\t\tsubject = testhelpers.RandomEmail()\n\t\t\tscope = []string{\"openid\", \"offline\"}\n\t\t\tprovider := \"google\"\n\n\t\t\t_, userData := multiOIDCIdentity()\n\t\t\tuserID, client := testhelpers.AddAndLoginIdentity(t, reg, userData)\n\n\t\t\tupdatedFlow, res, originalFlow := link(t, client, provider)\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\n\t\t\tupdatedFlowSDK, _, err := testhelpers.NewSDKCustomClient(publicTS, client).FrontendAPI.GetSettingsFlow(t.Context()).Id(originalFlow.Id).Execute()\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.EqualValues(t, flow.StateSuccess, updatedFlowSDK.State)\n\n\t\t\tt.Run(\"flow=original\", func(t *testing.T) {\n\t\t\t\tsnapshotx.SnapshotT(t, originalFlow.Ui.Nodes, snapshotx.ExceptPaths(\"0.attributes.value\", \"1.attributes.value\"))\n\t\t\t})\n\t\t\tt.Run(\"flow=response\", func(t *testing.T) {\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.GetBytes(updatedFlow, \"ui.nodes\").Raw), snapshotx.ExceptPaths(\"0.attributes.value\", \"1.attributes.value\"))\n\t\t\t})\n\t\t\tt.Run(\"flow=fetch\", func(t *testing.T) {\n\t\t\t\tsnapshotx.SnapshotT(t, updatedFlowSDK.Ui.Nodes, snapshotx.ExceptPaths(\"0.attributes.value\", \"1.attributes.value\"))\n\t\t\t})\n\n\t\t\tcheckCredentials(t, true, userID, provider, subject, true)\n\t\t})\n\n\t\tt.Run(\"case=should link a connection and add auth method to session\", func(t *testing.T) {\n\t\t\t_, userData := multiOIDCIdentity()\n\t\t\t_, client := testhelpers.AddAndLoginIdentity(t, reg, userData)\n\t\t\tprovider := \"google\"\n\n\t\t\tsubject = testhelpers.RandomEmail()\n\t\t\tscope = []string{\"openid\", \"offline\"}\n\n\t\t\t_, res, _ := link(t, client, provider)\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\n\t\t\t// Get the specific session for this agent using SDK\n\t\t\tsess, _, err := testhelpers.NewSDKCustomClient(publicTS, client).\n\t\t\t\tFrontendAPI.\n\t\t\t\tToSession(t.Context()).\n\t\t\t\tExecute()\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NotNil(t, sess)\n\n\t\t\t// Check that the session has the expected auth method\n\t\t\tfound := slices.ContainsFunc(sess.AuthenticationMethods, func(am kratos.SessionAuthenticationMethod) bool {\n\t\t\t\treturn am.Method != nil &&\n\t\t\t\t\tam.Provider != nil &&\n\t\t\t\t\t*am.Method == string(identity.CredentialsTypeOIDC) &&\n\t\t\t\t\t*am.Provider == provider\n\t\t\t})\n\n\t\t\trequire.True(t, found, \"session should contain OIDC auth method for provider %s\", provider)\n\t\t})\n\n\t\tt.Run(\"case=should link a connection even if user does not have oidc credentials yet\", func(t *testing.T) {\n\t\t\t_, userData := passwordIdentity()\n\t\t\tuserID, client := testhelpers.AddAndLoginIdentity(t, reg, userData)\n\t\t\tprovider := \"google\"\n\n\t\t\tsubject = testhelpers.RandomEmail()\n\t\t\tscope = []string{\"openid\", \"offline\"}\n\n\t\t\t_, res, req := link(t, client, provider)\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\n\t\t\trs, _, err := testhelpers.NewSDKCustomClient(publicTS, client).FrontendAPI.GetSettingsFlow(t.Context()).Id(req.Id).Execute()\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.EqualValues(t, flow.StateSuccess, rs.State)\n\n\t\t\tsnapshotx.SnapshotT(t, rs.Ui.Nodes, snapshotx.ExceptPaths(\"0.attributes.value\", \"1.attributes.value\"))\n\n\t\t\tcheckCredentials(t, true, userID, provider, subject, true)\n\t\t})\n\n\t\tt.Run(\"case=upstream parameters\", func(t *testing.T) {\n\t\t\tsubject = \"\"\n\t\t\tscope = nil\n\t\t\tprovider := \"google\"\n\n\t\t\tt.Run(\"case=should be able to pass upstream paramters when linking a connection\", func(t *testing.T) {\n\t\t\t\treq := nprSDK(t, client)\n\t\t\t\t// copy over the client so we can disable redirects\n\t\t\t\tc := *client\n\t\t\t\t// We need to disable redirects because the upstream parameters are only passed on to the provider\n\t\t\t\tc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\n\t\t\t\tvalues := &url.Values{}\n\t\t\t\tvalues.Set(\"csrf_token\", nosurfx.FakeCSRFToken)\n\t\t\t\tvalues.Set(\"link\", provider)\n\t\t\t\tvalues.Set(\"upstream_parameters.login_hint\", \"foo@bar.com\")\n\t\t\t\tvalues.Set(\"upstream_parameters.hd\", \"bar.com\")\n\t\t\t\tvalues.Set(\"upstream_parameters.prompt\", \"consent\")\n\n\t\t\t\tresp, err := c.PostForm(action(req), *values)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusSeeOther, resp.StatusCode)\n\n\t\t\t\tloc, err := resp.Location()\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\trequire.EqualValues(t, \"foo@bar.com\", loc.Query().Get(\"login_hint\"))\n\t\t\t\trequire.EqualValues(t, \"bar.com\", loc.Query().Get(\"hd\"))\n\t\t\t\trequire.EqualValues(t, \"consent\", loc.Query().Get(\"prompt\"))\n\t\t\t})\n\n\t\t\tt.Run(\"case=invalid query parameters should be ignored\", func(t *testing.T) {\n\t\t\t\treq := nprSDK(t, client)\n\t\t\t\t// copy over the client so we can disable redirects\n\t\t\t\tc := *client\n\t\t\t\t// We need to disable redirects because the upstream parameters are only passed on to the provider\n\t\t\t\tc.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t}\n\n\t\t\t\tvalues := &url.Values{}\n\t\t\t\tvalues.Set(\"csrf_token\", nosurfx.FakeCSRFToken)\n\t\t\t\tvalues.Set(\"link\", provider)\n\t\t\t\tvalues.Set(\"upstream_parameters.lol\", \"invalid\")\n\n\t\t\t\tresp, err := c.PostForm(action(req), *values)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusSeeOther, resp.StatusCode)\n\n\t\t\t\tloc, err := resp.Location()\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\trequire.Empty(t, loc.Query().Get(\"lol\"))\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should not be able to link a connection without a privileged session\", func(t *testing.T) {\n\t\t\t_, userData := singleOIDCIdentity()\n\t\t\tuserID, client := testhelpers.AddAndLoginIdentity(t, reg, userData)\n\t\t\tprovider := \"google\"\n\n\t\t\tsubject = testhelpers.RandomEmail()\n\t\t\tscope = []string{\"openid\", \"offline\"}\n\n\t\t\trunUnauthed := func(t *testing.T) *kratos.SettingsFlow {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, time.Millisecond)\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, normalPrivilegedSessionFor)\n\t\t\t\t})\n\t\t\t\t_, res, req := link(t, client, provider)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login\")\n\n\t\t\t\tfa := testhelpers.NewSDKCustomClient(publicTS, client).FrontendAPI\n\t\t\t\tlf, _, err := fa.GetLoginFlow(t.Context()).Id(res.Request.URL.Query()[\"flow\"][0]).Execute()\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tfor _, n := range lf.Ui.Nodes {\n\t\t\t\t\tif n.Group == \"oidc\" && n.Attributes.UiNodeInputAttributes.Name == \"provider\" {\n\t\t\t\t\t\tassert.Contains(t, []string{\"ory\", \"github\"}, n.Attributes.UiNodeInputAttributes.Value)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trs, _, err := fa.GetSettingsFlow(t.Context()).Id(req.Id).Execute()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.EqualValues(t, flow.StateShowForm, rs.State)\n\n\t\t\t\tcheckCredentials(t, false, userID, provider, subject, true)\n\n\t\t\t\treturn req\n\t\t\t}\n\n\t\t\tt.Run(\"subcase=should not update without re-auth\", func(t *testing.T) {\n\t\t\t\t_ = runUnauthed(t)\n\t\t\t})\n\n\t\t\tt.Run(\"subcase=should update after re-auth\", func(t *testing.T) {\n\t\t\t\treq := runUnauthed(t)\n\n\t\t\t\t// fake login by allowing longer sessions...\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, normalPrivilegedSessionFor)\n\n\t\t\t\tbody, res := testhelpers.HTTPPostForm(t, client, action(req),\n\t\t\t\t\t&url.Values{\"csrf_token\": {nosurfx.FakeCSRFToken}, \"unlink\": {provider}})\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/settings?flow=\"+req.Id)\n\n\t\t\t\tassert.Equal(t, \"success\", gjson.GetBytes(body, \"state\").String())\n\n\t\t\t\tcheckCredentials(t, true, userID, provider, subject, true)\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"suite=api\", func(t *testing.T) {\n\t\tnewAPIClient := func(t *testing.T, userData userDataFunc) (uuid.UUID, *http.Client) {\n\t\t\t_, id := userData()\n\t\t\tid.State = identity.StateActive\n\t\t\treturn id.ID, testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\t}\n\n\t\tnprAPISDK := func(t *testing.T, client *http.Client) *kratos.SettingsFlow {\n\t\t\treturn testhelpers.InitializeSettingsFlowViaAPI(t, client, publicTS)\n\t\t}\n\n\t\tt.Run(\"case=API flow populates link and unlink nodes\", func(t *testing.T) {\n\t\t\tfor name, userData := range map[string]userDataFunc{\n\t\t\t\t\"password_only\":     passwordIdentity,\n\t\t\t\t\"single_oidc\":       singleOIDCIdentity,\n\t\t\t\t\"multi_oidc\":        multiOIDCIdentity,\n\t\t\t\t\"multi_credentials\": multiCredentialIdentity,\n\t\t\t} {\n\t\t\t\tt.Run(\"identity=\"+name, func(t *testing.T) {\n\t\t\t\t\t_, client := newAPIClient(t, userData)\n\t\t\t\t\trs := nprAPISDK(t, client)\n\t\t\t\t\tsnapshotx.SnapshotT(t, rs.Ui.Nodes, snapshotx.ExceptPaths(\"0.attributes.value\", \"1.attributes.value\"))\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tmakeOIDCPayload := func(rs *kratos.SettingsFlow, key, provider string) string {\n\t\t\tvalues := url.Values{\n\t\t\t\t\"csrf_token\": {nosurfx.FakeCSRFToken},\n\t\t\t\tkey:          {provider},\n\t\t\t}\n\t\t\treturn testhelpers.EncodeFormAsJSON(t, true, values)\n\t\t}\n\n\t\tt.Run(\"case=API flow link returns 422 BrowserLocationChangeRequired\", func(t *testing.T) {\n\t\t\t_, client := newAPIClient(t, multiOIDCIdentity)\n\t\t\tprovider := \"google\"\n\n\t\t\trs := nprAPISDK(t, client)\n\t\t\tpayload := makeOIDCPayload(rs, \"link\", provider)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, rs, client, payload)\n\t\t\trequire.Equal(t, http.StatusUnprocessableEntity, res.StatusCode, \"%s\", actual)\n\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"redirect_browser_to\").String(), \"%s\", actual)\n\n\t\t\tvar changeLocation flow.BrowserLocationChangeRequiredError\n\t\t\trequire.NoError(t, json.Unmarshal([]byte(actual), &changeLocation))\n\t\t\tassert.Contains(t, changeLocation.RedirectBrowserTo, \"oauth2/auth\")\n\t\t})\n\n\t\tt.Run(\"case=API flow unlink succeeds\", func(t *testing.T) {\n\t\t\temail, id := multiOIDCIdentity()\n\t\t\tid.State = identity.StateActive\n\t\t\tclient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\t\tprovider := \"github\"\n\n\t\t\trs := nprAPISDK(t, client)\n\t\t\tpayload := makeOIDCPayload(rs, \"unlink\", provider)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, rs, client, payload)\n\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode, \"%s\", actual)\n\t\t\tassert.EqualValues(t, \"success\", gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\n\t\t\t// id.ID is set by NewHTTPClientWithIdentitySessionToken after persistence\n\t\t\tcheckCredentials(t, false, id.ID, provider, email, false)\n\t\t\tcheckCredentials(t, true, id.ID, \"ory\", email, false)\n\t\t})\n\n\t\tt.Run(\"case=API flow unlink last connection fails\", func(t *testing.T) {\n\t\t\t_, client := newAPIClient(t, singleOIDCIdentity)\n\t\t\tprovider := \"ory\"\n\n\t\t\trs := nprAPISDK(t, client)\n\t\t\tpayload := makeOIDCPayload(rs, \"unlink\", provider)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, rs, client, payload)\n\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode, \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.messages.0.text\").String(),\n\t\t\t\t\"can not unlink OpenID Connect connection because it is the last remaining first factor credential\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=API flow unlink non-existing connection fails\", func(t *testing.T) {\n\t\t\t_, client := newAPIClient(t, multiOIDCIdentity)\n\t\t\tprovider := \"i-do-not-exist\"\n\n\t\t\trs := nprAPISDK(t, client)\n\t\t\tpayload := makeOIDCPayload(rs, \"unlink\", provider)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, rs, client, payload)\n\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode, \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.messages.0.text\").String(),\n\t\t\t\t\"can not unlink non-existing OpenID Connect connection\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=API flow unlink connection not yet linked fails\", func(t *testing.T) {\n\t\t\t_, client := newAPIClient(t, multiOIDCIdentity)\n\t\t\tprovider := \"google\"\n\n\t\t\trs := nprAPISDK(t, client)\n\t\t\tpayload := makeOIDCPayload(rs, \"unlink\", provider)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, rs, client, payload)\n\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode, \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.messages.0.text\").String(),\n\t\t\t\t\"can not unlink non-existing OpenID Connect connection\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=API flow link non-existing provider fails\", func(t *testing.T) {\n\t\t\t_, client := newAPIClient(t, singleOIDCIdentity)\n\t\t\tprovider := \"i-do-not-exist\"\n\n\t\t\trs := nprAPISDK(t, client)\n\t\t\tpayload := makeOIDCPayload(rs, \"link\", provider)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, rs, client, payload)\n\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode, \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.messages.0.text\").String(),\n\t\t\t\t\"can not link unknown or already existing OpenID Connect connection\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=API flow link already linked provider fails\", func(t *testing.T) {\n\t\t\t_, client := newAPIClient(t, multiOIDCIdentity)\n\t\t\tprovider := \"github\"\n\n\t\t\trs := nprAPISDK(t, client)\n\t\t\tpayload := makeOIDCPayload(rs, \"link\", provider)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, rs, client, payload)\n\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode, \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.messages.0.text\").String(),\n\t\t\t\t\"can not link unknown or already existing OpenID Connect connection\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=API flow privileged session requirement for unlink returns 403\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, time.Nanosecond)\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, normalPrivilegedSessionFor)\n\t\t\t})\n\n\t\t\t_, client := newAPIClient(t, multiOIDCIdentity)\n\t\t\tprovider := \"github\"\n\n\t\t\t// Ensure the privileged session window has expired.\n\t\t\ttime.Sleep(time.Millisecond)\n\n\t\t\trs := nprAPISDK(t, client)\n\t\t\tpayload := makeOIDCPayload(rs, \"unlink\", provider)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, rs, client, payload)\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode, \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"redirect_browser_to\").String(),\n\t\t\t\tpublicTS.URL+\"/self-service/login/browser?refresh=true&return_to=\")\n\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(actual), []string{\"redirect_browser_to\"})\n\t\t})\n\n\t\tt.Run(\"case=API flow privileged session requirement for link returns 403\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, time.Nanosecond)\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, normalPrivilegedSessionFor)\n\t\t\t})\n\n\t\t\t_, client := newAPIClient(t, multiOIDCIdentity)\n\t\t\tprovider := \"google\"\n\n\t\t\t// Ensure the privileged session window has expired.\n\t\t\ttime.Sleep(time.Millisecond)\n\n\t\t\trs := nprAPISDK(t, client)\n\t\t\tpayload := makeOIDCPayload(rs, \"link\", provider)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, rs, client, payload)\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode, \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"redirect_browser_to\").String(),\n\t\t\t\tpublicTS.URL+\"/self-service/login/browser?refresh=true&return_to=\")\n\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(actual), []string{\"redirect_browser_to\"})\n\t\t})\n\t})\n}\n\nfunc TestPopulateSettingsMethod(t *testing.T) {\n\tt.Parallel()\n\tnCtx := func(t *testing.T, conf *oidc.ConfigurationCollection) (*driver.RegistryDefault, context.Context) {\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\tctx := context.Background()\n\t\tctx = testhelpers.WithDefaultIdentitySchema(ctx, \"file://stub/registration.schema.json\")\n\t\tctx = contextx.WithConfigValue(ctx, config.ViperKeyPublicBaseURL, \"https://www.ory.sh/\")\n\t\tbaseKey := fmt.Sprintf(\"%s.%s\", config.ViperKeySelfServiceStrategyConfig, identity.CredentialsTypeOIDC)\n\n\t\tctx = contextx.WithConfigValues(ctx, map[string]interface{}{\n\t\t\tbaseKey + \".enabled\": true,\n\t\t\tbaseKey + \".config\":  conf,\n\t\t})\n\n\t\t// Enabled per default:\n\t\t// \t\tconf.Set(ctx, configuration.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword), map[string]interface{}{\"enabled\": true})\n\t\t// viperSetProviderConfig(t, c, conf.Providers...)\n\t\treturn reg, ctx\n\t}\n\n\tns := func(t *testing.T, reg *driver.RegistryDefault, ctx context.Context) *oidc.Strategy {\n\t\tss, err := reg.SettingsStrategies(ctx).Strategy(identity.CredentialsTypeOIDC.String())\n\t\trequire.NoError(t, err)\n\t\treturn ss.(*oidc.Strategy)\n\t}\n\n\tnr := func() *settings.Flow {\n\t\treturn &settings.Flow{Type: flow.TypeBrowser, ID: x.NewUUID(), UI: container.New(\"\")}\n\t}\n\n\tpopulate := func(t *testing.T, reg *driver.RegistryDefault, ctx context.Context, i *identity.Identity, f *settings.Flow) *container.Container {\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, i))\n\t\treq := new(http.Request)\n\t\trequire.NoError(t, ns(t, reg, ctx).PopulateSettingsMethod(ctx, req, i, f))\n\t\trequire.NotNil(t, f.UI)\n\t\trequire.NotNil(t, f.UI.Nodes)\n\t\tassert.Equal(t, \"POST\", f.UI.Method)\n\t\treturn f.UI\n\t}\n\n\tdefaultConfig := []oidc.Configuration{\n\t\t{Provider: \"generic\", ID: \"facebook\"},\n\t\t{Provider: \"generic\", ID: \"google\"},\n\t\t{Provider: \"generic\", ID: \"github\"},\n\t}\n\n\tt.Run(\"case=should populate API flow with link nodes\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\treg, ctx := nCtx(t, &oidc.ConfigurationCollection{Providers: []oidc.Configuration{{Provider: \"generic\", ID: \"github\"}}})\n\t\ti := &identity.Identity{Traits: []byte(`{\"subject\":\"foo@bar.com\"}`)}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, i))\n\t\tf := &settings.Flow{Type: flow.TypeAPI, ID: x.NewUUID(), UI: container.New(\"\")}\n\t\treq := new(http.Request)\n\t\trequire.NoError(t, ns(t, reg, ctx).PopulateSettingsMethod(ctx, req, i, f))\n\t\trequire.NotEmpty(t, f.UI.Nodes)\n\t\tassert.EqualValues(t, node.Nodes{\n\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\toidc.NewLinkNode(\"github\", \"github\"),\n\t\t}, f.UI.Nodes)\n\t})\n\n\tfor k, tc := range []struct {\n\t\tc      []oidc.Configuration\n\t\ti      *identity.Credentials\n\t\te      node.Nodes\n\t\twithpw bool\n\t}{\n\t\t{\n\t\t\tc: []oidc.Configuration{},\n\t\t\te: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tc: []oidc.Configuration{\n\t\t\t\t{Provider: \"generic\", ID: \"github\"},\n\t\t\t},\n\t\t\te: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\toidc.NewLinkNode(\"github\", \"github\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tc: defaultConfig,\n\t\t\te: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\toidc.NewLinkNode(\"facebook\", \"facebook\"),\n\t\t\t\toidc.NewLinkNode(\"google\", \"google\"),\n\t\t\t\toidc.NewLinkNode(\"github\", \"github\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tc: defaultConfig,\n\t\t\te: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\toidc.NewLinkNode(\"facebook\", \"facebook\"),\n\t\t\t\toidc.NewLinkNode(\"google\", \"google\"),\n\t\t\t\toidc.NewLinkNode(\"github\", \"github\"),\n\t\t\t},\n\t\t\ti: &identity.Credentials{Type: identity.CredentialsTypeOIDC, Identifiers: []string{}, Config: []byte(`{}`)},\n\t\t},\n\t\t{\n\t\t\tc: defaultConfig,\n\t\t\te: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\toidc.NewLinkNode(\"facebook\", \"facebook\"),\n\t\t\t\toidc.NewLinkNode(\"github\", \"github\"),\n\t\t\t},\n\t\t\ti: &identity.Credentials{Type: identity.CredentialsTypeOIDC, Identifiers: []string{\n\t\t\t\t\"google:1234\",\n\t\t\t}, Config: []byte(`{\"providers\":[{\"provider\":\"google\",\"subject\":\"1234\"}]}`)},\n\t\t},\n\t\t{\n\t\t\tc: defaultConfig,\n\t\t\te: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\toidc.NewLinkNode(\"facebook\", \"facebook\"),\n\t\t\t\toidc.NewLinkNode(\"github\", \"github\"),\n\t\t\t\toidc.NewUnlinkNode(\"google\", \"google\"),\n\t\t\t},\n\t\t\twithpw: true,\n\t\t\ti: &identity.Credentials{\n\t\t\t\tType: identity.CredentialsTypeOIDC, Identifiers: []string{\n\t\t\t\t\t\"google:1234\",\n\t\t\t\t},\n\t\t\t\tConfig: []byte(`{\"providers\":[{\"provider\":\"google\",\"subject\":\"1234\"}]}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tc: defaultConfig,\n\t\t\te: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\toidc.NewLinkNode(\"github\", \"github\"),\n\t\t\t\toidc.NewUnlinkNode(\"google\", \"google\"),\n\t\t\t\toidc.NewUnlinkNode(\"facebook\", \"facebook\"),\n\t\t\t},\n\t\t\ti: &identity.Credentials{\n\t\t\t\tType: identity.CredentialsTypeOIDC, Identifiers: []string{\n\t\t\t\t\t\"google:1234\",\n\t\t\t\t\t\"facebook:1234\",\n\t\t\t\t},\n\t\t\t\tConfig: []byte(`{\"providers\":[{\"provider\":\"google\",\"subject\":\"1234\"},{\"provider\":\"facebook\",\"subject\":\"1234\"}]}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tc: []oidc.Configuration{\n\t\t\t\t{Provider: \"generic\", ID: \"labeled\", Label: \"Labeled\"},\n\t\t\t},\n\t\t\te: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\toidc.NewLinkNode(\"labeled\", \"Labeled\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tc: []oidc.Configuration{\n\t\t\t\t{Provider: \"generic\", ID: \"labeled\", Label: \"Labeled\"},\n\t\t\t\t{Provider: \"generic\", ID: \"facebook\"},\n\t\t\t},\n\t\t\te: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\toidc.NewUnlinkNode(\"labeled\", \"Labeled\"),\n\t\t\t\toidc.NewUnlinkNode(\"facebook\", \"facebook\"),\n\t\t\t},\n\t\t\ti: &identity.Credentials{\n\t\t\t\tType: identity.CredentialsTypeOIDC, Identifiers: []string{\n\t\t\t\t\t\"labeled:1234\",\n\t\t\t\t\t\"facebook:1234\",\n\t\t\t\t},\n\t\t\t\tConfig: []byte(`{\"providers\":[{\"provider\":\"labeled\",\"subject\":\"1234\"},{\"provider\":\"facebook\",\"subject\":\"1234\"}]}`),\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(\"iteration=\"+strconv.Itoa(k), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\treg, ctx := nCtx(t, &oidc.ConfigurationCollection{Providers: tc.c})\n\t\t\ti := &identity.Identity{\n\t\t\t\tTraits:      []byte(`{\"subject\":\"foo@bar.com\"}`),\n\t\t\t\tCredentials: make(map[identity.CredentialsType]identity.Credentials, 2),\n\t\t\t}\n\t\t\tif tc.i != nil {\n\t\t\t\ti.Credentials[identity.CredentialsTypeOIDC] = *tc.i\n\t\t\t}\n\t\t\tif tc.withpw {\n\t\t\t\ti.Credentials[identity.CredentialsTypePassword] = identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\tIdentifiers: []string{\"foo@bar.com\"},\n\t\t\t\t\tConfig:      []byte(`{\"hashed_password\":\"$argon2id$...\"}`),\n\t\t\t\t}\n\t\t\t}\n\t\t\tactual := populate(t, reg, ctx, i, nr())\n\t\t\tassert.EqualValues(t, tc.e, actual.Nodes)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/strategy_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/cookiejar\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/davecgh/go-spew/spew\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/samber/lo\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/hook/hooktest\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/randx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n\t\"github.com/ory/x/uuidx\"\n)\n\nfunc TestStrategy(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\n\tvar (\n\t\tsubject string\n\t\tclaims  idTokenClaims\n\t\tscope   []string\n\t)\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeyIdentitySchemas: config.Schemas{\n\t\t\t\t{ID: \"default\", URL: \"file://./stub/registration.schema.json\"},\n\t\t\t\t{ID: \"email\", URL: \"file://./stub/registration.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t{ID: \"phone\", URL: \"file://./stub/registration-phone.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t{ID: \"extra_data\", URL: \"file://./stub/registration-multi-schema-extra-fields.schema.json\", SelfserviceSelectable: true},\n\t\t\t},\n\t\t\tconfig.ViperKeyDefaultIdentitySchemaID: \"default\",\n\t\t\tconfig.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeOIDC.String()): []config.SelfServiceHook{{Name: \"session\"}},\n\t\t}),\n\t)\n\n\tremoteAdmin, remotePublic, hydraIntegrationTSURL := newHydra(t, &subject, &claims, &scope)\n\treturnTS := newReturnTS(t, reg)\n\tuiTS := newUI(t, reg)\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\trouterP, routerA := httprouterx.NewTestRouterPublic(t), httprouterx.NewTestRouterAdminWithPrefix(t)\n\tts, _ := testhelpers.NewKratosServerWithRouters(t, reg, routerP, routerA)\n\tinvalid := newOIDCProvider(t, ts, remotePublic, remoteAdmin, \"invalid-issuer\")\n\n\torgID := uuidx.NewV4()\n\tviperSetProviderConfig(\n\t\tt,\n\t\tconf,\n\t\tnewOIDCProvider(t, ts, remotePublic, remoteAdmin, \"valid\"),\n\t\tnewOIDCProvider(t, ts, remotePublic, remoteAdmin, \"valid2\"),\n\t\tnewOIDCProvider(t, ts, remotePublic, remoteAdmin, \"secondProvider\"),\n\t\tnewOIDCProvider(t, ts, remotePublic, remoteAdmin, \"claimsViaUserInfo\", func(c *oidc.Configuration) {\n\t\t\tc.ClaimsSource = oidc.ClaimsSourceUserInfo\n\t\t}),\n\t\tnewOIDCProvider(t, ts, remotePublic, remoteAdmin, \"neverPKCE\", func(c *oidc.Configuration) {\n\t\t\tc.PKCE = \"never\"\n\t\t}),\n\t\tnewOIDCProvider(t, ts, remotePublic, remoteAdmin, \"autoPKCE\", func(c *oidc.Configuration) {\n\t\t\tc.PKCE = \"auto\"\n\t\t}),\n\t\tnewOIDCProvider(t, ts, remotePublic, remoteAdmin, \"forcePKCE\", func(c *oidc.Configuration) {\n\t\t\tc.PKCE = \"force\"\n\t\t}),\n\t\toidc.Configuration{\n\t\t\tProvider:     \"generic\",\n\t\t\tID:           \"invalid-issuer\",\n\t\t\tClientID:     invalid.ClientID,\n\t\t\tClientSecret: invalid.ClientSecret,\n\t\t\t// We replace this URL to cause an issuer validation mismatch.\n\t\t\tIssuerURL: strings.Replace(remotePublic, \"localhost\", \"127.0.0.1\", 1) + \"/\",\n\t\t\tMapper:    \"file://./stub/oidc.hydra.jsonnet\",\n\t\t},\n\t)\n\n\tt.Logf(\"Kratos Public URL: %s\", ts.URL)\n\tt.Logf(\"Kratos Error URL: %s\", errTS.URL)\n\tt.Logf(\"Hydra Public URL: %s\", remotePublic)\n\tt.Logf(\"Hydra Admin URL: %s\", remoteAdmin)\n\tt.Logf(\"Hydra Integration URL: %s\", hydraIntegrationTSURL)\n\tt.Logf(\"Return URL: %s\", returnTS.URL)\n\n\tsubject = \"foo@bar.com\"\n\tscope = []string{}\n\n\t// assert form values\n\tassertFormValues := func(t *testing.T, flowID uuid.UUID, provider string) (action string) {\n\t\tvar cfg *container.Container\n\t\tif req, err := reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), flowID); err == nil {\n\t\t\trequire.EqualValues(t, req.ID, flowID)\n\t\t\tcfg = req.UI\n\t\t\trequire.NotNil(t, cfg)\n\t\t} else if req, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), flowID); err == nil {\n\t\t\trequire.EqualValues(t, req.ID, flowID)\n\t\t\tcfg = req.UI\n\t\t\trequire.NotNil(t, cfg)\n\t\t} else {\n\t\t\trequire.NoError(t, err)\n\t\t\treturn\n\t\t}\n\n\t\tassert.Equal(t, \"POST\", cfg.Method)\n\n\t\tvar providers []interface{}\n\t\tfor _, nodes := range cfg.Nodes {\n\t\t\tif strings.Contains(nodes.ID(), \"provider\") {\n\t\t\t\tproviders = append(providers, nodes.GetValue())\n\t\t\t}\n\t\t}\n\t\trequire.Contains(t, providers, provider, \"%+v\", assertx.PrettifyJSONPayload(t, cfg))\n\n\t\treturn cfg.Action\n\t}\n\n\tregisterAction := func(flowID uuid.UUID) string {\n\t\treturn ts.URL + registration.RouteSubmitFlow + \"?flow=\" + flowID.String()\n\t}\n\n\tloginAction := func(flowID uuid.UUID) string {\n\t\treturn ts.URL + login.RouteSubmitFlow + \"?flow=\" + flowID.String()\n\t}\n\n\tmakeRequestWithCookieJar := func(t *testing.T, provider string, action string, fv url.Values, jar *cookiejar.Jar, checkRedirect testhelpers.CheckRedirectFunc) (*http.Response, []byte) {\n\t\tfv.Set(\"provider\", provider)\n\t\tres, err := testhelpers.NewClientWithCookieJar(t, jar, checkRedirect).PostForm(action, fv)\n\t\trequire.NoError(t, err, action)\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equalf(t, 200, res.StatusCode, \"%s: %s\\n\\t%s\", action, res.Request.URL.String(), body)\n\n\t\treturn res, body\n\t}\n\n\tmakeRequest := func(t *testing.T, provider string, action string, fv url.Values) (*http.Response, []byte) {\n\t\treturn makeRequestWithCookieJar(t, provider, action, fv, nil, nil)\n\t}\n\n\tmakeJSONRequest := func(t *testing.T, provider string, action string, fv url.Values) (*http.Response, []byte) {\n\t\tfv.Set(\"provider\", provider)\n\t\tclient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\t\treq, err := http.NewRequest(\"POST\", action, strings.NewReader(fv.Encode()))\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, err, action)\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, 422, res.StatusCode, \"%s: %s\\n\\t%s\", action, res.Request.URL.String(), body)\n\n\t\treturn res, body\n\t}\n\n\tmakeAPICodeFlowRequest := func(t *testing.T, provider, action string, cookieJar *cookiejar.Jar) (returnToURL *url.URL) {\n\t\tres, err := http.Post( // #nosec G107 -- test code\n\t\t\taction,\n\t\t\t\"application/json\",\n\t\t\tstrings.NewReader(fmt.Sprintf(`\n{\n\t\"method\": \"oidc\",\n\t\"provider\": %q\n}`, provider)),\n\t\t)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusUnprocessableEntity, res.StatusCode)\n\t\tvar changeLocation flow.BrowserLocationChangeRequiredError\n\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&changeLocation))\n\n\t\tres, err = testhelpers.NewClientWithCookieJar(t, cookieJar, nil).Get(changeLocation.RedirectBrowserTo)\n\t\trequire.NoError(t, err)\n\n\t\treturnToURL = res.Request.URL\n\t\tassert.True(t, strings.HasPrefix(returnToURL.String(), returnTS.URL+\"/app_code\"))\n\n\t\treturn returnToURL\n\t}\n\n\texchangeCodeForToken := func(t *testing.T, codes sessiontokenexchange.Codes) (codeResponse session.CodeExchangeResponse, err error) {\n\t\ttokenURL := urlx.ParseOrPanic(ts.URL)\n\t\ttokenURL.Path = \"/sessions/token-exchange\"\n\t\ttokenURL.RawQuery = fmt.Sprintf(\"init_code=%s&return_to_code=%s\", codes.InitCode, codes.ReturnToCode)\n\t\tres, err := ts.Client().Get(tokenURL.String())\n\t\tif err != nil {\n\t\t\treturn codeResponse, err\n\t\t}\n\t\tif res.StatusCode != 200 {\n\t\t\treturn codeResponse, fmt.Errorf(\"got status code %d\", res.StatusCode)\n\t\t}\n\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&codeResponse))\n\n\t\treturn\n\t}\n\n\tassertSystemErrorWithReason := func(t *testing.T, res *http.Response, body []byte, code int, reason string) {\n\t\trequire.Contains(t, res.Request.URL.String(), errTS.URL, \"%s\", body)\n\n\t\tassert.Equal(t, int64(code), gjson.GetBytes(body, \"code\").Int(), \"%s\", prettyJSON(t, body))\n\t\tassert.Contains(t, gjson.GetBytes(body, \"reason\").String(), reason, \"%s\", prettyJSON(t, body))\n\t}\n\n\t// assert system error (redirect to error endpoint)\n\tassertSystemErrorWithMessage := func(t *testing.T, res *http.Response, body []byte, code int, message string) {\n\t\trequire.Contains(t, res.Request.URL.String(), errTS.URL, \"%s\", body)\n\n\t\tassert.Equal(t, int64(code), gjson.GetBytes(body, \"code\").Int(), \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"message\").String(), message, \"%s\", body)\n\t}\n\n\t// assert ui error (redirect to login/registration ui endpoint)\n\tassertUIError := func(t *testing.T, res *http.Response, body []byte, reason string) {\n\t\trequire.Contains(t, res.Request.URL.String(), uiTS.URL, \"Redirect does not point to UI server. Status: %d, body: %s\", res.StatusCode, body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.messages.0.text\").String(), reason, \"%s\", prettyJSON(t, body))\n\t}\n\n\t// assert identity (success)\n\tassertIdentity := func(t *testing.T, res *http.Response, body []byte) {\n\t\tassert.Contains(t, res.Request.URL.String(), returnTS.URL, \"%s\", body)\n\t\tassert.Equal(t, subject, gjson.GetBytes(body, \"identity.traits.subject\").String(), \"%s\", prettyJSON(t, body))\n\t\tassert.Equal(t, claims.traits.website, gjson.GetBytes(body, \"identity.traits.website\").String(), \"%s\", prettyJSON(t, body))\n\t\tassert.Equal(t, claims.metadataPublic.picture, gjson.GetBytes(body, \"identity.metadata_public.picture\").String(), \"%s\", prettyJSON(t, body))\n\t}\n\n\tnewLoginFlow := func(t *testing.T, requestURL string, exp time.Duration, flowType flow.Type) (req *login.Flow) {\n\t\t// Use NewLoginFlow to instantiate the request but change the things we need to control a copy of it.\n\t\treq, _, err := reg.LoginHandler().NewLoginFlow(httptest.NewRecorder(),\n\t\t\t&http.Request{URL: urlx.ParseOrPanic(requestURL)}, flowType)\n\t\trequire.NoError(t, err)\n\t\treq.RequestURL = requestURL\n\t\treq.ExpiresAt = time.Now().Add(exp)\n\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), req))\n\n\t\t// sanity check\n\t\tgot, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), req.ID)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, got.UI.Nodes, len(req.UI.Nodes), \"%+v\", got)\n\n\t\treturn\n\t}\n\tnewBrowserLoginFlow := func(t *testing.T, redirectTo string, exp time.Duration) (req *login.Flow) {\n\t\treturn newLoginFlow(t, redirectTo, exp, flow.TypeBrowser)\n\t}\n\tnewAPILoginFlow := func(t *testing.T, redirectTo string, exp time.Duration) (req *login.Flow) {\n\t\treturn newLoginFlow(t, redirectTo, exp, flow.TypeAPI)\n\t}\n\n\tnewRegistrationFlow := func(t *testing.T, redirectTo string, exp time.Duration, flowType flow.Type) *registration.Flow {\n\t\t// Use NewLoginFlow to instantiate the request but change the things we need to control a copy of it.\n\t\treq, err := reg.RegistrationHandler().NewRegistrationFlow(httptest.NewRecorder(),\n\t\t\t&http.Request{URL: urlx.ParseOrPanic(redirectTo)}, flowType)\n\t\trequire.NoError(t, err)\n\t\treq.RequestURL = redirectTo\n\t\treq.ExpiresAt = time.Now().Add(exp)\n\t\trequire.NoError(t, reg.RegistrationFlowPersister().UpdateRegistrationFlow(context.Background(), req))\n\n\t\t// sanity check\n\t\tgot, err := reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), req.ID)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, got.UI.Nodes, len(req.UI.Nodes), \"%+v\", req)\n\n\t\treturn req\n\t}\n\tnewBrowserRegistrationFlow := func(t *testing.T, redirectTo string, exp time.Duration) *registration.Flow {\n\t\treturn newRegistrationFlow(t, redirectTo, exp, flow.TypeBrowser)\n\t}\n\tnewAPIRegistrationFlow := func(t *testing.T, redirectTo string, exp time.Duration) *registration.Flow {\n\t\treturn newRegistrationFlow(t, redirectTo, exp, flow.TypeAPI)\n\t}\n\n\tt.Run(\"case=should fail because provider does not exist\", func(t *testing.T) {\n\t\tfor k, v := range []string{\n\t\t\tloginAction(newBrowserLoginFlow(t, returnTS.URL, time.Minute).ID),\n\t\t\tregisterAction(newBrowserRegistrationFlow(t, returnTS.URL, time.Minute).ID),\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tres, body := makeRequest(t, \"provider-does-not-exist\", v, url.Values{})\n\t\t\t\tassertSystemErrorWithReason(t, res, body, http.StatusNotFound, \"is unknown or has not been configured\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail because the issuer is mismatching\", func(t *testing.T) {\n\t\tscope = []string{\"openid\"}\n\t\tfor k, v := range []string{\n\t\t\tloginAction(newBrowserLoginFlow(t, returnTS.URL, time.Minute).ID),\n\t\t\tregisterAction(newBrowserRegistrationFlow(t, returnTS.URL, time.Minute).ID),\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tres, body := makeRequest(t, \"invalid-issuer\", v, url.Values{})\n\t\t\t\tassertSystemErrorWithReason(t, res, body, http.StatusInternalServerError, \"issuer did not match the issuer returned by provider\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail because flow does not exist\", func(t *testing.T) {\n\t\tfor k, v := range []string{loginAction(x.NewUUID()), registerAction(x.NewUUID())} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tres, body := makeRequest(t, \"valid\", v, url.Values{})\n\t\t\t\tassertSystemErrorWithMessage(t, res, body, http.StatusNotFound, \"Unable to locate the resource\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail because the flow is expired\", func(t *testing.T) {\n\t\tfor k, v := range []uuid.UUID{\n\t\t\tnewBrowserLoginFlow(t, returnTS.URL, -time.Minute).ID,\n\t\t\tnewBrowserRegistrationFlow(t, returnTS.URL, -time.Minute).ID,\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\taction := assertFormValues(t, v, \"valid\")\n\t\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{})\n\n\t\t\t\tassert.NotEqual(t, v, gjson.GetBytes(body, \"id\"))\n\t\t\t\trequire.Contains(t, res.Request.URL.String(), uiTS.URL, \"%s\", body)\n\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.messages.0.text\").String(), \"flow expired\", \"%s\", body)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail registration because scope was not provided\", func(t *testing.T) {\n\t\tsubject = \"foo@bar.com\"\n\t\tscope = []string{}\n\n\t\tfor k, v := range []uuid.UUID{\n\t\t\tnewBrowserLoginFlow(t, returnTS.URL, time.Minute).ID,\n\t\t\tnewBrowserRegistrationFlow(t, returnTS.URL, time.Minute).ID,\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\taction := assertFormValues(t, v, \"valid\")\n\t\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\t\t\tassertUIError(t, res, body, \"no id_token was returned\")\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should fail because password can not handle AAL2\", func(t *testing.T) {\n\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration-aal.schema.json\")\n\t\tt.Cleanup(func() {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\t\t})\n\t\tbc := testhelpers.NewDebugClient(t)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, bc, ts, false)\n\n\t\tupdate, err := reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\trequire.NoError(t, err)\n\t\tupdate.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), update))\n\n\t\treq, err := http.NewRequest(\"POST\", f.Ui.Action, bytes.NewBufferString(`{\"method\":\"oidc\"}`))\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tactual, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, routerP, req)\n\t\tassert.Contains(t, res.Request.URL.String(), ts.URL+login.RouteSubmitFlow)\n\t\tassert.Equal(t, text.NewErrorValidationLoginNoStrategyFound().Text, gjson.GetBytes(actual, \"ui.messages.0.text\").String())\n\t})\n\n\tt.Run(\"case=should fail login because scope was not provided\", func(t *testing.T) {\n\t\tr := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\tres, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\tassertUIError(t, res, body, \"no id_token was returned\")\n\t})\n\n\tt.Run(\"case=should fail registration flow because subject is not an email\", func(t *testing.T) {\n\t\tsubject = \"not-an-email\"\n\t\tscope = []string{\"openid\"}\n\n\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\tres, body := makeRequest(t, \"valid\", action, url.Values{})\n\n\t\trequire.Contains(t, res.Request.URL.String(), uiTS.URL, \"%s\", body)\n\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.subject).messages.0.text\").String(), \"Enter a valid email address\", \"%s\\n%s\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.subject)\").Raw, body)\n\t})\n\n\tt.Run(\"case=cannot register multiple accounts with the same OIDC account\", func(t *testing.T) {\n\t\tsubject = \"oidc-register-then-login@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\n\t\texpectTokens := func(t *testing.T, provider string, body []byte) {\n\t\t\ti, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), uuid.FromStringOrNil(gjson.GetBytes(body, \"identity.id\").String()))\n\t\t\trequire.NoError(t, err)\n\t\t\tc := i.Credentials[identity.CredentialsTypeOIDC].Config\n\t\t\tassert.NotEmpty(t, gjson.GetBytes(c, \"providers.0.initial_access_token\").String())\n\t\t\tassertx.EqualAsJSONExcept(\n\t\t\t\tt,\n\t\t\t\tjson.RawMessage(fmt.Sprintf(`{\"providers\": [{\"subject\":\"%s\",\"provider\":\"%s\"}]}`, subject, provider)),\n\t\t\t\tjson.RawMessage(c),\n\t\t\t\t[]string{\"providers.0.initial_id_token\", \"providers.0.initial_access_token\", \"providers.0.initial_refresh_token\"},\n\t\t\t)\n\t\t}\n\n\t\tt.Run(\"case=should pass registration\", func(t *testing.T) {\n\t\t\tpostRegistrationWebhook := hooktest.NewServer()\n\t\t\tt.Cleanup(postRegistrationWebhook.Close)\n\t\t\tpostRegistrationWebhook.SetConfig(t, conf.GetProvider(ctx), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeOIDC.String()))\n\t\t\ttransientPayload := `{\"data\": \"registration-one\"}`\n\n\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\"transient_payload\": {transientPayload}})\n\t\t\tassertIdentity(t, res, body)\n\t\t\texpectTokens(t, \"valid\", body)\n\t\t\tpostRegistrationWebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\n\t\tt.Run(\"case=try another registration\", func(t *testing.T) {\n\t\t\ttransientPayload := `{\"data\": \"registration-two\"}`\n\t\t\tpostLoginWebhook := hooktest.NewServer()\n\t\t\tt.Cleanup(postLoginWebhook.Close)\n\t\t\tpostLoginWebhook.SetConfig(t, conf.GetProvider(ctx), config.HookStrategyKey(config.ViperKeySelfServiceLoginAfter, identity.CredentialsTypeOIDC.String()))\n\n\t\t\treturnTo := fmt.Sprintf(\"%s/home?query=true\", returnTS.URL)\n\t\t\tr := newBrowserRegistrationFlow(t, fmt.Sprintf(\"%s?return_to=%s\", returnTS.URL, url.QueryEscape(returnTo)), time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\"transient_payload\": {transientPayload}})\n\t\t\tassert.Equal(t, returnTo, res.Request.URL.String())\n\t\t\tassertIdentity(t, res, body)\n\t\t\texpectTokens(t, \"valid\", body)\n\n\t\t\tpostLoginWebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\t})\n\n\texpectTokens := func(t *testing.T, provider string, body []byte) uuid.UUID {\n\t\tid := uuid.FromStringOrNil(gjson.GetBytes(body, \"identity.id\").String())\n\t\ti, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), id)\n\t\trequire.NoError(t, err)\n\t\tc := i.Credentials[identity.CredentialsTypeOIDC].Config\n\t\tassert.NotEmpty(t, gjson.GetBytes(c, \"providers.0.initial_access_token\").String())\n\t\tassertx.EqualAsJSONExcept(\n\t\t\tt,\n\t\t\tjson.RawMessage(fmt.Sprintf(`{\"providers\": [{\"subject\":\"%s\",\"provider\":\"%s\"}]}`, subject, provider)),\n\t\t\tjson.RawMessage(c),\n\t\t\t[]string{\"providers.0.initial_id_token\", \"providers.0.initial_access_token\", \"providers.0.initial_refresh_token\"},\n\t\t)\n\t\treturn id\n\t}\n\n\tt.Run(\"case=force PKCE\", func(t *testing.T) {\n\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\taction := assertFormValues(t, r.ID, \"forcePKCE\")\n\t\tsubject = \"force-pkce@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\t\tvar redirects []*http.Request\n\t\tres, body := makeRequestWithCookieJar(t, \"forcePKCE\", action, url.Values{}, nil, func(_ *http.Request, via []*http.Request) error {\n\t\t\tredirects = via\n\t\t\treturn nil\n\t\t})\n\t\trequire.GreaterOrEqual(t, len(redirects), 3)\n\t\tassert.Contains(t, redirects[1].URL.String(), \"/oauth2/auth\")\n\t\tassert.Contains(t, redirects[1].URL.String(), \"code_challenge_method=S256\")\n\t\tassert.Contains(t, redirects[1].URL.String(), \"code_challenge=\")\n\t\tassert.Equal(t, redirects[len(redirects)-1].URL.Path, \"/self-service/methods/oidc/callback\")\n\n\t\tassertIdentity(t, res, body)\n\t\texpectTokens(t, \"forcePKCE\", body)\n\t\tassert.Equal(t, \"forcePKCE\", gjson.GetBytes(body, \"authentication_methods.0.provider\").String(), \"%s\", body)\n\t})\n\tt.Run(\"case=force PKCE, invalid verifier\", func(t *testing.T) {\n\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\taction := assertFormValues(t, r.ID, \"forcePKCE\")\n\t\tsubject = \"force-pkce@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\t\tverifierFalsified := false\n\t\tres, body := makeRequestWithCookieJar(t, \"forcePKCE\", action, url.Values{}, nil, func(req *http.Request, via []*http.Request) error {\n\t\t\tif req.URL.Path == \"/oauth2/auth\" && !verifierFalsified {\n\t\t\t\tq := req.URL.Query()\n\t\t\t\trequire.NotEmpty(t, q.Get(\"code_challenge\"))\n\t\t\t\trequire.Equal(t, \"S256\", q.Get(\"code_challenge_method\"))\n\t\t\t\tq.Set(\"code_challenge\", oauth2.S256ChallengeFromVerifier(oauth2.GenerateVerifier()))\n\t\t\t\treq.URL.RawQuery = q.Encode()\n\t\t\t\tverifierFalsified = true\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\trequire.True(t, verifierFalsified)\n\t\tassertSystemErrorWithMessage(t, res, body, http.StatusInternalServerError, \"The PKCE code challenge did not match the code verifier.\")\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String())\n\t})\n\tt.Run(\"case=force PKCE, code challenge params removed from initial redirect\", func(t *testing.T) {\n\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\taction := assertFormValues(t, r.ID, \"forcePKCE\")\n\t\tsubject = \"force-pkce@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\t\tchallengeParamsRemoved := false\n\t\tres, body := makeRequestWithCookieJar(t, \"forcePKCE\", action, url.Values{}, nil, func(req *http.Request, via []*http.Request) error {\n\t\t\tif req.URL.Path == \"/oauth2/auth\" && !challengeParamsRemoved {\n\t\t\t\tq := req.URL.Query()\n\t\t\t\trequire.NotEmpty(t, q.Get(\"code_challenge\"))\n\t\t\t\trequire.Equal(t, \"S256\", q.Get(\"code_challenge_method\"))\n\t\t\t\tq.Del(\"code_challenge\")\n\t\t\t\tq.Del(\"code_challenge_method\")\n\t\t\t\treq.URL.RawQuery = q.Encode()\n\t\t\t\tchallengeParamsRemoved = true\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\trequire.True(t, challengeParamsRemoved)\n\t\tassertSystemErrorWithMessage(t, res, body, http.StatusInternalServerError, \"The PKCE code challenge did not match the code verifier.\")\n\t\tassert.Contains(t, res.Request.URL.String(), conf.SelfServiceFlowErrorURL(ctx).String())\n\t})\n\tt.Run(\"case=PKCE prevents authorization code injection attacks\", func(t *testing.T) {\n\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\taction := assertFormValues(t, r.ID, \"forcePKCE\")\n\t\tsubject = \"attacker@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\t\tvar code string\n\t\t_, err := testhelpers.NewClientWithCookieJar(t, nil, func(req *http.Request, via []*http.Request) error {\n\t\t\tif req.URL.Query().Has(\"code\") {\n\t\t\t\tcode = req.URL.Query().Get(\"code\")\n\t\t\t\treturn errors.New(\"code intercepted\")\n\t\t\t}\n\t\t\treturn nil\n\t\t}).PostForm(action, url.Values{\"provider\": {\"forcePKCE\"}})\n\t\trequire.ErrorContains(t, err, \"code intercepted\")\n\t\trequire.NotEmpty(t, code) // code now contains a valid authorization code\n\n\t\tr2 := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\taction = assertFormValues(t, r2.ID, \"forcePKCE\")\n\t\tjar, err := cookiejar.New(nil) // must capture the continuity cookie\n\t\trequire.NoError(t, err)\n\t\tvar redirectURI, state string\n\t\t_, err = testhelpers.NewClientWithCookieJar(t, jar, func(req *http.Request, via []*http.Request) error {\n\t\t\tif req.URL.Path == \"/oauth2/auth\" {\n\t\t\t\tredirectURI = req.URL.Query().Get(\"redirect_uri\")\n\t\t\t\tstate = req.URL.Query().Get(\"state\")\n\t\t\t\treturn errors.New(\"stop before redirect to Authorization URL\")\n\t\t\t}\n\t\t\treturn nil\n\t\t}).PostForm(action, url.Values{\"provider\": {\"forcePKCE\"}})\n\t\trequire.ErrorContains(t, err, \"stop\")\n\t\trequire.NotEmpty(t, redirectURI)\n\t\trequire.NotEmpty(t, state)\n\t\tres, err := testhelpers.NewClientWithCookieJar(t, jar, nil).Get(redirectURI + \"?code=\" + code + \"&state=\" + state)\n\t\trequire.NoError(t, err)\n\t\tbody := x.MustReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\t\tassertSystemErrorWithMessage(t, res, body, http.StatusInternalServerError, \"The PKCE code challenge did not match the code verifier.\")\n\t})\n\tt.Run(\"case=confused providers are detected\", func(t *testing.T) {\n\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\tsubject = \"attacker@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\t\tredirectConfused := false\n\t\tres, err := testhelpers.NewClientWithCookieJar(t, nil, func(req *http.Request, via []*http.Request) error {\n\t\t\tif req.URL.Query().Has(\"code\") {\n\t\t\t\treq.URL.Path = strings.Replace(req.URL.Path, \"valid\", \"valid2\", 1)\n\t\t\t\tredirectConfused = true\n\t\t\t}\n\t\t\treturn nil\n\t\t}).PostForm(action, url.Values{\"provider\": {\"valid\"}})\n\t\trequire.True(t, redirectConfused)\n\t\trequire.NoError(t, err)\n\t\tbody := x.MustReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\tassertSystemErrorWithReason(t, res, body, http.StatusBadRequest, \"provider mismatch between internal state and URL\")\n\t})\n\tt.Run(\"case=automatic PKCE\", func(t *testing.T) {\n\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\taction := assertFormValues(t, r.ID, \"autoPKCE\")\n\t\tsubject = \"auto-pkce@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\t\tvar redirects []*http.Request\n\t\tres, body := makeRequestWithCookieJar(t, \"autoPKCE\", action, url.Values{}, nil, func(_ *http.Request, via []*http.Request) error {\n\t\t\tredirects = via\n\t\t\treturn nil\n\t\t})\n\t\trequire.GreaterOrEqual(t, len(redirects), 3)\n\t\tassert.Contains(t, redirects[1].URL.String(), \"/oauth2/auth\")\n\t\tassert.Contains(t, redirects[1].URL.String(), \"code_challenge_method=S256\")\n\t\tassert.Contains(t, redirects[1].URL.String(), \"code_challenge=\")\n\t\tassert.Equal(t, redirects[len(redirects)-1].URL.Path, \"/self-service/methods/oidc/callback/autoPKCE\")\n\n\t\tassertIdentity(t, res, body)\n\t\texpectTokens(t, \"autoPKCE\", body)\n\t\tassert.Equal(t, \"autoPKCE\", gjson.GetBytes(body, \"authentication_methods.0.provider\").String(), \"%s\", body)\n\t})\n\tt.Run(\"case=disabled PKCE\", func(t *testing.T) {\n\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\taction := assertFormValues(t, r.ID, \"neverPKCE\")\n\t\tsubject = \"never-pkce@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\t\tvar redirects []*http.Request\n\t\tres, body := makeRequestWithCookieJar(t, \"neverPKCE\", action, url.Values{}, nil, func(_ *http.Request, via []*http.Request) error {\n\t\t\tredirects = via\n\t\t\treturn nil\n\t\t})\n\t\trequire.GreaterOrEqual(t, len(redirects), 3)\n\t\tassert.Contains(t, redirects[1].URL.String(), \"/oauth2/auth\")\n\t\tassert.NotContains(t, redirects[1].URL.String(), \"code_challenge_method=\")\n\t\tassert.NotContains(t, redirects[1].URL.String(), \"code_challenge=\")\n\t\tassert.Equal(t, redirects[len(redirects)-1].URL.Path, \"/self-service/methods/oidc/callback/neverPKCE\")\n\n\t\tassertIdentity(t, res, body)\n\t\texpectTokens(t, \"neverPKCE\", body)\n\t\tassert.Equal(t, \"neverPKCE\", gjson.GetBytes(body, \"authentication_methods.0.provider\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=register and then login\", func(t *testing.T) {\n\t\tpostRegistrationWebhook := hooktest.NewServer()\n\t\tt.Cleanup(postRegistrationWebhook.Close)\n\t\tpostLoginWebhook := hooktest.NewServer()\n\t\tt.Cleanup(postLoginWebhook.Close)\n\n\t\tpostRegistrationWebhook.SetConfig(t, conf.GetProvider(ctx),\n\t\t\tconfig.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeOIDC.String()))\n\t\tpostLoginWebhook.SetConfig(t, conf.GetProvider(ctx),\n\t\t\tconfig.HookStrategyKey(config.ViperKeySelfServiceLoginAfter, config.HookGlobal))\n\n\t\tsubject = \"register-then-login@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\n\t\tt.Run(\"case=should pass registration\", func(t *testing.T) {\n\t\t\ttransientPayload := `{\"data\": \"registration\"}`\n\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\n\t\t\t\t\"transient_payload\": {transientPayload},\n\t\t\t})\n\t\t\tassertIdentity(t, res, body)\n\t\t\texpectTokens(t, \"valid\", body)\n\t\t\tassert.Equal(t, \"valid\", gjson.GetBytes(body, \"authentication_methods.0.provider\").String(), \"%s\", body)\n\n\t\t\tpostRegistrationWebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\n\t\tt.Run(\"case=should pass login\", func(t *testing.T) {\n\t\t\ttransientPayload := `{\"data\": \"login\"}`\n\t\t\tr := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\n\t\t\t\t\"transient_payload\": {transientPayload},\n\t\t\t})\n\t\t\tassertIdentity(t, res, body)\n\t\t\texpectTokens(t, \"valid\", body)\n\t\t\tassert.Equal(t, \"valid\", gjson.GetBytes(body, \"authentication_methods.0.provider\").String(), \"%s\", body)\n\n\t\t\tpostLoginWebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\n\t\tt.Run(\"case=should pass double submit\", func(t *testing.T) {\n\t\t\t// This test checks that the continuity manager uses a grace period to handle potential double-submit issues.\n\t\t\t//\n\t\t\t// It addresses issues where Facebook and Apple consent screens on mobile behave in a way that makes it\n\t\t\t// easy for users to experience double-submit issues.\n\t\t\tj, err := cookiejar.New(nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tmakeInitialRequest := func(t *testing.T, provider, action string, fv url.Values) (*http.Response, []byte, []string) {\n\t\t\t\tfv.Set(\"provider\", provider)\n\n\t\t\t\tvar lastVia []*http.Request\n\t\t\t\thc := &http.Client{\n\t\t\t\t\tJar: j,\n\t\t\t\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\t\tlastVia = via\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tres, err := hc.PostForm(action, fv)\n\t\t\t\trequire.NoError(t, err, action)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotEmpty(t, lastVia)\n\n\t\t\t\tvias := make([]string, len(lastVia))\n\t\t\t\tfor k, v := range lastVia {\n\t\t\t\t\tvias[k] = v.URL.String()\n\t\t\t\t}\n\n\t\t\t\treturn res, body, vias\n\t\t\t}\n\n\t\t\tr := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\n\t\t\t// First login\n\t\t\tres, body, via := makeInitialRequest(t, \"valid\", action, url.Values{})\n\t\t\tassertIdentity(t, res, body)\n\t\t\texpectTokens(t, \"valid\", body)\n\t\t\tassert.Equal(t, \"valid\", gjson.GetBytes(body, \"authentication_methods.0.provider\").String(), \"%s\", body)\n\n\t\t\t// We fetch the URL which includes the `?code` query parameter.\n\t\t\tresult := lo.Filter(via, func(s string, _ int) bool {\n\t\t\t\treturn strings.Contains(s, \"code=\")\n\t\t\t})\n\t\t\trequire.Len(t, result, 1)\n\n\t\t\t// And call that URL again. What's interesting here is that the whole requets passes because we are already authenticated.\n\t\t\t//\n\t\t\t// In this scenario, Ory Kratos correctly forwards the user to the return URL, which in our case returns the identity.\n\t\t\t//\n\t\t\t// We essentially run into this bit:\n\t\t\t//\n\t\t\t// \tif authenticated, err := s.alreadyAuthenticated(w, r, req); err != nil {\n\t\t\t//\t\ts.forwardError(w, r, req, s.handleError(w, , r, req, pid, nil, err))\n\t\t\t//\t} else if authenticated {\n\t\t\t//\t\treturn <-- we end up here on the second call\n\t\t\t//\t}\n\t\t\tres, err = (&http.Client{Jar: j}).Get(result[0])\n\t\t\trequire.NoError(t, err)\n\t\t\tbody, err = io.ReadAll(res.Body)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\tassertIdentity(t, res, body)\n\t\t\texpectTokens(t, \"valid\", body)\n\t\t\tassert.Equal(t, \"valid\", gjson.GetBytes(body, \"authentication_methods.0.provider\").String(), \"%s\", body)\n\n\t\t\t// Trying this flow again without the Ory Session cookie will fail as we run into code reuse:\n\t\t\tcookies := j.Cookies(urlx.ParseOrPanic(ts.URL))\n\t\t\tt.Logf(\"Cookies: %s\", spew.Sdump(cookies))\n\n\t\t\tsecondJar, err := cookiejar.New(nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tsecondJar.SetCookies(urlx.ParseOrPanic(ts.URL), lo.Filter(cookies, func(item *http.Cookie, index int) bool {\n\t\t\t\treturn item.Name != \"ory_kratos_session\"\n\t\t\t}))\n\n\t\t\tcookies = secondJar.Cookies(urlx.ParseOrPanic(ts.URL))\n\t\t\tt.Logf(\"Cookies after: %s\", spew.Sdump(cookies))\n\n\t\t\t// Doing the request but this time without the Ory Session Cookie. This may be the case in scenarios where we run into race conditions\n\t\t\t// where the server sent a response but the client did not process it.\n\t\t\tres, err = (&http.Client{Jar: secondJar}).Get(result[0])\n\t\t\trequire.NoError(t, err)\n\t\t\tbody, err = io.ReadAll(res.Body)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, res.Body.Close())\n\n\t\t\tassert.Contains(t, string(body), \"The authorization code has already been used\", \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=login without registered account\", func(t *testing.T) {\n\t\tpostRegistrationWebhook := hooktest.NewServer()\n\t\tt.Cleanup(postRegistrationWebhook.Close)\n\t\tpostLoginWebhook := hooktest.NewServer()\n\t\tt.Cleanup(postLoginWebhook.Close)\n\n\t\tpostRegistrationWebhook.SetConfig(t, conf.GetProvider(ctx),\n\t\t\tconfig.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeOIDC.String()))\n\t\tpostLoginWebhook.SetConfig(t, conf.GetProvider(ctx),\n\t\t\tconfig.HookStrategyKey(config.ViperKeySelfServiceLoginAfter, config.HookGlobal))\n\n\t\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t{ID: \"default\", URL: \"file://./stub/registration.schema.json\"},\n\t\t\t{ID: \"email\", URL: \"file://./stub/registration.schema.json\", SelfserviceSelectable: true},\n\t\t\t{ID: \"phone\", URL: \"file://./stub/registration-phone.schema.json\", SelfserviceSelectable: true},\n\t\t\t{ID: \"extra_data\", URL: \"file://./stub/registration-multi-schema-extra-fields.schema.json\", SelfserviceSelectable: true},\n\t\t})\n\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\n\t\tsubject = \"login-without-register@ory.sh\"\n\t\tscope = []string{\"openid\"}\n\n\t\tt.Run(\"case=should pass login\", func(t *testing.T) {\n\t\t\ttransientPayload := `{\"data\": \"login to registration\"}`\n\n\t\t\tr := newBrowserLoginFlow(t, \"https://example.com?identity_schema=phone\", time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\n\t\t\t\t\"transient_payload\": {transientPayload},\n\t\t\t})\n\t\t\tassertIdentity(t, res, body)\n\t\t\tassert.Equal(t, \"phone\", gjson.GetBytes(body, \"identity.schema_id\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"valid\", gjson.GetBytes(body, \"authentication_methods.0.provider\").String(), \"%s\", body)\n\n\t\t\tassert.Empty(t, postLoginWebhook.LastBody,\n\t\t\t\t\"post login hook should not have been called, because this was a registration flow\")\n\t\t\tpostRegistrationWebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\t})\n\n\tt.Run(\"case=login with Browser+JSON\", func(t *testing.T) {\n\t\tsubject = \"login-with-browser-json@ory.sh\"\n\t\tscope = []string{\"openid\"}\n\n\t\tt.Run(\"case=should pass login\", func(t *testing.T) {\n\t\t\tr := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeJSONRequest(t, \"valid\", action, url.Values{})\n\n\t\t\tassert.Equal(t, \"browser_location_change_required\", gjson.GetBytes(body, \"error.id\").String(), \"%s\", body)\n\n\t\t\tcontinuityCookie := res.Header.Get(\"Set-Cookie\")\n\t\t\tkey, val, ok := strings.Cut(continuityCookie, \"=\")\n\t\t\trequire.True(t, ok)\n\t\t\tassert.Equal(t, \"ory_kratos_continuity\", key)\n\t\t\tassert.NotEmpty(t, val)\n\t\t})\n\t})\n\n\tt.Run(\"suite=API with session token exchange code\", func(t *testing.T) {\n\t\tscope = []string{\"openid\"}\n\n\t\tloginOrRegister := func(t *testing.T, flowID uuid.UUID, code string, cookieJar *cookiejar.Jar) {\n\t\t\t_, err := exchangeCodeForToken(t, sessiontokenexchange.Codes{InitCode: code})\n\t\t\trequire.Error(t, err)\n\n\t\t\taction := assertFormValues(t, flowID, \"valid\")\n\t\t\treturnToURL := makeAPICodeFlowRequest(t, \"valid\", action, cookieJar)\n\t\t\treturnToCode := returnToURL.Query().Get(\"code\")\n\t\t\tassert.NotEmpty(t, code, \"code query param was empty in the return_to URL\")\n\n\t\t\tcodeResponse, err := exchangeCodeForToken(t, sessiontokenexchange.Codes{\n\t\t\t\tInitCode:     code,\n\t\t\t\tReturnToCode: returnToCode,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.NotEmpty(t, codeResponse.Token)\n\t\t\tassert.Equal(t, subject, gjson.GetBytes(codeResponse.Session.Identity.Traits, \"subject\").String())\n\t\t}\n\t\tperformRegistration := func(t *testing.T, cookieJar *cookiejar.Jar) {\n\t\t\tf := newAPIRegistrationFlow(t, returnTS.URL+\"?return_session_token_exchange_code=true&return_to=/app_code\", 1*time.Minute)\n\t\t\tloginOrRegister(t, f.ID, f.SessionTokenExchangeCode, cookieJar)\n\t\t}\n\t\tperformLogin := func(t *testing.T, cookieJar *cookiejar.Jar) {\n\t\t\tf := newAPILoginFlow(t, returnTS.URL+\"?return_session_token_exchange_code=true&return_to=/app_code\", 1*time.Minute)\n\t\t\tloginOrRegister(t, f.ID, f.SessionTokenExchangeCode, cookieJar)\n\t\t}\n\n\t\tfor _, tc := range []struct {\n\t\t\tname        string\n\t\t\tfirst, then func(*testing.T, *cookiejar.Jar)\n\t\t}{{\n\t\t\tname:  \"login-twice\",\n\t\t\tfirst: performLogin, then: performLogin,\n\t\t}, {\n\t\t\tname:  \"login-then-register\",\n\t\t\tfirst: performLogin, then: performRegistration,\n\t\t}, {\n\t\t\tname:  \"register-then-login\",\n\t\t\tfirst: performRegistration, then: performLogin,\n\t\t}, {\n\t\t\tname:  \"register-twice\",\n\t\t\tfirst: performRegistration, then: performRegistration,\n\t\t}} {\n\t\t\tt.Run(\"case=\"+tc.name, func(t *testing.T) {\n\t\t\t\tsubject = tc.name + \"-api-code-testing@ory.sh\"\n\t\t\t\ttc.first(t, nil)\n\t\t\t\ttc.then(t, nil)\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"case=should pass transient payload to registration webhook\", func(t *testing.T) {\n\t\t\tsubject = \"api-transient-payload-registration@ory.sh\"\n\t\t\ttransientPayload := `{\"data\": \"api-registration\"}`\n\n\t\t\twebhook := hooktest.NewServer()\n\t\t\tt.Cleanup(webhook.Close)\n\t\t\twebhook.SetConfig(t, conf.GetProvider(ctx), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeOIDC.String()))\n\n\t\t\tf := newAPIRegistrationFlow(t, returnTS.URL+\"?return_session_token_exchange_code=true&return_to=/app_code\", time.Minute)\n\t\t\taction := assertFormValues(t, f.ID, \"valid\")\n\n\t\t\tres, err := http.Post(action, \"application/json\", // #nosec G107 -- test code\n\t\t\t\tstrings.NewReader(fmt.Sprintf(`{\"method\":\"oidc\",\"provider\":\"valid\",\"transient_payload\":%s}`, transientPayload)))\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusUnprocessableEntity, res.StatusCode)\n\n\t\t\tvar changeLocation flow.BrowserLocationChangeRequiredError\n\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&changeLocation))\n\n\t\t\tres, err = testhelpers.NewClientWithCookieJar(t, nil, nil).Get(changeLocation.RedirectBrowserTo)\n\t\t\trequire.NoError(t, err)\n\n\t\t\treturnToCode := res.Request.URL.Query().Get(\"code\")\n\t\t\trequire.NotEmpty(t, returnToCode, \"code query param was empty in the return_to URL\")\n\n\t\t\tcodeResponse, err := exchangeCodeForToken(t, sessiontokenexchange.Codes{\n\t\t\t\tInitCode:     f.SessionTokenExchangeCode,\n\t\t\t\tReturnToCode: returnToCode,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.NotEmpty(t, codeResponse.Token)\n\n\t\t\twebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\n\t\tt.Run(\"case=should pass transient payload to login webhook\", func(t *testing.T) {\n\t\t\t// subject is the same as above, so this triggers a login (already registered)\n\t\t\ttransientPayload := `{\"data\": \"api-login\"}`\n\n\t\t\twebhook := hooktest.NewServer()\n\t\t\tt.Cleanup(webhook.Close)\n\t\t\twebhook.SetConfig(t, conf.GetProvider(ctx), config.HookStrategyKey(config.ViperKeySelfServiceLoginAfter, identity.CredentialsTypeOIDC.String()))\n\n\t\t\tf := newAPILoginFlow(t, returnTS.URL+\"?return_session_token_exchange_code=true&return_to=/app_code\", time.Minute)\n\t\t\taction := assertFormValues(t, f.ID, \"valid\")\n\n\t\t\tres, err := http.Post(action, \"application/json\", // #nosec G107 -- test code\n\t\t\t\tstrings.NewReader(fmt.Sprintf(`{\"method\":\"oidc\",\"provider\":\"valid\",\"transient_payload\":%s}`, transientPayload)))\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusUnprocessableEntity, res.StatusCode)\n\n\t\t\tvar changeLocation flow.BrowserLocationChangeRequiredError\n\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&changeLocation))\n\n\t\t\tres, err = testhelpers.NewClientWithCookieJar(t, nil, nil).Get(changeLocation.RedirectBrowserTo)\n\t\t\trequire.NoError(t, err)\n\n\t\t\treturnToCode := res.Request.URL.Query().Get(\"code\")\n\t\t\trequire.NotEmpty(t, returnToCode, \"code query param was empty in the return_to URL\")\n\n\t\t\tcodeResponse, err := exchangeCodeForToken(t, sessiontokenexchange.Codes{\n\t\t\t\tInitCode:     f.SessionTokenExchangeCode,\n\t\t\t\tReturnToCode: returnToCode,\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.NotEmpty(t, codeResponse.Token)\n\n\t\t\twebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\n\t\tt.Run(\"case=should return exchange code even if already authenticated\", func(t *testing.T) {\n\t\t\tsubject = \"existing-session-api-code-testing@ory.sh\"\n\t\t\tjar := x.Must(cookiejar.New(nil))\n\n\t\t\tt.Run(\"step=register and create a session\", func(t *testing.T) {\n\t\t\t\treturnTo := \"/foo\"\n\t\t\t\tr := newBrowserLoginFlow(t, fmt.Sprintf(\"%s?return_to=%s\", returnTS.URL, returnTo), time.Minute)\n\t\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\n\t\t\t\tres, body := makeRequestWithCookieJar(t, \"valid\", action, url.Values{}, jar, nil)\n\t\t\t\tassert.True(t, strings.HasSuffix(res.Request.URL.String(), returnTo))\n\t\t\t\tassertIdentity(t, res, body)\n\t\t\t})\n\n\t\t\tt.Run(\"step=perform login and get exchange code\", func(t *testing.T) {\n\t\t\t\tperformLogin(t, jar)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should use redirect_to URL on failure\", func(t *testing.T) {\n\t\t\tctx := context.Background()\n\t\t\tsubject = \"existing-subject-api-code-testing@ory.sh\"\n\n\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\ti.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\tIdentifiers: []string{subject},\n\t\t\t})\n\t\t\ti.Traits = identity.Traits(`{\"subject\":\"` + subject + `\"}`)\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, i))\n\n\t\t\tf := newAPILoginFlow(t, returnTS.URL+\"?return_session_token_exchange_code=true&return_to=/app_code\", 1*time.Minute)\n\n\t\t\t_, err := exchangeCodeForToken(t, sessiontokenexchange.Codes{InitCode: f.SessionTokenExchangeCode})\n\t\t\trequire.Error(t, err)\n\n\t\t\taction := assertFormValues(t, f.ID, \"valid\")\n\t\t\treturnToURL := makeAPICodeFlowRequest(t, \"valid\", action, nil)\n\t\t\treturnedFlow := returnToURL.Query().Get(\"flow\")\n\n\t\t\trequire.NotEmpty(t, returnedFlow, \"flow query param was empty in the return_to URL\")\n\t\t\tloginFlow, err := reg.LoginFlowPersister().GetLoginFlow(ctx, uuid.FromStringOrNil(returnedFlow))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, text.InfoSelfServiceLoginLink, loginFlow.UI.Messages[0].ID)\n\t\t})\n\t})\n\n\tt.Run(\"case=submit id_token during registration or login\", func(t *testing.T) {\n\t\tviperSetProviderConfig(\n\t\t\tt,\n\t\t\tconf,\n\t\t\tnewOIDCProvider(t, ts, remotePublic, remoteAdmin, \"valid\"),\n\t\t\toidc.Configuration{\n\t\t\t\tProvider:     \"test-provider\",\n\t\t\t\tID:           \"test-provider\",\n\t\t\t\tClientID:     invalid.ClientID,\n\t\t\t\tClientSecret: invalid.ClientSecret,\n\t\t\t\tIssuerURL:    remotePublic + \"/\",\n\t\t\t\tMapper:       \"file://./stub/oidc.facebook.jsonnet\",\n\t\t\t},\n\t\t)\n\t\toidc.RegisterTestProvider(t, \"test-provider\")\n\n\t\tcl := http.Client{}\n\n\t\ttype testCase struct {\n\t\t\tname     string\n\t\t\tidToken  string\n\t\t\tprovider string\n\t\t\tv        func(string, string, string) url.Values\n\t\t\texpect   func(t *testing.T, res *http.Response, body []byte)\n\t\t}\n\n\t\tprep := func(tc *testCase) (provider string, token string, nonce string) {\n\t\t\tprovider = tc.provider\n\t\t\tif provider == \"\" {\n\t\t\t\tprovider = \"test-provider\"\n\t\t\t}\n\t\t\ttoken = tc.idToken\n\t\t\ttoken = strings.ReplaceAll(token, \"{{sub}}\", testhelpers.RandomEmail())\n\t\t\tnonce = randx.MustString(16, randx.Alpha)\n\t\t\ttoken = strings.ReplaceAll(token, \"{{nonce}}\", nonce)\n\t\t\treturn\n\t\t}\n\n\t\tfor _, tc := range []testCase{\n\t\t\t{\n\t\t\t\tname:     \"should fail if provider does not support id_token submission\",\n\t\t\t\tidToken:  \"error\",\n\t\t\t\tprovider: \"valid\",\n\t\t\t\texpect: func(t *testing.T, res *http.Response, body []byte) {\n\t\t\t\t\trequire.Equal(t, \"The provider generic does not support id_token verification\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:    \"should fail because id_token is invalid\",\n\t\t\t\tidToken: \"error\",\n\t\t\t\texpect: func(t *testing.T, res *http.Response, body []byte) {\n\t\t\t\t\trequire.Equal(t, \"Could not verify id_token\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t\trequire.Equal(t, \"stub error\", gjson.GetBytes(body, \"error.message\").String(), \"%s\", body)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:    \"should fail because claims are invalid\",\n\t\t\t\tidToken: \"{}\",\n\t\t\t\texpect: func(t *testing.T, res *http.Response, body []byte) {\n\t\t\t\t\trequire.Equal(t, \"The id_token claims were invalid\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"should fail if no nonce is included in the id_token\",\n\t\t\t\tidToken: `{\n\t\t\t\t\t\"iss\": \"https://appleid.apple.com\",\n\t\t\t\t\t\"sub\": \"{{sub}}\"\n\t\t\t\t}`,\n\t\t\t\texpect: func(t *testing.T, res *http.Response, body []byte) {\n\t\t\t\t\trequire.Equal(t, \"No nonce was included in the id_token but is required by the provider\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"should fail if no nonce is supplied in request\",\n\t\t\t\tidToken: `{\n\t\t\t\t\t\"iss\": \"https://appleid.apple.com\",\n\t\t\t\t\t\"sub\": \"{{sub}}\",\n\t\t\t\t\t\"nonce\": \"{{nonce}}\"\n\t\t\t\t}`,\n\t\t\t\tv: func(provider, token, _ string) url.Values {\n\t\t\t\t\treturn url.Values{\n\t\t\t\t\t\t\"id_token\": {token},\n\t\t\t\t\t\t\"provider\": {provider},\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\texpect: func(t *testing.T, res *http.Response, body []byte) {\n\t\t\t\t\trequire.Equal(t, \"No nonce was provided but is required by the provider\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"should pass if claims are valid\",\n\t\t\t\tidToken: `{\n\t\t\t\t\t\"iss\": \"https://appleid.apple.com\",\n\t\t\t\t\t\"sub\": \"{{sub}}\",\n\t\t\t\t\t\"nonce\": \"{{nonce}}\"\n\t\t\t\t}`,\n\t\t\t\texpect: func(t *testing.T, res *http.Response, body []byte) {\n\t\t\t\t\trequire.NotEmpty(t, gjson.GetBytes(body, \"session_token\").String(), \"%s\", body)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"nonce mismatch\",\n\t\t\t\tidToken: `{\n\t\t\t\t\t\"iss\": \"https://appleid.apple.com\",\n\t\t\t\t\t\"sub\": \"{{sub}}\",\n\t\t\t\t\t\"nonce\": \"random-nonce\"\n\t\t\t\t}`,\n\t\t\t\texpect: func(t *testing.T, res *http.Response, body []byte) {\n\t\t\t\t\trequire.Equal(t, \"The supplied nonce does not match the nonce from the id_token\", gjson.GetBytes(body, \"error.reason\").String(), \"%s\", body)\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\ttc := tc\n\t\t\tt.Run(fmt.Sprintf(\"flow=registration/case=%s\", tc.name), func(t *testing.T) {\n\t\t\t\tf := newAPIRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\tprovider, token, nonce := prep(&tc)\n\t\t\t\taction := assertFormValues(t, f.ID, \"test-provider\")\n\t\t\t\tv := url.Values{\n\t\t\t\t\t\"id_token\":       {token},\n\t\t\t\t\t\"provider\":       {provider},\n\t\t\t\t\t\"id_token_nonce\": {nonce},\n\t\t\t\t}\n\t\t\t\tif tc.v != nil {\n\t\t\t\t\tv = tc.v(provider, token, nonce)\n\t\t\t\t}\n\t\t\t\tres, err := cl.PostForm(action, v)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\ttc.expect(t, res, body)\n\t\t\t})\n\n\t\t\tt.Run(fmt.Sprintf(\"flow=login/case=%s\", tc.name), func(t *testing.T) {\n\t\t\t\tprovider, token, nonce := prep(&tc)\n\t\t\t\trf := newAPIRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\taction := assertFormValues(t, rf.ID, \"test-provider\")\n\t\t\t\tv := url.Values{\n\t\t\t\t\t\"id_token\":       {token},\n\t\t\t\t\t\"provider\":       {provider},\n\t\t\t\t\t\"id_token_nonce\": {nonce},\n\t\t\t\t}\n\t\t\t\tif tc.v != nil {\n\t\t\t\t\tv = tc.v(provider, token, nonce)\n\t\t\t\t}\n\t\t\t\t_, err := cl.PostForm(action, v)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tlf := newAPILoginFlow(t, returnTS.URL, time.Minute)\n\t\t\t\taction = assertFormValues(t, lf.ID, \"test-provider\")\n\n\t\t\t\tres, err := cl.PostForm(action, v)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\ttc.expect(t, res, body)\n\t\t\t})\n\n\t\t\tt.Run(fmt.Sprintf(\"flow=login_without_registration/case=%s\", tc.name), func(t *testing.T) {\n\t\t\t\tprovider, token, nonce := prep(&tc)\n\t\t\t\trf := newAPIRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\taction := assertFormValues(t, rf.ID, \"test-provider\")\n\n\t\t\t\tv := url.Values{\n\t\t\t\t\t\"id_token\":       {token},\n\t\t\t\t\t\"provider\":       {provider},\n\t\t\t\t\t\"id_token_nonce\": {nonce},\n\t\t\t\t}\n\t\t\t\tif tc.v != nil {\n\t\t\t\t\tv = tc.v(provider, token, nonce)\n\t\t\t\t}\n\t\t\t\t_, err := cl.PostForm(action, v)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tlf := newAPIRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\taction = assertFormValues(t, lf.ID, \"test-provider\")\n\n\t\t\t\tres, err := cl.PostForm(action, v)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\ttc.expect(t, res, body)\n\t\t\t})\n\n\t\t\tt.Run(fmt.Sprintf(\"flow=login_with_return_session_token_exchange_code/case=%s\", tc.name), func(t *testing.T) {\n\t\t\t\tprovider, token, nonce := prep(&tc)\n\t\t\t\tlf := newAPILoginFlow(t, returnTS.URL+\"?return_session_token_exchange_code=true&return_to=/app_code\", time.Minute)\n\t\t\t\taction := assertFormValues(t, lf.ID, \"test-provider\")\n\t\t\t\tv := url.Values{\n\t\t\t\t\t\"id_token\":       {token},\n\t\t\t\t\t\"provider\":       {provider},\n\t\t\t\t\t\"id_token_nonce\": {nonce},\n\t\t\t\t}\n\t\t\t\tif tc.v != nil {\n\t\t\t\t\tv = tc.v(provider, token, nonce)\n\t\t\t\t}\n\t\t\t\tres, err := cl.PostForm(action, v)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\ttc.expect(t, res, body)\n\t\t\t})\n\n\t\t\tt.Run(fmt.Sprintf(\"flow=registration_with_return_session_token_exchange_code/case=%s\", tc.name), func(t *testing.T) {\n\t\t\t\tprovider, token, nonce := prep(&tc)\n\t\t\t\tlf := newAPIRegistrationFlow(t, returnTS.URL+\"?return_session_token_exchange_code=true&return_to=/app_code\", time.Minute)\n\t\t\t\taction := assertFormValues(t, lf.ID, \"test-provider\")\n\t\t\t\tv := url.Values{\n\t\t\t\t\t\"id_token\":       {token},\n\t\t\t\t\t\"provider\":       {provider},\n\t\t\t\t\t\"id_token_nonce\": {nonce},\n\t\t\t\t}\n\t\t\t\tif tc.v != nil {\n\t\t\t\t\tv = tc.v(provider, token, nonce)\n\t\t\t\t}\n\t\t\t\tres, err := cl.PostForm(action, v)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\ttc.expect(t, res, body)\n\t\t\t})\n\n\t\t}\n\t})\n\n\tt.Run(\"case=login without registered account with return_to\", func(t *testing.T) {\n\t\tt.Run(\"case=should pass login\", func(t *testing.T) {\n\t\t\tsubject = \"login-without-register-return-to@ory.sh\"\n\t\t\tscope = []string{\"openid\"}\n\t\t\treturnTo := \"/foo\"\n\t\t\tr := newBrowserLoginFlow(t, fmt.Sprintf(\"%s?return_to=%s\", returnTS.URL, returnTo), time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\t\tassert.True(t, strings.HasSuffix(res.Request.URL.String(), returnTo))\n\t\t\tassertIdentity(t, res, body)\n\t\t})\n\n\t\tt.Run(\"case=should pass login and carry over login_challenge to registration\", func(t *testing.T) {\n\t\t\tsubject = \"login_challenge_carry_over@ory.sh\"\n\t\t\tscope = []string{\"openid\"}\n\t\t\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, \"http://fake-hydra\")\n\n\t\t\treg.SetHydra(hydra.NewFake())\n\t\t\tr := newBrowserLoginFlow(t, fmt.Sprintf(\"%s?login_challenge=%s\", returnTS.URL, hydra.FakeValidLoginChallenge), time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tfv := url.Values{}\n\t\t\tfv.Set(\"provider\", \"valid\")\n\t\t\tres, err := testhelpers.NewClientWithCookieJar(t, nil, nil).PostForm(action, fv)\n\t\t\trequire.NoError(t, err)\n\t\t\t// Expect to be returned to the hydra instance, that instantiated the request\n\t\t\tassert.Equal(t, hydra.FakePostLoginURL, strings.TrimSuffix(res.Request.URL.String(), \"/\"))\n\t\t})\n\t})\n\n\tt.Run(\"case=register and register again but login\", func(t *testing.T) {\n\t\tsubject = \"register-twice@ory.sh\"\n\t\tscope = []string{\"openid\"}\n\n\t\tt.Run(\"case=should pass registration\", func(t *testing.T) {\n\t\t\tpostRegistrationWebhook := hooktest.NewServer()\n\t\t\tt.Cleanup(postRegistrationWebhook.Close)\n\t\t\tpostRegistrationWebhook.SetConfig(t, conf.GetProvider(ctx), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeOIDC.String()))\n\n\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL+\"?identity_schema=phone\", time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\ttransientPayload := `{\"data\": \"registration-one\"}`\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\"transient_payload\": {transientPayload}})\n\t\t\tassertIdentity(t, res, body)\n\t\t\tassert.Equal(t, \"phone\", gjson.GetBytes(body, \"identity.schema_id\").String(), \"%s\", body)\n\t\t\tpostRegistrationWebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\n\t\tt.Run(\"case=should pass second time registration\", func(t *testing.T) {\n\t\t\tpostLoginWebhook := hooktest.NewServer()\n\t\t\tt.Cleanup(postLoginWebhook.Close)\n\t\t\tpostLoginWebhook.SetConfig(t, conf.GetProvider(ctx), config.HookStrategyKey(config.ViperKeySelfServiceLoginAfter, identity.CredentialsTypeOIDC.String()))\n\n\t\t\tr := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\ttransientPayload := `{\"data\": \"registration-two\"}`\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\"transient_payload\": {transientPayload}})\n\t\t\tassertIdentity(t, res, body)\n\t\t\tpostLoginWebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\n\t\tt.Run(\"case=should pass third time registration with return to\", func(t *testing.T) {\n\t\t\tpostLoginWebhook := hooktest.NewServer()\n\t\t\tt.Cleanup(postLoginWebhook.Close)\n\t\t\tpostLoginWebhook.SetConfig(t, conf.GetProvider(ctx), config.HookStrategyKey(config.ViperKeySelfServiceLoginAfter, identity.CredentialsTypeOIDC.String()))\n\n\t\t\treturnTo := \"/foo\"\n\t\t\tr := newBrowserLoginFlow(t, fmt.Sprintf(\"%s?return_to=%s\", returnTS.URL, returnTo), time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\ttransientPayload := `{\"data\": \"registration-three\"}`\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\"transient_payload\": {transientPayload}})\n\t\t\tassert.True(t, strings.HasSuffix(res.Request.URL.String(), returnTo))\n\t\t\tassertIdentity(t, res, body)\n\t\t\tpostLoginWebhook.AssertTransientPayload(t, transientPayload)\n\t\t})\n\t})\n\n\tt.Run(\"case=register, merge, and complete data\", func(t *testing.T) {\n\t\tfor _, tc := range []struct{ name, provider string }{\n\t\t\t{name: \"idtoken\", provider: \"valid\"},\n\t\t\t{name: \"userinfo\", provider: \"claimsViaUserInfo\"},\n\t\t\t{name: \"disable-pkce\", provider: \"neverPKCE\"},\n\t\t\t{name: \"auto-pkce\", provider: \"autoPKCE\"},\n\t\t\t{name: \"force-pkce\", provider: \"forcePKCE\"},\n\t\t} {\n\t\t\tsubject = fmt.Sprintf(\"incomplete-data@%s.ory.sh\", tc.name)\n\t\t\tscope = []string{\"openid\"}\n\t\t\tclaims = idTokenClaims{}\n\t\t\tclaims.traits.website = \"https://www.ory.sh/kratos\"\n\t\t\tclaims.traits.groups = []string{\"group1\", \"group2\"}\n\t\t\tclaims.metadataPublic.picture = \"picture.png\"\n\t\t\tclaims.metadataAdmin.phoneNumber = \"911\"\n\n\t\t\tt.Run(fmt.Sprintf(\"ClaimsSource=%s\", tc.name), func(t *testing.T) {\n\t\t\t\tt.Run(\"case=should fail registration on first attempt\", func(t *testing.T) {\n\t\t\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, tc.provider)\n\t\t\t\t\tres, body := makeRequest(t, tc.provider, action, url.Values{\"traits.name\": {\"i\"}})\n\t\t\t\t\trequire.Contains(t, res.Request.URL.String(), uiTS.URL, \"%s\", body)\n\n\t\t\t\t\tassert.Equal(t, \"length must be >= 2, but got 1\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.name).messages.0.text\").String(), \"%s\", body) // make sure the field is being echoed\n\t\t\t\t\tassert.Equal(t, \"traits.name\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.name).attributes.name\").String(), \"%s\", body)                    // make sure the field is being echoed\n\t\t\t\t\tassert.Equal(t, \"i\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.name).attributes.value\").String(), \"%s\", body)                             // make sure the field is being echoed\n\t\t\t\t\tassert.Equal(t, \"https://www.ory.sh/kratos\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.website).attributes.value\").String(), \"%s\", body)  // make sure the field is being echoed\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should pass registration with valid data\", func(t *testing.T) {\n\t\t\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, tc.provider)\n\t\t\t\t\tres, body := makeRequest(t, tc.provider, action, url.Values{\"traits.name\": {\"valid-name\"}})\n\t\t\t\t\tassertIdentity(t, res, body)\n\t\t\t\t\tassert.Equal(t, \"https://www.ory.sh/kratos\", gjson.GetBytes(body, \"identity.traits.website\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"valid-name\", gjson.GetBytes(body, \"identity.traits.name\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"[\\\"group1\\\",\\\"group2\\\"]\", gjson.GetBytes(body, \"identity.traits.groups\").String(), \"%s\", body)\n\t\t\t\t})\n\n\t\t\t\t// We need to make sure we use a different subject for the next test cases.\n\t\t\t\tsubject = \"multi-\" + subject\n\n\t\t\t\tt.Run(\"case=should fail registration on first attempt with multi-schema select\", func(t *testing.T) {\n\t\t\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL+\"?identity_schema=extra_data\", time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, tc.provider)\n\t\t\t\t\tres, body := makeRequest(t, tc.provider, action, url.Values{\"traits.name\": {\"i\"}})\n\t\t\t\t\trequire.Contains(t, res.Request.URL.String(), uiTS.URL, \"%s\", body)\n\n\t\t\t\t\tassert.Equal(t, \"length must be >= 2, but got 1\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.name).messages.0.text\").String(), \"%s\", body) // make sure the field is being echoed\n\t\t\t\t\tassert.Equal(t, \"traits.name\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.name).attributes.name\").String(), \"%s\", body)                    // make sure the field is being echoed\n\t\t\t\t\tassert.Equal(t, \"traits.extra_data\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.extra_data).attributes.name\").String(), \"%s\", body)        // make sure the field is being echoed\n\t\t\t\t\tassert.Equal(t, \"i\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.name).attributes.value\").String(), \"%s\", body)                             // make sure the field is being echoed\n\t\t\t\t\tassert.Equal(t, \"https://www.ory.sh/kratos\", gjson.GetBytes(body, \"ui.nodes.#(attributes.name==traits.website).attributes.value\").String(), \"%s\", body)  // make sure the field is being echoed\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should pass registration with selected schema with valid data\", func(t *testing.T) {\n\t\t\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL+\"?identity_schema=extra_data\", time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, tc.provider)\n\t\t\t\t\tres, body := makeRequest(t, tc.provider, action, url.Values{\"traits.name\": {\"valid-name-2\"}, \"traits.extra_data\": {\"extra-data\"}})\n\t\t\t\t\tassertIdentity(t, res, body)\n\t\t\t\t\tassert.Equal(t, \"https://www.ory.sh/kratos\", gjson.GetBytes(body, \"identity.traits.website\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"valid-name-2\", gjson.GetBytes(body, \"identity.traits.name\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"extra-data\", gjson.GetBytes(body, \"identity.traits.extra_data\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, \"[\\\"group1\\\",\\\"group2\\\"]\", gjson.GetBytes(body, \"identity.traits.groups\").String(), \"%s\", body)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tfor _, loginHintsEnabled := range []bool{true, false} {\n\t\tt.Run(\"login-hints-enabled=\"+strconv.FormatBool(loginHintsEnabled), func(t *testing.T) {\n\t\t\tt.Run(\"case=should fail to register and return fresh login flow if email is already being used by password credentials\", func(t *testing.T) {\n\t\t\t\tsubject = \"email-exist-with-password-strategy-lh-\" + strconv.FormatBool(loginHintsEnabled) + \"@ory.sh\"\n\t\t\t\tscope = []string{\"openid\"}\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationLoginHints, strconv.FormatBool(loginHintsEnabled))\n\n\t\t\t\tt.Run(\"case=create password identity\", func(t *testing.T) {\n\t\t\t\t\ti := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\t\ti.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\t\t\tIdentifiers: []string{subject},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\": \"foo\"}`),\n\t\t\t\t\t})\n\t\t\t\t\ti.Traits = identity.Traits(`{\"subject\":\"` + subject + `\"}`)\n\n\t\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should fail registration\", func(t *testing.T) {\n\t\t\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\t\t\t_, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"expires_at\", \"updated_at\", \"issued_at\", \"id\", \"created_at\", \"ui.action\", findCsrfTokenPath(t, body), \"request_url\"), snapshotx.ExceptNestedKeys(\"newLoginUrl\", \"new_login_url\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should fail registration id_first strategy enabled\", func(t *testing.T) {\n\t\t\t\t\trequire.NoError(t, conf.Set(ctx, config.ViperKeySelfServiceLoginFlowStyle, \"identifier_first\"))\n\t\t\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\t\t\t_, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"expires_at\", \"updated_at\", \"issued_at\", \"id\", \"created_at\", \"ui.action\", findCsrfTokenPath(t, body), \"request_url\"), snapshotx.ExceptNestedKeys(\"newLoginUrl\", \"new_login_url\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should fail login\", func(t *testing.T) {\n\t\t\t\t\tr := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\t\t\t_, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"expires_at\", \"updated_at\", \"issued_at\", \"id\", \"created_at\", \"ui.action\", findCsrfTokenPath(t, body), \"request_url\"), snapshotx.ExceptNestedKeys(\"newLoginUrl\", \"new_login_url\"))\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=should fail to register and return fresh login flow if email is already being used by oidc credentials\", func(t *testing.T) {\n\t\t\t\tsubject = \"email-exist-with-oidc-strategy-lh-\" + strconv.FormatBool(loginHintsEnabled) + \"@ory.sh\"\n\t\t\t\tscope = []string{\"openid\"}\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationLoginHints, strconv.FormatBool(loginHintsEnabled))\n\n\t\t\t\tt.Run(\"case=create oidc identity\", func(t *testing.T) {\n\t\t\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, \"secondProvider\")\n\t\t\t\t\tres, _ := makeRequest(t, \"secondProvider\", action, url.Values{})\n\t\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should fail registration\", func(t *testing.T) {\n\t\t\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\t\t\t_, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"expires_at\", \"updated_at\", \"issued_at\", \"id\", \"created_at\", \"ui.action\", findCsrfTokenPath(t, body), \"request_url\"), snapshotx.ExceptNestedKeys(\"newLoginUrl\", \"new_login_url\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should fail registration id_first strategy enabled\", func(t *testing.T) {\n\t\t\t\t\trequire.NoError(t, conf.Set(ctx, config.ViperKeySelfServiceLoginFlowStyle, \"identifier_first\"))\n\t\t\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\t\t\t_, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"expires_at\", \"updated_at\", \"issued_at\", \"id\", \"created_at\", \"ui.action\", findCsrfTokenPath(t, body), \"request_url\"), snapshotx.ExceptNestedKeys(\"newLoginUrl\", \"new_login_url\"))\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=should fail login\", func(t *testing.T) {\n\t\t\t\t\tr := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\t\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\t\t\t_, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths(\"expires_at\", \"updated_at\", \"issued_at\", \"id\", \"created_at\", \"ui.action\", findCsrfTokenPath(t, body), \"request_url\"), snapshotx.ExceptNestedKeys(\"newLoginUrl\", \"new_login_url\"))\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"case=should redirect to default return ts when sending authenticated login flow without forced flag\", func(t *testing.T) {\n\t\tsubject = \"no-reauth-login@ory.sh\"\n\t\tscope = []string{\"openid\"}\n\n\t\tfv := url.Values{\"traits.name\": {\"valid-name\"}}\n\t\tjar, _ := cookiejar.New(nil)\n\t\tr1 := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\tres1, body1 := makeRequestWithCookieJar(t, \"valid\", assertFormValues(t, r1.ID, \"valid\"), fv, jar, nil)\n\t\tassertIdentity(t, res1, body1)\n\t\tr2 := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\tres2, body2 := makeRequestWithCookieJar(t, \"valid\", assertFormValues(t, r2.ID, \"valid\"), fv, jar, nil)\n\t\tassertIdentity(t, res2, body2)\n\t\tassert.Equal(t, body1, body2)\n\t})\n\n\tt.Run(\"case=should reauthenticate when sending authenticated login flow with forced flag\", func(t *testing.T) {\n\t\tsubject = \"reauth-login@ory.sh\"\n\t\tscope = []string{\"openid\"}\n\n\t\tfv := url.Values{\"traits.name\": {\"valid-name\"}}\n\t\tjar, _ := cookiejar.New(nil)\n\t\tr1 := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\tres1, body1 := makeRequestWithCookieJar(t, \"valid\", assertFormValues(t, r1.ID, \"valid\"), fv, jar, nil)\n\t\tassertIdentity(t, res1, body1)\n\t\tr2 := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\trequire.NoError(t, reg.LoginFlowPersister().ForceLoginFlow(context.Background(), r2.ID))\n\t\tres2, body2 := makeRequestWithCookieJar(t, \"valid\", assertFormValues(t, r2.ID, \"valid\"), fv, jar, nil)\n\t\tassertIdentity(t, res2, body2)\n\t\tassert.NotEqual(t, gjson.GetBytes(body1, \"id\"), gjson.GetBytes(body2, \"id\"))\n\t\tauthAt1, err := time.Parse(time.RFC3339, gjson.GetBytes(body1, \"authenticated_at\").String())\n\t\trequire.NoError(t, err)\n\t\tauthAt2, err := time.Parse(time.RFC3339, gjson.GetBytes(body2, \"authenticated_at\").String())\n\t\trequire.NoError(t, err)\n\t\t// authenticated at is newer in the second body\n\t\tassert.Greater(t, authAt2.Sub(authAt1).Milliseconds(), int64(0), \"%s - %s : %s - %s\", authAt2, authAt1, body2, body1)\n\t})\n\n\tt.Run(\"case=upstream parameters should be passed on to provider\", func(t *testing.T) {\n\t\tsubject = \"oidc-upstream-parameters@ory.sh\"\n\t\tscope = []string{\"openid\", \"offline\"}\n\n\t\t// We need to disable redirects because the upstream parameters are only passed on to the provider\n\t\tc := &http.Client{\n\t\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t},\n\t\t}\n\n\t\tt.Run(\"case=should pass when registering\", func(t *testing.T) {\n\t\t\tf := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, f.ID, \"valid\")\n\n\t\t\tfv := url.Values{}\n\n\t\t\tfv.Set(\"provider\", \"valid\")\n\t\t\tfv.Set(\"upstream_parameters.login_hint\", \"oidc-upstream-parameters@ory.sh\")\n\t\t\tfv.Set(\"upstream_parameters.hd\", \"ory.sh\")\n\t\t\tfv.Set(\"upstream_parameters.prompt\", \"select_account\")\n\t\t\tfv.Set(\"upstream_parameters.auth_type\", \"reauthenticate\")\n\n\t\t\tres, err := c.PostForm(action, fv)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\n\t\t\tloc, err := res.Location()\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.Equal(t, \"oidc-upstream-parameters@ory.sh\", loc.Query().Get(\"login_hint\"))\n\t\t\trequire.Equal(t, \"ory.sh\", loc.Query().Get(\"hd\"))\n\t\t\trequire.Equal(t, \"select_account\", loc.Query().Get(\"prompt\"))\n\t\t\trequire.Equal(t, \"reauthenticate\", loc.Query().Get(\"auth_type\"))\n\t\t})\n\n\t\tt.Run(\"case=should pass when logging in\", func(t *testing.T) {\n\t\t\tf := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\n\t\t\taction := assertFormValues(t, f.ID, \"valid\")\n\n\t\t\tfv := url.Values{}\n\n\t\t\tfv.Set(\"provider\", \"valid\")\n\t\t\tfv.Set(\"upstream_parameters.login_hint\", \"oidc-upstream-parameters@ory.sh\")\n\t\t\tfv.Set(\"upstream_parameters.hd\", \"ory.sh\")\n\t\t\tfv.Set(\"upstream_parameters.prompt\", \"select_account\")\n\n\t\t\tres, err := c.PostForm(action, fv)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\n\t\t\tloc, err := res.Location()\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.Equal(t, \"oidc-upstream-parameters@ory.sh\", loc.Query().Get(\"login_hint\"))\n\t\t\trequire.Equal(t, \"ory.sh\", loc.Query().Get(\"hd\"))\n\t\t\trequire.Equal(t, \"select_account\", loc.Query().Get(\"prompt\"))\n\t\t})\n\n\t\tt.Run(\"case=should ignore invalid parameters when logging in\", func(t *testing.T) {\n\t\t\tf := newBrowserLoginFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, f.ID, \"valid\")\n\n\t\t\tfv := url.Values{}\n\t\t\tfv.Set(\"upstream_parameters.lol\", \"invalid\")\n\n\t\t\tres, err := c.PostForm(action, fv)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusSeeOther, res.StatusCode)\n\n\t\t\tloc, err := res.Location()\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// upstream parameters that are not on the allow list will be ignored and not passed on to the upstream provider.\n\t\t\trequire.Empty(t, loc.Query().Get(\"lol\"))\n\t\t})\n\n\t\tt.Run(\"case=should ignore invalid parameters when registering\", func(t *testing.T) {\n\t\t\tf := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, f.ID, \"valid\")\n\n\t\t\tfv := url.Values{}\n\t\t\tfv.Set(\"upstream_parameters.lol\", \"invalid\")\n\n\t\t\tres, err := c.PostForm(action, fv)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusFound, res.StatusCode)\n\n\t\t\tloc, err := res.Location()\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// upstream parameters that are not on the allow list will be ignored and not passed on to the upstream provider.\n\t\t\trequire.Empty(t, loc.Query().Get(\"lol\"))\n\t\t})\n\t})\n\n\tt.Run(\"case=verified addresses should be respected\", func(t *testing.T) {\n\t\tscope = []string{\"openid\"}\n\n\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration-verifiable-email.schema.json\")\n\n\t\tassertVerifiedEmail := func(t *testing.T, body []byte, verified bool) {\n\t\t\tassert.Len(t, gjson.GetBytes(body, \"identity.verifiable_addresses\").Array(), 1, \"%s\", body)\n\t\t\tassert.Equal(t, \"email\", gjson.GetBytes(body, \"identity.verifiable_addresses.0.via\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, subject, gjson.GetBytes(body, \"identity.verifiable_addresses.0.value\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, verified, gjson.GetBytes(body, \"identity.verifiable_addresses.0.verified\").Bool(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"case=should have verified address when subject matches\", func(t *testing.T) {\n\t\t\tsubject = \"verified-email@ory.sh\"\n\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{})\n\t\t\tassertIdentity(t, res, body)\n\t\t\tassertVerifiedEmail(t, body, true)\n\t\t})\n\n\t\tt.Run(\"case=should have verified address when subject matches after normalization\", func(t *testing.T) {\n\t\t\tsubject = \" Denormalized-Verified-Email@ory.sh \"\n\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\"traits.subject\": {\"denormalized-verified-EMAIL@ory.sh\"}})\n\t\t\tsubject = \"denormalized-verified-EMAIL@ory.sh\"\n\t\t\tassertIdentity(t, res, body)\n\t\t\tsubject = \"denormalized-verified-email@ory.sh\"\n\t\t\tassertVerifiedEmail(t, body, true)\n\t\t})\n\n\t\tt.Run(\"case=should have unverified address when subject does not match\", func(t *testing.T) {\n\t\t\tsubject = \"changed-verified-email@ory.sh\"\n\t\t\tr := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)\n\t\t\taction := assertFormValues(t, r.ID, \"valid\")\n\t\t\tres, body := makeRequest(t, \"valid\", action, url.Values{\"traits.subject\": {\"unverified-email@ory.sh\"}})\n\t\t\tsubject = \"unverified-email@ory.sh\"\n\t\t\tassertIdentity(t, res, body)\n\t\t\tassertVerifiedEmail(t, body, false)\n\t\t})\n\t})\n\n\tloginWithOIDC := func(t *testing.T, c *http.Client, flowID uuid.UUID, provider string) (*http.Response, []byte) {\n\t\taction := assertFormValues(t, flowID, provider)\n\t\tres, err := c.PostForm(action, url.Values{\"provider\": {provider}})\n\t\trequire.NoError(t, err, action)\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.NoError(t, err)\n\t\treturn res, body\n\t}\n\n\tcheckCredentialsLinked := func(t *testing.T, res *http.Response, body []byte, identityID uuid.UUID, provider string) {\n\t\tassert.Contains(t, res.Request.URL.String(), returnTS.URL, \"%s\", body)\n\t\tassert.Equal(t, strings.ToLower(subject), gjson.GetBytes(body, \"identity.traits.subject\").String(), \"%s\", body)\n\t\ti, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(ctx, identityID)\n\t\trequire.NoError(t, err)\n\t\tassert.NotEmpty(t, i.Credentials[\"oidc\"], \"%+v\", i.Credentials)\n\t\tassert.True(t, gjson.GetBytes(i.Credentials[\"oidc\"].Config, fmt.Sprintf(\"providers.#(provider=%q)\", provider)).Exists(),\n\t\t\t\"%s\", string(i.Credentials[\"oidc\"].Config[:]))\n\t\tassert.Contains(t, gjson.GetBytes(body, \"authentication_methods\").String(), \"oidc\", \"%s\", body)\n\t}\n\n\tt.Run(\"case=registration should start new login flow if duplicate credentials detected\", func(t *testing.T) {\n\t\trequire.NoError(t, reg.Config().Set(ctx, config.ViperKeySelfServiceRegistrationLoginHints, true))\n\n\t\tt.Run(\"case=second login is password\", func(t *testing.T) {\n\t\t\tsubject = \"new-login-if-email-exist-with-password-strategy@ory.sh\"\n\t\t\tsubject2 := \"new-login-subject2@ory.sh\"\n\t\t\tscope = []string{\"openid\"}\n\t\t\tpassword := \"lwkj52sdkjf\" // #nosec G101\n\n\t\t\tvar i *identity.Identity\n\t\t\tt.Run(\"step=create password identity\", func(t *testing.T) {\n\t\t\t\ti = identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\tp, err := reg.Hasher(ctx).Generate(ctx, []byte(password))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\ti.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\t\tIdentifiers: []string{subject},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t\t\t})\n\t\t\t\ti.Traits = identity.Traits(`{\"subject\":\"` + subject + `\"}`)\n\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\n\t\t\t\ti2 := identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\ti2.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\t\tIdentifiers: []string{subject2},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t\t\t})\n\t\t\t\ti2.Traits = identity.Traits(`{\"subject\":\"` + subject2 + `\"}`)\n\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i2))\n\t\t\t})\n\n\t\t\tclient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\t\t\tloginFlow := newLoginFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser)\n\t\t\tloginFlow.OrganizationID = uuid.NullUUID{UUID: orgID, Valid: true}\n\t\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), loginFlow))\n\n\t\t\tvar linkingLoginFlow struct {\n\t\t\t\tID        string\n\t\t\t\tUIAction  string\n\t\t\t\tCSRFToken string\n\t\t\t}\n\n\t\t\t// To test that the subject is normalized properly\n\t\t\tsubject = strings.ToUpper(subject)\n\n\t\t\tt.Run(\"step=should fail login and start a new flow\", func(t *testing.T) {\n\t\t\t\tres, body := loginWithOIDC(t, client, loginFlow.ID, \"valid\")\n\t\t\t\tassert.True(t, res.Request.URL.Query().Has(\"no_org_ui\"))\n\t\t\t\tassertUIError(t, res, body, \"You tried to sign in with \\\"new-login-if-email-exist-with-password-strategy@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"new-login-if-email-exist-with-password-strategy@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\")\n\t\t\t\tassert.True(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier)\").Exists(), \"%s\", body)\n\t\t\t\tassert.True(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==password)\").Exists(), \"%s\", body)\n\t\t\t\tassert.Equal(t, \"new-login-if-email-exist-with-password-strategy@ory.sh\", gjson.GetBytes(body, \"ui.messages.#(id==1010016).context.duplicateIdentifier\").String())\n\t\t\t\tassert.Equal(t, gjson.GetBytes(body, \"organization_id\").String(), orgID.String())\n\t\t\t\tassert.NotContains(t, gjson.GetBytes(body, \"request_url\").String(), \"code=\")\n\t\t\t\tlinkingLoginFlow.ID = gjson.GetBytes(body, \"id\").String()\n\t\t\t\tlinkingLoginFlow.UIAction = gjson.GetBytes(body, \"ui.action\").String()\n\t\t\t\tlinkingLoginFlow.CSRFToken = gjson.GetBytes(body, `ui.nodes.#(attributes.name==\"csrf_token\").attributes.value`).String()\n\t\t\t\tassert.NotEqual(t, loginFlow.ID.String(), linkingLoginFlow.ID, \"should have started a new flow\")\n\t\t\t})\n\n\t\t\tt.Run(\"step=should fail login if existing identity identifier doesn't match\", func(t *testing.T) {\n\t\t\t\tres, err := client.PostForm(linkingLoginFlow.UIAction, url.Values{\n\t\t\t\t\t\"csrf_token\": {linkingLoginFlow.CSRFToken},\n\t\t\t\t\t\"method\":     {\"password\"},\n\t\t\t\t\t\"identifier\": {subject2},\n\t\t\t\t\t\"password\":   {password},\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err, linkingLoginFlow.UIAction)\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t,\n\t\t\t\t\ttext.ErrorValidationLoginLinkedCredentialsDoNotMatch,\n\t\t\t\t\tgjson.GetBytes(body, \"ui.messages.0.id\").Int(),\n\t\t\t\t\tprettyJSON(t, body),\n\t\t\t\t)\n\t\t\t})\n\n\t\t\tt.Run(\"step=should link oidc credentials to existing identity\", func(t *testing.T) {\n\t\t\t\tres, err := client.PostForm(linkingLoginFlow.UIAction, url.Values{\n\t\t\t\t\t\"csrf_token\": {linkingLoginFlow.CSRFToken},\n\t\t\t\t\t\"method\":     {\"password\"},\n\t\t\t\t\t\"identifier\": {subject},\n\t\t\t\t\t\"password\":   {password},\n\t\t\t\t})\n\t\t\t\trequire.NoError(t, err, linkingLoginFlow.UIAction)\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tcheckCredentialsLinked(t, res, body, i.ID, \"valid\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=second login is OIDC\", func(t *testing.T) {\n\t\t\temail1 := \"existing-oidc-identity-1@ory.sh\"\n\t\t\temail2 := \"existing-oidc-identity-2@ory.sh\"\n\t\t\tscope = []string{\"openid\", \"offline\"}\n\n\t\t\tvar identityID uuid.UUID\n\t\t\tt.Run(\"step=create OIDC identity\", func(t *testing.T) {\n\t\t\t\tsubject = email1\n\t\t\t\tr := newRegistrationFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser)\n\t\t\t\taction := assertFormValues(t, r.ID, \"secondProvider\")\n\t\t\t\tres, body := makeRequest(t, \"secondProvider\", action, url.Values{})\n\t\t\t\tassertIdentity(t, res, body)\n\t\t\t\tidentityID = expectTokens(t, \"secondProvider\", body)\n\n\t\t\t\tsubject = email2\n\t\t\t\tr = newRegistrationFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser)\n\t\t\t\taction = assertFormValues(t, r.ID, \"valid\")\n\t\t\t\tres, body = makeRequest(t, \"valid\", action, url.Values{})\n\t\t\t\tassertIdentity(t, res, body)\n\t\t\t\texpectTokens(t, \"valid\", body)\n\t\t\t})\n\n\t\t\tsubject = email1\n\t\t\tclient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\t\t\tloginFlow := newLoginFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser)\n\t\t\tvar linkingLoginFlow struct{ ID string }\n\t\t\tt.Run(\"step=should fail login and start a new login\", func(t *testing.T) {\n\t\t\t\t// To test the identifier mismatch\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationLoginHints, false)\n\t\t\t\tres, body := loginWithOIDC(t, client, loginFlow.ID, \"valid2\")\n\t\t\t\tassertUIError(t, res, body, \"You tried to sign in with \\\"existing-oidc-identity-1@ory.sh\\\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \\\"existing-oidc-identity-1@ory.sh\\\" at \\\"generic\\\" as another way to sign in.\")\n\t\t\t\tlinkingLoginFlow.ID = gjson.GetBytes(body, \"id\").String()\n\t\t\t\tassert.NotEqual(t, loginFlow.ID.String(), linkingLoginFlow.ID, \"should have started a new flow\")\n\t\t\t})\n\n\t\t\tsubject = email2\n\t\t\tt.Run(\"step=should fail login if existing identity identifier doesn't match\", func(t *testing.T) {\n\t\t\t\trequire.NotNil(t, linkingLoginFlow.ID)\n\t\t\t\trequire.NotEmpty(t, linkingLoginFlow.ID)\n\t\t\t\tres, body := loginWithOIDC(t, client, uuid.Must(uuid.FromString(linkingLoginFlow.ID)), \"valid\")\n\t\t\t\tassertUIError(t, res, body, \"Linked credentials do not match.\")\n\t\t\t})\n\n\t\t\tsubject = email1\n\t\t\tt.Run(\"step=should link oidc credentials to existing identity\", func(t *testing.T) {\n\t\t\t\tres, body := loginWithOIDC(t, client, uuid.Must(uuid.FromString(linkingLoginFlow.ID)), \"secondProvider\")\n\t\t\t\tcheckCredentialsLinked(t, res, body, identityID, \"secondProvider\")\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"suite=auto link policy\", func(t *testing.T) {\n\t\tt.Run(\"case=should automatically link credential if policy says so\", func(t *testing.T) {\n\t\t\tsubject = \"user-in-org@ory.sh\"\n\t\t\tscope = []string{\"openid\"}\n\n\t\t\treg.AllLoginStrategies().MustStrategy(\"oidc\").(*oidc.Strategy).SetOnConflictingIdentity(t,\n\t\t\t\tfunc(ctx context.Context, existingIdentity, newIdentity *identity.Identity, _ oidc.Provider, _ *oidc.Claims) oidc.ConflictingIdentityVerdict {\n\t\t\t\t\treturn oidc.ConflictingIdentityVerdictMerge\n\t\t\t\t})\n\n\t\t\tvar i *identity.Identity\n\t\t\tt.Run(\"step=create identity in org without credentials\", func(t *testing.T) {\n\t\t\t\ti = identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\ti.Traits = identity.Traits(`{\"subject\":\"` + subject + `\"}`)\n\t\t\t\ti.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\tIdentifiers: []string{subject},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\t})\n\t\t\t\ti.OrganizationID = uuid.NullUUID{\n\t\t\t\t\tUUID:  orgID,\n\t\t\t\t\tValid: true,\n\t\t\t\t}\n\t\t\t\ti.VerifiableAddresses = []identity.VerifiableAddress{{Value: subject, Via: \"email\", Verified: true}}\n\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, i))\n\t\t\t})\n\n\t\t\tt.Run(\"step=log in with OIDC\", func(t *testing.T) {\n\t\t\t\tloginFlow := newLoginFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser)\n\t\t\t\tloginFlow.OrganizationID = i.OrganizationID\n\t\t\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(ctx, loginFlow))\n\t\t\t\tclient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\t\t\tres, body := loginWithOIDC(t, client, loginFlow.ID, \"valid\")\n\t\t\t\tcheckCredentialsLinked(t, res, body, i.ID, \"valid\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should remove use_auto_link credential if policy says so\", func(t *testing.T) {\n\t\t\tsubject = \"user-with-use-auto-link@ory.sh\"\n\t\t\tscope = []string{\"openid\"}\n\n\t\t\treg.AllLoginStrategies().MustStrategy(\"oidc\").(*oidc.Strategy).SetOnConflictingIdentity(t,\n\t\t\t\tfunc(ctx context.Context, existingIdentity, newIdentity *identity.Identity, _ oidc.Provider, _ *oidc.Claims) oidc.ConflictingIdentityVerdict {\n\t\t\t\t\treturn oidc.ConflictingIdentityVerdictMerge\n\t\t\t\t})\n\n\t\t\tvar i *identity.Identity\n\n\t\t\tt.Run(\"step=create identity with use_auto_link\", func(t *testing.T) {\n\t\t\t\ti = identity.NewIdentity(config.DefaultIdentityTraitsSchemaID)\n\t\t\t\ti.Traits = identity.Traits(`{\"subject\":\"` + subject + `\"}`)\n\t\t\t\ti.SetCredentials(identity.CredentialsTypePassword, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\tIdentifiers: []string{subject},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{}`),\n\t\t\t\t})\n\t\t\t\ti.SetCredentials(identity.CredentialsTypeOIDC, identity.Credentials{\n\t\t\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\t\t\tIdentifiers: []string{\"valid:stub\", \"other:other-identifier\"},\n\t\t\t\t\tConfig: sqlxx.JSONRawMessage(`{\"providers\": [{\n\t\"subject\": \"stub\",\n\t\"provider\": \"valid\",\n\t\"use_auto_link\": true\n},{\n\t\"subject\": \"other-identifier\",\n\t\"provider\": \"other\",\n\t\"use_auto_link\": true\n}]}`),\n\t\t\t\t})\n\t\t\t\ti.VerifiableAddresses = []identity.VerifiableAddress{{Value: subject, Via: \"email\", Verified: true}}\n\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, i))\n\t\t\t})\n\n\t\t\tt.Run(\"step=log in with OIDC\", func(t *testing.T) {\n\t\t\t\tloginFlow := newLoginFlow(t, returnTS.URL, time.Minute, flow.TypeBrowser)\n\t\t\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(ctx, loginFlow))\n\t\t\t\tclient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\t\t\tres, body := loginWithOIDC(t, client, loginFlow.ID, \"valid\")\n\t\t\t\tcheckCredentialsLinked(t, res, body, i.ID, \"valid\")\n\t\t\t})\n\n\t\t\tt.Run(\"step=should remove use_auto_link\", func(t *testing.T) {\n\t\t\t\tvar err error\n\t\t\t\ti, err = reg.PrivilegedIdentityPool().GetIdentityConfidential(ctx, i.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.False(t, gjson.GetBytes(i.Credentials[\"oidc\"].Config, `providers.#(provider==\"valid\").use_auto_link`).Bool())\n\t\t\t\tassert.NotContains(t, i.Credentials[\"oidc\"].Identifiers, \"valid:stub\")\n\t\t\t\tassert.Contains(t, i.Credentials[\"oidc\"].Identifiers, \"valid:user-with-use-auto-link@ory.sh\")\n\t\t\t\tassert.True(t, gjson.GetBytes(i.Credentials[\"oidc\"].Config, `providers.#(provider==\"other\").use_auto_link`).Bool())\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"method=TestPopulateSignUpMethod\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, \"https://foo/\")\n\n\t\tsr, err := registration.NewFlow(conf, time.Minute, \"nosurf\", &http.Request{URL: urlx.ParseOrPanic(\"/\")}, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, reg.RegistrationStrategies(context.Background()).MustStrategy(identity.CredentialsTypeOIDC).(*oidc.Strategy).PopulateRegistrationMethod(&http.Request{}, sr))\n\n\t\tsnapshotx.SnapshotTExcept(t, sr.UI, []string{\"action\", \"nodes.0.attributes.value\"})\n\t})\n}\n\nfunc prettyJSON(t *testing.T, body []byte) string {\n\tvar out bytes.Buffer\n\trequire.NoError(t, json.Indent(&out, body, \"\", \"\\t\"))\n\n\treturn out.String()\n}\n\nfunc TestCountActiveFirstFactorCredentials(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tstrategy := oidc.NewStrategy(reg)\n\n\ttoJSON := func(c identity.CredentialsOIDC) []byte {\n\t\tout, err := json.Marshal(&c)\n\t\trequire.NoError(t, err)\n\t\treturn out\n\t}\n\n\tfor k, tc := range []struct {\n\t\tin       map[identity.CredentialsType]identity.Credentials\n\t\texpected int\n\t}{\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:   strategy.ID(),\n\t\t\t\tConfig: []byte{},\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType: strategy.ID(),\n\t\t\t\tConfig: toJSON(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{\n\t\t\t\t\t{Subject: \"foo\", Provider: \"bar\"},\n\t\t\t\t}}),\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\"\"},\n\t\t\t\tConfig: toJSON(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{\n\t\t\t\t\t{Subject: \"foo\", Provider: \"bar\"},\n\t\t\t\t}}),\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\"bar:\"},\n\t\t\t\tConfig: toJSON(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{\n\t\t\t\t\t{Subject: \"foo\", Provider: \"bar\"},\n\t\t\t\t}}),\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\":foo\"},\n\t\t\t\tConfig: toJSON(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{\n\t\t\t\t\t{Subject: \"foo\", Provider: \"bar\"},\n\t\t\t\t}}),\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\"not-bar:foo\"},\n\t\t\t\tConfig: toJSON(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{\n\t\t\t\t\t{Subject: \"foo\", Provider: \"bar\"},\n\t\t\t\t}}),\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\"bar:not-foo\"},\n\t\t\t\tConfig: toJSON(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{\n\t\t\t\t\t{Subject: \"foo\", Provider: \"bar\"},\n\t\t\t\t}}),\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\"bar:foo\"},\n\t\t\t\tConfig: toJSON(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{\n\t\t\t\t\t{Subject: \"foo\", Provider: \"bar\"},\n\t\t\t\t}}),\n\t\t\t}},\n\t\t\texpected: 1,\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tin := make(map[identity.CredentialsType]identity.Credentials)\n\t\t\tfor _, v := range tc.in {\n\t\t\t\tin[v.Type] = v\n\t\t\t}\n\t\t\tactual, err := strategy.CountActiveFirstFactorCredentials(context.Background(), in)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tc.expected, actual)\n\t\t})\n\t}\n}\n\nfunc TestDisabledEndpoint(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeOIDC.String(), false)\n\tctx := context.Background()\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\tt.Run(\"case=should not callback when oidc method is disabled\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\t\tres, err := c.Get(publicTS.URL + oidc.RouteCallback)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusNotFound, res.StatusCode)\n\n\t\tb := ioutilx.MustReadAll(res.Body)\n\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\")\n\t})\n\n\tt.Run(\"case=should not auth when oidc method is disabled\", func(t *testing.T) {\n\t\tc := testhelpers.NewClientWithCookies(t)\n\n\t\tt.Run(\"flow=settings\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://stub/stub.schema.json\")\n\t\t\tc := testhelpers.NewHTTPClientWithArbitrarySessionCookie(ctx, t, reg)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, c, publicTS)\n\n\t\t\tres, err := c.PostForm(f.Ui.Action, url.Values{\"link\": {\"oidc\"}})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusNotFound, res.StatusCode)\n\n\t\t\tb := ioutilx.MustReadAll(res.Body)\n\t\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\")\n\t\t})\n\n\t\tt.Run(\"flow=login\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, c, publicTS, false)\n\t\t\tres, err := c.PostForm(f.Ui.Action, url.Values{\"provider\": {\"oidc\"}})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusNotFound, res.StatusCode)\n\n\t\t\tb := ioutilx.MustReadAll(res.Body)\n\t\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\")\n\t\t})\n\n\t\tt.Run(\"flow=registration\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeRegistrationFlowViaAPI(t, c, publicTS)\n\t\t\tres, err := c.PostForm(f.Ui.Action, url.Values{\"provider\": {\"oidc\"}})\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusNotFound, res.StatusCode)\n\n\t\t\tb := ioutilx.MustReadAll(res.Body)\n\t\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\")\n\t\t})\n\t})\n}\n\nfunc TestPostEndpointRedirect(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeOIDC, true)),\n\t)\n\n\tvar (\n\t\tsubject string\n\t\tclaims  idTokenClaims\n\t\tscope   []string\n\t)\n\n\tremoteAdmin, remotePublic, _ := newHydra(t, &subject, &claims, &scope)\n\n\tpublicTS, _, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\tviperSetProviderConfig(\n\t\tt,\n\t\tconf,\n\t\tnewOIDCProvider(t, publicTS, remotePublic, remoteAdmin, \"apple\"),\n\t)\n\n\tfor _, providerID := range []string{\"apple\", \"apple-asd123\"} {\n\t\tt.Run(\"case=should redirect to GET and preserve parameters/id=\"+providerID, func(t *testing.T) {\n\t\t\t// create a client that does not follow redirects\n\t\t\tc := &http.Client{\n\t\t\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t\t},\n\t\t\t}\n\t\t\tres, err := c.PostForm(publicTS.URL+\"/self-service/methods/oidc/callback/\"+providerID, url.Values{\"state\": {\"foo\"}, \"test\": {\"3\"}})\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.Equal(t, http.StatusFound, res.StatusCode)\n\n\t\t\tlocation, err := res.Location()\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, publicTS.URL+\"/self-service/methods/oidc/callback/\"+providerID+\"?state=foo&test=3\", location.String())\n\n\t\t\t// We don't want to add/override CSRF cookie when redirecting\n\t\t\ttesthelpers.AssertNoCSRFCookieInResponse(t, res)\n\t\t})\n\t}\n}\n\nfunc findCsrfTokenPath(t *testing.T, body []byte) string {\n\tnodes := gjson.GetBytes(body, \"ui.nodes\").Array()\n\tindex := slices.IndexFunc(nodes, func(n gjson.Result) bool {\n\t\treturn n.Get(\"attributes.name\").String() == \"csrf_token\"\n\t})\n\trequire.GreaterOrEqual(t, index, 0)\n\treturn fmt.Sprintf(\"ui.nodes.%v.attributes.value\", index)\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/extension/payload.json",
    "content": "{\n  \"email\": \"someone@email.org\",\n  \"names\": [\n    \"peter\",\n    \"pan\"\n  ]\n}"
  },
  {
    "path": "selfservice/strategy/oidc/stub/extension/schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"mappings\": {\n              \"identity\": {\n                \"traits\": [\n                  {\n                    \"path\": \"email\"\n                  }\n                ]\n              }\n            }\n          }\n        },\n        \"names\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\",\n            \"ory.sh/kratos\": {\n              \"mappings\": {\n                \"identity\": {\n                  \"traits\": [\n                    {\n                      \"path\": \"names.-1\"\n                    }\n                  ]\n                }\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/jwk.json",
    "content": "{\n  \"p\": \"8Bh18gHHaVBWHgHX2s9eAsTHSgFq1kSvAuhvWpipsfbrDMBYZ2nwPCB1g3hb-L6cIMzxTmsv8bxwC0l316I8f77NgjSB4mlZAHjXZp861Z9xAFQ_Kx9ZcRldGmbUQ0NOQaHxMCSh5C1hmr8X54BzTEuMTlOnjVQrKUAPoaxhRpU\",\n  \"kty\": \"RSA\",\n  \"q\": \"4id2MrhpalEruyQUIDeLts0rqqanYhny1PU_K7CibTmAFm88U2npceffxg0o6RLYccybx51VYvzPqp01uGL5-TWAJLHihpXbVt8pgidok21mN54IXScG_EOblrp6sPjzhn39dpiCzAgIZhgOQR-IHepWSYwEuKbD8mVnKkYD44c\",\n  \"d\": \"LsaTOZr2KEUMppL92JtlDatcVSXSDYBTqhG4Sr2Pn-Pbq-VVpHTIRtvHeAz3Kiyei8MYG7jhOLzoA2zQGq7DqNP3BGOAPjPgPqW7nQ33TFIAlS8Q-8iJnmZC3NNG4G0go5YJoNgu7aQeBmOqL_jngCwAxvF2KvmOEOpfZzG9Nt0XMBMnAWa5NskKrtnB7b8oxxjghPyLsFa_N9JfjYPUDh_zZWfgv866k2UEqLaWOUMDRhfFpXeEu_YL4i8xwGmX-mDAxESXEjRZQrK29_SldIhTAopzGoacmic9CJwntGENOt5hihlDzCpMUEKafiFzODCQVYZSl0GaYupopZoH2Q\",\n  \"e\": \"AQAB\",\n  \"use\": \"sig\",\n  \"kid\": \"RTSkc-Jk-iD1lx2TK9WtYDtBtWs0MSyCG0nQSR1mglU\",\n  \"qi\": \"ini3TIy3h4Lm85imQRXVjd5VyzA4_b7CkRhIAbj2Q-Pv2xoHJ6xhDgE6jRa1rher4DNrcAuBuOel5f_2U6EcVe6IoQU5-IvqS7hfo0JCWTjCjBKNTqpNFjUC2kE8kaWKaP_tfDESmW-ZO0iKAbuRCneSyNHgj2RQIcXFbq1bIRQ\",\n  \"dp\": \"nk7ClgtuPJZn8ektNm08g37UGIvOsfEfpD82DPpUCa_RU9sPb0B-0mZklYcqvVyQ_V-kTByIxE-HYSnUBy5FzcU1JAETEwJ7WMBU5qle1bQHgjwKWpiVFOmwZdQfaSpb0xLAQQomZJk3nh0Z2d7sJwY5QPwPojQ5MT24ENXkXfE\",\n  \"alg\": \"RS256\",\n  \"dq\": \"bbifi_QUkNRY1y6l5QuN6V6ZdO3t_5Z_Tfq-bz__Tea70iadqgqUjALnensgAhR2lp-iZLJcnu3xAuHLEm5SwSnHxgXX1VwXUopq5Q6hmgVVtl4hyLAKn5Fdhz9qDzp5TCMMOeG8c6jiCkZZhBb8PydWPdCE6eFe59dyufvGHzk\",\n  \"n\": \"1BqatHWJWPTN4mnkrhns2pSk4LRe7W0cyjs8vp7INp3PtgzQ0-KuUibnE0v0k-6DQcu-3hkP88fUOGlYm2z0x9y2urMoAepBwiWybs1v0xqaQx_eLxtkCiElF3i9zYTqmR6cdAOb_duKpTeuTZm326UdpmDoU3TwcGBcszNkNfcNjeq-3z2bvS3GXvJIR9A-6j17xAXVVifUwjPIcNs21ajJVhNFcM9cM90OG9mivbE645KkqMT-WQxFPEQ-DqjijmXnLed9QabcfXcZdkT96O7x_zxHTgF46FlVyJtswuLRUO-h-48ai3-6NmfnR0tOCoVYhHJxndcQ0L_-uqFXkw\"\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/jwks_public.json",
    "content": "{\n  \"keys\": [\n    {\n      \"kty\": \"RSA\",\n      \"e\": \"AQAB\",\n      \"use\": \"sig\",\n      \"kid\": \"RTSkc-Jk-iD1lx2TK9WtYDtBtWs0MSyCG0nQSR1mglU\",\n      \"alg\": \"RS256\",\n      \"n\": \"1BqatHWJWPTN4mnkrhns2pSk4LRe7W0cyjs8vp7INp3PtgzQ0-KuUibnE0v0k-6DQcu-3hkP88fUOGlYm2z0x9y2urMoAepBwiWybs1v0xqaQx_eLxtkCiElF3i9zYTqmR6cdAOb_duKpTeuTZm326UdpmDoU3TwcGBcszNkNfcNjeq-3z2bvS3GXvJIR9A-6j17xAXVVifUwjPIcNs21ajJVhNFcM9cM90OG9mivbE645KkqMT-WQxFPEQ-DqjijmXnLed9QabcfXcZdkT96O7x_zxHTgF46FlVyJtswuLRUO-h-48ai3-6NmfnR0tOCoVYhHJxndcQ0L_-uqFXkw\"\n    }\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/jwks_public2.json",
    "content": "{\n  \"keys\": [\n    {\n      \"kty\": \"RSA\",\n      \"e\": \"AQAB\",\n      \"use\": \"sig\",\n      \"kid\": \"swo5qZbZECb_alwFmwoleMN7nFw6Us-TP6f-sKIPDF0\",\n      \"alg\": \"RS256\",\n      \"n\": \"kOB5UX-fhCEesMn7BBRKCwkV33blQrD4xZhRK3rQDySNGwf9Uoeemm6SsO5E3WBnYQHWyH4X4jlwVNmkqBHyijKs3v2DBhIZTXa0dU2qp6dGJXQObSHKN51RzPX6yE3DiuzhKcl0ORlvjZk2nzPDl3l9y_Fl6opjFsnnCdHUovqdTBi9HcdocF7E5QeFvQG0QHs8zDC1myjs73m1F18IlTF6peXgFhgsiPKrXrgugh8vItpzr4dfA8fK3ND-NBLloNaZbtCAGmW6vxJnRae2PVBzSf8GamMBCuCNOJBRaQMRkZnZvkAIMGc9WngaHFhGBnW3IC__B0e-_fCyAVr4KQ\"\n    }\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/merge/0.schema.json",
    "content": "{}"
  },
  {
    "path": "selfservice/strategy/oidc/stub/merge/1.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"foo\": {\n          \"type\": \"string\"\n        },\n        \"baz\": {\n          \"type\": \"string\"\n        },\n        \"bool\": {\n          \"type\": \"boolean\"\n        },\n        \"opv\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/merge/2.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"foo\": {\n          \"type\": \"string\"\n        },\n        \"baz\": {\n          \"type\": \"string\"\n        },\n        \"bool\": {\n          \"type\": \"boolean\"\n        },\n        \"opv\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/merge/3.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"foo\": {\n          \"type\": \"string\"\n        },\n        \"baz\": {\n          \"type\": \"string\"\n        },\n        \"bool\": {\n          \"type\": \"boolean\"\n        },\n        \"opv\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/oidc.facebook.jsonnet",
    "content": "local claims = std.extVar('claims');\n\nif std.length(claims.sub) == 0 then\n  error 'claim sub not set'\nelse\n  {\n    identity: {\n      traits: {\n        subject: claims.sub,\n        [if \"email\" in claims then \"email\" else null]: claims.email,\n      },\n    },\n  }\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/oidc.hydra.jsonnet",
    "content": "local claims = std.extVar('claims');\n\nif std.length(claims.sub) == 0 then\n  error 'claim sub not set'\nelse\n  {\n    identity: {\n      traits: {\n        subject: claims.sub,\n        [if \"website\" in claims then \"website\" else null]: claims.website,\n        [if \"groups\" in claims.raw_claims then \"groups\" else null]: claims.raw_claims.groups,\n      },\n      metadata_public: {\n        [if \"picture\" in claims then \"picture\" else null]: claims.picture,\n      },\n      metadata_admin: {\n        [if \"phone_number\" in claims then \"phone_number\" else null]: claims.phone_number,\n      },\n      verified_addresses: [\n        { via: \"email\", value: claims.sub },\n      ],\n    },\n  }\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/oidc.linkedin.jsonnet",
    "content": "local claims =\n{\n  email_verified: false\n} + std.extVar('claims');\n{\n  identity:\n  {\n    traits:\n    {\n      email: claims.email,\n      [if \"picture\" in claims then \"picture\" else null]: claims.picture,\n      name:\n      {\n        first: claims.name,\n        last: claims.last_name,\n      }\n    },\n  },\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/oidc.uaepass.jsonnet",
    "content": "local claims = std.extVar('claims');\n\nlocal raw = claims.raw_claims;\n\nif std.length(claims.sub) == 0 then\n  error 'claim sub not set'\nelse\n  {\n    identity: {\n      traits: {\n        subject: claims.sub,\n        [if \"email\" in claims && claims.email != '' then \"email\" else null]: claims.email,\n        [if \"fullnameEN\" in raw && raw.fullnameEN != '' then \"name\" else null]: {\n          full: raw.fullnameEN,\n          [if \"firstnameEN\" in raw && raw.firstnameEN != '' then \"first\" else null]: raw.firstnameEN,\n          [if \"lastnameEN\" in raw && raw.lastnameEN != '' then \"last\" else null]: raw.lastnameEN,\n        },\n        [if \"fullnameAR\" in raw && raw.fullnameAR != '' then \"name_ar\" else null]: {\n          full: raw.fullnameAR,\n          [if \"firstnameAR\" in raw && raw.firstnameAR != '' then \"first\" else null]: raw.firstnameAR,\n          [if \"lastnameAR\" in raw && raw.lastnameAR != '' then \"last\" else null]: raw.lastnameAR,\n        },\n        [if \"uuid\" in raw && raw.uuid != '' then \"uuid\" else null]: raw.uuid,\n        [if \"unifiedId\" in raw && raw.unifiedId != '' then \"unified_id\" else null]: raw.unifiedId,\n        [if \"idn\" in raw && raw.idn != '' then \"idn\" else null]: raw.idn,\n        [if \"userType\" in raw && raw.userType != '' then \"user_type\" else null]: raw.userType,\n        [if \"nationalityEN\" in raw && raw.nationalityEN != '' then \"nationality\" else null]: raw.nationalityEN,\n        [if \"gender\" in raw && raw.gender != '' then \"gender\" else null]: raw.gender,\n        [if \"mobile\" in raw && raw.mobile != '' then \"phone\" else null]: raw.mobile,\n      },\n    },\n  }\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/registration-aal.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"format\": \"email\",\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/registration-multi-schema-extra-fields.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"subject\": {\n          \"format\": \"email\",\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"extra_data\": {\n          \"type\": \"string\",\n          \"minLength\": 1\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        },\n        \"website\": {\n          \"type\": \"string\",\n          \"format\": \"uri\"\n        },\n        \"groups\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      },\n      \"required\": [\"subject\", \"extra_data\"]\n    },\n    \"metadata_public\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"picture\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"metadata_admin\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"phone_number\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/registration-phone.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"subject\": {\n          \"format\": \"email\",\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        },\n        \"website\": {\n          \"type\": \"string\",\n          \"format\": \"uri\"\n        },\n        \"groups\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      },\n      \"required\": [\n        \"subject\"\n      ]\n    },\n    \"metadata_public\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"picture\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"metadata_admin\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"phone_number\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/registration-verifiable-email.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"subject\": {\n          \"format\": \"email\",\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        },\n        \"website\": {\n          \"type\": \"string\",\n          \"format\": \"uri\"\n        },\n        \"groups\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      },\n      \"required\": [\n        \"subject\"\n      ]\n    },\n    \"metadata_public\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"picture\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"metadata_admin\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"phone_number\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/registration.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"subject\": {\n          \"format\": \"email\",\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        },\n        \"website\": {\n          \"type\": \"string\",\n          \"format\": \"uri\"\n        },\n        \"groups\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      },\n      \"required\": [\n        \"subject\"\n      ]\n    },\n    \"metadata_public\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"picture\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"metadata_admin\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"phone_number\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/settings.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"format\": \"email\",\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        }\n      },\n      \"required\": [\n        \"email\"\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/stub/stub.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {}\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/token_verifier.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/coreos/go-oidc/v3/oidc\"\n\n\t\"github.com/ory/x/reqlog\"\n)\n\nfunc verifyToken(ctx context.Context, keySet oidc.KeySet, config *Configuration, rawIDToken, issuerURL string) (*Claims, error) {\n\ttokenAudiences := append([]string{config.ClientID}, config.AdditionalIDTokenAudiences...)\n\tvar token *oidc.IDToken\n\terr := fmt.Errorf(\"no audience matched the token's audience\")\n\tfor _, aud := range tokenAudiences {\n\t\tverifier := oidc.NewVerifier(issuerURL, keySet, &oidc.Config{\n\t\t\tClientID: aud,\n\t\t})\n\t\tt0 := time.Now()\n\t\ttoken, err = verifier.Verify(ctx, rawIDToken)\n\t\treqlog.AccumulateExternalLatency(ctx, time.Since(t0))\n\t\tif err != nil && strings.Contains(err.Error(), \"oidc: expected audience\") {\n\t\t\t// The audience is not the one we expect, try the next one\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\t// Something else went wrong\n\t\t\treturn nil, err\n\t\t}\n\t\t// The token was verified successfully\n\t\tbreak\n\t}\n\tif err != nil {\n\t\t// None of the allowed audiences matched the audience in the token\n\t\treturn nil, fmt.Errorf(\"token audience didn't match allowed audiences: %+v %w\", tokenAudiences, err)\n\t}\n\tclaims := &Claims{}\n\tvar rawClaims map[string]any\n\n\tif token == nil {\n\t\treturn nil, fmt.Errorf(\"token is nil\")\n\t}\n\n\tif err := token.Claims(claims); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = token.Claims(&rawClaims); err != nil {\n\t\treturn nil, err\n\t}\n\tclaims.RawClaims = rawClaims\n\n\treturn claims, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/oidc/types.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage oidc\n\nimport (\n\t\"cmp\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/ory/kratos/ui/container\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype FlowMethod struct {\n\t*container.Container\n}\n\nfunc AddProviders(c *container.Container, providers []Configuration, message func(provider string, providerId string) *text.Message, credentialsType identity.CredentialsType) {\n\tfor _, p := range providers {\n\t\tif len(p.OrganizationID) > 0 {\n\t\t\tcontinue\n\t\t}\n\t\tAddProvider(c, p.ID, message(cmp.Or(p.Label, p.ID), p.ID), credentialsType)\n\t}\n}\n\nfunc AddProvider(c *container.Container, providerID string, message *text.Message, credentialsType identity.CredentialsType) {\n\tgroup := node.OpenIDConnectGroup\n\tif credentialsType == identity.CredentialsTypeSAML {\n\t\tgroup = node.SAMLGroup\n\t}\n\tfield := node.NewInputField(\"provider\", providerID, group, node.InputAttributeTypeSubmit).WithMetaLabel(message)\n\tc.GetNodes().RemoveMatching(field)\n\tc.GetNodes().Append(field)\n}\n\nfunc NewFlowMethod(f *container.Container) *FlowMethod {\n\treturn &FlowMethod{Container: f}\n}\n\ntype ider interface {\n\tGetID() uuid.UUID\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/.schema/login.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/passkey/login.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"passkey_login\": {\n      \"type\": \"string\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/.schema/registration.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/passkey/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"traits\": {\n      \"description\": \"This field will be overwritten in registration.go's decoder() method. Do not add anything to this field as it has no effect.\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"passkey_register\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/.schema/settings.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/passkey/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"passkey_settings_register\": {\n      \"type\": \"string\"\n    },\n    \"passkey_remove\": {\n      \"type\": \"string\"\n    }\n  }\n}\n\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=passkey_button_exists.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login\",\n      \"node_type\": \"input\",\n      \"onload\": \"window.oryPasskeyLoginAutocompleteInit()\",\n      \"onloadTrigger\": \"oryPasskeyLoginAutocompleteInit\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_challenge\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=succeeds_with_passwordless_login-passkey_display_name-type=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login\",\n      \"node_type\": \"input\",\n      \"onloadTrigger\": \"oryPasskeyLoginAutocompleteInit\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_challenge\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=succeeds_with_passwordless_login-passkey_display_name-type=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login\",\n      \"node_type\": \"input\",\n      \"onload\": \"window.oryPasskeyLoginAutocompleteInit()\",\n      \"onloadTrigger\": \"oryPasskeyLoginAutocompleteInit\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_challenge\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=succeeds_with_passwordless_login-passkey_display_name-type=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login\",\n      \"node_type\": \"input\",\n      \"onload\": \"window.oryPasskeyLoginAutocompleteInit()\",\n      \"onloadTrigger\": \"oryPasskeyLoginAutocompleteInit\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_challenge\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=succeeds_with_passwordless_login-webauthn_identifier-type=api.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login\",\n      \"node_type\": \"input\",\n      \"onloadTrigger\": \"oryPasskeyLoginAutocompleteInit\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_challenge\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=succeeds_with_passwordless_login-webauthn_identifier-type=browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login\",\n      \"node_type\": \"input\",\n      \"onload\": \"window.oryPasskeyLoginAutocompleteInit()\",\n      \"onloadTrigger\": \"oryPasskeyLoginAutocompleteInit\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_challenge\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=passwordless-case=succeeds_with_passwordless_login-webauthn_identifier-type=spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login\",\n      \"node_type\": \"input\",\n      \"onload\": \"window.oryPasskeyLoginAutocompleteInit()\",\n      \"onloadTrigger\": \"oryPasskeyLoginAutocompleteInit\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_challenge\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"foo@bar.com\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_challenge\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteLogin-flow=refresh-case=refresh_passwordless_credentials-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"foo@bar.com\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_login\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_challenge\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_remove\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"626172626172\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"added_at\": \"0001-01-01T00:00:00Z\",\n          \"added_at_unix\": -62135596800,\n          \"display_name\": \"bar\"\n        },\n        \"id\": 1050020,\n        \"text\": \"Remove passkey \\\"bar\\\"\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_remove\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"666f6f666f6f\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"added_at\": \"0001-01-01T00:00:00Z\",\n          \"added_at_unix\": -62135596800,\n          \"display_name\": \"foo\"\n        },\n        \"id\": 1050020,\n        \"text\": \"Remove passkey \\\"foo\\\"\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_register_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeySettingsRegistration()\",\n      \"onclickTrigger\": \"oryPasskeySettingsRegistration\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050019,\n        \"text\": \"Add passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_settings_register\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_create_data\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=fails_to_remove_passkey_if_it_is_the_last_credential_available-type=api-response.json",
    "content": "{\n  \"type\": \"input\",\n  \"group\": \"passkey\",\n  \"attributes\": {\n    \"name\": \"passkey_remove\",\n    \"type\": \"submit\",\n    \"value\": \"666f6f666f6f\",\n    \"disabled\": true,\n    \"node_type\": \"input\"\n  },\n  \"messages\": [],\n  \"meta\": {\n    \"label\": {\n      \"id\": 1050020,\n      \"text\": \"Remove passkey \\\"foo\\\"\",\n      \"type\": \"info\",\n      \"context\": {\n        \"added_at\": \"0001-01-01T00:00:00Z\",\n        \"added_at_unix\": -62135596800,\n        \"display_name\": \"foo\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=fails_to_remove_passkey_if_it_is_the_last_credential_available-type=api.json",
    "content": "{\n  \"passkey_register_trigger\": [\n    \"\"\n  ],\n  \"passkey_remove\": [\n    \"666f6f666f6f\"\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=fails_to_remove_passkey_if_it_is_the_last_credential_available-type=browser-response.json",
    "content": "{\n  \"type\": \"input\",\n  \"group\": \"passkey\",\n  \"attributes\": {\n    \"name\": \"passkey_remove\",\n    \"type\": \"submit\",\n    \"value\": \"666f6f666f6f\",\n    \"disabled\": true,\n    \"node_type\": \"input\"\n  },\n  \"messages\": [],\n  \"meta\": {\n    \"label\": {\n      \"id\": 1050020,\n      \"text\": \"Remove passkey \\\"foo\\\"\",\n      \"type\": \"info\",\n      \"context\": {\n        \"added_at\": \"0001-01-01T00:00:00Z\",\n        \"added_at_unix\": -62135596800,\n        \"display_name\": \"foo\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=fails_to_remove_passkey_if_it_is_the_last_credential_available-type=browser.json",
    "content": "{\n  \"passkey_register_trigger\": [\n    \"\"\n  ],\n  \"passkey_remove\": [\n    \"666f6f666f6f\"\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=fails_to_remove_passkey_if_it_is_the_last_credential_available-type=spa-response.json",
    "content": "{\n  \"type\": \"input\",\n  \"group\": \"passkey\",\n  \"attributes\": {\n    \"name\": \"passkey_remove\",\n    \"type\": \"submit\",\n    \"value\": \"666f6f666f6f\",\n    \"disabled\": true,\n    \"node_type\": \"input\"\n  },\n  \"messages\": [],\n  \"meta\": {\n    \"label\": {\n      \"id\": 1050020,\n      \"text\": \"Remove passkey \\\"foo\\\"\",\n      \"type\": \"info\",\n      \"context\": {\n        \"added_at\": \"0001-01-01T00:00:00Z\",\n        \"added_at_unix\": -62135596800,\n        \"display_name\": \"foo\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=fails_to_remove_passkey_if_it_is_the_last_credential_available-type=spa.json",
    "content": "{\n  \"passkey_register_trigger\": [\n    \"\"\n  ],\n  \"passkey_remove\": [\n    \"666f6f666f6f\"\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_register_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeySettingsRegistration()\",\n      \"onclickTrigger\": \"oryPasskeySettingsRegistration\",\n      \"type\": \"button\",\n      \"value\": \"\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050019,\n        \"text\": \"Add passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_settings_register\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_create_data\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_challenge\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_login\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"onload\": \"window.oryPasskeyLoginAutocompleteInit()\",\n      \"onloadTrigger\": \"oryPasskeyLoginAutocompleteInit\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_login_trigger\",\n      \"type\": \"button\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_challenge\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_login\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_login_trigger\",\n      \"type\": \"button\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"hidden\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier-case=account_enumeration_mitigation_disabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier-case=account_enumeration_mitigation_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_login_trigger\",\n      \"type\": \"button\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_does_not_have_a_passkey.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_has_passkey.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_login_trigger\",\n      \"type\": \"button\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_login_trigger\",\n      \"type\": \"button\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=account_enumeration_mitigation_disabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=account_enumeration_mitigation_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_login_trigger\",\n      \"type\": \"button\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyLogin()\",\n      \"onclickTrigger\": \"oryPasskeyLogin\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010021,\n        \"text\": \"Sign in with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_challenge\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_login\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"onload\": \"window.oryPasskeyLoginAutocompleteInit()\",\n      \"onloadTrigger\": \"oryPasskeyLoginAutocompleteInit\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactorRefresh.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethod-type=api.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethod-type=browser.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethod.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodCredentials-type=api.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodCredentials-type=browser.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodCredentials.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodProfile.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=1.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=2.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=3.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=4.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateSettingsMethod-method=PopulateSettingsMethod-type=api.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryPasskeySettingsRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050019,\n        \"text\": \"Add passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_settings_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestPopulateSettingsMethod-method=PopulateSettingsMethod-type=browser.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeySettingsRegistration()\",\n      \"onclickTrigger\": \"oryPasskeySettingsRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050019,\n        \"text\": \"Add passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_settings_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_does_not_exist_when_passwordless_is_disabled-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.foobar\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.username\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_does_not_exist_when_passwordless_is_disabled-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.foobar\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.username\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.foobar\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.username\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_register_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"type\": \"button\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_register\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_create_data\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/.snapshots/TestRegistration-case=passkey_button_exists-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.foobar\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.username\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_register_trigger\",\n      \"node_type\": \"input\",\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"type\": \"button\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_register\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"passkey_create_data\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"passkey\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/login/success/credentials.json",
    "content": "{\n  \"credentials\": [\n    {\n      \"id\": \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\",\n      \"public_key\": \"pQECAyYgASFYIPW2FsD6d/Lc7SU33hMhJUxafOA3JWpsLka8eKO+OPRkIlggkkPt8ocrupQOuvy+8HbQLSLiu899EdchJlWdMPE1tiw=\",\n      \"attestation_type\": \"none\",\n      \"authenticator\": {\n        \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n        \"sign_count\": 3,\n        \"clone_warning\": false\n      },\n      \"display_name\": \"some-key\",\n      \"added_at\": \"2021-08-17T10:18:55Z\",\n      \"is_passwordless\": true\n    }\n  ],\n  \"user_handle\": \"c29tZS1yYW5kb20tdXNlci1oYW5kbGU=\"\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/login/success/identity.json",
    "content": "{\n  \"id\": \"f5d1b6a3-a4bb-44f7-9161-f4f877efe9ad\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"http://localhost:4455/schemas/default\",\n  \"state\": \"active\",\n  \"state_changed_at\": \"2021-08-17T12:18:01.690425+02:00\",\n  \"traits\": {\n    \"email\": \"foo@bar.com\",\n    \"website\": \"https://www.ory.sh\"\n  },\n  \"created_at\": \"2021-08-17T12:18:01.690741+02:00\",\n  \"updated_at\": \"2021-08-17T12:18:01.690741+02:00\"\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/login/success/internal_context.json",
    "content": "{\n  \"passkey_session_data\": {\n    \"challenge\": \"WzZCWULmaq5xTAlA0YYHlqoubqAhe1AWdLRZCIBAMcM\",\n    \"user_id\": \"\",\n    \"allowed_credentials\": [\n      \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\"\n    ],\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/login/success/response.json",
    "content": "{\n  \"id\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"rawId\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAACg\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiV3paQ1dVTG1hcTV4VEFsQTBZWUhscW91YnFBaGUxQVdkTFJaQ0lCQU1jTSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n    \"signature\": \"MEQCIHtRzzmLJrTPucNIRpPkstxR8oGJEzrm558LFe2jHTesAiAy2SGuBMDkdVdMJU4WJR2qFSpbAHQUvwG--Gv3vK8vDA\",\n    \"userHandle\": \"c29tZS1yYW5kb20tdXNlci1oYW5kbGU=\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/registration/failure/internal_context_missing_user_id.json",
    "content": "{\n  \"passkey_session_data\": {\n    \"challenge\": \"UlxHSTkuMvtVDoV9y5lhu9OyNUP8P7MP0RYAT6Im_rY\",\n    \"user_id\": \"\",\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/registration/failure/internal_context_wrong_user_id.json",
    "content": "{\n  \"passkey_session_data\": {\n    \"challenge\": \"UlxHSTkuMvtVDoV9y5lhu9OyNUP8P7MP0RYAT6Im_rY\",\n    \"user_id\": \"wrong\",\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/registration/success/android/internal_context.json",
    "content": "{\n  \"passkey_session_data\": {\n    \"challenge\": \"mFtAwmtDDdwcO6200I2H6oWjzOiF21lZhQVlrC4tdaU\",\n    \"user_id\": \"d29OeDNJVjdYR2NRa09RVHhNVG1ZbHE1ejBDYzM1dGV3UWxFT25yaUJKcTUyb0VOR0pUMk5PeXExRXp3Z2M2dg\",\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/registration/success/android/response.json",
    "content": "{\n  \"id\": \"mK2RV0b2NUGDsj8QqH0XtQ\",\n  \"rawId\": \"mK2RV0b2NUGDsj8QqH0XtQ\",\n  \"response\": {\n    \"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUJYVxRmHaAcJuz7n2X5FJILFPwxIhVpoURyBRglMxnFpdAAAAAOqbjWZNAR0hPOS2tIy1ddQAEJitkVdG9jVBg7I_EKh9F7WlAQIDJiABIVggjEkfDDjIm8yAYfth4u0EV7ApX4kclQONhpK5BLc7W6wiWCCHiHhRNqf8Qhc7bjoIFTqw4lafiC7yrXvojU_WMNcutA\",\n    \"clientDataJson\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibUZ0QXdtdEREZHdjTzYyMDBJMkg2b1dqek9pRjIxbFpoUVZsckM0dGRhVSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOlMyUmZOWWdKbVFpS2dkNi1zZGJqVzdwaGNMX09UUDR2R0U4TDUxUTJHQjAiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20udHJwLmFuZC5wZXJzb25hbC5xbCJ9\"\n  },\n  \"type\": \"public-key\"\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/registration/success/browser/identity.json",
    "content": "{\n  \"id\": \"6e11a9a7-62fd-4c88-871a-097f18f0306f\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"http://localhost:4455/schemas/default\",\n  \"state\": \"active\",\n  \"state_changed_at\": \"2021-08-17T11:15:59.232051+02:00\",\n  \"traits\": {\n    \"email\": \"foo@bar.com\",\n    \"website\": \"https://www.ory.sh\"\n  },\n  \"created_at\": \"2021-08-17T11:15:59.232288+02:00\",\n  \"updated_at\": \"2021-08-17T11:15:59.232288+02:00\"\n}\n\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/registration/success/browser/internal_context.json",
    "content": "{\n  \"passkey_session_data\": {\n    \"challenge\": \"UlxHSTkuMvtVDoV9y5lhu9OyNUP8P7MP0RYAT6Im_rY\",\n    \"user_id\": \"bhGpp2L9TIiHGgl/GPAwbw==\",\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/registration/success/browser/response.json",
    "content": "{\n  \"id\": \"L1yOrxHy5Lq72lAPahaWdl0q9gsXRzV2BJ4xJmkTVH_8uuVKU-FVbJlVRwYGzPNc1IjCWUYAK0H0YSpd5hz-Pg\",\n  \"rawId\": \"L1yOrxHy5Lq72lAPahaWdl0q9gsXRzV2BJ4xJmkTVH_8uuVKU-FVbJlVRwYGzPNc1IjCWUYAK0H0YSpd5hz-Pg\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjESZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NBAAAABAAAAAAAAAAAAAAAAAAAAAAAQC9cjq8R8uS6u9pQD2oWlnZdKvYLF0c1dgSeMSZpE1R__LrlSlPhVWyZVUcGBszzXNSIwllGACtB9GEqXeYc_j6lAQIDJiABIVggFFzdor6hBMgrpYLCds8Uu2JtPaaaxKU6LEAUT6QRZ5UiWCA24TI4vED6rrTUjykchoAln67u5GT1nwmzjvrk79HhlQ\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiVWx4SFNUa3VNdnRWRG9WOXk1bGh1OU95TlVQOFA3TVAwUllBVDZJbV9yWSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/settings/success/identity.json",
    "content": "{\n  \"id\": \"6e11a9a7-62fd-4c88-871a-097f18f0306f\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"http://localhost:4455/schemas/default\",\n  \"state\": \"active\",\n  \"state_changed_at\": \"2021-08-17T11:15:59.232051+02:00\",\n  \"traits\": {\n    \"email\": \"foo@bar.com\",\n    \"website\": \"https://www.ory.sh\"\n  },\n  \"created_at\": \"2021-08-17T11:15:59.232288+02:00\",\n  \"updated_at\": \"2021-08-17T11:15:59.232288+02:00\"\n}\n\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/settings/success/internal_context.json",
    "content": "{\n  \"passkey_session_data\": {\n    \"challenge\": \"UlxHSTkuMvtVDoV9y5lhu9OyNUP8P7MP0RYAT6Im_rY\",\n    \"user_id\": \"bhGpp2L9TIiHGgl/GPAwbw==\",\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/fixtures/settings/success/response.json",
    "content": "{\n  \"id\": \"L1yOrxHy5Lq72lAPahaWdl0q9gsXRzV2BJ4xJmkTVH_8uuVKU-FVbJlVRwYGzPNc1IjCWUYAK0H0YSpd5hz-Pg\",\n  \"rawId\": \"L1yOrxHy5Lq72lAPahaWdl0q9gsXRzV2BJ4xJmkTVH_8uuVKU-FVbJlVRwYGzPNc1IjCWUYAK0H0YSpd5hz-Pg\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjESZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NBAAAABAAAAAAAAAAAAAAAAAAAAAAAQC9cjq8R8uS6u9pQD2oWlnZdKvYLF0c1dgSeMSZpE1R__LrlSlPhVWyZVUcGBszzXNSIwllGACtB9GEqXeYc_j6lAQIDJiABIVggFFzdor6hBMgrpYLCds8Uu2JtPaaaxKU6LEAUT6QRZ5UiWCA24TI4vED6rrTUjykchoAln67u5GT1nwmzjvrk79HhlQ\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiVWx4SFNUa3VNdnRWRG9WOXk1bGh1OU95TlVQOFA3TVAwUllBVDZJbV9yWSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/nodes.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey\n\nimport (\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/webauthnx/js\"\n)\n\nfunc injectOptions(o []byte) *node.Node {\n\treturn &node.Node{\n\t\tType:  node.Input,\n\t\tGroup: node.PasskeyGroup,\n\t\tMeta:  &node.Meta{},\n\t\tAttributes: &node.InputAttributes{\n\t\t\tName:       node.PasskeyCreateData,\n\t\t\tType:       node.InputAttributeTypeHidden,\n\t\t\tFieldValue: string(o),\n\t\t},\n\t}\n}\n\nfunc passkeyRegister() *node.Node {\n\treturn &node.Node{\n\t\tType:  node.Input,\n\t\tGroup: node.PasskeyGroup,\n\t\tMeta:  &node.Meta{},\n\t\tAttributes: &node.InputAttributes{\n\t\t\tName: node.PasskeyRegister,\n\t\t\tType: node.InputAttributeTypeHidden,\n\t\t},\n\t}\n}\n\nfunc passkeyRegisterTrigger() *node.Node {\n\treturn &node.Node{\n\t\tType:  node.Input,\n\t\tGroup: node.PasskeyGroup,\n\t\tMeta:  &node.Meta{Label: text.NewInfoSelfServiceRegistrationRegisterPasskey()},\n\t\tAttributes: &node.InputAttributes{\n\t\t\tName:           node.PasskeyRegisterTrigger,\n\t\t\tType:           node.InputAttributeTypeButton,\n\t\t\tOnClick:        js.WebAuthnTriggersPasskeyRegistration.String() + \"()\", // defined in webauthn.js\n\t\t\tOnClickTrigger: js.WebAuthnTriggersPasskeyRegistration,\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/passkey_login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\n\t\"github.com/ory/kratos/x/webauthnx/js\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flowhelpers\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/webauthnx\"\n\t\"github.com/ory/x/decoderx\"\n)\n\nvar _ login.AAL1FormHydrator = new(Strategy)\n\nfunc (s *Strategy) populateLoginMethodForPasskeys(r *http.Request, loginFlow *login.Flow) error {\n\tctx := r.Context()\n\n\tloginFlow.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\n\tds, err := loginFlow.IdentitySchema.URL(r.Context(), s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tidentifierLabel, err := login.GetIdentifierLabelFromSchema(r.Context(), ds.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\twebAuthn, err := webauthn.New(s.d.Config().PasskeyConfig(ctx))\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\toption, sessionData, err := webAuthn.BeginDiscoverableLogin()\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tloginFlow.InternalContext, err = sjson.SetBytes(\n\t\tloginFlow.InternalContext,\n\t\tflow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData),\n\t\tsessionData,\n\t)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tinjectWebAuthnOptions, err := json.Marshal(option)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tloginFlow.UI.Nodes.Upsert(node.NewInputField(\n\t\t\"identifier\",\n\t\t\"\",\n\t\tnode.DefaultGroup,\n\t\tnode.InputAttributeTypeText,\n\t\tnode.WithRequiredInputAttribute,\n\t\tfunc(attributes *node.InputAttributes) {\n\t\t\tattributes.Autocomplete = node.InputAttributeAutocompleteUsernameWebauthn\n\t\t},\n\t).WithMetaLabel(identifierLabel))\n\n\tloginFlow.UI.Nodes.Upsert(&node.Node{\n\t\tType:  node.Input,\n\t\tGroup: node.PasskeyGroup,\n\t\tMeta:  &node.Meta{},\n\t\tAttributes: &node.InputAttributes{\n\t\t\tName:       node.PasskeyChallenge,\n\t\t\tType:       node.InputAttributeTypeHidden,\n\t\t\tFieldValue: string(injectWebAuthnOptions),\n\t\t},\n\t})\n\n\t// Inject the WebAuthn JS script only for non-API flows. API flows must not include script nodes.\n\tif loginFlow.Type == flow.TypeBrowser {\n\t\tloginFlow.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\t}\n\n\tpasskeyLoginAttr := &node.InputAttributes{\n\t\tName:          node.PasskeyLogin,\n\t\tType:          node.InputAttributeTypeHidden,\n\t\tOnLoadTrigger: js.WebAuthnTriggersPasskeyLoginAutocompleteInit,\n\t}\n\t// Only attach raw JS onLoad for browser flows; keep onLoadTrigger as a semantic\n\t// enum for all flow types so SPA/native apps can key off of it.\n\tif loginFlow.Type == flow.TypeBrowser {\n\t\tpasskeyLoginAttr.OnLoad = js.WebAuthnTriggersPasskeyLoginAutocompleteInit.String() + \"()\"\n\t}\n\tloginFlow.UI.Nodes.Upsert(&node.Node{\n\t\tType:       node.Input,\n\t\tGroup:      node.PasskeyGroup,\n\t\tMeta:       &node.Meta{},\n\t\tAttributes: passkeyLoginAttr,\n\t})\n\n\treturn nil\n}\n\nfunc (s *Strategy) handleLoginError(r *http.Request, f *login.Flow, err error) error {\n\tif f != nil {\n\t\tf.UI.Nodes.ResetNodes(node.PasskeyLogin)\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn err\n}\n\n// Update Login Flow with Passkey Method\n//\n// swagger:model updateLoginFlowWithPasskeyMethod\ntype updateLoginFlowWithPasskeyMethod struct {\n\t// Method should be set to \"passkey\" when logging in using the Passkey strategy.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Login a WebAuthn Security Key\n\t//\n\t// This must contain the ID of the WebAuthN connection.\n\tLogin string `json:\"passkey_login\"`\n}\n\nfunc (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, _ *session.Session) (i *identity.Identity, err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.passkey.Strategy.Login\")\n\tdefer otelx.End(span, &err)\n\n\tvar p updateLoginFlowWithPasskeyMethod\n\tif err := decoderx.Decode(r, &p,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.MustHTTPRawJSONSchemaCompiler(loginSchema),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tif len(p.Login) > 0 || p.Method == s.SettingsStrategyID() {\n\t\t// This method has only two submit buttons\n\t\tp.Method = s.SettingsStrategyID()\n\t} else {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"no login value and mismatched method\"))\n\t\treturn nil, flow.ErrStrategyNotResponsible\n\t}\n\n\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\treturn s.loginPasswordless(ctx, w, r, f, &p)\n}\n\nfunc (s *Strategy) loginPasswordless(ctx context.Context, w http.ResponseWriter, r *http.Request, f *login.Flow, p *updateLoginFlowWithPasskeyMethod) (i *identity.Identity, err error) {\n\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel1); err != nil {\n\t\ttrace.SpanFromContext(ctx).SetAttributes(attribute.String(\"not_responsible_reason\", \"requested AAL is not AAL1\"))\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tif len(p.Login) == 0 {\n\t\t// Reset all nodes to not confuse users.\n\t\tf.UI.Nodes = node.Nodes{}\n\n\t\tif err := s.populateLoginMethodForPasskeys(r, f); err != nil {\n\t\t\treturn nil, s.handleLoginError(r, f, err)\n\t\t}\n\n\t\tredirectTo := f.AppendTo(s.d.Config().SelfServiceFlowLoginUI(ctx)).String()\n\t\tif x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(redirectTo))\n\t\t} else {\n\t\t\thttp.Redirect(w, r, redirectTo, http.StatusSeeOther)\n\t\t}\n\n\t\treturn nil, errors.WithStack(flow.ErrCompletedByStrategy)\n\t}\n\n\treturn s.loginAuthenticate(ctx, r, f, p, identity.AuthenticatorAssuranceLevel1)\n}\n\nfunc (s *Strategy) loginAuthenticate(ctx context.Context, r *http.Request, f *login.Flow, p *updateLoginFlowWithPasskeyMethod, _ identity.AuthenticatorAssuranceLevel) (*identity.Identity, error) {\n\tweb, err := webauthn.New(s.d.Config().PasskeyConfig(ctx))\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to get webAuthn config.\").WithDebug(err.Error())))\n\t}\n\n\twebAuthnResponse, err := protocol.ParseCredentialRequestResponseBody(strings.NewReader(p.Login))\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to parse WebAuthn response.\").WithDebug(err.Error())))\n\t}\n\n\tvar webAuthnSess webauthn.SessionData\n\tif err := json.Unmarshal([]byte(gjson.GetBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData)).Raw), &webAuthnSess); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrInternalServerError.\n\t\t\tWithReasonf(\"Expected WebAuthN in internal context to be an object but got: %s\", err)))\n\t}\n\twebAuthnSess.UserID = nil\n\n\tuserHandle := webAuthnResponse.Response.UserHandle\n\tcredentialType := identity.CredentialsTypePasskey\n\ti, _, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypePasskey, string(userHandle))\n\tif err != nil {\n\t\t// Migration strategy: Don't give up yet! If we don't find a \"passkey\" credential\n\t\t// here, look for a \"webauthn\" credential next\n\t\tif i, err = s.d.PrivilegedIdentityPool().FindIdentityByWebauthnUserHandle(ctx, userHandle); err != nil {\n\t\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoWebAuthnCredentials()))\n\t\t}\n\t\tcredentialType = identity.CredentialsTypeWebAuthn\n\t}\n\terr = s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, i, identity.ExpandCredentials)\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.\n\t\t\tWithReason(\"Could not load identity credentials\").\n\t\t\tWithWrap(err)), i.ID))\n\t}\n\n\tc, ok := i.GetCredentials(credentialType)\n\tif !ok {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoWebAuthnRegistered()))\n\t}\n\n\tvar o identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(c.Config, &o); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.\n\t\t\tWithReason(\"The WebAuthn credentials could not be decoded properly\").\n\t\t\tWithDebug(err.Error()).\n\t\t\tWithWrap(err)), i.ID))\n\t}\n\n\twebAuthCreds := o.Credentials.PasswordlessOnly(&webAuthnResponse.Response.AuthenticatorData.Flags)\n\t_, err = web.ValidateDiscoverableLogin(\n\t\tfunc(rawID, userHandle []byte) (user webauthn.User, err error) {\n\t\t\treturn webauthnx.NewUser(userHandle, webAuthCreds, web.Config), nil\n\t\t}, webAuthnSess, webAuthnResponse)\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(schema.NewWebAuthnVerifierWrongError(\"#/\")), i.ID))\n\t}\n\n\t// Remove the WebAuthn URL from the internal context now that it is set!\n\tf.InternalContext, err = sjson.DeleteBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData))\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(err), i.ID))\n\t}\n\n\tf.Active = s.ID()\n\tif err = s.d.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Could not update flow\").WithDebug(err.Error())), i.ID))\n\t}\n\n\treturn i, nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactorRefresh(r *http.Request, f *login.Flow, _ *session.Session) error {\n\tctx := r.Context()\n\n\tidentifier, id, _ := flowhelpers.GuessForcedLoginIdentifier(r, s.d, f, s.ID())\n\tif identifier == \"\" {\n\t\treturn nil\n\t}\n\n\tid, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, id.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcred, ok := id.GetCredentials(s.ID())\n\tif !ok {\n\t\t// Identity has no passkey\n\t\treturn nil\n\t}\n\n\tvar conf identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(cred.Config, &conf); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\twebAuthCreds := conf.Credentials.ToWebAuthn()\n\tif len(webAuthCreds) == 0 {\n\t\t// Identity has no webauthn\n\t\treturn nil\n\t}\n\n\tpasskeyIdentifier := s.PasskeyDisplayNameFromIdentity(ctx, id)\n\n\twebAuthn, err := webauthn.New(s.d.Config().PasskeyConfig(ctx))\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\toption, sessionData, err := webAuthn.BeginLogin(&webauthnx.User{\n\t\tName:        passkeyIdentifier,\n\t\tID:          conf.UserHandle,\n\t\tCredentials: webAuthCreds,\n\t\tConfig:      webAuthn.Config,\n\t})\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to initiate passkey login.\").WithDebug(err.Error()))\n\t}\n\n\tf.InternalContext, err = sjson.SetBytes(\n\t\tf.InternalContext,\n\t\tflow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData),\n\t\tsessionData,\n\t)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tinjectWebAuthnOptions, err := json.Marshal(option)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tf.UI.Nodes.Upsert(&node.Node{\n\t\tType:  node.Input,\n\t\tGroup: node.PasskeyGroup,\n\t\tMeta:  &node.Meta{},\n\t\tAttributes: &node.InputAttributes{\n\t\t\tName:       node.PasskeyChallenge,\n\t\t\tType:       node.InputAttributeTypeHidden,\n\t\t\tFieldValue: string(injectWebAuthnOptions),\n\t\t},\n\t})\n\n\t// Inject the WebAuthn JS script only for non-API flows. API flows must not include script nodes.\n\tif f.Type == flow.TypeBrowser {\n\t\tf.UI.Nodes.Append(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\t}\n\n\tf.UI.Nodes.Upsert(&node.Node{\n\t\tType:  node.Input,\n\t\tGroup: node.PasskeyGroup,\n\t\tMeta:  &node.Meta{},\n\t\tAttributes: &node.InputAttributes{\n\t\t\tName: node.PasskeyLogin,\n\t\t\tType: node.InputAttributeTypeHidden,\n\t\t},\n\t})\n\n\tf.UI.Nodes.Append(node.NewInputField(\n\t\tnode.PasskeyLoginTrigger,\n\t\t\"\",\n\t\tnode.PasskeyGroup,\n\t\tnode.InputAttributeTypeButton,\n\t\tnode.WithInputAttributes(func(attr *node.InputAttributes) {\n\t\t\tattr.OnClickTrigger = js.WebAuthnTriggersPasskeyLogin\n\t\t\t// Only attach raw JS handler for browser flows; API flows must stay script-free.\n\t\t\tif f.Type == flow.TypeBrowser {\n\t\t\t\t//nolint:staticcheck\n\t\t\t\tattr.OnClick = js.WebAuthnTriggersPasskeyLogin.String() + \"()\" // this function is defined in webauthn.js\n\t\t\t}\n\t\t}),\n\t).WithMetaLabel(text.NewInfoSelfServiceLoginPasskey()))\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.SetNode(node.NewInputField(\n\t\t\"identifier\",\n\t\tpasskeyIdentifier,\n\t\tnode.DefaultGroup,\n\t\tnode.InputAttributeTypeHidden,\n\t))\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactor(r *http.Request, f *login.Flow) error {\n\tif err := s.populateLoginMethodForPasskeys(r, f); err != nil {\n\t\treturn err\n\t}\n\n\ttrigger := node.NewInputField(\n\t\tnode.PasskeyLoginTrigger,\n\t\t\"\",\n\t\tnode.PasskeyGroup,\n\t\tnode.InputAttributeTypeButton,\n\t\tnode.WithInputAttributes(func(attr *node.InputAttributes) {\n\t\t\tattr.OnClickTrigger = js.WebAuthnTriggersPasskeyLogin\n\t\t\t// Only attach raw JS handler for browser flows; API flows must stay script-free.\n\t\t\tif f.Type == flow.TypeBrowser {\n\t\t\t\t//nolint:staticcheck\n\t\t\t\tattr.OnClick = js.WebAuthnTriggersPasskeyLogin.String() + \"()\" // this function is defined in webauthn.js\n\t\t\t}\n\t\t}),\n\t).WithMetaLabel(text.NewInfoSelfServiceLoginPasskey())\n\n\tf.UI.Nodes.Append(trigger)\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstCredentials(r *http.Request, sr *login.Flow, opts ...login.FormHydratorModifier) error {\n\tctx := r.Context()\n\to := login.NewFormHydratorOptions(opts)\n\n\tvar count int\n\tif o.IdentityHint != nil {\n\t\tvar err error\n\t\t// If we have an identity hint we can perform identity credentials discovery and\n\t\t// hide this credential if it should not be included.\n\t\tcount, err = s.CountActiveFirstFactorCredentials(ctx, o.IdentityHint.Credentials)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif count > 0 || s.d.Config().SecurityAccountEnumerationMitigate(ctx) {\n\t\tsr.UI.Nodes.Append(node.NewInputField(\n\t\t\tnode.PasskeyLoginTrigger,\n\t\t\t\"\",\n\t\t\tnode.PasskeyGroup,\n\t\t\tnode.InputAttributeTypeButton,\n\t\t\tnode.WithInputAttributes(func(attr *node.InputAttributes) {\n\t\t\t\tattr.OnClickTrigger = js.WebAuthnTriggersPasskeyLogin\n\t\t\t\t// Only attach raw JS handler for browser flows; API flows must stay script-free.\n\t\t\t\tif sr.Type == flow.TypeBrowser {\n\t\t\t\t\t//nolint:staticcheck\n\t\t\t\t\tattr.OnClick = js.WebAuthnTriggersPasskeyLogin.String() + \"()\" // this function is defined in webauthn.js\n\t\t\t\t}\n\t\t\t}),\n\t\t).WithMetaLabel(text.NewInfoSelfServiceLoginPasskey()))\n\t}\n\n\tif count == 0 {\n\t\treturn errors.WithStack(idfirst.ErrNoCredentialsFound)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstIdentification(r *http.Request, sr *login.Flow) error {\n\tif err := s.populateLoginMethodForPasskeys(r, sr); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/passkey_login_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\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\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/selfservice/strategy/passkey\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\nvar (\n\t//go:embed fixtures/login/success/identity.json\n\tloginSuccessIdentity []byte\n\t//go:embed fixtures/login/success/credentials.json\n\tloginPasswordlessCredentials []byte\n\t//go:embed fixtures/login/success/internal_context.json\n\tloginPasswordlessContext []byte\n\t//go:embed fixtures/login/success/response.json\n\tloginPasswordlessResponse []byte\n)\n\nfunc TestPopulateLoginMethod(t *testing.T) {\n\tt.Parallel()\n\tfix := newLoginFixture(t)\n\ts := passkey.NewStrategy(fix.reg)\n\n\tt.Run(\"case=API flow builds standard nodes and skips JS\", func(t *testing.T) {\n\t\t// Build a properly initialized API flow to avoid nil UI/container issues.\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/api\", nil).WithContext(t.Context())\n\t\tf, err := login.NewFlow(fix.conf, time.Minute, \"csrf_token\", r, flow.TypeAPI)\n\t\trequire.NoError(t, err)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\n\t\t// For API flows, the strategy should build standard nodes and return before adding JS nodes.\n\t\tassert.Nil(t, s.PopulateLoginMethodFirstFactor(r, f))\n\n\t\t// Assert that the passkey challenge input exists (standard node built for API as well).\n\t\trequire.NotNil(t, f.UI.Nodes.Find(\"passkey_challenge\"))\n\n\t\t// Assert no script nodes are present for API flows (JS injection is skipped).\n\t\tfor _, n := range f.UI.Nodes {\n\t\t\tassert.NotEqual(t, node.Script, n.Type, \"API flow must not include script nodes\")\n\t\t}\n\t})\n}\n\nfunc TestCompleteLogin(t *testing.T) {\n\tt.Parallel()\n\tfix := newLoginFixture(t)\n\n\tt.Run(\"case=should return webauthn.js\", func(t *testing.T) {\n\t\tres, err := fix.publicTS.Client().Get(fix.publicTS.URL + \"/.well-known/ory/webauthn.js\")\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\tassert.Equal(t, \"text/javascript; charset=UTF-8\", res.Header.Get(\"Content-Type\"))\n\t})\n\n\tt.Run(\"flow=passwordless\", func(t *testing.T) {\n\t\tt.Run(\"case=passkey button exists\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, client, fix.publicTS, false, true, false, false)\n\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\"0.attributes.value\",\n\t\t\t\t\"2.attributes.nonce\",\n\t\t\t\t\"2.attributes.src\",\n\t\t\t\t\"5.attributes.value\",\n\t\t\t})\n\t\t})\n\n\t\t// Assert API-specific CSRF/header failures (Cookie/Origin) similar to registration tests\n\t\tt.Run(\"AssertCSRFFailuresAPI\", func(t *testing.T) {\n\t\t\tfix := newLoginFixture(t)\n\t\t\t// Create identity with passkey credentials so the test setup is valid\n\t\t\tfix.createIdentityWithPasskey(t, identity.Credentials{\n\t\t\t\tConfig:  loginPasswordlessCredentials,\n\t\t\t\tVersion: 1,\n\t\t\t})\n\n\t\t\tapiClient := testhelpers.NewDebugClient(t)\n\n\t\t\t// Now test the specific CSRF error causes for API flows: adding Cookie and Origin headers\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tmod    func(http.Header)\n\t\t\t\texpKey string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tmod:    func(h http.Header) { h.Add(\"Cookie\", \"name=bar\") },\n\t\t\t\t\texpKey: \"Cookie\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmod:    func(h http.Header) { h.Add(\"Origin\", \"www.bar.com\") },\n\t\t\t\t\texpKey: \"Origin\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(fmt.Sprintf(\"case=should_fail_with_correct_CSRF_error_cause_local/type=api/%s\", tc.expKey), func(t *testing.T) {\n\t\t\t\t\tf := testhelpers.InitializeLoginFlowViaAPI(t, apiClient, fix.publicTS, false)\n\n\t\t\t\t\t// Attach internal context to the flow\n\t\t\t\t\tinterim, err := fix.reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tinterim.InternalContext = loginPasswordlessContext\n\t\t\t\t\trequire.NoError(t, fix.reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), interim))\n\n\t\t\t\t\tvalues := url.Values{\n\t\t\t\t\t\t\"csrf_token\": {\"invalid_token\"},\n\t\t\t\t\t}\n\t\t\t\t\tvalues.Set(node.PasskeyLogin, string(loginPasswordlessResponse))\n\t\t\t\t\tvalues.Del(\"method\")\n\n\t\t\t\t\treq, err := http.NewRequest(\"POST\", f.Ui.Action, bytes.NewBufferString(testhelpers.EncodeFormAsJSON(t, true, values)))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\t\t\ttc.mod(req.Header)\n\n\t\t\t\t\tres, err := apiClient.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\t\tbodyBytes := ioutilx.MustReadAll(res.Body)\n\t\t\t\t\tactual := string(bodyBytes)\n\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\t\tmsg := gjson.Get(actual, \"ui.messages.0.text\").String()\n\t\t\t\t\tassert.Contains(t, msg, tc.expKey, \"actual payload: %s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=passkey shows error if user tries to sign in but no such user exists\", func(t *testing.T) {\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"method\", \"passkey\")\n\t\t\t\tv.Set(node.PasskeyLogin, string(loginPasswordlessResponse))\n\t\t\t}\n\n\t\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\t\tfix.checkURL(t, shouldRedirect, res)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\tassert.Equal(t, text.NewErrorValidationSuchNoWebAuthnUser().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tbody, res := fix.loginViaBrowser(t, false, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, true, body, res)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tbody, res := fix.loginViaBrowser(t, true, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, false, body, res)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tapiClient := testhelpers.NewDebugClient(t)\n\t\t\t\tf := testhelpers.InitializeLoginFlowViaAPI(t, apiClient, fix.publicTS, false)\n\t\t\t\t// Inject internal context required for replaying the WebAuthn response\n\t\t\t\tinterim, err := fix.reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tinterim.InternalContext = loginPasswordlessContext\n\t\t\t\trequire.NoError(t, fix.reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), interim))\n\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tpayload(values)\n\t\t\t\tbody, res := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\t\tcheck(t, false, body, res)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should fail if passkey login is invalid\", func(t *testing.T) {\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"method\", \"passkey\")\n\t\t\t\tv.Set(\"passkey_login\", \"invalid passkey data\")\n\t\t\t}\n\n\t\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\t\tfix.checkURL(t, shouldRedirect, res)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\tassert.Equal(t, \"Unable to parse WebAuthn response.\", gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tbody, res := fix.loginViaBrowser(t, false, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, true, body, res)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tbody, res := fix.loginViaBrowser(t, true, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, false, body, res)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tapiClient := testhelpers.NewDebugClient(t)\n\t\t\t\tf := testhelpers.InitializeLoginFlowViaAPI(t, apiClient, fix.publicTS, false)\n\t\t\t\t// Inject internal context required for replaying the WebAuthn response\n\t\t\t\tinterim, err := fix.reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tinterim.InternalContext = loginPasswordlessContext\n\t\t\t\trequire.NoError(t, fix.reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), interim))\n\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tpayload(values)\n\t\t\t\tbody, res := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\t\tcheck(t, false, body, res)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should fail if passkey login is empty\", func(t *testing.T) {\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"method\", \"passkey\")\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t_, res := fix.loginViaBrowser(t, false, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tfix.checkURL(t, true, res)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tbody, res := fix.loginViaBrowser(t, true, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tfix.checkURL(t, false, res)\n\t\t\t\tassert.Equal(t, \"browser_location_change_required\", gjson.Get(body, \"error.id\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tapiClient := testhelpers.NewDebugClient(t)\n\t\t\t\tf := testhelpers.InitializeLoginFlowViaAPI(t, apiClient, fix.publicTS, false)\n\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tpayload(values)\n\t\t\t\tbody, res := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\t\tassert.Equal(t, http.StatusUnprocessableEntity, res.StatusCode)\n\t\t\t\tassert.Equal(t, \"browser_location_change_required\", gjson.Get(body, \"error.id\").String(), \"%s\", body)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=fails with invalid internal state\", func(t *testing.T) {\n\t\t\trun := func(t *testing.T, spa bool) {\n\t\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\t// We load our identity which we will use to replay the webauth session\n\t\t\t\tfix.createIdentityWithPasskey(t, identity.Credentials{\n\t\t\t\t\tConfig:  loginPasswordlessCredentials,\n\t\t\t\t\tVersion: 1,\n\t\t\t\t})\n\n\t\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tbody, _, _ := fix.submitWebAuthnLoginWithClient(t, spa, []byte(\"invalid context\"), browserClient, func(values url.Values) {\n\t\t\t\t\tvalues.Set(node.PasskeyLogin, string(loginPasswordlessResponse))\n\t\t\t\t}, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel1))\n\n\t\t\t\tif spa {\n\t\t\t\t\tassert.Equal(\n\t\t\t\t\t\tt,\n\t\t\t\t\t\t\"Expected WebAuthN in internal context to be an object but got: unexpected end of JSON input\",\n\t\t\t\t\t\tgjson.Get(body, \"error.reason\").String(),\n\t\t\t\t\t\t\"%s\", body,\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Equal(\n\t\t\t\t\t\tt,\n\t\t\t\t\t\t\"Expected WebAuthN in internal context to be an object but got: unexpected end of JSON input\",\n\t\t\t\t\t\tgjson.Get(body, \"reason\").String(),\n\t\t\t\t\t\t\"%s\", body,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\trun(t, false)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\trun(t, true)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\tfix.createIdentityWithPasskey(t, identity.Credentials{\n\t\t\t\t\tConfig:  loginPasswordlessCredentials,\n\t\t\t\t\tVersion: 1,\n\t\t\t\t})\n\n\t\t\t\tapiClient := testhelpers.NewDebugClient(t)\n\t\t\t\tf := testhelpers.InitializeLoginFlowViaAPI(t, apiClient, fix.publicTS, false)\n\n\t\t\t\t// Inject invalid internal context\n\t\t\t\tinterim, err := fix.reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tinterim.InternalContext = []byte(\"invalid context\")\n\t\t\t\trequire.NoError(t, fix.reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), interim))\n\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tvalues.Set(node.PasskeyLogin, string(loginPasswordlessResponse))\n\t\t\t\tbody, res := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\n\t\t\t\tassert.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\t\t\t\tassert.Equal(\n\t\t\t\t\tt,\n\t\t\t\t\t\"Expected WebAuthN in internal context to be an object but got: unexpected end of JSON input\",\n\t\t\t\t\tgjson.Get(body, \"error.reason\").String(),\n\t\t\t\t\t\"%s\", body,\n\t\t\t\t)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=succeeds with passwordless login\", func(t *testing.T) {\n\t\t\trun := func(t *testing.T, spa bool) {\n\t\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\t// We load our identity which we will use to replay the webauth session\n\t\t\t\tid := fix.createIdentityWithPasskey(t, identity.Credentials{\n\t\t\t\t\tConfig:  loginPasswordlessCredentials,\n\t\t\t\t\tVersion: 1,\n\t\t\t\t})\n\n\t\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tbody, res, f := fix.submitWebAuthnLoginWithClient(t, spa, loginPasswordlessContext, browserClient, func(values url.Values) {\n\t\t\t\t\tvalues.Set(node.PasskeyLogin, string(loginPasswordlessResponse))\n\t\t\t\t}, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel1))\n\n\t\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t\t\"2.attributes.nonce\",\n\t\t\t\t\t\"2.attributes.src\",\n\t\t\t\t\t\"5.attributes.value\",\n\t\t\t\t})\n\n\t\t\t\tprefix := \"\"\n\t\t\t\tif spa {\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+login.RouteSubmitFlow)\n\t\t\t\t\tprefix = \"session.\"\n\t\t\t\t} else {\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.redirTS.URL)\n\t\t\t\t}\n\n\t\t\t\tassert.True(t, gjson.Get(body, prefix+\"active\").Bool(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel1, gjson.Get(body, prefix+\"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, identity.CredentialsTypePasskey, gjson.Get(body, prefix+\"authentication_methods.#(method==passkey).method\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, id.ID.String(), gjson.Get(body, prefix+\"identity.id\").String(), \"%s\", body)\n\n\t\t\t\tactualFlow, err := fix.reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Empty(t, gjson.GetBytes(actualFlow.InternalContext, flow.PrefixInternalContextKey(identity.CredentialsTypePasskey, passkey.InternalContextKeySessionData)))\n\t\t\t\tif spa {\n\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(body, \"continue_with.0.action\").String(), \"%s\", body)\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"continue_with.0.redirect_browser_to\").String(), fix.conf.SelfServiceBrowserDefaultReturnTo(t.Context()).String(), \"%s\", body)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trunAPI := func(t *testing.T) {\n\t\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\tid := fix.createIdentityWithPasskey(t, identity.Credentials{\n\t\t\t\t\tConfig:  loginPasswordlessCredentials,\n\t\t\t\t\tVersion: 1,\n\t\t\t\t})\n\n\t\t\t\tapiClient := testhelpers.NewDebugClient(t)\n\t\t\t\tf := testhelpers.InitializeLoginFlowViaAPI(t, apiClient, fix.publicTS, false)\n\n\t\t\t\t// Inject internal context\n\t\t\t\tinterim, err := fix.reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tinterim.InternalContext = loginPasswordlessContext\n\t\t\t\trequire.NoError(t, fix.reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), interim))\n\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tvalues.Set(node.PasskeyLogin, string(loginPasswordlessResponse))\n\t\t\t\tbody, res := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\n\t\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t\t\"4.attributes.value\", // passkey_challenge value for API (no script node)\n\t\t\t\t})\n\n\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+login.RouteSubmitFlow)\n\n\t\t\t\tassert.True(t, gjson.Get(body, \"session.active\").Bool(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel1, gjson.Get(body, \"session.authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, identity.CredentialsTypePasskey, gjson.Get(body, \"session.authentication_methods.#(method==passkey).method\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, id.ID.String(), gjson.Get(body, \"session.identity.id\").String(), \"%s\", body)\n\n\t\t\t\tactualFlow, err := fix.reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tassert.Empty(t, gjson.GetBytes(actualFlow.InternalContext, flow.PrefixInternalContextKey(identity.CredentialsTypePasskey, passkey.InternalContextKeySessionData)))\n\t\t\t\t// API flows don't have continue_with redirect\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t\t}\n\n\t\t\t// We test here that login works even if the identity schema contains\n\t\t\t// { webauthn: { identifier: true } } instead of\n\t\t\t// { passkey: { display_name: true } }\n\t\t\tt.Run(\"webauthn_identifier\", func(t *testing.T) {\n\t\t\t\ttesthelpers.SetDefaultIdentitySchema(fix.conf, \"file://./stub/login_webauthn.schema.json\")\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) { run(t, false) })\n\t\t\t\tt.Run(\"type=spa\", func(t *testing.T) { run(t, true) })\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) { runAPI(t) })\n\t\t\t})\n\t\t\tt.Run(\"passkey_display_name\", func(t *testing.T) {\n\t\t\t\ttesthelpers.SetDefaultIdentitySchema(fix.conf, \"file://./stub/login.schema.json\")\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) { run(t, false) })\n\t\t\t\tt.Run(\"type=spa\", func(t *testing.T) { run(t, true) })\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) { runAPI(t) })\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"flow=refresh\", func(t *testing.T) {\n\t\tfix := newLoginFixture(t)\n\t\tfix.conf.MustSet(t.Context(), config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\tloginFixtureSuccessEmail := gjson.GetBytes(loginSuccessIdentity, \"traits.email\").String()\n\n\t\trun := func(t *testing.T, ctx context.Context, id *identity.Identity, context, response []byte, isSPA bool, expectedAAL identity.AuthenticatorAssuranceLevel) {\n\t\t\tbody, res, f := fix.submitWebAuthnLogin(t, ctx, isSPA, id, context, func(values url.Values) {\n\t\t\t\tvalues.Set(\"identifier\", loginFixtureSuccessEmail)\n\t\t\t\tvalues.Set(node.PasskeyLogin, string(response))\n\t\t\t}, testhelpers.InitFlowWithRefresh())\n\t\t\tsnapshotx.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\"0.attributes.value\",\n\t\t\t\t\"2.attributes.nonce\",\n\t\t\t\t\"2.attributes.src\",\n\t\t\t\t\"5.attributes.value\",\n\t\t\t})\n\t\t\tnodes, err := json.Marshal(f.Ui.Nodes)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, loginFixtureSuccessEmail, gjson.GetBytes(nodes, \"#(attributes.name==identifier).attributes.value\").String(), \"%s\", nodes)\n\n\t\t\tprefix := \"\"\n\t\t\tif isSPA {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\t\t\t\tprefix = \"session.\"\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.redirTS.URL, \"%s\", body)\n\t\t\t}\n\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"active\").Bool(), \"%s\", body)\n\n\t\t\tassert.EqualValues(t, expectedAAL, gjson.Get(body, prefix+\"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\tassert.EqualValues(t, identity.CredentialsTypePasskey, gjson.Get(body, prefix+\"authentication_methods.#(method==passkey).method\").String(), \"%s\", body)\n\t\t\tassert.Len(t, gjson.Get(body, prefix+\"authentication_methods\").Array(), 2, \"%s\", body)\n\t\t\tassert.EqualValues(t, id.ID.String(), gjson.Get(body, prefix+\"identity.id\").String(), \"%s\", body)\n\t\t}\n\n\t\texpectedAAL := identity.AuthenticatorAssuranceLevel1\n\n\t\tfor _, tc := range []struct {\n\t\t\tcreds    identity.Credentials\n\t\t\tresponse []byte\n\t\t\tcontext  []byte\n\t\t\tdescript string\n\t\t}{\n\t\t\t{\n\t\t\t\tcreds: identity.Credentials{\n\t\t\t\t\tConfig:  loginPasswordlessCredentials,\n\t\t\t\t\tVersion: 1,\n\t\t\t\t},\n\t\t\t\tcontext:  loginPasswordlessContext,\n\t\t\t\tresponse: loginPasswordlessResponse,\n\t\t\t\tdescript: \"passwordless credentials\",\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"case=refresh \"+tc.descript, func(t *testing.T) {\n\t\t\t\tid := fix.createIdentityWithPasskey(t, tc.creds)\n\n\t\t\t\tfor _, f := range []string{\n\t\t\t\t\t\"browser\",\n\t\t\t\t\t\"spa\",\n\t\t\t\t\t\"api\",\n\t\t\t\t} {\n\t\t\t\t\tt.Run(f, func(t *testing.T) {\n\t\t\t\t\t\tif f == \"api\" {\n\t\t\t\t\t\t\t// API refresh flow\n\t\t\t\t\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, fix.reg, id)\n\t\t\t\t\t\t\t// Pass true for forced to enable refresh in API flow\n\t\t\t\t\t\t\tapiFlow := testhelpers.InitializeLoginFlowViaAPI(t, apiClient, fix.publicTS, true)\n\n\t\t\t\t\t\t\t// Inject internal context\n\t\t\t\t\t\t\tinterim, err := fix.reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(apiFlow.Id))\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\tinterim.InternalContext = tc.context\n\t\t\t\t\t\t\trequire.NoError(t, fix.reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), interim))\n\n\t\t\t\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(apiFlow.Ui.Nodes)\n\t\t\t\t\t\t\tvalues.Set(\"identifier\", loginFixtureSuccessEmail)\n\t\t\t\t\t\t\tvalues.Set(node.PasskeyLogin, string(tc.response))\n\t\t\t\t\t\t\tbody, res := testhelpers.LoginMakeRequest(t, true, false, apiFlow, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\n\t\t\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\n\t\t\t\t\t\t\tassert.True(t, gjson.Get(body, \"session.active\").Bool(), \"%s\", body)\n\t\t\t\t\t\t\tassert.EqualValues(t, expectedAAL, gjson.Get(body, \"session.authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\t\t\t\tassert.EqualValues(t, identity.CredentialsTypePasskey, gjson.Get(body, \"session.authentication_methods.#(method==passkey).method\").String(), \"%s\", body)\n\t\t\t\t\t\t\tassert.Len(t, gjson.Get(body, \"session.authentication_methods\").Array(), 2, \"%s\", body)\n\t\t\t\t\t\t\tassert.EqualValues(t, id.ID.String(), gjson.Get(body, \"session.identity.id\").String(), \"%s\", body)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trun(t, t.Context(), id, tc.context, tc.response, f == \"spa\", expectedAAL)\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\nfunc createIdentity(t *testing.T, ctx context.Context, reg driver.Registry, id uuid.UUID) *identity.Identity {\n\ti := identity.NewIdentity(\"default\")\n\ti.SetCredentials(identity.CredentialsTypePasskey, identity.Credentials{\n\t\tIdentifiers: []string{id.String()},\n\t\tConfig:      loginPasswordlessCredentials,\n\t\tType:        identity.CredentialsTypePasskey,\n\t\tVersion:     1,\n\t})\n\n\trequire.NoError(t, reg.IdentityManager().Create(ctx, i))\n\treturn i\n}\n\nfunc TestFormHydration(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePasskey)+\".enabled\", true)\n\tctx = contextx.WithConfigValue(\n\t\tctx,\n\t\tconfig.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePasskey)+\".config\",\n\t\tmap[string]interface{}{\n\t\t\t\"rp\": map[string]interface{}{\n\t\t\t\t\"display_name\": \"foo\",\n\t\t\t\t\"id\":           \"localhost\",\n\t\t\t\t\"origins\":      []string{\"http://localhost\"},\n\t\t\t},\n\t\t},\n\t)\n\tctx = testhelpers.WithDefaultIdentitySchema(ctx, \"file://stub/login.schema.json\")\n\n\ts, err := reg.AllLoginStrategies().Strategy(identity.CredentialsTypePasskey)\n\trequire.NoError(t, err)\n\tfh, ok := s.(login.AAL1FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f *login.Flow) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.UI.Nodes.ResetNodes(\"csrf_token\")\n\t\tf.UI.Nodes.ResetNodes(\"passkey_challenge\")\n\t\tsnapshotx.SnapshotT(t, f.UI.Nodes, snapshotx.ExceptNestedKeys(\"nonce\", \"src\"))\n\t}\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *login.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := login.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactor\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactor(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactorRefresh\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\n\t\tid := createIdentity(t, ctx, reg, x.NewUUID())\n\t\tr.Header = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\tf.Refresh = true\n\n\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstCredentials\", func(t *testing.T) {\n\t\tt.Run(\"case=no options\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecurityAccountEnumerationMitigate, false)\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecurityAccountEnumerationMitigate, true)\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=WithIdentifier\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecurityAccountEnumerationMitigate, false)\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecurityAccountEnumerationMitigate, true)\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=WithIdentityHint\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecurityAccountEnumerationMitigate, true)\n\n\t\t\t\tid := identity.NewIdentity(\"test-provider\")\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySecurityAccountEnumerationMitigate, false)\n\n\t\t\t\tt.Run(\"case=identity has passkey\", func(t *testing.T) {\n\t\t\t\t\tidentifier := x.NewUUID()\n\t\t\t\t\tid := createIdentity(t, ctx, reg, identifier)\n\n\t\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\t\trequire.NoError(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)))\n\t\t\t\t\t// Verify passkey button is present\n\t\t\t\t\tassert.NotNil(t, f.UI.Nodes.Find(node.PasskeyLoginTrigger), \"passkey button should be present when user has credentials\")\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=identity does not have a passkey\", func(t *testing.T) {\n\t\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\t// Verify passkey button is NOT present\n\t\t\t\t\tassert.Nil(t, f.UI.Nodes.Find(node.PasskeyLoginTrigger), \"passkey button should not be present when user has no credentials\")\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstIdentification\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodIdentifierFirstIdentification(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/passkey_registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/webauthnx\"\n\t\"github.com/ory/x/randx\"\n)\n\nvar _ registration.FormHydrator = new(Strategy)\n\n// Update Registration Flow with Passkey Method\n//\n// swagger:model updateRegistrationFlowWithPasskeyMethod\ntype updateRegistrationFlowWithPasskeyMethod struct {\n\t// Register a WebAuthn Security Key\n\t//\n\t// It is expected that the JSON returned by the WebAuthn registration process\n\t// is included here.\n\tRegister string `json:\"passkey_register\"`\n\n\t// CSRFToken is the anti-CSRF token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// The identity's traits\n\t//\n\t// required: true\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// Method\n\t//\n\t// Should be set to \"passkey\" when trying to add, update, or remove a Passkey.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Flow is flow ID.\n\t//\n\t// swagger:ignore\n\tFlow string `json:\"flow\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\"`\n}\n\nfunc (s *Strategy) handleRegistrationError(_ http.ResponseWriter, r *http.Request, f *registration.Flow, p *updateRegistrationFlowWithPasskeyMethod, err error) error {\n\tif f != nil {\n\t\tif p != nil {\n\t\t\tfor _, n := range container.NewFromJSON(\"\", node.DefaultGroup, p.Traits, \"traits\").Nodes {\n\t\t\t\t// we only set the value and not the whole field because we want to keep types from the initial form generation\n\t\t\t\tf.UI.Nodes.SetValueAttribute(n.ID(), n.Attributes.GetValue())\n\t\t\t}\n\t\t}\n\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (s *Strategy) decode(r *http.Request, ds *url.URL) (*updateRegistrationFlowWithPasskeyMethod, error) {\n\tvar p updateRegistrationFlowWithPasskeyMethod\n\terr := registration.DecodeBody(&p, r, registrationSchema, ds)\n\treturn &p, err\n}\n\nfunc (s *Strategy) Register(w http.ResponseWriter, r *http.Request, regFlow *registration.Flow, ident *identity.Identity) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.passkey.Strategy.Register\")\n\tdefer otelx.End(span, &err)\n\n\tds, err := regFlow.IdentitySchema.URL(ctx, s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tparams, err := s.decode(r, ds)\n\tif err != nil {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, err)\n\t}\n\n\tregFlow.TransientPayload = params.TransientPayload\n\n\tif params.Register == \"\" ||\n\t\tparams.Register == \"true\" { // The React SDK sends \"true\" on empty values, so we ignore these.\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"register is empty\"))\n\t\treturn flow.ErrStrategyNotResponsible\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, regFlow.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, params.CSRFToken); err != nil {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, err)\n\t}\n\n\tparams.Method = s.ID().String()\n\tif err := flow.MethodEnabledAndAllowed(ctx, regFlow.GetFlowName(), params.Method, params.Method, s.d); err != nil {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, err)\n\t}\n\n\tif len(params.Traits) == 0 {\n\t\tparams.Traits = json.RawMessage(\"{}\")\n\t}\n\tident.Traits = identity.Traits(params.Traits)\n\n\twebAuthnSession := gjson.GetBytes(regFlow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData))\n\tif !webAuthnSession.IsObject() {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN in internal context to be an object.\")))\n\t}\n\tvar webAuthnSess webauthn.SessionData\n\tif err := json.Unmarshal([]byte(webAuthnSession.Raw), &webAuthnSess); err != nil {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN in internal context to be an object but got: %s\", err)))\n\t}\n\n\tif len(webAuthnSess.UserID) == 0 {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN session data to contain a user ID\")))\n\t}\n\n\twebAuthnResponse, err := protocol.ParseCredentialCreationResponseBody(strings.NewReader(params.Register))\n\tif err != nil {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, errors.WithStack(\n\t\t\therodot.ErrBadRequest.WithReasonf(\"Unable to parse WebAuthn response: %s\", err)))\n\t}\n\n\twebAuthn, err := webauthn.New(s.d.Config().PasskeyConfig(ctx))\n\tif err != nil {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Unable to get webAuthn config\").WithDebug(err.Error())))\n\t}\n\n\tcredential, err := webAuthn.CreateCredential(&webauthnx.User{\n\t\tID:     webAuthnSess.UserID,\n\t\tConfig: webAuthn.Config,\n\t}, webAuthnSess, webAuthnResponse)\n\tif err != nil {\n\t\tif devErr := new(protocol.Error); errors.As(err, &devErr) {\n\t\t\ts.d.Logger().WithError(err).WithField(\"error_devinfo\", devErr.DevInfo).Error(\"Failed to create WebAuthn credential\")\n\t\t}\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Unable to create WebAuthn credential: %s\", err)))\n\t}\n\n\tcredentialWebAuthn := identity.CredentialFromWebAuthn(credential, true)\n\tcredentialWebAuthnConfig, err := json.Marshal(identity.CredentialsWebAuthnConfig{\n\t\tCredentials: identity.CredentialsWebAuthn{*credentialWebAuthn},\n\t\tUserHandle:  webAuthnSess.UserID,\n\t})\n\tif err != nil {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Unable to encode identity credentials.\").WithDebug(err.Error())))\n\t}\n\n\tident.UpsertCredentialsConfig(s.ID(), credentialWebAuthnConfig, 1)\n\tpasskeyCred, _ := ident.GetCredentials(s.ID())\n\tpasskeyCred.Identifiers = []string{string(webAuthnSess.UserID)}\n\tident.SetCredentials(s.ID(), *passkeyCred)\n\tif err := s.validateCredentials(ctx, ident); err != nil {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, err)\n\t}\n\n\tif err := s.d.RegistrationFlowPersister().UpdateRegistrationFlow(ctx, regFlow); err != nil {\n\t\treturn s.handleRegistrationError(w, r, regFlow, params, err)\n\t}\n\n\treturn nil\n}\n\ntype passkeyCreateData struct {\n\tCredentialOptions    *protocol.CredentialCreation `json:\"credentialOptions\"`\n\tDisplayNameFieldName string                       `json:\"displayNameFieldName\"`\n}\n\nfunc (s *Strategy) PopulateRegistrationMethod(r *http.Request, f *registration.Flow) error {\n\tctx := r.Context()\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\topts, err := s.hydratePassKeyRegistrationOptions(ctx, f)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.Nodes.Upsert(injectOptions(opts))\n\n\ttrigger := passkeyRegisterTrigger()\n\tif f.Type == flow.TypeAPI {\n\t\t// API flows should not have raw JS onClick handlers, but keep onClickTrigger\n\t\t// as a semantic enum for SPA/native apps to key off of.\n\t\tif attr, ok := trigger.Attributes.(*node.InputAttributes); ok {\n\t\t\tattr.OnClick = \"\"\n\t\t}\n\t}\n\tf.UI.Nodes.Upsert(trigger)\n\n\tif f.Type == flow.TypeBrowser {\n\t\tf.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\t}\n\n\tf.UI.Nodes.Upsert(passkeyRegister())\n\treturn nil\n}\n\nfunc (s *Strategy) validateCredentials(ctx context.Context, i *identity.Identity) error {\n\tif err := s.d.IdentityValidator().Validate(ctx, i); err != nil {\n\t\treturn err\n\t}\n\n\tc := i.GetCredentialsOr(identity.CredentialsTypePasskey, &identity.Credentials{})\n\tif len(c.Identifiers) == 0 {\n\t\treturn schema.NewMissingIdentifierError()\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodCredentials(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\tctx := r.Context()\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\topts, err := s.hydratePassKeyRegistrationOptions(ctx, f)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.Nodes.Upsert(injectOptions(opts))\n\n\ttrigger := passkeyRegisterTrigger()\n\tif f.Type == flow.TypeAPI {\n\t\t// API flows should not have raw JS onClick handlers, but keep onClickTrigger\n\t\t// as a semantic enum for SPA/native apps to key off of.\n\t\tif attr, ok := trigger.Attributes.(*node.InputAttributes); ok {\n\t\t\tattr.OnClick = \"\"\n\t\t}\n\t}\n\tf.UI.Nodes.Upsert(trigger)\n\n\tif f.Type == flow.TypeBrowser {\n\t\tf.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\t}\n\n\tf.UI.Nodes.Upsert(passkeyRegister())\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodProfile(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\tctx := r.Context()\n\n\topts, err := s.hydratePassKeyRegistrationOptions(ctx, f)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.Nodes.RemoveMatching(injectOptions(opts))\n\tf.UI.Nodes.RemoveMatching(passkeyRegisterTrigger())\n\tf.UI.Nodes.RemoveMatching(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\tf.UI.Nodes.RemoveMatching(passkeyRegister())\n\treturn nil\n}\n\nfunc (s *Strategy) hydratePassKeyRegistrationOptions(ctx context.Context, f *registration.Flow) ([]byte, error) {\n\tdefaultSchemaURL, err := f.IdentitySchema.URL(ctx, s.d.Config())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif options := gjson.GetBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionOptions)); options.IsObject() {\n\t\treturn []byte(options.Raw), nil\n\t}\n\n\tcreateData := new(passkeyCreateData)\n\tfieldName, err := s.PasskeyDisplayNameFromSchema(ctx, defaultSchemaURL.String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcreateData.DisplayNameFieldName = fieldName\n\n\twebAuthn, err := webauthn.New(s.d.Config().PasskeyConfig(ctx))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tuser := &webauthnx.User{\n\t\tName:   \"\",\n\t\tID:     []byte(randx.MustString(64, randx.AlphaNum)),\n\t\tConfig: s.d.Config().PasskeyConfig(ctx),\n\t}\n\n\toption, sessionData, err := webAuthn.BeginRegistration(user)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tcreateData.CredentialOptions = option\n\tinjectWebAuthnOptions, err := json.Marshal(createData)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tf.InternalContext, err = sjson.SetBytes(\n\t\tf.InternalContext,\n\t\tflow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData),\n\t\tsessionData,\n\t)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tf.InternalContext, err = sjson.SetRawBytes(\n\t\tf.InternalContext,\n\t\tflow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionOptions),\n\t\tinjectWebAuthnOptions,\n\t)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn injectWebAuthnOptions, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/passkey_registration_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\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\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/registrationhelpers\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/randx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nvar (\n\tflows = []string{\"spa\", \"browser\", \"api\"}\n\n\t//go:embed fixtures/registration/success/browser/response.json\n\tregistrationFixtureSuccessResponse []byte\n\n\t//go:embed fixtures/registration/success/browser/internal_context.json\n\tregistrationFixtureSuccessBrowserInternalContext []byte\n\n\t//go:embed fixtures/registration/success/android/response.json\n\tregistrationFixtureSuccessAndroidResponse []byte\n\n\t//go:embed fixtures/registration/success/android/internal_context.json\n\tregistrationFixtureSuccessAndroidInternalContext []byte\n\n\t//go:embed fixtures/registration/failure/internal_context_missing_user_id.json\n\tregistrationFixtureFailureInternalContextMissingUserID []byte\n\n\t//go:embed fixtures/registration/failure/internal_context_wrong_user_id.json\n\tregistrationFixtureFailureInternalContextWrongUserID []byte\n)\n\nfunc flowIsSPA(flow string) bool {\n\treturn flow == \"spa\"\n}\n\nfunc TestRegistration(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"AssertCommonErrorCases\", func(t *testing.T) {\n\t\tregistrationhelpers.AssertCommonErrorCases(t, flows)\n\t})\n\n\tt.Run(\"AssertRegistrationRespectsValidation\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\treg := newRegistrationRegistry(t)\n\t\tregistrationhelpers.AssertRegistrationRespectsValidation(t, reg, flows, func(v url.Values) {\n\t\t\tv.Del(\"traits.foobar\")\n\t\t\tv.Set(node.PasskeyRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t})\n\t})\n\n\tt.Run(\"AssertCSRFFailures\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t// Use the shared helper for browser and spa flows (they already provide\n\t\t// the WebAuthn internal context via the UI flow initialization).\n\t\treg := newRegistrationRegistry(t)\n\t\tregistrationhelpers.AssertCSRFFailures(t, reg, []string{\"spa\", \"browser\"}, func(v url.Values) {\n\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\t\tv.Del(\"method\")\n\t\t})\n\n\t\t// Prepare API flow once and persist the internal context so the\n\t\t// subsequent header-mangling subtests can reuse it.\n\t\tfix := newRegistrationFixture(t)\n\t\tapiClient := testhelpers.NewDebugClient(t)\n\t\tf := testhelpers.InitializeRegistrationFlowViaAPI(t, apiClient, fix.publicTS)\n\n\t\tinterim, err := fix.reg.RegistrationFlowPersister().GetRegistrationFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\trequire.NoError(t, err)\n\t\tinterim.InternalContext = registrationFixtureSuccessBrowserInternalContext\n\t\trequire.NoError(t, fix.reg.RegistrationFlowPersister().UpdateRegistrationFlow(t.Context(), interim))\n\n\t\tvalues := url.Values{\n\t\t\t\"csrf_token\":      {\"invalid_token\"},\n\t\t\t\"traits.username\": {testhelpers.RandomEmail()},\n\t\t\t\"traits.foobar\":   {\"bar\"},\n\t\t}\n\t\tvalues.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\tvalues.Del(\"method\")\n\n\t\t// Check that missing CSRF passes for API (registration happens)\n\t\tactual, res := testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\trequire.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.NotEmpty(t, gjson.Get(actual, \"identity.id\").Raw, \"%s\", actual)\n\n\t\t// Now test the specific CSRF error causes for API flows: adding Cookie and Origin headers\n\t\tfor _, tc := range []struct {\n\t\t\tmod    func(http.Header)\n\t\t\texp    string\n\t\t\texpKey string\n\t\t}{\n\t\t\t{\n\t\t\t\tmod:    func(h http.Header) { h.Add(\"Cookie\", \"name=bar\") },\n\t\t\t\texp:    \"The HTTP Request Header included the \\\"Cookie\\\" key\",\n\t\t\t\texpKey: \"Cookie\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tmod:    func(h http.Header) { h.Add(\"Origin\", \"www.bar.com\") },\n\t\t\t\texp:    \"The HTTP Request Header included the \\\"Origin\\\" key\",\n\t\t\t\texpKey: \"Origin\",\n\t\t\t},\n\t\t} {\n\t\t\t// Name this test to match the project's existing case naming and mark it as a local variant.\n\t\t\tt.Run(fmt.Sprintf(\"case=should_fail_with_correct_CSRF_error_cause_local/type=api/%s\", tc.expKey), func(t *testing.T) {\n\t\t\t\tf2 := testhelpers.InitializeRegistrationFlowViaAPI(t, apiClient, fix.publicTS)\n\n\t\t\t\t// attach internal context to the new flow\n\t\t\t\tinterim2, err := fix.reg.RegistrationFlowPersister().GetRegistrationFlow(t.Context(), uuid.FromStringOrNil(f2.Id))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tinterim2.InternalContext = registrationFixtureSuccessBrowserInternalContext\n\t\t\t\trequire.NoError(t, fix.reg.RegistrationFlowPersister().UpdateRegistrationFlow(t.Context(), interim2))\n\n\t\t\t\treq, err := http.NewRequest(\"POST\", f2.Ui.Action, bytes.NewBufferString(testhelpers.EncodeFormAsJSON(t, true, values)))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\t\t\ttc.mod(req.Header)\n\n\t\t\t\tres, err := apiClient.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tbodyBytes := ioutilx.MustReadAll(res.Body)\n\t\t\t\tactual := string(bodyBytes)\n\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\n\t\t\t\t// Extract the first ui message text (API error payload)\n\t\t\t\tmsg := gjson.Get(actual, \"ui.messages.0.text\").String()\n\t\t\t\t// Ensure the API error mentions the header key we added (Cookie/Origin)\n\t\t\t\tassert.Contains(t, msg, tc.expKey, \"actual payload: %s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"AssertSchemaDoesNotExist\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\treg := newRegistrationRegistry(t)\n\t\tregistrationhelpers.AssertSchemaDoesNotExist(t, reg, flows, func(v url.Values) {\n\t\t\tv.Set(node.PasskeyRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t})\n\t})\n\n\tt.Run(\"case=passkey button does not exist when passwordless is disabled\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t, configx.WithValue(config.ViperKeyPasskeyEnabled, false))\n\t\tuiFlows := []string{\"spa\", \"browser\"}\n\t\tfor _, flowType := range uiFlows {\n\t\t\tflowType := flowType\n\t\t\tt.Run(flowType, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tflo := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, fix.publicTS, flowIsSPA(flowType), false, false)\n\t\t\t\ttesthelpers.SnapshotTExcept(t, flo.Ui.Nodes, []string{\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=passkey button exists\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\tuiFlows := []string{\"spa\", \"browser\"}\n\t\tfor _, flowType := range uiFlows {\n\t\t\tflowType := flowType\n\t\t\tt.Run(flowType, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, fix.publicTS, flowIsSPA(flowType), false, false)\n\t\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t\t\"3.attributes.src\",\n\t\t\t\t\t\"3.attributes.nonce\",\n\t\t\t\t\t\"6.attributes.value\",\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should return an error because not passing validation\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\temail := testhelpers.RandomEmail()\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"traits.username\", email)\n\t\t\tv.Del(\"traits.foobar\")\n\t\t\tv.Set(node.PasskeyRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t}\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tactual := registrationhelpers.ExpectValidationError(t.Context(), t, fix.publicTS, fix.conf, f, values)\n\n\t\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), fix.publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0\").String(), `Property foobar is missing`, \"%s\", actual)\n\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should reject invalid transient payload\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\temail := testhelpers.RandomEmail()\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"traits.username\", email)\n\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\tv.Set(\"transient_payload\", \"42\")\n\t\t\tv.Set(node.PasskeyRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t}\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tactual := registrationhelpers.ExpectValidationError(t.Context(), t, fix.publicTS, fix.conf, f, values)\n\n\t\t\t\tassert.NotEmptyf(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\t\tassert.Containsf(t, gjson.Get(actual, \"ui.action\").String(), fix.publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\tassert.Equalf(t, \"bar\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Equalf(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Equalf(t, int64(4000026), gjson.Get(actual, \"ui.nodes.#(attributes.name==transient_payload).messages.0.id\").Int(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should return an error because passkey response is invalid\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\temail := testhelpers.RandomEmail()\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"traits.username\", email)\n\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\tv.Set(node.PasskeyRegister, \"invalid\")\n\t\t\tv.Set(\"method\", \"passkey\")\n\t\t}\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tactual, _, _ := fix.submitPasskeyRegistration(t.Context(), t, f, testhelpers.NewClientWithCookies(t), values)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), fix.publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), node.PasskeyRegister, \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\tassert.Equal(t, \"bazbar\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.messages.0\").String(), `Unable to parse WebAuthn response: Parse error for Registration`, \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should return an error because internal context is invalid\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\temail := testhelpers.RandomEmail()\n\n\t\tfor _, tc := range []struct {\n\t\t\tname            string\n\t\t\tinternalContext string\n\t\t}{{\n\t\t\tname:            \"invalid json\",\n\t\t\tinternalContext: \"invalid\",\n\t\t}, {\n\t\t\tname:            \"missing user ID\",\n\t\t\tinternalContext: string(registrationFixtureFailureInternalContextMissingUserID),\n\t\t}, {\n\t\t\tname:            \"wrong user ID\",\n\t\t\tinternalContext: string(registrationFixtureFailureInternalContextWrongUserID),\n\t\t}} {\n\t\t\ttc := tc\n\t\t\tt.Run(\"context=\"+tc.name, func(t *testing.T) {\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\t\tv.Del(\"method\")\n\t\t\t\t}\n\n\t\t\t\tfor _, f := range flows {\n\t\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\t\tactual, _, _ := fix.submitPasskeyRegistration(t.Context(), t, f, testhelpers.NewClientWithCookies(t), values,\n\t\t\t\t\t\t\twithInternalContext(sqlxx.JSONRawMessage(tc.internalContext)))\n\t\t\t\t\t\tif flowIsSPA(f) || f == \"api\" {\n\t\t\t\t\t\t\tassert.Equal(t, \"Internal Server Error\", gjson.Get(actual, \"error.status\").String(), \"%s\", actual)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Browser uses top-level status\n\t\t\t\t\t\t\tassert.Equal(t, \"Internal Server Error\", gjson.Get(actual, \"status\").String(), \"%s\", actual)\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\tt.Run(\"case=should fail to create identity if schema is missing the identifier\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\ttesthelpers.SetDefaultIdentitySchema(fix.conf, \"file://./stub/noid.schema.json\")\n\t\temail := testhelpers.RandomEmail()\n\n\t\tuiFlows := []string{\"spa\", \"browser\"}\n\t\tfor _, f := range uiFlows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tisSPA := f == \"spa\"\n\t\t\t\tregFlow := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, fix.publicTS, isSPA, false, false)\n\n\t\t\t\t// fill out traits and click on \"sign up with passkey\"\n\t\t\t\turlValues := testhelpers.SDKFormFieldsToURLValues(regFlow.Ui.Nodes)\n\t\t\t\turlValues.Set(\"traits.email\", email)\n\t\t\t\turlValues.Set(\"method\", \"passkey\")\n\t\t\t\tactual, _ := testhelpers.RegistrationMakeRequest(t, false, isSPA, regFlow, client, urlValues.Encode())\n\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), fix.publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), \"csrf_token\", \"traits.email\")\n\t\t\t\tassert.Equal(t, text.NewErrorValidationRegistrationNoStrategyFound().Text, gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tgetPrefix := func(f string) (prefix string) {\n\t\tif f == \"spa\" || f == \"api\" {\n\t\t\tprefix = \"session.\"\n\t\t}\n\t\treturn\n\t}\n\n\tt.Run(\"successful registration\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\tt.Cleanup(fix.disableSessionAfterRegistration)\n\n\t\tvalues := func(email string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\tv.Del(\"method\")\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"case=should create the identity but not a session\", func(t *testing.T) {\n\t\t\tfix.useRedirNoSessionTS()\n\t\t\tt.Cleanup(fix.useRedirTS)\n\t\t\tfix.disableSessionAfterRegistration()\n\n\t\t\tfor _, f := range flows {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := f + \"-\" + testhelpers.RandomEmail()\n\t\t\t\t\tuserID := f + \"-user-\" + randx.MustString(8, randx.AlphaNum)\n\t\t\t\t\tactual := fix.makeSuccessfulRegistration(t, f, fix.redirNoSessionTS.URL+\"/registration-return-ts\", values(email), withUserID(userID))\n\n\t\t\t\t\tif f == \"spa\" || f == \"api\" {\n\t\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.False(t, gjson.Get(actual, \"session\").Exists(), \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, \"null\\n\", actual, \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t}\n\n\t\t\t\t\ti, _, err := fix.reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePasskey, userID)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, \"aal1\", i.InternalAvailableAAL.String)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should accept valid transient payload\", func(t *testing.T) {\n\t\t\tfix.useRedirNoSessionTS()\n\t\t\tt.Cleanup(fix.useRedirTS)\n\t\t\tfix.disableSessionAfterRegistration()\n\n\t\t\tfor _, f := range []string{\"spa\", \"browser\", \"api\"} {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tuserID := f + \"-user-\" + randx.MustString(8, randx.AlphaNum)\n\t\t\t\t\tactual := fix.makeSuccessfulRegistration(t, f, fix.redirNoSessionTS.URL+\"/registration-return-ts\", func(v url.Values) {\n\t\t\t\t\t\tvalues(email)(v)\n\t\t\t\t\t\tv.Set(\"transient_payload.stuff\", \"42\")\n\t\t\t\t\t}, withUserID(userID))\n\n\t\t\t\t\tif f == \"spa\" {\n\t\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.False(t, gjson.Get(actual, \"session\").Exists(), \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif f == \"api\" {\n\t\t\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\t\t\tassert.False(t, gjson.Get(actual, \"session\").Exists(), \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tassert.Equal(t, \"null\\n\", actual, \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\ti, _, err := fix.reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePasskey, userID)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\n\t\t\t\t\tif f == \"spa\" {\n\t\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(actual, \"continue_with.0.action\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"continue_with.0.redirect_browser_to\").String(), fix.redirNoSessionTS.URL+\"/registration-return-ts\", \"%s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should create the identity and a session and use the correct schema\", func(t *testing.T) {\n\t\t\tfix.enableSessionAfterRegistration()\n\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"advanced-user\")\n\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t\t{ID: \"does-not-exist\", URL: \"file://./stub/profile.schema.json\"},\n\t\t\t\t{ID: \"advanced-user\", URL: \"file://./stub/registration.schema.json\"},\n\t\t\t})\n\n\t\t\tfor _, f := range []string{\"spa\", \"browser\", \"api\"} {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tuserID := f + \"-user-\" + randx.MustString(8, randx.AlphaNum)\n\t\t\t\t\tactual := fix.makeSuccessfulRegistration(t, f, fix.redirTS.URL+\"/registration-return-ts\", values(email), withUserID(userID))\n\n\t\t\t\t\tprefix := getPrefix(f)\n\n\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, prefix+\"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\tassert.True(t, gjson.Get(actual, prefix+\"active\").Bool(), \"%s\", actual)\n\n\t\t\t\t\ti, _, err := fix.reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePasskey, userID)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=not able to create the same account twice\", func(t *testing.T) {\n\t\t\tfix.enableSessionAfterRegistration()\n\t\t\ttesthelpers.SetDefaultIdentitySchema(fix.conf, \"file://./stub/registration.schema.json\")\n\n\t\t\tfor _, f := range []string{\"spa\", \"browser\", \"api\"} {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tuserID := f + \"-user-\" + randx.MustString(8, randx.AlphaNum)\n\t\t\t\t\tactual := fix.makeSuccessfulRegistration(t, f, fix.redirTS.URL+\"/registration-return-ts\", values(email), withUserID(userID))\n\t\t\t\t\tassert.True(t, gjson.Get(actual, getPrefix(f)+\"active\").Bool(), \"%s\", actual)\n\n\t\t\t\t\tactual, _, _ = fix.makeRegistration(t, f, values(email))\n\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), fix.publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), \"csrf_token\", \"traits.username\")\n\t\t\t\t\tassert.Equal(t,\n\t\t\t\t\t\t\"You tried signing in with \"+email+\" which is already in use by another account. You can sign in using your passkey.\",\n\t\t\t\t\t\tgjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=reset previous form errors\", func(t *testing.T) {\n\t\t\tfix.enableSessionAfterRegistration()\n\t\t\ttesthelpers.SetDefaultIdentitySchema(fix.conf, \"file://./stub/registration.schema.json\")\n\n\t\t\tfor _, f := range []string{\"spa\", \"browser\", \"api\"} {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tactual, _, _ := fix.makeRegistration(t, f, func(v url.Values) {\n\t\t\t\t\t\tv.Del(\"traits.username\")\n\t\t\t\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\t\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\t\t})\n\t\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).messages.0\").String(), `Property username is missing`, \"%s\", actual)\n\n\t\t\t\t\tactual, _, _ = fix.makeRegistration(t, f, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\t\t\tv.Del(\"traits.foobar\")\n\t\t\t\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\t\t\tv.Del(\"method\")\n\t\t\t\t\t})\n\t\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0\").String(), `Property foobar is missing`, \"%s\", actual)\n\t\t\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).messages\").Array())\n\t\t\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.messages\").Array())\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should create the identity when using android\", func(t *testing.T) {\n\t\t\tfix.useRedirNoSessionTS()\n\t\t\tt.Cleanup(fix.useRedirTS)\n\t\t\tfix.disableSessionAfterRegistration()\n\n\t\t\tprevRPID := fix.conf.GetProvider(t.Context()).String(config.ViperKeyPasskeyRPID)\n\t\t\tprevOrigins := fix.conf.GetProvider(t.Context()).String(config.ViperKeyPasskeyRPOrigins)\n\n\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeyPasskeyRPID, \"www.troweprice.com\")\n\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeyPasskeyRPOrigins, []string{\"android:apk-key-hash:S2RfNYgJmQiKgd6-sdbjW7phcL_OTP4vGE8L51Q2GB0\"})\n\t\t\tt.Cleanup(func() {\n\t\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeyPasskeyRPID, prevRPID)\n\t\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeyPasskeyRPOrigins, prevOrigins)\n\t\t\t})\n\n\t\t\tfor _, f := range flows {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := f + \"-\" + testhelpers.RandomEmail()\n\t\t\t\t\tuserID := f + \"-user-\" + randx.MustString(8, randx.AlphaNum)\n\n\t\t\t\t\texpectReturnTo := fix.redirNoSessionTS.URL + \"/registration-return-ts\"\n\t\t\t\t\tactual, res, _ := fix.submitPasskeyAndroidRegistration(t, f, testhelpers.NewClientWithCookies(t), func(v url.Values) {\n\t\t\t\t\t\tvalues(email)(v)\n\t\t\t\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessAndroidResponse))\n\t\t\t\t\t}, withUserID(userID))\n\n\t\t\t\t\tif f == \"spa\" || f == \"api\" {\n\t\t\t\t\t\tif f == \"spa\" {\n\t\t\t\t\t\t\texpectReturnTo = fix.publicTS.URL\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.False(t, gjson.Get(actual, \"session\").Exists(), \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, \"null\\n\", actual, \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t}\n\n\t\t\t\t\tif f != \"api\" {\n\t\t\t\t\t\tassert.Containsf(t, res.Request.URL.String(), expectReturnTo, \"%+v\\n\\t%s\", res.Request, assertx.PrettifyJSONPayload(t, actual))\n\t\t\t\t\t}\n\n\t\t\t\t\ti, _, err := fix.reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePasskey, userID)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, \"aal1\", i.InternalAvailableAAL.String)\n\t\t\t\t\tassert.Equalf(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"case=multi-schema registration\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\tfix.enableSessionAfterRegistration()\n\n\t\tvalues := func(email string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\tv.Del(\"method\")\n\t\t\t}\n\t\t}\n\t\tinvalidValues := func(email string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\tv.Set(\"traits.foobar\", \"b\")\n\t\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\tv.Del(\"method\")\n\t\t\t}\n\t\t}\n\n\t\tfix.conf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"does-not-exist\")\n\t\tfix.conf.MustSet(t.Context(), config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t{ID: \"does-not-exist\", URL: \"file://./stub/profile.schema.json\"},\n\t\t\t{ID: \"advanced-user\", URL: \"file://./stub/registration.schema.json\", SelfserviceSelectable: true},\n\t\t})\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tt.Run(\"should create the identity and a session and use the correct schema\", func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tuserID := f + \"-user-\" + randx.MustString(8, randx.AlphaNum)\n\t\t\t\t\tactual := fix.makeSuccessfulRegistration(t, f, fix.redirTS.URL+\"/registration-return-ts\", values(email), withUserID(userID), withInitFlowWithOption([]testhelpers.InitFlowWithOption{testhelpers.InitFlowWithIdentitySchema(\"advanced-user\")}))\n\n\t\t\t\t\tprefix := getPrefix(f)\n\n\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, prefix+\"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\tassert.True(t, gjson.Get(actual, prefix+\"active\").Bool(), \"%s\", actual)\n\n\t\t\t\t\ti, _, err := fix.reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePasskey, userID)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\t\t\t\t\tassert.Equal(t, \"advanced-user\", i.SchemaID, \"%s\", actual)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"registration should fail with invalid form data using the correct schema\", func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tuserID := f + \"-user-\" + randx.MustString(8, randx.AlphaNum)\n\t\t\t\t\tactual, res := fix.makeUnsuccessfulRegistration(t, f, fix.redirTS.URL+\"/registration-return-ts\", invalidValues(email), withUserID(userID), withInitFlowWithOption([]testhelpers.InitFlowWithOption{testhelpers.InitFlowWithIdentitySchema(\"advanced-user\")}))\n\n\t\t\t\t\tif f == \"browser\" {\n\t\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode, \"%s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode, \"%s\", actual)\n\t\t\t\t\t}\n\t\t\t\t\tassert.Equal(t, int64(4000003), gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0.id\").Int(), \"%s\", actual)\n\t\t\t\t\tassert.Equal(t, \"length must be \\u003e= 2, but got 1\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0.text\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=profile_first registration flow\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\tfix.enableSessionAfterRegistration()\n\t\tfix.conf.MustSet(t.Context(), config.ViperKeySelfServiceRegistrationFlowStyle, \"profile_first\")\n\n\t\tvalues := func(email string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\tv.Del(\"method\")\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"case=successful registration\", func(t *testing.T) {\n\t\t\tfor _, f := range []string{\"browser\", \"spa\"} {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tuserID := f + \"-profile-first-\" + randx.MustString(8, randx.AlphaNum)\n\t\t\t\t\tactual := fix.makeSuccessfulRegistration(t, f, fix.redirTS.URL+\"/registration-return-ts\", values(email), withUserID(userID))\n\n\t\t\t\t\tprefix := getPrefix(f)\n\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, prefix+\"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\tassert.True(t, gjson.Get(actual, prefix+\"active\").Bool(), \"%s\", actual)\n\n\t\t\t\t\ti, _, err := fix.reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePasskey, userID)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"case=unified registration flow\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tfix := newRegistrationFixture(t)\n\t\tfix.enableSessionAfterRegistration()\n\t\tfix.conf.MustSet(t.Context(), config.ViperKeySelfServiceRegistrationFlowStyle, \"unified\")\n\n\t\tvalues := func(email string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\t\tv.Set(node.PasskeyRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\tv.Del(\"method\")\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"case=successful registration\", func(t *testing.T) {\n\t\t\tfor _, f := range []string{\"browser\", \"spa\"} {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tuserID := f + \"-unified-\" + randx.MustString(8, randx.AlphaNum)\n\t\t\t\t\tactual := fix.makeSuccessfulRegistration(t, f, fix.redirTS.URL+\"/registration-return-ts\", values(email), withUserID(userID))\n\n\t\t\t\t\tprefix := getPrefix(f)\n\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, prefix+\"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\tassert.True(t, gjson.Get(actual, prefix+\"active\").Bool(), \"%s\", actual)\n\n\t\t\t\t\ti, _, err := fix.reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePasskey, userID)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc TestPopulateRegistrationMethod(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tctx = testhelpers.WithDefaultIdentitySchema(ctx, \"file://stub/registration.schema.json\")\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeyPasskeyRPDisplayName, \"localhost\")\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeyPasskeyRPID, \"localhost\")\n\n\ts, err := reg.AllRegistrationStrategies().Strategy(identity.CredentialsTypePasskey)\n\trequire.NoError(t, err)\n\n\tfh, ok := s.(registration.FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f node.Nodes, except ...snapshotx.Opt) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f, append(except, snapshotx.ExceptNestedKeys(\"nonce\", \"src\"))...)\n\t}\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *registration.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/registration/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := registration.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tnewFlowWithType := func(ctx context.Context, t *testing.T, flowType flow.Type) (*http.Request, *registration.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/registration/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := registration.NewFlow(conf, time.Minute, \"csrf_token\", r, flowType)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateRegistrationMethod\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tr, f := newFlowWithType(ctx, t, flow.TypeBrowser)\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethod(r, f))\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"1.attributes.value\"))\n\t\t})\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tr, f := newFlowWithType(ctx, t, flow.TypeAPI)\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethod(r, f))\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"1.attributes.value\"))\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodProfile\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"1.attributes.value\"))\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodCredentials\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tr, f := newFlowWithType(ctx, t, flow.TypeBrowser)\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"1.attributes.value\"))\n\t\t})\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tr, f := newFlowWithType(ctx, t, flow.TypeAPI)\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"1.attributes.value\"))\n\t\t})\n\t})\n\n\tt.Run(\"method=idempotency\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\n\t\tvar snapshots []node.Nodes\n\n\t\tt.Run(\"case=1\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"1.attributes.value\"))\n\t\t})\n\n\t\tt.Run(\"case=2\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"1.attributes.value\"))\n\t\t})\n\n\t\tt.Run(\"case=3\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"1.attributes.value\"))\n\t\t})\n\n\t\tt.Run(\"case=4\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"1.attributes.value\"))\n\t\t})\n\n\t\tt.Run(\"case=evaluate\", func(t *testing.T) {\n\t\t\tassertx.EqualAsJSON(t, snapshots[0], snapshots[2])\n\t\t\tassertx.EqualAsJSONExcept(t, snapshots[1], snapshots[3], []string{\"3.attributes.nonce\"})\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/passkey_schema_extension.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n)\n\ntype SchemaExtension struct {\n\tWebauthnIdentifier string\n\tPasskeyDisplayName string\n\tsync.Mutex\n}\n\nfunc (e *SchemaExtension) Run(_ jsonschema.ValidationContext, s schema.ExtensionConfig, value any) error {\n\te.Lock()\n\tdefer e.Unlock()\n\n\tif s.Credentials.WebAuthn.Identifier {\n\t\te.WebauthnIdentifier = strings.ToLower(fmt.Sprintf(\"%s\", value))\n\t}\n\n\tif s.Credentials.Passkey.DisplayName {\n\t\te.PasskeyDisplayName = fmt.Sprintf(\"%s\", value)\n\t}\n\n\treturn nil\n}\n\nfunc (e *SchemaExtension) Finish() error { return nil }\n\n// PasskeyDisplayNameFromIdentity returns the passkey display name from the\n// identity. It is usually the email address and used to name the passkey in the\n// browser.\nfunc (s *Strategy) PasskeyDisplayNameFromIdentity(ctx context.Context, id *identity.Identity) string {\n\te := new(SchemaExtension)\n\t// We can ignore teh error here because proper validation happens once the identity is persisted.\n\t_ = s.d.IdentityValidator().ValidateWithRunner(ctx, id, e)\n\n\treturn cmp.Or(e.PasskeyDisplayName, e.WebauthnIdentifier)\n}\n\nfunc (s *Strategy) PasskeyDisplayNameFromTraits(ctx context.Context, traits identity.Traits) string {\n\tid := identity.NewIdentity(\"\")\n\tid.Traits = traits\n\n\treturn s.PasskeyDisplayNameFromIdentity(ctx, id)\n}\n\nfunc (s *Strategy) PasskeyDisplayNameFromSchema(ctx context.Context, schemaURL string) (string, error) {\n\text := &passkeyDisplayNameExtension{}\n\n\trunner, err := schema.NewExtensionRunner(ctx, schema.WithCompileRunners(ext))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tc := jsonschema.NewCompiler()\n\tc.ExtractAnnotations = true\n\trunner.Register(c)\n\n\tschem, err := c.Compile(ctx, schemaURL)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfor key, value := range schem.Properties[\"traits\"].Properties {\n\t\tif value.Title == ext.getLabel() {\n\t\t\treturn \"traits.\" + key, nil\n\t\t}\n\t}\n\n\treturn \"\", errors.New(\"no identifier found\")\n}\n\ntype passkeyDisplayNameExtension struct {\n\tidentifierLabelCandidates []string\n}\n\nfunc (i *passkeyDisplayNameExtension) Run(_ jsonschema.CompilerContext, config schema.ExtensionConfig, rawSchema map[string]interface{}) error {\n\tif config.Credentials.WebAuthn.Identifier ||\n\t\tconfig.Credentials.Passkey.DisplayName {\n\t\tif title, ok := rawSchema[\"title\"]; ok {\n\t\t\t// The jsonschema compiler validates the title to be a string, so this should always work.\n\t\t\tswitch t := title.(type) {\n\t\t\tcase string:\n\t\t\t\tif t != \"\" {\n\t\t\t\t\ti.identifierLabelCandidates = append(i.identifierLabelCandidates, t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (i *passkeyDisplayNameExtension) getLabel() string {\n\tif len(i.identifierLabelCandidates) != 1 {\n\t\t// sane default is set elsewhere\n\t\treturn \"\"\n\t}\n\treturn i.identifierLabelCandidates[0]\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/passkey_settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/x/webauthnx/js\"\n\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/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/webauthnx\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/randx\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc (s *Strategy) SettingsStrategyID() string { return s.ID().String() }\n\nconst (\n\tInternalContextKeySessionData    = \"session_data\"\n\tInternalContextKeySessionOptions = \"session_options\"\n)\n\nfunc (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.passkey.Strategy.PopulateSettingsMethod\")\n\tdefer otelx.End(span, &err)\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tcount, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif webAuthns, err := s.identityListWebAuthn(id); errors.Is(err, sqlcon.ErrNoRows) {\n\t\t// Do nothing\n\t} else if err != nil {\n\t\treturn err\n\t} else {\n\t\tfor k := range webAuthns.Credentials {\n\t\t\t// We only show the option to remove a credential, if it is not the last one when passwordless,\n\t\t\t// or, if it is for MFA we show it always.\n\t\t\tcred := &webAuthns.Credentials[k]\n\n\t\t\tf.UI.Nodes.Append(webauthnx.NewPasskeyUnlink(cred, func(a *node.InputAttributes) {\n\t\t\t\t// Do not remove this node if it is the last credential the identity can sign in with.\n\t\t\t\ta.Disabled = count < 2\n\t\t\t}))\n\t\t}\n\t}\n\n\tweb, err := webauthn.New(s.d.Config().PasskeyConfig(ctx))\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tidentifier := s.PasskeyDisplayNameFromIdentity(ctx, id)\n\tif identifier == \"\" {\n\t\tf.UI.Messages.Add(text.NewErrorValidationIdentifierMissing())\n\t\treturn nil\n\t}\n\n\tuser := &webauthnx.User{\n\t\tName:   identifier,\n\t\tID:     []byte(randx.MustString(64, randx.AlphaNum)),\n\t\tConfig: s.d.Config().PasskeyConfig(ctx),\n\t}\n\toption, sessionData, err := web.BeginRegistration(user)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tf.InternalContext, err = sjson.SetBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData), sessionData)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tinjectWebAuthnOptions, err := json.Marshal(option)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif f.Type == flow.TypeBrowser {\n\t\tf.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\t}\n\n\tf.UI.Nodes.Upsert(node.NewInputField(\n\t\tnode.PasskeyRegisterTrigger,\n\t\t\"\",\n\t\tnode.PasskeyGroup,\n\t\tnode.InputAttributeTypeButton,\n\t\tnode.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\ta.OnClickTrigger = js.WebAuthnTriggersPasskeySettingsRegistration\n\t\t\t// Only attach raw JS handler for browser flows; API flows must stay script-free.\n\t\t\tif f.Type == flow.TypeBrowser {\n\t\t\t\t//nolint:staticcheck\n\t\t\t\ta.OnClick = js.WebAuthnTriggersPasskeySettingsRegistration.String() + \"()\"\n\t\t\t}\n\t\t}),\n\t).WithMetaLabel(text.NewInfoSelfServiceSettingsRegisterPasskey()))\n\n\tf.UI.Nodes.Upsert(&node.Node{\n\t\tType:  node.Input,\n\t\tGroup: node.PasskeyGroup,\n\t\tMeta:  &node.Meta{},\n\t\tAttributes: &node.InputAttributes{\n\t\t\tName: node.PasskeySettingsRegister,\n\t\t\tType: node.InputAttributeTypeHidden,\n\t\t},\n\t})\n\n\tf.UI.Nodes.Upsert(&node.Node{\n\t\tType:  node.Input,\n\t\tGroup: node.PasskeyGroup,\n\t\tMeta:  &node.Meta{},\n\t\tAttributes: &node.InputAttributes{\n\t\t\tName:       node.PasskeyCreateData,\n\t\t\tType:       node.InputAttributeTypeHidden,\n\t\t\tFieldValue: string(injectWebAuthnOptions),\n\t\t},\n\t})\n\n\treturn nil\n}\n\nfunc (s *Strategy) identityListWebAuthn(id *identity.Identity) (*identity.CredentialsWebAuthnConfig, error) {\n\tcred, ok := id.GetCredentials(s.ID())\n\tif !ok {\n\t\treturn nil, errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\n\tvar cc identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(cred.Config, &cc); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn &cc, nil\n}\n\nfunc (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.passkey.Strategy.Settings\")\n\tdefer otelx.End(span, &err)\n\n\tvar p updateSettingsFlowWithPasskeyMethod\n\tctxUpdate, err := settings.PrepareUpdate(s.d, w, r, f, ss, settings.ContinuityKey(s.SettingsStrategyID()), &p)\n\tif errors.Is(err, settings.ErrContinuePreviousAction) {\n\t\treturn ctxUpdate, s.continueSettingsFlow(ctx, w, r, ctxUpdate, p)\n\t} else if err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif err := s.decodeSettingsFlow(r, &p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif len(p.Register)+len(p.Remove) > 0 {\n\t\t// This method has only two submit buttons\n\t\tp.Method = s.SettingsStrategyID()\n\t\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\t\treturn nil, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t\t}\n\t} else {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"neither register nor remove provided\"))\n\t\treturn nil, errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\t// This does not come from the payload!\n\tp.Flow = ctxUpdate.Flow.ID.String()\n\tif err := s.continueSettingsFlow(ctx, w, r, ctxUpdate, p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\treturn ctxUpdate, nil\n}\n\n// Update Settings Flow with Passkey Method\n//\n// swagger:model updateSettingsFlowWithPasskeyMethod\ntype updateSettingsFlowWithPasskeyMethod struct {\n\t// Register a WebAuthn Security Key\n\t//\n\t// It is expected that the JSON returned by the WebAuthn registration process\n\t// is included here.\n\tRegister string `json:\"passkey_settings_register\"`\n\n\t// Remove a WebAuthn Security Key\n\t//\n\t// This must contain the ID of the WebAuthN connection.\n\tRemove string `json:\"passkey_remove\"`\n\n\t// CSRFToken is the anti-CSRF token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Method\n\t//\n\t// Should be set to \"passkey\" when trying to add, update, or remove a webAuthn pairing.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Flow is flow ID.\n\t//\n\t// swagger:ignore\n\tFlow string `json:\"flow\"`\n}\n\nfunc (p *updateSettingsFlowWithPasskeyMethod) GetFlowID() uuid.UUID {\n\treturn x.ParseUUID(p.Flow)\n}\n\nfunc (p *updateSettingsFlowWithPasskeyMethod) SetFlowID(rid uuid.UUID) {\n\tp.Flow = rid.String()\n}\n\nfunc (s *Strategy) continueSettingsFlow(\n\tctx context.Context,\n\tw http.ResponseWriter, r *http.Request,\n\tctxUpdate *settings.UpdateContext, p updateSettingsFlowWithPasskeyMethod,\n) error {\n\tif len(p.Register+p.Remove) > 0 {\n\t\tif err := flow.MethodEnabledAndAllowed(ctx, flow.SettingsFlow, s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := flow.EnsureCSRF(s.d, r, ctxUpdate.Flow.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) {\n\t\t\treturn errors.WithStack(settings.NewFlowNeedsReAuth())\n\t\t}\n\t} else {\n\t\treturn errors.New(\"ended up in unexpected state\")\n\t}\n\n\tswitch {\n\tcase len(p.Remove) > 0:\n\t\treturn s.continueSettingsFlowRemove(ctx, w, r, ctxUpdate, p)\n\tcase len(p.Register) > 0:\n\t\treturn s.continueSettingsFlowAdd(ctx, ctxUpdate, p)\n\tdefault:\n\t\treturn errors.New(\"ended up in unexpected state\")\n\t}\n}\n\nfunc (s *Strategy) continueSettingsFlowRemove(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithPasskeyMethod) error {\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.IdentityID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcred, ok := i.GetCredentials(s.ID())\n\tif !ok {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"You tried to remove a WebAuthn but you have no WebAuthn set up.\"))\n\t}\n\n\tvar cc identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(cred.Config, &cc); err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\tupdated := make([]identity.CredentialWebAuthn, 0)\n\tfor k, cred := range cc.Credentials {\n\t\tif fmt.Sprintf(\"%x\", cred.ID) != p.Remove {\n\t\t\tupdated = append(updated, cc.Credentials[k])\n\t\t}\n\t}\n\n\tif len(updated) == len(cc.Credentials) {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"You tried to remove a passkey which does not exist.\"))\n\t}\n\n\tcount, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, i)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif count < 2 {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(webauthnx.ErrNotEnoughCredentials))\n\t}\n\n\tif len(updated) == 0 {\n\t\ti.DeleteCredentialsType(identity.CredentialsTypePasskey)\n\t\tctxUpdate.UpdateIdentity(i)\n\t\treturn nil\n\t}\n\n\tcc.Credentials = updated\n\tcred.Config, err = json.Marshal(cc)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\ti.SetCredentials(s.ID(), *cred)\n\tctxUpdate.UpdateIdentity(i)\n\treturn nil\n}\n\nfunc (s *Strategy) continueSettingsFlowAdd(ctx context.Context, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithPasskeyMethod) error {\n\twebAuthnSession := gjson.GetBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData))\n\tif !webAuthnSession.IsObject() {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN in internal context to be an object.\"))\n\t}\n\n\tvar webAuthnSess webauthn.SessionData\n\tif err := json.Unmarshal([]byte(gjson.GetBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData)).Raw), &webAuthnSess); err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN in internal context to be an object but got: %s\", err))\n\t}\n\n\twebAuthnResponse, err := protocol.ParseCredentialCreationResponseBody(strings.NewReader(p.Register))\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to parse WebAuthn response: %s\", err))\n\t}\n\n\tweb, err := webauthn.New(s.d.Config().PasskeyConfig(ctx))\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to get webAuthn config.\").WithDebug(err.Error()))\n\t}\n\n\tcredential, err := web.CreateCredential(&webauthnx.User{\n\t\tID:     webAuthnSess.UserID,\n\t\tConfig: web.Config,\n\t}, webAuthnSess, webAuthnResponse)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to create WebAuthn credential: %s\", err))\n\t}\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.IdentityID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcred := i.GetCredentialsOr(s.ID(), &identity.Credentials{Config: sqlxx.JSONRawMessage(\"{}\")})\n\n\tvar cc identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(cred.Config, &cc); err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\tcredentialWebAuthn := identity.CredentialFromWebAuthn(credential, true)\n\tcc.UserHandle = webAuthnSess.UserID\n\tcc.Credentials = append(cc.Credentials, *credentialWebAuthn)\n\tcredentialsConfig, err := json.Marshal(cc)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\ti.UpsertCredentialsConfig(s.ID(), credentialsConfig, 1, identity.WithAdditionalIdentifier(string(webAuthnSess.UserID)))\n\tif err := s.validateCredentials(ctx, i); err != nil {\n\t\treturn err\n\t}\n\n\t// Remove the WebAuthn URL from the internal context now that it is set!\n\tctxUpdate.Flow.InternalContext, err = sjson.DeleteBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, ctxUpdate.Flow); err != nil {\n\t\treturn err\n\t}\n\n\taal := identity.AuthenticatorAssuranceLevel1\n\n\t// Since we added the method, it also means that we have authenticated it\n\tif err := s.d.SessionManager().SessionAddAuthenticationMethods(ctx, ctxUpdate.Session.ID, session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    aal,\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\tctxUpdate.UpdateIdentity(i)\n\treturn nil\n}\n\nfunc (s *Strategy) decodeSettingsFlow(r *http.Request, dest interface{}) error {\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(settingsSchema)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn decoderx.Decode(r, dest, compiler,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t)\n}\n\nfunc (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithPasskeyMethod, err error) error {\n\t// Do not pause flow if the flow type is an API flow as we can't save cookies in those flows.\n\tif e := new(settings.FlowNeedsReAuth); errors.As(err, &e) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser {\n\t\tif err := s.d.ContinuityManager().Pause(ctx, w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif ctxUpdate.Flow != nil {\n\t\tctxUpdate.Flow.UI.ResetMessages()\n\t\tctxUpdate.Flow.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/passkey_settings_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/strategy/passkey\"\n\n\t\"github.com/ory/x/snapshotx\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/x\"\n)\n\n//go:embed fixtures/settings/success/identity.json\nvar settingsFixtureSuccessIdentity []byte\n\n//go:embed fixtures/settings/success/response.json\nvar settingsFixtureSuccessResponse []byte\n\n//go:embed fixtures/settings/success/internal_context.json\nvar settingsFixtureSuccessInternalContext []byte\n\nfunc TestCompleteSettings(t *testing.T) {\n\tfix := newSettingsFixture(t)\n\n\tt.Run(\"case=invalid passkey config\", func(t *testing.T) {\n\t\tfix := newSettingsFixture(t, configx.WithValue(config.ViperKeyPasskeyRPID, \"\"))\n\t\tid := fix.createIdentity(t)\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, fix.reg, id)\n\n\t\treq, err := http.NewRequest(\"GET\", fix.publicTS.URL+settings.RouteInitBrowserFlow, nil)\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\tres, err := apiClient.Do(req)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusInternalServerError, res.StatusCode)\n\t})\n\n\tt.Run(\"case=a device is shown which can be unlinked\", func(t *testing.T) {\n\t\tid := fix.createIdentity(t)\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, fix.reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, apiClient, true, fix.publicTS)\n\n\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\"0.attributes.value\", // CSRF\n\t\t\t\"1.attributes.nonce\", // script\n\t\t\t\"1.attributes.src\",   // script\n\t\t\t\"6.attributes.value\", // passkey_settings_register\n\t\t})\n\t})\n\n\tt.Run(\"case=one activation element is shown\", func(t *testing.T) {\n\t\tid := fix.createIdentityWithoutPasskey(t)\n\t\trequire.NoError(t, fix.reg.PrivilegedIdentityPool().UpdateIdentity(t.Context(), id))\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, fix.reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, apiClient, true, fix.publicTS)\n\n\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\"0.attributes.value\", // CSRF\n\t\t\t\"1.attributes.nonce\", // script\n\t\t\t\"1.attributes.src\",   // script\n\t\t\t\"4.attributes.value\", // passkey_create_data\n\t\t})\n\t})\n\n\tt.Run(\"case=passkey nodes exist for API but without browser script\", func(t *testing.T) {\n\t\tid := fix.createIdentityWithoutPasskey(t)\n\t\trequire.NoError(t, fix.reg.PrivilegedIdentityPool().UpdateIdentity(t.Context(), id))\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, fix.reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, fix.publicTS)\n\n\t\t// Passkey nodes should exist for API\n\t\thasPasskeyGroup := false\n\t\thasWebAuthnScript := false\n\t\tfor _, n := range f.Ui.Nodes {\n\t\t\tif n.Group == \"passkey\" {\n\t\t\t\thasPasskeyGroup = true\n\t\t\t}\n\t\t\tif n.Type == \"script\" {\n\t\t\t\thasWebAuthnScript = true\n\t\t\t}\n\t\t}\n\t\tassert.True(t, hasPasskeyGroup, \"passkey group should be present for API flows\")\n\t\tassert.False(t, hasWebAuthnScript, \"WebAuthn script should not be present for API flows\")\n\t})\n\n\tdoAPIFlow := func(t *testing.T, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, fix.reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, fix.publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tv(values)\n\t\tpayload := testhelpers.EncodeFormAsJSON(t, true, values)\n\t\treturn testhelpers.SettingsMakeRequest(t, true, false, f, apiClient, payload)\n\t}\n\n\tdoBrowserFlow := func(t *testing.T, spa bool, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, fix.reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, spa, fix.publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tv(values)\n\t\treturn testhelpers.SettingsMakeRequest(t, false, spa, f, browserClient, testhelpers.EncodeFormAsJSON(t, spa, values))\n\t}\n\n\tt.Run(\"case=fails with invalid passkey payload for API\", func(t *testing.T) {\n\t\tid := fix.createIdentityWithoutPasskey(t)\n\t\tbody, res := doAPIFlow(t, func(v url.Values) {\n\t\t\tv.Set(node.PasskeySettingsRegister, \"{}\")\n\t\t\tv.Set(\"method\", \"passkey\")\n\t\t}, id)\n\n\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t// API should process the request but fail on invalid WebAuthn payload\n\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"Parse error for Registration\", \"%s\", body)\n\t})\n\n\tt.Run(\"case=fails with browser submit because csrf token is missing\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := fix.createIdentityWithoutPasskey(t)\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(node.PasskeySettingsRegister, \"{}\")\n\t\t\t\tv.Set(\"method\", \"passkey\")\n\t\t\t}, id)\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"error.reason\").String(), body)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.errTS.URL)\n\t\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"reason\").String(), body)\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t// API should work without CSRF token\n\t\t\tid := fix.createIdentityWithoutPasskey(t)\n\t\t\tbody, res := doAPIFlow(t, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(node.PasskeySettingsRegister, \"{}\")\n\t\t\t\tv.Set(\"method\", \"passkey\")\n\t\t\t}, id)\n\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t// API should process and fail on invalid payload, not CSRF\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"Parse error for Registration\", \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=fails when register payload is invalid\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := fix.createIdentityWithoutPasskey(t)\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Set(node.PasskeySettingsRegister, \"{}\")\n\t\t\t\tv.Set(\"method\", \"passkey\")\n\t\t\t}, id)\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.uiTS.URL)\n\t\t\t}\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"Parse error for Registration\", \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tid := fix.createIdentityWithoutPasskey(t)\n\t\t\tbody, res := doAPIFlow(t, func(v url.Values) {\n\t\t\t\tv.Set(node.PasskeySettingsRegister, \"{}\")\n\t\t\t\tv.Set(\"method\", \"passkey\")\n\t\t\t}, id)\n\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"Parse error for Registration\", \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=requires privileged session for register\", func(t *testing.T) {\n\t\tfix.conf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"1ns\")\n\t\tt.Cleanup(func() {\n\t\t\tfix.conf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"5m\")\n\t\t})\n\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := fix.createIdentityWithoutPasskey(t)\n\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Set(node.PasskeySettingsRegister, \"{}\")\n\t\t\t\tv.Set(\"method\", \"passkey\")\n\t\t\t}, id)\n\n\t\t\tif spa {\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"redirect_browser_to\").String())\n\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(body), []string{\"redirect_browser_to\"})\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.loginTS.URL+\"/login-ts\")\n\t\t\t\tassertx.EqualAsJSON(t, text.NewInfoLoginReAuth(), json.RawMessage(gjson.Get(body, \"ui.messages.0\").Raw))\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tid := fix.createIdentityWithoutPasskey(t)\n\t\t\tbody, res := doAPIFlow(t, func(v url.Values) {\n\t\t\t\tv.Set(node.PasskeySettingsRegister, \"{}\")\n\t\t\t\tv.Set(\"method\", \"passkey\")\n\t\t\t}, id)\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t// API flow should fail with 403 Forbidden due to insufficient privileges\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, settings.NewFlowNeedsReAuth().DefaultError, json.RawMessage(gjson.Get(body, \"error\").Raw))\n\t\t})\n\t})\n\n\tt.Run(\"case=add a passkey\", func(t *testing.T) {\n\t\trun := func(t *testing.T, flowType string) {\n\t\t\t// We load our identity which we will use to replay the webauth session\n\t\t\tvar id identity.Identity\n\t\t\trequire.NoError(t, json.Unmarshal(settingsFixtureSuccessIdentity, &id))\n\t\t\tid.NID = x.NewUUID()\n\t\t\t_ = fix.reg.PrivilegedIdentityPool().DeleteIdentity(t.Context(), id.ID)\n\n\t\t\tvar f *kratos.SettingsFlow\n\t\t\tvar client *http.Client\n\t\t\tvar body string\n\t\t\tvar res *http.Response\n\n\t\t\tif flowType == \"api\" {\n\t\t\t\tclient = testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, fix.reg, &id)\n\t\t\t\tf = testhelpers.InitializeSettingsFlowViaAPI(t, client, fix.publicTS)\n\t\t\t} else {\n\t\t\t\tclient = testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, fix.reg, &id)\n\t\t\t\tf = testhelpers.InitializeSettingsFlowViaBrowser(t, client, flowType == \"spa\", fix.publicTS)\n\t\t\t}\n\n\t\t\t// We inject the session to replay\n\t\t\tinterim, err := fix.reg.SettingsFlowPersister().GetSettingsFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\t\trequire.NoError(t, err)\n\t\t\tinterim.InternalContext = settingsFixtureSuccessInternalContext\n\t\t\trequire.NoError(t, fix.reg.SettingsFlowPersister().UpdateSettingsFlow(t.Context(), interim))\n\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\n\t\t\t// We use the response replay\n\t\t\tvalues.Set(\"method\", \"passkey\")\n\t\t\tvalues.Set(node.PasskeySettingsRegister, string(settingsFixtureSuccessResponse))\n\n\t\t\tif flowType == \"api\" {\n\t\t\t\tbody, res = testhelpers.SettingsMakeRequest(t, true, false, f, client, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\t} else {\n\t\t\t\tbody, res = testhelpers.SettingsMakeRequest(t, false, flowType == \"spa\", f, client, testhelpers.EncodeFormAsJSON(t, flowType == \"spa\", values))\n\t\t\t}\n\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\n\t\t\tswitch flowType {\n\t\t\tcase \"spa\":\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tcase \"browser\":\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.uiTS.URL)\n\t\t\t}\n\t\t\t// For API, no redirect check needed\n\n\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(body, \"state\").String(), body)\n\n\t\t\tactual, err := fix.reg.Persister().GetIdentityConfidential(t.Context(), id.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tcred, ok := actual.GetCredentials(identity.CredentialsTypePasskey)\n\t\t\trequire.True(t, ok)\n\t\t\tassert.Len(t, gjson.GetBytes(cred.Config, \"credentials\").Array(), 1)\n\n\t\t\tactualFlow, err := fix.reg.SettingsFlowPersister().GetSettingsFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\t\trequire.NoError(t, err)\n\t\t\t// new session data has been generated\n\t\t\tassert.NotEqual(t, settingsFixtureSuccessInternalContext,\n\t\t\t\tgjson.GetBytes(actualFlow.InternalContext,\n\t\t\t\t\tflow.PrefixInternalContextKey(identity.CredentialsTypePasskey, passkey.InternalContextKeySessionData)))\n\n\t\t\ttesthelpers.EnsureAAL(t, client, fix.publicTS, \"aal1\", string(identity.CredentialsTypePasskey))\n\n\t\t\tif flowType == \"spa\" {\n\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(body, \"continue_with.0.action\").String(), \"%s\", body)\n\t\t\t\tassert.Contains(t, gjson.Get(body, \"continue_with.0.redirect_browser_to\").String(), fix.uiTS.URL, \"%s\", body)\n\t\t\t} else {\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, \"browser\")\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, \"spa\")\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\trun(t, \"api\")\n\t\t})\n\t})\n\n\tt.Run(\"case=fails to remove passkey if it is the last credential available\", func(t *testing.T) {\n\t\trun := func(t *testing.T, flowType string) {\n\t\t\tid := fix.createIdentity(t)\n\t\t\tid.DeleteCredentialsType(identity.CredentialsTypePassword)\n\t\t\tconf := sqlxx.JSONRawMessage(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\",\"is_passwordless\":true}]}`)\n\t\t\tid.UpsertCredentialsConfig(identity.CredentialsTypePasskey, conf, 0)\n\t\t\trequire.NoError(t, fix.reg.IdentityManager().Update(t.Context(), id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\tvar body string\n\t\t\tvar res *http.Response\n\t\t\tif flowType == \"api\" {\n\t\t\t\tbody, res = doAPIFlow(t, func(v url.Values) {\n\t\t\t\t\t// The remove key should be empty\n\t\t\t\t\tsnapshotx.SnapshotT(t, v, snapshotx.ExceptPaths(\"csrf_token\", \"passkey_create_data\"))\n\t\t\t\t\tv.Set(node.PasskeyRemove, \"666f6f666f6f\")\n\t\t\t\t}, id)\n\t\t\t} else {\n\t\t\t\tbody, res = doBrowserFlow(t, flowType == \"spa\", func(v url.Values) {\n\t\t\t\t\t// The remove key should be empty\n\t\t\t\t\tsnapshotx.SnapshotT(t, v, snapshotx.ExceptPaths(\"csrf_token\", \"passkey_create_data\"))\n\t\t\t\t\tv.Set(node.PasskeyRemove, \"666f6f666f6f\")\n\t\t\t\t}, id)\n\t\t\t}\n\n\t\t\tswitch flowType {\n\t\t\tcase \"spa\":\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tcase \"browser\":\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.uiTS.URL)\n\t\t\t}\n\t\t\t// For API, no redirect check needed\n\n\t\t\tt.Run(\"response\", func(t *testing.T) {\n\t\t\t\tassert.EqualValues(t, flow.StateShowForm, gjson.Get(body, \"state\").String(), body)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.Get(body, \"ui.nodes.#(attributes.name==passkey_remove)\").String()))\n\n\t\t\t\tactual, err := fix.reg.Persister().GetIdentityConfidential(t.Context(), id.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tcred, ok := actual.GetCredentials(identity.CredentialsTypePasskey)\n\t\t\t\tassert.True(t, ok)\n\t\t\t\tassert.Len(t, gjson.GetBytes(cred.Config, \"credentials\").Array(), 1)\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, \"browser\")\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, \"spa\")\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\trun(t, \"api\")\n\t\t})\n\t})\n\n\tt.Run(\"case=remove all passkeys\", func(t *testing.T) {\n\t\trun := func(t *testing.T, flowType string) {\n\t\t\tid := fix.createIdentity(t)\n\t\t\tallCred, ok := id.GetCredentials(identity.CredentialsTypePasskey)\n\t\t\tassert.True(t, ok)\n\n\t\t\tvar cc identity.CredentialsWebAuthnConfig\n\t\t\trequire.NoError(t, json.Unmarshal(allCred.Config, &cc))\n\t\t\trequire.Len(t, cc.Credentials, 2)\n\n\t\t\tfor _, cred := range cc.Credentials {\n\t\t\t\tvar body string\n\t\t\t\tvar res *http.Response\n\t\t\t\tif flowType == \"api\" {\n\t\t\t\t\tbody, res = doAPIFlow(t, func(v url.Values) {\n\t\t\t\t\t\tv.Set(node.PasskeyRemove, fmt.Sprintf(\"%x\", cred.ID))\n\t\t\t\t\t}, id)\n\t\t\t\t} else {\n\t\t\t\t\tbody, res = doBrowserFlow(t, flowType == \"spa\", func(v url.Values) {\n\t\t\t\t\t\tv.Set(node.PasskeyRemove, fmt.Sprintf(\"%x\", cred.ID))\n\t\t\t\t\t}, id)\n\t\t\t\t}\n\n\t\t\t\tswitch flowType {\n\t\t\t\tcase \"spa\":\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\tcase \"browser\":\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.uiTS.URL)\n\t\t\t\t}\n\t\t\t\t// For API, no redirect check needed\n\t\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(body, \"state\").String(), body)\n\t\t\t}\n\n\t\t\tactual, err := fix.reg.Persister().GetIdentityConfidential(t.Context(), id.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\t_, ok = actual.GetCredentials(identity.CredentialsTypePasskey)\n\t\t\tassert.False(t, ok)\n\t\t\t// Check not to remove other credentials with webauthn\n\t\t\t_, ok = actual.GetCredentials(identity.CredentialsTypePassword)\n\t\t\tassert.True(t, ok)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, \"browser\")\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, \"spa\")\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\trun(t, \"api\")\n\t\t})\n\t})\n\n\tt.Run(\"case=fails with browser submit register payload is invalid\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := fix.createIdentity(t)\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Set(node.PasskeyRemove, fmt.Sprintf(\"%x\", []byte(\"foofoo\")))\n\t\t\t}, id)\n\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.uiTS.URL)\n\t\t\t}\n\t\t\tassert.EqualValues(t, flow.StateSuccess, json.RawMessage(gjson.Get(body, \"state\").String()))\n\n\t\t\tactual, err := fix.reg.Persister().GetIdentityConfidential(t.Context(), id.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tcred, ok := actual.GetCredentials(identity.CredentialsTypePasskey)\n\t\t\tassert.True(t, ok)\n\t\t\tassert.Len(t, gjson.GetBytes(cred.Config, \"credentials\").Array(), 1)\n\t\t\tassert.Equal(t, \"bar\", gjson.GetBytes(cred.Config, \"credentials.0.display_name\").String())\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=is not responsible if neither remove or register is set\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := fix.createIdentity(t)\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Set(node.PasskeyRemove, \"\")\n\t\t\t\tv.Set(node.PasskeyRegister, \"\")\n\t\t\t}, id)\n\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), fix.uiTS.URL)\n\t\t\t}\n\n\t\t\tassert.Equal(t, text.NewErrorValidationSettingsNoStrategyFound().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail if no identifier was set in the schema\", func(t *testing.T) {\n\t\ttesthelpers.SetDefaultIdentitySchema(fix.conf, \"file://stub/missing-identifier.schema.json\")\n\n\t\tfor _, f := range []string{\"spa\", \"browser\"} {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tisSPA := f == \"spa\"\n\n\t\t\t\tvar id identity.Identity\n\t\t\t\trequire.NoError(t, json.Unmarshal(settingsFixtureSuccessIdentity, &id))\n\t\t\t\t_ = fix.reg.PrivilegedIdentityPool().DeleteIdentity(t.Context(), id.ID)\n\t\t\t\tid.NID = x.NewUUID()\n\t\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, fix.reg, &id)\n\n\t\t\t\treq, err := http.NewRequest(\"GET\", fix.publicTS.URL+settings.RouteInitBrowserFlow, nil)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tif isSPA {\n\t\t\t\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\t\t\t}\n\t\t\t\tres, err := browserClient.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tactual := x.MustReadAll(res.Body)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tassert.Equal(t, text.NewErrorValidationIdentifierMissing().Text, gjson.GetBytes(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestPopulateSettingsMethod(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tctx = testhelpers.WithDefaultIdentitySchema(ctx, \"file://stub/settings.schema.json\")\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeyPasskeyRPDisplayName, \"localhost\")\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeyPasskeyRPID, \"localhost\")\n\n\ts, err := reg.AllSettingsStrategies().Strategy(string(identity.CredentialsTypePasskey))\n\trequire.NoError(t, err)\n\n\ttoSnapshot := func(t *testing.T, f node.Nodes, except ...snapshotx.Opt) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f, append(except, snapshotx.ExceptNestedKeys(\"nonce\", \"src\"))...)\n\t}\n\n\tnewFlowWithType := func(ctx context.Context, t *testing.T, flowType flow.Type) (*http.Request, *settings.Flow, *identity.Identity) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/settings/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tid := identity.NewIdentity(\"default\")\n\t\tid.Traits = identity.Traits(`{\"email\":\"testuser@ory.sh\"}`)\n\t\tf, err := settings.NewFlow(conf, time.Minute, r, id, flowType)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f, id\n\t}\n\n\tt.Run(\"method=PopulateSettingsMethod\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tr, f, id := newFlowWithType(ctx, t, flow.TypeBrowser)\n\t\t\trequire.NoError(t, s.PopulateSettingsMethod(ctx, r, id, f))\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"4.attributes.value\"))\n\t\t})\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tr, f, id := newFlowWithType(ctx, t, flow.TypeAPI)\n\t\t\trequire.NoError(t, s.PopulateSettingsMethod(ctx, r, id, f))\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"3.attributes.value\"))\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/passkey_strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype dependencies interface {\n\tlogrusx.Provider\n\thttpx.WriterProvider\n\tnosurfx.CSRFTokenGeneratorProvider\n\tnosurfx.CSRFProvider\n\totelx.Provider\n\n\tconfig.Provider\n\n\tcontinuity.ManagementProvider\n\n\terrorx.ManagementProvider\n\thash.HashProvider\n\n\tregistration.HandlerProvider\n\tregistration.HooksProvider\n\tregistration.ErrorHandlerProvider\n\tregistration.HookExecutorProvider\n\tregistration.FlowPersistenceProvider\n\n\tlogin.HooksProvider\n\tlogin.ErrorHandlerProvider\n\tlogin.HookExecutorProvider\n\tlogin.FlowPersistenceProvider\n\tlogin.HandlerProvider\n\n\tsettings.FlowPersistenceProvider\n\tsettings.HookExecutorProvider\n\tsettings.HooksProvider\n\tsettings.ErrorHandlerProvider\n\n\tidentity.PrivilegedPoolProvider\n\tidentity.ValidationProvider\n\tidentity.ActiveCredentialsCounterStrategyProvider\n\tidentity.ManagementProvider\n\n\tsession.HandlerProvider\n\tsession.ManagementProvider\n}\n\nvar (\n\t_ login.Strategy                    = (*Strategy)(nil)\n\t_ registration.Strategy             = (*Strategy)(nil)\n\t_ identity.ActiveCredentialsCounter = (*Strategy)(nil)\n)\n\ntype Strategy struct{ d dependencies }\n\nfunc NewStrategy(d dependencies) *Strategy { return &Strategy{d: d} }\n\nfunc (*Strategy) ID() identity.CredentialsType {\n\treturn identity.CredentialsTypePasskey\n}\n\nfunc (*Strategy) NodeGroup() node.UiNodeGroup {\n\treturn node.PasskeyGroup\n}\n\nfunc (s *Strategy) CompletedAuthenticationMethod(context.Context) session.AuthenticationMethod {\n\treturn session.AuthenticationMethod{\n\t\tMethod: identity.CredentialsTypePasskey,\n\t\tAAL:    identity.AuthenticatorAssuranceLevel1,\n\t}\n}\n\nfunc (s *Strategy) CountActiveMultiFactorCredentials(_ context.Context, _ map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\treturn 0, nil\n}\n\nfunc (s *Strategy) CountActiveFirstFactorCredentials(_ context.Context, cc map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\treturn s.countCredentials(cc)\n}\n\nfunc (s *Strategy) countCredentials(cc map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\tfor _, c := range cc {\n\t\tif c.Type == s.ID() && len(c.Config) > 0 && len(strings.Join(c.Identifiers, \"\")) > 0 {\n\t\t\tvar conf identity.CredentialsWebAuthnConfig\n\t\t\tif err = json.Unmarshal(c.Config, &conf); err != nil {\n\t\t\t\treturn 0, errors.WithStack(err)\n\t\t\t}\n\t\t\tcount += len(conf.Credentials)\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/schema.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey\n\nimport _ \"embed\"\n\n//go:embed .schema/registration.schema.json\nvar registrationSchema []byte\n\n//go:embed .schema/login.schema.json\nvar loginSchema []byte\n\n//go:embed .schema/settings.schema.json\nvar settingsSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/passkey/stub/login.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"website\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"passkey\": {\n                \"display_name\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": []\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/stub/login_webauthn.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"website\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": []\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/stub/missing-identifier.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\"\n        }\n      },\n      \"required\": [\n        \"email\"\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/stub/noid.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\"\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/stub/profile.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"passkey\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/stub/registration.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"foobar\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        },\n        \"username\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"passkey\": {\n                \"display_name\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\"foobar\", \"username\"]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/stub/settings.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"website\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"passkey\": {\n                \"display_name\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": []\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/passkey/testfixture_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage passkey_test\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\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\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc newRegistrationRegistry(t *testing.T, cfgOpts ...configx.OptionModifier) *driver.RegistryDefault {\n\t_, reg := pkg.NewFastRegistryWithMocks(t, append([]configx.OptionModifier{\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypePassword) + \".enabled\": true,\n\t\t\tconfig.ViperKeySelfServiceRegistrationEnableLegacyOneStep:                                              true,\n\t\t\tconfig.ViperKeySelfServiceRegistrationLoginHints:                                                       true,\n\t\t}),\n\t\twithEnabledPasskeyStrategy,\n\t}, cfgOpts...)...)\n\treturn reg\n}\n\nfunc newLoginRegistry(t *testing.T, cfgOpts ...configx.OptionModifier) *driver.RegistryDefault {\n\t_, reg := pkg.NewFastRegistryWithMocks(t, append([]configx.OptionModifier{\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypePassword) + \".enabled\": true,\n\t\t\tconfig.ViperKeySelfServiceRegistrationLoginHints:                                                       true,\n\t\t}),\n\t\twithEnabledPasskeyStrategy,\n\t}, cfgOpts...)...)\n\treturn reg\n}\n\nvar withEnabledPasskeyStrategy = func() configx.OptionModifier {\n\tkey := config.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypePasskey)\n\treturn configx.WithValues(map[string]any{\n\t\tkey + \".enabled\":                true,\n\t\tkey + \".config.rp.display_name\": \"Ory Corp\",\n\t\tkey + \".config.rp.id\":           \"localhost\",\n\t\tkey + \".config.rp.origins\":      []string{\"http://localhost:4455\"},\n\t})\n}()\n\ntype fixture struct {\n\tconf *config.Config\n\treg  *driver.RegistryDefault\n\n\tpublicTS         *httptest.Server\n\tredirTS          *httptest.Server\n\tredirNoSessionTS *httptest.Server\n\tuiTS             *httptest.Server\n\terrTS            *httptest.Server\n\tloginTS          *httptest.Server\n}\n\nfunc newRegistrationFixture(t *testing.T, cfgOpts ...configx.OptionModifier) *fixture {\n\tfix := new(fixture)\n\tfix.reg = newRegistrationRegistry(t, append([]configx.OptionModifier{\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/registration.schema.json\")),\n\t\tconfigx.WithValue(config.ViperKeySecretsDefault, []string{\"not-a-secure-session-key\"}),\n\t}, cfgOpts...)...)\n\tfix.conf = fix.reg.Config()\n\n\tfix.publicTS, _ = testhelpers.NewKratosServer(t, fix.reg)\n\n\t_ = testhelpers.NewErrorTestServer(t, fix.reg)\n\t_ = testhelpers.NewRegistrationUIFlowEchoServer(t, fix.reg)\n\t_ = testhelpers.NewRedirSessionEchoTS(t, fix.reg)\n\n\tfix.redirTS = testhelpers.NewRedirSessionEchoTS(t, fix.reg)\n\tfix.redirNoSessionTS = testhelpers.NewRedirNoSessionTS(t, fix.reg)\n\n\tfix.useReturnToFromTS(fix.redirTS)\n\n\treturn fix\n}\n\nfunc newLoginFixture(t *testing.T, cfgOpts ...configx.OptionModifier) *fixture {\n\tfix := new(fixture)\n\tfix.reg = newLoginRegistry(t, append([]configx.OptionModifier{\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypePassword) + \".enabled\": false,\n\t\t\tconfig.ViperKeySecretsDefault: []string{\"not-a-secure-session-key\"},\n\t\t}),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/login.schema.json\")),\n\t}, cfgOpts...)...)\n\tfix.conf = fix.reg.Config()\n\n\tfix.publicTS, _ = testhelpers.NewKratosServer(t, fix.reg)\n\n\tfix.errTS = testhelpers.NewErrorTestServer(t, fix.reg)\n\tfix.uiTS = testhelpers.NewLoginUIFlowEchoServer(t, fix.reg)\n\tfix.loginTS = fix.uiTS\n\n\t// Overwrite these two to make it more explicit when tests fail\n\tfix.conf.MustSet(t.Context(), config.ViperKeySelfServiceLoginUI, fix.uiTS.URL+\"/login-ts\")\n\n\tfix.redirTS = testhelpers.NewRedirSessionEchoTS(t, fix.reg)\n\tfix.redirNoSessionTS = testhelpers.NewRedirNoSessionTS(t, fix.reg)\n\n\tfix.useReturnToFromTS(fix.redirTS)\n\n\treturn fix\n}\n\nfunc newSettingsFixture(t *testing.T, cfgOpts ...configx.OptionModifier) *fixture {\n\tfix := newLoginFixture(t, append([]configx.OptionModifier{\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter: \"1m\",\n\t\t\tconfig.ViperKeySelfServiceSettingsRequiredAAL:                   \"aal1\",\n\t\t\tconfig.ViperKeySessionWhoAmIAAL:                                 \"aal1\",\n\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".profile.enabled\":   false,\n\t\t}),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/settings.schema.json\")),\n\t}, cfgOpts...)...)\n\tfix.uiTS = testhelpers.NewSettingsUIFlowEchoServer(t, fix.reg)\n\n\treturn fix\n}\n\nfunc (fix *fixture) checkURL(t *testing.T, shouldRedirect bool, res *http.Response) {\n\tif shouldRedirect {\n\t\tassert.Contains(t, res.Request.URL.String(), fix.uiTS.URL+\"/login-ts\")\n\t} else {\n\t\tassert.Contains(t, res.Request.URL.String(), fix.publicTS.URL+login.RouteSubmitFlow)\n\t}\n}\n\nfunc (fix *fixture) loginViaBrowser(t *testing.T, spa bool, cb func(url.Values), browserClient *http.Client, opts ...testhelpers.InitFlowWithOption) (string, *http.Response) {\n\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, fix.publicTS, false, spa, false, false, opts...)\n\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\tcb(values)\n\treturn testhelpers.LoginMakeRequest(t, false, spa, f, browserClient, values.Encode())\n}\n\nfunc (fix *fixture) createIdentityWithPasskey(t *testing.T, c identity.Credentials) *identity.Identity {\n\tvar id identity.Identity\n\trequire.NoError(t, json.Unmarshal(loginSuccessIdentity, &id))\n\n\tid.SetCredentials(identity.CredentialsTypePasskey, identity.Credentials{\n\t\tIdentifiers: []string{\"some-random-user-handle\"},\n\t\tConfig:      c.Config,\n\t\tType:        identity.CredentialsTypePasskey,\n\t\tVersion:     c.Version,\n\t})\n\n\t// We clean up the identity in case it has been created before\n\t_ = fix.reg.PrivilegedIdentityPool().DeleteIdentity(t.Context(), id.ID)\n\n\trequire.NoError(t, fix.reg.PrivilegedIdentityPool().CreateIdentity(t.Context(), &id))\n\n\treturn &id\n}\n\nfunc (fix *fixture) submitWebAuthnLoginFlowWithClient(t *testing.T, isSPA bool, f *kratos.LoginFlow, contextFixture []byte, client *http.Client, cb func(values url.Values)) (string, *http.Response, *kratos.LoginFlow) {\n\t// We inject the session to replay\n\tinterim, err := fix.reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\trequire.NoError(t, err)\n\tinterim.InternalContext = contextFixture\n\trequire.NoError(t, fix.reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), interim))\n\n\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\tcb(values)\n\n\t// We use the response replay\n\tbody, res := testhelpers.LoginMakeRequest(t, false, isSPA, f, client, values.Encode())\n\treturn body, res, f\n}\n\nfunc (fix *fixture) submitWebAuthnLoginWithClient(t *testing.T, isSPA bool, contextFixture []byte, client *http.Client, cb func(values url.Values), opts ...testhelpers.InitFlowWithOption) (string, *http.Response, *kratos.LoginFlow) {\n\tf := testhelpers.InitializeLoginFlowViaBrowser(t, client, fix.publicTS, false, isSPA, false, false, opts...)\n\treturn fix.submitWebAuthnLoginFlowWithClient(t, isSPA, f, contextFixture, client, cb)\n}\n\nfunc (fix *fixture) submitWebAuthnLogin(t *testing.T, ctx context.Context, isSPA bool, id *identity.Identity, contextFixture []byte, cb func(values url.Values), opts ...testhelpers.InitFlowWithOption) (string, *http.Response, *kratos.LoginFlow) {\n\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, fix.reg, id)\n\treturn fix.submitWebAuthnLoginWithClient(t, isSPA, contextFixture, browserClient, cb, opts...)\n}\n\n// useReturnToFromTS sets the \"return to\" server, which will assert the session\n// state (redirTS: enforce that a session exists, redirNoSessionTS: enforce that\n// no session exists)\nfunc (fix *fixture) useReturnToFromTS(ts *httptest.Server) {\n\tfix.conf.MustSet(context.TODO(), config.ViperKeySelfServiceBrowserDefaultReturnTo, ts.URL+\"/default-return-to\")\n\tfix.conf.MustSet(context.TODO(), config.ViperKeySelfServiceRegistrationAfter+\".\"+config.DefaultBrowserReturnURL, ts.URL+\"/registration-return-ts\")\n}\nfunc (fix *fixture) useRedirTS()          { fix.useReturnToFromTS(fix.redirTS) }\nfunc (fix *fixture) useRedirNoSessionTS() { fix.useReturnToFromTS(fix.redirNoSessionTS) }\n\nfunc (fix *fixture) disableSessionAfterRegistration() {\n\tfix.conf.MustSet(context.TODO(), config.HookStrategyKey(\n\t\tconfig.ViperKeySelfServiceRegistrationAfter,\n\t\tidentity.CredentialsTypePasskey.String(),\n\t), nil)\n}\n\nfunc (fix *fixture) enableSessionAfterRegistration() {\n\tfix.conf.MustSet(context.TODO(), config.HookStrategyKey(\n\t\tconfig.ViperKeySelfServiceRegistrationAfter,\n\t\tidentity.CredentialsTypePasskey.String(),\n\t), []config.SelfServiceHook{{Name: \"session\"}})\n}\n\ntype submitPasskeyOpt struct {\n\tinitFlowOpts    []testhelpers.InitFlowWithOption\n\tuserID          string\n\tinternalContext sqlxx.JSONRawMessage\n}\n\ntype submitPasskeyOption func(o *submitPasskeyOpt)\n\nfunc withUserID(id string) submitPasskeyOption {\n\treturn func(o *submitPasskeyOpt) {\n\t\to.userID = base64.StdEncoding.EncodeToString([]byte(id))\n\t}\n}\n\nfunc withInternalContext(ic sqlxx.JSONRawMessage) submitPasskeyOption {\n\treturn func(o *submitPasskeyOpt) {\n\t\to.internalContext = ic\n\t}\n}\n\nfunc withInitFlowWithOption(ifo []testhelpers.InitFlowWithOption) submitPasskeyOption {\n\treturn func(o *submitPasskeyOpt) {\n\t\to.initFlowOpts = ifo\n\t}\n}\n\nfunc (fix *fixture) submitPasskeyBrowserRegistration(\n\tt *testing.T,\n\tflowType string,\n\tclient *http.Client,\n\tcb func(values url.Values),\n\topts ...submitPasskeyOption,\n) (string, *http.Response, *kratos.RegistrationFlow) {\n\treturn fix.submitPasskeyRegistration(t.Context(), t, flowType, client, cb, append([]submitPasskeyOption{withInternalContext(registrationFixtureSuccessBrowserInternalContext)}, opts...)...)\n}\n\nfunc (fix *fixture) submitPasskeyAndroidRegistration(\n\tt *testing.T,\n\tflowType string,\n\tclient *http.Client,\n\tcb func(values url.Values),\n\topts ...submitPasskeyOption,\n) (string, *http.Response, *kratos.RegistrationFlow) {\n\treturn fix.submitPasskeyRegistration(t.Context(), t, flowType, client, cb,\n\t\tappend([]submitPasskeyOption{withInternalContext(\n\t\t\tregistrationFixtureSuccessAndroidInternalContext,\n\t\t)}, opts...)...)\n}\n\nfunc (fix *fixture) submitPasskeyRegistration(\n\tctx context.Context,\n\tt *testing.T,\n\tflowType string,\n\tclient *http.Client,\n\tcb func(values url.Values),\n\topts ...submitPasskeyOption,\n) (string, *http.Response, *kratos.RegistrationFlow) {\n\to := &submitPasskeyOpt{}\n\tfor _, fn := range opts {\n\t\tfn(o)\n\t}\n\n\tisSPA := flowType == \"spa\"\n\tisAPI := flowType == \"api\"\n\n\tvar regFlow *kratos.RegistrationFlow\n\tif isAPI {\n\t\tregFlow = testhelpers.InitializeRegistrationFlowViaAPI(t, client, fix.publicTS, o.initFlowOpts...)\n\t} else {\n\t\tregFlow = testhelpers.InitializeRegistrationFlowViaBrowserCtx(ctx, t, client, fix.publicTS, isSPA, false, false, o.initFlowOpts...)\n\t}\n\n\t// First step: fill out traits and click on \"sign up with passkey\"\n\tvalues := testhelpers.SDKFormFieldsToURLValues(regFlow.Ui.Nodes)\n\tcb(values)\n\tpasskeyRegisterVal := values.Get(node.PasskeyRegister) // needed in the second step\n\tvalues.Del(node.PasskeyRegister)\n\tvalues.Set(\"method\", \"passkey\")\n\n\t// Encode payload based on flow type\n\tvar payload string\n\tif isAPI {\n\t\tpayload = testhelpers.EncodeFormAsJSON(t, true, values)\n\t} else {\n\t\tpayload = values.Encode()\n\t}\n\t_, _ = testhelpers.RegistrationMakeRequest(t, isAPI, isSPA, regFlow, client, payload)\n\n\t// We inject the session to replay\n\tinterim, err := fix.reg.RegistrationFlowPersister().GetRegistrationFlow(ctx, uuid.FromStringOrNil(regFlow.Id))\n\trequire.NoError(t, err)\n\tif len(o.internalContext) > 0 {\n\t\tinterim.InternalContext = o.internalContext\n\t}\n\tif o.userID != \"\" {\n\t\tinterim.InternalContext, err = sjson.SetBytes(interim.InternalContext, \"passkey_session_data.user_id\", o.userID)\n\t\trequire.NoError(t, err)\n\t}\n\trequire.NoError(t, fix.reg.RegistrationFlowPersister().UpdateRegistrationFlow(ctx, interim))\n\n\t// Second step: fill out passkey response\n\tvalues.Set(node.PasskeyRegister, passkeyRegisterVal)\n\tif isAPI {\n\t\tpayload = testhelpers.EncodeFormAsJSON(t, true, values)\n\t} else {\n\t\tpayload = values.Encode()\n\t}\n\tbody, res := testhelpers.RegistrationMakeRequest(t, isAPI, isSPA, regFlow, client, payload)\n\n\treturn body, res, regFlow\n}\n\nfunc (fix *fixture) makeRegistration(t *testing.T, flowType string, values func(v url.Values), opts ...submitPasskeyOption) (actual string, res *http.Response, fetchedFlow *registration.Flow) {\n\tactual, res, actualFlow := fix.submitPasskeyBrowserRegistration(t, flowType, testhelpers.NewClientWithCookies(t), values, opts...)\n\tfetchedFlow, err := fix.reg.RegistrationFlowPersister().GetRegistrationFlow(t.Context(), uuid.FromStringOrNil(actualFlow.Id))\n\trequire.NoError(t, err)\n\n\treturn actual, res, fetchedFlow\n}\n\nfunc (fix *fixture) makeSuccessfulRegistration(t *testing.T, flowType string, expectReturnTo string, values func(v url.Values), opts ...submitPasskeyOption) (actual string) {\n\tactual, res, _ := fix.makeRegistration(t, flowType, values, opts...)\n\tif flowType == \"spa\" {\n\t\texpectReturnTo = fix.publicTS.URL\n\t}\n\tif flowType == \"api\" {\n\t\t// API does not redirect; response body should contain JSON with identity/session fields.\n\t\t// Leave detailed assertions to the calling test.\n\t\treturn actual\n\t}\n\tassert.Contains(t, res.Request.URL.String(), expectReturnTo, \"%+v\\n\\t%s\", res.Request, assertx.PrettifyJSONPayload(t, actual))\n\treturn actual\n}\n\nfunc (fix *fixture) makeUnsuccessfulRegistration(t *testing.T, flowType string, expectReturnTo string, values func(v url.Values), opts ...submitPasskeyOption) (actual string, res *http.Response) {\n\tactual, res, _ = fix.makeRegistration(t, flowType, values, opts...)\n\treturn actual, res\n}\n\nfunc (fix *fixture) createIdentityWithoutPasskey(t *testing.T) *identity.Identity {\n\tid := fix.createIdentity(t)\n\tdelete(id.Credentials, identity.CredentialsTypePasskey)\n\trequire.NoError(t, fix.reg.PrivilegedIdentityPool().UpdateIdentity(t.Context(), id))\n\treturn id\n}\n\nfunc (fix *fixture) createIdentityAndReturnIdentifier(t *testing.T, conf []byte) (*identity.Identity, string) {\n\tidentifier := x.NewUUID().String() + \"@ory.sh\"\n\tpassword := x.NewUUID().String()\n\tp, err := fix.reg.Hasher(t.Context()).Generate(t.Context(), []byte(password))\n\trequire.NoError(t, err)\n\ti := &identity.Identity{\n\t\tTraits: identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, identifier)),\n\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t{\n\t\t\t\tValue:     identifier,\n\t\t\t\tVerified:  false,\n\t\t\t\tCreatedAt: time.Now(),\n\t\t\t},\n\t\t},\n\t}\n\tif conf == nil {\n\t\tconf = []byte(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\"},{\"id\":\"YmFyYmFy\",\"display_name\":\"bar\"}]}`)\n\t}\n\trequire.NoError(t, fix.reg.PrivilegedIdentityPool().CreateIdentity(t.Context(), i))\n\ti.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\tidentity.CredentialsTypePassword: {\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{identifier},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t},\n\t\tidentity.CredentialsTypePasskey: {\n\t\t\tType:        identity.CredentialsTypePasskey,\n\t\t\tIdentifiers: []string{identifier},\n\t\t\tConfig:      conf,\n\t\t},\n\t}\n\trequire.NoError(t, fix.reg.PrivilegedIdentityPool().UpdateIdentity(t.Context(), i))\n\treturn i, identifier\n}\n\nfunc (fix *fixture) createIdentity(t *testing.T) *identity.Identity {\n\tid, _ := fix.createIdentityAndReturnIdentifier(t, nil)\n\treturn id\n}\n"
  },
  {
    "path": "selfservice/strategy/password/.schema/login.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/password/login.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"password\": {\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n    \"identifier\": {\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  },\n  \"allOf\": [\n    {\n      \"if\": {\n        \"properties\": {\n          \"method\": {\n            \"const\": \"password\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ]\n      },\n      \"then\": {\n        \"required\": [\n          \"password\"\n        ]\n      }\n    },\n    {\n      \"if\": {\n        \"properties\": {\n          \"password_identifier\": {\n            \"type\": \"string\",\n            \"minLength\": 1\n          }\n        },\n        \"required\": [\n          \"password_identifier\"\n        ]\n      },\n      \"then\": {\n        \"required\": [\n          \"password_identifier\"\n        ]\n      },\n      \"else\": {\n        \"required\": [\n          \"identifier\"\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/password/.schema/registration.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/password/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"password\": {\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n    \"traits\": {\n      \"description\": \"This field will be overwritten in registration.go's decoder() method. Do not add anything to this field as it has no effect.\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  },\n  \"if\": {\n    \"properties\": {\n      \"method\": {\n        \"const\": \"password\"\n      }\n    },\n    \"required\": [\n      \"method\"\n    ]\n  },\n  \"then\": {\n    \"required\": [\n      \"password\"\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/password/.schema/settings.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/password/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\n    \"password\"\n  ],\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"password\": {\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"current-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010022,\n        \"text\": \"Sign in with password\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactorRefresh.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"hidden\",\n      \"value\": \"some@user.com\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"current-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010001,\n        \"text\": \"Sign in\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier-case=account_enumeration_mitigation_disabled.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier-case=account_enumeration_mitigation_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"current-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010022,\n        \"text\": \"Sign in with password\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_does_not_have_a_password.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_has_password.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"current-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010022,\n        \"text\": \"Sign in with password\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_enabled_and_identity_has_no_password.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"current-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010022,\n        \"text\": \"Sign in with password\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=account_enumeration_mitigation_disabled.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=account_enumeration_mitigation_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"current-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010022,\n        \"text\": \"Sign in with password\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactorRefresh.json",
    "content": "null\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethod.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodCredentials.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodProfile.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=1.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=2.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=3.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=4.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/password/helpers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport \"net/url\"\n\nfunc tidyForm(vv url.Values) url.Values {\n\tfor _, k := range []string{\"password\", \"csrf_token\", \"flow\"} {\n\t\tvv.Del(k)\n\t}\n\n\treturn vv\n}\n"
  },
  {
    "path": "selfservice/strategy/password/helpers_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport (\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTidyForm(t *testing.T) {\n\tassert.EqualValues(t, url.Values{\"foobar\": {\"foo\"}}, tidyForm(url.Values{\n\t\t\"password\":   {\"some-value\"},\n\t\t\"csrf_token\": {\"some-value\"},\n\t\t\"flow\":       {\"some-value\"},\n\t\t\"foobar\":     {\"foo\"},\n\t}))\n}\n"
  },
  {
    "path": "selfservice/strategy/password/login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport (\n\t\"bytes\"\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flowhelpers\"\n\t\"github.com/ory/kratos/selfservice/hook\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n)\n\nvar _ login.AAL1FormHydrator = new(Strategy)\n\nfunc (s *Strategy) handleLoginError(r *http.Request, f *login.Flow, payload updateLoginFlowWithPasswordMethod, err error) error {\n\tif f != nil {\n\t\tf.UI.Nodes.ResetNodes(\"password\")\n\t\tf.UI.Nodes.SetValueAttribute(\"identifier\", cmp.Or(payload.Identifier, payload.LegacyIdentifier))\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, _ *session.Session) (i *identity.Identity, err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.password.Strategy.Login\")\n\tdefer otelx.End(span, &err)\n\n\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel1); err != nil {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"requested AAL is not AAL1\"))\n\t\treturn nil, err\n\t}\n\n\tif err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.ID().String(), s.d); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar p updateLoginFlowWithPasswordMethod\n\tif err := decoderx.Decode(r, &p,\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.MustHTTPRawJSONSchemaCompiler(loginSchema),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, p, err)\n\t}\n\tf.TransientPayload = p.TransientPayload\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, p, err)\n\t}\n\n\tidentifier := cmp.Or(p.Identifier, p.LegacyIdentifier)\n\ti, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), identifier)\n\tif err != nil {\n\t\ttime.Sleep(x.RandomDelay(s.d.Config().HasherArgon2(ctx).ExpectedDuration, s.d.Config().HasherArgon2(ctx).ExpectedDeviation))\n\t\treturn nil, s.handleLoginError(r, f, p, errors.WithStack(schema.NewInvalidCredentialsError()))\n\t}\n\n\tvar o identity.CredentialsPassword\n\td := json.NewDecoder(bytes.NewBuffer(c.Config))\n\tif err := d.Decode(&o); err != nil {\n\t\treturn nil, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"The password credentials could not be decoded properly\").WithDebug(err.Error()).WithWrap(err)), i.ID)\n\t}\n\n\tif o.ShouldUsePasswordMigrationHook() {\n\t\tpwHook := s.d.Config().PasswordMigrationHook(ctx)\n\t\tif !pwHook.Enabled {\n\t\t\treturn nil, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrMisconfiguration.WithReasonf(\"Password migration hook is not enabled but password migration is requested.\")), i.ID)\n\t\t}\n\n\t\tmigrationHook := hook.NewPasswordMigrationHook(s.d, &pwHook.Config)\n\t\terr = migrationHook.Execute(ctx, r, f, &hook.PasswordMigrationRequest{\n\t\t\tIdentifier: identifier,\n\t\t\tPassword:   p.Password,\n\t\t\tIdentity:   i,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, s.handleLoginError(r, f, p, x.WrapWithIdentityIDError(err, i.ID))\n\t\t}\n\n\t\tif err := s.migratePasswordHash(ctx, i.ID, []byte(p.Password)); err != nil {\n\t\t\treturn nil, s.handleLoginError(r, f, p, x.WrapWithIdentityIDError(err, i.ID))\n\t\t}\n\t} else {\n\t\tif err := hash.Compare(ctx, []byte(p.Password), []byte(o.HashedPassword)); err != nil {\n\t\t\treturn nil, s.handleLoginError(r, f, p, errors.WithStack(x.WrapWithIdentityIDError(schema.NewInvalidCredentialsError(), i.ID)))\n\t\t}\n\n\t\tif !s.d.Hasher(ctx).Understands([]byte(o.HashedPassword)) {\n\t\t\tif err := s.migratePasswordHash(ctx, i.ID, []byte(p.Password)); err != nil {\n\t\t\t\ts.d.Logger().Warnf(\"Unable to migrate password hash for identity %s: %s Keeping existing password hash and continuing.\", i.ID, x.WrapWithIdentityIDError(err, i.ID))\n\t\t\t}\n\t\t}\n\t}\n\n\tf.Active = s.ID()\n\tif err = s.d.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, p, errors.WithStack(x.WrapWithIdentityIDError(herodot.ErrInternalServerError.WithReason(\"Could not update flow\").WithDebug(err.Error()), i.ID)))\n\t}\n\n\treturn i, nil\n}\n\nfunc (s *Strategy) migratePasswordHash(ctx context.Context, identifier uuid.UUID, password []byte) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.password.Strategy.migratePasswordHash\")\n\tdefer otelx.End(span, &err)\n\n\thpw, err := s.d.Hasher(ctx).Generate(ctx, password)\n\tif err != nil {\n\t\treturn err\n\t}\n\tco, err := json.Marshal(&identity.CredentialsPassword{HashedPassword: string(hpw)})\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"unable to encode password configuration to JSON\")\n\t}\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, identifier)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc, ok := i.GetCredentials(s.ID())\n\tif !ok {\n\t\treturn errors.New(\"expected to find password credential but could not\")\n\t}\n\n\tc.Config = co\n\ti.SetCredentials(s.ID(), *c)\n\n\treturn s.d.IdentityManager().Update(ctx, i, identity.ManagerAllowWriteProtectedTraits)\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactorRefresh(r *http.Request, sr *login.Flow, _ *session.Session) (err error) {\n\tctx := r.Context()\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.password.Strategy.PopulateLoginMethodFirstFactorRefresh\")\n\tdefer otelx.End(span, &err)\n\n\tidentifier, id, _ := flowhelpers.GuessForcedLoginIdentifier(r, s.d, sr, s.ID())\n\tif identifier == \"\" {\n\t\treturn nil\n\t}\n\n\t// If we don't have a password set, do not show the password field.\n\tcount, err := s.CountActiveFirstFactorCredentials(ctx, id.Credentials)\n\tif err != nil {\n\t\treturn err\n\t} else if count == 0 {\n\t\treturn nil\n\t}\n\n\tsr.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tsr.UI.SetNode(node.NewInputField(\"identifier\", identifier, node.DefaultGroup, node.InputAttributeTypeHidden))\n\tsr.UI.SetNode(NewPasswordNode(\"password\", node.InputAttributeAutocompleteCurrentPassword))\n\tsr.UI.GetNodes().Append(node.NewInputField(\"method\", \"password\", node.PasswordGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoLogin()))\n\treturn nil\n}\n\nfunc (s *Strategy) addIdentifierNode(r *http.Request, sr *login.Flow) error {\n\tds, err := sr.IdentitySchema.URL(r.Context(), s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tidentifierLabel, err := login.GetIdentifierLabelFromSchema(r.Context(), ds.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsr.UI.SetNode(node.NewInputField(\"identifier\", \"\", node.DefaultGroup, node.InputAttributeTypeText, node.WithRequiredInputAttribute).WithMetaLabel(identifierLabel))\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactor(r *http.Request, sr *login.Flow) error {\n\tif err := s.addIdentifierNode(r, sr); err != nil {\n\t\treturn err\n\t}\n\n\tsr.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tsr.UI.SetNode(NewPasswordNode(\"password\", node.InputAttributeAutocompleteCurrentPassword))\n\tsr.UI.GetNodes().Append(node.NewInputField(\"method\", \"password\", node.PasswordGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoLoginPassword()))\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstCredentials(r *http.Request, sr *login.Flow, opts ...login.FormHydratorModifier) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.password.Strategy.PopulateLoginMethodIdentifierFirstCredentials\")\n\tdefer otelx.End(span, &err)\n\n\to := login.NewFormHydratorOptions(opts)\n\n\tvar count int\n\tif o.IdentityHint != nil {\n\t\tvar err error\n\t\t// If we have an identity hint we can perform identity credentials discovery and\n\t\t// hide this credential if it should not be included.\n\t\tif count, err = s.CountActiveFirstFactorCredentials(ctx, o.IdentityHint.Credentials); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif count > 0 || s.d.Config().SecurityAccountEnumerationMitigate(ctx) {\n\t\tsr.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\tsr.UI.SetNode(NewPasswordNode(\"password\", node.InputAttributeAutocompleteCurrentPassword))\n\t\tsr.UI.GetNodes().Append(node.NewInputField(\"method\", \"password\", node.PasswordGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoLoginPassword()))\n\t}\n\n\tif count == 0 {\n\t\treturn errors.WithStack(idfirst.ErrNoCredentialsFound)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstIdentification(r *http.Request, sr *login.Flow) error {\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/password/login_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/sha256\"\n\t_ \"embed\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"slices\"\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/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/registrationhelpers\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/errorsx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/randx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc createIdentity(ctx context.Context, reg *driver.RegistryDefault, t *testing.T, identifier, password string) *identity.Identity {\n\tp, _ := reg.Hasher(ctx).Generate(t.Context(), []byte(password))\n\tiId := x.NewUUID()\n\tid := &identity.Identity{\n\t\tID:     iId,\n\t\tTraits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, identifier)),\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{identifier},\n\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t\t},\n\t\t},\n\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t{\n\t\t\t\tID:         x.NewUUID(),\n\t\t\t\tValue:      identifier,\n\t\t\t\tVerified:   false,\n\t\t\t\tCreatedAt:  time.Now(),\n\t\t\t\tIdentityID: iId,\n\t\t\t},\n\t\t},\n\t}\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, id))\n\treturn id\n}\n\nfunc TestCompleteLogin(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword)+\".enabled\", true),\n\t\tconfigx.WithValues(testhelpers.IdentitySchemasConfig(map[string]string{\n\t\t\t\"migration\": \"file://./stub/migration.schema.json\",\n\t\t\t\"default\":   \"file://./stub/login.schema.json\",\n\t\t})),\n\t)\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tpublicTS, _ := testhelpers.NewKratosServerWithRouters(t, reg, router, httprouterx.NewTestRouterAdminWithPrefix(t))\n\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tuiTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\n\tensureFieldsExist := func(t *testing.T, body []byte) {\n\t\tregistrationhelpers.CheckFormContent(t, body, \"identifier\",\n\t\t\t\"password\",\n\t\t\t\"csrf_token\")\n\t}\n\n\tapiClient := testhelpers.NewDebugClient(t)\n\n\tt.Run(\"case=should show the error ui because the request payload is malformed\", func(t *testing.T) {\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\t\tbody, res := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, body, `Expected JSON sent in request body to be an object but got: Number`)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)\n\n\t\t\tbody, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login-ts\")\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"invalid URL escape\", \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, true, false, false)\n\n\t\t\tbody, res := testhelpers.LoginMakeRequest(t, false, true, f, browserClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"invalid URL escape\", \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail because password can not handle AAL2\", func(t *testing.T) {\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\tupdate, err := reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\trequire.NoError(t, err)\n\t\tupdate.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), update))\n\n\t\treq, err := http.NewRequest(\"POST\", f.Ui.Action, bytes.NewBufferString(`{\"method\":\"password\"}`))\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tactual, res := testhelpers.MockMakeAuthenticatedRequest(t, reg, conf, router, req)\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\tassert.Equal(t, text.NewErrorValidationLoginNoStrategyFound().Text, gjson.GetBytes(actual, \"ui.messages.0.text\").String())\n\t})\n\n\tt.Run(\"should return an error because the request does not exist\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.Equal(t, int64(http.StatusNotFound), gjson.Get(actual, \"code\").Int(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"Not Found\", gjson.Get(actual, \"status\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"message\").String(), \"Unable to locate the resource\", \"%s\", actual)\n\t\t}\n\n\t\tfakeFlow := &kratos.LoginFlow{\n\t\t\tUi: kratos.UiContainer{\n\t\t\t\tAction: publicTS.URL + login.RouteSubmitFlow + \"?flow=\" + x.NewUUID().String(),\n\t\t\t},\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, true, false, fakeFlow, apiClient, \"{}\")\n\t\t\tassert.Len(t, res.Cookies(), 0)\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tcheck(t, gjson.Get(actual, \"error\").Raw)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, false, false, fakeFlow, browserClient, \"\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tcheck(t, actual)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, false, true, fakeFlow, apiClient, \"{}\")\n\t\t\tassert.Len(t, res.Cookies(), 0)\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tcheck(t, gjson.Get(actual, \"error\").Raw)\n\t\t})\n\t})\n\n\tt.Run(\"case=should return an error because the request is expired\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceLoginRequestLifespan, 100*time.Millisecond)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceLoginRequestLifespan, 10*time.Minute)\n\t\t})\n\t\tvalues := url.Values{\n\t\t\t\"csrf_token\": {nosurfx.FakeCSRFToken},\n\t\t\t\"identifier\": {\"identifier\"},\n\t\t\t\"password\":   {\"password\"},\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\t\ttime.Sleep(101 * time.Millisecond)\n\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.Get(actual, \"use_flow_id\").Str)\n\t\t\tassert.NotZero(t, gjson.Get(actual, \"use_flow_id\").Str)\n\t\t\tassert.Regexpf(t, regexp.MustCompile(`The self-service flow expired 0\\.0\\d minutes ago, initialize a new one\\.`), gjson.Get(actual, \"error.reason\").Str, \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)\n\n\t\t\ttime.Sleep(101 * time.Millisecond)\n\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values.Encode())\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login-ts\")\n\t\t\tassert.NotEqual(t, f.Id, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\tassert.Regexpf(t, regexp.MustCompile(`The login flow expired 0\\.0\\d minutes ago, please try again\\.`), gjson.Get(actual, \"ui.messages.0.text\").Str, \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=SPA\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, true, false, false)\n\n\t\t\ttime.Sleep(101 * time.Millisecond)\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, false, true, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEqual(t, \"00000000-0000-0000-0000-000000000000\", gjson.Get(actual, \"use_flow_id\").String())\n\t\t\tassert.NotZero(t, gjson.Get(actual, \"use_flow_id\").String())\n\t\t\tassert.Regexpf(t, regexp.MustCompile(`The self-service flow expired 0\\.0\\d minutes ago, initialize a new one\\.`), gjson.Get(actual, \"error.reason\").Str, \"%s\", actual)\n\t\t})\n\t})\n\n\tt.Run(\"case=should have correct CSRF behavior\", func(t *testing.T) {\n\t\tvalues := url.Values{\n\t\t\t\"method\":     {\"password\"},\n\t\t\t\"csrf_token\": {\"invalid_token\"},\n\t\t\t\"identifier\": {\"login-identifier-csrf-browser\"},\n\t\t\t\"password\":   {x.NewUUID().String()},\n\t\t}\n\n\t\tt.Run(\"case=should fail because of missing CSRF token/type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tbrowserClient.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)\n\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values.Encode())\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenServerTokenMismatch,\n\t\t\t\tjson.RawMessage(actual), \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=should fail because of missing CSRF token/type=spa\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tbrowserClient.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, true, false, false)\n\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, false, true, f, browserClient, values.Encode())\n\t\t\tassert.EqualValues(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch,\n\t\t\t\tjson.RawMessage(gjson.Get(actual, \"error\").Raw), \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=should pass even without CSRF token/type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\t\tactual, res := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassert.Contains(t, actual, \"provided credentials are invalid\")\n\t\t})\n\n\t\tt.Run(\"case=should fail with correct CSRF error cause/type=api\", func(t *testing.T) {\n\t\t\tfor k, tc := range []struct {\n\t\t\t\tmod func(http.Header)\n\t\t\t\texp string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\t\th.Add(\"Cookie\", \"name=bar\")\n\t\t\t\t\t},\n\t\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Cookie\\\\\\\" key\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\t\th.Add(\"Origin\", \"www.bar.com\")\n\t\t\t\t\t},\n\t\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Origin\\\\\\\" key\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\t\t\t\treq := testhelpers.NewPostRequest(t, true, f.Ui.Action, bytes.NewBufferString(testhelpers.EncodeFormAsJSON(t, true, values)))\n\t\t\t\t\ttc.mod(req.Header)\n\n\t\t\t\t\tres, err := apiClient.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\t\tactual := string(ioutilx.MustReadAll(res.Body))\n\t\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\t\tassert.Contains(t, actual, tc.exp)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\texpectValidationError := func(t *testing.T, isAPI, refresh, isSPA bool, values func(url.Values)) string {\n\t\treturn testhelpers.SubmitLoginForm(t, isAPI, nil, publicTS, values,\n\t\t\tisSPA, refresh,\n\t\t\ttesthelpers.ExpectStatusCode(isAPI || isSPA, http.StatusBadRequest, http.StatusOK),\n\t\t\ttesthelpers.ExpectURL(isAPI || isSPA, publicTS.URL+login.RouteSubmitFlow, conf.SelfServiceFlowLoginUI(t.Context()).String()))\n\t}\n\n\tt.Run(\"should return an error because the credentials are invalid (user does not exist)\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, body string, start time.Time) {\n\t\t\tdelay := time.Since(start)\n\t\t\tminConfiguredDelay := conf.HasherArgon2(t.Context()).ExpectedDuration - conf.HasherArgon2(t.Context()).ExpectedDeviation\n\t\t\tassert.GreaterOrEqual(t, delay, minConfiguredDelay)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.action\").String(), publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\t\t\tassert.Equal(t, text.NewErrorValidationInvalidCredentials().Text, gjson.Get(body, \"ui.messages.0.text\").String(), body)\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"identifier\", \"identifier\")\n\t\t\tv.Set(\"password\", \"password\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tstart := time.Now()\n\t\t\tcheck(t, expectValidationError(t, false, false, false, values), start)\n\t\t})\n\n\t\tt.Run(\"type=SPA\", func(t *testing.T) {\n\t\t\tstart := time.Now()\n\t\t\tcheck(t, expectValidationError(t, false, false, true, values), start)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tstart := time.Now()\n\t\t\tcheck(t, expectValidationError(t, true, false, false, values), start)\n\t\t})\n\t})\n\n\tt.Run(\"should return an error because no identifier is set\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, body string) {\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.action\").String(), publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\n\t\t\tensureFieldsExist(t, []byte(body))\n\t\t\tassert.Equal(t, \"Property identifier is missing.\", gjson.Get(body, \"ui.nodes.#(attributes.name==identifier).messages.0.text\").String(), \"%s\", body)\n\t\t\tassert.Len(t, gjson.Get(body, \"ui.nodes\").Array(), 4)\n\n\t\t\t// The password value should not be returned!\n\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==password).attributes.value\").String())\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Del(\"identifier\")\n\t\t\tv.Set(\"method\", identity.CredentialsTypePassword.String())\n\t\t\tv.Set(\"password\", \"password\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, true, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"should return an error because no password is set\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, body string) {\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.action\").String(), publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\n\t\t\tensureFieldsExist(t, []byte(body))\n\t\t\tassert.Equal(t, \"Property password is missing.\", gjson.Get(body, \"ui.nodes.#(attributes.name==password).messages.0.text\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"identifier\", gjson.Get(body, \"ui.nodes.#(attributes.name==identifier).attributes.value\").String(), \"%s\", body)\n\t\t\tassert.Len(t, gjson.Get(body, \"ui.nodes\").Array(), 4)\n\n\t\t\t// This must not include the password!\n\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==password).attributes.value\").String())\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"identifier\", \"identifier\")\n\t\t\tv.Del(\"password\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"should return an error both identifier and password are missing\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, body string) {\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.action\").String(), publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\n\t\t\tensureFieldsExist(t, []byte(body))\n\t\t\tassert.Equal(t, \"Property password is missing.\", gjson.Get(body, \"ui.nodes.#(attributes.name==password).messages.0.text\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"Property identifier is missing.\", gjson.Get(body, \"ui.nodes.#(attributes.name==identifier).messages.0.text\").String(), \"%s\", body)\n\t\t\tassert.Len(t, gjson.Get(body, \"ui.nodes\").Array(), 4)\n\n\t\t\t// This must not include the password!\n\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==password).attributes.value\").String())\n\t\t}\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"password\", \"\")\n\t\t\tv.Set(\"identifier\", \"\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, true, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"should return an error because the credentials are invalid (password not correct)\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, body string) {\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.action\").String(), publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\n\t\t\tensureFieldsExist(t, []byte(body))\n\t\t\tassert.Equal(t,\n\t\t\t\terrorsx.Cause(schema.NewInvalidCredentialsError()).(*schema.ValidationError).Messages[0].Text,\n\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String(),\n\t\t\t\t\"%s\", body,\n\t\t\t)\n\n\t\t\t// This must not include the password!\n\t\t\tassert.Empty(t, gjson.Get(body, \"ui.nodes.#(attributes.name==password).attributes.value\").String())\n\t\t}\n\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, pwd)\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"identifier\", identifier)\n\t\t\tv.Set(\"password\", \"not-password\")\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, false, values))\n\t\t})\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, true, values))\n\t\t})\n\t})\n\n\tt.Run(\"should pass with real request\", func(t *testing.T) {\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, pwd)\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"identifier\", identifier)\n\t\t\tv.Set(\"password\", pwd)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\n\t\t\tbody := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,\n\t\t\t\tfalse, false, http.StatusOK, redirTS.URL)\n\n\t\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.subject\").String(), \"%s\", body)\n\n\t\t\tt.Run(\"retry with different refresh\", func(t *testing.T) {\n\t\t\t\tt.Run(\"redirect to returnTS if refresh is missing\", func(t *testing.T) {\n\t\t\t\t\tres, err := browserClient.Get(publicTS.URL + login.RouteInitBrowserFlow)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"show UI and hint at username\", func(t *testing.T) {\n\t\t\t\t\tres, err := browserClient.Get(publicTS.URL + login.RouteInitBrowserFlow + \"?refresh=true\")\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\trid := res.Request.URL.Query().Get(\"flow\")\n\t\t\t\t\tassert.NotEmpty(t, rid, \"%s\", res.Request.URL)\n\n\t\t\t\t\tres, err = browserClient.Get(publicTS.URL + login.RouteGetFlow + \"?id=\" + rid)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.True(t, gjson.GetBytes(body, \"refresh\").Bool())\n\t\t\t\t\tassert.Equal(t, identifier, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier).attributes.value\").String(), \"%s\", body)\n\t\t\t\t\tassert.Empty(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==password).attributes.value\").String(), \"%s\", body)\n\t\t\t\t\tassert.True(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==password)\").Exists(), \"%s\", body)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"do not show password method if identity has no password set\", func(t *testing.T) {\n\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\tid.NID = x.NewUUID()\n\t\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\n\t\t\t\tres, err := browserClient.Get(publicTS.URL + login.RouteInitBrowserFlow + \"?refresh=true\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\trid := res.Request.URL.Query().Get(\"flow\")\n\t\t\t\tassert.NotEmpty(t, rid, \"%s\", res.Request.URL)\n\n\t\t\t\tres, err = browserClient.Get(publicTS.URL + login.RouteGetFlow + \"?id=\" + rid)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.True(t, gjson.GetBytes(body, \"refresh\").Bool())\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier)\").Exists(), \"%s\", body)\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier)\").Exists(), \"%s\", body)\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==password)\").Exists(), \"%s\", body)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\thc := testhelpers.NewClientWithCookies(t)\n\n\t\t\tbody := testhelpers.SubmitLoginForm(t, false, hc, publicTS, values,\n\t\t\t\ttrue, false, http.StatusOK, publicTS.URL+login.RouteSubmitFlow)\n\n\t\t\tassert.Equal(t, identifier, gjson.Get(body, \"session.identity.traits.subject\").String(), \"%s\", body)\n\t\t\tassert.Empty(t, gjson.Get(body, \"session_token\").String(), \"%s\", body)\n\t\t\tassert.Empty(t, gjson.Get(body, \"session.token\").String(), \"%s\", body)\n\n\t\t\t// Was the session cookie set?\n\t\t\trequire.NotEmpty(t, hc.Jar.Cookies(urlx.ParseOrPanic(publicTS.URL)), \"%+v\", hc.Jar)\n\n\t\t\tt.Run(\"retry with different refresh\", func(t *testing.T) {\n\t\t\t\tt.Run(\"redirect to returnTS if refresh is missing\", func(t *testing.T) {\n\t\t\t\t\tres, err := hc.Do(testhelpers.NewHTTPGetAJAXRequest(t, publicTS.URL+login.RouteInitBrowserFlow))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode, \"%s\", body)\n\t\t\t\t\tassertx.EqualAsJSON(t, login.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"show UI and hint at username\", func(t *testing.T) {\n\t\t\t\t\tres, err := hc.Do(testhelpers.NewHTTPGetAJAXRequest(t, publicTS.URL+login.RouteInitBrowserFlow+\"?refresh=true\"))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\tassert.True(t, gjson.GetBytes(body, \"refresh\").Bool())\n\t\t\t\t\tassert.Equal(t, identifier, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier).attributes.value\").String(), \"%s\", body)\n\t\t\t\t\tassert.Empty(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==password).attributes.value\").String(), \"%s\", body)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"do not show password method if identity has no password set\", func(t *testing.T) {\n\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\tid.NID = x.NewUUID()\n\t\t\t\thc := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\n\t\t\t\tres, err := hc.Do(testhelpers.NewHTTPGetAJAXRequest(t, publicTS.URL+login.RouteInitBrowserFlow+\"?refresh=true\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\tassert.True(t, gjson.GetBytes(body, \"refresh\").Bool())\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier)\").Exists(), \"%s\", body)\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier)\").Exists(), \"%s\", body)\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==password)\").Exists(), \"%s\", body)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tbody := testhelpers.SubmitLoginForm(t, true, nil, publicTS, values,\n\t\t\t\tfalse, false, http.StatusOK, publicTS.URL+login.RouteSubmitFlow)\n\n\t\t\tassert.Equal(t, identifier, gjson.Get(body, \"session.identity.traits.subject\").String(), \"%s\", body)\n\t\t\tst := gjson.Get(body, \"session_token\").String()\n\t\t\tassert.NotEmpty(t, st, \"%s\", body)\n\n\t\t\tt.Run(\"retry with different refresh\", func(t *testing.T) {\n\t\t\t\tc := &http.Client{Transport: testhelpers.NewTransportWithHeader(t, http.Header{\"Authorization\": {\"Bearer \" + st}})}\n\n\t\t\t\tt.Run(\"redirect to returnTS if refresh is missing\", func(t *testing.T) {\n\t\t\t\t\tres, err := c.Do(testhelpers.NewHTTPGetJSONRequest(t, publicTS.URL+login.RouteInitAPIFlow))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\t\tassertx.EqualAsJSON(t, login.ErrAlreadyLoggedIn, json.RawMessage(gjson.GetBytes(body, \"error\").Raw), \"%s\", body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"show UI and hint at username\", func(t *testing.T) {\n\t\t\t\t\tres, err := c.Do(testhelpers.NewHTTPGetJSONRequest(t, publicTS.URL+login.RouteInitAPIFlow+\"?refresh=true\"))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\tassert.True(t, gjson.GetBytes(body, \"refresh\").Bool())\n\t\t\t\t\tassert.Equal(t, identifier, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier).attributes.value\").String(), \"%s\", body)\n\t\t\t\t\tassert.Empty(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==password).attributes.value\").String(), \"%s\", body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"show verification confirmation when refresh is set to true\", func(t *testing.T) {\n\t\t\t\t\tres, err := c.Do(testhelpers.NewHTTPGetJSONRequest(t, publicTS.URL+login.RouteInitAPIFlow+\"?refresh=true\"))\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\t\tassert.True(t, gjson.GetBytes(body, \"refresh\").Bool())\n\t\t\t\t\tassert.Contains(t, gjson.GetBytes(body, \"ui.messages.0.text\").String(), \"verifying that\", \"%s\", body)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"do not show password method if identity has no password set\", func(t *testing.T) {\n\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\tid.NID = x.NewUUID()\n\t\t\t\thc := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\n\t\t\t\tres, err := hc.Do(testhelpers.NewHTTPGetAJAXRequest(t, publicTS.URL+login.RouteInitAPIFlow+\"?refresh=true\"))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\n\t\t\t\tassert.True(t, gjson.GetBytes(body, \"refresh\").Bool())\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier)\").Exists(), \"%s\", body)\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==identifier)\").Exists(), \"%s\", body)\n\t\t\t\tassert.False(t, gjson.GetBytes(body, \"ui.nodes.#(attributes.name==password)\").Exists(), \"%s\", body)\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"case=should return an error because not passing validation and reset previous errors and values\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+login.RouteSubmitFlow, \"%s\", actual)\n\t\t}\n\n\t\tcheckFirst := func(t *testing.T, actual string) {\n\t\t\tcheck(t, actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==identifier).messages.0\").String(), \"Property identifier is missing.\", \"%s\", actual)\n\t\t}\n\n\t\tcheckSecond := func(t *testing.T, actual string) {\n\t\t\tcheck(t, actual)\n\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==identifier).attributes.error\"))\n\t\t\tassert.EqualValues(t, \"identifier\", gjson.Get(actual, \"ui.nodes.#(attributes.name==identifier).attributes.value\").String(), actual)\n\t\t\tassert.EqualValues(t, \"password\", gjson.Get(actual, \"ui.nodes.#(attributes.name==method).attributes.value\").String(), actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.error\"))\n\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==password).messages.0\").String(), \"Property password is missing.\", \"%s\", actual)\n\t\t}\n\n\t\tvaluesFirst := func(v url.Values) url.Values {\n\t\t\tv.Del(\"identifier\")\n\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\treturn v\n\t\t}\n\n\t\tvaluesSecond := func(v url.Values) url.Values {\n\t\t\tv.Set(\"identifier\", \"identifier\")\n\t\t\tv.Del(\"password\")\n\t\t\treturn v\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\t\tactual, _ := testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, valuesFirst(testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes))))\n\t\t\tcheckFirst(t, actual)\n\t\t\tactual, _ = testhelpers.LoginMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, valuesSecond(testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes))))\n\t\t\tcheckSecond(t, actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)\n\n\t\t\tactual, _ := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, valuesFirst(testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)).Encode())\n\t\t\tcheckFirst(t, actual)\n\t\t\tactual, _ = testhelpers.LoginMakeRequest(t, false, false, f, browserClient, valuesSecond(testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)).Encode())\n\t\t\tcheckSecond(t, actual)\n\t\t})\n\t})\n\n\tt.Run(\"should be a new session with refresh flag\", func(t *testing.T) {\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, pwd)\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)\n\n\t\tvalues := url.Values{\n\t\t\t\"method\": {\"password\"}, \"identifier\": {identifier},\n\t\t\t\"password\": {pwd}, \"csrf_token\": {nosurfx.FakeCSRFToken},\n\t\t}.Encode()\n\n\t\tbody1, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\tf = testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, true, false, false, false)\n\t\tbody2, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)\n\n\t\trequire.Contains(t, res.Request.URL.Path, \"return-ts\", \"%s\", res.Request.URL.String())\n\t\tassert.Equal(t, identifier, gjson.Get(body2, \"identity.traits.subject\").String(), \"%s\", body2)\n\t\tassert.Equal(t, gjson.Get(body1, \"id\").String(), gjson.Get(body2, \"id\").String(), \"%s\\n\\n%s\\n\", body1, body2)\n\t})\n\n\tt.Run(\"should login same identity regardless of identifier capitalization\", func(t *testing.T) {\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, pwd)\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)\n\n\t\tvalues := url.Values{\"method\": {\"password\"}, \"identifier\": {strings.ToUpper(identifier)}, \"password\": {pwd}, \"csrf_token\": {nosurfx.FakeCSRFToken}}.Encode()\n\n\t\tbody, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)\n\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.subject\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"should succeed and include redirect continue_with in SPA flow\", func(t *testing.T) {\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, pwd)\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, true, false, false)\n\t\tvalues := url.Values{\"method\": {\"password\"}, \"identifier\": {strings.ToUpper(identifier)}, \"password\": {pwd}, \"csrf_token\": {nosurfx.FakeCSRFToken}}.Encode()\n\t\tbody, res := testhelpers.LoginMakeRequest(t, false, true, f, browserClient, values)\n\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(body, \"continue_with.0.action\").String(), \"%s\", body)\n\t\tassert.EqualValues(t, conf.SelfServiceBrowserDefaultReturnTo(t.Context()).String(), gjson.Get(body, \"continue_with.0.redirect_browser_to\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"should succeed and not have redirect continue_with in api flow\", func(t *testing.T) {\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, pwd)\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\tbody, res := testhelpers.LoginMakeRequest(t, true, true, f, browserClient, fmt.Sprintf(`{\"method\":\"password\",\"identifier\":\"%s\",\"password\":\"%s\"}`, strings.ToUpper(identifier), pwd))\n\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, body)\n\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t})\n\n\tt.Run(\"should login even if old form field name is used\", func(t *testing.T) {\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, pwd)\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)\n\n\t\tvalues := url.Values{\"method\": {\"password\"}, \"password_identifier\": {strings.ToUpper(identifier)}, \"password\": {pwd}, \"csrf_token\": {nosurfx.FakeCSRFToken}}.Encode()\n\n\t\tbody, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)\n\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.subject\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"should login same identity regardless of leading or trailing whitespace\", func(t *testing.T) {\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, pwd)\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false)\n\n\t\tvalues := url.Values{\"method\": {\"password\"}, \"identifier\": {\"  \" + identifier + \"  \"}, \"password\": {pwd}, \"csrf_token\": {nosurfx.FakeCSRFToken}}.Encode()\n\n\t\tbody, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)\n\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.subject\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"should fail as email is not yet verified\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceLoginAfter+\".password.hooks\", []map[string]interface{}{\n\t\t\t{\"hook\": \"require_verified_address\"},\n\t\t})\n\t\tconf.MustSet(t.Context(), config.ViperKeyUseLegacyRequireVerifiedLoginError, true)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyUseLegacyRequireVerifiedLoginError, false)\n\t\t})\n\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(t.Context(), reg, t, identifier, pwd)\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"method\", \"password\")\n\t\t\tv.Set(\"identifier\", identifier)\n\t\t\tv.Set(\"password\", pwd)\n\t\t}\n\n\t\tcheck := func(t *testing.T, body string) {\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.action\").String(), publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\n\t\t\tensureFieldsExist(t, []byte(body))\n\t\t\tassert.Equal(t,\n\t\t\t\terrorsx.Cause(schema.NewAddressNotVerifiedError()).(*schema.ValidationError).Messages[0].Text,\n\t\t\t\tgjson.Get(body, \"ui.messages.0.text\").String(),\n\t\t\t\t\"%s\", body,\n\t\t\t)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, false, values))\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, false, values))\n\t\t})\n\t})\n\n\tt.Run(\"should upgrade password not primary hashing algorithm\", func(t *testing.T) {\n\t\tidentifier, pwd := x.NewUUID().String()+\"@google.com\", \"password\"\n\t\th := &hash.Pbkdf2{\n\t\t\tAlgorithm:  \"sha256\",\n\t\t\tIterations: 100000,\n\t\t\tSaltLength: 32,\n\t\t\tKeyLength:  32,\n\t\t}\n\t\tp, _ := h.Generate(t.Context(), []byte(pwd))\n\n\t\tiId := x.NewUUID()\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(t.Context(), &identity.Identity{\n\t\t\tID:       iId,\n\t\t\tSchemaID: \"migration\",\n\t\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, identifier)),\n\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\tIdentifiers: []string{identifier},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t\t\t},\n\t\t\t},\n\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t{\n\t\t\t\t\tID:         x.NewUUID(),\n\t\t\t\t\tValue:      identifier,\n\t\t\t\t\tVerified:   true,\n\t\t\t\t\tCreatedAt:  time.Now(),\n\t\t\t\t\tIdentityID: iId,\n\t\t\t\t},\n\t\t\t},\n\t\t}))\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"identifier\", identifier)\n\t\t\tv.Set(\"method\", identity.CredentialsTypePassword.String())\n\t\t\tv.Set(\"password\", pwd)\n\t\t}\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\n\t\tbody := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,\n\t\t\tfalse, false, http.StatusOK, redirTS.URL)\n\n\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.email\").String(), \"%s\", body)\n\n\t\t// check if password hash algorithm is upgraded\n\t\t_, c, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePassword, identifier)\n\t\trequire.NoError(t, err)\n\t\tvar o identity.CredentialsPassword\n\t\trequire.NoError(t, json.NewDecoder(bytes.NewBuffer(c.Config)).Decode(&o))\n\t\tassert.True(t, reg.Hasher(t.Context()).Understands([]byte(o.HashedPassword)), \"%s\", o.HashedPassword)\n\t\tassert.True(t, hash.IsBcryptHash([]byte(o.HashedPassword)), \"%s\", o.HashedPassword)\n\n\t\t// retry after upgraded\n\t\tbody = testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,\n\t\t\tfalse, true, http.StatusOK, redirTS.URL)\n\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.email\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"suite=password rehashing degrades gracefully during login\", func(t *testing.T) {\n\t\tidentifier := x.NewUUID().String() + \"@google.com\"\n\t\t// pwd := \"Kd9hUV4Xkcq87VSca6A4fq1iBijrMScBFhkpIPEwBtvTDsBwfqJCqXPPr4TkhOhsd9wFGeB3MzS4bJuesLCAjJc5s1GKJ51zW7F\"\n\t\tpwd := randx.MustString(100, randx.AlphaNum) // longer than bcrypt max length\n\t\trequire.Greater(t, len(pwd), 72)             // bcrypt max length\n\t\tsalt := randx.MustString(32, randx.AlphaNum)\n\t\tsha := sha256.Sum256([]byte(pwd + salt))\n\t\thashed := \"{SSHA256}\" + base64.StdEncoding.EncodeToString(slices.Concat(sha[:], []byte(salt)))\n\t\tiId := x.NewUUID()\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(t.Context(), &identity.Identity{\n\t\t\tID:       iId,\n\t\t\tSchemaID: \"migration\",\n\t\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":%q}`, identifier)),\n\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\tIdentifiers: []string{identifier},\n\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + hashed + `\"}`),\n\t\t\t\t},\n\t\t\t},\n\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t{\n\t\t\t\t\tID:         x.NewUUID(),\n\t\t\t\t\tValue:      identifier,\n\t\t\t\t\tVerified:   true,\n\t\t\t\t\tCreatedAt:  time.Now(),\n\t\t\t\t\tIdentityID: iId,\n\t\t\t\t},\n\t\t\t},\n\t\t}))\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"identifier\", identifier)\n\t\t\tv.Set(\"method\", identity.CredentialsTypePassword.String())\n\t\t\tv.Set(\"password\", pwd)\n\t\t}\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\n\t\tbody := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,\n\t\t\tfalse, false, http.StatusOK, redirTS.URL)\n\n\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.email\").String(), \"%s\", body)\n\n\t\t// check that the password hash algorithm is unchanged\n\t\t_, c, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePassword, identifier)\n\t\trequire.NoError(t, err)\n\t\tvar o identity.CredentialsPassword\n\t\trequire.NoError(t, json.NewDecoder(bytes.NewBuffer(c.Config)).Decode(&o))\n\t\tassert.Equal(t, hashed, o.HashedPassword)\n\n\t\t// login still works\n\t\tbody = testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,\n\t\t\tfalse, true, http.StatusOK, redirTS.URL)\n\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.email\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"suite=password migration hook\", func(t *testing.T) {\n\t\tctx := t.Context()\n\n\t\ttype (\n\t\t\thookPayload = struct {\n\t\t\t\tIdentifier string `json:\"identifier\"`\n\t\t\t\tPassword   string `json:\"password\"`\n\t\t\t}\n\t\t\ttsRequestHandler = func(hookPayload) (status int, body string)\n\t\t)\n\t\treturnStatus := func(status int) func(string, string) tsRequestHandler {\n\t\t\treturn func(string, string) tsRequestHandler {\n\t\t\t\treturn func(hookPayload) (int, string) { return status, \"\" }\n\t\t\t}\n\t\t}\n\t\treturnStatic := func(status int, body string) func(string, string) tsRequestHandler {\n\t\t\treturn func(string, string) tsRequestHandler {\n\t\t\t\treturn func(hookPayload) (int, string) { return status, body }\n\t\t\t}\n\t\t}\n\n\t\t// each test case sends (number of expected calls) handlers to the channel, at a max of 3\n\t\ttsChan := make(chan tsRequestHandler, 3)\n\n\t\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tb, err := io.ReadAll(r.Body)\n\t\t\trequire.NoError(t, err)\n\t\t\t_ = r.Body.Close()\n\t\t\tvar payload hookPayload\n\t\t\trequire.NoError(t, json.Unmarshal(b, &payload))\n\n\t\t\tselect {\n\t\t\tcase handlerFn := <-tsChan:\n\t\t\t\tstatus, body := handlerFn(payload)\n\t\t\t\tw.WriteHeader(status)\n\t\t\t\t_, _ = io.WriteString(w, body)\n\n\t\t\tdefault:\n\t\t\t\tt.Fatal(\"unexpected call to the password migration hook\")\n\t\t\t}\n\t\t}))\n\t\tt.Cleanup(ts.Close)\n\n\t\trequire.NoError(t, reg.Config().Set(ctx, config.ViperKeyPasswordMigrationHook, map[string]any{\n\t\t\t\"config\":  map[string]any{\"url\": ts.URL},\n\t\t\t\"enabled\": true,\n\t\t}))\n\n\t\tfor _, tc := range []struct {\n\t\t\tname              string\n\t\t\thookHandler       func(identifier, password string) tsRequestHandler\n\t\t\texpectHookCalls   int\n\t\t\tsetupFn           func() func()\n\t\t\tcredentialsConfig string\n\t\t\texpectSuccess     bool\n\t\t}{{\n\t\t\tname:              \"should call migration hook\",\n\t\t\tcredentialsConfig: `{\"use_password_migration_hook\": true}`,\n\t\t\thookHandler: func(identifier, password string) tsRequestHandler {\n\t\t\t\treturn func(payload hookPayload) (status int, body string) {\n\t\t\t\t\tif payload.Identifier == identifier && payload.Password == password {\n\t\t\t\t\t\treturn http.StatusOK, `{\"status\":\"password_match\"}`\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn http.StatusOK, `{\"status\":\"no_match\"}`\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\texpectHookCalls: 1,\n\t\t\texpectSuccess:   true,\n\t\t}, {\n\t\t\tname:              \"should not update identity when the password is wrong\",\n\t\t\tcredentialsConfig: `{\"use_password_migration_hook\": true}`,\n\t\t\thookHandler:       returnStatus(http.StatusForbidden),\n\t\t\texpectHookCalls:   1,\n\t\t\texpectSuccess:     false,\n\t\t}, {\n\t\t\tname:              \"should inspect response\",\n\t\t\tcredentialsConfig: `{\"use_password_migration_hook\": true}`,\n\t\t\thookHandler:       returnStatic(http.StatusOK, `{\"status\":\"password_no_match\"}`),\n\t\t\texpectHookCalls:   1,\n\t\t\texpectSuccess:     false,\n\t\t}, {\n\t\t\tname:              \"should not update identity when the migration hook returns 200 without JSON\",\n\t\t\tcredentialsConfig: `{\"use_password_migration_hook\": true}`,\n\t\t\thookHandler:       returnStatus(http.StatusOK),\n\t\t\texpectHookCalls:   1,\n\t\t\texpectSuccess:     false,\n\t\t}, {\n\t\t\tname:              \"should not update identity when the migration hook returns 500\",\n\t\t\tcredentialsConfig: `{\"use_password_migration_hook\": true}`,\n\t\t\thookHandler:       returnStatus(http.StatusInternalServerError),\n\t\t\texpectHookCalls:   3, // expect retries on 500\n\t\t\texpectSuccess:     false,\n\t\t}, {\n\t\t\tname:              \"should not update identity when the migration hook returns 201\",\n\t\t\tcredentialsConfig: `{\"use_password_migration_hook\": true}`,\n\t\t\thookHandler:       returnStatic(http.StatusCreated, `{\"status\":\"password_match\"}`),\n\t\t\texpectHookCalls:   1,\n\t\t\texpectSuccess:     false,\n\t\t}, {\n\t\t\tname:              \"should not update identity and not call hook when hash is set\",\n\t\t\tcredentialsConfig: `{\"use_password_migration_hook\": true, \"hashed_password\":\"hash\"}`,\n\t\t\texpectSuccess:     false,\n\t\t}, {\n\t\t\tname:              \"should not update identity and not call hook when use_password_migration_hook is not set\",\n\t\t\tcredentialsConfig: `{\"hashed_password\":\"hash\"}`,\n\t\t\texpectSuccess:     false,\n\t\t}, {\n\t\t\tname:              \"should not update identity and not call hook when credential is empty\",\n\t\t\tcredentialsConfig: `{}`,\n\t\t\texpectSuccess:     false,\n\t\t}, {\n\t\t\tname:              \"should not call migration hook if disabled\",\n\t\t\tcredentialsConfig: `{\"use_password_migration_hook\": true}`,\n\t\t\tsetupFn: func() func() {\n\t\t\t\trequire.NoError(t, reg.Config().Set(ctx, config.ViperKeyPasswordMigrationHook+\".enabled\", false))\n\t\t\t\treturn func() {\n\t\t\t\t\trequire.NoError(t, reg.Config().Set(ctx, config.ViperKeyPasswordMigrationHook+\".enabled\", true))\n\t\t\t\t}\n\t\t\t},\n\t\t\texpectSuccess: false,\n\t\t}} {\n\t\t\tt.Run(\"case=\"+tc.name, func(t *testing.T) {\n\t\t\t\tif tc.setupFn != nil {\n\t\t\t\t\tcleanup := tc.setupFn()\n\t\t\t\t\tt.Cleanup(cleanup)\n\t\t\t\t}\n\n\t\t\t\tidentifier := x.NewUUID().String() + \"@google.com\"\n\t\t\t\tpassword := x.NewUUID().String()\n\t\t\t\tiId := x.NewUUID()\n\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, &identity.Identity{\n\t\t\t\t\tID:       iId,\n\t\t\t\t\tSchemaID: \"migration\",\n\t\t\t\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, identifier)),\n\t\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\t\tIdentifiers: []string{identifier},\n\t\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(tc.credentialsConfig),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tID:         x.NewUUID(),\n\t\t\t\t\t\t\tValue:      identifier,\n\t\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\t\tCreatedAt:  time.Now(),\n\t\t\t\t\t\t\tIdentityID: iId,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}))\n\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"identifier\", identifier)\n\t\t\t\t\tv.Set(\"method\", identity.CredentialsTypePassword.String())\n\t\t\t\t\tv.Set(\"password\", password)\n\t\t\t\t}\n\n\t\t\t\tfor range tc.expectHookCalls {\n\t\t\t\t\ttsChan <- tc.hookHandler(identifier, password)\n\t\t\t\t}\n\n\t\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\n\t\t\t\tif tc.expectSuccess {\n\t\t\t\t\tbody := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,\n\t\t\t\t\t\tfalse, false, http.StatusOK, redirTS.URL)\n\t\t\t\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.email\").String(), \"%s\", body)\n\n\t\t\t\t\t// check if password hash algorithm is upgraded\n\t\t\t\t\t_, c, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, identity.CredentialsTypePassword, identifier)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tvar o identity.CredentialsPassword\n\t\t\t\t\trequire.NoError(t, json.NewDecoder(bytes.NewBuffer(c.Config)).Decode(&o))\n\t\t\t\t\tassert.True(t, reg.Hasher(ctx).Understands([]byte(o.HashedPassword)), \"%s\", o.HashedPassword)\n\t\t\t\t\tassert.True(t, hash.IsBcryptHash([]byte(o.HashedPassword)), \"%s\", o.HashedPassword)\n\n\t\t\t\t\t// retry after upgraded\n\t\t\t\t\tbody = testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,\n\t\t\t\t\t\tfalse, true, http.StatusOK, redirTS.URL)\n\t\t\t\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.email\").String(), \"%s\", body)\n\t\t\t\t} else {\n\t\t\t\t\tbody := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,\n\t\t\t\t\t\tfalse, false, http.StatusOK, \"\")\n\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"identity.traits.subject\").String(), \"%s\", body)\n\t\t\t\t\t// Check that the config did not change\n\t\t\t\t\t_, c, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePassword, identifier)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.JSONEq(t, tc.credentialsConfig, string(c.Config))\n\t\t\t\t}\n\n\t\t\t\t// expect all hook calls to be done\n\t\t\t\tselect {\n\t\t\t\tcase <-tsChan:\n\t\t\t\t\tt.Fatal(\"the test unexpectedly did too few calls to the password hook\")\n\t\t\t\tdefault:\n\t\t\t\t\t// pass\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"case=custom hook payload\", func(t *testing.T) {\n\t\t\tvar rawBody []byte\n\t\t\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\tvar err error\n\t\t\t\trawBody, err = io.ReadAll(r.Body)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\t_ = r.Body.Close()\n\n\t\t\t\tw.WriteHeader(http.StatusOK)\n\t\t\t\t_, _ = w.Write([]byte(`{\"status\":\"password_match\"}`))\n\t\t\t}))\n\n\t\t\tt.Cleanup(ts.Close)\n\t\t\trequire.NoError(t, reg.Config().Set(ctx, config.ViperKeyPasswordMigrationHook, map[string]any{\n\t\t\t\t\"config\": map[string]any{\n\t\t\t\t\t\"url\":  ts.URL,\n\t\t\t\t\t\"body\": \"base64://\" + base64.StdEncoding.EncodeToString([]byte(`function(ctx) ctx`)),\n\t\t\t\t},\n\t\t\t}))\n\n\t\t\tidentifier := x.NewUUID().String() + \"@google.com\"\n\t\t\tidentityID := x.NewUUID()\n\t\t\tvalues := func(v url.Values) {\n\t\t\t\tv.Set(\"identifier\", identifier)\n\t\t\t\tv.Set(\"method\", identity.CredentialsTypePassword.String())\n\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t}\n\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, &identity.Identity{\n\t\t\t\tID:       identityID,\n\t\t\t\tSchemaID: \"migration\",\n\t\t\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"email\":\"%s\"}`, identifier)),\n\t\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\t\t\tIdentifiers: []string{identifier},\n\t\t\t\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"use_password_migration_hook\": true}`),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t\t\t{\n\t\t\t\t\t\tID:         x.NewUUID(),\n\t\t\t\t\t\tValue:      identifier,\n\t\t\t\t\t\tVerified:   true,\n\t\t\t\t\t\tIdentityID: identityID,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}))\n\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tbody := testhelpers.SubmitLoginForm(t, false, browserClient, publicTS, values,\n\t\t\t\tfalse, false, http.StatusOK, redirTS.URL)\n\t\t\tassert.Equalf(t, identifier, gjson.Get(body, \"identity.traits.email\").String(), \"%s\", body)\n\n\t\t\tfor _, path := range []string{\n\t\t\t\t\"identifier\", \"password\",\n\t\t\t\t\"identity\", \"identity.traits\",\n\t\t\t\t\"flow\", \"flow.id\",\n\t\t\t\t\"request_headers\", \"request_cookies\", \"request_method\", \"request_url\",\n\t\t\t} {\n\t\t\t\tassert.Truef(t, gjson.GetBytes(rawBody, path).Exists(), \"%s does not exist in %s\", path, rawBody)\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc TestFormHydration(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword)+\".enabled\", true),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://stub/login.schema.json\")),\n\t)\n\n\ts, err := reg.AllLoginStrategies().Strategy(identity.CredentialsTypePassword)\n\trequire.NoError(t, err)\n\tfh, ok := s.(login.AAL1FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f *login.Flow) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.UI.Nodes.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f.UI.Nodes)\n\t}\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *login.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := login.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactor\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactor(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactorRefresh\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\tid := createIdentity(t.Context(), reg, t, \"some@user.com\", \"password\")\n\t\tr.Header = testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\tf.Refresh = true\n\t\trequire.NoError(t, fh.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstCredentials\", func(t *testing.T) {\n\t\tt.Run(\"case=no options\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, false)\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, true)\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=WithIdentifier\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, false)\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, true)\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=WithIdentityHint\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation enabled and identity has no password\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, true)\n\n\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\ttoSnapshot(t, f)\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeySecurityAccountEnumerationMitigate, false)\n\n\t\t\t\tt.Run(\"case=identity has password\", func(t *testing.T) {\n\t\t\t\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\t\t\t\tid := createIdentity(ctx, reg, t, identifier, pwd)\n\n\t\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\t\trequire.NoError(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)))\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=identity does not have a password\", func(t *testing.T) {\n\t\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\t\tr, f := newFlow(ctx, t)\n\t\t\t\t\trequire.ErrorIs(t, fh.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstIdentification\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateLoginMethodIdentifierFirstIdentification(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/password/nodes.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport (\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc NewPasswordNode(name string, autocomplete node.UiNodeInputAttributeAutocomplete) *node.Node {\n\treturn node.NewInputField(name, nil, node.PasswordGroup,\n\t\tnode.InputAttributeTypePassword,\n\t\tnode.WithRequiredInputAttribute,\n\t\tnode.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\ta.Autocomplete = autocomplete\n\t\t})).\n\t\tWithMetaLabel(text.NewInfoNodeInputPassword())\n}\n"
  },
  {
    "path": "selfservice/strategy/password/op_helpers_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/phayes/freeport\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/moby/moby/api/types/container\"\n\t\"github.com/moby/moby/api/types/network\"\n\n\tdockertest \"github.com/ory/dockertest/v4\"\n\thydraclientgo \"github.com/ory/hydra-client-go/v2\"\n\t\"github.com/ory/x/urlx\"\n)\n\ntype clientAppConfig struct {\n\tclient      *oauth2.Config\n\texpectToken bool\n\tstate       *clientAppState\n}\n\ntype clientAppState struct {\n\tvisits int64\n\ttokens int64\n}\n\ntype callTrace string\n\nconst (\n\tRegistrationUI                       callTrace = \"registration-ui\"\n\tRegistrationWithOAuth2LoginChallenge callTrace = \"registration-with-oauth2-login-challenge\"\n\tRegistrationWithFlowID               callTrace = \"registration-with-flow-id\"\n\tLoginUI                              callTrace = \"login-ui\"\n\tLoginWithOAuth2LoginChallenge        callTrace = \"login-with-oauth2-login-challenge\"\n\tLoginWithFlowID                      callTrace = \"login-with-flow-id\"\n\tConsent                              callTrace = \"consent\"\n\tConsentWithChallenge                 callTrace = \"consent-with-challenge\"\n\tConsentAccept                        callTrace = \"consent-accept\"\n\tConsentSkip                          callTrace = \"consent-skip\"\n\tConsentClientSkip                    callTrace = \"consent-client-skip\"\n\tCodeExchange                         callTrace = \"code-exchange\"\n\tCodeExchangeWithToken                callTrace = \"code-exchange-with-token\"\n)\n\ntype testContextKey string\n\nconst (\n\tTestUIConfig         testContextKey = \"test-ui-config\"\n\tTestOAuthClientState testContextKey = \"test-oauth-client-state\"\n)\n\ntype testConfig struct {\n\tidentifier       string\n\tpassword         string\n\tbrowserClient    *http.Client\n\tkratosPublicTS   *httptest.Server\n\tclientAppTS      *httptest.Server\n\thydraAdminClient hydraclientgo.OAuth2API\n\tconsentRemember  bool\n\trequestedScope   []string\n\tcallTrace        *[]callTrace\n}\n\nfunc createHydraOAuth2ApiClient(url string) hydraclientgo.OAuth2API {\n\tconfiguration := hydraclientgo.NewConfiguration()\n\tconfiguration.Host = urlx.ParseOrPanic(url).Host\n\tconfiguration.Servers = hydraclientgo.ServerConfigurations{{URL: url}}\n\n\treturn hydraclientgo.NewAPIClient(configuration).OAuth2API\n}\n\nfunc createOAuth2Client(t *testing.T, ctx context.Context, hydraAdmin hydraclientgo.OAuth2API, redirectURIs []string, scope string, skipConsent bool) string {\n\tt.Helper()\n\n\tclientName := \"kratos-hydra-integration-test-client-1\"\n\ttokenEndpointAuthMethod := \"client_secret_post\"\n\tclientSecret := \"client-secret\"\n\n\tc, r, err := hydraAdmin.CreateOAuth2Client(ctx).OAuth2Client(\n\t\thydraclientgo.OAuth2Client{\n\t\t\tClientName:              &clientName,\n\t\t\tRedirectUris:            redirectURIs,\n\t\t\tScope:                   &scope,\n\t\t\tTokenEndpointAuthMethod: &tokenEndpointAuthMethod,\n\t\t\tClientSecret:            &clientSecret,\n\t\t\tSkipConsent:             &skipConsent,\n\t\t},\n\t).Execute()\n\trequire.NoError(t, err)\n\trequire.Equal(t, r.StatusCode, http.StatusCreated)\n\treturn *c.ClientId\n}\n\nfunc makeAuthCodeURL(t *testing.T, c *oauth2.Config, requestedClaims string, isForced bool) string {\n\tt.Helper()\n\n\tvar options []oauth2.AuthCodeOption\n\n\tif isForced {\n\t\toptions = append(options, oauth2.SetAuthURLParam(\"prompt\", \"login\"))\n\t}\n\tif requestedClaims != \"\" {\n\t\toptions = append(options, oauth2.SetAuthURLParam(\"claims\", requestedClaims))\n\t}\n\n\tstate := fmt.Sprintf(\"%x\", uuid.Must(uuid.NewV4()))\n\treturn c.AuthCodeURL(state, options...)\n}\n\nfunc newHydra(t *testing.T, loginUI string, consentUI string) (hydraAdmin string, hydraPublic string) {\n\tpublicPort, err := freeport.GetFreePort()\n\trequire.NoError(t, err)\n\tadminPort, err := freeport.GetFreePort()\n\trequire.NoError(t, err)\n\n\tpool := dockertest.NewPoolT(t, \"\")\n\n\thydraResource := pool.RunT(t, \"oryd/hydra\",\n\t\t// Keep tag in sync with the version in ci.yaml\n\t\tdockertest.WithTag(\"v2.2.0\"),\n\t\tdockertest.WithoutReuse(),\n\t\tdockertest.WithEnv([]string{\n\t\t\t\"DSN=memory\",\n\t\t\tfmt.Sprintf(\"URLS_SELF_ISSUER=http://127.0.0.1:%d/\", publicPort),\n\t\t\t\"URLS_LOGIN=\" + loginUI,\n\t\t\t\"URLS_CONSENT=\" + consentUI,\n\t\t\t\"LOG_LEAK_SENSITIVE_VALUES=true\",\n\t\t\t\"SECRETS_SYSTEM=someverylongsecretthatis32byteslong\",\n\t\t}),\n\t\tdockertest.WithCmd([]string{\"serve\", \"all\", \"--dev\"}),\n\t\tdockertest.WithContainerConfig(func(cc *container.Config) {\n\t\t\tcc.ExposedPorts = network.PortSet{\n\t\t\t\tnetwork.MustParsePort(\"4444/tcp\"): struct{}{},\n\t\t\t\tnetwork.MustParsePort(\"4445/tcp\"): struct{}{},\n\t\t\t}\n\t\t}),\n\t\tdockertest.WithHostConfig(func(hc *container.HostConfig) {\n\t\t\thc.PortBindings = network.PortMap{\n\t\t\t\tnetwork.MustParsePort(\"4444/tcp\"): {{HostPort: strconv.Itoa(publicPort)}},\n\t\t\t\tnetwork.MustParsePort(\"4445/tcp\"): {{HostPort: strconv.Itoa(adminPort)}},\n\t\t\t}\n\t\t}),\n\t)\n\trequire.NotEmpty(t, hydraResource.GetPort(\"4444/tcp\"), \"%+v\", hydraResource.Container().NetworkSettings.Ports)\n\trequire.NotEmpty(t, hydraResource.GetPort(\"4445/tcp\"), \"%+v\", hydraResource.Container)\n\n\thydraPublic = \"http://127.0.0.1:\" + hydraResource.GetPort(\"4444/tcp\")\n\thydraAdmin = \"http://127.0.0.1:\" + hydraResource.GetPort(\"4445/tcp\")\n\n\tgo func() {\n\t\tstdout, stderr, err := hydraResource.Logs(context.Background())\n\t\tif err == nil {\n\t\t\tif stdout != \"\" {\n\t\t\t\tt.Logf(\"[hydra stdout]:\\n%s\\n\", stdout)\n\t\t\t}\n\t\t\tif stderr != \"\" {\n\t\t\t\tt.Logf(\"[hydra stderr]:\\n%s\\n\", stderr)\n\t\t\t}\n\t\t}\n\t}()\n\trequire.EventuallyWithT(t, func(t *assert.CollectT) {\n\t\tres, err := http.DefaultClient.Get(hydraPublic + \"/health/ready\")\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 200, res.StatusCode)\n\n\t\tres, err = http.DefaultClient.Get(hydraAdmin + \"/health/ready\")\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 200, res.StatusCode)\n\t}, 30*time.Second, time.Second)\n\n\tt.Logf(\"Ory Hydra running at: %s %s\", hydraPublic, hydraAdmin)\n\n\treturn hydraAdmin, hydraPublic\n}\n\ntype TestLogWriter struct {\n\tstreamName string\n\t*testing.T\n}\n\nfunc (t TestLogWriter) Write(p []byte) (int, error) {\n\tt.Logf(\"[%d bytes @ %s]:\\n\\n%s\\n\", len(p), t.streamName, string(p))\n\treturn len(p), nil\n}\n\nfunc doOAuthFlow(t *testing.T, ctx context.Context, oauthClient *oauth2.Config, browserClient *http.Client) {\n\tt.Helper()\n\n\tauthCodeURL := makeAuthCodeURL(t, oauthClient, \"\", false)\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, authCodeURL, nil)\n\trequire.NoError(t, err)\n\tres, err := browserClient.Do(req)\n\trequire.NoError(t, err)\n\n\tbody, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err)\n\n\trequire.NoError(t, res.Body.Close())\n\trequire.Equal(t, \"\", string(body))\n\trequire.Equal(t, http.StatusOK, res.StatusCode)\n}\n"
  },
  {
    "path": "selfservice/strategy/password/op_login_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/urfave/negroni\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\thydraclientgo \"github.com/ory/hydra-client-go/v2\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hydra\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httprouterx\"\n)\n\nfunc TestOAuth2Provider(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, true)),\n\t)\n\n\tvar testRequireLogin atomic.Bool\n\ttestRequireLogin.Store(true)\n\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tkratosPublicTS, _ := testhelpers.NewKratosServerWithRouters(t, reg, router, httprouterx.NewTestRouterAdminWithPrefix(t))\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\n\trouter.Handler(\"GET\", \"/login-ts\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tt.Log(\"[loginTS] navigated to the login ui\")\n\t\tc := r.Context().Value(TestUIConfig).(*testConfig)\n\t\t*c.callTrace = append(*c.callTrace, LoginUI)\n\n\t\tq := r.URL.Query()\n\t\thlc := r.URL.Query().Get(\"login_challenge\")\n\t\tif hlc != \"\" {\n\t\t\t*c.callTrace = append(*c.callTrace, LoginWithOAuth2LoginChallenge)\n\n\t\t\tloginUrl, err := url.Parse(c.kratosPublicTS.URL + login.RouteInitBrowserFlow)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tq := loginUrl.Query()\n\t\t\tq.Set(\"login_challenge\", hlc)\n\t\t\tloginUrl.RawQuery = q.Encode()\n\n\t\t\treq, err := http.NewRequest(\"GET\", loginUrl.String(), nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tresp, err := c.browserClient.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusOK, resp.StatusCode)\n\t\t\trequire.NoError(t, resp.Body.Close())\n\n\t\t\t// if the registration page redirects us to the login page\n\t\t\t// we will sign in which means we might have a session\n\t\t\tvar oryCookie *http.Cookie\n\t\t\tcurrentURL, err := url.Parse(kratosPublicTS.URL)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tfor _, c := range c.browserClient.Jar.Cookies(currentURL) {\n\t\t\t\tif c.Name == config.DefaultSessionCookieName {\n\t\t\t\t\toryCookie = c\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// in some cases the initialize login flow already navigated to the consent page\n\t\t\t// in which case no flow exists, but a session cookie does\n\t\t\tif oryCookie != nil && !resp.Request.URL.Query().Has(\"flow\") {\n\t\t\t\tt.Logf(\"[loginTS] found a session cookie: %s\", oryCookie.String())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tflowID := resp.Request.URL.Query().Get(\"flow\")\n\t\t\tlf := testhelpers.GetLoginFlow(t, c.browserClient, c.kratosPublicTS, flowID)\n\t\t\trequire.NotNil(t, lf)\n\n\t\t\tvalues := url.Values{\"method\": {\"password\"}, \"identifier\": {c.identifier}, \"password\": {c.password}, \"csrf_token\": {nosurfx.FakeCSRFToken}}.Encode()\n\t\t\t_, res := testhelpers.LoginMakeRequest(t, false, false, lf, c.browserClient, values)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\treturn\n\t\t}\n\n\t\tif q.Has(\"flow\") {\n\t\t\t*c.callTrace = append(*c.callTrace, LoginWithFlowID)\n\t\t\tt.Log(\"[loginTS] login flow is ignored here since it will be handled by the code above, we just need to return\")\n\t\t\treturn\n\t\t}\n\t}))\n\n\trouter.Handler(\"GET\", \"/consent\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tt.Log(\"[consentTS] navigated to the consent ui\")\n\t\tc := r.Context().Value(TestUIConfig).(*testConfig)\n\t\t*c.callTrace = append(*c.callTrace, Consent)\n\n\t\tq := r.URL.Query()\n\t\tconsentChallenge := q.Get(\"consent_challenge\")\n\t\tassert.NotEmpty(t, consentChallenge)\n\n\t\tif consentChallenge != \"\" {\n\t\t\t*c.callTrace = append(*c.callTrace, ConsentWithChallenge)\n\t\t}\n\n\t\tcr, resp, err := c.hydraAdminClient.GetOAuth2ConsentRequest(ctx).ConsentChallenge(q.Get(\"consent_challenge\")).Execute()\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\t\tassert.ElementsMatch(t, cr.RequestedScope, c.requestedScope)\n\n\t\tif cr.GetSkip() {\n\t\t\t*c.callTrace = append(*c.callTrace, ConsentSkip)\n\t\t}\n\n\t\tif cr.Client.GetSkipConsent() {\n\t\t\t*c.callTrace = append(*c.callTrace, ConsentClientSkip)\n\t\t}\n\n\t\tcompletedAcceptRequest, resp, err := c.hydraAdminClient.AcceptOAuth2ConsentRequest(r.Context()).AcceptOAuth2ConsentRequest(hydraclientgo.AcceptOAuth2ConsentRequest{\n\t\t\tRemember:   &c.consentRemember,\n\t\t\tGrantScope: c.requestedScope,\n\t\t}).ConsentChallenge(q.Get(\"consent_challenge\")).Execute()\n\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\n\t\trequire.NotNil(t, completedAcceptRequest)\n\t\t*c.callTrace = append(*c.callTrace, ConsentAccept)\n\n\t\tt.Logf(\"[consentTS] navigating to %s\", completedAcceptRequest.RedirectTo)\n\t\tresp, err = c.browserClient.Get(completedAcceptRequest.RedirectTo)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, c.clientAppTS.URL, fmt.Sprintf(\"%s://%s\", resp.Request.URL.Scheme, resp.Request.URL.Host))\n\t}))\n\n\tkratosUIMiddleware := negroni.New()\n\tkratosUIMiddleware.UseFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\t// add the context from the global context to each request\n\t\tnext(rw, r.WithContext(ctx))\n\t})\n\tkratosUIMiddleware.UseHandler(router)\n\n\tkratosUITS := testhelpers.NewHTTPTestServer(t, kratosUIMiddleware)\n\n\tclientAppTSMiddleware := negroni.New()\n\tclientAppTSMiddleware.UseFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\t// add the context from the global context to each request\n\t\tnext(rw, r.WithContext(ctx))\n\t})\n\tclientAppTSMiddleware.UseHandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tc := ctx.Value(TestOAuthClientState).(*clientAppConfig)\n\t\tkc := ctx.Value(TestUIConfig).(*testConfig)\n\t\t*kc.callTrace = append(*kc.callTrace, CodeExchange)\n\n\t\tc.state.visits += 1\n\t\tt.Logf(\"[clientAppTS] handling a callback at client app %s\", r.URL.String())\n\t\tif r.URL.Query().Has(\"code\") {\n\t\t\ttoken, err := c.client.Exchange(r.Context(), r.URL.Query().Get(\"code\"))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tif token != nil && token.AccessToken != \"\" {\n\t\t\t\tt.Log(\"[clientAppTS] successfully exchanged code for token\")\n\t\t\t\t*kc.callTrace = append(*kc.callTrace, CodeExchangeWithToken)\n\t\t\t\tc.state.tokens += 1\n\t\t\t} else {\n\t\t\t\tt.Log(\"[clientAppTS] did not receive a token\")\n\t\t\t}\n\t\t} else {\n\t\t\tt.Error(\"[clientAppTS] code query parameter is missing\")\n\t\t}\n\t})\n\t// A new OAuth client which will also function as the callback for the code exchange\n\tclientAppTS := testhelpers.NewHTTPTestServer(t, clientAppTSMiddleware)\n\n\tconf.MustSet(ctx, config.ViperKeySecretsDefault, []string{\"not-a-secure-session-key\"})\n\tconf.MustSet(ctx, config.ViperKeySelfServiceErrorUI, errTS.URL+\"/error-ts\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginUI, kratosUITS.URL+\"/login-ts\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, redirTS.URL+\"/return-ts\")\n\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, true)\n\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://stub/login.schema.json\")\n\n\thydraAdmin, hydraPublic := newHydra(t, kratosUITS.URL+\"/login-ts\", kratosUITS.URL+\"/consent\")\n\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, hydraAdmin)\n\n\thydraAdminClient := createHydraOAuth2ApiClient(hydraAdmin)\n\n\tloginToAccount := func(t *testing.T, browserClient *http.Client, identifier, pwd string) {\n\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, kratosPublicTS, false, false, false, false)\n\n\t\tvalues := url.Values{\"method\": {\"password\"}, \"identifier\": {identifier}, \"password\": {pwd}, \"csrf_token\": {nosurfx.FakeCSRFToken}}.Encode()\n\n\t\tbody, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, values)\n\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\tassert.Equal(t, identifier, gjson.Get(body, \"identity.traits.subject\").String(), \"%s\", body)\n\t}\n\n\tt.Run(\"should prompt the user for login and consent\", func(t *testing.T) {\n\t\t// This is the default case, where the user is prompted for login and consent.\n\n\t\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, false)\n\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, true)\n\t\t})\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(ctx, reg, t, identifier, pwd)\n\n\t\tscopes := []string{\"profile\", \"email\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\tstate:       &clientAS,\n\t\t\texpectToken: true,\n\t\t})\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\tcallTrace:        &ct,\n\t\t\trequestedScope:   scopes,\n\t\t\tconsentRemember:  true,\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         pwd,\n\t\t}\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\texpected := []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\t\trequire.ElementsMatch(t, expected, ct)\n\t})\n\n\tt.Run(\"should prompt the user for login and consent again\", func(t *testing.T) {\n\t\t// This test verifies that when Kratos is set\n\t\t// to SessionPersistentCookie=false, the user is not\n\t\t// remembered from the previous OAuth2 flow.\n\t\t// The user must then re-authenticate and re-consent.\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(ctx, reg, t, identifier, pwd)\n\n\t\tscopes := []string{\"profile\", \"email\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\tstate:       &clientAS,\n\t\t\texpectToken: true,\n\t\t})\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\tpassword:         pwd,\n\t\t\tidentifier:       identifier,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\tcallTrace:        &ct,\n\t\t\trequestedScope:   scopes,\n\t\t\tconsentRemember:  false,\n\t\t}\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\n\t\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, false)\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\texpected := []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\t\trequire.ElementsMatch(t, expected, ct)\n\n\t\t// Reset the call trace\n\t\tct = []callTrace{}\n\n\t\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, true)\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 2,\n\t\t\ttokens: 2,\n\t\t}, clientAS)\n\n\t\texpected = []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\t\trequire.ElementsMatch(t, expected, ct)\n\t})\n\n\tt.Run(\"should prompt the user for consent, but not for login\", func(t *testing.T) {\n\t\t// This test verifies that when Kratos is set\n\t\t// to SessionPersistentCookie=true, the user is\n\t\t// remembered from the previous OAuth2 flow.\n\t\t// The user must then only re-consent.\n\t\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, true)\n\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\n\t\tcreateIdentity(ctx, reg, t, identifier, pwd)\n\n\t\tscopes := []string{\"profile\", \"email\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\tstate:       &clientAS,\n\t\t\texpectToken: true,\n\t\t})\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\tcallTrace:        &ct,\n\t\t\trequestedScope:   scopes,\n\t\t\tconsentRemember:  false,\n\t\t\tpassword:         pwd,\n\t\t\tidentifier:       identifier,\n\t\t}\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\texpected := []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\t\trequire.ElementsMatch(t, expected, ct)\n\n\t\t// reset the call trace\n\t\tct = []callTrace{}\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 2,\n\t\t\ttokens: 2,\n\t\t}, clientAS)\n\n\t\texpected = []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\n\t\trequire.ElementsMatch(t, expected, ct)\n\t})\n\n\tt.Run(\"should prompt login even with session with OAuth flow\", func(t *testing.T) {\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(ctx, reg, t, identifier, pwd)\n\n\t\tscopes := []string{\"profile\", \"email\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\tcallTrace:        &ct,\n\t\t\trequestedScope:   scopes,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         pwd,\n\t\t\tconsentRemember:  false,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\tstate:       &clientAS,\n\t\t\texpectToken: true,\n\t\t})\n\n\t\tloginToAccount(t, browserClient, identifier, pwd)\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\trequire.ElementsMatch(t, []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}, ct)\n\t})\n\n\tt.Run(\"first party clients can skip consent\", func(t *testing.T) {\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(ctx, reg, t, identifier, pwd)\n\n\t\tclientSkipConsent := true\n\t\tscopes := []string{\"profile\", \"email\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), clientSkipConsent)\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\tcallTrace:        &ct,\n\t\t\trequestedScope:   scopes,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         pwd,\n\t\t\tconsentRemember:  false,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\tstate:       &clientAS,\n\t\t\texpectToken: true,\n\t\t})\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\trequire.ElementsMatch(t, []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentClientSkip,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}, ct)\n\t})\n\n\tt.Run(\"oauth flow with consent remember, skips consent\", func(t *testing.T) {\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(ctx, reg, t, identifier, pwd)\n\n\t\tscopes := []string{\"profile\", \"email\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\tcallTrace:        &ct,\n\t\t\trequestedScope:   scopes,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         pwd,\n\t\t\tconsentRemember:  true,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\tstate:       &clientAS,\n\t\t\texpectToken: true,\n\t\t})\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\trequire.ElementsMatch(t, []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}, ct)\n\n\t\t// reset the call trace\n\t\tct = []callTrace{}\n\t\tclientAS = clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\trequire.ElementsMatch(t, []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentSkip,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}, ct)\n\t})\n\n\tt.Run(\"should fail when Hydra session subject doesn't match the subject authenticated by Kratos\", func(t *testing.T) {\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\tidentifier, pwd := x.NewUUID().String(), \"password\"\n\t\tcreateIdentity(ctx, reg, t, identifier, pwd)\n\n\t\tscopes := []string{\"profile\", \"email\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\tcallTrace:        &ct,\n\t\t\trequestedScope:   scopes,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         pwd,\n\t\t\tconsentRemember:  false,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\tstate:       &clientAS,\n\t\t\texpectToken: false,\n\t\t})\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\trequire.ElementsMatch(t, []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}, ct)\n\n\t\t// reset the call trace\n\t\tct = []callTrace{}\n\t\tclientAS = clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\treg.SetHydra(&AcceptWrongSubject{h: reg.Hydra().(*hydra.DefaultHydra)})\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\t// In Hydra v2.2.0-rc.3 this would have failed outright: Hydra returned an error\n\t\t// when AcceptLoginRequest arrived with a subject that did not match the subject\n\t\t// stored in the existing login session (skip=true flow).\n\t\t//\n\t\t// In Hydra v2.2.0 final the mismatch is handled gracefully instead of with an\n\t\t// error. When Hydra detects that the accepted subject differs from the session\n\t\t// subject (consent/handler.go, acceptOAuth2LoginRequest), it redirects the\n\t\t// browser back to the original authorization URL with prompt=login appended.\n\t\t// That restart produces a fresh flow with f.Subject=\"\" (no prior session\n\t\t// context), so the mismatch guard (f.Subject != \"\" && payload.Subject !=\n\t\t// f.Subject) does not fire on the second attempt — and AcceptWrongSubject's\n\t\t// random UUID is accepted, eventually completing the flow.\n\t\t//\n\t\t// Security note: this is not exploitable in production. AcceptLoginRequest is a\n\t\t// trusted admin API reachable only by the login provider (Kratos), which always\n\t\t// sends the correctly authenticated identity ID. AcceptWrongSubject is a test\n\t\t// construct only. The change removes a hard early-failure signal for accidental\n\t\t// subject substitution but does not introduce a new attack surface.\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\texpected := []callTrace{\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge, // skip=true, wrong subject → prompt=login redirect\n\t\t\tLoginUI,\n\t\t\tLoginWithOAuth2LoginChallenge, // skip=false, f.Subject=\"\" → guard bypassed, wrong subject accepted\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\t\trequire.ElementsMatch(t, expected, ct)\n\t})\n}\n\nvar _ hydra.Hydra = &AcceptWrongSubject{}\n\ntype AcceptWrongSubject struct {\n\th *hydra.DefaultHydra\n}\n\nfunc (h *AcceptWrongSubject) AcceptLoginRequest(ctx context.Context, params hydra.AcceptLoginRequestParams) (string, error) {\n\tparams.IdentityID = uuid.Must(uuid.NewV4()).String()\n\treturn h.h.AcceptLoginRequest(ctx, params)\n}\n\nfunc (h *AcceptWrongSubject) GetLoginRequest(ctx context.Context, loginChallenge string) (*hydraclientgo.OAuth2LoginRequest, error) {\n\treturn h.h.GetLoginRequest(ctx, loginChallenge)\n}\n"
  },
  {
    "path": "selfservice/strategy/password/op_registration_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/urfave/negroni\"\n\t\"golang.org/x/oauth2\"\n\n\thydraclientgo \"github.com/ory/hydra-client-go/v2\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/configx\"\n)\n\nfunc TestOAuth2ProviderRegistration(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValue(config.ViperKeySelfServiceRegistrationEnableLegacyOneStep, true))\n\n\tkratosPublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\n\tvar hydraAdminClient hydraclientgo.OAuth2API\n\n\trouter := http.NewServeMux()\n\n\ttype contextKey string\n\tconst (\n\t\tTestUIConfig         contextKey = \"test-ui-config\"\n\t\tTestOAuthClientState contextKey = \"test-oauth-client-state\"\n\t)\n\n\trouter.HandleFunc(\"GET /login-ts\", func(w http.ResponseWriter, r *http.Request) {\n\t\tt.Log(\"[loginTS] navigated to the login ui\")\n\t\tc := r.Context().Value(TestUIConfig).(*testConfig)\n\t\t*c.callTrace = append(*c.callTrace, LoginUI)\n\n\t\tq := r.URL.Query()\n\t\thlc := r.URL.Query().Get(\"login_challenge\")\n\n\t\tif hlc != \"\" {\n\t\t\t*c.callTrace = append(*c.callTrace, LoginWithOAuth2LoginChallenge)\n\t\t\treturn\n\t\t}\n\n\t\tif q.Has(\"flow\") {\n\t\t\t*c.callTrace = append(*c.callTrace, LoginWithFlowID)\n\t\t\tlf := testhelpers.GetLoginFlow(t, c.browserClient, c.kratosPublicTS, q.Get(\"flow\"))\n\t\t\trequire.NotNil(t, lf)\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(lf.Ui.Nodes)\n\t\t\tvalues.Set(\"password\", c.password)\n\n\t\t\t_, _ = testhelpers.LoginMakeRequest(t, false, false, lf, c.browserClient, values.Encode())\n\t\t\tt.Log(\"[loginTS] login flow is ignored here since it will be handled by the code above, we just need to return\")\n\t\t\treturn\n\t\t}\n\t})\n\n\trouter.HandleFunc(\"GET /registration-ts\", func(w http.ResponseWriter, r *http.Request) {\n\t\tt.Log(\"[registrationTS] navigated to the registration ui\")\n\t\tc := r.Context().Value(TestUIConfig).(*testConfig)\n\t\t*c.callTrace = append(*c.callTrace, RegistrationUI)\n\n\t\tq := r.URL.Query()\n\t\thlc := q.Get(\"login_challenge\")\n\n\t\tif hlc != \"\" {\n\t\t\t*c.callTrace = append(*c.callTrace, RegistrationWithOAuth2LoginChallenge)\n\t\t\tt.Log(\"[registrationTS] initializing a new OpenID Provider flow through the registration endpoint\")\n\t\t\tregistrationUrl, err := url.Parse(c.kratosPublicTS.URL + registration.RouteInitBrowserFlow)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tq := registrationUrl.Query()\n\t\t\tq.Set(\"login_challenge\", hlc)\n\t\t\tregistrationUrl.RawQuery = q.Encode()\n\n\t\t\treq, err := http.NewRequest(\"GET\", registrationUrl.String(), nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tresp, err := c.browserClient.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t\t\trequire.NoError(t, resp.Body.Close())\n\n\t\t\t// if the registration page redirects us to the login page\n\t\t\t// we will sign in which means we might have a session\n\t\t\tvar oryCookie *http.Cookie\n\t\t\tcurrentURL, err := url.Parse(kratosPublicTS.URL)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tfor _, c := range c.browserClient.Jar.Cookies(currentURL) {\n\t\t\t\tif c.Name == config.DefaultSessionCookieName {\n\t\t\t\t\toryCookie = c\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif oryCookie != nil {\n\t\t\t\tt.Log(\"[registrationTS] we expect to have been at the login screen and got an active flow. This means we have a session now\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tflowID := resp.Request.URL.Query().Get(\"flow\")\n\t\t\tassert.NotEmpty(t, flowID)\n\n\t\t\tf := testhelpers.GetRegistrationFlow(t, c.browserClient, c.kratosPublicTS, flowID)\n\t\t\trequire.NotNil(t, f)\n\n\t\t\t// continue the registration flow here\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tvalues.Set(\"traits.foobar\", c.identifier)\n\t\t\tvalues.Set(\"traits.username\", c.identifier)\n\t\t\tvalues.Set(\"password\", c.password)\n\n\t\t\t_, res := testhelpers.RegistrationMakeRequest(t, false, false, f, c.browserClient, values.Encode())\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\treturn\n\t\t}\n\n\t\tif q.Has(\"flow\") {\n\t\t\t*c.callTrace = append(*c.callTrace, RegistrationWithFlowID)\n\t\t\tt.Log(\"[registrationTS] registration flow is ignored here since it will be handled by the code above, we just need to return\")\n\t\t\treturn\n\t\t}\n\t})\n\n\trouter.HandleFunc(\"GET /consent\", func(w http.ResponseWriter, r *http.Request) {\n\t\tt.Log(\"[consentTS] navigated to the consent ui\")\n\t\tc := r.Context().Value(TestUIConfig).(*testConfig)\n\t\t*c.callTrace = append(*c.callTrace, Consent)\n\n\t\tq := r.URL.Query()\n\t\tconsentChallenge := q.Get(\"consent_challenge\")\n\t\tassert.NotEmpty(t, consentChallenge)\n\n\t\tif consentChallenge != \"\" {\n\t\t\t*c.callTrace = append(*c.callTrace, ConsentWithChallenge)\n\t\t}\n\n\t\tcr, resp, err := hydraAdminClient.GetOAuth2ConsentRequest(ctx).ConsentChallenge(q.Get(\"consent_challenge\")).Execute()\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\t\tassert.ElementsMatch(t, cr.RequestedScope, c.requestedScope)\n\n\t\tif cr.GetSkip() {\n\t\t\t*c.callTrace = append(*c.callTrace, ConsentSkip)\n\t\t}\n\n\t\tif cr.Client.GetSkipConsent() {\n\t\t\t*c.callTrace = append(*c.callTrace, ConsentClientSkip)\n\t\t}\n\n\t\tcompletedAcceptRequest, resp, err := hydraAdminClient.AcceptOAuth2ConsentRequest(r.Context()).AcceptOAuth2ConsentRequest(hydraclientgo.AcceptOAuth2ConsentRequest{\n\t\t\tRemember:   &c.consentRemember,\n\t\t\tGrantScope: c.requestedScope,\n\t\t}).ConsentChallenge(q.Get(\"consent_challenge\")).Execute()\n\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\n\t\trequire.NotNil(t, completedAcceptRequest)\n\t\t*c.callTrace = append(*c.callTrace, ConsentAccept)\n\n\t\tt.Logf(\"[consentTS] navigating to %s\", completedAcceptRequest.RedirectTo)\n\t\tresp, err = c.browserClient.Get(completedAcceptRequest.RedirectTo)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, c.clientAppTS.URL, fmt.Sprintf(\"%s://%s\", resp.Request.URL.Scheme, resp.Request.URL.Host))\n\t})\n\n\tkratosUIMiddleware := negroni.New()\n\tkratosUIMiddleware.UseFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\t// add the context from the global context to each request\n\t\tnext(rw, r.WithContext(ctx))\n\t})\n\tkratosUIMiddleware.UseHandler(router)\n\n\tkratosUITS := testhelpers.NewHTTPTestServer(t, kratosUIMiddleware)\n\n\tclientAppTSMiddleware := negroni.New()\n\tclientAppTSMiddleware.UseFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\t// add the context from the global context to each request\n\t\tnext(rw, r.WithContext(ctx))\n\t})\n\tclientAppTSMiddleware.UseHandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tc := ctx.Value(TestOAuthClientState).(*clientAppConfig)\n\t\tkc := ctx.Value(TestUIConfig).(*testConfig)\n\t\t*kc.callTrace = append(*kc.callTrace, CodeExchange)\n\n\t\tc.state.visits += 1\n\t\tt.Logf(\"[clientAppTS] handling a callback at client app %s\", r.URL.String())\n\t\tif r.URL.Query().Has(\"code\") {\n\t\t\ttoken, err := c.client.Exchange(r.Context(), r.URL.Query().Get(\"code\"))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tif token != nil && token.AccessToken != \"\" {\n\t\t\t\tt.Log(\"[clientAppTS] successfully exchanged code for token\")\n\t\t\t\t*kc.callTrace = append(*kc.callTrace, CodeExchangeWithToken)\n\t\t\t\tc.state.tokens += 1\n\t\t\t} else {\n\t\t\t\tt.Log(\"[clientAppTS] did not receive a token\")\n\t\t\t}\n\t\t} else {\n\t\t\tt.Error(\"[clientAppTS] code query parameter is missing\")\n\t\t}\n\t})\n\t// A new OAuth client which will also function as the callback for the code exchange\n\tclientAppTS := testhelpers.NewHTTPTestServer(t, clientAppTSMiddleware)\n\n\t// we want to test if the registration ui is used if the flow contains an oauth2 login challenge\n\t// so we will have Hydra redirect to the base path of the test kratos ui server which\n\t// will then initiate the registration flow\n\thydraAdmin, hydraPublic := newHydra(t, kratosUITS.URL+\"/registration-ts\", kratosUITS.URL+\"/consent\")\n\n\thydraAdminClient = createHydraOAuth2ApiClient(hydraAdmin)\n\n\tconf.MustSet(ctx, config.ViperKeyOAuth2ProviderURL, hydraAdmin+\"/\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceErrorUI, errTS.URL+\"/error-ts\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceLoginUI, kratosUITS.URL+\"/login-ts\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationUI, kratosUITS.URL+\"/registration-ts\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, redirTS.URL+\"/return-ts\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationAfter+\".\"+config.DefaultBrowserReturnURL, redirTS.URL+\"/registration-return-ts\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword), map[string]interface{}{\"enabled\": true})\n\tconf.MustSet(ctx, config.ViperKeySecretsDefault, []string{\"not-a-secure-session-key\"})\n\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\n\tdoOAuthFlow := func(t *testing.T, ctx context.Context, oauthClient *oauth2.Config, browserClient *http.Client) {\n\t\tt.Helper()\n\n\t\tauthCodeURL := makeAuthCodeURL(t, oauthClient, \"\", false)\n\t\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, authCodeURL, nil)\n\t\trequire.NoError(t, err)\n\t\tres, err := browserClient.Do(req)\n\t\trequire.NoError(t, err)\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\n\t\trequire.NoError(t, res.Body.Close())\n\t\trequire.Equal(t, \"\", string(body))\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t}\n\n\tregisterNewAccount := func(t *testing.T, browserClient *http.Client, identifier, password string) {\n\t\t// we need to create a new session directly with kratos\n\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, browserClient, kratosPublicTS, false, false, false)\n\t\trequire.NotNil(t, f)\n\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"traits.foobar\", identifier)\n\t\tvalues.Set(\"traits.username\", identifier)\n\t\tvalues.Set(\"password\", password)\n\n\t\t_, resp := testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, values.Encode())\n\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\n\t\tvar cookie *http.Cookie\n\t\tcurrentURL, err := url.Parse(kratosPublicTS.URL)\n\t\trequire.NoError(t, err)\n\n\t\tfor _, c := range browserClient.Jar.Cookies(currentURL) {\n\t\t\tif c.Name == config.DefaultSessionCookieName {\n\t\t\t\tcookie = c\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\trequire.NotNil(t, cookie, \"expected exactly one session cookie to be set but got none\")\n\t}\n\n\t// important, we will set the persistent cookie to false for most of the tests here\n\t// once this is true, the behavior of the consent flow changes\n\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, false)\n\n\tt.Run(\"case=should accept oauth login request on registration\", func(t *testing.T) {\n\t\t// this test initiates a new OAuth2 flow which goes directly to the registration page\n\t\t// we then create a new account through the registration flow\n\t\t// and expect the OAuth2 flow to succeed\n\t\tscopes := []string{\"profile\", \"email\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\n\t\toauth2Client := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\tidentifier := x.NewUUID().String()\n\t\tpassword := x.NewUUID().String()\n\n\t\tct := make([]callTrace, 0)\n\n\t\tctx = context.WithValue(ctx, TestUIConfig, &testConfig{\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         password,\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tconsentRemember:  true,\n\t\t\tcallTrace:        &ct,\n\t\t\trequestedScope:   scopes,\n\t\t})\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, &clientAppConfig{\n\t\t\tclient:      oauth2Client,\n\t\t\texpectToken: true,\n\t\t\tstate:       &clientAS,\n\t\t})\n\n\t\tdoOAuthFlow(t, ctx,\n\t\t\toauth2Client,\n\t\t\tbrowserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\texpected := []callTrace{\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge,\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\t\trequire.ElementsMatch(t, expected, ct, \"expected the call trace to match\")\n\t})\n\n\tt.Run(\"case=registration with session should redirect to login to re-authenticate and to consent\", func(t *testing.T) {\n\t\t// this test registers a new account which sets a session\n\t\t// we then initiate a new OAuth2 flow which should redirect us to the registration page\n\t\t// the registration page does a session validation and retrieves the loginRequest from Hydra\n\t\t// which in this case will indicate we cannot skip the login flow (since the there is no previous OAuth flow associated)\n\t\t// we then get redirected to the the login page with refresh=true\n\t\t// we then sign in and expect to be redirected to the consent page\n\t\t// and then back to the client app\n\t\tscopes := []string{\"profile\", \"email\", \"offline_access\"}\n\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\t\tidentifier := x.NewUUID().String()\n\t\tpassword := x.NewUUID().String()\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         password,\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tconsentRemember:  true,\n\t\t\trequestedScope:   scopes,\n\t\t\tcallTrace:        &ct,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\texpectToken: true,\n\t\t\tstate:       &clientAS,\n\t\t})\n\n\t\tregisterNewAccount(t, browserClient, identifier, password)\n\n\t\trequire.ElementsMatch(t, []callTrace{\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithFlowID,\n\t\t}, ct, \"expected the call trace to match\")\n\n\t\t// reset the call trace\n\t\tct = []callTrace{}\n\t\ttc.callTrace = &ct\n\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\texpected := []callTrace{\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge,\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\t\trequire.ElementsMatch(t, expected, ct, \"expected the call trace to match\")\n\t})\n\n\tt.Run(\"case=registration should redirect to login if session exists and skip=false\", func(t *testing.T) {\n\t\t// we dont want to skip the consent page here\n\t\t// we want the registration page to redirect to the login page\n\t\t// since we have a session but do not skip the consent page\n\t\tclientSkipConsent := false\n\t\tconsentRemember := false\n\n\t\tscopes := []string{\"profile\", \"email\", \"offline_access\"}\n\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), clientSkipConsent)\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\t\tidentifier := x.NewUUID().String()\n\t\tpassword := x.NewUUID().String()\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         password,\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tconsentRemember:  consentRemember,\n\t\t\trequestedScope:   scopes,\n\t\t\tcallTrace:        &ct,\n\t\t}\n\n\t\tclientAppConfig := &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\texpectToken: true,\n\t\t}\n\n\t\texpected := []callTrace{\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge,\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\n\t\t// set the global context values\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, clientAppConfig)\n\n\t\tdoSuccessfulOAuthFlow := func(t *testing.T) {\n\t\t\tt.Helper()\n\n\t\t\tclientAS := clientAppState{\n\t\t\t\tvisits: 0,\n\t\t\t\ttokens: 0,\n\t\t\t}\n\t\t\tclientAppConfig.state = &clientAS\n\n\t\t\tdoOAuthFlow(t, ctx,\n\t\t\t\toauthClient,\n\t\t\t\tbrowserClient)\n\t\t\tassert.EqualValues(t, clientAppState{\n\t\t\t\tvisits: 1,\n\t\t\t\ttokens: 1,\n\t\t\t}, clientAS)\n\n\t\t\trequire.ElementsMatchf(t, expected, ct, \"expected the call trace to match\")\n\t\t}\n\n\t\tdoSuccessfulOAuthFlow(t)\n\n\t\t// reset our state on the client app\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tclientAppConfig.state = &clientAS\n\n\t\t// we should now have a session, but not skip the consent page\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\texpected = append(expected,\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge,\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t)\n\t\trequire.ElementsMatchf(t, expected, ct, \"expected the call trace to match\")\n\t})\n\n\tt.Run(\"case=consent should be skipped if client is configured to skip\", func(t *testing.T) {\n\t\tclientSkipConsent := true\n\t\tscopes := []string{\"profile\", \"email\", \"offline_access\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), clientSkipConsent)\n\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\t\tidentifier := x.NewUUID().String()\n\t\tpassword := x.NewUUID().String()\n\n\t\tct := make([]callTrace, 0)\n\n\t\ttc := &testConfig{\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         password,\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tconsentRemember:  false,\n\t\t\trequestedScope:   scopes,\n\t\t\tcallTrace:        &ct,\n\t\t}\n\n\t\tclientAppConfig := &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\texpectToken: true,\n\t\t}\n\n\t\texpected := []callTrace{\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge,\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentClientSkip,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\n\t\t// set the global context values\n\t\tctx = context.WithValue(ctx, TestUIConfig, tc)\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, clientAppConfig)\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\t\tclientAppConfig.state = &clientAS\n\n\t\tdoOAuthFlow(t, ctx,\n\t\t\toauthClient,\n\t\t\tbrowserClient)\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\trequire.ElementsMatchf(t, expected, ct, \"expected the call trace to match\")\n\t})\n\n\tt.Run(\"case=consent should be skipped if user has a session and has already consented\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, true)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, false)\n\t\t})\n\n\t\tconsentRemember := true\n\n\t\tscopes := []string{\"profile\", \"email\", \"offline_access\"}\n\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\t\tidentifier := x.NewUUID().String()\n\t\tpassword := x.NewUUID().String()\n\n\t\tct := make([]callTrace, 0)\n\n\t\tkratosUIConfig := &testConfig{\n\t\t\tidentifier:       identifier,\n\t\t\tpassword:         password,\n\t\t\tbrowserClient:    browserClient,\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tconsentRemember:  consentRemember,\n\t\t\trequestedScope:   scopes,\n\t\t\tcallTrace:        &ct,\n\t\t}\n\n\t\tclientAppConfig := &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\texpectToken: true,\n\t\t}\n\n\t\texpected := []callTrace{\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge,\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\n\t\t// set the global context values\n\t\tctx = context.WithValue(ctx, TestUIConfig, kratosUIConfig)\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, clientAppConfig)\n\n\t\tdoSuccessfulOAuthFlow := func(t *testing.T) {\n\t\t\tt.Helper()\n\n\t\t\tclientAS := clientAppState{\n\t\t\t\tvisits: 0,\n\t\t\t\ttokens: 0,\n\t\t\t}\n\t\t\tclientAppConfig.state = &clientAS\n\n\t\t\tdoOAuthFlow(t, ctx,\n\t\t\t\toauthClient,\n\t\t\t\tbrowserClient)\n\t\t\tassert.EqualValues(t, clientAppState{\n\t\t\t\tvisits: 1,\n\t\t\t\ttokens: 1,\n\t\t\t}, clientAS)\n\n\t\t\trequire.ElementsMatchf(t, expected, ct, \"expected the call trace to match\")\n\t\t}\n\n\t\tdoSuccessfulOAuthFlow(t)\n\n\t\t// reset our state on the client app\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\n\t\tclientAppConfig.state = &clientAS\n\n\t\t// reset the call trace\n\t\tct = []callTrace{}\n\t\tkratosUIConfig.callTrace = &ct\n\n\t\t// we should now have a session, but not skip the consent page\n\t\tdoOAuthFlow(t, ctx, oauthClient, browserClient)\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\texpected = []callTrace{\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentSkip,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\t\trequire.ElementsMatchf(t, expected, ct, \"expected the call trace to match\")\n\t})\n\n\tt.Run(\"case=should fail because the persistent Hydra session doesn't match the new Kratos session subject\", func(t *testing.T) {\n\t\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, true)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionPersistentCookie, false)\n\t\t})\n\n\t\t// this test re-uses the previous oauthClient\n\t\t// but creates a new user account through the registration flow\n\t\t// since the session with the new user does not match the hydra session it should fail\n\t\tscopes := []string{\"profile\", \"email\", \"offline_access\"}\n\t\tclientID := createOAuth2Client(t, ctx, hydraAdminClient, []string{clientAppTS.URL}, strings.Join(scopes, \" \"), false)\n\n\t\toauthClient := &oauth2.Config{\n\t\t\tClientID:     clientID,\n\t\t\tClientSecret: \"client-secret\",\n\t\t\tEndpoint: oauth2.Endpoint{\n\t\t\t\tAuthURL:   hydraPublic + \"/oauth2/auth\",\n\t\t\t\tTokenURL:  hydraPublic + \"/oauth2/token\",\n\t\t\t\tAuthStyle: oauth2.AuthStyleInParams,\n\t\t\t},\n\t\t\tScopes:      scopes,\n\t\t\tRedirectURL: clientAppTS.URL,\n\t\t}\n\n\t\tbrowserClient := testhelpers.NewClientWithCookieJar(t, nil, nil)\n\n\t\tct := make([]callTrace, 0)\n\n\t\tkratosUIConfig := &testConfig{\n\t\t\tidentifier:       x.NewUUID().String(),\n\t\t\tpassword:         x.NewUUID().String(),\n\t\t\tkratosPublicTS:   kratosPublicTS,\n\t\t\tbrowserClient:    browserClient,\n\t\t\tclientAppTS:      clientAppTS,\n\t\t\thydraAdminClient: hydraAdminClient,\n\t\t\tconsentRemember:  true,\n\t\t\tcallTrace:        &ct,\n\t\t\trequestedScope:   scopes,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestUIConfig, kratosUIConfig)\n\n\t\tclientAppConfig := &clientAppConfig{\n\t\t\tclient:      oauthClient,\n\t\t\texpectToken: false,\n\t\t}\n\n\t\tctx = context.WithValue(ctx, TestOAuthClientState, clientAppConfig)\n\n\t\texpected := []callTrace{\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge,\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\n\t\tdoSuccessfulOAuthFlow := func(t *testing.T) {\n\t\t\tt.Helper()\n\n\t\t\tclientAS := clientAppState{\n\t\t\t\tvisits: 0,\n\t\t\t\ttokens: 0,\n\t\t\t}\n\t\t\tclientAppConfig.state = &clientAS\n\n\t\t\tdoOAuthFlow(t, ctx,\n\t\t\t\toauthClient,\n\t\t\t\tbrowserClient)\n\n\t\t\tassert.EqualValues(t, clientAppState{\n\t\t\t\tvisits: 1,\n\t\t\t\ttokens: 1,\n\t\t\t}, clientAS)\n\n\t\t\trequire.ElementsMatch(t, expected, ct, \"expected the call trace to match\")\n\t\t}\n\n\t\tdoSuccessfulOAuthFlow(t)\n\n\t\tclientAS := clientAppState{\n\t\t\tvisits: 0,\n\t\t\ttokens: 0,\n\t\t}\n\t\tclientAppConfig.state = &clientAS\n\n\t\tcurrentURL, err := url.Parse(kratosPublicTS.URL)\n\t\trequire.NoError(t, err)\n\t\tcookies := browserClient.Jar.Cookies(currentURL)\n\n\t\t// remove the kratos session so we can register a new account\n\t\tfor _, c := range cookies {\n\t\t\tif c.Name == config.DefaultSessionCookieName {\n\t\t\t\tc.MaxAge = -1\n\t\t\t\tc.Value = \"\"\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tbrowserClient.Jar.SetCookies(currentURL, cookies)\n\n\t\tkratosUIConfig.identifier = x.NewUUID().String()\n\t\tkratosUIConfig.password = x.NewUUID().String()\n\n\t\t// reset the call trace\n\t\tct = []callTrace{}\n\t\tkratosUIConfig.callTrace = &ct\n\n\t\tdoOAuthFlow(t, ctx,\n\t\t\toauthClient,\n\t\t\tbrowserClient)\n\n\t\t// In Hydra v2.2.0-rc.3 this would have failed outright: Hydra returned an error\n\t\t// when AcceptLoginRequest arrived with a subject (new user B) that did not match\n\t\t// the subject in the existing login session (user A, skip=true flow).\n\t\t//\n\t\t// In Hydra v2.2.0 final the mismatch is handled with a prompt=login redirect\n\t\t// instead of an error. The restart produces a fresh flow with f.Subject=\"\" so\n\t\t// the mismatch guard (f.Subject != \"\" && payload.Subject != f.Subject) in\n\t\t// consent/handler.go does not fire on the second attempt, and the new user's\n\t\t// subject is accepted. The flow completes successfully for user B.\n\t\t//\n\t\t// Security note: this is not exploitable in production. AcceptLoginRequest is a\n\t\t// trusted admin API reachable only by the login provider (Kratos), which always\n\t\t// sends the correctly authenticated identity ID. The change removes a hard\n\t\t// early-failure signal for accidental subject substitution but does not\n\t\t// introduce a new attack surface.\n\t\tassert.EqualValues(t, clientAppState{\n\t\t\tvisits: 1,\n\t\t\ttokens: 1,\n\t\t}, clientAS)\n\n\t\texpected = []callTrace{\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge, // skip=true, subject mismatch → prompt=login redirect\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithFlowID,\n\t\t\tRegistrationUI,\n\t\t\tRegistrationWithOAuth2LoginChallenge, // skip=false, f.Subject=\"\" → guard bypassed, new subject accepted\n\t\t\tLoginUI,\n\t\t\tLoginWithFlowID,\n\t\t\tConsent,\n\t\t\tConsentWithChallenge,\n\t\t\tConsentAccept,\n\t\t\tCodeExchange,\n\t\t\tCodeExchangeWithToken,\n\t\t}\n\t\trequire.ElementsMatch(t, expected, ct, \"expected the call trace to match\")\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/password/registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/ory/x/otelx/semconv\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc nodePasswordInput() *node.Node {\n\treturn NewPasswordNode(\"password\", node.InputAttributeAutocompleteNewPassword)\n}\n\nfunc nodeSubmit() *node.Node {\n\treturn node.NewInputField(\"method\", \"password\", node.PasswordGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoRegistration())\n}\n\nvar _ registration.FormHydrator = new(Strategy)\n\n// Update Registration Flow with Password Method\n//\n// swagger:model updateRegistrationFlowWithPasswordMethod\ntype UpdateRegistrationFlowWithPasswordMethod struct {\n\t// Password to sign the user up with\n\t//\n\t// required: true\n\tPassword string `json:\"password\"`\n\n\t// The identity's traits\n\t//\n\t// required: true\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// The CSRF Token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Method to use\n\t//\n\t// This field must be set to `password` when using the password method.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) handleRegistrationError(r *http.Request, f *registration.Flow, p UpdateRegistrationFlowWithPasswordMethod, err error) error {\n\tif f != nil {\n\t\tfor _, n := range container.NewFromJSON(\"\", node.DefaultGroup, p.Traits, \"traits\").Nodes {\n\t\t\t// we only set the value and not the whole field because we want to keep types from the initial form generation\n\t\t\tf.UI.Nodes.SetValueAttribute(n.ID(), n.Attributes.GetValue())\n\t\t}\n\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (s *Strategy) decode(p *UpdateRegistrationFlowWithPasswordMethod, r *http.Request, ds *url.URL) (err error) {\n\treturn registration.DecodeBody(p, r, registrationSchema, ds)\n}\n\nfunc (s *Strategy) Register(_ http.ResponseWriter, r *http.Request, f *registration.Flow, i *identity.Identity) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.password.Strategy.Register\")\n\tdefer otelx.End(span, &err)\n\n\tif err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.ID().String(), s.d); err != nil {\n\t\treturn err\n\t}\n\n\tds, err := f.IdentitySchema.URL(ctx, s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar p UpdateRegistrationFlowWithPasswordMethod\n\tif err := s.decode(&p, r, ds); err != nil {\n\t\treturn s.handleRegistrationError(r, f, p, err)\n\t}\n\n\tf.TransientPayload = p.TransientPayload\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn s.handleRegistrationError(r, f, p, err)\n\t}\n\n\tif len(p.Password) == 0 {\n\t\treturn s.handleRegistrationError(r, f, p, schema.NewRequiredError(\"#/password\", \"password\"))\n\t}\n\n\tif len(p.Traits) == 0 {\n\t\tp.Traits = json.RawMessage(\"{}\")\n\t}\n\n\thpw := make(chan []byte)\n\terrC := make(chan error)\n\tgo func() {\n\t\tdefer close(hpw)\n\t\tdefer close(errC)\n\n\t\th, err := s.d.Hasher(ctx).Generate(ctx, []byte(p.Password))\n\t\tif err != nil {\n\t\t\terrC <- err\n\t\t\treturn\n\t\t}\n\t\thpw <- h\n\t}()\n\n\tif err != nil {\n\t\treturn s.handleRegistrationError(r, f, p, err)\n\t}\n\n\ti.Traits = identity.Traits(p.Traits)\n\t// We have to set the credential here, so the identity validator can populate the identifiers.\n\t// The password hash is computed in parallel and set later.\n\tif err := i.SetCredentialsWithConfig(s.ID(), identity.Credentials{Type: s.ID(), Identifiers: []string{}}, json.RawMessage(\"{}\")); err != nil {\n\t\treturn s.handleRegistrationError(r, f, p, err)\n\t}\n\n\tif err := s.validateCredentials(ctx, i, p.Password); err != nil {\n\t\treturn s.handleRegistrationError(r, f, p, err)\n\t}\n\n\tselect {\n\tcase err := <-errC:\n\t\treturn s.handleRegistrationError(r, f, p, err)\n\tcase h := <-hpw:\n\t\tco, err := json.Marshal(&identity.CredentialsPassword{HashedPassword: string(h)})\n\t\tif err != nil {\n\t\t\treturn s.handleRegistrationError(r, f, p, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode password options to JSON: %s\", err)))\n\t\t}\n\t\ti.UpsertCredentialsConfig(s.ID(), co, 0)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) validateCredentials(ctx context.Context, i *identity.Identity, pw string) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.password.Strategy.validateCredentials\")\n\tdefer otelx.End(span, &err)\n\n\tif err := s.d.IdentityValidator().Validate(ctx, i); err != nil {\n\t\treturn err\n\t}\n\n\tc, ok := i.GetCredentials(identity.CredentialsTypePassword)\n\tif !ok {\n\t\t// This should never happen\n\t\treturn errors.WithStack(x.PseudoPanic.WithReasonf(\"identity object did not provide the %s CredentialType unexpectedly\", identity.CredentialsTypePassword))\n\t} else if len(c.Identifiers) == 0 {\n\t\treturn schema.NewMissingIdentifierError()\n\t}\n\n\tfor _, id := range c.Identifiers {\n\t\tif err := s.d.PasswordValidator().Validate(ctx, id, pw); err != nil {\n\t\t\tif herodotErr := new(herodot.DefaultError); errors.As(err, &herodotErr) {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif message := new(text.Message); errors.As(err, &message) {\n\t\t\t\treturn schema.NewPasswordPolicyViolationError(\"#/password\", message)\n\t\t\t}\n\t\t\treturn schema.NewPasswordPolicyViolationError(\"#/password\", text.NewErrorValidationPasswordPolicyViolationGeneric(err.Error()))\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethod(r *http.Request, f *registration.Flow) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.password.Strategy.PopulateRegistrationMethod\")\n\tdefer otelx.End(span, &err)\n\n\tds, err := f.IdentitySchema.URL(ctx, s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// The group used to be `password`, but to make it consistent with other methods and two-step registration,\n\t// it is now `default`. To make this switch backwards compatible, this feature flag is used.\n\t//\n\t// Previously, the behavior would be that the group of NodesFromJSONSchema would be `password`, but as soon\n\t// as any other method (code, passkeys, two-step) would be enabled, the group would be `default`.\n\t//\n\t// Going forward, the default is that the group is `default` and the feature flag is not set.\n\t//\n\t// TODO remove me when everyone has migrated.\n\tif !s.d.Config().SelfServiceFlowRegistrationTwoSteps(r.Context()) && node.UiNodeGroup(s.d.Config().SelfServiceFlowRegistrationPasswordMethodProfileGroup(r.Context())) == node.PasswordGroup {\n\t\tspan.AddEvent(semconv.NewDeprecatedFeatureUsedEvent(ctx, \"password_profile_registration_node_group=password\"))\n\n\t\t// This is the legacy code path. In the new code path, the profile method is responsible for hydrating the form\n\t\t// nodes. In the old code path, the password method is responsible for hydrating the form nodes if it is\n\t\t// the only method enabled.\n\t\tnodes, err := container.NodesFromJSONSchema(r.Context(), node.PasswordGroup, ds.String(), \"\", nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, n := range nodes {\n\t\t\tf.UI.SetNode(n)\n\t\t}\n\t}\n\t// TODO end\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.Nodes.Upsert(nodePasswordInput())\n\tf.UI.Nodes.Upsert(nodeSubmit())\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodCredentials(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.Nodes.Upsert(nodePasswordInput())\n\tf.UI.Nodes.Upsert(nodeSubmit())\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodProfile(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\t// The profile method is responsible for rendering the profile form fields.\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.Nodes.RemoveMatching(nodePasswordInput())\n\tf.UI.Nodes.RemoveMatching(nodeSubmit())\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/password/registration_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\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/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/registrationhelpers\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nvar flows = []string{\"spa\", \"api\", \"browser\"}\n\nfunc newRegistrationRegistry(t *testing.T, cfgOpts ...configx.OptionModifier) *driver.RegistryDefault {\n\t_, reg := pkg.NewFastRegistryWithMocks(t, append([]configx.OptionModifier{\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypePassword) + \".enabled\": true,\n\t\t\tconfig.ViperKeySelfServiceRegistrationLoginHints:                                                       true,\n\t\t\tconfig.ViperKeySelfServiceRegistrationEnableLegacyOneStep:                                              true,\n\t\t}),\n\t}, cfgOpts...)...)\n\n\treturn reg\n}\n\nfunc TestRegistration(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"case=registration\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\treg := newRegistrationRegistry(t, configx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceRegistrationEnableLegacyOneStep: true,\n\t\t\tconfig.ViperKeyDefaultIdentitySchemaID:                    \"default\",\n\t\t\tconfig.ViperKeyIdentitySchemas: config.Schemas{\n\t\t\t\t{ID: \"default\", URL: \"file://stub/registration.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t{ID: \"other\", URL: \"file://stub/registration.schema.json\", SelfserviceSelectable: true},\n\t\t\t},\n\t\t}))\n\t\tconf := reg.Config()\n\n\t\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\t\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t\t_ = testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\t\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\t\tredirNoSessionTS := testhelpers.NewRedirNoSessionTS(t, reg)\n\n\t\t// set the \"return to\" server, which will assert the session state\n\t\t// (redirTS: enforce that a session exists, redirNoSessionTS: enforce that no session exists)\n\t\tuseReturnToFromTS := func(ts *httptest.Server) {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceBrowserDefaultReturnTo, ts.URL+\"/default-return-to\")\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceRegistrationAfter+\".\"+config.DefaultBrowserReturnURL, ts.URL+\"/registration-return-ts\")\n\t\t}\n\n\t\tuseReturnToFromTS(redirTS)\n\n\t\tapiClient := testhelpers.NewDebugClient(t)\n\n\t\tt.Run(\"AssertCommonErrorCases\", func(t *testing.T) {\n\t\t\tregistrationhelpers.AssertCommonErrorCases(t, flows)\n\t\t})\n\n\t\tt.Run(\"AssertRegistrationRespectsValidation\", func(t *testing.T) {\n\t\t\treg := newRegistrationRegistry(t)\n\t\t\tregistrationhelpers.AssertRegistrationRespectsValidation(t, reg, flows, func(v url.Values) {\n\t\t\t\tv.Del(\"traits.foobar\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"AssertCSRFFailures\", func(t *testing.T) {\n\t\t\treg := newRegistrationRegistry(t)\n\t\t\tregistrationhelpers.AssertCSRFFailures(t, reg, flows, func(v url.Values) {\n\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\tv.Set(\"method\", identity.CredentialsTypePassword.String())\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"AssertSchemaDoesNotExist\", func(t *testing.T) {\n\t\t\treg := newRegistrationRegistry(t)\n\t\t\tregistrationhelpers.AssertSchemaDoesNotExist(t, reg, flows, func(v url.Values) {\n\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\tv.Set(\"method\", identity.CredentialsTypePassword.String())\n\t\t\t})\n\t\t})\n\n\t\texpectRegistrationBody := func(t *testing.T, browserRedirTS *httptest.Server, isAPI, isSPA bool, hc *http.Client, values func(url.Values)) string {\n\t\t\tif isAPI {\n\t\t\t\treturn testhelpers.SubmitRegistrationForm(t, isAPI, hc, publicTS, values,\n\t\t\t\t\tisSPA, http.StatusOK,\n\t\t\t\t\tpublicTS.URL+registration.RouteSubmitFlow)\n\t\t\t}\n\n\t\t\tif hc == nil {\n\t\t\t\thc = testhelpers.NewClientWithCookies(t)\n\t\t\t}\n\n\t\t\texpectReturnTo := browserRedirTS.URL + \"/registration-return-ts\"\n\t\t\tif isSPA {\n\t\t\t\texpectReturnTo = publicTS.URL\n\t\t\t}\n\n\t\t\treturn testhelpers.SubmitRegistrationForm(t, isAPI, hc, publicTS, values,\n\t\t\t\tisSPA, http.StatusOK, expectReturnTo)\n\t\t}\n\n\t\texpectSuccessfulRegistration := func(t *testing.T, isAPI, isSPA bool, hc *http.Client, values func(url.Values)) string {\n\t\t\tuseReturnToFromTS(redirTS)\n\t\t\treturn expectRegistrationBody(t, redirTS, isAPI, isSPA, hc, values)\n\t\t}\n\n\t\texpectNoRegistration := func(t *testing.T, isAPI, isSPA bool, hc *http.Client, values func(url.Values)) string {\n\t\t\tuseReturnToFromTS(redirNoSessionTS)\n\t\t\tt.Cleanup(func() {\n\t\t\t\tuseReturnToFromTS(redirTS)\n\t\t\t})\n\t\t\treturn expectRegistrationBody(t, redirNoSessionTS, isAPI, isSPA, hc, values)\n\t\t}\n\n\t\tt.Run(\"case=should reject invalid transient payload\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t\t})\n\n\t\t\tfor _, f := range flows {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\tusername := x.NewUUID().String()\n\t\t\t\t\tbody := registrationhelpers.ExpectValidationError(t.Context(), t, publicTS, conf, f, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"traits.username\", username)\n\t\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t\t\tv.Set(\"transient_payload\", \"42\")\n\t\t\t\t\t})\n\t\t\t\t\tassert.Equal(t, \"bar\", gjson.Get(body, \"ui.nodes.#(attributes.name==traits.foobar).attributes.value\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, username, gjson.Get(body, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", body)\n\t\t\t\t\tassert.Equal(t, int64(4000026), gjson.Get(body, \"ui.nodes.#(attributes.name==transient_payload).messages.0.id\").Int(), \"%s\", body)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should accept valid transient payload\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t\t})\n\n\t\t\tsetValues := func(username string, v url.Values) {\n\t\t\t\tv.Set(\"traits.username\", username)\n\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\tv.Set(\"transient_payload.stuff\", \"42\")\n\t\t\t}\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tusername := x.NewUUID().String()\n\t\t\t\tbody := expectSuccessfulRegistration(t, true, false, nil, func(v url.Values) {\n\t\t\t\t\tsetValues(username, v)\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, username, gjson.Get(body, \"identity.traits.username\").String(), \"%s\", body)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session_token\").String(), \"%s\", body)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session.id\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tusername := x.NewUUID().String()\n\t\t\t\tbody := expectSuccessfulRegistration(t, false, true, nil, func(v url.Values) {\n\t\t\t\t\tsetValues(username, v)\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, username, gjson.Get(body, \"identity.traits.username\").String(), \"%s\", body)\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"session_token\").String(), \"%s\", body)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session.id\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tusername := x.NewUUID().String()\n\t\t\t\tbody := expectSuccessfulRegistration(t, false, false, nil, func(v url.Values) {\n\t\t\t\t\tsetValues(username, v)\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, username, gjson.Get(body, \"identity.traits.username\").String(), \"%s\", body)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should pass and set up a session\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tbody := expectSuccessfulRegistration(t, true, false, nil, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-8-api\")\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, `registration-identifier-8-api`, gjson.Get(body, \"identity.traits.username\").String(), \"%s\", body)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session_token\").String(), \"%s\", body)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session.id\").String(), \"%s\", body)\n\t\t\t\tassert.NotContains(t, gjson.Get(body, \"continue_with\").Raw, string(flow.ContinueWithActionRedirectBrowserToString), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tbody := expectSuccessfulRegistration(t, false, true, nil, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-8-spa\")\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, `registration-identifier-8-spa`, gjson.Get(body, \"identity.traits.username\").String(), \"%s\", body)\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"session_token\").String(), \"%s\", body)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"session.id\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(body, \"continue_with.0.action\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tbody := expectSuccessfulRegistration(t, false, false, nil, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-8-browser\")\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, `registration-identifier-8-browser`, gjson.Get(body, \"identity.traits.username\").String(), \"%s\", body)\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should not set up a session if hook is not configured\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tbody := expectNoRegistration(t, true, false, nil, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-8-api-nosession\")\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, `registration-identifier-8-api-nosession`, gjson.Get(body, \"identity.traits.username\").String(), \"%s\", body)\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"session_token\").String(), \"%s\", body)\n\t\t\t\tassert.Empty(t, gjson.Get(body, \"session.id\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\texpectNoRegistration(t, false, true, nil, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-8-spa-nosession\")\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\texpectNoRegistration(t, false, false, nil, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-8-browser-nosession\")\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should fail to register the same user again\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t\t})\n\n\t\t\tapplyTransform := func(values, transform func(v url.Values)) func(v url.Values) {\n\t\t\t\treturn func(v url.Values) {\n\t\t\t\t\tvalues(v)\n\t\t\t\t\ttransform(v)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// test duplicate registration on all client types, where the values can be transformed before\n\t\t\t// they are sent the second time\n\t\t\ttestWithTransform := func(t *testing.T, suffix string, transform func(v url.Values)) {\n\t\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-8-api-duplicate-\"+suffix)\n\t\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t\t}\n\n\t\t\t\t\t_ = expectSuccessfulRegistration(t, true, false, apiClient, values)\n\t\t\t\t\tbody := testhelpers.SubmitRegistrationForm(t, true, apiClient, publicTS,\n\t\t\t\t\t\tapplyTransform(values, transform), false, http.StatusBadRequest,\n\t\t\t\t\t\tpublicTS.URL+registration.RouteSubmitFlow)\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"You tried signing in with registration-identifier-8-api-duplicate-\"+suffix+\" which is already in use by another account. You can sign in using your password.\", \"%s\", body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-8-spa-duplicate-\"+suffix)\n\t\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t\t}\n\n\t\t\t\t\t_ = expectSuccessfulRegistration(t, false, true, nil, values)\n\t\t\t\t\tbody := registrationhelpers.ExpectValidationError(t.Context(), t, publicTS, conf, \"spa\", applyTransform(values, transform))\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"You tried signing in with registration-identifier-8-spa-duplicate-\"+suffix+\" which is already in use by another account. You can sign in using your password.\", \"%s\", body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-8-browser-duplicate-\"+suffix)\n\t\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t\t}\n\n\t\t\t\t\t_ = expectSuccessfulRegistration(t, false, false, nil, values)\n\t\t\t\t\tbody := registrationhelpers.ExpectValidationError(t.Context(), t, publicTS, conf, \"browser\", applyTransform(values, transform))\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"You tried signing in with registration-identifier-8-browser-duplicate-\"+suffix+\" which is already in use by another account. You can sign in using your password.\", \"%s\", body)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tt.Run(\"case=identical input\", func(t *testing.T) {\n\t\t\t\ttestWithTransform(t, \"identical\", func(v url.Values) {\n\t\t\t\t\t// base case\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=different capitalization\", func(t *testing.T) {\n\t\t\t\ttestWithTransform(t, \"caps\", func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", strings.ToUpper(v.Get(\"traits.username\")))\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=leading whitespace\", func(t *testing.T) {\n\t\t\t\ttestWithTransform(t, \"leading\", func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"  \"+v.Get(\"traits.username\"))\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=trailing whitespace\", func(t *testing.T) {\n\t\t\t\ttestWithTransform(t, \"trailing\", func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", v.Get(\"traits.username\")+\"  \")\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should return correct error ids from validation failures\", func(t *testing.T) {\n\t\t\ttest := func(t *testing.T, constraint string, setValues func(url.Values), expectedId text.ID, expectedMesage string) {\n\t\t\t\ttemplate := `{\n\t\t\t\t\t\"$id\": \"https://example.com/person.schema.json\",\n\t\t\t\t\t\"$schema\": \"http://json-schema.org/draft-07/schema#\",\n\t\t\t\t\t\"title\": \"Person\",\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"traits\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"foobar\": {\n\t\t\t\t\t\t\t\t\t%s\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"username\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\t\t\"ory.sh/kratos\": {\n\t\t\t\t\t\t\t\t\t\t\"credentials\": {\n\t\t\t\t\t\t\t\t\t\t\t\"password\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\"identifier\": true\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"foobar\",\n\t\t\t\t\t\t\t\t\"username\"\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\t\"additionalProperties\": false\n\t\t\t\t}`\n\n\t\t\t\ttesthelpers.SetDefaultIdentitySchemaFromRaw(conf, []byte(fmt.Sprintf(template, constraint)))\n\n\t\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, browserClient, publicTS, false, false, false)\n\t\t\t\tc := f.Ui\n\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(c.Nodes)\n\t\t\t\tsetValues(values)\n\t\t\t\tvalues.Set(\"traits.username\", \"registration-identifier-9\")\n\t\t\t\tvalues.Set(\"password\", x.NewUUID().String())\n\t\t\t\tactual, _ := testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, values.Encode())\n\n\t\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), \"password\", \"csrf_token\", \"traits.username\")\n\t\t\t\tassert.EqualValues(t, \"registration-identifier-9\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.messages\").Array())\n\t\t\t\tassert.Len(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).messages\").Array(), 0)\n\t\t\t\tassert.Equal(t, int64(expectedId), gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0.id\").Int())\n\t\t\t\tassert.Equal(t, expectedMesage, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0.text\").String())\n\t\t\t\tassert.Equal(t, \"error\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0.type\").String())\n\t\t\t}\n\n\t\t\tconst key = \"traits.foobar\"\n\t\t\tt.Run(\"case=string violating minLength\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"string\", \"minLength\": 5`, func(v url.Values) { v.Set(key, \"bar\") }, text.ErrorValidationMinLength, \"length must be >= 5, but got 3\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=string violating maxLength\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"string\", \"maxLength\": 5`, func(v url.Values) { v.Set(key, \"qwerty\") }, text.ErrorValidationMaxLength, \"length must be <= 5, but got 6\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=string violating pattern\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"string\", \"pattern\": \"^[a-z]*$\"`, func(v url.Values) { v.Set(key, \"FUBAR\") }, text.ErrorValidationInvalidFormat, \"does not match pattern \\\"^[a-z]*$\\\"\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=number violating minimum\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"number\", \"minimum\": 5`, func(v url.Values) { v.Set(key, \"3\") }, text.ErrorValidationMinimum, \"must be >= 5 but found 3\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=number violating exclusiveMinimum\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"number\", \"exclusiveMinimum\": 5`, func(v url.Values) { v.Set(key, \"5\") }, text.ErrorValidationExclusiveMinimum, \"must be > 5 but found 5\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=number violating maximum\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"number\", \"maximum\": 5`, func(v url.Values) { v.Set(key, \"6\") }, text.ErrorValidationMaximum, \"must be <= 5 but found 6\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=number violating exclusiveMaximum\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"number\", \"exclusiveMaximum\": 5`, func(v url.Values) { v.Set(key, \"5\") }, text.ErrorValidationExclusiveMaximum, \"must be < 5 but found 5\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=number violating multipleOf\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"number\", \"multipleOf\": 3`, func(v url.Values) { v.Set(key, \"7\") }, text.ErrorValidationMultipleOf, \"7 not multipleOf 3\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=array violating maxItems\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"array\", \"items\": { \"type\": \"string\" }, \"maxItems\": 3`, func(v url.Values) { v.Add(key, \"a\"); v.Add(key, \"b\"); v.Add(key, \"c\"); v.Add(key, \"d\") }, text.ErrorValidationMaxItems, \"maximum 3 items allowed, but found 4 items\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=array violating minItems\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"array\", \"items\": { \"type\": \"string\" }, \"minItems\": 3`, func(v url.Values) { v.Add(key, \"a\"); v.Add(key, \"b\") }, text.ErrorValidationMinItems, \"minimum 3 items allowed, but found 2 items\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=array violating uniqueItems\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"array\", \"items\": { \"type\": \"string\" }, \"uniqueItems\": true`, func(v url.Values) { v.Add(key, \"abc\"); v.Add(key, \"XYZ\"); v.Add(key, \"abc\") }, text.ErrorValidationUniqueItems, \"items at index 0 and 2 are equal\")\n\t\t\t})\n\n\t\t\tt.Run(\"case=wrong type\", func(t *testing.T) {\n\t\t\t\ttest(t, `\"type\": \"number\"`, func(v url.Values) { v.Set(key, \"blabla\") }, text.ErrorValidationWrongType, \"expected number, but got string\")\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should return an error because not passing validation and reset previous errors and values\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\n\t\t\tcheck := func(t *testing.T, actual string) {\n\t\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), \"password\", \"csrf_token\", \"traits.username\")\n\t\t\t}\n\n\t\t\tcheckFirst := func(t *testing.T, actual string) {\n\t\t\t\tcheck(t, actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).messages.0\").String(), `Property username is missing`, \"%s\", actual)\n\t\t\t}\n\n\t\t\tcheckSecond := func(t *testing.T, actual string) {\n\t\t\t\tcheck(t, actual)\n\t\t\t\tassert.EqualValues(t, \"registration-identifier-9\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).messages\").Array())\n\t\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.messages\").Array())\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0\").String(), `Property foobar is missing`, \"%s\", actual)\n\t\t\t}\n\n\t\t\tvaluesFirst := func(v url.Values) url.Values {\n\t\t\t\tv.Del(\"traits.username\")\n\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\treturn v\n\t\t\t}\n\n\t\t\tvaluesSecond := func(v url.Values) url.Values {\n\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-9\")\n\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\tv.Del(\"traits.foobar\")\n\t\t\t\treturn v\n\t\t\t}\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaAPI(t, apiClient, publicTS)\n\t\t\t\tc := f.Ui\n\n\t\t\t\tactual, _ := testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, valuesFirst(testhelpers.SDKFormFieldsToURLValues(c.Nodes))))\n\t\t\t\tcheckFirst(t, actual)\n\t\t\t\tactual, _ = testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, valuesSecond(testhelpers.SDKFormFieldsToURLValues(c.Nodes))))\n\t\t\t\tcheckSecond(t, actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, browserClient, publicTS, true, false, false)\n\t\t\t\tc := f.Ui\n\n\t\t\t\tactual, _ := testhelpers.RegistrationMakeRequest(t, false, true, f, browserClient, testhelpers.EncodeFormAsJSON(t, false, valuesFirst(testhelpers.SDKFormFieldsToURLValues(c.Nodes))))\n\t\t\t\tcheckFirst(t, actual)\n\t\t\t\tactual, _ = testhelpers.RegistrationMakeRequest(t, false, true, f, browserClient, testhelpers.EncodeFormAsJSON(t, false, valuesSecond(testhelpers.SDKFormFieldsToURLValues(c.Nodes))))\n\t\t\t\tcheckSecond(t, actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, browserClient, publicTS, false, false, false)\n\t\t\t\tc := f.Ui\n\n\t\t\t\tactual, _ := testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, valuesFirst(testhelpers.SDKFormFieldsToURLValues(c.Nodes)).Encode())\n\t\t\t\tcheckFirst(t, actual)\n\t\t\t\tactual, _ = testhelpers.RegistrationMakeRequest(t, false, false, f, browserClient, valuesSecond(testhelpers.SDKFormFieldsToURLValues(c.Nodes)).Encode())\n\t\t\t\tcheckSecond(t, actual)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should work even if password is just numbers\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://stub/registration.schema.json\")\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tactual := expectSuccessfulRegistration(t, true, false, nil, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-10-api\")\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, `registration-identifier-10-api`, gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tactual := expectSuccessfulRegistration(t, false, false, nil, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-10-spa\")\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, `registration-identifier-10-spa`, gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tactual := expectSuccessfulRegistration(t, false, false, nil, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", \"registration-identifier-10-browser\")\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, `registration-identifier-10-browser`, gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should fail if no identifier was set in the schema\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://stub/missing-identifier.schema.json\")\n\n\t\t\tfor _, f := range []string{\"spa\", \"api\", \"browser\"} {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\tactual := registrationhelpers.ExpectValidationError(t.Context(), t, publicTS, conf, f, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"traits.email\", testhelpers.RandomEmail())\n\t\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\t\tv.Set(\"method\", \"password\")\n\t\t\t\t\t})\n\t\t\t\t\tassert.Equal(t, text.NewErrorValidationIdentifierMissing().Text, gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should work with regular JSON\", func(t *testing.T) {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://stub/registration.schema.json\")\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t\t})\n\n\t\t\thc := testhelpers.NewClientWithCookies(t)\n\t\t\thc.Transport = testhelpers.NewTransportWithLogger(hc.Transport, t)\n\t\t\tpayload := testhelpers.InitializeRegistrationFlowViaBrowser(t, hc, publicTS, false, false, false)\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(payload.Ui.Nodes)\n\t\t\ttime.Sleep(time.Millisecond) // add a bit of delay to allow `1ns` to time out.\n\n\t\t\tusername := x.NewUUID()\n\t\t\tactual, res := testhelpers.RegistrationMakeRequest(t, true, false, payload, hc, fmt.Sprintf(`{\n  \"method\": \"password\",\n  \"csrf_token\": \"%s\",\n  \"password\": \"%s\",\n  \"traits\": {\n    \"foobar\": \"bar\",\n    \"username\": \"%s\"\n  }\n}`, values.Get(\"csrf_token\"), x.NewUUID(), username))\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, assertx.PrettifyJSONPayload(t, actual))\n\t\t\tassert.Equal(t, username.String(), gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"case=should choose the correct identity schema\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"advanced-user\")\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t\t{ID: \"does-not-exist\", URL: \"file://./stub/not-exists.schema.json\"},\n\t\t\t\t{ID: \"advanced-user\", URL: \"file://./stub/registration.secondary.schema.json\"},\n\t\t\t})\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\n\t\t\tusername := \"registration-custom-schema\"\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tname  string\n\t\t\t\tisAPI bool\n\t\t\t\tisSPA bool\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tname:  \"api\",\n\t\t\t\t\tisAPI: true,\n\t\t\t\t}, {\n\t\t\t\t\tname:  \"spa\",\n\t\t\t\t\tisSPA: true,\n\t\t\t\t}, {\n\t\t\t\t\tname: \"browser\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"type=\"+tc.name, func(t *testing.T) {\n\t\t\t\t\tbody := expectNoRegistration(t, tc.isAPI, tc.isSPA, nil, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"traits.username\", username+\"-\"+tc.name)\n\t\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\t\tv.Set(\"traits.baz\", \"bar\")\n\t\t\t\t\t})\n\n\t\t\t\t\tif tc.isAPI || tc.isSPA { // body is empty for browser flow\n\t\t\t\t\t\tassert.Equal(t, username+\"-\"+tc.name, gjson.Get(body, \"identity.traits.username\").String(), \"%s\", body)\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"session_token\").String(), \"%s\", body)\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"session.id\").String(), \"%s\", body)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=multi-schema select\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t\t{ID: \"username\", URL: \"file://stub/sort.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t{ID: \"email\", URL: \"file://stub/email.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t{ID: \"phone\", URL: \"file://stub/phone.schema.json\"},\n\t\t\t\t{ID: \"not-allowed\", URL: \"file://stub/login.schema.json\"},\n\t\t\t})\n\t\t\t//  We set default to phone and select email in InitializeRegistrationFlowViaBrowser\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"phone\")\n\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceRegistrationAfter+\".\"+config.DefaultBrowserReturnURL, \"https://www.ory.sh\")\n\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\n\t\t\texpected := container.Container{\n\t\t\t\tMethod: \"POST\",\n\t\t\t\tNodes: node.Nodes{\n\t\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\t\tnode.NewInputField(\"traits.email\", nil, node.DefaultGroup, node.InputAttributeTypeEmail, node.WithRequiredInputAttribute, node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\t\ta.Autocomplete = node.InputAttributeAutocompleteEmail\n\t\t\t\t\t})).WithMetaLabel(text.NewInfoNodeLabelGenerated(\"E-Mail\", \"traits.email\")),\n\t\t\t\t\tnode.NewInputField(\"password\", nil, node.PasswordGroup, node.InputAttributeTypePassword, node.WithRequiredInputAttribute, node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\t\ta.Autocomplete = node.InputAttributeAutocompleteNewPassword\n\t\t\t\t\t})).WithMetaLabel(text.NewInfoNodeInputPassword()),\n\t\t\t\t\tnode.NewInputField(\"method\", \"password\", node.PasswordGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoRegistration()),\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tname  string\n\t\t\t\tisAPI bool\n\t\t\t\tisSPA bool\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tname:  \"api\",\n\t\t\t\t\tisAPI: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname:  \"spa\",\n\t\t\t\t\tisSPA: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"browser\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"type=\"+tc.name+\" registration success\", func(t *testing.T) {\n\t\t\t\t\tvar f *kratos.RegistrationFlow\n\t\t\t\t\tvar hc *http.Client\n\t\t\t\t\tvar payload string\n\t\t\t\t\tif tc.isAPI {\n\t\t\t\t\t\tf = testhelpers.InitializeRegistrationFlowViaAPICtx(t.Context(), t, apiClient, publicTS, testhelpers.InitFlowWithIdentitySchema(\"email\"))\n\t\t\t\t\t\thc = apiClient\n\t\t\t\t\t} else {\n\t\t\t\t\t\tf = testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, tc.isSPA, false, false, testhelpers.InitFlowWithIdentitySchema(\"email\"))\n\t\t\t\t\t\thc = browserClient\n\t\t\t\t\t}\n\n\t\t\t\t\texpected.Action = conf.SelfPublicURL(t.Context()).String() + registration.RouteSubmitFlow + \"?flow=\" + f.Id\n\t\t\t\t\tassertx.EqualAsJSON(t, expected, f.Ui)\n\n\t\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\t\tvalues.Set(\"traits.email\", testhelpers.RandomEmail())\n\t\t\t\t\tvalues.Set(\"password\", x.NewUUID().String())\n\n\t\t\t\t\tvar expectedURL string\n\t\t\t\t\tif tc.isAPI {\n\t\t\t\t\t\tpayload = testhelpers.EncodeFormAsJSON(t, true, values)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpayload = values.Encode()\n\t\t\t\t\t}\n\t\t\t\t\tif tc.name == \"browser\" {\n\t\t\t\t\t\texpectedURL = redirTS.URL\n\t\t\t\t\t} else {\n\t\t\t\t\t\texpectedURL = conf.SelfPublicURL(t.Context()).String() + login.RouteSubmitFlow\n\t\t\t\t\t}\n\n\t\t\t\t\tactual, resp := testhelpers.RegistrationMakeRequest(t, tc.isAPI, tc.isSPA, f, hc, payload)\n\t\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\n\t\t\t\t\tif tc.isAPI {\n\t\t\t\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"identity.id\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.Equal(t, gjson.Get(actual, \"identity.traits.email\").String(), values.Get(\"traits.email\"), \"%s\", actual)\n\t\t\t\t\t\tassert.Equal(t, gjson.Get(actual, \"identity.schema_id\").String(), \"email\", \"%s\", actual)\n\t\t\t\t\t}\n\n\t\t\t\t\tidentity, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypePassword, values.Get(\"traits.email\"))\n\t\t\t\t\trequire.NoError(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t\tassert.NotEmpty(t, identity.ID)\n\t\t\t\t\tassert.Equalf(t, values.Get(\"traits.email\"), gjson.GetBytes(identity.Traits, \"email\").String(), \"%s\", identity.Traits)\n\t\t\t\t\tassert.Equal(t, \"email\", identity.SchemaID)\n\n\t\t\t\t\t// login\n\t\t\t\t\tloginValues := func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"identifier\", values.Get(\"traits.email\"))\n\t\t\t\t\t\tv.Set(\"password\", values.Get(\"password\"))\n\t\t\t\t\t}\n\n\t\t\t\t\tbody := testhelpers.SubmitLoginForm(t, tc.isAPI, nil, publicTS, loginValues,\n\t\t\t\t\t\ttc.isSPA, false, http.StatusOK, expectedURL, testhelpers.InitFlowWithIdentitySchema(\"email\"))\n\n\t\t\t\t\tif tc.name == \"browser\" {\n\t\t\t\t\t\tassert.Equalf(t, identity.ID.String(), gjson.Get(body, \"identity.id\").String(), \"%s\", body)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equalf(t, identity.ID.String(), gjson.Get(body, \"session.identity.id\").String(), \"%s\", body)\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=\"+tc.name+\" registration fail due to invalid form data\", func(t *testing.T) {\n\t\t\t\t\tvar f *kratos.RegistrationFlow\n\t\t\t\t\tvar hc *http.Client\n\t\t\t\t\tvar payload string\n\t\t\t\t\tif tc.isAPI {\n\t\t\t\t\t\tf = testhelpers.InitializeRegistrationFlowViaAPICtx(t.Context(), t, apiClient, publicTS, testhelpers.InitFlowWithIdentitySchema(\"email\"))\n\t\t\t\t\t\thc = apiClient\n\t\t\t\t\t} else {\n\t\t\t\t\t\tf = testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, tc.isSPA, false, false, testhelpers.InitFlowWithIdentitySchema(\"email\"))\n\t\t\t\t\t\thc = browserClient\n\t\t\t\t\t}\n\n\t\t\t\t\texpected.Action = conf.SelfPublicURL(t.Context()).String() + registration.RouteSubmitFlow + \"?flow=\" + f.Id\n\t\t\t\t\tassertx.EqualAsJSON(t, expected, f.Ui)\n\n\t\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\t\tvalues.Set(\"traits.email\", \"invalidemail\")\n\t\t\t\t\tvalues.Set(\"password\", x.NewUUID().String())\n\n\t\t\t\t\tif tc.isAPI {\n\t\t\t\t\t\tpayload = testhelpers.EncodeFormAsJSON(t, true, values)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpayload = values.Encode()\n\t\t\t\t\t}\n\n\t\t\t\t\tactual, resp := testhelpers.RegistrationMakeRequest(t, tc.isAPI, tc.isSPA, f, hc, payload)\n\t\t\t\t\tif tc.name == \"browser\" {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusOK, resp.StatusCode)\n\t\t\t\t\t} else {\n\t\t\t\t\t\trequire.EqualValues(t, http.StatusBadRequest, resp.StatusCode)\n\t\t\t\t\t}\n\n\t\t\t\t\tassert.EqualValuesf(t, 4000040, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.email).messages.0.id\").Int(), \"%s\", actual)\n\t\t\t\t\tassert.Equalf(t, \"Enter a valid email address\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.email).messages.0.text\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser schema=default\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"phone\")\n\t\t\t\t})\n\t\t\t\t//  We set default to email\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"email\")\n\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, false)\n\n\t\t\t\texpected.Action = conf.SelfPublicURL(t.Context()).String() + registration.RouteSubmitFlow + \"?flow=\" + f.Id\n\t\t\t\tassertx.EqualAsJSON(t, expected, f.Ui)\n\t\t\t})\n\n\t\t\tt.Run(\"type=api schema=default\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\tconf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"phone\")\n\t\t\t\t})\n\t\t\t\t//  We set default to email\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"email\")\n\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaAPICtx(t.Context(), t, apiClient, publicTS)\n\n\t\t\t\texpected.Action = conf.SelfPublicURL(t.Context()).String() + registration.RouteSubmitFlow + \"?flow=\" + f.Id\n\t\t\t\tassertx.EqualAsJSON(t, expected, f.Ui)\n\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tvalues.Set(\"traits.email\", testhelpers.RandomEmail())\n\t\t\t\tvalues.Set(\"password\", x.NewUUID().String())\n\n\t\t\t\tactual, _ := testhelpers.RegistrationMakeRequest(t, true, false, f, apiClient, testhelpers.EncodeFormAsJSON(t, true, values))\n\n\t\t\t\tassert.NotEmptyf(t, gjson.Get(actual, \"identity.id\").String(), \"%s\", actual)\n\t\t\t\tassert.Containsf(t, gjson.Get(actual, \"identity.traits.email\").String(), values.Get(\"email\"), \"%s\", actual)\n\t\t\t\tassert.Containsf(t, gjson.Get(actual, \"identity.schema_id\").String(), \"email\", \"%s\", actual)\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser schema=does-not-exist\", func(t *testing.T) {\n\t\t\t\ttesthelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, true, testhelpers.InitFlowWithIdentitySchema(\"does-not-exist\"))\n\n\t\t\t\ttesthelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, true, testhelpers.InitFlowWithIdentitySchema(\"does-not-exist\"))\n\t\t\t})\n\n\t\t\tt.Run(\"type=api schema=does-not-exist\", func(t *testing.T) {\n\t\t\t\ttesthelpers.InitializeRegistrationFlowViaAPIExpectError(t, apiClient, publicTS, testhelpers.InitFlowWithIdentitySchema(\"does-not-exist\"))\n\n\t\t\t\ttesthelpers.InitializeLoginFlowViaAPIExpectError(t.Context(), t, apiClient, publicTS, false, testhelpers.InitFlowWithIdentitySchema(\"does-not-exist\"))\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser schema=not-allowed\", func(t *testing.T) {\n\t\t\t\ttesthelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, true, testhelpers.InitFlowWithIdentitySchema(\"not-allowed\"))\n\n\t\t\t\ttesthelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, true, testhelpers.InitFlowWithIdentitySchema(\"not-allowed\"))\n\t\t\t})\n\n\t\t\tt.Run(\"type=api schema=not-allowed\", func(t *testing.T) {\n\t\t\t\ttesthelpers.InitializeRegistrationFlowViaAPIExpectError(t, apiClient, publicTS, testhelpers.InitFlowWithIdentitySchema(\"not-allowed\"))\n\n\t\t\t\ttesthelpers.InitializeLoginFlowViaAPIExpectError(t.Context(), t, apiClient, publicTS, false, testhelpers.InitFlowWithIdentitySchema(\"not-allowed\"))\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateSignUpMethod\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\t\tconfigx.WithValues(map[string]any{\n\t\t\t\tconfig.ViperKeyPublicBaseURL:                              \"https://foo/\",\n\t\t\t\tconfig.ViperKeySelfServiceRegistrationEnableLegacyOneStep: true,\n\t\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypePassword) + \".enabled\": true,\n\t\t\t}),\n\t\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://stub/sort.schema.json\")),\n\t\t)\n\n\t\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\t\t_ = testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\n\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\tf := testhelpers.InitializeRegistrationFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, false)\n\n\t\tassertx.EqualAsJSON(t, container.Container{\n\t\t\tAction: conf.SelfPublicURL(t.Context()).String() + registration.RouteSubmitFlow + \"?flow=\" + f.Id,\n\t\t\tMethod: \"POST\",\n\t\t\tNodes: node.Nodes{\n\t\t\t\tnode.NewCSRFNode(nosurfx.FakeCSRFToken),\n\t\t\t\tnode.NewInputField(\"traits.username\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\tnode.NewInputField(\"password\", nil, node.PasswordGroup, node.InputAttributeTypePassword, node.WithRequiredInputAttribute, node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t\t\ta.Autocomplete = node.InputAttributeAutocompleteNewPassword\n\t\t\t\t})).WithMetaLabel(text.NewInfoNodeInputPassword()),\n\t\t\t\tnode.NewInputField(\"traits.bar\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\tnode.NewInputField(\"method\", \"password\", node.PasswordGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoRegistration()),\n\t\t\t},\n\t\t}, f.Ui)\n\t})\n}\n\nfunc TestPopulateRegistrationMethod(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://stub/identity.schema.json\")),\n\t)\n\n\ts, err := reg.AllRegistrationStrategies().Strategy(identity.CredentialsTypePassword)\n\trequire.NoError(t, err)\n\tfh, ok := s.(registration.FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f node.Nodes) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f, snapshotx.ExceptNestedKeys(\"nonce\", \"src\"))\n\t}\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *registration.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/registration/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := registration.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateRegistrationMethod\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethod(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodProfile\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodCredentials\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=idempotency\", func(t *testing.T) {\n\t\tr, f := newFlow(t.Context(), t)\n\n\t\tvar snapshots []node.Nodes\n\n\t\tt.Run(\"case=1\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=2\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=3\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=4\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=evaluate\", func(t *testing.T) {\n\t\t\tassertx.EqualAsJSON(t, snapshots[0], snapshots[2])\n\t\t\tassertx.EqualAsJSON(t, snapshots[1], snapshots[3])\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/password/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed .schema/login.schema.json\nvar loginSchema []byte\n\n//go:embed .schema/registration.schema.json\nvar registrationSchema []byte\n\n//go:embed .schema/settings.schema.json\nvar settingsSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/password/settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/ory/kratos/session\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n)\n\nfunc (s *Strategy) SettingsStrategyID() string {\n\treturn identity.CredentialsTypePassword.String()\n}\n\n// Update Settings Flow with Password Method\n//\n// swagger:model updateSettingsFlowWithPasswordMethod\ntype updateSettingsFlowWithPasswordMethod struct {\n\t// Password is the updated password\n\t//\n\t// required: true\n\tPassword string `json:\"password\"`\n\n\t// CSRFToken is the anti-CSRF token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Method\n\t//\n\t// Should be set to password when trying to update a password.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Flow is flow ID.\n\t//\n\t// swagger:ignore\n\tFlow string `json:\"flow\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (p *updateSettingsFlowWithPasswordMethod) GetFlowID() uuid.UUID {\n\treturn x.ParseUUID(p.Flow)\n}\n\nfunc (p *updateSettingsFlowWithPasswordMethod) SetFlowID(rid uuid.UUID) {\n\tp.Flow = rid.String()\n}\n\nfunc (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.password.Strategy.Settings\")\n\tdefer otelx.End(span, &err)\n\n\tvar p updateSettingsFlowWithPasswordMethod\n\tctxUpdate, err := settings.PrepareUpdate(s.d, w, r, f, ss, settings.ContinuityKey(s.SettingsStrategyID()), &p)\n\tif errors.Is(err, settings.ErrContinuePreviousAction) {\n\t\treturn ctxUpdate, s.continueSettingsFlow(ctx, r, ctxUpdate, p)\n\t} else if err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.SettingsStrategyID(), s.d); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif err := s.decodeSettingsFlow(r, &p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\t// This does not come from the payload!\n\tp.Flow = ctxUpdate.Flow.ID.String()\n\tif err := s.continueSettingsFlow(ctx, r, ctxUpdate, p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\treturn ctxUpdate, nil\n}\n\nfunc (s *Strategy) decodeSettingsFlow(r *http.Request, dest interface{}) error {\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(settingsSchema)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn decoderx.Decode(r, dest, compiler,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t)\n}\n\n// Try to find a password hash in the credentials. Returns it if found, otherwise return an empty string.\nfunc getPasswordHashFromCredential(creds map[identity.CredentialsType]identity.Credentials) string {\n\tif creds == nil {\n\t\treturn \"\"\n\t}\n\n\tcred, ok := creds[identity.CredentialsTypePassword]\n\tif !ok {\n\t\treturn \"\"\n\t}\n\n\tvar hashedPassword identity.CredentialsPassword\n\tif err := json.Unmarshal(cred.Config, &hashedPassword); err != nil {\n\t\treturn \"\"\n\t}\n\treturn hashedPassword.HashedPassword\n}\n\n// Detect whether the new password is the same as the old password.\n// This is helpful to a user, e.g. in the case of a password leak: they want to change their password,\n// and unknowingly set the new password to be the same as the old one (that leaked). We force them to\n// set a different password in that case.\nfunc isNewPasswordSameAsOld(ctx context.Context, oldHashedPassword string, newPassword string) bool {\n\tif oldHashedPassword == \"\" {\n\t\treturn false\n\t}\n\n\t// `hash.Compare` returns `nil` on 'success' i.e. old and new are the same.\n\treturn hash.Compare(ctx, []byte(newPassword), []byte(oldHashedPassword)) == nil\n}\n\nfunc (s *Strategy) continueSettingsFlow(ctx context.Context, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithPasswordMethod) error {\n\tif err := flow.MethodEnabledAndAllowed(ctx, flow.SettingsFlow, s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\treturn err\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, ctxUpdate.Flow.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn err\n\t}\n\n\tif ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) {\n\t\treturn errors.WithStack(settings.NewFlowNeedsReAuth())\n\t}\n\n\tif len(p.Password) == 0 {\n\t\treturn schema.NewRequiredError(\"#/password\", \"password\")\n\t}\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.Identity.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tg, ctx := errgroup.WithContext(ctx)\n\tvar newPasswordHash []byte\n\t// Extract an immutable value to avoid data races between goroutines.\n\toldHashedPassword := getPasswordHashFromCredential(i.Credentials)\n\n\t// Do in parallel due to limitations of the `bcrypt` library and for performance:\n\t// - `hash(newPassword)` (expensive).\n\t// - Check that the new password is not the same as the old password,\n\t//   which internally computes `hash(newPassword)` (expensive).\n\t// - `validateCredentials` which may call the HaveIBeenPawned external API.\n\tg.Go(func() error {\n\t\tvar err error\n\t\tnewPasswordHash, err = s.d.Hasher(ctx).Generate(ctx, []byte(p.Password))\n\t\treturn err\n\t})\n\tg.Go(func() error {\n\t\tif isNewPasswordSameAsOld(ctx, oldHashedPassword, p.Password) {\n\t\t\treturn schema.NewPasswordPolicyViolationError(\"#/password\", text.NewErrorValidationPasswordNewSameAsOld())\n\t\t}\n\t\treturn nil\n\t})\n\tg.Go(func() error {\n\t\t// Note: this goroutine mutates `i` so careful not to share it with other goroutines!\n\n\t\t// The credentials could have been modified in many ways possible. To keep it simple, we reset, and the validators\n\t\t// will populate it correctly.\n\t\ti.UpsertCredentialsConfig(s.ID(), []byte(\"{}\"), 0)\n\t\treturn s.validateCredentials(ctx, i, p.Password)\n\t})\n\tif err := g.Wait(); err != nil {\n\t\treturn err\n\t}\n\n\tco, err := json.Marshal(&identity.CredentialsPassword{HashedPassword: string(newPasswordHash)})\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode password options to JSON: %s\", err))\n\t}\n\ti.UpsertCredentialsConfig(s.ID(), co, 0)\n\tctxUpdate.UpdateIdentity(i)\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, _ *identity.Identity, f *settings.Flow) (err error) {\n\t_, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.password.Strategy.PopulateSettingsMethod\")\n\tdefer otelx.End(span, &err)\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.Nodes.Upsert(NewPasswordNode(\"password\", node.InputAttributeAutocompleteNewPassword).WithMetaLabel(text.NewInfoNodeInputPassword()))\n\tf.UI.Nodes.Append(node.NewInputField(\"method\", \"password\", node.PasswordGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelSave()))\n\n\treturn nil\n}\n\nfunc (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithPasswordMethod, err error) error {\n\t// Do not pause flow if the flow type is an API flow as we can't save cookies in those flows.\n\tif e := new(settings.FlowNeedsReAuth); errors.As(err, &e) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser {\n\t\tif err := s.d.ContinuityManager().Pause(ctx, w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif ctxUpdate.Flow != nil {\n\t\tctxUpdate.Flow.UI.ResetMessages()\n\t\tctxUpdate.Flow.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "selfservice/strategy/password/settings_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/client-go\"\n\t\"github.com/ory/kratos/corpx\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/settingshelpers\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/randx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc newIdentityWithPassword(email string) *identity.Identity {\n\treturn &identity.Identity{\n\t\tID:  x.NewUUID(),\n\t\tNID: x.NewUUID(),\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\"password\": {\n\t\t\t\tType:        \"password\",\n\t\t\t\tIdentifiers: []string{email},\n\t\t\t\tConfig:      []byte(`{\"hashed_password\":\"foo\"}`),\n\t\t\t},\n\t\t},\n\t\tState:    identity.StateActive,\n\t\tTraits:   identity.Traits(`{\"email\":\"` + email + `\"}`),\n\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t}\n}\n\nfunc newEmptyIdentity() *identity.Identity {\n\treturn &identity.Identity{\n\t\tID:       x.NewUUID(),\n\t\tNID:      x.NewUUID(),\n\t\tState:    identity.StateActive,\n\t\tTraits:   identity.Traits(`{}`),\n\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t}\n}\n\nfunc newIdentityWithoutCredentials(email string) *identity.Identity {\n\treturn &identity.Identity{\n\t\tID:       x.NewUUID(),\n\t\tNID:      x.NewUUID(),\n\t\tState:    identity.StateActive,\n\t\tTraits:   identity.Traits(`{\"email\":\"` + email + `\"}`),\n\t\tSchemaID: config.DefaultIdentityTraitsSchemaID,\n\t}\n}\n\nfunc TestSettings(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/profile.schema.json\")),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, true)),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(settings.StrategyProfile, true)),\n\t\tconfigx.WithValue(config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"1m\"),\n\t)\n\n\tsettingsUI := testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewLoginUIWith401Response(t, conf)\n\n\tbrowserIdentity1 := newIdentityWithPassword(\"john-browser@doe.com\")\n\tapiIdentity1 := newIdentityWithPassword(\"john-api@doe.com\")\n\tbrowserIdentity2 := newEmptyIdentity()\n\tapiIdentity2 := newEmptyIdentity()\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\tbrowserUser1 := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, browserIdentity1)\n\tbrowserUser1.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\tbrowserUser2 := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, browserIdentity2)\n\tbrowserUser2.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\tapiUser1 := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, apiIdentity1)\n\tapiUser2 := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, apiIdentity2)\n\n\tt.Run(\"case=should reject a new password if it is the same as the old one\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(context.Background(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\t\t})\n\n\t\tcfg := client.NewConfiguration()\n\t\tu, err := url.Parse(publicTS.URL)\n\t\trequire.NoError(t, err)\n\t\tcfg.Scheme = u.Scheme\n\t\tcfg.Host = u.Host\n\t\tcl := client.NewAPIClient(cfg)\n\t\tapi := cl.FrontendAPI\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t// Create a new account.\n\t\t\tpassword := uuid.Must(uuid.NewV4()).String()\n\t\t\tvar sessionToken string\n\t\t\t{\n\t\t\t\tregistrationFlow, _, err := api.CreateNativeRegistrationFlow(t.Context()).Execute()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, registrationFlow)\n\n\t\t\t\tregistrationBody := client.UpdateRegistrationFlowBody{\n\t\t\t\t\tUpdateRegistrationFlowWithPasswordMethod: &client.UpdateRegistrationFlowWithPasswordMethod{\n\t\t\t\t\t\tMethod:   \"password\",\n\t\t\t\t\t\tPassword: password,\n\t\t\t\t\t\tTraits: map[string]any{\n\t\t\t\t\t\t\t\"email\": uuid.Must(uuid.NewV4()).String() + \"@ory.dev\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tregistration, _, err := api.UpdateRegistrationFlow(t.Context()).Flow(registrationFlow.Id).UpdateRegistrationFlowBody(registrationBody).Execute()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, registration)\n\t\t\t\trequire.NotNil(t, registration.SessionToken)\n\n\t\t\t\tsessionToken = *registration.SessionToken\n\t\t\t\trequire.NotEmpty(t, sessionToken)\n\t\t\t}\n\n\t\t\t// Create a settings flow.\n\t\t\tvar settingsFlow *client.SettingsFlow\n\t\t\t{\n\n\t\t\t\tvar err error\n\t\t\t\tsettingsFlow, _, err = api.CreateNativeSettingsFlow(t.Context()).XSessionToken(sessionToken).Execute()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, settingsFlow)\n\t\t\t}\n\n\t\t\t// Try to set the same password: fails.\n\t\t\t{\n\t\t\t\tupdate := client.UpdateSettingsFlowBody{\n\t\t\t\t\tUpdateSettingsFlowWithPasswordMethod: &client.UpdateSettingsFlowWithPasswordMethod{\n\t\t\t\t\t\tMethod:   \"password\",\n\t\t\t\t\t\tPassword: password,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\treq := api.UpdateSettingsFlow(t.Context()).UpdateSettingsFlowBody(update).Flow(settingsFlow.Id).XSessionToken(sessionToken)\n\t\t\t\tsettingsFlow, httpResp, err := api.UpdateSettingsFlowExecute(req)\n\t\t\t\trequire.Error(t, err)\n\t\t\t\trequire.Nil(t, settingsFlow)\n\t\t\t\trequire.NotNil(t, httpResp)\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, httpResp.StatusCode)\n\t\t\t}\n\n\t\t\t// Try to set a different password: succeeds.\n\t\t\t{\n\t\t\t\tupdate := client.UpdateSettingsFlowBody{\n\t\t\t\t\tUpdateSettingsFlowWithPasswordMethod: &client.UpdateSettingsFlowWithPasswordMethod{\n\t\t\t\t\t\tMethod:   \"password\",\n\t\t\t\t\t\tPassword: uuid.Must(uuid.NewV4()).String(),\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\treq := api.UpdateSettingsFlow(t.Context()).UpdateSettingsFlowBody(update).Flow(settingsFlow.Id).XSessionToken(sessionToken)\n\t\t\t\tsettingsFlow, httpResp, err := api.UpdateSettingsFlowExecute(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, settingsFlow)\n\t\t\t\trequire.NotNil(t, httpResp)\n\t\t\t\trequire.Equal(t, http.StatusOK, httpResp.StatusCode)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t// Create a new account.\n\t\t\tpassword := uuid.Must(uuid.NewV4()).String()\n\t\t\tvar cookie string\n\t\t\t{\n\t\t\t\tregistrationFlow, _, err := api.CreateBrowserRegistrationFlow(t.Context()).Execute()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, registrationFlow)\n\n\t\t\t\tcsrfToken := registrationFlow.Ui.Nodes[0].Attributes.UiNodeInputAttributes.Value.(string)\n\t\t\t\trequire.NotEmpty(t, csrfToken)\n\n\t\t\t\tregistrationBody := client.UpdateRegistrationFlowBody{\n\t\t\t\t\tUpdateRegistrationFlowWithPasswordMethod: &client.UpdateRegistrationFlowWithPasswordMethod{\n\t\t\t\t\t\tMethod:   \"password\",\n\t\t\t\t\t\tPassword: password,\n\t\t\t\t\t\tTraits: map[string]any{\n\t\t\t\t\t\t\t\"email\": uuid.Must(uuid.NewV4()).String() + \"@ory.dev\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tCsrfToken: &csrfToken,\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tregistration, httpResp, err := api.UpdateRegistrationFlow(t.Context()).Flow(registrationFlow.Id).UpdateRegistrationFlowBody(registrationBody).Execute()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, httpResp)\n\t\t\t\trequire.NotNil(t, registration)\n\t\t\t\tcookie = httpResp.Header.Get(\"Set-Cookie\")\n\t\t\t\trequire.NotEmpty(t, cookie)\n\t\t\t}\n\n\t\t\t// Create a settings flow.\n\t\t\tvar settingsFlow *client.SettingsFlow\n\t\t\tvar csrfToken string\n\t\t\t{\n\n\t\t\t\tvar err error\n\t\t\t\tsettingsFlow, _, err = api.CreateBrowserSettingsFlow(t.Context()).Cookie(cookie).Execute()\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, settingsFlow)\n\n\t\t\t\tcsrfToken = settingsFlow.Ui.Nodes[0].Attributes.UiNodeInputAttributes.Value.(string)\n\t\t\t\trequire.NotEmpty(t, csrfToken)\n\t\t\t}\n\n\t\t\t// Try to set the same password: fails.\n\t\t\t{\n\t\t\t\tupdate := client.UpdateSettingsFlowBody{\n\t\t\t\t\tUpdateSettingsFlowWithPasswordMethod: &client.UpdateSettingsFlowWithPasswordMethod{\n\t\t\t\t\t\tMethod:    \"password\",\n\t\t\t\t\t\tPassword:  password,\n\t\t\t\t\t\tCsrfToken: &csrfToken,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\treq := api.UpdateSettingsFlow(t.Context()).UpdateSettingsFlowBody(update).Flow(settingsFlow.Id).Cookie(cookie)\n\t\t\t\tsettingsFlow, httpResp, err := api.UpdateSettingsFlowExecute(req)\n\t\t\t\trequire.Error(t, err)\n\t\t\t\trequire.Nil(t, settingsFlow)\n\t\t\t\trequire.NotNil(t, httpResp)\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, httpResp.StatusCode)\n\t\t\t}\n\n\t\t\t// Try to set a different password: succeeds.\n\t\t\t{\n\t\t\t\tupdate := client.UpdateSettingsFlowBody{\n\t\t\t\t\tUpdateSettingsFlowWithPasswordMethod: &client.UpdateSettingsFlowWithPasswordMethod{\n\t\t\t\t\t\tMethod:    \"password\",\n\t\t\t\t\t\tPassword:  uuid.Must(uuid.NewV4()).String(),\n\t\t\t\t\t\tCsrfToken: &csrfToken,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\treq := api.UpdateSettingsFlow(t.Context()).UpdateSettingsFlowBody(update).Flow(settingsFlow.Id).Cookie(cookie)\n\t\t\t\tsettingsFlow, httpResp, err := api.UpdateSettingsFlowExecute(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NotNil(t, settingsFlow)\n\t\t\t\trequire.NotNil(t, httpResp)\n\t\t\t\trequire.Equal(t, http.StatusOK, httpResp.StatusCode)\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"description=not authorized to call endpoints without a session\", func(t *testing.T) {\n\t\tc := testhelpers.NewDebugClient(t)\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tres, err := c.Do(httpx.MustNewRequest(\"POST\", publicTS.URL+settings.RouteSubmitFlow, strings.NewReader(url.Values{\"foo\": {\"bar\"}}.Encode()), \"application/x-www-form-urlencoded\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode, \"%+v\", res.Request)\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.GetProvider(t.Context()).String(config.ViperKeySelfServiceLoginUI))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tres, err := c.Do(httpx.MustNewRequest(\"POST\", publicTS.URL+settings.RouteSubmitFlow, strings.NewReader(url.Values{\"foo\": {\"bar\"}}.Encode()), \"application/json\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode, \"%+v\", res.Request)\n\t\t\tassert.Contains(t, res.Request.URL.String(), settings.RouteSubmitFlow)\n\t\t})\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tres, err := c.Do(httpx.MustNewRequest(\"POST\", publicTS.URL+settings.RouteSubmitFlow, strings.NewReader(`{\"foo\":\"bar\"}`), \"application/json\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Len(t, res.Cookies(), 0)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode)\n\t\t})\n\t})\n\n\texpectValidationError := func(t *testing.T, isAPI, isSPA bool, hc *http.Client, values func(url.Values)) string {\n\t\treturn testhelpers.SubmitSettingsForm(t, isAPI, isSPA, hc, publicTS, values,\n\t\t\ttesthelpers.ExpectStatusCode(isAPI || isSPA, http.StatusBadRequest, http.StatusOK),\n\t\t\ttesthelpers.ExpectURL(isAPI || isSPA, publicTS.URL+settings.RouteSubmitFlow, conf.SelfServiceFlowSettingsUI(t.Context()).String()))\n\t}\n\n\tt.Run(\"description=should fail if password violates policy\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, reason, actual string) {\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==password).attributes.value\").String(), \"%s\", actual)\n\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, reason, gjson.Get(actual, \"ui.nodes.#(attributes.name==password).messages.0.text\").String(), \"%s\", actual)\n\t\t}\n\n\t\tt.Run(\"session=with privileged session\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"5m\")\n\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"password\", \"123456\")\n\t\t\t\tv.Set(\"method\", \"password\")\n\t\t\t}\n\n\t\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\t\tcheck(t, \"The password must be at least 8 characters long, but got 6.\", expectValidationError(t, true, false, apiUser1, payload))\n\t\t\t})\n\n\t\t\tt.Run(\"spa=spa\", func(t *testing.T) {\n\t\t\t\tcheck(t, \"The password must be at least 8 characters long, but got 6.\", expectValidationError(t, false, true, browserUser1, payload))\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tcheck(t, \"The password must be at least 8 characters long, but got 6.\", expectValidationError(t, false, false, browserUser1, payload))\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"session=needs reauthentication\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"1ns\")\n\t\t\tdefer testhelpers.NewLoginUIWith401Response(t, conf)\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"5m\")\n\t\t\t})\n\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"method\", \"password\")\n\t\t\t\tv.Set(\"password\", \"123456\")\n\t\t\t}\n\n\t\t\tt.Run(\"type=api/expected=an error because reauth can not be initialized for API clients\", func(t *testing.T) {\n\t\t\t\t_ = testhelpers.NewSettingsLoginAcceptAPIServer(t, testhelpers.NewSDKCustomClient(publicTS, apiUser1), conf)\n\t\t\t\tactual := testhelpers.SubmitSettingsForm(t, true, false, apiUser1, publicTS, payload,\n\t\t\t\t\thttp.StatusForbidden, publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(actual), []string{\"redirect_browser_to\"})\n\t\t\t\tassert.NotEmpty(t, json.RawMessage(gjson.Get(actual, \"redirect_browser_to\").String()))\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\t_ = testhelpers.NewSettingsLoginAcceptAPIServer(t, testhelpers.NewSDKCustomClient(publicTS, browserUser1), conf)\n\t\t\t\tactual := testhelpers.SubmitSettingsForm(t, false, true, browserUser1, publicTS, payload,\n\t\t\t\t\thttp.StatusForbidden, publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\tassertx.EqualAsJSON(t, settings.NewFlowNeedsReAuth().DefaultError, json.RawMessage(gjson.Get(actual, \"error\").Raw))\n\t\t\t\tassert.NotEmpty(t, json.RawMessage(gjson.Get(actual, \"redirect_browser_to\").String()))\n\t\t\t})\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t_ = testhelpers.NewSettingsLoginAcceptAPIServer(t, testhelpers.NewSDKCustomClient(publicTS, browserUser1), conf)\n\t\t\t\tcheck(t, \"The password must be at least 8 characters long, but got 6.\", expectValidationError(t, false, false, browserUser1, payload))\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"description=should not be able to make requests for another user\", func(t *testing.T) {\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiUser1, publicTS)\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tvalues.Set(\"method\", \"password\")\n\t\t\tvalues.Set(\"password\", x.NewUUID().String())\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, f, apiUser2, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"error.reason\").String(), \"initiated by someone else\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, true, publicTS)\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tvalues.Set(\"method\", \"password\")\n\t\t\tvalues.Set(\"password\", x.NewUUID().String())\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, true, f, browserUser2, values.Encode())\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"error.reason\").String(), \"initiated by someone else\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, false, publicTS)\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tvalues.Set(\"method\", \"password\")\n\t\t\tvalues.Set(\"password\", x.NewUUID().String())\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, false, f, browserUser2, values.Encode())\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"reason\").String(), \"initiated by someone else\", \"%s\", actual)\n\t\t})\n\t})\n\n\tt.Run(\"description=should update the password and clear errors if everything is ok\", func(t *testing.T) {\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.Equal(t, \"success\", gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==password).value\").String(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==password).messages.0.text\").String(), actual)\n\t\t}\n\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"method\", \"password\")\n\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual := testhelpers.SubmitSettingsForm(t, true, false, apiUser1, publicTS, payload, http.StatusOK, publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tcheck(t, actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tactual := testhelpers.SubmitSettingsForm(t, false, true, browserUser1, publicTS, payload, http.StatusOK, publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tcheck(t, actual)\n\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(actual, \"continue_with.0.action\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"continue_with.0.redirect_browser_to\").String(), settingsUI.URL, \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tactual := testhelpers.SubmitSettingsForm(t, false, false, browserUser1, publicTS, payload, http.StatusOK, conf.SelfServiceFlowSettingsUI(t.Context()).String())\n\t\t\tcheck(t, actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail because of missing CSRF token/type=browser\", func(t *testing.T) {\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, false, publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", \"password\")\n\t\tvalues.Set(\"password\", x.NewUUID().String())\n\t\tvalues.Set(\"csrf_token\", \"invalid_token\")\n\n\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, false, f, browserUser1, values.Encode())\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\tassert.Contains(t, res.Request.URL.String(), conf.GetProvider(t.Context()).String(config.ViperKeySelfServiceErrorUI))\n\n\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenServerTokenMismatch, json.RawMessage(actual), \"%s\", actual)\n\t})\n\n\tt.Run(\"case=should pass even without CSRF token/type=spa\", func(t *testing.T) {\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, true, publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", \"password\")\n\t\tvalues.Set(\"password\", x.NewUUID().String())\n\t\tvalues.Set(\"csrf_token\", \"invalid_token\")\n\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, true, f, browserUser1, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch, json.RawMessage(gjson.Get(actual, \"error\").Raw), \"%s\", actual)\n\t})\n\n\tt.Run(\"case=should pass even without CSRF token/type=api\", func(t *testing.T) {\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiUser1, publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", \"password\")\n\t\tvalues.Set(\"password\", x.NewUUID().String())\n\t\tvalues.Set(\"csrf_token\", \"invalid_token\")\n\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, f, apiUser1, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\tassert.NotEmpty(t, gjson.Get(actual, \"identity.id\").String(), \"%s\", actual)\n\t})\n\n\tt.Run(\"case=should fail with correct CSRF error cause/type=api\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\tmod func(http.Header)\n\t\t\texp string\n\t\t}{\n\t\t\t{\n\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\th.Add(\"Cookie\", \"name=bar\")\n\t\t\t\t},\n\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Cookie\\\\\\\" key\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\th.Add(\"Origin\", \"www.bar.com\")\n\t\t\t\t},\n\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Origin\\\\\\\" key\",\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiUser1, publicTS)\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\t\tvalues.Set(\"password\", x.NewUUID().String())\n\n\t\t\t\treq := testhelpers.NewPostRequest(t, true, f.Ui.Action, bytes.NewBufferString(testhelpers.EncodeFormAsJSON(t, true, values)))\n\t\t\t\ttc.mod(req.Header)\n\n\t\t\t\tres, err := apiUser1.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tactual := string(ioutilx.MustReadAll(res.Body))\n\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\tassert.Contains(t, actual, tc.exp)\n\t\t\t})\n\t\t}\n\t})\n\n\texpectSuccess := func(t *testing.T, isAPI, isSPA bool, hc *http.Client, values func(url.Values)) string {\n\t\treturn testhelpers.SubmitSettingsForm(t, isAPI, isSPA, hc, publicTS, values, http.StatusOK,\n\t\t\ttesthelpers.ExpectURL(isAPI || isSPA, publicTS.URL+settings.RouteSubmitFlow, conf.SelfServiceFlowSettingsUI(t.Context()).String()))\n\t}\n\n\tt.Run(\"description=should update the password even if no password was set before\", func(t *testing.T) {\n\t\tbi := newIdentityWithoutCredentials(x.NewUUID().String() + \"@ory.sh\")\n\t\tsi := newIdentityWithoutCredentials(x.NewUUID().String() + \"@ory.sh\")\n\t\tai := newIdentityWithoutCredentials(x.NewUUID().String() + \"@ory.sh\")\n\t\tbrowserUser := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, bi)\n\t\tspaUser := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, si)\n\t\tapiUser := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, ai)\n\n\t\tcheck := func(t *testing.T, actual string, id *identity.Identity) {\n\t\t\tassert.Equal(t, \"success\", gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(name==password).attributes.value\").String(), \"%s\", actual)\n\n\t\t\tactualIdentity, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), id.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tcfg := string(actualIdentity.Credentials[identity.CredentialsTypePassword].Config)\n\t\t\tassert.Contains(t, cfg, \"hashed_password\", \"%+v\", actualIdentity.Credentials)\n\t\t\trequire.Len(t, actualIdentity.Credentials[identity.CredentialsTypePassword].Identifiers, 1)\n\t\t\tassert.Contains(t, actualIdentity.Credentials[identity.CredentialsTypePassword].Identifiers[0], \"-4\")\n\t\t}\n\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"method\", \"password\")\n\t\t\tv.Set(\"password\", randx.MustString(16, randx.AlphaNum))\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual := expectSuccess(t, true, false, apiUser, payload)\n\t\t\tcheck(t, actual, ai)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tactual := expectSuccess(t, false, true, spaUser, payload)\n\t\t\tcheck(t, actual, si)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tactual := expectSuccess(t, false, false, browserUser, payload)\n\t\t\tcheck(t, actual, bi)\n\t\t})\n\t})\n\n\tt.Run(\"description=should update the password and perform the correct redirection\", func(t *testing.T) {\n\t\trts := testhelpers.NewRedirTS(t, \"\", conf)\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsAfter+\".\"+config.DefaultBrowserReturnURL, rts.URL+\"/return-ts\")\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceSettingsAfter, nil)\n\t\t})\n\n\t\trun := func(t *testing.T, f *kratos.SettingsFlow, isAPI bool, c *http.Client, _ *identity.Identity) {\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tvalues.Set(\"method\", \"password\")\n\t\t\tvalues.Set(\"password\", randx.MustString(16, randx.AlphaNum))\n\t\t\t_, res := testhelpers.SettingsMakeRequest(t, isAPI, false, f, c, testhelpers.EncodeFormAsJSON(t, isAPI, values))\n\t\t\trequire.EqualValues(t, rts.URL+\"/return-ts\", res.Request.URL.String())\n\n\t\t\tactualIdentity, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), browserIdentity1.ID)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tcfg := string(actualIdentity.Credentials[identity.CredentialsTypePassword].Config)\n\t\t\tassert.NotContains(t, cfg, \"foo\")\n\t\t\tassert.NotEqual(t, `{\"hashed_password\":\"foo\"}`, cfg)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tt.Skip(\"Post-registration redirects do not work for API flows and are thus not tested here.\")\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tt.Skip(\"Post-registration redirects do not work for API flows and are thus not tested here.\")\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trs := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, false, publicTS)\n\t\t\trun(t, rs, false, browserUser1, browserIdentity1)\n\t\t})\n\t})\n\n\tt.Run(\"description=should update the password and revoke other user sessions\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceSettingsAfter, \"password\"), []config.SelfServiceHook{{Name: \"revoke_active_sessions\"}})\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(context.Background(), config.ViperKeySelfServiceSettingsAfter, nil)\n\t\t})\n\n\t\tcheck := func(t *testing.T, actual string, id *identity.Identity) {\n\t\t\tassert.Equal(t, \"success\", gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(name==password).attributes.value\").String(), \"%s\", actual)\n\n\t\t\tactualIdentity, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), id.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tcfg := string(actualIdentity.Credentials[identity.CredentialsTypePassword].Config)\n\t\t\tassert.Contains(t, cfg, \"hashed_password\", \"%+v\", actualIdentity.Credentials)\n\t\t\trequire.Len(t, actualIdentity.Credentials[identity.CredentialsTypePassword].Identifiers, 1)\n\t\t\tassert.Contains(t, actualIdentity.Credentials[identity.CredentialsTypePassword].Identifiers[0], \"-4\")\n\t\t}\n\n\t\tinitClients := func(isAPI bool, id *identity.Identity) (client1, client2 *http.Client) {\n\t\t\tif isAPI {\n\t\t\t\tclient1 = testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\t\t\tclient2 = testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\t\t\treturn client1, client2\n\t\t\t}\n\t\t\tclient1 = testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\t\tclient2 = testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\n\t\t\treturn client1, client2\n\t\t}\n\n\t\trun := func(t *testing.T, isAPI, isSPA bool, id *identity.Identity) {\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"method\", \"password\")\n\t\t\t\tv.Set(\"password\", randx.MustString(16, randx.AlphaNum))\n\t\t\t}\n\n\t\t\tuser1, user2 := initClients(isAPI, id)\n\n\t\t\tactual := expectSuccess(t, isAPI, isSPA, user1, payload)\n\t\t\tcheck(t, actual, id)\n\n\t\t\t// second client should be logged out\n\t\t\tres, err := user2.Do(httpx.MustNewRequest(\"POST\", publicTS.URL+settings.RouteSubmitFlow, strings.NewReader(url.Values{\"foo\": {\"bar\"}}.Encode()), \"application/json\"))\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode, \"%+v\", res.Request)\n\n\t\t\t// again change password via first client\n\t\t\tactual = expectSuccess(t, isAPI, isSPA, user1, payload)\n\t\t\tcheck(t, actual, id)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tid := newIdentityWithoutCredentials(x.NewUUID().String() + \"@ory.sh\")\n\t\t\trun(t, true, false, id)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tid := newIdentityWithoutCredentials(x.NewUUID().String() + \"@ory.sh\")\n\t\t\trun(t, false, true, id)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tid := newIdentityWithoutCredentials(x.NewUUID().String() + \"@ory.sh\")\n\t\t\trun(t, false, false, id)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail if no identifier was set in the schema\", func(t *testing.T) {\n\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://stub/missing-identifier.schema.json\")\n\n\t\tid := newIdentityWithoutCredentials(testhelpers.RandomEmail())\n\t\tbrowser := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\tapi := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\n\t\tfor _, f := range []string{\"spa\", \"api\", \"browser\"} {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\thc := browser\n\t\t\t\tif f == \"api\" {\n\t\t\t\t\thc = api\n\t\t\t\t}\n\t\t\t\tactual := settingshelpers.ExpectValidationError(t, publicTS, hc, conf, f, func(v url.Values) {\n\t\t\t\t\tv.Set(\"password\", x.NewUUID().String())\n\t\t\t\t\tv.Set(\"method\", \"password\")\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, text.NewErrorValidationIdentifierMissing().Text, gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/password/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n)\n\nvar (\n\t_ login.Strategy                    = (*Strategy)(nil)\n\t_ registration.Strategy             = (*Strategy)(nil)\n\t_ identity.ActiveCredentialsCounter = (*Strategy)(nil)\n)\n\ntype dependencies interface {\n\tlogrusx.Provider\n\thttpx.WriterProvider\n\tnosurfx.CSRFTokenGeneratorProvider\n\tnosurfx.CSRFProvider\n\thttpx.ClientProvider\n\totelx.Provider\n\tjsonnetsecure.VMProvider\n\tconfig.Provider\n\tcontinuity.ManagementProvider\n\n\terrorx.ManagementProvider\n\tValidationProvider\n\thash.HashProvider\n\n\tregistration.HandlerProvider\n\tregistration.HooksProvider\n\tregistration.ErrorHandlerProvider\n\tregistration.HookExecutorProvider\n\tregistration.FlowPersistenceProvider\n\n\tlogin.HooksProvider\n\tlogin.ErrorHandlerProvider\n\tlogin.HookExecutorProvider\n\tlogin.FlowPersistenceProvider\n\tlogin.HandlerProvider\n\n\tsettings.FlowPersistenceProvider\n\tsettings.HookExecutorProvider\n\tsettings.HooksProvider\n\tsettings.ErrorHandlerProvider\n\n\tidentity.PrivilegedPoolProvider\n\tidentity.ValidationProvider\n\tidentity.ManagementProvider\n\n\tsession.HandlerProvider\n\tsession.ManagementProvider\n}\n\ntype Strategy struct{ d dependencies }\n\nfunc NewStrategy(d dependencies) *Strategy { return &Strategy{d: d} }\n\nfunc (s *Strategy) CountActiveFirstFactorCredentials(ctx context.Context, cc map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\tfor _, c := range cc {\n\t\tif c.Type == s.ID() && len(c.Config) > 0 {\n\t\t\tvar conf identity.CredentialsPassword\n\t\t\tif err = json.Unmarshal(c.Config, &conf); err != nil {\n\t\t\t\treturn 0, errors.WithStack(err)\n\t\t\t}\n\n\t\t\tif len(strings.Join(c.Identifiers, \"\")) > 0 &&\n\t\t\t\t((s.d.Config().PasswordMigrationHook(ctx).Enabled && conf.UsePasswordMigrationHook) ||\n\t\t\t\t\tlen(conf.HashedPassword) > 0) {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (s *Strategy) CountActiveMultiFactorCredentials(_ context.Context, _ map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\treturn 0, nil\n}\n\nfunc (s *Strategy) ID() identity.CredentialsType {\n\treturn identity.CredentialsTypePassword\n}\n\nfunc (s *Strategy) CompletedAuthenticationMethod(_ context.Context) session.AuthenticationMethod {\n\treturn session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    identity.AuthenticatorAssuranceLevel1,\n\t}\n}\n\nfunc (s *Strategy) NodeGroup() node.UiNodeGroup {\n\treturn node.PasswordGroup\n}\n"
  },
  {
    "path": "selfservice/strategy/password/strategy_disabled_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password_test\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/x/configx\"\n)\n\nfunc TestDisabledEndpoint(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, false)),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://stub/sort.schema.json\")),\n\t)\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\tc := testhelpers.NewClientWithCookies(t)\n\tt.Run(\"case=should not login when password method is disabled\", func(t *testing.T) {\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(context.Background(), t, c, publicTS, false)\n\n\t\tres, err := c.PostForm(f.Ui.Action, url.Values{\"method\": {\"password\"}, \"password_identifier\": []string{\"identifier\"}, \"password\": []string{\"password\"}})\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tassert.Equal(t, http.StatusNotFound, res.StatusCode)\n\n\t\tb, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\", \"%s\", b)\n\t})\n\n\tt.Run(\"case=should not registration when password method is disabled\", func(t *testing.T) {\n\t\tf := testhelpers.InitializeRegistrationFlowViaAPICtx(t.Context(), t, c, publicTS)\n\n\t\tres, err := c.PostForm(f.Ui.Action, url.Values{\"method\": {\"password\"}, \"password_identifier\": []string{\"identifier\"}, \"password\": []string{\"password\"}})\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\t\tassert.Equal(t, http.StatusNotFound, res.StatusCode)\n\n\t\tb, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\", \"%s\", b)\n\t})\n\n\tt.Run(\"case=should not settings when password method is disabled\", func(t *testing.T) {\n\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://stub/login.schema.json\")\n\t\tc := testhelpers.NewHTTPClientWithArbitrarySessionCookie(context.Background(), t, reg)\n\n\t\tt.Run(\"method=GET\", func(t *testing.T) {\n\t\t\tt.Skip(\"GET is currently not supported for this endpoint.\")\n\t\t})\n\n\t\tt.Run(\"method=POST\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, c, publicTS)\n\t\t\tres, err := c.PostForm(f.Ui.Action, url.Values{\n\t\t\t\t\"method\":   {\"password\"},\n\t\t\t\t\"password\": {\"bar\"},\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.Equal(t, http.StatusNotFound, res.StatusCode)\n\n\t\t\tb, err := io.ReadAll(res.Body)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\", \"%s\", b)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/password/strategy_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password_test\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/password\"\n\t\"github.com/ory/x/contextx\"\n)\n\nfunc generateRandomConfig(t *testing.T) (identity.CredentialsPassword, []byte) {\n\tt.Helper()\n\tvar cred identity.CredentialsPassword\n\trequire.NoError(t, faker.FakeData(&cred))\n\tc, err := json.Marshal(cred)\n\trequire.NoError(t, err)\n\treturn cred, c\n}\n\nfunc TestCountActiveFirstFactorCredentials(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tstrategy := password.NewStrategy(reg)\n\n\th1, err := hash.NewHasherBcrypt(reg).Generate(t.Context(), []byte(\"a password\"))\n\trequire.NoError(t, err)\n\th2, err := reg.Hasher(t.Context()).Generate(t.Context(), []byte(\"a password\"))\n\trequire.NoError(t, err)\n\n\tt.Run(\"test regressions fixtures\", func(t *testing.T) {\n\t\t// This test ensures we do not add regressions to this method by, for example, adding a new field.\n\t\tfor k := range 100 {\n\t\t\tt.Run(fmt.Sprintf(\"run=%d\", k), func(t *testing.T) {\n\t\t\t\tcred, c := generateRandomConfig(t)\n\t\t\t\tactual, err := strategy.CountActiveFirstFactorCredentials(t.Context(), map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:        strategy.ID(),\n\t\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\t\tConfig:      c,\n\t\t\t\t}})\n\t\t\t\tassert.NoError(t, err)\n\n\t\t\t\tif len(cred.HashedPassword) == 0 && cred.UsePasswordMigrationHook {\n\t\t\t\t\t// This case is OK\n\t\t\t\t\tassert.Equal(t, 0, actual)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(t, 1, actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"with fixtures\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\tin       map[identity.CredentialsType]identity.Credentials\n\t\t\texpected int\n\t\t\tctx      context.Context\n\t\t}{\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte{},\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{\"hashed_password\": \"` + string(h1) + `\"}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:        strategy.ID(),\n\t\t\t\t\tIdentifiers: []string{\"\"},\n\t\t\t\t\tConfig:      []byte(`{\"hashed_password\": \"` + string(h1) + `\"}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:        strategy.ID(),\n\t\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\t\tConfig:      []byte(`{\"hashed_password\": \"` + string(h1) + `\"}`),\n\t\t\t\t}},\n\t\t\t\texpected: 1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:        strategy.ID(),\n\t\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\t\tConfig:      []byte(`{\"hashed_password\": \"` + string(h2) + `\"}`),\n\t\t\t\t}},\n\t\t\t\texpected: 1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:        strategy.ID(),\n\t\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\t\tConfig:      []byte(`{\"use_password_migration_hook\":true}`),\n\t\t\t\t}},\n\t\t\t\texpected: 1,\n\t\t\t\tctx:      contextx.WithConfigValue(t.Context(), config.ViperKeyPasswordMigrationHook+\".enabled\", true),\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:        strategy.ID(),\n\t\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\t\tConfig:      []byte(`{\"use_password_migration_hook\":true}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t\tctx:      contextx.WithConfigValue(t.Context(), config.ViperKeyPasswordMigrationHook+\".enabled\", false),\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:        strategy.ID(),\n\t\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\t\tConfig:      []byte(`{\"use_password_migration_hook\":false}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{\"hashed_password\": \"asdf\"}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin:       nil,\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tactual, err := strategy.CountActiveFirstFactorCredentials(cmp.Or(tc.ctx, t.Context()), tc.in)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.Equal(t, tc.expected, actual)\n\n\t\t\t\tactual, err = strategy.CountActiveMultiFactorCredentials(cmp.Or(tc.ctx, t.Context()), tc.in)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.Equal(t, 0, actual)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/password/stub/email.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/identity.email.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"totp\": {\n                \"account_name\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              },\n              \"passkey\": {\n                \"display_name\": true\n              }\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"organizations\": {\n              \"matcher\": \"email_domain\"\n            }\n          },\n          \"maxLength\": 320\n        }\n      },\n      \"required\": [\"email\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/password/stub/login.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/password/stub/migration.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/password/stub/missing-identifier.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\"\n        }\n      },\n      \"required\": [\n        \"email\"\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/password/stub/phone.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/identity.sms.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"phone_number\": {\n          \"type\": \"string\",\n          \"format\": \"tel\",\n          \"title\": \"Phone Number\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"totp\": {\n                \"account_name\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"sms\"\n              },\n              \"passkey\": {\n                \"display_name\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"sms\"\n            }\n          },\n          \"maxLength\": 320\n        }\n      },\n      \"required\": [\"phone_number\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/password/stub/profile.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/password/stub/registration.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"foobar\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        },\n        \"username\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\"foobar\", \"username\"]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/password/stub/registration.secondary.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"baz\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        },\n        \"username\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"baz\",\n        \"username\"\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/password/stub/sort.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"username\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/password/types.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport \"encoding/json\"\n\n// Update Login Flow with Password Method\n//\n// swagger:model updateLoginFlowWithPasswordMethod\ntype updateLoginFlowWithPasswordMethod struct {\n\t// Method should be set to \"password\" when logging in using the identifier and password strategy.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// The user's password.\n\t//\n\t// required: true\n\tPassword string `json:\"password\"`\n\n\t// Identifier is the email or username of the user trying to log in.\n\t// This field is deprecated!\n\tLegacyIdentifier string `json:\"password_identifier\"`\n\n\t// Identifier is the email or username of the user trying to log in.\n\t//\n\t// required: true\n\tIdentifier string `json:\"identifier\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n"
  },
  {
    "path": "selfservice/strategy/password/validator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"crypto/sha1\" //#nosec G505 -- sha1 is used for k-anonymity\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/arbovm/levenshtein\"\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/hashicorp/go-retryablehttp\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nconst hashCacheItemTTL = time.Hour\n\n// Validator implements a validation strategy for passwords. One example is that the password\n// has to have at least 6 characters and at least one lower and one uppercase password.\ntype Validator interface {\n\t// Validate returns nil if the password is passing the validation strategy and an error otherwise. If a validation error\n\t// occurs, a regular error will be returned. If some other type of error occurs (e.g. HTTP request failed), an error\n\t// of type *herodot.DefaultError will be returned.\n\tValidate(ctx context.Context, identifier, password string) error\n}\n\ntype ValidationProvider interface {\n\tPasswordValidator() Validator\n}\n\nvar (\n\t_                       Validator = new(DefaultPasswordValidator)\n\tErrNetworkFailure                 = herodot.ErrUpstreamError.WithError(\"Leaked password server unavailable\").WithReasonf(\"Unable to check if password has been leaked because an unexpected network error occurred\")\n\tErrUnexpectedStatusCode           = herodot.ErrUpstreamError.WithError(\"Leaked password server unavailable\").WithReasonf(\"Unexpected status code from haveibeenpwned.com\")\n)\n\n// DefaultPasswordValidator implements Validator. It is based on best\n// practices as defined in the following blog posts:\n//\n// - https://www.troyhunt.com/passwords-evolved-authentication-guidance-for-the-modern-era/\n// - https://www.microsoft.com/en-us/research/wp-content/uploads/2016/06/Microsoft_Password_Guidance-1.pdf\n//\n// Additionally passwords are being checked against Troy Hunt's\n// [haveibeenpwnd](https://haveibeenpwned.com/API/v2#SearchingPwnedPasswordsByRange) service to check if the\n// password has been breached in a previous data leak using k-anonymity.\ntype DefaultPasswordValidator struct {\n\treg    validatorDependencies\n\tClient *retryablehttp.Client\n\thashes *ristretto.Cache[string, int64]\n\n\tminIdentifierPasswordDist            int\n\tmaxIdentifierPasswordSubstrThreshold float32\n}\n\ntype validatorDependencies interface {\n\tconfig.Provider\n}\n\nfunc NewDefaultPasswordValidatorStrategy(reg validatorDependencies) (*DefaultPasswordValidator, error) {\n\tcache, err := ristretto.NewCache(&ristretto.Config[string, int64]{\n\t\tNumCounters:        10 * 10000,\n\t\tMaxCost:            60 * 10000, // BCrypt hash size is 60 bytes\n\t\tBufferItems:        64,\n\t\tIgnoreInternalCost: true,\n\t})\n\t// sanity check - this should never happen unless above configuration variables are invalid\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"error while setting up validator cache\")\n\t}\n\treturn &DefaultPasswordValidator{\n\t\tClient: httpx.NewResilientClient(\n\t\t\thttpx.ResilientClientWithConnectionTimeout(time.Second),\n\t\t),\n\t\treg:                       reg,\n\t\thashes:                    cache,\n\t\tminIdentifierPasswordDist: 5, maxIdentifierPasswordSubstrThreshold: 0.5,\n\t}, nil\n}\n\nfunc b20(src []byte) string {\n\treturn fmt.Sprintf(\"%X\", src)\n}\n\n// code inspired by https://rosettacode.org/wiki/Longest_Common_Substring#Go\nfunc lcsLength(a, b string) int {\n\tlengths := make([]int, len(a)*len(b))\n\tgreatestLength := 0\n\tfor i, x := range a {\n\t\tfor j, y := range b {\n\t\t\tif x == y {\n\t\t\t\tcurr := 1\n\t\t\t\tif i != 0 && j != 0 {\n\t\t\t\t\tcurr = lengths[(i-1)*len(b)+j-1] + 1\n\t\t\t\t}\n\n\t\t\t\tif curr > greatestLength {\n\t\t\t\t\tgreatestLength = curr\n\t\t\t\t}\n\t\t\t\tlengths[i*len(b)+j] = curr\n\t\t\t}\n\t\t}\n\t}\n\treturn greatestLength\n}\n\nfunc (s *DefaultPasswordValidator) fetch(ctx context.Context, hpw []byte, apiDNSName string) (int64, error) {\n\tprefix := fmt.Sprintf(\"%X\", hpw)[0:5]\n\tloc := fmt.Sprintf(\"https://%s/range/%s\", apiDNSName, prefix)\n\treq, err := retryablehttp.NewRequestWithContext(ctx, \"GET\", loc, nil)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tres, err := s.Client.Do(req)\n\tif err != nil {\n\t\treturn 0, errors.Wrapf(ErrNetworkFailure, \"%s\", err)\n\t}\n\tdefer func() { _ = res.Body.Close() }()\n\n\tif res.StatusCode != http.StatusOK {\n\t\treturn 0, errors.Wrapf(ErrUnexpectedStatusCode, \"%d\", res.StatusCode)\n\t}\n\n\tvar thisCount int64\n\n\tsc := bufio.NewScanner(res.Body)\n\tfor sc.Scan() {\n\t\trow := sc.Text()\n\t\tresult := strings.Split(strings.TrimSpace(row), \":\")\n\n\t\t// We assume a count of 1. HIBP API sometimes responds without the\n\t\t// colon, so we just assume that the leak count is one.\n\t\t//\n\t\t// See https://github.com/ory/kratos/issues/2145\n\t\tcount := int64(1)\n\t\tif len(result) == 2 {\n\t\t\tcount, err = strconv.ParseInt(strings.ReplaceAll(result[1], \",\", \"\"), 10, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, errors.WithStack(herodot.ErrUpstreamError.WithReasonf(\"Expected password hash to contain a count formatted as int but got: %s\", result[1]))\n\t\t\t}\n\t\t}\n\n\t\ts.hashes.SetWithTTL(prefix+result[0], count, 1, hashCacheItemTTL)\n\t\tif prefix+result[0] == b20(hpw) {\n\t\t\tthisCount = count\n\t\t}\n\t}\n\n\tif err := sc.Err(); err != nil {\n\t\treturn 0, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to initialize string scanner: %s\", err))\n\t}\n\n\ts.hashes.SetWithTTL(b20(hpw), thisCount, 1, hashCacheItemTTL)\n\treturn thisCount, nil\n}\n\nfunc (s *DefaultPasswordValidator) Validate(ctx context.Context, identifier, password string) error {\n\treturn otelx.WithSpan(ctx, \"password.DefaultPasswordValidator.Validate\", func(ctx context.Context) error {\n\t\treturn s.validate(ctx, identifier, password)\n\t})\n}\n\nfunc (s *DefaultPasswordValidator) validate(ctx context.Context, identifier, password string) error {\n\tpasswordPolicyConfig := s.reg.Config().PasswordPolicyConfig(ctx)\n\n\t//nolint:gosec // disable G115\n\tif len(password) < int(passwordPolicyConfig.MinPasswordLength) {\n\t\t//nolint:gosec // disable G115\n\t\treturn text.NewErrorValidationPasswordMinLength(int(passwordPolicyConfig.MinPasswordLength), len(password))\n\t}\n\n\tif passwordPolicyConfig.IdentifierSimilarityCheckEnabled && len(identifier) > 0 {\n\t\tcompIdentifier, compPassword := strings.ToLower(identifier), strings.ToLower(password)\n\t\tdist := levenshtein.Distance(compIdentifier, compPassword)\n\t\tlcs := float32(lcsLength(compIdentifier, compPassword)) / float32(len(compPassword))\n\t\tif dist < s.minIdentifierPasswordDist || lcs > s.maxIdentifierPasswordSubstrThreshold {\n\t\t\treturn text.NewErrorValidationPasswordIdentifierTooSimilar()\n\t\t}\n\t}\n\n\tif !passwordPolicyConfig.HaveIBeenPwnedEnabled {\n\t\treturn nil\n\t}\n\n\t//#nosec G401 -- sha1 is used for k-anonymity\n\th := sha1.New()\n\tif _, err := h.Write([]byte(password)); err != nil {\n\t\treturn err\n\t}\n\thpw := h.Sum(nil)\n\n\tc, ok := s.hashes.Get(b20(hpw))\n\tif !ok {\n\t\tvar err error\n\t\tc, err = s.fetch(ctx, hpw, passwordPolicyConfig.HaveIBeenPwnedHost)\n\t\tif (errors.Is(err, ErrNetworkFailure) || errors.Is(err, ErrUnexpectedStatusCode)) && passwordPolicyConfig.IgnoreNetworkErrors {\n\t\t\treturn nil\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t//nolint:gosec // disable G115\n\tif c > int64(s.reg.Config().PasswordPolicyConfig(ctx).MaxBreaches) {\n\t\treturn text.NewErrorValidationPasswordTooManyBreaches(c)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/password/validator_lcs_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestLCSLength(t *testing.T) {\n\tfor k, tc := range []struct {\n\t\ta string\n\t\tb string\n\t\tl int\n\t}{\n\t\t{a: \"foo\", b: \"foo\", l: 3},\n\t\t{a: \"fo\", b: \"foo\", l: 2},\n\t\t{a: \"bar\", b: \"foo\", l: 0},\n\t\t{a: \"foobar\", b: \"foo\", l: 3},\n\t\t{a: \"foobar\", b: \"oo\", l: 2},\n\t\t{a: \"foobar\", b: \"a\", l: 1},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\trequire.Equal(t, tc.l, lcsLength(tc.a, tc.b))\n\t\t\trequire.Equal(t, tc.l, lcsLength(tc.b, tc.a))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/password/validator_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage password_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/sha1\" //#nosec G505 -- compatibility for imported passwords\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/password\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/httpx\"\n)\n\nfunc TestDefaultPasswordValidationStrategy(t *testing.T) {\n\tt.Parallel()\n\n\t// Tests are based on:\n\t// - https://www.troyhunt.com/passwords-evolved-authentication-guidance-for-the-modern-era/\n\t// - https://www.microsoft.com/en-us/research/wp-content/uploads/2016/06/Microsoft_Password_Guidance-1.pdf\n\n\tt.Run(\"default strategy\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ts, err := password.NewDefaultPasswordValidatorStrategy(reg)\n\t\trequire.NoError(t, err)\n\n\t\tfor k, tc := range []struct {\n\t\t\tid, pw      string\n\t\t\texpectedErr error\n\t\t}{\n\t\t\t{pw: \"\", expectedErr: text.NewErrorValidationPasswordMinLength(0, 0)},\n\t\t\t{pw: \"12\", expectedErr: text.NewErrorValidationPasswordMinLength(0, 0)},\n\t\t\t{pw: \"1234\", expectedErr: text.NewErrorValidationPasswordMinLength(0, 0)},\n\t\t\t{pw: \"123456\", expectedErr: text.NewErrorValidationPasswordMinLength(0, 0)},\n\t\t\t{pw: \"12345678\", expectedErr: text.NewErrorValidationPasswordTooManyBreaches(0)},\n\t\t\t{pw: \"password\", expectedErr: text.NewErrorValidationPasswordTooManyBreaches(0)},\n\t\t\t{pw: \"1234567890\", expectedErr: text.NewErrorValidationPasswordTooManyBreaches(0)},\n\t\t\t{pw: \"qwertyui\", expectedErr: text.NewErrorValidationPasswordTooManyBreaches(0)},\n\t\t\t{pw: \"l3f9to\", expectedErr: text.NewErrorValidationPasswordMinLength(0, 0)},\n\t\t\t{pw: \"l3f9toh1uaf81n21\"},\n\t\t\t{pw: \"l3f9toh1uaf81n21\", id: \"l3f9toh1uaf81n21\", expectedErr: text.NewErrorValidationPasswordIdentifierTooSimilar()},\n\t\t\t{pw: \"l3f9toh1\"},\n\t\t\t{pw: \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"},\n\t\t\t// simple permutation tests\n\t\t\t{id: \"hello@example.com\", pw: \"hello@example.com12345\", expectedErr: text.NewErrorValidationPasswordIdentifierTooSimilar()},\n\t\t\t{id: \"hello@example.com\", pw: \"123hello@example.com123\", expectedErr: text.NewErrorValidationPasswordIdentifierTooSimilar()},\n\t\t\t{id: \"hello@example.com\", pw: \"hello@exam\", expectedErr: text.NewErrorValidationPasswordIdentifierTooSimilar()},\n\t\t\t{id: \"hello@example.com\", pw: \"HELLO@EXAMPLE.COM\", expectedErr: text.NewErrorValidationPasswordIdentifierTooSimilar()},\n\t\t\t{id: \"hello@example.com\", pw: \"HELLO@example.com\", expectedErr: text.NewErrorValidationPasswordIdentifierTooSimilar()},\n\t\t\t{pw: \"hello@example.com\", id: \"hello@exam\", expectedErr: text.NewErrorValidationPasswordIdentifierTooSimilar()},\n\t\t\t{id: \"hello@example.com\", pw: \"h3ll0@example\", expectedErr: text.NewErrorValidationPasswordIdentifierTooSimilar()},\n\t\t\t{pw: \"hello@example.com\", id: \"hello@exam\", expectedErr: text.NewErrorValidationPasswordIdentifierTooSimilar()},\n\t\t\t{id: \"abcd\", pw: \"9d3c8a1b\"},\n\t\t\t{id: \"a\", pw: \"kjOklafe\"},\n\t\t\t{id: \"ab\", pw: \"0000ab0000123\"},\n\t\t\t// longest common substring with long password\n\t\t\t{id: \"d4f6090b-5a84\", pw: \"d4f6090b-5a84-2184-4404-8d1b-8da3eb00ebbe\"},\n\t\t\t{id: \"asdflasdflasdf\", pw: \"asdflasdflpiuhefnciluaksdzuföfhg\"},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\terr := s.Validate(t.Context(), tc.id, tc.pw)\n\t\t\t\tif tc.expectedErr == nil {\n\t\t\t\t\trequire.NoErrorf(t, err, \"id: %s, pw: %s\", tc.id, tc.pw)\n\t\t\t\t} else {\n\t\t\t\t\trequire.ErrorIsf(t, err, tc.expectedErr, \"id: %s, pw: %s\", tc.id, tc.pw)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"failure cases\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\ts, err := password.NewDefaultPasswordValidatorStrategy(reg)\n\t\trequire.NoError(t, err)\n\n\t\tfakeClient := NewFakeHTTPClient()\n\t\ts.Client = httpx.NewResilientClient(\n\t\t\thttpx.ResilientClientWithMaxRetry(1),\n\t\t\thttpx.ResilientClientWithConnectionTimeout(time.Millisecond),\n\t\t\thttpx.ResilientClientWithMaxRetryWait(time.Millisecond))\n\t\ts.Client.HTTPClient = &fakeClient.Client\n\n\t\tt.Run(\"case=should send request to pwnedpasswords.com\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyIgnoreNetworkErrors, false)\n\t\t\trequire.Error(t, s.Validate(ctx, \"mohutdesub\", \"damrumukuh\"))\n\t\t\trequire.Contains(t, fakeClient.RequestedURLs(), \"https://api.pwnedpasswords.com/range/BCBA9\")\n\t\t})\n\n\t\tt.Run(\"case=should fail if request fails and ignoreNetworkErrors is not set\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyIgnoreNetworkErrors, false)\n\t\t\tfakeClient.RespondWithError(\"Network request failed\")\n\t\t\trequire.Error(t, s.Validate(ctx, \"\", \"sumdarmetp\"))\n\t\t})\n\n\t\tt.Run(\"case=should not fail if request fails and ignoreNetworkErrors is set\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyIgnoreNetworkErrors, true)\n\t\t\tfakeClient.RespondWithError(\"Network request failed\")\n\t\t\trequire.NoError(t, s.Validate(ctx, \"\", \"pepegtawni\"))\n\t\t})\n\n\t\tt.Run(\"case=should fail if response has non 200 code and ignoreNetworkErrors is not set\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyIgnoreNetworkErrors, false)\n\t\t\tfakeClient.RespondWith(http.StatusForbidden, \"\")\n\t\t\trequire.Error(t, s.Validate(ctx, \"\", \"jolhakowef\"))\n\t\t})\n\n\t\tt.Run(\"case=should not fail if response has non 200 code code and ignoreNetworkErrors is set\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyIgnoreNetworkErrors, true)\n\t\t\tfakeClient.RespondWith(http.StatusInternalServerError, \"\")\n\t\t\trequire.NoError(t, s.Validate(ctx, \"\", \"jenuzuhjoj\"))\n\t\t})\n\t})\n\n\tt.Run(\"max breaches\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tconst maxBreaches = 5\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValue(config.ViperKeyPasswordMaxBreaches, maxBreaches))\n\t\ts, err := password.NewDefaultPasswordValidatorStrategy(reg)\n\t\trequire.NoError(t, err)\n\n\t\thibpResp := make(chan string, 1)\n\t\tfakeClient := NewFakeHTTPClient()\n\t\tfakeClient.responder = func(req *http.Request) (*http.Response, error) {\n\t\t\tbuffer := bytes.NewBufferString(<-hibpResp)\n\t\t\treturn &http.Response{\n\t\t\t\tStatusCode:    http.StatusOK,\n\t\t\t\tBody:          io.NopCloser(buffer),\n\t\t\t\tContentLength: int64(buffer.Len()),\n\t\t\t\tRequest:       req,\n\t\t\t}, nil\n\t\t}\n\t\ts.Client = httpx.NewResilientClient(httpx.ResilientClientWithMaxRetry(1), httpx.ResilientClientWithConnectionTimeout(time.Millisecond))\n\t\ts.Client.HTTPClient = &fakeClient.Client\n\n\t\thashPw := func(t *testing.T, pw string) string {\n\t\t\t//#nosec G401 -- sha1 is used for k-anonymity\n\t\t\th := sha1.New()\n\t\t\t_, err := h.Write([]byte(pw))\n\t\t\trequire.NoError(t, err)\n\t\t\thpw := h.Sum(nil)\n\t\t\treturn fmt.Sprintf(\"%X\", hpw)[5:]\n\t\t}\n\t\trandomPassword := func(t *testing.T) string {\n\t\t\tpw := make([]byte, 10)\n\t\t\t_, err := rand.Read(pw)\n\t\t\trequire.NoError(t, err)\n\t\t\treturn fmt.Sprintf(\"%x\", pw)\n\t\t}\n\n\t\tfor _, tc := range []struct {\n\t\t\tname      string\n\t\t\tres       func(t *testing.T, hash string) string\n\t\t\texpectErr error\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"contains invalid data which is ignored\",\n\t\t\t\tres: func(t *testing.T, hash string) string {\n\t\t\t\t\treturn fmt.Sprintf(\"%s:2\\ninvalid\", hash)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"is missing a colon\",\n\t\t\t\tres: func(t *testing.T, hash string) string {\n\t\t\t\t\treturn hash\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"contains invalid hash count\",\n\t\t\t\tres: func(t *testing.T, hash string) string {\n\t\t\t\t\treturn fmt.Sprintf(\"%s:text\\n%s:2\", hashPw(t, randomPassword(t)), hash)\n\t\t\t\t},\n\t\t\t\texpectErr: herodot.ErrUpstreamError,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"is missing hash count\",\n\t\t\t\tres: func(t *testing.T, hash string) string {\n\t\t\t\t\treturn fmt.Sprintf(\"%s\\n%s:2\", hash, hashPw(t, randomPassword(t)))\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"response contains no matches\",\n\t\t\t\tres: func(t *testing.T, hash string) string {\n\t\t\t\t\treturn fmt.Sprintf(\"%s:57\", hashPw(t, randomPassword(t)))\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"contains less than maxBreachesThreshold\",\n\t\t\t\tres: func(t *testing.T, hash string) string {\n\t\t\t\t\treturn fmt.Sprintf(\n\t\t\t\t\t\t\"%s:%d\\n%s:%d\",\n\t\t\t\t\t\thash,\n\t\t\t\t\t\tmaxBreaches,\n\t\t\t\t\t\thashPw(t, randomPassword(t)),\n\t\t\t\t\t\tmaxBreaches+1,\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"contains less than maxBreachesThreshold with a leading comma\",\n\t\t\t\tres: func(t *testing.T, hash string) string {\n\t\t\t\t\treturn fmt.Sprintf(\n\t\t\t\t\t\t\"%s:%d\\n%s:0,%d\",\n\t\t\t\t\t\thash,\n\t\t\t\t\t\tmaxBreaches,\n\t\t\t\t\t\thashPw(t, randomPassword(t)),\n\t\t\t\t\t\tmaxBreaches+1,\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"contains more than maxBreachesThreshold\",\n\t\t\t\tres: func(t *testing.T, hash string) string {\n\t\t\t\t\treturn fmt.Sprintf(\"%s:%d\", hash, maxBreaches+1)\n\t\t\t\t},\n\t\t\t\texpectErr: text.NewErrorValidationPasswordTooManyBreaches(\n\t\t\t\t\tint64(maxBreaches) + 1, // #nosec G115\n\t\t\t\t),\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%s/expected err=%s\", tc.name, tc.expectErr), func(t *testing.T) {\n\t\t\t\tpw := randomPassword(t)\n\t\t\t\thash := hashPw(t, pw)\n\t\t\t\thibpResp <- tc.res(t, hash)\n\n\t\t\t\terr := s.Validate(context.Background(), \"\", pw)\n\t\t\t\tassert.ErrorIs(t, err, tc.expectErr)\n\t\t\t})\n\n\t\t\t// verify the fetch was done, i.e. channel is empty\n\t\t\tselect {\n\t\t\tcase r := <-hibpResp:\n\t\t\t\tt.Logf(\"expected the validate step to fetch the response, but I still got %s\", r)\n\t\t\t\tt.FailNow()\n\t\t\tdefault:\n\t\t\t\t// continue\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestChangeHaveIBeenPwnedValidationHost(t *testing.T) {\n\tt.Parallel()\n\n\ttestServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }))\n\tt.Cleanup(testServer.Close)\n\ttestServerURL, err := url.Parse(testServer.URL)\n\trequire.NoError(t, err)\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValue(config.ViperKeyPasswordHaveIBeenPwnedHost, testServerURL.Host))\n\ts, err := password.NewDefaultPasswordValidatorStrategy(reg)\n\trequire.NoError(t, err)\n\n\tfakeClient := NewFakeHTTPClient()\n\ts.Client = httpx.NewResilientClient(httpx.ResilientClientWithMaxRetry(1), httpx.ResilientClientWithConnectionTimeout(time.Millisecond))\n\ts.Client.HTTPClient = &fakeClient.Client\n\n\ttestServerExpectedCallURL := fmt.Sprintf(\"https://%s/range/BCBA9\", testServerURL.Host)\n\n\tt.Run(\"case=should send request to test server\", func(t *testing.T) {\n\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyIgnoreNetworkErrors, false)\n\t\trequire.Error(t, s.Validate(ctx, \"mohutdesub\", \"damrumukuh\"))\n\t\trequire.Contains(t, fakeClient.RequestedURLs(), testServerExpectedCallURL)\n\t})\n}\n\nfunc TestDisableHaveIBeenPwnedValidationHost(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValue(config.ViperKeyPasswordHaveIBeenPwnedEnabled, false))\n\ts, err := password.NewDefaultPasswordValidatorStrategy(reg)\n\trequire.NoError(t, err)\n\n\tfakeClient := NewFakeHTTPClient()\n\ts.Client = httpx.NewResilientClient(httpx.ResilientClientWithMaxRetry(1), httpx.ResilientClientWithConnectionTimeout(time.Millisecond))\n\ts.Client.HTTPClient = &fakeClient.Client\n\n\tt.Run(\"case=should not send request to test server\", func(t *testing.T) {\n\t\trequire.NoError(t, s.Validate(t.Context(), \"mohutdesub\", \"damrumukuh\"))\n\t\trequire.Empty(t, fakeClient.RequestedURLs())\n\t})\n}\n\nfunc TestChangeMinPasswordLength(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValue(config.ViperKeyPasswordMinLength, 10))\n\ts, err := password.NewDefaultPasswordValidatorStrategy(reg)\n\trequire.NoError(t, err)\n\n\tt.Run(\"case=should not fail if password is longer than min length\", func(t *testing.T) {\n\t\trequire.NoError(t, s.Validate(t.Context(), \"\", \"kuobahcaas\"))\n\t})\n\n\tt.Run(\"case=should fail if password is shorter than min length\", func(t *testing.T) {\n\t\trequire.Error(t, s.Validate(t.Context(), \"\", \"rfqyfjied\"))\n\t})\n}\n\nfunc TestChangeIdentifierSimilarityCheckEnabled(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\ts, err := password.NewDefaultPasswordValidatorStrategy(reg)\n\trequire.NoError(t, err)\n\n\tt.Run(\"case=should not fail if password is similar to identifier\", func(t *testing.T) {\n\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyPasswordIdentifierSimilarityCheckEnabled, false)\n\t\trequire.NoError(t, s.Validate(ctx, \"bosqwfaxee\", \"bosqwfaxee\"))\n\t})\n\n\tt.Run(\"case=should fail if password is similar to identifier\", func(t *testing.T) {\n\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyPasswordIdentifierSimilarityCheckEnabled, true)\n\t\trequire.Error(t, s.Validate(ctx, \"bosqwfaxee\", \"bosqwfaxee\"))\n\t})\n}\n\ntype fakeHttpClient struct {\n\thttp.Client\n\n\trequestedURLs []string\n\tresponder     func(*http.Request) (*http.Response, error)\n}\n\nfunc NewFakeHTTPClient() *fakeHttpClient {\n\tclient := fakeHttpClient{\n\t\tresponder: func(*http.Request) (*http.Response, error) {\n\t\t\treturn nil, errors.New(\"No responder defined in fake HTTP client\")\n\t\t},\n\t}\n\tclient.Client = http.Client{\n\t\tTransport: &fakeRoundTripper{&client},\n\t\tTimeout:   time.Second,\n\t}\n\treturn &client\n}\n\nfunc (c *fakeHttpClient) RespondWith(status int, body string) {\n\tc.responder = func(request *http.Request) (*http.Response, error) {\n\t\tbuffer := bytes.NewBufferString(body)\n\t\treturn &http.Response{\n\t\t\tStatusCode:    status,\n\t\t\tBody:          io.NopCloser(buffer),\n\t\t\tContentLength: int64(buffer.Len()),\n\t\t\tRequest:       request,\n\t\t}, nil\n\t}\n}\n\nfunc (c *fakeHttpClient) RespondWithError(err string) {\n\tc.responder = func(*http.Request) (*http.Response, error) {\n\t\treturn nil, errors.New(err)\n\t}\n}\n\nfunc (c *fakeHttpClient) Reset() {\n\tc.requestedURLs = nil\n}\n\nfunc (c *fakeHttpClient) RequestedURLs() []string {\n\treturn c.requestedURLs\n}\n\nfunc (c *fakeHttpClient) handle(request *http.Request) (*http.Response, error) {\n\tc.requestedURLs = append(c.requestedURLs, request.URL.String())\n\tif request.Body != nil {\n\t\t_ = request.Body.Close()\n\t}\n\treturn c.responder(request)\n}\n\ntype fakeRoundTripper struct {\n\tclient *fakeHttpClient\n}\n\nfunc (rt *fakeRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {\n\treturn rt.client.handle(request)\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/.schema/registration.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/profile/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"traits\": {\n      \"description\": \"This field will be overwritten in registration.go's decoder() method. Do not add anything to this field as it has no effect.\"\n    },\n    \"screen\": {\n      \"type\": \"string\",\n      \"enum\": [\n        \"credential-selection\",\n        \"previous\"\n      ]\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/.schema/settings.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/profile/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\n    \"traits\"\n  ],\n  \"properties\": {\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"traits\": {},\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"action\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestOneStepRegistration-initial_form_is_populated_with_identity_traits-type=browser-case=multi-schema-empty_flow.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.stringy\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.numby\",\n      \"node_type\": \"input\",\n      \"type\": \"number\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.booly\",\n      \"node_type\": \"input\",\n      \"type\": \"checkbox\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.should_big_number\",\n      \"node_type\": \"input\",\n      \"type\": \"number\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.should_long_string\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestOneStepRegistration-initial_form_is_populated_with_identity_traits-type=browser-empty_flow.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.stringy\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.numby\",\n      \"node_type\": \"input\",\n      \"type\": \"number\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.booly\",\n      \"node_type\": \"input\",\n      \"type\": \"checkbox\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.should_big_number\",\n      \"node_type\": \"input\",\n      \"type\": \"number\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.should_long_string\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestPopulateRegistrationMethod-case=multi-schema-method=PopulateRegistrationMethodProfile.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethod.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodCredentials.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040008,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodProfile.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=1.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=2.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040008,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=3.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=4.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040008,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestStrategyTraits-description=hydrate_the_proper_fields-type=api.json",
    "content": "{\n  \"method\": \"POST\",\n  \"nodes\": [\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"csrf_token\",\n        \"node_type\": \"input\",\n        \"required\": true,\n        \"type\": \"hidden\"\n      },\n      \"group\": \"default\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.email\",\n        \"node_type\": \"input\",\n        \"type\": \"text\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.stringy\",\n        \"node_type\": \"input\",\n        \"type\": \"text\",\n        \"value\": \"foobar\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.numby\",\n        \"node_type\": \"input\",\n        \"type\": \"number\",\n        \"value\": 2.5\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.booly\",\n        \"node_type\": \"input\",\n        \"type\": \"checkbox\",\n        \"value\": false\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.should_big_number\",\n        \"node_type\": \"input\",\n        \"type\": \"number\",\n        \"value\": 2048\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.should_long_string\",\n        \"node_type\": \"input\",\n        \"type\": \"text\",\n        \"value\": \"asdfasdfasdfasdfasfdasdfasdfasdf\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"method\",\n        \"node_type\": \"input\",\n        \"type\": \"submit\",\n        \"value\": \"profile\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1070003,\n          \"text\": \"Save\",\n          \"type\": \"info\"\n        }\n      },\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"autocomplete\": \"new-password\",\n        \"disabled\": false,\n        \"name\": \"password\",\n        \"node_type\": \"input\",\n        \"required\": true,\n        \"type\": \"password\"\n      },\n      \"group\": \"password\",\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1070001,\n          \"text\": \"Password\",\n          \"type\": \"info\"\n        }\n      },\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"method\",\n        \"node_type\": \"input\",\n        \"type\": \"submit\",\n        \"value\": \"password\"\n      },\n      \"group\": \"password\",\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1070003,\n          \"text\": \"Save\",\n          \"type\": \"info\"\n        }\n      },\n      \"type\": \"input\"\n    }\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestStrategyTraits-description=hydrate_the_proper_fields-type=browser.json",
    "content": "{\n  \"method\": \"POST\",\n  \"nodes\": [\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"csrf_token\",\n        \"node_type\": \"input\",\n        \"required\": true,\n        \"type\": \"hidden\"\n      },\n      \"group\": \"default\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.email\",\n        \"node_type\": \"input\",\n        \"type\": \"text\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.stringy\",\n        \"node_type\": \"input\",\n        \"type\": \"text\",\n        \"value\": \"foobar\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.numby\",\n        \"node_type\": \"input\",\n        \"type\": \"number\",\n        \"value\": 2.5\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.booly\",\n        \"node_type\": \"input\",\n        \"type\": \"checkbox\",\n        \"value\": false\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.should_big_number\",\n        \"node_type\": \"input\",\n        \"type\": \"number\",\n        \"value\": 2048\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.should_long_string\",\n        \"node_type\": \"input\",\n        \"type\": \"text\",\n        \"value\": \"asdfasdfasdfasdfasfdasdfasdfasdf\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"method\",\n        \"node_type\": \"input\",\n        \"type\": \"submit\",\n        \"value\": \"profile\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1070003,\n          \"text\": \"Save\",\n          \"type\": \"info\"\n        }\n      },\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"autocomplete\": \"new-password\",\n        \"disabled\": false,\n        \"name\": \"password\",\n        \"node_type\": \"input\",\n        \"required\": true,\n        \"type\": \"password\"\n      },\n      \"group\": \"password\",\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1070001,\n          \"text\": \"Password\",\n          \"type\": \"info\"\n        }\n      },\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"method\",\n        \"node_type\": \"input\",\n        \"type\": \"submit\",\n        \"value\": \"password\"\n      },\n      \"group\": \"password\",\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1070003,\n          \"text\": \"Save\",\n          \"type\": \"info\"\n        }\n      },\n      \"type\": \"input\"\n    }\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestStrategyTraits-description=hydrate_the_proper_fields-type=spa.json",
    "content": "{\n  \"method\": \"POST\",\n  \"nodes\": [\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"csrf_token\",\n        \"node_type\": \"input\",\n        \"required\": true,\n        \"type\": \"hidden\"\n      },\n      \"group\": \"default\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.email\",\n        \"node_type\": \"input\",\n        \"type\": \"text\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.stringy\",\n        \"node_type\": \"input\",\n        \"type\": \"text\",\n        \"value\": \"foobar\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.numby\",\n        \"node_type\": \"input\",\n        \"type\": \"number\",\n        \"value\": 2.5\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.booly\",\n        \"node_type\": \"input\",\n        \"type\": \"checkbox\",\n        \"value\": false\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.should_big_number\",\n        \"node_type\": \"input\",\n        \"type\": \"number\",\n        \"value\": 2048\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"traits.should_long_string\",\n        \"node_type\": \"input\",\n        \"type\": \"text\",\n        \"value\": \"asdfasdfasdfasdfasfdasdfasdfasdf\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {},\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"method\",\n        \"node_type\": \"input\",\n        \"type\": \"submit\",\n        \"value\": \"profile\"\n      },\n      \"group\": \"profile\",\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1070003,\n          \"text\": \"Save\",\n          \"type\": \"info\"\n        }\n      },\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"autocomplete\": \"new-password\",\n        \"disabled\": false,\n        \"name\": \"password\",\n        \"node_type\": \"input\",\n        \"required\": true,\n        \"type\": \"password\"\n      },\n      \"group\": \"password\",\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1070001,\n          \"text\": \"Password\",\n          \"type\": \"info\"\n        }\n      },\n      \"type\": \"input\"\n    },\n    {\n      \"attributes\": {\n        \"disabled\": false,\n        \"name\": \"method\",\n        \"node_type\": \"input\",\n        \"type\": \"submit\",\n        \"value\": \"password\"\n      },\n      \"group\": \"password\",\n      \"messages\": [],\n      \"meta\": {\n        \"label\": {\n          \"id\": 1070003,\n          \"text\": \"Save\",\n          \"type\": \"info\"\n        }\n      },\n      \"type\": \"input\"\n    }\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestTwoStepRegistration-initial_form_is_populated_with_identity_traits-type=browser-case=multi-schema-empty_flow.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"provider\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\",\n          \"provider_id\": \"google\"\n        },\n        \"id\": 1040002,\n        \"text\": \"Sign up with google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.stringy\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.numby\",\n      \"node_type\": \"input\",\n      \"type\": \"number\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.booly\",\n      \"node_type\": \"input\",\n      \"type\": \"checkbox\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.should_big_number\",\n      \"node_type\": \"input\",\n      \"type\": \"number\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.should_long_string\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestTwoStepRegistration-initial_form_is_populated_with_identity_traits-type=browser-empty_flow.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"provider\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\",\n          \"provider_id\": \"google\"\n        },\n        \"id\": 1040002,\n        \"text\": \"Sign up with google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.stringy\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.numby\",\n      \"node_type\": \"input\",\n      \"type\": \"number\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.booly\",\n      \"node_type\": \"input\",\n      \"type\": \"checkbox\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.should_big_number\",\n      \"node_type\": \"input\",\n      \"type\": \"number\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.should_long_string\",\n      \"node_type\": \"input\",\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestTwoStepRegistration-initial_form_is_populated_with_identity_traits-type=browser-return_to_profile.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\",\n          \"provider_id\": \"google\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"text\",\n      \"value\": \"browser-1-1@example.org\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"text\",\n      \"value\": \"string\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"number\",\n      \"value\": 1,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"checkbox\",\n      \"value\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"number\",\n      \"value\": 1000000,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"text\",\n      \"value\": \"1111111111111111111111111111111111111111111111111111111111\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestTwoStepRegistration-initial_form_is_populated_with_identity_traits-type=browser-select_credentials.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\",\n          \"provider_id\": \"google\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"hidden\",\n      \"value\": \"browser-1@example.org\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"hidden\",\n      \"value\": \"string\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"hidden\",\n      \"value\": 1,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"hidden\",\n      \"value\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"hidden\",\n      \"value\": 1000000,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"hidden\",\n      \"value\": \"1111111111111111111111111111111111111111111111111111111111\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040006,\n        \"text\": \"Send sign up code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040008,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/.snapshots/TestTwoStepRegistration-initial_form_is_populated_with_identity_traits-type=browser-select_credentials_again.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"provider\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040002,\n        \"text\": \"Sign up with google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\",\n          \"provider_id\": \"google\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"hidden\",\n      \"value\": \"browser-1-1@example.org\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.stringy\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.numby\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.booly\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_big_number\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"traits.should_long_string\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclick\": \"window.oryPasskeyRegistration()\",\n      \"onclickTrigger\": \"oryPasskeyRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040007,\n        \"text\": \"Sign up with passkey\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_register\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"passkey\",\n    \"attributes\": {\n      \"name\": \"passkey_create_data\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"code\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"code\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040006,\n        \"text\": \"Send sign up code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"screen\",\n      \"type\": \"submit\",\n      \"value\": \"previous\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040008,\n        \"text\": \"Back\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/profile/nodes.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage profile\n\nimport (\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc nodePreviousScreen() *node.Node {\n\treturn node.NewInputField(\n\t\t\"screen\",\n\t\t\"previous\",\n\t\tnode.ProfileGroup,\n\t\tnode.InputAttributeTypeSubmit,\n\t).WithMetaLabel(text.NewInfoRegistrationBack())\n}\n\nfunc nodeSubmitProfile() *node.Node {\n\treturn node.NewInputField(\n\t\t\"method\",\n\t\t\"profile\",\n\t\tnode.ProfileGroup,\n\t\tnode.InputAttributeTypeSubmit,\n\t).WithMetaLabel(text.NewInfoRegistration())\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/registration.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage profile\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/otelx\"\n\t\"github.com/ory/x/otelx/semconv\"\n)\n\n//go:embed .schema/registration.schema.json\nvar registrationSchema []byte\n\n// The RegistrationScreen\n// swagger:enum RegistrationScreen\ntype RegistrationScreen string\n\nconst (\n\t//nolint:gosec // not a credential\n\tRegistrationScreenCredentialSelection RegistrationScreen = \"credential-selection\"\n\tRegistrationScreenPrevious            RegistrationScreen = \"previous\"\n)\n\nvar _ registration.Strategy = new(Strategy)\nvar _ registration.FormHydrator = new(Strategy)\n\n// Update Registration Flow with Profile Method\n//\n// swagger:model updateRegistrationFlowWithProfileMethod\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateRegistrationFlowWithProfileMethod struct {\n\t// Traits\n\t//\n\t// The identity's traits.\n\t//\n\t// required: true\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// Method\n\t//\n\t// Should be set to profile when trying to update a profile.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Screen requests navigation to a previous screen.\n\t//\n\t// This must be set to credential-selection to go back to the credential\n\t// selection screen.\n\t//\n\t// required: false\n\tScreen RegistrationScreen `json:\"screen\" form:\"screen\"`\n\n\t// FlowIDRequestID is the flow ID.\n\t//\n\t// swagger:ignore\n\tFlowID string `json:\"flow\"`\n\n\t// The Anti-CSRF Token\n\t//\n\t// This token is only required when performing browser flows.\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\"`\n}\n\nfunc (s *Strategy) ID() identity.CredentialsType {\n\treturn identity.CredentialsTypeProfile\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodCredentials(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\tf.UI.Nodes.Append(nodePreviousScreen())\n\tf.UI.Nodes.RemoveMatching(nodeSubmitProfile())\n\n\tfor _, n := range f.UI.Nodes {\n\t\tif n.Group != node.DefaultGroup || n.Type != node.Input {\n\t\t\tcontinue\n\t\t}\n\t\tif attr, ok := n.Attributes.(*node.InputAttributes); ok {\n\t\t\tattr.Type = node.InputAttributeTypeHidden\n\t\t}\n\t}\n\n\tf.UI.Messages.Add(text.NewInfoSelfServiceChooseCredentials())\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodProfile(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\tds, err := f.IdentitySchema.URL(r.Context(), s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tconf := new(registration.FormHydratorOptions)\n\tfor _, o := range options {\n\t\to(conf)\n\t}\n\n\tf.UI.Nodes.RemoveMatching(nodePreviousScreen())\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\n\tnodes, err := container.NodesFromJSONSchema(r.Context(), node.DefaultGroup, ds.String(), \"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, n := range nodes {\n\t\tf.UI.Nodes.Upsert(n)\n\t}\n\n\tif len(conf.WithTraits) > 0 {\n\t\tf.UI.UpdateNodeValuesFromJSON(conf.WithTraits, \"traits\", node.DefaultGroup)\n\t}\n\n\tf.UI.Nodes.Append(nodeSubmitProfile())\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethod(r *http.Request, f *registration.Flow) error {\n\tds, err := f.IdentitySchema.URL(r.Context(), s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tnodes, err := container.NodesFromJSONSchema(r.Context(), node.DefaultGroup, ds.String(), \"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, n := range nodes {\n\t\tf.UI.SetNode(n)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) decode(p *updateRegistrationFlowWithProfileMethod, r *http.Request, ds *url.URL) error {\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(registrationSchema)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif err := decoderx.Decode(r, p, compiler,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(false),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t); err != nil {\n\t\treturn err\n\t}\n\n\tif p.Method != \"profile\" && p.Method != \"profile:back\" && len(p.Screen) == 0 {\n\t\treturn errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\treturn registration.DecodeBody(p, r, registrationSchema, ds)\n}\n\nfunc (s *Strategy) Register(w http.ResponseWriter, r *http.Request, regFlow *registration.Flow, i *identity.Identity) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.profile.Strategy.Register\")\n\tdefer otelx.End(span, &err)\n\n\tif !s.d.Config().SelfServiceFlowRegistrationTwoSteps(ctx) {\n\t\treturn flow.ErrStrategyNotResponsible\n\t}\n\n\tvar params updateRegistrationFlowWithProfileMethod\n\n\tds, err := regFlow.IdentitySchema.URL(ctx, s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err = s.decode(&params, r, ds); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t}\n\tif params.Method == \"profile\" || len(params.Screen) > 0 {\n\t\tswitch params.Screen {\n\t\tcase RegistrationScreenCredentialSelection:\n\t\t\treturn s.showCredentialsSelection(ctx, w, r, regFlow, i, params)\n\t\tcase RegistrationScreenPrevious:\n\t\t\treturn s.returnToProfileForm(ctx, w, r, regFlow, params)\n\t\tdefault:\n\t\t\t// FIXME In this scenario we are on the first step of the registration flow and the user clicked on \"continue\".\n\t\t\t// FIXME The appropriate solution would be to also have `screen=credential-selection` available, but that\n\t\t\t// FIXME is not the case right now. So instead, we fall back.\n\t\t\tspan.AddEvent(semconv.NewDeprecatedFeatureUsedEvent(ctx, \"profile:missing_screen_parameter\"))\n\t\t\treturn s.showCredentialsSelection(ctx, w, r, regFlow, i, params)\n\t\t}\n\t} else if params.Method == \"profile:back\" {\n\t\t// \"profile:back\" is kept for backwards compatibility.\n\t\t// FIXME remove this at some point.\n\t\tspan.AddEvent(semconv.NewDeprecatedFeatureUsedEvent(ctx, \"profile:back\"))\n\t\treturn s.returnToProfileForm(ctx, w, r, regFlow, params)\n\t}\n\n\treturn flow.ErrStrategyNotResponsible\n}\n\nfunc (s *Strategy) returnToProfileForm(ctx context.Context, w http.ResponseWriter, r *http.Request, regFlow *registration.Flow, params updateRegistrationFlowWithProfileMethod) error {\n\tregFlow.UI.ResetMessages()\n\tregFlow.OrganizationID = uuid.NullUUID{}\n\tregFlow.UI.UpdateNodeValuesFromJSON(params.Traits, \"traits\", node.DefaultGroup)\n\n\tfor _, ls := range SortForHydration(s.d.RegistrationStrategies(ctx)) {\n\t\tpopulator, ok := ls.(registration.FormHydrator)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := populator.PopulateRegistrationMethodProfile(r, regFlow, registration.WithTraits(params.Traits)); err != nil {\n\t\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t\t}\n\t}\n\n\tds, err := regFlow.IdentitySchema.URL(r.Context(), s.d.Config())\n\tif err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t}\n\n\tif err := registration.SortNodes(r.Context(), regFlow.UI.Nodes, ds.String()); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t}\n\n\tif err := s.d.RegistrationFlowPersister().UpdateRegistrationFlow(ctx, regFlow); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t}\n\n\tredirectTo := regFlow.AppendTo(s.d.Config().SelfServiceFlowRegistrationUI(ctx)).String()\n\tif x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteCode(w, r, http.StatusBadRequest, regFlow)\n\t} else {\n\t\thttp.Redirect(w, r, redirectTo, http.StatusSeeOther)\n\t}\n\n\treturn flow.ErrCompletedByStrategy\n}\n\nfunc (s *Strategy) showCredentialsSelection(ctx context.Context, w http.ResponseWriter, r *http.Request, regFlow *registration.Flow, i *identity.Identity, params updateRegistrationFlowWithProfileMethod) error {\n\t// Reset state-esque flow fields\n\tregFlow.Active = \"\"\n\tregFlow.State = \"choose_method\"\n\n\tregFlow.UI.ResetMessages()\n\tregFlow.TransientPayload = params.TransientPayload\n\n\tif err := flow.EnsureCSRF(s.d, r, regFlow.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, params.CSRFToken); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t}\n\n\tif len(params.Traits) == 0 {\n\t\tparams.Traits = json.RawMessage(\"{}\")\n\t}\n\n\ti.Traits = identity.Traits(params.Traits)\n\tif err := s.d.IdentityValidator().Validate(ctx, i); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t}\n\n\tvar didPopulate bool\n\tfor _, ls := range SortForHydration(s.d.RegistrationStrategies(ctx)) {\n\t\tpopulator, ok := ls.(registration.FormHydrator)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := populator.PopulateRegistrationMethodCredentials(r, regFlow, registration.WithTraits([]byte(i.Traits))); errors.Is(err, registration.ErrBreakRegistrationPopulate) {\n\t\t\tdidPopulate = true\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t\t} else {\n\t\t\tdidPopulate = true\n\t\t}\n\t}\n\n\t// If no strategy populated, it means that the account (very likely) does not exist. We show a user not found error,\n\t// but only if account enumeration mitigation is disabled. Otherwise, we proceed to render the rest of the form.\n\tif !didPopulate && !s.d.Config().SecurityAccountEnumerationMitigate(ctx) {\n\t\treturn s.handleRegistrationError(r, regFlow, params, errors.WithStack(schema.NewNoRegistrationStrategyResponsible()))\n\t}\n\n\tregFlow.UI.UpdateNodeValuesFromJSON(json.RawMessage(i.Traits), \"traits\", node.DefaultGroup)\n\n\tds, err := regFlow.IdentitySchema.URL(r.Context(), s.d.Config())\n\tif err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t}\n\n\tif err := registration.SortNodes(r.Context(), regFlow.UI.Nodes, ds.String()); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t}\n\n\tif err := s.d.RegistrationFlowPersister().UpdateRegistrationFlow(ctx, regFlow); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, params, err)\n\t}\n\n\tredirectTo := regFlow.AppendTo(s.d.Config().SelfServiceFlowRegistrationUI(ctx)).String()\n\tif x.IsJSONRequest(r) {\n\t\ts.d.Writer().WriteCode(w, r, http.StatusBadRequest, regFlow)\n\t} else {\n\t\thttp.Redirect(w, r, redirectTo, http.StatusSeeOther)\n\t}\n\n\treturn flow.ErrCompletedByStrategy\n}\n\nfunc (s *Strategy) handleRegistrationError(r *http.Request, regFlow *registration.Flow, params updateRegistrationFlowWithProfileMethod, err error) error {\n\tif regFlow != nil {\n\t\tfor _, n := range container.NewFromJSON(\"\", node.DefaultGroup, params.Traits, \"traits\").Nodes {\n\t\t\t// we only set the value and not the whole field because we want to keep types from the initial form generation\n\t\t\tregFlow.UI.Nodes.SetValueAttribute(n.ID(), n.Attributes.GetValue())\n\t\t}\n\n\t\tregFlow.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/registration_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage profile_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\nfunc TestTwoStepRegistration(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\n\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePassword.String(), true)\n\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypeOIDC.String(), true)\n\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePasskey.String(), true)\n\tconf.MustSet(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeOIDC)+\".config\", &oidc.ConfigurationCollection{Providers: []oidc.Configuration{\n\t\t{\n\t\t\tID:           \"google\",\n\t\t\tProvider:     \"google\",\n\t\t\tClientID:     \"1234\",\n\t\t\tClientSecret: \"1234\",\n\t\t},\n\t}})\n\n\tconf.MustSet(ctx, config.ViperKeyWebAuthnPasswordless, true)\n\tconf.MustSet(ctx, config.ViperKeyPasskeyRPID, \"localhost\")\n\tconf.MustSet(ctx, config.ViperKeyPasskeyRPDisplayName, \"localhost\")\n\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPID, \"localhost\")\n\tconf.MustSet(ctx, config.ViperKeyWebAuthnRPDisplayName, \"localhost\")\n\tconf.MustSet(ctx, fmt.Sprintf(\"%s.%s.passwordless_enabled\", config.ViperKeySelfServiceStrategyConfig, identity.CredentialsTypeCodeAuth), true)\n\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationFlowStyle, \"profile_first\")\n\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\t_ = testhelpers.NewRedirSessionEchoTS(t, reg)\n\tui := testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\n\tt.Run(\"initial form is populated with identity traits\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\t\tt.Run(\"empty_flow\", func(t *testing.T) {\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, publicTS, false, false, false)\n\t\t\t\tsnapshotx.SnapshotT(t, f.Ui.Nodes, snapshotx.ExceptPaths(\n\t\t\t\t\t\"1.attributes.value\",\n\t\t\t\t\t\"8.attributes.nonce\",\n\t\t\t\t\t\"8.attributes.src\",\n\t\t\t\t\t\"10.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\n\t\t\tt.Run(\"case=multi-schema-empty_flow\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\t\t\t\t})\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t\t\t{ID: \"default\", URL: \"file://./stub/identity-doesnotexist.schema.json\"},\n\t\t\t\t\t{ID: \"not-default\", URL: \"file://./stub/identity.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t})\n\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, publicTS, false, false, false, testhelpers.InitFlowWithIdentitySchema(\"not-default\"))\n\t\t\t\tsnapshotx.SnapshotT(t, f.Ui.Nodes, snapshotx.ExceptPaths(\n\t\t\t\t\t\"1.attributes.value\",\n\t\t\t\t\t\"8.attributes.nonce\",\n\t\t\t\t\t\"8.attributes.src\",\n\t\t\t\t\t\"10.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\n\t\t\tt.Run(\"case=multi-schema-invalid-form-data\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\t\t\t\t})\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t\t\t{ID: \"default\", URL: \"file://./stub/identity-doesnotexist.schema.json\"},\n\t\t\t\t\t{ID: \"not-default\", URL: \"file://./stub/identity.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t})\n\n\t\t\t\tbody := testhelpers.SubmitRegistrationForm(t, false, client, publicTS, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.email\", \"invalidemail\")\n\t\t\t\t\tv.Set(\"traits.booly\", \"true\")\n\t\t\t\t\tv.Set(\"traits.numby\", \"1\")\n\t\t\t\t\tv.Set(\"traits.stringy\", \"string\")\n\t\t\t\t\tv.Set(\"traits.should_big_number\", \"1000000\")\n\t\t\t\t\tv.Set(\"traits.should_long_string\", \"1111111111111111111111111111111111111111111111111111111111\")\n\n\t\t\t\t\tv.Set(\"method\", \"profile\")\n\t\t\t\t}, false, http.StatusOK, ui.URL, testhelpers.InitFlowWithIdentitySchema(\"not-default\"))\n\n\t\t\t\tfmt.Println(body)\n\n\t\t\t\trequire.Equal(t, int64(4000040), gjson.Get(body, \"ui.nodes.#(attributes.name==traits.email).messages.0.id\").Int(), \"%s\", body)\n\t\t\t\trequire.Equal(t, \"Enter a valid email address\", gjson.Get(body, \"ui.nodes.#(attributes.name==traits.email).messages.0.text\").String(), \"%s\", body)\n\t\t\t})\n\n\t\t\tt.Run(\"select_credentials\", func(t *testing.T) {\n\t\t\t\tres := testhelpers.SubmitRegistrationForm(t, false, client, publicTS, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.email\", \"browser-1@example.org\")\n\t\t\t\t\tv.Set(\"traits.booly\", \"true\")\n\t\t\t\t\tv.Set(\"traits.numby\", \"1\")\n\t\t\t\t\tv.Set(\"traits.stringy\", \"string\")\n\t\t\t\t\tv.Set(\"traits.should_big_number\", \"1000000\")\n\t\t\t\t\tv.Set(\"traits.should_long_string\", \"1111111111111111111111111111111111111111111111111111111111\")\n\n\t\t\t\t\tv.Set(\"method\", \"profile\")\n\t\t\t\t}, false, http.StatusOK, ui.URL)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.Get(res, \"ui.nodes\").Raw), snapshotx.ExceptPaths(\n\t\t\t\t\t\"1.attributes.value\",\n\t\t\t\t\t\"8.attributes.nonce\",\n\t\t\t\t\t\"8.attributes.src\",\n\t\t\t\t\t\"11.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\n\t\t\tt.Run(\"return_to_profile\", func(t *testing.T) {\n\t\t\t\tres := testhelpers.SubmitRegistrationForm(t, false, client, publicTS, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.email\", \"browser-1-1@example.org\")\n\t\t\t\t\tv.Set(\"traits.booly\", \"true\")\n\t\t\t\t\tv.Set(\"traits.numby\", \"1\")\n\t\t\t\t\tv.Set(\"traits.stringy\", \"string\")\n\t\t\t\t\tv.Set(\"traits.should_big_number\", \"1000000\")\n\t\t\t\t\tv.Set(\"traits.should_long_string\", \"1111111111111111111111111111111111111111111111111111111111\")\n\n\t\t\t\t\tv.Set(\"screen\", \"previous\")\n\t\t\t\t}, false, http.StatusOK, ui.URL)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.Get(res, \"ui.nodes\").Raw), snapshotx.ExceptPaths(\n\t\t\t\t\t\"1.attributes.value\",\n\t\t\t\t\t\"8.attributes.nonce\",\n\t\t\t\t\t\"8.attributes.src\",\n\t\t\t\t\t\"10.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\n\t\t\tt.Run(\"select_credentials_again\", func(t *testing.T) {\n\t\t\t\tres := testhelpers.SubmitRegistrationForm(t, false, client, publicTS, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.email\", \"browser-1-1@example.org\")\n\t\t\t\t\tv.Set(\"method\", \"profile\")\n\t\t\t\t}, false, http.StatusOK, ui.URL)\n\t\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(gjson.Get(res, \"ui.nodes\").Raw), snapshotx.ExceptPaths(\n\t\t\t\t\t\"1.attributes.value\",\n\t\t\t\t\t\"8.attributes.nonce\",\n\t\t\t\t\t\"8.attributes.src\",\n\t\t\t\t\t\"11.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\t\t})\n\t})\n}\n\nfunc TestOneStepRegistration(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePassword.String(), true)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, \"https://www.ory.sh/\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceRegistrationFlowStyle, \"unified\")\n\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\t_ = testhelpers.NewRedirSessionEchoTS(t, reg)\n\tui := testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\n\tt.Run(\"initial form is populated with identity traits\", func(t *testing.T) {\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\t\tt.Run(\"empty_flow\", func(t *testing.T) {\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, publicTS, false, false, false)\n\t\t\t\tsnapshotx.SnapshotT(t, f.Ui.Nodes, snapshotx.ExceptPaths(\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\n\t\t\tt.Run(\"case=multi-schema-empty_flow\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\t\t\t\t})\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t\t\t{ID: \"default\", URL: \"file://./stub/identity-doesnotexist.schema.json\"},\n\t\t\t\t\t{ID: \"not-default\", URL: \"file://./stub/identity.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t})\n\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, publicTS, false, false, false, testhelpers.InitFlowWithIdentitySchema(\"not-default\"))\n\t\t\t\tsnapshotx.SnapshotT(t, f.Ui.Nodes, snapshotx.ExceptPaths(\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t))\n\t\t\t})\n\n\t\t\tt.Run(\"case=multi-schema-invalid-form-data\", func(t *testing.T) {\n\t\t\t\tt.Cleanup(func() {\n\t\t\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\t\t\t\t})\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\t\t\tconf.MustSet(ctx, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t\t\t{ID: \"default\", URL: \"file://./stub/identity-doesnotexist.schema.json\"},\n\t\t\t\t\t{ID: \"not-default\", URL: \"file://./stub/identity.schema.json\", SelfserviceSelectable: true},\n\t\t\t\t})\n\n\t\t\t\tbody := testhelpers.SubmitRegistrationForm(t, false, client, publicTS, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.email\", \"invalidemail\")\n\t\t\t\t\tv.Set(\"traits.booly\", \"true\")\n\t\t\t\t\tv.Set(\"traits.numby\", \"1\")\n\t\t\t\t\tv.Set(\"traits.stringy\", \"string\")\n\t\t\t\t\tv.Set(\"traits.should_big_number\", \"1000000\")\n\t\t\t\t\tv.Set(\"traits.should_long_string\", \"1111111111111111111111111111111111111111111111111111111111\")\n\t\t\t\t\tv.Set(\"password\", \"password\")\n\n\t\t\t\t\tv.Set(\"method\", \"password\")\n\t\t\t\t}, false, http.StatusOK, ui.URL, testhelpers.InitFlowWithIdentitySchema(\"not-default\"))\n\n\t\t\t\tfmt.Println(body)\n\n\t\t\t\trequire.Equal(t, int64(4000040), gjson.Get(body, \"ui.nodes.#(attributes.name==traits.email).messages.0.id\").Int(), \"%s\", body)\n\t\t\t\trequire.Equal(t, \"Enter a valid email address\", gjson.Get(body, \"ui.nodes.#(attributes.name==traits.email).messages.0.text\").String(), \"%s\", body)\n\t\t\t})\n\t\t})\n\t})\n}\n\nfunc TestPopulateRegistrationMethod(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tctx = testhelpers.WithDefaultIdentitySchema(ctx, \"file://stub/identity.schema.json\")\n\n\ts, err := reg.AllRegistrationStrategies().Strategy(identity.CredentialsTypeProfile)\n\trequire.NoError(t, err)\n\tfh, ok := s.(registration.FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f node.Nodes) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.ResetNodes(\"csrf_token\")\n\t\tf.ResetNodes(\"passkey_challenge\")\n\t\tsnapshotx.SnapshotT(t, f, snapshotx.ExceptNestedKeys(\"nonce\", \"src\"))\n\t}\n\n\tnewFlowInternal := func(ctx context.Context, t *testing.T, identitySchema string) (*http.Request, *registration.Flow) {\n\t\tquery := \"\"\n\t\tif identitySchema != \"\" {\n\t\t\tquery = \"?identity_schema=\" + identitySchema\n\t\t}\n\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/registration/browser\"+query, nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := registration.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *registration.Flow) {\n\t\treturn newFlowInternal(ctx, t, \"\")\n\t}\n\tnewFlowWithIdentitySchema := func(ctx context.Context, t *testing.T, identitySchema string) (*http.Request, *registration.Flow) {\n\t\treturn newFlowInternal(ctx, t, identitySchema)\n\t}\n\n\tt.Run(\"method=PopulateRegistrationMethod\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethod(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodProfile\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodCredentials\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=idempotency\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\n\t\tvar snapshots []node.Nodes\n\n\t\tt.Run(\"case=1\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=2\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=3\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=4\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=evaluate\", func(t *testing.T) {\n\t\t\tassertx.EqualAsJSON(t, snapshots[0], snapshots[2])\n\t\t\tassertx.EqualAsJSON(t, snapshots[1], snapshots[3])\n\t\t})\n\t})\n\n\tt.Run(\"case=multi-schema-method=PopulateRegistrationMethodProfile\", func(t *testing.T) {\n\t\tt.Cleanup(func() {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\t\t})\n\t\tmultiSchema := contextx.WithConfigValue(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\tmultiSchema = contextx.WithConfigValue(multiSchema, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t{ID: \"default\", URL: \"file://./stub/identity-doesnotexist.schema.json\"},\n\t\t\t{ID: \"not-default\", URL: \"file://./stub/identity.schema.json\", SelfserviceSelectable: true},\n\t\t})\n\n\t\tr, f := newFlowWithIdentitySchema(multiSchema, t, \"not-default\")\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage profile\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed .schema/settings.schema.json\nvar settingsSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/profile/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage profile\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n)\n\nvar _ settings.Strategy = new(Strategy)\n\ntype (\n\tstrategyDependencies interface {\n\t\tnosurfx.CSRFProvider\n\t\tnosurfx.CSRFTokenGeneratorProvider\n\t\thttpx.WriterProvider\n\t\tlogrusx.Provider\n\t\totelx.Provider\n\n\t\tconfig.Provider\n\n\t\tcontinuity.ManagementProvider\n\n\t\tsession.HandlerProvider\n\t\tsession.ManagementProvider\n\n\t\tidentity.ValidationProvider\n\t\tidentity.ManagementProvider\n\t\tidentity.PrivilegedPoolProvider\n\n\t\terrorx.ManagementProvider\n\n\t\tsettings.HookExecutorProvider\n\t\tsettings.ErrorHandlerProvider\n\t\tsettings.FlowPersistenceProvider\n\t\tsettings.StrategyProvider\n\t\tsettings.HooksProvider\n\n\t\tregistration.FlowPersistenceProvider\n\t\tregistration.StrategyProvider\n\n\t\tschema.IdentitySchemaProvider\n\t}\n\tStrategy struct{ d strategyDependencies }\n)\n\nfunc NewStrategy(d strategyDependencies) *Strategy { return &Strategy{d: d} }\n\nfunc (s *Strategy) SettingsStrategyID() string {\n\treturn settings.StrategyProfile\n}\n\nfunc (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.profile.Strategy.PopulateSettingsMethod\")\n\tdefer otelx.End(span, &err)\n\n\tschemas, err := s.d.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttraitsSchema, err := schemas.GetByID(id.SchemaID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// use a schema compiler that disables identifiers\n\tschemaCompiler := jsonschema.NewCompiler()\n\tnodes, err := container.NodesFromJSONSchema(ctx, node.ProfileGroup, traitsSchema.URL.String(), \"\", schemaCompiler)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, n := range nodes {\n\t\tf.UI.SetNode(n)\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tf.UI.UpdateNodeValuesFromJSON(json.RawMessage(id.Traits), \"traits\", node.ProfileGroup)\n\tf.UI.Nodes.Append(node.NewInputField(\"method\", \"profile\", node.ProfileGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelSave()))\n\n\treturn nil\n}\n\nfunc (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.profile.Strategy.Settings\")\n\tdefer otelx.End(span, &err)\n\n\tvar p updateSettingsFlowWithProfileMethod\n\tctxUpdate, err := settings.PrepareUpdate(s.d, w, r, f, ss, settings.ContinuityKey(s.SettingsStrategyID()), &p)\n\tif errors.Is(err, settings.ErrContinuePreviousAction) {\n\t\treturn ctxUpdate, s.continueFlow(ctx, r, ctxUpdate, p)\n\t} else if err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, nil, p, err)\n\t}\n\n\tif err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.SettingsStrategyID(), s.d); err != nil {\n\t\treturn ctxUpdate, err\n\t}\n\n\toption, err := s.newSettingsProfileDecoder(ctx, ctxUpdate.GetSessionIdentity())\n\tif err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, nil, p, err)\n\t}\n\n\tif err := decoderx.Decode(r, &p, option,\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, nil, p, err)\n\t}\n\n\t// Reset after decoding form\n\tp.SetFlowID(ctxUpdate.Flow.ID)\n\n\tif err := s.continueFlow(ctx, r, ctxUpdate, p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, nil, p, err)\n\t}\n\n\treturn ctxUpdate, nil\n}\n\nfunc (s *Strategy) continueFlow(ctx context.Context, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithProfileMethod) error {\n\tif err := flow.MethodEnabledAndAllowed(ctx, flow.SettingsFlow, s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\treturn err\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, ctxUpdate.Flow.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn err\n\t}\n\n\tif len(p.Traits) == 0 {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Did not receive any value changes.\"))\n\t}\n\n\tif err := s.hydrateForm(r, ctxUpdate.Flow, p.Traits); err != nil {\n\t\treturn err\n\t}\n\n\toptions := []identity.ManagerOption{identity.ManagerExposeValidationErrorsForInternalTypeAssertion}\n\tttl := s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)\n\tif ctxUpdate.Session.AuthenticatedAt.Add(ttl).After(time.Now()) {\n\t\toptions = append(options, identity.ManagerAllowWriteProtectedTraits)\n\t}\n\n\tupdate, err := s.d.IdentityManager().SetTraits(ctx, ctxUpdate.GetSessionIdentity().ID, identity.Traits(p.Traits), options...)\n\tif err != nil {\n\t\tif errors.Is(err, identity.ErrProtectedFieldModified) {\n\t\t\treturn settings.NewFlowNeedsReAuth()\n\t\t}\n\t\treturn err\n\t}\n\n\tctxUpdate.UpdateIdentity(update)\n\treturn nil\n}\n\n// Update Settings Flow with Profile Method\n//\n// swagger:model updateSettingsFlowWithProfileMethod\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype updateSettingsFlowWithProfileMethod struct {\n\t// Traits\n\t//\n\t// The identity's traits.\n\t//\n\t// required: true\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// Method\n\t//\n\t// Should be set to profile when trying to update a profile.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// FlowIDRequestID is the flow ID.\n\t//\n\t// swagger:ignore\n\tFlowID string `json:\"flow\"`\n\n\t// The Anti-CSRF Token\n\t//\n\t// This token is only required when performing browser flows.\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (p *updateSettingsFlowWithProfileMethod) GetFlowID() uuid.UUID {\n\treturn x.ParseUUID(p.FlowID)\n}\n\nfunc (p *updateSettingsFlowWithProfileMethod) SetFlowID(rid uuid.UUID) {\n\tp.FlowID = rid.String()\n}\n\nfunc (s *Strategy) hydrateForm(r *http.Request, ar *settings.Flow, traits json.RawMessage) error {\n\tif traits != nil {\n\t\tar.UI.Nodes.ResetNodesWithPrefix(\"traits.\")\n\t\tar.UI.UpdateNodeValuesFromJSON(traits, \"traits\", node.ProfileGroup)\n\t}\n\tar.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\n\treturn nil\n}\n\n// handleSettingsError is a convenience function for handling all types of errors that may occur (e.g. validation error)\n// during a settings request.\nfunc (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, puc *settings.UpdateContext, traits json.RawMessage, p updateSettingsFlowWithProfileMethod, err error) error {\n\tif e := new(settings.FlowNeedsReAuth); errors.As(err, &e) {\n\t\tif err := s.d.ContinuityManager().Pause(ctx, w, r,\n\t\t\tsettings.ContinuityKey(s.SettingsStrategyID()),\n\t\t\tsettings.ContinuityOptions(p, puc.GetSessionIdentity())...); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif puc.Flow != nil {\n\t\tif traits == nil {\n\t\t\tif len(p.Traits) >= 0 {\n\t\t\t\ttraits = p.Traits\n\t\t\t} else {\n\t\t\t\ttraits = json.RawMessage(puc.GetSessionIdentity().Traits)\n\t\t\t}\n\t\t}\n\n\t\tif err := s.hydrateForm(r, puc.Flow, traits); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn err\n}\n\n// newSettingsProfileDecoder returns a decoderx.HTTPDecoderOption with a JSON Schema for type assertion and\n// validation.\nfunc (s *Strategy) newSettingsProfileDecoder(ctx context.Context, i *identity.Identity) (decoderx.HTTPDecoderOption, error) {\n\tschemas, err := s.d.IdentityTraitsSchemas(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tss, err := schemas.GetByID(i.SchemaID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\traw, err := sjson.SetBytes(settingsSchema,\n\t\t\"properties.traits.$ref\", ss.URL.String()+\"#/properties/traits\")\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\to, err := decoderx.HTTPRawJSONSchemaCompiler(raw)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn o, nil\n}\n\nfunc (s *Strategy) NodeGroup() node.UiNodeGroup {\n\treturn node.ProfileGroup\n}\n\n// SortForHydration sorts the strategies so that the profile strategy is always first.\nfunc SortForHydration(strats registration.Strategies) registration.Strategies {\n\tsorted := make(registration.Strategies, len(strats))\n\tcopy(sorted, strats)\n\n\tfor i, strat := range sorted {\n\t\tif strat.ID() == identity.CredentialsTypeProfile {\n\t\t\tsorted = append([]registration.Strategy{strat}, append(sorted[:i], sorted[i+1:]...)...)\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn sorted\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/strategy_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage profile_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/strategy/code\"\n\t\"github.com/ory/kratos/selfservice/strategy/oidc\"\n\t\"github.com/ory/kratos/selfservice/strategy/passkey\"\n\t\"github.com/ory/kratos/selfservice/strategy/password\"\n\t\"github.com/ory/kratos/selfservice/strategy/webauthn\"\n\n\t\"github.com/ory/kratos/selfservice/strategy/profile\"\n\n\t\"github.com/ory/x/jsonx\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\n\t\"github.com/ory/kratos/corpx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc newIdentityWithPassword(email string) *identity.Identity {\n\treturn &identity.Identity{\n\t\tID: x.NewUUID(),\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\"password\": {Type: \"password\", Identifiers: []string{email}, Config: sqlxx.JSONRawMessage(`{\"hashed_password\":\"foo\"}`)},\n\t\t},\n\t\tTraits:              identity.Traits(`{\"email\":\"` + email + `\",\"stringy\":\"foobar\",\"booly\":false,\"numby\":2.5,\"should_long_string\":\"asdfasdfasdfasdfasfdasdfasdfasdf\",\"should_big_number\":2048}`),\n\t\tSchemaID:            config.DefaultIdentityTraitsSchemaID,\n\t\tState:               identity.StateActive,\n\t\tVerifiableAddresses: []identity.VerifiableAddress{{Value: email, Via: identity.AddressTypeEmail}},\n\t\t// TO ADD - RECOVERY EMAIL,\n\t}\n}\n\nfunc TestStrategyTraits(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, \"https://www.ory.sh/\")\n\ttesthelpers.StrategyEnable(t, conf, identity.CredentialsTypePassword.String(), true)\n\ttesthelpers.StrategyEnable(t, conf, settings.StrategyProfile, true)\n\n\tsetPrivilegedTime := func(t *testing.T, duration time.Duration) {\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, duration.String())\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"1ns\")\n\t\t})\n\t}\n\n\tsetPrivileged := func(t *testing.T) {\n\t\tsetPrivilegedTime(t, time.Minute*10)\n\t}\n\n\tsetUnprivileged := func(t *testing.T) {\n\t\tsetPrivilegedTime(t, time.Nanosecond)\n\t}\n\n\tui := testhelpers.NewSettingsUIEchoServer(t, reg)\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\tbrowserIdentity1 := newIdentityWithPassword(\"john-browser@doe.com\")\n\tapiIdentity1 := newIdentityWithPassword(\"john-api@doe.com\")\n\tbrowserID2 := x.NewUUID()\n\tbrowserIdentity2 := &identity.Identity{ID: browserID2, Traits: identity.Traits(`{}`), State: identity.StateActive, Credentials: map[identity.CredentialsType]identity.Credentials{\n\t\tidentity.CredentialsTypePassword: {Type: \"password\", Identifiers: []string{browserID2.String()}, Config: []byte(`{\"hashed_password\":\"$2a$04$zvZz1zV\"}`)},\n\t}}\n\tapiID2 := x.NewUUID()\n\tapiIdentity2 := &identity.Identity{ID: apiID2, Traits: identity.Traits(`{}`), State: identity.StateActive, Credentials: map[identity.CredentialsType]identity.Credentials{\n\t\tidentity.CredentialsTypePassword: {Type: \"password\", Identifiers: []string{apiID2.String()}, Config: []byte(`{\"hashed_password\":\"$2a$04$zvZz1zV\"}`)},\n\t}}\n\n\tbrowserUser1 := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, browserIdentity1)\n\tbrowserUser1.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\tbrowserUser2 := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, browserIdentity2)\n\tbrowserUser2.Jar.SetCookies(nosurfx.WithFakeCSRFCookie(t, reg, publicTS.URL))\n\tapiUser1 := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, apiIdentity1)\n\tapiUser2 := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, apiIdentity2)\n\n\tt.Run(\"description=not authorized to call endpoints without a session\", func(t *testing.T) {\n\t\tsetUnprivileged(t)\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tres, err := http.DefaultClient.Do(httpx.MustNewRequest(\"POST\", publicTS.URL+settings.RouteSubmitFlow, strings.NewReader(url.Values{\"foo\": {\"bar\"}}.Encode()), \"application/x-www-form-urlencoded\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode, \"%+v\", res.Request)\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.GetProvider(ctx).String(config.ViperKeySelfServiceLoginUI))\n\t\t})\n\n\t\tt.Run(\"type=api/spa\", func(t *testing.T) {\n\t\t\tres, err := http.DefaultClient.Do(httpx.MustNewRequest(\"POST\", publicTS.URL+settings.RouteSubmitFlow, strings.NewReader(`{\"foo\":\"bar\"}`), \"application/json\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode)\n\t\t})\n\t})\n\n\tt.Run(\"description=should fail to post data if CSRF is invalid/type=browser\", func(t *testing.T) {\n\t\tsetUnprivileged(t)\n\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, false, publicTS)\n\n\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, false, f, browserUser1,\n\t\t\turl.Values{\"traits.booly\": {\"true\"}, \"csrf_token\": {\"invalid\"}, \"method\": {\"profile\"}}.Encode())\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"should return a 400 error because CSRF token is not set\\n\\t%s\", actual)\n\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenServerTokenMismatch, json.RawMessage(actual), \"%s\", actual)\n\t})\n\n\tt.Run(\"description=should fail to post data if CSRF is invalid/type=spa\", func(t *testing.T) {\n\t\tsetUnprivileged(t)\n\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, true, publicTS)\n\n\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, true, f, browserUser1,\n\t\t\ttesthelpers.EncodeFormAsJSON(t, true, url.Values{\"traits.booly\": {\"true\"}, \"csrf_token\": {\"invalid\"}, \"method\": {\"profile\"}}))\n\t\tassert.EqualValues(t, http.StatusForbidden, res.StatusCode, \"should return a 400 error because CSRF token is not set\\n\\t%s\", actual)\n\t\tassertx.EqualAsJSON(t, nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch, json.RawMessage(gjson.Get(actual, \"error\").Raw), \"%s\", actual)\n\t})\n\n\tt.Run(\"description=should not fail because of CSRF token but because of unprivileged/type=api\", func(t *testing.T) {\n\t\tsetUnprivileged(t)\n\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiUser1, publicTS)\n\n\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, f, apiUser1, `{\"traits.booly\":true,\"method\":\"profile\",\"csrf_token\":\"`+nosurfx.FakeCSRFToken+`\"}`)\n\t\tassert.EqualValues(t, http.StatusForbidden, res.StatusCode, \"should return a 403 error because the session is unprivileged\\n\\t%s\", actual)\n\t\trequire.Len(t, res.Cookies(), 1)\n\t\tassert.Equal(t, \"ory_kratos_continuity\", res.Cookies()[0].Name)\n\t\tassert.Contains(t, gjson.Get(actual, \"error.reason\").String(), \"login session is too old\", actual)\n\t})\n\n\tt.Run(\"case=should fail with correct CSRF error cause/type=api\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\tfor k, tc := range []struct {\n\t\t\tmod func(http.Header)\n\t\t\texp string\n\t\t}{\n\t\t\t{\n\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\th.Add(\"Cookie\", \"name=bar\")\n\t\t\t\t},\n\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Cookie\\\\\\\" key\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tmod: func(h http.Header) {\n\t\t\t\t\th.Add(\"Origin\", \"www.bar.com\")\n\t\t\t\t},\n\t\t\t\texp: \"The HTTP Request Header included the \\\\\\\"Origin\\\\\\\" key\",\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiUser1, publicTS)\n\n\t\t\t\treq := testhelpers.NewPostRequest(t, true, f.Ui.Action, bytes.NewBufferString(`{\"traits.booly\":true,\"method\":\"profile\",\"csrf_token\":\"invalid\"}`))\n\t\t\t\ttc.mod(req.Header)\n\n\t\t\t\tres, err := apiUser1.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tactual := string(ioutilx.MustReadAll(res.Body))\n\t\t\t\tassert.EqualValues(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t\tassert.Contains(t, actual, tc.exp, \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"description=hydrate the proper fields\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\trun := func(t *testing.T, id *identity.Identity, payload *kratos.SettingsFlow, route string) {\n\t\t\tassert.NotEmpty(t, payload.Identity)\n\t\t\tassert.Equal(t, id.ID.String(), string(payload.Identity.Id))\n\t\t\tassert.JSONEq(t, string(id.Traits), x.MustEncodeJSON(t, payload.Identity.Traits))\n\t\t\tassert.Equal(t, id.SchemaID, payload.Identity.SchemaId)\n\t\t\tassert.Equal(t, publicTS.URL+route, payload.RequestUrl)\n\n\t\t\tactual := jsonx.TestMarshalJSONString(t, payload.Ui)\n\t\t\tassert.EqualValues(t, payload.Identity.Traits.(map[string]interface{})[\"email\"], gjson.Get(actual, \"nodes.#(attributes.name==traits.email).attributes.value\").String())\n\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"nodes.#(attributes.name==csrf_token).attributes.value\").String(), \"csrf token missing\")\n\n\t\t\ttesthelpers.SnapshotTExcept(t, payload.Ui, []string{\"action\", \"nodes.0.attributes.value\", \"nodes.1.attributes.value\"})\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tpr, _, err := testhelpers.NewSDKCustomClient(publicTS, apiUser1).FrontendAPI.CreateNativeSettingsFlow(context.Background()).Execute()\n\t\t\trequire.NoError(t, err)\n\t\t\trun(t, apiIdentity1, pr, settings.RouteInitAPIFlow)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tpr, _, err := testhelpers.NewSDKCustomClient(publicTS, browserUser1).FrontendAPI.CreateBrowserSettingsFlow(context.Background()).Execute()\n\t\t\trequire.NoError(t, err)\n\t\t\trun(t, browserIdentity1, pr, settings.RouteInitBrowserFlow)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tres, err := browserUser1.Get(publicTS.URL + settings.RouteInitBrowserFlow)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Contains(t, res.Request.URL.String(), ui.URL+\"/settings?flow\")\n\n\t\t\trid := res.Request.URL.Query().Get(\"flow\")\n\t\t\trequire.NotEmpty(t, rid)\n\n\t\t\tpr, _, err := testhelpers.NewSDKCustomClient(publicTS, browserUser1).FrontendAPI.GetSettingsFlow(context.Background()).Id(res.Request.URL.Query().Get(\"flow\")).Execute()\n\t\t\trequire.NoError(t, err, \"%s\", rid)\n\n\t\t\trun(t, browserIdentity1, pr, settings.RouteInitBrowserFlow)\n\t\t})\n\t})\n\n\texpectValidationError := func(t *testing.T, isAPI, isSPA bool, hc *http.Client, values func(url.Values)) string {\n\t\treturn testhelpers.SubmitSettingsForm(t, isAPI, isSPA, hc, publicTS, values,\n\t\t\ttesthelpers.ExpectStatusCode(isAPI || isSPA, http.StatusBadRequest, http.StatusOK),\n\t\t\ttesthelpers.ExpectURL(isAPI || isSPA, publicTS.URL+settings.RouteSubmitFlow, conf.SelfServiceFlowSettingsUI(ctx).String()))\n\t}\n\n\tt.Run(\"description=should come back with form errors if some profile data is invalid\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==csrf_token).attributes.value\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"too-short\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_long_string).attributes.value\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"bazbar\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.stringy).attributes.value\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"2.5\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.numby).attributes.value\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"length must be >= 25, but got 9\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_long_string).messages.0.text\").String(), \"%s\", actual)\n\t\t}\n\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"method\", \"profile\")\n\t\t\tv.Set(\"traits.should_long_string\", \"too-short\")\n\t\t\tv.Set(\"traits.stringy\", \"bazbar\")\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, apiUser1, payload))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, true, browserUser1, payload))\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, browserUser1, payload))\n\t\t})\n\t})\n\n\tt.Run(\"description=should not be able to make requests for another user\", func(t *testing.T) {\n\t\tsetUnprivileged(t)\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiUser1, publicTS)\n\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, true, false, f, apiUser2, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"error.reason\").String(), \"initiated by someone else\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, browserUser1, publicTS)\n\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, true, f, browserUser2, testhelpers.EncodeFormAsJSON(t, true, values))\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"error.reason\").String(), \"initiated by someone else\", \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, false, publicTS)\n\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, false, false, f, browserUser2, values.Encode())\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"reason\").String(), \"initiated by someone else\", \"%s\", actual)\n\t\t})\n\t})\n\n\tt.Run(\"description=should end up at the login endpoint if trying to update protected field without sudo mode\", func(t *testing.T) {\n\t\trun := func(t *testing.T, config *kratos.SettingsFlow, isAPI bool, c *http.Client) *http.Response {\n\t\t\ttime.Sleep(time.Millisecond)\n\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(config.Ui.Nodes)\n\t\t\tvalues.Set(\"method\", \"profile\")\n\t\t\tvalues.Set(\"traits.email\", \"not-john-doe@foo.bar\")\n\t\t\tres, err := c.PostForm(config.Ui.Action, values)\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\treturn res\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tsetUnprivileged(t)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiUser1, publicTS)\n\t\t\tres := run(t, f, true, apiUser1)\n\t\t\tassert.EqualValues(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t})\n\n\t\tt.Run(\"type=sqa\", func(t *testing.T) {\n\t\t\tsetUnprivileged(t)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, true, publicTS)\n\t\t\tres := run(t, f, true, browserUser1)\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.GetProvider(ctx).String(config.ViperKeySelfServiceLoginUI))\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tsetUnprivileged(t)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, false, publicTS)\n\t\t\tres := run(t, f, false, browserUser1)\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), conf.GetProvider(ctx).String(config.ViperKeySelfServiceLoginUI))\n\n\t\t\tt.Run(\"should update when signed back in\", func(t *testing.T) {\n\t\t\t\tsetPrivileged(t)\n\t\t\t\tres, err := browserUser1.Get(f.Ui.Action)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\t\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.GetBytes(body, \"state\").String(), \"%s\", body)\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"flow=fail first update\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, flow.StateShowForm, gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"1\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_big_number).attributes.value\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"must be >= 1200 but found 1\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_big_number).messages.0.text\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"foobar\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.stringy).attributes.value\").String(), \"%s\", actual) // sanity check if original payload is still here\n\t\t}\n\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"method\", settings.StrategyProfile)\n\t\t\tv.Set(\"traits.should_big_number\", \"1\")\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, apiUser1, payload))\n\t\t})\n\n\t\tt.Run(\"type=sqa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, true, browserUser1, payload))\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, browserUser1, payload))\n\t\t})\n\t})\n\n\tt.Run(\"flow=fail second update\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, flow.StateShowForm, gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_big_number).messages.0.text\").String(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_big_number).attributes.value\").String(), \"%s\", actual)\n\n\t\t\tassert.Equal(t, \"short\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_long_string).attributes.value\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"length must be >= 25, but got 5\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_long_string).messages.0.text\").String(), \"%s\", actual)\n\n\t\t\tassert.Equal(t, \"this-is-not-a-number\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.numby).attributes.value\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"expected number, but got string\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.numby).messages.0.text\").String(), \"%s\", actual)\n\n\t\t\tassert.Equal(t, \"foobar\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.stringy).attributes.value\").String(), \"%s\", actual) // sanity check if original payload is still here\n\t\t}\n\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"method\", settings.StrategyProfile)\n\t\t\tv.Del(\"traits.should_big_number\")\n\t\t\tv.Set(\"traits.should_long_string\", \"short\")\n\t\t\tv.Set(\"traits.numby\", \"this-is-not-a-number\")\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, apiUser1, payload))\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, true, browserUser1, payload))\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, browserUser1, payload))\n\t\t})\n\t})\n\n\texpectSuccess := func(t *testing.T, isAPI, isSPA bool, hc *http.Client, values func(url.Values)) string {\n\t\treturn testhelpers.SubmitSettingsForm(t, isAPI, isSPA, hc, publicTS, values,\n\t\t\thttp.StatusOK,\n\t\t\ttesthelpers.ExpectURL(isAPI || isSPA, publicTS.URL+settings.RouteSubmitFlow, conf.SelfServiceFlowSettingsUI(ctx).String()))\n\t}\n\n\tt.Run(\"flow=succeed with final request\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.numby).attributes.errors\").Value(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_big_number).attributes.errors\").Value(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_long_string).attributes.errors\").Value(), \"%s\", actual)\n\n\t\t\tassert.Equal(t, 15.0, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.numby).attributes.value\").Value(), \"%s\", actual)\n\t\t\tassert.Equal(t, 9001.0, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_big_number).attributes.value\").Value(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"this is such a long string, amazing stuff!\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_long_string).attributes.value\").Value(), \"%s\", actual)\n\t\t}\n\n\t\tpayload := func(newEmail string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(\"method\", settings.StrategyProfile)\n\t\t\t\tv.Set(\"traits.email\", newEmail)\n\t\t\t\tv.Set(\"traits.numby\", \"15\")\n\t\t\t\tv.Set(\"traits.should_big_number\", \"9001\")\n\t\t\t\tv.Set(\"traits.should_long_string\", \"this is such a long string, amazing stuff!\")\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual := expectSuccess(t, true, false, apiUser1, payload(\"not-john-doe-api@mail.com\"))\n\t\t\tcheck(t, actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=sqa\", func(t *testing.T) {\n\t\t\tactual := expectSuccess(t, false, true, browserUser1, payload(\"not-john-doe-browser@mail.com\"))\n\t\t\tcheck(t, actual)\n\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(actual, \"continue_with.0.action\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"continue_with.0.redirect_browser_to\").String(), ui.URL, \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tactual := expectSuccess(t, false, false, browserUser1, payload(\"not-john-doe-browser@mail.com\"))\n\t\t\tcheck(t, actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t})\n\t})\n\n\tt.Run(\"flow=try another update with invalid data\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\tcheck := func(t *testing.T, actual string) {\n\t\t\tassert.EqualValues(t, flow.StateShowForm, gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\t\t}\n\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"method\", settings.StrategyProfile)\n\t\t\tv.Set(\"traits.should_long_string\", \"short\")\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, true, false, apiUser1, payload))\n\t\t})\n\n\t\tt.Run(\"type=sqa\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, true, browserUser1, payload))\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tcheck(t, expectValidationError(t, false, false, browserUser1, payload))\n\t\t})\n\t})\n\n\tt.Run(\"description=ensure that hooks are running\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\tvar returned bool\n\t\trouter := http.NewServeMux()\n\t\trouter.HandleFunc(\"GET /return-ts\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\treturned = true\n\t\t})\n\t\trts := httptest.NewServer(router)\n\t\tt.Cleanup(rts.Close)\n\n\t\ttesthelpers.SelfServiceHookSettingsSetDefaultRedirectTo(t, conf, rts.URL+\"/return-ts\")\n\t\tt.Cleanup(testhelpers.SelfServiceHookConfigReset(t, conf))\n\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserUser1, false, publicTS)\n\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", settings.StrategyProfile)\n\t\tvalues.Set(\"traits.should_big_number\", \"9001\")\n\t\tres, err := browserUser1.PostForm(f.Ui.Action, values)\n\n\t\trequire.NoError(t, err)\n\t\tdefer func() { _ = res.Body.Close() }()\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, returned, \"%d - %s\", res.StatusCode, body)\n\t})\n\n\t// Update the login endpoint to auto-accept any incoming login request!\n\t_ = testhelpers.NewSettingsLoginAcceptAPIServer(t, testhelpers.NewSDKCustomClient(publicTS, browserUser1), conf)\n\n\tt.Run(\"description=should send email with verifiable address\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\tconf.MustSet(ctx, config.ViperKeySelfServiceVerificationEnabled, true)\n\t\tconf.MustSet(ctx, config.ViperKeyCourierSMTPURL, \"smtp://foo:bar@irrelevant.com/\")\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(ctx, config.HookStrategyKey(config.ViperKeySelfServiceSettingsAfter, settings.StrategyProfile), nil)\n\t\t})\n\n\t\tcheck := func(t *testing.T, actual, newEmail string) {\n\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\t\t\tassert.Equal(t, newEmail, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.email).attributes.value\").Value(), \"%s\", actual)\n\n\t\t\tm, err := reg.CourierPersister().LatestQueuedMessage(context.Background())\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Contains(t, m.Subject, \"Use code\")\n\t\t}\n\n\t\tpayload := func(newEmail string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(\"method\", settings.StrategyProfile)\n\t\t\t\tv.Set(\"traits.email\", newEmail)\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tnewEmail := \"update-verify-api@mail.com\"\n\t\t\tactual := expectSuccess(t, true, false, apiUser1, payload(newEmail))\n\t\t\tcheck(t, actual, newEmail)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tnewEmail := \"update-verify-browser@mail.com\"\n\t\t\tactual := expectSuccess(t, false, true, browserUser1, payload(newEmail))\n\t\t\tcheck(t, actual, newEmail)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tnewEmail := \"update-verify-browser@mail.com\"\n\t\t\tactual := expectSuccess(t, false, false, browserUser1, payload(newEmail))\n\t\t\tcheck(t, actual, newEmail)\n\t\t})\n\t})\n\n\tt.Run(\"description=should update protected field with sudo mode\", func(t *testing.T) {\n\t\tsetPrivileged(t)\n\n\t\tcheck := func(t *testing.T, newEmail string, actual string) {\n\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(actual, \"state\").String(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.numby).attributes.errors\").Value(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_big_number).attributes.errors\").Value(), \"%s\", actual)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.should_long_string).attributes.errors\").Value(), \"%s\", actual)\n\t\t\tassert.Equal(t, newEmail, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.email).attributes.value\").Value(), \"%s\", actual)\n\t\t\tassert.Equal(t, \"foobar\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.stringy).attributes.value\").String(), \"%s\", actual) // sanity check if original payload is still here\n\t\t}\n\n\t\tpayload := func(email string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(\"method\", settings.StrategyProfile)\n\t\t\t\tv.Set(\"traits.email\", email)\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\temail := \"not-john-doe-api@mail.com\"\n\t\t\tactual := expectSuccess(t, true, false, apiUser1, payload(email))\n\t\t\tcheck(t, email, actual)\n\t\t})\n\n\t\tt.Run(\"type=sqa\", func(t *testing.T) {\n\t\t\temail := \"not-john-doe-browser@mail.com\"\n\t\t\tactual := expectSuccess(t, false, true, browserUser1, payload(email))\n\t\t\tcheck(t, email, actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\temail := \"not-john-doe-browser@mail.com\"\n\t\t\tactual := expectSuccess(t, false, false, browserUser1, payload(email))\n\t\t\tcheck(t, email, actual)\n\t\t})\n\t})\n}\n\nfunc TestDisabledEndpoint(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/identity.schema.json\")\n\ttesthelpers.StrategyEnable(t, conf, settings.StrategyProfile, false)\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\tbrowserIdentity1 := newIdentityWithPassword(\"john-browser@doe.com\")\n\tbrowserUser1 := testhelpers.NewHTTPClientWithIdentitySessionCookie(context.Background(), t, reg, browserIdentity1)\n\n\tt.Run(\"case=should not submit when profile method is disabled\", func(t *testing.T) {\n\t\tt.Run(\"method=GET\", func(t *testing.T) {\n\t\t\tt.Skip(\"GET is not supported here\")\n\t\t})\n\n\t\tt.Run(\"method=POST\", func(t *testing.T) {\n\t\t\tb := testhelpers.SubmitSettingsForm(t, false, false, browserUser1, publicTS, func(v url.Values) {\n\t\t\t\tv.Set(\"method\", settings.StrategyProfile)\n\t\t\t},\n\t\t\t\thttp.StatusOK,\n\t\t\t\ttesthelpers.ExpectURL(false, publicTS.URL+settings.RouteSubmitFlow, errTS.URL))\n\n\t\t\tassert.Contains(t, string(b), \"404\")\n\t\t\tassert.Contains(t, string(b), \"This endpoint was disabled by system administrator\")\n\t\t})\n\t})\n}\n\nfunc TestSortedForHydration(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\t// Get a reference to all registration strategies\n\tallStrategies := []registration.Strategy{\n\t\tpassword.NewStrategy(reg),\n\t\tcode.NewStrategy(reg),\n\t\toidc.NewStrategy(reg),\n\t\tcode.NewStrategy(reg),\n\t\tpasskey.NewStrategy(reg),\n\t\tpasskey.NewStrategy(reg),\n\t\tprofile.NewStrategy(reg),\n\t\twebauthn.NewStrategy(reg),\n\t}\n\n\tvar originalOrder []string\n\tfor _, s := range allStrategies {\n\t\tif s.ID().String() == \"profile\" {\n\t\t\tcontinue\n\t\t}\n\t\toriginalOrder = append(originalOrder, s.ID().String())\n\t}\n\n\tvar actual []string\n\tfor _, s := range profile.SortForHydration(allStrategies) {\n\t\tactual = append(actual, s.ID().String())\n\t}\n\n\tassert.EqualValues(t, append([]string{\"profile\"}, originalOrder...), actual)\n}\n"
  },
  {
    "path": "selfservice/strategy/profile/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"booly\": {\n          \"type\": \"boolean\"\n        },\n        \"should_big_number\": {\n          \"type\": \"number\",\n          \"minimum\": 1200\n        },\n        \"should_long_string\": {\n          \"type\": \"string\",\n          \"minLength\": 25\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/saml/login.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage saml\n\nimport \"encoding/json\"\n\n// Update login flow using SAML\n//\n// swagger:model updateLoginFlowWithSamlMethod\ntype _ struct {\n\t// The provider to register with\n\t//\n\t// required: true\n\tProvider string `json:\"provider\"`\n\n\t// The CSRF Token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Method to use\n\t//\n\t// This field must be set to `saml` when using the saml method.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n"
  },
  {
    "path": "selfservice/strategy/saml/registration.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage saml\n\nimport \"encoding/json\"\n\n// Update registration flow using SAML\n//\n// swagger:model updateRegistrationFlowWithSamlMethod\ntype _ struct {\n\t// The provider to register with\n\t//\n\t// required: true\n\tProvider string `json:\"provider\"`\n\n\t// The CSRF Token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// The identity traits\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// Method to use\n\t//\n\t// This field must be set to `saml` when using the saml method.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n"
  },
  {
    "path": "selfservice/strategy/saml/settings.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage saml\n\nimport \"encoding/json\"\n\n// Update settings flow using SAML\n//\n// swagger:model updateSettingsFlowWithSamlMethod\ntype _ struct {\n\t// Method\n\t//\n\t// Should be set to saml when trying to update a profile.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Link this provider\n\t//\n\t// Either this or `unlink` must be set.\n\t//\n\t// type: string\n\t// in: body\n\tLink string `json:\"link\"`\n\n\t// Unlink this provider\n\t//\n\t// Either this or `link` must be set.\n\t//\n\t// type: string\n\t// in: body\n\tUnlink string `json:\"unlink\"`\n\n\t// Flow ID is the flow's ID.\n\t//\n\t// in: query\n\tFlowID string `json:\"flow\"`\n\n\t// The CSRF Token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// The identity's traits\n\t//\n\t// in: body\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/.schema/login.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/totp/login.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\n    \"totp_code\",\n    \"method\"\n  ],\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"totp_code\": {\n      \"type\": \"string\",\n      \"minLength\": 6\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/.schema/settings.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/totp/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"totp_code\": {\n      \"type\": \"string\"\n    },\n    \"totp_unlink\": {\n      \"type\": \"boolean\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  },\n  \"if\": {\n    \"properties\": {\n      \"method\": {\n        \"const\": \"totp\"\n      }\n    }\n  },\n  \"then\": {\n    \"totp_code\": {\n      \"type\": \"string\",\n      \"maxLength\": 6,\n      \"minLength\": 6\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/.snapshots/TestCompleteLogin-case=totp_payload_is_set_when_identity_has_totp.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"totp_code\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"totp\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010006,\n        \"text\": \"Authentication code\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"totp\"\n    },\n    \"group\": \"totp\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010009,\n        \"text\": \"Use Authenticator\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/totp/.snapshots/TestCompleteSettings-case=device_setup_is_available_when_identity_has_no_totp_yet.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"height\": 256,\n      \"id\": \"totp_qr\",\n      \"node_type\": \"img\",\n      \"width\": 256\n    },\n    \"group\": \"totp\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050005,\n        \"text\": \"Authenticator app QR code\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"img\"\n  },\n  {\n    \"attributes\": {\n      \"id\": \"totp_secret_key\",\n      \"node_type\": \"text\",\n      \"text\": {\n        \"context\": {\n        },\n        \"id\": 1050006,\n        \"type\": \"info\"\n      }\n    },\n    \"group\": \"totp\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050017,\n        \"text\": \"This is your authenticator app secret. Use it if you can not scan the QR code.\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"text\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"totp_code\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"totp\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070006,\n        \"text\": \"Verify code\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"totp\"\n    },\n    \"group\": \"totp\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/totp/.snapshots/TestCompleteSettings-case=device_unlinking_is_available_when_identity_has_totp.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"totp_unlink\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"submit\",\n      \"value\": \"true\"\n    },\n    \"group\": \"totp\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050004,\n        \"text\": \"Unlink TOTP Authenticator App\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/totp/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"totp\",\n    \"attributes\": {\n      \"name\": \"totp_code\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010006,\n        \"text\": \"Authentication code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"totp\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"totp\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010009,\n        \"text\": \"Use Authenticator\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/totp/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactorRefresh.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"totp\",\n    \"attributes\": {\n      \"name\": \"totp_code\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010006,\n        \"text\": \"Authentication code\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"totp\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"totp\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010009,\n        \"text\": \"Use Authenticator\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/totp/generator.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"image/png\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/pquerna/otp\"\n\tstdtotp \"github.com/pquerna/otp/totp\"\n\n\t\"github.com/ory/kratos/driver/config\"\n)\n\n// rfc4226 recommends:\n//\n// The algorithm MUST use a strong shared secret. The length of\n// the shared secret MUST be at least 128 bits. This document\n// RECOMMENDs a shared secret length of 160 bits.\n//\n// So we need 160/8 = 20 key length. stdtotp.Generate uses the key\n// length for reading from crypto.Rand.\nconst secretSize = 160 / 8\nconst digits = otp.DigitsSix\n\nfunc NewKey(ctx context.Context, accountName string, d interface {\n\tconfig.Provider\n}) (*otp.Key, error) {\n\tkey, err := stdtotp.Generate(stdtotp.GenerateOpts{\n\t\tIssuer:      d.Config().TOTPIssuer(ctx),\n\t\tAccountName: accountName,\n\t\tSecretSize:  secretSize,\n\t\tDigits:      digits,\n\t\tPeriod:      30,\n\t})\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn key, err\n}\n\nfunc KeyToHTMLImage(key *otp.Key) (string, error) {\n\tvar buf bytes.Buffer\n\timg, err := key.Image(256, 256)\n\tif err != nil {\n\t\treturn \"\", errors.WithStack(err)\n\t}\n\n\tif err := png.Encode(&buf, img); err != nil {\n\t\treturn \"\", errors.WithStack(err)\n\t}\n\n\treturn \"data:image/png;base64,\" + base64.StdEncoding.EncodeToString(buf.Bytes()), nil\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/generator_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/contextx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/totp\"\n)\n\nfunc TestGenerator(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tt.Run(\"no issuer set\", func(t *testing.T) {\n\t\tkey, err := totp.NewKey(t.Context(), \"foo\", reg)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, reg.Config().SelfPublicURL(t.Context()).Hostname(), key.Issuer(), \"if issuer is not set explicitly it should be the public URL\")\n\t})\n\n\tt.Run(\"custom issuer set\", func(t *testing.T) {\n\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyTOTPIssuer, \"foobar.com\")\n\n\t\tkey, err := totp.NewKey(ctx, \"foo\", reg)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, \"foobar.com\", key.Issuer(), \"if issuer is set explicitly it should be the correct value\")\n\t\tassert.Equal(t, \"foo\", key.AccountName())\n\t})\n\n\tt.Run(\"generate HTML image\", func(t *testing.T) {\n\t\tkey, err := totp.NewKey(t.Context(), \"foo\", reg)\n\t\trequire.NoError(t, err)\n\n\t\timg, err := totp.KeyToHTMLImage(key)\n\t\trequire.NoError(t, err)\n\t\tassert.Truef(t, strings.HasPrefix(img, \"data:image/png;base64,\"), \"image is a base64 encoded png: %s\", img)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/pquerna/otp\"\n\t\"github.com/pquerna/otp/totp\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nfunc (s *Strategy) PopulateLoginMethod(r *http.Request, requestedAAL identity.AuthenticatorAssuranceLevel, sr *login.Flow) error {\n\t// This strategy can only solve AAL2\n\tif requestedAAL != identity.AuthenticatorAssuranceLevel2 {\n\t\treturn nil\n\t}\n\n\t// We have done proper validation before so this should never error\n\tsess, err := s.d.SessionManager().FetchFromRequest(r.Context(), r)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tid, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), sess.IdentityID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, ok := id.GetCredentials(s.ID())\n\tif !ok {\n\t\t// Identity has no TOTP\n\t\treturn nil\n\t}\n\n\tsr.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tsr.UI.SetNode(node.NewInputField(\"totp_code\", \"\", node.TOTPGroup, node.InputAttributeTypeText, node.WithRequiredInputAttribute).WithMetaLabel(text.NewInfoLoginTOTPLabel()))\n\tsr.UI.GetNodes().Append(node.NewInputField(\"method\", s.ID(), node.TOTPGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoLoginTOTP()))\n\n\treturn nil\n}\n\nfunc (s *Strategy) handleLoginError(r *http.Request, f *login.Flow, err error) error {\n\tif f != nil {\n\t\tf.UI.Nodes.ResetNodes(\"totp_code\")\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn err\n}\n\n// Update Login Flow with TOTP Method\n//\n// swagger:model updateLoginFlowWithTotpMethod\ntype updateLoginFlowWithTotpMethod struct {\n\t// Method should be set to \"totp\" when logging in using the TOTP strategy.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// The TOTP code.\n\t//\n\t// required: true\n\tTOTPCode string `json:\"totp_code\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) Login(_ http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (i *identity.Identity, err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.totp.Strategy.Login\")\n\tdefer otelx.End(span, &err)\n\n\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel2); err != nil {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"requested AAL is not AAL2\"))\n\t\treturn nil, err\n\t}\n\n\tif err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.ID().String(), s.d); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar p updateLoginFlowWithTotpMethod\n\tif err := decoderx.Decode(r, &p,\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.MustHTTPRawJSONSchemaCompiler(loginSchema),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\tf.TransientPayload = p.TransientPayload\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\ti, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), sess.IdentityID.String())\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoTOTPDeviceRegistered()))\n\t}\n\n\tvar o identity.CredentialsTOTPConfig\n\tif err := json.Unmarshal(c.Config, &o); err != nil {\n\t\treturn nil, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"The TOTP credentials could not be decoded properly\").WithDebug(err.Error()).WithWrap(err)), i.ID)\n\t}\n\n\tkey, err := otp.NewKeyFromURL(o.TOTPURL)\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(err), i.ID))\n\t}\n\n\tif !totp.Validate(p.TOTPCode, key.Secret()) {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(schema.NewTOTPVerifierWrongError(\"#/\")), i.ID))\n\t}\n\n\tf.Active = s.ID()\n\tif err = s.d.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, x.WrapWithIdentityIDError(errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Could not update flow\").WithDebug(err.Error())), i.ID))\n\t}\n\n\treturn i, nil\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/login_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pquerna/otp\"\n\tstdtotp \"github.com/pquerna/otp/totp\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/totp\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/snapshotx\"\n\t\"github.com/ory/x/sqlxx\"\n)\n\nconst totpCodeGJSONQuery = \"ui.nodes.#(attributes.name==totp_code)\"\n\nfunc createIdentityWithoutTOTP(ctx context.Context, t *testing.T, reg driver.Registry) *identity.Identity {\n\tid, _, _ := createIdentity(ctx, t, reg)\n\tdelete(id.Credentials, identity.CredentialsTypeTOTP)\n\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(ctx, id))\n\treturn id\n}\n\nfunc createIdentity(ctx context.Context, t *testing.T, reg driver.Registry) (*identity.Identity, string, *otp.Key) {\n\tidentifier := x.NewUUID().String() + \"@ory.sh\"\n\tpassword := x.NewUUID().String()\n\tkey, err := totp.NewKey(ctx, \"foo\", reg)\n\trequire.NoError(t, err)\n\tp, err := reg.Hasher(ctx).Generate(ctx, []byte(password))\n\trequire.NoError(t, err)\n\ti := &identity.Identity{\n\t\tTraits: identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, identifier)),\n\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t{\n\t\t\t\tValue:     identifier,\n\t\t\t\tVerified:  false,\n\t\t\t\tCreatedAt: time.Now(),\n\t\t\t},\n\t\t},\n\t}\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, i))\n\ti.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\tidentity.CredentialsTypePassword: {\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{identifier},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t},\n\t\tidentity.CredentialsTypeTOTP: {\n\t\t\tType:        identity.CredentialsTypeTOTP,\n\t\t\tIdentifiers: []string{i.ID.String()},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"totp_url\":\"` + string(key.URL()) + `\"}`),\n\t\t},\n\t}\n\trequire.NoError(t, i.SetAvailableAAL(ctx, reg.IdentityManager()))\n\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(ctx, i))\n\treturn i, password, key\n}\n\nfunc TestCompleteLogin(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, true)),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeTOTP, true)),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/login.schema.json\")),\n\t)\n\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{redirTS.URL + \"/return-to-wherever\"})\n\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tpublicTS, _ := testhelpers.NewKratosServerWithRouters(t, reg, router, httprouterx.NewTestRouterAdminWithPrefix(t))\n\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tuiTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\n\tt.Run(\"case=totp payload is set when identity has totp\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(context.Background(), t, apiClient, publicTS, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\"0.attributes.value\",\n\t\t})\n\t})\n\n\tt.Run(\"case=totp payload is not set when identity has no totp\", func(t *testing.T) {\n\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(context.Background(), t, apiClient, publicTS, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\tassertx.EqualAsJSON(t, nil, f.Ui.Nodes)\n\t})\n\n\tt.Run(\"case=should show the error ui because the request payload is malformed\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\n\t\t\tbody, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, true, false, f, apiClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, body, `Expected JSON sent in request body to be an object but got: Number`)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, false, false, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\n\t\t\tbody, res := testhelpers.LoginMakeRequest(t, false, false, f, browserClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login-ts\")\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"invalid URL escape\", \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, true, false, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\n\t\t\tbody, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, false, true, f, browserClient, \"14=)=!(%)$/ZP()GHIÖ\")\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"invalid URL escape\", \"%s\", body)\n\t\t})\n\t})\n\n\tdoAPIFlow := func(t *testing.T, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", \"totp\")\n\t\tv(values)\n\t\tpayload := testhelpers.EncodeFormAsJSON(t, true, values)\n\t\treturn testhelpers.LoginMakeRequestCtx(t.Context(), t, true, false, f, apiClient, payload)\n\t}\n\n\tdoBrowserFlow := func(t *testing.T, spa bool, v func(url.Values), id *identity.Identity, returnTo string) (string, *http.Response) {\n\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(ctx, t, reg, id)\n\n\t\topts := []testhelpers.InitFlowWithOption{testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2)}\n\t\tif len(returnTo) > 0 {\n\t\t\topts = append(opts, testhelpers.InitFlowWithReturnTo(returnTo))\n\t\t}\n\n\t\tf := testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, spa, false, false, opts...)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", \"totp\")\n\t\tv(values)\n\t\treturn testhelpers.LoginMakeRequestCtx(t.Context(), t, false, spa, f, browserClient, values.Encode())\n\t}\n\n\tcheckURL := func(t *testing.T, shouldRedirect bool, res *http.Response) {\n\t\tif shouldRedirect {\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login-ts\")\n\t\t} else {\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t}\n\t}\n\n\tt.Run(\"case=should fail if code is empty\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"totp_code\", \"\")\n\t\t}\n\n\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"Property totp_code is missing.\", gjson.Get(body, totpCodeGJSONQuery+\".messages.0.text\").String(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tbody, res := doAPIFlow(t, payload, id)\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, payload, id, \"\")\n\t\t\tcheck(t, true, body, res)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, payload, id, \"\")\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail if code is invalid\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"totp_code\", \"111111\")\n\t\t}\n\n\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, text.NewErrorValidationTOTPVerifierWrong().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tbody, res := doAPIFlow(t, payload, id)\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, payload, id, \"\")\n\t\t\tcheck(t, true, body, res)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, payload, id, \"\")\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail if code is too long\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"totp_code\", \"1111111111\")\n\t\t}\n\n\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, text.NewErrorValidationTOTPVerifierWrong().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tbody, res := doAPIFlow(t, payload, id)\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, payload, id, \"\")\n\t\t\tcheck(t, true, body, res)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, payload, id, \"\")\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail if TOTP was not set up for identity\", func(t *testing.T) {\n\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"totp_code\", \"111111\")\n\t\t}\n\n\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, text.NewErrorValidationNoTOTPDevice().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tbody, res := doAPIFlow(t, payload, id)\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, payload, id, \"\")\n\t\t\tcheck(t, true, body, res)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, payload, id, \"\")\n\t\t\tcheck(t, false, body, res)\n\t\t})\n\t})\n\n\tt.Run(\"case=should pass when TOTP is supplied correctly\", func(t *testing.T) {\n\t\tid, _, key := createIdentity(t.Context(), t, reg)\n\t\tcode, err := stdtotp.GenerateCode(key.Secret(), time.Now())\n\t\trequire.NoError(t, err)\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"totp_code\", code)\n\t\t}\n\n\t\tstartAt := time.Now()\n\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\tprefix := \"session.\"\n\t\t\tif shouldRedirect {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), redirTS.URL+\"/return-ts\")\n\t\t\t\tprefix = \"\"\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\t}\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"active\").Bool(), \"%s\", body)\n\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel2, gjson.Get(body, prefix+\"authenticator_assurance_level\").String())\n\t\t\trequire.Len(t, gjson.Get(body, prefix+\"authentication_methods\").Array(), 2)\n\t\t\tassert.EqualValues(t, identity.CredentialsTypePassword, gjson.Get(body, prefix+\"authentication_methods.0.method\").String(), 2)\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"authentication_methods.0.completed_at\").Time().After(startAt), 2)\n\t\t\tassert.EqualValues(t, identity.CredentialsTypeTOTP, gjson.Get(body, prefix+\"authentication_methods.1.method\").String(), 2)\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"authentication_methods.1.completed_at\").Time().After(startAt), 2)\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"authentication_methods.1.completed_at\").Time().After(gjson.Get(body, prefix+\"authentication_methods.0.completed_at\").Time()), 2)\n\t\t\tassert.Equal(t, gjson.Get(body, prefix+\"authentication_methods.1.completed_at\").Time().Unix(), gjson.Get(body, prefix+\"authenticated_at\").Time().Unix(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tbody, res := doAPIFlow(t, payload, id)\n\t\t\tcheck(t, false, body, res)\n\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, payload, id, \"\")\n\t\t\tcheck(t, true, body, res)\n\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=browser set return_to\", func(t *testing.T) {\n\t\t\treturnTo := redirTS.URL + \"/return-to-wherever\"\n\t\t\tbody, res := doBrowserFlow(t, false, payload, id, returnTo)\n\t\t\tt.Log(res.Request.URL.String())\n\t\t\tassert.Contains(t, res.Request.URL.String(), returnTo)\n\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, payload, id, \"\")\n\t\t\tcheck(t, false, body, res)\n\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(body, \"continue_with.0.action\").String(), \"%s\", body)\n\t\t\tassert.EqualValues(t, conf.SelfServiceBrowserDefaultReturnTo(ctx).String(), gjson.Get(body, \"continue_with.0.redirect_browser_to\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"type=spa set return_to\", func(t *testing.T) {\n\t\t\treturnTo := redirTS.URL + \"/return-to-wherever\"\n\t\t\tbody, res := doBrowserFlow(t, true, payload, id, returnTo)\n\t\t\tcheck(t, false, body, res)\n\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(body, \"continue_with.0.action\").String(), \"%s\", body)\n\t\t\tassert.EqualValues(t, returnTo, gjson.Get(body, \"continue_with.0.redirect_browser_to\").String(), \"%s\", body)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail because totp can not handle AAL1\", func(t *testing.T) {\n\t\tapiClient := testhelpers.NewDebugClient(t)\n\t\tf := testhelpers.InitializeLoginFlowViaAPICtx(t.Context(), t, apiClient, publicTS, false)\n\n\t\tupdate, err := reg.LoginFlowPersister().GetLoginFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\trequire.NoError(t, err)\n\t\tupdate.RequestedAAL = identity.AuthenticatorAssuranceLevel1\n\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(t.Context(), update))\n\n\t\treq, err := http.NewRequest(\"POST\", f.Ui.Action, bytes.NewBufferString(`{\"method\":\"totp\"}`))\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Accept\", \"application/json\")\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tres, err := http.DefaultClient.Do(req)\n\t\trequire.NoError(t, err)\n\t\tbody := x.MustReadAll(res.Body)\n\t\trequire.NoError(t, res.Body.Close())\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\tassert.Equal(t, text.NewErrorValidationLoginNoStrategyFound().Text, gjson.GetBytes(body, \"ui.messages.0.text\").String())\n\t})\n\n\tt.Run(\"case=should pass without csrf if API flow\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\tbody, res := doAPIFlow(t, func(v url.Values) {\n\t\t\tv.Del(\"csrf_token\")\n\t\t\tv.Set(\"totp_code\", \"111111\")\n\t\t}, id)\n\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\tassert.Equal(t, text.NewErrorValidationTOTPVerifierWrong().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=should fail if CSRF token is invalid\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(\"totp_code\", \"111111\")\n\t\t\t}, id, \"\")\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"reason\").String(), body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(\"totp_code\", \"111111\")\n\t\t\t}, id, \"\")\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"error.reason\").String(), body)\n\t\t})\n\t})\n\n\tt.Run(\"case=should pass return_to URL after login\", func(t *testing.T) {\n\t\tid, pwd, _ := createIdentity(t.Context(), t, reg)\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\treturnTo := redirTS.URL + \"/return-to-wherever\"\n\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowserCtx(t.Context(), t, browserClient, publicTS, false, false, false, false, testhelpers.InitFlowWithReturnTo(returnTo))\n\n\t\t\tcred, ok := id.GetCredentials(identity.CredentialsTypePassword)\n\t\t\trequire.True(t, ok)\n\t\t\tvalues := url.Values{\n\t\t\t\t\"method\": {\"password\"}, \"password_identifier\": {cred.Identifiers[0]},\n\t\t\t\t\"password\": {pwd}, \"csrf_token\": {nosurfx.FakeCSRFToken},\n\t\t\t}.Encode()\n\n\t\t\tbody, res := testhelpers.LoginMakeRequestCtx(t.Context(), t, false, false, f, browserClient, values)\n\t\t\trequire.Contains(t, res.Request.URL.Path, \"login\", \"%s\", res.Request.URL.String())\n\t\t\tassert.Equal(t, gjson.Get(body, \"requested_aal\").String(), \"aal2\", \"%s\", body)\n\t\t\tassert.Equal(t, gjson.Get(body, \"return_to\").String(), returnTo, \"%s\", body)\n\t\t})\n\t})\n}\n\nfunc TestFormHydration(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeTOTP, true)),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/login.schema.json\")),\n\t)\n\n\ts, err := reg.AllLoginStrategies().Strategy(identity.CredentialsTypeTOTP)\n\trequire.NoError(t, err)\n\tfh, ok := s.(login.AAL2FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f *login.Flow) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.UI.Nodes.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f.UI.Nodes)\n\t}\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *login.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := login.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateLoginMethodSecondFactor\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\theaders := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\tr, f := newFlow(t.Context(), t)\n\n\t\tr.Header = headers\n\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\n\t\trequire.NoError(t, fh.PopulateLoginMethodSecondFactor(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodSecondFactorRefresh\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\theaders := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\tr, f := newFlow(t.Context(), t)\n\n\t\tr.Header = headers\n\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\n\t\trequire.NoError(t, fh.PopulateLoginMethodSecondFactorRefresh(r, f))\n\t\ttoSnapshot(t, f)\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/nodes.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp\n\nimport (\n\t\"github.com/pquerna/otp\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc NewVerifyTOTPNode() *node.Node {\n\treturn node.NewInputField(node.TOTPCode, nil, node.TOTPGroup,\n\t\tnode.InputAttributeTypeText,\n\t\tnode.WithRequiredInputAttribute).\n\t\tWithMetaLabel(text.NewInfoNodeLabelVerifyOTP())\n}\n\nfunc NewTOTPImageQRNode(key *otp.Key) (*node.Node, error) {\n\tsrc, err := KeyToHTMLImage(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn node.NewImageField(node.TOTPQR, src, node.TOTPGroup, node.WithImageAttributes(func(a *node.ImageAttributes) {\n\t\ta.Height = 256\n\t\ta.Width = 256\n\t})).WithMetaLabel(text.NewInfoSelfServiceSettingsTOTPQRCode()), nil\n}\n\nfunc NewTOTPSourceURLNode(key *otp.Key) *node.Node {\n\treturn node.NewTextField(node.TOTPSecretKey,\n\t\ttext.NewInfoSelfServiceSettingsTOTPSecret(key.Secret()), node.TOTPGroup).\n\t\tWithMetaLabel(text.NewInfoSelfServiceSettingsTOTPSecretLabel())\n}\n\nfunc NewUnlinkTOTPNode() *node.Node {\n\treturn node.NewInputField(node.TOTPUnlink, \"true\", node.TOTPGroup,\n\t\tnode.InputAttributeTypeSubmit,\n\t\tnode.WithRequiredInputAttribute).\n\t\tWithMetaLabel(text.NewInfoSelfServiceSettingsUpdateUnlinkTOTP())\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed .schema/settings.schema.json\nvar settingsSchema []byte\n\n//go:embed .schema/login.schema.json\nvar loginSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/totp/schema_extension.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/schema\"\n)\n\ntype SchemaExtension struct {\n\tAccountName string\n\tl           sync.Mutex\n}\n\nfunc NewSchemaExtension(fallback string) *SchemaExtension {\n\treturn &SchemaExtension{AccountName: fallback}\n}\n\nfunc (r *SchemaExtension) Run(_ jsonschema.ValidationContext, s schema.ExtensionConfig, value interface{}) error {\n\tr.l.Lock()\n\tdefer r.l.Unlock()\n\tif s.Credentials.TOTP.AccountName {\n\t\tr.AccountName = fmt.Sprintf(\"%s\", value)\n\t}\n\treturn nil\n}\n\nfunc (r *SchemaExtension) Finish() error {\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/pquerna/otp\"\n\t\"github.com/pquerna/otp/totp\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/schema\"\n\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/ory/kratos/session\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/decoderx\"\n)\n\nconst InternalContextKeyURL = \"url\"\n\nfunc (s *Strategy) SettingsStrategyID() string {\n\treturn identity.CredentialsTypeTOTP.String()\n}\n\n// Update Settings Flow with TOTP Method\n//\n// swagger:model updateSettingsFlowWithTotpMethod\ntype updateSettingsFlowWithTotpMethod struct {\n\t// ValidationTOTP must contain a valid TOTP based on the\n\tValidationTOTP string `json:\"totp_code\"`\n\n\t// UnlinkTOTP if true will remove the TOTP pairing,\n\t// effectively removing the credential. This can be used\n\t// to set up a new TOTP device.\n\tUnlinkTOTP bool `json:\"totp_unlink\"`\n\n\t// CSRFToken is the anti-CSRF token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Method\n\t//\n\t// Should be set to \"totp\" when trying to add, update, or remove a totp pairing.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Flow is flow ID.\n\t//\n\t// swagger:ignore\n\tFlow string `json:\"flow\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (p *updateSettingsFlowWithTotpMethod) GetFlowID() uuid.UUID {\n\treturn x.ParseUUID(p.Flow)\n}\n\nfunc (p *updateSettingsFlowWithTotpMethod) SetFlowID(rid uuid.UUID) {\n\tp.Flow = rid.String()\n}\n\nfunc (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.totp.Strategy.Settings\")\n\tdefer otelx.End(span, &err)\n\n\tvar p updateSettingsFlowWithTotpMethod\n\tctxUpdate, err := settings.PrepareUpdate(s.d, w, r, f, ss, settings.ContinuityKey(s.SettingsStrategyID()), &p)\n\tif errors.Is(err, settings.ErrContinuePreviousAction) {\n\t\treturn ctxUpdate, s.continueSettingsFlow(ctx, r, ctxUpdate, p)\n\t} else if err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif err := s.decodeSettingsFlow(r, &p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif p.UnlinkTOTP {\n\t\t// This is a submit so we need to manually set the type to TOTP\n\t\tp.Method = s.SettingsStrategyID()\n\t\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\t\treturn nil, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t\t}\n\t} else if err := flow.MethodEnabledAndAllowedFromRequest(r, f.GetFlowName(), s.SettingsStrategyID(), s.d); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\t// This does not come from the payload!\n\tp.Flow = ctxUpdate.Flow.ID.String()\n\tif err := s.continueSettingsFlow(ctx, r, ctxUpdate, p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\treturn ctxUpdate, nil\n}\n\nfunc (s *Strategy) decodeSettingsFlow(r *http.Request, dest interface{}) error {\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(settingsSchema)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn decoderx.Decode(r, dest, compiler,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t)\n}\n\nfunc (s *Strategy) continueSettingsFlow(ctx context.Context, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithTotpMethod) error {\n\tif err := flow.MethodEnabledAndAllowed(ctx, flow.SettingsFlow, s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\treturn err\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, ctxUpdate.Flow.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn err\n\t}\n\n\tif ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) {\n\t\treturn errors.WithStack(settings.NewFlowNeedsReAuth())\n\t}\n\n\thasTOTP, err := s.identityHasTOTP(ctx, ctxUpdate.Session.Identity)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// We have now two cases:\n\t//\n\t// 1. TOTP should be removed -> we have it already\n\t// 2. TOTP should be added -> we do not have it yet\n\tvar i *identity.Identity\n\tif hasTOTP {\n\t\ti, err = s.continueSettingsFlowRemoveTOTP(ctx, ctxUpdate, p)\n\t} else {\n\t\ti, err = s.continueSettingsFlowAddTOTP(ctx, ctxUpdate, p)\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tctxUpdate.UpdateIdentity(i)\n\treturn nil\n}\n\nfunc (s *Strategy) continueSettingsFlowAddTOTP(ctx context.Context, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithTotpMethod) (*identity.Identity, error) {\n\tkeyURL := gjson.GetBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeyURL)).String()\n\tif len(keyURL) == 0 {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Could not find the TOTP key in the internal context. This is a code bug and should be reported to https://github.com/ory/kratos/.\"))\n\t}\n\n\tkey, err := otp.NewKeyFromURL(keyURL)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithTrace(err).WithReasonf(\"Could not decode TOTP key from the internal context. This is a code bug and should be reported to https://github.com/ory/kratos/.\"))\n\t}\n\n\tif len(key.Secret()) == 0 {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"TOTP secret is not set. This is a code bug and should be reported to https://github.com/ory/kratos/.\"))\n\t}\n\n\tif p.ValidationTOTP == \"\" {\n\t\treturn nil, schema.NewRequiredError(\"#/totp_code\", \"totp_code\")\n\t}\n\n\tif !totp.Validate(p.ValidationTOTP, key.Secret()) {\n\t\treturn nil, schema.NewTOTPVerifierWrongError(\"#/totp_code\")\n\t}\n\n\tco, err := json.Marshal(&identity.CredentialsTOTPConfig{TOTPURL: key.URL()})\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode totp options to JSON: %s\", err))\n\t}\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.Identity.ID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// We do not really need the identifier, so we add the identity's ID\n\tc := &identity.Credentials{Type: s.ID(), Identifiers: []string{i.ID.String()}, Config: co}\n\tc.Config = co\n\ti.SetCredentials(s.ID(), *c)\n\n\t// Remove the TOTP URL from the internal context now that it is set!\n\tctxUpdate.Flow.InternalContext, err = sjson.DeleteBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeyURL))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, ctxUpdate.Flow); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Since we added the method, it also means that we have authenticated it\n\tif err := s.d.SessionManager().SessionAddAuthenticationMethods(ctx, ctxUpdate.Session.ID, session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    identity.AuthenticatorAssuranceLevel2,\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn i, nil\n}\n\nfunc (s *Strategy) continueSettingsFlowRemoveTOTP(ctx context.Context, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithTotpMethod) (*identity.Identity, error) {\n\tif !p.UnlinkTOTP {\n\t\treturn ctxUpdate.Session.Identity, nil\n\t}\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.Identity.ID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ti.DeleteCredentialsType(identity.CredentialsTypeTOTP)\n\treturn i, nil\n}\n\nfunc (s *Strategy) identityHasTOTP(ctx context.Context, id *identity.Identity) (bool, error) {\n\tif len(id.Credentials) == 0 {\n\t\tif err := s.d.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, id, identity.ExpandCredentials); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tcount, err := s.CountActiveMultiFactorCredentials(ctx, id.Credentials)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn count > 0, nil\n}\n\nfunc (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.totp.Strategy.PopulateSettingsMethod\")\n\tdefer otelx.End(span, &err)\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\n\thasTOTP, err := s.identityHasTOTP(ctx, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// OTP already set up, just add an unlink option\n\tif hasTOTP {\n\t\tf.UI.Nodes.Upsert(NewUnlinkTOTPNode())\n\t} else {\n\t\te := NewSchemaExtension(id.ID.String())\n\t\t_ = s.d.IdentityValidator().ValidateWithRunner(ctx, id, e)\n\n\t\t// No TOTP set up yet, add nodes allowing us to add it.\n\t\tkey, err := NewKey(ctx, e.AccountName, s.d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tf.InternalContext, err = sjson.SetBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeyURL), key.URL())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tqr, err := NewTOTPImageQRNode(key)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tf.UI.Nodes.Upsert(NewTOTPSourceURLNode(key))\n\t\tf.UI.Nodes.Upsert(qr)\n\t\tf.UI.Nodes.Upsert(NewVerifyTOTPNode())\n\t\tf.UI.Nodes.Append(node.NewInputField(\"method\", \"totp\", node.TOTPGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelSave()))\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithTotpMethod, err error) error {\n\t// Do not pause flow if the flow type is an API flow as we can't save cookies in those flows.\n\tif e := new(settings.FlowNeedsReAuth); errors.As(err, &e) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser {\n\t\tif err := s.d.ContinuityManager().Pause(ctx, w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif ctxUpdate.Flow != nil {\n\t\tctxUpdate.Flow.UI.ResetMessages()\n\t\tctxUpdate.Flow.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/settings_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp_test\n\nimport (\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pquerna/otp\"\n\tstdtotp \"github.com/pquerna/otp/totp\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/selfservice/strategy/totp\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc TestCompleteSettings(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypePassword, false)),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(\"profile\", false)),\n\t\tconfigx.WithValues(testhelpers.MethodEnableConfig(identity.CredentialsTypeTOTP, true)),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/settings.schema.json\")),\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceSettingsRequiredAAL:                   \"aal1\",\n\t\t\tconfig.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter: \"1m\",\n\t\t}),\n\t)\n\n\trouter := httprouterx.NewTestRouterPublic(t)\n\tpublicTS, _ := testhelpers.NewKratosServerWithRouters(t, reg, router, httprouterx.NewTestRouterAdminWithPrefix(t))\n\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tuiTS := testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewRedirSessionEchoTS(t, reg)\n\tloginTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\n\tt.Run(\"case=device unlinking is available when identity has totp\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\"0.attributes.value\",\n\t\t})\n\t})\n\n\tt.Run(\"case=device setup is available when identity has no totp yet\", func(t *testing.T) {\n\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\tid.Credentials = nil\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(t.Context(), id))\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\"0.attributes.value\",\n\t\t\t\"1.attributes.src\",\n\t\t\t\"2.attributes.text.context.secret\",\n\t\t\t\"2.attributes.text.text\",\n\t\t})\n\t})\n\n\tdoAPIFlow := func(t *testing.T, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", \"totp\")\n\t\tv(values)\n\t\tpayload := testhelpers.EncodeFormAsJSON(t, true, values)\n\t\treturn testhelpers.SettingsMakeRequest(t, true, false, f, apiClient, payload)\n\t}\n\n\tdoBrowserFlow := func(t *testing.T, spa bool, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, spa, publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tvalues.Set(\"method\", \"totp\")\n\t\tv(values)\n\t\treturn testhelpers.SettingsMakeRequest(t, false, spa, f, browserClient, testhelpers.EncodeFormAsJSON(t, spa, values))\n\t}\n\n\tt.Run(\"case=should pass without csrf if API flow\", func(t *testing.T) {\n\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\n\t\tbody, res := doAPIFlow(t, func(v url.Values) {\n\t\t\tv.Del(\"csrf_token\")\n\t\t\tv.Set(node.TOTPCode, \"111111\")\n\t\t}, id)\n\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\tassert.Equal(t, text.NewErrorValidationTOTPVerifierWrong().Text, gjson.Get(body, totpCodeGJSONQuery+\".messages.0.text\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=should fail if CSRF token is invalid\", func(t *testing.T) {\n\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, false, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(node.TOTPCode, \"111111\")\n\t\t\t}, id)\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"reason\").String(), body)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tbody, res := doBrowserFlow(t, true, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(node.TOTPCode, \"111111\")\n\t\t\t}, id)\n\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"error.reason\").String(), body)\n\t\t})\n\t})\n\n\tt.Run(\"type=can not unlink without privileged session\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"1ns\")\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"5m\")\n\t\t})\n\n\t\tid, _, key := createIdentity(t.Context(), t, reg)\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"totp_unlink\", \"true\")\n\t\t}\n\n\t\tcheckIdentity := func(t *testing.T) {\n\t\t\t_, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypeTOTP, id.ID.String())\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, key.URL(), gjson.GetBytes(cred.Config, \"totp_url\").String())\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual, res := doAPIFlow(t, payload, id)\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"redirect_browser_to\").String(), publicTS.URL+\"/self-service/login/browser?refresh=true&return_to=\")\n\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(actual), []string{\"redirect_browser_to\"})\n\t\t\tcheckIdentity(t)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tactual, res := doBrowserFlow(t, true, payload, id)\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"redirect_browser_to\").String(), publicTS.URL+\"/self-service/login/browser?refresh=true&return_to=\")\n\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(actual), []string{\"redirect_browser_to\"})\n\t\t\tcheckIdentity(t)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tactual, res := doBrowserFlow(t, false, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL+\"/login-ts\")\n\t\t\tassertx.EqualAsJSON(t, text.NewInfoLoginReAuth().Text, json.RawMessage(gjson.Get(actual, \"ui.messages.0.text\").Raw), actual)\n\t\t\tcheckIdentity(t)\n\t\t})\n\t})\n\n\tt.Run(\"type=can not set up new totp device without privileged session\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"1ns\")\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"5m\")\n\t\t})\n\n\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(node.TOTPCode, \"111111\")\n\t\t}\n\n\t\tcheckIdentity := func(t *testing.T) {\n\t\t\t_, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypeTOTP, id.ID.String())\n\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tactual, res := doAPIFlow(t, payload, id)\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"redirect_browser_to\").String(), publicTS.URL+\"/self-service/login/browser?refresh=true&return_to=\")\n\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(actual), []string{\"redirect_browser_to\"})\n\t\t\tcheckIdentity(t)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tactual, res := doBrowserFlow(t, true, payload, id)\n\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"redirect_browser_to\").String(), publicTS.URL+\"/self-service/login/browser?refresh=true&return_to=\")\n\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(actual), []string{\"redirect_browser_to\"})\n\t\t\tcheckIdentity(t)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tactual, res := doBrowserFlow(t, false, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL+\"/login-ts\")\n\t\t\tassertx.EqualAsJSON(t, text.NewInfoLoginReAuth().Text, json.RawMessage(gjson.Get(actual, \"ui.messages.0.text\").Raw), actual)\n\t\t\tcheckIdentity(t)\n\t\t})\n\t})\n\n\tt.Run(\"type=unlink TOTP device\", func(t *testing.T) {\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(\"totp_unlink\", \"true\")\n\t\t}\n\n\t\tcheckIdentity := func(t *testing.T, id *identity.Identity) {\n\t\t\t_, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypeTOTP, id.ID.String())\n\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\t\tactual, res := doAPIFlow(t, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(actual, \"state\").String(), actual)\n\t\t\tcheckIdentity(t, id)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\t\tactual, res := doBrowserFlow(t, true, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(actual, \"state\").String(), actual)\n\t\t\tcheckIdentity(t, id)\n\n\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(actual, \"continue_with.0.action\").String(), \"%s\", actual)\n\t\t\tassert.Contains(t, gjson.Get(actual, \"continue_with.0.redirect_browser_to\").String(), uiTS.URL, \"%s\", actual)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tid, _, _ := createIdentity(t.Context(), t, reg)\n\t\t\tactual, res := doBrowserFlow(t, false, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(actual, \"state\").String(), actual)\n\t\t\tcheckIdentity(t, id)\n\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t})\n\t})\n\n\tt.Run(\"type=set up TOTP device but code is incorrect\", func(t *testing.T) {\n\t\tpayload := func(v url.Values) {\n\t\t\tv.Set(node.TOTPCode, \"111111\")\n\t\t}\n\n\t\tcheckIdentity := func(t *testing.T, id *identity.Identity) {\n\t\t\t_, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypeTOTP, id.ID.String())\n\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\t\t\tactual, res := doAPIFlow(t, payload, id)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tassert.Equal(t, text.NewErrorValidationTOTPVerifierWrong().Text, gjson.Get(actual, totpCodeGJSONQuery+\".messages.0.text\").String(), actual)\n\t\t\tcheckIdentity(t, id)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\t\t\tactual, res := doBrowserFlow(t, true, payload, id)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\tassert.Equal(t, text.NewErrorValidationTOTPVerifierWrong().Text, gjson.Get(actual, totpCodeGJSONQuery+\".messages.0.text\").String(), actual)\n\t\t\tcheckIdentity(t, id)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\t\t\tactual, res := doBrowserFlow(t, false, payload, id)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\tassert.Equal(t, text.NewErrorValidationTOTPVerifierWrong().Text, gjson.Get(actual, totpCodeGJSONQuery+\".messages.0.text\").String(), actual)\n\t\t\tcheckIdentity(t, id)\n\t\t})\n\t})\n\n\tt.Run(\"type=set up TOTP device\", func(t *testing.T) {\n\t\tcheckIdentity := func(t *testing.T, id *identity.Identity, key string) {\n\t\t\ti, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(t.Context(), identity.CredentialsTypeTOTP, id.ID.String())\n\t\t\trequire.NoError(t, err)\n\t\t\tvar c identity.CredentialsTOTPConfig\n\t\t\trequire.NoError(t, json.Unmarshal(cred.Config, &c))\n\t\t\tactual, err := otp.NewKeyFromURL(c.TOTPURL)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, key, actual.Secret())\n\t\t\tassert.Contains(t, c.TOTPURL, gjson.GetBytes(i.Traits, \"subject\").String())\n\t\t}\n\n\t\trun := func(t *testing.T, isAPI, isSPA bool, id *identity.Identity, hc *http.Client, f *kratos.SettingsFlow) {\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\n\t\t\tnodes, err := json.Marshal(f.Ui.Nodes)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tkey := gjson.GetBytes(nodes, \"#(attributes.id==totp_secret_key).attributes.text.context.secret\").String()\n\t\t\trequire.NotEmpty(t, key, nodes)\n\n\t\t\tcode, err := stdtotp.GenerateCode(key, time.Now())\n\t\t\trequire.NoError(t, err)\n\t\t\tvalues.Set(\"method\", \"totp\")\n\t\t\tvalues.Set(node.TOTPCode, code)\n\n\t\t\tactual, res := testhelpers.SettingsMakeRequest(t, isAPI, isSPA, f, hc, testhelpers.EncodeFormAsJSON(t, isAPI || isSPA, values))\n\t\t\trequire.NotEmpty(t, key)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tif isAPI || isSPA {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(actual, \"state\").String(), actual)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(actual, \"state\").String(), actual)\n\t\t\t}\n\n\t\t\tactualFlow, err := reg.SettingsFlowPersister().GetSettingsFlow(t.Context(), uuid.FromStringOrNil(f.Id))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Empty(t, gjson.GetBytes(actualFlow.InternalContext, flow.PrefixInternalContextKey(identity.CredentialsTypeTOTP, totp.InternalContextKeyURL)))\n\n\t\t\tcheckIdentity(t, id, key)\n\t\t\ttesthelpers.EnsureAAL(t, hc, publicTS, \"aal2\", string(identity.CredentialsTypeTOTP))\n\n\t\t\tif isSPA {\n\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(actual, \"continue_with.0.action\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"continue_with.0.redirect_browser_to\").String(), uiTS.URL, \"%s\", actual)\n\t\t\t} else {\n\t\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=api\", func(t *testing.T) {\n\t\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\n\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\n\t\t\trun(t, true, false, id, apiClient, f)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\n\t\t\tuser := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, user, true, publicTS)\n\n\t\t\trun(t, false, true, id, user, f)\n\t\t})\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\tid := createIdentityWithoutTOTP(t.Context(), t, reg)\n\n\t\t\tuser := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, user, false, publicTS)\n\n\t\t\trun(t, false, false, id, user, f)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/pquerna/otp\"\n\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar (\n\t_ login.Strategy                    = (*Strategy)(nil)\n\t_ login.AAL2FormHydrator            = (*Strategy)(nil)\n\t_ settings.Strategy                 = (*Strategy)(nil)\n\t_ identity.ActiveCredentialsCounter = (*Strategy)(nil)\n)\n\ntype dependencies interface {\n\tlogrusx.Provider\n\thttpx.WriterProvider\n\tnosurfx.CSRFTokenGeneratorProvider\n\tnosurfx.CSRFProvider\n\totelx.Provider\n\n\tconfig.Provider\n\n\tcontinuity.ManagementProvider\n\n\terrorx.ManagementProvider\n\thash.HashProvider\n\n\tregistration.HandlerProvider\n\tregistration.HooksProvider\n\tregistration.ErrorHandlerProvider\n\tregistration.HookExecutorProvider\n\tregistration.FlowPersistenceProvider\n\n\tlogin.HooksProvider\n\tlogin.ErrorHandlerProvider\n\tlogin.HookExecutorProvider\n\tlogin.FlowPersistenceProvider\n\tlogin.HandlerProvider\n\n\tsettings.FlowPersistenceProvider\n\tsettings.HookExecutorProvider\n\tsettings.HooksProvider\n\tsettings.ErrorHandlerProvider\n\n\tidentity.PrivilegedPoolProvider\n\tidentity.ValidationProvider\n\n\tsession.HandlerProvider\n\tsession.ManagementProvider\n\tsession.PersistenceProvider\n}\n\ntype Strategy struct{ d dependencies }\n\nfunc NewStrategy(d dependencies) *Strategy { return &Strategy{d: d} }\n\nfunc (s *Strategy) CountActiveFirstFactorCredentials(_ context.Context, _ map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\treturn 0, nil\n}\n\nfunc (s *Strategy) CountActiveMultiFactorCredentials(_ context.Context, cc map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\tfor _, c := range cc {\n\t\tif c.Type == s.ID() && len(c.Config) > 0 {\n\t\t\tvar conf identity.CredentialsTOTPConfig\n\t\t\tif err = json.Unmarshal(c.Config, &conf); err != nil {\n\t\t\t\treturn 0, errors.WithStack(err)\n\t\t\t}\n\n\t\t\t_, err := otp.NewKeyFromURL(conf.TOTPURL)\n\t\t\tif len(conf.TOTPURL) > 0 && err == nil {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (s *Strategy) ID() identity.CredentialsType {\n\treturn identity.CredentialsTypeTOTP\n}\n\nfunc (s *Strategy) NodeGroup() node.UiNodeGroup {\n\treturn node.TOTPGroup\n}\n\nfunc (s *Strategy) CompletedAuthenticationMethod(ctx context.Context) session.AuthenticationMethod {\n\treturn session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    identity.AuthenticatorAssuranceLevel2,\n\t}\n}\n\nfunc (s *Strategy) PopulateLoginMethodSecondFactor(r *http.Request, f *login.Flow) error {\n\treturn s.PopulateLoginMethod(r, identity.AuthenticatorAssuranceLevel2, f)\n}\n\nfunc (s *Strategy) PopulateLoginMethodSecondFactorRefresh(r *http.Request, f *login.Flow) error {\n\treturn s.PopulateLoginMethod(r, identity.AuthenticatorAssuranceLevel2, f)\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/strategy_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage totp_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/totp\"\n)\n\nfunc TestCountActiveCredentials(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tstrategy := totp.NewStrategy(reg)\n\n\tkey, err := totp.NewKey(t.Context(), \"foo\", reg)\n\trequire.NoError(t, err)\n\n\tt.Run(\"first factor\", func(t *testing.T) {\n\t\tactual, err := strategy.CountActiveFirstFactorCredentials(t.Context(), nil)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, 0, actual)\n\t})\n\n\tt.Run(\"multi factor\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\tin       map[identity.CredentialsType]identity.Credentials\n\t\t\texpected int\n\t\t}{\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte{},\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{\"totp_url\": \"\"}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:        strategy.ID(),\n\t\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\t\tConfig:      []byte(`{\"totp_url\": \"` + key.URL() + `\"}`),\n\t\t\t\t}},\n\t\t\t\texpected: 1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\t\tType:   strategy.ID(),\n\t\t\t\t\tConfig: []byte(`{}`),\n\t\t\t\t}},\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tin:       nil,\n\t\t\t\texpected: 0,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tcc := map[identity.CredentialsType]identity.Credentials{}\n\t\t\t\tfor _, c := range tc.in {\n\t\t\t\t\tcc[c.Type] = c\n\t\t\t\t}\n\n\t\t\t\tactual, err := strategy.CountActiveMultiFactorCredentials(t.Context(), cc)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, tc.expected, actual)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/stub/login.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/totp/stub/settings.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"subject\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"totp\": {\n                \"account_name\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.schema/login.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/webauthn/login.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"webauthn_login\": {\n      \"type\": \"string\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"identifier\": {\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  },\n  \"if\": {\n    \"anyOf\": [\n      {\n        \"properties\": {\n          \"method\": {\n            \"const\": \"webauthn\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ]\n      },\n      {\n        \"properties\": {\n          \"webauthn_login\": {\n            \"type\": \"string\",\n            \"minLength\": 1\n          }\n        },\n        \"required\": [\n          \"webauthn_login\"\n        ]\n      }\n    ]\n  },\n  \"then\": {\n    \"required\": [\n      \"identifier\"\n    ]\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.schema/registration.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/password/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"traits\": {\n      \"description\": \"This field will be overwritten in registration.go's decoder() method. Do not add anything to this field as it has no effect.\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"webauthn_register\": {\n      \"type\": \"string\"\n    },\n    \"webauthn_register_displayname\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  },\n  \"oneOf\": [\n    {\n      \"required\": [\n        \"webauthn_register\"\n      ]\n    },\n    {\n      \"method\": {\n        \"const\": \"webauthn\"\n      },\n      \"required\": [\n        \"identifier\",\n        \"method\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.schema/settings.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/kratos/selfservice/strategy/webauthn/settings.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"csrf_token\": {\n      \"type\": \"string\"\n    },\n    \"method\": {\n      \"type\": \"string\"\n    },\n    \"webauthn_register\": {\n      \"type\": \"string\"\n    },\n    \"webauthn_register_displayname\": {\n      \"type\": \"string\"\n    },\n    \"webauthn_remove\": {\n      \"type\": \"string\"\n    },\n    \"transient_payload\": {\n      \"type\": \"object\",\n      \"additionalProperties\": true\n    }\n  },\n  \"if\": {\n    \"properties\": {\n      \"webauthn_register\": {\n        \"minLength\": 1\n      }\n    },\n    \"required\": [\n      \"webauthn_register\"\n    ]\n  },\n  \"then\": {\n    \"properties\": {\n      \"webauthn_register_displayname\": {\n        \"minLength\": 1\n      }\n    },\n    \"required\": [\n      \"webauthn_register_displayname\"\n    ]\n  }\n}\n\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=can_not_use_security_key_for_passwordless_in_mfa_flow.json",
    "content": "[\n  {\n    \"id\": 4000008,\n    \"text\": \"The provided authentication code is invalid, please try again.\",\n    \"type\": \"error\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_not_set_when_identity_has_no_webauthn.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=mfa-case=webauthn_payload_is_set_when_identity_has_webauthn.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=browser.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"script\",\n        \"group\": \"webauthn\",\n        \"attributes\": {\n          \"async\": true,\n          \"referrerpolicy\": \"no-referrer\",\n          \"crossorigin\": \"anonymous\",\n          \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n          \"type\": \"text/javascript\",\n          \"node_type\": \"script\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"webauthn\",\n        \"attributes\": {\n          \"name\": \"webauthn_login_trigger\",\n          \"type\": \"button\",\n          \"disabled\": false,\n          \"onclickTrigger\": \"oryWebAuthnLogin\",\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"webauthn\",\n        \"attributes\": {\n          \"name\": \"webauthn_login\",\n          \"type\": \"hidden\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      }\n    ],\n    \"messages\": [\n      {\n        \"text\": \"Prepare your WebAuthn device (e.g. security key, biometrics scanner, ...) and press continue.\",\n        \"type\": \"info\"\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=should_fail_if_webauthn_login_is_invalid-type=spa.json",
    "content": "{\n  \"organization_id\": null,\n  \"type\": \"browser\",\n  \"ui\": {\n    \"method\": \"POST\",\n    \"nodes\": [\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"identifier\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"default\",\n        \"attributes\": {\n          \"name\": \"csrf_token\",\n          \"type\": \"hidden\",\n          \"required\": true,\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"script\",\n        \"group\": \"webauthn\",\n        \"attributes\": {\n          \"async\": true,\n          \"referrerpolicy\": \"no-referrer\",\n          \"crossorigin\": \"anonymous\",\n          \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n          \"type\": \"text/javascript\",\n          \"node_type\": \"script\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"webauthn\",\n        \"attributes\": {\n          \"name\": \"webauthn_login_trigger\",\n          \"type\": \"button\",\n          \"disabled\": false,\n          \"onclickTrigger\": \"oryWebAuthnLogin\",\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {\n          \"label\": {\n            \"text\": \"Continue\",\n            \"type\": \"info\"\n          }\n        }\n      },\n      {\n        \"type\": \"input\",\n        \"group\": \"webauthn\",\n        \"attributes\": {\n          \"name\": \"webauthn_login\",\n          \"type\": \"hidden\",\n          \"disabled\": false,\n          \"node_type\": \"input\"\n        },\n        \"messages\": [],\n        \"meta\": {}\n      }\n    ],\n    \"messages\": [\n      {\n        \"text\": \"Prepare your WebAuthn device (e.g. security key, biometrics scanner, ...) and press continue.\",\n        \"type\": \"info\"\n      }\n    ]\n  },\n  \"refresh\": false,\n  \"requested_aal\": \"aal1\",\n  \"state\": \"choose_method\"\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=passwordless-case=webauthn_button_exists.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"webauthn\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=no_webauth_credentials-passwordless=false-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=no_webauth_credentials-passwordless=false-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=no_webauth_credentials-passwordless=true-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=no_webauth_credentials-passwordless=true-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"foo@bar.com\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v0_credentials-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"foo@bar.com\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"foo@bar.com\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=mfa_v1_credentials-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"foo@bar.com\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=passwordless_credentials-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=false-case=passwordless_credentials-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=mfa_v0_credentials-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=mfa_v0_credentials-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=mfa_v1_credentials-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=mfa_v1_credentials-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"foo@bar.com\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteLogin-flow=refresh-case=passwordless-passwordless_enabled=true-case=passwordless_credentials-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"identifier\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"foo@bar.com\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_login_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=a_device_is_shown_which_can_be_unlinked.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_remove\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"626172626172\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"added_at\": \"0001-01-01T00:00:00Z\",\n          \"added_at_unix\": -62135596800,\n          \"display_name\": \"bar\"\n        },\n        \"id\": 1050018,\n        \"text\": \"Remove security key \\\"bar\\\"\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_remove\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"666f6f666f6f\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"added_at\": \"0001-01-01T00:00:00Z\",\n          \"added_at_unix\": -62135596800,\n          \"display_name\": \"foo\"\n        },\n        \"id\": 1050018,\n        \"text\": \"Remove security key \\\"foo\\\"\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register_displayname\",\n      \"node_type\": \"input\",\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050013,\n        \"text\": \"Name of the security key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnRegistration\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050012,\n        \"text\": \"Add security key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=fails_to_remove_security_key_if_it_is_passwordless_and_the_last_credential_available-type=browser-response.json",
    "content": "{\n  \"type\": \"input\",\n  \"group\": \"webauthn\",\n  \"attributes\": {\n    \"name\": \"webauthn_remove\",\n    \"type\": \"submit\",\n    \"value\": \"666f6f666f6f\",\n    \"disabled\": true,\n    \"node_type\": \"input\"\n  },\n  \"messages\": [\n    {\n      \"id\": 4000001,\n      \"text\": \"unable to remove this security key because it would lock you out of your account\",\n      \"type\": \"error\",\n      \"context\": {\n        \"reason\": \"unable to remove this security key because it would lock you out of your account\"\n      }\n    }\n  ],\n  \"meta\": {\n    \"label\": {\n      \"id\": 1050018,\n      \"text\": \"Remove security key \\\"foo\\\"\",\n      \"type\": \"info\",\n      \"context\": {\n        \"added_at\": \"0001-01-01T00:00:00Z\",\n        \"added_at_unix\": -62135596800,\n        \"display_name\": \"foo\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=fails_to_remove_security_key_if_it_is_passwordless_and_the_last_credential_available-type=browser.json",
    "content": "{\n  \"webauthn_register\": [\n    \"\"\n  ],\n  \"webauthn_register_displayname\": [\n    \"\"\n  ],\n  \"webauthn_remove\": [\n    \"666f6f666f6f\"\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=fails_to_remove_security_key_if_it_is_passwordless_and_the_last_credential_available-type=spa-response.json",
    "content": "{\n  \"type\": \"input\",\n  \"group\": \"webauthn\",\n  \"attributes\": {\n    \"name\": \"webauthn_remove\",\n    \"type\": \"submit\",\n    \"value\": \"666f6f666f6f\",\n    \"disabled\": true,\n    \"node_type\": \"input\"\n  },\n  \"messages\": [\n    {\n      \"id\": 4000001,\n      \"text\": \"unable to remove this security key because it would lock you out of your account\",\n      \"type\": \"error\",\n      \"context\": {\n        \"reason\": \"unable to remove this security key because it would lock you out of your account\"\n      }\n    }\n  ],\n  \"meta\": {\n    \"label\": {\n      \"id\": 1050018,\n      \"text\": \"Remove security key \\\"foo\\\"\",\n      \"type\": \"info\",\n      \"context\": {\n        \"added_at\": \"0001-01-01T00:00:00Z\",\n        \"added_at_unix\": -62135596800,\n        \"display_name\": \"foo\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=fails_to_remove_security_key_if_it_is_passwordless_and_the_last_credential_available-type=spa.json",
    "content": "{\n  \"webauthn_register\": [\n    \"\"\n  ],\n  \"webauthn_register_displayname\": [\n    \"\"\n  ],\n  \"webauthn_remove\": [\n    \"666f6f666f6f\"\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=one_activation_element_is_shown.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register_displayname\",\n      \"node_type\": \"input\",\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050013,\n        \"text\": \"Name of the security key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnRegistration\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050012,\n        \"text\": \"Add security key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=possible_to_remove_webauthn_credential_if_it_is_MFA_at_all_times-type=browser.json",
    "content": "{\n  \"webauthn_register\": [\n    \"\"\n  ],\n  \"webauthn_register_displayname\": [\n    \"\"\n  ],\n  \"webauthn_remove\": [\n    \"666f6f666f6f\"\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestCompleteSettings-case=possible_to_remove_webauthn_credential_if_it_is_MFA_at_all_times-type=spa.json",
    "content": "{\n  \"webauthn_register\": [\n    \"\"\n  ],\n  \"webauthn_register_displayname\": [\n    \"\"\n  ],\n  \"webauthn_remove\": [\n    \"666f6f666f6f\"\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-case=Multi-Schema-method=PopulateLoginMethodFirstFactor-case=mfa_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-case=Multi-Schema-method=PopulateLoginMethodFirstFactor-case=passwordless_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor-case=mfa_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodFirstFactor-case=passwordless_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"autocomplete\": \"username webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070004,\n        \"text\": \"ID\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier-case=mfa_enabled-case=account_enumeration_mitigation_disabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier-case=mfa_enabled-case=account_enumeration_mitigation_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier-case=mfa_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier-case=passwordless_enabled-case=account_enumeration_mitigation_disabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentifier-case=passwordless_enabled-case=account_enumeration_mitigation_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_does_not_have_a_webauthn-case=mfa_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_does_not_have_a_webauthn-case=passwordless_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_has_webauthn-case=mfa_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_disabled-case=identity_has_webauthn-case=passwordless_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_enabled-case=mfa_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=WithIdentityHint-case=account_enumeration_mitigation_enabled-case=passwordless_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=mfa_enabled-case=account_enumeration_mitigation_disabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=mfa_enabled-case=account_enumeration_mitigation_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=mfa_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=passwordless_enabled-case=account_enumeration_mitigation_disabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstCredentials-case=no_options-case=passwordless_enabled-case=account_enumeration_mitigation_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"webauthn\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification-case=mfa_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodIdentifierFirstIdentification-case=passwordless_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_and_user_has_mfa_credentials.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_login_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_login\",\n      \"type\": \"hidden\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=mfa_enabled_but_user_has_passwordless_credentials.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_and_user_has_passwordless_credentials.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_login_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_login\",\n      \"type\": \"hidden\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodRefresh-case=passwordless_enabled_but_user_has_no_passwordless_credentials.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=mfa_enabled.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"identifier\",\n      \"type\": \"hidden\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_login_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryWebAuthnLogin\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1010008,\n        \"text\": \"Sign in with hardware key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_login\",\n      \"type\": \"hidden\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestFormHydration-method=PopulateLoginMethodSecondFactor-case=passwordless_enabled.json",
    "content": "[]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethod.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register_displayname\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050013,\n        \"text\": \"Name of the security key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryWebAuthnRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040004,\n        \"text\": \"Sign up with security key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register\",\n      \"type\": \"hidden\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodCredentials.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register_displayname\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050013,\n        \"text\": \"Name of the security key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryWebAuthnRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040004,\n        \"text\": \"Sign up with security key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register\",\n      \"type\": \"hidden\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestPopulateRegistrationMethod-method=PopulateRegistrationMethodProfile.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=1.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=2.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register_displayname\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050013,\n        \"text\": \"Name of the security key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryWebAuthnRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040004,\n        \"text\": \"Sign up with security key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register\",\n      \"type\": \"hidden\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=3.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestPopulateRegistrationMethod-method=idempotency-case=4.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register_displayname\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050013,\n        \"text\": \"Name of the security key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register_trigger\",\n      \"type\": \"button\",\n      \"disabled\": false,\n      \"onclickTrigger\": \"oryWebAuthnRegistration\",\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040004,\n        \"text\": \"Sign up with security key\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"script\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"async\": true,\n      \"referrerpolicy\": \"no-referrer\",\n      \"crossorigin\": \"anonymous\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"type\": \"text/javascript\",\n      \"id\": \"webauthn_script\",\n      \"node_type\": \"script\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"webauthn\",\n    \"attributes\": {\n      \"name\": \"webauthn_register\",\n      \"type\": \"hidden\",\n      \"value\": \"\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_does_not_exist_when_passwordless_is_disabled-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.foobar\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.username\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_does_not_exist_when_passwordless_is_disabled-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.foobar\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.username\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-browser.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.foobar\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.username\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register_displayname\",\n      \"node_type\": \"input\",\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050013,\n        \"text\": \"Name of the security key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnRegistration\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040004,\n        \"text\": \"Sign up with security key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/.snapshots/TestRegistration-case=webauthn_button_exists-spa.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"hidden\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.foobar\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.username\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"text\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register_displayname\",\n      \"node_type\": \"input\",\n      \"type\": \"text\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050013,\n        \"text\": \"Name of the security key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register\",\n      \"node_type\": \"input\",\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"webauthn_register_trigger\",\n      \"node_type\": \"input\",\n      \"onclickTrigger\": \"oryWebAuthnRegistration\",\n      \"type\": \"button\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040004,\n        \"text\": \"Sign up with security key\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"async\": true,\n      \"crossorigin\": \"anonymous\",\n      \"id\": \"webauthn_script\",\n      \"integrity\": \"sha512-Dg0gN3fy+JoKxRp9Zda/4KYn3SlMdaKjs3fK5g6nDVQ/CVakD1dfMQyvRtJeiAtzSMEFviJbBLcVSrsBPGsFBA==\",\n      \"node_type\": \"script\",\n      \"referrerpolicy\": \"no-referrer\",\n      \"type\": \"text/javascript\"\n    },\n    \"group\": \"webauthn\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"script\"\n  },\n  {\n    \"attributes\": {\n      \"autocomplete\": \"new-password\",\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"node_type\": \"input\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"node_type\": \"input\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1040001,\n        \"text\": \"Sign up\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/identity.json",
    "content": "{\n  \"id\": \"f5d1b6a3-a4bb-44f7-9161-f4f877efe9ad\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"http://localhost:4455/schemas/default\",\n  \"state\": \"active\",\n  \"state_changed_at\": \"2021-08-17T12:18:01.690425+02:00\",\n  \"traits\": {\n    \"email\": \"foo@bar.com\",\n    \"website\": \"https://www.ory.sh\"\n  },\n  \"created_at\": \"2021-08-17T12:18:01.690741+02:00\",\n  \"updated_at\": \"2021-08-17T12:18:01.690741+02:00\"\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/response.invalid.json",
    "content": "{\n  \"id\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"rawId\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAACg\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiV3paQ1dVTG1hcTV4VEFsQTBZWUhscW91YnFBaGUxQVdkTFJaQ0lCQU1jTSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n    \"signature\": \"MEQCIHtRzzmLJrTPucNIRpPkstxR8oGJEzrm558LFe2jHTesAiAy2SGuBMDkdVdMJU4WJR2qFSpbAHQUvwG--Gv3vK8vDA\",\n    \"userHandle\": \"AAaaaaaaTD6lazfHs42XPg\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v0/credentials.json",
    "content": "{\n  \"credentials\": [\n    {\n      \"id\": \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\",\n      \"public_key\": \"pQECAyYgASFYIPW2FsD6d/Lc7SU33hMhJUxafOA3JWpsLka8eKO+OPRkIlggkkPt8ocrupQOuvy+8HbQLSLiu899EdchJlWdMPE1tiw=\",\n      \"attestation_type\": \"none\",\n      \"authenticator\": {\n        \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n        \"sign_count\": 3,\n        \"clone_warning\": false\n      },\n      \"display_name\": \"some-key\",\n      \"added_at\": \"2021-08-17T10:18:55Z\"\n    }\n  ]\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v0/internal_context.json",
    "content": "{\n  \"webauthn_session_data\": {\n    \"challenge\": \"WzZCWULmaq5xTAlA0YYHlqoubqAhe1AWdLRZCIBAMcM\",\n    \"user_id\": \"9dG2o6S7RPeRYfT4d+/prQ==\",\n    \"allowed_credentials\": [\n      \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\"\n    ],\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v0/response.json",
    "content": "{\n  \"id\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"rawId\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAACg\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiV3paQ1dVTG1hcTV4VEFsQTBZWUhscW91YnFBaGUxQVdkTFJaQ0lCQU1jTSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n    \"signature\": \"MEQCIHtRzzmLJrTPucNIRpPkstxR8oGJEzrm558LFe2jHTesAiAy2SGuBMDkdVdMJU4WJR2qFSpbAHQUvwG--Gv3vK8vDA\",\n    \"userHandle\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v1/credentials.json",
    "content": "{\n  \"credentials\": [\n    {\n      \"id\": \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\",\n      \"public_key\": \"pQECAyYgASFYIPW2FsD6d/Lc7SU33hMhJUxafOA3JWpsLka8eKO+OPRkIlggkkPt8ocrupQOuvy+8HbQLSLiu899EdchJlWdMPE1tiw=\",\n      \"attestation_type\": \"none\",\n      \"authenticator\": {\n        \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n        \"sign_count\": 3,\n        \"clone_warning\": false\n      },\n      \"display_name\": \"some-key\",\n      \"added_at\": \"2021-08-17T10:18:55Z\",\n      \"is_passwordless\": false\n    }\n  ],\n  \"user_handle\": \"9dG2o6S7RPeRYfT4d+/prQ==\"\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v1/internal_context.json",
    "content": "{\n  \"webauthn_session_data\": {\n    \"challenge\": \"WzZCWULmaq5xTAlA0YYHlqoubqAhe1AWdLRZCIBAMcM\",\n    \"user_id\": \"9dG2o6S7RPeRYfT4d+/prQ==\",\n    \"allowed_credentials\": [\n      \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\"\n    ],\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v1/response.json",
    "content": "{\n  \"id\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"rawId\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAACg\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiV3paQ1dVTG1hcTV4VEFsQTBZWUhscW91YnFBaGUxQVdkTFJaQ0lCQU1jTSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n    \"signature\": \"MEQCIHtRzzmLJrTPucNIRpPkstxR8oGJEzrm558LFe2jHTesAiAy2SGuBMDkdVdMJU4WJR2qFSpbAHQUvwG--Gv3vK8vDA\",\n    \"userHandle\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v1_handle/credentials.json",
    "content": "{\n  \"credentials\": [\n    {\n      \"id\": \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\",\n      \"public_key\": \"pQECAyYgASFYIPW2FsD6d/Lc7SU33hMhJUxafOA3JWpsLka8eKO+OPRkIlggkkPt8ocrupQOuvy+8HbQLSLiu899EdchJlWdMPE1tiw=\",\n      \"attestation_type\": \"none\",\n      \"authenticator\": {\n        \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n        \"sign_count\": 3,\n        \"clone_warning\": false\n      },\n      \"display_name\": \"some-key\",\n      \"added_at\": \"2021-08-17T10:18:55Z\",\n      \"is_passwordless\": false\n    }\n  ],\n  \"user_handle\": \"RPwiyauuTD6lazfHs42XPg==\"\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v1_handle/internal_context.json",
    "content": "{\n  \"webauthn_session_data\": {\n    \"challenge\": \"WzZCWULmaq5xTAlA0YYHlqoubqAhe1AWdLRZCIBAMcM\",\n    \"user_id\": \"RPwiyauuTD6lazfHs42XPg==\",\n    \"allowed_credentials\": [\n      \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\"\n    ],\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v1_handle/response.json",
    "content": "{\n  \"id\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"rawId\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAACg\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiV3paQ1dVTG1hcTV4VEFsQTBZWUhscW91YnFBaGUxQVdkTFJaQ0lCQU1jTSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n    \"signature\": \"MEQCIHtRzzmLJrTPucNIRpPkstxR8oGJEzrm558LFe2jHTesAiAy2SGuBMDkdVdMJU4WJR2qFSpbAHQUvwG--Gv3vK8vDA\",\n    \"userHandle\": \"RPwiyauuTD6lazfHs42XPg\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v1_passwordless/credentials.json",
    "content": "{\n  \"credentials\": [\n    {\n      \"id\": \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\",\n      \"public_key\": \"pQECAyYgASFYIPW2FsD6d/Lc7SU33hMhJUxafOA3JWpsLka8eKO+OPRkIlggkkPt8ocrupQOuvy+8HbQLSLiu899EdchJlWdMPE1tiw=\",\n      \"attestation_type\": \"none\",\n      \"authenticator\": {\n        \"aaguid\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n        \"sign_count\": 3,\n        \"clone_warning\": false\n      },\n      \"display_name\": \"some-key\",\n      \"added_at\": \"2021-08-17T10:18:55Z\",\n      \"is_passwordless\": true\n    }\n  ],\n  \"user_handle\": \"9dG2o6S7RPeRYfT4d+/prQ==\"\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v1_passwordless/internal_context.json",
    "content": "{\n  \"webauthn_session_data\": {\n    \"challenge\": \"WzZCWULmaq5xTAlA0YYHlqoubqAhe1AWdLRZCIBAMcM\",\n    \"user_id\": \"9dG2o6S7RPeRYfT4d+/prQ==\",\n    \"allowed_credentials\": [\n      \"OE7fnoAeqaydiBM4+fQsbYMiO+EObq97WYb/c1HuX8Crbsb2777xs+upv7muXE8hOLkm6lQHC1ahegnzw+aIsQ==\"\n    ],\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/login/success/mfa/v1_passwordless/response.json",
    "content": "{\n  \"id\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"rawId\": \"OE7fnoAeqaydiBM4-fQsbYMiO-EObq97WYb_c1HuX8Crbsb2777xs-upv7muXE8hOLkm6lQHC1ahegnzw-aIsQ\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAACg\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiV3paQ1dVTG1hcTV4VEFsQTBZWUhscW91YnFBaGUxQVdkTFJaQ0lCQU1jTSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n    \"signature\": \"MEQCIHtRzzmLJrTPucNIRpPkstxR8oGJEzrm558LFe2jHTesAiAy2SGuBMDkdVdMJU4WJR2qFSpbAHQUvwG--Gv3vK8vDA\",\n    \"userHandle\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/registration/failure/internal_context_missing_user_id.json",
    "content": "{\n  \"webauthn_session_data\": {\n    \"challenge\": \"UlxHSTkuMvtVDoV9y5lhu9OyNUP8P7MP0RYAT6Im_rY\",\n    \"user_id\": \"\",\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/registration/failure/internal_context_wrong_user_id.json",
    "content": "{\n  \"webauthn_session_data\": {\n    \"challenge\": \"UlxHSTkuMvtVDoV9y5lhu9OyNUP8P7MP0RYAT6Im_rY\",\n    \"user_id\": \"wrong\",\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/registration/success/identity.json",
    "content": "{\n  \"id\": \"6e11a9a7-62fd-4c88-871a-097f18f0306f\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"http://localhost:4455/schemas/default\",\n  \"state\": \"active\",\n  \"state_changed_at\": \"2021-08-17T11:15:59.232051+02:00\",\n  \"traits\": {\n    \"email\": \"foo@bar.com\",\n    \"website\": \"https://www.ory.sh\"\n  },\n  \"created_at\": \"2021-08-17T11:15:59.232288+02:00\",\n  \"updated_at\": \"2021-08-17T11:15:59.232288+02:00\"\n}\n\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/registration/success/internal_context.json",
    "content": "{\n  \"totp_url\": \"otpauth://totp/issuer.ory.sh:6e11a9a7-62fd-4c88-871a-097f18f0306f?algorithm=SHA1&digits=6&issuer=issuer.ory.sh&period=30&secret=2F43HRJNMUW67EDMRR7AKQYRZP3AI6IG\",\n  \"webauthn_session_data\": {\n    \"challenge\": \"UlxHSTkuMvtVDoV9y5lhu9OyNUP8P7MP0RYAT6Im_rY\",\n    \"user_id\": \"bhGpp2L9TIiHGgl/GPAwbw==\",\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/registration/success/response.json",
    "content": "{\n  \"id\": \"L1yOrxHy5Lq72lAPahaWdl0q9gsXRzV2BJ4xJmkTVH_8uuVKU-FVbJlVRwYGzPNc1IjCWUYAK0H0YSpd5hz-Pg\",\n  \"rawId\": \"L1yOrxHy5Lq72lAPahaWdl0q9gsXRzV2BJ4xJmkTVH_8uuVKU-FVbJlVRwYGzPNc1IjCWUYAK0H0YSpd5hz-Pg\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjESZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NBAAAABAAAAAAAAAAAAAAAAAAAAAAAQC9cjq8R8uS6u9pQD2oWlnZdKvYLF0c1dgSeMSZpE1R__LrlSlPhVWyZVUcGBszzXNSIwllGACtB9GEqXeYc_j6lAQIDJiABIVggFFzdor6hBMgrpYLCds8Uu2JtPaaaxKU6LEAUT6QRZ5UiWCA24TI4vED6rrTUjykchoAln67u5GT1nwmzjvrk79HhlQ\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiVWx4SFNUa3VNdnRWRG9WOXk1bGh1OU95TlVQOFA3TVAwUllBVDZJbV9yWSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/settings/success/identity.json",
    "content": "{\n  \"id\": \"6e11a9a7-62fd-4c88-871a-097f18f0306f\",\n  \"schema_id\": \"default\",\n  \"schema_url\": \"http://localhost:4455/schemas/default\",\n  \"state\": \"active\",\n  \"state_changed_at\": \"2021-08-17T11:15:59.232051+02:00\",\n  \"traits\": {\n    \"email\": \"foo@bar.com\",\n    \"website\": \"https://www.ory.sh\"\n  },\n  \"created_at\": \"2021-08-17T11:15:59.232288+02:00\",\n  \"updated_at\": \"2021-08-17T11:15:59.232288+02:00\"\n}\n\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/settings/success/internal_context.json",
    "content": "{\n  \"totp_url\": \"otpauth://totp/issuer.ory.sh:6e11a9a7-62fd-4c88-871a-097f18f0306f?algorithm=SHA1&digits=6&issuer=issuer.ory.sh&period=30&secret=2F43HRJNMUW67EDMRR7AKQYRZP3AI6IG\",\n  \"webauthn_session_data\": {\n    \"challenge\": \"UlxHSTkuMvtVDoV9y5lhu9OyNUP8P7MP0RYAT6Im_rY\",\n    \"user_id\": \"bhGpp2L9TIiHGgl/GPAwbw==\",\n    \"userVerification\": \"\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/fixtures/settings/success/response.json",
    "content": "{\n  \"id\": \"L1yOrxHy5Lq72lAPahaWdl0q9gsXRzV2BJ4xJmkTVH_8uuVKU-FVbJlVRwYGzPNc1IjCWUYAK0H0YSpd5hz-Pg\",\n  \"rawId\": \"L1yOrxHy5Lq72lAPahaWdl0q9gsXRzV2BJ4xJmkTVH_8uuVKU-FVbJlVRwYGzPNc1IjCWUYAK0H0YSpd5hz-Pg\",\n  \"type\": \"public-key\",\n  \"response\": {\n    \"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjESZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NBAAAABAAAAAAAAAAAAAAAAAAAAAAAQC9cjq8R8uS6u9pQD2oWlnZdKvYLF0c1dgSeMSZpE1R__LrlSlPhVWyZVUcGBszzXNSIwllGACtB9GEqXeYc_j6lAQIDJiABIVggFFzdor6hBMgrpYLCds8Uu2JtPaaaxKU6LEAUT6QRZ5UiWCA24TI4vED6rrTUjykchoAln67u5GT1nwmzjvrk79HhlQ\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiVWx4SFNUa3VNdnRWRG9WOXk1bGh1OU95TlVQOFA3TVAwUllBVDZJbV9yWSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQ1NSIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\n\t\"github.com/ory/kratos/selfservice/flowhelpers\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x/webauthnx\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/x/decoderx\"\n)\n\nvar (\n\t_ login.AAL1FormHydrator = new(Strategy)\n\t_ login.AAL2FormHydrator = new(Strategy)\n)\n\nfunc (s *Strategy) populateLoginMethodForPasswordless(r *http.Request, sr *login.Flow) error {\n\tsr.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tsr.UI.GetNodes().Append(node.NewInputField(\"method\", \"webauthn\", node.WebAuthnGroup, node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoSelfServiceLoginWebAuthn()))\n\treturn nil\n}\n\nfunc (s *Strategy) populateLoginMethod(r *http.Request, sr *login.Flow, i *identity.Identity, label *text.Message, aal identity.AuthenticatorAssuranceLevel) error {\n\tid, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), i.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcred, ok := id.GetCredentials(s.ID())\n\tif !ok {\n\t\t// Identity has no webauth\n\t\treturn nil\n\t}\n\n\tvar conf identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(cred.Config, &conf); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\twebAuthCreds := conf.Credentials.ToWebAuthnFiltered(aal, nil)\n\tif len(webAuthCreds) == 0 {\n\t\t// Identity has no webauthn\n\t\treturn webauthnx.ErrNoCredentials\n\t}\n\n\tweb, err := webauthn.New(s.d.Config().WebAuthnConfig(r.Context()))\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to initiate WebAuth.\").WithDebug(err.Error()))\n\t}\n\n\toptions, sessionData, err := web.BeginLogin(webauthnx.NewUser(conf.UserHandle, webAuthCreds, web.Config))\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to initiate WebAuth login.\").WithDebug(err.Error()))\n\t}\n\n\t// Remove the WebAuthn URL from the internal context now that it is set!\n\tsr.InternalContext, err = sjson.SetBytes(sr.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData), sessionData)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tinjectWebAuthnOptions, err := json.Marshal(options)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif len(cred.Identifiers) > 0 {\n\t\tsr.UI.SetNode(node.NewInputField(\"identifier\", cred.Identifiers[0], node.DefaultGroup, node.InputAttributeTypeHidden))\n\t}\n\n\tsr.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tsr.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(r.Context())))\n\tsr.UI.SetNode(webauthnx.NewWebAuthnLoginTrigger(string(injectWebAuthnOptions)).\n\t\tWithMetaLabel(label))\n\tsr.UI.Nodes.Upsert(webauthnx.NewWebAuthnLoginInput())\n\n\treturn nil\n}\n\nfunc (s *Strategy) handleLoginError(r *http.Request, f *login.Flow, err error) error {\n\tif f != nil {\n\t\tf.UI.Nodes.ResetNodes(\"webauth_login\")\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn err\n}\n\n// Update Login Flow with WebAuthn Method\n//\n// swagger:model updateLoginFlowWithWebAuthnMethod\ntype updateLoginFlowWithWebAuthnMethod struct {\n\t// Identifier is the email or username of the user trying to log in.\n\t//\n\t// required: true\n\tIdentifier string `json:\"identifier\"`\n\n\t// Method should be set to \"webAuthn\" when logging in using the WebAuthn strategy.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Sending the anti-csrf token is only required for browser login flows.\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Login a WebAuthn Security Key\n\t//\n\t// This must contain the ID of the WebAuthN connection.\n\tLogin string `json:\"webauthn_login\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, sess *session.Session) (i *identity.Identity, err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.webauthn.Strategy.Login\")\n\tdefer otelx.End(span, &err)\n\n\tif f.Type != flow.TypeBrowser {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"flow type is not browser\"))\n\t\treturn nil, flow.ErrStrategyNotResponsible\n\t}\n\n\tvar p updateLoginFlowWithWebAuthnMethod\n\tif err := decoderx.Decode(r, &p,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.MustHTTPRawJSONSchemaCompiler(loginSchema),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat()); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\tf.TransientPayload = p.TransientPayload\n\n\tif len(p.Login) > 0 || p.Method == s.SettingsStrategyID() {\n\t\t// This method has only two submit buttons\n\t\tp.Method = s.SettingsStrategyID()\n\t} else {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"login is not provided and method is not webauthn\"))\n\t\treturn nil, flow.ErrStrategyNotResponsible\n\t}\n\n\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tif s.d.Config().WebAuthnForPasswordless(ctx) || f.IsRefresh() && f.RequestedAAL == identity.AuthenticatorAssuranceLevel1 {\n\t\treturn s.loginPasswordless(ctx, w, r, f, &p)\n\t}\n\n\treturn s.loginMultiFactor(ctx, r, f, sess.IdentityID, &p)\n}\n\nfunc (s *Strategy) loginPasswordless(ctx context.Context, w http.ResponseWriter, r *http.Request, f *login.Flow, p *updateLoginFlowWithWebAuthnMethod) (i *identity.Identity, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.webauthn.Strategy.loginPasswordless\")\n\tdefer otelx.End(span, &err)\n\n\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel1); err != nil {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"requested AAL is not AAL1\"))\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tif err := flow.EnsureCSRF(s.d, r, f.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, err)\n\t}\n\n\tif p.Identifier == \"\" {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrBadRequest.WithReason(\"identifier is required\")))\n\t}\n\n\ti, _, err = s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(ctx, s.ID(), p.Identifier)\n\tif err != nil {\n\t\ttime.Sleep(x.RandomDelay(s.d.Config().HasherArgon2(ctx).ExpectedDuration, s.d.Config().HasherArgon2(ctx).ExpectedDeviation))\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoWebAuthnCredentials()))\n\t}\n\n\tif len(p.Login) == 0 {\n\t\t// Reset all nodes to not confuse users.\n\t\t// This is kinda hacky and will probably need to be updated at some point.\n\t\tpreviousNodes := f.UI.Nodes\n\t\tf.UI.Nodes = node.Nodes{}\n\n\t\tif err := s.populateLoginMethod(r, f, i, text.NewInfoSelfServiceLoginContinue(), identity.AuthenticatorAssuranceLevel1); errors.Is(err, webauthnx.ErrNoCredentials) {\n\t\t\tf.UI.Nodes = previousNodes\n\t\t\treturn nil, s.handleLoginError(r, f, schema.NewNoWebAuthnCredentials())\n\t\t} else if err != nil {\n\t\t\treturn nil, s.handleLoginError(r, f, err)\n\t\t}\n\n\t\t// Adds the \"Continue\" button\n\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\tf.UI.Messages.Add(text.NewInfoLoginWebAuthnPasswordless())\n\t\tf.UI.SetNode(node.NewInputField(\"identifier\", p.Identifier, node.DefaultGroup, node.InputAttributeTypeHidden, node.WithRequiredInputAttribute))\n\t\tif err := s.d.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\t\treturn nil, s.handleLoginError(r, f, err)\n\t\t}\n\n\t\tredirectTo := f.AppendTo(s.d.Config().SelfServiceFlowLoginUI(ctx)).String()\n\t\tif x.IsJSONRequest(r) {\n\t\t\ts.d.Writer().WriteError(w, r, flow.NewBrowserLocationChangeRequiredError(redirectTo))\n\t\t} else {\n\t\t\thttp.Redirect(w, r, f.AppendTo(s.d.Config().SelfServiceFlowLoginUI(ctx)).String(), http.StatusSeeOther)\n\t\t}\n\n\t\treturn nil, errors.WithStack(flow.ErrCompletedByStrategy)\n\t}\n\n\treturn s.loginAuthenticate(ctx, r, f, i.ID, p, identity.AuthenticatorAssuranceLevel1)\n}\n\nfunc (s *Strategy) loginAuthenticate(ctx context.Context, r *http.Request, f *login.Flow, identityID uuid.UUID, p *updateLoginFlowWithWebAuthnMethod, aal identity.AuthenticatorAssuranceLevel) (_ *identity.Identity, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.webauthn.Strategy.loginAuthenticate\")\n\tdefer otelx.End(span, &err)\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, identityID)\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoWebAuthnRegistered()))\n\t}\n\n\tc, ok := i.GetCredentials(s.ID())\n\tif !ok {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(schema.NewNoWebAuthnRegistered()))\n\t}\n\n\tvar o identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(c.Config, &o); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrInternalServerError.WithReason(\"The WebAuthn credentials could not be decoded properly\").WithDebug(err.Error()).WithWrap(err)))\n\t}\n\n\tweb, err := webauthn.New(s.d.Config().WebAuthnConfig(ctx))\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to get webAuthn config.\").WithDebug(err.Error())))\n\t}\n\n\twebAuthnResponse, err := protocol.ParseCredentialRequestResponseBody(strings.NewReader(p.Login))\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to parse WebAuthn response.\").WithDebug(err.Error())))\n\t}\n\n\tvar webAuthnSess webauthn.SessionData\n\tif err := json.Unmarshal([]byte(gjson.GetBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData)).Raw), &webAuthnSess); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN in internal context to be an object but got: %s\", err)))\n\t}\n\n\twebAuthCreds := o.Credentials.ToWebAuthnFiltered(aal, &webAuthnResponse.Response.AuthenticatorData.Flags)\n\tif f.IsRefresh() {\n\t\twebAuthCreds = o.Credentials.ToWebAuthn()\n\t}\n\n\tif _, err := web.ValidateLogin(webauthnx.NewUser(o.UserHandle, webAuthCreds, web.Config), webAuthnSess, webAuthnResponse); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(schema.NewWebAuthnVerifierWrongError(\"#/\")))\n\t}\n\n\t// Remove the WebAuthn URL from the internal context now that it is set!\n\tf.InternalContext, err = sjson.DeleteBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData))\n\tif err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(err))\n\t}\n\n\tf.Active = s.ID()\n\tif err = s.d.LoginFlowPersister().UpdateLoginFlow(ctx, f); err != nil {\n\t\treturn nil, s.handleLoginError(r, f, errors.WithStack(herodot.ErrInternalServerError.WithReason(\"Could not update flow\").WithDebug(err.Error())))\n\t}\n\n\treturn i, nil\n}\n\nfunc (s *Strategy) loginMultiFactor(ctx context.Context, r *http.Request, f *login.Flow, identityID uuid.UUID, p *updateLoginFlowWithWebAuthnMethod) (*identity.Identity, error) {\n\tif err := login.CheckAAL(f, identity.AuthenticatorAssuranceLevel2); err != nil {\n\t\ttrace.SpanFromContext(ctx).SetAttributes(attribute.String(\"not_responsible_reason\", \"requested AAL is not AAL2\"))\n\t\treturn nil, err\n\t}\n\treturn s.loginAuthenticate(ctx, r, f, identityID, p, identity.AuthenticatorAssuranceLevel2)\n}\n\nfunc (s *Strategy) populateLoginMethodRefresh(r *http.Request, sr *login.Flow) error {\n\tif sr.Type != flow.TypeBrowser {\n\t\treturn nil\n\t}\n\n\tidentifier, id, _ := flowhelpers.GuessForcedLoginIdentifier(r, s.d, sr, s.ID())\n\tif identifier == \"\" {\n\t\treturn nil\n\t}\n\n\tif err := s.populateLoginMethod(r, sr, id, text.NewInfoSelfServiceLoginWebAuthn(), sr.RequestedAAL); errors.Is(err, webauthnx.ErrNoCredentials) {\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn err\n\t}\n\n\tsr.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tsr.UI.SetNode(node.NewInputField(\"identifier\", identifier, node.DefaultGroup, node.InputAttributeTypeHidden))\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactorRefresh(r *http.Request, sr *login.Flow, _ *session.Session) error {\n\treturn s.populateLoginMethodRefresh(r, sr)\n}\n\nfunc (s *Strategy) PopulateLoginMethodSecondFactorRefresh(r *http.Request, sr *login.Flow) error {\n\treturn s.populateLoginMethodRefresh(r, sr)\n}\n\nfunc (s *Strategy) PopulateLoginMethodFirstFactor(r *http.Request, sr *login.Flow) error {\n\tif sr.Type != flow.TypeBrowser || !s.d.Config().WebAuthnForPasswordless(r.Context()) {\n\t\treturn nil\n\t}\n\n\tds, err := sr.IdentitySchema.URL(r.Context(), s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tidentifierLabel, err := login.GetIdentifierLabelFromSchema(r.Context(), ds.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsr.UI.SetNode(node.NewInputField(\n\t\t\"identifier\",\n\t\t\"\",\n\t\tnode.DefaultGroup,\n\t\tnode.InputAttributeTypeText,\n\t\tnode.WithRequiredInputAttribute,\n\t\tfunc(attributes *node.InputAttributes) {\n\t\t\tattributes.Autocomplete = node.InputAttributeAutocompleteUsernameWebauthn\n\t\t},\n\t).WithMetaLabel(identifierLabel))\n\n\tif err := s.populateLoginMethodForPasswordless(r, sr); errors.Is(err, webauthnx.ErrNoCredentials) {\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodSecondFactor(r *http.Request, sr *login.Flow) error {\n\tif sr.Type != flow.TypeBrowser || s.d.Config().WebAuthnForPasswordless(r.Context()) {\n\t\treturn nil\n\t}\n\n\t// We have done proper validation before so this should never error\n\tsess, err := s.d.SessionManager().FetchFromRequest(r.Context(), r)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.populateLoginMethod(r, sr, sess.Identity, text.NewInfoSelfServiceLoginWebAuthn(), identity.AuthenticatorAssuranceLevel2); errors.Is(err, webauthnx.ErrNoCredentials) {\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstCredentials(r *http.Request, sr *login.Flow, opts ...login.FormHydratorModifier) error {\n\tif sr.Type != flow.TypeBrowser || !s.d.Config().WebAuthnForPasswordless(r.Context()) {\n\t\treturn errors.WithStack(idfirst.ErrNoCredentialsFound)\n\t}\n\n\to := login.NewFormHydratorOptions(opts)\n\n\tvar count int\n\tif o.IdentityHint != nil {\n\t\tvar err error\n\t\t// If we have an identity hint we can perform identity credentials discovery and\n\t\t// hide this credential if it should not be included.\n\t\tif count, err = s.CountActiveFirstFactorCredentials(r.Context(), o.IdentityHint.Credentials); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif count > 0 || s.d.Config().SecurityAccountEnumerationMitigate(r.Context()) {\n\t\tif err := s.populateLoginMethodForPasswordless(r, sr); errors.Is(err, webauthnx.ErrNoCredentials) {\n\t\t\tif !s.d.Config().SecurityAccountEnumerationMitigate(r.Context()) {\n\t\t\t\treturn errors.WithStack(idfirst.ErrNoCredentialsFound)\n\t\t\t}\n\t\t\treturn nil\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif count == 0 {\n\t\treturn errors.WithStack(idfirst.ErrNoCredentialsFound)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateLoginMethodIdentifierFirstIdentification(r *http.Request, sr *login.Flow) error {\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/login_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/strategy/idfirst\"\n\t\"github.com/ory/kratos/selfservice/strategy/webauthn\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/jsonx\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\nvar (\n\t//go:embed fixtures/login/success/mfa/response.invalid.json\n\tloginFixtureSuccessResponseInvalid []byte\n\t//go:embed fixtures/login/success/mfa/identity.json\n\tloginFixtureSuccessIdentity []byte\n\t//go:embed fixtures/login/success/mfa/v0/credentials.json\n\tloginFixtureSuccessV0Credentials []byte\n\t//go:embed fixtures/login/success/mfa/v0/internal_context.json\n\tloginFixtureSuccessV0Context []byte\n\t//go:embed fixtures/login/success/mfa/v0/response.json\n\tloginFixtureSuccessV0Response []byte\n\t//go:embed fixtures/login/success/mfa/v1/credentials.json\n\tloginFixtureSuccessV1Credentials []byte\n\t//go:embed fixtures/login/success/mfa/v1/internal_context.json\n\tloginFixtureSuccessV1Context []byte\n\t//go:embed fixtures/login/success/mfa/v1/response.json\n\tloginFixtureSuccessV1Response []byte\n\t//go:embed fixtures/login/success/mfa/v1_handle/credentials.json\n\tloginFixtureSuccessV1WithHandleCredentials []byte\n\t//go:embed fixtures/login/success/mfa/v1_handle/internal_context.json\n\tloginFixtureSuccessV1WithHandleContext []byte\n\t//go:embed fixtures/login/success/mfa/v1_handle/response.json\n\tloginFixtureSuccessV1WithHandleResponse []byte\n\t//go:embed fixtures/login/success/mfa/v1_passwordless/credentials.json\n\tloginFixtureSuccessV1PasswordlessCredentials []byte\n\t//go:embed fixtures/login/success/mfa/v1_passwordless/internal_context.json\n\tloginFixtureSuccessV1PasswordlessContext []byte\n\t//go:embed fixtures/login/success/mfa/v1_passwordless/response.json\n\tloginFixtureSuccessV1PasswordlessResponse []byte\n)\n\nvar loginFixtureSuccessEmail = gjson.GetBytes(loginFixtureSuccessIdentity, \"traits.email\").String()\n\nfunc TestCompleteLogin(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypePassword)+\".enabled\", false),\n\t\tenabledWebauthn,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/login.schema.json\")),\n\t)\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\tuiTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\n\tcheckURL := func(t *testing.T, shouldRedirect bool, res *http.Response) {\n\t\tif shouldRedirect {\n\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/login-ts\")\n\t\t} else {\n\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t}\n\t}\n\n\tdoAPIFlow := func(t *testing.T, v func(url.Values), apiClient *http.Client, opts ...testhelpers.InitFlowWithOption) (string, *http.Response) {\n\t\tf := testhelpers.InitializeLoginFlowViaAPI(t, apiClient, publicTS, false, opts...)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tv(values)\n\t\tpayload := testhelpers.EncodeFormAsJSON(t, true, values)\n\t\treturn testhelpers.LoginMakeRequest(t, true, false, f, apiClient, payload)\n\t}\n\n\tdoBrowserFlow := func(t *testing.T, spa bool, v func(url.Values), browserClient *http.Client, opts ...testhelpers.InitFlowWithOption) (string, *http.Response) {\n\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, spa, false, false, opts...)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tv(values)\n\t\treturn testhelpers.LoginMakeRequest(t, false, spa, f, browserClient, values.Encode())\n\t}\n\n\tcreateIdentityWithWebAuthn := func(t *testing.T, c identity.Credentials) *identity.Identity {\n\t\tvar id identity.Identity\n\t\trequire.NoError(t, json.Unmarshal(loginFixtureSuccessIdentity, &id))\n\n\t\tid.SetCredentials(identity.CredentialsTypeWebAuthn, identity.Credentials{\n\t\t\tIdentifiers: []string{loginFixtureSuccessEmail},\n\t\t\tConfig:      c.Config,\n\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\tVersion:     c.Version,\n\t\t})\n\n\t\t// We clean up the identity in case it has been created before\n\t\t_ = reg.PrivilegedIdentityPool().DeleteIdentity(context.Background(), id.ID)\n\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &id))\n\t\treturn &id\n\t}\n\n\tsubmitWebAuthnLoginFlowWithClient := func(t *testing.T, isSPA bool, f *kratos.LoginFlow, contextFixture []byte, client *http.Client, cb func(values url.Values)) (string, *http.Response, *kratos.LoginFlow) {\n\t\t// We inject the session to replay\n\t\tinterim, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\trequire.NoError(t, err)\n\t\tinterim.InternalContext = contextFixture\n\t\trequire.NoError(t, reg.LoginFlowPersister().UpdateLoginFlow(context.Background(), interim))\n\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tcb(values)\n\n\t\t// We use the response replay\n\t\tbody, res := testhelpers.LoginMakeRequest(t, false, isSPA, f, client, values.Encode())\n\t\treturn body, res, f\n\t}\n\n\tsubmitWebAuthnLoginWithClient := func(t *testing.T, isSPA bool, id *identity.Identity, contextFixture []byte, client *http.Client, cb func(values url.Values), opts ...testhelpers.InitFlowWithOption) (string, *http.Response, *kratos.LoginFlow) {\n\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, client, publicTS, false, isSPA, false, false, opts...)\n\t\treturn submitWebAuthnLoginFlowWithClient(t, isSPA, f, contextFixture, client, cb)\n\t}\n\n\tsubmitWebAuthnLogin := func(t *testing.T, isSPA bool, id *identity.Identity, contextFixture []byte, cb func(values url.Values), opts ...testhelpers.InitFlowWithOption) (string, *http.Response, *kratos.LoginFlow) {\n\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\treturn submitWebAuthnLoginWithClient(t, isSPA, id, contextFixture, browserClient, cb, opts...)\n\t}\n\n\tt.Run(\"flow=refresh\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySessionWhoAmIAAL, nil)\n\t\t})\n\n\t\trun := func(t *testing.T, id *identity.Identity, context, response []byte, isSPA bool, expectedAAL identity.AuthenticatorAssuranceLevel, expectTriggers bool) {\n\t\t\tbody, res, f := submitWebAuthnLogin(t, isSPA, id, context, func(values url.Values) {\n\t\t\t\tvalues.Set(\"identifier\", loginFixtureSuccessEmail)\n\t\t\t\tvalues.Set(node.WebAuthnLogin, string(response))\n\t\t\t},\n\t\t\t\ttesthelpers.InitFlowWithRefresh(),\n\t\t\t\ttesthelpers.InitFlowWithAAL(expectedAAL),\n\t\t\t)\n\t\t\tsnapshotx.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\"0.attributes.value\",\n\t\t\t\t\"3.attributes.nonce\",\n\t\t\t\t\"3.attributes.src\",\n\t\t\t\t\"4.attributes.value\",\n\t\t\t\t\"4.attributes.onclick\",\n\t\t\t})\n\n\t\t\tnodes, err := json.Marshal(f.Ui.Nodes)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tif !expectTriggers {\n\t\t\t\tassert.Falsef(t, gjson.GetBytes(nodes, \"#(attributes.name==identifier)\").Exists(), \"%s\", nodes)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tassert.Equal(t, loginFixtureSuccessEmail, gjson.GetBytes(nodes, \"#(attributes.name==identifier).attributes.value\").String(), \"%s\", nodes)\n\n\t\t\tprefix := \"\"\n\t\t\tif isSPA {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow, \"%s\", body)\n\t\t\t\tprefix = \"session.\"\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), redirTS.URL, \"%s\", body)\n\t\t\t}\n\n\t\t\tassert.True(t, gjson.Get(body, prefix+\"active\").Bool(), \"%s\", body)\n\n\t\t\tassert.EqualValues(t, expectedAAL, gjson.Get(body, prefix+\"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\tassert.EqualValues(t, identity.CredentialsTypeWebAuthn, gjson.Get(body, prefix+\"authentication_methods.#(method==webauthn).method\").String(), \"%s\", body)\n\t\t\tassert.Len(t, gjson.Get(body, prefix+\"authentication_methods\").Array(), 2, \"%s\", body)\n\t\t\tassert.EqualValues(t, id.ID.String(), gjson.Get(body, prefix+\"identity.id\").String(), \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"case=passwordless\", func(t *testing.T) {\n\t\t\tfor _, e := range []bool{\n\t\t\t\ttrue,\n\t\t\t\tfalse,\n\t\t\t} {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeyWebAuthnPasswordless, e)\n\t\t\t\texpectedAAL := identity.AuthenticatorAssuranceLevel1\n\t\t\t\tif !e {\n\t\t\t\t\t// If passwordless is disabled, using WebAuthn means that we have a second factor enabled.\n\t\t\t\t\t// Thus, AAL2 :)\n\t\t\t\t\texpectedAAL = identity.AuthenticatorAssuranceLevel2\n\t\t\t\t}\n\n\t\t\t\tfor _, tc := range []struct {\n\t\t\t\t\tcreds          identity.Credentials\n\t\t\t\t\tresponse       []byte\n\t\t\t\t\tcontext        []byte\n\t\t\t\t\tdescript       string\n\t\t\t\t\texpectTriggers bool\n\t\t\t\t}{\n\t\t\t\t\t{\n\t\t\t\t\t\tcreds: identity.Credentials{\n\t\t\t\t\t\t\tConfig:  loginFixtureSuccessV0Credentials,\n\t\t\t\t\t\t\tVersion: 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcontext:        loginFixtureSuccessV0Context,\n\t\t\t\t\t\tresponse:       loginFixtureSuccessV0Response,\n\t\t\t\t\t\tdescript:       \"mfa v0 credentials\",\n\t\t\t\t\t\texpectTriggers: !e,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tcreds: identity.Credentials{\n\t\t\t\t\t\t\tConfig:  loginFixtureSuccessV1Credentials,\n\t\t\t\t\t\t\tVersion: 1,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcontext:        loginFixtureSuccessV1Context,\n\t\t\t\t\t\tresponse:       loginFixtureSuccessV1Response,\n\t\t\t\t\t\tdescript:       \"mfa v1 credentials\",\n\t\t\t\t\t\texpectTriggers: !e,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tcreds: identity.Credentials{\n\t\t\t\t\t\t\tConfig:  loginFixtureSuccessV1PasswordlessCredentials,\n\t\t\t\t\t\t\tVersion: 1,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcontext:        loginFixtureSuccessV1PasswordlessContext,\n\t\t\t\t\t\tresponse:       loginFixtureSuccessV1PasswordlessResponse,\n\t\t\t\t\t\tdescript:       \"passwordless credentials\",\n\t\t\t\t\t\texpectTriggers: e,\n\t\t\t\t\t},\n\t\t\t\t} {\n\t\t\t\t\tt.Run(fmt.Sprintf(\"passwordless enabled=%v/case=%s\", e, tc.descript), func(t *testing.T) {\n\t\t\t\t\t\tid := createIdentityWithWebAuthn(t, tc.creds)\n\n\t\t\t\t\t\tfor _, f := range []string{\n\t\t\t\t\t\t\t\"browser\",\n\t\t\t\t\t\t\t\"spa\",\n\t\t\t\t\t\t} {\n\t\t\t\t\t\t\tt.Run(f, func(t *testing.T) {\n\t\t\t\t\t\t\t\trun(t, id, tc.context, tc.response, f == \"spa\", expectedAAL, tc.expectTriggers)\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}\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=no webauth credentials\", func(t *testing.T) {\n\t\t\tfor _, e := range []bool{true, false} {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeyWebAuthnPasswordless, e)\n\t\t\t\tt.Run(fmt.Sprintf(\"passwordless=%v\", e), func(t *testing.T) {\n\t\t\t\t\tfor _, f := range []string{\"browser\", \"spa\"} {\n\t\t\t\t\t\tt.Run(f, func(t *testing.T) {\n\t\t\t\t\t\t\tid := identity.NewIdentity(\"\")\n\t\t\t\t\t\t\tid.NID = x.NewUUID()\n\t\t\t\t\t\t\tclient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\n\t\t\t\t\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, client, publicTS, true, f == \"spa\", false, false)\n\t\t\t\t\t\t\tsnapshotx.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tnodes, err := json.Marshal(f.Ui.Nodes)\n\t\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\t\tassert.False(t, gjson.GetBytes(nodes, \"#(attributes.name==identifier).attributes.value\").Bool(), \"%s\", nodes)\n\t\t\t\t\t\t\tassert.False(t, gjson.GetBytes(nodes, \"#(attributes.name==\"+node.WebAuthnLoginTrigger+\").attributes.value\").Bool(), \"%s\", nodes)\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\tt.Run(\"flow=passwordless\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeyWebAuthnPasswordless, true)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyWebAuthnPasswordless, false)\n\t\t})\n\n\t\tt.Run(\"case=webauthn button exists\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, client, publicTS, false, true, false, false)\n\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\"0.attributes.value\"})\n\t\t})\n\n\t\tt.Run(\"case=webauthn shows error if user tries to sign in but no such user exists\", func(t *testing.T) {\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"method\", identity.CredentialsTypeWebAuthn.String())\n\t\t\t\tv.Set(\"identifier\", \"doesnotexist\")\n\t\t\t}\n\n\t\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\tassert.Equal(t, text.NewErrorValidationSuchNoWebAuthnUser().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tbody, res := doBrowserFlow(t, false, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, true, body, res)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tbody, res := doBrowserFlow(t, true, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, false, body, res)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=webauthn shows error if user tries to sign in but user has no webauth credentials set up\", func(t *testing.T) {\n\t\t\tid, subject := createIdentityAndReturnIdentifier(t.Context(), t, reg, nil)\n\t\t\tid.DeleteCredentialsType(identity.CredentialsTypeWebAuthn)\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"method\", identity.CredentialsTypeWebAuthn.String())\n\t\t\t\tv.Set(\"identifier\", subject)\n\t\t\t}\n\n\t\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\tassert.Equal(t, text.NewErrorValidationSuchNoWebAuthnUser().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tbody, res := doBrowserFlow(t, false, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, true, body, res)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tbody, res := doBrowserFlow(t, true, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, false, body, res)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=webauthn MFA credentials can not be used for passwordless login\", func(t *testing.T) {\n\t\t\t_, subject := createIdentityAndReturnIdentifier(t.Context(), t, reg, []byte(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"is_passwordless\":false}]}`))\n\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"method\", identity.CredentialsTypeWebAuthn.String())\n\t\t\t\tv.Set(\"identifier\", subject)\n\t\t\t}\n\n\t\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\tassert.Equal(t, text.NewErrorValidationSuchNoWebAuthnUser().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tbody, res := doBrowserFlow(t, false, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, true, body, res)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tbody, res := doBrowserFlow(t, true, payload, testhelpers.NewClientWithCookies(t))\n\t\t\t\tcheck(t, false, body, res)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=should fail if webauthn login is invalid\", func(t *testing.T) {\n\t\t\t_, subject := createIdentityAndReturnIdentifier(t.Context(), t, reg, []byte(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\",\"is_passwordless\":true}]}`))\n\n\t\t\tdoBrowserFlow := func(t *testing.T, spa bool, browserClient *http.Client, opts ...testhelpers.InitFlowWithOption) {\n\t\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, browserClient, publicTS, false, spa, false, false, opts...)\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\n\t\t\t\tvalues.Set(\"method\", identity.CredentialsTypeWebAuthn.String())\n\t\t\t\tvalues.Set(\"identifier\", subject)\n\t\t\t\tbody, res := testhelpers.LoginMakeRequest(t, false, spa, f, browserClient, values.Encode())\n\t\t\t\tif spa {\n\t\t\t\t\tassert.Equal(t, http.StatusUnprocessableEntity, res.StatusCode)\n\t\t\t\t\tredir := gjson.Get(body, \"redirect_browser_to\").String()\n\t\t\t\t\tassert.NotEmpty(t, redir)\n\n\t\t\t\t\tres, err := browserClient.Get(redir)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tdefer func() { _ = res.Body.Close() }()\n\t\t\t\t\traw, err := io.ReadAll(res.Body)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tbody = string(raw)\n\t\t\t\t} else {\n\t\t\t\t\tcheckURL(t, !spa, res)\n\t\t\t\t}\n\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\tsnapshotx.SnapshotTJSON(t, body, snapshotx.ExceptNestedKeys(\"value\", \"src\", \"nonce\", \"action\", \"request_url\", \"issued_at\", \"expires_at\", \"created_at\", \"updated_at\", \"id\", \"onclick\"))\n\t\t\t\tassert.Equal(t, text.NewInfoLoginWebAuthnPasswordless().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\n\t\t\t\tvalues.Set(node.WebAuthnLogin, string(loginFixtureSuccessResponseInvalid))\n\t\t\t\tvalues.Set(\"identifier\", subject)\n\t\t\t\tbody, res = testhelpers.LoginMakeRequest(t, false, spa, f, browserClient, values.Encode())\n\t\t\t\tcheckURL(t, !spa, res)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\tassert.Equal(t, \"The provided authentication code is invalid, please try again.\", gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tdoBrowserFlow(t, false, testhelpers.NewClientWithCookies(t))\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tdoBrowserFlow(t, true, testhelpers.NewClientWithCookies(t))\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=succeeds with passwordless login\", func(t *testing.T) {\n\t\t\trun := func(t *testing.T, spa bool) {\n\t\t\t\tconf.MustSet(t.Context(), config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\t// We load our identity which we will use to replay the webauth session\n\t\t\t\tid := createIdentityWithWebAuthn(t, identity.Credentials{\n\t\t\t\t\tConfig:  loginFixtureSuccessV1PasswordlessCredentials,\n\t\t\t\t\tVersion: 1,\n\t\t\t\t})\n\n\t\t\t\tbrowserClient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tbody, res, f := submitWebAuthnLoginWithClient(t, spa, id, loginFixtureSuccessV1PasswordlessContext, browserClient, func(values url.Values) {\n\t\t\t\t\tvalues.Set(\"identifier\", loginFixtureSuccessEmail)\n\t\t\t\t\tvalues.Set(node.WebAuthnLogin, string(loginFixtureSuccessV1PasswordlessResponse))\n\t\t\t\t}, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel1))\n\n\t\t\t\tprefix := \"\"\n\t\t\t\tif spa {\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\t\t\tprefix = \"session.\"\n\t\t\t\t} else {\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), redirTS.URL)\n\t\t\t\t}\n\n\t\t\t\tassert.True(t, gjson.Get(body, prefix+\"active\").Bool(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel1, gjson.Get(body, prefix+\"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, identity.CredentialsTypeWebAuthn, gjson.Get(body, prefix+\"authentication_methods.#(method==webauthn).method\").String(), \"%s\", body)\n\t\t\t\tassert.EqualValues(t, id.ID.String(), gjson.Get(body, prefix+\"identity.id\").String(), \"%s\", body)\n\n\t\t\t\tactualFlow, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Empty(t, gjson.GetBytes(actualFlow.InternalContext, flow.PrefixInternalContextKey(identity.CredentialsTypeWebAuthn, webauthn.InternalContextKeySessionData)))\n\n\t\t\t\tif spa {\n\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(body, \"continue_with.0.action\").String(), \"%s\", body)\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"continue_with.0.redirect_browser_to\").String(), conf.SelfServiceBrowserDefaultReturnTo(t.Context()).String(), \"%s\", body)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\trun(t, false)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\trun(t, true)\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"flow=mfa\", func(t *testing.T) {\n\t\tt.Run(\"case=webauthn payload is set when identity has webauthn\", func(t *testing.T) {\n\t\t\tid := createIdentity(t.Context(), t, reg)\n\n\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, apiClient, publicTS, false, true, false, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\t\tassert.Equal(t, gjson.GetBytes(id.Traits, \"subject\").String(), f.Ui.Nodes[1].Attributes.UiNodeInputAttributes.Value, jsonx.TestMarshalJSONString(t, f.Ui))\n\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\"0.attributes.value\",\n\t\t\t\t\"1.attributes.value\",\n\t\t\t\t\"3.attributes.src\",\n\t\t\t\t\"3.attributes.nonce\",\n\t\t\t\t\"4.attributes.onclick\",\n\t\t\t\t\"4.attributes.onload\",\n\t\t\t\t\"4.attributes.value\",\n\t\t\t})\n\t\t\tensureReplacement(t, \"4\", f.Ui, \"allowCredentials\")\n\t\t})\n\n\t\tt.Run(\"case=webauthn payload is not set when identity has no webauthn\", func(t *testing.T) {\n\t\t\tid := createIdentityWithoutWebAuthn(t, reg)\n\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\t\tf := testhelpers.InitializeLoginFlowViaBrowser(t, apiClient, publicTS, false, true, false, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\n\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\"0.attributes.value\",\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=webauthn payload is not set for API clients\", func(t *testing.T) {\n\t\t\tid := createIdentity(t.Context(), t, reg)\n\n\t\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\t\tf := testhelpers.InitializeLoginFlowViaAPI(t, apiClient, publicTS, false, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\t\tassertx.EqualAsJSON(t, nil, f.Ui.Nodes)\n\t\t})\n\n\t\tdoAPIFlowSignedIn := func(t *testing.T, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\t\treturn doAPIFlow(t, v, testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id), testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\t}\n\n\t\tdoBrowserFlowSignIn := func(t *testing.T, spa bool, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\t\treturn doBrowserFlow(t, spa, v, testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id), testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\t}\n\n\t\tt.Run(\"case=should refuse to execute api flow\", func(t *testing.T) {\n\t\t\tid := createIdentity(t.Context(), t, reg)\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(node.WebAuthnLogin, \"{}\")\n\t\t\t}\n\n\t\t\tbody, res := doAPIFlowSignedIn(t, payload, id)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\tassert.Equal(t, \"Could not find a strategy to log you in with. Did you fill out the form correctly?\", gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"case=should fail if webauthn login is invalid\", func(t *testing.T) {\n\t\t\tid, sub := createIdentityAndReturnIdentifier(t.Context(), t, reg, nil)\n\t\t\tpayload := func(v url.Values) {\n\t\t\t\tv.Set(\"identifier\", sub)\n\t\t\t\tv.Set(node.WebAuthnLogin, string(loginFixtureSuccessResponseInvalid))\n\t\t\t}\n\n\t\t\tcheck := func(t *testing.T, shouldRedirect bool, body string, res *http.Response) {\n\t\t\t\tcheckURL(t, shouldRedirect, res)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"id\").String(), \"%s\", body)\n\t\t\t\tassert.Equal(t, \"The provided authentication code is invalid, please try again.\", gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t\t\t}\n\n\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\tbody, res := doBrowserFlowSignIn(t, false, payload, id)\n\t\t\t\tcheck(t, true, body, res)\n\t\t\t})\n\n\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\tbody, res := doBrowserFlowSignIn(t, true, payload, id)\n\t\t\t\tcheck(t, false, body, res)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=can not use security key for passwordless in mfa flow\", func(t *testing.T) {\n\t\t\tid := createIdentityWithWebAuthn(t, identity.Credentials{\n\t\t\t\tConfig:  loginFixtureSuccessV1PasswordlessCredentials,\n\t\t\t\tVersion: 1,\n\t\t\t})\n\n\t\t\tbody, res, _ := submitWebAuthnLogin(t, true, id, loginFixtureSuccessV1PasswordlessContext, func(values url.Values) {\n\t\t\t\tvalues.Set(\"identifier\", loginFixtureSuccessEmail)\n\t\t\t\tvalues.Set(node.WebAuthnLogin, string(loginFixtureSuccessV1PasswordlessResponse))\n\t\t\t}, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\tsnapshotx.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.messages\").Raw), []string{})\n\t\t})\n\n\t\tt.Run(\"case=login with a security key using\", func(t *testing.T) {\n\t\t\tidd := uuid.FromStringOrNil(\"44fc22c9-abae-4c3e-a56b-37c7b38d973e\")\n\t\t\tout, err := json.Marshal(identity.CredentialsWebAuthnConfig{UserHandle: idd[:]})\n\t\t\trequire.NoError(t, err)\n\t\t\tt.Logf(\"json: %s\", out)\n\t\t\tout, err = json.Marshal(protocol.AuthenticatorAssertionResponse{UserHandle: idd[:]})\n\t\t\trequire.NoError(t, err)\n\t\t\tt.Logf(\"wa: %s\", out)\n\n\t\t\tfor _, tc := range []struct {\n\t\t\t\td  string\n\t\t\t\tv  int\n\t\t\t\tcf []byte\n\t\t\t\tsf []byte\n\t\t\t\tix []byte\n\t\t\t}{\n\t\t\t\t{d: \"v0 without userhandle\", v: 0, cf: loginFixtureSuccessV0Credentials, sf: loginFixtureSuccessV0Response, ix: loginFixtureSuccessV0Context},\n\t\t\t\t{d: \"v1 without userhandle\", v: 1, cf: loginFixtureSuccessV1Credentials, sf: loginFixtureSuccessV1Response, ix: loginFixtureSuccessV1Context},\n\t\t\t\t{d: \"v1 with differing userhandle\", v: 1, cf: loginFixtureSuccessV1WithHandleCredentials, sf: loginFixtureSuccessV1WithHandleResponse, ix: loginFixtureSuccessV1WithHandleContext},\n\t\t\t} {\n\t\t\t\tt.Run(tc.d, func(t *testing.T) {\n\t\t\t\t\trun := func(t *testing.T, spa bool) {\n\t\t\t\t\t\t// We load our identity which we will use to replay the webauth session\n\t\t\t\t\t\tid := createIdentityWithWebAuthn(t, identity.Credentials{\n\t\t\t\t\t\t\tConfig:  tc.cf,\n\t\t\t\t\t\t\tVersion: tc.v,\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tbody, res, f := submitWebAuthnLogin(t, spa, id, tc.ix, func(values url.Values) {\n\t\t\t\t\t\t\tvalues.Set(\"identifier\", loginFixtureSuccessEmail)\n\t\t\t\t\t\t\tvalues.Set(node.WebAuthnLogin, string(tc.sf))\n\t\t\t\t\t\t}, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel2))\n\n\t\t\t\t\t\tprefix := \"\"\n\t\t\t\t\t\tif spa {\n\t\t\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+login.RouteSubmitFlow)\n\t\t\t\t\t\t\tprefix = \"session.\"\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), redirTS.URL)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tassert.True(t, gjson.Get(body, prefix+\"active\").Bool(), \"%s\", body)\n\t\t\t\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel2, gjson.Get(body, prefix+\"authenticator_assurance_level\").String(), \"%s\", body)\n\t\t\t\t\t\tassert.EqualValues(t, identity.CredentialsTypeWebAuthn, gjson.Get(body, prefix+\"authentication_methods.#(method==webauthn).method\").String(), \"%s\", body)\n\t\t\t\t\t\tassert.EqualValues(t, id.ID.String(), gjson.Get(body, prefix+\"identity.id\").String(), \"%s\", body)\n\n\t\t\t\t\t\tactualFlow, err := reg.LoginFlowPersister().GetLoginFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t\tassert.Empty(t, gjson.GetBytes(actualFlow.InternalContext, flow.PrefixInternalContextKey(identity.CredentialsTypeWebAuthn, webauthn.InternalContextKeySessionData)))\n\t\t\t\t\t}\n\n\t\t\t\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\t\t\t\trun(t, false)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\t\t\t\trun(t, true)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc TestFormHydration(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeWebAuthn)+\".enabled\", true)\n\tctx = contextx.WithConfigValue(\n\t\tctx,\n\t\tconfig.ViperKeySelfServiceStrategyConfig+\".\"+string(identity.CredentialsTypeWebAuthn)+\".config\",\n\t\tmap[string]interface{}{\n\t\t\t\"rp\": map[string]interface{}{\n\t\t\t\t\"display_name\": \"foo\",\n\t\t\t\t\"id\":           \"localhost\",\n\t\t\t\t\"origins\":      []string{\"http://localhost\"},\n\t\t\t},\n\t\t},\n\t)\n\tctx = testhelpers.WithDefaultIdentitySchema(ctx, \"file://stub/login.schema.json\")\n\n\ts, err := reg.AllLoginStrategies().Strategy(identity.CredentialsTypeWebAuthn)\n\trequire.NoError(t, err)\n\tfhAAL1, ok := s.(login.AAL1FormHydrator)\n\trequire.True(t, ok)\n\tfhAAL2, ok := s.(login.AAL2FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f *login.Flow) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.UI.Nodes.ResetNodes(\"csrf_token\")\n\t\tf.UI.Nodes.ResetNodes(\"identifier\")\n\t\tf.UI.Nodes.ResetNodes(\"webauthn_login_trigger\")\n\t\tsnapshotx.SnapshotT(t, f.UI.Nodes, snapshotx.ExceptNestedKeys(\"onclick\", \"nonce\", \"src\"))\n\t}\n\n\tnewFlowInternal := func(ctx context.Context, t *testing.T, identitySchema string) (*http.Request, *login.Flow) {\n\t\tquery := \"\"\n\t\tif identitySchema != \"\" {\n\t\t\tquery = \"?identity_schema=\" + identitySchema\n\t\t}\n\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/login/browser\"+query, nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := login.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *login.Flow) {\n\t\treturn newFlowInternal(ctx, t, \"\")\n\t}\n\tnewFlowWithIdentitySchema := func(ctx context.Context, t *testing.T, identitySchema string) (*http.Request, *login.Flow) {\n\t\treturn newFlowInternal(ctx, t, identitySchema)\n\t}\n\n\tpasswordlessEnabled := contextx.WithConfigValue(ctx, config.ViperKeyWebAuthnPasswordless, true)\n\tmfaEnabled := contextx.WithConfigValue(ctx, config.ViperKeyWebAuthnPasswordless, false)\n\n\tt.Run(\"method=PopulateLoginMethodSecondFactor\", func(t *testing.T) {\n\t\tid := createIdentity(ctx, t, reg)\n\t\theaders := testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\tt.Run(\"case=passwordless enabled\", func(t *testing.T) {\n\t\t\tr, f := newFlow(passwordlessEnabled, t)\n\n\t\t\tr.Header = headers\n\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\n\t\t\trequire.NoError(t, fhAAL2.PopulateLoginMethodSecondFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=mfa enabled\", func(t *testing.T) {\n\t\t\tr, f := newFlow(mfaEnabled, t)\n\n\t\t\tr.Header = headers\n\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\n\t\t\trequire.NoError(t, fhAAL2.PopulateLoginMethodSecondFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodFirstFactor\", func(t *testing.T) {\n\t\tt.Run(\"case=passwordless enabled\", func(t *testing.T) {\n\t\t\tr, f := newFlow(passwordlessEnabled, t)\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=mfa enabled\", func(t *testing.T) {\n\t\t\tr, f := newFlow(mfaEnabled, t)\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodRefresh\", func(t *testing.T) {\n\t\tt.Run(\"case=passwordless enabled but user has no passwordless credentials\", func(t *testing.T) {\n\t\t\tid := createIdentity(ctx, t, reg)\n\t\t\tr, f := newFlow(passwordlessEnabled, t)\n\t\t\tr.Header = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\t\tf.Refresh = true\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=passwordless enabled and user has passwordless credentials\", func(t *testing.T) {\n\t\t\tid, _ := createIdentityAndReturnIdentifier(ctx, t, reg, []byte(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\",\"is_passwordless\":true}]}`))\n\t\t\tr, f := newFlow(passwordlessEnabled, t)\n\t\t\tr.Header = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\t\tf.Refresh = true\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=mfa enabled and user has mfa credentials\", func(t *testing.T) {\n\t\t\tid := createIdentity(ctx, t, reg)\n\t\t\tr, f := newFlow(mfaEnabled, t)\n\t\t\tr.Header = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\t\tf.Refresh = true\n\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=mfa enabled but user has passwordless credentials\", func(t *testing.T) {\n\t\t\tid, _ := createIdentityAndReturnIdentifier(ctx, t, reg, []byte(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\",\"is_passwordless\":true}]}`))\n\t\t\tr, f := newFlow(mfaEnabled, t)\n\t\t\tr.Header = testhelpers.NewHTTPClientWithIdentitySessionToken(ctx, t, reg, id).Transport.(*testhelpers.TransportWithHeader).GetHeader()\n\t\t\tf.Refresh = true\n\t\t\tf.RequestedAAL = identity.AuthenticatorAssuranceLevel2\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactorRefresh(r, f, nil))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstCredentials\", func(t *testing.T) {\n\t\tt.Run(\"case=no options\", func(t *testing.T) {\n\t\t\tt.Run(\"case=passwordless enabled\", func(t *testing.T) {\n\t\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(passwordlessEnabled, config.ViperKeySecurityAccountEnumerationMitigate, false),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(passwordlessEnabled, config.ViperKeySecurityAccountEnumerationMitigate, true),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=mfa enabled\", func(t *testing.T) {\n\t\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(mfaEnabled, config.ViperKeySecurityAccountEnumerationMitigate, false),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(mfaEnabled, config.ViperKeySecurityAccountEnumerationMitigate, true),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=WithIdentifier\", func(t *testing.T) {\n\t\t\tt.Run(\"case=passwordless enabled\", func(t *testing.T) {\n\t\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(passwordlessEnabled, config.ViperKeySecurityAccountEnumerationMitigate, false),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(passwordlessEnabled, config.ViperKeySecurityAccountEnumerationMitigate, true),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=mfa enabled\", func(t *testing.T) {\n\t\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(mfaEnabled, config.ViperKeySecurityAccountEnumerationMitigate, false),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(\n\t\t\t\t\t\tcontextx.WithConfigValue(mfaEnabled, config.ViperKeySecurityAccountEnumerationMitigate, true),\n\t\t\t\t\t\tt,\n\t\t\t\t\t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentifier(\"foo@bar.com\")), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=WithIdentityHint\", func(t *testing.T) {\n\t\t\tt.Run(\"case=account enumeration mitigation enabled\", func(t *testing.T) {\n\t\t\t\tmfaEnabled := contextx.WithConfigValue(mfaEnabled, config.ViperKeySecurityAccountEnumerationMitigate, true)\n\t\t\t\tpasswordlessEnabled := contextx.WithConfigValue(passwordlessEnabled, config.ViperKeySecurityAccountEnumerationMitigate, true)\n\n\t\t\t\tid := identity.NewIdentity(\"test-provider\")\n\t\t\t\tt.Run(\"case=passwordless enabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(passwordlessEnabled, t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=mfa enabled\", func(t *testing.T) {\n\t\t\t\t\tr, f := newFlow(mfaEnabled, t)\n\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=account enumeration mitigation disabled\", func(t *testing.T) {\n\t\t\t\tmfaEnabled := contextx.WithConfigValue(mfaEnabled, config.ViperKeySecurityAccountEnumerationMitigate, false)\n\t\t\t\tpasswordlessEnabled := contextx.WithConfigValue(passwordlessEnabled, config.ViperKeySecurityAccountEnumerationMitigate, false)\n\n\t\t\t\tid, _ := createIdentityAndReturnIdentifier(ctx, t, reg, []byte(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\",\"is_passwordless\":true}]}`))\n\n\t\t\t\tt.Run(\"case=identity has webauthn\", func(t *testing.T) {\n\t\t\t\t\tt.Run(\"case=passwordless enabled\", func(t *testing.T) {\n\t\t\t\t\t\tr, f := newFlow(passwordlessEnabled, t)\n\t\t\t\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)))\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=mfa enabled\", func(t *testing.T) {\n\t\t\t\t\t\tr, f := newFlow(mfaEnabled, t)\n\t\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=identity does not have a webauthn\", func(t *testing.T) {\n\t\t\t\t\tt.Run(\"case=passwordless enabled\", func(t *testing.T) {\n\t\t\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\t\t\tr, f := newFlow(passwordlessEnabled, t)\n\t\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"case=mfa enabled\", func(t *testing.T) {\n\t\t\t\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\t\t\t\tr, f := newFlow(mfaEnabled, t)\n\t\t\t\t\t\trequire.ErrorIs(t, fhAAL1.PopulateLoginMethodIdentifierFirstCredentials(r, f, login.WithIdentityHint(id)), idfirst.ErrNoCredentialsFound)\n\t\t\t\t\t\ttoSnapshot(t, f)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\n\tt.Run(\"method=PopulateLoginMethodIdentifierFirstIdentification\", func(t *testing.T) {\n\t\tt.Run(\"case=passwordless enabled\", func(t *testing.T) {\n\t\t\tr, f := newFlow(passwordlessEnabled, t)\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodIdentifierFirstIdentification(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=mfa enabled\", func(t *testing.T) {\n\t\t\tr, f := newFlow(mfaEnabled, t)\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodIdentifierFirstIdentification(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\t})\n\n\tt.Run(\"case=Multi-Schema-method=PopulateLoginMethodFirstFactor\", func(t *testing.T) {\n\t\tmultiSchema := contextx.WithConfigValue(ctx, config.ViperKeyDefaultIdentitySchemaID, \"default\")\n\t\tmultiSchema = contextx.WithConfigValue(multiSchema, config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t{ID: \"default\", URL: \"file://./stub/missing-identifier.schema.json\"},\n\t\t\t{ID: \"not-default\", URL: \"file://./stub/login.schema.json\", SelfserviceSelectable: true},\n\t\t})\n\n\t\tt.Run(\"case=passwordless enabled\", func(t *testing.T) {\n\t\t\tr, f := newFlowWithIdentitySchema(contextx.WithConfigValue(multiSchema, config.ViperKeyWebAuthnPasswordless, true), t, \"not-default\")\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\n\t\tt.Run(\"case=mfa enabled\", func(t *testing.T) {\n\t\t\tr, f := newFlowWithIdentitySchema(contextx.WithConfigValue(multiSchema, config.ViperKeyWebAuthnPasswordless, false), t, \"not-default\")\n\t\t\trequire.NoError(t, fhAAL1.PopulateLoginMethodFirstFactor(r, f))\n\t\t\ttoSnapshot(t, f)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/nodes.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn\n\nimport (\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/webauthnx\"\n)\n\nfunc nodeWebauthnRegistrationOptions(opts []byte) *node.Node {\n\treturn webauthnx.NewWebAuthnConnectionTrigger(string(opts)).WithMetaLabel(text.NewInfoSelfServiceRegistrationRegisterWebAuthn())\n}\n\nfunc nodeDisplayName() *node.Node     { return webauthnx.NewWebAuthnConnectionName() }\nfunc nodeConnectionInput() *node.Node { return webauthnx.NewWebAuthnConnectionInput() }\n"
  },
  {
    "path": "selfservice/strategy/webauthn/registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/ui/container\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/webauthnx\"\n)\n\nvar _ registration.FormHydrator = new(Strategy)\n\n// Update Registration Flow with WebAuthn Method\n//\n// swagger:model updateRegistrationFlowWithWebAuthnMethod\ntype updateRegistrationFlowWithWebAuthnMethod struct {\n\t// Register a WebAuthn Security Key\n\t//\n\t// It is expected that the JSON returned by the WebAuthn registration process\n\t// is included here.\n\tRegister string `json:\"webauthn_register\"`\n\n\t// Name of the WebAuthn Security Key to be Added\n\t//\n\t// A human-readable name for the security key which will be added.\n\tRegisterDisplayName string `json:\"webauthn_register_displayname\"`\n\n\t// CSRFToken is the anti-CSRF token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// The identity's traits\n\t//\n\t// required: true\n\tTraits json.RawMessage `json:\"traits\"`\n\n\t// Method\n\t//\n\t// Should be set to \"webauthn\" when trying to add, update, or remove a webAuthn pairing.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Flow is flow ID.\n\t//\n\t// swagger:ignore\n\tFlow string `json:\"flow\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (s *Strategy) handleRegistrationError(r *http.Request, f *registration.Flow, p updateRegistrationFlowWithWebAuthnMethod, err error) error {\n\tif f != nil {\n\t\tfor _, n := range container.NewFromJSON(\"\", node.DefaultGroup, p.Traits, \"traits\").Nodes {\n\t\t\t// we only set the value and not the whole field because we want to keep types from the initial form generation\n\t\t\tf.UI.Nodes.SetValueAttribute(n.ID(), n.Attributes.GetValue())\n\t\t}\n\n\t\tf.UI.Nodes.SetValueAttribute(node.WebAuthnRegisterDisplayName, p.RegisterDisplayName)\n\n\t\tif f.Type == flow.TypeBrowser {\n\t\t\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (s *Strategy) decode(p *updateRegistrationFlowWithWebAuthnMethod, r *http.Request, ds *url.URL) error {\n\treturn registration.DecodeBody(p, r, registrationSchema, ds)\n}\n\nfunc (s *Strategy) Register(_ http.ResponseWriter, r *http.Request, regFlow *registration.Flow, i *identity.Identity) (err error) {\n\tctx, span := s.d.Tracer(r.Context()).Tracer().Start(r.Context(), \"selfservice.strategy.webauthn.Strategy.Register\")\n\tdefer otelx.End(span, &err)\n\n\tif regFlow.Type != flow.TypeBrowser || !s.d.Config().WebAuthnForPasswordless(ctx) {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"registration flow is not a browser flow or WebAuthn is not enabled\"))\n\t\treturn flow.ErrStrategyNotResponsible\n\t}\n\n\tds, err := regFlow.IdentitySchema.URL(ctx, s.d.Config())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar p updateRegistrationFlowWithWebAuthnMethod\n\tif err := s.decode(&p, r, ds); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, err)\n\t}\n\n\tregFlow.TransientPayload = p.TransientPayload\n\n\tif err := flow.EnsureCSRF(s.d, r, regFlow.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, err)\n\t}\n\n\tif len(p.Register) == 0 {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"register field is empty\"))\n\t\treturn flow.ErrStrategyNotResponsible\n\t}\n\n\tp.Method = s.SettingsStrategyID()\n\tif err := flow.MethodEnabledAndAllowed(ctx, regFlow.GetFlowName(), s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, err)\n\t}\n\n\tif len(p.Traits) == 0 {\n\t\tp.Traits = json.RawMessage(\"{}\")\n\t}\n\ti.Traits = identity.Traits(p.Traits)\n\n\twebAuthnSession := gjson.GetBytes(regFlow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData))\n\tif !webAuthnSession.IsObject() {\n\t\treturn s.handleRegistrationError(r, regFlow, p, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN in internal context to be an object.\")))\n\t}\n\n\tvar webAuthnSess webauthn.SessionData\n\tif err := json.Unmarshal([]byte(webAuthnSession.Raw), &webAuthnSess); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN in internal context to be an object but got: %s\", err)))\n\t}\n\n\twebAuthnResponse, err := protocol.ParseCredentialCreationResponseBody(strings.NewReader(p.Register))\n\tif err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, errors.WithStack(\n\t\t\therodot.ErrBadRequest.WithReasonf(\"Unable to parse WebAuthn response: %s\", err)))\n\t}\n\n\tweb, err := webauthn.New(s.d.Config().WebAuthnConfig(ctx))\n\tif err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Unable to get webAuthn config.\").WithDebug(err.Error())))\n\t}\n\n\tcredential, err := web.CreateCredential(webauthnx.NewUser(webAuthnSess.UserID, nil, web.Config), webAuthnSess, webAuthnResponse)\n\tif err != nil {\n\t\tif devErr := new(protocol.Error); errors.As(err, &devErr) {\n\t\t\ts.d.Logger().WithError(err).WithField(\"error_devinfo\", devErr.DevInfo).Error(\"Failed to create WebAuthn credential\")\n\t\t}\n\t\treturn s.handleRegistrationError(r, regFlow, p, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Unable to create WebAuthn credential: %s\", err)))\n\t}\n\n\tcredentialWebAuthn := identity.CredentialFromWebAuthn(credential, true)\n\tcredentialWebAuthn.DisplayName = p.RegisterDisplayName\n\tcredentialWebAuthnConfig, err := json.Marshal(identity.CredentialsWebAuthnConfig{\n\t\tCredentials: identity.CredentialsWebAuthn{*credentialWebAuthn},\n\t\tUserHandle:  webAuthnSess.UserID,\n\t})\n\tif err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, errors.WithStack(\n\t\t\therodot.ErrInternalServerError.WithReasonf(\"Unable to encode identity credentials.\").WithDebug(err.Error())))\n\t}\n\n\ti.UpsertCredentialsConfig(s.ID(), credentialWebAuthnConfig, 1)\n\tif err := s.validateCredentials(ctx, i); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, err)\n\t}\n\n\t// Remove the WebAuthn URL from the internal context now that it is set!\n\tregFlow.InternalContext, err = sjson.DeleteBytes(regFlow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData))\n\tif err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, err)\n\t}\n\n\tif err := s.d.RegistrationFlowPersister().UpdateRegistrationFlow(ctx, regFlow); err != nil {\n\t\treturn s.handleRegistrationError(r, regFlow, p, err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Strategy) injectWebauthnRegistrationOptions(r *http.Request, f *registration.Flow) ([]byte, error) {\n\tctx := r.Context()\n\tif options := gjson.GetBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeyWebauthnOptions)); options.IsObject() {\n\t\treturn []byte(options.Raw), nil\n\t}\n\n\tweb, err := webauthn.New(s.d.Config().WebAuthnConfig(ctx))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\twebauthID := x.NewUUID()\n\tuser := webauthnx.NewUser(webauthID[:], nil, s.d.Config().WebAuthnConfig(ctx))\n\toption, sessionData, err := web.BeginRegistration(user)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tinjectWebAuthnOptions, err := json.Marshal(option)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tf.InternalContext, err = sjson.SetBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData), sessionData)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\tf.InternalContext, err = sjson.SetRawBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeyWebauthnOptions), injectWebAuthnOptions)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn injectWebAuthnOptions, nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethod(r *http.Request, f *registration.Flow) error {\n\tctx := r.Context()\n\tif f.Type != flow.TypeBrowser || !s.d.Config().WebAuthnForPasswordless(ctx) {\n\t\treturn nil\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\topts, err := s.injectWebauthnRegistrationOptions(r, f)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tf.UI.Nodes.Upsert(nodeDisplayName())\n\tf.UI.Nodes.Upsert(nodeWebauthnRegistrationOptions(opts))\n\n\tf.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\tf.UI.Nodes.Upsert(nodeConnectionInput())\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodProfile(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\tctx := r.Context()\n\tif f.Type != flow.TypeBrowser || !s.d.Config().WebAuthnForPasswordless(ctx) {\n\t\treturn nil\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\topts, err := s.injectWebauthnRegistrationOptions(r, f)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tf.UI.Nodes.RemoveMatching(nodeDisplayName())\n\tf.UI.Nodes.RemoveMatching(nodeWebauthnRegistrationOptions(opts))\n\n\tf.UI.Nodes.RemoveMatching(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\tf.UI.Nodes.RemoveMatching(nodeConnectionInput())\n\treturn nil\n}\n\nfunc (s *Strategy) PopulateRegistrationMethodCredentials(r *http.Request, f *registration.Flow, options ...registration.FormHydratorModifier) error {\n\tctx := r.Context()\n\tif f.Type != flow.TypeBrowser || !s.d.Config().WebAuthnForPasswordless(ctx) {\n\t\treturn nil\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\topts, err := s.injectWebauthnRegistrationOptions(r, f)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tf.UI.Nodes.Upsert(nodeDisplayName())\n\tf.UI.Nodes.Upsert(nodeWebauthnRegistrationOptions(opts))\n\n\tf.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\tf.UI.Nodes.Upsert(nodeConnectionInput())\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/registration_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\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\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/pkg/registrationhelpers\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/strategy/webauthn\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\nvar (\n\tflows = []string{\"spa\", \"browser\"}\n\t//go:embed fixtures/registration/success/response.json\n\tregistrationFixtureSuccessResponse []byte\n\t//go:embed fixtures/registration/success/internal_context.json\n\tregistrationFixtureSuccessInternalContext []byte\n\t//go:embed fixtures/registration/failure/internal_context_wrong_user_id.json\n\tregistrationFixtureFailureInternalContextWrongUserID []byte\n)\n\nfunc flowToIsSPA(flow string) bool {\n\treturn flow == \"spa\"\n}\n\nfunc newRegistrationRegistry(t *testing.T, cfgOpts ...configx.OptionModifier) *driver.RegistryDefault {\n\t_, reg := pkg.NewFastRegistryWithMocks(t, append([]configx.OptionModifier{\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypePassword) + \".enabled\": true,\n\t\t\tconfig.ViperKeyWebAuthnPasswordless:                       true,\n\t\t\tconfig.ViperKeySelfServiceRegistrationLoginHints:          true,\n\t\t\tconfig.ViperKeySelfServiceRegistrationEnableLegacyOneStep: true,\n\t\t}),\n\t\tenabledWebauthn,\n\t}, cfgOpts...)...)\n\treturn reg\n}\n\nfunc TestRegistration(t *testing.T) {\n\treg := newRegistrationRegistry(t, configx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/registration.schema.json\")))\n\tconf := reg.Config()\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\t_ = testhelpers.NewErrorTestServer(t, reg)\n\t_ = testhelpers.NewRegistrationUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewRedirSessionEchoTS(t, reg)\n\n\tredirTS := testhelpers.NewRedirSessionEchoTS(t, reg)\n\tredirNoSessionTS := testhelpers.NewRedirNoSessionTS(t, reg)\n\n\t// set the \"return to\" server, which will assert the session state\n\t// (redirTS: enforce that a session exists, redirNoSessionTS: enforce that no session exists)\n\tuseReturnToFromTS := func(ts *httptest.Server) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceBrowserDefaultReturnTo, ts.URL+\"/default-return-to\")\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceRegistrationAfter+\".\"+config.DefaultBrowserReturnURL, ts.URL+\"/registration-return-ts\")\n\t}\n\tuseReturnToFromTS(redirTS)\n\n\t//checkURL := func(t *testing.T, shouldRedirect bool, res *http.Response) {\n\t//\tif shouldRedirect {\n\t//\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL+\"/registration-ts\")\n\t//\t} else {\n\t//\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+registration.RouteSubmitFlow)\n\t//\t}\n\t//}\n\n\tt.Run(\"AssertCommonErrorCases\", func(t *testing.T) {\n\t\tregistrationhelpers.AssertCommonErrorCases(t, flows)\n\t})\n\n\tt.Run(\"AssertRegistrationRespectsValidation\", func(t *testing.T) {\n\t\treg := newRegistrationRegistry(t)\n\t\tregistrationhelpers.AssertRegistrationRespectsValidation(t, reg, flows, func(v url.Values) {\n\t\t\tv.Del(\"traits.foobar\")\n\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t})\n\t})\n\n\tt.Run(\"AssertCSRFFailures\", func(t *testing.T) {\n\t\treg := newRegistrationRegistry(t)\n\t\tregistrationhelpers.AssertCSRFFailures(t, reg, flows, func(v url.Values) {\n\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t})\n\t})\n\n\tt.Run(\"AssertSchemaDoesNotExist\", func(t *testing.T) {\n\t\treg := newRegistrationRegistry(t)\n\t\tregistrationhelpers.AssertSchemaDoesNotExist(t, reg, flows, func(v url.Values) {\n\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t})\n\t})\n\n\tt.Run(\"case=webauthn button does not exist when passwordless is disabled\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeyWebAuthnPasswordless, false)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyWebAuthnPasswordless, true)\n\t\t})\n\t\tfor _, f := range flows {\n\t\t\tt.Run(f, func(t *testing.T) {\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, publicTS, flowToIsSPA(f), false, false)\n\t\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=webauthn button exists\", func(t *testing.T) {\n\t\tfor _, f := range flows {\n\t\t\tt.Run(f, func(t *testing.T) {\n\t\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, publicTS, flowToIsSPA(f), false, false)\n\t\t\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\t\t\"0.attributes.value\",\n\t\t\t\t\t\"5.attributes.onclick\",\n\t\t\t\t\t\"5.attributes.value\",\n\t\t\t\t\t\"6.attributes.nonce\",\n\t\t\t\t\t\"6.attributes.src\",\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should return an error because not passing validation\", func(t *testing.T) {\n\t\temail := testhelpers.RandomEmail()\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"traits.username\", email)\n\t\t\tv.Del(\"traits.foobar\")\n\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t}\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tactual := registrationhelpers.ExpectValidationError(t.Context(), t, publicTS, conf, f, values)\n\n\t\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), node.WebAuthnRegisterTrigger, \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0\").String(), `Property foobar is missing`, \"%s\", actual)\n\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should reject invalid transient payload\", func(t *testing.T) {\n\t\temail := testhelpers.RandomEmail()\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"traits.username\", email)\n\t\t\tv.Set(\"traits.foobar\", \"bar\")\n\t\t\tv.Set(\"transient_payload\", \"42\")\n\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t}\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tactual := registrationhelpers.ExpectValidationError(t.Context(), t, publicTS, conf, f, values)\n\n\t\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), node.WebAuthnRegisterTrigger, \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\tassert.Equal(t, \"bar\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Equal(t, int64(4000026), gjson.Get(actual, \"ui.nodes.#(attributes.name==transient_payload).messages.0.id\").Int(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should return an error because webauthn response is invalid\", func(t *testing.T) {\n\t\temail := testhelpers.RandomEmail()\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"traits.username\", email)\n\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\tv.Del(\"method\")\n\t\t}\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tactual := registrationhelpers.ExpectValidationError(t.Context(), t, publicTS, conf, f, values)\n\t\t\t\tassert.NotEmpty(t, gjson.Get(actual, \"id\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), node.WebAuthnRegisterTrigger, \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\tassert.Equal(t, \"bazbar\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).attributes.value\").String(), \"%s\", actual)\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.messages.0\").String(), `Unable to parse WebAuthn response: Parse error for Registration`, \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tsubmitWebAuthnRegistrationWithClient := func(t *testing.T, flow string, contextFixture []byte, client *http.Client, cb func(values url.Values), opts ...testhelpers.InitFlowWithOption) (string, *http.Response, *kratos.RegistrationFlow) {\n\t\tisSPA := flow == \"spa\"\n\t\tf := testhelpers.InitializeRegistrationFlowViaBrowser(t, client, publicTS, isSPA, false, false, opts...)\n\n\t\t// We inject the session to replay\n\t\tinterim, err := reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\trequire.NoError(t, err)\n\t\tinterim.InternalContext = contextFixture\n\t\trequire.NoError(t, reg.RegistrationFlowPersister().UpdateRegistrationFlow(context.Background(), interim))\n\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tcb(values)\n\n\t\t// We use the response replay\n\t\tbody, res := testhelpers.RegistrationMakeRequest(t, false, isSPA, f, client, values.Encode())\n\t\treturn body, res, f\n\t}\n\n\tt.Run(\"case=should return an error because internal context is invalid\", func(t *testing.T) {\n\t\temail := testhelpers.RandomEmail()\n\n\t\tfor _, tc := range []struct {\n\t\t\tname            string\n\t\t\tinternalContext string\n\t\t}{{\n\t\t\tname:            \"invalid json\",\n\t\t\tinternalContext: \"invalid\",\n\t\t}, {\n\t\t\tname:            \"wrong user ID\",\n\t\t\tinternalContext: string(registrationFixtureFailureInternalContextWrongUserID),\n\t\t}} {\n\t\t\ttc := tc\n\t\t\tt.Run(\"context=\"+tc.name, func(t *testing.T) {\n\t\t\t\tvalues := func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\t\t\tv.Set(node.WebAuthnRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\t\tv.Del(\"method\")\n\t\t\t\t}\n\n\t\t\t\tfor _, f := range flows {\n\t\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\t\tactual, _, _ := submitWebAuthnRegistrationWithClient(t, f,\n\t\t\t\t\t\t\t[]byte(tc.internalContext),\n\t\t\t\t\t\t\ttesthelpers.NewClientWithCookies(t),\n\t\t\t\t\t\t\tvalues,\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tif f == \"spa\" {\n\t\t\t\t\t\t\tassert.Equal(t, \"Internal Server Error\", gjson.Get(actual, \"error.status\").String(), \"%s\", actual)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tassert.Equal(t, \"Internal Server Error\", gjson.Get(actual, \"status\").String(), \"%s\", actual)\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\tt.Run(\"case=should fail to create identity if schema is missing the identifier\", func(t *testing.T) {\n\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/noid.schema.json\")\n\t\tt.Cleanup(func() {\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\t\t})\n\n\t\temail := testhelpers.RandomEmail()\n\n\t\tvalues := func(v url.Values) {\n\t\t\tv.Set(\"traits.email\", email)\n\t\t\tv.Set(node.WebAuthnRegister, string(registrationFixtureSuccessResponse))\n\t\t\tv.Del(\"method\")\n\t\t}\n\n\t\tfor _, f := range flows {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tactual, _, _ := submitWebAuthnRegistrationWithClient(t, f, registrationFixtureSuccessInternalContext, testhelpers.NewClientWithCookies(t), values)\n\n\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), node.WebAuthnRegisterTrigger, \"csrf_token\", \"traits.email\")\n\t\t\t\tassert.Equal(t, text.NewErrorValidationIdentifierMissing().Text, gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n\n\tmakeRegistration := func(t *testing.T, f string, values func(v url.Values), opts ...testhelpers.InitFlowWithOption) (actual string, res *http.Response, fetchedFlow *registration.Flow) {\n\t\tactual, res, actualFlow := submitWebAuthnRegistrationWithClient(t, f, registrationFixtureSuccessInternalContext, testhelpers.NewClientWithCookies(t), values, opts...)\n\t\tfetchedFlow, err := reg.RegistrationFlowPersister().GetRegistrationFlow(context.Background(), uuid.FromStringOrNil(actualFlow.Id))\n\t\trequire.NoError(t, err)\n\n\t\treturn actual, res, fetchedFlow\n\t}\n\n\tmakeSuccessfulRegistration := func(t *testing.T, f string, expectReturnTo string, values func(v url.Values), opts ...testhelpers.InitFlowWithOption) (actual string) {\n\t\tactual, res, fetchedFlow := makeRegistration(t, f, values, opts...)\n\t\tassert.Empty(t, gjson.GetBytes(fetchedFlow.InternalContext, flow.PrefixInternalContextKey(identity.CredentialsTypeWebAuthn, webauthn.InternalContextKeySessionData)), \"has cleaned up the internal context after success\")\n\t\tif f == \"spa\" {\n\t\t\texpectReturnTo = publicTS.URL\n\t\t}\n\t\tassert.Contains(t, res.Request.URL.String(), expectReturnTo, \"%+v\\n\\t%s\", res.Request, assertx.PrettifyJSONPayload(t, actual))\n\t\treturn actual\n\t}\n\n\tgetPrefix := func(f string) (prefix string) {\n\t\tif f == \"spa\" {\n\t\t\tprefix = \"session.\"\n\t\t}\n\t\treturn\n\t}\n\n\tt.Run(\"successful registration\", func(t *testing.T) {\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeWebAuthn.String()), nil)\n\t\t})\n\n\t\tvalues := func(email string) func(v url.Values) {\n\t\t\treturn func(v url.Values) {\n\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\t\tv.Set(node.WebAuthnRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\tv.Del(\"method\")\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"case=should create the identity but not a session\", func(t *testing.T) {\n\t\t\tuseReturnToFromTS(redirNoSessionTS)\n\t\t\tt.Cleanup(func() {\n\t\t\t\tuseReturnToFromTS(redirTS)\n\t\t\t})\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\n\t\t\tfor _, f := range flows {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tactual := makeSuccessfulRegistration(t, f, redirNoSessionTS.URL+\"/registration-return-ts\", values(email))\n\n\t\t\t\t\tif f == \"spa\" {\n\t\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.False(t, gjson.Get(actual, \"session\").Exists(), \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, \"null\\n\", actual, \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t}\n\n\t\t\t\t\ti, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeWebAuthn, email)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\n\t\t\t\t\tif f == \"spa\" {\n\t\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(actual, \"continue_with.0.action\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"continue_with.0.redirect_browser_to\").String(), redirNoSessionTS.URL+\"/registration-return-ts\", \"%s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Empty(t, gjson.Get(actual, \"continue_with\").Array(), \"%s\", actual)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should accept valid transient payload\", func(t *testing.T) {\n\t\t\tuseReturnToFromTS(redirNoSessionTS)\n\t\t\tt.Cleanup(func() {\n\t\t\t\tuseReturnToFromTS(redirTS)\n\t\t\t})\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypePassword.String()), nil)\n\n\t\t\tfor _, f := range flows {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tactual := makeSuccessfulRegistration(t, f, redirNoSessionTS.URL+\"/registration-return-ts\", func(v url.Values) {\n\t\t\t\t\t\tvalues(email)(v)\n\t\t\t\t\t\tv.Set(\"transient_payload.stuff\", \"42\")\n\t\t\t\t\t})\n\n\t\t\t\t\tif f == \"spa\" {\n\t\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, \"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\t\tassert.False(t, gjson.Get(actual, \"session\").Exists(), \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, \"null\\n\", actual, \"because the registration yielded no session, the user is not expected to be signed in: %s\", actual)\n\t\t\t\t\t}\n\n\t\t\t\t\ti, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeWebAuthn, email)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=should create the identity and a session and use the correct schema\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeWebAuthn.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"advanced-user\")\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t\t{ID: \"does-not-exist\", URL: \"file://./stub/profile.schema.json\"},\n\t\t\t\t{ID: \"advanced-user\", URL: \"file://./stub/registration.schema.json\"},\n\t\t\t})\n\n\t\t\tfor _, f := range flows {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tactual := makeSuccessfulRegistration(t, f, redirTS.URL+\"/registration-return-ts\", values(email))\n\n\t\t\t\t\tprefix := getPrefix(f)\n\n\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, prefix+\"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\tassert.True(t, gjson.Get(actual, prefix+\"active\").Bool(), \"%s\", actual)\n\n\t\t\t\t\ti, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeWebAuthn, email)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=not able to create the same account twice\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeWebAuthn.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\n\t\t\tfor _, f := range flows {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tactual := makeSuccessfulRegistration(t, f, redirTS.URL+\"/registration-return-ts\", values(email))\n\t\t\t\t\tassert.True(t, gjson.Get(actual, getPrefix(f)+\"active\").Bool(), \"%s\", actual)\n\n\t\t\t\t\tactual, _, _ = makeRegistration(t, f, values(email))\n\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.action\").String(), publicTS.URL+registration.RouteSubmitFlow, \"%s\", actual)\n\t\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), node.WebAuthnRegisterTrigger, \"csrf_token\", \"traits.username\")\n\t\t\t\t\tassert.Equal(t, \"You tried signing in with \"+email+\" which is already in use by another account. You can sign in using your passkey or a security key.\", gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=reset previous form errors\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeWebAuthn.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://./stub/registration.schema.json\")\n\n\t\t\tfor _, f := range flows {\n\t\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tactual, _, _ := makeRegistration(t, f, func(v url.Values) {\n\t\t\t\t\t\tv.Del(\"traits.username\")\n\t\t\t\t\t\tv.Set(\"traits.foobar\", \"bazbar\")\n\t\t\t\t\t\tv.Set(node.WebAuthnRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\t\t\tv.Del(\"method\")\n\t\t\t\t\t})\n\t\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), node.WebAuthnRegisterTrigger, \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).messages.0\").String(), `Property username is missing`, \"%s\", actual)\n\n\t\t\t\t\tactual, _, _ = makeRegistration(t, f, func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"traits.username\", email)\n\t\t\t\t\t\tv.Del(\"traits.foobar\")\n\t\t\t\t\t\tv.Set(node.WebAuthnRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\t\t\tv.Del(\"method\")\n\t\t\t\t\t})\n\t\t\t\t\tregistrationhelpers.CheckFormContent(t, []byte(actual), node.WebAuthnRegisterTrigger, \"csrf_token\", \"traits.username\", \"traits.foobar\")\n\t\t\t\t\tassert.Contains(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0\").String(), `Property foobar is missing`, \"%s\", actual)\n\t\t\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.username).messages\").Array())\n\t\t\t\t\tassert.Empty(t, gjson.Get(actual, \"ui.nodes.messages\").Array())\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=multi-schema should create the identity and a session and use the correct schema\", func(t *testing.T) {\n\t\t\tconf.MustSet(t.Context(), config.HookStrategyKey(config.ViperKeySelfServiceRegistrationAfter, identity.CredentialsTypeWebAuthn.String()), []config.SelfServiceHook{{Name: \"session\"}})\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyDefaultIdentitySchemaID, \"does-not-exist\")\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyIdentitySchemas, config.Schemas{\n\t\t\t\t{ID: \"does-not-exist\", URL: \"file://./stub/profile.schema.json\"},\n\t\t\t\t{ID: \"advanced-user\", URL: \"file://./stub/registration.schema.json\", SelfserviceSelectable: true},\n\t\t\t})\n\n\t\t\tfor _, f := range flows {\n\t\t\t\tt.Run(\"type=\"+f+\" registration success\", func(t *testing.T) {\n\t\t\t\t\temail := testhelpers.RandomEmail()\n\t\t\t\t\tactual := makeSuccessfulRegistration(t, f, redirTS.URL+\"/registration-return-ts\", values(email), testhelpers.InitFlowWithIdentitySchema(\"advanced-user\"))\n\n\t\t\t\t\tprefix := getPrefix(f)\n\n\t\t\t\t\tassert.Equal(t, email, gjson.Get(actual, prefix+\"identity.traits.username\").String(), \"%s\", actual)\n\t\t\t\t\tassert.True(t, gjson.Get(actual, prefix+\"active\").Bool(), \"%s\", actual)\n\n\t\t\t\t\ti, _, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeWebAuthn, email)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, email, gjson.GetBytes(i.Traits, \"username\").String(), \"%s\", actual)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"type=\"+f+\" registration failure due to invalid form data\", func(t *testing.T) {\n\t\t\t\t\tinvalidValues := func(v url.Values) {\n\t\t\t\t\t\tv.Set(\"traits.username\", testhelpers.RandomEmail())\n\t\t\t\t\t\tv.Set(\"traits.foobar\", \"b\")\n\t\t\t\t\t\tv.Set(node.WebAuthnRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\t\t\tv.Del(\"method\")\n\t\t\t\t\t}\n\n\t\t\t\t\tactual, res, _ := submitWebAuthnRegistrationWithClient(t, f, registrationFixtureSuccessInternalContext, testhelpers.NewClientWithCookies(t), invalidValues, testhelpers.InitFlowWithIdentitySchema(\"advanced-user\"))\n\n\t\t\t\t\tif f == \"browser\" {\n\t\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode, \"%s\", actual)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode, \"%s\", actual)\n\t\t\t\t\t}\n\t\t\t\t\tassert.Equal(t, int64(4000003), gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0.id\").Int(), \"%s\", actual)\n\t\t\t\t\tassert.Equal(t, \"length must be \\u003e= 2, but got 1\", gjson.Get(actual, \"ui.nodes.#(attributes.name==traits.foobar).messages.0.text\").String(), \"%s\", actual)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail if no identifier was set in the schema\", func(t *testing.T) {\n\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://stub/missing-identifier.schema.json\")\n\n\t\tfor _, f := range []string{\"spa\", \"api\", \"browser\"} {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tactual, _, _ := makeRegistration(t, f, func(v url.Values) {\n\t\t\t\t\tv.Set(\"traits.email\", testhelpers.RandomEmail())\n\t\t\t\t\tv.Set(node.WebAuthnRegister, string(registrationFixtureSuccessResponse))\n\t\t\t\t\tv.Del(\"method\")\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, text.NewErrorValidationIdentifierMissing().Text, gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestPopulateRegistrationMethod(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\tctx = testhelpers.WithDefaultIdentitySchema(ctx, \"file://stub/registration.schema.json\")\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeyWebAuthnRPID, \"localhost\")\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeyWebAuthnRPDisplayName, \"localhost\")\n\tctx = contextx.WithConfigValue(ctx, config.ViperKeyWebAuthnPasswordless, true)\n\n\ts, err := reg.AllRegistrationStrategies().Strategy(identity.CredentialsTypeWebAuthn)\n\trequire.NoError(t, err)\n\n\tfh, ok := s.(registration.FormHydrator)\n\trequire.True(t, ok)\n\n\ttoSnapshot := func(t *testing.T, f node.Nodes, except ...snapshotx.Opt) {\n\t\tt.Helper()\n\t\t// The CSRF token has a unique value that messes with the snapshot - ignore it.\n\t\tf.ResetNodes(\"csrf_token\")\n\t\tsnapshotx.SnapshotT(t, f, append(except, snapshotx.ExceptNestedKeys(\"nonce\", \"src\", \"onclick\"))...)\n\t}\n\n\tnewFlow := func(ctx context.Context, t *testing.T) (*http.Request, *registration.Flow) {\n\t\tr := httptest.NewRequest(\"GET\", \"/self-service/registration/browser\", nil)\n\t\tr = r.WithContext(ctx)\n\t\tt.Helper()\n\t\tf, err := registration.NewFlow(conf, time.Minute, \"csrf_token\", r, flow.TypeBrowser)\n\t\tf.UI.Nodes = make(node.Nodes, 0)\n\t\trequire.NoError(t, err)\n\t\treturn r, f\n\t}\n\n\tt.Run(\"method=PopulateRegistrationMethod\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethod(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"2.attributes.value\"))\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodProfile\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes)\n\t})\n\n\tt.Run(\"method=PopulateRegistrationMethodCredentials\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"2.attributes.value\"))\n\t})\n\n\tt.Run(\"method=idempotency\", func(t *testing.T) {\n\t\tr, f := newFlow(ctx, t)\n\n\t\tvar snapshots []node.Nodes\n\n\t\tt.Run(\"case=1\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=2\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"2.attributes.value\"))\n\t\t})\n\n\t\tt.Run(\"case=3\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodProfile(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes)\n\t\t})\n\n\t\tt.Run(\"case=4\", func(t *testing.T) {\n\t\t\trequire.NoError(t, fh.PopulateRegistrationMethodCredentials(r, f))\n\t\t\tsnapshots = append(snapshots, f.UI.Nodes)\n\t\t\ttoSnapshot(t, f.UI.Nodes, snapshotx.ExceptPaths(\"2.attributes.value\"))\n\t\t})\n\n\t\tt.Run(\"case=evaluate\", func(t *testing.T) {\n\t\t\tassertx.EqualAsJSON(t, snapshots[0], snapshots[2])\n\t\t\tassertx.EqualAsJSONExcept(t, snapshots[1], snapshots[3], []string{\"3.attributes.nonce\"})\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/schema.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed .schema/login.schema.json\nvar loginSchema []byte\n\n//go:embed .schema/settings.schema.json\nvar settingsSchema []byte\n\n//go:embed .schema/registration.schema.json\nvar registrationSchema []byte\n"
  },
  {
    "path": "selfservice/strategy/webauthn/settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/webauthnx\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/tidwall/gjson\"\n\t\"github.com/tidwall/sjson\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/session\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/x/decoderx\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc (s *Strategy) SettingsStrategyID() string {\n\treturn identity.CredentialsTypeWebAuthn.String()\n}\n\nconst (\n\tInternalContextKeySessionData     = \"session_data\"\n\tInternalContextKeyWebauthnOptions = \"session_options\"\n)\n\n// Update Settings Flow with WebAuthn Method\n//\n// swagger:model updateSettingsFlowWithWebAuthnMethod\ntype updateSettingsFlowWithWebAuthnMethod struct {\n\t// Register a WebAuthn Security Key\n\t//\n\t// It is expected that the JSON returned by the WebAuthn registration process\n\t// is included here.\n\tRegister string `json:\"webauthn_register\"`\n\n\t// Name of the WebAuthn Security Key to be Added\n\t//\n\t// A human-readable name for the security key which will be added.\n\tRegisterDisplayName string `json:\"webauthn_register_displayname\"`\n\n\t// Remove a WebAuthn Security Key\n\t//\n\t// This must contain the ID of the WebAuthN connection.\n\tRemove string `json:\"webauthn_remove\"`\n\n\t// CSRFToken is the anti-CSRF token\n\tCSRFToken string `json:\"csrf_token\"`\n\n\t// Method\n\t//\n\t// Should be set to \"webauthn\" when trying to add, update, or remove a webAuthn pairing.\n\t//\n\t// required: true\n\tMethod string `json:\"method\"`\n\n\t// Flow is flow ID.\n\t//\n\t// swagger:ignore\n\tFlow string `json:\"flow\"`\n\n\t// Transient data to pass along to any webhooks\n\t//\n\t// required: false\n\tTransientPayload json.RawMessage `json:\"transient_payload,omitempty\" form:\"transient_payload\"`\n}\n\nfunc (p *updateSettingsFlowWithWebAuthnMethod) GetFlowID() uuid.UUID {\n\treturn x.ParseUUID(p.Flow)\n}\n\nfunc (p *updateSettingsFlowWithWebAuthnMethod) SetFlowID(rid uuid.UUID) {\n\tp.Flow = rid.String()\n}\n\nfunc (s *Strategy) Settings(ctx context.Context, w http.ResponseWriter, r *http.Request, f *settings.Flow, ss *session.Session) (_ *settings.UpdateContext, err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.webauthn.Strategy.Settings\")\n\tdefer otelx.End(span, &err)\n\n\tif f.Type != flow.TypeBrowser {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"flow is not a browser flow\"))\n\t\treturn nil, flow.ErrStrategyNotResponsible\n\t}\n\tvar p updateSettingsFlowWithWebAuthnMethod\n\tctxUpdate, err := settings.PrepareUpdate(s.d, w, r, f, ss, settings.ContinuityKey(s.SettingsStrategyID()), &p)\n\tif errors.Is(err, settings.ErrContinuePreviousAction) {\n\t\treturn ctxUpdate, s.continueSettingsFlow(ctx, w, r, ctxUpdate, p)\n\t} else if err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif err := s.decodeSettingsFlow(r, &p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\tif len(p.Register)+len(p.Remove) > 0 {\n\t\t// This method has only two submit buttons\n\t\tp.Method = s.SettingsStrategyID()\n\t\tif err := flow.MethodEnabledAndAllowed(ctx, f.GetFlowName(), s.SettingsStrategyID(), p.Method, s.d); err != nil {\n\t\t\treturn nil, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t\t}\n\t} else {\n\t\tspan.SetAttributes(attribute.String(\"not_responsible_reason\", \"neither register nor remove is set\"))\n\t\treturn nil, errors.WithStack(flow.ErrStrategyNotResponsible)\n\t}\n\n\t// This does not come from the payload!\n\tp.Flow = ctxUpdate.Flow.ID.String()\n\tif err := s.continueSettingsFlow(ctx, w, r, ctxUpdate, p); err != nil {\n\t\treturn ctxUpdate, s.handleSettingsError(ctx, w, r, ctxUpdate, p, err)\n\t}\n\n\treturn ctxUpdate, nil\n}\n\nfunc (s *Strategy) decodeSettingsFlow(r *http.Request, dest interface{}) error {\n\tcompiler, err := decoderx.HTTPRawJSONSchemaCompiler(settingsSchema)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\treturn decoderx.Decode(r, dest, compiler,\n\t\tdecoderx.HTTPKeepRequestBody(true),\n\t\tdecoderx.HTTPDecoderAllowedMethods(\"POST\", \"GET\"),\n\t\tdecoderx.HTTPDecoderSetValidatePayloads(true),\n\t\tdecoderx.HTTPDecoderJSONFollowsFormFormat(),\n\t)\n}\n\nfunc (s *Strategy) continueSettingsFlow(\n\tctx context.Context,\n\tw http.ResponseWriter, r *http.Request,\n\tctxUpdate *settings.UpdateContext, p updateSettingsFlowWithWebAuthnMethod,\n) error {\n\tif len(p.Register+p.Remove) > 0 {\n\t\tif err := flow.MethodEnabledAndAllowed(ctx, flow.SettingsFlow, s.SettingsStrategyID(), s.SettingsStrategyID(), s.d); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := flow.EnsureCSRF(s.d, r, ctxUpdate.Flow.Type, s.d.Config().DisableAPIFlowEnforcement(ctx), s.d.GenerateCSRFToken, p.CSRFToken); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif ctxUpdate.Session.AuthenticatedAt.Add(s.d.Config().SelfServiceFlowSettingsPrivilegedSessionMaxAge(ctx)).Before(time.Now()) {\n\t\t\treturn errors.WithStack(settings.NewFlowNeedsReAuth())\n\t\t}\n\t} else {\n\t\treturn errors.New(\"ended up in unexpected state\")\n\t}\n\n\tif len(p.Register) > 0 {\n\t\treturn s.continueSettingsFlowAdd(ctx, ctxUpdate, p)\n\t} else if len(p.Remove) > 0 {\n\t\treturn s.continueSettingsFlowRemove(ctx, w, r, ctxUpdate, p)\n\t}\n\n\treturn errors.New(\"ended up in unexpected state\")\n}\n\nfunc (s *Strategy) continueSettingsFlowRemove(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithWebAuthnMethod) error {\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.IdentityID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcred, ok := i.GetCredentials(s.ID())\n\tif !ok {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"You tried to remove a WebAuthn but you have no WebAuthn set up.\"))\n\t}\n\n\tvar cc identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(cred.Config, &cc); err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\tvar wasPasswordless bool\n\tupdated := make([]identity.CredentialWebAuthn, 0)\n\tfor k, cred := range cc.Credentials {\n\t\tif fmt.Sprintf(\"%x\", cred.ID) != p.Remove {\n\t\t\tupdated = append(updated, cc.Credentials[k])\n\t\t} else if cred.IsPasswordless {\n\t\t\twasPasswordless = true\n\t\t}\n\t}\n\n\tif len(updated) == len(cc.Credentials) {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"You tried to remove a WebAuthn credential which does not exist.\"))\n\t}\n\n\tcount, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, i)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif count < 2 && wasPasswordless {\n\t\treturn s.handleSettingsError(ctx, w, r, ctxUpdate, p, errors.WithStack(webauthnx.ErrNotEnoughCredentials))\n\t}\n\n\tif len(updated) == 0 {\n\t\ti.DeleteCredentialsType(identity.CredentialsTypeWebAuthn)\n\t\tctxUpdate.UpdateIdentity(i)\n\t\treturn nil\n\t}\n\n\tcc.Credentials = updated\n\tcred.Config, err = json.Marshal(cc)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\ti.SetCredentials(s.ID(), *cred)\n\tctxUpdate.UpdateIdentity(i)\n\treturn nil\n}\n\nfunc (s *Strategy) continueSettingsFlowAdd(ctx context.Context, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithWebAuthnMethod) error {\n\twebAuthnSession := gjson.GetBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData))\n\tif !webAuthnSession.IsObject() {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN in internal context to be an object.\"))\n\t}\n\n\tvar webAuthnSess webauthn.SessionData\n\tif err := json.Unmarshal([]byte(gjson.GetBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData)).Raw), &webAuthnSess); err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Expected WebAuthN in internal context to be an object but got: %s\", err))\n\t}\n\n\twebAuthnResponse, err := protocol.ParseCredentialCreationResponseBody(strings.NewReader(p.Register))\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unable to parse WebAuthn response: %s\", err))\n\t}\n\n\tweb, err := webauthn.New(s.d.Config().WebAuthnConfig(ctx))\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to get webAuthn config.\").WithDebug(err.Error()))\n\t}\n\n\tcredential, err := web.CreateCredential(webauthnx.NewUser(ctxUpdate.Session.IdentityID[:], nil, web.Config), webAuthnSess, webAuthnResponse)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to create WebAuthn credential: %s\", err))\n\t}\n\n\ti, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(ctx, ctxUpdate.Session.IdentityID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcred := i.GetCredentialsOr(s.ID(), &identity.Credentials{Config: sqlxx.JSONRawMessage(\"{}\")})\n\n\tvar cc identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(cred.Config, &cc); err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to decode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\twc := identity.CredentialFromWebAuthn(credential, s.d.Config().WebAuthnForPasswordless(ctx))\n\twc.AddedAt = time.Now().UTC().Round(time.Second)\n\twc.DisplayName = p.RegisterDisplayName\n\twc.IsPasswordless = s.d.Config().WebAuthnForPasswordless(ctx)\n\tcc.UserHandle = ctxUpdate.Session.IdentityID[:]\n\n\tcc.Credentials = append(cc.Credentials, *wc)\n\tco, err := json.Marshal(cc)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithReasonf(\"Unable to encode identity credentials.\").WithDebug(err.Error()))\n\t}\n\n\ti.UpsertCredentialsConfig(s.ID(), co, 1)\n\tif err := s.validateCredentials(ctx, i); err != nil {\n\t\treturn err\n\t}\n\n\t// Remove the WebAuthn URL from the internal context now that it is set!\n\tctxUpdate.Flow.InternalContext, err = sjson.DeleteBytes(ctxUpdate.Flow.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.d.SettingsFlowPersister().UpdateSettingsFlow(ctx, ctxUpdate.Flow); err != nil {\n\t\treturn err\n\t}\n\n\taal := identity.AuthenticatorAssuranceLevel1\n\tif !s.d.Config().WebAuthnForPasswordless(ctx) {\n\t\taal = identity.AuthenticatorAssuranceLevel2\n\t}\n\n\t// Since we added the method, it also means that we have authenticated it\n\tif err := s.d.SessionManager().SessionAddAuthenticationMethods(ctx, ctxUpdate.Session.ID, session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    aal,\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\tctxUpdate.UpdateIdentity(i)\n\treturn nil\n}\n\nfunc (s *Strategy) identityListWebAuthn(id *identity.Identity) (*identity.CredentialsWebAuthnConfig, error) {\n\tcred, ok := id.GetCredentials(s.ID())\n\tif !ok {\n\t\treturn nil, errors.WithStack(sqlcon.ErrNoRows)\n\t}\n\n\tvar cc identity.CredentialsWebAuthnConfig\n\tif err := json.Unmarshal(cred.Config, &cc); err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\n\treturn &cc, nil\n}\n\nfunc (s *Strategy) PopulateSettingsMethod(ctx context.Context, r *http.Request, id *identity.Identity, f *settings.Flow) (err error) {\n\tctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, \"selfservice.strategy.webauthn.Strategy.PopulateSettingsMethod\")\n\tdefer otelx.End(span, &err)\n\n\tif f.Type != flow.TypeBrowser {\n\t\treturn nil\n\t}\n\n\tf.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\tcount, err := s.d.IdentityManager().CountActiveFirstFactorCredentials(ctx, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif webAuthns, err := s.identityListWebAuthn(id); errors.Is(err, sqlcon.ErrNoRows) {\n\t\t// Do nothing\n\t} else if err != nil {\n\t\treturn err\n\t} else {\n\t\tfor k := range webAuthns.Credentials {\n\t\t\t// We only show the option to remove a credential, if it is not the last one when passwordless,\n\t\t\t// or, if it is for MFA we show it always.\n\t\t\tcred := &webAuthns.Credentials[k]\n\t\t\tf.UI.Nodes.Append(webauthnx.NewWebAuthnUnlink(cred, func(a *node.InputAttributes) {\n\t\t\t\t// Do not remove this node because it is the last credential the identity can sign in with.\n\t\t\t\ta.Disabled = cred.IsPasswordless && count < 2\n\t\t\t}))\n\t\t}\n\t}\n\n\tweb, err := webauthn.New(s.d.Config().WebAuthnConfig(ctx))\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\toption, sessionData, err := web.BeginRegistration(webauthnx.NewUser(id.ID.Bytes(), nil, web.Config))\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tf.InternalContext, err = sjson.SetBytes(f.InternalContext, flow.PrefixInternalContextKey(s.ID(), InternalContextKeySessionData), sessionData)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tinjectWebAuthnOptions, err := json.Marshal(option)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tf.UI.Nodes.Upsert(webauthnx.NewWebAuthnScript(s.d.Config().SelfPublicURL(ctx)))\n\tf.UI.Nodes.Upsert(webauthnx.NewWebAuthnConnectionName())\n\tf.UI.Nodes.Upsert(webauthnx.NewWebAuthnConnectionTrigger(string(injectWebAuthnOptions)).\n\t\tWithMetaLabel(text.NewInfoSelfServiceSettingsRegisterWebAuthn()))\n\tf.UI.Nodes.Upsert(webauthnx.NewWebAuthnConnectionInput())\n\treturn nil\n}\n\nfunc (s *Strategy) handleSettingsError(ctx context.Context, w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p updateSettingsFlowWithWebAuthnMethod, err error) error {\n\t// Do not pause flow if the flow type is an API flow as we can't save cookies in those flows.\n\tif e := new(settings.FlowNeedsReAuth); errors.As(err, &e) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser {\n\t\tif err := s.d.ContinuityManager().Pause(ctx, w, r, settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.GetSessionIdentity())...); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif ctxUpdate.Flow != nil {\n\t\tctxUpdate.Flow.UI.ResetMessages()\n\t\tctxUpdate.Flow.UI.SetCSRF(s.d.GenerateCSRFToken(r))\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/settings_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/configx\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\n\t\"github.com/ory/x/snapshotx\"\n\n\t\"github.com/ory/kratos/selfservice/strategy/webauthn\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/tidwall/gjson\"\n\n\tkratos \"github.com/ory/kratos/pkg/httpclient\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/kratos/driver\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/x\"\n)\n\n//go:embed fixtures/settings/success/identity.json\nvar settingsFixtureSuccessIdentity []byte\n\n//go:embed fixtures/settings/success/response.json\nvar settingsFixtureSuccessResponse []byte\n\n//go:embed fixtures/settings/success/internal_context.json\nvar settingsFixtureSuccessInternalContext []byte\n\nconst registerDisplayNameGJSONQuery = \"ui.nodes.#(attributes.name==\" + node.WebAuthnRegisterDisplayName + \")\"\n\nfunc createIdentityWithoutWebAuthn(t *testing.T, reg driver.Registry) *identity.Identity {\n\tid := createIdentity(t.Context(), t, reg)\n\tdelete(id.Credentials, identity.CredentialsTypeWebAuthn)\n\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(context.Background(), id))\n\treturn id\n}\n\nfunc createIdentityAndReturnIdentifier(ctx context.Context, t *testing.T, reg driver.Registry, conf []byte) (*identity.Identity, string) {\n\tidentifier := x.NewUUID().String() + \"@ory.sh\"\n\tpassword := x.NewUUID().String()\n\tp, err := reg.Hasher(ctx).Generate(ctx, []byte(password))\n\trequire.NoError(t, err)\n\ti := &identity.Identity{\n\t\tSchemaID: \"default\",\n\t\tNID:      uuid.Must(uuid.NewV4()),\n\t\tTraits:   identity.Traits(fmt.Sprintf(`{\"subject\":\"%s\"}`, identifier)),\n\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t{\n\t\t\t\tValue:     identifier,\n\t\t\t\tVerified:  false,\n\t\t\t\tCreatedAt: time.Now(),\n\t\t\t},\n\t\t},\n\t}\n\tif conf == nil {\n\t\tconf = []byte(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\"},{\"id\":\"YmFyYmFy\",\"display_name\":\"bar\"}]}`)\n\t}\n\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(ctx, i))\n\ti.Credentials = map[identity.CredentialsType]identity.Credentials{\n\t\tidentity.CredentialsTypePassword: {\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{identifier},\n\t\t\tConfig:      sqlxx.JSONRawMessage(`{\"hashed_password\":\"` + string(p) + `\"}`),\n\t\t},\n\t\tidentity.CredentialsTypeWebAuthn: {\n\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\tIdentifiers: []string{identifier},\n\t\t\tConfig:      conf,\n\t\t},\n\t}\n\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(ctx, i))\n\treturn i, identifier\n}\n\nfunc createIdentity(ctx context.Context, t *testing.T, reg driver.Registry) *identity.Identity {\n\tid, _ := createIdentityAndReturnIdentifier(ctx, t, reg, nil)\n\treturn id\n}\n\nvar enabledWebauthn = func() configx.OptionModifier {\n\twebauthn := config.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypeWebAuthn)\n\treturn configx.WithValues(map[string]any{\n\t\twebauthn + \".enabled\":                true,\n\t\twebauthn + \".config.rp.display_name\": \"Ory Corp\",\n\t\twebauthn + \".config.rp.id\":           \"localhost\",\n\t\twebauthn + \".config.rp.origin\":       \"http://localhost:4455\",\n\t})\n}()\n\nfunc ensureReplacement(t *testing.T, index string, ui kratos.UiContainer, expected string) {\n\tactual, err := json.Marshal(ui.Nodes)\n\trequire.NoError(t, err)\n\tassert.Contains(t, gjson.GetBytes(actual, index+\".attributes.onclick\").String(), expected, \"ensure that the replacement works\")\n}\n\nfunc TestCompleteSettings(t *testing.T) {\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(map[string]any{\n\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".\" + string(identity.CredentialsTypePassword) + \".enabled\": false,\n\t\t\tconfig.ViperKeySelfServiceStrategyConfig + \".profile.enabled\":                                          false,\n\t\t\tconfig.ViperKeySelfServiceSettingsRequiredAAL:                                                          \"aal1\",\n\t\t\tconfig.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter:                                        \"1m\",\n\t\t}),\n\t\tenabledWebauthn,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/settings.schema.json\")),\n\t)\n\n\tpublicTS, _ := testhelpers.NewKratosServer(t, reg)\n\n\terrTS := testhelpers.NewErrorTestServer(t, reg)\n\tuiTS := testhelpers.NewSettingsUIFlowEchoServer(t, reg)\n\t_ = testhelpers.NewRedirSessionEchoTS(t, reg)\n\tloginTS := testhelpers.NewLoginUIFlowEchoServer(t, reg)\n\n\tt.Run(\"case=a device is shown which can be unlinked\", func(t *testing.T) {\n\t\tid := createIdentity(t.Context(), t, reg)\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, apiClient, true, publicTS)\n\n\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\"0.attributes.value\",\n\t\t\t\"5.attributes.onclick\",\n\t\t\t\"5.attributes.value\",\n\t\t\t\"6.attributes.src\",\n\t\t\t\"6.attributes.nonce\",\n\t\t})\n\t\tensureReplacement(t, \"5\", f.Ui, \"Ory Corp\")\n\t})\n\n\tt.Run(\"case=one activation element is shown\", func(t *testing.T) {\n\t\tid := createIdentityWithoutWebAuthn(t, reg)\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(t.Context(), id))\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, apiClient, true, publicTS)\n\n\t\ttesthelpers.SnapshotTExcept(t, f.Ui.Nodes, []string{\n\t\t\t\"0.attributes.value\",\n\t\t\t\"3.attributes.onload\",\n\t\t\t\"3.attributes.onclick\",\n\t\t\t\"3.attributes.value\",\n\t\t\t\"4.attributes.src\",\n\t\t\t\"4.attributes.nonce\",\n\t\t})\n\t\tensureReplacement(t, \"3\", f.Ui, \"Ory Corp\")\n\t})\n\n\tt.Run(\"case=webauthn only works for browsers\", func(t *testing.T) {\n\t\tid := createIdentityWithoutWebAuthn(t, reg)\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().UpdateIdentity(t.Context(), id))\n\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\tassert.Empty(t, f.Ui.Nodes)\n\t})\n\n\tdoAPIFlow := func(t *testing.T, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tapiClient := testhelpers.NewHTTPClientWithIdentitySessionToken(t.Context(), t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaAPI(t, apiClient, publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tv(values)\n\t\tpayload := testhelpers.EncodeFormAsJSON(t, true, values)\n\t\treturn testhelpers.SettingsMakeRequest(t, true, false, f, apiClient, payload)\n\t}\n\n\tdoBrowserFlow := func(t *testing.T, spa bool, v func(url.Values), id *identity.Identity) (string, *http.Response) {\n\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, id)\n\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, spa, publicTS)\n\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\t\tv(values)\n\t\treturn testhelpers.SettingsMakeRequest(t, false, spa, f, browserClient, testhelpers.EncodeFormAsJSON(t, spa, values))\n\t}\n\n\tt.Run(\"case=fails with api submit because only browsers are supported\", func(t *testing.T) {\n\t\tid := createIdentityWithoutWebAuthn(t, reg)\n\t\tbody, res := doAPIFlow(t, func(v url.Values) {\n\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t}, id)\n\n\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\tassert.Equal(t, text.NewErrorValidationSettingsNoStrategyFound().Text, gjson.Get(body, \"ui.messages.0.text\").String(), \"%s\", body)\n\t})\n\n\tt.Run(\"case=fails with browser submit if name is missing\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := createIdentityWithoutWebAuthn(t, reg)\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\t}, id)\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t}\n\t\t\tassert.Contains(t, gjson.Get(body, registerDisplayNameGJSONQuery+\".messages.0.text\").String(), \"Property webauthn_register_displayname is missing.\", \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=fails with browser submit because csrf token is missing\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := createIdentityWithoutWebAuthn(t, reg)\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Del(\"csrf_token\")\n\t\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\t\tv.Set(node.WebAuthnRegisterDisplayName, \"foobar\")\n\t\t\t}, id)\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"error.reason\").String(), body)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), errTS.URL)\n\t\t\t\tassert.Equal(t, nosurfx.ErrInvalidCSRFToken.Reason(), gjson.Get(body, \"reason\").String(), body)\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=fails with browser submit register payload is invalid\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := createIdentityWithoutWebAuthn(t, reg)\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\t\tv.Set(node.WebAuthnRegisterDisplayName, \"foobar\")\n\t\t\t}, id)\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t}\n\t\t\tassert.Contains(t, gjson.Get(body, \"ui.messages.0.text\").String(), \"Parse error for Registration\", \"%s\", body)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=requires privileged session for register\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"1ns\")\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, \"5m\")\n\t\t})\n\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := createIdentityWithoutWebAuthn(t, reg)\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Set(node.WebAuthnRegister, \"{}\")\n\t\t\t\tv.Set(node.WebAuthnRegisterDisplayName, \"foobar\")\n\t\t\t}, id)\n\n\t\t\tif spa {\n\t\t\t\tassert.NotEmpty(t, gjson.Get(body, \"redirect_browser_to\").String())\n\t\t\t\tassert.Equal(t, http.StatusForbidden, res.StatusCode)\n\t\t\t\tassertx.EqualAsJSONExcept(t, settings.NewFlowNeedsReAuth(), json.RawMessage(body), []string{\"redirect_browser_to\"})\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), loginTS.URL+\"/login-ts\")\n\t\t\t\tassertx.EqualAsJSON(t, text.NewInfoLoginReAuth(), json.RawMessage(gjson.Get(body, \"ui.messages.0\").Raw))\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=add a security key\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\t// We load our identity which we will use to replay the webauth session\n\t\t\tvar id identity.Identity\n\t\t\trequire.NoError(t, json.Unmarshal(settingsFixtureSuccessIdentity, &id))\n\t\t\tid.NID = uuid.Must(uuid.NewV4())\n\t\t\t_ = reg.PrivilegedIdentityPool().DeleteIdentity(t.Context(), id.ID)\n\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, &id)\n\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, spa, publicTS)\n\n\t\t\t// We inject the session to replay\n\t\t\tinterim, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\t\trequire.NoError(t, err)\n\t\t\tinterim.InternalContext = settingsFixtureSuccessInternalContext\n\t\t\trequire.NoError(t, reg.SettingsFlowPersister().UpdateSettingsFlow(context.Background(), interim))\n\n\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\n\t\t\t// We use the response replay\n\t\t\tvalues.Set(node.WebAuthnRegister, string(settingsFixtureSuccessResponse))\n\t\t\tvalues.Set(node.WebAuthnRegisterDisplayName, \"foobar\")\n\t\t\tbody, res := testhelpers.SettingsMakeRequest(t, false, spa, f, browserClient, testhelpers.EncodeFormAsJSON(t, spa, values))\n\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode, body)\n\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t}\n\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(body, \"state\").String(), body)\n\n\t\t\tactual, err := reg.Persister().GetIdentityConfidential(context.Background(), id.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tcred, ok := actual.GetCredentials(identity.CredentialsTypeWebAuthn)\n\t\t\trequire.True(t, ok)\n\t\t\tassert.Len(t, gjson.GetBytes(cred.Config, \"credentials\").Array(), 1)\n\n\t\t\tactualFlow, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\t\trequire.NoError(t, err)\n\t\t\t// new session data has been generated\n\t\t\tassert.NotEqual(t, settingsFixtureSuccessInternalContext, gjson.GetBytes(actualFlow.InternalContext, flow.PrefixInternalContextKey(identity.CredentialsTypeWebAuthn, webauthn.InternalContextKeySessionData)))\n\n\t\t\ttesthelpers.EnsureAAL(t, browserClient, publicTS, \"aal2\", string(identity.CredentialsTypeWebAuthn))\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=fails to remove security key if it is passwordless and the last credential available\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeyWebAuthnPasswordless, true)\n\t\tt.Cleanup(func() {\n\t\t\tconf.MustSet(t.Context(), config.ViperKeyWebAuthnPasswordless, false)\n\t\t})\n\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := createIdentity(t.Context(), t, reg)\n\t\t\tid.DeleteCredentialsType(identity.CredentialsTypePassword)\n\t\t\tconf := sqlxx.JSONRawMessage(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\",\"is_passwordless\":true}]}`)\n\t\t\tid.UpsertCredentialsConfig(identity.CredentialsTypeWebAuthn, conf, 0)\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\t// The remove key should be empty\n\t\t\t\tsnapshotx.SnapshotTExcept(t, v, []string{\"csrf_token\", \"webauthn_register_trigger\"})\n\n\t\t\t\tv.Set(node.WebAuthnRemove, \"666f6f666f6f\")\n\t\t\t}, id)\n\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t}\n\n\t\t\tt.Run(\"response\", func(t *testing.T) {\n\t\t\t\tassert.EqualValues(t, flow.StateShowForm, gjson.Get(body, \"state\").String(), body)\n\t\t\t\tsnapshotx.SnapshotTExcept(t, json.RawMessage(gjson.Get(body, \"ui.nodes.#(attributes.name==webauthn_remove)\").String()), nil)\n\n\t\t\t\tactual, err := reg.Persister().GetIdentityConfidential(context.Background(), id.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tcred, ok := actual.GetCredentials(identity.CredentialsTypeWebAuthn)\n\t\t\t\tassert.True(t, ok)\n\t\t\t\tassert.Len(t, gjson.GetBytes(cred.Config, \"credentials\").Array(), 1)\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=possible to remove webauthn credential if it is MFA at all times\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := createIdentity(t.Context(), t, reg)\n\t\t\tid.DeleteCredentialsType(identity.CredentialsTypePassword)\n\t\t\tid.UpsertCredentialsConfig(identity.CredentialsTypeWebAuthn, sqlxx.JSONRawMessage(`{\"credentials\":[{\"id\":\"Zm9vZm9v\",\"display_name\":\"foo\",\"is_passwordless\":false}]}`), 0)\n\t\t\trequire.NoError(t, reg.IdentityManager().Update(t.Context(), id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\t// The remove key should be set\n\t\t\t\tsnapshotx.SnapshotTExcept(t, v, []string{\n\t\t\t\t\t\"csrf_token\",\n\t\t\t\t\t\"webauthn_register_trigger\",\n\t\t\t\t})\n\t\t\t\tv.Set(node.WebAuthnRemove, \"666f6f666f6f\")\n\t\t\t}, id)\n\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t}\n\n\t\t\tt.Run(\"response\", func(t *testing.T) {\n\t\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(body, \"state\").String(), body)\n\t\t\t\tactual, err := reg.Persister().GetIdentityConfidential(context.Background(), id.ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\t_, ok := actual.GetCredentials(identity.CredentialsTypeWebAuthn)\n\t\t\t\tassert.False(t, ok)\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=remove all security keys\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := createIdentity(t.Context(), t, reg)\n\t\t\tallCred, ok := id.GetCredentials(identity.CredentialsTypeWebAuthn)\n\t\t\tassert.True(t, ok)\n\n\t\t\tvar cc identity.CredentialsWebAuthnConfig\n\t\t\trequire.NoError(t, json.Unmarshal(allCred.Config, &cc))\n\t\t\trequire.Len(t, cc.Credentials, 2)\n\n\t\t\tfor _, cred := range cc.Credentials {\n\t\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\t\tv.Set(node.WebAuthnRemove, fmt.Sprintf(\"%x\", cred.ID))\n\t\t\t\t}, id)\n\n\t\t\t\tif spa {\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t\t}\n\t\t\t\tassert.EqualValues(t, flow.StateSuccess, gjson.Get(body, \"state\").String(), body)\n\n\t\t\t\tif spa {\n\t\t\t\t\tassert.EqualValues(t, flow.ContinueWithActionRedirectBrowserToString, gjson.Get(body, \"continue_with.0.action\").String(), \"%s\", body)\n\t\t\t\t\tassert.Contains(t, gjson.Get(body, \"continue_with.0.redirect_browser_to\").String(), uiTS.URL, \"%s\", body)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Empty(t, gjson.Get(body, \"continue_with\").Array(), \"%s\", body)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tactual, err := reg.Persister().GetIdentityConfidential(context.Background(), id.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\t_, ok = actual.GetCredentials(identity.CredentialsTypeWebAuthn)\n\t\t\tassert.False(t, ok)\n\t\t\t// Check not to remove other credentials with webauthn\n\t\t\t_, ok = actual.GetCredentials(identity.CredentialsTypePassword)\n\t\t\tassert.True(t, ok)\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=fails with browser submit register payload is invalid\", func(t *testing.T) {\n\t\trun := func(t *testing.T, spa bool) {\n\t\t\tid := createIdentity(t.Context(), t, reg)\n\t\t\tbody, res := doBrowserFlow(t, spa, func(v url.Values) {\n\t\t\t\tv.Set(node.WebAuthnRemove, fmt.Sprintf(\"%x\", []byte(\"foofoo\")))\n\t\t\t}, id)\n\n\t\t\tif spa {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow)\n\t\t\t} else {\n\t\t\t\tassert.Contains(t, res.Request.URL.String(), uiTS.URL)\n\t\t\t}\n\t\t\tassert.EqualValues(t, flow.StateSuccess, json.RawMessage(gjson.Get(body, \"state\").String()))\n\n\t\t\tactual, err := reg.Persister().GetIdentityConfidential(context.Background(), id.ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tcred, ok := actual.GetCredentials(identity.CredentialsTypeWebAuthn)\n\t\t\tassert.True(t, ok)\n\t\t\tassert.Len(t, gjson.GetBytes(cred.Config, \"credentials\").Array(), 1)\n\t\t\tassert.Equal(t, \"bar\", gjson.GetBytes(cred.Config, \"credentials.0.display_name\").String())\n\t\t}\n\n\t\tt.Run(\"type=browser\", func(t *testing.T) {\n\t\t\trun(t, false)\n\t\t})\n\n\t\tt.Run(\"type=spa\", func(t *testing.T) {\n\t\t\trun(t, true)\n\t\t})\n\t})\n\n\tt.Run(\"case=should fail if no identifier was set in the schema\", func(t *testing.T) {\n\t\ttesthelpers.SetDefaultIdentitySchema(conf, \"file://stub/missing-identifier.schema.json\")\n\n\t\tfor _, f := range []string{\"spa\", \"browser\"} {\n\t\t\tt.Run(\"type=\"+f, func(t *testing.T) {\n\t\t\t\tisSPA := f == \"spa\"\n\n\t\t\t\tvar id identity.Identity\n\t\t\t\tid.NID = uuid.Must(uuid.NewV4())\n\t\t\t\trequire.NoError(t, json.Unmarshal(settingsFixtureSuccessIdentity, &id))\n\t\t\t\t_ = reg.PrivilegedIdentityPool().DeleteIdentity(context.Background(), id.ID)\n\t\t\t\tbrowserClient := testhelpers.NewHTTPClientWithIdentitySessionCookie(t.Context(), t, reg, &id)\n\t\t\t\tf := testhelpers.InitializeSettingsFlowViaBrowser(t, browserClient, isSPA, publicTS)\n\n\t\t\t\t// We inject the session to replay\n\t\t\t\tinterim, err := reg.SettingsFlowPersister().GetSettingsFlow(context.Background(), uuid.FromStringOrNil(f.Id))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tinterim.InternalContext = settingsFixtureSuccessInternalContext\n\t\t\t\trequire.NoError(t, reg.SettingsFlowPersister().UpdateSettingsFlow(context.Background(), interim))\n\n\t\t\t\tvalues := testhelpers.SDKFormFieldsToURLValues(f.Ui.Nodes)\n\n\t\t\t\t// We use the response replay\n\t\t\t\tvalues.Set(node.WebAuthnRegister, string(settingsFixtureSuccessResponse))\n\t\t\t\tvalues.Set(node.WebAuthnRegisterDisplayName, \"foobar\")\n\t\t\t\tactual, _ := testhelpers.SettingsMakeRequest(t, false, isSPA, f, browserClient, testhelpers.EncodeFormAsJSON(t, isSPA, values))\n\t\t\t\tassert.Equal(t, text.NewErrorValidationIdentifierMissing().Text, gjson.Get(actual, \"ui.messages.0.text\").String(), \"%s\", actual)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/strategy.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/continuity\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/hash\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/selfservice/errorx\"\n\t\"github.com/ory/kratos/selfservice/flow/login\"\n\t\"github.com/ory/kratos/selfservice/flow/registration\"\n\t\"github.com/ory/kratos/selfservice/flow/settings\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\nvar (\n\t_ login.Strategy                    = new(Strategy)\n\t_ settings.Strategy                 = new(Strategy)\n\t_ identity.ActiveCredentialsCounter = new(Strategy)\n)\n\ntype dependencies interface {\n\tlogrusx.Provider\n\thttpx.WriterProvider\n\tnosurfx.CSRFTokenGeneratorProvider\n\tnosurfx.CSRFProvider\n\totelx.Provider\n\n\tconfig.Provider\n\n\tcontinuity.ManagementProvider\n\n\terrorx.ManagementProvider\n\thash.HashProvider\n\n\tregistration.HandlerProvider\n\tregistration.HooksProvider\n\tregistration.ErrorHandlerProvider\n\tregistration.HookExecutorProvider\n\tregistration.FlowPersistenceProvider\n\n\tlogin.HooksProvider\n\tlogin.ErrorHandlerProvider\n\tlogin.HookExecutorProvider\n\tlogin.FlowPersistenceProvider\n\tlogin.HandlerProvider\n\n\tsettings.FlowPersistenceProvider\n\tsettings.HookExecutorProvider\n\tsettings.HooksProvider\n\tsettings.ErrorHandlerProvider\n\n\tidentity.PrivilegedPoolProvider\n\tidentity.ValidationProvider\n\tidentity.ActiveCredentialsCounterStrategyProvider\n\tidentity.ManagementProvider\n\n\tsession.HandlerProvider\n\tsession.ManagementProvider\n}\n\ntype Strategy struct{ d dependencies }\n\nfunc NewStrategy(d dependencies) *Strategy { return &Strategy{d: d} }\n\nfunc (s *Strategy) CountActiveMultiFactorCredentials(_ context.Context, cc map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\treturn s.countCredentials(cc, false)\n}\n\nfunc (s *Strategy) CountActiveFirstFactorCredentials(_ context.Context, cc map[identity.CredentialsType]identity.Credentials) (count int, err error) {\n\treturn s.countCredentials(cc, true)\n}\n\nfunc (s *Strategy) countCredentials(cc map[identity.CredentialsType]identity.Credentials, onlyPasswordlessCredentials bool) (count int, err error) {\n\tfor _, c := range cc {\n\t\tif c.Type == s.ID() && len(c.Config) > 0 {\n\t\t\tvar conf identity.CredentialsWebAuthnConfig\n\t\t\tif err = json.Unmarshal(c.Config, &conf); err != nil {\n\t\t\t\treturn 0, errors.WithStack(err)\n\t\t\t}\n\n\t\t\tfor _, cred := range conf.Credentials {\n\t\t\t\tif cred.IsPasswordless && len(strings.Join(c.Identifiers, \"\")) == 0 {\n\t\t\t\t\t// If this is a passwordless credential, it will only work if the identifier is set, as\n\t\t\t\t\t// we use the identifier to look up the identity. If the identifier is not set, we can\n\t\t\t\t\t// assume that the user can't sign in using this method.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif cred.IsPasswordless != onlyPasswordlessCredentials {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// If the credential is passwordless and we require passwordless credentials, or if the credential is not\n\t\t\t\t// passwordless and we require non-passwordless credentials, we count it.\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (s *Strategy) ID() identity.CredentialsType {\n\treturn identity.CredentialsTypeWebAuthn\n}\n\nfunc (s *Strategy) NodeGroup() node.UiNodeGroup {\n\treturn node.WebAuthnGroup\n}\n\nfunc (s *Strategy) CompletedAuthenticationMethod(ctx context.Context) session.AuthenticationMethod {\n\taal := identity.AuthenticatorAssuranceLevel1\n\tif !s.d.Config().WebAuthnForPasswordless(ctx) {\n\t\taal = identity.AuthenticatorAssuranceLevel2\n\t}\n\treturn session.AuthenticationMethod{\n\t\tMethod: s.ID(),\n\t\tAAL:    aal,\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/strategy_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/selfservice/strategy/webauthn\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/x/contextx\"\n)\n\nfunc TestCompletedAuthenticationMethod(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tstrategy := webauthn.NewStrategy(reg)\n\n\tt.Run(\"default config\", func(t *testing.T) {\n\t\tassert.Equal(t, session.AuthenticationMethod{\n\t\t\tMethod: strategy.ID(),\n\t\t\tAAL:    identity.AuthenticatorAssuranceLevel2,\n\t\t}, strategy.CompletedAuthenticationMethod(t.Context()))\n\t})\n\n\tt.Run(\"with passwordless enabled\", func(t *testing.T) {\n\t\tctx := contextx.WithConfigValue(t.Context(), config.ViperKeyWebAuthnPasswordless, true)\n\t\tassert.Equal(t, session.AuthenticationMethod{\n\t\t\tMethod: strategy.ID(),\n\t\t\tAAL:    identity.AuthenticatorAssuranceLevel1,\n\t\t}, strategy.CompletedAuthenticationMethod(ctx))\n\t})\n}\n\nfunc TestCountActiveFirstFactorCredentials(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\tstrategy := webauthn.NewStrategy(reg)\n\n\tfor k, tc := range []struct {\n\t\tin            map[identity.CredentialsType]identity.Credentials\n\t\texpectedFirst int\n\t\texpectedMulti int\n\t}{\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:   strategy.ID(),\n\t\t\t\tConfig: []byte{},\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:   strategy.ID(),\n\t\t\t\tConfig: []byte(`{\"credentials\": []}`),\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\tConfig:      []byte(`{\"credentials\": [{}]}`),\n\t\t\t}},\n\t\t\texpectedMulti: 1,\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{}, // also works without identifier\n\t\t\t\tConfig:      []byte(`{\"credentials\": [{}]}`),\n\t\t\t}},\n\t\t\texpectedMulti: 1,\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\tConfig:      []byte(`{\"credentials\": [{\"is_passwordless\": true}]}`),\n\t\t\t}},\n\t\t\texpectedFirst: 1,\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:   strategy.ID(),\n\t\t\t\tConfig: []byte(`{\"credentials\": [{\"is_passwordless\": true}]}`),\n\t\t\t}},\n\t\t\texpectedFirst: 0, // missing identifier\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\tConfig:      []byte(`{\"credentials\": [{\"is_passwordless\": true}, {\"is_passwordless\": true}]}`),\n\t\t\t}},\n\t\t\texpectedFirst: 2,\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:        strategy.ID(),\n\t\t\t\tIdentifiers: []string{\"foo\"},\n\t\t\t\tConfig:      []byte(`{\"credentials\": [{\"is_passwordless\": true}, {\"is_passwordless\": false}]}`),\n\t\t\t}},\n\t\t\texpectedFirst: 1,\n\t\t\texpectedMulti: 1,\n\t\t},\n\t\t{\n\t\t\tin: map[identity.CredentialsType]identity.Credentials{strategy.ID(): {\n\t\t\t\tType:   strategy.ID(),\n\t\t\t\tConfig: []byte(`{}`),\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tin: nil,\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tcc := map[identity.CredentialsType]identity.Credentials{}\n\t\t\tfor _, c := range tc.in {\n\t\t\t\tcc[c.Type] = c\n\t\t\t}\n\n\t\t\tactual, err := strategy.CountActiveFirstFactorCredentials(t.Context(), cc)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tc.expectedFirst, actual)\n\n\t\t\tactual, err = strategy.CountActiveMultiFactorCredentials(t.Context(), cc)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tc.expectedMulti, actual)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/stub/login.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\"\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/stub/missing-identifier.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\"\n        }\n      },\n      \"required\": [\n        \"email\"\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/stub/noid.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\"\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/stub/profile.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/stub/registration.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"foobar\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        },\n        \"username\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"foobar\",\n        \"username\"\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/stub/settings.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"website\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "selfservice/strategy/webauthn/validate.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthn\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/schema\"\n)\n\nfunc (s *Strategy) validateCredentials(ctx context.Context, i *identity.Identity) error {\n\tif err := s.d.IdentityValidator().Validate(ctx, i); err != nil {\n\t\treturn err\n\t}\n\n\tc := i.GetCredentialsOr(identity.CredentialsTypeWebAuthn, &identity.Credentials{})\n\tif len(c.Identifiers) == 0 {\n\t\treturn schema.NewMissingIdentifierError()\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "selfservice/stub/fake-session.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {}\n}\n"
  },
  {
    "path": "selfservice/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/stub/new-form.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"objy\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"stringy\": {\n              \"type\": \"string\"\n            },\n            \"numby\": {\n              \"type\": \"number\"\n            },\n            \"objy\": {\n              \"type\": \"object\",\n              \"properties\": {}\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/stub/registration.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "selfservice/stub/updated.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"bar\": {\n          \"type\": \"string\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "session/.snapshots/TestTokenizer-case=es256-without-jsonnet.json",
    "content": "{\n  \"exp\": 1675209660,\n  \"iat\": 1675209600,\n  \"iss\": \"http://localhost/\",\n  \"nbf\": 1675209600,\n  \"sid\": \"432caf86-c1d8-401c-978a-8da89133f78b\",\n  \"sub\": \"7458af86-c1d8-401c-978a-8da89133f78b\"\n}\n"
  },
  {
    "path": "session/.snapshots/TestTokenizer-case=es512-without-jsonnet.json",
    "content": "{\n  \"exp\": 1675209660,\n  \"iat\": 1675209600,\n  \"iss\": \"http://localhost/\",\n  \"nbf\": 1675209600,\n  \"sid\": \"432caf86-c1d8-401c-978a-8da89133f78b\",\n  \"sub\": \"7458af86-c1d8-401c-978a-8da89133f78b\"\n}\n"
  },
  {
    "path": "session/.snapshots/TestTokenizer-case=rs512-with-external_id-in-sub.json",
    "content": "{\n  \"aal\": \"aal1\",\n  \"exp\": 1675209660,\n  \"external_id\": \"external-id\",\n  \"foo\": \"bar\",\n  \"iat\": 1675209600,\n  \"iss\": \"http://localhost/\",\n  \"nbf\": 1675209600,\n  \"schema_id\": \"default\",\n  \"second_claim\": 1675209660,\n  \"sid\": \"432caf86-c1d8-401c-978a-8da89133f78b\",\n  \"sub\": \"external-id\"\n}\n"
  },
  {
    "path": "session/.snapshots/TestTokenizer-case=rs512-with-jsonnet.json",
    "content": "{\n  \"aal\": \"aal1\",\n  \"exp\": 1675209660,\n  \"external_id\": \"external-id\",\n  \"foo\": \"bar\",\n  \"iat\": 1675209600,\n  \"iss\": \"http://localhost/\",\n  \"nbf\": 1675209600,\n  \"schema_id\": \"default\",\n  \"second_claim\": 1675209660,\n  \"sid\": \"432caf86-c1d8-401c-978a-8da89133f78b\",\n  \"sub\": \"7458af86-c1d8-401c-978a-8da89133f78b\"\n}\n"
  },
  {
    "path": "session/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ory/herodot\"\n)\n\nfunc RespondWithJSONErrorOnAuthenticated(h herodot.Writer, err error) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\th.WriteError(w, r, err)\n\t}\n}\n\nvar ErrNoSessionFound = herodot.ErrUnauthorized.WithReasonf(\"No valid session credentials found in the request.\")\n"
  },
  {
    "path": "session/expand.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"strings\"\n\n\t\"github.com/ory/x/sqlxx\"\n)\n\n// Expandable controls what fields to expand for sessions.\ntype Expandable = sqlxx.Expandable\n\nconst (\n\t// ExpandSessionDevices expands devices related to the session\n\tExpandSessionDevices Expandable = \"Devices\"\n\t// ExpandSessionIdentity expands Identity related to the session\n\tExpandSessionIdentity                  Expandable = \"Identity\"\n\tExpandSessionIdentityRecoveryAddress   Expandable = \"Identity.RecoveryAddresses\"\n\tExpandSessionIdentityVerifiableAddress Expandable = \"Identity.VerifiableAddresses\"\n)\n\nvar expandablesMap = map[string]Expandable{\n\t\"devices\":  ExpandSessionDevices,\n\t\"identity\": ExpandSessionIdentity,\n}\n\n// Expandables is a list of Expandable values.\ntype Expandables = sqlxx.Expandables\n\nfunc ParseExpandable(in string) (Expandable, bool) {\n\te, ok := expandablesMap[strings.ToLower(in)]\n\treturn e, ok\n}\n\n// ExpandNothing expands nothing\nvar ExpandNothing Expandables\n\n// ExpandDefault expands the default fields of a session\n// - Associated Identity\nvar ExpandDefault = Expandables{\n\tExpandSessionIdentity,\n}\n\n// ExpandEverything expands all the fields of a session.\nvar ExpandEverything = Expandables{\n\tExpandSessionDevices,\n\tExpandSessionIdentity,\n}\n"
  },
  {
    "path": "session/expand_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestExpandableSearch_ExistingExpand(t *testing.T) {\n\te := ExpandEverything\n\n\tassert.True(t, e.Has(ExpandSessionDevices))\n}\n\nfunc TestExpandableSearch_NonExistingExpandOption(t *testing.T) {\n\te := ExpandEverything\n\n\tassert.False(t, e.Has(\"SomeExpand\"))\n}\n\nfunc TestExpandables_ToEager_skips_Identity(t *testing.T) {\n\te := ExpandEverything\n\n\tres := e.ToEager()\n\tassert.ElementsMatch(t, []string{string(ExpandSessionDevices), string(ExpandSessionIdentity)}, res)\n}\n\nfunc TestExpandables_ExpandNothing_IsEmpty(t *testing.T) {\n\te := ExpandNothing\n\n\tassert.Len(t, e, 0)\n}\n"
  },
  {
    "path": "session/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/x/httprouterx\"\n\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/x/otelx/semconv\"\n\t\"github.com/ory/x/pagination/migrationpagination\"\n\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/x\"\n)\n\ntype (\n\thandlerDependencies interface {\n\t\tManagementProvider\n\t\tPersistenceProvider\n\t\thttpx.WriterProvider\n\t\totelx.Provider\n\t\tlogrusx.Provider\n\t\tnosurfx.CSRFProvider\n\t\tconfig.Provider\n\t\tsessiontokenexchange.PersistenceProvider\n\t\tTokenizerProvider\n\t}\n\tHandlerProvider interface {\n\t\tSessionHandler() *Handler\n\t}\n\tHandler struct{ r handlerDependencies }\n)\n\nfunc NewHandler(r handlerDependencies) *Handler { return &Handler{r: r} }\n\nconst (\n\tRouteCollection                  = \"/sessions\"\n\tRouteExchangeCodeForSessionToken = RouteCollection + \"/token-exchange\" // #nosec G101\n\tRouteWhoami                      = RouteCollection + \"/whoami\"\n\tRouteSession                     = RouteCollection + \"/{id}\"\n)\n\nconst (\n\tAdminRouteIdentity           = \"/identities\"\n\tAdminRouteIdentitiesSessions = AdminRouteIdentity + \"/{id}/sessions\"\n\tAdminRouteSessionExtendId    = RouteSession + \"/extend\"\n)\n\nfunc (h *Handler) RegisterAdminRoutes(admin *httprouterx.RouterAdmin) {\n\tadmin.GET(RouteCollection, h.adminListSessions)\n\tadmin.GET(RouteSession, h.getSession)\n\tadmin.DELETE(RouteSession, h.disableSession)\n\n\tadmin.GET(AdminRouteIdentitiesSessions, h.listIdentitySessions)\n\tadmin.DELETE(AdminRouteIdentitiesSessions, h.deleteIdentitySessions)\n\tadmin.PATCH(AdminRouteSessionExtendId, h.adminSessionExtend)\n\n\tadmin.DELETE(RouteCollection, redir.RedirectToPublicRoute(h.r))\n}\n\nfunc (h *Handler) RegisterPublicRoutes(public *httprouterx.RouterPublic) {\n\t// We need to completely ignore the whoami/logout path so that we do not accidentally set\n\t// some cookie.\n\th.r.CSRFHandler().IgnorePath(RouteWhoami)\n\th.r.CSRFHandler().IgnorePath(RouteCollection)\n\th.r.CSRFHandler().IgnoreGlob(RouteCollection + \"/*\")\n\th.r.CSRFHandler().IgnoreGlob(RouteCollection + \"/*/extend\")\n\th.r.CSRFHandler().IgnoreGlob(AdminRouteIdentity + \"/*/sessions\")\n\n\tfor _, m := range []string{http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodConnect, http.MethodOptions, http.MethodTrace} {\n\t\tpublic.Handler(m, RouteWhoami, http.HandlerFunc(h.whoami))\n\t}\n\n\tpublic.DELETE(RouteCollection, h.deleteMySessions)\n\tpublic.DELETE(RouteSession, h.deleteMySession)\n\tpublic.GET(RouteCollection, h.listMySessions)\n\n\tpublic.GET(RouteExchangeCodeForSessionToken, h.exchangeCode)\n\n\tpublic.DELETE(AdminRouteIdentitiesSessions, redir.RedirectToAdminRoute(h.r))\n}\n\n// Check Session Request Parameters\n//\n// swagger:parameters toSession\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype toSession struct {\n\t// Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\n\t//\n\t// in: header\n\tSessionToken string `json:\"X-Session-Token\"`\n\n\t// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\n\t// scenario you must include the HTTP Cookie Header which originally was included in the request to your server.\n\t// An example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\n\t//\n\t// It is ok if more than one cookie are included here as all other cookies will be ignored.\n\t//\n\t// in: header\n\tCookie string `json:\"Cookie\"`\n\n\t// Returns the session additionally as a token (such as a JWT)\n\t//\n\t// The value of this parameter has to be a valid, configured Ory Session token template. For more information head over to [the documentation](http://ory.sh/docs/identities/session-to-jwt-cors).\n\t//\n\t// in: query\n\tTokenizeAs string `json:\"tokenize_as\"`\n}\n\n// swagger:route GET /sessions/whoami frontend toSession\n//\n// # Check Who the Current HTTP Session Belongs To\n//\n// Uses the HTTP Headers in the GET request to determine (e.g. by using checking the cookies) who is authenticated.\n// Returns a session object in the body or 401 if the credentials are invalid or no credentials were sent.\n// When the request it successful it adds the user ID to the 'X-Kratos-Authenticated-Identity-Id' header\n// in the response.\n//\n// If you call this endpoint from a server-side application, you must forward the HTTP Cookie Header to this endpoint:\n//\n//\t```js\n//\t// pseudo-code example\n//\trouter.get('/protected-endpoint', async function (req, res) {\n//\t  const session = await client.toSession(undefined, req.header('cookie'))\n//\n//\t  // console.log(session)\n//\t})\n//\t```\n//\n// When calling this endpoint from a non-browser application (e.g. mobile app) you must include the session token:\n//\n//\t```js\n//\t// pseudo-code example\n//\t// ...\n//\tconst session = await client.toSession(\"the-session-token\")\n//\n//\t// console.log(session)\n//\t```\n//\n// When using a token template, the token is included in the `tokenized` field of the session.\n//\n//\t```js\n//\t// pseudo-code example\n//\t// ...\n//\tconst session = await client.toSession(\"the-session-token\", { tokenize_as: \"example-jwt-template\" })\n//\n//\tconsole.log(session.tokenized) // The JWT\n//\t```\n//\n// Depending on your configuration this endpoint might return a 403 status code if the session has a lower Authenticator\n// Assurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\n// credentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\n// to sign in with the second factor or change the configuration.\n//\n// This endpoint is useful for:\n//\n// - AJAX calls. Remember to send credentials and set up CORS correctly!\n// - Reverse proxies and API Gateways\n// - Server-side calls - use the `X-Session-Token` header!\n//\n// This endpoint authenticates users by checking:\n//\n// - if the `Cookie` HTTP header was set containing an Ory Kratos Session Cookie;\n// - if the `Authorization: bearer <ory-session-token>` HTTP header was set with a valid Ory Kratos Session Token;\n// - if the `X-Session-Token` HTTP header was set with a valid Ory Kratos Session Token.\n//\n// If none of these headers are set or the cookie or token are invalid, the endpoint returns a HTTP 401 status code.\n//\n// As explained above, this request may fail due to several reasons. The `error.id` can be one of:\n//\n// - `session_inactive`: No active session was found in the request (e.g. no Ory Session Cookie / Ory Session Token).\n// - `session_aal2_required`: An active session was found but it does not fulfil the Authenticator Assurance Level, implying that the session must (e.g.) authenticate the second factor.\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: session\n//\t  401: errorGeneric\n//\t  403: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-low\nfunc (h *Handler) whoami(w http.ResponseWriter, r *http.Request) {\n\tctx, span := h.r.Tracer(r.Context()).Tracer().Start(r.Context(), \"sessions.Handler.whoami\")\n\tdefer span.End()\n\n\ts, err := h.r.SessionManager().FetchFromRequest(ctx, r)\n\tc := h.r.Config()\n\tif err != nil {\n\t\t// We cache errors (and set cache header only when configured) where no session was found.\n\t\tif noSess := new(ErrNoActiveSessionFound); c.SessionWhoAmICaching(ctx) && errors.As(err, &noSess) && noSess.CredentialsMissing {\n\t\t\tw.Header().Set(\"Ory-Session-Cache-For\", fmt.Sprintf(\"%d\", int64(time.Minute.Seconds())))\n\t\t}\n\n\t\th.r.Logger().WithRequest(r).WithError(err).Info(\"No valid session found.\")\n\t\th.r.Writer().WriteError(w, r, ErrNoSessionFound.WithWrap(err))\n\t\treturn\n\t}\n\n\tvar aalErr *ErrAALNotSatisfied\n\tif err := h.r.SessionManager().DoesSessionSatisfy(ctx, s, c.SessionWhoAmIAAL(ctx),\n\t\t// For the time being we want to update the AAL in the database if it is unset.\n\t\tUpsertAAL,\n\t); errors.As(err, &aalErr) {\n\t\th.r.Logger().WithRequest(r).WithError(err).Info(\"Session was found but AAL is not satisfied for calling this endpoint.\")\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t} else if err != nil {\n\t\th.r.Logger().WithRequest(r).WithError(err).Info(\"No valid session cookie found.\")\n\t\th.r.Writer().WriteError(w, r, herodot.ErrUnauthorized.WithWrap(err).WithReasonf(\"Unable to determine AAL.\"))\n\t\treturn\n\t}\n\n\t// s.Devices = nil\n\ts.Identity = s.Identity.CopyWithoutCredentials()\n\n\ttokenizeTemplate := r.URL.Query().Get(\"tokenize_as\")\n\tif tokenizeTemplate != \"\" {\n\t\tif err := h.r.SessionTokenizer().TokenizeSession(ctx, tokenizeTemplate, s); err != nil {\n\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Set userId as the X-Kratos-Authenticated-Identity-Id header.\n\tw.Header().Set(\"X-Kratos-Authenticated-Identity-Id\", s.Identity.ID.String())\n\n\t// Set Cache header only when configured, and when no tokenization is requested.\n\tif c.SessionWhoAmICaching(ctx) && len(tokenizeTemplate) == 0 {\n\t\texpiry := time.Until(s.ExpiresAt)\n\t\tif c.SessionWhoAmICachingMaxAge(ctx) > 0 && expiry > c.SessionWhoAmICachingMaxAge(ctx) {\n\t\t\texpiry = c.SessionWhoAmICachingMaxAge(ctx)\n\t\t}\n\n\t\tw.Header().Set(\"Ory-Session-Cache-For\", fmt.Sprintf(\"%0.f\", expiry.Seconds()))\n\t}\n\n\tif err := h.r.SessionManager().RefreshCookie(ctx, w, r, s); err != nil {\n\t\th.r.Logger().WithRequest(r).WithError(err).Info(\"Could not re-issue cookie.\")\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.r.Writer().Write(w, r, s)\n}\n\n// Delete Identity Session Parameters\n//\n// swagger:parameters deleteIdentitySessions\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype deleteIdentitySessions struct {\n\t// ID is the identity's ID.\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n}\n\n// swagger:route DELETE /admin/identities/{id}/sessions identity deleteIdentitySessions\n//\n// # Delete & Invalidate an Identity's Sessions\n//\n// Calling this endpoint irrecoverably and permanently deletes and invalidates all sessions that belong to the given Identity.\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  204: emptyResponse\n//\t  400: errorGeneric\n//\t  401: errorGeneric\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) deleteIdentitySessions(w http.ResponseWriter, r *http.Request) {\n\tiID, err := uuid.FromString(r.PathValue(\"id\"))\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(err.Error()).WithDebug(\"could not parse UUID\"))\n\t\treturn\n\t}\n\tif err := h.r.SessionPersister().DeleteSessionsByIdentity(r.Context(), iID); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusNoContent)\n}\n\n// Session List Request\n//\n// The request object for listing sessions in an administrative context.\n//\n// swagger:parameters listSessions\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype listSessionsRequest struct {\n\tkeysetpagination.RequestParameters\n\n\t// Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\n\t//\n\t// required: false\n\t// in: query\n\tActive bool `json:\"active\"`\n\n\t// ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session.\n\t// If no value is provided, the expandable properties are skipped.\n\t//\n\t// required: false\n\t// in: query\n\tExpandOptions []SessionExpandable `json:\"expand\"`\n}\n\n// Expandable properties of a session\n// swagger:enum SessionExpandable\ntype SessionExpandable string\n\nconst (\n\tSessionExpandableIdentity SessionExpandable = \"identity\"\n\tSessionExpandableDevices  SessionExpandable = \"devices\"\n)\n\n// Session List Response\n//\n// The response given when listing sessions in an administrative context.\n//\n// swagger:response listSessions\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype listSessionsResponse struct {\n\tkeysetpagination.ResponseHeaders\n\n\t// The list of sessions found\n\t// in: body\n\tSessions []Session\n}\n\n// swagger:route GET /admin/sessions identity listSessions\n//\n// # List All Sessions\n//\n// Listing all sessions that exist.\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: listSessions\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-medium\nfunc (h *Handler) adminListSessions(w http.ResponseWriter, r *http.Request) {\n\tactiveRaw := r.URL.Query().Get(\"active\")\n\tactiveBool, err := strconv.ParseBool(activeRaw)\n\tif activeRaw != \"\" && err != nil {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(\"could not parse parameter active\"))\n\t\treturn\n\t}\n\n\tvar active *bool\n\tif activeRaw != \"\" {\n\t\tactive = &activeBool\n\t}\n\n\t// Parse request pagination parameters\n\topts, err := keysetpagination.Parse(r.URL.Query(), keysetpagination.NewMapPageToken)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(\"could not parse parameter page_size\"))\n\t\treturn\n\t}\n\n\tvar expandables Expandables\n\tif es, ok := r.URL.Query()[\"expand\"]; ok {\n\t\tfor _, e := range es {\n\t\t\texpand, ok := ParseExpandable(e)\n\t\t\tif !ok {\n\t\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Could not parse expand option: %s\", e)))\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpandables = append(expandables, expand)\n\t\t}\n\t}\n\n\tsess, nextPage, err := h.r.SessionPersister().ListSessions(r.Context(), active, opts, expandables)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tu := *r.URL\n\tkeysetpagination.Header(w, &u, nextPage)\n\th.r.Writer().Write(w, r, sess)\n}\n\n// Session Get Request\n//\n// The request object for getting a session in an administrative context.\n//\n// swagger:parameters getSession\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype getSession struct {\n\t// ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session.\n\t// Example - ?expand=Identity&expand=Devices\n\t// If no value is provided, the expandable properties are skipped.\n\t//\n\t// required: false\n\t// in: query\n\tExpandOptions []SessionExpandable `json:\"expand\"`\n\n\t// ID is the session's ID.\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n}\n\n// swagger:route GET /admin/sessions/{id} identity getSession\n//\n// # Get Session\n//\n// This endpoint is useful for:\n//\n// - Getting a session object with all specified expandables that exist in an administrative context.\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: session\n//\t  400: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-low\nfunc (h *Handler) getSession(w http.ResponseWriter, r *http.Request) {\n\tif r.PathValue(\"id\") == \"whoami\" {\n\t\t// for /admin/sessions/whoami redirect to the public route\n\t\tredir.RedirectToPublicRoute(h.r)(w, r)\n\t\treturn\n\t}\n\n\tsID, err := uuid.FromString(r.PathValue(\"id\"))\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(err.Error()).WithDebug(\"could not parse UUID\"))\n\t\treturn\n\t}\n\n\tvar expandables Expandables\n\n\turlValues := r.URL.Query()\n\tif es, ok := urlValues[\"expand\"]; ok {\n\t\tfor _, e := range es {\n\t\t\texpand, ok := ParseExpandable(e)\n\t\t\tif !ok {\n\t\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Could not parse expand option: %s\", e)))\n\t\t\t\treturn\n\t\t\t}\n\t\t\texpandables = append(expandables, expand)\n\t\t}\n\t}\n\n\tsess, err := h.r.SessionPersister().GetSession(r.Context(), sID, expandables)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.r.Writer().Write(w, r, sess)\n}\n\n// List Identity Sessions Parameters\n//\n// swagger:parameters listIdentitySessions\n// Deactivate Session Parameters\n//\n// swagger:parameters disableSession\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype disableSession struct {\n\t// ID is the session's ID.\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n}\n\n// swagger:route DELETE /admin/sessions/{id} identity disableSession\n//\n// # Deactivate a Session\n//\n// Calling this endpoint deactivates the specified session. Session data is not deleted.\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t\toryAccessToken:\n//\n//\tResponses:\n//\t\t204: emptyResponse\n//\t\t400: errorGeneric\n//\t\t401: errorGeneric\n//\t\tdefault: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) disableSession(w http.ResponseWriter, r *http.Request) {\n\tsID, err := uuid.FromString(r.PathValue(\"id\"))\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(err.Error()).WithDebug(\"could not parse UUID\"))\n\t\treturn\n\t}\n\n\tif err := h.r.SessionPersister().RevokeSessionById(r.Context(), sID); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.r.Writer().WriteCode(w, r, http.StatusNoContent, nil)\n}\n\n// List Identity Sessions Parameters\n//\n// swagger:parameters listIdentitySessions\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype listIdentitySessionsRequest struct {\n\tmigrationpagination.RequestParameters\n\n\t// ID is the identity's ID.\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n\n\t// Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\n\t//\n\t// required: false\n\t// in: query\n\tActive bool `json:\"active\"`\n}\n\n// List Identity Sessions Response\n//\n// swagger:response listIdentitySessions\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype listIdentitySessionsResponse struct {\n\tmigrationpagination.ResponseHeaderAnnotation\n\n\t// in: body\n\tBody []Session\n}\n\n// swagger:route GET /admin/identities/{id}/sessions identity listIdentitySessions\n//\n// # List an Identity's Sessions\n//\n// This endpoint returns all sessions that belong to the given Identity.\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: listIdentitySessions\n//\t  400: errorGeneric\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-medium\nfunc (h *Handler) listIdentitySessions(w http.ResponseWriter, r *http.Request) {\n\tiID, err := uuid.FromString(r.PathValue(\"id\"))\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithError(err.Error()).WithDebug(\"could not parse UUID\")))\n\t\treturn\n\t}\n\n\tactiveRaw := r.URL.Query().Get(\"active\")\n\tactiveBool, err := strconv.ParseBool(activeRaw)\n\tif activeRaw != \"\" && err != nil {\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithError(\"could not parse parameter active\")))\n\t\treturn\n\t}\n\n\tvar active *bool\n\tif activeRaw != \"\" {\n\t\tactive = &activeBool\n\t}\n\n\tpage, perPage := x.ParsePagination(r)\n\tsess, total, err := h.r.SessionPersister().ListSessionsByIdentity(r.Context(), iID, active, page, perPage, uuid.Nil, ExpandEverything)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tx.PaginationHeader(w, *r.URL, total, page, perPage)\n\th.r.Writer().Write(w, r, sess)\n}\n\n// Deleted Session Count\n//\n// swagger:model deleteMySessionsCount\ntype deleteMySessionsCount struct {\n\t// The number of sessions that were revoked.\n\tCount int `json:\"count\"`\n}\n\n// Disable My Other Session Parameters\n//\n// swagger:parameters disableMyOtherSessions\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype disableMyOtherSessions struct {\n\t// Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\n\t//\n\t// in: header\n\tSessionToken string `json:\"X-Session-Token\"`\n\n\t// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\n\t// scenario you must include the HTTP Cookie Header which originally was included in the request to your server.\n\t// An example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\n\t//\n\t// It is ok if more than one cookie are included here as all other cookies will be ignored.\n\t//\n\t// in: header\n\tCookie string `json:\"Cookie\"`\n}\n\n// swagger:route DELETE /sessions frontend disableMyOtherSessions\n//\n// # Disable my other sessions\n//\n// Calling this endpoint invalidates all except the current session that belong to the logged-in user.\n// Session data are not deleted.\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: deleteMySessionsCount\n//\t  400: errorGeneric\n//\t  401: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-high\nfunc (h *Handler) deleteMySessions(w http.ResponseWriter, r *http.Request) {\n\ts, err := h.r.SessionManager().FetchFromRequest(r.Context(), r)\n\tif err != nil {\n\t\th.r.Logger().WithRequest(r).WithError(err).Info(\"No valid session cookie found.\")\n\t\th.r.Writer().WriteError(w, r, herodot.ErrUnauthorized.WithWrap(err).WithReasonf(\"No valid session cookie found.\"))\n\t\treturn\n\t}\n\n\tn, err := h.r.SessionPersister().RevokeSessionsIdentityExcept(r.Context(), s.IdentityID, s.ID)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.r.Writer().WriteCode(w, r, http.StatusOK, &deleteMySessionsCount{Count: n})\n}\n\n// Disable My Session Parameters\n//\n// swagger:parameters disableMySession\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype disableMySession struct {\n\t// ID is the session's ID.\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n\n\t// Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\n\t//\n\t// in: header\n\tSessionToken string `json:\"X-Session-Token\"`\n\n\t// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\n\t// scenario you must include the HTTP Cookie Header which originally was included in the request to your server.\n\t// An example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\n\t//\n\t// It is ok if more than one cookie are included here as all other cookies will be ignored.\n\t//\n\t// in: header\n\tCookie string `json:\"Cookie\"`\n}\n\n// swagger:route DELETE /sessions/{id} frontend disableMySession\n//\n// # Disable one of my sessions\n//\n// Calling this endpoint invalidates the specified session. The current session cannot be revoked.\n// Session data are not deleted.\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  204: emptyResponse\n//\t  400: errorGeneric\n//\t  401: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-high\nfunc (h *Handler) deleteMySession(w http.ResponseWriter, r *http.Request) {\n\tsid := r.PathValue(\"id\")\n\tif sid == \"whoami\" {\n\t\t// Special case where we actually want to handle the whoami endpoint.\n\t\th.whoami(w, r)\n\t\treturn\n\t}\n\n\ts, err := h.r.SessionManager().FetchFromRequest(r.Context(), r)\n\tif err != nil {\n\t\th.r.Logger().WithRequest(r).WithError(err).Info(\"No valid session cookie found.\")\n\t\th.r.Writer().WriteError(w, r, herodot.ErrUnauthorized.WithWrap(err).WithReasonf(\"No valid session cookie found.\"))\n\t\treturn\n\t}\n\n\tsessionID, err := uuid.FromString(sid)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(err.Error()).WithDebug(\"could not parse UUID\"))\n\t\treturn\n\t}\n\tif sessionID == s.ID {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(\"cannot revoke current session\").WithDebug(\"use the logout flow instead\"))\n\t\treturn\n\t}\n\n\tif err := h.r.SessionPersister().RevokeSession(r.Context(), s.Identity.ID, sessionID); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.r.Writer().WriteCode(w, r, http.StatusNoContent, nil)\n}\n\n// List My Session Parameters\n//\n// swagger:parameters listMySessions\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype listMySessionsParameters struct {\n\tmigrationpagination.RequestParameters\n\n\t// Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\n\t//\n\t// in: header\n\tSessionToken string `json:\"X-Session-Token\"`\n\n\t// Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\n\t// scenario you must include the HTTP Cookie Header which originally was included in the request to your server.\n\t// An example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\n\t//\n\t// It is ok if more than one cookie are included here as all other cookies will be ignored.\n\t//\n\t// in: header\n\tCookie string `json:\"Cookie\"`\n}\n\n// List My Session Response\n//\n// swagger:response listMySessions\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype listMySessionsResponse struct {\n\tmigrationpagination.ResponseHeaderAnnotation\n\n\t// in: body\n\tBody []Session\n}\n\n// swagger:route GET /sessions frontend listMySessions\n//\n// # Get My Active Sessions\n//\n// This endpoints returns all other active sessions that belong to the logged-in user.\n// The current session can be retrieved by calling the `/sessions/whoami` endpoint.\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: listMySessions\n//\t  400: errorGeneric\n//\t  401: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) listMySessions(w http.ResponseWriter, r *http.Request) {\n\ts, err := h.r.SessionManager().FetchFromRequest(r.Context(), r)\n\tif err != nil {\n\t\th.r.Logger().WithRequest(r).WithError(err).Info(\"No valid session cookie found.\")\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrUnauthorized.WithWrap(err).WithReasonf(\"No valid session cookie found.\")))\n\t\treturn\n\t}\n\n\tc := h.r.Config()\n\n\tvar aalErr *ErrAALNotSatisfied\n\tif err := h.r.SessionManager().DoesSessionSatisfy(r.Context(), s, c.SessionWhoAmIAAL(r.Context())); errors.As(err, &aalErr) {\n\t\th.r.Logger().WithRequest(r).WithError(err).Info(\"Session was found but AAL is not satisfied for calling this endpoint.\")\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t} else if err != nil {\n\t\th.r.Logger().WithRequest(r).WithError(err).Info(\"No valid session cookie found.\")\n\t\th.r.Writer().WriteError(w, r, herodot.ErrUnauthorized.WithWrap(err).WithReasonf(\"Unable to determine AAL.\"))\n\t\treturn\n\t}\n\n\tpage, perPage := x.ParsePagination(r)\n\tsess, total, err := h.r.SessionPersister().ListSessionsByIdentity(r.Context(), s.IdentityID, new(true), page, perPage, s.ID, ExpandEverything)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tx.PaginationHeader(w, *r.URL, total, page, perPage)\n\th.r.Writer().Write(w, r, sess)\n}\n\ntype sessionInContext int\n\nconst (\n\tsessionInContextKey sessionInContext = iota\n)\n\nfunc (h *Handler) IsAuthenticated(wrap http.HandlerFunc, onUnauthenticated http.HandlerFunc) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tctx := r.Context()\n\t\tsess, err := h.r.SessionManager().FetchFromRequest(ctx, r)\n\t\tif err != nil {\n\t\t\tif onUnauthenticated != nil {\n\t\t\t\tonUnauthenticated(w, r)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\th.r.Writer().WriteError(w, r, errors.WithStack(NewErrNoActiveSessionFound().WithReason(\"This endpoint can only be accessed with a valid session. Please log in and try again.\")))\n\t\t\treturn\n\t\t}\n\n\t\twrap(w, r.WithContext(context.WithValue(ctx, sessionInContextKey, sess)))\n\t}\n}\n\n// swagger:parameters extendSession\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype extendSession struct {\n\t// ID is the session's ID.\n\t//\n\t// required: true\n\t// in: path\n\tID string `json:\"id\"`\n}\n\n// swagger:route PATCH /admin/sessions/{id}/extend identity extendSession\n//\n// # Extend a Session\n//\n// Calling this endpoint extends the given session ID. If `session.earliest_possible_extend` is set it\n// will only extend the session after the specified time has passed.\n//\n// This endpoint returns per default a 204 No Content response on success. Older Ory Network projects may\n// return a 200 OK response with the session in the body. Returning the session as part of the response\n// will be deprecated in the future and should not be relied upon.\n//\n// This endpoint ignores consecutive requests to extend the same session and returns a 404 error in those\n// scenarios. This endpoint also returns 404 errors if the session does not exist.\n//\n// Retrieve the session ID from the `/sessions/whoami` endpoint / `toSession` SDK method.\n//\n//\tSchemes: http, https\n//\n//\tSecurity:\n//\t  oryAccessToken:\n//\n//\tResponses:\n//\t  200: session\n//\t  204: emptyResponse\n//\t  400: errorGeneric\n//\t  404: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-admin-high\nfunc (h *Handler) adminSessionExtend(w http.ResponseWriter, r *http.Request) {\n\tid, err := uuid.FromString(r.PathValue(\"id\"))\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithError(err.Error()).WithDebug(\"could not parse UUID\")))\n\t\treturn\n\t}\n\n\tc := h.r.Config()\n\tif err := h.r.SessionPersister().ExtendSession(r.Context(), id); err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\t// Default behavior going forward.\n\tif c.FeatureFlagFasterSessionExtend(r.Context()) {\n\t\tw.WriteHeader(http.StatusNoContent)\n\t\treturn\n\t}\n\n\ttrace.SpanFromContext(r.Context()).AddEvent(semconv.NewDeprecatedFeatureUsedEvent(r.Context(), \"legacy_slower_session_extend\"))\n\n\t// WARNING - this will be deprecated at some point!\n\ts, err := h.r.SessionPersister().GetSession(r.Context(), id, ExpandDefault)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\th.r.Writer().Write(w, r, s)\n}\n\nfunc (h *Handler) IsNotAuthenticated(wrap http.HandlerFunc, onAuthenticated http.HandlerFunc) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tif _, err := h.r.SessionManager().FetchFromRequest(r.Context(), r); err != nil {\n\t\t\tif e := new(ErrNoActiveSessionFound); errors.As(err, &e) {\n\t\t\t\twrap(w, r)\n\t\t\t\treturn\n\t\t\t}\n\t\t\th.r.Writer().WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\n\t\tif onAuthenticated != nil {\n\t\t\tonAuthenticated(w, r)\n\t\t\treturn\n\t\t}\n\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrForbidden.WithReason(\"This endpoint can only be accessed without a login session. Please log out and try again.\")))\n\t}\n}\n\nfunc RedirectOnAuthenticated(d interface{ config.Provider }) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tctx := r.Context()\n\t\treturnTo, err := redir.SecureRedirectTo(r, d.Config().SelfServiceBrowserDefaultReturnTo(ctx), redir.SecureRedirectAllowSelfServiceURLs(d.Config().SelfPublicURL(ctx)))\n\t\tif err != nil {\n\t\t\thttp.Redirect(w, r, d.Config().SelfServiceBrowserDefaultReturnTo(ctx).String(), http.StatusFound)\n\t\t\treturn\n\t\t}\n\n\t\thttp.Redirect(w, r, returnTo.String(), http.StatusFound)\n\t}\n}\n\nfunc RedirectOnUnauthenticated(to string) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\thttp.Redirect(w, r, to, http.StatusFound)\n\t}\n}\n\nfunc RespondWitherrorGenericOnAuthenticated(h herodot.Writer, err error) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\th.WriteError(w, r, err)\n\t}\n}\n\n// Exchange Session Token Parameters\n//\n// swagger:parameters exchangeSessionToken\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype exchangeSessionToken struct {\n\t// The part of the code return when initializing the flow.\n\t//\n\t// required: true\n\t// in: query\n\tInitCode string `json:\"init_code\"`\n\n\t// The part of the code returned by the return_to URL.\n\t//\n\t// required: true\n\t// in: query\n\tReturnToCode string `json:\"return_to_code\"`\n}\n\n// The Response for Registration Flows via API\n//\n// swagger:model successfulCodeExchangeResponse\ntype CodeExchangeResponse struct {\n\t// The Session Token\n\t//\n\t// A session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\n\t// Header:\n\t//\n\t// \t\tAuthorization: bearer ${session-token}\n\t//\n\t// The session token is only issued for API flows, not for Browser flows!\n\tToken string `json:\"session_token,omitempty\"`\n\n\t// The Session\n\t//\n\t// The session contains information about the user, the session device, and so on.\n\t// This is only available for API flows, not for Browser flows!\n\t//\n\t// required: true\n\tSession *Session `json:\"session\"`\n}\n\n// swagger:route GET /sessions/token-exchange frontend exchangeSessionToken\n//\n// # Exchange Session Token\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: successfulNativeLogin\n//\t  403: errorGeneric\n//\t  404: errorGeneric\n//\t  410: errorGeneric\n//\t  default: errorGeneric\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: kratos-public-medium\nfunc (h *Handler) exchangeCode(w http.ResponseWriter, r *http.Request) {\n\tvar (\n\t\tctx          = r.Context()\n\t\tinitCode     = r.URL.Query().Get(\"init_code\")\n\t\treturnToCode = r.URL.Query().Get(\"return_to_code\")\n\t)\n\n\tif initCode == \"\" || returnToCode == \"\" {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithReason(`\"init_code\" and \"return_to_code\" query params must be set`))\n\t\treturn\n\t}\n\n\te, err := h.r.SessionTokenExchangePersister().GetExchangerFromCode(ctx, initCode, returnToCode)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, herodot.ErrNotFound.WithReason(`no session yet for this \"code\"`))\n\t\treturn\n\t}\n\n\tsess, err := h.r.SessionPersister().GetSession(ctx, e.SessionID.UUID, ExpandDefault)\n\tif err != nil {\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\th.r.Writer().Write(w, r, &CodeExchangeResponse{\n\t\tToken:   sess.Token,\n\t\tSession: sess,\n\t})\n}\n"
  },
  {
    "path": "session/handler_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session_test\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/peterhellberg/link\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/corpx\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t. \"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/ioutilx\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/sqlcon\"\n\t\"github.com/ory/x/sqlxx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\nfunc send(code int) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, _ *http.Request) {\n\t\tw.WriteHeader(code)\n\t}\n}\n\nfunc TestSessionWhoAmI(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tts, _, r, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\tctx := context.Background()\n\n\temail := \"foo\" + uuid.Must(uuid.NewV4()).String() + \"@bar.sh\"\n\texternalID := x.NewUUID().String()\n\ti := &identity.Identity{\n\t\tID:         x.NewUUID(),\n\t\tExternalID: sqlxx.NullString(externalID),\n\t\tState:      identity.StateActive,\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tIdentifiers: []string{x.NewUUID().String()},\n\t\t\t\tConfig:      []byte(`{\"hashed_password\":\"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`),\n\t\t\t},\n\t\t},\n\t\tTraits:         identity.Traits(`{\"email\": \"` + email + `\",\"baz\":\"bar\",\"foo\":true,\"bar\":2.5}`),\n\t\tMetadataAdmin:  []byte(`{\"admin\":\"ma\"}`),\n\t\tMetadataPublic: []byte(`{\"public\":\"mp\"}`),\n\t\tRecoveryAddresses: []identity.RecoveryAddress{\n\t\t\t{\n\t\t\t\tValue: email,\n\t\t\t\tVia:   identity.AddressTypeEmail,\n\t\t\t},\n\t\t},\n\t\tVerifiableAddresses: []identity.VerifiableAddress{\n\t\t\t{\n\t\t\t\tValue: email,\n\t\t\t\tVia:   identity.AddressTypeEmail,\n\t\t\t},\n\t\t},\n\t}\n\th, _ := testhelpers.MockSessionCreateHandlerWithIdentity(t, reg, i)\n\n\tr.GET(\"/set\", h)\n\n\tt.Run(\"case=aal requirements\", func(t *testing.T) {\n\t\th1, _ := testhelpers.MockSessionCreateHandlerWithIdentityAndAMR(t, reg,\n\t\t\tnewAAL2Identity(),\n\t\t\t[]identity.CredentialsType{identity.CredentialsTypePassword, identity.CredentialsTypeWebAuthn})\n\t\tr.GET(\"/set/aal2-aal2\", h1)\n\n\t\th2, _ := testhelpers.MockSessionCreateHandlerWithIdentityAndAMR(t, reg,\n\t\t\tnewAAL2Identity(),\n\t\t\t[]identity.CredentialsType{identity.CredentialsTypePassword})\n\t\tr.GET(\"/set/aal2-aal1\", h2)\n\n\t\th3, _ := testhelpers.MockSessionCreateHandlerWithIdentityAndAMR(t, reg,\n\t\t\tnewAAL1Identity(),\n\t\t\t[]identity.CredentialsType{identity.CredentialsTypePassword})\n\t\tr.GET(\"/set/aal1-aal1\", h3)\n\n\t\trun := func(t *testing.T, endpoint string, kind string, code int) string {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\ttesthelpers.MockHydrateCookieClient(t, client, ts.URL+\"/set/\"+kind)\n\n\t\t\tres, err := client.Get(ts.URL + endpoint)\n\t\t\trequire.NoError(t, err)\n\t\t\tbody := x.MustReadAll(res.Body)\n\t\t\tassert.EqualValues(t, code, res.StatusCode)\n\t\t\treturn string(body)\n\t\t}\n\n\t\tfor k, e := range map[string]string{\n\t\t\t\"whoami\":     RouteWhoami,\n\t\t\t\"collection\": RouteCollection,\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"endpoint=%s\", k), func(t *testing.T) {\n\t\t\t\tt.Run(\"case=aal2-aal2\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\trun(t, e, \"aal2-aal2\", http.StatusOK)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=aal2-aal2\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\t\trun(t, e, \"aal2-aal2\", http.StatusOK)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=aal2-aal1\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\tbody := run(t, e, \"aal2-aal1\", http.StatusForbidden)\n\t\t\t\t\tassert.EqualValues(t, NewErrAALNotSatisfied(\"\").Reason(), gjson.Get(body, \"error.reason\").String(), body)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=aal2-aal1\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\t\trun(t, e, \"aal2-aal1\", http.StatusOK)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"case=aal1-aal1\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\trun(t, e, \"aal1-aal1\", http.StatusOK)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=http methods\", func(t *testing.T) {\n\t\trun := func(t *testing.T, cacheEnabled bool, maxAge time.Duration) {\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmICaching, cacheEnabled)\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmICachingMaxAge, maxAge)\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\t\t// No cookie yet -> 401\n\t\t\tres, err := client.Get(ts.URL + RouteWhoami)\n\t\t\trequire.NoError(t, err)\n\t\t\ttesthelpers.AssertNoCSRFCookieInResponse(t, res) // Test that no CSRF cookie is ever set here.\n\n\t\t\tif cacheEnabled {\n\t\t\t\tassert.NotEmpty(t, res.Header.Get(\"Ory-Session-Cache-For\"))\n\t\t\t\tassert.Equal(t, \"60\", res.Header.Get(\"Ory-Session-Cache-For\"))\n\t\t\t} else {\n\t\t\t\tassert.Empty(t, res.Header.Get(\"Ory-Session-Cache-For\"))\n\t\t\t}\n\n\t\t\t// Set cookie\n\t\t\treg.CSRFHandler().IgnorePath(\"/set\")\n\t\t\ttesthelpers.MockHydrateCookieClient(t, client, ts.URL+\"/set\")\n\n\t\t\t// Cookie set -> 200 (GET)\n\t\t\tfor _, method := range []string{\n\t\t\t\t\"GET\",\n\t\t\t\t\"POST\",\n\t\t\t\t\"PUT\",\n\t\t\t\t\"DELETE\",\n\t\t\t} {\n\t\t\t\tt.Run(\"http_method=\"+method, func(t *testing.T) {\n\t\t\t\t\treq, err := http.NewRequest(method, ts.URL+RouteWhoami, nil)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tres, err = client.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\ttesthelpers.AssertNoCSRFCookieInResponse(t, res) // Test that no CSRF cookie is ever set here.\n\n\t\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\tassert.NotEmpty(t, res.Header.Get(\"X-Kratos-Authenticated-Identity-Id\"))\n\n\t\t\t\t\tif cacheEnabled {\n\t\t\t\t\t\tvar expectedSeconds int\n\t\t\t\t\t\tif maxAge > 0 {\n\t\t\t\t\t\t\texpectedSeconds = int(maxAge.Seconds())\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\texpectedSeconds = int(conf.SessionLifespan(ctx).Seconds())\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassert.InDelta(t, expectedSeconds, x.Must(strconv.Atoi(res.Header.Get(\"Ory-Session-Cache-For\"))), 5)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Empty(t, res.Header.Get(\"Ory-Session-Cache-For\"))\n\t\t\t\t\t}\n\n\t\t\t\t\tassert.Empty(t, gjson.GetBytes(body, \"identity.credentials\"))\n\t\t\t\t\tassert.Equal(t, \"mp\", gjson.GetBytes(body, \"identity.metadata_public.public\").String(), \"%s\", body)\n\t\t\t\t\tassert.False(t, gjson.GetBytes(body, \"identity.metadata_admin\").Exists())\n\n\t\t\t\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"identity.recovery_addresses\").String(), \"%s\", body)\n\t\t\t\t\tassert.NotEmpty(t, gjson.GetBytes(body, \"identity.verifiable_addresses\").String(), \"%s\", body)\n\n\t\t\t\t\tassert.Equal(t, externalID, gjson.GetBytes(body, \"identity.external_id\").String(), \"%s\", body)\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tt.Run(\"cache disabled\", func(t *testing.T) {\n\t\t\trun(t, false, 0)\n\t\t})\n\n\t\tt.Run(\"cache enabled\", func(t *testing.T) {\n\t\t\trun(t, true, 0)\n\t\t})\n\n\t\tt.Run(\"cache enabled with max age\", func(t *testing.T) {\n\t\t\trun(t, true, time.Minute)\n\t\t})\n\t})\n\n\tt.Run(\"tokenize\", func(t *testing.T) {\n\t\tconf.MustSet(t.Context(), config.ViperKeySessionTokenizerTemplates+\".es256\", &config.SessionTokenizeFormat{\n\t\t\tTTL:     time.Minute,\n\t\t\tJWKSURL: \"file://stub/jwk.es256.json\",\n\t\t})\n\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmICaching, true)\n\n\t\th3, _ := testhelpers.MockSessionCreateHandlerWithIdentityAndAMR(t, reg, newAAL1Identity(), []identity.CredentialsType{identity.CredentialsTypePassword})\n\t\tr.GET(\"/set/tokenize\", h3)\n\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\ttesthelpers.MockHydrateCookieClient(t, client, ts.URL+\"/set/\"+\"tokenize\")\n\n\t\tres, err := client.Get(ts.URL + RouteWhoami + \"?tokenize_as=es256\")\n\t\trequire.NoError(t, err)\n\t\tbody := x.MustReadAll(res.Body)\n\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode, string(body))\n\n\t\ttoken := gjson.GetBytes(body, \"tokenized\").String()\n\t\trequire.NotEmpty(t, token)\n\t\tsegments := strings.Split(token, \".\")\n\t\trequire.Len(t, segments, 3, token)\n\t\tdecoded, err := base64.RawURLEncoding.DecodeString(segments[1])\n\t\trequire.NoError(t, err)\n\n\t\tassert.NotEmpty(t, gjson.GetBytes(decoded, \"sub\").Str, decoded)\n\t\tassert.Empty(t, res.Header.Get(\"Ory-Session-Cache-For\"))\n\t})\n\n\t/*\n\t\tt.Run(\"case=respects AAL config\", func(t *testing.T) {\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionLifespan, \"1m\")\n\n\t\t\tt.Run(\"required_aal=aal1\", func(t *testing.T) {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySelfServiceSettingsRequiredAAL, \"aal1\")\n\n\t\t\t\ti := identity.Identity{Traits: []byte(\"{}\"), State: identity.StateActive}\n\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &i))\n\t\t\t\ts, err := testhelpers.NewActiveSession(&i, conf, time.Now(), identity.CredentialsTypePassword)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), s))\n\t\t\t\trequire.NotEmpty(t, s.Token)\n\n\t\t\t\treq, err := http.NewPostRequest(\"GET\", pts.URL+\"/session/get\", nil)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\treq.Header.Set(\"Authorization\", \"Bearer \"+s.Token)\n\n\t\t\t\tc := http.DefaultClient\n\t\t\t\tres, err := c.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\t})\n\n\t\t\tt.Run(\"required_aal=aal2\", func(t *testing.T) {\n\t\t\t\tidAAL2 := identity.Identity{Traits: []byte(\"{}\"), State: identity.StateActive, Credentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {Type: identity.CredentialsTypePassword, Config: []byte(\"{}\")},\n\t\t\t\t\tidentity.CredentialsTypeWebAuthn: {Type: identity.CredentialsTypeWebAuthn, Config: []byte(\"{}\")},\n\t\t\t\t}}\n\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &idAAL2))\n\n\t\t\t\tidAAL1 := identity.Identity{Traits: []byte(\"{}\"), State: identity.StateActive, Credentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\t\tidentity.CredentialsTypePassword: {Type: identity.CredentialsTypePassword, Config: []byte(\"{}\")},\n\t\t\t\t}}\n\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &idAAL1))\n\n\t\t\t\trun := func(t *testing.T, complete []identity.CredentialsType, expectedCode int, i *identity.Identity) {\n\n\t\t\t\t\ts := session.NewInactiveSession()\n\t\t\t\t\tfor _, m := range complete {\n\t\t\t\t\t\ts.CompletedLoginFor(m)\n\t\t\t\t\t}\n\t\t\t\t\trequire.NoError(t, s.Activate(i, conf, time.Now().UTC()))\n\n\t\t\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), s))\n\t\t\t\t\trequire.NotEmpty(t, s.Token)\n\n\t\t\t\t\treq, err := http.NewPostRequest(\"GET\", pts.URL+\"/session/get\", nil)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\treq.Header.Set(\"Authorization\", \"Bearer \"+s.Token)\n\n\t\t\t\t\tc := http.DefaultClient\n\t\t\t\t\tres, err := c.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.EqualValues(t, expectedCode, res.StatusCode)\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"fulfilled for aal2 if identity has aal2\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword, identity.CredentialsTypeWebAuthn}, 200, &idAAL2)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"rejected for aal1 if identity has aal2\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword}, 403, &idAAL2)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"fulfilled for aal1 if identity has aal2 but config is aal1\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, \"aal1\")\n\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword}, 200, &idAAL2)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"fulfilled for aal2 if identity has aal1\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword, identity.CredentialsTypeWebAuthn}, 200, &idAAL1)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"fulfilled for aal1 if identity has aal1\", func(t *testing.T) {\n\t\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionWhoAmIAAL, config.HighestAvailableAAL)\n\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword}, 200, &idAAL1)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t*/\n}\n\nfunc TestIsNotAuthenticatedSecurecookie(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tr := httprouterx.NewTestRouterPublic(t)\n\tr.GET(\"/public/with-callback\", reg.SessionHandler().IsNotAuthenticated(send(http.StatusOK), send(http.StatusBadRequest)))\n\n\tts := httptest.NewServer(r)\n\tdefer ts.Close()\n\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)\n\n\tc := testhelpers.NewClientWithCookies(t)\n\tc.Jar.SetCookies(urlx.ParseOrPanic(ts.URL), []*http.Cookie{\n\t\t{\n\t\t\tName: config.DefaultSessionCookieName,\n\t\t\t// This is an invalid cookie because it is generated by a very random secret\n\t\t\tValue:    \"MTU3Mjg4Njg0MXxEdi1CQkFFQ180SUFBUkFCRUFBQU52LUNBQUVHYzNSeWFXNW5EQVVBQTNOcFpBWnpkSEpwYm1jTUd3QVpUWFZXVUhSQlZVeExXRWRUUmxkVVoyUkpUVXhzY201SFNBPT187kdI3dMP-ep389egDR2TajYXGG-6xqC2mAlgnBi0vsg=\",\n\t\t\tHttpOnly: true,\n\t\t\tPath:     \"/\",\n\t\t\tExpires:  time.Now().Add(time.Hour),\n\t\t},\n\t})\n\n\tres, err := c.Get(ts.URL + \"/public/with-callback\")\n\trequire.NoError(t, err)\n\n\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n}\n\nfunc TestIsNotAuthenticated(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\tr := httprouterx.NewTestRouterPublic(t)\n\n\treg.WithCSRFHandler(new(nosurfx.FakeCSRFHandler))\n\th, _ := testhelpers.MockSessionCreateHandler(t, reg)\n\tr.GET(\"/set\", h)\n\tr.GET(\"/public/with-callback\", reg.SessionHandler().IsNotAuthenticated(send(http.StatusOK), send(http.StatusBadRequest)))\n\tr.GET(\"/public/without-callback\", reg.SessionHandler().IsNotAuthenticated(send(http.StatusOK), nil))\n\tts := httptest.NewServer(r)\n\tdefer ts.Close()\n\n\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)\n\n\tsessionClient := testhelpers.NewClientWithCookies(t)\n\ttesthelpers.MockHydrateCookieClient(t, sessionClient, ts.URL+\"/set\")\n\n\tfor k, tc := range []struct {\n\t\tc    *http.Client\n\t\tcall string\n\t\tcode int\n\t}{\n\t\t{\n\t\t\tc:    sessionClient,\n\t\t\tcall: \"/public/with-callback\",\n\t\t\tcode: http.StatusBadRequest,\n\t\t},\n\t\t{\n\t\t\tc:    http.DefaultClient,\n\t\t\tcall: \"/public/with-callback\",\n\t\t\tcode: http.StatusOK,\n\t\t},\n\n\t\t{\n\t\t\tc:    sessionClient,\n\t\t\tcall: \"/public/without-callback\",\n\t\t\tcode: http.StatusForbidden,\n\t\t},\n\t\t{\n\t\t\tc:    http.DefaultClient,\n\t\t\tcall: \"/public/without-callback\",\n\t\t\tcode: http.StatusOK,\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tres, err := tc.c.Get(ts.URL + tc.call)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.EqualValues(t, tc.code, res.StatusCode)\n\t\t})\n\t}\n}\n\nfunc TestIsAuthenticated(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\treg.WithCSRFHandler(new(nosurfx.FakeCSRFHandler))\n\tr := httprouterx.NewTestRouterPublic(t)\n\n\th, _ := testhelpers.MockSessionCreateHandler(t, reg)\n\tr.GET(\"/set\", h)\n\tr.GET(\"/privileged/with-callback\", reg.SessionHandler().IsAuthenticated(send(http.StatusOK), send(http.StatusBadRequest)))\n\tr.GET(\"/privileged/without-callback\", reg.SessionHandler().IsAuthenticated(send(http.StatusOK), nil))\n\tts := httptest.NewServer(r)\n\tdefer ts.Close()\n\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)\n\n\tsessionClient := testhelpers.NewClientWithCookies(t)\n\ttesthelpers.MockHydrateCookieClient(t, sessionClient, ts.URL+\"/set\")\n\n\tfor k, tc := range []struct {\n\t\tc    *http.Client\n\t\tcall string\n\t\tcode int\n\t}{\n\t\t{\n\t\t\tc:    sessionClient,\n\t\t\tcall: \"/privileged/with-callback\",\n\t\t\tcode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tc:    http.DefaultClient,\n\t\t\tcall: \"/privileged/with-callback\",\n\t\t\tcode: http.StatusBadRequest,\n\t\t},\n\n\t\t{\n\t\t\tc:    sessionClient,\n\t\t\tcall: \"/privileged/without-callback\",\n\t\t\tcode: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tc:    http.DefaultClient,\n\t\t\tcall: \"/privileged/without-callback\",\n\t\t\tcode: http.StatusUnauthorized,\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tres, err := tc.c.Get(ts.URL + tc.call)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.EqualValues(t, tc.code, res.StatusCode)\n\t\t})\n\t}\n}\n\nfunc TestHandlerAdminSessionManagement(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")))\n\tpublic, ts := testhelpers.NewKratosServer(t, reg)\n\n\tt.Run(\"case=should return 202 after invalidating all sessions\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tvar s *Session\n\t\trequire.NoError(t, faker.FakeData(&s))\n\t\ts.Active = true\n\t\ts.AMR = AuthenticationMethods{\n\t\t\t{Method: identity.CredentialsTypePassword, CompletedAt: time.Now().UTC().Round(time.Second)},\n\t\t\t{Method: identity.CredentialsTypeOIDC, CompletedAt: time.Now().UTC().Round(time.Second)},\n\t\t}\n\t\trequire.NoError(t, reg.Persister().CreateIdentity(t.Context(), s.Identity))\n\n\t\tvar expectedSessionDevice Device\n\t\trequire.NoError(t, faker.FakeData(&expectedSessionDevice))\n\t\ts.Devices = []Device{\n\t\t\texpectedSessionDevice,\n\t\t}\n\n\t\tassert.Zero(t, s.ID)\n\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), s))\n\t\tassert.NotZero(t, s.ID)\n\t\tassert.NotZero(t, s.Identity.ID)\n\n\t\tt.Run(\"get session\", func(t *testing.T) {\n\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/sessions/\"+s.ID.String(), nil)\n\t\t\tres, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tvar session Session\n\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&session))\n\t\t\tassert.Equal(t, s.ID, session.ID)\n\t\t\tassert.Nil(t, session.Identity)\n\t\t\tassert.Empty(t, session.Devices)\n\t\t})\n\n\t\tt.Run(\"get session expand\", func(t *testing.T) {\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdescription        string\n\t\t\t\texpand             string\n\t\t\t\texpectedIdentityId string\n\t\t\t\texpectedDevices    int\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdescription:        \"expand Identity\",\n\t\t\t\t\texpand:             \"?expand=Identity\",\n\t\t\t\t\texpectedIdentityId: s.Identity.ID.String(),\n\t\t\t\t\texpectedDevices:    0,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdescription:        \"expand Devices\",\n\t\t\t\t\texpand:             \"/?expand=Devices\",\n\t\t\t\t\texpectedIdentityId: \"\",\n\t\t\t\t\texpectedDevices:    1,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdescription:        \"expand Identity and Devices\",\n\t\t\t\t\texpand:             \"/?expand=Identity&expand=Devices\",\n\t\t\t\t\texpectedIdentityId: s.Identity.ID.String(),\n\t\t\t\t\texpectedDevices:    1,\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(fmt.Sprintf(\"description=%s\", tc.description), func(t *testing.T) {\n\t\t\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/sessions/\"+s.ID.String()+tc.expand, nil)\n\t\t\t\t\tres, err := client.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\t\trequire.Equalf(t, http.StatusOK, res.StatusCode, \"%s\", body)\n\n\t\t\t\t\tassert.Equal(t, s.ID.String(), gjson.GetBytes(body, \"id\").String())\n\t\t\t\t\tassert.Equal(t, tc.expectedIdentityId, gjson.GetBytes(body, \"identity.id\").String())\n\t\t\t\t\tassert.EqualValuesf(t, tc.expectedDevices, gjson.GetBytes(body, \"devices.#\").Int(), \"%s\", gjson.GetBytes(body, \"devices\").Raw)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"get session expand invalid\", func(t *testing.T) {\n\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/sessions/\"+s.ID.String()+\"/?expand=invalid\", nil)\n\t\t\tres, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"should redirect to public for whoami\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewHTTPClientWithSessionToken(t.Context(), t, reg, s)\n\t\t\tclient.CheckRedirect = func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\n\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", ts.URL+\"/admin/sessions/whoami\", nil)\n\t\t\tres, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, http.StatusTemporaryRedirect, res.StatusCode)\n\t\t\trequire.Equal(t, public.URL+\"/sessions/whoami\", res.Header.Get(\"Location\"))\n\t\t})\n\n\t\tassertPageToken := func(t *testing.T, id, linkHeader string) {\n\t\t\tt.Helper()\n\n\t\t\tg := link.Parse(linkHeader)\n\t\t\trequire.Len(t, g, 1)\n\t\t\tu, err := url.Parse(g[\"first\"].URI)\n\t\t\trequire.NoError(t, err)\n\t\t\tpt, err := keysetpagination.NewMapPageToken(u.Query().Get(\"page_token\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tmpt := pt.(keysetpagination.MapPageToken)\n\t\t\tassert.Equal(t, id, mpt[\"id\"])\n\t\t}\n\n\t\tt.Run(\"list sessions\", func(t *testing.T) {\n\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/sessions/\", nil)\n\t\t\tres, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tassertPageToken(t, uuid.Nil.String(), res.Header.Get(\"Link\"))\n\n\t\t\tvar sessions []Session\n\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&sessions))\n\t\t\trequire.Len(t, sessions, 1)\n\t\t\tassert.Equal(t, s.ID, sessions[0].ID)\n\t\t\tassert.Empty(t, sessions[0].Identity)\n\t\t\tassert.Empty(t, sessions[0].Devices)\n\t\t})\n\n\t\tt.Run(\"list sessions expand\", func(t *testing.T) {\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdescription          string\n\t\t\t\texpand               string\n\t\t\t\texpectedIdentityId   string\n\t\t\t\texpectedDevicesCount string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdescription:          \"expand nothing\",\n\t\t\t\t\texpand:               \"\",\n\t\t\t\t\texpectedIdentityId:   \"\",\n\t\t\t\t\texpectedDevicesCount: \"\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdescription:          \"expand Identity\",\n\t\t\t\t\texpand:               \"expand=identity&\",\n\t\t\t\t\texpectedIdentityId:   s.Identity.ID.String(),\n\t\t\t\t\texpectedDevicesCount: \"\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdescription:          \"expand Devices\",\n\t\t\t\t\texpand:               \"expand=devices&\",\n\t\t\t\t\texpectedIdentityId:   \"\",\n\t\t\t\t\texpectedDevicesCount: \"1\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdescription:          \"expand Identity and Devices\",\n\t\t\t\t\texpand:               \"expand=identity&expand=devices&\",\n\t\t\t\t\texpectedIdentityId:   s.Identity.ID.String(),\n\t\t\t\t\texpectedDevicesCount: \"1\",\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(fmt.Sprintf(\"description=%s\", tc.description), func(t *testing.T) {\n\t\t\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/sessions?\"+tc.expand, nil)\n\t\t\t\t\tres, err := client.Do(req)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\t\t\tassertPageToken(t, uuid.Nil.String(), res.Header.Get(\"Link\"))\n\n\t\t\t\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\t\t\t\tassert.Equal(t, s.ID.String(), gjson.GetBytes(body, \"0.id\").String())\n\t\t\t\t\tassert.Equal(t, tc.expectedIdentityId, gjson.GetBytes(body, \"0.identity.id\").String())\n\t\t\t\t\tassert.Equal(t, tc.expectedDevicesCount, gjson.GetBytes(body, \"0.devices.#\").String())\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"should list sessions for an identity\", func(t *testing.T) {\n\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/identities/\"+s.Identity.ID.String()+\"/sessions\", nil)\n\t\t\tres, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tvar sessions []Session\n\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&sessions))\n\t\t\trequire.Len(t, sessions, 1)\n\t\t\tassert.Equal(t, s.ID, sessions[0].ID)\n\t\t})\n\n\t\tt.Run(\"should revoke session by id\", func(t *testing.T) {\n\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/sessions/\"+s.ID.String(), nil)\n\t\t\tres, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tvar session Session\n\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&session))\n\t\t\tassert.Equal(t, s.ID, session.ID)\n\t\t\tassert.True(t, session.Active)\n\n\t\t\treq, _ = http.NewRequest(\"DELETE\", ts.URL+\"/admin/sessions/\"+s.ID.String(), nil)\n\t\t\tres, err = client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\t\treq, _ = http.NewRequest(\"GET\", ts.URL+\"/admin/sessions/\"+s.ID.String(), nil)\n\t\t\tres, err = client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&session))\n\t\t\tassert.Equal(t, s.ID, session.ID)\n\t\t\tassert.False(t, session.Active)\n\t\t})\n\n\t\tt.Run(\"case=session status should be false when session expiry is past\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\t\ts.ExpiresAt = time.Now().Add(-time.Hour * 1)\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), s))\n\n\t\t\tassert.NotEqual(t, uuid.Nil, s.ID)\n\t\t\tassert.NotEqual(t, uuid.Nil, s.Identity.ID)\n\n\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/sessions/\"+s.ID.String(), nil)\n\t\t\tres, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"false\", gjson.GetBytes(body, \"active\").String(), \"%s\", body)\n\t\t})\n\n\t\tt.Run(\"case=session status should be false for inactive identity\", func(t *testing.T) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\tvar s1 *Session\n\t\t\trequire.NoError(t, faker.FakeData(&s1))\n\t\t\ts1.Active = true\n\t\t\ts1.Identity.State = identity.StateInactive\n\t\t\trequire.NoError(t, reg.Persister().CreateIdentity(t.Context(), s1.Identity))\n\n\t\t\tassert.Equal(t, uuid.Nil, s1.ID)\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), s1))\n\t\t\tassert.NotEqual(t, uuid.Nil, s1.ID)\n\t\t\tassert.NotEqual(t, uuid.Nil, s1.Identity.ID)\n\n\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/sessions/\"+s1.ID.String()+\"?expand=Identity\", nil)\n\t\t\tres, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"false\", gjson.GetBytes(body, \"active\").String(), \"%s\", body)\n\t\t})\n\n\t\treq, _ := http.NewRequest(\"DELETE\", ts.URL+\"/admin/identities/\"+s.Identity.ID.String()+\"/sessions\", nil)\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\t_, err = reg.SessionPersister().GetSession(t.Context(), s.ID, ExpandNothing)\n\t\trequire.True(t, errors.Is(err, sqlcon.ErrNoRows))\n\n\t\tt.Run(\"should not list session\", func(t *testing.T) {\n\t\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/admin/identities/\"+s.Identity.ID.String()+\"/sessions\", nil)\n\t\t\tres, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.JSONEq(t, \"[]\", string(ioutilx.MustReadAll(res.Body)))\n\t\t})\n\t})\n\n\tt.Run(\"case=should return 400 when bad UUID is sent\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\tfor _, method := range []string{http.MethodGet, http.MethodDelete} {\n\t\t\tt.Run(\"http method=\"+method, func(t *testing.T) {\n\t\t\t\treq, _ := http.NewRequest(method, ts.URL+\"/admin/identities/BADUUID/sessions\", nil)\n\t\t\t\tres, err := client.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=should return 404 when deleting with unknown UUID\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsomeID, _ := uuid.NewV4()\n\t\treq, _ := http.NewRequest(\"DELETE\", ts.URL+\"/admin/identities/\"+someID.String()+\"/sessions\", nil)\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusNotFound, res.StatusCode)\n\t})\n\n\tt.Run(\"case=should return pagination headers on list response\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tvar i *identity.Identity\n\t\trequire.NoError(t, faker.FakeData(&i))\n\t\trequire.NoError(t, reg.Persister().CreateIdentity(t.Context(), i))\n\n\t\tnumSessions := 5\n\t\tnumSessionsActive := 2\n\n\t\tsess := make([]Session, numSessions)\n\t\tfor j := range sess {\n\t\t\trequire.NoError(t, faker.FakeData(&sess[j]))\n\t\t\tsess[j].Identity = i\n\t\t\tif j < numSessionsActive {\n\t\t\t\tsess[j].Active = true\n\t\t\t\tsess[j].ExpiresAt = time.Now().UTC().Add(time.Hour)\n\t\t\t} else {\n\t\t\t\tsess[j].Active = false\n\t\t\t\tsess[j].ExpiresAt = time.Now().UTC().Add(-time.Hour)\n\t\t\t}\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), &sess[j]))\n\t\t}\n\n\t\tfor _, tc := range []struct {\n\t\t\tactiveOnly         string\n\t\t\texpectedSessionIds []uuid.UUID\n\t\t}{\n\t\t\t{\n\t\t\t\tactiveOnly:         \"true\",\n\t\t\t\texpectedSessionIds: []uuid.UUID{sess[0].ID, sess[1].ID},\n\t\t\t},\n\t\t\t{\n\t\t\t\tactiveOnly:         \"false\",\n\t\t\t\texpectedSessionIds: []uuid.UUID{sess[2].ID, sess[3].ID, sess[4].ID},\n\t\t\t},\n\t\t\t{\n\t\t\t\tactiveOnly:         \"\",\n\t\t\t\texpectedSessionIds: []uuid.UUID{sess[0].ID, sess[1].ID, sess[2].ID, sess[3].ID, sess[4].ID},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"active=%#v\", tc.activeOnly), func(t *testing.T) {\n\t\t\t\tsessions, _, err := reg.SessionPersister().ListSessionsByIdentity(t.Context(), i.ID, nil, 1, 10, uuid.Nil, ExpandEverything)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Len(t, sessions, 5)\n\t\t\t\tassert.True(t, sort.IsSorted(sort.Reverse(byCreatedAt(sessions))))\n\n\t\t\t\treqURL := ts.URL + \"/admin/identities/\" + i.ID.String() + \"/sessions\"\n\t\t\t\tif tc.activeOnly != \"\" {\n\t\t\t\t\treqURL += \"?active=\" + tc.activeOnly\n\t\t\t\t}\n\t\t\t\treq, _ := http.NewRequest(\"GET\", reqURL, nil)\n\t\t\t\tres, err := client.Do(req)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\t\t\tvar actualSessions []Session\n\t\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&actualSessions))\n\t\t\t\tactualSessionIds := make([]uuid.UUID, 0)\n\t\t\t\tfor _, s := range actualSessions {\n\t\t\t\t\tactualSessionIds = append(actualSessionIds, s.ID)\n\t\t\t\t}\n\n\t\t\t\tassert.NotEqual(t, \"\", res.Header.Get(\"Link\"))\n\t\t\t\tassert.ElementsMatch(t, tc.expectedSessionIds, actualSessionIds)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestHandlerSelfServiceSessionManagement(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t)\n\tts, _, r, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\tvar setup func(t *testing.T) (*http.Client, *identity.Identity, *Session)\n\t{\n\t\t// we limit the scope of the channels, so you cannot accidentally mess up a test case\n\t\tident := make(chan *identity.Identity, 1)\n\t\tsess := make(chan *Session, 1)\n\t\tr.GET(\"/set\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\th, s := testhelpers.MockSessionCreateHandlerWithIdentity(t, reg, <-ident)\n\t\t\th(w, r)\n\t\t\tsess <- s\n\t\t})\n\n\t\tsetup = func(t *testing.T) (*http.Client, *identity.Identity, *Session) {\n\t\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\t\ti := identity.NewIdentity(\"\") // the identity is created by the handler\n\n\t\t\tident <- i\n\t\t\ttesthelpers.MockHydrateCookieClient(t, client, ts.URL+\"/set\")\n\t\t\treturn client, i, <-sess\n\t\t}\n\t}\n\n\tt.Run(\"case=list should return pagination headers\", func(t *testing.T) {\n\t\tclient, i, _ := setup(t)\n\n\t\tnumSessions := 5\n\t\tnumSessionsActive := 2\n\n\t\tsess := make([]Session, numSessions)\n\t\tfor j := range sess {\n\t\t\trequire.NoError(t, faker.FakeData(&sess[j]))\n\t\t\tsess[j].Identity = i\n\t\t\tif j < numSessionsActive {\n\t\t\t\tsess[j].Active = true\n\t\t\t} else {\n\t\t\t\tsess[j].Active = false\n\t\t\t}\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), &sess[j]))\n\t\t}\n\n\t\treqURL := ts.URL + \"/sessions\"\n\t\treq, _ := http.NewRequest(\"GET\", reqURL, nil)\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\trequire.NotEqual(t, \"\", res.Header.Get(\"Link\"))\n\t})\n\n\tt.Run(\"case=should return 200 and number after invalidating all other sessions\", func(t *testing.T) {\n\t\tclient, i, currSess := setup(t)\n\n\t\totherSess := Session{}\n\t\trequire.NoError(t, faker.FakeData(&otherSess))\n\t\totherSess.Identity = i\n\t\totherSess.Active = true\n\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), &otherSess))\n\n\t\treq, _ := http.NewRequest(\"DELETE\", ts.URL+\"/sessions\", nil)\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, int64(1), gjson.GetBytes(body, \"count\").Int(), \"%s\", body)\n\n\t\tactualOther, err := reg.SessionPersister().GetSession(t.Context(), otherSess.ID, ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.False(t, actualOther.Active)\n\n\t\tactualCurr, err := reg.SessionPersister().GetSession(t.Context(), currSess.ID, ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, actualCurr.Active)\n\t})\n\n\tt.Run(\"case=should revoke specific other session\", func(t *testing.T) {\n\t\tclient, i, _ := setup(t)\n\n\t\tothers := make([]Session, 2)\n\t\tfor j := range others {\n\t\t\trequire.NoError(t, faker.FakeData(&others[j]))\n\t\t\tothers[j].Identity = i\n\t\t\tothers[j].Active = true\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), &others[j]))\n\t\t}\n\n\t\treq, _ := http.NewRequest(\"DELETE\", ts.URL+\"/sessions/\"+others[0].ID.String(), nil)\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusNoContent, res.StatusCode)\n\n\t\tactualOthers, total, err := reg.SessionPersister().ListSessionsByIdentity(t.Context(), i.ID, nil, 1, 10, uuid.Nil, ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, actualOthers, 3)\n\t\trequire.EqualValues(t, 3, total)\n\n\t\tfor _, s := range actualOthers {\n\t\t\tif s.ID == others[0].ID {\n\t\t\t\tassert.False(t, s.Active)\n\t\t\t} else {\n\t\t\t\tassert.True(t, s.Active)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"case=should not revoke current session\", func(t *testing.T) {\n\t\tclient, _, currSess := setup(t)\n\n\t\treq, _ := http.NewRequest(\"DELETE\", ts.URL+\"/sessions/\"+currSess.ID.String(), nil)\n\t\tresp, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusBadRequest, resp.StatusCode)\n\t})\n\n\tt.Run(\"case=should not error on unknown or revoked session\", func(t *testing.T) {\n\t\tclient, i, _ := setup(t)\n\n\t\totherSess := Session{}\n\t\trequire.NoError(t, faker.FakeData(&otherSess))\n\t\totherSess.Identity = i\n\t\totherSess.Active = false\n\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(t.Context(), &otherSess))\n\n\t\tfor j, id := range []uuid.UUID{otherSess.ID, uuid.Must(uuid.NewV4())} {\n\t\t\treq, _ := http.NewRequest(\"DELETE\", ts.URL+\"/sessions/\"+id.String(), nil)\n\t\t\tresp, err := client.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, http.StatusNoContent, resp.StatusCode, \"case=%d\", j)\n\t\t}\n\t})\n\n\tt.Run(\"case=whoami should not issue cookie for up to date session\", func(t *testing.T) {\n\t\tclient, _, _ := setup(t)\n\n\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/sessions/whoami\", nil)\n\t\tresp, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\n\t\tassert.Empty(t, resp.Cookies())\n\t})\n\n\tt.Run(\"case=whoami should reissue cookie for outdated session\", func(t *testing.T) {\n\t\tclient, _, session := setup(t)\n\t\toldExpires := session.ExpiresAt\n\n\t\tsession.ExpiresAt = time.Now().Add(time.Hour * 24 * 30).UTC().Round(time.Hour)\n\t\terr := reg.SessionPersister().UpsertSession(context.Background(), session)\n\t\trequire.NoError(t, err)\n\n\t\tresp, err := client.Get(ts.URL + \"/sessions/whoami\")\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\n\t\trequire.Len(t, resp.Cookies(), 1)\n\t\tfor _, c := range resp.Cookies() {\n\t\t\tassert.WithinDuration(t, session.ExpiresAt, c.Expires, 5*time.Second, \"Ensure the expiry does not deviate +- 5 seconds from the expiry of the session for cookie: %s\", c.Name)\n\t\t\tassert.NotEqual(t, oldExpires, c.Expires, \"%s\", c.Name)\n\t\t}\n\t})\n\n\tt.Run(\"case=whoami should not issue cookie if request is token based\", func(t *testing.T) {\n\t\t_, _, session := setup(t)\n\n\t\tsession.ExpiresAt = time.Now().Add(time.Hour * 24 * 30).UTC().Round(time.Hour)\n\t\terr := reg.SessionPersister().UpsertSession(context.Background(), session)\n\t\trequire.NoError(t, err)\n\n\t\treq, err := http.NewRequest(\"GET\", ts.URL+\"/sessions/whoami\", nil)\n\t\trequire.NoError(t, err)\n\t\treq.Header.Set(\"Authorization\", \"Bearer \"+session.Token)\n\n\t\tresp, err := http.DefaultClient.Do(req)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\n\t\trequire.Len(t, resp.Cookies(), 0)\n\t})\n}\n\nfunc TestHandlerRefreshSessionBySessionID(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t)\n\tpublicServer, adminServer, _, _ := testhelpers.NewKratosServerWithCSRFAndRouters(t, reg)\n\n\ti := identity.NewIdentity(\"\")\n\trequire.NoError(t, reg.IdentityManager().Create(context.Background(), i))\n\ts := &Session{Identity: i, ExpiresAt: time.Now().Add(5 * time.Minute)}\n\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), s))\n\n\tt.Run(\"case=should return 200 after refreshing one session\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\n\t\treq, _ := http.NewRequest(\"PATCH\", adminServer.URL+\"/admin/sessions/\"+s.ID.String()+\"/extend\", nil)\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusOK, res.StatusCode)\n\n\t\tupdatedSession, err := reg.SessionPersister().GetSession(context.Background(), s.ID, ExpandNothing)\n\t\trequire.Nil(t, err)\n\t\trequire.True(t, s.ExpiresAt.Before(updatedSession.ExpiresAt))\n\t})\n\n\tt.Run(\"case=should return 400 when bad UUID is sent\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\treq, _ := http.NewRequest(\"PATCH\", adminServer.URL+\"/admin/sessions/BADUUID/extend\", nil)\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusBadRequest, res.StatusCode)\n\t})\n\n\tt.Run(\"case=should return 404 when calling with missing UUID\", func(t *testing.T) {\n\t\tclient := testhelpers.NewClientWithCookies(t)\n\t\tsomeID, _ := uuid.NewV4()\n\t\treq, _ := http.NewRequest(\"PATCH\", adminServer.URL+\"/admin/sessions/\"+someID.String()+\"/extend\", nil)\n\t\tres, err := client.Do(req)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, http.StatusNotFound, res.StatusCode)\n\t})\n\n\tt.Run(\"case=should return 404 when calling puplic server\", func(t *testing.T) {\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"PATCH\", publicServer.URL+\"/sessions/\"+s.ID.String()+\"/extend\", nil)\n\n\t\tres, err := publicServer.Client().Do(req)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, http.StatusNotFound, res.StatusCode)\n\t\tbody := ioutilx.MustReadAll(res.Body)\n\t\tassert.NotEqual(t, gjson.GetBytes(body, \"error.id\").String(), \"security_csrf_violation\")\n\t})\n}\n\ntype byCreatedAt []Session\n\nfunc (s byCreatedAt) Len() int      { return len(s) }\nfunc (s byCreatedAt) Swap(i, j int) { s[i], s[j] = s[j], s[i] }\nfunc (s byCreatedAt) Less(i, j int) bool {\n\treturn s[i].CreatedAt.Before(s[j].CreatedAt)\n}\n"
  },
  {
    "path": "session/helper.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc bearerTokenFromRequest(r *http.Request) (string, bool) {\n\tparts := strings.Split(r.Header.Get(\"Authorization\"), \" \")\n\n\tif len(parts) == 2 && strings.ToLower(parts[0]) == \"bearer\" {\n\t\treturn parts[1], true\n\t}\n\n\treturn \"\", false\n}\n"
  },
  {
    "path": "session/helper_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestBearerTokenFromRequest(t *testing.T) {\n\tfor k, tc := range []struct {\n\t\th http.Header\n\t\tt string\n\t\tf bool\n\t}{\n\t\t{\n\t\t\th: http.Header{\"Authorization\": {\"Bearer token\"}},\n\t\t\tt: \"token\", f: true,\n\t\t},\n\t\t{\n\t\t\th: http.Header{\"Authorization\": {\"bearer token\"}},\n\t\t\tt: \"token\", f: true,\n\t\t},\n\t\t{\n\t\t\th: http.Header{\"Authorization\": {\"beaRer token\"}},\n\t\t\tt: \"token\", f: true,\n\t\t},\n\t\t{\n\t\t\th: http.Header{\"Authorization\": {\"BEARER token\"}},\n\t\t\tt: \"token\", f: true,\n\t\t},\n\t\t{\n\t\t\th: http.Header{\"Authorization\": {\"notbearer token\"}},\n\t\t},\n\t\t{\n\t\t\th: http.Header{\"Authorization\": {\"token\"}},\n\t\t},\n\t\t{\n\t\t\th: http.Header{\"Authorization\": {}},\n\t\t},\n\t\t{\n\t\t\th: http.Header{},\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\ttoken, found := bearerTokenFromRequest(&http.Request{Header: tc.h})\n\t\t\tassert.Equal(t, tc.f, found)\n\t\t\tassert.Equal(t, tc.t, token)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "session/manager.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/identity\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/kratos/x/swagger\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/herodot\"\n)\n\n// ErrNoActiveSessionFound is returned when no active cookie session could be found in the request.\ntype ErrNoActiveSessionFound struct {\n\t*herodot.DefaultError `json:\"error\"`\n\n\t// True when the request had no credentials in it.\n\tCredentialsMissing bool\n}\n\n// NewErrNoActiveSessionFound creates a new ErrNoActiveSessionFound\nfunc NewErrNoActiveSessionFound() *ErrNoActiveSessionFound {\n\treturn &ErrNoActiveSessionFound{\n\t\tDefaultError: herodot.ErrUnauthorized.WithID(text.ErrNoActiveSession).WithError(\"request does not have a valid authentication session\").WithReason(\"No active session was found in this request.\"),\n\t}\n}\n\n// NewErrNoCredentialsForSession creates a new NewErrNoCredentialsForSession\nfunc NewErrNoCredentialsForSession() *ErrNoActiveSessionFound {\n\te := NewErrNoActiveSessionFound()\n\te.CredentialsMissing = true\n\treturn e\n}\n\nfunc (e *ErrNoActiveSessionFound) EnhanceJSONError() interface{} {\n\treturn e\n}\n\n// Is returned when an active session was found but the requested AAL is not satisfied.\n//\n// swagger:model errorAuthenticatorAssuranceLevelNotSatisfied\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype errorAuthenticatorAssuranceLevelNotSatisfied struct {\n\tError swagger.GenericError `json:\"error\"`\n\n\t// Points to where to redirect the user to next.\n\tRedirectTo string `json:\"redirect_browser_to\"`\n}\n\n// ErrAALNotSatisfied is returned when an active session was found but the requested AAL is not satisfied.\ntype ErrAALNotSatisfied struct {\n\t*herodot.DefaultError `json:\"error\"`\n\tRedirectTo            string `json:\"redirect_browser_to\"`\n}\n\nfunc (e *ErrAALNotSatisfied) EnhanceJSONError() interface{} {\n\treturn e\n}\n\nfunc (e *ErrAALNotSatisfied) PassReturnToAndLoginChallengeParameters(requestURL string) error {\n\treq, err := url.Parse(requestURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(e.RedirectTo)\n\tif err != nil {\n\t\treturn err\n\t}\n\tq := u.Query()\n\n\thlc := req.Query().Get(\"login_challenge\")\n\tif len(hlc) != 0 {\n\t\tq.Set(\"login_challenge\", hlc)\n\t}\n\n\treturnTo := req.Query().Get(\"return_to\")\n\tif len(returnTo) != 0 {\n\t\tq.Set(\"return_to\", returnTo)\n\t}\n\n\tu.RawQuery = q.Encode()\n\te.RedirectTo = u.String()\n\n\treturn nil\n}\n\n// NewErrAALNotSatisfied creates a new ErrAALNotSatisfied.\nfunc NewErrAALNotSatisfied(redirectTo string) *ErrAALNotSatisfied {\n\treturn &ErrAALNotSatisfied{\n\t\tRedirectTo: redirectTo,\n\t\tDefaultError: &herodot.DefaultError{\n\t\t\tIDField:     text.ErrIDHigherAALRequired,\n\t\t\tStatusField: http.StatusText(http.StatusForbidden),\n\t\t\tErrorField:  \"Session does not fulfill the requested Authenticator Assurance Level\",\n\t\t\tReasonField: \"An active session was found but it does not fulfill the requested Authenticator Assurance Level. Please verify yourself with a second factor to resolve this issue.\",\n\t\t\tCodeField:   http.StatusForbidden,\n\t\t\tDetailsField: map[string]interface{}{\n\t\t\t\t\"redirect_browser_to\": redirectTo,\n\t\t\t},\n\t\t},\n\t}\n}\n\n// Manager handles identity sessions.\ntype Manager interface {\n\t// UpsertAndIssueCookie stores a session in the database and issues a cookie by calling IssueCookie.\n\t//\n\t// Also regenerates CSRF tokens due to assumed principal change.\n\tUpsertAndIssueCookie(context.Context, http.ResponseWriter, *http.Request, *Session) error\n\n\t// IssueCookie issues a cookie for the given session.\n\t//\n\t// Also regenerates CSRF tokens due to assumed principal change.\n\tIssueCookie(context.Context, http.ResponseWriter, *http.Request, *Session) error\n\n\t// RefreshCookie checks if the request uses an outdated cookie and refreshes the cookie if needed.\n\tRefreshCookie(context.Context, http.ResponseWriter, *http.Request, *Session) error\n\n\t// FetchFromRequest creates an HTTP session using cookies.\n\tFetchFromRequest(context.Context, *http.Request) (*Session, error)\n\n\t// FetchFromRequestContext returns the session from the context or if that is unset, falls back to FetchFromRequest.\n\tFetchFromRequestContext(context.Context, *http.Request) (*Session, error)\n\n\t// PurgeFromRequest removes an HTTP session.\n\tPurgeFromRequest(context.Context, http.ResponseWriter, *http.Request) error\n\n\t// DoesSessionSatisfy answers if a session is satisfying the AAL of a user.\n\t//\n\t// The matcher value can be one of:\n\t//\n\t// - `highest_available`: If set requires the user to upgrade their session to the highest available AAL for that user.\n\t// - `aal1`: Requires the user to have authenticated with at least one authentication factor.\n\t//\n\t// This method is implemented in such a way, that if a second factor is found for the user, it is always assumed\n\t// that the user is able to authenticate with it. This means that if a user has a second factor, the user is always\n\t// asked to authenticate with it if `highest_available` is set and the session's AAL is `aal1`.\n\tDoesSessionSatisfy(ctx context.Context, sess *Session, matcher string, opts ...ManagerOptions) error\n\n\t// SessionAddAuthenticationMethods adds one or more authentication method to the session.\n\tSessionAddAuthenticationMethods(ctx context.Context, sid uuid.UUID, methods ...AuthenticationMethod) error\n\n\t// MaybeRedirectAPICodeFlow for API+Code flows redirects the user to the return_to URL and adds the code query parameter.\n\t// `handled` is true if the request a redirect was written, false otherwise.\n\tMaybeRedirectAPICodeFlow(w http.ResponseWriter, r *http.Request, f flow.Flow, sessionID uuid.UUID, uiNode node.UiNodeGroup) (handled bool, err error)\n\n\t// ActivateSession activates a session.\n\t//\n\t// This method is used to activate a session after a user authenticated with a first or second factor. It sets\n\t// all computed values (e.g. authenticator assurance level) and updates the session object but does not store\n\t// the session in the database or on the client device.\n\tActivateSession(r *http.Request, session *Session, i *identity.Identity, authenticatedAt time.Time) error\n}\n\ntype ManagementProvider interface {\n\tSessionManager() Manager\n}\n"
  },
  {
    "path": "session/manager_http.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/x/events\"\n\n\t\"github.com/ory/kratos/selfservice/flow\"\n\t\"github.com/ory/kratos/selfservice/sessiontokenexchange\"\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/ory/x/randx\"\n\n\t\"github.com/gorilla/sessions\"\n\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\n\t\"github.com/ory/x/sqlcon\"\n\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/x\"\n)\n\nvar ErrNoAALAvailable = herodot.ErrForbidden.WithReasonf(\"Unable to detect available authentication methods. Perform account recovery or contact support.\")\n\ntype (\n\tmanagerHTTPDependencies interface {\n\t\tconfig.Provider\n\t\tidentity.PoolProvider\n\t\tidentity.PrivilegedPoolProvider\n\t\tidentity.ManagementProvider\n\t\tx.CookieProvider\n\t\tlogrusx.Provider\n\t\tnosurfx.CSRFProvider\n\t\totelx.Provider\n\t\tx.TransactionPersistenceProvider\n\t\tPersistenceProvider\n\t\tsessiontokenexchange.PersistenceProvider\n\t}\n\tManagerHTTP struct {\n\t\tcookieName func(ctx context.Context) string\n\t\tr          managerHTTPDependencies\n\t}\n)\n\nfunc NewManagerHTTP(r managerHTTPDependencies) *ManagerHTTP {\n\treturn &ManagerHTTP{\n\t\tr: r,\n\t\tcookieName: func(ctx context.Context) string {\n\t\t\treturn r.Config().SessionName(ctx)\n\t\t},\n\t}\n}\n\ntype options struct {\n\trequestURL string\n\tupsertAAL  bool\n}\n\ntype ManagerOptions func(*options)\n\n// WithRequestURL passes along query parameters from the requestURL to the new URL (if any exist)\nfunc WithRequestURL(requestURL string) ManagerOptions {\n\treturn func(opts *options) {\n\t\topts.requestURL = requestURL\n\t}\n}\n\n// UpsertAAL will update the available AAL of the identity if it was previoulsy unset. This is used to migrate\n// identities from older versions of Ory Kratos.\nfunc UpsertAAL(opts *options) {\n\topts.upsertAAL = true\n}\n\nfunc (s *ManagerHTTP) UpsertAndIssueCookie(ctx context.Context, w http.ResponseWriter, r *http.Request, ss *Session) (err error) {\n\tctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, \"sessions.ManagerHTTP.UpsertAndIssueCookie\")\n\tdefer otelx.End(span, &err)\n\n\tif err := s.r.SessionPersister().UpsertSession(ctx, ss); err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.IssueCookie(ctx, w, r, ss); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *ManagerHTTP) RefreshCookie(ctx context.Context, w http.ResponseWriter, r *http.Request, session *Session) (err error) {\n\tctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, \"sessions.ManagerHTTP.RefreshCookie\")\n\tdefer otelx.End(span, &err)\n\n\t// If it is a session token there is nothing to do.\n\t_, cookieErr := r.Cookie(s.cookieName(r.Context()))\n\tif errors.Is(cookieErr, http.ErrNoCookie) {\n\t\treturn nil\n\t}\n\n\tcookie, err := s.getCookie(r)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\texpiresAt := getCookieExpiry(cookie)\n\tif expiresAt == nil || expiresAt.Before(session.ExpiresAt) {\n\t\tif err := s.IssueCookie(ctx, w, r, session); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *ManagerHTTP) IssueCookie(ctx context.Context, w http.ResponseWriter, r *http.Request, session *Session) (err error) {\n\tctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, \"sessions.ManagerHTTP.IssueCookie\")\n\tdefer otelx.End(span, &err)\n\n\tcookie, err := s.r.CookieManager(ctx).Get(r, s.cookieName(ctx))\n\t// Fix for https://github.com/ory/kratos/issues/1695\n\tif err != nil && cookie == nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tif s.r.Config().SessionPath(ctx) != \"\" {\n\t\tcookie.Options.Path = s.r.Config().SessionPath(ctx)\n\t}\n\n\tif domain := s.r.Config().SessionDomain(ctx); domain != \"\" {\n\t\tcookie.Options.Domain = domain\n\t}\n\n\tif alias := s.r.Config().SelfPublicURL(ctx); s.r.Config().SelfPublicURL(ctx).String() != alias.String() {\n\t\t// If a domain alias is detected use that instead.\n\t\tcookie.Options.Domain = alias.Hostname()\n\t\tcookie.Options.Path = alias.Path\n\t}\n\n\told, err := s.FetchFromRequest(ctx, r)\n\tif err != nil {\n\t\t// No session was set prior -> regenerate anti-csrf token\n\t\t_ = s.r.CSRFHandler().RegenerateToken(w, r)\n\t} else if old.Identity.ID != session.Identity.ID {\n\t\t// No session was set prior -> regenerate anti-csrf token\n\t\t_ = s.r.CSRFHandler().RegenerateToken(w, r)\n\t}\n\n\tif s.r.Config().SessionSameSiteMode(ctx) != 0 {\n\t\tcookie.Options.SameSite = s.r.Config().SessionSameSiteMode(ctx)\n\t}\n\n\tcookie.Options.MaxAge = 0\n\tif s.r.Config().SessionPersistentCookie(ctx) {\n\t\tif session.ExpiresAt.IsZero() {\n\t\t\tcookie.Options.MaxAge = int(s.r.Config().SessionLifespan(ctx).Seconds())\n\t\t} else {\n\t\t\tcookie.Options.MaxAge = int(time.Until(session.ExpiresAt).Seconds())\n\t\t}\n\t}\n\n\tcookie.Values[\"session_token\"] = session.Token\n\tcookie.Values[\"expires_at\"] = session.ExpiresAt.UTC().Format(time.RFC3339Nano)\n\tcookie.Values[\"nonce\"] = randx.MustString(8, randx.Alpha) // Guarantee new kratos session identifier\n\n\tif err := cookie.Save(r, w); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\treturn nil\n}\n\nfunc getCookieExpiry(s *sessions.Session) *time.Time {\n\texpiresAt, ok := s.Values[\"expires_at\"].(string)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tn, err := time.Parse(time.RFC3339Nano, expiresAt)\n\tif err != nil {\n\t\treturn nil\n\t}\n\treturn &n\n}\n\nfunc (s *ManagerHTTP) getCookie(r *http.Request) (*sessions.Session, error) {\n\treturn s.r.CookieManager(r.Context()).Get(r, s.cookieName(r.Context()))\n}\n\nfunc (s *ManagerHTTP) extractToken(r *http.Request) string {\n\tctx, span := s.r.Tracer(r.Context()).Tracer().Start(r.Context(), \"sessions.ManagerHTTP.extractToken\")\n\tdefer span.End()\n\n\tif token := r.Header.Get(\"X-Session-Token\"); len(token) > 0 {\n\t\treturn token\n\t}\n\n\tcookie, err := s.getCookie(r.WithContext(ctx))\n\tif err != nil {\n\t\ttoken, _ := bearerTokenFromRequest(r.WithContext(ctx))\n\t\treturn token\n\t}\n\n\ttoken, ok := cookie.Values[\"session_token\"].(string)\n\tif ok {\n\t\treturn token\n\t}\n\n\ttoken, _ = bearerTokenFromRequest(r.WithContext(ctx))\n\treturn token\n}\n\nfunc (s *ManagerHTTP) FetchFromRequestContext(ctx context.Context, r *http.Request) (_ *Session, err error) {\n\tctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, \"sessions.ManagerHTTP.FetchFromRequestContext\")\n\totelx.End(span, &err)\n\n\tif sess, ok := ctx.Value(sessionInContextKey).(*Session); ok {\n\t\treturn sess, nil\n\t}\n\n\treturn s.FetchFromRequest(ctx, r)\n}\n\nfunc (s *ManagerHTTP) FetchFromRequest(ctx context.Context, r *http.Request) (_ *Session, err error) {\n\tctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, \"sessions.ManagerHTTP.FetchFromRequest\")\n\tdefer func() {\n\t\tif e := new(ErrNoActiveSessionFound); errors.As(err, &e) {\n\t\t\tspan.End()\n\t\t} else {\n\t\t\totelx.End(span, &err)\n\t\t}\n\t}()\n\n\ttoken := s.extractToken(r.WithContext(ctx))\n\tif token == \"\" {\n\t\treturn nil, errors.WithStack(NewErrNoCredentialsForSession())\n\t}\n\n\tse, err := s.r.SessionPersister().GetSessionByToken(ctx, token,\n\t\t// Don't change this unless you want bad performance down the line (because we constantly are unsure if we have the full data fetched or not).\n\t\tExpandEverything, identity.ExpandEverything)\n\tif err != nil {\n\t\tif errors.Is(err, herodot.ErrNotFound) || errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\treturn nil, errors.WithStack(NewErrNoActiveSessionFound())\n\t\t}\n\t\treturn nil, err\n\t}\n\n\ttrace.SpanFromContext(ctx).AddEvent(events.NewSessionChecked(ctx, se.ID, se.IdentityID))\n\n\tif !se.IsActive() {\n\t\treturn nil, errors.WithStack(NewErrNoActiveSessionFound())\n\t}\n\n\treturn se, nil\n}\n\nfunc (s *ManagerHTTP) PurgeFromRequest(ctx context.Context, w http.ResponseWriter, r *http.Request) (err error) {\n\tctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, \"sessions.ManagerHTTP.PurgeFromRequest\")\n\tdefer otelx.End(span, &err)\n\n\tif token, ok := bearerTokenFromRequest(r); ok {\n\t\treturn errors.WithStack(s.r.SessionPersister().RevokeSessionByToken(ctx, token))\n\t}\n\n\tcookie, _ := s.r.CookieManager(r.Context()).Get(r, s.cookieName(ctx))\n\ttoken, ok := cookie.Values[\"session_token\"].(string)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tif err := s.r.SessionPersister().RevokeSessionByToken(ctx, token); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tcookie.Options.MaxAge = -1\n\tif err := cookie.Save(r, w); err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\treturn nil\n}\n\nfunc (s *ManagerHTTP) DoesSessionSatisfy(ctx context.Context, sess *Session, requestedAAL string, opts ...ManagerOptions) (err error) {\n\tctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, \"sessions.ManagerHTTP.DoesSessionSatisfy\")\n\tdefer otelx.End(span, &err)\n\n\tsess.SetAuthenticatorAssuranceLevel()\n\n\t// If we already have AAL2 there is no need to check further because it is the highest AAL.\n\tif sess.AuthenticatorAssuranceLevel == identity.AuthenticatorAssuranceLevel2 {\n\t\treturn nil\n\t}\n\n\tmanagerOpts := &options{}\n\tfor _, o := range opts {\n\t\to(managerOpts)\n\t}\n\n\tloginURL := urlx.AppendPaths(s.r.Config().SelfPublicURL(ctx), \"/self-service/login/browser\")\n\tquery := url.Values{\n\t\t\"aal\": {\"aal2\"},\n\t}\n\n\t// return to the requestURL if it was set\n\tif managerOpts.requestURL != \"\" {\n\t\tquery.Set(\"return_to\", managerOpts.requestURL)\n\t}\n\n\tloginURL.RawQuery = query.Encode()\n\n\tswitch requestedAAL {\n\tcase string(identity.AuthenticatorAssuranceLevel1):\n\t\tif sess.AuthenticatorAssuranceLevel >= identity.AuthenticatorAssuranceLevel1 {\n\t\t\treturn nil\n\t\t}\n\t\treturn NewErrAALNotSatisfied(loginURL.String())\n\tcase config.HighestAvailableAAL:\n\t\tif sess.AuthenticatorAssuranceLevel == identity.AuthenticatorAssuranceLevel2 {\n\t\t\t// The session has AAL2, nothing to check.\n\t\t\treturn nil\n\t\t}\n\n\t\t// The session is AAL1, we asked for `highest_available` AAL, so the only thing we can do\n\t\t// is actually check what authentication methods the identity has.\n\t\tif sess.Identity == nil {\n\t\t\t// This is nil if the session did not expand the identity field.\n\t\t\tsess.Identity, err = s.r.IdentityPool().GetIdentity(ctx, sess.IdentityID, identity.ExpandNothing)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif aal, ok := sess.Identity.InternalAvailableAAL.ToAAL(); ok && aal == identity.AuthenticatorAssuranceLevel2 {\n\t\t\t// Identity gives us AAL2, but the session is still AAL1. We need to upgrade the session.\n\t\t\treturn NewErrAALNotSatisfied(loginURL.String())\n\t\t}\n\n\t\t// Identity AAL is not 2, we refresh:\n\n\t\t// The identity was apparently fetched without credentials. Let's hydrate them.\n\t\tif len(sess.Identity.Credentials) == 0 {\n\t\t\tif err := s.r.PrivilegedIdentityPool().HydrateIdentityAssociations(ctx, sess.Identity, identity.ExpandCredentials); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\t// Great, now we determine the identity's available AAL\n\t\tif err := sess.Identity.SetAvailableAAL(ctx, s.r.IdentityManager()); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// We override the result with our newly computed values\n\t\tavailable, valid := sess.Identity.InternalAvailableAAL.ToAAL()\n\t\tif !valid {\n\t\t\t// Unlikely to happen because SetAvailableAAL will either return an error, or a valid value - but not no error and an invalid value.\n\t\t\treturn errors.WithStack(x.PseudoPanic.WithReasonf(\"Unable to determine available authentication methods for session: %s\", sess.ID))\n\t\t}\n\n\t\tswitch available {\n\t\tcase identity.NoAuthenticatorAssuranceLevel:\n\t\t\t// The identity has AAL0, the session has AAL1, we're good.\n\t\t\treturn nil\n\t\tcase identity.AuthenticatorAssuranceLevel1:\n\t\t\t// The identity has AAL1, the session has AAL1, we're good.\n\t\t\treturn nil\n\t\tcase identity.AuthenticatorAssuranceLevel2:\n\t\t\t// The identity has AAL2, the session has AAL1, we need to upgrade the session.\n\n\t\t\t// Since we ended up here, it also means that `sess.Identity.InternalAvailableAAL` was `aal1` and is now `aal2`.\n\t\t\t// Let's update the database.\n\t\t\tif managerOpts.upsertAAL {\n\t\t\t\tif err := s.r.PrivilegedIdentityPool().UpdateIdentityColumns(ctx, sess.Identity, \"available_aal\"); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn NewErrAALNotSatisfied(loginURL.String())\n\t}\n\n\treturn errors.Errorf(\"requested unknown aal: %s\", requestedAAL)\n}\n\nfunc (s *ManagerHTTP) SessionAddAuthenticationMethods(ctx context.Context, sid uuid.UUID, ams ...AuthenticationMethod) (err error) {\n\tctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, \"sessions.ManagerHTTP.SessionAddAuthenticationMethods\")\n\tdefer otelx.End(span, &err)\n\n\t// Since we added the method, it also means that we have authenticated it\n\tsess, err := s.r.SessionPersister().GetSession(ctx, sid, ExpandNothing)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, m := range ams {\n\t\tsess.CompletedLoginForMethod(m)\n\t}\n\tsess.SetAuthenticatorAssuranceLevel()\n\treturn s.r.SessionPersister().UpsertSession(ctx, sess)\n}\n\nfunc (s *ManagerHTTP) MaybeRedirectAPICodeFlow(w http.ResponseWriter, r *http.Request, f flow.Flow, sessionID uuid.UUID, uiNode node.UiNodeGroup) (handled bool, err error) {\n\tctx, span := s.r.Tracer(r.Context()).Tracer().Start(r.Context(), \"sessions.ManagerHTTP.MaybeRedirectAPICodeFlow\")\n\tdefer otelx.End(span, &err)\n\n\tif uiNode != node.OpenIDConnectGroup {\n\t\treturn false, nil\n\t}\n\n\tcode, ok, _ := s.r.SessionTokenExchangePersister().CodeForFlow(ctx, f.GetID())\n\tif !ok {\n\t\treturn false, nil\n\t}\n\n\treturnTo := s.r.Config().SelfServiceBrowserDefaultReturnTo(ctx)\n\tif redirecter, ok := f.(flow.FlowWithRedirect); ok {\n\t\tr, err := redir.SecureRedirectTo(r, returnTo, redirecter.SecureRedirectToOpts(ctx, s.r)...)\n\t\tif err == nil {\n\t\t\treturnTo = r\n\t\t}\n\t}\n\n\tif err = s.r.SessionTokenExchangePersister().UpdateSessionOnExchanger(r.Context(), f.GetID(), sessionID); err != nil {\n\t\treturn false, errors.WithStack(err)\n\t}\n\n\tq := returnTo.Query()\n\tq.Set(\"code\", code.ReturnToCode)\n\treturnTo.RawQuery = q.Encode()\n\thttp.Redirect(w, r, returnTo.String(), http.StatusSeeOther)\n\n\treturn true, nil\n}\n\nfunc (s *ManagerHTTP) ActivateSession(r *http.Request, session *Session, i *identity.Identity, authenticatedAt time.Time) (err error) {\n\tctx, span := s.r.Tracer(r.Context()).Tracer().Start(r.Context(), \"sessions.ManagerHTTP.ActivateSession\", trace.WithAttributes(\n\t\tattribute.String(\"session.id\", session.ID.String()),\n\t\tattribute.String(\"identity.id\", session.ID.String()),\n\t\tattribute.String(\"authenticated_at\", session.ID.String()),\n\t))\n\tdefer otelx.End(span, &err)\n\n\tif i == nil {\n\t\treturn errors.WithStack(x.PseudoPanic.WithReasonf(\"Identity must not be nil when activating a session.\"))\n\t}\n\n\tif !i.IsActive() {\n\t\treturn errors.WithStack(ErrIdentityDisabled.WithDetail(\"identity_id\", i.ID))\n\t}\n\n\tif err := s.r.IdentityManager().RefreshAvailableAAL(ctx, i); err != nil {\n\t\treturn err\n\t}\n\n\tsession.Identity = i\n\tsession.IdentityID = i.ID\n\n\tsession.Active = true\n\tsession.IssuedAt = authenticatedAt\n\tsession.ExpiresAt = authenticatedAt.Add(s.r.Config().SessionLifespan(ctx))\n\tsession.AuthenticatedAt = authenticatedAt\n\n\tsession.SetSessionDeviceInformation(r.WithContext(ctx))\n\tsession.SetAuthenticatorAssuranceLevel()\n\n\tspan.SetAttributes(\n\t\tattribute.String(\"identity.available_aal\", session.Identity.InternalAvailableAAL.String),\n\t)\n\n\treturn nil\n}\n"
  },
  {
    "path": "session/manager_http_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/httprouterx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nvar _ nosurf.Handler = new(mockCSRFHandler)\n\ntype mockCSRFHandler struct {\n\tc int\n}\n\nfunc (f *mockCSRFHandler) DisablePath(string)                           {}\nfunc (f *mockCSRFHandler) DisableGlob(string)                           {}\nfunc (f *mockCSRFHandler) DisableGlobs(...string)                       {}\nfunc (f *mockCSRFHandler) IgnoreGlob(string)                            {}\nfunc (f *mockCSRFHandler) IgnoreGlobs(...string)                        {}\nfunc (f *mockCSRFHandler) ExemptPath(string)                            {}\nfunc (f *mockCSRFHandler) IgnorePath(string)                            {}\nfunc (f *mockCSRFHandler) ServeHTTP(http.ResponseWriter, *http.Request) {}\n\nfunc (f *mockCSRFHandler) RegenerateToken(_ http.ResponseWriter, _ *http.Request) string {\n\tf.c++\n\treturn nosurfx.FakeCSRFToken\n}\n\nfunc newAAL2Identity() *identity.Identity {\n\treturn &identity.Identity{\n\t\tSchemaID: \"default\",\n\t\tTraits:   []byte(\"{}\"),\n\t\tState:    identity.StateActive,\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tConfig:      []byte(`{\"hashed_password\": \"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`),\n\t\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\t},\n\t\t\tidentity.CredentialsTypeWebAuthn: {\n\t\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":false}]}`),\n\t\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc newAAL1Identity() *identity.Identity {\n\treturn &identity.Identity{\n\t\tSchemaID: \"default\",\n\t\tTraits:   []byte(\"{}\"),\n\t\tState:    identity.StateActive,\n\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\tidentity.CredentialsTypePassword: {\n\t\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\t\tConfig:      []byte(`{\"hashed_password\": \"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`),\n\t\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc TestManagerHTTP(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\n\tt.Run(\"case=regenerate csrf on principal change\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\t\tmock := new(mockCSRFHandler)\n\t\treg.WithCSRFHandler(mock)\n\n\t\trequire.NoError(t, reg.SessionManager().IssueCookie(t.Context(), httptest.NewRecorder(), new(http.Request), new(session.Session)))\n\t\tassert.Equal(t, 1, mock.c)\n\t})\n\n\tt.Run(\"case=cookie settings\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\t\tconfigx.WithValues(map[string]any{\n\t\t\t\t\"dev\":                        false,\n\t\t\t\tconfig.ViperKeyPublicBaseURL: \"https://baseurl.com/base_url\",\n\t\t\t}),\n\t\t)\n\t\tmock := new(mockCSRFHandler)\n\t\treg.WithCSRFHandler(mock)\n\t\ts := &session.Session{Identity: new(identity.Identity)}\n\n\t\tgetCookie := func(ctx context.Context, t *testing.T, req *http.Request) *http.Cookie {\n\t\t\trec := httptest.NewRecorder()\n\t\t\trequire.NoError(t, reg.SessionManager().IssueCookie(ctx, rec, req, s))\n\t\t\trequire.Len(t, rec.Result().Cookies(), 1)\n\t\t\treturn rec.Result().Cookies()[0]\n\t\t}\n\n\t\tt.Run(\"case=immutability\", func(t *testing.T) {\n\t\t\tcookie1 := getCookie(t.Context(), t, testhelpers.NewTestHTTPRequest(t, \"GET\", \"https://baseurl.com/bar\", nil))\n\t\t\tcookie2 := getCookie(t.Context(), t, testhelpers.NewTestHTTPRequest(t, \"GET\", \"https://baseurl.com/bar\", nil))\n\n\t\t\tassert.NotEqual(t, cookie1.Value, cookie2.Value)\n\t\t})\n\n\t\tt.Run(\"case=with default options\", func(t *testing.T) {\n\t\t\tactual := getCookie(t.Context(), t, httptest.NewRequest(\"GET\", \"https://baseurl.com/bar\", nil))\n\t\t\tassert.EqualValues(t, \"\", actual.Domain, \"Domain is empty because unset as a config option\")\n\t\t\tassert.EqualValues(t, \"/\", actual.Path, \"Path is the default /\")\n\t\t\tassert.EqualValues(t, http.SameSiteLaxMode, actual.SameSite)\n\t\t\tassert.True(t, actual.HttpOnly)\n\t\t\tassert.True(t, actual.Secure)\n\t\t})\n\n\t\tt.Run(\"case=with base cookie customization\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValues(t.Context(), map[string]any{\n\t\t\t\tconfig.ViperKeyCookiePath:     \"/cookie\",\n\t\t\t\tconfig.ViperKeyCookieDomain:   \"cookie.com\",\n\t\t\t\tconfig.ViperKeyCookieSameSite: \"Strict\",\n\t\t\t})\n\n\t\t\tactual := getCookie(ctx, t, httptest.NewRequest(\"GET\", \"https://baseurl.com/bar\", nil))\n\t\t\tassert.EqualValues(t, \"cookie.com\", actual.Domain, \"Domain is empty because unset as a config option\")\n\t\t\tassert.EqualValues(t, \"/cookie\", actual.Path, \"Path is the default /\")\n\t\t\tassert.EqualValues(t, http.SameSiteStrictMode, actual.SameSite)\n\t\t\tassert.True(t, actual.HttpOnly)\n\t\t\tassert.True(t, actual.Secure)\n\t\t})\n\n\t\tt.Run(\"case=with base session customization\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValues(t.Context(), map[string]any{\n\t\t\t\tconfig.ViperKeySessionPath:     \"/session\",\n\t\t\t\tconfig.ViperKeySessionDomain:   \"session.com\",\n\t\t\t\tconfig.ViperKeySessionSameSite: \"None\",\n\t\t\t})\n\n\t\t\tactual := getCookie(ctx, t, httptest.NewRequest(\"GET\", \"https://baseurl.com/bar\", nil))\n\t\t\tassert.EqualValues(t, \"session.com\", actual.Domain, \"Domain is empty because unset as a config option\")\n\t\t\tassert.EqualValues(t, \"/session\", actual.Path, \"Path is the default /\")\n\t\t\tassert.EqualValues(t, http.SameSiteNoneMode, actual.SameSite)\n\t\t\tassert.True(t, actual.HttpOnly)\n\t\t\tassert.True(t, actual.Secure)\n\t\t})\n\t})\n\n\tt.Run(\"suite=SessionActivate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t\t)\n\n\t\ti := &identity.Identity{\n\t\t\tTraits: []byte(\"{}\"), State: identity.StateActive,\n\t\t\tCredentials: map[identity.CredentialsType]identity.Credentials{\n\t\t\t\tidentity.CredentialsTypePassword: {Type: identity.CredentialsTypePassword, Identifiers: []string{x.NewUUID().String()}, Config: []byte(`{\"hashed_password\":\"foo\"}`)},\n\t\t\t},\n\t\t}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\tassert.EqualValues(t, i.InternalAvailableAAL.String, \"\")\n\n\t\tsess := session.NewInactiveSession()\n\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, sess, i, time.Now().UTC()))\n\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), sess))\n\n\t\tactual, err := reg.SessionPersister().GetSession(context.Background(), sess.ID, session.ExpandEverything)\n\t\trequire.NoError(t, err)\n\n\t\tassert.EqualValues(t, true, actual.Active)\n\t\tassert.NotZero(t, actual.IssuedAt)\n\t\tassert.True(t, time.Now().Before(actual.ExpiresAt))\n\t\trequire.Len(t, actual.Devices, 1)\n\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel1, i.InternalAvailableAAL.String)\n\n\t\tactualIdentity, err := reg.IdentityPool().GetIdentity(ctx, i.ID, identity.ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel1, actualIdentity.InternalAvailableAAL.String)\n\t})\n\n\tt.Run(\"suite=SessionAddAuthenticationMethod\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\n\t\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t\t)\n\n\t\ti := &identity.Identity{Traits: []byte(\"{}\"), State: identity.StateActive}\n\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), i))\n\t\tsess := session.NewInactiveSession()\n\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, sess, i, time.Now().UTC()))\n\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), sess))\n\t\trequire.NoError(t, reg.SessionManager().SessionAddAuthenticationMethods(context.Background(), sess.ID,\n\t\t\tsession.AuthenticationMethod{\n\t\t\t\tMethod: identity.CredentialsTypeOIDC,\n\t\t\t\tAAL:    identity.AuthenticatorAssuranceLevel1,\n\t\t\t},\n\t\t\tsession.AuthenticationMethod{\n\t\t\t\tMethod: identity.CredentialsTypeWebAuthn,\n\t\t\t\tAAL:    identity.AuthenticatorAssuranceLevel2,\n\t\t\t}))\n\t\tassert.Len(t, sess.AMR, 0)\n\n\t\tactual, err := reg.SessionPersister().GetSession(context.Background(), sess.ID, session.ExpandNothing)\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel2, actual.AuthenticatorAssuranceLevel)\n\t\tfor _, amr := range actual.AMR {\n\t\t\tassert.True(t, amr.Method == identity.CredentialsTypeWebAuthn || amr.Method == identity.CredentialsTypeOIDC)\n\t\t}\n\t\tassert.Len(t, actual.AMR, 2)\n\t})\n\n\tt.Run(\"suite=lifecycle\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/fake-session.schema.json\")),\n\t\t)\n\n\t\tvar s *session.Session\n\t\trp := httprouterx.NewTestRouterPublic(t)\n\t\trp.GET(\"/session/revoke\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\trequire.NoError(t, reg.SessionManager().PurgeFromRequest(r.Context(), w, r))\n\t\t\tw.WriteHeader(http.StatusOK)\n\t\t})\n\n\t\trp.GET(\"/session/set\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\trequire.NoError(t, reg.SessionManager().UpsertAndIssueCookie(r.Context(), w, r, s))\n\t\t\tw.WriteHeader(http.StatusOK)\n\t\t})\n\n\t\trp.GET(\"/session/get\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\tsess, err := reg.SessionManager().FetchFromRequest(r.Context(), r)\n\t\t\tif err != nil {\n\t\t\t\tt.Logf(\"Got error on lookup: %s %T\", err, errors.Unwrap(err))\n\t\t\t\treg.Writer().WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\treg.Writer().Write(w, r, sess)\n\t\t})\n\n\t\trp.GET(\"/session/get-middleware\", reg.SessionHandler().IsAuthenticated(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tsess, err := reg.SessionManager().FetchFromRequestContext(r.Context(), r)\n\t\t\tif err != nil {\n\t\t\t\tt.Logf(\"Got error on lookup: %s %T\", err, errors.Unwrap(err))\n\t\t\t\treg.Writer().WriteError(w, r, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\treg.Writer().Write(w, r, sess)\n\t\t}, session.RedirectOnUnauthenticated(\"https://failed.com\")))\n\n\t\tpts := httptest.NewServer(nosurfx.NewTestCSRFHandler(rp, reg))\n\t\tt.Cleanup(pts.Close)\n\t\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, pts.URL)\n\t\treg.RegisterPublicRoutes(context.Background(), rp)\n\n\t\tt.Run(\"case=valid\", func(t *testing.T) {\n\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\tconf.MustSet(req.Context(), config.ViperKeySessionLifespan, \"1m\")\n\n\t\t\ti := identity.Identity{Traits: []byte(\"{}\")}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &i))\n\t\t\ts, _ = testhelpers.NewActiveSession(req, reg, &i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\n\t\t\tc := testhelpers.NewClientWithCookies(t)\n\t\t\ttesthelpers.MockHydrateCookieClient(t, c, pts.URL+\"/session/set\")\n\n\t\t\tres, err := c.Get(pts.URL + \"/session/get\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\t\tres, err = c.Get(pts.URL + \"/session/get-middleware\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=key rotation\", func(t *testing.T) {\n\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\toriginal := conf.GetProvider(ctx).Strings(config.ViperKeySecretsCookie)\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySecretsCookie, original)\n\t\t\t})\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionLifespan, \"1m\")\n\t\t\tconf.MustSet(ctx, config.ViperKeySecretsCookie, []string{\"foo\"})\n\n\t\t\ti := identity.Identity{Traits: []byte(\"{}\")}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &i))\n\t\t\ts, _ = testhelpers.NewActiveSession(req, reg, &i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\n\t\t\tc := testhelpers.NewClientWithCookies(t)\n\t\t\ttesthelpers.MockHydrateCookieClient(t, c, pts.URL+\"/session/set\")\n\n\t\t\tres, err := c.Get(pts.URL + \"/session/get\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\t\tconf.MustSet(ctx, config.ViperKeySecretsCookie, []string{\"bar\", \"foo\"})\n\t\t\tres, err = c.Get(pts.URL + \"/session/get\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=no panic on invalid cookie name\", func(t *testing.T) {\n\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionLifespan, \"1m\")\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionName, \"$%˜\\\"\")\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionName, \"\")\n\t\t\t})\n\n\t\t\trp.GET(\"/session/set/invalid\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\trequire.Error(t, reg.SessionManager().UpsertAndIssueCookie(r.Context(), w, r, s))\n\t\t\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\t\t})\n\n\t\t\ti := identity.Identity{Traits: []byte(\"{}\")}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &i))\n\t\t\ts, _ = testhelpers.NewActiveSession(req, reg, &i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\n\t\t\tc := testhelpers.NewClientWithCookies(t)\n\t\t\tres, err := c.Get(pts.URL + \"/session/set/invalid\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusInternalServerError, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=valid bearer auth as fallback\", func(t *testing.T) {\n\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionLifespan, \"1m\")\n\n\t\t\ti := identity.Identity{Traits: []byte(\"{}\"), State: identity.StateActive}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &i))\n\t\t\ts, err := testhelpers.NewActiveSession(req, reg, &i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), s))\n\t\t\trequire.NotEmpty(t, s.Token)\n\n\t\t\treq, err = http.NewRequest(\"GET\", pts.URL+\"/session/get\", nil)\n\t\t\trequire.NoError(t, err)\n\t\t\treq.Header.Set(\"Authorization\", \"Bearer \"+s.Token)\n\n\t\t\tc := http.DefaultClient\n\t\t\tres, err := c.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=valid x-session-token auth even if bearer is set\", func(t *testing.T) {\n\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionLifespan, \"1m\")\n\n\t\t\ti := identity.Identity{Traits: []byte(\"{}\"), State: identity.StateActive}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &i))\n\t\t\ts, err := testhelpers.NewActiveSession(req, reg, &i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.NoError(t, reg.SessionPersister().UpsertSession(context.Background(), s))\n\n\t\t\treq, err = http.NewRequest(\"GET\", pts.URL+\"/session/get\", nil)\n\t\t\trequire.NoError(t, err)\n\t\t\treq.Header.Set(\"Authorization\", \"Bearer invalid\")\n\t\t\treq.Header.Set(\"X-Session-Token\", s.Token)\n\n\t\t\tc := http.DefaultClient\n\t\t\tres, err := c.Do(req)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=expired\", func(t *testing.T) {\n\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionLifespan, \"1ns\")\n\t\t\tt.Cleanup(func() {\n\t\t\t\tconf.MustSet(ctx, config.ViperKeySessionLifespan, \"1m\")\n\t\t\t})\n\n\t\t\ti := identity.Identity{Traits: []byte(\"{}\")}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &i))\n\t\t\ts, _ = testhelpers.NewActiveSession(req, reg, &i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\n\t\t\tc := testhelpers.NewClientWithCookies(t)\n\t\t\ttesthelpers.MockHydrateCookieClient(t, c, pts.URL+\"/session/set\")\n\n\t\t\ttime.Sleep(time.Nanosecond * 2)\n\n\t\t\tres, err := c.Get(pts.URL + \"/session/get\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=revoked\", func(t *testing.T) {\n\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\ti := identity.Identity{Traits: []byte(\"{}\")}\n\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), &i))\n\t\t\ts, _ = testhelpers.NewActiveSession(req, reg, &i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\n\t\t\ts, _ = testhelpers.NewActiveSession(req, reg, &i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\n\t\t\tc := testhelpers.NewClientWithCookies(t)\n\t\t\ttesthelpers.MockHydrateCookieClient(t, c, pts.URL+\"/session/set\")\n\n\t\t\tres, err := c.Get(pts.URL + \"/session/revoke\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\n\t\t\tres, err = c.Get(pts.URL + \"/session/get\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusUnauthorized, res.StatusCode)\n\t\t})\n\n\t\tt.Run(\"case=respects AAL config\", func(t *testing.T) {\n\t\t\tconf.MustSet(ctx, config.ViperKeySessionLifespan, \"1m\")\n\n\t\t\tt.Run(\"required_aal=aal2\", func(t *testing.T) {\n\t\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\t\trun := func(t *testing.T, complete []identity.CredentialsType, requested string, i *identity.Identity, expectedError error) {\n\t\t\t\t\ts := session.NewInactiveSession()\n\t\t\t\t\tfor _, m := range complete {\n\t\t\t\t\t\ts.CompletedLoginFor(m, \"\")\n\t\t\t\t\t}\n\t\t\t\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, s, i, time.Now().UTC()))\n\t\t\t\t\terr := reg.SessionManager().DoesSessionSatisfy(ctx, s, requested)\n\t\t\t\t\tif expectedError != nil {\n\t\t\t\t\t\tassert.EqualExportedValues(t, expectedError, err)\n\t\t\t\t\t} else {\n\t\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttest := func(t *testing.T, idAAL1, idAAL2 *identity.Identity) {\n\t\t\t\t\tt.Run(\"fulfilled for aal2 if identity has aal2\", func(t *testing.T) {\n\t\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword, identity.CredentialsTypeWebAuthn}, config.HighestAvailableAAL, idAAL2, nil)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"rejected for aal1 if identity has aal2\", func(t *testing.T) {\n\t\t\t\t\t\treturnURL := urlx.AppendPaths(reg.Config().SelfPublicURL(ctx), \"/self-service/login/browser\")\n\t\t\t\t\t\treturnURL.RawQuery = \"aal=aal2\"\n\t\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword}, config.HighestAvailableAAL, idAAL2,\n\t\t\t\t\t\t\tsession.NewErrAALNotSatisfied(returnURL.String()))\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"fulfilled for aal1 if identity has aal2 but config is aal1\", func(t *testing.T) {\n\t\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword}, \"aal1\", idAAL2, nil)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"fulfilled for aal2 if identity has aal1\", func(t *testing.T) {\n\t\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword}, \"aal1\", idAAL2, nil)\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"fulfilled for aal1 if identity has aal1\", func(t *testing.T) {\n\t\t\t\t\t\trun(t, []identity.CredentialsType{identity.CredentialsTypePassword}, \"aal1\", idAAL1, nil)\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tt.Run(\"identity available AAL is not hydrated\", func(t *testing.T) {\n\t\t\t\t\tidAAL2 := newAAL2Identity()\n\t\t\t\t\tidAAL1 := newAAL1Identity()\n\t\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), idAAL1))\n\t\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), idAAL2))\n\t\t\t\t\ttest(t, idAAL1, idAAL2)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"identity available AAL is hydrated and updated in the DB\", func(t *testing.T) {\n\t\t\t\t\t// We do not create the identity in the database, proving that we do not need\n\t\t\t\t\t// to do any DB roundtrips in this case.\n\t\t\t\t\tidAAL1 := newAAL2Identity()\n\t\t\t\t\trequire.NoError(t, reg.PrivilegedIdentityPool().CreateIdentity(context.Background(), idAAL1))\n\n\t\t\t\t\ts := session.NewInactiveSession()\n\t\t\t\t\ts.CompletedLoginFor(identity.CredentialsTypePassword, \"\")\n\t\t\t\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, s, idAAL1, time.Now().UTC()))\n\t\t\t\t\trequire.Error(t, reg.SessionManager().DoesSessionSatisfy(ctx, s, config.HighestAvailableAAL, session.UpsertAAL))\n\n\t\t\t\t\tresult, err := reg.IdentityPool().GetIdentity(context.Background(), idAAL1.ID, identity.ExpandNothing)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.EqualValues(t, identity.AuthenticatorAssuranceLevel2, result.InternalAvailableAAL.String)\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"identity available AAL is hydrated without DB\", func(t *testing.T) {\n\t\t\t\t\t// We do not create the identity in the database, proving that we do not need\n\t\t\t\t\t// to do any DB roundtrips in this case.\n\t\t\t\t\tidAAL2 := newAAL2Identity()\n\t\t\t\t\tidAAL2.InternalAvailableAAL = identity.NewNullableAuthenticatorAssuranceLevel(identity.AuthenticatorAssuranceLevel2)\n\n\t\t\t\t\tidAAL1 := newAAL1Identity()\n\t\t\t\t\tidAAL1.InternalAvailableAAL = identity.NewNullableAuthenticatorAssuranceLevel(identity.AuthenticatorAssuranceLevel1)\n\n\t\t\t\t\ttest(t, idAAL1, idAAL2)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n}\n\nfunc TestDoesSessionSatisfy(t *testing.T) {\n\tt.Parallel()\n\n\tconf, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t)\n\n\tpasswordEmpty := func() identity.Credentials {\n\t\treturn identity.Credentials{Type: identity.CredentialsTypePassword, Config: []byte(`{}`), Identifiers: []string{testhelpers.RandomEmail()}}\n\t}\n\tpassword := func() identity.Credentials {\n\t\treturn identity.Credentials{\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\tConfig:      []byte(`{\"hashed_password\": \"$argon2id$v=19$m=32,t=2,p=4$cm94YnRVOW5jZzFzcVE4bQ$MNzk5BtR2vUhrp6qQEjRNw\"}`),\n\t\t}\n\t}\n\tpasswordMigration := func() identity.Credentials {\n\t\treturn identity.Credentials{\n\t\t\tType:        identity.CredentialsTypePassword,\n\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\tConfig:      []byte(`{\"use_password_migration_hook\":true}`),\n\t\t}\n\t}\n\n\tcode := func() identity.Credentials {\n\t\treturn identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeCodeAuth,\n\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\tConfig:      []byte(`{\"address_type\":\"email\",\"used_at\":{\"Time\":\"0001-01-01T00:00:00Z\",\"Valid\":false}}`),\n\t\t}\n\t}\n\n\tcodeV2 := func() identity.Credentials {\n\t\treturn identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeCodeAuth,\n\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t\tConfig:      []byte(`{\"addresses\":[{\"channel\":\"email\",\"address\":\"test@ory.sh\"}]}`),\n\t\t}\n\t}\n\n\tcodeEmpty := func() identity.Credentials {\n\t\treturn identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeCodeAuth,\n\t\t\tIdentifiers: []string{},\n\t\t\tConfig:      []byte(`{}`),\n\t\t}\n\t}\n\n\toidc := func() identity.Credentials {\n\t\tsub := testhelpers.RandomEmail()\n\t\treturn identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeOIDC,\n\t\t\tConfig:      []byte(fmt.Sprintf(`{\"providers\":[{\"subject\":\"%s\",\"provider\":\"hydra\",\"initial_id_token\":\"65794a6862\",\"initial_access_token\":\"5234475274\",\"initial_refresh_token\":\"32787233\"}]}`, sub)),\n\t\t\tIdentifiers: []string{\"hydra:\" + sub},\n\t\t}\n\t}\n\t// oidcEmpty := identity.Credentials{\n\t//\tType:        identity.CredentialsTypeOIDC,\n\t//\tConfig:      []byte(`{}`),\n\t//\tIdentifiers: []string{\"hydra:0.fywegkf7hd@ory.sh\"},\n\t// }\n\n\tlookupSecrets := func() identity.Credentials {\n\t\treturn identity.Credentials{\n\t\t\tType:   identity.CredentialsTypeLookup,\n\t\t\tConfig: []byte(`{\"recovery_codes\": [{\"code\": \"abcde\", \"used_at\": null}]}`),\n\t\t}\n\t}\n\t// lookupSecretsEmpty := identity.Credentials{\n\t//\tType:   identity.CredentialsTypeLookup,\n\t//\tConfig: []byte(`{}`),\n\t// }\n\n\ttotp := func() identity.Credentials {\n\t\treturn identity.Credentials{\n\t\t\tType:   identity.CredentialsTypeTOTP,\n\t\t\tConfig: []byte(`{\"totp_url\": \"otpauth://totp/...\"}`),\n\t\t}\n\t}\n\t// totpEmpty := identity.Credentials{\n\t//\tType:   identity.CredentialsTypeTOTP,\n\t//\tConfig: []byte(`{}`),\n\t// }\n\n\t// passkey\n\tpasskey := func() identity.Credentials {\n\t\treturn identity.Credentials{ // passkey\n\t\t\tType:        identity.CredentialsTypePasskey,\n\t\t\tConfig:      []byte(`{\"credentials\":[{}]}`),\n\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t}\n\t}\n\t// passkeyEmpty := identity.Credentials{ // passkey\n\t//\tType:        identity.CredentialsTypePasskey,\n\t//\tConfig:      []byte(`{\"credentials\":null}`),\n\t//\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t// }\n\n\t// webAuthn\n\tmfaWebAuth := func() identity.Credentials {\n\t\treturn identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":false}]}`),\n\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t}\n\t}\n\tpasswordlessWebAuth := func() identity.Credentials {\n\t\treturn identity.Credentials{\n\t\t\tType:        identity.CredentialsTypeWebAuthn,\n\t\t\tConfig:      []byte(`{\"credentials\":[{\"is_passwordless\":true}]}`),\n\t\t\tIdentifiers: []string{testhelpers.RandomEmail()},\n\t\t}\n\t}\n\twebAuthEmpty := func() identity.Credentials {\n\t\treturn identity.Credentials{Type: identity.CredentialsTypeWebAuthn, Config: []byte(`{}`), Identifiers: []string{testhelpers.RandomEmail()}}\n\t}\n\n\tamrs := map[identity.CredentialsType]session.AuthenticationMethod{}\n\tfor _, strat := range reg.AllLoginStrategies() {\n\t\tamrs[strat.ID()] = strat.CompletedAuthenticationMethod(t.Context())\n\t}\n\n\tfor _, tc := range []struct {\n\t\tdesc                  string\n\t\twithContext           func(*testing.T, context.Context) context.Context\n\t\terrAs                 error\n\t\terrIs                 error\n\t\tmatcher               identity.AuthenticatorAssuranceLevel\n\t\tcreds                 []identity.Credentials\n\t\twithAMR               session.AuthenticationMethods\n\t\tsessionManagerOptions []session.ManagerOptions\n\t\texpectedFunc          func(t *testing.T, err error, tcError error)\n\t}{\n\t\t{\n\t\t\tdesc:    \"with highest_available a password user is aal1\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a password migration user is aal1 if password migration is enabled\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{passwordMigration()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t\twithContext: func(t *testing.T, ctx context.Context) context.Context {\n\t\t\t\treturn contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\t\"selfservice.methods.password_migration.enabled\": true,\n\t\t\t\t})\n\t\t\t},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\t// This is not an error because DoesSessionSatisfy always assumes at least aal1\n\t\t\tdesc:    \"with highest_available a password migration user is aal1 if password migration is disabled\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{passwordMigration()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t\twithContext: func(t *testing.T, ctx context.Context) context.Context {\n\t\t\t\treturn contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\t\"selfservice.methods.password_migration.enabled\": false,\n\t\t\t\t})\n\t\t\t},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a otp code user is aal1\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{code()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeCodeAuth]},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a otp codeV2 user is aal1\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{codeV2()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeCodeAuth]},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a empty mfa code user is aal1\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{codeEmpty()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeCodeAuth]},\n\t\t\twithContext: func(t *testing.T, ctx context.Context) context.Context {\n\t\t\t\treturn contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\t\"selfservice.methods.code.mfa_enabled\": true,\n\t\t\t\t})\n\t\t\t},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a password user with empty mfa code is aal1\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), codeEmpty()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t\twithContext: func(t *testing.T, ctx context.Context) context.Context {\n\t\t\t\treturn contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\t\"selfservice.methods.code.passwordless_enabled\": false,\n\t\t\t\t\t\"selfservice.methods.code.mfa_enabled\":          true,\n\t\t\t\t})\n\t\t\t},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a oidc user is aal1\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{oidc()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeOIDC]},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a passkey user is aal1\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{passkey()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePasskey]},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a recovery token user is aal1 even if they have no credentials\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeRecoveryLink]},\n\t\t\t// No error\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a recovery code user is aal1 even if they have no credentials\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeRecoveryCode]},\n\t\t\t// No error\n\t\t},\n\t\t// Test a recovery method with an identity that has only 2fa methods enabled.\n\t\t{\n\t\t\tdesc:    \"with highest_available a recovery link user requires aal2 if they have 2fa totp configured\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{totp()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeRecoveryLink]},\n\t\t\terrIs:   new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a recovery code user requires aal2 if they have 2fa lookup configured\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{lookupSecrets()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeRecoveryCode]},\n\t\t\terrIs:   new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a recovery code user requires aal2 if they have 2fa lookup configured\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{mfaWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeRecoveryCode]},\n\t\t\terrIs:   new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a recovery code user requires aal2 if they have many 2fa methods configured\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{lookupSecrets(), mfaWebAuth(), totp()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeRecoveryCode]},\n\t\t\terrIs:   new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a recovery link user requires aal2 if they have 2fa code configured\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{code()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeRecoveryLink]},\n\t\t\twithContext: func(t *testing.T, ctx context.Context) context.Context {\n\t\t\t\treturn contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\t\"selfservice.methods.code.passwordless_enabled\": false,\n\t\t\t\t\t\"selfservice.methods.code.mfa_enabled\":          true,\n\t\t\t\t})\n\t\t\t},\n\t\t\terrIs: new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"with highest_available a recovery link user requires aal2 if they have 2fa code v2 configured\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{codeV2()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeRecoveryLink]},\n\t\t\twithContext: func(t *testing.T, ctx context.Context) context.Context {\n\t\t\t\treturn contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\t\"selfservice.methods.code.passwordless_enabled\": false,\n\t\t\t\t\t\"selfservice.methods.code.mfa_enabled\":          true,\n\t\t\t\t})\n\t\t\t},\n\t\t\terrIs: new(session.ErrAALNotSatisfied),\n\t\t},\n\n\t\t// Legacy tests\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal0, credential=code\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{totp()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypeRecoveryCode]},\n\t\t\terrIs:   session.ErrNoAALAvailable,\n\t\t},\n\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal1, credential=password\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal1, credential=password, legacy=true\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password()},\n\t\t\twithAMR: session.AuthenticationMethods{{Method: identity.CredentialsTypePassword}},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal1, credential=password+webauth_empty\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), webAuthEmpty()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal1, credential=password+webauth_empty, legacy=true\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), webAuthEmpty()},\n\t\t\twithAMR: session.AuthenticationMethods{{Method: identity.CredentialsTypePassword}},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal1, credential=password+webauth_passwordless\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), passwordlessWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal1, credential=password+webauth_passwordless, legacy=true\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), passwordlessWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{{Method: identity.CredentialsTypePassword}},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal2, credential=password+webauth_mfa\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), mfaWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t\terrAs:   new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal2, credential=password+totp\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), totp()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t\terrAs:   new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal2, credential=password+code-mfa\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), code()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t\terrAs:   new(session.ErrAALNotSatisfied),\n\t\t\twithContext: func(t *testing.T, ctx context.Context) context.Context {\n\t\t\t\treturn contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\t\"selfservice.methods.code.passwordless_enabled\": false,\n\t\t\t\t\t\"selfservice.methods.code.mfa_enabled\":          true,\n\t\t\t\t})\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal2, credential=password+codeV2-mfa\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), codeV2()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t\terrAs:   new(session.ErrAALNotSatisfied),\n\t\t\twithContext: func(t *testing.T, ctx context.Context) context.Context {\n\t\t\t\treturn contextx.WithConfigValues(ctx, map[string]any{\n\t\t\t\t\t\"selfservice.methods.code.passwordless_enabled\": false,\n\t\t\t\t\t\"selfservice.methods.code.mfa_enabled\":          true,\n\t\t\t\t})\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal2, credential=password+lookup_secrets\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), lookupSecrets()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword]},\n\t\t\terrAs:   new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal2, credential=password+webauth_mfa, legacy=true\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), mfaWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{{Method: identity.CredentialsTypePassword}},\n\t\t\terrAs:   new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal2, credential=password+webauth_mfa\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), mfaWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword], {Method: identity.CredentialsTypeWebAuthn, AAL: identity.AuthenticatorAssuranceLevel1}},\n\t\t\terrAs:   new(session.ErrAALNotSatisfied),\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal2, credential=password+webauth_passwordless\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), passwordlessWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword], {Method: identity.CredentialsTypeWebAuthn, AAL: identity.AuthenticatorAssuranceLevel1}},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal2, requested=highest, available=aal2, credential=password+webauth_mfa\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), mfaWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword], {Method: identity.CredentialsTypeWebAuthn, AAL: identity.AuthenticatorAssuranceLevel2}},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal2, requested=highest, available=aal2, credential=password+webauth_mfa, legacy=true\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), mfaWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{amrs[identity.CredentialsTypePassword], {Method: identity.CredentialsTypeWebAuthn}},\n\t\t},\n\n\t\t// oidc\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal1, credential=oidc_and_empties\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{oidc(), webAuthEmpty(), passwordEmpty()},\n\t\t\twithAMR: session.AuthenticationMethods{{Method: identity.CredentialsTypeOIDC, AAL: identity.AuthenticatorAssuranceLevel1}},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal1, credential=code and totp\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{code(), totp()},\n\t\t\twithAMR: session.AuthenticationMethods{{Method: identity.CredentialsTypeCodeAuth, AAL: identity.AuthenticatorAssuranceLevel1}},\n\t\t\terrAs:   session.NewErrAALNotSatisfied(urlx.CopyWithQuery(urlx.AppendPaths(conf.SelfPublicURL(t.Context()), \"/self-service/login/browser\"), url.Values{\"aal\": {\"aal2\"}, \"return_to\": {\"https://myapp.com/settings?id=123\"}}).String()),\n\t\t},\n\t\t{\n\t\t\tdesc:                  \"has=aal1, requested=highest, available=aal1, credentials=password+webauthn_mfa, recovery with session manager options\",\n\t\t\tmatcher:               config.HighestAvailableAAL,\n\t\t\tcreds:                 []identity.Credentials{password(), mfaWebAuth()},\n\t\t\twithAMR:               session.AuthenticationMethods{{Method: identity.CredentialsTypeRecoveryCode}},\n\t\t\terrAs:                 session.NewErrAALNotSatisfied(urlx.CopyWithQuery(urlx.AppendPaths(conf.SelfPublicURL(t.Context()), \"/self-service/login/browser\"), url.Values{\"aal\": {\"aal2\"}, \"return_to\": {\"https://myapp.com/settings?id=123\"}}).String()),\n\t\t\tsessionManagerOptions: []session.ManagerOptions{session.WithRequestURL(\"https://myapp.com/settings?id=123\")},\n\t\t\texpectedFunc: func(t *testing.T, err error, tcError error) {\n\t\t\t\trequire.Contains(t, err.(*session.ErrAALNotSatisfied).RedirectTo, \"myapp.com\")\n\t\t\t\trequire.Equal(t, tcError.(*session.ErrAALNotSatisfied).RedirectTo, err.(*session.ErrAALNotSatisfied).RedirectTo)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:    \"has=aal1, requested=highest, available=aal1, credentials=password+webauthn_mfa, recovery without session manager options\",\n\t\t\tmatcher: config.HighestAvailableAAL,\n\t\t\tcreds:   []identity.Credentials{password(), mfaWebAuth()},\n\t\t\twithAMR: session.AuthenticationMethods{{Method: identity.CredentialsTypeRecoveryCode}},\n\t\t\terrAs:   session.NewErrAALNotSatisfied(urlx.CopyWithQuery(urlx.AppendPaths(conf.SelfPublicURL(t.Context()), \"/self-service/login/browser\"), url.Values{\"aal\": {\"aal2\"}}).String()),\n\t\t\texpectedFunc: func(t *testing.T, err error, tcError error) {\n\t\t\t\trequire.Equal(t, tcError.(*session.ErrAALNotSatisfied).RedirectTo, err.(*session.ErrAALNotSatisfied).RedirectTo)\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tctx := t.Context()\n\t\t\tif tc.withContext != nil {\n\t\t\t\tctx = tc.withContext(t, ctx)\n\t\t\t}\n\n\t\t\tid := identity.NewIdentity(\"default\")\n\t\t\tfor _, c := range tc.creds {\n\t\t\t\tid.SetCredentials(c.Type, c)\n\t\t\t}\n\t\t\trequire.NoError(t, reg.IdentityManager().Create(ctx, id, identity.ManagerAllowWriteProtectedTraits))\n\n\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\ts := session.NewInactiveSession()\n\t\t\tfor _, m := range tc.withAMR {\n\t\t\t\ts.CompletedLoginFor(m.Method, m.AAL)\n\t\t\t}\n\t\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, s, id, time.Now().UTC()))\n\n\t\t\terr := reg.SessionManager().DoesSessionSatisfy(ctx, s, string(tc.matcher), tc.sessionManagerOptions...)\n\t\t\tif tc.errAs != nil || tc.errIs != nil {\n\t\t\t\tif tc.expectedFunc != nil {\n\t\t\t\t\ttc.expectedFunc(t, err, tc.errAs)\n\t\t\t\t}\n\t\t\t\trequire.ErrorAs(t, err, &tc.errAs)\n\t\t\t} else if tc.errIs != nil {\n\t\t\t\tif tc.expectedFunc != nil {\n\t\t\t\t\ttc.expectedFunc(t, err, tc.errIs)\n\t\t\t\t}\n\t\t\t\trequire.ErrorIs(t, err, tc.errIs)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}\n\n\t\t\t// This should still work even if the session does not have identity data attached yet ...\n\t\t\ts.Identity = nil\n\t\t\terr = reg.SessionManager().DoesSessionSatisfy(ctx, s, string(tc.matcher), tc.sessionManagerOptions...)\n\t\t\tif tc.errAs != nil {\n\t\t\t\tif tc.expectedFunc != nil {\n\t\t\t\t\t// If there is no identity, we can't expect the error to contain the identity\n\t\t\t\t\t// schema in the RedirectTo URL.\n\t\t\t\t\tvar errAALNotSatisfied *session.ErrAALNotSatisfied\n\t\t\t\t\terrors.As(tc.errAs, &errAALNotSatisfied)\n\t\t\t\t\tu := x.Must(url.Parse(errAALNotSatisfied.RedirectTo))\n\t\t\t\t\tq := u.Query()\n\t\t\t\t\tq.Del(\"identity_schema\")\n\t\t\t\t\tu.RawQuery = q.Encode()\n\n\t\t\t\t\ttc.expectedFunc(t, err, session.NewErrAALNotSatisfied(u.String()))\n\t\t\t\t} else {\n\t\t\t\t\tassert.ErrorAs(t, err, &tc.errAs)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\n\t\t\t// ... or no credentials attached.\n\t\t\ts.Identity = id\n\t\t\ts.Identity.Credentials = nil\n\t\t\terr = reg.SessionManager().DoesSessionSatisfy(ctx, s, string(tc.matcher), tc.sessionManagerOptions...)\n\t\t\tif tc.errAs != nil {\n\t\t\t\tif tc.expectedFunc != nil {\n\t\t\t\t\ttc.expectedFunc(t, err, tc.errAs)\n\t\t\t\t} else {\n\t\t\t\t\tassert.ErrorAs(t, err, &tc.errAs)\n\t\t\t\t}\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": "session/manager_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/session\"\n)\n\nfunc TestErrAALNotSatisfied_PassReturnToAndLoginChallengeParameters(t *testing.T) {\n\tcases := []struct {\n\t\tname       string\n\t\tinstance   *session.ErrAALNotSatisfied\n\t\trequestURL string\n\t\twantErr    assert.ErrorAssertionFunc\n\t\texpected   string\n\t}{\n\t\t{\n\t\t\tname: \"no parameter\",\n\t\t\tinstance: &session.ErrAALNotSatisfied{\n\t\t\t\tDefaultError: &herodot.DefaultError{},\n\t\t\t\tRedirectTo:   \"https://localhost/?foo=bar\",\n\t\t\t},\n\t\t\trequestURL: \"https://localhost:1234/\",\n\t\t\twantErr:    assert.NoError,\n\t\t\texpected:   \"https://localhost/?foo=bar\",\n\t\t},\n\t\t{\n\t\t\tname: \"pass return_to parameter\",\n\t\t\tinstance: &session.ErrAALNotSatisfied{\n\t\t\t\tDefaultError: &herodot.DefaultError{},\n\t\t\t\tRedirectTo:   \"https://localhost/?foo=bar\",\n\t\t\t},\n\t\t\trequestURL: \"https://localhost:1234/?return_to=https%3A%2F%2Fory.sh\",\n\t\t\twantErr:    assert.NoError,\n\t\t\texpected:   \"https://localhost/?foo=bar&return_to=https%3A%2F%2Fory.sh\",\n\t\t},\n\t\t{\n\t\t\tname: \"pass login_challenge parameter\",\n\t\t\tinstance: &session.ErrAALNotSatisfied{\n\t\t\t\tDefaultError: &herodot.DefaultError{},\n\t\t\t\tRedirectTo:   \"https://localhost/?foo=bar\",\n\t\t\t},\n\t\t\trequestURL: \"https://localhost:1234/?login_challenge=badee1\",\n\t\t\twantErr:    assert.NoError,\n\t\t\texpected:   \"https://localhost/?foo=bar&login_challenge=badee1\",\n\t\t},\n\t\t{\n\t\t\tname: \"pass login_challenge and return_to parameters\",\n\t\t\tinstance: &session.ErrAALNotSatisfied{\n\t\t\t\tDefaultError: &herodot.DefaultError{},\n\t\t\t\tRedirectTo:   \"https://localhost/?foo=bar\",\n\t\t\t},\n\t\t\trequestURL: \"https://localhost:1234/?return_to=https%3A%2F%2Fory.sh&login_challenge=badee1\",\n\t\t\twantErr:    assert.NoError,\n\t\t\texpected:   \"https://localhost/?foo=bar&login_challenge=badee1&return_to=https%3A%2F%2Fory.sh\",\n\t\t},\n\t\t{\n\t\t\tname: \"invalid RedirectTo URL\",\n\t\t\tinstance: &session.ErrAALNotSatisfied{\n\t\t\t\tDefaultError: &herodot.DefaultError{},\n\t\t\t\tRedirectTo:   \"https://user:{{{@localhost/?foo=bar\",\n\t\t\t},\n\t\t\trequestURL: \"https://localhost:1234/?return_to=https%3A%2F%2Fory.sh\",\n\t\t\twantErr:    assert.Error,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid request URL URL\",\n\t\t\tinstance: &session.ErrAALNotSatisfied{\n\t\t\t\tDefaultError: &herodot.DefaultError{},\n\t\t\t\tRedirectTo:   \"https://localhost/?foo=bar\",\n\t\t\t},\n\t\t\trequestURL: \"https://user:{{{@localhost:1234/?return_to=https%3A%2F%2Fory.sh\",\n\t\t\twantErr:    assert.Error,\n\t\t},\n\t}\n\tfor _, tc := range cases {\n\t\tt.Run(fmt.Sprintf(\"case=%s\", tc.name), func(t *testing.T) {\n\t\t\terr := tc.instance.PassReturnToAndLoginChallengeParameters(tc.requestURL)\n\n\t\t\ttc.wantErr(t, err)\n\t\t\tif err == nil {\n\t\t\t\tassert.Equal(t, tc.expected, tc.instance.RedirectTo)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "session/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/ory/pop/v6\"\n\n\t\"github.com/ory/kratos/identity\"\n\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\ntype PersistenceProvider interface {\n\tSessionPersister() Persister\n}\n\ntype Persister interface {\n\tGetConnection(ctx context.Context) *pop.Connection\n\n\t// GetSession retrieves a session from the store.\n\tGetSession(ctx context.Context, sid uuid.UUID, expandables Expandables) (*Session, error)\n\n\t// ListSessions retrieves all sessions.\n\tListSessions(ctx context.Context, active *bool, paginatorOpts []keysetpagination.Option, expandables Expandables) ([]Session, *keysetpagination.Paginator, error)\n\n\t// ListSessionsByIdentity retrieves sessions for an identity from the store.\n\tListSessionsByIdentity(ctx context.Context, iID uuid.UUID, active *bool, page, perPage int, except uuid.UUID, expandables Expandables) ([]Session, int64, error)\n\n\t// UpsertSession inserts or updates a session into / in the store.\n\tUpsertSession(ctx context.Context, s *Session) error\n\n\t// ExtendSession updates the expiry of a session.\n\tExtendSession(ctx context.Context, sessionID uuid.UUID) error\n\n\t// DeleteSession removes a session from the store.\n\tDeleteSession(ctx context.Context, id uuid.UUID) error\n\n\t// DeleteSessionsByIdentity removes all active session from the store for the given identity.\n\tDeleteSessionsByIdentity(ctx context.Context, identity uuid.UUID) error\n\n\t// GetSessionByToken gets the session associated with the given token.\n\t//\n\t// Functionality is similar to GetSession but accepts a session token\n\t// instead of a session ID.\n\tGetSessionByToken(ctx context.Context, token string, expandables Expandables, identityExpandables identity.Expandables) (*Session, error)\n\n\t// DeleteExpiredSessions deletes sessions that expired before the given time.\n\tDeleteExpiredSessions(context.Context, time.Time, int) error\n\n\t// DeleteSessionByToken deletes a session associated with the given token.\n\t//\n\t// Functionality is similar to DeleteSession but accepts a session token\n\t// instead of a session ID.\n\tDeleteSessionByToken(context.Context, string) error\n\n\t// RevokeSessionByToken marks a session inactive with the given token.\n\tRevokeSessionByToken(ctx context.Context, token string) error\n\n\t// RevokeSessionById marks a session inactive with the specified uuid\n\tRevokeSessionById(ctx context.Context, sID uuid.UUID) error\n\n\t// RevokeSession marks a given session inactive.\n\tRevokeSession(ctx context.Context, iID, sID uuid.UUID) error\n\n\t// RevokeSessionsIdentityExcept marks all except the given session of an identity inactive. It returns the number of sessions that were revoked.\n\tRevokeSessionsIdentityExcept(ctx context.Context, iID, sID uuid.UUID) (int, error)\n}\n\ntype DevicePersister interface {\n\tCreateDevice(ctx context.Context, d *Device) error\n}\n"
  },
  {
    "path": "session/session.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"context\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/randx\"\n)\n\nvar ErrIdentityDisabled = herodot.ErrUnauthorized.WithError(\"identity is disabled\").WithReason(\"This account was disabled.\")\n\ntype lifespanProvider interface {\n\tSessionLifespan(ctx context.Context) time.Duration\n}\n\ntype refreshWindowProvider interface {\n\tSessionRefreshMinTimeLeft(ctx context.Context) time.Duration\n}\n\n// Device corresponding to a Session\n//\n// swagger:model sessionDevice\ntype Device struct {\n\t// Device record ID\n\t//\n\t// required: true\n\tID uuid.UUID `json:\"id\" faker:\"-\" db:\"id\"`\n\n\t// SessionID is a helper struct field for gobuffalo.pop.\n\tSessionID uuid.UUID `json:\"-\" faker:\"-\" db:\"session_id\"`\n\n\t// IPAddress of the client\n\tIPAddress *string `json:\"ip_address\" faker:\"ptr_ipv4\" db:\"ip_address\"`\n\n\t// UserAgent of the client\n\tUserAgent *string `json:\"user_agent\" faker:\"-\" db:\"user_agent\"`\n\n\t// Geo Location corresponding to the IP Address\n\tLocation *string `json:\"location\" faker:\"ptr_geo_location\" db:\"location\"`\n\n\t// Time of capture\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\n\t// Last updated at\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\n\tNID        uuid.UUID  `json:\"-\"  faker:\"-\" db:\"nid\"`\n\tIdentityID *uuid.UUID `json:\"-\"  faker:\"-\" db:\"identity_id\"`\n}\n\nfunc (Device) TableName() string { return \"session_devices\" }\n\n// A Session\n//\n// swagger:model session\ntype Session struct {\n\t// Session ID\n\t//\n\t// required: true\n\tID uuid.UUID `json:\"id\" faker:\"-\" db:\"id\"`\n\n\t// Active state. If false the session is no longer active.\n\tActive bool `json:\"active\" db:\"active\"`\n\n\t// The Session Expiry\n\t//\n\t// When this session expires at.\n\tExpiresAt time.Time `json:\"expires_at\" db:\"expires_at\" faker:\"time_type\"`\n\n\t// The Session Authentication Timestamp\n\t//\n\t// When this session was authenticated at. If multi-factor authentication was used this\n\t// is the time when the last factor was authenticated (e.g. the TOTP code challenge was completed).\n\tAuthenticatedAt time.Time `json:\"authenticated_at\" db:\"authenticated_at\" faker:\"time_type\"`\n\n\t// AuthenticationMethod Assurance Level (AAL)\n\t//\n\t// The authenticator assurance level can be one of \"aal1\", \"aal2\", or \"aal3\". A higher number means that it is harder\n\t// for an attacker to compromise the account.\n\t//\n\t// Generally, \"aal1\" implies that one authentication factor was used while AAL2 implies that two factors (e.g.\n\t// password + TOTP) have been used.\n\t//\n\t// To learn more about these levels please head over to: https://www.ory.sh/kratos/docs/concepts/credentials\n\tAuthenticatorAssuranceLevel identity.AuthenticatorAssuranceLevel `faker:\"aal_type\" db:\"aal\" json:\"authenticator_assurance_level\"`\n\n\t// Authentication Method References (AMR)\n\t//\n\t// A list of authentication methods (e.g. password, oidc, ...) used to issue this session.\n\tAMR AuthenticationMethods `db:\"authentication_methods\" json:\"authentication_methods\"`\n\n\t// The Session Issuance Timestamp\n\t//\n\t// When this session was issued at. Usually equal or close to `authenticated_at`.\n\tIssuedAt time.Time `json:\"issued_at\" db:\"issued_at\" faker:\"time_type\"`\n\n\t// The Logout Token\n\t//\n\t// Use this token to log out a user.\n\tLogoutToken string `json:\"-\" db:\"logout_token\"`\n\n\t// The Session Identity\n\t//\n\t// The identity that authenticated this session.\n\t//\n\t// If 2FA is required for the user, and the authentication process only solved the first factor, this field will be\n\t// null until the session has been fully authenticated with the second factor.\n\tIdentity *identity.Identity `json:\"identity\" faker:\"identity\" db:\"-\" belongs_to:\"identities\" fk_id:\"IdentityID\"`\n\n\t// Devices has history of all endpoints where the session was used\n\tDevices []Device `json:\"devices\" faker:\"-\" has_many:\"session_devices\" fk_id:\"session_id\"`\n\n\t// IdentityID is a helper struct field for gobuffalo.pop.\n\tIdentityID uuid.UUID `json:\"-\" faker:\"-\" db:\"identity_id\"`\n\n\t// CreatedAt is a helper struct field for gobuffalo.pop.\n\tCreatedAt time.Time `json:\"-\" faker:\"-\" db:\"created_at\"`\n\n\t// UpdatedAt is a helper struct field for gobuffalo.pop.\n\tUpdatedAt time.Time `json:\"-\" faker:\"-\" db:\"updated_at\"`\n\n\t// Tokenized is the tokenized (e.g. JWT) version of the session.\n\t//\n\t// It is only set when the `tokenize_as` query parameter was set to a valid tokenize template during calls to `/session/whoami`.\n\tTokenized string `json:\"tokenized,omitempty\" faker:\"-\" db:\"-\"`\n\n\t// The Session Token\n\t//\n\t// The token of this session.\n\tToken string    `json:\"-\" db:\"token\"`\n\tNID   uuid.UUID `json:\"-\"  faker:\"-\" db:\"nid\"`\n}\n\nfunc (s Session) PageToken() keysetpagination.PageToken {\n\treturn keysetpagination.MapPageToken{\n\t\t\"id\":         s.ID.String(),\n\t\t\"created_at\": s.CreatedAt.Format(x.MapPaginationDateFormat),\n\t}\n}\n\nfunc (Session) DefaultPageToken() keysetpagination.PageToken {\n\treturn keysetpagination.MapPageToken{\n\t\t\"id\":         uuid.Nil.String(),\n\t\t\"created_at\": time.Date(2200, 12, 31, 23, 59, 59, 0, time.UTC).Format(x.MapPaginationDateFormat),\n\t}\n}\n\nfunc (Session) TableName() string { return \"sessions\" }\n\nfunc (s *Session) CompletedLoginForMethod(method AuthenticationMethod) {\n\tmethod.CompletedAt = time.Now().UTC()\n\ts.AMR = append(s.AMR, method)\n}\n\nfunc (s *Session) CompletedLoginFor(method identity.CredentialsType, aal identity.AuthenticatorAssuranceLevel) {\n\ts.CompletedLoginForMethod(AuthenticationMethod{Method: method, AAL: aal})\n}\n\nfunc (s *Session) CompletedLoginForWithProvider(method identity.CredentialsType, aal identity.AuthenticatorAssuranceLevel, providerID string, organizationID string) {\n\ts.CompletedLoginForMethod(AuthenticationMethod{\n\t\tMethod:       method,\n\t\tAAL:          aal,\n\t\tProvider:     providerID,\n\t\tOrganization: organizationID,\n\t})\n}\n\nfunc (s *Session) AuthenticatedVia(method identity.CredentialsType) bool {\n\tfor _, authMethod := range s.AMR {\n\t\tif authMethod.Method == method {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (s *Session) SetAuthenticatorAssuranceLevel() {\n\tif len(s.AMR) == 0 {\n\t\t// No AMR is set\n\t\ts.AuthenticatorAssuranceLevel = identity.NoAuthenticatorAssuranceLevel\n\t}\n\n\tvar isAAL1, isAAL2 bool\n\tfor _, amr := range s.AMR {\n\t\tswitch amr.AAL {\n\t\tcase identity.AuthenticatorAssuranceLevel1:\n\t\t\tisAAL1 = true\n\t\tcase identity.AuthenticatorAssuranceLevel2:\n\t\t\tisAAL2 = true\n\t\t// The following section is a graceful migration from Ory Kratos v0.9.\n\t\t//\n\t\t// TODO remove this section, it is already over 2 years old.\n\t\tcase \"\":\n\t\t\t// Sessions before Ory Kratos 0.9 did not have the AAL\n\t\t\t// be part of the AMR.\n\t\t\tswitch amr.Method {\n\t\t\tcase identity.CredentialsTypeRecoveryLink:\n\t\t\tcase identity.CredentialsTypeRecoveryCode:\n\t\t\t\tisAAL1 = true\n\t\t\tcase identity.CredentialsTypeOIDC:\n\t\t\t\tisAAL1 = true\n\t\t\tcase \"v0.6_legacy_session\":\n\t\t\t\tisAAL1 = true\n\t\t\tcase identity.CredentialsTypePassword:\n\t\t\t\tisAAL1 = true\n\t\t\tcase identity.CredentialsTypeWebAuthn:\n\t\t\t\tisAAL2 = true\n\t\t\tcase identity.CredentialsTypeTOTP:\n\t\t\t\tisAAL2 = true\n\t\t\tcase identity.CredentialsTypeLookup:\n\t\t\t\tisAAL2 = true\n\t\t\t}\n\t\t}\n\t}\n\n\tif isAAL1 && isAAL2 {\n\t\ts.AuthenticatorAssuranceLevel = identity.AuthenticatorAssuranceLevel2\n\t} else if isAAL1 {\n\t\ts.AuthenticatorAssuranceLevel = identity.AuthenticatorAssuranceLevel1\n\t} else if len(s.AMR) > 0 {\n\t\t// A fallback. If an AMR is set, but we did not satisfy the above, gracefully fall back to level 1.\n\t\ts.AuthenticatorAssuranceLevel = identity.AuthenticatorAssuranceLevel1\n\t}\n}\n\nfunc NewInactiveSession() *Session {\n\treturn &Session{\n\t\tID:                          uuid.Nil,\n\t\tToken:                       x.OrySessionToken + randx.MustString(32, randx.AlphaNum),\n\t\tLogoutToken:                 x.OryLogoutToken + randx.MustString(32, randx.AlphaNum),\n\t\tActive:                      false,\n\t\tAuthenticatorAssuranceLevel: identity.NoAuthenticatorAssuranceLevel,\n\t}\n}\n\nfunc (s *Session) SetSessionDeviceInformation(r *http.Request) {\n\tdevice := Device{\n\t\tSessionID:  s.ID,\n\t\tIdentityID: new(s.IdentityID),\n\t\tIPAddress:  new(httpx.ClientIP(r)),\n\t}\n\n\tagent := r.Header[\"User-Agent\"]\n\tif len(agent) > 0 {\n\t\tdevice.UserAgent = new(strings.Join(agent, \" \"))\n\t}\n\n\tvar clientGeoLocation []string\n\tif r.Header.Get(\"Cf-Ipcity\") != \"\" {\n\t\tclientGeoLocation = append(clientGeoLocation, r.Header.Get(\"Cf-Ipcity\"))\n\t}\n\tif r.Header.Get(\"Cf-Ipcountry\") != \"\" {\n\t\tclientGeoLocation = append(clientGeoLocation, r.Header.Get(\"Cf-Ipcountry\"))\n\t}\n\tloc := strings.Join(clientGeoLocation, \", \")\n\tdevice.Location = &loc\n\n\ts.Devices = append(s.Devices, device)\n}\n\nfunc (s Session) Declassified() *Session {\n\ts.Identity = s.Identity.CopyWithoutCredentials()\n\treturn &s\n}\n\nfunc (s *Session) IsActive() bool {\n\treturn s.Active && s.ExpiresAt.After(time.Now()) && (s.Identity == nil || s.Identity.IsActive())\n}\n\nfunc (s *Session) Refresh(ctx context.Context, c lifespanProvider) *Session {\n\ts.ExpiresAt = time.Now().Add(c.SessionLifespan(ctx)).UTC()\n\treturn s\n}\n\nfunc (s *Session) MarshalJSON() ([]byte, error) {\n\ttype ss Session\n\tout := ss(*s)\n\tout.Active = s.IsActive()\n\treturn json.Marshal(out)\n}\n\nfunc (s *Session) CanBeRefreshed(ctx context.Context, c refreshWindowProvider) bool {\n\treturn s.ExpiresAt.Add(-c.SessionRefreshMinTimeLeft(ctx)).Before(time.Now())\n}\n\n// List of (Used) AuthenticationMethods\n//\n// A list of authenticators which were used to authenticate the session.\n//\n// swagger:model sessionAuthenticationMethods\ntype AuthenticationMethods []AuthenticationMethod\n\n// AuthenticationMethod identifies an authentication method\n//\n// A singular authenticator used during authentication / login.\n//\n// swagger:model sessionAuthenticationMethod\ntype AuthenticationMethod struct {\n\t// The method used in this authenticator.\n\tMethod identity.CredentialsType `json:\"method\"`\n\n\t// The AAL this method introduced.\n\tAAL identity.AuthenticatorAssuranceLevel `json:\"aal\" faker:\"aal_type\"`\n\n\t// When the authentication challenge was completed.\n\tCompletedAt time.Time `json:\"completed_at\"`\n\n\t// OIDC or SAML provider id used for authentication\n\tProvider string `json:\"provider,omitempty\"`\n\n\t// The Organization id used for authentication\n\tOrganization string `json:\"organization,omitempty\"`\n}\n\n// Scan implements the Scanner interface.\nfunc (n *AuthenticationMethod) Scan(value interface{}) error {\n\tif value == nil {\n\t\treturn nil\n\t}\n\tv := fmt.Sprintf(\"%s\", value)\n\tif len(v) == 0 {\n\t\treturn nil\n\t}\n\treturn errors.WithStack(json.Unmarshal([]byte(v), n))\n}\n\n// Value implements the driver Valuer interface.\nfunc (n AuthenticationMethod) Value() (driver.Value, error) {\n\tvalue, err := json.Marshal(n)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\treturn string(value), nil\n}\n\n// Scan implements the Scanner interface.\nfunc (n *AuthenticationMethods) Scan(value interface{}) error {\n\tif value == nil {\n\t\treturn nil\n\t}\n\tv := fmt.Sprintf(\"%s\", value)\n\tif len(v) == 0 {\n\t\treturn nil\n\t}\n\treturn errors.WithStack(json.Unmarshal([]byte(v), n))\n}\n\n// Value implements the driver Valuer interface.\nfunc (n AuthenticationMethods) Value() (driver.Value, error) {\n\tvalue, err := json.Marshal(n)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\treturn string(value), nil\n}\n"
  },
  {
    "path": "session/session_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n)\n\nfunc TestSession(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t)\n\tauthAt := time.Now()\n\n\tt.Run(\"case=active session\", func(t *testing.T) {\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\n\t\ti := new(identity.Identity)\n\t\ti.State = identity.StateActive\n\t\ti.NID = x.NewUUID()\n\t\ts, err := testhelpers.NewActiveSession(req, reg, i, authAt, identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\trequire.NoError(t, err)\n\t\tassert.True(t, s.IsActive())\n\t\trequire.NotEmpty(t, s.Token)\n\t\trequire.NotEmpty(t, s.LogoutToken)\n\t\tassert.EqualValues(t, identity.CredentialsTypePassword, s.AMR[0].Method)\n\n\t\ti = new(identity.Identity)\n\t\ti.NID = x.NewUUID()\n\t\ts, err = testhelpers.NewActiveSession(req, reg, i, authAt, identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\tassert.Nil(t, s)\n\t\tassert.ErrorIs(t, err, session.ErrIdentityDisabled)\n\t})\n\n\tt.Run(\"case=expired\", func(t *testing.T) {\n\t\tassert.False(t, (&session.Session{ExpiresAt: time.Now().Add(time.Hour)}).IsActive())\n\t\tassert.False(t, (&session.Session{Active: true}).IsActive())\n\t})\n\n\tt.Run(\"case=amr add\", func(t *testing.T) {\n\t\ts := session.NewInactiveSession()\n\t\ts.CompletedLoginFor(identity.CredentialsTypeOIDC, identity.AuthenticatorAssuranceLevel1)\n\t\tassert.EqualValues(t, identity.CredentialsTypeOIDC, s.AMR[0].Method)\n\t\ts.CompletedLoginFor(identity.CredentialsTypeRecoveryLink, identity.AuthenticatorAssuranceLevel1)\n\t\tassert.EqualValues(t, identity.CredentialsTypeOIDC, s.AMR[0].Method)\n\t\tassert.EqualValues(t, identity.CredentialsTypeRecoveryLink, s.AMR[1].Method)\n\t\ts.CompletedLoginFor(identity.CredentialsTypeRecoveryCode, identity.AuthenticatorAssuranceLevel1)\n\t\tassert.EqualValues(t, identity.CredentialsTypeRecoveryCode, s.AMR[2].Method)\n\t})\n\n\tt.Run(\"case=activate\", func(t *testing.T) {\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\n\t\ts := session.NewInactiveSession()\n\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, s, &identity.Identity{NID: x.NewUUID(), State: identity.StateActive}, authAt))\n\t\tassert.True(t, s.Active)\n\t\tassert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel)\n\t\tassert.Equal(t, authAt, s.AuthenticatedAt)\n\n\t\ts = session.NewInactiveSession()\n\t\trequire.ErrorIs(t, reg.SessionManager().ActivateSession(req, s, &identity.Identity{NID: x.NewUUID(), State: identity.StateInactive}, authAt), session.ErrIdentityDisabled)\n\t\tassert.False(t, s.Active)\n\t\tassert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel)\n\t\tassert.Empty(t, s.AuthenticatedAt)\n\t})\n\n\tt.Run(\"case=client information reverse proxy forward\", func(t *testing.T) {\n\t\tfor _, tc := range []struct {\n\t\t\tinput    string\n\t\t\texpected string\n\t\t}{\n\t\t\t{\n\t\t\t\tinput:    \"10.10.8.1, 172.19.2.7\",\n\t\t\t\texpected: \"\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tinput:    \"217.73.188.139,162.158.203.149, 172.19.2.7\",\n\t\t\t\texpected: \"162.158.203.149\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tinput:    \"122.122.122.122 , 123.123.123.123\",\n\t\t\t\texpected: \"123.123.123.123\",\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(\"case=parse \"+tc.input, func(t *testing.T) {\n\t\t\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\t\t\treq.Header[\"User-Agent\"] = []string{\"Mozilla/5.0 (X11; Linux x86_64)\", \"AppleWebKit/537.36 (KHTML, like Gecko)\", \"Chrome/51.0.2704.103 Safari/537.36\"}\n\t\t\t\treq.Header.Set(\"X-Forwarded-For\", tc.input)\n\n\t\t\t\ts := session.NewInactiveSession()\n\t\t\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, s, &identity.Identity{NID: x.NewUUID(), State: identity.StateActive}, authAt))\n\t\t\t\tassert.True(t, s.Active)\n\t\t\t\tassert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel)\n\t\t\t\tassert.Equal(t, authAt, s.AuthenticatedAt)\n\t\t\t\tassert.Equal(t, 1, len(s.Devices))\n\t\t\t\tassert.Equal(t, s.ID.String(), s.Devices[0].SessionID.String())\n\t\t\t\tassert.Equal(t, tc.expected, *s.Devices[0].IPAddress)\n\t\t\t\tassert.Equal(t, \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\", *s.Devices[0].UserAgent)\n\t\t\t\tassert.Equal(t, \"\", *s.Devices[0].Location)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"case=client information reverse proxy real IP set\", func(t *testing.T) {\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\treq.Header[\"User-Agent\"] = []string{\"Mozilla/5.0 (X11; Linux x86_64)\", \"AppleWebKit/537.36 (KHTML, like Gecko)\", \"Chrome/51.0.2704.103 Safari/537.36\"}\n\t\treq.Header.Set(\"X-Real-IP\", \"54.155.246.155\")\n\t\treq.Header[\"X-Forwarded-For\"] = []string{\"54.155.246.232\", \"10.145.1.10\"}\n\n\t\ts := session.NewInactiveSession()\n\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, s, &identity.Identity{NID: x.NewUUID(), State: identity.StateActive}, authAt))\n\t\tassert.True(t, s.Active)\n\t\tassert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel)\n\t\tassert.Equal(t, authAt, s.AuthenticatedAt)\n\t\tassert.Equal(t, 1, len(s.Devices))\n\t\tassert.Equal(t, s.ID.String(), s.Devices[0].SessionID.String())\n\t\tassert.NotNil(t, s.Devices[0].UpdatedAt)\n\t\tassert.NotNil(t, s.Devices[0].CreatedAt)\n\t\tassert.Equal(t, \"54.155.246.155\", *s.Devices[0].IPAddress)\n\t\tassert.Equal(t, \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\", *s.Devices[0].UserAgent)\n\t\tassert.Equal(t, \"\", *s.Devices[0].Location)\n\t})\n\n\tt.Run(\"case=client information CF true client IP set\", func(t *testing.T) {\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\treq.Header[\"User-Agent\"] = []string{\"Mozilla/5.0 (X11; Linux x86_64)\", \"AppleWebKit/537.36 (KHTML, like Gecko)\", \"Chrome/51.0.2704.103 Safari/537.36\"}\n\t\treq.Header.Set(\"True-Client-IP\", \"54.155.246.155\")\n\t\treq.Header.Set(\"X-Forwarded-For\", \"217.73.188.139,162.158.203.149, 172.19.2.7\")\n\n\t\ts := session.NewInactiveSession()\n\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, s, &identity.Identity{State: identity.StateActive, NID: x.NewUUID()}, authAt))\n\t\tassert.True(t, s.Active)\n\t\tassert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel)\n\t\tassert.Equal(t, authAt, s.AuthenticatedAt)\n\t\tassert.Equal(t, 1, len(s.Devices))\n\t\tassert.Equal(t, s.ID.String(), s.Devices[0].SessionID.String())\n\t\tassert.NotNil(t, s.Devices[0].UpdatedAt)\n\t\tassert.NotNil(t, s.Devices[0].CreatedAt)\n\t\tassert.Equal(t, \"54.155.246.155\", *s.Devices[0].IPAddress)\n\t\tassert.Equal(t, \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\", *s.Devices[0].UserAgent)\n\t\tassert.Equal(t, \"\", *s.Devices[0].Location)\n\t})\n\n\tt.Run(\"case=client information CF\", func(t *testing.T) {\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\t\treq.Header[\"User-Agent\"] = []string{\"Mozilla/5.0 (X11; Linux x86_64)\", \"AppleWebKit/537.36 (KHTML, like Gecko)\", \"Chrome/51.0.2704.103 Safari/537.36\"}\n\t\treq.Header.Set(\"True-Client-IP\", \"54.155.246.232\")\n\t\treq.Header.Set(\"Cf-Ipcity\", \"Munich\")\n\t\treq.Header.Set(\"Cf-Ipcountry\", \"Germany\")\n\n\t\ts := session.NewInactiveSession()\n\t\trequire.NoError(t, reg.SessionManager().ActivateSession(req, s, &identity.Identity{NID: x.NewUUID(), State: identity.StateActive}, authAt))\n\t\tassert.True(t, s.Active)\n\t\tassert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel)\n\t\tassert.Equal(t, authAt, s.AuthenticatedAt)\n\t\tassert.Equal(t, 1, len(s.Devices))\n\t\tassert.Equal(t, s.ID.String(), s.Devices[0].SessionID.String())\n\t\tassert.Equal(t, \"54.155.246.232\", *s.Devices[0].IPAddress)\n\t\tassert.Equal(t, \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\", *s.Devices[0].UserAgent)\n\t\tassert.Equal(t, \"Munich, Germany\", *s.Devices[0].Location)\n\t})\n\n\tfor k, tc := range []struct {\n\t\td        string\n\t\tmethods  []session.AuthenticationMethod\n\t\texpected identity.AuthenticatorAssuranceLevel\n\t}{\n\t\t{\n\t\t\td:        \"no amr means no assurance\",\n\t\t\texpected: identity.NoAuthenticatorAssuranceLevel,\n\t\t},\n\t\t{\n\t\t\td:        \"password is aal1\",\n\t\t\tmethods:  []session.AuthenticationMethod{{Method: identity.CredentialsTypePassword}},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td:        \"oidc is aal1\",\n\t\t\tmethods:  []session.AuthenticationMethod{{Method: identity.CredentialsTypeOIDC}},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td: \"recovery link is aal1\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryLink},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td: \"recovery code is aal1\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryCode},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td: \"mix of password, oidc, recovery link is still aal1\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryLink},\n\t\t\t\t{Method: identity.CredentialsTypeOIDC},\n\t\t\t\t{Method: identity.CredentialsTypePassword},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td: \"mix of password, oidc, recovery code is still aal1\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryCode},\n\t\t\t\t{Method: identity.CredentialsTypeOIDC},\n\t\t\t\t{Method: identity.CredentialsTypePassword},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td:        \"just totp is gracefully aal1\",\n\t\t\tmethods:  []session.AuthenticationMethod{{Method: identity.CredentialsTypeTOTP}},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td: \"password + totp is aal2\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypePassword},\n\t\t\t\t{Method: identity.CredentialsTypeTOTP},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel2,\n\t\t},\n\t\t{\n\t\t\td: \"password + lookup is aal2\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypePassword},\n\t\t\t\t{Method: identity.CredentialsTypeLookup},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel2,\n\t\t},\n\t\t{\n\t\t\td: \"oidc + totp is aal2\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeOIDC},\n\t\t\t\t{Method: identity.CredentialsTypeTOTP},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel2,\n\t\t},\n\t\t{\n\t\t\td: \"oidc + lookup is aal2\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeOIDC},\n\t\t\t\t{Method: identity.CredentialsTypeLookup},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel2,\n\t\t},\n\t\t{\n\t\t\td: \"recovery link + totp is aal2\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryLink},\n\t\t\t\t{Method: identity.CredentialsTypeTOTP},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel2,\n\t\t},\n\t\t{\n\t\t\td: \"recovery code + totp is aal2\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryCode},\n\t\t\t\t{Method: identity.CredentialsTypeTOTP},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel2,\n\t\t},\n\t\t{\n\t\t\td: \"recovery link + lookup is aal2\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryLink},\n\t\t\t\t{Method: identity.CredentialsTypeLookup},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel2,\n\t\t},\n\t\t{\n\t\t\td: \"recovery code + lookup is aal2\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryCode},\n\t\t\t\t{Method: identity.CredentialsTypeLookup},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel2,\n\t\t},\n\t\t{\n\t\t\td: \"recovery link + passwordless webauth is aal1\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryLink},\n\t\t\t\t{Method: identity.CredentialsTypeWebAuthn, AAL: identity.AuthenticatorAssuranceLevel1},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td: \"recovery code + passwordless webauth is aal1\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypeRecoveryCode},\n\t\t\t\t{Method: identity.CredentialsTypeWebAuthn, AAL: identity.AuthenticatorAssuranceLevel1},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td: \"respects AAL on AAL1\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypePassword, AAL: identity.AuthenticatorAssuranceLevel1},\n\t\t\t\t{Method: identity.CredentialsTypeWebAuthn, AAL: identity.AuthenticatorAssuranceLevel1},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td: \"respects AAL on AAL2 without AAL1\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypePassword, AAL: identity.AuthenticatorAssuranceLevel2},\n\t\t\t\t{Method: identity.CredentialsTypeWebAuthn, AAL: identity.AuthenticatorAssuranceLevel2},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel1,\n\t\t},\n\t\t{\n\t\t\td: \"respects AAL on AAL2\",\n\t\t\tmethods: []session.AuthenticationMethod{\n\t\t\t\t{Method: identity.CredentialsTypePassword, AAL: identity.AuthenticatorAssuranceLevel1},\n\t\t\t\t{Method: identity.CredentialsTypeWebAuthn, AAL: identity.AuthenticatorAssuranceLevel2},\n\t\t\t},\n\t\t\texpected: identity.AuthenticatorAssuranceLevel2,\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d/description=%s\", k, tc.d), func(t *testing.T) {\n\t\t\ts := session.NewInactiveSession()\n\t\t\tfor _, m := range tc.methods {\n\t\t\t\ts.CompletedLoginFor(m.Method, m.AAL)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"case=session refresh\", func(t *testing.T) {\n\t\treq := testhelpers.NewTestHTTPRequest(t, \"GET\", \"/sessions/whoami\", nil)\n\n\t\tctx := contextx.WithConfigValues(t.Context(), map[string]any{\n\t\t\tconfig.ViperKeySessionLifespan:           \"24h\",\n\t\t\tconfig.ViperKeySessionRefreshMinTimeLeft: \"12h\",\n\t\t})\n\t\ti := new(identity.Identity)\n\t\ti.State = identity.StateActive\n\t\ti.NID = x.NewUUID()\n\t\ts, err := testhelpers.NewActiveSession(req, reg, i, authAt, identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\t\trequire.NoError(t, err)\n\t\tassert.False(t, s.CanBeRefreshed(ctx, reg.Config()), \"fresh session is not refreshable\")\n\n\t\ts.ExpiresAt = s.ExpiresAt.Add(-12 * time.Hour)\n\t\tassert.True(t, s.CanBeRefreshed(ctx, reg.Config()), \"session is refreshable after 12hrs\")\n\t})\n}\n"
  },
  {
    "path": "session/stub/fake-session.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {}\n}\n"
  },
  {
    "path": "session/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "session/stub/jwk.es256.json",
    "content": "{\n  \"keys\": [\n    {\n      \"use\": \"sig\",\n      \"kty\": \"EC\",\n      \"kid\": \"247f1420-e581-4023-88e0-07ee662f80da\",\n      \"crv\": \"P-256\",\n      \"alg\": \"ES256\",\n      \"x\": \"1odGSu9bvVq_9QqqNny8TvvUElscLYoTExxhnomYOgQ\",\n      \"y\": \"pa4d4Ql1lO86PBnQ8efYzSzW9nUrsfLlomn3RIpH2Ic\",\n      \"d\": \"kPoEy2OcUeHobxp9jK00YKTs0CBoRTMWZJoPOe9K5hQ\"\n    }\n  ]\n}\n"
  },
  {
    "path": "session/stub/jwk.es512.broken.json",
    "content": "{\n  \"keys\": [\n    {\n      \"use\": \"sig\",\n      \"kty\": \"EC\",\n      \"kid\": \"bc7f7afc-6742-427c-bb9e-164fe0f8b6a7\",\n      \"crv\": \"P-521\",\n      \"alg\": \"ES512\",\n      \"x\": \"ASj36HQOpsWiaGyzK1F0GkxXRt37R01M-OCWFk8rFqH8UnFBk0qnCmVYWv3pwVPPsN0CfFiaXTrV1gUSapkkDgWY\",\n      \"y\": \"ALf5bqXExUq6FzQNQg01hDhR2lOKzkrC02Bc6Alld8Zji3-echbimNZltoOi4MhXbSJeWHpU8wzb3v9XAAW4eovn\",\n      \"d\": \"ALP0Sf7cmcELc9CQ2bWd6Qs-YxMu0N9EYZhDmR6qbYdGnvv-lcGy_ySoEJD0vPMKagA8PHDvFhC7ORwP-sBIJ4O_\"\n    }\n  ]\n\n"
  },
  {
    "path": "session/stub/jwk.es512.json",
    "content": "{\n  \"keys\": [\n    {\n      \"use\": \"sig\",\n      \"kty\": \"EC\",\n      \"kid\": \"bc7f7afc-6742-427c-bb9e-164fe0f8b6a7\",\n      \"crv\": \"P-521\",\n      \"alg\": \"ES512\",\n      \"x\": \"ASj36HQOpsWiaGyzK1F0GkxXRt37R01M-OCWFk8rFqH8UnFBk0qnCmVYWv3pwVPPsN0CfFiaXTrV1gUSapkkDgWY\",\n      \"y\": \"ALf5bqXExUq6FzQNQg01hDhR2lOKzkrC02Bc6Alld8Zji3-echbimNZltoOi4MhXbSJeWHpU8wzb3v9XAAW4eovn\",\n      \"d\": \"ALP0Sf7cmcELc9CQ2bWd6Qs-YxMu0N9EYZhDmR6qbYdGnvv-lcGy_ySoEJD0vPMKagA8PHDvFhC7ORwP-sBIJ4O_\"\n    }\n  ]\n}\n"
  },
  {
    "path": "session/stub/jwk.rs512.json",
    "content": "{\n  \"keys\": [\n    {\n      \"use\": \"sig\",\n      \"kty\": \"RSA\",\n      \"kid\": \"95311ff8-ff91-486b-9ad9-21df8bdc95d7\",\n      \"alg\": \"RS512\",\n      \"n\": \"6_ygtx-8qvTeN7ts_qFJCuOIEyxOnUpggbx04cG3vqjtyfzZMfi0wlidgMH3zhglij2MwC5lPLbze5n4lGQk26s8bl0uhdWlFHO_44hN3l2NVbPcocVZDWwqOdct2qRx1sEdRAt-P1a-2gxYN4HaemER9lgZSgbikJhmL3EEKhcr0QklUZyMcUnbaHAopzdiMKpnykR28-SXEizBi7JTI0hRDgCVmjuCRsciI5GAFy-nQ5n3Ofm0x8wGflKN1RAeWvolpakb7YJgQAXKQhOY3huoHlr-sh3ZO9vQjBgVQ1AM3k-z4OiQjJwvgogfLa1lSLmh9_Ax3LJQ5iax-aN9yQ\",\n      \"e\": \"AQAB\",\n      \"d\": \"DjZhy4WazEUBGSQtlUxLZN99M4Jonap8E3QxKeOL2Gy-HXsf7ZWH9Wh22-lSrlPf9upsDqr8p-Jw2ZHVWcKKQbyXYCI2ihLq5UdvWBm-btT9jRrO_-Mt0NQh2uftuAxNWty4kX-Ls-7agbFaosUsTlCIT2jQ5RdzD7hN9y98S7iJ6oUiBKdc7cRkNj2lGqowwM_IR4IoU2rY2pC7OtHZg_BKU8UnSYcPopn9tnobpr4E_AA1x49iUOkEny3wSXEfP2gEqb2Frqsf342b0pOtiq4WuiAopG2vBfMCKGddDBG_fFsxp858i03G9S4IuMDYk1uSeXd0zc_nb0GxFjXpXQ\",\n      \"p\": \"-_rHgqAtRJpeUbdTWhWQxlUFp-g0Yt1WCnKhCYXzgwM1kG-m9mzHW9mEJxTc0olygeo2A0vJ7htZJpd9NtJkD6g5yDZ7GIm5Emr9fNk4glz8nkVV9udU2gv6hVe83XU86cI4B4XWXhjdNvjQd7ohO7kBwB3uBmekVRm6ZaUCD-c\",\n      \"q\": \"78CGezUjroDlUA5CiF75_apyBgrEI41eNiOPwN9X7Jy-3zpvecY9ZfIuyf5O1yNkC_zjanGEKom6L3iqcAV_1-wzGMOMvtIwhmkTPT9g0f1Z1utRQVljyXFBml7__xb9cCfq62YnH7irsM5nbe9Txziq2zvBjWCkkjJwTBPWDs8\",\n      \"dp\": \"lXjhuJ8Du1pG8Ppqu1lnk_8DZ-LakHrzeyccV-XZ2bGhqJhS1oMYj2esePJrO4jFIEOq3rGqi1A1xiq-4DJVoOQNwrJuutOXsVE-JT1FxC8cu1Yt9FSthNruNQMiycut4oyPaAcAbrkZIG7gWuVSqXbJjwkyFSKN3N1yMLF9U6k\",\n      \"dq\": \"MLapnHcbnOVLsoxzMEo7-TKcoGWnnKGots9a8hFvSABBOBIjfFavOvuOTjSByGzEczsa6hHOjOYXEnYuCzzS0QiJCUsSWeNTQLww0I0EGyajDmwZwnFrOQ7uCXOsCCSfsh4qOVI0ONnI6M_HbCrolt4IuSrXFObCCYJ-FrchEzk\",\n      \"qi\": \"fBkpKoiyeXnwBYezKcxkeu6E2F2tWiRgRikM-s50kRu2fGgmAZ-nIIHU5glPXCPbn4IolURxeLm8ZZyCCo2tNPWiAQV2C7UF04Kh8QANULoUdpObjplLEWQuCDgz5m9EkR4Sx6FGGA1TvUpYCjks8YxvdSIa56o3l72I49aL1t8\"\n    }\n  ]\n}\n"
  },
  {
    "path": "session/stub/rs512-template.jsonnet",
    "content": "local claims = std.extVar('claims');\nlocal session = std.extVar('session');\n\n{\n  claims: {\n    foo: \"bar\",\n    sub: \"can not be overwritten\",\n    schema_id: session.identity.schema_id,\n    aal: session.authenticator_assurance_level,\n    second_claim: claims.exp,\n    [if std.objectHas(session.identity, 'external_id') then 'external_id']: session.identity.external_id,\n  }\n}\n"
  },
  {
    "path": "session/test/persistence.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage test\n\nimport (\n\t\"context\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/persistence\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/dbal\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/randx\"\n\t\"github.com/ory/x/sqlcon\"\n)\n\nfunc TestPersister(ctx context.Context, conf *config.Config, p interface {\n\tpersistence.Persister\n},\n) func(t *testing.T) {\n\treturn func(t *testing.T) {\n\t\t_, p := testhelpers.NewNetworkUnlessExisting(t, ctx, p)\n\n\t\tt.Run(\"case=not found\", func(t *testing.T) {\n\t\t\t_, err := p.GetSession(ctx, x.NewUUID(), session.ExpandNothing)\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tt.Run(\"case=create session\", func(t *testing.T) {\n\t\t\tvar expected session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&expected))\n\t\t\texpected.Active = true\n\t\t\texpected.AMR = session.AuthenticationMethods{\n\t\t\t\t{Method: identity.CredentialsTypePassword, CompletedAt: time.Now().UTC().Round(time.Second)},\n\t\t\t\t{Method: identity.CredentialsTypeOIDC, CompletedAt: time.Now().UTC().Round(time.Second)},\n\t\t\t}\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected.Identity))\n\n\t\t\tvar expectedSessionDevice session.Device\n\t\t\trequire.NoError(t, faker.FakeData(&expectedSessionDevice))\n\t\t\texpected.Devices = []session.Device{\n\t\t\t\texpectedSessionDevice,\n\t\t\t}\n\n\t\t\tassert.Equal(t, uuid.Nil, expected.ID)\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\t\t\tassert.NotEqual(t, uuid.Nil, expected.ID)\n\n\t\t\tcheck := func(actual *session.Session, err error) {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, expected.Identity.ID, actual.Identity.ID)\n\t\t\t\tassert.NotEmpty(t, actual.Identity.SchemaURL)\n\t\t\t\tassert.NotEmpty(t, actual.Identity.SchemaID)\n\t\t\t\tassert.Equal(t, expected.ID, actual.ID)\n\t\t\t\tassert.Equal(t, expected.Active, actual.Active)\n\t\t\t\tassert.Equal(t, expected.Token, actual.Token)\n\t\t\t\tassert.EqualValues(t, expected.ExpiresAt.Unix(), actual.ExpiresAt.Unix())\n\t\t\t\tassert.Equal(t, expected.AuthenticatedAt.Unix(), actual.AuthenticatedAt.Unix())\n\t\t\t\tassert.Equal(t, expected.IssuedAt.Unix(), actual.IssuedAt.Unix())\n\t\t\t\tassert.Equal(t, expected.AuthenticatorAssuranceLevel, actual.AuthenticatorAssuranceLevel)\n\t\t\t\tassert.Equal(t, expected.AMR, actual.AMR)\n\t\t\t}\n\n\t\t\tcheckDevices := func(actual []session.Device, err error) {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, len(expected.Devices), len(actual))\n\n\t\t\t\tfor i, d := range actual {\n\t\t\t\t\tassert.Equal(t, expected.Devices[i].SessionID, d.SessionID)\n\t\t\t\t\tassert.Equal(t, expected.Devices[i].NID, d.NID)\n\t\t\t\t\tassert.Equal(t, *expected.Devices[i].IPAddress, *d.IPAddress)\n\t\t\t\t\tassert.Equal(t, expected.Devices[i].UserAgent, d.UserAgent)\n\t\t\t\t\tassert.Equal(t, *expected.Devices[i].Location, *d.Location)\n\t\t\t\t\tassert.Equal(t, *expected.Devices[i].IdentityID, *d.IdentityID)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tt.Run(\"method=get by id\", func(t *testing.T) {\n\t\t\t\tsess, err := p.GetSession(ctx, expected.ID, session.ExpandEverything)\n\t\t\t\tcheck(sess, err)\n\t\t\t\tcheckDevices(sess.Devices, err)\n\n\t\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.GetSession(ctx, expected.ID, session.ExpandEverything)\n\t\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"method=get by token\", func(t *testing.T) {\n\t\t\t\tsess, err := p.GetSessionByToken(ctx, expected.Token, session.ExpandEverything, identity.ExpandDefault)\n\t\t\t\tcheck(sess, err)\n\t\t\t\tcheckDevices(sess.Devices, err)\n\n\t\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\t\t_, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\t\t_, err := p.GetSessionByToken(ctx, expected.Token, session.ExpandNothing, identity.ExpandDefault)\n\t\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tt.Run(\"case=update session\", func(t *testing.T) {\n\t\t\t\texpected.AuthenticatorAssuranceLevel = identity.AuthenticatorAssuranceLevel1\n\t\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\n\t\t\t\tactual, err := p.GetSessionByToken(ctx, expected.Token, session.ExpandDefault, identity.ExpandDefault)\n\t\t\t\tcheck(actual, err)\n\t\t\t\tassert.Equal(t, identity.AuthenticatorAssuranceLevel1, actual.AuthenticatorAssuranceLevel)\n\t\t\t})\n\n\t\t\tt.Run(\"case=remove amr and update\", func(t *testing.T) {\n\t\t\t\texpected.AMR = nil\n\t\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\n\t\t\t\tactual, err := p.GetSessionByToken(ctx, expected.Token, session.ExpandDefault, identity.ExpandDefault)\n\t\t\t\tcheck(actual, err)\n\t\t\t\tassert.Empty(t, actual.AMR)\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=list sessions\", func(t *testing.T) {\n\t\t\tvar identity1 identity.Identity\n\t\t\trequire.NoError(t, faker.FakeData(&identity1))\n\n\t\t\t// Second identity to test listing by identity isolation\n\t\t\tvar identity2 identity.Identity\n\t\t\tvar identity2Session session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&identity2))\n\t\t\trequire.NoError(t, faker.FakeData(&identity2Session))\n\n\t\t\t// Create seed identities\n\t\t\t_, l := testhelpers.NewNetwork(t, ctx, p)\n\t\t\trequire.NoError(t, l.CreateIdentity(ctx, &identity1))\n\t\t\trequire.NoError(t, l.CreateIdentity(ctx, &identity2))\n\n\t\t\tseedSessionIDs := make([]uuid.UUID, 5)\n\t\t\tseedSessionsList := make([]session.Session, 5)\n\t\t\tnow := time.Now()\n\t\t\tfor j := range seedSessionsList {\n\t\t\t\trequire.NoError(t, faker.FakeData(&seedSessionsList[j]))\n\t\t\t\tseedSessionsList[j].Identity = &identity1\n\t\t\t\tseedSessionsList[j].Active = j%2 == 0\n\n\t\t\t\tif seedSessionsList[j].Active {\n\t\t\t\t\tseedSessionsList[j].ExpiresAt = time.Now().UTC().Add(time.Hour)\n\t\t\t\t} else {\n\t\t\t\t\tseedSessionsList[j].ExpiresAt = time.Now().UTC().Add(-time.Hour)\n\t\t\t\t}\n\n\t\t\t\tvar device session.Device\n\t\t\t\trequire.NoError(t, faker.FakeData(&device))\n\t\t\t\tseedSessionsList[j].Devices = []session.Device{\n\t\t\t\t\tdevice,\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, l.UpsertSession(ctx, &seedSessionsList[j]))\n\t\t\t\trequire.NoError(t, p.GetConnection(ctx).\n\t\t\t\t\tRawQuery(\n\t\t\t\t\t\t\"UPDATE sessions SET created_at = ?, updated_at = ? WHERE id = ?\",\n\t\t\t\t\t\tnow.Add(time.Duration(j)*time.Minute).Round(time.Second),\n\t\t\t\t\t\tnow.Add(time.Duration(j)*time.Minute).Round(time.Second),\n\t\t\t\t\t\tseedSessionsList[j].ID).\n\t\t\t\t\tExec())\n\t\t\t\tseedSessionIDs[j] = seedSessionsList[j].ID\n\t\t\t}\n\n\t\t\tidentity2Session.Identity = &identity2\n\t\t\tidentity2Session.Active = true\n\t\t\tidentity2Session.ExpiresAt = time.Now().UTC().Add(time.Hour)\n\t\t\trequire.NoError(t, l.UpsertSession(ctx, &identity2Session))\n\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdesc               string\n\t\t\t\texcept             uuid.UUID\n\t\t\t\texpectedSessionIds []uuid.UUID\n\t\t\t\tactive             *bool\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdesc:               \"all\",\n\t\t\t\t\texpectedSessionIds: seedSessionIDs,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:   \"except one\",\n\t\t\t\t\texcept: seedSessionsList[0].ID,\n\t\t\t\t\texpectedSessionIds: []uuid.UUID{\n\t\t\t\t\t\tseedSessionIDs[1],\n\t\t\t\t\t\tseedSessionIDs[2],\n\t\t\t\t\t\tseedSessionIDs[3],\n\t\t\t\t\t\tseedSessionIDs[4],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:   \"active only\",\n\t\t\t\t\tactive: new(true),\n\t\t\t\t\texpectedSessionIds: []uuid.UUID{\n\t\t\t\t\t\tseedSessionIDs[0],\n\t\t\t\t\t\tseedSessionIDs[2],\n\t\t\t\t\t\tseedSessionIDs[4],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:   \"active only and except\",\n\t\t\t\t\tactive: new(true),\n\t\t\t\t\texcept: seedSessionsList[0].ID,\n\t\t\t\t\texpectedSessionIds: []uuid.UUID{\n\t\t\t\t\t\tseedSessionIDs[2],\n\t\t\t\t\t\tseedSessionIDs[4],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:   \"inactive only\",\n\t\t\t\t\tactive: new(false),\n\t\t\t\t\texpectedSessionIds: []uuid.UUID{\n\t\t\t\t\t\tseedSessionIDs[1],\n\t\t\t\t\t\tseedSessionIDs[3],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:   \"inactive only and except\",\n\t\t\t\t\tactive: new(false),\n\t\t\t\t\texcept: seedSessionsList[3].ID,\n\t\t\t\t\texpectedSessionIds: []uuid.UUID{\n\t\t\t\t\t\tseedSessionIDs[1],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"case=by Identity \"+tc.desc, func(t *testing.T) {\n\t\t\t\t\tactual, total, err := l.ListSessionsByIdentity(ctx, identity1.ID, tc.active, 1, 10, tc.except, session.ExpandEverything)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tactualSessionIds := make([]uuid.UUID, 0)\n\t\t\t\t\tfor _, s := range actual {\n\t\t\t\t\t\tactualSessionIds = append(actualSessionIds, s.ID)\n\t\t\t\t\t}\n\n\t\t\t\t\tassert.Equal(t, int64(len(tc.expectedSessionIds)), total)\n\t\t\t\t\tassert.ElementsMatch(t, tc.expectedSessionIds, actualSessionIds)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tt.Run(\"case=by Identity on other network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\tactual, total, err := other.ListSessionsByIdentity(ctx, identity1.ID, nil, 1, 10, uuid.Nil, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, int64(0), total)\n\t\t\t\tassert.Len(t, actual, 0)\n\t\t\t})\n\n\t\t\tfor _, tc := range []struct {\n\t\t\t\tdesc     string\n\t\t\t\texcept   uuid.UUID\n\t\t\t\texpected []session.Session\n\t\t\t\tactive   *bool\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tdesc:     \"all\",\n\t\t\t\t\texpected: append(seedSessionsList, identity2Session),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:   \"active only\",\n\t\t\t\t\tactive: new(true),\n\t\t\t\t\texpected: []session.Session{\n\t\t\t\t\t\tseedSessionsList[0],\n\t\t\t\t\t\tseedSessionsList[2],\n\t\t\t\t\t\tseedSessionsList[4],\n\t\t\t\t\t\tidentity2Session,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdesc:   \"inactive only\",\n\t\t\t\t\tactive: new(false),\n\t\t\t\t\texpected: []session.Session{\n\t\t\t\t\t\tseedSessionsList[1],\n\t\t\t\t\t\tseedSessionsList[3],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t} {\n\t\t\t\tt.Run(\"case=all \"+tc.desc, func(t *testing.T) {\n\t\t\t\t\tpaginatorOpts := make([]keysetpagination.Option, 0)\n\t\t\t\t\tactual, nextPage, err := l.ListSessions(ctx, tc.active, paginatorOpts, session.ExpandEverything)\n\t\t\t\t\trequire.NoError(t, err, \"%+v\", err)\n\n\t\t\t\t\trequire.Equal(t, len(tc.expected), len(actual))\n\t\t\t\t\tassert.Equal(t, true, nextPage.IsLast())\n\n\t\t\t\t\tmapPageToken := nextPage.Token().Parse(\"\")\n\t\t\t\t\tassert.Equal(t, uuid.Nil.String(), mapPageToken[\"id\"])\n\n\t\t\t\t\tassert.Equal(t, 250, nextPage.Size())\n\t\t\t\t\tfor _, es := range tc.expected {\n\t\t\t\t\t\tfound := false\n\t\t\t\t\t\tfor _, as := range actual {\n\t\t\t\t\t\t\tif as.ID == es.ID {\n\t\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t\t\tassert.Equal(t, len(es.Devices), len(as.Devices))\n\t\t\t\t\t\t\t\tassert.Equal(t, es.Identity.ID.String(), as.Identity.ID.String())\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassert.True(t, found)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tt.Run(\"case=all sessions pagination only one page\", func(t *testing.T) {\n\t\t\t\tpaginatorOpts := make([]keysetpagination.Option, 0)\n\t\t\t\tactual, page, err := l.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\trequire.Equal(t, 6, len(actual))\n\t\t\t\tassert.Equal(t, true, page.IsLast())\n\t\t\t\tmapPageToken := page.Token().Parse(\"\")\n\t\t\t\tassert.Equal(t, uuid.Nil.String(), mapPageToken[\"id\"])\n\t\t\t\tassert.Equal(t, 250, page.Size())\n\t\t\t})\n\n\t\t\tt.Run(\"case=all sessions pagination multiple pages\", func(t *testing.T) {\n\t\t\t\tpaginatorOpts := make([]keysetpagination.Option, 0)\n\t\t\t\tpaginatorOpts = append(paginatorOpts, keysetpagination.WithSize(3))\n\t\t\t\tfirstPageItems, page1, err := l.ListSessions(ctx, nil, paginatorOpts, session.ExpandEverything)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, firstPageItems, 3)\n\n\t\t\t\tassert.Equal(t, false, page1.IsLast())\n\t\t\t\tmapPageToken := page1.Token().Parse(\"\")\n\t\t\t\tassert.Equal(t, firstPageItems[len(firstPageItems)-1].ID.String(), mapPageToken[\"id\"])\n\t\t\t\tassert.Equal(t, 3, page1.Size())\n\n\t\t\t\t// Validate secondPageItems page\n\t\t\t\tsecondPageItems, page2, err := l.ListSessions(ctx, nil, page1.ToOptions(), session.ExpandEverything)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Len(t, secondPageItems, 3)\n\n\t\t\t\tacutalIDs := make([]uuid.UUID, 0)\n\t\t\t\tfor _, s := range append(firstPageItems, secondPageItems...) {\n\t\t\t\t\tacutalIDs = append(acutalIDs, s.ID)\n\t\t\t\t}\n\t\t\t\texpect := append(seedSessionIDs, identity2Session.ID)\n\t\t\t\trequire.Len(t, acutalIDs, len(expect))\n\t\t\t\tassert.ElementsMatch(t, expect, acutalIDs)\n\n\t\t\t\tassert.True(t, page2.IsLast())\n\t\t\t\tassert.Equal(t, 3, page2.Size())\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"case=delete session\", func(t *testing.T) {\n\t\t\tvar expected session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&expected))\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected.Identity))\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\n\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\terr := other.DeleteSession(ctx, expected.ID)\n\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t_, err = p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t})\n\n\t\t\trequire.NoError(t, p.DeleteSession(ctx, expected.ID))\n\t\t\t_, err := p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\tassert.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t})\n\n\t\tt.Run(\"case=delete session by token\", func(t *testing.T) {\n\t\t\tvar expected session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&expected))\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected.Identity))\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\n\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\terr := other.DeleteSessionByToken(ctx, expected.Token)\n\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t_, err = p.GetSessionByToken(ctx, expected.Token, session.ExpandNothing, identity.ExpandDefault)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t})\n\n\t\t\trequire.NoError(t, p.DeleteSessionByToken(ctx, expected.Token))\n\t\t\t_, err := p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tt.Run(\"case=revoke session by token\", func(t *testing.T) {\n\t\t\tvar expected session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&expected))\n\t\t\texpected.Active = true\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected.Identity))\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\n\t\t\tactual, err := p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.True(t, actual.Active)\n\n\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\terr := other.RevokeSessionByToken(ctx, expected.Token)\n\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\tactual, err = p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.True(t, actual.Active)\n\t\t\t})\n\n\t\t\trequire.NoError(t, p.RevokeSessionByToken(ctx, expected.Token))\n\n\t\t\tactual, err = p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.False(t, actual.Active)\n\t\t})\n\n\t\tt.Run(\"case=revoke session by id\", func(t *testing.T) {\n\t\t\tvar expected session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&expected))\n\t\t\texpected.Active = true\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected.Identity))\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\n\t\t\tactual, err := p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.True(t, actual.Active)\n\n\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\terr := other.RevokeSessionById(ctx, expected.ID)\n\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\tactual, err = p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.True(t, actual.Active)\n\t\t\t})\n\n\t\t\trequire.NoError(t, p.RevokeSessionById(ctx, expected.ID))\n\n\t\t\tactual, err = p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.False(t, actual.Active)\n\t\t})\n\n\t\tt.Run(\"method=revoke other sessions for identity\", func(t *testing.T) {\n\t\t\t// here we set up 2 identities with each having 2 sessions\n\t\t\tsessions := make([]session.Session, 4)\n\t\t\tfor i := range sessions {\n\t\t\t\trequire.NoError(t, faker.FakeData(&sessions[i]))\n\t\t\t}\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, sessions[0].Identity))\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, sessions[2].Identity))\n\t\t\tsessions[1].IdentityID, sessions[1].Identity = sessions[0].IdentityID, sessions[0].Identity\n\t\t\tsessions[3].IdentityID, sessions[3].Identity = sessions[2].IdentityID, sessions[2].Identity\n\t\t\tfor i := range sessions {\n\t\t\t\tsessions[i].Active = true\n\t\t\t\trequire.NoError(t, p.UpsertSession(ctx, &sessions[i]))\n\t\t\t}\n\n\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\tn, err := other.RevokeSessionsIdentityExcept(ctx, sessions[0].IdentityID, sessions[0].ID)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, 0, n)\n\n\t\t\t\tfor _, s := range sessions {\n\t\t\t\t\tactual, err := p.GetSession(ctx, s.ID, session.ExpandNothing)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.True(t, actual.Active)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tn, err := p.RevokeSessionsIdentityExcept(ctx, sessions[0].IdentityID, sessions[0].ID)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, 1, n)\n\n\t\t\tactual, total, err := p.ListSessionsByIdentity(ctx, sessions[0].IdentityID, nil, 1, 10, uuid.Nil, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, actual, 2)\n\t\t\trequire.Equal(t, int64(2), total)\n\n\t\t\tif actual[0].ID == sessions[0].ID {\n\t\t\t\tassert.True(t, actual[0].Active)\n\t\t\t\tassert.False(t, actual[1].Active)\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, actual[0].ID, sessions[1].ID)\n\t\t\t\tassert.True(t, actual[1].Active)\n\t\t\t\tassert.False(t, actual[0].Active)\n\t\t\t}\n\n\t\t\totherIdentitiesSessions, total, err := p.ListSessionsByIdentity(ctx, sessions[2].IdentityID, nil, 1, 10, uuid.Nil, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, actual, 2)\n\t\t\trequire.Equal(t, int64(2), total)\n\n\t\t\tfor _, s := range otherIdentitiesSessions {\n\t\t\t\tassert.True(t, s.Active)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"method=revoke specific session for identity\", func(t *testing.T) {\n\t\t\tsessions := make([]session.Session, 2)\n\t\t\tfor i := range sessions {\n\t\t\t\trequire.NoError(t, faker.FakeData(&sessions[i]))\n\t\t\t}\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, sessions[0].Identity))\n\t\t\tsessions[1].IdentityID, sessions[1].Identity = sessions[0].IdentityID, sessions[0].Identity\n\t\t\tfor i := range sessions {\n\t\t\t\tsessions[i].Active = true\n\t\t\t\trequire.NoError(t, p.UpsertSession(ctx, &sessions[i]))\n\t\t\t}\n\n\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\trequire.NoError(t, other.RevokeSession(ctx, sessions[0].IdentityID, sessions[0].ID))\n\n\t\t\t\tfor _, s := range sessions {\n\t\t\t\t\tactual, err := p.GetSession(ctx, s.ID, session.ExpandNothing)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.True(t, actual.Active)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\trequire.NoError(t, p.RevokeSession(ctx, sessions[0].IdentityID, sessions[0].ID))\n\n\t\t\tactual, total, err := p.ListSessionsByIdentity(ctx, sessions[0].IdentityID, nil, 1, 10, uuid.Nil, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, actual, 2)\n\t\t\trequire.Equal(t, int64(2), total)\n\n\t\t\tif actual[0].ID == sessions[0].ID {\n\t\t\t\tassert.False(t, actual[0].Active)\n\t\t\t\tassert.True(t, actual[1].Active)\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, actual[0].ID, sessions[1].ID)\n\t\t\t\tassert.False(t, actual[1].Active)\n\t\t\t\tassert.True(t, actual[0].Active)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"case=delete session for\", func(t *testing.T) {\n\t\t\tvar expected1 session.Session\n\t\t\tvar expected2 session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&expected1))\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected1.Identity))\n\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected1))\n\n\t\t\trequire.NoError(t, faker.FakeData(&expected2))\n\t\t\texpected2.Identity = expected1.Identity\n\t\t\texpected2.IdentityID = expected1.IdentityID\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected2))\n\n\t\t\tt.Run(\"on another network\", func(t *testing.T) {\n\t\t\t\t_, other := testhelpers.NewNetwork(t, ctx, p)\n\t\t\t\terr := other.DeleteSessionsByIdentity(ctx, expected2.IdentityID)\n\t\t\t\tassert.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t\t_, err = p.GetSession(ctx, expected1.ID, session.ExpandNothing)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t})\n\n\t\t\trequire.NoError(t, p.DeleteSessionsByIdentity(ctx, expected2.IdentityID))\n\t\t\t_, err := p.GetSession(ctx, expected1.ID, session.ExpandNothing)\n\t\t\trequire.Error(t, err)\n\t\t\t_, err = p.GetSession(ctx, expected2.ID, session.ExpandNothing)\n\t\t\trequire.Error(t, err)\n\t\t})\n\n\t\tt.Run(\"network isolation\", func(t *testing.T) {\n\t\t\tnid1, p := testhelpers.NewNetwork(t, ctx, p)\n\t\t\tnid2, _ := testhelpers.NewNetwork(t, ctx, p)\n\n\t\t\tiid1, iid2 := x.NewUUID(), x.NewUUID()\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at) VALUES (?, ?, 'default', '{}', ?, ?)\", iid1, nid1, time.Now(), time.Now()).Exec())\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO identities (id, nid, schema_id, traits, created_at, updated_at) VALUES (?, ?, 'default', '{}', ?, ?)\", iid2, nid2, time.Now(), time.Now()).Exec())\n\n\t\t\tt1, t2 := randx.MustString(32, randx.AlphaNum), randx.MustString(32, randx.AlphaNum)\n\t\t\tsid1, sid2 := x.NewUUID(), x.NewUUID()\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO sessions (id, nid, identity_id, token, expires_at,authenticated_at, created_at, updated_at, logout_token, authentication_methods) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\", sid1, nid1, iid1, t1, time.Now().Add(time.Hour), time.Now(), time.Now(), time.Now(), randx.MustString(32, randx.AlphaNum), \"[]\").Exec())\n\t\t\trequire.NoError(t, p.GetConnection(ctx).RawQuery(\"INSERT INTO sessions (id, nid, identity_id, token, expires_at,authenticated_at, created_at, updated_at, logout_token, authentication_methods) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\", sid2, nid2, iid2, t2, time.Now().Add(time.Hour), time.Now(), time.Now(), time.Now(), randx.MustString(32, randx.AlphaNum), \"[]\").Exec())\n\n\t\t\t_, err := p.GetSession(ctx, sid1, session.ExpandEverything)\n\t\t\trequire.NoError(t, err)\n\t\t\t_, err = p.GetSession(ctx, sid2, session.ExpandNothing)\n\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\n\t\t\t_, err = p.GetSessionByToken(ctx, t1, session.ExpandNothing, identity.ExpandDefault)\n\t\t\trequire.NoError(t, err)\n\t\t\t_, err = p.GetSessionByToken(ctx, t2, session.ExpandNothing, identity.ExpandDefault)\n\t\t\trequire.ErrorIs(t, err, sqlcon.ErrNoRows)\n\t\t})\n\n\t\tt.Run(\"extend session lifespan but min time is not yet reached\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValues(ctx, map[string]any{config.ViperKeySessionRefreshMinTimeLeft: 2 * time.Hour})\n\n\t\t\tvar expected session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&expected))\n\t\t\texpected.ExpiresAt = time.Now().Add(time.Hour * 10).Round(time.Second).UTC()\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected.Identity))\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\n\t\t\trequire.NoError(t, p.ExtendSession(ctx, expected.ID))\n\t\t\tactual, err := p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, expected.ExpiresAt, actual.ExpiresAt)\n\t\t})\n\n\t\tt.Run(\"extend session lifespan\", func(t *testing.T) {\n\t\t\tctx := contextx.WithConfigValues(ctx, map[string]any{config.ViperKeySessionRefreshMinTimeLeft: 2 * time.Hour})\n\n\t\t\tvar expected session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&expected))\n\t\t\texpected.ExpiresAt = time.Now().Add(time.Hour).UTC()\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected.Identity))\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\n\t\t\texpectedExpiry := expected.Refresh(ctx, conf).ExpiresAt\n\t\t\trequire.NoError(t, p.ExtendSession(ctx, expected.ID))\n\t\t\tactual, err := p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.GreaterOrEqual(t, 10*time.Second, expectedExpiry.Sub(actual.ExpiresAt).Abs())\n\t\t})\n\n\t\tt.Run(\"extend session lifespan on CockroachDB\", func(t *testing.T) {\n\t\t\tif p.GetConnection(ctx).Dialect.Name() != dbal.DriverCockroachDB {\n\t\t\t\tt.Skip(\"Skipping test because driver is not CockroachDB\")\n\t\t\t}\n\n\t\t\tctx := contextx.WithConfigValue(ctx, config.ViperKeySessionRefreshMinTimeLeft, 2*time.Hour)\n\n\t\t\tvar expected session.Session\n\t\t\trequire.NoError(t, faker.FakeData(&expected))\n\t\t\texpected.ExpiresAt = time.Now().Add(time.Hour).UTC()\n\t\t\trequire.NoError(t, p.CreateIdentity(ctx, expected.Identity))\n\t\t\trequire.NoError(t, p.UpsertSession(ctx, &expected))\n\n\t\t\texpectedExpiry := expected.Refresh(ctx, conf).ExpiresAt\n\n\t\t\tfoundExpectedCockroachError := atomic.Bool{}\n\t\t\tg := errgroup.Group{}\n\t\t\tfor range 10 {\n\t\t\t\tg.Go(func() error {\n\t\t\t\t\terr := p.ExtendSession(ctx, expected.ID)\n\t\t\t\t\tif errors.Is(err, sqlcon.ErrNoRows) {\n\t\t\t\t\t\tfoundExpectedCockroachError.Store(true)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t})\n\t\t\t}\n\t\t\trequire.NoError(t, g.Wait())\n\n\t\t\tactual, err := p.GetSession(ctx, expected.ID, session.ExpandNothing)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.LessOrEqual(t, expectedExpiry.Sub(actual.ExpiresAt).Abs(), 10*time.Second)\n\t\t\tassert.True(t, foundExpectedCockroachError.Load(), \"We expect to find a not found error caused by ... FOR UPDATE SKIP LOCKED\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "session/tokenizer.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/golang-jwt/jwt/v5\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n\t\"github.com/ory/x/fetcher\"\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/jsonnetsecure\"\n\t\"github.com/ory/x/jwksx\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype (\n\ttokenizerDependencies interface {\n\t\tjsonnetsecure.VMProvider\n\t\totelx.Provider\n\t\thttpx.ClientProvider\n\t\tconfig.Provider\n\t\tx.JWKSFetchProvider\n\t}\n\tTokenizer struct {\n\t\tr       tokenizerDependencies\n\t\tnowFunc func() time.Time\n\t\tcache   *ristretto.Cache[[]byte, []byte]\n\t}\n\tTokenizerProvider interface {\n\t\tSessionTokenizer() *Tokenizer\n\t}\n)\n\nfunc NewTokenizer(r tokenizerDependencies) *Tokenizer {\n\tcache, _ := ristretto.NewCache(&ristretto.Config[[]byte, []byte]{\n\t\tMaxCost:     50 << 20, // 50MB,\n\t\tNumCounters: 500_000,  // 1kB per snippet -> 50k snippets -> 500k counters\n\t\tBufferItems: 64,\n\t})\n\treturn &Tokenizer{r: r, nowFunc: time.Now, cache: cache}\n}\n\nfunc (s *Tokenizer) SetNowFunc(t func() time.Time) {\n\ts.nowFunc = t\n}\n\nfunc SetSubjectClaim(claims jwt.MapClaims, session *Session, subjectSource string) error {\n\tswitch subjectSource {\n\tcase \"\", \"id\":\n\t\tclaims[\"sub\"] = session.IdentityID.String()\n\tcase \"external_id\":\n\t\tif session.Identity.ExternalID == \"\" {\n\t\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"The session's identity does not have an external ID set, but it is required for the subject claim.\"))\n\t\t}\n\t\tclaims[\"sub\"] = session.Identity.ExternalID.String()\n\tdefault:\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Unknown subject source %q\", subjectSource))\n\t}\n\treturn nil\n}\n\nfunc (s *Tokenizer) TokenizeSession(ctx context.Context, template string, session *Session) (err error) {\n\tctx, span := s.r.Tracer(ctx).Tracer().Start(ctx, \"sessions.ManagerHTTP.TokenizeSession\")\n\tdefer otelx.End(span, &err)\n\n\ttpl, err := s.r.Config().TokenizeTemplate(ctx, template)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\thttpClient := s.r.HTTPClient(ctx)\n\tkey, err := s.r.JWKSFetcher().ResolveKey(\n\t\tctx,\n\t\ttpl.JWKSURL,\n\t\tjwksx.WithCacheEnabled(),\n\t\tjwksx.WithCacheTTL(time.Hour),\n\t\tjwksx.WithHTTPClient(httpClient))\n\tif err != nil {\n\t\tif errors.Is(err, jwksx.ErrUnableToFindKeyID) {\n\t\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"Could not find key a suitable key for tokenization in the JWKS url.\"))\n\t\t}\n\t\treturn err\n\t}\n\n\talg := jwt.GetSigningMethod(key.Algorithm())\n\tif alg == nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithReasonf(\"The JSON Web Key must include a valid \\\"alg\\\" parameter but \\\"%s\\\" was given.\", key.Algorithm()))\n\t}\n\n\tvm, err := s.r.JsonnetVM(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnow := s.nowFunc()\n\ttoken := jwt.New(alg)\n\ttoken.Header[\"kid\"] = key.KeyID()\n\tclaims := jwt.MapClaims{\n\t\t\"jti\": uuid.Must(uuid.NewV4()).String(),\n\t\t\"iss\": s.r.Config().SelfPublicURL(ctx).String(),\n\t\t\"exp\": now.Add(tpl.TTL).Unix(),\n\t\t\"sid\": session.ID.String(),\n\t\t\"nbf\": now.Unix(),\n\t\t\"iat\": now.Unix(),\n\t}\n\n\tif err = SetSubjectClaim(claims, session, tpl.SubjectSource); err != nil {\n\t\treturn err\n\t}\n\n\tif mapper := tpl.ClaimsMapperURL; len(mapper) > 0 {\n\t\tsessionRaw, err := json.Marshal(session)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"Unable to encode session to JSON.\"))\n\t\t}\n\n\t\tclaimsRaw, err := json.Marshal(&claims)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"Unable to encode claims to JSON.\"))\n\t\t}\n\n\t\tvm.ExtCode(\"session\", string(sessionRaw))\n\t\tvm.ExtCode(\"claims\", string(claimsRaw))\n\n\t\tfetcher := fetcher.NewFetcher(fetcher.WithClient(httpClient), fetcher.WithCache(s.cache, 60*time.Minute))\n\t\tjsonnet, err := fetcher.FetchContext(ctx, mapper)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tevaluated, err := vm.EvaluateAnonymousSnippet(tpl.ClaimsMapperURL, jsonnet.String())\n\t\tif err != nil {\n\t\t\ttrace.SpanFromContext(ctx).AddEvent(events.NewJsonnetMappingFailed(\n\t\t\t\tctx, err, jsonnet.Bytes(), evaluated, \"\", \"\",\n\t\t\t))\n\t\t\treturn errors.WithStack(herodot.ErrBadRequest.WithWrap(err).WithDebug(err.Error()).WithReasonf(\"Unable to execute tokenizer JsonNet.\"))\n\t\t}\n\n\t\tevaluatedClaims := gjson.Get(evaluated, \"claims\")\n\t\tif !evaluatedClaims.IsObject() {\n\t\t\ttrace.SpanFromContext(ctx).AddEvent(events.NewJsonnetMappingFailed(\n\t\t\t\tctx, err, jsonnet.Bytes(), evaluated, \"\", \"\",\n\t\t\t))\n\t\t\treturn errors.WithStack(herodot.ErrBadRequest.WithWrap(err).WithReasonf(\"Expected tokenizer JsonNet to return a claims object but it did not.\"))\n\t\t}\n\n\t\tif err := json.Unmarshal([]byte(evaluatedClaims.Raw), &claims); err != nil {\n\t\t\treturn errors.WithStack(herodot.ErrBadRequest.WithWrap(err).WithReasonf(\"Unable to encode tokenized claims.\"))\n\t\t}\n\t}\n\tif err = SetSubjectClaim(claims, session, tpl.SubjectSource); err != nil {\n\t\treturn err\n\t}\n\n\tvar privateKey interface{}\n\tif err := key.Raw(&privateKey); err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithWrap(err).WithReasonf(\"Unable to decode the given private key.\"))\n\t}\n\n\ttoken.Claims = claims\n\tresult, err := token.SignedString(privateKey)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrBadRequest.WithWrap(err).WithReasonf(\"Unable to sign JSON Web Token.\"))\n\t}\n\n\ttrace.SpanFromContext(ctx).AddEvent(events.NewSessionJWTIssued(ctx, session.ID, session.IdentityID, tpl.TTL))\n\tsession.Tokenized = result\n\treturn nil\n}\n"
  },
  {
    "path": "session/tokenizer_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage session_test\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/golang-jwt/jwt/v5\"\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/pkg/testhelpers\"\n\t\"github.com/ory/kratos/session\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/contextx\"\n\t\"github.com/ory/x/snapshotx\"\n)\n\n//go:embed stub/jwk.es256.json\nvar es256Key []byte\n\n//go:embed stub/jwk.es512.json\nvar es512Key []byte\n\nfunc validateTokenized(t *testing.T, raw string, key []byte) *jwt.Token {\n\ttoken, err := jwt.Parse(\n\t\traw,\n\t\tfunc(token *jwt.Token) (target interface{}, _ error) {\n\t\t\tset, err := jwk.Parse(key)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tkey, _ := set.Key(0)\n\t\t\tif pk, err := key.PublicKey(); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t} else if err := pk.Raw(&target); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn target, nil\n\t\t},\n\t\t// We use a fixed time function for snapshot testing, and thus can not validate claims.\n\t\tjwt.WithoutClaimsValidation(),\n\t)\n\trequire.NoError(t, err)\n\treturn token\n}\n\nfunc setTokenizeConfig(ctx context.Context, templateID, keyFile, mapper string) context.Context {\n\treturn contextx.WithConfigValue(ctx, config.ViperKeySessionTokenizerTemplates+\".\"+templateID, &config.SessionTokenizeFormat{\n\t\tTTL:             time.Minute,\n\t\tJWKSURL:         \"file://stub/\" + keyFile,\n\t\tClaimsMapperURL: mapper,\n\t})\n}\nfunc setTokenizeConfigWitSubjectSource(ctx context.Context, templateID, keyFile, mapper, subjectSource string) context.Context {\n\treturn contextx.WithConfigValue(ctx, config.ViperKeySessionTokenizerTemplates+\".\"+templateID, &config.SessionTokenizeFormat{\n\t\tTTL:             time.Minute,\n\t\tJWKSURL:         \"file://stub/\" + keyFile,\n\t\tClaimsMapperURL: mapper,\n\t\tSubjectSource:   subjectSource,\n\t})\n}\n\nfunc TestTokenizer(t *testing.T) {\n\tt.Parallel()\n\n\t_, reg := pkg.NewFastRegistryWithMocks(t,\n\t\tconfigx.WithValue(config.ViperKeyPublicBaseURL, \"http://localhost/\"),\n\t\tconfigx.WithValues(testhelpers.DefaultIdentitySchemaConfig(\"file://./stub/identity.schema.json\")),\n\t)\n\ttkn := session.NewTokenizer(reg)\n\tnowDate := time.Date(2023, 02, 01, 00, 00, 00, 0, time.UTC)\n\ttkn.SetNowFunc(func() time.Time {\n\t\treturn nowDate.Round(time.Second)\n\t})\n\n\tr := httptest.NewRequest(\"GET\", \"/sessions/whoami\", nil)\n\ti := identity.NewIdentity(\"default\")\n\ti.ID = uuid.FromStringOrNil(\"7458af86-c1d8-401c-978a-8da89133f78b\")\n\ti.ExternalID = \"external-id\"\n\ti.NID = uuid.Must(uuid.NewV4())\n\n\ts, err := testhelpers.NewActiveSession(r, reg, i, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\trequire.NoError(t, err)\n\ts.ID = uuid.FromStringOrNil(\"432caf86-c1d8-401c-978a-8da89133f78b\")\n\n\tiWithoutExtID := identity.NewIdentity(\"default\")\n\tiWithoutExtID.ID = uuid.FromStringOrNil(\"710678c5-7761-455a-9e3b-be66e3019da2\")\n\tiWithoutExtID.NID = i.NID\n\n\ts2, err := testhelpers.NewActiveSession(r, reg, iWithoutExtID, time.Now(), identity.CredentialsTypePassword, identity.AuthenticatorAssuranceLevel1)\n\trequire.NoError(t, err)\n\ts2.ID = uuid.FromStringOrNil(\"44de370d-c8ae-4e2c-b943-5e9d9cc385da\")\n\n\tt.Run(\"case=es256-without-jsonnet\", func(t *testing.T) {\n\t\ttid := \"es256-no-template\"\n\t\tctx := setTokenizeConfig(t.Context(), tid, \"jwk.es256.json\", \"\")\n\n\t\trequire.NoError(t, tkn.TokenizeSession(ctx, tid, s))\n\t\ttoken := validateTokenized(t, s.Tokenized, es256Key)\n\n\t\tresultClaims := token.Claims.(jwt.MapClaims)\n\t\tassert.Equal(t, i.ID.String(), resultClaims[\"sub\"])\n\t\tassert.Equal(t, s.ID.String(), resultClaims[\"sid\"])\n\t\tassert.NotEmpty(t, resultClaims[\"jti\"])\n\t\tassert.EqualValues(t, resultClaims[\"exp\"], nowDate.Add(time.Minute).Unix())\n\n\t\tsnapshotx.SnapshotT(t, token.Claims, snapshotx.ExceptPaths(\"jti\"))\n\t})\n\n\tt.Run(\"case=es512-without-jsonnet\", func(t *testing.T) {\n\t\ttid := \"es512-no-template\"\n\t\tctx := setTokenizeConfig(t.Context(), tid, \"jwk.es512.json\", \"\")\n\n\t\trequire.NoError(t, tkn.TokenizeSession(ctx, tid, s))\n\t\ttoken := validateTokenized(t, s.Tokenized, es512Key)\n\n\t\tsnapshotx.SnapshotT(t, token.Claims, snapshotx.ExceptPaths(\"jti\"))\n\t})\n\n\tt.Run(\"case=rs512-with-jsonnet\", func(t *testing.T) {\n\t\ttid := \"rs512-template\"\n\t\tctx := setTokenizeConfigWitSubjectSource(t.Context(), tid, \"jwk.es512.json\", \"file://stub/rs512-template.jsonnet\", \"id\")\n\n\t\trequire.NoError(t, tkn.TokenizeSession(ctx, tid, s))\n\t\ttoken := validateTokenized(t, s.Tokenized, es512Key)\n\n\t\tsnapshotx.SnapshotT(t, token.Claims, snapshotx.ExceptPaths(\"jti\"))\n\t})\n\n\tt.Run(\"case=rs512-with-external_id-in-sub\", func(t *testing.T) {\n\t\ttid := \"rs512-template\"\n\t\tctx := setTokenizeConfigWitSubjectSource(t.Context(), tid, \"jwk.es512.json\", \"file://stub/rs512-template.jsonnet\", \"external_id\")\n\n\t\trequire.NoError(t, tkn.TokenizeSession(ctx, tid, s))\n\t\ttoken := validateTokenized(t, s.Tokenized, es512Key)\n\n\t\tsnapshotx.SnapshotT(t, token.Claims, snapshotx.ExceptPaths(\"jti\"))\n\t})\n\n\tt.Run(\"case=rs512-with-empty-external_id-in-sub\", func(t *testing.T) {\n\t\ttid := \"rs512-template\"\n\t\tctx := setTokenizeConfigWitSubjectSource(t.Context(), tid, \"jwk.es512.json\", \"file://stub/rs512-template.jsonnet\", \"external_id\")\n\n\t\t// This should fail because the identity does not have an external ID set.\n\t\trequire.Error(t, tkn.TokenizeSession(ctx, tid, s2))\n\t})\n\n\tt.Run(\"case=rs512-with-broken-keyfile\", func(t *testing.T) {\n\t\ttid := \"rs512-template\"\n\t\tctx := setTokenizeConfig(t.Context(), tid, \"jwk.es512.broken.json\", \"file://stub/rs512-template.jsonnet\")\n\t\terr := tkn.TokenizeSession(ctx, tid, s)\n\t\trequire.ErrorIs(t, err, herodot.ErrBadRequest)\n\t})\n}\n"
  },
  {
    "path": "spec/api.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage spec\n\nimport _ \"embed\"\n\n//go:embed api.json\nvar API []byte\n"
  },
  {
    "path": "spec/api.json",
    "content": "{\n  \"components\": {\n    \"responses\": {\n      \"emptyResponse\": {\n        \"description\": \"Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 204.\"\n      },\n      \"identitySchemas\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"$ref\": \"#/components/schemas/identitySchemas\"\n            }\n          }\n        },\n        \"description\": \"List Identity JSON Schemas Response\"\n      },\n      \"listCourierMessages\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"items\": {\n                \"$ref\": \"#/components/schemas/message\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        },\n        \"description\": \"Paginated Courier Message List Response\"\n      },\n      \"listIdentities\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"items\": {\n                \"$ref\": \"#/components/schemas/identity\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        },\n        \"description\": \"Paginated Identity List Response\"\n      },\n      \"listIdentitySessions\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"items\": {\n                \"$ref\": \"#/components/schemas/session\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        },\n        \"description\": \"List Identity Sessions Response\"\n      },\n      \"listMySessions\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"items\": {\n                \"$ref\": \"#/components/schemas/session\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        },\n        \"description\": \"List My Session Response\"\n      },\n      \"listSessions\": {\n        \"content\": {\n          \"application/json\": {\n            \"schema\": {\n              \"items\": {\n                \"$ref\": \"#/components/schemas/session\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        },\n        \"description\": \"Session List Response\\n\\nThe response given when listing sessions in an administrative context.\"\n      }\n    },\n    \"schemas\": {\n      \"CodeChannel\": {\n        \"type\": \"string\"\n      },\n      \"DefaultError\": {},\n      \"Duration\": {\n        \"description\": \"A Duration represents the elapsed time between two instants\\nas an int64 nanosecond count. The representation limits the\\nlargest representable duration to approximately 290 years.\",\n        \"format\": \"int64\",\n        \"type\": \"integer\"\n      },\n      \"ID\": {\n        \"format\": \"int64\",\n        \"type\": \"integer\"\n      },\n      \"JSONRawMessage\": {\n        \"title\": \"JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger.\",\n        \"type\": \"object\"\n      },\n      \"NullBool\": {\n        \"nullable\": true,\n        \"type\": \"boolean\"\n      },\n      \"NullInt\": {\n        \"nullable\": true,\n        \"type\": \"integer\"\n      },\n      \"NullString\": {\n        \"nullable\": true,\n        \"type\": \"string\"\n      },\n      \"NullTime\": {\n        \"format\": \"date-time\",\n        \"nullable\": true,\n        \"type\": \"string\"\n      },\n      \"NullUUID\": {\n        \"format\": \"uuid4\",\n        \"nullable\": true,\n        \"type\": \"string\"\n      },\n      \"OAuth2Client\": {\n        \"properties\": {\n          \"access_token_strategy\": {\n            \"description\": \"OAuth 2.0 Access Token Strategy  AccessTokenStrategy is the strategy used to generate access tokens. Valid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens Setting the stragegy here overrides the global setting in `strategies.access_token`.\",\n            \"type\": \"string\"\n          },\n          \"allowed_cors_origins\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"audience\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"authorization_code_grant_access_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"authorization_code_grant_id_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"authorization_code_grant_refresh_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"backchannel_logout_session_required\": {\n            \"description\": \"OpenID Connect Back-Channel Logout Session Required  Boolean value specifying whether the RP requires that a sid (session ID) Claim be included in the Logout Token to identify the RP session with the OP when the backchannel_logout_uri is used. If omitted, the default value is false.\",\n            \"type\": \"boolean\"\n          },\n          \"backchannel_logout_uri\": {\n            \"description\": \"OpenID Connect Back-Channel Logout URI  RP URL that will cause the RP to log itself out when sent a Logout Token by the OP.\",\n            \"type\": \"string\"\n          },\n          \"client_credentials_grant_access_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"client_id\": {\n            \"description\": \"OAuth 2.0 Client ID  The ID is immutable. If no ID is provided, a UUID4 will be generated.\",\n            \"type\": \"string\"\n          },\n          \"client_name\": {\n            \"description\": \"OAuth 2.0 Client Name  The human-readable name of the client to be presented to the end-user during authorization.\",\n            \"type\": \"string\"\n          },\n          \"client_secret\": {\n            \"description\": \"OAuth 2.0 Client Secret  The secret will be included in the create request as cleartext, and then never again. The secret is kept in hashed format and is not recoverable once lost.\",\n            \"type\": \"string\"\n          },\n          \"client_secret_expires_at\": {\n            \"description\": \"OAuth 2.0 Client Secret Expires At  The field is currently not supported and its value is always 0.\",\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          },\n          \"client_uri\": {\n            \"description\": \"OAuth 2.0 Client URI  ClientURI is a URL string of a web page providing information about the client. If present, the server SHOULD display this URL to the end-user in a clickable fashion.\",\n            \"type\": \"string\"\n          },\n          \"contacts\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"created_at\": {\n            \"description\": \"OAuth 2.0 Client Creation Date  CreatedAt returns the timestamp of the client's creation.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"frontchannel_logout_session_required\": {\n            \"description\": \"OpenID Connect Front-Channel Logout Session Required  Boolean value specifying whether the RP requires that iss (issuer) and sid (session ID) query parameters be included to identify the RP session with the OP when the frontchannel_logout_uri is used. If omitted, the default value is false.\",\n            \"type\": \"boolean\"\n          },\n          \"frontchannel_logout_uri\": {\n            \"description\": \"OpenID Connect Front-Channel Logout URI  RP URL that will cause the RP to log itself out when rendered in an iframe by the OP. An iss (issuer) query parameter and a sid (session ID) query parameter MAY be included by the OP to enable the RP to validate the request and to determine which of the potentially multiple sessions is to be logged out; if either is included, both MUST be.\",\n            \"type\": \"string\"\n          },\n          \"grant_types\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"implicit_grant_access_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"implicit_grant_id_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"jwks\": {\n            \"description\": \"OAuth 2.0 Client JSON Web Key Set  Client's JSON Web Key Set [JWK] document, passed by value. The semantics of the jwks parameter are the same as the jwks_uri parameter, other than that the JWK Set is passed by value, rather than by reference. This parameter is intended only to be used by Clients that, for some reason, are unable to use the jwks_uri parameter, for instance, by native applications that might not have a location to host the contents of the JWK Set. If a Client can use jwks_uri, it MUST NOT use jwks. One significant downside of jwks is that it does not enable key rotation (which jwks_uri does, as described in Section 10 of OpenID Connect Core 1.0 [OpenID.Core]). The jwks_uri and jwks parameters MUST NOT be used together.\"\n          },\n          \"jwks_uri\": {\n            \"description\": \"OAuth 2.0 Client JSON Web Key Set URL  URL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains the signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the Client's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing and encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both signatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.\",\n            \"type\": \"string\"\n          },\n          \"jwt_bearer_grant_access_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"logo_uri\": {\n            \"description\": \"OAuth 2.0 Client Logo URI  A URL string referencing the client's logo.\",\n            \"type\": \"string\"\n          },\n          \"metadata\": {},\n          \"owner\": {\n            \"description\": \"OAuth 2.0 Client Owner  Owner is a string identifying the owner of the OAuth 2.0 Client.\",\n            \"type\": \"string\"\n          },\n          \"policy_uri\": {\n            \"description\": \"OAuth 2.0 Client Policy URI  PolicyURI is a URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data.\",\n            \"type\": \"string\"\n          },\n          \"post_logout_redirect_uris\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"redirect_uris\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"refresh_token_grant_access_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"refresh_token_grant_id_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"refresh_token_grant_refresh_token_lifespan\": {\n            \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n            \"type\": \"string\"\n          },\n          \"registration_access_token\": {\n            \"description\": \"OpenID Connect Dynamic Client Registration Access Token  RegistrationAccessToken can be used to update, get, or delete the OAuth2 Client. It is sent when creating a client using Dynamic Client Registration.\",\n            \"type\": \"string\"\n          },\n          \"registration_client_uri\": {\n            \"description\": \"OpenID Connect Dynamic Client Registration URL  RegistrationClientURI is the URL used to update, get, or delete the OAuth2 Client.\",\n            \"type\": \"string\"\n          },\n          \"request_object_signing_alg\": {\n            \"description\": \"OpenID Connect Request Object Signing Algorithm  JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.\",\n            \"type\": \"string\"\n          },\n          \"request_uris\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"response_types\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"scope\": {\n            \"description\": \"OAuth 2.0 Client Scope  Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens.\",\n            \"type\": \"string\"\n          },\n          \"sector_identifier_uri\": {\n            \"description\": \"OpenID Connect Sector Identifier URI  URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values.\",\n            \"type\": \"string\"\n          },\n          \"skip_consent\": {\n            \"description\": \"SkipConsent skips the consent screen for this client. This field can only be set from the admin API.\",\n            \"type\": \"boolean\"\n          },\n          \"skip_logout_consent\": {\n            \"description\": \"SkipLogoutConsent skips the logout consent screen for this client. This field can only be set from the admin API.\",\n            \"type\": \"boolean\"\n          },\n          \"subject_type\": {\n            \"description\": \"OpenID Connect Subject Type  The `subject_types_supported` Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`.\",\n            \"type\": \"string\"\n          },\n          \"token_endpoint_auth_method\": {\n            \"description\": \"OAuth 2.0 Token Endpoint Authentication Method  Requested Client Authentication method for the Token Endpoint. The options are:  `client_secret_basic`: (default) Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` encoded in the HTTP Authorization header. `client_secret_post`: Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` in the HTTP body. `private_key_jwt`: Use JSON Web Tokens to authenticate the client. `none`: Used for public clients (native apps, mobile apps) which can not have secrets.\",\n            \"type\": \"string\"\n          },\n          \"token_endpoint_auth_signing_alg\": {\n            \"description\": \"OAuth 2.0 Token Endpoint Signing Algorithm  Requested Client Authentication signing algorithm for the Token Endpoint.\",\n            \"type\": \"string\"\n          },\n          \"tos_uri\": {\n            \"description\": \"OAuth 2.0 Client Terms of Service URI  A URL string pointing to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client.\",\n            \"type\": \"string\"\n          },\n          \"updated_at\": {\n            \"description\": \"OAuth 2.0 Client Last Update Date  UpdatedAt returns the timestamp of the last update.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"userinfo_signed_response_alg\": {\n            \"description\": \"OpenID Connect Request Userinfo Signed Response Algorithm  JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type.\",\n            \"type\": \"string\"\n          }\n        },\n        \"title\": \"OAuth2Client OAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.\",\n        \"type\": \"object\"\n      },\n      \"OAuth2ConsentRequestOpenIDConnectContext\": {\n        \"description\": \"OAuth2ConsentRequestOpenIDConnectContext struct for OAuth2ConsentRequestOpenIDConnectContext\",\n        \"properties\": {\n          \"acr_values\": {\n            \"description\": \"ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request. It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required.  OpenID Connect defines it as follows: \\u003e Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter.\",\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"display\": {\n            \"description\": \"Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are: page: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode. popup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over. touch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. wap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \\\\\\\"feature phone\\\\\\\" type display.  The Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.\",\n            \"type\": \"string\"\n          },\n          \"id_token_hint_claims\": {\n            \"additionalProperties\": {},\n            \"description\": \"IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client.\",\n            \"type\": \"object\"\n          },\n          \"login_hint\": {\n            \"description\": \"LoginHint hints about the login identifier the End-User might use to log in (if necessary). This hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier) and then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a phone number in the format specified for the phone_number Claim. The use of this parameter is optional.\",\n            \"type\": \"string\"\n          },\n          \"ui_locales\": {\n            \"description\": \"UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a space-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value \\\\\\\"fr-CA fr en\\\\\\\" represents a preference for French as spoken in Canada, then French (without a region designation), followed by English (without a region designation). An error SHOULD NOT result if some or all of the requested locales are not supported by the OpenID Provider.\",\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"OAuth2LoginChallengeParams\": {\n        \"type\": \"object\"\n      },\n      \"OAuth2LoginRequest\": {\n        \"description\": \"OAuth2LoginRequest struct for OAuth2LoginRequest\",\n        \"properties\": {\n          \"challenge\": {\n            \"description\": \"ID is the identifier (\\\\\\\"login challenge\\\\\\\") of the login request. It is used to identify the session.\",\n            \"type\": \"string\"\n          },\n          \"client\": {\n            \"$ref\": \"#/components/schemas/OAuth2Client\"\n          },\n          \"oidc_context\": {\n            \"$ref\": \"#/components/schemas/OAuth2ConsentRequestOpenIDConnectContext\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters.\",\n            \"type\": \"string\"\n          },\n          \"requested_access_token_audience\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"requested_scope\": {\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"session_id\": {\n            \"description\": \"SessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag) this ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false) this will be a new random value. This value is used as the \\\\\\\"sid\\\\\\\" parameter in the ID Token and in OIDC Front-/Back- channel logout. It's value can generally be used to associate consecutive login requests by a certain user.\",\n            \"type\": \"string\"\n          },\n          \"skip\": {\n            \"description\": \"Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL.  This feature allows you to update / set session information.\",\n            \"type\": \"boolean\"\n          },\n          \"subject\": {\n            \"description\": \"Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail.\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"Provider\": {\n        \"properties\": {\n          \"client_id\": {\n            \"description\": \"The RP's client identifier, issued by the IdP.\",\n            \"type\": \"string\"\n          },\n          \"config_url\": {\n            \"description\": \"A full path of the IdP config file.\",\n            \"type\": \"string\"\n          },\n          \"domain_hint\": {\n            \"description\": \"By specifying one of domain_hints values provided by the accounts endpoints,\\nthe FedCM dialog selectively shows the specified account.\",\n            \"type\": \"string\"\n          },\n          \"fields\": {\n            \"description\": \"Array of strings that specifies the user information (\\\"name\\\", \\\" email\\\",\\n\\\"picture\\\") that RP needs IdP to share with them.\\n\\nNote: Field API is supported by Chrome 132 and later.\",\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"login_hint\": {\n            \"description\": \"By specifying one of login_hints values provided by the accounts endpoints,\\nthe FedCM dialog selectively shows the specified account.\",\n            \"type\": \"string\"\n          },\n          \"nonce\": {\n            \"description\": \"A random string to ensure the response is issued for this specific request.\\nPrevents replay attacks.\",\n            \"type\": \"string\"\n          },\n          \"parameters\": {\n            \"additionalProperties\": {\n              \"type\": \"string\"\n            },\n            \"description\": \"Custom object that allows to specify additional key-value parameters:\\nscope: A string value containing additional permissions that RP needs to\\nrequest, for example \\\" drive.readonly calendar.readonly\\\"\\nnonce: A random string to ensure the response is issued for this specific\\nrequest. Prevents replay attacks.\\n\\nOther custom key-value parameters.\\n\\nNote: parameters is supported from Chrome 132.\",\n            \"type\": \"object\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"Time\": {\n        \"format\": \"date-time\",\n        \"type\": \"string\"\n      },\n      \"UUID\": {\n        \"format\": \"uuid4\",\n        \"type\": \"string\"\n      },\n      \"UpdateFedcmFlowBody\": {\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token.\",\n            \"type\": \"string\"\n          },\n          \"nonce\": {\n            \"description\": \"Nonce is the nonce that was used in the `navigator.credentials.get` call. If\\nspecified, it must match the `nonce` claim in the token.\",\n            \"type\": \"string\"\n          },\n          \"token\": {\n            \"description\": \"Token contains the result of `navigator.credentials.get`.\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks.\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"token\",\n          \"csrf_token\"\n        ],\n        \"type\": \"object\"\n      },\n      \"authenticatorAssuranceLevel\": {\n        \"description\": \"The authenticator assurance level can be one of \\\"aal1\\\", \\\"aal2\\\", or \\\"aal3\\\". A higher number means that it is harder\\nfor an attacker to compromise the account.\\n\\nGenerally, \\\"aal1\\\" implies that one authentication factor was used while AAL2 implies that two factors (e.g.\\npassword + TOTP) have been used.\\n\\nTo learn more about these levels please head over to: https://www.ory.sh/kratos/docs/concepts/credentials\",\n        \"enum\": [\n          \"aal0\",\n          \"aal1\",\n          \"aal2\",\n          \"aal3\"\n        ],\n        \"title\": \"Authenticator Assurance Level (AAL)\",\n        \"type\": \"string\"\n      },\n      \"batchPatchIdentitiesResponse\": {\n        \"description\": \"Patch identities response\",\n        \"properties\": {\n          \"identities\": {\n            \"description\": \"The patch responses for the individual identities.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/identityPatchResponse\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"consistencyRequestParameters\": {\n        \"description\": \"Control API consistency guarantees\",\n        \"properties\": {\n          \"consistency\": {\n            \"description\": \"Read Consistency Level (preview)\\n\\nThe read consistency level determines the consistency guarantee for reads:\\n\\nstrong (slow): The read is guaranteed to return the most recent data committed at the start of the read.\\neventual (very fast): The result will return data that is about 4.8 seconds old.\\n\\nThe default consistency guarantee can be changed in the Ory Network Console or using the Ory CLI with\\n`ory patch project --replace '/previews/default_read_consistency_level=\\\"strong\\\"'`.\\n\\nSetting the default consistency level to `eventual` may cause regressions in the future as we add consistency\\ncontrols to more APIs. Currently, the following APIs will be affected by this setting:\\n\\n`GET /admin/identities`\\n\\nThis feature is in preview and only available in Ory Network.\\n ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level.\\nstrong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level.\\neventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\",\n            \"enum\": [\n              \"\",\n              \"strong\",\n              \"eventual\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \" ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level.\\nstrong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level.\\neventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"continueWith\": {\n        \"discriminator\": {\n          \"mapping\": {\n            \"redirect_browser_to\": \"#/components/schemas/continueWithRedirectBrowserTo\",\n            \"set_ory_session_token\": \"#/components/schemas/continueWithSetOrySessionToken\",\n            \"show_recovery_ui\": \"#/components/schemas/continueWithRecoveryUi\",\n            \"show_settings_ui\": \"#/components/schemas/continueWithSettingsUi\",\n            \"show_verification_ui\": \"#/components/schemas/continueWithVerificationUi\"\n          },\n          \"propertyName\": \"action\"\n        },\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/components/schemas/continueWithVerificationUi\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/continueWithSetOrySessionToken\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/continueWithSettingsUi\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/continueWithRecoveryUi\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/continueWithRedirectBrowserTo\"\n          }\n        ],\n        \"type\": \"object\"\n      },\n      \"continueWithRecoveryUi\": {\n        \"description\": \"Indicates, that the UI flow could be continued by showing a recovery ui\",\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action will always be `show_recovery_ui`\\nshow_recovery_ui ContinueWithActionShowRecoveryUIString\",\n            \"enum\": [\n              \"show_recovery_ui\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"show_recovery_ui ContinueWithActionShowRecoveryUIString\"\n          },\n          \"flow\": {\n            \"$ref\": \"#/components/schemas/continueWithRecoveryUiFlow\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"flow\"\n        ],\n        \"type\": \"object\"\n      },\n      \"continueWithRecoveryUiFlow\": {\n        \"properties\": {\n          \"id\": {\n            \"description\": \"The ID of the recovery flow\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"url\": {\n            \"description\": \"The URL of the recovery flow\\n\\nIf this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\"\n        ],\n        \"type\": \"object\"\n      },\n      \"continueWithRedirectBrowserTo\": {\n        \"description\": \"Indicates, that the UI flow could be continued by showing a recovery ui\",\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action will always be `redirect_browser_to`\\nredirect_browser_to ContinueWithActionRedirectBrowserToString\",\n            \"enum\": [\n              \"redirect_browser_to\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"redirect_browser_to ContinueWithActionRedirectBrowserToString\"\n          },\n          \"redirect_browser_to\": {\n            \"description\": \"The URL to redirect the browser to\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"redirect_browser_to\"\n        ],\n        \"type\": \"object\"\n      },\n      \"continueWithSetOrySessionToken\": {\n        \"description\": \"Indicates that a session was issued, and the application should use this token for authenticated requests\",\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action will always be `set_ory_session_token`\\nset_ory_session_token ContinueWithActionSetOrySessionTokenString\",\n            \"enum\": [\n              \"set_ory_session_token\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"set_ory_session_token ContinueWithActionSetOrySessionTokenString\"\n          },\n          \"ory_session_token\": {\n            \"description\": \"Token is the token of the session\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"ory_session_token\"\n        ],\n        \"type\": \"object\"\n      },\n      \"continueWithSettingsUi\": {\n        \"description\": \"Indicates, that the UI flow could be continued by showing a settings ui\",\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action will always be `show_settings_ui`\\nshow_settings_ui ContinueWithActionShowSettingsUIString\",\n            \"enum\": [\n              \"show_settings_ui\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"show_settings_ui ContinueWithActionShowSettingsUIString\"\n          },\n          \"flow\": {\n            \"$ref\": \"#/components/schemas/continueWithSettingsUiFlow\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"flow\"\n        ],\n        \"type\": \"object\"\n      },\n      \"continueWithSettingsUiFlow\": {\n        \"properties\": {\n          \"id\": {\n            \"description\": \"The ID of the settings flow\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"url\": {\n            \"description\": \"The URL of the settings flow\\n\\nIf this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\"\n        ],\n        \"type\": \"object\"\n      },\n      \"continueWithVerificationUi\": {\n        \"description\": \"Indicates, that the UI flow could be continued by showing a verification ui\",\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action will always be `show_verification_ui`\\nshow_verification_ui ContinueWithActionShowVerificationUIString\",\n            \"enum\": [\n              \"show_verification_ui\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"show_verification_ui ContinueWithActionShowVerificationUIString\"\n          },\n          \"flow\": {\n            \"$ref\": \"#/components/schemas/continueWithVerificationUiFlow\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"flow\"\n        ],\n        \"type\": \"object\"\n      },\n      \"continueWithVerificationUiFlow\": {\n        \"properties\": {\n          \"id\": {\n            \"description\": \"The ID of the verification flow\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"url\": {\n            \"description\": \"The URL of the verification flow\\n\\nIf this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\",\n            \"type\": \"string\"\n          },\n          \"verifiable_address\": {\n            \"description\": \"The address that should be verified in this flow\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"verifiable_address\"\n        ],\n        \"type\": \"object\"\n      },\n      \"courierMessageStatus\": {\n        \"description\": \"A Message's Status\",\n        \"enum\": [\n          \"queued\",\n          \"sent\",\n          \"processing\",\n          \"abandoned\"\n        ],\n        \"type\": \"string\"\n      },\n      \"courierMessageType\": {\n        \"description\": \"It can either be `email` or `phone`\",\n        \"enum\": [\n          \"email\",\n          \"phone\"\n        ],\n        \"title\": \"A Message's Type\",\n        \"type\": \"string\"\n      },\n      \"createFedcmFlowResponse\": {\n        \"description\": \"Contains a list of all available FedCM providers.\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"type\": \"string\"\n          },\n          \"providers\": {\n            \"items\": {\n              \"$ref\": \"#/components/schemas/Provider\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"title\": \"CreateFedcmFlowResponse\",\n        \"type\": \"object\"\n      },\n      \"createIdentityBody\": {\n        \"description\": \"Create Identity Body\",\n        \"properties\": {\n          \"credentials\": {\n            \"$ref\": \"#/components/schemas/identityWithCredentials\"\n          },\n          \"external_id\": {\n            \"description\": \"ExternalID is an optional external ID of the identity. This is used to link\\nthe identity to an external system. If set, the external ID must be unique\\nacross all identities.\",\n            \"type\": \"string\"\n          },\n          \"metadata_admin\": {\n            \"description\": \"Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/\\u003cid\\u003e`.\"\n          },\n          \"metadata_public\": {\n            \"description\": \"Store metadata about the identity which the identity itself can see when calling for example the\\nsession endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\"\n          },\n          \"organization_id\": {\n            \"$ref\": \"#/components/schemas/NullUUID\"\n          },\n          \"recovery_addresses\": {\n            \"description\": \"RecoveryAddresses contains all the addresses that can be used to recover an identity.\\n\\nUse this structure to import recovery addresses for an identity. Please keep in mind\\nthat the address needs to be represented in the Identity Schema or this field will be overwritten\\non the next identity update.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/recoveryIdentityAddress\"\n            },\n            \"type\": \"array\"\n          },\n          \"schema_id\": {\n            \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"description\": \"State is the identity's state.\\nactive StateActive\\ninactive StateInactive\",\n            \"enum\": [\n              \"active\",\n              \"inactive\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"active StateActive\\ninactive StateInactive\"\n          },\n          \"traits\": {\n            \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_url`.\",\n            \"type\": \"object\"\n          },\n          \"verifiable_addresses\": {\n            \"description\": \"VerifiableAddresses contains all the addresses that can be verified by the user.\\n\\nUse this structure to import verified addresses for an identity. Please keep in mind\\nthat the address needs to be represented in the Identity Schema or this field will be overwritten\\non the next identity update.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/verifiableIdentityAddress\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"required\": [\n          \"schema_id\",\n          \"traits\"\n        ],\n        \"type\": \"object\"\n      },\n      \"createRecoveryCodeForIdentityBody\": {\n        \"description\": \"Create Recovery Code for Identity Request Body\",\n        \"properties\": {\n          \"expires_in\": {\n            \"description\": \"Code Expires In\\n\\nThe recovery code will expire after that amount of time has passed. Defaults to the configuration value of\\n`selfservice.methods.code.config.lifespan`.\",\n            \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))*$\",\n            \"type\": \"string\"\n          },\n          \"flow_type\": {\n            \"$ref\": \"#/components/schemas/selfServiceFlowType\"\n          },\n          \"identity_id\": {\n            \"description\": \"Identity to Recover\\n\\nThe identity's ID you wish to recover.\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"identity_id\"\n        ],\n        \"type\": \"object\"\n      },\n      \"createRecoveryLinkForIdentityBody\": {\n        \"description\": \"Create Recovery Link for Identity Request Body\",\n        \"properties\": {\n          \"expires_in\": {\n            \"description\": \"Link Expires In\\n\\nThe recovery link will expire after that amount of time has passed. Defaults to the configuration value of\\n`selfservice.methods.code.config.lifespan`.\",\n            \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n            \"type\": \"string\"\n          },\n          \"identity_id\": {\n            \"description\": \"Identity to Recover\\n\\nThe identity's ID you wish to recover.\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"identity_id\"\n        ],\n        \"type\": \"object\"\n      },\n      \"deleteMySessionsCount\": {\n        \"description\": \"Deleted Session Count\",\n        \"properties\": {\n          \"count\": {\n            \"description\": \"The number of sessions that were revoked.\",\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"errorAuthenticatorAssuranceLevelNotSatisfied\": {\n        \"properties\": {\n          \"error\": {\n            \"$ref\": \"#/components/schemas/genericError\"\n          },\n          \"redirect_browser_to\": {\n            \"description\": \"Points to where to redirect the user to next.\",\n            \"type\": \"string\"\n          }\n        },\n        \"title\": \"Is returned when an active session was found but the requested AAL is not satisfied.\",\n        \"type\": \"object\"\n      },\n      \"errorBrowserLocationChangeRequired\": {\n        \"properties\": {\n          \"error\": {\n            \"$ref\": \"#/components/schemas/errorGeneric\"\n          },\n          \"redirect_browser_to\": {\n            \"description\": \"Points to where to redirect the user to next.\",\n            \"type\": \"string\"\n          }\n        },\n        \"title\": \"Is sent when a flow requires a browser to change its location.\",\n        \"type\": \"object\"\n      },\n      \"errorFlowReplaced\": {\n        \"description\": \"Is sent when a flow is replaced by a different flow of the same class\",\n        \"properties\": {\n          \"error\": {\n            \"$ref\": \"#/components/schemas/genericError\"\n          },\n          \"use_flow_id\": {\n            \"description\": \"The flow ID that should be used for the new flow as it contains the correct messages.\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"errorGeneric\": {\n        \"description\": \"The standard Ory JSON API error format.\",\n        \"properties\": {\n          \"error\": {\n            \"$ref\": \"#/components/schemas/genericError\"\n          }\n        },\n        \"required\": [\n          \"error\"\n        ],\n        \"title\": \"JSON API Error Response\",\n        \"type\": \"object\"\n      },\n      \"flowError\": {\n        \"properties\": {\n          \"created_at\": {\n            \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"error\": {\n            \"type\": \"object\"\n          },\n          \"id\": {\n            \"description\": \"ID of the error container.\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"updated_at\": {\n            \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\"\n        ],\n        \"type\": \"object\"\n      },\n      \"genericError\": {\n        \"properties\": {\n          \"code\": {\n            \"description\": \"The status code\",\n            \"example\": 404,\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          },\n          \"debug\": {\n            \"description\": \"Debug information\\n\\nThis field is often not exposed to protect against leaking\\nsensitive information.\",\n            \"example\": \"SQL field \\\"foo\\\" is not a bool.\",\n            \"type\": \"string\"\n          },\n          \"details\": {\n            \"additionalProperties\": false,\n            \"description\": \"Further error details\",\n            \"type\": \"object\"\n          },\n          \"id\": {\n            \"description\": \"The error ID\\n\\nUseful when trying to identify various errors in application logic.\",\n            \"type\": \"string\"\n          },\n          \"message\": {\n            \"description\": \"Error message\\n\\nThe error's message.\",\n            \"example\": \"The resource could not be found\",\n            \"type\": \"string\"\n          },\n          \"reason\": {\n            \"description\": \"A human-readable reason for the error\",\n            \"example\": \"User with ID 1234 does not exist.\",\n            \"type\": \"string\"\n          },\n          \"request\": {\n            \"description\": \"The request ID\\n\\nThe request ID is often exposed internally in order to trace\\nerrors across service architectures. This is often a UUID.\",\n            \"example\": \"d7ef54b1-ec15-46e6-bccb-524b82c035e6\",\n            \"type\": \"string\"\n          },\n          \"status\": {\n            \"description\": \"The status description\",\n            \"example\": \"Not Found\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"message\"\n        ],\n        \"type\": \"object\"\n      },\n      \"healthNotReadyStatus\": {\n        \"properties\": {\n          \"errors\": {\n            \"additionalProperties\": {\n              \"type\": \"string\"\n            },\n            \"description\": \"Errors contains a list of errors that caused the not ready status.\",\n            \"type\": \"object\"\n          }\n        },\n        \"title\": \"The not ready status of the service.\",\n        \"type\": \"object\"\n      },\n      \"healthStatus\": {\n        \"properties\": {\n          \"status\": {\n            \"description\": \"Status always contains \\\"ok\\\".\",\n            \"type\": \"string\"\n          }\n        },\n        \"title\": \"The health status of the service.\",\n        \"type\": \"object\"\n      },\n      \"identity\": {\n        \"description\": \"An [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) represents a (human) user in Ory.\",\n        \"properties\": {\n          \"created_at\": {\n            \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"credentials\": {\n            \"additionalProperties\": {\n              \"$ref\": \"#/components/schemas/identityCredentials\"\n            },\n            \"description\": \"Credentials represents all credentials that can be used for authenticating this identity.\",\n            \"type\": \"object\"\n          },\n          \"external_id\": {\n            \"description\": \"ExternalID is an optional external ID of the identity. This is used to link\\nthe identity to an external system. If set, the external ID must be unique\\nacross all identities.\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"ID is the identity's unique identifier.\\n\\nThe Identity ID can not be changed and can not be chosen. This ensures future\\ncompatibility and optimization for distributed stores such as CockroachDB.\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"metadata_admin\": {\n            \"$ref\": \"#/components/schemas/nullJsonRawMessage\"\n          },\n          \"metadata_public\": {\n            \"$ref\": \"#/components/schemas/nullJsonRawMessage\"\n          },\n          \"organization_id\": {\n            \"$ref\": \"#/components/schemas/NullUUID\"\n          },\n          \"recovery_addresses\": {\n            \"description\": \"RecoveryAddresses contains all the addresses that can be used to recover an identity.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/recoveryIdentityAddress\"\n            },\n            \"type\": \"array\",\n            \"x-omitempty\": true\n          },\n          \"schema_id\": {\n            \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\",\n            \"type\": \"string\"\n          },\n          \"schema_url\": {\n            \"description\": \"SchemaURL is the URL of the endpoint where the identity's traits schema can be fetched from.\\n\\nformat: url\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"description\": \"State is the identity's state.\\n\\nThis value has currently no effect.\\nactive StateActive\\ninactive StateInactive\",\n            \"enum\": [\n              \"active\",\n              \"inactive\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"active StateActive\\ninactive StateInactive\"\n          },\n          \"state_changed_at\": {\n            \"$ref\": \"#/components/schemas/nullTime\"\n          },\n          \"traits\": {\n            \"$ref\": \"#/components/schemas/identityTraits\"\n          },\n          \"updated_at\": {\n            \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"verifiable_addresses\": {\n            \"description\": \"VerifiableAddresses contains all the addresses that can be verified by the user.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/verifiableIdentityAddress\"\n            },\n            \"type\": \"array\",\n            \"x-omitempty\": true\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"schema_id\",\n          \"schema_url\",\n          \"traits\"\n        ],\n        \"title\": \"Identity represents an Ory Kratos identity\",\n        \"type\": \"object\"\n      },\n      \"identityCredentials\": {\n        \"description\": \"Credentials represents a specific credential type\",\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/JSONRawMessage\"\n          },\n          \"created_at\": {\n            \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"identifiers\": {\n            \"description\": \"Identifiers represent a list of unique identifiers this credential type matches.\",\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"type\": \"array\"\n          },\n          \"type\": {\n            \"description\": \"Type discriminates between different types of credentials.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n            \"enum\": [\n              \"password\",\n              \"oidc\",\n              \"totp\",\n              \"lookup_secret\",\n              \"webauthn\",\n              \"code\",\n              \"passkey\",\n              \"profile\",\n              \"saml\",\n              \"link_recovery\",\n              \"code_recovery\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\"\n          },\n          \"updated_at\": {\n            \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"version\": {\n            \"description\": \"Version refers to the version of the credential. Useful when changing the config schema.\",\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityCredentialsCode\": {\n        \"description\": \"CredentialsCode represents a one time login/registration code\",\n        \"properties\": {\n          \"addresses\": {\n            \"items\": {\n              \"$ref\": \"#/components/schemas/identityCredentialsCodeAddress\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityCredentialsCodeAddress\": {\n        \"properties\": {\n          \"address\": {\n            \"description\": \"The address for this code\",\n            \"type\": \"string\"\n          },\n          \"channel\": {\n            \"$ref\": \"#/components/schemas/CodeChannel\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityCredentialsOidc\": {\n        \"properties\": {\n          \"providers\": {\n            \"items\": {\n              \"$ref\": \"#/components/schemas/identityCredentialsOidcProvider\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"title\": \"CredentialsOIDC is contains the configuration for credentials of the type oidc.\",\n        \"type\": \"object\"\n      },\n      \"identityCredentialsOidcProvider\": {\n        \"properties\": {\n          \"initial_access_token\": {\n            \"type\": \"string\"\n          },\n          \"initial_id_token\": {\n            \"type\": \"string\"\n          },\n          \"initial_refresh_token\": {\n            \"type\": \"string\"\n          },\n          \"organization\": {\n            \"type\": \"string\"\n          },\n          \"provider\": {\n            \"type\": \"string\"\n          },\n          \"subject\": {\n            \"type\": \"string\"\n          },\n          \"use_auto_link\": {\n            \"type\": \"boolean\"\n          }\n        },\n        \"title\": \"CredentialsOIDCProvider is contains a specific OpenID COnnect credential for a particular connection (e.g. Google).\",\n        \"type\": \"object\"\n      },\n      \"identityCredentialsPassword\": {\n        \"properties\": {\n          \"hashed_password\": {\n            \"description\": \"HashedPassword is a hash-representation of the password.\",\n            \"type\": \"string\"\n          },\n          \"use_password_migration_hook\": {\n            \"description\": \"UsePasswordMigrationHook is set to true if the password should be migrated\\nusing the password migration hook. If set, and the HashedPassword is empty, a\\nwebhook will be called during login to migrate the password.\",\n            \"type\": \"boolean\"\n          }\n        },\n        \"title\": \"CredentialsPassword is contains the configuration for credentials of the type password.\",\n        \"type\": \"object\"\n      },\n      \"identityPatch\": {\n        \"description\": \"Payload for patching an identity\",\n        \"properties\": {\n          \"create\": {\n            \"$ref\": \"#/components/schemas/createIdentityBody\"\n          },\n          \"patch_id\": {\n            \"description\": \"The ID of this patch.\\n\\nThe patch ID is optional. If specified, the ID will be returned in the\\nresponse, so consumers of this API can correlate the response with the\\npatch.\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityPatchResponse\": {\n        \"description\": \"Response for a single identity patch\",\n        \"properties\": {\n          \"action\": {\n            \"description\": \"The action for this specific patch\\ncreate ActionCreate  Create this identity.\\nerror ActionError  Error indicates that the patch failed.\",\n            \"enum\": [\n              \"create\",\n              \"error\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"create ActionCreate  Create this identity.\\nerror ActionError  Error indicates that the patch failed.\"\n          },\n          \"error\": {\n            \"$ref\": \"#/components/schemas/DefaultError\"\n          },\n          \"identity\": {\n            \"description\": \"The identity ID payload of this patch\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"patch_id\": {\n            \"description\": \"The ID of this patch response, if an ID was specified in the patch.\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identitySchema\": {\n        \"description\": \"Raw JSON Schema\",\n        \"type\": \"object\"\n      },\n      \"identitySchemaContainer\": {\n        \"description\": \"An Identity JSON Schema Container\",\n        \"properties\": {\n          \"id\": {\n            \"description\": \"The ID of the Identity JSON Schema\",\n            \"type\": \"string\"\n          },\n          \"schema\": {\n            \"description\": \"The actual Identity JSON Schema\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"schema\"\n        ],\n        \"type\": \"object\"\n      },\n      \"identitySchemas\": {\n        \"description\": \"List of Identity JSON Schemas\",\n        \"items\": {\n          \"$ref\": \"#/components/schemas/identitySchemaContainer\"\n        },\n        \"type\": \"array\"\n      },\n      \"identityTraits\": {\n        \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_url`.\"\n      },\n      \"identityVerifiableAddressStatus\": {\n        \"description\": \"VerifiableAddressStatus must not exceed 16 characters as that is the limitation in the SQL Schema\",\n        \"type\": \"string\"\n      },\n      \"identityWithCredentials\": {\n        \"description\": \"Create Identity and Import Credentials\",\n        \"properties\": {\n          \"oidc\": {\n            \"$ref\": \"#/components/schemas/identityWithCredentialsOidc\"\n          },\n          \"password\": {\n            \"$ref\": \"#/components/schemas/identityWithCredentialsPassword\"\n          },\n          \"saml\": {\n            \"$ref\": \"#/components/schemas/identityWithCredentialsSaml\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityWithCredentialsOidc\": {\n        \"description\": \"Create Identity and Import Social Sign In Credentials\",\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/identityWithCredentialsOidcConfig\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityWithCredentialsOidcConfig\": {\n        \"properties\": {\n          \"providers\": {\n            \"description\": \"A list of OpenID Connect Providers\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/identityWithCredentialsOidcConfigProvider\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityWithCredentialsOidcConfigProvider\": {\n        \"description\": \"Create Identity and Import Social Sign In Credentials Configuration\",\n        \"properties\": {\n          \"organization\": {\n            \"$ref\": \"#/components/schemas/NullUUID\"\n          },\n          \"provider\": {\n            \"description\": \"The OpenID Connect provider to link the subject to. Usually something like `google` or `github`.\",\n            \"type\": \"string\"\n          },\n          \"subject\": {\n            \"description\": \"The subject (`sub`) of the OpenID Connect connection. Usually the `sub` field of the ID Token.\",\n            \"type\": \"string\"\n          },\n          \"use_auto_link\": {\n            \"description\": \"If set, this credential allows the user to sign in using the OpenID Connect provider without setting the subject first.\",\n            \"type\": \"boolean\"\n          }\n        },\n        \"required\": [\n          \"subject\",\n          \"provider\"\n        ],\n        \"type\": \"object\"\n      },\n      \"identityWithCredentialsPassword\": {\n        \"description\": \"Create Identity and Import Password Credentials\",\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/identityWithCredentialsPasswordConfig\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityWithCredentialsPasswordConfig\": {\n        \"description\": \"Create Identity and Import Password Credentials Configuration\",\n        \"properties\": {\n          \"hashed_password\": {\n            \"description\": \"The hashed password in [PHC format](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities#hashed-passwords)\",\n            \"type\": \"string\"\n          },\n          \"password\": {\n            \"description\": \"The password in plain text if no hash is available.\",\n            \"type\": \"string\"\n          },\n          \"use_password_migration_hook\": {\n            \"description\": \"If set to true, the password will be migrated using the password migration hook.\",\n            \"type\": \"boolean\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityWithCredentialsSaml\": {\n        \"description\": \"Payload to import SAML credentials\",\n        \"properties\": {\n          \"config\": {\n            \"$ref\": \"#/components/schemas/identityWithCredentialsSamlConfig\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityWithCredentialsSamlConfig\": {\n        \"description\": \"Payload of SAML providers\",\n        \"properties\": {\n          \"providers\": {\n            \"description\": \"A list of SAML Providers\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/identityWithCredentialsSamlConfigProvider\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"identityWithCredentialsSamlConfigProvider\": {\n        \"description\": \"Payload of specific SAML provider\",\n        \"properties\": {\n          \"organization\": {\n            \"$ref\": \"#/components/schemas/NullUUID\"\n          },\n          \"provider\": {\n            \"description\": \"The SAML provider to link the subject to.\",\n            \"type\": \"string\"\n          },\n          \"subject\": {\n            \"description\": \"The unique subject of the SAML connection. This value must be immutable at the source.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"subject\",\n          \"provider\"\n        ],\n        \"type\": \"object\"\n      },\n      \"jsonPatch\": {\n        \"description\": \"A JSONPatch document as defined by RFC 6902\",\n        \"properties\": {\n          \"from\": {\n            \"description\": \"This field is used together with operation \\\"move\\\" and uses JSON Pointer notation.\\n\\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\",\n            \"example\": \"/name\",\n            \"type\": \"string\"\n          },\n          \"op\": {\n            \"description\": \"The operation to be performed. One of \\\"add\\\", \\\"remove\\\", \\\"replace\\\", \\\"move\\\", \\\"copy\\\", or \\\"test\\\".\",\n            \"example\": \"replace\",\n            \"type\": \"string\"\n          },\n          \"path\": {\n            \"description\": \"The path to the target path. Uses JSON pointer notation.\\n\\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\",\n            \"example\": \"/name\",\n            \"type\": \"string\"\n          },\n          \"value\": {\n            \"description\": \"The value to be used within the operations.\\n\\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\",\n            \"example\": \"foobar\"\n          }\n        },\n        \"required\": [\n          \"op\",\n          \"path\"\n        ],\n        \"type\": \"object\"\n      },\n      \"jsonPatchDocument\": {\n        \"description\": \"A JSONPatchDocument request\",\n        \"items\": {\n          \"$ref\": \"#/components/schemas/jsonPatch\"\n        },\n        \"type\": \"array\"\n      },\n      \"loginFlow\": {\n        \"description\": \"This object represents a login flow. A login flow is initiated at the \\\"Initiate Login API / Browser Flow\\\"\\nendpoint by a client.\\n\\nOnce a login flow is completed successfully, a session cookie or session token will be issued.\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"The active login method\\n\\nIf set contains the login method used. If the flow is new, it is unset.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n            \"enum\": [\n              \"password\",\n              \"oidc\",\n              \"totp\",\n              \"lookup_secret\",\n              \"webauthn\",\n              \"code\",\n              \"passkey\",\n              \"profile\",\n              \"saml\",\n              \"link_recovery\",\n              \"code_recovery\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\"\n          },\n          \"created_at\": {\n            \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\\na new flow has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"ID represents the flow's unique ID. When performing the login flow, this\\nrepresents the id in the login UI's query parameter: http://\\u003cselfservice.flows.login.ui_url\\u003e/?flow=\\u003cflow_id\\u003e\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"identity_schema\": {\n            \"description\": \"IdentitySchema optionally holds the ID of the identity schema that is used\\nfor this flow. This value can be set by the user when creating the flow and\\nshould be retained when the flow is saved or converted to another flow.\",\n            \"type\": \"string\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the flow started.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"oauth2_login_challenge\": {\n            \"description\": \"Ory OAuth 2.0 Login Challenge.\\n\\nThis value is set using the `login_challenge` query parameter of the registration and login endpoints.\\nIf set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\",\n            \"type\": \"string\"\n          },\n          \"oauth2_login_request\": {\n            \"$ref\": \"#/components/schemas/OAuth2LoginRequest\"\n          },\n          \"organization_id\": {\n            \"$ref\": \"#/components/schemas/NullUUID\"\n          },\n          \"refresh\": {\n            \"description\": \"Refresh stores whether this login flow should enforce re-authentication.\",\n            \"type\": \"boolean\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"requested_aal\": {\n            \"$ref\": \"#/components/schemas/authenticatorAssuranceLevel\"\n          },\n          \"return_to\": {\n            \"description\": \"ReturnTo contains the requested return_to URL.\",\n            \"type\": \"string\"\n          },\n          \"session_token_exchange_code\": {\n            \"description\": \"SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the login flow has been completed.\\nThis is only set if the client has requested a session token exchange code, and if the flow is of type \\\"api\\\",\\nand only on creating the login flow.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"description\": \"State represents the state of this request:\\n\\nchoose_method: ask the user to choose a method to sign in with\\nsent_email: the email has been sent to the user\\npassed_challenge: the request was successful and the login challenge was passed.\"\n          },\n          \"transient_payload\": {\n            \"description\": \"TransientPayload is used to pass data from the login to hooks and email templates\",\n            \"type\": \"object\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/selfServiceFlowType\"\n          },\n          \"ui\": {\n            \"$ref\": \"#/components/schemas/uiContainer\"\n          },\n          \"updated_at\": {\n            \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"type\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"ui\",\n          \"state\"\n        ],\n        \"title\": \"Login Flow\",\n        \"type\": \"object\"\n      },\n      \"loginFlowState\": {\n        \"description\": \"The experimental state represents the state of a login flow. This field is EXPERIMENTAL and subject to change!\",\n        \"enum\": [\n          \"choose_method\",\n          \"sent_email\",\n          \"passed_challenge\"\n        ],\n        \"title\": \"Login flow state (experimental)\",\n        \"type\": \"string\"\n      },\n      \"logoutFlow\": {\n        \"description\": \"Logout Flow\",\n        \"properties\": {\n          \"logout_token\": {\n            \"description\": \"LogoutToken can be used to perform logout using AJAX.\",\n            \"type\": \"string\"\n          },\n          \"logout_url\": {\n            \"description\": \"LogoutURL can be opened in a browser to sign the user out.\\n\\nformat: uri\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"logout_url\",\n          \"logout_token\"\n        ],\n        \"type\": \"object\"\n      },\n      \"message\": {\n        \"properties\": {\n          \"body\": {\n            \"type\": \"string\"\n          },\n          \"channel\": {\n            \"type\": \"string\"\n          },\n          \"created_at\": {\n            \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"dispatches\": {\n            \"description\": \"Dispatches store information about the attempts of delivering a message\\nMay contain an error if any happened, or just the `success` state.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/messageDispatch\"\n            },\n            \"type\": \"array\"\n          },\n          \"id\": {\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"recipient\": {\n            \"type\": \"string\"\n          },\n          \"send_count\": {\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          },\n          \"status\": {\n            \"$ref\": \"#/components/schemas/courierMessageStatus\"\n          },\n          \"subject\": {\n            \"type\": \"string\"\n          },\n          \"template_type\": {\n            \"description\": \"\\nrecovery_invalid TypeRecoveryInvalid\\nrecovery_valid TypeRecoveryValid\\nrecovery_code_invalid TypeRecoveryCodeInvalid\\nrecovery_code_valid TypeRecoveryCodeValid\\nverification_invalid TypeVerificationInvalid\\nverification_valid TypeVerificationValid\\nverification_code_invalid TypeVerificationCodeInvalid\\nverification_code_valid TypeVerificationCodeValid\\nstub TypeTestStub\\nlogin_code_valid TypeLoginCodeValid\\nregistration_code_valid TypeRegistrationCodeValid\",\n            \"enum\": [\n              \"recovery_invalid\",\n              \"recovery_valid\",\n              \"recovery_code_invalid\",\n              \"recovery_code_valid\",\n              \"verification_invalid\",\n              \"verification_valid\",\n              \"verification_code_invalid\",\n              \"verification_code_valid\",\n              \"stub\",\n              \"login_code_valid\",\n              \"registration_code_valid\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"recovery_invalid TypeRecoveryInvalid\\nrecovery_valid TypeRecoveryValid\\nrecovery_code_invalid TypeRecoveryCodeInvalid\\nrecovery_code_valid TypeRecoveryCodeValid\\nverification_invalid TypeVerificationInvalid\\nverification_valid TypeVerificationValid\\nverification_code_invalid TypeVerificationCodeInvalid\\nverification_code_valid TypeVerificationCodeValid\\nstub TypeTestStub\\nlogin_code_valid TypeLoginCodeValid\\nregistration_code_valid TypeRegistrationCodeValid\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/courierMessageType\"\n          },\n          \"updated_at\": {\n            \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"status\",\n          \"type\",\n          \"recipient\",\n          \"body\",\n          \"subject\",\n          \"template_type\",\n          \"send_count\",\n          \"created_at\",\n          \"updated_at\"\n        ],\n        \"type\": \"object\"\n      },\n      \"messageDispatch\": {\n        \"description\": \"MessageDispatch represents an attempt of sending a courier message\\nIt contains the status of the attempt (failed or successful) and the error if any occured\",\n        \"properties\": {\n          \"created_at\": {\n            \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"error\": {\n            \"$ref\": \"#/components/schemas/JSONRawMessage\"\n          },\n          \"id\": {\n            \"description\": \"The ID of this message dispatch\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"message_id\": {\n            \"description\": \"The ID of the message being dispatched\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"status\": {\n            \"description\": \"The status of this dispatch\\nEither \\\"failed\\\" or \\\"success\\\"\\nfailed CourierMessageDispatchStatusFailed\\nsuccess CourierMessageDispatchStatusSuccess\",\n            \"enum\": [\n              \"failed\",\n              \"success\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"failed CourierMessageDispatchStatusFailed\\nsuccess CourierMessageDispatchStatusSuccess\"\n          },\n          \"updated_at\": {\n            \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"message_id\",\n          \"status\",\n          \"created_at\",\n          \"updated_at\"\n        ],\n        \"type\": \"object\"\n      },\n      \"needsPrivilegedSessionError\": {\n        \"properties\": {\n          \"error\": {\n            \"$ref\": \"#/components/schemas/genericError\"\n          },\n          \"redirect_browser_to\": {\n            \"description\": \"Points to where to redirect the user to next.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"redirect_browser_to\"\n        ],\n        \"title\": \"Is sent when a privileged session is required to perform the settings update.\",\n        \"type\": \"object\"\n      },\n      \"nullDuration\": {\n        \"nullable\": true,\n        \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\",\n        \"type\": \"string\"\n      },\n      \"nullInt64\": {\n        \"nullable\": true,\n        \"type\": \"integer\"\n      },\n      \"nullJsonRawMessage\": {\n        \"description\": \"NullJSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger and is NULLable-\",\n        \"nullable\": true\n      },\n      \"nullTime\": {\n        \"format\": \"date-time\",\n        \"title\": \"NullTime implements sql.NullTime functionality.\",\n        \"type\": \"string\"\n      },\n      \"patchIdentitiesBody\": {\n        \"description\": \"Patch Identities Body\",\n        \"properties\": {\n          \"identities\": {\n            \"description\": \"Identities holds the list of patches to apply\\n\\nrequired\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/identityPatch\"\n            },\n            \"type\": \"array\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"performNativeLogoutBody\": {\n        \"description\": \"Perform Native Logout Request Body\",\n        \"properties\": {\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nInvalidate this session token.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"session_token\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryCodeForIdentity\": {\n        \"description\": \"Used when an administrator creates a recovery code for an identity.\",\n        \"properties\": {\n          \"expires_at\": {\n            \"description\": \"Expires At is the timestamp of when the recovery flow expires\\n\\nThe timestamp when the recovery code expires.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"recovery_code\": {\n            \"description\": \"RecoveryCode is the code that can be used to recover the account\",\n            \"type\": \"string\"\n          },\n          \"recovery_link\": {\n            \"description\": \"RecoveryLink with flow\\n\\nThis link opens the recovery UI with an empty `code` field.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"recovery_link\",\n          \"recovery_code\"\n        ],\n        \"title\": \"Recovery Code for Identity\",\n        \"type\": \"object\"\n      },\n      \"recoveryFlow\": {\n        \"description\": \"This request is used when an identity wants to recover their account.\\n\\nWe recommend reading the [Account Recovery Documentation](../self-service/flows/password-reset-account-recovery)\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the recovery method that is being used. It is initially\\nnot set.\",\n            \"type\": \"string\"\n          },\n          \"continue_with\": {\n            \"description\": \"Contains possible actions that could follow this flow\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/continueWith\"\n            },\n            \"type\": \"array\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the request expires. If the user still wishes to update the setting,\\na new request has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"ID represents the request's unique ID. When performing the recovery flow, this\\nrepresents the id in the recovery ui's query parameter: http://\\u003cselfservice.flows.recovery.ui_url\\u003e?request=\\u003cid\\u003e\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the request occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"return_to\": {\n            \"description\": \"ReturnTo contains the requested return_to URL.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"description\": \"State represents the state of this request:\\n\\nchoose_method: ask the user to choose a method (e.g. recover account via email)\\nsent_email: the email has been sent to the user\\npassed_challenge: the request was successful and the recovery challenge was passed.\"\n          },\n          \"transient_payload\": {\n            \"description\": \"TransientPayload is used to pass data from the recovery flow to hooks and email templates\",\n            \"type\": \"object\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/selfServiceFlowType\"\n          },\n          \"ui\": {\n            \"$ref\": \"#/components/schemas/uiContainer\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"type\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"ui\",\n          \"state\"\n        ],\n        \"title\": \"A Recovery Flow\",\n        \"type\": \"object\"\n      },\n      \"recoveryFlowState\": {\n        \"description\": \"The experimental state represents the state of a recovery flow. This field is EXPERIMENTAL and subject to change!\",\n        \"enum\": [\n          \"choose_method\",\n          \"sent_email\",\n          \"passed_challenge\"\n        ],\n        \"title\": \"Recovery flow state (experimental)\"\n      },\n      \"recoveryIdentityAddress\": {\n        \"properties\": {\n          \"created_at\": {\n            \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"updated_at\": {\n            \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"value\": {\n            \"type\": \"string\"\n          },\n          \"via\": {\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"value\",\n          \"via\"\n        ],\n        \"type\": \"object\"\n      },\n      \"recoveryLinkForIdentity\": {\n        \"description\": \"Used when an administrator creates a recovery link for an identity.\",\n        \"properties\": {\n          \"expires_at\": {\n            \"description\": \"Recovery Link Expires At\\n\\nThe timestamp when the recovery link expires.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"recovery_link\": {\n            \"description\": \"Recovery Link\\n\\nThis link can be used to recover the account.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"recovery_link\"\n        ],\n        \"title\": \"Identity Recovery Link\",\n        \"type\": \"object\"\n      },\n      \"registrationFlow\": {\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n            \"enum\": [\n              \"password\",\n              \"oidc\",\n              \"totp\",\n              \"lookup_secret\",\n              \"webauthn\",\n              \"code\",\n              \"passkey\",\n              \"profile\",\n              \"saml\",\n              \"link_recovery\",\n              \"code_recovery\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\\na new flow has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"ID represents the flow's unique ID. When performing the registration flow, this\\nrepresents the id in the registration ui's query parameter: http://\\u003cselfservice.flows.registration.ui_url\\u003e/?flow=\\u003cid\\u003e\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"identity_schema\": {\n            \"description\": \"IdentitySchema optionally holds the ID of the identity schema that is used\\nfor this flow. This value can be set by the user when creating the flow and\\nshould be retained when the flow is saved or converted to another flow.\",\n            \"type\": \"string\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the flow occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"oauth2_login_challenge\": {\n            \"description\": \"Ory OAuth 2.0 Login Challenge.\\n\\nThis value is set using the `login_challenge` query parameter of the registration and login endpoints.\\nIf set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\",\n            \"type\": \"string\"\n          },\n          \"oauth2_login_request\": {\n            \"$ref\": \"#/components/schemas/OAuth2LoginRequest\"\n          },\n          \"organization_id\": {\n            \"$ref\": \"#/components/schemas/NullUUID\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"return_to\": {\n            \"description\": \"ReturnTo contains the requested return_to URL.\",\n            \"type\": \"string\"\n          },\n          \"session_token_exchange_code\": {\n            \"description\": \"SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the flow has been completed.\\nThis is only set if the client has requested a session token exchange code, and if the flow is of type \\\"api\\\",\\nand only on creating the flow.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"description\": \"State represents the state of this request:\\n\\nchoose_method: ask the user to choose a method (e.g. registration with email)\\nsent_email: the email has been sent to the user\\npassed_challenge: the request was successful and the registration challenge was passed.\"\n          },\n          \"transient_payload\": {\n            \"description\": \"TransientPayload is used to pass data from the registration to a webhook\",\n            \"type\": \"object\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/selfServiceFlowType\"\n          },\n          \"ui\": {\n            \"$ref\": \"#/components/schemas/uiContainer\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"type\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"ui\",\n          \"state\"\n        ],\n        \"type\": \"object\"\n      },\n      \"registrationFlowState\": {\n        \"description\": \"The experimental state represents the state of a registration flow. This field is EXPERIMENTAL and subject to change!\",\n        \"enum\": [\n          \"choose_method\",\n          \"sent_email\",\n          \"passed_challenge\"\n        ],\n        \"title\": \"Registration flow state (experimental)\",\n        \"type\": \"string\"\n      },\n      \"selfServiceFlowExpiredError\": {\n        \"description\": \"Is sent when a flow is expired\",\n        \"properties\": {\n          \"error\": {\n            \"$ref\": \"#/components/schemas/genericError\"\n          },\n          \"expired_at\": {\n            \"description\": \"When the flow has expired\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"since\": {\n            \"$ref\": \"#/components/schemas/Duration\"\n          },\n          \"use_flow_id\": {\n            \"description\": \"The flow ID that should be used for the new flow as it contains the correct messages.\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"selfServiceFlowType\": {\n        \"description\": \"The flow type can either be `api` or `browser`.\",\n        \"title\": \"Type is the flow type.\",\n        \"type\": \"string\"\n      },\n      \"session\": {\n        \"description\": \"A Session\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active state. If false the session is no longer active.\",\n            \"type\": \"boolean\"\n          },\n          \"authenticated_at\": {\n            \"description\": \"The Session Authentication Timestamp\\n\\nWhen this session was authenticated at. If multi-factor authentication was used this\\nis the time when the last factor was authenticated (e.g. the TOTP code challenge was completed).\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"authentication_methods\": {\n            \"$ref\": \"#/components/schemas/sessionAuthenticationMethods\"\n          },\n          \"authenticator_assurance_level\": {\n            \"$ref\": \"#/components/schemas/authenticatorAssuranceLevel\"\n          },\n          \"devices\": {\n            \"description\": \"Devices has history of all endpoints where the session was used\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/sessionDevice\"\n            },\n            \"type\": \"array\"\n          },\n          \"expires_at\": {\n            \"description\": \"The Session Expiry\\n\\nWhen this session expires at.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"Session ID\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/identity\"\n          },\n          \"issued_at\": {\n            \"description\": \"The Session Issuance Timestamp\\n\\nWhen this session was issued at. Usually equal or close to `authenticated_at`.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"tokenized\": {\n            \"description\": \"Tokenized is the tokenized (e.g. JWT) version of the session.\\n\\nIt is only set when the `tokenize_as` query parameter was set to a valid tokenize template during calls to `/session/whoami`.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\"\n        ],\n        \"type\": \"object\"\n      },\n      \"sessionAuthenticationMethod\": {\n        \"description\": \"A singular authenticator used during authentication / login.\",\n        \"properties\": {\n          \"aal\": {\n            \"$ref\": \"#/components/schemas/authenticatorAssuranceLevel\"\n          },\n          \"completed_at\": {\n            \"description\": \"When the authentication challenge was completed.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"The method used in this authenticator.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n            \"enum\": [\n              \"password\",\n              \"oidc\",\n              \"totp\",\n              \"lookup_secret\",\n              \"webauthn\",\n              \"code\",\n              \"passkey\",\n              \"profile\",\n              \"saml\",\n              \"link_recovery\",\n              \"code_recovery\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\"\n          },\n          \"organization\": {\n            \"description\": \"The Organization id used for authentication\",\n            \"type\": \"string\"\n          },\n          \"provider\": {\n            \"description\": \"OIDC or SAML provider id used for authentication\",\n            \"type\": \"string\"\n          }\n        },\n        \"title\": \"AuthenticationMethod identifies an authentication method\",\n        \"type\": \"object\"\n      },\n      \"sessionAuthenticationMethods\": {\n        \"description\": \"A list of authenticators which were used to authenticate the session.\",\n        \"items\": {\n          \"$ref\": \"#/components/schemas/sessionAuthenticationMethod\"\n        },\n        \"title\": \"List of (Used) AuthenticationMethods\",\n        \"type\": \"array\"\n      },\n      \"sessionDevice\": {\n        \"description\": \"Device corresponding to a Session\",\n        \"properties\": {\n          \"id\": {\n            \"description\": \"Device record ID\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"ip_address\": {\n            \"description\": \"IPAddress of the client\",\n            \"type\": \"string\"\n          },\n          \"location\": {\n            \"description\": \"Geo Location corresponding to the IP Address\",\n            \"type\": \"string\"\n          },\n          \"user_agent\": {\n            \"description\": \"UserAgent of the client\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"id\"\n        ],\n        \"type\": \"object\"\n      },\n      \"settingsFlow\": {\n        \"description\": \"This flow is used when an identity wants to update settings\\n(e.g. profile data, passwords, ...) in a selfservice manner.\\n\\nWe recommend reading the [User Settings Documentation](../self-service/flows/user-settings)\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n            \"type\": \"string\"\n          },\n          \"continue_with\": {\n            \"description\": \"Contains a list of actions, that could follow this flow\\n\\nIt can, for example, contain a reference to the verification flow, created as part of the user's\\nregistration.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/continueWith\"\n            },\n            \"type\": \"array\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to update the setting,\\na new flow has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"ID represents the flow's unique ID. When performing the settings flow, this\\nrepresents the id in the settings ui's query parameter: http://\\u003cselfservice.flows.settings.ui_url\\u003e?flow=\\u003cid\\u003e\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/identity\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the flow occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"return_to\": {\n            \"description\": \"ReturnTo contains the requested return_to URL.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"description\": \"State represents the state of this flow. It knows two states:\\n\\nshow_form: No user data has been collected, or it is invalid, and thus the form should be shown.\\nsuccess: Indicates that the settings flow has been updated successfully with the provided data.\\nDone will stay true when repeatedly checking. If set to true, done will revert back to false only\\nwhen a flow with invalid (e.g. \\\"please use a valid phone number\\\") data was sent.\"\n          },\n          \"transient_payload\": {\n            \"description\": \"TransientPayload is used to pass data from the settings flow to hooks and email templates\",\n            \"type\": \"object\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/selfServiceFlowType\"\n          },\n          \"ui\": {\n            \"$ref\": \"#/components/schemas/uiContainer\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"type\",\n          \"expires_at\",\n          \"issued_at\",\n          \"request_url\",\n          \"ui\",\n          \"identity\",\n          \"state\"\n        ],\n        \"title\": \"Flow represents a Settings Flow\",\n        \"type\": \"object\"\n      },\n      \"settingsFlowState\": {\n        \"description\": \"The experimental state represents the state of a settings flow. This field is EXPERIMENTAL and subject to change!\",\n        \"enum\": [\n          \"show_form\",\n          \"success\"\n        ],\n        \"title\": \"Settings flow state (experimental)\",\n        \"type\": \"string\"\n      },\n      \"successfulCodeExchangeResponse\": {\n        \"description\": \"The Response for Registration Flows via API\",\n        \"properties\": {\n          \"session\": {\n            \"$ref\": \"#/components/schemas/session\"\n          },\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"session\"\n        ],\n        \"type\": \"object\"\n      },\n      \"successfulNativeLogin\": {\n        \"description\": \"The Response for Login Flows via API\",\n        \"properties\": {\n          \"continue_with\": {\n            \"description\": \"Contains a list of actions, that could follow this flow\\n\\nIt can, for example, this will contain a reference to the verification flow, created as part of the user's\\nregistration or the token of the session.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/continueWith\"\n            },\n            \"type\": \"array\"\n          },\n          \"session\": {\n            \"$ref\": \"#/components/schemas/session\"\n          },\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"session\"\n        ],\n        \"type\": \"object\"\n      },\n      \"successfulNativeRegistration\": {\n        \"description\": \"The Response for Registration Flows via API\",\n        \"properties\": {\n          \"continue_with\": {\n            \"description\": \"Contains a list of actions, that could follow this flow\\n\\nIt can, for example, this will contain a reference to the verification flow, created as part of the user's\\nregistration or the token of the session.\",\n            \"items\": {\n              \"$ref\": \"#/components/schemas/continueWith\"\n            },\n            \"type\": \"array\"\n          },\n          \"identity\": {\n            \"$ref\": \"#/components/schemas/identity\"\n          },\n          \"session\": {\n            \"$ref\": \"#/components/schemas/session\"\n          },\n          \"session_token\": {\n            \"description\": \"The Session Token\\n\\nThis field is only set when the session hook is configured as a post-registration hook.\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"identity\"\n        ],\n        \"type\": \"object\"\n      },\n      \"tokenPagination\": {\n        \"properties\": {\n          \"page_size\": {\n            \"default\": 250,\n            \"description\": \"Items per page\\n\\nThis is the number of items per page to return.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"format\": \"int64\",\n            \"maximum\": 1000,\n            \"minimum\": 1,\n            \"type\": \"integer\"\n          },\n          \"page_token\": {\n            \"description\": \"Next Page Token\\n\\nThe next page token.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"tokenPaginationHeaders\": {\n        \"properties\": {\n          \"link\": {\n            \"description\": \"The link header contains pagination links.\\n\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\\n\\nin: header\",\n            \"type\": \"string\"\n          },\n          \"x-total-count\": {\n            \"description\": \"The total number of clients.\\n\\nin: header\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"uiContainer\": {\n        \"description\": \"Container represents a HTML Form. The container can work with both HTTP Form and JSON requests\",\n        \"properties\": {\n          \"action\": {\n            \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n            \"type\": \"string\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"method\": {\n            \"description\": \"Method is the form method (e.g. POST)\",\n            \"type\": \"string\"\n          },\n          \"nodes\": {\n            \"$ref\": \"#/components/schemas/uiNodes\"\n          }\n        },\n        \"required\": [\n          \"action\",\n          \"method\",\n          \"nodes\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiNode\": {\n        \"description\": \"Nodes are represented as HTML elements or their native UI equivalents. For example,\\na node can be an `\\u003cimg\\u003e` tag, or an `\\u003cinput element\\u003e` but also `some plain text`.\",\n        \"properties\": {\n          \"attributes\": {\n            \"$ref\": \"#/components/schemas/uiNodeAttributes\"\n          },\n          \"group\": {\n            \"description\": \"Group specifies which group (e.g. password authenticator) this node belongs to.\\ndefault DefaultGroup\\npassword PasswordGroup\\noidc OpenIDConnectGroup\\nprofile ProfileGroup\\nlink LinkGroup\\ncode CodeGroup\\ntotp TOTPGroup\\nlookup_secret LookupGroup\\nwebauthn WebAuthnGroup\\npasskey PasskeyGroup\\nidentifier_first IdentifierFirstGroup\\ncaptcha CaptchaGroup\\nsaml SAMLGroup\",\n            \"enum\": [\n              \"default\",\n              \"password\",\n              \"oidc\",\n              \"profile\",\n              \"link\",\n              \"code\",\n              \"totp\",\n              \"lookup_secret\",\n              \"webauthn\",\n              \"passkey\",\n              \"identifier_first\",\n              \"captcha\",\n              \"saml\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"default DefaultGroup\\npassword PasswordGroup\\noidc OpenIDConnectGroup\\nprofile ProfileGroup\\nlink LinkGroup\\ncode CodeGroup\\ntotp TOTPGroup\\nlookup_secret LookupGroup\\nwebauthn WebAuthnGroup\\npasskey PasskeyGroup\\nidentifier_first IdentifierFirstGroup\\ncaptcha CaptchaGroup\\nsaml SAMLGroup\"\n          },\n          \"messages\": {\n            \"$ref\": \"#/components/schemas/uiTexts\"\n          },\n          \"meta\": {\n            \"$ref\": \"#/components/schemas/uiNodeMeta\"\n          },\n          \"type\": {\n            \"description\": \"The node's type\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n            \"enum\": [\n              \"text\",\n              \"input\",\n              \"img\",\n              \"a\",\n              \"script\",\n              \"div\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n          }\n        },\n        \"required\": [\n          \"type\",\n          \"group\",\n          \"attributes\",\n          \"messages\",\n          \"meta\"\n        ],\n        \"title\": \"Node represents a flow's nodes\",\n        \"type\": \"object\"\n      },\n      \"uiNodeAnchorAttributes\": {\n        \"properties\": {\n          \"href\": {\n            \"description\": \"The link's href (destination) URL.\\n\\nformat: uri\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"A unique identifier\",\n            \"type\": \"string\"\n          },\n          \"node_type\": {\n            \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"a\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n            \"enum\": [\n              \"a\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n          },\n          \"title\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          }\n        },\n        \"required\": [\n          \"href\",\n          \"title\",\n          \"id\",\n          \"node_type\"\n        ],\n        \"title\": \"AnchorAttributes represents the attributes of an anchor node.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeAttributes\": {\n        \"discriminator\": {\n          \"mapping\": {\n            \"a\": \"#/components/schemas/uiNodeAnchorAttributes\",\n            \"div\": \"#/components/schemas/uiNodeDivisionAttributes\",\n            \"img\": \"#/components/schemas/uiNodeImageAttributes\",\n            \"input\": \"#/components/schemas/uiNodeInputAttributes\",\n            \"script\": \"#/components/schemas/uiNodeScriptAttributes\",\n            \"text\": \"#/components/schemas/uiNodeTextAttributes\"\n          },\n          \"propertyName\": \"node_type\"\n        },\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/components/schemas/uiNodeInputAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeTextAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeImageAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeAnchorAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeScriptAttributes\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/uiNodeDivisionAttributes\"\n          }\n        ],\n        \"title\": \"Attributes represents a list of attributes (e.g. `href=\\\"foo\\\"` for links).\"\n      },\n      \"uiNodeDivisionAttributes\": {\n        \"description\": \"Division sections are used for interactive widgets that require a hook in the DOM / view.\",\n        \"properties\": {\n          \"class\": {\n            \"description\": \"A classname that should be rendered into the DOM.\",\n            \"type\": \"string\"\n          },\n          \"data\": {\n            \"additionalProperties\": {\n              \"type\": \"string\"\n            },\n            \"description\": \"Data is a map of key-value pairs that are passed to the division.\\n\\nThey may be used for `data-...` attributes.\",\n            \"type\": \"object\"\n          },\n          \"id\": {\n            \"description\": \"A unique identifier\",\n            \"type\": \"string\"\n          },\n          \"node_type\": {\n            \"description\": \"NodeType represents this node's type. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \\\"script\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n            \"enum\": [\n              \"div\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"node_type\"\n        ],\n        \"title\": \"DivisionAttributes represent a division section.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeImageAttributes\": {\n        \"properties\": {\n          \"height\": {\n            \"description\": \"Height of the image\",\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          },\n          \"id\": {\n            \"description\": \"A unique identifier\",\n            \"type\": \"string\"\n          },\n          \"node_type\": {\n            \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"img\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n            \"enum\": [\n              \"img\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n          },\n          \"src\": {\n            \"description\": \"The image's source URL.\\n\\nformat: uri\",\n            \"type\": \"string\"\n          },\n          \"width\": {\n            \"description\": \"Width of the image\",\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          }\n        },\n        \"required\": [\n          \"src\",\n          \"id\",\n          \"width\",\n          \"height\",\n          \"node_type\"\n        ],\n        \"title\": \"ImageAttributes represents the attributes of an image node.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeInputAttributes\": {\n        \"description\": \"InputAttributes represents the attributes of an input node\",\n        \"properties\": {\n          \"autocomplete\": {\n            \"description\": \"The autocomplete attribute for the input.\\nemail InputAttributeAutocompleteEmail\\ntel InputAttributeAutocompleteTel\\nurl InputAttributeAutocompleteUrl\\ncurrent-password InputAttributeAutocompleteCurrentPassword\\nnew-password InputAttributeAutocompleteNewPassword\\none-time-code InputAttributeAutocompleteOneTimeCode\\nusername webauthn InputAttributeAutocompleteUsernameWebauthn\",\n            \"enum\": [\n              \"email\",\n              \"tel\",\n              \"url\",\n              \"current-password\",\n              \"new-password\",\n              \"one-time-code\",\n              \"username webauthn\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"email InputAttributeAutocompleteEmail\\ntel InputAttributeAutocompleteTel\\nurl InputAttributeAutocompleteUrl\\ncurrent-password InputAttributeAutocompleteCurrentPassword\\nnew-password InputAttributeAutocompleteNewPassword\\none-time-code InputAttributeAutocompleteOneTimeCode\\nusername webauthn InputAttributeAutocompleteUsernameWebauthn\"\n          },\n          \"disabled\": {\n            \"description\": \"Sets the input's disabled field to true or false.\",\n            \"type\": \"boolean\"\n          },\n          \"label\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          },\n          \"maxlength\": {\n            \"description\": \"MaxLength may contain the input's maximum length.\",\n            \"format\": \"int64\",\n            \"type\": \"integer\"\n          },\n          \"name\": {\n            \"description\": \"The input's element name.\",\n            \"type\": \"string\"\n          },\n          \"node_type\": {\n            \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"input\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n            \"enum\": [\n              \"input\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n          },\n          \"onclick\": {\n            \"description\": \"OnClick may contain javascript which should be executed on click. This is primarily\\nused for WebAuthn.\\n\\nDeprecated: Using OnClick requires the use of eval() which is a security risk. Use OnClickTrigger instead.\",\n            \"type\": \"string\"\n          },\n          \"onclickTrigger\": {\n            \"description\": \"OnClickTrigger may contain a WebAuthn trigger which should be executed on click.\\n\\nThe trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login.\\noryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration\\noryWebAuthnLogin WebAuthnTriggersWebAuthnLogin\\noryPasskeyLogin WebAuthnTriggersPasskeyLogin\\noryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit\\noryPasskeyRegistration WebAuthnTriggersPasskeyRegistration\\noryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\",\n            \"enum\": [\n              \"oryWebAuthnRegistration\",\n              \"oryWebAuthnLogin\",\n              \"oryPasskeyLogin\",\n              \"oryPasskeyLoginAutocompleteInit\",\n              \"oryPasskeyRegistration\",\n              \"oryPasskeySettingsRegistration\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"oryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration\\noryWebAuthnLogin WebAuthnTriggersWebAuthnLogin\\noryPasskeyLogin WebAuthnTriggersPasskeyLogin\\noryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit\\noryPasskeyRegistration WebAuthnTriggersPasskeyRegistration\\noryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\"\n          },\n          \"onload\": {\n            \"description\": \"OnLoad may contain javascript which should be executed on load. This is primarily\\nused for WebAuthn.\\n\\nDeprecated: Using OnLoad requires the use of eval() which is a security risk. Use OnLoadTrigger instead.\",\n            \"type\": \"string\"\n          },\n          \"onloadTrigger\": {\n            \"description\": \"OnLoadTrigger may contain a WebAuthn trigger which should be executed on load.\\n\\nThe trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login.\\noryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration\\noryWebAuthnLogin WebAuthnTriggersWebAuthnLogin\\noryPasskeyLogin WebAuthnTriggersPasskeyLogin\\noryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit\\noryPasskeyRegistration WebAuthnTriggersPasskeyRegistration\\noryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\",\n            \"enum\": [\n              \"oryWebAuthnRegistration\",\n              \"oryWebAuthnLogin\",\n              \"oryPasskeyLogin\",\n              \"oryPasskeyLoginAutocompleteInit\",\n              \"oryPasskeyRegistration\",\n              \"oryPasskeySettingsRegistration\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"oryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration\\noryWebAuthnLogin WebAuthnTriggersWebAuthnLogin\\noryPasskeyLogin WebAuthnTriggersPasskeyLogin\\noryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit\\noryPasskeyRegistration WebAuthnTriggersPasskeyRegistration\\noryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\"\n          },\n          \"pattern\": {\n            \"description\": \"The input's pattern.\",\n            \"type\": \"string\"\n          },\n          \"required\": {\n            \"description\": \"Mark this input field as required.\",\n            \"type\": \"boolean\"\n          },\n          \"type\": {\n            \"description\": \"The input's element type.\\ntext InputAttributeTypeText\\npassword InputAttributeTypePassword\\nnumber InputAttributeTypeNumber\\ncheckbox InputAttributeTypeCheckbox\\nhidden InputAttributeTypeHidden\\nemail InputAttributeTypeEmail\\ntel InputAttributeTypeTel\\nsubmit InputAttributeTypeSubmit\\nbutton InputAttributeTypeButton\\ndatetime-local InputAttributeTypeDateTimeLocal\\ndate InputAttributeTypeDate\\nurl InputAttributeTypeURI\",\n            \"enum\": [\n              \"text\",\n              \"password\",\n              \"number\",\n              \"checkbox\",\n              \"hidden\",\n              \"email\",\n              \"tel\",\n              \"submit\",\n              \"button\",\n              \"datetime-local\",\n              \"date\",\n              \"url\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"text InputAttributeTypeText\\npassword InputAttributeTypePassword\\nnumber InputAttributeTypeNumber\\ncheckbox InputAttributeTypeCheckbox\\nhidden InputAttributeTypeHidden\\nemail InputAttributeTypeEmail\\ntel InputAttributeTypeTel\\nsubmit InputAttributeTypeSubmit\\nbutton InputAttributeTypeButton\\ndatetime-local InputAttributeTypeDateTimeLocal\\ndate InputAttributeTypeDate\\nurl InputAttributeTypeURI\"\n          },\n          \"value\": {\n            \"description\": \"The input's value.\",\n            \"nullable\": true\n          }\n        },\n        \"required\": [\n          \"name\",\n          \"type\",\n          \"disabled\",\n          \"node_type\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiNodeMeta\": {\n        \"description\": \"This might include a label and other information that can optionally\\nbe used to render UIs.\",\n        \"properties\": {\n          \"label\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          }\n        },\n        \"title\": \"A Node's Meta Information\",\n        \"type\": \"object\"\n      },\n      \"uiNodeScriptAttributes\": {\n        \"properties\": {\n          \"async\": {\n            \"description\": \"The script async type\",\n            \"type\": \"boolean\"\n          },\n          \"crossorigin\": {\n            \"description\": \"The script cross origin policy\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"A unique identifier\",\n            \"type\": \"string\"\n          },\n          \"integrity\": {\n            \"description\": \"The script's integrity hash\",\n            \"type\": \"string\"\n          },\n          \"node_type\": {\n            \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \\\"script\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n            \"enum\": [\n              \"script\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n          },\n          \"nonce\": {\n            \"description\": \"Nonce for CSP\\n\\nA nonce you may want to use to improve your Content Security Policy.\\nYou do not have to use this value but if you want to improve your CSP\\npolicies you may use it. You can also choose to use your own nonce value!\",\n            \"type\": \"string\"\n          },\n          \"referrerpolicy\": {\n            \"description\": \"The script referrer policy\",\n            \"type\": \"string\"\n          },\n          \"src\": {\n            \"description\": \"The script source\",\n            \"type\": \"string\"\n          },\n          \"type\": {\n            \"description\": \"The script MIME type\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"src\",\n          \"async\",\n          \"referrerpolicy\",\n          \"crossorigin\",\n          \"integrity\",\n          \"type\",\n          \"id\",\n          \"nonce\",\n          \"node_type\"\n        ],\n        \"title\": \"ScriptAttributes represent script nodes which load javascript.\",\n        \"type\": \"object\"\n      },\n      \"uiNodeTextAttributes\": {\n        \"properties\": {\n          \"id\": {\n            \"description\": \"A unique identifier\",\n            \"type\": \"string\"\n          },\n          \"node_type\": {\n            \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"text\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n            \"enum\": [\n              \"text\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n          },\n          \"text\": {\n            \"$ref\": \"#/components/schemas/uiText\"\n          }\n        },\n        \"required\": [\n          \"text\",\n          \"id\",\n          \"node_type\"\n        ],\n        \"title\": \"TextAttributes represents the attributes of a text node.\",\n        \"type\": \"object\"\n      },\n      \"uiNodes\": {\n        \"items\": {\n          \"$ref\": \"#/components/schemas/uiNode\"\n        },\n        \"type\": \"array\"\n      },\n      \"uiText\": {\n        \"properties\": {\n          \"context\": {\n            \"description\": \"The message's context. Useful when customizing messages.\",\n            \"type\": \"object\"\n          },\n          \"id\": {\n            \"$ref\": \"#/components/schemas/ID\"\n          },\n          \"text\": {\n            \"description\": \"The message text. Written in american english.\",\n            \"type\": \"string\"\n          },\n          \"type\": {\n            \"description\": \"The message type.\\ninfo Info\\nerror Error\\nsuccess Success\",\n            \"enum\": [\n              \"info\",\n              \"error\",\n              \"success\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"info Info\\nerror Error\\nsuccess Success\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"text\",\n          \"type\"\n        ],\n        \"type\": \"object\"\n      },\n      \"uiTexts\": {\n        \"items\": {\n          \"$ref\": \"#/components/schemas/uiText\"\n        },\n        \"type\": \"array\"\n      },\n      \"unexpectedError\": {\n        \"type\": \"string\"\n      },\n      \"updateIdentityBody\": {\n        \"description\": \"Update Identity Body\",\n        \"properties\": {\n          \"credentials\": {\n            \"$ref\": \"#/components/schemas/identityWithCredentials\"\n          },\n          \"external_id\": {\n            \"description\": \"ExternalID is an optional external ID of the identity. This is used to link\\nthe identity to an external system. If set, the external ID must be unique\\nacross all identities.\",\n            \"type\": \"string\"\n          },\n          \"metadata_admin\": {\n            \"description\": \"Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/\\u003cid\\u003e`.\"\n          },\n          \"metadata_public\": {\n            \"description\": \"Store metadata about the identity which the identity itself can see when calling for example the\\nsession endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\"\n          },\n          \"schema_id\": {\n            \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. If set\\nwill update the Identity's SchemaID.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"description\": \"State is the identity's state.\\nactive StateActive\\ninactive StateInactive\",\n            \"enum\": [\n              \"active\",\n              \"inactive\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"active StateActive\\ninactive StateInactive\"\n          },\n          \"traits\": {\n            \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_id`.\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"schema_id\",\n          \"traits\",\n          \"state\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateLoginFlowBody\": {\n        \"discriminator\": {\n          \"mapping\": {\n            \"code\": \"#/components/schemas/updateLoginFlowWithCodeMethod\",\n            \"identifier_first\": \"#/components/schemas/updateLoginFlowWithIdentifierFirstMethod\",\n            \"lookup_secret\": \"#/components/schemas/updateLoginFlowWithLookupSecretMethod\",\n            \"oidc\": \"#/components/schemas/updateLoginFlowWithOidcMethod\",\n            \"passkey\": \"#/components/schemas/updateLoginFlowWithPasskeyMethod\",\n            \"password\": \"#/components/schemas/updateLoginFlowWithPasswordMethod\",\n            \"saml\": \"#/components/schemas/updateLoginFlowWithSamlMethod\",\n            \"totp\": \"#/components/schemas/updateLoginFlowWithTotpMethod\",\n            \"webauthn\": \"#/components/schemas/updateLoginFlowWithWebAuthnMethod\"\n          },\n          \"propertyName\": \"method\"\n        },\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/components/schemas/updateLoginFlowWithPasswordMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateLoginFlowWithOidcMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateLoginFlowWithSamlMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateLoginFlowWithTotpMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateLoginFlowWithWebAuthnMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateLoginFlowWithLookupSecretMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateLoginFlowWithCodeMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateLoginFlowWithPasskeyMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateLoginFlowWithIdentifierFirstMethod\"\n          }\n        ]\n      },\n      \"updateLoginFlowWithCodeMethod\": {\n        \"description\": \"Update Login flow using the code method\",\n        \"properties\": {\n          \"address\": {\n            \"description\": \"Address is the address to send the code to, in case that there are multiple addresses. This field\\nis only used in two-factor flows and is ineffective for passwordless flows.\",\n            \"type\": \"string\"\n          },\n          \"code\": {\n            \"description\": \"Code is the 6 digits code sent to the user\",\n            \"type\": \"string\"\n          },\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\",\n            \"type\": \"string\"\n          },\n          \"identifier\": {\n            \"description\": \"Identifier is the code identifier\\nThe identifier requires that the user has already completed the registration or settings with code flow.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method should be set to \\\"code\\\" when logging in using the code strategy.\",\n            \"type\": \"string\"\n          },\n          \"resend\": {\n            \"description\": \"Resend is set when the user wants to resend the code\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"csrf_token\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateLoginFlowWithIdentifierFirstMethod\": {\n        \"description\": \"Update Login Flow with Multi-Step Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"identifier\": {\n            \"description\": \"Identifier is the email or username of the user trying to log in.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method should be set to \\\"password\\\" when logging in using the identifier and password strategy.\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"identifier\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateLoginFlowWithLookupSecretMethod\": {\n        \"description\": \"Update Login Flow with Lookup Secret Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"lookup_secret\": {\n            \"description\": \"The lookup secret.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method should be set to \\\"lookup_secret\\\" when logging in using the lookup_secret strategy.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"lookup_secret\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateLoginFlowWithOidcMethod\": {\n        \"description\": \"Update Login Flow with OpenID Connect Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The CSRF Token\",\n            \"type\": \"string\"\n          },\n          \"id_token\": {\n            \"description\": \"IDToken is an optional id token provided by an OIDC provider\\n\\nIf submitted, it is verified using the OIDC provider's public key set and the claims are used to populate\\nthe OIDC credentials of the identity.\\nIf the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use\\nthe `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.\\n\\nSupported providers are\\nApple\\nGoogle\",\n            \"type\": \"string\"\n          },\n          \"id_token_nonce\": {\n            \"description\": \"IDTokenNonce is the nonce, used when generating the IDToken.\\nIf the provider supports nonce validation, the nonce will be validated against this value and required.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method to use\\n\\nThis field must be set to `oidc` when using the oidc method.\",\n            \"type\": \"string\"\n          },\n          \"provider\": {\n            \"description\": \"The provider to register with\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"The identity traits. This is a placeholder for the registration flow.\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          },\n          \"upstream_parameters\": {\n            \"description\": \"UpstreamParameters are the parameters that are passed to the upstream identity provider.\\n\\nThese parameters are optional and depend on what the upstream identity provider supports.\\nSupported parameters are:\\n`login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\\n`hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\\n`prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\\n`acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"provider\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateLoginFlowWithPasskeyMethod\": {\n        \"description\": \"Update Login Flow with Passkey Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method should be set to \\\"passkey\\\" when logging in using the Passkey strategy.\",\n            \"type\": \"string\"\n          },\n          \"passkey_login\": {\n            \"description\": \"Login a WebAuthn Security Key\\n\\nThis must contain the ID of the WebAuthN connection.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateLoginFlowWithPasswordMethod\": {\n        \"description\": \"Update Login Flow with Password Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"identifier\": {\n            \"description\": \"Identifier is the email or username of the user trying to log in.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method should be set to \\\"password\\\" when logging in using the identifier and password strategy.\",\n            \"type\": \"string\"\n          },\n          \"password\": {\n            \"description\": \"The user's password.\",\n            \"type\": \"string\"\n          },\n          \"password_identifier\": {\n            \"description\": \"Identifier is the email or username of the user trying to log in.\\nThis field is deprecated!\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"password\",\n          \"identifier\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateLoginFlowWithSamlMethod\": {\n        \"description\": \"Update login flow using SAML\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The CSRF Token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method to use\\n\\nThis field must be set to `saml` when using the saml method.\",\n            \"type\": \"string\"\n          },\n          \"provider\": {\n            \"description\": \"The provider to register with\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"provider\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateLoginFlowWithTotpMethod\": {\n        \"description\": \"Update Login Flow with TOTP Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method should be set to \\\"totp\\\" when logging in using the TOTP strategy.\",\n            \"type\": \"string\"\n          },\n          \"totp_code\": {\n            \"description\": \"The TOTP code.\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"method\",\n          \"totp_code\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateLoginFlowWithWebAuthnMethod\": {\n        \"description\": \"Update Login Flow with WebAuthn Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"identifier\": {\n            \"description\": \"Identifier is the email or username of the user trying to log in.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method should be set to \\\"webAuthn\\\" when logging in using the WebAuthn strategy.\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          },\n          \"webauthn_login\": {\n            \"description\": \"Login a WebAuthn Security Key\\n\\nThis must contain the ID of the WebAuthN connection.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"identifier\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateRecoveryFlowBody\": {\n        \"description\": \"Update Recovery Flow Request Body\",\n        \"discriminator\": {\n          \"mapping\": {\n            \"code\": \"#/components/schemas/updateRecoveryFlowWithCodeMethod\",\n            \"link\": \"#/components/schemas/updateRecoveryFlowWithLinkMethod\"\n          },\n          \"propertyName\": \"method\"\n        },\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/components/schemas/updateRecoveryFlowWithLinkMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateRecoveryFlowWithCodeMethod\"\n          }\n        ]\n      },\n      \"updateRecoveryFlowWithCodeMethod\": {\n        \"description\": \"Update Recovery Flow with Code Method\",\n        \"properties\": {\n          \"code\": {\n            \"description\": \"Code from the recovery email\\n\\nIf you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\",\n            \"type\": \"string\"\n          },\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"email\": {\n            \"description\": \"The email address of the account to recover\\n\\nIf the email belongs to a valid account, a recovery email will be sent.\\n\\nIf you want to notify the email address if the account does not exist, see\\nthe [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/account-recovery-password-reset#attempted-recovery-notifications)\\n\\nIf a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.\\n\\nformat: email\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method is the method that should be used for this recovery flow\\n\\nAllowed values are `link` and `code`.\\nlink RecoveryStrategyLink\\ncode RecoveryStrategyCode\",\n            \"enum\": [\n              \"link\",\n              \"code\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"link RecoveryStrategyLink\\ncode RecoveryStrategyCode\"\n          },\n          \"recovery_address\": {\n            \"description\": \"A recovery address that is registered for the user.\\nIt can be an email, a phone number (to receive the code via SMS), etc.\\nUsed in RecoveryV2.\",\n            \"type\": \"string\"\n          },\n          \"recovery_confirm_address\": {\n            \"description\": \"If there are multiple recovery addresses registered for the user, and the initially provided address\\nis different from the address chosen when the choice (of masked addresses) is presented, then we need to make sure\\nthat the user actually knows the full address to avoid information exfiltration, so we ask for the full address.\\nUsed in RecoveryV2.\",\n            \"type\": \"string\"\n          },\n          \"recovery_select_address\": {\n            \"description\": \"If there are multiple addresses registered for the user, a choice is presented and this field\\nstores the result of this choice.\\nAddresses are 'masked' (never sent in full to the client and shown partially in the UI) since at this point in the recovery flow,\\nthe user has not yet proven that it knows the full address and we want to avoid\\ninformation exfiltration.\\nSo for all intents and purposes, the value of this field should be treated as an opaque identifier.\\nUsed in RecoveryV2.\",\n            \"type\": \"string\"\n          },\n          \"screen\": {\n            \"description\": \"Set to \\\"previous\\\" to go back in the flow, meaningfully.\\nUsed in RecoveryV2.\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateRecoveryFlowWithLinkMethod\": {\n        \"description\": \"Update Recovery Flow with Link Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"email\": {\n            \"description\": \"Email to Recover\\n\\nNeeds to be set when initiating the flow. If the email is a registered\\nrecovery email, a recovery link will be sent. If the email is not known,\\nan email with details on what happened will be sent instead.\\n\\nformat: email\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method is the method that should be used for this recovery flow\\n\\nAllowed values are `link` and `code`\\nlink RecoveryStrategyLink\\ncode RecoveryStrategyCode\",\n            \"enum\": [\n              \"link\",\n              \"code\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"link RecoveryStrategyLink\\ncode RecoveryStrategyCode\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"email\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateRegistrationFlowBody\": {\n        \"description\": \"Update Registration Request Body\",\n        \"discriminator\": {\n          \"mapping\": {\n            \"code\": \"#/components/schemas/updateRegistrationFlowWithCodeMethod\",\n            \"oidc\": \"#/components/schemas/updateRegistrationFlowWithOidcMethod\",\n            \"passkey\": \"#/components/schemas/updateRegistrationFlowWithPasskeyMethod\",\n            \"password\": \"#/components/schemas/updateRegistrationFlowWithPasswordMethod\",\n            \"profile\": \"#/components/schemas/updateRegistrationFlowWithProfileMethod\",\n            \"saml\": \"#/components/schemas/updateRegistrationFlowWithSamlMethod\",\n            \"webauthn\": \"#/components/schemas/updateRegistrationFlowWithWebAuthnMethod\"\n          },\n          \"propertyName\": \"method\"\n        },\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/components/schemas/updateRegistrationFlowWithPasswordMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateRegistrationFlowWithOidcMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateRegistrationFlowWithSamlMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateRegistrationFlowWithWebAuthnMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateRegistrationFlowWithCodeMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateRegistrationFlowWithPasskeyMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateRegistrationFlowWithProfileMethod\"\n          }\n        ]\n      },\n      \"updateRegistrationFlowWithCodeMethod\": {\n        \"description\": \"Update Registration Flow with Code Method\",\n        \"properties\": {\n          \"code\": {\n            \"description\": \"The OTP Code sent to the user\",\n            \"type\": \"string\"\n          },\n          \"csrf_token\": {\n            \"description\": \"The CSRF Token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method to use\\n\\nThis field must be set to `code` when using the code method.\",\n            \"type\": \"string\"\n          },\n          \"resend\": {\n            \"description\": \"Resend restarts the flow with a new code\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"The identity's traits\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"traits\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateRegistrationFlowWithOidcMethod\": {\n        \"description\": \"Update Registration Flow with OpenID Connect Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The CSRF Token\",\n            \"type\": \"string\"\n          },\n          \"id_token\": {\n            \"description\": \"IDToken is an optional id token provided by an OIDC provider\\n\\nIf submitted, it is verified using the OIDC provider's public key set and the claims are used to populate\\nthe OIDC credentials of the identity.\\nIf the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use\\nthe `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.\\n\\nSupported providers are\\nApple\\nGoogle\",\n            \"type\": \"string\"\n          },\n          \"id_token_nonce\": {\n            \"description\": \"IDTokenNonce is the nonce, used when generating the IDToken.\\nIf the provider supports nonce validation, the nonce will be validated against this value and is required.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method to use\\n\\nThis field must be set to `oidc` when using the oidc method.\",\n            \"type\": \"string\"\n          },\n          \"provider\": {\n            \"description\": \"The provider to register with\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"The identity traits\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          },\n          \"upstream_parameters\": {\n            \"description\": \"UpstreamParameters are the parameters that are passed to the upstream identity provider.\\n\\nThese parameters are optional and depend on what the upstream identity provider supports.\\nSupported parameters are:\\n`login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\\n`hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\\n`prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\\n`acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"provider\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateRegistrationFlowWithPasskeyMethod\": {\n        \"description\": \"Update Registration Flow with Passkey Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to \\\"passkey\\\" when trying to add, update, or remove a Passkey.\",\n            \"type\": \"string\"\n          },\n          \"passkey_register\": {\n            \"description\": \"Register a WebAuthn Security Key\\n\\nIt is expected that the JSON returned by the WebAuthn registration process\\nis included here.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"The identity's traits\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"traits\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateRegistrationFlowWithPasswordMethod\": {\n        \"description\": \"Update Registration Flow with Password Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The CSRF Token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method to use\\n\\nThis field must be set to `password` when using the password method.\",\n            \"type\": \"string\"\n          },\n          \"password\": {\n            \"description\": \"Password to sign the user up with\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"The identity's traits\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"password\",\n          \"traits\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateRegistrationFlowWithProfileMethod\": {\n        \"description\": \"Update Registration Flow with Profile Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The Anti-CSRF Token\\n\\nThis token is only required when performing browser flows.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to profile when trying to update a profile.\",\n            \"type\": \"string\"\n          },\n          \"screen\": {\n            \"description\": \"Screen requests navigation to a previous screen.\\n\\nThis must be set to credential-selection to go back to the credential\\nselection screen.\\ncredential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential\\nprevious RegistrationScreenPrevious\",\n            \"enum\": [\n              \"credential-selection\",\n              \"previous\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"credential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential\\nprevious RegistrationScreenPrevious\"\n          },\n          \"traits\": {\n            \"description\": \"Traits\\n\\nThe identity's traits.\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"traits\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateRegistrationFlowWithSamlMethod\": {\n        \"description\": \"Update registration flow using SAML\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The CSRF Token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method to use\\n\\nThis field must be set to `saml` when using the saml method.\",\n            \"type\": \"string\"\n          },\n          \"provider\": {\n            \"description\": \"The provider to register with\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"The identity traits\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"provider\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateRegistrationFlowWithWebAuthnMethod\": {\n        \"description\": \"Update Registration Flow with WebAuthn Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to \\\"webauthn\\\" when trying to add, update, or remove a webAuthn pairing.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"The identity's traits\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          },\n          \"webauthn_register\": {\n            \"description\": \"Register a WebAuthn Security Key\\n\\nIt is expected that the JSON returned by the WebAuthn registration process\\nis included here.\",\n            \"type\": \"string\"\n          },\n          \"webauthn_register_displayname\": {\n            \"description\": \"Name of the WebAuthn Security Key to be Added\\n\\nA human-readable name for the security key which will be added.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"traits\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateSettingsFlowBody\": {\n        \"description\": \"Update Settings Flow Request Body\",\n        \"discriminator\": {\n          \"mapping\": {\n            \"lookup_secret\": \"#/components/schemas/updateSettingsFlowWithLookupMethod\",\n            \"oidc\": \"#/components/schemas/updateSettingsFlowWithOidcMethod\",\n            \"passkey\": \"#/components/schemas/updateSettingsFlowWithPasskeyMethod\",\n            \"password\": \"#/components/schemas/updateSettingsFlowWithPasswordMethod\",\n            \"profile\": \"#/components/schemas/updateSettingsFlowWithProfileMethod\",\n            \"saml\": \"#/components/schemas/updateSettingsFlowWithSamlMethod\",\n            \"totp\": \"#/components/schemas/updateSettingsFlowWithTotpMethod\",\n            \"webauthn\": \"#/components/schemas/updateSettingsFlowWithWebAuthnMethod\"\n          },\n          \"propertyName\": \"method\"\n        },\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/components/schemas/updateSettingsFlowWithPasswordMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateSettingsFlowWithProfileMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateSettingsFlowWithOidcMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateSettingsFlowWithSamlMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateSettingsFlowWithTotpMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateSettingsFlowWithWebAuthnMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateSettingsFlowWithLookupMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateSettingsFlowWithPasskeyMethod\"\n          }\n        ]\n      },\n      \"updateSettingsFlowWithLookupMethod\": {\n        \"description\": \"Update Settings Flow with Lookup Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\",\n            \"type\": \"string\"\n          },\n          \"lookup_secret_confirm\": {\n            \"description\": \"If set to true will save the regenerated lookup secrets\",\n            \"type\": \"boolean\"\n          },\n          \"lookup_secret_disable\": {\n            \"description\": \"Disables this method if true.\",\n            \"type\": \"boolean\"\n          },\n          \"lookup_secret_regenerate\": {\n            \"description\": \"If set to true will regenerate the lookup secrets\",\n            \"type\": \"boolean\"\n          },\n          \"lookup_secret_reveal\": {\n            \"description\": \"If set to true will reveal the lookup secrets\",\n            \"type\": \"boolean\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to \\\"lookup\\\" when trying to add, update, or remove a lookup pairing.\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateSettingsFlowWithOidcMethod\": {\n        \"description\": \"Update Settings Flow with OpenID Connect Method\",\n        \"properties\": {\n          \"flow\": {\n            \"description\": \"Flow ID is the flow's ID.\\n\\nin: query\",\n            \"type\": \"string\"\n          },\n          \"link\": {\n            \"description\": \"Link this provider\\n\\nEither this or `unlink` must be set.\\n\\ntype: string\\nin: body\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to profile when trying to update a profile.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"The identity's traits\\n\\nin: body\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          },\n          \"unlink\": {\n            \"description\": \"Unlink this provider\\n\\nEither this or `link` must be set.\\n\\ntype: string\\nin: body\",\n            \"type\": \"string\"\n          },\n          \"upstream_parameters\": {\n            \"description\": \"UpstreamParameters are the parameters that are passed to the upstream identity provider.\\n\\nThese parameters are optional and depend on what the upstream identity provider supports.\\nSupported parameters are:\\n`login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\\n`hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\\n`prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\\n`acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateSettingsFlowWithPasskeyMethod\": {\n        \"description\": \"Update Settings Flow with Passkey Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to \\\"passkey\\\" when trying to add, update, or remove a webAuthn pairing.\",\n            \"type\": \"string\"\n          },\n          \"passkey_remove\": {\n            \"description\": \"Remove a WebAuthn Security Key\\n\\nThis must contain the ID of the WebAuthN connection.\",\n            \"type\": \"string\"\n          },\n          \"passkey_settings_register\": {\n            \"description\": \"Register a WebAuthn Security Key\\n\\nIt is expected that the JSON returned by the WebAuthn registration process\\nis included here.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateSettingsFlowWithPasswordMethod\": {\n        \"description\": \"Update Settings Flow with Password Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to password when trying to update a password.\",\n            \"type\": \"string\"\n          },\n          \"password\": {\n            \"description\": \"Password is the updated password\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"password\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateSettingsFlowWithProfileMethod\": {\n        \"description\": \"Update Settings Flow with Profile Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The Anti-CSRF Token\\n\\nThis token is only required when performing browser flows.\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to profile when trying to update a profile.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"Traits\\n\\nThe identity's traits.\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"traits\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateSettingsFlowWithSamlMethod\": {\n        \"description\": \"Update settings flow using SAML\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"The CSRF Token\",\n            \"type\": \"string\"\n          },\n          \"flow\": {\n            \"description\": \"Flow ID is the flow's ID.\\n\\nin: query\",\n            \"type\": \"string\"\n          },\n          \"link\": {\n            \"description\": \"Link this provider\\n\\nEither this or `unlink` must be set.\\n\\ntype: string\\nin: body\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to saml when trying to update a profile.\",\n            \"type\": \"string\"\n          },\n          \"traits\": {\n            \"description\": \"The identity's traits\\n\\nin: body\",\n            \"type\": \"object\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          },\n          \"unlink\": {\n            \"description\": \"Unlink this provider\\n\\nEither this or `link` must be set.\\n\\ntype: string\\nin: body\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateSettingsFlowWithTotpMethod\": {\n        \"description\": \"Update Settings Flow with TOTP Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to \\\"totp\\\" when trying to add, update, or remove a totp pairing.\",\n            \"type\": \"string\"\n          },\n          \"totp_code\": {\n            \"description\": \"ValidationTOTP must contain a valid TOTP based on the\",\n            \"type\": \"string\"\n          },\n          \"totp_unlink\": {\n            \"description\": \"UnlinkTOTP if true will remove the TOTP pairing,\\neffectively removing the credential. This can be used\\nto set up a new TOTP device.\",\n            \"type\": \"boolean\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateSettingsFlowWithWebAuthnMethod\": {\n        \"description\": \"Update Settings Flow with WebAuthn Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"CSRFToken is the anti-CSRF token\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method\\n\\nShould be set to \\\"webauthn\\\" when trying to add, update, or remove a webAuthn pairing.\",\n            \"type\": \"string\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          },\n          \"webauthn_register\": {\n            \"description\": \"Register a WebAuthn Security Key\\n\\nIt is expected that the JSON returned by the WebAuthn registration process\\nis included here.\",\n            \"type\": \"string\"\n          },\n          \"webauthn_register_displayname\": {\n            \"description\": \"Name of the WebAuthn Security Key to be Added\\n\\nA human-readable name for the security key which will be added.\",\n            \"type\": \"string\"\n          },\n          \"webauthn_remove\": {\n            \"description\": \"Remove a WebAuthn Security Key\\n\\nThis must contain the ID of the WebAuthN connection.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateVerificationFlowBody\": {\n        \"description\": \"Update Verification Flow Request Body\",\n        \"discriminator\": {\n          \"mapping\": {\n            \"code\": \"#/components/schemas/updateVerificationFlowWithCodeMethod\",\n            \"link\": \"#/components/schemas/updateVerificationFlowWithLinkMethod\"\n          },\n          \"propertyName\": \"method\"\n        },\n        \"oneOf\": [\n          {\n            \"$ref\": \"#/components/schemas/updateVerificationFlowWithLinkMethod\"\n          },\n          {\n            \"$ref\": \"#/components/schemas/updateVerificationFlowWithCodeMethod\"\n          }\n        ]\n      },\n      \"updateVerificationFlowWithCodeMethod\": {\n        \"properties\": {\n          \"code\": {\n            \"description\": \"Code from the recovery email\\n\\nIf you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\",\n            \"type\": \"string\"\n          },\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"email\": {\n            \"description\": \"The email address to verify\\n\\nIf the email belongs to a valid account, a verifiation email will be sent.\\n\\nIf you want to notify the email address if the account does not exist, see\\nthe [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation#attempted-verification-notifications)\\n\\nIf a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.\\n\\nformat: email\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method is the method that should be used for this verification flow\\n\\nAllowed values are `link` and `code`.\\nlink VerificationStrategyLink\\ncode VerificationStrategyCode\",\n            \"enum\": [\n              \"link\",\n              \"code\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"link VerificationStrategyLink\\ncode VerificationStrategyCode\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"updateVerificationFlowWithLinkMethod\": {\n        \"description\": \"Update Verification Flow with Link Method\",\n        \"properties\": {\n          \"csrf_token\": {\n            \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n            \"type\": \"string\"\n          },\n          \"email\": {\n            \"description\": \"Email to Verify\\n\\nNeeds to be set when initiating the flow. If the email is a registered\\nverification email, a verification link will be sent. If the email is not known,\\na email with details on what happened will be sent instead.\\n\\nformat: email\",\n            \"type\": \"string\"\n          },\n          \"method\": {\n            \"description\": \"Method is the method that should be used for this verification flow\\n\\nAllowed values are `link` and `code`\\nlink VerificationStrategyLink\\ncode VerificationStrategyCode\",\n            \"enum\": [\n              \"link\",\n              \"code\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"link VerificationStrategyLink\\ncode VerificationStrategyCode\"\n          },\n          \"transient_payload\": {\n            \"description\": \"Transient data to pass along to any webhooks\",\n            \"type\": \"object\"\n          }\n        },\n        \"required\": [\n          \"email\",\n          \"method\"\n        ],\n        \"type\": \"object\"\n      },\n      \"verifiableIdentityAddress\": {\n        \"description\": \"VerifiableAddress is an identity's verifiable address\",\n        \"properties\": {\n          \"created_at\": {\n            \"description\": \"When this entry was created\",\n            \"example\": \"2014-01-01T23:28:56.782Z\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"The ID\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"status\": {\n            \"$ref\": \"#/components/schemas/identityVerifiableAddressStatus\"\n          },\n          \"updated_at\": {\n            \"description\": \"When this entry was last updated\",\n            \"example\": \"2014-01-01T23:28:56.782Z\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"value\": {\n            \"description\": \"The address value\\n\\nexample foo@user.com\",\n            \"type\": \"string\"\n          },\n          \"verified\": {\n            \"description\": \"Indicates if the address has already been verified\",\n            \"example\": true,\n            \"type\": \"boolean\"\n          },\n          \"verified_at\": {\n            \"$ref\": \"#/components/schemas/nullTime\"\n          },\n          \"via\": {\n            \"description\": \"The delivery method\",\n            \"enum\": [\n              \"email\",\n              \"sms\"\n            ],\n            \"example\": \"email\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"value\",\n          \"verified\",\n          \"via\",\n          \"status\"\n        ],\n        \"type\": \"object\"\n      },\n      \"verificationFlow\": {\n        \"description\": \"Used to verify an out-of-band communication\\nchannel such as an email address or a phone number.\\n\\nFor more information head over to: https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation\",\n        \"properties\": {\n          \"active\": {\n            \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n            \"type\": \"string\"\n          },\n          \"expires_at\": {\n            \"description\": \"ExpiresAt is the time (UTC) when the request expires. If the user still wishes to verify the address,\\na new request has to be initiated.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"id\": {\n            \"description\": \"ID represents the request's unique ID. When performing the verification flow, this\\nrepresents the id in the verify ui's query parameter: http://\\u003cselfservice.flows.verification.ui_url\\u003e?request=\\u003cid\\u003e\\n\\ntype: string\\nformat: uuid\",\n            \"format\": \"uuid\",\n            \"type\": \"string\"\n          },\n          \"issued_at\": {\n            \"description\": \"IssuedAt is the time (UTC) when the request occurred.\",\n            \"format\": \"date-time\",\n            \"type\": \"string\"\n          },\n          \"request_url\": {\n            \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n            \"type\": \"string\"\n          },\n          \"return_to\": {\n            \"description\": \"ReturnTo contains the requested return_to URL.\",\n            \"type\": \"string\"\n          },\n          \"state\": {\n            \"description\": \"State represents the state of this request:\\n\\nchoose_method: ask the user to choose a method (e.g. verify your email)\\nsent_email: the email has been sent to the user\\npassed_challenge: the request was successful and the verification challenge was passed.\"\n          },\n          \"transient_payload\": {\n            \"description\": \"TransientPayload is used to pass data from the verification flow to hooks and email templates\",\n            \"type\": \"object\"\n          },\n          \"type\": {\n            \"$ref\": \"#/components/schemas/selfServiceFlowType\"\n          },\n          \"ui\": {\n            \"$ref\": \"#/components/schemas/uiContainer\"\n          }\n        },\n        \"required\": [\n          \"id\",\n          \"type\",\n          \"ui\",\n          \"state\"\n        ],\n        \"title\": \"A Verification Flow\",\n        \"type\": \"object\"\n      },\n      \"verificationFlowState\": {\n        \"description\": \"The experimental state represents the state of a verification flow. This field is EXPERIMENTAL and subject to change!\",\n        \"enum\": [\n          \"choose_method\",\n          \"sent_email\",\n          \"passed_challenge\"\n        ],\n        \"title\": \"Verification flow state (experimental)\"\n      },\n      \"version\": {\n        \"properties\": {\n          \"version\": {\n            \"description\": \"Version is the service's version.\",\n            \"type\": \"string\"\n          }\n        },\n        \"type\": \"object\"\n      },\n      \"webAuthnJavaScript\": {\n        \"type\": \"string\"\n      }\n    },\n    \"securitySchemes\": {\n      \"oryAccessToken\": {\n        \"in\": \"header\",\n        \"name\": \"Authorization\",\n        \"type\": \"apiKey\"\n      }\n    }\n  },\n  \"info\": {\n    \"contact\": {\n      \"email\": \"office@ory.sh\"\n    },\n    \"description\": \"This is the API specification for Ory Identities with features such as registration, login, recovery, account verification, profile settings, password reset, identity management, session management, email and sms delivery, and more.\\n\",\n    \"license\": {\n      \"name\": \"Apache 2.0\"\n    },\n    \"title\": \"Ory Identities API\",\n    \"version\": \"\"\n  },\n  \"openapi\": \"3.0.3\",\n  \"paths\": {\n    \"/.well-known/ory/webauthn.js\": {\n      \"get\": {\n        \"description\": \"This endpoint provides JavaScript which is needed in order to perform WebAuthn login and registration.\\n\\nIf you are building a JavaScript Browser App (e.g. in ReactJS or AngularJS) you will need to load this file:\\n\\n```html\\n\\u003cscript src=\\\"https://public-kratos.example.org/.well-known/ory/webauthn.js\\\" type=\\\"script\\\" async /\\u003e\\n```\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"operationId\": \"getWebAuthnJavaScript\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/webAuthnJavaScript\"\n                }\n              }\n            },\n            \"description\": \"webAuthnJavaScript\"\n          }\n        },\n        \"summary\": \"Get WebAuthn JavaScript\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"hydra-public-low\"\n      }\n    },\n    \"/admin/courier/messages\": {\n      \"get\": {\n        \"description\": \"Lists all messages by given status and recipient.\",\n        \"operationId\": \"listCourierMessages\",\n        \"parameters\": [\n          {\n            \"description\": \"Items per Page\\n\\nThis is the number of items per page to return.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_size\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 1000,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Next Page Token\\n\\nThe next page token.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Status filters out messages based on status.\\nIf no value is provided, it doesn't take effect on filter.\",\n            \"in\": \"query\",\n            \"name\": \"status\",\n            \"schema\": {\n              \"$ref\": \"#/components/schemas/courierMessageStatus\"\n            }\n          },\n          {\n            \"description\": \"Recipient filters out messages based on recipient.\\nIf no value is provided, it doesn't take effect on filter.\",\n            \"in\": \"query\",\n            \"name\": \"recipient\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/listCourierMessages\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"List Messages\",\n        \"tags\": [\n          \"courier\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/courier/messages/{id}\": {\n      \"get\": {\n        \"description\": \"Gets a specific messages by the given ID.\",\n        \"operationId\": \"getCourierMessage\",\n        \"parameters\": [\n          {\n            \"description\": \"MessageID is the ID of the message.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/message\"\n                }\n              }\n            },\n            \"description\": \"message\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Get a Message\",\n        \"tags\": [\n          \"courier\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/admin/identities\": {\n      \"get\": {\n        \"description\": \"Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.\",\n        \"operationId\": \"listIdentities\",\n        \"parameters\": [\n          {\n            \"description\": \"Deprecated Items per Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis is the number of items per page.\",\n            \"in\": \"query\",\n            \"name\": \"per_page\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 1000,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Deprecated Pagination Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis value is currently an integer, but it is not sequential. The value is not the page number, but a\\nreference. The next page can be any number and some numbers might return an empty list.\\n\\nFor example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist.\\nThe first page can be retrieved by omitting this parameter. Following page pointers will be returned in the\\n`Link` header.\",\n            \"in\": \"query\",\n            \"name\": \"page\",\n            \"schema\": {\n              \"format\": \"int64\",\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Page Size\\n\\nThis is the number of items per page to return. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_size\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 500,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Next Page Token\\n\\nThe next page token. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Read Consistency Level (preview)\\n\\nThe read consistency level determines the consistency guarantee for reads:\\n\\nstrong (slow): The read is guaranteed to return the most recent data committed at the start of the read.\\neventual (very fast): The result will return data that is about 4.8 seconds old.\\n\\nThe default consistency guarantee can be changed in the Ory Network Console or using the Ory CLI with\\n`ory patch project --replace '/previews/default_read_consistency_level=\\\"strong\\\"'`.\\n\\nSetting the default consistency level to `eventual` may cause regressions in the future as we add consistency\\ncontrols to more APIs. Currently, the following APIs will be affected by this setting:\\n\\n`GET /admin/identities`\\n\\nThis feature is in preview and only available in Ory Network.\\n ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level.\\nstrong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level.\\neventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\",\n            \"in\": \"query\",\n            \"name\": \"consistency\",\n            \"schema\": {\n              \"enum\": [\n                \"\",\n                \"strong\",\n                \"eventual\"\n              ],\n              \"type\": \"string\"\n            },\n            \"x-go-enum-desc\": \" ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level.\\nstrong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level.\\neventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\"\n          },\n          {\n            \"description\": \"Retrieve multiple identities by their IDs.\\n\\nThis parameter has the following limitations:\\n\\nDuplicate or non-existent IDs are ignored.\\nThe order of returned IDs may be different from the request.\\nThis filter does not support pagination. You must implement your own pagination as the maximum number of items returned by this endpoint may not exceed a certain threshold (currently 500).\",\n            \"in\": \"query\",\n            \"name\": \"ids\",\n            \"schema\": {\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"type\": \"array\"\n            }\n          },\n          {\n            \"description\": \"CredentialsIdentifier is the identifier (username, email) of the credentials to look up using exact match.\\nOnly one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\",\n            \"in\": \"query\",\n            \"name\": \"credentials_identifier\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"This is an EXPERIMENTAL parameter that WILL CHANGE. Do NOT rely on consistent, deterministic behavior.\\nTHIS PARAMETER WILL BE REMOVED IN AN UPCOMING RELEASE WITHOUT ANY MIGRATION PATH.\\n\\nCredentialsIdentifierSimilar is the (partial) identifier (username, email) of the credentials to look up using similarity search.\\nOnly one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\",\n            \"in\": \"query\",\n            \"name\": \"preview_credentials_identifier_similar\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Include Credentials in Response\\n\\nInclude any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\\nthe initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\",\n            \"in\": \"query\",\n            \"name\": \"include_credential\",\n            \"schema\": {\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"type\": \"array\"\n            }\n          },\n          {\n            \"description\": \"List identities that belong to a specific organization.\",\n            \"in\": \"query\",\n            \"name\": \"organization_id\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/listIdentities\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"List Identities\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      },\n      \"patch\": {\n        \"description\": \"Creates multiple [identities](https://www.ory.com/docs/kratos/concepts/identity-user-model).\\n\\nYou can also use this endpoint to [import credentials](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities),\\nincluding passwords, social sign-in settings, and multi-factor authentication methods.\\n\\nIf the patch includes hashed passwords you can import up to 1,000 identities per request.\\n\\nIf the patch includes at least one plaintext password you can import up to 200 identities per request.\\n\\nAvoid importing large batches with plaintext passwords. They can cause timeouts as the passwords need to be hashed before they are stored.\\n\\nIf at least one identity is imported successfully, the response status is 200 OK.\\nIf all imports fail, the response is one of the following 4xx errors:\\n400 Bad Request: The request payload is invalid or improperly formatted.\\n409 Conflict: Duplicate identities or conflicting data were detected.\\n\\nIf you get a 504 Gateway Timeout:\\nReduce the batch size\\nAvoid duplicate identities\\nPre-hash passwords with BCrypt\\n\\nIf the issue persists, contact support.\",\n        \"operationId\": \"batchPatchIdentities\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/patchIdentitiesBody\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/batchPatchIdentitiesResponse\"\n                }\n              }\n            },\n            \"description\": \"batchPatchIdentitiesResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"409\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Create multiple identities\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      },\n      \"post\": {\n        \"description\": \"Create an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model).  This endpoint can also be used to\\n[import credentials](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities)\\nfor instance passwords, social sign in configurations or multifactor methods.\",\n        \"operationId\": \"createIdentity\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/createIdentityBody\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"201\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/identity\"\n                }\n              }\n            },\n            \"description\": \"identity\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"409\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Create an Identity\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/identities/by/external/{externalID}\": {\n      \"get\": {\n        \"description\": \"Return an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its external ID. You can optionally\\ninclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\",\n        \"operationId\": \"getIdentityByExternalID\",\n        \"parameters\": [\n          {\n            \"description\": \"ExternalID must be set to the ID of identity you want to get\",\n            \"in\": \"path\",\n            \"name\": \"externalID\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Include Credentials in Response\\n\\nInclude any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\\nthe initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\",\n            \"in\": \"query\",\n            \"name\": \"include_credential\",\n            \"schema\": {\n              \"items\": {\n                \"enum\": [\n                  \"password\",\n                  \"oidc\",\n                  \"totp\",\n                  \"lookup_secret\",\n                  \"webauthn\",\n                  \"code\",\n                  \"passkey\",\n                  \"profile\",\n                  \"saml\",\n                  \"link_recovery\",\n                  \"code_recovery\"\n                ],\n                \"type\": \"string\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/identity\"\n                }\n              }\n            },\n            \"description\": \"identity\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Get an Identity by its External ID\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/admin/identities/{id}\": {\n      \"delete\": {\n        \"description\": \"Calling this endpoint irrecoverably and permanently deletes the [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) given its ID. This action can not be undone.\\nThis endpoint returns 204 when the identity was deleted or 404 if the identity was not found.\",\n        \"operationId\": \"deleteIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the identity's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Delete an Identity\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      },\n      \"get\": {\n        \"description\": \"Return an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its ID. You can optionally\\ninclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\",\n        \"operationId\": \"getIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of identity you want to get\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Include Credentials in Response\\n\\nInclude any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\\nthe initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\",\n            \"in\": \"query\",\n            \"name\": \"include_credential\",\n            \"schema\": {\n              \"items\": {\n                \"enum\": [\n                  \"password\",\n                  \"oidc\",\n                  \"totp\",\n                  \"lookup_secret\",\n                  \"webauthn\",\n                  \"code\",\n                  \"passkey\",\n                  \"profile\",\n                  \"saml\",\n                  \"link_recovery\",\n                  \"code_recovery\"\n                ],\n                \"type\": \"string\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/identity\"\n                }\n              }\n            },\n            \"description\": \"identity\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Get an Identity\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-low\"\n      },\n      \"patch\": {\n        \"description\": \"Partially updates an [identity's](https://www.ory.sh/docs/kratos/concepts/identity-user-model) field using [JSON Patch](https://jsonpatch.com/).\\nThe fields `id`, `stateChangedAt` and `credentials` can not be updated using this method.\",\n        \"operationId\": \"patchIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of identity you want to update\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/jsonPatchDocument\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/identity\"\n                }\n              }\n            },\n            \"description\": \"identity\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"409\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Patch an Identity\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      },\n      \"put\": {\n        \"description\": \"This endpoint updates an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model). The full identity\\npayload, except credentials, is expected. For partial updates, use the [patchIdentity](https://www.ory.sh/docs/reference/api#tag/identity/operation/patchIdentity) operation.\\n\\nA credential can be provided via the `credentials` field in the request body.\\nIf provided, the credentials will be imported and added to the existing credentials of the identity.\",\n        \"operationId\": \"updateIdentity\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of identity you want to update\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateIdentityBody\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/identity\"\n                }\n              }\n            },\n            \"description\": \"identity\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"409\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Update an Identity\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/identities/{id}/credentials/{type}\": {\n      \"delete\": {\n        \"description\": \"Delete an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) credential by its type.\\nYou cannot delete passkeys or code auth credentials through this API.\",\n        \"operationId\": \"deleteIdentityCredentials\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the identity's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Type is the type of credentials to delete.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n            \"in\": \"path\",\n            \"name\": \"type\",\n            \"required\": true,\n            \"schema\": {\n              \"enum\": [\n                \"password\",\n                \"oidc\",\n                \"totp\",\n                \"lookup_secret\",\n                \"webauthn\",\n                \"code\",\n                \"passkey\",\n                \"profile\",\n                \"saml\",\n                \"link_recovery\",\n                \"code_recovery\"\n              ],\n              \"type\": \"string\"\n            },\n            \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\"\n          },\n          {\n            \"description\": \"Identifier is the identifier of the OIDC/SAML credential to delete.\\nFind the identifier by calling the `GET /admin/identities/{id}?include_credential={oidc,saml}` endpoint.\",\n            \"in\": \"query\",\n            \"name\": \"identifier\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Delete a credential for a specific identity\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/identities/{id}/sessions\": {\n      \"delete\": {\n        \"description\": \"Calling this endpoint irrecoverably and permanently deletes and invalidates all sessions that belong to the given Identity.\",\n        \"operationId\": \"deleteIdentitySessions\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the identity's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Delete \\u0026 Invalidate an Identity's Sessions\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      },\n      \"get\": {\n        \"description\": \"This endpoint returns all sessions that belong to the given Identity.\",\n        \"operationId\": \"listIdentitySessions\",\n        \"parameters\": [\n          {\n            \"description\": \"Deprecated Items per Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis is the number of items per page.\",\n            \"in\": \"query\",\n            \"name\": \"per_page\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 1000,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Deprecated Pagination Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis value is currently an integer, but it is not sequential. The value is not the page number, but a\\nreference. The next page can be any number and some numbers might return an empty list.\\n\\nFor example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist.\\nThe first page can be retrieved by omitting this parameter. Following page pointers will be returned in the\\n`Link` header.\",\n            \"in\": \"query\",\n            \"name\": \"page\",\n            \"schema\": {\n              \"format\": \"int64\",\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Page Size\\n\\nThis is the number of items per page to return. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_size\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 500,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Next Page Token\\n\\nThe next page token. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"ID is the identity's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\",\n            \"in\": \"query\",\n            \"name\": \"active\",\n            \"schema\": {\n              \"type\": \"boolean\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/listIdentitySessions\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"List an Identity's Sessions\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/admin/recovery/code\": {\n      \"post\": {\n        \"description\": \"This endpoint creates a recovery code which should be given to the user in order for them to recover\\n(or activate) their account.\",\n        \"operationId\": \"createRecoveryCodeForIdentity\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/createRecoveryCodeForIdentityBody\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"201\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryCodeForIdentity\"\n                }\n              }\n            },\n            \"description\": \"recoveryCodeForIdentity\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Create a Recovery Code\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/recovery/link\": {\n      \"post\": {\n        \"description\": \"This endpoint creates a recovery link which should be given to the user in order for them to recover\\n(or activate) their account.\",\n        \"operationId\": \"createRecoveryLinkForIdentity\",\n        \"parameters\": [\n          {\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/createRecoveryLinkForIdentityBody\"\n              }\n            }\n          },\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryLinkForIdentity\"\n                }\n              }\n            },\n            \"description\": \"recoveryLinkForIdentity\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Create a Recovery Link\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/sessions\": {\n      \"get\": {\n        \"description\": \"Listing all sessions that exist.\",\n        \"operationId\": \"listSessions\",\n        \"parameters\": [\n          {\n            \"description\": \"Items per Page\\n\\nThis is the number of items per page to return.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_size\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 1000,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Next Page Token\\n\\nThe next page token.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\",\n            \"in\": \"query\",\n            \"name\": \"active\",\n            \"schema\": {\n              \"type\": \"boolean\"\n            }\n          },\n          {\n            \"description\": \"ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session.\\nIf no value is provided, the expandable properties are skipped.\",\n            \"in\": \"query\",\n            \"name\": \"expand\",\n            \"schema\": {\n              \"items\": {\n                \"enum\": [\n                  \"identity\",\n                  \"devices\"\n                ],\n                \"type\": \"string\"\n              },\n              \"type\": \"array\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/listSessions\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"List All Sessions\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/admin/sessions/{id}\": {\n      \"delete\": {\n        \"description\": \"Calling this endpoint deactivates the specified session. Session data is not deleted.\",\n        \"operationId\": \"disableSession\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the session's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Deactivate a Session\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      },\n      \"get\": {\n        \"description\": \"This endpoint is useful for:\\n\\nGetting a session object with all specified expandables that exist in an administrative context.\",\n        \"operationId\": \"getSession\",\n        \"parameters\": [\n          {\n            \"description\": \"ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session.\\nExample - ?expand=Identity\\u0026expand=Devices\\nIf no value is provided, the expandable properties are skipped.\",\n            \"in\": \"query\",\n            \"name\": \"expand\",\n            \"schema\": {\n              \"items\": {\n                \"enum\": [\n                  \"identity\",\n                  \"devices\"\n                ],\n                \"type\": \"string\"\n              },\n              \"type\": \"array\"\n            }\n          },\n          {\n            \"description\": \"ID is the session's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/session\"\n                }\n              }\n            },\n            \"description\": \"session\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Get Session\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-low\"\n      }\n    },\n    \"/admin/sessions/{id}/extend\": {\n      \"patch\": {\n        \"description\": \"Calling this endpoint extends the given session ID. If `session.earliest_possible_extend` is set it\\nwill only extend the session after the specified time has passed.\\n\\nThis endpoint returns per default a 204 No Content response on success. Older Ory Network projects may\\nreturn a 200 OK response with the session in the body. Returning the session as part of the response\\nwill be deprecated in the future and should not be relied upon.\\n\\nThis endpoint ignores consecutive requests to extend the same session and returns a 404 error in those\\nscenarios. This endpoint also returns 404 errors if the session does not exist.\\n\\nRetrieve the session ID from the `/sessions/whoami` endpoint / `toSession` SDK method.\",\n        \"operationId\": \"extendSession\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the session's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/session\"\n                }\n              }\n            },\n            \"description\": \"session\"\n          },\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"summary\": \"Extend a Session\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/health/alive\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a HTTP 200 status code when Ory Kratos is accepting incoming\\nHTTP requests. This status does currently not include checks whether the database connection is working.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of this service, the health status will never\\nrefer to the cluster state, only to a single instance.\",\n        \"operationId\": \"isAlive\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"status\": {\n                      \"description\": \"Always \\\"ok\\\".\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"required\": [\n                    \"status\"\n                  ],\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Ory Kratos is ready to accept connections.\"\n          },\n          \"default\": {\n            \"content\": {\n              \"text/plain\": {\n                \"schema\": {\n                  \"type\": \"string\"\n                }\n              }\n            },\n            \"description\": \"Unexpected error\"\n          }\n        },\n        \"summary\": \"Check HTTP Server Status\",\n        \"tags\": [\n          \"metadata\"\n        ]\n      }\n    },\n    \"/health/ready\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a HTTP 200 status code when Ory Kratos is up running and the environment dependencies (e.g.\\nthe database) are responsive as well.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of Ory Kratos, the health status will never\\nrefer to the cluster state, only to a single instance.\",\n        \"operationId\": \"isReady\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"status\": {\n                      \"description\": \"Always \\\"ok\\\".\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"required\": [\n                    \"status\"\n                  ],\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Ory Kratos is ready to accept requests.\"\n          },\n          \"503\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"errors\": {\n                      \"additionalProperties\": {\n                        \"type\": \"string\"\n                      },\n                      \"description\": \"Errors contains a list of errors that caused the not ready status.\",\n                      \"type\": \"object\"\n                    }\n                  },\n                  \"required\": [\n                    \"errors\"\n                  ],\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Ory Kratos is not yet ready to accept requests.\"\n          },\n          \"default\": {\n            \"content\": {\n              \"text/plain\": {\n                \"schema\": {\n                  \"type\": \"string\"\n                }\n              }\n            },\n            \"description\": \"Unexpected error\"\n          }\n        },\n        \"summary\": \"Check HTTP Server and Database Status\",\n        \"tags\": [\n          \"metadata\"\n        ]\n      }\n    },\n    \"/schemas\": {\n      \"get\": {\n        \"description\": \"Returns a list of all identity schemas currently in use.\",\n        \"operationId\": \"listIdentitySchemas\",\n        \"parameters\": [\n          {\n            \"description\": \"Deprecated Items per Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis is the number of items per page.\",\n            \"in\": \"query\",\n            \"name\": \"per_page\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 1000,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Deprecated Pagination Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis value is currently an integer, but it is not sequential. The value is not the page number, but a\\nreference. The next page can be any number and some numbers might return an empty list.\\n\\nFor example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist.\\nThe first page can be retrieved by omitting this parameter. Following page pointers will be returned in the\\n`Link` header.\",\n            \"in\": \"query\",\n            \"name\": \"page\",\n            \"schema\": {\n              \"format\": \"int64\",\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Page Size\\n\\nThis is the number of items per page to return. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_size\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 500,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Next Page Token\\n\\nThe next page token. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/identitySchemas\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get all Identity Schemas\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/schemas/{id}\": {\n      \"get\": {\n        \"description\": \"Return a specific identity schema.\",\n        \"operationId\": \"getIdentitySchema\",\n        \"parameters\": [\n          {\n            \"description\": \"ID must be set to the ID of schema you want to get\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/identitySchema\"\n                }\n              }\n            },\n            \"description\": \"identitySchema\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get Identity JSON Schema\",\n        \"tags\": [\n          \"identity\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/self-service/errors\": {\n      \"get\": {\n        \"description\": \"This endpoint returns the error associated with a user-facing self service errors.\\n\\nThis endpoint supports stub values to help you implement the error UI:\\n\\n`?id=stub:500` - returns a stub 500 (Internal Server Error) error.\\n\\nMore information can be found at [Ory Kratos User User Facing Error Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors).\",\n        \"operationId\": \"getFlowError\",\n        \"parameters\": [\n          {\n            \"description\": \"Error is the error's ID\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/flowError\"\n                }\n              }\n            },\n            \"description\": \"flowError\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get User-Flow Errors\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/fed-cm/parameters\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a list of all available FedCM providers. It is only supported on the Ory Network.\",\n        \"operationId\": \"createFedcmFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/createFedcmFlowResponse\"\n                }\n              }\n            },\n            \"description\": \"createFedcmFlowResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get FedCM Parameters\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/fed-cm/token\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to submit a token from a FedCM provider through\\n`navigator.credentials.get` and log the user in. The parameters from\\n`navigator.credentials.get` must have come from `GET\\nself-service/fed-cm/parameters`.\",\n        \"operationId\": \"updateFedcmFlow\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/UpdateFedcmFlowBody\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/UpdateFedcmFlowBody\"\n              }\n            }\n          },\n          \"required\": true,\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/successfulNativeLogin\"\n                }\n              }\n            },\n            \"description\": \"successfulNativeLogin\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"422\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorBrowserLocationChangeRequired\"\n                }\n              }\n            },\n            \"description\": \"errorBrowserLocationChangeRequired\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Submit a FedCM token\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/login\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a login flow. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI flows expect `application/json` to be sent in the body and responds with\\nHTTP 200 and a application/json body with the session token on success;\\nHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\\nHTTP 400 on form validation errors.\\n\\nBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\\na HTTP 303 redirect to the post/after login URL or the `return_to` value if it was set and if the login succeeded;\\na HTTP 303 redirect to the login UI URL with the flow ID containing the validation errors otherwise.\\n\\nBrowser flows with an accept header of `application/json` will not redirect but instead respond with\\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\n\\nIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\\nMost likely used in Social Sign In flows.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"operationId\": \"updateLoginFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Login Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/login?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The Session Token of the Identity performing the settings flow.\",\n            \"in\": \"header\",\n            \"name\": \"X-Session-Token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateLoginFlowBody\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateLoginFlowBody\"\n              }\n            }\n          },\n          \"required\": true,\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/successfulNativeLogin\"\n                }\n              }\n            },\n            \"description\": \"successfulNativeLogin\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"422\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorBrowserLocationChangeRequired\"\n                }\n              }\n            },\n            \"description\": \"errorBrowserLocationChangeRequired\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Submit a Login Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/login/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a login flow for native apps that do not use a browser, such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\\nwill be returned unless the URL query parameter `?refresh=true` is set.\\n\\nTo fetch an existing login flow call `/self-service/login/flows?flow=\\u003cflow_id\\u003e`.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks, including CSRF login attacks.\\n\\nIn the case of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"operationId\": \"createNativeLoginFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"Refresh a login session\\n\\nIf set to true, this will refresh an existing login session by\\nasking the user to sign in again. This will reset the\\nauthenticated_at time of the session.\",\n            \"in\": \"query\",\n            \"name\": \"refresh\",\n            \"schema\": {\n              \"type\": \"boolean\"\n            }\n          },\n          {\n            \"description\": \"Request a Specific AuthenticationMethod Assurance Level\\n\\nUse this parameter to upgrade an existing session's authenticator assurance level (AAL). This\\nallows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password,\\nthe AAL is 1. If you wish to \\\"upgrade\\\" the session's security by asking the user to perform TOTP / WebAuth/ ...\\nyou would set this to \\\"aal2\\\".\",\n            \"in\": \"query\",\n            \"name\": \"aal\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The Session Token of the Identity performing the settings flow.\",\n            \"in\": \"header\",\n            \"name\": \"X-Session-Token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token\\nafter the login flow has been completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_session_token_exchange_code\",\n            \"schema\": {\n              \"type\": \"boolean\"\n            }\n          },\n          {\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"An optional organization ID that should be used for logging this user in.\\nThis parameter is only effective in the Ory Network.\",\n            \"in\": \"query\",\n            \"name\": \"organization\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Via should contain the identity's credential the code should be sent to. Only relevant in aal2 flows.\\n\\nDEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice\\nof MFA credentials to choose from to perform the second factor instead.\",\n            \"in\": \"query\",\n            \"name\": \"via\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"An optional identity schema to use for the login flow.\",\n            \"in\": \"query\",\n            \"name\": \"identity_schema\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Login Flow for Native Apps\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/login/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user login flow. This endpoint will set the appropriate\\ncookies and anti-CSRF measures required for browser-based flows.\\n\\nIf this endpoint is opened as a link in the browser, it will be redirected to\\n`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\\n`?refresh=true` was set.\\n\\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n\\nThe optional query parameter login_challenge is set when using Kratos with\\nHydra in an OAuth2 flow. See the oauth2_provider.url configuration\\noption.\\n\\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"operationId\": \"createBrowserLoginFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"Refresh a login session\\n\\nIf set to true, this will refresh an existing login session by\\nasking the user to sign in again. This will reset the\\nauthenticated_at time of the session.\",\n            \"in\": \"query\",\n            \"name\": \"refresh\",\n            \"schema\": {\n              \"type\": \"boolean\"\n            }\n          },\n          {\n            \"description\": \"Request a Specific AuthenticationMethod Assurance Level\\n\\nUse this parameter to upgrade an existing session's authenticator assurance level (AAL). This\\nallows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password,\\nthe AAL is 1. If you wish to \\\"upgrade\\\" the session's security by asking the user to perform TOTP / WebAuth/ ...\\nyou would set this to \\\"aal2\\\".\",\n            \"in\": \"query\",\n            \"name\": \"aal\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"An optional Hydra login challenge. If present, Kratos will cooperate with\\nOry Hydra to act as an OAuth2 identity provider.\\n\\nThe value for this parameter comes from `login_challenge` URL Query parameter sent to your\\napplication (e.g. `/login?login_challenge=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"login_challenge\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"An optional organization ID that should be used for logging this user in.\\nThis parameter is only effective in the Ory Network.\",\n            \"in\": \"query\",\n            \"name\": \"organization\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Via should contain the identity's credential the code should be sent to. Only relevant in aal2 flows.\\n\\nDEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice\\nof MFA credentials to choose from to perform the second factor instead.\",\n            \"in\": \"query\",\n            \"name\": \"via\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"An optional identity schema to use for the login flow.\",\n            \"in\": \"query\",\n            \"name\": \"identity_schema\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Login Flow for Browsers\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/login/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a login flow's context with, for example, error details and other information.\\n\\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\\n\\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\\nand you need to forward the incoming HTTP Cookie header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/login', async function (req, res) {\\nconst flow = await client.getLoginFlow(req.header('cookie'), req.query['flow'])\\n\\nres.render('login', flow)\\n})\\n```\\n\\nThis request may fail due to several reasons. The `error.id` can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`self_service_flow_expired`: The flow is expired and you should request a new one.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"operationId\": \"getLoginFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Login Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/login?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/loginFlow\"\n                }\n              }\n            },\n            \"description\": \"loginFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get Login Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/logout\": {\n      \"get\": {\n        \"description\": \"This endpoint logs out an identity in a self-service manner.\\n\\nIf the `Accept` HTTP header is not set to `application/json`, the browser will be redirected (HTTP 303 See Other)\\nto the `return_to` parameter of the initial request or fall back to `urls.default_return_to`.\\n\\nIf the `Accept` HTTP header is set to `application/json`, a 204 No Content response\\nwill be sent on successful logout instead.\\n\\nThis endpoint is NOT INTENDED for API clients and only works\\nwith browsers (Chrome, Firefox, ...). For API clients you can\\ncall the `/self-service/logout/api` URL directly with the Ory Session Token.\\n\\nMore information can be found at [Ory Kratos User Logout Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-logout).\",\n        \"operationId\": \"updateLogoutFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"A Valid Logout Token\\n\\nIf you do not have a logout token because you only have a session cookie,\\ncall `/self-service/logout/browser` to generate a URL for this endpoint.\",\n            \"in\": \"query\",\n            \"name\": \"token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The URL to return to after the logout was completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Update Logout Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/logout/api\": {\n      \"delete\": {\n        \"description\": \"Use this endpoint to log out an identity using an Ory Session Token. If the Ory Session Token was successfully\\nrevoked, the server returns a 204 No Content response. A 204 No Content response is also sent when\\nthe Ory Session Token has been revoked already before.\\n\\nIf the Ory Session Token is malformed or does not exist a 403 Forbidden response will be returned.\\n\\nThis endpoint does not remove any HTTP\\nCookies - use the Browser-Based Self-Service Logout Flow instead.\",\n        \"operationId\": \"performNativeLogout\",\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/performNativeLogoutBody\"\n              }\n            }\n          },\n          \"required\": true,\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Perform Logout for Native Apps\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/logout/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user logout flow and a URL which can be used to log out the user.\\n\\nThis endpoint is NOT INTENDED for API clients and only works\\nwith browsers (Chrome, Firefox, ...). For API clients you can\\ncall the `/self-service/logout/api` URL directly with the Ory Session Token.\\n\\nThe URL is only valid for the currently signed in user. If no user is signed in, this endpoint returns\\na 401 error.\\n\\nWhen calling this endpoint from a backend, please ensure to properly forward the HTTP cookies.\",\n        \"operationId\": \"createBrowserLogoutFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"HTTP Cookies\\n\\nIf you call this endpoint from a backend, please include the\\noriginal Cookie header in the request.\",\n            \"in\": \"header\",\n            \"name\": \"cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Return to URL\\n\\nThe URL to which the browser should be redirected to after the logout\\nhas been performed.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/logoutFlow\"\n                }\n              }\n            },\n            \"description\": \"logoutFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"500\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create a Logout URL for Browsers\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/recovery\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to update a recovery flow. This endpoint\\nbehaves differently for API and browser flows and has several states:\\n\\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\\nand works with API- and Browser-initiated flows.\\nFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid.\\nand a HTTP 303 See Other redirect with a fresh recovery flow if the flow was otherwise invalid (e.g. expired).\\nFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Recovery UI URL with the Recovery Flow ID appended.\\n`sent_email` is the success state after `choose_method` for the `link` method and allows the user to request another recovery email. It\\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\\\"sending a recovery link\\\")\\ndoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\\n(if the link was valid) and instructs the user to update their password, or a redirect to the Recover UI URL with\\na new Recovery Flow ID which contains an error message that the recovery link was invalid.\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\",\n        \"operationId\": \"updateRecoveryFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Recovery Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/recovery?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Recovery Token\\n\\nThe recovery token which completes the recovery request. If the token\\nis invalid (e.g. expired) an error will be shown to the end-user.\\n\\nThis parameter is usually set in a link and not used by any direct API call.\",\n            \"in\": \"query\",\n            \"name\": \"token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateRecoveryFlowBody\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateRecoveryFlowBody\"\n              }\n            }\n          },\n          \"required\": true,\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"422\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorBrowserLocationChangeRequired\"\n                }\n              }\n            },\n            \"description\": \"errorBrowserLocationChangeRequired\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Update Recovery Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/recovery/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a recovery flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error.\\n\\nOn an existing recovery flow, use the `getRecoveryFlow` API endpoint.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\",\n        \"operationId\": \"createNativeRecoveryFlow\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Recovery Flow for Native Apps\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/recovery/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based account recovery flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.recovery.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists, the browser is returned to the configured return URL.\\n\\nIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects\\nor a 400 bad request error if the user is already authenticated.\\n\\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\",\n        \"operationId\": \"createBrowserRecoveryFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Recovery Flow for Browsers\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/recovery/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a recovery flow's context with, for example, error details and other information.\\n\\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\\n\\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\\nand you need to forward the incoming HTTP Cookie header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/recovery', async function (req, res) {\\nconst flow = await client.getRecoveryFlow(req.header('Cookie'), req.query['flow'])\\n\\nres.render('recovery', flow)\\n})\\n```\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\",\n        \"operationId\": \"getRecoveryFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Flow ID\\n\\nThe value for this parameter comes from `request` URL Query parameter sent to your\\napplication (e.g. `/recovery?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/recoveryFlow\"\n                }\n              }\n            },\n            \"description\": \"recoveryFlow\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get Recovery Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/registration\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a registration flow by sending an identity's traits and password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and a application/json body with the created identity success - if the session hook is configured the\\n`session` and `session_token` will also be included;\\nHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\\nHTTP 400 on form validation errors.\\n\\nBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\\na HTTP 303 redirect to the post/after registration URL or the `return_to` value if it was set and if the registration succeeded;\\na HTTP 303 redirect to the registration UI URL with the flow ID containing the validation errors otherwise.\\n\\nBrowser flows with an accept header of `application/json` will not redirect but instead respond with\\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\n\\nIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\\nMost likely used in Social Sign In flows.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"operationId\": \"updateRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Registration Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/registration?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateRegistrationFlowBody\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateRegistrationFlowBody\"\n              }\n            }\n          },\n          \"required\": true,\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/successfulNativeRegistration\"\n                }\n              }\n            },\n            \"description\": \"successfulNativeRegistration\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"422\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorBrowserLocationChangeRequired\"\n                }\n              }\n            },\n            \"description\": \"errorBrowserLocationChangeRequired\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Update Registration Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/registration/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\\nwill be returned unless the URL query parameter `?refresh=true` is set.\\n\\nTo fetch an existing registration flow call `/self-service/registration/flows?flow=\\u003cflow_id\\u003e`.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nIn the case of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"operationId\": \"createNativeRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token\\nafter the login flow has been completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_session_token_exchange_code\",\n            \"schema\": {\n              \"type\": \"boolean\"\n            }\n          },\n          {\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"An optional organization ID that should be used to register this user.\\nThis parameter is only effective in the Ory Network.\",\n            \"in\": \"query\",\n            \"name\": \"organization\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"An optional identity schema to use for the registration flow.\",\n            \"in\": \"query\",\n            \"name\": \"identity_schema\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Registration Flow for Native Apps\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/registration/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user registration flow. This endpoint will set the appropriate\\ncookies and anti-CSRF measures required for browser-based flows.\\n\\nIf this endpoint is opened as a link in the browser, it will be redirected to\\n`selfservice.flows.registration.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists already, the browser will be redirected to `urls.default_redirect_url`.\\n\\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n\\nIf this endpoint is called via an AJAX request, the response contains the registration flow without a redirect.\\n\\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"operationId\": \"createBrowserRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Ory OAuth 2.0 Login Challenge.\\n\\nIf set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\\n\\nThe value for this parameter comes from `login_challenge` URL Query parameter sent to your\\napplication (e.g. `/registration?login_challenge=abcde`).\\n\\nThis feature is compatible with Ory Hydra when not running on the Ory Network.\",\n            \"in\": \"query\",\n            \"name\": \"login_challenge\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The URL to return the browser to after the verification flow was completed.\\n\\nAfter the registration flow is completed, the user will be sent a verification email.\\nUpon completing the verification flow, this URL will be used to override the default\\n`selfservice.flows.verification.after.default_redirect_to` value.\",\n            \"in\": \"query\",\n            \"name\": \"after_verification_return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"An optional organization ID that should be used to register this user.\\nThis parameter is only effective in the Ory Network.\",\n            \"in\": \"query\",\n            \"name\": \"organization\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"An optional identity schema to use for the registration flow.\",\n            \"in\": \"query\",\n            \"name\": \"identity_schema\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Registration Flow for Browsers\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/registration/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a registration flow's context with, for example, error details and other information.\\n\\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\\n\\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\\nand you need to forward the incoming HTTP Cookie header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/registration', async function (req, res) {\\nconst flow = await client.getRegistrationFlow(req.header('cookie'), req.query['flow'])\\n\\nres.render('registration', flow)\\n})\\n```\\n\\nThis request may fail due to several reasons. The `error.id` can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`self_service_flow_expired`: The flow is expired and you should request a new one.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"operationId\": \"getRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Registration Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/registration?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/registrationFlow\"\n                }\n              }\n            },\n            \"description\": \"registrationFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get Registration Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/settings\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a settings flow by sending an identity's updated password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI-initiated flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and an application/json body with the session token on success;\\nHTTP 303 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\nHTTP 401 when the endpoint is called without a valid session token.\\nHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\\nImplies that the user needs to re-authenticate.\\n\\nBrowser flows without HTTP Header `Accept` or with `Accept: text/*` respond with\\na HTTP 303 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\\na HTTP 303 redirect to the Settings UI URL with the flow ID containing the validation errors otherwise.\\na HTTP 303 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\\n\\nBrowser flows with HTTP Header `Accept: application/json` respond with\\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\\nHTTP 401 when the endpoint is called without a valid session cookie.\\nHTTP 403 when the page is accessed without a session cookie or the session's AAL is too low.\\nHTTP 400 on form validation errors.\\n\\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\\n\\nIf this endpoint is called with a `Accept: application/json` HTTP header, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_refresh_required`: The identity requested to change something that needs a privileged session. Redirect\\nthe identity to the login init endpoint with query parameters `?refresh=true\\u0026return_to=\\u003cthe-current-browser-url\\u003e`,\\nor initiate a refresh login flow otherwise.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`session_inactive`: No Ory Session was found - sign in a user first.\\n`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\\nidentity logged in instead.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\\nMost likely used in Social Sign In flows.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"updateSettingsFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Settings Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/settings?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The Session Token of the Identity performing the settings flow.\",\n            \"in\": \"header\",\n            \"name\": \"X-Session-Token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateSettingsFlowBody\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateSettingsFlowBody\"\n              }\n            }\n          },\n          \"required\": true,\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"422\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorBrowserLocationChangeRequired\"\n                }\n              }\n            },\n            \"description\": \"errorBrowserLocationChangeRequired\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"summary\": \"Complete Settings Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/settings/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\\nYou must provide a valid Ory Kratos Session Token for this endpoint to respond with HTTP 200 OK.\\n\\nTo fetch an existing settings flow call `/self-service/settings/flows?flow=\\u003cflow_id\\u003e`.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor or change the configuration.\\n\\nIn the case of an error, the `error.id` of the JSON response body can be one of:\\n\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`session_inactive`: No Ory Session was found - sign in a user first.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"createNativeSettingsFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Session Token of the Identity performing the settings flow.\",\n            \"in\": \"header\",\n            \"name\": \"X-Session-Token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Settings Flow for Native Apps\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/settings/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\\nOry Kratos Session Cookie is included in the request, a login flow will be initialized.\\n\\nIf this endpoint is opened as a link in the browser, it will be redirected to\\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid user session\\nwas set, the browser will be redirected to the login endpoint.\\n\\nIf this endpoint is called via an AJAX request, the response contains the settings flow without any redirects\\nor a 401 forbidden error if no valid session was set.\\n\\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\\n\\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`session_inactive`: No Ory Session was found - sign in a user first.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n\\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"createBrowserSettingsFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Settings Flow for Browsers\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/settings/flows\": {\n      \"get\": {\n        \"description\": \"When accessing this endpoint through Ory Kratos' Public API you must ensure that either the Ory Kratos Session Cookie\\nor the Ory Kratos Session Token are set.\\n\\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor or change the configuration.\\n\\nYou can access this endpoint without credentials when using Ory Kratos' Admin API.\\n\\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`session_inactive`: No Ory Session was found - sign in a user first.\\n`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\\nidentity logged in instead.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"operationId\": \"getSettingsFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the Settings Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/settings?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The Session Token\\n\\nWhen using the SDK in an app without a browser, please include the\\nsession token here.\",\n            \"in\": \"header\",\n            \"name\": \"X-Session-Token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/settingsFlow\"\n                }\n              }\n            },\n            \"description\": \"settingsFlow\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get Settings Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/verification\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a verification flow. This endpoint\\nbehaves differently for API and browser flows and has several states:\\n\\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\\nand works with API- and Browser-initiated flows.\\nFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\\nand a HTTP 303 See Other redirect with a fresh verification flow if the flow was otherwise invalid (e.g. expired).\\nFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Verification UI URL with the Verification Flow ID appended.\\n`sent_email` is the success state after `choose_method` when using the `link` method and allows the user to request another verification email. It\\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\\\"sending a verification link\\\")\\ndoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\\n(if the link was valid) and instructs the user to update their password, or a redirect to the Verification UI URL with\\na new Verification Flow ID which contains an error message that the verification link was invalid.\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"updateVerificationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Verification Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/verification?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"flow\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Verification Token\\n\\nThe verification token which completes the verification request. If the token\\nis invalid (e.g. expired) an error will be shown to the end-user.\\n\\nThis parameter is usually set in a link and not used by any direct API call.\",\n            \"in\": \"query\",\n            \"name\": \"token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"requestBody\": {\n          \"content\": {\n            \"application/json\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateVerificationFlowBody\"\n              }\n            },\n            \"application/x-www-form-urlencoded\": {\n              \"schema\": {\n                \"$ref\": \"#/components/schemas/updateVerificationFlowBody\"\n              }\n            }\n          },\n          \"required\": true,\n          \"x-originalParamName\": \"Body\"\n        },\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Complete Verification Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/verification/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nTo fetch an existing verification flow call `/self-service/verification/flows?flow=\\u003cflow_id\\u003e`.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"createNativeVerificationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"A URL contained in the return_to key of the verification flow.\\nThis piece of data has no effect on the actual logic of the flow and is purely informational.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Verification Flow for Native Apps\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/verification/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based account verification flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.verification.ui_url` with the flow ID set as the query parameter `?flow=`.\\n\\nIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects.\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"createBrowserVerificationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"in\": \"query\",\n            \"name\": \"return_to\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"303\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Create Verification Flow for Browser Clients\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/verification/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a verification flow's context with, for example, error details and other information.\\n\\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\\n\\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\\nand you need to forward the incoming HTTP Cookie header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/recovery', async function (req, res) {\\nconst flow = await client.getVerificationFlow(req.header('cookie'), req.query['flow'])\\n\\nres.render('verification', flow)\\n})\\n```\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"operationId\": \"getVerificationFlow\",\n        \"parameters\": [\n          {\n            \"description\": \"The Flow ID\\n\\nThe value for this parameter comes from `request` URL Query parameter sent to your\\napplication (e.g. `/verification?flow=abcde`).\",\n            \"in\": \"query\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK on the server side you must include the HTTP Cookie Header\\noriginally sent to your HTTP handler here.\",\n            \"in\": \"header\",\n            \"name\": \"cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/verificationFlow\"\n                }\n              }\n            },\n            \"description\": \"verificationFlow\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get Verification Flow\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/sessions\": {\n      \"delete\": {\n        \"description\": \"Calling this endpoint invalidates all except the current session that belong to the logged-in user.\\nSession data are not deleted.\",\n        \"operationId\": \"disableMyOtherSessions\",\n        \"parameters\": [\n          {\n            \"description\": \"Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\",\n            \"in\": \"header\",\n            \"name\": \"X-Session-Token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\\nscenario you must include the HTTP Cookie Header which originally was included in the request to your server.\\nAn example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\\n\\nIt is ok if more than one cookie are included here as all other cookies will be ignored.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/deleteMySessionsCount\"\n                }\n              }\n            },\n            \"description\": \"deleteMySessionsCount\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Disable my other sessions\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      },\n      \"get\": {\n        \"description\": \"This endpoints returns all other active sessions that belong to the logged-in user.\\nThe current session can be retrieved by calling the `/sessions/whoami` endpoint.\",\n        \"operationId\": \"listMySessions\",\n        \"parameters\": [\n          {\n            \"description\": \"Deprecated Items per Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis is the number of items per page.\",\n            \"in\": \"query\",\n            \"name\": \"per_page\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 1000,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Deprecated Pagination Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis value is currently an integer, but it is not sequential. The value is not the page number, but a\\nreference. The next page can be any number and some numbers might return an empty list.\\n\\nFor example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist.\\nThe first page can be retrieved by omitting this parameter. Following page pointers will be returned in the\\n`Link` header.\",\n            \"in\": \"query\",\n            \"name\": \"page\",\n            \"schema\": {\n              \"format\": \"int64\",\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Page Size\\n\\nThis is the number of items per page to return. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_size\",\n            \"schema\": {\n              \"default\": 250,\n              \"format\": \"int64\",\n              \"maximum\": 500,\n              \"minimum\": 1,\n              \"type\": \"integer\"\n            }\n          },\n          {\n            \"description\": \"Next Page Token\\n\\nThe next page token. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"in\": \"query\",\n            \"name\": \"page_token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\",\n            \"in\": \"header\",\n            \"name\": \"X-Session-Token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\\nscenario you must include the HTTP Cookie Header which originally was included in the request to your server.\\nAn example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\\n\\nIt is ok if more than one cookie are included here as all other cookies will be ignored.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/components/responses/listMySessions\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Get My Active Sessions\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/sessions/token-exchange\": {\n      \"get\": {\n        \"operationId\": \"exchangeSessionToken\",\n        \"parameters\": [\n          {\n            \"description\": \"The part of the code return when initializing the flow.\",\n            \"in\": \"query\",\n            \"name\": \"init_code\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"The part of the code returned by the return_to URL.\",\n            \"in\": \"query\",\n            \"name\": \"return_to_code\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/successfulNativeLogin\"\n                }\n              }\n            },\n            \"description\": \"successfulNativeLogin\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"404\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"410\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Exchange Session Token\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/sessions/whoami\": {\n      \"get\": {\n        \"description\": \"Uses the HTTP Headers in the GET request to determine (e.g. by using checking the cookies) who is authenticated.\\nReturns a session object in the body or 401 if the credentials are invalid or no credentials were sent.\\nWhen the request it successful it adds the user ID to the 'X-Kratos-Authenticated-Identity-Id' header\\nin the response.\\n\\nIf you call this endpoint from a server-side application, you must forward the HTTP Cookie Header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/protected-endpoint', async function (req, res) {\\nconst session = await client.toSession(undefined, req.header('cookie'))\\n\\nconsole.log(session)\\n})\\n```\\n\\nWhen calling this endpoint from a non-browser application (e.g. mobile app) you must include the session token:\\n\\n```js\\npseudo-code example\\n...\\nconst session = await client.toSession(\\\"the-session-token\\\")\\n\\nconsole.log(session)\\n```\\n\\nWhen using a token template, the token is included in the `tokenized` field of the session.\\n\\n```js\\npseudo-code example\\n...\\nconst session = await client.toSession(\\\"the-session-token\\\", { tokenize_as: \\\"example-jwt-template\\\" })\\n\\nconsole.log(session.tokenized) // The JWT\\n```\\n\\nDepending on your configuration this endpoint might return a 403 status code if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor or change the configuration.\\n\\nThis endpoint is useful for:\\n\\nAJAX calls. Remember to send credentials and set up CORS correctly!\\nReverse proxies and API Gateways\\nServer-side calls - use the `X-Session-Token` header!\\n\\nThis endpoint authenticates users by checking:\\n\\nif the `Cookie` HTTP header was set containing an Ory Kratos Session Cookie;\\nif the `Authorization: bearer \\u003cory-session-token\\u003e` HTTP header was set with a valid Ory Kratos Session Token;\\nif the `X-Session-Token` HTTP header was set with a valid Ory Kratos Session Token.\\n\\nIf none of these headers are set or the cookie or token are invalid, the endpoint returns a HTTP 401 status code.\\n\\nAs explained above, this request may fail due to several reasons. The `error.id` can be one of:\\n\\n`session_inactive`: No active session was found in the request (e.g. no Ory Session Cookie / Ory Session Token).\\n`session_aal2_required`: An active session was found but it does not fulfil the Authenticator Assurance Level, implying that the session must (e.g.) authenticate the second factor.\",\n        \"operationId\": \"toSession\",\n        \"parameters\": [\n          {\n            \"description\": \"Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\",\n            \"example\": \"MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj\",\n            \"in\": \"header\",\n            \"name\": \"X-Session-Token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\\nscenario you must include the HTTP Cookie Header which originally was included in the request to your server.\\nAn example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\\n\\nIt is ok if more than one cookie are included here as all other cookies will be ignored.\",\n            \"example\": \"ory_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Returns the session additionally as a token (such as a JWT)\\n\\nThe value of this parameter has to be a valid, configured Ory Session token template. For more information head over to [the documentation](http://ory.sh/docs/identities/session-to-jwt-cors).\",\n            \"in\": \"query\",\n            \"name\": \"tokenize_as\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/session\"\n                }\n              }\n            },\n            \"description\": \"session\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"403\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Check Who the Current HTTP Session Belongs To\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/sessions/{id}\": {\n      \"delete\": {\n        \"description\": \"Calling this endpoint invalidates the specified session. The current session cannot be revoked.\\nSession data are not deleted.\",\n        \"operationId\": \"disableMySession\",\n        \"parameters\": [\n          {\n            \"description\": \"ID is the session's ID.\",\n            \"in\": \"path\",\n            \"name\": \"id\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\",\n            \"in\": \"header\",\n            \"name\": \"X-Session-Token\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          },\n          {\n            \"description\": \"Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\\nscenario you must include the HTTP Cookie Header which originally was included in the request to your server.\\nAn example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\\n\\nIt is ok if more than one cookie are included here as all other cookies will be ignored.\",\n            \"in\": \"header\",\n            \"name\": \"Cookie\",\n            \"schema\": {\n              \"type\": \"string\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/components/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"401\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          },\n          \"default\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"$ref\": \"#/components/schemas/errorGeneric\"\n                }\n              }\n            },\n            \"description\": \"errorGeneric\"\n          }\n        },\n        \"summary\": \"Disable one of my sessions\",\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/version\": {\n      \"get\": {\n        \"description\": \"This endpoint returns the version of Ory Kratos.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of this service, the version will never\\nrefer to the cluster state, only to a single instance.\",\n        \"operationId\": \"getVersion\",\n        \"responses\": {\n          \"200\": {\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"properties\": {\n                    \"version\": {\n                      \"description\": \"The version of Ory Kratos.\",\n                      \"type\": \"string\"\n                    }\n                  },\n                  \"required\": [\n                    \"version\"\n                  ],\n                  \"type\": \"object\"\n                }\n              }\n            },\n            \"description\": \"Returns the Ory Kratos version.\"\n          }\n        },\n        \"summary\": \"Return Running Software Version.\",\n        \"tags\": [\n          \"metadata\"\n        ]\n      }\n    }\n  },\n  \"tags\": [\n    {\n      \"description\": \"APIs for managing identities.\",\n      \"name\": \"identity\"\n    },\n    {\n      \"description\": \"Endpoints used by frontend applications (e.g. Single-Page-App, Native Apps, Server Apps, ...) to manage a user's own profile.\",\n      \"name\": \"frontend\"\n    },\n    {\n      \"description\": \"APIs for managing email and SMS message delivery.\",\n      \"name\": \"courier\"\n    },\n    {\n      \"description\": \"Server Metadata provides relevant information about the running server. Only available when self-hosting this service.\",\n      \"name\": \"metadata\"\n    }\n  ],\n  \"x-forwarded-proto\": \"string\",\n  \"x-request-id\": \"string\"\n}"
  },
  {
    "path": "spec/swagger.json",
    "content": "{\n  \"consumes\": [\n    \"application/json\",\n    \"application/x-www-form-urlencoded\"\n  ],\n  \"produces\": [\n    \"application/json\"\n  ],\n  \"schemes\": [\n    \"http\",\n    \"https\"\n  ],\n  \"swagger\": \"2.0\",\n  \"info\": {\n    \"description\": \"Welcome to the Ory Kratos HTTP API documentation!\",\n    \"title\": \"Ory Kratos\",\n    \"version\": \"latest\"\n  },\n  \"basePath\": \"/\",\n  \"paths\": {\n    \"/.well-known/ory/webauthn.js\": {\n      \"get\": {\n        \"description\": \"This endpoint provides JavaScript which is needed in order to perform WebAuthn login and registration.\\n\\nIf you are building a JavaScript Browser App (e.g. in ReactJS or AngularJS) you will need to load this file:\\n\\n```html\\n\\u003cscript src=\\\"https://public-kratos.example.org/.well-known/ory/webauthn.js\\\" type=\\\"script\\\" async /\\u003e\\n```\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"produces\": [\n          \"text/javascript\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Get WebAuthn JavaScript\",\n        \"operationId\": \"getWebAuthnJavaScript\",\n        \"responses\": {\n          \"200\": {\n            \"description\": \"webAuthnJavaScript\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/webAuthnJavaScript\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"hydra-public-low\"\n      }\n    },\n    \"/admin/courier/messages\": {\n      \"get\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Lists all messages by given status and recipient.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"courier\"\n        ],\n        \"summary\": \"List Messages\",\n        \"operationId\": \"listCourierMessages\",\n        \"parameters\": [\n          {\n            \"maximum\": 1000,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Items per Page\\n\\nThis is the number of items per page to return.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_size\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Next Page Token\\n\\nThe next page token.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_token\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"description\": \"Status filters out messages based on status.\\nIf no value is provided, it doesn't take effect on filter.\",\n            \"name\": \"status\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Recipient filters out messages based on recipient.\\nIf no value is provided, it doesn't take effect on filter.\",\n            \"name\": \"recipient\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/responses/listCourierMessages\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/courier/messages/{id}\": {\n      \"get\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Gets a specific messages by the given ID.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"courier\"\n        ],\n        \"summary\": \"Get a Message\",\n        \"operationId\": \"getCourierMessage\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"MessageID is the ID of the message.\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"message\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/message\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/admin/identities\": {\n      \"get\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Lists all [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model) in the system. Note: filters cannot be combined.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"List Identities\",\n        \"operationId\": \"listIdentities\",\n        \"parameters\": [\n          {\n            \"maximum\": 1000,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Deprecated Items per Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis is the number of items per page.\",\n            \"name\": \"per_page\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"description\": \"Deprecated Pagination Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis value is currently an integer, but it is not sequential. The value is not the page number, but a\\nreference. The next page can be any number and some numbers might return an empty list.\\n\\nFor example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist.\\nThe first page can be retrieved by omitting this parameter. Following page pointers will be returned in the\\n`Link` header.\",\n            \"name\": \"page\",\n            \"in\": \"query\"\n          },\n          {\n            \"maximum\": 500,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Page Size\\n\\nThis is the number of items per page to return. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_size\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Next Page Token\\n\\nThe next page token. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_token\",\n            \"in\": \"query\"\n          },\n          {\n            \"enum\": [\n              \"\",\n              \"strong\",\n              \"eventual\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \" ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level.\\nstrong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level.\\neventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\",\n            \"description\": \"Read Consistency Level (preview)\\n\\nThe read consistency level determines the consistency guarantee for reads:\\n\\nstrong (slow): The read is guaranteed to return the most recent data committed at the start of the read.\\neventual (very fast): The result will return data that is about 4.8 seconds old.\\n\\nThe default consistency guarantee can be changed in the Ory Network Console or using the Ory CLI with\\n`ory patch project --replace '/previews/default_read_consistency_level=\\\"strong\\\"'`.\\n\\nSetting the default consistency level to `eventual` may cause regressions in the future as we add consistency\\ncontrols to more APIs. Currently, the following APIs will be affected by this setting:\\n\\n`GET /admin/identities`\\n\\nThis feature is in preview and only available in Ory Network.\\n ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level.\\nstrong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level.\\neventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\",\n            \"name\": \"consistency\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"array\",\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"description\": \"Retrieve multiple identities by their IDs.\\n\\nThis parameter has the following limitations:\\n\\nDuplicate or non-existent IDs are ignored.\\nThe order of returned IDs may be different from the request.\\nThis filter does not support pagination. You must implement your own pagination as the maximum number of items returned by this endpoint may not exceed a certain threshold (currently 500).\",\n            \"name\": \"ids\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"CredentialsIdentifier is the identifier (username, email) of the credentials to look up using exact match.\\nOnly one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\",\n            \"name\": \"credentials_identifier\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"This is an EXPERIMENTAL parameter that WILL CHANGE. Do NOT rely on consistent, deterministic behavior.\\nTHIS PARAMETER WILL BE REMOVED IN AN UPCOMING RELEASE WITHOUT ANY MIGRATION PATH.\\n\\nCredentialsIdentifierSimilar is the (partial) identifier (username, email) of the credentials to look up using similarity search.\\nOnly one of CredentialsIdentifier and CredentialsIdentifierSimilar can be used.\",\n            \"name\": \"preview_credentials_identifier_similar\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"array\",\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"description\": \"Include Credentials in Response\\n\\nInclude any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\\nthe initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\",\n            \"name\": \"include_credential\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"List identities that belong to a specific organization.\",\n            \"name\": \"organization_id\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/responses/listIdentities\"\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      },\n      \"post\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Create an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model).  This endpoint can also be used to\\n[import credentials](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities)\\nfor instance passwords, social sign in configurations or multifactor methods.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Create an Identity\",\n        \"operationId\": \"createIdentity\",\n        \"parameters\": [\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/createIdentityBody\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"201\": {\n            \"description\": \"identity\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/identity\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"409\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      },\n      \"patch\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Creates multiple [identities](https://www.ory.com/docs/kratos/concepts/identity-user-model).\\n\\nYou can also use this endpoint to [import credentials](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities),\\nincluding passwords, social sign-in settings, and multi-factor authentication methods.\\n\\nIf the patch includes hashed passwords you can import up to 1,000 identities per request.\\n\\nIf the patch includes at least one plaintext password you can import up to 200 identities per request.\\n\\nAvoid importing large batches with plaintext passwords. They can cause timeouts as the passwords need to be hashed before they are stored.\\n\\nIf at least one identity is imported successfully, the response status is 200 OK.\\nIf all imports fail, the response is one of the following 4xx errors:\\n400 Bad Request: The request payload is invalid or improperly formatted.\\n409 Conflict: Duplicate identities or conflicting data were detected.\\n\\nIf you get a 504 Gateway Timeout:\\nReduce the batch size\\nAvoid duplicate identities\\nPre-hash passwords with BCrypt\\n\\nIf the issue persists, contact support.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Create multiple identities\",\n        \"operationId\": \"batchPatchIdentities\",\n        \"parameters\": [\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/patchIdentitiesBody\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"batchPatchIdentitiesResponse\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/batchPatchIdentitiesResponse\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"409\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/identities/by/external/{externalID}\": {\n      \"get\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Return an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its external ID. You can optionally\\ninclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Get an Identity by its External ID\",\n        \"operationId\": \"getIdentityByExternalID\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ExternalID must be set to the ID of identity you want to get\",\n            \"name\": \"externalID\",\n            \"in\": \"path\",\n            \"required\": true\n          },\n          {\n            \"type\": \"array\",\n            \"items\": {\n              \"enum\": [\n                \"password\",\n                \"oidc\",\n                \"totp\",\n                \"lookup_secret\",\n                \"webauthn\",\n                \"code\",\n                \"passkey\",\n                \"profile\",\n                \"saml\",\n                \"link_recovery\",\n                \"code_recovery\"\n              ],\n              \"type\": \"string\"\n            },\n            \"description\": \"Include Credentials in Response\\n\\nInclude any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\\nthe initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\",\n            \"name\": \"include_credential\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"identity\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/identity\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/admin/identities/{id}\": {\n      \"get\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Return an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) by its ID. You can optionally\\ninclude credentials (e.g. social sign in connections) in the response by using the `include_credential` query parameter.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Get an Identity\",\n        \"operationId\": \"getIdentity\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID must be set to the ID of identity you want to get\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          },\n          {\n            \"type\": \"array\",\n            \"items\": {\n              \"enum\": [\n                \"password\",\n                \"oidc\",\n                \"totp\",\n                \"lookup_secret\",\n                \"webauthn\",\n                \"code\",\n                \"passkey\",\n                \"profile\",\n                \"saml\",\n                \"link_recovery\",\n                \"code_recovery\"\n              ],\n              \"type\": \"string\"\n            },\n            \"description\": \"Include Credentials in Response\\n\\nInclude any credential, for example `password` or `oidc`, in the response. When set to `oidc`, This will return\\nthe initial OAuth 2.0 Access Token, OAuth 2.0 Refresh Token and the OpenID Connect ID Token if available.\",\n            \"name\": \"include_credential\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"identity\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/identity\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-low\"\n      },\n      \"put\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"This endpoint updates an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model). The full identity\\npayload, except credentials, is expected. For partial updates, use the [patchIdentity](https://www.ory.sh/docs/reference/api#tag/identity/operation/patchIdentity) operation.\\n\\nA credential can be provided via the `credentials` field in the request body.\\nIf provided, the credentials will be imported and added to the existing credentials of the identity.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Update an Identity\",\n        \"operationId\": \"updateIdentity\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID must be set to the ID of identity you want to update\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          },\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/updateIdentityBody\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"identity\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/identity\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"409\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      },\n      \"delete\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Calling this endpoint irrecoverably and permanently deletes the [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) given its ID. This action can not be undone.\\nThis endpoint returns 204 when the identity was deleted or 404 if the identity was not found.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Delete an Identity\",\n        \"operationId\": \"deleteIdentity\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID is the identity's ID.\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      },\n      \"patch\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Partially updates an [identity's](https://www.ory.sh/docs/kratos/concepts/identity-user-model) field using [JSON Patch](https://jsonpatch.com/).\\nThe fields `id`, `stateChangedAt` and `credentials` can not be updated using this method.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Patch an Identity\",\n        \"operationId\": \"patchIdentity\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID must be set to the ID of identity you want to update\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          },\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/jsonPatchDocument\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"identity\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/identity\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"409\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/identities/{id}/credentials/{type}\": {\n      \"delete\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Delete an [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) credential by its type.\\nYou cannot delete passkeys or code auth credentials through this API.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Delete a credential for a specific identity\",\n        \"operationId\": \"deleteIdentityCredentials\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID is the identity's ID.\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          },\n          {\n            \"enum\": [\n              \"password\",\n              \"oidc\",\n              \"totp\",\n              \"lookup_secret\",\n              \"webauthn\",\n              \"code\",\n              \"passkey\",\n              \"profile\",\n              \"saml\",\n              \"link_recovery\",\n              \"code_recovery\"\n            ],\n            \"type\": \"string\",\n            \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n            \"description\": \"Type is the type of credentials to delete.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n            \"name\": \"type\",\n            \"in\": \"path\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Identifier is the identifier of the OIDC/SAML credential to delete.\\nFind the identifier by calling the `GET /admin/identities/{id}?include_credential={oidc,saml}` endpoint.\",\n            \"name\": \"identifier\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/identities/{id}/sessions\": {\n      \"get\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"This endpoint returns all sessions that belong to the given Identity.\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"List an Identity's Sessions\",\n        \"operationId\": \"listIdentitySessions\",\n        \"parameters\": [\n          {\n            \"maximum\": 1000,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Deprecated Items per Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis is the number of items per page.\",\n            \"name\": \"per_page\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"description\": \"Deprecated Pagination Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis value is currently an integer, but it is not sequential. The value is not the page number, but a\\nreference. The next page can be any number and some numbers might return an empty list.\\n\\nFor example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist.\\nThe first page can be retrieved by omitting this parameter. Following page pointers will be returned in the\\n`Link` header.\",\n            \"name\": \"page\",\n            \"in\": \"query\"\n          },\n          {\n            \"maximum\": 500,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Page Size\\n\\nThis is the number of items per page to return. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_size\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Next Page Token\\n\\nThe next page token. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_token\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"ID is the identity's ID.\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          },\n          {\n            \"type\": \"boolean\",\n            \"description\": \"Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\",\n            \"name\": \"active\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/responses/listIdentitySessions\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      },\n      \"delete\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Calling this endpoint irrecoverably and permanently deletes and invalidates all sessions that belong to the given Identity.\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Delete \\u0026 Invalidate an Identity's Sessions\",\n        \"operationId\": \"deleteIdentitySessions\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID is the identity's ID.\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/recovery/code\": {\n      \"post\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"This endpoint creates a recovery code which should be given to the user in order for them to recover\\n(or activate) their account.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Create a Recovery Code\",\n        \"operationId\": \"createRecoveryCodeForIdentity\",\n        \"parameters\": [\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/createRecoveryCodeForIdentityBody\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"201\": {\n            \"description\": \"recoveryCodeForIdentity\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/recoveryCodeForIdentity\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/recovery/link\": {\n      \"post\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"This endpoint creates a recovery link which should be given to the user in order for them to recover\\n(or activate) their account.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Create a Recovery Link\",\n        \"operationId\": \"createRecoveryLinkForIdentity\",\n        \"parameters\": [\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/createRecoveryLinkForIdentityBody\"\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"recoveryLinkForIdentity\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/recoveryLinkForIdentity\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/sessions\": {\n      \"get\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Listing all sessions that exist.\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"List All Sessions\",\n        \"operationId\": \"listSessions\",\n        \"parameters\": [\n          {\n            \"maximum\": 1000,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Items per Page\\n\\nThis is the number of items per page to return.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_size\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Next Page Token\\n\\nThe next page token.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_token\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"boolean\",\n            \"description\": \"Active is a boolean flag that filters out sessions based on the state. If no value is provided, all sessions are returned.\",\n            \"name\": \"active\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"array\",\n            \"items\": {\n              \"enum\": [\n                \"identity\",\n                \"devices\"\n              ],\n              \"type\": \"string\"\n            },\n            \"description\": \"ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session.\\nIf no value is provided, the expandable properties are skipped.\",\n            \"name\": \"expand\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/responses/listSessions\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/admin/sessions/{id}\": {\n      \"get\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"This endpoint is useful for:\\n\\nGetting a session object with all specified expandables that exist in an administrative context.\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Get Session\",\n        \"operationId\": \"getSession\",\n        \"parameters\": [\n          {\n            \"type\": \"array\",\n            \"items\": {\n              \"enum\": [\n                \"identity\",\n                \"devices\"\n              ],\n              \"type\": \"string\"\n            },\n            \"description\": \"ExpandOptions is a query parameter encoded list of all properties that must be expanded in the Session.\\nExample - ?expand=Identity\\u0026expand=Devices\\nIf no value is provided, the expandable properties are skipped.\",\n            \"name\": \"expand\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"ID is the session's ID.\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"session\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/session\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-low\"\n      },\n      \"delete\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Calling this endpoint deactivates the specified session. Session data is not deleted.\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Deactivate a Session\",\n        \"operationId\": \"disableSession\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID is the session's ID.\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/admin/sessions/{id}/extend\": {\n      \"patch\": {\n        \"security\": [\n          {\n            \"oryAccessToken\": []\n          }\n        ],\n        \"description\": \"Calling this endpoint extends the given session ID. If `session.earliest_possible_extend` is set it\\nwill only extend the session after the specified time has passed.\\n\\nThis endpoint returns per default a 204 No Content response on success. Older Ory Network projects may\\nreturn a 200 OK response with the session in the body. Returning the session as part of the response\\nwill be deprecated in the future and should not be relied upon.\\n\\nThis endpoint ignores consecutive requests to extend the same session and returns a 404 error in those\\nscenarios. This endpoint also returns 404 errors if the session does not exist.\\n\\nRetrieve the session ID from the `/sessions/whoami` endpoint / `toSession` SDK method.\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Extend a Session\",\n        \"operationId\": \"extendSession\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID is the session's ID.\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"session\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/session\"\n            }\n          },\n          \"204\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-high\"\n      }\n    },\n    \"/health/alive\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a 200 status code when the HTTP server is up running.\\nThis status does currently not include checks whether the database connection is working.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of this service, the health status will never\\nrefer to the cluster state, only to a single instance.\",\n        \"produces\": [\n          \"application/json\",\n          \"text/plain\"\n        ],\n        \"tags\": [\n          \"health\"\n        ],\n        \"summary\": \"Check alive status\",\n        \"operationId\": \"isInstanceAlive\",\n        \"responses\": {\n          \"200\": {\n            \"description\": \"healthStatus\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/healthStatus\"\n            }\n          },\n          \"default\": {\n            \"description\": \"unexpectedError\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/unexpectedError\"\n            }\n          }\n        }\n      }\n    },\n    \"/health/ready\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g.\\nthe database) are responsive as well.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of this service, the health status will never\\nrefer to the cluster state, only to a single instance.\",\n        \"produces\": [\n          \"application/json\",\n          \"text/plain\"\n        ],\n        \"tags\": [\n          \"health\"\n        ],\n        \"summary\": \"Check readiness status\",\n        \"operationId\": \"isInstanceReady\",\n        \"responses\": {\n          \"200\": {\n            \"description\": \"healthStatus\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/healthStatus\"\n            }\n          },\n          \"503\": {\n            \"description\": \"healthNotReadyStatus\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/healthNotReadyStatus\"\n            }\n          },\n          \"default\": {\n            \"description\": \"unexpectedError\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/unexpectedError\"\n            }\n          }\n        }\n      }\n    },\n    \"/schemas\": {\n      \"get\": {\n        \"description\": \"Returns a list of all identity schemas currently in use.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Get all Identity Schemas\",\n        \"operationId\": \"listIdentitySchemas\",\n        \"parameters\": [\n          {\n            \"maximum\": 1000,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Deprecated Items per Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis is the number of items per page.\",\n            \"name\": \"per_page\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"description\": \"Deprecated Pagination Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis value is currently an integer, but it is not sequential. The value is not the page number, but a\\nreference. The next page can be any number and some numbers might return an empty list.\\n\\nFor example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist.\\nThe first page can be retrieved by omitting this parameter. Following page pointers will be returned in the\\n`Link` header.\",\n            \"name\": \"page\",\n            \"in\": \"query\"\n          },\n          {\n            \"maximum\": 500,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Page Size\\n\\nThis is the number of items per page to return. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_size\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Next Page Token\\n\\nThe next page token. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_token\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/responses/identitySchemas\"\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/schemas/{id}\": {\n      \"get\": {\n        \"description\": \"Return a specific identity schema.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"identity\"\n        ],\n        \"summary\": \"Get Identity JSON Schema\",\n        \"operationId\": \"getIdentitySchema\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID must be set to the ID of schema you want to get\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"identitySchema\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/identitySchema\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-admin-medium\"\n      }\n    },\n    \"/self-service/errors\": {\n      \"get\": {\n        \"description\": \"This endpoint returns the error associated with a user-facing self service errors.\\n\\nThis endpoint supports stub values to help you implement the error UI:\\n\\n`?id=stub:500` - returns a stub 500 (Internal Server Error) error.\\n\\nMore information can be found at [Ory Kratos User User Facing Error Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-facing-errors).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Get User-Flow Errors\",\n        \"operationId\": \"getFlowError\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"Error is the error's ID\",\n            \"name\": \"id\",\n            \"in\": \"query\",\n            \"required\": true\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"flowError\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/flowError\"\n            }\n          },\n          \"403\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"500\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/fed-cm/parameters\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a list of all available FedCM providers. It is only supported on the Ory Network.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Get FedCM Parameters\",\n        \"operationId\": \"createFedcmFlow\",\n        \"responses\": {\n          \"200\": {\n            \"description\": \"createFedcmFlowResponse\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/createFedcmFlowResponse\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/fed-cm/token\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to submit a token from a FedCM provider through\\n`navigator.credentials.get` and log the user in. The parameters from\\n`navigator.credentials.get` must have come from `GET\\nself-service/fed-cm/parameters`.\",\n        \"consumes\": [\n          \"application/json\",\n          \"application/x-www-form-urlencoded\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Submit a FedCM token\",\n        \"operationId\": \"updateFedcmFlow\",\n        \"parameters\": [\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"required\": true,\n            \"schema\": {\n              \"$ref\": \"#/definitions/UpdateFedcmFlowBody\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"successfulNativeLogin\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/successfulNativeLogin\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"loginFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/loginFlow\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"422\": {\n            \"description\": \"errorBrowserLocationChangeRequired\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorBrowserLocationChangeRequired\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/login\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a login flow. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI flows expect `application/json` to be sent in the body and responds with\\nHTTP 200 and a application/json body with the session token on success;\\nHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\\nHTTP 400 on form validation errors.\\n\\nBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\\na HTTP 303 redirect to the post/after login URL or the `return_to` value if it was set and if the login succeeded;\\na HTTP 303 redirect to the login UI URL with the flow ID containing the validation errors otherwise.\\n\\nBrowser flows with an accept header of `application/json` will not redirect but instead respond with\\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\n\\nIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\\nMost likely used in Social Sign In flows.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"consumes\": [\n          \"application/json\",\n          \"application/x-www-form-urlencoded\"\n        ],\n        \"produces\": [\n          \"application/json\",\n          \"Header:\",\n          \"Set-Cookie\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Submit a Login Flow\",\n        \"operationId\": \"updateLoginFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Login Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/login?flow=abcde`).\",\n            \"name\": \"flow\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"required\": true,\n            \"schema\": {\n              \"$ref\": \"#/definitions/updateLoginFlowBody\"\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The Session Token of the Identity performing the settings flow.\",\n            \"name\": \"X-Session-Token\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"successfulNativeLogin\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/successfulNativeLogin\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"loginFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/loginFlow\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"422\": {\n            \"description\": \"errorBrowserLocationChangeRequired\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorBrowserLocationChangeRequired\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/login/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a login flow for native apps that do not use a browser, such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\\nwill be returned unless the URL query parameter `?refresh=true` is set.\\n\\nTo fetch an existing login flow call `/self-service/login/flows?flow=\\u003cflow_id\\u003e`.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks, including CSRF login attacks.\\n\\nIn the case of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Login Flow for Native Apps\",\n        \"operationId\": \"createNativeLoginFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"boolean\",\n            \"description\": \"Refresh a login session\\n\\nIf set to true, this will refresh an existing login session by\\nasking the user to sign in again. This will reset the\\nauthenticated_at time of the session.\",\n            \"name\": \"refresh\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Request a Specific AuthenticationMethod Assurance Level\\n\\nUse this parameter to upgrade an existing session's authenticator assurance level (AAL). This\\nallows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password,\\nthe AAL is 1. If you wish to \\\"upgrade\\\" the session's security by asking the user to perform TOTP / WebAuth/ ...\\nyou would set this to \\\"aal2\\\".\",\n            \"name\": \"aal\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The Session Token of the Identity performing the settings flow.\",\n            \"name\": \"X-Session-Token\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"boolean\",\n            \"description\": \"EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token\\nafter the login flow has been completed.\",\n            \"name\": \"return_session_token_exchange_code\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"An optional organization ID that should be used for logging this user in.\\nThis parameter is only effective in the Ory Network.\",\n            \"name\": \"organization\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Via should contain the identity's credential the code should be sent to. Only relevant in aal2 flows.\\n\\nDEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice\\nof MFA credentials to choose from to perform the second factor instead.\",\n            \"name\": \"via\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"An optional identity schema to use for the login flow.\",\n            \"name\": \"identity_schema\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"loginFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/loginFlow\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/login/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user login flow. This endpoint will set the appropriate\\ncookies and anti-CSRF measures required for browser-based flows.\\n\\nIf this endpoint is opened as a link in the browser, it will be redirected to\\n`selfservice.flows.login.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists already, the browser will be redirected to `urls.default_redirect_url` unless the query parameter\\n`?refresh=true` was set.\\n\\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`session_aal1_required`: Multi-factor auth (e.g. 2fa) was requested but the user has no session yet.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n\\nThe optional query parameter login_challenge is set when using Kratos with\\nHydra in an OAuth2 flow. See the oauth2_provider.url configuration\\noption.\\n\\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Login Flow for Browsers\",\n        \"operationId\": \"createBrowserLoginFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"boolean\",\n            \"description\": \"Refresh a login session\\n\\nIf set to true, this will refresh an existing login session by\\nasking the user to sign in again. This will reset the\\nauthenticated_at time of the session.\",\n            \"name\": \"refresh\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Request a Specific AuthenticationMethod Assurance Level\\n\\nUse this parameter to upgrade an existing session's authenticator assurance level (AAL). This\\nallows you to ask for multi-factor authentication. When an identity sign in using e.g. username+password,\\nthe AAL is 1. If you wish to \\\"upgrade\\\" the session's security by asking the user to perform TOTP / WebAuth/ ...\\nyou would set this to \\\"aal2\\\".\",\n            \"name\": \"aal\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"An optional Hydra login challenge. If present, Kratos will cooperate with\\nOry Hydra to act as an OAuth2 identity provider.\\n\\nThe value for this parameter comes from `login_challenge` URL Query parameter sent to your\\napplication (e.g. `/login?login_challenge=abcde`).\",\n            \"name\": \"login_challenge\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"An optional organization ID that should be used for logging this user in.\\nThis parameter is only effective in the Ory Network.\",\n            \"name\": \"organization\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Via should contain the identity's credential the code should be sent to. Only relevant in aal2 flows.\\n\\nDEPRECATED: This field is deprecated. Please remove it from your requests. The user will now see a choice\\nof MFA credentials to choose from to perform the second factor instead.\",\n            \"name\": \"via\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"An optional identity schema to use for the login flow.\",\n            \"name\": \"identity_schema\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"loginFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/loginFlow\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/login/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a login flow's context with, for example, error details and other information.\\n\\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\\n\\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\\nand you need to forward the incoming HTTP Cookie header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/login', async function (req, res) {\\nconst flow = await client.getLoginFlow(req.header('cookie'), req.query['flow'])\\n\\nres.render('login', flow)\\n})\\n```\\n\\nThis request may fail due to several reasons. The `error.id` can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`self_service_flow_expired`: The flow is expired and you should request a new one.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Get Login Flow\",\n        \"operationId\": \"getLoginFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Login Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/login?flow=abcde`).\",\n            \"name\": \"id\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"loginFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/loginFlow\"\n            }\n          },\n          \"403\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/logout\": {\n      \"get\": {\n        \"description\": \"This endpoint logs out an identity in a self-service manner.\\n\\nIf the `Accept` HTTP header is not set to `application/json`, the browser will be redirected (HTTP 303 See Other)\\nto the `return_to` parameter of the initial request or fall back to `urls.default_return_to`.\\n\\nIf the `Accept` HTTP header is set to `application/json`, a 204 No Content response\\nwill be sent on successful logout instead.\\n\\nThis endpoint is NOT INTENDED for API clients and only works\\nwith browsers (Chrome, Firefox, ...). For API clients you can\\ncall the `/self-service/logout/api` URL directly with the Ory Session Token.\\n\\nMore information can be found at [Ory Kratos User Logout Documentation](https://www.ory.sh/docs/next/kratos/self-service/flows/user-logout).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Update Logout Flow\",\n        \"operationId\": \"updateLogoutFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"A Valid Logout Token\\n\\nIf you do not have a logout token because you only have a session cookie,\\ncall `/self-service/logout/browser` to generate a URL for this endpoint.\",\n            \"name\": \"token\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The URL to return to after the logout was completed.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/logout/api\": {\n      \"delete\": {\n        \"description\": \"Use this endpoint to log out an identity using an Ory Session Token. If the Ory Session Token was successfully\\nrevoked, the server returns a 204 No Content response. A 204 No Content response is also sent when\\nthe Ory Session Token has been revoked already before.\\n\\nIf the Ory Session Token is malformed or does not exist a 403 Forbidden response will be returned.\\n\\nThis endpoint does not remove any HTTP\\nCookies - use the Browser-Based Self-Service Logout Flow instead.\",\n        \"consumes\": [\n          \"application/json\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Perform Logout for Native Apps\",\n        \"operationId\": \"performNativeLogout\",\n        \"parameters\": [\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"required\": true,\n            \"schema\": {\n              \"$ref\": \"#/definitions/performNativeLogoutBody\"\n            }\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/logout/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user logout flow and a URL which can be used to log out the user.\\n\\nThis endpoint is NOT INTENDED for API clients and only works\\nwith browsers (Chrome, Firefox, ...). For API clients you can\\ncall the `/self-service/logout/api` URL directly with the Ory Session Token.\\n\\nThe URL is only valid for the currently signed in user. If no user is signed in, this endpoint returns\\na 401 error.\\n\\nWhen calling this endpoint from a backend, please ensure to properly forward the HTTP cookies.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create a Logout URL for Browsers\",\n        \"operationId\": \"createBrowserLogoutFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nIf you call this endpoint from a backend, please include the\\noriginal Cookie header in the request.\",\n            \"name\": \"cookie\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Return to URL\\n\\nThe URL to which the browser should be redirected to after the logout\\nhas been performed.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"logoutFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/logoutFlow\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"500\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/recovery\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to update a recovery flow. This endpoint\\nbehaves differently for API and browser flows and has several states:\\n\\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\\nand works with API- and Browser-initiated flows.\\nFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid.\\nand a HTTP 303 See Other redirect with a fresh recovery flow if the flow was otherwise invalid (e.g. expired).\\nFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Recovery UI URL with the Recovery Flow ID appended.\\n`sent_email` is the success state after `choose_method` for the `link` method and allows the user to request another recovery email. It\\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\\\"sending a recovery link\\\")\\ndoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\\n(if the link was valid) and instructs the user to update their password, or a redirect to the Recover UI URL with\\na new Recovery Flow ID which contains an error message that the recovery link was invalid.\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\",\n        \"consumes\": [\n          \"application/json\",\n          \"application/x-www-form-urlencoded\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Update Recovery Flow\",\n        \"operationId\": \"updateRecoveryFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Recovery Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/recovery?flow=abcde`).\",\n            \"name\": \"flow\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Recovery Token\\n\\nThe recovery token which completes the recovery request. If the token\\nis invalid (e.g. expired) an error will be shown to the end-user.\\n\\nThis parameter is usually set in a link and not used by any direct API call.\",\n            \"name\": \"token\",\n            \"in\": \"query\"\n          },\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"required\": true,\n            \"schema\": {\n              \"$ref\": \"#/definitions/updateRecoveryFlowBody\"\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"recoveryFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/recoveryFlow\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"recoveryFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/recoveryFlow\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"422\": {\n            \"description\": \"errorBrowserLocationChangeRequired\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorBrowserLocationChangeRequired\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/recovery/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a recovery flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error.\\n\\nOn an existing recovery flow, use the `getRecoveryFlow` API endpoint.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Recovery Flow for Native Apps\",\n        \"operationId\": \"createNativeRecoveryFlow\",\n        \"responses\": {\n          \"200\": {\n            \"description\": \"recoveryFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/recoveryFlow\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/recovery/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based account recovery flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.recovery.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists, the browser is returned to the configured return URL.\\n\\nIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects\\nor a 400 bad request error if the user is already authenticated.\\n\\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Recovery Flow for Browsers\",\n        \"operationId\": \"createBrowserRecoveryFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"recoveryFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/recoveryFlow\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/recovery/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a recovery flow's context with, for example, error details and other information.\\n\\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\\n\\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\\nand you need to forward the incoming HTTP Cookie header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/recovery', async function (req, res) {\\nconst flow = await client.getRecoveryFlow(req.header('Cookie'), req.query['flow'])\\n\\nres.render('recovery', flow)\\n})\\n```\\n\\nMore information can be found at [Ory Kratos Account Recovery Documentation](../self-service/flows/account-recovery).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Get Recovery Flow\",\n        \"operationId\": \"getRecoveryFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Flow ID\\n\\nThe value for this parameter comes from `request` URL Query parameter sent to your\\napplication (e.g. `/recovery?flow=abcde`).\",\n            \"name\": \"id\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"recoveryFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/recoveryFlow\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/registration\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a registration flow by sending an identity's traits and password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and a application/json body with the created identity success - if the session hook is configured the\\n`session` and `session_token` will also be included;\\nHTTP 410 if the original flow expired with the appropriate error messages set and optionally a `use_flow_id` parameter in the body;\\nHTTP 400 on form validation errors.\\n\\nBrowser flows expect a Content-Type of `application/x-www-form-urlencoded` or `application/json` to be sent in the body and respond with\\na HTTP 303 redirect to the post/after registration URL or the `return_to` value if it was set and if the registration succeeded;\\na HTTP 303 redirect to the registration UI URL with the flow ID containing the validation errors otherwise.\\n\\nBrowser flows with an accept header of `application/json` will not redirect but instead respond with\\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\n\\nIf this endpoint is called with `Accept: application/json` in the header, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\\nMost likely used in Social Sign In flows.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"consumes\": [\n          \"application/json\",\n          \"application/x-www-form-urlencoded\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Update Registration Flow\",\n        \"operationId\": \"updateRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Registration Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/registration?flow=abcde`).\",\n            \"name\": \"flow\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"required\": true,\n            \"schema\": {\n              \"$ref\": \"#/definitions/updateRegistrationFlowBody\"\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"successfulNativeRegistration\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/successfulNativeRegistration\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"registrationFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/registrationFlow\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"422\": {\n            \"description\": \"errorBrowserLocationChangeRequired\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorBrowserLocationChangeRequired\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/registration/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a registration flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nIf a valid provided session cookie or session token is provided, a 400 Bad Request error\\nwill be returned unless the URL query parameter `?refresh=true` is set.\\n\\nTo fetch an existing registration flow call `/self-service/registration/flows?flow=\\u003cflow_id\\u003e`.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nIn the case of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Registration Flow for Native Apps\",\n        \"operationId\": \"createNativeRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"boolean\",\n            \"description\": \"EnableSessionTokenExchangeCode requests the login flow to include a code that can be used to retrieve the session token\\nafter the login flow has been completed.\",\n            \"name\": \"return_session_token_exchange_code\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"An optional organization ID that should be used to register this user.\\nThis parameter is only effective in the Ory Network.\",\n            \"name\": \"organization\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"An optional identity schema to use for the registration flow.\",\n            \"name\": \"identity_schema\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"registrationFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/registrationFlow\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/registration/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user registration flow. This endpoint will set the appropriate\\ncookies and anti-CSRF measures required for browser-based flows.\\n\\nIf this endpoint is opened as a link in the browser, it will be redirected to\\n`selfservice.flows.registration.ui_url` with the flow ID set as the query parameter `?flow=`. If a valid user session\\nexists already, the browser will be redirected to `urls.default_redirect_url`.\\n\\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n\\nIf this endpoint is called via an AJAX request, the response contains the registration flow without a redirect.\\n\\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Registration Flow for Browsers\",\n        \"operationId\": \"createBrowserRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Ory OAuth 2.0 Login Challenge.\\n\\nIf set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\\n\\nThe value for this parameter comes from `login_challenge` URL Query parameter sent to your\\napplication (e.g. `/registration?login_challenge=abcde`).\\n\\nThis feature is compatible with Ory Hydra when not running on the Ory Network.\",\n            \"name\": \"login_challenge\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The URL to return the browser to after the verification flow was completed.\\n\\nAfter the registration flow is completed, the user will be sent a verification email.\\nUpon completing the verification flow, this URL will be used to override the default\\n`selfservice.flows.verification.after.default_redirect_to` value.\",\n            \"name\": \"after_verification_return_to\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"An optional organization ID that should be used to register this user.\\nThis parameter is only effective in the Ory Network.\",\n            \"name\": \"organization\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"An optional identity schema to use for the registration flow.\",\n            \"name\": \"identity_schema\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"registrationFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/registrationFlow\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/registration/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a registration flow's context with, for example, error details and other information.\\n\\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\\n\\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\\nand you need to forward the incoming HTTP Cookie header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/registration', async function (req, res) {\\nconst flow = await client.getRegistrationFlow(req.header('cookie'), req.query['flow'])\\n\\nres.render('registration', flow)\\n})\\n```\\n\\nThis request may fail due to several reasons. The `error.id` can be one of:\\n\\n`session_already_available`: The user is already signed in.\\n`self_service_flow_expired`: The flow is expired and you should request a new one.\\n\\nMore information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Get Registration Flow\",\n        \"operationId\": \"getRegistrationFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Registration Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/registration?flow=abcde`).\",\n            \"name\": \"id\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"registrationFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/registrationFlow\"\n            }\n          },\n          \"403\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/settings\": {\n      \"post\": {\n        \"security\": [\n          {\n            \"sessionToken\": []\n          }\n        ],\n        \"description\": \"Use this endpoint to complete a settings flow by sending an identity's updated password. This endpoint\\nbehaves differently for API and browser flows.\\n\\nAPI-initiated flows expect `application/json` to be sent in the body and respond with\\nHTTP 200 and an application/json body with the session token on success;\\nHTTP 303 redirect to a fresh settings flow if the original flow expired with the appropriate error messages set;\\nHTTP 400 on form validation errors.\\nHTTP 401 when the endpoint is called without a valid session token.\\nHTTP 403 when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\\nImplies that the user needs to re-authenticate.\\n\\nBrowser flows without HTTP Header `Accept` or with `Accept: text/*` respond with\\na HTTP 303 redirect to the post/after settings URL or the `return_to` value if it was set and if the flow succeeded;\\na HTTP 303 redirect to the Settings UI URL with the flow ID containing the validation errors otherwise.\\na HTTP 303 redirect to the login endpoint when `selfservice.flows.settings.privileged_session_max_age` was reached or the session's AAL is too low.\\n\\nBrowser flows with HTTP Header `Accept: application/json` respond with\\nHTTP 200 and a application/json body with the signed in identity and a `Set-Cookie` header on success;\\nHTTP 303 redirect to a fresh login flow if the original flow expired with the appropriate error messages set;\\nHTTP 401 when the endpoint is called without a valid session cookie.\\nHTTP 403 when the page is accessed without a session cookie or the session's AAL is too low.\\nHTTP 400 on form validation errors.\\n\\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\\n\\nIf this endpoint is called with a `Accept: application/json` HTTP header, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`session_refresh_required`: The identity requested to change something that needs a privileged session. Redirect\\nthe identity to the login init endpoint with query parameters `?refresh=true\\u0026return_to=\\u003cthe-current-browser-url\\u003e`,\\nor initiate a refresh login flow otherwise.\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`session_inactive`: No Ory Session was found - sign in a user first.\\n`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\\nidentity logged in instead.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n`browser_location_change_required`: Usually sent when an AJAX request indicates that the browser needs to open a specific URL.\\nMost likely used in Social Sign In flows.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"consumes\": [\n          \"application/json\",\n          \"application/x-www-form-urlencoded\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Complete Settings Flow\",\n        \"operationId\": \"updateSettingsFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Settings Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/settings?flow=abcde`).\",\n            \"name\": \"flow\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"required\": true,\n            \"schema\": {\n              \"$ref\": \"#/definitions/updateSettingsFlowBody\"\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The Session Token of the Identity performing the settings flow.\",\n            \"name\": \"X-Session-Token\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"settingsFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/settingsFlow\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"settingsFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/settingsFlow\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"403\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"422\": {\n            \"description\": \"errorBrowserLocationChangeRequired\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorBrowserLocationChangeRequired\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/settings/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a settings flow for API clients such as mobile devices, smart TVs, and so on.\\nYou must provide a valid Ory Kratos Session Token for this endpoint to respond with HTTP 200 OK.\\n\\nTo fetch an existing settings flow call `/self-service/settings/flows?flow=\\u003cflow_id\\u003e`.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor or change the configuration.\\n\\nIn the case of an error, the `error.id` of the JSON response body can be one of:\\n\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`session_inactive`: No Ory Session was found - sign in a user first.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Settings Flow for Native Apps\",\n        \"operationId\": \"createNativeSettingsFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Session Token of the Identity performing the settings flow.\",\n            \"name\": \"X-Session-Token\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"settingsFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/settingsFlow\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/settings/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based user settings flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid\\nOry Kratos Session Cookie is included in the request, a login flow will be initialized.\\n\\nIf this endpoint is opened as a link in the browser, it will be redirected to\\n`selfservice.flows.settings.ui_url` with the flow ID set as the query parameter `?flow=`. If no valid user session\\nwas set, the browser will be redirected to the login endpoint.\\n\\nIf this endpoint is called via an AJAX request, the response contains the settings flow without any redirects\\nor a 401 forbidden error if no valid session was set.\\n\\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor (happens automatically for server-side browser flows) or change the configuration.\\n\\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`session_inactive`: No Ory Session was found - sign in a user first.\\n`security_identity_mismatch`: The requested `?return_to` address is not allowed to be used. Adjust this in the configuration!\\n\\nThis endpoint is NOT INTENDED for clients that do not have a browser (Chrome, Firefox, ...) as cookies are needed.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Settings Flow for Browsers\",\n        \"operationId\": \"createBrowserSettingsFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"settingsFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/settingsFlow\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"403\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/settings/flows\": {\n      \"get\": {\n        \"description\": \"When accessing this endpoint through Ory Kratos' Public API you must ensure that either the Ory Kratos Session Cookie\\nor the Ory Kratos Session Token are set.\\n\\nDepending on your configuration this endpoint might return a 403 error if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor or change the configuration.\\n\\nYou can access this endpoint without credentials when using Ory Kratos' Admin API.\\n\\nIf this endpoint is called via an AJAX request, the response contains the flow without a redirect. In the\\ncase of an error, the `error.id` of the JSON response body can be one of:\\n\\n`security_csrf_violation`: Unable to fetch the flow because a CSRF violation occurred.\\n`session_inactive`: No Ory Session was found - sign in a user first.\\n`security_identity_mismatch`: The flow was interrupted with `session_refresh_required` but apparently some other\\nidentity logged in instead.\\n\\nMore information can be found at [Ory Kratos User Settings \\u0026 Profile Management Documentation](../self-service/flows/user-settings).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Get Settings Flow\",\n        \"operationId\": \"getSettingsFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID is the Settings Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/settings?flow=abcde`).\",\n            \"name\": \"id\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The Session Token\\n\\nWhen using the SDK in an app without a browser, please include the\\nsession token here.\",\n            \"name\": \"X-Session-Token\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"settingsFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/settingsFlow\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"403\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/self-service/verification\": {\n      \"post\": {\n        \"description\": \"Use this endpoint to complete a verification flow. This endpoint\\nbehaves differently for API and browser flows and has several states:\\n\\n`choose_method` expects `flow` (in the URL query) and `email` (in the body) to be sent\\nand works with API- and Browser-initiated flows.\\nFor API clients and Browser clients with HTTP Header `Accept: application/json` it either returns a HTTP 200 OK when the form is valid and HTTP 400 OK when the form is invalid\\nand a HTTP 303 See Other redirect with a fresh verification flow if the flow was otherwise invalid (e.g. expired).\\nFor Browser clients without HTTP Header `Accept` or with `Accept: text/*` it returns a HTTP 303 See Other redirect to the Verification UI URL with the Verification Flow ID appended.\\n`sent_email` is the success state after `choose_method` when using the `link` method and allows the user to request another verification email. It\\nworks for both API and Browser-initiated flows and returns the same responses as the flow in `choose_method` state.\\n`passed_challenge` expects a `token` to be sent in the URL query and given the nature of the flow (\\\"sending a verification link\\\")\\ndoes not have any API capabilities. The server responds with a HTTP 303 See Other redirect either to the Settings UI URL\\n(if the link was valid) and instructs the user to update their password, or a redirect to the Verification UI URL with\\na new Verification Flow ID which contains an error message that the verification link was invalid.\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"consumes\": [\n          \"application/json\",\n          \"application/x-www-form-urlencoded\"\n        ],\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Complete Verification Flow\",\n        \"operationId\": \"updateVerificationFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Verification Flow ID\\n\\nThe value for this parameter comes from `flow` URL Query parameter sent to your\\napplication (e.g. `/verification?flow=abcde`).\",\n            \"name\": \"flow\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Verification Token\\n\\nThe verification token which completes the verification request. If the token\\nis invalid (e.g. expired) an error will be shown to the end-user.\\n\\nThis parameter is usually set in a link and not used by any direct API call.\",\n            \"name\": \"token\",\n            \"in\": \"query\"\n          },\n          {\n            \"name\": \"Body\",\n            \"in\": \"body\",\n            \"required\": true,\n            \"schema\": {\n              \"$ref\": \"#/definitions/updateVerificationFlowBody\"\n            }\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK in a browser app, on the server side you must include the HTTP Cookie Header\\nsent by the client to your server here. This ensures that CSRF and session cookies are respected.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"verificationFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/verificationFlow\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"verificationFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/verificationFlow\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/self-service/verification/api\": {\n      \"get\": {\n        \"description\": \"This endpoint initiates a verification flow for API clients such as mobile devices, smart TVs, and so on.\\n\\nTo fetch an existing verification flow call `/self-service/verification/flows?flow=\\u003cflow_id\\u003e`.\\n\\nYou MUST NOT use this endpoint in client-side (Single Page Apps, ReactJS, AngularJS) nor server-side (Java Server\\nPages, NodeJS, PHP, Golang, ...) browser applications. Using this endpoint in these applications will make\\nyou vulnerable to a variety of CSRF attacks.\\n\\nThis endpoint MUST ONLY be used in scenarios such as native mobile apps (React Native, Objective C, Swift, Java, ...).\\n\\nMore information can be found at [Ory Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Verification Flow for Native Apps\",\n        \"operationId\": \"createNativeVerificationFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"A URL contained in the return_to key of the verification flow.\\nThis piece of data has no effect on the actual logic of the flow and is purely informational.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"verificationFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/verificationFlow\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/verification/browser\": {\n      \"get\": {\n        \"description\": \"This endpoint initializes a browser-based account verification flow. Once initialized, the browser will be redirected to\\n`selfservice.flows.verification.ui_url` with the flow ID set as the query parameter `?flow=`.\\n\\nIf this endpoint is called via an AJAX request, the response contains the recovery flow without any redirects.\\n\\nThis endpoint is NOT INTENDED for API clients and only works with browsers (Chrome, Firefox, ...).\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Create Verification Flow for Browser Clients\",\n        \"operationId\": \"createBrowserVerificationFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The URL to return the browser to after the flow was completed.\",\n            \"name\": \"return_to\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"verificationFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/verificationFlow\"\n            }\n          },\n          \"303\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/self-service/verification/flows\": {\n      \"get\": {\n        \"description\": \"This endpoint returns a verification flow's context with, for example, error details and other information.\\n\\nBrowser flows expect the anti-CSRF cookie to be included in the request's HTTP Cookie Header.\\nFor AJAX requests you must ensure that cookies are included in the request or requests will fail.\\n\\nIf you use the browser-flow for server-side apps, the services need to run on a common top-level-domain\\nand you need to forward the incoming HTTP Cookie header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/recovery', async function (req, res) {\\nconst flow = await client.getVerificationFlow(req.header('cookie'), req.query['flow'])\\n\\nres.render('verification', flow)\\n})\\n```\\n\\nMore information can be found at [Ory Kratos Email and Phone Verification Documentation](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Get Verification Flow\",\n        \"operationId\": \"getVerificationFlow\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The Flow ID\\n\\nThe value for this parameter comes from `request` URL Query parameter sent to your\\napplication (e.g. `/verification?flow=abcde`).\",\n            \"name\": \"id\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"HTTP Cookies\\n\\nWhen using the SDK on the server side you must include the HTTP Cookie Header\\noriginally sent to your HTTP handler here.\",\n            \"name\": \"cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"verificationFlow\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/verificationFlow\"\n            }\n          },\n          \"403\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/sessions\": {\n      \"get\": {\n        \"description\": \"This endpoints returns all other active sessions that belong to the logged-in user.\\nThe current session can be retrieved by calling the `/sessions/whoami` endpoint.\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Get My Active Sessions\",\n        \"operationId\": \"listMySessions\",\n        \"parameters\": [\n          {\n            \"maximum\": 1000,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Deprecated Items per Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis is the number of items per page.\",\n            \"name\": \"per_page\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"description\": \"Deprecated Pagination Page\\n\\nDEPRECATED: Please use `page_token` instead. This parameter will be removed in the future.\\n\\nThis value is currently an integer, but it is not sequential. The value is not the page number, but a\\nreference. The next page can be any number and some numbers might return an empty list.\\n\\nFor example, page 2 might not follow after page 1. And even if page 3 and 5 exist, but page 4 might not exist.\\nThe first page can be retrieved by omitting this parameter. Following page pointers will be returned in the\\n`Link` header.\",\n            \"name\": \"page\",\n            \"in\": \"query\"\n          },\n          {\n            \"maximum\": 500,\n            \"minimum\": 1,\n            \"type\": \"integer\",\n            \"format\": \"int64\",\n            \"default\": 250,\n            \"description\": \"Page Size\\n\\nThis is the number of items per page to return. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_size\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Next Page Token\\n\\nThe next page token. For details on pagination please head over to the\\n[pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n            \"name\": \"page_token\",\n            \"in\": \"query\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\",\n            \"name\": \"X-Session-Token\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\\nscenario you must include the HTTP Cookie Header which originally was included in the request to your server.\\nAn example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\\n\\nIt is ok if more than one cookie are included here as all other cookies will be ignored.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"$ref\": \"#/responses/listMySessions\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      },\n      \"delete\": {\n        \"description\": \"Calling this endpoint invalidates all except the current session that belong to the logged-in user.\\nSession data are not deleted.\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Disable my other sessions\",\n        \"operationId\": \"disableMyOtherSessions\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\",\n            \"name\": \"X-Session-Token\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\\nscenario you must include the HTTP Cookie Header which originally was included in the request to your server.\\nAn example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\\n\\nIt is ok if more than one cookie are included here as all other cookies will be ignored.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"deleteMySessionsCount\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/deleteMySessionsCount\"\n            }\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/sessions/token-exchange\": {\n      \"get\": {\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Exchange Session Token\",\n        \"operationId\": \"exchangeSessionToken\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"The part of the code return when initializing the flow.\",\n            \"name\": \"init_code\",\n            \"in\": \"query\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"The part of the code returned by the return_to URL.\",\n            \"name\": \"return_to_code\",\n            \"in\": \"query\",\n            \"required\": true\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"successfulNativeLogin\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/successfulNativeLogin\"\n            }\n          },\n          \"403\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"404\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"410\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-medium\"\n      }\n    },\n    \"/sessions/whoami\": {\n      \"get\": {\n        \"description\": \"Uses the HTTP Headers in the GET request to determine (e.g. by using checking the cookies) who is authenticated.\\nReturns a session object in the body or 401 if the credentials are invalid or no credentials were sent.\\nWhen the request it successful it adds the user ID to the 'X-Kratos-Authenticated-Identity-Id' header\\nin the response.\\n\\nIf you call this endpoint from a server-side application, you must forward the HTTP Cookie Header to this endpoint:\\n\\n```js\\npseudo-code example\\nrouter.get('/protected-endpoint', async function (req, res) {\\nconst session = await client.toSession(undefined, req.header('cookie'))\\n\\nconsole.log(session)\\n})\\n```\\n\\nWhen calling this endpoint from a non-browser application (e.g. mobile app) you must include the session token:\\n\\n```js\\npseudo-code example\\n...\\nconst session = await client.toSession(\\\"the-session-token\\\")\\n\\nconsole.log(session)\\n```\\n\\nWhen using a token template, the token is included in the `tokenized` field of the session.\\n\\n```js\\npseudo-code example\\n...\\nconst session = await client.toSession(\\\"the-session-token\\\", { tokenize_as: \\\"example-jwt-template\\\" })\\n\\nconsole.log(session.tokenized) // The JWT\\n```\\n\\nDepending on your configuration this endpoint might return a 403 status code if the session has a lower Authenticator\\nAssurance Level (AAL) than is possible for the identity. This can happen if the identity has password + webauthn\\ncredentials (which would result in AAL2) but the session has only AAL1. If this error occurs, ask the user\\nto sign in with the second factor or change the configuration.\\n\\nThis endpoint is useful for:\\n\\nAJAX calls. Remember to send credentials and set up CORS correctly!\\nReverse proxies and API Gateways\\nServer-side calls - use the `X-Session-Token` header!\\n\\nThis endpoint authenticates users by checking:\\n\\nif the `Cookie` HTTP header was set containing an Ory Kratos Session Cookie;\\nif the `Authorization: bearer \\u003cory-session-token\\u003e` HTTP header was set with a valid Ory Kratos Session Token;\\nif the `X-Session-Token` HTTP header was set with a valid Ory Kratos Session Token.\\n\\nIf none of these headers are set or the cookie or token are invalid, the endpoint returns a HTTP 401 status code.\\n\\nAs explained above, this request may fail due to several reasons. The `error.id` can be one of:\\n\\n`session_inactive`: No active session was found in the request (e.g. no Ory Session Cookie / Ory Session Token).\\n`session_aal2_required`: An active session was found but it does not fulfil the Authenticator Assurance Level, implying that the session must (e.g.) authenticate the second factor.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Check Who the Current HTTP Session Belongs To\",\n        \"operationId\": \"toSession\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\",\n            \"name\": \"X-Session-Token\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\\nscenario you must include the HTTP Cookie Header which originally was included in the request to your server.\\nAn example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\\n\\nIt is ok if more than one cookie are included here as all other cookies will be ignored.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Returns the session additionally as a token (such as a JWT)\\n\\nThe value of this parameter has to be a valid, configured Ory Session token template. For more information head over to [the documentation](http://ory.sh/docs/identities/session-to-jwt-cors).\",\n            \"name\": \"tokenize_as\",\n            \"in\": \"query\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"session\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/session\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"403\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-low\"\n      }\n    },\n    \"/sessions/{id}\": {\n      \"delete\": {\n        \"description\": \"Calling this endpoint invalidates the specified session. The current session cannot be revoked.\\nSession data are not deleted.\",\n        \"schemes\": [\n          \"http\",\n          \"https\"\n        ],\n        \"tags\": [\n          \"frontend\"\n        ],\n        \"summary\": \"Disable one of my sessions\",\n        \"operationId\": \"disableMySession\",\n        \"parameters\": [\n          {\n            \"type\": \"string\",\n            \"description\": \"ID is the session's ID.\",\n            \"name\": \"id\",\n            \"in\": \"path\",\n            \"required\": true\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Set the Session Token when calling from non-browser clients. A session token has a format of `MP2YWEMeM8MxjkGKpH4dqOQ4Q4DlSPaj`.\",\n            \"name\": \"X-Session-Token\",\n            \"in\": \"header\"\n          },\n          {\n            \"type\": \"string\",\n            \"description\": \"Set the Cookie Header. This is especially useful when calling this endpoint from a server-side application. In that\\nscenario you must include the HTTP Cookie Header which originally was included in the request to your server.\\nAn example of a session in the HTTP Cookie Header is: `ory_kratos_session=a19iOVAbdzdgl70Rq1QZmrKmcjDtdsviCTZx7m9a9yHIUS8Wa9T7hvqyGTsLHi6Qifn2WUfpAKx9DWp0SJGleIn9vh2YF4A16id93kXFTgIgmwIOvbVAScyrx7yVl6bPZnCx27ec4WQDtaTewC1CpgudeDV2jQQnSaCP6ny3xa8qLH-QUgYqdQuoA_LF1phxgRCUfIrCLQOkolX5nv3ze_f==`.\\n\\nIt is ok if more than one cookie are included here as all other cookies will be ignored.\",\n            \"name\": \"Cookie\",\n            \"in\": \"header\"\n          }\n        ],\n        \"responses\": {\n          \"204\": {\n            \"$ref\": \"#/responses/emptyResponse\"\n          },\n          \"400\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"401\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          },\n          \"default\": {\n            \"description\": \"errorGeneric\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/errorGeneric\"\n            }\n          }\n        },\n        \"x-ory-ratelimit-bucket\": \"kratos-public-high\"\n      }\n    },\n    \"/version\": {\n      \"get\": {\n        \"description\": \"This endpoint returns the service version typically notated using semantic versioning.\\n\\nIf the service supports TLS Edge Termination, this endpoint does not require the\\n`X-Forwarded-Proto` header to be set.\\n\\nBe aware that if you are running multiple nodes of this service, the health status will never\\nrefer to the cluster state, only to a single instance.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"tags\": [\n          \"version\"\n        ],\n        \"summary\": \"Get service version\",\n        \"operationId\": \"getVersion\",\n        \"responses\": {\n          \"200\": {\n            \"description\": \"version\",\n            \"schema\": {\n              \"$ref\": \"#/definitions/version\"\n            }\n          }\n        }\n      }\n    }\n  },\n  \"definitions\": {\n    \"CodeChannel\": {\n      \"type\": \"string\"\n    },\n    \"DefaultError\": {},\n    \"Duration\": {\n      \"description\": \"A Duration represents the elapsed time between two instants\\nas an int64 nanosecond count. The representation limits the\\nlargest representable duration to approximately 290 years.\",\n      \"type\": \"integer\",\n      \"format\": \"int64\"\n    },\n    \"ID\": {\n      \"type\": \"integer\",\n      \"format\": \"int64\"\n    },\n    \"JSONRawMessage\": {\n      \"type\": \"object\",\n      \"title\": \"JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger.\"\n    },\n    \"NullUUID\": {\n      \"description\": \"NullUUID can be used with the standard sql package to represent a\\nUUID value that can be NULL in the database.\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"UUID\": {\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"Valid\": {\n          \"type\": \"boolean\"\n        }\n      }\n    },\n    \"OAuth2Client\": {\n      \"type\": \"object\",\n      \"title\": \"OAuth2Client OAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.\",\n      \"properties\": {\n        \"AdditionalProperties\": {\n          \"type\": \"object\",\n          \"additionalProperties\": {}\n        },\n        \"access_token_strategy\": {\n          \"description\": \"OAuth 2.0 Access Token Strategy  AccessTokenStrategy is the strategy used to generate access tokens. Valid options are `jwt` and `opaque`. `jwt` is a bad idea, see https://www.ory.sh/docs/hydra/advanced#json-web-tokens Setting the stragegy here overrides the global setting in `strategies.access_token`.\",\n          \"type\": \"string\"\n        },\n        \"allowed_cors_origins\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"audience\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"authorization_code_grant_access_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"authorization_code_grant_id_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"authorization_code_grant_refresh_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"backchannel_logout_session_required\": {\n          \"description\": \"OpenID Connect Back-Channel Logout Session Required  Boolean value specifying whether the RP requires that a sid (session ID) Claim be included in the Logout Token to identify the RP session with the OP when the backchannel_logout_uri is used. If omitted, the default value is false.\",\n          \"type\": \"boolean\"\n        },\n        \"backchannel_logout_uri\": {\n          \"description\": \"OpenID Connect Back-Channel Logout URI  RP URL that will cause the RP to log itself out when sent a Logout Token by the OP.\",\n          \"type\": \"string\"\n        },\n        \"client_credentials_grant_access_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"client_id\": {\n          \"description\": \"OAuth 2.0 Client ID  The ID is immutable. If no ID is provided, a UUID4 will be generated.\",\n          \"type\": \"string\"\n        },\n        \"client_name\": {\n          \"description\": \"OAuth 2.0 Client Name  The human-readable name of the client to be presented to the end-user during authorization.\",\n          \"type\": \"string\"\n        },\n        \"client_secret\": {\n          \"description\": \"OAuth 2.0 Client Secret  The secret will be included in the create request as cleartext, and then never again. The secret is kept in hashed format and is not recoverable once lost.\",\n          \"type\": \"string\"\n        },\n        \"client_secret_expires_at\": {\n          \"description\": \"OAuth 2.0 Client Secret Expires At  The field is currently not supported and its value is always 0.\",\n          \"type\": \"integer\",\n          \"format\": \"int64\"\n        },\n        \"client_uri\": {\n          \"description\": \"OAuth 2.0 Client URI  ClientURI is a URL string of a web page providing information about the client. If present, the server SHOULD display this URL to the end-user in a clickable fashion.\",\n          \"type\": \"string\"\n        },\n        \"contacts\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"created_at\": {\n          \"description\": \"OAuth 2.0 Client Creation Date  CreatedAt returns the timestamp of the client's creation.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"frontchannel_logout_session_required\": {\n          \"description\": \"OpenID Connect Front-Channel Logout Session Required  Boolean value specifying whether the RP requires that iss (issuer) and sid (session ID) query parameters be included to identify the RP session with the OP when the frontchannel_logout_uri is used. If omitted, the default value is false.\",\n          \"type\": \"boolean\"\n        },\n        \"frontchannel_logout_uri\": {\n          \"description\": \"OpenID Connect Front-Channel Logout URI  RP URL that will cause the RP to log itself out when rendered in an iframe by the OP. An iss (issuer) query parameter and a sid (session ID) query parameter MAY be included by the OP to enable the RP to validate the request and to determine which of the potentially multiple sessions is to be logged out; if either is included, both MUST be.\",\n          \"type\": \"string\"\n        },\n        \"grant_types\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"implicit_grant_access_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"implicit_grant_id_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"jwks\": {\n          \"description\": \"OAuth 2.0 Client JSON Web Key Set  Client's JSON Web Key Set [JWK] document, passed by value. The semantics of the jwks parameter are the same as the jwks_uri parameter, other than that the JWK Set is passed by value, rather than by reference. This parameter is intended only to be used by Clients that, for some reason, are unable to use the jwks_uri parameter, for instance, by native applications that might not have a location to host the contents of the JWK Set. If a Client can use jwks_uri, it MUST NOT use jwks. One significant downside of jwks is that it does not enable key rotation (which jwks_uri does, as described in Section 10 of OpenID Connect Core 1.0 [OpenID.Core]). The jwks_uri and jwks parameters MUST NOT be used together.\"\n        },\n        \"jwks_uri\": {\n          \"description\": \"OAuth 2.0 Client JSON Web Key Set URL  URL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains the signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the Client's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing and encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both signatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.\",\n          \"type\": \"string\"\n        },\n        \"jwt_bearer_grant_access_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"logo_uri\": {\n          \"description\": \"OAuth 2.0 Client Logo URI  A URL string referencing the client's logo.\",\n          \"type\": \"string\"\n        },\n        \"metadata\": {\n        },\n        \"owner\": {\n          \"description\": \"OAuth 2.0 Client Owner  Owner is a string identifying the owner of the OAuth 2.0 Client.\",\n          \"type\": \"string\"\n        },\n        \"policy_uri\": {\n          \"description\": \"OAuth 2.0 Client Policy URI  PolicyURI is a URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data.\",\n          \"type\": \"string\"\n        },\n        \"post_logout_redirect_uris\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"redirect_uris\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"refresh_token_grant_access_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"refresh_token_grant_id_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"refresh_token_grant_refresh_token_lifespan\": {\n          \"description\": \"Specify a time duration in milliseconds, seconds, minutes, hours.\",\n          \"type\": \"string\"\n        },\n        \"registration_access_token\": {\n          \"description\": \"OpenID Connect Dynamic Client Registration Access Token  RegistrationAccessToken can be used to update, get, or delete the OAuth2 Client. It is sent when creating a client using Dynamic Client Registration.\",\n          \"type\": \"string\"\n        },\n        \"registration_client_uri\": {\n          \"description\": \"OpenID Connect Dynamic Client Registration URL  RegistrationClientURI is the URL used to update, get, or delete the OAuth2 Client.\",\n          \"type\": \"string\"\n        },\n        \"request_object_signing_alg\": {\n          \"description\": \"OpenID Connect Request Object Signing Algorithm  JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.\",\n          \"type\": \"string\"\n        },\n        \"request_uris\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"response_types\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"scope\": {\n          \"description\": \"OAuth 2.0 Client Scope  Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens.\",\n          \"type\": \"string\"\n        },\n        \"sector_identifier_uri\": {\n          \"description\": \"OpenID Connect Sector Identifier URI  URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values.\",\n          \"type\": \"string\"\n        },\n        \"skip_consent\": {\n          \"description\": \"SkipConsent skips the consent screen for this client. This field can only be set from the admin API.\",\n          \"type\": \"boolean\"\n        },\n        \"skip_logout_consent\": {\n          \"description\": \"SkipLogoutConsent skips the logout consent screen for this client. This field can only be set from the admin API.\",\n          \"type\": \"boolean\"\n        },\n        \"subject_type\": {\n          \"description\": \"OpenID Connect Subject Type  The `subject_types_supported` Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`.\",\n          \"type\": \"string\"\n        },\n        \"token_endpoint_auth_method\": {\n          \"description\": \"OAuth 2.0 Token Endpoint Authentication Method  Requested Client Authentication method for the Token Endpoint. The options are:  `client_secret_basic`: (default) Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` encoded in the HTTP Authorization header. `client_secret_post`: Send `client_id` and `client_secret` as `application/x-www-form-urlencoded` in the HTTP body. `private_key_jwt`: Use JSON Web Tokens to authenticate the client. `none`: Used for public clients (native apps, mobile apps) which can not have secrets.\",\n          \"type\": \"string\"\n        },\n        \"token_endpoint_auth_signing_alg\": {\n          \"description\": \"OAuth 2.0 Token Endpoint Signing Algorithm  Requested Client Authentication signing algorithm for the Token Endpoint.\",\n          \"type\": \"string\"\n        },\n        \"tos_uri\": {\n          \"description\": \"OAuth 2.0 Client Terms of Service URI  A URL string pointing to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client.\",\n          \"type\": \"string\"\n        },\n        \"updated_at\": {\n          \"description\": \"OAuth 2.0 Client Last Update Date  UpdatedAt returns the timestamp of the last update.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"userinfo_signed_response_alg\": {\n          \"description\": \"OpenID Connect Request Userinfo Signed Response Algorithm  JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"OAuth2ConsentRequestOpenIDConnectContext\": {\n      \"description\": \"OAuth2ConsentRequestOpenIDConnectContext struct for OAuth2ConsentRequestOpenIDConnectContext\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"AdditionalProperties\": {\n          \"type\": \"object\",\n          \"additionalProperties\": {}\n        },\n        \"acr_values\": {\n          \"description\": \"ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request. It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required.  OpenID Connect defines it as follows: \\u003e Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"display\": {\n          \"description\": \"Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are: page: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode. popup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over. touch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. wap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \\\\\\\"feature phone\\\\\\\" type display.  The Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.\",\n          \"type\": \"string\"\n        },\n        \"id_token_hint_claims\": {\n          \"description\": \"IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client.\",\n          \"type\": \"object\",\n          \"additionalProperties\": {}\n        },\n        \"login_hint\": {\n          \"description\": \"LoginHint hints about the login identifier the End-User might use to log in (if necessary). This hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier) and then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a phone number in the format specified for the phone_number Claim. The use of this parameter is optional.\",\n          \"type\": \"string\"\n        },\n        \"ui_locales\": {\n          \"description\": \"UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a space-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value \\\\\\\"fr-CA fr en\\\\\\\" represents a preference for French as spoken in Canada, then French (without a region designation), followed by English (without a region designation). An error SHOULD NOT result if some or all of the requested locales are not supported by the OpenID Provider.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        }\n      }\n    },\n    \"OAuth2LoginChallengeParams\": {\n      \"type\": \"object\"\n    },\n    \"OAuth2LoginRequest\": {\n      \"description\": \"OAuth2LoginRequest struct for OAuth2LoginRequest\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"AdditionalProperties\": {\n          \"type\": \"object\",\n          \"additionalProperties\": {}\n        },\n        \"challenge\": {\n          \"description\": \"ID is the identifier (\\\\\\\"login challenge\\\\\\\") of the login request. It is used to identify the session.\",\n          \"type\": \"string\"\n        },\n        \"client\": {\n          \"$ref\": \"#/definitions/OAuth2Client\"\n        },\n        \"oidc_context\": {\n          \"$ref\": \"#/definitions/OAuth2ConsentRequestOpenIDConnectContext\"\n        },\n        \"request_url\": {\n          \"description\": \"RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters.\",\n          \"type\": \"string\"\n        },\n        \"requested_access_token_audience\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"requested_scope\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"session_id\": {\n          \"description\": \"SessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag) this ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false) this will be a new random value. This value is used as the \\\\\\\"sid\\\\\\\" parameter in the ID Token and in OIDC Front-/Back- channel logout. It's value can generally be used to associate consecutive login requests by a certain user.\",\n          \"type\": \"string\"\n        },\n        \"skip\": {\n          \"description\": \"Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL.  This feature allows you to update / set session information.\",\n          \"type\": \"boolean\"\n        },\n        \"subject\": {\n          \"description\": \"Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"Provider\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"client_id\": {\n          \"description\": \"The RP's client identifier, issued by the IdP.\",\n          \"type\": \"string\"\n        },\n        \"config_url\": {\n          \"description\": \"A full path of the IdP config file.\",\n          \"type\": \"string\"\n        },\n        \"domain_hint\": {\n          \"description\": \"By specifying one of domain_hints values provided by the accounts endpoints,\\nthe FedCM dialog selectively shows the specified account.\",\n          \"type\": \"string\"\n        },\n        \"fields\": {\n          \"description\": \"Array of strings that specifies the user information (\\\"name\\\", \\\" email\\\",\\n\\\"picture\\\") that RP needs IdP to share with them.\\n\\nNote: Field API is supported by Chrome 132 and later.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"login_hint\": {\n          \"description\": \"By specifying one of login_hints values provided by the accounts endpoints,\\nthe FedCM dialog selectively shows the specified account.\",\n          \"type\": \"string\"\n        },\n        \"nonce\": {\n          \"description\": \"A random string to ensure the response is issued for this specific request.\\nPrevents replay attacks.\",\n          \"type\": \"string\"\n        },\n        \"parameters\": {\n          \"description\": \"Custom object that allows to specify additional key-value parameters:\\nscope: A string value containing additional permissions that RP needs to\\nrequest, for example \\\" drive.readonly calendar.readonly\\\"\\nnonce: A random string to ensure the response is issued for this specific\\nrequest. Prevents replay attacks.\\n\\nOther custom key-value parameters.\\n\\nNote: parameters is supported from Chrome 132.\",\n          \"type\": \"object\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          }\n        }\n      }\n    },\n    \"UUID\": {\"type\": \"string\", \"format\": \"uuid4\"},\n    \"UpdateFedcmFlowBody\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"token\",\n        \"csrf_token\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"CSRFToken is the anti-CSRF token.\",\n          \"type\": \"string\"\n        },\n        \"nonce\": {\n          \"description\": \"Nonce is the nonce that was used in the `navigator.credentials.get` call. If\\nspecified, it must match the `nonce` claim in the token.\",\n          \"type\": \"string\"\n        },\n        \"token\": {\n          \"description\": \"Token contains the result of `navigator.credentials.get`.\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks.\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"authenticatorAssuranceLevel\": {\n      \"description\": \"The authenticator assurance level can be one of \\\"aal1\\\", \\\"aal2\\\", or \\\"aal3\\\". A higher number means that it is harder\\nfor an attacker to compromise the account.\\n\\nGenerally, \\\"aal1\\\" implies that one authentication factor was used while AAL2 implies that two factors (e.g.\\npassword + TOTP) have been used.\\n\\nTo learn more about these levels please head over to: https://www.ory.sh/kratos/docs/concepts/credentials\",\n      \"type\": \"string\",\n      \"title\": \"Authenticator Assurance Level (AAL)\"\n    },\n    \"batchPatchIdentitiesResponse\": {\n      \"description\": \"Patch identities response\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"identities\": {\n          \"description\": \"The patch responses for the individual identities.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/identityPatchResponse\"\n          }\n        }\n      }\n    },\n    \"consistencyRequestParameters\": {\n      \"description\": \"Control API consistency guarantees\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"consistency\": {\n          \"description\": \"Read Consistency Level (preview)\\n\\nThe read consistency level determines the consistency guarantee for reads:\\n\\nstrong (slow): The read is guaranteed to return the most recent data committed at the start of the read.\\neventual (very fast): The result will return data that is about 4.8 seconds old.\\n\\nThe default consistency guarantee can be changed in the Ory Network Console or using the Ory CLI with\\n`ory patch project --replace '/previews/default_read_consistency_level=\\\"strong\\\"'`.\\n\\nSetting the default consistency level to `eventual` may cause regressions in the future as we add consistency\\ncontrols to more APIs. Currently, the following APIs will be affected by this setting:\\n\\n`GET /admin/identities`\\n\\nThis feature is in preview and only available in Ory Network.\\n ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level.\\nstrong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level.\\neventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"\",\n            \"strong\",\n            \"eventual\"\n          ],\n          \"x-go-enum-desc\": \" ConsistencyLevelUnset  ConsistencyLevelUnset is the unset / default consistency level.\\nstrong ConsistencyLevelStrong  ConsistencyLevelStrong is the strong consistency level.\\neventual ConsistencyLevelEventual  ConsistencyLevelEventual is the eventual consistency level using follower read timestamps.\"\n        }\n      }\n    },\n    \"continueWith\": {\n      \"type\": \"object\"\n    },\n    \"continueWithRecoveryUi\": {\n      \"description\": \"Indicates, that the UI flow could be continued by showing a recovery ui\",\n      \"type\": \"object\",\n      \"required\": [\n        \"action\",\n        \"flow\"\n      ],\n      \"properties\": {\n        \"action\": {\n          \"description\": \"Action will always be `show_recovery_ui`\\nshow_recovery_ui ContinueWithActionShowRecoveryUIString\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"show_recovery_ui\"\n          ],\n          \"x-go-enum-desc\": \"show_recovery_ui ContinueWithActionShowRecoveryUIString\"\n        },\n        \"flow\": {\n          \"$ref\": \"#/definitions/continueWithRecoveryUiFlow\"\n        }\n      }\n    },\n    \"continueWithRecoveryUiFlow\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"id\"\n      ],\n      \"properties\": {\n        \"id\": {\n          \"description\": \"The ID of the recovery flow\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"url\": {\n          \"description\": \"The URL of the recovery flow\\n\\nIf this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"continueWithRedirectBrowserTo\": {\n      \"description\": \"Indicates, that the UI flow could be continued by showing a recovery ui\",\n      \"type\": \"object\",\n      \"required\": [\n        \"action\",\n        \"redirect_browser_to\"\n      ],\n      \"properties\": {\n        \"action\": {\n          \"description\": \"Action will always be `redirect_browser_to`\\nredirect_browser_to ContinueWithActionRedirectBrowserToString\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"redirect_browser_to\"\n          ],\n          \"x-go-enum-desc\": \"redirect_browser_to ContinueWithActionRedirectBrowserToString\"\n        },\n        \"redirect_browser_to\": {\n          \"description\": \"The URL to redirect the browser to\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"continueWithSetOrySessionToken\": {\n      \"description\": \"Indicates that a session was issued, and the application should use this token for authenticated requests\",\n      \"type\": \"object\",\n      \"required\": [\n        \"action\",\n        \"ory_session_token\"\n      ],\n      \"properties\": {\n        \"action\": {\n          \"description\": \"Action will always be `set_ory_session_token`\\nset_ory_session_token ContinueWithActionSetOrySessionTokenString\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"set_ory_session_token\"\n          ],\n          \"x-go-enum-desc\": \"set_ory_session_token ContinueWithActionSetOrySessionTokenString\"\n        },\n        \"ory_session_token\": {\n          \"description\": \"Token is the token of the session\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"continueWithSettingsUi\": {\n      \"description\": \"Indicates, that the UI flow could be continued by showing a settings ui\",\n      \"type\": \"object\",\n      \"required\": [\n        \"action\",\n        \"flow\"\n      ],\n      \"properties\": {\n        \"action\": {\n          \"description\": \"Action will always be `show_settings_ui`\\nshow_settings_ui ContinueWithActionShowSettingsUIString\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"show_settings_ui\"\n          ],\n          \"x-go-enum-desc\": \"show_settings_ui ContinueWithActionShowSettingsUIString\"\n        },\n        \"flow\": {\n          \"$ref\": \"#/definitions/continueWithSettingsUiFlow\"\n        }\n      }\n    },\n    \"continueWithSettingsUiFlow\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"id\"\n      ],\n      \"properties\": {\n        \"id\": {\n          \"description\": \"The ID of the settings flow\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"url\": {\n          \"description\": \"The URL of the settings flow\\n\\nIf this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"continueWithVerificationUi\": {\n      \"description\": \"Indicates, that the UI flow could be continued by showing a verification ui\",\n      \"type\": \"object\",\n      \"required\": [\n        \"action\",\n        \"flow\"\n      ],\n      \"properties\": {\n        \"action\": {\n          \"description\": \"Action will always be `show_verification_ui`\\nshow_verification_ui ContinueWithActionShowVerificationUIString\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"show_verification_ui\"\n          ],\n          \"x-go-enum-desc\": \"show_verification_ui ContinueWithActionShowVerificationUIString\"\n        },\n        \"flow\": {\n          \"$ref\": \"#/definitions/continueWithVerificationUiFlow\"\n        }\n      }\n    },\n    \"continueWithVerificationUiFlow\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"id\",\n        \"verifiable_address\"\n      ],\n      \"properties\": {\n        \"id\": {\n          \"description\": \"The ID of the verification flow\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"url\": {\n          \"description\": \"The URL of the verification flow\\n\\nIf this value is set, redirect the user's browser to this URL. This value is typically unset for native clients / API flows.\",\n          \"type\": \"string\"\n        },\n        \"verifiable_address\": {\n          \"description\": \"The address that should be verified in this flow\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"courierMessageStatus\": {\n      \"description\": \"A Message's Status\",\n      \"type\": \"integer\",\n      \"format\": \"int64\"\n    },\n    \"courierMessageType\": {\n      \"description\": \"It can either be `email` or `phone`\",\n      \"type\": \"integer\",\n      \"format\": \"int64\",\n      \"title\": \"A Message's Type\"\n    },\n    \"createFedcmFlowResponse\": {\n      \"description\": \"Contains a list of all available FedCM providers.\",\n      \"type\": \"object\",\n      \"title\": \"CreateFedcmFlowResponse\",\n      \"properties\": {\n        \"csrf_token\": {\n          \"type\": \"string\"\n        },\n        \"providers\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/Provider\"\n          }\n        }\n      }\n    },\n    \"createIdentityBody\": {\n      \"description\": \"Create Identity Body\",\n      \"type\": \"object\",\n      \"required\": [\n        \"schema_id\",\n        \"traits\"\n      ],\n      \"properties\": {\n        \"credentials\": {\n          \"$ref\": \"#/definitions/identityWithCredentials\"\n        },\n        \"external_id\": {\n          \"description\": \"ExternalID is an optional external ID of the identity. This is used to link\\nthe identity to an external system. If set, the external ID must be unique\\nacross all identities.\",\n          \"type\": \"string\"\n        },\n        \"metadata_admin\": {\n          \"description\": \"Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/\\u003cid\\u003e`.\",\n          \"type\": \"object\"\n        },\n        \"metadata_public\": {\n          \"description\": \"Store metadata about the identity which the identity itself can see when calling for example the\\nsession endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\",\n          \"type\": \"object\"\n        },\n        \"organization_id\": {\n          \"$ref\": \"#/definitions/NullUUID\"\n        },\n        \"recovery_addresses\": {\n          \"description\": \"RecoveryAddresses contains all the addresses that can be used to recover an identity.\\n\\nUse this structure to import recovery addresses for an identity. Please keep in mind\\nthat the address needs to be represented in the Identity Schema or this field will be overwritten\\non the next identity update.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/recoveryIdentityAddress\"\n          }\n        },\n        \"schema_id\": {\n          \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\",\n          \"type\": \"string\"\n        },\n        \"state\": {\n          \"description\": \"State is the identity's state.\\nactive StateActive\\ninactive StateInactive\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"active\",\n            \"inactive\"\n          ],\n          \"x-go-enum-desc\": \"active StateActive\\ninactive StateInactive\"\n        },\n        \"traits\": {\n          \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_url`.\",\n          \"type\": \"object\"\n        },\n        \"verifiable_addresses\": {\n          \"description\": \"VerifiableAddresses contains all the addresses that can be verified by the user.\\n\\nUse this structure to import verified addresses for an identity. Please keep in mind\\nthat the address needs to be represented in the Identity Schema or this field will be overwritten\\non the next identity update.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/verifiableIdentityAddress\"\n          }\n        }\n      }\n    },\n    \"createRecoveryCodeForIdentityBody\": {\n      \"description\": \"Create Recovery Code for Identity Request Body\",\n      \"type\": \"object\",\n      \"required\": [\n        \"identity_id\"\n      ],\n      \"properties\": {\n        \"expires_in\": {\n          \"description\": \"Code Expires In\\n\\nThe recovery code will expire after that amount of time has passed. Defaults to the configuration value of\\n`selfservice.methods.code.config.lifespan`.\",\n          \"type\": \"string\",\n          \"pattern\": \"^([0-9]+(ns|us|ms|s|m|h))*$\"\n        },\n        \"flow_type\": {\n          \"$ref\": \"#/definitions/selfServiceFlowType\"\n        },\n        \"identity_id\": {\n          \"description\": \"Identity to Recover\\n\\nThe identity's ID you wish to recover.\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        }\n      }\n    },\n    \"createRecoveryLinkForIdentityBody\": {\n      \"description\": \"Create Recovery Link for Identity Request Body\",\n      \"type\": \"object\",\n      \"required\": [\n        \"identity_id\"\n      ],\n      \"properties\": {\n        \"expires_in\": {\n          \"description\": \"Link Expires In\\n\\nThe recovery link will expire after that amount of time has passed. Defaults to the configuration value of\\n`selfservice.methods.code.config.lifespan`.\",\n          \"type\": \"string\",\n          \"pattern\": \"^[0-9]+(ns|us|ms|s|m|h)$\"\n        },\n        \"identity_id\": {\n          \"description\": \"Identity to Recover\\n\\nThe identity's ID you wish to recover.\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        }\n      }\n    },\n    \"deleteMySessionsCount\": {\n      \"description\": \"Deleted Session Count\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"count\": {\n          \"description\": \"The number of sessions that were revoked.\",\n          \"type\": \"integer\",\n          \"format\": \"int64\"\n        }\n      }\n    },\n    \"errorAuthenticatorAssuranceLevelNotSatisfied\": {\n      \"type\": \"object\",\n      \"title\": \"Is returned when an active session was found but the requested AAL is not satisfied.\",\n      \"properties\": {\n        \"error\": {\n          \"$ref\": \"#/definitions/genericError\"\n        },\n        \"redirect_browser_to\": {\n          \"description\": \"Points to where to redirect the user to next.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"errorBrowserLocationChangeRequired\": {\n      \"type\": \"object\",\n      \"title\": \"Is sent when a flow requires a browser to change its location.\",\n      \"properties\": {\n        \"error\": {\n          \"$ref\": \"#/definitions/errorGeneric\"\n        },\n        \"redirect_browser_to\": {\n          \"description\": \"Points to where to redirect the user to next.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"errorFlowReplaced\": {\n      \"description\": \"Is sent when a flow is replaced by a different flow of the same class\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"error\": {\n          \"$ref\": \"#/definitions/genericError\"\n        },\n        \"use_flow_id\": {\n          \"description\": \"The flow ID that should be used for the new flow as it contains the correct messages.\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        }\n      }\n    },\n    \"errorGeneric\": {\n      \"description\": \"The standard Ory JSON API error format.\",\n      \"type\": \"object\",\n      \"title\": \"JSON API Error Response\",\n      \"required\": [\n        \"error\"\n      ],\n      \"properties\": {\n        \"error\": {\n          \"$ref\": \"#/definitions/genericError\"\n        }\n      }\n    },\n    \"flowError\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"id\"\n      ],\n      \"properties\": {\n        \"created_at\": {\n          \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"error\": {\n          \"description\": \"The error\",\n          \"type\": \"object\"\n        },\n        \"id\": {\n          \"description\": \"ID of the error container.\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"updated_at\": {\n          \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        }\n      }\n    },\n    \"genericError\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"message\"\n      ],\n      \"properties\": {\n        \"code\": {\n          \"description\": \"The status code\",\n          \"type\": \"integer\",\n          \"format\": \"int64\",\n          \"example\": 404\n        },\n        \"debug\": {\n          \"description\": \"Debug information\\n\\nThis field is often not exposed to protect against leaking\\nsensitive information.\",\n          \"type\": \"string\",\n          \"example\": \"SQL field \\\"foo\\\" is not a bool.\"\n        },\n        \"details\": {\n          \"description\": \"Further error details\",\n          \"type\": \"object\",\n          \"additionalProperties\": {}\n        },\n        \"id\": {\n          \"description\": \"The error ID\\n\\nUseful when trying to identify various errors in application logic.\",\n          \"type\": \"string\"\n        },\n        \"message\": {\n          \"description\": \"Error message\\n\\nThe error's message.\",\n          \"type\": \"string\",\n          \"example\": \"The resource could not be found\"\n        },\n        \"reason\": {\n          \"description\": \"A human-readable reason for the error\",\n          \"type\": \"string\",\n          \"example\": \"User with ID 1234 does not exist.\"\n        },\n        \"request\": {\n          \"description\": \"The request ID\\n\\nThe request ID is often exposed internally in order to trace\\nerrors across service architectures. This is often a UUID.\",\n          \"type\": \"string\",\n          \"example\": \"d7ef54b1-ec15-46e6-bccb-524b82c035e6\"\n        },\n        \"status\": {\n          \"description\": \"The status description\",\n          \"type\": \"string\",\n          \"example\": \"Not Found\"\n        }\n      }\n    },\n    \"healthNotReadyStatus\": {\n      \"type\": \"object\",\n      \"title\": \"The not ready status of the service.\",\n      \"properties\": {\n        \"errors\": {\n          \"description\": \"Errors contains a list of errors that caused the not ready status.\",\n          \"type\": \"object\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          }\n        }\n      }\n    },\n    \"healthStatus\": {\n      \"type\": \"object\",\n      \"title\": \"The health status of the service.\",\n      \"properties\": {\n        \"status\": {\n          \"description\": \"Status always contains \\\"ok\\\".\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"identity\": {\n      \"description\": \"An [identity](https://www.ory.sh/docs/kratos/concepts/identity-user-model) represents a (human) user in Ory.\",\n      \"type\": \"object\",\n      \"title\": \"Identity represents an Ory Kratos identity\",\n      \"required\": [\n        \"id\",\n        \"schema_id\",\n        \"schema_url\",\n        \"traits\"\n      ],\n      \"properties\": {\n        \"created_at\": {\n          \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"credentials\": {\n          \"description\": \"Credentials represents all credentials that can be used for authenticating this identity.\",\n          \"type\": \"object\",\n          \"additionalProperties\": {\n            \"$ref\": \"#/definitions/identityCredentials\"\n          }\n        },\n        \"external_id\": {\n          \"description\": \"ExternalID is an optional external ID of the identity. This is used to link\\nthe identity to an external system. If set, the external ID must be unique\\nacross all identities.\",\n          \"type\": \"string\"\n        },\n        \"id\": {\n          \"description\": \"ID is the identity's unique identifier.\\n\\nThe Identity ID can not be changed and can not be chosen. This ensures future\\ncompatibility and optimization for distributed stores such as CockroachDB.\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"metadata_admin\": {\n          \"$ref\": \"#/definitions/nullJsonRawMessage\"\n        },\n        \"metadata_public\": {\n          \"$ref\": \"#/definitions/nullJsonRawMessage\"\n        },\n        \"organization_id\": {\n          \"$ref\": \"#/definitions/NullUUID\"\n        },\n        \"recovery_addresses\": {\n          \"description\": \"RecoveryAddresses contains all the addresses that can be used to recover an identity.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/recoveryIdentityAddress\"\n          },\n          \"x-omitempty\": true\n        },\n        \"schema_id\": {\n          \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits.\",\n          \"type\": \"string\"\n        },\n        \"schema_url\": {\n          \"description\": \"SchemaURL is the URL of the endpoint where the identity's traits schema can be fetched from.\\n\\nformat: url\",\n          \"type\": \"string\"\n        },\n        \"state\": {\n          \"description\": \"State is the identity's state.\\n\\nThis value has currently no effect.\\nactive StateActive\\ninactive StateInactive\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"active\",\n            \"inactive\"\n          ],\n          \"x-go-enum-desc\": \"active StateActive\\ninactive StateInactive\"\n        },\n        \"state_changed_at\": {\n          \"$ref\": \"#/definitions/nullTime\"\n        },\n        \"traits\": {\n          \"$ref\": \"#/definitions/identityTraits\"\n        },\n        \"updated_at\": {\n          \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"verifiable_addresses\": {\n          \"description\": \"VerifiableAddresses contains all the addresses that can be verified by the user.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/verifiableIdentityAddress\"\n          },\n          \"x-omitempty\": true\n        }\n      }\n    },\n    \"identityCredentials\": {\n      \"description\": \"Credentials represents a specific credential type\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"config\": {\n          \"$ref\": \"#/definitions/JSONRawMessage\"\n        },\n        \"created_at\": {\n          \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"identifiers\": {\n          \"description\": \"Identifiers represent a list of unique identifiers this credential type matches.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        \"type\": {\n          \"description\": \"Type discriminates between different types of credentials.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"password\",\n            \"oidc\",\n            \"totp\",\n            \"lookup_secret\",\n            \"webauthn\",\n            \"code\",\n            \"passkey\",\n            \"profile\",\n            \"saml\",\n            \"link_recovery\",\n            \"code_recovery\"\n          ],\n          \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\"\n        },\n        \"updated_at\": {\n          \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"version\": {\n          \"description\": \"Version refers to the version of the credential. Useful when changing the config schema.\",\n          \"type\": \"integer\",\n          \"format\": \"int64\"\n        }\n      }\n    },\n    \"identityCredentialsCode\": {\n      \"description\": \"CredentialsCode represents a one time login/registration code\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"addresses\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/identityCredentialsCodeAddress\"\n          }\n        }\n      }\n    },\n    \"identityCredentialsCodeAddress\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"address\": {\n          \"description\": \"The address for this code\",\n          \"type\": \"string\"\n        },\n        \"channel\": {\n          \"$ref\": \"#/definitions/CodeChannel\"\n        }\n      }\n    },\n    \"identityCredentialsOidc\": {\n      \"type\": \"object\",\n      \"title\": \"CredentialsOIDC is contains the configuration for credentials of the type oidc.\",\n      \"properties\": {\n        \"providers\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/identityCredentialsOidcProvider\"\n          }\n        }\n      }\n    },\n    \"identityCredentialsOidcProvider\": {\n      \"type\": \"object\",\n      \"title\": \"CredentialsOIDCProvider is contains a specific OpenID COnnect credential for a particular connection (e.g. Google).\",\n      \"properties\": {\n        \"initial_access_token\": {\n          \"type\": \"string\"\n        },\n        \"initial_id_token\": {\n          \"type\": \"string\"\n        },\n        \"initial_refresh_token\": {\n          \"type\": \"string\"\n        },\n        \"organization\": {\n          \"type\": \"string\"\n        },\n        \"provider\": {\n          \"type\": \"string\"\n        },\n        \"subject\": {\n          \"type\": \"string\"\n        },\n        \"use_auto_link\": {\n          \"type\": \"boolean\"\n        }\n      }\n    },\n    \"identityCredentialsPassword\": {\n      \"type\": \"object\",\n      \"title\": \"CredentialsPassword is contains the configuration for credentials of the type password.\",\n      \"properties\": {\n        \"hashed_password\": {\n          \"description\": \"HashedPassword is a hash-representation of the password.\",\n          \"type\": \"string\"\n        },\n        \"use_password_migration_hook\": {\n          \"description\": \"UsePasswordMigrationHook is set to true if the password should be migrated\\nusing the password migration hook. If set, and the HashedPassword is empty, a\\nwebhook will be called during login to migrate the password.\",\n          \"type\": \"boolean\"\n        }\n      }\n    },\n    \"identityPatch\": {\n      \"description\": \"Payload for patching an identity\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"create\": {\n          \"$ref\": \"#/definitions/createIdentityBody\"\n        },\n        \"patch_id\": {\n          \"description\": \"The ID of this patch.\\n\\nThe patch ID is optional. If specified, the ID will be returned in the\\nresponse, so consumers of this API can correlate the response with the\\npatch.\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        }\n      }\n    },\n    \"identityPatchResponse\": {\n      \"description\": \"Response for a single identity patch\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"action\": {\n          \"description\": \"The action for this specific patch\\ncreate ActionCreate  Create this identity.\\nerror ActionError  Error indicates that the patch failed.\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"create\",\n            \"error\"\n          ],\n          \"x-go-enum-desc\": \"create ActionCreate  Create this identity.\\nerror ActionError  Error indicates that the patch failed.\"\n        },\n        \"error\": {\n          \"$ref\": \"#/definitions/DefaultError\"\n        },\n        \"identity\": {\n          \"description\": \"The identity ID payload of this patch\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"patch_id\": {\n          \"description\": \"The ID of this patch response, if an ID was specified in the patch.\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        }\n      }\n    },\n    \"identitySchema\": {\n      \"description\": \"Raw JSON Schema\",\n      \"type\": \"object\"\n    },\n    \"identitySchemaContainer\": {\n      \"description\": \"An Identity JSON Schema Container\",\n      \"type\": \"object\",\n      \"required\": [\n        \"id\",\n        \"schema\"\n      ],\n      \"properties\": {\n        \"id\": {\n          \"description\": \"The ID of the Identity JSON Schema\",\n          \"type\": \"string\"\n        },\n        \"schema\": {\n          \"description\": \"The actual Identity JSON Schema\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"identitySchemas\": {\n      \"description\": \"List of Identity JSON Schemas\",\n      \"type\": \"array\",\n      \"items\": {\n        \"$ref\": \"#/definitions/identitySchemaContainer\"\n      }\n    },\n    \"identityTraits\": {\n      \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_url`.\",\n      \"type\": \"object\"\n    },\n    \"identityVerifiableAddressStatus\": {\n      \"description\": \"VerifiableAddressStatus must not exceed 16 characters as that is the limitation in the SQL Schema\",\n      \"type\": \"string\"\n    },\n    \"identityWithCredentials\": {\n      \"description\": \"Create Identity and Import Credentials\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"oidc\": {\n          \"$ref\": \"#/definitions/identityWithCredentialsOidc\"\n        },\n        \"password\": {\n          \"$ref\": \"#/definitions/identityWithCredentialsPassword\"\n        },\n        \"saml\": {\n          \"$ref\": \"#/definitions/identityWithCredentialsSaml\"\n        }\n      }\n    },\n    \"identityWithCredentialsOidc\": {\n      \"description\": \"Create Identity and Import Social Sign In Credentials\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"config\": {\n          \"$ref\": \"#/definitions/identityWithCredentialsOidcConfig\"\n        }\n      }\n    },\n    \"identityWithCredentialsOidcConfig\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"providers\": {\n          \"description\": \"A list of OpenID Connect Providers\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/identityWithCredentialsOidcConfigProvider\"\n          }\n        }\n      }\n    },\n    \"identityWithCredentialsOidcConfigProvider\": {\n      \"description\": \"Create Identity and Import Social Sign In Credentials Configuration\",\n      \"type\": \"object\",\n      \"required\": [\n        \"subject\",\n        \"provider\"\n      ],\n      \"properties\": {\n        \"organization\": {\n          \"$ref\": \"#/definitions/NullUUID\"\n        },\n        \"provider\": {\n          \"description\": \"The OpenID Connect provider to link the subject to. Usually something like `google` or `github`.\",\n          \"type\": \"string\"\n        },\n        \"subject\": {\n          \"description\": \"The subject (`sub`) of the OpenID Connect connection. Usually the `sub` field of the ID Token.\",\n          \"type\": \"string\"\n        },\n        \"use_auto_link\": {\n          \"description\": \"If set, this credential allows the user to sign in using the OpenID Connect provider without setting the subject first.\",\n          \"type\": \"boolean\"\n        }\n      }\n    },\n    \"identityWithCredentialsPassword\": {\n      \"description\": \"Create Identity and Import Password Credentials\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"config\": {\n          \"$ref\": \"#/definitions/identityWithCredentialsPasswordConfig\"\n        }\n      }\n    },\n    \"identityWithCredentialsPasswordConfig\": {\n      \"description\": \"Create Identity and Import Password Credentials Configuration\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"hashed_password\": {\n          \"description\": \"The hashed password in [PHC format](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities#hashed-passwords)\",\n          \"type\": \"string\"\n        },\n        \"password\": {\n          \"description\": \"The password in plain text if no hash is available.\",\n          \"type\": \"string\"\n        },\n        \"use_password_migration_hook\": {\n          \"description\": \"If set to true, the password will be migrated using the password migration hook.\",\n          \"type\": \"boolean\"\n        }\n      }\n    },\n    \"identityWithCredentialsSaml\": {\n      \"description\": \"Payload to import SAML credentials\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"config\": {\n          \"$ref\": \"#/definitions/identityWithCredentialsSamlConfig\"\n        }\n      }\n    },\n    \"identityWithCredentialsSamlConfig\": {\n      \"description\": \"Payload of SAML providers\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"providers\": {\n          \"description\": \"A list of SAML Providers\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/identityWithCredentialsSamlConfigProvider\"\n          }\n        }\n      }\n    },\n    \"identityWithCredentialsSamlConfigProvider\": {\n      \"description\": \"Payload of specific SAML provider\",\n      \"type\": \"object\",\n      \"required\": [\n        \"subject\",\n        \"provider\"\n      ],\n      \"properties\": {\n        \"organization\": {\n          \"$ref\": \"#/definitions/NullUUID\"\n        },\n        \"provider\": {\n          \"description\": \"The SAML provider to link the subject to.\",\n          \"type\": \"string\"\n        },\n        \"subject\": {\n          \"description\": \"The unique subject of the SAML connection. This value must be immutable at the source.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"jsonPatch\": {\n      \"description\": \"A JSONPatch document as defined by RFC 6902\",\n      \"type\": \"object\",\n      \"required\": [\n        \"op\",\n        \"path\"\n      ],\n      \"properties\": {\n        \"from\": {\n          \"description\": \"This field is used together with operation \\\"move\\\" and uses JSON Pointer notation.\\n\\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\",\n          \"type\": \"string\",\n          \"example\": \"/name\"\n        },\n        \"op\": {\n          \"description\": \"The operation to be performed. One of \\\"add\\\", \\\"remove\\\", \\\"replace\\\", \\\"move\\\", \\\"copy\\\", or \\\"test\\\".\",\n          \"type\": \"string\",\n          \"example\": \"replace\"\n        },\n        \"path\": {\n          \"description\": \"The path to the target path. Uses JSON pointer notation.\\n\\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\",\n          \"type\": \"string\",\n          \"example\": \"/name\"\n        },\n        \"value\": {\n          \"description\": \"The value to be used within the operations.\\n\\nLearn more [about JSON Pointers](https://datatracker.ietf.org/doc/html/rfc6901#section-5).\",\n          \"example\": \"foobar\"\n        }\n      }\n    },\n    \"jsonPatchDocument\": {\n      \"description\": \"A JSONPatchDocument request\",\n      \"type\": \"array\",\n      \"items\": {\n        \"$ref\": \"#/definitions/jsonPatch\"\n      }\n    },\n    \"loginFlow\": {\n      \"description\": \"This object represents a login flow. A login flow is initiated at the \\\"Initiate Login API / Browser Flow\\\"\\nendpoint by a client.\\n\\nOnce a login flow is completed successfully, a session cookie or session token will be issued.\",\n      \"type\": \"object\",\n      \"title\": \"Login Flow\",\n      \"required\": [\n        \"id\",\n        \"type\",\n        \"expires_at\",\n        \"issued_at\",\n        \"request_url\",\n        \"ui\",\n        \"state\"\n      ],\n      \"properties\": {\n        \"active\": {\n          \"description\": \"The active login method\\n\\nIf set contains the login method used. If the flow is new, it is unset.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"password\",\n            \"oidc\",\n            \"totp\",\n            \"lookup_secret\",\n            \"webauthn\",\n            \"code\",\n            \"passkey\",\n            \"profile\",\n            \"saml\",\n            \"link_recovery\",\n            \"code_recovery\"\n          ],\n          \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\"\n        },\n        \"created_at\": {\n          \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"expires_at\": {\n          \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\\na new flow has to be initiated.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"id\": {\n          \"description\": \"ID represents the flow's unique ID. When performing the login flow, this\\nrepresents the id in the login UI's query parameter: http://\\u003cselfservice.flows.login.ui_url\\u003e/?flow=\\u003cflow_id\\u003e\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"identity_schema\": {\n          \"description\": \"IdentitySchema optionally holds the ID of the identity schema that is used\\nfor this flow. This value can be set by the user when creating the flow and\\nshould be retained when the flow is saved or converted to another flow.\",\n          \"type\": \"string\"\n        },\n        \"issued_at\": {\n          \"description\": \"IssuedAt is the time (UTC) when the flow started.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"oauth2_login_challenge\": {\n          \"description\": \"Ory OAuth 2.0 Login Challenge.\\n\\nThis value is set using the `login_challenge` query parameter of the registration and login endpoints.\\nIf set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\",\n          \"type\": \"string\"\n        },\n        \"oauth2_login_request\": {\n          \"$ref\": \"#/definitions/OAuth2LoginRequest\"\n        },\n        \"organization_id\": {\n          \"$ref\": \"#/definitions/NullUUID\"\n        },\n        \"refresh\": {\n          \"description\": \"Refresh stores whether this login flow should enforce re-authentication.\",\n          \"type\": \"boolean\"\n        },\n        \"request_url\": {\n          \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n          \"type\": \"string\"\n        },\n        \"requested_aal\": {\n          \"$ref\": \"#/definitions/authenticatorAssuranceLevel\"\n        },\n        \"return_to\": {\n          \"description\": \"ReturnTo contains the requested return_to URL.\",\n          \"type\": \"string\"\n        },\n        \"session_token_exchange_code\": {\n          \"description\": \"SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the login flow has been completed.\\nThis is only set if the client has requested a session token exchange code, and if the flow is of type \\\"api\\\",\\nand only on creating the login flow.\",\n          \"type\": \"string\"\n        },\n        \"state\": {\n          \"description\": \"State represents the state of this request:\\n\\nchoose_method: ask the user to choose a method to sign in with\\nsent_email: the email has been sent to the user\\npassed_challenge: the request was successful and the login challenge was passed.\"\n        },\n        \"transient_payload\": {\n          \"description\": \"TransientPayload is used to pass data from the login to hooks and email templates\",\n          \"type\": \"object\"\n        },\n        \"type\": {\n          \"$ref\": \"#/definitions/selfServiceFlowType\"\n        },\n        \"ui\": {\n          \"$ref\": \"#/definitions/uiContainer\"\n        },\n        \"updated_at\": {\n          \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        }\n      }\n    },\n    \"logoutFlow\": {\n      \"description\": \"Logout Flow\",\n      \"type\": \"object\",\n      \"required\": [\n        \"logout_url\",\n        \"logout_token\"\n      ],\n      \"properties\": {\n        \"logout_token\": {\n          \"description\": \"LogoutToken can be used to perform logout using AJAX.\",\n          \"type\": \"string\"\n        },\n        \"logout_url\": {\n          \"description\": \"LogoutURL can be opened in a browser to sign the user out.\\n\\nformat: uri\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"message\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"id\",\n        \"status\",\n        \"type\",\n        \"recipient\",\n        \"body\",\n        \"subject\",\n        \"template_type\",\n        \"send_count\",\n        \"created_at\",\n        \"updated_at\"\n      ],\n      \"properties\": {\n        \"body\": {\n          \"type\": \"string\"\n        },\n        \"channel\": {\n          \"type\": \"string\"\n        },\n        \"created_at\": {\n          \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"dispatches\": {\n          \"description\": \"Dispatches store information about the attempts of delivering a message\\nMay contain an error if any happened, or just the `success` state.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/messageDispatch\"\n          }\n        },\n        \"id\": {\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"recipient\": {\n          \"type\": \"string\"\n        },\n        \"send_count\": {\n          \"type\": \"integer\",\n          \"format\": \"int64\"\n        },\n        \"status\": {\n          \"$ref\": \"#/definitions/courierMessageStatus\"\n        },\n        \"subject\": {\n          \"type\": \"string\"\n        },\n        \"template_type\": {\n          \"description\": \"\\nrecovery_invalid TypeRecoveryInvalid\\nrecovery_valid TypeRecoveryValid\\nrecovery_code_invalid TypeRecoveryCodeInvalid\\nrecovery_code_valid TypeRecoveryCodeValid\\nverification_invalid TypeVerificationInvalid\\nverification_valid TypeVerificationValid\\nverification_code_invalid TypeVerificationCodeInvalid\\nverification_code_valid TypeVerificationCodeValid\\nstub TypeTestStub\\nlogin_code_valid TypeLoginCodeValid\\nregistration_code_valid TypeRegistrationCodeValid\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"recovery_invalid\",\n            \"recovery_valid\",\n            \"recovery_code_invalid\",\n            \"recovery_code_valid\",\n            \"verification_invalid\",\n            \"verification_valid\",\n            \"verification_code_invalid\",\n            \"verification_code_valid\",\n            \"stub\",\n            \"login_code_valid\",\n            \"registration_code_valid\"\n          ],\n          \"x-go-enum-desc\": \"recovery_invalid TypeRecoveryInvalid\\nrecovery_valid TypeRecoveryValid\\nrecovery_code_invalid TypeRecoveryCodeInvalid\\nrecovery_code_valid TypeRecoveryCodeValid\\nverification_invalid TypeVerificationInvalid\\nverification_valid TypeVerificationValid\\nverification_code_invalid TypeVerificationCodeInvalid\\nverification_code_valid TypeVerificationCodeValid\\nstub TypeTestStub\\nlogin_code_valid TypeLoginCodeValid\\nregistration_code_valid TypeRegistrationCodeValid\"\n        },\n        \"type\": {\n          \"$ref\": \"#/definitions/courierMessageType\"\n        },\n        \"updated_at\": {\n          \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        }\n      }\n    },\n    \"messageDispatch\": {\n      \"description\": \"MessageDispatch represents an attempt of sending a courier message\\nIt contains the status of the attempt (failed or successful) and the error if any occured\",\n      \"type\": \"object\",\n      \"required\": [\n        \"id\",\n        \"message_id\",\n        \"status\",\n        \"created_at\",\n        \"updated_at\"\n      ],\n      \"properties\": {\n        \"created_at\": {\n          \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"error\": {\n          \"$ref\": \"#/definitions/JSONRawMessage\"\n        },\n        \"id\": {\n          \"description\": \"The ID of this message dispatch\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"message_id\": {\n          \"description\": \"The ID of the message being dispatched\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"status\": {\n          \"description\": \"The status of this dispatch\\nEither \\\"failed\\\" or \\\"success\\\"\\nfailed CourierMessageDispatchStatusFailed\\nsuccess CourierMessageDispatchStatusSuccess\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"failed\",\n            \"success\"\n          ],\n          \"x-go-enum-desc\": \"failed CourierMessageDispatchStatusFailed\\nsuccess CourierMessageDispatchStatusSuccess\"\n        },\n        \"updated_at\": {\n          \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        }\n      }\n    },\n    \"needsPrivilegedSessionError\": {\n      \"type\": \"object\",\n      \"title\": \"Is sent when a privileged session is required to perform the settings update.\",\n      \"required\": [\n        \"redirect_browser_to\"\n      ],\n      \"properties\": {\n        \"error\": {\n          \"$ref\": \"#/definitions/genericError\"\n        },\n        \"redirect_browser_to\": {\n          \"description\": \"Points to where to redirect the user to next.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"nullJsonRawMessage\": {\n      \"description\": \"NullJSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger and is NULLable-\",\n      \"type\": \"object\"\n    },\n    \"nullTime\": {\n      \"type\": \"string\",\n      \"format\": \"date-time\",\n      \"title\": \"NullTime implements sql.NullTime functionality.\"\n    },\n    \"patchIdentitiesBody\": {\n      \"description\": \"Patch Identities Body\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"identities\": {\n          \"description\": \"Identities holds the list of patches to apply\\n\\nrequired\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/identityPatch\"\n          }\n        }\n      }\n    },\n    \"performNativeLogoutBody\": {\n      \"description\": \"Perform Native Logout Request Body\",\n      \"type\": \"object\",\n      \"required\": [\n        \"session_token\"\n      ],\n      \"properties\": {\n        \"session_token\": {\n          \"description\": \"The Session Token\\n\\nInvalidate this session token.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"recoveryCodeForIdentity\": {\n      \"description\": \"Used when an administrator creates a recovery code for an identity.\",\n      \"type\": \"object\",\n      \"title\": \"Recovery Code for Identity\",\n      \"required\": [\n        \"recovery_link\",\n        \"recovery_code\"\n      ],\n      \"properties\": {\n        \"expires_at\": {\n          \"description\": \"Expires At is the timestamp of when the recovery flow expires\\n\\nThe timestamp when the recovery code expires.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"recovery_code\": {\n          \"description\": \"RecoveryCode is the code that can be used to recover the account\",\n          \"type\": \"string\"\n        },\n        \"recovery_link\": {\n          \"description\": \"RecoveryLink with flow\\n\\nThis link opens the recovery UI with an empty `code` field.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"recoveryFlow\": {\n      \"description\": \"This request is used when an identity wants to recover their account.\\n\\nWe recommend reading the [Account Recovery Documentation](../self-service/flows/password-reset-account-recovery)\",\n      \"type\": \"object\",\n      \"title\": \"A Recovery Flow\",\n      \"required\": [\n        \"id\",\n        \"type\",\n        \"expires_at\",\n        \"issued_at\",\n        \"request_url\",\n        \"ui\",\n        \"state\"\n      ],\n      \"properties\": {\n        \"active\": {\n          \"description\": \"Active, if set, contains the recovery method that is being used. It is initially\\nnot set.\",\n          \"type\": \"string\"\n        },\n        \"continue_with\": {\n          \"description\": \"Contains possible actions that could follow this flow\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/continueWith\"\n          }\n        },\n        \"expires_at\": {\n          \"description\": \"ExpiresAt is the time (UTC) when the request expires. If the user still wishes to update the setting,\\na new request has to be initiated.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"id\": {\n          \"description\": \"ID represents the request's unique ID. When performing the recovery flow, this\\nrepresents the id in the recovery ui's query parameter: http://\\u003cselfservice.flows.recovery.ui_url\\u003e?request=\\u003cid\\u003e\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"issued_at\": {\n          \"description\": \"IssuedAt is the time (UTC) when the request occurred.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"request_url\": {\n          \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n          \"type\": \"string\"\n        },\n        \"return_to\": {\n          \"description\": \"ReturnTo contains the requested return_to URL.\",\n          \"type\": \"string\"\n        },\n        \"state\": {\n          \"description\": \"State represents the state of this request:\\n\\nchoose_method: ask the user to choose a method (e.g. recover account via email)\\nsent_email: the email has been sent to the user\\npassed_challenge: the request was successful and the recovery challenge was passed.\"\n        },\n        \"transient_payload\": {\n          \"description\": \"TransientPayload is used to pass data from the recovery flow to hooks and email templates\",\n          \"type\": \"object\"\n        },\n        \"type\": {\n          \"$ref\": \"#/definitions/selfServiceFlowType\"\n        },\n        \"ui\": {\n          \"$ref\": \"#/definitions/uiContainer\"\n        }\n      }\n    },\n    \"recoveryIdentityAddress\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"value\",\n        \"via\"\n      ],\n      \"properties\": {\n        \"created_at\": {\n          \"description\": \"CreatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"id\": {\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"updated_at\": {\n          \"description\": \"UpdatedAt is a helper struct field for gobuffalo.pop.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"value\": {\n          \"type\": \"string\"\n        },\n        \"via\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"recoveryLinkForIdentity\": {\n      \"description\": \"Used when an administrator creates a recovery link for an identity.\",\n      \"type\": \"object\",\n      \"title\": \"Identity Recovery Link\",\n      \"required\": [\n        \"recovery_link\"\n      ],\n      \"properties\": {\n        \"expires_at\": {\n          \"description\": \"Recovery Link Expires At\\n\\nThe timestamp when the recovery link expires.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"recovery_link\": {\n          \"description\": \"Recovery Link\\n\\nThis link can be used to recover the account.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"registrationFlow\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"id\",\n        \"type\",\n        \"expires_at\",\n        \"issued_at\",\n        \"request_url\",\n        \"ui\",\n        \"state\"\n      ],\n      \"properties\": {\n        \"active\": {\n          \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"password\",\n            \"oidc\",\n            \"totp\",\n            \"lookup_secret\",\n            \"webauthn\",\n            \"code\",\n            \"passkey\",\n            \"profile\",\n            \"saml\",\n            \"link_recovery\",\n            \"code_recovery\"\n          ],\n          \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\"\n        },\n        \"expires_at\": {\n          \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to log in,\\na new flow has to be initiated.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"id\": {\n          \"description\": \"ID represents the flow's unique ID. When performing the registration flow, this\\nrepresents the id in the registration ui's query parameter: http://\\u003cselfservice.flows.registration.ui_url\\u003e/?flow=\\u003cid\\u003e\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"identity_schema\": {\n          \"description\": \"IdentitySchema optionally holds the ID of the identity schema that is used\\nfor this flow. This value can be set by the user when creating the flow and\\nshould be retained when the flow is saved or converted to another flow.\",\n          \"type\": \"string\"\n        },\n        \"issued_at\": {\n          \"description\": \"IssuedAt is the time (UTC) when the flow occurred.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"oauth2_login_challenge\": {\n          \"description\": \"Ory OAuth 2.0 Login Challenge.\\n\\nThis value is set using the `login_challenge` query parameter of the registration and login endpoints.\\nIf set will cooperate with Ory OAuth2 and OpenID to act as an OAuth2 server / OpenID Provider.\",\n          \"type\": \"string\"\n        },\n        \"oauth2_login_request\": {\n          \"$ref\": \"#/definitions/OAuth2LoginRequest\"\n        },\n        \"organization_id\": {\n          \"$ref\": \"#/definitions/NullUUID\"\n        },\n        \"request_url\": {\n          \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n          \"type\": \"string\"\n        },\n        \"return_to\": {\n          \"description\": \"ReturnTo contains the requested return_to URL.\",\n          \"type\": \"string\"\n        },\n        \"session_token_exchange_code\": {\n          \"description\": \"SessionTokenExchangeCode holds the secret code that the client can use to retrieve a session token after the flow has been completed.\\nThis is only set if the client has requested a session token exchange code, and if the flow is of type \\\"api\\\",\\nand only on creating the flow.\",\n          \"type\": \"string\"\n        },\n        \"state\": {\n          \"description\": \"State represents the state of this request:\\n\\nchoose_method: ask the user to choose a method (e.g. registration with email)\\nsent_email: the email has been sent to the user\\npassed_challenge: the request was successful and the registration challenge was passed.\"\n        },\n        \"transient_payload\": {\n          \"description\": \"TransientPayload is used to pass data from the registration to a webhook\",\n          \"type\": \"object\"\n        },\n        \"type\": {\n          \"$ref\": \"#/definitions/selfServiceFlowType\"\n        },\n        \"ui\": {\n          \"$ref\": \"#/definitions/uiContainer\"\n        }\n      }\n    },\n    \"selfServiceFlowExpiredError\": {\n      \"description\": \"Is sent when a flow is expired\",\n      \"type\": \"object\",\n      \"properties\": {\n        \"error\": {\n          \"$ref\": \"#/definitions/genericError\"\n        },\n        \"expired_at\": {\n          \"description\": \"When the flow has expired\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"since\": {\n          \"$ref\": \"#/definitions/Duration\"\n        },\n        \"use_flow_id\": {\n          \"description\": \"The flow ID that should be used for the new flow as it contains the correct messages.\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        }\n      }\n    },\n    \"selfServiceFlowType\": {\n      \"description\": \"The flow type can either be `api` or `browser`.\",\n      \"type\": \"string\",\n      \"title\": \"Type is the flow type.\"\n    },\n    \"session\": {\n      \"description\": \"A Session\",\n      \"type\": \"object\",\n      \"required\": [\n        \"id\"\n      ],\n      \"properties\": {\n        \"active\": {\n          \"description\": \"Active state. If false the session is no longer active.\",\n          \"type\": \"boolean\"\n        },\n        \"authenticated_at\": {\n          \"description\": \"The Session Authentication Timestamp\\n\\nWhen this session was authenticated at. If multi-factor authentication was used this\\nis the time when the last factor was authenticated (e.g. the TOTP code challenge was completed).\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"authentication_methods\": {\n          \"$ref\": \"#/definitions/sessionAuthenticationMethods\"\n        },\n        \"authenticator_assurance_level\": {\n          \"$ref\": \"#/definitions/authenticatorAssuranceLevel\"\n        },\n        \"devices\": {\n          \"description\": \"Devices has history of all endpoints where the session was used\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/sessionDevice\"\n          }\n        },\n        \"expires_at\": {\n          \"description\": \"The Session Expiry\\n\\nWhen this session expires at.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"id\": {\n          \"description\": \"Session ID\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"identity\": {\n          \"$ref\": \"#/definitions/identity\"\n        },\n        \"issued_at\": {\n          \"description\": \"The Session Issuance Timestamp\\n\\nWhen this session was issued at. Usually equal or close to `authenticated_at`.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"tokenized\": {\n          \"description\": \"Tokenized is the tokenized (e.g. JWT) version of the session.\\n\\nIt is only set when the `tokenize_as` query parameter was set to a valid tokenize template during calls to `/session/whoami`.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"sessionAuthenticationMethod\": {\n      \"description\": \"A singular authenticator used during authentication / login.\",\n      \"type\": \"object\",\n      \"title\": \"AuthenticationMethod identifies an authentication method\",\n      \"properties\": {\n        \"aal\": {\n          \"$ref\": \"#/definitions/authenticatorAssuranceLevel\"\n        },\n        \"completed_at\": {\n          \"description\": \"When the authentication challenge was completed.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"method\": {\n          \"description\": \"The method used in this authenticator.\\npassword CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"password\",\n            \"oidc\",\n            \"totp\",\n            \"lookup_secret\",\n            \"webauthn\",\n            \"code\",\n            \"passkey\",\n            \"profile\",\n            \"saml\",\n            \"link_recovery\",\n            \"code_recovery\"\n          ],\n          \"x-go-enum-desc\": \"password CredentialsTypePassword\\noidc CredentialsTypeOIDC\\ntotp CredentialsTypeTOTP\\nlookup_secret CredentialsTypeLookup\\nwebauthn CredentialsTypeWebAuthn\\ncode CredentialsTypeCodeAuth\\npasskey CredentialsTypePasskey\\nprofile CredentialsTypeProfile\\nsaml CredentialsTypeSAML\\nlink_recovery CredentialsTypeRecoveryLink  CredentialsTypeRecoveryLink is a special credential type linked to the link strategy (recovery flow).  It is not used within the credentials object itself.\\ncode_recovery CredentialsTypeRecoveryCode\"\n        },\n        \"organization\": {\n          \"description\": \"The Organization id used for authentication\",\n          \"type\": \"string\"\n        },\n        \"provider\": {\n          \"description\": \"OIDC or SAML provider id used for authentication\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"sessionAuthenticationMethods\": {\n      \"description\": \"A list of authenticators which were used to authenticate the session.\",\n      \"type\": \"array\",\n      \"title\": \"List of (Used) AuthenticationMethods\",\n      \"items\": {\n        \"$ref\": \"#/definitions/sessionAuthenticationMethod\"\n      }\n    },\n    \"sessionDevice\": {\n      \"description\": \"Device corresponding to a Session\",\n      \"type\": \"object\",\n      \"required\": [\n        \"id\"\n      ],\n      \"properties\": {\n        \"id\": {\n          \"description\": \"Device record ID\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"ip_address\": {\n          \"description\": \"IPAddress of the client\",\n          \"type\": \"string\"\n        },\n        \"location\": {\n          \"description\": \"Geo Location corresponding to the IP Address\",\n          \"type\": \"string\"\n        },\n        \"user_agent\": {\n          \"description\": \"UserAgent of the client\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"settingsFlow\": {\n      \"description\": \"This flow is used when an identity wants to update settings\\n(e.g. profile data, passwords, ...) in a selfservice manner.\\n\\nWe recommend reading the [User Settings Documentation](../self-service/flows/user-settings)\",\n      \"type\": \"object\",\n      \"title\": \"Flow represents a Settings Flow\",\n      \"required\": [\n        \"id\",\n        \"type\",\n        \"expires_at\",\n        \"issued_at\",\n        \"request_url\",\n        \"ui\",\n        \"identity\",\n        \"state\"\n      ],\n      \"properties\": {\n        \"active\": {\n          \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n          \"type\": \"string\"\n        },\n        \"continue_with\": {\n          \"description\": \"Contains a list of actions, that could follow this flow\\n\\nIt can, for example, contain a reference to the verification flow, created as part of the user's\\nregistration.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/continueWith\"\n          }\n        },\n        \"expires_at\": {\n          \"description\": \"ExpiresAt is the time (UTC) when the flow expires. If the user still wishes to update the setting,\\na new flow has to be initiated.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"id\": {\n          \"description\": \"ID represents the flow's unique ID. When performing the settings flow, this\\nrepresents the id in the settings ui's query parameter: http://\\u003cselfservice.flows.settings.ui_url\\u003e?flow=\\u003cid\\u003e\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"identity\": {\n          \"$ref\": \"#/definitions/identity\"\n        },\n        \"issued_at\": {\n          \"description\": \"IssuedAt is the time (UTC) when the flow occurred.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"request_url\": {\n          \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n          \"type\": \"string\"\n        },\n        \"return_to\": {\n          \"description\": \"ReturnTo contains the requested return_to URL.\",\n          \"type\": \"string\"\n        },\n        \"state\": {\n          \"description\": \"State represents the state of this flow. It knows two states:\\n\\nshow_form: No user data has been collected, or it is invalid, and thus the form should be shown.\\nsuccess: Indicates that the settings flow has been updated successfully with the provided data.\\nDone will stay true when repeatedly checking. If set to true, done will revert back to false only\\nwhen a flow with invalid (e.g. \\\"please use a valid phone number\\\") data was sent.\"\n        },\n        \"transient_payload\": {\n          \"description\": \"TransientPayload is used to pass data from the settings flow to hooks and email templates\",\n          \"type\": \"object\"\n        },\n        \"type\": {\n          \"$ref\": \"#/definitions/selfServiceFlowType\"\n        },\n        \"ui\": {\n          \"$ref\": \"#/definitions/uiContainer\"\n        }\n      }\n    },\n    \"successfulCodeExchangeResponse\": {\n      \"description\": \"The Response for Registration Flows via API\",\n      \"type\": \"object\",\n      \"required\": [\n        \"session\"\n      ],\n      \"properties\": {\n        \"session\": {\n          \"$ref\": \"#/definitions/session\"\n        },\n        \"session_token\": {\n          \"description\": \"The Session Token\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"successfulNativeLogin\": {\n      \"description\": \"The Response for Login Flows via API\",\n      \"type\": \"object\",\n      \"required\": [\n        \"session\"\n      ],\n      \"properties\": {\n        \"continue_with\": {\n          \"description\": \"Contains a list of actions, that could follow this flow\\n\\nIt can, for example, this will contain a reference to the verification flow, created as part of the user's\\nregistration or the token of the session.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/continueWith\"\n          }\n        },\n        \"session\": {\n          \"$ref\": \"#/definitions/session\"\n        },\n        \"session_token\": {\n          \"description\": \"The Session Token\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"successfulNativeRegistration\": {\n      \"description\": \"The Response for Registration Flows via API\",\n      \"type\": \"object\",\n      \"required\": [\n        \"identity\"\n      ],\n      \"properties\": {\n        \"continue_with\": {\n          \"description\": \"Contains a list of actions, that could follow this flow\\n\\nIt can, for example, this will contain a reference to the verification flow, created as part of the user's\\nregistration or the token of the session.\",\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/definitions/continueWith\"\n          }\n        },\n        \"identity\": {\n          \"$ref\": \"#/definitions/identity\"\n        },\n        \"session\": {\n          \"$ref\": \"#/definitions/session\"\n        },\n        \"session_token\": {\n          \"description\": \"The Session Token\\n\\nThis field is only set when the session hook is configured as a post-registration hook.\\n\\nA session token is equivalent to a session cookie, but it can be sent in the HTTP Authorization\\nHeader:\\n\\nAuthorization: bearer ${session-token}\\n\\nThe session token is only issued for API flows, not for Browser flows!\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"tokenPagination\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"page_size\": {\n          \"description\": \"Items per page\\n\\nThis is the number of items per page to return.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n          \"type\": \"integer\",\n          \"format\": \"int64\",\n          \"default\": 250,\n          \"maximum\": 1000,\n          \"minimum\": 1\n        },\n        \"page_token\": {\n          \"description\": \"Next Page Token\\n\\nThe next page token.\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"tokenPaginationHeaders\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"link\": {\n          \"description\": \"The link header contains pagination links.\\n\\nFor details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination).\\n\\nin: header\",\n          \"type\": \"string\"\n        },\n        \"x-total-count\": {\n          \"description\": \"The total number of clients.\\n\\nin: header\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"uiContainer\": {\n      \"description\": \"Container represents a HTML Form. The container can work with both HTTP Form and JSON requests\",\n      \"type\": \"object\",\n      \"required\": [\n        \"action\",\n        \"method\",\n        \"nodes\"\n      ],\n      \"properties\": {\n        \"action\": {\n          \"description\": \"Action should be used as the form action URL `\\u003cform action=\\\"{{ .Action }}\\\" method=\\\"post\\\"\\u003e`.\",\n          \"type\": \"string\"\n        },\n        \"messages\": {\n          \"$ref\": \"#/definitions/uiTexts\"\n        },\n        \"method\": {\n          \"description\": \"Method is the form method (e.g. POST)\",\n          \"type\": \"string\"\n        },\n        \"nodes\": {\n          \"$ref\": \"#/definitions/uiNodes\"\n        }\n      }\n    },\n    \"uiNode\": {\n      \"description\": \"Nodes are represented as HTML elements or their native UI equivalents. For example,\\na node can be an `\\u003cimg\\u003e` tag, or an `\\u003cinput element\\u003e` but also `some plain text`.\",\n      \"type\": \"object\",\n      \"title\": \"Node represents a flow's nodes\",\n      \"required\": [\n        \"type\",\n        \"group\",\n        \"attributes\",\n        \"messages\",\n        \"meta\"\n      ],\n      \"properties\": {\n        \"attributes\": {\n          \"$ref\": \"#/definitions/uiNodeAttributes\"\n        },\n        \"group\": {\n          \"description\": \"Group specifies which group (e.g. password authenticator) this node belongs to.\\ndefault DefaultGroup\\npassword PasswordGroup\\noidc OpenIDConnectGroup\\nprofile ProfileGroup\\nlink LinkGroup\\ncode CodeGroup\\ntotp TOTPGroup\\nlookup_secret LookupGroup\\nwebauthn WebAuthnGroup\\npasskey PasskeyGroup\\nidentifier_first IdentifierFirstGroup\\ncaptcha CaptchaGroup\\nsaml SAMLGroup\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"default\",\n            \"password\",\n            \"oidc\",\n            \"profile\",\n            \"link\",\n            \"code\",\n            \"totp\",\n            \"lookup_secret\",\n            \"webauthn\",\n            \"passkey\",\n            \"identifier_first\",\n            \"captcha\",\n            \"saml\"\n          ],\n          \"x-go-enum-desc\": \"default DefaultGroup\\npassword PasswordGroup\\noidc OpenIDConnectGroup\\nprofile ProfileGroup\\nlink LinkGroup\\ncode CodeGroup\\ntotp TOTPGroup\\nlookup_secret LookupGroup\\nwebauthn WebAuthnGroup\\npasskey PasskeyGroup\\nidentifier_first IdentifierFirstGroup\\ncaptcha CaptchaGroup\\nsaml SAMLGroup\"\n        },\n        \"messages\": {\n          \"$ref\": \"#/definitions/uiTexts\"\n        },\n        \"meta\": {\n          \"$ref\": \"#/definitions/uiNodeMeta\"\n        },\n        \"type\": {\n          \"description\": \"The node's type\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"input\",\n            \"img\",\n            \"a\",\n            \"script\",\n            \"div\"\n          ],\n          \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n        }\n      }\n    },\n    \"uiNodeAnchorAttributes\": {\n      \"type\": \"object\",\n      \"title\": \"AnchorAttributes represents the attributes of an anchor node.\",\n      \"required\": [\n        \"href\",\n        \"title\",\n        \"id\",\n        \"node_type\"\n      ],\n      \"properties\": {\n        \"href\": {\n          \"description\": \"The link's href (destination) URL.\\n\\nformat: uri\",\n          \"type\": \"string\"\n        },\n        \"id\": {\n          \"description\": \"A unique identifier\",\n          \"type\": \"string\"\n        },\n        \"node_type\": {\n          \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"a\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"input\",\n            \"img\",\n            \"a\",\n            \"script\",\n            \"div\"\n          ],\n          \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n        },\n        \"title\": {\n          \"$ref\": \"#/definitions/uiText\"\n        }\n      }\n    },\n    \"uiNodeAttributes\": {\n      \"type\": \"object\",\n      \"title\": \"Attributes represents a list of attributes (e.g. `href=\\\"foo\\\"` for links).\"\n    },\n    \"uiNodeDivisionAttributes\": {\n      \"description\": \"Division sections are used for interactive widgets that require a hook in the DOM / view.\",\n      \"type\": \"object\",\n      \"title\": \"DivisionAttributes represent a division section.\",\n      \"required\": [\n        \"id\",\n        \"node_type\"\n      ],\n      \"properties\": {\n        \"class\": {\n          \"description\": \"A classname that should be rendered into the DOM.\",\n          \"type\": \"string\"\n        },\n        \"data\": {\n          \"description\": \"Data is a map of key-value pairs that are passed to the division.\\n\\nThey may be used for `data-...` attributes.\",\n          \"type\": \"object\",\n          \"additionalProperties\": {\n            \"type\": \"string\"\n          }\n        },\n        \"id\": {\n          \"description\": \"A unique identifier\",\n          \"type\": \"string\"\n        },\n        \"node_type\": {\n          \"description\": \"NodeType represents this node's type. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \\\"script\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"input\",\n            \"img\",\n            \"a\",\n            \"script\",\n            \"div\"\n          ],\n          \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n        }\n      }\n    },\n    \"uiNodeImageAttributes\": {\n      \"type\": \"object\",\n      \"title\": \"ImageAttributes represents the attributes of an image node.\",\n      \"required\": [\n        \"src\",\n        \"id\",\n        \"width\",\n        \"height\",\n        \"node_type\"\n      ],\n      \"properties\": {\n        \"height\": {\n          \"description\": \"Height of the image\",\n          \"type\": \"integer\",\n          \"format\": \"int64\"\n        },\n        \"id\": {\n          \"description\": \"A unique identifier\",\n          \"type\": \"string\"\n        },\n        \"node_type\": {\n          \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"img\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"input\",\n            \"img\",\n            \"a\",\n            \"script\",\n            \"div\"\n          ],\n          \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n        },\n        \"src\": {\n          \"description\": \"The image's source URL.\\n\\nformat: uri\",\n          \"type\": \"string\"\n        },\n        \"width\": {\n          \"description\": \"Width of the image\",\n          \"type\": \"integer\",\n          \"format\": \"int64\"\n        }\n      }\n    },\n    \"uiNodeInputAttributes\": {\n      \"description\": \"InputAttributes represents the attributes of an input node\",\n      \"type\": \"object\",\n      \"required\": [\n        \"name\",\n        \"type\",\n        \"disabled\",\n        \"node_type\"\n      ],\n      \"properties\": {\n        \"autocomplete\": {\n          \"description\": \"The autocomplete attribute for the input.\\nemail InputAttributeAutocompleteEmail\\ntel InputAttributeAutocompleteTel\\nurl InputAttributeAutocompleteUrl\\ncurrent-password InputAttributeAutocompleteCurrentPassword\\nnew-password InputAttributeAutocompleteNewPassword\\none-time-code InputAttributeAutocompleteOneTimeCode\\nusername webauthn InputAttributeAutocompleteUsernameWebauthn\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"email\",\n            \"tel\",\n            \"url\",\n            \"current-password\",\n            \"new-password\",\n            \"one-time-code\",\n            \"username webauthn\"\n          ],\n          \"x-go-enum-desc\": \"email InputAttributeAutocompleteEmail\\ntel InputAttributeAutocompleteTel\\nurl InputAttributeAutocompleteUrl\\ncurrent-password InputAttributeAutocompleteCurrentPassword\\nnew-password InputAttributeAutocompleteNewPassword\\none-time-code InputAttributeAutocompleteOneTimeCode\\nusername webauthn InputAttributeAutocompleteUsernameWebauthn\"\n        },\n        \"disabled\": {\n          \"description\": \"Sets the input's disabled field to true or false.\",\n          \"type\": \"boolean\"\n        },\n        \"label\": {\n          \"$ref\": \"#/definitions/uiText\"\n        },\n        \"maxlength\": {\n          \"description\": \"MaxLength may contain the input's maximum length.\",\n          \"type\": \"integer\",\n          \"format\": \"int64\"\n        },\n        \"name\": {\n          \"description\": \"The input's element name.\",\n          \"type\": \"string\"\n        },\n        \"node_type\": {\n          \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"input\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"input\",\n            \"img\",\n            \"a\",\n            \"script\",\n            \"div\"\n          ],\n          \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n        },\n        \"onclick\": {\n          \"description\": \"OnClick may contain javascript which should be executed on click. This is primarily\\nused for WebAuthn.\\n\\nDeprecated: Using OnClick requires the use of eval() which is a security risk. Use OnClickTrigger instead.\",\n          \"type\": \"string\"\n        },\n        \"onclickTrigger\": {\n          \"description\": \"OnClickTrigger may contain a WebAuthn trigger which should be executed on click.\\n\\nThe trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login.\\noryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration\\noryWebAuthnLogin WebAuthnTriggersWebAuthnLogin\\noryPasskeyLogin WebAuthnTriggersPasskeyLogin\\noryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit\\noryPasskeyRegistration WebAuthnTriggersPasskeyRegistration\\noryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"oryWebAuthnRegistration\",\n            \"oryWebAuthnLogin\",\n            \"oryPasskeyLogin\",\n            \"oryPasskeyLoginAutocompleteInit\",\n            \"oryPasskeyRegistration\",\n            \"oryPasskeySettingsRegistration\"\n          ],\n          \"x-go-enum-desc\": \"oryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration\\noryWebAuthnLogin WebAuthnTriggersWebAuthnLogin\\noryPasskeyLogin WebAuthnTriggersPasskeyLogin\\noryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit\\noryPasskeyRegistration WebAuthnTriggersPasskeyRegistration\\noryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\"\n        },\n        \"onload\": {\n          \"description\": \"OnLoad may contain javascript which should be executed on load. This is primarily\\nused for WebAuthn.\\n\\nDeprecated: Using OnLoad requires the use of eval() which is a security risk. Use OnLoadTrigger instead.\",\n          \"type\": \"string\"\n        },\n        \"onloadTrigger\": {\n          \"description\": \"OnLoadTrigger may contain a WebAuthn trigger which should be executed on load.\\n\\nThe trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login.\\noryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration\\noryWebAuthnLogin WebAuthnTriggersWebAuthnLogin\\noryPasskeyLogin WebAuthnTriggersPasskeyLogin\\noryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit\\noryPasskeyRegistration WebAuthnTriggersPasskeyRegistration\\noryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"oryWebAuthnRegistration\",\n            \"oryWebAuthnLogin\",\n            \"oryPasskeyLogin\",\n            \"oryPasskeyLoginAutocompleteInit\",\n            \"oryPasskeyRegistration\",\n            \"oryPasskeySettingsRegistration\"\n          ],\n          \"x-go-enum-desc\": \"oryWebAuthnRegistration WebAuthnTriggersWebAuthnRegistration\\noryWebAuthnLogin WebAuthnTriggersWebAuthnLogin\\noryPasskeyLogin WebAuthnTriggersPasskeyLogin\\noryPasskeyLoginAutocompleteInit WebAuthnTriggersPasskeyLoginAutocompleteInit\\noryPasskeyRegistration WebAuthnTriggersPasskeyRegistration\\noryPasskeySettingsRegistration WebAuthnTriggersPasskeySettingsRegistration\"\n        },\n        \"pattern\": {\n          \"description\": \"The input's pattern.\",\n          \"type\": \"string\"\n        },\n        \"required\": {\n          \"description\": \"Mark this input field as required.\",\n          \"type\": \"boolean\"\n        },\n        \"type\": {\n          \"description\": \"The input's element type.\\ntext InputAttributeTypeText\\npassword InputAttributeTypePassword\\nnumber InputAttributeTypeNumber\\ncheckbox InputAttributeTypeCheckbox\\nhidden InputAttributeTypeHidden\\nemail InputAttributeTypeEmail\\ntel InputAttributeTypeTel\\nsubmit InputAttributeTypeSubmit\\nbutton InputAttributeTypeButton\\ndatetime-local InputAttributeTypeDateTimeLocal\\ndate InputAttributeTypeDate\\nurl InputAttributeTypeURI\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"password\",\n            \"number\",\n            \"checkbox\",\n            \"hidden\",\n            \"email\",\n            \"tel\",\n            \"submit\",\n            \"button\",\n            \"datetime-local\",\n            \"date\",\n            \"url\"\n          ],\n          \"x-go-enum-desc\": \"text InputAttributeTypeText\\npassword InputAttributeTypePassword\\nnumber InputAttributeTypeNumber\\ncheckbox InputAttributeTypeCheckbox\\nhidden InputAttributeTypeHidden\\nemail InputAttributeTypeEmail\\ntel InputAttributeTypeTel\\nsubmit InputAttributeTypeSubmit\\nbutton InputAttributeTypeButton\\ndatetime-local InputAttributeTypeDateTimeLocal\\ndate InputAttributeTypeDate\\nurl InputAttributeTypeURI\"\n        },\n        \"value\": {\n          \"description\": \"The input's value.\"\n        }\n      }\n    },\n    \"uiNodeMeta\": {\n      \"description\": \"This might include a label and other information that can optionally\\nbe used to render UIs.\",\n      \"type\": \"object\",\n      \"title\": \"A Node's Meta Information\",\n      \"properties\": {\n        \"label\": {\n          \"$ref\": \"#/definitions/uiText\"\n        }\n      }\n    },\n    \"uiNodeScriptAttributes\": {\n      \"type\": \"object\",\n      \"title\": \"ScriptAttributes represent script nodes which load javascript.\",\n      \"required\": [\n        \"src\",\n        \"async\",\n        \"referrerpolicy\",\n        \"crossorigin\",\n        \"integrity\",\n        \"type\",\n        \"id\",\n        \"nonce\",\n        \"node_type\"\n      ],\n      \"properties\": {\n        \"async\": {\n          \"description\": \"The script async type\",\n          \"type\": \"boolean\"\n        },\n        \"crossorigin\": {\n          \"description\": \"The script cross origin policy\",\n          \"type\": \"string\"\n        },\n        \"id\": {\n          \"description\": \"A unique identifier\",\n          \"type\": \"string\"\n        },\n        \"integrity\": {\n          \"description\": \"The script's integrity hash\",\n          \"type\": \"string\"\n        },\n        \"node_type\": {\n          \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \\\"script\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"input\",\n            \"img\",\n            \"a\",\n            \"script\",\n            \"div\"\n          ],\n          \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n        },\n        \"nonce\": {\n          \"description\": \"Nonce for CSP\\n\\nA nonce you may want to use to improve your Content Security Policy.\\nYou do not have to use this value but if you want to improve your CSP\\npolicies you may use it. You can also choose to use your own nonce value!\",\n          \"type\": \"string\"\n        },\n        \"referrerpolicy\": {\n          \"description\": \"The script referrer policy\",\n          \"type\": \"string\"\n        },\n        \"src\": {\n          \"description\": \"The script source\",\n          \"type\": \"string\"\n        },\n        \"type\": {\n          \"description\": \"The script MIME type\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"uiNodeTextAttributes\": {\n      \"type\": \"object\",\n      \"title\": \"TextAttributes represents the attributes of a text node.\",\n      \"required\": [\n        \"text\",\n        \"id\",\n        \"node_type\"\n      ],\n      \"properties\": {\n        \"id\": {\n          \"description\": \"A unique identifier\",\n          \"type\": \"string\"\n        },\n        \"node_type\": {\n          \"description\": \"NodeType represents this node's types. It is a mirror of `node.type` and\\nis primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \\\"text\\\".\\ntext Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"text\",\n            \"input\",\n            \"img\",\n            \"a\",\n            \"script\",\n            \"div\"\n          ],\n          \"x-go-enum-desc\": \"text Text\\ninput Input\\nimg Image\\na Anchor\\nscript Script\\ndiv Division\"\n        },\n        \"text\": {\n          \"$ref\": \"#/definitions/uiText\"\n        }\n      }\n    },\n    \"uiNodes\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"$ref\": \"#/definitions/uiNode\"\n      }\n    },\n    \"uiText\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"id\",\n        \"text\",\n        \"type\"\n      ],\n      \"properties\": {\n        \"context\": {\n          \"description\": \"The message's context. Useful when customizing messages.\",\n          \"type\": \"object\"\n        },\n        \"id\": {\n          \"$ref\": \"#/definitions/ID\"\n        },\n        \"text\": {\n          \"description\": \"The message text. Written in american english.\",\n          \"type\": \"string\"\n        },\n        \"type\": {\n          \"description\": \"The message type.\\ninfo Info\\nerror Error\\nsuccess Success\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"info\",\n            \"error\",\n            \"success\"\n          ],\n          \"x-go-enum-desc\": \"info Info\\nerror Error\\nsuccess Success\"\n        }\n      }\n    },\n    \"uiTexts\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"$ref\": \"#/definitions/uiText\"\n      }\n    },\n    \"unexpectedError\": {\n      \"type\": \"string\"\n    },\n    \"updateIdentityBody\": {\n      \"description\": \"Update Identity Body\",\n      \"type\": \"object\",\n      \"required\": [\n        \"schema_id\",\n        \"traits\",\n        \"state\"\n      ],\n      \"properties\": {\n        \"credentials\": {\n          \"$ref\": \"#/definitions/identityWithCredentials\"\n        },\n        \"external_id\": {\n          \"description\": \"ExternalID is an optional external ID of the identity. This is used to link\\nthe identity to an external system. If set, the external ID must be unique\\nacross all identities.\",\n          \"type\": \"string\"\n        },\n        \"metadata_admin\": {\n          \"description\": \"Store metadata about the user which is only accessible through admin APIs such as `GET /admin/identities/\\u003cid\\u003e`.\",\n          \"type\": \"object\"\n        },\n        \"metadata_public\": {\n          \"description\": \"Store metadata about the identity which the identity itself can see when calling for example the\\nsession endpoint. Do not store sensitive information (e.g. credit score) about the identity in this field.\",\n          \"type\": \"object\"\n        },\n        \"schema_id\": {\n          \"description\": \"SchemaID is the ID of the JSON Schema to be used for validating the identity's traits. If set\\nwill update the Identity's SchemaID.\",\n          \"type\": \"string\"\n        },\n        \"state\": {\n          \"description\": \"State is the identity's state.\\nactive StateActive\\ninactive StateInactive\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"active\",\n            \"inactive\"\n          ],\n          \"x-go-enum-desc\": \"active StateActive\\ninactive StateInactive\"\n        },\n        \"traits\": {\n          \"description\": \"Traits represent an identity's traits. The identity is able to create, modify, and delete traits\\nin a self-service manner. The input will always be validated against the JSON Schema defined\\nin `schema_id`.\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateLoginFlowBody\": {\n      \"type\": \"object\"\n    },\n    \"updateLoginFlowWithCodeMethod\": {\n      \"description\": \"Update Login flow using the code method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\",\n        \"csrf_token\"\n      ],\n      \"properties\": {\n        \"address\": {\n          \"description\": \"Address is the address to send the code to, in case that there are multiple addresses. This field\\nis only used in two-factor flows and is ineffective for passwordless flows.\",\n          \"type\": \"string\"\n        },\n        \"code\": {\n          \"description\": \"Code is the 6 digits code sent to the user\",\n          \"type\": \"string\"\n        },\n        \"csrf_token\": {\n          \"description\": \"CSRFToken is the anti-CSRF token\",\n          \"type\": \"string\"\n        },\n        \"identifier\": {\n          \"description\": \"Identifier is the code identifier\\nThe identifier requires that the user has already completed the registration or settings with code flow.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method should be set to \\\"code\\\" when logging in using the code strategy.\",\n          \"type\": \"string\"\n        },\n        \"resend\": {\n          \"description\": \"Resend is set when the user wants to resend the code\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateLoginFlowWithIdentifierFirstMethod\": {\n      \"description\": \"Update Login Flow with Multi-Step Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\",\n        \"identifier\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"identifier\": {\n          \"description\": \"Identifier is the email or username of the user trying to log in.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method should be set to \\\"password\\\" when logging in using the identifier and password strategy.\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateLoginFlowWithLookupSecretMethod\": {\n      \"description\": \"Update Login Flow with Lookup Secret Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\",\n        \"lookup_secret\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"lookup_secret\": {\n          \"description\": \"The lookup secret.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method should be set to \\\"lookup_secret\\\" when logging in using the lookup_secret strategy.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"updateLoginFlowWithOidcMethod\": {\n      \"description\": \"Update Login Flow with OpenID Connect Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"provider\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"The CSRF Token\",\n          \"type\": \"string\"\n        },\n        \"id_token\": {\n          \"description\": \"IDToken is an optional id token provided by an OIDC provider\\n\\nIf submitted, it is verified using the OIDC provider's public key set and the claims are used to populate\\nthe OIDC credentials of the identity.\\nIf the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use\\nthe `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.\\n\\nSupported providers are\\nApple\\nGoogle\",\n          \"type\": \"string\"\n        },\n        \"id_token_nonce\": {\n          \"description\": \"IDTokenNonce is the nonce, used when generating the IDToken.\\nIf the provider supports nonce validation, the nonce will be validated against this value and required.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method to use\\n\\nThis field must be set to `oidc` when using the oidc method.\",\n          \"type\": \"string\"\n        },\n        \"provider\": {\n          \"description\": \"The provider to register with\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"The identity traits. This is a placeholder for the registration flow.\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        },\n        \"upstream_parameters\": {\n          \"description\": \"UpstreamParameters are the parameters that are passed to the upstream identity provider.\\n\\nThese parameters are optional and depend on what the upstream identity provider supports.\\nSupported parameters are:\\n`login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\\n`hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\\n`prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\\n`acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateLoginFlowWithPasskeyMethod\": {\n      \"description\": \"Update Login Flow with Passkey Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method should be set to \\\"passkey\\\" when logging in using the Passkey strategy.\",\n          \"type\": \"string\"\n        },\n        \"passkey_login\": {\n          \"description\": \"Login a WebAuthn Security Key\\n\\nThis must contain the ID of the WebAuthN connection.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"updateLoginFlowWithPasswordMethod\": {\n      \"description\": \"Update Login Flow with Password Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\",\n        \"password\",\n        \"identifier\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"identifier\": {\n          \"description\": \"Identifier is the email or username of the user trying to log in.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method should be set to \\\"password\\\" when logging in using the identifier and password strategy.\",\n          \"type\": \"string\"\n        },\n        \"password\": {\n          \"description\": \"The user's password.\",\n          \"type\": \"string\"\n        },\n        \"password_identifier\": {\n          \"description\": \"Identifier is the email or username of the user trying to log in.\\nThis field is deprecated!\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateLoginFlowWithSamlMethod\": {\n      \"description\": \"Update login flow using SAML\",\n      \"type\": \"object\",\n      \"required\": [\n        \"provider\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"The CSRF Token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method to use\\n\\nThis field must be set to `saml` when using the saml method.\",\n          \"type\": \"string\"\n        },\n        \"provider\": {\n          \"description\": \"The provider to register with\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateLoginFlowWithTotpMethod\": {\n      \"description\": \"Update Login Flow with TOTP Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\",\n        \"totp_code\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method should be set to \\\"totp\\\" when logging in using the TOTP strategy.\",\n          \"type\": \"string\"\n        },\n        \"totp_code\": {\n          \"description\": \"The TOTP code.\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateLoginFlowWithWebAuthnMethod\": {\n      \"description\": \"Update Login Flow with WebAuthn Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"identifier\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"identifier\": {\n          \"description\": \"Identifier is the email or username of the user trying to log in.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method should be set to \\\"webAuthn\\\" when logging in using the WebAuthn strategy.\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        },\n        \"webauthn_login\": {\n          \"description\": \"Login a WebAuthn Security Key\\n\\nThis must contain the ID of the WebAuthN connection.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"updateRecoveryFlowBody\": {\n      \"description\": \"Update Recovery Flow Request Body\",\n      \"type\": \"object\"\n    },\n    \"updateRecoveryFlowWithCodeMethod\": {\n      \"description\": \"Update Recovery Flow with Code Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\"\n      ],\n      \"properties\": {\n        \"code\": {\n          \"description\": \"Code from the recovery email\\n\\nIf you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\",\n          \"type\": \"string\"\n        },\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"description\": \"The email address of the account to recover\\n\\nIf the email belongs to a valid account, a recovery email will be sent.\\n\\nIf you want to notify the email address if the account does not exist, see\\nthe [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/account-recovery-password-reset#attempted-recovery-notifications)\\n\\nIf a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.\\n\\nformat: email\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method is the method that should be used for this recovery flow\\n\\nAllowed values are `link` and `code`.\\nlink RecoveryStrategyLink\\ncode RecoveryStrategyCode\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"link\",\n            \"code\"\n          ],\n          \"x-go-enum-desc\": \"link RecoveryStrategyLink\\ncode RecoveryStrategyCode\"\n        },\n        \"recovery_address\": {\n          \"description\": \"A recovery address that is registered for the user.\\nIt can be an email, a phone number (to receive the code via SMS), etc.\\nUsed in RecoveryV2.\",\n          \"type\": \"string\"\n        },\n        \"recovery_confirm_address\": {\n          \"description\": \"If there are multiple recovery addresses registered for the user, and the initially provided address\\nis different from the address chosen when the choice (of masked addresses) is presented, then we need to make sure\\nthat the user actually knows the full address to avoid information exfiltration, so we ask for the full address.\\nUsed in RecoveryV2.\",\n          \"type\": \"string\"\n        },\n        \"recovery_select_address\": {\n          \"description\": \"If there are multiple addresses registered for the user, a choice is presented and this field\\nstores the result of this choice.\\nAddresses are 'masked' (never sent in full to the client and shown partially in the UI) since at this point in the recovery flow,\\nthe user has not yet proven that it knows the full address and we want to avoid\\ninformation exfiltration.\\nSo for all intents and purposes, the value of this field should be treated as an opaque identifier.\\nUsed in RecoveryV2.\",\n          \"type\": \"string\"\n        },\n        \"screen\": {\n          \"description\": \"Set to \\\"previous\\\" to go back in the flow, meaningfully.\\nUsed in RecoveryV2.\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateRecoveryFlowWithLinkMethod\": {\n      \"description\": \"Update Recovery Flow with Link Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"email\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"description\": \"Email to Recover\\n\\nNeeds to be set when initiating the flow. If the email is a registered\\nrecovery email, a recovery link will be sent. If the email is not known,\\nan email with details on what happened will be sent instead.\\n\\nformat: email\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method is the method that should be used for this recovery flow\\n\\nAllowed values are `link` and `code`\\nlink RecoveryStrategyLink\\ncode RecoveryStrategyCode\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"link\",\n            \"code\"\n          ],\n          \"x-go-enum-desc\": \"link RecoveryStrategyLink\\ncode RecoveryStrategyCode\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateRegistrationFlowBody\": {\n      \"description\": \"Update Registration Request Body\",\n      \"type\": \"object\"\n    },\n    \"updateRegistrationFlowWithCodeMethod\": {\n      \"description\": \"Update Registration Flow with Code Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"traits\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"code\": {\n          \"description\": \"The OTP Code sent to the user\",\n          \"type\": \"string\"\n        },\n        \"csrf_token\": {\n          \"description\": \"The CSRF Token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method to use\\n\\nThis field must be set to `code` when using the code method.\",\n          \"type\": \"string\"\n        },\n        \"resend\": {\n          \"description\": \"Resend restarts the flow with a new code\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"The identity's traits\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateRegistrationFlowWithOidcMethod\": {\n      \"description\": \"Update Registration Flow with OpenID Connect Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"provider\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"The CSRF Token\",\n          \"type\": \"string\"\n        },\n        \"id_token\": {\n          \"description\": \"IDToken is an optional id token provided by an OIDC provider\\n\\nIf submitted, it is verified using the OIDC provider's public key set and the claims are used to populate\\nthe OIDC credentials of the identity.\\nIf the OIDC provider does not store additional claims (such as name, etc.) in the IDToken itself, you can use\\nthe `traits` field to populate the identity's traits. Note, that Apple only includes the users email in the IDToken.\\n\\nSupported providers are\\nApple\\nGoogle\",\n          \"type\": \"string\"\n        },\n        \"id_token_nonce\": {\n          \"description\": \"IDTokenNonce is the nonce, used when generating the IDToken.\\nIf the provider supports nonce validation, the nonce will be validated against this value and is required.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method to use\\n\\nThis field must be set to `oidc` when using the oidc method.\",\n          \"type\": \"string\"\n        },\n        \"provider\": {\n          \"description\": \"The provider to register with\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"The identity traits\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        },\n        \"upstream_parameters\": {\n          \"description\": \"UpstreamParameters are the parameters that are passed to the upstream identity provider.\\n\\nThese parameters are optional and depend on what the upstream identity provider supports.\\nSupported parameters are:\\n`login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\\n`hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\\n`prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\\n`acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateRegistrationFlowWithPasskeyMethod\": {\n      \"description\": \"Update Registration Flow with Passkey Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"traits\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"CSRFToken is the anti-CSRF token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to \\\"passkey\\\" when trying to add, update, or remove a Passkey.\",\n          \"type\": \"string\"\n        },\n        \"passkey_register\": {\n          \"description\": \"Register a WebAuthn Security Key\\n\\nIt is expected that the JSON returned by the WebAuthn registration process\\nis included here.\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"The identity's traits\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateRegistrationFlowWithPasswordMethod\": {\n      \"description\": \"Update Registration Flow with Password Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"password\",\n        \"traits\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"The CSRF Token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method to use\\n\\nThis field must be set to `password` when using the password method.\",\n          \"type\": \"string\"\n        },\n        \"password\": {\n          \"description\": \"Password to sign the user up with\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"The identity's traits\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateRegistrationFlowWithProfileMethod\": {\n      \"description\": \"Update Registration Flow with Profile Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"traits\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"The Anti-CSRF Token\\n\\nThis token is only required when performing browser flows.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to profile when trying to update a profile.\",\n          \"type\": \"string\"\n        },\n        \"screen\": {\n          \"description\": \"Screen requests navigation to a previous screen.\\n\\nThis must be set to credential-selection to go back to the credential\\nselection screen.\\ncredential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential\\nprevious RegistrationScreenPrevious\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"credential-selection\",\n            \"previous\"\n          ],\n          \"x-go-enum-desc\": \"credential-selection RegistrationScreenCredentialSelection nolint:gosec // not a credential\\nprevious RegistrationScreenPrevious\"\n        },\n        \"traits\": {\n          \"description\": \"Traits\\n\\nThe identity's traits.\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateRegistrationFlowWithSamlMethod\": {\n      \"description\": \"Update registration flow using SAML\",\n      \"type\": \"object\",\n      \"required\": [\n        \"provider\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"The CSRF Token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method to use\\n\\nThis field must be set to `saml` when using the saml method.\",\n          \"type\": \"string\"\n        },\n        \"provider\": {\n          \"description\": \"The provider to register with\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"The identity traits\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateRegistrationFlowWithWebAuthnMethod\": {\n      \"description\": \"Update Registration Flow with WebAuthn Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"traits\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"CSRFToken is the anti-CSRF token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to \\\"webauthn\\\" when trying to add, update, or remove a webAuthn pairing.\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"The identity's traits\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        },\n        \"webauthn_register\": {\n          \"description\": \"Register a WebAuthn Security Key\\n\\nIt is expected that the JSON returned by the WebAuthn registration process\\nis included here.\",\n          \"type\": \"string\"\n        },\n        \"webauthn_register_displayname\": {\n          \"description\": \"Name of the WebAuthn Security Key to be Added\\n\\nA human-readable name for the security key which will be added.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"updateSettingsFlowBody\": {\n      \"description\": \"Update Settings Flow Request Body\",\n      \"type\": \"object\"\n    },\n    \"updateSettingsFlowWithLookupMethod\": {\n      \"description\": \"Update Settings Flow with Lookup Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"CSRFToken is the anti-CSRF token\",\n          \"type\": \"string\"\n        },\n        \"lookup_secret_confirm\": {\n          \"description\": \"If set to true will save the regenerated lookup secrets\",\n          \"type\": \"boolean\"\n        },\n        \"lookup_secret_disable\": {\n          \"description\": \"Disables this method if true.\",\n          \"type\": \"boolean\"\n        },\n        \"lookup_secret_regenerate\": {\n          \"description\": \"If set to true will regenerate the lookup secrets\",\n          \"type\": \"boolean\"\n        },\n        \"lookup_secret_reveal\": {\n          \"description\": \"If set to true will reveal the lookup secrets\",\n          \"type\": \"boolean\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to \\\"lookup\\\" when trying to add, update, or remove a lookup pairing.\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateSettingsFlowWithOidcMethod\": {\n      \"description\": \"Update Settings Flow with OpenID Connect Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\"\n      ],\n      \"properties\": {\n        \"flow\": {\n          \"description\": \"Flow ID is the flow's ID.\\n\\nin: query\",\n          \"type\": \"string\"\n        },\n        \"link\": {\n          \"description\": \"Link this provider\\n\\nEither this or `unlink` must be set.\\n\\ntype: string\\nin: body\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to profile when trying to update a profile.\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"The identity's traits\\n\\nin: body\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        },\n        \"unlink\": {\n          \"description\": \"Unlink this provider\\n\\nEither this or `link` must be set.\\n\\ntype: string\\nin: body\",\n          \"type\": \"string\"\n        },\n        \"upstream_parameters\": {\n          \"description\": \"UpstreamParameters are the parameters that are passed to the upstream identity provider.\\n\\nThese parameters are optional and depend on what the upstream identity provider supports.\\nSupported parameters are:\\n`login_hint` (string): The `login_hint` parameter suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session.\\n`hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.\\n`prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.\\n`acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateSettingsFlowWithPasskeyMethod\": {\n      \"description\": \"Update Settings Flow with Passkey Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"CSRFToken is the anti-CSRF token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to \\\"passkey\\\" when trying to add, update, or remove a webAuthn pairing.\",\n          \"type\": \"string\"\n        },\n        \"passkey_remove\": {\n          \"description\": \"Remove a WebAuthn Security Key\\n\\nThis must contain the ID of the WebAuthN connection.\",\n          \"type\": \"string\"\n        },\n        \"passkey_settings_register\": {\n          \"description\": \"Register a WebAuthn Security Key\\n\\nIt is expected that the JSON returned by the WebAuthn registration process\\nis included here.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"updateSettingsFlowWithPasswordMethod\": {\n      \"description\": \"Update Settings Flow with Password Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"password\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"CSRFToken is the anti-CSRF token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to password when trying to update a password.\",\n          \"type\": \"string\"\n        },\n        \"password\": {\n          \"description\": \"Password is the updated password\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateSettingsFlowWithProfileMethod\": {\n      \"description\": \"Update Settings Flow with Profile Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"traits\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"The Anti-CSRF Token\\n\\nThis token is only required when performing browser flows.\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to profile when trying to update a profile.\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"Traits\\n\\nThe identity's traits.\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateSettingsFlowWithSamlMethod\": {\n      \"description\": \"Update settings flow using SAML\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"The CSRF Token\",\n          \"type\": \"string\"\n        },\n        \"flow\": {\n          \"description\": \"Flow ID is the flow's ID.\\n\\nin: query\",\n          \"type\": \"string\"\n        },\n        \"link\": {\n          \"description\": \"Link this provider\\n\\nEither this or `unlink` must be set.\\n\\ntype: string\\nin: body\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to saml when trying to update a profile.\",\n          \"type\": \"string\"\n        },\n        \"traits\": {\n          \"description\": \"The identity's traits\\n\\nin: body\",\n          \"type\": \"object\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        },\n        \"unlink\": {\n          \"description\": \"Unlink this provider\\n\\nEither this or `link` must be set.\\n\\ntype: string\\nin: body\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"updateSettingsFlowWithTotpMethod\": {\n      \"description\": \"Update Settings Flow with TOTP Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"CSRFToken is the anti-CSRF token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to \\\"totp\\\" when trying to add, update, or remove a totp pairing.\",\n          \"type\": \"string\"\n        },\n        \"totp_code\": {\n          \"description\": \"ValidationTOTP must contain a valid TOTP based on the\",\n          \"type\": \"string\"\n        },\n        \"totp_unlink\": {\n          \"description\": \"UnlinkTOTP if true will remove the TOTP pairing,\\neffectively removing the credential. This can be used\\nto set up a new TOTP device.\",\n          \"type\": \"boolean\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateSettingsFlowWithWebAuthnMethod\": {\n      \"description\": \"Update Settings Flow with WebAuthn Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"CSRFToken is the anti-CSRF token\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method\\n\\nShould be set to \\\"webauthn\\\" when trying to add, update, or remove a webAuthn pairing.\",\n          \"type\": \"string\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        },\n        \"webauthn_register\": {\n          \"description\": \"Register a WebAuthn Security Key\\n\\nIt is expected that the JSON returned by the WebAuthn registration process\\nis included here.\",\n          \"type\": \"string\"\n        },\n        \"webauthn_register_displayname\": {\n          \"description\": \"Name of the WebAuthn Security Key to be Added\\n\\nA human-readable name for the security key which will be added.\",\n          \"type\": \"string\"\n        },\n        \"webauthn_remove\": {\n          \"description\": \"Remove a WebAuthn Security Key\\n\\nThis must contain the ID of the WebAuthN connection.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"updateVerificationFlowBody\": {\n      \"description\": \"Update Verification Flow Request Body\",\n      \"type\": \"object\"\n    },\n    \"updateVerificationFlowWithCodeMethod\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"method\"\n      ],\n      \"properties\": {\n        \"code\": {\n          \"description\": \"Code from the recovery email\\n\\nIf you want to submit a code, use this field, but make sure to _not_ include the email field, as well.\",\n          \"type\": \"string\"\n        },\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"description\": \"The email address to verify\\n\\nIf the email belongs to a valid account, a verifiation email will be sent.\\n\\nIf you want to notify the email address if the account does not exist, see\\nthe [notify_unknown_recipients flag](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation#attempted-verification-notifications)\\n\\nIf a code was already sent, including this field in the payload will invalidate the sent code and re-send a new code.\\n\\nformat: email\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method is the method that should be used for this verification flow\\n\\nAllowed values are `link` and `code`.\\nlink VerificationStrategyLink\\ncode VerificationStrategyCode\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"link\",\n            \"code\"\n          ],\n          \"x-go-enum-desc\": \"link VerificationStrategyLink\\ncode VerificationStrategyCode\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"updateVerificationFlowWithLinkMethod\": {\n      \"description\": \"Update Verification Flow with Link Method\",\n      \"type\": \"object\",\n      \"required\": [\n        \"email\",\n        \"method\"\n      ],\n      \"properties\": {\n        \"csrf_token\": {\n          \"description\": \"Sending the anti-csrf token is only required for browser login flows.\",\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"description\": \"Email to Verify\\n\\nNeeds to be set when initiating the flow. If the email is a registered\\nverification email, a verification link will be sent. If the email is not known,\\na email with details on what happened will be sent instead.\\n\\nformat: email\",\n          \"type\": \"string\"\n        },\n        \"method\": {\n          \"description\": \"Method is the method that should be used for this verification flow\\n\\nAllowed values are `link` and `code`\\nlink VerificationStrategyLink\\ncode VerificationStrategyCode\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"link\",\n            \"code\"\n          ],\n          \"x-go-enum-desc\": \"link VerificationStrategyLink\\ncode VerificationStrategyCode\"\n        },\n        \"transient_payload\": {\n          \"description\": \"Transient data to pass along to any webhooks\",\n          \"type\": \"object\"\n        }\n      }\n    },\n    \"verifiableIdentityAddress\": {\n      \"description\": \"VerifiableAddress is an identity's verifiable address\",\n      \"type\": \"object\",\n      \"required\": [\n        \"value\",\n        \"verified\",\n        \"via\",\n        \"status\"\n      ],\n      \"properties\": {\n        \"created_at\": {\n          \"description\": \"When this entry was created\",\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"example\": \"2014-01-01T23:28:56.782Z\"\n        },\n        \"id\": {\n          \"description\": \"The ID\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"status\": {\n          \"$ref\": \"#/definitions/identityVerifiableAddressStatus\"\n        },\n        \"updated_at\": {\n          \"description\": \"When this entry was last updated\",\n          \"type\": \"string\",\n          \"format\": \"date-time\",\n          \"example\": \"2014-01-01T23:28:56.782Z\"\n        },\n        \"value\": {\n          \"description\": \"The address value\\n\\nexample foo@user.com\",\n          \"type\": \"string\"\n        },\n        \"verified\": {\n          \"description\": \"Indicates if the address has already been verified\",\n          \"type\": \"boolean\",\n          \"example\": true\n        },\n        \"verified_at\": {\n          \"$ref\": \"#/definitions/nullTime\"\n        },\n        \"via\": {\n          \"description\": \"The delivery method\",\n          \"type\": \"string\",\n          \"enum\": [\n            \"email\",\n            \"sms\"\n          ],\n          \"example\": \"email\"\n        }\n      }\n    },\n    \"verificationFlow\": {\n      \"description\": \"Used to verify an out-of-band communication\\nchannel such as an email address or a phone number.\\n\\nFor more information head over to: https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation\",\n      \"type\": \"object\",\n      \"title\": \"A Verification Flow\",\n      \"required\": [\n        \"id\",\n        \"type\",\n        \"ui\",\n        \"state\"\n      ],\n      \"properties\": {\n        \"active\": {\n          \"description\": \"Active, if set, contains the registration method that is being used. It is initially\\nnot set.\",\n          \"type\": \"string\"\n        },\n        \"expires_at\": {\n          \"description\": \"ExpiresAt is the time (UTC) when the request expires. If the user still wishes to verify the address,\\na new request has to be initiated.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"id\": {\n          \"description\": \"ID represents the request's unique ID. When performing the verification flow, this\\nrepresents the id in the verify ui's query parameter: http://\\u003cselfservice.flows.verification.ui_url\\u003e?request=\\u003cid\\u003e\\n\\ntype: string\\nformat: uuid\",\n          \"type\": \"string\",\n          \"format\": \"uuid\"\n        },\n        \"issued_at\": {\n          \"description\": \"IssuedAt is the time (UTC) when the request occurred.\",\n          \"type\": \"string\",\n          \"format\": \"date-time\"\n        },\n        \"request_url\": {\n          \"description\": \"RequestURL is the initial URL that was requested from Ory Kratos. It can be used\\nto forward information contained in the URL's path or query for example.\",\n          \"type\": \"string\"\n        },\n        \"return_to\": {\n          \"description\": \"ReturnTo contains the requested return_to URL.\",\n          \"type\": \"string\"\n        },\n        \"state\": {\n          \"description\": \"State represents the state of this request:\\n\\nchoose_method: ask the user to choose a method (e.g. verify your email)\\nsent_email: the email has been sent to the user\\npassed_challenge: the request was successful and the verification challenge was passed.\"\n        },\n        \"transient_payload\": {\n          \"description\": \"TransientPayload is used to pass data from the verification flow to hooks and email templates\",\n          \"type\": \"object\"\n        },\n        \"type\": {\n          \"$ref\": \"#/definitions/selfServiceFlowType\"\n        },\n        \"ui\": {\n          \"$ref\": \"#/definitions/uiContainer\"\n        }\n      }\n    },\n    \"version\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"version\": {\n          \"description\": \"Version is the service's version.\",\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"webAuthnJavaScript\": {\n      \"type\": \"string\"\n    }\n  },\n  \"responses\": {\n    \"emptyResponse\": {\n      \"description\": \"Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 204.\"\n    },\n    \"identitySchemas\": {\n      \"description\": \"List Identity JSON Schemas Response\",\n      \"schema\": {\n        \"$ref\": \"#/definitions/identitySchemas\"\n      },\n      \"headers\": {\n        \"link\": {\n          \"type\": \"string\",\n          \"description\": \"The Link HTTP Header\\n\\nThe `Link` header contains a comma-delimited list of links to the following pages:\\n\\nfirst: The first page of results.\\nnext: The next page of results.\\nprev: The previous page of results.\\nlast: The last page of results.\\n\\nPages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted.\\n\\nThe header value may look like follows:\\n\\n\\u003c/clients?limit=5\\u0026offset=0\\u003e; rel=\\\"first\\\",\\u003c/clients?limit=5\\u0026offset=15\\u003e; rel=\\\"next\\\",\\u003c/clients?limit=5\\u0026offset=5\\u003e; rel=\\\"prev\\\",\\u003c/clients?limit=5\\u0026offset=20\\u003e; rel=\\\"last\\\"\"\n        },\n        \"x-total-count\": {\n          \"type\": \"integer\",\n          \"format\": \"int64\",\n          \"description\": \"The X-Total-Count HTTP Header\\n\\nThe `X-Total-Count` header contains the total number of items in the collection.\\n\\nDEPRECATED: This header will be removed eventually. Please use the `Link` header\\ninstead to check whether you are on the last page.\"\n        }\n      }\n    },\n    \"listCourierMessages\": {\n      \"description\": \"Paginated Courier Message List Response\",\n      \"schema\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"$ref\": \"#/definitions/message\"\n        }\n      },\n      \"headers\": {\n        \"link\": {\n          \"type\": \"string\",\n          \"description\": \"The Link HTTP Header\\n\\nThe `Link` header contains a comma-delimited list of links to the following pages:\\n\\nfirst: The first page of results.\\nnext: The next page of results.\\n\\nPages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted. Examples:\\n\\n\\u003c/admin/sessions?page_size=250\\u0026page_token={last_item_uuid}; rel=\\\"first\\\",/admin/sessions?page_size=250\\u0026page_token=\\u003e; rel=\\\"next\\\"\"\n        }\n      }\n    },\n    \"listIdentities\": {\n      \"description\": \"Paginated Identity List Response\",\n      \"schema\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"$ref\": \"#/definitions/identity\"\n        }\n      },\n      \"headers\": {\n        \"link\": {\n          \"type\": \"string\",\n          \"description\": \"The Link HTTP Header\\n\\nThe `Link` header contains a comma-delimited list of links to the following pages:\\n\\nfirst: The first page of results.\\nnext: The next page of results.\\nprev: The previous page of results.\\nlast: The last page of results.\\n\\nPages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted.\\n\\nThe header value may look like follows:\\n\\n\\u003c/clients?limit=5\\u0026offset=0\\u003e; rel=\\\"first\\\",\\u003c/clients?limit=5\\u0026offset=15\\u003e; rel=\\\"next\\\",\\u003c/clients?limit=5\\u0026offset=5\\u003e; rel=\\\"prev\\\",\\u003c/clients?limit=5\\u0026offset=20\\u003e; rel=\\\"last\\\"\"\n        },\n        \"x-total-count\": {\n          \"type\": \"integer\",\n          \"format\": \"int64\",\n          \"description\": \"The X-Total-Count HTTP Header\\n\\nThe `X-Total-Count` header contains the total number of items in the collection.\\n\\nDEPRECATED: This header will be removed eventually. Please use the `Link` header\\ninstead to check whether you are on the last page.\"\n        }\n      }\n    },\n    \"listIdentitySessions\": {\n      \"description\": \"List Identity Sessions Response\",\n      \"schema\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"$ref\": \"#/definitions/session\"\n        }\n      },\n      \"headers\": {\n        \"link\": {\n          \"type\": \"string\",\n          \"description\": \"The Link HTTP Header\\n\\nThe `Link` header contains a comma-delimited list of links to the following pages:\\n\\nfirst: The first page of results.\\nnext: The next page of results.\\nprev: The previous page of results.\\nlast: The last page of results.\\n\\nPages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted.\\n\\nThe header value may look like follows:\\n\\n\\u003c/clients?limit=5\\u0026offset=0\\u003e; rel=\\\"first\\\",\\u003c/clients?limit=5\\u0026offset=15\\u003e; rel=\\\"next\\\",\\u003c/clients?limit=5\\u0026offset=5\\u003e; rel=\\\"prev\\\",\\u003c/clients?limit=5\\u0026offset=20\\u003e; rel=\\\"last\\\"\"\n        },\n        \"x-total-count\": {\n          \"type\": \"integer\",\n          \"format\": \"int64\",\n          \"description\": \"The X-Total-Count HTTP Header\\n\\nThe `X-Total-Count` header contains the total number of items in the collection.\\n\\nDEPRECATED: This header will be removed eventually. Please use the `Link` header\\ninstead to check whether you are on the last page.\"\n        }\n      }\n    },\n    \"listMySessions\": {\n      \"description\": \"List My Session Response\",\n      \"schema\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"$ref\": \"#/definitions/session\"\n        }\n      },\n      \"headers\": {\n        \"link\": {\n          \"type\": \"string\",\n          \"description\": \"The Link HTTP Header\\n\\nThe `Link` header contains a comma-delimited list of links to the following pages:\\n\\nfirst: The first page of results.\\nnext: The next page of results.\\nprev: The previous page of results.\\nlast: The last page of results.\\n\\nPages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted.\\n\\nThe header value may look like follows:\\n\\n\\u003c/clients?limit=5\\u0026offset=0\\u003e; rel=\\\"first\\\",\\u003c/clients?limit=5\\u0026offset=15\\u003e; rel=\\\"next\\\",\\u003c/clients?limit=5\\u0026offset=5\\u003e; rel=\\\"prev\\\",\\u003c/clients?limit=5\\u0026offset=20\\u003e; rel=\\\"last\\\"\"\n        },\n        \"x-total-count\": {\n          \"type\": \"integer\",\n          \"format\": \"int64\",\n          \"description\": \"The X-Total-Count HTTP Header\\n\\nThe `X-Total-Count` header contains the total number of items in the collection.\\n\\nDEPRECATED: This header will be removed eventually. Please use the `Link` header\\ninstead to check whether you are on the last page.\"\n        }\n      }\n    },\n    \"listSessions\": {\n      \"description\": \"Session List Response\\n\\nThe response given when listing sessions in an administrative context.\",\n      \"schema\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"$ref\": \"#/definitions/session\"\n        }\n      },\n      \"headers\": {\n        \"link\": {\n          \"type\": \"string\",\n          \"description\": \"The Link HTTP Header\\n\\nThe `Link` header contains a comma-delimited list of links to the following pages:\\n\\nfirst: The first page of results.\\nnext: The next page of results.\\n\\nPages are omitted if they do not exist. For example, if there is no next page, the `next` link is omitted. Examples:\\n\\n\\u003c/admin/sessions?page_size=250\\u0026page_token={last_item_uuid}; rel=\\\"first\\\",/admin/sessions?page_size=250\\u0026page_token=\\u003e; rel=\\\"next\\\"\"\n        }\n      }\n    }\n  },\n  \"securityDefinitions\": {\n    \"oryAccessToken\": {\n      \"type\": \"apiKey\",\n      \"name\": \"Authorization\",\n      \"in\": \"header\"\n    }\n  },\n  \"x-forwarded-proto\": \"string\",\n  \"x-request-id\": \"string\"\n}"
  },
  {
    "path": "test/e2e/.gitignore",
    "content": "node_modules/\n/test-results/\n/playwright-report/\n/playwright/.cache/\n/playwright/playwright.env\n/playwright/kratos.config.json\n"
  },
  {
    "path": "test/e2e/.go-version",
    "content": "1.21.1\n"
  },
  {
    "path": "test/e2e/.npmignore",
    "content": "proxy\nprofiles\nhydra-login-consent\ncypress/videos\ncypress/screenshots\n*.log\n*.yml\n"
  },
  {
    "path": "test/e2e/cypress/fixtures/example.json",
    "content": "{\n  \"name\": \"Using fixtures to represent data\",\n  \"email\": \"hello@cypress.io\",\n  \"body\": \"Fixtures are a great way to mock data for responses to routes\"\n}\n"
  },
  {
    "path": "test/e2e/cypress/helpers/express.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APP_URL } from \"./index\"\n\nexport const routes = {\n  base: APP_URL,\n  login: APP_URL + \"/login\",\n  registration: APP_URL + \"/registration\",\n  settings: APP_URL + \"/settings\",\n  recovery: APP_URL + \"/recovery\",\n  verification: APP_URL + \"/verification\",\n  welcome: APP_URL + \"/welcome\",\n}\n"
  },
  {
    "path": "test/e2e/cypress/helpers/httpbin.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport * as oauth2 from \"./oauth2\"\n\nexport function checkToken(\n  client: oauth2.oAuth2Client,\n  scope: string[],\n  check: (token: any) => void,\n) {\n  cy.location(\"href\")\n    .should(\n      \"match\",\n      new RegExp(\n        \"https://ory-network-httpbin-ijakee5waq-ez.a.run.app/anything[?]code=.*\",\n      ),\n    )\n    .then(() => {\n      cy.get(\"body\")\n        .invoke(\"text\")\n        .then((text) => {\n          const result = JSON.parse(text)\n          const tokenParams = {\n            code: result.args.code,\n            redirect_uri:\n              \"https://ory-network-httpbin-ijakee5waq-ez.a.run.app/anything\",\n            scope: scope.join(\" \"),\n          }\n          oauth2\n            .getToken(\n              client.token_endpoint,\n              client.id,\n              client.secret,\n              \"authorization_code\",\n              tokenParams.code,\n              tokenParams.redirect_uri,\n              tokenParams.scope,\n            )\n            .then((res) => {\n              const token = res.body\n              check(token)\n            })\n        })\n    })\n}\n"
  },
  {
    "path": "test/e2e/cypress/helpers/index.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nexport const email = () => Math.random().toString(36) + \"@ory.sh\"\nexport const blockedEmail = () =>\n  Math.random().toString(36) + \"_blocked\" + \"@ory.sh\"\n\nexport const password = () => Math.random().toString(36)\n\nexport const assertVerifiableAddress =\n  ({ isVerified, email }) =>\n  (session) => {\n    const { identity } = session\n    expect(identity).to.have.property(\"verifiable_addresses\")\n    expect(identity.verifiable_addresses).to.have.length(1)\n\n    const address = identity.verifiable_addresses[0]\n    expect(address.id).to.not.be.empty\n    expect(address.verified).to.equal(isVerified)\n    expect(address.value).to.equal(email)\n\n    if (isVerified) {\n      expect(address.verified_at).to.not.be.null\n    } else {\n      expect(address).to.not.have.property(\"verified_at\")\n    }\n  }\n\nexport const assertRecoveryAddress =\n  ({ email }) =>\n  ({ identity }) => {\n    expect(identity).to.have.property(\"recovery_addresses\")\n    expect(identity.recovery_addresses).to.have.length(1)\n\n    const address = identity.recovery_addresses[0]\n    expect(address.id).to.not.be.empty\n    expect(address.value).to.equal(email)\n  }\n\nexport const parseHtml = (html: string) =>\n  new DOMParser().parseFromString(html, \"text/html\")\n\nexport const APP_URL = (\n  Cypress.env(\"app_url\") || \"http://localhost:4455\"\n).replace(/\\/$/, \"\")\n\nexport const MOBILE_URL = (\n  Cypress.env(\"mobile_url\") || \"http://localhost:19006\"\n).replace(/\\/$/, \"\")\nexport const SPA_URL = (\n  Cypress.env(\"react_url\") || \"http://localhost:4455\"\n).replace(/\\/$/, \"\")\nexport const KRATOS_ADMIN = (\n  Cypress.env(\"kratos_admin\") || \"http://localhost:4434\"\n)\n  .replace()\n  .replace(/\\/$/, \"\")\n\nexport const KRATOS_PUBLIC = (\n  Cypress.env(\"kratos_public\") || \"http://localhost:4433\"\n)\n  .replace()\n  .replace(/\\/$/, \"\")\n\nexport const MAIL_API = (\n  Cypress.env(\"mail_url\") || \"http://localhost:4437\"\n).replace(/\\/$/, \"\")\n\nexport const website = \"https://www.ory.sh/\"\n\nexport const gen = {\n  email,\n  blockedEmail,\n  password,\n  identity: () => ({ email: email(), password: password() }),\n  identityWithWebsite: () => ({\n    email: email(),\n    password: password(),\n    fields: { \"traits.website\": \"https://www.ory.sh\" },\n  }),\n}\n\n// Format is\nexport const verifyHrefPattern =\n  /^http:.*\\/self-service\\/verification\\?(((&|)code|(&|)token|(&|)flow)=([\\-a-zA-Z0-9]+)){2}$/\n\n// intervals define how long to wait for something,\nexport const pollInterval = 250 // how long to wait before retry\n\n// Adding 1+ second on top because MySQL doesn't do millisecs.\nexport const verifyLifespan = 5000 + 1000\nexport const privilegedLifespan = 5000 + 1000\n\nexport const appPrefix = (app) => `[data-testid=\"app-${app}\"] `\n\nexport const codeRegex = /(\\d{6})/\n\nexport function extractOTPCode(body: string): string | null {\n  const result = codeRegex.exec(body)\n  if (result != null && result.length > 0) {\n    return result[0]\n  }\n  return null\n}\n"
  },
  {
    "path": "test/e2e/cypress/helpers/oauth2.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport * as uuid from \"uuid\"\n\nexport type oAuth2Client = {\n  auth_endpoint: string\n  token_endpoint: string\n  id: string\n  secret: string\n  token_endpoint_auth_method: string\n  grant_types: string[]\n  response_types: string[]\n  scopes: string[]\n  callbacks: string[]\n}\n\nexport function getDefaultAuthorizeURL(client: oAuth2Client) {\n  const state = uuid.v4()\n  const nonce = uuid.v4()\n  return getAuthorizeURL(\n    client.auth_endpoint,\n    \"\",\n    client.id,\n    undefined,\n    nonce,\n    \"https://ory-network-httpbin-ijakee5waq-ez.a.run.app/anything\",\n    \"code\",\n    [\"offline\", \"openid\"],\n    state,\n    undefined,\n  )\n}\n\nexport function getAuthorizeURL(\n  auth_endpoint: string,\n  audience: string,\n  client_id: string,\n  max_age: string | undefined,\n  nonce: string,\n  redirect_uri: string,\n  response_type:\n    | \"code\"\n    | \"id_token\"\n    | \"id_token token\"\n    | \"code id_token\"\n    | \"code token\"\n    | \"code id_token token\",\n  scopes: string[],\n  state: string,\n  code_challenge?: string,\n): string {\n  const r = new URL(auth_endpoint)\n  r.searchParams.append(\"audience\", audience)\n  r.searchParams.append(\"client_id\", client_id)\n  if (max_age !== undefined) {\n    r.searchParams.append(\"max_age\", max_age)\n  }\n  r.searchParams.append(\"nonce\", nonce)\n  r.searchParams.append(\"prompt\", \"\")\n  r.searchParams.append(\"redirect_uri\", redirect_uri)\n  r.searchParams.append(\"response_type\", response_type)\n  r.searchParams.append(\"scope\", scopes.join(\" \"))\n  r.searchParams.append(\"state\", state)\n\n  code_challenge && r.searchParams.append(\"code_challenge\", code_challenge)\n  return r.toString()\n}\n\nexport function getToken(\n  token_endpoint: string,\n  client_id: string,\n  client_secret: string,\n  grant_type: \"authorization_code\",\n  code: string,\n  redirect_uri: string,\n  scope: string,\n) {\n  let urlEncodedData = \"\"\n  const urlEncodedDataPairs = []\n  urlEncodedDataPairs.push(\n    encodeURIComponent(\"grant_type\") + \"=\" + encodeURIComponent(grant_type),\n  )\n  urlEncodedDataPairs.push(\n    encodeURIComponent(\"code\") + \"=\" + encodeURIComponent(code),\n  )\n  urlEncodedDataPairs.push(\n    encodeURIComponent(\"redirect_uri\") + \"=\" + encodeURIComponent(redirect_uri),\n  )\n  urlEncodedDataPairs.push(\n    encodeURIComponent(\"scope\") + \"=\" + encodeURIComponent(scope),\n  )\n\n  urlEncodedData = urlEncodedDataPairs.join(\"&\").replace(/%20/g, \"+\")\n\n  return cy.request({\n    method: \"POST\",\n    url: token_endpoint,\n    form: true,\n    body: urlEncodedData,\n    headers: {\n      Accept: \"application/json\",\n      Authorization: \"Basic \" + btoa(client_id + \":\" + client_secret),\n    },\n  })\n}\n"
  },
  {
    "path": "test/e2e/cypress/helpers/react.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { SPA_URL } from \"./index\"\n\nexport const routes = {\n  base: SPA_URL,\n  login: SPA_URL + \"/login\",\n  registration: SPA_URL + \"/registration\",\n  settings: SPA_URL + \"/settings\",\n  recovery: SPA_URL + \"/recovery\",\n  verification: SPA_URL + \"/verification\",\n}\n"
  },
  {
    "path": "test/e2e/cypress/helpers/webhook.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fail } from \"assert\"\nimport { gen } from \".\"\n\nconst WEBHOOK_TARGET = \"https://webhook-target-gsmwn5ab4a-uc.a.run.app\"\nconst documentUrl = (key: string) => `${WEBHOOK_TARGET}/documents/${key}`\nconst jsonnet = Buffer.from(\"function(ctx) ctx\").toString(\"base64\")\n\nexport const testFlowWebhook = (\n  configSetup: (\n    hooks: Array<{ hook: string; config?: any }>,\n  ) => Cypress.Chainable<void>,\n  act: () => Cypress.Chainable<void> | void,\n) => {\n  const documentID = gen.password()\n  configSetup([\n    {\n      hook: \"web_hook\",\n      config: {\n        body: \"base64://\" + jsonnet,\n        url: documentUrl(documentID),\n        method: \"PUT\",\n      },\n    },\n  ])\n\n  const transient_payload = {\n    stuff: {\n      blub: [42, 3.14152],\n      fu: \"bar\",\n    },\n    consent: true,\n  }\n  cy.intercept(\n    \"POST\",\n    /.*\\/self-service\\/(registration|login|recovery|verification|settings).*/,\n    (req) => {\n      switch (typeof req.body) {\n        case \"string\":\n          req.body =\n            req.body +\n            \"&transient_payload=\" +\n            encodeURIComponent(JSON.stringify(transient_payload))\n          break\n        case \"object\":\n          req.body = {\n            ...req.body,\n            transient_payload,\n          }\n          break\n\n        default:\n          fail()\n          break\n      }\n      req.continue()\n    },\n  )\n\n  act()\n\n  cy.request(documentUrl(documentID)).then(({ body, status }) => {\n    const b = JSON.parse(body)\n    expect(status).to.equal(200)\n    expect(b.identity).is.not.undefined\n    expect(b.flow.transient_payload).to.deep.equal(transient_payload)\n  })\n}\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/code/login/error.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { MOBILE_URL, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Login error messages with code method\", () => {\n  ;[\n    {\n      route: express.login,\n      app: \"express\" as \"express\",\n      profile: \"code\",\n    },\n    {\n      route: react.login,\n      app: \"react\" as \"react\",\n      profile: \"code\",\n    },\n    {\n      route: MOBILE_URL + \"/Login\",\n      app: \"mobile\" as \"mobile\",\n      profile: \"code\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      const Selectors = {\n        mobile: {\n          identity: '[data-testid=\"identifier\"]',\n          code: '[data-testid=\"code\"]',\n        },\n        express: {\n          identity: '[data-testid=\"login-flow-code\"] input[name=\"identifier\"]',\n          code: 'input[name=\"code\"]',\n        },\n        react: {\n          identity: 'input[name=\"identifier\"]',\n          code: 'input[name=\"code\"]',\n        },\n      }\n\n      before(() => {\n        if (app !== \"mobile\") {\n          cy.proxy(app)\n        }\n      })\n\n      beforeEach(() => {\n        cy.useConfigProfile(profile)\n        cy.deleteMail()\n        cy.clearAllCookies()\n\n        const email = gen.email()\n        cy.wrap(email).as(\"email\")\n        cy.registerWithCode({ email, traits: { \"traits.tos\": 1 } })\n\n        cy.deleteMail()\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      it(\"should show error message when account identifier does not exist\", () => {\n        const email = gen.email()\n\n        cy.get(Selectors[app][\"identity\"]).type(email)\n        cy.submitCodeForm(app)\n\n        cy.get('[data-testid=\"ui/message/4000035\"]').should(\n          \"contain\",\n          \"This account does not exist or has not setup sign in with code.\",\n        )\n      })\n\n      it(\"should show error message when code is invalid\", () => {\n        cy.get(\"@email\").then((email) => {\n          cy.get(Selectors[app][\"identity\"]).clear().type(email.toString())\n        })\n\n        cy.submitCodeForm(app)\n\n        cy.get('[data-testid=\"ui/message/1010014\"]').should(\"exist\")\n\n        cy.get(Selectors[app][\"code\"]).type(\"123456\")\n        cy.submitCodeForm(app)\n\n        cy.get('[data-testid=\"ui/message/4010008\"]').should(\n          \"contain\",\n          \"The login code is invalid or has already been used. Please try again.\",\n        )\n      })\n\n      it(\"should show error message when identifier has changed\", () => {\n        cy.get(\"@email\").then((email) => {\n          cy.get(Selectors[app][\"identity\"]).type(email.toString())\n        })\n\n        cy.submitCodeForm(app)\n\n        if (app !== \"express\") {\n          cy.intercept(\"POST\", \"/self-service/login*\", (req) => {\n            req.body = {\n              ...req.body,\n              identifier: gen.email(),\n            }\n            req.continue()\n          }).as(\"login\")\n        } else {\n          cy.get(Selectors[app][\"identity\"])\n            .type(\"{selectall}{backspace}\", { force: true })\n            .type(gen.email(), { force: true })\n        }\n\n        cy.get(Selectors[app][\"code\"]).type(\"123456\")\n\n        cy.submitCodeForm(app)\n        if (app !== \"express\") {\n          cy.wait(\"@login\")\n        }\n        cy.get('[data-testid=\"ui/message/4000035\"]').should(\n          \"contain\",\n          \"This account does not exist or has not setup sign in with code.\",\n        )\n      })\n\n      it(\"should show error message when required fields are missing\", () => {\n        cy.removeAttribute([Selectors[app][\"identity\"]], \"required\")\n\n        cy.submitCodeForm(app)\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"field/identifier\"]').should(\n            \"contain\",\n            \"Property identifier is missing\",\n          )\n        } else {\n          cy.get('[data-testid=\"ui/message/4000002\"]').should(\n            \"contain\",\n            \"Property identifier is missing\",\n          )\n        }\n\n        cy.get(\"@email\").then((email) => {\n          cy.get(Selectors[app][\"identity\"]).type(email.toString())\n        })\n\n        cy.submitCodeForm(app)\n\n        cy.removeAttribute([Selectors[app][\"code\"]], \"required\")\n        cy.submitCodeForm(app)\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"field/code\"]').should(\n            \"contain\",\n            \"Property code is missing\",\n          )\n        } else {\n          cy.get('[data-testid=\"ui/message/4000002\"]').should(\n            \"contain\",\n            \"Property code is missing\",\n          )\n        }\n      })\n\n      it(\"should show error message when code is expired\", () => {\n        cy.updateConfigFile((config) => {\n          config.selfservice.methods.code = {\n            passwordless_enabled: true,\n            config: {\n              lifespan: \"1ns\",\n            },\n          }\n          return config\n        }).then(() => {\n          cy.visit(route)\n        })\n\n        cy.get(\"@email\").then((email) => {\n          cy.get(Selectors[app][\"identity\"]).type(email.toString())\n        })\n        cy.submitCodeForm(app)\n\n        cy.get(\"@email\").then((email) => {\n          cy.getLoginCodeFromEmail(email.toString()).then((code) => {\n            cy.get(Selectors[app][\"code\"]).type(code)\n          })\n        })\n\n        cy.submitCodeForm(app)\n\n        // the react app does not show the error message for 410 errors\n        // it just creates a new flow\n        if (app === \"express\") {\n          cy.get('[data-testid=\"ui/message/4010001\"]').should(\n            \"contain\",\n            \"The login flow expired\",\n          )\n        } else {\n          cy.get(Selectors[app][\"identity\"]).should(\"be.visible\")\n        }\n\n        cy.noSession()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/code/login/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Session } from \"@ory/kratos-client\"\nimport { gen, MOBILE_URL } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Login success with code method\", () => {\n  ;[\n    {\n      route: express.login,\n      app: \"express\" as \"express\",\n      profile: \"code\",\n    },\n    {\n      route: react.login,\n      app: \"react\" as \"react\",\n      profile: \"code\",\n    },\n    {\n      route: MOBILE_URL + \"/Login\",\n      app: \"mobile\" as \"mobile\",\n      profile: \"code\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      const Selectors = {\n        mobile: {\n          identity: '[data-testid=\"identifier\"]',\n          code: '[data-testid=\"code\"]',\n          submit: '[data-testid=\"field/method/code\"]',\n          resend: '[data-testid=\"field/resend/code\"]',\n        },\n        express: {\n          identity: '[data-testid=\"login-flow-code\"] input[name=\"identifier\"]',\n          code: 'input[name=\"code\"]',\n          submit: 'button[name=\"method\"][value=\"code\"]',\n          resend: 'button[name=\"resend\"]',\n        },\n        react: {\n          identity: 'input[name=\"identifier\"]',\n          code: 'input[name=\"code\"]',\n          submit: 'button[name=\"method\"][value=\"code\"]',\n          resend: 'button[name=\"resend\"]',\n        },\n      }\n\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        if (app !== \"mobile\") {\n          cy.proxy(app)\n        }\n        cy.setPostCodeRegistrationHooks([])\n        cy.setupHooks(\"login\", \"after\", \"code\", [])\n      })\n\n      beforeEach(() => {\n        const email = gen.email()\n        cy.wrap(email).as(\"email\")\n        cy.registerWithCode({ email, traits: { \"traits.tos\": 1 } })\n\n        cy.deleteMail()\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      it(\"should be able to sign in with code\", () => {\n        cy.get(\"@email\").then((email) => {\n          cy.get(Selectors[app][\"identity\"]).clear().type(email.toString())\n          cy.submitCodeForm(app)\n\n          cy.getLoginCodeFromEmail(email.toString()).then((code) => {\n            cy.get(Selectors[app][\"code\"]).type(code)\n\n            cy.get(Selectors[app][\"submit\"]).click()\n          })\n\n          cy.location(\"pathname\").should(\"not.contain\", \"login\")\n\n          if (app === \"mobile\") {\n            cy.get('[data-testid=\"session-token\"]').then((token) => {\n              cy.getSession({\n                expectAal: \"aal1\",\n                expectMethods: [\"code\"],\n                token: token.text(),\n              }).then((session) => {\n                cy.wrap(session).as(\"session\")\n              })\n            })\n\n            cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n            cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n          } else {\n            cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n              (session) => {\n                cy.wrap(session).as(\"session\")\n              },\n            )\n          }\n\n          cy.get<Session>(\"@session\").then(({ identity }) => {\n            expect(identity.id).to.not.be.empty\n            expect(identity.verifiable_addresses).to.have.length(1)\n            expect(identity.verifiable_addresses[0].status).to.equal(\n              \"completed\",\n            )\n            expect(identity.traits.email).to.equal(email)\n          })\n        })\n      })\n\n      it(\"should be able to sign in with code on account registered with password\", () => {\n        const email = gen.email()\n        // register account with password\n        cy.register({\n          email,\n          password: gen.password(),\n          fields: { \"traits.tos\": 1 },\n        })\n\n        cy.getVerificationCodeFromEmail(email).then((code) => {\n          expect(code).to.not.be.empty\n          cy.deleteMail()\n        })\n\n        cy.clearAllCookies()\n\n        cy.visit(route)\n\n        cy.get(Selectors[app][\"identity\"]).clear().type(email)\n        cy.submitCodeForm(app)\n\n        cy.getLoginCodeFromEmail(email).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n\n          cy.get(Selectors[app][\"submit\"]).click()\n        })\n\n        if (app === \"express\") {\n          cy.url().should(\"match\", /\\/welcome/)\n        } else {\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n        }\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.verifiable_addresses).to.have.length(1)\n          expect(identity.verifiable_addresses[0].status).to.equal(\"completed\")\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      it(\"should be able to resend login code\", () => {\n        cy.get(\"@email\").then((email) => {\n          cy.get(Selectors[app][\"identity\"]).clear().type(email.toString())\n          cy.submitCodeForm(app)\n\n          cy.getLoginCodeFromEmail(email.toString()).then((code) => {\n            cy.wrap(code).as(\"code1\")\n          })\n\n          cy.get(Selectors[app][\"resend\"]).click()\n\n          cy.getLoginCodeFromEmail(email.toString()).then((code) => {\n            cy.wrap(code).as(\"code2\")\n          })\n\n          cy.get(\"@code1\").then((code1) => {\n            cy.get(\"@code2\").then((code2) => {\n              expect(code1).to.not.equal(code2)\n            })\n          })\n\n          // attempt to submit code 1\n          cy.get(\"@code1\").then((code1) => {\n            cy.get(Selectors[app][\"code\"]).clear().type(code1.toString())\n          })\n\n          cy.get(Selectors[app][\"submit\"]).click()\n\n          cy.get(\"[data-testid='ui/message/4010008']\").contains(\n            \"The login code is invalid or has already been used\",\n          )\n\n          // attempt to submit code 2\n          cy.get(\"@code2\").then((code2) => {\n            cy.get(Selectors[app][\"code\"]).clear().type(code2.toString())\n          })\n\n          cy.get(Selectors[app][\"submit\"]).click()\n\n          if (app === \"express\") {\n            cy.url().should(\"match\", /\\/welcome/)\n          } else {\n            cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n          }\n\n          if (app === \"express\") {\n            cy.get('a[href*=\"sessions\"').click()\n          }\n\n          if (app === \"mobile\") {\n            cy.get('[data-testid=\"session-token\"]').then((token) => {\n              cy.getSession({\n                expectAal: \"aal1\",\n                expectMethods: [\"code\"],\n                token: token.text(),\n              }).then((session) => {\n                cy.wrap(session).as(\"session\")\n              })\n            })\n\n            cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n            cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n          } else {\n            cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n              (session) => {\n                cy.wrap(session).as(\"session\")\n              },\n            )\n          }\n\n          cy.get<Session>(\"@session\").then((session) => {\n            const { identity } = session\n            expect(identity.id).to.not.be.empty\n            expect(identity.verifiable_addresses).to.have.length(1)\n            expect(identity.verifiable_addresses[0].status).to.equal(\n              \"completed\",\n            )\n            expect(identity.traits.email).to.equal(email)\n          })\n        })\n      })\n\n      it(\"should be able to login to un-verfied email\", () => {\n        const email = gen.email()\n        const email2 = gen.email()\n\n        // Setup complex schema\n        cy.setIdentitySchema(\n          \"file://test/e2e/profiles/code/identity.complex.traits.schema.json\",\n        )\n\n        cy.registerWithCode({\n          email: email,\n          traits: {\n            \"traits.username\": Math.random().toString(36),\n            \"traits.email2\": email2,\n          },\n        })\n\n        // There are verification emails from the registration process in the inbox that we need to deleted\n        // for the assertions below to pass.\n        cy.deleteMail({ atLeast: 1 })\n\n        cy.visit(route)\n\n        cy.get(Selectors[app][\"identity\"]).clear().type(email2)\n        cy.submitCodeForm(app)\n\n        cy.getLoginCodeFromEmail(email2).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.get(Selectors[app][\"submit\"]).click()\n        })\n\n        if (app === \"express\") {\n          cy.url().should(\"match\", /\\/welcome/)\n        } else {\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n        }\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email2)\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then((session) => {\n          expect(session?.identity?.verifiable_addresses).to.have.length(2)\n          expect(session?.identity?.verifiable_addresses[0].status).to.equal(\n            \"completed\",\n          )\n          expect(session.identity.verifiable_addresses[1].status).to.equal(\n            \"completed\",\n          )\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/code/registration/error.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\nimport { UiNode } from \"@ory/kratos-client\"\nimport { gen, MOBILE_URL } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Registration error messages with code method\", () => {\n  ;[\n    {\n      route: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"code\",\n    },\n    {\n      route: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"code\",\n    },\n    {\n      route: MOBILE_URL + \"/Registration\",\n      app: \"mobile\" as \"mobile\",\n      profile: \"code\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      const Selectors = {\n        mobile: {\n          identifier: \"[data-testid='field/identifier']\",\n          email: \"[data-testid='field/traits.email']\",\n          tos: \"[data-testid='traits.tos']\",\n          code: \"[data-testid='field/code']\",\n        },\n        express: {\n          identifier:\n            \"[data-testid='registration-flow-code'] input[name='identifier']\",\n          email:\n            \"[data-testid='registration-flow-code'] input[name='traits.email']\",\n          tos: \"[data-testid='registration-flow-code'] [name='traits.tos'] + label\",\n          code: \"input[name='code']\",\n        },\n        react: {\n          identifier: \"input[name='identifier']\",\n          email: \"input[name='traits.email']\",\n          tos: \"[name='traits.tos'] + label\",\n          code: \"input[name='code']\",\n        },\n      }\n\n      before(() => {\n        if (app !== \"mobile\") {\n          cy.proxy(app)\n        }\n        cy.useConfigProfile(profile)\n        cy.deleteMail()\n      })\n\n      beforeEach(() => {\n        cy.deleteMail()\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      it(\"should show error message when code is invalid\", () => {\n        const email = gen.email()\n\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.get(Selectors[app][\"tos\"]).click()\n\n        cy.submitCodeForm(app)\n\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        cy.get(Selectors[app][\"code\"]).type(\"123456\")\n        cy.submitCodeForm(app)\n\n        cy.get('[data-testid=\"ui/message/4040003\"]').should(\n          \"contain\",\n          \"The registration code is invalid or has already been used. Please try again.\",\n        )\n      })\n\n      it(\"should show error message when traits have changed\", () => {\n        const email = gen.email()\n\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.get(Selectors[app][\"tos\"]).click()\n\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        if (app !== \"express\") {\n          // the mobile app doesn't render hidden fields in the DOM\n          // we need to replace the request body\n          cy.intercept(\"POST\", \"/self-service/registration*\", (req) => {\n            req.body = {\n              ...req.body,\n              \"traits.email\": \"changed-email@email.com\",\n            }\n            req.continue()\n          }).as(\"registration\")\n        } else {\n          cy.get(Selectors[app][\"email\"])\n            .type(\"{selectall}{backspace}\", { force: true })\n            .type(\"changed-email@email.com\", { force: true })\n        }\n\n        cy.get(Selectors[app][\"code\"]).type(\"123456\")\n        cy.submitCodeForm(app)\n\n        if (app !== \"express\") {\n          cy.wait(\"@registration\")\n        }\n\n        cy.get('[data-testid=\"ui/message/4000036\"]').should(\n          \"contain\",\n          \"The provided traits do not match the traits previously associated with this flow.\",\n        )\n      })\n\n      it(\"should show error message when required fields are missing\", () => {\n        cy.removeAttribute([Selectors[app][\"email\"]], \"required\")\n        const email = gen.email()\n\n        cy.get(Selectors[app][\"tos\"]).click()\n        cy.submitCodeForm(app)\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"field/traits.email\"]').should(\n            \"contain\",\n            \"Property email is missing\",\n          )\n        } else {\n          cy.get('[data-testid=\"ui/message/4000002\"]').should(\n            \"contain\",\n            \"Property email is missing\",\n          )\n        }\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.submitCodeForm(app)\n\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        cy.removeAttribute([Selectors[app][\"code\"]], \"required\")\n        cy.submitCodeForm(app)\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"field/code\"]').should(\n            \"contain\",\n            \"Property code is missing\",\n          )\n        } else {\n          cy.get('[data-testid=\"ui/message/4000002\"]').should(\n            \"contain\",\n            \"Property code is missing\",\n          )\n        }\n      })\n\n      it(\"should show error message when code is expired\", () => {\n        cy.updateConfigFile((config) => {\n          config.selfservice.methods.code.config.lifespan = \"1ns\"\n          return config\n        })\n        cy.visit(route)\n\n        const email = gen.email()\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.get(Selectors[app][\"tos\"]).click()\n\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        cy.getRegistrationCodeFromEmail(email).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.submitCodeForm(app)\n        })\n\n        // in the react spa app we don't show the 410 gone error. we create a new flow.\n        if (app === \"express\") {\n          cy.get('[data-testid=\"ui/message/4040001\"]').should(\n            \"contain\",\n            \"The registration flow expired\",\n          )\n        } else {\n          cy.get(Selectors[app][\"email\"]).should(\"be.visible\")\n        }\n\n        cy.noSession()\n\n        cy.updateConfigFile((config) => {\n          config.selfservice.methods.code.config.lifespan = \"1h\"\n          return config\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/code/registration/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Session } from \"@ory/kratos-client\"\nimport { gen, MOBILE_URL } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\nconst Selectors = {\n  mobile: {\n    identifier: \"[data-testid='field/identifier']\",\n    recoveryEmail: \"[data-testid='field/email']\",\n    email: \"[data-testid='traits.email']\",\n    email2: \"[data-testid='traits.email2']\",\n    tos: \"[data-testid='traits.tos']\",\n    username: \"[data-testid='traits.username']\",\n    code: \"[data-testid='field/code'] input\",\n    recoveryCode: \"[data-testid='code']\",\n    submitCode: \"[data-testid='field/method/code']\",\n    resendCode: \"[data-testid='field/resend/code']\",\n    submitRecovery: \"[data-testid='field/method/code']\",\n    codeHiddenMethod: \"[data-testid='field/method/code']\",\n  },\n  express: {\n    identifier: \"[data-testid='login-flow-code'] input[name='identifier']\",\n    recoveryEmail: \"input[name=email]\",\n    email: \"[data-testid='registration-flow-code'] input[name='traits.email']\",\n    email2:\n      \"[data-testid='registration-flow-code'] input[name='traits.email2']\",\n    tos: \"[data-testid='registration-flow-code'] [name='traits.tos'] + label\",\n    username:\n      \"[data-testid='registration-flow-code'] input[name='traits.username']\",\n    code: \"input[name='code']\",\n    recoveryCode: \"input[name=code]\",\n    submitRecovery: \"button[name=method][value=code]\",\n    submitCode: \"button[name='method'][value='code']\",\n    resendCode: \"button[name='resend'][value='code']\",\n    codeHiddenMethod: \"input[name='method'][value='code'][type='hidden']\",\n  },\n  react: {\n    identifier: \"input[name='identifier']\",\n    recoveryEmail: \"input[name=email]\",\n    email: \"input[name='traits.email']\",\n    email2: \"input[name='traits.email2']\",\n    tos: \"[name='traits.tos'] + label\",\n    username: \"input[name='traits.username']\",\n    code: \"input[name='code']\",\n    recoveryCode: \"input[name=code]\",\n    submitRecovery: \"button[name=method][value=code]\",\n    submitCode: \"button[name='method'][value='code']\",\n    resendCode: \"button[name='resend'][value='code']\",\n    codeHiddenMethod: \"input[name='method'][value='code'][type='hidden']\",\n  },\n}\n\ncontext(\"Registration success with code method\", () => {\n  ;[\n    {\n      route: express.registration,\n      login: express.login,\n      recovery: express.recovery,\n      app: \"express\" as \"express\",\n      profile: \"code\",\n    },\n    {\n      route: react.registration,\n      login: react.login,\n      recovery: react.recovery,\n      app: \"react\" as \"react\",\n      profile: \"code\",\n    },\n    {\n      route: MOBILE_URL + \"/Registration\",\n      login: MOBILE_URL + \"/Login\",\n      recovery: MOBILE_URL + \"/Recovery\",\n      app: \"mobile\" as \"mobile\",\n      profile: \"code\",\n    },\n  ].forEach(({ route, login, recovery, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        if (app !== \"mobile\") {\n          cy.proxy(app)\n        }\n      })\n\n      beforeEach(() => {\n        cy.deleteMail()\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      it(\"should be able to resend the registration code\", () => {\n        const email = gen.email()\n\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.get(Selectors[app][\"tos\"]).click()\n\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        cy.getRegistrationCodeFromEmail(email).then((code) =>\n          cy.wrap(code).as(\"code1\"),\n        )\n\n        cy.get(Selectors[app][\"codeHiddenMethod\"]).should(\"exist\")\n        cy.get(Selectors[app][\"resendCode\"]).click()\n\n        cy.getRegistrationCodeFromEmail(email).then((code) => {\n          cy.wrap(code).as(\"code2\")\n        })\n\n        cy.get(\"@code1\").then((code1) => {\n          // previous code should not work\n          cy.get(Selectors[app][\"code\"]).clear().type(code1.toString())\n\n          cy.submitCodeForm(app)\n          cy.get('[data-testid=\"ui/message/4040003\"]').should(\n            \"contain.text\",\n            \"The registration code is invalid or has already been used. Please try again.\",\n          )\n        })\n\n        cy.get(\"@code2\").then((code2) => {\n          cy.get(Selectors[app][\"code\"]).clear().type(code2.toString())\n          cy.submitCodeForm(app)\n        })\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.verifiable_addresses).to.have.length(1)\n          expect(identity.verifiable_addresses[0].status).to.equal(\"completed\")\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      it(\"should sign up and be logged in with session hook\", () => {\n        const email = gen.email()\n\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.get(Selectors[app][\"tos\"]).click()\n\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        cy.getRegistrationCodeFromEmail(email).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.get(Selectors[app][\"submitCode\"]).click()\n        })\n\n        if (app === \"express\") {\n          cy.url().should(\"match\", /\\/welcome/)\n        } else {\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n        }\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.verifiable_addresses).to.have.length(1)\n          expect(identity.verifiable_addresses[0].status).to.equal(\"completed\")\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      it(\"should be able to sign up without session hook\", () => {\n        cy.setPostCodeRegistrationHooks([])\n        const email = gen.email()\n\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.get(Selectors[app][\"tos\"]).click()\n\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        cy.getRegistrationCodeFromEmail(email).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.get(Selectors[app][\"submitCode\"]).click()\n        })\n\n        cy.visit(login)\n        cy.get(Selectors[app][\"identifier\"]).type(email)\n        cy.get(Selectors[app][\"submitCode\"]).click()\n\n        cy.getLoginCodeFromEmail(email).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.get(Selectors[app][\"submitCode\"]).click()\n        })\n\n        if (app === \"express\") {\n          cy.url().should(\"match\", /\\/welcome/)\n        } else {\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n        }\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.verifiable_addresses).to.have.length(1)\n          expect(identity.verifiable_addresses[0].status).to.equal(\"completed\")\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      it(\"should be able to recover account when registered with code\", () => {\n        if (app === \"mobile\") {\n          cy.log(\"WARNING: skipping test for mobile app\")\n          return\n        }\n        if (app === \"react\") {\n          // This test is flaky on React, so we skip it for now.\n          return\n        }\n        const email = gen.email()\n        cy.registerWithCode({ email, traits: { \"traits.tos\": 1 } })\n\n        cy.clearAllCookies()\n        cy.visit(recovery)\n\n        cy.get(Selectors[app][\"recoveryEmail\"]).type(email)\n        cy.get(Selectors[app][\"submitRecovery\"]).click()\n\n        cy.recoveryEmailWithCode({ expect: { email, enterCode: false } }).then(\n          () => {\n            cy.get<string>(\"@recoveryCode\").then((code) => {\n              cy.get(Selectors[app][\"recoveryCode\"]).type(code)\n            })\n          },\n        )\n\n        cy.get(Selectors[app][\"submitRecovery\"]).click()\n\n        cy.getSession({ expectAal: \"aal1\" }).then((session) => {\n          cy.wrap(session).as(\"session\")\n        })\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      // Try keep this test as the last one, as it updates the identity schema.\n      it(\"should be able to use multiple identifiers to signup with and sign in to\", () => {\n        cy.setPostCodeRegistrationHooks([\n          {\n            hook: \"session\",\n          },\n          {\n            hook: \"show_verification_ui\",\n          },\n        ])\n\n        // Setup complex schema\n        cy.setIdentitySchema(\n          \"file://test/e2e/profiles/code/identity.complex.traits.schema.json\",\n        )\n\n        cy.visit(route)\n\n        cy.get(Selectors[app][\"username\"]).type(Math.random().toString(36))\n\n        const email = gen.email()\n        cy.get(Selectors[app][\"email\"]).type(email)\n\n        const email2 = gen.email()\n        cy.get(Selectors[app][\"email2\"]).type(email2)\n\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        // intentionally use email 1 to sign up for the account\n        cy.getRegistrationCodeFromEmail(email, { expectedCount: 1 }).then(\n          (code) => {\n            cy.get(Selectors[app][\"code\"]).type(code)\n            cy.get(Selectors[app][\"submitCode\"]).click()\n          },\n        )\n        cy.get('[data-testid=\"ui/message/1080003\"]').should(\"be.visible\")\n\n        if (app === \"mobile\") {\n          cy.visit(MOBILE_URL + \"/Home\")\n          cy.get('*[data-testid=\"logout\"]').click()\n        } else {\n          cy.logout()\n        }\n\n        // There are verification emails from the registration process in the inbox that we need to deleted\n        // for the assertions below to pass.\n        cy.deleteMail({ atLeast: 1 })\n\n        // Attempt to sign in with email 2 (should fail)\n        cy.visit(login)\n        cy.get(Selectors[app][\"identifier\"]).type(email2)\n\n        cy.get(Selectors[app][\"submitCode\"]).click()\n\n        cy.getLoginCodeFromEmail(email2, {\n          expectedCount: 1,\n        }).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.get(Selectors[app][\"submitCode\"]).click()\n        })\n        if (app === \"express\") {\n          cy.url().should(\"match\", /\\/welcome/)\n        } else {\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n        }\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.verifiable_addresses).to.have.length(2)\n          expect(\n            identity.verifiable_addresses.filter((v) => v.value === email)[0]\n              .status,\n          ).to.equal(\"completed\")\n          expect(\n            identity.verifiable_addresses.filter((v) => v.value === email2)[0]\n              .status,\n          ).to.equal(\"completed\")\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/error/ui.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { appPrefix } from \"../../../../helpers\"\n\n// playwright:migrated\ndescribe(\"Handling self-service error flows\", () => {\n  ;[\n    {\n      route: express.base,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n    },\n    {\n      route: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ route, app, profile }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      // playwright:migrated\n      it(\"should show the error\", () => {\n        cy.visit(`${route}/error?id=stub:500`, {\n          failOnStatusCode: false,\n        })\n\n        if (app === \"express\") {\n          cy.get(`${appPrefix(app)} [data-testid=\"ui/error/message\"]`).should(\n            \"contain.text\",\n            \"This is a stub error.\",\n          )\n        } else {\n          cy.get(`${appPrefix(app)}code`).should(\n            \"contain.text\",\n            \"This is a stub error.\",\n          )\n        }\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/login/error.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\n// playwright:migrated\ndescribe(\"Basic email profile with failing login flows\", () => {\n  ;[\n    {\n      route: express.login,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n    },\n    {\n      route: react.login,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      // playwright:migrated\n      it(\"fails when CSRF cookies are missing\", () => {\n        cy.get(`${appPrefix(app)}input[name=\"identifier\"]`).type(\n          \"i-do-not-exist\",\n        )\n        cy.get('input[name=\"password\"]').type(\"invalid-password\")\n\n        cy.shouldHaveCsrfError({ app })\n      })\n\n      // playwright:migrated\n      it(\"fails when a disallowed return_to url is requested\", () => {\n        cy.shouldErrorOnDisallowedReturnTo(\n          route + \"?return_to=https://not-allowed\",\n          { app },\n        )\n      })\n\n      // playwright:migrated - partially\n      describe(\"shows validation errors when invalid signup data is used\", () => {\n        // playwright:migrated\n        it(\"should show an error when the identifier is missing\", () => {\n          // the browser will prevent the form from submitting if the fields are empty since they are required\n          // here we just remove the required attribute to make the form submit\n          cy.removeAttribute(\n            ['input[name=\"identifier\"]', 'input[name=\"password\"]'],\n            \"required\",\n          )\n          cy.submitPasswordForm()\n          cy.get('*[data-testid=\"ui/message/4000002\"]').should(\n            \"contain.text\",\n            \"Property identifier is missing\",\n          )\n          cy.get('*[data-testid=\"ui/message/4000002\"]').should(\n            \"contain.text\",\n            \"Property password is missing\",\n          )\n        })\n\n        // playwright:migrated\n        it(\"should show an error when the password is missing\", () => {\n          const identity = gen.email()\n          cy.get('input[name=\"identifier\"]')\n            .type(identity)\n            .should(\"have.value\", identity)\n\n          // the browser will prevent the form from submitting if the fields are empty since they are required\n          // here we just remove the required attribute to make the form submit\n          cy.removeAttribute(['input[name=\"password\"]'], \"required\")\n\n          cy.submitPasswordForm()\n          cy.get('*[data-testid^=\"ui/message/\"]')\n            .invoke(\"text\")\n            .then((text) => {\n              expect(text.trim()).to.be.oneOf([\n                \"length must be >= 1, but got 0\",\n                \"Property password is missing.\",\n              ])\n            })\n        })\n\n        // playwright:migrated\n        it(\"should show fail to sign in\", () => {\n          cy.get('input[name=\"identifier\"]').type(\"i-do-not-exist\")\n          cy.get('input[name=\"password\"]').type(\"invalid-password\")\n\n          cy.submitPasswordForm()\n          cy.get('*[data-testid=\"ui/message/4000006\"]').should(\n            \"contain.text\",\n            \"credentials are invalid\",\n          )\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/login/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APP_URL, appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\n// playwright:migrated\ndescribe(\"Basic email profile with succeeding login flows\", () => {\n  const email = gen.email()\n  const password = gen.password()\n\n  before(() => {\n    cy.registerApi({ email, password, fields: { \"traits.website\": website } })\n  })\n  ;[\n    {\n      route: express.login,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n    },\n    {\n      route: react.login,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.useConfigProfile(profile)\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      // playwright:migrated\n      it(\"should sign in and be logged in\", () => {\n        cy.get(`${appPrefix(app)}input[name=\"identifier\"]`).type(email)\n        cy.get('input[name=\"password\"]').type(password)\n        cy.submitPasswordForm()\n        cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n\n        cy.getSession().should((session) => {\n          const { identity } = session\n          expect(identity.id).to.not.be.empty\n          expect(identity.schema_id).to.equal(\"default\")\n          expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)\n          expect(identity.traits.website).to.equal(website)\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      // playwright:migrated\n      it(\"should sign in with case insensitive identifier surrounded by whitespace\", () => {\n        cy.get('input[name=\"identifier\"]').type(\n          \"  \" + email.toUpperCase() + \"  \",\n        )\n        cy.get('input[name=\"password\"]').type(password)\n        cy.submitPasswordForm()\n        cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n\n        cy.getSession().should((session) => {\n          const { identity } = session\n          expect(identity.id).to.not.be.empty\n          expect(identity.schema_id).to.equal(\"default\")\n          expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)\n          expect(identity.traits.website).to.equal(website)\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      // playwright:migrated\n      it(\"should sign in and be redirected\", () => {\n        cy.browserReturnUrlOry()\n        cy.visit(route + \"?return_to=https://www.example.org/\")\n\n        cy.get('input[name=\"identifier\"]').type(email.toUpperCase())\n        cy.get('input[name=\"password\"]').type(password)\n        cy.submitPasswordForm()\n\n        cy.url().should(\"eq\", \"https://www.example.org/\")\n      })\n    })\n  })\n\n  // playwright:migrated\n  describe(\"for app express handle return_to correctly for expired flows\", () => {\n    before(() => {\n      cy.proxy(\"express\")\n      cy.useConfigProfile(\"email\")\n\n      cy.browserReturnUrlOry()\n    })\n\n    beforeEach(() => {\n      cy.clearAllCookies()\n    })\n\n    // playwright:migrated\n    it(\"should redirect to return_to when retrying expired flow\", () => {\n      cy.shortLoginLifespan()\n      cy.wait(500)\n\n      cy.visit(express.login + \"?return_to=https://www.example.org/\")\n\n      cy.longLoginLifespan()\n\n      cy.get(appPrefix(\"express\") + 'input[name=\"identifier\"]').type(\n        email.toUpperCase(),\n      )\n      cy.get('input[name=\"password\"]').type(password)\n\n      cy.submitPasswordForm()\n      cy.get('[data-testid=\"ui/message/4010001\"]').should(\n        \"contain.text\",\n        \"The login flow expired\",\n      )\n\n      // try again with long lifespan set\n      cy.get('input[name=\"identifier\"]').type(email.toUpperCase())\n      cy.get('input[name=\"password\"]').type(password)\n      cy.submitPasswordForm()\n\n      // check that redirection has happened\n      cy.url().should(\"eq\", \"https://www.example.org/\")\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/login/ui.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { appPrefix } from \"../../../../helpers\"\n\n// playwright:migrated\ncontext(\"UI tests using the email profile\", () => {\n  ;[\n    {\n      route: express.login,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n    },\n    {\n      route: react.login,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.visit(route)\n      })\n\n      // playwright:migrated\n      it(\"should use the json schema titles\", () => {\n        cy.get(`${appPrefix(app)}input[name=\"identifier\"]`)\n          .parent()\n          .should(\"contain.text\", \"Your E-Mail\")\n\n        cy.get('input[name=\"password\"]')\n          .parentsUntil(\"label\")\n          .should(\"contain.text\", \"Password\")\n        cy.get('button[value=\"password\"]').should(\"contain.text\", \"Sign in\")\n      })\n\n      // playwright:migrated\n      it(\"clicks the log in link\", () => {\n        cy.get('a[href*=\"registration\"]').click()\n        cy.location(\"pathname\").should(\"include\", \"registration\")\n\n        if (app === \"express\") {\n          cy.location(\"search\").should(\"not.be.empty\")\n        }\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/logout/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Testing logout flows\", () => {\n  ;[\n    {\n      route: express.login,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n      settings: express.settings,\n      welcome: express.welcome,\n    },\n    {\n      route: react.login,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n      settings: react.settings,\n      welcome: \"\",\n    },\n  ].forEach(({ route, profile, app, settings, welcome }) => {\n    describe(`for app ${app}`, () => {\n      let email: string\n      let password: string\n\n      before(() => {\n        cy.proxy(app)\n\n        email = gen.email()\n        password = gen.password()\n\n        cy.useConfigProfile(profile)\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.login({ email, password, cookieUrl: route })\n        cy.visit(route)\n      })\n\n      it(\"should sign out and be able to sign in again\", () => {\n        cy.getSession()\n        cy.getCookie(\"ory_kratos_session\").should(\"not.be.null\")\n        if (app === \"express\") {\n          cy.get(\n            `${appPrefix(app)} [data-testid=\"logout\"] a:not(.disabled)`,\n          ).click()\n        } else {\n          cy.get(\n            `${appPrefix(app)} [data-testid=\"logout\"]:not(.disabled)`,\n          ).click()\n        }\n        cy.getCookie(\"ory_kratos_session\").should(\"be.null\")\n        cy.noSession()\n        cy.url().should(\"include\", \"/login\")\n      })\n\n      it(\"should be able to sign out on settings page\", () => {\n        if (app === \"react\") {\n          return\n        }\n        cy.sessionRequiresNo2fa()\n        cy.useLaxAal()\n\n        cy.getSession({ expectAal: \"aal1\" })\n        cy.getCookie(\"ory_kratos_session\").should(\"not.be.null\")\n\n        cy.visit(settings, {\n          qs: {\n            return_to: \"https://www.example.org\",\n          },\n        })\n\n        cy.get(\"a[href*='logout']\").click()\n        cy.location(\"host\").should(\"eq\", \"www.example.org\")\n      })\n\n      it(\"should be able to sign out on welcome page\", () => {\n        if (app === \"react\") {\n          return\n        }\n        cy.sessionRequiresNo2fa()\n        cy.useLaxAal()\n\n        cy.getSession({ expectAal: \"aal1\" })\n\n        cy.visit(welcome, {\n          qs: {\n            return_to: \"https://www.example.org\",\n          },\n        })\n\n        cy.get(\"a[href*='logout']\").click()\n        cy.location(\"host\").should(\"eq\", \"www.example.org\")\n      })\n\n      it(\"should be able to sign out at 2fa page\", () => {\n        if (app === \"react\") {\n          return\n        }\n        cy.useLookupSecrets(true)\n        cy.sessionRequires2fa()\n        cy.getSession({ expectAal: \"aal1\" })\n        cy.getCookie(\"ory_kratos_session\").should(\"not.be.null\")\n\n        // add 2fa to account\n        cy.visit(settings)\n        cy.get(\n          appPrefix(app) + 'button[name=\"lookup_secret_regenerate\"]',\n        ).click()\n        cy.get('button[name=\"lookup_secret_confirm\"]').click()\n        cy.expectSettingsSaved()\n\n        cy.logout()\n        cy.visit(route, {\n          qs: {\n            return_to: \"https://www.example.org\",\n          },\n        })\n\n        cy.get('[name=\"identifier\"]').clear().type(email)\n\n        cy.reauth({\n          expect: { email, success: false },\n          type: { password: password },\n        })\n\n        cy.get(\"a[href*='logout']\").click()\n\n        cy.location(\"host\").should(\"eq\", \"www.example.org\")\n        cy.useLookupSecrets(false)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/registration/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ndescribe(\"Registration failures with email profile\", () => {\n  ;[\n    {\n      route: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n    },\n    {\n      route: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n        cy.updateConfigFile((config) => {\n          config.selfservice.flows.registration.login_hints = true\n          return config\n        })\n      })\n\n      beforeEach(() => {\n        cy.visit(route)\n      })\n\n      const identity = gen.email()\n      const password = gen.password()\n\n      it(\"fails when CSRF cookies are missing\", () => {\n        cy.get(`${appPrefix(app)} input[name=\"traits.website\"]`).type(\n          \"https://www.ory.sh\",\n        )\n        cy.get('input[name=\"traits.email\"]')\n          .type(identity)\n          .should(\"have.value\", identity)\n        cy.get('input[name=\"password\"]')\n          .type(\"12345678\")\n          .should(\"have.value\", \"12345678\")\n\n        cy.shouldHaveCsrfError({ app })\n      })\n\n      it(\"fails when a disallowed return_to url is requested\", () => {\n        cy.shouldErrorOnDisallowedReturnTo(\n          route + \"?return_to=https://not-allowed\",\n          { app },\n        )\n      })\n\n      describe(\"show errors when invalid signup data is used\", () => {\n        it(\"should show an error when the password has leaked before\", () => {\n          cy.get('input[name=\"traits.website\"]').type(\"https://www.ory.sh\")\n          cy.get('input[name=\"traits.email\"]')\n            .type(identity)\n            .should(\"have.value\", identity)\n          cy.get('input[name=\"password\"]')\n            .type(\"12345678\")\n            .should(\"have.value\", \"12345678\")\n\n          cy.submitPasswordForm()\n          cy.get('[data-testid=\"ui/message/4000034\"]').should(\n            \"contain.text\",\n            \"data breaches\",\n          )\n        })\n\n        it(\"should show an error when the password is too similar\", () => {\n          cy.get('input[name=\"traits.website\"]').type(\"https://www.ory.sh\")\n          cy.get('input[name=\"traits.email\"]').type(identity)\n          cy.get('input[name=\"password\"]').type(identity)\n\n          cy.submitPasswordForm()\n          cy.get('[data-testid=\"ui/message/4000031\"]').should(\n            \"contain.text\",\n            \"too similar\",\n          )\n        })\n\n        it(\"should show an error when the password is empty\", () => {\n          cy.get('input[name=\"traits.website\"]').type(\"https://www.ory.sh\")\n          cy.get('input[name=\"traits.email\"]').type(identity)\n\n          // the browser will prevent the form from being submitted if the input field is required\n          // we should remove the required attribute to simulate the data not being sent\n          cy.removeAttribute(['input[name=\"password\"]'], \"required\")\n\n          cy.submitPasswordForm()\n          cy.get('*[data-testid^=\"ui/message/\"]')\n            .invoke(\"text\")\n            .then((text) => {\n              expect(text.trim()).to.be.oneOf([\n                \"length must be >= 1, but got 0\",\n                \"Property password is missing.\",\n              ])\n            })\n        })\n\n        it(\"should show an error when the email is empty\", () => {\n          cy.get('input[name=\"traits.website\"]').type(\"https://www.ory.sh\")\n          cy.get('input[name=\"password\"]').type(password)\n\n          // the browser will prevent the form from being submitted if the input field is required\n          // we should remove the required attribute to simulate the data not being sent\n          cy.removeAttribute(['input[name=\"traits.email\"]'], \"required\")\n\n          cy.submitPasswordForm()\n          cy.get('*[data-testid^=\"ui/message/\"]')\n            .invoke(\"text\")\n            .then((text) => {\n              expect(text.trim()).to.be.oneOf([\n                '\"\" is not valid \"email\"',\n                \"length must be >= 3, but got 0\",\n                \"Property email is missing.\",\n              ])\n            })\n        })\n\n        it(\"should show an error when the email is not an email\", () => {\n          cy.get('input[name=\"traits.website\"]').type(\"https://www.ory.sh\")\n          cy.get('input[name=\"password\"]').type(password)\n\n          // the browser will prevent the form from being submitted if the input data doesn't conform to the input field type\n          // in this case an invalid email will prevent the form from being submitted by the browser\n          // we should remove it to ensure kratos is validating the payload\n          cy.get('input[name=\"traits.email\"]').then(($el) =>\n            $el.removeAttr(\"type\"),\n          )\n          cy.get('input[name=\"traits.email\"]').type(\"not-an-email\")\n\n          cy.submitPasswordForm()\n          cy.get(\n            '*[data-testid=\"ui/message/4000040\"], *[data-testid=\"ui/message/4000002\"]',\n          ).should(\"exist\")\n        })\n\n        it(\"should show a missing indicator if no fields are set\", () => {\n          // the browser will prevent the form from being submitted if the input field is required\n          // we should remove the required attribute to simulate the data not being sent\n          cy.removeAttribute(\n            [\n              'input[name=\"traits.email\"]',\n              'input[name=\"traits.website\"]',\n              'input[name=\"password\"]',\n            ],\n            \"required\",\n          )\n\n          cy.submitPasswordForm()\n          cy.get(\n            '*[data-testid=\"ui/message/4000001\"], *[data-testid=\"ui/message/4000002\"]',\n          ).should(\"exist\")\n        })\n\n        it(\"should show an error when the website is not a valid URI\", () => {\n          cy.get('input[name=\"traits.website\"]')\n            .type(\"1234\")\n            .then(($input) => {\n              expect(\n                ($input[0] as HTMLInputElement).validationMessage,\n              ).to.contain(\"URL\")\n            })\n        })\n\n        it(\"should show an error when the website is too short\", () => {\n          // the browser will prevent the form from being submitted if the input field is required\n          // we should remove the required attribute to simulate the data not being sent\n          cy.removeAttribute(\n            ['input[name=\"traits.email\"]', 'input[name=\"password\"]'],\n            \"required\",\n          )\n\n          cy.get('input[name=\"traits.website\"]').type(\"http://s\")\n\n          cy.submitPasswordForm()\n          cy.get('[data-testid=\"ui/message/4000003\"]').should(\n            \"contain.text\",\n            \"length must be >= 10\",\n          )\n        })\n\n        it(\"should show an error when required params are missing\", () => {\n          // the browser will prevent the form from being submitted if the input field is required\n          // we should remove it from the DOM entirely to simulate the data not being sent\n          cy.get('input[name=\"traits.website\"]').then(($el) => $el.remove())\n          cy.get('input[name=\"traits.email\"]').then(($el) => $el.remove())\n          cy.get('input[name=\"password\"]').then(($el) => $el.remove())\n\n          cy.submitPasswordForm()\n          cy.get('[data-testid=\"ui/message/4000002\"]').should(\n            \"contain.text\",\n            \"Property website is missing.\",\n          )\n          cy.get('[data-testid=\"ui/message/4000002\"]').should(\n            \"contain.text\",\n            \"Property email is missing.\",\n          )\n          cy.get('[data-testid=\"ui/message/4000002\"]').should(\n            \"contain.text\",\n            \"Property password is missing.\",\n          )\n        })\n\n        it(\"should show an error when the age is too high\", () => {\n          // the browser will prevent the form from being submitted if the input field is required\n          // we should remove the required attribute to simulate the data not being sent\n          cy.removeAttribute(\n            [\n              'input[name=\"traits.email\"]',\n              'input[name=\"traits.website\"]',\n              'input[name=\"password\"]',\n            ],\n            \"required\",\n          )\n\n          cy.get('input[name=\"traits.age\"]').type(\"600\")\n\n          cy.submitPasswordForm()\n          cy.get('[data-testid=\"ui/message/4000020\"]').should(\n            \"contain.text\",\n            \"must be <= 300 but found 600\",\n          )\n        })\n\n        it(\"should show a hint for existing account\", () => {\n          const email = gen.email()\n          const password = gen.password()\n\n          cy.registerApi({\n            email,\n            password,\n            fields: { \"traits.website\": website },\n          })\n\n          cy.get('input[name=\"traits.email\"]').type(email)\n          cy.get('input[name=\"password\"]').type(password)\n          cy.get('input[name=\"traits.website').type(website)\n          cy.get('input[name=\"traits.age\"]').type(`30`)\n          cy.submitPasswordForm()\n          cy.get('[data-testid=\"ui/message/4000028\"]').should(\n            \"contain.text\",\n            \"You tried signing in with \" +\n              email +\n              \" which is already in use by another account. You can sign in using your password.\",\n          )\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/registration/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, APP_URL, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Registration success with email profile\", () => {\n  ;[\n    {\n      route: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n    },\n    {\n      route: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.deleteMail()\n        cy.clearAllCookies()\n        cy.visit(route)\n        cy.enableVerification()\n        if (app === \"express\") {\n          cy.enableVerificationUIAfterRegistration(\"password\")\n        }\n      })\n\n      it(\"should sign up and be logged in\", () => {\n        const email = gen.email()\n        const password = gen.password()\n        const website = \"https://www.example.org/\"\n        const age = 30\n\n        cy.get(appPrefix(app) + 'input[name=\"traits\"]').should(\"not.exist\")\n        cy.get('input[name=\"traits.email\"]').type(email)\n        cy.get('input[name=\"password\"]').type(password)\n        cy.get('input[name=\"traits.website').type(website)\n        cy.get('input[name=\"traits.age\"]').type(`${age}`)\n        cy.get('[type=\"checkbox\"][name=\"traits.tos\"]').click({ force: true })\n\n        cy.submitPasswordForm()\n\n        cy.url().should(\"contain\", \"verification\")\n        cy.getVerificationCodeFromEmail(email).then((code) => {\n          cy.get(\"input[name=code]\").type(code)\n          cy.get(\"button[name=method][value=code]\").click()\n        })\n\n        cy.get('[data-testid=\"ui/message/1080002\"]').should(\n          \"have.text\",\n          \"You successfully verified your email address.\",\n        )\n\n        cy.get(\"[data-testid='node/anchor/continue']\").click()\n\n        if (app === \"express\") {\n          cy.get('a[href*=\"sessions\"').click()\n        }\n        cy.get(\"pre\").should(\"contain.text\", email)\n\n        cy.getSession().should((session) => {\n          const { identity } = session\n          expect(identity.id).to.not.be.empty\n          expect(identity.verifiable_addresses).to.have.length(1)\n          expect(identity.schema_id).to.equal(\"default\")\n          expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)\n          expect(identity.traits.website).to.equal(website)\n          expect(identity.traits.email).to.equal(email)\n          expect(identity.traits.age).to.equal(age)\n          expect(identity.traits.tos).to.equal(true)\n        })\n      })\n\n      it(\"should sign up with advanced form field values be logged in\", () => {\n        const email = gen.email()\n        const password = gen.password()\n\n        cy.get('input[name=\"traits\"]').should(\"not.exist\")\n        cy.get('input[name=\"traits.email\"]').type(email)\n        cy.get('input[name=\"password\"]').type(password)\n        const website = \"https://www.example.org/\"\n        cy.get('input[name=\"traits.website\"]').type(website)\n\n        cy.submitPasswordForm()\n\n        cy.url().should(\"contain\", \"verification\")\n        cy.getVerificationCodeFromEmail(email).then((code) => {\n          cy.get(\"input[name=code]\").type(code)\n          cy.get(\"button[name=method][value=code]\").click()\n        })\n\n        cy.get('[data-testid=\"ui/message/1080002\"]').should(\n          \"have.text\",\n          \"You successfully verified your email address.\",\n        )\n\n        cy.get(\"[data-testid='node/anchor/continue']\").click()\n\n        if (app === \"express\") {\n          cy.get('a[href*=\"sessions\"').click()\n        }\n        cy.get(\"pre\").should(\"contain.text\", email)\n\n        cy.getSession().should((session) => {\n          const { identity } = session\n          expect(identity.id).to.not.be.empty\n          expect(identity.verifiable_addresses).to.have.length(1)\n          expect(identity.schema_id).to.equal(\"default\")\n          expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)\n          expect(identity.traits.website).to.equal(website)\n          expect(identity.traits.email).to.equal(email)\n          expect(identity.traits.age).to.be.undefined\n          expect(identity.traits.tos).to.be.oneOf([false, undefined])\n        })\n      })\n\n      it(\"should sign up and be redirected\", () => {\n        cy.disableVerification()\n        cy.browserReturnUrlOry()\n        cy.visit(route + \"?return_to=https://www.example.org/\")\n\n        const email = gen.email()\n        const password = gen.password()\n        const website = \"https://www.example.org/\"\n\n        cy.get('input[name=\"traits\"]').should(\"not.exist\")\n        cy.get('input[name=\"traits.email\"]').type(email)\n        cy.get('input[name=\"traits.website').type(website)\n        cy.get('input[name=\"password\"]').type(password)\n        cy.submitPasswordForm()\n\n        cy.url().should(\"eq\", \"https://www.example.org/\")\n      })\n    })\n  })\n\n  describe(\"redirect for express app\", () => {\n    it(\"should redirect to return_to after flow expires\", () => {\n      // Wait for flow to expire\n      cy.useConfigProfile(\"email\")\n      cy.shortRegisterLifespan()\n      cy.browserReturnUrlOry()\n      cy.proxy(\"express\")\n      cy.visit(express.registration + \"?return_to=https://www.example.org/\")\n      cy.wait(105)\n\n      const email = gen.email()\n      const password = gen.password()\n      const website = \"https://www.example.org/\"\n\n      cy.get(`${appPrefix(\"express\")} input[name=\"traits\"]`).should(\"not.exist\")\n      cy.get('input[name=\"traits.email\"]').type(email)\n      cy.get('input[name=\"traits.website').type(website)\n      cy.get('input[name=\"password\"]').type(password)\n\n      cy.longRegisterLifespan()\n      cy.submitPasswordForm()\n\n      cy.get('[data-testid=\"ui/message/4040001\"]').should(\n        \"contain.text\",\n        \"The registration flow expired\",\n      )\n\n      // Try again with long lifespan set\n      cy.get('input[name=\"traits\"]').should(\"not.exist\")\n      cy.get('input[name=\"traits.email\"]').type(email)\n      cy.get('input[name=\"traits.website').type(website)\n      cy.get('input[name=\"password\"]').type(password)\n      cy.submitPasswordForm()\n\n      cy.url().should(\"eq\", \"https://www.example.org/\")\n    })\n\n    it(\"should not redirect to verification_flow if not configured\", () => {\n      cy.deleteMail()\n      cy.useConfigProfile(\"email\")\n      cy.enableVerification()\n      cy.proxy(\"express\")\n      cy.visit(express.registration + \"?return_to=https://www.example.org/\")\n\n      const email = gen.email()\n      const password = gen.password()\n      const website = \"https://www.example.org/\"\n\n      cy.get(`${appPrefix(\"express\")} input[name=\"traits\"]`).should(\"not.exist\")\n      cy.get('input[name=\"traits.email\"]').type(email)\n      cy.get('input[name=\"traits.website').type(website)\n      cy.get('input[name=\"password\"]').type(password)\n\n      cy.submitPasswordForm()\n\n      // Verify that the verification code is still sent\n      cy.getVerificationCodeFromEmail(email).should(\"not.be.undefined\")\n\n      cy.url().should(\"eq\", \"https://www.example.org/\")\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/registration/ui.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { appPrefix } from \"../../../../helpers\"\n\ndescribe(\"Registration UI for email flows\", () => {\n  ;[\n    {\n      route: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n    },\n    {\n      route: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.visit(route)\n      })\n\n      describe(\"use ui elements\", () => {\n        it(\"should use the json schema titles\", () => {\n          cy.get(appPrefix(app) + 'input[name=\"traits.email\"]')\n            .parent()\n            .should(\"contain.text\", \"Your E-Mail\")\n          cy.get('input[name=\"traits.website\"]')\n            .parent()\n            .should(\"contain.text\", \"Your website\")\n          cy.get('button[value=\"password\"]').should(\"contain.text\", \"Sign up\")\n        })\n\n        it(\"clicks the log in link\", () => {\n          cy.get('*[data-testid=\"cta-link\"]').click()\n          cy.location(\"pathname\").should(\"include\", \"/login\")\n          if (app === \"express\") {\n            cy.location(\"search\").should(\"not.be.empty\")\n          }\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/settings/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Settings failures with email profile\", () => {\n  ;[\n    {\n      route: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n    },\n    {\n      route: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ route, profile, app, base }) => {\n    describe(`for app ${app}`, () => {\n      let email = gen.email()\n      let password = gen.password()\n\n      const emailSecond = `second-${gen.email()}`\n      const passwordSecond = gen.password()\n\n      const up = (value) => `not-${value}`\n\n      before(() => {\n        cy.proxy(app)\n        cy.useConfigProfile(profile)\n        cy.registerApi({\n          email: emailSecond,\n          password: passwordSecond,\n          fields: { \"traits.website\": \"https://github.com/ory/kratos\" },\n        })\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n      })\n\n      beforeEach(() => {\n        cy.longPrivilegedSessionTime()\n\n        cy.visit(base)\n        cy.clearAllCookies()\n\n        cy.login({ email, password, cookieUrl: base })\n        cy.visit(route)\n      })\n\n      describe(\"profile\", () => {\n        beforeEach(() => {\n          cy.visit(route)\n        })\n\n        it(\"fails with validation errors\", () => {\n          cy.get('input[name=\"traits.website\"]').clear().type(\"http://s\")\n          cy.get('[name=\"method\"][value=\"profile\"]').click()\n          cy.get('[data-testid=\"ui/message/4000003\"]').should(\n            \"contain.text\",\n            \"length must be >= 10\",\n          )\n        })\n\n        it(\"fails because reauth is another person\", () => {\n          // Force this because it is hidden\n          cy.get('input[name=\"traits.email\"]').clear().type(up(email))\n          cy.shortPrivilegedSessionTime()\n\n          cy.location().then((loc) => {\n            cy.get('button[value=\"profile\"]').click()\n\n            cy.reauthWithOtherAccount({\n              previousUrl: loc.toString(),\n              expect: { email },\n              type: { email: emailSecond, password: passwordSecond },\n            })\n\n            cy.location(\"pathname\").should(\"contain\", \"/settings\")\n          })\n\n          // We end up in a new settings flow for the second user\n          cy.get('input[name=\"traits.email\"]').should(\"have.value\", emailSecond)\n\n          // Try to log in with updated credentials -> should fail\n          cy.clearAllCookies()\n          cy.login({\n            email: up(email),\n            password,\n            expectSession: false,\n            cookieUrl: base,\n          })\n        })\n\n        it(\"does not update data because resumable session was removed\", () => {\n          cy.get('input[name=\"traits.email\"]').clear().type(up(email))\n          cy.shortPrivilegedSessionTime()\n          cy.get('button[value=\"profile\"]').click()\n\n          cy.login({ email, password, cookieUrl: base })\n\n          cy.getSession().should((session) => {\n            const { identity } = session\n            expect(identity.traits.email).to.equal(email)\n          })\n        })\n\n        it(\"does not update without re-auth\", () => {\n          cy.get('input[name=\"traits.email\"]').clear().type(up(email))\n          cy.shortPrivilegedSessionTime() // wait for the privileged session to time out\n          cy.get('button[value=\"profile\"]').click()\n\n          cy.visit(base)\n\n          cy.getSession().should((session) => {\n            const { identity } = session\n            expect(identity.traits.email).to.equal(email)\n          })\n        })\n\n        it(\"does not resume another failed request\", () => {\n          // checks here that we're checking settingsRequest.id == cookie.stored.id\n          cy.get('input[name=\"traits.email\"]').clear().type(up(email))\n          cy.shortPrivilegedSessionTime() // wait for the privileged session to time out\n          cy.get('button[value=\"profile\"]').click()\n          cy.location(\"pathname\").should(\"not.contain\", \"/settings\")\n\n          cy.visit(route)\n          cy.get('input[name=\"traits.website\"]')\n            .clear()\n            .type(\"http://github.com/aeneasr\")\n          cy.get('button[value=\"profile\"]').click()\n          cy.expectSettingsSaved()\n\n          cy.getSession().should((session) => {\n            const { identity } = session\n            expect(identity.traits.email).to.equal(email) // this is NOT up(email)\n            expect(identity.traits.website).to.equal(\n              \"http://github.com/aeneasr\",\n            ) // this is NOT up(email)\n          })\n        })\n      })\n\n      describe(\"password\", () => {\n        beforeEach(() => {\n          cy.longPrivilegedSessionTime()\n        })\n\n        afterEach(() => {\n          cy.longPrivilegedSessionTime()\n        })\n\n        it(\"fails if password policy is violated\", () => {\n          cy.get('input[name=\"password\"]').clear().type(\"12345678\")\n          cy.get('button[value=\"password\"]').click()\n          cy.get('[data-testid=\"ui/message/4000034\"]').should(\n            \"contain.text\",\n            \"data breaches\",\n          )\n        })\n\n        it(\"fails because reauth is another person\", () => {\n          cy.shortPrivilegedSessionTime() // wait for the privileged session to time out\n          cy.get('input[name=\"password\"]').clear().type(up(password))\n\n          let firstSession\n          cy.getSession().then((session) => {\n            firstSession = session\n          })\n\n          cy.location().then((loc) => {\n            cy.get('button[value=\"password\"]').click()\n\n            cy.reauthWithOtherAccount({\n              previousUrl: loc.toString(),\n              expect: { email },\n              type: { email: emailSecond, password: passwordSecond },\n            })\n\n            cy.location(\"pathname\").should(\"contain\", \"/settings\")\n          })\n\n          // We want to ensure that the reauth session is completely different from the one we had in the first place.\n          cy.getSession().then((session) => {\n            expect(session.authentication_methods).to.have.length(1)\n            expect(session.identity.traits.email).to.eq(emailSecond)\n            expect(session.id).to.not.eq(firstSession.id)\n            expect(session.identity.id).to.not.eq(firstSession.identity.id)\n            expect(session.authenticated_at).to.not.eq(\n              firstSession.authenticated_at,\n            )\n          })\n\n          // We end up in a new settings flow for the second user\n          cy.get('input[name=\"traits.email\"]').should(\"have.value\", emailSecond)\n\n          // Try to log in with updated credentials -> should fail\n          cy.clearAllCookies()\n          cy.login({\n            email,\n            password: up(password),\n            expectSession: false,\n            cookieUrl: base,\n          })\n        })\n\n        it(\"does not update without re-auth\", () => {\n          cy.get('input[name=\"password\"]').clear().type(up(password))\n          cy.shortPrivilegedSessionTime() // wait for the privileged session to time out\n          cy.get('button[value=\"password\"]').click()\n\n          cy.visit(base)\n          cy.clearAllCookies()\n          cy.login({\n            email,\n            password: up(password),\n            expectSession: false,\n            cookieUrl: base,\n          })\n        })\n\n        it(\"does not update data because resumable session was removed\", () => {\n          cy.get('input[name=\"password\"]').clear().type(up(password))\n          cy.shortPrivilegedSessionTime() // wait for the privileged session to time out\n          cy.get('button[value=\"password\"]').click()\n\n          cy.clearAllCookies()\n          cy.login({ email, password, cookieUrl: base })\n          cy.clearAllCookies()\n          cy.login({\n            email,\n            password: up(password),\n            expectSession: false,\n            cookieUrl: base,\n          })\n        })\n\n        it(\"does not resume another queued request\", () => {\n          const email = gen.email()\n          const password = gen.password()\n          cy.clearAllCookies()\n          cy.register({\n            email,\n            password,\n            fields: { \"traits.website\": website },\n          })\n          cy.visit(route)\n\n          // checks here that we're checking settingsRequest.id == cookie.stored.id\n          const invalidPassword = \"invalid-\" + gen.password()\n          cy.get('input[name=\"password\"]').clear().type(invalidPassword)\n          cy.shortPrivilegedSessionTime() // wait for the privileged session to time out\n          cy.get('button[value=\"password\"]').click()\n          cy.location(\"pathname\").should(\"include\", \"/login\")\n\n          const validPassword = \"valid-\" + gen.password()\n          cy.visit(route)\n          cy.get('input[name=\"password\"]').clear().type(validPassword)\n          cy.get('button[value=\"password\"]').click()\n\n          cy.location(\"pathname\").should(\"include\", \"/login\")\n          cy.reauth({ expect: { email }, type: { password: password } })\n\n          cy.location(\"pathname\").should(\"include\", \"/settings\")\n          cy.get('input[name=\"password\"]').should(\"exist\")\n\n          // This should pass because it is the correct password\n          cy.clearAllCookies()\n          cy.login({ email, password: validPassword, cookieUrl: base })\n\n          // This should fail because it is the wrong password\n          cy.clearAllCookies()\n          cy.login({\n            email,\n            password: invalidPassword,\n            expectSession: false,\n            cookieUrl: base,\n          })\n\n          cy.clearAllCookies()\n          cy.login({\n            email,\n            password: password,\n            expectSession: false,\n            cookieUrl: base,\n          })\n        })\n      })\n\n      describe(\"global errors\", () => {\n        it(\"fails when CSRF is incorrect\", () => {\n          cy.get(appPrefix(app) + 'input[name=\"password\"]').type(\"12345678\")\n          cy.shouldHaveCsrfError({ app })\n        })\n\n        it(\"fails when a disallowed return_to url is requested\", () => {\n          cy.shouldErrorOnDisallowedReturnTo(\n            route + \"?return_to=https://not-allowed\",\n            { app },\n          )\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/settings/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Settings success with email profile\", () => {\n  ;[\n    {\n      route: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n      login: express.login,\n    },\n    {\n      route: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n      login: react.login,\n    },\n  ].forEach(({ route, profile, app, base, login }) => {\n    describe(`for app ${app}`, () => {\n      let email = gen.email()\n      const firstPassword = gen.password()\n      const secondPassword = gen.password()\n      const thirdPassword = gen.password()\n      let password = firstPassword\n\n      const up = (value: string) => `not-${value}`\n      const down = (value: string) => value.replace(/not-/, \"\")\n\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.deleteMail()\n        cy.login({ email, password, cookieUrl: base })\n        cy.visit(route)\n      })\n\n      it(\"shows all settings forms\", () => {\n        cy.get(appPrefix(app) + \"h3\").should(\"contain.text\", \"Profile\")\n        cy.get('input[name=\"traits.email\"]').should(\"contain.value\", email)\n        cy.get('input[name=\"traits.website\"]').should(\"contain.value\", website)\n\n        cy.get(\"h3\").should(\"contain.text\", \"Password\")\n        cy.get('input[name=\"password\"]').should(\"be.empty\")\n      })\n\n      describe(\"password\", () => {\n        it(\"modifies the password with privileged session\", () => {\n          // Once input weak password to test which error message is cleared after updating successfully\n          cy.get('input[name=\"password\"]').clear().type(\"123\")\n          cy.get('button[value=\"password\"]').click()\n          cy.get('[data-testid=\"ui/message/1050001\"]').should(\"not.exist\")\n          cy.get('[data-testid=\"ui/message/4000032\"]').should(\"exist\")\n          cy.get('input[name=\"password\"]').should(\"be.empty\")\n\n          password = secondPassword\n          cy.get('input[name=\"password\"]').clear().type(secondPassword)\n          cy.get('button[value=\"password\"]').click()\n          cy.expectSettingsSaved()\n          cy.get('[data-testid=\"ui/message/4000032\"]').should(\"not.exist\")\n          cy.get('[data-testid=\"ui/message/1050001\"]').should(\"exist\")\n          cy.get('input[name=\"password\"]').should(\"be.empty\")\n        })\n\n        it(\"is unable to log in with the old password\", () => {\n          cy.login({\n            email: email,\n            password: firstPassword,\n            expectSession: false,\n            cookieUrl: base,\n          })\n        })\n\n        it(\"modifies the password with an unprivileged session\", () => {\n          password = thirdPassword\n          cy.get('input[name=\"password\"]').clear().type(password)\n          cy.shortPrivilegedSessionTime() // wait for the privileged session to time out\n          cy.get('button[value=\"password\"]').click()\n\n          cy.reauth({ expect: { email }, type: { password: secondPassword } })\n\n          cy.url().should(\"include\", \"/settings\")\n          cy.expectSettingsSaved()\n          cy.get('input[name=\"password\"]').should(\"be.empty\")\n        })\n      })\n\n      describe(\"profile\", () => {\n        it(\"modifies an unprotected traits\", () => {\n          cy.get('input[name=\"traits.website\"]')\n            .clear()\n            .type(\"https://github.com/ory\")\n          cy.get('input[name=\"traits.age\"]').clear().type(\"30\")\n          cy.get('input[type=\"checkbox\"][name=\"traits.tos\"]').click({\n            force: true,\n          })\n          cy.submitProfileForm()\n          cy.expectSettingsSaved()\n\n          cy.get('input[name=\"traits.website\"]').should(\n            \"contain.value\",\n            \"https://github.com/ory\",\n          )\n          cy.get('input[type=\"checkbox\"][name=\"traits.tos\"]')\n            .should(\"be.checked\")\n            .click({ force: true })\n          cy.get('input[name=\"traits.age\"]')\n            .should(\"have.value\", \"30\")\n            .clear()\n            .type(\"90\")\n\n          cy.submitProfileForm()\n          cy.expectSettingsSaved()\n\n          cy.get('input[type=\"checkbox\"][name=\"traits.tos\"]').should(\n            \"not.be.checked\",\n          )\n          cy.get('input[name=\"traits.age\"]').should(\"have.value\", \"90\")\n        })\n\n        it(\"modifies a protected trait with privileged session\", () => {\n          email = up(email)\n          cy.disableVerification()\n          cy.get('input[name=\"traits.email\"]').clear().type(email)\n          cy.get('button[value=\"profile\"]').click()\n          cy.expectSettingsSaved()\n          cy.get('input[name=\"traits.email\"]').should(\"contain.value\", email)\n        })\n\n        it(\"is unable to log in with the old email\", () => {\n          cy.login({\n            email: down(email),\n            password,\n            expectSession: false,\n            cookieUrl: base,\n          })\n        })\n\n        it(\"modifies a protected trait with unprivileged session\", () => {\n          email = up(email)\n          cy.get('input[name=\"traits.email\"]').clear().type(email)\n          cy.shortPrivilegedSessionTime() // wait for the privileged session to time out\n          cy.get('button[value=\"profile\"]').click()\n\n          cy.reauth({ expect: { email: down(email) }, type: { password } })\n\n          cy.url().should(\"include\", \"/settings\")\n          cy.expectSettingsSaved()\n          cy.get('input[name=\"traits.email\"]').should(\"contain.value\", email)\n        })\n\n        if (app === \"react\") {\n          it(\"shows verification screen after email update\", () => {\n            cy.deleteMail()\n            cy.enableVerification()\n            email = up(email)\n            cy.get('input[name=\"traits.email\"]').clear().type(email)\n            cy.get('button[value=\"profile\"]').click()\n\n            cy.url().should(\"contain\", \"verification\")\n            cy.getVerificationCodeFromEmail(email).then((code) => {\n              cy.get(\"input[name=code]\").type(code)\n              cy.get(\"button[name=method][value=code]\").click()\n            })\n\n            cy.get('[data-testid=\"ui/message/1080002\"]').should(\n              \"have.text\",\n              \"You successfully verified your email address.\",\n            )\n          })\n        }\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/email/settings/ui.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Settings errors with email profile\", () => {\n  ;[\n    {\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"email\",\n    },\n    {\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ profile, app, base }) => {\n    describe(`for app ${app}`, () => {\n      const identity = gen.identity()\n\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.registerApi({\n          ...identity,\n          fields: { \"traits.website\": \"https://www.ory.sh/\" },\n        })\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.login({ ...identity, cookieUrl: base })\n        cy.visit(base)\n      })\n\n      describe(\"use ui elements\", () => {\n        it(\"should use the json schema titles\", () => {\n          const settingsLink = appPrefix(app) + 'a[href*=\"settings\"]'\n          if (app === \"express\") {\n            cy.get(settingsLink).should(\"have.attr\", \"target\", \"_blank\")\n            cy.removeAttribute([settingsLink], \"target\")\n          }\n          cy.get(settingsLink).click()\n          cy.get('input[name=\"traits.email\"]')\n            .parent()\n            .should(\"contain.text\", \"Your E-Mail\")\n          cy.get('input[name=\"traits.website\"]')\n            .parent()\n            .should(\"contain.text\", \"Your website\")\n\n          cy.get('input[name=\"password\"]')\n            .parentsUntil(\"label\")\n            .should(\"contain.text\", \"Password\")\n\n          cy.get('button[value=\"profile\"]').should(\"contain.text\", \"Save\")\n          cy.get('button[value=\"password\"]').should(\"contain.text\", \"Save\")\n        })\n\n        it(\"clicks the settings link\", () => {\n          const settingsLink = 'a[href*=\"settings\"]'\n          if (app === \"express\") {\n            cy.get(settingsLink).should(\"have.attr\", \"target\", \"_blank\")\n            cy.removeAttribute([settingsLink], \"target\")\n          }\n          cy.get(settingsLink).click()\n          cy.location(\"pathname\").should(\"include\", \"settings\")\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/import/import.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { routes as express } from \"../../../helpers/express\"\nimport { gen, KRATOS_ADMIN, website } from \"../../../helpers\"\n\ncontext(\"Import Identities\", () => {\n  before(() => {\n    cy.useConfigProfile(\"oidc\")\n    cy.proxy(\"express\")\n  })\n\n  beforeEach(() => {\n    cy.clearAllCookies()\n  })\n\n  const password = gen.password()\n  for (const tc of [\n    {\n      name: \"cleartext\",\n      config: {\n        password,\n      },\n      checkPassword: password,\n    },\n    {\n      name: \"pbkdf2\",\n      config: {\n        hashed_password:\n          \"$pbkdf2-sha256$i=1000,l=128$e8/arsEf4cvQihdNgqj0Nw$5xQQKNTyeTHx2Ld5/JDE7A\",\n      },\n      checkPassword: \"123456\",\n    },\n    {\n      name: \"bcrypt\",\n      config: {\n        hashed_password:\n          \"$2a$10$ZsCsoVQ3xfBG/K2z2XpBf.tm90GZmtOqtqWcB5.pYd5Eq8y7RlDyq\",\n      },\n      checkPassword: \"123456\",\n    },\n    {\n      name: \"argon2id\",\n      config: {\n        hashed_password:\n          \"$argon2id$v=19$m=16,t=2,p=1$bVI1aE1SaTV6SGQ3bzdXdw$fnjCcZYmEPOUOjYXsT92Cg\",\n      },\n      checkPassword: \"123456\",\n    },\n  ]) {\n    it(`should be able to sign in using an imported password (${tc.name})`, () => {\n      const email = gen.email()\n      cy.request(\"POST\", `${KRATOS_ADMIN}/identities`, {\n        schema_id: \"default\",\n        traits: {\n          email,\n          website,\n        },\n        credentials: {\n          password: {\n            config: tc.config,\n          },\n        },\n      })\n\n      cy.visit(express.login)\n\n      // Try to sign in with an incorrect password\n      cy.get('input[name=\"identifier\"]').type(email)\n      cy.get('input[name=\"password\"]').type(\"invalid-password\")\n      cy.submitPasswordForm()\n      cy.get('*[data-testid=\"ui/message/4000006\"]').should(\n        \"contain.text\",\n        \"credentials are invalid\",\n      )\n\n      // But with correct password it succeeds\n      cy.get('input[name=\"password\"]').type(tc.checkPassword)\n      cy.submitPasswordForm()\n\n      cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n      cy.getSession().should((session) => {\n        const { identity } = session\n        expect(identity.id).to.not.be.empty\n        expect(identity.traits.website).to.equal(website)\n      })\n    })\n  }\n\n  it(`should be able to sign in using imported oidc credentials`, () => {\n    const email = gen.email()\n    const website = \"https://\" + gen.password() + \".com\"\n    cy.request(\"POST\", `${KRATOS_ADMIN}/identities`, {\n      schema_id: \"default\",\n      traits: {\n        email,\n        website,\n      },\n      credentials: {\n        oidc: {\n          config: {\n            providers: [\n              {\n                provider: \"hydra\",\n                subject: email,\n              },\n            ],\n          },\n        },\n      },\n    })\n\n    cy.visit(express.login)\n    cy.triggerOidc(\"express\")\n\n    cy.get(\"#username\").clear().type(email)\n    cy.get(\"#remember\").click()\n    cy.get(\"#accept\").click()\n    cy.get('[name=\"scope\"]').each(($el) => cy.wrap($el).click())\n    cy.get(\"#remember\").click()\n    cy.get(\"#accept\").click()\n\n    cy.getSession().should((session) => {\n      const { identity } = session\n      expect(identity.id).to.not.be.empty\n      expect(identity.traits.website).to.equal(website)\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mfa/code.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../helpers\"\nimport { routes as express } from \"../../../helpers/express\"\n\ncontext(\"2FA code\", () => {\n  ;[\n    // {\n    //   login: react.login,\n    //   settings: react.settings,\n    //   base: react.base,\n    //   app: \"react\" as \"react\",\n    //   profile: \"spa\",\n    // },\n    {\n      login: express.login,\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"mfa\",\n    },\n  ].forEach(({ settings, login, profile, app, base }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let email: string\n      let password: string\n\n      describe(\"when using highest_available aal\", () => {\n        beforeEach(() => {\n          cy.useConfig((builder) =>\n            builder\n              .longPrivilegedSessionTime()\n              .useHighestAvailable()\n              .enableCodeMFA(),\n          )\n        })\n\n        it(\"should show second factor screen on whoami call\", () => {\n          email = gen.email()\n          password = gen.password()\n          cy.register({\n            email,\n            password,\n            fields: { \"traits.website\": website },\n          })\n          cy.deleteMail()\n\n          cy.visit(settings)\n          cy.location(\"pathname\").should(\"contain\", \"/login\") // we get redirected to login\n\n          cy.getLoginCodeFromEmail(email).then((code) => {\n            cy.get(\"input[name='code']\").type(code)\n            cy.contains(\"Continue\").click()\n          })\n\n          cy.getSession({\n            expectAal: \"aal2\",\n            expectMethods: [\"password\", \"code\"],\n          })\n        })\n      })\n\n      describe(\"when using aal1 required aal\", () => {\n        beforeEach(() => {\n          email = gen.email()\n          password = gen.password()\n          cy.useConfig((builder) =>\n            builder\n              .longPrivilegedSessionTime()\n              .useLaxAal()\n              .enableCode()\n              .enableCodeMFA(),\n          )\n\n          cy.register({\n            email,\n            password,\n            fields: { \"traits.website\": website },\n          })\n          cy.deleteMail()\n          cy.visit(login + \"?aal=aal2&via=email\")\n        })\n\n        it(\"should be asked to sign in with 2fa if set up\", () => {\n          cy.get(\"input[name='code']\").should(\"be.visible\")\n          cy.getLoginCodeFromEmail(email).then((code) => {\n            cy.get(\"input[name='code']\").type(code)\n            cy.contains(\"Continue\").click()\n          })\n\n          cy.getSession({\n            expectAal: \"aal2\",\n            expectMethods: [\"password\", \"code\"],\n          })\n        })\n\n        it(\"can't use different email in 2fa request\", () => {\n          // Setting up another 2fa method to prevent fast-login to happen\n          cy.visit(base)\n          cy.clearAllCookies()\n          cy.useConfig((builder) => builder.disableCodeMfa())\n          cy.login({ email, password, cookieUrl: base })\n          cy.longPrivilegedSessionTime()\n          cy.visit(settings)\n          cy.get(\n            appPrefix(app) + 'button[name=\"lookup_secret_regenerate\"]',\n          ).click()\n          cy.get('button[name=\"lookup_secret_confirm\"]').click()\n          cy.expectSettingsSaved()\n          cy.visit(settings)\n          cy.clearAllCookies()\n          cy.useConfig((builder) => builder.enableCodeMFA())\n          cy.login({ email: email, password: password, cookieUrl: base })\n\n          cy.visit(login + \"?aal=aal2\")\n          cy.get('[name=\"method\"][value=\"lookup_secret\"]').should(\"exist\")\n          cy.get('[name=\"address\"]').should(\"exist\")\n\n          cy.get('[name=\"address\"]').invoke(\"attr\", \"value\", gen.email())\n\n          cy.get('[name=\"address\"]').click()\n\n          cy.get(\"*[data-testid='ui/message/4000035']\").should(\"be.visible\")\n          cy.get(\"input[name='code']\").should(\"not.exist\")\n          cy.get(\"[name='address']\").should(\"be.visible\")\n\n          // The current session should be unchanged\n          cy.getSession({\n            expectAal: \"aal1\",\n            expectMethods: [\"password\"],\n          })\n        })\n\n        it(\"entering wrong code should not invalidate correct codes\", () => {\n          cy.get(\"input[name='code']\").should(\"be.visible\").type(\"123456\")\n\n          cy.contains(\"Continue\").click()\n          cy.getLoginCodeFromEmail(email).then((code) => {\n            cy.get(\"input[name='code']\").type(code)\n            cy.contains(\"Continue\").click()\n          })\n\n          cy.getSession({\n            expectAal: \"aal2\",\n            expectMethods: [\"password\", \"code\"],\n          })\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mfa/code_optional.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen } from \"../../../helpers\"\nimport { routes as express } from \"../../../helpers/express\"\n\ncontext(\"2FA code with optional field\", () => {\n  ;[\n    {\n      login: express.login,\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"mfa-optional\",\n    },\n  ].forEach(({ settings, login, profile, app, base }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      describe(\"when using highest_available aal with empty optionalMfaEmail field\", () => {\n        let email: string\n        let password: string\n\n        beforeEach(() => {\n          email = gen.email()\n          password = gen.password()\n\n          // Configure system to use highest available AAL\n          cy.useConfig((builder) =>\n            builder.longPrivilegedSessionTime().useHighestAvailable(),\n          )\n\n          // Register a user without setting up code MFA (empty optionalMfaEmail field)\n          cy.register({\n            email,\n            password,\n            fields: { \"traits.optionalMfaEmail\": \"\" },\n          })\n          cy.deleteMail()\n        })\n\n        it(\"should not show 2FA page during login when optionalMfaEmail field is empty\", () => {\n          // Log out first\n          cy.clearAllCookies()\n\n          // Login with the user\n          cy.visit(login)\n          cy.get('input[name=\"identifier\"]').type(email)\n          cy.get('input[name=\"password\"]').type(password)\n          cy.get('button[name=\"method\"][value=\"password\"]').click()\n\n          // Should be logged in directly without 2FA page\n          cy.getSession({\n            expectAal: \"aal1\",\n            expectMethods: [\"password\"],\n          })\n\n          // Verify we're not asked for 2FA when visiting settings\n          cy.visit(settings)\n          cy.location(\"pathname\").should(\"contain\", \"/settings\")\n          cy.get('input[name=\"traits.email\"]').should(\"contain.value\", email)\n        })\n      })\n\n      describe(\"when using highest_available aal with configured optionalMfaEmail field\", () => {\n        let email: string\n        let mfaEmail: string\n        let password: string\n\n        beforeEach(() => {\n          email = gen.email()\n          mfaEmail = gen.email()\n          password = gen.password()\n\n          // Configure system to use highest available AAL\n          cy.useConfig((builder) =>\n            builder.longPrivilegedSessionTime().useHighestAvailable(),\n          )\n\n          // Register a user with optionalMfaEmail field set\n          cy.register({\n            email,\n            password,\n            fields: { \"traits.optionalMfaEmail\": mfaEmail },\n          })\n          cy.deleteMail()\n        })\n\n        it(\"should show 2FA page during login when optionalMfaEmail field is configured\", () => {\n          // Log out first\n          cy.clearAllCookies()\n\n          // Login with the user\n          cy.visit(login)\n          cy.get('input[name=\"identifier\"]').type(email)\n          cy.get('input[name=\"password\"]').type(password)\n          cy.get('button[name=\"method\"][value=\"password\"]').click()\n\n          // Should see the code input field\n          cy.location(\"pathname\").should(\"contain\", \"/login\")\n          cy.get(\"input[name='code']\").should(\"be.visible\")\n\n          // Get the code from email and enter it\n          cy.getLoginCodeFromEmail(mfaEmail).then((code) => {\n            cy.get(\"input[name='code']\").type(code)\n            cy.contains(\"Continue\").click()\n          })\n\n          // Should be logged in with AAL2\n          cy.getSession({\n            expectAal: \"aal2\",\n            expectMethods: [\"password\", \"code\"],\n          })\n        })\n\n        it(\"should require 2FA for settings flow when optionalMfaEmail field is configured\", () => {\n          // Log out first\n          cy.clearAllCookies()\n\n          // Try to access settings directly\n          cy.visit(settings)\n\n          // Should be redirected to login\n          cy.location(\"pathname\").should(\"contain\", \"/login\")\n\n          // Login with password\n          cy.get('input[name=\"identifier\"]').type(email)\n          cy.get('input[name=\"password\"]').type(password)\n          cy.get('button[name=\"method\"][value=\"password\"]').click()\n\n          // Should see the code input field\n          cy.location(\"pathname\").should(\"contain\", \"/login\")\n          cy.get(\"input[name='code']\").should(\"be.visible\")\n\n          // Get the code from email and enter it\n          cy.getLoginCodeFromEmail(mfaEmail).then((code) => {\n            cy.get(\"input[name='code']\").type(code)\n            cy.contains(\"Continue\").click()\n          })\n\n          // Go the the settings page again\n          cy.visit(settings)\n\n          // Should now be at settings with AAL2\n          cy.location(\"pathname\").should(\"contain\", \"/settings\")\n          cy.getSession({\n            expectAal: \"aal2\",\n            expectMethods: [\"password\", \"code\"],\n          })\n        })\n      })\n\n      describe(\"when user with configured optionalMfaEmail logs in with aal1 session\", () => {\n        let email: string\n        let mfaEmail: string\n        let password: string\n\n        beforeEach(() => {\n          email = gen.email()\n          mfaEmail = gen.email()\n          password = gen.password()\n\n          // Configure system to allow aal1 login but still require highest_available for settings\n          cy.useConfig((builder) =>\n            builder\n              .longPrivilegedSessionTime()\n              .useLaxSessionAal()\n              .useHighestSettingsFlowAal(),\n          )\n\n          // Register a user with optionalMfaEmail field set\n          cy.register({\n            email,\n            password,\n            fields: { \"traits.optionalMfaEmail\": mfaEmail },\n          })\n          cy.deleteMail()\n        })\n\n        it(\"should not allow access to settings page with aal1 session\", () => {\n          // Log out first\n          cy.clearAllCookies()\n\n          // Login with just password (aal1)\n          cy.visit(login)\n          cy.get('input[name=\"identifier\"]').type(email)\n          cy.get('input[name=\"password\"]').type(password)\n          cy.get('button[name=\"method\"][value=\"password\"]').click()\n\n          // Verify we have an aal1 session\n          cy.getSession({\n            expectAal: \"aal1\",\n            expectMethods: [\"password\"],\n          })\n\n          // Try to access settings page\n          cy.visit(settings)\n\n          // Should be redirected to login for 2FA\n          cy.location(\"pathname\").should(\"contain\", \"/login\")\n\n          // Verify we're being asked for 2FA code input\n          cy.get(\"input[name='code']\").should(\"be.visible\")\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mfa/lookup.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../helpers\"\nimport { routes as express } from \"../../../helpers/express\"\nimport { routes as react } from \"../../../helpers/react\"\n\ncontext(\"2FA lookup secrets\", () => {\n  ;[\n    {\n      login: react.login,\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      login: express.login,\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"mfa\",\n    },\n  ].forEach(({ settings, login, profile, app, base }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let email = gen.email()\n      let password = gen.password()\n\n      beforeEach(() => {\n        cy.visit(base)\n        cy.clearAllCookies()\n        cy.useConfig((builder) => builder.disableCodeMfa())\n        email = gen.email()\n        password = gen.password()\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n        cy.login({ email, password, cookieUrl: base })\n\n        cy.longPrivilegedSessionTime()\n        cy.sessionRequiresNo2fa()\n      })\n\n      it(\"should be able to remove lookup codes\", () => {\n        cy.sessionRequires2fa()\n        cy.longPrivilegedSessionTime()\n        cy.visit(settings)\n        cy.get(\n          appPrefix(app) + 'button[name=\"lookup_secret_regenerate\"]',\n        ).click()\n        cy.get('button[name=\"lookup_secret_confirm\"]').click()\n        cy.expectSettingsSaved()\n        cy.visit(settings)\n\n        cy.shortPrivilegedSessionTime()\n        cy.get('button[name=\"lookup_secret_disable\"]').click()\n        cy.reauth({\n          expect: { email },\n          type: { email: email, password: password },\n        })\n        cy.expectSettingsSaved()\n\n        cy.clearAllCookies()\n        cy.login({ email: email, password: password, cookieUrl: base })\n\n        cy.visit(login + \"?aal=aal2\")\n        cy.get(\"h2\")\n          .invoke(\"text\")\n          .should(\n            \"match\",\n            /Second factor authentication|Two-Factor Authentication/,\n          )\n        cy.get('*[name=\"method\"][value=\"totp\"]').should(\"not.exist\")\n        cy.get('*[name=\"method\"][value=\"lookup_secret\"]').should(\"not.exist\")\n        cy.get('*[name=\"method\"][value=\"password\"]').should(\"not.exist\")\n      })\n\n      it(\"should go through several lookup secret lifecycles\", () => {\n        cy.visit(settings)\n\n        cy.get('[data-testid=\"node/text/lookup_secret_codes/label\"]').should(\n          \"not.exist\",\n        )\n        cy.get('[data-testid=\"text-lookup_secret_codes-content\"] code').should(\n          \"not.exist\",\n        )\n        cy.get('button[name=\"lookup_secret_confirm\"]').should(\"not.exist\")\n        cy.get('button[name=\"lookup_secret_regenerate\"]').click()\n        cy.get('[data-testid=\"node/text/lookup_secret_codes/label\"]').should(\n          \"contain.text\",\n          \"These are your back up recovery codes.\",\n        )\n        cy.get('[data-testid=\"text-lookup_secret_codes-content\"] code').should(\n          \"not.be.empty\",\n        )\n\n        let codes: string[]\n        cy.getLookupSecrets().should((c) => {\n          codes = c\n        })\n\n        cy.get('button[name=\"lookup_secret_confirm\"]').click()\n        cy.expectSettingsSaved()\n\n        cy.get('button[name=\"lookup_secret_reveal\"]').should(\"exist\")\n        cy.get('[data-testid=\"text-lookup_secret_codes-content\"] code').should(\n          \"not.exist\",\n        )\n        cy.get('button[name=\"lookup_secret_confirm\"]').should(\"not.exist\")\n        cy.get('button[name=\"lookup_secret_regenerate\"]').should(\"not.exist\")\n\n        cy.get('button[name=\"lookup_secret_reveal\"]').click()\n        cy.getLookupSecrets().should((c) => {\n          codes = c\n        })\n\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"password\", \"lookup_secret\"],\n        })\n\n        // Try to log in with a recovery code now\n        cy.visit(login + \"?aal=aal2&refresh=true\")\n        cy.location(\"pathname\").should(\"contain\", \"login\")\n\n        cy.get('*[name=\"method\"][value=\"lookup_secret\"]').should(\"exist\")\n        cy.get('*[name=\"method\"][value=\"password\"]').should(\"not.exist\")\n\n        // Type an invalid code\n        cy.get('input[name=\"lookup_secret\"]').should(\"exist\")\n        cy.get('input[name=\"lookup_secret\"]').type(\"invalid-code\")\n        cy.get('*[name=\"method\"][value=\"lookup_secret\"]').click()\n        cy.get('[data-testid=\"ui/message/4000016\"]').should(\n          \"contain.text\",\n          \"The backup recovery code is not valid.\",\n        )\n\n        // Type a valid code\n        cy.get('input[name=\"lookup_secret\"]').should(\"exist\")\n        cy.get('input[name=\"lookup_secret\"]').should(\"have.value\", \"\")\n        cy.get('input[name=\"lookup_secret\"]').then(($e) => {\n          cy.wrap($e).type(codes[0])\n        })\n        cy.get('*[name=\"method\"][value=\"lookup_secret\"]').click()\n        cy.location(\"pathname\").should(\"not.contain\", \"login\")\n\n        let authenticatedAt\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"password\", \"lookup_secret\", \"lookup_secret\"],\n        }).then((session) => {\n          authenticatedAt = session.authenticated_at\n          expect(session.authenticator_assurance_level).to.equal(\"aal2\")\n        })\n\n        // Retry auth with the used code\n        cy.visit(login + \"?aal=aal2&refresh=true\")\n        cy.location().should((loc) => {\n          expect(loc.href).to.include(\"/login\")\n        })\n        cy.get('input[name=\"lookup_secret\"]').then(($e) => {\n          cy.wrap($e).type(codes[0])\n        })\n        cy.get('*[name=\"method\"][value=\"lookup_secret\"]').click()\n        // Use a valid code\n        cy.get('[data-testid=\"ui/message/4000012\"]').should(\n          \"contain.text\",\n          \"This backup recovery code has already been used.\",\n        )\n\n        cy.get('input[name=\"lookup_secret\"]').then(($e) => {\n          cy.wrap($e).type(codes[1])\n        })\n        cy.get('*[name=\"method\"][value=\"lookup_secret\"]').click()\n        cy.location(\"pathname\").should(\"not.contain\", \"login\")\n\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\n            \"password\",\n            \"lookup_secret\",\n            \"lookup_secret\",\n            \"lookup_secret\",\n          ],\n        }).then((session) => {\n          expect(session.authenticated_at).to.not.equal(authenticatedAt)\n        })\n\n        // Going back to the settings UI we should see that the codes have been \"used\"\n        cy.visit(settings)\n        cy.get('button[name=\"lookup_secret_reveal\"]').click()\n        cy.getLookupSecrets().should((c) => {\n          expect(c.slice(2)).to.eql(codes.slice(2))\n          expect(c[0]).to.match(/(Secret was used at )|(Used)/g)\n          expect(c[1]).to.match(/(Secret was used at )|(Used)/g)\n        })\n\n        // Regenerating the codes means the old one become invalid\n        cy.get(\"*[name=lookup_secret_regenerate]\").click()\n        cy.get(\"*[name=lookup_secret_confirm]\").should(\"exist\")\n        let regenCodes\n        cy.getLookupSecrets().should((c) => {\n          regenCodes = c\n        })\n\n        // Confirm it\n        cy.get(\"*[name=lookup_secret_confirm]\").click()\n        cy.get('*[name=\"lookup_secret_reveal\"]').click()\n        cy.getLookupSecrets().should((c) => {\n          expect(c).to.eql(regenCodes)\n        })\n\n        // Log in and see if we can use the old / new keys\n        cy.visit(login + \"?aal=aal2&refresh=true\")\n        cy.location(\"pathname\").should(\"contain\", \"login\")\n\n        // Using an old code fails\n        cy.get('input[name=\"lookup_secret\"]').then(($e) => {\n          cy.wrap($e).type(codes[3])\n        })\n        cy.get('*[name=\"method\"][value=\"lookup_secret\"]').click()\n        cy.get('[data-testid=\"ui/message/4000016\"]').should(\"exist\")\n\n        // Using a new code succeeds\n        cy.get('input[name=\"lookup_secret\"]').then(($e) => {\n          cy.wrap($e).type(regenCodes[0])\n        })\n        cy.get('*[name=\"method\"][value=\"lookup_secret\"]').click()\n        cy.location(\"pathname\").should(\"not.contain\", \"login\")\n\n        // Going back to the settings UI we should see that the codes have been \"used\"\n        cy.visit(settings)\n        cy.get('button[name=\"lookup_secret_reveal\"]').click()\n        cy.getLookupSecrets().should((c) => {\n          expect(c.slice(1)).to.eql(regenCodes.slice(1))\n          expect(c[0]).to.match(/(Secret was used at )|(Used)/g)\n        })\n      })\n\n      it(\"should end up at login screen if trying to reveal without privileged session\", () => {\n        cy.shortPrivilegedSessionTime()\n        cy.visit(settings)\n        cy.get('button[name=\"lookup_secret_regenerate\"]').click()\n        cy.reauth({\n          expect: { email },\n          type: { email: email, password: password },\n        })\n\n        let codes\n        cy.getLookupSecrets().should((c) => {\n          codes = c\n        })\n\n        cy.shortPrivilegedSessionTime()\n        cy.get('button[name=\"lookup_secret_confirm\"]').click()\n        cy.reauth({\n          expect: { email },\n          type: { email: email, password: password },\n        })\n        if (app === \"react\") {\n          cy.get('button[value=\"profile\"]').click()\n        }\n        cy.expectSettingsSaved()\n\n        if (app !== \"react\") {\n          cy.shortPrivilegedSessionTime()\n          cy.get('button[name=\"lookup_secret_reveal\"]').click()\n          cy.reauth({\n            expect: { email },\n            type: { email: email, password: password },\n          })\n          cy.getLookupSecrets().should((c) => {\n            expect(c).to.not.be.empty\n          })\n          cy.getSession({\n            expectAal: \"aal2\",\n          })\n        }\n      })\n\n      it(\"should not show lookup as an option if not configured\", () => {\n        cy.visit(login + \"?aal=aal2\")\n        cy.get('*[name=\"method\"][value=\"totp\"]').should(\"not.exist\")\n        cy.get('*[name=\"method\"][value=\"lookup_secret\"]').should(\"not.exist\")\n        cy.get('*[name=\"method\"][value=\"password\"]').should(\"not.exist\")\n        cy.get(\"h2\")\n          .invoke(\"text\")\n          .should(\n            \"match\",\n            /Second factor authentication|Two-Factor Authentication/,\n          )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mfa/mix.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../helpers\"\nimport { routes as react } from \"../../../helpers/react\"\nimport { routes as express } from \"../../../helpers/express\"\nimport { TOTP } from \"otpauth\"\n\ncontext(\"2FA with various methods\", () => {\n  beforeEach(() => {\n    cy.task(\"resetCRI\", {})\n  })\n  ;[\n    {\n      login: react.login,\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      login: express.login,\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"mfa\",\n    },\n  ].forEach(({ settings, login, profile, app, base }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n      let email = gen.email()\n      let password = gen.password()\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        email = gen.email()\n        password = gen.password()\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n        cy.clearAllCookies()\n        cy.login({ email, password, cookieUrl: base })\n        cy.longPrivilegedSessionTime()\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.disable\",\n          opts: {},\n        })\n      })\n\n      it(\"should set up an use all mfa combinations\", () => {\n        cy.visit(settings)\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.enable\",\n          opts: {},\n        }).then(() => {\n          cy.task(\"sendCRI\", {\n            query: \"WebAuthn.addVirtualAuthenticator\",\n            opts: {\n              options: {\n                protocol: \"ctap2\",\n                transport: \"usb\",\n                hasResidentKey: true,\n                hasUserVerification: true,\n                isUserVerified: true,\n              },\n            },\n          }).then(() => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"password\"],\n            })\n\n            cy.visit(settings)\n            // Set up TOTP\n            let secret: string\n            cy.get(\n              appPrefix(app) + '[data-testid=\"node/text/totp_secret_key/text\"]',\n            ).then(($e) => {\n              secret = $e.text().trim()\n            })\n            cy.get('[name=\"totp_code\"]').then(($e) => {\n              cy.wrap($e).type(\n                new TOTP({\n                  secret,\n                }).generate(),\n              )\n            })\n            cy.get('[name=\"method\"][value=\"totp\"]').click()\n            cy.expectSettingsSaved()\n            cy.getSession({\n              expectAal: \"aal2\",\n              expectMethods: [\"password\", \"totp\"],\n            })\n\n            // Set up lookup secrets\n            cy.visit(settings)\n            cy.get('[name=\"lookup_secret_regenerate\"]').click()\n            let codes: string[]\n            cy.getLookupSecrets().then((c) => {\n              codes = c\n            })\n            cy.get('[name=\"lookup_secret_confirm\"]').click()\n            cy.expectSettingsSaved()\n            cy.getSession({\n              expectAal: \"aal2\",\n              expectMethods: [\"password\", \"totp\", \"lookup_secret\"],\n            })\n\n            // Set up WebAuthn\n            cy.visit(settings)\n            cy.get('[name=\"webauthn_register_displayname\"]').type(\"my-key\")\n            // We need a workaround here. So first we click, then we submit\n            cy.clickWebAuthButton(\"register\")\n            cy.expectSettingsSaved()\n            cy.getSession({\n              expectAal: \"aal2\",\n              expectMethods: [\"password\", \"totp\", \"webauthn\", \"lookup_secret\"],\n            })\n\n            cy.visit(login + \"?aal=aal2&refresh=true\")\n            cy.get('[name=\"totp_code\"]').then(($e) => {\n              cy.wrap($e).type(\n                new TOTP({\n                  secret,\n                }).generate(),\n              )\n            })\n\n            cy.get('[name=\"method\"][value=\"totp\"]').click()\n            cy.location(\"pathname\").should(\"not.include\", \"/login\")\n\n            cy.getSession({\n              expectAal: \"aal2\",\n              expectMethods: [\n                \"password\",\n                \"totp\",\n                \"webauthn\",\n                \"lookup_secret\",\n                \"totp\",\n              ],\n            })\n\n            // Use TOTP\n            cy.visit(login + \"?aal=aal2&refresh=true\")\n            cy.clickWebAuthButton(\"login\")\n            cy.getSession({\n              expectAal: \"aal2\",\n              expectMethods: [\n                \"password\",\n                \"totp\",\n                \"webauthn\",\n                \"lookup_secret\",\n                \"totp\",\n                \"webauthn\",\n              ],\n            })\n\n            // Use lookup\n            cy.visit(login + \"?aal=aal2&refresh=true\")\n            cy.get('[name=\"lookup_secret\"]').then(($e) => {\n              cy.wrap($e).type(codes[1])\n            })\n            cy.get('[name=\"method\"][value=\"lookup_secret\"]').click()\n            cy.location(\"pathname\").should(\"not.include\", \"/login\")\n\n            cy.getSession({\n              expectAal: \"aal2\",\n              expectMethods: [\n                \"password\",\n                \"totp\",\n                \"webauthn\",\n                \"lookup_secret\",\n                \"totp\",\n                \"webauthn\",\n                \"lookup_secret\",\n              ],\n            })\n          })\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mfa/settings.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../helpers\"\nimport { routes as express } from \"../../../helpers/express\"\nimport { routes as react } from \"../../../helpers/react\"\n\ncontext(\"2FA UI settings tests\", () => {\n  ;[\n    {\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"mfa\",\n    },\n  ].forEach(({ settings, profile, base, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      const email = gen.email()\n      const password = gen.password()\n\n      before(() => {\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.useConfig((builder) => builder.disableCodeMfa())\n        cy.login({ email, password, cookieUrl: base })\n        cy.visit(settings)\n      })\n\n      it(\"shows all settings forms\", () => {\n        cy.get(appPrefix(app) + \"h3\").should(\"contain.text\", \"Profile Settings\")\n        cy.get(\"h3\").should(\"contain.text\", \"Change Password\")\n        cy.get(\"h3\").should(\"contain.text\", \"Manage 2FA Backup Recovery Codes\")\n        cy.get(\"h3\").should(\"contain.text\", \"Manage 2FA TOTP Authenticator App\")\n        cy.get(\"h3\").should(\"contain.text\", \"Manage Hardware Tokens\")\n        cy.get('input[name=\"traits.email\"]').should(\"contain.value\", email)\n        cy.get('input[name=\"traits.website\"]').should(\"contain.value\", website)\n\n        cy.get('[data-testid=\"node/text/totp_secret_key/label\"]').should(\n          \"contain.text\",\n          \"This is your authenticator app secret\",\n        )\n        cy.get(\"button\").should(\n          \"contain.text\",\n          \"Generate new backup recovery codes\",\n        )\n        cy.get(\"button\").should(\"contain.text\", \"Add security key\")\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mfa/totp.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, website } from \"../../../helpers\"\nimport { routes as express } from \"../../../helpers/express\"\nimport { routes as react } from \"../../../helpers/react\"\nimport { TOTP } from \"otpauth\"\n\ncontext(\"2FA TOTP\", () => {\n  ;[\n    {\n      login: react.login,\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      login: express.login,\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"mfa\",\n    },\n  ].forEach(({ settings, login, profile, app, base }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let email = gen.email()\n      let password = gen.password()\n\n      beforeEach(() => {\n        cy.useConfig((builder) =>\n          builder.longPrivilegedSessionTime().useLaxAal().disableCodeMfa(),\n        )\n        email = gen.email()\n        password = gen.password()\n\n        cy.register({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n      })\n\n      it(\"should be be asked to sign in with 2fa if set up\", () => {\n        cy.visit(settings)\n        cy.requireStrictAal()\n\n        let secret: string\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').then(($e) => {\n          secret = $e.text().trim()\n        })\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.expectSettingsSaved()\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"password\", \"totp\"],\n        })\n\n        cy.clearAllCookies()\n        cy.visit(login)\n\n        cy.get('input[name=\"identifier\"]').type(email)\n        cy.get('input[name=\"password\"]').type(password)\n        cy.submitPasswordForm()\n\n        // MFA is now requested\n        cy.shouldShow2FAScreen()\n\n        // If we visit settings page we still end up at 2fa screen\n        cy.visit(settings)\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.include(\"/login\")\n        })\n\n        cy.shouldShow2FAScreen()\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.oneOf([\"/welcome\", \"/\", \"/sessions\", \"/settings\"])\n        })\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"password\", \"totp\"],\n        })\n      })\n\n      it(\"signin with 2fa and be redirected\", () => {\n        if (app !== \"express\") {\n          return\n        }\n\n        cy.visit(settings)\n        cy.requireStrictAal()\n\n        let secret: string\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').then(($e) => {\n          secret = $e.text().trim()\n        })\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.expectSettingsSaved()\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"password\", \"totp\"],\n        })\n\n        cy.clearAllCookies()\n        cy.visit(`${login}?return_to=https://www.example.org/`)\n\n        cy.get('input[name=\"identifier\"]').type(email)\n        cy.get('input[name=\"password\"]').type(password)\n        cy.submitPasswordForm()\n\n        // MFA is now requested\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.include(\"/login\")\n        })\n        cy.shouldShow2FAScreen()\n\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.include(\"/login\")\n        })\n\n        cy.shouldShow2FAScreen()\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.url().should(\"eq\", \"https://www.example.org/\")\n      })\n\n      it(\"should go through several totp lifecycles\", () => {\n        cy.visit(settings)\n\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').should(\"exist\")\n        cy.get('img[data-testid=\"node/image/totp_qr\"]').should(\"exist\")\n\n        // Set up TOTP\n        let secret: string\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').then(($e) => {\n          secret = $e.text().trim()\n        })\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.expectSettingsSaved()\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').should(\n          \"not.exist\",\n        )\n        cy.get('img[data-testid=\"node/image/totp_qr\"]').should(\"not.exist\")\n        cy.get('*[name=\"method\"][value=\"totp\"]').should(\"not.exist\")\n        cy.get('*[name=\"totp_unlink\"]').should(\"exist\")\n\n        // Let's try to do 2FA\n        cy.visit(login + \"?aal=aal2&refresh=true\")\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.include(\"/login\")\n        })\n        cy.get('*[name=\"method\"][value=\"password\"]').should(\"not.exist\")\n\n        // Typing a wrong code leaves us with an error message\n        cy.get('*[name=\"totp_code\"]').type(\"111111\")\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n\n        cy.get('[data-testid=\"ui/message/4000008\"]').should(\n          \"contain.text\",\n          \"The provided authentication code is invalid, please try again.\",\n        )\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"password\", \"totp\", \"totp\"],\n        })\n\n        // Going to settings and unlinking the device\n        cy.visit(settings)\n        cy.get('*[name=\"totp_unlink\"]').click()\n        cy.expectSettingsSaved()\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').should(\"exist\")\n        cy.get('img[data-testid=\"node/image/totp_qr\"]').should(\"exist\")\n        cy.get('*[name=\"method\"][value=\"totp\"]').should(\"exist\")\n        cy.get('*[name=\"totp_unlink\"]').should(\"not.exist\")\n\n        // 2FA should be gone\n        cy.visit(login + \"?aal=aal2&refresh=true\")\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.include(\"/login\")\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').should(\"not.exist\")\n\n        // Linking a new device works\n        cy.visit(settings)\n        let newSecret: string\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').then(($e) => {\n          newSecret = $e.text().trim()\n        })\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret: newSecret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.expectSettingsSaved()\n\n        // Old secret no longer works in login\n        cy.visit(login + \"?aal=aal2&refresh=true\")\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.include(\"/login\")\n        })\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.get('[data-testid=\"ui/message/4000008\"]').should(\n          \"contain.text\",\n          \"The provided authentication code is invalid, please try again.\",\n        )\n\n        // But new one does!\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret: newSecret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.not.include(\"/login\")\n        })\n\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"password\", \"totp\", \"totp\", \"totp\", \"totp\"],\n        })\n\n        // The React app keeps using the same flow. The following scenario used to be broken,\n        // because the internal context wasn't populated properly in the flow after settings were saved.\n        cy.visit(settings)\n        cy.get('*[name=\"totp_unlink\"]').click()\n        cy.expectSettingsSaved()\n\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').then(($e) => {\n          secret = $e.text().trim()\n        })\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.expectSettingsSaved()\n      })\n\n      it(\"should not show totp as an option if not configured\", () => {\n        cy.visit(login + \"?aal=aal2\")\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.include(\"/login\")\n        })\n\n        cy.get('*[name=\"method\"][value=\"totp\"]').should(\"not.exist\")\n        cy.get('*[name=\"method\"][value=\"password\"]').should(\"not.exist\")\n        cy.shouldShow2FAScreen()\n\n        cy.get('[data-testid=\"logout-link\"]').click()\n        cy.location().should((loc) => {\n          expect(loc.href).to.include(\"/login\")\n          expect(loc.search).to.not.include(\"aal\")\n          expect(loc.search).to.not.include(\"refresh\")\n        })\n        cy.noSession()\n      })\n\n      it(\"should fail to set up totp if verify code is wrong\", () => {\n        cy.visit(settings)\n        cy.get('input[name=\"totp_code\"]').type(\"12345678\")\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.get('[data-testid=\"ui/message/4000008\"]').should(\n          \"contain.text\",\n          \"The provided authentication code is invalid, please try again.\",\n        )\n      })\n\n      // The React app keeps using the same flow. The following scenario used to be broken,\n      // because the internal context wasn't populated properly in the flow after settings were saved.\n      it(\"should allow changing other settings and then setting up totp\", () => {\n        cy.visit(settings)\n        cy.get('input[name=\"traits.website\"]')\n          .clear()\n          .type(\"https://some-website.com\")\n        cy.get('*[name=\"method\"][value=\"profile\"]').click()\n        cy.expectSettingsSaved()\n\n        let secret: string\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').then(($e) => {\n          secret = $e.text().trim()\n        })\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.expectSettingsSaved()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mfa/webauthn.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../helpers\"\nimport { routes as react } from \"../../../helpers/react\"\nimport { routes as express } from \"../../../helpers/express\"\n\ncontext(\"2FA WebAuthn\", () => {\n  beforeEach(() => {\n    cy.task(\"resetCRI\", {})\n  })\n  ;[\n    {\n      login: express.login,\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"mfa\",\n    },\n    {\n      login: react.login,\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n  ].forEach(({ settings, login, profile, app, base }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let email = gen.email()\n      let password = gen.password()\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n\n        cy.useConfig((builder) => builder.disableCodeMfa())\n        email = gen.email()\n        password = gen.password()\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n\n        cy.login({ email, password })\n\n        cy.longPrivilegedSessionTime()\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.disable\",\n          opts: {},\n        })\n      })\n\n      it(\"should be able to identify if the authenticator is wrong\", () => {\n        cy.visit(settings)\n\n        // Set up virtual authenticator\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.enable\",\n          opts: {},\n        }).then(() => {\n          cy.task(\"sendCRI\", {\n            query: \"WebAuthn.addVirtualAuthenticator\",\n            opts: {\n              options: {\n                protocol: \"ctap2\",\n                transport: \"usb\",\n                hasResidentKey: true,\n                hasUserVerification: true,\n                isUserVerified: true,\n              },\n            },\n          }).then((addResult) => {\n            cy.get(\n              appPrefix(app) + '[name=\"webauthn_register_displayname\"]',\n            ).type(\"key1\")\n\n            cy.clickWebAuthButton(\"register\")\n\n            cy.get('*[name=\"webauthn_remove\"]').should(\"have.length\", 1)\n\n            cy.task(\"sendCRI\", {\n              query: \"WebAuthn.removeVirtualAuthenticator\",\n              opts: addResult,\n            }).then(() => {\n              cy.visit(login + \"?aal=aal2&refresh=true\")\n              cy.location().should((loc) => {\n                expect(loc.href).to.include(\"/login\")\n              })\n              cy.clickWebAuthButton(\"login\")\n              cy.location().should((loc) => {\n                expect(loc.href).to.include(\"/login\")\n              })\n              cy.getSession({\n                expectAal: \"aal2\",\n                expectMethods: [\"password\", \"webauthn\"],\n              })\n\n              cy.task(\"sendCRI\", {\n                query: \"WebAuthn.addVirtualAuthenticator\",\n                opts: {\n                  options: {\n                    protocol: \"ctap2\",\n                    transport: \"usb\",\n                    hasResidentKey: true,\n                    hasUserVerification: true,\n                    isUserVerified: true,\n                  },\n                },\n              }).then((addResult) => {\n                cy.visit(login + \"?aal=aal2&refresh=true\")\n                cy.location().should((loc) => {\n                  expect(loc.href).to.include(\"/login\")\n                })\n                cy.clickWebAuthButton(\"login\")\n\n                cy.location().should((loc) => {\n                  expect(loc.href).to.include(\"/login\")\n                })\n\n                cy.getSession({\n                  expectAal: \"aal2\",\n                  expectMethods: [\"password\", \"webauthn\"],\n                })\n              })\n            })\n          })\n        })\n      })\n\n      it(\"should be able to link multiple authenticators\", () => {\n        cy.visit(settings)\n\n        // Set up virtual authenticator\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.enable\",\n          opts: {},\n        }).then(() => {\n          cy.task(\"sendCRI\", {\n            query: \"WebAuthn.addVirtualAuthenticator\",\n            opts: {\n              options: {\n                protocol: \"ctap2\",\n                transport: \"usb\",\n                hasResidentKey: true,\n                hasUserVerification: true,\n                isUserVerified: true,\n              },\n            },\n          }).then((addResult) => {\n            cy.get('*[name=\"webauthn_register_displayname\"]').type(\"key1\")\n            cy.clickWebAuthButton(\"register\")\n\n            cy.get('*[name=\"webauthn_register_displayname\"]').type(\"key2\")\n            cy.clickWebAuthButton(\"register\")\n\n            cy.get('*[name=\"webauthn_remove\"]').should(\"have.length\", 2)\n\n            cy.visit(login + \"?aal=aal2&refresh=true\")\n            cy.location().should((loc) => {\n              expect(loc.href).to.include(\"/login\")\n            })\n            cy.get('*[name=\"webauthn_login_trigger\"]').should(\"have.length\", 1)\n            cy.clickWebAuthButton(\"login\")\n          })\n        })\n      })\n\n      it(\"should be not be able to link provider if webauth is not enabled\", () => {\n        cy.visit(settings)\n        cy.get('*[name=\"webauthn_register_displayname\"]').type(\"my-key\")\n        cy.clickWebAuthButton(\"register\")\n        cy.get('*[name=\"webauthn_remove\"]').should(\"not.exist\")\n      })\n\n      it(\"should be able to link a webauthn provider\", () => {\n        cy.visit(settings)\n\n        // Set up virtual authenticator\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.enable\",\n          opts: {},\n        }).then(() => {\n          cy.task(\"sendCRI\", {\n            query: \"WebAuthn.addVirtualAuthenticator\",\n            opts: {\n              options: {\n                protocol: \"ctap2\",\n                transport: \"usb\",\n                hasResidentKey: true,\n                hasUserVerification: true,\n                isUserVerified: true,\n              },\n            },\n          }).then((addResult) => {\n            // Signing up without a display name causes an error\n            cy.get('*[name=\"webauthn_remove\"]').should(\"not.exist\")\n\n            cy.clickWebAuthButton(\"register\")\n\n            cy.get('[data-testid=\"ui/message/4000002\"]').should(\n              \"contain.text\",\n              \"Property webauthn_register_displayname is missing.\",\n            )\n\n            // Setting up with key works\n            cy.get('*[name=\"webauthn_register_displayname\"]').type(\"my-key\")\n\n            // We need a workaround here. So first we click, then we submit\n            cy.clickWebAuthButton(\"register\")\n\n            cy.expectSettingsSaved()\n            cy.get('*[name=\"webauthn_remove\"]').should(\"exist\")\n\n            // Login without refresh\n            cy.login({ email, password })\n            cy.visit(login + \"?aal=aal2\")\n            cy.location().should((loc) => {\n              expect(loc.href).to.include(\"/login\")\n            })\n\n            cy.get('*[name=\"webauthn_login_trigger\"]').should(\"have.length\", 1)\n            cy.clickWebAuthButton(\"login\")\n            cy.location().should((loc) => {\n              expect(loc.href).to.not.include(\"/login\")\n            })\n\n            cy.getSession({\n              expectAal: \"aal2\",\n              expectMethods: [\"password\", \"webauthn\"],\n            })\n\n            // Login with refresh\n            cy.visit(login + \"?aal=aal2&refresh=true\")\n            cy.location().should((loc) => {\n              expect(loc.href).to.include(\"/login\")\n            })\n\n            cy.get('*[name=\"webauthn_login_trigger\"]').should(\"have.length\", 1)\n            cy.clickWebAuthButton(\"login\")\n            cy.location().should((loc) => {\n              expect(loc.href).to.not.include(\"/login\")\n            })\n\n            cy.getSession({\n              expectAal: \"aal2\",\n              expectMethods: [\"password\", \"webauthn\", \"webauthn\"],\n            })\n            cy.visit(settings)\n            cy.get('*[name=\"webauthn_remove\"]').click()\n            cy.get('*[name=\"webauthn_remove\"]').should(\"not.exist\")\n\n            cy.visit(login + \"?aal=aal2&refresh=true\")\n            cy.location().should((loc) => {\n              expect(loc.href).to.include(\"/login\")\n            })\n\n            cy.get('button[name=\"webauthn_login_trigger\"]').should(\"not.exist\")\n            cy.get('[data-testid=\"ui/message/1010003\"]').should(\"exist\")\n          })\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/login/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL } from \"../../../../helpers\"\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"Login Flow Errors\", () => {\n    before(() => {\n      cy.clearAllCookies()\n      cy.useConfigProfile(\"mobile\")\n    })\n\n    beforeEach(() => {\n      cy.visit(MOBILE_URL + \"/Login\")\n    })\n\n    describe(\"shows validation errors when invalid signup data is used\", () => {\n      it(\"should show an error when the identifier is missing\", () => {\n        cy.get('input[data-testid=\"password\"]').type(gen.password())\n\n        cy.get(\n          'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n\n        cy.get('*[data-testid=\"field/identifier\"]').should(\n          \"contain.text\",\n          \"Property identifier is missing.\",\n        )\n\n        cy.get('*[data-testid=\"field/password\"]').should(\n          \"not.contain.text\",\n          \"Property password is missing.\",\n        )\n      })\n\n      it(\"should show an error when the password is missing\", () => {\n        const email = gen.email()\n        cy.get('input[data-testid=\"identifier\"]')\n          .type(email)\n          .should(\"have.value\", email)\n\n        cy.get(\n          'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n\n        cy.get('*[data-testid=\"field/password\"]').should(\n          \"contain.text\",\n          \"Property password is missing.\",\n        )\n      })\n\n      it(\"should show fail to sign in\", () => {\n        cy.get('input[data-testid=\"identifier\"]').type(gen.email())\n        cy.get('input[data-testid=\"password\"]').type(gen.password())\n        cy.get(\n          '*[data-testid=\"field/method/password\"] *[data-testid=\"submit-form\"]',\n        ).click()\n        cy.get('*[data-testid=\"form-messages\"]').should(\n          \"contain.text\",\n          \"credentials are invalid\",\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/login/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL, website } from \"../../../../helpers\"\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"Login Flow Success\", () => {\n    before(() => {\n      cy.useConfigProfile(\"mobile\")\n    })\n\n    const email = gen.email()\n    const password = gen.password()\n\n    before(() => {\n      cy.registerApi({ email, password, fields: { \"traits.website\": website } })\n    })\n\n    beforeEach(() => {\n      cy.clearAllCookies()\n      cy.visit(MOBILE_URL + \"/Login\")\n    })\n\n    it(\"should sign up and be logged in\", () => {\n      cy.get('input[data-testid=\"identifier\"]').type(email)\n      cy.get('input[data-testid=\"password\"]').type(password)\n      cy.get(\n        'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n      ).click()\n\n      cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n      cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/mfa/backup.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL, website } from \"../../../../helpers\"\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"TOTP 2FA Flow\", () => {\n    before(() => {\n      cy.useConfigProfile(\"mobile\")\n    })\n\n    describe(\"password\", () => {\n      let email = gen.email()\n      let password = gen.password()\n\n      before(() => {\n        cy.clearAllCookies()\n      })\n\n      beforeEach(() => {\n        email = gen.email()\n        password = gen.password()\n\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n\n        cy.loginMobile({ email, password })\n        cy.visit(MOBILE_URL + \"/Settings\")\n      })\n\n      it(\"should be able to lifecycle through lookup_secret flows\", () => {\n        cy.get('[data-testid=\"field/lookup_secret_codes\"]').should(\"not.exist\")\n        cy.get('[data-testid=\"field/lookup_secret_confirm/true\"]').should(\n          \"not.exist\",\n        )\n        cy.get('[data-testid=\"field/lookup_secret_reveal/true\"]').should(\n          \"not.exist\",\n        )\n        cy.get('[data-testid=\"field/lookup_secret_regenerate/true\"]').click()\n\n        cy.get('[data-testid=\"field/lookup_secret_reveal/true\"]').should(\n          \"not.exist\",\n        )\n        cy.get('[data-testid=\"field/lookup_secret_codes\"]').should(\"exist\")\n        let codes\n        cy.get('[data-testid=\"field/lookup_secret_codes/text\"]').then(($e) => {\n          codes = $e.text().trim().split(\", \")\n        })\n        cy.get('[data-testid=\"field/lookup_secret_confirm/true\"]').click()\n        cy.expectSettingsSaved()\n\n        cy.get('[data-testid=\"field/lookup_secret_confirm/true\"]').should(\n          \"not.exist\",\n        )\n        cy.get('[data-testid=\"field/lookup_secret_regenerate/true\"]').should(\n          \"not.exist\",\n        )\n        cy.get('[data-testid=\"field/lookup_secret_codes/true\"]').should(\n          \"not.exist\",\n        )\n\n        cy.get('[data-testid=\"field/lookup_secret_reveal/true\"]').click()\n        cy.get('[data-testid=\"field/lookup_secret_regenerate/true\"]').should(\n          \"exist\",\n        )\n        cy.get('[data-testid=\"field/lookup_secret_codes/text\"]').then(($e) => {\n          const actualCodes = $e.text().trim().split(\", \")\n          expect(actualCodes.join(\", \")).to.eq(codes.join(\", \"))\n        })\n\n        let newCodes\n        cy.get('[data-testid=\"field/lookup_secret_regenerate/true\"]').click()\n        cy.get(\n          '[data-testid=\"field/lookup_secret_regenerate/true\"]:disabled',\n        ).should(\"not.exist\")\n\n        cy.get('[data-testid=\"field/lookup_secret_codes/text\"]')\n          .and(($e) => {\n            expect($e.text().trim().split(\", \").join(\", \")).to.not.eq(\n              codes.join(\", \"),\n            )\n          })\n          .then(($e) => {\n            newCodes = $e.text().trim().split(\", \")\n          })\n\n        cy.get('[data-testid=\"field/lookup_secret_confirm/true\"]').click()\n        cy.expectSettingsSaved()\n\n        cy.get('[data-testid=\"field/lookup_secret_reveal/true\"]').click()\n        cy.get('[data-testid=\"field/lookup_secret_codes/text\"]').and(($e) => {\n          const actualCodes = $e.text().trim().split(\", \")\n          expect(actualCodes.join(\", \")).to.eq(newCodes.join(\", \"))\n        })\n\n        cy.visit(MOBILE_URL + \"/Login?aal=aal2&refresh=true\")\n\n        // First use a wrong code\n        cy.get(\"[data-testid=lookup_secret]\").then(($e) => {\n          console.log(codes)\n          cy.wrap($e).type(\"1234\")\n        })\n        cy.get('[data-testid=\"field/method/lookup_secret\"]').click()\n        cy.get('[data-testid=\"form-messages\"]').should(\n          \"contain.text\",\n          \"The backup recovery code is not valid.\",\n        )\n        cy.get(\"[data-testid=lookup_secret]\").then(($e) => {\n          cy.wrap($e).type(newCodes[0])\n        })\n        cy.get('[data-testid=\"field/method/lookup_secret\"]').click()\n        cy.get('[data-testid=\"session-content\"]').should(\"contain\", \"aal2\")\n        cy.get('[data-testid=\"session-content\"]').should(\n          \"contain\",\n          \"lookup_secret\",\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/mfa/mix.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL, website } from \"../../../../helpers\"\nimport { TOTP } from \"otpauth\"\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"TOTP 2FA Flow\", () => {\n    before(() => {\n      cy.useConfigProfile(\"mobile\")\n    })\n\n    describe(\"password\", () => {\n      let email = gen.email()\n      let password = gen.password()\n\n      before(() => {\n        cy.clearAllCookies()\n      })\n\n      beforeEach(() => {\n        email = gen.email()\n        password = gen.password()\n\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n        cy.loginMobile({ email, password })\n        cy.visit(MOBILE_URL + \"/Settings\")\n      })\n\n      it(\"should be able to use both TOTP and lookup\", () => {\n        // set up totp\n        let totpSecret\n        cy.get('*[data-testid=\"field/totp_secret_key/text\"]').then(($e) => {\n          totpSecret = $e.text().trim()\n        })\n        cy.get('*[data-testid=\"field/totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret: totpSecret,\n            }).generate(),\n          )\n        })\n        cy.get('*[data-testid=\"field/method/totp\"]').click()\n        cy.expectSettingsSaved()\n\n        // Set up backup code\n        cy.get('*[data-testid=\"field/lookup_secret_regenerate/true\"]').click()\n        let recoveryCodes\n        cy.get('*[data-testid=\"field/lookup_secret_codes/text\"]').then(($e) => {\n          recoveryCodes = $e.text().trim().split(\", \")\n        })\n        cy.get('*[data-testid=\"field/lookup_secret_confirm/true\"]').click()\n        cy.expectSettingsSaved()\n\n        // Lets sign in with TOTP\n        cy.visit(MOBILE_URL + \"/Login?aal=aal2&refresh=true\")\n        cy.get('*[data-testid=\"field/totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret: totpSecret,\n            }).generate(),\n          )\n        })\n        cy.get('*[data-testid=\"field/method/totp\"]').click()\n\n        // We have AAL now\n        cy.get('[data-testid=\"session-content\"]').should(\"contain\", \"aal2\")\n        cy.get('[data-testid=\"session-content\"]').should(\"contain\", \"totp\")\n\n        // Lets sign in with lookup secret\n        cy.visit(MOBILE_URL + \"/Login?aal=aal2&refresh=true\")\n        cy.get('*[data-testid=\"field/lookup_secret\"]').then(($e) => {\n          cy.wrap($e).type(recoveryCodes[0])\n        })\n        cy.get('*[data-testid=\"field/method/lookup_secret\"]').click()\n\n        // We have AAL now\n        cy.get('[data-testid=\"session-content\"]').should(\"contain\", \"aal2\")\n        cy.get('[data-testid=\"session-content\"]').should(\"contain\", \"totp\")\n        cy.get('[data-testid=\"session-content\"]').should(\n          \"contain\",\n          \"lookup_secret\",\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/mfa/totp.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL, website } from \"../../../../helpers\"\nimport { TOTP } from \"otpauth\"\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"TOTP 2FA Flow\", () => {\n    before(() => {\n      cy.useConfigProfile(\"mobile\")\n    })\n\n    describe(\"password\", () => {\n      let email = gen.email()\n      let password = gen.password()\n\n      before(() => {\n        cy.clearAllCookies()\n      })\n\n      beforeEach(() => {\n        email = gen.email()\n        password = gen.password()\n\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n        cy.loginMobile({ email, password })\n        cy.visit(MOBILE_URL + \"/Settings\")\n      })\n\n      it(\"should be able to lifecycle through TOTP flows\", () => {\n        cy.get('*[data-testid=\"field/totp_qr\"]').should(\"exist\")\n        cy.get('*[data-testid=\"field/totp_code\"]').should(\"exist\")\n\n        // Set up TOTP with invalid key\n        cy.get('*[data-testid=\"field/totp_code\"]').type(\"111111\")\n        cy.get('*[data-testid=\"field/method/totp\"]').click()\n        cy.get('*[data-testid=\"field/totp_code\"]').should(\n          \"contain.text\",\n          \"The provided authentication code is invalid, please try again.\",\n        )\n\n        // Set up TOTP with valid key\n        let secret\n        cy.get('*[data-testid=\"field/totp_secret_key/text\"]').then(($e) => {\n          secret = $e.text().trim()\n        })\n        cy.get('*[data-testid=\"field/totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[data-testid=\"field/method/totp\"]').click()\n        cy.expectSettingsSaved()\n\n        // Form should look different now\n        cy.get('*[data-testid=\"field/totp_secret_key/text\"]').should(\n          \"not.exist\",\n        )\n        cy.get('*[data-testid=\"field/totp_code\"]').should(\"not.exist\")\n        cy.get('*[data-testid=\"field/totp_qr\"]').should(\"not.exist\")\n        cy.get('*[data-testid=\"field/totp_unlink/true\"]').should(\"exist\")\n\n        // Lets sign in\n        cy.visit(MOBILE_URL + \"/Login?aal=aal2&refresh=true\")\n\n        // First use a wrong code\n        cy.get('*[data-testid=\"field/totp_code\"]').type(\"111111\")\n        cy.get('*[data-testid=\"field/method/totp\"]').click()\n        cy.get('*[data-testid=\"form-messages\"]').should(\n          \"contain.text\",\n          \"The provided authentication code is invalid, please try again.\",\n        )\n\n        // Use the correct code\n        cy.get('*[data-testid=\"field/totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[data-testid=\"field/method/totp\"]').click()\n\n        // We have AAL now\n        cy.get('[data-testid=\"session-content\"]').should(\"contain\", \"aal2\")\n        cy.get('[data-testid=\"session-content\"]').should(\"contain\", \"totp\")\n\n        // Go back to settings and unlink\n        cy.visit(MOBILE_URL + \"/Settings\")\n        cy.get('*[data-testid=\"field/totp_unlink/true\"]').click()\n        cy.get('*[data-testid=\"field/totp_unlink/true\"]').should(\"not.exist\")\n        cy.get('*[data-testid=\"field/totp_qr\"]').should(\"exist\")\n        cy.get('*[data-testid=\"field/totp_code\"]').should(\"exist\")\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/passkey/flows.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL, website } from \"../../../../helpers\"\n\nconst signupMobilePasskey = (email = gen.email()) => {\n  cy.visit(MOBILE_URL + \"/Registration\")\n  cy.get('input[data-testid=\"traits.email\"]').type(email)\n  cy.get('input[data-testid=\"traits.website\"]').type(website)\n  cy.get('[data-testid=\"passkey-button\"]').click()\n\n  cy.wait(1000)\n\n  cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n  cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n  return cy.wrap(email)\n}\n\n// The RN app stores the session token in localStorage (AsyncStorage on web).\n// cy.clearAllCookies() alone does not clear it, so login flows fail with a\n// stale token. This helper clears both cookies and localStorage.\nconst clearMobileSession = () => {\n  cy.clearAllCookies()\n  cy.window().then((win) => win.localStorage.clear())\n}\n\n// Logout via the RN app's logout button which clears the stored session.\nconst logoutMobile = () => {\n  cy.get('*[data-testid=\"logout\"]').click()\n  cy.get('input[data-testid=\"identifier\"]').should(\"exist\")\n}\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"Passkey Flows\", () => {\n    let authenticator: any\n\n    before(() => {\n      cy.task(\"resetCRI\", {})\n      cy.useConfigProfile(\"passkey\")\n\n      cy.task(\"sendCRI\", {\n        query: \"WebAuthn.enable\",\n        opts: {},\n      })\n        .then(() => {\n          cy.task(\"sendCRI\", {\n            query: \"WebAuthn.addVirtualAuthenticator\",\n            opts: {\n              options: {\n                protocol: \"ctap2\",\n                transport: \"internal\",\n                hasResidentKey: true,\n                hasUserVerification: true,\n                isUserVerified: true,\n              },\n            },\n          })\n        })\n        .then((result) => {\n          authenticator = result\n          cy.log(\"authenticator ID:\", authenticator)\n        })\n\n      cy.longPrivilegedSessionTime()\n    })\n\n    beforeEach(() => {\n      clearMobileSession()\n      cy.task(\"sendCRI\", {\n        query: \"WebAuthn.clearCredentials\",\n        opts: authenticator,\n      })\n    })\n\n    after(() => {\n      cy.task(\"sendCRI\", {\n        query: \"WebAuthn.removeVirtualAuthenticator\",\n        opts: authenticator,\n      }).then(() => {\n        cy.task(\"resetCRI\", {})\n      })\n    })\n\n    it(\"should register with passkey\", () => {\n      const email = gen.email()\n      signupMobilePasskey(email)\n    })\n\n    it(\"should login with passkey after registration\", () => {\n      const email = gen.email()\n      signupMobilePasskey(email).then((registeredEmail) => {\n        logoutMobile()\n        cy.visit(MOBILE_URL + \"/Login\")\n\n        cy.get('[data-testid=\"passkey-button\"]').click()\n        cy.wait(1000)\n\n        cy.get('[data-testid=\"session-content\"]').should(\n          \"contain\",\n          registeredEmail,\n        )\n        cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n      })\n    })\n\n    it(\"should add passkey in settings after password registration\", () => {\n      const email = gen.email()\n      const password = gen.password()\n\n      cy.registerApi({\n        email,\n        password,\n        fields: { \"traits.website\": website },\n      })\n      cy.loginMobile({ email, password })\n      cy.visit(MOBILE_URL + \"/Settings\")\n\n      cy.get(\n        '*[data-testid=\"settings-passkey\"] [data-testid=\"passkey-button\"]',\n      ).click()\n      cy.wait(1000)\n      cy.expectSettingsSaved()\n    })\n\n    it(\"should not be able to unlink last passkey\", () => {\n      signupMobilePasskey().then(() => {\n        cy.visit(MOBILE_URL + \"/Settings\")\n        // React Native Web renders disabled as aria-disabled on TouchableOpacity\n        cy.get('[data-testid^=\"passkey-remove-\"]').should(\n          \"have.attr\",\n          \"aria-disabled\",\n          \"true\",\n        )\n      })\n    })\n\n    it(\"should be able to link password then remove passkey\", () => {\n      const email = gen.email()\n      const password = gen.password()\n\n      signupMobilePasskey(email).then(() => {\n        cy.visit(MOBILE_URL + \"/Settings\")\n\n        // Add a password\n        cy.get(\n          '*[data-testid=\"settings-password\"] input[data-testid=\"password\"]',\n        )\n          .clear()\n          .type(password)\n        cy.get(\n          '*[data-testid=\"settings-password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.expectSettingsSaved()\n\n        // Now passkey remove should be enabled\n        cy.get('[data-testid^=\"passkey-remove-\"]').should(\n          \"not.have.attr\",\n          \"aria-disabled\",\n          \"true\",\n        )\n        cy.get('[data-testid^=\"passkey-remove-\"]').click()\n        cy.expectSettingsSaved()\n\n        // Logout and login with password\n        logoutMobile()\n        cy.loginMobile({ email, password })\n        cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n        cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/registration/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL, website } from \"../../../../helpers\"\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"Registration Flow Errors\", () => {\n    before(() => {\n      cy.useConfigProfile(\"mobile\")\n    })\n\n    beforeEach(() => {\n      cy.visit(MOBILE_URL + \"/Registration\")\n    })\n\n    const email = gen.email()\n    const password = gen.password()\n\n    describe(\"show errors when invalid signup data is used\", () => {\n      it(\"should show an error when the password has leaked before\", () => {\n        cy.get('input[data-testid=\"traits.email\"]').type(email)\n        cy.get('input[data-testid=\"password\"]').type(\"12345678\")\n        cy.get('input[data-testid=\"traits.website\"]').type(website)\n        cy.get(\n          'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n\n        cy.get('*[data-testid=\"field/password\"]').should(\n          \"contain.text\",\n          \"data breaches\",\n        )\n      })\n\n      it(\"should show an error when the password is too similar\", () => {\n        cy.get('input[data-testid=\"traits.email\"]').type(email)\n        cy.get('input[data-testid=\"password\"]').type(email)\n        cy.get('input[data-testid=\"traits.website\"]').type(website)\n        cy.get(\n          'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n\n        cy.get('*[data-testid=\"field/password\"]').should(\n          \"contain.text\",\n          \"too similar\",\n        )\n      })\n\n      it(\"should show an error when the password is empty\", () => {\n        cy.get('input[data-testid=\"traits.website\"]').type(website)\n        cy.get('input[data-testid=\"traits.email\"]').type(email)\n\n        cy.get(\n          'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.get('*[data-testid=\"field/password\"]').should(\n          \"contain.text\",\n          \"Property password is missing\",\n        )\n      })\n\n      it(\"should show an error when the email is empty\", () => {\n        cy.get('input[data-testid=\"traits.website\"]').type(\"https://www.ory.sh\")\n        cy.get('input[data-testid=\"password\"]').type(password)\n\n        cy.get(\n          'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.get('*[data-testid=\"field/traits.email\"]').should(\n          \"contain.text\",\n          \"Property email is missing\",\n        )\n      })\n\n      it(\"should show an error when the email is not an email\", () => {\n        cy.get('input[data-testid=\"traits.website\"]').type(\"https://www.ory.sh\")\n        cy.get('input[data-testid=\"traits.email\"]').type(\"not-an-email\")\n        cy.get('input[data-testid=\"password\"]').type(password)\n\n        cy.get(\n          'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.get('*[data-testid=\"field/traits.email\"]').should(\n          \"contain.text\",\n          \"Enter a valid email address\",\n        )\n      })\n\n      it(\"should show a missing indicator if no fields are set\", () => {\n        cy.get(\n          'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.get('*[data-testid=\"field/password\"]').should(\n          \"contain.text\",\n          \"Property password is missing\",\n        )\n      })\n\n      it(\"should show an error when the website is too short\", () => {\n        cy.get('input[data-testid=\"traits.website\"]').type(\"http://s\")\n        cy.get('input[data-testid=\"traits.email\"]').type(email)\n\n        cy.get(\n          'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.get('*[data-testid=\"field/traits.website\"]').should(\n          \"contain.text\",\n          \"length must be >= 10\",\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/registration/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL, website } from \"../../../../helpers\"\nimport { testFlowWebhook } from \"../../../../helpers/webhook\"\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"Login Flow Success\", () => {\n    before(() => {\n      cy.useConfigProfile(\"mobile\")\n    })\n\n    beforeEach(() => {\n      cy.deleteMail()\n      cy.visit(MOBILE_URL + \"/Registration\")\n    })\n\n    it(\"should sign up and be logged in\", () => {\n      const email = gen.email()\n      const password = gen.password()\n\n      cy.get('input[data-testid=\"traits.email\"]').type(email)\n      cy.get('input[data-testid=\"password\"]').type(password)\n      cy.get('input[data-testid=\"traits.website\"]').type(website)\n      cy.get(\n        'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n      ).click()\n\n      cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n      cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n    })\n\n    it(\"should pass transient_payload to webhook\", () => {\n      testFlowWebhook(\n        (hooks) =>\n          cy.setupHooks(\"registration\", \"after\", \"password\", [\n            ...hooks,\n            { hook: \"session\" },\n          ]),\n        () => {\n          const email = gen.email()\n          const password = gen.password()\n\n          cy.get('input[data-testid=\"traits.email\"]').type(email)\n          cy.get('input[data-testid=\"password\"]').type(password)\n          cy.get('input[data-testid=\"traits.website\"]').type(website)\n          cy.get(\n            'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n          ).click()\n\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n        },\n      )\n    })\n\n    it(\"should sign up and show verification form\", () => {\n      cy.enableVerification()\n      const email = gen.email()\n      const password = gen.password()\n\n      cy.get('input[data-testid=\"traits.email\"]').type(email)\n      cy.get('input[data-testid=\"password\"]').type(password)\n      cy.get('input[data-testid=\"traits.website\"]').type(website)\n      cy.get(\n        'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n      ).click()\n\n      cy.get('div[data-testid=\"field/code\"] input').should(\"be.visible\")\n\n      cy.getVerificationCodeFromEmail(email).then((code) => {\n        cy.get('div[data-testid=\"field/code\"] input').type(code)\n        cy.get(\n          'div[data-testid=\"field/method/code\"] div[data-testid=submit-form]',\n        ).click()\n      })\n\n      cy.get('[data-testid=\"ui/message/1080002\"]').should(\n        \"have.text\",\n        \"You successfully verified your email address.\",\n      )\n\n      cy.get(\"div[data-testid=continue-button]\").click()\n\n      cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n      cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/settings/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL, website } from \"../../../../helpers\"\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"Settings Flow Errors\", () => {\n    before(() => {\n      cy.useConfigProfile(\"mobile\")\n    })\n\n    let email, password\n\n    before(() => {\n      email = gen.email()\n      password = gen.password()\n      cy.registerApi({ email, password, fields: { \"traits.website\": website } })\n    })\n\n    beforeEach(() => {\n      cy.loginMobile({ email, password })\n      cy.visit(MOBILE_URL + \"/Settings\")\n    })\n\n    describe(\"profile\", () => {\n      it(\"fails with validation errors\", () => {\n        cy.get(\n          '*[data-testid=\"settings-profile\"] input[data-testid=\"traits.website\"]',\n        )\n          .clear()\n          .type(\"http://s\")\n        cy.get(\n          '*[data-testid=\"settings-profile\"] div[data-testid=\"submit-form\"]',\n        ).click()\n\n        cy.get(\n          '*[data-testid=\"settings-profile\"] div[data-testid=\"submit-form\"]',\n        ).should(\"not.have.attr\", \"data-focusable\", \"false\")\n\n        cy.get('*[data-testid=\"field/traits.website\"]').should(\n          \"contain.text\",\n          \"length must be >= 10\",\n        )\n\n        cy.get('*[data-testid=\"settings-password\"]').should(\"exist\")\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/settings/oidc.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, KRATOS_ADMIN, MOBILE_URL, website } from \"../../../../helpers\"\n\ncontext(\"Mobile OIDC Settings\", () => {\n  before(() => {\n    cy.useConfigProfile(\"mobile\")\n  })\n\n  describe(\"link buttons\", () => {\n    const email = gen.email()\n    const password = gen.password()\n\n    before(() => {\n      cy.registerApi({\n        email,\n        password,\n        fields: { \"traits.website\": website },\n      })\n    })\n\n    beforeEach(() => {\n      cy.clearAllCookies()\n      cy.loginMobile({ email, password })\n      cy.visit(MOBILE_URL + \"/Settings\")\n    })\n\n    it(\"should show link buttons for unlinked providers\", () => {\n      cy.get('[data-testid=\"settings-oidc\"]').should(\"exist\")\n      cy.get('[data-testid=\"settings-oidc\"]').within(() => {\n        cy.get('[data-testid=\"field/link/hydra\"]').should(\"exist\")\n        cy.get('[data-testid=\"field/link/google\"]').should(\"exist\")\n        cy.get('[data-testid=\"field/link/github\"]').should(\"exist\")\n      })\n    })\n  })\n\n  describe(\"unlink\", () => {\n    let email: string\n    let password: string\n\n    beforeEach(() => {\n      email = gen.email()\n      password = gen.password()\n\n      // Create an identity with both password and OIDC credentials via Admin API\n      cy.request({\n        method: \"POST\",\n        url: KRATOS_ADMIN + \"/admin/identities\",\n        body: {\n          schema_id: \"default\",\n          traits: { email, website },\n          credentials: {\n            password: { config: { password } },\n            oidc: {\n              config: {\n                providers: [\n                  {\n                    provider: \"hydra\",\n                    subject: email,\n                  },\n                ],\n              },\n            },\n          },\n        },\n      })\n\n      cy.clearAllCookies()\n      cy.loginMobile({ email, password })\n      cy.visit(MOBILE_URL + \"/Settings\")\n    })\n\n    it(\"should show unlink button for linked provider\", () => {\n      cy.get('[data-testid=\"settings-oidc\"]').should(\"exist\")\n      cy.get('[data-testid=\"settings-oidc\"]').within(() => {\n        cy.get('[data-testid=\"field/unlink/hydra\"]').should(\"exist\")\n        cy.get('[data-testid=\"field/link/google\"]').should(\"exist\")\n        cy.get('[data-testid=\"field/link/github\"]').should(\"exist\")\n      })\n    })\n\n    it(\"should unlink a provider\", () => {\n      cy.get('[data-testid=\"settings-oidc\"]').within(() => {\n        cy.get('[data-testid=\"field/unlink/hydra\"]').should(\"exist\")\n        cy.get('[data-testid=\"field/unlink/hydra\"]').click()\n      })\n\n      cy.expectSettingsSaved()\n\n      // After unlinking, the button should change to link\n      cy.get('[data-testid=\"settings-oidc\"]').within(() => {\n        cy.get('[data-testid=\"field/link/hydra\"]').should(\"exist\")\n        cy.get('[data-testid=\"field/unlink/hydra\"]').should(\"not.exist\")\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/mobile/settings/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen, MOBILE_URL, website } from \"../../../../helpers\"\n\ncontext(\"Mobile Profile\", () => {\n  describe(\"Login Flow Success\", () => {\n    before(() => {\n      cy.useConfigProfile(\"mobile\")\n    })\n\n    const up = (value) => `not-${value}`\n\n    describe(\"password\", () => {\n      const email = gen.email()\n      const password = gen.password()\n\n      before(() => {\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.loginMobile({ email, password })\n        cy.visit(MOBILE_URL + \"/Settings\")\n      })\n\n      it(\"modifies the password\", () => {\n        const newPassword = up(password)\n        cy.get(\n          '*[data-testid=\"settings-password\"] input[data-testid=\"password\"]',\n        )\n          .clear()\n          .type(newPassword)\n        cy.get(\n          '*[data-testid=\"settings-password\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.expectSettingsSaved()\n\n        cy.get(\n          '*[data-testid=\"settings-password\"] div[data-testid=\"submit-form\"]',\n        ).should(\"not.have.attr\", \"data-focusable\", \"false\")\n        cy.get('*[data-testid=\"logout\"]').click()\n        cy.get('input[data-testid=\"identifier\"]').should(\"exist\")\n\n        cy.loginMobile({ email, password })\n        cy.get('[data-testid=\"session-token\"]').should(\"not.exist\")\n        cy.loginMobile({ email, password: newPassword })\n        cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n      })\n    })\n\n    describe(\"profile\", () => {\n      let email: string\n      let password: string\n\n      beforeEach(() => {\n        cy.deleteMail()\n        email = gen.email()\n        password = gen.password()\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n        cy.loginMobile({ email, password })\n        cy.location(\"pathname\").should(\"not.contain\", \"/Login\")\n        cy.visit(MOBILE_URL + \"/Settings\")\n      })\n\n      it(\"modifies an unprotected trait\", () => {\n        cy.get(\n          '*[data-testid=\"settings-profile\"] input[data-testid=\"traits.website\"]',\n        )\n          .clear()\n          .type(\"https://github.com/ory\")\n        cy.get(\n          '*[data-testid=\"settings-profile\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.get(\n          '*[data-testid=\"settings-profile\"] div[data-testid=\"submit-form\"]',\n        ).should(\"not.have.attr\", \"data-focusable\", \"false\")\n\n        cy.visit(MOBILE_URL + \"/Home\")\n        cy.get('[data-testid=\"session-content\"]').should(\n          \"contain\",\n          \"https://github.com/ory\",\n        )\n      })\n\n      it(\"modifies a protected trait\", () => {\n        const newEmail = up(email)\n        cy.get(\n          '*[data-testid=\"settings-profile\"] input[data-testid=\"traits.email\"]',\n        )\n          .clear()\n          .type(newEmail)\n        cy.get(\n          '*[data-testid=\"settings-profile\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.get(\n          '*[data-testid=\"settings-profile\"] div[data-testid=\"submit-form\"]',\n        ).should(\"not.have.attr\", \"data-focusable\", \"false\")\n\n        cy.visit(MOBILE_URL + \"/Home\")\n        cy.get('[data-testid=\"session-content\"]').should(\"contain\", newEmail)\n      })\n\n      it(\"shows verification screen after email update\", () => {\n        cy.enableVerification()\n        const newEmail = up(email)\n        cy.get(\n          '*[data-testid=\"settings-profile\"] input[data-testid=\"traits.email\"]',\n        )\n          .clear()\n          .type(newEmail)\n        cy.get(\n          '*[data-testid=\"settings-profile\"] div[data-testid=\"submit-form\"]',\n        ).click()\n        cy.get(\n          '*[data-testid=\"settings-profile\"] div[data-testid=\"submit-form\"]',\n        ).should(\"not.have.attr\", \"data-focusable\", \"false\")\n\n        cy.get('div[data-testid=\"field/code\"] input').should(\"be.visible\")\n\n        cy.getVerificationCodeFromEmail(newEmail).then((code) => {\n          cy.get('div[data-testid=\"field/code\"] input').type(code)\n          cy.get(\n            'div[data-testid=\"field/method/code\"] div[data-testid=submit-form]',\n          ).click()\n        })\n\n        cy.get('[data-testid=\"ui/message/1080002\"]').should(\n          \"have.text\",\n          \"You successfully verified your email address.\",\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/network/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { routes as express } from \"../../../helpers/express\"\nimport { gen } from \"../../../helpers\"\n\ndescribe(\"Registration failures with email profile\", () => {\n  before(() => {\n    cy.useConfigProfile(\"network\")\n    cy.proxy(\"express\")\n  })\n\n  it(\"should not be able to register if we need a localhost schema\", () => {\n    cy.setDefaultIdentitySchema(\"localhost\")\n    cy.visit(express.registration, { failOnStatusCode: false })\n    cy.get('[data-testid=\"code-box\"]').should(\n      \"contain.text\",\n      \"is not a permitted destination\", // could be ::1 or 127.0.0.1\n    )\n  })\n\n  it(\"should not be able to register if we schema has a local ref\", () => {\n    cy.setDefaultIdentitySchema(\"ref\")\n    cy.visit(express.registration, { failOnStatusCode: false })\n    cy.get('[data-testid=\"code-box\"]').should(\n      \"contain.text\",\n      \"192.168.178.1 is not a permitted destination\",\n    )\n  })\n\n  it(\"should not be able to login because pre webhook uses local url\", () => {\n    cy.setDefaultIdentitySchema(\"working\")\n    cy.visit(express.login, { failOnStatusCode: false })\n    cy.get('[data-testid=\"code-box\"]').should(\n      \"contain.text\",\n      \"192.168.178.2 is not a permitted destination\",\n    )\n  })\n\n  it(\"should not be able to verify because post webhook uses local jsonnet\", () => {\n    cy.setDefaultIdentitySchema(\"working\")\n    cy.visit(express.registration, { failOnStatusCode: false })\n    cy.get('input[name=\"traits.email\"]').type(gen.email())\n    cy.get('input[name=\"traits.website\"]').type(\"https://google.com/\")\n    cy.get('input[name=\"password\"]').type(gen.password())\n    cy.get('[type=\"submit\"]').click()\n    cy.get('[data-testid=\"code-box\"]').should(\n      \"contain.text\",\n      \"192.168.178.3 is not a permitted destination\",\n    )\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc/login/error.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { routes as express } from \"../../../../helpers/express\"\n\ncontext(\"Social Sign In Errors\", () => {\n  ;[\n    {\n      login: react.login,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      login: express.login,\n      app: \"express\" as \"express\",\n      profile: \"oidc\",\n    },\n  ].forEach(({ login, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.visit(login)\n      })\n\n      it(\"should fail when the login request is rejected\", () => {\n        cy.triggerOidc(app)\n        cy.get(\"#reject\").click()\n        cy.location(\"pathname\").should(\"equal\", \"/login\")\n        cy.get(appPrefix(app) + '[data-testid=\"ui/message/4000001\"]').should(\n          \"contain.text\",\n          \"login rejected request\",\n        )\n        cy.noSession()\n      })\n\n      it(\"should fail when the consent request is rejected\", () => {\n        const email = gen.email()\n        cy.triggerOidc(app)\n        cy.get(\"#username\").type(email)\n        cy.get(\"#accept\").click()\n        cy.get(\"#reject\").click()\n        cy.location(\"pathname\").should(\"equal\", \"/login\")\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\n          \"contain.text\",\n          \"consent rejected request\",\n        )\n        cy.noSession()\n      })\n\n      it(\"should fail when the id_token is missing\", () => {\n        const email = gen.email()\n        cy.triggerOidc(app)\n        cy.get(\"#username\").type(email)\n        cy.get(\"#accept\").click()\n        cy.get(\"#website\").type(website)\n        cy.get(\"#accept\").click()\n        cy.location(\"pathname\").should(\"equal\", \"/login\")\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\n          \"contain.text\",\n          \"no id_token\",\n        )\n      })\n\n      it(\"should fail to convert a sign in flow to a sign up flow when registration is disabled\", () => {\n        cy.disableRegistration()\n\n        const email = gen.email()\n        cy.visit(login)\n        cy.triggerOidc(app)\n\n        cy.get(\"#username\").clear().type(email)\n        cy.get(\"#remember\").click()\n        cy.get(\"#accept\").click()\n        cy.get('[name=\"scope\"]').each(($el) => cy.wrap($el).click())\n        cy.get(\"#remember\").click()\n        cy.get(\"#accept\").click()\n\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\n          \"contain.text\",\n          \"Registration is not allowed because it was disabled\",\n        )\n\n        cy.noSession()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc/login/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Social Sign In Successes\", () => {\n  ;[\n    {\n      login: react.login,\n      registration: react.registration,\n      settings: react.settings,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      login: express.login,\n      registration: express.registration,\n      settings: express.settings,\n      app: \"express\" as \"express\",\n      profile: \"oidc\",\n    },\n  ].forEach(({ login, registration, profile, app, settings }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n      })\n\n      it(\"should be able to sign up, sign out, and then sign in\", () => {\n        const email = gen.email()\n        cy.registerOidc({ app, email, website, route: registration })\n        cy.logout()\n        cy.noSession()\n        cy.loginOidc({ app, url: login })\n      })\n\n      it(\"should be able to sign up and link existing account\", () => {\n        const email = gen.email()\n        const password = gen.password()\n\n        // Create a new account\n        cy.registerApi({\n          email,\n          password,\n          fields: { \"traits.website\": website },\n        })\n\n        // Try to log in with the same identifier through OIDC. This should fail and create a new login flow.\n        cy.registerOidc({\n          app,\n          email,\n          website,\n          expectSession: false,\n        })\n        cy.noSession()\n\n        // Log in with the same identifier through the login flow. This should link the accounts.\n        cy.get('input[name=\"password\"]').type(password)\n        cy.submitPasswordForm()\n        cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n        cy.getSession()\n\n        // Hydra OIDC should now be linked\n        cy.visit(settings)\n        cy.get('[value=\"hydra\"]')\n          .should(\"have.attr\", \"name\", \"unlink\")\n          .should(\"contain.text\", \"Unlink Ory\")\n      })\n\n      it(\"should be able to sign up with redirects\", () => {\n        const email = gen.email()\n        cy.registerOidc({\n          app,\n          email,\n          website,\n          route: registration + \"?return_to=https://www.example.org/\",\n        })\n        cy.location(\"href\").should(\"eq\", \"https://www.example.org/\")\n        cy.logout()\n        cy.noSession()\n        cy.loginOidc({\n          app,\n          url: login + \"?return_to=https://www.example.org/\",\n        })\n        cy.location(\"href\").should(\"eq\", \"https://www.example.org/\")\n      })\n\n      it(\"should be able to log in with upstream parameters\", () => {\n        const email = gen.email()\n        cy.registerOidc({\n          app,\n          email,\n          website,\n          route: registration + \"?return_to=https://www.example.org/\",\n        })\n\n        cy.location(\"href\").should(\"eq\", \"https://www.example.org/\")\n        cy.logout()\n        cy.noSession()\n        cy.intercept(\"GET\", \"**/oauth2/auth*\").as(\"getHydraLogin\")\n        cy.loginOidc({\n          app,\n          url: login + \"?return_to=https://www.example.org/\",\n          preTriggerHook: () => {\n            // add login_hint to upstream_parameters\n            // this injects an input element into the form\n            cy.addInputElement(\"form\", \"upstream_parameters.login_hint\", email)\n          },\n        })\n        // once a request to getHydraLogin responds, 'cy.wait' will resolve\n        cy.wait(\"@getHydraLogin\")\n          .its(\"request.url\")\n          .should(\"include\", \"login_hint=\" + encodeURIComponent(email))\n\n        cy.location(\"href\").should(\"eq\", \"https://www.example.org/\")\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc/logout/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { routes as express } from \"../../../../helpers/express\"\n\ncontext(\"Social Sign Out Successes\", () => {\n  ;[\n    {\n      base: react.base,\n      registration: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      base: express.base,\n      registration: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"oidc\",\n    },\n  ].forEach(({ base, registration, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      before(() => {\n        cy.clearAllCookies()\n      })\n\n      beforeEach(() => {\n        cy.visit(base)\n        const email = gen.email()\n        cy.registerOidc({ app, email, website, route: registration })\n      })\n\n      it(\"should sign out and be able to sign in again\", () => {\n        if (app === \"express\") {\n          cy.get(\n            `${appPrefix(app)} [data-testid=\"logout\"] a:not(disabled)`,\n          ).click()\n        } else {\n          cy.get(\n            `${appPrefix(app)} [data-testid=\"logout\"]:not(disabled)`,\n          ).click()\n        }\n        cy.noSession()\n        cy.url().should(\"include\", \"/login\")\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc/registration/error.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Social Sign Up Errors\", () => {\n  ;[\n    {\n      registration: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      registration: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"oidc\",\n    },\n  ].forEach(({ registration, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.visit(registration)\n      })\n\n      it(\"should fail when the login request is rejected\", () => {\n        cy.triggerOidc(app)\n        cy.get(\"#reject\").click()\n        cy.location(\"pathname\").should(\"equal\", \"/registration\")\n        cy.get(appPrefix(app) + '[data-testid=\"ui/message/4000001\"]').should(\n          \"contain.text\",\n          \"login rejected request\",\n        )\n        cy.noSession()\n      })\n\n      it(\"should fail when the consent request is rejected\", () => {\n        const email = gen.email()\n        cy.triggerOidc(app)\n        cy.get(\"#username\").type(email)\n        cy.get(\"#accept\").click()\n        cy.get(\"#reject\").click()\n        cy.location(\"pathname\").should(\"equal\", \"/registration\")\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\n          \"contain.text\",\n          \"consent rejected request\",\n        )\n        cy.noSession()\n      })\n\n      it(\"should fail when the id_token is missing\", () => {\n        const email = gen.email()\n        cy.triggerOidc(app)\n        cy.get(\"#username\").type(email)\n        cy.get(\"#accept\").click()\n        cy.get(\"#website\").type(website)\n        cy.get(\"#accept\").click()\n        cy.location(\"pathname\").should(\"equal\", \"/registration\")\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\n          \"contain.text\",\n          \"no id_token\",\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc/registration/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { testFlowWebhook } from \"../../../../helpers/webhook\"\n\ncontext(\"Social Sign Up Successes\", () => {\n  ;[\n    {\n      login: react.login,\n      registration: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      login: express.login,\n      registration: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"oidc\",\n    },\n  ].forEach(({ registration, login, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.visit(registration)\n        cy.setIdentitySchema(\n          \"file://test/e2e/profiles/oidc/identity.traits.schema.json\",\n        )\n      })\n\n      const shouldSession = (email) => (session) => {\n        const { identity } = session\n        expect(identity.id).to.not.be.empty\n        expect(identity.traits.website).to.equal(website)\n        expect(identity.traits.email).to.equal(email)\n      }\n\n      it(\"should be able to sign up with incomplete data and finally be signed in\", () => {\n        const email = gen.email()\n\n        cy.registerOidc({\n          app,\n          email,\n          expectSession: false,\n          route: registration,\n        })\n\n        cy.get(\"#registration-password\").should(\"not.exist\")\n        cy.get(appPrefix(app) + '[name=\"traits.email\"]').should(\n          \"have.value\",\n          email,\n        )\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\n          \"contain.text\",\n          \"Property website is missing\",\n        )\n\n        cy.get('[name=\"traits.consent\"][type=\"checkbox\"]')\n          .siblings(\"label\")\n          .click()\n        cy.get('[name=\"traits.newsletter\"][type=\"checkbox\"]')\n          .siblings(\"label\")\n          .click()\n        cy.get('[name=\"traits.website\"]').type(\"http://s\")\n\n        cy.get('[name=\"provider\"]')\n          .should(\"have.length\", 1)\n          .should(\"have.value\", \"hydra\")\n          .should(\"contain.text\", \"Continue\")\n          .click()\n\n        cy.get(\"#registration-password\").should(\"not.exist\")\n        cy.get('[name=\"traits.email\"]').should(\"have.value\", email)\n        cy.get('[name=\"traits.website\"]').should(\"have.value\", \"http://s\")\n        cy.get('[data-testid=\"ui/message/4000003\"]').should(\n          \"contain.text\",\n          \"length must be >= 10\",\n        )\n        cy.get('[name=\"traits.website\"]')\n          .should(\"have.value\", \"http://s\")\n          .clear()\n          .type(website)\n\n        cy.get('[name=\"traits.consent\"]').should(\"be.checked\")\n        cy.get('[name=\"traits.newsletter\"]').should(\"be.checked\")\n\n        cy.triggerOidc(app)\n\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.be.oneOf([\"/welcome\", \"/\", \"/sessions\"])\n        })\n\n        cy.getSession().should((session) => {\n          shouldSession(email)(session)\n          expect(session.identity.traits.consent).to.equal(true)\n        })\n      })\n\n      it(\"should pass transient_payload to webhook\", () => {\n        testFlowWebhook(\n          (hooks) =>\n            cy.setupHooks(\"registration\", \"after\", \"oidc\", [\n              ...hooks,\n              { hook: \"session\" },\n            ]),\n          () => {\n            const email = gen.email()\n            cy.registerOidc({\n              app,\n              email,\n              website,\n              route: registration,\n            })\n            cy.getSession().should(shouldSession(email))\n          },\n        )\n      })\n\n      it(\"should be able to sign up with complete data\", () => {\n        const email = gen.email()\n\n        cy.registerOidc({ app, email, website, route: registration })\n        cy.getSession().should(shouldSession(email))\n      })\n\n      it(\"should be able to convert a sign up flow to a sign in flow\", () => {\n        const email = gen.email()\n\n        cy.registerOidc({ app, email, website, route: registration })\n        cy.logout()\n        cy.noSession()\n        cy.visit(registration)\n        cy.triggerOidc(app)\n\n        cy.location(\"pathname\").should((path) => {\n          expect(path).to.oneOf([\"/\", \"/welcome\", \"/sessions\"])\n        })\n\n        cy.getSession().should(shouldSession(email))\n      })\n\n      it(\"should be able to convert a sign in flow to a sign up flow\", () => {\n        cy.setIdentitySchema(\n          \"file://test/e2e/profiles/oidc/identity-required.traits.schema.json\",\n        )\n\n        const email = gen.email()\n        cy.visit(login)\n        cy.triggerOidc(app)\n\n        cy.get(\"#username\").clear().type(email)\n        cy.get(\"#remember\").click()\n        cy.get(\"#accept\").click()\n        cy.get('[name=\"scope\"]').each(($el) => cy.wrap($el).click())\n        cy.get(\"#remember\").click()\n        cy.get(\"#accept\").click()\n\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\n          \"contain.text\",\n          \"Property website is missing\",\n        )\n        cy.get('[name=\"traits.website\"]').type(\"http://s\")\n\n        cy.triggerOidc(app)\n\n        cy.get('[data-testid=\"ui/message/4000003\"]').should(\n          \"contain.text\",\n          \"length must be >= 10\",\n        )\n        cy.get('[name=\"traits.requirednested\"]').should(\"not.exist\")\n        cy.get('[name=\"traits.requirednested.a\"]').siblings(\"label\").click()\n        cy.get('[name=\"traits.consent\"]').siblings(\"label\").click()\n        cy.get('[name=\"traits.website\"]')\n          .should(\"have.value\", \"http://s\")\n          .clear()\n          .type(website)\n        cy.triggerOidc(app)\n\n        cy.location(\"pathname\").should(\"not.contain\", \"/registration\")\n\n        cy.getSession().should(shouldSession(email))\n      })\n\n      it(\"should be able to sign up with redirects\", () => {\n        const email = gen.email()\n        cy.registerOidc({\n          app,\n          email,\n          website,\n          route: registration + \"?return_to=https://www.example.org/\",\n        })\n        cy.location(\"href\").should(\"eq\", \"https://www.example.org/\")\n        cy.logout()\n      })\n\n      it(\"should be able to register with upstream parameters\", () => {\n        const email = gen.email()\n        cy.intercept(\"GET\", \"**/oauth2/auth*\").as(\"getHydraRegistration\")\n\n        cy.visit(registration + \"?return_to=https://www.example.org/\")\n\n        cy.addInputElement(\"form\", \"upstream_parameters.login_hint\", email)\n\n        cy.triggerOidc(app)\n\n        // once a request to getHydraRegistration responds, 'cy.wait' will resolve\n        cy.wait(\"@getHydraRegistration\")\n          .its(\"request.url\")\n          .should(\"include\", \"login_hint=\" + encodeURIComponent(email))\n      })\n\n      it(\"oidc registration with duplicate identifier should return new login flow with duplicate error\", () => {\n        cy.visit(registration)\n\n        const email = gen.email()\n        const password = gen.password()\n\n        cy.get('input[name=\"traits.email\"]').type(email)\n        cy.get('input[name=\"password\"]').type(password)\n        cy.get('input[name=\"traits.website\"]').type(website)\n        cy.get('[name=\"traits.consent\"]').siblings(\"label\").click()\n        cy.get('[name=\"traits.newsletter\"]').siblings(\"label\").click()\n\n        cy.submitPasswordForm()\n\n        cy.location(\"pathname\").should(\"not.contain\", \"/registration\")\n\n        cy.getSession().should(shouldSession(email))\n\n        cy.logout()\n        cy.noSession()\n\n        // register the account through the OIDC provider\n        cy.registerOidc({\n          app,\n          acceptConsent: true,\n          acceptLogin: true,\n          expectSession: false,\n          email,\n          website,\n          route: registration,\n        })\n\n        cy.get('[data-testid=\"ui/message/1010016\"]').should(\"be.visible\")\n\n        cy.location(\"href\").should(\"contain\", \"/login\")\n\n        cy.get(\"[name='provider'][value='google']\").should(\"be.visible\")\n        cy.get(\"[name='provider'][value='github']\").should(\"be.visible\")\n\n        if (app === \"express\") {\n          cy.get(\"[data-testid='forgot-password-link']\").should(\"be.visible\")\n        }\n\n        cy.get(\"input[name='password']\").type(password)\n        cy.submitPasswordForm()\n        cy.getSession()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc/settings/error.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APP_URL, appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { routes as express } from \"../../../../helpers/express\"\n\ncontext(\"Social Sign In Settings Errors\", () => {\n  ;[\n    {\n      registration: react.registration,\n      settings: react.settings,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      registration: express.registration,\n      settings: express.settings,\n      app: \"express\" as \"express\",\n      profile: \"oidc\",\n    },\n  ].forEach(({ registration, profile, app, settings }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n      let email\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        email = gen.email()\n\n        cy.registerOidc({\n          app,\n          email,\n          expectSession: true,\n          website,\n          route: registration,\n        })\n        cy.visit(settings)\n      })\n\n      describe(\"oidc\", () => {\n        it(\"should fail to link google because id token is missing\", () => {\n          cy.get(appPrefix(app) + 'button[value=\"google\"]').click()\n          cy.get(\"#remember\").click()\n          cy.get(\"#accept\").click()\n\n          cy.get('[data-testid=\"ui/message/4000001\"]').should(\n            \"contain.text\",\n            'Authentication failed because no id_token was returned. Please accept the \"openid\" permission and try again.',\n          )\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc/settings/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { util } from \"prettier\"\nimport skip = util.skip\n\ncontext(\"Social Sign In Settings Success\", () => {\n  ;[\n    {\n      registration: react.registration,\n      settings: react.settings,\n      login: react.login,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      registration: express.registration,\n      settings: express.settings,\n      login: express.login,\n      app: \"express\" as \"express\",\n      profile: \"oidc\",\n    },\n  ].forEach(({ registration, login, profile, app, settings }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let email\n\n      const hydraReauthFails = () => {\n        cy.clearAllCookies()\n        cy.visit(login)\n        cy.get(appPrefix(app) + '[value=\"hydra\"]').click()\n\n        cy.get(\"#username\").type(email)\n        cy.get(\"#remember\").click()\n        cy.get(\"#accept\").click()\n\n        cy.get('input[name=\"traits.website\"]').clear().type(website)\n        cy.triggerOidc(app, \"hydra\")\n\n        cy.get('[data-testid=\"ui/message/1010016\"]').should(\n          \"contain.text\",\n          \"as another way to sign in.\",\n        )\n\n        cy.noSession()\n      }\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        email = gen.email()\n\n        cy.registerOidc({\n          app,\n          email,\n          expectSession: true,\n          website,\n          route: registration,\n        })\n        cy.visit(settings)\n      })\n\n      describe(\"oidc\", () => {\n        beforeEach(() => {\n          cy.useConfig((builder) =>\n            builder\n              .longRecoveryLifespan()\n              .longVerificationLifespan()\n              .longPrivilegedSessionTime(),\n          )\n        })\n\n        it(\"should show the correct options\", () => {\n          cy.get('[value=\"hydra\"]').should(\"not.exist\")\n\n          cy.get('[value=\"google\"]')\n            .should(\"have.attr\", \"name\", \"link\")\n            .should(\"contain.text\", \"Link google\")\n\n          cy.get('[value=\"github\"]')\n            .should(\"have.attr\", \"name\", \"link\")\n            .should(\"contain.text\", \"Link github\")\n        })\n\n        it(\"should show the unlink once password is set\", () => {\n          cy.get('[value=\"hydra\"]').should(\"not.exist\")\n\n          cy.get('input[name=\"password\"]').type(gen.password())\n          cy.get('button[value=\"password\"]').click()\n\n          cy.get('[value=\"hydra\"]')\n            .should(\"have.attr\", \"name\", \"unlink\")\n            .should(\"contain.text\", \"Unlink Ory\")\n        })\n\n        it(\"should link google\", () => {\n          cy.get('[value=\"google\"]').click()\n\n          cy.get('input[name=\"scope\"]').each(($el) => cy.wrap($el).click())\n          cy.get(\"#remember\").click()\n          cy.get(\"#accept\").click()\n\n          cy.visit(settings)\n\n          cy.get('[value=\"google\"]')\n            .should(\"have.attr\", \"name\", \"unlink\")\n            .should(\"contain.text\", \"Unlink google\")\n\n          cy.logout()\n\n          cy.visit(login)\n\n          // we create an intercept for login so we make sure the login endpoint is called\n          cy.intercept(\"GET\", \"**/oauth2/auth*\").as(\"login\")\n\n          cy.get('[value=\"google\"]').click()\n\n          // wait for the login endpoint to be called\n          cy.wait(\"@login\")\n\n          // check the session\n          cy.getSession()\n        })\n\n        it(\"should link google after re-auth\", () => {\n          cy.shortPrivilegedSessionTime()\n          cy.get('[value=\"google\"]').click()\n          cy.location(\"pathname\").should(\"equal\", \"/login\")\n\n          cy.longPrivilegedSessionTime()\n          cy.get('[value=\"hydra\"]').click()\n\n          // prompt=login means that we need to re-auth!\n          cy.get(\"#username\").type(email)\n          cy.get(\"#accept\").click()\n\n          // we re-authed, now we do the google oauth2 dance\n          cy.get(\"#username\").type(gen.email())\n          cy.get(\"#accept\").click()\n          cy.get('input[name=\"scope\"]').each(($el) => cy.wrap($el).click())\n          cy.get(\"#accept\").click()\n\n          cy.expectSettingsSaved()\n\n          cy.get('[value=\"google\"]')\n            .should(\"have.attr\", \"name\", \"unlink\")\n            .should(\"contain.text\", \"Unlink google\")\n\n          cy.visit(settings)\n\n          cy.get('[value=\"google\"]')\n            .should(\"have.attr\", \"name\", \"unlink\")\n            .should(\"contain.text\", \"Unlink google\")\n        })\n\n        it(\"should unlink hydra and no longer be able to sign in\", () => {\n          if (app === \"react\") {\n            // This test is flaky on React, so we skip it for now.\n            return\n          }\n          cy.get('[value=\"hydra\"]').should(\"not.exist\")\n          cy.get('input[name=\"password\"]').type(gen.password())\n          cy.get('[value=\"password\"]').click()\n          cy.expectSettingsSaved()\n          cy.visit(settings)\n\n          cy.get('[value=\"hydra\"]').click()\n\n          // It will no longer be possible to sign up with this provider because of a UNIQUE key violation\n          // because of the email verification table.\n          //\n          // Basically what needs to happen is for the user to use the LINK feature to link this account.\n          hydraReauthFails()\n        })\n\n        it(\"should unlink hydra after reauth\", () => {\n          cy.get('[value=\"hydra\"]').should(\"not.exist\")\n\n          cy.get('input[name=\"password\"]').type(gen.password())\n          cy.get('[value=\"password\"]').click()\n          cy.expectSettingsSaved()\n          cy.visit(settings)\n\n          cy.shortPrivilegedSessionTime()\n          cy.get('[value=\"hydra\"]').click()\n\n          cy.longPrivilegedSessionTime()\n          cy.location(\"pathname\").should(\"equal\", \"/login\")\n          cy.get('[value=\"hydra\"]').click()\n\n          // prompt=login means that we need to re-auth!\n          cy.get(\"#username\").type(email)\n          cy.get(\"#accept\").click()\n          cy.expectSettingsSaved()\n\n          hydraReauthFails()\n        })\n\n        it(\"should show only linked providers during reauth\", () => {\n          cy.shortPrivilegedSessionTime()\n\n          cy.get('input[name=\"password\"]').type(gen.password())\n          cy.get('[value=\"password\"]').click()\n\n          cy.location(\"pathname\").should(\"equal\", \"/login\")\n\n          cy.get('[value=\"hydra\"]').should(\"exist\")\n          cy.get('[value=\"google\"]').should(\"not.exist\")\n          cy.get('[value=\"github\"]').should(\"not.exist\")\n        })\n\n        it(\"settings screen stays intact when the original sign up method gets removed\", () => {\n          const expectSettingsOk = () => {\n            cy.get('[value=\"google\"]', { timeout: 1000 })\n              .should(\"have.attr\", \"name\", \"link\")\n              .should(\"contain.text\", \"Link google\")\n\n            cy.get('[value=\"github\"]', { timeout: 1000 })\n              .should(\"have.attr\", \"name\", \"link\")\n              .should(\"contain.text\", \"Link github\")\n          }\n\n          cy.visit(settings)\n          expectSettingsOk()\n\n          // set password\n          cy.get('input[name=\"password\"]').type(gen.password())\n          cy.get('button[value=\"password\"]').click()\n\n          // remove the provider used to log in\n          cy.updateConfigFile((config) => {\n            config.selfservice.methods.oidc.config.providers =\n              config.selfservice.methods.oidc.config.providers.filter(\n                ({ id }) => id !== \"hydra\",\n              )\n            return config\n          })\n\n          // visit settings and everything should still be fine\n          cy.visit(settings)\n          expectSettingsOk()\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc-provider/login.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen } from \"../../../helpers\"\nimport * as httpbin from \"../../../helpers/httpbin\"\nimport * as oauth2 from \"../../../helpers/oauth2\"\n\ncontext(\"OpenID Provider\", () => {\n  const client = {\n    auth_endpoint: \"http://localhost:4744/oauth2/auth\",\n    token_endpoint: \"http://localhost:4744/oauth2/token\",\n    id: Cypress.env(\"OIDC_DUMMY_CLIENT_ID\"),\n    secret: Cypress.env(\"OIDC_DUMMY_CLIENT_SECRET\"),\n    token_endpoint_auth_method: \"client_secret_basic\",\n    grant_types: [\"authorization_code\", \"refresh_token\"],\n    response_types: [\"code\", \"id_token\"],\n    scopes: [\"openid\", \"offline\", \"email\", \"website\"],\n    callbacks: [\n      \"http://localhost:5555/callback\",\n      \"https://ory-network-httpbin-ijakee5waq-ez.a.run.app/anything\",\n    ],\n  }\n\n  before(() => {\n    cy.deleteMail()\n    cy.useConfigProfile(\"oidc-provider\")\n    cy.proxy(\"express\")\n  })\n\n  it(\"login\", () => {\n    const email = gen.email()\n    const password = gen.password()\n    cy.registerApi({\n      email: email,\n      password: password,\n      fields: { \"traits.website\": \"http://t1.local\" },\n    })\n\n    const url = oauth2.getDefaultAuthorizeURL(client)\n\n    cy.visit(url)\n\n    // kratos login ui\n    cy.get(\"[name=identifier]\").type(email)\n    cy.get(\"[name=password]\").type(password)\n    cy.get(\"[type='submit'][value='password']\").click()\n\n    // consent ui\n    cy.get(\"#openid\").click()\n    cy.get(\"#offline\").click()\n    cy.get(\"#accept\").click()\n\n    const scope = [\"offline\", \"openid\"]\n    httpbin.checkToken(client, scope, (token: any) => {\n      expect(token).to.have.property(\"access_token\")\n      expect(token).to.have.property(\"id_token\")\n      expect(token).to.have.property(\"refresh_token\")\n      expect(token).to.have.property(\"token_type\")\n      expect(token).to.have.property(\"expires_in\")\n      expect(token.scope).to.equal(\"offline openid\")\n      let idToken = JSON.parse(\n        decodeURIComponent(escape(window.atob(token.id_token.split(\".\")[1]))),\n      )\n      expect(idToken).to.have.property(\"amr\")\n      expect(idToken.amr).to.deep.equal([\"password\"])\n    })\n  })\n\n  it(\"login-without-scopes\", () => {\n    const email = gen.email()\n    const password = gen.password()\n    cy.registerApi({\n      email: email,\n      password: password,\n      fields: { \"traits.website\": \"http://t1.local\" },\n    })\n\n    const url = oauth2.getDefaultAuthorizeURL(client)\n    cy.visit(url)\n\n    // kratos login ui\n    cy.get(\"[name=identifier]\").type(email)\n    cy.get(\"[name=password]\").type(password)\n    cy.get(\"[type='submit'][value='password']\").click()\n\n    // consent ui\n    cy.get(\"#accept\").click()\n\n    const scope = [\"offline\", \"openid\"]\n    httpbin.checkToken(client, scope, (token: any) => {\n      expect(token).to.have.property(\"access_token\")\n      expect(token).not.to.have.property(\"id_token\")\n      expect(token).not.to.have.property(\"refresh_token\")\n      expect(token).to.have.property(\"token_type\")\n      expect(token).to.have.property(\"expires_in\")\n      expect(token.scope).to.equal(\"\")\n    })\n  })\n\n  it(\"respects-login-remember-config\", () => {\n    let odicLogin = () => {\n      const email = gen.email()\n      const password = gen.password()\n      cy.registerApi({\n        email: email,\n        password: password,\n        fields: { \"traits.website\": \"http://t1.local\" },\n      })\n\n      let url = oauth2.getDefaultAuthorizeURL(client)\n      cy.visit(url)\n\n      // kratos login ui\n      cy.get(\"[name=identifier]\").type(email)\n      cy.get(\"[name=password]\").type(password)\n      cy.get(\"[type='submit'][value='password']\").click()\n      // consent ui\n      cy.get(\"#accept\").click()\n    }\n\n    cy.clearAllCookies()\n    cy.updateConfigFile((config) => {\n      config.session.cookie = config.session.cookie || {}\n      config.session.cookie.persistent = true\n      config.session.lifespan = \"1234s\"\n      return config\n    })\n\n    odicLogin()\n    cy.getCookies({ domain: \"localhost\" })\n    cy.getCookie(\"ory_hydra_session_dev\", { domain: \"localhost\" }).should(\n      \"not.be.null\",\n    )\n    cy.getCookie(\"ory_hydra_session_dev\", { domain: \"localhost\" }).then(\n      (cookie: Cypress.Cookie) => {\n        let expected = Date.now() / 1000 + 1234\n        let precision = 10\n        expect(cookie.expiry).to.be.lessThan(expected + precision)\n        expect(cookie.expiry).to.be.greaterThan(expected - precision)\n      },\n    )\n\n    cy.clearAllCookies()\n    cy.updateConfigFile((config) => {\n      config.session.cookie = config.session.cookie || {}\n      config.session.cookie.persistent = false\n      return config\n    })\n\n    odicLogin()\n    cy.getCookie(\"ory_hydra_session_dev\", { domain: \"localhost\" }).should(\n      \"be.null\",\n    )\n  })\n})\n\ncontext(\"OpenID Provider - change between flows\", () => {\n  const client = {\n    auth_endpoint: \"http://localhost:4744/oauth2/auth\",\n    token_endpoint: \"http://localhost:4744/oauth2/token\",\n    id: Cypress.env(\"OIDC_DUMMY_CLIENT_ID\"),\n    secret: Cypress.env(\"OIDC_DUMMY_CLIENT_SECRET\"),\n    token_endpoint_auth_method: \"client_secret_basic\",\n    grant_types: [\"authorization_code\", \"refresh_token\"],\n    response_types: [\"code\", \"id_token\"],\n    scopes: [\"openid\", \"offline\", \"email\", \"website\"],\n    callbacks: [\n      \"http://localhost:5555/callback\",\n      \"https://ory-network-httpbin-ijakee5waq-ez.a.run.app/anything\",\n    ],\n  }\n\n  function doConsent(amrs?: string[]) {\n    cy.url().should(\"contain\", \"/consent\")\n\n    // consent ui\n    cy.get(\"#openid\").click()\n    cy.get(\"#offline\").click()\n    cy.get(\"#accept\").click()\n\n    const scope = [\"offline\", \"openid\"]\n    httpbin.checkToken(client, scope, (token: any) => {\n      expect(token).to.have.property(\"access_token\")\n      expect(token).to.have.property(\"id_token\")\n      expect(token).to.have.property(\"refresh_token\")\n      expect(token).to.have.property(\"token_type\")\n      expect(token).to.have.property(\"expires_in\")\n      expect(token.scope).to.equal(\"offline openid\")\n      let idToken = JSON.parse(\n        decodeURIComponent(escape(window.atob(token.id_token.split(\".\")[1]))),\n      )\n      expect(idToken).to.have.property(\"amr\")\n      expect(idToken.amr).to.deep.equal(amrs || [\"password\"])\n    })\n  }\n\n  before(() => {\n    cy.useConfigProfile(\"oidc-provider\")\n    cy.updateConfigFile((config) => {\n      config.selfservice.allowed_return_urls = [\n        oauth2.getDefaultAuthorizeURL(client),\n      ]\n      config.oauth2_provider.override_return_to = true\n      return config\n    })\n    cy.proxy(\"express\")\n  })\n\n  it(\"switch to registration flow\", () => {\n    const identity = gen.identityWithWebsite()\n\n    const url = oauth2.getDefaultAuthorizeURL(client)\n\n    cy.visit(url)\n    cy.get(\"[href*='/registration']\").click()\n    cy.url().should(\"contain\", \"/registration\")\n\n    cy.get(\"[name='traits.email']\").type(identity.email)\n    cy.get(\"[name='password']\").type(identity.password)\n    cy.get(\"[name='traits.website']\").type(identity.fields[\"traits.website\"])\n    cy.get(\"[type='submit'][value='password']\").click()\n\n    doConsent()\n  })\n\n  it(\"switch to recovery flow with password reset\", () => {\n    cy.updateConfigFile((config) => {\n      config.selfservice.flows.recovery.lifespan = \"1m\"\n      config.selfservice.methods.link.config.lifespan = \"1m\"\n      config.selfservice.flows.verification.enabled = false\n      if (!config.selfservice.flows.recovery) {\n        config.selfservice.flows.recovery = {}\n      }\n      config.selfservice.flows.recovery.enabled = true\n      config.selfservice.flows.settings.privileged_session_max_age = \"5m\"\n      return config\n    })\n    cy.deleteMail()\n    cy.useRecoveryStrategy(\"code\")\n    cy.notifyUnknownRecipients(\"recovery\", false)\n\n    const identity = gen.identityWithWebsite()\n    cy.registerApi(identity)\n\n    const url = oauth2.getDefaultAuthorizeURL(client)\n    cy.visit(url)\n    cy.get(\"[href*='/recovery']\").click()\n\n    cy.get(\"input[name='email']\").type(identity.email)\n    cy.get(\"button[value='code']\").click()\n    cy.get('[data-testid=\"ui/message/1060003\"]').should(\n      \"have.text\",\n      \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n    )\n\n    cy.recoveryEmailWithCode({ expect: { email: identity.email } })\n    cy.get(\"button[value='code']\").click()\n\n    cy.get('[data-testid=\"ui/message/1060001\"]', { timeout: 30000 }).should(\n      \"contain.text\",\n      \"You successfully recovered your account. \",\n    )\n\n    cy.getSession()\n    cy.location(\"pathname\").should(\"eq\", \"/settings\")\n    // do a password change\n    const newPassword = gen.password()\n    cy.get('input[name=\"password\"]').clear().type(newPassword)\n    cy.get('button[value=\"password\"]').click()\n\n    // since we aren't skipping the consent, Kratos will ask us to login again\n    cy.get('input[name=\"password\"]').clear().type(newPassword)\n    cy.get('button[value=\"password\"]').click()\n\n    // we should now end up on the consent screen\n    doConsent([\"code_recovery\", \"password\"])\n  })\n\n  it(\"switch to recovery flow with oidc link\", () => {\n    cy.deleteMail()\n    cy.longRecoveryLifespan()\n    cy.longLinkLifespan()\n    cy.enableRecovery()\n    cy.useRecoveryStrategy(\"code\")\n    cy.notifyUnknownRecipients(\"recovery\", false)\n    cy.longPrivilegedSessionTime()\n\n    const fakeOidcFlow = (\n      identity: ReturnType<typeof gen.identityWithWebsite>,\n    ) => {\n      cy.get(\"input[name='username']\").type(identity.email)\n      cy.get(\"button[name='action'][value='accept']\").click()\n      // consent screen for the 'fake' oidc provider\n      cy.url().should(\"contain\", \"/consent\")\n      // consent ui for 3rd party integration to Kratos (sign in with Google)\n      // it will look the same as Hydra consent ui since we are reusing it as\n      // a 'fake' oidc provider\n      cy.get(\"#openid\").click()\n      cy.get(\"#offline\").click()\n      cy.get(\"#accept\").click()\n    }\n\n    const identity = gen.identityWithWebsite()\n    cy.registerApi(identity)\n\n    const url = oauth2.getDefaultAuthorizeURL(client)\n    cy.visit(url)\n\n    cy.get(\"[href*='/recovery']\").click()\n    cy.get(\"input[name='email']\").type(identity.email)\n    cy.get(\"button[value='code']\").click()\n\n    cy.get('[data-testid=\"ui/message/1060003\"]').should(\n      \"have.text\",\n      \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n    )\n\n    cy.recoveryEmailWithCode({ expect: { email: identity.email } })\n    cy.get(\"button[value='code']\").click()\n\n    cy.get('[data-testid=\"ui/message/1060001\"]', { timeout: 30000 }).should(\n      \"contain.text\",\n      \"You successfully recovered your account. \",\n    )\n\n    cy.getSession()\n    cy.location(\"pathname\").should(\"eq\", \"/settings\")\n\n    // (OAauth2+Kratos provider '/settings' -> Fake OAuth2 '/login')\n\n    // we are reusing Hydra as an OIDC provider for Kratos and doing an OIDC account\n    // linking process here\n    // this is to check if the user can recover their account through OIDC linking\n    // and still be redirected back to the original request\n    cy.get(\"button[value='google']\").click()\n    cy.location(\"pathname\").should(\"eq\", \"/login\")\n    fakeOidcFlow(identity)\n\n    // it should return us back to the initial OAauth2 login flow\n    // sign in through the 'fake' oidc provider\n    cy.get(\"button[type='submit'][value='google']\").click()\n\n    // login again at the 'fake' oidc provider\n    fakeOidcFlow(identity)\n\n    // complete the consent screen\n    doConsent([\"oidc\"])\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc-provider/mfa.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen } from \"../../../helpers\"\nimport { routes as express } from \"../../../helpers/express\"\nimport * as oauth2 from \"../../../helpers/oauth2\"\nimport * as httpbin from \"../../../helpers/httpbin\"\nimport { TOTP } from \"otpauth\"\n\ncontext(\"OIDC Provider 2FA\", () => {\n  const client = {\n    auth_endpoint: \"http://localhost:4744/oauth2/auth\",\n    token_endpoint: \"http://localhost:4744/oauth2/token\",\n    id: Cypress.env(\"OIDC_DUMMY_CLIENT_ID\"),\n    secret: Cypress.env(\"OIDC_DUMMY_CLIENT_SECRET\"),\n    token_endpoint_auth_method: \"client_secret_basic\",\n    grant_types: [\"authorization_code\", \"refresh_token\"],\n    response_types: [\"code\", \"id_token\"],\n    scopes: [\"openid\", \"offline\", \"email\", \"website\"],\n    callbacks: [\n      \"http://localhost:5555/callback\",\n      \"https://ory-network-httpbin-ijakee5waq-ez.a.run.app/anything\",\n    ],\n  }\n\n  ;[\n    {\n      login: express.login,\n      settings: express.settings,\n      base: express.base,\n      profile: \"oidc-provider-mfa\",\n      app: \"express\" as \"express\",\n    },\n  ].forEach(({ settings, login, profile, app, base }) => {\n    describe(`for app ${app}`, () => {\n      let email = gen.email()\n      let password = gen.password()\n      let secret\n\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n\n        email = gen.email()\n        password = gen.password()\n\n        cy.register({\n          email,\n          password,\n          fields: { \"traits.website\": \"http://t1.local\" },\n        })\n        cy.visit(settings)\n\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').then(($e) => {\n          secret = $e.text().trim()\n        })\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.expectSettingsSaved()\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"password\", \"totp\"],\n        })\n\n        cy.clearAllCookies()\n      })\n\n      it(\"should be be asked to sign in with 2fa if set up\", () => {\n        let url = oauth2.getDefaultAuthorizeURL(client)\n\n        cy.get(\"body\")\n          .then((body$) => {\n            // Credits https://github.com/suchipi, https://github.com/cypress-io/cypress/issues/944#issuecomment-444312914\n            const appWindow = body$[0].ownerDocument.defaultView\n            const appIframe = appWindow.parent.document.querySelector(\"iframe\")\n\n            return new Promise((resolve) => {\n              appIframe.onload = () => resolve(undefined)\n              appWindow.location.href = url\n            })\n          })\n          .then(() => {\n            // kratos login ui\n            cy.get(\"[name=identifier]\").type(email)\n            cy.get(\"[name=password]\").type(password)\n            cy.get(\"[type=submit]\").click()\n\n            cy.get('input[name=\"totp_code\"]').then(($e) => {\n              cy.wrap($e).type(\n                new TOTP({\n                  secret,\n                }).generate(),\n              )\n            })\n            cy.get('*[name=\"method\"][value=\"totp\"]').click()\n\n            // consent ui\n            cy.get(\"#openid\").click()\n            cy.get(\"#offline\").click()\n            cy.get(\"#accept\").click()\n\n            let scope = [\"offline\", \"openid\"]\n            httpbin.checkToken(client, scope, (token: any) => {\n              expect(token).to.have.property(\"access_token\")\n              expect(token).to.have.property(\"id_token\")\n              expect(token).to.have.property(\"refresh_token\")\n              expect(token).to.have.property(\"token_type\")\n              expect(token).to.have.property(\"expires_in\")\n              expect(token.scope).to.equal(\"offline openid\")\n              let idToken = JSON.parse(\n                decodeURIComponent(\n                  escape(window.atob(token.id_token.split(\".\")[1])),\n                ),\n              )\n              expect(idToken).to.have.property(\"amr\")\n              expect(idToken.amr).to.deep.equal([\"password\", \"totp\"])\n            })\n\n            // We shouldn't need to authenticate again\n            url = oauth2.getDefaultAuthorizeURL(client)\n\n            cy.get(\"body\")\n              .then((body$) => {\n                // Credits https://github.com/suchipi, https://github.com/cypress-io/cypress/issues/944#issuecomment-444312914\n                const appWindow = body$[0].ownerDocument.defaultView\n                const appIframe =\n                  appWindow.parent.document.querySelector(\"iframe\")\n\n                return new Promise((resolve) => {\n                  appIframe.onload = () => resolve(undefined)\n                  appWindow.location.href = url\n                })\n              })\n              .then(() => {\n                // We get the consent screen instead of login\n                cy.get(\"#openid\").click()\n                cy.get(\"#offline\").click()\n                cy.get(\"#accept\").click()\n\n                httpbin.checkToken(client, scope, (token: any) => {\n                  expect(token).to.have.property(\"access_token\")\n                  expect(token).to.have.property(\"id_token\")\n                  expect(token).to.have.property(\"refresh_token\")\n                  expect(token).to.have.property(\"token_type\")\n                  expect(token).to.have.property(\"expires_in\")\n                  expect(token.scope).to.equal(\"offline openid\")\n                  let idToken = JSON.parse(\n                    decodeURIComponent(\n                      escape(window.atob(token.id_token.split(\".\")[1])),\n                    ),\n                  )\n                  expect(idToken).to.have.property(\"amr\")\n                  expect(idToken.amr).to.deep.equal([\"password\", \"totp\"])\n                })\n              })\n          })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/oidc-provider/registration.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APP_URL, gen } from \"../../../helpers\"\nimport * as oauth2 from \"../../../helpers/oauth2\"\nimport * as httpbin from \"../../../helpers/httpbin\"\n\ncontext(\"OpenID Provider\", () => {\n  before(() => {\n    cy.useConfigProfile(\"oidc-provider\")\n    cy.proxy(\"express\")\n  })\n  const client = {\n    auth_endpoint: \"http://localhost:4744/oauth2/auth\",\n    token_endpoint: \"http://localhost:4744/oauth2/token\",\n    id: Cypress.env(\"OIDC_DUMMY_CLIENT_ID\"),\n    secret: Cypress.env(\"OIDC_DUMMY_CLIENT_SECRET\"),\n    token_endpoint_auth_method: \"client_secret_basic\",\n    grant_types: [\"authorization_code\", \"refresh_token\"],\n    response_types: [\"code\", \"id_token\"],\n    scopes: [\"openid\", \"offline\", \"email\", \"website\"],\n    callbacks: [\n      \"http://localhost:5555/callback\",\n      \"https://ory-network-httpbin-ijakee5waq-ez.a.run.app/anything\",\n    ],\n  }\n\n  it(\"registration\", () => {\n    const url = oauth2.getDefaultAuthorizeURL(client)\n\n    cy.visit(url)\n    cy.get(\"[data-testid=signup-link]\").click()\n\n    const email = gen.email()\n    const password = gen.password()\n\n    cy.get('[name=\"traits.email\"]').type(email)\n    cy.get(\"[name=password]\").type(password)\n    cy.get('[name=\"traits.website\"]').type(\"http://example.com\")\n    cy.get('input[type=checkbox][name=\"traits.tos\"]').click({ force: true })\n    cy.get('[name=\"traits.age\"]').type(\"199\")\n    cy.get('input[type=checkbox][name=\"traits.consent\"]').click({ force: true })\n    cy.get('input[type=checkbox][name=\"traits.newsletter\"]').click({\n      force: true,\n    })\n\n    cy.get(\"[type='submit'][value='password']\").click()\n\n    cy.get(\"#openid\").click()\n    cy.get(\"#offline\").click()\n    cy.get(\"#accept\").click()\n\n    const scope = [\"offline\", \"openid\"]\n    httpbin.checkToken(client, scope, (token: any) => {\n      expect(token).to.have.property(\"access_token\")\n      expect(token).to.have.property(\"id_token\")\n      expect(token).to.have.property(\"refresh_token\")\n      expect(token).to.have.property(\"token_type\")\n      expect(token).to.have.property(\"expires_in\")\n      expect(token.scope).to.equal(\"offline openid\")\n      let idToken = JSON.parse(\n        decodeURIComponent(escape(window.atob(token.id_token.split(\".\")[1]))),\n      )\n      expect(idToken).to.have.property(\"amr\")\n      expect(idToken.amr).to.deep.equal([\"password\"])\n    })\n  })\n\n  it(\"registration with session, skip=false and skip=true\", () => {\n    const email = gen.email()\n    const password = gen.password()\n\n    cy.register({\n      email,\n      password,\n      fields: {\n        \"traits.website\": \"https://www.ory.sh\",\n        \"traits.tos\": \"1\",\n        \"traits.age\": 22,\n      },\n    })\n\n    const url = oauth2.getDefaultAuthorizeURL(client)\n\n    cy.request(url).then((res) => {\n      const lastResp = res.allRequestResponses[1][\"Request URL\"]\n      const login_challenge = new URL(lastResp).searchParams.get(\n        \"login_challenge\",\n      )\n      expect(login_challenge).to.not.be.null\n      cy.visit(\n        APP_URL +\n          \"/self-service/registration/browser?login_challenge=\" +\n          login_challenge,\n      )\n    })\n\n    cy.url().should(\"contain\", \"/login\")\n    cy.get(\"[data-testid='login-flow']\").should(\"exist\")\n    cy.get(\"[data-testid='login-flow'] [name='password']\").type(password)\n    cy.get(\n      \"[data-testid='login-flow'] button[name='method'][value='password']\",\n    ).click()\n\n    // we want to skip the consent flow here\n    // so we ask to remember the user\n    cy.get(\"[name='remember']\").click()\n    cy.get(\"#openid\").click()\n    cy.get(\"#offline\").click()\n    cy.get(\"#accept\").click()\n\n    const scope = [\"offline\", \"openid\"]\n    httpbin.checkToken(client, scope, (token: any) => {\n      expect(token).to.have.property(\"access_token\")\n      expect(token).to.have.property(\"id_token\")\n      expect(token).to.have.property(\"refresh_token\")\n      expect(token).to.have.property(\"token_type\")\n      expect(token).to.have.property(\"expires_in\")\n      expect(token.scope).to.equal(\"offline openid\")\n      let idToken = JSON.parse(\n        decodeURIComponent(escape(window.atob(token.id_token.split(\".\")[1]))),\n      )\n      expect(idToken).to.have.property(\"amr\")\n      expect(idToken.amr).to.deep.equal([\"password\", \"password\"])\n    })\n\n    // use the hydra origin to make a new OAuth request from it\n    cy.get(\"body\")\n      .then((body$) => {\n        // Credits https://github.com/suchipi, https://github.com/cypress-io/cypress/issues/944#issuecomment-444312914\n        const appWindow = body$[0].ownerDocument.defaultView\n        const appIframe = appWindow.parent.document.querySelector(\"iframe\")\n\n        return new Promise((resolve) => {\n          appIframe.onload = () => resolve(undefined)\n          appWindow.location.href = \"http://localhost:4744/health/ready\"\n        })\n      })\n      .then(() => {\n        // we don't want to redirect here since we only want the login challenge from hydra\n        // we reusing the challenge to navigate to the registration page\n        cy.request({\n          url: oauth2.getDefaultAuthorizeURL(client),\n          followRedirect: false,\n        })\n          .then((res) => {\n            expect(res.redirectedToUrl).to.include(\"login_challenge\")\n            return new URL(res.redirectedToUrl).searchParams.get(\n              \"login_challenge\",\n            )\n          })\n          .then((login_challenge) => {\n            cy.get(\"body\").then((body$) => {\n              const appWindow = body$[0].ownerDocument.defaultView\n              const appIframe =\n                appWindow.parent.document.querySelector(\"iframe\")\n\n              return new Promise((resolve) => {\n                appIframe.onload = () => resolve(undefined)\n                appWindow.location.href =\n                  APP_URL +\n                  \"/self-service/registration/browser?login_challenge=\" +\n                  login_challenge\n              })\n            })\n          })\n      })\n\n    httpbin.checkToken(client, scope, (token: any) => {\n      expect(token).to.have.property(\"access_token\")\n      expect(token).to.have.property(\"id_token\")\n      expect(token).to.have.property(\"refresh_token\")\n      expect(token).to.have.property(\"token_type\")\n      expect(token).to.have.property(\"expires_in\")\n      expect(token.scope).to.equal(\"offline openid\")\n      let idToken = JSON.parse(\n        decodeURIComponent(escape(window.atob(token.id_token.split(\".\")[1]))),\n      )\n      expect(idToken).to.have.property(\"amr\")\n      expect(idToken.amr).to.deep.equal([\"password\", \"password\"])\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/passkey/flows.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen } from \"../../../helpers\"\nimport { routes as express } from \"../../../helpers/express\"\nimport { routes as react } from \"../../../helpers/react\"\nimport { testFlowWebhook } from \"../../../helpers/webhook\"\n\nconst signup = (registration: string, app: string, email = gen.email()) => {\n  cy.visit(registration)\n\n  const websiteTrait = `${\n    app === \"express\" ? `form[data-testid=\"passkey-flow\"]` : \"\"\n  } input[name=\"traits.website\"]`\n  const emailTrait = `${\n    app === \"express\" ? `form[data-testid=\"passkey-flow\"]` : \"\"\n  } input[name=\"traits.email\"]`\n\n  cy.get(emailTrait).type(email)\n  cy.get(websiteTrait).type(\"https://www.ory.sh\")\n  cy.get('[name=\"passkey_register_trigger\"]').click()\n\n  cy.wait(1000)\n\n  cy.getSession({\n    expectAal: \"aal1\",\n    expectMethods: [\"passkey\"],\n  }).then((session) => {\n    expect(session.identity.traits.email).to.equal(email)\n    expect(session.identity.traits.website).to.equal(\"https://www.ory.sh\")\n  })\n}\n\ncontext(\"Passkey registration\", () => {\n  before(() => {\n    cy.task(\"resetCRI\", {})\n  })\n  after(() => {\n    cy.task(\"resetCRI\", {})\n  })\n  ;[\n    {\n      login: react.login,\n      registration: express.registration,\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"passkey\",\n    },\n    {\n      login: express.login,\n      registration: express.registration,\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"passkey\",\n    },\n  ].forEach(({ registration, login, profile, app, base, settings }) => {\n    describe(`for app ${app}`, () => {\n      let authenticator: any\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.enable\",\n          opts: {},\n        })\n          .then(() => {\n            cy.task(\"sendCRI\", {\n              query: \"WebAuthn.addVirtualAuthenticator\",\n              opts: {\n                options: {\n                  protocol: \"ctap2\",\n                  transport: \"internal\",\n                  hasResidentKey: true,\n                  hasUserVerification: true,\n                  isUserVerified: true,\n                },\n              },\n            })\n          })\n          .then((result) => {\n            authenticator = result\n            cy.log(\"authenticator ID:\", authenticator)\n          })\n\n        cy.longPrivilegedSessionTime()\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.clearCredentials\",\n          opts: authenticator,\n        })\n      })\n\n      after(() => {\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.removeVirtualAuthenticator\",\n          opts: authenticator,\n        })\n      })\n\n      it(\"should register after validation errors\", () => {\n        cy.visit(registration)\n\n        // the browser will prevent the form from being submitted if the input field is required\n        // we should remove the required attribute to simulate the data not being sent\n        cy.removeAttribute(\n          ['input[name=\"traits.email\"]', 'input[name=\"traits.website\"]'],\n          \"required\",\n        )\n\n        cy.get(`input[name=\"traits.website\"]`).then(($el) => {\n          $el.removeAttr(\"type\")\n        })\n\n        const websiteTrait = `${\n          app === \"express\" ? `form[data-testid=\"passkey-flow\"]` : \"\"\n        } input[name=\"traits.website\"]`\n\n        const emailTrait = `${\n          app === \"express\" ? `form[data-testid=\"passkey-flow\"]` : \"\"\n        } input[name=\"traits.email\"]`\n\n        cy.get(websiteTrait).type(\"b\")\n        cy.get('[name=\"passkey_register_trigger\"]').click()\n\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\"to.exist\")\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\"to.exist\")\n        cy.get(websiteTrait).should(\"have.value\", \"b\")\n\n        const email = gen.email()\n        cy.get(emailTrait).type(email)\n        cy.get('[name=\"passkey_register_trigger\"]').click()\n\n        cy.wait(1000)\n\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\"to.exist\")\n        cy.get(websiteTrait).should(\"have.value\", \"b\")\n        cy.get(emailTrait).should(\"have.value\", email)\n        cy.get(websiteTrait).clear()\n        cy.get(websiteTrait).type(\"https://www.ory.sh\")\n\n        cy.get('[name=\"passkey_register_trigger\"]').click()\n\n        cy.wait(1000)\n\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"passkey\"],\n        }).then((session) => {\n          expect(session.identity.traits.email).to.equal(email)\n          expect(session.identity.traits.website).to.equal(\"https://www.ory.sh\")\n        })\n      })\n\n      it(\"should pass transient_payload to webhook\", () => {\n        testFlowWebhook(\n          (hooks) =>\n            cy.setupHooks(\"registration\", \"after\", \"passkey\", [\n              ...hooks,\n              { hook: \"session\" },\n            ]),\n          () => {\n            signup(registration, app)\n          },\n        )\n      })\n\n      it(\"should be able to login with registered account\", () => {\n        const email = gen.email()\n\n        signup(registration, app, email)\n        cy.logout()\n        cy.visit(login)\n\n        cy.get('[name=\"passkey_login_trigger\"]').click()\n        cy.wait(1000)\n\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"passkey\"],\n        }).then((session) => {\n          expect(session.identity.traits.email).to.equal(email)\n          expect(session.identity.traits.website).to.equal(\"https://www.ory.sh\")\n        })\n      })\n\n      it(\"should not be able to unlink last passkey\", () => {\n        const email = gen.email()\n        signup(registration, app, email)\n        cy.visit(settings)\n        cy.get('[name=\"passkey_remove\"]').should(\"have.attr\", \"disabled\")\n      })\n\n      it(\"should be able to link password and use both methods for sign in\", () => {\n        const email = gen.email()\n        const password = gen.password()\n        signup(registration, app, email)\n        cy.visit(settings)\n        cy.get('[name=\"passkey_remove\"]').should(\"have.attr\", \"disabled\")\n        cy.get('[name=\"password\"]').type(password)\n        cy.get('[value=\"password\"]').click()\n        cy.expectSettingsSaved()\n        cy.get('[name=\"passkey_remove\"]').click()\n        cy.expectSettingsSaved()\n        cy.logout()\n        cy.visit(login)\n\n        cy.get('[name=\"identifier\"]').type(email)\n        cy.get('[name=\"password\"]').type(password)\n        cy.get('[name=\"method\"][value=\"password\"]').click()\n      })\n\n      it(\"should be able to refresh\", () => {\n        const email = gen.email()\n        signup(registration, app, email)\n        cy.visit(login + \"?refresh=true\")\n        cy.get('[name=\"identifier\"][type=\"hidden\"]').should(\"exist\")\n        cy.get('[name=\"identifier\"][type=\"input\"]').should(\"not.exist\")\n        cy.get('[name=\"password\"]').should(\"not.exist\")\n        cy.get('[value=\"password\"]').should(\"not.exist\")\n        cy.get('[name=\"passkey_login_trigger\"]').click()\n        cy.wait(1000)\n\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"passkey\", \"passkey\"],\n        }).then((session) => {\n          expect(session.identity.traits.email).to.equal(email)\n          expect(session.identity.traits.website).to.equal(\"https://www.ory.sh\")\n        })\n      })\n\n      it(\"should not be able to use for MFA\", () => {\n        const email = gen.email()\n        signup(registration, app, email)\n        cy.visit(login + \"?aal=aal2\")\n        cy.get('[value=\"passkey\"]').should(\"not.exist\")\n        cy.get('[name=\"passkey_login_trigger\"]').should(\"not.exist\")\n      })\n\n      it(\"should be able to add method later and try a variety of refresh flows\", () => {\n        const email = gen.email()\n        const password = gen.password()\n        cy.visit(registration)\n\n        const emailTrait = `${\n          app === \"express\" ? `[data-testid=\"registration-flow\"]` : \"\"\n        } [name=\"traits.email\"]`\n        const websiteTrait = `${\n          app === \"express\" ? `[data-testid=\"registration-flow\"]` : \"\"\n        } [name=\"traits.website\"]`\n\n        cy.get(emailTrait).type(email)\n        cy.get('[name=\"password\"]').type(password)\n        cy.get(websiteTrait).type(\"https://www.ory.sh\")\n        cy.get('[value=\"password\"]').click()\n        cy.location(\"pathname\").should(\"not.contain\", \"/registration\")\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"password\"],\n        })\n\n        cy.visit(settings)\n        cy.get('[name=\"passkey_register_trigger\"]').click()\n        cy.expectSettingsSaved()\n\n        cy.visit(login + \"?refresh=true\")\n        cy.get('[name=\"password\"]').should(\"exist\")\n        cy.get('[name=\"passkey_login_trigger\"]').click()\n        cy.wait(1000)\n        cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"password\", \"passkey\", \"passkey\"],\n        })\n\n        cy.visit(login + \"?refresh=true\")\n        cy.get('[name=\"password\"]').type(password)\n        cy.get('[value=\"password\"]').click()\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"password\", \"passkey\", \"passkey\", \"password\"],\n        })\n\n        cy.logout()\n        cy.visit(login)\n\n        cy.get('[name=\"passkey_login_trigger\"]').click()\n        cy.wait(1000)\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"passkey\"],\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/passwordless/flows.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen } from \"../../../helpers\"\nimport { routes as express } from \"../../../helpers/express\"\nimport { routes as react } from \"../../../helpers/react\"\nimport { testFlowWebhook } from \"../../../helpers/webhook\"\n\nconst signup = (registration: string, app: string, email = gen.email()) => {\n  cy.visit(registration)\n\n  const emailTrait = `${\n    app === \"express\" ? '[data-testid=\"passwordless-flow\"]' : \"\"\n  } [name=\"traits.email\"]`\n  const websiteTrait = `${\n    app === \"express\" ? '[data-testid=\"passwordless-flow\"]' : \"\"\n  } [name=\"traits.website\"]`\n\n  cy.get('[name=\"webauthn_register_displayname\"]').type(\"key1\")\n  cy.get(emailTrait).type(email)\n  cy.get(websiteTrait).type(\"https://www.ory.sh\")\n  cy.clickWebAuthButton(\"register\")\n  cy.getSession({\n    expectAal: \"aal1\",\n    expectMethods: [\"webauthn\"],\n  }).then((session) => {\n    expect(session.identity.traits.email).to.equal(email)\n    expect(session.identity.traits.website).to.equal(\"https://www.ory.sh\")\n  })\n}\n\ncontext(\"Passwordless registration\", () => {\n  before(() => {\n    cy.task(\"resetCRI\", {})\n  })\n  after(() => {\n    cy.task(\"resetCRI\", {})\n  })\n  ;[\n    {\n      login: react.login,\n      registration: express.registration,\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"passwordless\",\n    },\n    {\n      login: express.login,\n      registration: express.registration,\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"passwordless\",\n    },\n  ].forEach(({ registration, login, profile, app, base, settings }) => {\n    describe(`for app ${app}`, () => {\n      let authenticator\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n        cy.addVirtualAuthenticator().then((result) => {\n          authenticator = result\n        })\n        cy.longPrivilegedSessionTime()\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n      })\n\n      after(() => {\n        cy.task(\"sendCRI\", {\n          query: \"WebAuthn.removeVirtualAuthenticator\",\n          opts: authenticator,\n        })\n      })\n\n      it(\"should register after validation errors\", () => {\n        cy.visit(registration)\n\n        // the browser will prevent the form from being submitted if the input field is required\n        // we should remove the required attribute to simulate the data not being sent\n        cy.removeAttribute(\n          ['input[name=\"traits.email\"]', 'input[name=\"traits.website\"]'],\n          \"required\",\n        )\n\n        cy.get(`input[name=\"traits.website\"]`).then(($el) => {\n          $el.removeAttr(\"type\")\n        })\n\n        const websiteTrait = `${\n          app === \"express\" ? `[data-testid=\"passwordless-flow\"]` : \"\"\n        } [name=\"traits.website\"]`\n\n        const emailTrait = `${\n          app === \"express\" ? `[data-testid=\"passwordless-flow\"]` : \"\"\n        } [name=\"traits.email\"]`\n\n        cy.get(appPrefix(app) + '[name=\"webauthn_register_displayname\"]').type(\n          \"key1\",\n        )\n        cy.get(websiteTrait).type(\"b\")\n        cy.clickWebAuthButton(\"register\")\n\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\"to.exist\")\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\"to.exist\")\n        cy.get(websiteTrait).should(\"have.value\", \"b\")\n\n        const email = gen.email()\n        cy.get(emailTrait).type(email)\n        cy.clickWebAuthButton(\"register\")\n\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\"to.exist\")\n        cy.get(websiteTrait).should(\"have.value\", \"b\")\n        cy.get(emailTrait).should(\"have.value\", email)\n        cy.get(websiteTrait).clear()\n        cy.get(websiteTrait).type(\"https://www.ory.sh\")\n        cy.clickWebAuthButton(\"register\")\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"webauthn\"],\n        }).then((session) => {\n          expect(session.identity.traits.email).to.equal(email)\n          expect(session.identity.traits.website).to.equal(\"https://www.ory.sh\")\n        })\n      })\n\n      it(\"should pass transient_payload to webhook\", () => {\n        testFlowWebhook(\n          (hooks) =>\n            cy.setupHooks(\"registration\", \"after\", \"webauthn\", [\n              ...hooks,\n              { hook: \"session\" },\n            ]),\n          () => {\n            signup(registration, app)\n          },\n        )\n      })\n\n      // I have no idea why this does not work in an E2E test. It works just fine manually.\n      xit(\"should use webauthn credential as passkey\", () => {\n        const email = gen.email()\n\n        signup(registration, app, email)\n        cy.logout()\n        cy.visit(login)\n\n        cy.get('[name=\"passkey_login_trigger\"]').click()\n        cy.wait(1000)\n\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"passkey\"],\n        }).then((session) => {\n          expect(session.identity.traits.email).to.equal(email)\n          expect(session.identity.traits.website).to.equal(\"https://www.ory.sh\")\n        })\n      })\n\n      it(\"should be able to login with registered account\", () => {\n        const email = gen.email()\n\n        signup(registration, app, email)\n        cy.logout()\n        cy.visit(login)\n\n        const identifierTrait = `${\n          app === \"express\" ? `[data-testid=\"passwordless-flow\"]` : \"\"\n        } [name=\"identifier\"]`\n\n        cy.get(identifierTrait).type(email)\n        cy.get('[value=\"webauthn\"]').click()\n        cy.get('[data-testid=\"ui/message/1010012\"]').should(\"to.exist\")\n        cy.get('[name=\"password\"]').should(\"to.not.exist\")\n        cy.clickWebAuthButton(\"login\")\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"webauthn\"],\n        }).then((session) => {\n          expect(session.identity.traits.email).to.equal(email)\n          expect(session.identity.traits.website).to.equal(\"https://www.ory.sh\")\n        })\n      })\n\n      it(\"should not be able to unlink last security key\", () => {\n        const email = gen.email()\n        signup(registration, app, email)\n        cy.visit(settings)\n        cy.get('[name=\"webauthn_remove\"]').should(\"be.disabled\")\n      })\n\n      it(\"should be able to link password and use both methods for sign in\", () => {\n        const email = gen.email()\n        const password = gen.password()\n        signup(registration, app, email)\n        cy.visit(settings)\n        cy.get('[name=\"webauthn_remove\"]').should(\"be.disabled\")\n        cy.get('[name=\"password\"]').type(password)\n        cy.get('[value=\"password\"]').click()\n        cy.expectSettingsSaved()\n        cy.get('[name=\"webauthn_remove\"]').click()\n        cy.expectSettingsSaved()\n        cy.logout()\n        cy.visit(login)\n\n        const identifierTrait = `${\n          app === \"express\" ? `[data-testid=\"passwordless-flow\"]` : \"\"\n        } [name=\"identifier\"]`\n\n        cy.get(identifierTrait).type(email)\n        cy.get('[value=\"webauthn\"]').click()\n        cy.get('[data-testid=\"ui/message/4000015\"]').should(\"to.exist\")\n        cy.get(identifierTrait).should(\"exist\")\n        cy.get('[name=\"password\"]').should(\"exist\")\n        cy.get('[value=\"password\"]').should(\"exist\")\n      })\n\n      it(\"should be able to refresh\", () => {\n        const email = gen.email()\n        signup(registration, app, email)\n        cy.visit(login + \"?refresh=true\")\n        cy.get('[name=\"identifier\"][type=\"hidden\"]').should(\"exist\")\n        cy.get('[name=\"identifier\"][type=\"input\"]').should(\"not.exist\")\n        cy.get('[name=\"password\"]').should(\"not.exist\")\n        cy.get('[value=\"password\"]').should(\"not.exist\")\n        cy.clickWebAuthButton(\"login\")\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"webauthn\", \"webauthn\"],\n        }).then((session) => {\n          expect(session.identity.traits.email).to.equal(email)\n          expect(session.identity.traits.website).to.equal(\"https://www.ory.sh\")\n        })\n      })\n\n      it(\"should not be able to use for MFA\", () => {\n        const email = gen.email()\n        signup(registration, app, email)\n        cy.visit(login + \"?aal=aal2\")\n        cy.get('[value=\"webauthn\"]').should(\"not.exist\")\n        cy.get('[name=\"webauthn_login_trigger\"]').should(\"not.exist\")\n      })\n\n      it(\"should be able to add method later and try a variety of refresh flows\", () => {\n        const email = gen.email()\n        const password = gen.password()\n        cy.visit(registration)\n\n        const emailTrait = `${\n          app === \"express\" ? `[data-testid=\"registration-flow\"]` : \"\"\n        } [name=\"traits.email\"]`\n        const websiteTrait = `${\n          app === \"express\" ? `[data-testid=\"registration-flow\"]` : \"\"\n        } [name=\"traits.website\"]`\n\n        cy.get(emailTrait).type(email)\n        cy.get('[name=\"password\"]').type(password)\n        cy.get(websiteTrait).type(\"https://www.ory.sh\")\n        cy.get('[value=\"password\"]').click()\n        cy.location(\"pathname\").should(\"not.contain\", \"/registration\")\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"password\"],\n        })\n\n        cy.visit(settings)\n        cy.get('[name=\"webauthn_register_displayname\"]').type(\"key2\")\n        cy.clickWebAuthButton(\"register\")\n        cy.expectSettingsSaved()\n\n        cy.visit(login + \"?refresh=true\")\n        cy.get('[name=\"password\"]').should(\"exist\")\n        cy.clickWebAuthButton(\"login\")\n        cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"password\", \"webauthn\", \"webauthn\"],\n        })\n\n        cy.visit(login + \"?refresh=true\")\n        cy.get('[name=\"password\"]').type(password)\n        cy.get('[value=\"password\"]').click()\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"password\", \"webauthn\", \"webauthn\", \"password\"],\n        })\n\n        cy.logout()\n        cy.visit(login)\n\n        const identifierTrait = `${\n          app === \"express\" ? `[data-testid=\"passwordless-flow\"]` : \"\"\n        } [name=\"identifier\"]`\n\n        cy.get(identifierTrait).type(email)\n        cy.get('[value=\"webauthn\"]').click()\n        cy.clickWebAuthButton(\"login\")\n        cy.getSession({\n          expectAal: \"aal1\",\n          expectMethods: [\"webauthn\"],\n        })\n      })\n\n      it(\"should not be able to use for MFA even when passwordless is false\", () => {\n        const email = gen.email()\n        signup(registration, app, email)\n        cy.updateConfigFile((config) => {\n          config.selfservice.methods.webauthn.config.passwordless = false\n          return config\n        })\n        cy.visit(login + \"?aal=aal2\")\n        cy.get('[value=\"webauthn\"]').should(\"not.exist\")\n        cy.get('[name=\"webauthn_login_trigger\"]').should(\"not.exist\")\n\n        cy.visit(settings)\n        cy.get('[name=\"webauthn_remove\"]').should(\"be.disabled\")\n        cy.get('[name=\"webauthn_register_displayname\"]').type(\"key2\")\n        cy.clickWebAuthButton(\"register\")\n        cy.expectSettingsSaved()\n\n        cy.visit(login + \"?aal=aal2&refresh=true\")\n        cy.clickWebAuthButton(\"login\")\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"webauthn\", \"webauthn\", \"webauthn\"],\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/recovery/code/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, email, extractOTPCode, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Account Recovery Errors\", () => {\n  ;[\n    {\n      recovery: react.recovery,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      recovery: express.recovery,\n      app: \"express\" as \"express\",\n      profile: \"recovery\",\n    },\n  ].forEach(({ recovery, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.deleteMail()\n        cy.useConfig((builder) =>\n          builder\n            .longRecoveryLifespan()\n            .longCodeLifespan()\n            .disableVerification()\n            .enableRecovery()\n            .useRecoveryStrategy(\"code\")\n            .notifyUnknownRecipients(\"recovery\", false),\n        )\n      })\n\n      it(\"should invalidate flow if wrong code is submitted too often\", () => {\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.visit(recovery)\n        cy.get(appPrefix(app) + \"input[name='email']\").type(identity.email)\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n        cy.recoveryEmailWithCode({\n          expect: { email: identity.email, enterCode: false },\n        })\n        for (let i = 0; i < 5; i++) {\n          cy.get(\"input[name='code']\").type((i + \"\").repeat(8)) // Invalid code\n          cy.get(\"button[value='code']\").click()\n          cy.get('[data-testid=\"ui/message/4060006\"]').should(\n            \"have.text\",\n            \"The recovery code is invalid or has already been used. Please try again.\",\n          )\n          cy.noSession()\n        }\n\n        cy.get(\"input[name='code']\").type(\"12312312\") // Invalid code\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/4000001\"]').should(\n          \"have.text\",\n          \"The request was submitted too often. Please request another code.\",\n        )\n        cy.noSession()\n        cy.get(appPrefix(app) + \"input[name='email']\").type(identity.email)\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n        cy.recoveryEmailWithCode({\n          expect: { email: identity.email, enterCode: false },\n        })\n      })\n\n      it(\"shows code expired message if expired code is submitted\", () => {\n        cy.visit(recovery)\n\n        cy.shortCodeLifespan()\n\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.get(appPrefix(app) + \"input[name='email']\").type(identity.email)\n        cy.get(\"button[value='code']\").click()\n        cy.recoveryEmailWithCode({ expect: { email: identity.email } })\n        cy.get(\"button[value='code']\").click()\n\n        cy.get('[data-testid=\"ui/message/4060006\"]').should(\n          \"contain.text\",\n          \"The recovery code is invalid or has already been used. Please try again.\",\n        )\n\n        cy.noSession()\n      })\n\n      it(\"should receive a stub email when recovering a non-existent account\", () => {\n        cy.notifyUnknownRecipients(\"recovery\")\n        cy.visit(recovery)\n\n        const email = gen.email()\n        cy.get(appPrefix(app) + 'input[name=\"email\"]').type(email)\n        cy.get('button[value=\"code\"]').click()\n\n        cy.location(\"pathname\").should(\"eq\", \"/recovery\")\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n        cy.get('input[name=\"code\"]').should(\"be.visible\")\n\n        cy.getMail({\n          subject: \"Account access attempted\",\n          email,\n        }).should((message) => {\n          expect(message.subject).to.equal(\"Account access attempted\")\n          expect(message.fromAddress.trim()).to.equal(\"no-reply@ory.kratos.sh\")\n          expect(message.toAddresses).to.have.length(1)\n          expect(message.toAddresses[0].trim()).to.equal(email)\n\n          const code = extractOTPCode(message.body)\n          expect(code).to.be.null\n        })\n      })\n\n      it(\"should cause form errors\", () => {\n        cy.visit(recovery)\n        cy.removeAttribute([\"input[name='email']\"], \"required\")\n        cy.get('button[value=\"code\"]').click()\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\n          \"contain.text\",\n          \"Property email is missing.\",\n        )\n        cy.get('[name=\"method\"][value=\"code\"]').should(\"exist\")\n      })\n\n      it(\"is unable to recover the account if the code is incorrect\", () => {\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.visit(recovery)\n        cy.get(appPrefix(app) + \"input[name='email']\").type(identity.email)\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n        cy.get(\"input[name='code']\").type(\"01234567\") // Invalid code\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/4060006\"]').should(\n          \"have.text\",\n          \"The recovery code is invalid or has already been used. Please try again.\",\n        )\n        cy.noSession()\n      })\n\n      it(\"should cause non-repeating form errors after submitting empty form twice. see: #2512\", () => {\n        cy.visit(recovery)\n        cy.location(\"pathname\").should(\"eq\", \"/recovery\")\n        cy.removeAttribute([\"input[name='email']\"], \"required\")\n        cy.get('button[value=\"code\"]').click()\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\n          \"contain.text\",\n          \"Property email is missing.\",\n        )\n        cy.get(\"form\")\n          .find('[data-testid=\"ui/message/4000002\"]')\n          .should(\"have.length\", 1)\n        cy.get('[name=\"method\"][value=\"code\"]').should(\"exist\")\n      })\n\n      it(\"remote recovery email template (recovery_code_valid)\", () => {\n        cy.remoteCourierRecoveryCodeTemplates()\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.visit(recovery)\n        cy.get(appPrefix(app) + \"input[name='email']\").type(identity.email)\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n\n        cy.getMail({\n          subject: \"recovery_code_valid REMOTE TEMPLATE SUBJECT\",\n          email: identity.email,\n        }).then((mail) => {\n          expect(mail.body).to.include(\"recovery_code_valid REMOTE TEMPLATE\")\n        })\n      })\n\n      it(\"remote recovery email template (recovery_code_invalid)\", () => {\n        cy.notifyUnknownRecipients(\"recovery\")\n        cy.remoteCourierRecoveryCodeTemplates()\n        cy.visit(recovery)\n        const email = gen.email()\n        cy.get(appPrefix(app) + \"input[name='email']\").type(email)\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n\n        cy.getMail({\n          subject: \"recovery_code_invalid REMOTE TEMPLATE SUBJECT\",\n          email: email,\n        }).then((mail) => {\n          expect(mail.body).to.include(\"recovery_code_invalid REMOTE TEMPLATE\")\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/recovery/code/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, assertRecoveryAddress, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Account Recovery With Code Success\", () => {\n  ;[\n    {\n      recovery: react.recovery,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      recovery: express.recovery,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"recovery\",\n    },\n  ].forEach(({ recovery, profile, base, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let identity\n\n      beforeEach(() => {\n        cy.deleteMail()\n\n        cy.useConfig((builder) =>\n          builder\n            .longRecoveryLifespan()\n            .disableVerification()\n            .enableRecovery()\n            .useRecoveryStrategy(\"code\")\n            .notifyUnknownRecipients(\"recovery\", false),\n        )\n\n        identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n      })\n\n      it(\"should contain the recovery address in the session\", () => {\n        cy.visit(recovery)\n        cy.login({ ...identity, cookieUrl: base })\n        cy.getSession().should(assertRecoveryAddress(identity))\n      })\n\n      it(\"should perform a recovery flow\", () => {\n        cy.visit(recovery)\n        cy.get(appPrefix(app) + \"input[name='email']\").type(identity.email)\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n\n        cy.recoveryEmailWithCode({ expect: { email: identity.email } })\n        cy.get(\"button[value='code']\").click()\n\n        cy.get('[data-testid=\"ui/message/1060001\"]', { timeout: 30000 }).should(\n          \"contain.text\",\n          \"You successfully recovered your account. \",\n        )\n\n        cy.getSession()\n        cy.location(\"pathname\").should(\"eq\", \"/settings\")\n\n        const newPassword = gen.password()\n        cy.get(appPrefix(app) + 'input[name=\"password\"]')\n          .clear()\n          .type(newPassword)\n        cy.get('button[value=\"password\"]').click()\n        cy.expectSettingsSaved()\n        cy.get('input[name=\"password\"]').should(\"be.empty\")\n\n        cy.logout()\n        cy.login({\n          email: identity.email,\n          password: newPassword,\n          cookieUrl: base,\n        })\n      })\n\n      it(\"should recover account with correct code after entering wrong code\", () => {\n        cy.visit(recovery)\n        cy.get(appPrefix(app) + \"input[name='email']\").type(identity.email)\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n        cy.get(\"input[name='code']\").type(\"12312312\") // Invalid code\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/4060006\"]').should(\n          \"have.text\",\n          \"The recovery code is invalid or has already been used. Please try again.\",\n        )\n        cy.noSession()\n        cy.recoveryEmailWithCode({ expect: { email: identity.email } })\n        cy.get(\"button[value='code']\").click()\n\n        cy.get('[data-testid=\"ui/message/1060001\"]', { timeout: 30000 }).should(\n          \"contain.text\",\n          \"You successfully recovered your account. \",\n        )\n        cy.getSession()\n        cy.location(\"pathname\").should(\"eq\", \"/settings\")\n        cy.get('input[name=\"traits.email\"]').should(\n          \"have.value\",\n          identity.email,\n        )\n      })\n\n      it(\"should recover account after resending code\", () => {\n        cy.visit(recovery)\n        cy.get(appPrefix(app) + \"input[name='email']\").type(identity.email)\n        cy.get(\"button[value='code']\").click()\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n\n        cy.recoveryEmailWithCode({\n          expect: { email: identity.email, enterCode: false },\n        })\n\n        cy.get(\"button[name='email']\").click() // resend code\n        cy.noSession()\n\n        cy.recoveryEmailWithCode({\n          expect: { email: identity.email },\n        })\n        cy.get(\"button[value='code']\").click()\n\n        cy.get('[data-testid=\"ui/message/1060001\"]', { timeout: 30000 }).should(\n          \"contain.text\",\n          \"You successfully recovered your account. \",\n        )\n        cy.getSession()\n        cy.location(\"pathname\").should(\"eq\", \"/settings\")\n        cy.get('input[name=\"traits.email\"]').should(\n          \"have.value\",\n          identity.email,\n        )\n      })\n      it(\"should not notify an unknown recipient\", () => {\n        const recipient = gen.email()\n\n        cy.visit(recovery)\n        cy.get('input[name=\"email\"]').type(recipient)\n        cy.get(`[name=\"method\"][value=\"code\"]`).click()\n\n        cy.getCourierMessages().then((messages) => {\n          expect(messages.map((msg) => msg.recipient)).to.not.include(recipient)\n        })\n      })\n    })\n  })\n\n  it(\"should recover, set password and be redirected\", () => {\n    const app = \"express\" as \"express\"\n    cy.deleteMail()\n    cy.useConfigProfile(\"recovery\")\n    cy.proxy(app)\n\n    cy.useConfig((builder) =>\n      builder\n        .longRecoveryLifespan()\n        .longCodeLifespan()\n        .disableVerification()\n        .enableRecovery()\n        .useRecoveryStrategy(\"code\"),\n    )\n\n    const identity = gen.identityWithWebsite()\n    cy.registerApi(identity)\n    cy.visit(express.recovery + \"?return_to=https://www.example.org/\")\n    cy.get(\"input[name='email']\").type(identity.email)\n    cy.get(\"button[value='code']\").click()\n    cy.get('[data-testid=\"ui/message/1060003\"]').should(\n      \"have.text\",\n      \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n    )\n\n    cy.recoveryEmailWithCode({ expect: { email: identity.email } })\n    cy.get(\"button[value='code']\").click()\n\n    cy.getSession()\n    cy.location(\"pathname\").should(\"eq\", \"/settings\")\n\n    cy.get('input[name=\"password\"]').clear().type(gen.password())\n    cy.get('button[value=\"password\"]').click()\n    cy.url().should(\"eq\", \"https://www.example.org/\")\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/recovery/link/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, parseHtml } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Account Recovery Errors\", () => {\n  ;[\n    {\n      recovery: react.recovery,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      recovery: express.recovery,\n      app: \"express\" as \"express\",\n      profile: \"recovery\",\n    },\n  ].forEach(({ recovery, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.deleteMail()\n        cy.clearAllCookies()\n\n        cy.useConfig((builder) =>\n          builder\n            .longRecoveryLifespan()\n            .longLinkLifespan()\n            .disableVerification()\n            .enableRecovery()\n            .useRecoveryStrategy(\"link\")\n            .disableRecoveryStrategy(\"code\")\n            .notifyUnknownRecipients(\"verification\", false),\n        )\n      })\n\n      it(\"responds with a HTML response on link click of an API flow if the link is expired\", () => {\n        cy.visit(recovery)\n\n        cy.shortLinkLifespan()\n\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.recoverApi({ email: identity.email })\n        cy.recoverEmailButExpired({ expect: { email: identity.email } })\n\n        cy.get('[data-testid=\"ui/message/4060005\"]').should(\n          \"contain.text\",\n          \"The recovery flow expired\",\n        )\n\n        cy.noSession()\n      })\n\n      it(\"responds with a HTML response on link click of an API flow if the flow is expired\", () => {\n        cy.visit(recovery)\n\n        cy.updateConfigFile((config) => {\n          config.selfservice.flows.recovery.lifespan = \"1s\"\n          return config\n        })\n\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.recoverApi({ email: identity.email })\n        cy.wait(1000)\n\n        cy.getMail({\n          subject: \"Recover access to your account\",\n          email: identity.email,\n        }).then((message) => {\n          expect(message.subject).to.equal(\"Recover access to your account\")\n          expect(message.toAddresses[0].trim()).to.equal(identity.email)\n\n          const link = parseHtml(message.body).querySelector(\"a\")\n          cy.longRecoveryLifespan()\n          cy.visit(link.href)\n        })\n\n        cy.get('[data-testid=\"ui/message/4060005\"]').should(\n          \"contain.text\",\n          \"The recovery flow expired\",\n        )\n\n        cy.noSession()\n      })\n\n      it(\"should receive a stub email when recovering a non-existent account\", () => {\n        cy.notifyUnknownRecipients(\"recovery\")\n        cy.visit(recovery)\n\n        const email = gen.email()\n        cy.get(appPrefix(app) + 'input[name=\"email\"]').type(email)\n        cy.get('button[value=\"link\"]').click()\n\n        cy.location(\"pathname\").should(\"eq\", \"/recovery\")\n        cy.get('[data-testid=\"ui/message/1060002\"]').should(\n          \"have.text\",\n          \"An email containing a recovery link has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n        cy.get('input[name=\"email\"]').should(\"have.value\", email)\n\n        cy.getMail({\n          subject: \"Account access attempted\",\n          email,\n        }).then((message) => {\n          expect(message.subject).to.equal(\"Account access attempted\")\n          expect(message.fromAddress.trim()).to.equal(\"no-reply@ory.kratos.sh\")\n          expect(message.toAddresses).to.have.length(1)\n          expect(message.toAddresses[0].trim()).to.equal(email)\n\n          const link = parseHtml(message.body).querySelector(\"a\")\n          expect(link).to.be.null\n        })\n      })\n\n      it(\"should cause form errors\", () => {\n        cy.visit(recovery)\n\n        // we need to remove the required attribute of the element since the browser prevents us from submitting the form\n        // this is to simulate the case where the form is submitted with an empty input field.\n        cy.removeAttribute([\"input[name='email']\"], \"required\")\n\n        cy.get('button[value=\"link\"]').click()\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\n          \"contain.text\",\n          \"Property email is missing.\",\n        )\n        cy.get('[name=\"method\"][value=\"link\"]').should(\"exist\")\n      })\n\n      it(\"should cause non-repeating form errors after submitting empty form twice. see: #2512\", () => {\n        cy.visit(recovery)\n        // we need to remove the required attribute of the element since the browser prevents us from submitting the form\n        // this is to simulate the case where the form is submitted with an empty input field.\n        cy.removeAttribute([\"input[name='email']\"], \"required\")\n        cy.get('button[value=\"link\"]').click()\n        cy.location(\"pathname\").should(\"eq\", \"/recovery\")\n\n        // we need to remove the required attribute of the element since the browser prevents us from submitting the form\n        // this is to simulate the case where the form is submitted with an empty input field.\n        cy.removeAttribute([\"input[name='email']\"], \"required\")\n        cy.get('button[value=\"link\"]').click()\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\n          \"contain.text\",\n          \"Property email is missing.\",\n        )\n        cy.get(\"form\")\n          .find('[data-testid=\"ui/message/4000002\"]')\n          .should(\"have.length\", 1)\n        cy.get('[name=\"method\"][value=\"link\"]').should(\"exist\")\n      })\n\n      it(\"is unable to recover the email address if the code is expired\", () => {\n        cy.shortLinkLifespan()\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.recoverApi({ email: identity.email })\n        cy.recoverEmailButExpired({ expect: { email: identity.email } })\n\n        cy.get('[data-testid=\"ui/message/4060005\"]').should(\n          \"contain.text\",\n          \"The recovery flow expired\",\n        )\n\n        cy.noSession()\n      })\n\n      it(\"is unable to recover the account if the code is incorrect\", () => {\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.recoverApi({ email: identity.email })\n\n        cy.getMail({\n          subject: \"Recover access to your account\",\n          email: identity.email,\n        }).then((mail) => {\n          console.log(mail)\n          const link = parseHtml(mail.body).querySelector(\"a\")\n          cy.visit(link.href + \"-not\") // add random stuff to the confirm challenge\n          cy.get('[data-testid=\"ui/message/4060004\"]').should(\n            \"have.text\",\n            \"The recovery token is invalid or has already been used. Please retry the flow.\",\n          )\n          cy.noSession()\n        })\n      })\n\n      it(\"is unable to recover the account using the token twice\", () => {\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.recoverApi({ email: identity.email })\n\n        cy.getMail({\n          subject: \"Recover access to your account\",\n          email: identity.email,\n        }).then((mail) => {\n          const link = parseHtml(mail.body).querySelector(\"a\")\n\n          // Workaround for cypress cy.visit limitation.\n          cy.request(link.href).should((response) => {\n            // add random stuff to the confirm challenge\n            expect(response.status).to.eq(200)\n          })\n\n          cy.clearAllCookies()\n\n          cy.visit(link.href)\n          cy.get('[data-testid=\"ui/message/4060004\"]').should(\n            \"have.text\",\n            \"The recovery token is invalid or has already been used. Please retry the flow.\",\n          )\n          cy.noSession()\n        })\n      })\n\n      it(\"invalid remote recovery email template\", () => {\n        cy.notifyUnknownRecipients(\"recovery\")\n        cy.remoteCourierRecoveryTemplates()\n        const identity = gen.identityWithWebsite()\n        cy.recoverApi({ email: identity.email })\n\n        cy.getMail({\n          subject: \"Account Access Attempted\",\n          email: identity.email,\n        }).then((mail) => {\n          expect(mail.body).to.include(\n            \"this is a remote invalid recovery template\",\n          )\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/recovery/link/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, assertRecoveryAddress, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Account Recovery Success\", () => {\n  ;[\n    {\n      recovery: react.recovery,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      recovery: express.recovery,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"recovery\",\n    },\n  ].forEach(({ recovery, profile, base, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let identity\n\n      beforeEach(() => {\n        cy.deleteMail()\n\n        cy.useConfig((builder) =>\n          builder\n            .longRecoveryLifespan()\n            .longLinkLifespan()\n            .disableVerification()\n            .enableRecovery()\n            .useRecoveryStrategy(\"link\"),\n        )\n\n        identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n      })\n\n      it(\"should contain the recovery address in the session\", () => {\n        cy.visit(recovery)\n        cy.login({ ...identity, cookieUrl: base })\n        cy.getSession().should(assertRecoveryAddress(identity))\n      })\n\n      it(\"should perform a recovery flow\", () => {\n        cy.recoverApi({ email: identity.email })\n\n        cy.recoverEmail({ expect: identity })\n\n        cy.getSession()\n        cy.location(\"pathname\").should(\"eq\", \"/settings\")\n\n        const newPassword = gen.password()\n        cy.get(appPrefix(app) + 'input[name=\"password\"]')\n          .clear()\n          .type(newPassword)\n        cy.get('button[value=\"password\"]').click()\n        cy.expectSettingsSaved()\n        cy.get('input[name=\"password\"]').should(\"be.empty\")\n\n        cy.logout()\n        cy.login({\n          email: identity.email,\n          password: newPassword,\n          cookieUrl: base,\n        })\n      })\n\n      it(\"should not notify an unknown recipient\", () => {\n        const recipient = gen.email()\n\n        cy.visit(recovery)\n        cy.get('input[name=\"email\"]').type(recipient)\n        cy.get(`[name=\"method\"][value=\"link\"]`).click()\n\n        cy.getCourierMessages().then((messages) => {\n          expect(messages.map((msg) => msg.recipient)).to.not.include(recipient)\n        })\n      })\n    })\n  })\n\n  it(\"should recover, set password and be redirected\", () => {\n    const app = \"express\" as \"express\"\n\n    cy.deleteMail()\n    cy.useConfigProfile(\"recovery\")\n    cy.proxy(app)\n\n    cy.deleteMail()\n\n    cy.useConfig((builder) =>\n      builder\n        .longRecoveryLifespan()\n        .longLinkLifespan()\n        .disableVerification()\n        .enableRecovery(),\n    )\n\n    const identity = gen.identityWithWebsite()\n    cy.registerApi(identity)\n\n    cy.recoverApi({\n      email: identity.email,\n      returnTo: \"https://www.example.org/\",\n    })\n\n    cy.recoverEmail({ expect: identity })\n\n    cy.getSession()\n    cy.location(\"pathname\").should(\"eq\", \"/settings\")\n\n    cy.get(appPrefix(app) + 'input[name=\"password\"]')\n      .clear()\n      .type(gen.password())\n    cy.get('button[value=\"password\"]').click()\n    cy.url().should(\"eq\", \"https://www.example.org/\")\n  })\n\n  it(\"should recover even if already logged into another account\", () => {\n    const app = \"express\" as \"express\"\n\n    cy.deleteMail()\n    cy.useConfigProfile(\"recovery\")\n    cy.proxy(app)\n\n    cy.deleteMail()\n\n    cy.useConfig((builder) => builder.disableVerification())\n\n    const identity1 = gen.identityWithWebsite()\n    cy.registerApi(identity1)\n    const identity2 = gen.identityWithWebsite()\n    cy.registerApi(identity2)\n\n    cy.recoverApi({ email: identity2.email })\n\n    // first log in as identity1\n\n    cy.visit(express.login)\n\n    cy.get(appPrefix(app) + 'input[name=\"identifier\"]').type(identity1.email)\n    cy.get('input[name=\"password\"]').type(identity1.password)\n    cy.get('button[value=\"password\"]').click()\n\n    cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n\n    // then recover identity2, while still logged in as identity1\n\n    cy.recoverEmail({ expect: identity2 })\n\n    cy.getSession()\n    cy.location(\"pathname\").should(\"eq\", \"/settings\")\n    cy.get('input[name=\"traits.email\"]').should(\"have.value\", identity2.email)\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/recovery/return-to/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { TOTP } from \"otpauth\"\n\ncontext(\"Recovery with `return_to`\", () => {\n  ;[\n    {\n      recovery: react.recovery,\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"recovery-mfa\",\n    },\n    {\n      recovery: express.recovery,\n      settings: express.settings,\n      app: \"express\" as \"express\",\n      profile: \"recovery-mfa\",\n    },\n  ].forEach(({ app, recovery, settings, profile }) => {\n    describe(\"Recovery with `return_to` query paramter success\", () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let identity: any\n\n      beforeEach(() => {\n        cy.deleteMail()\n\n        cy.useConfig((builder) =>\n          builder\n            .longRecoveryLifespan()\n            .disableVerification()\n            .enableRecovery()\n            .useRecoveryStrategy(\"code\")\n            .notifyUnknownRecipients(\"recovery\", false)\n            .longPrivilegedSessionTime()\n            .requireStrictAal(),\n        )\n        cy.clearAllCookies()\n        identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n      })\n\n      const doRecovery = () => {\n        cy.get(appPrefix(app) + \"input[name='email']\").type(identity.email)\n        cy.get(\"button[value='code']\").click()\n\n        cy.get('[data-testid=\"ui/message/1060003\"]').should(\n          \"have.text\",\n          \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n        )\n\n        cy.recoveryEmailWithCode({ expect: { email: identity.email } })\n        cy.get(\"button[value='code']\").click()\n      }\n\n      it(\"should return to the `return_to` url after successful account recovery and settings update\", () => {\n        cy.visit(recovery + \"?return_to=https://www.example.org/\")\n        doRecovery()\n\n        cy.get('[data-testid=\"ui/message/1060001\"]', { timeout: 30000 }).should(\n          \"contain.text\",\n          \"You successfully recovered your account. \",\n        )\n\n        cy.getSession()\n        cy.location(\"pathname\").should(\"eq\", \"/settings\")\n\n        const newPassword = gen.password()\n        cy.get(appPrefix(app) + 'input[name=\"password\"]')\n          .clear()\n          .type(newPassword)\n        cy.get('button[value=\"password\"]').click()\n\n        cy.location(\"hostname\").should(\"eq\", \"www.example.org\")\n      })\n\n      it(\"should return to the `return_to` url even with mfa enabled after successful account recovery and settings update\", () => {\n        cy.visit(settings)\n        cy.get('input[name=\"identifier\"]').type(identity.email)\n        cy.get('input[name=\"password\"]').type(identity.password)\n        cy.get('button[value=\"password\"]').click()\n        cy.visit(settings)\n\n        // enable mfa for this account\n        let secret: string\n        cy.get('[data-testid=\"node/text/totp_secret_key/text\"]').then(($e) => {\n          secret = $e.text().trim()\n        })\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n        cy.expectSettingsSaved()\n        cy.getSession({\n          expectAal: \"aal2\",\n          expectMethods: [\"password\", \"totp\"],\n        })\n\n        cy.logout()\n        cy.clearAllCookies()\n\n        cy.visit(recovery + \"?return_to=https://www.example.org/\")\n        doRecovery()\n\n        cy.shouldShow2FAScreen()\n        cy.get('input[name=\"totp_code\"]').then(($e) => {\n          cy.wrap($e).type(\n            new TOTP({\n              secret,\n            }).generate(),\n          )\n        })\n        cy.get('*[name=\"method\"][value=\"totp\"]').click()\n\n        const newPassword = gen.password()\n        cy.get(appPrefix(app) + 'input[name=\"password\"]')\n          .clear()\n          .type(newPassword)\n        cy.get('button[value=\"password\"]').click()\n        cy.location(\"hostname\").should(\"eq\", \"www.example.org\")\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/recovery/settings/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, assertRecoveryAddress, gen } from \"../../../../helpers\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { routes as express } from \"../../../../helpers/express\"\n\ncontext(\"Account Recovery Success\", () => {\n  ;[\n    {\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"spa\",\n    },\n    {\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"recovery\",\n    },\n  ].forEach(({ settings, profile, base, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let identity\n\n      beforeEach(() => {\n        cy.deleteMail()\n\n        cy.useConfig((builder) =>\n          builder\n            .longRecoveryLifespan()\n            .longLinkLifespan()\n            .disableVerification()\n            .enableRecovery(),\n        )\n\n        identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.login({ ...identity, cookieUrl: base })\n      })\n\n      it(\"should update the recovery address when updating the email\", () => {\n        cy.visit(settings)\n        const email = gen.email()\n        cy.get(appPrefix(app) + 'input[name=\"traits.email\"]')\n          .clear()\n          .type(email)\n        cy.get('button[value=\"profile\"]').click()\n        cy.expectSettingsSaved()\n        cy.get('input[name=\"traits.email\"]').should(\"contain.value\", email)\n\n        cy.getSession().should(assertRecoveryAddress({ email }))\n      })\n\n      xit(\"should not show an immediate error when a recovery address already exists\", () => {\n        // account enumeration prevention, needs to be implemented.\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/two-steps/registration/code.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Session } from \"@ory/kratos-client\"\nimport { gen, MOBILE_URL } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\nconst Selectors = {\n  mobile: {\n    identifier: \"[data-testid='field/identifier']\",\n    recoveryEmail: \"[data-testid='field/email']\",\n    email: \"[data-testid='traits.email']\",\n    email2: \"[data-testid='traits.email2']\",\n    website: \"[data-testid='traits.website']\",\n    username: \"[data-testid='traits.username']\",\n    code: \"[data-testid='field/code'] input\",\n    recoveryCode: \"[data-testid='code']\",\n    submitCode: \"[data-testid='field/method/code']\",\n    resendCode: \"[data-testid='field/resend/code']\",\n    credentialSelection: \"[data-testid='field/screen/credential-selection']\",\n    submitRecovery: \"[data-testid='field/method/code']\",\n    codeHiddenMethod: \"[data-testid='field/method/code']\",\n  },\n  express: {\n    identifier: \"[data-testid='login-flow-code'] input[name='identifier']\",\n    recoveryEmail: \"input[name=email]\",\n    email: \"[data-testid='node/input/traits.email'] input[name='traits.email']\",\n    email2:\n      \"[data-testid='node/input/traits.email2'] input[name='traits.email2']\",\n    website:\n      \"[data-testid='node/input/traits.website'] [name='traits.website']\",\n    username:\n      \"[data-testid='node/input/traits.username'] input[name='traits.username']\",\n    code: \"input[name='code']\",\n    recoveryCode: \"input[name=code]\",\n    submitRecovery: \"button[name=method][value=code]\",\n    submitCode: \"button[name='method'][value='code']\",\n    resendCode: \"button[name='resend'][value='code']\",\n    codeHiddenMethod: \"input[name='method'][value='code'][type='hidden']\",\n    credentialSelection: \"[name='screen'][value='credential-selection']\",\n  },\n  react: {\n    identifier: \"input[name='identifier']\",\n    recoveryEmail: \"input[name=email]\",\n    email: \"input[name='traits.email']\",\n    email2: \"input[name='traits.email2']\",\n    website: \"[name='traits.website']\",\n    username: \"input[name='traits.username']\",\n    code: \"input[name='code']\",\n    recoveryCode: \"input[name=code]\",\n    submitRecovery: \"button[name=method][value=code]\",\n    submitCode: \"button[name='method'][value='code']\",\n    resendCode: \"button[name='resend'][value='code']\",\n    codeHiddenMethod: \"input[name='method'][value='code'][type='hidden']\",\n    credentialSelection: \"[name='screen'][value='credential-selection']\",\n  },\n}\n\ncontext(\"Registration success with code method\", () => {\n  ;[\n    {\n      route: express.registration,\n      login: express.login,\n      recovery: express.recovery,\n      app: \"express\" as \"express\",\n      profile: \"two-steps\",\n    },\n    {\n      route: react.registration,\n      login: react.login,\n      recovery: react.recovery,\n      app: \"react\" as \"react\",\n      profile: \"two-steps\",\n    },\n    {\n      route: MOBILE_URL + \"/Registration\",\n      login: MOBILE_URL + \"/Login\",\n      recovery: MOBILE_URL + \"/Recovery\",\n      app: \"mobile\" as \"mobile\",\n      profile: \"two-steps\",\n    },\n  ].forEach(({ route, login, recovery, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        if (app !== \"mobile\") {\n          cy.proxy(app)\n        }\n      })\n\n      beforeEach(() => {\n        cy.deleteMail({ atLeast: 0 })\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      it(\"should be able to resend the registration code\", () => {\n        const email = gen.email()\n        const website = \"https://www.example.org/\"\n\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.get(Selectors[app][\"website\"]).type(website)\n\n        cy.submitProfileForm(app)\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        cy.getRegistrationCodeFromEmail(email).then((code) =>\n          cy.wrap(code).as(\"code1\"),\n        )\n\n        // cy.get(Selectors[app][\"email\"]).should(\"have.value\", email)\n        cy.get(Selectors[app][\"codeHiddenMethod\"]).should(\"exist\")\n        cy.get(Selectors[app][\"resendCode\"]).click()\n\n        cy.getRegistrationCodeFromEmail(email).then((code) => {\n          cy.wrap(code).as(\"code2\")\n        })\n\n        cy.get(\"@code1\").then((code1) => {\n          // previous code should not work\n          cy.get(Selectors[app][\"code\"]).clear()\n          cy.get(Selectors[app][\"code\"]).type(code1.toString())\n\n          cy.submitCodeForm(app)\n          cy.get('[data-testid=\"ui/message/4040003\"]').should(\n            \"contain.text\",\n            \"The registration code is invalid or has already been used. Please try again.\",\n          )\n        })\n\n        // Navigate back and forth again\n        cy.get(Selectors[app][\"credentialSelection\"]).click()\n        cy.submitCodeForm(app)\n\n        cy.getRegistrationCodeFromEmail(email).then((code) => {\n          cy.wrap(code).as(\"code2\")\n        })\n\n        cy.get(\"@code2\").then((code2) => {\n          cy.get(Selectors[app][\"code\"]).clear()\n          cy.get(Selectors[app][\"code\"]).type(code2.toString())\n          cy.submitCodeForm(app)\n        })\n\n        if (app === \"express\") {\n          cy.url().should(\"match\", /\\/welcome/)\n        } else {\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n        }\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      it(\"should sign up and be logged in with session hook\", () => {\n        if (app === \"react\") {\n          // This test is flaky on React, so we skip it for now.\n          return\n        }\n\n        const email = gen.email()\n        const website = \"https://www.example.org/\"\n\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.get(Selectors[app][\"website\"]).type(website)\n        cy.submitProfileForm(app)\n\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        cy.getRegistrationCodeFromEmail(email).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.get(Selectors[app][\"submitCode\"]).click()\n        })\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      it(\"should be able to sign up without session hook\", () => {\n        if (app === \"react\") {\n          // This test is flaky on React, so we skip it for now.\n          return\n        }\n\n        cy.setPostCodeRegistrationHooks([])\n        const email = gen.email()\n        const website = \"https://www.example.org/\"\n\n        cy.get(Selectors[app][\"email\"]).type(email)\n        cy.get(Selectors[app][\"website\"]).type(website)\n        cy.submitProfileForm(app)\n\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        cy.getRegistrationCodeFromEmail(email).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.get(Selectors[app][\"submitCode\"]).click()\n        })\n\n        cy.visit(login)\n        cy.get(Selectors[app][\"identifier\"]).type(email)\n        cy.get(Selectors[app][\"submitCode\"]).click()\n\n        cy.getLoginCodeFromEmail(email).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.get(Selectors[app][\"submitCode\"]).click()\n        })\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      // Try keep this test as the last one, as it updates the identity schema.\n      it(\"should be able to use multiple identifiers to signup with and sign in to\", () => {\n        cy.setPostCodeRegistrationHooks([\n          {\n            hook: \"session\",\n          },\n        ])\n\n        // Setup complex schema\n        cy.setIdentitySchema(\n          \"file://test/e2e/profiles/code/identity.complex.traits.schema.json\",\n        )\n\n        cy.visit(route)\n\n        cy.get(Selectors[app][\"username\"]).type(Math.random().toString(36))\n\n        const email = gen.email()\n        cy.get(Selectors[app][\"email\"]).type(email)\n\n        const email2 = gen.email()\n        cy.get(Selectors[app][\"email2\"]).type(email2)\n\n        cy.submitProfileForm(app)\n        cy.submitCodeForm(app)\n        cy.get('[data-testid=\"ui/message/1040005\"]').should(\"be.visible\")\n\n        // intentionally use email 1 to sign up for the account\n        cy.getRegistrationCodeFromEmail(email, { expectedCount: 1 }).then(\n          (code) => {\n            cy.get(Selectors[app][\"code\"]).type(code)\n            cy.get(Selectors[app][\"submitCode\"]).click()\n          },\n        )\n\n        if (app === \"mobile\") {\n          cy.visit(MOBILE_URL + \"/Home\")\n          cy.get('*[data-testid=\"logout\"]').click()\n        } else {\n          cy.logout()\n        }\n\n        // There are verification emails from the registration process in the inbox that we need to deleted\n        // for the assertions below to pass.\n        cy.deleteMail({ atLeast: 1 })\n\n        // Attempt to sign in with email 2 (should fail)\n        cy.visit(login)\n        cy.get(Selectors[app][\"identifier\"]).type(email2)\n\n        cy.get(Selectors[app][\"submitCode\"]).click()\n\n        cy.getLoginCodeFromEmail(email2, {\n          expectedCount: 1,\n        }).then((code) => {\n          cy.get(Selectors[app][\"code\"]).type(code)\n          cy.get(Selectors[app][\"submitCode\"]).click()\n        })\n\n        if (app === \"mobile\") {\n          cy.get('[data-testid=\"session-token\"]').then((token) => {\n            cy.getSession({\n              expectAal: \"aal1\",\n              expectMethods: [\"code\"],\n              token: token.text(),\n            }).then((session) => {\n              cy.wrap(session).as(\"session\")\n            })\n          })\n\n          cy.get('[data-testid=\"session-content\"]').should(\"contain\", email)\n          cy.get('[data-testid=\"session-token\"]').should(\"not.be.empty\")\n        } else {\n          cy.getSession({ expectAal: \"aal1\", expectMethods: [\"code\"] }).then(\n            (session) => {\n              cy.wrap(session).as(\"session\")\n            },\n          )\n        }\n\n        cy.get<Session>(\"@session\").then(({ identity }) => {\n          expect(identity.id).to.not.be.empty\n          expect(identity.verifiable_addresses).to.have.length(2)\n          expect(\n            identity.verifiable_addresses.filter((v) => v.value === email)[0]\n              .status,\n          ).to.equal(\"completed\")\n          expect(\n            identity.verifiable_addresses.filter((v) => v.value === email2)[0]\n              .status,\n          ).to.equal(\"completed\")\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/two-steps/registration/oidc.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { testFlowWebhook } from \"../../../../helpers/webhook\"\n\ncontext(\"Social Sign Up Successes\", () => {\n  ;[\n    {\n      login: react.login,\n      registration: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"two-steps\",\n    },\n    {\n      login: express.login,\n      registration: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"two-steps\",\n    },\n  ].forEach(({ registration, login, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.visit(registration)\n        cy.setIdentitySchema(\n          \"file://test/e2e/profiles/oidc/identity.traits.schema.json\",\n        )\n      })\n\n      const shouldSession = (email) => (session) => {\n        const { identity } = session\n        expect(identity.id).to.not.be.empty\n        expect(identity.traits.website).to.equal(website)\n        expect(identity.traits.email).to.equal(email)\n      }\n\n      it(\"should be able to sign up with incomplete data and finally be signed in\", () => {\n        const email = gen.email()\n\n        cy.registerOidc({\n          app,\n          email,\n          expectSession: false,\n          route: registration,\n        })\n\n        cy.get(\"#registration-password\").should(\"not.exist\")\n        cy.get(appPrefix(app) + '[name=\"traits.email\"]').should(\n          \"have.value\",\n          email,\n        )\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\n          \"contain.text\",\n          \"Property website is missing\",\n        )\n\n        cy.get('[name=\"traits.consent\"][type=\"checkbox\"]')\n          .siblings(\"label\")\n          .click()\n        cy.get('[name=\"traits.newsletter\"][type=\"checkbox\"]')\n          .siblings(\"label\")\n          .click()\n        cy.get('[name=\"traits.website\"]').type(\"http://s\")\n\n        cy.get('[name=\"provider\"]')\n          .should(\"have.length\", 1)\n          .should(\"have.value\", \"hydra\")\n          .should(\"contain.text\", \"Continue\")\n          .click()\n\n        cy.get(\"#registration-password\").should(\"not.exist\")\n        cy.get('[name=\"traits.email\"]').should(\"have.value\", email)\n        cy.get('[name=\"traits.website\"]').should(\"have.value\", \"http://s\")\n        cy.get('[data-testid=\"ui/message/4000003\"]').should(\n          \"contain.text\",\n          \"length must be >= 10\",\n        )\n        cy.get('[name=\"traits.website\"]')\n          .should(\"have.value\", \"http://s\")\n          .clear()\n          .type(website)\n\n        cy.get('[name=\"traits.consent\"]').should(\"be.checked\")\n        cy.get('[name=\"traits.newsletter\"]').should(\"be.checked\")\n\n        cy.triggerOidc(app)\n\n        cy.location(\"pathname\").should((loc) => {\n          expect(loc).to.be.oneOf([\"/welcome\", \"/\", \"/sessions\"])\n        })\n\n        cy.getSession().should((session) => {\n          shouldSession(email)(session)\n          expect(session.identity.traits.consent).to.equal(true)\n        })\n      })\n\n      it(\"should pass transient_payload to webhook\", () => {\n        testFlowWebhook(\n          (hooks) =>\n            cy.setupHooks(\"registration\", \"after\", \"oidc\", [\n              ...hooks,\n              { hook: \"session\" },\n            ]),\n          () => {\n            const email = gen.email()\n            cy.registerOidc({\n              app,\n              email,\n              website,\n              route: registration,\n            })\n            cy.getSession().should(shouldSession(email))\n          },\n        )\n      })\n\n      it(\"should be able to sign up with complete data\", () => {\n        const email = gen.email()\n\n        cy.registerOidc({ app, email, website, route: registration })\n        cy.getSession().should(shouldSession(email))\n      })\n\n      it(\"should be able to convert a sign up flow to a sign in flow\", () => {\n        const email = gen.email()\n\n        cy.registerOidc({ app, email, website, route: registration })\n        cy.logout()\n        cy.noSession()\n        cy.visit(registration)\n        cy.triggerOidc(app)\n\n        cy.location(\"pathname\").should((path) => {\n          expect(path).to.oneOf([\"/\", \"/welcome\", \"/sessions\"])\n        })\n\n        cy.getSession().should(shouldSession(email))\n      })\n\n      it(\"should be able to convert a sign in flow to a sign up flow\", () => {\n        cy.setIdentitySchema(\n          \"file://test/e2e/profiles/oidc/identity-required.traits.schema.json\",\n        )\n\n        const email = gen.email()\n        cy.visit(login)\n        cy.triggerOidc(app)\n\n        cy.get(\"#username\").clear().type(email)\n        cy.get(\"#remember\").click()\n        cy.get(\"#accept\").click()\n        cy.get('[name=\"scope\"]').each(($el) => cy.wrap($el).click())\n        cy.get(\"#remember\").click()\n        cy.get(\"#accept\").click()\n\n        cy.get('[data-testid=\"ui/message/4000002\"]').should(\n          \"contain.text\",\n          \"Property website is missing\",\n        )\n        cy.get('[name=\"traits.website\"]').type(\"http://s\")\n\n        cy.triggerOidc(app)\n\n        cy.get('[data-testid=\"ui/message/4000003\"]').should(\n          \"contain.text\",\n          \"length must be >= 10\",\n        )\n        cy.get('[name=\"traits.requirednested\"]').should(\"not.exist\")\n        cy.get('[name=\"traits.requirednested.a\"]').siblings(\"label\").click()\n        cy.get('[name=\"traits.consent\"]').siblings(\"label\").click()\n        cy.get('[name=\"traits.website\"]')\n          .should(\"have.value\", \"http://s\")\n          .clear()\n          .type(website)\n        cy.triggerOidc(app)\n\n        cy.location(\"pathname\").should(\"not.contain\", \"/registration\")\n\n        cy.getSession().should(shouldSession(email))\n      })\n\n      it(\"should be able to sign up with redirects\", () => {\n        const email = gen.email()\n        cy.registerOidc({\n          app,\n          email,\n          website,\n          route: registration + \"?return_to=https://www.example.org/\",\n        })\n        cy.location(\"href\").should(\"eq\", \"https://www.example.org/\")\n        cy.logout()\n      })\n\n      it(\"should be able to register with upstream parameters\", () => {\n        const email = gen.email()\n        cy.intercept(\"GET\", \"**/oauth2/auth*\").as(\"getHydraRegistration\")\n\n        cy.visit(registration + \"?return_to=https://www.example.org/\")\n\n        cy.addInputElement(\"form\", \"upstream_parameters.login_hint\", email)\n\n        cy.triggerOidc(app)\n\n        // once a request to getHydraRegistration responds, 'cy.wait' will resolve\n        cy.wait(\"@getHydraRegistration\")\n          .its(\"request.url\")\n          .should(\"include\", \"login_hint=\" + encodeURIComponent(email))\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/two-steps/registration/password.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, APP_URL, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Registration success with two-step signup\", () => {\n  ;[\n    {\n      route: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"two-steps\",\n    },\n    {\n      route: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"two-steps\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.deleteMail()\n        cy.clearAllCookies()\n        cy.visit(route)\n        cy.enableVerification()\n        if (app === \"express\") {\n          cy.enableVerificationUIAfterRegistration(\"password\")\n        }\n      })\n\n      it(\"should sign up and be logged in\", () => {\n        const email = gen.email()\n        const password = gen.password()\n        const website = \"https://www.example.org/\"\n\n        // Fill out step one forms\n        cy.get(appPrefix(app) + 'input[name=\"traits\"]').should(\"not.exist\")\n        cy.get('input[name=\"traits.email\"]').type(\"someone@foo\")\n        cy.get('input[name=\"traits.website\"]').type(website)\n        cy.get('[name=\"method\"][value=\"profile\"]').click()\n\n        // navigate back, fill traits again\n        cy.get('[name=\"screen\"][value=\"previous\"]').click()\n        cy.get('input[name=\"traits.email\"]').type(\n          \"{selectall}{backspace}\" + email,\n        )\n        cy.get('[name=\"method\"][value=\"profile\"]').click()\n\n        // Fill out step two forms\n        cy.get('input[name=\"password\"]').type(password)\n        cy.get('[name=\"method\"][value=\"password\"]').click()\n\n        if (app === \"express\") {\n          cy.get('a[href*=\"sessions\"]').click()\n        }\n        cy.get(\"pre\").should(\"contain.text\", email)\n\n        cy.getSession().should((session) => {\n          const { identity } = session\n          expect(identity.id).to.not.be.empty\n          expect(identity.schema_id).to.equal(\"default\")\n          expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)\n          expect(identity.traits.website).to.equal(website)\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      it(\"should handle form errors\", () => {\n        const email = gen.email()\n        const password = gen.password()\n        const websiteTooShort = \"a://b\"\n        const website = \"https://www.example.com\"\n\n        // Fill out step one forms\n        cy.get(appPrefix(app) + 'input[name=\"traits\"]').should(\"not.exist\")\n        cy.get('input[name=\"traits.email\"]').type(email)\n        cy.get('input[name=\"traits.website\"]').type(websiteTooShort)\n        cy.get('[name=\"method\"][value=\"profile\"]').click()\n\n        // Assert form errors\n        cy.get('[data-testid=\"ui/message/4000003\"]').should(\"to.exist\")\n\n        // Enter correct value\n        cy.get('input[name=\"traits.website\"]').type(\n          \"{selectall}{backspace}\" + website,\n        )\n        cy.get('[name=\"method\"][value=\"profile\"]').click()\n\n        // Fill out step two forms\n        cy.get('input[name=\"password\"]').type(password)\n        cy.get('[name=\"method\"][value=\"password\"]').click()\n\n        if (app === \"express\") {\n          cy.get('a[href*=\"sessions\"]').click()\n        }\n        cy.get(\"pre\").should(\"contain.text\", email)\n\n        cy.getSession().should((session) => {\n          const { identity } = session\n          expect(identity.id).to.not.be.empty\n          expect(identity.schema_id).to.equal(\"default\")\n          expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)\n          expect(identity.traits.website).to.equal(website)\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/verification/login/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Account Verification Login Errors\", () => {\n  ;[\n    {\n      login: react.login,\n      app: \"react\" as \"react\",\n      profile: \"verification\",\n    },\n    {\n      login: express.login,\n      app: \"express\" as \"express\",\n      profile: \"verification\",\n    },\n  ].forEach(({ profile, login, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.useConfig((builder) => builder.enableLoginForVerifiedAddressOnly())\n        cy.proxy(app)\n      })\n\n      it(\"is unable to login as long as the email is not verified\", () => {\n        cy.deleteMail()\n\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.visit(login)\n\n        cy.get(appPrefix(app) + '[name=\"identifier\"]').type(identity.email)\n        cy.get('[name=\"password\"]').type(identity.password)\n        cy.get('[value=\"password\"]').click()\n\n        if (app === \"express\") {\n          cy.url().should(\"contain\", \"/verification\")\n        }\n\n        cy.get('[data-testid=\"ui/message/4000010\"]').contains(\n          \"Account not active yet\",\n        )\n\n        cy.noSession()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/verification/login/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, gen } from \"../../../../helpers\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { routes as express } from \"../../../../helpers/express\"\n\ncontext(\"Account Verification Login Success\", () => {\n  ;[\n    {\n      login: react.login,\n      app: \"react\" as \"react\",\n      profile: \"verification\",\n    },\n    {\n      login: express.login,\n      app: \"express\" as \"express\",\n      profile: \"verification\",\n    },\n  ].forEach(({ profile, login, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.useConfig((builder) => builder.enableLoginForVerifiedAddressOnly())\n        cy.proxy(app)\n      })\n\n      it(\"is able to login after successful email verification\", () => {\n        cy.deleteMail()\n\n        const identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.performEmailVerification({ expect: { email: identity.email } })\n\n        cy.visit(login)\n\n        cy.get(appPrefix(app) + 'input[name=\"identifier\"]').type(identity.email)\n        cy.get('input[name=\"password\"]').type(identity.password)\n        cy.get('button[value=\"password\"]').click()\n\n        cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n\n        cy.getSession()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/verification/registration/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n  appPrefix,\n  assertVerifiableAddress,\n  gen,\n  parseHtml,\n  verifyHrefPattern,\n} from \"../../../../helpers\"\n\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Account Verification Registration Errors\", () => {\n  ;[\n    {\n      login: react.login,\n      app: \"react\" as \"react\",\n      profile: \"verification\",\n    },\n    {\n      login: express.login,\n      app: \"express\" as \"express\",\n      profile: \"verification\",\n    },\n  ].forEach(({ profile, login, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      let identity\n      beforeEach(() => {\n        cy.useConfig((builder) =>\n          builder\n            .enableVerification()\n            .disableRecovery()\n            .shortCodeLifespan()\n            .longVerificationLifespan(),\n        )\n        cy.deleteMail()\n\n        identity = gen.identityWithWebsite()\n        cy.registerApi(identity)\n        cy.login(identity)\n      })\n\n      it(\"is unable to verify the email address if the code is no longer valid and resend the code\", () => {\n        cy.verifyEmailButExpired({\n          expect: { email: identity.email },\n        })\n\n        cy.longCodeLifespan()\n\n        cy.get(appPrefix(app) + 'input[name=\"email\"]').should(\"be.empty\")\n        cy.get('input[name=\"email\"]').type(identity.email)\n        cy.get('button[value=\"code\"]').click()\n        cy.contains(\"An email containing a verification\")\n        cy.verifyEmail({\n          expect: { email: identity.email, password: identity.password },\n        })\n      })\n\n      it(\"is unable to verify the email address if the code is incorrect\", () => {\n        cy.getMail({\n          body: \"Verify your account\",\n          email: identity.email,\n        }).then((mail) => {\n          const link = parseHtml(mail.body).querySelector(\"a\")\n\n          expect(verifyHrefPattern.test(link.href)).to.be.true\n\n          cy.visit(link.href + \"-not\") // add random stuff to the confirm challenge\n          cy.getSession().should((session) =>\n            assertVerifiableAddress({\n              isVerified: false,\n              email: identity.email,\n            })(session),\n          )\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/verification/registration/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { assertVerifiableAddress, gen } from \"../../../../helpers\"\n\nimport { routes as react } from \"../../../../helpers/react\"\nimport { routes as express } from \"../../../../helpers/express\"\n\ncontext(\"Account Verification Registration Success\", () => {\n  ;[\n    {\n      registration: react.registration,\n      app: \"react\" as \"react\",\n      profile: \"verification\",\n    },\n    {\n      registration: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"verification\",\n    },\n  ].forEach(({ profile, registration, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.useConfig((builder) => builder.longVerificationLifespan())\n        cy.deleteMail()\n      })\n\n      afterEach(() => {\n        cy.deleteMail()\n      })\n\n      const up = (value) => `up-${value}`\n      const { email, password } = gen.identity()\n\n      it(\"is able to verify the email address after sign up\", () => {\n        const identity = gen.identityWithWebsite()\n        const { email, password } = identity\n        cy.registerApi(identity)\n        cy.login(identity)\n        cy.getSession().should((session) =>\n          assertVerifiableAddress({\n            isVerified: false,\n            email,\n          })(session),\n        )\n\n        cy.verifyEmail({ expect: { email, password } })\n      })\n\n      xit(\"sends the warning email on double sign up\", () => {\n        // FIXME https://github.com/ory/kratos/issues/133\n        cy.clearAllCookies()\n        cy.register({ email, password: up(password) })\n        cy.clearAllCookies()\n        cy.login({ email, password })\n\n        cy.verifyEmail({ expect: { email, password } })\n      })\n\n      it(\"is redirected to after_verification_return_to after verification\", () => {\n        cy.clearAllCookies()\n        const { email, password } = gen.identity()\n        cy.register({\n          email,\n          password,\n          query: {\n            return_to: \"http://localhost:4455/verification_return_to_callback\",\n            after_verification_return_to:\n              \"http://localhost:4455/verification_callback\",\n          },\n        })\n        cy.login({ email, password })\n        cy.verifyEmail({\n          expect: {\n            email,\n            password,\n            redirectTo: \"http://localhost:4455/verification_callback\",\n          },\n        })\n      })\n\n      it(\"is redirected to `return_to` after verification\", () => {\n        cy.clearAllCookies()\n        const { email, password } = gen.identity()\n        cy.register({\n          email,\n          password,\n          query: {\n            return_to: \"http://localhost:4455/verification_return_to_callback\",\n          },\n        })\n        cy.login({ email, password })\n        cy.verifyEmail({\n          expect: {\n            email,\n            password,\n            redirectTo: \"http://localhost:4455/verification_return_to_callback\",\n          },\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/verification/settings/error.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n  appPrefix,\n  assertVerifiableAddress,\n  gen,\n  parseHtml,\n  verifyHrefPattern,\n} from \"../../../../helpers\"\n\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\n\ncontext(\"Account Verification Settings Error\", () => {\n  ;[\n    {\n      settings: react.settings,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"verification\",\n    },\n    {\n      settings: express.settings,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"verification\",\n    },\n  ].forEach(({ profile, settings, app, base }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      describe(\"error flow\", () => {\n        let identity\n        before(() => {\n          cy.deleteMail()\n        })\n\n        beforeEach(() => {\n          cy.useConfig((builder) => builder.longCodeLifespan())\n          identity = gen.identityWithWebsite()\n          cy.clearAllCookies()\n          cy.registerApi(identity)\n          cy.deleteMail({ atLeast: 1 }) // clean up registration email\n\n          cy.login({ ...identity, cookieUrl: base })\n          cy.visit(settings)\n        })\n\n        it(\"is unable to verify the email address if the code is no longer valid\", () => {\n          cy.shortCodeLifespan()\n          cy.visit(settings)\n\n          const email = `not-${identity.email}`\n          cy.get(appPrefix(app) + 'input[name=\"traits.email\"]')\n            .clear()\n            .type(email)\n          cy.get('button[value=\"profile\"]').click()\n\n          cy.verifyEmailButExpired({\n            expect: { email },\n          })\n        })\n\n        it(\"is unable to verify the email address if the code is incorrect\", () => {\n          const email = `not-${identity.email}`\n          cy.get('input[name=\"traits.email\"]').clear().type(email)\n          cy.get('button[value=\"profile\"]').click()\n\n          cy.getMail({\n            body: \"Verify your account\",\n            email,\n          }).then((mail) => {\n            const link = parseHtml(mail.body).querySelector(\"a\")\n\n            expect(verifyHrefPattern.test(link.href)).to.be.true\n\n            cy.visit(link.href + \"-not\") // add random stuff to the confirm challenge\n            cy.log(link.href)\n            cy.getSession().then(\n              assertVerifiableAddress({ isVerified: false, email }),\n            )\n          })\n        })\n\n        xit(\"should not update the traits until the email has been verified and the old email has accepted the change\", () => {\n          // FIXME https://github.com/ory/kratos/issues/292\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/verification/settings/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n  APP_URL,\n  appPrefix,\n  assertVerifiableAddress,\n  gen,\n} from \"../../../../helpers\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { routes as express } from \"../../../../helpers/express\"\n\ncontext(\"Account Verification Settings Success\", () => {\n  ;[\n    {\n      settings: react.settings,\n      app: \"react\" as \"react\",\n      profile: \"verification\",\n    },\n    {\n      settings: express.settings,\n      app: \"express\" as \"express\",\n      profile: \"verification\",\n    },\n  ].forEach(({ profile, settings, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.deleteMail()\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n      let identity\n\n      before(() => {\n        cy.deleteMail()\n      })\n\n      beforeEach(() => {\n        identity = gen.identity()\n        cy.register(identity)\n        cy.deleteMail({ atLeast: 1 }) // clean up registration email\n\n        cy.login(identity)\n        cy.visit(settings)\n      })\n\n      it(\"should update the verify address and request a verification email\", () => {\n        const email = `not-${identity.email}`\n        cy.get(appPrefix(app) + 'input[name=\"traits.email\"]')\n          .clear()\n          .type(email)\n        cy.get('[value=\"profile\"]').click()\n\n        if (app == \"express\") {\n          cy.expectSettingsSaved()\n          cy.get('input[name=\"traits.email\"]').should(\"contain.value\", email)\n          cy.getSession().then(\n            assertVerifiableAddress({ isVerified: false, email }),\n          )\n        }\n        cy.verifyEmail({ expect: { email } })\n      })\n\n      xit(\"should should be able to allow or deny (and revert?) the address change\", () => {\n        // FIXME https://github.com/ory/kratos/issues/292\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/verification/verify/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n  assertVerifiableAddress,\n  gen,\n  parseHtml,\n  verifyHrefPattern,\n} from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { Strategy } from \"../../../../support\"\n\ncontext(\"Account Verification Error\", () => {\n  ;[\n    {\n      verification: react.verification,\n      base: react.base,\n      app: \"react\" as \"react\",\n      profile: \"verification\",\n    },\n    {\n      verification: express.verification,\n      base: express.base,\n      app: \"express\" as \"express\",\n      profile: \"verification\",\n    },\n  ].forEach(({ profile, verification, app, base }) => {\n    for (let s of [\"code\", \"link\"] as Strategy[]) {\n      describe(`for strategy ${s}`, () => {\n        describe(`for app ${app}`, () => {\n          before(() => {\n            cy.deleteMail()\n            cy.useConfigProfile(profile)\n            cy.proxy(app)\n          })\n\n          let identity\n          beforeEach(() => {\n            cy.clearAllCookies()\n\n            cy.useConfig((builder) =>\n              builder\n                .longVerificationLifespan()\n                .longLifespan(s)\n                .useVerificationStrategy(s)\n                .notifyUnknownRecipients(\"verification\", false),\n            )\n\n            identity = gen.identity()\n            cy.registerApi(identity)\n            cy.deleteMail({ atLeast: 1 }) // clean up registration email\n            cy.login(identity)\n            cy.visit(verification)\n          })\n\n          it(\"responds with a HTML response on link click of an API flow if the flow is expired\", () => {\n            cy.updateConfigFile((config) => {\n              config.selfservice.flows.verification.lifespan = \"1s\"\n              return config\n            })\n\n            cy.verificationApi({\n              email: identity.email,\n              strategy: s,\n            })\n\n            cy.wait(1000)\n            cy.shortVerificationLifespan()\n\n            cy.getMail({\n              removeMail: true,\n              body: \"Verify your account\",\n              email: identity.email,\n            }).then((message) => {\n              expect(message.toAddresses[0].trim()).to.equal(identity.email)\n\n              const link = parseHtml(message.body).querySelector(\"a\")\n\n              cy.longVerificationLifespan()\n              cy.visit(link.href)\n              cy.get('[data-testid=\"ui/message/4070005\"]').should(\n                \"contain.text\",\n                \"verification flow expired\",\n              )\n\n              cy.getSession().should((session) => {\n                assertVerifiableAddress({\n                  isVerified: false,\n                  email: identity.email,\n                })(session)\n              })\n            })\n          })\n\n          it(\"responds with a HTML response on link click of an API flow if the code is expired\", () => {\n            cy.shortLifespan(s)\n\n            // Init expired flow\n            cy.verificationApi({\n              email: identity.email,\n              strategy: s,\n            })\n\n            cy.wait(1000)\n\n            cy.verifyEmailButExpired({\n              expect: { email: identity.email },\n              strategy: s,\n            })\n          })\n\n          it(\"is unable to verify the email address if the code is expired\", () => {\n            cy.shortLifespan(s)\n\n            cy.visit(verification)\n            cy.get('input[name=\"email\"]').type(identity.email)\n            cy.get(`button[value=\"${s}\"]`).click()\n\n            cy.contains(\"An email containing a verification\")\n            cy.get(`[name=\"method\"][value=\"${s}\"]`).should(\"exist\")\n            cy.verifyEmailButExpired({\n              expect: { email: identity.email },\n              strategy: s,\n            })\n          })\n\n          it(\"is unable to verify the email address if the code is incorrect\", () => {\n            cy.get('input[name=\"email\"]').type(identity.email)\n            cy.get(`button[value=\"${s}\"]`).click()\n\n            cy.contains(\"An email containing a verification\")\n\n            cy.getMail({\n              email: identity.email,\n              body: \"Verify your account\",\n            }).then((mail) => {\n              const link = parseHtml(mail.body).querySelector(\"a\")\n\n              expect(verifyHrefPattern.test(link.href)).to.be.true\n\n              cy.visit(link.href + \"-not\") // add random stuff to the confirm challenge\n              cy.getSession().then(\n                assertVerifiableAddress({\n                  isVerified: false,\n                  email: identity.email,\n                }),\n              )\n            })\n          })\n\n          it(\"unable to verify non-existent account\", () => {\n            cy.notifyUnknownRecipients(\"verification\")\n            const email = gen.identity().email\n            cy.get('input[name=\"email\"]').type(email)\n            cy.get(`button[value=\"${s}\"]`).click()\n            cy.getMail({\n              subject: \"Someone tried to verify this email address\",\n              email,\n              removeMail: true,\n            }).then((mail) => {\n              expect(mail.toAddresses).includes(email)\n              expect(mail.subject).eq(\n                \"Someone tried to verify this email address\",\n              )\n            })\n          })\n\n          if (s === \"code\") {\n            it(\"is unable to brute force the code\", () => {\n              cy.visit(verification)\n              cy.get('input[name=\"email\"]').type(identity.email)\n              cy.get(`button[value=\"${s}\"]`).click()\n\n              cy.contains(\"An email containing a verification\")\n\n              for (let i = 0; i < 5; i++) {\n                cy.get(\"input[name='code']\").type((i + \"\").repeat(8)) // Invalid code\n                cy.get(\"button[value='code']\").click()\n                cy.get('[data-testid=\"ui/message/4070006\"]').should(\n                  \"have.text\",\n                  \"The verification code is invalid or has already been used. Please try again.\",\n                )\n              }\n\n              cy.get(\"input[name='code']\").type(\"12312312\") // Invalid code\n              cy.get(\"button[value='code']\").click()\n              cy.get('[data-testid=\"ui/message/4000001\"]').should(\n                \"have.text\",\n                \"The request was submitted too often. Please request another code.\",\n              )\n              cy.getSession().then(\n                assertVerifiableAddress({\n                  isVerified: false,\n                  email: identity.email,\n                }),\n              )\n            })\n          }\n        })\n      })\n    }\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/verification/verify/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APP_URL, assertVerifiableAddress, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { routes as react } from \"../../../../helpers/react\"\nimport { Strategy } from \"../../../../support\"\n\ncontext(\"Account Verification Settings Success\", () => {\n  ;[\n    {\n      verification: react.verification,\n      app: \"react\" as \"react\",\n      profile: \"verification\",\n    },\n    {\n      verification: express.verification,\n      app: \"express\" as \"express\",\n      profile: \"verification\",\n    },\n  ].forEach(({ profile, verification, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n      for (let s of [\"code\", \"link\"] as Strategy[]) {\n        describe(`for strategy ${s}`, () => {\n          beforeEach(() => {\n            cy.useConfig((builder) =>\n              builder\n                .useVerificationStrategy(s)\n                .notifyUnknownRecipients(\"verification\", false),\n            )\n          })\n\n          it(\"should request verification and receive an email and verify it\", () => {\n            const identity = gen.identity()\n            cy.register(identity)\n            cy.deleteMail({ atLeast: 1 }) // clean up registration email\n\n            cy.login(identity)\n            cy.visit(verification)\n            cy.get('input[name=\"email\"]').type(identity.email)\n            cy.get(`button[value=\"${s}\"]`).click()\n\n            cy.contains(\"An email containing a verification\")\n\n            cy.get(`[name=\"method\"][value=\"${s}\"]`).should(\"exist\")\n\n            cy.verifyEmail({ expect: { email: identity.email }, strategy: s })\n          })\n\n          it(\"should request verification for an email that does not exist yet\", () => {\n            cy.notifyUnknownRecipients(\"verification\", true)\n            const identity = gen.identity()\n            cy.register(identity)\n            cy.deleteMail({ atLeast: 1 }) // clean up registration email\n\n            cy.login(identity)\n            cy.visit(verification)\n            const email = `not-${identity.email}`\n            cy.get('input[name=\"email\"]').type(email)\n            cy.get(`button[value=\"${s}\"]`).click()\n\n            cy.contains(\"An email containing a verification\")\n\n            cy.getMail({\n              subject: \"Someone tried to verify this email address\",\n              email,\n            }).should((message) => {\n              expect(message.subject.trim()).to.equal(\n                \"Someone tried to verify this email address\",\n              )\n              expect(message.fromAddress.trim()).to.equal(\n                \"no-reply@ory.kratos.sh\",\n              )\n              expect(message.toAddresses).to.have.length(1)\n              expect(message.toAddresses[0].trim()).to.equal(email)\n            })\n\n            cy.getSession().then(\n              assertVerifiableAddress({\n                isVerified: false,\n                email: identity.email,\n              }),\n            )\n          })\n\n          it(\"should not verify email when clicking on link received on different address\", () => {\n            const identity = gen.identity()\n            cy.register(identity)\n            cy.deleteMail({ atLeast: 1 }) // clean up registration email\n\n            cy.login(identity)\n            cy.visit(verification)\n            cy.get('input[name=\"email\"]').type(identity.email)\n            cy.get(`button[value=\"${s}\"]`).click()\n\n            cy.verifyEmail({ expect: { email: identity.email }, strategy: s })\n\n            // identity is verified\n            cy.logout()\n\n            // registered with other email address\n            const identity2 = gen.identity()\n            cy.register(identity2)\n            cy.deleteMail({ atLeast: 1 }) // clean up registration email\n\n            cy.login(identity2)\n\n            cy.visit(APP_URL + \"/verification\")\n\n            // request verification link for identity\n            cy.get('input[name=\"email\"]').type(identity.email)\n            cy.get(`button[value=\"${s}\"]`).click()\n\n            cy.performEmailVerification({\n              expect: { email: identity.email },\n              strategy: s,\n            })\n\n            // expect current session to still not have a verified email address\n            cy.getSession().should(\n              assertVerifiableAddress({\n                email: identity2.email,\n                isVerified: false,\n              }),\n            )\n          })\n\n          it(\"should redirect to return_to after completing verification\", () => {\n            cy.clearAllCookies()\n            // registered with other email address\n            const identity2 = gen.identity()\n            cy.register(identity2)\n            cy.deleteMail({ atLeast: 1 }) // clean up registration email\n\n            cy.login(identity2)\n\n            cy.visit(APP_URL + \"/self-service/verification/browser\", {\n              qs: { return_to: \"http://localhost:4455/verification_callback\" },\n            })\n            // request verification link for identity\n            cy.get('input[name=\"email\"]').type(identity2.email)\n            cy.get(`[name=\"method\"][value=\"${s}\"]`).click()\n            cy.verifyEmail({\n              expect: {\n                email: identity2.email,\n                redirectTo: \"http://localhost:4455/verification_callback\",\n              },\n              strategy: s,\n            })\n          })\n\n          it(\"should not notify an unknown recipient\", () => {\n            const recipient = gen.email()\n\n            cy.visit(APP_URL + \"/self-service/verification/browser\")\n            cy.get('input[name=\"email\"]').type(recipient)\n            cy.get(`[name=\"method\"][value=\"${s}\"]`).click()\n\n            cy.getCourierMessages().then((messages) => {\n              expect(messages.map((msg) => msg.recipient)).to.not.include(\n                recipient,\n              )\n            })\n          })\n        })\n      }\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/webhooks/login/error.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\n\ndescribe(\"Basic email profile with failing login flows with webhooks\", () => {\n  ;[\n    {\n      route: express.login,\n      app: \"express\" as \"express\",\n      profile: \"webhooks\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      it(\"should show fail to sign in when webhooks rejects login\", () => {\n        const email = gen.blockedEmail()\n        const password = gen.password()\n\n        cy.registerApi({ email, password, fields: {} })\n        cy.get('input[name=\"identifier\"]').type(email)\n        cy.get('input[name=\"password\"]').type(password)\n\n        cy.submitPasswordForm()\n        cy.get('*[data-testid=\"ui/message/1234\"]').should(\n          \"contain.text\",\n          \"email could not be validated\",\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/webhooks/login/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APP_URL, appPrefix, gen, website } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { testFlowWebhook } from \"../../../../helpers/webhook\"\n\ndescribe(\"Basic email profile with succeeding login flows with webhooks\", () => {\n  const email = gen.email()\n  const password = gen.password()\n\n  before(() => {\n    cy.registerApi({ email, password, fields: { \"traits.website\": website } })\n  })\n  ;[\n    {\n      route: express.login,\n      app: \"express\" as \"express\",\n      profile: \"webhooks\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.useConfigProfile(profile)\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      it(\"should sign in and be logged in\", () => {\n        cy.get(`${appPrefix(app)}input[name=\"identifier\"]`).type(email)\n        cy.get('input[name=\"password\"]').type(password)\n        cy.submitPasswordForm()\n        cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n\n        cy.getSession().should((session) => {\n          const { identity } = session\n          expect(identity.id).to.not.be.empty\n          expect(identity.schema_id).to.equal(\"default\")\n          expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      it(\"should pass transient_payload to webhook\", () => {\n        testFlowWebhook(\n          (h) => cy.setupHooks(\"login\", \"after\", \"password\", h),\n          () => {\n            cy.get(`${appPrefix(app)}input[name=\"identifier\"]`).type(email)\n            cy.get('input[name=\"password\"]').type(password)\n            cy.submitPasswordForm()\n          },\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/webhooks/registration/errors.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\n\ndescribe(\"Registration failures with email profile with webhooks\", () => {\n  ;[\n    {\n      route: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"webhooks\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.visit(route)\n      })\n\n      const blockedIdentity = gen.blockedEmail()\n      const password = gen.password()\n\n      it(\"should show an error when the webhook is blocking registration\", () => {\n        cy.get('input[name=\"traits.email\"]').type(blockedIdentity)\n        cy.get('input[name=\"password\"]').type(password)\n\n        cy.submitPasswordForm()\n        cy.get('input[name=\"traits.email\"]').should(\n          \"have.value\",\n          blockedIdentity,\n        )\n        cy.get('*[data-testid=\"ui/message/1234\"]').should(\n          \"contain.text\",\n          \"email could not be validated\",\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/integration/profiles/webhooks/registration/success.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { appPrefix, APP_URL, gen } from \"../../../../helpers\"\nimport { routes as express } from \"../../../../helpers/express\"\nimport { testFlowWebhook } from \"../../../../helpers/webhook\"\n\ncontext(\"Registration success with email profile with webhooks\", () => {\n  ;[\n    {\n      route: express.registration,\n      app: \"express\" as \"express\",\n      profile: \"webhooks\",\n    },\n  ].forEach(({ route, profile, app }) => {\n    describe(`for app ${app}`, () => {\n      before(() => {\n        cy.useConfigProfile(profile)\n        cy.proxy(app)\n      })\n\n      beforeEach(() => {\n        cy.clearAllCookies()\n        cy.visit(route)\n      })\n\n      it(\"should sign up and be logged in\", () => {\n        const email = gen.email()\n        const password = gen.password()\n\n        cy.get(appPrefix(app) + 'input[name=\"traits\"]').should(\"not.exist\")\n        cy.get('input[name=\"traits.email\"]').type(email)\n        cy.get('input[name=\"password\"]').type(password)\n\n        cy.submitPasswordForm()\n        if (app === \"express\") {\n          cy.get(\"a[href*='sessions']\").click()\n        }\n        cy.get(\"pre\").should(\"contain.text\", email)\n\n        cy.getSession().should((session) => {\n          const { identity } = session\n          expect(identity.id).to.not.be.empty\n          expect(identity.schema_id).to.equal(\"default\")\n          expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)\n          expect(identity.traits.email).to.equal(email)\n        })\n      })\n\n      it(\"should work without transient_payload\", () => {\n        const WEBHOOK_TARGET = \"https://webhook-target-gsmwn5ab4a-uc.a.run.app\"\n        const documentUrl = (key: string) =>\n          `${WEBHOOK_TARGET}/documents/${key}`\n        const email = gen.email()\n        const password = gen.password()\n        const documentID = gen.password()\n        const jsonnet = \"function(ctx) ctx\"\n        cy.setupHooks(\"registration\", \"after\", \"password\", [\n          {\n            hook: \"web_hook\",\n            config: {\n              body: `base64://${Buffer.from(jsonnet).toString(\"base64\")}`,\n              url: documentUrl(documentID),\n              method: \"PUT\",\n            },\n          },\n        ])\n\n        cy.get('input[name=\"traits.email\"]').type(email)\n        cy.get('input[name=\"password\"]').type(password)\n\n        cy.submitPasswordForm()\n\n        cy.request({\n          url: documentUrl(documentID),\n          method: \"GET\",\n          headers: {\n            Accept: \"application/json\",\n          },\n        }).then(({ body, status }) => {\n          const b = JSON.parse(body)\n          expect(status).to.equal(200)\n          expect(b.identity).is.not.undefined\n          expect(b.flow.transient_payload).is.empty\n        })\n      })\n\n      it(\"should pass transient_payload to webhook\", () => {\n        testFlowWebhook(cy.setPostPasswordRegistrationHooks.bind(cy), () => {\n          const email = gen.email()\n          const password = gen.password()\n\n          cy.get('input[name=\"traits.email\"]').type(email)\n          cy.get('input[name=\"password\"]').type(password)\n\n          cy.submitPasswordForm()\n        })\n      })\n\n      it(\"should sign up and modify the identity\", () => {\n        const email = gen.email()\n        const password = gen.password()\n\n        const updatedEmail = {\n          identity: {\n            traits: { email: \"updated-\" + email },\n            verifiable_addresses: [\n              { via: \"email\", value: \"updated-\" + email, verified: true },\n              { via: \"email\", value: \"this-email-should-be-ignored\" },\n              { via: \"email\", value: \"\" },\n            ],\n            recovery_addresses: [\n              { via: \"email\", value: \"updated-\" + email },\n              { via: \"email\", value: \"this-email-should-be-ignored\" },\n              { via: \"email\", value: \"\" },\n            ],\n            metadata_public: { some: \"public fields\" },\n          },\n        }\n        cy.setPostPasswordRegistrationHooks([\n          { hook: \"session\" },\n          {\n            hook: \"web_hook\",\n            config: {\n              url:\n                \"http://127.0.0.1:4459/webhook/write?response=\" +\n                encodeURIComponent(JSON.stringify(updatedEmail)),\n              method: \"POST\",\n              body: \"file://test/e2e/profiles/webhooks/webhook_body.jsonnet\",\n              response: { parse: true },\n            },\n          },\n        ])\n\n        cy.get(appPrefix(app) + 'input[name=\"traits\"]').should(\"not.exist\")\n        cy.get('input[name=\"traits.email\"]').type(email)\n        cy.get('input[name=\"password\"]').type(password)\n\n        cy.submitPasswordForm()\n\n        cy.getSession().should((session) => {\n          const { identity } = session\n          expect(identity.id).to.not.be.empty\n          expect(identity.schema_id).to.equal(\"default\")\n          expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)\n          expect(identity.traits.email).to.equal(\"updated-\" + email)\n          expect((identity as any).metadata_public.some).to.equal(\n            \"public fields\",\n          )\n          expect(identity.verifiable_addresses[0].verified).to.equal(true)\n          expect(identity.verifiable_addresses[0].verified_at).not.to.be.empty\n          expect(identity.verifiable_addresses[0].via).to.eq(\"email\")\n          expect(identity.verifiable_addresses[0].value).to.eq(\n            \"updated-\" + email,\n          )\n          expect(identity.verifiable_addresses).to.have.length(1)\n\n          expect(identity.recovery_addresses).to.have.length(1)\n          expect(identity.recovery_addresses[0].via).to.eq(\"email\")\n          expect(identity.recovery_addresses[0].value).to.eq(\"updated-\" + email)\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/plugins/index.js",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n/// <reference types=\"cypress\" />\n\nconst got = require(\"got\")\nconst CRI = require(\"chrome-remote-interface\")\nlet criPort = 0,\n  criClient = null\n\n/**\n * @type {Cypress.PluginConfig}\n */\nmodule.exports = (on) => {\n  // `on` is used to hook into various events Cypress emits\n  // `config` is the resolved Cypress config\n}\n\nfunction ensureRdpPort(args) {\n  const existing = args.find(\n    (arg) => arg.slice(0, 23) === \"--remote-debugging-port\",\n  )\n\n  if (existing) {\n    return Number(existing.split(\"=\")[1])\n  }\n\n  const port = 40000 + Math.round(Math.random() * 25000)\n  args.push(`--remote-debugging-port=${port}`)\n  return port\n}\n"
  },
  {
    "path": "test/e2e/cypress/support/commands.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n  APP_URL,\n  assertVerifiableAddress,\n  extractOTPCode,\n  gen,\n  KRATOS_ADMIN,\n  KRATOS_PUBLIC,\n  MAIL_API,\n  MOBILE_URL,\n  parseHtml,\n  pollInterval,\n} from \"../helpers\"\n\nimport dayjs from \"dayjs\"\nimport YAML from \"yamljs\"\nimport { MailMessage, Strategy } from \".\"\nimport { OryKratosConfiguration } from \"../../shared/config\"\nimport { UiNode } from \"@ory/kratos-client\"\nimport { ConfigBuilder } from \"./configHelpers\"\n\nconst configFile = \"kratos.generated.yml\"\n\nconst mergeFields = (form, fields) => {\n  const result = {}\n  form.nodes.forEach(({ attributes, type }) => {\n    if (type === \"input\") {\n      result[attributes.name] = attributes.value\n    }\n  })\n\n  return { ...result, ...fields }\n}\n\nfunction checkConfigVersion(previous, tries = 0) {\n  cy.wait(50)\n  cy.request(\"GET\", KRATOS_ADMIN + \"/health/config\").then(({ body }) => {\n    if (previous !== body) {\n      return\n    } else if (tries > 8) {\n      console.warn(\n        \"Config version did not change after 5 tries, maybe the changes did not have an effect?\",\n      )\n      return\n    }\n    cy.wait(50)\n    checkConfigVersion(previous, tries + 1)\n  })\n}\n\nconst updateConfigFile = (\n  cb: (arg: OryKratosConfiguration) => OryKratosConfiguration,\n) => {\n  cy.request(\"GET\", KRATOS_ADMIN + \"/health/config\").then(({ body }) => {\n    cy.readFile(configFile).then((contents) => {\n      cy.writeFile(configFile, YAML.stringify(cb(YAML.parse(contents))))\n      cy.wait(500)\n    })\n    checkConfigVersion(body)\n  })\n}\n\nCypress.Commands.add(\"useConfigProfile\", (profile: string) => {\n  cy.request(\"GET\", KRATOS_ADMIN + \"/health/config\").then(({ body }) => {\n    console.log(\"Switching config profile to:\", profile)\n    cy.readFile(`kratos.${profile}.yml`).then((contents) =>\n      cy.writeFile(configFile, contents),\n    )\n    checkConfigVersion(body)\n  })\n})\n\nCypress.Commands.add(\"proxy\", (app: string) => {\n  console.log(\"Switching proxy profile to:\", app)\n  cy.writeFile(`proxy.json`, `\"${app}\"`)\n  cy.request(APP_URL + \"/\")\n    .its(\"body\", { log: false })\n    .then((body) => {\n      expect(body.indexOf(`data-testid=\"app-${app}\"`) > -1).to.be.true\n    })\n})\n\nCypress.Commands.add(\"shortPrivilegedSessionTime\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.settings.privileged_session_max_age = \"1ms\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"setIdentitySchema\", (schema: string) => {\n  updateConfigFile((config) => {\n    const id = gen.password()\n    config.identity.default_schema_id = id\n    config.identity.schemas = [\n      ...(config.identity.schemas || []),\n      {\n        id,\n        url: schema,\n      },\n    ]\n    return config\n  })\n})\n\nCypress.Commands.add(\"setDefaultIdentitySchema\", (id: string) => {\n  updateConfigFile((config) => {\n    config.identity.default_schema_id = id\n    return config\n  })\n})\n\nCypress.Commands.add(\"longPrivilegedSessionTime\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.settings.privileged_session_max_age = \"5m\"\n    return config\n  })\n})\nCypress.Commands.add(\"longVerificationLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.verification.lifespan = \"1m\"\n    return config\n  })\n})\nCypress.Commands.add(\"shortVerificationLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.verification.lifespan = \"1ms\"\n    return config\n  })\n})\nCypress.Commands.add(\"sessionRequiresNo2fa\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.session.whoami.required_aal = \"aal1\"\n    return config\n  })\n})\nCypress.Commands.add(\"sessionRequires2fa\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.session.whoami.required_aal = \"highest_available\"\n    return config\n  })\n})\nCypress.Commands.add(\"shortLinkLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.methods.link.config.lifespan = \"1ms\"\n    return config\n  })\n})\nCypress.Commands.add(\"longLinkLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.methods.link.config.lifespan = \"1m\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"shortCodeLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.methods.code.config.lifespan = \"1ms\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"shortLifespan\", (strategy: Strategy) => {\n  updateConfigFile((config) => {\n    config.selfservice.methods[strategy].config.lifespan = \"1ms\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"longLifespan\", (strategy: Strategy) => {\n  updateConfigFile((config) => {\n    config.selfservice.methods[strategy].config.lifespan = \"1m\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"longCodeLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.methods.code.config.lifespan = \"1m\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"shortCodeLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.methods.code.config.lifespan = \"1ms\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"longCodeLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.methods.code.config.lifespan = \"1m\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"longRecoveryLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.recovery.lifespan = \"1m\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"enableLoginForVerifiedAddressOnly\", () => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.login[\"after\"] = {\n      password: { hooks: [{ hook: \"require_verified_address\" }] },\n    }\n    return config\n  })\n})\n\nCypress.Commands.add(\"setupHooks\", (flow, phase, kind, hooks) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows[flow][phase][kind] = { hooks }\n    return config\n  })\n})\n\nCypress.Commands.add(\"setPostPasswordRegistrationHooks\", (hooks) => {\n  cy.setupHooks(\"registration\", \"after\", \"password\", hooks)\n})\n\nCypress.Commands.add(\"setPostCodeRegistrationHooks\", (hooks) => {\n  cy.setupHooks(\"registration\", \"after\", \"code\", hooks)\n})\n\nCypress.Commands.add(\"shortLoginLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.login.lifespan = \"100ms\"\n    return config\n  })\n})\nCypress.Commands.add(\"longLoginLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.login.lifespan = \"1h\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"shortRecoveryLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.recovery.lifespan = \"1ms\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"requireStrictAal\", () => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.settings.required_aal = \"highest_available\"\n    config.session.whoami.required_aal = \"highest_available\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"useLaxAal\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.settings.required_aal = \"aal1\"\n    config.session.whoami.required_aal = \"aal1\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"disableVerification\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.verification.enabled = false\n    return config\n  })\n})\n\nCypress.Commands.add(\"enableVerification\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.verification.enabled = true\n    return config\n  })\n})\n\nCypress.Commands.add(\"enableRecovery\", ({} = {}) => {\n  updateConfigFile((config) => {\n    if (!config.selfservice.flows.recovery) {\n      config.selfservice.flows.recovery = {}\n    }\n    config.selfservice.flows.recovery.enabled = true\n    return config\n  })\n})\n\nCypress.Commands.add(\"useRecoveryStrategy\", (strategy: Strategy) => {\n  updateConfigFile((config) => {\n    if (!config.selfservice.flows.recovery) {\n      config.selfservice.flows.recovery = {}\n    }\n    config.selfservice.flows.recovery.use = strategy\n    if (!config.selfservice.methods[strategy]) {\n      config.selfservice.methods[strategy] = {}\n    }\n    config.selfservice.methods[strategy].enabled = true\n    return config\n  })\n})\n\nCypress.Commands.add(\"disableRecoveryStrategy\", (strategy: Strategy) => {\n  updateConfigFile((config) => {\n    config.selfservice.methods[strategy].enabled = false\n    return config\n  })\n})\n\nCypress.Commands.add(\"disableRecovery\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.recovery.enabled = false\n    return config\n  })\n})\n\nCypress.Commands.add(\"disableRegistration\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.registration.enabled = false\n    return config\n  })\n})\n\nCypress.Commands.add(\"enableRegistration\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.registration.enabled = true\n    return config\n  })\n})\n\nCypress.Commands.add(\"useLaxAal\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.settings.required_aal = \"aal1\"\n    config.session.whoami.required_aal = \"aal1\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"updateConfigFile\", (cb: (arg: any) => any) => {\n  updateConfigFile(cb)\n})\n\nCypress.Commands.add(\n  \"register\",\n  ({\n    email = gen.email(),\n    password = gen.password(),\n    query = {},\n    fields = {},\n  } = {}) => {\n    console.log(\"Creating user account: \", { email, password })\n\n    const cookieDomain = new URL(APP_URL).hostname\n    // Somehow, this line makes the clearCookies call work, without it, the cookies are not always cleared and the test fails randomly. Make it make sense.\n    cy.getCookies({\n      domain: cookieDomain,\n    })\n    cy.clearCookies({\n      domain: cookieDomain,\n    })\n\n    cy.request({\n      url: APP_URL + \"/self-service/registration/browser\",\n      followRedirect: false,\n      headers: {\n        Accept: \"application/json\",\n      },\n      qs: query,\n    })\n      .then(({ body, status }) => {\n        expect(status).to.eq(200)\n        const form = body.ui\n        return cy.request({\n          method: form.method,\n          body: mergeFields(form, {\n            ...fields,\n            \"traits.email\": email,\n            password,\n            method: \"password\",\n          }),\n          url: form.action,\n          followRedirect: false,\n        })\n      })\n      .then(({ body }) => {\n        expect(body.identity.traits.email).to.contain(email)\n      })\n  },\n)\n\nCypress.Commands.add(\n  \"registerWithCode\",\n  ({\n    email = gen.email(),\n    code = undefined,\n    traits = {},\n    query = {},\n    expectedMailCount = 1,\n  } = {}) => {\n    const cookieDomain = new URL(APP_URL).hostname\n    cy.clearCookies({\n      domain: cookieDomain,\n    })\n    cy.getCookies({\n      domain: cookieDomain,\n    }).should(\"be.empty\") // assert cookies are actually empty\n\n    cy.request({\n      url: APP_URL + \"/self-service/registration/browser\",\n      method: \"GET\",\n      followRedirect: false,\n      headers: {\n        Accept: \"application/json\",\n      },\n      qs: query || {},\n    }).then(({ body, status }) => {\n      expect(status).to.eq(200)\n      const form = body.ui\n      return cy\n        .request({\n          headers: {\n            Accept: \"application/json\",\n          },\n          method: form.method,\n          body: mergeFields(form, {\n            method: \"code\",\n            \"traits.email\": email,\n            ...traits,\n            ...(code && { code }),\n          }),\n          url: form.action,\n          followRedirect: false,\n          failOnStatusCode: false,\n        })\n        .then(({ body }) => {\n          if (!code) {\n            expect(\n              body.ui.nodes.find(\n                (f: UiNode) =>\n                  f.group === \"default\" &&\n                  \"name\" in f.attributes &&\n                  f.attributes.name === \"traits.email\",\n              )?.attributes.value,\n            ).to.eq(email)\n\n            return cy\n              .getRegistrationCodeFromEmail(email, {\n                expectedCount: expectedMailCount,\n              })\n              .then((code) => {\n                return cy.request({\n                  headers: {\n                    Accept: \"application/json\",\n                  },\n                  method: form.method,\n                  body: mergeFields(form, {\n                    method: \"code\",\n                    \"traits.email\": email,\n                    code,\n                    ...traits,\n                  }),\n                  url: form.action,\n                  followRedirect: false,\n                })\n              })\n          } else {\n            expect(body.session).to.contain(email)\n          }\n        })\n    })\n  },\n)\n\nCypress.Commands.add(\n  \"registerApi\",\n  ({ email = gen.email(), password = gen.password(), fields = {} } = {}) =>\n    cy\n      .request({\n        url: APP_URL + \"/self-service/registration/api\",\n      })\n      .then(({ body }) => {\n        const form = body.ui\n        return cy.request({\n          method: form.method,\n          body: mergeFields(form, {\n            ...fields,\n            \"traits.email\": email,\n            password,\n            method: \"password\",\n          }),\n          url: form.action,\n        })\n      })\n      .then(({ body }) => {\n        expect(body.identity.traits.email).to.contain(email)\n        return body\n      }),\n)\n\nCypress.Commands.add(\"settingsApi\", ({ fields = {} } = {}) =>\n  cy\n    .request({\n      url: APP_URL + \"/self-service/settings/api\",\n    })\n    .then(({ body }) => {\n      const form = body.ui\n      return cy.request({\n        method: form.method,\n        body: mergeFields(form, {\n          ...fields,\n        }),\n        url: form.action,\n      })\n    })\n    .then(({ body }) => {\n      expect(body.statusCode).to.eq(200)\n    }),\n)\n\nCypress.Commands.add(\"loginApi\", ({ email, password } = {}) =>\n  cy\n    .request({\n      url: APP_URL + \"/self-service/login/api\",\n    })\n    .then(({ body }) => {\n      const form = body.ui\n      return cy.request({\n        method: form.method,\n        body: mergeFields(form, {\n          identifier: email,\n          password,\n          method: \"password\",\n        }),\n        url: form.action,\n      })\n    })\n    .then(({ body }) => {\n      expect(body.session.identity.traits.email).to.contain(email)\n      return body\n    }),\n)\n\nCypress.Commands.add(\"loginApiWithoutCookies\", ({ email, password } = {}) => {\n  cy.task(\"httpRequest\", {\n    url: APP_URL + \"/self-service/login/api\",\n    headers: {\n      Accept: \"application/json\",\n    },\n    responseType: \"json\",\n  }).then((body: any) => {\n    cy.task(\"httpRequest\", {\n      method: body.ui.method,\n      json: mergeFields(body.ui, {\n        identifier: email,\n        password,\n        method: \"password\",\n      }),\n      headers: {\n        Accept: \"application/json\",\n      },\n      responseType: \"json\",\n      url: body.ui.action,\n    }).then((body: any) => {\n      expect(body.session.identity.traits.email).to.contain(email)\n      return body\n    })\n  })\n})\n\nCypress.Commands.add(\"recoverApi\", ({ email, returnTo }) => {\n  let url = APP_URL + \"/self-service/recovery/api\"\n  if (returnTo) {\n    url += \"?return_to=\" + returnTo\n  }\n  cy.request({ url })\n    .then(({ body }) => {\n      const form = body.ui\n      // label should still exist after request, for more detail: #2591\n      expect(form.nodes[1].meta).to.not.be.null\n      expect(form.nodes[1].meta.label).to.not.be.null\n      expect(form.nodes[1].meta.label.text).to.equal(\"Email\")\n\n      return cy.request({\n        method: form.method,\n        body: mergeFields(form, { email, method: \"link\" }),\n        url: form.action,\n      })\n    })\n    .then(({ body }) => {\n      expect(body.state).to.contain(\"sent_email\")\n    })\n})\n\nCypress.Commands.add(\n  \"verificationApi\",\n  ({ email, returnTo, strategy = \"code\" }) => {\n    let url = APP_URL + \"/self-service/verification/api\"\n    if (returnTo) {\n      url += \"?return_to=\" + returnTo\n    }\n    cy.request({ url })\n      .then(({ body }) => {\n        const form = body.ui\n        expect(form.nodes.some((node) => node.meta?.label?.text === \"Email\")).to\n          .be.true\n\n        return cy.request({\n          method: form.method,\n          body: mergeFields(form, { email, method: strategy }),\n          url: form.action,\n          headers: {\n            Accept: \"application/json\", // \"Emulate\" an API client, as kratos responds with a redirect otherwise\n          },\n        })\n      })\n      .then(({ body }) => {\n        expect(body.state).to.contain(\"sent_email\")\n      })\n  },\n)\n\nCypress.Commands.add(\n  \"verificationApiExpired\",\n  ({ email, returnTo, strategy = \"code\" }) => {\n    cy.shortVerificationLifespan()\n    let url = APP_URL + \"/self-service/verification/api\"\n    if (returnTo) {\n      url += \"?return_to=\" + returnTo\n    }\n    cy.request({ url })\n      .then(({ body }) => {\n        const form = body.ui\n        return cy.request({\n          method: form.method,\n          body: mergeFields(form, { email, method: strategy }),\n          url: form.action,\n          failOnStatusCode: false,\n        })\n      })\n      .then((response) => {\n        expect(response.status).to.eq(410)\n        expect(response.body.error.reason).to.eq(\n          \"The verification flow has expired. Redirect the user to the verification flow init endpoint to initialize a new verification flow.\",\n        )\n        expect(response.body.error.details.redirect_to).to.eq(\n          \"http://localhost:4455/self-service/verification/browser\",\n        )\n      })\n  },\n)\n\nCypress.Commands.add(\"verificationBrowser\", ({ email, returnTo }) => {\n  let url = APP_URL + \"/self-service/verification/browser\"\n  if (returnTo) {\n    url += \"?return_to=\" + returnTo\n  }\n  cy.request({ url })\n    .then(({ body }) => {\n      const form = body.ui\n      return cy.request({\n        method: form.method,\n        body: mergeFields(form, { email, method: \"link\" }),\n        url: form.action,\n      })\n    })\n    .then(({ body }) => {\n      expect(body.state).to.contain(\"sent_email\")\n    })\n})\nCypress.Commands.add(\"addVirtualAuthenticator\", () =>\n  cy\n    .task(\"sendCRI\", {\n      query: \"WebAuthn.enable\",\n      opts: {},\n    })\n    .then(() =>\n      cy.task(\"sendCRI\", {\n        query: \"WebAuthn.addVirtualAuthenticator\",\n        opts: {\n          options: {\n            protocol: \"ctap2\",\n            transport: \"usb\",\n            hasResidentKey: true,\n            hasUserVerification: true,\n            isUserVerified: true,\n          },\n        },\n      }),\n    ),\n)\n\nCypress.Commands.add(\n  \"registerOidc\",\n  ({\n    app,\n    email,\n    website,\n    scopes,\n    rememberLogin = true,\n    rememberConsent = true,\n    acceptLogin = true,\n    acceptConsent = true,\n    expectSession = true,\n    route = APP_URL + \"/registration\",\n  }) => {\n    cy.visit(route)\n\n    cy.triggerOidc(app)\n\n    cy.get(\"#username\").type(email)\n    if (rememberLogin) {\n      cy.get(\"#remember\").click()\n    }\n    if (acceptLogin) {\n      cy.get(\"#accept\").click()\n    } else {\n      cy.get(\"#reject\").click()\n    }\n\n    if (scopes) {\n      scopes.forEach((scope) => {\n        cy.get(\"#\" + scope).click()\n      })\n    } else {\n      cy.get('input[name=\"scope\"]').each(($el) => cy.wrap($el).click())\n    }\n\n    if (website) {\n      cy.get(\"#website\").clear().type(website)\n    }\n\n    if (rememberConsent) {\n      cy.get(\"#remember\").click()\n    }\n\n    if (acceptConsent) {\n      cy.get(\"#accept\").click()\n    } else {\n      cy.get(\"#reject\").click()\n    }\n\n    cy.location(\"pathname\").should(\"not.include\", \"consent\")\n\n    if (expectSession) {\n      cy.getSession()\n    } else {\n      cy.noSession()\n    }\n  },\n)\n\nCypress.Commands.add(\"shortRegisterLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.registration.lifespan = \"100ms\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"longRegisterLifespan\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.flows.registration.lifespan = \"1h\"\n    return config\n  })\n})\n\nCypress.Commands.add(\"browserReturnUrlOry\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.selfservice.allowed_return_urls = [\n      \"https://www.ory.sh/\",\n      \"https://www.example.org/\",\n    ]\n    return config\n  })\n})\n\nCypress.Commands.add(\"remoteCourierRecoveryTemplates\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.courier.templates = {\n      recovery: {\n        invalid: {\n          email: {\n            body: {\n              html: \"base64://SGksCgp0aGlzIGlzIGEgcmVtb3RlIGludmFsaWQgcmVjb3ZlcnkgdGVtcGxhdGU=\",\n              plaintext:\n                \"base64://SGksCgp0aGlzIGlzIGEgcmVtb3RlIGludmFsaWQgcmVjb3ZlcnkgdGVtcGxhdGU=\",\n            },\n            subject: \"base64://QWNjb3VudCBBY2Nlc3MgQXR0ZW1wdGVk\",\n          },\n        },\n        valid: {\n          email: {\n            body: {\n              html: \"base64://SGksCgp0aGlzIGlzIGEgcmVtb3RlIHRlbXBsYXRlCnBsZWFzZSByZWNvdmVyIGFjY2VzcyB0byB5b3VyIGFjY291bnQgYnkgY2xpY2tpbmcgdGhlIGZvbGxvd2luZyBsaW5rOgo8YSBocmVmPSJ7eyAuUmVjb3ZlcnlVUkwgfX0iPnt7IC5SZWNvdmVyeVVSTCB9fTwvYT4=\",\n              plaintext:\n                \"base64://SGksCgp0aGlzIGlzIGEgcmVtb3RlIHRlbXBsYXRlCnBsZWFzZSByZWNvdmVyIGFjY2VzcyB0byB5b3VyIGFjY291bnQgYnkgY2xpY2tpbmcgdGhlIGZvbGxvd2luZyBsaW5rOgp7eyAuUmVjb3ZlcnlVUkwgfX0=\",\n            },\n            subject: \"base64://UmVjb3ZlciBhY2Nlc3MgdG8geW91ciBhY2NvdW50\",\n          },\n        },\n      },\n    }\n    return config\n  })\n})\n\nCypress.Commands.add(\"remoteCourierRecoveryCodeTemplates\", ({} = {}) => {\n  updateConfigFile((config) => {\n    config.courier.templates = {\n      recovery_code: {\n        invalid: {\n          email: {\n            body: {\n              html: \"base64://cmVjb3ZlcnlfY29kZV9pbnZhbGlkIFJFTU9URSBURU1QTEFURSBIVE1M\", // only\n              plaintext:\n                \"base64://cmVjb3ZlcnlfY29kZV9pbnZhbGlkIFJFTU9URSBURU1QTEFURSBUWFQ=\",\n            },\n            subject:\n              \"base64://cmVjb3ZlcnlfY29kZV9pbnZhbGlkIFJFTU9URSBURU1QTEFURSBTVUJKRUNU\",\n          },\n        },\n        valid: {\n          email: {\n            body: {\n              html: \"base://cmVjb3ZlcnlfY29kZV92YWxpZCBSRU1PVEUgVEVNUExBVEUgSFRNTA==\",\n              plaintext:\n                \"base64://cmVjb3ZlcnlfY29kZV92YWxpZCBSRU1PVEUgVEVNUExBVEUgVFhU\",\n            },\n            subject:\n              \"base64://cmVjb3ZlcnlfY29kZV92YWxpZCBSRU1PVEUgVEVNUExBVEUgU1VCSkVDVA==\",\n          },\n        },\n      },\n    }\n    return config\n  })\n})\n\nCypress.Commands.add(\"resetCourierTemplates\", (type) => {\n  updateConfigFile((config) => {\n    if (config?.courier?.templates && type in config.courier.templates) {\n      delete config.courier.templates[type]\n    }\n    return config\n  })\n})\n\nCypress.Commands.add(\n  \"loginOidc\",\n  ({ app, expectSession = true, url = APP_URL + \"/login\", preTriggerHook }) => {\n    cy.visit(url)\n    if (preTriggerHook) {\n      preTriggerHook()\n    }\n    cy.triggerOidc(app, \"hydra\")\n    cy.location(\"href\").should(\"not.eq\", \"/consent\")\n    if (expectSession) {\n      // for some reason react flakes here although the login succeeded and there should be a session it fails\n      if (app === \"react\") {\n        cy.wait(500) // adding arbitrary wait here. not sure if there is a better way in this case\n      }\n      cy.getSession()\n    } else {\n      cy.noSession()\n    }\n  },\n)\n\nCypress.Commands.add(\n  \"login\",\n  ({ email, password, expectSession = true, cookieUrl = APP_URL }) => {\n    if (expectSession) {\n      console.log(\"Singing in user: \", { email, password })\n    } else {\n      console.log(\"Attempting user sign in: \", { email, password })\n    }\n\n    const cookieDomain = new URL(cookieUrl).hostname\n    // Somehow, this line makes the clearCookies call work, without it, the cookies are not always cleared and the test fails randomly. Make it make sense.\n    cy.getCookies({\n      domain: cookieDomain,\n    })\n    cy.clearCookies({\n      domain: cookieDomain,\n    })\n\n    cy.request({\n      url: APP_URL + \"/self-service/login/browser\",\n      followRedirect: false,\n      failOnStatusCode: false,\n      headers: {\n        Accept: \"application/json\",\n      },\n    })\n      .then(({ body, status }) => {\n        expect(status).to.eq(200)\n        const form = body.ui\n        return cy.request({\n          method: form.method,\n          body: mergeFields(form, {\n            identifier: email,\n            password,\n            method: \"password\",\n          }),\n          headers: {\n            Accept: \"application/json\",\n          },\n          url: form.action,\n          followRedirect: false,\n          failOnStatusCode: false,\n        })\n      })\n      .then(({ status, body }) => {\n        console.log(\"Login sequence completed: \", {\n          email,\n          password,\n          expectSession,\n        })\n        if (expectSession) {\n          expect(status).to.eq(200, JSON.stringify(body))\n          return cy.getSession()\n        } else {\n          expect(status).to.not.eq(200, body)\n          return cy.noSession()\n        }\n      })\n  },\n)\n\nCypress.Commands.add(\"loginMobile\", ({ email, password }) => {\n  cy.visit(MOBILE_URL + \"/Login\")\n  cy.get('input[data-testid=\"identifier\"]').type(email)\n  cy.get('input[data-testid=\"password\"]').type(password)\n  cy.get(\n    'div[data-testid=\"field/method/password\"] div[data-testid=\"submit-form\"]',\n  ).click()\n})\n\nCypress.Commands.add(\"logout\", () => {\n  cy.getCookies({ domain: \"localhost\" }).then((cookies) => {\n    const c = cookies.find(\n      ({ name }) => name.indexOf(\"ory_kratos_session\") > -1,\n    )\n    if (c) {\n      cy.clearCookie(c.name, { domain: \"localhost\" })\n    }\n  })\n  cy.noSession()\n})\n\nCypress.Commands.add(\n  \"reauthWithOtherAccount\",\n  ({\n    previousUrl,\n    expect: { email, success = true },\n    type: { email: temail, password: tpassword } = {\n      email: undefined,\n      password: undefined,\n    },\n  }) => {\n    cy.location(\"pathname\").should(\"contain\", \"/login\")\n    cy.location().then((loc) => {\n      const uri = new URLSearchParams(loc.search)\n      const flow = uri.get(\"flow\")\n      expect(flow).to.not.be.empty\n      cy.request({\n        url: APP_URL + `/self-service/login/flows?id=${flow}`,\n        followRedirect: false,\n        failOnStatusCode: false,\n        headers: {\n          Accept: \"application/json\",\n        },\n      }).then(({ body, status }) => {\n        expect(status).to.eq(200)\n        const form = body.ui\n        console.log(form.action)\n        return cy\n          .request({\n            method: form.method,\n            body: mergeFields(form, {\n              identifier: temail || email,\n              password: tpassword,\n              method: \"password\",\n            }),\n            headers: {\n              Accept: \"application/json\",\n              ContentType: \"application/json\",\n            },\n            url: form.action,\n            followRedirect: false,\n            failOnStatusCode: false,\n          })\n          .then((res) => {\n            expect(res.status).to.eq(200)\n            cy.visit(previousUrl)\n          })\n      })\n    })\n  },\n)\nCypress.Commands.add(\n  \"reauth\",\n  ({\n    expect: { email, success = true },\n    type: { email: temail, password: tpassword } = {\n      email: undefined,\n      password: undefined,\n    },\n  }) => {\n    cy.location(\"pathname\").should(\"contain\", \"/login\")\n    cy.get('input[name=\"identifier\"]').should(\"have.value\", email)\n    if (temail) {\n      cy.get('input[name=\"identifier\"]').invoke(\"attr\", \"value\", temail)\n    }\n    if (tpassword) {\n      cy.get('input[name=\"password\"]').clear().type(tpassword)\n    }\n    cy.longPrivilegedSessionTime()\n    cy.get('button[value=\"password\"]').click()\n    if (success) {\n      cy.location(\"pathname\").should(\"not.contain\", \"/login\")\n    }\n  },\n)\n\nCypress.Commands.add(\"deleteMail\", ({ atLeast = 0 } = {}) => {\n  let tries = 0\n  let count = 0\n  const req = () =>\n    cy\n      .request(\"DELETE\", `${MAIL_API}/mail`, { pruneCode: \"all\" })\n      .then(({ body }) => {\n        count += parseInt(body)\n        if (count < atLeast && tries < 100) {\n          cy.log(\n            `Expected at least ${atLeast} messages but deleteted only ${count} so far (body: ${body})`,\n          )\n          tries++\n          cy.wait(pollInterval)\n          return req()\n        }\n\n        return Promise.resolve()\n      })\n\n  try {\n    return req()\n  } catch (e) {\n    cy.log(e)\n    // just retry, since retry logic is already implemented in req() and will abort after enough tries\n    return req()\n  }\n})\n\nCypress.Commands.add(\n  \"getSession\",\n  ({ expectAal = \"aal1\", expectMethods = [], token } = {}) => {\n    // Do the request once to ensure we have a session (with retry)\n    cy.request({\n      method: \"GET\",\n      url: `${KRATOS_PUBLIC}/sessions/whoami`,\n      ...(token && {\n        auth: {\n          bearer: token,\n        },\n      }),\n    })\n      .its(\"status\") // adds retry\n      .should(\"eq\", 200)\n\n    // Return the session for further propagation\n    return cy\n      .request({\n        method: \"GET\",\n        url: `${KRATOS_PUBLIC}/sessions/whoami`,\n        ...(token && {\n          auth: {\n            bearer: token,\n          },\n        }),\n      })\n      .then((response) => {\n        expect(response.body.id).to.not.be.empty\n        expect(dayjs().isBefore(dayjs(response.body.expires_at))).to.be.true\n\n        // Add a grace second for MySQL which does not support millisecs.\n        expect(dayjs().isAfter(dayjs(response.body.issued_at).subtract(1, \"s\")))\n          .to.be.true\n        expect(\n          dayjs().isAfter(\n            dayjs(response.body.authenticated_at).subtract(1, \"s\"),\n          ),\n        ).to.be.true\n\n        expect(response.body.identity).to.exist\n\n        expect(response.body.authenticator_assurance_level).to.equal(expectAal)\n        if (expectMethods.length > 0) {\n          expect(response.body.authentication_methods).to.have.lengthOf(\n            expectMethods.length,\n          )\n          expectMethods.forEach((value) => {\n            expect(\n              response.body.authentication_methods.find(\n                ({ method }) => method === value,\n              ),\n            ).to.exist\n          })\n        }\n\n        return response.body\n      })\n  },\n)\n\nCypress.Commands.add(\"noSession\", () =>\n  cy\n    .request({\n      method: \"GET\",\n      url: `${KRATOS_PUBLIC}/sessions/whoami`,\n      failOnStatusCode: false,\n    })\n    .then((request) => {\n      expect(request.status).to.eq(401)\n      return request\n    }),\n)\n\nCypress.Commands.add(\n  \"performEmailVerification\",\n  ({ expect: { email, redirectTo }, strategy = \"code\" }) => {\n    cy.getMail({\n      email,\n      body: \"Verify your account\",\n    }).then((message) => {\n      expect(message.fromAddress.trim()).to.equal(\"no-reply@ory.kratos.sh\")\n      expect(message.toAddresses).to.have.length(1)\n      expect(message.toAddresses[0].trim()).to.equal(email)\n\n      const link = parseHtml(message.body).querySelector(\"a\")\n      expect(link).to.not.be.null\n      expect(link.href).to.contain(APP_URL)\n      const params = new URL(link.href).searchParams\n\n      cy.visit(link.href)\n      if (strategy === \"code\") {\n        const code = params.get(\"code\")\n        expect(code).to.not.be.null\n        cy.get(`button[name=\"method\"][value=\"code\"]`).click()\n      }\n\n      if (redirectTo) {\n        cy.get(`[data-testid=\"node/anchor/continue\"]`)\n          .contains(\"Continue\")\n          .click()\n        cy.url().should(\"be.equal\", redirectTo)\n      }\n    })\n  },\n)\n\nCypress.Commands.add(\n  \"verifyEmail\",\n  ({ expect: { email, password, redirectTo }, strategy }) => {\n    cy.performEmailVerification({\n      expect: { email, redirectTo },\n      strategy,\n    }).then(() => {\n      cy.getSession().should((session) =>\n        assertVerifiableAddress({ email, isVerified: true })(session),\n      )\n    })\n  },\n)\n\n// Uses the verification email but waits so that it expires\nCypress.Commands.add(\"recoverEmailButExpired\", ({ expect: { email } }) => {\n  cy.getMail({\n    removeMail: true,\n    email,\n    subject: \"Recover access to your account\",\n  }).then((message) => {\n    const link = parseHtml(message.body).querySelector(\"a\")\n    expect(link).to.not.be.null\n    expect(link.href).to.contain(APP_URL)\n\n    cy.visit(link.href)\n  })\n})\n\nCypress.Commands.add(\n  \"recoveryEmailWithCode\",\n  ({ expect: { email, enterCode = true } }) => {\n    cy.getMail({\n      removeMail: true,\n      email,\n      body: \"Recover access to your account\",\n    })\n      .then((message) => extractOTPCode(message.body))\n      .then((code) => {\n        expect(code).to.not.be.undefined\n        expect(code.length).to.equal(6)\n        cy.wrap(code).as(\"recoveryCode\")\n        if (enterCode) {\n          cy.get(\"input[name='code']\").type(code)\n        }\n      })\n  },\n)\n\nCypress.Commands.add(\n  \"recoverEmail\",\n  ({ expect: { email }, shouldVisit = true }) =>\n    cy\n      .getMail({\n        removeMail: true,\n        email,\n        subject: \"Recover access to your account\",\n      })\n      .then((message) => {\n        expect(message.fromAddress.trim()).to.equal(\"no-reply@ory.kratos.sh\")\n        expect(message.toAddresses).to.have.length(1)\n        expect(message.toAddresses[0].trim()).to.equal(email)\n\n        const link = parseHtml(message.body).querySelector(\"a\")\n        expect(link).to.not.be.null\n        expect(link.href).to.contain(APP_URL)\n\n        if (shouldVisit) {\n          cy.visit(link.href)\n        }\n      }),\n)\n\n// Uses the verification email but waits so that it expires\nCypress.Commands.add(\n  \"verifyEmailButExpired\",\n  ({ expect: { email }, strategy = \"code\" }) => {\n    cy.getMail({\n      removeMail: true,\n      email,\n      body: \"Verify your account\",\n    }).then((message) => {\n      expect(message.fromAddress.trim()).to.equal(\"no-reply@ory.kratos.sh\")\n      expect(message.toAddresses).to.have.length(1)\n      expect(message.toAddresses[0].trim()).to.equal(email)\n\n      const link = parseHtml(message.body).querySelector(\"a\")\n      cy.getSession().should((session) => {\n        assertVerifiableAddress({\n          isVerified: false,\n          email: email,\n        })(session)\n      })\n\n      cy.visit(link.href)\n      if (strategy === \"code\") {\n        cy.get('button[name=\"method\"][value=\"code\"]').click()\n      }\n      cy.get('[data-testid=\"ui/message/4070005\"]').should(\n        \"contain.text\",\n        \"verification flow expired\",\n      )\n      cy.location(\"pathname\").should(\"include\", \"verification\")\n\n      cy.getSession().should((session) => {\n        assertVerifiableAddress({\n          isVerified: false,\n          email: email,\n        })(session)\n      })\n    })\n  },\n)\n\nCypress.Commands.add(\"useLookupSecrets\", (value: boolean) => {\n  cy.updateConfigFile((config) => {\n    config.selfservice.methods = {\n      ...config.selfservice.methods,\n      lookup_secret: {\n        enabled: value,\n      },\n    }\n    return config\n  })\n})\n\nCypress.Commands.add(\"getLookupSecrets\", () =>\n  cy\n    .get('[data-testid=\"node/text/lookup_secret_codes/text\"] code')\n    .then(($e) => $e.map((_, e) => e.innerText.trim()).toArray()),\n)\nCypress.Commands.add(\"expectSettingsSaved\", () => {\n  cy.get('[data-testid=\"ui/message/1050001\"]').should(\n    \"contain.text\",\n    \"Your changes have been saved\",\n  )\n})\n\nCypress.Commands.add(\n  \"getMail\",\n  ({\n    removeMail = true,\n    expectedCount = 1,\n    email = undefined,\n    subject = undefined,\n    body = undefined,\n  }) => {\n    let tries = 0\n    const req = () =>\n      cy\n        .request(`${MAIL_API}/mail`)\n        .then((response: Cypress.Response<{ mailItems: MailMessage[] }>) => {\n          expect(response.body).to.have.property(\"mailItems\")\n          let count = response.body.mailItems.length\n          if (count === 0 && tries < 100) {\n            tries++\n            cy.wait(pollInterval)\n            return req()\n          }\n\n          let mailItem: MailMessage\n\n          if (!subject && !email) {\n            expect(count).to.equal(expectedCount)\n            mailItem = response.body.mailItems[0]\n          } else {\n            const filters: ((m: MailMessage) => boolean)[] = []\n            if (email) {\n              filters.push((m: MailMessage) => m.toAddresses.includes(email))\n            }\n            if (subject) {\n              filters.push((m: MailMessage) => m.subject.includes(subject))\n            }\n            if (body) {\n              filters.push((m: MailMessage) => m.body.includes(body))\n            }\n            const filtered = response.body.mailItems.filter((m) => {\n              return filters.every((f) => f(m))\n            })\n\n            if (filtered.length === 0) {\n              tries++\n              cy.wait(pollInterval)\n              return req()\n            }\n\n            expect(filtered.length).to.equal(expectedCount)\n            mailItem = filtered[0]\n\n            if (removeMail) {\n              return cy.deleteMail({ atLeast: count }).then(() => {\n                return Promise.resolve(mailItem)\n              })\n            }\n          }\n\n          return Promise.resolve(mailItem)\n        })\n\n    return req()\n  },\n)\n\nCypress.Commands.add(\"submitPasswordForm\", () => {\n  cy.get('[name=\"method\"][value=\"password\"]').click()\n  cy.get('[name=\"method\"][value=\"password\"]:disabled').should(\"not.exist\")\n})\n\nCypress.Commands.add(\"submitProfileForm\", (app?: string) => {\n  if (app === \"mobile\") {\n    cy.get('[data-testid=\"field/method/profile\"]').click()\n    cy.get('[data-testid=\"field/method/profile\"]:disabled').should(\"not.exist\")\n  } else {\n    cy.get('[name=\"method\"][value=\"profile\"]').click()\n    cy.get('[name=\"method\"][value=\"profile\"]:disabled').should(\"not.exist\")\n  }\n})\n\nCypress.Commands.add(\"submitCodeForm\", (app) => {\n  if (app === \"mobile\") {\n    cy.get('[data-testid=\"field/method/code\"]').click()\n    cy.get('[data-testid=\"field/method/code\"]:disabled').should(\"not.exist\")\n  } else {\n    cy.get('button[name=\"method\"][value=\"code\"]').click()\n    cy.get('button[name=\"method\"][value=\"code\"]:disabled').should(\"not.exist\")\n  }\n})\n\nCypress.Commands.add(\"clickWebAuthButton\", (type: string) => {\n  cy.get('*[data-testid=\"node/script/webauthn_script\"]').should(\"exist\")\n  cy.wait(500) // Wait for script to load\n  cy.get('*[name=\"webauthn_' + type + '_trigger\"]').click()\n  cy.wait(500) // Wait webauth to pass\n})\n\nCypress.Commands.add(\"shouldShow2FAScreen\", () => {\n  cy.location().should((loc) => {\n    expect(loc.pathname).to.include(\"/login\")\n  })\n  cy.get(\"h2\")\n    .invoke(\"text\")\n    .should(\"match\", /Second factor authentication|Two-Factor Authentication/)\n  cy.get('[data-testid=\"ui/message/1010004\"]').should(\n    \"contain.text\",\n    \"Please complete the second authentication challenge.\",\n  )\n})\n\nCypress.Commands.add(\n  \"shouldErrorOnDisallowedReturnTo\",\n  (init: string, { app }: { app: \"express\" | \"react\" }) => {\n    cy.visit(init, { failOnStatusCode: false })\n    if (app === \"react\") {\n      cy.location(\"href\").should(\"include\", init.split(\"?\")[0])\n      cy.get(\".Toastify\").should(\n        \"contain.text\",\n        \"The return_to address is not allowed.\",\n      )\n    } else {\n      cy.location(\"pathname\").should(\"contain\", \"error\")\n      cy.get(\"div\").should(\n        \"contain.text\",\n        'Requested return_to URL \"https://not-allowed\" is not allowed.',\n      )\n    }\n  },\n)\n\nCypress.Commands.add(\n  \"shouldHaveCsrfError\",\n  ({ app }: { app: \"express\" | \"react\" }) => {\n    let initial: string\n    let pathname: string\n    cy.location().should((location) => {\n      initial = location.search\n      pathname = location.pathname\n    })\n\n    cy.getCookies().then((cookies) => {\n      const csrf = cookies.find(({ name }) => name.indexOf(\"csrf\") > -1)\n      expect(csrf).to.not.be.undefined\n      cy.clearCookie(csrf.name)\n    })\n    cy.submitPasswordForm()\n\n    // We end up at a new flow\n    if (app === \"express\") {\n      cy.location().should((location) => {\n        expect(initial).to.not.be.empty\n        expect(location.search).to.not.eq(initial)\n      })\n\n      cy.location(\"pathname\").should(\"include\", \"/error\")\n      cy.get(`div`).should(\"contain.text\", \"CSRF\")\n    } else {\n      cy.location(\"pathname\").should((got) => {\n        expect(got).to.eql(pathname)\n      })\n      cy.get(\".Toastify\").should(\n        \"contain.text\",\n        \"A security violation was detected, please fill out the form again.\",\n      )\n    }\n  },\n)\n\nCypress.Commands.add(\n  \"triggerOidc\",\n  (app: \"react\" | \"express\", provider: string = \"hydra\") => {\n    let initial, didHaveSearch\n    cy.location().then((loc) => {\n      didHaveSearch = loc.search.length > 0\n      initial = loc.pathname + loc.search\n    })\n    cy.get('[name=\"provider\"][value=\"' + provider + '\"]').click()\n    cy.location().should((loc) => {\n      if (app === \"express\" || didHaveSearch) {\n        return\n      }\n      expect(loc.pathname + loc.search).not.to.eql(initial)\n    })\n  },\n)\n\nCypress.Commands.add(\n  \"removeAttribute\",\n  (selectors: string[], attribute: string) => {\n    selectors.forEach((selector) => {\n      cy.get(selector).then(($el) => {\n        $el.removeAttr(attribute)\n      })\n    })\n  },\n)\n\nCypress.Commands.add(\n  \"addInputElement\",\n  (parent: string, attribute: string, value: string) => {\n    cy.get(parent).then(($el) => {\n      $el.append(`<input type=\"hidden\" name=\"${attribute}\" value=\"${value}\" />`)\n    })\n  },\n)\n\nCypress.Commands.add(\n  \"notifyUnknownRecipients\",\n  (flow: \"recovery\" | \"verification\", value: boolean = true) => {\n    cy.updateConfigFile((config) => {\n      config.selfservice.flows[flow].notify_unknown_recipients = value\n      return config\n    })\n  },\n)\n\nCypress.Commands.add(\"getCourierMessages\", () => {\n  return cy.request(KRATOS_ADMIN + \"/courier/messages\").then((res) => {\n    return res.body\n  })\n})\n\nCypress.Commands.add(\n  \"enableVerificationUIAfterRegistration\",\n  (strategy: \"password\" | \"oidc\" | \"webauthn\") => {\n    cy.updateConfigFile((config) => {\n      if (!config.selfservice.flows.registration.after[strategy]) {\n        config.selfservice.flows.registration.after = {\n          [strategy]: { hooks: [] },\n        }\n      }\n\n      const hooks =\n        config.selfservice.flows.registration.after[strategy].hooks || []\n      config.selfservice.flows.registration.after[strategy].hooks = [\n        ...hooks.filter((h) => h.hook !== \"show_verification_ui\"),\n        { hook: \"show_verification_ui\" },\n      ]\n      return config\n    })\n  },\n)\n\nCypress.Commands.add(\"getVerificationCodeFromEmail\", (email) => {\n  return cy\n    .getMail({\n      removeMail: true,\n      email,\n      body: \"Verify your account\",\n    })\n    .then((message) => {\n      expect(message.toAddresses[0].trim()).to.equal(email)\n      const code = extractOTPCode(message.body)\n      expect(code).to.not.be.undefined\n      expect(code.length).to.equal(6)\n      return code\n    })\n})\n\nCypress.Commands.add(\"getRegistrationCodeFromEmail\", (email, opts) => {\n  return cy\n    .getMail({\n      removeMail: true,\n      email,\n      body: \"Complete your account registration with the following code\",\n      ...opts,\n    })\n    .then((message) => {\n      expect(message.toAddresses[0].trim()).to.equal(email)\n    })\n    .then((message) => {\n      const code = extractOTPCode(message.body)\n      expect(code).to.not.be.undefined\n      expect(code.length).to.equal(6)\n      return code\n    })\n})\n\nCypress.Commands.add(\"getLoginCodeFromEmail\", (email, opts) => {\n  return cy\n    .getMail({\n      removeMail: true,\n      email,\n      body: \"Login to your account with the following code\",\n      ...opts,\n    })\n    .then((message) => {\n      expect(message.toAddresses[0].trim()).to.equal(email)\n    })\n    .then((message) => {\n      const code = extractOTPCode(message.body)\n      expect(code).to.not.be.undefined\n      expect(code.length).to.equal(6)\n      return code\n    })\n})\n\nCypress.Commands.add(\"useConfig\", (cb) => {\n  cy.updateConfigFile((config) => {\n    const builder = cb(new ConfigBuilder(config))\n    return builder.build()\n  })\n})\n"
  },
  {
    "path": "test/e2e/cypress/support/configHelpers.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { OryKratosConfiguration } from \"../../shared/config\"\n\nexport class ConfigBuilder {\n  constructor(readonly config: OryKratosConfiguration) {}\n\n  public build() {\n    return this.config\n  }\n\n  public longRecoveryLifespan() {\n    this.config.selfservice.flows.recovery.lifespan = \"1h\"\n    return this\n  }\n\n  public longLinkLifespan() {\n    this.config.selfservice.methods.link.config.lifespan = \"1m\"\n    return this\n  }\n\n  public disableVerification() {\n    this.config.selfservice.flows.verification.enabled = false\n    return this\n  }\n\n  public disableCodeMfa() {\n    this.config.selfservice.methods.code.mfa_enabled = false\n    return this\n  }\n\n  public enableRecovery() {\n    if (!this.config.selfservice.flows.recovery) {\n      this.config.selfservice.flows.recovery = {}\n    }\n    this.config.selfservice.flows.recovery.enabled = true\n    return this\n  }\n\n  public disableRecovery() {\n    this.config.selfservice.flows.recovery.enabled = false\n    return this\n  }\n\n  public enableVerification() {\n    this.config.selfservice.flows.verification.enabled = true\n    return this\n  }\n\n  public useRecoveryStrategy(strategy: \"link\" | \"code\") {\n    if (!this.config.selfservice.flows.recovery) {\n      this.config.selfservice.flows.recovery = {}\n    }\n    this.config.selfservice.flows.recovery.use = strategy\n    if (!this.config.selfservice.methods[strategy]) {\n      this.config.selfservice.methods[strategy] = {}\n    }\n    this.config.selfservice.methods[strategy].enabled = true\n    return this\n  }\n\n  public disableRecoveryStrategy(strategy: \"link\" | \"code\") {\n    this.config.selfservice.methods[strategy].enabled = false\n    return this\n  }\n\n  public notifyUnknownRecipients(\n    flow: \"recovery\" | \"verification\",\n    value: boolean,\n  ) {\n    this.config.selfservice.flows[flow].notify_unknown_recipients = value\n    return this\n  }\n\n  public longCodeLifespan() {\n    this.config.selfservice.methods.code.config.lifespan = \"1m\"\n    return this\n  }\n\n  public shortCodeLifespan() {\n    this.config.selfservice.methods.code.config.lifespan = \"1ms\"\n    return this\n  }\n\n  public longPrivilegedSessionTime() {\n    this.config.selfservice.flows.settings.privileged_session_max_age = \"5m\"\n    return this\n  }\n\n  public requireStrictAal() {\n    this.config.selfservice.flows.settings.required_aal = \"highest_available\"\n    this.config.session.whoami.required_aal = \"highest_available\"\n    return this\n  }\n\n  public enableLoginForVerifiedAddressOnly() {\n    this.config.selfservice.flows.login[\"after\"] = {\n      password: { hooks: [{ hook: \"require_verified_address\" }] },\n    }\n    return this\n  }\n\n  public longVerificationLifespan() {\n    this.config.selfservice.flows.verification.lifespan = \"1m\"\n    return this\n  }\n\n  public longLifespan(strategy: \"link\" | \"code\") {\n    this.config.selfservice.methods[strategy].config.lifespan = \"1m\"\n    return this\n  }\n\n  public useVerificationStrategy(strategy: \"link\" | \"code\") {\n    this.config.selfservice.flows.verification.use = strategy\n    if (!this.config.selfservice.methods[strategy]) {\n      this.config.selfservice.methods[strategy] = {}\n    }\n    this.config.selfservice.methods[strategy].enabled = true\n    return this\n  }\n\n  public resetCourierTemplates(\n    type: \"recovery\" | \"verification\" | \"recovery_code\" | \"verification_code\",\n  ) {\n    if (\n      this.config.courier?.templates &&\n      type in this.config.courier.templates\n    ) {\n      delete this.config.courier.templates[type]\n    }\n    return this\n  }\n\n  public useLaxSessionAal() {\n    this.config.session.whoami.required_aal = \"aal1\"\n    return this\n  }\n\n  public useLaxSettingsFlowAal() {\n    this.config.selfservice.flows.settings.required_aal = \"aal1\"\n    return this\n  }\n\n  public useLaxAal() {\n    return this.useLaxSessionAal().useLaxSettingsFlowAal()\n  }\n\n  public useHighestSessionAal() {\n    this.config.session.whoami.required_aal = \"highest_available\"\n    return this\n  }\n\n  public useHighestSettingsFlowAal() {\n    this.config.selfservice.flows.settings.required_aal = \"highest_available\"\n    return this\n  }\n\n  public useHighestAvailable() {\n    return this.useHighestSessionAal().useHighestSettingsFlowAal()\n  }\n\n  public enableCode() {\n    this.config.selfservice.methods.code.enabled = true\n    return this\n  }\n\n  public enableCodeMFA() {\n    this.config.selfservice.methods.code.mfa_enabled = true\n    return this\n  }\n}\n"
  },
  {
    "path": "test/e2e/cypress/support/index.d.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Session as KratosSession } from \"@ory/kratos-client\"\nimport { OryKratosConfiguration } from \"../../shared/config\"\nimport { ConfigBuilder } from \"./configHelpers\"\n\nexport interface MailMessage {\n  fromAddress: string\n  toAddresses: Array<string>\n  body: string\n  subject: string\n}\n\nexport type Strategy = \"code\" | \"link\"\ntype app = \"express\" | \"react\"\n\ndeclare global {\n  namespace Cypress {\n    interface Chainable {\n      /**\n       * Delete mails from the email server.\n       *\n       * @param options\n       */\n      deleteMail(options: { atLeast?: boolean }): Chainable<void>\n\n      /**\n       * Adds end enables a WebAuth authenticator key.\n       */\n      addVirtualAuthenticator(): Chainable<any>\n\n      /**\n       * Fetch the browser's Ory Session.\n       *\n       * @param opts\n       */\n      getSession(opts?: {\n        expectAal?: \"aal2\" | \"aal1\"\n        expectMethods?: Array<\n          | \"password\"\n          | \"webauthn\"\n          | \"lookup_secret\"\n          | \"totp\"\n          | \"code\"\n          | \"passkey\"\n        >\n        token?: string\n      }): Chainable<KratosSession>\n\n      /**\n       * Expect that the browser has no valid Ory Kratos Cookie Session.\n       */\n      noSession(): Chainable<Response<any>>\n\n      /**\n       * Log a user in\n       *\n       * @param opts\n       */\n      login(opts: {\n        email: string\n        password: string\n        expectSession?: boolean\n        cookieUrl?: string\n      }): Chainable<Response<KratosSession | undefined>>\n\n      /**\n       * Sign up a user\n       *\n       * @param opts\n       */\n      register(opts: {\n        email: string\n        password: string\n        query?: { [key: string]: string }\n        fields?: { [key: string]: any }\n      }): Chainable<Response<void>>\n\n      /**\n       * Register a user with a code\n       *\n       * @param opts\n       */\n      registerWithCode(opts: {\n        email: string\n        code?: string\n        traits?: { [key: string]: any }\n        query?: { [key: string]: string }\n        expectedMailCount?: number\n      }): Chainable<Response<void>>\n\n      /**\n       * Updates a user's settings using an API flow\n       *\n       * @param opts\n       */\n      settingsApi(opts: {\n        fields?: { [key: string]: any }\n      }): Chainable<Response<void>>\n\n      /**\n       * Set the \"privileged session lifespan\" to a large value.\n       */\n      longPrivilegedSessionTime(): Chainable<void>\n\n      /**\n       * Return and optionally delete an email from the fake email server.\n       *\n       * @param opts\n       */\n      getMail(opts: {\n        removeMail?: boolean\n        expectedCount?: number\n        email?: string\n        subject?: string\n        body?: string\n      }): Chainable<MailMessage>\n\n      performEmailVerification(opts?: {\n        expect?: { email?: string; redirectTo?: string }\n        strategy?: Strategy\n        useLinkFromEmail?: boolean\n      }): Chainable<void>\n\n      /**\n       * Sets the Ory Kratos configuration profile.\n       *\n       * @param profile\n       */\n      useConfigProfile(profile: string): Chainable<void>\n\n      /**\n       * Register a new user with email + password via the API.\n       *\n       * Recommended to use if you need an Ory Session Token or alternatively the user created.\n       *\n       * @param opts\n       */\n      registerApi(opts?: {\n        email: string\n        password: string\n        fields: { [key: string]: string }\n      }): Chainable<KratosSession>\n\n      /**\n       * Submits a recovery flow via the API\n       *\n       * @param opts\n       */\n      recoverApi(opts: { email: string; returnTo?: string }): Chainable<void>\n\n      /**\n       * Submits a verification flow via the API\n       *\n       * @param opts\n       */\n      verificationApi(opts: {\n        email: string\n        returnTo?: string\n        strategy?: Strategy\n      }): Chainable<void>\n\n      /**\n       * Update the config file\n       *\n       * @param cb\n       */\n      updateConfigFile(cb: (arg: OryKratosConfiguration) => any): Chainable<any>\n\n      /**\n       * Submits a verification flow via the API\n       *\n       * @param opts\n       */\n      verificationApiExpired(opts: {\n        email: string\n        strategy?: Strategy\n        returnTo?: string\n      }): Chainable<void>\n\n      /**\n       *  Sets the hook.\n       *\n       * @param hooks\n       */\n      setupHooks(\n        flow:\n          | \"registration\"\n          | \"login\"\n          | \"recovery\"\n          | \"verification\"\n          | \"settings\",\n        phase: \"before\" | \"after\",\n        kind: \"password\" | \"webauthn\" | \"oidc\" | \"code\" | \"passkey\",\n        hooks: Array<{ hook: string; config?: any }>,\n      ): Chainable<void>\n\n      /**\n       *  Sets the post registration hook.\n       *\n       * @param hooks\n       */\n      setPostPasswordRegistrationHooks(\n        hooks: Array<{ hook: string; config?: any }>,\n      ): Chainable<void>\n\n      /**\n       * Sets the post code registration hook.\n       *\n       * @param hooks\n       */\n      setPostCodeRegistrationHooks(\n        hooks: Array<{ hook: string; config?: any }>,\n      ): Chainable<void>\n\n      /**\n       * Submits a verification flow via the Browser\n       *\n       * @param opts\n       */\n      verificationBrowser(opts: {\n        email: string\n        returnTo?: string\n      }): Chainable<void>\n\n      /**\n       * Changes the config so that the login flow lifespan is very short.\n       *\n       *\n       * Useful when testing expiry of login flows.\n       * @see longLoginLifespan()\n       */\n      shortLoginLifespan(): Chainable<void>\n\n      /**\n       * Changes the config so that the login flow lifespan is very long.\n       *\n       * Useful when testing expiry of login flows.\n       *\n       * @see shortLoginLifespan()\n       */\n      longLoginLifespan(): Chainable<void>\n\n      /**\n       * Change the config so that `https://www.ory.sh/` is a allowed return to URL.\n       */\n      browserReturnUrlOry(): Chainable<void>\n\n      /**\n       * Change the courier recovery invalid and valid templates to remote base64 strings\n       */\n      remoteCourierRecoveryTemplates(): Chainable<void>\n\n      /**\n       * Resets the remote courier templates for the given template type to their default values\n       */\n      resetCourierTemplates(\n        type: \"recovery_code\" | \"recovery\" | \"verification\",\n      ): Chainable<void>\n\n      /**\n       * Change the courier recovery code invalid and valid templates to remote base64 strings\n       */\n      remoteCourierRecoveryCodeTemplates(): Chainable<void>\n\n      /**\n       * Changes the config so that the registration flow lifespan is very short.\n       *\n       * Useful when testing expiry of registration flows.\n       *\n       * @see longRegisterLifespan()\n       */\n      shortRegisterLifespan(): Chainable<void>\n\n      /**\n       * Changes the config so that the registration flow lifespan is very long.\n       *\n       * Useful when testing expiry of registration flows.\n       *\n       * @see shortRegisterLifespan()\n       */\n      longRegisterLifespan(): Chainable<void>\n\n      /**\n       * Changes the config so that the settings privileged lifespan is very long.\n       *\n       * Useful when testing privileged settings flows.\n       *\n       * @see longPrivilegedSessionTime()\n       */\n      shortPrivilegedSessionTime(): Chainable<void>\n\n      /**\n       * Re-authenticates a user.\n       *\n       * @param opts\n       */\n      reauth(opts: {\n        expect: { email: string; success?: boolean }\n        type: { email?: string; password?: string }\n      }): Chainable<void>\n\n      /**\n       * Change the config file to support lookup secrets\n       * @param value\n       */\n      useLookupSecrets(value: boolean): Chainable<void>\n\n      /**\n       * Re-authenticates a user.\n       *\n       * @param opts\n       */\n      reauthWithOtherAccount(opts: {\n        previousUrl: string\n        expect: { email: string; success?: boolean }\n        type: { email?: string; password?: string }\n      }): Chainable<void>\n\n      /**\n       * Do not require 2fa for /session/whoami\n       */\n      sessionRequiresNo2fa(): Chainable<void>\n\n      /**\n       * Require 2fa for /session/whoami if available\n       */\n      sessionRequires2fa(): Chainable<void>\n\n      /**\n       * Like sessionRequires2fa but sets this also for settings\n       */\n      requireStrictAal(): Chainable<void>\n\n      /**\n       * Like sessionRequiresNo2fa but sets this also for settings\n       */\n      useLaxAal(): Chainable<void>\n\n      /**\n       * Gets the lookup codes from the settings page\n       */\n      getLookupSecrets(): Chainable<Array<string>>\n\n      /**\n       * Expect the settings to be saved.\n       */\n      expectSettingsSaved(): Chainable<void>\n\n      clearCookies(\n        options?: Partial<Loggable & Timeoutable & { domain: null | string }>,\n      ): Chainable<null>\n\n      /**\n       * Submits a password form by clicking the button with method=password\n       */\n      submitPasswordForm(): Chainable<null>\n\n      /**\n       * Submits a profile form by clicking the button with method=profile\n       */\n      submitProfileForm(app?: \"mobile\" | \"express\" | \"react\"): Chainable<null>\n\n      /**\n       * Submits a code form by clicking the button with method=code\n       */\n      submitCodeForm(app: \"mobile\" | \"express\" | \"react\"): Chainable<void>\n\n      /**\n       * Expect a CSRF error to occur\n       *\n       * @param opts\n       */\n      shouldHaveCsrfError(opts: { app: string }): Chainable<void>\n\n      /**\n       * Expects the app to error if a return_to is used which isn't allowed.\n       *\n       * @param init\n       * @param opts\n       */\n      shouldErrorOnDisallowedReturnTo(\n        init: string,\n        opts: { app: string },\n      ): Chainable<void>\n\n      /**\n       * Expect that the second factor login screen is shown\n       */\n      shouldShow2FAScreen(): Chainable<void>\n\n      /** Click a webauthn button\n       *\n       * @param type\n       */\n      clickWebAuthButton(type: \"login\" | \"register\"): Chainable<void>\n\n      /**\n       * Sign up a user using Social Sign In\n       *\n       * @param opts\n       */\n      registerOidc(opts: {\n        app: app\n        email?: string\n        website?: string\n        scopes?: Array<string>\n        rememberLogin?: boolean\n        rememberConsent?: boolean\n        acceptLogin?: boolean\n        acceptConsent?: boolean\n        expectSession?: boolean\n        route?: string\n      }): Chainable<void>\n\n      /**\n       * Sign in a user using Social Sign In\n       *\n       * @param opts\n       */\n      loginOidc(opts: {\n        app: app\n        expectSession?: boolean\n        url?: string\n        preTriggerHook?: () => void\n      }): Chainable<void>\n\n      /**\n       * Triggers a Social Sign In flow for the given provider\n       *\n       * @param app\n       * @param provider\n       */\n      triggerOidc(app: \"react\" | \"express\", provider?: string): Chainable<void>\n\n      /**\n       * Changes the config so that the recovery privileged lifespan is very long.\n       *\n       * Useful when testing privileged recovery flows.\n       *\n       * @see shortPrivilegedRecoveryTime()\n       */\n      longRecoveryLifespan(): Chainable<void>\n\n      /**\n       * Changes the config so that the recovery privileged lifespan is very short.\n       *\n       * Useful when testing privileged recovery flows.\n       *\n       * @see shortPrivilegedRecoveryTime()\n       */\n      shortRecoveryLifespan(): Chainable<void>\n\n      /**\n       * Changes the config so that the verification privileged lifespan is very long.\n       *\n       * Useful when testing recovery/verification flows.\n       *\n       * @see shortLinkLifespan()\n       */\n      longVerificationLifespan(): Chainable<void>\n\n      /**\n       * Changes the config so that the verification privileged lifespan is very short.\n       *\n       * Useful when testing privileged verification flows.\n       *\n       * @see shortPrivilegedVerificationTime()\n       */\n      shortVerificationLifespan(): Chainable<void>\n\n      /**\n       * Log a user out\n       */\n      logout(): Chainable<void>\n\n      /**\n       * Deletes all mail in the mail mock server.\n       *\n       * @param opts\n       */\n      deleteMail(opts?: { atLeast?: number }): Chainable<void>\n\n      /**\n       * Changes the config so that the link lifespan is very long.\n       *\n       * Useful when testing recovery/verification flows.\n       *\n       * @see shortLinkLifespan()\n       */\n      longLinkLifespan(): Chainable<void>\n\n      /**\n       * Changes the config so that the link lifespan is very short.\n       *\n       * Useful when testing recovery/verification flows.\n       *\n       * @see longLinkLifespan()\n       */\n      shortLinkLifespan(): Chainable<void>\n\n      /**\n       * Changes the config so that the code lifespan is very short.\n       *\n       * Useful when testing recovery/verification flows.\n       *\n       * @see longCodeLifespan()\n       */\n      shortCodeLifespan(): Chainable<void>\n\n      /**\n       * Sets the `lifespan` of a strategy to 1ms (a short value)\n       *\n       * Useful to test the behavior if the subject of the strategy expired\n       *\n       * @param s the strategy\n       */\n      shortLifespan(s: Strategy): Chainable<void>\n\n      /**\n       * Sets the `lifespan` of a strategy to 1m\n       *\n       * @param s the strategy\n       */\n      longLifespan(s: Strategy): Chainable<void>\n\n      /**\n       * Changes the config so that the code lifespan is very long.\n       *\n       * Useful when testing recovery/verification flows.\n       *\n       * @see shortCodeLifespan()\n       */\n      longCodeLifespan(): Chainable<void>\n\n      /**\n       * Expect a recovery email which is expired.\n       *\n       * @param opts\n       */\n      recoverEmailButExpired(opts?: {\n        expect: { email: string }\n      }): Chainable<void>\n\n      /**\n       * Expect a recovery email with a recovery code.\n       *\n       * @param opts\n       */\n      recoveryEmailWithCode(opts?: {\n        expect: { email: string; enterCode?: boolean }\n      }): Chainable<void>\n\n      /**\n       * Expect a verification email which is expired.\n       *\n       * @param opts\n       */\n      verifyEmailButExpired(opts?: {\n        expect: { email: string }\n        strategy?: Strategy\n      }): Chainable<void>\n\n      /**\n       * Disables verification\n       */\n      disableVerification(): Chainable<void>\n\n      /**\n       * Enables verification\n       */\n      enableVerification(): Chainable<void>\n\n      /**\n       * Enables recovery\n       */\n      enableRecovery(): Chainable<void>\n\n      /**\n       * Sets the recovery strategy to use\n       */\n      useRecoveryStrategy(strategy: Strategy): Chainable<void>\n\n      /**\n       * Disables a specific recovery strategy\n       *\n       * @param strategy the recovery strategy to disable\n       */\n      disableRecoveryStrategy(strategy: Strategy): Chainable<void>\n\n      /**\n       * Disabled recovery\n       */\n      disableRecovery(): Chainable<void>\n\n      /**\n       * Disables registration\n       */\n      disableRegistration(): Chainable<void>\n\n      /**\n       * Enables registration\n       */\n      enableRegistration(): Chainable<void>\n\n      /**\n       * Expect a recovery email which is valid.\n       *\n       * @param opts\n       */\n      recoverEmail(opts: {\n        expect: { email: string }\n        shouldVisit?: boolean\n      }): Chainable<MailMessage>\n\n      /**\n       * Expect a verification email which is valid.\n       *\n       * @param opts\n       */\n      verifyEmail(opts: {\n        expect: { email: string; password?: string; redirectTo?: string }\n        strategy?: Strategy\n        shouldVisit?: boolean\n      }): Chainable<void>\n\n      /**\n       * Configures a hook which only allows verified email addresses to sign in.\n       */\n      enableLoginForVerifiedAddressOnly(): Chainable<void>\n\n      /**\n       * Sets the value for the `notify_unknown_recipients` key for a flow\n       *\n       * @param flow the flow for which to set the config value\n       * @param value the value, defaults to true\n       */\n      notifyUnknownRecipients(\n        flow: \"recovery\" | \"verification\",\n        value?: boolean,\n      ): Chainable<void>\n\n      /**\n       * Sign a user in via the API and return the session.\n       *\n       * @param opts\n       */\n      loginApi(opts: {\n        email: string\n        password: string\n      }): Chainable<{ session: KratosSession }>\n\n      /**\n       * Same as loginApi but uses dark magic to avoid cookie issues.\n       *\n       * @param opts\n       */\n      loginApiWithoutCookies(opts: {\n        email: string\n        password: string\n      }): Chainable<{ session: KratosSession }>\n\n      /**\n       * Which app to proxy\n       */\n      proxy(app: \"react\" | \"express\"): Chainable<void>\n\n      /**\n       * Log a user in on mobile\n       *\n       * @param opts\n       */\n      loginMobile(opts: { email: string; password: string }): Chainable<void>\n\n      /**\n       * Set the identity schema\n       * @param schema\n       */\n      setIdentitySchema(schema: string): Chainable<void>\n\n      /**\n       * Set the default schema\n       * @param id\n       */\n      setDefaultIdentitySchema(id: string): Chainable<void>\n\n      /**\n       * Remove the specified attribute from the given HTML elements\n       */\n      removeAttribute(selectors: string[], attribute: string): Chainable<void>\n\n      /**\n       * Add an input element to the DOM as a child of the given parent\n       */\n      addInputElement(\n        parent: string,\n        attribute: string,\n        value: string,\n      ): Chainable<void>\n\n      /**\n       * Fetches the courier messages from the admin API\n       */\n      getCourierMessages(): Chainable<\n        { recipient: string; template_type: string }[]\n      >\n\n      /**\n       * Enable the verification UI after registration hook\n       */\n      enableVerificationUIAfterRegistration(\n        strategy: \"password\" | \"oidc\" | \"webauthn\",\n      ): Chainable<void>\n\n      /**\n       * Extracts a verification code from the received email\n       */\n      getVerificationCodeFromEmail(email: string): Chainable<string>\n\n      /**\n       * Extracts a registration code from the received email\n       */\n      getRegistrationCodeFromEmail(\n        email: string,\n        opts?: { expectedCount: number; removeMail?: boolean },\n      ): Chainable<string>\n\n      /**\n       * Extracts a login code from the received email\n       */\n      getLoginCodeFromEmail(\n        email: string,\n        opts?: { expectedCount: number },\n      ): Chainable<string>\n\n      useConfig(cb: (config: ConfigBuilder) => ConfigBuilder): Chainable<void>\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/cypress/support/index.js",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport \"./commands\"\n\nCypress.Cookies.debug(true)\n"
  },
  {
    "path": "test/e2e/cypress/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"../../../node_modules\",\n    \"target\": \"es5\",\n    \"lib\": [\"es2015\", \"dom\"],\n    \"types\": [\"cypress\", \"node\"],\n    \"esModuleInterop\": true\n  },\n  \"include\": [\"**/*.ts\", \"support/index.ts\", \"../shared/config.d.ts\"]\n}\n"
  },
  {
    "path": "test/e2e/cypress.config.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { defineConfig } from \"cypress\"\nimport got from \"got\"\nconst CRI = require(\"chrome-remote-interface\")\n\nlet criPort = 0,\n  criClient = null\n\nexport default defineConfig({\n  chromeWebSecurity: false,\n  defaultCommandTimeout: 10000,\n  requestTimeout: 10000,\n  projectId: \"bc48bg\",\n  video: true,\n  videoCompression: false,\n  screenshotOnRunFailure: true,\n\n  e2e: {\n    retries: {\n      runMode: 6,\n      openMode: 1,\n    },\n    experimentalRunAllSpecs: true,\n    videosFolder: \"cypress/videos\",\n    screenshotsFolder: \"cypress/screenshots\",\n    excludeSpecPattern: [\"**/*snapshots.js\", \"playwright/**\"],\n    supportFile: \"cypress/support/index.js\",\n    specPattern: \"**/*.spec.{js,ts}\",\n    baseUrl: \"http://localhost:4455/\",\n    setupNodeEvents(on, config) {\n      on(\"before:browser:launch\", (browser, launchOptions) => {\n        criPort = ensureRdpPort(launchOptions.args)\n        console.log(\"criPort is\", criPort)\n\n        if (browser.name.includes(\"chrom\") && browser.isHeadless) {\n          launchOptions.args.push(\"--headless=new\")\n        }\n        return launchOptions\n      })\n\n      on(\"task\", {\n        httpRequest(params) {\n          return got(params).then(({ body }) => body)\n        },\n        // Reset chrome remote interface for clean state\n        async resetCRI() {\n          if (criClient) {\n            const c = criClient\n            criClient = null\n            await c.close()\n          }\n\n          return Promise.resolve(true)\n        },\n        // Execute CRI command\n        async sendCRI(args) {\n          if (!criClient) {\n            criClient = await CRI({ port: criPort })\n          }\n\n          return criClient.send(args.query, args.opts)\n        },\n      })\n    },\n  },\n})\n\nfunction ensureRdpPort(args) {\n  const existing = args.find(\n    (arg) => arg.slice(0, 23) === \"--remote-debugging-port\",\n  )\n\n  if (existing) {\n    return Number(existing.split(\"=\")[1])\n  }\n\n  const port = 40000 + Math.round(Math.random() * 25000)\n  args.push(`--remote-debugging-port=${port}`)\n  return port\n}\n"
  },
  {
    "path": "test/e2e/hydra-kratos-login-consent/go.mod",
    "content": "module github.com/ory/kratos/test/e2e/hydra-kratos-login-consent\n\ngo 1.26\n\nrequire (\n\tgithub.com/ory/hydra-client-go v1.7.4\n\tgithub.com/ory/kratos-client-go v0.10.1\n\tgithub.com/ory/x v0.0.722-0.20250620091013-eeb8bd14b65a\n)\n\nrequire (\n\tcode.dny.dev/ssrf v0.2.0 // indirect\n\tgithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect\n\tgithub.com/avast/retry-go/v4 v4.6.1 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-openapi/analysis v0.23.0 // indirect\n\tgithub.com/go-openapi/errors v0.22.1 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.21.1 // indirect\n\tgithub.com/go-openapi/jsonreference v0.21.0 // indirect\n\tgithub.com/go-openapi/loads v0.22.0 // indirect\n\tgithub.com/go-openapi/runtime v0.28.0 // indirect\n\tgithub.com/go-openapi/spec v0.21.0 // indirect\n\tgithub.com/go-openapi/strfmt v0.23.0 // indirect\n\tgithub.com/go-openapi/swag v0.23.1 // indirect\n\tgithub.com/go-openapi/validate v0.24.0 // indirect\n\tgithub.com/gobwas/glob v0.2.3 // indirect\n\tgithub.com/goccy/go-yaml v1.18.0 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-retryablehttp v0.7.8 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/mailru/easyjson v0.9.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.0 // indirect\n\tgithub.com/oklog/ulid v1.3.1 // indirect\n\tgithub.com/opentracing/opentracing-go v1.2.0 // indirect\n\tgithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect\n\tgithub.com/sirupsen/logrus v1.9.3 // indirect\n\tgithub.com/spf13/cobra v1.10.1 // indirect\n\tgithub.com/spf13/pflag v1.0.10 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tidwall/gjson v1.18.0 // indirect\n\tgithub.com/tidwall/match v1.1.1 // indirect\n\tgithub.com/tidwall/pretty v1.2.1 // indirect\n\tgithub.com/urfave/negroni v1.0.0 // indirect\n\tgo.mongodb.org/mongo-driver v1.17.4 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.1.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect\n\tgo.opentelemetry.io/otel v1.38.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.38.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.38.0 // indirect\n\tgolang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect\n\tgolang.org/x/net v0.44.0 // indirect\n\tgolang.org/x/oauth2 v0.31.0 // indirect\n\tgolang.org/x/sync v0.17.0 // indirect\n\tgolang.org/x/sys v0.36.0 // indirect\n\tgolang.org/x/text v0.29.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "test/e2e/hydra-kratos-login-consent/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ncode.dny.dev/ssrf v0.2.0 h1:wCBP990rQQ1CYfRpW+YK1+8xhwUjv189AQ3WMo1jQaI=\ncode.dny.dev/ssrf v0.2.0/go.mod h1:B+91l25OnyaLIeCx0WRJN5qfJ/4/ZTZxRXgm0lj/2w8=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=\ngithub.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=\ngithub.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=\ngithub.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=\ngithub.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=\ngithub.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=\ngithub.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/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-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=\ngithub.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=\ngithub.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=\ngithub.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=\ngithub.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=\ngithub.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=\ngithub.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=\ngithub.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=\ngithub.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=\ngithub.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=\ngithub.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=\ngithub.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=\ngithub.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=\ngithub.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=\ngithub.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU=\ngithub.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0=\ngithub.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=\ngithub.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=\ngithub.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=\ngithub.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=\ngithub.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=\ngithub.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=\ngithub.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=\ngithub.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=\ngithub.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=\ngithub.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=\ngithub.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=\ngithub.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=\ngithub.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY=\ngithub.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=\ngithub.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=\ngithub.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=\ngithub.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=\ngithub.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=\ngithub.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=\ngithub.com/go-openapi/runtime v0.19.21/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=\ngithub.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=\ngithub.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=\ngithub.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=\ngithub.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=\ngithub.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=\ngithub.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=\ngithub.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=\ngithub.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=\ngithub.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=\ngithub.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=\ngithub.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=\ngithub.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=\ngithub.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=\ngithub.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=\ngithub.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=\ngithub.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=\ngithub.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=\ngithub.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=\ngithub.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=\ngithub.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=\ngithub.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=\ngithub.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=\ngithub.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=\ngithub.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=\ngithub.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=\ngithub.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=\ngithub.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=\ngithub.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=\ngithub.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=\ngithub.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=\ngithub.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=\ngithub.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=\ngithub.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=\ngithub.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=\ngithub.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=\ngithub.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=\ngithub.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=\ngithub.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=\ngithub.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=\ngithub.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=\ngithub.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=\ngithub.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=\ngithub.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=\ngithub.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=\ngithub.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=\ngithub.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=\ngithub.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=\ngithub.com/gobuffalo/httptest v1.5.2 h1:GpGy520SfY1QEmyPvaqmznTpG4gEQqQ82HtHqyNEreM=\ngithub.com/gobuffalo/httptest v1.5.2/go.mod h1:FA23yjsWLGj92mVV74Qtc8eqluc11VqcWr8/C1vxt4g=\ngithub.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=\ngithub.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=\ngithub.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=\ngithub.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=\ngithub.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=\ngithub.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=\ngithub.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=\ngithub.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=\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-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=\ngithub.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=\ngithub.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=\ngithub.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=\ngithub.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=\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/providers/rawbytes v1.0.0 h1:MrKDh/HksJlKJmaZjgs4r8aVBb/zsJyc/8qaSnzcdNI=\ngithub.com/knadh/koanf/providers/rawbytes v1.0.0/go.mod h1:KxwYJf1uezTKy6PBtfE+m725NGp4GPVA7XoNTJ/PtLo=\ngithub.com/knadh/koanf/v2 v2.2.1 h1:jaleChtw85y3UdBnI0wCqcg1sj1gPoz6D3caGNHtrNE=\ngithub.com/knadh/koanf/v2 v2.2.1/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY=\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.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=\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/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=\ngithub.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=\ngithub.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=\ngithub.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=\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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\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.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nyaruka/phonenumbers v1.6.3 h1:JU7Q30+UM/03/vto6Q4EiZfEuRpTVyXMqImIbI942Qw=\ngithub.com/nyaruka/phonenumbers v1.6.3/go.mod h1:7gjs+Lchqm49adhAKB5cdcng5ZXgt6x7Jgvi0ZorUtU=\ngithub.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=\ngithub.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=\ngithub.com/ory/herodot v0.10.5 h1:pJv+Y4qQqZgqtQQeb/B+e9MgQe5YVGfNZ2O8DEJ1w3U=\ngithub.com/ory/herodot v0.10.5/go.mod h1:j6i246U6iX8TStYNKIVQxb2waweQvtOLi+b/9q+OULg=\ngithub.com/ory/hydra-client-go v1.7.4 h1:xazbWaXCsAjRazT8EWStU6qjkT0I0EC6WtXZOGtNau4=\ngithub.com/ory/hydra-client-go v1.7.4/go.mod h1:g1By+kj32wbTmbtBWnFV0NWDif3YBxPvse882PU912I=\ngithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e h1:4tUrC7x4YWRVMFp+c64KACNSGchW1zXo4l6Pa9/1hA8=\ngithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e/go.mod h1:XWLxVK4un/iuIcrw+6lCeanbF3NZwO5k6RdLeu/loQk=\ngithub.com/ory/kratos-client-go v0.10.1 h1:kSRk+0leCJ1nPMS+FPho8b9WMzrKNpgszvta0Xo32QU=\ngithub.com/ory/kratos-client-go v0.10.1/go.mod h1:dOQIsar76K07wMPJD/6aMhrWyY+sFGEagLDLso1CpsA=\ngithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e h1:gsbAteu8HZYnkIF4WVBaxklvF/s5IbcxYcCi6qX93ms=\ngithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e/go.mod h1:PEqjxMcIV87rBhlyDDha76I7/w2W/FHenSq3V3X1A/A=\ngithub.com/ory/x v0.0.722-0.20250620091013-eeb8bd14b65a h1:zfM0Xzu1J4GZlUNhkcYD4g8rSrmA7QfkFSLn5ELdBBM=\ngithub.com/ory/x v0.0.722-0.20250620091013-eeb8bd14b65a/go.mod h1:157mGLF6EksbwsZfheBr8iF8wiFIZMwYAcwxrNkBVoM=\ngithub.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=\ngithub.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 h1:0b8DF5kR0PhRoRXDiEEdzrgBc8UqVY4JWLkQJCRsLME=\ngithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761/go.mod h1:/THDZYi7F/BsVEcYzYPqdcWFQ+1C2InkawTKfLOAnzg=\ngithub.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\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.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=\ngithub.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=\ngithub.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/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/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.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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\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.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=\ngithub.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=\ngithub.com/tidwall/pretty v1.2.1/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/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=\ngithub.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=\ngithub.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=\ngithub.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=\ngo.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=\ngo.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=\ngo.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=\ngo.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=\ngo.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\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/httptrace/otelhttptrace v0.61.0 h1:lREC4C0ilyP4WibDhQ7Gg2ygAQFP8oR07Fst/5cafwI=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0/go.mod h1:HfvuU0kW9HewH14VCOLImqKvUgONodURG7Alj/IrnGI=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=\ngo.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=\ngo.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=\ngo.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=\ngo.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=\ngo.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=\ngo.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=\ngo.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=\ngo.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=\ngo.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=\ngo.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=\ngolang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=\ngolang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=\ngolang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=\ngolang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\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-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=\ngolang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=\ngolang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=\ngoogle.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\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/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-20200227125254-8fa46927fb4f/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/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\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.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  },
  {
    "path": "test/e2e/hydra-kratos-login-consent/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/ory/hydra-client-go/client\"\n\t\"github.com/ory/hydra-client-go/client/admin\"\n\t\"github.com/ory/hydra-client-go/models\"\n\tkratos \"github.com/ory/kratos-client-go\"\n\t\"github.com/ory/x/osx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc checkReq(w http.ResponseWriter, err error) bool {\n\tif err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"%+v\", err), 500)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc main() {\n\tkratosPublicURL := urlx.ParseOrPanic(osx.GetenvDefault(\"KRATOS_PUBLIC_URL\", \"http://localhost:4433\"))\n\tadminURL := urlx.ParseOrPanic(osx.GetenvDefault(\"HYDRA_ADMIN_URL\", \"http://localhost:4445\"))\n\thc := client.NewHTTPClientWithConfig(nil, &client.TransportConfig{Schemes: []string{adminURL.Scheme}, Host: adminURL.Host, BasePath: adminURL.Path})\n\n\thttp.HandleFunc(\"GET /\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Write([]byte(`ok`))\n\t})\n\thttp.HandleFunc(\"GET /login\", func(w http.ResponseWriter, r *http.Request) {\n\t\tres, err := hc.Admin.GetLoginRequest(admin.NewGetLoginRequestParams().\n\t\t\tWithLoginChallenge(r.URL.Query().Get(\"login_challenge\")))\n\t\tif !checkReq(w, err) {\n\t\t\treturn\n\t\t}\n\t\tif *res.Payload.Skip {\n\t\t\tres, err := hc.Admin.AcceptLoginRequest(admin.NewAcceptLoginRequestParams().\n\t\t\t\tWithLoginChallenge(r.URL.Query().Get(\"login_challenge\")).\n\t\t\t\tWithBody(&models.AcceptLoginRequest{\n\t\t\t\t\tRemember: true, RememberFor: 3600,\n\t\t\t\t\tSubject: res.Payload.Subject,\n\t\t\t\t}))\n\t\t\tif !checkReq(w, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thttp.Redirect(w, r, *res.Payload.RedirectTo, http.StatusFound)\n\t\t\treturn\n\t\t}\n\n\t\tchallenge := r.URL.Query().Get(\"login_challenge\")\n\t\t_, _ = fmt.Fprintf(w, `<html>\n<body>\n\t<form action=\"/login?login_challenge=%s\" method=\"post\">\n\t\t<input type=\"text\" name=\"username\" id=\"username\" />\n\t\t<input type=\"checkbox\" name=\"remember\" id=\"remember\" value=\"true\"/> Remember me\n\t\t<button type=\"submit\" name=\"action\" value=\"accept\" id=\"accept\">login</button>\n\t\t<button type=\"submit\" name=\"action\" value=\"reject\" id=\"reject\">reject</button>\n\t</form>\n</body>\n</html>`, challenge)\n\t})\n\n\thttp.HandleFunc(\"POST /login\", func(w http.ResponseWriter, r *http.Request) {\n\t\tif !checkReq(w, r.ParseForm()) {\n\t\t\treturn\n\t\t}\n\t\tif r.Form.Get(\"action\") == \"accept\" {\n\t\t\tres, err := hc.Admin.AcceptLoginRequest(admin.NewAcceptLoginRequestParams().\n\t\t\t\tWithLoginChallenge(r.URL.Query().Get(\"login_challenge\")).\n\t\t\t\tWithBody(&models.AcceptLoginRequest{\n\t\t\t\t\tRememberFor: 3600, Remember: r.Form.Get(\"remember\") == \"true\",\n\t\t\t\t\tSubject: new(r.Form.Get(\"username\")),\n\t\t\t\t}))\n\t\t\tif !checkReq(w, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thttp.Redirect(w, r, *res.Payload.RedirectTo, http.StatusFound)\n\t\t\treturn\n\t\t}\n\t\tres, err := hc.Admin.RejectLoginRequest(admin.NewRejectLoginRequestParams().\n\t\t\tWithLoginChallenge(r.URL.Query().Get(\"login_challenge\")).\n\t\t\tWithBody(&models.RejectRequest{Error: \"login rejected request\"}))\n\t\tif !checkReq(w, err) {\n\t\t\treturn\n\t\t}\n\t\thttp.Redirect(w, r, *res.Payload.RedirectTo, http.StatusFound)\n\t})\n\n\thttp.HandleFunc(\"GET /consent\", func(w http.ResponseWriter, r *http.Request) {\n\t\tres, err := hc.Admin.GetConsentRequest(admin.NewGetConsentRequestParams().\n\t\t\tWithConsentChallenge(r.URL.Query().Get(\"consent_challenge\")))\n\t\tif !checkReq(w, err) {\n\t\t\treturn\n\t\t}\n\t\tif res.Payload.Skip {\n\t\t\tres, err := hc.Admin.AcceptConsentRequest(admin.NewAcceptConsentRequestParams().\n\t\t\t\tWithConsentChallenge(r.URL.Query().Get(\"consent_challenge\")).\n\t\t\t\tWithBody(&models.AcceptConsentRequest{GrantScope: res.Payload.RequestedScope}))\n\t\t\tif !checkReq(w, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thttp.Redirect(w, r, *res.Payload.RedirectTo, http.StatusFound)\n\t\t\treturn\n\t\t}\n\n\t\tcheckoxes := \"\"\n\t\tfor _, s := range res.Payload.RequestedScope {\n\t\t\tcheckoxes += fmt.Sprintf(`<li><input type=\"checkbox\" name=\"scope\" value=\"%s\" id=\"%s\"/>%s</li>`, s, s, s)\n\t\t}\n\n\t\tchallenge := r.URL.Query().Get(\"consent_challenge\")\n\t\t_, _ = fmt.Fprintf(w, `<html>\n<body>\n\t<form action=\"/consent?consent_challenge=%s\" method=\"post\">\n\t\t<ul>\n\t\t%s\n\t\t</ul>\n\t\t<input type=\"text\" name=\"website\" id=\"website\" />\n\t\t<input type=\"checkbox\" name=\"remember\" id=\"remember\" value=\"true\"/> Remember me\n\t\t<button type=\"submit\" name=\"action\" value=\"accept\" id=\"accept\">login</button>\n\t\t<button type=\"submit\" name=\"action\" value=\"reject\" id=\"reject\">reject</button>\n\t</form>\n</body>\n</html>`, challenge, checkoxes)\n\t})\n\n\thttp.HandleFunc(\"POST /consent\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_ = r.ParseForm()\n\t\tif r.Form.Get(\"action\") == \"accept\" {\n\t\t\tkratosConfig := kratos.NewConfiguration()\n\t\t\tkratosConfig.Servers = kratos.ServerConfigurations{{URL: kratosPublicURL.String()}}\n\t\t\tkratosClient := kratos.NewAPIClient(kratosConfig)\n\t\t\tsession, _, err := kratosClient.V0alpha2Api.ToSession(r.Context()).Cookie(r.Header.Get(\"Cookie\")).Execute()\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\ttraitMap, ok := session.Identity.Traits.(map[string]interface{})\n\t\t\tif !ok {\n\t\t\t\tpanic(\"type assertion failed\")\n\t\t\t}\n\t\t\tidToken := map[string]interface{}{}\n\t\t\t// Populate ID token claims with values found in the session's traits\n\t\t\tfor _, scope := range r.Form[\"scope\"] {\n\t\t\t\tif v, ok := traitMap[scope]; ok {\n\t\t\t\t\tidToken[scope] = v\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tres, err := hc.Admin.AcceptConsentRequest(admin.NewAcceptConsentRequestParams().\n\t\t\t\tWithConsentChallenge(r.URL.Query().Get(\"consent_challenge\")).\n\t\t\t\tWithBody(&models.AcceptConsentRequest{\n\t\t\t\t\tSession:  &models.ConsentRequestSession{IDToken: idToken},\n\t\t\t\t\tRemember: r.Form.Get(\"remember\") == \"true\", RememberFor: 3600,\n\t\t\t\t\tGrantScope: r.Form[\"scope\"],\n\t\t\t\t}))\n\t\t\tif !checkReq(w, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thttp.Redirect(w, r, *res.Payload.RedirectTo, http.StatusFound)\n\t\t\treturn\n\t\t}\n\t\tres, err := hc.Admin.RejectConsentRequest(admin.NewRejectConsentRequestParams().\n\t\t\tWithConsentChallenge(r.URL.Query().Get(\"consent_challenge\")).\n\t\t\tWithBody(&models.RejectRequest{Error: \"consent rejected request\"}))\n\t\tif !checkReq(w, err) {\n\t\t\treturn\n\t\t}\n\t\thttp.Redirect(w, r, *res.Payload.RedirectTo, http.StatusFound)\n\t})\n\n\taddr := \":\" + osx.GetenvDefault(\"PORT\", \"4746\")\n\tfmt.Printf(\"Starting web server at %s\\n\", addr)\n\tlog.Fatal(http.ListenAndServe(addr, nil))\n}\n"
  },
  {
    "path": "test/e2e/hydra-login-consent/go.mod",
    "content": "module github.com/ory/kratos/test/e2e/hydra-login-consent\n\ngo 1.26\n\nrequire (\n\tgithub.com/ory/hydra-client-go/v2 v2.0.3\n\tgithub.com/ory/x v0.0.721\n)\n\nrequire (\n\tcode.dny.dev/ssrf v0.2.0 // indirect\n\tgithub.com/avast/retry-go/v4 v4.6.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.21.1 // indirect\n\tgithub.com/go-openapi/swag v0.23.1 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.4.0 // indirect\n\tgithub.com/gobwas/glob v0.2.3 // indirect\n\tgithub.com/goccy/go-yaml v1.16.0 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-retryablehttp v0.7.7 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/mailru/easyjson v0.9.0 // indirect\n\tgithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect\n\tgithub.com/sirupsen/logrus v1.9.3 // indirect\n\tgithub.com/spf13/cobra v1.10.1 // indirect\n\tgithub.com/spf13/pflag v1.0.10 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tidwall/gjson v1.18.0 // indirect\n\tgithub.com/tidwall/match v1.1.1 // indirect\n\tgithub.com/tidwall/pretty v1.2.1 // indirect\n\tgithub.com/urfave/negroni v1.0.0 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.2.1 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect\n\tgo.opentelemetry.io/otel v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/sdk v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.39.0 // indirect\n\tgolang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect\n\tgolang.org/x/oauth2 v0.34.0 // indirect\n\tgolang.org/x/sync v0.19.0 // indirect\n\tgolang.org/x/sys v0.39.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect\n\tgoogle.golang.org/grpc v1.79.3 // indirect\n\tgoogle.golang.org/protobuf v1.36.10 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "test/e2e/hydra-login-consent/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ncode.dny.dev/ssrf v0.2.0 h1:wCBP990rQQ1CYfRpW+YK1+8xhwUjv189AQ3WMo1jQaI=\ncode.dny.dev/ssrf v0.2.0/go.mod h1:B+91l25OnyaLIeCx0WRJN5qfJ/4/ZTZxRXgm0lj/2w8=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=\ngithub.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/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-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=\ngithub.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=\ngithub.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=\ngithub.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=\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/gobuffalo/httptest v1.5.2 h1:GpGy520SfY1QEmyPvaqmznTpG4gEQqQ82HtHqyNEreM=\ngithub.com/gobuffalo/httptest v1.5.2/go.mod h1:FA23yjsWLGj92mVV74Qtc8eqluc11VqcWr8/C1vxt4g=\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-yaml v1.16.0 h1:d7m1G7A0t+logajVtklHfDYJs2Et9g3gHwdBNNFou0w=\ngithub.com/goccy/go-yaml v1.16.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=\ngithub.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=\ngithub.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=\ngithub.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=\ngithub.com/knadh/koanf/parsers/json v0.1.0 h1:dzSZl5pf5bBcW0Acnu20Djleto19T0CfHcvZ14NJ6fU=\ngithub.com/knadh/koanf/parsers/json v0.1.0/go.mod h1:ll2/MlXcZ2BfXD6YJcjVFzhG9P0TdJ207aIBKQhV2hY=\ngithub.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME=\ngithub.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c=\ngithub.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=\ngithub.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=\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/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/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=\ngithub.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\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/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=\ngithub.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\ngithub.com/nyaruka/phonenumbers v1.5.0 h1:0M+Gd9zl53QC4Nl5z1Yj1O/zPk2XXBUwR/vlzdXSJv4=\ngithub.com/nyaruka/phonenumbers v1.5.0/go.mod h1:gv+CtldaFz+G3vHHnasBSirAi3O2XLqZzVWz4V1pl2E=\ngithub.com/ory/herodot v0.10.3-0.20250318104651-3179543efba8 h1:bBFBzJ+sy1l/9+uYaz5TLGNNe0GWeXPMyqLhUEy9gPg=\ngithub.com/ory/herodot v0.10.3-0.20250318104651-3179543efba8/go.mod h1:aq2fDNzFXlh8wF6+ILtlEin2oZSrqR79/Zdsi05WEVA=\ngithub.com/ory/hydra-client-go/v2 v2.0.3 h1:jIx968J9RBnjRuaQ21QMLCwZoa28FPvzYWAQ+88XVLw=\ngithub.com/ory/hydra-client-go/v2 v2.0.3/go.mod h1:FRuayIF1H/HD2umlad8c3h7RuHpcmsjBDpW0/R2OQ/U=\ngithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e h1:4tUrC7x4YWRVMFp+c64KACNSGchW1zXo4l6Pa9/1hA8=\ngithub.com/ory/jsonschema/v3 v3.0.9-0.20250317235931-280c5fc7bf0e/go.mod h1:XWLxVK4un/iuIcrw+6lCeanbF3NZwO5k6RdLeu/loQk=\ngithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e h1:gsbAteu8HZYnkIF4WVBaxklvF/s5IbcxYcCi6qX93ms=\ngithub.com/ory/pop/v6 v6.3.2-0.20251203152233-a32233875f7e/go.mod h1:PEqjxMcIV87rBhlyDDha76I7/w2W/FHenSq3V3X1A/A=\ngithub.com/ory/x v0.0.721 h1:MN25GGP2GN+fiinoCIe4v4iybn8r70Ssj/ifWMydiUE=\ngithub.com/ory/x v0.0.721/go.mod h1:9uJPOoL3R1K2NJBM+JOpmyYgcVWfeqQeT/udkft+rcE=\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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 h1:0b8DF5kR0PhRoRXDiEEdzrgBc8UqVY4JWLkQJCRsLME=\ngithub.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761/go.mod h1:/THDZYi7F/BsVEcYzYPqdcWFQ+1C2InkawTKfLOAnzg=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=\ngithub.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\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/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/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=\ngithub.com/tidwall/pretty v1.2.1/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/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=\ngithub.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=\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.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=\ngo.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=\ngo.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=\ngo.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=\ngo.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=\ngo.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=\ngo.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=\ngo.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=\ngo.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=\ngo.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=\ngolang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=\ngolang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=\ngolang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=\ngolang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=\ngolang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=\ngoogle.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=\ngoogle.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\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-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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\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=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  },
  {
    "path": "test/e2e/hydra-login-consent/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\tclient \"github.com/ory/hydra-client-go/v2\"\n\n\t\"github.com/ory/x/osx\"\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc check(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc checkReq(w http.ResponseWriter, err error) bool {\n\tif err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"%+v\", err), 500)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc main() {\n\trouter := http.NewServeMux()\n\n\tadminURL := urlx.ParseOrPanic(osx.GetenvDefault(\"HYDRA_ADMIN_URL\", \"http://localhost:4445\"))\n\tcfg := client.NewConfiguration()\n\tcfg.Servers = client.ServerConfigurations{\n\t\t{URL: adminURL.String()},\n\t}\n\thc := client.NewAPIClient(cfg)\n\n\trouter.HandleFunc(\"GET /\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Write([]byte(`ok`))\n\t})\n\trouter.HandleFunc(\"GET /login\", func(w http.ResponseWriter, r *http.Request) {\n\t\tres, _, err := hc.OAuth2Api.GetOAuth2LoginRequest(r.Context()).LoginChallenge(r.URL.Query().Get(\"login_challenge\")).Execute()\n\t\tif !checkReq(w, err) {\n\t\t\treturn\n\t\t}\n\n\t\tif res.Skip {\n\t\t\tres, _, err := hc.OAuth2Api.AcceptOAuth2LoginRequest(r.Context()).\n\t\t\t\tLoginChallenge(r.URL.Query().Get(\"login_challenge\")).\n\t\t\t\tAcceptOAuth2LoginRequest(client.AcceptOAuth2LoginRequest{\n\t\t\t\t\tRemember:    new(true),\n\t\t\t\t\tRememberFor: new(int64(3600)),\n\t\t\t\t\tSubject:     res.Subject,\n\t\t\t\t}).Execute()\n\t\t\tif !checkReq(w, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thttp.Redirect(w, r, res.RedirectTo, http.StatusFound)\n\t\t\treturn\n\t\t}\n\n\t\tchallenge := r.URL.Query().Get(\"login_challenge\")\n\t\t_, _ = fmt.Fprintf(w, `<html>\n<body>\n\t<form action=\"/login?login_challenge=%s\" method=\"post\">\n\t\t<input type=\"text\" name=\"username\" id=\"username\" />\n\t\t<input type=\"checkbox\" name=\"remember\" id=\"remember\" value=\"true\"/> Remember me\n\t\t<button type=\"submit\" name=\"action\" value=\"accept\" id=\"accept\">login</button>\n\t\t<button type=\"submit\" name=\"action\" value=\"reject\" id=\"reject\">reject</button>\n\t</form>\n</body>\n</html>`, challenge)\n\t})\n\n\trouter.HandleFunc(\"POST /login\", func(w http.ResponseWriter, r *http.Request) {\n\t\tcheck(r.ParseForm())\n\t\tremember := new(r.Form.Get(\"remember\") == \"true\")\n\t\tif r.Form.Get(\"action\") == \"accept\" {\n\t\t\tres, _, err := hc.OAuth2Api.AcceptOAuth2LoginRequest(r.Context()).\n\t\t\t\tLoginChallenge(r.URL.Query().Get(\"login_challenge\")).\n\t\t\t\tAcceptOAuth2LoginRequest(client.AcceptOAuth2LoginRequest{\n\t\t\t\t\tRememberFor: new(int64(3600)),\n\t\t\t\t\tRemember:    remember,\n\t\t\t\t\tSubject:     r.Form.Get(\"username\"),\n\t\t\t\t}).Execute()\n\n\t\t\tif !checkReq(w, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thttp.Redirect(w, r, res.RedirectTo, http.StatusFound)\n\t\t\treturn\n\t\t}\n\t\tres, _, err := hc.OAuth2Api.RejectOAuth2LoginRequest(r.Context()).LoginChallenge(r.URL.Query().Get(\"login_challenge\")).\n\t\t\tRejectOAuth2Request(client.RejectOAuth2Request{Error: new(\"login rejected request\")}).Execute()\n\t\tif !checkReq(w, err) {\n\t\t\treturn\n\t\t}\n\t\thttp.Redirect(w, r, res.RedirectTo, http.StatusFound)\n\t})\n\n\trouter.HandleFunc(\"GET /consent\", func(w http.ResponseWriter, r *http.Request) {\n\t\tres, _, err := hc.OAuth2Api.GetOAuth2ConsentRequest(r.Context()).ConsentChallenge(r.URL.Query().\n\t\t\tGet(\"consent_challenge\")).Execute()\n\t\tif !checkReq(w, err) {\n\t\t\treturn\n\t\t}\n\n\t\tif *res.Skip {\n\t\t\tres, _, err := hc.OAuth2Api.AcceptOAuth2ConsentRequest(r.Context()).\n\t\t\t\tConsentChallenge(r.URL.Query().Get(\"consent_challenge\")).\n\t\t\t\tAcceptOAuth2ConsentRequest(client.AcceptOAuth2ConsentRequest{\n\t\t\t\t\tGrantScope: res.RequestedScope,\n\t\t\t\t}).Execute()\n\t\t\tif !checkReq(w, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thttp.Redirect(w, r, res.RedirectTo, http.StatusFound)\n\t\t\treturn\n\t\t}\n\n\t\tcheckoxes := \"\"\n\t\tfor _, s := range res.RequestedScope {\n\t\t\tcheckoxes += fmt.Sprintf(`<li><input type=\"checkbox\" name=\"scope\" value=\"%s\" id=\"%s\"/>%s</li>`, s, s, s)\n\t\t}\n\n\t\tchallenge := r.URL.Query().Get(\"consent_challenge\")\n\t\t_, _ = fmt.Fprintf(w, `<html>\n<body>\n\t<form action=\"/consent?consent_challenge=%s\" method=\"post\">\n\t\t<ul>\n\t\t%s\n\t\t</ul>\n\t\t<input type=\"text\" name=\"website\" id=\"website\" />\n\t\t<input type=\"checkbox\" name=\"remember\" id=\"remember\" value=\"true\"/> Remember me\n\t\t<button type=\"submit\" name=\"action\" value=\"accept\" id=\"accept\">login</button>\n\t\t<button type=\"submit\" name=\"action\" value=\"reject\" id=\"reject\">reject</button>\n\t</form>\n</body>\n</html>`, challenge, checkoxes)\n\t})\n\n\trouter.HandleFunc(\"POST /consent\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_ = r.ParseForm()\n\t\tremember := new(r.Form.Get(\"remember\") == \"true\")\n\t\tif r.Form.Get(\"action\") == \"accept\" {\n\t\t\tres, _, err := hc.OAuth2Api.AcceptOAuth2ConsentRequest(r.Context()).\n\t\t\t\tConsentChallenge(r.URL.Query().Get(\"consent_challenge\")).\n\t\t\t\tAcceptOAuth2ConsentRequest(client.AcceptOAuth2ConsentRequest{\n\t\t\t\t\tSession: &client.AcceptOAuth2ConsentRequestSession{\n\t\t\t\t\t\tIdToken: map[string]interface{}{\n\t\t\t\t\t\t\t\"website\": r.Form.Get(\"website\"),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tRememberFor: new(int64(3600)),\n\t\t\t\t\tRemember:    remember,\n\t\t\t\t\tGrantScope:  r.Form[\"scope\"],\n\t\t\t\t},\n\t\t\t\t).Execute()\n\t\t\tif !checkReq(w, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thttp.Redirect(w, r, res.RedirectTo, http.StatusFound)\n\t\t\treturn\n\t\t}\n\t\tres, _, err := hc.OAuth2Api.RejectOAuth2ConsentRequest(r.Context()).\n\t\t\tConsentChallenge(r.URL.Query().Get(\"consent_challenge\")).\n\t\t\tRejectOAuth2Request(client.RejectOAuth2Request{Error: new(\"consent rejected request\")}).Execute()\n\t\tif !checkReq(w, err) {\n\t\t\treturn\n\t\t}\n\t\thttp.Redirect(w, r, res.RedirectTo, http.StatusFound)\n\t})\n\n\taddr := \":\" + osx.GetenvDefault(\"PORT\", \"4446\")\n\t//#nosec G112\n\tserver := &http.Server{Addr: addr, Handler: router}\n\tfmt.Printf(\"Starting web server at %s\\n\", addr)\n\tcheck(server.ListenAndServe())\n}\n"
  },
  {
    "path": "test/e2e/hydra.yml",
    "content": "version: v2.0.1\n\ndsn: memory\n\nlog:\n  level: trace\n\nurls:\n  self:\n    issuer: http://localhost:4444/\n    public: http://localhost:4444/\n  login: http://localhost:4446/login\n  consent: http://localhost:4446/consent\n\nsecrets:\n  system:\n    - \"1234567890123456789012345678901\"\n"
  },
  {
    "path": "test/e2e/mock/httptarget/go.mod",
    "content": "module github.com/ory/mock\n\ngo 1.26\n"
  },
  {
    "path": "test/e2e/mock/httptarget/go.sum",
    "content": ""
  },
  {
    "path": "test/e2e/mock/httptarget/main.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"sync\"\n)\n\nvar (\n\tdocumentsLock sync.RWMutex\n\tdocuments     = make(map[string][]byte)\n)\n\nfunc main() {\n\tport := cmp.Or(os.Getenv(\"PORT\"), \"4471\")\n\tlog.Fatal(http.ListenAndServe(fmt.Sprintf(\":%s\", port), nil))\n}\n\nfunc init() {\n\thttp.HandleFunc(\"/health\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = w.Write([]byte(\"OK\"))\n\t})\n\n\thttp.HandleFunc(\"GET /documents/{id}\", func(w http.ResponseWriter, r *http.Request) {\n\t\tid := r.PathValue(\"id\")\n\n\t\tdocumentsLock.RLock()\n\t\tdoc, ok := documents[id]\n\t\tdocumentsLock.RUnlock()\n\n\t\tif ok {\n\t\t\t_, _ = w.Write(doc)\n\t\t} else {\n\t\t\tw.WriteHeader(http.StatusNotFound)\n\t\t}\n\t})\n\n\thttp.HandleFunc(\"PUT /documents/{id}\", func(w http.ResponseWriter, r *http.Request) {\n\t\tdocumentsLock.Lock()\n\t\tdefer documentsLock.Unlock()\n\t\tid := r.PathValue(\"id\")\n\n\t\tbody, err := io.ReadAll(r.Body)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tdocuments[id] = body\n\t})\n\n\thttp.HandleFunc(\"DELETE /documents/{id}\", func(w http.ResponseWriter, r *http.Request) {\n\t\tdocumentsLock.Lock()\n\t\tdefer documentsLock.Unlock()\n\t\tid := r.PathValue(\"id\")\n\n\t\tdelete(documents, id)\n\t\tw.WriteHeader(http.StatusNoContent)\n\t})\n}\n"
  },
  {
    "path": "test/e2e/mock/webhook/Dockerfile",
    "content": "FROM golang:1.21-alpine AS build\n\nWORKDIR /build\n\nADD . .\n\nRUN cat go.*\nRUN CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -ldflags \"-s -w -extldflags '-static'\" -o ./app main.go\n\nFROM alpine\n\nWORKDIR /app\n\nCOPY --from=build /build/app ./mock\n\nUSER 65534:65534\n\nEXPOSE 4459\nENTRYPOINT [\"/app/mock\"]\n"
  },
  {
    "path": "test/e2e/mock/webhook/go.mod",
    "content": "module github.com/ory/mock\n\ngo 1.26\n\nrequire github.com/sirupsen/logrus v1.8.3\n\nrequire golang.org/x/sys v0.36.0 // indirect\n"
  },
  {
    "path": "test/e2e/mock/webhook/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/sirupsen/logrus v1.8.3 h1:DBBfY8eMYazKEJHb3JKpSPfpgd2mBCoNFlQx6C5fftU=\ngithub.com/sirupsen/logrus v1.8.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=\ngolang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "test/e2e/mock/webhook/main.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\n\tlog \"github.com/sirupsen/logrus\"\n)\n\nconst AuthHeader = \"X-Authorize-Request\"\n\ntype (\n\t// copied from selfservice/hook/web_hook.go\n\tdetailedMessage struct {\n\t\tID      int             `json:\"id\"`\n\t\tText    string          `json:\"text\"`\n\t\tType    string          `json:\"type\"`\n\t\tContext json.RawMessage `json:\"context,omitempty\"`\n\t}\n\n\terrorMessage struct {\n\t\tInstancePtr string            `json:\"instance_ptr\"`\n\t\tMessages    []detailedMessage `json:\"messages\"`\n\t}\n\n\trawHookResponse struct {\n\t\tMessages []errorMessage `json:\"messages\"`\n\t}\n\n\tlogResponseWriter struct {\n\t\tStatus int\n\t\tSize   int\n\t\thttp.ResponseWriter\n\t}\n)\n\n// Header returns & satisfies the http.ResponseWriter interface\nfunc (w *logResponseWriter) Header() http.Header {\n\treturn w.ResponseWriter.Header()\n}\n\n// Write satisfies the http.ResponseWriter interface and\n// captures data written, in bytes\nfunc (w *logResponseWriter) Write(data []byte) (int, error) {\n\n\twritten, err := w.ResponseWriter.Write(data)\n\tw.Size += written\n\n\treturn written, err\n}\n\n// WriteHeader satisfies the http.ResponseWriter interface and\n// allows us to catch the status code\nfunc (w *logResponseWriter) WriteHeader(statusCode int) {\n\n\tw.Status = statusCode\n\tw.ResponseWriter.WriteHeader(statusCode)\n}\n\nfunc accessLog(next http.HandlerFunc) http.HandlerFunc {\n\tfn := func(w http.ResponseWriter, r *http.Request) {\n\t\tlog.WithFields(log.Fields{\"application\": \"webhooks\", \"method\": r.Method, \"path\": r.URL.Path}).Info(\"incoming request\")\n\t\tresponseWriter := logResponseWriter{http.StatusOK, 0, w}\n\t\tnext.ServeHTTP(&responseWriter, r)\n\t\tlog.WithFields(log.Fields{\"application\": \"webhooks\", \"status\": responseWriter.Status, \"size\": responseWriter.Size, \"path\": r.URL.Path}).Info(\"response generated\")\n\t}\n\n\treturn fn\n}\n\nfunc headerAuth(next http.HandlerFunc) http.HandlerFunc {\n\tfn := func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.Header.Get(AuthHeader) != \"1\" {\n\t\t\tw.WriteHeader(http.StatusUnauthorized)\n\t\t} else {\n\t\t\tnext.ServeHTTP(w, r)\n\t\t}\n\t}\n\n\treturn fn\n}\n\nfunc healthCheck(w http.ResponseWriter, _ *http.Request) {\n\t_, _ = w.Write([]byte(\"OK\"))\n}\n\nfunc webhookHandler(w http.ResponseWriter, r *http.Request) {\n\tif r.Method != http.MethodPost {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tpayload := struct {\n\t\tIdentityId string          `json:\"identity_id,omitempty\"`\n\t\tEmail      string          `json:\"email,omitempty\"`\n\t\tFlowId     string          `json:\"flow_id\"`\n\t\tFlowType   string          `json:\"flow_type\"`\n\t\tContext    json.RawMessage `json:\"ctx\"`\n\t}{}\n\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tlog.WithError(err).Warn(\"could not read request body\")\n\t\tb := bytes.NewBufferString(fmt.Sprintf(\"error while reading request body: %s\", err))\n\t\t_, _ = w.Write(b.Bytes())\n\t\treturn\n\t}\n\tdefer r.Body.Close()\n\n\tif err := json.Unmarshal(body, &payload); err != nil {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tlog.WithError(err).Warn(\"could not unmarshal request JSON\")\n\t\tb := bytes.NewBufferString(fmt.Sprintf(\"error while parsing request JSON: %s\", err))\n\t\t_, _ = w.Write(b.Bytes())\n\t\treturn\n\t}\n\n\tlog.WithField(\"payload\", string(body)).Info(\"unmarshalled request\")\n\n\tif !strings.Contains(payload.Email, \"_blocked@ory.sh\") || payload.FlowType == \"api\" {\n\t\tw.WriteHeader(http.StatusOK)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusConflict)\n\n\tdetail := detailedMessage{\n\t\tID:   1234,\n\t\tType: \"error\",\n\t\tText: \"email could not be validated\",\n\t}\n\tmsg := errorMessage{InstancePtr: \"#/traits/email\", Messages: []detailedMessage{detail}}\n\tresp := rawHookResponse{Messages: []errorMessage{msg}}\n\terr = json.NewEncoder(w).Encode(&resp)\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\tbuff := bytes.NewBufferString(err.Error())\n\t\t_, _ = w.Write(buff.Bytes())\n\t\treturn\n\t}\n}\n\nfunc writeWebhookHandler(w http.ResponseWriter, r *http.Request) {\n\tif r.Method != http.MethodPost {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tlog.WithFields(log.Fields{\"application\": \"webhooks\", \"path\": r.URL.Path, \"response\": r.URL.Query().Get(\"response\")}).Info(\"sending response\")\n\t_, _ = w.Write([]byte(r.URL.Query().Get(\"response\")))\n}\n\nfunc main() {\n\tmux := http.NewServeMux()\n\n\tmux.HandleFunc(\"/health\", healthCheck)\n\tmux.HandleFunc(\"/webhook\", accessLog(headerAuth(webhookHandler)))\n\tmux.HandleFunc(\"/webhook/write\", accessLog(writeWebhookHandler))\n\n\ts := http.Server{\n\t\tAddr:    \":4459\",\n\t\tHandler: mux,\n\t}\n\n\terr := s.ListenAndServe()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "test/e2e/modd.conf",
    "content": "test/e2e/mock/webhook/*.go {\n    indir: ./test/e2e/mock/webhook\n    prep: go build -o ../../.bin/webhook .\n    daemon +sigterm: ../../.bin/webhook\n}\n\n**/*.go !**/*_test.go {\n    prep: go build -tags sqlite,json1 -o test/e2e/.bin/kratos .\n    prep: test/e2e/.bin/kratos migrate sql -e --yes\n    daemon +sigterm: test/e2e/.bin/kratos serve --watch-courier --dev -c test/e2e/kratos.generated.yml\n}\n"
  },
  {
    "path": "test/e2e/package.json",
    "content": "{\n  \"name\": \"@ory/kratos-e2e-suite\",\n  \"version\": \"0.0.1\",\n  \"scripts\": {\n    \"openapi-generator-cli\": \"openapi-generator-cli\",\n    \"playwright\": \"playwright test\",\n    \"playwright:ui\": \"playwright test --ui\",\n    \"test\": \"cypress run --browser chrome\",\n    \"test:watch\": \"cypress open --browser chrome\",\n    \"text-run\": \"exit 0\",\n    \"wait-on\": \"wait-on\"\n  },\n  \"dependencies\": {\n    \"@faker-js/faker\": \"10.1.0\",\n    \"@types/promise-retry\": \"1.1.6\",\n    \"async-retry\": \"1.3.3\",\n    \"mailhog\": \"4.16.0\",\n    \"promise-retry\": \"2.0.1\"\n  },\n  \"devDependencies\": {\n    \"@ory/kratos-client\": \"25.4.0\",\n    \"@playwright/test\": \"1.56.1\",\n    \"@types/async-retry\": \"1.4.9\",\n    \"@types/node\": \"24.10.0\",\n    \"@types/yamljs\": \"0.2.34\",\n    \"chrome-remote-interface\": \"0.33.3\",\n    \"cypress\": \"14.4.0\",\n    \"dayjs\": \"1.11.19\",\n    \"dotenv\": \"17.2.3\",\n    \"got\": \"14.6.3\",\n    \"json-schema-to-typescript\": \"15.0.4\",\n    \"otpauth\": \"9.4.1\",\n    \"phone-number-generator-js\": \"1.2.16\",\n    \"process\": \"0.11.10\",\n    \"typescript\": \"5.9.3\",\n    \"wait-on\": \"9.0.3\",\n    \"yamljs\": \"0.3.0\"\n  }\n}\n"
  },
  {
    "path": "test/e2e/playwright/actions/identity.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { faker } from \"@faker-js/faker\"\nimport { APIRequestContext } from \"@playwright/test\"\nimport { CreateIdentityBody } from \"@ory/kratos-client\"\nimport { generatePhoneNumber, CountryNames } from \"phone-number-generator-js\"\nimport { expect } from \"../fixtures\"\n\nexport async function createIdentity(\n  request: APIRequestContext,\n  data: Partial<CreateIdentityBody>,\n) {\n  const resp = await request.post(\"http://localhost:4434/admin/identities\", {\n    data,\n  })\n  expect(resp.status()).toBe(201)\n  return await resp.json()\n}\n\nexport async function createIdentityWithPhoneNumber(\n  request: APIRequestContext,\n) {\n  const phone = generatePhoneNumber({\n    countryName: CountryNames.Germany,\n    withoutCountryCode: false,\n  })\n  return {\n    identity: await createIdentity(request, {\n      schema_id: \"sms\",\n      traits: {\n        phone,\n      },\n    }),\n    phone,\n  }\n}\n\nexport async function createIdentityWithEmail(request: APIRequestContext) {\n  const email = faker.internet.email({ provider: \"ory.sh\" })\n  return {\n    identity: await createIdentity(request, {\n      schema_id: \"email\",\n      traits: {\n        email,\n        website: faker.internet.url(),\n      },\n    }),\n    email,\n  }\n}\n\nexport async function createIdentityWithPassword(request: APIRequestContext) {\n  const email = faker.internet.email({ provider: \"ory.sh\" })\n  const password = faker.internet.password()\n  return {\n    identity: await createIdentity(request, {\n      schema_id: \"email\",\n      traits: {\n        email,\n        website: faker.internet.url(),\n      },\n\n      credentials: {\n        password: {\n          config: {\n            password,\n          },\n        },\n      },\n    }),\n    email,\n    password,\n  }\n}\n"
  },
  {
    "path": "test/e2e/playwright/actions/login.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APIRequestContext } from \"@playwright/test\"\nimport { findCsrfToken } from \"../lib/helper\"\nimport { LoginFlow, LogoutFlow, Session } from \"@ory/kratos-client\"\nimport { expectJSONResponse } from \"../lib/request\"\nimport { expect } from \"../fixtures\"\n\nexport async function loginWithPassword(\n  user: { password: string; traits: { email: string } },\n  r: APIRequestContext,\n  baseUrl: string,\n): Promise<void> {\n  const { ui } = await expectJSONResponse<LoginFlow>(\n    await r.get(baseUrl + \"/self-service/login/browser\", {\n      headers: {\n        Accept: \"application/json\",\n      },\n    }),\n    {\n      message: \"Initializing login flow failed\",\n    },\n  )\n\n  const res = await r.post(ui.action, {\n    headers: {\n      Accept: \"application/json\",\n    },\n    data: {\n      identifier: user.traits.email,\n      password: user.password,\n      method: \"password\",\n      csrf_token: findCsrfToken(ui),\n    },\n  })\n  const { session } = await expectJSONResponse<{ session: Session }>(res)\n  expect(session?.identity?.traits.email).toEqual(user.traits.email)\n  expect(\n    res.headersArray().find(\n      ({ name, value }) =>\n        name.toLowerCase() === \"set-cookie\" &&\n        (value.indexOf(\"ory_session_\") > -1 || // Ory Network\n          value.indexOf(\"ory_kratos_session\") > -1), // Locally hosted\n    ),\n  ).toBeDefined()\n}\n\nexport async function logoutUrl(r: APIRequestContext, baseUrl: string) {\n  const { logout_url } = await expectJSONResponse<LogoutFlow>(\n    await r.get(baseUrl + \"/self-service/logout/browser\", {\n      headers: {\n        Accept: \"application/json\",\n      },\n    }),\n    {\n      message: \"Logout failed\",\n    },\n  )\n  return logout_url\n}\n"
  },
  {
    "path": "test/e2e/playwright/actions/mail.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport mailhog from \"mailhog\"\nimport retry from \"async-retry\"\n\nconst mh = mailhog({\n  basePath: \"http://localhost:8025/api\",\n})\n\ntype searchProps = {\n  query: string\n  kind: \"to\" | \"from\" | \"containing\"\n  /**\n   *\n   * @param message an email message\n   * @returns decide whether to include the message in the result\n   */\n  filter?: (message: mailhog.Message) => boolean\n}\n\nexport function search({ query, kind, filter }: searchProps) {\n  return retry(\n    async () => {\n      const res = await mh.search(query, kind)\n      if (res.total === 0) {\n        throw new Error(\"no emails found\")\n      }\n      const result = filter ? res.items.filter(filter) : res.items\n      if (result.length === 0) {\n        throw new Error(\"no emails found\")\n      }\n      return result\n    },\n    {\n      retries: 3,\n    },\n  )\n}\n"
  },
  {
    "path": "test/e2e/playwright/actions/session.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APIRequestContext, expect } from \"@playwright/test\"\nimport { Session } from \"@ory/kratos-client\"\n\nexport async function hasSession(\n  r: APIRequestContext,\n  kratosPublicURL: string,\n): Promise<void> {\n  const resp = await r.get(kratosPublicURL + \"/sessions/whoami\", {\n    failOnStatusCode: true,\n  })\n  const session = await resp.json()\n  expect(session).toBeDefined()\n  expect(session.active).toBe(true)\n}\n\nexport async function getSession(\n  r: APIRequestContext,\n  kratosPublicURL: string,\n): Promise<Session> {\n  const resp = await r.get(kratosPublicURL + \"/sessions/whoami\", {\n    failOnStatusCode: true,\n  })\n  return resp.json()\n}\n\nexport async function hasNoSession(\n  r: APIRequestContext,\n  kratosPublicURL: string,\n): Promise<void> {\n  const resp = await r.get(kratosPublicURL + \"/sessions/whoami\", {\n    failOnStatusCode: false,\n  })\n  expect(resp.status()).toBe(401)\n  return resp.json()\n}\n"
  },
  {
    "path": "test/e2e/playwright/actions/webhook.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { request } from \"@playwright/test\"\nimport retry from \"promise-retry\"\nimport { retryOptions } from \"../lib/config\"\n\nexport const WEBHOOK_TARGET = \"http://127.0.0.1:4471\"\n\nconst baseUrl = WEBHOOK_TARGET\n\n/**\n * Fetches a documented (hopefully) created by web hook\n *\n * @param key\n */\nexport async function fetchDocument(key: string) {\n  const r = await request.newContext()\n\n  return retry(async (retry) => {\n    const res = await r.get(documentUrl(key))\n    if (res.status() !== 200) {\n      const body = await res.text()\n      const message = `Expected response code 200 but received ${res.status()}: ${body}`\n      return retry(message)\n    }\n    return await res.json()\n  }, retryOptions)\n}\n\n/**\n * Fetches a documented (hopefully) created by web hook\n *\n * @param key\n */\nexport async function deleteDocument(key: string) {\n  const r = await request.newContext()\n\n  return retry(async (retry) => {\n    const res = await r.delete(documentUrl(key))\n    if (res.status() !== 204) {\n      const body = await res.text()\n      const message = `Expected response code 204 but received ${res.status()}: ${body}`\n      return retry(message)\n    }\n    return\n  }, retryOptions)\n}\n\n/**\n * Returns the URL for a specific document\n *\n * @param key\n */\nexport function documentUrl(key: string) {\n  return `${baseUrl}/documents/${key}`\n}\n"
  },
  {
    "path": "test/e2e/playwright/fixtures/index.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Identity } from \"@ory/kratos-client\"\nimport {\n  APIRequestContext,\n  CDPSession,\n  expect as baseExpect,\n  Page,\n  test as base,\n} from \"@playwright/test\"\nimport { writeFile } from \"fs/promises\"\nimport { merge } from \"lodash\"\nimport { OryKratosConfiguration } from \"../../shared/config\"\nimport { default_config } from \"../setup/default_config\"\nimport { APIResponse } from \"playwright-core\"\nimport { SessionWithResponse } from \"../types\"\nimport { retryOptions } from \"../lib/request\"\nimport promiseRetry from \"promise-retry\"\nimport { Protocol } from \"playwright-core/types/protocol\"\nimport {\n  createIdentityWithEmail,\n  createIdentityWithPassword,\n} from \"../actions/identity\"\nimport { randomBytes } from \"crypto\"\n\n// from https://stackoverflow.com/questions/61132262/typescript-deep-partial\ntype DeepPartial<T> = T extends object\n  ? {\n      [P in keyof T]?: DeepPartial<T[P]>\n    }\n  : T\n\ntype TestFixtures = {\n  identity: { oryIdentity: Identity; email: string; password: string }\n  identityWithoutPassword: { oryIdentity: Identity; email: string }\n  configOverride: DeepPartial<OryKratosConfiguration>\n  config: OryKratosConfiguration\n  virtualAuthenticatorOptions: Partial<Protocol.WebAuthn.VirtualAuthenticatorOptions>\n  pageCDPSession: CDPSession\n  virtualAuthenticator: Protocol.WebAuthn.addVirtualAuthenticatorReturnValue\n}\n\ntype WorkerFixtures = {\n  kratosAdminURL: string\n  kratosPublicURL: string\n  mode:\n    | \"reconfigure_kratos\"\n    | \"reconfigure_ory_network_project\"\n    | \"existing_kratos\"\n    | \"existing_ory_network_project\"\n}\n\nexport const test = base.extend<TestFixtures, WorkerFixtures>({\n  configOverride: {},\n  config: [\n    async ({ request, configOverride }, use) => {\n      const configToWrite = merge(default_config, configOverride)\n\n      const revision = randomBytes(16).toString(\"hex\")\n      const fileDirectory = __dirname + \"/../..\"\n      await writeFile(\n        fileDirectory + \"/playwright/kratos.config.json\",\n        JSON.stringify(\n          {\n            ...configToWrite,\n            // Forces a new hash, even if the config was not changed.\n            revision,\n          },\n          null,\n          2,\n        ),\n      )\n\n      await expect(async () => {\n        const resp = await request.get(\n          \"http://localhost:4434/admin/health/config\",\n        )\n        const updatedRevision = (await resp.body()).toString()\n        expect(updatedRevision).toBe(revision)\n      }).toPass()\n\n      await use(configToWrite)\n    },\n    { auto: true },\n  ],\n  virtualAuthenticatorOptions: undefined,\n  pageCDPSession: async ({ page }, use) => {\n    const cdpSession = await page.context().newCDPSession(page)\n    await use(cdpSession)\n    await cdpSession.detach()\n  },\n  virtualAuthenticator: async (\n    { pageCDPSession, virtualAuthenticatorOptions },\n    use,\n  ) => {\n    await pageCDPSession.send(\"WebAuthn.enable\")\n    const { authenticatorId } = await pageCDPSession.send(\n      \"WebAuthn.addVirtualAuthenticator\",\n      {\n        options: {\n          protocol: \"ctap2\",\n          transport: \"internal\",\n          hasResidentKey: true,\n          hasUserVerification: true,\n          isUserVerified: true,\n          ...virtualAuthenticatorOptions,\n        },\n      },\n    )\n    await use({ authenticatorId })\n    await pageCDPSession.send(\"WebAuthn.removeVirtualAuthenticator\", {\n      authenticatorId,\n    })\n\n    await pageCDPSession.send(\"WebAuthn.disable\")\n  },\n  identity: async ({ request }, use, i) => {\n    const {\n      identity: oryIdentity,\n      password,\n      email,\n    } = await createIdentityWithPassword(request)\n    i.attach(\"identity\", {\n      body: JSON.stringify(oryIdentity, null, 2),\n      contentType: \"application/json\",\n    })\n    await use({\n      oryIdentity,\n      email,\n      password,\n    })\n  },\n  identityWithoutPassword: async ({ request }, use, i) => {\n    const { identity: oryIdentity, email } =\n      await createIdentityWithEmail(request)\n    i.attach(\"identity\", {\n      body: JSON.stringify(oryIdentity, null, 2),\n      contentType: \"application/json\",\n    })\n    await use({\n      oryIdentity,\n      email,\n    })\n  },\n  kratosAdminURL: [\"http://localhost:4434\", { option: true, scope: \"worker\" }],\n  kratosPublicURL: [\"http://localhost:4433\", { option: true, scope: \"worker\" }],\n})\n\nexport const expect = baseExpect.extend({\n  toHaveSession,\n  toMatchResponseData,\n})\n\nasync function toHaveSession(\n  requestOrPage: APIRequestContext | Page,\n  baseUrl: string,\n) {\n  let r: APIRequestContext\n  if (\"request\" in requestOrPage) {\n    r = requestOrPage.request\n  } else {\n    r = requestOrPage\n  }\n  let pass = true\n\n  let responseData: string\n  let response: APIResponse = null\n  try {\n    const result = await promiseRetry(\n      () =>\n        r\n          .get(baseUrl + \"/sessions/whoami\", {\n            failOnStatusCode: false,\n          })\n          .then<SessionWithResponse>(\n            async (res: APIResponse): Promise<SessionWithResponse> => {\n              return {\n                session: await res.json(),\n                response: res,\n              }\n            },\n          ),\n      retryOptions,\n    )\n    pass = !!result.session.active\n    responseData = await result.response.text()\n    response = result.response\n  } catch (e) {\n    pass = false\n    responseData = JSON.stringify(e.message, undefined, 2)\n  }\n\n  const message = () =>\n    this.utils.matcherHint(\"toHaveSession\", undefined, undefined, {\n      isNot: this.isNot,\n    }) +\n    `\\n\n    \\n\n    Expected: ${this.isNot ? \"not\" : \"\"} to have session\\n\n    Session data received: ${responseData}\\n\n    Headers: ${JSON.stringify(response?.headers(), null, 2)}\\n\n    `\n\n  return {\n    message,\n    pass,\n    name: \"toHaveSession\",\n  }\n}\n\nasync function toMatchResponseData(\n  res: APIResponse,\n  options: {\n    statusCode?: number\n    failureHint?: string\n  },\n) {\n  const body = await res.text()\n  const statusCode = options.statusCode ?? 200\n  const failureHint = options.failureHint ?? \"\"\n  const message = () =>\n    this.utils.matcherHint(\"toMatch\", undefined, undefined, {\n      isNot: this.isNot,\n    }) +\n    `\\n\n    ${failureHint}\n    \\n\n    Expected: ${this.isNot ? \"not\" : \"\"} to match\\n\n    Status Code: ${statusCode}\\n\n    Body: ${body}\\n\n    Headers: ${JSON.stringify(res.headers(), null, 2)}\\n\n    URL: ${JSON.stringify(res.url(), null, 2)}\\n\n    `\n\n  return {\n    message,\n    pass: res.status() === statusCode,\n    name: \"toMatch\",\n  }\n}\n"
  },
  {
    "path": "test/e2e/playwright/fixtures/schemas/sms.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nexport default {\n  $id: \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  $schema: \"http://json-schema.org/draft-07/schema#\",\n  title: \"Person\",\n  type: \"object\",\n  properties: {\n    traits: {\n      type: \"object\",\n      properties: {\n        phone: {\n          type: \"string\",\n          format: \"tel\",\n          title: \"Your Phone Number\",\n          minLength: 3,\n          \"ory.sh/kratos\": {\n            credentials: {\n              code: {\n                identifier: true,\n                via: \"sms\",\n              },\n            },\n            verification: {\n              via: \"sms\",\n            },\n          },\n        },\n      },\n      required: [\"phone\"],\n      additionalProperties: false,\n    },\n  },\n}\n"
  },
  {
    "path": "test/e2e/playwright/kratos.base-config.json",
    "content": "{\n  \"identity\": {\n    \"schemas\": [\n      {\n        \"id\": \"default\",\n        \"url\": \"file://test/e2e/profiles/oidc/identity.traits.schema.json\"\n      },\n      {\n        \"id\": \"email\",\n        \"url\": \"file://test/e2e/profiles/email/identity.traits.schema.json\"\n      }\n    ]\n  },\n  \"serve\": {\n    \"public\": {\n      \"base_url\": \"http://localhost:4455/\",\n      \"cors\": {\n        \"enabled\": true,\n        \"allowed_origins\": [\"http://localhost:3000\", \"http://localhost:19006\"],\n        \"allowed_headers\": [\"Authorization\", \"Content-Type\", \"X-Session-Token\"]\n      }\n    }\n  },\n  \"log\": {\n    \"level\": \"trace\",\n    \"leak_sensitive_values\": true\n  },\n  \"secrets\": {\n    \"cookie\": [\"PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\"],\n    \"cipher\": [\"secret-thirty-two-character-long\"]\n  },\n  \"selfservice\": {\n    \"default_browser_return_url\": \"http://localhost:4455/\",\n    \"allowed_return_urls\": [\n      \"http://localhost:4455\",\n      \"http://localhost:19006\",\n      \"https://www.ory.sh/\",\n      \"https://example.org/\",\n      \"https://www.example.org/\",\n      \"exp://example.com/my-app\",\n      \"https://example.com/my-app\"\n    ],\n    \"methods\": {\n      \"link\": {\n        \"config\": {\n          \"lifespan\": \"1h\"\n        }\n      },\n      \"code\": {\n        \"config\": {\n          \"lifespan\": \"1h\"\n        }\n      },\n      \"oidc\": {\n        \"enabled\": true,\n        \"config\": {\n          \"providers\": [\n            {\n              \"id\": \"hydra\",\n              \"label\": \"Ory\",\n              \"provider\": \"generic\",\n              \"client_id\": \"client_id\",\n              \"client_secret\": \"client_secret\",\n              \"issuer_url\": \"http://localhost:4444/\",\n              \"scope\": [\"offline\"],\n              \"mapper_url\": \"file://test/e2e/profiles/oidc/hydra.jsonnet\"\n            }\n          ]\n        }\n      }\n    },\n    \"flows\": {\n      \"settings\": {\n        \"privileged_session_max_age\": \"5m\",\n        \"ui_url\": \"http://localhost:4455/settings\"\n      },\n      \"logout\": {\n        \"after\": {\n          \"default_browser_return_url\": \"http://localhost:4455/login\"\n        }\n      },\n      \"registration\": {\n        \"ui_url\": \"http://localhost:4455/registration\",\n        \"after\": {\n          \"password\": {\n            \"hooks\": [\n              {\n                \"hook\": \"session\"\n              }\n            ]\n          },\n          \"oidc\": {\n            \"hooks\": [\n              {\n                \"hook\": \"session\"\n              }\n            ]\n          }\n        }\n      },\n      \"login\": {\n        \"ui_url\": \"http://localhost:4455/login\"\n      },\n      \"error\": {\n        \"ui_url\": \"http://localhost:4455/error\"\n      },\n      \"verification\": {\n        \"ui_url\": \"http://localhost:4455/verify\"\n      },\n      \"recovery\": {\n        \"ui_url\": \"http://localhost:4455/recovery\"\n      }\n    }\n  },\n  \"courier\": {\n    \"smtp\": {\n      \"connection_uri\": \"smtps://test:test@localhost:8026/?skip_ssl_verify=true\"\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/playwright/lib/config.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { OperationOptions } from \"retry\"\n\nexport type RetryOptions = OperationOptions\n\nexport const retryOptions: RetryOptions = {\n  retries: 20,\n  factor: 1,\n  maxTimeout: 500,\n  minTimeout: 250,\n  randomize: false,\n}\n"
  },
  {
    "path": "test/e2e/playwright/lib/helper.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Message } from \"mailhog\"\nimport {\n  UiContainer,\n  UiNodeAttributes,\n  UiNodeInputAttributes,\n} from \"@ory/kratos-client\"\nimport { expect } from \"../fixtures\"\nimport { LoginFlowStyle, OryKratosConfiguration } from \"../../shared/config\"\n\nexport const codeRegex = /(\\d{6})/\n\n/**\n * Extracts the recovery or verification code from a mail\n *\n * @param mail the mail to extract the code from\n * @returns the code or null if no code was found\n */\nexport function extractCode(mail: Message) {\n  const result = codeRegex.exec(mail.html || mail.text)\n  if (result != null && result.length > 0) {\n    return result[0]\n  }\n  return null\n}\n\nexport function findCsrfToken(ui: UiContainer) {\n  const csrf = ui.nodes\n    .filter((node) => isUiNodeInputAttributes(node.attributes))\n    // Since we filter all non-input attributes, the following as is ok:\n    .map(\n      (node): UiNodeInputAttributes => node.attributes as UiNodeInputAttributes,\n    )\n    .find(({ name }) => name === \"csrf_token\")?.value\n  expect(csrf).toBeDefined()\n  return csrf\n}\n\nexport function isUiNodeInputAttributes(\n  attrs: UiNodeAttributes,\n): attrs is UiNodeInputAttributes & {\n  node_type: \"input\"\n} {\n  return attrs.node_type === \"input\"\n}\n\nexport const toConfig = ({\n  style = \"identifier_first\",\n  mitigateEnumeration = false,\n  selfservice,\n}: {\n  style?: LoginFlowStyle\n  mitigateEnumeration?: boolean\n  selfservice?: Partial<OryKratosConfiguration[\"selfservice\"]>\n}) => ({\n  selfservice: {\n    default_browser_return_url: \"http://localhost:4455/welcome\",\n    ...selfservice,\n    flows: {\n      login: {\n        ...selfservice?.flows?.login,\n        style,\n      },\n      ...selfservice?.flows,\n    },\n  },\n  security: {\n    account_enumeration: {\n      mitigate: mitigateEnumeration,\n    },\n  },\n})\n"
  },
  {
    "path": "test/e2e/playwright/lib/request.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APIResponse } from \"playwright-core\"\nimport { expect } from \"../fixtures\"\nimport { OperationOptions } from \"retry\"\n\nexport type RetryOptions = OperationOptions\n\nexport const retryOptions: RetryOptions = {\n  retries: 20,\n  factor: 1,\n  maxTimeout: 500,\n  minTimeout: 250,\n  randomize: false,\n}\n\nexport async function expectJSONResponse<T>(\n  res: APIResponse,\n  { statusCode = 200, message }: { statusCode?: number; message?: string } = {},\n): Promise<T> {\n  await expect(res).toMatchResponseData({\n    statusCode,\n    failureHint: message,\n  })\n  try {\n    return (await res.json()) as T\n  } catch (e) {\n    const body = await res.text()\n    throw Error(\n      `Expected to be able to parse body as json: ${e} (body: ${body})`,\n    )\n  }\n}\n"
  },
  {
    "path": "test/e2e/playwright/models/elements/login.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { expect, Locator, Page } from \"@playwright/test\"\nimport { createInputLocator, InputLocator } from \"../../selectors/input\"\nimport { URLSearchParams } from \"node:url\"\nimport { OryKratosConfiguration } from \"../../../shared/config\"\n\nenum LoginStyle {\n  IdentifierFirst = \"identifier_first\",\n  Unified = \"unified\",\n}\n\ntype SubmitOptions = {\n  submitWithKeyboard?: boolean\n  waitForURL?: string | RegExp\n}\n\nexport class LoginPage {\n  public submitPassword: Locator\n  public github: Locator\n  public google: Locator\n  public signup: Locator\n\n  public identifier: InputLocator\n  public password: InputLocator\n  public totpInput: InputLocator\n  public totpSubmit: Locator\n  public lookupInput: InputLocator\n  public lookupSubmit: Locator\n  public codeSubmit = this.page.locator(\n    'button[type=\"submit\"][name=\"method\"][value=\"code\"]',\n  )\n  public codeSubmitMfa = this.page.locator(\n    'button[type=\"submit\"][name=\"address\"]',\n  )\n  public codeInput = createInputLocator(this.page, \"code\")\n\n  public alert: Locator\n\n  constructor(\n    readonly page: Page,\n    readonly config: OryKratosConfiguration,\n  ) {\n    this.identifier = createInputLocator(page, \"identifier\")\n    this.password = createInputLocator(page, \"password\")\n    this.totpInput = createInputLocator(page, \"totp_code\")\n    this.lookupInput = createInputLocator(page, \"lookup_secret\")\n\n    this.submitPassword = page.locator(\n      '[type=\"submit\"][name=\"method\"][value=\"password\"]',\n    )\n\n    this.github = page.locator('[name=\"provider\"][value=\"github\"]')\n    this.google = page.locator('[name=\"provider\"][value=\"google\"]')\n\n    this.totpSubmit = page.locator('[name=\"method\"][value=\"totp\"]')\n    this.lookupSubmit = page.locator('[name=\"method\"][value=\"lookup_secret\"]')\n\n    this.signup = page.locator('[data-testid=\"signup-link\"]')\n\n    // this.submitHydra = page.locator('[name=\"provider\"][value=\"hydra\"]')\n    // this.forgotPasswordLink = page.locator(\n    //   \"[data-testid='forgot-password-link']\",\n    // )\n    // this.logoutLink = page.locator(\"[data-testid='logout-link']\")\n  }\n\n  async submitIdentifierFirst(identifier: string) {\n    await this.inputField(\"identifier\").fill(identifier)\n    await this.submit(\"identifier_first\", {\n      waitForURL: new RegExp(this.config.selfservice.flows.login.ui_url),\n    })\n  }\n\n  async loginWithPassword(\n    identifier: string,\n    password: string,\n    opts?: SubmitOptions,\n  ) {\n    switch (this.config.selfservice.flows.login.style) {\n      case LoginStyle.IdentifierFirst:\n        await this.submitIdentifierFirst(identifier)\n        break\n      case LoginStyle.Unified:\n        await this.inputField(\"identifier\").fill(identifier)\n        break\n    }\n\n    await this.inputField(\"password\").fill(password)\n    await this.submit(\"password\", opts)\n  }\n\n  async triggerLoginWithCode(identifier: string, opts?: SubmitOptions) {\n    switch (this.config.selfservice.flows.login.style) {\n      case LoginStyle.IdentifierFirst:\n        await this.submitIdentifierFirst(identifier)\n        const enabled = [\n          this.config.selfservice.methods.passkey?.enabled,\n          this.config.selfservice.methods.password?.enabled,\n          this.config.selfservice.methods.code?.passwordless_enabled,\n          this.config.selfservice.methods.webauthn?.enabled &&\n            this.config.selfservice.methods.webauthn?.config?.passwordless,\n          this.config.selfservice.methods.oidc?.enabled &&\n            this.config.selfservice.methods.oidc.config.providers.length > 0,\n        ].filter((e) => e)\n\n        if (enabled.length > 1) {\n          await this.codeSubmit.click()\n        }\n        break\n      case LoginStyle.Unified:\n        await this.inputField(\"identifier\").fill(identifier)\n        await this.codeSubmit.click()\n        break\n    }\n  }\n\n  async open({\n    aal,\n    refresh,\n  }: {\n    aal?: string\n    refresh?: boolean\n  } = {}) {\n    const p = new URLSearchParams()\n    if (refresh) {\n      p.append(\"refresh\", \"true\")\n    }\n\n    if (aal) {\n      p.append(\"aal\", aal)\n    }\n\n    await Promise.all([\n      this.page.goto(\n        this.config.selfservice.flows.login.ui_url + \"?\" + p.toString(),\n      ),\n      this.isReady(),\n      this.page.waitForURL((url) =>\n        url.toString().includes(this.config.selfservice.flows.login.ui_url),\n      ),\n    ])\n    await this.isReady()\n  }\n\n  async isReady() {\n    await expect(this.inputField(\"csrf_token\").nth(0)).toBeHidden()\n  }\n\n  submitMethod(method: string) {\n    switch (method) {\n      case \"google\":\n      case \"github\":\n      case \"hydra\":\n        return this.page.locator(`[name=\"provider\"][value=\"${method}\"]`)\n    }\n    return this.page.locator(`[name=\"method\"][value=\"${method}\"]`)\n  }\n\n  inputField(name: string) {\n    return this.page.locator(`input[name=${name}]`)\n  }\n\n  async submit(method: string, opts?: SubmitOptions) {\n    const waitFor = [\n      opts?.waitForURL\n        ? this.page.waitForURL(opts.waitForURL)\n        : Promise.resolve(),\n    ]\n\n    if (opts?.submitWithKeyboard) {\n      waitFor.push(this.page.keyboard.press(\"Enter\"))\n    } else {\n      waitFor.push(this.submitMethod(method).click())\n    }\n\n    await Promise.all(waitFor)\n  }\n\n  //\n  // async submitPasswordForm(\n  //   id: string,\n  //   password: string,\n  //   expectURL: string | RegExp,\n  //   options: {\n  //     submitWithKeyboard?: boolean\n  //     style?: LoginStyle\n  //   } = {\n  //     submitWithKeyboard: false,\n  //     style: LoginStyle.OneStep,\n  //   },\n  // ) {\n  //   await this.isReady()\n  //   await this.inputField(\"identifier\").fill(id)\n  //\n  //   if (options.style === LoginStyle.IdentifierFirst) {\n  //     await this.submitMethod(\"identifier_first\").click()\n  //     await this.inputField(\"password\").fill(password)\n  //   } else {\n  //     await this.inputField(\"password\").fill(password)\n  //   }\n  //\n  //   const nav = this.page.waitForURL(expectURL)\n  //\n  //   if (submitWithKeyboard) {\n  //     await this.page.keyboard.press(\"Enter\")\n  //   } else {\n  //     await this.submitPassword.click()\n  //   }\n  //\n  //   await nav\n  // }\n  //\n  // readonly baseURL: string\n  // readonly submitHydra: Locator\n  // readonly forgotPasswordLink: Locator\n  // readonly logoutLink: Locator\n  //\n  // async goto(returnTo?: string, refresh?: boolean) {\n  //   const u = new URL(routes.hosted.login(this.baseURL))\n  //   if (returnTo) {\n  //     u.searchParams.append(\"return_to\", returnTo)\n  //   }\n  //   if (refresh) {\n  //     u.searchParams.append(\"refresh\", refresh.toString())\n  //   }\n  //   await this.page.goto(u.toString())\n  //   await this.isReady()\n  // }\n  //\n  // async loginWithHydra(email: string, password: string) {\n  //   await this.submitHydra.click()\n  //   await this.page.waitForURL(new RegExp(OIDC_PROVIDER))\n  //\n  //   await this.page.locator(\"input[name=email]\").fill(email)\n  //   await this.page.locator(\"input[name=password]\").fill(password)\n  //\n  //   await this.page.locator(\"input[name=submit][id=accept]\").click()\n  // }\n  //\n  // async loginWithOIDC(email = generateEmail(), password = generatePassword()) {\n  //   await this.page.fill('[name=\"email\"]', email)\n  //   await this.page.fill('[name=\"password\"]', password)\n  //   await this.page.click(\"#accept\")\n  // }\n  //\n  // async loginAndAcceptConsent(\n  //   email = generateEmail(),\n  //   password = generatePassword(),\n  //   {rememberConsent = true, rememberLogin = false} = {},\n  // ) {\n  //   await this.page.fill('[name=\"email\"]', email)\n  //   await this.page.fill('[name=\"password\"]', password)\n  //   rememberLogin && (await this.page.check('[name=\"remember\"]'))\n  //   await this.page.click(\"#accept\")\n  //\n  //   await this.page.click(\"#offline\")\n  //   await this.page.click(\"#openid\")\n  //   rememberConsent && (await this.page.check(\"[name=remember]\"))\n  //   await this.page.click(\"#accept\")\n  //\n  //   return email\n  // }\n  //\n  // async expectAlert(id: string) {\n  //   await this.page.getByTestId(`ui/message/${id}`).waitFor()\n  // }\n}\n"
  },
  {
    "path": "test/e2e/playwright/models/elements/registration.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { expect, Page } from \"@playwright/test\"\nimport { createInputLocator, InputLocator } from \"../../selectors/input\"\nimport { OryKratosConfiguration } from \"../../../shared/config\"\n\nexport class RegistrationPage {\n  public identifier: InputLocator\n\n  constructor(\n    readonly page: Page,\n    readonly config: OryKratosConfiguration,\n  ) {\n    this.identifier = createInputLocator(page, \"identifier\")\n  }\n\n  async open() {\n    await Promise.all([\n      this.page.goto(this.config.selfservice.flows.registration.ui_url),\n      this.isReady(),\n      this.page.waitForURL((url) =>\n        url\n          .toString()\n          .includes(this.config.selfservice.flows.registration.ui_url),\n      ),\n    ])\n    await this.isReady()\n  }\n\n  inputField(name: string) {\n    return this.page.locator(`input[name=\"${name}\"]`)\n  }\n\n  submitField(name: string) {\n    return this.page.locator(`[type=\"submit\"][name=\"method\"][value=\"${name}\"]`)\n  }\n\n  async isReady() {\n    await expect(this.inputField(\"csrf_token\").nth(0)).toBeHidden()\n  }\n\n  async triggerRegistrationWithCode(identifier: string) {\n    await this.inputField(\"traits.phone\").fill(identifier)\n    await this.submitField(\"profile\").click()\n    await this.submitField(\"code\").click()\n  }\n}\n"
  },
  {
    "path": "test/e2e/playwright/models/elements/settings.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { expect, Page } from \"@playwright/test\"\nimport { TOTP } from \"otpauth\"\nimport { OryKratosConfiguration } from \"../../../shared/config\"\n\nexport class SettingsPage {\n  constructor(\n    readonly page: Page,\n    readonly config: OryKratosConfiguration,\n  ) {}\n\n  async isReady() {\n    for (const csrfInput of await this.page\n      .locator(`input[name=\"csrf_token\"]`)\n      .all()) {\n      await expect(csrfInput).toHaveValue(/.+/)\n    }\n  }\n\n  async open() {\n    await this.page.goto(this.config.selfservice.flows.settings.ui_url)\n    await this.isReady()\n  }\n\n  async setupTotp() {\n    const totpSecret = await this.page\n      .getByTestId(\"node/text/totp_secret_key/text\")\n      .locator(\"code\")\n      .textContent()\n    await expect(totpSecret).toMatch(/^[A-Z2-7]{32}$/)\n    const totpCode = () => {\n      return new TOTP({ secret: totpSecret }).generate()\n    }\n    await this.page.fill(\"input[name=totp_code]\", totpCode())\n    await this.page.locator(\"button[name=method][value=totp]\").click()\n\n    return { totpCode }\n  }\n}\n"
  },
  {
    "path": "test/e2e/playwright/selectors/input.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Locator, Page } from \"@playwright/test\"\n\nexport interface InputLocator {\n  input: Locator\n  message: Locator\n  label: Locator\n}\n\nexport const createInputLocator = (page: Page, field: string): InputLocator => {\n  const prefix = `[data-testid=\"node/input/${field}\"]`\n  return {\n    input: page.locator(`${prefix} input`),\n    label: page.locator(`${prefix} label`),\n    message: page.locator(`${prefix} p`),\n  }\n}\n"
  },
  {
    "path": "test/e2e/playwright/setup/default_config.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { OryKratosConfiguration } from \"../../shared/config\"\n\nexport const default_config: OryKratosConfiguration = {\n  dsn: \"\",\n  identity: {\n    schemas: [\n      {\n        id: \"default\",\n        url: \"file://test/e2e/profiles/oidc/identity.traits.schema.json\",\n      },\n      {\n        id: \"email\",\n        url: \"file://test/e2e/profiles/email/identity.traits.schema.json\",\n      },\n    ],\n  },\n  serve: {\n    public: {\n      base_url: \"http://localhost:4455/\",\n      cors: {\n        enabled: true,\n        allowed_origins: [\"http://localhost:3000\", \"http://localhost:19006\"],\n        allowed_headers: [\n          \"Authorization\",\n          \"Content-Type\",\n          \"Max-Age\",\n          \"X-Session-Token\",\n          \"X-XSRF-TOKEN\",\n          \"X-CSRF-TOKEN\",\n        ],\n      },\n    },\n  },\n\n  log: {\n    level: \"trace\",\n    leak_sensitive_values: true,\n  },\n  secrets: {\n    cookie: [\"PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\"],\n    cipher: [\"secret-thirty-two-character-long\"],\n  },\n  selfservice: {\n    default_browser_return_url: \"http://localhost:4455/welcome\",\n    allowed_return_urls: [\n      \"http://localhost:4455\",\n      \"http://localhost:19006\",\n      \"https://www.ory.sh/\",\n      \"https://example.org/\",\n      \"https://www.example.org/\",\n      \"exp://example.com/my-app\",\n      \"https://example.com/my-app\",\n    ],\n    methods: {\n      link: {\n        config: {\n          lifespan: \"1h\",\n        },\n      },\n      code: {\n        config: {\n          lifespan: \"1h\",\n        },\n      },\n      oidc: {\n        enabled: true,\n        config: {\n          providers: [\n            {\n              id: \"hydra\",\n              label: \"Ory\",\n              provider: \"generic\",\n              client_id: process.env[\"OIDC_HYDRA_CLIENT_ID\"],\n              client_secret: process.env[\"OIDC_HYDRA_CLIENT_SECRET\"],\n              issuer_url: \"http://localhost:4444/\",\n              scope: [\"offline\"],\n              mapper_url: \"file://test/e2e/profiles/oidc/hydra.jsonnet\",\n            },\n          ],\n        },\n      },\n    },\n\n    flows: {\n      settings: {\n        privileged_session_max_age: \"5m\",\n        ui_url: \"http://localhost:4455/settings\",\n      },\n      logout: {\n        after: {\n          default_browser_return_url: \"http://localhost:4455/login\",\n        },\n      },\n      registration: {\n        ui_url: \"http://localhost:4455/registration\",\n        after: {\n          password: {\n            hooks: [\n              {\n                hook: \"session\",\n              },\n            ],\n          },\n          oidc: {\n            hooks: [\n              {\n                hook: \"session\",\n              },\n            ],\n          },\n        },\n      },\n      login: {\n        ui_url: \"http://localhost:4455/login\",\n      },\n      error: {\n        ui_url: \"http://localhost:4455/error\",\n      },\n      verification: {\n        ui_url: \"http://localhost:4455/verify\",\n      },\n      recovery: {\n        enabled: true,\n        ui_url: \"http://localhost:4455/recovery\",\n      },\n    },\n  },\n\n  courier: {\n    smtp: {\n      connection_uri: \"smtp://localhost:8026/?disable_starttls=true\",\n    },\n  },\n}\n"
  },
  {
    "path": "test/e2e/playwright/tests/desktop/code/sms.spec.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { test } from \"../../../fixtures\"\nimport { toConfig } from \"../../../lib/helper\"\nimport smsSchema from \"../../../fixtures/schemas/sms\"\nimport { LoginPage } from \"../../../models/elements/login\"\nimport { hasSession } from \"../../../actions/session\"\nimport { createIdentityWithPhoneNumber } from \"../../../actions/identity\"\nimport {\n  deleteDocument,\n  documentUrl,\n  fetchDocument,\n} from \"../../../actions/webhook\"\nimport { RegistrationPage } from \"../../../models/elements/registration\"\nimport { CountryNames, generatePhoneNumber } from \"phone-number-generator-js\"\n\nconst documentId = \"doc-\" + Math.random().toString(36).substring(7)\n\ntest.describe(\"account enumeration protection off\", () => {\n  test.use({\n    configOverride: {\n      security: {\n        account_enumeration: {\n          enabled: false,\n        },\n      },\n      selfservice: {\n        flows: {\n          login: {\n            style: \"unified\",\n          },\n          registration: {\n            after: {\n              code: {\n                hooks: [\n                  {\n                    hook: \"session\",\n                  },\n                ],\n              },\n            },\n          },\n        },\n        methods: {\n          code: {\n            passwordless_enabled: true,\n          },\n          password: {\n            enabled: false,\n          },\n        },\n      },\n      courier: {\n        channels: [\n          {\n            id: \"sms\",\n            type: \"http\",\n            request_config: {\n              body: \"base64://ZnVuY3Rpb24oY3R4KSB7DQpjdHg6IGN0eCwNCn0=\",\n              method: \"PUT\",\n              url: documentUrl(documentId),\n            },\n          },\n        ],\n      },\n      identity: {\n        default_schema_id: \"sms\",\n        schemas: [\n          {\n            id: \"sms\",\n            url:\n              \"base64://\" +\n              Buffer.from(JSON.stringify(smsSchema), \"ascii\").toString(\n                \"base64\",\n              ),\n          },\n        ],\n      },\n    },\n  })\n\n  test.afterEach(async () => {\n    await deleteDocument(documentId)\n  })\n\n  test(\"login succeeds\", async ({ page, config, kratosPublicURL }) => {\n    const identity = await createIdentityWithPhoneNumber(page.request)\n\n    const login = new LoginPage(page, config)\n    await login.open()\n    await login.triggerLoginWithCode(identity.phone)\n\n    const result = await fetchDocument(documentId)\n    await login.codeInput.input.fill(result.ctx.template_data.login_code)\n    await login.codeSubmit.getByText(\"Continue\").click()\n    await hasSession(page.request, kratosPublicURL)\n  })\n\n  test(\"registration succeeds\", async ({ page, config, kratosPublicURL }) => {\n    const phone = generatePhoneNumber({\n      countryName: CountryNames.Germany,\n      withoutCountryCode: false,\n    })\n\n    const registration = new RegistrationPage(page, config)\n    await registration.open()\n    await registration.triggerRegistrationWithCode(phone)\n\n    const result = await fetchDocument(documentId)\n    const code = result.ctx.template_data.registration_code\n    await registration.inputField(\"code\").fill(code)\n    await registration.submitField(\"code\").getByText(\"Continue\").click()\n    await hasSession(page.request, kratosPublicURL)\n  })\n})\n"
  },
  {
    "path": "test/e2e/playwright/tests/desktop/identifier_first/code.login.spec.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { expect } from \"@playwright/test\"\nimport { search } from \"../../../actions/mail\"\nimport { getSession, hasNoSession, hasSession } from \"../../../actions/session\"\nimport { test } from \"../../../fixtures\"\nimport { extractCode, toConfig } from \"../../../lib/helper\"\nimport { LoginPage } from \"../../../models/elements/login\"\nimport { SettingsPage } from \"../../../models/elements/settings\"\nimport { logoutUrl } from \"../../../actions/login\"\n\ntest.describe(\"account enumeration protection off\", () => {\n  test.use({\n    configOverride: toConfig({\n      style: \"identifier_first\",\n      mitigateEnumeration: false,\n      selfservice: {\n        methods: {\n          code: {\n            passwordless_enabled: true,\n          },\n        },\n      },\n    }),\n  })\n\n  test(\"login fails because user does not exist\", async ({ page, config }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.submitIdentifierFirst(\"i@donot.exist\")\n\n    await expect(\n      page.locator('[data-testid=\"ui/message/4000037\"]'),\n      \"expect account not exist message to be shown\",\n    ).toBeVisible()\n  })\n\n  test(\"login with wrong code fails\", async ({\n    page,\n    identity,\n    kratosPublicURL,\n    config,\n  }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.triggerLoginWithCode(identity.email)\n\n    await login.codeInput.input.fill(\"123123\")\n\n    await login.codeSubmit.getByText(\"Continue\").click()\n\n    await hasNoSession(page.request, kratosPublicURL)\n    await expect(\n      page.locator('[data-testid=\"ui/message/4010008\"]'),\n      \"expect to be shown a wrong code error\",\n    ).toBeVisible()\n  })\n\n  test(\"login succeeds\", async ({\n    page,\n    identity,\n    config,\n    kratosPublicURL,\n  }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.triggerLoginWithCode(identity.email)\n\n    const mails = await search({ query: identity.email, kind: \"to\" })\n    expect(mails).toHaveLength(1)\n\n    const code = extractCode(mails[0])\n\n    await login.codeInput.input.fill(code)\n\n    await login.codeSubmit.getByText(\"Continue\").click()\n\n    await hasSession(page.request, kratosPublicURL)\n  })\n})\n\ntest.describe(\"account enumeration protection on\", () => {\n  test.use({\n    configOverride: toConfig({\n      style: \"identifier_first\",\n      mitigateEnumeration: true,\n      selfservice: {\n        methods: {\n          password: {\n            enabled: false,\n          },\n          code: {\n            passwordless_enabled: true,\n          },\n        },\n      },\n    }),\n  })\n\n  test(\"login fails because user does not exist\", async ({ page, config }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.submitIdentifierFirst(\"i@donot.exist\")\n\n    await expect(\n      page.locator('button[name=\"method\"][value=\"code\"]'),\n      \"expect to show the code form\",\n    ).toBeVisible()\n  })\n\n  test(\"login with wrong code fails\", async ({\n    page,\n    identity,\n    kratosPublicURL,\n    config,\n  }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.triggerLoginWithCode(identity.email)\n\n    await login.codeInput.input.fill(\"123123\")\n\n    await login.codeSubmit.getByText(\"Continue\").click()\n\n    await hasNoSession(page.request, kratosPublicURL)\n    await expect(\n      page.locator('[data-testid=\"ui/message/4010008\"]'),\n      \"expect to be shown a wrong code error\",\n    ).toBeVisible()\n  })\n\n  test(\"login succeeds\", async ({\n    page,\n    identity,\n    config,\n    kratosPublicURL,\n  }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.triggerLoginWithCode(identity.email)\n\n    const mails = await search({ query: identity.email, kind: \"to\" })\n    expect(mails).toHaveLength(1)\n\n    const code = extractCode(mails[0])\n\n    await login.codeInput.input.fill(code)\n\n    await login.codeSubmit.getByText(\"Continue\").click()\n\n    await hasSession(page.request, kratosPublicURL)\n  })\n})\n\nfor (const tc of [\n  {\n    name: \"do fast login when only code method enabled and configured, mitigation off\",\n    methodsEnabled: [\"code\"],\n    methodsConfigured: \"code\",\n    mitigation: false,\n    expectFastLogin: true,\n  },\n  {\n    name: \"do fast login when only code method enabled and configured, mitigation on\",\n    methodsEnabled: [\"code\"],\n    methodsConfigured: \"code\",\n    mitigation: true,\n    expectFastLogin: true,\n  },\n  {\n    name: \"do not fast login when multiple methods enabled, all configured, mitigation off\",\n    methodsEnabled: [\"password\", \"code\"],\n    methodsConfigured: \"all\",\n    mitigation: false,\n    expectFastLogin: false,\n  },\n  {\n    name: \"do not fast login when multiple methods enabled, all configured, mitigation on\",\n    methodsEnabled: [\"password\", \"code\"],\n    methodsConfigured: \"all\",\n    mitigation: true,\n    expectFastLogin: false,\n  },\n  {\n    name: \"do fast login when multiple methods enabled, only code configured, mitigation off\",\n    methodsEnabled: [\"password\", \"code\"],\n    methodsConfigured: \"code\",\n    mitigation: false,\n    expectFastLogin: true,\n  },\n  {\n    name: \"do not fast login when multiple methods enabled, only code configured, mitigation on\",\n    methodsEnabled: [\"password\", \"code\"],\n    methodsConfigured: \"code\",\n    mitigation: true,\n    expectFastLogin: false,\n  },\n]) {\n  test.describe(`account enumeration protection ${\n    tc.mitigation ? \"on\" : \"off\"\n  }`, () => {\n    test.use({\n      configOverride: toConfig({\n        style: \"identifier_first\",\n        mitigateEnumeration: tc.mitigation,\n        selfservice: {\n          methods: {\n            oidc: {\n              enabled: false,\n            },\n            password: {\n              enabled: tc.methodsEnabled.includes(\"password\"),\n            },\n            code: {\n              passwordless_enabled: tc.methodsEnabled.includes(\"code\"),\n            },\n          },\n        },\n      }),\n    })\n\n    test(\n      tc.name,\n      async ({ page, config, identity, identityWithoutPassword }) => {\n        const id =\n          tc.methodsConfigured === \"all\" ? identity : identityWithoutPassword\n\n        const login = new LoginPage(page, config)\n        await login.open()\n\n        await login.submitIdentifierFirst(id.email)\n\n        if (tc.expectFastLogin) {\n          await expect(login.submitPassword).toBeHidden()\n          await expect(\n            page.locator('[data-testid=\"ui/message/1010014\"]'),\n            \"expect code sent message to be shown\",\n          ).toBeVisible()\n          await expect(login.codeSubmit).toBeVisible()\n        } else {\n          await expect(login.submitPassword).toBeVisible()\n          await expect(login.codeSubmit).toBeVisible()\n        }\n      },\n    )\n  })\n}\n\ntest.describe(() => {\n  test.use({\n    configOverride: toConfig({\n      style: \"identifier_first\",\n      mitigateEnumeration: false,\n      selfservice: {\n        methods: {\n          password: {\n            enabled: false,\n          },\n          code: {\n            passwordless_enabled: true,\n          },\n        },\n      },\n    }),\n  })\n  test(\"refresh\", async ({ page, identity, config, kratosPublicURL }) => {\n    const login = new LoginPage(page, config)\n\n    const [initialSession, initialCode] =\n      await test.step(\"initial login\", async () => {\n        await login.open()\n        await login.triggerLoginWithCode(identity.email)\n\n        const mails = await search({ query: identity.email, kind: \"to\" })\n        expect(mails).toHaveLength(1)\n\n        const code = extractCode(mails[0])\n\n        await login.codeInput.input.fill(code)\n\n        await login.codeSubmit.getByText(\"Continue\").click()\n\n        const session = await getSession(page.request, kratosPublicURL)\n        expect(session).toBeDefined()\n        expect(session.active).toBe(true)\n        return [session, code]\n      })\n\n    await login.open({\n      refresh: true,\n    })\n    await login.inputField(\"identifier\").fill(identity.email)\n    await login.submit(\"code\")\n\n    const mails = await search({\n      query: identity.email,\n      kind: \"to\",\n      filter: (m) => !m.html.includes(initialCode),\n    })\n    expect(mails).toHaveLength(1)\n\n    const code = extractCode(mails[0])\n\n    await login.codeInput.input.fill(code)\n\n    await login.codeSubmit.getByText(\"Continue\").click()\n    await page.waitForURL(\n      new RegExp(config.selfservice.default_browser_return_url),\n    )\n\n    const newSession = await getSession(page.request, kratosPublicURL)\n    expect(newSession).toBeDefined()\n    expect(newSession.active).toBe(true)\n\n    const initDate = Date.parse(initialSession.authenticated_at)\n    const newDate = Date.parse(newSession.authenticated_at)\n    expect(newDate).toBeGreaterThanOrEqual(initDate)\n  })\n})\n\ntest.describe(\"second factor\", () => {\n  for (const tc of [\n    {\n      name: \"do fast login when only code method enabled and configured\",\n      methodsEnabled: [\"code\"],\n      methodsConfigured: \"code\",\n      expectFastLogin: true,\n    },\n    {\n      name: \"do not fast login when multiple methods enabled, all configured\",\n      methodsEnabled: [\"totp\", \"code\"],\n      methodsConfigured: \"all\",\n      expectFastLogin: false,\n    },\n    {\n      name: \"do fast login when multiple methods enabled, only code configured\",\n      methodsEnabled: [\"totp\", \"code\"],\n      methodsConfigured: \"code\",\n      expectFastLogin: true,\n    },\n  ]) {\n    test.describe(`code mfa ${\n      tc.expectFastLogin ? \"with\" : \"without\"\n    } fast login`, () => {\n      test.use({\n        configOverride: toConfig({\n          style: \"identifier_first\",\n          selfservice: {\n            methods: {\n              oidc: {\n                enabled: false,\n              },\n              password: {\n                enabled: true,\n              },\n              totp: {\n                enabled: tc.methodsEnabled.includes(\"totp\"),\n              },\n              code: {\n                passwordless_enabled: false,\n                mfa_enabled: tc.methodsEnabled.includes(\"code\"),\n              },\n            },\n          },\n        }),\n      })\n\n      test(tc.name, async ({ page, config, identity, kratosPublicURL }) => {\n        const login = new LoginPage(page, config)\n\n        if (tc.methodsConfigured === \"all\") {\n          await test.step(\"setup TOTP\", async () => {\n            await login.open()\n            await login.loginWithPassword(identity.email, identity.password)\n\n            const mails = await search({ query: identity.email, kind: \"to\" })\n            expect(mails).toHaveLength(1)\n            const code = extractCode(mails[0])\n            await login.codeInput.input.fill(code)\n            await login.codeSubmit.getByText(\"Continue\").click()\n\n            const settings = new SettingsPage(page, config)\n            await settings.open()\n\n            await settings.setupTotp()\n\n            await page.goto(await logoutUrl(page.request, kratosPublicURL))\n          })\n        }\n\n        test.step(\"first factor authentication\", async () => {\n          await login.open()\n          await login.loginWithPassword(identity.email, identity.password)\n        })\n\n        if (tc.expectFastLogin) {\n          await expect(login.totpInput.input).toBeHidden()\n          await expect(\n            page.locator('[data-testid=\"ui/message/1010014\"]'),\n            \"expect code sent message to be shown\",\n          ).toBeVisible()\n          await expect(login.codeSubmit).toBeVisible()\n        } else {\n          await expect(login.totpInput.input).toBeVisible()\n          await expect(login.codeSubmitMfa).toBeVisible()\n        }\n      })\n    })\n  }\n})\n"
  },
  {
    "path": "test/e2e/playwright/tests/desktop/identifier_first/oidc.login.spec.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { faker } from \"@faker-js/faker\"\nimport { expect, Page } from \"@playwright/test\"\nimport { getSession, hasSession } from \"../../../actions/session\"\nimport { test } from \"../../../fixtures\"\nimport { toConfig } from \"../../../lib/helper\"\nimport { LoginPage } from \"../../../models/elements/login\"\nimport { OryKratosConfiguration } from \"../../../../shared/config\"\n\nasync function loginHydra(page: Page) {\n  return test.step(\"login with hydra\", async () => {\n    await page\n      .locator(\"input[name=username]\")\n      .fill(faker.internet.email({ provider: \"ory.sh\" }))\n    await page.locator(\"button[name=action][value=accept]\").click()\n    await page.locator(\"#offline\").check()\n    await page.locator(\"#openid\").check()\n\n    await page.locator(\"input[name=website]\").fill(faker.internet.url())\n\n    await page.locator(\"button[name=action][value=accept]\").click()\n  })\n}\n\nasync function registerWithHydra(\n  page: Page,\n  config: OryKratosConfiguration,\n  kratosPublicURL: string,\n) {\n  return await test.step(\"register\", async () => {\n    await page.goto(\"/registration\")\n\n    await page.locator(`button[name=provider][value=hydra]`).click()\n\n    const email = faker.internet.email({ provider: \"ory.sh\" })\n    await page.locator(\"input[name=username]\").fill(email)\n    await page.locator(\"#remember\").check()\n    await page.locator(\"button[name=action][value=accept]\").click()\n    await page.locator(\"#offline\").check()\n    await page.locator(\"#openid\").check()\n\n    await page.locator(\"input[name=website]\").fill(faker.internet.url())\n\n    await page.locator(\"button[name=action][value=accept]\").click()\n    await page.waitForURL(\n      new RegExp(config.selfservice.default_browser_return_url),\n    )\n    await page.context().clearCookies({\n      domain: new URL(kratosPublicURL).hostname,\n    })\n\n    await expect(\n      getSession(page.request, kratosPublicURL),\n    ).rejects.toThrowError()\n    return email\n  })\n}\n\nfor (const mitigateEnumeration of [true, false]) {\n  test.describe(`account enumeration protection ${\n    mitigateEnumeration ? \"on\" : \"off\"\n  }`, () => {\n    test.use({\n      configOverride: toConfig({\n        mitigateEnumeration,\n        selfservice: {\n          methods: {\n            password: {\n              enabled: true,\n            },\n          },\n        },\n      }),\n    })\n\n    test(\"login\", async ({ page, config, kratosPublicURL }) => {\n      const login = new LoginPage(page, config)\n      await login.open()\n\n      await page.locator(`button[name=provider][value=hydra]`).click()\n\n      await loginHydra(page)\n\n      await page.waitForURL(\n        new RegExp(config.selfservice.default_browser_return_url),\n      )\n\n      await hasSession(page.request, kratosPublicURL)\n    })\n\n    test(\"oidc sign in on second step\", async ({\n      page,\n      config,\n      kratosPublicURL,\n    }) => {\n      const email = await registerWithHydra(page, config, kratosPublicURL)\n\n      const login = new LoginPage(page, config)\n      await login.open()\n\n      await login.submitIdentifierFirst(email)\n\n      // If account enumeration is mitigated, we should see the password method,\n      // because the identity has not set up a password\n      await expect(\n        page.locator('button[name=\"method\"][value=\"password\"]'),\n        \"hide the password method\",\n      ).toBeVisible({ visible: mitigateEnumeration })\n\n      await page.locator(`button[name=provider][value=hydra]`).click()\n\n      await loginHydra(page)\n\n      await page.waitForURL(\n        new RegExp(config.selfservice.default_browser_return_url),\n      )\n\n      const session = await getSession(page.request, kratosPublicURL)\n      expect(session).toBeDefined()\n      expect(session.active).toBe(true)\n    })\n  })\n}\n\ntest(\"login with refresh\", async ({ page, config, kratosPublicURL }) => {\n  await registerWithHydra(page, config, kratosPublicURL)\n\n  const login = new LoginPage(page, config)\n\n  const initialSession = await test.step(\"initial login\", async () => {\n    await login.open()\n    await page.locator(`button[name=provider][value=hydra]`).click()\n\n    await loginHydra(page)\n\n    await page.waitForURL(\n      new RegExp(config.selfservice.default_browser_return_url),\n    )\n    return await getSession(page.request, kratosPublicURL)\n  })\n\n  // This is required, because OIDC issues a new session on refresh (TODO), and MySQL does not store sub second timestamps, so we need to wait a bit\n  await page.waitForTimeout(1000)\n  await test.step(\"refresh login\", async () => {\n    await login.open({\n      refresh: true,\n    })\n\n    await expect(\n      page.locator('[data-testid=\"ui/message/1010003\"]'),\n      \"show the refresh message\",\n    ).toBeVisible()\n\n    await page.locator(`button[name=provider][value=hydra]`).click()\n\n    await loginHydra(page)\n\n    await page.waitForURL(\n      new RegExp(config.selfservice.default_browser_return_url),\n    )\n    const newSession = await getSession(page.request, kratosPublicURL)\n    // expect(newSession.authentication_methods).toHaveLength(\n    //   initialSession.authentication_methods.length + 1,\n    // )\n    expect(newSession.authenticated_at).not.toBe(\n      initialSession.authenticated_at,\n    )\n  })\n})\n"
  },
  {
    "path": "test/e2e/playwright/tests/desktop/identifier_first/passkeys.login.spec.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { faker } from \"@faker-js/faker\"\nimport { CDPSession, expect, Page } from \"@playwright/test\"\nimport { OryKratosConfiguration } from \"../../../../shared/config\"\nimport { getSession } from \"../../../actions/session\"\nimport { test } from \"../../../fixtures\"\nimport { toConfig } from \"../../../lib/helper\"\nimport { LoginPage } from \"../../../models/elements/login\"\n\nasync function toggleAutomaticPresenceSimulation(\n  cdpSession: CDPSession,\n  authenticatorId: string,\n  enabled: boolean,\n) {\n  await cdpSession.send(\"WebAuthn.setAutomaticPresenceSimulation\", {\n    authenticatorId,\n    enabled,\n  })\n}\n\nasync function registerWithPasskey(\n  page: Page,\n  pageCDPSession: CDPSession,\n  config: OryKratosConfiguration,\n  authenticatorId: string,\n  simulatePresence: boolean,\n) {\n  return await test.step(\"create webauthn identity\", async () => {\n    await page.goto(\"/registration\")\n    const identifier = faker.internet.email()\n    await page.locator(`input[name=\"traits.email\"]`).fill(identifier)\n    await page\n      .locator(`input[name=\"traits.website\"]`)\n      .fill(faker.internet.url())\n    await page.locator(\"button[name=method][value=profile]\").click()\n\n    await toggleAutomaticPresenceSimulation(\n      pageCDPSession,\n      authenticatorId,\n      true,\n    )\n    await page.locator(\"button[name=passkey_register_trigger]\").click()\n\n    await toggleAutomaticPresenceSimulation(\n      pageCDPSession,\n      authenticatorId,\n      simulatePresence,\n    )\n\n    await page.waitForURL(\n      new RegExp(config.selfservice.default_browser_return_url),\n    )\n    return identifier\n  })\n}\n\nconst passkeyConfig = {\n  methods: {\n    passkey: {\n      enabled: true,\n      config: {\n        rp: {\n          display_name: \"ORY\",\n          id: \"localhost\",\n          origins: [\"http://localhost:4455\"],\n        },\n      },\n    },\n  },\n}\n\nfor (const mitigateEnumeration of [true, false]) {\n  test.describe(`account enumeration protection ${\n    mitigateEnumeration ? \"on\" : \"off\"\n  }`, () => {\n    test.use({\n      configOverride: toConfig({\n        mitigateEnumeration,\n        style: \"identifier_first\",\n        selfservice: passkeyConfig,\n      }),\n    })\n\n    for (const simulatePresence of [true, false]) {\n      test.describe(`${\n        simulatePresence ? \"with\" : \"without\"\n      } automatic presence proof`, () => {\n        test.use({\n          virtualAuthenticatorOptions: {\n            automaticPresenceSimulation: simulatePresence,\n            // hasResidentKey: simulatePresence,\n          },\n        })\n        test(\"login\", async ({\n          config,\n          page,\n          kratosPublicURL,\n          virtualAuthenticator,\n          pageCDPSession,\n        }) => {\n          const identifier = await registerWithPasskey(\n            page,\n            pageCDPSession,\n            config,\n            virtualAuthenticator.authenticatorId,\n            simulatePresence,\n          )\n          await page.context().clearCookies({})\n\n          const login = new LoginPage(page, config)\n          await login.open()\n\n          if (!simulatePresence) {\n            await login.submitIdentifierFirst(identifier)\n\n            const passkeyLoginTrigger = page.locator(\n              \"button[name=passkey_login_trigger]\",\n            )\n            await passkeyLoginTrigger.waitFor()\n\n            await page.waitForLoadState(\"load\")\n\n            await toggleAutomaticPresenceSimulation(\n              pageCDPSession,\n              virtualAuthenticator.authenticatorId,\n              true,\n            )\n\n            await passkeyLoginTrigger.click()\n\n            await toggleAutomaticPresenceSimulation(\n              pageCDPSession,\n              virtualAuthenticator.authenticatorId,\n              false,\n            )\n          }\n\n          await page.waitForURL(\n            new RegExp(config.selfservice.default_browser_return_url),\n          )\n\n          await expect(\n            getSession(page.request, kratosPublicURL),\n          ).resolves.toMatchObject({\n            active: true,\n            identity: {\n              traits: {\n                email: identifier,\n              },\n            },\n          })\n        })\n      })\n    }\n  })\n}\n\ntest.describe(\"without automatic presence simulation\", () => {\n  test.use({\n    virtualAuthenticatorOptions: {\n      automaticPresenceSimulation: false,\n    },\n    configOverride: toConfig({\n      selfservice: passkeyConfig,\n    }),\n  })\n  test(\"login with refresh\", async ({\n    page,\n    config,\n    kratosPublicURL,\n    pageCDPSession,\n    virtualAuthenticator,\n  }) => {\n    const identifier = await registerWithPasskey(\n      page,\n      pageCDPSession,\n      config,\n      virtualAuthenticator.authenticatorId,\n      true,\n    )\n\n    const login = new LoginPage(page, config)\n    // Due to resetting automatic presence simulating to \"true\" in the previous step,\n    // opening the login page automatically triggers the passkey login\n    await login.open()\n\n    await page.waitForURL(\n      new RegExp(config.selfservice.default_browser_return_url),\n    )\n\n    await expect(\n      getSession(page.request, kratosPublicURL),\n    ).resolves.toMatchObject({\n      active: true,\n      identity: {\n        traits: {\n          email: identifier,\n        },\n      },\n    })\n\n    await login.open({\n      refresh: true,\n    })\n\n    await expect(\n      page.locator('[data-testid=\"ui/message/1010003\"]'),\n      \"show the refresh message\",\n    ).toBeVisible()\n\n    const initialSession = await getSession(page.request, kratosPublicURL)\n\n    const passkeyLoginTrigger = page.locator(\n      \"button[name=passkey_login_trigger]\",\n    )\n    await passkeyLoginTrigger.waitFor()\n\n    await page.waitForLoadState(\"load\")\n\n    await toggleAutomaticPresenceSimulation(\n      pageCDPSession,\n      virtualAuthenticator.authenticatorId,\n      true,\n    )\n\n    await passkeyLoginTrigger.click()\n    await page.waitForURL(\n      new RegExp(config.selfservice.default_browser_return_url),\n    )\n    const newSession = await getSession(page.request, kratosPublicURL)\n\n    expect(newSession.authentication_methods).toHaveLength(\n      initialSession.authentication_methods.length + 1,\n    )\n  })\n})\n"
  },
  {
    "path": "test/e2e/playwright/tests/desktop/identifier_first/password.login.spec.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { expect } from \"@playwright/test\"\nimport { loginWithPassword } from \"../../../actions/login\"\nimport { getSession, hasNoSession, hasSession } from \"../../../actions/session\"\nimport { test } from \"../../../fixtures\"\nimport { toConfig } from \"../../../lib/helper\"\nimport { LoginPage } from \"../../../models/elements/login\"\n\n// These can run in parallel because they use the same config.\ntest.describe(\"account enumeration protection off\", () => {\n  test.use({\n    configOverride: toConfig({\n      style: \"identifier_first\",\n      mitigateEnumeration: false,\n      selfservice: {\n        methods: {\n          password: {\n            enabled: true,\n          },\n          code: {\n            passwordless_enabled: false,\n          },\n        },\n      },\n    }),\n  })\n\n  test(\"login fails because user does not exist\", async ({ page, config }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.submitIdentifierFirst(\"i@donot.exist\")\n\n    await expect(\n      page.locator('[data-testid=\"ui/message/4000037\"]'),\n      \"expect account not exist message to be shown\",\n    ).toBeVisible()\n  })\n\n  test(\"login with wrong password fails\", async ({\n    page,\n    identity,\n    kratosPublicURL,\n    config,\n  }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.loginWithPassword(identity.email, \"wrong-password\")\n    await login.isReady()\n\n    await hasNoSession(page.request, kratosPublicURL)\n    await expect(\n      page.locator('[data-testid=\"ui/message/4000006\"]'),\n      \"expect to be shown a credentials do not exist error\",\n    ).toBeVisible()\n  })\n\n  test(\"login succeeds\", async ({\n    page,\n    identity,\n    config,\n    kratosPublicURL,\n  }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.inputField(\"identifier\").fill(identity.email)\n    await login.submit(\"identifier_first\", {\n      waitForURL: new RegExp(config.selfservice.flows.login.ui_url),\n    })\n\n    await login.inputField(\"password\").fill(identity.password)\n    await login.submit(\"password\", {\n      waitForURL: new RegExp(config.selfservice.default_browser_return_url),\n    })\n\n    await hasSession(page.request, kratosPublicURL)\n  })\n\n  test(\"login with refresh\", async ({\n    page,\n    config,\n    identity,\n    kratosPublicURL,\n  }) => {\n    await loginWithPassword(\n      {\n        password: identity.password,\n        traits: {\n          email: identity.email,\n        },\n      },\n      page.request,\n      kratosPublicURL,\n    )\n\n    const login = new LoginPage(page, config)\n    await login.open({\n      refresh: true,\n    })\n\n    await expect(\n      page.locator('[data-testid=\"ui/message/1010003\"]'),\n      \"show the refresh message\",\n    ).toBeVisible()\n\n    const initialSession = await getSession(page.request, kratosPublicURL)\n    await login.inputField(\"password\").fill(identity.password)\n    await login.submit(\"password\", {\n      waitForURL: new RegExp(config.selfservice.default_browser_return_url),\n    })\n\n    const newSession = await getSession(page.request, kratosPublicURL)\n\n    expect(newSession.authentication_methods).toHaveLength(\n      initialSession.authentication_methods.length + 1,\n    )\n  })\n})\n\ntest.describe(\"account enumeration protection on\", () => {\n  test.use({\n    configOverride: toConfig({\n      style: \"identifier_first\",\n      mitigateEnumeration: true,\n      selfservice: {\n        methods: {\n          password: {\n            enabled: true,\n          },\n          code: {\n            passwordless_enabled: false,\n          },\n        },\n      },\n    }),\n  })\n\n  test(\"login fails because user does not exist\", async ({ page, config }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.submitIdentifierFirst(\"i@donot.exist\")\n\n    await expect(\n      page.locator('button[name=\"method\"][value=\"password\"]'),\n      \"expect to show the password form\",\n    ).toBeVisible()\n  })\n\n  test(\"login with wrong password fails\", async ({\n    page,\n    identity,\n    kratosPublicURL,\n    config,\n  }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.loginWithPassword(identity.email, \"wrong-password\")\n    await login.isReady()\n\n    await hasNoSession(page.request, kratosPublicURL)\n    await expect(\n      page.locator('[data-testid=\"ui/message/4000006\"]'),\n      \"expect to be shown a credentials do not exist error\",\n    ).toBeVisible()\n  })\n\n  test(\"login succeeds\", async ({\n    page,\n    // projectFrontendClient,\n    identity,\n    config,\n    kratosPublicURL,\n  }) => {\n    const login = new LoginPage(page, config)\n    await login.open()\n\n    await login.inputField(\"identifier\").fill(identity.email)\n    await login.submit(\"identifier_first\", {\n      waitForURL: new RegExp(config.selfservice.flows.login.ui_url),\n    })\n\n    await login.inputField(\"password\").fill(identity.password)\n    await login.submit(\"password\", {\n      waitForURL: new RegExp(config.selfservice.default_browser_return_url),\n    })\n\n    await hasSession(page.request, kratosPublicURL)\n  })\n\n  test(\"login with refresh\", async ({\n    page,\n    config,\n    identity,\n    kratosPublicURL,\n  }) => {\n    await loginWithPassword(\n      {\n        password: identity.password,\n        traits: {\n          email: identity.email,\n        },\n      },\n      page.request,\n      kratosPublicURL,\n    )\n\n    const login = new LoginPage(page, config)\n    await login.open({\n      refresh: true,\n    })\n\n    await expect(\n      page.locator('[data-testid=\"ui/message/1010003\"]'),\n      \"show the refresh message\",\n    ).toBeVisible()\n\n    const initialSession = await getSession(page.request, kratosPublicURL)\n\n    await login.inputField(\"password\").fill(identity.password)\n    await login.submit(\"password\", {\n      waitForURL: new RegExp(config.selfservice.default_browser_return_url),\n    })\n\n    const newSession = await getSession(page.request, kratosPublicURL)\n\n    expect(newSession.authentication_methods).toHaveLength(\n      initialSession.authentication_methods.length + 1,\n    )\n  })\n})\n"
  },
  {
    "path": "test/e2e/playwright/tests/desktop/profile_first/everything.registration.spec.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { expect } from \"@playwright/test\"\nimport { test } from \"../../../fixtures\"\nimport { toConfig } from \"../../../lib/helper\"\nimport { RegistrationPage } from \"../../../models/elements/registration\"\nimport {\n  OryKratosConfiguration,\n  RegistrationFlowStyle,\n  RegistrationNodeGroup,\n} from \"../../../../shared/config\"\n\nconst selfservice: Partial<OryKratosConfiguration[\"selfservice\"]> = {\n  methods: {\n    code: {\n      passwordless_enabled: true,\n    },\n    password: {\n      enabled: true,\n    },\n    webauthn: {\n      enabled: true,\n      config: {\n        passwordless: true,\n        rp: { id: \"localhost\", display_name: \"Ory Kratos\" },\n      },\n    },\n    passkey: {\n      enabled: true,\n      config: {\n        rp: { id: \"localhost\", display_name: \"Ory Kratos\" },\n      },\n    },\n    totp: {\n      enabled: true,\n    },\n    lookup_secret: {\n      enabled: true,\n    },\n    oidc: {\n      enabled: true,\n      config: {\n        providers: [\n          {\n            id: \"github\",\n            provider: \"github\",\n            label: \"GitHub\",\n            client_id: \"1\",\n            client_secret: \"1\",\n            mapper_url: \"base64://\",\n          },\n          {\n            id: \"google\",\n            provider: \"google\",\n            label: \"Google\",\n            client_id: \"1\",\n            client_secret: \"1\",\n            mapper_url: \"base64://e30=\",\n          },\n        ],\n      },\n    },\n  },\n}\n\ntest.describe(\"profile_first strategy with all methods enabled\", () => {\n  ;[\"default\", \"password\"].forEach((group: RegistrationNodeGroup) => {\n    test.describe(`password group behavior is ${group}`, () => {\n      ;[\"profile_first\", \"unified\"].forEach((style: RegistrationFlowStyle) => {\n        test.describe(`registration with ${style} enabled`, () => {\n          ;[\n            [\"password\"],\n            [\"password\", \"webauthn\"],\n            [\"password\", \"code\"],\n            [\"password\", \"code\", \"webauthn\"],\n            [\"password\", \"code\", \"passkey\"],\n            [\"password\", \"code\", \"passkey\", \"webauthn\"],\n          ].forEach((methods) => {\n            test.describe(`methods ${methods.join(\", \")} enabled`, () => {\n              test.use({\n                configOverride: {\n                  ...toConfig({\n                    style: \"identifier_first\",\n                    mitigateEnumeration: false,\n                    selfservice: {\n                      ...selfservice,\n                      methods: {\n                        password: {\n                          enabled: methods.includes(\"password\"),\n                        },\n                        webauthn: {\n                          enabled: methods.includes(\"webauthn\"),\n                          config: {\n                            passwordless: true,\n                            rp: { id: \"localhost\", display_name: \"Ory Kratos\" },\n                          },\n                        },\n                        passkey: {\n                          enabled: methods.includes(\"passkey\"),\n                          config: {\n                            rp: { id: \"localhost\", display_name: \"Ory Kratos\" },\n                          },\n                        },\n                        code: {\n                          enabled: methods.includes(\"code\"),\n                          passwordless_enabled: methods.includes(\"code\"),\n                        },\n                        totp: {\n                          enabled: false,\n                        },\n                        lookup_secret: {\n                          enabled: false,\n                        },\n                        oidc: {\n                          enabled: false,\n                        },\n                      },\n                      flows: {\n                        registration: { style },\n                      },\n                    },\n                  }),\n                  feature_flags: {\n                    password_profile_registration_node_group: group,\n                  },\n                },\n              })\n              test(\"registration does not have any duplicated fields when using profile first\", async ({\n                page,\n                config,\n              }) => {\n                const registration = new RegistrationPage(page, config)\n                await registration.open()\n\n                await expect(\n                  page.locator('[name=\"traits.email\"]'),\n                  \"expect the profile form fields to not be duplicated\",\n                ).toHaveCount(style === \"profile_first\" ? 1 : methods.length)\n              })\n            })\n          })\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/playwright/tests/mobile/app_login.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { expect, Page } from \"@playwright/test\"\nimport { test } from \"../../fixtures\"\n\nasync function performOidcLogin(popup: Page, username: string) {\n  await popup.waitForLoadState()\n\n  await popup.locator(\"#username\").fill(username)\n  await popup.getByRole(\"button\", { name: \"login\" }).click()\n\n  await popup.locator(\"#offline\").click()\n  await popup.locator(\"#openid\").click()\n  await popup.locator(\"#website\").fill(\"https://example.com\")\n  await popup.getByRole(\"button\", { name: \"login\" }).click()\n}\n\nasync function rejectOidcLogin(popup: Page) {\n  await popup.waitForLoadState()\n  await popup.getByRole(\"button\", { name: \"reject\" }).click()\n  await popup.close()\n}\n\nasync function testRegistrationOrLogin(page: Page, username: string) {\n  const popupPromise = page.waitForEvent(\"popup\")\n  await page.getByText(/sign (up|in) with ory/i).click()\n  const popup = await popupPromise\n\n  await performOidcLogin(popup, username)\n\n  await page.waitForURL(\"Home\")\n  expect(popup.isClosed()).toBeTruthy()\n  await expect(page.getByText(\"Welcome back\")).toBeVisible()\n}\n\nasync function logout(page: Page) {\n  return page.getByTestId(\"logout\").click()\n}\n\nasync function testRegistration(page: Page, username: string) {\n  await page.goto(\"Registration\")\n  return testRegistrationOrLogin(page, username)\n}\n\nasync function testLogin(page: Page, username: string) {\n  await page.goto(\"Login\")\n  return testRegistrationOrLogin(page, username)\n}\n\ntest.describe(\"Registration\", () => {\n  test(\"register twice\", async ({ page }) => {\n    await testRegistration(page, \"registration@example.com\")\n    await logout(page)\n    await testRegistration(page, \"registration@example.com\")\n  })\n\n  test(\"register, then login\", async ({ page }) => {\n    await testRegistration(page, \"registration-login@example.com\")\n    await logout(page)\n    await testLogin(page, \"registration-login@example.com\")\n  })\n\n  test(\"register, cancel, register\", async ({ page }) => {\n    await page.goto(\"Registration\")\n\n    let popupPromise = page.waitForEvent(\"popup\")\n    await page.getByText(/sign (up|in) with ory/i).click()\n    let popup = await popupPromise\n\n    await rejectOidcLogin(popup)\n\n    await expect(page.getByText(\"login rejected request\")).toBeVisible()\n    popupPromise = page.waitForEvent(\"popup\")\n    await page.getByText(/continue/i).click()\n    popup = await popupPromise\n    await performOidcLogin(popup, \"register-reject-then-accept@example.com\")\n\n    await page.waitForURL(\"Home\")\n    expect(popup.isClosed()).toBeTruthy()\n    await expect(page.getByText(\"Welcome back\")).toBeVisible()\n  })\n})\n\ntest.describe(\"Login\", () => {\n  test(\"login twice\", async ({ page }) => {\n    await testLogin(page, \"login@example.com\")\n    await logout(page)\n    await testLogin(page, \"login@example.com\")\n  })\n\n  test(\"login, then register\", async ({ page }) => {\n    await testLogin(page, \"login-registration@example.com\")\n    await logout(page)\n    await testRegistration(page, \"login-registration@example.com\")\n  })\n\n  test(\"login, cancel, login\", async ({ page }) => {\n    await page.goto(\"Login\")\n\n    let popupPromise = page.waitForEvent(\"popup\")\n    await page.getByText(/sign (up|in) with ory/i).click()\n    let popup = await popupPromise\n\n    await rejectOidcLogin(popup)\n\n    await expect(page.getByText(\"login rejected request\")).toBeVisible()\n    popupPromise = page.waitForEvent(\"popup\")\n    await page.getByText(/sign in with ory/i).click()\n    popup = await popupPromise\n    await performOidcLogin(popup, \"login-reject-then-accept@example.com\")\n\n    await page.waitForURL(\"Home\")\n    expect(popup.isClosed()).toBeTruthy()\n    await expect(page.getByText(\"Welcome back\")).toBeVisible()\n  })\n})\n"
  },
  {
    "path": "test/e2e/playwright/tests/mobile/app_recovery.spec.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { expect } from \"@playwright/test\"\nimport { test } from \"../../fixtures\"\nimport { search } from \"../../actions/mail\"\nimport { extractCode } from \"../../lib/helper\"\n\nconst schemaConfig = {\n  default_schema_id: \"email\",\n  schemas: [\n    {\n      id: \"email\",\n      url: \"file://test/e2e/profiles/email/identity.traits.schema.json\",\n    },\n  ],\n}\n\ntest.describe(\"Recovery\", () => {\n  test.use({\n    configOverride: {\n      identity: {\n        ...schemaConfig,\n      },\n      feature_flags: {\n        use_continue_with_transitions: true,\n      },\n    },\n  })\n\n  test(\"succeeds with a valid email address\", async ({ page, identity }) => {\n    await page.goto(\"/Recovery\")\n\n    await page.getByTestId(\"email\").fill(identity.email)\n    await page.getByTestId(\"submit-form\").click()\n    await expect(page.getByTestId(\"ui/message/1060003\")).toBeVisible()\n\n    const mails = await search({ query: identity.email, kind: \"to\" })\n    expect(mails).toHaveLength(1)\n\n    const code = extractCode(mails[0])\n    const wrongCode = \"0\" + code\n\n    await test.step(\"enter wrong code\", async () => {\n      await page.getByTestId(\"code\").fill(wrongCode)\n      await page.getByText(\"Continue\").click()\n      await expect(page.getByTestId(\"ui/message/4060006\")).toBeVisible()\n    })\n\n    await test.step(\"enter correct code\", async () => {\n      await page.getByTestId(\"code\").fill(code)\n      await page.getByText(\"Continue\").click()\n      await page.waitForURL(/Settings/)\n      await expect(page.getByTestId(\"ui/message/1060001\").first()).toBeVisible()\n    })\n  })\n\n  test(\"wrong email address does not get sent\", async ({ page, identity }) => {\n    await page.goto(\"/Recovery\")\n\n    const wrongEmailAddress = \"wrong-\" + identity.email\n    await page.getByTestId(\"email\").fill(wrongEmailAddress)\n    await page.getByTestId(\"submit-form\").click()\n    await expect(page.getByTestId(\"ui/message/1060003\")).toBeVisible()\n\n    try {\n      await search({ query: identity.email, kind: \"to\" })\n      expect(false).toBeTruthy()\n    } catch (e) {\n      // this is expected\n    }\n  })\n\n  test(\"fails with an invalid code\", async ({ page, identity }) => {\n    await page.goto(\"/Recovery\")\n\n    await page.getByTestId(\"email\").fill(identity.email)\n    await page.getByTestId(\"submit-form\").click()\n    await page.getByTestId(\"ui/message/1060003\").isVisible()\n\n    const mails = await search({ query: identity.email, kind: \"to\" })\n    expect(mails).toHaveLength(1)\n\n    const code = extractCode(mails[0])\n    const wrongCode = \"0\" + code\n\n    await test.step(\"enter wrong repeatedly\", async () => {\n      for (let i = 0; i < 10; i++) {\n        await page.getByTestId(\"code\").fill(wrongCode)\n        await page.getByText(\"Continue\", { exact: true }).click()\n        await expect(page.getByTestId(\"ui/message/4060006\")).toBeVisible()\n      }\n    })\n\n    await test.step(\"enter correct code fails\", async () => {\n      await page.getByTestId(\"code\").fill(code)\n      await page.getByText(\"Continue\", { exact: true }).click()\n      await expect(page.getByTestId(\"ui/message/4060006\")).toBeVisible()\n    })\n  })\n\n  test.describe(\"with short code expiration\", () => {\n    test.use({\n      configOverride: {\n        identity: {\n          ...schemaConfig,\n        },\n        selfservice: {\n          methods: {\n            code: {\n              config: {\n                lifespan: \"1ms\",\n              },\n            },\n          },\n        },\n        feature_flags: {\n          use_continue_with_transitions: true,\n        },\n      },\n    })\n\n    test(\"fails with an expired code\", async ({ page, identity }) => {\n      await page.goto(\"/Recovery\")\n\n      await page.getByTestId(\"email\").fill(identity.email)\n      await page.getByTestId(\"submit-form\").click()\n      await page.getByTestId(\"ui/message/1060003\").isVisible()\n\n      const mails = await search({ query: identity.email, kind: \"to\" })\n      expect(mails).toHaveLength(1)\n\n      const code = extractCode(mails[0])\n\n      await page.getByTestId(\"code\").fill(code)\n      await page.getByText(\"Continue\", { exact: true }).click()\n      await expect(page.getByTestId(\"ui/message/4060006\")).toBeVisible()\n    })\n  })\n})\n"
  },
  {
    "path": "test/e2e/playwright/types/index.ts",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { APIResponse } from \"playwright-core\"\nimport { Session } from \"@ory/kratos-client\"\n\nexport type SessionWithResponse = {\n  session: Session\n  response: APIResponse\n}\n"
  },
  {
    "path": "test/e2e/playwright.config.ts",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { defineConfig, devices } from \"@playwright/test\"\nimport * as dotenv from \"dotenv\"\n\ndotenv.config({ path: __dirname + \"/playwright/playwright.env\" })\n\n/**\n * See https://playwright.dev/docs/test-configuration.\n */\nexport default defineConfig({\n  testDir: \"./playwright/tests\",\n  fullyParallel: false,\n  forbidOnly: !!process.env.CI,\n  retries: process.env.CI ? 2 : 1,\n  workers: 1,\n  reporter: process.env.CI ? [[\"github\"], [\"html\"], [\"list\"]] : \"html\",\n\n  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */\n  use: {\n    trace: process.env.CI ? \"retain-on-failure\" : \"on\",\n  },\n\n  /* Configure projects for major browsers */\n  projects: [\n    {\n      name: \"mobile-chrome\",\n      testMatch: /.*\\/mobile\\/.*\\.spec\\.ts/,\n      use: {\n        ...devices[\"Pixel 5\"],\n        baseURL: \"http://localhost:19006\",\n      },\n    },\n    {\n      name: \"chromium\",\n      testMatch: /.*\\/desktop\\/.*\\.spec\\.ts/,\n      use: {\n        ...devices[\"Desktop Chrome\"],\n        baseURL: \"http://localhost:4455\",\n      },\n    },\n  ],\n\n  webServer: [\n    {\n      command: [\n        \"cp test/e2e/playwright/kratos.base-config.json test/e2e/playwright/kratos.config.json\",\n        \"go run -tags sqlite,json1 . migrate sql -e --yes\",\n        \"go run -tags sqlite,json1 . serve --watch-courier --dev -c test/e2e/playwright/kratos.config.json\",\n      ].join(\" && \"),\n      cwd: \"../..\",\n      url: \"http://localhost:4433/health/ready\",\n      env: {\n        DSN: dbToDsn(),\n        COURIER_SMTP_CONNECTION_URI:\n          \"smtp://localhost:8026/?disable_starttls=true\",\n      },\n      timeout: 7 * 60 * 1000, // 7 minutes\n    },\n    {\n      command: \"go tool MailHog -smtp-bind-addr=localhost:8026\",\n      cwd: \"../..\",\n      reuseExistingServer: false,\n      url: \"http://localhost:8025/\",\n    },\n    {\n      command: \"go run test/e2e/mock/httptarget/main.go\",\n      cwd: \"../..\",\n      reuseExistingServer: false,\n      url: \"http://localhost:4471/health\",\n    },\n  ],\n})\n\nfunction dbToDsn(): string {\n  switch (process.env.DB) {\n    case \"postgres\":\n      return process.env.TEST_DATABASE_POSTGRESQL\n    case \"cockroach\":\n      return process.env.TEST_DATABASE_COCKROACHDB\n    case \"mysql\":\n      return process.env.TEST_DATABASE_MYSQL\n    case \"sqlite\":\n      return process.env.TEST_DATABASE_SQLITE\n    default:\n      return \"memory\"\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/code/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        code:\n          hooks:\n            - hook: show_verification_ui\n            - hook: session\n\n    login:\n      ui_url: http://localhost:4455/login\n      after:\n        code:\n          hooks:\n            - hook: require_verified_address\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      enabled: true\n      use: code\n      ui_url: http://localhost:4455/verification\n    recovery:\n      enabled: true\n      use: code\n      ui_url: http://localhost:4455/recovery\n  methods:\n    password:\n      enabled: true\n    code:\n      passwordless_enabled: true\n      enabled: true\n      config:\n        lifespan: 1h\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/code/identity.traits.schema.json\n"
  },
  {
    "path": "test/e2e/profiles/code/identity.code.only.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\"email\"]\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/code/identity.complex.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"username\": {\n          \"type\": \"string\",\n          \"title\": \"Your Username\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"email2\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your Second E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/code/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"tos\": {\n          \"type\": \"boolean\",\n          \"title\": \"Accept Terms of Service\",\n          \"description\": \"In order to sign up, you have to accept our terms of service.\"\n        }\n      },\n      \"required\": [\"email\", \"tos\"]\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/email/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verification\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/email/identity.traits.schema.json\n"
  },
  {
    "path": "test/e2e/profiles/email/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Your website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        },\n        \"tos\": {\n          \"title\": \"Accept Terms of Service\",\n          \"type\": \"boolean\"\n        },\n        \"age\": {\n          \"title\": \"Age\",\n          \"type\": \"number\",\n          \"maximum\": 300\n        },\n        \"consent\": {\n          \"title\": \"Consent\",\n          \"type\": \"boolean\"\n        },\n        \"newsletter\": {\n          \"title\": \"Newsletter\",\n          \"type\": \"boolean\"\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/kratos.base.yml",
    "content": "log:\n  level: trace\n  leak_sensitive_values: true\n\nsecrets:\n  cookie:\n    - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE\n\nselfservice:\n  default_browser_return_url: http://localhost:4455/\n  allowed_return_urls:\n    - http://localhost:4455\n    - http://localhost:19006/Callback\n    - exp://example.com/Callback\n    - https://www.ory.sh/\n    - https://example.org/\n    - https://www.example.org/\n  methods:\n    link:\n      config:\n        lifespan: 1h\n    code:\n      config:\n        lifespan: 1h\n\nserve:\n  public:\n    base_url: http://localhost:4455/\n    cors:\n      enabled: true\n      allowed_origins:\n        - http://localhost:3000\n        - http://localhost:19006\n      allowed_headers:\n        - Authorization\n        - Content-Type\n        - X-Session-Token\n  admin:\n    base_url: http://kratos:4434/\n\nhashers:\n  algorithm: argon2\n  argon2:\n    memory: 1KB\n    iterations: 1\n    parallelism: 1\n\ncourier:\n  smtp:\n    connection_uri: smtps://test:test@localhost:1025/?skip_ssl_verify=true\n\nsession:\n  whoami:\n    required_aal: aal1\n\nfeature_flags:\n  legacy_continue_with_verification_ui: true\n  legacy_require_verified_login_error: true\n  legacy_oidc_registration_node_group: true\n"
  },
  {
    "path": "test/e2e/profiles/mfa/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n      required_aal: aal1\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\n  methods:\n    code:\n      mfa_enabled: true\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    lookup_secret:\n      enabled: true\n    webauthn:\n      enabled: true\n      config:\n        rp:\n          id: localhost\n          origin: http://localhost:4455\n          display_name: Ory\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/mfa/identity.traits.schema.json\n\nsession:\n  whoami:\n    required_aal: aal1\n"
  },
  {
    "path": "test/e2e/profiles/mfa/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Your website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/mfa-optional/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n      required_aal: highest_available\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\n  methods:\n    code:\n      mfa_enabled: true\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    lookup_secret:\n      enabled: true\n    webauthn:\n      enabled: true\n      config:\n        rp:\n          id: localhost\n          origin: http://localhost:4455\n          display_name: Ory\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/mfa-optional/identity.traits.schema.json\n\nsession:\n  whoami:\n    required_aal: highest_available\n"
  },
  {
    "path": "test/e2e/profiles/mfa-optional/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"optionalMfaEmail\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail for MFA (Optional)\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            }\n          }\n        }\n      },\n      \"required\": [\"email\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/mobile/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      privileged_session_max_age: 5m\n      required_aal: aal1\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      after:\n        password:\n          hooks:\n            - hook: session\n        oidc:\n          hooks:\n            - hook: session\n    recovery:\n      enabled: true\n\n    verification:\n      enabled: false\n  methods:\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    lookup_secret:\n      enabled: true\n    webauthn:\n      enabled: true\n      config:\n        rp:\n          id: localhost\n          origin: http://localhost:4455\n          display_name: Ory\n    oidc:\n      enabled: true\n      config:\n        providers:\n          - id: hydra\n            label: Ory\n            provider: generic\n            client_id: ${OIDC_HYDRA_CLIENT_ID}\n            client_secret: ${OIDC_HYDRA_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n          - id: google\n            provider: generic\n            client_id: ${OIDC_GOOGLE_CLIENT_ID}\n            client_secret: ${OIDC_GOOGLE_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n          - id: github\n            provider: generic\n            client_id: ${OIDC_GITHUB_CLIENT_ID}\n            client_secret: ${OIDC_GITHUB_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/email/identity.traits.schema.json\n\nsecrets:\n  cipher:\n    - secret-thirty-two-character-long\n\nsession:\n  whoami:\n    required_aal: aal1\n"
  },
  {
    "path": "test/e2e/profiles/mobile/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"website\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        },\n        \"tos\": {\n          \"title\": \"Accept Terms of Service\",\n          \"type\": \"boolean\"\n        },\n        \"age\": {\n          \"title\": \"Age\",\n          \"type\": \"number\",\n          \"maximum\": 300\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/network/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        hooks:\n          - hook: web_hook\n            config:\n              method: GET\n              url: https://www.google.com/login/hook\n              body:  http://192.168.178.3:4455/body\n\n    login:\n      ui_url: http://localhost:4455/login\n      before:\n        hooks:\n          - hook: web_hook\n            config:\n              method: GET\n              url: http://192.168.178.2:4455/login/hook\n              body: file://test/e2e/profiles/network/webhook.jsonnet\n\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verification\n      enabled: true\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\nidentity:\n  default_schema_id: localhost\n  schemas:\n    - id: localhost\n      url: http://localhost/\n    - id: ref\n      url: file://test/e2e/profiles/network/identity.traits.schema.json\n    - id: working\n      url: file://test/e2e/profiles/email/identity.traits.schema.json\n\nclients:\n  http:\n    disallow_private_ip_ranges: true\n"
  },
  {
    "path": "test/e2e/profiles/network/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"website\": {\n          \"$ref\": \"http://192.168.178.1/\"\n        },\n        \"tos\": {\n          \"title\": \"Accept Terms of Service\",\n          \"type\": \"boolean\"\n        },\n        \"age\": {\n          \"title\": \"Age\",\n          \"type\": \"number\",\n          \"maximum\": 300\n        },\n        \"consent\": {\n          \"title\": \"Consent\",\n          \"type\": \"boolean\"\n        },\n        \"newsletter\": {\n          \"title\": \"Newsletter\",\n          \"type\": \"boolean\"\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/network/webhook.jsonnet",
    "content": "{}\n"
  },
  {
    "path": "test/e2e/profiles/oidc/.kratos.yml",
    "content": "selfservice:\n  methods:\n    oidc:\n      enabled: true\n      config:\n        providers:\n          - id: hydra\n            label: Ory\n            provider: generic\n            client_id: ${OIDC_HYDRA_CLIENT_ID}\n            client_secret: ${OIDC_HYDRA_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n          - id: google\n            provider: generic\n            client_id: ${OIDC_GOOGLE_CLIENT_ID}\n            client_secret: ${OIDC_GOOGLE_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n          - id: github\n            provider: generic\n            client_id: ${OIDC_GITHUB_CLIENT_ID}\n            client_secret: ${OIDC_GITHUB_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n\n  flows:\n    settings:\n      privileged_session_max_age: 5m\n      ui_url: http://localhost:4455/settings\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n        oidc:\n          hooks:\n            - hook: session\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/oidc/identity.traits.schema.json\n\nsecrets:\n  cipher:\n    - secret-thirty-two-character-long\n\nsession:\n  whoami:\n    required_aal: aal1\n"
  },
  {
    "path": "test/e2e/profiles/oidc/hydra.jsonnet",
    "content": "local claims = std.extVar('claims');\n\nif std.length(claims.sub) == 0 then\n  error 'claim sub not set'\nelse\n  {\n    identity: {\n      traits: {\n        email: claims.sub,\n        [if \"website\" in claims then \"website\" else null]: claims.website\n      },\n    },\n  }\n"
  },
  {
    "path": "test/e2e/profiles/oidc/identity-required.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        },\n        \"consent\": {\n          \"title\": \"Consent\",\n          \"type\": \"boolean\"\n        },\n        \"newsletter\": {\n          \"title\": \"Newsletter\",\n          \"type\": \"boolean\"\n        },\n        \"nested\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"b\": {\n              \"title\": \" optional B\",\n              \"type\": \"string\"\n            }\n          }\n        },\n        \"requirednested\": {\n          \"title\": \"required nested\",\n          \"type\": \"object\",\n          \"properties\": {\n            \"a\": {\n              \"title\": \"Required A\",\n              \"const\": true\n            },\n            \"b\": {\n              \"title\": \"Required optional B\",\n              \"type\": \"string\"\n            }\n          },\n          \"required\": [\n            \"a\"\n          ]\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\",\n        \"requirednested\",\n        \"consent\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/oidc/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        },\n        \"consent\": {\n          \"title\": \"Consent\",\n          \"type\": \"boolean\"\n        },\n        \"newsletter\": {\n          \"title\": \"Newsletter\",\n          \"type\": \"boolean\"\n        }\n      },\n      \"required\": [\"email\", \"website\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/oidc-provider/.kratos.yml",
    "content": "clients:\n  http:\n    disallow_private_ip_ranges: false\noauth2_provider:\n  url: \"http://localhost:4745\"\nselfservice:\n  methods:\n    password:\n      enabled: true\n    code:\n      enabled: true\n    oidc:\n      enabled: true\n      config:\n        providers:\n          - id: google\n            provider: generic\n            client_id: ${OIDC_GOOGLE_CLIENT_ID}\n            client_secret: ${OIDC_GOOGLE_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc-provider/hydra.jsonnet\n  flows:\n    settings:\n      privileged_session_max_age: 5m\n      ui_url: http://localhost:4455/settings\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        oidc:\n          hooks:\n            - hook: session\n        password:\n          hooks:\n            - hook: session\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      ui_url: http://localhost:4455/recovery\n      enabled: true\n      lifespan: 5m\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/oidc-provider/identity.traits.schema.json\n\nsecrets:\n  cipher:\n    - secret-thirty-two-character-long\n"
  },
  {
    "path": "test/e2e/profiles/oidc-provider/hydra.jsonnet",
    "content": "local claims = std.extVar('claims');\n\nif std.length(claims.sub) == 0 then\n  error 'claim sub not set'\nelse\n  {\n    identity: {\n      traits: {\n        email: claims.sub,\n        [if \"website\" in claims then \"website\" else null]: claims.website\n      },\n    },\n  }\n"
  },
  {
    "path": "test/e2e/profiles/oidc-provider/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        },\n        \"tos\": {\n          \"title\": \"Accept Terms of Service\",\n          \"type\": \"boolean\"\n        },\n        \"age\": {\n          \"title\": \"Age\",\n          \"type\": \"number\",\n          \"maximum\": 300\n        },\n        \"consent\": {\n          \"title\": \"Consent\",\n          \"type\": \"boolean\"\n        },\n        \"newsletter\": {\n          \"title\": \"Newsletter\",\n          \"type\": \"boolean\"\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}"
  },
  {
    "path": "test/e2e/profiles/oidc-provider-mfa/.kratos.yml",
    "content": "clients:\n  http:\n    disallow_private_ip_ranges: false\nselfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n      required_aal: highest_available\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\n  methods:\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    webauthn:\n      enabled: true\n      config:\n        rp:\n          id: localhost\n          origin: http://localhost:4455\n          display_name: Ory\n\noauth2_provider:\n  url: \"http://localhost:4745\"\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/oidc-provider-mfa/identity.traits.schema.json\n\nsession:\n  whoami:\n    required_aal: highest_available\n"
  },
  {
    "path": "test/e2e/profiles/oidc-provider-mfa/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        },\n        \"tos\": {\n          \"title\": \"Accept Terms of Service\",\n          \"type\": \"boolean\"\n        },\n        \"age\": {\n          \"title\": \"Age\",\n          \"type\": \"number\",\n          \"maximum\": 300\n        },\n        \"consent\": {\n          \"title\": \"Consent\",\n          \"type\": \"boolean\"\n        },\n        \"newsletter\": {\n          \"title\": \"Newsletter\",\n          \"type\": \"boolean\"\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/passkey/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n      required_aal: highest_available\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n        passkey:\n          hooks:\n            - hook: session\n\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\n  methods:\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    lookup_secret:\n      enabled: true\n    passkey:\n      enabled: true\n      config:\n        rp:\n          id: localhost\n          origins:\n            - http://localhost:4455\n            - http://localhost:19006\n          display_name: Ory\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/passkey/identity.traits.schema.json\n\nsession:\n  whoami:\n    required_aal: highest_available\n"
  },
  {
    "path": "test/e2e/profiles/passkey/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"passkey\": {\n                \"display_name\": true\n              }\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Your website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        }\n      },\n      \"required\": [\"email\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/passwordless/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n      required_aal: aal1\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n        webauthn:\n          hooks:\n            - hook: session\n        passkey:\n          hooks:\n            - hook: session\n\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\n  methods:\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    lookup_secret:\n      enabled: true\n    webauthn:\n      enabled: true\n      config:\n        passwordless: true\n        rp:\n          id: localhost\n          origin: http://localhost:4455\n          display_name: Ory\n    passkey:\n      enabled: true\n      config:\n        rp:\n          display_name: Your Application name\n          # Set 'id' to the top-level domain.\n          id: localhost\n          # Set 'origin' to the exact URL of the page that prompts the user to use WebAuthn. You must include the scheme, host, and port.\n          origins:\n            - http://localhost:4455\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/passwordless/identity.traits.schema.json\n\nsession:\n  whoami:\n    required_aal: aal1\n"
  },
  {
    "path": "test/e2e/profiles/passwordless/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"passkey\": {\n                \"display_name\": true\n              }\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Your website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        }\n      },\n      \"required\": [\"email\", \"website\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/recovery/.kratos.yml",
    "content": "selfservice:\n  methods:\n    password:\n      enabled: true\n    link:\n      enabled: true\n\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 1m\n\n    recovery:\n      ui_url: http://localhost:4455/recovery\n      enabled: true\n      lifespan: 5m\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    login:\n      ui_url: http://localhost:4455/login\n    registration:\n      ui_url: http://localhost:4455/registration\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/recovery/identity.traits.schema.json\n\nsecrets:\n  cipher:\n    - secret-thirty-two-character-long\n"
  },
  {
    "path": "test/e2e/profiles/recovery/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/recovery-mfa/.kratos.yml",
    "content": "selfservice:\n  methods:\n    password:\n      enabled: true\n    link:\n      enabled: true\n\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 1m\n      required_aal: highest_available\n\n    recovery:\n      ui_url: http://localhost:4455/recovery\n      enabled: true\n      lifespan: 5m\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    login:\n      ui_url: http://localhost:4455/login\n    registration:\n      ui_url: http://localhost:4455/registration\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n\n  methods:\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    lookup_secret:\n      enabled: true\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/recovery/identity.traits.schema.json\n\nsecrets:\n  cipher:\n    - secret-thirty-two-character-long\n"
  },
  {
    "path": "test/e2e/profiles/recovery-mfa/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/spa/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      privileged_session_max_age: 5m\n      ui_url: http://localhost:4455/settings\n      required_aal: aal1\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            -\n              hook: session\n        oidc:\n          hooks:\n            -\n              hook: session\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      enabled: true\n      lifespan: 5m\n      ui_url: http://localhost:4455/verify\n    recovery:\n      enabled: true\n      lifespan: 5m\n      ui_url: http://localhost:4455/recovery\n\n  methods:\n    oidc:\n      enabled: true\n      config:\n        providers:\n          -\n            id: hydra\n            label: Ory\n            provider: generic\n            client_id: ${OIDC_HYDRA_CLIENT_ID}\n            client_secret: ${OIDC_HYDRA_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n          -\n            id: google\n            provider: generic\n            client_id: ${OIDC_GOOGLE_CLIENT_ID}\n            client_secret: ${OIDC_GOOGLE_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n          -\n            id: github\n            provider: generic\n            client_id: ${OIDC_GITHUB_CLIENT_ID}\n            client_secret: ${OIDC_GITHUB_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n    totp:\n      enabled: true\n      config:\n        issuer: issuer.ory.sh\n    lookup_secret:\n      enabled: true\n    webauthn:\n      enabled: true\n      config:\n        rp:\n          id: localhost\n          origin: http://localhost:4455\n          display_name: Ory\n\nidentity:\n  schemas:\n  - id: default\n    url: file://test/e2e/profiles/spa/identity.traits.schema.json\n\nsession:\n  whoami:\n    required_aal: aal1\n"
  },
  {
    "path": "test/e2e/profiles/spa/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              },\n              \"totp\": {\n                \"account_name\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Your website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        },\n        \"tos\": {\n          \"title\": \"Accept Terms of Service\",\n          \"type\": \"boolean\"\n        },\n        \"age\": {\n          \"title\": \"Age\",\n          \"type\": \"number\",\n          \"maximum\": 300\n        },\n        \"consent\": {\n          \"title\": \"Consent\",\n          \"type\": \"boolean\"\n        },\n        \"newsletter\": {\n          \"title\": \"Newsletter\",\n          \"type\": \"boolean\"\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/two-steps/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n      required_aal: aal1\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: false\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n        webauthn:\n          hooks:\n            - hook: session\n        code:\n          hooks:\n            - hook: session\n        oidc:\n          hooks:\n            - hook: session\n\n    login:\n      ui_url: http://localhost:4455/login\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      enabled: true\n      use: code\n      ui_url: http://localhost:4455/recovery\n\n  methods:\n    password:\n      enabled: true\n\n    webauthn:\n      enabled: true\n      config:\n        passwordless: true\n        rp:\n          display_name: Your Application name\n          # Set 'id' to the top-level domain.\n          id: localhost\n          # Set 'origin' to the exact URL of the page that prompts the user to use WebAuthn. You must include the scheme, host, and port.\n          origin: http://localhost:4455\n\n    totp:\n      config:\n        issuer: Kratos\n      enabled: true\n\n    lookup_secret:\n      enabled: true\n\n    link:\n      enabled: true\n\n    code:\n      enabled: true\n      passwordless_enabled: true\n      config:\n        lifespan: 1h\n\n    oidc:\n      enabled: true\n      config:\n        providers:\n          - id: hydra\n            label: Ory\n            provider: generic\n            client_id: ${OIDC_HYDRA_CLIENT_ID}\n            client_secret: ${OIDC_HYDRA_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n          - id: google\n            provider: generic\n            client_id: ${OIDC_GOOGLE_CLIENT_ID}\n            client_secret: ${OIDC_GOOGLE_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n          - id: github\n            provider: generic\n            client_id: ${OIDC_GITHUB_CLIENT_ID}\n            client_secret: ${OIDC_GITHUB_CLIENT_SECRET}\n            issuer_url: http://localhost:4444/\n            scope:\n              - offline\n            mapper_url: file://test/e2e/profiles/oidc/hydra.jsonnet\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/two-steps/identity.traits.schema.json\n\nsession:\n  whoami:\n    required_aal: aal1\n"
  },
  {
    "path": "test/e2e/profiles/two-steps/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"code\": {\n                \"identifier\": true,\n                \"via\": \"email\"\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"website\": {\n          \"title\": \"Your website\",\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        }\n      },\n      \"required\": [\"email\", \"website\"],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/verification/.kratos.yml",
    "content": "selfservice:\n  methods:\n    password:\n      enabled: true\n    link:\n      enabled: true\n    code:\n      enabled: true\n\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 1m\n\n    verification:\n      ui_url: http://localhost:4455/verification\n      enabled: true\n      lifespan: 5m\n      use: code\n      after:\n        default_browser_return_url: http://localhost:4455/\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    login:\n      ui_url: http://localhost:4455/login\n    registration:\n      ui_url: http://localhost:4455/registration\n    error:\n      ui_url: http://localhost:4455/error\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/verification/identity.traits.schema.json\n\nsecrets:\n  cipher:\n    - secret-thirty-two-character-long\n"
  },
  {
    "path": "test/e2e/profiles/verification/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              },\n              \"webauthn\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/webhooks/.kratos.yml",
    "content": "selfservice:\n  flows:\n    settings:\n      ui_url: http://localhost:4455/settings\n      privileged_session_max_age: 5m\n\n    logout:\n      after:\n        default_browser_return_url: http://localhost:4455/login\n\n    registration:\n      enable_legacy_one_step: true\n      ui_url: http://localhost:4455/registration\n      after:\n        password:\n          hooks:\n            - hook: session\n            - hook: web_hook\n              config:\n                url: http://127.0.0.1:4459/webhook\n                method: POST\n                body: file://test/e2e/profiles/webhooks/webhook_body.jsonnet\n                can_interrupt: true\n                auth:\n                  type: api_key\n                  config:\n                    name: X-Authorize-Request\n                    value: \"1\"\n                    in: header\n\n    login:\n      ui_url: http://localhost:4455/login\n      after:\n        password:\n          hooks:\n            - hook: web_hook\n              config:\n                url: http://127.0.0.1:4459/webhook\n                method: POST\n                body: file://test/e2e/profiles/webhooks/webhook_body.jsonnet\n                can_interrupt: true\n                auth:\n                  type: api_key\n                  config:\n                    name: X-Authorize-Request\n                    value: \"1\"\n                    in: header\n    error:\n      ui_url: http://localhost:4455/error\n    verification:\n      ui_url: http://localhost:4455/verify\n    recovery:\n      ui_url: http://localhost:4455/recovery\n\nidentity:\n  schemas:\n    - id: default\n      url: file://test/e2e/profiles/webhooks/identity.traits.schema.json\n"
  },
  {
    "path": "test/e2e/profiles/webhooks/identity.traits.schema.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"Your E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            },\n            \"recovery\": {\n              \"via\": \"email\"\n            }\n          }\n        }\n      },\n      \"website\": {\n        \"title\": \"Your website\",\n        \"type\": \"string\",\n        \"format\": \"uri\",\n        \"minLength\": 10\n      },\n      \"required\": [\n        \"email\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "test/e2e/profiles/webhooks/webhook_body.jsonnet",
    "content": "function(ctx) {\n    identity_id: if std.objectHas(ctx, \"identity\") then ctx.identity.id else null,\n    email: if std.objectHas(ctx, \"identity\") then ctx.identity.traits.email else null,\n    flow_id: ctx.flow.id,\n    flow_type: ctx.flow.type,\n    identity: if std.objectHas(ctx, \"identity\") then ctx.identity else null,\n    ctx: ctx\n}\n"
  },
  {
    "path": "test/e2e/proxy/package.json",
    "content": "{\n  \"name\": \"proxy\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"index.js\",\n  \"overrides\": {\n    \"tough-cookie\": \">= 4.1.3\"\n  },\n  \"scripts\": {\n    \"start\": \"nodemon ./proxy.js\"\n  },\n  \"dependencies\": {\n    \"express\": \"4.21.2\",\n    \"nodemon\": \"3.1.10\",\n    \"request\": \"2.88.2\",\n    \"url-join\": \"5.0.0\"\n  }\n}\n"
  },
  {
    "path": "test/e2e/proxy/proxy.js",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport request from \"request\"\nimport { resolve } from \"path\"\nimport urljoin from \"url-join\"\nimport express from \"express\"\nimport fs from \"fs\"\n\nconst app = express()\n\nconst proxy =\n  (base, prefix = null) =>\n  (req, res, next) => {\n    let url = urljoin(base, req.url)\n\n    // we need to tell Krato we are behind a reverse proxy\n    req.headers[\"x-forwarded-host\"] = req.get(\"host\")\n\n    if (prefix) {\n      url = urljoin(base, prefix, req.url)\n    }\n    req\n      .pipe(request(url, { followRedirect: false }).on(\"error\", next))\n      .pipe(res)\n  }\n\napp.use(\n  \"/self-service/\",\n  proxy(process.env.KRATOS_PUBLIC_URL, \"/self-service/\"),\n)\napp.use(\"/schemas/\", proxy(process.env.KRATOS_PUBLIC_URL, \"/schemas/\"))\napp.use(\"/.well-known/\", proxy(process.env.KRATOS_PUBLIC_URL, \"/.well-known/\"))\n\napp.use(\"/\", (req, res, next) => {\n  const pc = JSON.parse(fs.readFileSync(resolve(\"../proxy.json\")).toString())\n  switch (pc) {\n    case \"react\":\n      proxy(process.env.KRATOS_UI_REACT_URL)(req, res, next)\n      return\n    case \"react-native\":\n      proxy(process.env.KRATOS_UI_REACT_NATIVE_URL)(req, res, next)\n      return\n  }\n  proxy(process.env.KRATOS_UI_URL)(req, res, next)\n})\n\nconst port = parseInt(process.env.PORT) || 4455\n\nlet listener = () => {\n  console.log(`Listening on http://0.0.0.0:${port}`)\n}\n\napp.listen(port, \"0.0.0.0\", listener)\n"
  },
  {
    "path": "test/e2e/render-kratos-config.sh",
    "content": "#!/bin/sh\n\n# Renders the config schema to a TypeScript file that can be used in Cypress tests.\n\nset -euxo pipefail\n\ndir=$(realpath $(dirname \"${BASH_SOURCE[0]}\"))\n\nory_x_version=\"$(cd $dir/../..; go list -f '{{.Version}}' -m github.com/ory/x)\"\n\ncurl --retry 7 --retry-connrefused -s https://raw.githubusercontent.com/ory/x/$ory_x_version/otelx/config.schema.json > $dir/.tracing-config.schema.json\n\n(cd $dir; sed \"s!ory://tracing-config!.tracing-config.schema.json!g;\" $dir/../../embedx/config.schema.json | npx json2ts --strictIndexSignatures > $dir/shared/config.d.ts)\n\nrm $dir/.tracing-config.schema.json\n\n(cd $dir/../..; make format)\n"
  },
  {
    "path": "test/e2e/run.sh",
    "content": "#!/usr/bin/env bash\n\necho \"Running Ory Kratos E2E Tests...\"\necho \"\"\nset -euxo pipefail\n\ncd \"$(dirname \"${BASH_SOURCE[0]}\")/../..\"\n\nmake .bin/hydra\n\nexport PATH=.bin:$PATH\nexport KRATOS_PUBLIC_URL=http://localhost:4433/\nexport KRATOS_BROWSER_URL=http://localhost:4433/\nexport KRATOS_ADMIN_URL=http://localhost:4434/\nexport KRATOS_UI_URL=http://localhost:4456/\nexport KRATOS_UI_REACT_URL=http://localhost:4458/\nexport KRATOS_UI_REACT_NATIVE_URL=http://localhost:19006/\nexport LOG_LEAK_SENSITIVE_VALUES=true\nexport DEV_DISABLE_API_FLOW_ENFORCEMENT=true\nexport COOKIE_SECRET=kweifawskf23weas\nexport CSRF_COOKIE_NAME=node_csrf_token\nexport CSRF_COOKIE_SECRET=lkaw9oe8isedrhq2\n\nbase=$(pwd)\n\nsetup=yes\ndev=no\nnokill=no\ncleanup=no\nfor i in \"$@\"; do\n  case $i in\n  --no-kill)\n    nokill=yes\n    shift # past argument=value\n    ;;\n  --only-setup)\n    setup=only\n    shift # past argument=value\n    ;;\n  --no-setup)\n    setup=no\n    shift # past argument=value\n    ;;\n  --dev)\n    dev=yes\n    shift # past argument=value\n    ;;\n  --cleanup)\n    cleanup=yes\n    shift # past argument=value\n    ;;\n  esac\ndone\n\ncleanup() {\n    # Temporarily disable exit on error for cleanup (services may not be running)\n    set +e\n\n    echo \"=== E2E Cleanup ===\"\n\n    # Step 1: Kill process supervisors FIRST (they respawn children)\n    echo \"Killing process supervisors (modd, nodemon)...\"\n    pkill -9 -f \"modd\" 2>/dev/null\n    pkill -9 -f \"nodemon\" 2>/dev/null\n    sleep 2\n\n    # Step 2: Kill Kratos and webhook (were managed by modd)\n    echo \"Killing Kratos and webhook...\"\n    pkill -9 -f \"kratos serve\" 2>/dev/null\n    pkill -9 -f \"\\.bin/kratos\" 2>/dev/null\n    pkill -9 -f \"\\.bin/webhook\" 2>/dev/null\n    killall kratos 2>/dev/null\n    killall webhook 2>/dev/null\n\n    # Step 3: Kill Node.js services\n    echo \"Killing Node.js services...\"\n    pkill -9 -f \"node.*proxy\" 2>/dev/null\n    pkill -9 -f \"kratos-selfservice-ui-node\" 2>/dev/null\n    pkill -9 -f \"kratos-selfservice-ui-react-nextjs\" 2>/dev/null\n    pkill -9 -f \"expo\" 2>/dev/null\n    pkill -9 -f \"webpack.*19006\" 2>/dev/null\n\n    # Step 4: Kill Hydra processes\n    echo \"Killing Hydra processes...\"\n    killall hydra 2>/dev/null\n    killall hydra-login-consent 2>/dev/null\n    killall hydra-kratos-login-consent 2>/dev/null\n    pkill -9 -f \"hydra serve\" 2>/dev/null\n\n    # Step 5: Kill any remaining processes on known ports\n    echo \"Cleaning up any remaining port listeners...\"\n    for port in 4433 4434 4444 4445 4446 4455 4456 4458 4744 4745 4746 19006; do\n        pid=$(lsof -ti:$port 2>/dev/null)\n        if [ -n \"$pid\" ]; then\n            echo \"  Killing PID $pid on port $port\"\n            kill -9 $pid 2>/dev/null\n        fi\n    done\n\n    # Step 6: Stop and remove Docker containers\n    echo \"Stopping Docker containers...\"\n    docker stop kratos_test_database_mysql kratos_test_database_postgres kratos_test_database_cockroach mailslurper kratos_test_hydra 2>/dev/null\n    docker rm kratos_test_database_mysql kratos_test_database_postgres kratos_test_database_cockroach mailslurper kratos_test_hydra 2>/dev/null\n\n    echo \"=== Cleanup Complete ===\"\n\n    # Re-enable exit on error\n    set -e\n}\n\nprepare() {\n  echo \"::group::prepare\"\n  if [[ \"${nokill}\" == \"no\" ]]; then\n    cleanup\n  fi\n\n  if [ -z ${TEST_DATABASE_POSTGRESQL-} ]; then\n    docker rm -f kratos_test_database_mysql kratos_test_database_postgres kratos_test_database_cockroach || true\n    docker run --name kratos_test_database_mysql -p 3444:3306 -e MYSQL_ROOT_PASSWORD=secret -d mysql:8.0\n    docker run --name kratos_test_database_postgres -p 3445:5432 -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=postgres -d postgres:14 postgres -c log_statement=all\n    docker run --name kratos_test_database_cockroach -p 3446:26257 -d cockroachdb/cockroach:latest-v25.4 start-single-node --insecure\n\n    export TEST_DATABASE_MYSQL=\"mysql://root:secret@(localhost:3444)/mysql?parseTime=true&multiStatements=true\"\n    export TEST_DATABASE_POSTGRESQL=\"postgres://postgres:secret@localhost:3445/postgres?sslmode=disable\"\n    export TEST_DATABASE_COCKROACHDB=\"cockroach://root@localhost:3446/defaultdb?sslmode=disable\"\n  fi\n\n  if [ -z \"${NODE_UI_PATH-}\" ]; then\n    node_ui_dir=\"$(mktemp -d -t ci-XXXXXXXXXX)/kratos-selfservice-ui-node\"\n    git clone --depth 1 --branch master https://github.com/ory/kratos-selfservice-ui-node.git \"$node_ui_dir\"\n    (cd \"$node_ui_dir\" && npm i --legacy-peer-deps && npm run build)\n  else\n    node_ui_dir=\"${NODE_UI_PATH}\"\n  fi\n\n  if [ -z \"${RN_UI_PATH-}\" ]; then\n    rn_ui_dir=\"$(mktemp -d -t ci-XXXXXXXXXX)/kratos-selfservice-ui-react-native\"\n    git clone --depth 1 --branch master https://github.com/ory/kratos-selfservice-ui-react-native.git \"$rn_ui_dir\"\n    (cd \"$rn_ui_dir\" && npm i)\n  else\n    rn_ui_dir=\"${RN_UI_PATH}\"\n  fi\n\n  if [ -z \"${REACT_UI_PATH-}\" ]; then\n    react_ui_dir=\"$(mktemp -d -t ci-XXXXXXXXXX)/ory/kratos-selfservice-ui-react-nextjs\"\n    git clone --depth 1 --branch master https://github.com/ory/kratos-selfservice-ui-react-nextjs.git \"$react_ui_dir\"\n    (cd \"$react_ui_dir\" && npm i)\n  else\n    react_ui_dir=\"${REACT_UI_PATH}\"\n  fi\n\n  (\n    rm test/e2e/proxy.json || true\n    echo '\"express\"' > test/e2e/proxy.json\n    cd test/e2e/proxy\n    npm i\n  )\n\n  if [ -z ${CI-} ]; then\n    docker rm mailslurper hydra hydra-ui -f || true\n    docker run --name mailslurper -p 4436:4436 -p 4437:4437 -p 1025:1025 oryd/mailslurper:latest-smtps > \"${base}/test/e2e/mailslurper.e2e.log\" 2>&1 &\n  fi\n\n  # Check if any ports that we need are open already\n  nc -zv localhost 4444 && exit 1\n  nc -zv localhost 4445 && exit 1\n  nc -zv localhost 4446 && exit 1\n  nc -zv localhost 4455 && exit 1\n  nc -zv localhost 19006 && exit 1\n  nc -zv localhost 4456 && exit 1\n  nc -zv localhost 4458 && exit 1\n  nc -zv localhost 4744 && exit 1\n  nc -zv localhost 4745 && exit 1\n\n  (\n    cd \"$rn_ui_dir\"\n    KRATOS_URL=http://localhost:4433 CI=1 npm run web \\\n      >\"${base}/test/e2e/rn-profile-app.e2e.log\" 2>&1 &\n  )\n\n  hydra serve all -c test/e2e/hydra.yml --dev >\"${base}/test/e2e/hydra.e2e.log\" 2>&1 &\n\n  (cd test/e2e; npm run wait-on -- -l -t 300000 http-get://localhost:4445/health/alive)\n\n  hydra_client=$(hydra create oauth2-client \\\n    --endpoint http://localhost:4445 \\\n    --grant-type authorization_code --grant-type refresh_token \\\n    --response-type code --response-type id_token \\\n    --scope openid --scope offline \\\n    --redirect-uri http://localhost:4455/self-service/methods/oidc/callback/hydra \\\n    --format json)\n  export OIDC_HYDRA_CLIENT_ID=$(jq -r '.client_id' <<< \"$hydra_client\" )\n  export OIDC_HYDRA_CLIENT_SECRET=$(jq -r '.client_secret' <<< \"$hydra_client\" )\n\n  google_client=$(hydra create oauth2-client \\\n    --endpoint http://localhost:4445 \\\n    --grant-type authorization_code --grant-type refresh_token \\\n    --response-type code --response-type id_token \\\n    --scope openid --scope offline \\\n    --redirect-uri http://localhost:4455/self-service/methods/oidc/callback/google \\\n    --format json)\n  export OIDC_GOOGLE_CLIENT_ID=$(jq -r '.client_id' <<< \"$google_client\" )\n  export OIDC_GOOGLE_CLIENT_SECRET=$(jq -r '.client_secret' <<< \"$google_client\" )\n\n  github_client=$(hydra create oauth2-client \\\n    --endpoint http://localhost:4445 \\\n    --grant-type authorization_code --grant-type refresh_token \\\n    --response-type code --response-type id_token \\\n    --scope openid --scope offline \\\n    --redirect-uri http://localhost:4455/self-service/methods/oidc/callback/github \\\n    --format json)\n  export OIDC_GITHUB_CLIENT_ID=$(jq -r '.client_id' <<< \"$github_client\" )\n  export OIDC_GITHUB_CLIENT_SECRET=$(jq -r '.client_secret' <<< \"$github_client\" )\n\n  (\n    cd test/e2e/hydra-login-consent\n    go build .\n    PORT=4446 HYDRA_ADMIN_URL=http://localhost:4445 ./hydra-login-consent >\"${base}/test/e2e/hydra-ui.e2e.log\" 2>&1 &\n  )\n\n  # Spin up another Hydra instance with the express node app used as the login UI for kratos-hydra OIDC provider tests\n  DSN=memory SERVE_PUBLIC_PORT=4744 \\\n    SERVE_ADMIN_PORT=4745 \\\n    URLS_SELF_ISSUER=http://localhost:4744 \\\n    LOG_LEVEL=trace \\\n    URLS_LOGIN=http://localhost:4455/login \\\n    URLS_CONSENT=http://localhost:4746/consent \\\n    SECRETS_SYSTEM=\"[\\\"1234567890123456789012345678901\\\"]\" \\\n    hydra serve all --dev >\"${base}/test/e2e/hydra-kratos.e2e.log\" 2>&1 &\n\n  (cd test/e2e; npm run wait-on -- -l -t 300000 http-get://127.0.0.1:4745/health/alive)\n\n  dummy_client=$(hydra create oauth2-client \\\n    --endpoint http://localhost:4745 \\\n    --token-endpoint-auth-method client_secret_basic \\\n    --grant-type authorization_code --grant-type refresh_token \\\n    --response-type code --response-type id_token \\\n    --scope openid --scope offline --scope email --scope website \\\n    --redirect-uri http://localhost:5555/callback \\\n    --redirect-uri https://ory-network-httpbin-ijakee5waq-ez.a.run.app/anything \\\n    --format json)\n  export CYPRESS_OIDC_DUMMY_CLIENT_ID=$(jq -r '.client_id' <<< \"$dummy_client\" )\n  export CYPRESS_OIDC_DUMMY_CLIENT_SECRET=$(jq -r '.client_secret' <<< \"$dummy_client\" )\n\n  (\n    cd test/e2e/hydra-kratos-login-consent\n    go build .\n    PORT=4746 HYDRA_ADMIN_URL=http://localhost:4745 ./hydra-kratos-login-consent >\"${base}/test/e2e/hydra-kratos-ui.e2e.log\" 2>&1 &\n  )\n\n  (\n    cd \"$node_ui_dir\"\n    PORT=4456 SECURITY_MODE=cookie npm run start \\\n      >\"${base}/test/e2e/ui-node.e2e.log\" 2>&1 &\n  )\n\n  if [ -z \"${REACT_UI_PATH-}\" ]; then\n    (\n      cd \"$react_ui_dir\"\n      NEXT_PUBLIC_KRATOS_PUBLIC_URL=http://localhost:4433 npm run build\n      NEXT_PUBLIC_KRATOS_PUBLIC_URL=http://localhost:4433 npm run start -- --hostname 127.0.0.1 --port 4458 \\\n        >\"${base}/test/e2e/react-iu.e2e.log\" 2>&1 &\n    )\n  else\n    (\n      cd \"$react_ui_dir\"\n      PORT=4458 NEXT_PUBLIC_KRATOS_PUBLIC_URL=http://localhost:4433 npm run dev \\\n        >\"${base}/test/e2e/react-iu.e2e.log\" 2>&1 &\n    )\n  fi\n\n  (\n    cd test/e2e/proxy\n    PORT=4455 npm run start \\\n      >\"${base}/test/e2e/proxy.e2e.log\" 2>&1 &\n  )\n\n  # Make the environment available to Playwright\n  env | grep KRATOS_                         >  test/e2e/playwright/playwright.env\n  env | grep TEST_DATABASE_                  >> test/e2e/playwright/playwright.env\n  env | grep OIDC_                           >> test/e2e/playwright/playwright.env\n  env | grep CYPRESS_                        >> test/e2e/playwright/playwright.env\n  echo LOG_LEAK_SENSITIVE_VALUES=true        >> test/e2e/playwright/playwright.env\n  echo DEV_DISABLE_API_FLOW_ENFORCEMENT=true >> test/e2e/playwright/playwright.env\n\n  echo \"::endgroup::\"\n}\n\nrun() {\n  echo \"::group::run-prep\"\n  killall modd || true\n  killall kratos || true\n\n  export DSN=${1}\n\n  nc -zv localhost 4434 && (echo \"Port 4434 unavailable, used by\" ; lsof -i:4434 ; exit 1)\n  nc -zv localhost 4433 && (echo \"Port 4433 unavailable, used by\" ; lsof -i:4433 ; exit 1)\n\n  ls -la .\n  for profile in code email mobile oidc recovery recovery-mfa verification mfa mfa-optional spa network passwordless passkey webhooks oidc-provider oidc-provider-mfa two-steps; do\n    go tool yq ea '. as $item ireduce ({}; . * $item )' test/e2e/profiles/kratos.base.yml \"test/e2e/profiles/${profile}/.kratos.yml\" > test/e2e/kratos.${profile}.yml\n    cat \"test/e2e/kratos.${profile}.yml\" | envsubst | sponge \"test/e2e/kratos.${profile}.yml\"\n  done\n  cp test/e2e/kratos.email.yml test/e2e/kratos.generated.yml\n\n  (go tool modd -f test/e2e/modd.conf >\"${base}/test/e2e/kratos.e2e.log\" 2>&1 &)\n\n  # Having to wait 10 minutes for cockroach to apply the migrations is ridiculous but sometimes it takes that long in CI\n  npm run wait-on -- -l -t 10m http-get://127.0.0.1:4434/health/ready \\\n    http-get://127.0.0.1:4444/.well-known/openid-configuration \\\n    http-get://127.0.0.1:4455/health/ready \\\n    http-get://127.0.0.1:4445/health/ready \\\n    http-get://127.0.0.1:4446/ \\\n    http-get://127.0.0.1:4456/health/alive \\\n    http-get://127.0.0.1:19006/ \\\n    http-get://127.0.0.1:4437/mail \\\n    http-get://127.0.0.1:4458/ \\\n    http-get://127.0.0.1:4459/health\n  echo \"::endgroup::\"\n\n  if [[ $dev == \"yes\" ]]; then\n    (cd test/e2e; npm run test:watch --)\n  else\n    if [ -z \"${CYPRESS_RECORD_KEY-}\" ]; then\n      (cd test/e2e; npx cypress run --browser chrome ${CYPRESS_OPTS-})\n    else\n      (cd test/e2e; npx cypress run --browser chrome ${CYPRESS_OPTS-} --record --tag \"${2}\" )\n    fi\n  fi\n}\n\nusage() {\n  echo $\"This script runs the e2e tests.\n\nTo run the tests just pick a database name:\n\n  $0 <database>\n\n  Supported databases are 'sqlite', 'mysql', 'postgres', 'cockroach':\n\n    $0 sqlite\n    $0 mysql\n    $0 postgres\n    $0 cockroach\n    ...\n\n  If you are using a database other than SQLite, you need to set\n  an environment variable that points to it:\n\n    export TEST_DATABASE_MYSQL=...\n    export TEST_DATABASE_POSTGRESQL=...\n    export TEST_DATABASE_COCKROACHDB=...\n    $0 <database>\n\n  The Makefile has a helper for that which uses Docker to start the\n  databases:\n\n    make test-resetdb\n    source script/test-envs.sh\n    $0 <database>\n\nTo run e2e tests in dev mode (useful for writing them), run:\n\n  $0 --dev <database>\n\nTo set up all the services without running the tests, use:\n\n  $0 --only-setup\n\nTo then run the tests without the set up steps, use:\n\n  $0 --no-setup <database>\n\nTo prevent processes from being killed during set up phase, use:\n  $0 --no-kill\n\nIf you are making changes to the kratos-selfservice-ui-node\nproject as well, point the 'NODE_UI_PATH' environment variable to\nthe path where the kratos-selfservice-ui-node project is checked out:\n\n  export NODE_UI_PATH=$HOME/workspace/kratos-selfservice-ui-node\n  export RN_UI_PATH=$HOME/workspace/kratos-selfservice-ui-react-native\n  export REACT_UI_PATH=$HOME/workspace/kratos-selfservice-ui-react-nextjs\n  $0 ...\"\n}\n\nif [[ \"${cleanup}\" == \"yes\" ]]; then\n  cleanup\n  exit 0\nfi\n\nexport TEST_DATABASE_SQLITE=\"sqlite:///$(mktemp -d -t ci-XXXXXXXXXX)/db.sqlite?_fk=true\"\nexport TEST_DATABASE_MEMORY=\"memory\"\n\ncase \"${1:-default}\" in\nsqlite)\n  echo \"Database set up at: $TEST_DATABASE_SQLITE\"\n  dsn=\"${TEST_DATABASE_SQLITE}\"\n  db=\"sqlite\"\n  ;;\n\nmysql)\n  dsn=\"${TEST_DATABASE_MYSQL}\"\n  db=\"mysql\"\n  ;;\n\npostgres)\n  dsn=\"${TEST_DATABASE_POSTGRESQL}\"\n  db=\"postgres\"\n  ;;\n\ncockroach)\n  dsn=\"${TEST_DATABASE_COCKROACHDB}\"\n  db=\"cockroach\"\n  ;;\n\n*)\n  usage\n  if [[ \"${setup}\" == \"only\" ]]; then\n    prepare\n    exit 0\n  else\n    exit 1\n  fi\n  ;;\nesac\n\nif [[ \"${setup}\" == \"yes\" ]]; then\n  prepare\nfi\n\nrun \"${dsn}\" \"${db}\"\n"
  },
  {
    "path": "test/e2e/shared/config.d.ts",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n/* eslint-disable */\n/**\n * This file was automatically generated by json-schema-to-typescript.\n * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,\n * and run json-schema-to-typescript to regenerate this file.\n */\n\nexport type OryKratosConfiguration = OryKratosConfiguration1 &\n  OryKratosConfiguration2\nexport type OryKratosConfiguration1 = {\n  [k: string]: unknown | undefined\n}\n/**\n * Ory Kratos redirects to this URL per default on completion of self-service flows and other browser interaction. Read this [article for more information on browser redirects](https://www.ory.sh/kratos/docs/concepts/browser-redirect-flow-completion).\n */\nexport type RedirectBrowsersToSetURLPerDefault = string\n/**\n * List of URLs that are allowed to be redirected to. A redirection request is made by appending `?return_to=...` to Login, Registration, and other self-service flows.\n */\nexport type AllowedReturnToURLs = string[]\n/**\n * URL where the Settings UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\n */\nexport type URLOfTheSettingsPage = string\n/**\n * Sets what Authenticator Assurance Level (used for 2FA) is required to access this feature. If set to `highest_available` then this endpoint requires the highest AAL the identity has set up. If set to `aal1` then the identity can access this feature without 2FA.\n */\nexport type RequiredAuthenticatorAssuranceLevel = \"aal1\" | \"highest_available\"\n/**\n * Define what the hook should do\n */\nexport type WebHookConfiguration =\n  | {\n      [k: string]: unknown | undefined\n    }\n  | {\n      can_interrupt?: false\n      [k: string]: unknown | undefined\n    }\nexport type SelfServiceHooks = (SelfServiceWebHook | B2BSSOHook)[]\n/**\n * If set to true will enable [User Registration](https://www.ory.sh/kratos/docs/self-service/flows/user-registration/).\n */\nexport type EnableUserRegistration = boolean\n/**\n * When registration fails because an account with the given credentials or addresses previously signed up, provide login hints about available methods to sign in to the user.\n */\nexport type ProvideLoginHintsOnFailedRegistration = boolean\n/**\n * URL where the Registration UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\n */\nexport type RegistrationUIURL = string\n/**\n * Deprecated, please use `style` instead.\n */\nexport type DisableTwoStepRegistration = boolean\n/**\n * The style of the registration flow. If set to `unified` the login flow will be a one-step process. If set to `profile_first` the registration flow will first ask for the profile information first, and then the credentials.\n */\nexport type RegistrationFlowStyle = \"unified\" | \"profile_first\"\n/**\n * URL where the Login UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\n */\nexport type LoginUIURL = string\n/**\n * The style of the login flow. If set to `unified` the login flow will be a one-step process. If set to `identifier_first` (experimental!) the login flow will first ask for the identifier and then the credentials.\n */\nexport type LoginFlowStyle = \"unified\" | \"identifier_first\"\n/**\n * If set to true will enable [Email and Phone Verification and Account Activation](https://www.ory.sh/kratos/docs/self-service/flows/verify-email-account-activation/).\n */\nexport type EnableEmailPhoneVerification = boolean\n/**\n * URL where the Ory Verify UI is hosted. This is the page where users activate and / or verify their email or telephone number. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\n */\nexport type VerifyUIURL = string\n/**\n * Sets how long the verification request (for the UI interaction) is valid.\n */\nexport type SelfServiceVerificationRequestLifespan = string\n/**\n * The strategy to use for verification requests\n */\nexport type VerificationStrategy = \"link\" | \"code\"\n/**\n * Whether to notify recipients, if verification was requested for their address.\n */\nexport type NotifyUnknownRecipients = boolean\n/**\n * If set to true will enable [Account Recovery](https://www.ory.sh/kratos/docs/self-service/flows/password-reset-account-recovery/).\n */\nexport type EnableAccountRecovery = boolean\n/**\n * URL where the Ory Recovery UI is hosted. This is the page where users request and complete account recovery. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\n */\nexport type RecoveryUIURL = string\nexport type SelfServiceAfterRecoveryHooks = (\n  | SelfServiceWebHook\n  | SelfServiceSessionRevokerHook\n)[]\n/**\n * Sets how long the recovery request is valid. If expired, the user has to redo the flow.\n */\nexport type SelfServiceRecoveryRequestLifespan = string\n/**\n * The strategy to use for recovery requests\n */\nexport type RecoveryStrategy = \"link\" | \"code\"\n/**\n * Whether to notify recipients, if recovery was requested for their account.\n */\nexport type NotifyUnknownRecipients1 = boolean\n/**\n * URL where the Ory Kratos Error UI is hosted. Check the [reference implementation](https://github.com/ory/kratos-selfservice-ui-node).\n */\nexport type OryKratosErrorUIURL = string\nexport type EnablesProfileManagementMethod = boolean\nexport type EnablesLinkMethod = boolean\nexport type OverrideTheBaseURLWhichShouldBeUsedAsTheBaseForRecoveryAndVerificationLinks =\n  string\nexport type HowLongALinkIsValidFor = string\nexport type EnablesUsernameEmailAndPasswordMethod = boolean\n/**\n * Allows changing the default HIBP host to a self hosted version.\n */\nexport type CustomHaveibeenpwnedHost = string\n/**\n * If set to false the password validation does not utilize the Have I Been Pwnd API.\n */\nexport type EnableTheHaveIBeenPwnedAPI = boolean\n/**\n * Defines how often a password may have been breached before it is rejected.\n */\nexport type AllowPasswordBreaches = number\n/**\n * If set to false the password validation fails when the network or the Have I Been Pwnd API is down.\n */\nexport type IgnoreLookupNetworkErrors = boolean\n/**\n * Defines the minimum length of the password.\n */\nexport type MinimumPasswordLength = number\n/**\n * If set to false the password validation does not check for similarity between the password and the user identifier.\n */\nexport type EnablePasswordIdentifierSimilarityCheck = boolean\n/**\n * If set to true will enable password migration.\n */\nexport type EnablePasswordMigration = boolean\n/**\n * Define which auth mechanism the Web-Hook should use\n */\nexport type AuthMechanisms =\n  | WebHookAuthApiKeyProperties\n  | WebHookAuthBasicAuthProperties\nexport type EnablesTheTOTPMethod = boolean\n/**\n * The issuer (e.g. a domain name) will be shown in the TOTP app (e.g. Google Authenticator). It helps the user differentiate between different codes.\n */\nexport type TOTPIssuer = string\nexport type EnablesTheLookupSecretMethod = boolean\nexport type EnablesTheWebAuthnMethod = boolean\n/**\n * If enabled will have the effect that WebAuthn is used for passwordless flows (as a first factor) and not for multi-factor set ups. With this set to true, users will see an option to sign up with WebAuthn on the registration screen.\n */\nexport type UseForPasswordlessFlows = boolean\nexport type RelyingPartyRPConfig =\n  | {\n      origin?: {\n        [k: string]: unknown | undefined\n      }\n      origins?: {\n        [k: string]: unknown | undefined\n      }\n      [k: string]: unknown | undefined\n    }\n  | {\n      origin: string\n      origins?: {\n        [k: string]: unknown | undefined\n      }\n      [k: string]: unknown | undefined\n    }\n  | {\n      origin?: {\n        [k: string]: unknown | undefined\n      }\n      origins: string[]\n      [k: string]: unknown | undefined\n    }\nexport type EnablesThePasskeyMethod = boolean\n/**\n * A name to help the user identify this RP.\n */\nexport type RelyingPartyDisplayName = string\n/**\n * The id must be a subset of the domain currently in the browser.\n */\nexport type RelyingPartyIdentifier = string\n/**\n * A list of explicit RP origins. If left empty, this defaults to either `origin` or `id`, prepended with the current protocol schema (HTTP or HTTPS).\n */\nexport type RelyingPartyOrigins = string[]\nexport type EnablesOpenIDConnectMethod = boolean\n/**\n * Can be used to modify the base URL for OAuth2 Redirect URLs. If unset, the Public Base URL will be used.\n */\nexport type BaseURLForOAuth2RedirectURIs = string\nexport type SelfServiceOIDCProvider = SelfServiceOIDCProvider1 & {\n  id: string\n  provider: Provider\n  label?: OptionalStringWhichWillBeUsedWhenGeneratingLabelsForUIButtons\n  client_id: string\n  client_secret?: string\n  issuer_url?: string\n  auth_url?: string\n  token_url?: string\n  mapper_url: JsonnetMapperURL\n  scope?: string[]\n  microsoft_tenant?: AzureADTenant\n  subject_source?: MicrosoftSubjectSource\n  apple_team_id?: AppleDeveloperTeamID\n  apple_private_key_id?: ApplePrivateKeyIdentifier\n  apple_private_key?: ApplePrivateKey\n  requested_claims?: OpenIDConnectClaims\n  organization_id?: OrganizationID\n  additional_id_token_audiences?: AdditionalClientIdsAllowedWhenUsingIDTokenSubmission\n  claims_source?: ClaimsSource\n  pkce?: ProofKeyForCodeExchange\n  fedcm_config_url?: FederationConfigurationURL\n  net_id_token_origin_header?: NetIDTokenOriginHeader\n}\nexport type SelfServiceOIDCProvider1 = {\n  [k: string]: unknown | undefined\n} & {\n  [k: string]: unknown | undefined\n}\n/**\n * Can be one of github, github-app, gitlab, generic, google, microsoft, discord, salesforce, slack, facebook, auth0, vk, yandex, apple, spotify, netid, dingtalk, patreon.\n */\nexport type Provider =\n  | \"github\"\n  | \"github-app\"\n  | \"gitlab\"\n  | \"generic\"\n  | \"google\"\n  | \"microsoft\"\n  | \"discord\"\n  | \"salesforce\"\n  | \"slack\"\n  | \"facebook\"\n  | \"auth0\"\n  | \"vk\"\n  | \"yandex\"\n  | \"apple\"\n  | \"spotify\"\n  | \"netid\"\n  | \"dingtalk\"\n  | \"patreon\"\n  | \"linkedin\"\n  | \"linkedin_v2\"\n  | \"lark\"\n  | \"x\"\n  | \"fedcm-test\"\n  | \"uaepass\"\nexport type OptionalStringWhichWillBeUsedWhenGeneratingLabelsForUIButtons =\n  string\n/**\n * The URL where the jsonnet source is located for mapping the provider's data to Ory Kratos data.\n */\nexport type JsonnetMapperURL = string\n/**\n * The Azure AD Tenant to use for authentication.\n */\nexport type AzureADTenant = string\n/**\n * Controls which source the subject identifier is taken from by microsoft provider. If set to `userinfo` (the default) then the identifier is taken from the `sub` field of OIDC ID token or data received from `/userinfo` standard OIDC endpoint. If set to `me` then the `id` field of data structure received from `https://graph.microsoft.com/v1.0/me` is taken as an identifier. If the value is `oid` then the the oid (Object ID) is taken to identify users across different services.\n */\nexport type MicrosoftSubjectSource = \"userinfo\" | \"me\" | \"oid\"\n/**\n * Apple Developer Team ID needed for generating a JWT token for client secret\n */\nexport type AppleDeveloperTeamID = string\n/**\n * Sign In with Apple Private Key Identifier needed for generating a JWT token for client secret\n */\nexport type ApplePrivateKeyIdentifier = string\n/**\n * Sign In with Apple Private Key needed for generating a JWT token for client secret\n */\nexport type ApplePrivateKey = string\n/**\n * The ID of the organization that this provider belongs to. Only effective in the Ory Network.\n */\nexport type OrganizationID = string\nexport type AdditionalClientIdsAllowedWhenUsingIDTokenSubmission = string[]\n/**\n * Can be either `userinfo` (calls the userinfo endpoint to get the claims) or `id_token` (takes the claims from the id token). It defaults to `id_token`\n */\nexport type ClaimsSource = \"id_token\" | \"userinfo\"\n/**\n * PKCE controls if the OpenID Connect OAuth2 flow should use PKCE (Proof Key for Code Exchange). IMPORTANT: If you set this to `force`, you must whitelist a different return URL for your OAuth2 client in the provider's configuration. Instead of <base-url>/self-service/methods/oidc/callback/<provider>, you must use <base-url>/self-service/methods/oidc/callback\n */\nexport type ProofKeyForCodeExchange = \"auto\" | \"never\" | \"force\"\n/**\n * The URL where the FedCM IdP configuration is located for the provider. This is only effective in the Ory Network.\n */\nexport type FederationConfigurationURL = string\n/**\n * Contains the orgin header to be used when exchanging a NetID FedCM token for an ID token\n */\nexport type NetIDTokenOriginHeader = string\n/**\n * A list and configuration of OAuth2 and OpenID Connect providers Ory Kratos should integrate with.\n */\nexport type OpenIDConnectAndOAuth2Providers = SelfServiceOIDCProvider[]\n/**\n * Controls how many records should be purged from one table during database cleanup task\n */\nexport type NumberOfRecordsToCleanInOneIteration = number\n/**\n * Controls the delay time between cleaning each table in one cleanup iteration\n */\nexport type DelayBetweenEachTableCleanups = string\n/**\n * Controls how old records do we want to leave\n */\nexport type RemoveRecordsOlderThan = string\n/**\n * DSN is used to specify the database credentials as a connection URI.\n */\nexport type DataSourceName = string\n/**\n * You can override certain or all message templates by pointing this key to the path where the templates are located.\n */\nexport type OverrideMessageTemplates = string\n/**\n * Defines how emails will be sent, either through SMTP (default) or HTTP.\n */\nexport type DeliveryStrategy = \"smtp\" | \"http\"\n/**\n * This URL will be used to send the emails to.\n */\nexport type HTTPAddressOfAPIEndpoint = string\n/**\n * Define which auth mechanism to use for auth with the HTTP email provider\n */\nexport type AuthMechanisms1 =\n  | WebHookAuthApiKeyProperties\n  | WebHookAuthBasicAuthProperties\n/**\n * This URI will be used to connect to the SMTP server. Use the scheme smtps for implicit TLS sessions or smtp for explicit StartTLS/cleartext sessions. Please note that TLS is always enforced with certificate trust verification by default for security reasons on both schemes. With the smtp scheme you can use the query parameter (`?disable_starttls=true`) to allow cleartext sessions or (`?disable_starttls=false`) to enforce StartTLS (default behaviour). Additionally, use the query parameter to allow (`?skip_ssl_verify=true`) or disallow (`?skip_ssl_verify=false`) self-signed TLS certificates (default behaviour) on both implicit and explicit TLS sessions.\n */\nexport type SMTPConnectionString = string\n/**\n * Path of the client X.509 certificate, in case of certificate based client authentication to the SMTP server.\n */\nexport type SMTPClientCertificatePath = string\n/**\n * Path of the client certificate private key, in case of certificate based client authentication to the SMTP server\n */\nexport type SMTPClientPrivateKeyPath = string\n/**\n * The recipient of an email will see this as the sender address.\n */\nexport type SMTPSenderAddress = string\n/**\n * The recipient of an email will see this as the sender name.\n */\nexport type SMTPSenderName = string\n/**\n * Identifier used in the SMTP HELO/EHLO command. Some SMTP relays require a unique identifier.\n */\nexport type SMTPHELOEHLOName = string\n/**\n * The channel id. Corresponds to the .via property of the identity schema for recovery, verification, etc. Currently only sms is supported.\n */\nexport type ChannelId = \"sms\"\n/**\n * The channel type. Currently only http is supported.\n */\nexport type ChannelType = \"http\"\n/**\n * If set, the login and registration flows will handle the Ory OAuth 2.0 & OpenID `login_challenge` query parameter to serve as an OpenID Connect Provider. This URL should point to Ory Hydra when you are not running on the Ory Network and be left untouched otherwise.\n */\nexport type OAuth20ProviderURL = string\n/**\n * Override the return_to query parameter with the OAuth2 provider request URL when perfoming an OAuth2 login flow.\n */\nexport type PersistOAuth2RequestBetweenFlows = boolean\n/**\n * The default consistency level to use when reading from the database. Defaults to `strong` to not break existing API contracts. Only set this to `eventual` if you can accept that other read APIs will suddenly return eventually consistent results. It is only effective in Ory Network.\n */\nexport type DefaultReadConsistencyLevel = \"strong\" | \"eventual\"\n/**\n * Disable request logging for /health/alive and /health/ready endpoints\n */\nexport type DisableHealthEndpointsRequestLogging = boolean\n/**\n * The URL where the admin endpoint is exposed at.\n */\nexport type AdminBaseURL = string\n/**\n * The host (interface) kratos' admin endpoint listens on.\n */\nexport type AdminHost = string\n/**\n * The port kratos' admin endpoint listens on.\n */\nexport type AdminPort = number\nexport type PrivateKeyPEM = TlsxSource\nexport type PathToPEMEncodedFle = string\n/**\n * The base64 string of the PEM-encoded file content. Can be generated using for example `base64 -i path/to/file.pem`.\n */\nexport type Base64EncodedInline = string\nexport type TLSCertificatePEM = TlsxSource\n/**\n * Disable request logging for /health/alive and /health/ready endpoints\n */\nexport type DisableHealthEndpointsRequestLogging1 = boolean\n/**\n * The URL where the endpoint is exposed at. This domain is used to generate redirects, form URLs, and more.\n */\nexport type BaseURL = string\n/**\n * The host (interface) kratos' public endpoint listens on.\n */\nexport type PublicHost = string\n/**\n * The port kratos' public endpoint listens on.\n */\nexport type PublicPort = number\n/**\n * If set will leak sensitive values (e.g. emails) in the logs.\n */\nexport type LeakSensitiveLogValues = boolean\n/**\n * Text to use, when redacting sensitive log value.\n */\nexport type SensitiveLogValueRedactionText = string\n/**\n * This Identity Schema will be used as the default for self-service flows. Its ID needs to exist in the \"schemas\" list.\n */\nexport type TheDefaultIdentitySchema = string\n/**\n * Note that identities that used the \"default_schema_url\" field in older kratos versions will be corrupted unless you specify their schema url with the id \"default\" in this list.\n *\n * @minItems 1\n */\nexport type AllJSONSchemasForIdentityTraits = [\n  {\n    id: TheSchemaSID\n    url: JSONSchemaURLForIdentityTraitsSchema\n    [k: string]: unknown | undefined\n  },\n  ...{\n    id: TheSchemaSID\n    url: JSONSchemaURLForIdentityTraitsSchema\n    [k: string]: unknown | undefined\n  }[],\n]\nexport type TheSchemaSID = string\n/**\n * URL for JSON Schema which describes a identity's traits. Can be a file path, a https URL, or a base64 encoded string.\n */\nexport type JSONSchemaURLForIdentityTraitsSchema = string\n/**\n * The first secret in the array is used for signing and encrypting things while all other keys are used to verify and decrypt older things that were signed with that old secret.\n */\nexport type DefaultEncryptionSigningSecrets = string[]\n/**\n * The first secret in the array is used for encrypting cookies while all other keys are used to decrypt older cookies that were signed with that old secret.\n */\nexport type SigningKeysForCookies = string[]\n/**\n * The first secret in the array is used for encryption data while all other keys are used to decrypt older data that were signed with.\n *\n * @minItems 1\n */\nexport type SecretsToUseForEncryptionByCipher = [string, ...string[]]\n/**\n * One of the values: argon2, bcrypt.\n * Any other hashes will be migrated to the set algorithm once an identity authenticates using their password.\n */\nexport type PasswordHashingAlgorithm = \"argon2\" | \"bcrypt\"\n/**\n * One of the values: noop, aes, xchacha20-poly1305\n */\nexport type CipheringAlgorithm = \"noop\" | \"aes\" | \"xchacha20-poly1305\"\n/**\n * Sets the cookie domain for session and CSRF cookies. Useful when dealing with subdomains. Use with care!\n */\nexport type HTTPCookieDomain = string\n/**\n * Sets the session and CSRF cookie path. Use with care!\n */\nexport type HTTPCookiePath = string\n/**\n * Sets the session secure flag. If unset, defaults to !dev mode.\n */\nexport type SessionCookieSecureFlag = string\n/**\n * Sets the session and CSRF cookie SameSite.\n */\nexport type HTTPCookieSameSiteConfiguration = \"Strict\" | \"Lax\" | \"None\"\nexport type TokenTimeToLive = string\nexport type JsonNetMapperURL = string\nexport type JSONWebKeySetURL = string\n/**\n * Defines how long a session is active. Once that lifespan has been reached, the user needs to sign in again.\n */\nexport type SessionLifespan = string\n/**\n * Sets the session cookie domain. Useful when dealing with subdomains. Use with care! Overrides `cookies.domain`.\n */\nexport type SessionCookieDomain = string\n/**\n * Sets the session cookie name. Use with care!\n */\nexport type SessionCookieName = string\n/**\n * If set to true will persist the cookie in the end-user's browser using the `max-age` parameter which is set to the `session.lifespan` value. Persistent cookies are not deleted when the browser is closed (e.g. on reboot or alt+f4). This option affects the Ory OAuth2 and OpenID Provider's remember feature as well.\n */\nexport type MakeSessionCookiePersistent = boolean\n/**\n * Sets the session cookie path. Use with care! Overrides `cookies.path`.\n */\nexport type SessionCookiePath = string\n/**\n * Sets the session secure flag. If unset, defaults to !dev mode.\n */\nexport type SessionCookieSecureFlag1 = string\n/**\n * Sets the session cookie SameSite. Overrides `cookies.same_site`.\n */\nexport type SessionCookieSameSiteConfiguration = \"Strict\" | \"Lax\" | \"None\"\n/**\n * Sets when a session can be extended. Settings this value to `24h` will prevent the session from being extended before until 24 hours before it expires. This setting prevents excessive writes to the database. We highly recommend setting this value.\n */\nexport type EarliestPossibleSessionExtension = string\n/**\n * SemVer according to https://semver.org/ prefixed with `v` as in our releases.\n */\nexport type TheKratosVersionThisConfigIsWrittenFor = string\n/**\n * The port the courier's metrics endpoint listens on (0/disabled by default). This is a CLI flag and environment variable and can not be set using the config file.\n */\nexport type MetricsPort = number\n/**\n * Disallow all outgoing HTTP calls to private IP ranges. This feature can help protect against SSRF attacks.\n */\nexport type DisallowPrivateIPRanges = boolean\n/**\n * Allows the given URLs to be called despite them being in the private IP range. URLs need to have an exact and case-sensitive match to be excempt.\n */\nexport type AddExemptURLsToPrivateIPRanges = string[]\n/**\n * List of request headers that are forwarded to the web hook target in canonical form.\n */\nexport type AllowedRequestHeaders = string[]\n/**\n * If enabled allows Ory Sessions to be cached. Only effective in the Ory Network.\n */\nexport type EnableOrySessionsCaching = boolean\n/**\n * Set how long Ory Sessions are cached on the edge. If unset, the session expiry will be used. Only effective in the Ory Network.\n */\nexport type SetOrySessionEdgeCachingMaximumAge = string\n/**\n * If enabled allows new flow transitions using `continue_with` items.\n */\nexport type EnableNewFlowTransitionsUsingContinueWithItems = boolean\n/**\n * If enabled allows faster session extension by skipping the session lookup. Disabling this feature will be deprecated in the future.\n */\nexport type EnableFasterSessionExtension = boolean\n/**\n * The node group to use for registration flows. Previously, the node group for the password method's profile fields was `password`. Going forward, it will be `default`. This switch can toggle between those two for backwards compatibility\n */\nexport type RegistrationNodeGroup = \"password\" | \"default\"\n/**\n * Please use selfservice.methods.b2b instead. This key will be removed. Only effective in the Ory Network.\n */\nexport type Organizations = unknown[]\n/**\n * A fallback URL template used when looking up identity schemas.\n */\nexport type FallbackURLTemplateForIdentitySchemas = string\n\nexport interface OryKratosConfiguration2 {\n  selfservice: {\n    default_browser_return_url: RedirectBrowsersToSetURLPerDefault\n    allowed_return_urls?: AllowedReturnToURLs\n    flows?: {\n      settings?: {\n        ui_url?: URLOfTheSettingsPage\n        lifespan?: string\n        privileged_session_max_age?: string\n        required_aal?: RequiredAuthenticatorAssuranceLevel\n        after?: SelfServiceAfterSettings\n        before?: SelfServiceBeforeSettings\n      }\n      logout?: {\n        after?: {\n          default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n        }\n      }\n      registration?: {\n        enabled?: EnableUserRegistration\n        login_hints?: ProvideLoginHintsOnFailedRegistration\n        ui_url?: RegistrationUIURL\n        lifespan?: string\n        before?: SelfServiceBeforeRegistration\n        after?: SelfServiceAfterRegistration\n        enable_legacy_one_step?: DisableTwoStepRegistration\n        style?: RegistrationFlowStyle\n      }\n      login?: {\n        ui_url?: LoginUIURL\n        lifespan?: string\n        style?: LoginFlowStyle\n        before?: SelfServiceBeforeLogin\n        after?: SelfServiceAfterLogin\n      }\n      verification?: EmailAndPhoneVerificationAndAccountActivationConfiguration\n      recovery?: AccountRecoveryConfiguration\n      error?: {\n        ui_url?: OryKratosErrorUIURL\n      }\n    }\n    methods?: {\n      b2b?: SingleSignOnForB2B\n      profile?: {\n        enabled?: EnablesProfileManagementMethod\n      }\n      link?: {\n        enabled?: EnablesLinkMethod\n        config?: LinkConfiguration\n      }\n      code?:\n        | {\n            passwordless_enabled?: true\n            mfa_enabled?: false\n            [k: string]: unknown | undefined\n          }\n        | {\n            mfa_enabled?: true\n            passwordless_enabled?: false\n            [k: string]: unknown | undefined\n          }\n        | {\n            mfa_enabled?: false\n            passwordless_enabled?: false\n            [k: string]: unknown | undefined\n          }\n      password?: {\n        enabled?: EnablesUsernameEmailAndPasswordMethod\n        config?: PasswordConfiguration\n      }\n      totp?: {\n        enabled?: EnablesTheTOTPMethod\n        config?: TOTPConfiguration\n      }\n      lookup_secret?: {\n        enabled?: EnablesTheLookupSecretMethod\n      }\n      webauthn?: {\n        enabled?: EnablesTheWebAuthnMethod\n        config?: WebAuthnConfiguration\n      }\n      passkey?: {\n        enabled?: EnablesThePasskeyMethod\n        config?: PasskeyConfiguration\n      }\n      oidc?: SpecifyOpenIDConnectAndOAuth2Configuration\n    }\n  }\n  database?: DatabaseRelatedConfiguration\n  dsn: DataSourceName\n  courier?: CourierConfiguration\n  oauth2_provider?: OAuth2ProviderConfiguration\n  preview?: ConfigurePreviewFeatures\n  serve?: {\n    admin?: {\n      request_log?: {\n        disable_for_health?: DisableHealthEndpointsRequestLogging\n      }\n      base_url?: AdminBaseURL\n      host?: AdminHost\n      port?: AdminPort\n      socket?: Socket\n      tls?: HTTPS\n    }\n    public?: {\n      request_log?: {\n        disable_for_health?: DisableHealthEndpointsRequestLogging1\n      }\n      /**\n       * Configures Cross Origin Resource Sharing for public endpoints.\n       */\n      cors?: {\n        /**\n         * Sets whether CORS is enabled.\n         */\n        enabled?: boolean\n        /**\n         * A list of origins a cross-domain request can be executed from. If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin.\n         */\n        allowed_origins?: ((string | \"*\") & string)[]\n        /**\n         * A list of HTTP methods the user agent is allowed to use with cross-domain requests.\n         */\n        allowed_methods?: (\n          | \"POST\"\n          | \"GET\"\n          | \"PUT\"\n          | \"PATCH\"\n          | \"DELETE\"\n          | \"CONNECT\"\n          | \"HEAD\"\n          | \"OPTIONS\"\n          | \"TRACE\"\n        )[]\n        /**\n         * A list of non simple headers the client is allowed to use with cross-domain requests.\n         */\n        allowed_headers?: string[]\n        /**\n         * Sets which headers are safe to expose to the API of a CORS API specification.\n         */\n        exposed_headers?: string[]\n        /**\n         * Sets whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.\n         */\n        allow_credentials?: boolean\n        /**\n         * TODO\n         */\n        options_passthrough?: boolean\n        /**\n         * Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request is preceded by a preflight request.\n         */\n        max_age?: number\n        /**\n         * Adds additional log output to debug server side CORS issues.\n         */\n        debug?: boolean\n      }\n      base_url?: BaseURL\n      host?: PublicHost\n      port?: PublicPort\n      socket?: Socket\n      tls?: HTTPS\n    }\n  }\n  tracing?: OryTracingConfig\n  log?: Log\n  identity: {\n    default_schema_id?: TheDefaultIdentitySchema\n    schemas: AllJSONSchemasForIdentityTraits\n  }\n  secrets?: {\n    default?: DefaultEncryptionSigningSecrets\n    cookie?: SigningKeysForCookies\n    cipher?: SecretsToUseForEncryptionByCipher\n  }\n  hashers?: HashingAlgorithmConfiguration\n  ciphers?: CipherAlgorithmConfiguration\n  cookies?: HTTPCookieConfiguration\n  session?: {\n    whoami?: WhoAmIToSessionSettings\n    lifespan?: SessionLifespan\n    cookie?: {\n      domain?: SessionCookieDomain\n      name?: SessionCookieName\n      persistent?: MakeSessionCookiePersistent\n      path?: SessionCookiePath\n      secure?: SessionCookieSecureFlag1\n      same_site?: SessionCookieSameSiteConfiguration\n    }\n    earliest_possible_extend?: EarliestPossibleSessionExtension\n  }\n  security?: {\n    account_enumeration?: {\n      /**\n       * Mitigate account enumeration by making it harder to figure out if an identifier (email, phone number) exists or not. Enabling this setting degrades user experience. This setting does not mitigate all possible attack vectors yet.\n       */\n      mitigate?: boolean\n      [k: string]: unknown | undefined\n    }\n    [k: string]: unknown | undefined\n  }\n  version?: TheKratosVersionThisConfigIsWrittenFor\n  dev?: boolean\n  help?: boolean\n  /**\n   * This is a CLI flag and environment variable and can not be set using the config file.\n   */\n  \"sqa-opt-out\"?: boolean\n  /**\n   * This is a CLI flag and environment variable and can not be set using the config file.\n   */\n  \"watch-courier\"?: boolean\n  \"expose-metrics-port\"?: MetricsPort\n  /**\n   * This is a CLI flag and environment variable and can not be set using the config file.\n   */\n  config?: string[]\n  clients?: GlobalOutgoingNetworkSettings\n  feature_flags?: FeatureFlags\n  organizations?: Organizations\n  enterprise?: EnterpriseFeatures\n  revision?: ConfigRevision\n}\nexport interface SelfServiceAfterSettings {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  password?: SelfServiceAfterSettingsAuthMethod\n  totp?: SelfServiceAfterSettingsAuthMethod\n  oidc?: SelfServiceAfterSettingsAuthMethod\n  webauthn?: SelfServiceAfterSettingsAuthMethod\n  passkey?: SelfServiceAfterSettingsAuthMethod\n  lookup_secret?: SelfServiceAfterSettingsAuthMethod\n  profile?: SelfServiceAfterSettingsMethod\n  hooks?: SelfServiceHooks\n}\nexport interface SelfServiceAfterSettingsAuthMethod {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  hooks?: (SelfServiceWebHook | SelfServiceSessionRevokerHook)[]\n}\nexport interface SelfServiceWebHook {\n  hook: \"web_hook\"\n  config: WebHookConfiguration\n}\nexport interface SelfServiceSessionRevokerHook {\n  hook: \"revoke_active_sessions\"\n}\nexport interface SelfServiceAfterSettingsMethod {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  hooks?: (SelfServiceWebHook | B2BSSOHook)[]\n}\nexport interface B2BSSOHook {\n  hook: \"b2b_sso\" | \"organization\"\n  config: {\n    [k: string]: unknown | undefined\n  }\n}\nexport interface SelfServiceBeforeSettings {\n  hooks?: SelfServiceHooks\n}\nexport interface SelfServiceBeforeRegistration {\n  hooks?: SelfServiceHooks\n}\nexport interface SelfServiceAfterRegistration {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  password?: SelfServiceAfterRegistrationMethod\n  webauthn?: SelfServiceAfterRegistrationMethod\n  passkey?: SelfServiceAfterRegistrationMethod\n  oidc?: SelfServiceAfterRegistrationMethod\n  code?: SelfServiceAfterRegistrationMethod\n  hooks?: SelfServiceHooks\n}\nexport interface SelfServiceAfterRegistrationMethod {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  hooks?: (\n    | SelfServiceSessionIssuerHook\n    | SelfServiceWebHook\n    | SelfServiceShowVerificationUIHook\n    | B2BSSOHook\n  )[]\n}\nexport interface SelfServiceSessionIssuerHook {\n  hook: \"session\"\n}\nexport interface SelfServiceShowVerificationUIHook {\n  hook: \"show_verification_ui\"\n}\nexport interface SelfServiceBeforeLogin {\n  hooks?: SelfServiceHooks\n}\nexport interface SelfServiceAfterLogin {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  password?: SelfServiceAfterDefaultLoginMethod\n  webauthn?: SelfServiceAfterDefaultLoginMethod\n  passkey?: SelfServiceAfterDefaultLoginMethod\n  oidc?: SelfServiceAfterOIDCLoginMethod\n  code?: SelfServiceAfterDefaultLoginMethod\n  totp?: SelfServiceAfterDefaultLoginMethod\n  lookup_secret?: SelfServiceAfterDefaultLoginMethod\n  hooks?: (\n    | SelfServiceWebHook\n    | SelfServiceSessionRevokerHook\n    | SelfServiceRequireVerifiedAddressHook\n    | SelfServiceVerificationHook\n    | SelfServiceShowVerificationUIHook\n    | B2BSSOHook\n  )[]\n}\nexport interface SelfServiceAfterDefaultLoginMethod {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  hooks?: (\n    | SelfServiceSessionRevokerHook\n    | SelfServiceRequireVerifiedAddressHook\n    | SelfServiceWebHook\n    | SelfServiceVerificationHook\n    | SelfServiceShowVerificationUIHook\n    | B2BSSOHook\n  )[]\n}\nexport interface SelfServiceRequireVerifiedAddressHook {\n  hook: \"require_verified_address\"\n}\nexport interface SelfServiceVerificationHook {\n  hook: \"verification\"\n}\nexport interface SelfServiceAfterOIDCLoginMethod {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  hooks?: (\n    | SelfServiceSessionRevokerHook\n    | SelfServiceWebHook\n    | SelfServiceRequireVerifiedAddressHook\n    | B2BSSOHook\n  )[]\n}\nexport interface EmailAndPhoneVerificationAndAccountActivationConfiguration {\n  enabled?: EnableEmailPhoneVerification\n  ui_url?: VerifyUIURL\n  after?: SelfServiceAfterVerification\n  lifespan?: SelfServiceVerificationRequestLifespan\n  before?: SelfServiceBeforeVerification\n  use?: VerificationStrategy\n  notify_unknown_recipients?: NotifyUnknownRecipients\n}\nexport interface SelfServiceAfterVerification {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  hooks?: SelfServiceHooks\n}\nexport interface SelfServiceBeforeVerification {\n  hooks?: SelfServiceHooks\n}\nexport interface AccountRecoveryConfiguration {\n  enabled?: EnableAccountRecovery\n  ui_url?: RecoveryUIURL\n  after?: SelfServiceAfterRecovery\n  lifespan?: SelfServiceRecoveryRequestLifespan\n  before?: SelfServiceBeforeRecovery\n  use?: RecoveryStrategy\n  notify_unknown_recipients?: NotifyUnknownRecipients1\n}\nexport interface SelfServiceAfterRecovery {\n  default_browser_return_url?: RedirectBrowsersToSetURLPerDefault\n  hooks?: SelfServiceAfterRecoveryHooks\n}\nexport interface SelfServiceBeforeRecovery {\n  hooks?: SelfServiceHooks\n}\n/**\n * Single Sign-On for B2B allows your customers to bring their own (workforce) identity server (e.g. OneLogin). This feature is not available in the open source licensed code.\n */\nexport interface SingleSignOnForB2B {\n  config?: {\n    organizations?: {\n      /**\n       * The ID of the organization.\n       */\n      id?: string\n      /**\n       * The label of the organization.\n       */\n      label?: string\n      domains?: string[]\n      [k: string]: unknown | undefined\n    }[]\n  }\n}\n/**\n * Additional configuration for the link strategy.\n */\nexport interface LinkConfiguration {\n  base_url?: OverrideTheBaseURLWhichShouldBeUsedAsTheBaseForRecoveryAndVerificationLinks\n  lifespan?: HowLongALinkIsValidFor\n  [k: string]: unknown | undefined\n}\n/**\n * Define how passwords are validated.\n */\nexport interface PasswordConfiguration {\n  haveibeenpwned_host?: CustomHaveibeenpwnedHost\n  haveibeenpwned_enabled?: EnableTheHaveIBeenPwnedAPI\n  max_breaches?: AllowPasswordBreaches\n  ignore_network_errors?: IgnoreLookupNetworkErrors\n  min_password_length?: MinimumPasswordLength\n  identifier_similarity_check_enabled?: EnablePasswordIdentifierSimilarityCheck\n  migrate_hook?: {\n    enabled?: EnablePasswordMigration\n    config?: {\n      /**\n       * The URL the password migration hook should call\n       */\n      url?: string\n      /**\n       * The HTTP method to use (GET, POST, etc).\n       */\n      method?: \"POST\"\n      /**\n       * The HTTP headers that must be applied to the password migration hook.\n       */\n      headers?: {\n        [k: string]: string | undefined\n      }\n      /**\n       * Emit tracing events for this hook on delivery or error\n       */\n      emit_analytics_event?: boolean\n      auth?: AuthMechanisms\n      additionalProperties?: false\n    }\n  }\n}\nexport interface WebHookAuthApiKeyProperties {\n  type: \"api_key\"\n  config: {\n    /**\n     * The name of the api key\n     */\n    name: string\n    /**\n     * The value of the api key\n     */\n    value: string\n    /**\n     * How the api key should be transferred\n     */\n    in: \"header\" | \"cookie\"\n  }\n}\nexport interface WebHookAuthBasicAuthProperties {\n  type: \"basic_auth\"\n  config: {\n    /**\n     * user name for basic auth\n     */\n    user: string\n    /**\n     * password for basic auth\n     */\n    password: string\n  }\n}\nexport interface TOTPConfiguration {\n  issuer?: TOTPIssuer\n}\nexport interface WebAuthnConfiguration {\n  passwordless?: UseForPasswordlessFlows\n  rp?: RelyingPartyRPConfig\n}\nexport interface PasskeyConfiguration {\n  rp?: RelyingPartyRPConfig1\n}\nexport interface RelyingPartyRPConfig1 {\n  display_name: RelyingPartyDisplayName\n  id: RelyingPartyIdentifier\n  origins?: RelyingPartyOrigins\n  [k: string]: unknown | undefined\n}\nexport interface SpecifyOpenIDConnectAndOAuth2Configuration {\n  enabled?: EnablesOpenIDConnectMethod\n  config?: {\n    base_redirect_uri?: BaseURLForOAuth2RedirectURIs\n    providers?: OpenIDConnectAndOAuth2Providers\n  }\n}\n/**\n * The OpenID Connect claims and optionally their properties which should be included in the id_token or returned from the UserInfo Endpoint.\n */\nexport interface OpenIDConnectClaims {\n  /**\n   * This interface was referenced by `OpenIDConnectClaims`'s JSON-Schema definition\n   * via the `patternProperty` \"^userinfo$|^id_token$\".\n   */\n  [k: string]: {\n    /**\n     * This interface was referenced by `undefined`'s JSON-Schema definition\n     * via the `patternProperty` \".*\".\n     */\n    [k: string]: null | {\n      /**\n       * Indicates whether the Claim being requested is an Essential Claim.\n       */\n      essential?: boolean\n      /**\n       * Requests that the Claim be returned with a particular value.\n       */\n      value?: {\n        [k: string]: unknown | undefined\n      }\n      /**\n       * Requests that the Claim be returned with one of a set of values, with the values appearing in order of preference.\n       */\n      values?: {\n        [k: string]: unknown | undefined\n      }[]\n    }\n  }\n}\n/**\n * Miscellaneous settings used in database related tasks (cleanup, etc.)\n */\nexport interface DatabaseRelatedConfiguration {\n  cleanup?: DatabaseCleanupSettings\n}\n/**\n * Settings that controls how the database cleanup process is configured (delays, batch size, etc.)\n */\nexport interface DatabaseCleanupSettings {\n  batch_size?: NumberOfRecordsToCleanInOneIteration\n  sleep?: DelaysBetweenVariousDatabaseCleanupPhases\n  older_than?: RemoveRecordsOlderThan\n  [k: string]: unknown | undefined\n}\n/**\n * Configures delays between each step of the cleanup process. It is useful to tune the process so it will be efficient and performant.\n */\nexport interface DelaysBetweenVariousDatabaseCleanupPhases {\n  tables?: DelayBetweenEachTableCleanups\n  [k: string]: unknown | undefined\n}\n/**\n * The courier is responsible for sending and delivering messages over email, sms, and other means.\n */\nexport interface CourierConfiguration {\n  templates?: {\n    recovery?: CourierTemplates\n    recovery_code?: CourierTemplates\n    verification?: CourierTemplates\n    verification_code?: CourierTemplates\n    registration_code?: {\n      valid?: {\n        email: EmailCourierTemplate\n        sms?: SmsCourierTemplate\n      }\n    }\n    login_code?: {\n      valid?: {\n        email: EmailCourierTemplate\n        sms?: SmsCourierTemplate\n      }\n    }\n  }\n  template_override_path?: OverrideMessageTemplates\n  /**\n   * Defines the maximum number of times the sending of a message is retried after it failed before it is marked as abandoned\n   */\n  message_retries?: number\n  /**\n   * Configures the dispatch worker.\n   */\n  worker?: {\n    /**\n     * Defines how many messages are pulled from the queue at once.\n     */\n    pull_count?: number\n    /**\n     * Defines how long the worker waits before pulling messages from the queue again.\n     */\n    pull_wait?: string\n    [k: string]: unknown | undefined\n  }\n  delivery_strategy?: DeliveryStrategy\n  http?: HTTPConfiguration\n  smtp?: SMTPConfiguration\n  channels?: CourierChannelConfiguration[]\n}\nexport interface CourierTemplates {\n  invalid?: {\n    email: EmailCourierTemplate\n  }\n  valid?: {\n    email: EmailCourierTemplate\n    sms?: SmsCourierTemplate\n  }\n}\nexport interface EmailCourierTemplate {\n  body?: {\n    /**\n     * The fallback template for email clients that do not support html.\n     */\n    plaintext?: string\n    /**\n     * The default template used for sending out emails. The template can contain HTML\n     */\n    html?: string\n  }\n  subject?: string\n}\nexport interface SmsCourierTemplate {\n  body?: {\n    /**\n     * A template send to the SMS provider.\n     */\n    plaintext?: string\n  }\n}\n/**\n * Configures outgoing emails using HTTP.\n */\nexport interface HTTPConfiguration {\n  request_config?: HttpRequestConfig\n}\nexport interface HttpRequestConfig {\n  url?: HTTPAddressOfAPIEndpoint\n  /**\n   * The HTTP method to use (GET, POST, etc). Defaults to POST.\n   */\n  method?: string\n  /**\n   * The HTTP headers that must be applied to request\n   */\n  headers?: {\n    [k: string]: string | undefined\n  }\n  /**\n   * URI pointing to the jsonnet template used for payload generation. Only used for those HTTP methods, which support HTTP body payloads\n   */\n  body?: string\n  auth?: AuthMechanisms1\n  additionalProperties?: false\n}\n/**\n * Configures outgoing emails using the SMTP protocol.\n */\nexport interface SMTPConfiguration {\n  connection_uri?: SMTPConnectionString\n  client_cert_path?: SMTPClientCertificatePath\n  client_key_path?: SMTPClientPrivateKeyPath\n  from_address?: SMTPSenderAddress\n  from_name?: SMTPSenderName\n  headers?: SMTPHeaders\n  local_name?: SMTPHELOEHLOName\n}\n/**\n * These headers will be passed in the SMTP conversation -- e.g. when using the AWS SES SMTP interface for cross-account sending.\n */\nexport interface SMTPHeaders {\n  [k: string]: string | undefined\n}\nexport interface CourierChannelConfiguration {\n  id: ChannelId\n  type?: ChannelType\n  request_config: HttpRequestConfig\n}\nexport interface OAuth2ProviderConfiguration {\n  url?: OAuth20ProviderURL\n  headers?: HTTPRequestHeaders\n  override_return_to?: PersistOAuth2RequestBetweenFlows\n}\n/**\n * These headers will be passed in HTTP request to the OAuth2 Provider.\n */\nexport interface HTTPRequestHeaders {\n  [k: string]: string | undefined\n}\nexport interface ConfigurePreviewFeatures {\n  default_read_consistency_level?: DefaultReadConsistencyLevel\n  [k: string]: unknown | undefined\n}\n/**\n * Sets the permissions of the unix socket\n */\nexport interface Socket {\n  /**\n   * Owner of unix socket. If empty, the owner will be the user running Kratos.\n   */\n  owner?: string\n  /**\n   * Group of unix socket. If empty, the group will be the primary group of the user running Kratos.\n   */\n  group?: string\n  /**\n   * Mode of unix socket in numeric form\n   */\n  mode?: number\n}\n/**\n * Configure HTTP over TLS (HTTPS). All options can also be set using environment variables by replacing dots (`.`) with underscores (`_`) and uppercasing the key. For example, `some.prefix.tls.key.path` becomes `export SOME_PREFIX_TLS_KEY_PATH`. If all keys are left undefined, TLS will be disabled.\n */\nexport interface HTTPS {\n  key?: PrivateKeyPEM\n  cert?: TLSCertificatePEM\n}\nexport interface TlsxSource {\n  path?: PathToPEMEncodedFle\n  base64?: Base64EncodedInline\n}\n/**\n * Configure distributed tracing using OpenTelemetry\n */\nexport interface OryTracingConfig {\n  /**\n   * Set this to the tracing backend you wish to use. Supports Jaeger, Zipkin, and OTEL.\n   */\n  provider?: \"jaeger\" | \"otel\" | \"zipkin\"\n  /**\n   * Specifies the service name to use on the tracer.\n   */\n  service_name?: string\n  /**\n   * Specifies the deployment environment to use on the tracer.\n   */\n  deployment_environment?: string\n  providers?: {\n    /**\n     * Configures the jaeger tracing backend.\n     */\n    jaeger?: {\n      /**\n       * The address of the jaeger-agent where spans should be sent to.\n       */\n      local_agent_address?: (\n        | IPv6AddressAndPort\n        | IPv4AddressAndPort\n        | HostnameAndPort\n      ) &\n        string\n      sampling?: {\n        /**\n         * The address of jaeger-agent's HTTP sampling server\n         */\n        server_url?: string\n        /**\n         * Trace Id ratio sample\n         */\n        trace_id_ratio?: number\n      }\n    }\n    /**\n     * Configures the zipkin tracing backend.\n     */\n    zipkin?: {\n      /**\n       * The address of the Zipkin server where spans should be sent to.\n       */\n      server_url?: string\n      sampling?: {\n        /**\n         * Sampling ratio for spans.\n         */\n        sampling_ratio?: number\n      }\n    }\n    /**\n     * Configures the OTLP tracing backend.\n     */\n    otlp?: {\n      /**\n       * The endpoint of the OTLP exporter (HTTP) where spans should be sent to.\n       */\n      server_url?: (\n        | IPv6AddressAndPort1\n        | IPv4AddressAndPort1\n        | HostnameAndPort1\n      ) &\n        string\n      /**\n       * Will use HTTP if set to true; defaults to HTTPS.\n       */\n      insecure?: boolean\n      sampling?: {\n        /**\n         * Sampling ratio for spans.\n         */\n        sampling_ratio?: number\n      }\n      authorization_header?: string\n    }\n  }\n}\nexport interface IPv6AddressAndPort {\n  [k: string]: unknown | undefined\n}\nexport interface IPv4AddressAndPort {\n  [k: string]: unknown | undefined\n}\nexport interface HostnameAndPort {\n  [k: string]: unknown | undefined\n}\nexport interface IPv6AddressAndPort1 {\n  [k: string]: unknown | undefined\n}\nexport interface IPv4AddressAndPort1 {\n  [k: string]: unknown | undefined\n}\nexport interface HostnameAndPort1 {\n  [k: string]: unknown | undefined\n}\n/**\n * Configure logging using the following options. Logging will always be sent to stdout and stderr.\n */\nexport interface Log {\n  /**\n   * Debug enables stack traces on errors. Can also be set using environment variable LOG_LEVEL.\n   */\n  level?: \"trace\" | \"debug\" | \"info\" | \"warning\" | \"error\" | \"fatal\" | \"panic\"\n  leak_sensitive_values?: LeakSensitiveLogValues\n  redaction_text?: SensitiveLogValueRedactionText\n  /**\n   * The log format can either be text or JSON.\n   */\n  format?: \"json\" | \"text\"\n}\nexport interface HashingAlgorithmConfiguration {\n  algorithm?: PasswordHashingAlgorithm\n  argon2?: ConfigurationForTheArgon2IdHasher\n  bcrypt?: ConfigurationForTheBcryptHasherMinimumIs4WhenDevFlagIsUsedAnd12Otherwise\n}\nexport interface ConfigurationForTheArgon2IdHasher {\n  memory?: string\n  iterations?: number\n  /**\n   * Number of parallel workers, defaults to 2*runtime.NumCPU().\n   */\n  parallelism?: number\n  salt_length?: number\n  key_length?: number\n  /**\n   * The time a hashing operation (~login latency) should take.\n   */\n  expected_duration?: string\n  /**\n   * The standard deviation expected for hashing operations. If this value is exceeded you will be warned in the logs to adjust the parameters.\n   */\n  expected_deviation?: string\n  /**\n   * The memory dedicated for Kratos. As password hashing is very resource intense, Kratos will monitor the memory consumption and warn about high values.\n   */\n  dedicated_memory?: string\n}\nexport interface ConfigurationForTheBcryptHasherMinimumIs4WhenDevFlagIsUsedAnd12Otherwise {\n  cost: number\n}\nexport interface CipherAlgorithmConfiguration {\n  algorithm?: CipheringAlgorithm\n  [k: string]: unknown | undefined\n}\n/**\n * Configure the HTTP Cookies. Applies to both CSRF and session cookies.\n */\nexport interface HTTPCookieConfiguration {\n  domain?: HTTPCookieDomain\n  path?: HTTPCookiePath\n  secure?: SessionCookieSecureFlag\n  same_site?: HTTPCookieSameSiteConfiguration\n}\n/**\n * Control how the `/sessions/whoami` endpoint is behaving.\n */\nexport interface WhoAmIToSessionSettings {\n  required_aal?: RequiredAuthenticatorAssuranceLevel\n  tokenizer?: TokenizerConfiguration\n}\n/**\n * Configure the tokenizer, responsible for converting a session into a token format such as JWT.\n */\nexport interface TokenizerConfiguration {\n  templates?: TokenizerTemplates\n  [k: string]: unknown | undefined\n}\n/**\n * A list of different templates that govern how a session is converted to a token format.\n */\nexport interface TokenizerTemplates {\n  /**\n   * This interface was referenced by `TokenizerTemplates`'s JSON-Schema definition\n   * via the `patternProperty` \"[a-zA-Z0-9-_.]+\".\n   */\n  [k: string]: {\n    ttl?: TokenTimeToLive\n    claims_mapper_url?: JsonNetMapperURL\n    jwks_url: JSONWebKeySetURL\n    [k: string]: unknown | undefined\n  }\n}\n/**\n * Configure how outgoing network calls behave.\n */\nexport interface GlobalOutgoingNetworkSettings {\n  http?: GlobalHTTPClientConfiguration\n  web_hook?: GlobalWebHookHTTPClientConfiguration\n  [k: string]: unknown | undefined\n}\n/**\n * Configure how outgoing HTTP calls behave.\n */\nexport interface GlobalHTTPClientConfiguration {\n  disallow_private_ip_ranges?: DisallowPrivateIPRanges\n  private_ip_exception_urls?: AddExemptURLsToPrivateIPRanges\n  [k: string]: unknown | undefined\n}\n/**\n * Configure the global HTTP client of the web_hook action.\n */\nexport interface GlobalWebHookHTTPClientConfiguration {\n  header_allowlist?: AllowedRequestHeaders\n  [k: string]: unknown | undefined\n}\nexport interface FeatureFlags {\n  cacheable_sessions?: EnableOrySessionsCaching\n  cacheable_sessions_max_age?: SetOrySessionEdgeCachingMaximumAge\n  use_continue_with_transitions?: EnableNewFlowTransitionsUsingContinueWithItems\n  faster_session_extend?: EnableFasterSessionExtension\n  password_profile_registration_node_group?: RegistrationNodeGroup\n}\n/**\n * Specifies enterprise features. Only effective in the Ory Network or with a valid license.\n */\nexport interface EnterpriseFeatures {\n  identity_schema_fallback_url_template?: FallbackURLTemplateForIdentitySchemas\n}\n/**\n * Only used in tests\n */\nexport interface ConfigRevision {\n  [k: string]: unknown | undefined\n}\n"
  },
  {
    "path": "test/e2e/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"es5\", \"dom\"],\n    \"types\": [\"cypress\", \"node\"],\n    \"esModuleInterop\": true\n  },\n  \"include\": [\"**/*.ts\"]\n}\n"
  },
  {
    "path": "test/schema/README.md",
    "content": "# Schema Tests\n\nThis test works by validating cases of the payload against a schema.\nTo add a case, place a file in the `${schemaName}.schema.test.${success|failure}` folder.\nA success case should validate, a failure case should throw a validation error.\n\n## Handling of \"$ref\"\n\nTo allow testing definitions on their own, and reduce the size of individual cases,\nevery `\"$ref\"` in the schema will be replaced with `\"const\"` before validation.\nThis means that a case only has to define the pointer as a string. Example:\n\n```yaml\ndefault_browser_return_url: \"#/definitions/defaultReturnTo\"\nhooks:\n  - \"#/definitions/selfServiceSessionRevokerHook\"\n```\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/OIDCClaims.malformed.yaml",
    "content": "userinfo:\n  given_name:\n    essential: true\n    someInvalidProperty: 'invalid' # Invalid property\n  nickname: null\n  email:\n    essential: InvalidValueForEssential\n  email_verified:\n    essentials: true # key typo\n  picture: null\n  http://example/info/claims/groups: null\nid_token:\n  auth_time:\n    essential: true\n  acr:\n    values: ['urn:mace:incommon:iap:silver']\n  sub:\n    value: '248289761001'\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/courierTemplates.bodyHtmlMissing.yaml",
    "content": "valid:\n  email:\n    body:\n      plaintext: \"https://some-url\"\n    subject: \"https://some-url\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/courierTemplates.emailMissing.yaml",
    "content": "invalid:\n  another-courier:\nvalid:\n  email:\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/emailCourierTemplate.malformed.yaml",
    "content": "body:\n  plaintext: \"http://call-something\"\n  html: \"malformed uri\"\nsubject: \"something\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/root.SMSConfigmalformedURL.yaml",
    "content": "sms:\n  request_config:\n    url: \"malformed uri\"\n    method: POST\n    body: \"malformed uri\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/root.invalidTypes.yaml",
    "content": "selfservice:\n\n  strategies:\n    password:\n      enabled: 0\n    oidc:\n      enabled: 0\n      config:\n        providers:\n          - foo\n\n  logout:\n    redirect_to: 0\n\n  profile:\n    lifespan: 10\n\n  login:\n    lifespan: 10\n    before: foo\n    after: foo\n\n  registration:\n    lifespan: 10\n    before:\n      - foo\n    after: foo\n\ndsn: foo\n\ncourier:\n  template_override_path: foo\n  smtp:\n    connection_uri: 0\n    from_address: 0\n  message_retries: invalid-value\n\nserve:\n  admin:\n    host: 0\n    port: foo\n  public:\n    host: 0\n    port: foo\n\nurls:\n  self:\n    public: 1\n    admin: 1\n  mfa_ui: 1\n  login_ui: 1\n  settings_ui: 1\n  default_return_to: 1\n  registration_ui: 1\n  error_ui: 1\n  allowed_return_urls:\n    - 1\n    - 1\n\nlog:\n  level: 1\n  format: 1\n\nidentity:\n  traits:\n    default_schema_id: 1123\n    schemas:\n      - id: 1\n        url: 1\n\nsecrets:\n  session:\n    - 1\n\nhashers:\n  argon2:\n    memory: foo\n    iterations: foo\n    parallelism: foo\n    salt_length: foo\n    key_length: foo\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/root.invalidVersion.yaml",
    "content": "version: x1.0.0\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/root.missingCourierTemplatesVerificationInvalid.yaml",
    "content": "selfservice:\n  default_browser_return_url: \"#/definitions/defaultReturnTo\"\n\ndsn: foo\n\nidentity:\n  schemas:\n    - id: default\n      url: https://example.com\n\ncourier:\n  template_override_path: foo\n  smtp:\n    connection_uri: smtps://foo:bar@my-mailserver:1234/\n    from_address: no-reply@ory.kratos.sh\n  templates:\n    recovery: \"#/definitions/courierTemplates\"\n    verification:\n      valid:\n        email: \"#/definitions/emailCourierTemplate\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/root.requiresCourierRecovery.yml",
    "content": "selfservice:\n  default_browser_return_url: \"#/definitions/defaultReturnTo\"\n  flows:\n    recovery:\n      enabled: true\n\ndsn: foo\n\nidentity:\n  schemas:\n    - id: default\n      url: https://example.com\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/root.requiresCourierVerification.yml",
    "content": "selfservice:\n  default_browser_return_url: \"#/definitions/defaultReturnTo\"\n  flows:\n    verification:\n      enabled: true\n\ndsn: foo\n\nidentity:\n  schemas:\n    - id: default\n      url: https://example.com\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/selfServiceOIDCProvider.invalidTypes.yaml",
    "content": "id: 1\nprovider: unknown\nclient_id: 1\nclient_secret: 1\nissuer_url: not an URL\nauth_url: not an URL\ntoken_url: not an URL\nmapper_url: not an URL\nscope:\n  - 1\nmicrosoft_tenant: 1\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/selfServiceOIDCProvider.privateKeyIdNotWithGithub.yaml",
    "content": "id: foo\nprovider: github\nclient_id: asdf\nclient_secret: asdf\nmapper_url: file://./mapper_file\napple_private_key_id: org\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/selfServiceOIDCProvider.privateKeyIdRequiredWithApple.yaml",
    "content": "id: foo\nprovider: apple\nclient_id: foo\nteam_id: foo\nprivate_key: foo\nmapper_url: https://example.com\nscope:\n  - foo\n  - bar\nrequested_claims: \"#/definitions/OIDCClaims\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/selfServiceOIDCProvider.privateKeyNotWithGithub.yaml",
    "content": "id: foo\nprovider: github\nclient_id: asdf\nclient_secret: asdf\nmapper_url: file://./mapper_file\nprivate_key: org\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/selfServiceOIDCProvider.privateKeyRequiredWithApple.yaml",
    "content": "id: foo\nprovider: apple\nclient_id: foo\napple_team_id: foo\napple_private_key_id: foo\nmapper_url: https://example.com\nscope:\n  - foo\n  - bar\nrequested_claims: \"#/definitions/OIDCClaims\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/selfServiceOIDCProvider.teamIdNotWithGithub.yaml",
    "content": "id: foo\nprovider: github\nclient_id: asdf\nclient_secret: asdf\nmapper_url: file://./mapper_file\nteam_id: org\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/selfServiceOIDCProvider.teamIdRequiredWithApple.yaml",
    "content": "id: foo\nprovider: apple\nclient_id: foo\napple_private_key_id: foo\napple_private_key: foo\nmapper_url: https://example.com\nscope:\n  - foo\n  - bar\nrequested_claims: \"#/definitions/OIDCClaims\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/selfServiceOIDCProvider.tenantNotWithGithub.yaml",
    "content": "id: foo\nprovider: github\nclient_id: asdf\nclient_secret: asdf\nmapper_url: file://./mapper_file\nmicrosoft_tenant: org\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.failure/selfServiceOIDCProvider.tenantRequiredWithMicrosoft.yaml",
    "content": "id: foo\nprovider: microsoft\nclient_id: asdf\nclient_secret: asdf\nmapper_url: file://./mapper_file\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/OIDCClaims.full.yaml",
    "content": "userinfo:\n  given_name:\n    essential: true\n  nickname: null\n  email:\n    essential: true\n  email_verified:\n    essential: true\n  picture: null\n  http://example/info/claims/groups: null\nid_token:\n  auth_time:\n    essential: true\n  acr:\n    values: ['urn:mace:incommon:iap:silver']\n  sub:\n    essential: true\n    value: 248289761001\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/courierTemplates.full.yaml",
    "content": "invalid:\n  email: \"#/definitions/emailCourierTemplate\"\nvalid:\n  email: \"#/definitions/emailCourierTemplate\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/defaultReturnTo.URI-Reference.yaml",
    "content": "/dashboard\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/defaultReturnTo.URL.yaml",
    "content": "https://my-app.com/dashboard\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/emailCourierTemplate.full.yaml",
    "content": "body:\n  plaintext: \"base64://\"\n  html: \"base64://\"\nsubject: \"http://\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/emailCourierTemplate.withMissingBody.yaml",
    "content": "subject: \"http://\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/emailCourierTemplate.withMissingSubject.yaml",
    "content": "body:\n  plaintext: \"base64://\"\n  html: \"base64://\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/root.courierSMS.yaml",
    "content": "selfservice:\n  default_browser_return_url: \"#/definitions/defaultReturnTo\"\n\ndsn: foo\n\nidentity:\n  schemas:\n    - id: default\n      url: https://example.com\n\ncourier:\n  message_retries: 50\n  smtp:\n    connection_uri: smtps://foo:bar@my-mailserver:1234/\n    from_address: no-reply@ory.kratos.sh\n  channels:\n    - id: sms\n      type: http\n      request_config: \"#/definitions/httpRequestConfig\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/root.courierTemplates.yaml",
    "content": "selfservice:\n  default_browser_return_url: \"#/definitions/defaultReturnTo\"\n\ndsn: foo\n\nidentity:\n  schemas:\n    - id: default\n      url: https://example.com\n\ncourier:\n  template_override_path: foo\n  smtp:\n    connection_uri: smtps://foo:bar@my-mailserver:1234/\n    from_address: no-reply@ory.kratos.sh\n  templates:\n    recovery: \"#/definitions/courierTemplates\"\n    verification: \"#/definitions/courierTemplates\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/root.full.yml",
    "content": "selfservice:\n  default_browser_return_url: \"#/definitions/defaultReturnTo\"\n\n  allowed_return_urls:\n    - https://foo.bar\n    - /dashboard\n\n  flows:\n    settings:\n      ui_url: /settings\n      lifespan: 1h5s\n      privileged_session_max_age: 1h\n      after: \"#/definitions/selfServiceAfterSettings\"\n\n    logout:\n      after:\n        default_browser_return_url: \"#/definitions/defaultReturnTo\"\n\n    registration:\n      ui_url: /registration\n      lifespan: 1h\n      after: \"#/definitions/selfServiceAfterRegistration\"\n\n    login:\n      ui_url: /login\n      lifespan: 1h\n      after: \"#/definitions/selfServiceAfterLogin\"\n\n    verification:\n      enabled: true\n      ui_url: /verification\n      lifespan: 1h\n      after: \"#/definitions/selfServiceAfterVerification\"\n\n    recovery:\n      enabled: true\n      ui_url: /recovery\n      lifespan: 1h\n      after: \"#/definitions/selfServiceAfterRecovery\"\n\n    error:\n      ui_url: /error\n\n  methods:\n    profile:\n      enabled: true\n    link:\n      enabled: true\n    password:\n      enabled: true\n    totp:\n      enabled: true\n      config:\n        issuer: \"foobar\"\n    oidc:\n      enabled: true\n      config:\n        providers:\n          - \"#/definitions/selfServiceOIDCProvider\"\n\ndsn: foo\n\ncourier:\n  template_override_path: foo\n  smtp:\n    connection_uri: smtps://foo:bar@my-mailserver:1234/\n    from_address: no-reply@ory.kratos.sh\n\nserve:\n  admin:\n    host: foo\n    port: 4434\n  public:\n    host: foo\n    port: 4433\n\nlog:\n  level: trace\n  format: json\n\nidentity:\n  default_schema_id: foo\n  schemas:\n    - id: foo\n      url: https://example.com\n\nsecrets:\n  default:\n    - default secret with a lot of chars\n  cookie:\n    - cookie secret with a lot of chars\n  cipher:\n    - secret-thirty-two-character-long\n\nciphers:\n  algorithm: xchacha20-poly1305\n\nhashers:\n  argon2:\n    memory: 128MB\n    iterations: 1\n    parallelism: 1\n    salt_length: 16\n    key_length: 16\n    dedicated_memory: 1GB\n    expected_deviation: 500ms\n    expected_duration: 500ms\n\nversion: v1.0.0\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/root.no_aliases.yaml",
    "content": "serve:\n  public:\n    base_url: \"#/definitions/baseUrl\"\n\nselfservice:\n  default_browser_return_url: \"#/definitions/defaultReturnTo\"\n\ndsn: foo\n\nidentity:\n  schemas:\n    - id: default\n      url: https://example.com\n\nsession:\n  cookie:\n    domain: foobar\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/root.required.yml",
    "content": "selfservice:\n  default_browser_return_url: \"#/definitions/defaultReturnTo\"\n\ndsn: foo\n\nidentity:\n  schemas:\n    - id: default\n      url: https://example.com\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterDefaultLoginMethod.full.yaml",
    "content": "default_browser_return_url: \"#/definitions/defaultReturnTo\"\nhooks: \"#/definitions/selfServiceAfterDefaultLoginMethodHooks\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterDefaultLoginMethod.required.yaml",
    "content": "{}\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterDefaultLoginMethodHooks.full.yaml",
    "content": "- \"#/definitions/selfServiceSessionRevokerHook\"\n- \"#/definitions/selfServiceRequireVerifiedAddressHook\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterDefaultLoginMethodHooks.required.yaml",
    "content": "[]\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterLogin.full.yaml",
    "content": "default_browser_return_url: \"#/definitions/defaultReturnTo\"\npassword: \"#/definitions/selfServiceAfterDefaultLoginMethod\"\nwebauthn: \"#/definitions/selfServiceAfterDefaultLoginMethod\"\noidc: \"#/definitions/selfServiceAfterOIDCLoginMethod\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterLogin.required.yaml",
    "content": "{}\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterOIDCLoginMethod.full.yaml",
    "content": "default_browser_return_url: \"#/definitions/defaultReturnTo\"\nhooks:\n  - \"#/definitions/selfServiceSessionRevokerHook\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterOIDCLoginMethod.required.yaml",
    "content": "{}\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterRegistration.full.yaml",
    "content": "default_browser_return_url: \"#/definitions/defaultReturnTo\"\npassword: \"#/definitions/selfServiceAfterRegistrationMethod\"\nwebauthn: \"#/definitions/selfServiceAfterRegistrationMethod\"\noidc: \"#/definitions/selfServiceAfterRegistrationMethod\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterRegistration.required.yaml",
    "content": "{}\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterRegistrationMethod.full.yaml",
    "content": "default_browser_return_url: \"#/definitions/defaultReturnTo\"\nhooks:\n  - \"#/definitions/selfServiceSessionIssuerHook\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterRegistrationMethod.required.yaml",
    "content": "{}\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterSettings.full.yaml",
    "content": "default_browser_return_url: \"#/definitions/defaultReturnTo\"\npassword: \"#/definitions/selfServiceAfterSettingsAuthMethod\"\nprofile: \"#/definitions/selfServiceAfterSettingsProfileMethod\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterSettings.required.yaml",
    "content": "{}\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterSettingsAuthMethod.full.yaml",
    "content": "default_browser_return_url: \"#/definitions/defaultReturnTo\"\nhooks:\n  - \"#/definitions/selfServiceWebHook\"\n  - \"#/definitions/selfServiceSessionRevokerHook\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterSettingsProfileMethod.full.yaml",
    "content": "default_browser_return_url: \"#/definitions/defaultReturnTo\"\nhooks:\n  - \"#/definitions/selfServiceWebHook\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceAfterSettingsProfileMethod.required.yaml",
    "content": "{}\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceOIDCProvider.fullApple.yaml",
    "content": "id: foo\nprovider: apple\nclient_id: foo\napple_team_id: foo\napple_private_key_id: foo\napple_private_key: foo\nmapper_url: https://example.com\nscope:\n  - foo\n  - bar\nrequested_claims: \"#/definitions/OIDCClaims\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceOIDCProvider.fullGithub.yaml",
    "content": "id: foo\nprovider: github\nclient_id: foo\nclient_secret: foo\nissuer_url: https://example.com\nauth_url: https://example.com\ntoken_url: https://example.com\nmapper_url: https://example.com\nscope:\n  - foo\n  - bar\nrequested_claims: \"#/definitions/OIDCClaims\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceOIDCProvider.fullMicrosoft.yaml",
    "content": "id: foo\nprovider: microsoft\nclient_id: foo\nclient_secret: foo\nissuer_url: https://example.com\nauth_url: https://example.com\ntoken_url: https://example.com\nmapper_url: https://example.com\nscope:\n  - foo\n  - bar\nmicrosoft_tenant: org\nrequested_claims: \"#/definitions/OIDCClaims\"\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceOIDCProvider.required.yaml",
    "content": "id: foo\nprovider: github\nclient_id: foo\nclient_secret: foo\nmapper_url: https://example.com\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceSessionIssuerHook.full.yaml",
    "content": "hook: session\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceSessionRevokerHook.full.yaml",
    "content": "hook: revoke_active_sessions\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/selfServiceWebHook.noauth.yaml",
    "content": "hook: web_hook\nconfig:\n  url: https://foo/bar\n  method: POST\n  body: base64://asdf\n  response:\n    ignore: false\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/webHookAuthApiKeyProperties.full.yaml",
    "content": "type: api_key\nconfig:\n  name: foo\n  value: bar\n  in: header\n"
  },
  {
    "path": "test/schema/fixtures/config.schema.test.success/webHookAuthBasicAuthProperties.full.yaml",
    "content": "type: basic_auth\nconfig:\n  user: foo\n  password: bar\n"
  },
  {
    "path": "test/schema/schema_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage schema\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ghodss/yaml\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t_ \"github.com/ory/jsonschema/v3/fileloader\"\n)\n\ntype schema struct {\n\tname string\n\traw  string\n\ts    *jsonschema.Schema\n}\n\ntype schemas []schema\n\ntype result int\n\nconst (\n\tsuccess result = iota\n\tfailure\n)\n\nfunc (r result) String() string {\n\treturn []string{\"success\", \"failure\"}[r]\n}\n\nfunc (s schema) validate(t *testing.T, path string) error {\n\tt.Helper()\n\tif s.s == nil {\n\t\tcompiler := jsonschema.NewCompiler()\n\t\tif err := compiler.AddResource(s.name, strings.NewReader(s.raw)); err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\tsx, err := compiler.Compile(context.Background(), s.name)\n\t\tif err != nil {\n\t\t\treturn errors.WithStack(err)\n\t\t}\n\n\t\ts.s = sx\n\t}\n\n\tvar doc io.Reader\n\ty, err := os.ReadFile(path) // #nosec G304 test code\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tj, err := yaml.YAMLToJSON(y)\n\tif err != nil {\n\t\treturn errors.WithStack(err)\n\t}\n\n\tdoc = bytes.NewBuffer(j)\n\n\tif err := s.s.Validate(doc); err != nil {\n\t\treturn errors.Errorf(\"there were validation errors: %s\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (ss *schemas) getByName(n string) (*schema, error) {\n\tfor _, s := range *ss {\n\t\tif s.name == n {\n\t\t\treturn &s, nil\n\t\t}\n\t}\n\n\treturn nil, errors.Errorf(\"could not find schema with name %s\", n)\n}\n\nfunc TestSchemas(t *testing.T) {\n\tt.Run(\"test embedx/config.schema.json\", SchemaTestRunner(\"../../embedx\", \"config\"))\n}\n\nfunc SchemaTestRunner(spath string, sname string) func(*testing.T) {\n\treturn func(t *testing.T) {\n\t\tsb, err := os.ReadFile(fmt.Sprintf(\"%s/%s.schema.json\", spath, sname))\n\t\trequire.NoError(t, err)\n\n\t\t// To test refs independently and reduce test case size we replace every \"$ref\" with \"const\".\n\t\t// That way refs will not be resolved but we still make sure that they are pointing to the right definition.\n\t\t// Changing a definition will result in just changing test cases for that definition.\n\t\ts := strings.ReplaceAll(string(sb), `\"$ref\":`, `\"const\":`)\n\t\tschemas := schemas{{\n\t\t\tname: \"root\",\n\t\t\traw:  s,\n\t\t}}\n\t\tdef := gjson.Get(s, \"definitions\")\n\t\tif def.Exists() {\n\t\t\trequire.True(t, def.IsObject())\n\t\t\tdef.ForEach(func(key, value gjson.Result) bool {\n\t\t\t\trequire.Equal(t, gjson.String, key.Type)\n\t\t\t\tschemas = append(schemas, schema{\n\t\t\t\t\tname: key.String(),\n\t\t\t\t\traw:  value.Raw,\n\t\t\t\t})\n\t\t\t\treturn true\n\t\t\t})\n\t\t}\n\n\t\tRunCases(t, schemas, fmt.Sprintf(\"./fixtures/%s.schema.test.success\", sname), success)\n\t\tRunCases(t, schemas, fmt.Sprintf(\"./fixtures/%s.schema.test.failure\", sname), failure)\n\t}\n}\n\nfunc RunCases(t *testing.T, ss schemas, dir string, expected result) {\n\terr := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {\n\t\trequire.NoError(t, err)\n\t\tif info.IsDir() {\n\t\t\treturn nil\n\t\t}\n\n\t\tparts := strings.Split(info.Name(), \".\")\n\t\trequire.Equal(t, 3, len(parts))\n\t\tsName, tc := parts[0], parts[1]\n\n\t\ts, err := ss.getByName(sName)\n\t\trequire.NoError(t, err)\n\n\t\tt.Run(fmt.Sprintf(\"case=schema %s test case %s expects %s\", sName, tc, expected), func(t *testing.T) {\n\t\t\terr := s.validate(t, path)\n\t\t\tif expected == success {\n\t\t\t\tassert.NoError(t, err, \"path: %s\", path)\n\t\t\t} else {\n\t\t\t\tassert.Error(t, err, \"path: %s\", path)\n\t\t\t}\n\t\t})\n\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "test/stub/identity/empty.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/stubs/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\"\n    }\n  }\n}\n"
  },
  {
    "path": "text/context.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"encoding/json\"\n)\n\nfunc context(ctx map[string]any) []byte {\n\tif len(ctx) == 0 {\n\t\tpanic(\"context must not be empty\")\n\t}\n\tres, err := json.Marshal(ctx)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn res\n}\n"
  },
  {
    "path": "text/id.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\n// This file MUST not have any imports to modules that are not in the standard library.\n// Otherwise, `make docs/cli` will fail.\n\ntype ID int\n\nconst (\n\tInfoSelfServiceLoginRoot                 ID = 1010000 + iota // 1010000\n\tInfoSelfServiceLogin                                         // 1010001\n\tInfoSelfServiceLoginWith                                     // 1010002\n\tInfoSelfServiceLoginReAuth                                   // 1010003\n\tInfoSelfServiceLoginMFA                                      // 1010004\n\tInfoSelfServiceLoginVerify                                   // 1010005\n\tInfoSelfServiceLoginTOTPLabel                                // 1010006\n\tInfoLoginLookupLabel                                         // 1010007\n\tInfoSelfServiceLoginWebAuthn                                 // 1010008\n\tInfoLoginTOTP                                                // 1010009\n\tInfoLoginLookup                                              // 1010010\n\tInfoSelfServiceLoginContinueWebAuthn                         // 1010011\n\tInfoSelfServiceLoginWebAuthnPasswordless                     // 1010012\n\tInfoSelfServiceLoginContinue                                 // 1010013\n\tInfoSelfServiceLoginCodeSent                                 // 1010014\n\tInfoSelfServiceLoginCode                                     // 1010015\n\tInfoSelfServiceLoginLink                                     // 1010016\n\tInfoSelfServiceLoginAndLink                                  // 1010017\n\tInfoSelfServiceLoginWithAndLink                              // 1010018\n\tInfoSelfServiceLoginCodeMFA                                  // 1010019\n\tInfoSelfServiceLoginCodeMFAHint                              // 1010020\n\tInfoSelfServiceLoginPasskey                                  // 1010021\n\tInfoSelfServiceLoginPassword                                 // 1010022\n\tInfoSelfServiceLoginAAL2CodeAddress                          // 1010023\n)\n\nconst (\n\tInfoSelfServiceLogout ID = 1020000 + iota\n)\n\nconst (\n\tInfoSelfServiceMFA ID = 1030000 + iota\n)\n\nconst (\n\tInfoSelfServiceRegistrationRoot              ID = 1040000 + iota // 1040000\n\tInfoSelfServiceRegistration                                      // 1040001\n\tInfoSelfServiceRegistrationWith                                  // 1040002\n\tInfoSelfServiceRegistrationContinue                              // 1040003\n\tInfoSelfServiceRegistrationRegisterWebAuthn                      // 1040004\n\tInfoSelfServiceRegistrationEmailWithCodeSent                     // 1040005\n\tInfoSelfServiceRegistrationRegisterCode                          // 1040006\n\tInfoSelfServiceRegistrationRegisterPasskey                       // 1040007\n\tInfoSelfServiceRegistrationBack                                  // 1040008\n\tInfoSelfServiceRegistrationChooseCredentials                     // 1040009\n)\n\nconst (\n\tInfoSelfServiceSettings ID = 1050000 + iota\n\tInfoSelfServiceSettingsUpdateSuccess\n\tInfoSelfServiceSettingsUpdateLinkOidc\n\tInfoSelfServiceSettingsUpdateUnlinkOidc\n\tInfoSelfServiceSettingsUpdateUnlinkTOTP\n\tInfoSelfServiceSettingsTOTPQRCode\n\tInfoSelfServiceSettingsTOTPSecret\n\tInfoSelfServiceSettingsRevealLookup\n\tInfoSelfServiceSettingsRegenerateLookup\n\tInfoSelfServiceSettingsLookupSecret\n\tInfoSelfServiceSettingsLookupSecretLabel\n\tInfoSelfServiceSettingsLookupConfirm\n\tInfoSelfServiceSettingsRegisterWebAuthn\n\tInfoSelfServiceSettingsRegisterWebAuthnDisplayName\n\tInfoSelfServiceSettingsLookupSecretUsed\n\tInfoSelfServiceSettingsLookupSecretList\n\tInfoSelfServiceSettingsDisableLookup\n\tInfoSelfServiceSettingsTOTPSecretLabel\n\tInfoSelfServiceSettingsRemoveWebAuthn\n\tInfoSelfServiceSettingsRegisterPasskey\n\tInfoSelfServiceSettingsRemovePasskey\n)\n\nconst (\n\tInfoSelfServiceRecovery                          ID = 1060000 + iota // 1060000\n\tInfoSelfServiceRecoverySuccessful                                    // 1060001\n\tInfoSelfServiceRecoveryEmailSent                                     // 1060002\n\tInfoSelfServiceRecoveryEmailWithCodeSent                             // 1060003\n\tInfoSelfServiceRecoveryMessageMaskedWithCodeSent                     // 1060004\n\tInfoSelfServiceRecoveryAskForFullAddress                             // 1060005\n\tInfoSelfServiceRecoveryAskToChooseAddress                            // 1060006\n\tInfoSelfServiceRecoveryBack                                          // 1060007\n)\n\nconst (\n\tInfoNodeLabel                       ID = 1070000 + iota // 1070000\n\tInfoNodeLabelInputPassword                              // 1070001\n\tInfoNodeLabelGenerated                                  // 1070002\n\tInfoNodeLabelSave                                       // 1070003\n\tInfoNodeLabelID                                         // 1070004\n\tInfoNodeLabelSubmit                                     // 1070005\n\tInfoNodeLabelVerifyOTP                                  // 1070006\n\tInfoNodeLabelEmail                                      // 1070007\n\tInfoNodeLabelResendOTP                                  // 1070008\n\tInfoNodeLabelContinue                                   // 1070009\n\tInfoNodeLabelRecoveryCode                               // 1070010\n\tInfoNodeLabelVerificationCode                           // 1070011\n\tInfoNodeLabelRegistrationCode                           // 1070012\n\tInfoNodeLabelLoginCode                                  // 1070013\n\tInfoNodeLabelLoginAndLinkCredential                     // 1070014\n\tInfoNodeLabelCaptcha                                    // 1070015\n\tInfoNodeLabelRecoveryAddress                            // 1070016\n\tInfoNodeLabelPhoneNumber                                // 1070017\n)\n\nconst (\n\tInfoSelfServiceVerification                  ID = 1080000 + iota // 1080000\n\tInfoSelfServiceVerificationEmailSent                             // 1080001\n\tInfoSelfServiceVerificationSuccessful                            // 1080002\n\tInfoSelfServiceVerificationEmailWithCodeSent                     // 1080003\n)\n\nconst (\n\tErrorValidation ID = 4000000 + iota\n\tErrorValidationGeneric\n\tErrorValidationRequired\n\tErrorValidationMinLength\n\tErrorValidationInvalidFormat\n\tErrorValidationPasswordPolicyViolationGeneric\n\tErrorValidationInvalidCredentials\n\tErrorValidationDuplicateCredentials\n\tErrorValidationTOTPVerifierWrong\n\tErrorValidationIdentifierMissing\n\tErrorValidationAddressNotVerified\n\tErrorValidationNoTOTPDevice\n\tErrorValidationLookupAlreadyUsed\n\tErrorValidationNoWebAuthnDevice\n\tErrorValidationNoLookup\n\tErrorValidationSuchNoWebAuthnUser\n\tErrorValidationLookupInvalid\n\tErrorValidationMaxLength\n\tErrorValidationMinimum\n\tErrorValidationExclusiveMinimum\n\tErrorValidationMaximum\n\tErrorValidationExclusiveMaximum\n\tErrorValidationMultipleOf\n\tErrorValidationMaxItems\n\tErrorValidationMinItems\n\tErrorValidationUniqueItems\n\tErrorValidationWrongType\n\tErrorValidationDuplicateCredentialsOnOIDCLink\n\tErrorValidationDuplicateCredentialsWithHints\n\tErrorValidationConst\n\tErrorValidationConstGeneric\n\tErrorValidationPasswordIdentifierTooSimilar\n\tErrorValidationPasswordMinLength\n\tErrorValidationPasswordMaxLength\n\tErrorValidationPasswordTooManyBreaches\n\tErrorValidationNoCodeUser\n\tErrorValidationTraitsMismatch\n\tErrorValidationAccountNotFound\n\tErrorValidationCaptchaError\n\tErrorValidationPasswordNewSameAsOld\n\tErrorValidationEmail\n\tErrorValidationPhone\n)\n\nconst (\n\tErrorValidationLogin                            ID = 4010000 + iota // 4010000\n\tErrorValidationLoginFlowExpired                                     // 4010001\n\tErrorValidationLoginNoStrategyFound                                 // 4010002\n\tErrorValidationRegistrationNoStrategyFound                          // 4010003\n\tErrorValidationSettingsNoStrategyFound                              // 4010004\n\tErrorValidationRecoveryNoStrategyFound                              // 4010005\n\tErrorValidationVerificationNoStrategyFound                          // 4010006\n\tErrorValidationLoginRetrySuccess                                    // 4010007\n\tErrorValidationLoginCodeInvalidOrAlreadyUsed                        // 4010008\n\tErrorValidationLoginLinkedCredentialsDoNotMatch                     // 4010009\n\tErrorValidationLoginAddressUnknown                                  // 4010010\n)\n\nconst (\n\tErrorValidationRegistration                         ID = 4040000 + iota\n\tErrorValidationRegistrationFlowExpired                 // 4040001\n\tErrorValidateionRegistrationRetrySuccess               // 4040002\n\tErrorValidationRegistrationCodeInvalidOrAlreadyUsed    // 4040003\n)\n\nconst (\n\tErrorValidationSettings ID = 4050000 + iota\n\tErrorValidationSettingsFlowExpired\n)\n\nconst (\n\tErrorValidationRecovery                          ID = 4060000 + iota // 4060000\n\tErrorValidationRecoveryRetrySuccess                                  // 4060001\n\tErrorValidationRecoveryStateFailure                                  // 4060002\n\tErrorValidationRecoveryMissingRecoveryToken                          // 4060003\n\tErrorValidationRecoveryTokenInvalidOrAlreadyUsed                     // 4060004\n\tErrorValidationRecoveryFlowExpired                                   // 4060005\n\tErrorValidationRecoveryCodeInvalidOrAlreadyUsed                      // 4060006\n)\n\nconst (\n\tErrorValidationVerification                          ID = 4070000 + iota // 4070000\n\tErrorValidationVerificationTokenInvalidOrAlreadyUsed                     // 4070001\n\tErrorValidationVerificationRetrySuccess                                  // 4070002\n\tErrorValidationVerificationStateFailure                                  // 4070003\n\tErrorValidationVerificationMissingVerificationToken                      // 4070004\n\tErrorValidationVerificationFlowExpired                                   // 4070005\n\tErrorValidationVerificationCodeInvalidOrAlreadyUsed                      // 4070006\n)\n\nconst (\n\tErrorSystem                                 ID = 5000000 + iota // 5000000\n\tErrorSystemGeneric                                              // 5000001\n\tErrorSystemNoAuthenticationMethodsAvailable                     // 5000002\n)\n"
  },
  {
    "path": "text/id_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestIDs(t *testing.T) {\n\tassert.Equal(t, 1010000, int(InfoSelfServiceLoginRoot))\n\n\tassert.Equal(t, 1020000, int(InfoSelfServiceLogout))\n\n\tassert.Equal(t, 1030000, int(InfoSelfServiceMFA))\n\n\tassert.Equal(t, 1040000, int(InfoSelfServiceRegistrationRoot))\n\tassert.Equal(t, 1040001, int(InfoSelfServiceRegistration))\n\n\tassert.Equal(t, 1050000, int(InfoSelfServiceSettings))\n\tassert.Equal(t, 1050001, int(InfoSelfServiceSettingsUpdateSuccess))\n\n\tassert.Equal(t, 1060000, int(InfoSelfServiceRecovery))\n\tassert.Equal(t, 1060001, int(InfoSelfServiceRecoverySuccessful))\n\tassert.Equal(t, 1060002, int(InfoSelfServiceRecoveryEmailSent))\n\tassert.Equal(t, 1060003, int(InfoSelfServiceRecoveryEmailWithCodeSent))\n\n\tassert.Equal(t, 1070000, int(InfoNodeLabel))\n\tassert.Equal(t, 1070001, int(InfoNodeLabelInputPassword))\n\tassert.Equal(t, 1070002, int(InfoNodeLabelGenerated))\n\tassert.Equal(t, 1070003, int(InfoNodeLabelSave))\n\tassert.Equal(t, 1070004, int(InfoNodeLabelID))\n\tassert.Equal(t, 1070005, int(InfoNodeLabelSubmit))\n\tassert.Equal(t, 1070006, int(InfoNodeLabelVerifyOTP))\n\tassert.Equal(t, 1070007, int(InfoNodeLabelEmail))\n\tassert.Equal(t, 1070008, int(InfoNodeLabelResendOTP))\n\tassert.Equal(t, 1070009, int(InfoNodeLabelContinue))\n\tassert.Equal(t, 1070010, int(InfoNodeLabelRecoveryCode))\n\tassert.Equal(t, 1070011, int(InfoNodeLabelVerificationCode))\n\n\tassert.Equal(t, 1080000, int(InfoSelfServiceVerification))\n\n\tassert.Equal(t, 4000000, int(ErrorValidation))\n\tassert.Equal(t, 4000001, int(ErrorValidationGeneric))\n\tassert.Equal(t, 4000002, int(ErrorValidationRequired))\n\n\tassert.Equal(t, 4010000, int(ErrorValidationLogin))\n\tassert.Equal(t, 4010001, int(ErrorValidationLoginFlowExpired))\n\n\tassert.Equal(t, 4040000, int(ErrorValidationRegistration))\n\tassert.Equal(t, 4040001, int(ErrorValidationRegistrationFlowExpired))\n\n\tassert.Equal(t, 4050000, int(ErrorValidationSettings))\n\tassert.Equal(t, 4050001, int(ErrorValidationSettingsFlowExpired))\n\n\tassert.Equal(t, 4060000, int(ErrorValidationRecovery))\n\tassert.Equal(t, 4060001, int(ErrorValidationRecoveryRetrySuccess))\n\tassert.Equal(t, 4060002, int(ErrorValidationRecoveryStateFailure))\n\n\tassert.Equal(t, 4070000, int(ErrorValidationVerification))\n\tassert.Equal(t, 4070001, int(ErrorValidationVerificationTokenInvalidOrAlreadyUsed))\n\n\tassert.Equal(t, 5000000, int(ErrorSystem))\n\tassert.Equal(t, 5000001, int(ErrorSystemGeneric))\n\tassert.Equal(t, 5000002, int(ErrorSystemNoAuthenticationMethodsAvailable))\n\n\tassert.Equal(t, 4060006, int(ErrorValidationRecoveryCodeInvalidOrAlreadyUsed))\n\tassert.Equal(t, 4070006, int(ErrorValidationVerificationCodeInvalidOrAlreadyUsed))\n\n\tassert.Equal(t, 1080000, int(InfoSelfServiceVerification))\n\tassert.Equal(t, 1080001, int(InfoSelfServiceVerificationEmailSent))\n\tassert.Equal(t, 1080002, int(InfoSelfServiceVerificationSuccessful))\n\tassert.Equal(t, 1080003, int(InfoSelfServiceVerificationEmailWithCodeSent))\n\n\tassert.Equal(t, 1070015, int(InfoNodeLabelCaptcha))\n\tassert.Equal(t, 4000038, int(ErrorValidationCaptchaError))\n\n\tassert.Equal(t, 4000040, int(ErrorValidationEmail))\n\tassert.Equal(t, 4000041, int(ErrorValidationPhone))\n}\n"
  },
  {
    "path": "text/message.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\n\t\"github.com/ory/x/sqlxx\"\n)\n\n// swagger:model uiTexts\ntype Messages []Message\n\nfunc (h Messages) MarshalJSON() ([]byte, error) {\n\tif len(h) == 0 {\n\t\treturn []byte(\"[]\"), nil\n\t}\n\n\treturn json.Marshal([]Message(h))\n}\n\nfunc (h *Messages) Scan(value interface{}) error {\n\treturn sqlxx.JSONScan(h, value)\n}\n\nfunc (h Messages) Value() (driver.Value, error) {\n\tif h == nil {\n\t\treturn []byte(\"[]\"), nil\n\t}\n\treturn json.Marshal(h)\n}\n\nfunc (h *Messages) Add(m *Message) Messages {\n\t*h = append(*h, *m)\n\treturn *h\n}\n\nfunc (h *Messages) Set(m *Message) Messages {\n\t*h = Messages{*m}\n\treturn *h\n}\n\nfunc (h *Messages) Clear() Messages {\n\t*h = *new(Messages)\n\treturn *h\n}\n\n// swagger:model uiText\ntype Message struct {\n\t// The message ID.\n\t//\n\t// required: true\n\tID ID `json:\"id\"`\n\n\t// The message text. Written in american english.\n\t//\n\t// required: true\n\tText string `json:\"text\"`\n\n\t// The message type.\n\t//\n\t// required: true\n\tType UITextType `json:\"type\"`\n\n\t// The message's context. Useful when customizing messages.\n\tContext json.RawMessage `json:\"context,omitempty\" faker:\"-\"`\n}\n\nfunc (m *Message) Scan(value interface{}) error {\n\treturn sqlxx.JSONScan(m, value)\n}\n\nfunc (m Message) Value() (driver.Value, error) {\n\treturn json.Marshal(&m)\n}\n\nfunc (m *Message) Error() string {\n\treturn m.Text\n}\n\nfunc (m *Message) Is(err error) bool {\n\tem, ok := err.(*Message)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn m.ID == em.ID\n}\n"
  },
  {
    "path": "text/message_error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\n// This file contains error IDs for all system errors / JSON errors\n\nconst (\n\tErrIDNeedsPrivilegedSession                        = \"session_refresh_required\"\n\tErrIDSelfServiceFlowExpired                        = \"self_service_flow_expired\"\n\tErrIDSelfServiceFlowDisabled                       = \"self_service_flow_disabled\"\n\tErrIDSelfServiceBrowserLocationChangeRequiredError = \"browser_location_change_required\"\n\tErrIDSelfServiceFlowReplaced                       = \"self_service_flow_replaced\"\n\n\tErrIDAlreadyLoggedIn             = \"session_already_available\"\n\tErrIDAddressNotVerified          = \"session_verified_address_required\"\n\tErrIDSessionHasAALAlready        = \"session_aal_already_fulfilled\"\n\tErrIDSessionRequiredForHigherAAL = \"session_aal1_required\"\n\tErrIDHigherAALRequired           = \"session_aal2_required\"\n\tErrNoActiveSession               = \"session_inactive\"\n\tErrIDRedirectURLNotAllowed       = \"self_service_flow_return_to_forbidden\"\n\tErrIDInitiatedBySomeoneElse      = \"security_identity_mismatch\"\n\n\tErrIDCSRF = \"security_csrf_violation\"\n)\n"
  },
  {
    "path": "text/message_login.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc NewInfoLoginReAuth() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginReAuth,\n\t\tType: Info,\n\t\tText: \"Please confirm this action by verifying that it is you.\",\n\t}\n}\n\nfunc NewInfoLoginMFA() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginMFA,\n\t\tType: Info,\n\t\tText: \"Please complete the second authentication challenge.\",\n\t}\n}\n\nfunc NewInfoLoginWebAuthnPasswordless() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginWebAuthnPasswordless,\n\t\tType: Info,\n\t\tText: \"Prepare your WebAuthn device (e.g. security key, biometrics scanner, ...) and press continue.\",\n\t}\n}\n\nfunc NewInfoLoginTOTPLabel() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginTOTPLabel,\n\t\tType: Info,\n\t\tText: \"Authentication code\",\n\t}\n}\n\nfunc NewInfoLoginLookupLabel() *Message {\n\treturn &Message{\n\t\tID:   InfoLoginLookupLabel,\n\t\tType: Info,\n\t\tText: \"Backup recovery code\",\n\t}\n}\n\nfunc NewInfoLogin() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLogin,\n\t\tText: \"Sign in\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoLoginLinkMessage(dupIdentifier, provider, newLoginURL string, availableCredentials, availableProviders []string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginLink,\n\t\tType: Info,\n\t\tText: fmt.Sprintf(\n\t\t\t\"You tried to sign in with %q, but that email is already used by another account. Sign in to your account with one of the options below to add your account %[1]q at %q as another way to sign in.\",\n\t\t\tdupIdentifier,\n\t\t\tprovider,\n\t\t),\n\t\tContext: context(map[string]any{\n\t\t\t\"duplicateIdentifier\":        dupIdentifier,\n\t\t\t\"provider\":                   provider,\n\t\t\t\"newLoginUrl\":                newLoginURL,\n\t\t\t\"duplicate_identifier\":       dupIdentifier,\n\t\t\t\"new_login_url\":              newLoginURL,\n\t\t\t\"available_credential_types\": availableCredentials,\n\t\t\t\"available_providers\":        availableProviders,\n\t\t}),\n\t}\n}\n\nfunc NewInfoLoginAndLink() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginAndLink,\n\t\tText: \"Sign in and link\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoLoginTOTP() *Message {\n\treturn &Message{\n\t\tID:   InfoLoginTOTP,\n\t\tText: \"Use Authenticator\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoLoginPassword() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginPassword,\n\t\tText: \"Sign in with password\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoLoginLookup() *Message {\n\treturn &Message{\n\t\tID:   InfoLoginLookup,\n\t\tText: \"Use backup recovery code\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoLoginVerify() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginVerify,\n\t\tText: \"Verify\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoLoginWith(provider string, providerId string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginWith,\n\t\tText: fmt.Sprintf(\"Sign in with %s\", provider),\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"provider\":    provider,\n\t\t\t\"provider_id\": providerId,\n\t\t}),\n\t}\n}\n\nfunc NewInfoLoginWithAndLink(provider string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginWithAndLink,\n\t\tText: fmt.Sprintf(\"Confirm with %s\", provider),\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"provider\": provider,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationLoginFlowExpired(expiredAt time.Time) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationLoginFlowExpired,\n\t\tText: fmt.Sprintf(\"The login flow expired %.2f minutes ago, please try again.\", Since(expiredAt).Minutes()),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"expired_at\":      expiredAt,\n\t\t\t\"expired_at_unix\": expiredAt.Unix(),\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationLoginNoStrategyFound() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationLoginNoStrategyFound,\n\t\tText: \"Could not find a strategy to log you in with. Did you fill out the form correctly?\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationRegistrationNoStrategyFound() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRegistrationNoStrategyFound,\n\t\tText: \"Could not find a strategy to sign you up with. Did you fill out the form correctly?\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationSettingsNoStrategyFound() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationSettingsNoStrategyFound,\n\t\tText: \"Could not find a strategy to update your settings. Did you fill out the form correctly?\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationRecoveryNoStrategyFound() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRecoveryNoStrategyFound,\n\t\tText: \"Could not find a strategy to recover your account with. Did you fill out the form correctly?\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationVerificationNoStrategyFound() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationVerificationNoStrategyFound,\n\t\tText: \"Could not find a strategy to verify your account with. Did you fill out the form correctly?\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewInfoSelfServiceLoginWebAuthn() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginWebAuthn,\n\t\tText: \"Sign in with hardware key\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceLoginPasskey() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginPasskey,\n\t\tText: \"Sign in with passkey\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceContinueLoginWebAuthn() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginContinueWebAuthn,\n\t\tText: \"Sign in with hardware key\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceLoginContinue() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginContinue,\n\t\tText: \"Continue\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewLoginCodeSent() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginCodeSent,\n\t\tType: Info,\n\t\tText: \"A code was sent to the address you provided. If you didn't receive it, please check the spelling of the address and try again.\",\n\t}\n}\n\nfunc NewErrorValidationLoginCodeInvalidOrAlreadyUsed() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationLoginCodeInvalidOrAlreadyUsed,\n\t\tText: \"The login code is invalid or has already been used. Please try again.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationLoginRetrySuccessful() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationLoginRetrySuccess,\n\t\tType: Error,\n\t\tText: \"The request was already completed successfully and can not be retried.\",\n\t}\n}\n\nfunc NewInfoSelfServiceLoginCode() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginCode,\n\t\tType: Info,\n\t\tText: \"Send sign in code\",\n\t}\n}\n\nfunc NewErrorValidationLoginLinkedCredentialsDoNotMatch() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationLoginLinkedCredentialsDoNotMatch,\n\t\tText: \"Linked credentials do not match.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationAddressUnknown() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationLoginAddressUnknown,\n\t\tText: \"The address you entered does not match any known addresses in the current account.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewInfoSelfServiceLoginCodeMFA() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginCodeMFA,\n\t\tType: Info,\n\t\tText: \"Request code to continue\",\n\t}\n}\n\nfunc NewInfoSelfServiceLoginAAL2CodeAddress(channel string, to string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceLoginAAL2CodeAddress,\n\t\tType: Info,\n\t\tText: fmt.Sprintf(\"Send code to %s\", to),\n\t\tContext: context(map[string]any{\n\t\t\t\"address\": to,\n\t\t\t\"channel\": channel,\n\t\t}),\n\t}\n}\n"
  },
  {
    "path": "text/message_node.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nfunc NewInfoNodeLabelVerifyOTP() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelVerifyOTP,\n\t\tText: \"Verify code\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeLabelVerificationCode() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelVerificationCode,\n\t\tText: \"Verification code\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeLabelRecoveryCode() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelRecoveryCode,\n\t\tText: \"Recovery code\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeLabelRegistrationCode() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelRegistrationCode,\n\t\tText: \"Registration code\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeLabelLoginCode() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelLoginCode,\n\t\tText: \"Login code\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeInputPassword() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelInputPassword,\n\t\tText: \"Password\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeLabelGenerated(title string, name string) *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelGenerated,\n\t\tText: title,\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"title\": title,\n\t\t\t\"name\":  name,\n\t\t}),\n\t}\n}\n\nfunc NewInfoNodeLabelSave() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelSave,\n\t\tText: \"Save\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeLabelSubmit() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelSubmit,\n\t\tText: \"Submit\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeLabelContinue() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelContinue,\n\t\tText: \"Continue\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeLabelID() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelID,\n\t\tText: \"ID\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeInputEmail() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelEmail,\n\t\tText: \"Email\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeInputPhoneNumber() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelPhoneNumber,\n\t\tText: \"Phone number\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeResendOTP() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelResendOTP,\n\t\tText: \"Resend code\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoNodeLoginAndLinkCredential() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelLoginAndLinkCredential,\n\t\tText: \"Login and link credential\",\n\t\tType: Info,\n\t}\n}\n"
  },
  {
    "path": "text/message_recovery.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc NewErrorValidationRecoveryFlowExpired(expiredAt time.Time) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRecoveryFlowExpired,\n\t\tText: fmt.Sprintf(\"The recovery flow expired %.2f minutes ago, please try again.\", Since(expiredAt).Minutes()),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"expired_at\":      expiredAt,\n\t\t\t\"expired_at_unix\": expiredAt.Unix(),\n\t\t}),\n\t}\n}\n\nfunc NewRecoverySuccessful(privilegedSessionExpiresAt time.Time) *Message {\n\thasLeft := Until(privilegedSessionExpiresAt)\n\treturn &Message{\n\t\tID:   InfoSelfServiceRecoverySuccessful,\n\t\tType: Success,\n\t\tText: fmt.Sprintf(\"You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next %.2f minutes.\", hasLeft.Minutes()),\n\t\tContext: context(map[string]any{\n\t\t\t\"privilegedSessionExpiresAt\":         privilegedSessionExpiresAt,\n\t\t\t\"privileged_session_expires_at\":      privilegedSessionExpiresAt,\n\t\t\t\"privileged_session_expires_at_unix\": privilegedSessionExpiresAt.Unix(),\n\t\t}),\n\t}\n}\n\nfunc NewRecoveryEmailSent() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRecoveryEmailSent,\n\t\tType: Info,\n\t\tText: \"An email containing a recovery link has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n\t}\n}\n\nfunc NewRecoveryEmailWithCodeSent() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRecoveryEmailWithCodeSent,\n\t\tType: Info,\n\t\tText: \"An email containing a recovery code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n\t}\n}\n\nfunc NewRecoveryAskAnyRecoveryAddress() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelRecoveryAddress,\n\t\tText: \"Recovery address\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewRecoveryCodeRecoverySelectAddressSent(maskedAddress string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRecoveryMessageMaskedWithCodeSent,\n\t\tType: Info,\n\t\tText: fmt.Sprintf(\"A recovery code has been sent to %s. If you have not received it, check the spelling of the address and make sure to use the address you registered with.\", maskedAddress),\n\t\tContext: context(map[string]any{\n\t\t\t\"masked_address\": maskedAddress,\n\t\t}),\n\t}\n}\n\nfunc NewRecoveryAskForFullAddress() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRecoveryAskForFullAddress,\n\t\tType: Info,\n\t\tText: \"Recover access to your account by providing your recovery address in full.\",\n\t}\n}\n\nfunc NewRecoveryAskToChooseAddress() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRecoveryAskToChooseAddress,\n\t\tType: Info,\n\t\tText: \"How do you want to recover your account?\",\n\t}\n}\n\nfunc NewRecoveryBack() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRecoveryBack,\n\t\tType: Info,\n\t\tText: \"Back\",\n\t}\n}\n\nfunc NewErrorValidationRecoveryTokenInvalidOrAlreadyUsed() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRecoveryTokenInvalidOrAlreadyUsed,\n\t\tText: \"The recovery token is invalid or has already been used. Please retry the flow.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationRecoveryCodeInvalidOrAlreadyUsed() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRecoveryCodeInvalidOrAlreadyUsed,\n\t\tText: \"The recovery code is invalid or has already been used. Please try again.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationRecoveryRetrySuccess() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRecoveryRetrySuccess,\n\t\tText: \"The request was already completed successfully and can not be retried.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationRecoveryStateFailure() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRecoveryStateFailure,\n\t\tText: \"The recovery flow reached a failure state and must be retried.\",\n\t\tType: Error,\n\t}\n}\n"
  },
  {
    "path": "text/message_registration.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc NewInfoRegistration() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRegistration,\n\t\tText: \"Sign up\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoRegistrationWith(provider string, providerID string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRegistrationWith,\n\t\tText: fmt.Sprintf(\"Sign up with %s\", provider),\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"provider\":    provider,\n\t\t\t\"provider_id\": providerID,\n\t\t}),\n\t}\n}\n\nfunc NewInfoRegistrationContinue() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRegistrationContinue,\n\t\tText: \"Continue\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoRegistrationBack() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRegistrationBack,\n\t\tText: \"Back\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceChooseCredentials() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRegistrationChooseCredentials,\n\t\tText: \"Please choose a credential to authenticate yourself with.\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewErrorValidationRegistrationFlowExpired(expiredAt time.Time) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRegistrationFlowExpired,\n\t\tText: fmt.Sprintf(\"The registration flow expired %.2f minutes ago, please try again.\", Since(expiredAt).Minutes()),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"expired_at\":      expiredAt,\n\t\t\t\"expired_at_unix\": expiredAt.Unix(),\n\t\t}),\n\t}\n}\n\nfunc NewInfoSelfServiceRegistrationRegisterWebAuthn() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRegistrationRegisterWebAuthn,\n\t\tText: \"Sign up with security key\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceRegistrationRegisterPasskey() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRegistrationRegisterPasskey,\n\t\tText: \"Sign up with passkey\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewRegistrationEmailWithCodeSent() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRegistrationEmailWithCodeSent,\n\t\tType: Info,\n\t\tText: \"A code has been sent to the address(es) you provided. If you have not received a message, check the spelling of the address and retry the registration.\",\n\t}\n}\n\nfunc NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRegistrationCodeInvalidOrAlreadyUsed,\n\t\tText: \"The registration code is invalid or has already been used. Please try again.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationRegistrationRetrySuccessful() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidateionRegistrationRetrySuccess,\n\t\tType: Error,\n\t\tText: \"The request was already completed successfully and can not be retried.\",\n\t}\n}\n\nfunc NewInfoSelfServiceRegistrationRegisterCode() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceRegistrationRegisterCode,\n\t\tText: \"Send sign up code\",\n\t\tType: Info,\n\t}\n}\n"
  },
  {
    "path": "text/message_settings.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc NewErrorValidationSettingsFlowExpired(expiredAt time.Time) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationSettingsFlowExpired,\n\t\tText: fmt.Sprintf(\"The settings flow expired %.2f minutes ago, please try again.\", Since(expiredAt).Minutes()),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"expired_at\":      expiredAt,\n\t\t\t\"expired_at_unix\": expiredAt.Unix(),\n\t\t}),\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsTOTPQRCode() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsTOTPQRCode,\n\t\tText: \"Authenticator app QR code\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsTOTPSecret(secret string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsTOTPSecret,\n\t\tText: secret,\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"secret\": secret,\n\t\t}),\n\t}\n}\nfunc NewInfoSelfServiceSettingsTOTPSecretLabel() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsTOTPSecretLabel,\n\t\tText: \"This is your authenticator app secret. Use it if you can not scan the QR code.\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsUpdateSuccess() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsUpdateSuccess,\n\t\tText: \"Your changes have been saved!\",\n\t\tType: Success,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsUpdateUnlinkTOTP() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsUpdateUnlinkTOTP,\n\t\tText: \"Unlink TOTP Authenticator App\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsRevealLookup() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsRevealLookup,\n\t\tText: \"Reveal backup recovery codes\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsRegenerateLookup() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsRegenerateLookup,\n\t\tText: \"Generate new backup recovery codes\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsDisableLookup() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsDisableLookup,\n\t\tText: \"Disable this method\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsLookupConfirm() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsLookupConfirm,\n\t\tText: \"Confirm backup recovery codes\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsLookupSecretList(secrets []string, raw any) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsLookupSecretList,\n\t\tText: strings.Join(secrets, \", \"),\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"secrets\": raw,\n\t\t}),\n\t}\n}\nfunc NewInfoSelfServiceSettingsLookupSecret(secret string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsLookupSecret,\n\t\tText: secret,\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"secret\": secret,\n\t\t}),\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsLookupSecretUsed(usedAt time.Time) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsLookupSecretUsed,\n\t\tText: fmt.Sprintf(\"Secret was used at %s\", usedAt),\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"used_at\":      usedAt,\n\t\t\t\"used_at_unix\": usedAt.Unix(),\n\t\t}),\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsLookupSecretsLabel() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsLookupSecretLabel,\n\t\tText: \"These are your back up recovery codes. Please keep them in a safe place!\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsUpdateLinkOIDC(provider string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsUpdateLinkOidc,\n\t\tText: fmt.Sprintf(\"Link %s\", provider),\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"provider\": provider,\n\t\t}),\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsUpdateUnlinkOIDC(provider string) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsUpdateUnlinkOidc,\n\t\tText: fmt.Sprintf(\"Unlink %s\", provider),\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"provider\": provider,\n\t\t}),\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsRegisterWebAuthn() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsRegisterWebAuthn,\n\t\tText: \"Add security key\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceSettingsRegisterPasskey() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsRegisterPasskey,\n\t\tText: \"Add passkey\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceRegisterWebAuthnDisplayName() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsRegisterWebAuthnDisplayName,\n\t\tText: \"Name of the security key\",\n\t\tType: Info,\n\t}\n}\n\nfunc NewInfoSelfServiceRemoveWebAuthn(name string, createdAt time.Time) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsRemoveWebAuthn,\n\t\tText: fmt.Sprintf(\"Remove security key \\\"%s\\\"\", name),\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"display_name\":  name,\n\t\t\t\"added_at\":      createdAt,\n\t\t\t\"added_at_unix\": createdAt.Unix(),\n\t\t}),\n\t}\n}\n\nfunc NewInfoSelfServiceRemovePasskey(name string, createdAt time.Time) *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceSettingsRemovePasskey,\n\t\tText: fmt.Sprintf(\"Remove passkey \\\"%s\\\"\", name),\n\t\tType: Info,\n\t\tContext: context(map[string]any{\n\t\t\t\"display_name\":  name,\n\t\t\t\"added_at\":      createdAt,\n\t\t\t\"added_at_unix\": createdAt.Unix(),\n\t\t}),\n\t}\n}\n"
  },
  {
    "path": "text/message_system.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nfunc NewErrorSystemGeneric(reason string) *Message {\n\treturn &Message{\n\t\tID:   ErrorSystemGeneric,\n\t\tText: reason,\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"reason\": reason,\n\t\t}),\n\t}\n}\n\nfunc NewErrorSystemNoAuthenticationMethodsAvailable() *Message {\n\treturn &Message{\n\t\tID:   ErrorSystemNoAuthenticationMethodsAvailable,\n\t\tText: \"No authentication methods are available. Please contact the system administrator.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewCaptchaContainerMessage() *Message {\n\treturn &Message{\n\t\tID:   InfoNodeLabelCaptcha,\n\t\tText: \"Please complete the captcha challenge to continue.\",\n\t\tType: Info,\n\t}\n}\n"
  },
  {
    "path": "text/message_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMessage(t *testing.T) {\n\texpected := &Message{ID: InfoSelfServiceSettingsUpdateSuccess, Text: \"foo\", Type: Info}\n\n\tv, err := expected.Value()\n\trequire.NoError(t, err)\n\n\tvar actual Message\n\trequire.NoError(t, actual.Scan(v))\n\n\tassert.EqualValues(t, expected, &actual, v)\n}\n\nfunc TestNewErrorValidationDuplicateCredentialsWithHints(t *testing.T) {\n\tt.Run(\"title-cases real provider names\", func(t *testing.T) {\n\t\tmsg := NewErrorValidationDuplicateCredentialsWithHints(nil, []string{\"google\", \"github\"}, \"user@example.com\")\n\t\tassert.Contains(t, msg.Text, \"Google, Github\")\n\t})\n\n\tt.Run(\"preserves placeholder templates without title-casing\", func(t *testing.T) {\n\t\tmsg := NewErrorValidationDuplicateCredentialsWithHints(\n\t\t\t[]string{\"{available_credential_types_list}\"},\n\t\t\t[]string{\"{available_oidc_providers_list}\"},\n\t\t\t\"{credential_identifier_hint}\",\n\t\t)\n\t\tassert.Contains(t, msg.Text, \"{available_oidc_providers_list}\")\n\t\tassert.NotContains(t, msg.Text, \"{Available_oidc_providers_list}\")\n\t})\n}\n\nfunc TestMessages(t *testing.T) {\n\texpected := Messages{{ID: InfoSelfServiceSettingsUpdateSuccess, Text: \"foo\", Type: Info}}\n\n\tv, err := expected.Value()\n\trequire.NoError(t, err)\n\n\tvar actual Messages\n\trequire.NoError(t, actual.Scan(v))\n\n\tassert.EqualValues(t, expected, actual, v)\n}\n"
  },
  {
    "path": "text/message_validation.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/ory/x/stringslice\"\n\n\t\"golang.org/x/text/cases\"\n\t\"golang.org/x/text/language\"\n)\n\nfunc NewValidationErrorGeneric(reason string) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationGeneric,\n\t\tText: reason,\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"reason\": reason,\n\t\t}),\n\t}\n}\n\nfunc NewValidationErrorRequired(missing string) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationRequired,\n\t\tText: fmt.Sprintf(\"Property %s is missing.\", missing),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"property\": missing,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationMinLength(minLength, actualLength int) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationMinLength,\n\t\tText: fmt.Sprintf(\"length must be >= %d, but got %d\", minLength, actualLength),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"min_length\":    minLength,\n\t\t\t\"actual_length\": actualLength,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationMaxLength(maxLength, actualLength int) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationMaxLength,\n\t\tText: fmt.Sprintf(\"length must be <= %d, but got %d\", maxLength, actualLength),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"max_length\":    maxLength,\n\t\t\t\"actual_length\": actualLength,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationInvalidFormat(pattern string) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationInvalidFormat,\n\t\tText: fmt.Sprintf(\"does not match pattern %q\", pattern),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"pattern\": pattern,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationMinimum(minimum, actual float64) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationMinimum,\n\t\tText: fmt.Sprintf(\"must be >= %v but found %v\", minimum, actual),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"minimum\": minimum,\n\t\t\t\"actual\":  actual,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationExclusiveMinimum(minimum, actual float64) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationExclusiveMinimum,\n\t\tText: fmt.Sprintf(\"must be > %v but found %v\", minimum, actual),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"minimum\": minimum,\n\t\t\t\"actual\":  actual,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationMaximum(maximum, actual float64) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationMaximum,\n\t\tText: fmt.Sprintf(\"must be <= %v but found %v\", maximum, actual),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"maximum\": maximum,\n\t\t\t\"actual\":  actual,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationExclusiveMaximum(maximum, actual float64) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationExclusiveMaximum,\n\t\tText: fmt.Sprintf(\"must be < %v but found %v\", maximum, actual),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"maximum\": maximum,\n\t\t\t\"actual\":  actual,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationMultipleOf(base, actual float64) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationMultipleOf,\n\t\tText: fmt.Sprintf(\"%v not multipleOf %v\", actual, base),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"base\":   base,\n\t\t\t\"actual\": actual,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationMaxItems(maxItems, actualItems int) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationMaxItems,\n\t\tText: fmt.Sprintf(\"maximum %d items allowed, but found %d items\", maxItems, actualItems),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"max_items\":    maxItems,\n\t\t\t\"actual_items\": actualItems,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationMinItems(minItems, actualItems int) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationMinItems,\n\t\tText: fmt.Sprintf(\"minimum %d items allowed, but found %d items\", minItems, actualItems),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"min_items\":    minItems,\n\t\t\t\"actual_items\": actualItems,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationUniqueItems(indexA, indexB int) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationUniqueItems,\n\t\tText: fmt.Sprintf(\"items at index %d and %d are equal\", indexA, indexB),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"index_a\": indexA,\n\t\t\t\"index_b\": indexB,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationWrongType(allowedTypes []string, actualType string) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationWrongType,\n\t\tText: fmt.Sprintf(\"expected %s, but got %s\", strings.Join(allowedTypes, \" or \"), actualType),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"allowed_types\": allowedTypes,\n\t\t\t\"actual_type\":   actualType,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationConst(expected any) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationConst,\n\t\tText: fmt.Sprintf(\"must be equal to constant %v\", expected),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"expected\": expected,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationConstGeneric() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationConstGeneric,\n\t\tText: \"const failed\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationEmail(value string) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationEmail,\n\t\tText: \"Enter a valid email address\",\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"value\": value,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationPhone(value string) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationPhone,\n\t\tText: \"Enter a valid phone number\",\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"value\": value,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationPasswordPolicyViolationGeneric(reason string) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationPasswordPolicyViolationGeneric,\n\t\tText: fmt.Sprintf(\"The password can not be used because %s.\", reason),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"reason\": reason,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationPasswordIdentifierTooSimilar() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationPasswordIdentifierTooSimilar,\n\t\tText: \"The password can not be used because it is too similar to the identifier.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationPasswordMinLength(minLength, actualLength int) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationPasswordMinLength,\n\t\tText: fmt.Sprintf(\"The password must be at least %d characters long, but got %d.\", minLength, actualLength),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"min_length\":    minLength,\n\t\t\t\"actual_length\": actualLength,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationPasswordMaxLength(maxLength, actualLength int) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationPasswordMaxLength,\n\t\tText: fmt.Sprintf(\"The password must be at most %d characters long, but got %d.\", maxLength, actualLength),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"max_length\":    maxLength,\n\t\t\t\"actual_length\": actualLength,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationPasswordNewSameAsOld() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationPasswordNewSameAsOld,\n\t\tText: \"The new password must be different from the old password.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationPasswordTooManyBreaches(breaches int64) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationPasswordTooManyBreaches,\n\t\tText: \"The password has been found in data breaches and must no longer be used.\",\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"breaches\": breaches,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationInvalidCredentials() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationInvalidCredentials,\n\t\tText: \"The provided credentials are invalid, check for spelling mistakes in your password or username, email address, or phone number.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationAccountNotFound() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationAccountNotFound,\n\t\tText: \"This account does not exist or has no login method configured.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationDuplicateCredentials() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationDuplicateCredentials,\n\t\tText: \"An account with the same identifier (email, phone, username, ...) exists already.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationDuplicateCredentialsWithHints(availableCredentialTypes []string, availableOIDCProviders []string, credentialIdentifierHint string) *Message {\n\tidentifier := credentialIdentifierHint\n\tif identifier == \"\" {\n\t\tidentifier = \"an email, phone, or username\"\n\t}\n\toidcProviders := make([]string, 0, len(availableOIDCProviders))\n\tfor _, provider := range availableOIDCProviders {\n\t\tif strings.ContainsAny(provider, \"{}\") {\n\t\t\toidcProviders = append(oidcProviders, provider)\n\t\t} else {\n\t\t\toidcProviders = append(oidcProviders, cases.Title(language.English).String(provider))\n\t\t}\n\t}\n\n\treason := fmt.Sprintf(\"You tried signing in with %s which is already in use by another account.\", identifier)\n\tif len(availableCredentialTypes) > 0 {\n\t\thumanReadable := make([]string, 0, len(availableCredentialTypes))\n\t\tfor _, cred := range availableCredentialTypes {\n\t\t\tswitch cred {\n\t\t\tcase \"password\":\n\t\t\t\thumanReadable = append(humanReadable, \"your password\")\n\t\t\tcase \"oidc\", \"saml\":\n\t\t\t\thumanReadable = append(humanReadable, \"social sign in\")\n\t\t\tcase \"webauthn\":\n\t\t\t\thumanReadable = append(humanReadable, \"your passkey or a security key\")\n\t\t\tcase \"passkey\":\n\t\t\t\thumanReadable = append(humanReadable, \"your passkey\")\n\t\t\t}\n\t\t}\n\t\tif len(humanReadable) == 0 {\n\t\t\t// show at least some hint\n\t\t\t// also our example message generation tool runs into this case\n\t\t\thumanReadable = append(humanReadable, availableCredentialTypes...)\n\t\t}\n\n\t\thumanReadable = stringslice.Unique(humanReadable)\n\n\t\t// Final format: \"You can sign in using foo, bar, or baz.\"\n\t\tif len(humanReadable) > 1 {\n\t\t\thumanReadable[len(humanReadable)-1] = \"or \" + humanReadable[len(humanReadable)-1]\n\t\t}\n\t\tif len(humanReadable) > 0 {\n\t\t\treason += fmt.Sprintf(\" You can sign in using %s.\", strings.Join(humanReadable, \", \"))\n\t\t}\n\t}\n\tif len(oidcProviders) > 0 {\n\t\treason += fmt.Sprintf(\" You can sign in using one of the following social sign in providers: %s.\", strings.Join(oidcProviders, \", \"))\n\t}\n\n\treturn &Message{\n\t\tID:   ErrorValidationDuplicateCredentialsWithHints,\n\t\tText: reason,\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"available_credential_types\": availableCredentialTypes,\n\t\t\t\"available_oidc_providers\":   availableOIDCProviders,\n\t\t\t\"credential_identifier_hint\": credentialIdentifierHint,\n\t\t}),\n\t}\n}\n\nfunc NewErrorValidationDuplicateCredentialsOnOIDCLink() *Message {\n\treturn &Message{\n\t\tID: ErrorValidationDuplicateCredentialsOnOIDCLink,\n\t\tText: \"An account with the same identifier (email, phone, username, ...) exists already. \" +\n\t\t\t\"Please sign in to your existing account to link your social profile.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationTOTPVerifierWrong() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationTOTPVerifierWrong,\n\t\tText: \"The provided authentication code is invalid, please try again.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationLookupAlreadyUsed() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationLookupAlreadyUsed,\n\t\tText: \"This backup recovery code has already been used.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationLookupInvalid() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationLookupInvalid,\n\t\tText: \"The backup recovery code is not valid.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationIdentifierMissing() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationIdentifierMissing,\n\t\tText: \"Could not find any login identifiers. Did you forget to set them? This could also be caused by a server misconfiguration.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationAddressNotVerified() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationAddressNotVerified,\n\t\tText: \"Account not active yet. Did you forget to verify your email address?\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationNoTOTPDevice() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationNoTOTPDevice,\n\t\tText: \"You have no TOTP device set up.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationNoLookup() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationNoLookup,\n\t\tText: \"You have no backup recovery codes set up.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationNoWebAuthnDevice() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationNoWebAuthnDevice,\n\t\tText: \"You have no WebAuthn device set up.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationSuchNoWebAuthnUser() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationSuchNoWebAuthnUser,\n\t\tText: \"This account does not exist or has no security key set up.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationNoCodeUser() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationNoCodeUser,\n\t\tText: \"This account does not exist or has not setup sign in with code.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationTraitsMismatch() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationTraitsMismatch,\n\t\tText: \"The provided traits do not match the traits previously associated with this flow.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorCaptchaFailed() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationCaptchaError,\n\t\tText: \"Captcha verification failed, please try again.\",\n\t\tType: Error,\n\t}\n}\n"
  },
  {
    "path": "text/message_verification.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc NewErrorValidationVerificationFlowExpired(expiredAt time.Time) *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationVerificationFlowExpired,\n\t\tText: fmt.Sprintf(\"The verification flow expired %.2f minutes ago, please try again.\", Since(expiredAt).Minutes()),\n\t\tType: Error,\n\t\tContext: context(map[string]any{\n\t\t\t\"expired_at\":      expiredAt,\n\t\t\t\"expired_at_unix\": expiredAt.Unix(),\n\t\t}),\n\t}\n}\n\nfunc NewInfoSelfServiceVerificationSuccessful() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceVerificationSuccessful,\n\t\tType: Success,\n\t\tText: \"You successfully verified your email address.\",\n\t}\n}\n\nfunc NewVerificationEmailSent() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceVerificationEmailSent,\n\t\tType: Info,\n\t\tText: \"An email containing a verification link has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n\t}\n}\n\nfunc NewErrorValidationVerificationTokenInvalidOrAlreadyUsed() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationVerificationTokenInvalidOrAlreadyUsed,\n\t\tText: \"The verification token is invalid or has already been used. Please retry the flow.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationVerificationRetrySuccess() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationVerificationRetrySuccess,\n\t\tText: \"The request was already completed successfully and can not be retried.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationVerificationStateFailure() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationVerificationStateFailure,\n\t\tText: \"The verification flow reached a failure state and must be retried.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewErrorValidationVerificationCodeInvalidOrAlreadyUsed() *Message {\n\treturn &Message{\n\t\tID:   ErrorValidationVerificationCodeInvalidOrAlreadyUsed,\n\t\tText: \"The verification code is invalid or has already been used. Please try again.\",\n\t\tType: Error,\n\t}\n}\n\nfunc NewVerificationEmailWithCodeSent() *Message {\n\treturn &Message{\n\t\tID:   InfoSelfServiceVerificationEmailWithCodeSent,\n\t\tType: Info,\n\t\tText: \"An email containing a verification code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and make sure to use the address you registered with.\",\n\t}\n}\n"
  },
  {
    "path": "text/type.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage text\n\nimport \"time\"\n\n// swagger:enum UITextType\ntype UITextType string\n\n// aligned with https://github.com/ory/elements/blob/main/src/theme/message.css.ts\nconst (\n\tInfo    UITextType = \"info\"\n\tError   UITextType = \"error\"\n\tSuccess UITextType = \"success\"\n)\n\nvar Until = time.Until\nvar Since = time.Since\n"
  },
  {
    "path": "ui/container/container.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage container\n\nimport (\n\t\"context\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\t\"github.com/ory/x/sqlxx\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\n\t\"github.com/ory/x/decoderx\"\n\t\"github.com/ory/x/jsonschemax\"\n\t\"github.com/ory/x/jsonx\"\n\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/text\"\n)\n\nvar (\n\t_ ErrorParser = (*Container)(nil)\n\t_ ValueSetter = (*Container)(nil)\n\t_ Resetter    = (*Container)(nil)\n\t_ CSRFSetter  = (*Container)(nil)\n\t_ NodeGetter  = (*Container)(nil)\n)\n\n// Container represents a HTML Form. The container can work with both HTTP Form and JSON requests\n//\n// swagger:model uiContainer\ntype Container struct {\n\t// Action should be used as the form action URL `<form action=\"{{ .Action }}\" method=\"post\">`.\n\t//\n\t// required: true\n\tAction string `json:\"action\" faker:\"url\"`\n\n\t// Method is the form method (e.g. POST)\n\t//\n\t// required: true\n\tMethod string `json:\"method\" faker:\"http_method\"`\n\n\t// Nodes contains the form's nodes\n\t//\n\t// The form's nodes can be input fields, text, images, and other UI elements.\n\t//\n\t// required: true\n\tNodes node.Nodes `json:\"nodes\"`\n\n\t// Messages contains all global form messages and errors.\n\tMessages text.Messages `json:\"messages,omitempty\"`\n}\n\n// New returns an empty container.\nfunc New(action string) *Container {\n\treturn &Container{\n\t\tAction: action,\n\t\tMethod: \"POST\",\n\t\tNodes:  node.Nodes{},\n\t}\n}\n\n// NewFromHTTPRequest creates a new Container and populates fields by parsing the HTTP Request body.\n// A jsonSchemaRef needs to be added to allow HTTP Form Post Body parsing.\nfunc NewFromHTTPRequest(r *http.Request, group node.UiNodeGroup, action string, compiler decoderx.HTTPDecoderOption) (*Container, error) {\n\tc := New(action)\n\traw := json.RawMessage(`{}`)\n\tif err := decoderx.Decode(r, &raw, compiler); err != nil {\n\t\tif err := c.ParseError(group, err); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tc.UpdateNodeValuesFromJSON(raw, \"\", group)\n\treturn c, nil\n}\n\n// NewFromJSON creates a UI Container based on the provided JSON struct.\nfunc NewFromJSON(action string, group node.UiNodeGroup, raw json.RawMessage, prefix string) *Container {\n\tc := New(action)\n\tc.UpdateNodeValuesFromJSON(raw, prefix, group)\n\treturn c\n}\n\n// NewFromStruct creates a UI Container based on serialized contents of the provided struct.\nfunc NewFromStruct(action string, group node.UiNodeGroup, v interface{}, prefix string) (*Container, error) {\n\tc := New(action)\n\tdata, err := json.Marshal(v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc.UpdateNodeValuesFromJSON(data, prefix, group)\n\treturn c, nil\n}\n\n// NewFromJSONSchema creates a new Container and populates the fields\n// using the provided JSON Schema.\nfunc NewFromJSONSchema(ctx context.Context, action string, group node.UiNodeGroup, jsonSchemaRef, prefix string, compiler *jsonschema.Compiler) (*Container, error) {\n\tc := New(action)\n\tnodes, err := NodesFromJSONSchema(ctx, group, jsonSchemaRef, prefix, compiler)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tc.Nodes = nodes\n\treturn c, nil\n}\n\nfunc NodesFromJSONSchema(ctx context.Context, group node.UiNodeGroup, jsonSchemaRef, prefix string, compiler *jsonschema.Compiler) (node.Nodes, error) {\n\tpaths, err := jsonschemax.ListPaths(ctx, jsonSchemaRef, compiler)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnodes := node.Nodes{}\n\tfor _, value := range paths {\n\t\tif value.TypeHint == jsonschemax.JSON {\n\t\t\tcontinue\n\t\t}\n\n\t\tname := addPrefix(value.Name, prefix, \".\")\n\t\tnodes = append(nodes, node.NewInputFieldFromSchema(name, group, value))\n\t}\n\n\treturn nodes, nil\n}\n\nfunc (c *Container) GetNodes() *node.Nodes {\n\treturn &c.Nodes\n}\n\nfunc (c *Container) SortNodes(ctx context.Context, opts ...node.SortOption) error {\n\treturn c.Nodes.SortBySchema(ctx, opts...)\n}\n\n// ResetMessages resets the container's own and its node's messages.\nfunc (c *Container) ResetMessages(exclude ...string) {\n\tc.Messages = nil\n\tfor k, n := range c.Nodes {\n\t\tif !slices.Contains(exclude, n.ID()) {\n\t\t\tn.Messages = nil\n\t\t}\n\t\tc.Nodes[k] = n\n\t}\n}\n\n// Reset resets the container's errors as well as each field's value and errors.\nfunc (c *Container) Reset(exclude ...string) {\n\tc.Messages = nil\n\tc.Nodes.Reset(exclude...)\n}\n\n// ParseError type asserts the given error and sets the container's errors or a\n// field's errors and if the error is not something to be handled by the\n// formUI Container, the error is returned.\n//\n// This method DOES NOT touch the values of the node values/names, only its errors.\nfunc (c *Container) ParseError(group node.UiNodeGroup, err error) error {\n\tif e := richError(nil); errors.As(err, &e) {\n\t\tif e.StatusCode() == http.StatusBadRequest {\n\t\t\tc.AddMessage(group, text.NewValidationErrorGeneric(e.Reason()))\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t} else if e := new(schema.ValidationError); errors.As(err, &e) {\n\t\tpointer, _ := jsonschemax.JSONPointerToDotNotation(e.InstancePtr)\n\t\tfor i := range e.Messages {\n\t\t\tc.AddMessage(group, &e.Messages[i], pointer)\n\t\t}\n\t\treturn nil\n\t} else if e := new(jsonschema.ValidationError); errors.As(err, &e) {\n\t\tswitch ctx := e.Context.(type) {\n\t\tcase *jsonschema.ValidationErrorContextRequired:\n\t\t\tfor _, required := range ctx.Missing {\n\t\t\t\t// The pointer can be ignored because if there is an error, we'll just use\n\t\t\t\t// the empty field (global error).\n\t\t\t\tpointer, _ := jsonschemax.JSONPointerToDotNotation(required)\n\t\t\t\tsegments := strings.Split(required, \"/\")\n\t\t\t\tc.AddMessage(group, text.NewValidationErrorRequired(segments[len(segments)-1]), pointer)\n\t\t\t}\n\t\tdefault:\n\t\t\t// The pointer can be ignored because if there is an error, we'll just use\n\t\t\t// the empty field (global error).\n\t\t\tcauses := e.Causes\n\t\t\tif len(e.Causes) == 0 {\n\t\t\t\tpointer, _ := jsonschemax.JSONPointerToDotNotation(e.InstancePtr)\n\t\t\t\tc.AddMessage(group, translateValidationError(e), pointer)\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tfor _, ee := range causes {\n\t\t\t\tif err := c.ParseError(group, ee); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t} else if e := new(schema.ValidationListError); errors.As(err, &e) {\n\t\tfor _, ee := range e.Validations {\n\t\t\tif err := c.ParseError(group, ee); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\treturn err\n}\n\nvar formatErrorMatcher = regexp.MustCompile(`\"([^\"]+)\"\\s+is not valid\\s+\"([^\"]+)\"`)\n\nfunc translateValidationError(err *jsonschema.ValidationError) *text.Message {\n\tsegments := strings.Split(err.SchemaPtr, \"/\")\n\tswitch segments[len(segments)-1] {\n\tcase \"minLength\":\n\t\tminLength, actual := -1, -1\n\t\t_, _ = fmt.Sscanf(err.Message, \"length must be >= %d, but got %d\", &minLength, &actual)\n\t\treturn text.NewErrorValidationMinLength(minLength, actual)\n\tcase \"maxLength\":\n\t\tmaxLength, actual := -1, -1\n\t\t_, _ = fmt.Sscanf(err.Message, \"length must be <= %d, but got %d\", &maxLength, &actual)\n\t\treturn text.NewErrorValidationMaxLength(maxLength, actual)\n\tcase \"pattern\":\n\t\tpattern := \"\"\n\t\t_, _ = fmt.Sscanf(err.Message, \"does not match pattern %q\", &pattern)\n\t\treturn text.NewErrorValidationInvalidFormat(pattern)\n\tcase \"minimum\":\n\t\tminimum, actual := -1.0, -1.0\n\t\t_, _ = fmt.Sscanf(err.Message, \"must be >= %v but found %v\", &minimum, &actual)\n\t\treturn text.NewErrorValidationMinimum(minimum, actual)\n\tcase \"exclusiveMinimum\":\n\t\tminimum, actual := -1.0, -1.0\n\t\t_, _ = fmt.Sscanf(err.Message, \"must be > %v but found %v\", &minimum, &actual)\n\t\treturn text.NewErrorValidationExclusiveMinimum(minimum, actual)\n\tcase \"maximum\":\n\t\tmaximum, actual := -1.0, -1.0\n\t\t_, _ = fmt.Sscanf(err.Message, \"must be <= %v but found %v\", &maximum, &actual)\n\t\treturn text.NewErrorValidationMaximum(maximum, actual)\n\tcase \"exclusiveMaximum\":\n\t\tmaximum, actual := -1.0, -1.0\n\t\t_, _ = fmt.Sscanf(err.Message, \"must be < %v but found %v\", &maximum, &actual)\n\t\treturn text.NewErrorValidationExclusiveMaximum(maximum, actual)\n\tcase \"multipleOf\":\n\t\tbase, actual := -1.0, -1.0\n\t\t_, _ = fmt.Sscanf(err.Message, \"%v not multipleOf %v\", &actual, &base)\n\t\treturn text.NewErrorValidationMultipleOf(base, actual)\n\tcase \"maxItems\":\n\t\tmaxItems, actual := -1, -1\n\t\t_, _ = fmt.Sscanf(err.Message, \"maximum %d items allowed, but found %d items\", &maxItems, &actual)\n\t\treturn text.NewErrorValidationMaxItems(maxItems, actual)\n\tcase \"minItems\":\n\t\tminItems, actual := -1, -1\n\t\t_, _ = fmt.Sscanf(err.Message, \"minimum %d items allowed, but found %d items\", &minItems, &actual)\n\t\treturn text.NewErrorValidationMinItems(minItems, actual)\n\tcase \"uniqueItems\":\n\t\tindexA, indexB := -1, -1\n\t\t_, _ = fmt.Sscanf(err.Message, \"items at index %d and %d are equal\", &indexA, &indexB)\n\t\treturn text.NewErrorValidationUniqueItems(indexA, indexB)\n\tcase \"type\":\n\t\tallowedTypes, actualType, _ := strings.Cut(strings.TrimPrefix(err.Message, \"expected \"), \", but got \")\n\t\treturn text.NewErrorValidationWrongType(strings.Split(allowedTypes, \" or \"), actualType)\n\tcase \"const\":\n\t\tif err.Message != \"const failed\" {\n\t\t\texpectedValue := strings.TrimPrefix(err.Message, \"value must be \")\n\t\t\treturn text.NewErrorValidationConst(expectedValue)\n\t\t}\n\t\treturn text.NewErrorValidationConstGeneric()\n\tcase \"format\":\n\t\tm := formatErrorMatcher.FindStringSubmatch(err.Message)\n\t\tif len(m) != 3 {\n\t\t\treturn text.NewValidationErrorGeneric(err.Message)\n\t\t}\n\n\t\tvalue := m[1]\n\t\tformat := m[2]\n\n\t\tswitch format {\n\t\tcase \"email\":\n\t\t\treturn text.NewErrorValidationEmail(value)\n\t\tcase \"tel\":\n\t\t\treturn text.NewErrorValidationPhone(value)\n\t\tdefault:\n\t\t\treturn text.NewValidationErrorGeneric(err.Message)\n\t\t}\n\tdefault:\n\t\treturn text.NewValidationErrorGeneric(err.Message)\n\t}\n}\n\n// UpdateNodeValuesFromJSON sets the container's fields to the provided values.\nfunc (c *Container) UpdateNodeValuesFromJSON(raw json.RawMessage, prefix string, group node.UiNodeGroup) {\n\tfor k, v := range jsonx.Flatten(raw) {\n\t\tk = addPrefix(k, prefix, \".\")\n\n\t\tif n := c.Nodes.Find(k); n != nil {\n\t\t\tn.Attributes.SetValue(v)\n\t\t\tn.Group = group\n\t\t\tcontinue\n\t\t}\n\n\t\tc.Nodes.Upsert(node.NewInputFieldFromJSON(k, v, group))\n\t}\n}\n\n// Unset removes a field from the container.\nfunc (c *Container) UnsetNode(id string) {\n\tc.Nodes.Remove(id)\n}\n\n// SetCSRF sets the CSRF value using e.g. nosurf.Token(r).\nfunc (c *Container) SetCSRF(token string) {\n\tc.SetNode(node.NewCSRFNode(token))\n}\n\n// SetNode sets a field.\nfunc (c *Container) SetNode(n *node.Node) {\n\tc.Nodes.Upsert(n)\n}\n\n// SetValue sets a container's field to the provided name and value.\nfunc (c *Container) SetValue(id string, n *node.Node) {\n\tif f := c.Nodes.Find(id); f != nil {\n\t\tf.Attributes.SetValue(n.GetValue())\n\t\treturn\n\t}\n\n\tc.Nodes.Upsert(n)\n}\n\n// AddMessage adds the provided error, and if a non-empty names list is set,\n// adds the error on the corresponding field.\nfunc (c *Container) AddMessage(group node.UiNodeGroup, err *text.Message, setForFields ...string) {\n\tif !slices.ContainsFunc(setForFields, func(s string) bool { return strings.TrimSpace(s) != \"\" }) {\n\t\tc.Messages = append(c.Messages, *err)\n\t\treturn\n\t}\n\n\tfor _, name := range setForFields {\n\t\tif ff := c.Nodes.Find(name); ff != nil {\n\t\t\tff.Messages = append(ff.Messages, *err)\n\t\t\tcontinue\n\t\t}\n\n\t\tn := node.NewInputField(name, nil, node.DefaultGroup, node.InputAttributeTypeText)\n\t\tn.Messages = text.Messages{*err}\n\t\tc.Nodes = append(c.Nodes, n)\n\t}\n}\n\nfunc (c *Container) Scan(value interface{}) error {\n\treturn sqlxx.JSONScan(c, value)\n}\n\nfunc (c *Container) Value() (driver.Value, error) {\n\tif c == nil {\n\t\treturn nil, nil\n\t}\n\treturn json.Marshal(c)\n}\n\nfunc addPrefix(name, prefix, separator string) string {\n\tif prefix == \"\" {\n\t\treturn name\n\t}\n\treturn fmt.Sprintf(\"%s%s%s\", prefix, separator, name)\n}\n"
  },
  {
    "path": "ui/container/container_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage container\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/ui/node\"\n\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/decoderx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/text\"\n)\n\nfunc newJSONRequest(t *testing.T, j string) *http.Request {\n\treq, err := http.NewRequest(\"POST\", \"/\", bytes.NewBufferString(j))\n\trequire.NoError(t, err)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treturn req\n}\n\nfunc newFormRequest(t *testing.T, values url.Values) *http.Request {\n\treq, err := http.NewRequest(\"POST\", \"/\", bytes.NewBufferString(values.Encode()))\n\trequire.NoError(t, err)\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treturn req\n}\n\nfunc TestContainer(t *testing.T) {\n\tctx := context.Background()\n\tt.Run(\"method=NewFromJSON\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\tr      string\n\t\t\tprefix string\n\t\t\texpect *Container\n\t\t}{\n\t\t\t{\n\t\t\t\tr: `{\"numby\":1.5,\"stringy\":\"foobar\",\"objy\":{\"objy\":{},\"numby\":1.5,\"stringy\":\"foobar\"}}`,\n\t\t\t\texpect: &Container{\n\t\t\t\t\tNodes: node.Nodes{\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"numby\", 1.5, node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"objy.numby\", 1.5, node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"objy.stringy\", \"foobar\", node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"stringy\", \"foobar\", node.DefaultGroup),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tr:      `{\"numby\":1.5,\"stringy\":\"foobar\",\"objy\":{\"objy\":{},\"numby\":1.5,\"stringy\":\"foobar\"}}`,\n\t\t\t\tprefix: \"traits\",\n\t\t\t\texpect: &Container{\n\t\t\t\t\tNodes: node.Nodes{\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"traits.numby\", 1.5, node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"traits.objy.numby\", 1.5, node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"traits.objy.stringy\", \"foobar\", node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"traits.stringy\", \"foobar\", node.DefaultGroup),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tactual := NewFromJSON(\"action\", node.DefaultGroup, json.RawMessage(tc.r), tc.prefix)\n\n\t\t\t\t// sort actual.fields lexicographically to have a deterministic order\n\t\t\t\tsort.SliceStable(actual.Nodes, func(i, j int) bool {\n\t\t\t\t\treturn actual.Nodes[i].ID() < actual.Nodes[j].ID()\n\t\t\t\t})\n\n\t\t\t\tassert.Equal(t, \"action\", actual.Action)\n\t\t\t\tassert.EqualValues(t, tc.expect.Nodes, actual.Nodes)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"method=NewFromHTTPRequest\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\tref    string\n\t\t\tr      *http.Request\n\t\t\texpect *Container\n\t\t}{\n\t\t\t{\n\t\t\t\tref: \"./stub/simple.schema.json\",\n\t\t\t\tr:   newJSONRequest(t, `{\"numby\":1.5,\"stringy\":\"foobar\",\"objy\":{\"objy\":{},\"numby\":1.5,\"stringy\":\"foobar\"}}`),\n\t\t\t\texpect: &Container{\n\t\t\t\t\tNodes: node.Nodes{\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"numby\", 1.5, node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"objy.numby\", 1.5, node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"objy.stringy\", \"foobar\", node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"stringy\", \"foobar\", node.DefaultGroup),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tref: \"./stub/simple.schema.json\",\n\t\t\t\tr: newFormRequest(t, url.Values{\n\t\t\t\t\t\"numby\":        {\"1.5\"},\n\t\t\t\t\t\"stringy\":      {\"foobar\"},\n\t\t\t\t\t\"objy.numby\":   {\"1.5\"},\n\t\t\t\t\t\"objy.stringy\": {\"foobar\"},\n\t\t\t\t}),\n\t\t\t\texpect: &Container{\n\t\t\t\t\tNodes: node.Nodes{\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"numby\", 1.5, node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"objy.numby\", 1.5, node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"objy.stringy\", \"foobar\", node.DefaultGroup),\n\t\t\t\t\t\tnode.NewInputFieldFromJSON(\"stringy\", \"foobar\", node.DefaultGroup),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t// FIXME https://github.com/ory/kratos/issues/1316\n\t\t\t//\n\t\t\t//{\n\t\t\t//\tref: \"./stub/complex.schema.json\",\n\t\t\t//\tr: newFormRequest(t, url.Values{\n\t\t\t//\t\t\"meal.chef\": {\"aeneas\"},\n\t\t\t//\t}),\n\t\t\t//\texpect: &Container{\n\t\t\t//\t\tNodes: node.Nodes{\n\t\t\t//\t\t\tnode.NewInputFieldFromJSON(\"meal.chef\", \"aeneas\", node.DefaultGroup),\n\t\t\t//\t\t\t&node.Node{Group: node.DefaultGroup, Type: node.Input, Attributes: &node.InputAttributes{Name: \"meal.name\", Type: node.InputAttributeTypeText}, Messages: text.Messages{*text.NewValidationErrorRequired(\"name\")}, Meta: &node.Meta{}},\n\t\t\t//\t\t},\n\t\t\t//\t},\n\t\t\t//},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tactual, err := NewFromHTTPRequest(tc.r, node.DefaultGroup, \"action\", decoderx.HTTPJSONSchemaCompiler(tc.ref, nil))\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\t// sort actual.fields lexicographically to have a deterministic order\n\t\t\t\tsort.SliceStable(actual.Nodes, func(i, j int) bool {\n\t\t\t\t\treturn actual.Nodes[i].ID() < actual.Nodes[j].ID()\n\t\t\t\t})\n\t\t\t\tassert.Equal(t, \"action\", actual.Action)\n\t\t\t\tassert.EqualValues(t, tc.expect.Nodes, actual.Nodes)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"method=NewFromJSONSchema\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\tref    string\n\t\t\tprefix string\n\t\t\texpect *Container\n\t\t}{\n\t\t\t{\n\t\t\t\tref:    \"./stub/simple.schema.json\",\n\t\t\t\tprefix: \"\",\n\t\t\t\texpect: &Container{\n\t\t\t\t\tNodes: node.Nodes{\n\t\t\t\t\t\tnode.NewInputField(\"numby\", nil, node.DefaultGroup, node.InputAttributeTypeNumber),\n\t\t\t\t\t\tnode.NewInputField(\"objy.numby\", nil, node.DefaultGroup, node.InputAttributeTypeNumber),\n\t\t\t\t\t\tnode.NewInputField(\"objy.stringy\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\t\t\tnode.NewInputField(\"stringy\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tref:    \"./stub/simple.schema.json\",\n\t\t\t\tprefix: \"traits\",\n\t\t\t\texpect: &Container{\n\t\t\t\t\tNodes: node.Nodes{\n\t\t\t\t\t\tnode.NewInputField(\"traits.numby\", nil, node.DefaultGroup, node.InputAttributeTypeNumber),\n\t\t\t\t\t\tnode.NewInputField(\"traits.objy.numby\", nil, node.DefaultGroup, node.InputAttributeTypeNumber),\n\t\t\t\t\t\tnode.NewInputField(\"traits.objy.stringy\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\t\t\tnode.NewInputField(\"traits.stringy\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tref: \"./stub/complex.schema.json\",\n\t\t\t\texpect: &Container{\n\t\t\t\t\tNodes: node.Nodes{\n\t\t\t\t\t\tnode.NewInputField(\"fruits\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\t\t\tnode.NewInputField(\"meal.chef\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\t\t\tnode.NewInputField(\"meal.name\", nil, node.DefaultGroup, node.InputAttributeTypeText, node.WithRequiredInputAttribute),\n\n\t\t\t\t\t\t// FIXME https://github.com/ory/kratos/issues/1316\n\t\t\t\t\t\t//\n\t\t\t\t\t\t//node.NewInputField(\"vegetables.veggieName\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\t\t\t//node.NewInputField(\"vegetables.veggieLike\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\t\t\t//node.NewInputField(\"vegetables.veggieAmount\", nil, node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tactual, err := NewFromJSONSchema(ctx, \"action\",\n\t\t\t\t\tnode.DefaultGroup, tc.ref, tc.prefix, nil)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, \"action\", actual.Action)\n\t\t\t\tassert.EqualValues(t, tc.expect.Messages, actual.Messages)\n\t\t\t\tassert.EqualValues(t, tc.expect.Nodes, actual.Nodes)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"method=ParseError\", func(t *testing.T) {\n\t\tfor k, tc := range []struct {\n\t\t\terr       error\n\t\t\texpectErr bool\n\t\t\texpect    Container\n\t\t}{\n\t\t\t{err: errors.New(\"foo\"), expectErr: true},\n\t\t\t{err: &herodot.ErrNotFound, expectErr: true},\n\t\t\t{err: herodot.ErrBadRequest.WithReason(\"tests\"), expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewValidationErrorGeneric(\"tests\")}}},\n\t\t\t{err: schema.NewInvalidCredentialsError(), expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewErrorValidationInvalidCredentials()}}},\n\t\t\t{err: &jsonschema.ValidationError{Message: \"test\", InstancePtr: \"#/foo/bar/baz\"}, expect: Container{Nodes: node.Nodes{\n\t\t\t\t&node.Node{Group: node.DefaultGroup, Type: node.Input, Attributes: &node.InputAttributes{Name: \"foo.bar.baz\", Type: node.InputAttributeTypeText}, Messages: text.Messages{*text.NewValidationErrorGeneric(\"test\")}, Meta: new(node.Meta)},\n\t\t\t}}},\n\t\t\t{err: &jsonschema.ValidationError{Message: \"test\", InstancePtr: \"\"}, expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewValidationErrorGeneric(\"test\")}}},\n\t\t\t{err: &jsonschema.ValidationError{Message: `\"asd\" is not valid \"email\"`, InstancePtr: \"\", SchemaPtr: \"#/email/format\"}, expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewErrorValidationEmail(\"asd\")}}},\n\t\t\t{err: &jsonschema.ValidationError{Message: `\"asd\" is not valid \"tel\"`, InstancePtr: \"\", SchemaPtr: \"#/phone/format\"}, expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewErrorValidationPhone(\"asd\")}}},\n\t\t\t{err: &jsonschema.ValidationError{Message: `\"asd\" is not valid \"unknown-format\"`, InstancePtr: \"\", SchemaPtr: \"#/unknown/format\"}, expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewValidationErrorGeneric(`\"asd\" is not valid \"unknown-format\"`)}}},\n\t\t\t// Malformed format error message\n\t\t\t{err: &jsonschema.ValidationError{Message: `\"asd\" is not valid \"unknown-format`, InstancePtr: \"\", SchemaPtr: \"#/unknown/format\"}, expect: Container{Nodes: node.Nodes{}, Messages: text.Messages{*text.NewValidationErrorGeneric(`\"asd\" is not valid \"unknown-format`)}}},\n\t\t} {\n\t\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\t\tfor _, in := range []error{tc.err, errors.WithStack(tc.err)} {\n\t\t\t\t\tc := New(\"\")\n\t\t\t\t\terr := c.ParseError(node.DefaultGroup, in)\n\t\t\t\t\tif tc.expectErr {\n\t\t\t\t\t\trequire.Error(t, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tassert.EqualValues(t, tc.expect.Messages, c.Messages)\n\t\t\t\t\tassert.EqualValues(t, tc.expect.Nodes, c.Nodes)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"method=SetValue\", func(t *testing.T) {\n\t\tc := Container{\n\t\t\tNodes: node.Nodes{\n\t\t\t\tnode.NewInputField(\"1\", \"foo\", node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t\tnode.NewInputField(\"2\", \"\", node.DefaultGroup, node.InputAttributeTypeText),\n\t\t\t},\n\t\t}\n\n\t\tassert.Len(t, c.Nodes, 2)\n\n\t\tc.SetValue(\"1\", node.NewInputFieldFromJSON(\"1\", \"baz1\", node.DefaultGroup))\n\t\tc.SetValue(\"2\", node.NewInputFieldFromJSON(\"2\", \"baz2\", node.DefaultGroup))\n\t\tc.SetValue(\"3\", node.NewInputFieldFromJSON(\"3\", \"baz3\", node.DefaultGroup))\n\n\t\tassert.Len(t, c.Nodes, 3)\n\t\tfor _, k := range []string{\"1\", \"2\", \"3\"} {\n\t\t\tassert.EqualValues(t, fmt.Sprintf(\"baz%s\", k), c.Nodes.Find(k).Attributes.GetValue(), \"%+v\", c)\n\t\t}\n\t})\n\n\tt.Run(\"method=SetCSRF\", func(t *testing.T) {\n\t\tf := &Container{Nodes: node.Nodes{}}\n\t\tf.SetCSRF(\"csrf-token\")\n\t\tassert.Contains(t, f.Nodes, node.NewCSRFNode(\"csrf-token\"))\n\t})\n\n\tt.Run(\"method=AddMessage\", func(t *testing.T) {\n\t\tc := Container{\n\t\t\tNodes: node.Nodes{\n\t\t\t\t&node.Node{Messages: text.Messages{*text.NewValidationErrorGeneric(\"foo\")}, Group: node.DefaultGroup, Type: node.Input, Attributes: &node.InputAttributes{Name: \"1\", Type: node.InputAttributeTypeText, FieldValue: \"\"}},\n\t\t\t\t&node.Node{Messages: text.Messages{}, Group: node.DefaultGroup, Type: node.Input, Attributes: &node.InputAttributes{Name: \"2\", Type: node.InputAttributeTypeText, FieldValue: \"\"}},\n\t\t\t},\n\t\t}\n\t\tassert.Len(t, c.Nodes, 2)\n\t\tc.AddMessage(node.DefaultGroup, &text.Message{Text: \"baz1\"}, \"1\")\n\t\tc.AddMessage(node.DefaultGroup, &text.Message{Text: \"baz2\"}, \"2\")\n\t\tc.AddMessage(node.DefaultGroup, &text.Message{Text: \"baz3\"}, \"3\")\n\t\tc.AddMessage(node.DefaultGroup, &text.Message{Text: \"baz\"}, \"4\", \"5\", \"6\")\n\t\tc.AddMessage(node.DefaultGroup, &text.Message{Text: \"rootbar\"})\n\n\t\tassert.Len(t, c.Nodes, 6)\n\n\t\tfor _, k := range []string{\"1\", \"2\", \"3\"} {\n\t\t\tn := c.Nodes.Find(k)\n\t\t\tassert.EqualValues(t, fmt.Sprintf(\"baz%s\", k), n.Messages[len(n.Messages)-1].Text, \"%+v\", c)\n\t\t}\n\n\t\tfor _, k := range []string{\"4\", \"5\", \"6\"} {\n\t\t\tassert.EqualValues(t, \"baz\", c.Nodes.Find(k).Messages[0].Text, \"%+v\", c)\n\t\t}\n\n\t\tassert.Len(t, c.Messages, 1)\n\t\tassert.Equal(t, \"rootbar\", c.Messages[0].Text)\n\t})\n\n\tt.Run(\"method=Reset\", func(t *testing.T) {\n\t\tc := Container{\n\t\t\tNodes: node.Nodes{\n\t\t\t\t&node.Node{Messages: text.Messages{{Text: \"foo\"}}, Group: node.DefaultGroup, Type: node.Input, Attributes: &node.InputAttributes{Name: \"1\", Type: node.InputAttributeTypeText, FieldValue: \"foo\"}},\n\t\t\t\t&node.Node{Messages: text.Messages{{Text: \"bar\"}}, Group: node.DefaultGroup, Type: node.Input, Attributes: &node.InputAttributes{Name: \"2\", Type: node.InputAttributeTypeText, FieldValue: \"bar\"}},\n\t\t\t},\n\t\t\tMessages: text.Messages{{Text: \"\"}},\n\t\t}\n\t\tc.Reset()\n\n\t\tassert.Empty(t, c.Messages)\n\t\tassert.Empty(t, c.Nodes.Find(\"1\").Messages)\n\t\tassert.Empty(t, c.Nodes.Find(\"1\").Attributes.(*node.InputAttributes).FieldValue)\n\t\tassert.Empty(t, c.Nodes.Find(\"2\").Messages)\n\t\tassert.Empty(t, c.Nodes.Find(\"2\").Attributes.(*node.InputAttributes).FieldValue)\n\t})\n\n\tt.Run(\"method=remove\", func(t *testing.T) {\n\t\tc := Container{\n\t\t\tNodes: node.Nodes{\n\t\t\t\t&node.Node{Messages: text.Messages{{Text: \"foo\"}}, Group: node.DefaultGroup, Type: node.Input, Attributes: &node.InputAttributes{Name: \"1\", Type: node.InputAttributeTypeText, FieldValue: \"foo\"}},\n\t\t\t\t&node.Node{Messages: text.Messages{{Text: \"bar\"}}, Group: node.DefaultGroup, Type: node.Input, Attributes: &node.InputAttributes{Name: \"2\", Type: node.InputAttributeTypeText, FieldValue: \"bar\"}},\n\t\t\t},\n\t\t\tMessages: text.Messages{{Text: \"\"}},\n\t\t}\n\n\t\trequire.Len(t, c.Nodes, 2)\n\t\tc.GetNodes().Remove(\"1\")\n\t\trequire.Len(t, c.Nodes, 1)\n\t\trequire.EqualValues(t, \"bar\", c.Nodes[0].Attributes.GetValue())\n\t})\n}\n"
  },
  {
    "path": "ui/container/error.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage container\n\ntype (\n\trichError interface {\n\t\tStatusCode() int\n\t\tReason() string\n\t}\n)\n"
  },
  {
    "path": "ui/container/stub/all_formats.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/all_formats.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"password\": {\n      \"type\": \"string\",\n      \"test_expected_type\": \"password\"\n    },\n    \"csrf_token\": {\n      \"type\": \"string\",\n      \"test_expected_type\": \"hidden\"\n    },\n    \"simpleString\": {\n      \"type\": \"string\",\n      \"test_expected_type\": \"text\"\n    },\n    \"simpleNumber\": {\n      \"type\": \"number\",\n      \"test_expected_type\": \"number\"\n    },\n    \"simpleBoolean\": {\n      \"type\": \"boolean\",\n      \"test_expected_type\": \"checkbox\"\n    },\n    \"emailString\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"test_expected_type\": \"email\"\n    },\n    \"dateTimeString\": {\n      \"type\": \"string\",\n      \"format\": \"date-time\",\n      \"test_expected_type\": \"datetime-local\"\n    },\n    \"regexString\": {\n      \"type\": \"string\",\n      \"format\": \"regex\",\n      \"test_expected_type\": \"text\"\n    },\n    \"dateString\": {\n      \"type\": \"string\",\n      \"format\": \"date\",\n      \"test_expected_type\": \"date\"\n    },\n    \"uriString\": {\n      \"type\": \"string\",\n      \"format\": \"uri\",\n      \"test_expected_type\": \"url\"\n    },\n    \"patternString\": {\n      \"type\": \"string\",\n      \"pattern\": \"foo.*\",\n      \"test_expected_type\": \"text\",\n      \"test_expected_pattern\": true\n    }\n  }\n}\n"
  },
  {
    "path": "ui/container/stub/complex.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/complex.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\"meal\"],\n  \"properties\": {\n    \"meal\": {\n      \"type\": \"object\",\n      \"required\": [\"name\"],\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\"\n        },\n        \"chef\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"fruits\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"vegetables\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"$ref\": \"#/definitions/veggie\"\n      }\n    }\n  },\n  \"definitions\": {\n    \"veggie\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"veggieName\",\n        \"veggieLike\"\n      ],\n      \"properties\": {\n        \"veggieName\": {\n          \"type\": \"string\"\n        },\n        \"veggieLike\": {\n          \"type\": \"boolean\"\n        },\n        \"veggieAmount\": {\n          \"type\": \"number\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ui/container/stub/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"booly\": {\n          \"type\": \"boolean\"\n        },\n        \"should_big_number\": {\n          \"type\": \"number\",\n          \"minimum\": 1200\n        },\n        \"should_long_string\": {\n          \"type\": \"string\",\n          \"minLength\": 25\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ui/container/stub/simple.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"stringy\": {\n      \"type\": \"string\"\n    },\n    \"numby\": {\n      \"type\": \"number\"\n    },\n    \"objy\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"objy\": {\n          \"type\": \"object\",\n          \"properties\": {}\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ui/container/types.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage container\n\nimport (\n\t\"github.com/ory/kratos/ui/node\"\n)\n\n// ErrorParser is capable of parsing and processing errors.\ntype ErrorParser interface {\n\t// ParseError type asserts the given error and sets the forms's errors or a\n\t// field's errors and if the error is not something to be handled by the\n\t// formUI Container itself, the error is returned for further propagation (e.g. showing a 502 status code).\n\tParseError(group node.UiNodeGroup, err error) error\n}\n\ntype NodeSetter interface {\n\t// SetNode sets (adds / replaces) a node.\n\tSetNode(field node.Node)\n}\n\ntype NodeUnsetter interface {\n\t// UnsetFields removes a node.\n\tUnsetNode(name string)\n}\n\ntype ValueSetter interface {\n\t// SetValue sets a value the passed node.\n\tSetValue(name string, value *node.Node)\n}\n\ntype CSRFSetter interface {\n\t// SetCSRF sets the CSRF value.\n\tSetCSRF(string)\n}\n\ntype Resetter interface {\n\t// Resets all values and messages recursively.\n\tReset(exclude ...string)\n}\n\ntype MessageResetter interface {\n\t// ResetMessages resets the messages recursively.\n\tResetMessages(exclude ...string)\n}\n\ntype FieldSorter interface {\n\tSortNodes(schemaRef string, prefix string, keysInOrder []string) error\n}\n\ntype NodeGetter interface {\n\tGetNodes() *node.Nodes\n}\n"
  },
  {
    "path": "ui/node/.snapshots/TestNodeMarshalJSON-anchor_node.json",
    "content": "{\n  \"type\": \"a\",\n  \"group\": \"default\",\n  \"attributes\": {\n    \"href\": \"https://example.com\",\n    \"title\": null,\n    \"id\": \"\",\n    \"node_type\": \"a\"\n  },\n  \"messages\": [],\n  \"meta\": {}\n}\n"
  },
  {
    "path": "ui/node/.snapshots/TestNodeMarshalJSON-division_node.json",
    "content": "{\n  \"type\": \"div\",\n  \"group\": \"default\",\n  \"attributes\": {\n    \"id\": \"\",\n    \"node_type\": \"div\"\n  },\n  \"messages\": [],\n  \"meta\": {}\n}\n"
  },
  {
    "path": "ui/node/.snapshots/TestNodeMarshalJSON-empty_type_inferred_from_attributes.json",
    "content": "{\n  \"type\": \"input\",\n  \"group\": \"default\",\n  \"attributes\": {\n    \"name\": \"email\",\n    \"type\": \"\",\n    \"disabled\": false,\n    \"node_type\": \"input\"\n  },\n  \"messages\": [],\n  \"meta\": {}\n}\n"
  },
  {
    "path": "ui/node/.snapshots/TestNodeMarshalJSON-image_node.json",
    "content": "{\n  \"type\": \"img\",\n  \"group\": \"default\",\n  \"attributes\": {\n    \"src\": \"image.jpg\",\n    \"id\": \"\",\n    \"width\": 0,\n    \"height\": 0,\n    \"node_type\": \"img\"\n  },\n  \"messages\": [],\n  \"meta\": {}\n}\n"
  },
  {
    "path": "ui/node/.snapshots/TestNodeMarshalJSON-input_node.json",
    "content": "{\n  \"type\": \"input\",\n  \"group\": \"default\",\n  \"attributes\": {\n    \"name\": \"password\",\n    \"type\": \"password\",\n    \"value\": \"secret\",\n    \"disabled\": false,\n    \"node_type\": \"input\"\n  },\n  \"messages\": [],\n  \"meta\": {}\n}\n"
  },
  {
    "path": "ui/node/.snapshots/TestNodeMarshalJSON-script_node.json",
    "content": "{\n  \"type\": \"script\",\n  \"group\": \"default\",\n  \"attributes\": {\n    \"src\": \"script.js\",\n    \"async\": false,\n    \"referrerpolicy\": \"\",\n    \"crossorigin\": \"\",\n    \"integrity\": \"\",\n    \"type\": \"\",\n    \"id\": \"\",\n    \"nonce\": \"\",\n    \"node_type\": \"script\"\n  },\n  \"messages\": [],\n  \"meta\": {}\n}\n"
  },
  {
    "path": "ui/node/.snapshots/TestNodeMarshalJSON-text_node.json",
    "content": "{\n  \"type\": \"text\",\n  \"group\": \"default\",\n  \"attributes\": {\n    \"text\": null,\n    \"id\": \"\",\n    \"node_type\": \"text\"\n  },\n  \"messages\": [],\n  \"meta\": {}\n}\n"
  },
  {
    "path": "ui/node/attributes.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage node\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x/webauthnx/js\"\n)\n\nconst (\n\tInputAttributeTypeText          UiNodeInputAttributeType = \"text\"\n\tInputAttributeTypePassword      UiNodeInputAttributeType = \"password\"\n\tInputAttributeTypeNumber        UiNodeInputAttributeType = \"number\"\n\tInputAttributeTypeCheckbox      UiNodeInputAttributeType = \"checkbox\"\n\tInputAttributeTypeHidden        UiNodeInputAttributeType = \"hidden\"\n\tInputAttributeTypeEmail         UiNodeInputAttributeType = \"email\"\n\tInputAttributeTypeTel           UiNodeInputAttributeType = \"tel\"\n\tInputAttributeTypeSubmit        UiNodeInputAttributeType = \"submit\"\n\tInputAttributeTypeButton        UiNodeInputAttributeType = \"button\"\n\tInputAttributeTypeDateTimeLocal UiNodeInputAttributeType = \"datetime-local\"\n\tInputAttributeTypeDate          UiNodeInputAttributeType = \"date\"\n\tInputAttributeTypeURI           UiNodeInputAttributeType = \"url\"\n)\n\nconst (\n\tInputAttributeAutocompleteEmail            UiNodeInputAttributeAutocomplete = \"email\"\n\tInputAttributeAutocompleteTel              UiNodeInputAttributeAutocomplete = \"tel\"\n\tInputAttributeAutocompleteUrl              UiNodeInputAttributeAutocomplete = \"url\"\n\tInputAttributeAutocompleteCurrentPassword  UiNodeInputAttributeAutocomplete = \"current-password\"\n\tInputAttributeAutocompleteNewPassword      UiNodeInputAttributeAutocomplete = \"new-password\"\n\tInputAttributeAutocompleteOneTimeCode      UiNodeInputAttributeAutocomplete = \"one-time-code\"\n\tInputAttributeAutocompleteUsernameWebauthn UiNodeInputAttributeAutocomplete = \"username webauthn\"\n)\n\n// swagger:enum UiNodeInputAttributeType\ntype UiNodeInputAttributeType string\n\n// swagger:enum UiNodeInputAttributeAutocomplete\ntype UiNodeInputAttributeAutocomplete string\n\n// Attributes represents a list of attributes (e.g. `href=\"foo\"` for links).\n//\n// swagger:model uiNodeAttributes\ntype Attributes interface {\n\t// swagger:ignore\n\tID() string\n\n\t// swagger:ignore\n\tReset()\n\n\t// swagger:ignore\n\tSetValue(value interface{})\n\n\t// swagger:ignore\n\tGetValue() interface{}\n\n\t// swagger:ignore\n\tGetNodeType() UiNodeType\n\n\t// swagger:ignore\n\tMatches(other Attributes) bool\n}\n\n// InputAttributes represents the attributes of an input node\n//\n// swagger:model uiNodeInputAttributes\ntype InputAttributes struct {\n\t// The input's element name.\n\t//\n\t// required: true\n\tName string `json:\"name\"`\n\n\t// The input's element type.\n\t//\n\t// required: true\n\tType UiNodeInputAttributeType `json:\"type\" faker:\"-\"`\n\n\t// The input's value.\n\tFieldValue interface{} `json:\"value,omitempty\" faker:\"string\"`\n\n\t// Mark this input field as required.\n\tRequired bool `json:\"required,omitempty\"`\n\n\t// The autocomplete attribute for the input.\n\tAutocomplete UiNodeInputAttributeAutocomplete `json:\"autocomplete,omitempty\"`\n\n\t// The input's label text.\n\tLabel *text.Message `json:\"label,omitempty\"`\n\n\t// The input's pattern.\n\tPattern string `json:\"pattern,omitempty\"`\n\n\t// Sets the input's disabled field to true or false.\n\t//\n\t// required: true\n\tDisabled bool `json:\"disabled\"`\n\n\t// OnClick may contain javascript which should be executed on click. This is primarily\n\t// used for WebAuthn.\n\t//\n\t// Deprecated: Using OnClick requires the use of eval() which is a security risk. Use OnClickTrigger instead.\n\tOnClick string `json:\"onclick,omitempty\"`\n\n\t// OnClickTrigger may contain a WebAuthn trigger which should be executed on click.\n\t//\n\t// The trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login.\n\tOnClickTrigger js.WebAuthnTriggers `json:\"onclickTrigger,omitempty\"`\n\n\t// OnLoad may contain javascript which should be executed on load. This is primarily\n\t// used for WebAuthn.\n\t//\n\t// Deprecated: Using OnLoad requires the use of eval() which is a security risk. Use OnLoadTrigger instead.\n\tOnLoad string `json:\"onload,omitempty\"`\n\n\t// OnLoadTrigger may contain a WebAuthn trigger which should be executed on load.\n\t//\n\t// The trigger maps to a JavaScript function provided by Ory, which triggers actions such as PassKey registration or login.\n\tOnLoadTrigger js.WebAuthnTriggers `json:\"onloadTrigger,omitempty\"`\n\n\t// MaxLength may contain the input's maximum length.\n\tMaxLength int `json:\"maxlength,omitempty\"`\n\n\t// NodeType represents this node's types. It is a mirror of `node.type` and\n\t// is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \"input\".\n\t//\n\t// required: true\n\tNodeType UiNodeType `json:\"node_type\"`\n}\n\n// ImageAttributes represents the attributes of an image node.\n//\n// swagger:model uiNodeImageAttributes\ntype ImageAttributes struct {\n\t// The image's source URL.\n\t//\n\t// format: uri\n\t// required: true\n\tSource string `json:\"src\"`\n\n\t// A unique identifier\n\t//\n\t// required: true\n\tIdentifier string `json:\"id\"`\n\n\t// Width of the image\n\t//\n\t// required: true\n\tWidth int `json:\"width\"`\n\n\t// Height of the image\n\t//\n\t// required: true\n\tHeight int `json:\"height\"`\n\n\t// NodeType represents this node's types. It is a mirror of `node.type` and\n\t// is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \"img\".\n\t//\n\t// required: true\n\tNodeType UiNodeType `json:\"node_type\"`\n}\n\n// AnchorAttributes represents the attributes of an anchor node.\n//\n// swagger:model uiNodeAnchorAttributes\ntype AnchorAttributes struct {\n\t// The link's href (destination) URL.\n\t//\n\t// format: uri\n\t// required: true\n\tHREF string `json:\"href\"`\n\n\t// The link's title.\n\t//\n\t// required: true\n\tTitle *text.Message `json:\"title\"`\n\n\t// A unique identifier\n\t//\n\t// required: true\n\tIdentifier string `json:\"id\"`\n\n\t// NodeType represents this node's types. It is a mirror of `node.type` and\n\t// is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \"a\".\n\t//\n\t// required: true\n\tNodeType UiNodeType `json:\"node_type\"`\n}\n\n// TextAttributes represents the attributes of a text node.\n//\n// swagger:model uiNodeTextAttributes\ntype TextAttributes struct {\n\t// The text of the text node.\n\t//\n\t// required: true\n\tText *text.Message `json:\"text\"`\n\n\t// A unique identifier\n\t//\n\t// required: true\n\tIdentifier string `json:\"id\"`\n\n\t// NodeType represents this node's types. It is a mirror of `node.type` and\n\t// is primarily used to allow compatibility with OpenAPI 3.0.  In this struct it technically always is \"text\".\n\t//\n\t// required: true\n\tNodeType UiNodeType `json:\"node_type\"`\n}\n\n// ScriptAttributes represent script nodes which load javascript.\n//\n// swagger:model uiNodeScriptAttributes\ntype ScriptAttributes struct {\n\t// The script source\n\t//\n\t// required: true\n\tSource string `json:\"src\"`\n\n\t// The script async type\n\t//\n\t// required: true\n\tAsync bool `json:\"async\"`\n\n\t// The script referrer policy\n\t//\n\t// required: true\n\tReferrerPolicy string `json:\"referrerpolicy\"`\n\n\t// The script cross origin policy\n\t//\n\t// required: true\n\tCrossOrigin string `json:\"crossorigin\"`\n\n\t// The script's integrity hash\n\t//\n\t// required: true\n\tIntegrity string `json:\"integrity\"`\n\n\t// The script MIME type\n\t//\n\t// required: true\n\tType string `json:\"type\"`\n\n\t// A unique identifier\n\t//\n\t// required: true\n\tIdentifier string `json:\"id\"`\n\n\t// Nonce for CSP\n\t//\n\t// A nonce you may want to use to improve your Content Security Policy.\n\t// You do not have to use this value but if you want to improve your CSP\n\t// policies you may use it. You can also choose to use your own nonce value!\n\t//\n\t// required: true\n\tNonce string `json:\"nonce\"`\n\n\t// NodeType represents this node's types. It is a mirror of `node.type` and\n\t// is primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \"script\".\n\t//\n\t// required: true\n\tNodeType UiNodeType `json:\"node_type\"`\n}\n\n// DivisionAttributes represent a division section.\n//\n// Division sections are used for interactive widgets that require a hook in the DOM / view.\n//\n// swagger:model uiNodeDivisionAttributes\ntype DivisionAttributes struct {\n\t// A classname that should be rendered into the DOM.\n\tClassname string `json:\"class,omitzero\"`\n\n\t// A unique identifier\n\t//\n\t// required: true\n\tIdentifier string `json:\"id\"`\n\n\t// Data is a map of key-value pairs that are passed to the division.\n\t//\n\t// They may be used for `data-...` attributes.\n\tData map[string]string `json:\"data,omitzero\"`\n\n\t// NodeType represents this node's type. It is a mirror of `node.type` and\n\t// is primarily used to allow compatibility with OpenAPI 3.0. In this struct it technically always is \"script\".\n\t//\n\t// required: true\n\tNodeType UiNodeType `json:\"node_type\"`\n}\n\nfunc (d DivisionAttributes) ID() string {\n\treturn d.Identifier\n}\n\nfunc (d DivisionAttributes) Reset() {}\n\nfunc (d DivisionAttributes) SetValue(_ interface{}) {}\n\nfunc (d DivisionAttributes) GetValue() interface{} {\n\treturn nil\n}\n\nfunc (d DivisionAttributes) GetNodeType() UiNodeType {\n\treturn d.NodeType\n}\n\nfunc (d DivisionAttributes) Matches(other Attributes) bool {\n\tot, ok := other.(*DivisionAttributes)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif len(ot.ID()) > 0 && d.ID() != ot.ID() {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nvar (\n\t_ Attributes = new(InputAttributes)\n\t_ Attributes = new(ImageAttributes)\n\t_ Attributes = new(AnchorAttributes)\n\t_ Attributes = new(TextAttributes)\n\t_ Attributes = new(ScriptAttributes)\n\t_ Attributes = new(DivisionAttributes)\n)\n\nfunc (a *InputAttributes) ID() string {\n\treturn a.Name\n}\n\nfunc (a *ImageAttributes) ID() string {\n\treturn a.Identifier\n}\n\nfunc (a *AnchorAttributes) ID() string {\n\treturn a.Identifier\n}\n\nfunc (a *TextAttributes) ID() string {\n\treturn a.Identifier\n}\n\nfunc (a *ScriptAttributes) ID() string {\n\treturn a.Identifier\n}\n\nfunc (a *InputAttributes) Matches(other Attributes) bool {\n\tot, ok := other.(*InputAttributes)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif len(ot.ID()) > 0 && a.ID() != ot.ID() {\n\t\treturn false\n\t}\n\n\tif len(ot.Type) > 0 && a.Type != ot.Type {\n\t\treturn false\n\t}\n\n\tif ot.FieldValue != nil && fmt.Sprintf(\"%v\", a.FieldValue) != fmt.Sprintf(\"%v\", ot.FieldValue) {\n\t\treturn false\n\t}\n\n\tif len(ot.Name) > 0 && a.Name != ot.Name {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (a *ImageAttributes) Matches(other Attributes) bool {\n\tot, ok := other.(*ImageAttributes)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif len(ot.ID()) > 0 && a.ID() != ot.ID() {\n\t\treturn false\n\t}\n\n\tif len(ot.Source) > 0 && a.Source != ot.Source {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (a *AnchorAttributes) Matches(other Attributes) bool {\n\tot, ok := other.(*AnchorAttributes)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif len(ot.ID()) > 0 && a.ID() != ot.ID() {\n\t\treturn false\n\t}\n\n\tif len(ot.HREF) > 0 && a.HREF != ot.HREF {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (a *TextAttributes) Matches(other Attributes) bool {\n\tot, ok := other.(*TextAttributes)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif len(ot.ID()) > 0 && a.ID() != ot.ID() {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (a *ScriptAttributes) Matches(other Attributes) bool {\n\tot, ok := other.(*ScriptAttributes)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif len(ot.ID()) > 0 && a.ID() != ot.ID() {\n\t\treturn false\n\t}\n\n\tif ot.Type != \"\" && a.Type != ot.Type {\n\t\treturn false\n\t}\n\n\tif ot.Source != \"\" && a.Source != ot.Source {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (a *InputAttributes) SetValue(value interface{}) {\n\ta.FieldValue = value\n}\n\nfunc (a *ImageAttributes) SetValue(value interface{}) {\n\ta.Source, _ = value.(string)\n}\n\nfunc (a *AnchorAttributes) SetValue(value interface{}) {\n\ta.HREF, _ = value.(string)\n}\n\nfunc (a *TextAttributes) SetValue(value interface{}) {\n\ta.Text, _ = value.(*text.Message)\n}\n\nfunc (a *ScriptAttributes) SetValue(value interface{}) {\n\ta.Source, _ = value.(string)\n}\n\nfunc (a *InputAttributes) GetValue() interface{} {\n\treturn a.FieldValue\n}\n\nfunc (a *ImageAttributes) GetValue() interface{} {\n\treturn a.Source\n}\n\nfunc (a *AnchorAttributes) GetValue() interface{} {\n\treturn a.HREF\n}\n\nfunc (a *TextAttributes) GetValue() interface{} {\n\treturn a.Text\n}\n\nfunc (a *ScriptAttributes) GetValue() interface{} {\n\treturn a.Source\n}\n\nfunc (a *InputAttributes) Reset() {\n\ta.FieldValue = nil\n}\n\nfunc (a *ImageAttributes) Reset() {\n}\n\nfunc (a *AnchorAttributes) Reset() {\n}\n\nfunc (a *TextAttributes) Reset() {\n}\n\nfunc (a *ScriptAttributes) Reset() {\n}\n\nfunc (a *InputAttributes) GetNodeType() UiNodeType {\n\treturn UiNodeType(a.NodeType)\n}\n\nfunc (a *ImageAttributes) GetNodeType() UiNodeType {\n\treturn UiNodeType(a.NodeType)\n}\n\nfunc (a *AnchorAttributes) GetNodeType() UiNodeType {\n\treturn UiNodeType(a.NodeType)\n}\n\nfunc (a *TextAttributes) GetNodeType() UiNodeType {\n\treturn UiNodeType(a.NodeType)\n}\n\nfunc (a *ScriptAttributes) GetNodeType() UiNodeType {\n\treturn UiNodeType(a.NodeType)\n}\n"
  },
  {
    "path": "ui/node/attributes_input.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage node\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/nosurfx\"\n\t\"github.com/ory/x/jsonschemax\"\n)\n\nconst DisableFormField = \"disableFormField\"\n\nfunc toFormType(n string, i interface{}) UiNodeInputAttributeType {\n\tswitch n {\n\tcase nosurfx.CSRFTokenName:\n\t\treturn InputAttributeTypeHidden\n\tcase \"password\":\n\t\treturn InputAttributeTypePassword\n\t}\n\n\tswitch i.(type) {\n\tcase float64, int64, int32, float32, json.Number:\n\t\treturn InputAttributeTypeNumber\n\tcase bool:\n\t\treturn InputAttributeTypeCheckbox\n\t}\n\n\treturn InputAttributeTypeText\n}\n\ntype (\n\tInputAttributesModifier  func(attributes *InputAttributes)\n\tInputAttributesModifiers []InputAttributesModifier\n)\n\nfunc WithRequiredInputAttribute(a *InputAttributes) {\n\ta.Required = true\n}\n\nfunc WithMaxLengthInputAttribute(maxLength int) func(a *InputAttributes) {\n\treturn func(a *InputAttributes) {\n\t\ta.MaxLength = maxLength\n\t}\n}\n\nfunc WithInputAttributes(f func(a *InputAttributes)) func(a *InputAttributes) {\n\treturn func(a *InputAttributes) {\n\t\tf(a)\n\t}\n}\n\nfunc applyInputAttributes(opts []InputAttributesModifier, attributes *InputAttributes) *InputAttributes {\n\tfor _, f := range opts {\n\t\tf(attributes)\n\t}\n\treturn attributes\n}\n\ntype (\n\tImageAttributesModifier  func(attributes *ImageAttributes)\n\tImageAttributesModifiers []ImageAttributesModifier\n)\n\nfunc WithImageAttributes(f func(a *ImageAttributes)) func(a *ImageAttributes) {\n\treturn func(a *ImageAttributes) {\n\t\tf(a)\n\t}\n}\n\nfunc applyImageAttributes(opts ImageAttributesModifiers, attributes *ImageAttributes) *ImageAttributes {\n\tfor _, f := range opts {\n\t\tf(attributes)\n\t}\n\treturn attributes\n}\n\ntype (\n\tScriptAttributesModifier  func(attributes *ScriptAttributes)\n\tScriptAttributesModifiers []ScriptAttributesModifier\n)\n\nfunc applyScriptAttributes(opts ScriptAttributesModifiers, attributes *ScriptAttributes) *ScriptAttributes {\n\tfor _, f := range opts {\n\t\tf(attributes)\n\t}\n\treturn attributes\n}\n\ntype (\n\tDivisionAttributesModifier  func(attributes *DivisionAttributes)\n\tDivisionAttributesModifiers []DivisionAttributesModifier\n)\n\nfunc WithDivisionAttributes(f func(a *DivisionAttributes)) func(a *DivisionAttributes) {\n\treturn func(a *DivisionAttributes) {\n\t\tf(a)\n\t}\n}\n\nfunc applyDivisionAttributes(opts DivisionAttributesModifiers, attributes *DivisionAttributes) *DivisionAttributes {\n\tfor _, f := range opts {\n\t\tf(attributes)\n\t}\n\treturn attributes\n}\n\nfunc NewInputFieldFromJSON(name string, value interface{}, group UiNodeGroup, opts ...InputAttributesModifier) *Node {\n\treturn &Node{\n\t\tType:       Input,\n\t\tGroup:      group,\n\t\tAttributes: applyInputAttributes(opts, &InputAttributes{Name: name, Type: toFormType(name, value), FieldValue: value}),\n\t\tMeta:       &Meta{},\n\t}\n}\n\nfunc NewInputField(name string, value interface{}, group UiNodeGroup, inputType UiNodeInputAttributeType, opts ...InputAttributesModifier) *Node {\n\treturn &Node{\n\t\tType:       Input,\n\t\tGroup:      group,\n\t\tAttributes: applyInputAttributes(opts, &InputAttributes{Name: name, Type: inputType, FieldValue: value}),\n\t\tMeta:       &Meta{},\n\t}\n}\n\nfunc NewImageField(id string, src string, group UiNodeGroup, opts ...ImageAttributesModifier) *Node {\n\treturn &Node{\n\t\tType:       Image,\n\t\tGroup:      group,\n\t\tAttributes: applyImageAttributes(opts, &ImageAttributes{Source: src, Identifier: id}),\n\t\tMeta:       &Meta{},\n\t}\n}\n\nfunc NewTextField(id string, text *text.Message, group UiNodeGroup) *Node {\n\treturn &Node{\n\t\tType:       Text,\n\t\tGroup:      group,\n\t\tAttributes: &TextAttributes{Text: text, Identifier: id},\n\t\tMeta:       &Meta{},\n\t}\n}\n\nfunc NewDivisionField(id string, group UiNodeGroup, opts ...DivisionAttributesModifier) *Node {\n\treturn &Node{\n\t\tType:       Division,\n\t\tGroup:      group,\n\t\tAttributes: applyDivisionAttributes(opts, &DivisionAttributes{Identifier: id}),\n\t\tMeta:       &Meta{},\n\t}\n}\n\nfunc NewAnchorField(id string, href string, group UiNodeGroup, title *text.Message) *Node {\n\treturn &Node{\n\t\tType:       Anchor,\n\t\tGroup:      group,\n\t\tAttributes: &AnchorAttributes{Title: title, HREF: href, Identifier: id},\n\t\tMeta:       &Meta{},\n\t}\n}\n\nfunc NewScriptField(name string, src string, group UiNodeGroup, integrity string, opts ...ScriptAttributesModifier) *Node {\n\treturn &Node{\n\t\tType:  Script,\n\t\tGroup: group,\n\t\tAttributes: applyScriptAttributes(opts, &ScriptAttributes{\n\t\t\tIdentifier:     name,\n\t\t\tType:           \"text/javascript\",\n\t\t\tSource:         src,\n\t\t\tAsync:          true,\n\t\t\tReferrerPolicy: \"no-referrer\",\n\t\t\tCrossOrigin:    \"anonymous\",\n\t\t\tIntegrity:      integrity,\n\t\t\tNonce:          x.NewUUID().String(),\n\t\t}),\n\t\tMeta: &Meta{},\n\t}\n}\n\nfunc NewInputFieldFromSchema(name string, group UiNodeGroup, p jsonschemax.Path, opts ...InputAttributesModifier) *Node {\n\tattr := &InputAttributes{\n\t\tName:     name,\n\t\tType:     toFormType(p.Name, p.Type),\n\t\tRequired: p.Required,\n\t}\n\n\t// If format is set, we can make a more distinct decision:\n\tswitch p.Format {\n\tcase \"date-time\":\n\t\tattr.Type = InputAttributeTypeDateTimeLocal\n\tcase \"email\":\n\t\tattr.Type = InputAttributeTypeEmail\n\t\tattr.Autocomplete = InputAttributeAutocompleteEmail\n\tcase \"tel\":\n\t\tattr.Type = InputAttributeTypeTel\n\t\tattr.Autocomplete = InputAttributeAutocompleteTel\n\tcase \"date\":\n\t\tattr.Type = InputAttributeTypeDate\n\tcase \"uri\":\n\t\tattr.Type = InputAttributeTypeURI\n\t\tattr.Autocomplete = InputAttributeAutocompleteUrl\n\tcase \"regex\":\n\t\tattr.Type = InputAttributeTypeText\n\t}\n\n\t// Other properties\n\tif p.Pattern != nil {\n\t\tattr.Pattern = p.Pattern.String()\n\t}\n\n\t// Set disabled if the custom property is set\n\tif isDisabled, ok := p.CustomProperties[DisableFormField]; ok {\n\t\tif isDisabled, ok := isDisabled.(bool); ok {\n\t\t\tattr.Disabled = isDisabled\n\t\t}\n\t}\n\n\tvar meta Meta\n\tif len(p.Title) > 0 {\n\t\tmeta.Label = text.NewInfoNodeLabelGenerated(p.Title, name)\n\t}\n\n\treturn &Node{\n\t\tType:       Input,\n\t\tAttributes: applyInputAttributes(opts, attr),\n\t\tGroup:      group,\n\t\tMeta:       &meta,\n\t}\n}\n"
  },
  {
    "path": "ui/node/attributes_input_csrf.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage node\n\nimport \"github.com/ory/kratos/x/nosurfx\"\n\nfunc NewCSRFNode(token string) *Node {\n\treturn &Node{\n\t\tType:  Input,\n\t\tGroup: DefaultGroup,\n\t\tAttributes: &InputAttributes{\n\t\t\tName:       nosurfx.CSRFTokenName,\n\t\t\tType:       InputAttributeTypeHidden,\n\t\t\tFieldValue: token,\n\t\t\tRequired:   true,\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "ui/node/attributes_input_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage node\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/x/jsonschemax\"\n)\n\nfunc TestFieldFromPath(t *testing.T) {\n\n\tvar ctx = context.Background()\n\tt.Run(\"all properties are properly transferred\", func(t *testing.T) {\n\t\tschema, err := os.ReadFile(\"./fixtures/all_formats.schema.json\")\n\t\trequire.NoError(t, err)\n\n\t\tc := jsonschema.NewCompiler()\n\t\trequire.NoError(t, c.AddResource(\"test.json\", bytes.NewBuffer(schema)))\n\n\t\tpaths, err := jsonschemax.ListPaths(ctx, \"test.json\", c)\n\t\trequire.NoError(t, err)\n\n\t\tfor _, path := range paths {\n\t\t\tnode := NewInputFieldFromSchema(path.Name, DefaultGroup, path)\n\t\t\tassert.EqualValues(t, \"input\", node.Type)\n\t\t\trequire.IsType(t, new(InputAttributes), node.Attributes)\n\t\t\tattr := node.Attributes.(*InputAttributes)\n\n\t\t\tassert.EqualValues(t, gjson.GetBytes(schema, fmt.Sprintf(\"properties.%s.test_expected_type\", path.Name)).String(), attr.Type)\n\t\t\tassert.True(t, !gjson.GetBytes(schema, fmt.Sprintf(\"properties.%s.test_expected_pattern\", path.Name)).Exists() ||\n\t\t\t\t(gjson.GetBytes(schema, fmt.Sprintf(\"properties.%s.test_expected_pattern\", path.Name)).Bool() && attr.Pattern != \"\"))\n\n\t\t\texpectedAutocomplete := gjson.GetBytes(schema, fmt.Sprintf(\"properties.%s.test_expected_autocomplete\", path.Name))\n\n\t\t\tif expectedAutocomplete.Exists() {\n\t\t\t\tassert.EqualValues(t, expectedAutocomplete.String(), attr.Autocomplete)\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "ui/node/attributes_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage node\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/jsonx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestIDs(t *testing.T) {\n\tassert.EqualValues(t, \"foo\", (&AnchorAttributes{Identifier: \"foo\"}).ID())\n\tassert.EqualValues(t, \"foo\", (&ImageAttributes{Identifier: \"foo\"}).ID())\n\tassert.EqualValues(t, \"foo\", (&TextAttributes{Identifier: \"foo\"}).ID())\n\tassert.EqualValues(t, \"foo\", (&InputAttributes{Name: \"foo\"}).ID())\n\tassert.EqualValues(t, \"foo\", (&ScriptAttributes{Identifier: \"foo\"}).ID())\n}\n\nfunc TestMatchesAnchorAttributes(t *testing.T) {\n\tassert.True(t, (&AnchorAttributes{Identifier: \"foo\"}).Matches(&AnchorAttributes{Identifier: \"foo\"}))\n\tassert.True(t, (&AnchorAttributes{HREF: \"bar\"}).Matches(&AnchorAttributes{HREF: \"bar\"}))\n\tassert.False(t, (&AnchorAttributes{HREF: \"foo\"}).Matches(&AnchorAttributes{HREF: \"bar\"}))\n\tassert.False(t, (&AnchorAttributes{Identifier: \"foo\"}).Matches(&AnchorAttributes{HREF: \"bar\"}))\n\n\tassert.True(t, (&AnchorAttributes{Identifier: \"foo\", HREF: \"bar\"}).Matches(&AnchorAttributes{Identifier: \"foo\", HREF: \"bar\"}))\n\tassert.False(t, (&AnchorAttributes{Identifier: \"foo\", HREF: \"bar\"}).Matches(&AnchorAttributes{Identifier: \"foo\", HREF: \"baz\"}))\n\tassert.False(t, (&AnchorAttributes{Identifier: \"foo\", HREF: \"bar\"}).Matches(&AnchorAttributes{Identifier: \"bar\", HREF: \"bar\"}))\n\n\tassert.False(t, (&AnchorAttributes{Identifier: \"foo\"}).Matches(&TextAttributes{Identifier: \"foo\"}))\n}\n\nfunc TestMatchesImageAttributes(t *testing.T) {\n\tassert.True(t, (&ImageAttributes{Identifier: \"foo\"}).Matches(&ImageAttributes{Identifier: \"foo\"}))\n\tassert.True(t, (&ImageAttributes{Source: \"bar\"}).Matches(&ImageAttributes{Source: \"bar\"}))\n\tassert.False(t, (&ImageAttributes{Source: \"foo\"}).Matches(&ImageAttributes{Source: \"bar\"}))\n\tassert.False(t, (&ImageAttributes{Identifier: \"foo\"}).Matches(&ImageAttributes{Source: \"bar\"}))\n\n\tassert.True(t, (&ImageAttributes{Identifier: \"foo\", Source: \"bar\"}).Matches(&ImageAttributes{Identifier: \"foo\", Source: \"bar\"}))\n\tassert.False(t, (&ImageAttributes{Identifier: \"foo\", Source: \"bar\"}).Matches(&ImageAttributes{Identifier: \"foo\", Source: \"baz\"}))\n\tassert.False(t, (&ImageAttributes{Identifier: \"foo\", Source: \"bar\"}).Matches(&ImageAttributes{Identifier: \"bar\", Source: \"bar\"}))\n\n\tassert.False(t, (&ImageAttributes{Identifier: \"foo\"}).Matches(&TextAttributes{Identifier: \"foo\"}))\n}\n\nfunc TestMatchesInputAttributes(t *testing.T) {\n\t// Test when other is not of type *InputAttributes\n\tvar attr Attributes = &ImageAttributes{}\n\tinputAttr := &InputAttributes{Name: \"foo\"}\n\tassert.False(t, inputAttr.Matches(attr))\n\n\t// Test when ID is different\n\tattr = &InputAttributes{Name: \"foo\", Type: InputAttributeTypeText}\n\tinputAttr = &InputAttributes{Name: \"bar\", Type: InputAttributeTypeText}\n\tassert.False(t, inputAttr.Matches(attr))\n\n\t// Test when Type is different\n\tattr = &InputAttributes{Name: \"foo\", Type: InputAttributeTypeText}\n\tinputAttr = &InputAttributes{Name: \"foo\", Type: InputAttributeTypeNumber}\n\tassert.False(t, inputAttr.Matches(attr))\n\n\t// Test when FieldValue is different\n\tattr = &InputAttributes{Name: \"foo\", Type: InputAttributeTypeText, FieldValue: \"bar\"}\n\tinputAttr = &InputAttributes{Name: \"foo\", Type: InputAttributeTypeText, FieldValue: \"baz\"}\n\tassert.False(t, inputAttr.Matches(attr))\n\n\t// Test when Name is different\n\tattr = &InputAttributes{Name: \"foo\", Type: InputAttributeTypeText}\n\tinputAttr = &InputAttributes{Name: \"bar\", Type: InputAttributeTypeText}\n\tassert.False(t, inputAttr.Matches(attr))\n\n\t// Test when all fields are the same\n\tattr = &InputAttributes{Name: \"foo\", Type: InputAttributeTypeText, FieldValue: \"bar\"}\n\tinputAttr = &InputAttributes{Name: \"foo\", Type: InputAttributeTypeText, FieldValue: \"bar\"}\n\tassert.True(t, inputAttr.Matches(attr))\n}\n\nfunc TestMatchesTextAttributes(t *testing.T) {\n\tassert.True(t, (&TextAttributes{Identifier: \"foo\"}).Matches(&TextAttributes{Identifier: \"foo\"}))\n\tassert.True(t, (&TextAttributes{Identifier: \"foo\"}).Matches(&TextAttributes{Identifier: \"foo\"}))\n\tassert.False(t, (&TextAttributes{Identifier: \"foo\"}).Matches(&ImageAttributes{Identifier: \"foo\"}))\n}\n\nfunc TestNodeEncode(t *testing.T) {\n\tscript := jsonx.TestMarshalJSONString(t, &Node{Attributes: &ScriptAttributes{}})\n\tassert.EqualValues(t, Script, gjson.Get(script, \"attributes.node_type\").String())\n\tassert.EqualValues(t, Script, gjson.Get(script, \"type\").String())\n\n\ttext := jsonx.TestMarshalJSONString(t, &Node{Attributes: &TextAttributes{}})\n\tassert.EqualValues(t, Text, gjson.Get(text, \"attributes.node_type\").String())\n\tassert.EqualValues(t, Text, gjson.Get(text, \"type\").String())\n\n\timage := jsonx.TestMarshalJSONString(t, &Node{Attributes: &ImageAttributes{}})\n\tassert.EqualValues(t, Image, gjson.Get(image, \"attributes.node_type\").String())\n\tassert.EqualValues(t, Image, gjson.Get(image, \"type\").String())\n\n\tinput := jsonx.TestMarshalJSONString(t, &Node{Attributes: &InputAttributes{}})\n\tassert.EqualValues(t, Input, gjson.Get(input, \"attributes.node_type\").String())\n\tassert.EqualValues(t, Input, gjson.Get(input, \"type\").String())\n\n\tanchor := jsonx.TestMarshalJSONString(t, &Node{Attributes: &AnchorAttributes{}})\n\tassert.EqualValues(t, Anchor, gjson.Get(anchor, \"attributes.node_type\").String())\n\tassert.EqualValues(t, Anchor, gjson.Get(anchor, \"type\").String())\n}\n\nfunc TestNodeDecode(t *testing.T) {\n\tfor _, kind := range []UiNodeType{\n\t\tText,\n\t\tInput,\n\t\tImage,\n\t\tAnchor,\n\t\tScript,\n\t} {\n\t\tvar n Node\n\t\trequire.NoError(t, json.Unmarshal([]byte(`{\"type\":\"`+kind+`\"}`), &n))\n\t\tassert.EqualValues(t, kind, n.Attributes.GetNodeType())\n\t}\n}\n"
  },
  {
    "path": "ui/node/fixtures/all_formats.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/all_formats.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"password\": {\n      \"type\": \"string\",\n      \"test_expected_type\": \"password\"\n    },\n    \"csrf_token\": {\n      \"type\": \"string\",\n      \"test_expected_type\": \"hidden\"\n    },\n    \"simpleString\": {\n      \"type\": \"string\",\n      \"test_expected_type\": \"text\"\n    },\n    \"simpleNumber\": {\n      \"type\": \"number\",\n      \"test_expected_type\": \"number\"\n    },\n    \"simpleBoolean\": {\n      \"type\": \"boolean\",\n      \"test_expected_type\": \"checkbox\"\n    },\n    \"emailString\": {\n      \"type\": \"string\",\n      \"format\": \"email\",\n      \"test_expected_type\": \"email\",\n      \"test_expected_autocomplete\": \"email\"\n    },\n    \"phoneString\": {\n      \"type\": \"string\",\n      \"format\": \"tel\",\n      \"test_expected_type\": \"tel\",\n      \"test_expected_autocomplete\": \"tel\"\n    },\n    \"dateTimeString\": {\n      \"type\": \"string\",\n      \"format\": \"date-time\",\n      \"test_expected_type\": \"datetime-local\"\n    },\n    \"regexString\": {\n      \"type\": \"string\",\n      \"format\": \"regex\",\n      \"test_expected_type\": \"text\"\n    },\n    \"dateString\": {\n      \"type\": \"string\",\n      \"format\": \"date\",\n      \"test_expected_type\": \"date\"\n    },\n    \"uriString\": {\n      \"type\": \"string\",\n      \"format\": \"uri\",\n      \"test_expected_type\": \"url\",\n      \"test_expected_autocomplete\": \"url\"\n    },\n    \"patternString\": {\n      \"type\": \"string\",\n      \"pattern\": \"foo.*\",\n      \"test_expected_type\": \"text\",\n      \"test_expected_pattern\": true\n    }\n  }\n}\n"
  },
  {
    "path": "ui/node/fixtures/complex.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/complex.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\"meal\"],\n  \"properties\": {\n    \"meal\": {\n      \"type\": \"object\",\n      \"required\": [\"name\"],\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\"\n        },\n        \"chef\": {\n          \"type\": \"string\"\n        }\n      }\n    },\n    \"fruits\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"vegetables\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"$ref\": \"#/definitions/veggie\"\n      }\n    }\n  },\n  \"definitions\": {\n    \"veggie\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"veggieName\",\n        \"veggieLike\"\n      ],\n      \"properties\": {\n        \"veggieName\": {\n          \"type\": \"string\"\n        },\n        \"veggieLike\": {\n          \"type\": \"boolean\"\n        },\n        \"veggieAmount\": {\n          \"type\": \"number\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ui/node/fixtures/identity.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            },\n            \"verification\": {\n              \"via\": \"email\"\n            }\n          }\n        },\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"booly\": {\n          \"type\": \"boolean\"\n        },\n        \"should_big_number\": {\n          \"type\": \"number\",\n          \"minimum\": 1200\n        },\n        \"should_long_string\": {\n          \"type\": \"string\",\n          \"minLength\": 25\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ui/node/fixtures/simple.schema.json",
    "content": "{\n  \"$id\": \"https://example.com/registration.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"stringy\": {\n      \"type\": \"string\"\n    },\n    \"numby\": {\n      \"type\": \"number\"\n    },\n    \"objy\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"stringy\": {\n          \"type\": \"string\"\n        },\n        \"numby\": {\n          \"type\": \"number\"\n        },\n        \"objy\": {\n          \"type\": \"object\",\n          \"properties\": {}\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ui/node/fixtures/sort/expected/1.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"value\": \"Sk3WySQDVpL5V9IuQelZTXcRVbBE1STI0HAJEcHOnM0/xPz5p1piBzuBMKZWxgYJc5TN8k8BTM9Hazrf/n76pg==\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"hydra\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"oidc\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password_identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "ui/node/fixtures/sort/expected/2.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"value\": \"qomtJu6UOzh5Gqkq3MEwbbJuNgJEfDg9gEowfMH0OhjfAIcWbc0PrbvMS6LL7m8ptuuuQE+oUDoXUQOy/kRccw==\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"hydra\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"oidc\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"traits.abcd\",\n      \"type\": \"input\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "ui/node/fixtures/sort/expected/3.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"value\": \"qomtJu6UOzh5Gqkq3MEwbbJuNgJEfDg9gEowfMH0OhjfAIcWbc0PrbvMS6LL7m8ptuuuQE+oUDoXUQOy/kRccw==\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"hydra\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"oidc\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"traits.abcd\",\n      \"type\": \"input\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  }\n]\n"
  },
  {
    "path": "ui/node/fixtures/sort/expected/4.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"value\": \"john@doe.com\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"traits.name\",\n      \"type\": \"text\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {}\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"profile\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"github\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050003,\n        \"text\": \"Unlink google\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"google\"\n        }\n      }\n    }\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"ory\",\n      \"disabled\": false,\n      \"node_type\": \"input\"\n    },\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1050002,\n        \"text\": \"Link ory\",\n        \"type\": \"info\",\n        \"context\": {\n          \"provider\": \"ory\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "ui/node/fixtures/sort/input/1.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"oidc\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password_identifier\",\n      \"type\": \"text\",\n      \"value\": \"\",\n      \"required\": true,\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"value\": \"Sk3WySQDVpL5V9IuQelZTXcRVbBE1STI0HAJEcHOnM0/xPz5p1piBzuBMKZWxgYJc5TN8k8BTM9Hazrf/n76pg==\",\n      \"required\": true,\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"hydra\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"disabled\": false\n    },\n    \"messages\": null\n  }\n]\n"
  },
  {
    "path": "ui/node/fixtures/sort/input/2.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"password\",\n      \"type\": \"password\",\n      \"required\": true,\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"oidc\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"value\": \"qomtJu6UOzh5Gqkq3MEwbbJuNgJEfDg9gEowfMH0OhjfAIcWbc0PrbvMS6LL7m8ptuuuQE+oUDoXUQOy/kRccw==\",\n      \"required\": true,\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"hydra\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"traits.abcd\",\n      \"type\": \"input\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  }\n]\n"
  },
  {
    "path": "ui/node/fixtures/sort/input/3.json",
    "content": "[\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"oidc\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"default\",\n    \"attributes\": {\n      \"name\": \"csrf_token\",\n      \"type\": \"hidden\",\n      \"value\": \"qomtJu6UOzh5Gqkq3MEwbbJuNgJEfDg9gEowfMH0OhjfAIcWbc0PrbvMS6LL7m8ptuuuQE+oUDoXUQOy/kRccw==\",\n      \"required\": true,\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"github\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"google\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"oidc\",\n    \"attributes\": {\n      \"name\": \"oidc.provider\",\n      \"type\": \"submit\",\n      \"value\": \"hydra\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  },\n  {\n    \"type\": \"input\",\n    \"group\": \"password\",\n    \"attributes\": {\n      \"name\": \"traits.abcd\",\n      \"type\": \"input\",\n      \"disabled\": false\n    },\n    \"messages\": null\n  }\n]\n"
  },
  {
    "path": "ui/node/fixtures/sort/input/4.json",
    "content": "[\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"csrf_token\",\n      \"required\": true,\n      \"type\": \"hidden\",\n      \"value\": \"\"\n    },\n    \"group\": \"default\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.email\",\n      \"type\": \"email\",\n      \"value\": \"john@doe.com\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"traits.name\",\n      \"type\": \"text\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {},\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"profile\"\n    },\n    \"group\": \"profile\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"password\",\n      \"required\": true,\n      \"type\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070001,\n        \"text\": \"Password\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"method\",\n      \"type\": \"submit\",\n      \"value\": \"password\"\n    },\n    \"group\": \"password\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"id\": 1070003,\n        \"text\": \"Save\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"github\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"github\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link github\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"unlink\",\n      \"type\": \"submit\",\n      \"value\": \"google\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"google\"\n        },\n        \"id\": 1050003,\n        \"text\": \"Unlink google\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  },\n  {\n    \"attributes\": {\n      \"disabled\": false,\n      \"name\": \"link\",\n      \"type\": \"submit\",\n      \"value\": \"ory\"\n    },\n    \"group\": \"oidc\",\n    \"messages\": [],\n    \"meta\": {\n      \"label\": {\n        \"context\": {\n          \"provider\": \"ory\"\n        },\n        \"id\": 1050002,\n        \"text\": \"Link ory\",\n        \"type\": \"info\"\n      }\n    },\n    \"type\": \"input\"\n  }\n]\n"
  },
  {
    "path": "ui/node/fixtures/sort/schema/2.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"abcd\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"website\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "ui/node/fixtures/sort/schema/3.json",
    "content": "{\n  \"$id\": \"https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"abcd\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\",\n          \"format\": \"email\",\n          \"title\": \"E-Mail\",\n          \"minLength\": 3,\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"website\": {\n          \"type\": \"string\",\n          \"format\": \"uri\",\n          \"minLength\": 10\n        }\n      },\n      \"required\": [\n        \"email\",\n        \"website\"\n      ],\n      \"additionalProperties\": false\n    }\n  }\n}\n"
  },
  {
    "path": "ui/node/fixtures/sort/schema/4.json",
    "content": "{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"traits\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"email\": {\n          \"format\": \"email\",\n          \"type\": \"string\",\n          \"ory.sh/kratos\": {\n            \"credentials\": {\n              \"password\": {\n                \"identifier\": true\n              }\n            }\n          }\n        },\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 2\n        }\n      },\n      \"required\": [\n        \"email\"\n      ]\n    }\n  },\n  \"additionalProperties\": false\n}\n"
  },
  {
    "path": "ui/node/helper.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage node\n\nfunc PasswordLoginOrder(in []string) []string {\n\tif len(in) == 0 {\n\t\treturn []string{\"csrf_token\", \"password\"}\n\t}\n\tif len(in) == 1 {\n\t\treturn append([]string{\"csrf_token\"}, in[0], \"password\")\n\t}\n\treturn append([]string{\"csrf_token\", in[0], \"password\"}, in[1:]...)\n}\n"
  },
  {
    "path": "ui/node/identifiers.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage node\n\nconst (\n\tTOTPCode      = \"totp_code\"\n\tTOTPSecretKey = \"totp_secret_key\"\n\tTOTPQR        = \"totp_qr\"\n\tTOTPUnlink    = \"totp_unlink\"\n)\n\nconst (\n\tLookupReveal     = \"lookup_secret_reveal\"\n\tLookupRegenerate = \"lookup_secret_regenerate\"\n\tLookupDisable    = \"lookup_secret_disable\"\n\tLookupCodes      = \"lookup_secret_codes\"\n\tLookupConfirm    = \"lookup_secret_confirm\"\n\tLookupCodeEnter  = \"lookup_secret\"\n)\n\nconst (\n\tProfileChooseCredentials = \"profile_choose_credentials\"\n)\n\nconst (\n\tWebAuthnRegisterTrigger     = \"webauthn_register_trigger\"\n\tWebAuthnRegister            = \"webauthn_register\"\n\tWebAuthnLogin               = \"webauthn_login\"\n\tWebAuthnLoginTrigger        = \"webauthn_login_trigger\"\n\tWebAuthnRegisterDisplayName = \"webauthn_register_displayname\"\n\tWebAuthnRemove              = \"webauthn_remove\"\n\tWebAuthnScript              = \"webauthn_script\"\n)\n\nconst (\n\tPasskeyRegisterTrigger  = \"passkey_register_trigger\"\n\tPasskeyRegister         = \"passkey_register\"\n\tPasskeySettingsRegister = \"passkey_settings_register\"\n\tPasskeyCreateData       = \"passkey_create_data\"\n\tPasskeyLogin            = \"passkey_login\"\n\tPasskeyChallenge        = \"passkey_challenge\"\n\tPasskeyLoginTrigger     = \"passkey_login_trigger\" //#nosec G101 -- Not a credential\n\tPasskeyRemove           = \"passkey_remove\"\n)\n"
  },
  {
    "path": "ui/node/node.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage node\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/kratos/schema\"\n\n\t\"github.com/ory/kratos/text\"\n)\n\n// swagger:enum UiNodeType\ntype UiNodeType string\n\nconst (\n\tText     UiNodeType = \"text\"\n\tInput    UiNodeType = \"input\"\n\tImage    UiNodeType = \"img\"\n\tAnchor   UiNodeType = \"a\"\n\tScript   UiNodeType = \"script\"\n\tDivision UiNodeType = \"div\"\n)\n\nfunc (t UiNodeType) String() string {\n\treturn string(t)\n}\n\n// swagger:enum UiNodeGroup\ntype UiNodeGroup string\n\nconst (\n\tDefaultGroup         UiNodeGroup = \"default\"\n\tPasswordGroup        UiNodeGroup = \"password\"\n\tOpenIDConnectGroup   UiNodeGroup = \"oidc\"\n\tProfileGroup         UiNodeGroup = \"profile\"\n\tLinkGroup            UiNodeGroup = \"link\"\n\tCodeGroup            UiNodeGroup = \"code\"\n\tTOTPGroup            UiNodeGroup = \"totp\"\n\tLookupGroup          UiNodeGroup = \"lookup_secret\"\n\tWebAuthnGroup        UiNodeGroup = \"webauthn\"\n\tPasskeyGroup         UiNodeGroup = \"passkey\"\n\tIdentifierFirstGroup UiNodeGroup = \"identifier_first\"\n\tCaptchaGroup         UiNodeGroup = \"captcha\" // Available in OEL\n\tSAMLGroup            UiNodeGroup = \"saml\"    // Available in OEL\n)\n\nfunc (g UiNodeGroup) String() string {\n\treturn string(g)\n}\n\n// swagger:model uiNodes\ntype Nodes []*Node\n\n// Node represents a flow's nodes\n//\n// Nodes are represented as HTML elements or their native UI equivalents. For example,\n// a node can be an `<img>` tag, or an `<input element>` but also `some plain text`.\n//\n// swagger:model uiNode\ntype Node struct {\n\t// The node's type\n\t//\n\t// required: true\n\tType UiNodeType `json:\"type\" faker:\"-\"`\n\n\t// Group specifies which group (e.g. password authenticator) this node belongs to.\n\t//\n\t// required: true\n\tGroup UiNodeGroup `json:\"group\"`\n\n\t// The node's attributes.\n\t//\n\t// required: true\n\t// swagger:type uiNodeAttributes\n\tAttributes Attributes `json:\"attributes\" faker:\"ui_node_attributes\"`\n\n\t// The node's messages\n\t//\n\t// Contains error, validation, or other messages relevant to this node.\n\t//\n\t// required: true\n\tMessages text.Messages `json:\"messages\"`\n\n\t// Meta contains a node meta information\n\t//\n\t// This might include a label and other information that can optionally\n\t// be used to render UIs.\n\t//\n\t// required: true\n\tMeta *Meta `json:\"meta\"`\n}\n\n// A Node's Meta Information\n//\n// This might include a label and other information that can optionally\n// be used to render UIs.\n//\n// swagger:model uiNodeMeta\ntype Meta struct {\n\t// Label represents the node's label.\n\t//\n\t// Keep in mind that these values are autogenerated and can not be changed.\n\t// If you wish to use other titles or labels implement that directly in\n\t// your UI.\n\tLabel *text.Message `json:\"label,omitempty\"`\n}\n\n// Used for en/decoding the Attributes field.\ntype jsonRawNode struct {\n\tType       UiNodeType    `json:\"type\"`\n\tGroup      UiNodeGroup   `json:\"group\"`\n\tAttributes Attributes    `json:\"attributes\"`\n\tMessages   text.Messages `json:\"messages\"`\n\tMeta       *Meta         `json:\"meta\"`\n}\n\nfunc (n *Node) ID() string {\n\treturn n.Attributes.ID()\n}\n\nfunc (n *Node) Reset() {\n\tn.Messages = nil\n\tn.Attributes.Reset()\n}\n\nfunc (n *Node) WithMetaLabel(label *text.Message) *Node {\n\tif n.Meta == nil {\n\t\tn.Meta = new(Meta)\n\t}\n\tn.Meta.Label = label\n\treturn n\n}\n\nfunc (n *Node) GetValue() interface{} {\n\treturn n.Attributes.GetValue()\n}\n\nfunc (n Nodes) Find(id string) *Node {\n\tfor _, nn := range n {\n\t\tif nn.ID() == id {\n\t\t\treturn nn\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (n Nodes) FindAll(id string) []Node {\n\tvar nodes []Node\n\tfor _, nn := range n {\n\t\tif nn.ID() == id {\n\t\t\tnodes = append(nodes, *nn)\n\t\t}\n\t}\n\n\treturn nodes\n}\n\nfunc (n Nodes) Reset(exclude ...string) {\n\tfor k, nn := range n {\n\t\tnn.Messages = nil\n\t\tif !slices.Contains(exclude, nn.ID()) {\n\t\t\tnn.Reset()\n\t\t}\n\t\tn[k] = nn\n\t}\n}\n\nfunc (n Nodes) ResetNodes(reset ...string) {\n\tfor k, nn := range n {\n\t\tif slices.Contains(reset, nn.ID()) {\n\t\t\tnn.Reset()\n\t\t}\n\t\tn[k] = nn\n\t}\n}\n\nfunc (n Nodes) ResetNodesWithPrefix(prefix string) {\n\tfor k := range n {\n\t\tif strings.HasPrefix(n[k].ID(), prefix) {\n\t\t\tn[k].Reset()\n\t\t}\n\t}\n}\n\nfunc getStringSliceIndexOf(needle []string, haystack string) int {\n\tfor k := range needle {\n\t\tif needle[k] == haystack {\n\t\t\treturn k\n\t\t}\n\t}\n\treturn -1\n}\n\ntype sortOptions struct {\n\torderByGroups     []string\n\tschemaRef         string\n\tkeysInOrder       []string\n\tkeysInOrderAppend []string\n\tkeysInOrderPost   func([]string) []string\n}\n\ntype SortOption func(*sortOptions)\n\nfunc SortByGroups(orderByGroups []UiNodeGroup) func(*sortOptions) {\n\treturn func(options *sortOptions) {\n\t\toptions.orderByGroups = make([]string, len(orderByGroups))\n\t\tfor k := range orderByGroups {\n\t\t\toptions.orderByGroups[k] = string(orderByGroups[k])\n\t\t}\n\t}\n}\n\nfunc SortBySchema(schemaRef string) func(*sortOptions) {\n\treturn func(options *sortOptions) {\n\t\toptions.schemaRef = schemaRef\n\t}\n}\n\nfunc SortUseOrder(keysInOrder []string) func(*sortOptions) {\n\treturn func(options *sortOptions) {\n\t\toptions.keysInOrder = keysInOrder\n\t}\n}\n\nfunc SortUseOrderAppend(keysInOrder []string) func(*sortOptions) {\n\treturn func(options *sortOptions) {\n\t\toptions.keysInOrderAppend = keysInOrder\n\t}\n}\n\nfunc SortUpdateOrder(f func([]string) []string) func(*sortOptions) {\n\treturn func(options *sortOptions) {\n\t\toptions.keysInOrderPost = f\n\t}\n}\n\nfunc (n Nodes) SortBySchema(ctx context.Context, opts ...SortOption) error {\n\tvar o sortOptions\n\tfor _, f := range opts {\n\t\tf(&o)\n\t}\n\n\tif o.schemaRef != \"\" {\n\t\tschemaKeys, err := schema.GetKeysInOrder(ctx, o.schemaRef)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\to.keysInOrder = append(o.keysInOrder, schemaKeys...)\n\t}\n\n\tif o.keysInOrderPost != nil {\n\t\to.keysInOrder = o.keysInOrderPost(o.keysInOrder)\n\t}\n\n\to.keysInOrder = append(o.keysInOrder, o.keysInOrderAppend...)\n\n\tgetKeyPosition := func(node *Node) int {\n\t\tlastPrefix := len(o.keysInOrder)\n\n\t\t// Method should always be the last element in the list\n\t\tif node.Attributes.ID() == \"method\" {\n\t\t\treturn len(n) + len(o.keysInOrder) + 1\n\t\t}\n\n\t\tfor i, n := range o.keysInOrder {\n\t\t\tif strings.HasPrefix(node.ID(), n) {\n\t\t\t\treturn i\n\t\t\t}\n\t\t}\n\n\t\treturn lastPrefix\n\t}\n\n\tif len(o.orderByGroups) > 0 {\n\t\t// Sort by groups so that default is in front, then oidc, password, ...\n\t\tsort.Slice(n, func(i, j int) bool {\n\t\t\ta := string(n[i].Group)\n\t\t\tb := string(n[j].Group)\n\t\t\treturn getStringSliceIndexOf(o.orderByGroups, a) < getStringSliceIndexOf(o.orderByGroups, b)\n\t\t})\n\t}\n\n\tsort.SliceStable(n, func(i, j int) bool {\n\t\ta := n[i]\n\t\tb := n[j]\n\n\t\tif a.Group == b.Group ||\n\t\t\t(a.Group == \"default\" && b.Group == \"password\") || (b.Group == \"default\" && a.Group == \"password\") {\n\t\t\tpa, pb := getKeyPosition(a), getKeyPosition(b)\n\t\t\tif pa < pb {\n\t\t\t\treturn true\n\t\t\t} else if pa > pb {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn fmt.Sprintf(\"%v\", a.GetValue()) < fmt.Sprintf(\"%v\", b.GetValue())\n\t\t}\n\n\t\treturn false\n\t})\n\n\treturn nil\n}\n\n// Remove removes one or more nodes by their IDs.\nfunc (n *Nodes) Remove(ids ...string) {\n\tif n == nil {\n\t\treturn\n\t}\n\n\tvar r Nodes\n\tfor k, v := range *n {\n\t\tvar found bool\n\t\tfor _, needle := range ids {\n\t\t\tif (*n)[k].ID() == needle {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tr = append(r, v)\n\t\t}\n\t}\n\t*n = r\n}\n\n// RemoveGroup removes all nodes belonging to a specific group.\nfunc (n *Nodes) RemoveGroup(group UiNodeGroup) {\n\tif n == nil {\n\t\treturn\n\t}\n\n\tvar r Nodes\n\tfor k, v := range *n {\n\t\tif (*n)[k] != nil && string((*n)[k].Group) != string(group) {\n\t\t\tr = append(r, v)\n\t\t}\n\t}\n\t*n = r\n}\n\n// RemoveGroup removes all nodes belonging to a set of groups.\nfunc (n *Nodes) RemoveGroups(group ...UiNodeGroup) {\n\tif n == nil {\n\t\treturn\n\t}\n\n\tfor _, g := range group {\n\t\tn.RemoveGroup(g)\n\t}\n}\n\n// ClearTransientNodes removes all nodes that belong to transient groups.\nfunc (n *Nodes) ClearTransientNodes() {\n\tn.RemoveGroups(\n\t\tDefaultGroup,\n\t\tPasswordGroup,\n\t\tOpenIDConnectGroup,\n\t\tProfileGroup,\n\t\tLinkGroup,\n\t\tCodeGroup,\n\t\tTOTPGroup,\n\t\tLookupGroup,\n\t\tWebAuthnGroup,\n\t\tPasskeyGroup,\n\t\tIdentifierFirstGroup,\n\t\tSAMLGroup,\n\t)\n}\n\n// Upsert updates or appends a node.\nfunc (n *Nodes) Upsert(node *Node) {\n\tif n == nil {\n\t\t*n = append(*n, node)\n\t\treturn\n\t}\n\n\tfor i := range *n {\n\t\tif (*n)[i].ID() == node.ID() {\n\t\t\t(*n)[i] = node\n\t\t\treturn\n\t\t}\n\t}\n\n\t*n = append(*n, node)\n}\n\n// SetValueAttribute sets a node's attribute's value or returns false if no node is found.\nfunc (n *Nodes) SetValueAttribute(id string, value interface{}) bool {\n\tfor i := range *n {\n\t\tif (*n)[i].ID() == id {\n\t\t\t(*n)[i].Attributes.SetValue(value)\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Append appends a node.\nfunc (n *Nodes) Append(node *Node) {\n\t*n = append(*n, node)\n}\n\nfunc (n *Nodes) RemoveMatching(node *Node) {\n\tif n == nil {\n\t\treturn\n\t}\n\n\tr := Nodes{}\n\tfor k, v := range *n {\n\t\tif !(*n)[k].Matches(node) {\n\t\t\tr = append(r, v)\n\t\t}\n\t}\n\n\t*n = r\n}\n\nfunc (n *Node) Matches(needle *Node) bool {\n\tif len(needle.ID()) > 0 && n.ID() != needle.ID() {\n\t\treturn false\n\t}\n\n\tif needle.Type != \"\" && n.Type != needle.Type {\n\t\treturn false\n\t}\n\n\tif needle.Group != \"\" && n.Group != needle.Group {\n\t\treturn false\n\t}\n\n\treturn n.Attributes.Matches(needle.Attributes)\n}\n\nfunc (n *Node) UnmarshalJSON(data []byte) error {\n\tvar attr Attributes\n\tswitch t := gjson.GetBytes(data, \"type\").String(); UiNodeType(t) {\n\tcase Text:\n\t\tattr = &TextAttributes{\n\t\t\tNodeType: Text,\n\t\t}\n\tcase Input:\n\t\tattr = &InputAttributes{\n\t\t\tNodeType: Input,\n\t\t}\n\tcase Anchor:\n\t\tattr = &AnchorAttributes{\n\t\t\tNodeType: Anchor,\n\t\t}\n\tcase Image:\n\t\tattr = &ImageAttributes{\n\t\t\tNodeType: Image,\n\t\t}\n\tcase Script:\n\t\tattr = &ScriptAttributes{\n\t\t\tNodeType: Script,\n\t\t}\n\tcase Division:\n\t\tattr = &DivisionAttributes{\n\t\t\tNodeType: Division,\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"unexpected node type: %s\", t)\n\t}\n\n\tvar d jsonRawNode\n\td.Attributes = attr\n\tif err := json.NewDecoder(bytes.NewReader(data)).Decode(&d); err != nil {\n\t\treturn err\n\t}\n\n\t*n = Node(d)\n\n\tif n.Meta == nil {\n\t\tn.Meta = new(Meta)\n\t}\n\n\treturn nil\n}\n\nfunc (n *Node) MarshalJSON() ([]byte, error) {\n\tvar t UiNodeType\n\tif n.Attributes != nil {\n\t\tswitch attr := n.Attributes.(type) {\n\t\tcase *TextAttributes:\n\t\t\tt = Text\n\t\t\tattr.NodeType = Text\n\t\tcase *InputAttributes:\n\t\t\tt = Input\n\t\t\tattr.NodeType = Input\n\t\tcase *AnchorAttributes:\n\t\t\tt = Anchor\n\t\t\tattr.NodeType = Anchor\n\t\tcase *ImageAttributes:\n\t\t\tt = Image\n\t\t\tattr.NodeType = Image\n\t\tcase *ScriptAttributes:\n\t\t\tt = Script\n\t\t\tattr.NodeType = Script\n\t\tcase *DivisionAttributes:\n\t\t\tt = Division\n\t\t\tattr.NodeType = Division\n\t\tdefault:\n\t\t\treturn nil, errors.WithStack(fmt.Errorf(\"unknown node type: %T\", n.Attributes))\n\t\t}\n\t}\n\n\tif n.Type == \"\" {\n\t\tn.Type = t\n\t} else if n.Type != t {\n\t\treturn nil, errors.WithStack(fmt.Errorf(\"node type and node attributes mismatch: %T != %s\", n.Attributes, n.Type))\n\t}\n\n\tif n.Meta == nil {\n\t\tn.Meta = new(Meta)\n\t}\n\n\treturn json.Marshal((*jsonRawNode)(n))\n}\n"
  },
  {
    "path": "ui/node/node_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage node_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"embed\"\n\t\"encoding/json\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/ory/x/snapshotx\"\n\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/ory/x/assertx\"\n\n\t\"github.com/ory/kratos/corpx\"\n\n\t\"github.com/ory/kratos/ui/container\"\n\n\t\"github.com/go-faker/faker/v4\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/jsonschema/v3\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc init() {\n\tcorpx.RegisterFakes()\n}\n\n//go:embed fixtures/sort/*\nvar sortFixtures embed.FS\nvar ctx = context.Background()\n\nfunc TestNodesSort(t *testing.T) {\n\t// use a schema compiler that disables identifiers\n\tschemaCompiler := jsonschema.NewCompiler()\n\tschemaPath := \"fixtures/identity.schema.json\"\n\n\tf, err := container.NewFromJSONSchema(ctx, \"/foo\", node.DefaultGroup, schemaPath, \"\", schemaCompiler)\n\trequire.NoError(t, err)\n\n\tf.UpdateNodeValuesFromJSON(json.RawMessage(`{}`), \"traits\", node.DefaultGroup)\n\tf.SetCSRF(\"csrf_token\")\n\n\tinputs, err := sortFixtures.ReadDir(\"fixtures/sort/input\")\n\trequire.NoError(t, err)\n\n\toptions := map[string][]node.SortOption{\n\t\t\"1.json\": {\n\t\t\tnode.SortUseOrder([]string{\"password_identifier\"}),\n\t\t\tnode.SortUpdateOrder(node.PasswordLoginOrder),\n\t\t\tnode.SortByGroups([]node.UiNodeGroup{\n\t\t\t\tnode.DefaultGroup,\n\t\t\t\tnode.ProfileGroup,\n\t\t\t\tnode.OpenIDConnectGroup,\n\t\t\t\tnode.PasswordGroup,\n\t\t\t\tnode.LinkGroup,\n\t\t\t\tnode.LinkGroup,\n\t\t\t}),\n\t\t},\n\t\t\"2.json\": {\n\t\t\tnode.SortBySchema(filepath.Join(\"fixtures/sort/schema\", \"2.json\")),\n\t\t\tnode.SortUpdateOrder(node.PasswordLoginOrder),\n\t\t\tnode.SortByGroups([]node.UiNodeGroup{\n\t\t\t\tnode.DefaultGroup,\n\t\t\t\tnode.OpenIDConnectGroup,\n\t\t\t\tnode.PasswordGroup,\n\t\t\t}),\n\t\t},\n\t\t\"3.json\": {\n\t\t\tnode.SortBySchema(filepath.Join(\"fixtures/sort/schema\", \"3.json\")),\n\t\t\tnode.SortByGroups([]node.UiNodeGroup{\n\t\t\t\tnode.DefaultGroup,\n\t\t\t\tnode.OpenIDConnectGroup,\n\t\t\t\tnode.PasswordGroup,\n\t\t\t}),\n\t\t},\n\t\t\"4.json\": {\n\t\t\tnode.SortBySchema(filepath.Join(\"fixtures/sort/schema\", \"4.json\")),\n\t\t\tnode.SortByGroups([]node.UiNodeGroup{\n\t\t\t\tnode.DefaultGroup,\n\t\t\t\tnode.ProfileGroup,\n\t\t\t\tnode.PasswordGroup,\n\t\t\t\tnode.OpenIDConnectGroup,\n\t\t\t\tnode.LookupGroup,\n\t\t\t\tnode.WebAuthnGroup,\n\t\t\t\tnode.TOTPGroup,\n\t\t\t}),\n\t\t\tnode.SortUseOrderAppend([]string{\n\t\t\t\t// Lookup\n\t\t\t\tnode.LookupReveal,\n\t\t\t\tnode.LookupRegenerate,\n\t\t\t\tnode.LookupCodes,\n\t\t\t\tnode.LookupConfirm,\n\n\t\t\t\t// Lookup\n\t\t\t\tnode.WebAuthnRemove,\n\t\t\t\tnode.WebAuthnRegisterDisplayName,\n\t\t\t\tnode.WebAuthnRegister,\n\n\t\t\t\t// TOTP\n\t\t\t\tnode.TOTPSecretKey,\n\t\t\t\tnode.TOTPQR,\n\t\t\t\tnode.TOTPUnlink,\n\t\t\t\tnode.TOTPCode,\n\t\t\t}),\n\t\t},\n\t}\n\n\tfor _, in := range inputs {\n\t\tt.Run(\"file=\"+in.Name(), func(t *testing.T) {\n\t\t\tif in.IsDir() {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tfi, err := sortFixtures.Open(filepath.Join(\"fixtures/sort/input\", in.Name()))\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() { _ = fi.Close() }()\n\n\t\t\tvar nodes node.Nodes\n\t\t\trequire.NoError(t, json.NewDecoder(fi).Decode(&nodes))\n\t\t\trequire.NotEmpty(t, nodes)\n\n\t\t\trequire.NoError(t, nodes.SortBySchema(ctx, options[in.Name()]...))\n\n\t\t\tfe, err := sortFixtures.ReadFile(filepath.Join(\"fixtures/sort/expected\", in.Name()))\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassertx.EqualAsJSON(t, json.RawMessage(fe), nodes)\n\t\t})\n\t}\n}\n\nfunc TestNodesUpsert(t *testing.T) {\n\tvar nodes node.Nodes\n\tnodes.Upsert(node.NewCSRFNode(\"foo\"))\n\trequire.Len(t, nodes, 1)\n\tnodes.Upsert(node.NewCSRFNode(\"bar\"))\n\trequire.Len(t, nodes, 1)\n\tassert.EqualValues(t, \"bar\", nodes[0].Attributes.GetValue())\n}\n\nfunc TestNodesRemove(t *testing.T) {\n\tvar nodes node.Nodes\n\tnodes.Append(node.NewInputField(\"other\", \"foo\", node.OpenIDConnectGroup, node.InputAttributeTypeSubmit))\n\tnodes.Append(node.NewInputField(\"link\", \"foo\", node.OpenIDConnectGroup, node.InputAttributeTypeSubmit))\n\tnodes.Append(node.NewInputField(\"link\", \"bar\", node.OpenIDConnectGroup, node.InputAttributeTypeSubmit))\n\tnodes.Append(node.NewInputField(\"unlink\", \"baz\", node.OpenIDConnectGroup, node.InputAttributeTypeSubmit))\n\trequire.Len(t, nodes, 4)\n\n\tnodes.Remove(\"link\", \"unlink\")\n\trequire.Len(t, nodes, 1)\n}\n\nfunc TestNodeJSON(t *testing.T) {\n\tt.Run(\"idempotent decode\", func(t *testing.T) {\n\t\tnodes := make(node.Nodes, 5)\n\t\tfor k := range nodes {\n\t\t\tnodes[k] = new(node.Node)\n\t\t\texpected := nodes[k]\n\n\t\t\trequire.NoError(t, faker.FakeData(expected))\n\t\t\tvar b bytes.Buffer\n\t\t\trequire.NoError(t, json.NewEncoder(&b).Encode(expected))\n\n\t\t\tvar actual node.Node\n\t\t\trequire.NoError(t, json.NewDecoder(&b).Decode(&actual))\n\n\t\t\tassert.EqualValues(t, *expected, actual)\n\t\t\tassert.NotEmpty(t, actual.Type)\n\t\t\tassert.NotNil(t, actual.Attributes)\n\t\t}\n\t})\n\n\tt.Run(\"type mismatch\", func(t *testing.T) {\n\t\tn := &node.Node{Type: node.Image, Attributes: new(node.InputAttributes)}\n\t\tvar b bytes.Buffer\n\t\trequire.EqualError(t, json.NewEncoder(&b).Encode(n), \"json: error calling MarshalJSON for type *node.Node: node type and node attributes mismatch: *node.InputAttributes != img\")\n\t})\n\n\tt.Run(\"type empty\", func(t *testing.T) {\n\t\tn := &node.Node{Attributes: new(node.InputAttributes)}\n\t\tvar b bytes.Buffer\n\t\trequire.NoError(t, json.NewEncoder(&b).Encode(n))\n\t\tassert.EqualValues(t, node.Input, gjson.GetBytes(b.Bytes(), \"type\").String())\n\t})\n\n\tt.Run(\"type decode unknown\", func(t *testing.T) {\n\t\tvar n node.Node\n\t\trequire.EqualError(t, json.NewDecoder(bytes.NewReader(json.RawMessage(`{\"type\": \"foo\"}`))).Decode(&n), \"unexpected node type: foo\")\n\t})\n}\n\nfunc TestMatchesNode(t *testing.T) {\n\t// Test when ID is different\n\tnode1 := &node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"foo\"}}\n\tnode2 := &node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"bar\"}}\n\tassert.False(t, node1.Matches(node2))\n\n\t// Test when Type is different\n\tnode1 = &node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"foo\"}}\n\tnode2 = &node.Node{Type: node.Text, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"foo\"}}\n\tassert.False(t, node1.Matches(node2))\n\n\t// Test when Group is different\n\tnode1 = &node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"foo\"}}\n\tnode2 = &node.Node{Type: node.Input, Group: node.OpenIDConnectGroup, Attributes: &node.InputAttributes{Name: \"foo\"}}\n\tassert.False(t, node1.Matches(node2))\n\n\t// Test when all fields are the same\n\tnode1 = &node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"foo\"}}\n\tnode2 = &node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"foo\"}}\n\tassert.True(t, node1.Matches(node2))\n}\n\nfunc TestRemoveMatchingNodes(t *testing.T) {\n\tnodes := node.Nodes{\n\t\t&node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"foo\"}},\n\t\t&node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"bar\"}},\n\t\t&node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"baz\"}},\n\t}\n\n\t// Test when node to remove is present\n\tnodeToRemove := &node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"bar\"}}\n\tnodes.RemoveMatching(nodeToRemove)\n\tassert.Len(t, nodes, 2)\n\tfor _, n := range nodes {\n\t\tassert.NotEqual(t, nodeToRemove.ID(), n.ID())\n\t}\n\n\t// Test when node to remove is not present\n\tnodeToRemove = &node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"qux\"}}\n\tnodes.RemoveMatching(nodeToRemove)\n\tassert.Len(t, nodes, 2) // length should remain the same\n\n\t// Test when node to remove is present\n\tnodeToRemove = &node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"baz\"}}\n\tui := &container.Container{\n\t\tNodes: nodes,\n\t}\n\n\tui.GetNodes().RemoveMatching(nodeToRemove)\n\tassert.Len(t, *ui.GetNodes(), 1)\n\tfor _, n := range *ui.GetNodes() {\n\t\tassert.NotEqual(t, \"bar\", n.ID())\n\t\tassert.NotEqual(t, \"baz\", n.ID())\n\t}\n\n\tui.Nodes.Append(node.NewInputField(\"method\", \"foo\", \"bar\", node.InputAttributeTypeSubmit).WithMetaLabel(text.NewInfoNodeLabelContinue()))\n\tassert.NotNil(t, ui.Nodes.Find(\"method\"))\n\tui.GetNodes().RemoveMatching(node.NewInputField(\"method\", \"foo\", \"bar\", node.InputAttributeTypeSubmit))\n\tassert.Nil(t, ui.Nodes.Find(\"method\"))\n}\n\nfunc TestNodeMarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\tnode    *node.Node\n\t\twantErr bool\n\t\terrMsg  string\n\t}{\n\t\t{\n\t\t\tname: \"text node\",\n\t\t\tnode: &node.Node{\n\t\t\t\tType:  node.Text,\n\t\t\t\tGroup: node.DefaultGroup,\n\t\t\t\tAttributes: &node.TextAttributes{\n\t\t\t\t\tNodeType: node.Text,\n\t\t\t\t},\n\t\t\t\tMessages: text.Messages{},\n\t\t\t\tMeta:     &node.Meta{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"input node\",\n\t\t\tnode: &node.Node{\n\t\t\t\tType:  node.Input,\n\t\t\t\tGroup: node.DefaultGroup,\n\t\t\t\tAttributes: &node.InputAttributes{\n\t\t\t\t\tNodeType:   node.Input,\n\t\t\t\t\tName:       \"password\",\n\t\t\t\t\tType:       \"password\",\n\t\t\t\t\tFieldValue: \"secret\",\n\t\t\t\t},\n\t\t\t\tMessages: text.Messages{},\n\t\t\t\tMeta:     &node.Meta{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"anchor node\",\n\t\t\tnode: &node.Node{\n\t\t\t\tType:  node.Anchor,\n\t\t\t\tGroup: node.DefaultGroup,\n\t\t\t\tAttributes: &node.AnchorAttributes{\n\t\t\t\t\tNodeType: node.Anchor,\n\t\t\t\t\tHREF:     \"https://example.com\",\n\t\t\t\t},\n\t\t\t\tMessages: text.Messages{},\n\t\t\t\tMeta:     &node.Meta{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"image node\",\n\t\t\tnode: &node.Node{\n\t\t\t\tType:  node.Image,\n\t\t\t\tGroup: node.DefaultGroup,\n\t\t\t\tAttributes: &node.ImageAttributes{\n\t\t\t\t\tNodeType: node.Image,\n\t\t\t\t\tSource:   \"image.jpg\",\n\t\t\t\t},\n\t\t\t\tMessages: text.Messages{},\n\t\t\t\tMeta:     &node.Meta{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"script node\",\n\t\t\tnode: &node.Node{\n\t\t\t\tType:  node.Script,\n\t\t\t\tGroup: node.DefaultGroup,\n\t\t\t\tAttributes: &node.ScriptAttributes{\n\t\t\t\t\tNodeType: node.Script,\n\t\t\t\t\tSource:   \"script.js\",\n\t\t\t\t},\n\t\t\t\tMessages: text.Messages{},\n\t\t\t\tMeta:     &node.Meta{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"division node\",\n\t\t\tnode: &node.Node{\n\t\t\t\tType:  node.Division,\n\t\t\t\tGroup: node.DefaultGroup,\n\t\t\t\tAttributes: &node.DivisionAttributes{\n\t\t\t\t\tNodeType: node.Division,\n\t\t\t\t},\n\t\t\t\tMessages: text.Messages{},\n\t\t\t\tMeta:     &node.Meta{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"type mismatch\",\n\t\t\tnode: &node.Node{\n\t\t\t\tType:       node.Image,\n\t\t\t\tGroup:      node.DefaultGroup,\n\t\t\t\tAttributes: &node.InputAttributes{NodeType: node.Input},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t\terrMsg:  \"node type and node attributes mismatch\",\n\t\t},\n\t\t{\n\t\t\tname: \"empty type inferred from attributes\",\n\t\t\tnode: &node.Node{\n\t\t\t\tGroup: node.DefaultGroup,\n\t\t\t\tAttributes: &node.InputAttributes{\n\t\t\t\t\tNodeType: node.Input,\n\t\t\t\t\tName:     \"email\",\n\t\t\t\t},\n\t\t\t\tMessages: text.Messages{},\n\t\t\t\tMeta:     &node.Meta{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"nil attributes\",\n\t\t\tnode: &node.Node{\n\t\t\t\tType:       node.Image,\n\t\t\t\tGroup:      node.DefaultGroup,\n\t\t\t\tAttributes: nil,\n\t\t\t\tMessages:   text.Messages{},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t\terrMsg:  \"node type and node attributes mismatch\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tdata, err := json.Marshal(tt.node)\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.errMsg)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// Use snapshotx for testing serialization\n\t\t\tsnapshotx.SnapshotT(t, json.RawMessage(data))\n\n\t\t\t// Verify roundtrip\n\t\t\tvar unmarshalled node.Node\n\t\t\terr = json.Unmarshal(data, &unmarshalled)\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// Re-marshal for comparison\n\t\t\tremarshalled, err := json.Marshal(&unmarshalled)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.JSONEq(t, string(data), string(remarshalled))\n\t\t})\n\t}\n}\n\nfunc TestNodes_FindAll(t *testing.T) {\n\tnodes := node.Nodes{\n\t\t&node.Node{Type: node.Input, Group: node.DefaultGroup, Attributes: &node.InputAttributes{Name: \"a\"}},\n\t\t&node.Node{Type: node.Input, Group: node.DefaultGroup, Attributes: &node.InputAttributes{Name: \"b\"}},\n\t\t&node.Node{Type: node.Input, Group: node.DefaultGroup, Attributes: &node.InputAttributes{Name: \"a\"}},\n\t\t&node.Node{Type: node.Input, Group: node.DefaultGroup, Attributes: &node.InputAttributes{Name: \"c\"}},\n\t}\n\n\tt.Run(\"find matches multiple nodes\", func(t *testing.T) {\n\t\tgot := nodes.FindAll(\"a\")\n\t\tassert.Equal(t, []node.Node{*nodes[0], *nodes[2]}, got)\n\t})\n\n\tt.Run(\"find matches one node\", func(t *testing.T) {\n\t\tgot := nodes.FindAll(\"b\")\n\t\tassert.Equal(t, []node.Node{*nodes[1]}, got)\n\t})\n\n\tt.Run(\"no match returns empty slice\", func(t *testing.T) {\n\t\tgot := nodes.FindAll(\"x\")\n\t\tassert.Empty(t, got)\n\t})\n}\n\nfunc TestNodes_RemoveGroup(t *testing.T) {\n\tnodes := node.Nodes{\n\t\t&node.Node{Type: node.Input, Group: node.DefaultGroup, Attributes: &node.InputAttributes{Name: \"default\"}},\n\t\t&node.Node{Type: node.Input, Group: node.CaptchaGroup, Attributes: &node.InputAttributes{Name: \"captcha1\"}},\n\t\t&node.Node{Type: node.Input, Group: node.IdentifierFirstGroup, Attributes: &node.InputAttributes{Name: \"idfirst\"}},\n\t\t&node.Node{Type: node.Input, Group: node.CaptchaGroup, Attributes: &node.InputAttributes{Name: \"captcha2\"}},\n\t\t&node.Node{Type: node.Input, Group: node.PasswordGroup, Attributes: &node.InputAttributes{Name: \"password\"}},\n\t\tnil,\n\t}\n\n\tt.Run(\"removes single node\", func(t *testing.T) {\n\t\tn := append(node.Nodes(nil), nodes...)\n\t\tn.RemoveGroup(node.IdentifierFirstGroup)\n\t\tassert.Equal(t, node.Nodes{nodes[0], nodes[1], nodes[3], nodes[4]}, n)\n\t})\n\n\tt.Run(\"removes multiple nodes\", func(t *testing.T) {\n\t\tn := append(node.Nodes(nil), nodes...)\n\t\tn.RemoveGroup(node.CaptchaGroup)\n\t\tassert.Equal(t, node.Nodes{nodes[0], nodes[2], nodes[4]}, n)\n\t})\n\n\tt.Run(\"removes no nodes\", func(t *testing.T) {\n\t\tn := append(node.Nodes(nil), nodes...)\n\t\tn.RemoveGroup(node.PasskeyGroup)\n\t\tassert.Equal(t, nodes[:5], n)\n\t})\n\n\tt.Run(\"removes multiple groups\", func(t *testing.T) {\n\t\tn := append(node.Nodes(nil), nodes...)\n\t\tn.RemoveGroups(node.CaptchaGroup, node.PasswordGroup)\n\t\tassert.Equal(t, node.Nodes{nodes[0], nodes[2]}, n)\n\t})\n\n\tt.Run(\"removes multiple groups empty\", func(t *testing.T) {\n\t\tn := append(node.Nodes(nil), nodes...)\n\t\tn.RemoveGroups()\n\t\tassert.Equal(t, nodes, n)\n\t})\n\n\tt.Run(\"removes multiple groups no matching\", func(t *testing.T) {\n\t\tn := append(node.Nodes(nil), nodes...)\n\t\tn.RemoveGroups(node.PasskeyGroup)\n\t\tassert.Equal(t, nodes[:5], n)\n\t})\n}\n"
  },
  {
    "path": "x/cookie.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gorilla/sessions\"\n\t\"github.com/pkg/errors\"\n)\n\n// SessionPersistValues adds values to the session store and persists the changes.\nfunc SessionPersistValues(w http.ResponseWriter, r *http.Request, s sessions.StoreExact, id string, values map[string]interface{}) error {\n\t// The error does not matter because in the worst case we're re-writing the session cookie.\n\tcookie, _ := s.Get(r, id)\n\tfor k, v := range values {\n\t\tcookie.Values[k] = v\n\t}\n\n\treturn errors.WithStack(cookie.Save(r, w))\n}\n\n// SessionGetString returns a string for the given id and key or an error if the session is invalid,\n// the key does not exist, or the key value is not a string.\nfunc SessionGetString(r *http.Request, s sessions.StoreExact, id string, key interface{}) (string, error) {\n\tcheck := func(v map[interface{}]interface{}) (string, error) {\n\t\tvv, ok := v[key]\n\t\tif !ok {\n\t\t\treturn \"\", errors.Errorf(\"key %s does not exist in cookie: %+v\", key, id)\n\t\t} else if vvv, ok := vv.(string); !ok {\n\t\t\treturn \"\", errors.Errorf(\"value of key %s is not of type string in cookie\", key)\n\t\t} else {\n\t\t\treturn vvv, nil\n\t\t}\n\t}\n\n\tvar exactErr error\n\tcookie, err := s.GetExact(r, id, func(s *sessions.Session) bool {\n\t\t_, exactErr = check(s.Values)\n\t\treturn exactErr == nil\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t} else if exactErr != nil {\n\t\treturn \"\", exactErr\n\t}\n\n\treturn check(cookie.Values)\n}\n\n// SessionGetStringOr returns a string for the given id and key or the fallback value if the session is invalid,\n// the key does not exist, or the key value is not a string.\nfunc SessionGetStringOr(r *http.Request, s sessions.StoreExact, id, key, fallback string) string {\n\tv, err := SessionGetString(r, s, id, key)\n\tif err != nil {\n\t\treturn fallback\n\t}\n\treturn v\n}\n\nfunc SessionUnset(w http.ResponseWriter, r *http.Request, s sessions.StoreExact, id string) error {\n\tcookie, err := s.Get(r, id)\n\tif err == nil && cookie.IsNew {\n\t\t// No cookie was sent in the request. We have nothing to do.\n\t\treturn nil\n\t}\n\n\tcookie.Options.MaxAge = -1\n\tcookie.Values = make(map[interface{}]interface{})\n\treturn errors.WithStack(cookie.Save(r, w))\n}\n\nfunc SessionSetExpiresIn(w http.ResponseWriter, r *http.Request, s sessions.StoreExact, id string, expiresIn time.Duration) error {\n\tcookie, err := s.Get(r, id)\n\tif err == nil && cookie.IsNew {\n\t\t// No cookie was sent in the request. We have nothing to do.\n\t\treturn nil\n\t}\n\n\tcookie.Options.MaxAge = int(expiresIn.Seconds())\n\treturn errors.WithStack(cookie.Save(r, w))\n}\n\nfunc SessionUnsetKey(w http.ResponseWriter, r *http.Request, s sessions.StoreExact, id, key string) error {\n\tcookie, err := s.Get(r, id)\n\tif err == nil && cookie.IsNew {\n\t\t// No cookie was sent in the request. We have nothing to do.\n\t\treturn nil\n\t}\n\n\tdelete(cookie.Values, key)\n\treturn errors.WithStack(cookie.Save(r, w))\n}\n"
  },
  {
    "path": "x/cookie_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"net/http\"\n\t\"net/http/cookiejar\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gorilla/sessions\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSession(t *testing.T) {\n\tconst sid = \"test_session\"\n\n\ts := sessions.NewCookieStore([]byte(\"cyan cat walking over keyboard\"))\n\ts.Options.MaxAge = 78652871\n\tcj, err := cookiejar.New(&cookiejar.Options{})\n\trequire.NoError(t, err)\n\tc := http.Client{Jar: cj}\n\n\tisExpiryCorrect := func(t *testing.T, r *http.Request) {\n\t\tcookie, _ := s.Get(r, sid)\n\t\tassert.EqualValues(t, 78652871, cookie.Options.MaxAge, \"we ensure the options are always copied correctly.\")\n\t}\n\n\trouter := http.NewServeMux()\n\trouter.HandleFunc(\"GET /set\", func(w http.ResponseWriter, r *http.Request) {\n\t\trequire.NoError(t, SessionPersistValues(w, r, s, sid, map[string]interface{}{\n\t\t\t\"string-1\": \"foo\",\n\t\t\t\"string-2\": \"bar\",\n\t\t\t\"string-3\": \"\",\n\t\t\t\"int\":      1234,\n\t\t}))\n\t\tisExpiryCorrect(t, r)\n\t\tw.WriteHeader(http.StatusNoContent)\n\t})\n\n\tts := httptest.NewServer(router)\n\tdefer ts.Close()\n\n\tmr := func(t *testing.T, path string) {\n\t\tres, err := c.Get(ts.URL + \"/\" + path)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, http.StatusNoContent, res.StatusCode)\n\t\trequire.NoError(t, res.Body.Close())\n\t}\n\tmr(t, \"set\")\n\n\tt.Run(\"case=GetString\", func(t *testing.T) {\n\t\tid := \"get-string\"\n\t\trouter.HandleFunc(\"GET /\"+id, func(w http.ResponseWriter, r *http.Request) {\n\t\t\tgot, err := SessionGetString(r, s, sid, \"string-1\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, \"foo\", got)\n\n\t\t\tgot, err = SessionGetString(r, s, sid, \"string-2\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, \"bar\", got)\n\n\t\t\tgot, err = SessionGetString(r, s, sid, \"string-3\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, \"\", got)\n\n\t\t\t_, err = SessionGetString(r, s, sid, \"int\")\n\t\t\trequire.Error(t, err)\n\n\t\t\t_, err = SessionGetString(r, s, sid, \"i-dont-exist\")\n\t\t\trequire.Error(t, err)\n\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\t})\n\n\tt.Run(\"case=GetStringMultipleCookies\", func(t *testing.T) {\n\t\tid := \"get-string-multiple\"\n\n\t\trouter.HandleFunc(\"GET /set/\"+id, func(w http.ResponseWriter, r *http.Request) {\n\t\t\trequire.NoError(t, SessionPersistValues(w, r, s, sid, map[string]interface{}{\n\t\t\t\t\"multiple-string-1\": \"foo\",\n\t\t\t}))\n\t\t\trequire.NoError(t, SessionPersistValues(w, r, s, sid, map[string]interface{}{\n\t\t\t\t\"multiple-string-2\": \"bar\",\n\t\t\t}))\n\t\t\tisExpiryCorrect(t, r)\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\n\t\trouter.HandleFunc(\"GET /get/\"+id, func(w http.ResponseWriter, r *http.Request) {\n\t\t\tgot, err := SessionGetString(r, s, sid, \"multiple-string-1\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, \"foo\", got)\n\n\t\t\tgot, err = SessionGetString(r, s, sid, \"multiple-string-2\")\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, \"bar\", got)\n\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\n\t\tres, err := http.DefaultClient.Get(ts.URL + \"/set/\" + id)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, http.StatusNoContent, res.StatusCode)\n\t\trequire.NoError(t, res.Body.Close())\n\n\t\treq, _ := http.NewRequest(\"GET\", ts.URL+\"/get/\"+id, nil)\n\t\tfor _, c := range res.Cookies() {\n\t\t\treq.AddCookie(c)\n\t\t}\n\n\t\tres, err = http.DefaultClient.Do(req)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, http.StatusNoContent, res.StatusCode)\n\t\trequire.NoError(t, res.Body.Close())\n\t})\n\n\tt.Run(\"case=GetStringOr\", func(t *testing.T) {\n\t\tid := \"get-string-or\"\n\t\trouter.HandleFunc(\"GET /\"+id, func(w http.ResponseWriter, r *http.Request) {\n\t\t\tassert.EqualValues(t, \"foo\", SessionGetStringOr(r, s, sid, \"string-1\", \"baz\"))\n\t\t\tassert.EqualValues(t, \"bar\", SessionGetStringOr(r, s, sid, \"string-2\", \"baz\"))\n\t\t\tassert.EqualValues(t, \"\", SessionGetStringOr(r, s, sid, \"string-3\", \"baz\"))\n\t\t\tassert.EqualValues(t, \"baz\", SessionGetStringOr(r, s, sid, \"int\", \"baz\"))\n\t\t\tassert.EqualValues(t, \"baz\", SessionGetStringOr(r, s, sid, \"i-dont-exist\", \"baz\"))\n\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\t\tmr(t, id)\n\t})\n\n\tmrCookie := func(t *testing.T, loc string, cks []*http.Cookie, f func(c []*http.Cookie, req *http.Request)) *http.Response {\n\t\treq, err := http.NewRequest(\"GET\", loc, nil)\n\t\trequire.NoError(t, err)\n\n\t\t// Mess with the signature\n\t\tif f != nil {\n\t\t\tf(cks, req)\n\t\t}\n\n\t\tres, err := http.DefaultClient.Do(req)\n\t\trequire.NoError(t, err)\n\t\treturn res\n\t}\n\n\tmrUnset := func(t *testing.T, ts *httptest.Server, id string, f func(c []*http.Cookie, req *http.Request)) *http.Response {\n\t\tres, err := http.Get(ts.URL + \"/set\")\n\t\trequire.NoError(t, err)\n\n\t\tres = mrCookie(t, ts.URL+\"/\"+id+\"/unset\", res.Cookies(), f)\n\t\trequire.EqualValues(t, http.StatusNoContent, res.StatusCode)\n\t\trequire.NoError(t, res.Body.Close())\n\t\treturn res\n\t}\n\n\tsignatureScrambler := func(c []*http.Cookie, req *http.Request) {\n\t\tfor _, c := range c {\n\t\t\tc.Value = strings.ReplaceAll(c.Value, \"a\", \"b\")\n\t\t\treq.AddCookie(c)\n\t\t}\n\t}\n\n\tcookieCopier := func(c []*http.Cookie, req *http.Request) {\n\t\tfor _, c := range c {\n\t\t\treq.AddCookie(c)\n\t\t}\n\t}\n\n\tt.Run(\"case=SessionSet\", func(t *testing.T) {\n\t\tres, err := http.Get(ts.URL + \"/set\")\n\t\trequire.NoError(t, err)\n\n\t\tres = mrCookie(t, ts.URL+\"/set\", res.Cookies(), signatureScrambler)\n\t\trequire.EqualValues(t, http.StatusNoContent, res.StatusCode)\n\t\trequire.NoError(t, res.Body.Close())\n\t})\n\n\tt.Run(\"case=SessionUnset\", func(t *testing.T) {\n\t\tt.Run(\"prevent https://github.com/gorilla/sessions/pull/251\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\t\t\trequire.NoError(t, SessionUnset(w, new(http.Request), s, \"idonotexist\"))\n\t\t\trequire.NoError(t, SessionUnset(w, new(http.Request), s, \"idonotexist\"))\n\t\t\trequire.Len(t, w.Result().Cookies(), 0)\n\t\t})\n\n\t\tid := \"session-unset\"\n\t\trouter.HandleFunc(\"GET /\"+id+\"/unset\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\trequire.NoError(t, SessionUnset(w, r, s, sid))\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t\tcookie, _ := s.Get(r, sid)\n\t\t\tassert.EqualValues(t, -1, cookie.Options.MaxAge, \"we ensure the options are always copied correctly.\")\n\t\t})\n\n\t\trouter.HandleFunc(\"GET /\"+id+\"/get\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"string-1\", \"\"))\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"string-2\", \"\"))\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"string-3\", \"\"))\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"int\", \"\"))\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"i-dont-exist\", \"\"))\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\n\t\tt.Run(\"with invalid cookie signature\", func(t *testing.T) {\n\t\t\tres := mrUnset(t, ts, id, signatureScrambler)\n\t\t\tassert.Len(t, res.Cookies(), 1)\n\t\t\tmrCookie(t, ts.URL+\"/\"+id+\"/get\", res.Cookies(), cookieCopier)\n\t\t\trequire.EqualValues(t, http.StatusNoContent, res.StatusCode)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t})\n\n\t\tt.Run(\"with valid cookie signature\", func(t *testing.T) {\n\t\t\tres := mrUnset(t, ts, id, cookieCopier)\n\t\t\tassert.Len(t, res.Cookies(), 1)\n\t\t\tmrCookie(t, ts.URL+\"/\"+id+\"/get\", res.Cookies(), cookieCopier)\n\t\t\trequire.EqualValues(t, http.StatusNoContent, res.StatusCode)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t})\n\t})\n\n\tt.Run(\"case=SessionUnsetKey\", func(t *testing.T) {\n\t\tt.Run(\"prevent https://github.com/gorilla/sessions/pull/251\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\t\t\trequire.NoError(t, SessionUnsetKey(w, new(http.Request), s, \"idonotexist\", \"\"))\n\t\t\trequire.NoError(t, SessionUnsetKey(w, new(http.Request), s, \"idonotexist\", \"\"))\n\t\t\trequire.Len(t, w.Result().Cookies(), 0)\n\t\t})\n\n\t\tid := \"session-unset-key\"\n\t\trouter.HandleFunc(\"GET /\"+id+\"/unset\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\trequire.NoError(t, SessionUnsetKey(w, r, s, sid, \"string-1\"))\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t\tisExpiryCorrect(t, r)\n\t\t})\n\n\t\trouter.HandleFunc(\"GET /\"+id+\"/expect-unset\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"string-1\", \"\"))\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"string-2\", \"\"))\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"string-3\", \"\"))\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"int\", \"\"))\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"i-dont-exist\", \"\"))\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\n\t\trouter.HandleFunc(\"GET /\"+id+\"/expect-one\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\trequire.Empty(t, SessionGetStringOr(r, s, sid, \"string-1\", \"\"))\n\t\t\tassert.EqualValues(t, \"bar\", SessionGetStringOr(r, s, sid, \"string-2\", \"baz\"))\n\t\t\tassert.EqualValues(t, \"\", SessionGetStringOr(r, s, sid, \"string-3\", \"baz\"))\n\t\t\tassert.EqualValues(t, \"baz\", SessionGetStringOr(r, s, sid, \"int\", \"baz\"))\n\t\t\tassert.EqualValues(t, \"baz\", SessionGetStringOr(r, s, sid, \"i-dont-exist\", \"baz\"))\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t})\n\n\t\tt.Run(\"with invalid cookie signature\", func(t *testing.T) {\n\t\t\tres := mrUnset(t, ts, id, signatureScrambler)\n\t\t\tassert.Len(t, res.Cookies(), 1)\n\t\t\tmrCookie(t, ts.URL+\"/\"+id+\"/expect-unset\", res.Cookies(), cookieCopier)\n\t\t\trequire.EqualValues(t, http.StatusNoContent, res.StatusCode)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t})\n\n\t\tt.Run(\"with valid cookie signature\", func(t *testing.T) {\n\t\t\tres := mrUnset(t, ts, id, cookieCopier)\n\t\t\tassert.Len(t, res.Cookies(), 1)\n\t\t\tmrCookie(t, ts.URL+\"/\"+id+\"/expect-one\", res.Cookies(), cookieCopier)\n\t\t\trequire.EqualValues(t, http.StatusNoContent, res.StatusCode)\n\t\t\trequire.NoError(t, res.Body.Close())\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "x/err.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\n\t\"github.com/gofrs/uuid\"\n\n\t\"github.com/ory/herodot\"\n)\n\ntype WithIdentityIDError struct {\n\terr        error\n\tidentityID uuid.UUID\n}\n\nfunc (e *WithIdentityIDError) Error() string {\n\treturn e.err.Error()\n}\n\nfunc (e *WithIdentityIDError) Unwrap() error {\n\treturn e.err\n}\n\nfunc (e *WithIdentityIDError) IdentityID() uuid.UUID {\n\treturn e.identityID\n}\n\nfunc WrapWithIdentityIDError(err error, identityID uuid.UUID) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\treturn &WithIdentityIDError{\n\t\terr:        err,\n\t\tidentityID: identityID,\n\t}\n}\n\nvar (\n\tPseudoPanic = herodot.DefaultError{\n\t\tStatusField: http.StatusText(http.StatusInternalServerError),\n\t\tErrorField:  \"Code Bug Detected\",\n\t\tReasonField: \"The code ended up at a place where it should not have. Please report this as an issue at https://github.com/ory/kratos\",\n\t\tCodeField:   http.StatusInternalServerError,\n\t}\n\tPageTokenInvalid = herodot.ErrBadRequest.WithReason(\"The page token is invalid, do not craft your own page tokens\")\n)\n\nfunc RecoverStatusCode(err error, fallback int) int {\n\tvar sc herodot.StatusCodeCarrier\n\tif errors.As(err, &sc) {\n\t\treturn sc.StatusCode()\n\t}\n\treturn fallback\n}\n\nfunc Must[T any](t T, err error) T {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn t\n}\n"
  },
  {
    "path": "x/err_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestWrapWithIdentityIDError(t *testing.T) {\n\tt.Run(\"case=wraps error with identity ID\", func(t *testing.T) {\n\t\tbaseErr := errors.New(\"test error\")\n\t\tidentityID := uuid.Must(uuid.NewV4())\n\n\t\twrappedErr := x.WrapWithIdentityIDError(baseErr, identityID)\n\n\t\trequire.NotNil(t, wrappedErr)\n\t\tassert.Equal(t, \"test error\", wrappedErr.Error())\n\n\t\tvar withIDErr *x.WithIdentityIDError\n\t\trequire.True(t, errors.As(wrappedErr, &withIDErr))\n\t\tassert.Equal(t, identityID, withIDErr.IdentityID())\n\t})\n\n\tt.Run(\"case=unwraps to original error\", func(t *testing.T) {\n\t\tbaseErr := errors.New(\"original error\")\n\t\tidentityID := uuid.Must(uuid.NewV4())\n\n\t\twrappedErr := x.WrapWithIdentityIDError(baseErr, identityID)\n\n\t\tunwrappedErr := errors.Unwrap(wrappedErr)\n\t\tassert.Equal(t, baseErr, unwrappedErr)\n\t})\n\n\tt.Run(\"case=returns nil when wrapping nil error\", func(t *testing.T) {\n\t\tidentityID := uuid.Must(uuid.NewV4())\n\n\t\twrappedErr := x.WrapWithIdentityIDError(nil, identityID)\n\n\t\tassert.Nil(t, wrappedErr)\n\t})\n\n\tt.Run(\"case=preserves identity ID with nil UUID\", func(t *testing.T) {\n\t\tbaseErr := errors.New(\"test error\")\n\t\tvar identityID uuid.UUID // nil UUID\n\n\t\twrappedErr := x.WrapWithIdentityIDError(baseErr, identityID)\n\n\t\tvar withIDErr *x.WithIdentityIDError\n\t\trequire.True(t, errors.As(wrappedErr, &withIDErr))\n\t\tassert.Equal(t, uuid.Nil, withIDErr.IdentityID())\n\t})\n\n\tt.Run(\"case=can wrap already wrapped error\", func(t *testing.T) {\n\t\tbaseErr := errors.New(\"base error\")\n\t\tfirstID := uuid.Must(uuid.NewV4())\n\t\tsecondID := uuid.Must(uuid.NewV4())\n\n\t\tfirstWrap := x.WrapWithIdentityIDError(baseErr, firstID)\n\t\tsecondWrap := x.WrapWithIdentityIDError(firstWrap, secondID)\n\n\t\tvar withIDErr *x.WithIdentityIDError\n\t\trequire.True(t, errors.As(secondWrap, &withIDErr))\n\t\t// Should get the outermost identity ID\n\t\tassert.Equal(t, secondID, withIDErr.IdentityID())\n\t})\n\n\tt.Run(\"case=works with errors.Is\", func(t *testing.T) {\n\t\tbaseErr := errors.New(\"base error\")\n\t\tidentityID := uuid.Must(uuid.NewV4())\n\n\t\twrappedErr := x.WrapWithIdentityIDError(baseErr, identityID)\n\n\t\tassert.True(t, errors.Is(wrappedErr, baseErr))\n\t})\n}\n"
  },
  {
    "path": "x/events/events.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage events\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\totelattr \"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/kratos/schema\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/x/jsonx\"\n\t\"github.com/ory/x/otelx/semconv\"\n)\n\nconst (\n\tIdentityCreated          semconv.Event = \"IdentityCreated\"\n\tIdentityDeleted          semconv.Event = \"IdentityDeleted\"\n\tIdentityUpdated          semconv.Event = \"IdentityUpdated\"\n\tJsonnetMappingFailed     semconv.Event = \"JsonnetMappingFailed\"\n\tLoginFailed              semconv.Event = \"LoginFailed\"\n\tLoginInitiated           semconv.Event = \"LoginInitiated\"\n\tLoginSucceeded           semconv.Event = \"LoginSucceeded\"\n\tRecoveryFailed           semconv.Event = \"RecoveryFailed\"\n\tRecoveryInitiatedByAdmin semconv.Event = \"RecoveryInitiatedByAdmin\"\n\tRecoverySucceeded        semconv.Event = \"RecoverySucceeded\"\n\tRegistrationFailed       semconv.Event = \"RegistrationFailed\"\n\tRegistrationInitiated    semconv.Event = \"RegistrationInitiated\"\n\tRegistrationSucceeded    semconv.Event = \"RegistrationSucceeded\"\n\tSessionChanged           semconv.Event = \"SessionChanged\"\n\tSessionChecked           semconv.Event = \"SessionChecked\"\n\tSessionIssued            semconv.Event = \"SessionIssued\"\n\tSessionLifespanExtended  semconv.Event = \"SessionLifespanExtended\"\n\tSessionRevoked           semconv.Event = \"SessionRevoked\"\n\tSessionTokenizedAsJWT    semconv.Event = \"SessionTokenizedAsJWT\"\n\tSettingsFailed           semconv.Event = \"SettingsFailed\"\n\tSettingsSucceeded        semconv.Event = \"SettingsSucceeded\"\n\tVerificationFailed       semconv.Event = \"VerificationFailed\"\n\tVerificationSucceeded    semconv.Event = \"VerificationSucceeded\"\n\tWebhookDelivered         semconv.Event = \"WebhookDelivered\"\n\tWebhookFailed            semconv.Event = \"WebhookFailed\"\n\tWebhookSucceeded         semconv.Event = \"WebhookSucceeded\"\n\tCourierMessageAbandoned  semconv.Event = \"CourierMessageAbandoned\"\n\tCourierMessageDispatched semconv.Event = \"CourierMessageDispatched\"\n)\n\nconst (\n\tAttributeKeyErrorReason                     semconv.AttributeKey = \"ErrorReason\"\n\tAttributeKeyFlowID                          semconv.AttributeKey = \"FlowID\"\n\tAttributeKeyFlowRefresh                     semconv.AttributeKey = \"FlowRefresh\"\n\tAttributeKeyFlowRequestedAAL                semconv.AttributeKey = \"FlowRequestedAAL\"\n\tAttributeKeyJsonnetInput                    semconv.AttributeKey = \"JsonnetInput\"\n\tAttributeKeyJsonnetOutput                   semconv.AttributeKey = \"JsonnetOutput\"\n\tAttributeKeyLoginRequestedAAL               semconv.AttributeKey = \"LoginRequestedAAL\"\n\tAttributeKeyLoginRequestedPrivilegedSession semconv.AttributeKey = \"LoginRequestedPrivilegedSession\"\n\tAttributeKeyOrganizationID                  semconv.AttributeKey = \"OrganizationID\"\n\tAttributeKeyReason                          semconv.AttributeKey = \"Reason\" // Deprecated, use AttributeKeyErrorReason\n\t// AttributeKeySelfServiceFlowType is the type of self-service flow, e.g. \"api\" or \"browser\".\n\tAttributeKeySelfServiceFlowType semconv.AttributeKey = \"SelfServiceFlowType\"\n\t// AttributeKeySelfServiceFlowName is the client used in the self-service flow, e.g. \"login\" or \"registration\"\n\tAttributeKeySelfServiceFlowName semconv.AttributeKey = \"SelfServiceFlowName\"\n\t// AttributeKeySelfServiceMethodUsed is the strategy used in the self-service flow, e.g. \"oidc\" or \"password\".\n\tAttributeKeySelfServiceMethodUsed      semconv.AttributeKey = \"SelfServiceMethodUsed\"\n\tAttributeKeySelfServiceSSOProviderUsed semconv.AttributeKey = \"SelfServiceSSOProviderUsed\"\n\t// AttributeKeySelfServiceStrategyUsed is the name of the Flow used in the self-service flow, e.g. \"login\" or \"registration\".\n\t//\n\t// Deprecated: use AttributeKeySelfServiceFlowName instead.\n\tAttributeKeySelfServiceStrategyUsed    semconv.AttributeKey = \"SelfServiceStrategyUsed\"\n\tAttributeKeySessionAAL                 semconv.AttributeKey = \"SessionAAL\"\n\tAttributeKeySessionExpiresAt           semconv.AttributeKey = \"SessionExpiresAt\"\n\tAttributeKeySessionID                  semconv.AttributeKey = \"SessionID\"\n\tAttributeKeyTokenizedSessionTTL        semconv.AttributeKey = \"TokenizedSessionTTL\"\n\tAttributeKeyWebhookAttemptNumber       semconv.AttributeKey = \"WebhookAttemptNumber\"\n\tAttributeKeyWebhookID                  semconv.AttributeKey = \"WebhookID\"\n\tAttributeKeyWebhookRequestBody         semconv.AttributeKey = \"WebhookRequestBody\"\n\tAttributeKeyWebhookRequestID           semconv.AttributeKey = \"WebhookRequestID\"\n\tAttributeKeyWebhookResponseBody        semconv.AttributeKey = \"WebhookResponseBody\"\n\tAttributeKeyWebhookResponseStatusCode  semconv.AttributeKey = \"WebhookResponseStatusCode\"\n\tAttributeKeyWebhookTriggerID           semconv.AttributeKey = \"WebhookTriggerID\"\n\tAttributeKeyWebhookURL                 semconv.AttributeKey = \"WebhookURL\"\n\tAttributeKeyCourierMessageID           semconv.AttributeKey = \"CourierMessageID\"\n\tAttributeKeyCourierMessageChannel      semconv.AttributeKey = \"CourierMessageChannel\"\n\tAttributeKeyCourierMessageTemplateType semconv.AttributeKey = \"CourierMessageTemplateType\"\n)\n\nfunc attrSessionID(val uuid.UUID) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeySessionID.String(), val.String())\n}\n\nfunc attrTokenizedSessionTTL(ttl time.Duration) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyTokenizedSessionTTL.String(), ttl.String())\n}\n\nfunc attrSessionAAL(val string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeySessionAAL.String(), val)\n}\n\nfunc attLoginRequestedAAL(val string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyLoginRequestedAAL.String(), val)\n}\n\nfunc attrFlowRefresh(val bool) otelattr.KeyValue {\n\treturn otelattr.Bool(AttributeKeyFlowRefresh.String(), val)\n}\n\nfunc attrOrganizationID(val string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyOrganizationID.String(), val)\n}\n\nfunc attrFlowRequestedAAL(val string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyFlowRequestedAAL.String(), val)\n}\n\nfunc attSessionExpiresAt(expiresAt time.Time) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeySessionExpiresAt.String(), expiresAt.String())\n}\n\nfunc attLoginRequestedPrivilegedSession(val bool) otelattr.KeyValue {\n\treturn otelattr.Bool(AttributeKeyLoginRequestedPrivilegedSession.String(), val)\n}\n\nfunc attrSelfServiceFlowType(val string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeySelfServiceFlowType.String(), val)\n}\n\nfunc attrSelfServiceMethodUsed(val string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeySelfServiceMethodUsed.String(), val)\n}\n\nfunc attrSelfServiceSSOProviderUsed(val string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeySelfServiceSSOProviderUsed.String(), val)\n}\n\nfunc attrWebhookID(id string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyWebhookID.String(), id)\n}\n\nfunc attrWebhookURL(URL *url.URL) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyWebhookURL.String(), URL.Redacted())\n}\n\nfunc attrWebhookReq(body []byte) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyWebhookRequestBody.String(), string(body))\n}\n\nfunc attrWebhookRes(body []byte) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyWebhookResponseBody.String(), string(body))\n}\n\nfunc attrWebhookStatus(status int) otelattr.KeyValue {\n\treturn otelattr.Int(AttributeKeyWebhookResponseStatusCode.String(), status)\n}\n\nfunc attrWebhookAttempt(n int) otelattr.KeyValue {\n\treturn otelattr.Int(AttributeKeyWebhookAttemptNumber.String(), n)\n}\n\nfunc attrWebhookRequestID(id uuid.UUID) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyWebhookRequestID.String(), id.String())\n}\n\nfunc attrWebhookTriggerID(id uuid.UUID) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyWebhookTriggerID.String(), id.String())\n}\n\n// deprecated: use attrErrorReason instead\nfunc attrReason(err error) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyReason.String(), reasonForError(err))\n}\n\nfunc attrErrorReason(err error) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyErrorReason.String(), reasonForError(err))\n}\n\nfunc attrJsonnetInput(in []byte) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyJsonnetInput.String(), string(jsonx.Anonymize(in)))\n}\n\nfunc attrJsonnetOutput(out string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyJsonnetOutput.String(), string(jsonx.Anonymize([]byte(out))))\n}\n\nfunc attrFlowID(id uuid.UUID) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyFlowID.String(), id.String())\n}\n\nfunc attrCourierMessageID(id uuid.UUID) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyCourierMessageID.String(), id.String())\n}\n\nfunc attrCourierMessageChannel(channel string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyCourierMessageChannel.String(), channel)\n}\n\nfunc attrCourierMessageTemplateType(templateType string) otelattr.KeyValue {\n\treturn otelattr.String(AttributeKeyCourierMessageTemplateType.String(), templateType)\n}\n\nfunc NewSessionIssued(ctx context.Context, aal string, sessionID, identityID uuid.UUID) (string, trace.EventOption) {\n\treturn SessionIssued.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\t\tattrSessionID(sessionID),\n\t\t\t\tattrSessionAAL(aal),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewSessionChanged(ctx context.Context, aal string, sessionID, identityID uuid.UUID) (string, trace.EventOption) {\n\treturn SessionChanged.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\t\tattrSessionID(sessionID),\n\t\t\t\tattrSessionAAL(aal),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewSessionLifespanExtended(ctx context.Context, sessionID, identityID uuid.UUID, newExpiry time.Time) (string, trace.EventOption) {\n\treturn SessionLifespanExtended.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\t\tattrSessionID(sessionID),\n\t\t\t\tattSessionExpiresAt(newExpiry),\n\t\t\t)...,\n\t\t)\n}\n\ntype LoginSucceededOpts struct {\n\tSessionID, IdentityID, FlowID               uuid.UUID\n\tFlowType, RequestedAAL, Method, SSOProvider string\n\tIsRefresh                                   bool\n}\n\nfunc NewLoginSucceeded(ctx context.Context, o *LoginSucceededOpts) (string, trace.EventOption) {\n\treturn LoginSucceeded.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(o.IdentityID),\n\t\t\t\tattrSessionID(o.SessionID),\n\t\t\t\tattrSelfServiceFlowType(o.FlowType),\n\t\t\t\tattLoginRequestedAAL(o.RequestedAAL),\n\t\t\t\tattLoginRequestedPrivilegedSession(o.IsRefresh),\n\t\t\t\tattrSelfServiceMethodUsed(o.Method),\n\t\t\t\tattrSelfServiceSSOProviderUsed(o.SSOProvider),\n\t\t\t\tattrFlowID(o.FlowID),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewRegistrationSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method, provider string) (string, trace.EventOption) {\n\treturn RegistrationSucceeded.String(),\n\t\ttrace.WithAttributes(append(\n\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\tattrSelfServiceFlowType(flowType),\n\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\tattrSelfServiceMethodUsed(method),\n\t\t\tattrSelfServiceSSOProviderUsed(provider),\n\t\t\tattrFlowID(flowID),\n\t\t)...)\n}\n\nfunc NewRecoverySucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) {\n\treturn RecoverySucceeded.String(),\n\t\ttrace.WithAttributes(append(\n\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\tattrSelfServiceFlowType(flowType),\n\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\tattrSelfServiceMethodUsed(method),\n\t\t\tattrFlowID(flowID),\n\t\t)...)\n}\n\nfunc NewRecoveryInitiatedByAdmin(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) {\n\treturn RecoveryInitiatedByAdmin.String(),\n\t\ttrace.WithAttributes(append(\n\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\tattrSelfServiceFlowType(flowType),\n\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\tattrSelfServiceMethodUsed(method),\n\t\t\tattrFlowID(flowID),\n\t\t)...)\n}\n\nfunc NewSettingsSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) {\n\treturn SettingsSucceeded.String(),\n\t\ttrace.WithAttributes(append(\n\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\tattrSelfServiceFlowType(flowType),\n\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\tattrSelfServiceMethodUsed(method),\n\t\t\tattrFlowID(flowID),\n\t\t)...)\n}\n\nfunc NewVerificationSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) {\n\treturn VerificationSucceeded.String(),\n\t\ttrace.WithAttributes(append(\n\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\tattrSelfServiceMethodUsed(method),\n\t\t\tattrSelfServiceFlowType(flowType),\n\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\tattrFlowID(flowID),\n\t\t)...)\n}\n\nfunc NewRegistrationFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) {\n\treturn RegistrationFailed.String(),\n\t\ttrace.WithAttributes(append(\n\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\tattrSelfServiceFlowType(flowType),\n\t\t\tattrSelfServiceMethodUsed(method),\n\t\t\tattrReason(err),\n\t\t\tattrErrorReason(err),\n\t\t\tattrFlowID(flowID),\n\t\t)...)\n}\n\nfunc NewRecoveryFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) {\n\tattrs := append(\n\t\tsemconv.AttributesFromContext(ctx),\n\t\tattrSelfServiceFlowType(flowType),\n\t\tattrSelfServiceMethodUsed(method),\n\t\tattrReason(err),\n\t\tattrErrorReason(err),\n\t\tattrFlowID(flowID),\n\t)\n\n\tvar identityIDError *x.WithIdentityIDError\n\tif errors.As(err, &identityIDError) {\n\t\tattrs = append(attrs, semconv.AttrIdentityID(identityIDError.IdentityID()))\n\t}\n\n\treturn RecoveryFailed.String(), trace.WithAttributes(attrs...)\n}\n\nfunc NewSettingsFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) {\n\tattrs := append(\n\t\tsemconv.AttributesFromContext(ctx),\n\t\tattrSelfServiceFlowType(flowType),\n\t\tattrSelfServiceMethodUsed(method),\n\t\tattrReason(err),\n\t\tattrErrorReason(err),\n\t\tattrFlowID(flowID),\n\t)\n\n\tvar identityIDError *x.WithIdentityIDError\n\tif errors.As(err, &identityIDError) {\n\t\tattrs = append(attrs, semconv.AttrIdentityID(identityIDError.IdentityID()))\n\t}\n\n\treturn SettingsFailed.String(), trace.WithAttributes(attrs...)\n}\n\nfunc NewVerificationFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) {\n\tattrs := append(\n\t\tsemconv.AttributesFromContext(ctx),\n\t\tattrSelfServiceFlowType(flowType),\n\t\tattrSelfServiceMethodUsed(method),\n\t\tattrReason(err),\n\t\tattrErrorReason(err),\n\t\tattrFlowID(flowID),\n\t)\n\n\tvar identityIDError *x.WithIdentityIDError\n\tif errors.As(err, &identityIDError) {\n\t\tattrs = append(attrs, semconv.AttrIdentityID(identityIDError.IdentityID()))\n\t}\n\n\treturn VerificationFailed.String(),\n\t\ttrace.WithAttributes(attrs...)\n}\n\nfunc NewIdentityCreated(ctx context.Context, identityID uuid.UUID) (string, trace.EventOption) {\n\treturn IdentityCreated.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewIdentityDeleted(ctx context.Context, identityID uuid.UUID) (string, trace.EventOption) {\n\treturn IdentityDeleted.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewIdentityUpdated(ctx context.Context, identityID uuid.UUID) (string, trace.EventOption) {\n\treturn IdentityUpdated.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewLoginFailed(ctx context.Context, flowID uuid.UUID, flowType, method, requestedAAL string, isRefresh bool, err error) (string, trace.EventOption) {\n\tattrs := append(\n\t\tsemconv.AttributesFromContext(ctx),\n\t\tattrSelfServiceFlowType(flowType),\n\t\tattLoginRequestedAAL(requestedAAL),\n\t\tattLoginRequestedPrivilegedSession(isRefresh),\n\t\tattrSelfServiceMethodUsed(method),\n\t\tattrReason(err),\n\t\tattrErrorReason(err),\n\t\tattrFlowID(flowID),\n\t)\n\n\tvar identityIDError *x.WithIdentityIDError\n\tif errors.As(err, &identityIDError) {\n\t\tattrs = append(attrs, semconv.AttrIdentityID(identityIDError.IdentityID()))\n\t}\n\n\treturn LoginFailed.String(), trace.WithAttributes(attrs...)\n}\n\nfunc NewSessionRevoked(ctx context.Context, sessionID, identityID uuid.UUID) (string, trace.EventOption) {\n\treturn SessionRevoked.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\t\tattrSessionID(sessionID),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewSessionChecked(ctx context.Context, sessionID, identityID uuid.UUID) (string, trace.EventOption) {\n\treturn SessionChecked.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\t\tattrSessionID(sessionID),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewSessionJWTIssued(ctx context.Context, sessionID, identityID uuid.UUID, ttl time.Duration) (string, trace.EventOption) {\n\treturn SessionTokenizedAsJWT.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tsemconv.AttrIdentityID(identityID),\n\t\t\t\tattrSessionID(sessionID),\n\t\t\t\tattrTokenizedSessionTTL(ttl),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewWebhookDelivered(ctx context.Context, URL *url.URL, reqBody []byte, status int, resBody []byte, attempt int, requestID, triggerID uuid.UUID, webhookID string) (string, trace.EventOption) {\n\treturn WebhookDelivered.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tattrWebhookReq(reqBody),\n\t\t\t\tattrWebhookRes(resBody),\n\t\t\t\tattrWebhookStatus(status),\n\t\t\t\tattrWebhookURL(URL),\n\t\t\t\tattrWebhookAttempt(attempt),\n\t\t\t\tattrWebhookRequestID(requestID),\n\t\t\t\tattrWebhookID(webhookID),\n\t\t\t\tattrWebhookTriggerID(triggerID),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewWebhookSucceeded(ctx context.Context, triggerID uuid.UUID, webhookID string) (string, trace.EventOption) {\n\treturn WebhookSucceeded.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tattrWebhookID(webhookID),\n\t\t\t\tattrWebhookTriggerID(triggerID),\n\t\t\t)...)\n}\n\nfunc NewWebhookFailed(ctx context.Context, err error, triggerID uuid.UUID, id string) (string, trace.EventOption) {\n\treturn WebhookFailed.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tattrWebhookID(id),\n\t\t\t\tattrWebhookTriggerID(triggerID),\n\t\t\t\totelattr.String(\"Error\", err.Error()),\n\t\t\t\tattrErrorReason(err),\n\t\t\t)...,\n\t\t)\n}\n\n// NewJsonnetMappingFailed is used to log errors that occur during the Jsonnet\n// mapping process. The jsonnetInput and jsonnetOutput is anonymized before\n// emitting the event.\nfunc NewJsonnetMappingFailed(ctx context.Context, err error, jsonnetInput []byte, jsonnetOutput, provider string, method string) (string, trace.EventOption) {\n\tattrs := append(\n\t\tsemconv.AttributesFromContext(ctx),\n\t\tattrErrorReason(err),\n\t\tattrJsonnetInput(jsonnetInput),\n\t\tattrSelfServiceSSOProviderUsed(provider),\n\t\tattrSelfServiceMethodUsed(method),\n\t)\n\tif jsonnetOutput != \"\" {\n\t\tattrs = append(attrs, attrJsonnetOutput(jsonnetOutput))\n\t}\n\treturn JsonnetMappingFailed.String(),\n\t\ttrace.WithAttributes(\n\t\t\tattrs...,\n\t\t)\n}\n\nfunc NewLoginInitiated(ctx context.Context, flowID uuid.UUID, flowType string, refresh bool, organizationID uuid.NullUUID, requestedAAL string) (string, trace.EventOption) {\n\tattrs := append(semconv.AttributesFromContext(ctx),\n\t\tattrFlowID(flowID),\n\t\tattrSelfServiceFlowType(flowType),\n\t\tattrFlowRefresh(refresh),\n\t\tattrFlowRequestedAAL(requestedAAL),\n\t)\n\n\tif organizationID.Valid {\n\t\tattrs = append(attrs,\n\t\t\tattrOrganizationID(organizationID.UUID.String()))\n\t}\n\n\treturn LoginInitiated.String(),\n\t\ttrace.WithAttributes(attrs...)\n}\n\nfunc NewRegistrationInitiated(ctx context.Context, flowID uuid.UUID, flowType string, organizationID uuid.NullUUID) (string, trace.EventOption) {\n\tattrs := append(semconv.AttributesFromContext(ctx),\n\t\tattrFlowID(flowID),\n\t\tattrSelfServiceFlowType(flowType),\n\t)\n\n\tif organizationID.Valid {\n\t\tattrs = append(attrs,\n\t\t\tattrOrganizationID(organizationID.UUID.String()))\n\t}\n\n\treturn RegistrationInitiated.String(),\n\t\ttrace.WithAttributes(attrs...)\n}\n\nfunc reasonForError(err error) string {\n\tif ve := new(schema.ValidationError); errors.As(err, &ve) {\n\t\treturn ve.Message\n\t}\n\tif r := *new(herodot.ReasonCarrier); errors.As(err, &r) {\n\t\treturn r.Reason()\n\t}\n\treturn err.Error()\n}\n\nfunc NewCourierMessageAbandoned(ctx context.Context, messageID uuid.UUID, channel string, templateType string) (string, trace.EventOption) {\n\treturn CourierMessageAbandoned.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tattrCourierMessageID(messageID),\n\t\t\t\tattrCourierMessageChannel(channel),\n\t\t\t\tattrCourierMessageTemplateType(templateType),\n\t\t\t)...,\n\t\t)\n}\n\nfunc NewCourierMessageDispatched(ctx context.Context, messageID uuid.UUID, channel string, templateType string) (string, trace.EventOption) {\n\treturn CourierMessageDispatched.String(),\n\t\ttrace.WithAttributes(\n\t\t\tappend(\n\t\t\t\tsemconv.AttributesFromContext(ctx),\n\t\t\t\tattrCourierMessageID(messageID),\n\t\t\t\tattrCourierMessageChannel(channel),\n\t\t\t\tattrCourierMessageTemplateType(templateType),\n\t\t\t)...,\n\t\t)\n}\n"
  },
  {
    "path": "x/events/events_test.go",
    "content": "// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage events_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/events\"\n)\n\nfunc TestNewJsonnetMappingFailed(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\terr           error\n\t\tjsonnetInput  []byte\n\t\tjsonnetOutput string\n\t\tprovider      string\n\t\tmethod        identity.CredentialsType\n\t\texpectedAttrs []attribute.KeyValue\n\t}{\n\t\t{\n\t\t\tname:          \"With all attributes\",\n\t\t\terr:           errors.New(\"test error\"),\n\t\t\tjsonnetInput:  []byte(`{\"key\": \"PII value\"}`),\n\t\t\tjsonnetOutput: `{\"key\": 123}`,\n\t\t\tprovider:      \"test-provider\",\n\t\t\tmethod:        identity.CredentialsTypeOIDC,\n\t\t\texpectedAttrs: []attribute.KeyValue{\n\t\t\t\tattribute.String(\"SelfServiceSSOProviderUsed\", \"test-provider\"),\n\t\t\t\tattribute.String(\"SelfServiceMethodUsed\", \"oidc\"),\n\t\t\t\tattribute.String(\"ErrorReason\", \"test error\"),\n\t\t\t\tattribute.String(\"JsonnetInput\", `{\n  \"key\": \"string\"\n}`),\n\t\t\t\tattribute.String(\"JsonnetOutput\", `{\n  \"key\": \"number\"\n}`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:          \"Without JsonnetOutput\",\n\t\t\terr:           errors.New(\"another error\"),\n\t\t\tjsonnetInput:  []byte(`{\"key\": \"PII value\"}`),\n\t\t\tjsonnetOutput: \"\",\n\t\t\tprovider:      \"another-provider\",\n\t\t\tmethod:        identity.CredentialsTypeSAML,\n\t\t\texpectedAttrs: []attribute.KeyValue{\n\t\t\t\tattribute.String(\"SelfServiceSSOProviderUsed\", \"another-provider\"),\n\t\t\t\tattribute.String(\"SelfServiceMethodUsed\", \"saml\"),\n\t\t\t\tattribute.String(\"ErrorReason\", \"another error\"),\n\t\t\t\tattribute.String(\"JsonnetInput\", `{\n  \"key\": \"string\"\n}`),\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\tctx := t.Context()\n\t\t\teventName, opts := events.NewJsonnetMappingFailed(ctx, tt.err, tt.jsonnetInput, tt.jsonnetOutput, tt.provider, tt.method.String())\n\n\t\t\tassert.Equal(t, events.JsonnetMappingFailed.String(), eventName)\n\n\t\t\teventConfig := trace.NewEventConfig(opts)\n\t\t\tassert.ElementsMatch(t, tt.expectedAttrs, eventConfig.Attributes())\n\t\t})\n\t}\n}\n\nfunc TestNewLoginFailed(t *testing.T) {\n\tctx := t.Context()\n\tflowID := uuid.Must(uuid.NewV4())\n\tidentityID := uuid.Must(uuid.NewV4())\n\tbaseErr := errors.New(\"login failed\")\n\n\tt.Run(\"case=without identity ID\", func(t *testing.T) {\n\t\teventName, opts := events.NewLoginFailed(ctx, flowID, \"browser\", \"password\", \"aal1\", false, baseErr)\n\n\t\tassert.Equal(t, events.LoginFailed.String(), eventName)\n\n\t\teventConfig := trace.NewEventConfig(opts)\n\t\tattrs := eventConfig.Attributes()\n\n\t\t// Should not contain IdentityID attribute\n\t\tfor _, attr := range attrs {\n\t\t\tassert.NotEqual(t, \"IdentityID\", string(attr.Key))\n\t\t}\n\n\t\t// Should contain other attributes\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceFlowType\", \"browser\"))\n\t\tassert.Contains(t, attrs, attribute.String(\"LoginRequestedAAL\", \"aal1\"))\n\t\tassert.Contains(t, attrs, attribute.Bool(\"LoginRequestedPrivilegedSession\", false))\n\t\tassert.Contains(t, attrs, attribute.String(\"ErrorReason\", \"login failed\"))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceMethodUsed\", \"password\"))\n\t})\n\n\tt.Run(\"case=with identity ID\", func(t *testing.T) {\n\t\twrappedErr := x.WrapWithIdentityIDError(baseErr, identityID)\n\t\teventName, opts := events.NewLoginFailed(ctx, flowID, \"browser\", \"password\", \"aal1\", false, wrappedErr)\n\n\t\tassert.Equal(t, events.LoginFailed.String(), eventName)\n\n\t\teventConfig := trace.NewEventConfig(opts)\n\t\tattrs := eventConfig.Attributes()\n\n\t\tassert.Contains(t, attrs, attribute.String(\"IdentityID\", identityID.String()))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceFlowType\", \"browser\"))\n\t\tassert.Contains(t, attrs, attribute.String(\"ErrorReason\", \"login failed\"))\n\t})\n}\n\nfunc TestNewRecoveryFailed(t *testing.T) {\n\tctx := t.Context()\n\tflowID := uuid.Must(uuid.NewV4())\n\tidentityID := uuid.Must(uuid.NewV4())\n\tbaseErr := errors.New(\"recovery failed\")\n\n\tt.Run(\"case=without identity ID\", func(t *testing.T) {\n\t\teventName, opts := events.NewRecoveryFailed(ctx, flowID, \"browser\", \"code\", baseErr)\n\n\t\tassert.Equal(t, events.RecoveryFailed.String(), eventName)\n\n\t\teventConfig := trace.NewEventConfig(opts)\n\t\tattrs := eventConfig.Attributes()\n\n\t\tfor _, attr := range attrs {\n\t\t\tassert.NotEqual(t, \"IdentityID\", string(attr.Key))\n\t\t}\n\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceFlowType\", \"browser\"))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceMethodUsed\", \"code\"))\n\t})\n\n\tt.Run(\"case=with identity ID\", func(t *testing.T) {\n\t\twrappedErr := x.WrapWithIdentityIDError(baseErr, identityID)\n\t\teventName, opts := events.NewRecoveryFailed(ctx, flowID, \"browser\", \"link\", wrappedErr)\n\n\t\tassert.Equal(t, events.RecoveryFailed.String(), eventName)\n\n\t\teventConfig := trace.NewEventConfig(opts)\n\t\tattrs := eventConfig.Attributes()\n\n\t\tassert.Contains(t, attrs, attribute.String(\"IdentityID\", identityID.String()))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceFlowType\", \"browser\"))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceMethodUsed\", \"link\"))\n\t})\n}\n\nfunc TestNewSettingsFailed(t *testing.T) {\n\tctx := t.Context()\n\tflowID := uuid.Must(uuid.NewV4())\n\tidentityID := uuid.Must(uuid.NewV4())\n\tbaseErr := errors.New(\"settings failed\")\n\n\tt.Run(\"case=without identity ID\", func(t *testing.T) {\n\t\teventName, opts := events.NewSettingsFailed(ctx, flowID, \"browser\", \"profile\", baseErr)\n\n\t\tassert.Equal(t, events.SettingsFailed.String(), eventName)\n\n\t\teventConfig := trace.NewEventConfig(opts)\n\t\tattrs := eventConfig.Attributes()\n\n\t\tfor _, attr := range attrs {\n\t\t\tassert.NotEqual(t, \"IdentityID\", string(attr.Key))\n\t\t}\n\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceFlowType\", \"browser\"))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceMethodUsed\", \"profile\"))\n\t})\n\n\tt.Run(\"case=with identity ID\", func(t *testing.T) {\n\t\twrappedErr := x.WrapWithIdentityIDError(baseErr, identityID)\n\t\teventName, opts := events.NewSettingsFailed(ctx, flowID, \"browser\", \"password\", wrappedErr)\n\n\t\tassert.Equal(t, events.SettingsFailed.String(), eventName)\n\n\t\teventConfig := trace.NewEventConfig(opts)\n\t\tattrs := eventConfig.Attributes()\n\n\t\tassert.Contains(t, attrs, attribute.String(\"IdentityID\", identityID.String()))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceFlowType\", \"browser\"))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceMethodUsed\", \"password\"))\n\t})\n}\n\nfunc TestNewVerificationFailed(t *testing.T) {\n\tctx := t.Context()\n\tflowID := uuid.Must(uuid.NewV4())\n\tidentityID := uuid.Must(uuid.NewV4())\n\tbaseErr := errors.New(\"verification failed\")\n\n\tt.Run(\"case=without identity ID\", func(t *testing.T) {\n\t\teventName, opts := events.NewVerificationFailed(ctx, flowID, \"browser\", \"code\", baseErr)\n\n\t\tassert.Equal(t, events.VerificationFailed.String(), eventName)\n\n\t\teventConfig := trace.NewEventConfig(opts)\n\t\tattrs := eventConfig.Attributes()\n\n\t\tfor _, attr := range attrs {\n\t\t\tassert.NotEqual(t, \"IdentityID\", string(attr.Key))\n\t\t}\n\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceFlowType\", \"browser\"))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceMethodUsed\", \"code\"))\n\t})\n\n\tt.Run(\"case=with identity ID\", func(t *testing.T) {\n\t\twrappedErr := x.WrapWithIdentityIDError(baseErr, identityID)\n\t\teventName, opts := events.NewVerificationFailed(ctx, flowID, \"browser\", \"link\", wrappedErr)\n\n\t\tassert.Equal(t, events.VerificationFailed.String(), eventName)\n\n\t\teventConfig := trace.NewEventConfig(opts)\n\t\tattrs := eventConfig.Attributes()\n\n\t\tassert.Contains(t, attrs, attribute.String(\"IdentityID\", identityID.String()))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceFlowType\", \"browser\"))\n\t\tassert.Contains(t, attrs, attribute.String(\"SelfServiceMethodUsed\", \"link\"))\n\t})\n}\n"
  },
  {
    "path": "x/fetcher.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport \"github.com/ory/x/jwksx\"\n\ntype JWKSFetchProvider interface {\n\tJWKSFetcher() *jwksx.FetcherNext\n}\n"
  },
  {
    "path": "x/http.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/golang/gddo/httputil\"\n\n\t\"github.com/ory/herodot\"\n)\n\ntype ctxKey struct{}\n\nvar baseURLKey ctxKey\n\nfunc WithBaseURL(ctx context.Context, baseURL *url.URL) context.Context {\n\tif baseURL == nil {\n\t\treturn ctx\n\t}\n\tbaseURL.Scheme = \"https\" // Force https\n\treturn context.WithValue(ctx, baseURLKey, baseURL)\n}\n\nfunc BaseURLFromContext(ctx context.Context) *url.URL {\n\tif ctx == nil {\n\t\treturn nil\n\t}\n\tif v := ctx.Value(baseURLKey); v != nil {\n\t\tif u, ok := v.(*url.URL); ok {\n\t\t\treturn u\n\t\t}\n\t}\n\treturn nil\n}\n\n// FlowBaseURL returns the base URL to be used for a self-service flow. It will\n// either take the request URL, or an explicit base URL set in the context.\nfunc FlowBaseURL(ctx context.Context, flow interface{ GetRequestURL() string }) (*url.URL, error) {\n\tif u := BaseURLFromContext(ctx); u != nil {\n\t\treturn u, nil\n\t}\n\tu, err := url.Parse(flow.GetRequestURL())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tu.Path = \"/\"\n\n\treturn u, nil\n}\n\nfunc RequestURL(r *http.Request) *url.URL {\n\tsource := *r.URL\n\tsource.Host = cmp.Or(source.Host, r.Header.Get(\"X-Forwarded-Host\"), r.Host)\n\n\tif proto := r.Header.Get(\"X-Forwarded-Proto\"); len(proto) > 0 {\n\t\tsource.Scheme = proto\n\t}\n\n\tif source.Scheme == \"\" {\n\t\tsource.Scheme = \"https\"\n\t\tif r.TLS == nil {\n\t\t\tsource.Scheme = \"http\"\n\t\t}\n\t}\n\n\treturn &source\n}\n\n// SendFlowCompletedAsRedirectOrJSON should be used when a login, registration, ... flow has been completed successfully.\n// It will redirect the user to the provided URL if the request accepts HTML, or return a JSON response if the request is\n// an SPA request\nfunc SendFlowCompletedAsRedirectOrJSON(\n\tw http.ResponseWriter, r *http.Request, writer herodot.Writer, out interface{}, redirectTo string,\n) {\n\tsendFlowAsRedirectOrJSON(w, r, writer, out, redirectTo, http.StatusOK)\n}\n\n// SendFlowErrorAsRedirectOrJSON should be used when a login, registration, ... flow has errors (e.g. validation errors\n// or missing data) and should be redirected to the provided URL if the request accepts HTML, or return a JSON response\n// if the request is an SPA request.\nfunc SendFlowErrorAsRedirectOrJSON(\n\tw http.ResponseWriter, r *http.Request, writer herodot.Writer, out interface{}, redirectTo string,\n) {\n\tsendFlowAsRedirectOrJSON(w, r, writer, out, redirectTo, http.StatusBadRequest)\n}\n\nfunc sendFlowAsRedirectOrJSON(\n\tw http.ResponseWriter, r *http.Request, writer herodot.Writer, out interface{}, redirectTo string, jsonResponseCode int,\n) {\n\tswitch httputil.NegotiateContentType(r, []string{\n\t\t\"text/html\",\n\t\t\"application/json\",\n\t}, \"text/html\") {\n\tcase \"application/json\":\n\t\tif err, ok := out.(error); ok {\n\t\t\twriter.WriteError(w, r, err)\n\t\t\treturn\n\t\t}\n\n\t\twriter.WriteCode(w, r, jsonResponseCode, out)\n\tcase \"text/html\":\n\t\tfallthrough\n\tdefault:\n\t\thttp.Redirect(w, r, redirectTo, http.StatusSeeOther)\n\t}\n}\n\nfunc AcceptsJSON(r *http.Request) bool {\n\treturn httputil.NegotiateContentType(r, []string{\n\t\t\"text/html\",\n\t\t\"application/json\",\n\t}, \"text/html\") == \"application/json\"\n}\n"
  },
  {
    "path": "x/http_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/assertx\"\n\t\"github.com/ory/x/logrusx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/ory/x/urlx\"\n)\n\nfunc TestWithBaseURL(t *testing.T) {\n\tctx := WithBaseURL(context.Background(), urlx.ParseOrPanic(\"https://www.ory.sh/\"))\n\tassert.EqualValues(t, \"https://www.ory.sh/\", BaseURLFromContext(ctx).String())\n\tassert.Nil(t, BaseURLFromContext(context.Background()))\n}\n\nfunc TestRequestURL(t *testing.T) {\n\tassert.EqualValues(t, RequestURL(&http.Request{\n\t\tURL: urlx.ParseOrPanic(\"/foo\"), Host: \"foobar\", TLS: &tls.ConnectionState{},\n\t}).String(), \"https://foobar/foo\")\n\tassert.EqualValues(t, RequestURL(&http.Request{\n\t\tURL: urlx.ParseOrPanic(\"/foo\"), Host: \"foobar\",\n\t}).String(), \"http://foobar/foo\")\n\tassert.EqualValues(t, RequestURL(&http.Request{\n\t\tURL: urlx.ParseOrPanic(\"/foo\"), Host: \"foobar\", Header: http.Header{\"X-Forwarded-Host\": []string{\"notfoobar\"}, \"X-Forwarded-Proto\": {\"https\"}},\n\t}).String(), \"https://notfoobar/foo\")\n}\n\nfunc TestAcceptToRedirectOrJSON(t *testing.T) {\n\twr := herodot.NewJSONWriter(logrusx.New(\"\", \"\"))\n\n\tt.Run(\"case=browser\", func(t *testing.T) {\n\t\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\tr.Header.Set(\"Accept\", \"text/html\")\n\n\t\tt.Run(\"regular payload\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\t\t\tSendFlowCompletedAsRedirectOrJSON(w, r, wr, json.RawMessage(`{\"foo\":\"bar\"}`), \"https://www.ory.sh/redir\")\n\t\t\tloc, err := w.Result().Location()\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"https://www.ory.sh/redir\", loc.String())\n\t\t})\n\n\t\tt.Run(\"error payload\", func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\t\t\tSendFlowCompletedAsRedirectOrJSON(w, r, wr, errors.New(\"foo\"), \"https://www.ory.sh/redir\")\n\t\t\tloc, err := w.Result().Location()\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, \"https://www.ory.sh/redir\", loc.String())\n\t\t})\n\t})\n\n\tt.Run(\"case=json\", func(t *testing.T) {\n\t\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\tr.Header.Set(\"Accept\", \"application/json\")\n\n\t\tt.Run(\"regular payload\", func(t *testing.T) {\n\t\t\tmsg := json.RawMessage(`{\"foo\":\"bar\"}`)\n\t\t\tw := httptest.NewRecorder()\n\t\t\tSendFlowCompletedAsRedirectOrJSON(w, r, wr, msg, \"https://www.ory.sh/redir\")\n\t\t\t_, err := w.Result().Location()\n\t\t\trequire.ErrorIs(t, err, http.ErrNoLocation)\n\n\t\t\tbody := MustReadAll(w.Result().Body)\n\t\t\tassertx.EqualAsJSON(t, msg, json.RawMessage(body))\n\t\t})\n\n\t\tt.Run(\"error payload\", func(t *testing.T) {\n\t\t\tee := errors.WithStack(herodot.ErrBadRequest)\n\t\t\tw := httptest.NewRecorder()\n\t\t\tSendFlowCompletedAsRedirectOrJSON(w, r, wr, ee, \"https://www.ory.sh/redir\")\n\t\t\t_, err := w.Result().Location()\n\t\t\trequire.ErrorIs(t, err, http.ErrNoLocation)\n\n\t\t\tbody := MustReadAll(w.Result().Body)\n\t\t\tassertx.EqualAsJSON(t, map[string]interface{}{\"error\": map[string]interface{}{\"code\": 400, \"message\": \"The request was malformed or contained invalid parameters\", \"status\": \"Bad Request\"}}, json.RawMessage(body))\n\t\t\tassert.EqualValues(t, http.StatusBadRequest, w.Result().StatusCode)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "x/httploadermiddleware.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/urfave/negroni\"\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/ory/jsonschema/v3/httploader\"\n\t\"github.com/ory/x/httpx\"\n)\n\nfunc HTTPLoaderContextMiddleware(reg httpx.ClientProvider) negroni.HandlerFunc {\n\treturn func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\thc := reg.HTTPClient(r.Context())\n\t\tctx := context.WithValue(r.Context(), oauth2.HTTPClient, hc)\n\t\tctx = context.WithValue(ctx, httploader.ContextKey, hc)\n\t\tnext(rw, r.WithContext(ctx))\n\t}\n}\n"
  },
  {
    "path": "x/ider.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\ntype Ider interface {\n\tRequestID() string\n}\n"
  },
  {
    "path": "x/isjsonrequest.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/golang/gddo/httputil\"\n)\n\nvar offers = []string{\"text/html\", \"text/*\", \"*/*\", \"application/json\"}\nvar defaultOffer = \"text/html\"\n\nfunc IsJSONRequest(r *http.Request) bool {\n\treturn httputil.NegotiateContentType(r, offers, defaultOffer) == \"application/json\" ||\n\t\tr.Header.Get(\"Content-Type\") == \"application/json\"\n}\n\nfunc IsBrowserRequest(r *http.Request) bool {\n\treturn httputil.NegotiateContentType(r, offers, defaultOffer) == \"text/html\"\n}\n"
  },
  {
    "path": "x/isjsonrequest_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/golang/gddo/httputil\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestIsBrowserOrAPIRequest(t *testing.T) {\n\tfor k, tc := range []struct {\n\t\tua string\n\t\th  string\n\t\te  bool\n\t}{\n\t\t{ua: \"firefox-66\", h: \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\", e: true},\n\t\t{ua: \"safari-chrome\", h: \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\", e: true},\n\t\t{ua: \"ie8\", h: \"image/jpeg,application/x-ms-application,image/gif,application/xaml+xml,image/pjpeg,application/x-ms-xbap,application/x-shockwave-flash,application/msword,*/*\", e: true},\n\t\t{ua: \"ie8-any\", h: \"*/*\", e: true},\n\t\t{ua: \"edge\", h: \"text/html,application/xhtml+xml,image/jxr,*/*\", e: true},\n\t\t{ua: \"opera\", h: \"text/html,application/xml;q=0.9,application/xhtml+xml,image/png,image/webp,image/jpeg,image/gif,image/x-xbitmap,*/*;q=0.1\", e: true},\n\t\t{ua: \"json-api\", h: \"application/json\", e: false},\n\t\t{ua: \"no-accept\", h: \"\", e: true},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case=%d/ua=%s\", k, tc.ua), func(t *testing.T) {\n\t\t\tr := &http.Request{Header: map[string][]string{\"Accept\": {tc.h}}}\n\t\t\tt.Logf(\"isBrowser: %s\", httputil.NegotiateContentType(r, offers, defaultOffer))\n\n\t\t\tt.Logf(\"isJSON: %s\", httputil.NegotiateContentType(r,\n\t\t\t\t[]string{\"application/json\"},\n\t\t\t\t\"text/html\",\n\t\t\t))\n\n\t\t\tassert.Equal(t, tc.e, IsBrowserRequest(r))\n\t\t\tassert.Equal(t, !tc.e, IsJSONRequest(r))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/json_bool.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"fmt\"\n)\n\n// ConvertibleBoolean can unmarshal both booleans and strings.\ntype ConvertibleBoolean bool\n\nfunc (bit *ConvertibleBoolean) UnmarshalJSON(data []byte) error {\n\tasString := string(data)\n\tswitch asString {\n\tcase \"true\", `\"true\"`:\n\t\t*bit = true\n\tcase \"false\", `\"false\"`:\n\t\t*bit = false\n\tdefault:\n\t\treturn fmt.Errorf(\"boolean unmarshal error: invalid input %s\", asString)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/json_bool_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype ConvertibleBooleanTest struct {\n\tVerified ConvertibleBoolean `json:\"verified,omitempty\"`\n}\n\nfunc TestUnmarshalBool(t *testing.T) {\n\tdata := `{\"verified\":true}`\n\tc := ConvertibleBooleanTest{}\n\terr := json.Unmarshal([]byte(data), &c)\n\trequire.NoError(t, err)\n\tassert.Equal(t, ConvertibleBoolean(true), c.Verified)\n\n\tdata = `{\"verified\":false}`\n\terr = json.Unmarshal([]byte(data), &c)\n\trequire.NoError(t, err)\n\tassert.Equal(t, ConvertibleBoolean(false), c.Verified)\n}\n\nfunc TestUnmarshalString(t *testing.T) {\n\tdata := `{\"verified\":\"true\"}`\n\tc := ConvertibleBooleanTest{}\n\terr := json.Unmarshal([]byte(data), &c)\n\trequire.NoError(t, err)\n\tassert.Equal(t, ConvertibleBoolean(true), c.Verified)\n\n\tdata = `{\"verified\":\"false\"}`\n\terr = json.Unmarshal([]byte(data), &c)\n\trequire.NoError(t, err)\n\tassert.Equal(t, ConvertibleBoolean(false), c.Verified)\n}\n\nfunc TestUnmarshalError(t *testing.T) {\n\tdata := `{\"verified\":1}`\n\tc := ConvertibleBooleanTest{}\n\terr := json.Unmarshal([]byte(data), &c)\n\tassert.Error(t, err)\n}\n"
  },
  {
    "path": "x/json_marshal.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport \"encoding/json\"\n\n// ParseRawMessageOrEmpty parses a json.RawMessage and returns an empty map if the input is empty.\nfunc ParseRawMessageOrEmpty(input json.RawMessage) (map[string]interface{}, error) {\n\tif len(input) == 0 {\n\t\treturn map[string]interface{}{}, nil\n\t}\n\tvar m map[string]interface{}\n\tif err := json.Unmarshal(input, &m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n"
  },
  {
    "path": "x/json_marshal_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x_test\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestParseRawMessageOrEmpty(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\tinput  json.RawMessage\n\t\texpect map[string]interface{}\n\t\terr    any\n\t}{\n\t\t{\n\t\t\tinput: json.RawMessage(\"invalid json\"),\n\t\t\terr:   \"invalid character 'i' looking for beginning of value\",\n\t\t},\n\t\t{\n\t\t\tinput:  json.RawMessage(\"\"),\n\t\t\texpect: map[string]interface{}{},\n\t\t},\n\t\t{\n\t\t\tinput: json.RawMessage(`{\"foo\": \"bar\"}`),\n\t\t\texpect: map[string]interface{}{\n\t\t\t\t\"foo\": \"bar\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tinput: json.RawMessage(`{\"foo\": \"b`),\n\t\t\terr:   \"unexpected end of JSON input\",\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"with input '%s'\", tc.input), func(t *testing.T) {\n\t\t\tm, err := x.ParseRawMessageOrEmpty(tc.input)\n\t\t\tif tc.err != nil {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\trequire.Equal(t, tc.err, err.Error())\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, tc.expect, m)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/json_number.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\n// IsValidNumber reports whether s is a valid JSON number literal.\n//\n// Taken from encoding/json\nfunc IsValidNumber(s string) bool {\n\t// This function implements the JSON numbers grammar.\n\t// See https://tools.ietf.org/html/rfc7159#section-6\n\t// and https://json.org/number.gif\n\n\tif s == \"\" {\n\t\treturn false\n\t}\n\n\t// Optional -\n\tif s[0] == '-' {\n\t\ts = s[1:]\n\t\tif s == \"\" {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t// Digits\n\tswitch {\n\tdefault:\n\t\treturn false\n\n\tcase s[0] == '0':\n\t\ts = s[1:]\n\n\tcase '1' <= s[0] && s[0] <= '9':\n\t\ts = s[1:]\n\t\tfor len(s) > 0 && '0' <= s[0] && s[0] <= '9' {\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\n\t// . followed by 1 or more digits.\n\tif len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {\n\t\ts = s[2:]\n\t\tfor len(s) > 0 && '0' <= s[0] && s[0] <= '9' {\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\n\t// e or E followed by an optional - or + and\n\t// 1 or more digits.\n\tif len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {\n\t\ts = s[1:]\n\t\tif s[0] == '+' || s[0] == '-' {\n\t\t\ts = s[1:]\n\t\t\tif s == \"\" {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\tfor len(s) > 0 && '0' <= s[0] && s[0] <= '9' {\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\n\t// Make sure we are at the end.\n\treturn s == \"\"\n}\n"
  },
  {
    "path": "x/keys.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nfunc Keys[K comparable, V any](m map[K]V) []K {\n\tkeys := make([]K, len(m))\n\ti := 0\n\tfor k := range m {\n\t\tkeys[i] = k\n\t\ti++\n\t}\n\treturn keys\n}\n"
  },
  {
    "path": "x/mailhog.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\tstdlog \"log\"\n\t\"net\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gorilla/pat\"\n\t\"github.com/ian-kent/go-log/levels\"\n\t\"github.com/ian-kent/go-log/log\"\n\t\"github.com/mailhog/MailHog-Server/api\"\n\tmailhogconf \"github.com/mailhog/MailHog-Server/config\"\n\t\"github.com/mailhog/MailHog-Server/monkey\"\n\t\"github.com/mailhog/MailHog-Server/smtp\"\n\t\"github.com/mailhog/data\"\n\t\"github.com/mailhog/storage\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// StartMailhog starts a MailHog server for testing purposes.\n// It returns the SMTP connection URL and the API URL.\n// If withChaosMonkey is true, the SMTP server will randomly drop connections and simulate network issues.\nfunc StartMailhog(t testing.TB, withChaosMonkey bool) (smtpAddr, apiAddr string) {\n\tt.Helper()\n\n\t// hacky but should silence most MailHog logs during tests\n\tlog.Logger().SetLevel(levels.FATAL)\n\tstdlog.Default().SetOutput(io.Discard)\n\n\tapiconf := &mailhogconf.Config{\n\t\tStorage:     storage.CreateInMemory(),\n\t\tMessageChan: make(chan *data.Message),\n\t}\n\tif withChaosMonkey {\n\t\tjim := &monkey.Jim{\n\t\t\tDisconnectChance: 0.005,\n\t\t\tAcceptChance:     0.99,\n\t\t\tLinkSpeedAffect:  0.05,\n\t\t\tLinkSpeedMin:     1250,\n\t\t\tLinkSpeedMax:     12500,\n\t\t\tRejectAuthChance: 0.05,\n\n\t\t\t// important: set to 0 to avoid flakes in tests, because those errors are not retryable\n\t\t\tRejectSenderChance:    0,\n\t\t\tRejectRecipientChance: 0,\n\t\t}\n\t\tjim.Configure(t.Logf)\n\t\tapiconf.Monkey = jim\n\t}\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() { _ = ln.Close() })\n\tgo serveSMTP(t.Context(), ln, apiconf)\n\n\tr := pat.New()\n\tapi.CreateAPI(apiconf, r)\n\ts := httptest.NewServer(r)\n\tt.Cleanup(s.Close)\n\n\treturn fmt.Sprintf(\"smtp://%s?disable_starttls=true\", ln.Addr().String()), s.URL\n}\n\nfunc serveSMTP(ctx context.Context, ln net.Listener, cfg *mailhogconf.Config) {\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tdefault:\n\t\t\tconn, err := ln.Accept()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"[SMTP] Error accepting connection: %s\\n\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif cfg.Monkey != nil {\n\t\t\t\tok := cfg.Monkey.Accept(conn)\n\t\t\t\tif !ok {\n\t\t\t\t\t_ = conn.Close()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tgo smtp.Accept(\n\t\t\t\tconn.(*net.TCPConn).RemoteAddr().String(),\n\t\t\t\tio.ReadWriteCloser(conn),\n\t\t\t\tcfg.Storage,\n\t\t\t\tcfg.MessageChan,\n\t\t\t\tcfg.Hostname,\n\t\t\t\tcfg.Monkey,\n\t\t\t)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "x/map_json.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar (\n\tregexMatchInt   = regexp.MustCompile(`^[0-9]+$`)\n\tregexMatchFloat = regexp.MustCompile(`^[0-9]+\\.[0-9]+$`)\n\tregexMatchBool  = regexp.MustCompile(`^(?i)false|true|on$`)\n)\n\nfunc TypeMap(m map[string]string) (map[string]interface{}, error) {\n\tjm := make(map[string]interface{})\n\tfor k, v := range m {\n\t\tif regexMatchInt.MatchString(v) {\n\t\t\tvv, err := strconv.ParseInt(v, 10, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tjm[k] = vv\n\t\t} else if regexMatchFloat.MatchString(v) {\n\t\t\tvv, err := strconv.ParseFloat(v, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tjm[k] = vv\n\t\t} else if regexMatchBool.MatchString(v) {\n\t\t\t// Checkboxes have default values of `on` when checked, so set this to true.\n\t\t\tif strings.ToLower(v) == \"on\" {\n\t\t\t\tv = \"true\"\n\t\t\t}\n\t\t\tvv, err := strconv.ParseBool(strings.ToLower(v))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tjm[k] = vv\n\t\t} else {\n\t\t\tjm[k] = v\n\t\t}\n\t}\n\n\treturn jm, nil\n}\n\nfunc UntypedMapToJSON(m map[string]string) (json.RawMessage, error) {\n\tjm, err := TypeMap(m)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar b bytes.Buffer\n\tif err := json.NewEncoder(&b).Encode(jm); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn b.Bytes(), nil\n}\n\nfunc StructToMap(s interface{}) (map[string]interface{}, error) {\n\tb, err := json.Marshal(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmapData := make(map[string]interface{})\n\terr = json.Unmarshal(b, &mapData)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn mapData, nil\n}\n"
  },
  {
    "path": "x/map_json_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestStructToMap(t *testing.T) {\n\tm := json.RawMessage(`{\"string\": \"123\"}`)\n\tr, err := StructToMap(struct {\n\t\tTestString string           `json:\"string\"`\n\t\tTestRaw    *json.RawMessage `json:\"raw\"`\n\t}{\n\t\tTestString: \"string\",\n\t\tTestRaw:    &m,\n\t})\n\trequire.NoError(t, err)\n\tassert.Equal(t, map[string]interface{}{\n\t\t\"string\": \"string\",\n\t\t\"raw\": map[string]interface{}{\n\t\t\t\"string\": \"123\",\n\t\t},\n\t}, r)\n}\n\nfunc TestTypeMap(t *testing.T) {\n\tr, err := TypeMap(map[string]string{\n\t\t\"string\":  \"string\",\n\t\t\"int\":     \"123\",\n\t\t\"float\":   \"123.123\",\n\t\t\"bool\":    \"TrUe\",\n\t\t\"bool_on\": \"oN\",\n\t})\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, map[string]interface{}{\n\t\t\"string\":  \"string\",\n\t\t\"int\":     int64(123),\n\t\t\"float\":   123.123,\n\t\t\"bool\":    true,\n\t\t\"bool_on\": true,\n\t}, r)\n\n\t_, err = TypeMap(map[string]string{\n\t\t\"int\": \"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999\",\n\t})\n\tassert.Error(t, err)\n\n\t_, err = TypeMap(map[string]string{\n\t\t\"float\": \"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.9\",\n\t})\n\tassert.Error(t, err)\n}\n\nfunc TestUntypedMapToJSON(t *testing.T) {\n\tr, err := UntypedMapToJSON(map[string]string{\n\t\t\"string\":  \"string\",\n\t\t\"int\":     \"123\",\n\t\t\"float\":   \"123.123\",\n\t\t\"bool\":    \"TrUe\",\n\t\t\"bool_on\": \"oN\",\n\t})\n\trequire.NoError(t, err)\n\tassert.JSONEq(t, `{\"string\":\"string\",\"int\":123,\"float\":123.123,\"bool\":true,\"bool_on\":true}`, string(r))\n\n\t_, err = UntypedMapToJSON(map[string]string{\n\t\t\"int\": \"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999\",\n\t})\n\tassert.Error(t, err)\n}\n"
  },
  {
    "path": "x/maxitems.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\n// MaxItemsPerPage is used to prevent DoS attacks against large lists by limiting the items per page to 500.\nfunc MaxItemsPerPage(is int) int {\n\tif is > 500 {\n\t\treturn 500\n\t}\n\treturn is\n}\n"
  },
  {
    "path": "x/nocache.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"net/http\"\n)\n\n// NoCache adds `Cache-Control: private, no-cache, no-store, must-revalidate` to the response header.\nfunc NoCache(w http.ResponseWriter) {\n\tw.Header().Set(\"Cache-Control\", \"private, no-cache, no-store, must-revalidate\")\n}\n\n// NoCacheHandlerFunc wraps http.HandlerFunc with `Cache-Control: private, no-cache, no-store, must-revalidate` headers.\nfunc NoCacheHandlerFunc(handle http.HandlerFunc) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tNoCache(w)\n\t\thandle(w, r)\n\t}\n}\n\n// NoCacheHandler wraps http.HandlerFunc with `Cache-Control: private, no-cache, no-store, must-revalidate` headers.\nfunc NoCacheHandler(handle http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tNoCache(w)\n\t\thandle.ServeHTTP(w, r)\n\t})\n}\n"
  },
  {
    "path": "x/normalize.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"strings\"\n\n\t\"github.com/nyaruka/phonenumbers\"\n\t\"github.com/pkg/errors\"\n)\n\n// NormalizeEmailIdentifier normalizes an email address.\nfunc NormalizeEmailIdentifier(value string) string {\n\tif strings.Contains(value, \"@\") {\n\t\tvalue = strings.TrimSpace(strings.ToLower(value))\n\t}\n\treturn value\n}\n\n// NormalizePhoneIdentifier normalizes a phone number.\nfunc NormalizePhoneIdentifier(value string) string {\n\tif number, err := phonenumbers.Parse(value, \"\"); err == nil && phonenumbers.IsValidNumber(number) {\n\t\tvalue = phonenumbers.Format(number, phonenumbers.E164)\n\t}\n\treturn value\n}\n\n// NormalizeOtherIdentifier normalizes an identifier that is not an email or phone number.\nfunc NormalizeOtherIdentifier(value string) string {\n\treturn strings.TrimSpace(value)\n}\n\n// GracefulNormalization normalizes an identifier based on the format.\n//\n// Supported formats are:\n//\n// - email\n// - phone\n// - username\nfunc GracefulNormalization(value string) string {\n\tif number, err := phonenumbers.Parse(value, \"\"); err == nil && phonenumbers.IsValidNumber(number) {\n\t\treturn phonenumbers.Format(number, phonenumbers.E164)\n\t} else if strings.Contains(value, \"@\") {\n\t\treturn NormalizeEmailIdentifier(value)\n\t}\n\treturn NormalizeOtherIdentifier(value)\n}\n\n// NormalizeIdentifier normalizes an identifier based on the format.\n//\n// Supported formats are:\n//\n// - email\n// - phone\n// - username\nfunc NormalizeIdentifier(value, format string) (string, error) {\n\tswitch format {\n\tcase \"email\":\n\t\treturn NormalizeEmailIdentifier(value), nil\n\tcase \"sms\":\n\t\tnumber, err := phonenumbers.Parse(value, \"\")\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tif !phonenumbers.IsValidNumber(number) {\n\t\t\treturn \"\", errors.New(\"the provided number is not a valid phone number\")\n\t\t}\n\n\t\treturn phonenumbers.Format(number, phonenumbers.E164), nil\n\tcase \"username\":\n\t\tfallthrough\n\tdefault:\n\t\treturn NormalizeOtherIdentifier(value), nil\n\t}\n}\n"
  },
  {
    "path": "x/normalize_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNormalizeEmailIdentifier(t *testing.T) {\n\ttests := []struct {\n\t\tinput    string\n\t\texpected string\n\t}{\n\t\t{\"  EXAMPLE@DOMAIN.COM  \", \"example@domain.com\"},\n\t\t{\"user@domain.com\", \"user@domain.com\"},\n\t\t{\"invalid-email\", \"invalid-email\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tassert.Equal(t, test.expected, NormalizeEmailIdentifier(test.input))\n\t}\n}\n\nfunc TestNormalizePhoneIdentifier(t *testing.T) {\n\ttests := []struct {\n\t\tinput    string\n\t\texpected string\n\t}{\n\t\t{\"+1 650-253-0000\", \"+16502530000\"},\n\t\t{\"+1 (650) 253-0000\", \"+16502530000\"},\n\t\t{\"invalid-phone\", \"invalid-phone\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tassert.Equal(t, test.expected, NormalizePhoneIdentifier(test.input))\n\t}\n}\n\nfunc TestNormalizeOtherIdentifier(t *testing.T) {\n\ttests := []struct {\n\t\tinput    string\n\t\texpected string\n\t}{\n\t\t{\"  username  \", \"username\"},\n\t\t{\"user123\", \"user123\"},\n\t\t{\"  \", \"\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tassert.Equal(t, test.expected, NormalizeOtherIdentifier(test.input))\n\t}\n}\n\nfunc TestGracefulNormalization(t *testing.T) {\n\ttests := []struct {\n\t\tinput    string\n\t\texpected string\n\t}{\n\t\t{\"+1 650-253-0000\", \"+16502530000\"},\n\t\t{\"  EXAMPLE@DOMAIN.COM  \", \"example@domain.com\"},\n\t\t{\"  username  \", \"username\"},\n\t\t{\"invalid-phone\", \"invalid-phone\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tassert.Equal(t, test.expected, GracefulNormalization(test.input))\n\t}\n}\n\nfunc TestNormalizeIdentifier(t *testing.T) {\n\ttests := []struct {\n\t\tinput    string\n\t\tformat   string\n\t\texpected string\n\t\terr      bool\n\t}{\n\t\t{\"  EXAMPLE@DOMAIN.COM  \", \"email\", \"example@domain.com\", false},\n\t\t{\"+1 650-253-0000\", \"sms\", \"+16502530000\", false},\n\t\t{\"  username  \", \"username\", \"username\", false},\n\t\t{\"invalid-phone\", \"sms\", \"\", true},\n\t}\n\n\tfor _, test := range tests {\n\t\tresult, err := NormalizeIdentifier(test.input, test.format)\n\t\tif test.err {\n\t\t\tassert.Error(t, err)\n\t\t} else {\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, test.expected, result)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "x/nosurfx/nosurf_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage nosurfx_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/x/nosurfx\"\n\n\t\"github.com/tidwall/gjson\"\n\n\t\"github.com/ory/x/assertx\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/nosurf\"\n\t\"github.com/ory/x/randx\"\n)\n\nfunc TestNosurfBaseCookieHandler(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\trequire.NoError(t, conf.Set(ctx, config.ViperKeyPublicBaseURL, \"http://foo.com/bar\"))\n\n\tcookie := nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest(\"GET\", \"https://foo/bar\", nil))\n\tassert.EqualValues(t, \"csrf_token_01c86631efd1537ee34a98e75884a6e21dd8e2d9e944934bca21204106bfd32f\", cookie.Name, \"base64 representation of http://foo.com/bar\")\n\tassert.EqualValues(t, http.SameSiteLaxMode, cookie.SameSite, \"is set to lax because https/secure is false - chrome rejects none samesite on non-https\")\n\tassert.EqualValues(t, nosurf.MaxAge, cookie.MaxAge)\n\tassert.EqualValues(t, \"/\", cookie.Path, \"cookie path is site root by default\")\n\tassert.EqualValues(t, \"\", cookie.Domain, \"domain for the cookie is set to empty per default\")\n\tassert.False(t, cookie.Secure, \"false because insecure dev mode\")\n\tassert.True(t, cookie.HttpOnly)\n\n\talNum := regexp.MustCompile(\"[a-zA-Z_0-9]+\")\n\tfor i := 0; i < 10; i++ {\n\t\trequire.NoError(t, conf.Set(ctx, config.ViperKeyPublicBaseURL, randx.MustString(16, randx.AlphaNum)))\n\t\tcookie := nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest(\"GET\", \"https://foo/bar\", nil))\n\n\t\tassert.NotEqual(t, \"aHR0cDovL2Zvby5jb20vYmFy_csrf_token\", cookie.Name, \"should no longer be http://foo.com/bar\")\n\t\tassert.True(t, alNum.MatchString(cookie.Name), \"does not have any special chars\")\n\t}\n\n\trequire.NoError(t, conf.Set(ctx, config.ViperKeyCookieSameSite, \"None\"))\n\trequire.NoError(t, conf.Set(ctx, \"dev\", false))\n\tcookie = nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest(\"GET\", \"https://foo/bar\", nil))\n\tassert.EqualValues(t, http.SameSiteNoneMode, cookie.SameSite, \"can be none because https/secure is true\")\n\tassert.True(t, cookie.Secure, \"true because secure mode\")\n\tassert.True(t, cookie.HttpOnly)\n}\n\nfunc TestNosurfBaseCookieHandlerAliasing(t *testing.T) {\n\tctx := context.Background()\n\tconf, reg := pkg.NewFastRegistryWithMocks(t)\n\n\trequire.NoError(t, conf.Set(ctx, config.ViperKeyPublicBaseURL, \"http://foo.com/bar\"))\n\n\tcookie := nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest(\"GET\", \"http://foo.com/bar\", nil))\n\tassert.EqualValues(t, \"\", cookie.Domain, \"remains unset\")\n\tassert.EqualValues(t, \"/\", cookie.Path, \"cookie path is site root by default\")\n\n\t// Check root settings\n\trequire.NoError(t, conf.Set(ctx, config.ViperKeyCookieDomain, \"bar.com\"))\n\trequire.NoError(t, conf.Set(ctx, config.ViperKeyCookiePath, \"/baz\"))\n\tcookie = nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest(\"GET\", \"http://foo.com/bar\", nil))\n\tassert.EqualValues(t, \"bar.com\", cookie.Domain, \"domain doesn't change when request not from an alias but is overwritten by ViperKeyCookieDomain\")\n\tassert.EqualValues(t, \"/baz\", cookie.Path, \"cookie path is site root by default but is overwritten by ViperKeyCookiePath\")\n}\n\nfunc TestNosurfBaseCookieErrorHandler(t *testing.T) {\n\t_, reg := pkg.NewFastRegistryWithMocks(t)\n\n\th := nosurfx.CSRFFailureHandler(reg)\n\texpectError := func(t *testing.T, err error, req *http.Request) {\n\t\tt.Helper()\n\t\trec := httptest.NewRecorder()\n\t\th(rec, req)\n\t\tassertx.EqualAsJSON(t, err, json.RawMessage(gjson.Get(rec.Body.String(), \"error\").Raw))\n\t}\n\n\tnewAjaxFormRequest := func() *http.Request {\n\t\treq := httptest.NewRequest(\"GET\", \"https://foo/bar\", nil)\n\t\treq.Header.Set(\"Sec-Fetch-Mode\", \"cors\")\n\t\treturn req\n\t}\n\n\tnewAjaxJSONRequest := func() *http.Request {\n\t\treq := httptest.NewRequest(\"GET\", \"https://foo/bar\", nil)\n\t\treq.Header.Set(\"Sec-Fetch-Mode\", \"cors\")\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t\treturn req\n\t}\n\n\tnewBrowserRequest := func() *http.Request {\n\t\treq := httptest.NewRequest(\"GET\", \"https://foo/bar\", nil)\n\t\treturn req\n\t}\n\n\tt.Run(\"case=without cookie\", func(t *testing.T) {\n\t\tt.Run(\"source=ajax\", func(t *testing.T) {\n\t\t\texpectError(t, nosurfx.ErrInvalidCSRFTokenAJAXNoCookies, newAjaxFormRequest())\n\t\t})\n\n\t\tt.Run(\"source=browser\", func(t *testing.T) {\n\t\t\texpectError(t, nosurfx.ErrInvalidCSRFTokenServerNoCookies, newBrowserRequest())\n\t\t})\n\t})\n\n\tt.Run(\"case=ajax with cookie but without csrf cookie\", func(t *testing.T) {\n\t\ttest := func(t *testing.T, req *http.Request, err error) {\n\t\t\treq.Header.Set(\"Cookie\", \"foo=bar;\")\n\t\t\texpectError(t, err, req)\n\t\t}\n\n\t\tt.Run(\"source=ajax\", func(t *testing.T) {\n\t\t\ttest(t, newAjaxFormRequest(), nosurfx.ErrInvalidCSRFTokenAJAXCookieMissing)\n\t\t})\n\n\t\tt.Run(\"source=browser\", func(t *testing.T) {\n\t\t\ttest(t, newBrowserRequest(), nosurfx.ErrInvalidCSRFTokenServerCookieMissing)\n\t\t})\n\t})\n\n\tt.Run(\"case=with correct cookie but token was not sent in header\", func(t *testing.T) {\n\t\ttest := func(t *testing.T, req *http.Request, err error) {\n\t\t\treq.Header.Set(\"Cookie\", nosurfx.CSRFCookieName(req.Context(), reg)+\"=bar;\")\n\t\t\texpectError(t, err, req)\n\t\t}\n\n\t\tt.Run(\"source=ajax\", func(t *testing.T) {\n\t\t\ttest(t, newAjaxFormRequest(), nosurfx.ErrInvalidCSRFTokenAJAXTokenNotSent)\n\t\t})\n\n\t\tt.Run(\"source=ajax json\", func(t *testing.T) {\n\t\t\ttest(t, newAjaxJSONRequest(), nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch)\n\t\t})\n\n\t\tt.Run(\"source=browser\", func(t *testing.T) {\n\t\t\ttest(t, newBrowserRequest(), nosurfx.ErrInvalidCSRFTokenServerTokenNotSent)\n\t\t})\n\t})\n\n\tt.Run(\"case=ajax with correct cookie and token in header but they do not match\", func(t *testing.T) {\n\t\ttest := func(t *testing.T, req *http.Request, err error) {\n\t\t\treq.Header.Set(nosurf.HeaderName, \"bar\")\n\t\t\treq.Header.Set(\"Cookie\", nosurfx.CSRFCookieName(req.Context(), reg)+\"=bar;\")\n\t\t\texpectError(t, err, req)\n\t\t}\n\n\t\tt.Run(\"source=ajax\", func(t *testing.T) {\n\t\t\ttest(t, newAjaxFormRequest(), nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch)\n\t\t})\n\n\t\tt.Run(\"source=browser\", func(t *testing.T) {\n\t\t\ttest(t, newBrowserRequest(), nosurfx.ErrInvalidCSRFTokenServerTokenMismatch)\n\t\t})\n\t})\n\n\tt.Run(\"case=correct cookie and token in body but they do not match\", func(t *testing.T) {\n\t\ttest := func(t *testing.T, req *http.Request, err error) {\n\t\t\treq.Header.Set(\"Accept\", \"application/x-www-form-urlencoded\")\n\t\t\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\t\t\treq.Header.Set(\"Cookie\", nosurfx.CSRFCookieName(req.Context(), reg)+\"=bar;\")\n\t\t\trequire.NoError(t, req.ParseForm())\n\t\t\texpectError(t, err, req)\n\t\t}\n\n\t\tt.Run(\"source=ajax\", func(t *testing.T) {\n\t\t\treq := httptest.NewRequest(\"POST\", \"https://foo/bar\", strings.NewReader(url.Values{nosurf.FormFieldName: {\"bar\"}}.Encode()))\n\t\t\treq.Header.Set(\"Sec-Fetch-Mode\", \"cors\")\n\t\t\ttest(t, req, nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch)\n\t\t})\n\n\t\tt.Run(\"source=browser\", func(t *testing.T) {\n\t\t\treq := httptest.NewRequest(\"POST\", \"https://foo/bar\", strings.NewReader(url.Values{nosurf.FormFieldName: {\"bar\"}}.Encode()))\n\t\t\ttest(t, req, nosurfx.ErrInvalidCSRFTokenServerTokenMismatch)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "x/pagination.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/pagination/keysetpagination\"\n\t\"github.com/ory/x/pagination/migrationpagination\"\n\t\"github.com/ory/x/pagination/pagepagination\"\n)\n\n// The format we need to use in the Page tokens, as it's the only format that is understood by all DBs\nconst MapPaginationDateFormat = \"2006-01-02 15:04:05.99999\"\n\n// ParsePagination parses limit and page from *http.Request with given limits and defaults.\nfunc ParsePagination(r *http.Request) (page, itemsPerPage int) {\n\treturn migrationpagination.NewDefaultPaginator().ParsePagination(r)\n}\n\nfunc PaginationHeader(w http.ResponseWriter, u url.URL, total int64, page, itemsPerPage int) {\n\tmigrationpagination.PaginationHeader(w, &u, total, page, itemsPerPage)\n}\n\ntype Page struct {\n\tPage, ItemsPerPage int\n}\n\nvar PagePaginationLimit = 1000\n\nfunc ParseKeysetOrPagePagination(r *http.Request) ([]keysetpagination.Option, *Page, error) {\n\tq := r.URL.Query()\n\t// If we have any new-style pagination parameters, use those and ignore the rest.\n\tif q.Has(\"page_token\") || q.Has(\"page_size\") {\n\t\tkeyset, err := keysetpagination.Parse(q, keysetpagination.NewStringPageToken)\n\t\tif err != nil {\n\t\t\treturn nil, nil, herodot.ErrBadRequest.WithReason(err.Error())\n\t\t}\n\t\treturn keyset, nil, nil\n\t}\n\t// allow fallback page pagination with upper limit\n\tif q.Has(\"page\") {\n\t\tpaginator := pagepagination.PagePaginator{MaxItems: 500, DefaultItems: 250}\n\t\tpage, perPage := paginator.ParsePagination(r)\n\t\tif page*perPage > PagePaginationLimit {\n\t\t\treturn nil, nil, herodot.ErrBadRequest.WithReasonf(\"Legacy pagination is not supported for enumerating over %d items. Please switch to using page_token and page_size.\", PagePaginationLimit)\n\t\t}\n\t\treturn nil, &Page{page, perPage}, nil\n\t}\n\t// Allow passing per_page instead of page_size if only the former is set...\n\tif q.Has(\"per_page\") && !q.Has(\"page_size\") {\n\t\tq.Set(\"page_size\", q.Get(\"per_page\"))\n\t\tq.Del(\"per_page\")\n\t\tr.URL.RawQuery = q.Encode()\n\t}\n\t// ... and defaul to keyset pagination\n\tkeyset, err := keysetpagination.Parse(q, keysetpagination.NewStringPageToken)\n\tif err != nil {\n\t\treturn nil, nil, herodot.ErrBadRequest.WithReason(err.Error())\n\t}\n\treturn keyset, nil, nil\n}\n"
  },
  {
    "path": "x/pointer.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport \"github.com/gofrs/uuid\"\n\nfunc PointToUUID(id uuid.UUID) *uuid.UUID {\n\tif id == uuid.Nil {\n\t\treturn nil\n\t}\n\treturn &id\n}\n"
  },
  {
    "path": "x/provider.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"context\"\n\n\t\"github.com/gorilla/sessions\"\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/ory/x/httpx\"\n\t\"github.com/ory/x/logrusx\"\n\t\"github.com/ory/x/otelx\"\n)\n\ntype CookieProvider interface {\n\tCookieManager(ctx context.Context) sessions.StoreExact\n\tContinuityCookieManager(ctx context.Context) sessions.StoreExact\n}\n\ntype BasicRegistry struct {\n\tL *logrusx.Logger\n\tC *retryablehttp.Client\n\tT *otelx.Tracer\n}\n\nfunc (s *BasicRegistry) Tracer(_ context.Context) *otelx.Tracer { return s.T }\nfunc (s *BasicRegistry) Logger() *logrusx.Logger                { return s.L }\n\nfunc (s *BasicRegistry) HTTPClient(_ context.Context, _ ...httpx.ResilientOptions) *retryablehttp.Client {\n\treturn s.C\n}\n\nvar _ logrusx.Provider = (*BasicRegistry)(nil)\nvar _ httpx.ClientProvider = (*BasicRegistry)(nil)\nvar _ otelx.Provider = (*BasicRegistry)(nil)\n"
  },
  {
    "path": "x/readall.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"io\"\n)\n\nfunc MustReadAll(r io.Reader) []byte {\n\tall, err := io.ReadAll(r)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn all\n}\n"
  },
  {
    "path": "x/redir/port_redirect.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage redir\n\nimport (\n\t\"net/http\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/x/httprouterx\"\n)\n\nfunc RedirectToAdminRoute(reg config.Provider) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tadmin := reg.Config().SelfAdminURL(r.Context())\n\n\t\tdest := *r.URL\n\t\tdest.Host = admin.Host\n\t\tdest.Scheme = admin.Scheme\n\t\tdest.Path = strings.TrimPrefix(dest.Path, httprouterx.AdminPrefix)\n\t\tdest.Path = path.Join(admin.Path, httprouterx.AdminPrefix, dest.Path)\n\n\t\thttp.Redirect(w, r, dest.String(), http.StatusTemporaryRedirect)\n\t}\n}\n\nfunc RedirectToPublicRoute(reg config.Provider) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tpublic := reg.Config().SelfPublicURL(r.Context())\n\n\t\tdest := *r.URL\n\t\tdest.Host = public.Host\n\t\tdest.Scheme = public.Scheme\n\t\tdest.Path = strings.TrimPrefix(dest.Path, httprouterx.AdminPrefix)\n\t\tdest.Path = path.Join(public.Path, dest.Path)\n\n\t\thttp.Redirect(w, r, dest.String(), http.StatusTemporaryRedirect)\n\t}\n}\n"
  },
  {
    "path": "x/redir/port_redirect_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage redir_test\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/x\"\n\t\"github.com/ory/kratos/x/redir\"\n\t\"github.com/ory/x/configx\"\n\t\"github.com/ory/x/httprouterx\"\n)\n\nfunc TestRedirectToPublicAdminRoute(t *testing.T) {\n\tpub, adm := httprouterx.NewTestRouterPublic(t), httprouterx.NewTestRouterAdminWithPrefix(t)\n\n\tadminTS := httptest.NewServer(adm)\n\tpubTS := httptest.NewServer(pub)\n\tt.Cleanup(pubTS.Close)\n\tt.Cleanup(adminTS.Close)\n\t_, reg := pkg.NewFastRegistryWithMocks(t, configx.WithValues(map[string]any{\n\t\tconfig.ViperKeyAdminBaseURL:  adminTS.URL,\n\t\tconfig.ViperKeyPublicBaseURL: pubTS.URL,\n\t}))\n\n\tpub.POST(\"/privileged\", redir.RedirectToAdminRoute(reg))\n\tpub.POST(\"/admin/privileged\", redir.RedirectToAdminRoute(reg))\n\tadm.POST(\"/privileged\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = io.Copy(w, r.Body)\n\t})\n\n\tadm.POST(\"/read\", redir.RedirectToPublicRoute(reg))\n\tpub.POST(\"/read\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = io.Copy(w, r.Body)\n\t})\n\n\tfor k, tc := range []struct {\n\t\tsource string\n\t\tdest   string\n\t}{\n\t\t{\n\t\t\tsource: pubTS.URL + \"/privileged?foo=bar\",\n\t\t\tdest:   adminTS.URL + \"/admin/privileged?foo=bar\",\n\t\t},\n\t\t{\n\t\t\tsource: pubTS.URL + \"/admin/privileged?foo=bar\",\n\t\t\tdest:   adminTS.URL + \"/admin/privileged?foo=bar\",\n\t\t},\n\t\t{\n\t\t\tsource: adminTS.URL + \"/admin/read?foo=bar\",\n\t\t\tdest:   pubTS.URL + \"/read?foo=bar\",\n\t\t},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"%d\", k), func(t *testing.T) {\n\t\t\tid := x.NewUUID().String()\n\t\t\tres, err := adminTS.Client().Post(tc.source, \"\", strings.NewReader(id))\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.EqualValues(t, http.StatusOK, res.StatusCode)\n\t\t\tassert.Equal(t, tc.dest, res.Request.URL.String())\n\t\t\tbody, err := io.ReadAll(res.Body)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, id, string(body))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/redir/secure_redirect.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage redir\n\nimport (\n\t\"cmp\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/ory/kratos/x\"\n\n\t\"github.com/ory/kratos/text\"\n\n\t\"github.com/golang/gddo/httputil\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n)\n\ntype secureRedirectOptions struct {\n\tallowlist       []url.URL\n\tdefaultReturnTo *url.URL\n\treturnTo        string\n\tsourceURL       string\n}\n\ntype SecureRedirectOption func(*secureRedirectOptions)\n\n// SecureRedirectAllowURLs allows the given URLs for redirects.\nfunc SecureRedirectAllowURLs(urls []url.URL) SecureRedirectOption {\n\treturn func(o *secureRedirectOptions) {\n\t\to.allowlist = append(o.allowlist, urls...)\n\t}\n}\n\n// SecureRedirectUseSourceURL uses the given source URL (checks the `?return_to` value)\n// instead of r.URL.\nfunc SecureRedirectUseSourceURL(source string) SecureRedirectOption {\n\treturn func(o *secureRedirectOptions) {\n\t\to.sourceURL = source\n\t}\n}\n\n// SecureRedirectReturnTo uses the provided URL to redirect the user to it.\nfunc SecureRedirectReturnTo(returnTo string) SecureRedirectOption {\n\treturn func(o *secureRedirectOptions) {\n\t\to.returnTo = returnTo\n\t}\n}\n\n// SecureRedirectAllowSelfServiceURLs allows the caller to define `?return_to=` values\n// which contain the server's URL and `/self-service` path prefix. Useful for redirecting\n// to the login endpoint, for example.\nfunc SecureRedirectAllowSelfServiceURLs(publicURL *url.URL) SecureRedirectOption {\n\treturn func(o *secureRedirectOptions) {\n\t\to.allowlist = append(o.allowlist, *urlx.AppendPaths(publicURL, \"/self-service\"))\n\t}\n}\n\n// SecureRedirectOverrideDefaultReturnTo overrides the defaultReturnTo address specified\n// as the second arg.\nfunc SecureRedirectOverrideDefaultReturnTo(defaultReturnTo *url.URL) SecureRedirectOption {\n\treturn func(o *secureRedirectOptions) {\n\t\to.defaultReturnTo = defaultReturnTo\n\t}\n}\n\n// SecureRedirectToIsAllowedHost validates if the redirect_to param is allowed for a given wildcard\nfunc SecureRedirectToIsAllowedHost(returnTo *url.URL, allowed url.URL) bool {\n\tif allowed.Host != \"\" && allowed.Host[:1] == \"*\" {\n\t\treturn strings.HasSuffix(strings.ToLower(returnTo.Host), strings.ToLower(allowed.Host)[1:])\n\t}\n\treturn strings.EqualFold(allowed.Host, returnTo.Host)\n}\n\n// TakeOverReturnToParameter carries over the return_to parameter to a new URL\n// If `from` does not contain the `return_to` query parameter, the first non-empty value from `fallback` is used instead.\nfunc TakeOverReturnToParameter(from string, to string, fallback ...string) (string, error) {\n\tfromURL, err := url.Parse(from)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturnTo := cmp.Or(append([]string{fromURL.Query().Get(\"return_to\")}, fallback...)...)\n\t// Empty return_to parameter, return early\n\tif returnTo == \"\" {\n\t\treturn to, nil\n\t}\n\ttoURL, err := url.Parse(to)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\ttoQuery := toURL.Query()\n\ttoQuery.Set(\"return_to\", returnTo)\n\ttoURL.RawQuery = toQuery.Encode()\n\treturn toURL.String(), nil\n}\n\n// SecureRedirectTo implements a HTTP redirector who mitigates open redirect vulnerabilities by\n// working with allow lists.\nfunc SecureRedirectTo(r *http.Request, defaultReturnTo *url.URL, opts ...SecureRedirectOption) (returnTo *url.URL, err error) {\n\to := &secureRedirectOptions{defaultReturnTo: defaultReturnTo}\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\n\tif len(o.allowlist) == 0 {\n\t\treturn o.defaultReturnTo, nil\n\t}\n\n\tsource := x.RequestURL(r)\n\tif o.sourceURL != \"\" {\n\t\tsource, err = url.ParseRequestURI(o.sourceURL)\n\t\tif err != nil {\n\t\t\treturn nil, errors.WithStack(herodot.ErrInternalServerError.WithWrap(err).WithReasonf(\"Unable to parse the original request URL: %s\", err))\n\t\t}\n\t}\n\n\trawReturnTo := cmp.Or(o.returnTo, source.Query().Get(\"return_to\"))\n\tif rawReturnTo == \"\" {\n\t\treturn o.defaultReturnTo, nil\n\t}\n\n\treturnTo, err = url.Parse(rawReturnTo)\n\tif err != nil {\n\t\treturn nil, errors.WithStack(herodot.ErrBadRequest.WithWrap(err).WithReasonf(\"Unable to parse the return_to query parameter as an URL: %s\", err))\n\t}\n\n\treturnTo.Host = cmp.Or(returnTo.Host, o.defaultReturnTo.Host)\n\treturnTo.Scheme = cmp.Or(returnTo.Scheme, o.defaultReturnTo.Scheme)\n\n\tfor _, allowed := range o.allowlist {\n\t\tif strings.EqualFold(allowed.Scheme, returnTo.Scheme) &&\n\t\t\tSecureRedirectToIsAllowedHost(returnTo, allowed) &&\n\t\t\tstrings.HasPrefix(\n\t\t\t\tcmp.Or(returnTo.Path, \"/\"),\n\t\t\t\tcmp.Or(allowed.Path, \"/\")) {\n\t\t\treturn returnTo, nil\n\t\t}\n\t}\n\n\treturn nil, errors.WithStack(herodot.ErrBadRequest.\n\t\tWithID(text.ErrIDRedirectURLNotAllowed).\n\t\tWithReasonf(\"Requested return_to URL %q is not allowed.\", returnTo),\n\t)\n}\n\nfunc SecureContentNegotiationRedirection(\n\tw http.ResponseWriter, r *http.Request, out interface{},\n\trequestURL string, writer herodot.Writer, c *config.Config,\n\topts ...SecureRedirectOption,\n) error {\n\tswitch httputil.NegotiateContentType(r, []string{\n\t\t\"text/html\",\n\t\t\"application/json\",\n\t}, \"text/html\") {\n\tcase \"application/json\":\n\t\twriter.Write(w, r, out)\n\tcase \"text/html\":\n\t\tfallthrough\n\tdefault:\n\t\tret, err := SecureRedirectTo(r, c.SelfServiceBrowserDefaultReturnTo(r.Context()),\n\t\t\tappend([]SecureRedirectOption{\n\t\t\t\tSecureRedirectUseSourceURL(requestURL),\n\t\t\t\tSecureRedirectAllowURLs(c.SelfServiceBrowserAllowedReturnToDomains(r.Context())),\n\t\t\t\tSecureRedirectAllowSelfServiceURLs(c.SelfPublicURL(r.Context())),\n\t\t\t}, opts...)...,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\thttp.Redirect(w, r, ret.String(), http.StatusSeeOther)\n\t}\n\n\treturn nil\n}\n\nfunc ContentNegotiationRedirection(\n\tw http.ResponseWriter, r *http.Request, out interface{}, writer herodot.Writer, returnTo string,\n) {\n\tswitch httputil.NegotiateContentType(r, []string{\n\t\t\"text/html\",\n\t\t\"application/json\",\n\t}, \"text/html\") {\n\tcase \"application/json\":\n\t\twriter.Write(w, r, out)\n\tcase \"text/html\":\n\t\tfallthrough\n\tdefault:\n\t\thttp.Redirect(w, r, returnTo, http.StatusSeeOther)\n\t}\n}\n"
  },
  {
    "path": "x/redir/secure_redirect_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage redir_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/ory/kratos/x/redir\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ory/herodot\"\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/driver/config\"\n\t\"github.com/ory/kratos/pkg\"\n\t\"github.com/ory/kratos/x\"\n)\n\nfunc TestSecureContentNegotiationRedirection(t *testing.T) {\n\tconf, _ := pkg.NewFastRegistryWithMocks(t)\n\tvar jsonActual = json.RawMessage(`{\"foo\":\"bar\"}` + \"\\n\")\n\twriter := herodot.NewJSONWriter(nil)\n\n\trouter := http.NewServeMux()\n\trouter.HandleFunc(\"GET /redir\", func(w http.ResponseWriter, r *http.Request) {\n\t\trequire.NoError(t, redir.SecureContentNegotiationRedirection(w, r, jsonActual, x.RequestURL(r).String(), writer, conf))\n\t})\n\trouter.HandleFunc(\"GET /default-return-to\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusNoContent)\n\t})\n\trouter.HandleFunc(\"GET /return-to\", func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusNoContent)\n\t})\n\n\tts := httptest.NewServer(router)\n\tdefer ts.Close()\n\n\tctx := context.Background()\n\n\tdefaultReturnTo := ts.URL + \"/default-return-to\"\n\tconf.MustSet(ctx, config.ViperKeySelfServiceBrowserDefaultReturnTo, defaultReturnTo)\n\tconf.MustSet(ctx, config.ViperKeyPublicBaseURL, ts.URL)\n\tconf.MustSet(ctx, config.ViperKeyURLsAllowedReturnToDomains, []string{ts.URL})\n\n\trun := func(t *testing.T, href string, contentType string) (*http.Response, string) {\n\t\treq, err := http.NewRequest(\"GET\", href, nil)\n\t\trequire.NoError(t, err)\n\t\treq.Header.Add(\"Accept\", contentType)\n\t\tres, err := ts.Client().Do(req)\n\t\trequire.NoError(t, err)\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\treturn res, string(body)\n\t}\n\n\tt.Run(\"case=html browser causes redirect\", func(t *testing.T) {\n\t\tres, _ := run(t, ts.URL+\"/redir\", \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\")\n\t\tassert.EqualValues(t, res.Request.URL.String(), defaultReturnTo)\n\t})\n\n\tt.Run(\"case=html browser causes redirect with redirect_to\", func(t *testing.T) {\n\t\tres, _ := run(t, ts.URL+\"/redir?return_to=/redirect-to\", \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\")\n\t\tassert.EqualValues(t, res.Request.URL.String(), ts.URL+\"/redirect-to\")\n\t})\n\n\tt.Run(\"case=html browser causes redirect with redirect_to\", func(t *testing.T) {\n\t\tres, body := run(t, ts.URL+\"/redir?return_to=/redirect-to\", \"application/json\")\n\t\tassert.EqualValues(t, res.Request.URL.String(), ts.URL+\"/redir?return_to=/redirect-to\")\n\t\tassert.EqualValues(t, body, jsonActual)\n\t})\n}\n\nfunc TestSecureRedirectToIsAllowedHost(t *testing.T) {\n\ttype testCase struct {\n\t\tallowedURL  string\n\t\tredirectURL string\n\t\tvalid       bool\n\t}\n\ttests := map[string]testCase{\n\t\t\"case=Domain is allowed\":              {allowedURL: \"https://foo.bar\", redirectURL: \"https://foo.bar/redir\", valid: true},\n\t\t\"case=Domain prefix is allowed\":       {allowedURL: \"https://*.bar\", redirectURL: \"https://foo.bar/redir\", valid: true},\n\t\t\"case=Subdomain prefix is allowed\":    {allowedURL: \"https://*.foo.bar\", redirectURL: \"https://auth.foo.bar/redir\", valid: true},\n\t\t\"case=Domain is not allowed\":          {allowedURL: \"https://foo.baz\", redirectURL: \"https://foo.bar/redir\", valid: false},\n\t\t\"case=Domain wildcard is not allowed\": {allowedURL: \"https://*.foo.baz\", redirectURL: \"https://foo.bar/redir\", valid: false},\n\t\t\"case=Subdomain is not allowed\":       {allowedURL: \"https://*.foo.baz\", redirectURL: \"https://auth.foo.bar/redir\", valid: false},\n\t}\n\tfor name, tc := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tallowedURL, err := url.Parse(tc.allowedURL)\n\t\t\trequire.NoError(t, err)\n\t\t\tredirectURL, err := url.Parse(tc.redirectURL)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, redir.SecureRedirectToIsAllowedHost(redirectURL, *allowedURL), tc.valid)\n\t\t})\n\t}\n}\n\nfunc TestTakeOverReturnToParameter(t *testing.T) {\n\ttype testCase struct {\n\t\tfromUrl           string\n\t\ttoURL             string\n\t\texpectedOutputUrl string\n\t}\n\ttests := map[string]testCase{\n\t\t\"case=return_to is taken over\":                                             {fromUrl: \"https://original.bar?return_to=https://allowed.domain\", toURL: \"https://output.bar\", expectedOutputUrl: \"https://output.bar?return_to=https%3A%2F%2Fallowed.domain\"},\n\t\t\"case=only return_to is taken over when multiple query parameters are set\": {fromUrl: \"https://original.bar?return_to=https://allowed.domain&flow=12312\", toURL: \"https://output.bar\", expectedOutputUrl: \"https://output.bar?return_to=https%3A%2F%2Fallowed.domain\"},\n\t\t\"case=output query parameters are preserved\":                               {fromUrl: \"https://original.bar?return_to=https://allowed.domain\", toURL: \"https://output.bar?flow=123321\", expectedOutputUrl: \"https://output.bar?flow=123321&return_to=https%3A%2F%2Fallowed.domain\"},\n\t\t\"case=when original return_to is empty do nothing\":                         {fromUrl: \"https://original.bar?return_to=\", toURL: \"https://output.bar?flow=123123\", expectedOutputUrl: \"https://output.bar?flow=123123\"},\n\t}\n\tfor name, tc := range tests {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\toutput, err := redir.TakeOverReturnToParameter(tc.fromUrl, tc.toURL)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, output, tc.expectedOutputUrl)\n\t\t})\n\t}\n}\n\nfunc TestSecureRedirectTo(t *testing.T) {\n\tnewServer := func(t *testing.T, isTLS bool, isRelative bool, expectErr bool, opts func(ts *httptest.Server) []redir.SecureRedirectOption) *httptest.Server {\n\t\tvar ts *httptest.Server\n\t\thandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif opts == nil {\n\t\t\t\topts = func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t\tdefaultReturnTo := \"/default-return-to\"\n\t\t\tif !isRelative {\n\t\t\t\tdefaultReturnTo = ts.URL + defaultReturnTo\n\t\t\t}\n\t\t\treturnTo, err := redir.SecureRedirectTo(r, urlx.ParseOrPanic(defaultReturnTo), opts(ts)...)\n\t\t\tif expectErr {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\t_, _ = w.Write([]byte(\"error\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err)\n\n\t\t\t_, _ = w.Write([]byte(returnTo.String()))\n\t\t})\n\n\t\tif isTLS {\n\t\t\tts = httptest.NewTLSServer(handler)\n\t\t} else {\n\t\t\tts = httptest.NewServer(handler)\n\t\t}\n\n\t\tt.Cleanup(ts.Close)\n\t\treturn ts\n\t}\n\n\tmakeRequest := func(t *testing.T, ts *httptest.Server, path string) (*http.Response, string) {\n\t\tres, err := ts.Client().Get(ts.URL + \"/\" + path)\n\t\trequire.NoError(t, err)\n\n\t\tbody, err := io.ReadAll(res.Body)\n\t\trequire.NoError(t, err)\n\t\trequire.NoError(t, res.Body.Close())\n\t\treturn res, string(body)\n\t}\n\n\tt.Run(\"case=return to a relative path with anchor works\", func(t *testing.T) {\n\t\treturnTo, err := redir.SecureRedirectTo(\n\t\t\thttptest.NewRequest(\"GET\", \"/?return_to=/foo/kratos%23abcd\", nil),\n\t\t\turlx.ParseOrPanic(\"/default-return-to\"),\n\t\t\tredir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"/foo\")}),\n\t\t)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, returnTo.String(), \"/foo/kratos#abcd\")\n\t})\n\n\tt.Run(\"case=return to default URL if nothing is allowed\", func(t *testing.T) {\n\t\treturnTo, err := redir.SecureRedirectTo(\n\t\t\thttptest.NewRequest(\"GET\", \"/?return_to=/foo\", nil),\n\t\t\turlx.ParseOrPanic(\"https://www.ory.sh/default-return-to\"),\n\t\t)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, returnTo.String(), \"https://www.ory.sh/default-return-to\")\n\t})\n\n\tt.Run(\"case=return to foo with server baseURL if allowed\", func(t *testing.T) {\n\t\treturnTo, err := redir.SecureRedirectTo(\n\t\t\thttptest.NewRequest(\"GET\", \"/?return_to=/foo\", nil),\n\t\t\turlx.ParseOrPanic(\"https://www.ory.sh/default-return-to\"),\n\t\t\tredir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"https://www.ory.sh\")}),\n\t\t)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, returnTo.String(), \"https://www.ory.sh/foo\")\n\t})\n\n\tt.Run(\"case=return to a relative path works\", func(t *testing.T) {\n\t\treturnTo, err := redir.SecureRedirectTo(\n\t\t\thttptest.NewRequest(\"GET\", \"/?return_to=/foo/kratos\", nil),\n\t\t\turlx.ParseOrPanic(\"/default-return-to\"),\n\t\t\tredir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"/foo\")}),\n\t\t)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, returnTo.String(), \"/foo/kratos\")\n\t})\n\n\tt.Run(\"case=return to a fully qualified domain is forbidden if allowlist is relative\", func(t *testing.T) {\n\t\t_, err := redir.SecureRedirectTo(\n\t\t\thttptest.NewRequest(\"GET\", \"/?return_to=https://www.ory.sh/foo/kratos\", nil),\n\t\t\turlx.ParseOrPanic(\"/default-return-to\"),\n\t\t\tredir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"/foo\")}),\n\t\t)\n\t\trequire.Error(t, err)\n\t})\n\n\tt.Run(\"case=return to another domain works\", func(t *testing.T) {\n\t\treturnTo, err := redir.SecureRedirectTo(\n\t\t\thttptest.NewRequest(\"GET\", \"https://example.com/?return_to=https://www.ory.sh/foo/kratos\", nil),\n\t\t\turlx.ParseOrPanic(\"https://www.ory.sh/default-return-to\"),\n\t\t\tredir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"https://www.ory.sh/foo\")}),\n\t\t)\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, returnTo.String(), \"https://www.ory.sh/foo/kratos\")\n\t})\n\n\tt.Run(\"case=return to another domain fails if host mismatches\", func(t *testing.T) {\n\t\ts := newServer(t, false, false, true, func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\treturn []redir.SecureRedirectOption{redir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"https://www.not-ory.sh/\")})}\n\t\t})\n\t\t_, body := makeRequest(t, s, \"?return_to=https://www.ory.sh/kratos\")\n\t\tassert.Equal(t, body, \"error\")\n\t})\n\n\tt.Run(\"case=return to another domain fails if path mismatches\", func(t *testing.T) {\n\t\ts := newServer(t, false, false, true, func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\treturn []redir.SecureRedirectOption{redir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"https://www.ory.sh/not-kratos\")})}\n\t\t})\n\t\t_, body := makeRequest(t, s, \"?return_to=https://www.ory.sh/kratos\")\n\t\tassert.Equal(t, body, \"error\")\n\t})\n\n\tt.Run(\"case=return to another domain fails if scheme mismatches\", func(t *testing.T) {\n\t\ts := newServer(t, false, false, true, func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\treturn []redir.SecureRedirectOption{redir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"http://www.ory.sh/\")})}\n\t\t})\n\t\t_, body := makeRequest(t, s, \"?return_to=https://www.ory.sh/kratos\")\n\t\tassert.Equal(t, body, \"error\")\n\t})\n\n\tt.Run(\"case=should work with self-service modifier\", func(t *testing.T) {\n\t\ts := newServer(t, false, false, false, func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\treturn []redir.SecureRedirectOption{redir.SecureRedirectAllowSelfServiceURLs(urlx.ParseOrPanic(ts.URL))}\n\t\t})\n\t\t_, body := makeRequest(t, s, \"?return_to=/self-service/foo\")\n\t\tassert.Equal(t, body, s.URL+\"/self-service/foo\")\n\t})\n\n\tt.Run(\"case=should work with default return to\", func(t *testing.T) {\n\t\ts := newServer(t, false, false, false, func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\treturn []redir.SecureRedirectOption{redir.SecureRedirectOverrideDefaultReturnTo(urlx.ParseOrPanic(ts.URL + \"/another-default\"))}\n\t\t})\n\t\t_, body := makeRequest(t, s, \"\")\n\t\tassert.Equal(t, body, s.URL+\"/another-default\")\n\t})\n\n\tt.Run(\"case=should override return_to\", func(t *testing.T) {\n\t\ts := newServer(t, false, false, false, func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\treturn []redir.SecureRedirectOption{\n\t\t\t\tredir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(ts.URL)}),\n\t\t\t\tredir.SecureRedirectUseSourceURL(\"https://foo/bar?return_to=/override\"),\n\t\t\t}\n\t\t})\n\t\t_, body := makeRequest(t, s, \"?return_to=/original\")\n\t\tassert.Equal(t, body, s.URL+\"/override\")\n\t})\n\n\tt.Run(\"case=should work with subdomain wildcard\", func(t *testing.T) {\n\t\ts := newServer(t, false, false, false, func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\treturn []redir.SecureRedirectOption{redir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"https://*.ory.sh/\")})}\n\t\t})\n\t\t_, body := makeRequest(t, s, \"?return_to=https://www.ory.sh/kratos\")\n\t\tassert.Equal(t, body, \"https://www.ory.sh/kratos\")\n\t\t_, body = makeRequest(t, s, \"?return_to=https://even.deeper.nested.ory.sh/kratos\")\n\t\tassert.Equal(t, body, \"https://even.deeper.nested.ory.sh/kratos\")\n\t})\n\n\tt.Run(\"case=should fallback to default return_to scheme\", func(t *testing.T) {\n\t\ts := newServer(t, false, false, false, func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\treturn []redir.SecureRedirectOption{\n\t\t\t\tredir.SecureRedirectAllowURLs([]url.URL{*urlx.ParseOrPanic(\"https://www.ory.sh\")}),\n\t\t\t\tredir.SecureRedirectOverrideDefaultReturnTo(urlx.ParseOrPanic(\"https://www.ory.sh/docs\")),\n\t\t\t}\n\t\t})\n\t\t_, body := makeRequest(t, s, \"?return_to=//www.ory.sh/kratos\")\n\t\tassert.Equal(t, body, \"https://www.ory.sh/kratos\")\n\t})\n\n\tt.Run(\"case=should fallback to default return_to host\", func(t *testing.T) {\n\t\ts := newServer(t, false, false, false, func(ts *httptest.Server) []redir.SecureRedirectOption {\n\t\t\treturn []redir.SecureRedirectOption{\n\t\t\t\tredir.SecureRedirectAllowURLs([]url.URL{\n\t\t\t\t\t*urlx.ParseOrPanic(\"https://www.ory.sh\"),\n\t\t\t\t\t*urlx.ParseOrPanic(\"http://www.ory.sh\"),\n\t\t\t\t}),\n\t\t\t\tredir.SecureRedirectOverrideDefaultReturnTo(urlx.ParseOrPanic(\"https://www.ory.sh/docs\")),\n\t\t\t}\n\t\t})\n\t\t_, body := makeRequest(t, s, \"?return_to=http:///kratos\")\n\t\tassert.Equal(t, body, \"http://www.ory.sh/kratos\")\n\t})\n}\n"
  },
  {
    "path": "x/require.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc RequireJSONMarshal(t *testing.T, in interface{}) []byte {\n\tvar b bytes.Buffer\n\trequire.NoError(t, json.NewEncoder(&b).Encode(in))\n\treturn b.Bytes()\n}\n"
  },
  {
    "path": "x/router.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"github.com/ory/x/httprouterx\"\n)\n\ntype PublicHandler interface {\n\tRegisterPublicRoutes(public *httprouterx.RouterPublic)\n}\n\ntype AdminHandler interface {\n\tRegisterAdminRoutes(admin *httprouterx.RouterAdmin)\n}\n\ntype Handler interface {\n\tPublicHandler\n\tAdminHandler\n}\n"
  },
  {
    "path": "x/sdkx.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nfunc SDKError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif err.Error() == \"\" {\n\t\treturn nil\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "x/sql.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport \"strings\"\n\nfunc EscapeLikePattern(s string) string {\n\treturn strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(s, \"\\\\\", \"\\\\\\\\\"), \"%\", \"\\\\%\"), \"_\", \"\\\\_\")\n}\n"
  },
  {
    "path": "x/sql_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestEscapeLikePattern(t *testing.T) {\n\tfor name, tc := range map[string]struct {\n\t\tinput    string\n\t\texpected string\n\t}{\n\t\t\"empty\": {\n\t\t\tinput:    \"\",\n\t\t\texpected: \"\",\n\t\t},\n\t\t\"no escape\": {\n\t\t\tinput:    \"foo\",\n\t\t\texpected: \"foo\",\n\t\t},\n\t\t\"escape\": {\n\t\t\tinput:    \"foo%bar_baz\\\\\",\n\t\t\texpected: \"foo\\\\%bar\\\\_baz\\\\\\\\\",\n\t\t},\n\t} {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\trequire.Equal(t, tc.expected, EscapeLikePattern(tc.input))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/stub_fs.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"io\"\n\t\"io/fs\"\n\t\"time\"\n)\n\ntype StubFS struct {\n\tname   string\n\tdata   []byte\n\toffset int\n}\n\nfunc NewStubFS(name string, data []byte) fs.FS {\n\treturn &StubFS{\n\t\tname: name,\n\t\tdata: data,\n\t}\n}\n\nfunc (stub *StubFS) Mode() fs.FileMode {\n\treturn 0444\n}\n\nfunc (stub *StubFS) ModTime() time.Time {\n\treturn time.Time{}\n}\n\nfunc (stub *StubFS) IsDir() bool {\n\treturn false\n}\n\nfunc (stub *StubFS) Sys() interface{} {\n\treturn nil\n}\n\nfunc (stub *StubFS) Stat() (fs.FileInfo, error) {\n\treturn stub, nil\n}\n\nfunc (stub *StubFS) Read(bytes []byte) (int, error) {\n\tif stub.offset >= len(stub.data) {\n\t\treturn 0, io.EOF\n\t}\n\tn := copy(bytes, stub.data[stub.offset:])\n\tstub.offset += n\n\treturn n, nil\n}\n\nfunc (stub *StubFS) Close() error {\n\treturn nil\n}\n\nfunc (stub *StubFS) Open(name string) (fs.File, error) {\n\treturn stub, nil\n}\n\nfunc (stub *StubFS) Name() string {\n\treturn stub.name\n}\n\nfunc (stub *StubFS) Size() int64 {\n\treturn int64(len(stub.data))\n}\n"
  },
  {
    "path": "x/swagger/swagger_meta.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n// Ory Kratos\n//\n// Welcome to the Ory Kratos HTTP API documentation!\n//\n//\tSchemes: http, https\n//\tHost:\n//\tBasePath: /\n//\tVersion: latest\n//\n//\tConsumes:\n//\t- application/json\n//\t- application/x-www-form-urlencoded\n//\n//\tProduces:\n//\t- application/json\n//\n//\tSecurityDefinitions:\n//\toryAccessToken:\n//\t     type: apiKey\n//\t     name: Authorization\n//\t     in: header\n//\n//\tExtensions:\n//\t---\n//\tx-request-id: string\n//\tx-forwarded-proto: string\n//\t---\n//\n// swagger:meta\npackage swagger\n"
  },
  {
    "path": "x/swagger/swagger_types_global.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage swagger\n\nimport \"github.com/ory/herodot\"\n\n// JSON API Error Response\n//\n// The standard Ory JSON API error format.\n//\n// swagger:model errorGeneric\ntype ErrorGeneric struct {\n\t// Contains error details\n\t//\n\t// required: true\n\tError GenericError `json:\"error\"`\n}\n\n// swagger:model genericError\ntype GenericError struct{ herodot.DefaultError }\n\n// Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is typically 204.\n//\n// swagger:response emptyResponse\ntype _ struct{}\n"
  },
  {
    "path": "x/swagger/swagger_types_overrides.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage swagger\n\nimport \"github.com/go-openapi/strfmt\"\n\n// swagger:model UUID\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype uuid strfmt.UUID4\n"
  },
  {
    "path": "x/tests.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc MustEncodeJSON(t *testing.T, in interface{}) string {\n\tvar b bytes.Buffer\n\trequire.NoError(t, json.NewEncoder(&b).Encode(in))\n\treturn b.String()\n}\n\nconst HostedHttpBin = \"https://ory-network-httpbin-ijakee5waq-ez.a.run.app\"\n"
  },
  {
    "path": "x/time.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"math\"\n\t\"math/rand\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar rnd = rand.New(rand.NewSource(time.Now().Unix())) //#nosec G404\n\nfunc AssertEqualTime(t *testing.T, expected, actual time.Time) {\n\tassert.EqualValues(t, expected.UTC().Round(time.Second), actual.UTC().Round(time.Second))\n}\n\nfunc RequireEqualTime(t *testing.T, expected, actual time.Time) {\n\trequire.EqualValues(t, expected.UTC().Round(time.Second), actual.UTC().Round(time.Second))\n}\n\n// RandomDelay returns a time randomly chosen from a normal distribution with mean of base and max/min of base +- deviation\n// From the docstring for the rand.NormFloat64():\n// To produce a different normal distribution, callers can\n// adjust the output using:\n//\n//\tsample = NormFloat64() * desiredStdDev + desiredMean\n//\n// Since 99.73% of values in a normal distribution lie within three standard deviations from the mean (https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule),\n// by taking the standard deviation to be deviation/3, we can get a distribution which fits our bounds nicely with minimal clipping when we take max/mins to cut off the tails.\nfunc RandomDelay(base, deviation time.Duration) time.Duration {\n\tmax := float64(base + deviation)\n\tmin := float64(base - deviation)\n\tstddev := float64(deviation) / 3\n\tsample := rnd.NormFloat64() * (stddev + float64(base))\n\tboundedSample := math.Min(math.Max(sample, min), max)\n\treturn time.Duration(boundedSample)\n}\n"
  },
  {
    "path": "x/time_test.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRandomDelay(t *testing.T) {\n\tbase := time.Millisecond * 2\n\tdeviation := time.Millisecond\n\tfor i := 0; i < 100; i++ {\n\t\tdelay := RandomDelay(base, deviation)\n\t\trequire.LessOrEqual(t, delay, base+deviation)\n\t\trequire.GreaterOrEqual(t, delay, base-deviation)\n\t}\n}\n"
  },
  {
    "path": "x/token_prefixes.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nconst OrySessionToken = \"ory_st_\"\nconst OryLogoutToken = \"ory_lo_\"\n"
  },
  {
    "path": "x/transaction.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"context\"\n\n\t\"github.com/ory/pop/v6\"\n)\n\ntype (\n\tTransactionPersistenceProvider interface {\n\t\tTransactionalPersisterProvider() TransactionalPersister\n\t}\n\n\tTransactionalPersister interface {\n\t\tTransaction(ctx context.Context, callback func(ctx context.Context, connection *pop.Connection) error) error\n\t}\n)\n"
  },
  {
    "path": "x/uuid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage x\n\nimport (\n\t\"github.com/gofrs/uuid\"\n)\n\nvar EmptyUUID uuid.UUID\n\nfunc NewUUID() uuid.UUID {\n\treturn uuid.Must(uuid.NewV4())\n}\n\nfunc ParseUUID(in string) uuid.UUID {\n\treturn uuid.FromStringOrNil(in)\n}\n"
  },
  {
    "path": "x/webauthnx/aaguid/aaguid.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n//go:generate curl https://raw.githubusercontent.com/passkeydeveloper/passkey-authenticator-aaguids/main/aaguid.json --output passkey-aaguids.json\n\npackage aaguid\n\nimport (\n\t_ \"embed\"\n\t\"encoding/json\"\n\t\"maps\"\n\n\t\"github.com/gofrs/uuid\"\n)\n\nvar (\n\t//go:embed aaguids.json\n\trawAAGUIDs []byte\n\t//go:embed passkey-aaguids.json\n\trawPasskeyAAGUIDs []byte\n\taaguids           map[string]AAGUID\n)\n\ntype AAGUID struct {\n\tName      string `json:\"name\"`\n\tIconDark  string `json:\"icon_dark\"`\n\tIconLight string `json:\"icon_light\"`\n}\n\nfunc init() {\n\terr := json.Unmarshal(rawAAGUIDs, &aaguids)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tvar passkeyAAGUIDs map[string]AAGUID\n\terr = json.Unmarshal(rawPasskeyAAGUIDs, &passkeyAAGUIDs)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tmaps.Copy(aaguids, passkeyAAGUIDs)\n}\n\nfunc Lookup(id []byte) *AAGUID {\n\tuid, err := uuid.FromBytes(id)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tif aaguid, ok := aaguids[uid.String()]; ok {\n\t\treturn &aaguid\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/webauthnx/aaguid/aaguid_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage aaguid\n\nimport (\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLookup(t *testing.T) {\n\tgoodUUIDBytes := uuid.Must(uuid.FromString(\"adce0002-35bc-c60a-648b-0b25f1f05503\")).Bytes()\n\tbadUUIDBytes := uuid.Must(uuid.NewV4()).Bytes()\n\n\ttests := []struct {\n\t\tname string\n\t\tid   []byte\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"GoodUUID\",\n\t\t\tid:   goodUUIDBytes,\n\t\t\twant: \"Chrome on Mac\",\n\t\t},\n\t\t{\n\t\t\tname: \"BadUUID\",\n\t\t\tid:   badUUIDBytes,\n\t\t},\n\t\t{\n\t\t\tname: \"NilUUID\",\n\t\t\tid:   nil,\n\t\t},\n\t\t{\n\t\t\tname: \"EmptyUUID\",\n\t\t\tid:   []byte{},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tres := Lookup(tt.id)\n\t\t\tif tt.want == \"\" {\n\t\t\t\tassert.Nil(t, res)\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, tt.want, res.Name)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/webauthnx/aaguid/aaguids.json",
    "content": "{\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  \"08987058-cadc-4b81-b6e1-30de50dcbe96\": {\n    \"name\": \"Windows Hello Hardware Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAACkUlEQVR42uyai3GDMAyGQyegGzACnaCMkBHoBhkhnSAj0A2SDaAT0E6QbEA3cOXW6XEpBtnImMv9utOllxjF/qKHLTdRSm0gdnkAAgACIAACIAACIAACIAgAARAAARAAARAAARBEAFCSJINKkpLuSTtSZbQz76W25zhKkpFWPbtaz6Q75vPuoluuPmqxlZK2yi76s9RznjlpN2K7CrFWaUAHNS0HT0Atw3YpDSjxbdoPuaziG3uk579cvIdeWsbQD7L7NAYoWpKmLy8chueO5reB7KKKrQnQJdDYn9AJZHc5QBT7enINY2hjxrqItsvJWSdxFxKuYlOlWJmE6zPPcsJuN7WFiF7me5DOAws4OyZyG6TOsr/KQziDaJm/mcy2V1V0+T0JeXxqqlrWC9mGGy3O6wwFaI0SdR+EMg9AEAACIAByqViZb+/prgFdN6qb306j3lTWs0BJ76Qjw0ktO+3ad60PQhMrfM9YwqK7lUPe4j+/OR40cDaqJeJ+xo80JsWih1WTBAcb8ysKrb+TfowQKy3v55wbBkk49FJbQusqr4snadL9hEtXC3nO1G1HG6UfxIj5oDnJlHPOVVAerWGmvYQxwc70hiTh7Bidy3/3ZFE6isxf8epNhUCl4n5ftYqWKzMP3IIquaFnquXO0sZ1yn/RWq69SuK6GdPXORfSz4HPnk1bNXO0+UZze5HqKIodNYwnHVVcOUivNcStxj4CGFYhWAWgXgmuF4JzdMhn6wDUm1DpmFyVY7IvQqeTRdod2v2F8lNn/gcpW+rUsOi9mAmFwlSo3Pw9JQ3p+8bhgnAMkPM613BxOBQqc2FEB4SmPQSAAAiAAAiAAAiAAAiAIAAEQAAEQAAEQPco3wIMADOXgFhOTghuAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAACkUlEQVR42uyai3GDMAyGQyegGzACnaCMkBHoBhkhnSAj0A2SDaAT0E6QbEA3cOXW6XEpBtnImMv9utOllxjF/qKHLTdRSm0gdnkAAgACIAACIAACIAACIAgAARAAARAAARAAARBEAFCSJINKkpLuSTtSZbQz76W25zhKkpFWPbtaz6Q75vPuoluuPmqxlZK2yi76s9RznjlpN2K7CrFWaUAHNS0HT0Atw3YpDSjxbdoPuaziG3uk579cvIdeWsbQD7L7NAYoWpKmLy8chueO5reB7KKKrQnQJdDYn9AJZHc5QBT7enINY2hjxrqItsvJWSdxFxKuYlOlWJmE6zPPcsJuN7WFiF7me5DOAws4OyZyG6TOsr/KQziDaJm/mcy2V1V0+T0JeXxqqlrWC9mGGy3O6wwFaI0SdR+EMg9AEAACIAByqViZb+/prgFdN6qb306j3lTWs0BJ76Qjw0ktO+3ad60PQhMrfM9YwqK7lUPe4j+/OR40cDaqJeJ+xo80JsWih1WTBAcb8ysKrb+TfowQKy3v55wbBkk49FJbQusqr4snadL9hEtXC3nO1G1HG6UfxIj5oDnJlHPOVVAerWGmvYQxwc70hiTh7Bidy3/3ZFE6isxf8epNhUCl4n5ftYqWKzMP3IIquaFnquXO0sZ1yn/RWq69SuK6GdPXORfSz4HPnk1bNXO0+UZze5HqKIodNYwnHVVcOUivNcStxj4CGFYhWAWgXgmuF4JzdMhn6wDUm1DpmFyVY7IvQqeTRdod2v2F8lNn/gcpW+rUsOi9mAmFwlSo3Pw9JQ3p+8bhgnAMkPM613BxOBQqc2FEB4SmPQSAAAiAAAiAAAiAAAiAIAAEQAAEQAAEQPco3wIMADOXgFhOTghuAAAAAElFTkSuQmCC\"\n  },\n  \"9ddd1817-af5a-4672-a2b9-3e3dd95000a9\": {\n    \"name\": \"Windows Hello VBS Hardware Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAACkUlEQVR42uyai3GDMAyGQyegGzACnaCMkBHoBhkhnSAj0A2SDaAT0E6QbEA3cOXW6XEpBtnImMv9utOllxjF/qKHLTdRSm0gdnkAAgACIAACIAACIAACIAgAARAAARAAARAAARBEAFCSJINKkpLuSTtSZbQz76W25zhKkpFWPbtaz6Q75vPuoluuPmqxlZK2yi76s9RznjlpN2K7CrFWaUAHNS0HT0Atw3YpDSjxbdoPuaziG3uk579cvIdeWsbQD7L7NAYoWpKmLy8chueO5reB7KKKrQnQJdDYn9AJZHc5QBT7enINY2hjxrqItsvJWSdxFxKuYlOlWJmE6zPPcsJuN7WFiF7me5DOAws4OyZyG6TOsr/KQziDaJm/mcy2V1V0+T0JeXxqqlrWC9mGGy3O6wwFaI0SdR+EMg9AEAACIAByqViZb+/prgFdN6qb306j3lTWs0BJ76Qjw0ktO+3ad60PQhMrfM9YwqK7lUPe4j+/OR40cDaqJeJ+xo80JsWih1WTBAcb8ysKrb+TfowQKy3v55wbBkk49FJbQusqr4snadL9hEtXC3nO1G1HG6UfxIj5oDnJlHPOVVAerWGmvYQxwc70hiTh7Bidy3/3ZFE6isxf8epNhUCl4n5ftYqWKzMP3IIquaFnquXO0sZ1yn/RWq69SuK6GdPXORfSz4HPnk1bNXO0+UZze5HqKIodNYwnHVVcOUivNcStxj4CGFYhWAWgXgmuF4JzdMhn6wDUm1DpmFyVY7IvQqeTRdod2v2F8lNn/gcpW+rUsOi9mAmFwlSo3Pw9JQ3p+8bhgnAMkPM613BxOBQqc2FEB4SmPQSAAAiAAAiAAAiAAAiAIAAEQAAEQAAEQPco3wIMADOXgFhOTghuAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAACkUlEQVR42uyai3GDMAyGQyegGzACnaCMkBHoBhkhnSAj0A2SDaAT0E6QbEA3cOXW6XEpBtnImMv9utOllxjF/qKHLTdRSm0gdnkAAgACIAACIAACIAACIAgAARAAARAAARAAARBEAFCSJINKkpLuSTtSZbQz76W25zhKkpFWPbtaz6Q75vPuoluuPmqxlZK2yi76s9RznjlpN2K7CrFWaUAHNS0HT0Atw3YpDSjxbdoPuaziG3uk579cvIdeWsbQD7L7NAYoWpKmLy8chueO5reB7KKKrQnQJdDYn9AJZHc5QBT7enINY2hjxrqItsvJWSdxFxKuYlOlWJmE6zPPcsJuN7WFiF7me5DOAws4OyZyG6TOsr/KQziDaJm/mcy2V1V0+T0JeXxqqlrWC9mGGy3O6wwFaI0SdR+EMg9AEAACIAByqViZb+/prgFdN6qb306j3lTWs0BJ76Qjw0ktO+3ad60PQhMrfM9YwqK7lUPe4j+/OR40cDaqJeJ+xo80JsWih1WTBAcb8ysKrb+TfowQKy3v55wbBkk49FJbQusqr4snadL9hEtXC3nO1G1HG6UfxIj5oDnJlHPOVVAerWGmvYQxwc70hiTh7Bidy3/3ZFE6isxf8epNhUCl4n5ftYqWKzMP3IIquaFnquXO0sZ1yn/RWq69SuK6GdPXORfSz4HPnk1bNXO0+UZze5HqKIodNYwnHVVcOUivNcStxj4CGFYhWAWgXgmuF4JzdMhn6wDUm1DpmFyVY7IvQqeTRdod2v2F8lNn/gcpW+rUsOi9mAmFwlSo3Pw9JQ3p+8bhgnAMkPM613BxOBQqc2FEB4SmPQSAAAiAAAiAAAiAAAiAIAAEQAAEQAAEQPco3wIMADOXgFhOTghuAAAAAElFTkSuQmCC\"\n  },\n  \"6028b017-b1d4-4c02-b4b3-afcdafc96bb2\": {\n    \"name\": \"Windows Hello Software Authenticator\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAACkUlEQVR42uyai3GDMAyGQyegGzACnaCMkBHoBhkhnSAj0A2SDaAT0E6QbEA3cOXW6XEpBtnImMv9utOllxjF/qKHLTdRSm0gdnkAAgACIAACIAACIAACIAgAARAAARAAARAAARBEAFCSJINKkpLuSTtSZbQz76W25zhKkpFWPbtaz6Q75vPuoluuPmqxlZK2yi76s9RznjlpN2K7CrFWaUAHNS0HT0Atw3YpDSjxbdoPuaziG3uk579cvIdeWsbQD7L7NAYoWpKmLy8chueO5reB7KKKrQnQJdDYn9AJZHc5QBT7enINY2hjxrqItsvJWSdxFxKuYlOlWJmE6zPPcsJuN7WFiF7me5DOAws4OyZyG6TOsr/KQziDaJm/mcy2V1V0+T0JeXxqqlrWC9mGGy3O6wwFaI0SdR+EMg9AEAACIAByqViZb+/prgFdN6qb306j3lTWs0BJ76Qjw0ktO+3ad60PQhMrfM9YwqK7lUPe4j+/OR40cDaqJeJ+xo80JsWih1WTBAcb8ysKrb+TfowQKy3v55wbBkk49FJbQusqr4snadL9hEtXC3nO1G1HG6UfxIj5oDnJlHPOVVAerWGmvYQxwc70hiTh7Bidy3/3ZFE6isxf8epNhUCl4n5ftYqWKzMP3IIquaFnquXO0sZ1yn/RWq69SuK6GdPXORfSz4HPnk1bNXO0+UZze5HqKIodNYwnHVVcOUivNcStxj4CGFYhWAWgXgmuF4JzdMhn6wDUm1DpmFyVY7IvQqeTRdod2v2F8lNn/gcpW+rUsOi9mAmFwlSo3Pw9JQ3p+8bhgnAMkPM613BxOBQqc2FEB4SmPQSAAAiAAAiAAAiAAAiAIAAEQAAEQAAEQPco3wIMADOXgFhOTghuAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAACkUlEQVR42uyai3GDMAyGQyegGzACnaCMkBHoBhkhnSAj0A2SDaAT0E6QbEA3cOXW6XEpBtnImMv9utOllxjF/qKHLTdRSm0gdnkAAgACIAACIAACIAACIAgAARAAARAAARAAARBEAFCSJINKkpLuSTtSZbQz76W25zhKkpFWPbtaz6Q75vPuoluuPmqxlZK2yi76s9RznjlpN2K7CrFWaUAHNS0HT0Atw3YpDSjxbdoPuaziG3uk579cvIdeWsbQD7L7NAYoWpKmLy8chueO5reB7KKKrQnQJdDYn9AJZHc5QBT7enINY2hjxrqItsvJWSdxFxKuYlOlWJmE6zPPcsJuN7WFiF7me5DOAws4OyZyG6TOsr/KQziDaJm/mcy2V1V0+T0JeXxqqlrWC9mGGy3O6wwFaI0SdR+EMg9AEAACIAByqViZb+/prgFdN6qb306j3lTWs0BJ76Qjw0ktO+3ad60PQhMrfM9YwqK7lUPe4j+/OR40cDaqJeJ+xo80JsWih1WTBAcb8ysKrb+TfowQKy3v55wbBkk49FJbQusqr4snadL9hEtXC3nO1G1HG6UfxIj5oDnJlHPOVVAerWGmvYQxwc70hiTh7Bidy3/3ZFE6isxf8epNhUCl4n5ftYqWKzMP3IIquaFnquXO0sZ1yn/RWq69SuK6GdPXORfSz4HPnk1bNXO0+UZze5HqKIodNYwnHVVcOUivNcStxj4CGFYhWAWgXgmuF4JzdMhn6wDUm1DpmFyVY7IvQqeTRdod2v2F8lNn/gcpW+rUsOi9mAmFwlSo3Pw9JQ3p+8bhgnAMkPM613BxOBQqc2FEB4SmPQSAAAiAAAiAAAiAAAiAIAAEQAAEQAAEQPco3wIMADOXgFhOTghuAAAAAElFTkSuQmCC\"\n  },\n  \"dd4ec289-e01d-41c9-bb89-70fa845d4bf2\": {\n    \"name\": \"Apple iCloud Keychain (Managed)\"\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  \"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  \"39a5647e-1853-446c-a1f6-a79bae9f5bc7\": {\n    \"name\": \"IDmelon Android 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  \"6e8248d5-b479-40db-a3d8-11116f7e8349\": {\n    \"name\": \"Bitwarden\",\n    \"icon_dark\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMywgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9Ikljb24iIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEwMjQgMTAyNDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiMxNzVEREM7fQoJLnN0MXtmaWxsOiNGRkZGRkY7fQo8L3N0eWxlPgo8cmVjdCBpZD0iQmFja2dyb3VuZCIgY2xhc3M9InN0MCIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIvPgo8cGF0aCBpZD0iSWRlbnRpdHkiIGNsYXNzPSJzdDEiIGQ9Ik04MjkuOCwxMjguNmMtNi41LTYuNS0xNC4yLTkuNy0yMy05LjdIMjE3LjJjLTguOSwwLTE2LjUsMy4yLTIzLDkuN3MtOS43LDE0LjItOS43LDIzdjM5My4xCgljMCwyOS4zLDUuNyw1OC40LDE3LjEsODcuM2MxMS40LDI4LjgsMjUuNiw1NC40LDQyLjUsNzYuOGMxNi45LDIyLjMsMzcsNDQuMSw2MC40LDY1LjNzNDUsMzguNyw2NC43LDUyLjcKCWMxOS44LDE0LDQwLjQsMjcuMiw2MS45LDM5LjdzMzYuOCwyMC45LDQ1LjgsMjUuM2M5LDQuNCwxNi4zLDcuOSwyMS43LDEwLjJjNC4xLDIsOC41LDMuMSwxMy4zLDMuMWM0LjgsMCw5LjItMSwxMy4zLTMuMQoJYzUuNS0yLjQsMTIuNy01LjgsMjEuOC0xMC4yYzktNC40LDI0LjMtMTIuOSw0NS44LTI1LjNjMjEuNS0xMi41LDQyLjEtMjUuNyw2MS45LTM5LjdjMTkuOC0xNCw0MS40LTMxLjYsNjQuOC01Mi43CgljMjMuNC0yMS4yLDQzLjUtNDIuOSw2MC40LTY1LjNjMTYuOS0yMi40LDMxLTQ3LjksNDIuNS03Ni44YzExLjQtMjguOCwxNy4xLTU3LjksMTcuMS04Ny4zdi0zOTMKCUM4MzkuNiwxNDIuOCw4MzYuMywxMzUuMSw4MjkuOCwxMjguNnogTTc1My44LDU0OC40YzAsMTQyLjMtMjQxLjgsMjY0LjktMjQxLjgsMjY0LjlWMjAzaDI0MS44Qzc1My44LDIwMyw3NTMuOCw0MDYuMSw3NTMuOCw1NDguNHoKCSIvPgo8L3N2Zz4K\",\n    \"icon_light\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMywgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9Ikljb24iIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEwMjQgMTAyNDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiMxNzVEREM7fQoJLnN0MXtmaWxsOiNGRkZGRkY7fQo8L3N0eWxlPgo8cmVjdCBpZD0iQmFja2dyb3VuZCIgY2xhc3M9InN0MCIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIvPgo8cGF0aCBpZD0iSWRlbnRpdHkiIGNsYXNzPSJzdDEiIGQ9Ik04MjkuOCwxMjguNmMtNi41LTYuNS0xNC4yLTkuNy0yMy05LjdIMjE3LjJjLTguOSwwLTE2LjUsMy4yLTIzLDkuN3MtOS43LDE0LjItOS43LDIzdjM5My4xCgljMCwyOS4zLDUuNyw1OC40LDE3LjEsODcuM2MxMS40LDI4LjgsMjUuNiw1NC40LDQyLjUsNzYuOGMxNi45LDIyLjMsMzcsNDQuMSw2MC40LDY1LjNzNDUsMzguNyw2NC43LDUyLjcKCWMxOS44LDE0LDQwLjQsMjcuMiw2MS45LDM5LjdzMzYuOCwyMC45LDQ1LjgsMjUuM2M5LDQuNCwxNi4zLDcuOSwyMS43LDEwLjJjNC4xLDIsOC41LDMuMSwxMy4zLDMuMWM0LjgsMCw5LjItMSwxMy4zLTMuMQoJYzUuNS0yLjQsMTIuNy01LjgsMjEuOC0xMC4yYzktNC40LDI0LjMtMTIuOSw0NS44LTI1LjNjMjEuNS0xMi41LDQyLjEtMjUuNyw2MS45LTM5LjdjMTkuOC0xNCw0MS40LTMxLjYsNjQuOC01Mi43CgljMjMuNC0yMS4yLDQzLjUtNDIuOSw2MC40LTY1LjNjMTYuOS0yMi40LDMxLTQ3LjksNDIuNS03Ni44YzExLjQtMjguOCwxNy4xLTU3LjksMTcuMS04Ny4zdi0zOTMKCUM4MzkuNiwxNDIuOCw4MzYuMywxMzUuMSw4MjkuOCwxMjguNnogTTc1My44LDU0OC40YzAsMTQyLjMtMjQxLjgsMjY0LjktMjQxLjgsMjY0LjlWMjAzaDI0MS44Qzc1My44LDIwMyw3NTMuOCw0MDYuMSw3NTMuOCw1NDguNHoKCSIvPgo8L3N2Zz4K\"\n  },\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  \"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  \"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,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  \"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  \"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+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT54bXAuZGlkOjc5MDY4MzA0NzNCODExRURCRTM1OEMyNENERDkyQzE1PC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+8bsE2gAAJc9JREFUeJzt3XmYZGVh7/FvdffsKzPDIDsIShIU92gARVFApxRc4nKpmE1NYtTEuGa9RnO9iUtQE6/GNRpTeN0iLjWiIJpg3AIoIOiNjCyDwzYDMz17L1X3j7dLipo6p6uq69Rbp+r7eZ5+eqb7LG/NdJ9fvXuhVqshafgVLt5cqP9x7nMNoHbhplobxxXqxzdfNuWWjcePNf39F3+u33/uvr+4T3O5NNwKhpGUjYaHOtD2Qz/puMZr1ToMkLTAaDyn8fhWn+H+UKk1nVdNOK5VWWoN57UMqKbvHRJc9ddsYA0Pw0jqkebwaXVI02do/QBuDJ+kB3rzNRuPaT63HhaNATHWcHy14bza3PeqHBpijfceA2Ybzmu+Z3Xu83jTter3qDYc31xrqqufV23xvV8Et6E0HAwjqQvzBE9z6LQKh8aH8Dj31zaaj0u6TnOA1B/6jTWXxuCBBwZQ4/ebX0tzcNTPqV+nuVaUdJ3moJ1puEa97NW5j8ZgbC5L/WuzJLNpL+cMI6lNbdR8Gh+09c/ND+T6cWn9MfUHf/MvZ3MfTOM1m8OpVS0s7Xv10BnngWWql6Xa9PfmQC1wf02pMZxmgQkeGDbNtad6LasesvW/N6uXcbbh7y3VLtzUqjalAWYYSW1oCqJWodT8jr/xa43nFFr8uTGYai3Oq/LAkKDh+FZ9OPV71wOq8XqzPDAMm8vTqsZUv8cED2w6a1WucVqHUqsaTz2AGsOpsfmuXl6avl5tcV4rh/StaXAZRlKKFrWh5r+PJXxuftjXv9748G4OgeZzW9U66sbnPjdeq9UAgcaQqDZ8bjx2vOGc8ab7NIZX40djDaa5v4mmezXXmurn12tNM3PnTDW8pubAmWn6euP3m5s3H/DZQMqHidgFkAZVQm2o+WuNtYwxDn1wQ/g9a/xa40O6Maia+2Wam8zq6g/6xnvUv16vEU1waCA016Ka+4AaA46G48carlW/z0TDsfV7NYdR/TU11lwaaz/1mtH43HkTc38+2PD95us0hlO16WuNxx4yEk+DzZqR1CShb6hV81tSENU/j/PA0Kh/NL4JnGi6VqtaTWMY1IOl3hzWqv+msVmu8c9w/8O/VcAtbrp//dj6dabnjml86DcH1CywaO5r0w3HtWpmq9d26l+r/3167pyDTcfXj0k6rzGUmmtH9iENuLbDqFCYr+92BJQrK4HDgQ3AemAZsGbuuyuxpjksksKo+XOrgBrn0ICqf735vFYfNQ69ZnPfTKsmucbRdI1lbjXyrbm5r3mOUNK96tdorJm1+l49FBtrao2DIBqDosoDazj1AQr1z62+3uqcxtCi4fppg0B6ZRbYPffnSWA/sGPu425Kxd1JJ46KdnLGMGpWriwCfgU4DXg4cDJwAvBg7g8eSWrXbuBnwC3AFuB64FrgRkrFgynnDQ3DqB3lyhHAmXMfZwCPwhqOpOzNAtcB/wl8C7iSUnFb3CJlwzBqpVwZA34NeAawiRA+kjQIrge+MvdxJaVi2kTf3DCM6sqVAnA68CLgecCRcQskSfO6B/g34FPAv1Mq5nYQhmFUrjwI+F3gpcCJkUsjSd26HfgI8BFKxa2xC9Op0Q2jcuUM4DXABdw/ikmS8q4GbAYuolS8InZh2jVaYRSa4i4A3kDoE5KkYXYN8A7g04PehDc6YVSuPBt4E/DIuAWRpL77MfAW4FOUigO5isHwh1G5cjpwEfD42EWR5tE8GTPpc+Nk01ZLENHiawP4y6kIfgi8jlLx67EL0mx4w6hcOQ54G2F0nCTpfl8ghNJNsQtSN3xhVK6MA68G/oawFI8k6VBTwFuBv6VUnJ7v4KwNVxiVK48gDG18TNyCSFJu3AC8hFLxezELMRxhFFZMeAOhNuQyPZLUmSrwt8BfUyrOzHdwFvIfRuXKMcAngCf3/+aSNFS+B5QoFbf0+8bt5EzzXieDo1x5GmF0yJPjFkSShsLjgWsoVy6IXZBWBq9mFCavvpHQ+Ta4YSlJ+fVW4E39Wog1f8105coS4OPAC7O/mSSNtArwQkrFvVnfKF9hVK6sJ4yPPyPbG0mS5vwAKFIq3pHlTfITRmGgwhXAQ7K7iSSpha3A2VlOks3HAIZy5WTguxhEkhTDscB3KFceHrMQccMovPgrgaOjlkOSRtsG4JuUK9HW+YzXTBdqRN8CjujthSVJXdoJPIlS8fpeXnRwm+nKlWOBb2IQSdIgWQtcQbny0H7fuP9hVK5sIAxWsGlOkgZPeEaXK319Rvc3jMqVpYTh2yf39b6SpE4cDVQoV1b164b9C6OwssK/AKf37Z6SpG49AvjM3NY9metnzegvgOf38X6SpIU5j7CRaeb6M5quXDkP2EzsoeSSpG68gFLxM92ePBgrMIQtwn8IHNbdBSRJke0FHkup+JNuTo4/tDu0NZYxiCQpz1YAn5xbzDoTWTeb/RlwZsb3kCRl75GErScykV0zXbnyWMKac30ZiSFJ6ouzKRW/0ckJ8fqMypUJ4CrC0EBJ0vC4CTiNUnF/uyfE7DN6PQaRJA2jk4G/7vVFe18zKlceDNwIZNbRJUmKahZ4NKXide0cHKtm9E4MIkkaZuPAu3t5wd6GUbnyFOA5Pb2mJGkQPYVypWfP+94104W1564hDP+TJA2/LcAvUSrOpB3U72a6F2IQSdIoOQl4aS8u1JuaUbkyBvwI+OVeFEqSlBtbgZMpFaeSDuhnzegFGESSNIqOBX53oRfpVRi9sUfXkSTlz+vnWsi6tvAwKleehn1FkjTKHgw8dyEX6EXN6LU9uIYkKd8WlAULG8BQrpxIGNq3gJ33JElD4pGUitc2f7EfAxh+F4NIkhR0Pcy7+5pR2DhvK3BktzeXJA2VXcCRzSt6Z10zOgeDSJJ0vzXABd2cuJAwev4CzpUkDaeusqG7ZrpyZRFwF3BYNzeVJA2tA8DhlIp76l/IspnuKRhEkqRDLQU2dXpSt2HU8Y0kSSOjb2H0jC7PkyQNv6fPbSvUts7DKGwr/tCOz5MkjYojgEd1ckI3NaOzujhHkjRaOsqKbsLo9C7OkSSNlo6yopswOrOLcyRJo6WjrOhsnlG5sgbY2UWhJEmj53hKxduymGf08O7KI0kaQae1e2CnYdT2hSVJI88wkiRFl1kYndzh8ZKk0dV2ZnQaRid2eLwkaXS1nRntj6a7ePM4YTXWiS4LJUkaPWtqF26anO+gTmpGD8IgkiR15th2DuokjDZ2WRBJ0uhqKzs6CaMNXRZEkjS62sqOTsJofZcFkSSNrrayo5MwWt5lQSRJo6ut7OgkjFZ3WRBJ0uhqKzu63elVkqSe6SSMVmZWCknSsGorOzoJI+cYSZI61VZ22EwnSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKy1FbOGEaSpCwV2jnIMJIkZcmakSQpHwwjSVJ0hpEkKUu1dg4yjCRJ0RlGkqQsOYBBkhTdeDsHGUaSpCxNFC7ePO9cI8NIkpQla0aSpHwwjCRJWaq2c5BhJEnK0mztwk3zzjUyjCRJWZpp5yDDSJKUJZvpJEn5YBhJkrLkfkaSpOgMI0lSPhhGkqQsOYBBkhSdYSRJis7N9SRJ0VkzkiTlg2EkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKbqJ2AUYBKvG4XHL4KQlcPxiOGIRLC7AskL4/mQVpmuwdQpumYIfHYAbDsBsW/sXSsq7VeNw+nLYXQ3blh6owlQNZmqwd27ruPr39s89L9SZkQ2jBy2C56yBZ66GU5d1XkXcU4Xv7IXP74LLJuHgAP7wffx4eOiSOPfePQvnbuntNf/uKDhrZW+v2Ynn3gx3TPfuei9ZDy9d37vrQXgI3jMD/30QrtkHV+yBHTO9vUcvvOVIOGdV8vf3VOEZW8LDfhActwg+dFxn58zUQjBBeEMLsHosBNaOWbh3Bm6dgmsPwA/2wXX729wSdUiNXBiduhRevRHOXQWFBVxn5Vj4ZTpnVfjF+cS98IHtcO9sz4q6YEdMwNGL4tx7dwYNwBsivh6A8R5fb/V4Nq/nhMXwuOVQOiw83L6xGz64I7x5GgRLx+AFa2H5PD8jZ66Ab+7pS5EyMVEINSq4/3Pd6nE4cTE8Zjk8d+5rd8/A1yahfF9oeRk1I9NndPQi+OCx8JWT4LwFBlGzlWPw8g3w7YfCqw4PP4TSIBgDnroKPnUCfOy4uGFed/bK+YMI4Nlrsi/LINk4Ab+xLjyj/u8J8GsrYpeov0YijH5rHVx+Mjx9dbb3WT4Gr98Yfpgevizbe0mdOnsVfG3uzVhMF7QZMueuDn23o+j0FeENxPuPhaMG4A1EPwx1GC0bg388Bv7mSFjRx1d6yhL4/Inw/LX9u6fUjlXj8IHj4MXr4tx/5VgIxXaPfVrk4IytuBq+elL2b6QHwdCG0foJ+LcT238X1mimFtpvb54KI+junO68Y3FxAf7+aPizI3rbJCgt1BjhDdqmCA+4c1fDkg5+IZ45Ag/h+awZD10Mw/4sGcoBDOsnQpvrKW2OJNtfhct2w3/sgav3hxEuzaN4FhXC0O9fXQ5PXhk+2ukbevmG0GH7pjs6fhmZufEAvPnObO/Rz1FQ22fgFbdnf597+jgq7bM74TM7OztnWQGOWwyPXR5qHytT3mqOEd4s3XAg/Lz3S6dvDs9ZHVo19g74MLOr9rU3eGlfNbyedeNw8pIQNO16+YYwKOm124ZzWsnQhdHSMfjoce0F0bZpeN92+LedYURcmuka/ORA+PiXe8PIrhevg5euO3SkTLPfWRcemP94T9svI1OTs4MzsqoXDtaG6/UAbJ3u/jV97N7wwHvxOvijw5NDacUYvPVI+I1buy9nJ9aOwxM77JRfUghNdV/YlU2ZeuUdd3f3/3XCYnjKSti0Bh6/fP7jn7sW7pqBv72r83sNuqFqpisA7zwKHjXP4IGpWvjPfOJPQ7DMF0StbJ+Bd90NZ/40vIudz+s3wvkjNjpI8eytwj9th3NuSh8m/KSVcEafRm1tWt26NeG2qfSWg2EeVXfLFPzzvfD8m+HpW2Dz5PznvHxDCLBhM1Rh9JL18z/wtxwM/+nv396bWdL3zcJrfg6/tzVUwdNcdDQ8JNIkVI2mn0/DhbekT9bt9cTbJEm/m5VJ+PJkcr/sk1Z21pyVVzcegD/YCi+6BW6fZ3L1RUeHmuYwGZowesgS+NMj0o/59l644Ga46WDv73/pJDzv5vR248UF+IdjQv+T1C/3zcKfp9Q8zlqZ/YPtiAl4QkIN7KuToT/uuwnNXIsK8IwRGsjw7b1h9YnLdycfs34C/nye513eDEUYFYB3Hp0+J+E7e+F3bgv9JVm54UB4F5rW7HfqUnjlhuzKILXy9d3wg/2tvzdRgCdm3OyzaXXrh8226fvLldYvNGpN3Ltm4WVb4XM7k4950WHwsKV9K1LmhiKMLliT3k90+3RoRtvfhxE5Nx6AV25NP+YPNoS18aR+Shudd1rGD7Vnr2399cpkWKsN4CuTyaMwT18RagOjZLYGr9sGV+9LPuaNQ1Q7yn0YTRTS/0Nma/DyreGdRr9csSf0SSVZNgav29i/8kgQpi4kOWZxdvc9elHym8VLGzrsd87CvyeUcQx41gg11dXN1uCPf568EPNZK+G0IVntJfdh9KzV6ettfXgHXJvQPJGli+4Ok2aTPHdNaEeX+uW2qbD1QSvrM+wzSppbdPfMoe/6v5jSVPesEWuqq7ttKv3N7YsP619ZspT7MEobCXTPDFwUaW7PwRq8OaXTeKIQb0kWja4Yq8onhcjmFiPovrY7uRbw2OWDsdBrDB/YHmqOrZy/Jn2Cc17k+iX80tL0BUnft70//URJrtiT3GkM8Otrh3t5Dw2efi88etKSMGinlVZzavZWw/5grRQIa7WNor3VMCeylWUdrPc3yHIdRmnrVu2ehYvv619ZkvxTSvX6qJS2dKnXFhVgXULTcFary5yf8Du6Ywb+K6Fj/sspEz+TBkKMgk+mPM+GYUHZXIfReSlh9NldcWtFdZftTl/TLO01SL30mOXJv/BZNd8lDcm+dHfy+mqX706eHvGwpWEJnVH08+nkAO90maVBlNswWjuevv7clwdkLauZWhiymuRX21iPSuqFs1PmEt2cwUTwU5eGZrpW0n4/p2phImySblbiHxZJow3XT8AxOe9Py20YPTrlIb5zFq6JMIIuyWUpM6lPWza6G4ipf1aMwQtTRl1dlcHvS1KtaNcsfC9l7gw4ATbJ91P+3fI+xDu3YfTLKbWia/YN1hLrV+9LbpNfVIATR7TZQf3zJxvhsITh2/uqYQmaXiqQ0kSXMrm17lt7k5sOH7IkDF4aRT9JWfQ2782XuQ2j41L+4a9N+Q+LYU8VfpbSDJLlhEPpaavSp0Bcsit5/lG3HrUseRh2pY2Vqedr3k4aGDHsds4mT+DP+/bkuZ12mdY+2s/Nwtp161Ry+3m/5048ejl856G9v+6Hd8BHdvT+uvN50EQ2r6cyCf8r400IszRRgFdsCLWipHedM7X0EZ/dSurX2T0baj3tuGQnlBKaFs9fA2+/u6ui5d59s61XMd+Y26d5kNvir0yZMX7nPMuvx/DzlDL1exXvxYVsAnB1pCXtxzN6PetyukT/+omwMslL1sPx89S6P7Qj7KnTS2NAMSGMLtvd/i7A/7UvrNLQ6iF73GJ45DL44QD1DfdL0lY1Yznve85tGC1J+YfPcmXubiXNKgdYndvGUmXl/NXJk0VbOViF5WMhfE5a0t5k6h8dgL/PoHbxhBXJ79LT5hA1qxIGMrwsoYnxWWtGM4yS5H0VhtyGUdq/+wCNXfiFqbRC5fwdjXrvpCXJzbq9cPs0vOS2eX4uu5S0/M+eavpira18KSWMzl8Db70zeVO+YZU0+jbnWZTf8qf9AC4bwFe1NCVwdg9gTU7D67r9YZvrtN1fuzVRSF4Z5Ru7Ow+/H+4PC4W2csREWK9u1GTxBmIQDOBjuz1p/yFJQ1hjStuLpRfbn0vzma2F9Rqfd3N6H+ZCPHFF8hbh7YyiayVtJe/nrO3umnk2nvDGNq0rIA9y20y3PWWJnUEcKp02+q/fAy7++yC8467eX/emSKMYd8zAn27r/XWzemD3WxXYvCusYH9TBistNEoaRbevGhYO7sYXJ+GVh7f+3tNXwV8WBmteYdbWJFQh0pYdy4PchtHWlAdF2jJBMYwBp6R0Rt/a54fevTPw1ZRVIfLmQG24Xk8v1Ai7Dl86CZ/d2Z9gXVKAcxOa6L65p/u5TD85AP/vYOvf6/UToTb2zS6DLm8mCrAh4al9t2EUR9pcoscMWDvySUuSR7rM1gZzXpTiev/2ELLtOlANTdc7Z0Pw/Gh/8mKjWXnqquSf86v3LWzttG/vTX6T+cw1oxNGJywOgdTKTzOu9WYtt2H0w5Q1mk5ZEjo37xqQdwpnpSxQecOBwVhdXIPl/2wfzCkKadJ2Yv2rB4WPLGxaDX++bXg79hulbTlzw4CtPNOp3A5guP5A+g/fOQO0XEjaNhFJS8JLebJiLN6eOivH4Mkpb/iGyekJW0XsqWbfH5i13IbRdA2+k7KsyIvW9q0oqU5YDI9PaTa83L4ODYFzV6VPRM/aKGwrsbiQHPhX7ml/ZYtBldswgvShoqctC8uFxPbidcnf2z4D37VmpCEQe1uHp64KtbNhdvaq5GHzw/CmNtf/fZdOps/Ree3G/pWllY0T8JspYfT5XaM1JFXDae14er9oPywfC4E0zJJWothbTV/hPC86CaOBW7Rm52x4oCc5a2VyG2s/vGZjctPFbA0+GmGFa6nXnr46eYRXP8WunWXpCSvgcQnN/V/Y1f+Rk1noZDTdAPy4HerDO+AFa5O//3dHwTk39X928uOWw4UpO2t+aXJ4JlVqtKX11/z2bXBFD5uQlo7BD08JNaFmT1kJq8aHb3mtMeBNCSMRa8TZtiULua4ZQZgQl9Z3dMJi+Osj+1ceCO267zkm+ftTNXjHiO7FouGyfgJ+LaH1Ydds5wujzudAFb6eEG6LCvCMIWyq+/0NySu4f35n/ucX1eW6z6juf9+VPsy7dBi8MKWW0kuLCvCPx6RP8PvwDtjqRFcNgfNXJz9ENrexvXg30prm0+Y65dFjlsPrE/q+D1TDEk/DopMwGtiu9q1T8N55/lPedhQ8J+Mf1EUF+MCx6XMebjoI77JWpCGR9vBPW+B0If5jT3IfyZkr0hclzpMzV8Anj0/uj7vonuQVzfNoKMII4L3b4ZqUjbbGgHcdA7+XMCJloTZMwMUnpE/8m67BK2/P/+q6EoTddZO2cMhy2sJUDSoJQTdegGcM0IT3bowBrzocPnF86CNr5Qf7QwvLMBmKZjoIzQF/fHsYYZdkDPjLB8E/HZu82GA3zlsFXzkpfXIrwBu3hcUrpWGQViv68mS20xbS+omzbgHJ0qOXwRceHJrmkraK2DEDv781/5Ncm+V+AEOjW6fgpbfN/5+0aTV84+TQMZj0zqMdpy6Fjx0HHzourIWX5l13h9WTpWGRNpQ6qya6uiv3hodyK49dPv/v4yBZXAhzpC4+AS55MDwiZbL+gWoIon5vO9MPnfyXDeCWdYf6/j541e1hEEHa3Ic14/AXR8DLN8DndsIlu+CG/fNvYbx+As5eGQZE/Gqbq4P/673w7iHqaJROXAwPSxjhtW06rNKdpdkaXLo7DE5qViAMN//gADVjLSmEkNlbDTtRHz4BJy8JK8WcsSJ5tfNGk7Nhq/jvD+mqLZ2EUW6a9CqTsOc2+OCx829Bvm48zGx+2Xq4dzZsc3zTwfDOY181vGs5bByOXwy/sjTsS9RJFfGjO+DNdw54h5vUobRa0Zd29efn/fM7W4cRhG0l+hlGbz8qBE2SxYUQPt368QH4w9thy5AM425lKMMI4N/3wHNuhvcdCw9uc+fXdeOh1nN2D5Y2ma7BX90BF9+38GtJgyZtousX+7Q0zVX7woZyG1s8xR65LLyB7NdeYcdntLt0Dfj4vfDWO4d/4NNQ9Rk1u/EAFLfAp3f2975bDsLzbjaINJx+aWnyu/xbpuD6lFGtvVQlLIWTJO9zjq7eB8/8GfzPO4Y/iKCzMMrl6kd7q/C6n8Nzb85+86mDtdA3dN6W0NwnDaNnRxy40CxpiDfkd1uJK/bAhbeElp1+BfsgyNGYk4W5ah9s2hJGrfzhhuT5Ed3YW4V/uTe0USeN8JGGxTNT5vH0O4yu2R8mfh7XopnslCXw0CXw3wPez7K3Ctfuh69Owld3hwEgo2hkwghC++vlu8PHSUvCfITzVocf2k4dqIZJfZ/fGX6A9g1gvfGuGVjT4gf7npwG5vaZ1ovL3pHTX97J2eTFcqsD2ixzypIw/6VVuW+divPg//RO+B8JAxnOWNmbMt05EwYiLcSqMSgUoFaD7bNwzzT8bCo06w/g46PvCrVaez/1hYs3vxN4bbbFieOw8VBTOnlJ6IjcMDH3gzP3/VnCoo93TIdfuOv2w3UHhm/SmSRl4D21Cze9er6D2qoZFS7eXABSlv7Mt/tm4bLd4UOS1H/tDmAY6jCSJMXVSRjlap6RJCk/hmbVbklSfrUbRjVCP74kST1nGEmSomsrjGoXbqphM50kKSNDvTadJCkf2gqjuXlGudjPSJKUPw7tliRFZxhJkqJznpEkKTqHdkuSojOMJEnROc9IkhRdJ0O7nWckScpEJ6PpRmpXWElS/zi0W5IUncsBSZKi62Q0XTXLgkiSRlcnYTSdZUEkSaOrk6HdhpEkqVNtdfF00mdkGEmSOtXzMLLPSJLUqZ6HkSRJmXBotyQpOmtGkqTo3M9IkpSltsYbOIBBkpSltioyhpEkKTqb6SRJ0TmAQZIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6AwjSVJ0hpEkKTrDSJIUnWEkSYrOMJIkRWcYSZKiM4wkSdEZRpKk6DoJo1pmpZAkDau2sqOTMNrVZUEkSaOrreywmU6SFF0nYbQ3s1JIkoZVW9nRSRjt67IgkqTR1VZ2dBJGO7osiCRpdLWVHYaRJClLPQ+je7osiCRpdLWVHZ2E0TacayRJ6szt7RzUfhiVilOEQJIkqR17KRW3t3Ngp/OMftZFYSRJo6ntzDCMJElZySyMru/weEnS6Go7MzoNo+s6PF6SNLrazgzDSJKUlYzCqFS8C9jaaWkkSSNnF/DTdg/uZtXu/+ziHEnSaPkOpWK13YO7CaNvdXGOJGm0dJQVhpEkKQuZh9F1wB1dnCdJGg27gW93ckLnYVQq1oBLOz5PkjQqLqdUnO7khG63Hd/c5XmSpOHXcUZ0G0aXAge6PFeSNLyqwBc7Pam7MCoV92DtSJJ0qG9SKt7d6Und1owAPrOAcyVJw6mrbFhIGH2JMGJCkiSAKeCz3ZzYfRiVinuBf+36fEnSsPlcu5vpNVtIzQjgIws8X5I0PLrOhIWFUal4NXDVgq4hSRoGPwWu6PbkhdaMAN7Vg2tIkvLt3XOLInSlF2H0aeC2HlxHkpRPO4B/XsgFFh5GpeIM8J4FX0eSlFfvo1Tcv5AL9KJmBPB+4M4eXUuSlB+7gIsWepHehFFIxLf15FqSpDx5N6XizoVepFc1I4AP4JbkkjRKdtCjQWy9C6NQO/rLnl1PkjTo3kKpuKsXF+plzQjgE8D3e3xNSdLg+THwvl5drLdhFMaY/3FPrylJGkR/Mjeauid6XTOCUvG7wAfbPLrrCVKSpGg+Tan41V5esPdhFLwB2NbGcQXCRkySpHy4F/ijXl80mzAKHVqv6KAMBpIk5cNrKRXv6vVFs6oZQal4CfCxDsphIEnSYLuEUvFjWVw4uzAKXgVsafPYMexDkqRBtQ14aVYXzzaMSsU9wG8A022eUcNAkqRBUwV+m1JxR1Y3yLpmVB9d95o2j6431xlIkjQ43kSpeFmWN8g+jABKxfcSJsS2YxyYxUCSpEHwReCtWd+kP2EU/D5wdZvHTmAYSVJsPwZ+cyGb5rWrf2EU1q57JnBrm2c4oEGS4rkL2NSrtefm08+aEZSKdwKbCPtftKOQYWkkSa2FykOpeEu/btjfMAIoFW8kBNKCdgWUJGViGng2peJV/bxp/8MIoFT8NqHJbirK/SVJrVSBX6dU/Fq/bxwnjABKxSuA59P+HKQ6+5EkqfeqhMEKX4xx80Kt1t6zvVDIqPumXHkq8CVgWTY3kCTNYxp4PqXiF7K4eDs5E69mVFcqfh04F5js4mxrSZK0MPuBZ2UVRO2KH0YApeK3gDOBrR2e6Wg7Sere3cCTe703UTcGI4wASsXrgScAP+jyCtaSJKl9PwGeQKn4/dgFgUEKI4BScRvwJODTXZxdryW5FYUkpasAp1Mq3hy7IHXxBzAkKVdeA7ydsFZdN6bnzh2swJWkeGrAm4G39GOJn1/ctI2cGdwwAihXngSUgWMWcJUpQiBN9KRMkpRPdwO/Ral4ab9vnI/RdGlKxf8AHg58agFXWUwIolk6n9MkScPgy8DDYgRRuwa7ZtSoXCkB7wY29OBqNRyJJ2lw9eoZtQt4PfDhfjbLNct/M12zcuVw4F1AKXZRJKnHev0m+RLgFXMDw6IavjCqK1eeRqglnRq5JJI0aG4CXkOp+KXYBanLf59RklLxcuCRwCuBzPZkl6Qc2QW8ATh1kIKoXfmsGTUqV9YAfwK8GlgTtzCS1Hd7gfcC76BUHMg358PbTNdKubIWeC3wh8C6uIWRpMxNAh8E3kapuD12YdKMVhjVlSsrgN8m1JZOilsYSeq524B/AD5EqdjNAtN9N5phVFeujAFPA14GXAAsilsgSeraLGEJnw8DmykVZyOXpyOjHUaNypWNwAuBFwBn4BwjSfnwPeAzwCcHYYh2twyjVsqVo4BfB54OPBk39ZM0OA4CVwJfAT5HqXhr5PL0hGE0n3JlKWGV8LMI+yk9DsNJUv8cBK4mBNCVwDcoFffFLVLvGUadKlcWAQ8DHgGcRlgX72TgWLpfPVySqsDtwBbgOuB64FrgekrFgzEL1g+GUa+UKxOElcOPBTYC6wlr5C0HVs4dtQb7oqRRtXPu815gH7CdMCH/HsIO1lspFUd2oeaehpEkSVnJ53JAkqShYhhJkqIzjCRJ0RlGkqToDCNJUnSGkSQpOsNIkhSdYSRJis4wkiRF9/8BRzsC0iagxB0AAAAASUVORK5CYII=\"\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/9JREFUeNpiDDl6gQEP4ALiBCCehksBEw7x/1CsDdW8D0kMBbBg0QgCAkD8EUncCUo/RlLDiG4AigQOIIuk9i8QM6O7AJ9mdHX/kcPgPwmaUQxhItFmdHAFZAA3EJ8hEBv/ccjrgAyIB2JjMl0ADoNpDBQAFiICiqALYGAdiZb/R3YBI56AwutC9LxwgATbPdHDAOYKJSC+h0dzABC7APFebIHIiJYvCAYsQAAxEigPwoH4CxBvJSUa/xNwESO+AgU5SzOiacLqPSY0zVYEEg+GISxkZGdGpAwGTwfpZJQFcBf8J7M8AOn5x0QgtcGwE7FJGRfYS2q9AAL9BLL1TPRCFR0UYUkPyCANiE8wUVCggoAlshfqSC1MkL0AckUjOWmBCVttQ4TtjLhiASSxBy0NIGMt9DADCCBC5QE6+AzEPGhi36DtCGSwHIijiK1XGIhMzf+hljOiYW40ficQR6LpSya3gYMc5oxEJrkKLOrn4KqimfBYDDOAiYEygO5wkPmquApUEBClMHMR45BbQLwduUB+DcTngdiIgfYAuVZghYWACBB3k9G0QMaTyXDML5ADQqGcZeQURUggh5zmDRM0Hw8YYEJrdFSREI/mBFI7SYX5QijdSoLjT5FYPsCACbYqOYFA/FITnIbS5thqo1QaOwK5kDuFrSScQ2QLl1QgBzWvHz26WAgUFtJA/ASL/B1otj0G7dNKQhv8oKhkJaI4JrqT9BRNIyjE/gCxCp4mzFm0hIYXAAQQqe0BlAYV1KLvQLwfiO/SopuIDHyAeDMJ5ct/YhUSAieghm3GEa/Y4vcfUhOMohD4jyVNyBDb9wGCq4Q63LhCoAGL5Yx4LCeU4v+T4oAlQFxPZhmP7pALhByB7gAzII4mYwQJFzDE0erC6YCTVLScAUf3F28nm9qW4xqgmIovDdDCcnSzs9Ad8J8OlqM7oh5bdUwvwAfN6mAHaA9AU/Azckl4gILUTWnaYWKC9gkotZzcBkwfOf2+51SIgjJYDYvsAC4iNUvgkfMi0owmmJ3IDphHpOYleOS2EWkGO6x2RXZAOJGaY6mYG+YzQdtwlBSrDNDGKTm5YBoLtF33nwqOIBbsw1cbfqFDIeSIzwHcdCwN5ZAdgBycLTS0FDmqH6OHwCcoXU2nyggjCvixNRho5PvPuNIARoOBxi0jvC2iDzTqlhPVL2CERkkZhRYzA/FGfOUGC4GgArm8E4vcGiDexAAZcAR1x02hRbk5joKHkdyuGa7BihAopri0ZCIh4YBwDxFqrUnpTQEEECXjA8QCDSAuhPa4SClpQZPjoNHXRbR0HBOVzdvOgDmEfJ0BMsWF7vkSpJjiBeKXaPKgSnohA/aZH6PBEgAFaA7zwKHuI9STyOMpvWiNAAk0+Vl47D2LZOcvegeAHpLl/TjUvEPzjAAZLZ10NDNW4FDHiuSeB7QMgMVQSy4S4WBhGmTXSCTzFXCokWfAv3iGrACogxoYg61FTWSSpTZ4iGSvH57an2BAkDpECQO8dGq8EwM2M+CfXPgPTb1xpKSAYhyGwUJ9sHgel/uwdWT/E5sCdjNAViqhB9R/hqEDcKWI/4Ra4+vRPG/BQP5Cs8GaInCOEAcyQNapgcBMqMaTDMMDYFs6gREA65AUZzAMTwDy22wouxs5AJC74Ep0cIgntLGE3IpcQadASEVqisMDAHkIgJbDATDPgsYwBdHkwpHk99ApMDxAAWCJpQqkNggjsSB1plHBq4/eIWNiIGFunQKwktwYorI70McTNEEB8B2LwsBBUmjdorJ5LthagvuwKFxFo4YJqWML96joBlMsYnuYcFgCaiFy0iAQDpCg1ovK9h/FItaNbd0WDLylQZJ2ROvju0F7c0oM5C1CI6Xww7aY6Qr6yjlkAEoBwTTO47uhvbn7NLbnAo7IQGkJYusYrRkGrb9XWMQuw7IjcgCAtlxZkTAmMBQAqHMnikVcD1dv8DgD9tmFoRgIU5E6dzhrJGwDIqdwFERDKRDmYmnSb8LmL0JzU9dArSV8AwqDEOwCYldi2yGEBkW1cAwoMA1Szz9G83wdoQgjdW4OucDUHWSeB0WMDJrHmwlpYiHRElgggPrul7DIf4PmtQ0MkK0B1Bw8BQ3P+UILNi1qNbmpMTk6g4H0fYXUBKB1T2RPj1EjL2egNWNraOhZUItRGM0+iuYGWWjgyFYG7JtRWKBtf2doQ0QBqcPFDC3AbkHbIqCS/DY9kg9AAPKuLSSLIAofNaRAJBISI7sQWkSQJUZJmd3wJaxeIogsEIwuhD0I0oNG0UNlRQ9ZUYEQBRKIkRHdyCLyISqQIgsiqMgKoYcSpFDr9J/h36Yzu7P7z6y7fx/8oLOzO3O+ncuZM2fOhuEfIKOYfgW0QEHhPxEBWJmhMCszLoQyammMKPNxDw6el37/jhi2CVgZA2TgG22HpIHzvIvwqlNsOUTaG3rGd+o+kSZgMVUWz/hs9MiL50DQXU6chm3wyI/5btLzO6NGwHyqWI9GXrGTiwrLN0d6C6Wv0HjGOirvXhQIGFEYG2Q0g/tevkA35SskbdMNlURE3VgQsEdzYbSN8hzw+fwPNEDnaKxCz6ayUg0yC+CUle+RZzeY8XgdpJeEU+ZHjbUAuuS9stkCRj2Ev0hv3LS7bz8912ujpA9oz88GAW7N7AdVsMayTnGTynnkkucorU+MEuAm/FZIHsQIC+gOO83lOuoQrabGAO24PWNg/MggvSOLub6DFKljqbSAURdVNSqmsXG0eOLQ4mW4cSPgiiL9KSTc5KKEKlDHt+kNQkAJ8P7w6P1fCtHEflBHtBnyS8AzJg1D5qyHaAPruFZhNdquS8BFJq0LNOMFRQDXqUvIOKNLgOwT/AASxsg4AQdFbnu9w4sA2Vni3e/fcognbjCK2QYvAuTl6HSIN7A7N0ppbSoCjkRIyTEJPHZ2WtJcWQIa0lB4gZ20jhBYIxOQ67iYBekJXEkKU/s5mQBxOhFPfYxA+qJYHtsEAcI5ugz+H8zkZoEFIRXeAX87SmOMvZUhtgCxWvxDQG6IrLeRwPJ8jPE87oJ9L5Rljr83iaVkVUjCo6Niuab9wdYs5HQMLxQtIIymV60pvJcdIlXIDmDZmUy/L7ZQ8NUA96y2UI950v9zMiEZnl2gwnChQe2FrSG0zGlIwESP9YAJBSQIikIgYEImo/isMlxIHkQDXFy8DBGx0Yl8wwUH9cAYNlwPzqbx51sIA5aZfxrwPtOHsbl4Uf1IwAvmwgzDhfcEuMf06TXOsNOHBHAfsqg1XHi5z/wHQxoXBpCA28yFOguF6e5Eo87QZLjsQtUFJIA7HzzZAgHD8G/QTxnoPmfD9N7IpN3xeitIwhcLlRGaJ54TwrCOQ4pWaBLceHLKuRzmBsIWy5VC97drIQivQqeTAK6JbIH0QL3bRUFAl+J6fhoQcMJtnZEpNUkZ12MufI4ifRdHALepWBpzArhQo0NcF0C8VDzkeIwJWOZlFPHaGkPsjanwZxXpvW4EdCtuao4hAZw2O1c1CzgxhUnbnwZv/xPXzTkC+hXKyaGYv/0CNz1ABuebvy8mwnPOXZu9FCEO2UxaewwIkJ27MPzf5SAE/ITkh5EENkZceM65q0RHFVYB4wfIn6V6HVHhxzPCGglri9GFnZ5jRZbsBaniq1/hdQlA1EjL488RE34htQBfwvshAIEuNOsc/+MWdzWM7UnyImqhTxzjlq+NVb+VdwYhwC1utN+hqUvs8+Mg1OQ18ATAJLJPIOk/HOXheCS8Wy4oZi5XBD04iSQ8hITfvjzi4k92XMbzgWh9fk7a2HtHN8KdqTxSVGZBwkyGz/DjoodxQgLtb6RycnQpJD7PMaiRF/NVgPmN15PgYfEx3QWAebPYGhaF3Pe7qNz6VB9kagB7TBXCpvjOouDiM6fGfJdNj+AD1HexkpWgjkKtC/GBAfHp4cOmGbV5evy+NBvMpkXWEpq+pkJyBxi70lsiDI/E3gLzu8MsfgnQ3rmGWlFFcXx56FJkJISamMZNL5mifbCIougq9pKEypIwA82ulN0MNAsq+xJhoWCZ5aOXVpbaA7OXkd6MoqL8EJRmD5MkP5Qa2APLMszfPWt3htOZmT2PM2fm3P2Hg9dzZvbM3mvN7L3WXuu/GsEfUG+QzkMCZZt+BquPo69+TtBFU4tUYiNKOr3+oS91NHmv+hCg8f5OPzssX/qFwTEFvGdYN4h1nqBPVFoR/czUJlqoLcJ5KEaXrgk3S0JKk6xRyvn9taoxvt+z+D2ogz0jgfAPSXlvqL8uspfod3HA2hUH3JvahrlP3iDzxa5ip1MABQuHTz2DyLw4V5KHmWEqTpQK8RBTAHtj+9SJcJt+Z36nlMWXCa/JivAuNXpMf96TnIXjN1oBmJNf9gzQlhQG6C99uk/1CBTi6PUR2lirFqk5n7/ToBlur1JweFz79DQFYDX8hVRyJJKS1vKqnSXlNCeEdaw+3T+keM+8Da71KARP96Py//jSqMDLeEDHYqsE0yEUWgFwUr2uHYXhY2SCtti0m+4RxskqjCzTvPar0rV4FGJZwjbPVovjiL5tejWDAlyvHToktUNPbICL9161WHqpSbcyZ2sXFOIWj1Ky//5+gvYmSaWQ/VVFVADD6vRczPNxTozSweTtcX9WjpGUsEPne6MQSQJLTGrhoiIogClEFyfGeqPa4QwYUbTbmsjfcp9HGeJWLpqtY7s6jwqwTPwL8QUB1+dgqdSR+EWaHyukdq1NW0zRsV6YBwWYqjdzc4zzGAB85Xuk58JUmyVf4NsY5zL21zRCASA2JaB6VYRzWOEO0g4/Kw5e4PA6XcfmqYjnEgm3XWK69eMoAF4zCOROszy+S230Vikz6DoEo0MVIUqm4Ai1lqbXWwFIeVxseewG7chF0txULPXCMoleY4u3x6Z6KABPL5sw51oca+iir3QyTAUbxY5C14AHjvKd/dJSgHado8Kqzb0jdnTZDvFgKIRtwoEoX4qL/KykCnC5hJcE/FyV41Ino0xgAuJsPISEYo6NqwBjxD9/FPwq5Y0dqgn86eSSOV5VRegMOQ5O0NFRFYCk/aByDczvbGN+4+TQcCxVRXgg4Bh2GttsFYAdrtd8GjIFyza4cc8d7lbZrPWR8xu2CoApUR1q9ZZYVqpzaDgmq6y2Vn0/TGpQsVUrAAsLL0kGQRUDdDHoUCyQrXGKlOMnDCAMvThIAarnESJhfnJjWVhQg6h6V3W+9z9e/3GHvia8YFuWOPrfm2hQWOPgOh2q9jIbKjhOdqnCH26ivhJMW82XSuQRYXivVCtALXOCsGkCIj8p8CBAjvu4CjwKiFtkl/OjAvedoJpa9NCdRgHMFEC6kl9SaxHrSJDkYaJvu2II3wzeh1IJ5y4it/75Pt+PVVP/PwUI8uJdULBO87STvpVm/H27Tg0LCzYW40L61K0AJCoG+Yz57biCdBjTZ0Yd258r4a7xvKCfzvdBVkJ/FIBEyuEBBw4MaSgvWJfRfbZL9KCNRoCd26C6d8h8mClZ2jeksfE57yyv+yxZjKbFXFdkiTAafOQ+oKSWQNgCZ0LOOzsq4+uVapjMeUOY8647MLWkwg/bFj5T8s0f+nMDrvl3jscDqtCwUijd+YkIHhKEAxaNXp3jDrPRkWV0Mbugm3I8HjbTIRFeB1EA/P02xDaTctxhsoZmZni9jhyPRYvlw0qU124UgIiezyxOaMv5WoC3wGUZXIdSGB/keBymiA87bBXYI+iuH8KroMuy8ZtyvvAxcXPv1qHt9dr2xzkfg07L4wg2PVzyDNw+i5MmSPpVtuqBcSqsh1Noy+T1TSxAvydZ+kKY8jeLZ/XPbt9ay4vcI8XBbKnk4eEXh5Fjd8i8SO7eOZJOZm/WsC089IJaAeKlicMjuMOyAQpxrhOHPAE63wUWx5GkgxPre6my/2HueMzyYrxaj3djnhu0Hv08aHnsAiP8agUAsFrZVM0iTOxpN+65wWqxS/Jhipvn/aL6pN/EvoIgpEmz3Ng3HIvFf9+/lv/inyAFMPa0bZWUR6R2kRGHbHCDlLO1bTCvlnlcCjh4TQTbe5iTReYYE2EaXuH3UAfNG9epcG0AE+dAJ5PMQLDuFstjIZnyZXAJWzjgWrUpo9hblaCPk03dQZCubX1u+AYD9wVsVo54/56wtAzYJTvRyaiu5p6t8B+S2gXUIysAgPbNxsdMGDmetpOcrFLHGWrG2ZQGmnb0M8em0SgUMeSVEWQQRqsO1x8ZKYOczFIDKfg2Xlpo9uAbfsa24agcQVCZESEcxvIFYTNxBiOc7BKDsHybsi4r9OGLRJIdlyZuqmplGH3rdjVXHOIBHoaw2AOcd0MlJgNpEqJIAkkIKL0j5DjMlclOlpFB7EVYjYOZuujeFfciaVDFUlWTbdOgjSS2H+90MrUGMQjLA35fpGO+POmF0iSLvlVvaqnP79R8W+JkG4onpUyPHyT429O6WD3o4jv1Juf4KMl6J2NfQL1zo890kKrgDbKoG0ju4UYJzqTZowvGbfrh76+lzETWDMAvMlytIj4j9d+BIQvoS9SkrhuyLhxJjZxVkqwcCpm/O6Vcr2+nLoB2q/mzR+pPOY+zC4p76FfgSyZaeoj+PURN4Lig4BWU+y9lJZBGVg5FGeDD7emRRbzlyGh+sREXb2TZOJxJvfVtwHby2z1I6NDwtWrf+zRK+I1WAC/YRBovlUhc5svnRSNXCw6cZSt1LWT6d4UERyf3OAWoxlc6F5Y8g3ahlN2de3Ms7L06rZ3nuW+cZdN1vZI7NEP1cLahiYmDEGG0rrD711HAWCkwkcBBBIHUj0UevF5HjjTDW9YhLv4FMFbB7o//JIUAAAAASUVORK5CYII\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAAG0OVFdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMjFDOUI2OTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxMjFDOUI2QTVBMDExMUU1QkRBREQwQkJFMUZFRjhGRCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjEyMUM5QjY3NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjEyMUM5QjY4NUEwMTExRTVCREFERDBCQkUxRkVGOEZEIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+vr5XIgAAE/9JREFUeNpiDDl6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\"\n  },\n  \"931327dd-c89b-406c-a81e-ed7058ef36c6\": {\n    \"name\": \"Swissbit iShield 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  \"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,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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+AAAFIGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wNS0yM1QxNDo0MDo1NSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMTktMDUtMDVUMDk6MzM6NDcrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjE4NWYyYmYtODVmOS1jZjQ3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\",\n    \"icon_dark\": \"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  },\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,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAicSURBVGhD1ZjPi5VVGMf9C9ob6DJoIQi1iDBwI5QgEUEltBJ0YSAGEuRCFBMxIklCayFIQiaKBZUolY7QNJM63nGaca6j40w004zBMBO6LE7n89z7PfO85z3vtdq5+HLufX+c8/k+5znPOfeu+Puvv8LjLDPQGh4O7fHx0GoNp89Vta2dnJysaXp6Kmlubj610vz8XFhYWChqcWnRtLS4FB4+fBgePHxg4rMXjL6VDh482DXQBU9GYjvebic1wQu4BA+4Ps/OzjbCmwFn4r8oGRB0J9odJfh2HX4qgiIP7wU80KXoe3CDfwR4HnWJmeppoKN2DX56qpwytADPz3Ui3wse6P8L7lUxkCsHR3nUBc1nqQTu4b2JEtS/kQJQNxDThbQpwQNH6+HVCprvtMxCDk+eLy5VoXuZKM2Ani8aaMp3g45pY20Gj4BVvufR99GWPEhJvVLH90MwshnoHXkBe3gvD57DM1gvaNQLHFXhF22MZCCHRoB6AVmCz9NFstLYNVCCya+VpOcETn9+jEYDOTiL99+Cl9IG5XCKeK/IV/ro9uvHKhpQmQSyGHGX57M//BBmPvss3Nu1K9zbvDncWbeuprsvvJA08eJLYWb37vD7oUNh4cKF8OfMTBG6BO/BpZoBbVC+XGpxotlr18L0/v0GMvrEE2F0xYow+uSTBjr68sthdPv2pF/2vxduffxx5Roaf+65MPb00513o9qrV5v5+6dOmSEPLfCSAQpHxQDRVVuJeEyVX8+eTdC0d/bsCa1PP7UjSH9/v7WqZD4IDDI3TwpOm+iP69rlhz7/PAzv3dsxHwOBoek33wz3v/22YqAET1sx4NOGBxDgt59/Ptx94/Uw8ckxgxw8csQiOfLsM5Y696/0dQaLUfMp4MUYXKfN75HXjAUDhq6++qoF6taqVWEmzqCglbq0BIV3kgGB0wre8joK6NY334SbmzZZx7fXrAl3PvggTAxdt3sMTKea+g5U3YSXDOm73kVADrdaYXjrVhuPlJsfGrLrYhNnMpBHH0BeuvXdd+HWK6/Y1JLnYydOdE+uLXueTj2I5AEVdV3z92hz0ac0EtNzZP16MwIT1xgXkYqVGZAwwIO26CI4ESDfBwYHDJz7yk8GFAitpO8eNr/vxXhN+Q7TzZgJsIwdOJBmABUNLI6NpQU7/u67tkhJFbsXB1GNJ22m33knlUhKo8oifd6PplVaKZ1LsV8Bs0h/jQHSPcbMwelfYmyqmi3yjz6y72RLxQAP8qKVuFgRbp4+HQZj1Mlxrif4KEBZC3ToxTUAS/cICAseU7V7UUoRwVsbKyBsArasiP2wRtivKgZ4ob1liz0w1Ndnuc51H3XgiTCR18A3Nm4Mww6K6qTPrbVrO/din3atWyrTPRaqrsVnVBC8ZCCZiM8PvvWWPZsMAM8mRUftkyct8lwTvDeBAaaftUFEWBd0Zua7cGjkqafS/sC0mzEHa8UgipnGCCJdc+C8tT0omufdigGmltxXJ8vgndOkFqD028xvdvxmUZVSCmDgF7t5T58UA92n5jMu4h7Paq15CZ6qQ6Amvzhl78NZMUB0WOU2qIu4op6LRcmumdIjUzLQPUqjhQjhn2e9EbTfv/qqCC7xHXhaMoR3L126lBmIF4kQD/l0Ud7n8E3gEtOMAfq2WcRA/MwB0K8FiUUseOTBU/SjOBHw/vnz55cNAEwn148es5QwyIbI87xFnoExwTqIxm2ndkCaAaBzAcaR5OdYplkr6ksppGj7VmJjZazKDGCAmnzj7bc7G1UDvETdZ1AqDP9mcFDj2FExEMFk4I+44EgTiTMW1ymF7O56h7wm2kAzA/Tr4ZU+mL98uW/ZAGlipTFODS+XDPCcPk+89lpn0Pj85JUrthGltHCpRYUBvrQvkDIYSH1FEVUf8ampZQOcvRhjfMMGS59KFQKYSsLgbNuPmgF+jHgYL9KiaX3opNl0DwMGnkUeeBY8s/r9uXP2HLNbMQAY2z+dTZ85UwH20Zf4JZaiHjWycqXBE5kJNsK4iHUPaABJEWYlv0cqAsW7HhxZ2sRxMCB4niN1awbQ5LZt1jGbjwcuifVCJACzTrsAWqh8556kUyzP8B0YqQYfU1MnYUubaPzixYsGzpiVGcjByE9epEaT3/l9hGmJIqAKk6vpSKCWdaBfbDk4lYwFC/xP8acs0ASBdji2xRlAXKNe23EhTjELvPJ71YkaX4OOcEAzQ5LgU5XhzwOne/v2pfEwIHDSi7LJbwNmTSYqBjy4N0Jk2Z0t12PH9uOb36sN4BLwtIL2Eaf1acIZiBSZ2LnT9hNLqaNH7ZDIuByjlW4GH1MNeNrGFMpFBG8e/rDz66i78DDDb1aOyB6eZy1t3FFYAjpv0dUvz1kBEDTCWN/XX1vJxADQEvA1A72MKF0YlKm8fuh9GyztolFshKwZ/ZYmJdiwvDhJEmlE1O2E2n2fvkiX/uPHDVrggOaRLxooQatNcouVyKljHQuImuVrBJPIa/9d4tmrO3aEHw8ftlwHmCrDDivAlO/xB4yuSRz5H5lCTfBeWqwypCgRvZLIZSDRwOCgiecVDFpJsF6A63MyAKDaGnhUL3Ba5TjSQkV5rnvZ3/kO1gu4PF2Q4AlEZQYEnkeeKtRU4/NKg/Iqkx8JJP0zV4HublAG3gMeYYC2ZkDggs+hU4Xpiu+oZMAbEbRaD96BX96cesEr8vpcMfAoeEmwAvc1XvKnSK86+HLOG3gB3v6P6gKrxQTXiwbyDUqpoqjLgIdHAKrN1TPfIzSRL1WaErxaFn/NgAf3Km1KOTzfc3CU57uiTivQkpoiTytVDJTAgbPIZwYED2ATuICbBJTaXL3guVczkIMrbZAHz+Hz1gs4tQaqyEcg+/c5SxstTr9I1Q4MDCZor0YDAs9zHlWi33OxlvMeKLUl+eiT5522mjpSMsCHx1MHwz8ceHy7EhRz5QAAAABJRU5ErkJggg==\"\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  \"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,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\",\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,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\",\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,iVBORw0KGgoAAAANSUhEUgAAAQwAAAAgCAYAAADnlUZqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAK1ElEQVR4Xu1dDXAcZRm+NOAfKog6WO0QcreX3O71R41oHdSqqDAOg3+cYEXBolXRTEn220taKTc64mgBqzBiEUVpBdqiwwhqSdIS2upYSgvRtpTSckljWzHagjpSRdr4vLtvjrvk27vdvd1Ljn7PzDN3t/d+7/t+f8/+78aK0NDaar2qOdXZoqWyH9R0a0Fct67WdHGTZojVCcPqSejW1oQuHsOy/eBTsDmM/54ZT9j+LWGIg7DfB/sBcDPsf4XfP8X3b2uG1ZHQzU8mUuKdyWTHm5qaci/jHAKByif0bBr+LwaXIPYPkMdqfL8XdWpls1AA31/QjOw98L8S9b8BXIR2+nDc6Dozlsk0slnkQMxkPGXO9EJtVnYGF4sUyVnd8UTaep8bw+6LakBj5izdbNJS1rxEWnyWxg36EmPdWoPPDejf7eATGMsHaDzTuC6hbj0N/pXmAsrugs0WLP8NuBJjZJmWElcl09mPJ1JmW0tL5+uiHBuGkXsljX87ni4EzVnk9AvksQn57ESdhrB8BMuPjOWP//4OHsR/e7D8YdTlftRhFfgdLG9Hu1wAfzr55jAOkiQKhvVbGB6C0//i+2iNeRx8FgnvRfxfainzSk7NE0iIUPbf43wWmNTNd7BpKEA7LZfFAY9zp3yZTSMDiQVi/U+Sg5QYAIfOmG2ewsUjA/rhW7L4Bermj9h0UoB2OB+TZTW4B/k8OyG/yCiOoW1IYH6H8XPz9LbcKzilQGhpMZvhZyHGwG3g42Bk85Z8o90G8X0NiSs1Iv2QGk8KdWszt4snIP8RqR9mDQXDIdZSbBoZ0Il3S2OXZXYpF48MU14wnK1beW41pL3FEQCJlPVWtDG2fuyVrNR3tBTdSjB8YrIFoyVtno2OCzBgxDNBB6pXKMHwxiD9gK3Kc6PckvBGJRi+McmC0YD4fdK4Xoh9W/YTCZRgeKNvwchkGtG2e2W+akslGL4xmYJBaxlpTI+kNRQdmGR3oUMJhjf6FQw6cCrzU3tCMLDWuQsd3R+Aw3KnBQ5KynjhjdxOnnDiCEZuGjrsYWlMJtpiWUK3BmT/FfEudhg6UPe6Fgz0bR6fa6MmnY3klDwhaYjLUU6es27t0gzzm7VgUu96D6fkHxCa62UVGCMq8g02jRQnimBoRvYiaTwm2ntfW9vCk7W0dYHs/wJ163k6eMZuQ0W9CwbG9K1sOqWAvIU0X5tiDZtNbSjBcGEEgtHWdsvJ8E2nAuUxibp5hWM92oDf2yb8X0Kx3rENF0owogHm0hJpvjaVYPjCiSAYibT1eWksJibCk/Pm5U5ic8rxQpldMRPp7HlsHhqUYEQDJRgh4sUuGHSRD+pIV+TJ4xH1LG9djCHTiMlR4ViG2E7HRbhAKFCCEQ2UYISIF7tgoJ2z0jhMtHOejl2weQFY/lGZfSnFfDYPBUowokHCMBdL87WpBMMXKgqGIS5vTptnh0XU+05ZnAJDFAzD6Dgd/p6WxmHGDfFFNh+H0Qb0waOyMmOE+OUNI/cSLlA16l0w0F6747q4pRpGcdqa7kuR5UtEH45gDmwKi/DZj8/7IES34rOzeaaYzWlUh3oRjJozRMGoOAENa0i2dTGGeEp8TFJmPDvYvGrUu2CEQbqhksOFBsyli2WxasTj6Nd12psXv57TCQYlGC4MSTBaW603oo1db6qzqVtfYnM56ApBw9oxoVwRMYlGNK391VyiKijBiEYwmlPdLbJYtSTa7qHiA+u+oQTDhSEJBtpvhdT/GHWxv9zWxRi0tPiEtHwJxbVsXhWUYEQjGHRwGuOh0gV5kTOeMi/hhPxDCYYLQxCMs1qtVgzu8revpyyPjwHwspVh/SuVWjKdCwSGEoyoBAO5p833op+ek8WsFdF+wa8SVoLhwhAEA37WTPBbRHTcAexGvJTNHfQMNcf6Bs+P9ebnxfqePJWX2kCZzHgfExjCGQIlGNEJBsF+EJEudsvi1obiT5yKf9SNYOjWZjTyfaHRud9AHotYpWA4NxqJY1LfTNT5K2wei60fMiAUD4KjBfbmj8b68stj2w7aD2qhfU/0xy6ZrzHS2qulpTNl+wyIuhcMjBU661QNm2cuPoPDRYTRBjpbR2MAOV9HZzOQ98/w/fYwiPHtfje0bv2Fk/CPehGMOrsOo/Lt67o1XDgVuiE/BwLxjxKxKOXG2M6dti36w8ORdnGP7TcgkFudC8bUvA6jlkikO8+Ttg2IMXSYzfxDCYYLqxAML7evo77ttnF//0nYktghEYlxHLqazJ2tjEqbs9iySWXn2v4DQAlG/aOsYBjWATbzDyUYLgwsGLlpKLtV6pNJHVZ4YHLf/nfJBWICh2HdQEXi6ewlMr8ldJ5HYtv7hRKM+kc5wUD77GUz/1CC4cKAguHp9GdKXMXmEIx8u0QcXPjYa+0ymUwj2utxqe8ioo4X2vY+oQSj/lFhl+SPbOYfSjBcGEAw6HoK7A6Uncio58GmpsteeB1D79BX5eIg4f3Dp3OpGOLMl/kfxx2xzFrfj8VXglH/qLBLsoXN/EMJhgsDCEYiVf72dWbpJdw9+86RisN49g7uh3VhF4PF6QmJ/1Lq1gIu4hmVBAMT9u7x70wJg/TYfU6hLJRgVEaFXZIH2Mw/lGC40KdgzJ5tngKfB6S+mPj/0IwZHS/nIg5GRxshBgNSkSjlYi5RAPruUlmcYmJy/XnG3HExK6DiFkZExBjYyCmURSXBQDuPoA5bo2bSyL6dU/IE3iqUngYNm2gD17N0+G8Vp+QfSjBc6FMw4rplSf0UETFNNi9Fz/DMWG/+iEQkHPbmN8S2bZt4+bhzj0n5J3iBdFs1l/AE1L2uBaNWTOriA5ySJyDv78r81Jyery6WQAmGC30IRtOc3Glop8NSP2PUxVNl1/Tr8q2xvvx68Pkisfgnfl8f6x90fQUl4n5GGq+Yujhy5qzu13CRilCC4Y11KRj0WkgtF/wmRSUYLvQhGF4mGAaLYPPy2Dg0PdYz9H7spsyN9QxUfC0iXfyFPtoni1lMGqxcpCKUYHhj3QkGxCKpW+/mdIJBCYYLPQoGvYQa9uXf71lp66JKlHt8/QsUR+0XTXuAEgxvrA/BoLfr2QfHr/GzlemKKSMYunkHTSzElL4+sFaCgfo+B+7WjOzn2LQsnNcGiD1UTubPodnF5pGAzpggvutWBur6H7tOuriUi5QFXSWKMt/HBN5EayXUr+w9McEpjvGK4vfIbwVdw8IplAWNBZS5DvWhN5Xn4edoqd8oiFyx2wk+iu/0Iuil9KwTTskT4mlxDtrzRm5XjPUo2pXe6G49gjxvw+fChNGhcfhwQC9jaTLEG9xoGFeWviY+UuSm2Q+coXdy6NYiNOwyVPrHGBh3JozuUCseT5mXQfF/jhg/xOfXNd28gjo0aH3pLAlNNGdtL5Yi55vQgbej4+6g/9gsMqAOH3HaSfwEbXcDvmeThvUpTe96y4QzM76Qm9Y0Z9FpdPcm6vNpsAt9stxpO+vX4EbE20oTCcsGSonl+B/f6Wa/VcV50aSPx7tODeEBxg10xy+dkoXgfAgxFiDe19AO30M/rEQO9yLmA4i/Bb+3l+bnkPIHN4PrUL+1+FwB22vhox1if1G81XpbvA25ZjK+r2lxR24a1d8RPzEfuwoWcsEWiJMzYj+I3w+VtKshHgH/APZSnqjTzfi8xh67unUuPdrA28NxYrH/Az3tI4j5+TOLAAAAAElFTkSuQmCC\"\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  \"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,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  \"149a2021-8ef6-4133-96b8-81f8d5b7f1f5\": {\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  \"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,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\",\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  \"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  \"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  \"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,iVBORw0KGgoAAAANSUhEUgAAAVMAAACsCAYAAADG+E8MAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAAD2AAAA9gAXp4RY0AAAygSURBVHhe7Z1/bJTlHcBvjhjNcC4O+dXeXVtUTMziP7oYXZY51IkKd1fNnFHj5ohBmA7j2MRsZolmxhhNJort24KgsiFsim7TAdMYRFQEFTcVxw/rwAEFRChQ+uuePc/1qQP3TNs+33veu+vnk3zS42gfnve9t58+773XIwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUEpkG6/XPpnIRR8gIh5t41r9cYatBfwP9Q3n6x20TZtP1DcpRMTPNdeU14uuVt2Mq21FBkxtMjmrLpVq0R8311ZX32rvLmMKP230jqmP3DsNEfHzzEW7ExfOGWmL8oWkk8kf1qXSPXXVqaXJUaPOqKmqOrMumfprbTLVnUqlLrefVkZMmP11/ZOlw7lzEBEHojmrzUZTbV3+L3Vjx04wIR09evTJ41KpKdobjCNHjhw1duzY5Lh0jdKr1LPtp5cBJqSsRhFR0t6gzrSVcXGMDqmqSSYz+vYwE86aqtS1tdXp683tujFjUjVjk5P1KrW999PLgVzU5dwZiIg+mqBeOqfOluYo0un0cTqmXfaPw8wK1d5O6FP8t2rT6Vv0zS+bsPbeW+rkoo+cOwERUcJcdMDW5iiqq6uPH5eq6Vt1FlamOqI761I1209J1/RF9kvlEdP6hm87Nx4RUdJswz22Op9iYqpXo532j2Zlmj/ppJO+qj92p8eMOd3ef0x5xDTXtM+54YiIkuaiDludI+k9hU8njtO3CzE1d44YMWKMvn3Q3B4+evjJ+nbfKrWE4XWkiBjKy5vPsuX5lLpUamZtMr3f3K6tTr5TuFNTl0w+WpNK3az/rqO2Oj3N3l2iTI6mOjcYEbEY5pqetfU5irrq1DO1ydSBcVWpG+xdibqq5AyzOtX3L7R3lTD10XLnBiMiFsNcU+HU3UVyVPIMHdWVp9XWqVNravP69vKqEVWn2r8uceqj/c4NRkQshrmojF4vOhCIKSKG1H0RqgIgpogYUmKKiCggMUVEFJCYIiIKSEwREQUkpoiIAhJTREQBiSkiooDEFBFRQGKKiCggMUVEFJCYIiIKSEwREQUkpoiIAhJTQS97WCUueEAlLpwdVvNv5iL3nAbr9x50/1vF9iKtaz4DMa7HwDz+rvn0x6x+/OKYdzE023GRPn7MMXSp3ieTG93bXGkSUzlvnvuyiovjrpznnNOg1Af/us277Mhh2fnJod5vQNe8+qP+Jo6LadEq95z64deuXWBHqQw6u3tUW3un2rxjn1q9Yadasnqzuqn5ZXXyNQtU4uKHVCJTgYElpnKab6a4qJSYfrTnQNnG9IaHX3LPqR+eqCMzVNiz/7Ba8dZWdeV9z6vEBL2KrZSwElM5iak/xHRo0dnVo55d96Eaf+Miv6dJSkFiKicx9YeYDl3ebtmjzpu11O/xj1NiKicx9YeYwhtbdqlTpuqVqrko59hXJSsxlZOY+kNMwzPrsTXqzsVvqLuWvKEydy9TuXuWq18ufL1w371L16sV67cVLiaFpCefV4+++E+VuGC2c3+VpMRUTmLqDzENT2LCb/UqsFElMg3/nZO5KFS4TztJPx6XzlFVUxaqKXNWqo/bDtuvLD6729rVN366xITqqP1VkhJTOYmpP8Q0PIXXhjrm5FRH7ZjJDeqO36+1X118unt61C2PrNbH5RGxL0WJqZzE1B9iGp4BxbRPHbZJdy+zI4Rh/gvvF1bIzvmUgsRUTmLqDzENz6Biasw0qh/r0/6QPPnqB37HRzElpnISU3+IaXgGHVNjNlJ//3CPHSkMT7/WUppBJaZyElN/iGl4vGKqHf+TxXakcPzxFb1CLbXnUImpnMTUH2IaHt+Ymqi9t22vHS0cP1vwqns+cUlM5SSm/hDT8HjHNBep825/2o4Wjnw+r8ZPX+yeUxwSUzmJqT/ENDzeMdV+5apH7Ghh2XewQ2T+IhJTOYmpP8Q0PCIxmmRO9T+xI4blmTUthdWxc14hJaZyElN/iGl4RGKajdQt816xI4Zn+FWCx/9gJaZyElN/iGl4pE6Tz5yxxI4Ynvc/2tv766+OeQWTmMpJTP0hpuGRiuno6x+3I8bDiOsedc4rmMRUTmLqDzENj1RMh13RbEeMB3PMxvrcKTGVk5j6Q0zDIxVTcxGqq7vbjhqeru4euW0ZjMRUTmLqDzENj1iA9HGzdlOrHTUebp0f4wv5iamcxNQfYhoesZhmGtXClRvtqPGwbbc+fuJ6h35iKicx9YeYhkcspjpitz22xo4aD+0dXSoxMaa36SOmchJTf4hpeCRjGudrTfuI7ao+MZUzzph+51d/UufOelrEb/78KbUhhjeuMBDT8IjFNKbf0f8stz2+xj2/YktM5YwzppUCMQ2PWEy159y21I4aH6ve3e6cW9ElpnISU3+IaXgqLaZb47oIRUzlJKb+ENPwVFpMt+892Pu/qjrmV1SJqZzE1B9iGp5Ki+mufe0qlnfhJ6ZyElN/iGl4Ki2mhfc4vczjGBqsxFROYuoPMQ1PxZ3mf8xpvizEtCwhpuGptJju2HuImIpCTMsSYhqeSovpBzv3m7A551dUiamcccbUvMHE60Ku2bhTHWjvsiOHhZiGp9JiumT1Zufcii4xlTPOmB5rfhKbJ90lvPgh9frGeN79h5iGRyymJfIbUPX3LHfPr9gSUznjjCm/m28lpgNGLKYl8rv5sZziG4mpnMTUH2IaHsmYTo/5usH+Q529Z1eu+RVbYionMfWHmIZHLKaZRrXopU121HhY37Kblak4xHTwEtNBQUwb1Yr12+yo8XD2zKXuuYWQmMpJTP0hpuERi+nkBtX6ySE7anja2vUp/iUxvTG0kZjKSUz9IabhkXzONE6eWLXJPa9QElM5iak/xDQ8UjE98Zr5dsTw9PTk43nbvSMlpnISU3+IaXikYnrq9CfsiOH5y7p/mZg55xVMYionMfWHmIZHJKY6ZJfc+ZwdMSyHO7v1MRPjc6V9ElM5iak/xDQ8IjHNNKolq7fYEcMyrXGVe06hJaZyElN/iGl4RGIa08WnTdv3xfci/c9KTOUkpv4Q0/BIxHT8tEV2tHC0d+jTe32suuYTi8RUTmLqDzENj3dM9Sn+3Oc32NHCYK7enzXzSfd84pKYyklM/SGm4fGN6fAfzLMjhWPGvJedc4lVYionMfWHmIbHK6aTG9Tcv4Vdld6+cI0Jl3s+cUpM5SSm/hDT8Aw6ptlInX/Hn+0oYbipeVU8/yVJfySmchJTf4hpeAYV00yDOvf2Z+wIxae7J69+NPvF0lyR9klM5SSm/hDT8PQ7piZk+rTeHGv3PrXefnXxOdjeqcZNXeSeUylJTOUkpv4Q0/AkvnV/77stfdaJD6lhVzSrE6+er06/abHK3L1c/SHwC/OXvbm1MA/XPis5iamcxNQfYgqGg4c71VX3P19YCbv2V0lKTOUkpv4Q06FNR1e3enjZuyrx3Qec+6mkJaZyElN/iOnQpL2zSzWt2NB7Sl/KF5k+T2IqJzH1h5gOHfL5vHq7ZY+aMmelSlygV6LlGtE+iamcxNQfYlrZfNx2WK16b4e60bzTU7ZRJSZ5PNalJjGVc9Jvlqnlb24tXIEM6cp3/q2O/f5c55wGZaZRPfjsP5z/VrH93cqN+hvM46LDxDnqpXe3O8cupive2qYuues595z64QlXz1e797erlta2ivDNLbvV2k2thX3z6yfWqol3PqdOMD/wL9an8fqHtWsflL3EFLEENKe45uVIZlVe7prtMFfhy+lKvITEFBFRQGKKiCggMUVEFJCYIiIKSEwREQUkpoiIAhJTREQBiSkiooDEFBFRQGKKiCggMUVEFJCYIiIKSEwREQUkpoiIAhJTREQBKzamuajVucGIiMXxoK1PhZFtaHJsLCJiccxFu2x9Kowrmsc7NxgRsRhmol/Y+lQg5jkM10YjIkqai/K2OhVKrukF54YjIkqai3bY6lQwuajbufGIiBLmtOfcd7wtTgWTi6Y7dwAiooS5aJmtzRCgPnrNuRMQEX3MRq22MkOIbONG585ARByMuaYKfSlUf8hFi/QOyOuVqnvnICJ+kebKfX3TWluVIUw2Ok2vUluJKiIO2Fy0N5Ftus7WBAqYqNZH6/THfTqsnYn6Zr2zEBGP0KxCs1GbbsSWRKZhgq0HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBpkUj8B4Aom+MbT+3JAAAAAElFTkSuQmCC\"\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  \"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,iVBORw0KGgoAAAANSUhEUgAAAI0AAAAWCAYAAAD9/x8lAAAABHNCSVQICAgIfAhkiAAAB3FJREFUaIHtmk1y29gRx38NItLSzAnMnMDMNkmV6aqpynJ4A9MnMCSSVaG0MLwQsRBlwScQdYKRVlmlRG5mG+oEQ50g1C5USHQWj/h+/NBEtmcm+q8IvEa/fkD3v/v1Y4VNOAxa/Pm7Kj/+Y1oa8/wqf/nr3/nTd3fW8Wf8ZuGuHWn3mwgXwBvreGUvBBpAg8PgHZ96wy9i4TO+Ldr9JiKvVldjBr2RYxXsntSBiw2Khoi8Ta4dLjgMWk9o6jN+Cej0PURmwBiJromo0QkaZafpntSJ5AaR6gZFb0v3nx3nNwipMuiNUB2iElKJJqD1fHry/CoqF2sdxjjF+do5jOOwMVVl6U6ia06PJ7nxTtAAXq+uxiyY4pI66WL+mdCf5Z7pntRR5/tUhkt+F1WJ5BUitZINqlOWMibspbVYp++BvFhrd6w37E1NYFVeI2p5TzJh8e9xycZ4bSqvcs+JjviP3CW2PMaOGF5Qo6KvS2sVHXF6NMbzq7j7783aZcbZ3z7n5LyglrzjiLvk+0WYOUSqqNYYHE/oBM2807h7VyD1zJ1rBr1RsuBSytIDVFoIr5JbDhe0+zPOjq6sCxY8YqdQR4BJQaIBfFj9/gjzEPYPAPMiK3t/APKMFomHJI51D/PP6N4QkdfYIGKquVwtJuuDIYbLGJiiEiJq141CZW/GYXCQ6O6e1ImcH4AaogVxAVfHq3U/zg6AdhAivAexmCLQCeKa1DfqFSDvNC61ZNzRMWDsFuqrJQ1BjHOhszQ9tftDyLxk5ZbFvJUsWvWHgkkfGRyFLOcNlNvC2MWqLvrfYSI2TK5F3hrjV/CCWi5dRnjWKLfB4SKn66kgUkX0HM83jBLJFcLTz9MJfOMwXwhLQtpBCPITyE+4tFg8DA3THAatTKQah1nOG4T+DM+vlmoc1UvOjoxnGpkGlf1RwjgiVZQL4I9PYvyg59PutxB5CUAFD/DMb/WTKFO949NROTWqXiISU24NJ8OYDg3iyEofOAApMiAs5uV7Wd1ZlhSp4u7XgVFi9zrdomucfIsdSjMhGNU7IC5c87LGjsfDpECveNs1karnGXq7Z0kziVZ3fwhkc/c1Z0cpA50eT6yOg9TpBD6Dnv+zDC5CxV+1AAB9i+f7sF/NObuIvRAXmSZpFqDTbyWs6tgYQCY5+U3I6x7RDpq5dF3EQq5y9chm5ZvtyM4j0lor2wl2m25HuFTUz7FIhJdflFbTSOaW5SplxUVzzCahP6N70kKdf6aP6nviXGmD8pJuP18bRLy0pWc+9YbJxzZR7KFaS51dxwyOdvvQ3xIVbmj3fZYP1zunURu6J3Wy5dGuTv4EcBFpZq7v1+58iinL3bspFM1wejyh0x8nUSxSxQtqayNLaKEFdrA5TDroAzfGHn2f3+XJbs4ZUcvVbvEOIY+bUnSqzjg7+v1G3SoNsLCMSWGGEYUayBB3H9rBEOFywwcv22GCo4E69h3uV4BDvCsBUP61Rs6SssSeJ7VA9ztT8Q4wL/caoFRjbabxFiojVEaZ+gPgnmhu3+WVdKxpQ2R1Z1lV9S6xafngoXppfdY4xtOk8K8EFzTDDNQ4DFp5tpEZEjUIj1dbvP4Q+N6iK+4xZIu+8cbZVe+QQqQrtXzhWMACD7cw/3IDy6ydm1ucqGVNEYYZCs6+rli14hpHU5vMHC28wMfVJopXWOMHvGBYCjCbHVHRrq8PFyVESOla9JzuySRpui3m6Ys1PYFsN/g++WX6OIUew5aPKTIsFcom6j7YH8AwV7uf0r3yeSubZXc4u+R+Y9euNcIbVKuIZFsSYalpGdtu2gfh6n1dETO96ZXk17HJDrMrSq83lQFbZbW+pS7IwVk14a4zhpotdtxniR3GbMvzPQGJTEPK1sdRPn+x4iwbfcJ2Boh3OF/KnuI7RLc36Aa9EZpxkuiRfRzzXdKgrWwKtIKsm2mOml5Spt1i2eIXYPo0i3mLyt4koUyRKhE3dE/ecHo84TBo5XobABHv+HQ8sZ5VKbec9Ur7+18P9JxOUHZGiQ6sDALmHbr7U+BFrt1gjjjKTqTUcg2/SmTRu8UO1atMgd1aHdFMrLIwIi0rPtAO3iJMUa1Dtl7TrYFlnMZsl5urYs7QZew47b5nIidDXxFp+z1yhgjZovSO5UNj28S/bKwr8jfsWEJ/RqfvJ8cAqu/xgiFKleSIIDtFVq9eMrA54xY7luLj0iT7zYpzxbIS+ajTSGWpATUkY4hyu/b4J4P07On0eEL3pIE6eccpdktVL3Nd13wj6x5Hm5xt6D+oTJLzF1tRFzFdnX+sL/p2kdk2T/mBzUU7pJ3brO5sN3dwFNLu1xFqCCYNLBji8hE0PluqAy9WG5AZEVf5LvYj7Ah7U7ygTgUP0XqqG+MAwpTFKgWeHk+MrPog9fx30zHIiOU8LE5lnb50x9Bp6jhZmOODfF+lE2RbTG++ZpPpGd8G5f/TnB5PVgXufX5AxyWHySLi3bPD/H/A/s+9ouMotywemlZZI3Dw/HfPZxh0T+p0+qPkiN+GTv9XvEt6xs/BfwGhhmnYcaydgQAAAABJRU5ErkJggg==\",\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  \"f4c63eff-d26c-4248-801c-3736c7eaa93a\": {\n    \"name\": \"FIDO KeyPass S3\",\n    \"icon_light\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhYAAADfBAMAAABYEYe1AAAAG1BMVEUATJhAebKApsy/0uXeu1zmzIXv3a737tb////LZn6SAAAPyElEQVR42u2dTZKjOhLHAW+8VFRtWFL0hiX2bDhAdb8L9GIO8GJmDjAx8ZZIet3NsccIBPpISSkM1eCyF13RGAvpRyr/qQ+SJNE+py78+fWuf76BZ9HE/Khl8+J2IGu6XX3MCiOq9wPForZY5AoKIo6kza5ZIAzjO4oFsVgoX96soizfcGb4+1ik0WYBs+AWikztP8IimGYr+2MRrt2fKBatVfBZtZlqZJLtmkUa6TkdLIrE7YnY1DfqpNkzi5Bh/BfHwsf4RoCXt091o3LeNYss0ixAFl5FJVnvKgQeku2ahd9sf+BYeBX1ZgxM6Eh1O2/fLE4xgupg4VNUlvSdoz/pfLOfZtcsfNUDzAJiwXw9r+cgWBRnwWXXLE4xnhNk0fq6SDuy4P3BvbNIYjwnyMKnqF2bScNphO/YN4s8xiwAFtwbtUwsXrsDsHDGW+84Fv4x6sCiLISt7J6FyzB+IFnYilp1tr8QgHbvL1yG8R3JwjtGvVnNwKJO+960exawrP58x7Fg/lhWxhfD32b3LE5ozwmwaP19rtdSEV/crtLuPO50GcavdyQLEiiNnAYWt05SZAdgcUKbhcWCh9yPGKf2naXkOx+nOg3jO5IF9U3jDCfkE5XqCCxynKACLPyKOnSiZrSgtDsCixRrFhaLcExfD8LCyc7nO12G8fMdyYIhfA9J0qq7kn2ZhYdFivOcFosWEcUy/GrMLljovfzXO5YFwejzMMdXdUdhoZn231gWHBnQl2XZdYdhod3P71gWQUXd7yfBGcaPdyyLIqioh2ShGMafaBZJ/KrkIVjkCBUxWLC4afXjsEjj+0gdv0B7DBZKO7C+k8QtPR2IBcowvnkVNesehIViGDgWB1bUIItTZAx+YEUNsph7+y8UiwMrapgFwjC++brI6YFYzIbxE8HiyIqKYHGOmeMj+KXZ/bMgC2T122MoqsXiy4J465tnGud8YBZV7TGM4FpRceCgE2DBPYYxyurXP10sDq2oNgvA/Zmy+k8Xi2MrKsCChuKtr52LxbEVFWDhNYzee/7hZHFsRYVY+Ayjl9W/XCzYsRUVYsETr2H80blYtAfvIgCLzierf7//x8ni4IoKsvDK6tfOxYIfXFFBFl7D6Jwsjq6oMAsaWGiGWdRHnsZxsgDEMQuzOLqiOlhQ/3oiyOLwiupg4Y23HCwOr6guFrXXMEAW5OiK6mLhjbdAFsdXVBcLv6xCLOixp3F8LHzxFsji+IrqZNH5Jj4hFsnhFdXNwhdvASzY4YNODwvAMM4eFvXxFdXDgrodAMDiARTVw4LH+ItHUFQPC8vsfTryEO7Cw4JHxBePbheGYfjjzuKh/YXpPf3jkfahdcQQh8A49QFG7H4W1PEFFHeSB447DcNIQ/NajzweMbxncL7zvplf1qeMKRv/CXrdrv0vvixvuMhSo5fpZcHx8+D3qCqT9mckYuOSkMjSpk0CXOVVyjBUiBqTwqc+w+I37Bq8x+C81nJVfXNdn42HxixtMwuuXEzn1wKtKGxXflUu+YpkwcFmrTvfWTgrwPRtEBMLrjlqLZEdjsUFVrzg05NA1191HvzirsHIgprjZEOzVBgoFqZve8WxoNDRNddHLJ+rtGxkQQwWtQc6hgV3SV5ICAngBbHrZohO4p1kHli8GfMnwCzTaxQL55RdiAUF2rTiemrte/6RgXNJxLchF8GCOnc3h1hwQBzXW2fn3gc1QBa1d6c2ggVx7m4OBou1fXvX239Rex+MhVhw/07tMAvq3vYeZMHtFqH35ZyXmMV8lyEWrf8nYRYkcabRCg8i/tEhWcTvgJ4alt3CQvZm3mWVRWk25aWCfhJkMVeyj05Lovr5JQOq1fbxEV0HroYAMVtoqVFRZvwkyKI1SiyU27Ymi1hV5aYkUt2emO14apfWZEgWxIxiivkSa7KIVdXW6km1Zk/MRmoFFNNFGxQLbt8kMh1Zk0XsfvDCVhuiVoNZpTCgWKK2LsSC2o6MTbdtVRaRqgrYDlUFiFly1AJdj6rFhFjUQLVqyXdVFnGqyiBc6n2zM4YUkLUR5WCIBQEKoPIqq7KIU9UWzjc1Owy7RzjSMs3EAiw46NLJeHBVFnGqWocaxuDBmtnxuFL1AAu4AFmRdVlEqSoBr6jUg5lFUNghF2ZY6mTRgsbKxqPrsohSVdhwlIYxs04tXKRyOMCihp3YiHhdFjGqymGH0losTE5557ivYRYF3Mjh8NosIlSVwTdZaZiDRQVTTREsCFyl8Vcrs4hQVQp/zU0WWahXqccDLBwF0E1YRKhq67jgAhZFFIvMYaL5yiwiNiu5WJCpYVYvcsGd+46fBfOyOK/NAq+qrYNUYbA4Q67kHhZnRydbnQVeVeugwaNZtPtkgVfVYm8sTquzQGeJWcrifC+LysEiW50FepO8i0X7OCzQqrqURXYcFmhV/QwssKpaR7Pg62jqB7LA7gFuo1l0W7HgG2kqWlVdsVaIRdJ5QtVlLNhmLJCq6orBF7Dwj82SIAu6GQukqlKHL/GwWDZmD7Notxmb4VXVMX8RZnGGqSqtzSNZFJuM2SNU1aUKHhaOOb56PgyEY8ycKHMNjZstWCBV1dH7PSwcc7/EbO3JMWfEfH1s9Tm+GFV1zF56WDDfxsvKcB36nck9LOgm8+BRqlr7ZrVhgwfJtqqBuZZoKw+LYix1ExY4VaWwL/GxKKB+pa0LEtPYuLX4VMFdJN+GBU5VOdx/fCyCa8v2imtrLUpWsHtrtmGB3NJHbMPguZdFcM+BbHpjfJ1pLLRt0zzZZJ09UlVrK/rg5Oxl0dm++KIv3DKjTM1qJAtNyYtt9l9EqiozQzFOkgCL2iyOOTa457qF5joLBcYl2WZfTqyqEmN/FklCLKaCx+dBrhbrQm3J9ERBY7CYnht5UzYEbMMCmfZgNp/+PdRvQ8u9LObel5U3EoVd/LRy99J0ZQJthquG2r186fi8p/G0HQvc42fAXtcQC+rf6+rYP5sDLOzqbcQCOVato1k49u02/hOaEIu0244FUlV5PAvqNwvwBH1TdeV6Kf1GLLAzwHU0C+i+a/GC59JOFmm3JQvk42ecRLNgSaBo6rQaJ4tmUxbYGWAWzcK2pVNgONSEWLx2m7JAJxO6RLMw25oGvFDemSwIPO22GQucqpowsg7BQoeRNn5je+0sFnrPxD6TuZwFPvnYVa83goX6eG8W6Hmvnc1CozkXkJTap8Gw+N+/tM+/wZN4aX6cD5/Lx5Czarrp6VwEeHvkY9/pF3+R+msGlTH79Ny4UsBOUjMojS4wTySNoKq477X5C27dn/2lqUCyWPJhfj/wZLFnFqgnGJ8sPhsLhn0U/hOwoPg45+FZ1Oh0EY/PguCyAnwGFjTZTlKPxqLeUEYOxoIlG7rOg7EgyYauc+8sSmhiIvucLLRRd4HMIfKYLLiS5+NK0LllHpKFnIAqy9BU1eOzoJ4Vv8/GovXllPpkLOqPNIudsyg+0ix2zsK/ZP7ZWeTdJ2Vh7zl47T4rCy1b7eYodj82YwqNtNqYvH9tcAfjVF4WcjPZb3Ze3fPzZPFk8WTxZPFk8WTxZPFk8WTxZPFk8WTxZPFk8WTxZPHgLK5lGdiP7fmw8q73tOmzdDGb+jdhcblrc0DrfPADV2X17b8kWW8nzyIWNFmDxdJlIbXhdfK7WZCVWIzLhRzoLqzCsODJIhbXZjUWPFmJxbgcQgADIQmGRbuIBXM8B5gs6yJZrFmfdRZyXcQy+hl3JdZ2/CyKRatLrTDIwmpEsuy+NnezGBxwfh8LsmhVfk0WdfSmQ5BFf1ez4a+jjwRZLHv3+dBH1mFRRO+oglnwUUo40Nl5g2OxyGOxbkUW6SosegPL/TdwGxZdt2YfafQwshpCUTWwnM7gt+MOFtSwcWZEoyqLOVLVWGRmOPwlFPNWq7JotSawQdKKOV64zA9+3v6QKbvb2WTB5XvFhvq9aRmPKvXlCkN8mdm+U3O7+mN7fXFnNr2kJeuLo+NT4vJkkW1NnrBQU2cpG2qZ0vkpe6qlGiGJm0Xflrlxl0TLMaixINprCyvFQpVIvrYzbZksyNosuLprhFqbrYgSVWpZ/2wWtcKCq2UYLGr1O4UFVYc1WgEyJaHBIlmbxVC1VwW/utuKqv8JsGjH981V6tDiZLNI9BQ/lTYakDC0AmTyxs1ZcGWXmagLEWwuohqFONI/Lz6m76qEj1NG+BqLSjbu9oOXoZOrLPrxuMBbDbU3WNB5wMuH+3MZDIOKVEtlZbLIpuvf1FD44ztZKA8LMnG76dAjSH8pGTTItG65W1P730kW8qW1RFJQ4s56tEKioFPdpYAhRwZEXLKYU7lqLBqFheaKF7MY/Vwjr8WTKZRj0nnVo3E3HhZsbhwdDU14eoMFGf8O3+naMQlMPRynUz65BmBx6tZnMcA4iWvNje0vSGVVqZLtzcVC6SP1nIEzM1lM2gCwGHQslxHgqPBMScGnsjhvwWKAUcnqzizU4KHqJhkMsphCn8JiwaefF8Mt12J2ASMdgbHZgVcQi2oTFgLGCWKRKrHy2AoPi25icZJykBos2NT6YnBI+vhFwMjFNTiZk6s0H8hCCAbEIsOzKFQWZ3V2I4KFINC/we88oZgvYbDoNmLRX/lOFmQMyCrlFJBF52MxSMitAKK+7O9DWQjPfRcLdTxCltvFIObDS3Zu39O0/HgWFGaB9xftcPzePjJoiLCLdCzgw1kwmEWCZkHkAE7VEZtFN01z+FmcahF09ers0hGQxXkru5hvYpAFTeSPq1E+nCxOIRZZz4M10glN8QUfORYhFsV9LHrhsliMIWjHqyALog7CW9lEgEUx/r36/EU9p44/jcMkhWPiZEHkWG59HelbKCw1yKKQyxqy3aJuV51FM/6gV0qeuXXkNI1HanGxQhrGEL+L6F1nUc/vEW2GeG4Ji3FhmSisFRbD/Mo1UVkUYsCpjFO5+PmUN3Mep14SnUU/vhXRZJ9Fz2IxVERMYMlx6lUfp/bVycRtqwwWt39f+nFqLUfX+bK533ndzGYxTWspLGpz/kLL7aDPXygspldRjNNDFgttEqlWlya5rCCd00TqLGTWkemE5j4WDcBiet+8woLCLF7VyVwOsJDv/aIhFqk6r5XPI+mzml5dZzG9/0Sm9uvuYjGXr7JgNovheiaLTJ/kv9gsqD7feXKyqFzzneorHHQWw+mzIed3sXjpQBZDo15VTR34GCy+mAseotyXdh6bjUVNg9HGxaJyzoPLo1VnseAyZC9GG91kjxJbuOvm2ju7FlyV46i9N/D6yDW0gehtuDH726/VbpfzIbA+t8c8jU8Wa6yRPhgLuu0j7QdhcR0XzLbMdHAUFkUfE2+ZLudALMgHZH04Covt86IcjkXzZNGHlr2zaH7LlcWWov8DwifEzKp4rUgAAAAASUVORK5CYII=\",\n    \"icon_dark\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhYAAADfBAMAAABYEYe1AAAAG1BMVEUATJhAebKApsy/0uXeu1zmzIXv3a737tb////LZn6SAAAPyElEQVR42u2dTZKjOhLHAW+8VFRtWFL0hiX2bDhAdb8L9GIO8GJmDjAx8ZZIet3NsccIBPpISSkM1eCyF13RGAvpRyr/qQ+SJNE+py78+fWuf76BZ9HE/Khl8+J2IGu6XX3MCiOq9wPForZY5AoKIo6kza5ZIAzjO4oFsVgoX96soizfcGb4+1ik0WYBs+AWikztP8IimGYr+2MRrt2fKBatVfBZtZlqZJLtmkUa6TkdLIrE7YnY1DfqpNkzi5Bh/BfHwsf4RoCXt091o3LeNYss0ixAFl5FJVnvKgQeku2ahd9sf+BYeBX1ZgxM6Eh1O2/fLE4xgupg4VNUlvSdoz/pfLOfZtcsfNUDzAJiwXw9r+cgWBRnwWXXLE4xnhNk0fq6SDuy4P3BvbNIYjwnyMKnqF2bScNphO/YN4s8xiwAFtwbtUwsXrsDsHDGW+84Fv4x6sCiLISt7J6FyzB+IFnYilp1tr8QgHbvL1yG8R3JwjtGvVnNwKJO+960exawrP58x7Fg/lhWxhfD32b3LE5ozwmwaP19rtdSEV/crtLuPO50GcavdyQLEiiNnAYWt05SZAdgcUKbhcWCh9yPGKf2naXkOx+nOg3jO5IF9U3jDCfkE5XqCCxynKACLPyKOnSiZrSgtDsCixRrFhaLcExfD8LCyc7nO12G8fMdyYIhfA9J0qq7kn2ZhYdFivOcFosWEcUy/GrMLljovfzXO5YFwejzMMdXdUdhoZn231gWHBnQl2XZdYdhod3P71gWQUXd7yfBGcaPdyyLIqioh2ShGMafaBZJ/KrkIVjkCBUxWLC4afXjsEjj+0gdv0B7DBZKO7C+k8QtPR2IBcowvnkVNesehIViGDgWB1bUIItTZAx+YEUNsph7+y8UiwMrapgFwjC++brI6YFYzIbxE8HiyIqKYHGOmeMj+KXZ/bMgC2T122MoqsXiy4J465tnGud8YBZV7TGM4FpRceCgE2DBPYYxyurXP10sDq2oNgvA/Zmy+k8Xi2MrKsCChuKtr52LxbEVFWDhNYzee/7hZHFsRYVY+Ayjl9W/XCzYsRUVYsETr2H80blYtAfvIgCLzierf7//x8ni4IoKsvDK6tfOxYIfXFFBFl7D6Jwsjq6oMAsaWGiGWdRHnsZxsgDEMQuzOLqiOlhQ/3oiyOLwiupg4Y23HCwOr6guFrXXMEAW5OiK6mLhjbdAFsdXVBcLv6xCLOixp3F8LHzxFsji+IrqZNH5Jj4hFsnhFdXNwhdvASzY4YNODwvAMM4eFvXxFdXDgrodAMDiARTVw4LH+ItHUFQPC8vsfTryEO7Cw4JHxBePbheGYfjjzuKh/YXpPf3jkfahdcQQh8A49QFG7H4W1PEFFHeSB447DcNIQ/NajzweMbxncL7zvplf1qeMKRv/CXrdrv0vvixvuMhSo5fpZcHx8+D3qCqT9mckYuOSkMjSpk0CXOVVyjBUiBqTwqc+w+I37Bq8x+C81nJVfXNdn42HxixtMwuuXEzn1wKtKGxXflUu+YpkwcFmrTvfWTgrwPRtEBMLrjlqLZEdjsUFVrzg05NA1191HvzirsHIgprjZEOzVBgoFqZve8WxoNDRNddHLJ+rtGxkQQwWtQc6hgV3SV5ICAngBbHrZohO4p1kHli8GfMnwCzTaxQL55RdiAUF2rTiemrte/6RgXNJxLchF8GCOnc3h1hwQBzXW2fn3gc1QBa1d6c2ggVx7m4OBou1fXvX239Rex+MhVhw/07tMAvq3vYeZMHtFqH35ZyXmMV8lyEWrf8nYRYkcabRCg8i/tEhWcTvgJ4alt3CQvZm3mWVRWk25aWCfhJkMVeyj05Lovr5JQOq1fbxEV0HroYAMVtoqVFRZvwkyKI1SiyU27Ymi1hV5aYkUt2emO14apfWZEgWxIxiivkSa7KIVdXW6km1Zk/MRmoFFNNFGxQLbt8kMh1Zk0XsfvDCVhuiVoNZpTCgWKK2LsSC2o6MTbdtVRaRqgrYDlUFiFly1AJdj6rFhFjUQLVqyXdVFnGqyiBc6n2zM4YUkLUR5WCIBQEKoPIqq7KIU9UWzjc1Owy7RzjSMs3EAiw46NLJeHBVFnGqWocaxuDBmtnxuFL1AAu4AFmRdVlEqSoBr6jUg5lFUNghF2ZY6mTRgsbKxqPrsohSVdhwlIYxs04tXKRyOMCihp3YiHhdFjGqymGH0losTE5557ivYRYF3Mjh8NosIlSVwTdZaZiDRQVTTREsCFyl8Vcrs4hQVQp/zU0WWahXqccDLBwF0E1YRKhq67jgAhZFFIvMYaL5yiwiNiu5WJCpYVYvcsGd+46fBfOyOK/NAq+qrYNUYbA4Q67kHhZnRydbnQVeVeugwaNZtPtkgVfVYm8sTquzQGeJWcrifC+LysEiW50FepO8i0X7OCzQqrqURXYcFmhV/QwssKpaR7Pg62jqB7LA7gFuo1l0W7HgG2kqWlVdsVaIRdJ5QtVlLNhmLJCq6orBF7Dwj82SIAu6GQukqlKHL/GwWDZmD7Notxmb4VXVMX8RZnGGqSqtzSNZFJuM2SNU1aUKHhaOOb56PgyEY8ycKHMNjZstWCBV1dH7PSwcc7/EbO3JMWfEfH1s9Tm+GFV1zF56WDDfxsvKcB36nck9LOgm8+BRqlr7ZrVhgwfJtqqBuZZoKw+LYix1ExY4VaWwL/GxKKB+pa0LEtPYuLX4VMFdJN+GBU5VOdx/fCyCa8v2imtrLUpWsHtrtmGB3NJHbMPguZdFcM+BbHpjfJ1pLLRt0zzZZJ09UlVrK/rg5Oxl0dm++KIv3DKjTM1qJAtNyYtt9l9EqiozQzFOkgCL2iyOOTa457qF5joLBcYl2WZfTqyqEmN/FklCLKaCx+dBrhbrQm3J9ERBY7CYnht5UzYEbMMCmfZgNp/+PdRvQ8u9LObel5U3EoVd/LRy99J0ZQJthquG2r186fi8p/G0HQvc42fAXtcQC+rf6+rYP5sDLOzqbcQCOVato1k49u02/hOaEIu0244FUlV5PAvqNwvwBH1TdeV6Kf1GLLAzwHU0C+i+a/GC59JOFmm3JQvk42ecRLNgSaBo6rQaJ4tmUxbYGWAWzcK2pVNgONSEWLx2m7JAJxO6RLMw25oGvFDemSwIPO22GQucqpowsg7BQoeRNn5je+0sFnrPxD6TuZwFPvnYVa83goX6eG8W6Hmvnc1CozkXkJTap8Gw+N+/tM+/wZN4aX6cD5/Lx5Czarrp6VwEeHvkY9/pF3+R+msGlTH79Ny4UsBOUjMojS4wTySNoKq477X5C27dn/2lqUCyWPJhfj/wZLFnFqgnGJ8sPhsLhn0U/hOwoPg45+FZ1Oh0EY/PguCyAnwGFjTZTlKPxqLeUEYOxoIlG7rOg7EgyYauc+8sSmhiIvucLLRRd4HMIfKYLLiS5+NK0LllHpKFnIAqy9BU1eOzoJ4Vv8/GovXllPpkLOqPNIudsyg+0ix2zsK/ZP7ZWeTdJ2Vh7zl47T4rCy1b7eYodj82YwqNtNqYvH9tcAfjVF4WcjPZb3Ze3fPzZPFk8WTxZPFk8WTxZPFk8WTxZPFk8WTxZPFk8WTxZPHgLK5lGdiP7fmw8q73tOmzdDGb+jdhcblrc0DrfPADV2X17b8kWW8nzyIWNFmDxdJlIbXhdfK7WZCVWIzLhRzoLqzCsODJIhbXZjUWPFmJxbgcQgADIQmGRbuIBXM8B5gs6yJZrFmfdRZyXcQy+hl3JdZ2/CyKRatLrTDIwmpEsuy+NnezGBxwfh8LsmhVfk0WdfSmQ5BFf1ez4a+jjwRZLHv3+dBH1mFRRO+oglnwUUo40Nl5g2OxyGOxbkUW6SosegPL/TdwGxZdt2YfafQwshpCUTWwnM7gt+MOFtSwcWZEoyqLOVLVWGRmOPwlFPNWq7JotSawQdKKOV64zA9+3v6QKbvb2WTB5XvFhvq9aRmPKvXlCkN8mdm+U3O7+mN7fXFnNr2kJeuLo+NT4vJkkW1NnrBQU2cpG2qZ0vkpe6qlGiGJm0Xflrlxl0TLMaixINprCyvFQpVIvrYzbZksyNosuLprhFqbrYgSVWpZ/2wWtcKCq2UYLGr1O4UFVYc1WgEyJaHBIlmbxVC1VwW/utuKqv8JsGjH981V6tDiZLNI9BQ/lTYakDC0AmTyxs1ZcGWXmagLEWwuohqFONI/Lz6m76qEj1NG+BqLSjbu9oOXoZOrLPrxuMBbDbU3WNB5wMuH+3MZDIOKVEtlZbLIpuvf1FD44ztZKA8LMnG76dAjSH8pGTTItG65W1P730kW8qW1RFJQ4s56tEKioFPdpYAhRwZEXLKYU7lqLBqFheaKF7MY/Vwjr8WTKZRj0nnVo3E3HhZsbhwdDU14eoMFGf8O3+naMQlMPRynUz65BmBx6tZnMcA4iWvNje0vSGVVqZLtzcVC6SP1nIEzM1lM2gCwGHQslxHgqPBMScGnsjhvwWKAUcnqzizU4KHqJhkMsphCn8JiwaefF8Mt12J2ASMdgbHZgVcQi2oTFgLGCWKRKrHy2AoPi25icZJykBos2NT6YnBI+vhFwMjFNTiZk6s0H8hCCAbEIsOzKFQWZ3V2I4KFINC/we88oZgvYbDoNmLRX/lOFmQMyCrlFJBF52MxSMitAKK+7O9DWQjPfRcLdTxCltvFIObDS3Zu39O0/HgWFGaB9xftcPzePjJoiLCLdCzgw1kwmEWCZkHkAE7VEZtFN01z+FmcahF09ers0hGQxXkru5hvYpAFTeSPq1E+nCxOIRZZz4M10glN8QUfORYhFsV9LHrhsliMIWjHqyALog7CW9lEgEUx/r36/EU9p44/jcMkhWPiZEHkWG59HelbKCw1yKKQyxqy3aJuV51FM/6gV0qeuXXkNI1HanGxQhrGEL+L6F1nUc/vEW2GeG4Ji3FhmSisFRbD/Mo1UVkUYsCpjFO5+PmUN3Mep14SnUU/vhXRZJ9Fz2IxVERMYMlx6lUfp/bVycRtqwwWt39f+nFqLUfX+bK533ndzGYxTWspLGpz/kLL7aDPXygspldRjNNDFgttEqlWlya5rCCd00TqLGTWkemE5j4WDcBiet+8woLCLF7VyVwOsJDv/aIhFqk6r5XPI+mzml5dZzG9/0Sm9uvuYjGXr7JgNovheiaLTJ/kv9gsqD7feXKyqFzzneorHHQWw+mzIed3sXjpQBZDo15VTR34GCy+mAseotyXdh6bjUVNg9HGxaJyzoPLo1VnseAyZC9GG91kjxJbuOvm2ju7FlyV46i9N/D6yDW0gehtuDH726/VbpfzIbA+t8c8jU8Wa6yRPhgLuu0j7QdhcR0XzLbMdHAUFkUfE2+ZLudALMgHZH04Covt86IcjkXzZNGHlr2zaH7LlcWWov8DwifEzKp4rUgAAAAASUVORK5CYII=\"\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  \"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,iVBORw0KGgoAAAANSUhEUgAAASMAAAAwCAYAAABaFRysAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAEG2SURBVHhe7X0HeJxnla7aqBeXVELYwA11Lyxkl2XhhkAoCQTCLrtPlufChcveJdwAS9gssPAsgZDEXbZ6cS9JXOMSJ26J47j3Jlu2ujTSSJrRjEZlelE5933PP788VmyNF+c+a+fRefTpL/P/33e+ct7vnPOVP0kmaZJuYhqOhaZWm7y2603ZuHW7rN3yquzce0B6Bn1jv4eHR2R4dERGcS4yEhcm6UahSTCapJuaCDQnz9TKshfWSFF5tcyvqJa5pRUyq6hUihctkXMX62Qo9hyBiGFkhHdGYsdJulFoEowm6aYmh2tAFq94UZ6dNU/BqLC8SoqqFsn8qoUyu7Rclq5cJa7+AQWjoZHhy8BoaCjCKCbpBqFJMJqkm5oOnzgjC8qqpLCkQiqXrTS0opIymVNWIfMqqmTO/AVS19QsUaBQOBoBBCkcaRgejmock3Rj0CQYTdJNTWs3viKzi8sUhNREK69Urej5ohKZCzAqKiuX46fPXGamXdKKJn1GNxJNgtEk3dS0afsuKVm4RIGIZtmC6kVStmyFzCgulfk4n1dULIeOHX8bGEUiIT1O0o1Dk2A0STc1nTh/UcGIptlMABA1onmV1YZmVL1QNaP65hY10ziaRjONPqPRUcLTJBjdSDQJRpN0UxONrWVr16t/iKNns6AdUSOaWV4hsyoq5YU1aw0gwnPRYYAQjiYQTY6m3ViU5JKoyNCgSGgA9dMnQRlEBaOS0GkMX8Ngw9BImH2NBn2JtT2Cf8aAhUSjrHiDRvgzf49ROBzVRhLGP32NUeAkEgJPOI5Eono/iKCxoGcbRqQBRB7CMcS7w3hpAPxfvCCe1aula8ZMGZg1TwZmzJfeZwvF+8dnJPCHP4r398+Lt7xahvbvk+GeDjRMD/IZlnZEG2D8w4hrCKr7SFQbuJ/pxfk3R8F4fDDJjzgkgrITj8FLeAR3/OAQ8bAMwuB+eFT5D+A1Ggd4A3ny4JkBPZchlKG1VSJHDovn9V0SOXlSZLAP8eLdcBjlEARPzDuIZRRh7850WO5RCQz7kYcR8eKnIH5TvvFwGCnwlzDS4nNRZAyx4ocA+ADPw3yDEV49jFJgR2LlzIDoyYd3NKrphZGTENLROmKxaOExv0Pi0pJFHYO/ITzpRQiwXGgisYrfASxgFAOBgGx45RUpra6W+WVlMqeoSIorK2Xd5s1of1EZGro8IdZ1AO9M0o1FSa57Pik2y+3SnXabnMuYLmc/8kkJPjtbpLkBDZlNbGIiCAXRuIdjLYuVrwiCEA5GtOEO+kNytrZONr7ymhSXV8mMOYUyd0GJlNDOn18sJZVUpyvlxbXrxOsLGMJOQMOfhzEg6mFPCNyMasMfHgX4dLWIFC8V/8Nflgt33Sa1mcnSmZUkA5lJ4k5Nkq6kJHGmJUknjrbsJGnKTZJ6XDfjuj03R3o+9VEJfPdrImteg4Q6kF5QhkN+BQufHxCBNAEvl9GVwGgEUtX/zByp+7tvy8mvfE3Of+PbMrpqETLtkl4+YGRDPFHkC8IooUEZ3rxRvI8/KYH7vyXtmXnSnZ4l9pRUcSaDV/DXRp7z86QhO1d6/+Jh6X3iSXG/sloCXRdQwOBqCKCAPwWyGAXDCs1GYiEDOBTK9aEwfsOJD5k6Vie1KXdIe06BOJJzxIZ0Jwp2S5Z0JKVLa0q6XEixyPn8Amn+yL3ifOjzIo9/D3VQKWKtR3p9EPoYILMpBPFPOzPygjYBUIugDNiZKJFPhuskf9hoY5zU2N7VLSfOnJWaCxfF7upVNlhX2tGACOAmxdfhJN0YlNRXkILGliTdCIMIoaRMaUkqkNpv/S0Evib22NWJYEQgiq/cIQpLjGwut2x78y0phQo9j8OtsVBYXmkcK6tkNmz9eeVlsuutt7RhMSYFNcStWlAIvViUcUbF335ROn70U2mYcrccu3WKtENg29PSpCctVXrAvxOBYEQg6sKxCdcUcGdSsvQnpeGYpsLeiGDNTJdjGZnS8eH7RFauQnKAX68PwmPIsGqIcWQCUXyg5PV/9pvSi/j6UpLEjmPbtKlif+Jn+CmgGlYUR+nrElfhbDl870elLW86+AMA4VkPAt9V8ExPEgfAtBu8u9LS8XuyHASI2sCnE4DQXHCX2P7lXyRce1rLiXFDeTQKjKwyqPZilD/xnA+OQrsaVWTAw0f2SDPSdiC9KIILPE8UOsCLDUd3Roq48TzfM8o5TTwWAFNqvjSmTJXaj39KukpmijgBTOg2yJ8T/NBEUvxhgRIMcc7fGEaG+P/6idWgVREjtr/4a5Mu1ZlB8eA0Sf/1lFSL3rghB1rDNAgRjkOZ+Sq4pzOniTz9m9hjE9GlCmU1xzR5PXp9IVn18kaZU1YuzxbOV+CZX1UN+75S5sKmn1lcInMqynCvUl4/eED6gj6VJz96eYIcDRNej/L/sFvcpUXS/d5PiA3CfCHfosJsh5DYkIdOCEy3BYID4WnBeTOAqDkDWtAUCLUlVfrwbD/y5QT4dGVnaq9PAGZwZWZJDbSEMw88LHL2pERD/Uh1RPwB6kmXUzwQMUShS/m//Kj4EE8v0g2QD5xfyJwusmQFtCuPjLy6Rbrv/QyEOV36kpOlDhpcM8q7ATy2QWtrAAjVgu86HJsBRtZ0aHmp5C8VnYMBUNTq2nHej87C8YG/EndJKbih+QXRZ5nzhIUFk49akIo5/kGHVDNPYFbBYJGON16TxlveJy1MG/E1paVMGNpQho3U2MBDF85Zvl3pqdKWmiytyEsbyr8Z96jZtSN/Zz/059K9FNpSFFAJfmjK+Wm/kTcD4fUQGI3IKPm6ThoZQktB0IJAiIaRUOx8KDKsdRQPOjxnoLZkdHiTdKNQEtXwdgipNSdDzkCQLqKBupMs0mXJlNa774g9NgGxAcQqlQAUiQ6rIDj7PbJ24xaZAw1oZkmZzvkorFqoDkYGXvO3OWWlsmPfXhlEw2R7jaJXD+GcYETfQoDtaNgnAz//hbSCN2oQ7RAMakCelExxQago/FbeB//tFggI7nUgdAKcKHCtEP5WaIBtBCtcs3e3J6eLNStHgikpch7XPXg+iHyfnnKbhNauhBT3GgJ9BYoHI3T30vzggwoiDUwfcQ3k5IojLUci939JIqWz5PhddwMMk2UUv3VBsC/mGQDUj7TJey/uDSZD04C24YVADyZniAvnjqQU5DPFMDURdx86i14AwjlcX7zzdok89g9Inr4nA3Toe6Oo0zNDHxMVIQ2oGGp5fubo9BnkN11cWRnSmp2sIDJRMLWhbvDIvBGEOvkuAKk1w6KA6gBvbgRqeKyXlpRp4vjZz8FKJ3gZkX4EgpJRwdSbyCX4eyccyKiC2vP1cujgMdm395CcOlkjXZ094hmENsrqARF44v1GRr1N0o1GSc256dKBhua0wFyDQLVBSLopzGhcrTARElLMt6OqMS4pDz19g7Jx206ZMb9YJ6BxIhqHXhk46sG5IIWV1TpLds/Bg+KBGcb3QkP0cpijHLEw4hHvd38CQcgQO/jzgCc2+A6EnpRcFYBBmDHdlnRpScuA6VUgtqyp6MXz8Y5FhZwCTyBjIFh15kKYci1SA3CyplrUV0OtkL/TbOq33CXBZSsgMADFMdB5O+n9qE/qPvd5AJ5F/NnTYcLAbIH2VYd4nO+5BUACDa4gV2rBe0c+gApgQi0tAg3HnwTQSk6FRpUu7uRMPJsCUAWYQiOhllSHvDYg387sPK0fggHrpRvgSqAlQDnnzYF0e9W/ZbhkowDwfgOMoIIEwSLLls5jhS2vTdyf/bTUI64B8sg4JgjdAEU7eHOkZ0t7MvgDb7a0ZJjyhj8uCJ4d6eAXPDlQjiPQQqmZHrnrFhn43ndEXE4tRydqVuEAmhHxkdrRO+HB9kMTWrh0hSworZCKhUv0OLuwSF5cu0Fc6BBNupIjexKUbixKomALGpwfRwq4C418AGZCNxpUPc4TEus3VqfUjKgREYieLVygQ6yziypkfvkiDYVlAKaSKplRWCrli1fK1l1vjQ23koaiaKL0d7CRRo2RLfnRD+XIndNgCtDnY2hBztw09fk05uRIE4T2YnaqnL2lQDru+wsZePghcX7hC9L7+fvF94UHxPPA56T7z+6BNpUjHRCaFgIuAIE9Pk2rjhSLtGamSSdMDTeE34Men76zlmnvk5E9L2mvSrX+qg3XHxD7V7+hZiKFuwfg05ydDhABf+CtPceiwORGWfYiDTrQO8C3NWs6AGGKNAL4aYI1QJgvgLcW8NCJ0MX4cE3NiFpfHQS9AXy34R5BSf1NAIe66XeKbHpZAUf9xWGKuV81IU4y7sYV9SRkIlZP0Es2r5Xa+z4i3ozbxEpAniC0AzS7oUG2ZaTJBaTJzspOMAQ/XpiYdXyGoAUwr8V1DTszXA+Cb19Sjvifm49exi19gB8FIP5D9RqwcDk4/ClEzZlr0TgDm+vSGDjviLOyKwBSPT09sSeR2jjtaNJndGNRkg2NiYLYBEGtn4YeHMLJxu9JnQIBy4w9NgGhPjlqRqKPiKaZakQAIppi80urpYhgFDuaYdeeg2NNkb3WCEdcCEQ0+agZRcLSVHNWbBDAFvBH8HGlZqGR56hwX5ySKYOf/G/SveElEWszGjyEDGg4NDyqIysqeIiLQ9k6TD8MKTh1Stw//5XUvOfDAIJ8caVPVzOOmgid4AQ6B7SaI1NS5dS0dJFpd2gDJiBdVTtC6HzoUbHGwENHwyCQ/dQucd5ArRNmTV+qEX9LmkU6PvFR8T7+PfE//0sZ+cNMCTz9PM5n6/QD30+fApA+JM1Zt8AkhVaF/Dci1CPursxMCHieBBCcCE1puUJNrvl/fk/EAy2AzPT5UH5BCQwb2qaEUZ4RGElRmEoD6Cx4D3ni0L6/vlH8+49MHA4dkJEjByW8dZN4iudL9Le/Fd+j35aGW++GWZqvAwXUktqR5y6c25HvbnRmrTDn2jOzZG/BvbCjTqIOfBLhFAGqb2guRt1fPxgQ2zjz2tS4eV60cPHYvf3790soZPj+zFG1SboxKamTPoAJQqJ5RBzNYRW7PQHdR4YmGX1DM8rKxwJnxLKBzJhfJPOKSmX/gUMGgCEaznhhlOLnvJmo+BGZzlcZ6Jb+e9FzQxBt0Ny68qBNoNGPIND0afzV02iJELwE1BPrkRkl5cBPpntsEnx6thxNugtmXrIM0EwDmNCZXIv4O7LyNS2OGskTv8H7biO/4I1KG6cXSATCRS1jNCIDDz6iwkifFEe9aMoQ4O0pydI4zfBz1efcK+ee+qUMdbQzAvXs6uwcLUeOdRlzp3Tomz14u0POLF0np973EWhcU1WTO6cCnwYAop8P8SONDt5LSRPZsRYRRYWud8PRFlUTLRERY3XUja/gSNClo5t14SOjqH8ZppeHTMfOqcG6B6Rjxx5pvm2K8tbBwQ8cqT01Z2UrQA2mpABMM6X/m19HjC5VdHW+IZ3pAEzN+3USo1hQhjaHdqcjtQAgugAKKxbJnPJqWfEitNvYc8ymUSSmK4CZnphMbco8xndKV7o3vtMy0zEW5TI9I5jLUczntdxjvtd4MrU3phWf3rtRq0sIRgShRPOIrDaHbHp1h6rKnJJPjYhO6sJFixWI6Kim2VYEkNp78Ij4/GzcRhycHKgxA5tCKpIsbMjTqhXSAHDg0LLLkiuNAIsuCDt9RK77Pidy9hyeNXiaiChasAWNLhT1Z/SRSL+zVWTdWqm5fRqECT05TA8dMUrNkPosaGDJ6Wpu2D721wAGLrRkY9AIDcCAZBkADR0jBkYtCG8DI/DreN+HREoXi9iR5igYYZtDXPSlaAFqObAIRiREDQ6mq4QQnIOqVXR/8SGYZRnizczQAYaWXJhOudC2cE4thNqd6xdPgKdQbFIjOw9jwmgigt6Huo1AKaUpR5UFgTwMxQKySEUTZ0ZglhnwmLiRkbYWqXv0MYBPljRD+2vOzxEXyoAmcFcB2hDKtuUTH5ehgUYjk3wXYKT1wrxfJzHKicCoEm2QyUSAuCwPo0zMTDBcG5lAQBAYb7bzPBJhgVxOYTWZ49O6UkhM49OKP76b6JrAaKJ5RNSICESzCotl5oIS1YCoLnOhoq6axjWBiA3lLQBRIDareowQN6cDslUFVUpBQ8PS/rXHVEuhhuKwZKgPhqNJNHs8c2agIUd1BCkhoeELZ3QT/9A2whHOXWII4tIjvTOekq7kXKmDedGTliyetGypsSTDHExVs+MMtLBoeREgx68Kh/LO7MdAJBEYcZqE45//SaTXhvR84huBfoa88nVO4zQLg+um6D2LAABGYKIqIEEAyKc0nZX6D39MR7UGC3LkFHhtBQgNJCPu1BRpBmheeP+HkccBdWQzcs6+vqa2zmfAA/nhPMUQzilWODWAB3XCDiOE4EfwgB/vMDVS1hUCy+R8LTSi6RLIvlMcycY8LnYa9I9Ru6zJLxD3rg1GpJoeIZCmOc6vkxjlRGC0oLRMgehPBaMraSskygPDeA2F9y6f3X0pLe4UQA3p0iJd4934OJjeeLeAaWaSTNCL//3dQkkckZkomAVGYvZNdwyP6iOiaUaNCEC0oHKh0RAQOHI2o6hETbMFuN5z4LB4AsZkOHPBohYoIuLSAiYTVL8RHmhpEFv2+9VZSx7oe+mA0PUDJC6kZ8vo2cPqB+q/xNpViYIUZo+vTCPgdCgyCpEahRaBG54msX/689KcnCJOgJELaXF+DdPlaBJHjBz3fwXC7YRAxgQ01uHR7JgYjKCxpE0V35Y1KsgDCL5hxIJ3Kc4xGNayvOTPMExiimsE5eKEEHEmfHhVtTSkZqmTnJMR6ZtqybRA2FOkBaasNSlbpKvVYA2R0uRTSU1ERFjmg8njVBniMVZWGgfsZgoMT40wqnVomJd+5Ktb7D/8joIQh/nPohw4Stifmq2g3gAzsvs5mNVRahWIAAJHzcjM8fXQtYAR02Fg1jTAVry0WHZi4vOcrmK+GwobnWl8YHkHQwAa5G38/YiOELMIOWWFLg8WrwGM3OyNFA9G5jllQwPO+Rzf57k54GPK0LuJEoMR8mv2Dizs8fOI6COiaUYNiNs4cLtPHglMPNJHRNOMGhHfC4SCWrhGgaIp4Y+rp7gGihNyh4I+6Vu+WLWADgiZg+YTznvSKHjQjDJvRYvo4/Iso4UlIAo2mwCBRB9n6zWFDHGMAhZGysvFllYALcNIy52WJk1TM7R3DwAEm1KnyXBHDeKhdoAXWRxsM9puJgaj+tSpIo4WnWdDZUed9PhTrQ4mG53tY00qdkI+1WTjg2C8g3d8NrF+5jOIHzwhDKSmSl16mmoirSgnDzS44d3bNVu6kRgj4kUCCoEz+q7oJQqN+CQCrWd0lBobMkm05YI2+ogi6EgiOp/88gCTLuyzAixLxAtA7EMZ0J/FCabnLOkAz1RxplrE/o//iMQCxoxsaNYjyLtOP7hOYhYnAqPi8gotTwYmrUAAELrWVfv6Dv7xGA84UeTBDOa9eDDis+Y5g9nmGUwwIrDE0/j1cqoh4chAuTHP+Z4JTu8mSmimmTVxtXlEdFbTHDPnDikQlVXJnAWlUlyxUJ3V9BHpuyhAo1IYlxH4xzVb9FvwPIoG3/mD/60aAGf8Usjt6P056Y4akrXgzxAR9ArKgkpcAiLwgGmmz9Mo698MiEMVYEeb1ObcZoymZafosPn5aRZj1jbSpT9Etm5RPtWURKvjTF/GOTEYcbb1FOSrT5MTDzLIFhseNiYB4hxWoxYxiYuK6WdgGYUAziqskH/D9BoQ149/JPaCPOWvF3VD/xYDne90IruK5+mkUaNMjfwmJDLGB2NlQhllzgiEXkaEP40MQS1H/M42QFBhoAXBCYyuY2+JL/Ue5YMaZW9mipznSC2uPZxN/slP4wV1iWsEBLyIVuL1EdmeCIy4hQj5ZWA2jFbMTJlhYjp26qzY7M6xOBgGfEE5cOS4nD53QYLQ9niP9cj1l1te2yHrN70iO97YIxcamvE7ddxL756qOafBtBJMIhAdO3ZMfD52BuSRZWQssG7v6gQfJxFf/WVxmS3w3UKJwYi1Hcsz5Wj8PCKOltFZTR8RTTNqRASiBSWVsm3nm2PD/iRT7SQIBYe4sNPoVdCP4BwJoYSHcW772H06NN4NM43LFlwAIxuODVnJ0v/BT8twlCva8VbkGrxGpqCx9pA4BZzBBEKuJKDEN9z/eZ0l7ShIkzoI00UAixtgwqHqpgwAyr/NRBwePEkBgpYX5qo50sRgZE/PxzMuAzfBg079g61HFxYFn8nTj0VAMntgJZ4wgP9eP8B31C8jiyvlVEqKDGQb86y6oC22pWfFTDYA59O/A2eI32DRALEExGcG6eBXdCFvgAuABn8ZGXUr39RCWXzxgXnX/I968MwAmGyWw3m3q9nYBm2W86c4k5z1x46lfeqdyCDLD8QIRumFUmi6LiIP12qmmV2gAUJmmJjmoMPdtmu3zm5nHJ5AWN54a78sWfGCHD5+Su950cZ37n5Lqpcsl0XLVsri5atk6coXdfH3vkOH8buxiJlh9foN6lTff/iIXptkt9tl0aJF0tXVdZmPiICz+dWtiG+FvLh2DdLXefQadKrEu4iuQTNC+5xgHhHBiKNmel5UopoRNSICEatd/+kJDkR6OsMRKYGIm3VosY9QxGkKAGBGfTA7pouNws1GnJUGMEqWjsxkOZmXJMP3fx2PAYSC3OwksbgRrlRoWG/q9zCcsdQ7OJ9pBAx4cWd05u8lnGI4ymumQgsDINktyTrCVgPtJvDA98HfIIQJmgt4pD6gTuIEYORNykDa3coHuVUwC0LrAAAYwmyAkdnAVEti4BorPoAbLP2+SK/Ivp1yMTNPtY+L0Do4e74xLV1c0N7OTYdJ/cwftFxZlOIdNnYNSET2czK4brFYn3xC2j7/JbkIQGlOygQQ5wBY0sSODoAz03X+EI4MvbFzTlngotuGlFy0pDvkbJYxAklzutOSIq3JOdKal6z8NrIcAFrcbMXIaAglp/rhdRGL6E8Fo2vZA5szuqnpMB1qP3Q5cLeJV9Ahm/G2dzmkrGqRbNi8VYGJ97hTxUvrXpa5C4p0BwH9VBLub9i8RWbOnScVCxfpfdNHRDCaN2+eOJ3OsZE7EjXdykUL8TzzUiIt7VYJRNjyDK3p3UQJwUjFnYiBrnzpC5vl+erlMrdsiZpiz5eWyrLSJVJcBO2orExmLkRjWFIpbx7dD1MDEkGfUCJCXYR0WNmoWDl9WBzQJjjRkeYIfSS2zAxdjGnNTJX+v/kKeIJtzXZk2jfXQ2wLaCi+vdukOT1b7DQHIWw2ACABkWXQkZ0p8qlPofpppsHMxDtc/6WpJ5hnxHlSfF4zh2f1wKaNeyM0Ta+BdBgciYX37JWOjHTpgcC3paUai4PJL82h1DQZfG4m4jTmhWkCKndQ+znETBRANC4c6Iwf2rtW5MuPSBMHCXKN6QE6dyklVWdUNyM/jflJ6o+aKHTg+bMARTeAywVNiDsj9EM7uoD892sZIH7wZkU6zLxaICgQdkTvQO0lBKPxZpqRJiDpGn1GZaXV8tLajYrvpxsbZXZ5uWzfvVt/GyU6gV5/fbdUVS0Uj8fH/m6MvF6/7qu0eds25TOMzL+8datULV0qRRUVsvrll/U5muddXXapBN82W5fGMayT7URq6upkXkmJAldZFUBw62uaB/qq3onyu5EoIRjRGOHs6A1VK6R6VolqPb+rKJG5Sxeih4B5NnOuVFQvhYq5SkoLK+XModNax9SADM0hAeFZdW3jj08PH90r3Wm5OovZlZymvWx7OswRCBxXs3v+x8OXwOgdqA0VDoCRf992BSOOBl0GRrhu5UjenXdB0DkdAAzjj1MUR9XGnFgzmhiM2MQT07WAEQX+SmCEvlfj8EeQMlt5d6/YfvUb2Yv8tGQVyECqRTUcW0aq9KRniAfaIZdycADBmcypCakTBr7blJOqzv52S6pYwU9PhkXOca0fzLWrgVFE+bx++lPA6D/jwC4qrpAtr+6UExcuSumyZYi7Shx9OptLE2d8GzdtkRUrX7gUP4JpchN4tu7cKV50CMHhYdn46qsKSCfPnVOgOnP2nD5nbbchrVLp6nZcFs+m116TFatXy4DPL1te2ybLVr1wmXP83USJzTTkeH/NSdV4Fi5cLM/NmSPPvbhYnllaLfMXlMp/rFsi82HPblu/U6JOVDD+fOGYCnmNmhHgTo/UEyKH3pSu1BwVYhOMKHhWCDpX40e+8Ij43kEwUpoAjKidcfFqs4VmBuFVGdZz9ebe4GDEGdmD0SFxULXtapfWP39QbDl3A3CydUDAS1OMec1JV21Gl91wixUASiPqvxX3JgpOAE4XgEZ3U0Bem3CPPiJqWmw/Nz0YlVbJkpUvSfmSZbr9TXH1Imnt7NRlR4yM/dHLGzfLps2vaNy8b6ZDQKJZ9trOXWOO7DUbXpaXt7wiIbSd5S+8KJXQqDhdoNveA3kqlk5oSOa7gWBYAWvfEcO/xL28SwGGDY3N+oz2he8iSjy0DxONTseLAZc09XaIy+0Qa0ebqpVum0scEacM4iEOSpqBoKJ1fS2lFaVo4jn8EV9MMKJm5ICQEBy4zonLHtoheJEHvg5+fEYi19CYrokmACMXt/mwcOErgZlghIypowtAwjxOCEYAsv9iMKJ5xjqJ9tdJ3f0PANxzhSvxucjWDn7t6ek64sWN6DiVgmYbF+5y5b0rM1tnvU8UvHiXWhF9SH0EpmxjAbLukJAGoEM8Vwaj2DKg66T/72CEODihdyE0n/NNLbL8pZdk8fIVxo6k+J2xEHDomCYvnFxppkc/0fpNm3UHU/PeS+vWKyDxvLXDJvNLStXJbbM71L/VZuvU3whW/Bouwai5Qyd36AjcomXL5bVtO4y0E7N/U1FCMFK56xsQ34G3xPv6ZpGD20S2bxTZuU1G9uwReRX281mYZtE+PBubyMZaQbgmUUsARmzMTRCULphoFBrvfZ9HH28M7RtC9w7QODDiFhmdENZ3BRihqLxelwz825PGjOi8LJ2x3Qleec31brpTA851jVluulzM5i4GnGCZoeveJgpWmNTdeK7tzikKSFzfxykR3Czu/JSMmx6MuOC2dNFSOV/fpHEcOXFSnp05S87WXjAGIBE2vrJVzScznUG/sUiZgY7qdRs3KZ8Mm199Ta9NTWnn7jfVF3TgyFEpqagUa2fX2LsErbnFxWrWMQ2aaXy2Avmj1vROlN+NRInNtGC/RGeUSWf2B9ED5uo2ru7cPAUMbjnalnarHE6ZIkf/6nOolXWAb7sMhQeMgmLpJyK0BxOM6OALHXgDQpatQtybYtEem85r9sI02ewf+ARgALqayue1JHANBDAK7N8hLRk5l4FRJ3i42c00TeH0UWiW+eLB8yen07djaDLcMsUF/liu1pQssX3iU+L45x9I77O/ksiM34v8+tfifWbGhME64xkZfPY/xD7vKTkPTageaehQPtLgiNrVwejmMNM4beUFmGEB7hqJ60gY2s6GjVK9ZKn6jhjLWwcOqlZzsdEALAZqRdRyZs2eq34hml10OlOr4fumA7rH3afaDuPjBM1OR4/u681JwtSaXli3Tj8sQJOO4ERQmjtvvtTVN74j5XcjUUIwcrHCOq3S+OWvSgOXS6gJg8ZMYUuzqPAGIaz0PZzLzhPHvz8JTaoT9WwIXELCM6aA0/KKHt4zphm5U2FCIB2uwyIocVvXuil34znDTOMw/TtCcWDEHR/jwYgCfzM7sPntjv6fPKnLXFhv9fngM4lzlQp0GgO1oRPQCHt/8QuR5npkjEY518f1IwpOYaC4Xz3oDgajHlRcozROfa90JKXq3lgclaQJd7OD0ezKKlm9ZasO6w+xuSECOptnFhbK7kPGNjgdDoc6qpesWiXn6utlMBiU07W1snD5cnnxpTXS1z+oJhXTpm+JPiaTF75PZ/aC8nINtp4eCY2MyNHTp/W60+lUuTDzQK1r8ZJl6jRXv9W7iBKPpqEEWAhiuyBnHvhLNCpj4/suNOhOqOcUBAptL9RzOjFrkrPE85NfA5C8+hmghIRKCnPpARJhxcjJg2K35Ekr4uUEOgo191nm/BVOhDxtmWKAEV7h8PA7QhOBEcpgwqH9GxyMxN8vbXf9ufrBelBH3DiuNXeqnIHWye09bEnQcB/9pkh/l85LakFSaoWSUQT26BMF7rGvz4obWvMdMK3TdSTufA5N63cHGK3d+pqRRbwc8Bl19tKGDbKgukqBiDxcbG6WisWLZfaCBVK+aJEOx5ctXCg9zl5N03Rsr123QTYD3HhOgOK7tAhWrlmj4GPt7tZ7jGPxypXa3fKaZh1NQvKxfccuqV64WHrdumHMu4YSgpEu8Bz26QomabNKx//4B4DEFDVf2iBwPQg+aA4cPbHmG1+86EeDDDz+I5Riu9iZCuucs3x5wlm+o0OAE0MwtWpHuMgCwIILrv7qzrtD/RncYJ+jM9wAvjs7Hz1tujRk3yJyeAs0I/Q2rCnyxcWn/OaZ+iEYE9d1GY7bRMRPIYWQcOit7XJm6q06wsSvZpyazmHtPN2n+gT4cD/0ENLEk2RaJ4EGjQl8ABgTjG7EeUaBjjpoKbfpjphteemomyxpzAcIETjx7nkcI6Vz8KBXedN1aOFBvG74NEzS2fNxYYw4YqpV55PWpKnqwK5B+XGnhV6UxdXA6GaZZ9TZ2Slut1snJ8av4Oe6sW4Ax+Dg4NjExSA0onpoRlzWYbVadfJi/CJYXjMul8t1eRmCPB6P7krJI4lx89qk+L2MuGTIZrPpTG39wgqiUi09lkHz3Pj6CoD/sl0CjMAdBHg0tjm5RMyXScoje5xYvEPcvIyXsfjj00wYEA+fN4PGy3tx5ZDYZ0Sm0Xi4JSyXaoirTS585gEJpBYo8NRCEDqgLfHbX1T77bjutqTKgXSLAUhhQEIYqj8XFCJBCvBQAHGifBSQWDYQIJ2PjNs0hVrv/ZjhTIWwsHFzMl5ncqruk9NhKZD+53+H5/XziZoZCp8uLUEcUQCTbsFBvZrlnYC06CFQwwffgGYzTfwZydqztyLdDmh+5MOWlyGjv3oKjMe2TlWmAWA8x/8bWTPqP3VA3MnTdP5PI3hiR1GXY1EHNieUWhHk0HbURZS1qwAe5P/hIQkGLgmflnMsXEZ8BEmxe3Gk3a6DDJ0Faepb7Ldk3tSakZnXeAEl8Zq/meDE8/j9jEzgiAev8aAUf4wnM634uE2KPx9bMhLL1DA6eVPQFTQg6LwXZecSaw+XNpQzwCgcZo0bZKZr8jSWZ8Rtggfju1SICDGA4fFt92LXJl9jv48L9JwxcD1mYjDyhJVBqoksbhdnrtgvKiBRQ+KQO/cY0o3tLek6pMuZuQQkNvy+Hz2J1g07GE2cKxxYNIxHAYm9KvLMXf+UJZYDBLb1scegnRj7CVE7YgOnw5X7RPdxUt7D38KDHmNXQ0aITPFVxqsVxnLkJD9uUJaIWOdhvLn/DaSTKz6YaNzbiCYht93VyX9pOSJrlstoNGJoW4oueJEFeoOD0SA0vr7kAgUeBQgA7MUs8AdemTdPcr4M7l6vm7L50Gcwf7pbQKyHZnmOD5cR+xocvH67ONPv1JE0G+K3ZlpUW75ZwGh8vggepmDyt3jNKB5YzPfiNQxTkAlQ5nl82cW/TxoTfJCZ1pVofHykMYCIB4S4YO4waYISQehS3i/X9kjx+dD8M95xcb8NXMaB0ZXeeVvA74xHT/GfIeHQvqHPGJUZVQdBDJCgIdFkY+PjBwepvdDhTH+SrmWCgFBzakjKkc4n/hkRuCVAqYTcM7tq4rAg8afLCMEV84jSFefCCphkWerjYHz047Qj8AsUFKr2KXeLHD9s7GpolDUKPfZ+LLPcBdJY1JqAkL6fS1e2b4XmVaCg15hrjAh1A2C5Lq499RYZrjmsj3M1ldEQkJhGPxEYAaT/q31GR/ejbtJ1r2wCUG8ytJb0FGO7WpYlyjmwqlqfVz5ZxVwZi4bIdE0hMsPbCO9opzDSK1bLdE2He5T3TC9QrevqYHTjDO0zX1cCCAq/y907tg/R+G1A9Cu+sWIefzSDudUH343fBsS8RzLLlcJPPkxexnZwiKVvBsZovh8PZCbxnhmn+Y65D5IZzLyQvF6vro0jmWnHg54ZCFwMV0rTTI/EOPiM+RyP5rvx5cxzfgWYc6r4ZEIwsrPC+D7VDrxBQDJOoeK1WcX72b/S73hxZb0vDWYNAIkaEk02+pAaCErpU2Xwhz+F3PoUkKghGWwiYpxT4RwNx7afZRq1Z/D+dAieMWfFAcHmRLxeNGg2bqYX+Kef42GPfvCRyqaKNcuDABEdFc9owFwrPyGFkBkvDZP/eFraLTmq1fEz2QS9bgARP6bouO/L0ACM6QrUHBT0SLqp0g0ORm1NcjYt25hHBHDvQp6sXLYB/jpwXpucIuEf/wSFMKDr1noHwBPYI4/BwOBljfFK5EMP10/+umqlMTNHv6rbYknRbYIdyPvNDkbFpSVy8PAh8QeNAQsKNYMJCAzhyOWCzvbRYevS8/EgYAbeJyDEa1+m8MaXdfw7ZpomwDGQzPrh+2Y+zDji3/EF/NJu69DrMLR8vY93du7cKStXrrxsPyX6v0hmPDyOLyPyfqX7/xnidAZuqeILhRObaVyXRoWd+7YQhVhcNNnoQ1KnduNRufjNh6TJkqWARA2JJht9SFTT2/j5H5gGfUnTDECChqTwMYT4omgUsGNVJL0RRI97zDsK4uxfflHqIWg0JfiJoYto1Bwy5idxWujXmf5+iW5eice5Rw55MUBNkRIRcnGoAVMTkxY1eGr9y7/Wj1h25KWoxuBOsegi0DM4H3x+1lhjNpoLiKCnlXCDj6Z5/XL+o5/QciQYUVvpQr1yMSzfac1Olfr820Te2CJOlCUd+uSxdxg2G6TKbIzjifeN35CHsEccv31KF9dyBLIlM0O1Y5bj1cHI6O2vl94JMCKNzycFjMJ25MRxsXbadLsOvsFV9GZ8PKdrkuc8cnsRnrd2dEnloqXi7BvU6zCAh+8TlhiCAAIG/maSCUommT4ovmvEb6QfHw+vCTYmOMaDlAJd7B1zY8OObruUVy9UbYTX1EiY75aWFjl+/DjuIC8xQDTJBDMGnpsgZl7HH83AZxh4nzyM/93c14zHwuIyOX66RvlJ7DPycXxrRAEJByUtOETEUTYCwFDNcQUk1ZBgstGHRKc2R9k4D4lqOwGJGpKabMEecIzGjvhYTFoN6ntAwTMNhP6qCrHmwYxAXPyuWVeSMaWAo0JcC0XN68IH7xE5fBAC50YmwzIwil6Lkal2hEiMfWEnpiHwsX6NOApukToIkhtx00flB6jUZcGkee8UCV08ro1NzWvkW0sU58bnA25sMGL9eJ78peaJ24B0JHNrXfDF9xAXBwl0q5avQfs79jrqZkCGwhw9NaK4ErEBU1i14Q44RbbtkuZp79XROfrbGtAZDaTl3FRgdCViPs13TeH3wdQy7zl6XdLjHtC2Yd5jaGnvhNAvlsbWdr3mx0nN3/i+e3AAQOXW8/GjWX4/2iPI1HLM93oH+vVIS6Df60G6xheP4wWdAt6PuPvwrMfn1Xt8xpwSwEmYnMHNNW68ZjDJBEOODppETakXfA54Bsc0w/i0NH7UJdOz9zjQ7/kue4Zb7vK5QfLrcoq7v+8yTZHvFldUy95DR3U758RgxDlAACRqSDTZtHg8KMAh7n4cFQeUD9VIoCHRZKNZRX8ER9k47M8GSUCihkSTjT4kdWqHAzrsT2epgjGi5NdBQjjXBuuySv8908QKk6nOkiyBpAJxpmXp9/Ppi3JAQ6J/Yu8990mwbDEAqV8FiKCpxco4+HWLBOStOSqnP/RJ5ZtD8zRnOJkzmJQmJ3PTRP7uQbDWJwHkU/lCYWobRrEMkOkbHIz0O2nrN0NTzdBpC/wuHDeRIxi1Z8GsBp8DiIeTFeve8xHx/7FQpIdfgY3V6xVIhRSVxgbcOn+u7LjjQ6iLVP2M1AWUQXfBVJQDOg+kcTODEamwuEhOnuXXYahhhOXVHdt1k7OVL72ou1aUVi6UF9as1y/b8pk1Gzbpl225KRsBqbSyQg4dO6paVLezRzZs3qT7EjHe6iWLVSvhyBjLs62tTZYuXSo7duyQ5cuXS2lpqcbZ1NYqy1at1E3WGN/MuXM07ZoLtWN5ov/q6PFjUlldJTNmzZSSslLZ/MqWMa2Ii3M5S3zO/AU623vGnLm6tIV06NAhWb169RgwEgSPHj2q6c8tnCflSPO17dsU6JgWfV9Ma9OWzfLSmtUyv2iBzJk3V9PrsnfrMwQaPv/i6pdkAfL63IznZdmK5XKu9vwYIPGZovIqOXrqLFu4JPG79XUQbKrY3DKCDbYuG2o8zBUKZiJKNA+plpPfICz8ZDbT6sF5R2q+2L//OErQbbQHL94lNwrhaKi8GEavsXWveNOMoXYHGjTBqB2mILUuR56xeT4/YV2fPVUufvIzEvzDbJGDR8AUYckHjvzIMNVdOgUhXPygIZGByPL6Xun+9bNiL8iWpvwMAG+qhAEkfYi/EeBnnZItzvxcFTg2lPFq/CXyyIVHHlGho5+J4NuSDuFDvkM4r8vMRV6QrrnLG01dqG+6vu5qUcYRHfF8jUXSc2y3RFJu1Xw7kV4TzCFqi9ygvycpW6IznsODPsRtzLMawLmV7zrcYnv0b+UYBwQKOKJm8NqQNRWapkXaMizSk56J+k5X/hvz8sX2tQck+OwvxLmkWELb14uceFMiB3dKZP0LMvqHZyTwlW9Jz633SltmJtJOlmBKroJOY3aKnAcAN6Ou+QFKmm3cpZNgODrkBUfGVNWhkQHkiZV+fcSyMYGIG/sRiGaX4LpykcyvXgIwMgSawTQZSKrVXQNxoerew4c1HW4Bwr2JOLFx/ZYt0m63q7+jsLhEl4Qwxi4A+fHTZ/Qef+Nq/P4Bj6a66oWXFAjqmpqlratL42D8zgFoV/jd7nbLfO4LVlgob+zbJ4dOnNB0G61WnVBZUlUlJ2pqdILli+vX68RKxkuhbmu36hYkXONGPs5eqJcygOHh4ydUrLgfUm19g/JVc+GimmrmGrqDR4/pfZ7TBdMEOeY6Oa6b63a65MSZs7o75dbtO/QZBt4j0K9c9aLuNHC25ryUlJbLjp2vK08MnNxZvXSFtMBs5aZzr+7YKcUlZQqcpjN/Dq4PnDil+U/iCmtqBdxTmRtkubmvMr96mpwifWnX8EXZBPOQQklZAJN0qc9MUcDzAjwo8HV33SbWx78l0aBPzUCyxkxy+QkZU3trOCT+L31Jau+YLnYL988xPhLYBC2L/igCE7WjPvTAHAXj/tWt6anSgmf5LXmu9mcPzekAfQDAzvQCCHCONEK7oGZA/mxccoJjIwT1DMAzBNOSce3JmyJSMV8bLXuKq4JRwCdtn/sqzNM0scOs7MlMF0dyjk78IyC1ZrEMg2OqMouIpJ8g0hsT0zB3lSRg+0fEcWSnNKZkiw3pcHChE3lrS0mRdksWTK086f/9M3gBz7MFU85jfgmtlwPb5ML02xTIhrOzdN0YV+9zWQ/N6BoABrVOdhZsD26UbX8ywR+dQHI+ypDaTgE6qyxptaA+kbcahADqk5/lvgANluXpQbmyE+vPSpez7IBwTrByQyMGsqvGxfyEAEbvxAx6ZtUAoirjAxA4MswsqZDnFpTK/BKAacxMYg1ScCesz3HETdAOnzyps6RZpGs3bZLKJUsUQLSYEQ0XyW7a+qqmwVX79M3QHOKqfLodmJLT5ZaqxUvkzPlafY7v9vT3K6A0QCPiNWdzF0Ib2XPwoAIf7zEQpOYUFcmON98cu3ehqUl56+w2HOUX6+s0bvLDibnBoVF5Ye0GXcvG9Hjf3CWAOwSYI1gEHwILwYjx8h5Bh1uVcI0c7zFPvLdw6bKx93bv3afvmLsXcOHuS6vX6uZvdOhz7V1jU4s0wFSlCcZ3mP68omJptbbpOxwl5JeDjpzhxy44msY5IWwwUPt5ZODexf1oZGysCSnBPCS3JUdnU1sBSNw0jZ88voBGSj/FEAQq9CR68952GEJomqhtthGuiGLPTh+S+Duk6W++bEwXQOO3T0EDh9DwY4ZOyy26TES3v4AQdAJEyX83gJRa3iB6fX4Xnr09NYi+ZJgO0AJojtH3xK+gupIBvBDIHmqGADmuaG9FvIEnf48C9GujvVrD1fsjfml9+O913ddF8EG/2YBlivjybpcGxMWPCfikl9+kBQAhgyh1mlEcwbuqHXQZgYcoHkRlRy8elhoAHb+Bz/Ts0IzoS+tMz5Pm5Gnieu55PI8ECF6sfWgCLMLeIOpkGJrIkkVyKmUKACRHJ3P68yy6fowARQ2mA/lvR5k0I36OXrIDqQfwnUMZnsG9WjzTloUyxDkBhx1BF8qdRw42NOQbI6rUrjtzs3ULEYJxH3h2JgGUR6kRoswUkHW583UT2xyFkoA0Fz0zv7HPvdgZiqq5+LRszIHMWjTBiGQeJyKaNkdPGntdM3CrEPMrtWZY+/JGXYlvXlPYZxfOV62CaXIztDZrhwovtx+hoCvPACKeHzp2XMGCWhXBwtxGhIH7Z/M+hfhCQ6Pe4yJcajYcierotGkaDNtff0M1GG6BW7VkuRSWlOsWJnyH8XMRLrUZ8mUCD8OxU6c1XfOaeeRWJ+Y1A006bqGrAItran90hhN0zKUuXHe35ZVXx/hpbWuXlavXqYb23Ky5CsbUDK0d7WNa6tyiUjXT6J5Jsj/2JTmXk6mzZfvyARRoSBxp6USD46b0ichwsRkMX2keUgd6zTZqIWjIFCL2wp3ZMLOyYMrgnMPng0/9VPyhJggsWink1Q/Zo+9n1BdBj4QewtEi9Y8+igZNbY0OUghAFkwrvM/PCjlhYumWHwRUC+LNTkcvn2r4qCBMLdB66NNRH0tc4OehabJw6gC3ueW+PL0pt0vPs8+hsvy6N/WVyAQoBm6c4v3ds+AlTbzgq+cWaASME6BHTSEInjkThy52ndlMxzpKTXerNEz0iQkNjwOuHJsYbjgltvy7VfiDyNsF5IHaIsu4AaDvnPMs+A5qbxyOGJqmukZQptRcowCkwOZ1cjB7GrSfVO0kGmBmqakGLZKLWwlSBBp+74yDDl3IVycAuys1C+Y2ACY1U1pT0lFeFu1YPMgztZ/mzALx/eC74v7x99FxZCPf4A9lX4965yegmFYAsNzPTDND6MC0KK6TqEGsWrtWzRtqR6WLlkD1LzfAaCFMmwrDZ2SCkWmmka7FVCMYmSYYWzc1IG4DYmoI5p5FXFHP3xko9BRcgogpqK7ePgUCmj7cv+jNAwd0MS2F3BRwR69bAYvvMV4TMAhuBC1zFIyB2hdNKYezR4fsd+yCCQRwosnFr5k0WW2yas16BUpzjyXuEMB3+C7ByYyL78SDEfNIHxPPyQffJ2CSBwIZ71Obov/J/KYcHtHdCBjY/vhFIJpwa17eLLUNzdLn8cue/Qe0DEwApYO7pHKhHDoOMw03kmTjcjn7nvcbjQWC40Lv2D0VAIIjV3gnokTzkLq/9mWxZmRrDxyk9gGhZ0/M4eXaKfnay9ozIbj/+mMItx0QgCqgxsBIKEQ4KEU80varf5eW5Fv144A0w6Jo5GdjoEkTsBXCyV65G/no5XQC9Mh2mBVdyZk6TE/Tjr878Rw3sacgE4w4J6YZ2oLtA5+FYb8ewoI+OxggpL6N4oGIQfHk8EnpvO2Dqq0M5WZIPXjiVzH8AIlBapcwnYyG5WUNIF/cjB5vjmVuAkI5UINQXiJ4/6e/lCbkjwtevYjfC02mA2Bbn54sfbN/h4egV7LsqH2gXqiFseKZFHkNB9wydHCbnPrCp6UlJ0PLh2Z0TwrMzAwAOjsPlKWVZRkDO4K0+qkQqGFyHZwVpho/BsCyb5p6l3Q880ekC+1r8+swTz+gz3ZBo/IhDgKcFeURFCdAGLmh6ksz/Jo0w4nJEIzTUlpdLc/Onq1qPzWkYvTGheVVAAWY2rHnVGj0/7WTudcQ3ycAUUjjwYhCTVMoXngJGtQaWtqNOT0MXLlPgNr15h59jvXhGhxUjarZ2q4Cz72MqAERLPg7n2Pg7wQp/m6CCAEq3uR5edNG3e/IfKezp1fKFy657B75IRiRPzMeHk+erVHQ5TXT3bbrdQUnmmnmPW4gx7Iw4yKAEViYNgNBiUBk7njJBcIzZ82Rc3WNY3khCHO7lHhtjt9VPHLitP4OFcMu9t/8Xhpv/ShMpwxoK8m6xSvNKDa8RJRwHlIoKN6fPQ1N6z1qajFuFSQEJ3pQ5xTDQd2bdKf4fzMTb/OTjojLb1S2nzkh2PnAeiQi3oO7xfbVb4orLV9Oo4FTs9I5NDhXgAHY2XDssBg9N/1E3DSN67Fap2bqOjOuyucQN31jh3OzpDntNvE8/m+o4TZkAIlRkHH0Ryg1l2g8EDFQ7mmqRVAxXbd/CGAHQOeaLPBEcKrJnKbgQ2BVlYDzR1A29J7g9cQEBKG1xezrKFzTRTn8jYfkRFa2aoYECS7zaADg9j/3DFoOACsG4oMouEFc8HPeeiOmjHnpTbedk6E/PC3Nt9wpVmg1DoAmQYcmbTPKkrPdGwpwxHkr6oyztzmrnqOE1HAJ/g2ZadL/xOMi548jX4iZ+UFWm3/8C6mBudYGrYj7abMtdd2aJ+EoR1oI9HiODGmZXB+xjXCUa8/+fQpINH1mzy+WoqqFMquoBKBQBYEz9Heyp+YBzLNrdWAXzi+S02dq9F32/mvWrtcen9dsKgQfCqppDjFQ2Cn0NHcOHT4q52sv6rNvvLVXzRRqR/QL0f/E7UDcfcYola2zW1fjj98HmxMoy6H12R1Ovabgq6ZVXKrD5fS97Hx9lwIWQaK+uU2/4vMsTCP6eaipsfoJYNSeqC1R26MjnfxS6yEYqdzims9TCyL//KRSPN98nkBM05WAG8/nuvUvXwZGzBv9VvQbvbnvoG409/zsOfLGm7v1GZrM86HFrl6/Ub8xl0TLTTy9Et2xXbqf/Fdp+Po3pO4rD0vHI/8gvm//EK8koATzkPRzOdx24Uyt9M+ZJQ3ffFTc9z8k/V98RFoe/oa0PvhVcX79MRn44vel5bsABJ8fMkPXprGhvwp7CGIIVGJBqTgFXIjvmMjPfyuBW+8WJwSTS0YoSC4IC7UfDnezhyfwac+O+xzGpnOawtE6dZrY//vHRWYuEnHaES1yEDVcqvYhw9NvJH6J4kHIJMqVkxkHIMkRmFE/e0p9SN0PflNNy/afPyGO2IMwTHCCXADAR5HLYKy8JiJOoWf0UZSBP8CJoXjfbZfQsqXS9tj/kvavPiptj/69OL/1AxnYsEkCsfV+BB3OQOdnmHTRH/JCk41alpZjCMgUwlOhdtT9eul+/EfS9vFPS2vBXdAg81CWudAap8CUnQLAy0fIgWmbJ2133iNdf/ctkRXlQJ2TRrmFdFejS20gBBDfv1v6vve41H/9O3L6O9+R4L/8BICKvFAqwQfrWNciXidR02FtcBJhbX2djkJxxGvDlldlDQTj4FFj/2gGPkfTgHStYMS9h5pb2pRtvn/s+EnZt/+gnjMwFgIABdY0hwhQO97YrRuiLV+xSuobmvRZmkl0/NJvRLNywyuvSHtH51hcHJXiPkU8ajEhEAAJThyZ4sgcnyMYEcA2vLxJ/S+8xxGqV7ZtV22FEy5f27VbR9SYVvymbwQU+m5eWLNWTp87r/c46sd3401DjrzRN0bAYRw0sfg7fyMYca6SmqZxe37veWufHDh4WM/JNzeAW7LyRfUZLX9htfqmqAlSizMnah49eQbguBnxH4JmhLbKgfA+FSuo2X7AB0wirqQ3ZhAmoATzkPqRPV14qSUGYQ8bXxIdHYVY0OxguhEOMYbRX+M9TlT0evBGCNo8fgsEVAD5lRJ+/ojbbuiXRyBZ5Jgt2m9tEvfOrWKb/Zw4/u+PpOOrX5OBBx6S8P2PyMCDXxL7g1+QLmgTg4//HxGoyrJnnwy5nWr6cMSdgkttjOlxnyICNHHID7ZNigeieDAiE3x/IBTRIzURHdEKcAIYYh9skebYczRB9dOPKCvuhnktVgr3buJnp7X8EHU/GGODMACGOSDYoCztbn2GSzoUcUJszeQDXIEXAjtvq5+G8ohI6LohgGj9kMFR9NDBDulrPix9R3ZKdO9OGd17TAbf3Cf9J2Gq9HboczT9yAM/gMk4GFheXv0WGiNHfPjT/DG7LBm3Q7pwOeqFwOI9+o6G3wGnEXnRNWI4ZwhwxA71w6+hsPWaM5gJVixCahEmXYsDW4udO04wS7jgtXmPR8ZN4TQF1TRtGHjPfI4jTLxnAhZ54yZq/I1TTswlJebXlxnM9Ji2Gc+Vgjnb2YzXPHJEjecM8T4i/Q2ybV4zmHxzXpLJIwPfi7+ON081LfBGHshrfF55JFDxGY6mmR/B1DhQB/zdeMbMh8j/A8yPIpOS5y4eAAAAAElFTkSuQmCC\"\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/nywru85enBAIECBAgQOAxAgL6Y2C8TIAAAQIECFR5yfZqd3f3mRjjOfeg+44gQIAAAQLLFRDQl+urdAIECBAg0GeBEtDvbGykFdyrS31uiLoTIECAAIE+CAjofegldSRAgAABAmsUaOKdzSqWgG4Z9zX2g0sTIECAwPAFBPTh97EWEiBAgACBkwqUEfSq2TiX9kA/naa4lxXdT1qY8wgQIECAAIEnCwjoT/bxLgECBAgQGLNAG9Dr5nx6EmIIAvqYvxu0nQABAgSWLiCgL53YBQgQIECAQL8FQnNvD3RT3PvdlWpPgAABAh0XENA73kGqR4AAAQIE1i3QVOF6muKel3QX0NfdGa5PgAABAoMWENAH3b0aR4AAAQIE5hdIm6BfnL8UJRAgQIAAAQJPExDQnybkfQIECBAgMHKBtDScgD7y7wHNJ0CAAIHVCAjoq3F2FQIECBAg0DeBvEBcWRQuhHv3oPetDepLgAABAgR6JSCg96q7VJYAAQIECKxUoAT0GKsX0hZrK72wixEgQIAAgTEKCOhj7HVtJkCAAAECRxeYhBDO58NTRG+3XTv6uY4kQIAAAQIEjiEgoB8Dy6EECBAgQGBEAiWM7+7uno4xnh1RuzWVAAECBAisTUBAXxu9CxMgQIAAge4LvD+ZXEpbrF2azXA3gt79LlNDAgQIEOixgIDe485TdQIECBAgsESBEsabeGcz3X+eVnGP5rcvEVvRBAgQIEAgCwjovg8IECBAgACBxwqEZuNcevOZckCMRtAfK+UNAgQIECAwv4CAPr+hEggQIECAwGAFYl1fTIvE5b8XrBE32F7WMAIECBDoioCA3pWeUA8CBAgQINAtgTJaHprm2qxa9lnrVv+oDQECBAgMUEBAH2CnahIBAgQIEFiUQAzxWlokLo+fN+kmdFPcFwWrHAIECBAg8AgBAf0RKF4iQIAAAQIEWoG6qtMCcflhAL118C8BAgQIEFiegIC+PFslEyBAgACB3gukEfRLvW+EBhAgQIAAgZ4ICOg96SjVJECAAAECKxZo8vVCTFPcy8Ps9tbBvwQIECBAYHkCAvrybJVMgAABAgT6LFDmtDcxvpD2QU9J3f3nfe5MdSdAgACBfggI6P3oJ7UkQIAAAQKrFsgBfVKHSd4H3YMAAQIECBBYgYCAvgJklyBAgAABAj0TKPPZt7e3n0mj5+dny8OZ496zTlRdAgQIEOifgIDevz5TYwIECBAgsGyBEsbvTiaXYhUvlinueZK7BwECBAgQILBUAQF9qbwKJ0CAAAEC/RVoQsgruM+2WetvO9ScAAECBAj0RUBA70tPqScBAgQIEFidQBktD01zrgrh9OyyRtBX5+9KBAgQIDBSAQF9pB2v2QQIECBA4AkCbRivmwvpSX5etlx7wvHeIkCAAAECBBYgIKAvAFERBAgQIEBgiAKhqWd7oFezdeKG2EptIkCAAAEC3REQ0LvTF2pCgAABAgQ6JdCEtAd6SAPoaaW4TlVMZQgQIECAwEAFBPSBdqxmESBAgACBeQXqqp4tECefz2vpfAIECBAgcBQBAf0oSo4hQIAAAQLjEiiJPMaYV3H3IECAAAECBFYkIKCvCNplCBAgQIBATwTyonAloIcQZ/eg55c8CBAgQIAAgWULCOjLFlY+AQIECBDon0BZtb2J6R70aHp7/7pPjQkQIECgrwICel97Tr0JECBAgMBSBf74pA6Ts+USoWy1ttSrKZwAAQIECBCoKgHddwEBAgQIECBwWKDMZ7927WefjbE5b/z8MI3nBAgQIEBguQIC+nJ9lU6AAAECBHopMD19+mIaN784m+LuJvRe9qJKEyBAgEDfBAT0vvWY+hIgQIAAgeUKlDA+rarLaak4q7gv11rpBAgQIEDgPgEB/T4OXxAgQIAAAQJZoJ7Es1UIp2caRtB9WxAgQIAAgRUICOgrQHYJAgQIECDQN4E4DRdTKs/B3G3ofes89SVAgACB3goI6L3tOhUnQIAAAQJLESij5aFpXpiVXrZcW8qVFEqAAAECBAjcJyCg38fhCwIECBAgQCALhBCvpX/y+HkeQS+hnQwBAgQIECCwXAEBfbm+SidAgAABAr0UiGFyoa24Ge697ECVJkCAAIFeCgjovew2lSZAgAABAssWiFZwXzax8gkQIECAwAMCAvoDIL4kQIAAAQIjF2jvOY/x4B70kXNoPgECBAgQWJ2AgL46a1ciQIAAAQJ9EChz2mMVrlXR7ed96DB1JECAAIHhCAjow+lLLSFAgAABAosQyKl8UtfVuVJYsEDcIlCVQYAAAQIEjiIgoB9FyTEECBAgQGAcAmW19mvXrj2b1m4/N1sezgru4+h7rSRAgACBDggI6B3oBFUgQIAAAQIdEShhfP/UqUtpevtmO8XdCHpH+kY1CBAgQGAEAgL6CDpZEwkQIECAwBEFSkBvQthMK8XNtlk74pkOI0CAAAECBOYWENDnJlQAAQIECBAYlkAd49kQwulZq0xxH1b3ag0BAgQIdFhAQO9w56gaAQIECBBYsUAJ47GuL8xSebvl2oor4XIECBAgQGCsAgL6WHteuwkQIECAwGMEwnR6ffbWbJ24xxzoZQIECBAgQGChAgL6QjkVRoAAAQIE+i8QQrxWhTSGHstG6P1vkBYQIECAAIGeCAjoPeko1SRAgAABAqsSiGFigbhVYbsOAQIECBA4JCCgH8LwlAABAgQIjFxgNqU9bbHmQYAAAQIECKxcQEBfObkLEiBAgACBTgrkdeHagB7TFPfybLZUXCerq1IECBAgQGB4AgL68PpUiwgQIECAwEkFyqrtsQrXDrL6SQtyHgECBAgQIHB8AQH9+GbOIECAAAECQxaY1CGcLQ0MlSH0Ife0thEgQIBA5wQE9M51iQoRIECAAIG1CJQwfvXq1efS6u3n7K+2lj5wUQIECBAYuYCAPvJvAM0nQIAAAQKHBaanTqUF4uLlFNLzy0bQD+N4ToAAAQIEliwgoC8ZWPEECBAgQKAnAiWMx7q+lKK5bdZ60mmqSYAAAQLDEhDQh9WfWkOAAAECBOYSCBvxbBXCqVkhRtDn0nQyAQIECBA4noCAfjwvRxMgQIAAgaEKtGF8Wl9MT/Jzt6EPtae1iwABAgQ6KyCgd7ZrVIwAAQIECKxeIDTN9dlV85ZrRtBX3wWuSIAAAQIjFhDQR9z5mk6AAAECBB4SCOH5NMU9jZ+3q8Q99L4XCBAgQIAAgaUJCOhLo1UwAQIECBDooUAIFojrYbepMgECBAgMQ0BAH0Y/agUBAgQIEFiQQJO2WfMgQIAAAQIE1iGwsY6LuiYBAgQIECDQOYF8z3ma2h4O7kHvXAVViAABAgQIDF3ACPrQe1j7CBAgQIDA0QTKqu0hxqvtAu7Whzsam6MIECBAgMDiBAT0xVkqiQABAgQI9FkgB/RJNQnnSiOCFdz73JnqToAAAQL9FBDQ+9lvak2AAAECBBYpUIbLr169+lzVVOdnG6AbQl+ksLIIECBAgMARBAT0IyA5hAABAgQIDFyghPHpqVObsYqX0hZrubkC+sA7XfMIECBAoHsCAnr3+kSNCBAgQIDAqgVKGI91fSnlctusrVrf9QgQIECAwExAQPetQIAAAQIECBSB9EfBmTRufip9kYfQjaD7viBAgAABAisWENBXDO5yBAgQIECggwLtCHoIl2apfHYbegdrqkoECBAgQGDAAgL6gDtX0wgQIECAwHEEQtMc7IEuoB8HzrEECBAgQGBBAgL6giAVQ4AAAQIEei8QwvNVSGPosV0lrvft0QACBAgQINAzAQG9Zx2mugQIECBAYGkCwQJxS7NVMAECBAgQOIKAgH4EJIcQIECAAIGBC8ymtDebA2+n5hEgQIAAgU4LbHS6dipHgAABAgQILFsgrwvXlIvEkO5Bt4D7ssGVT4AAAQIEHidgBP1xMl4nQIAAAQLjESgj6CHGq+NpspYSIECAAIHuCQjo3esTNSJAgAABAusQ2Kgm4Wy5cLAH+jo6wDUJECBAgICA7nuAAAECBAiMW6Bsfb61tfVcmuh+3v5q4/5m0HoCBAgQWK+AgL5ef1cnQIAAAQKdENg/depyrOJm2mIt16eE9k5UTCUIECBAgMCIBAT0EXW2phIgQIAAgUcIlDAeJpOLaQ/0C49430sECBAgQIDAigQE9BVBuwwBAgQIEOiyQJjEfP/5qVkdjaB3ubPUjQABAgQGKyCgD7ZrNYwAAQIECBxJoITxyTRcmqVyt6Efic1BBAgQIEBg8QIC+uJNlUiAAAECBHonMA1N2gO9PPKe6EbQZxg+ESBAgACBVQoI6KvUdi0CBAgQINBRgRDrq+ke9CotEmcEvaN9pFoECBAgMHwBAX34fayFBAgQIEDg6QIWiHu6kSMIECBAgMCSBQT0JQMrngABAgQIdFxgNmLeXO54PVWPAAECBAgMXmBj8C3UQAIECBAgQOBJAm1AjzHdg252+5OgvEeAAAECBJYtYAR92cLKJ0CAAAEC3RYoqTyEsNVWM9+I7kGAAAECBAisQ0BAX4e6axIgQIAAge4I5IA+SQvE5X3Qrd9eEPxDgAABAgTWIyCgr8fdVQkQIECAQBcEymj51tbWmTS7/cJsgrsR9C70jDoQIECAwCgFBPRRdrtGEyBAgACBIlDC+PT06c20u9pm2mItvyig++YgQIAAAQJrEhDQ1wTvsgQIECBAoAMCbRiv60upLuc7UB9VIECAAAECoxYQ0Efd/RpPgAABAgTSkPlGPJPuQc87u+QhdCPovikIECBAgMCaBAT0NcG7LAECBAgQ6IBACeOT/bA5S+Wz29A7UDNVIECAAAECIxQQ0EfY6ZpMgAABAgQOC0xDk/ZAT49oI/TDLp4TIECAAIFVCwjoqxZ3PQIECBAg0DGBEOvn0xT3VKt2lbiOVU91CBAgQIDAaAQE9NF0tYYSIECAAIHHCIRggbjH0HiZAAECBAisUkBAX6W2axEgQIAAgW4JzPZVay53q1pqQ4AAAQIExikgoI+z37WaAAECBAjkOe1NZkh7oF9vZ7fPlopjQ4AAAQIECKxFQEBfC7uLEiBAgACBTgi0I+ghbJXayOed6BSVIECAAIHxCgjo4+17LSdAgAABAlV1u9pIC8SdnVGI6L4nCBAgQIDAGgUE9DXiuzQBAgQIEFijQAnjm7/w4bNpc7ULNkBfY0+4NAECBAgQmAkI6L4VCBAgQIDAiAWaC1+5nO5B35ztsGYEfcTfC5pOgAABAusXENDX3wdqQIAAAQIE1iFQwvgz+xsX0hT3c+uogGsSIECAAAEC9wsI6Pd7+IoAAQIECIxLYGMj339+atZoI+jj6n2tJUCAAIGOCQjoHesQ1SFAgAABAisSKGG82d/fnKVyt6GvCN5lCBAgQIDA4wQE9MfJeJ0AAQIECIxAoAnh+qyZeU90I+gj6HNNJECAAIHuCgjo3e0bNSNAgAABAksXCHW8mu5Br9IicUbQl67tAgQIECBA4MkCAvqTfbxLgAABAgSGLRDr88NuoNYRIECAAIH+CAjo/ekrNSVAgAABAosUKCPmIcYriyxUWQQIECBAgMDJBQT0k9s5kwABAgQI9FmgBPQY4vXZHuh9bou6EyBAgACBQQgI6IPoRo0gQIAAAQLHFmjvOY/VVntmvhHdgwABAgQIEFingIC+Tn3XJkCAAAEC6xOI1e1qI4RwplRBPF9fT7gyAQIECBCYCQjovhUIECBAgMD4BEocv/yLL+dwfmG2fLuIPr7vAy0mQIAAgY4JCOgd6xDVIUCAAAECKxAoYby58OXLaXe1zdk96AL6CuBdggABAgQIPElAQH+SjvcIECBAgMAwBUoYD/sbF1Lzzg2ziVpFgAABAgT6JyCg96/P1JgAAQIECCxE4NRkcq4KYSMVlme5G0FfiKpCCBAgQIDAyQUE9JPbOZMAAQIECPRVoJ3ivr+/OUvls9vQ+9oc9SZAgAABAsMQENCH0Y9aQYAAAQIEji3QhHC9nBTLCPqxz3cCAQIECBAgsFgBAX2xnkojQIAAAQK9EQh1vJqmuKf6RiPovek1FSVAgACBIQsI6EPuXW0jQIAAAQJPEmhCXiTOgwABAgQIEOiIgIDekY5QDQIECBAgsEKBMmKexs4vr/CaLkWAAAECBAg8RUBAfwqQtwkQIECAwMAE8pz2Jrcphni9nd0+WypuYA3VHAIECBAg0DcBAb1vPaa+BAgQIEBgfoH2nvNYXSlFBVuszU+qBAIECBAgML+AgD6/oRIIECBAgED/BG7dOhVCONe/iqsxAQIECBAYroCAPty+1TICBAgQIPAogTKf/eIXvpDD+XnLtz+KyGsECBAgQGA9AhvruayrEiBAgAABAmsSOLjhfDPGuDmrw8Fra6qSyxIgQIAAAQJZwAi67wMCBAgQIDBCgdP1NG+xZor7CPtekwkQIECguwICenf7Rs0IECBAgMDSBEIzOVuFcDCTzgj60qQVTIAAAQIEji4goB/dypEECBAgQGAIAiWMN9X+5Vkqdxv6EHpVGwgQIEBgEAIC+iC6USMIECBAgMAxBZr6+uyMvCe6EfRj8jmcAAECBAgsQ0BAX4aqMgkQIECAQMcFYohbaYp7VaWV4jpeVdUjQIAAAQKjERDQR9PVGkqAAAECBA4JhHD+0FeeEiBAgAABAh0QENA70AmqQIAAAQIEVihQRsxDU22t8JouRYAAAQIECBxBQEA/ApJDCBAgQIDAgARKQG+qeD1Nbx9QszSFAAECBAj0X0BA738fagEBAgQIEDiOQF4ULq0KF660J+Ub0T0IECBAgACBLggI6F3oBXUgQIAAAQKrETgI4xsplp8plzx4ZTXXdxUCBAgQIEDgCQIC+hNwvEWAAAECBIYocOmll87FKl6YTXAX0YfYydpEgAABAr0UENB72W0qTYAAAQIETiRQwniM721WMaSPdr24E5XkJAIECBAgQGDhAgL6wkkVSIAAAQIEOitQAvrpsHExbYB+rrO1VDECBAgQIDBSAQF9pB2v2QQIECAwXoEQN85UIUySQB5CN8V9vN8KWk6AAAECHRMQ0DvWIapDgAABAgSWKFDCeBP3rsxSuX3WloitaAIECBAgcFwBAf24Yo4nQIAAAQJ9F2jq66UJsSpbrvW9OepPgAABAgSGIiCgD6UntYMAAQIECBxRIIa4laa4p6MNoB+RzGEECBAgQGAlAgL6SphdhAABAgQIdEegDuF8d2qjJgQIECBAgMCBgIB+IOEzAQIECBAYvkAZMo9NtTX8pmohAQIECBDon4CA3r8+U2MCBAgQIHASgTynvdxz3lTxersH+mypuJOU5hwCBAgQIEBg4QIC+sJJFUiAAAECBDorULZVC1W4UmqYnnS2pipGgAABAgRGKCCgj7DTNZkAAQIERixw69ZGWh/uzIgFNJ0AAQIECHRWQEDvbNeoGAECBAgQWKhAGS2/8Pbb52OMF63fvlBbhREgQIAAgYUICOgLYVQIAQIECBDovEAJ6M+GsJm2V9ts70E3xb3zvaaCBAgQIDAqAQF9VN2tsQQIECAwdoFY1xeqKpjiPvZvBO0nQIAAgU4KCOid7BaVIkCAAAECyxEIMZ6pQtiYlW6RuOUwK5UAAQIECJxIQEA/EZuTCBAgQIBA7wRKGJ/GuDVL5WXLtd61QoUJECBAgMCABQT0AXeuphEgQIAAgQcFQtNcn71Wtlx78H1fEyBAgAABAusTENDXZ+/KBAgQIEBg5QLpHvQraYp7WicuWsh95fouSIAAAQIEniwgoD/Zx7sECBAgQGBQAnWI5wfVII0hQIAAAQIDEhDQB9SZmkKAAAECBJ4gUEbMm6a6+oRjvEWAAAECBAisUeBgFdc1VsGlCRAgQIAAgRUIlIAeqni9mj1bwTVdggABAgQIEDiGgBH0Y2A5lAABAgQI9FigrNoeq3C5tCFUtljrcWeqOgECBAgMU0BAH2a/ahUBAgQIEDgsMAvjtzdSLD9z+A3PCRAgQIAAge4ICOjd6Qs1IUCAAAECSxW4ePNXz6fV2y/O1m83gr5UbYUTIECAAIHjCwjoxzdzBgECBAgQ6JvAQRjfTPefb6Y91nL9D17rW1vUlwABAgQIDFZAQB9s12oYAQIECBC4J1DC+Om6vmCK+z0TTwgQIECAQOcEBPTOdYkKESBAgACB5QiEpjlbhTBJpechdCPoy2FWKgECBAgQOLGAgH5iOicSIECAAIHeCJQwPo37W7NUXua496b2KkqAAAECBEYiIKCPpKM1kwABAgQIhCZcLwqxKluuESFAgAABAgS6JSCgd6s/1IYAAQIECCxNINb1lTTFPZVvAH1pyAomQIAAAQJzCAjoc+A5lQABAgQI9EmgDvF8n+qrrgQIECBAYGwCAvrYelx7CRAgQGCMAmXIvGmqqwbPx9j92kyAAAECfRHY6EtF1ZMAAQIECBA4kUCe017uOQ9VbO9Bt4D7iSCdRIAAAQIEli1gBH3ZwsonQIAAAQLrFyjbqsUQLpeqBAl9/V2iBgQIECBA4GEBAf1hE68QIECAAIEhCZSd1V599dVTqVFnhtQwbSFAgAABAkMTENCH1qPaQ4AAAQIEHiHw/33xixeqGC9av/0ROF4iQIAAAQIdERDQO9IRqkGAAAECBJYkUEbQn63rS2mBuM0U0vNlymtLup5iCRAgQIAAgRMKCOgnhHMaAQIECBDok0CcTC6kWG6Ke586TV0JECBAYHQCAvroulyDCRAgQGCMAqFpzlYhTGZtN4I+xm8CbSZAgACBzgsI6J3vIhUkQIAAAQJzCZQw3sS4NUvlZcu1uUp0MgECBAgQILAUAQF9KawKJUCAAAECHRNomtke6OlOdPegd6xzVIcAAQIECLQCArrvBAIECBAgMAKBejK5nKa4V2mROAu5j6C/NZEAAQIE+ikgoPez39SaAAECBAgcSyCGeP5YJziYAAECBAgQWLmAgL5ychckQIAAAQIrFSgj5mng/PmVXtXFCBAgQIAAgWMLbBz7DCcQIECAAAECfRJop7THmO5Bd/t5nzpOXQkQIEBgfAJG0MfX51pMgAABAuMSaFdtD/VmaXZIu6F7ECBAgAABAp0UENA72S0qRYAAAQIEFiLQhvFbt06l0s4spESFECBAgAABAksTMMV9abQKJkCAAAEC3RA4/7nPXUjj5hdjG9eNoHejW9SCAAECBAg8JGAE/SESLxAgQIAAgcEIlDD+bAib6fbz/JEfAvpguldDCBAgQGBoAgL60HpUewgQIECAwAcCbRifNOdTLDfF/QMXzwgQIECAQCcFBPROdotKESBAgACBBQrEjbNVCPl3vmXcF8iqKAIECBAgsGgBAX3RosojQIAAAQLdESgj6E3TXJ3Na28nuXenfmpCgAABAgQIHBIQ0A9heEqAAAECBAYpEJq0B3p6xKrdcm2QjdQoAgQIECDQfwEBvf99qAUECBAgQOCJArGaXElT3NMxBtCfCOVNAgQIECCwZgEBfc0d4PIECBAgQGDZAnWI55Z9DeUTIECAAAEC8wsI6PMbKoEAAQIECHRVoExpjzE+31Zwdid6V2urXgQIECBAYOQCAvrIvwE0nwABAgQGK/DBnPam2qmi6e2D7WkNI0CAAIHBCAjog+lKDSFAgAABAg8JtNuq1eFieSek3dA9CBAgQIAAgc4KCOid7RoVI0CAAAECcwm0Yfzll0+nUs7OVZKTCRAgQIAAgZUICOgrYXYRAgQIECCwHoFzX/7yhTS9/eJsgrsR9PV0g6sSIECAAIEjCQjoR2JyEAECBAgQ6J1ACePPTiabqeaX3IPeu/5TYQIECBAYoYCAPsJO12QCBAgQGJHAZHI+tfa5EbVYUwkQIECAQG8FBPTedp2KEyBAgACBpwuEGM9WIUxmR5ri/nQyRxAgQIAAgbUJCOhro3dhAgQIECCwVIESxqcxXp2l8rIn+lKvqHACBAgQIEBgLgEBfS4+JxMgQIAAgW4LhKq5XmoYqxzQjaB3u7vUjgABAgRGLiCgj/wbQPMJECBAYNgCaXb7Zprinho5W8d92M3VOgIECBAg0GsBAb3X3afyBAgQIEDgaQLxwtOO8D4BAgQIECDQDQEBvRv9oBYECBAgQGDRAmXIPFbx+UUXrDwCBAgQIEBgOQIC+nJclUqAAAECBNYt0C4K11TX2z3Q3X6+7g5xfQIECBAg8DQBAf1pQt4nQIAAAQL9FGhvOq/DxVL9YIG4fnajWhMgQIDAmAQE9DH1trYSIECAwFgE2uHyV189nRp8diyN1k4CBAgQINB3AQG97z2o/gQIECBA4DEC57/4xQtpevul2I6lm+P+GCcvEyBAgACBrggI6F3pCfUgQIAAAQKLEyhh/Nm63kxFXpptsSagL85XSQQIECBAYCkCAvpSWBVKgAABAgTWJpCDePn9HkO5//y5WU0E9LV1iQsTIECAAIGjCQjoR3NyFAECBAgQ6KLAQRifVLdvb6QKTmaVnObPMcZJFUL+Xd9Ocp+96RMBAgQIECDQTYH8y9yDAAECBAgQ6L7AQRg/GAnPoTsH8TZ8v/bavRZsb2+f+d0Qngsh/oEqLd6eDsjHHJx37zhPCBAgQIAAgW4JCOjd6g+1IUCAAAECBwJtIL+dgvVrJWDnMF5Gxg8OSJ9PXXrphZ2NvcmHYl19bTrhq1MOf+VOVe2cjvFGWhzuwiy/mzF3CM1TAgQIECDQVQEBvas9o14ECBAgMDaBHKJzKM8fB6Pj0xTODx6Tazdvvjit9l+NMXx9FcM/mwN53I8fjnU4F0I+LT1SKj8ooH3BvwQIECBAgEBfBAT0vvSUehIgQIDAEAVyKD+4R/y+0fFr166dbZ6dfCSF8W9JmftbU2T/hv3YfFWo6gttGG9ntpcon242j03Tnt8G9ZzRD38M0U6bCBAgQIDA4AQE9MF1qQYRIECAQIcFcmg+GClv0vODj+rll19+5kvvvfdKU9e/P1TNPz+N4ZviNL4U6nrSZu6YF33LebypmiaflyJ4eactLwS/0wuKfwgQIECAQH8F/DLvb9+pOQECBAj0RyCvrp7D+X76uDdSvnXjxnYK3R9Ni7l95xfvvP8H0hFfmyJ3+t1cpyCeonj+/+k0n3MQxttRcWG8kPiHAAECBAgMTUBAH1qPag8BAgQIdEHg8Eh5DuT3QvmVF198pZpO/4V0wHeleekpnIfLVdoJLbSj4ymQNymQp2Tebo8W0me/q7vQo+pAgAABAgRWIOCX/gqQXYIAAQIERiOQg3keLb8vlF++efPVumn+5TRC/t1xuv/Nadr6s3kxtzJCHuM0TVlPK7uV6eopkOcR9FyMBwECBAgQIDA2AQF9bD2uvQQIECCwaIHDo+V5OnqZkn51d/flGOL3pK//WBop/+aqDqdLKE8vtNPW02mhhPlJCueLrpPyCBAgQIAAgR4KCOg97DRVJkCAAIFOCORUnUfLcyAvU9gv3ry5udE0fzhU8U80VbydZqmfbUfK0x3lTZq6fm+U3LT1TvSgShAgQIAAgY4JCOgd6xDVIUCAAIHOCxxe8K2Mll/e2flouo38B9NU9e9Jg+E7ZYp6vqe8LPA2Gyl3L3nnO1YFCRAgQIDAugUE9HX3gOsTIECAQF8E8u/MvL1ZGS2/sLt7+XRVfW9a3e0HUxb/trymW5rKnkbKy/vpnvK0FLtQ3pe+VU8CBAgQINAJAQG9E92gEgQIECDQUYGDaew5lLej5XnBtzj9oRTKvy/dV76dF3rLq72V0fKc0tv7yjvaHNUiQIAAAQIEuiwgoHe5d9SNAAECBNYlEKrb6f7y10ooL8H8ys7Od6QR8T8Xmua7q7p+5l4oTy8aLV9XN7kuAQIECBAYloCAPqz+1BoCBAgQmE+gTqfnj/1ZOA+Xd3f/WHrhz8dQ/cG8xlta7C1NdI976Zi8+rrfo/N5O5sAAQIECBA4JOAPi0MYnhIgQIDAaAU+COYpfl+7du1sc+rUn2hC9efSHPdbRSXdYJ7CeZNCeT721GilNJwAAQIECBBYmoCAvjRaBRMgQIBADwTuC+bb29tbd+r6R9IN5/9mur/8q0Jeib2s/JYWhwvVxiyc96BZqkiAAAECBAj0UUBA72OvqTMBAgQIzCtwXzDfevHF67HZ/9E7VfjhNI39egrlaa326cG+5XnhN78v5xV3PgECBAgQIPBUAX9wPJXIAQQIECAwIIG6up3uMW8Xf2tmwfzH4nT/z4S6vpImsad7zEswt0XagDpdUwgQIECAQF8EBPS+9JR6EiBAgMB8ArfTKHgO5q9VTdnDPMZ/KwXzH03B/HK7f3lj4bf5hJ1NgAABAgQIzCkgoM8J6HQCBAgQ6LxA/l03zeH8pZdeevbL070fTRPYfyJtlXa9Smu+pYXf2mBu4bfOd6QKEiBAgACBoQsI6EPvYe0jQIDAeAXyfeZpEfayl3l1ZXf3B353f+/fTyPmX1OCeZxtlSaYj/c7RMsJECBAgEDHBPIfLx4ECBAgQGBIAqG6dStvg5Y2LK+mW7u7f3Drxu7PpsXffjp9fE3Mi7+17+Vj/B5MCB4ECBAgQIBANwSMoHejH9SCAAECBBYjkH+v7Vevv753eXv7RpiEv5Kms//JPIyeprKnVdnz/wW/+xZjrRQCBAgQIEBgwQL+SFkwqOIIECBAYC0CeSQ8f+TR8WprZ+fHYx3+ozRifjEF87xr2jRFc7/zMo4HAQIECBAg0FkBf6x0tmtUjAABAgSOKJB/l5Vp65d3dn5fCNXfTAvAfcuh+8w3hPMjSjqMAAECBAgQWKuAgL5WfhcnQIAAgTkE7o2ab29vn7lTh/84lfUX0qh5Ve4zDyG/n+8z9yBAgAABAgQI9EJAQO9FN6kkAQIECDwgcG/U/MrN7X/xThP+dlqd/SNlOnsT03R295k/4OVLAgQIECBAoAcCeXTBgwABAgQI9EXgYIX2/d3d3efS1mn/eRXr/zONmudwnvczzxur+Y/PfelN9SRAgAABAgTuE/BHzH0cviBAgACBDgtMUt2meYX2qze3v+29GP/rNIv9lRTM0ypwaUu1YDp7h/tO1QgQIECAAIEjCBhBPwKSQwgQIEBgzQLtvubTXIvLN3b+g6ap/0HaL+2VlM3zqHnePM1/cF5zF7k8AQIECBAgML+AP2jmN1QCAQIECCxPIG9hPrm3r3kd/k4aNf+OGJu0r3m1n9aDswjc8uyVTIAAAQIECKxYwAj6isFdjgABAgSOLJCntOfH/taN7e8JdfiFdK/5d5QV2qsqGjVvcfxLgAABAgQIDEdAQB9OX2oJAQIEhiSQZ3jlKe3xyo2dv1pV9f+Wnm/GGPdmK7TnkXUPAgQIECBAgMCgBExxH1R3agwBAgQGIPDqq6erT33q7sWbNzdPNc3/lAL5d+Xt01LLmvRhSvsAulgTCBAgQIAAgUcLCOiPdvEqAQIECKxeoL3fPIXzSzs737ARm/+1qsOH8kJw6Y38++pgyvvqa+aKBAgQIECAAIEVCJjivgJklyBAgACBpwrk30f5Y//y7u73boTwD9PzD+W9zVM4z6PmprQnBA8CBAgQIEBg2AIC+rD7V+sIECDQB4E8Mp6nr0+v7Oz8VB2qvxur+EwK5/vpNVPa+9CD6kiAAAECBAgsRMAU94UwKoQAAQIETiiQfw/lIF5t7e7+V2lK+5+e3W+eVmkPfkedENVpBAgQIECAQD8F/PHTz35TawIECPRf4Ha6r/y1FM5feunZrf39fL95XgxuLzUs/24yw6v/PawFBAgQIECAwDEF/AF0TDCHEyBAgMACBG7dOpXD+c7OzpUr+/v/96Fw7n7zBfAqggABAgQIEOingIDez35TawIECPRXIIfz11/f29zevnknVP9PCNWttFL73dQg95v3t1fVnAABAgQIEFiAgCnuC0BUBAECBAgcUSDvcf7663e3tre/Jk7qn0lnXY8x5pXaTx+xBIcRIECAAAECBAYrYAR9sF2rYQQIEOiYQB45T3ucb12/fitNaf8HKZSXcJ5qaeS8Y12lOgQIECBAgMB6BIygr8fdVQkQIDAugdm09iu7u9+atlD7mbRC+3NV3kYtBOF8XN8JWkuAAAECBAg8QcAI+hNwvEWAAAECCxA4FM6rGP9+VaVwnqa120ZtAbaKIECAAAECBAYlIKAPqjs1hgABAh0TmIXzSzs7/0xVpXAewpn0Oe97buS8Y12lOgQIECBAgMD6BUxxX38fqAEBAgSGKTAL52VBuDr8H6mRZ8rIuXA+zP7WKgIECBAgQGBuASPocxMqgAABAgQeIbCRt1JL95zvxDr8vbQg3AvlnnPh/BFUXiJAgAABAgQItAICuu8EAgQIEFi0QJ6dtX/x5s3NNJ3974UQdhv3nC/aWHkECBAgQIDAAAUE9AF2qiYRIEBgjQKTdO1yj/lGM/3fUzj/2tk+5+45X2OnuDQBAgQIECDQDwEBvR/9pJYECBDog0D+nTLNFb1yY/d/CXX9rWnk/G76UjjPKB4ECBAgQIAAgacICOhPAfI2AQIECBxZIN1qnsL57u5/kUbO/0hsmr30wukjn+1AAgQIECBAgMDIBQT0kX8DaD4BAgQWInC7yvedT7du7PxEqMOPxenUVmoLgVUIAQIECBAgMCYBAX1Mva2tBAgQWIbAq6+erl6r9rdubn93VYW/kUbOY9rv3O+XZVgrkwABAgQIEBi0gH3QB929GkeAAIGlC2xUn/rU3SvXr78Sm/A/p1Xb8wWb9JEXi/MgQIAAAQIECBA4hoARjmNgOZQAAQIE7hMoK7Zfu3btbLUx+bvpvvMzVYx5artwfh+TLwgQIECAAAECRxMQ0I/m5CgCBAgQeFigDJfvn9r471M4/7qyYnsIZmY97OQVAgQIECBAgMCRBAT0IzE5iAABAgTuE7h1K2+d1qQV2/+9tJ3a91qx/T4dXxAgQIAAAQIETiQgoJ+IzUkECBAYsUAO56+/vre1s/PtVaj+WgrnGcO09hF/S2g6AQIECBAgsBgBAX0xjkohQIDAWAQmOZyf39m5EkP46Vmjp+mz3ydj+Q7QTgIECBAgQGBpAv6gWhqtggkQIDA4gZBaVIbLT9fhv037ne/EGPfSa0bPB9fVGkSAAAECBAisQ0BAX4e6axIgQKCPArdu5QXg4pUbO38pLQr3R+J0up8Se74X3YMAAQIECBAgQGABAgL6AhAVQYAAgcELzO47v3zjxh+qqvBX033nsQrByPngO14DCRAgQIAAgVUKCOir1HYtAgQI9FOg3HeeVmzfqWPzP86akKe65ynvHgQIECBAgAABAgsSENAXBKkYAgQIDFQgh/C8CFx6xP8hjZpvVe47bzn8S4AAAQIECBBYsICAvmBQxREgQGBQAreqfN95tbW7+5fTfuffMVsUzn3ng+pkjSFAgAABAgS6IlD+8OpKZdSDAAECBDokcCstAPd6VfY7j6H6D6t833nVBvYO1VJVCBAgQIAAAQKDETCCPpiu1BACBAgsVKDO4Xzzwx++GOvqv5uV7L7zhRIrjAABAgQIECBwv4CAfr+HrwgQIECgFSgLwNV37/ytEOqX7Hfu24IAAQIECBAgsHwBAX35xq5AgACBfgnkLdXSwnBXdnb+9VCHH4jTZprSului+tWLakuAAAECBAj0UEBA72GnqTIBAgSWKJCmtr++l7dUS5uo/Wcx33YeynZqtlRbIrqiCRAgQIAAAQJZQED3fUCAAAECBwIfhPAQ/8u0avuV9MZe+vC74kDIZwIECBAgQIDAEgX80bVEXEUTIECgVwK3buVp7E2a2v6D6b7z78lT29PXtlTrVSeqLAECBAgQINBnAQG9z72n7gQIEFicQJnavnXjxnaa0P6fxiYt2N5ObV/cFZREgAABAgQIECDwRAEB/Yk83iRAgMBoBNrp7TH+dVPbR9PnGkqAAAECBAh0TEBA71iHqA4BAgRWLnCrTGOfbt3Y/p40av79afQ873du1faVd4QLEiBAgAABAmMXENDH/h2g/QQIjF0gVK9Xe9vb22diDH8jrdmeH/nTBwvGlZf8Q4AAAQIECBAgsGwBAX3ZwsonQIBAtwUmuXp3JuGn0tT2r65izKu2l9e6XW21I0CAAAECBAgMT0BAH16fahEBAgSOKpCD+P7lmzdfTQPm/05ZGE44P6qd4wgQIECAAAECCxcQ0BdOqkACBAj0RqDMaK/j9K+lBdtPp9Hz/VRzvxd6030qSoAAAQIECAxNwB9iQ+tR7SFAgMDRBNo9z2/c+KNp9Py7Y0x7nodgYbij2TmKAAECBAgQILAUAQF9KawKJUCAQKcF8gJwabT81qk0av5XOl1TlSNAgAABAgQIjEhAQB9RZ2sqAQIEisCtW2WkfOvm53401OH3pnvP89R2C8P59iBAgAABAgQIrFnAdMY1d4DLEyBAYMUCdfX663vnt7e30rZqf7GKacvzEPzH2hV3gssRIECAAAECBB4l4I+yR6l4jQABAsMVKD/3T9f1T4QQXkjNzNuq+V0w3P7WMgIECBAgQKBHAv4o61FnqSoBAgTmFCjbql27efPDVRV/zLZqc2o6nQABAgQIECCwYAEBfcGgiiNAgECHBfLicNW0af5iqOtz6anR8w53lqoRIECAAAEC4xMQ0MfX51pMgMA4Bcro+ZUXr78SQ/UnZ6Pn1iEZ5/eCVhMgQIAAAQIdFRDQO9oxqkWAAIFlCITpJN97fjptr5ZXbi8j6su4jjIJECBAgAABAgSOL2D05PhmziBAgEDfBPLo+XRzd/frYxX/jaqJVm7vWw+qLwECBAgQIDAKASPoo+hmjSRAYOQCZaQ8pfQfS/eeb8xGz/38H/k3heYTIECAAAEC3RPwB1r3+kSNCBAgsEiBe/eepwntP1juPQ8hv+ZBgAABAgQIECDQMQEBvWMdojoECBBYsEB7n3kz+dEqhGfce75gXcURIECAAAECBBYoIKAvEFNRBAgQ6JhAGT2/dP36i6lePzAbPfdzv2OdpDoECBAgQIAAgQMBf6gdSPhMgACB4QmU0fONjfpH0srtF917PrwO1iICBAgQIEBgWAIC+rD6U2sIECBwIJDD+f7Fmzc3Yww/HK3cfuDiMwECBAgQIECgswICeme7RsUIECAwh8DtqiwEtxH3vy/UYaeKTd733M/8OUidSoAAAQIECBBYtoA/1pYtrHwCBAisXiBUr1UlkIcq/Eia2p73PW8Xi1t9XVyRAAECBAgQIEDgiAIC+hGhHEaAAIEeCZTR86u7u/9SSubfGGNsUt39vO9RB6oqAQIECBAgME4Bf7CNs9+1mgCBYQukIfOqSqn8T6WR8yqNoOeAbgR92H2udQQIECBAgMAABAT0AXSiJhAgQOCQQB49n17Z3v7a9Pm7ZlurlRH1Q8d4SoAAAQIECBAg0EEBAb2DnaJKBAgQmEOgHSmfhO9Pi8M9O9tazej5HKBOJUCAAAECBAisSkBAX5W06xAgQGD5Avln+v729vaZKlb/arr33OJwyzd3BQIECBAgQIDAwgQE9IVRKogAAQJrFyg/0+9MJt+ZFm3/yOzecz/n194tKkCAAAECBAgQOJqAP9yO5uQoAgQI9EGgLA4Xqub7LA7Xh+5SRwIECBAgQIDA/QIb93/pKwIECBDoqUD+D67Tze3tm7EKf6hq0sLtIVgcrqedqdoECBAgQIDAOAWMoI+z37WaAIGhCdxu9zmvJ5PvTtPbL1ocbmgdrD0ECBAgQIDAGASMoI+hl7WRAIGhC4TqtWq/NDLGP942Nm+A7kGAAAECBAgQINAnASPofeotdSVAgMCjBcrP8s0bN35PFarf167enp55ECBAgAABAgQI9EpAQO9Vd6ksAQIEHilQwngdmjy9/fRseruf74+k8iIBAgQIECBAoLsC/oDrbt+oGQECBI4q0E5vb8IfTeHc3udHVXMcAQIECBAgQKBjAgJ6xzpEdQgQIHBMgbJS+9WdnW+oqnirTG+v2gXjjlmOwwkQIECAAAECBNYsIKCvuQNcngABAnMKlOntTQjfGep6YvX2OTWdToAAAQIECBBYo4CAvkZ8lyZAgMCcAjmcl+ntaWL7Hy7T260NNyep0wkQIECAAAEC6xMQ0Ndn78oECBCYV6D8DL+6u/vVoYrfOFu93c/1eVWdT4AAAQIECBBYk4A/5NYE77IECBBYgECZ3p5Gz789hPpcFatpKtPP9QXAKoIAAQIECBAgsA4Bf8itQ901CRAgsBiBlM3T0nBV/M78b/ooXy+maKUQIECAAAECBAisWkBAX7W46xEgQGAxAnn0fHrx5s3NtK/aR0s0T8PoiylaKQQIECBAgAABAusQ8MfcOtRdkwABAvMLlO3VJjF+SwjVTho9b1KRfqbP76oEAgQIECBAgMDaBPwxtzZ6FyZAgMD8AiFOb1cpoafZ7TmgexAgQIAAAQIECPRYQEDvceepOgECoxW4t71vRCloAABAAElEQVRaCOHbyq3n6cloNTScAAECBAgQIDAQAQF9IB2pGQQIjEqg/Oy+dP36i7EKv7dsr5ZuRB+VgMYSIECAAAECBAYoIKAPsFM1iQCBwQuUMF5PJt+UnlxMrc3T2wX0wXe7BhIgQIAAAQJDFxDQh97D2keAwGAF6hB//6H7zwX0wfa0hhEgQIAAAQJjERDQx9LT2kmAwJAEprkxaWu1j7Zbn7v/fEidqy0ECBAgQIDAeAUE9PH2vZYTINBPgfxzO17e2dlNs9pfKfefB9ur9bMr1ZoAAQIECBAgcL+AgH6/h68IECDQdYH253Zdv5rGzTdTZd1/3vUeUz8CBAgQIECAwBEFBPQjQjmMAAECXRJIN5x/86H7z7tUNXUhQIAAAQIECBA4ocDGCc9zGgECBAisXiAvBJdHzPMN6N9YPlu8vWXwLwECBAgQIEBgAAJG0AfQiZpAgMBoBEpAv3bt2tmU0L+utDpI6KPpfQ0lQIAAAQIEBi8goA++izWQAIEBCZSt1Pafm9xIC8S9WBaIs//5gLpXUwgQIECAAIGxCwjoY/8O0H4CBPokUAJ63C8LxD2bKm6BuD71nroSIECAAAECBJ4iIKA/BcjbBAgQ6JpACPFrDy0QV0J71+qoPgQIECBAgAABAscXENCPb+YMAgQIrEsg5gunRP71aZG4ddXBdQkQIECAAAECBJYkIKAvCVaxBAgQWILANJWZ8nn4cCk7pJ3QPQgQIECAAAECBAYjYJu1wXSlhhAg0FGBgxCdPx88z8Pf7XZpR690/g+qzZXd3e20ONyHZqf5j6xH93MkAQIECBAgQKDzAgJ657tIBQkQ6LnAwVz0g88Hzclh/cHXDt571OcS7sNkci1O9zdnBxwE/kcd7zUCBAgQIECAAIGeCQjoPesw1SVAoBcCZbT7+eefvzY9deq/SePm50KsPhdD2Eu1v5BS9d985803X0vPJ+kjT1s/yqMN49PpK2lme51G0fN5+XwPAgQIECBAgACBgQgI6APpSM0gQKB7As1zz9XVdP/bQ12fzYu65YSdnlfNtNlNT78pfczuKT/CSPrt21X12mtVrKvdkEtqmlRgm9lTOR4ECBAgQIAAAQIDEHD/4gA6URMIEOimwHQyeTdF6Ldi06R8Hu+kz/vNdHonjYDf2trd/f5ZrY82Cv7aa+10+Kb6SDdbq1YECBAgQIAAAQLzCgjo8wo6nwABAo8ROP2Vr+ynVN3MRro30uc8ayl95KwdfzL9k8P5fvp42lB4fv9gKvyH2i3WnnZKOsODAAECBAgQIECgVwICeq+6S2UJEOiJQBntfvvtt99Lofx3H4jSkzySXtX1N1ze3f2hWXueNopeinjppZeeTVH+ajmnzHPviYZqEiBAgAABAgQIHElAQD8Sk4MIECBwIoG8ldrByPcHBeT9y/M96aH68erll59JbxxlFL36nbt3r6bz8jZruawHcv8HxXtGgAABAgQIECDQTwEBvZ/9ptYECPREIIXwrzyiqmkUPe6nnP51V+68+yOz9580il7CeJxMLqZUf+4R5XmJAAECBAgQIEBgAAIC+gA6URMIEOikQBuqY8ij6Pm28zLsfa+maYp6HgkPVf2T165dO5tef+ooet0011Ipp0tpRtDvUXpCgAABAgQIEBiKgIA+lJ7UDgIEuibQTkFvmvceU7FJ2iptP42If2jv1Kk/NTvmcaPobdiv44tpRD4/cuhvn5Uv/UOAAAECBAgQIDAEAQF9CL2oDQQIdFcgxDwy/uhHmuPejqLHn7x48+ZmOigf+9ify3U12cw3ruc92x5doFcJECBAgAABAgT6LPDYPwT73Ch1J0CAwJoFcoAuI9zpn3fap4/M1GUUPdT17sZ0+mdLnW+VrdceWf2Uy9sV3B/5rhcJECBAgAABAgT6LiCg970H1Z8AgW4LhPDwKu6HaxxCnbZdSxk+/Pnz29tb1evVXnr7wZ/NbboP8foDd7IfLslzAgQIECBAgACBngs8+Edgz5uj+gQIEOiMQBlBT8n6nafcLZ5/Du+FOlw/HcJfmNX+wZ/NJaCn+fDP59XmPAgQIECAAAECBIYp8OAfgcNspVYRIEBgTQIhPmUEva1X2natybeX/9mrL730QnrpwXvRZyPo9Zn28JL919QilyVAgAABAgQIEFiWgIC+LFnlEiAwboHbt9v2h/ClI0Dkn8V7VV1vTff3f2J2/MHP55zGc0Cv005t7R7oaYu22TE+ESBAgAABAgQIDEjg4A/AATVJUwgQINAdgbRQe7sP+tOrtDEbRf/Tm9vbN9Ph942ib21tnU2j8RdmE9wF9Kd7OoIAAQIECBAg0DsBAb13XabCBAj0QuC110o1p03zbtoWLT1/aqbOB+ylQH9hMgk/Xk5uF4srJ9Z1fSaVcq4ta/auTwQIECBAgAABAoMSENAH1Z0aQ4BA5wRC8+RV3O+vcLkXPeX5P7O1s/OR9NZ+Ndt2bW9j45mqamb3oD897d9frK8IECBAgAABAgT6ICCg96GX1JEAgd4KhFh/sVQ+PLR12qPalH8mpxXd6+diXbWj6O+/WkbQN+o6BfRw6lEneY0AAQIECBAgQGAYAgL6MPpRKwgQ6KhA2hrt7jGrVu5Fr2L44cs3b75afepT5fx4qqnT9PenzpM/5rUcToAAAQIECBAg0CEBAb1DnaEqBAgMSqCs55ZGw38nlnvQjzwtvb0XvQ6nQ5z+uwci9V79TCpwcvC1zwQIECBAgAABAsMTENCH16daRIBAhwRSqH4vVSeH9eOMfs9G0at/bevGjW/KzdmfNHmBuIMp7scpK5/uQYAAAQIECBAg0AMBAb0HnaSKBAj0VyCtEPd+FULeMi0/yqh6+/SJ/4YUxvfT6PtGGn3/qfbIkM896vlPLNybBAgQIECAAAEC3RQQ0LvZL2pFgED/BUqYnpxq7qbh7uOs5N62PIQ8ih7TuPu/cvmFF76uipPfSUE/j5wL6f3/3tACAgQIECBAgMAjBQT0R7J4kQABAosRmOxP9tMo+PEDer58Oi9n8rCx8VMprB/8vDa9fTFdoxQCBAgQIECAQOcENjpXIxUiQIDAgAT29vfTtml5BP0EuTqEsi96OvN7J6H+RKzi7ySaC+kjj6KfoMABwWoKAQIECBAgQGCAAgcjMgNsmiYRIEBg/QIb+/t305ZpeaG4/Dju9PQcwvMa8M/G2Px4enbwH1WF88LpHwIECBAgQIDAsAQE9GH1p9YQINAdgRLG987tvZ+mqb87x4B3CempWTvp40x3mqcmBAgQIECAAAECixYQ0BctqjwCBAgcEjj9ldP7aWr63TknpB+E9EMle0qAAAECBAgQIDA0AQF9aD2qPQQIdEWgjKC//fbb76bV1788m5N+3Cnuh9tiWvthDc8JECBAgAABAgMUENAH2KmaRIBApwRyKD/YB71TFVMZAgQIECBAgACBbgkI6N3qD7UhQGBYAmXUO+2U9pXSrDTXfVjN0xoCBAgQIECAAIFFCgjoi9RUFgECBO4XKAE9xtDc/7KvCBAgQIAAAQIECDwsIKA/bOIVAgQILFagaWbbrBlAXyys0ggQIECAAAECwxIQ0IfVn1pDgEAXBUJ0D3oX+0WdCBAgQIAAAQIdExDQO9YhqkOAwKAE2nvQq+o359gHfVAgGkOAAAECBAgQIPB4AQH98TbeIUCAwGIEQjCCvhhJpRAgQIAAAQIEBi0goA+6ezWOAIE1C7SLxFXVO1V5tubauDwBAgQIECBAgECnBQT0TnePyhEgMASBEMN0CO3QBgIECBAgQIAAgeUKCOjL9VU6AQIE0u3n4UsYCBAgQIAAAQIECDxNQEB/mpD3CRAgMKdACPZBn5PQ6QQIECBAgACBUQgI6KPoZo0kQGCdAtOmebeKeQ/04E70dXaEaxMgQIAAAQIEOi4goHe8g1SPAIEBCISmvQddPB9AZ2oCAQIECBAgQGB5AgL68myVTIAAgSIQYv3bM4oc0fNQugcBAgQIECBAgACBhwQE9IdIvECAAIHFCoQY785iuTH0xdIqjQABAgQIECAwKAEBfVDdqTEECHRMoIyWh7r+UmwTuoDesQ5SHQIECBAgQIBAlwQE9C71hroQIDBIgaaq3k8NS588CBAgQIAAAQIECDxeQEB/vI13CBAgsBCBjRjfTwu4788Kcw/6QlQVQoAAAQIECBAYnoCAPrw+1SICBDomMD116m6a296u5N6xuqkOAQIECBAgQIBAdwQE9O70hZoQIDBQgXp/fz/GKKAPtH81iwABAgQIECCwKAEBfVGSyiFAgMDDAmU6+950upfeEtAf9vEKAQIECBAgQIDAIQEB/RCGpwQIEFiGwMbe3t2qCu+lj2UUr0wCBAgQIECAAIGBCAjoA+lIzSBAoLsCe2fPvp+i+buzfG6RuO52lZoRIECAAAECBNYqIKCvld/FCRAYg8Az7723l/ZBT6PoHgQIECBAgAABAgQeLyCgP97GOwQIEJhXoIyWv/322++lbda+YoL7vJzOJ0CAAAECBAgMW0BAH3b/ah0BAt0QaFI1DvZB70aN1IIAAQIECBAgQKBzAgJ657pEhQgQGKJACNVXhtgubSJAgAABAgQIEFicgIC+OEslESBA4FECZWZ7bKp2cbh0M/qjDvIaAQIECBAgQIAAAQHd9wABAgSWK9Deeh7ju8u9jNIJECBAgAABAgT6LiCg970H1Z8AgX4IhOge9H70lFoSIECAAAECBNYmIKCvjd6FCRAYgUCezl5G0NM/77RPzXAfQb9rIgECBAgQIEDgRAIC+onYnESAAIFjCoQwPeYZDidAgAABAgQIEBiZgIA+sg7XXAIEVi7QLhKXR9Dbu9FXXgEXJECAAAECBAgQ6IeAgN6PflJLAgR6LhCiEfSed6HqEyBAgAABAgSWLiCgL53YBQgQGLXA7dtt80P40qgdNJ4AAQIECBAgQOCpAgL6U4kcQIAAgfkFQgjN/KUogQABAgQIECBAYMgCAvqQe1fbCBBYv8Brr5U6TJvm3SreW9R9/fVSAwIECBAgQIAAgc4JCOid6xIVIkBgkAKhsYr7IDtWowgQIECAAAECixMQ0BdnqSQCBAg8ViDE+ovlzVD5uftYJW8QIECAAAECBMYt4A/Fcfe/1hMgsCKBEOPeii7lMgQIECBAgAABAj0VENB72nGqTYBAbwTyjedVmEx+O5Z70O2G3pueU1ECBAgQIECAwIoFBPQVg7scAQLjFGhivJNabpW4cXa/VhMgQIAAAQIEjiQgoB+JyUEECBCYT2Cjqt6rQtiflVJG1ecr0dkECBAgQIAAAQJDExDQh9aj2kOAQNcEShifnmruhqqyknvXekd9CBAgQIAAAQIdEhDQO9QZqkKAwHAFJvuT/XQPuoA+3C7WMgIECBAgQIDA3AIC+tyECiBAgMDTBfb29/Mq7gdT3J9+giMIECBAgAABAgRGJyCgj67LNZgAgXUIbOzv301LxL0/u7Z70NfRCa5JgAABAgQIEOi4gIDe8Q5SPQIEei9Qwvjeub33Qwjvpg3Xet8gDSBAgAABAgQIEFiOgIC+HFelEiBA4D6B595/bi9W8a58fh+LLwgQIECAAAECBA4JCOiHMDwlQIDAEgTKCPpbb72Vt1n78mz83BT3JUArkgABAgQIECDQdwEBve89qP4ECPRFIIdyi8T1pbfUkwABAgQIECCwBgEBfQ3oLkmAwOgEysB5CNVXSsvTXPfRCWgwAQIECBAgQIDAUwUE9KcSOYAAAQJzC5SAHmNo5i5JAQQIECBAgAABAoMVENAH27UaRoBA5wSaZrbNmgH0zvWNChEgQIAAAQIEOiAgoHegE1SBAIHBC7Rrw4W8D/psmbjBN1kDCRAgQIAAAQIEjisgoB9XzPEECBA4vsBBQP+EfH58PGcQIECAAAECBMYiIKCPpae1kwCBtQvEED8WY5reHsIkVcY897X3iAoQIECAAAECBLolIKB3qz/UhgCBYQq0i8NNw6erGH8rNTGPqAvow+xrrSJAgAABAgQInFhAQD8xnRMJECBwZIESxn/rn/7TN9MZ/ySk/dbSQ0A/Mp8DCRAgQIAAAQLjEBDQx9HPWkmAwHoFchjP09rTI/x8muKe4nme6+5BgAABAgQIECBA4AMBAf0DC88IECCwPIHbs+Xh6vjxFM7Tddph9OVdUMkECBAgQIAAAQJ9ExDQ+9Zj6kuAQD8FXmuntDdN+PkUz/dSXLdQXD97Uq0JECBAgAABAksTENCXRqtgAgQI3CdQFoo7W1WfSSPovzobQG8Xj7vvMF8QIECAAAECBAiMVUBAH2vPazcBAqsWKPehv/nmm++l6e3/KOQZ7+5DX3UfuB4BAgQIECBAoNMCAnqnu0flCBAYmEBZvj216WOzO9IH1jzNIUCAAAECBAgQmEdAQJ9Hz7kECBA4nkC7cntaKK4MnofgPvTj+TmaAAECBAgQIDBoAQF90N2rcQQIdEygBPQQ60+HGN9Jdcsj6m1o71hFVYcAAQIECBAgQGD1AgL66s1dkQCB8QqUMP7OZz/7VormvxTandYE9PF+P2g5AQIECBAgQOA+AQH9Pg5fECBAYKkCOYznae1pfbjw8bKSu4XilgqucAIECBAgQIBAnwQ2+lRZdSVAgMAABNqF4ur4eju5vR1GH0C7NIEAAQIECBAgQGBOASPocwI6nQABAscUKFPamyZ8Mj3ZS1PdLRR3TECHEyBAgAABAgSGKiCgD7VntYsAga4KNLliZ6vqM2me+6+Wae4WiutqX6kXAQIECBAgQGClAgL6SrldjAABAmVi++TNN998Ly3i/o9CXsg9xhLa2RAgQIAAAQIECIxbQEAfd/9rPQEC6xFo70Ovqo+VjdbWUwdXJUCAAAECBAgQ6JiAgN6xDlEdAgRGIdBurRbjx8si7iG4D30U3a6RBAgQIECAAIEnCwjoT/bxLgECBJYhUAJ6qOtPhxjfSRfII+ptaF/G1ZRJgAABAgQIECDQCwEBvRfdpJIECAxMoITxdz772bdSNP+l0O60JqAPrJM1hwABAgQIECBwXAEB/bhijidAgMD8AjmM52ntaX248HpZyb3MdZ+/YCUQIECAAAECBAj0V0BA72/fqTkBAv0WKAvFpX9+LqX01JJ2GL3fTVJ7AgQIECBAgACBeQQE9Hn0nEuAAIGTC5Qp7U1dfzI92UtT3S0Ud3JLZxIgQIAAAQIEBiEgoA+iGzWCAIEeCpS9zy/U9a+kEfRfmd2Hbj/0HnakKhMgQIAAAQIEFiUgoC9KUjkECBA4nkC5D/2NN954P01u/8WykLv70I8n6GgCBAgQIECAwMAEBPSBdajmECDQK4FyH3oVw8fLRmu9qrrKEiBAgAABAgQILFpgY9EFKo8AAQIEjixQ7kNPA+evl13QQzi4D70N7kcuxoEECBAgQIAAAQJDEDCCPoRe1AYCBPoqUAJ6ferUPw4xfj41Igfz8lpfG6TeBAgQIECAAAECJxcQ0E9u50wCBAjMK1DC+BfeeONzKZr/8myhOAF9XlXnEyBAgAABAgR6KiCg97TjVJsAgUEI5DA+u9WoTvehpwF0C8UNomM1ggABAgQIECBwEgH3oJ9EzTkECBBYtECMHy9FzobRF1288ggQIECAAAECBLovYAS9+32khgQIDFugTGlv6vqT6cleaurBQnHDbrXWESBAgAABAgQIPCQgoD9E4gUCBAisVKDJV7tQ17+Sprf/ymwAvby20lq4GAECBAgQIECAwNoFBPS1d4EKECAwcoE8gj5544033k+3oP9iWcjdfegj/5bQfAIECBAgQGCsAgL6WHteuwkQ6JJA2fc8xvB62WitSzVTFwIECBAgQIAAgZUJCOgro3YhAgQIPFag3Ieeprh/vAyeh+A+9MdSeYMAAQIECBAgMFwBAX24fatlBAj0R6AE9LCx8ekQ4+dTtfOIehva+9MGNSVAgAABAgQIEJhTQECfE9DpBAgQWIBACePv/Pqv/0aK5r88WyhOQF8ArCIIECBAgAABAn0SEND71FvqSoDAUAVyGN9oG1d/vEqrxaXp7gL6UHtbuwgQIECAAAECjxGY/UH4mHe9TIAAAQKrFYjxY+0Fc0r3IECAAAECBAgQGJOAEfQx9ba2EiDQZYEyYh4n00+kJ3fTVHcLxXW5t9SNAAECBAgQILAEAQF9CaiKJECAwAkEmnzO+fDMr6X57b8yuw+9vHaCspxCgAABAgQIECDQQwEBvYedpsoECAxSII+gT9544433Q1P9Qmmh+9AH2dEaRYAAAQIECBB4nICA/jgZrxMgQGD1Au1953X1sbJQ3Oqv74oECBAgQIAAAQJrFBDQ14jv0gQIEHhAoNyHXjXVJ8rgeQh5Ic/2tQcO9CUBAgQIECBAgMDwBAT04fWpFhEg0F+BEsbr03v/ODXhc7NmCOj97U81J0CAAAECBAgcS0BAPxaXgwkQILBUgRLGP/9rn387zXX/pdlCcQL6UskVToAAAQIECBDojoCA3p2+UBMCBAjkMJ6ntadHfL3ch26huJbDvwQIECBAgACBEQgI6CPoZE0kQKCPAvFjVUx5fTaM3scWqDMBAgQIECBAgMDxBAT043k5mgABAssWKFPaYx3zVmt30sckfZjmvmx15RMgQIAAAQIEOiAgoHegE1SBAAEChwSa/Px8eObXYhV/dTaAXl47dIynBAgQIECAAAECAxQQ0AfYqZpEgECvBfJo+eSNN954P8TwydIS96H3ukNVngABAgQIECBwVAEB/ahSjiNAgMDqBNIi7ukRZgvFre66rkSAAAECBAgQILBGAQF9jfguTYAAgccItPecx/B6GTwPIa/s7j70x2B5mQABAgQIECAwFAEBfSg9qR0ECAxJoITx+tTdT6dY/rlZwwT0IfWwthAgQIAAAQIEHiEgoD8CxUsECBBYs0AJ45//tc+/nea6/9JsoTgBfc2d4vIECBAgQIAAgWULCOjLFlY+AQIEji+Qw3ie1p7vQ//5tBd6muCeN0X3IECAAAECBAgQGLKAgD7k3tU2AgQGIBB/LoXzFNRzSvcgQIAAAQIECBAYsoCAPuTe1TYCBPosUPY+j9Pqkymg30kNmaQPo+h97lF1J0CAAAECBAg8RUBAfwqQtwkQILAmgRLGf/PMmV+LIXxmNoBeQvua6uOyBAgQIECAAAECSxYQ0JcMrHgCBAicUCAH9En1mc/cSePmv+A+9BMqOo0AAQIECBAg0CMBAb1HnaWqBAiMTqDcdx7r+LGOtzymafj75aOqpqmueaTfdPyOd5rqESBAgAABAt0TaFcJ7l691IgAAQIEZiG3bsInUgLOC8Xln9k5+HZnwbgQ9lIwPxUmk/b3SVrQ7t6C87Hav1fdUOX/IJzr3Z26p8p4ECBAgAABAgS6JGAEvUu9oS4ECBC4X6CMQk/29j6dYu1vzLJtN+5DT+E73xcfYvV/pXp9axWbfzs28adTIP/59PUXc13DpN7IwT3U6T8shHAQ0PN/a2hH20uALyPuRt3v73dfESBAgAABAiMVMII+0o7XbAIEeiFQAvrbb7/9+Su7u/8k5eHr3dkNPY/o13m0/Ld+8803fy5p5o/8mFze3t6eTCYfamLzahXDKynFv5JC+Y303s0U1J8rgT0fOWtMaeQHDZt+MASfBttTzk9HHv7IZ3oQIECAAAECBAYpIKAPsls1igCBgQjk7Jp/TqfR6jQyHepvr5omlgXjOtLAlJy/kqty7dq1s+k/JLyfnk5/6623Pps+54+fTR/lkTL7mb263k6j7Cmox4/Eun4xhfYU3qvrKZBvpxy+FUP1XCpvUtWzyV0HAb5N8AdFPRjg8+s5wOfH4c8Hz9t3/EuAwLwCB7Ngcjl51osHAQIECCxBQEBfAqoiCRAgsGiBNHr+c+Xe7tl+a4su/7jlpa3fUp5Oj1B9KX9K4TwH9VC9+urp/HX1qU8dTMXP8Tq+9dZb76bPn5l9/Ez6fO+RwvvW3SpeCXX9Qjrp5VTuCym0fziVtpueX0lrzu2kGfKXUl5/NjkcCvC5iJLe238/GIXPbxwK8vnL/Eil3T8iP3uxvOkfAgQeFsihPH/k/6EJ5Q/7eIUAAQILFyh/Xy28VAUSIECAwKIE8h/Hzdb29tekkeVPphu4n01f5z+W1/3zu0n/raBO/9HgH6Yp7P/JdD9+4rd/4zd+/YFGT2b1PAjr+e1Q3U4fr+WnZbX3w++VFx/4p06j81vTjY1L6cDLaUX7lyYhvJCy+JUU4j+UZhNcS0VeSiRbSWUrff1M+nwqBfn08iGiQ+G9fdoG+3RUfpLvi0/FH7x277w2zrcVuvdiLrl9qfx7+Pmhlwf7dJr6Pffr//vOZ9/86GBbOc6G5e/lwx85kB/8j6LMkpmeOvXN6YXfU9+583e+8IUvfHl2/L1jxsmm1QQIEFiswNj+sFisntIIECCwfIH8czq+/PLLz3zxzvs/n774uhSK8x/OOSSt+5EG0tsUnELvb6dqvp7+vP+Z2FR//0wIn3zzzTffe6CCedZW/mM+h/L8+eB3UP58+Hn6srx/cGz++kmP+uLNmxfTf7nYjE1zbhrj1XTwTj0Jm6mAy+m2gO2mCtfrEC6kUi+mNH45vb+ZAvypFPJPz5qQajCrQr5quXz+fOjZoZA/e7lJh6Wjywnl2PafWTkfjNbnlw/a9+Dz9pT+/Cug96evjlrTw/8h7b7/YPb8jRtfNa2afy4V9O3p2/yj6X8rH8n/W7/bNF/9/7d3J/CVXPWZ96vqXqlXdbs327SkjuOQkNAshoYkLMZtY2AymTezJIF5E8gyMHl5mQzDfMJmIDPvO2ENTngJWZhhGTLJQBImk2SSMPPirY0NGBt5ARpsME23dK/sdkvqRXS3u6VbZ57/qVvSlaxua7lLLb+y1bq6S9U531PSvU+dU6emx8cndL8/gLjcDfE8BBBAAIEnF2j9wPDkz+YZCCCAAAK9ELAP0A1NFPdfNcHaL7hGY1ZhMiunKKVhWx3bekvRl0KyGX1HkfRLmiTu5mpl5otHjxz93iK4NBRYuk3Xsegpcz/ae9X81/79QXDggD2Yvm5xQrbHll727esbePTRLVG1uqXi3AZNJL8tiqPLtPKdQSUc0MGF7aFrXKoV7tKQ+wGFkU0K4Bbs1UsfbFYd+3SAZJ1V1Of5NNTb1uZKMXcjucv/OH9fs2B2x8UDvj2xtQ8/eaE5tC5P9nPrc9txm4DeDsXerWP+9yj5ndKlEOeXLUND27WDP0ex2wL5S/XIM/V7oN8B7d52gEpf+nciiN1zm3NNENDn+biFAAIItEVg8Rt7W1bKShBAAAEE2irgJ4rbuWfwTeqw+lDGArpV1MLm/DBxDYH28dXCa/KB/pQevU8/3abnHQjPnRtpDo+116ZLesBhwbDa9MGLfE/fx+x7etue3no7KV9azousbKmHbIK72dnZgXPr12/SUYX1oXrpdWRgRyVobNcQgq06LX6jRshv1Wu3h7GG3oduqyb0W6+NblL9B3T/Zn0NKGv368T9qu7TEPyW4rXetgJYEFpimbt36cfn6+ifuPST5lfb3P7CAwBpodLv809Pbtn9BPTFKvn4OT0gZge17Gtu2b5nz96o0XixhXLtNS/Usadhv3/qhySU27nn+kHzTuhFffo6obkqn318fHxUtwnoc5LcQAABBNojkH4gas/aWAsCCCCAQCcEfOQKXXRvbLkr6T23+y4UpDpRhout08phUU8f1pMi6YN9rKHlGlnu0+cWfbtGt6/xH/jXrzuk0QBfDCJ3SxSHXzpWq31Hr2/tyUvDhNUx7SW/0Pa9jR5Mv1/oeen9qVlS5uTe5L79+9OeeVvX3Fdzgjub5G7Fy9DQ0IaTzm1Ur+TGKArXNYJwvZA2CmabfLZYmNep/Bu04g2hc5ud0/n0oXrsYw3Bj8L1Kli/CrJR0chub1ZksjkIrEezKlObA8Am5asI3/yTeti//rLz+p4uczpzN9JH5r7bruXX4G/M3b3wRrL/aWDEwpC38En81GMBvweoDBaebbHfLTvw5ZfNl1++a0O1+nwXupdqf7taQ16eHVSiZHJH2+21U2kUjJ7v96lI+4R+H/2utSDYp+vjOwIIIIBAewWSN/P2rpO1IYAAAgi0V8A+aMeaLO3S2f6++3XbLk1mH5bTD+Dt3Vp712axz3rX/Sd/feav6MsWH4FVDZv9/QE9eqvuu6XR33/f8UOH/MzwLcVIDyavtHe9ZRUrvtn6/pjeTr/bylpvpyu3utqS1Dn5ntyz1n81O/4lp09vXHf2bP+5KNrQV6n0x9VqRb35m+JGo19DFrZph1innvztUaCz7/UcxayqhX2FsL7QxTvUBWq995pIT9E/CjeqSDomEOi7U69o2K9hy9bTbzWz0QC2b9lBALO3yNZvr1PNZrVuW88DmiTuKj3Gkg0B2x+tzez7wt8Tndax69FH92pWx6vV4i/TU56nJz3F8rfa0RrXvicHyPwv5tx6Ftcs/ZtDD/piGX5GAAEE2ihgf8hZEEAAAQSyLWB/q334U8+zgqyGosaaKM73bGW74Bconc691gGGJAzMn7ueBIVR9c5+KXLhLephvmNifPyhRetYSe/6opf25Mf0ffZC34Ng//6kYMl59Wm4t/tabyfP6cy/dnm8PjsAoNHLQTSzabPaJ+yrVvtmo6g/jGbioKHe/YaCuV1er9G4XFcUODVRq93emeKw1mUI2P5kXxbKbT+Z6yHX7WDn8PBujbZ5gZ5wjX7cr6fs1YEVO8Ci/+0f+2XzPev2evuydT3ZQkB/MiEeRwABBNogsJw/yG3YDKtAAAEEEFijgPVkzu4cHrxRw5d/I4Pnoa+mehYSlu5dtwfi2IaVf109v19QMLxZJz/fc3J09PiiDfWid31RETry41Lvz+l96ffWDS91X+vjZm3Lhb4nj/JvlgWsjdMw3XpKSBBcccX6HY3GMxW8r9OT9quZn6ffGbvsoG629pLrZ38qig/ktr6VLAT0lWjxXAQQQGCVAukHm1W+nJchgAACCHRTQJ+37046v+yTdyYW+9BuiwWHlS5Wh+aZ083qqGddwVzrVP1CnXsdhj9hXwoZbwljV98xNPhl3X9rHER3HB8b+6Ze3xpUrAz2ZSHUypWGUd3M3bJU2Ze6r10Va92fWm+n62+9z25bWRb02qZP5HvbBMzZ9udW7znzrT9w2Q/2xRX9bkTXu9mZF+t5T1MveTOQ6ycbpZJckjH5vWjflR8aKlAn90UVngUBBBAor4D90WdBAAEEEMi+gH3Ijnfu3v00nUF8nz4d28Ri9iG5l3/HbWZnXVfNf1afUVnsoG87y5Nehsy2o8mqdOZ087iEYvx5belr6hu8veLCWyuzs/c8+uijx7T91iU9CL3wnNzWZ3AbgWwJ2O+PncZhS+vBp2DXrl2bg/Xrn9twbr9+6a7R74OdS66JBvVv+3rJky0v8a9+y2e0JZvFfaYx2/iRE48+eli3raxzBw10mwUBBBBAYI0C9kbAggACCCCQfQH7e+2CfUHfjqND9+oz+TPUk24fjNMP892ugT84oEI9oA0/ReckX+qvf26TTdlEcO0N6mndknPXLZHo/Hsf1tNwErijmlr8Lj3xZnWdf+F4rXax3nUre9rzn66b7wj0QsB+rxf3ks+VY9fQ0A/HoXuR9u3rtau/QDvulX6/TwO5hWMbUpMcuUrXM/f6Nt3w2/CTA9rvW+z+Z/D446+amJiY1vqTv0tt2hCrQQABBBDozAcoXBFAAAEEOiPge6s0UdyfqC/51T09D11BPKxUNJt38FvnGo3fWxdF71dv9i/bh/iWoJ72YHdCIxnCnoQTP4TXOtktLuiuGX0dVIr/gnLLLZXz5+86evToY4sKkR5EsPUQ1hfh8GNHBSzUpgfWFvSSb92zZ1t1dnafDj/t1+/WdXres/Q7ZZfV8znc/+MPzGkVqz+X3Fa3nMUfEEuDuX6nRlSm907Wav+9+WLC+XIUeQ4CCCCwQgH748qCAAIIIJAPAQu8swro/1oB/fd6GdBtuKsmhdblvYP3TI6Nvcv4tg0PP6MSxO/Uff/cOvT0gd6GqGu2dh9GOv1+k/auW3Cxy4Ppu76SnsbHdOtu3X9bEMa3Twxs/3pw8OD5lib3AV8/W886vestMNxsi4Dt+7aP2ffFB4Si7Xv2/FjkGlfrsWu1u75Q++5Qy75re6RGyugRfwTKr0dP7eiS/C6FUVV/Z7Tl+Fva2m9PjtU/1bJVq4v9rrAggAACCLRZwP7AsiCAAAII5EPA96DvGh6+WpdQ+kLz87F9SO7+33KFBn14ryiE3zJZq79cZUjDbaADCJrYzb1TieL/8J/iLagnj6e9hp3WTranwqWhRr2Afpvq3dfl6UILHLerxLfq+1fUI1hfVCArpxV9cZha9DR+ROCCAq0HfRaco33ZZZddGvf1Pc+F7nrtoFfrYvTP1Cki62xNtstaIvZfltLtv2Rf7MbvuE33br8fCua6IlscH9GmbxyoVj9++PDhx5s1tYOEVh/CeROEbwgggEC7BbrxB7/dZWZ9CCCAQFkF7EN/PLB7987+KHpAH913+w/U88Nlu+viAg1ztyHt7q8Vcv+p3/jevf1p7/SOPbuvD+LwBvUIXmePNa/dbje7FdRtW7Y0A49uWfhY2Ls+pQx0t9LGbXrstg3OfaNWq531r0r+sfdJK296AMJCOwsCiwXsd9P2FftaeGBHvxO7jh/fG0fR1YFCufYkuzLBpZa/9fur/y2U24Rw+t69XvLW8lt5LXT3+QNZLj7qwuDDrn/DH0w9/PAp/0TNfRGMBDYRJAsCCCCAQIcF7I2EBQEEEEAgHwLp32y3Y3jwJgXL6/Xh3j5Ydzvwzms1z0VfIqTbubU+zO4YHv4nSiHvUB55vr1QPXM2kVzawzi/ru7csqBtgd16181zbrI5lcsee0iud2iEws16zpenxsfHFhXLrO11C0PYoifxYykE0n3Y9psFveQ7h4d3ax96gUaSXKfcfbUef7rCr/891X5mOM1h5H4ftP3J1tXtZWGPuXMK4+Ef9M3MfGjuigj79imYj1jdfKG7XUC2hwACCJRRIP2wV8a6U2cEEEAgjwLpeegf0BDzt/byPPQUT+kkOR+9tSd9/qCBfbC3ABPsHBr6BfXMKaiHe32vYW+D+nzxk4Mc1nu5qHc9OKGijyiO3+Ya7rb1QXD/+Pj4mfSF+u4Dvr5b/eyLECOEAi+tveQWWv1+bfXdvXv3xsfD8Fnat1+iveKlOrjzPN3ern1Kz0p7yXWFA1t6d3DKb17/LAzmcazh6+En4jj+7ePj46PNJzGUPdXiOwIIINBlAQJ6l8HZHAIIILBGAR/Qtw8O/lwUhZ/teQ96szJKKkuFdAs0Flp9mf1T7TJxj+3+l5qA+s0KKj+onnfd7TpxDXW/uRX+k4TspXvXbVUPKXx9SZe8urVRnb3zxGF/HejWTdC73qpRjNu2D9uX7RsLeskvufzyK6p9fS/Q7nK9Hn6RHn9aMkS8Gcjt+fP7kq2j95+5bCi9v0RhpFPf9csXBn8SzMbvn3zkEZuXwRb7XbXfWQ42mQYLAggg0AOB3r9Z9KDSbBIBBBDIsYAPvf76yIG7X/XYqC8LDz3/e65CNEN6/KeaOO41TWNfXl++/RqKf8DOtQ2CXbt2bY7XrXuDSv1v1NO4uzns14K6hVx7TRYWJS0LZarZE3rXna4BHd6rib5uqQTR7Y3Tp++fmppKztdNSm7tYXWxtrEvAo8QMr5Ym7V+JT3ezUJv3759S2XTpqsazl2rHXS/do592ncHMtpL3kqd7Mc6KqbyRrYzao/8y7ASv3fiyPi9zScSzFvFuI0AAgj0UKDnH+h6WHc2jQACCORRwP5uu0ATT+04eXJEI2ifkZVedF8uXQZOvYh96pz7pCaOe20TOA3p9mMYtAR1DQ3eeT4M3+jC0C4dd4kP6jqvXaEn7Y1urqLn35KQbT2ilsh8L6SaQjd9R2QYHNJDX1TL3FKJojsfGxv77qISm0HqQFhfhNPjH9N97Qk9x3YgrBHGLw5deJ3C7QvV+Ffqu34Dm73k85dAs99La1/7nqVFvfgqlK64YIVSqW+Kg+i3jo+N3dEspL9ftxeMDmg+xjcEEEAAgR4IZO2NpAcEbBIBBBDInYB9qG7ocmZ/og/er87CeegLBOcnjrtQSLenh8G+fVVNQOVnhtaQ/SHlnjfr/l9TwN+Q4aCeVjXplfTpJ6z6zO6Dm2W32M5TfyB0wc36fltj3bp7jx86dDJ9YfO79Vha6G/9WvQUfuyAgH3uScO0rX5BL/nWPXu2VYLZ5ymQ71fLXKfHdV55tNFe4Y/N2PEZO4Bkd+ggTXNdtp6sLX54vX6XbD/T4r6oOr33WK32ueTnuYMJBPMmCN8QQACBrAjYmxQLAggggEC+BOxD96wC+hsV0D+cuYCuNKAQ0wgrlWoQu09M1Gqva/KmPcit2pGCeiUN6n7ofuhu0Bp+SeGioqDu16UA3AwarS/NzG0L2cnM8It715NAd0TZ/cs6d/0WuXxhol7/9qKSm4t92XoITItw2vBjGsjt++Je8nD7nj1Pj1zjajXVS/X4T6gJhxf1kiuQq2n8nXPBtg3F6sgqktnhFcytuJr47f5KFLzv2Gj9L5pbS/e1BQcmOlISVooAAgggsCoBAvqq2HgRAggg0FMB34O+a8/uF8dxeEezJBbusvQ3fSUh3aqwIDhcMjh4VTUKblBoeqUFDfVeKngoXGW717LZFCqplVWlVqirWLCzOvgljs+qoR5QUx3QsP7bZuJ4ZLpen0xf2PxuByOsPVu/Fj2FHy8iYNj2ZfuULQvC6GU/dNmljcerP65Hr1MDvUSPP1Pt029PbPaSJweF1HBai60jS79XVsylFjvw0FCR+2xf02kXD2v/et/U2NindL89Zos/sJfc5F8EEEAAgawK5OFNJ6t2lAsBBBDolYCFhnjz5ZfvWlet3q/4sFvJwnpeLbhnaVlpSLeyWx3svcmHqkv37H5RHEfv1D0/ZQ8qQKU9zFmrqxVvqcVCdrN3XbeeONlcTffeZcPh40rlzqnRUZtNOw1Uujl34MLWk9bd7mdZKJAGcvtuTuaVLJqvYdeJE09vVIL9cr5OjfB8Pelyy992DKUZyrW/6WeL5Im5fc/DYvW035U+jThRMI/rqtKN6537Ty2XBLRgvtAkDzWjjAgggEBJBfLyBlTS5qHaCCCAwJIC6d9ut2N48CZliuvVY2aXT7IP4llbVhPSrQ5pAPehdPvw7pdHzgd16/G0IGITyZlD+jy7Ow+LPJbuXVdQPK8KfF1POFDRcPjH4/ie6fHxiUWVStvYQryFs/kguuiJBf/R2t6+7GCVGSw4eLF99+7hIIp+XNcSu05zl1+jFP6jdsqEnucn9bN/m6+x19tX+julm7lYFgRzjWU/FofBR+JK30dOHD58wtdgv/4eHCCY56I1KSQCCCDQIpC3N6SWonMTAQQQKLWABTU7D/0DOg/9rRk8D721ceZC+kVmd299futtq6eFKfsKtg8N/az6CdWjHj7Hfm4G9TRk2V15Wixk2dB9fdf/i3vXg2Bc939V565r5u3gC8drtW/458/X0N7DLXQm60m+zz9avFtpILfvC4atB1dcsX77zMxVOmazX1H7WgXy5+n29gv0kqeBPI+fgfzvkt9Xkh7z0xqm/4dRpfKhiSNHHvFNngTzud+Z4u0G1AgBBBAotkAe35yK3SLUDgEEEFiegA/omv3856Io/Gxz6HeWe5PXEtJNxNc3pdmxZ/CXFUvfphm2f6w5RDlr11BPi7qS72nQtnBlM8Pb4l9vByJ0S73r4R1hGN8SVdd95bHvfe/oopWbkS32eluXfeV5scqnYdrqsqCX/JLLL7+i2tf3Ag1IeJlq+kI9/jQb5j03bD1xsNekB3Dy/JlnYTB3Tvu7+0Q1rHzw6OjoIdUxCOgx9wz8gwACCORdIM9vVnm3p/wIIIDAWgQsdMSa9fyp6oLVpGPBRn1ZiMny3/W5kL6M2d1VlScsVjc7COF7T69Qr+n07Oy/VLXfrGC2RyHWwlkWr6H+hIos444kYCfD4Z/Yu+7cY1rH3S4Mbo0id/vEzqd8PZ0Jv7nu1MrWkwb2ZWy2509Jy20FWdBLvn379i2VTZuuajh3rXb+61T3q/TkLS295EmItV+B+cndbH35XpwcNDmiDkZpxL41ZfhpF0Xv1XwFB5sVWzDKJN+VpfQIIIAAAvl/46INEUAAgXIK2N9vC1/VnUODIwopz8pBL7q1lJV5VoG6L4gbH5uojf+afra62Jelj+UtSW+hD3Dbrrxya3Tu3BvU2fxvdd7xLh/Ug6AIPeqtFmnQNiMdpFBas951/a/66r7QJpc7oK9btB/cM1Wv13S7dbEDG6mxrcu+srBYmexgk323Mi3oJd85OPgjceRerGt4X6tnvFhPu8LXO53czZ5vQyiUXvVa+yrKooMN+n2QiupbaTbW3+i+907Wanc3K0kwL0prUw8EEECgRcDeEFkQQAABBPIpYKGrsWNo8L8o8L4m4+ehtwrP9aTrnPTfVuB4mx60cLXS4BjqGurVtOf4sssuu3S2v/9NSqy/Lo+BlqBuQaZI73eJ04V71yc1ceA9etJtCne3Tqxb9/Xg4YfPtTSAWdi+Y+uxwG/fu7mk27dtLugl3zI0tL0vip8bxNFL9di1Ktoz1Jab7InNUxmK2UtuFZxf/EEKC+Z2l+p9i1rofZP1+i3Np/j7dXvBwYzmY3xDAAEEEMi5QJE+sOS8KSg+AgggsGIBC56zO4aH/5U6U38/RwHdKmo9hI2wElUV0j+gkP523Zf2gC6/J93WZOF7vwLngSTsXfKUp/xApRq9VZOr/Qv1M68v2ND3pMYL/02DdrN3XfOW+951ux52bI89pMB+h3qib3Kz7ivHx8dHF77ch3X7PJCup92B3drV1m9fVsbW9q1sGxraWwndC9UPfr2e8gIVfbe6jS2ZNkO5BdFC9pKLYsHiRwPogIT9XluNvxJG8XsmRsf/tvms1JFgvoCNHxBAAIFiCdibJQsCCCCAQD4FrCetsW337hdporg7m1WwcJWXv+0W0mOF9IpC+vsV0m9Q2S2EWB1WExLttfble2W379nz9NA13qY1/ZJCTxJWdVBAOj4A6XlFXBK7C/auB8eDUDPDB8HtOp351o1heH+tVju7CMJ8bD0WpFfTDra6tC3s9QsC5eWXX75rtlL5CZ1Dfr2CuIatB8/SAYU+e1Gzl9yuG69tK6Xbf8n+nJd92qqx0iWpr4K5HVjR78I3NBHgeyfGxj/TXFFqaY6rbY+VlonnI4AAAgj0SKDIb3g9ImWzCCCAQNcE7IN7PLB7987+KLxfeWZQwcY+xKdDYLtWkDVs6EIh3VbZ2tO6kk2kgcYH9Z179uxzceMG+fysvekpBNqlzez8XnMq+vtgGrTN0urb2ruuH4PvKAN/QTa3VKLorqNHjnzP7mxZUqN0PRcKiGZulvZl25pvu6c+dd3OmTN7Yxe+JIqD67WC5yuIXmr5W41h7WGxU22l78U7l1wUF1zMyH5f++wAkiZO/K4Ontw4MVb/WPN+e6EdLPH7sf3AggACCCBQfIGifzApfgtSQwQQKLNA+jfc7Rge/LyC1svU+2YzPueth/hCId3CoH2tdknDZRLUh4aucaF7l5yutxUqGKY9u/a8MiyJ53zvumYGt17qZlAOglPSvk9Gt1TCym3B2bP3Hzt27PuLYNJ9y+xs/7NgbutNLXVT16sfHBzSt5/UE67VIYFrhP2jCqHeuTk3QNJrbNufX4+9tAyLedk+6YO5fmcfEcKHwnPn/qjF25zNdC37v17OggACCCCQNwF7Y2RBAAEEEMivgH2Qn90xtPt9YVR5e87OQ29Vv1BIt+fM98a2vmL5ty0YWtDx69Gl6f6h0uE7lQ3t2tk29N0uzWbvh2UJ6lbtdGkNyhbYrRfbhlnbt0Pq3P6iwrX1rt/52NjYd9MXLf6+e/fujY/rSgJ6tQXy/XqN9ZJvmwv/vpdcB49sKVcveSvVomAeH9fRkd8759xHpuv1Sf9ErmXe6sVtBBBAoJQCBPRSNjuVRgCBAgn4gL59aOhnozD4b81e4bwGzQuFdAs27ehJNCsL6UlQH979KufCG3Rptmf7YdZJUE+HxxdoF1lWVRLjlt51BWlbJK9mcW5aNx/Qk24P4+CW+OzZkXjdum3VSuVF6nF/mVrHDnb8iB+qnTzfNppeAs0+a6RD4O3+Mi522b9mj3msc/7D/6SfP6h5F+oeY9++Pl2NwHrM13owyq+OfxBAAAEE8itAQM9v21FyBBBAwAQs+MSXDg//UMPFD+i2XZLKwlZe/75fKKSrSm0LL/6ghq1QS0U96r+ibuS3KVz+sPUcq/vYetTLGtQTleTfpXvXk97wIzLaqgB/iX9qGsrdXC+5HSTK6z7YarCW23ZkQ5MShhXtW6GN1FCP+R8L5f3HarWHmytecNBoLRvjtQgggAACxRDIay9LMfSpBQIIINAmgdOnTn1/05YtP6/AdJlWab1wFjDzuCjD6L/Y2ezuL9kwsGX92VOnblZF2hn2zMfWZ+GocebUqfu2bNj4SReGx3TvM8NK5RIFK3vcej3L3POrlvAHKszCDpyoRzxO7CyYO7de7ZTelxwUsmt3z79GLyvpYpPeeT07797GIbg/r+hqAsfq9Y9pf5uSiu17tnCeeeLAvwgggAACTQECOrsCAgggkH8B+1s+u2HrFl1DOnp2ECtEJSEprzW7UEhv90GHJGzuC/pOf+f042emp+9at33HJ6NG/LgS6TPU6zlAUPe7kAV0a5OoJXybnd2b3lfmAxmewv+TXMbPhVFYtVyuUwP+Xgc2fmWyVv//Tk9PH9VzCObzWtxCAAEEEFhCgIC+BAp3IYAAAjkTsL/l8catlwyqq+4fKlTmPaAbfzOkxw3rSd84sCVUz+Ntur/971uPNEcc7Auqj3/rxBlt5/aN27b/aZBM8v4sBfUNPqjb8O18H/gw13YtSWhv19ryvx6NJAgsmNtEe5Fu3+6i+NemxsbffXZ6uqbq2X5rBzHoMc9/W1MDBBBAoKMC7f+g09HisnIEEEAAgSUE/BDkDZs39+mx1zZDZJ7PQ0+raIOE0+Hu127YPHBeYecLerAT710uSIJ6GGgm7TMPnDx15tT057dcsu3PdW7/ehXj2QrqfQrq6XnFBNS0lcr93SbCi7VvVC2Y65duROH81zX529vPnpw+JBoL5ba/EszLvZ9QewQQQGDZAp34kLPsjfNEBBBAAIG2CPiAXh0YOFuJwl9UqN2itdoQZAsHeV+sJ91mEnfqSb++wyE9sTo8Z1c5ffLk5NlT03+3fuvWv4qc26YnPFNhLHFNhjMXwTjv+0gvym8T6DV0BYCq7Q86avMt7TVv0VD2f6U5Ex5UgWyvteHs9nuYnA6gGywIIIAAAgg8mQAB/cmEeBwBBBDIh0B4fnr6zMatW/6BEu0PqRdPw9wLEdBN38JOd0O6TYo2f5Cj8vipU49q6Ptf6jSCz2nCr8t1EORpzaHMyaWxksMISTl9YfmnoAIWtu167lVNJqiDM+6wds93Ta5b/3+dPXJkpFlngnkTgm8IIIAAAisXIKCv3IxXIIAAAlkU8KFg45aBp6tH78V+tu1inS/t+9HnetK3arj7yY4Nd29t3zSo2/tlpN7Rmoa+f2bjwMAd+nmPzjm+shnU7YCIPZce9Va94ty2tk2CeRRVNPvbYzrZ4d1u/YbXTh0+fGcwNdUIdGpEcHjuwE5xak5NEEAAAQS6KkBA7yo3G0MAAQQ6JmDBMN6wZeslSrKvVExwBepBT9Hme9LDLg13T7ec9KhbMQpffAAAN89JREFUSLP3zVDnwh/S0Pc/3rh1830afH+lgvpwEtT9RHIE9Xm3vN9Kg7ldy9za/qR2hd9dF7vXPFavf/7s1NS5YN++vuCRR5zCOUPZ897alB8BBBDIgAABPQONQBEQQACBNgm4ga1bz8fOaaK4YJ3WaeGiaMOuF/akd3biuKWaxUxtsRELgXrTH1Sv+sc3bt36bT3wNIW4y3W3ZvH2Qd2eUjR/q1M5lqQNfTBXj/k59Zh/VFcwfM1UffyvpnU6SbPHPFA4t9McWBBAAAEEEGiLAB8c2sLIShBAAIHMCFR2Dg3eo3Okn6N51Sw4FPVArAVlXdZKE3Q14ndM1uvva9bVejHTEK2bHV8sqNvQZ1uqO4cHX6dM/hb5X+liX8QZ3W9twNB3E8r+YmNPGjqsYpdLs+uY20iUPw4a7gOT4+M2+Zst/nQSfafH3HPwDwIIIIBAOwWK+sGtnUasCwEEEMiLgP1Nb2zYuuUFOv38qkDdfQqKRQ2Gve5JT/cJC2n+0mwa4jyrHvWvDmzY8AlXqRzX/Tbj+1b1pltZLahbW3BgXAiZXJwOtNiF/XQtc/2r6/u5z4YV90uTo+Mf1SkNEyqzBXNrPy6ZlskGpFAIIIBAMQQI6MVoR2qBAAIImID9TY810/hTlC5+Wj2BRTwPvbWlk7DbzUuwtW699XZy/rGVp3r69OlzmvH9S7rs3aeqGhqtsGfXUN9EUG8Fy9RtXcvcRmOEdi3zUDdvioLoVyfGar9z5uT0Iyqp/V7ZwRWCeaaajcIggAACxRQgoBezXakVAgiUU8ACotswMKCePvc69fVZqLBx1kmQLaaJr/Pc7O4DW87pnPA7VNV0GHK3a2096pEmDquef+ih75+Znr5tw+bNn9YBEyvnVQrq63xQT85vLurohm6br3Z7aTC34exqC3dn6MLXT9Tq/14HWEa1UoL5amV5HQIIIIDAqgWK/KFt1Si8EAEEEMipgAW+eGBwcEd/GNynntthhcEin4fe2kzz56S7xq9Pjo3/gR60kN7LXk9rD/vy56jvGhr6YRXybeqh/RUF9YqLdZK6tU+oIdXFPoii6mVqUTDXueVRZD3muhnfq5MQPjA1Wv+LZints5G1STq3QKYKT2EQQAABBIotQEAvdvtSOwQQKJdA+jfd7Rga+l/KHq/QRGV2Xq0F1TIs/nzw5jDlN0yO1f9Ilba69zpopQE8CeqDg1e5KLhBEfGVSUB0scY52HXUy9JOvdoX5SzrNJjH8cNRGL3v2NjYp1SgZC6BJJj38qBOr2zYLgIIIIBARgTsyD4LAggggEAxBKwX2cKg+mPdV9Uzqxt2V2kWe09rTrwd/qFmVH+9frZQbME3PXihm11fLPBZOaxtKsfq9fsnxuqvqkTuxSrt39vwajv/WY/Z8+yLpb0CFr79JH1hpVKV+ZgGL7xpoNr3TIXzT+qxONjv9xH7ZbF2KtUvjerLggACCCCQIYHkg1yGCkRREEAAAQTWJGAhNd64ZetWJdJX6baFjTIdjLUgbiE3UvD9Rxu3DhzVzOp362cLwBbUerlYW9iXvfdGp09OH1HZPr1+69Yva2ayH1B5f9DCup5hl/kqW7t1ol3mTiGwUwp0zbQJ3fG+uH/drxw/cuT2EydOzAb7gr7gEVkf7vm+0Yn6s04EEEAAgRwK9LJHIYdcFBkBBBDIvIAP6Jft2XPlbNx4QKXdrC8Le2X7e29h3EK6Vf//Vo/1R3Uj7aU2jyws6UEDf+BApyX8M418eKfmk3uuL2Ac6/QEXwEOpq+stdJgXlUwD3Su/7QuZ/DRSrX6u8cOH360uaqs7QsrqyHPRgABBBAorEDZPrAVtiGpGAIIILBIoLJzaNCGuV+lMd/Wo1zGkOfrvURI9+eCL/Lq1Y/2PmxtY2X1uXzHnsFfUn/uDQqXP2pzmel69hbU7cBLmUZCqLqrWJLZ8ZNg7pyGtbuPV8PKjUdHRw/5tVmP+Yi37vVoilVUjpcggAACCJRBoIwf2MrQrtQRAQTKLeAD34atW35Sue4qBTxNQOYDXtlU/GgCVVoZ/QnD3bPSi25tkoZF36N+9uT0A2eH93xsw+OPP6Ye9aeHUWW7zpu2IG/nUdt3Dq4LoWVRj7k/LSDQOeYVm4VAP3/GRdEvTo3VPnX65Mnjeq7ZBhrOPncgxP/MPwgggAACCGRMgICesQahOAgggEAbBOxve7xx65bdCqY/rbBiM4SXtffVwqyFsiyek764qS2oW3mrwbFjM7qe+90bLr3sPwczs6d037PVoz7QEtStPQnqzUn1NMlexQ7DyORvdCzqNZO12u+fPXnymLdMnAjmwmBBAAEEEMi+AAE9+21ECRFAAIGVClhQcRsGtqjX0L1WMc7+1luPcVkDXV560tN2ToL6vn19Zw8ePKugfufWjZs+1YjCGYVQC+obCeo66KJ+cgvmNjxCnea3aKK9103W6u8/c+rUuCBtn7d2J5inexXfEUAAAQRyIVDWD2u5aBwKiQACCKxSwAfSgcHBHf1heL9i+ZACnQWVsh+U9QZJR+uCieOydE764iaPNNN4RedN2/D2YNvu3Xs0FOBt6iv+F7qe93pNgKZDL3ate3+ZtsWvLeLPaTD3Q9YVzL8SRu49E6Pjf9usbLqPW1uzIIAAAgggkDuB9I0sdwWnwAgggAACFxUIz09Pn9m0ZcvLFeaeWvJh7inUwp70LVvq6m39aqCe6uCRR9LzwNPnZuW703nTVjYre+Xx6enjZ6enP7dh88Bf6sDLgO57VvO861htbJdnswPvRTz4bgb+QIRGEEQ6y/zrYRi/abI2/qYzJ6e/3ayzhXZ6zIXAggACCCCQXwECen7bjpIjgAACFxOwsBJv2DrwYzon9yV2rSn1slrIK/ti4dVCXCSPn9m8dfODZx789tea18POaki3NrNTFKx89r5dUUh/7Oyp6b/edMnmv3dxsFN1ebpGBlj7phOmFaWtrd5pMK8omB/SgPYbJsfqr9c15L+mx2yxfT318XfwDwIIIIAAAnkVKMobeF79KTcCCCDQWQEXjmgItPpU/QRand1WftZuIVdDpW32vOjPdu0ZfKUfQm6X4Mr+YgcXbEi+D+oTo4+M6Lzrn9XRhpcomd9kIV3/VX1venIgIvs1WrqEdjDCz1qvHvM+tVVdB5nevD6OnzkxWv+Pemw22N+cmT3xsIDOggACCCCAQO4FijgMLveNQgUQQACBNgjYAdh46549V1bjxgO6vVlfFmL4uy+E5mJhV7N/2xTvwauOjdb/wvekN8/3Tp+U8e/pSDirS7BtaOinosC9S0H9hfazBk5Y77O1efo8uzvLi+2jdgCiT8Hcyn9co/Y/fM6535+u1yd9wZNrmdtzCOUehH8QQAABBIokwAe1IrUmdUEAAQSeKBDtGB68RyHnuZpQKwmkT3xOme9phnRdhy50eQ3p1n4Lzr/ePrz7VZEL36aJ5J5jlwUPkqBuB22yOnJucTA/o8MK6imPbpwYG7NZ2QM/V8DICMHcY/APAggggEBRBfJyRL2o/tQLAQQQ6KSA/Y2366H/pEY+P0chjfPQn6htgdVCeqSE+PObL9nyrTMPTn89B+ekL66JDQm3g+7+fGydn/4NnaP98fUDW0bVD/1jmkhul388mfHdXpudA/RJmSrqMa/YjPS6XNonNWT/1RO1+mc0id90cxK/QBP5+VECVngWBBBAAAEEiipAQC9qy1IvBBBAIBnWHG8a2HK5uof/kQYEO8WyrPag9rK9WkJ6qJA+kNeQbobJRHd2fvbhoKFrqN+3fcvWj593wbEwcM9QUL9EIdjCuT+/W997FdTTyewCH8ytILH7szgMXz1Vq31cwXxKd9nBhjSYM5zdY/APAggggEDRBXr1xlx0V+qHAAIIZEHADsI2dgwN/bhO171Lt+1vvgUd/vYLYYmlOdw91+ekt1Yr1EiAanoN9UuuuOKSaHb2jQrqb1Qo3uGvoZ4EdQvC3dwnzFlnxidXFdAQ/L/TKPz3TtXrX24W3o8C0G16zJsgfEMAAQQQKI9AN9+Qy6NKTRFAAIFsCFjPsE0Ut00Txd2vSLRHvadJCM1G+bJYimZIz/056a22YbBfk8Qd8JOvBZf+4KWXzc70/4aC+usV1AeaQb1bB26cJXMrnIL5AZ1Y8J7J0fGbm4VNR/URzFtbj9sIIIAAAqUSIKCXqrmpLAIIlEwg/Rvvdg4NfU59pD/lYqdZvZtDh0uGsYLqNkN6S096MtzaJijL8xIpqEdpUL9MM/w3XOOt6r3+VVWqX1+dDunp+u9TB/pvTdZqf9XEtANJ9pV332Z1+IYAAggggMDqBewNkQUBBBBAoJgCFoiSXskouEc96PrR7mJ5EgF/aoBRxS78c3+ddAuP+/bl4TrpF6ta3Azn9t5fPTo6emhirP56nQz+Fd+p7To6pDwJ52EYV4Pwl5vh3JxtOLudN084FwILAggggAACBHT2AQQQQKAEApoX7F6NKbYzf/m7v7z2boZ0p5Ae/LldXzwYGZkJ9u61nua8L2kg9pOw6RJ83RtS7jSgvlLx2xWiHTEimOd9b6L8CCCAAAJtFeCDWls5WRkCCCCQOQE/q3c1ir6mc36nVTr7u083+vKaaa4nXZcq+x87BwevDQ4ePF+AnvS09mkwT0+FSO/vxPf5bYS6kFqypN87sT3WiQACCCCAQC4FCOi5bDYKjQACCCxbwAf0o0eOHNEI9+805+fy9y17DeV+oq7N7Xt5q7o42ed9SLee9PwPd29t1W4E5W5so7VO3EYAAQQQQCCXAgT0XDYbhUYAAQRWJGA9wbGGudtM7n767BW9uuxPtkn15kP6TQUN6WVvZeqPAAIIIIBAJgQI6JloBgqBAAIIdFTADy/WyOJ7kq0kl7nq6BaLtvL5kF4pcE96J1ttfoh7J7fCuhFAAAEEEMi5AAE95w1I8RFAAIFlCPjhxTZRnKboijU1l/WoM+R4GXALnjIf0m24Oz3pC3Ce9Af2tycl4gkIIIAAAggkkwXhgAACCCBQbAEfjmaC4GFVs5Zcbs1f2qrYte5E7eZDOj3pK/OlB31lXjwbAQQQQKCkAvSgl7ThqTYCCJRKwAJ6eKpWm9IltQ76pKSLX5dKoJ2VnQ/pRZk4rhvhmf2tnfsg60IAAQQQKKwAAb2wTUvFEEAAgTkBC0c2rF0x3X016UEnL3mP1f5TrJDejZ2hGwcBVtuavA4BBBBAAIHMCBDQM9MUFAQBBBDovIA6zkcCpzwWhvz9Xyv3wpDOOekX9+zGQYCLl4BHEUAAAQQQyIEAH9By0EgUEQEEEGiDgL/2eTXq+5pz7vtan/39JzStFXY+pCfnpA8N7Q/sOul79/avddVdfH03ere7sY0ukrEpBBBAAAEEOiNAQO+MK2tFAAEEsibgA/rRI0cOq/f8wTC50pq/L2sFzV150pAehlWNUPifO4Yvf35w8OD5HIX0bhyo6cY2crfrUGAEEEAAAQQWCxDQF4vwMwIIIFBcAX95tdAF9/vz0NWVXtyqdrlmPqS7GbmuD1zl1p3DT3lezkJ6p8HoQe+0MOtHAAEEECiEAAG9EM1IJRBAAIFlCaQh6Z7k2Uk3+rJeyZOWI9AXxPGsQvpm56IDhPQFZBwMWsDBDwgggAACCCwtQEBf2oV7EUAAgSIKJCEpDO91cRwHoZ/ZnWHu7WxpDXPXJHzWk76JkL4ANj04tOBOfkAAAQQQQACBhQIE9IUe/IQAAggUWcAH9NlK5WGF81E/zJ2J4jrR3mlPel5CejfCMz3ondjTWCcCCCCAQOEECOiFa1IqhAACCFxQwEJSeOLw4RM6D/2gT2Wa1eyCz+aB1Qvkqye9G/tANw4CrL69eCUCCCCAAAIZESCgZ6QhKAYCCCDQBQELYjZRnJbwnqQHvRvZLNliCf/NW096J5uIHa2TuqwbAQQQQKAwAgT0wjQlFUEAAQRWIBDF9+pcaeX0kPeBFbCt+Kn56klfcfVW8AJ60FeAxVMRQAABBMorwAez8rY9NUcAgXIK+EnhZqP464rnp0Rg7wP0bnZ2X1i6J33fvr7ObjZTa2cfy1RzUBgEEEAAgawKENCz2jKUCwEEEOiMgA/oJw4/ekSr/3aYXGmNmdw7Yz2/1qV60kdGZoLyhHR60Of3Bm4hgAACCCBwQQEC+gVpeAABBBAorICdh+40Udx9/jx0Z2PdWbogsKAnfdvQ0LOC8oR09rEu7GBsAgEEEEAg/wIE9Py3ITVAAAEEViqQ9mZ+NXlh0o2+0pXw/FUINHvSNXJhUxS4m3bs3v2jJQnp6T63CjReggACCCCAQHkECOjlaWtqigACCKQCSW9mGN7r4jjWNdF9j3r6IN87LtAn91mF9EvDKLw9AyG9G+GZHvSO71ZsAAEEEECgCAIE9CK0InVAAAEEVibgw9JspfKwwvlocrm1gPPQV2a4tmerJz12bkb2l4aV8ECPQ3o3wnM3DgKsrU14NQIIIIAAAhkQIKBnoBEoAgIIINBlAQtk4YnDh0/oPPSDPjk5ZnLvchvo2EjQp9P/Z9QUl2WkJ72TBN04CNDJ8rNuBBBAAAEEuiJAQO8KMxtBAAEEMiVgYcmGtWsJ70l60MlPiUfX/+3LUE96JytPD3ondVk3AggggEBhBAjohWlKKoIAAgisQiByI4FN4h6GvB+sgq8dLylJTzpHgNqxs7AOBBBAAIHCC/CBrPBNTAURQACBJQX8OeezUeMbSk6n9Ax7PyBELUnVlTuX7kmfG+nQlTJ0ciP0oHdSl3UjgAACCBRGgIBemKakIggggMCKBHxAP3H40cOK5Q9qRnF7MRPFrYiwvU9e0JOuieO2DQ8/Q1to6KsI79Uc/Gnv7sLaEEAAAQQKKlCEN/2CNg3VQgABBDou4M9Dd6G7z5+HrhnLOr5FNvBkAjZx3DmdcXCZrpP++uaTO/1e3Y3e7W5s48lseRwBBBBAAIHMC3T6TT/zABQQAQQQKLFAEppc+NXEIOlGL7FHNqruXMWOlIRBqBneu7J048BMN7bRFSw2ggACCCCAQCcFCOid1GXdCCCAQLYFfGiKKvG9Lo4bSoTWo84w92y3WV5LRw96XluOciOAAAIIdFWAgN5VbjaGAAIIZErAB/RGZf131Vt7JLncGhPFZaWFNNS9SKGWHvSs7FiUAwEEEEAg0wIE9Ew3D4VDAAEEOipgoSk8fujQSRe4b/o0qBsd3SIrRwABBBBAAAEEELigAAH9gjQ8gAACCBRewMK4nyhOZ5/fnfSgk88L3+pUEAEEEEAAAQQyK0BAz2zTUDAEEECgewKhC0cCm8Rd04d3b6tsKSMC3RxKH2roPvtYRhqeYiCAAAIIZE+AN8nstQklQgABBLop4CeFm2k0Diqen9SG7X2BbvRutkDvttX8DBDWbfSEznjv+ASBYRjOBrOz329Wmf2sd23PlhFAAAEEMipAQM9ow1AsBBBAoEsCPpSdeOSRI4rlDylA2WY7HtS6VDc2sxyBKP7PNnpCLd/fwbY/H0b+2M9fT9Tr39Z27Af2s+W0D89BAAEEECiVAAG9VM1NZRFAAIElBfx56C509/nz0DUGeclncWfRBBqqUGVydPzmwMVvbZ7dYG3f1uCsFVo4X6dL+d1VOT/72qIhUh8EEEAAAQTaKUBAb6cm60IAAQTyKeC7zSM7D90vSTd6PqtCqVcoYCE9mqiNf1AB+h0aQeEP1ui+doX0mSgM+7XuAxuC8LqjR4+e1rptG+1av1bFggACCCCAQHEEqsWpCjVBAAEEEFilQNJjXolHXCNsaKxzGqA4iLtK0Jy9zNq/Mlmvv2/H4GAQRuF7NYjCArR9rXofsJ7zJJy7m7XuVzTXZ587ZvXFggACCCCAAAJLCKz6jXeJdXEXAggggEA+BXxAb1TWfzcMwtHkcmtMFJfPplxVqa39LYz7kO5iZz3p6eeD1fZ0N3vOCeerahFehAACCCBQWoH0Dbi0AFQcAQQQQMCH8fD4oUMnXeAO+vHuuoFLqQTaFtK1ovMK+H0K+vScl2oXorIIIIAAAu0QIKC3Q5F1IIAAAvkWsHDmzz1Wx+ndSQ86+TzfTbqq0rcjpM9EUaRzzgnnq2oBXoQAAgggUHoBAnrpdwEAEEAAgXmB0LkRP4n7/BDn+Qe5VQaBVYd0vXBGs7Vbz/lfcc55GXYV6ogAAggg0AkBAnonVFknAgggkD8Bf67xTKNxUEU/pS97f6AbPX/t2I4SrzykOzernvO+oBH/2WSt9s9UCH9Ou74zIVw7WoR1IIAAAgiURoCAXpqmpqIIIIDARQV8QD/xyCNHFMu/qXOILZ6vdoKwi26IB3Mh8GQh3R73X/pnJqxUqkHsPjNRr/+fzdrZKRN2CTcWBBBAAAEEEFiBAAF9BVg8FQEEECi4gD8PPQjdvc3z0C2AsZRXwNrf94TbJdhaZndvHV0R+55zC+e12i80qQjn5d1nqDkCCCCAwBoFCOhrBOTlCCCAQIEEmhO4RyOBs2xm3egsJRdYKqSLxF+GTQMtwoqC+6cJ5yXfS6g+AggggEDbBKptWxMrQgABBBDIu4DvMa80GvfFUTgbhIG9R1gPKgdz896yayt/GtJD60nfPjh4IIiigTB2DZtQUPcdaK6envO1OfNqBBBAAAEE/IcvGBBAAAEEEDABH9Dd+fMPh+vXHXZh+FT1pPv74Cm9QLofRFP1+peX0LCDOJxzvgQMdyGAAAIIILASAXpFVqLFcxFAAIFiC1gICycmJqZdqInirK4uCe3Frja1W4GAPyddz7fRFdZjbt9tV2FCQSGwIIAAAgggsFYBAvpaBXk9AgggUBwBC+gWumy5uzlRXPIT/yIwL2A95Xb5tPR72rs+/wxuIYAAAggggMCqBAjoq2LjRQgggECxBVwQfdVPFBf6ycCKXVlqhwACCCCAAAIIZESAgJ6RhqAYCCCAQEYEkqHKjcY3dfb5CZXJ3ifoIc1I41AMBBBAAAEEECi2AAG92O1L7RBAAIGVCviAPjU+PqYXPqTLaFk85/zilSryfAQQQAABBBBAYBUCBPRVoPESBBBAoOACyXnooRtpnodOD3rBG5zqIYAAAggggEA2BAjo2WgHSoEAAghkScBP4B4F0Yg/Dz1J6VkqH2VBAAEEEEAAAQQKKUBAL2SzUikEEEBgTQJJj3mjcZ8ugz6ri2hZjzrD3NdEyosRQAABBBBAAIEnFyCgP7kRz0AAAQTKJuAD+rooelAVf9ifh85EcWXbB6gvAggggAACCPRAgIDeA3Q2iQACCGRcwHrLq7Va7ax6z/+rH+GurvSMl5niIYAAAggggAACuRcgoOe+CakAAggg0BEBP6Q9brj/omx+SiG9qq0Q0jtCzUoRQAABBBBAAIFEgIDOnoAAAgggsJSA70U/Pj4+GrrgL8LIv13MLvVE7kMAAQQQQAABBBBojwABvT2OrAUBBBAookDSYx41PuriuKEK9umLXvQitjR1QgABBBBAAIFMCBDQM9EMFAIBBBDIpID1okcTo4+MBEH4N36yOOcsqLMggAACCCCAAAIIdECAgN4BVFaJAAIIFETAesv9+4QujP4R33UehrxvFKRxqQYCCCCAAAIIZE+AD1rZaxNKhAACCGRJwM47jyZqtQMa3X6TetGjwK6NzoIAAggggAACCCDQdgECettJWSECCCBQOAH/XuFC90FfszCs6DvnoheumakQAggggAACCPRagIDe6xZg+wgggED2Bey883BqdPwmpfLPqxc9VDznXPTstxslRAABBBBAAIGcCRDQc9ZgFBcBBBDogYD1lluveeDC+Ea//TA5N93f5h8EEEAAAQQQQACBtggQ0NvCyEoQQACBwgv4c9GtF13noH+Oc9EL395UEAEEEEAAAQR6IEBA7wE6m0QAAQRyKuDfM2IXvNtZn3oYVvUv56LntDEpNgIIIIAAAghkT4CAnr02oUQIIIBAVgWsF70yVa9/WZdd+0wY6S2E66Jnta0oFwIIIIAAAgjkUICAnsNGo8gIIIBArwXiKHq3wvk5etF73RJsHwEEEEAAAQSKJEBAL1JrUhcEEECg8wI2e3t1anT0m+o+/0Pfix4EXBe98+5sAQEEEEAAAQRKIEBAL0EjU0UEEECgzQKxra9yfvb9Lo4f08noffrR39fm7bA6BBBAAAEEEECgVAIE9FI1N5VFAAEE2iJgYbx69OhRC+fvCSOdke4cAb0ttKwEAQQQQAABBMosQEAvc+tTdwQQQGD1AjbUPZis1T6ibH6vhrpXNZ+7v2/1q+SVCCCAAAIIIIBAuQUI6OVuf2qPAAIIrFbALq/mL7Pmgugd/lprYaCudC67tlpQXocAAggggAACCBDQ2QcQQAABBFYrkFx2bWzs/w+dv+yavacwYdxqNXkdAggggAACCJRegIBe+l0AAAQQQGBNAr7zvBHHb3fOndCamDBuTZy8GAEEEEAAAQTKLEBAL3PrU3cEEEBg7QJxsG9f3/Hx8dEwcP/BX3aNCePWrsoaEEAAAQQQQKCUAgT0UjY7lUYAAQTaKDAy4oe1T4zVP6TLrt2VTBjnGOreRmJWhQACCCCAAALlECCgl6OdqSUCCCDQSYF0wjhdbS34txrqrquvhX4CuU5ulHUjgAACCCCAAAJFEyCgF61FqQ8CCCDQGwHrMa9O1et3hWF4ox/qzoRxvWkJtooAAggggAACuRUgoOe26Sg4AgggkDmB2Eq03gX/Tqehf0tBvU8XXWOoe+aaiQIhgAACCCCAQFYFCOhZbRnKhQACCORPwAJ6tVarnQ1C90Zf/DCw9xk/03v+qkOJEUAAAQQQQACB7goQ0LvrzdYQQACBogv4oe6To+M3u8D9oYa62/sMvehFb3XqhwACCCCAAAJtESCgt4WRlSCAAAIItAj4oe7rGu4tmtX9QYa6t8hwEwEEEEAAAQQQuIgAAf0iODyEAAIIILAqAT/UfXx8/Ezogjf48e1hUNGaGOq+Kk5ehAACCCCAAAJlESCgl6WlqScCCCDQXQE/1H2iXr/NhcEHNdQ91OYZ6t7dNmBrCCCAAAIIIJAzAQJ6zhqM4iKAAAI5EvBD3adGa+8IXHxvMtTdEdJz1IAUFQEEEEAAAQS6K0BA7643W0MAAQTKJOCHuqvCs2HkXuecawRhWNXPDHUv015AXRFAAAEEEEBg2QIE9GVT8UQEEEAAgVUIzAb79vUdOzJ+XxgGb9VQd8VzBXUWBBBAAAEEEEAAgScIENCfQMIdCCCAAAJtFRgZsWHt4cRY/XeDOP5cWKlU1YU+09ZtsDIEEEAAAQQQQKAAAgT0AjQiVUAAAQQyLmBD2v37TVjte61C+mNhEPbpPnrSM95wFA8BBBBAAAEEuitAQO+uN1tDAAEEyirQ8EPdDx9+VGegv1bD3W2xfzkf3VPwDwIIIIAAAggg0OzRAAIBBBBAAIGOC4yM2LD2qi699nfOBe/X+eh2kJhZ3TsOzwYQQAABBBBAIC8C9KDnpaUoJwIIIFAMAT+sfbJWu8HF7nZ/6TXORy9Gy1ILBBBAAAEEEFizAAF9zYSsAAEEEEBgBQI2pN0utaZLo8ev0aXXJjXSnfPRVwDIUxFAAAEEEECguAIE9OK2LTVDAAEEsirgL702NT4+puuj/yrno2e1mSgXAggggAACCHRbgIDebXG2hwACCCAQBHY+uq6PPjE6/rcucO/256NzfXT2DAQQQAABBBAouQABveQ7ANVHAAEEeiaQXB89mByr/2YQN/5X8/ro53tWHjaMAAIIIIAAAgj0WICA3uMGYPMIIIBAiQXsfPSK1f+cC1+tc9LHNGlcv35kZndDYUEAAQQQQACB0gkQ0EvX5FQYAQQQyJSAvz76dL1uk8X9fOCchXObRC7OVCkpDAIIIIAAAggg0AUBAnoXkNkEAggggMBFBJrno+vSa18Jg/ANOh9dU7zrPxYEEEAAAQQQQKBkAgT0kjU41UUAAQQyKWAhXT3nE7Xax3R99N8LK1FFCd3uY0EAAQQQQAABBEojQEAvTVNTUQQQQCDzAn5Yu3rS/43OR78tiiK7PjohPfPNRgERQAABBBBAoF0CBPR2SbIeBBBAAIG1ClhA95PGzQThzzkXH9akcX0a7M6kcWuV5fUIIIAAAgggkAsBAnoumolCIoAAAqUR8JPGnarVpqI4+KeaNO5cEPpJ4xqlEaCiCCCAAAIIIFBaAQJ6aZueiiOAAAIZFWhOGnesXr/fBeEvqBfdCmrvV0wcl9Emo1gIIIAAAggg0B4BAnp7HFkLAggggEA7BeZndv/vmjTuXZrZPVQ8pxe9ncasCwEEEEAAAQQyJ0BAz1yTUCAEEEAAAS8wMmLnnkeT9fp7FNI/qZndq+pCP48OAggggAACCCBQVAECelFblnohgAAC+ReYG9Kumd1fG8Tx7ZrZvV/VYmb3/LctNUAAAQQQQACBJQQI6EugcBcCCCCAQGYE5mZ2b/Sv+8e6/Nq3/czuhPTMNBAFQQABBBBAAIH2CRDQ22fJmhBAAAEEOiPQCPYH1eOHDp0MY/czzgWnArv8WsA56Z3hZq0IIIAAAggg0CsBAnqv5NkuAggggMDyBQ7oWuj79vVNjI8/pDndf0aXX0t71pk4bvmKPBMBBBBAAAEEMi5AQM94A1E8BBBAAIGmQHNm94la7fYwjF7N5dfYMxBAAAEEEECgaAIE9KK1KPVBAAEEiixgIT0IqhNjY59RJ/rbmpdfs970uQnlilx96oYAAggggAACxRYgoBe7fakdAgggUEQBG9ZemayN/3bg4t/V5dcq+tkuycaCAAIIIIAAAgjkWoCAnuvmo/AIIIBAKQWst9x6zcOJsfpv6Brpn1ZPeh/XSC/lvkClEUAAAQQQKJQAAb1QzUllEEAAgdIIWEj372G6Rvov6vJrt9o10gnppWl/KooAAggggEAhBQjohWxWKoUAAgiUQsAPdbeaDlT7fto5NxKFYb9+tPPUWRBAAAEEEEAAgdwJENBz12QUGAEEEECgRcBCevXw4cOPr2vE/0DXSP+OZne3a6QT0luQuIkAAggggAAC+RAgoOejnSglAggggMCFBWyCuOr4+PjEbKXyCvWkHwsspDvHxHEXNuMRBBBAAAEEEMigAAE9g41CkRBAAAEEViwwG+zb13fyyJHvRS54ucL5mSCMqrr4GiF9xZS8AAEEEEAAAQR6JUBA75U820UAAQQQaK+AXSNdIf1YvX5/HLuX69Los0EYVLURGwbPggACCCCAAAIIZF6AgJ75JqKACCCAAALLFrCQvndv//Hx8S+6MPppvc5me7frpBPSl43IExFAAAEEEECgVwIE9F7Js10EEEAAgc4IHDx43nrSp8bGPu+i4FU6H922YyHdrp3OggACCCCAAAIIZFaAgJ7ZpqFgCCCAAAKrFmgOd58arX9Wnei/qpndbVX2nkdIXzUqL0QAAQQQQACBTgsQ0DstzPoRQAABBHojYCFds7tPjtU/pcuvvbEZ0q0shPTetAhbRQABBBBAAIEnESCgPwkQDyOAAAII5FrAXyd9slb7SBy4N4dRlL7vEdJz3awUHgEEEEAAgWIKpB9Uilk7aoUAAgggUHYBmyTOQnplaqz+O7GL/10zpNv99sWCAAIIIIAAAghkRoCAnpmmoCAIIIAAAh0SsCBuPeYW0n8rjhv/XiE9nTSOkN4hdFaLAAIIIIAAAisXIKCv3IxXIIAAAgjkTyAN6dFUbfw/KKT/Pz6kO2e964T0/LUnJUYAAQQQQKCQAgT0QjYrlUIAAQQQWELAgrh9VRTS/9/AuRvDSqWqe6x3nZC+BBh3IYAAAggggEB3BQjo3fVmawgggAACvRWwIG6BPJwYq70lCeka7k5Pem9bha0jgAACCCCAgBcgoLMjIIAAAgiUTcBCul0YvRnS499JetIdPell2xOoLwIIIIAAAhkTIKBnrEEoDgIIIIBAVwR8L7q2pJBef3NzuLt60hnu3hV9NoIAAggggAACSwoQ0Jdk4U4EEEAAgRIItIT02ltc7N4fVvzs7tbDzjnpJdgBqCICCCCAAAJZEyCgZ61FKA8CCCCAQDcF5kL6ZK12Q8t10u1++2JBAAEEEEAAAQS6JkBA7xo1G0IAAQQQyKhAGsQXXyfdips+ltGiUywEEEAAAQQQKJIAAb1IrUldEEAAAQRWK5DO7u6vk+5c/JuhLpTeXBkhfbWqvA4BBBBAAAEEViSQfvhY0Yt4MgIIIIAAAgUUSM89r0yO1d8dO/emUCld9bQvQnoBG5wqIYAAAgggkDUBAnrWWoTyIIAAAgj0UiDtSa9M1Wofdi741wrpVh57v2z0smBsGwEEEEAAAQSKL0BAL34bU0MEEEAAgZUJpCG9qonjfl8h/TVBEtIrWg0hfWWWPBsBBBBAAAEEViBAQF8BFk9FAAEEECiNgIV0C+MW0v/Uhe7ndduGudu10mf1nQUBBBBAAAEEEGi7AAG97aSsEAEEEECgIAIW0meDvXv7p0br/82F0U/5n8OwGjhHSC9II1MNBBBAAAEEsiRAQM9Sa1AWBBBAAIHsCRw8eD7Yt69vamzs8y521wSBOxtEUVUFncleYSkRAggggAACCORZgICe59aj7AgggAAC3REYGZnxPenj41+KXPBC59ykJo/r08YJ6d1pAbaCAAIIIIBAKQQI6KVoZiqJAAIIILBmgWZP+rF6/f5qGP2E1nfIQrrGwZ9f87pZAQIIIIAAAgggIAECOrsBAggggAACyxVo9qQ/Njb23Ur/zAt0Lvr9URT1E9KXC8jzEEAAAQQQQOBiAgT0i+nwGAIIIIAAAosFmj3pR7979LH+2L0obsR3WkjX0xjuvtiKnxFAAAEEEEBgRQIE9BVx8WQEEEAAAQQkYD3pmjhufHz8zFS9fo1rxP8jjKK+5uzuNvs7CwIIIIAAAgggsGIBAvqKyXgBAggggAACErCQbtdF1/XRJ+v1fxy74D+GlYrN7m7XS7cvFgQQQAABBBBAYEUCBPQVcfFkBBBAAAEEFgg09JOF9ECXYXu9c/G71ZNuP4f6IqQbDAsCCCCAAAIILFuAgL5sKp6IAAIIIIDAkgIW0u39NJocq/9mHLs3aXZ3C+hR4ILZJV/BnQgggAACCCCAwBICBPQlULgLAQQQQACBFQpYb7mde16ZqtU+rOHuP6fbjSAKq83z0le4Op6OAAIIIIAAAmUUIKCXsdWpMwIIIIBAJwQsoDds8jiF9L+MI3eNIvtJDXmvchm2TnCzTgQQQAABBIonQEAvXptSIwQQQACBXgo0r5V+fHT8i6FzP+5c8F0uw9bLBmHbCCCAAAII5EeAgJ6ftqKkCCCAAAJ5EWheK32iXv92o1p9novjL/nLsCXXSucybHlpR8qJAAIIIIBAlwUI6F0GZ3MIIIAAAiURaF4r/cThwycma/WrAxd/thnSuQxbSXYBqokAAggggMBKBQjoKxXj+QgggAACCCxXILlWur82+sRY/ZUudh9oXobN3n9t9ncWBBBAAAEEEEBgToCAPkfBDQQQQAABBDoiYJda89dGn6zV3q5rpb8h8Fdh033OcRm2jpCzUgQQQAABBPIpQEDPZ7tRagQQQACBfAmkveUVXSv9j1wQvkLFP3OxGd51KXXOVc9XG1NaBBBAAAEE1ixAQF8zIStAAAEEEEBgWQLJZdj27u2fGhv7vIsaz3fOfcdmeNcDM4vXoMes150FAQQQQAABBEokQEAvUWNTVQQQQACBDAg0Z3ifGn30m+Gmc/s0w/stCul9uma69bLPaPj7rB8BHwXjGSgtRUAAAQQQQACBLgqEXdwWm0IAAQQQQACBVGDfvr4gmUQu2DE8+AdhEL7BHtLQ9iB27p647/TLjh86ftLu0hfD3Q2HBQEEEEAAgYILENAL3sBUDwEEEEAg0wI2jN2fn759ePgVmjTuer0xj1VnZj5x9OjR03rMRrrZZdlYEEAAAQQQQAABBBBAAAEEEECgwwIWwpc65Wyp+zpcFFaPAAIIIIAAAr0UoAe9l/psGwEEEEAAgXkBu156ulivOsPaUw2+I4AAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCLRX4H8D7duTS/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,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  \"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,iVBORw0KGgoAAAANSUhEUgAAAVMAAACsCAYAAADG+E8MAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAAD2AAAA9gAXp4RY0AAAygSURBVHhe7Z1/bJTlHcBvjhjNcC4O+dXeXVtUTMziP7oYXZY51IkKd1fNnFHj5ohBmA7j2MRsZolmxhhNJort24KgsiFsim7TAdMYRFQEFTcVxw/rwAEFRChQ+uuePc/1qQP3TNs+33veu+vnk3zS42gfnve9t58+773XIwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUEpkG6/XPpnIRR8gIh5t41r9cYatBfwP9Q3n6x20TZtP1DcpRMTPNdeU14uuVt2Mq21FBkxtMjmrLpVq0R8311ZX32rvLmMKP230jqmP3DsNEfHzzEW7ExfOGWmL8oWkk8kf1qXSPXXVqaXJUaPOqKmqOrMumfprbTLVnUqlLrefVkZMmP11/ZOlw7lzEBEHojmrzUZTbV3+L3Vjx04wIR09evTJ41KpKdobjCNHjhw1duzY5Lh0jdKr1LPtp5cBJqSsRhFR0t6gzrSVcXGMDqmqSSYz+vYwE86aqtS1tdXp683tujFjUjVjk5P1KrW999PLgVzU5dwZiIg+mqBeOqfOluYo0un0cTqmXfaPw8wK1d5O6FP8t2rT6Vv0zS+bsPbeW+rkoo+cOwERUcJcdMDW5iiqq6uPH5eq6Vt1FlamOqI761I1209J1/RF9kvlEdP6hm87Nx4RUdJswz22Op9iYqpXo532j2Zlmj/ppJO+qj92p8eMOd3ef0x5xDTXtM+54YiIkuaiDludI+k9hU8njtO3CzE1d44YMWKMvn3Q3B4+evjJ+nbfKrWE4XWkiBjKy5vPsuX5lLpUamZtMr3f3K6tTr5TuFNTl0w+WpNK3az/rqO2Oj3N3l2iTI6mOjcYEbEY5pqetfU5irrq1DO1ydSBcVWpG+xdibqq5AyzOtX3L7R3lTD10XLnBiMiFsNcU+HU3UVyVPIMHdWVp9XWqVNravP69vKqEVWn2r8uceqj/c4NRkQshrmojF4vOhCIKSKG1H0RqgIgpogYUmKKiCggMUVEFJCYIiIKSEwREQUkpoiIAhJTREQBiSkiooDEFBFRQGKKiCggMUVEFJCYIiIKSEwREQUkpoiIAhJTQS97WCUueEAlLpwdVvNv5iL3nAbr9x50/1vF9iKtaz4DMa7HwDz+rvn0x6x+/OKYdzE023GRPn7MMXSp3ieTG93bXGkSUzlvnvuyiovjrpznnNOg1Af/us277Mhh2fnJod5vQNe8+qP+Jo6LadEq95z64deuXWBHqQw6u3tUW3un2rxjn1q9Yadasnqzuqn5ZXXyNQtU4uKHVCJTgYElpnKab6a4qJSYfrTnQNnG9IaHX3LPqR+eqCMzVNiz/7Ba8dZWdeV9z6vEBL2KrZSwElM5iak/xHRo0dnVo55d96Eaf+Miv6dJSkFiKicx9YeYDl3ebtmjzpu11O/xj1NiKicx9YeYwhtbdqlTpuqVqrko59hXJSsxlZOY+kNMwzPrsTXqzsVvqLuWvKEydy9TuXuWq18ufL1w371L16sV67cVLiaFpCefV4+++E+VuGC2c3+VpMRUTmLqDzENT2LCb/UqsFElMg3/nZO5KFS4TztJPx6XzlFVUxaqKXNWqo/bDtuvLD6729rVN366xITqqP1VkhJTOYmpP8Q0PIXXhjrm5FRH7ZjJDeqO36+1X118unt61C2PrNbH5RGxL0WJqZzE1B9iGp4BxbRPHbZJdy+zI4Rh/gvvF1bIzvmUgsRUTmLqDzENz6Biasw0qh/r0/6QPPnqB37HRzElpnISU3+IaXgGHVNjNlJ//3CPHSkMT7/WUppBJaZyElN/iGl4vGKqHf+TxXakcPzxFb1CLbXnUImpnMTUH2IaHt+Ymqi9t22vHS0cP1vwqns+cUlM5SSm/hDT8HjHNBep825/2o4Wjnw+r8ZPX+yeUxwSUzmJqT/ENDzeMdV+5apH7Ghh2XewQ2T+IhJTOYmpP8Q0PCIxmmRO9T+xI4blmTUthdWxc14hJaZyElN/iGl4RGKajdQt816xI4Zn+FWCx/9gJaZyElN/iGl4pE6Tz5yxxI4Ynvc/2tv766+OeQWTmMpJTP0hpuGRiuno6x+3I8bDiOsedc4rmMRUTmLqDzENj1RMh13RbEeMB3PMxvrcKTGVk5j6Q0zDIxVTcxGqq7vbjhqeru4euW0ZjMRUTmLqDzENj1iA9HGzdlOrHTUebp0f4wv5iamcxNQfYhoesZhmGtXClRvtqPGwbbc+fuJ6h35iKicx9YeYhkcspjpitz22xo4aD+0dXSoxMaa36SOmchJTf4hpeCRjGudrTfuI7ao+MZUzzph+51d/UufOelrEb/78KbUhhjeuMBDT8IjFNKbf0f8stz2+xj2/YktM5YwzppUCMQ2PWEy159y21I4aH6ve3e6cW9ElpnISU3+IaXgqLaZb47oIRUzlJKb+ENPwVFpMt+892Pu/qjrmV1SJqZzE1B9iGp5Ki+mufe0qlnfhJ6ZyElN/iGl4Ki2mhfc4vczjGBqsxFROYuoPMQ1PxZ3mf8xpvizEtCwhpuGptJju2HuImIpCTMsSYhqeSovpBzv3m7A551dUiamcccbUvMHE60Ku2bhTHWjvsiOHhZiGp9JiumT1Zufcii4xlTPOmB5rfhKbJ90lvPgh9frGeN79h5iGRyymJfIbUPX3LHfPr9gSUznjjCm/m28lpgNGLKYl8rv5sZziG4mpnMTUH2IaHsmYTo/5usH+Q529Z1eu+RVbYionMfWHmIZHLKaZRrXopU121HhY37Kblak4xHTwEtNBQUwb1Yr12+yo8XD2zKXuuYWQmMpJTP0hpuERi+nkBtX6ySE7anja2vUp/iUxvTG0kZjKSUz9IabhkXzONE6eWLXJPa9QElM5iak/xDQ8UjE98Zr5dsTw9PTk43nbvSMlpnISU3+IaXikYnrq9CfsiOH5y7p/mZg55xVMYionMfWHmIZHJKY6ZJfc+ZwdMSyHO7v1MRPjc6V9ElM5iak/xDQ8IjHNNKolq7fYEcMyrXGVe06hJaZyElN/iGl4RGIa08WnTdv3xfci/c9KTOUkpv4Q0/BIxHT8tEV2tHC0d+jTe32suuYTi8RUTmLqDzENj3dM9Sn+3Oc32NHCYK7enzXzSfd84pKYyklM/SGm4fGN6fAfzLMjhWPGvJedc4lVYionMfWHmIbHK6aTG9Tcv4Vdld6+cI0Jl3s+cUpM5SSm/hDT8Aw6ptlInX/Hn+0oYbipeVU8/yVJfySmchJTf4hpeAYV00yDOvf2Z+wIxae7J69+NPvF0lyR9klM5SSm/hDT8PQ7piZk+rTeHGv3PrXefnXxOdjeqcZNXeSeUylJTOUkpv4Q0/AkvnV/77stfdaJD6lhVzSrE6+er06/abHK3L1c/SHwC/OXvbm1MA/XPis5iamcxNQfYgqGg4c71VX3P19YCbv2V0lKTOUkpv4Q06FNR1e3enjZuyrx3Qec+6mkJaZyElN/iOnQpL2zSzWt2NB7Sl/KF5k+T2IqJzH1h5gOHfL5vHq7ZY+aMmelSlygV6LlGtE+iamcxNQfYlrZfNx2WK16b4e60bzTU7ZRJSZ5PNalJjGVc9Jvlqnlb24tXIEM6cp3/q2O/f5c55wGZaZRPfjsP5z/VrH93cqN+hvM46LDxDnqpXe3O8cupive2qYuues595z64QlXz1e797erlta2ivDNLbvV2k2thX3z6yfWqol3PqdOMD/wL9an8fqHtWsflL3EFLEENKe45uVIZlVe7prtMFfhy+lKvITEFBFRQGKKiCggMUVEFJCYIiIKSEwREQUkpoiIAhJTREQBiSkiooDEFBFRQGKKiCggMUVEFJCYIiIKSEwREQUkpoiIAhJTREQBKzamuajVucGIiMXxoK1PhZFtaHJsLCJiccxFu2x9Kowrmsc7NxgRsRhmol/Y+lQg5jkM10YjIkqai/K2OhVKrukF54YjIkqai3bY6lQwuajbufGIiBLmtOfcd7wtTgWTi6Y7dwAiooS5aJmtzRCgPnrNuRMQEX3MRq22MkOIbONG585ARByMuaYKfSlUf8hFi/QOyOuVqnvnICJ+kebKfX3TWluVIUw2Ok2vUluJKiIO2Fy0N5Ftus7WBAqYqNZH6/THfTqsnYn6Zr2zEBGP0KxCs1GbbsSWRKZhgq0HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBpkUj8B4Aom+MbT+3JAAAAAElFTkSuQmCC\"\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  \"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  \"9f0d8150-baa5-4c00-9299-ad62c8bb4e87\": {\n    \"name\": \"GoTrust Idem Card 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  \"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  \"34f5766d-1536-4a24-9033-0e294e510fb0\": {\n    \"name\": \"YubiKey 5 Series CTAP2.1 Preview Expired \",\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,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\",\n    \"icon_dark\": \"data:image/png;base64,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\"\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  \"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  \"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  \"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  \"20f0be98-9af9-986a-4b42-8eca4acb28e4\": {\n    \"name\": \"Excelsecu eSecu FIDO2 Fingerprint 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  \"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  \"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  \"6d44ba9b-f6ec-2e49-b930-0c8fe920cb73\": {\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,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\"\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,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  },\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  \"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,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  \"73402251-f2a8-4f03-873e-3cb6db604b03\": {\n    \"name\": \"uTrust FIDO2 Security Key\",\n    \"icon_light\": \"data:image/png;base64,wolQTkcNChoKAAAADUlIRFIAAABkAAAADggGAAAAw5nCjcK5aAAAAAFzUkdCAMKuw44cw6kAAAAEZ0FNQQAAwrHCjwvDvGEFAAAACXBIWXMAABJ0AAASdAHDnmYfeAAAB8OfaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49IsOvwrvCvyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0OCA3OS4xNjQwMzYsIDIwMTkvMDgvMTMtMDE6MDY6NTcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjAtMDQtMTBUMTE6NDY6MTYtMDQ6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTA0LTEwVDExOjQ2OjMyLTA0OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTA0LTEwVDExOjQ2OjMyLTA0OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjUyM2FkMzNkLTkwMjMtNGNlNS05MGJmLWUzZmExZDdjMGFlNiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjBhMTFlZTdmLWQ5ZTQtYWM0NC1hM2I2LTllZmVkYTA0NDA5ZiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmI4ZGRmYTA5LTdiM2MtNDMwMy1iNTlmLWE2MTQyZTdiMTJhYSI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YjhkZGZhMDktN2IzYy00MzAzLWI1OWYtYTYxNDJlN2IxMmFhIiBzdEV2dDp3aGVuPSIyMDIwLTA0LTEwVDExOjQ2OjE2LTA0OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY29udmVydGVkIiBzdEV2dDpwYXJhbWV0ZXJzPSJmcm9tIGFwcGxpY2F0aW9uL3ZuZC5hZG9iZS5waG90b3Nob3AgdG8gaW1hZ2UvcG5nIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo1MjNhZDMzZC05MDIzLTRjZTUtOTBiZi1lM2ZhMWQ3YzBhZTYiIHN0RXZ0OndoZW49IjIwMjAtMDQtMTBUMTE6NDY6MzItMDQ6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4xIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8eG1wTU06SW5ncmVkaWVudHM+IDxyZGY6QmFnPiA8cmRmOmxpIHN0UmVmOmxpbmtGb3JtPSJSZWZlcmVuY2VT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\",\n    \"icon_dark\": \"data:image/png;base64,wolQTkcNChoKAAAADUlIRFIAAABkAAAADggGAAAAw5nCjcK5aAAAAAFzUkdCAMKuw44cw6kAAAAEZ0FNQQAAwrHCjwvDvGEFAAAACXBIWXMAABJ0AAASdAHDnmYfeAAAB8OfaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49IsOvwrvCvyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0OCA3OS4xNjQwMzYsIDIwMTkvMDgvMTMtMDE6MDY6NTcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjAtMDQtMTBUMTE6NDY6MTYtMDQ6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTA0LTEwVDExOjQ2OjMyLTA0OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTA0LTEwVDExOjQ2OjMyLTA0OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjUyM2FkMzNkLTkwMjMtNGNlNS05MGJmLWUzZmExZDdjMGFlNiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjBhMTFlZTdmLWQ5ZTQtYWM0NC1hM2I2LTllZmVkYTA0NDA5ZiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmI4ZGRmYTA5LTdiM2MtNDMwMy1iNTlmLWE2MTQyZTdiMTJhYSI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YjhkZGZhMDktN2IzYy00MzAzLWI1OWYtYTYxNDJlN2IxMmFhIiBzdEV2dDp3aGVuPSIyMDIwLTA0LTEwVDExOjQ2OjE2LTA0OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY29udmVydGVkIiBzdEV2dDpwYXJhbWV0ZXJzPSJmcm9tIGFwcGxpY2F0aW9uL3ZuZC5hZG9iZS5waG90b3Nob3AgdG8gaW1hZ2UvcG5nIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo1MjNhZDMzZC05MDIzLTRjZTUtOTBiZi1lM2ZhMWQ3YzBhZTYiIHN0RXZ0OndoZW49IjIwMjAtMDQtMTBUMTE6NDY6MzItMDQ6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4xIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8eG1wTU06SW5ncmVkaWVudHM+IDxyZGY6QmFnPiA8cmRmOmxpIHN0UmVmOmxpbmtGb3JtPSJSZWZlcmVuY2VT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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,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAACXBIWXMAAC4jAAAuIwF4pT92AAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAthJREFUeNrslt9Lk1EYx7/vNte0vXOk7yS7qyWBYvnjIktGU0vDCwktV4KXpv3wB/4BBiIa/QC1wjkVUxNsUuuuzd1k6iBLCxIFzcDXOTZwY8r2sr1rp4uXZuoggryJfS8eeL6c53w45+E5HIoQgoOUCAesGCAGiAEAyX6LZdn19XWGYdRq9T8gkN1qa20VDlVZcZUQYpuZKS0tHTca9ywz6Hurq6s/zs6SP2kXwGI2AzjKqHQ63ft3k4SQpoYGAMWFRXvKLmoLAAwODPwdoLdHD2BkaOh3843J5HK59pTV1dwE8Gp8fP+OS4tL5rfmH6GQkO70oLuzc2jwuSop2dBrOCynk5KO9PX3Z2ZkMCkpqyvfGIYBcL+9w2qdKCoqCgQCAHieF2ofP3xkMr1W0IraulptQYHP7wNF7e2BNl8DIO34CQANd+u7u7oASEABqKupJYRU6a4DoGXxqaoUpZwWA9aJCUJI4QUtgFPqkwnSQwD69ProVxQMBtvb2iiKetDRwfN8KBTiOO7Zk6cA+noNLMsCyMo8zfn9HMflnMkCsLS4OD01DUB39RohxOl0yhMS4iiR3W6PbLszB3FxcbRCQQhRJCZKJBKxWCyTyeRyGoBUKv0y/xmATlcpi4+XyWQajQaAz+ebmpwEUF5RDkClUhVqC3gSnp+biz4HnN8PwO/3R5xAgMvNzk5mkkWUCMDq6nfBdzg2BDCtUABwOl2/fIdAig4IBoORKIjneQVNb3m3ii+XiEHp+wzpGelut/ul0QggEAiUXSm7def2vZaWtLS0hYWvH+Y+5Z/Ny8nNjf5USCSSSIw44XDY4dhQKpXDw8NiiqpvbBwdeVF1owoAu7aWmnrM0KPf3t6+VFLc1Nx8Pu/c6NiYSCSKPsket2d5ednj8UQcr9drX7e73ZtCyrJrVqs1HA4TQpZXVrxer+C7N90Wi8Vms+0fCyr2q4gBYoD/APBzAI6VNqGQPUqnAAAAAElFTkSuQmCC\",\n    \"icon_dark\": \"data:image/png;base64,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\"\n  },\n  \"5fdb81b8-53f0-4967-a881-f5ec26fe4d18\": {\n    \"name\": \"VinCSS 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  \"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  \"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,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\"\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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AAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAAAAAAAgCGwAAAAAAAAAACAIbAAAAAAAAAAAIAhsAAAAAbsHRBAAAAMBbCWwAAAAA2Lt53KMZAAAAgLcS2AAAAACwZ/O4gxkAAACA9xDYAAAAALBX3ydxDQAAAHAGX0wAAAAAwA4tYc1sBgAAAOAc/MEGAAAAgL0R1wAAAABnJbABAAAAYE/ENQAAAMDZCWwAAAAA2IPXcQ+TuAYAAAC4gC8mAAAAAGDjlrjmftzRFAAAAMAl+IMNAAAAAFsmrgEAAAAuTmADAAAAwFaJawAAAIAPIbABAAAAYIuWqEZcAwD8w8693TQQQ1EUNZIbSwkpIaWkA0pwCZRACSllOgi2RBAI4jxIZuzxWtJt4HxvXQCAWUQTAAAAANCZU1wzmQIAAACYgw82AAAAAPREXAMAAADMTmADAAAAQC/ENQAAAMAiBDYAAAAA9OAtiGsAAACAhQhsAAAAAGhdyrcN4hoAAABgIQIbAAAAAFqW8u3MAAAAACxJYAMAAABAq1IQ1wAAAAANENgAAAAA0KJ9ENcAAAAAjYgmAAAAAKAxJaxJZgAAAABa4YMNAAAAAC0R1wAAAADNEdgAAAAA0ApxDQAAANAkgQ0AAAAAS5vybYO4BgAAAGhUNAEAAAAACypxzSbfwRQAAABAq3ywAQAAAGAp4hoAAACgCwIbAAAAAJYgrgEAAAC6IbABAAAAYG4lqhHXAAAAAN2IJgAAAABgRqe4ZjIFAAAA0AsfbAAAAACYi7gGAAAA6JLABgAAAIA5iGsAAACAbglsAAAAAHi2tyCuAQAAADomsAEAAADgmVK+bRDXAAAAAB0T2AAAAADwLCnfzgwAAABA7wQ2AAAAADxDCuIaAAAAYCUENgAAAAA82j6IawAAAIAViSYAAAAA4IFKWJPMAAAAAKyJDzYAAAAAPIq4BgAAAFglgQ0AAAAAjyCuAQAAAFZLYAMAAADAf0z5tkFcAwAAAKxYNAEAAAAAdypxzSbfwRQAAADAmvlgAwAAAMA9xDUAAADAMAQ2AAAAANxKXAMAAAAMRWADAAAAwC1KVCOuAQAAAIYSTQAAAADAlU5xzWQKAAAAYCQ+2AAAAABwDXENAAAAMCyBDQAAAACXiGsAAACAoQlsAAAAAKh5D+IaAAAAYHACGwAAAADOSUFcAwAAACCwAQAAAOBPKd/ODAAAAAACGwAAAAB+S0FcAwAAAPBFYAMAAADAd69BXAMAAADwQzQBAAAAAJ9KWJPMAAAAAPCTDzYAAAAAFOIaAAAAgDMENgAAAACIawAAAAAqBDYAAAAA45qCuAYAAADgomgCAAAAgCGVuGaT72AKAAAAgDofbAAAAADGI64BAAAAuMHL8Xi0AgAAAAAAAAAAnOGDDQAAAAAAAAAAVAhsAAAAAAAAAACgQmADAAAAAAAAAAAVAhsAAAAAAAAAAKgQ2AAAAAAAAAAAQIXABgAAAAAAAAAAKgQ2AAAAAAAAAABQIbABAAAAAAAAAICKDwHatQMBAAAAAEH+1htMUBwJNgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAAhmADAAAAAAAAAABDsAEAAAAAAAAAgCHYAAAAAAAAAADAEGwAAAAAAAAAAGAINgAAAAAAAAAAMAQbAAAAAAAAAAAYgg0AAAAAAAAAAAzBBgAAAAAAAAAARtX8nE+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  \"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,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\"\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  \"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,iVBORw0KGgoAAAANSUhEUgAAAbAAAAGxCAYAAAADEuOPAAAACXBIWXMAABcSAAAXEgFnn9JSAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDMgNzkuMTY0NTI3LCAyMDIwLzEwLzE1LTE3OjQ4OjMyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjIuMSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTExLTIwVDE0OjQwOjUwKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMy0wNC0xNlQxODoxOTo1OSswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMy0wNC0xNlQxODoxOTo1OSswMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2NGRiZjU4ZC05OTY4LTg4NDctYjM5NS05MTY5NjUxYTQwMGQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NjRkYmY1OGQtOTk2OC04ODQ3LWIzOTUtOTE2OTY1MWE0MDBkIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NjRkYmY1OGQtOTk2OC04ODQ3LWIzOTUtOTE2OTY1MWE0MDBkIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2NGRiZjU4ZC05OTY4LTg4NDctYjM5NS05MTY5NjUxYTQwMGQiIHN0RXZ0OndoZW49IjIwMjEtMTEtMjBUMTQ6NDA6NTArMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMi4xIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6zOXRlAABuN0lEQVR4nO29ebw1TVXf+60zP+MLL6+CYBBBEEREokEiiGMcbiSKika9GDXGRDSRqDFqjOjVa4y5RHFEccDgiEoUHFHjhBBHxIiAgswzvMD7DGd6zqn7R9WqWlVdvfc+z7PPPrt7r+/n06d79x7O7t7d9as11CrnvccwDMMwhsbaWX8BwzAMw7gZTMAMwzCMQWICZhiGYQySjbP+AreCc+6sv4IxUjws5cXlwILWxtwZai7EoAXMMG6FGUVqWYTMxz+938fEzVg1TMCMlWCKWLWeO4lw3arIzSI88j9ar+0VNxM1Y8yYgBmjZIJg1fvdjM+1Hp82tfj46jvI867ntZ3zYIJmjAkTMGMU9AjWLGJVrPXnuMa++rl54xsC47IY+Xof/eumqJmgGWPCBMwYLDOIVlOkUMLkqm3X2K/fc1rCVX9hJVY+ml0+fjevBa21Tf9a07HQTMyMoWECZgyKKaLVWhcC5UshcuR9aVGv6whZ4//PS9CaLsDK+hKRSouv9nn1voao6W1XPTYxMwaHCZix9JxAtNK2FizXFag1yufXfLUtr9VCVonfqVGLEA3B8nCM2hYR08813qPFDvUaEzNjkJiAGUvLBGunFi1tHa1VgiVitaYEak09l/aTl877fPf/1N9pDodbbvu2KIlwHQPHsj+u03NK1Ir3kUXx2HWFbKKYmZAZy4YJmLFUnES0aitLWU9rtWBFgVr3pVCtxdcW+5XIpc+jFEX9fW7ZIvMN8aK0oJIIuSxax+rxEWqfev7Iq/coQUvbPdZZU8zMKjOWDRMwYymYIlz1tohTsqLIwrVeC5aD9fia9fj8Onm/iNd6XES81hvWWi1iEAR0Tqeg2JalsKoqsTrycOTUNkGcZPuoErgkaOR9Whi9ErmWiKXvaVaZsQyYgBlnSo87rmltkd2DSVR8KTTr9aIEawO1rfb3Lg1rrRCwecfClCVWC1hhVdWLEqsjH5Ybav8NLWhU28o6KwStcjOaVWYsJW6oNbAAq4U4YGYRrmg9JfEiW1ZiXYlltVELVVzXS7E/vkc/11r6BKzjSrz1U5LWHQGjR7zIInVDBEst9eNivxK2G2QRO6LrphQXowic/p76u8sJGW6jsqIMVQfMAjMWygTh6sS2dGKFuAZ9aV1tiGgpIdqcst4ANl35nlr4tHhpEXNUGYnMWcBELOgK2BGliGmBOvJZxG4Ah2SxOuxbeyVqTllu0HU/xu012lZZcQzmXjQWhQmYsRBmFS5y8oQkU9QWUbKgfLCiNn0WqNayRRatTR+FzCsRU2Km/4cWr3WU63KKeM0qaK3GXfrBRfyLUryOUUIjolWLlwsCdUgWrAPy42KJ79Wipy01ibOl2JvvZjiakBlngrkQjVPlpMJFtrZaoqWtqbQ42PJZrDaBrca++j0bSvhaFlhtffW5EOHWrbC68Z/kQpT1jWp9WIlYvRxEUTsgi5neV79eW2vp/7gyEUQETbsXm65FE7HlZqg6YBaYcSrchHBJMoaOS0msSiwnbVVNXHz5uBay2vKaRbx0PG6e4iUUIqZcicfMIGJKvLTr8BAlVkq8pi3pfS67GpOYufx/j8lZkK4hZOl4zBozTgOzwIy50xAvLWBrDeGSrD/JEBS3oBafetlW6+1qX70UrkTayR1Fij157ei3vuZ9AU5K5JB1J1WedrJG7TpsCdW+Wu9X+9JSWWny2YVVhnIx0rXGzCJbcoaqA2aBGXNjmtXlGwOIUYkYYmn54ALcjFbUtoNtX4qV7Numu5xEvFpp83XSxknF61ZiYHr/JBErxnbRL2TTRGy/Wg4c7Hu1Lz6W19aW2SHZ8ptFyNLx+ZAMM8xW01gaTMCMW2YW4XJhOw0g1q5CLVxEoYrbO2Sh2iGvd3xbyFrildyGtMWr6S6kLV718Z0GtbXSK2K03Yp91ph2KRYipkUrrvco11rM9l10R4qIyboWMp+tRfnOxXGaW9G4VUzAjFuix12olzR2KwrXxiThoiFWLgjWDnCuft533YedeJeKdc2SpNGyuHotLzcnIfPdRrzPEpskZp34WBUb68TFKC2sWrz2POxF4dqT5yqxS3EzLWQxTrZGTPogWFx1fMysMeOWMAEzbhrftkqKOFcd30JlEdZuQaJgEUTrnIiWEi95XotYsrhczjws3IV+8uDkjnBVyRot4Urbp9Tq+sa2p0zumMUq03Gywq2o4lotl2ISK2DXKzGjXPaVuEm87MCr9HziAOtolTn1/YrjNREzbgYTMOPEzGJ1UcaW6mzCLVe6BmU5F62tcz4Ill5q8aqTNjZ8111Yi1YrMaNjcfnyuICOlXVa7kOh1xrz/ZZZXXW+5VpMQuYp0uX74mJarHZb6yhe23Fbv3c9/o81SmvsiB5rzFyKxkkxATNOxATxkgK7Os6Vxlq5yk0YxUuE6Vy0uM55OE+/eImA1e5CbXG1qmnoKvPTEjMmWlxnRK9FVm0nMYuWWtMqoxzb1Ur0ENdg4U50wRrTAlYvInabPsfN1pVYioVr1pgxF0zAjJnpcRlqS6bjKoxW15bPSReShKEF6nx8LOKlRawWrm3KONcsiRnarTkprjUpQeOsRMw3/refsPaUbsbaxTgp4UPiWOIKLKwx37XArqv1TlyLK3fXhd9nL37WOlnI1mJcTgRNf0eHiZgxIyZgxlQmuQy9StCIyRLrylVYZBPSdQ2ebyznyBaZFi+x3urMwmnFd6elwU+ztM7a+pL/76fs6xUzJid9FO5Fn1PjxSJrWWNicZ1zcN1n8ZLEmuuEa2CP8BvtUf4+hzHOKJU9iCK2pr+/uRSNaZiAGROZlKhBdsvJVCVaXHRixiTRuoCyvES4lHjVGYa6/FNLuPpEa5qV1RSqG8A7gXeBuzNuvxl4G/AOtbwNuErZ2h8SWnkXD05Oiij6ZeC9gHsAd8T1PYH3AW6Py93B3x7e2yeuXm1Dv5CtMdkqk2UrHrast+Mh7ZCtsXPx0HZ919WrrbDrqOLJdKepuaGsYj1+TGPWmNGLCZjRS5/LUFtdZJdhKvPklcXlclyrJVoXCKIlLsTa6tLuwk7dQqe+CydLf+8Vq7eD+yvgb4GXAG+iFKqrJzqDAQ9cO+F7LhEE7R7g7iAI3QOBhwMfDLwv+K3w0pYQa7djyyKrxWzd5SlURMg2yUImWYXiVkxJNwQha2WHbpMHpEtlFbHG5No5iOIk1rxO8BCRNZei0YsJmNFhmsvQqWK7yuqSJA3duJ33WahkOV9ti8DpRI2Wu7Aew6XF68SidQy8HtxrgT+Ny18SrCuxns6SK3F5TbV/g3CC7gnu4cBHAP8QuA9wP/A74WXTrLWOmKmEj/W43qCMkx36PFyhrwpKyhCV13qKQeUpVhldzXrmgUNigodSX/mOYC5Fo4HVQjQKJoiXNDZ1hXhxFyV3oWsLV72kmJfLLkOdFt8X5+pzFVJtU21zDdyLgD8A/hp4RVxqn9UQuS/wQQQL7bHAY4BL/en4sl0LWuFadLlkVZ3s0Ro3Jskc1wkG5zWCwXrNwVUf1td82C/JH7rSh07rL6ZvoRRcwERs3gxVB0zAjESfy5BgeUl6fJqHC5VdSBnj0kJ10cEFDxepLC/KLMO6BFSfcE2yuPR35wh4Fbi/AX4DeD7wFkKrOWa2CTG1RwOfTLDSHpBdjkJLzFqJH1I4WM9DpktTzSJiepH98jpJ0d8nDoJ2uXqIzBKtxTV9dxOx+TFUHTABM4CZ411S2aJjdVHGty6q9UV6xMuVpaA6biZmF67iQrgT3HOBXwdeBLxhHidowNwTeBTwCcA/Jbgaq5f0JX70ZS72WWM6Q/F6tLiuRgtskpDtkktWHbjweamKB+X8YyZip8BQdcAEzGiJl1hdKd7lVKWLGAMpYl2VlXXJwUVfCpmIWz22q89d2Cr31BJZAN4K7qXAzxKE682MwzU4b+4guBg/h2Ch3Xv2VPx6UHRtjenCwEnEaFthVyhF7BpZxKSiR5qLzOVZobWgpu9pInbrDFUHTMBWnEq89LJOTpGXDEBJrtCp8ReACyJYcX2JruUlVlpdTaOVFt+Kc+nvmHgVuGcDvwC8eG5nZTV4KPBpwGcDHzLZKmsKmQgLZTUP7VJsuRNFvK4oy0yLmI6NpTnIXHQpkl2KRVzMROzWGKoOmICtMD3iVSdrbLoyPV67DGs34aXqsVhe4jasEzXqKhozCdd14MXgfhL4JeCt8zslK8k9gI8C/hXwGPCXyqenCVmRqUhZjkpbYtfpWmFX6LoVtUtxD1UcmCnJHSZiN89QdcAEbEWZJl6SrIFKm3axkkZ0F4p4XVLr2vKqxUsnasg0JzMnaBwDzwX3Y8CvYS7C0+ATgc8nWGXb/e7Flluxrnhfx8WmidgVQrzsWsxY3PWhHFWatkWSO0zE5s9QdcAEbAWZRbzIMyNv+ShAUbjOoywuB5e8Eq/oQtTiVWcZ6kSNukJ8MznjKvD74J4GvIDxZxGeNevAhwNfATwOuDxZyPqKBevJM/cIYrTrczp9EjEHV3zXIrtGSASRqveS3HFoIjZ/hqoDNpB5xTiJeBGTNVwc1+VDrOuSLy0uvYhldsGVVTVSooayumqXYUe4PPAccD8E/PapnhVDcwT8cVw+CngSuM8mCUPda6x/vzr5RhdXLqbXoV3LspPAE69Z59U/qb6IVe1YUUzAVohGtuEky0sGF8ug5IvkBI3LxHUjYUOqazTjXX56rAuAPwP3HcCvEvxHxtnwhwQhexbwdeAeTUfItJ60Fi1EhZDRFbSOgEUxStduj4j5+HqrZr9imICtCA3x0tOM9IoXZaxLxOuy2u6IF5XL0IXPnWR1JeF6JbjvB55BCJgYZ88BIeb4O8AXAl8J7sGzWWPOlSW/WtZW36Sj6Trx1TXSI2JHZBGT15mIjRwTsBXA9zc065PEy+WxXFq4tICJ9SVp8h3xousybFpdB8Azo9X1mrmfAWMe7AM/RBhn9x/BfTEp0aPXGvPdjkrLxdgUMJff38HnlSdYXN5lC0y9xBgzJmCrhW5Y1hxpEsoNVMyLtnjdFteXXek6FMurTtao3UJauIoe9YvBfQvw3NM+emMuvA74csLv9a3gPnxGa4zyGqhjZPWs2S4qUMe9TDXgOgW/8ovSQGezwsaNCdjIaSRtSPVvES49QDmN71LiJcKVBKwhXudQ05/M6jK8DvwguP9OqJxhDIvfJEw585XgnkzTGpNJKuWxUwLVnLutYbHVwuVdECaJxXlPnoFa7U/vMREbLyZgI6aVcSjxiCgwUpRXsg3P+TLmJe7C28gCljIO6Vpem0wWL/ku/A24fw/81ikev3H6vAX4ekKyx3eC+6DSIJJ5x6a5FLVF1hIuIQlWvS2iRkjh95TDBE3ERooJ2EiZlC5PHkS86cMA5R3yvFwXKN2GWrx00kZLvPT4rt4G6WfBfT3w2lM7emPR/BrwUuDbwX1uv0tRaF2bnU6OQotU2iaXtJIYWBK2+D5d/NcYISZgq0ESL6cmonR5YkIZ6yWDky/7btKGWF+tbMNWId5Ow/R2cN8K/ABWRWOMvBb4AuB/x5jm3bpC1jd/Wy1cHbch5Ek3XZ49+pgsasdeiZzL701JHWaFjQ8TsBHSZ33FjEOZRVmmQ0mWV0zckMoatXD1pcrrGZNry0u+A38L7l8Dv3+6h26cMUfA9wKvCmv3/u0sRdRjVz0WUqKGsryOKcWrXrQFpoXMkjpGignYyJiQtCEZX62q8hcoBynrbEMtYHVR3tryqoPyAPwmuK8E/vYUj9tYLn4N+HvgB8B9TNsSg37x0uiYV122qiNiXlllDStMPs9EbCSYgI2IHsvLRatrnRDzEgErxIvGYOWebMM+8WrGu34U3H8E7jy9wzaWlJcDnwt8F7h/ngVDrg1J8OhDW1Mt0ZLtI5dnjE4xMbKQrSsrzERrZJiAjRftNlxzZY3DHUJV+fM9WYetChuthI1e8doH903AU7F41yrzVkJc7E0x1X6t3xoTfGNdW1pSNPiYPCdZ2udLIdOLxcNGhgnYSJiWdehzrCq5Dn1pfdXVNSaVh5ooXtfBfQPwPad6xMZQuAF8HWHc39eDW58uYtB2G3aq3rswxYoWND3pZRIxi4eNExOwETAp7uXKGZU7cS+XY196qS0vKco71fK6Au5JwE+d9kEbg+IG8BTgncC3gbswWcRqy0nKRIl1lSwuX07hkvarTMXkTsTiYaPDBGw81GnrabAyOWVei1eduKHn9LpAcC+eJFWet4L7t8AvLOZ4jYHhgacRLLHvAne+LWKesl0qUuhpW2A34vaRbPsoYuo9Nj5shJiADRxfNgAQxSuO90qzKvtsfclEkxcJYnXJZ0vsInnSymZhXnoGKN8J7ksI058YxiSeQbiAvhvcdr8ltk47kaMQMK9mgvZ5RujaOkuDnQnWnE40MitswJiADZg+1yF5vFcq0ktP1qG4D/1sMa+meL0rjvEy8TJm5YcIF9p/AbdViljhStTCQ3vmZ7G+tIAVQkZwJyYho+FKPKXDNE4ZE7DxoBM3JhbqJVtfYnXp8lA6VX7LTZ5BmavRbfiLizpKYzQ8jXCRfRu4KjtRX8cwwQIjW1+HqKV2LVImeYhIJleiWWHDxARsoPRlHbosNkW1DarYF9n6mjjOy5fWV2F57YH7KuCnT/1ojTFyDHwHcAfwVeVTMjGljokVVpjLVpUImRYwES/ZNlfiSDEBGzZJUCTrkLb1Vc+urBcRtcLyoh3zKtKdvx340dM9PmMF+M/A+1AUAZZ1KzNRshG1BZaEi8oSo3Ip+tIS0//PGCAmYAOkStxI47101iFd16EImBYxLV6zZBzK/+NHwX07ducbt84u8G+B+4B7bDc+ldyJtONgtZAdkMXrwMFhdC+KK7GVmWhW2EBpDSA0lphJA5ZdV8AkcaO2vi7QFa/WWK9mVfnfAPe1WIUNY37cCTwJeHkjMUkt+vrWiUl1bDdd315d3z641Au3uG/U7jSGgwnYsCkSN1Di5do3eCFirhSvur5ha04vXgru3wDvWszxGSvE3xBE7Ep/J01f43UnrZ5NXOK6RVati1m10VuxVsXBHDSHphhLignYgDiJ9eXbvVMRsfOEUlJ10sYk64sr4P4d8LpTP1JjVfk94Fvo+PC0JVYkKZFFrHaTt7wMImLFde4b17oxDEzAhklK3EAlbngV+3KqYC/lzXxeLXXSRitdHggBhm8Efnchh2esMt8PPKvdWUuWmOtPVOq73nfIbkTJsJVZGuR6T5gVNgxMwAZCZX1BCDY7dRNuOOVWidbVea8Ey7XT5bcIpaYmxr1+FtwzFnGgxsqzD3w9wV0dd9XxsHTNk62wPpd5uuZdI9brshXWueaN5ccEbFgUafM+TpVCKV7TbmQRsCLuNWmw8kvA/Qdgb1FHaaw8byZkJt41PR7WqjajM2/T4htTAvl2vBcwK2wImIANgMaNlG64mFG14cNcX8l1SHnzSnHeE09KeYUwNcpbT/cQDaPDH9KckqeO/RazLbgeEXPZAuuMdTQrbLiYgA0HbX05GlU3aPdCW9lY01LmE08Hfv2UD8wwWhwB/xX4o9lcibryTKcT58vZFbZdvxVWXP9mhS03JmBLzhTrqzNwuZF9eFLxSiL25+C+8zQPzjCmcA34BkLB6LirJWZ1an0tYnp9jhAjlvtgg34rzFhyrBLHcOhYX75hfbmQHl/7/8/F57Z95Trp633eFeNe71zgAQ6ZNXLvYJsy5xtCFqeUkdgnxBP3scHgs/CHhMK/TyFdoHKdSq1EHRPTArZPFq9dtd4B9h3s+1i5I94HR+ozj+L/seocS4wJ2BLTyDxsWl9OuU586T7U4190r7PPdZJ6ns8kjMkxSm4HPhB4P+DecbmDbl0uMQvkBjsk1zG6rparhE7CmwhxxtcAfwe8ZREHMyC+D/hMcA/rqVpPOLWFN8J17wcRsT1gz+cMRik1te7jTM4u/w8TriXGBGwYTLK+NuONuKNu2MJtEq2yPtdhR7xeBe47Fnt8S4cjiNT7Ag8HHhXX9yL3COTm8bfYyEljeURoWXcJovZS4E+AvyAMHn8tq5sJeifwTcDPkeYPg3ZW4hHlYH6xwrSISTaiGMKbBCvsiDCTs3yuFftdckzAlp963Fdf0V7d29QiJpaX9vm3sg4BuBHFa1UtgIcCnwg8Evgw4IFxf0ukelq1WRs7na7tIfwY4vO9B/Ag4DPi694E/DnwYuB/AS8kmA2rxG8AvwB8XnjYl9ih74ut6DbX98U5QqduVwncgQseiRsuezi8y8V+bb6wJcV5P9zfxLnxxlkr96GM+VpHpcv74LG65OAycHcfPFz3iMvtwN0d3M2H52XCyrryRhH/+nVwjycEBlaFDwA+jtAwPoxw4iZYVa39t3oTtS7k5sXtwF0luBr/J/Bc4P8QWuFV4KHAbwH3yuIiocVjckX6fYKldQ24AtxFKN95J8G4leVdDt7tw2uuEry6+y5c/jKjc6paP2YBG6oOmAW23CTXnlNCRnAhStFecZXo9PlWtpW4DnvT5vfBfSurI14fAzwBeDzwPmTRqm5lP8P2LI+FWphqF5WOvbh6vwd/gdCQf3BMtPl14DkE62TsLsaXEuag+0/hYcsK63gnXM7OTfdIdKvvxvtj34WO4QZBuCSmdoz6fcwKWz4sjX4J8d2GS9+cRdkor7IPqZI2XLvWYSvu5QB+CvjjUz+6s2ULeDTwc8AvA18G/l55ll5B9+x1D19vt+al0nNT1funPV9/buv/JpdW/JJ+G/yng38m8CLgXwN3u/XTtNT8ECFOq3Z1xofRPzbsHEG8RNB24ms2XVj6ZmIYr7tnwJiALS/6xkk3lAuuxE3fjX/VFtiJrK93gvs+xt29/FDgxwnZlU8Af2m6aGkRaYmQnkixXk9aWu8pZg6mK2yt75a+/xr4h4N/OvArwOcy3hb3DcAPkIYg1B2xpoiRRUxbYmnkg6vmCvM2JmwQmAtxuWllH8rAyy3JPqS8IWWZNevQATwLeMkCD2yR3A34KoJ18l79rj7f2K7jLOmxy1ZbvejP1NsddyDV7+Dyb103yPU+nSFXfKYH/5HAI2Ms81sIbrex8UzgC3NaPfR4KigFTFtitQWWilrHePNaFLFjlz/fxoQtGSZgS0aVvCGP11xO5JC6h/VN2elV0hWwpvX1dnDfzTjvyo8Hvo2QBt9wE8p6klg13Xm+/dpJQgY9wkWOb9aitVZtTxK14n9sgH8CuI8CvhV4BuPKWnwX8FSCkDE9FlaMlSQP+K/vlS2fBzbfwFLqlx4TsOUk3ZAup86neb9cKWD1DZlqvfnpk1Q6CG61sU1SuQX8O0Kw/7bQcxb6hKsQKhcHtNKNd/XFw1quvWkCVgtV/biIx7h8HfQJWyFkHvw9ge8B92jga4E3znb6BsGvAH8F7kO6VlhtiXU6fL5riaXi1tHTITM9rGHJHEuLCdgS4en0pCdZX4WAVb1JXTKqt9I8wJvBPWsBx7ZI7kPonX8OTaurJVwdkfLdOFS9XQvaNBGbRbzWG+u07avH1Xtbv68jPOE/D9xDCW7UsSTq3An8MKHM1Hp5zFrwO3USXZmVmNztqtNXu9ylvJT8DxOvJcEEbDnRsS/ncuX5YuoISheijA3rJG5UPfeikXse8LKFHtrp8hBCltpjJltdtWhpgbrRWNf70uKy2NUipv8flMJSiFfs8WvB0jEc/bvX++o53PSicR78hwC/BO7LCWn3Y+DZwFeAe3AjFubLc5kGN/vq3pH7xgcRkwSpA8LvcgRpGIuxZJiALR/6RpGqAK2sKl0+qlnrkJxR1Yx9XQX3vYynoOyjCMkoD5hsdU0SLZ0RKPXxJB4i+wpx821rrBUPayVniGWlf1+9FpfWhrIM9G+7SVfM5H/Wv7cDuCf4nwB3O/AjJz/FS8c7CMfx/4WHLStXl6VsZiT6rudCznmdzGFuxCXDBGw5EctL9yT17LPJn1/1JuU5HfvqzTz8ZcaTofZY4H8A9+0Xrz7haqW9H8jaV8+5IFqtlHctYrUVBm3rq3YVisUs6dxS61K7wPRaL9pKEyGrrTEHcAH8d4HbBH5wxvO7zDwb+Bpw92p3FloZien+Ue7EdP+4PKh53QcLuZUwY+K1BJiALQm+6j3GHl7qpbtcfaO+CYtMKuUC0e6TTuxrb0Sxr48EfpZUXgjKtQiKFhk9/upAFhcE64BYH0+ttaDVY7daA5KTcDrVY6c/yaAQMGVtacGS31viOJL6rZcjQsbdcWyAdYNeCNkF8P8d3DHB5Tpk3gD8DPDvw0OxklrxxY4Ho44dy/0jFhjZQnZKyEy8lgQTsOWicB961dD50pUkRUo71pcve+N1gD/9jz8jFIQdOo8AfoKJ4qWtrtri0kK158upuvZcmC9Ki5gIWcudmFyITqXZVy1dyiqlbFg7bkPKhlb/3juUVsOOOq4twv/ejMcv97eIl1xPHnDb4L8T3LsIVsyQeQ7wxeBuy+I1zQrbVut0D8VOgVhgG6jsT5/HhAHmRlwGTMCWj5b7cB0lXpSBaN0DL9LmXbeaQBKxnyFULx0y9wN+kiLmJWvtxtPuQu0iFLHSy65e+yxmtSXWZ4Edk9PvPUBlgaVxfXQFrE7SKSwvJV46/ftc/E6yv1W9w5Pn1ZSU8CRil8D/ALi3Ar8/+6lfOl4IvAD4p3lXJ9ZIOYfeZtUBTBU5yB3BZIGZG3E5MQFbAir3YXI3udj7I1fgkEGZEmzWPcnOmC9PJ/4FhKlSfmEhR3Z63A58D/CQtuWlxUtbXSJc+y4I1C5hkfkl9WMtbDLlxqHvFzA9dixV6vDKjeeqzFLfblx1A9tXBkmmytmL2wfq++zQTiYROiJ2O/gfBvc44G9P+DssC54wqDkKWF8yRxKwyv3ascBQiRyYG3FpMQFbHtJNJ24mr266qkErbjpKC2xS1Q0H8PPA2xdySKfDGqG6xuPotCLabajFS6ynZGlF8brmonj5MPXGdeC6y89rATuM8bFDFz/Xty2vPuEA1YOv3IgpzklM4FC/tQyZ0APWZWbh88TpP5Sw9mVD1qdQRAyAB4L/QUL5qbumnP9l5bcJRX6VRd6KORYZia6bVi8Dmjd9HtTc60Y0zhYTsOUiJXDE7TT7srbAfL7xtHjVWWhN8ToA97yFHtL8+QLgSykGKWurqyVe4i4UC0vE6moUrqtq33WfrbFkfZEtMMlCbMW9kmjId9MuxPhlU6NaWWGFu9hVLmPKSUvPo8S1sgqbySSN07hG2dDzccDXAd8w06+wfNxFmCPta8rdvVYYXUtM4o1Snb4o7ttyI8aAm1ljZ4QJ2JLhs3Dp6gv1OJZavPpKRnXch38Rl6HyKOD/JVSXiLta4iUCpsVL3IQiWPVyjSxiu8BulcShx4OlQcxk60u+QxIM3bBFn1Ph2lKNYj3eLxVtppu003Ft0hWwvrFoLVKChwf/NeD+BPilCW9YVjzwa8CXgrucd3c6g0xIqyfeW8rjsY4aDwY2qHmZMAE7Y3zVq3OqcaPHhejyTdaqd9iqOo+sf4tQgmeI7BDE63264tWKe0mWoRavq4TZd2UG3isOrvhKwFwUCF8KxA3X7zYsqtNXPXIdA5Md6bfWMTG6wyaSiMXfe5sgqnsE1+G+L2NzWsDqkla19VBcF7K9Af7bwb0IeOtMv8py8SeEGaofnXe13LZFskzDmyH3mozLqy0w+VyzvM4YE7DlQk+dousfbiihSm6PSryS9eXK7ENkfR3ckJM3nkxwc80Q9xLxkqnltXjdFZcrwF0+ipmDa+I69N3MQ215FYV+Rbh8OeZrYsMmPXgtZJS/e+3u2iBXSu9kRfqG+1AJavyXncQG4ueKBZi+3oPB/2dwT44fOiSuA88lCZiITCsW1hdb7ng1lNW21vjNbIqVM8QEbHlw8U/RW6xciPXN1ZqsMmVMUTZW/BWhdzpEHk6Y06sR90qWl4MjX7oOW+L1HheES4TsKiEWJgkcyfJyXbdhEi9XitbM4iVf3EWLzOffO1lktTVGaGgliWRLLC713TqxL5/PTS1craosxXUCIc74P4HfmXYwS8hzgW8Dt9kWr8IK8+V9VIuYzkRMHYvKhWiW2BliAnaGVDGRSenzrbFBrdhXs+ahbP/SKR/PabFBmBbljn7xOiaIlx6gLEkbEvO6AtwVxes9avsqKo3eN2JeURiTcFX/37v8nU4S0NcdFnms3V3aIrsRrwPJfuwUGHZVEol8pnJT1oPa6wk0QcXDLoJ/Crg/iidySLwaeBGhvFik1wKjMWic0tshYyp1x1CyOE28zhgTsLMnNWCV9VVX0q7HBjXdh7R72VwH9wcLO6T58inAZ3V3t1yHN2i4Dl2wsO5y8J4oXu9xIfZ1xWXray9aX3XMSz5f/l+ytlwpqPV3m0Td8El8LK0pf8fjKKK6Cn56rF2a6nMLS761+O71UnynxwCfB/zYlINZNg6AX6UQMOhaYX2Dx5v3l2+45x3GWWMCtjy0esobapEaba3YV58Flu6xFzPMQaoXCBMxMsX6Qo33cjGL0MfEjBjnuuKzFXYluhCv+jjuCyVeTqXKR3HwrhSHmxWu+nWu57E0jsc+NJoyZkuETKzBvjnItIWV4qmUSy1oLVeifxK4X2V4CR0vBN4F7u55V8uV2EzmcDmpQzqNhXvelZ/lsDjYmWECthxod5LEQZop9L7sKfZNvteJabyQYWYffhbwEd3dReyLstKGlICqsw5T8ka0vCRp43p8vYhfiieJSLhSHPrEq6CvMfOVQDSOy+l1/BynYlqS8ajT9318UxHzEUvelx2dusNTpIjH75FciR9G+A2+f9LBLiF/Cbwc+Mfl7kLYqe4v377HehOkXPm5Jl5ngAnY2eOgjH/RHq/SWurGqO4ZAriDgboPLwFfDmzMbn3ptHlJixcRK8Z8+TzmS4r2HhLiXkeuHOPVEi/gRPGumd7jy4awEDPXPXZJIiFuy4vlt5e4jW6kmyLmux0f+Tg8+K8A9xMMq3bmNeBPKQSsI+xUA5srF30nnR5VlYPyPjPxOiPWpr/EOA18JTKuHXAv5ohisnjVNxayvhP4o4Uc1Xz5ZOAf5Ycd8XJd6yvFvtyEAcsuug2dGkuFKhHFhMHALlpAp+Euanx2/f8lWeUoflfJTBTLU+J+1wju0dZgbV1lRFfX7z3mBwP/fN4HuwCenze11avdiPUcbCnWrNYbLluq63KP+XyvGmeIWWBLgrbAfK4akHrLvuwhNgcu16Ion/3nwLsXeCzzYBt4Es20dGlYJYmhcB8SG3FfWmCySKbhLjnZoxik7LrJEEm4TutYW8j/a1llhY9RbaOsi8r6kmunNdapTgLSCR3JCnsiuGczrDqJfwzcSShWTDcGVsTCfFleqlijrC95j8XBlgOzwM6e5JdXsYg66K5TelvJG33xLwfwGws8mHnxCcAjy1219XVM1/pKAkawtOoSUWJ9SEV6Pctyy214JuKl6bHGZDzakVihPlhj6Rz4YGEmV6oSdG2F6UojfVOxeAi/xWMWccBz5N3AH3Z3axGblo3YvNckDlZZYGaNnQEmYEuAuhk6szCT3Yd94tXKPEw30z64P1vkwcyBdeAzgHPtxInkRqNbMipNjeJyUV4tXnp+r6J6eyvmdVquwpPS41bUInYjLoUrtXIp6lJZu8RhA5TznImIFeIFsA3+sxdxsHPkiM4cZ/U9kgTM0XTVS3LHhnpdy9Nh4nVGmICdLeniF5dEvDnqKeaniVdv/OsVwOsWdTRz4n4EAVMUyQvK+qqnSknV5n3X2iimRnE5Tb7ZaC+DcNX0iRhByPS8Z2ksnMsxwSTmPrtS0/mgv45iOiePJ/w2Q+KvCJ04+uNgKWmqFQejm/CSrDBncbAzxwTsDKh6cMiNUPnYUy1E2tljhfuwL/71cuBtCzimefLPgLvlh3Wj3Yp9dSwwSpdhJ2nB58K89ViqpRQvYYJLMQ3m1paYz2PiWudlz1UFi+mKWOIy8OmnenTz5++B1+aH+h6p42ApZuhyp1FbZZLMkVz15kI8e0zAzh7tPnRUA099KVx9Qfe+gaj8H0KLNBS2gSfQSd6oLY5W8oa2wMTqqBvplG3oJjTUy07LElNLYZm67lxohaj7roD1ZSTiwf8zwqwAQ+H1wCu7uyfFwerybWlOMKoOo6Mo6gukzqmxIEzAzpbkPnR0CvhqK6t3ECo9A5cBtw/uLxdxFHPkHwEPyQ8LK4MY86E7WaWkz2sLTFxkRXFepxpot2QJGzdJ+v6+nAvtBmGm5iTwrhT4XdeeV6xvMkwPoajyhy7ksObDEfCSclcrftVK5tCxZ12FY52ccGVW2BljAnbG+PbNJDdK7UKctRSQg1D0b2gJHP8EuK1tfWlLQ1tfLQtsz+UkhcLC8BMy7YYkXi1XYtynRf6QINy6Qkk6Tz6OhaNhgbmukAFwd/AftZAjnB8vInkhJsXBOiLmGx1HT+90RSZeZ4AJ2NlRux0K8fJ5LM+G67fAWmNSEq9nWDXsLgIfU+7SjXTtIqvjX4UL0VfuMZW00aqwMSjxElxX6JMrUcXE9GBnfa7EOu2IPLGMFo1EDoBPJPjWhsJfAgdt8dJL0XGkYYFRdRyVC9E4I0zAFkztclDuw3Qj+bKGXR0H68y63JfA8eLTP5y58v7Ao/LDVpxHu8i0gIkLUTfOeuLHZH35fhfZkOmzwvqSXcSlKOdoUiLHcfV/eAzwD07/mObG24DX5IeTrLBUwi0mctSufF31xoFV5DhrTMDOhuKCry0wypulvnn64l/NBI4h8bGE8hCKVvJGUXme3CjrxrkY66WtL9cQriFaX0LlSoSGFeZKd6sWLW19tdyITaHfoWMpLzWHNO8FR5mEUdx7Pt97HfHyanFlx9GEbMGYgJ0theXl2v74joip1zm1Lj4XcH+1kEOYH59EkX1YuK98N/5VW2BauHSjXFteY7O+OiKmrDCZfuUGQcgL0fe5IklxvlDV+Gm4ET34j13c4d0ynuBGVCSx8f0dxyIe5sriv0XiVOVGNItsgZiAnR3p4tfuCMqbpCVikqU4MX3+ncCbF3Mcc+G9gA/KD/tiO7VFUYtXK54zutjXFJIrUVutLpabcl0rbL+KFcp50y7E+vfgYcDtizqiOfA37d2dGBiNtHooZodIHU1x/ZtgnR0mYMtBHUjWpaRmcR3Wn8PfA+9a1LefA48C7lnuqt2HScB8OwamrYginhNdaWnuLEZkfVXoY9MCpBM6Wu7EdM58NRasT/jfD3joYo5pLrwZeHf//aLvvz4rLD32FFU4zIV4hpiAnSG696Yssb6Cvq3U+d4MxDcQ0uiHwsOBna6oiPvQU45vktiWFqxWMkLKqPN0ZlUelfXlulZSciPSrV6SREzchy6ft/rcNQX/NvAfeMrHNE/eAbwlP2wJT9N973oq38RFD2Y2zgATsLOh0xOMYlZYYJQ3k97Xl4GYeCOhRRoCWwSXVKS2kmoXYkri8KUVVrjAXKy24ejPphspKR7mlQXrVOV6p0TMRyFT5zLFwOivi+gBHsFwWu63UQiYUFtgfckcfZ3HdA+aG/FsMAE7Q6T3pkRIRvi3YmDTSkcVQjakAr63Ax/S3T1JvAorQm/7MmVesvEkvTw1vmOyvoRWModK6EgWrO85d5QWWEvECj4MOHeaBzRHrtEbE25lIva5EtOklr6/82hCtkBMwBZI64L31c3jyxuo0+ujX7wSBxTjXpaeO4AHlrtSQ+y6Ila4EaliXq6M4ST3IeONe/VRuxFlrc9R5/xRxcDoDj1I5/CDgEsLO5xb5zXlw3TPeDoWWF8Hct2X96jztGsiGovBBOwM0fEvypugE1B2pduiN/sQcNfBvXZBxzAPHkI4yAod/2pZYDIW7FAvXonXCroPBa82dCKMiLpUrdcdgU78i3byS+ISnY7HUvMqOj9+y3VYdyDrAczT7j8g39vG6WICdnYk60l6cb59IxWWmZ/BAtuj6e9fWh5O//gv2tl0WsQ6Da/rWg/6c0fpPhQmHK8IWeoI+PK8NcWL/Bt0PteDV7HLpecNwI3Jbr9C0FzDC1LFqfV92/o845QxATsbOhe8Eq9eEXOTxSvt22NYc4B9aN5sZiFSWmAtN2Kr4T320YJzDethVXDxHPj+jkDRGXDTkzgED83Y5dISBaymvn+0e7DXhS9ux2ptLBgTsDNE+c6TG5HSRVFU56h89b3ui3cS0vKGgAPu3909yQKr3YgppiONr1fWwyqLF2pQM1U8DJWVqBflgq0HMjfP4wMWcRRz4k0UmbmtBKg+d2Idf07WV/UvLA62YEzAloN0I6l4WJ3U0UnicD1CNqQMxPsAt5W76gSOVgyssMR8nqBSuw6PxfrQnz1m96HQEu3WufRdAWud41YMMX32exNmah4CVwnjwXpoiVifmBXWl2uLmbEATMDOCF/dAMoV0REu1725Jvb03nj6X39uvB9wobs7xVgo4zC1C1FnG0ra/JE01o242qqRzkN0pYqw1xZtOq8qflhU7e+zZO8G3Hshh3LrHBGmGKqo76VJlljRuXRT7kPj9DEBO3tS/MuVrolO4JgyBtZMy4dhJXDcl0LA6hhL4UZ0U0SMnDWX3IerYHFNQ52HVlZnIV6+a3kdq/d1ROxuwL0WdSC3iKd/frwJncSOkDXiXiZiZ4QJ2OLp67UlIfN0KtPXyRutz0iPJ7hJlo73IdToUdRZiEnAfLfRLYQLNWbJ07HAVhp1Po6ViBVC5hvnk0YdRP34InCPRR3EHHjn5Kf7RKt2K3Zchx5L5DgLTMDOEO06rJY6YaMTQKYUseLGGZKA3ZPkKpyUgVgvkoDQETGVpLDqCRxCcQ5c1y3bssbkHNdp9J3zuU6Igw2Ft7d3992HLSEr0uf7vCDGYjABOyNcKT7pZnHZt966caaKFwxHwBy9jV8du+q4vcRacBTuxTpmU3zmKrsTKxdis2NQuWiLzgA9nQwPfkgCVt0bfeJTdxbrYSx1B3OWzzROAROwBdHqqfmGiHk6RX37XIjUnyfPvftUjmD+7NBxP+lGsuNCpLIYXHYrprgXUagqF+LK0bI+XXVOXXU+fb/rsP5dCt7rlI7hNJgwPnKqBeZ7OpEey0I8K0zAzhgJHrvJN0xfNmKHXYYzBmyHkATQoC6+W4vXsYiXqywI5fZKn8UKC5mgMhHTefVd66tvmShid6cTx1xa7mrvbnpDaAtWbzkpE7HFYwJ2RnjVe1PriTdQZcU1rbDrNKsNLCU7dMaA1bTiNXp+q8LNpa0ut+IuQ0XT9edywot0BnSZqamxL/34HsDmaX37ORM7d32uP/1cr2g17lXtTUmfZ0kdp48J2OLpiFDDsur0+mjcNHRvOvYYjoBt0xSwOlbTcSFWri+9v6+xXXmUoGsXq65Sr6ef0ZbX1LFgd2c4Aial93tI95eb4g1RrzH34RliAnb2TOr51TfUpPgXMCwLbJtiOo6+LMRaxFJD27OsfPxrCoUr0TU6CdX51S7H5jm9zHBciAeETl5Fp0NYWVlNd2KPF8VYICZgS0ArmaNeetyHHXYZjoBt0pkQsZXA0ZvEQUO46H6eCVkXPSastzPgSgFL760ec4HmdDhLSSVgk7wZM3lDXP/7jQVgAnZGuK7rob7wW5PkTXs8KBfiBiEOVlE3lsk16CZYXbVFYfGvLsoybVlenY6Cz/smxsHOMSwBm5DkVIhQw7XfWvT7jAVjAnb2dIK/srSCxepFzZvmkGCaDIGtsOprGDvZci1rwZXPafdYwYoLWpFB6JgcY6yWPhFL5/MCw2lIZBqDCfS5EZviZW7Ds2Uo192ouYkgcO/rjxhOS31u8tMtd2KngdXxnOp9rc9aaSoRr4Wp7hj4xmua53iH4VhgckAVUz0d07whDY+KsQBMwJaHOqtpmtuiyZAErCfwPykO1mpQiynvpZG2JI4kWL7eV1tfLdesLy2wSR0ED/ihNCQSQG3REKCZvCGNx8aCGMp1Z8zIgAWs1UjWItaJ27juc1q8LImjTcudOG0A88RzOZQsRMn+mcCJxMisrrPFBGxkDFjAavpciNraSi5ER0f8jAoRdi36OlHDdcWrzkLsPccDdyHW9Ho/Gl4S4wwxARsZ0voMgRkavaYl5srG11cNs9FlknUq57NwxdK2wKD7fmA4FtgkF6IxPEzAjGWjziKsXVc6hlPvA4KQmZhNJAm+uBAbrthJ4tXBTBHjLDABM5aVTrLAhEZ20vuMBlWyS2t82MziZRhnhQmYsczcTKNpRVS7NOM1viH+LetWP25lNhrGWTEU17WxutxUY+mrQPsKt7i9Yu6C2Pc9Z/UkjaXHBMxYdiZZU0V2mIhWYxzdKtPJoCOfq5NigmYsFSZgxjLTamObouV7Bpy64CZfZbeXnIvOrMK+fQ6BYMH2fJZhLA0mYGeEthbqxtc3Gumz/bZnQqfagS/PyVp8rBvldWDd5RRwR1luauVQrlSZ1VvO1zrq3PlS6MyCzUy8R7Wr+iatWuMWMAFbHHVZGiA3MLXlQLtczUrcH5Vg6/ORrIjYEK87WPehMd5wUcB8KI+E758SZJWQa644Z4TztIE6h051Ciivt2mW2RiZdA+2vABAN/bK6l53C8EE7JRp3PC6x9bryqmsjFaDPlZax1rPhiuN70Zcb8btTcI4VedD4fG6MO3KoRpTsbaKcwZsVudyncbsw3R/l7HSOVYR9Z57VHcQOp1Puf9X1QNw2piAnRJ9VkTDVVPEJqp1y6XT+vyx0Gd5yXlYj8sGuRHedLDlwwTPMpZJrLLkPlzVjLrKul9zWai2ge147jbJgiYiJkLW5xUY3fU3xfJ3PffoWvV8ssz0R5uQnQ4mYHOmcRPIOomVy/EHidmIC0dcO7oX7Oj2hDuunREQvX5puyVc64SGdisu2w52PBw6uKFiYjIt2rGzGFiy5snX3Cbh3G172InncJt8Xjcpz3mzE8X4rj9Z15a/3i7uU9VZWnfhmluPF5qnW9XNhGzOmIDNiSnClRoQ17UixPW1qXvCruvSaTUgNB4PnVYDsu7yeZJGdofQ8B4AR1Gk5JzdAI6cqu+3qhYYlUuaHC/cjqJ13oep2XbikkQsns+6MzVGF3brnqo7UOmeVPep3LOHhOtOiiLLxeZcOwZrQjYnTMBukWnCRXbdJIuLeBNE982Wi42Gz73fFJdgskun/r9joWN9xfOhxescoeGQXu5aPH/bwA0XRU2eX9WGQq5PidH4fP3JuToPXCRMrCxCJtdj37U3Jmax/Ot46yblfZuuN6c+J1pkjva8amBCdsuYgN0kswqXWnRDvBHFK7lxCMu2i7EJJWZ1IzKxJ+wZTrVt5V+RRqRpfZEbDhGv85TiJW6xg7j/hg8WWKdi/QqSYjLxmq3P53lKETtPFjF9/U10Yw/lmpsw3VDftVecL3V/yn17GMVLMl/l/Tfi+oj+OdYEE7KbxATsJpgQ7O0IV+z5auHa9GHZIrjAzjk45+G8i+4cV7pzpMc3TcQc8Q13ALuneQLmgAcul7tqEdONiDS22wQr4Qa5zUwxHYJ4SfxLLDBJ7FjJhkESOFQih44lbpI7BCJi5wnnWGJicu21MhLTfXB7XJade1A0etM6Tvo8abf1OQcH0fK6Ea+z5KJ1cOjDIs8fucmThSLr6O5dyWv1ZjABOwHTEjSk9xUFS+Jd0oPddNlnrhMQzvvQ872g1tKI7BAakron3OvKeSTwPLLvYlk5Bu7e3d2MPaAaEOntxtdr8donWl9U8S9oFq5dFdJ16qo4GPncaRG7EDtSfVZY0/r/YUKnaZmvOU84oAf0v6Tv2pNzdC7emwc+XmfxutIhgg0PBy6sdYcqCZnPCUbJxa2/plljs2MCNiOTrC7fcBUq8ZJ4Vop3xeD5ji8bjksOLvluPKIlYL094duAR5zGCTg9XENcxO2VGgXCeThWrpo1tX+f4D68EZdj3++uWUW0FSbXj8RXxbLdJlgWOqljmzzYuTUmUT7cPXQxxzE31AUxyfLvCBjxOotCJO/XMdo9B/s+vO7A5+vy0GXPwRq5I9ZK9DBrbEZMwGbAd4WiuNiloVWipQO+Wyjh8vlmkBsiuW+ieF2mFDHtypnYE3bL3QGehcICU+JV3ODKitgiW17J+iJng61y7KvA0RmDqBtoaaR1PLawwPz0bNhB4cqO0zTLv3Zd+/hGfR1uEYzQbQ97wH5DyA5ddCuSLTKd6NG0xkzE+jEBm8CsVpeLwiKi5Urh2m4Il4iXFrALBOGSRQfUxQqTnvAYM8JaPWFPdhPq14kFIXGJFPcix+lbc1utLL66fumOrSvG16ntmWJgIyBdd8ryF2GX60rX2JRrMIk/4VrcJQjYXiVk4iU4iK7FlOgR3ZHidehYY+ZS7McErIcJ4tVndUmMS1yErR6tiFYtYOfIgqXXOq257gnLZKSDFzLVG9YNoxYwTZ3YIb1ine3VDJCvMLXnoHaV1dbYpno8MQtxJFY/xFgWQTC05V8nXkD3GpT7e5csYGk7CtkeWcj2XejwHhKsMjfFGjOXYg8mYA2muQx9NZ6LOKaL3HutBesky3lKoZPe8CyDmseCHNdaY7928Wxh4jUrs4iYTjraqPaP0erX1J0nOQf6etKWl44f7hLu190ZllSyywVrbJ0oZLHdOVIn2FyKUzABq+gRr1T3zMWqEOTByJvaRehimi2lezBtu5DJlKwwl5M5tGDJOsW/XLa+miI2gp6woI9jrdovlu8R4TeohUv3ku0mb1M31LWQSRbtpKK+o7jWGpY/5GtOYqhQxsq09SX1JOWe3yUkcez6sH09LrojuuVyNvIeKizgslvRxWtcXIradWkipjABUzTEKxXpVDe1JGjU9fhSmq0LmVw6tpXGePnSupL3bVMKlo4/iHhJj3g0DYhGNSbxYaJoOFUCR0u4WuJlN3qgdonLuiNkvitsdfxM3jy66xAVI0yKkffX1v8WofO6TxCjcwSX4S6wqzqr18liVxQpcPm+3pfz7nI87Eipq1znJmIKE7CI797UqcF0FNN3dOrxES2sKFIyjuuCWkTQCvGiK1h64LKulThxEPNYGpIJIgb5uCVW0esutBt7MhNc5FrM+hZ5w9iuuablr8SrFjCd+CJeEy1kYoXpe13ES1thnUo7PiR7pHnG4pc7JseFwUQMMAED+sULVXUalV3oKcaFnI9Wl4iVZBAWAkZXvCaKFt34w6jFS+gRMbmPtVit0WN1VW4fo6S+XlrXft9j2RjrNTfJfa0FrE6zPySIjrQLe5TJW7WXZZscftBWmIQppPOsBVRivZqVF7GVF7Ae8UrJGsp9J8V25YI8T1lFQ6fAaxFrJWXosV2teZimxR9G1YDUTLDElEfH3IVzoM+t2FrLg1Ffe0xwX9MVsCPC/XuDUsj04PBicbEd8ere95UV5hsdVaWuImIWE2PFBawvYQMlXuQ4lFTPkIQMbXFdoi1gEgdrDQ6dRbi0S0e+32h7whqXb9w+l6I8nnbjruSN3WDatdJ3fuXBaK81mNpp0gImrrx14vxf5Pu4dit2xtX5fq9Lcf9rK0x9Py1i8l1WWsRWVsD6EjaoxMvnQK24DFvCdUltX3Q5DlbXNKwvXJkQb5q1NXo3Th8zCtmkG3clztMt0nuOVuU6g4kiBtll3REyF8pKaYvskO7gcOkI63n/dEp9kfnpGxYYKivSKfGa60kYGCsrYBUp5tUSL3LFeF0tQ0TrMpUF5ruWV10OKl2wfrJoraxw1dTH7csbdyXPyWmwqteXUHWYWu5rWUTIvA9isu5C1mBnwlq1FJPWqmXdlxaYg1R8ufUdvfpyMlZsJa2wlRSwRtxLj8NIJaHoipcI1WXgsgvFd2sBa4lXPdNyLVx9JXo6bpwD4B3Au4H3AG+Jj99JToFakSt4pRta4+ZZI7tT7gG8F3Avwk18e9y3PnscNomZzxaZFqQ+MSsETHliUufVq3/msvVFtR9WWMRWTsAmiJcWLqlhuBPFK1ldDi5H0ZJ1qiLvynT5lG2Echn6spc1k3C9DtwfAy8G/hZ4FfB64M55nxzDWGHeG3g/4P7AQ4BHgPvHwB39bkXZp/VkTXVO1xuLFqwkXL4bQgCKjFpPHlyt63zWluJKidhKCVhPxmGKe0GacDJNW6/chiJetxGE6zaiGzEKnMS9+pI1JqbDqzX7wKvBPR94LvA3BAvrcP6nxDCMyNvi8qfx8Q5hcthHgHs88FjgfuDXynu2NohkLR3UepFMQxmi02oTNJ6uYMkErbJAWYVmZVgpAatIF5gr6xqmmZJRMS8lXrcR3IdigdWV46fNpNybVfge4Hngfgl4PnD19I7dMIwp7AFviMvzCBbaJ4H7DOCTwW+Hl9WC0xfLbolZ0SbEmFdTwIgTYMZ4m/fKGnP5dfLelbHCVkbAeqyv1lgvPUOtCFhyG8blNp+TOPRYLz1IUT5z6liut4D7aeBHCS7CerSiYRhnz9uAZwE/DzwM3JOATwXuUVpdIhqdMWRRoHpj31F0JIHEU1XD993yaTKrsxaylYqHrYSAtcRLJ21Q1jarxUsyDS8Dt0XLS6fNt2Jek8o/yffgPeCeCfwQ8PLTOnjDMObKHsHN+EXAw4Eng/s8YLO0gmp3omQU9lloQCFe3pWidUwuXn1EntG5noXB5Y8aPyshYBXJnPcN1yFqoLILKfGSpHGZbtZhPc6rVdusc6EeA78D7inA/17MMRuGcQq8BPgS4OeAbwH3j7pJFTDj2E6Fpy1eRy4Il56FIVlh1eJYASts9ALW5zpEZR66nDK/Qy4PddHnJA0RLREu7TasxavlNpT/z6vAfSfwTCwpwzDGwBHwG8ALgC8D9x9ImYu1NYZ63NrW8S4RMKmBeAO44eMEri5sJ0vMZTfjSrkSRy9gkUmuwzRg2cXpUCjdh5dQqfL0i5dO2Oj4twF+HdzXAC9bwAEbhrFYrgL/Dfgj4KngPqJrjekCwbrtra0nbX0l8XJZwGohq+NjOiNy1IxawBoZPTLXzhrdWVV1nUNd0/ASwRqrxasuDdU72eQu8F3gvp0wMZBhGOPlhcDjgG8H90Xg18NuaYv60uWhm7yRxIsgVIeUSxIysnuxGQ8bqxU2agGLFNYXcfAgKvbl1GSUtGsdXnB5kHJdUX5SzIt3gPsq4CcXdbSGYZw57wC+Anhp7Liea1tjntwGFxYYZdyrJV6HBKvsUIsYZZbi6K2w0QpYZX0V4zB8ZX35KnmD7pQo9SDlOubVFK/XgftC4PdO8TgNw1hODoCnEdLvnwbujn6X4jo94kVbvA7icujDOllpLouYLj4MI7XCRitgEW19yViMomQU3XFfrYkpWxXl+4TLAbwc3BdjWYaGser8DHAX8CPg7lmKio6J4XLFjZYLUQvXPnDg4MCH9aFXlliV0CHCOErWpr9keDSsr07qPGqOL7L1pWdW1tOhFAkbrjF/j/o/vArcZ2HiZRhG4FeBJxJCCnFX0S7R9QzpYT3nCGGMYpZ38Qr52LF25RjUNKeYWkbHKAUs0rS+yGnzqeqGU+5DX10kdBM2Jg5SfhO4f0moX2gYhiH8NvDlwNUJQ3voitg2WcR0J7vVuZ4UkweaiW2DZnQuxFbmIY2Byy4LmCRv1Iu+MPTF0Rvzehe4LwH+4HQP0TCMgfLzhClb/juhgYlIGwU5seOY0OaIG1HiXfs+uBB3CUVBZNn3sO/CsKAbPmYl0oiFneoBLpjRW2C19UUe+yXuw8KFGJdzrjFI2ZXiVUx7cAR8I2FAo2EYRh/PAL6vbYU5ymlX6ji9tFO6vSo62r6cuqlZ5X5MVthYBUxfHNr6WidaX5QXhTbRzxGsMm15Fb5lGtbXj4D7wcUcm2EYA+YY+GZCYYO4qxaxotACZXslCWfnXdl2SVu1RZzxPSasSSd+lLGwUQlYK3nDhUXGfxWVN6h6Na70KetpUSaO9fpzcN+4gOMzDGMc7AJfDby+P6kjjVdFTbJLbpuk8IJur1JVIN+ea2x0jErAIq3A6BrhB92ka4ElN6IvLwQtXq24FwB3gnsyYcJJwzCMWXk58HWEwFaklTHdKXlH7njr9quwwMgzP8s0LqN0I45RwATnVfUNVHaPuAdd14W447oloiaO93oGofaZYRjGSfl54LkTshJrK4wc2tDiteN6xqmOPaV+NAJWuw+9ch/6hjlOCHh2LgTfuAjoCYa+AtxTF3BshmGMkxvAUwieHLU7iVhlhenarbU7cduVhcU3CLGwNR+WUQmXMBoBi6QehssXgLgP0/gvlCmu/MraBJ86r9cB8C2EmmeGYRg3y8uA784POwkdTiWg0WjDyBmIuv3ajEkco3Yjjk3AhOQ+1AkccfyXjoFJHcRavKa6Dn8X3C8v/LAMwxgjP0Io/Kt26VhYHQbR7kRtkSXvUWzvNtT7azfiKBijgDXdh14FQn35g+uLoGV9yYWUfvgbhB7T7mKPyzCMkfIW4On5YSszsTU+rCNesXO+6VUbFttBV4dZTvN4FsUoBMx3f3DIoiMiVvdeisV1La/e2NcLwP32aR+UYRgrxc8Ary7bMlmnTGrKdqzTlvkqBkZMp3dmgS09Tm3UGYi6Ar0s2vqSH35SqShH+EyeTqi8YRiGMS/uBH683NUaEtQUMZc74sUkuy5aYOQ2cTTiBeMSMMgiI4OXJQbWsr50D2aa9ZX4a3B/uJhjMQxjxXge8NZGx5l+V6IMXC7WKOtLFpeT25IVNnRBG5uA4csfKaWhTnAhdkxu2uWiHMCvAW9e5AEZhrEyvITmuFJtgU1yJaa2TA0bknFgaw2xGrR4wQgErC/+pcqoSBJHSkN15XqDGcVrD9xzF3NYhmGsKL9UPuy1wqpOuV5vtJI4oFMXcfAMXsAinfgXDb+xyz9u09wmC17zR34F8McLOBjDMFaX3wTe2W1/OlZYIzmtEDHKDrl2IY6GsQiYkMTLqd6KL3slrR+6iH31TUPwm4RK0oZhGKfFnYCKs+s2qNMxV5U6tMtQt2e6lFQrnX7QjEnAXPzjXM5A1BaY9hu3hKtZrFc+24P7X4s4CsMwVpoj4He6u3XHvPYu1W1b0ab5tnCNwo04JgFrxcPEJainJ9A/di1evfGvNwCvXtiRGIaxyvw1cDU/TELju+KlRSwtTiVvqNdLgYdCuIZskY1FwOofOP3IrqzGoX3GRZaOen0z/vUy4I0LORTDMFadvwdeW+6aJbU+ddLFA+Urr9LYMhHHImCgXIg600b9kOvkWZW11VVkHvZVbf574PpCDsMwjFXnTcAbe8IZaqktrLUqdFJ0yl2/iA2WQQtY/UMo07i2wtbVD12b231uQ91r4RWnfTCGYRiRI+CV3d11++aUhdURLv0aN6FzPmQGLWCRjj+3cgNKCZU+4WrFvgoOwb3mNI/AMAyj4u/yZp2JqPdJ1aFaxJxatxiFmI1BwBKNLJvWDzpJuOqLBMDtEZI4DMMwFsXf9z/V6aDX+ya8ZlSMRcAKwWlYYS3BWquebyZvABwAbz3Nb28YhlHRSBrri4el7UYSWtGmtbIQh8xYBAy61pes+zJ3tOnd93kO4JAwuNAwDGNRvL29u9VeTbLGpr130IxJwAoaY8ImmdW91hcEAbMMRMMwFsl7eva7CW1VH2OyujSjErCGeXxiE7vxmIPT+bqGYRi97E9/SW/n3HXbu1EyKgFT9PmKJ5rYfb2Uw9P4hoZhGBOwjvN0xipgN2My977e3+J3MQzDOCnW7kxntAIWaZnSfYthGIYxIMYuYIZhGMZIMQEzDMMwBokJmGEYhjFITMAMwzCMQWICZhiGYQwSEzDDMAxjkJiAGYZhGIPEBMwwDMMYJCZghmEYxiAxATMMwzAGiQmYYRiGMUhMwAzDMIxBYgJmGIZhDBITMMMwDGOQmIAZhmEYg8QEzDAMwxgkJmCGYRjGIDEBMwzDMAaJCZhhGIYxSEzADMMwjEFiAmYYhmEMEhMwwzAMY5CYgBmGYRiDxATMMAzDGCQmYIZhGMYg2TjrL2AYY2QtLi4+9sBxXAzDmA8mYIZxi+wADwLeD7gv8EDgXsBF4AJBvK4BdwFvBF4JvB54NfB3wNHiv7JhjAITMMO4CTaARwKfAfxDgmi97wne78kC9ifAs4GXYWJmGCfBBMwwTsC9gccDXwg8DNiunvdBm6biwN0fuD/wScBXA38M/BjwG8A75vR9DWPMmIAZxgxcBr4M+BzgEXGfBz+DWnlyKEzvLN56HtzHAh8L/G/gGcCzgMNb+dKGMXIsC9EwJrAOfBrwO8B3EMQrCpcWIF8tx2ppPdaLfICX5VHAjwC/BTz2VI/OMIaNCZhh9HA78FRCfOrD6QhXLURaqGrR6ls8pajJB3vAfzTwy8A3AedO8TgNY6iYgBlGg4cDvwJ8JbA5WbhqUTqacekTsULIbgP/LcDPEDIcDcPIWAzMMCo+CngmIcGix1Wot/sEqBAjchzMzbAU7/HgPw3cfYEnAi+91QM0jJFgFphhKD4F+EU64tWyusSSuiGLCzkXh8BBY9mvHstrb9C1yppuxUfE7yZJJIax6pgFZhiRxxCy/+4oswsnxbqOHRz5uO2zCLUSNbSFtaaW9eqxXjTOg/9AcD9NSOV/+RyP3TCGiAmYYQAPJaSt34fC79dneYlwHfnSEkvP07WitHitq/VGXOtF3rem3uOJIvZgcD8BPA5429zPhGEMBxMwY+W5B/A9wP3odRt2EjS8ch2SXYGFO9Cp5A8XxEdbXSJem3GtF3me+PpjVAzNg38kuO8E/g2wN+fzYRhDwQTMWHm+njCAeJp4Objhs2DppYhpxdcd+2yF4UvrS4RqE9iq1sdxrb+LiJlDWWKfD+5Pge+f+xkxjGFgAmasNB8H/LuwOUm8bkSXoRasfWDfwYEvEzQO4+uOXBAx+VyxvpJ4OdjyoRrVNkEYt8mitxnft67Wx6jY2Dr4bwb3fEJNRcNYNUzAjJXlMvDNlOZOpIh5KfFKwkXw3O36sJZFZxje0BYYwXoS62tLiddOXHRG4qT4md7HHcA3Al+MFQI2Vg8TMGNl+TzCmK8JrkNxG4p47QF7LgjXLnA9LrtxEXE7ICd1SPxKBGyTKF4OdnwosnGOaLVRil4fYoU5D/4J4H4KeP4tnQ3DGB4mYMZKcgn40nJXc5yXinmJ1XXdB9G65uCqD1N9iZDtkQXskHIOS+0+3Aa2o3idJwten4BJNqIsHjXg+Rzwr4HfVv/MMFYBEzBjJXkcuTCv2l2XhjpCiVe0vK4DVwjidQW4ShCxa2QrTATsiDIGJhaYuA7PxdeL+7B2OeoxY45SyNLrPPjHgfsI4EVzODeGMRRMwIyV5Inlw854r0bSxl4Ur6txuSsuWsR2ybGwWQTsPGXsS/5/PdhZL81Y2Cbw+ZiAGauFCZixcnwYubq82l1kHqpxXpK4sQdcd3AtWl53Ae+JaxGw64TMxH1C7GyagInrUKwvyLEyPVZMjw3T48IKd+LHEGaFfsOtnyLDGAQmYMbK8VhC9l5UFp3tV9c51Mkbu4T411WygGkr7Fp0Me75MqbVErAtsutQ4l76NXqcWD3QWafTJ/Hy4B8E7h9iAmasDiZgxkqxBfzj9lN1xQ1J3hALTLIOr5HdiFdkcd0YmKTfFwLmYMOHr1EIXKzUoatzbFXLIVnEmrUWN+OxPfcWzo9hDAkTMGOluBvwEfS6D73LRXm1gCULjDID8SpwNboVrxESPfZ9YxyYgzWfMxF1xiFk8RLhkhT7nfh523G/WIZSL7HDY+KHHN7ymTKM5ccEzFgp7k2YGLJyH6bFq+ob5BhYbYVJKn3adjGBw6sEDldmFa4RREzqJB77vF+L1w7RFRnFKw2Mpnyv/t4ppf7hhAHa75zfKTOMpcUEzFgpHtj/VO1ClBhYswIHsOuCYO0R1vvAgYvxLx+nWXGVgIn4VOIlY8N0VY99X6bYpyLBvus+lAPwO3HiSxMwYxWwCS2NleID27s7afSU06RoK0xiXPs+1kKkO0FlsYhLUooBy8SXLgue/uyirqL6nL5pWtDbG8CDbvrsGMawMAvMWCnu1d2VMhBd6ULUyRx1BfokWD4IUUuwjsWFGP17zsdtH1x+N2Lcq1XdvhZCPWNzx+2JciG69jEaxigxATNWikuzvayuyFFYZC6XmZJMwyPK6vMiXkVlJ9d9ThJG+pZkcam5xTquQ/WdHcDFGc+FYQwdEzBjpehp3H3803LNFYu20nQyhQiM64pM8XnV64/V52n3ZRJQ+VzfFa1mFuKEYzSM0WExMMNQLrgTvH6W9zRFx00QH8MwZscEzFgpdic850phatYj9I3tOMbLuVwVQ1eOL/5FHLCcnvfdIr263uGavNY1PutmjtEwxoQJmLFSTEgv18JSC9c6sOHKkk6ptJMPFTakTuGaKwWsWESwfP7cou6h69Y+XKcUzj7rL+2zFHpjVTABM1aKN3V3uWpbC1hRl9DHwcYuDDjeVMuGV6Ljs5i5xmeuOSVerqx5uOlh05X1D3UR39pa64iZbx+jYYwSS+IwVoq/be9uCU2rLqFMRCnV5LcJ47a2UOnzlBU4dKHeNYLAbRCsrSSK5HJRWz7/PxE2bY21hCttHwGvOvFZMYxhYgJmrBQvIzTy0fWgpyPRAlZXhBeBkUkozxFCTedQA46dqr7h8+eLuGhX5KYPyzZ52amWbRfETFekr+cFK6wvB+5dwKvncaIMYwCYgBkrxduAv6OoyKFFTFtf6wR33pbvitd5yokrbxDFi5zy7lzeJ8V864K92z5/pnzuOUIR3x2frTLtUpzoQvxzLInDWB1MwIyV4j3AC4EHx8oYcXfLhShxL7G+ztEu9VQU2qX8TJl8EkLsa0NZXjsiXg4ueLhAXs7H58RNKQJWx8M6/EH8MoaxClgSh7FSHAF/RDEQS6fNO4LQ1PEvceudJ4qMC+OFLwIXXRQdlOhE62rLxcWXltwOcD6+7yLlOolYfJ3Ew0TAahdissKuEsTZMFYFs8CMleN3gdcRplWJ6DiV98qFSJ588hyqbqFv1D5EWV8x7f1QuRDXYzxLrDmxui76UOHqInldiCFlMkctXnIA7q+Al8zlDBnGMDABM1aOVxNcbU8s3YhQuhE3COIjE0nWVeZbNQt1Aod8xhGAz8kbO2SL61K1XHRB0C4wmwuxELFfA949lzNkGMPABMxYSZ4OfA5BGSjjSWsEURM3oi7kO63wbj3P12F8DkL8a8vBuShQlwhzT152cNmH7Uu+646UJI6WeCXeDvyPWzwnhjE0TMCMleRPgecDn5qtsLqElBaxLdrV6fW6tr7WCfN9HcXqG/I554jWlwiXiBcqnhaTOOosxGbsy4H7SeD18z9NhrHUmIAZK8kh8FTgo+lUb68FTFeJ7ywqVf7Yl/N9rQMHMT4mArbtg3V1kWBtXUaJFyEuJtaXiJdOn9cJHInXEixKw1g1TMCMleUPgWcD/7JrhYHK0HXdubiSFeZLQdMW2CYh5V7S6CWB43yMc2nxSgJGzj6skzeasS8H7un0VhgxjFFjAmasLEfANwMfC9y/K2IiYD6WfmoJWG2FSUKIDILe9w0B890EjpZ4bcWkD506X8S/HLgXAN8z17NiGMPBBMxYad4AfAPwYwS/nUJnJEJXwJKQKfESAVwniM8BpQtxJyZxaAFrihdVgWD1fRJvBb4WuD6PE2EYA8QEzFh5ng08DPhPpRWmy0tBGQ/biusjlwXsuHrPJkHACgvM5yQOWeoxXzppo5V16ACOwP0n4EVzPheGMSRMwIyVxwPfBjwQ+Oz+eFid1OEpxetYvX6dIEQiYCmJg1zzUKyuOmmjFq9m3OupwI/O8RwYxhAxATMMQmXeLyOox+NnT+rQlpceEC1lqA4pBUzS6EXEJolXb9LG9wH/eX6HbhiDxQTMMCJ3Al9KUIpPb48Pc+Sq8nUyh48vcj4L2A1KAdsk10IUIZvmNizE6weAryEoo2GsOlbM1zAU7wD+BfBD4WFHvCinWxGrKk234rN7sFMmijJhoxAv17W8itjXAbhvBr6KkJtvGIZZYIbR4S6CO/EVhMSO29uVOup4GOr5DfI0KykzUaZTIQtXK+Ow4zZ8I7ivA37qtA7YMAaKCZhhNPDAdxFKTn0H8BHg1sNT2mvhVfkoIc0lRkihT5mJPlfVEJdhPVFlYXUdAs8H9w3A/5n7ERrG8DEXomFM4AXAJ4H7sXK3iMx6rDIvgrVNOXuzTtSQVHk9x1dv0sYNcN8BPB4TL8PowwTMMKZwDfizblJFqo5RiZiOiemEDdmuaxw2kzYOgBcEITMMowdzIRrGDKgbRafWS9UNontQhE1PcCkuxDoJRK+bRXo3T+E4DGNMmIAZxsnR9RKPKUs9HQNrVQFg/fpasJqVNlwlZoZhdDEBM4wZcTRncJYqHfJYEjZa2YnaEuuM88LEyzBOhAmYYZwAJWK6XiJkIZP9vv325iLPmXgZxgkwATOME1KJWPVUR7zq13VEq/E5hmHMgAmYYdwElTuxJWTQHQBdP+/UDhMxwzghJmCGcZNMEbG+fZ39Jl6GcXOYgBnGLSDiM0XIpr7fMIyTYwJmGHOgIWRTX2sYxq1hAmYYc8TEyTAWh5WSMgzDMAaJCZhhGIYxSEzADMMwjEFiAmYYhmEMEhMwwzAMY5CYgBmGYRiDxATMMAzDGCQmYIZhGMYgMQEzDMMwBokJmGEYhjFITMAMwzCMQWICZhiGYQwSEzDDMAxjkJiAGYZhGIPEBMwwDMMYJCZghmEYxiAxATMMwzAGiQmYYRiGMUhMwAzDMIxBYgJmGIZhDBITMMMwDGOQmIAZxgwcLfj/+TP4n4YxNEzADGMGrrFYQTkEri/w/xnGEDEBM4wZeAOwv8D/dw140wL/n2EMERMww5iBlwJXF/j/3g68foH/zzCGiAmYYczAO4C/XOD/+yPgYIH/zzCGiAmYYczIzyzo/xwDP7eg/2UYQ8YEzDBm5LeBly3g//wu8BcL+D+GMXRMwAxjRt4AfD/BQjotdoGnEpI4DMOYjAmYYZyAHwd+6xQ//5mn/PmGMSZMwAzjBFwHvgJ4+Sl89u8D3wjcOIXPNowxYgJmGCfklcATgb+b42e+EPgi4M45fqZhjB0TMMO4Cf4MeDzwe3P4rJ8HPhN49Rw+yzBWCRMww7hJXgo8Afh/uDnL6fXAkwiW11vm+L0MY1XYOOsvYBhD5h3AU4BnAV8CfDzwEOBCz+vvJKTiPw/4CUy4DONWMAEzjDnwSuDrgDuADwYeANwXuDsh7f4dwOsIcbO/Bq6czdc0jFFhAmYYc+QdhLjY753t1zCMlcBiYIZhGMYgMQEzDMMwBokJmGEYhjFITMAMwzCMQWICZhiGYQwSEzDDMAxjkJiAGYZhGIPEBMwwDMMYJCZghmEYxiAxATMMwzAGiQmYYRiGMUjGLmDeg9frCYthGIYxIEYrYP7komQiZhiGMSDGKmC1GCVLy02wvPpEb/00vqFhGMYEbKqQ6YzqHPmuKOnHLVdi/ZrWY7ZO5dsahmH0Y+3OdMZqgeG6AtVZ3GQhS2wBm6f6bQ3DMEouTn9Jb9vWiP2PkjEJWG1FyXrqj0z7B077N4G7ncpXNgzDaHNHz/6bEaWbyAkYBGMRsEK8XFecjtW6XvTzzQtjE7j9NL+9YRhGxXu3d0/qbE+M8fe8d9CMRcCAwm0IpWD1iZd+vnYnps/ZAd73VL+5YRhGyf26u/qS09L2tBi/z68ZBWMQsOLHaFhf3mWhOlLLJCEr2AZvAmYYxiJ5//6n+mL5s8b7R8OgBcz1/yCF+9Bn8eoTsb4fHggn6UGncwiGYRhNPihvtrxDOpbf8jLp0EiLUQjaoAWswot5rHobx64SL1cKmBa1Y9qZiQA8ABsPZhjGYrgM3Ke7uyNebnp837vQiR+lFTYWAUs/ihYvsvWVLC/fFbDCndiXmfggeoOqhmEYc+X+wPv1J2LU3qW0uIaQSZsmsa8JnqvBMRYBA5pjv+rY1w21tESsNwD6YODeizgIwzBWng8A7lnu6gtzFGER6aC7UtBSO9YQr0GL2ZgEzMc/KYkj/lhavAoRc6WQTUrm8DvgP3xRR2IYxsrigEdTNM61h0l3zOu4vhay2kLzqn2k+sxBMiYBgyr+pd2HrmuBHfq2NdZK6gDgUxZ4IIZhrCY7wCd0d2s34Em8S8cuLL4xzGiwwiWMRcBaPYravC7Eq1rLjz4xpf4jMTeiYRiny8MJIYuIbofqhA3dMZ8oYj4ntdVCNmgGL2B13EtlIkrP45hsUh+6IFqy3Ij7ahHTKajpArod/P+1yIMzDGPl+AxgvRHGoCFePodCdGe82SlvuQ+HzuAFrEb1MLwPP1phgfksXgfAgStdibrn0knoWAM+Fdhe6BEZhrEqvDfwT8pdWrgKr5LPbZoIWBIxHd/30fKCzqDnwTM2ASsSOVQmzhHhRy3EiyBe+ocv/MY0fuyPBj5kgQdkGMbq8NHAQ/pT51shkcPG0rHClIjVnztoxiZgEHsZTlXgqCwsLWL7KDFDCZnvxsI8wG3gP3fBB2QYxvjZAJ5ImgeslS4vS6ctc+U6tWM6lKIztBnJmLAxCVj6Yeo4GLHH4kvrq7V0LDFKC8wDfAFw30UdlWEYK8EjgU/piXvpdoxuR/zAx864z54lcS9KZ9yPLYEDRiJg9Y8iYyUkBqYydeRH36+WWsDqAGgnmeNJCzguwzBWAwf8e4pydUXsy7cFbKaOuMtt4ajiXzASAauog5WSyCHBTf1DFyLmTmCF/d/ABy/qiAzDGDWfDHxilVGtllbcK7Vfrt0RL8IhPePABs/YBCxdAGrcQ2GB+erHB/bisu8bPZiqtlj60e8N/qsXdFCGYYyX88DXApfCw77Yl/Yi1Z3vPUoROyR01ouyUmaBLTHVeLCmG1EFOvddFi758WWtRSyVZKF7YfH54B+3sCM0DGOMfAnw0VPGfKHEy3U737odE0/SDR9ELLkP62r0Y4iHjUbAKpIbUQ1klh/00MegpxKxXaqLgGyGiwXXqdCxAXw9cMeCD84wjHHwIODJ+WHL+qrj95Kwkdour9ouF56Xgg3JAhvjVCowTgFrZiMSfkidibjvuz2Ypoj5/gkweRT4b1jo4RmGMQa2gG8F7tdvfdVxr2R5udzxls73PjkMouP4rfJ4oxGyUQnYhGzEVHaFrhtxF9h1jYuBaIrTLTNVXAhPAv/4RRygYRij4cuBJ7QTN+oByzruJVaXtFdFuyWJaL5/PCswDvchjEzAKjp+ZFUz7MCXpviuh+uUF0QnHkbPvGFbwH8DHrqwQzMMY8j8E+Ab+8UrVQ9CiVeMfUn7VLdXOhGtCH34UrxGIVzCWAUsXRDaCkOZ406JF/liuA5cr6yxIh5Gjyvx/uCfBtxtQQdoGMYwuR/wNODu5e6is+27rsM9X4qXLCl+H9u0NJZVEtDGVv9QMzoBa815o5M5nLLA6IpYcWG4MiZW10zsuBI/DvwPAxdP/SgNwxgi9wKeBTx4sutQxKse7tNpp1wWsX2fS0lJCn1n+A+Mx30IIZFurOgLRMZBaL/yAaHXsgVsRxfidtyW9RawSThPGwTBl8Wp/yUdAfdZ4N8F7svjPzEMwwC4Dfhh4NFd8epMkUIZ9xLhugZcc3DNh+3r0Srbc1HAYpJan/U1GuESRilgLmQfisCki8TlQpiH0QLb9Llns10ttXit0xUwWfSF4f4V+LvAfVP8YMMwVpvLwNOBT50gXpTipa0ubXkl8UK5EL1yH7oVSN4QRilgiqYV5sqLZJNwEWw1ls24bDhY91nERLg02h3rvhr87eC+Erh6KodmGMYQeB+CeD2u7TasXYedjEOi5UVoSq6p5TrdYT8rY33B+AVM6LtQ1gk//gZZrLaq9Saw4UsLzKHchur/FCL2ReAvg/s3wDtP57gMw1hi3h/4ceCxs8W8tOW154KLUAvWVbKISbKZWF8HxIkslfVVxL/GZn3BiAVMuRFrK8z5UsREwHbJgrXpgntRuw9lqS2wiZbYZ4J/b3BPBl4830M0DGOJ+Wjge4EPvrmxXtd9jntdBa5QWmAp/kU5s7yuHDRq6wtGmIWoaWUkksdZ6ItGZ/hcA676fNHIhZN6PnTHifVVr/cAHwX+ueC/gK7aGYYxLjYJ5aGeA/4E4lVnGl5zud3RbdBVlPVFt+BCPe4LGKf1BSO2wCrkxzt2QUOOAKdEbF1ZWxsuuwzXXdt92IqB1RRuxvsAPwb+Q8H9V+Ctczw4wzCWgwcCTwE+r7/z7OlmQ+tsQ4l51Z3oZIG5PMxHJrEsSke5FbG+YAUErMpIhPDjEt2Ia4Qff83Bmg+PJVljnTJxYxYB0xfLWrVmLbgS/ceD+y/AcwhXrmEYw+Y8Yab2/wC8f7fqhc42lEzoPg/QVeCqCwJ2l4O7PFxxcCUK2jVfJm8UiRs0xGus1hesgIAp6h6RjLtwXokXWbDquNdJxMtTTK5axsweBv6ngedEa+xPb/XIDMM4Mz4R+BrgE9pW1zS3YT3O6ypBrO4iihdKvCirb6TYVyN1fiVYCQFrJXS4fEG5eBE4ZYWJgK0RrDPnp7sO+1JktQAWX+szwD+aIGTfB7yCaB4ahrHUbAAfDnwl8Ckh21io24FinBeTY+/iKryrWnQCh1hfdfy96Tocs/UFKyJgDVJMLJrejuyPdrVgTRCv2kWg1z4Kp4iYpzGG7J7gvgz8vwB+HtxzgN8jXLGGYSwXl4CPAz4f+HTwqgHtS9ZoVdjotbyA98jisgVWiJfLZaNuUJaNWinxghUSsIYVBjGtPl5c8kQSq0rIoBSw1oVaCFk06TfoF7H0eefB/Qvwnwv8ObhfAX4ReB2hu2UYxtlwHngw8ATgY4EPBb+Vn+7zvBSzKfuu5bVHV7zuIohX4T4kxMSukyevLKZMcSvoOhSc98M9ZudOnpTuSzFyRLehy5mIMoh5BzgHnHdw0YcavZcIVWFui+vLDi7H5y4CFwjX+w65JJVU8kiZjfJ/KYWsI5K7wIvB/SbwF8ArgddigmYYp8lFwgDkDwAeRYhxfRD4zfJl04TrWFldnQkpUeIlCRtUAkYUsPj8dRcELFXdiIOWawG7KetrqDqwMhaY0LDEjtUD50q3IcTnatON0so6cnE7XrTi7z4iipiyxo7pt8YKRT4H7iPBf2R802uA1wOvBfcK4FXx8duAO8k+iWFeioaxGByhZ3oOeC/gDsIUJw8APhD8PwDuC/yD7lt9Y62Fy5PdedplWM+mfD0K0lUfEjRS3Eu5De8iZx1KxY00P+E8xWvIrJyAVXSSOkSsRLCiG1Fe2OlpiXD58qLV5WGOCBbdEbk4cKuyR6vCh3wd1oD7g7t/+b0Nwzgd6nusT7w6wkU3WUO7DXd9rKbho+tQi5avxnyhCie4mLThy6SNlRUvWFEB6xkbtoayxioR01dFIWC+vGD1cqi2d+K6VeF+0jizQsSqbcMwTgff2O4TrlaWYV0aSrsN67jXFd8drHzVZctLi1dKmaeck3BlWUkBg44rUdbHUO5U4uUpMwu1n7vudelFTH6ZZ0wq3W+oZb1K4W8lerQSSQzDmD+1gLUW8b7ocIG+/w9itqCusKHFK5WJcnmcV6q04dVg5YZ4afFM33fVrC9YYQGDiZmJTRFzjUDtBPE60IsPLvcDyuQOWSSBRI8/ayV6QClgJmaGMR/6rK5icTlc0PK+6Pt+3yu3IWpCSkoBk5JReoqUXRdjXj3ipTMOV1a8YMUFDGYXMaLlJdv0D1AsLuJq2VFLZ84x+qt/9FXANwEzjPnQtLpqj4sv7/lWx1W7DMVtKPUNpVRU39xe1wnCVQxU7hGvlY17aVZewGA2EVPPFb0wuj2wOnArF/N5cvLTHlHEomtRi9issbFCvJyJmWGcCD85UcOTK7vrOoYtq0uEq56IsnYdaitMP5bX7bmyQO+hL6dHMfGqMAGLTBAxrywwuegLV6IEVX0pYJIyq10J59SyQzsuNs0aa7oUV/oqNoxbo9dlSBYt3Vk9ckFc6vu9KV4uZx5e14uLr1Htw4FXMyu7OD0KZcKGiZfCBEzREDFPEI4jcR/67FbQcbDUG4sXdu0+lIv5HHmgcxIxShHTA59FyNZoW2PQb3mZRWYYJX0NfkvAWlbXEbn6ex3nFm+LFNpNAuazG1HiW7u+Ei5ZnHIZ+jzOyxI2ejABq2hkJ0q5qU42IlHIVCaiCFkdyBW34TmykOl4mBaxTcLA544lFjMVWzExMMEyjJuhI16NEMGkRC3dUd1zedBxEiqUi5AsXPvEDEMfO76osaO+O84rfVcTr4wJWIO+FHuVhVgHdtNF7rOAHUZ/9r4PF/aehx0H53xpgfVZYTomtkHIVKwTO+LXLdaGYcxGRxh8KV51jHtSotYe4V4XodrzOa4lj+V1OjtZLDpxTTara2Di1cQErIeemBio5A5yZmK62KP5f+jDRaktsX1CzGuXtvUlAlan2CcrzHVFzKwww7g5fLWdOqXKAmsVKShch2qsVxKxuJaEDBGuA52goS2uRqJGc0ZlE68uJmATUCIGjeQOny/8OpnjhqsEjOAu2AJ2vBIt103kSAIW42CbBAHb8JOzEhtf3zAMRUsAtIUj97VO3Kgr7EiShVhQ+v5OYhZdg5KZmGoY+kq45P8ol2EnWQNMvPowAZuCXDiN5A5XxcF0762OiR0SLugtwkWdxMrn7a0ocFs+x8FayRypmr2jKDpsgmUYJ0O7DovkDUfvQOXeYgWUrsEkdMojo+fvkgLgR06JJ5jL8CSs3HQqt4LvuuvqZY2QaCHuPknASOnxLiZouCxUOuYljzfIFtgsKfVgAmYYJ6WTwEF/9qGIj4zPSkKmUuoP9D6UaJEzC8VT03IVnpnVNVQdMAE7IQ0Rk7UjWERiGa2T5xnTmYSbZGESYdukHAMm2yJ+fdU5gDQFjGEYJ8Q3Bi/Tb4WlNHpKq6wu3q2Xo1q4VNy8Fq0zcxkOVQdMwG6SWYQMtcQUeJ2MIcJUZBrqbS1+riz2a1mIhnHr1MKhXXmtYt3i+pP4lXYJpn2VYCVraxmFSxiqDpiA3SI9bsW0LRYZWXxSLMvnx1rYtHCJBVcnb6zREC6zxAxjNnwpFC0rTI8H05U4kmtRxE2J3LESL13ooBYuqu0zj3UNVQcsieMWmZCpCHnAs1MX8bHPSRgSL9PWVSFYvrTiJGmjlX3ohnkJGsaZUYuYJE4c+yqtnrKYrwyZqd2N+nkthL3CFf+f3bo3iQnYHOjJVNQkISNW9iBaUbHH5pyysJSgaWtrzat4l4imWV2Gcev4LF46IzGJkM/btXWWspBdtrZaSRkmXKeACdgc6REyES5ZiwjJ8ymjUARKuR3TfvJzhetQ3QEmZIZxMjoek3iTJhFzal3tO673q+fkszsxLjDhmicmYKdAJWRxMyFCFl+aqt3XrsFkaYlw+fw62TDRMow54fN9W1tjNISs2Jc/orlGvd6YIyZgp8gEIautMghCVax997kiaSS+2UTMMG4B3xCWlii5ag2djimYaC0Uy0JcML4tOHUmY9++vseGYcyHjlvxhNvA8IRrqDpgFtiC0Rf2FBejL98268cbhjEDs7bYzaSs1guHJlpjwATsDKkv+B5B04+nCZTdQIYxH6beSyZYZ48J2BLRuiEql6PdMIaxYEyolpdBx8AMwzCM1WVt+ksMwzAMY/kwATMMwzAGiQmYYRiGMUj+f+PJfPecaqpKAAAAAElFTkSuQmCC\"\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,iVBORw0KGgoAAAANSUhEUgAAAQwAAAAgCAYAAADnlUZqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAK1ElEQVR4Xu1dDXAcZRm+NOAfKog6WO0QcreX3O71R41oHdSqqDAOg3+cYEXBolXRTEn220taKTc64mgBqzBiEUVpBdqiwwhqSdIS2upYSgvRtpTSckljWzHagjpSRdr4vLtvjrvk27vdvd1Ljn7PzDN3t/d+7/t+f8/+78aK0NDaar2qOdXZoqWyH9R0a0Fct67WdHGTZojVCcPqSejW1oQuHsOy/eBTsDmM/54ZT9j+LWGIg7DfB/sBcDPsf4XfP8X3b2uG1ZHQzU8mUuKdyWTHm5qaci/jHAKByif0bBr+LwaXIPYPkMdqfL8XdWpls1AA31/QjOw98L8S9b8BXIR2+nDc6Dozlsk0slnkQMxkPGXO9EJtVnYGF4sUyVnd8UTaep8bw+6LakBj5izdbNJS1rxEWnyWxg36EmPdWoPPDejf7eATGMsHaDzTuC6hbj0N/pXmAsrugs0WLP8NuBJjZJmWElcl09mPJ1JmW0tL5+uiHBuGkXsljX87ni4EzVnk9AvksQn57ESdhrB8BMuPjOWP//4OHsR/e7D8YdTlftRhFfgdLG9Hu1wAfzr55jAOkiQKhvVbGB6C0//i+2iNeRx8FgnvRfxfainzSk7NE0iIUPbf43wWmNTNd7BpKEA7LZfFAY9zp3yZTSMDiQVi/U+Sg5QYAIfOmG2ewsUjA/rhW7L4Bermj9h0UoB2OB+TZTW4B/k8OyG/yCiOoW1IYH6H8XPz9LbcKzilQGhpMZvhZyHGwG3g42Bk85Z8o90G8X0NiSs1Iv2QGk8KdWszt4snIP8RqR9mDQXDIdZSbBoZ0Il3S2OXZXYpF48MU14wnK1beW41pL3FEQCJlPVWtDG2fuyVrNR3tBTdSjB8YrIFoyVtno2OCzBgxDNBB6pXKMHwxiD9gK3Kc6PckvBGJRi+McmC0YD4fdK4Xoh9W/YTCZRgeKNvwchkGtG2e2W+akslGL4xmYJBaxlpTI+kNRQdmGR3oUMJhjf6FQw6cCrzU3tCMLDWuQsd3R+Aw3KnBQ5KynjhjdxOnnDiCEZuGjrsYWlMJtpiWUK3BmT/FfEudhg6UPe6Fgz0bR6fa6MmnY3klDwhaYjLUU6es27t0gzzm7VgUu96D6fkHxCa62UVGCMq8g02jRQnimBoRvYiaTwm2ntfW9vCk7W0dYHs/wJ163k6eMZuQ0W9CwbG9K1sOqWAvIU0X5tiDZtNbSjBcGEEgtHWdsvJ8E2nAuUxibp5hWM92oDf2yb8X0Kx3rENF0owogHm0hJpvjaVYPjCiSAYibT1eWksJibCk/Pm5U5ic8rxQpldMRPp7HlsHhqUYEQDJRgh4sUuGHSRD+pIV+TJ4xH1LG9djCHTiMlR4ViG2E7HRbhAKFCCEQ2UYISIF7tgoJ2z0jhMtHOejl2weQFY/lGZfSnFfDYPBUowokHCMBdL87WpBMMXKgqGIS5vTptnh0XU+05ZnAJDFAzD6Dgd/p6WxmHGDfFFNh+H0Qb0waOyMmOE+OUNI/cSLlA16l0w0F6747q4pRpGcdqa7kuR5UtEH45gDmwKi/DZj8/7IES34rOzeaaYzWlUh3oRjJozRMGoOAENa0i2dTGGeEp8TFJmPDvYvGrUu2CEQbqhksOFBsyli2WxasTj6Nd12psXv57TCQYlGC4MSTBaW603oo1db6qzqVtfYnM56ApBw9oxoVwRMYlGNK391VyiKijBiEYwmlPdLbJYtSTa7qHiA+u+oQTDhSEJBtpvhdT/GHWxv9zWxRi0tPiEtHwJxbVsXhWUYEQjGHRwGuOh0gV5kTOeMi/hhPxDCYYLQxCMs1qtVgzu8revpyyPjwHwspVh/SuVWjKdCwSGEoyoBAO5p833op+ek8WsFdF+wa8SVoLhwhAEA37WTPBbRHTcAexGvJTNHfQMNcf6Bs+P9ebnxfqePJWX2kCZzHgfExjCGQIlGNEJBsF+EJEudsvi1obiT5yKf9SNYOjWZjTyfaHRud9AHotYpWA4NxqJY1LfTNT5K2wei60fMiAUD4KjBfbmj8b68stj2w7aD2qhfU/0xy6ZrzHS2qulpTNl+wyIuhcMjBU661QNm2cuPoPDRYTRBjpbR2MAOV9HZzOQ98/w/fYwiPHtfje0bv2Fk/CPehGMOrsOo/Lt67o1XDgVuiE/BwLxjxKxKOXG2M6dti36w8ORdnGP7TcgkFudC8bUvA6jlkikO8+Ttg2IMXSYzfxDCYYLqxAML7evo77ttnF//0nYktghEYlxHLqazJ2tjEqbs9iySWXn2v4DQAlG/aOsYBjWATbzDyUYLgwsGLlpKLtV6pNJHVZ4YHLf/nfJBWICh2HdQEXi6ewlMr8ldJ5HYtv7hRKM+kc5wUD77GUz/1CC4cKAguHp9GdKXMXmEIx8u0QcXPjYa+0ymUwj2utxqe8ioo4X2vY+oQSj/lFhl+SPbOYfSjBcGEAw6HoK7A6Uncio58GmpsteeB1D79BX5eIg4f3Dp3OpGOLMl/kfxx2xzFrfj8VXglH/qLBLsoXN/EMJhgsDCEYiVf72dWbpJdw9+86RisN49g7uh3VhF4PF6QmJ/1Lq1gIu4hmVBAMT9u7x70wJg/TYfU6hLJRgVEaFXZIH2Mw/lGC40KdgzJ5tngKfB6S+mPj/0IwZHS/nIg5GRxshBgNSkSjlYi5RAPruUlmcYmJy/XnG3HExK6DiFkZExBjYyCmURSXBQDuPoA5bo2bSyL6dU/IE3iqUngYNm2gD17N0+G8Vp+QfSjBc6FMw4rplSf0UETFNNi9Fz/DMWG/+iEQkHPbmN8S2bZt4+bhzj0n5J3iBdFs1l/AE1L2uBaNWTOriA5ySJyDv78r81Jyery6WQAmGC30IRtOc3Glop8NSP2PUxVNl1/Tr8q2xvvx68Pkisfgnfl8f6x90fQUl4n5GGq+Yujhy5qzu13CRilCC4Y11KRj0WkgtF/wmRSUYLvQhGF4mGAaLYPPy2Dg0PdYz9H7spsyN9QxUfC0iXfyFPtoni1lMGqxcpCKUYHhj3QkGxCKpW+/mdIJBCYYLPQoGvYQa9uXf71lp66JKlHt8/QsUR+0XTXuAEgxvrA/BoLfr2QfHr/GzlemKKSMYunkHTSzElL4+sFaCgfo+B+7WjOzn2LQsnNcGiD1UTubPodnF5pGAzpggvutWBur6H7tOuriUi5QFXSWKMt/HBN5EayXUr+w9McEpjvGK4vfIbwVdw8IplAWNBZS5DvWhN5Xn4edoqd8oiFyx2wk+iu/0Iuil9KwTTskT4mlxDtrzRm5XjPUo2pXe6G49gjxvw+fChNGhcfhwQC9jaTLEG9xoGFeWviY+UuSm2Q+coXdy6NYiNOwyVPrHGBh3JozuUCseT5mXQfF/jhg/xOfXNd28gjo0aH3pLAlNNGdtL5Yi55vQgbej4+6g/9gsMqAOH3HaSfwEbXcDvmeThvUpTe96y4QzM76Qm9Y0Z9FpdPcm6vNpsAt9stxpO+vX4EbE20oTCcsGSonl+B/f6Wa/VcV50aSPx7tODeEBxg10xy+dkoXgfAgxFiDe19AO30M/rEQO9yLmA4i/Bb+3l+bnkPIHN4PrUL+1+FwB22vhox1if1G81XpbvA25ZjK+r2lxR24a1d8RPzEfuwoWcsEWiJMzYj+I3w+VtKshHgH/APZSnqjTzfi8xh67unUuPdrA28NxYrH/Az3tI4j5+TOLAAAAAElFTkSuQmCC\"\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  \"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  \"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  \"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  \"95442b2e-f15e-4def-b270-efb106facb4e\": {\n    \"name\": \"eWBM eFA310 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  \"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}\n"
  },
  {
    "path": "x/webauthnx/aaguid/passkey-aaguids.json",
    "content": "{\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    \"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    \"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    \"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    \"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    \"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    \"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    \"d548826e-79b4-db40-a3d8-11116f7e8349\": {\n        \"name\": \"Bitwarden\",\n        \"icon_dark\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMywgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9Ikljb24iIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEwMjQgMTAyNDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiMxNzVEREM7fQoJLnN0MXtmaWxsOiNGRkZGRkY7fQo8L3N0eWxlPgo8cmVjdCBpZD0iQmFja2dyb3VuZCIgY2xhc3M9InN0MCIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIvPgo8cGF0aCBpZD0iSWRlbnRpdHkiIGNsYXNzPSJzdDEiIGQ9Ik04MjkuOCwxMjguNmMtNi41LTYuNS0xNC4yLTkuNy0yMy05LjdIMjE3LjJjLTguOSwwLTE2LjUsMy4yLTIzLDkuN3MtOS43LDE0LjItOS43LDIzdjM5My4xCgljMCwyOS4zLDUuNyw1OC40LDE3LjEsODcuM2MxMS40LDI4LjgsMjUuNiw1NC40LDQyLjUsNzYuOGMxNi45LDIyLjMsMzcsNDQuMSw2MC40LDY1LjNzNDUsMzguNyw2NC43LDUyLjcKCWMxOS44LDE0LDQwLjQsMjcuMiw2MS45LDM5LjdzMzYuOCwyMC45LDQ1LjgsMjUuM2M5LDQuNCwxNi4zLDcuOSwyMS43LDEwLjJjNC4xLDIsOC41LDMuMSwxMy4zLDMuMWM0LjgsMCw5LjItMSwxMy4zLTMuMQoJYzUuNS0yLjQsMTIuNy01LjgsMjEuOC0xMC4yYzktNC40LDI0LjMtMTIuOSw0NS44LTI1LjNjMjEuNS0xMi41LDQyLjEtMjUuNyw2MS45LTM5LjdjMTkuOC0xNCw0MS40LTMxLjYsNjQuOC01Mi43CgljMjMuNC0yMS4yLDQzLjUtNDIuOSw2MC40LTY1LjNjMTYuOS0yMi40LDMxLTQ3LjksNDIuNS03Ni44YzExLjQtMjguOCwxNy4xLTU3LjksMTcuMS04Ny4zdi0zOTMKCUM4MzkuNiwxNDIuOCw4MzYuMywxMzUuMSw4MjkuOCwxMjguNnogTTc1My44LDU0OC40YzAsMTQyLjMtMjQxLjgsMjY0LjktMjQxLjgsMjY0LjlWMjAzaDI0MS44Qzc1My44LDIwMyw3NTMuOCw0MDYuMSw3NTMuOCw1NDguNHoKCSIvPgo8L3N2Zz4K\",\n        \"icon_light\": \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMywgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9Ikljb24iIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEwMjQgMTAyNDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiMxNzVEREM7fQoJLnN0MXtmaWxsOiNGRkZGRkY7fQo8L3N0eWxlPgo8cmVjdCBpZD0iQmFja2dyb3VuZCIgY2xhc3M9InN0MCIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIvPgo8cGF0aCBpZD0iSWRlbnRpdHkiIGNsYXNzPSJzdDEiIGQ9Ik04MjkuOCwxMjguNmMtNi41LTYuNS0xNC4yLTkuNy0yMy05LjdIMjE3LjJjLTguOSwwLTE2LjUsMy4yLTIzLDkuN3MtOS43LDE0LjItOS43LDIzdjM5My4xCgljMCwyOS4zLDUuNyw1OC40LDE3LjEsODcuM2MxMS40LDI4LjgsMjUuNiw1NC40LDQyLjUsNzYuOGMxNi45LDIyLjMsMzcsNDQuMSw2MC40LDY1LjNzNDUsMzguNyw2NC43LDUyLjcKCWMxOS44LDE0LDQwLjQsMjcuMiw2MS45LDM5LjdzMzYuOCwyMC45LDQ1LjgsMjUuM2M5LDQuNCwxNi4zLDcuOSwyMS43LDEwLjJjNC4xLDIsOC41LDMuMSwxMy4zLDMuMWM0LjgsMCw5LjItMSwxMy4zLTMuMQoJYzUuNS0yLjQsMTIuNy01LjgsMjEuOC0xMC4yYzktNC40LDI0LjMtMTIuOSw0NS44LTI1LjNjMjEuNS0xMi41LDQyLjEtMjUuNyw2MS45LTM5LjdjMTkuOC0xNCw0MS40LTMxLjYsNjQuOC01Mi43CgljMjMuNC0yMS4yLDQzLjUtNDIuOSw2MC40LTY1LjNjMTYuOS0yMi40LDMxLTQ3LjksNDIuNS03Ni44YzExLjQtMjguOCwxNy4xLTU3LjksMTcuMS04Ny4zdi0zOTMKCUM4MzkuNiwxNDIuOCw4MzYuMywxMzUuMSw4MjkuOCwxMjguNnogTTc1My44LDU0OC40YzAsMTQyLjMtMjQxLjgsMjY0LjktMjQxLjgsMjY0LjlWMjAzaDI0MS44Qzc1My44LDIwMyw3NTMuOCw0MDYuMSw3NTMuOCw1NDguNHoKCSIvPgo8L3N2Zz4K\"\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    \"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    \"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}"
  },
  {
    "path": "x/webauthnx/errors.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthnx\n\nimport (\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/ory/jsonschema/v3\"\n)\n\nvar ErrNoCredentials = errors.New(\"required credentials not found\")\n\nvar ErrNotEnoughCredentials = &jsonschema.ValidationError{\n\tMessage: \"unable to remove this security key because it would lock you out of your account\", InstancePtr: \"#/webauthn_remove\"}\n"
  },
  {
    "path": "x/webauthnx/handler.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthnx\n\nimport (\n\t_ \"embed\"\n\t\"net/http\"\n\n\t\"github.com/ory/x/httprouterx\"\n)\n\n//go:embed js/webauthn.js\nvar jsOnLoad []byte\n\nconst ScriptURL = \"/.well-known/ory/webauthn.js\"\n\n// swagger:model webAuthnJavaScript\n//\n//nolint:deadcode,unused\n//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions\ntype webAuthnJavaScript string\n\n// swagger:route GET /.well-known/ory/webauthn.js frontend getWebAuthnJavaScript\n//\n// # Get WebAuthn JavaScript\n//\n// This endpoint provides JavaScript which is needed in order to perform WebAuthn login and registration.\n//\n// If you are building a JavaScript Browser App (e.g. in ReactJS or AngularJS) you will need to load this file:\n//\n//\t```html\n//\t<script src=\"https://public-kratos.example.org/.well-known/ory/webauthn.js\" type=\"script\" async />\n//\t```\n//\n// More information can be found at [Ory Kratos User Login](https://www.ory.sh/docs/kratos/self-service/flows/user-login) and [User Registration Documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-registration).\n//\n//\tProduces:\n//\t- text/javascript\n//\n//\tSchemes: http, https\n//\n//\tResponses:\n//\t  200: webAuthnJavaScript\n//\n//\tExtensions:\n//\t  x-ory-ratelimit-bucket: hydra-public-low\nfunc RegisterWebauthnRoute(r *httprouterx.RouterPublic) {\n\tr.GET(ScriptURL, func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Set(\"Content-Type\", \"text/javascript; charset=UTF-8\")\n\t\t_, _ = w.Write(jsOnLoad)\n\t})\n}\n"
  },
  {
    "path": "x/webauthnx/js/trigger.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage js\n\nimport \"fmt\"\n\n// swagger:enum WebAuthnTriggers\ntype WebAuthnTriggers string\n\nconst (\n\tWebAuthnTriggersWebAuthnRegistration         WebAuthnTriggers = \"oryWebAuthnRegistration\"\n\tWebAuthnTriggersWebAuthnLogin                WebAuthnTriggers = \"oryWebAuthnLogin\"\n\tWebAuthnTriggersPasskeyLogin                 WebAuthnTriggers = \"oryPasskeyLogin\"\n\tWebAuthnTriggersPasskeyLoginAutocompleteInit WebAuthnTriggers = \"oryPasskeyLoginAutocompleteInit\"\n\tWebAuthnTriggersPasskeyRegistration          WebAuthnTriggers = \"oryPasskeyRegistration\"\n\tWebAuthnTriggersPasskeySettingsRegistration  WebAuthnTriggers = \"oryPasskeySettingsRegistration\"\n)\n\nfunc (r WebAuthnTriggers) String() string {\n\treturn fmt.Sprintf(\"window.%s\", string(r))\n}\n"
  },
  {
    "path": "x/webauthnx/js/trigger_test.go",
    "content": "// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage js\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestToString(t *testing.T) {\n\tassert.Equal(t, \"window.oryWebAuthnRegistration\", WebAuthnTriggersWebAuthnRegistration.String())\n}\n"
  },
  {
    "path": "x/webauthnx/js/webauthn.js",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\n; (function () {\n  if (!window) {\n    return\n  }\n\n  if (!window.PublicKeyCredential) {\n    console.log(\"This browser does not support WebAuthn!\")\n    return\n  }\n\n  if (window.__oryWebAuthnInitialized) {\n    return\n  }\n\n  function __oryWebAuthnBufferDecode(value) {\n    return Uint8Array.from(\n      atob(value.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\")),\n      function (c) {\n        return c.charCodeAt(0)\n      },\n    )\n  }\n\n  function __oryWebAuthnBufferEncode(value) {\n    return btoa(String.fromCharCode.apply(null, new Uint8Array(value)))\n      .replaceAll(\"+\", \"-\")\n      .replaceAll(\"/\", \"_\")\n      .replaceAll(\"=\", \"\")\n  }\n\n  function __oryWebAuthnLogin(\n    options,\n    resultQuerySelector = '*[name=\"webauthn_login\"]',\n    triggerQuerySelector = '*[name=\"webauthn_login_trigger\"]',\n  ) {\n    if (!window.PublicKeyCredential) {\n      alert(\"This browser does not support WebAuthn!\")\n    }\n\n    const triggerEl = document.querySelector(triggerQuerySelector)\n    let opt = options\n    if (!opt) {\n      opt = JSON.parse(triggerEl.value)\n    }\n\n    opt.publicKey.challenge = __oryWebAuthnBufferDecode(opt.publicKey.challenge)\n    opt.publicKey.allowCredentials = opt.publicKey.allowCredentials.map(\n      function (value) {\n        return {\n          ...value,\n          id: __oryWebAuthnBufferDecode(value.id),\n        }\n      },\n    )\n\n    navigator.credentials\n      .get(opt)\n      .then(function (credential) {\n        document.querySelector(resultQuerySelector).value = JSON.stringify({\n          id: credential.id,\n          rawId: __oryWebAuthnBufferEncode(credential.rawId),\n          type: credential.type,\n          response: {\n            authenticatorData: __oryWebAuthnBufferEncode(\n              credential.response.authenticatorData,\n            ),\n            clientDataJSON: __oryWebAuthnBufferEncode(\n              credential.response.clientDataJSON,\n            ),\n            signature: __oryWebAuthnBufferEncode(credential.response.signature),\n            userHandle: __oryWebAuthnBufferEncode(\n              credential.response.userHandle,\n            ),\n          },\n        })\n\n        triggerEl.closest(\"form\").submit()\n      })\n      .catch((err) => {\n        alert(err)\n      })\n  }\n\n  function __oryWebAuthnRegistration(\n    options,\n    resultQuerySelector = '*[name=\"webauthn_register\"]',\n    triggerQuerySelector = '*[name=\"webauthn_register_trigger\"]',\n  ) {\n    if (!window.PublicKeyCredential) {\n      alert(\"This browser does not support WebAuthn!\")\n    }\n\n    const triggerEl = document.querySelector(triggerQuerySelector)\n    let opt = options\n    if (!opt) {\n      opt = JSON.parse(triggerEl.value)\n    }\n\n    opt.publicKey.user.id = __oryWebAuthnBufferDecode(opt.publicKey.user.id)\n    opt.publicKey.challenge = __oryWebAuthnBufferDecode(opt.publicKey.challenge)\n\n    if (opt.publicKey.excludeCredentials) {\n      opt.publicKey.excludeCredentials = opt.publicKey.excludeCredentials.map(\n        function (value) {\n          return {\n            ...value,\n            id: __oryWebAuthnBufferDecode(value.id),\n          }\n        },\n      )\n    }\n\n    navigator.credentials\n      .create(opt)\n      .then(function (credential) {\n        document.querySelector(resultQuerySelector).value = JSON.stringify({\n          id: credential.id,\n          rawId: __oryWebAuthnBufferEncode(credential.rawId),\n          type: credential.type,\n          response: {\n            attestationObject: __oryWebAuthnBufferEncode(\n              credential.response.attestationObject,\n            ),\n            clientDataJSON: __oryWebAuthnBufferEncode(\n              credential.response.clientDataJSON,\n            ),\n          },\n        })\n\n        triggerEl.closest(\"form\").submit()\n      })\n      .catch((err) => {\n        alert(err)\n      })\n  }\n\n  async function __oryPasskeyLoginAutocompleteInit() {\n    const dataEl = document.getElementsByName(\"passkey_challenge\")[0]\n    const resultEl = document.getElementsByName(\"passkey_login\")[0]\n    const identifierEl = document.getElementsByName(\"identifier\")[0]\n\n    if (!dataEl || !resultEl || !identifierEl) {\n      console.error(\n        \"Unable to initialize WebAuthn / Passkey autocomplete because one or more required form fields are missing.\",\n      )\n      return\n    }\n\n    if (\n      !window.PublicKeyCredential ||\n      !window.PublicKeyCredential.isConditionalMediationAvailable ||\n      window.Cypress // Cypress auto-fills the autocomplete, which we don't want\n    ) {\n      console.log(\"This browser does not support Passkey / WebAuthn!\")\n      return\n    }\n\n    const isCMA = await PublicKeyCredential.isConditionalMediationAvailable()\n    if (!isCMA) {\n      console.log(\n        \"This browser does not support WebAuthn Conditional Mediation!\",\n      )\n      return\n    }\n\n    let opt = JSON.parse(dataEl.value)\n\n    if (opt.publicKey.user && opt.publicKey.user.id) {\n      opt.publicKey.user.id = __oryWebAuthnBufferDecode(opt.publicKey.user.id)\n    }\n    opt.publicKey.challenge = __oryWebAuthnBufferDecode(opt.publicKey.challenge)\n\n    // If this is set we already have a request ongoing which we need to abort.\n    if (window.abortPasskeyConditionalUI) {\n      window.abortPasskeyConditionalUI.abort(\n        \"Canceling Passkey autocomplete to complete trigger-based passkey login.\",\n      )\n      window.abortPasskeyConditionalUI = undefined\n    }\n\n    // Allow aborting through a global variable\n    window.abortPasskeyConditionalUI = new AbortController()\n\n    navigator.credentials\n      .get({\n        publicKey: opt.publicKey,\n        mediation: \"conditional\",\n        signal: abortPasskeyConditionalUI.signal,\n      })\n      .then(function (credential) {\n        resultEl.value = JSON.stringify({\n          id: credential.id,\n          rawId: __oryWebAuthnBufferEncode(credential.rawId),\n          type: credential.type,\n          response: {\n            authenticatorData: __oryWebAuthnBufferEncode(\n              credential.response.authenticatorData,\n            ),\n            clientDataJSON: __oryWebAuthnBufferEncode(\n              credential.response.clientDataJSON,\n            ),\n            signature: __oryWebAuthnBufferEncode(credential.response.signature),\n            userHandle: __oryWebAuthnBufferEncode(\n              credential.response.userHandle,\n            ),\n          },\n        })\n\n        resultEl.closest(\"form\").submit()\n      })\n      .catch((err) => {\n        console.trace(err)\n        console.log(err)\n      })\n  }\n\n  function __oryPasskeyLogin() {\n    const dataEl = document.getElementsByName(\"passkey_challenge\")[0]\n    const resultEl = document.getElementsByName(\"passkey_login\")[0]\n\n    if (!dataEl || !resultEl) {\n      console.error(\n        \"Unable to initialize WebAuthn / Passkey autocomplete because one or more required form fields are missing.\",\n      )\n      return\n    }\n    if (!window.PublicKeyCredential) {\n      console.log(\"This browser does not support WebAuthn!\")\n      return\n    }\n\n    let opt = JSON.parse(dataEl.value)\n\n    if (opt.publicKey.user && opt.publicKey.user.id) {\n      opt.publicKey.user.id = __oryWebAuthnBufferDecode(opt.publicKey.user.id)\n    }\n    opt.publicKey.challenge = __oryWebAuthnBufferDecode(opt.publicKey.challenge)\n    if (opt.publicKey.allowCredentials) {\n      opt.publicKey.allowCredentials = opt.publicKey.allowCredentials.map(\n        function (cred) {\n          return {\n            ...cred,\n            id: __oryWebAuthnBufferDecode(cred.id),\n          }\n        },\n      )\n    }\n\n    if (window.abortPasskeyConditionalUI) {\n      window.abortPasskeyConditionalUI.abort(\n        \"Canceling Passkey autocomplete to complete trigger-based passkey login.\",\n      )\n      window.abortPasskeyConditionalUI = undefined\n    }\n\n    navigator.credentials\n      .get({\n        publicKey: opt.publicKey,\n      })\n      .then(function (credential) {\n        console.trace('login', credential)\n        resultEl.value = JSON.stringify({\n          id: credential.id,\n          rawId: __oryWebAuthnBufferEncode(credential.rawId),\n          type: credential.type,\n          response: {\n            authenticatorData: __oryWebAuthnBufferEncode(\n              credential.response.authenticatorData,\n            ),\n            clientDataJSON: __oryWebAuthnBufferEncode(\n              credential.response.clientDataJSON,\n            ),\n            signature: __oryWebAuthnBufferEncode(credential.response.signature),\n            userHandle: __oryWebAuthnBufferEncode(\n              credential.response.userHandle,\n            ),\n          },\n        })\n\n        resultEl.closest(\"form\").submit()\n      })\n      .catch((err) => {\n        // Calling this again will enable the autocomplete once again.\n        if (err instanceof DOMException && err.name === \"SecurityError\") {\n          console.error(`A security exception occurred while loading Passkeys / WebAuthn. To troubleshoot, please head over to https://www.ory.sh/docs/troubleshooting/passkeys-webauthn-security-error. The original error message is: ${err.message}`)\n        } else {\n          console.error(\"[Ory/Passkey] An unknown error occurred while getting passkey credentials\", err)\n        }\n\n        console.trace(err)\n\n        // Try re-initializing autocomplete\n        return __oryPasskeyLoginAutocompleteInit()\n      })\n  }\n\n  function __oryPasskeyRegistration() {\n    const dataEl = document.getElementsByName(\"passkey_create_data\")[0]\n    const resultEl = document.getElementsByName(\"passkey_register\")[0]\n\n    if (!dataEl || !resultEl) {\n      console.debug(\"__oryPasskeyRegistration: mandatory fields not found\")\n      return\n    }\n\n    const createData = JSON.parse(dataEl.value)\n\n    // Fetch display name from field value\n    const displayNameFieldName = createData.displayNameFieldName\n    const displayName = dataEl\n      .closest(\"form\")\n      .querySelector(\"[name='\" + displayNameFieldName + \"']\").value\n\n    let opts = createData.credentialOptions\n    opts.publicKey.user.name = displayName\n    opts.publicKey.user.displayName = displayName\n    opts.publicKey.user.id = __oryWebAuthnBufferDecode(opts.publicKey.user.id)\n    opts.publicKey.challenge = __oryWebAuthnBufferDecode(\n      opts.publicKey.challenge,\n    )\n\n    if (opts.publicKey.excludeCredentials) {\n      opts.publicKey.excludeCredentials = opts.publicKey.excludeCredentials.map(\n        function (value) {\n          return {\n            ...value,\n            id: __oryWebAuthnBufferDecode(value.id),\n          }\n        },\n      )\n    }\n\n    navigator.credentials\n      .create(opts)\n      .then(function (credential) {\n        resultEl.value = JSON.stringify({\n          id: credential.id,\n          rawId: __oryWebAuthnBufferEncode(credential.rawId),\n          type: credential.type,\n          response: {\n            attestationObject: __oryWebAuthnBufferEncode(\n              credential.response.attestationObject,\n            ),\n            clientDataJSON: __oryWebAuthnBufferEncode(\n              credential.response.clientDataJSON,\n            ),\n          },\n        })\n\n        resultEl.closest(\"form\").submit()\n      })\n      .catch((err) => {\n        console.error(err)\n      })\n  }\n\n  function __oryPasskeySettingsRegistration() {\n    const dataEl = document.getElementsByName(\"passkey_create_data\")[0]\n    const resultEl = document.getElementsByName(\"passkey_settings_register\")[0]\n\n    if (!dataEl || !resultEl) {\n      console.debug(\n        \"__oryPasskeySettingsRegistration: mandatory fields not found\",\n      )\n      return\n    }\n\n    let opt = JSON.parse(dataEl.value)\n\n    opt.publicKey.user.id = __oryWebAuthnBufferDecode(opt.publicKey.user.id)\n    opt.publicKey.challenge = __oryWebAuthnBufferDecode(opt.publicKey.challenge)\n\n    if (opt.publicKey.excludeCredentials) {\n      opt.publicKey.excludeCredentials = opt.publicKey.excludeCredentials.map(\n        function (value) {\n          return {\n            ...value,\n            id: __oryWebAuthnBufferDecode(value.id),\n          }\n        },\n      )\n    }\n\n    navigator.credentials\n      .create(opt)\n      .then(function (credential) {\n        resultEl.value = JSON.stringify({\n          id: credential.id,\n          rawId: __oryWebAuthnBufferEncode(credential.rawId),\n          type: credential.type,\n          response: {\n            attestationObject: __oryWebAuthnBufferEncode(\n              credential.response.attestationObject,\n            ),\n            clientDataJSON: __oryWebAuthnBufferEncode(\n              credential.response.clientDataJSON,\n            ),\n          },\n        })\n\n        resultEl.closest(\"form\").submit()\n      })\n      .catch((err) => {\n        console.error(err)\n      })\n  }\n\n  // Deprecated naming with underscores - kept for support with Ory Elements v0\n  window.__oryWebAuthnLogin = __oryWebAuthnLogin\n  window.__oryWebAuthnRegistration = __oryWebAuthnRegistration\n  window.__oryPasskeySettingsRegistration = __oryPasskeySettingsRegistration\n  window.__oryPasskeyLogin = __oryPasskeyLogin\n  window.__oryPasskeyRegistration = __oryPasskeyRegistration\n  window.__oryPasskeyLoginAutocompleteInit = __oryPasskeyLoginAutocompleteInit\n\n  // Current naming - use with Ory Elements v1\n  window.oryWebAuthnLogin = __oryWebAuthnLogin\n  window.oryWebAuthnRegistration = __oryWebAuthnRegistration\n  window.oryPasskeySettingsRegistration = __oryPasskeySettingsRegistration\n  window.oryPasskeyLogin = __oryPasskeyLogin\n  window.oryPasskeyRegistration = __oryPasskeyRegistration\n  window.oryPasskeyLoginAutocompleteInit = __oryPasskeyLoginAutocompleteInit\n\n  window.__oryWebAuthnInitialized = true\n  window.dispatchEvent(\n    new CustomEvent(\"oryWebAuthnInitialized\"),\n  )\n})()\n"
  },
  {
    "path": "x/webauthnx/nodes.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthnx\n\nimport (\n\t\"cmp\"\n\t\"crypto/sha512\"\n\t_ \"embed\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/url\"\n\n\t\"github.com/ory/kratos/x/webauthnx/js\"\n\n\t\"github.com/ory/x/urlx\"\n\n\t\"github.com/ory/kratos/identity\"\n\t\"github.com/ory/kratos/text\"\n\t\"github.com/ory/kratos/ui/node\"\n)\n\nfunc NewWebAuthnConnectionTrigger(options string) *node.Node {\n\treturn node.NewInputField(node.WebAuthnRegisterTrigger, \"\", node.WebAuthnGroup,\n\t\tnode.InputAttributeTypeButton, node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t//nolint:staticcheck\n\t\t\ta.OnClick = fmt.Sprintf(\"%s(%s)\", js.WebAuthnTriggersWebAuthnRegistration, options)\n\t\t\ta.OnClickTrigger = js.WebAuthnTriggersWebAuthnRegistration\n\t\t\ta.FieldValue = options\n\t\t}))\n}\n\nfunc NewWebAuthnScript(base *url.URL) *node.Node {\n\tsrc := urlx.AppendPaths(base, ScriptURL).String()\n\tintegrity := sha512.Sum512(jsOnLoad)\n\treturn node.NewScriptField(\n\t\tnode.WebAuthnScript,\n\t\tsrc,\n\t\tnode.WebAuthnGroup,\n\t\tfmt.Sprintf(\"sha512-%s\", base64.StdEncoding.EncodeToString(integrity[:])),\n\t)\n}\n\nfunc NewWebAuthnConnectionInput() *node.Node {\n\treturn node.NewInputField(node.WebAuthnRegister, \"\", node.WebAuthnGroup,\n\t\tnode.InputAttributeTypeHidden)\n}\n\nfunc NewWebAuthnLoginTrigger(options string) *node.Node {\n\treturn node.NewInputField(node.WebAuthnLoginTrigger, \"\", node.WebAuthnGroup,\n\t\tnode.InputAttributeTypeButton, node.WithInputAttributes(func(a *node.InputAttributes) {\n\t\t\t//nolint:staticcheck\n\t\t\ta.OnClick = fmt.Sprintf(\"%s(%s)\", js.WebAuthnTriggersWebAuthnLogin, options)\n\t\t\ta.FieldValue = options\n\t\t\ta.OnClickTrigger = js.WebAuthnTriggersWebAuthnLogin\n\t\t}))\n}\n\nfunc NewWebAuthnLoginInput() *node.Node {\n\treturn node.NewInputField(node.WebAuthnLogin, \"\", node.WebAuthnGroup,\n\t\tnode.InputAttributeTypeHidden)\n}\n\nfunc NewWebAuthnConnectionName() *node.Node {\n\treturn node.NewInputField(node.WebAuthnRegisterDisplayName, \"\", node.WebAuthnGroup, node.InputAttributeTypeText).\n\t\tWithMetaLabel(text.NewInfoSelfServiceRegisterWebAuthnDisplayName())\n}\n\nfunc NewWebAuthnUnlink(c *identity.CredentialWebAuthn, opts ...node.InputAttributesModifier) *node.Node {\n\treturn node.NewInputField(\n\t\tnode.WebAuthnRemove,\n\t\tfmt.Sprintf(\"%x\", c.ID),\n\t\tnode.WebAuthnGroup,\n\t\tnode.InputAttributeTypeSubmit,\n\t\topts...,\n\t).WithMetaLabel(text.NewInfoSelfServiceRemoveWebAuthn(cmp.Or(c.DisplayName, \"unnamed\"), c.AddedAt))\n}\n\nfunc NewPasskeyUnlink(c *identity.CredentialWebAuthn, opts ...node.InputAttributesModifier) *node.Node {\n\treturn node.NewInputField(\n\t\tnode.PasskeyRemove,\n\t\tfmt.Sprintf(\"%x\", c.ID),\n\t\tnode.PasskeyGroup,\n\t\tnode.InputAttributeTypeSubmit,\n\t\topts...,\n\t).WithMetaLabel(text.NewInfoSelfServiceRemovePasskey(cmp.Or(c.DisplayName, \"unnamed\"), c.AddedAt))\n}\n"
  },
  {
    "path": "x/webauthnx/user.go",
    "content": "// Copyright © 2023 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\npackage webauthnx\n\nimport (\n\t\"cmp\"\n\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n)\n\nvar _ webauthn.User = (*User)(nil)\n\ntype User struct {\n\tName        string\n\tID          []byte\n\tCredentials []webauthn.Credential\n\tConfig      *webauthn.Config\n}\n\nfunc NewUser(id []byte, credentials []webauthn.Credential, config *webauthn.Config) *User {\n\treturn &User{\n\t\tID:          id,\n\t\tCredentials: credentials,\n\t\tConfig:      config,\n\t}\n}\n\nfunc (u *User) WebAuthnID() []byte {\n\treturn u.ID\n}\n\nfunc (u *User) WebAuthnName() string {\n\treturn cmp.Or(u.Name, u.Config.RPDisplayName)\n}\n\nfunc (u *User) WebAuthnDisplayName() string {\n\treturn cmp.Or(u.Name, u.Config.RPDisplayName)\n}\n\nfunc (u *User) WebAuthnIcon() string {\n\treturn \"\" // Icon option has been removed due to security considerations.\n}\n\nfunc (u *User) WebAuthnCredentials() []webauthn.Credential {\n\treturn u.Credentials\n}\n"
  }
]